summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Keating <jkeating@redhat.com>2010-07-29 17:18:45 -0700
committerJesse Keating <jkeating@redhat.com>2010-07-29 17:18:45 -0700
commit2f82dda4a9bf41e64e864889bf06564bdf826e25 (patch)
tree118a7b483ae5de4dbf83d20001302f1404866ef0
parent64ba2e5ffde5f2418eb26c700cb0ab62b04e5013 (diff)
downloaddom0-kernel-2f82dda4a9bf41e64e864889bf06564bdf826e25.tar.gz
dom0-kernel-2f82dda4a9bf41e64e864889bf06564bdf826e25.tar.xz
dom0-kernel-2f82dda4a9bf41e64e864889bf06564bdf826e25.zip
initial srpm import
-rw-r--r--.gitignore2
-rw-r--r--Makefile.config105
-rw-r--r--acpi-ec-add-delay-before-write.patch52
-rw-r--r--add-appleir-usb-driver.patch635
-rw-r--r--btrfs-prohibit-a-operation-of-changing-acls-mask-when-noacl-mount-option-is-used.patch42
-rw-r--r--cifs-fix-malicious-redirect-problem-in-the-dns-lookup-code.patch203
-rw-r--r--config-arm102
-rw-r--r--config-debug64
-rw-r--r--config-generic4061
-rw-r--r--config-i686-PAE37
-rw-r--r--config-ia64-generic206
-rw-r--r--config-nodebug64
-rw-r--r--config-powerpc-generic326
-rw-r--r--config-powerpc32-generic184
-rw-r--r--config-powerpc32-smp4
-rw-r--r--config-powerpc64176
-rw-r--r--config-rhel-generic204
-rw-r--r--config-s390x227
-rw-r--r--config-sparc64-generic196
-rw-r--r--config-x86-generic476
-rw-r--r--config-x86_64-generic387
-rw-r--r--crypto-add-async-hash-testing.patch111
-rw-r--r--crypto-testmgr-add-null-test-for-aesni.patch138
-rw-r--r--crystalhd-2.6.34-staging.patch8287
-rw-r--r--die-floppy-die.patch18
-rw-r--r--drm-i915-add-reclaimable-to-page-allocations.patch48
-rw-r--r--drm-i915-fix-hibernate-memory-corruption.patch41
-rw-r--r--drm-i915-resume-force-mode.patch50
-rw-r--r--drm-intel-945gm-stability-fixes.patch117
-rw-r--r--drm-intel-acpi-populate-didl.patch70
-rw-r--r--drm-intel-big-hammer.patch16
-rw-r--r--drm-intel-make-lvds-work.patch19
-rw-r--r--drm-intel-next.patch1
-rw-r--r--drm-intel-no-tv-hotplug.patch11
-rw-r--r--drm-nouveau-d620.patch121
-rw-r--r--drm-nouveau-g80-ctxprog.patch10306
-rw-r--r--drm-nouveau-kconfig.patch11
-rw-r--r--drm-nouveau-mutex.patch56
-rw-r--r--drm-nouveau-safetile-getparam.patch26
-rw-r--r--drm-nouveau-tvout-disable.patch57
-rw-r--r--drm-nouveau-update.patch306
-rw-r--r--drm-radeon-pm.patch586
-rw-r--r--drm-upgrayedd.patch85406
-rw-r--r--ethtool-fix-buffer-overflow.patch33
-rw-r--r--ext4-issue-discard-operation-before-releasing-blocks.patch62
-rw-r--r--ext4-make-sure-the-move_ext-ioctl-can-t-overwrite-append-only-files.patch34
-rwxr-xr-xfind-provides44
-rw-r--r--fix-abrtd.patch774
-rw-r--r--fix-ima-null-ptr-deref.patch54
-rw-r--r--genkey7
-rw-r--r--git-bluetooth.patch3344
-rw-r--r--git-cpufreq.patch0
-rw-r--r--git-linus.diff0
-rw-r--r--hda_intel-prealloc-4mb-dmabuffer.patch35
-rw-r--r--hdpvr-ir-enable.patch213
-rw-r--r--ice1712-fix-revo71-mixer-names.patch42
-rw-r--r--inotify-fix-inotify-oneshot-support.patch25
-rw-r--r--inotify-send-IN_UNMOUNT-events.patch29
-rw-r--r--iwlwifi-cancel-scan-watchdog-in-iwl_bg_abort_scan.patch58
-rw-r--r--iwlwifi-fix-internal-scan-race.patch123
-rw-r--r--iwlwifi-fix-scan-races.patch139
-rw-r--r--iwlwifi-manage-QoS-by-mac-stack.patch100
-rw-r--r--iwlwifi-recover_from_tx_stall.patch13
-rw-r--r--iwlwifi-reset-card-during-probe.patch167
-rw-r--r--iwlwifi_-Adjusting-PLCP-error-threshold-for-1000-NIC.patch38
-rw-r--r--iwlwifi_-Logic-to-control-how-frequent-radio-should-be-reset-if-needed.patch82
-rw-r--r--iwlwifi_-Recover-TX-flow-failure.patch137
-rw-r--r--iwlwifi_-Recover-TX-flow-stall-due-to-stuck-queue.patch446
-rw-r--r--iwlwifi_-Tune-radio-to-prevent-unexpected-behavior.patch369
-rw-r--r--iwlwifi_-add-function-to-reset_tune-radio-if-needed.patch374
-rw-r--r--iwlwifi_-add-internal-short-scan-support-for-3945.patch85
-rw-r--r--iwlwifi_-code-cleanup-for-connectivity-recovery.patch230
-rw-r--r--iwlwifi_-iwl_good_ack_health-only-apply-to-AGN-device.patch146
-rw-r--r--iwlwifi_-move-plcp-check-to-separated-function.patch179
-rw-r--r--iwlwifi_-multiple-force-reset-mode.patch152
-rw-r--r--iwlwifi_-separated-time-check-for-different-type-of-force-reset.patch120
-rw-r--r--kernel.spec4423
-rw-r--r--kvm-mmu-fix-conflict-access-permissions-in-direct-sp.patch49
-rw-r--r--l2tp-fix-oops-in-pppol2tp_xmit.patch78
-rw-r--r--linux-2.6-acpi-video-dos.patch17
-rw-r--r--linux-2.6-ata-quirk.patch58
-rw-r--r--linux-2.6-autoload-wmi.patch244
-rw-r--r--linux-2.6-b43_-Rewrite-DMA-Tx-status-handling-sanity-checks.patch182
-rw-r--r--linux-2.6-block-silently-error-unsupported-empty-barriers-too.patch48
-rw-r--r--linux-2.6-btrfs-fix-acl.patch25
-rw-r--r--linux-2.6-build-nonintconfig.patch128
-rw-r--r--linux-2.6-cantiga-iommu-gfx.patch26
-rw-r--r--linux-2.6-compile-fixes.patch6
-rw-r--r--linux-2.6-crash-driver.patch363
-rw-r--r--linux-2.6-debug-always-inline-kzalloc.patch25
-rw-r--r--linux-2.6-debug-nmi-timeout.patch45
-rw-r--r--linux-2.6-debug-sizeof-structs.patch31
-rw-r--r--linux-2.6-debug-taint-vm.patch65
-rw-r--r--linux-2.6-debug-vm-would-have-oomkilled.patch52
-rw-r--r--linux-2.6-defaults-acpi-video.patch13
-rw-r--r--linux-2.6-defaults-alsa-hda-beep-off.patch13
-rw-r--r--linux-2.6-defaults-aspm.patch12
-rw-r--r--linux-2.6-defaults-pci_no_msi.patch92
-rw-r--r--linux-2.6-defaults-pciehp.patch13
-rw-r--r--linux-2.6-dell-laptop-rfkill-fix.patch323
-rw-r--r--linux-2.6-driver-level-usb-autosuspend.diff62
-rw-r--r--linux-2.6-e1000-ich9.patch27
-rw-r--r--linux-2.6-enable-btusb-autosuspend.patch18
-rw-r--r--linux-2.6-execshield.patch987
-rw-r--r--linux-2.6-ext4-quota-metadata-reservation.patch723
-rw-r--r--linux-2.6-firewire-git-pending.patch4
-rw-r--r--linux-2.6-firewire-git-update.patch3682
-rw-r--r--linux-2.6-g5-therm-shutdown.patch70
-rw-r--r--linux-2.6-hotfixes.patch1
-rwxr-xr-xlinux-2.6-imac-transparent-bridge.patch15
-rw-r--r--linux-2.6-input-fix-toshiba-hotkeys.patch278
-rw-r--r--linux-2.6-input-hid-quirk-egalax.patch41
-rw-r--r--linux-2.6-input-kill-stupid-messages.patch17
-rw-r--r--linux-2.6-ksm-kvm.patch314
-rw-r--r--linux-2.6-makefile-after_link.patch57
-rw-r--r--linux-2.6-nfs4-callback-hidden.patch20
-rw-r--r--linux-2.6-nfsd4-proots.patch226
-rw-r--r--linux-2.6-pci-cacheline-sizing.patch41
-rw-r--r--linux-2.6-pciehp-update.patch147
-rw-r--r--linux-2.6-phylib-autoload.patch406
-rw-r--r--linux-2.6-ps3-storage-alias.patch7
-rw-r--r--linux-2.6-revert-dvb-net-kabi-change.patch149
-rw-r--r--linux-2.6-rfkill-all.patch52
-rw-r--r--linux-2.6-selinux-mprotect-checks.patch41
-rw-r--r--linux-2.6-serial-460800.patch70
-rw-r--r--linux-2.6-silence-acpi-blacklist.patch25
-rw-r--r--linux-2.6-silence-fbcon-logo.patch42
-rw-r--r--linux-2.6-silence-noise.patch66
-rw-r--r--linux-2.6-sparc-selinux-mprotect-checks.patch21
-rw-r--r--linux-2.6-tracehook.patch368
-rw-r--r--linux-2.6-upstream-reverts.patch1936
-rw-r--r--linux-2.6-usb-uvc-autosuspend.diff19
-rw-r--r--linux-2.6-usb-wwan-update.patch1634
-rw-r--r--linux-2.6-utrace-ptrace.patch1825
-rw-r--r--linux-2.6-utrace.patch4163
-rw-r--r--linux-2.6-v4l-dvb-add-kworld-a340-support.patch151
-rw-r--r--linux-2.6-v4l-dvb-add-lgdt3304-support.patch350
-rw-r--r--linux-2.6-v4l-dvb-experimental.patch14750
-rw-r--r--linux-2.6-v4l-dvb-fixes.patch113215
-rw-r--r--linux-2.6-v4l-dvb-rebase-gspca-to-latest.patch23900
-rw-r--r--linux-2.6-v4l-dvb-update.patch366
-rw-r--r--linux-2.6-vio-modalias.patch32
-rw-r--r--linux-2.6-x86-64-fbdev-primary.patch49
-rw-r--r--linux-2.6.29-sparc-IOC_TYPECHECK.patch21
-rw-r--r--linux-2.6.30-hush-rom-warning.patch27
-rw-r--r--linux-2.6.30-no-pcspkr-modalias.patch11
-rw-r--r--lirc-2.6.32.patch17736
-rw-r--r--mac80211-do-not-wipe-out-old-supported-rates.patch68
-rw-r--r--mac80211-explicitly-disable-enable-QoS.patch358
-rw-r--r--mac80211-fix-supported-rates-IE-if-AP-doesnt-give-us-its-rates.patch57
-rwxr-xr-xmerge.pl66
-rw-r--r--pci-acpi-disable-aspm-if-no-osc.patch55
-rw-r--r--pci-aspm-dont-enable-too-early.patch50
-rw-r--r--perf12
-rw-r--r--sched-fix-over-scheduling-bug.patch60
-rw-r--r--sky2-optima-add-missing-write-bits.patch46
-rw-r--r--sky2-optima-add-register-definitions.patch281
-rw-r--r--sky2-optima-fix-tcp-offload.patch33
-rw-r--r--sky2-optima-print-chip-name.patch27
-rw-r--r--sky2-optima-support.patch157
-rw-r--r--sources2
-rw-r--r--ssb_check_for_sprom.patch185
-rw-r--r--thinkpad-acpi-add-x100e.patch11
-rw-r--r--thinkpad-acpi-fix-backlight.patch56
-rw-r--r--usb-obey-the-sysfs-power-wakeup-setting.patch51
-rw-r--r--via-hwmon-temp-sensor.patch391
-rw-r--r--viafb-neuter-device-table.patch21
-rw-r--r--wmi-check-find_guid-return-value-to-prevent-oops.patch36
-rw-r--r--wmi-survive-bios-with-duplicate-guids.patch76
-rw-r--r--xfs-prevent-swapext-from-operating-on-write-only-files.patch42
170 files changed, 317843 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index e69de29..5c310bd 100644
--- a/.gitignore
+++ b/.gitignore
@@ -0,0 +1,2 @@
+linux-2.6.32.tar.bz2
+patch-2.6.32.16.bz2
diff --git a/Makefile.config b/Makefile.config
new file mode 100644
index 0000000..53812fa
--- /dev/null
+++ b/Makefile.config
@@ -0,0 +1,105 @@
+# Make rules for configuration files.
+#
+# $Id$
+
+CFG = kernel-$(VERSION)
+
+CONFIGFILES = \
+ $(CFG)-i686.config $(CFG)-i686-debug.config \
+ $(CFG)-i686-PAE.config $(CFG)-i686-PAEdebug.config \
+ $(CFG)-x86_64.config $(CFG)-x86_64-debug.config \
+ $(CFG)-s390x.config $(CFG)-arm.config \
+ $(CFG)-ppc.config $(CFG)-ppc-smp.config \
+ $(CFG)-sparc64.config \
+ $(CFG)-ppc64.config $(CFG)-ppc64-debug.config \
+ $(CFG)-ia64.config
+
+PLATFORMS = x86 x86_64 powerpc powerpc32 powerpc64 s390x ia64 sparc64
+TEMPFILES = $(addprefix temp-, $(addsuffix -generic, $(PLATFORMS)))
+
+configs: $(CONFIGFILES)
+ @rm -f kernel-*-config
+ @rm -f $(TEMPFILES)
+ @rm -f temp-generic temp-*-generic temp-*-generic-tmp
+
+# Augment the clean target to clean up our own cruft
+clean ::
+ @rm -fv $(CONFIGFILES) $(TEMPFILES) temp-generic kernel-$(VERSION)*config
+
+temp-generic: config-generic
+ cat config-generic config-nodebug > temp-generic
+
+temp-debug-generic: config-generic
+ cat config-generic config-debug > temp-debug-generic
+
+temp-x86-generic: config-x86-generic temp-generic
+ perl merge.pl $^ > $@
+
+temp-x86-debug-generic: config-x86-generic temp-debug-generic
+ perl merge.pl $^ > $@
+
+temp-x86_64-generic: config-x86_64-generic temp-generic
+ perl merge.pl $^ > $@
+
+temp-x86_64-debug-generic: config-x86_64-generic temp-debug-generic
+ perl merge.pl $^ > $@
+
+temp-sparc64-generic: config-sparc64-generic temp-generic
+ perl merge.pl $^ > $@
+
+temp-powerpc-generic: config-powerpc-generic temp-generic
+ perl merge.pl $^ > $@
+
+temp-powerpc-debug-generic: config-powerpc-generic temp-debug-generic
+ perl merge.pl $^ > $@
+
+temp-powerpc32-generic: config-powerpc32-generic temp-powerpc-generic
+ perl merge.pl $^ > $@
+
+temp-s390-generic: config-s390x temp-generic
+ perl merge.pl $^ > $@
+
+temp-ia64-generic: config-ia64-generic temp-generic
+ perl merge.pl $^ > $@
+
+kernel-$(VERSION)-i686-PAE.config: config-i686-PAE temp-x86-generic
+ perl merge.pl $^ i386 > $@
+
+kernel-$(VERSION)-i686-PAEdebug.config: config-i686-PAE temp-x86-debug-generic
+ perl merge.pl $^ i386 > $@
+
+kernel-$(VERSION)-i686.config: /dev/null temp-x86-generic
+ perl merge.pl $^ i386 > $@
+
+kernel-$(VERSION)-i686-debug.config: /dev/null temp-x86-debug-generic
+ perl merge.pl $^ i386 > $@
+
+kernel-$(VERSION)-x86_64.config: /dev/null temp-x86_64-generic
+ perl merge.pl $^ x86_64 > $@
+
+kernel-$(VERSION)-x86_64-debug.config: /dev/null temp-x86_64-debug-generic
+ perl merge.pl $^ x86_64 > $@
+
+kernel-$(VERSION)-sparc64.config: /dev/null temp-sparc64-generic
+ perl merge.pl $^ sparc64 > $@
+
+kernel-$(VERSION)-ppc64.config: config-powerpc64 temp-powerpc-generic
+ perl merge.pl $^ powerpc > $@
+
+kernel-$(VERSION)-ppc64-debug.config: config-powerpc64 temp-powerpc-debug-generic
+ perl merge.pl $^ powerpc > $@
+
+kernel-$(VERSION)-s390x.config: config-s390x temp-s390-generic
+ perl merge.pl $^ s390 > $@
+
+kernel-$(VERSION)-arm.config: config-arm temp-generic
+ perl merge.pl $^ arm > $@
+
+kernel-$(VERSION)-ppc.config: /dev/null temp-powerpc32-generic
+ perl merge.pl $^ powerpc > $@
+
+kernel-$(VERSION)-ppc-smp.config: config-powerpc32-smp temp-powerpc32-generic
+ perl merge.pl $^ powerpc > $@
+
+kernel-$(VERSION)-ia64.config: /dev/null temp-ia64-generic
+ perl merge.pl $^ ia64 > $@
diff --git a/acpi-ec-add-delay-before-write.patch b/acpi-ec-add-delay-before-write.patch
new file mode 100644
index 0000000..af49ccc
--- /dev/null
+++ b/acpi-ec-add-delay-before-write.patch
@@ -0,0 +1,52 @@
+https://bugzilla.kernel.org/show_bug.cgi?id=14733#c41
+
+diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c
+index 27e0b92..09fbb69 100644
+--- a/drivers/acpi/ec.c
++++ b/drivers/acpi/ec.c
+@@ -226,6 +226,7 @@ static int ec_poll(struct acpi_ec *ec)
+ if (ec_transaction_done(ec))
+ return 0;
+ } else {
++ msleep(1);
+ if (wait_event_timeout(ec->wait,
+ ec_transaction_done(ec),
+ msecs_to_jiffies(1)))
+@@ -233,8 +234,8 @@ static int ec_poll(struct acpi_ec *ec)
+ }
+ advance_transaction(ec, acpi_ec_read_status(ec));
+ } while (time_before(jiffies, delay));
+- if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF)
+- break;
++// if (acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF)
++// break;
+ pr_debug(PREFIX "controller reset, restart transaction\n");
+ spin_lock_irqsave(&ec->curr_lock, flags);
+ start_transaction(ec);
+@@ -271,15 +272,25 @@ static int ec_check_ibf0(struct acpi_ec *ec)
+ return (status & ACPI_EC_FLAG_IBF) == 0;
+ }
+
++/* try to clean input buffer with burst_disable transaction */
++static int acpi_ec_clean_buffer(struct acpi_ec *ec)
++{
++ struct transaction t = {.command = ACPI_EC_BURST_DISABLE,
++ .wdata = NULL, .rdata = NULL,
++ .wlen = 0, .rlen = 0};
++ return acpi_ec_transaction_unlocked(ec, &t);
++}
++
+ static int ec_wait_ibf0(struct acpi_ec *ec)
+ {
++
+ unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY);
+ /* interrupt wait manually if GPE mode is not active */
+ while (time_before(jiffies, delay))
+ if (wait_event_timeout(ec->wait, ec_check_ibf0(ec),
+ msecs_to_jiffies(1)))
+ return 0;
+- return -ETIME;
++ return acpi_ec_clean_buffer(ec);
+ }
+
+ static int acpi_ec_transaction(struct acpi_ec *ec, struct transaction *t)
diff --git a/add-appleir-usb-driver.patch b/add-appleir-usb-driver.patch
new file mode 100644
index 0000000..ac68e71
--- /dev/null
+++ b/add-appleir-usb-driver.patch
@@ -0,0 +1,635 @@
+ Documentation/input/appleir.txt | 46 ++++
+ drivers/hid/hid-apple.c | 4 -
+ drivers/hid/hid-core.c | 5 +-
+ drivers/hid/hid-ids.h | 1 +
+ drivers/input/misc/Kconfig | 13 +
+ drivers/input/misc/Makefile | 1 +
+ drivers/input/misc/appleir.c | 477 +++++++++++++++++++++++++++++++++++++++
+ 7 files changed, 541 insertions(+), 6 deletions(-)
+ create mode 100644 Documentation/input/appleir.txt
+ create mode 100644 drivers/input/misc/appleir.c
+
+diff --git a/Documentation/input/appleir.txt b/Documentation/input/appleir.txt
+new file mode 100644
+index 0000000..0aaf5fe
+--- /dev/null
++++ b/Documentation/input/appleir.txt
+@@ -0,0 +1,46 @@
++Apple IR receiver Driver (appleir)
++----------------------------------
++ Copyright (C) 2009 Bastien Nocera <hadess@hadess.net>
++
++The appleir driver is a kernel input driver to handle Apple's IR
++receivers (and associated remotes) in the kernel.
++
++The driver is an input driver which only handles "official" remotes
++as built and sold by Apple.
++
++Authors
++-------
++
++James McKenzie (original driver)
++Alex Karpenko (05ac:8242 support)
++Greg Kroah-Hartman (cleanups and original submission)
++Bastien Nocera (further cleanups and suspend support)
++
++Supported hardware
++------------------
++
++- All Apple laptops and desktops from 2005 onwards, except:
++ - the unibody Macbook (2009)
++ - Mac Pro (all versions)
++- Apple TV (all revisions)
++
++The remote will only support the 6 buttons of the original remotes
++as sold by Apple. See the next section if you want to use other remotes
++or want to use lirc with the device instead of the kernel driver.
++
++Using lirc (native) instead of the kernel driver
++------------------------------------------------
++
++First, you will need to disable the kernel driver for the receiver.
++
++This can be achieved by passing quirks to the usbhid driver.
++The quirk line would be:
++usbhid.quirks=0x05ac:0x8242:0x08
++
++With 0x05ac being the vendor ID (Apple, you shouldn't need to change this)
++With 0x8242 being the product ID (check the output of lsusb for your hardware)
++And 0x08 being "HID_CONNECT_HIDDEV"
++
++This should force the creation of a hiddev device for the receiver, and
++make it usable under lirc.
++
+diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c
+index 4b96e7a..d1fdcd0 100644
+--- a/drivers/hid/hid-apple.c
++++ b/drivers/hid/hid-apple.c
+@@ -353,10 +353,6 @@ static void apple_remove(struct hid_device *hdev)
+ }
+
+ static const struct hid_device_id apple_devices[] = {
+- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL),
+- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
+- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4),
+- .driver_data = APPLE_HIDDEV | APPLE_IGNORE_HIDINPUT },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE),
+ .driver_data = APPLE_MIGHTYMOUSE | APPLE_INVERT_HWHEEL },
+
+diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c
+index 7d05c4b..3efb0fa 100644
+--- a/drivers/hid/hid-core.c
++++ b/drivers/hid/hid-core.c
+@@ -1252,8 +1252,6 @@ EXPORT_SYMBOL_GPL(hid_disconnect);
+ static const struct hid_device_id hid_blacklist[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_WCP32PU) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_A4TECH, USB_DEVICE_ID_A4TECH_X5_005D) },
+- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
+- { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_MIGHTYMOUSE) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ANSI) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_FOUNTAIN_ISO) },
+@@ -1539,6 +1537,9 @@ static const struct hid_device_id hid_ignore_list[] = {
+ { HID_USB_DEVICE(USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_AIRCABLE, USB_DEVICE_ID_AIRCABLE1) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232) },
++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
++ { HID_USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM)},
+ { HID_USB_DEVICE(USB_VENDOR_ID_ASUS, USB_DEVICE_ID_ASUS_LCM2)},
+ { HID_USB_DEVICE(USB_VENDOR_ID_AVERMEDIA, USB_DEVICE_ID_AVER_FM_MR800) },
+diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
+index adbef5d..c399110 100644
+--- a/drivers/hid/hid-ids.h
++++ b/drivers/hid/hid-ids.h
+@@ -90,6 +90,7 @@
+ #define USB_DEVICE_ID_APPLE_WELLSPRING3_JIS 0x0238
+ #define USB_DEVICE_ID_APPLE_FOUNTAIN_TP_ONLY 0x030a
+ #define USB_DEVICE_ID_APPLE_GEYSER1_TP_ONLY 0x030b
++#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
+ #define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
+ #define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
+
+diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
+index a9bb254..51b6684 100644
+--- a/drivers/input/misc/Kconfig
++++ b/drivers/input/misc/Kconfig
+@@ -148,6 +148,19 @@ config INPUT_KEYSPAN_REMOTE
+ To compile this driver as a module, choose M here: the module will
+ be called keyspan_remote.
+
++config INPUT_APPLEIR
++ tristate "Apple infrared receiver (built in)"
++ depends on USB_ARCH_HAS_HCD
++ select USB
++ help
++ Say Y here if you want to use a Apple infrared remote control. All
++ the Apple computers from 2005 onwards include such a port, except
++ the unibody Macbook (2009), and Mac Pros. This receiver is also
++ used in the Apple TV set-top box.
++
++ To compile this driver as a module, choose M here: the module will
++ be called appleir.
++
+ config INPUT_POWERMATE
+ tristate "Griffin PowerMate and Contour Jog support"
+ depends on USB_ARCH_HAS_HCD
+diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
+index a8b8485..041e6f5 100644
+--- a/drivers/input/misc/Makefile
++++ b/drivers/input/misc/Makefile
+@@ -5,6 +5,7 @@
+ # Each configuration option enables a list of files.
+
+ obj-$(CONFIG_INPUT_APANEL) += apanel.o
++obj-$(CONFIG_INPUT_APPLEIR) += appleir.o
+ obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o
+ obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o
+ obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o
+diff --git a/drivers/input/misc/appleir.c b/drivers/input/misc/appleir.c
+new file mode 100644
+index 0000000..6e332ab
+--- /dev/null
++++ b/drivers/input/misc/appleir.c
+@@ -0,0 +1,477 @@
++/*
++ * appleir: USB driver for the apple ir device
++ *
++ * Original driver written by James McKenzie
++ * Ported to recent 2.6 kernel versions by Greg Kroah-Hartman <gregkh@suse.de>
++ *
++ * Copyright (C) 2006 James McKenzie
++ * Copyright (C) 2008 Greg Kroah-Hartman <greg@kroah.com>
++ * Copyright (C) 2008 Novell Inc.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the Free
++ * Software Foundation, version 2.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/input.h>
++#include <linux/usb/input.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/usb.h>
++#include <linux/usb/input.h>
++#include <asm/unaligned.h>
++#include <asm/byteorder.h>
++
++#define DRIVER_VERSION "v1.2"
++#define DRIVER_AUTHOR "James McKenzie"
++#define DRIVER_DESC "Apple infrared receiver driver"
++#define DRIVER_LICENSE "GPL"
++
++MODULE_AUTHOR(DRIVER_AUTHOR);
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_LICENSE(DRIVER_LICENSE);
++
++#define USB_VENDOR_ID_APPLE 0x05ac
++#define USB_DEVICE_ID_APPLE_IRCONTROL 0x8240
++#define USB_DEVICE_ID_APPLE_ATV_IRCONTROL 0x8241
++#define USB_DEVICE_ID_APPLE_IRCONTROL4 0x8242
++
++#define URB_SIZE 32
++
++#define MAX_KEYS 8
++#define MAX_KEYS_MASK (MAX_KEYS - 1)
++
++#define dbginfo(dev, format, arg...) do { if (debug) dev_info(dev , format , ## arg); } while (0)
++
++static int debug;
++module_param(debug, int, 0644);
++MODULE_PARM_DESC(debug, "Enable extra debug messages and information");
++
++struct appleir {
++ struct input_dev *input_dev;
++ u8 *data;
++ dma_addr_t dma_buf;
++ struct usb_device *usbdev;
++ unsigned int flags;
++ struct urb *urb;
++ int timer_initted;
++ struct timer_list key_up_timer;
++ int current_key;
++ char phys[32];
++};
++
++static DEFINE_MUTEX(appleir_mutex);
++
++enum {
++ APPLEIR_OPENED = 0x1,
++ APPLEIR_SUSPENDED = 0x2,
++};
++
++static struct usb_device_id appleir_ids[] = {
++ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL) },
++ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_ATV_IRCONTROL) },
++ { USB_DEVICE(USB_VENDOR_ID_APPLE, USB_DEVICE_ID_APPLE_IRCONTROL4) },
++ {}
++};
++MODULE_DEVICE_TABLE(usb, appleir_ids);
++
++/* I have two devices both of which report the following */
++/* 25 87 ee 83 0a + */
++/* 25 87 ee 83 0c - */
++/* 25 87 ee 83 09 << */
++/* 25 87 ee 83 06 >> */
++/* 25 87 ee 83 05 >" */
++/* 25 87 ee 83 03 menu */
++/* 26 00 00 00 00 for key repeat*/
++
++/* Thomas Glanzmann reports the following responses */
++/* 25 87 ee ca 0b + */
++/* 25 87 ee ca 0d - */
++/* 25 87 ee ca 08 << */
++/* 25 87 ee ca 07 >> */
++/* 25 87 ee ca 04 >" */
++/* 25 87 ee ca 02 menu */
++/* 26 00 00 00 00 for key repeat*/
++/* He also observes the following event sometimes */
++/* sent after a key is release, which I interpret */
++/* as a flat battery message */
++/* 25 87 e0 ca 06 flat battery */
++
++/* Alexandre Karpenko reports the following responses for Device ID 0x8242 */
++/* 25 87 ee 47 0b + */
++/* 25 87 ee 47 0d - */
++/* 25 87 ee 47 08 << */
++/* 25 87 ee 47 07 >> */
++/* 25 87 ee 47 04 >" */
++/* 25 87 ee 47 02 menu */
++/* 26 87 ee 47 ** for key repeat (** is the code of the key being held) */
++
++static int keymap[MAX_KEYS] = {
++ KEY_RESERVED,
++ KEY_MENU,
++ KEY_PLAYPAUSE,
++ KEY_FORWARD,
++ KEY_BACK,
++ KEY_VOLUMEUP,
++ KEY_VOLUMEDOWN,
++ KEY_RESERVED,
++};
++
++static void dump_packet(struct appleir *appleir, char *msg, u8 *data, int len)
++{
++ int i;
++
++ printk(KERN_ERR "appleir: %s (%d bytes)", msg, len);
++
++ for (i = 0; i < len; ++i)
++ printk(" %02x", data[i]);
++ printk("\n");
++}
++
++static void key_up(struct appleir *appleir, int key)
++{
++ dbginfo (&appleir->input_dev->dev, "key %d up\n", key);
++ input_report_key(appleir->input_dev, key, 0);
++ input_sync(appleir->input_dev);
++}
++
++static void key_down(struct appleir *appleir, int key)
++{
++ dbginfo (&appleir->input_dev->dev, "key %d down\n", key);
++ input_report_key(appleir->input_dev, key, 1);
++ input_sync(appleir->input_dev);
++}
++
++static void battery_flat(struct appleir *appleir)
++{
++ dev_err(&appleir->input_dev->dev, "possible flat battery?\n");
++}
++
++static void key_up_tick(unsigned long data)
++{
++ struct appleir *appleir = (struct appleir *)data;
++
++ if (appleir->current_key) {
++ key_up(appleir, appleir->current_key);
++ appleir->current_key = 0;
++ }
++}
++
++static void new_data(struct appleir *appleir, u8 *data, int len)
++{
++ static const u8 keydown[] = { 0x25, 0x87, 0xee };
++ static const u8 keyrepeat[] = { 0x26, };
++ static const u8 flatbattery[] = { 0x25, 0x87, 0xe0 };
++
++ if (debug)
++ dump_packet(appleir, "received", data, len);
++
++ if (len != 5)
++ return;
++
++ if (!memcmp(data, keydown, sizeof(keydown))) {
++ /*If we already have a key down, take it up before marking */
++ /*this one down */
++ if (appleir->current_key)
++ key_up(appleir, appleir->current_key);
++ appleir->current_key = keymap[(data[4] >> 1) & MAX_KEYS_MASK];
++
++ key_down(appleir, appleir->current_key);
++ /*remote doesn't do key up, either pull them up, in the test */
++ /*above, or here set a timer which pulls them up after 1/8 s */
++ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
++
++ return;
++ }
++
++ if (!memcmp(data, keyrepeat, sizeof(keyrepeat))) {
++ key_down(appleir, appleir->current_key);
++ /*remote doesn't do key up, either pull them up, in the test */
++ /*above, or here set a timer which pulls them up after 1/8 s */
++ mod_timer(&appleir->key_up_timer, jiffies + HZ / 8);
++ return;
++ }
++
++ if (!memcmp(data, flatbattery, sizeof(flatbattery))) {
++ battery_flat(appleir);
++ /* Fall through */
++ }
++
++ dump_packet(appleir, "unknown packet", data, len);
++}
++
++static void appleir_urb(struct urb *urb)
++{
++ struct appleir *appleir = urb->context;
++ int status = urb->status;
++ int retval;
++
++ switch (status) {
++ case 0:
++ new_data(appleir, urb->transfer_buffer, urb->actual_length);
++ break;
++ case -ECONNRESET:
++ case -ENOENT:
++ case -ESHUTDOWN:
++ /* this urb is terminated, clean up */
++ dbginfo(&appleir->input_dev->dev, "%s - urb shutting down with status: %d", __func__,
++ urb->status);
++ return;
++ default:
++ dbginfo(&appleir->input_dev->dev, "%s - nonzero urb status received: %d", __func__,
++ urb->status);
++ }
++
++ retval = usb_submit_urb(urb, GFP_ATOMIC);
++ if (retval)
++ err("%s - usb_submit_urb failed with result %d", __func__,
++ retval);
++}
++
++static int appleir_open(struct input_dev *dev)
++{
++ struct appleir *appleir = input_get_drvdata(dev);
++ struct usb_interface *intf = usb_ifnum_to_if(appleir->usbdev, 0);
++ int r;
++
++ r = usb_autopm_get_interface(intf);
++ if (r) {
++ dev_err(&intf->dev,
++ "%s(): usb_autopm_get_interface() = %d\n", __func__, r);
++ return r;
++ }
++
++ mutex_lock(&appleir_mutex);
++
++ if (usb_submit_urb(appleir->urb, GFP_KERNEL)) {
++ r = -EIO;
++ goto fail;
++ }
++
++ appleir->flags |= APPLEIR_OPENED;
++
++ mutex_unlock(&appleir_mutex);
++
++ usb_autopm_put_interface(intf);
++
++ return 0;
++fail:
++ mutex_unlock(&appleir_mutex);
++ usb_autopm_put_interface(intf);
++ return r;
++}
++
++static void appleir_close(struct input_dev *dev)
++{
++ struct appleir *appleir = input_get_drvdata(dev);
++
++ mutex_lock(&appleir_mutex);
++
++ if (!(appleir->flags & APPLEIR_SUSPENDED)) {
++ usb_kill_urb(appleir->urb);
++ del_timer_sync(&appleir->key_up_timer);
++ }
++
++ appleir->flags &= ~APPLEIR_OPENED;
++
++ mutex_unlock(&appleir_mutex);
++}
++
++static int appleir_probe(struct usb_interface *intf,
++ const struct usb_device_id *id)
++{
++ struct usb_device *dev = interface_to_usbdev(intf);
++ struct usb_endpoint_descriptor *endpoint;
++ struct appleir *appleir = NULL;
++ struct input_dev *input_dev;
++ int retval = -ENOMEM;
++ int i;
++
++ appleir = kzalloc(sizeof(struct appleir), GFP_KERNEL);
++ if (!appleir)
++ goto fail;
++
++ appleir->data = usb_buffer_alloc(dev, URB_SIZE, GFP_KERNEL,
++ &appleir->dma_buf);
++ if (!appleir->data)
++ goto fail;
++
++ appleir->urb = usb_alloc_urb(0, GFP_KERNEL);
++ if (!appleir->urb)
++ goto fail;
++
++ appleir->usbdev = dev;
++
++ input_dev = input_allocate_device();
++ if (!input_dev)
++ goto fail;
++
++ appleir->input_dev = input_dev;
++
++ usb_make_path(dev, appleir->phys, sizeof(appleir->phys));
++ strlcpy(appleir->phys, "/input0", sizeof(appleir->phys));
++
++ input_dev->name = "Apple infrared remote control driver";
++ input_dev->phys = appleir->phys;
++ usb_to_input_id(dev, &input_dev->id);
++ input_dev->dev.parent = &intf->dev;
++ input_set_drvdata(input_dev, appleir);
++
++ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
++ input_dev->ledbit[0] = 0;
++
++ for (i = 0; i < MAX_KEYS; i++)
++ set_bit(keymap[i], input_dev->keybit);
++
++ clear_bit(0, input_dev->keybit);
++
++ input_dev->open = appleir_open;
++ input_dev->close = appleir_close;
++
++ endpoint = &intf->cur_altsetting->endpoint[0].desc;
++
++ usb_fill_int_urb(appleir->urb, dev,
++ usb_rcvintpipe(dev, endpoint->bEndpointAddress),
++ appleir->data, 8,
++ appleir_urb, appleir, endpoint->bInterval);
++
++ appleir->urb->transfer_dma = appleir->dma_buf;
++ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
++
++ usb_set_intfdata(intf, appleir);
++
++ init_timer(&appleir->key_up_timer);
++
++ appleir->key_up_timer.function = key_up_tick;
++ appleir->key_up_timer.data = (unsigned long)appleir;
++
++ appleir->timer_initted++;
++
++ retval = input_register_device(appleir->input_dev);
++ if (retval)
++ goto fail;
++
++ return 0;
++
++fail:
++ printk(KERN_WARNING "Failed to load appleir\n");
++ if (appleir) {
++ if (appleir->data)
++ usb_buffer_free(dev, URB_SIZE, appleir->data,
++ appleir->dma_buf);
++
++ if (appleir->timer_initted)
++ del_timer_sync(&appleir->key_up_timer);
++
++ if (appleir->input_dev)
++ input_free_device(appleir->input_dev);
++
++ kfree(appleir);
++ }
++
++ return retval;
++}
++
++static void appleir_disconnect(struct usb_interface *intf)
++{
++ struct appleir *appleir = usb_get_intfdata(intf);
++
++ usb_set_intfdata(intf, NULL);
++ if (appleir) {
++ input_unregister_device(appleir->input_dev);
++ if (appleir->timer_initted)
++ del_timer_sync(&appleir->key_up_timer);
++ usb_kill_urb(appleir->urb);
++ usb_free_urb(appleir->urb);
++ usb_buffer_free(interface_to_usbdev(intf), URB_SIZE,
++ appleir->data, appleir->dma_buf);
++ kfree(appleir);
++ }
++}
++
++static int appleir_suspend(struct usb_interface *interface,
++ pm_message_t message)
++{
++ struct appleir *appleir;
++
++ appleir = usb_get_intfdata(interface);
++
++ mutex_lock(&appleir_mutex);
++
++ if (appleir->flags & APPLEIR_OPENED) {
++ usb_kill_urb(appleir->urb);
++ del_timer_sync(&appleir->key_up_timer);
++ }
++
++ appleir->flags |= APPLEIR_SUSPENDED;
++
++ mutex_unlock(&appleir_mutex);
++
++ return 0;
++}
++
++static int appleir_resume(struct usb_interface *interface)
++{
++ struct appleir *appleir;
++
++ appleir = usb_get_intfdata(interface);
++
++ mutex_lock(&appleir_mutex);
++
++ if (appleir->flags & APPLEIR_OPENED) {
++ struct usb_endpoint_descriptor *endpoint;
++
++ endpoint = &interface->cur_altsetting->endpoint[0].desc;
++ usb_fill_int_urb(appleir->urb, appleir->usbdev,
++ usb_rcvintpipe(appleir->usbdev, endpoint->bEndpointAddress),
++ appleir->data, 8,
++ appleir_urb, appleir, endpoint->bInterval);
++ appleir->urb->transfer_dma = appleir->dma_buf;
++ appleir->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
++
++ init_timer(&appleir->key_up_timer);
++
++ appleir->key_up_timer.function = key_up_tick;
++ appleir->key_up_timer.data = (unsigned long)appleir;
++ }
++
++ appleir->flags &= ~APPLEIR_SUSPENDED;
++
++ mutex_unlock(&appleir_mutex);
++
++ return 0;
++}
++
++static struct usb_driver appleir_driver = {
++ .name = "appleir",
++ .probe = appleir_probe,
++ .disconnect = appleir_disconnect,
++ .suspend = appleir_suspend,
++ .resume = appleir_resume,
++ .reset_resume = appleir_resume,
++ .id_table = appleir_ids,
++ .supports_autosuspend = 1,
++};
++
++static int __init appleir_init(void)
++{
++ int retval;
++
++ retval = usb_register(&appleir_driver);
++ if (retval)
++ goto out;
++ printk(KERN_INFO DRIVER_VERSION ":" DRIVER_DESC);
++out:
++ return retval;
++}
++
++static void __exit appleir_exit(void)
++{
++ usb_deregister(&appleir_driver);
++}
++
++module_init(appleir_init);
++module_exit(appleir_exit);
+--
+1.6.5.2
+
diff --git a/btrfs-prohibit-a-operation-of-changing-acls-mask-when-noacl-mount-option-is-used.patch b/btrfs-prohibit-a-operation-of-changing-acls-mask-when-noacl-mount-option-is-used.patch
new file mode 100644
index 0000000..0792603
--- /dev/null
+++ b/btrfs-prohibit-a-operation-of-changing-acls-mask-when-noacl-mount-option-is-used.patch
@@ -0,0 +1,42 @@
+From: Shi Weihua <shiwh@cn.fujitsu.com>
+Date: Tue, 18 May 2010 00:51:54 +0000 (+0000)
+Subject: Btrfs: prohibit a operation of changing acl's mask when noacl mount option used
+X-Git-Tag: v2.6.35-rc3~3^2~3
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=731e3d1b
+
+Btrfs: prohibit a operation of changing acl's mask when noacl mount option used
+
+when used Posix File System Test Suite(pjd-fstest) to test btrfs,
+some cases about setfacl failed when noacl mount option used.
+I simplified used commands in pjd-fstest, and the following steps
+can reproduce it.
+------------------------
+# cd btrfs-part/
+# mkdir aaa
+# setfacl -m m::rw aaa <- successed, but not expected by pjd-fstest.
+------------------------
+I checked ext3, a warning message occured, like as:
+ setfacl: aaa/: Operation not supported
+Certainly, it's expected by pjd-fstest.
+
+So, i compared acl.c of btrfs and ext3. Based on that, a patch created.
+Fortunately, it works.
+
+Signed-off-by: Shi Weihua <shiwh@cn.fujitsu.com>
+Signed-off-by: Chris Mason <chris.mason@oracle.com>
+---
+
+diff --git a/fs/btrfs/acl.c b/fs/btrfs/acl.c
+index 6b4d0cc..a372985 100644
+--- a/fs/btrfs/acl.c
++++ b/fs/btrfs/acl.c
+@@ -163,6 +163,9 @@ static int btrfs_xattr_acl_set(struct dentry *dentry, const char *name,
+ if (!is_owner_or_cap(inode))
+ return -EPERM;
+
++ if (!IS_POSIXACL(inode))
++ return -EOPNOTSUPP;
++
+ if (value) {
+ acl = posix_acl_from_xattr(value, size);
+ if (acl == NULL) {
diff --git a/cifs-fix-malicious-redirect-problem-in-the-dns-lookup-code.patch b/cifs-fix-malicious-redirect-problem-in-the-dns-lookup-code.patch
new file mode 100644
index 0000000..083605a
--- /dev/null
+++ b/cifs-fix-malicious-redirect-problem-in-the-dns-lookup-code.patch
@@ -0,0 +1,203 @@
+From: David Howells <dhowells@redhat.com>
+Date: Thu, 22 Jul 2010 11:53:18 +0000 (+0100)
+Subject: CIFS: Fix a malicious redirect problem in the DNS lookup code
+X-Git-Tag: v2.6.35-rc6~6
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=4c0c03ca54f72fdd5912516ad0a23ec5cf01bda7
+
+CIFS: Fix a malicious redirect problem in the DNS lookup code
+
+[ trivial backport to 2.6.32 : cebbert@redhat.com ]
+
+Fix the security problem in the CIFS filesystem DNS lookup code in which a
+malicious redirect could be installed by a random user by simply adding a
+result record into one of their keyrings with add_key() and then invoking a
+CIFS CFS lookup [CVE-2010-2524].
+
+This is done by creating an internal keyring specifically for the caching of
+DNS lookups. To enforce the use of this keyring, the module init routine
+creates a set of override credentials with the keyring installed as the thread
+keyring and instructs request_key() to only install lookup result keys in that
+keyring.
+
+The override is then applied around the call to request_key().
+
+This has some additional benefits when a kernel service uses this module to
+request a key:
+
+ (1) The result keys are owned by root, not the user that caused the lookup.
+
+ (2) The result keys don't pop up in the user's keyrings.
+
+ (3) The result keys don't come out of the quota of the user that caused the
+ lookup.
+
+The keyring can be viewed as root by doing cat /proc/keys:
+
+2a0ca6c3 I----- 1 perm 1f030000 0 0 keyring .dns_resolver: 1/4
+
+It can then be listed with 'keyctl list' by root.
+
+ # keyctl list 0x2a0ca6c3
+ 1 key in keyring:
+ 726766307: --alswrv 0 0 dns_resolver: foo.bar.com
+
+Signed-off-by: David Howells <dhowells@redhat.com>
+Reviewed-and-Tested-by: Jeff Layton <jlayton@redhat.com>
+Acked-by: Steve French <smfrench@gmail.com>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+---
+
+diff --git a/fs/cifs/cifsfs.c b/fs/cifs/cifsfs.c
+index 484e52b..2cb1a70 100644
+--- a/fs/cifs/cifsfs.c
++++ b/fs/cifs/cifsfs.c
+@@ -923,7 +923,7 @@ init_cifs(void)
+ goto out_unregister_filesystem;
+ #endif
+ #ifdef CONFIG_CIFS_DFS_UPCALL
+- rc = register_key_type(&key_type_dns_resolver);
++ rc = cifs_init_dns_resolver();
+ if (rc)
+ goto out_unregister_key_type;
+ #endif
+@@ -935,7 +935,7 @@ init_cifs(void)
+
+ out_unregister_resolver_key:
+ #ifdef CONFIG_CIFS_DFS_UPCALL
+- unregister_key_type(&key_type_dns_resolver);
++ cifs_exit_dns_resolver();
+ out_unregister_key_type:
+ #endif
+ #ifdef CONFIG_CIFS_UPCALL
+@@ -961,7 +961,7 @@ exit_cifs(void)
+ cifs_proc_clean();
+ #ifdef CONFIG_CIFS_DFS_UPCALL
+ cifs_dfs_release_automount_timer();
+- unregister_key_type(&key_type_dns_resolver);
++ cifs_exit_dns_resolver();
+ #endif
+ #ifdef CONFIG_CIFS_UPCALL
+ unregister_key_type(&cifs_spnego_key_type);
+diff --git a/fs/cifs/dns_resolve.c b/fs/cifs/dns_resolve.c
+index 4db2c5e..49315cb 100644
+--- a/fs/cifs/dns_resolve.c
++++ b/fs/cifs/dns_resolve.c
+@@ -24,12 +24,17 @@
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
++#include <linux/slab.h>
++#include <linux/keyctl.h>
++#include <linux/key-type.h>
+ #include <keys/user-type.h>
+ #include "dns_resolve.h"
+ #include "cifsglob.h"
+ #include "cifsproto.h"
+ #include "cifs_debug.h"
+
++static const struct cred *dns_resolver_cache;
++
+ /* Checks if supplied name is IP address
+ * returns:
+ * 1 - name is IP
+@@ -94,6 +99,7 @@ struct key_type key_type_dns_resolver = {
+ int
+ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
+ {
++ const struct cred *saved_cred;
+ int rc = -EAGAIN;
+ struct key *rkey = ERR_PTR(-EAGAIN);
+ char *name;
+@@ -133,8 +139,15 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
+ goto skip_upcall;
+ }
+
++ saved_cred = override_creds(dns_resolver_cache);
+ rkey = request_key(&key_type_dns_resolver, name, "");
++ revert_creds(saved_cred);
+ if (!IS_ERR(rkey)) {
++ if (!(rkey->perm & KEY_USR_VIEW)) {
++ down_read(&rkey->sem);
++ rkey->perm |= KEY_USR_VIEW;
++ up_read(&rkey->sem);
++ }
+ len = rkey->type_data.x[0];
+ data = rkey->payload.data;
+ } else {
+@@ -165,4 +178,61 @@ out:
+ return rc;
+ }
+
++int __init cifs_init_dns_resolver(void)
++{
++ struct cred *cred;
++ struct key *keyring;
++ int ret;
++
++ printk(KERN_NOTICE "Registering the %s key type\n",
++ key_type_dns_resolver.name);
++
++ /* create an override credential set with a special thread keyring in
++ * which DNS requests are cached
++ *
++ * this is used to prevent malicious redirections from being installed
++ * with add_key().
++ */
++ cred = prepare_kernel_cred(NULL);
++ if (!cred)
++ return -ENOMEM;
++
++ keyring = key_alloc(&key_type_keyring, ".dns_resolver", 0, 0, cred,
++ (KEY_POS_ALL & ~KEY_POS_SETATTR) |
++ KEY_USR_VIEW | KEY_USR_READ,
++ KEY_ALLOC_NOT_IN_QUOTA);
++ if (IS_ERR(keyring)) {
++ ret = PTR_ERR(keyring);
++ goto failed_put_cred;
++ }
++
++ ret = key_instantiate_and_link(keyring, NULL, 0, NULL, NULL);
++ if (ret < 0)
++ goto failed_put_key;
++
++ ret = register_key_type(&key_type_dns_resolver);
++ if (ret < 0)
++ goto failed_put_key;
++
++ /* instruct request_key() to use this special keyring as a cache for
++ * the results it looks up */
++ cred->thread_keyring = keyring;
++ cred->jit_keyring = KEY_REQKEY_DEFL_THREAD_KEYRING;
++ dns_resolver_cache = cred;
++ return 0;
++
++failed_put_key:
++ key_put(keyring);
++failed_put_cred:
++ put_cred(cred);
++ return ret;
++}
+
++void __exit cifs_exit_dns_resolver(void)
++{
++ key_revoke(dns_resolver_cache->thread_keyring);
++ unregister_key_type(&key_type_dns_resolver);
++ put_cred(dns_resolver_cache);
++ printk(KERN_NOTICE "Unregistered %s key type\n",
++ key_type_dns_resolver.name);
++}
+diff --git a/fs/cifs/dns_resolve.h b/fs/cifs/dns_resolve.h
+index 966e928..26b9eaa 100644
+--- a/fs/cifs/dns_resolve.h
++++ b/fs/cifs/dns_resolve.h
+@@ -24,8 +24,8 @@
+ #define _DNS_RESOLVE_H
+
+ #ifdef __KERNEL__
+-#include <linux/key-type.h>
+-extern struct key_type key_type_dns_resolver;
++extern int __init cifs_init_dns_resolver(void);
++extern void __exit cifs_exit_dns_resolver(void);
+ extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr);
+ #endif /* KERNEL */
+
diff --git a/config-arm b/config-arm
new file mode 100644
index 0000000..0c90cd5
--- /dev/null
+++ b/config-arm
@@ -0,0 +1,102 @@
+CONFIG_ARM=y
+CONFIG_SYS_SUPPORTS_APM_EMULATION=y
+# CONFIG_SMP is not set
+
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+
+CONFIG_ARCH_VERSATILE=y
+CONFIG_ARCH_VERSATILE_PB=y
+CONFIG_MACH_VERSATILE_AB=y
+
+CONFIG_HIGHMEM=y
+# CONFIG_HIGHPTE is not set
+
+# CONFIG_CPU_ICACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_DISABLE is not set
+# CONFIG_CPU_DCACHE_WRITETHROUGH is not set
+# CONFIG_CPU_CACHE_ROUND_ROBIN is not set
+
+CONFIG_ZBOOT_ROM_TEXT=0
+CONFIG_ZBOOT_ROM_BSS=0
+
+# CONFIG_XIP_KERNEL is not set
+
+CONFIG_ATAGS_PROC=y
+
+# CONFIG_FPE_NWFPE is not set
+CONFIG_FPE_FASTFPE=y
+CONFIG_VFP=y
+
+CONFIG_PM=y
+# CONFIG_PM_DEBUG is not set
+# CONFIG_PM_TRACE is not set
+CONFIG_SUSPEND=y
+# CONFIG_PM_TEST_SUSPEND is not set
+CONFIG_APM_EMULATION=y
+
+CONFIG_ARM_THUMB=y
+
+CONFIG_AEABI=y
+CONFIG_OABI_COMPAT=y
+
+# CONFIG_UACCESS_WITH_MEMCPY is not set
+
+CONFIG_CMDLINE="console=ttyAM0,115200 root=/dev/sda1 rootdelay=20"
+
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+
+# CONFIG_CPU_IDLE is not set
+
+CONFIG_LEDS=y
+CONFIG_LEDS_CPU=y
+
+CONFIG_MTD_AFS_PARTS=y
+CONFIG_MTD_ARM_INTEGRATOR=y
+CONFIG_MTD_IMPA7=y
+
+CONFIG_AX88796=m
+CONFIG_AX88796_93CX6=y
+CONFIG_SMC91X=m
+CONFIG_DM9000=m
+CONFIG_DM9000_DEBUGLEVEL=4
+# CONFIG_DM9000_FORCE_SIMPLE_PHY_POLL is not set
+CONFIG_SMC911X=m
+CONFIG_SMSC911X=m
+
+CONFIG_SERIO_AMBAKMI=m
+
+CONFIG_SERIAL_AMBA_PL011=y
+CONFIG_SERIAL_AMBA_PL011_CONSOLE=y
+
+CONFIG_I2C_VERSATILE=y
+
+CONFIG_THERMAL=y
+
+# CONFIG_MFD_T7L66XB is not set
+# CONFIG_MFD_TC6387XB is not set
+
+CONFIG_FB_ARMCLCD=m
+
+CONFIG_SND_ARM=y
+CONFIG_SND_ARMAACI=m
+
+CONFIG_USB_MUSB_HDRC=m
+# CONFIG_MUSB_PIO_ONLY is not set
+CONFIG_USB_TUSB6010=y
+# CONFIG_USB_MUSB_DEBUG is not set
+
+CONFIG_MMC_ARMMMCI=m
+
+CONFIG_RTC_DRV_PL030=m
+CONFIG_RTC_DRV_PL031=m
+
+# CONFIG_SGI_IOC4 is not set
+
+# CONFIG_DEBUG_USER is not set
+# CONFIG_DEBUG_ERRORS is not set
+# CONFIG_DEBUG_LL is not set
+
+CONFIG_ARM_UNWIND=y
+
+CONFIG_RCU_FANOUT=32
diff --git a/config-debug b/config-debug
new file mode 100644
index 0000000..0ded62b
--- /dev/null
+++ b/config-debug
@@ -0,0 +1,64 @@
+CONFIG_SND_VERBOSE_PRINTK=y
+CONFIG_SND_DEBUG=y
+CONFIG_SND_PCM_XRUN_DEBUG=y
+
+CONFIG_DEBUG_MUTEXES=y
+CONFIG_DEBUG_RT_MUTEXES=y
+CONFIG_DEBUG_LOCK_ALLOC=y
+CONFIG_PROVE_LOCKING=y
+CONFIG_DEBUG_VM=y
+CONFIG_DEBUG_SPINLOCK=y
+
+CONFIG_FAULT_INJECTION=y
+CONFIG_FAILSLAB=y
+CONFIG_FAIL_PAGE_ALLOC=y
+CONFIG_FAIL_MAKE_REQUEST=y
+CONFIG_FAULT_INJECTION_DEBUG_FS=y
+CONFIG_FAULT_INJECTION_STACKTRACE_FILTER=y
+CONFIG_FAIL_IO_TIMEOUT=y
+
+CONFIG_SLUB_DEBUG_ON=y
+
+CONFIG_LOCK_STAT=y
+
+CONFIG_DEBUG_STACK_USAGE=y
+
+CONFIG_ACPI_DEBUG=y
+# CONFIG_ACPI_DEBUG_FUNC_TRACE is not set
+
+CONFIG_DEBUG_SG=y
+
+# CONFIG_DEBUG_PAGEALLOC is not set
+
+CONFIG_DEBUG_WRITECOUNT=y
+CONFIG_DEBUG_OBJECTS=y
+# CONFIG_DEBUG_OBJECTS_SELFTEST is not set
+CONFIG_DEBUG_OBJECTS_FREE=y
+CONFIG_DEBUG_OBJECTS_TIMERS=y
+CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT=1
+
+CONFIG_X86_PTDUMP=y
+
+CONFIG_CAN_DEBUG_DEVICES=y
+
+CONFIG_MODULE_FORCE_UNLOAD=y
+
+CONFIG_SYSCTL_SYSCALL_CHECK=y
+
+CONFIG_DEBUG_NOTIFIERS=y
+
+CONFIG_DMA_API_DEBUG=y
+
+CONFIG_MMIOTRACE=y
+
+CONFIG_DEBUG_CREDENTIALS=y
+
+CONFIG_EXT4_DEBUG=y
+
+CONFIG_DEBUG_PERF_USE_VMALLOC=y
+
+# off in both production debug and nodebug builds,
+# on in rawhide nodebug builds
+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
+
+CONFIG_JBD2_DEBUG=y
diff --git a/config-generic b/config-generic
new file mode 100644
index 0000000..3b99920
--- /dev/null
+++ b/config-generic
@@ -0,0 +1,4061 @@
+#
+# Automatically generated make config: don't edit
+#
+CONFIG_MMU=y
+CONFIG_SMP=y
+CONFIG_HOTPLUG_CPU=y
+CONFIG_LOCALVERSION=""
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+CONFIG_HOTPLUG=y
+CONFIG_UEVENT_HELPER_PATH=""
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_STANDALONE=y
+CONFIG_PREVENT_FIRMWARE_BUILD=y
+
+CONFIG_BUILD_DOCSRC=y
+
+#
+# General setup
+#
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_SWAP=y
+CONFIG_SYSVIPC=y
+CONFIG_BSD_PROCESS_ACCT=y
+# CONFIG_BSD_PROCESS_ACCT_V3 is not set
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_SYSCTL=y
+CONFIG_LOG_BUF_SHIFT=17
+# CONFIG_IKCONFIG is not set
+# CONFIG_EMBEDDED is not set
+CONFIG_KALLSYMS=y
+CONFIG_KALLSYMS_ALL=y
+CONFIG_KALLSYMS_EXTRA_PASS=y
+CONFIG_FUTEX=y
+CONFIG_EPOLL=y
+CONFIG_IOSCHED_NOOP=y
+CONFIG_IOSCHED_AS=y
+CONFIG_IOSCHED_DEADLINE=y
+CONFIG_IOSCHED_CFQ=y
+CONFIG_DEFAULT_CFQ=y
+CONFIG_USER_NS=y
+CONFIG_PID_NS=y
+CONFIG_UTS_NS=y
+CONFIG_IPC_NS=y
+CONFIG_NET_NS=y
+
+CONFIG_LANGWELL_IPC=n
+
+CONFIG_POSIX_MQUEUE=y
+# CONFIG_PREEMPT_NONE is not set
+CONFIG_PREEMPT_VOLUNTARY=y
+# CONFIG_PREEMPT is not set
+
+CONFIG_SLUB=y
+# CONFIG_SLUB_STATS is not set
+
+CONFIG_MISC_DEVICES=y
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+# CONFIG_MODULE_FORCE_LOAD is not set
+# -- MODULE_FORCE_UNLOAD is controlled by config-debug/nodebug
+# CONFIG_MODVERSIONS is not set
+CONFIG_MODULE_SRCVERSION_ALL=y
+
+#
+# Bus options (PCI, PCMCIA, EISA, MCA, ISA)
+#
+CONFIG_PCI=y
+# CONFIG_PCI_DEBUG is not set
+CONFIG_PCI_STUB=y
+CONFIG_PCI_IOV=y
+CONFIG_HT_IRQ=y
+CONFIG_PCI_MSI=y
+CONFIG_PCI_MSI_DEFAULT_ON=y
+CONFIG_PCIEPORTBUS=y
+CONFIG_PCIEAER=y
+CONFIG_PCIEASPM=y
+# CONFIG_PCIEASPM_DEBUG is not set
+CONFIG_PCIE_ECRC=y
+CONFIG_PCIEAER_INJECT=m
+CONFIG_HOTPLUG_PCI_PCIE=y
+CONFIG_HOTPLUG_PCI_FAKE=m
+CONFIG_PCI_LEGACY=y
+
+CONFIG_ISA=y
+# CONFIG_EISA is not set
+# CONFIG_MCA is not set
+# CONFIG_SCx200 is not set
+
+#
+# PCMCIA/CardBus support
+#
+CONFIG_PCMCIA=y
+CONFIG_PCMCIA_LOAD_CIS=y
+# CONFIG_PCMCIA_DEBUG is not set
+CONFIG_YENTA=m
+CONFIG_CARDBUS=y
+CONFIG_I82092=m
+CONFIG_PD6729=m
+CONFIG_PCMCIA_IOCTL=y
+
+CONFIG_PCCARD=y
+CONFIG_MMC=m
+CONFIG_MMC_BLOCK_BOUNCE=y
+CONFIG_SDIO_UART=m
+# CONFIG_MMC_TEST is not set
+# CONFIG_MMC_DEBUG is not set
+# CONFIG_MMC_UNSAFE_RESUME is not set
+CONFIG_MMC_BLOCK=m
+CONFIG_MMC_RICOH_MMC=m
+CONFIG_MMC_SDHCI=m
+CONFIG_MMC_SDHCI_PCI=m
+CONFIG_MMC_SDRICOH_CS=m
+CONFIG_MMC_TIFM_SD=m
+CONFIG_MMC_WBSD=m
+CONFIG_MMC_VIA_SDMMC=m
+CONFIG_MMC_SDHCI_PLTFM=m
+CONFIG_MMC_CB710=m
+
+CONFIG_CB710_CORE=m
+# CONFIG_CB710_DEBUG is not set
+
+CONFIG_INFINIBAND=m
+CONFIG_INFINIBAND_MTHCA=m
+# CONFIG_INFINIBAND_MTHCA_DEBUG is not set
+CONFIG_INFINIBAND_IPOIB=m
+CONFIG_INFINIBAND_IPOIB_DEBUG=y
+CONFIG_INFINIBAND_IPOIB_DEBUG_DATA=y
+CONFIG_INFINIBAND_IPOIB_CM=y
+CONFIG_INFINIBAND_SRP=m
+CONFIG_INFINIBAND_USER_MAD=m
+CONFIG_INFINIBAND_USER_ACCESS=m
+CONFIG_INFINIBAND_IPATH=m
+CONFIG_INFINIBAND_ISER=m
+CONFIG_INFINIBAND_AMSO1100=m
+# CONFIG_INFINIBAND_AMSO1100_DEBUG is not set
+CONFIG_INFINIBAND_CXGB3=m
+# CONFIG_INFINIBAND_CXGB3_DEBUG is not set
+CONFIG_MLX4_INFINIBAND=m
+CONFIG_INFINIBAND_NES=m
+# CONFIG_INFINIBAND_NES_DEBUG is not set
+
+#
+# Executable file formats
+#
+CONFIG_BINFMT_ELF=y
+CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
+# CONFIG_BINFMT_AOUT is not set
+CONFIG_BINFMT_MISC=y
+
+#
+# Device Drivers
+#
+
+#
+# Generic Driver Options
+#
+CONFIG_FW_LOADER=y
+# CONFIG_FIRMWARE_IN_KERNEL is not set
+CONFIG_EXTRA_FIRMWARE=""
+
+# CONFIG_SPI is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+CONFIG_MTD=m
+# CONFIG_MTD_DEBUG is not set
+CONFIG_MTD_PARTITIONS=y
+CONFIG_MTD_AR7_PARTS=m
+CONFIG_MTD_CONCAT=m
+CONFIG_MTD_CMDLINE_PARTS=y
+#
+# User Modules And Translation Layers
+#
+CONFIG_MTD_CHAR=m
+CONFIG_MTD_BLOCK=m
+CONFIG_MTD_BLOCK_RO=m
+CONFIG_MTD_BLOCK2MTD=m
+
+CONFIG_MTD_OOPS=m
+# CONFIG_MTD_INTEL_VR_NOR is not set
+CONFIG_MTD_ALAUDA=m
+
+CONFIG_FTL=m
+CONFIG_NFTL=m
+CONFIG_NFTL_RW=y
+CONFIG_INFTL=m
+CONFIG_RFD_FTL=m
+CONFIG_SSFDC=m
+
+CONFIG_MTD_UBI=m
+CONFIG_MTD_UBI_WL_THRESHOLD=4096
+CONFIG_MTD_UBI_BEB_RESERVE=1
+# CONFIG_MTD_UBI_GLUEBI is not set
+# CONFIG_MTD_UBI_DEBUG is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=m
+CONFIG_MTD_JEDECPROBE=m
+CONFIG_MTD_GEN_PROBE=m
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+CONFIG_MTD_CFI_INTELEXT=m
+CONFIG_MTD_CFI_AMDSTD=m
+CONFIG_MTD_CFI_STAA=m
+CONFIG_MTD_RAM=m
+CONFIG_MTD_ROM=m
+CONFIG_MTD_ABSENT=m
+
+#
+# Mapping drivers for chip access
+#
+CONFIG_MTD_COMPLEX_MAPPINGS=y
+# CONFIG_MTD_PHYSMAP is not set
+CONFIG_MTD_SC520CDP=m
+CONFIG_MTD_NETSC520=m
+# CONFIG_MTD_SBC_GXX is not set
+# CONFIG_MTD_SCx200_DOCFLASH is not set
+# CONFIG_MTD_AMD76XROM is not set
+CONFIG_MTD_SCB2_FLASH=m
+# CONFIG_MTD_NETtel is not set
+# CONFIG_MTD_DILNETPC is not set
+# CONFIG_MTD_L440GX is not set
+CONFIG_MTD_PCI=m
+CONFIG_MTD_TS5500=m
+# CONFIG_MTD_GPIO_ADDR is not set
+
+#
+# Self-contained MTD device drivers
+#
+CONFIG_MTD_PMC551=m
+# CONFIG_MTD_PMC551_BUGFIX is not set
+# CONFIG_MTD_PMC551_DEBUG is not set
+# CONFIG_MTD_SLRAM is not set
+CONFIG_MTD_MTDRAM=m
+CONFIG_MTDRAM_TOTAL_SIZE=4096
+CONFIG_MTDRAM_ERASE_SIZE=128
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_DOCPROBE is not set
+# CONFIG_MTD_DOCPROBE_ADVANCED is not set
+# CONFIG_MTD_DOCPROBE_ADDRESS is not set
+
+#
+# NAND Flash Device Drivers
+#
+CONFIG_MTD_NAND=m
+# CONFIG_MTD_NAND_MUSEUM_IDS is not set
+# CONFIG_MTD_NAND_PLATFORM is not set
+# CONFIG_MTD_NAND_VERIFY_WRITE is not set
+# CONFIG_MTD_NAND_CAFE is not set
+CONFIG_MTD_NAND_IDS=m
+CONFIG_MTD_NAND_NANDSIM=m
+# CONFIG_MTD_ONENAND is not set
+CONFIG_MTD_NAND_ECC_SMC=y
+CONFIG_MTD_NAND_CS553X=m
+
+CONFIG_MTD_REDBOOT_PARTS=m
+# CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED is not set
+# CONFIG_MTD_REDBOOT_PARTS_READONLY is not set
+CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK=-1
+# CONFIG_MTD_XIP is not set
+# CONFIG_MTD_ICHXROM is not set
+# CONFIG_MTD_PHRAM is not set
+CONFIG_MTD_NAND_DISKONCHIP=m
+# CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADVANCED is not set
+CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS=0
+# CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE is not set
+# CONFIG_MTD_PLATRAM is not set
+
+# CONFIG_MTD_TESTS is not set
+CONFIG_MTD_LPDDR=m
+CONFIG_MTD_QINFO_PROBE=m
+
+#
+# Parallel port support
+#
+CONFIG_PARPORT=m
+CONFIG_PARPORT_PC=m
+CONFIG_PARPORT_SERIAL=m
+# CONFIG_PARPORT_PC_FIFO is not set
+# CONFIG_PARPORT_PC_SUPERIO is not set
+CONFIG_PARPORT_PC_PCMCIA=m
+CONFIG_PARPORT_1284=y
+# CONFIG_PARPORT_AX88796 is not set
+
+#
+# Plug and Play support
+#
+CONFIG_PNP=y
+# CONFIG_PNP_DEBUG is not set
+# CONFIG_PNP_DEBUG_MESSAGES is not set
+
+#
+# Protocols
+#
+CONFIG_ISAPNP=y
+# CONFIG_PNPBIOS is not set
+
+CONFIG_ACPI_PCI_SLOT=y
+CONFIG_HOTPLUG_PCI_ACPI=y
+CONFIG_HOTPLUG_PCI_ACPI_IBM=m
+
+#
+# Block devices
+#
+CONFIG_BLK_DEV=y
+CONFIG_BLK_DEV_FD=m
+# CONFIG_BLK_DEV_XD is not set
+CONFIG_PARIDE=m
+CONFIG_PARIDE_PD=m
+CONFIG_PARIDE_PCD=m
+CONFIG_PARIDE_PF=m
+CONFIG_PARIDE_PT=m
+CONFIG_PARIDE_PG=m
+CONFIG_PARIDE_ATEN=m
+CONFIG_PARIDE_BPCK=m
+CONFIG_PARIDE_BPCK6=m
+CONFIG_PARIDE_COMM=m
+CONFIG_PARIDE_DSTR=m
+CONFIG_PARIDE_FIT2=m
+CONFIG_PARIDE_FIT3=m
+CONFIG_PARIDE_EPAT=m
+CONFIG_PARIDE_EPATC8=y
+CONFIG_PARIDE_EPIA=m
+CONFIG_PARIDE_FRIQ=m
+CONFIG_PARIDE_FRPW=m
+CONFIG_PARIDE_KBIC=m
+CONFIG_PARIDE_KTTI=m
+CONFIG_PARIDE_ON20=m
+CONFIG_PARIDE_ON26=m
+CONFIG_BLK_CPQ_DA=m
+CONFIG_BLK_CPQ_CISS_DA=m
+CONFIG_CISS_SCSI_TAPE=y
+CONFIG_BLK_DEV_DAC960=m
+CONFIG_BLK_DEV_UMEM=m
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+CONFIG_BLK_DEV_NBD=m
+CONFIG_BLK_DEV_OSD=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=16
+CONFIG_BLK_DEV_RAM_SIZE=16384
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_BLK_DEV_ATIIXP=y
+CONFIG_LBD=y
+CONFIG_BLK_DEV_IO_TRACE=y
+
+CONFIG_BLK_DEV_DELKIN=m
+# CONFIG_BLK_DEV_IT8213 is not set
+# CONFIG_BLK_DEV_TC86C001 is not set
+CONFIG_LBDAF=y
+CONFIG_BLK_DEV_BSG=y
+CONFIG_BLK_DEV_INTEGRITY=y
+
+
+#
+# ATA/ATAPI/MFM/RLL support
+#
+# CONFIG_IDE is not set
+
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+CONFIG_BLK_DEV_IDECS=m
+CONFIG_BLK_DEV_IDECD=m
+# CONFIG_BLK_DEV_IDETAPE is not set
+CONFIG_IDE_TASK_IOCTL=y
+# CONFIG_BLK_DEV_IDE_SATA is not set
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_BLK_DEV_CMD640=y
+CONFIG_BLK_DEV_CMD640_ENHANCED=y
+CONFIG_BLK_DEV_IDEPNP=y
+CONFIG_BLK_DEV_IDEPCI=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+CONFIG_BLK_DEV_GENERIC=y
+# CONFIG_BLK_DEV_OPTI621 is not set
+CONFIG_BLK_DEV_RZ1000=y
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+CONFIG_BLK_DEV_AEC62XX=y
+CONFIG_BLK_DEV_ALI15X3=y
+# CONFIG_BLK_DEV_AMD74XX is not set
+CONFIG_BLK_DEV_CMD64X=y
+CONFIG_BLK_DEV_TRIFLEX=y
+# CONFIG_BLK_DEV_CY82C693 is not set
+CONFIG_BLK_DEV_CS5520=y
+CONFIG_BLK_DEV_CS5530=y
+CONFIG_BLK_DEV_CS5535=y
+CONFIG_BLK_DEV_HPT366=y
+CONFIG_BLK_DEV_IT821X=y
+CONFIG_BLK_DEV_JMICRON=y
+# CONFIG_BLK_DEV_SC1200 is not set
+CONFIG_BLK_DEV_PIIX=y
+# CONFIG_BLK_DEV_NS87415 is not set
+CONFIG_BLK_DEV_PDC202XX_OLD=y
+CONFIG_BLK_DEV_PDC202XX_NEW=y
+CONFIG_BLK_DEV_SVWKS=y
+CONFIG_BLK_DEV_SIIMAGE=y
+CONFIG_BLK_DEV_SIS5513=y
+# CONFIG_BLK_DEV_SLC90E66 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+CONFIG_BLK_DEV_VIA82CXXX=y
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_BLK_DEV_HD is not set
+
+CONFIG_VIRTIO=m
+CONFIG_VIRTIO_BLK=m
+CONFIG_VIRTIO_RING=m
+CONFIG_VIRTIO_PCI=m
+CONFIG_VIRTIO_BALLOON=m
+CONFIG_VIRTIO_NET=m
+CONFIG_VMXNET3=m
+CONFIG_HW_RANDOM_VIRTIO=m
+CONFIG_VIRTIO_CONSOLE=m
+
+#
+# SCSI device support
+#
+CONFIG_SCSI=y
+
+CONFIG_SCSI_ENCLOSURE=m
+CONFIG_SCSI_PROC_FS=y
+CONFIG_SCSI_SCAN_ASYNC=y
+CONFIG_SCSI_SRP=m
+CONFIG_SCSI_SRP_ATTRS=m
+CONFIG_SCSI_TGT=m
+
+CONFIG_SCSI_DH=y
+CONFIG_SCSI_DH_RDAC=m
+CONFIG_SCSI_DH_HP_SW=m
+CONFIG_SCSI_DH_EMC=m
+CONFIG_SCSI_DH_ALUA=m
+
+#
+# SCSI support type (disk, tape, CD-ROM)
+#
+CONFIG_BLK_DEV_SD=y
+CONFIG_CHR_DEV_ST=m
+CONFIG_CHR_DEV_OSST=m
+CONFIG_BLK_DEV_SR=y
+CONFIG_BLK_DEV_SR_VENDOR=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=m
+
+#
+# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+#
+CONFIG_SCSI_MULTI_LUN=y
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SPI_ATTRS=m
+CONFIG_SCSI_FC_ATTRS=m
+CONFIG_SCSI_FC_TGT_ATTRS=y
+CONFIG_SCSI_ISCSI_ATTRS=m
+CONFIG_SCSI_SAS_ATTRS=m
+CONFIG_SCSI_SRP_TGT_ATTRS=y
+CONFIG_SCSI_SAS_LIBSAS=m
+CONFIG_SCSI_SAS_ATA=y
+# CONFIG_SCSI_SAS_LIBSAS_DEBUG is not set
+CONFIG_SCSI_SAS_HOST_SMP=y
+CONFIG_RAID_ATTRS=m
+
+CONFIG_ISCSI_TCP=m
+
+#
+# SCSI low-level drivers
+#
+CONFIG_SCSI_LOWLEVEL=y
+CONFIG_BLK_DEV_3W_XXXX_RAID=m
+CONFIG_SCSI_3W_9XXX=m
+# CONFIG_SCSI_7000FASST is not set
+CONFIG_SCSI_ACARD=m
+CONFIG_SCSI_AACRAID=m
+CONFIG_SCSI_AIC7XXX=m
+CONFIG_SCSI_AIC94XX=m
+# CONFIG_AIC94XX_DEBUG is not set
+CONFIG_AIC7XXX_CMDS_PER_DEVICE=4
+CONFIG_AIC7XXX_RESET_DELAY_MS=15000
+# CONFIG_AIC7XXX_BUILD_FIRMWARE is not set
+# CONFIG_AIC7XXX_DEBUG_ENABLE is not set
+CONFIG_AIC7XXX_DEBUG_MASK=0
+# CONFIG_AIC7XXX_REG_PRETTY_PRINT is not set
+CONFIG_SCSI_AIC7XXX_OLD=m
+CONFIG_SCSI_AIC79XX=m
+CONFIG_AIC79XX_CMDS_PER_DEVICE=4
+CONFIG_AIC79XX_RESET_DELAY_MS=15000
+# CONFIG_AIC79XX_BUILD_FIRMWARE is not set
+# CONFIG_AIC79XX_DEBUG_ENABLE is not set
+CONFIG_AIC79XX_DEBUG_MASK=0
+# CONFIG_AIC79XX_REG_PRETTY_PRINT is not set
+# CONFIG_SCSI_ADVANSYS is not set
+CONFIG_SCSI_BFA_FC=m
+CONFIG_MEGARAID_NEWGEN=y
+CONFIG_MEGARAID_MM=m
+CONFIG_MEGARAID_MAILBOX=m
+CONFIG_MEGARAID_LEGACY=m
+CONFIG_MEGARAID_SAS=m
+CONFIG_SCSI_MVSAS=m
+# CONFIG_SCSI_MVSAS_DEBUG is not set
+CONFIG_SCSI_MPT2SAS=m
+CONFIG_SCSI_MPT2SAS_MAX_SGE=128
+CONFIG_SCSI_MPT2SAS_LOGGING=y
+
+CONFIG_SCSI_OSD_INITIATOR=m
+CONFIG_SCSI_OSD_ULD=m
+CONFIG_SCSI_OSD_DPRINT_SENSE=1
+# CONFIG_SCSI_OSD_DEBUG is not set
+
+CONFIG_SCSI_BNX2_ISCSI=m
+CONFIG_BE2ISCSI=m
+CONFIG_SCSI_PMCRAID=m
+
+
+CONFIG_ATA=y
+CONFIG_ATA_VERBOSE_ERROR=y
+CONFIG_ATA_SFF=y
+CONFIG_ATA_PIIX=y
+CONFIG_ATA_ACPI=y
+CONFIG_BLK_DEV_SX8=m
+CONFIG_PDC_ADMA=m
+CONFIG_SATA_AHCI=y
+CONFIG_SATA_INIC162X=m
+CONFIG_SATA_MV=m
+CONFIG_SATA_NV=m
+CONFIG_SATA_PMP=y
+CONFIG_SATA_PROMISE=m
+CONFIG_SATA_QSTOR=m
+CONFIG_SATA_SIL=m
+CONFIG_SATA_SIL24=m
+CONFIG_SATA_SIS=m
+CONFIG_SATA_SVW=m
+CONFIG_SATA_SX4=m
+CONFIG_SATA_ULI=m
+CONFIG_SATA_VIA=m
+CONFIG_SATA_VITESSE=m
+
+CONFIG_PATA_ACPI=m
+CONFIG_PATA_ALI=m
+CONFIG_PATA_AMD=m
+CONFIG_PATA_ARTOP=m
+CONFIG_PATA_ATIIXP=m
+CONFIG_PATA_CMD640_PCI=m
+CONFIG_PATA_CMD64X=m
+CONFIG_PATA_CS5520=m
+CONFIG_PATA_CS5530=m
+CONFIG_PATA_CS5535=m
+CONFIG_PATA_CS5536=m
+CONFIG_PATA_CYPRESS=m
+CONFIG_PATA_EFAR=m
+CONFIG_ATA_GENERIC=m
+CONFIG_PATA_HPT366=m
+CONFIG_PATA_HPT37X=m
+CONFIG_PATA_HPT3X2N=m
+CONFIG_PATA_HPT3X3=m
+# CONFIG_PATA_HPT3X3_DMA is not set
+# CONFIG_PATA_ISAPNP is not set
+CONFIG_PATA_IT821X=m
+CONFIG_PATA_IT8213=m
+CONFIG_PATA_JMICRON=m
+# CONFIG_PATA_LEGACY is not set
+CONFIG_PATA_NINJA32=m
+CONFIG_PATA_MARVELL=m
+# CONFIG_PATA_WINBOND_VLB is not set
+CONFIG_PATA_MPIIX=m
+CONFIG_PATA_NETCELL=m
+CONFIG_PATA_NS87410=m
+CONFIG_PATA_NS87415=m
+CONFIG_PATA_OLDPIIX=m
+CONFIG_PATA_OPTI=m
+CONFIG_PATA_OPTIDMA=m
+CONFIG_PATA_PCMCIA=m
+CONFIG_PATA_PDC_OLD=m
+CONFIG_PATA_QDI=m
+# CONFIG_PATA_RADISYS is not set
+CONFIG_PATA_RDC=m
+# CONFIG_PATA_RZ1000 is not set
+# CONFIG_PATA_SC1200 is not set
+CONFIG_PATA_SERVERWORKS=m
+CONFIG_PATA_PDC2027X=m
+CONFIG_PATA_SCH=m
+CONFIG_PATA_SIL680=m
+CONFIG_PATA_SIS=m
+CONFIG_PATA_TRIFLEX=m
+CONFIG_PATA_VIA=m
+CONFIG_PATA_WINBOND=m
+CONFIG_PATA_ATP867X=m
+
+CONFIG_SCSI_BUSLOGIC=m
+CONFIG_SCSI_INITIO=m
+CONFIG_SCSI_FLASHPOINT=y
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+CONFIG_SCSI_GDTH=m
+CONFIG_SCSI_HPTIOP=m
+CONFIG_SCSI_IPS=m
+CONFIG_SCSI_INIA100=m
+CONFIG_SCSI_PPA=m
+CONFIG_SCSI_IMM=m
+# CONFIG_SCSI_IZIP_EPP16 is not set
+# CONFIG_SCSI_IZIP_SLOW_CTR is not set
+CONFIG_SCSI_STEX=m
+CONFIG_SCSI_SYM53C8XX_2=m
+CONFIG_SCSI_SYM53C8XX_DMA_ADDRESSING_MODE=1
+CONFIG_SCSI_SYM53C8XX_DEFAULT_TAGS=16
+CONFIG_SCSI_SYM53C8XX_MAX_TAGS=64
+CONFIG_SCSI_SYM53C8XX_MMIO=y
+CONFIG_SCSI_QLOGIC_1280=m
+CONFIG_SCSI_DC395x=m
+# CONFIG_SCSI_NSP32 is not set
+CONFIG_SCSI_DEBUG=m
+CONFIG_SCSI_DC390T=m
+CONFIG_SCSI_QLA_FC=m
+CONFIG_SCSI_QLA_ISCSI=m
+# CONFIG_SCSI_IPR is not set
+# CONFIG_SCSI_DPT_I2O is not set
+CONFIG_SCSI_LPFC=m
+
+#
+# PCMCIA SCSI adapter support
+#
+CONFIG_SCSI_LOWLEVEL_PCMCIA=y
+CONFIG_PCMCIA_AHA152X=m
+# CONFIG_PCMCIA_FDOMAIN is not set
+CONFIG_PCMCIA_NINJA_SCSI=m
+CONFIG_PCMCIA_QLOGIC=m
+CONFIG_PCMCIA_SYM53C500=m
+
+
+#
+# Multi-device support (RAID and LVM)
+#
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=y
+CONFIG_MD_AUTODETECT=y
+CONFIG_MD_FAULTY=m
+CONFIG_MD_LINEAR=m
+CONFIG_MD_MULTIPATH=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+CONFIG_MD_RAID10=m
+CONFIG_MD_RAID456=m
+# CONFIG_MULTICORE_RAID456 is not set
+CONFIG_ASYNC_RAID6_TEST=m
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=m
+CONFIG_DM_DEBUG=y
+# CONFIG_DM_DELAY is not set
+CONFIG_DM_MIRROR=y
+CONFIG_DM_MULTIPATH=m
+CONFIG_DM_SNAPSHOT=y
+CONFIG_DM_UEVENT=y
+CONFIG_DM_ZERO=y
+CONFIG_DM_LOG_USERSPACE=m
+CONFIG_DM_MULTIPATH_QL=m
+CONFIG_DM_MULTIPATH_ST=m
+
+#
+# Fusion MPT device support
+#
+CONFIG_FUSION=y
+CONFIG_FUSION_SPI=m
+CONFIG_FUSION_FC=m
+CONFIG_FUSION_MAX_SGE=40
+CONFIG_FUSION_CTL=m
+CONFIG_FUSION_LAN=m
+CONFIG_FUSION_SAS=m
+CONFIG_FUSION_LOGGING=y
+
+#
+# IEEE 1394 (FireWire) support (JUJU alternative stack)
+#
+CONFIG_FIREWIRE=m
+CONFIG_FIREWIRE_OHCI=m
+CONFIG_FIREWIRE_SBP2=m
+CONFIG_FIREWIRE_NET=m
+CONFIG_FIREWIRE_OHCI_DEBUG=y
+# CONFIG_FIREWIRE_OHCI_REMOTE_DMA is not set
+
+#
+# IEEE 1394 (FireWire) support
+#
+# CONFIG_IEEE1394 is not set
+
+#
+# I2O device support
+#
+# CONFIG_I2O is not set
+# CONFIG_I2O_LCT_NOTIFY_ON_CHANGES is not set
+
+#
+# Networking support
+#
+CONFIG_NET=y
+
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_BIC=m
+CONFIG_TCP_CONG_CUBIC=y
+CONFIG_TCP_CONG_HTCP=m
+CONFIG_TCP_CONG_HSTCP=m
+CONFIG_TCP_CONG_HYBLA=m
+CONFIG_TCP_CONG_ILLINOIS=m
+CONFIG_TCP_CONG_LP=m
+CONFIG_TCP_CONG_SCALABLE=m
+CONFIG_TCP_CONG_VEGAS=m
+CONFIG_TCP_CONG_VENO=m
+CONFIG_TCP_CONG_WESTWOOD=m
+CONFIG_TCP_CONG_YEAH=m
+
+CONFIG_TCP_MD5SIG=y
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+CONFIG_PACKET_MMAP=y
+CONFIG_UNIX=y
+CONFIG_NET_KEY=m
+CONFIG_NET_KEY_MIGRATE=y
+CONFIG_INET=y
+CONFIG_INET_LRO=y
+CONFIG_INET_TUNNEL=m
+CONFIG_INET_DIAG=m
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_NF_SECURITY=m
+# CONFIG_IP_PNP is not set
+CONFIG_NET_IPIP=m
+CONFIG_NET_IPGRE=m
+CONFIG_NET_IPGRE_BROADCAST=y
+CONFIG_IP_MROUTE=y
+CONFIG_IP_PIMSM_V1=y
+CONFIG_IP_PIMSM_V2=y
+CONFIG_ARPD=y
+CONFIG_SYN_COOKIES=y
+CONFIG_INET_AH=m
+CONFIG_INET_ESP=m
+CONFIG_INET_IPCOMP=m
+CONFIG_NETCONSOLE=m
+CONFIG_NETCONSOLE_DYNAMIC=y
+CONFIG_NETPOLL_TRAP=y
+CONFIG_NET_POLL_CONTROLLER=y
+
+#
+# IP: Virtual Server Configuration
+#
+CONFIG_IP_VS=m
+# CONFIG_IP_VS_DEBUG is not set
+CONFIG_IP_VS_TAB_BITS=12
+CONFIG_IP_VS_PROTO_TCP=y
+CONFIG_IP_VS_PROTO_UDP=y
+CONFIG_IP_VS_PROTO_ESP=y
+CONFIG_IP_VS_PROTO_AH=y
+CONFIG_IP_VS_RR=m
+CONFIG_IP_VS_WRR=m
+CONFIG_IP_VS_LC=m
+CONFIG_IP_VS_WLC=m
+CONFIG_IP_VS_LBLC=m
+CONFIG_IP_VS_LBLCR=m
+CONFIG_IP_VS_DH=m
+CONFIG_IP_VS_SH=m
+CONFIG_IP_VS_SED=m
+CONFIG_IP_VS_NQ=m
+CONFIG_IP_VS_FTP=m
+
+CONFIG_IPV6=m
+CONFIG_IPV6_PRIVACY=y
+CONFIG_IPV6_ROUTER_PREF=y
+CONFIG_IPV6_ROUTE_INFO=y
+CONFIG_IPV6_OPTIMISTIC_DAD=y
+CONFIG_INET6_AH=m
+CONFIG_INET6_ESP=m
+CONFIG_INET6_IPCOMP=m
+CONFIG_IPV6_MIP6=y
+CONFIG_IPV6_SIT=m
+CONFIG_IPV6_TUNNEL=m
+CONFIG_IPV6_SUBTREES=y
+CONFIG_IPV6_MULTIPLE_TABLES=y
+CONFIG_IPV6_MROUTE=y
+CONFIG_IPV6_PIMSM_V2=y
+
+CONFIG_RDS=m
+# CONFIG_RDS_DEBUG is not set
+CONFIG_RDS_RDMA=m
+CONFIG_RDS_TCP=m
+
+CONFIG_NET_9P=m
+CONFIG_NET_9P_VIRTIO=m
+# CONFIG_NET_9P_DEBUG is not set
+CONFIG_NET_9P_RDMA=m
+
+CONFIG_DECNET=m
+CONFIG_DECNET_ROUTER=y
+# CONFIG_DECNET_NF_GRABULATOR is not set
+CONFIG_BRIDGE=m
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_ADVANCED=y
+CONFIG_NF_CONNTRACK=y
+CONFIG_NETFILTER_NETLINK=m
+CONFIG_NETFILTER_NETLINK_QUEUE=m
+CONFIG_NETFILTER_NETLINK_LOG=m
+CONFIG_NETFILTER_XTABLES=y
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
+CONFIG_NETFILTER_XT_TARGET_CONNSECMARK=m
+CONFIG_NETFILTER_XT_TARGET_DSCP=m
+CONFIG_NETFILTER_XT_TARGET_MARK=m
+CONFIG_NETFILTER_XT_TARGET_NFLOG=m
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
+CONFIG_NETFILTER_XT_TARGET_NOTRACK=m
+CONFIG_NETFILTER_XT_TARGET_RATEEST=m
+CONFIG_NETFILTER_XT_TARGET_SECMARK=m
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
+CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m
+CONFIG_NETFILTER_XT_TARGET_TRACE=m
+CONFIG_NETFILTER_XT_TARGET_LED=m
+CONFIG_NETFILTER_XT_MATCH_COMMENT=m
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=y
+CONFIG_NETFILTER_XT_MATCH_DCCP=m
+CONFIG_NETFILTER_XT_MATCH_DSCP=m
+CONFIG_NETFILTER_XT_MATCH_ESP=m
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_HELPER=m
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
+CONFIG_NETFILTER_XT_MATCH_LENGTH=m
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MARK=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_OWNER=m
+CONFIG_NETFILTER_XT_MATCH_PHYSDEV=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_QUOTA=m
+CONFIG_NETFILTER_XT_MATCH_RATEEST=m
+CONFIG_NETFILTER_XT_MATCH_REALM=m
+CONFIG_NETFILTER_XT_MATCH_SCTP=m
+CONFIG_NETFILTER_XT_MATCH_STATE=y
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
+CONFIG_NETFILTER_XT_MATCH_STRING=m
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_TIME=m
+CONFIG_NETFILTER_XT_MATCH_U32=m
+CONFIG_NETFILTER_XT_MATCH_CLUSTER=m
+CONFIG_NETFILTER_XT_MATCH_HL=m
+CONFIG_NETFILTER_XT_MATCH_OSF=m
+
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_BRIDGE_NETFILTER=y
+
+#
+# IP: Netfilter Configuration
+#
+
+CONFIG_NF_CT_ACCT=y
+CONFIG_NF_CONNTRACK_MARK=y
+CONFIG_NF_CONNTRACK_SECMARK=y
+CONFIG_NF_CONNTRACK_EVENTS=y
+# CONFIG_NF_CONNTRACK_PROC_COMPAT is not set
+CONFIG_NF_CONNTRACK_AMANDA=m
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_H323=m
+CONFIG_NF_CONNTRACK_IRC=m
+CONFIG_NF_CONNTRACK_NETBIOS_NS=m
+CONFIG_NF_CONNTRACK_PPTP=m
+CONFIG_NF_CONNTRACK_SANE=m
+CONFIG_NF_CONNTRACK_SIP=m
+CONFIG_NF_CONNTRACK_TFTP=m
+CONFIG_NF_CONNTRACK_IPV4=y
+CONFIG_NF_CONNTRACK_IPV6=y
+CONFIG_NF_NAT=m
+CONFIG_NF_NAT_SNMP_BASIC=m
+CONFIG_NF_CT_PROTO_DCCP=m
+CONFIG_NF_CT_PROTO_SCTP=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NF_CT_PROTO_UDPLITE=m
+
+CONFIG_IP_NF_MATCH_ADDRTYPE=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_TARGET_CLUSTERIP=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_TARGET_NETMAP=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_LOG=m
+CONFIG_IP_NF_TARGET_ULOG=m
+CONFIG_IP_NF_TARGET_REJECT=y
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_IP_NF_QUEUE=m
+CONFIG_IP_NF_RAW=m
+
+CONFIG_IP_NF_IPTABLES=y
+CONFIG_IP_NF_FILTER=y
+
+#
+# IPv6: Netfilter Configuration
+#
+CONFIG_IP6_NF_FILTER=m
+CONFIG_IP6_NF_IPTABLES=m
+CONFIG_IP6_NF_MANGLE=m
+CONFIG_IP6_NF_MATCH_AH=m
+CONFIG_IP6_NF_MATCH_EUI64=m
+CONFIG_IP6_NF_MATCH_FRAG=m
+CONFIG_IP6_NF_MATCH_HL=m
+CONFIG_IP6_NF_MATCH_IPV6HEADER=m
+CONFIG_IP6_NF_MATCH_MH=m
+CONFIG_IP6_NF_MATCH_OPTS=m
+CONFIG_IP6_NF_MATCH_RT=m
+CONFIG_IP6_NF_QUEUE=m
+CONFIG_IP6_NF_RAW=m
+CONFIG_IP6_NF_SECURITY=m
+CONFIG_IP6_NF_TARGET_LOG=m
+CONFIG_IP6_NF_TARGET_REJECT=m
+CONFIG_IP6_NF_TARGET_HL=m
+
+#
+# Bridge: Netfilter Configuration
+#
+CONFIG_BRIDGE_NF_EBTABLES=m
+CONFIG_BRIDGE_EBT_802_3=m
+CONFIG_BRIDGE_EBT_AMONG=m
+CONFIG_BRIDGE_EBT_ARP=m
+CONFIG_BRIDGE_EBT_ARPREPLY=m
+CONFIG_BRIDGE_EBT_BROUTE=m
+CONFIG_BRIDGE_EBT_DNAT=m
+CONFIG_BRIDGE_EBT_IP=m
+CONFIG_BRIDGE_EBT_IP6=m
+CONFIG_BRIDGE_EBT_LIMIT=m
+CONFIG_BRIDGE_EBT_LOG=m
+CONFIG_BRIDGE_EBT_MARK=m
+CONFIG_BRIDGE_EBT_MARK_T=m
+CONFIG_BRIDGE_EBT_NFLOG=m
+CONFIG_BRIDGE_EBT_PKTTYPE=m
+CONFIG_BRIDGE_EBT_REDIRECT=m
+CONFIG_BRIDGE_EBT_SNAT=m
+CONFIG_BRIDGE_EBT_STP=m
+CONFIG_BRIDGE_EBT_T_FILTER=m
+CONFIG_BRIDGE_EBT_T_NAT=m
+CONFIG_BRIDGE_EBT_ULOG=m
+CONFIG_BRIDGE_EBT_VLAN=m
+CONFIG_XFRM=y
+CONFIG_XFRM_MIGRATE=y
+CONFIG_XFRM_SUB_POLICY=y
+CONFIG_XFRM_STATISTICS=y
+CONFIG_XFRM_USER=y
+CONFIG_INET_XFRM_MODE_TRANSPORT=m
+CONFIG_INET_XFRM_MODE_TUNNEL=m
+CONFIG_INET_XFRM_MODE_BEET=m
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_ROUTEOPTIMIZATION=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+
+#
+# SCTP Configuration (EXPERIMENTAL)
+#
+CONFIG_IP_SCTP=m
+# CONFIG_SCTP_DBG_MSG is not set
+# CONFIG_SCTP_DBG_OBJCNT is not set
+# CONFIG_SCTP_HMAC_NONE is not set
+CONFIG_SCTP_HMAC_SHA1=y
+# CONFIG_SCTP_HMAC_MD5 is not set
+CONFIG_ATM=m
+CONFIG_VLAN_8021Q=m
+CONFIG_VLAN_8021Q_GVRP=y
+CONFIG_LLC=m
+# CONFIG_LLC2 is not set
+CONFIG_IPX=m
+# CONFIG_IPX_INTERN is not set
+CONFIG_ATALK=m
+CONFIG_DEV_APPLETALK=y
+CONFIG_IPDDP=m
+CONFIG_IPDDP_ENCAP=y
+CONFIG_IPDDP_DECAP=y
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_ECONET is not set
+CONFIG_WAN_ROUTER=m
+CONFIG_IP_DCCP=m
+CONFIG_IP_DCCP_CCID2=m
+# CONFIG_IP_DCCP_CCID2_DEBUG is not set
+CONFIG_IP_DCCP_CCID3=y
+# CONFIG_IP_DCCP_CCID3_DEBUG is not set
+CONFIG_IP_DCCP_CCID3_RTO=100
+# CONFIG_IP_DCCP_DEBUG is not set
+CONFIG_NET_DCCPPROBE=m
+
+#
+# TIPC Configuration (EXPERIMENTAL)
+#
+# CONFIG_TIPC is not set
+# CONFIG_TIPC_ADVANCED is not set
+# CONFIG_TIPC_DEBUG is not set
+
+CONFIG_NETLABEL=y
+
+#
+# QoS and/or fair queueing
+#
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_CBQ=m
+CONFIG_NET_SCH_DSMARK=m
+CONFIG_NET_SCH_DRR=m
+CONFIG_NET_SCH_GRED=m
+CONFIG_NET_SCH_HFSC=m
+CONFIG_NET_SCH_HTB=m
+CONFIG_NET_SCH_INGRESS=m
+CONFIG_NET_SCH_NETEM=m
+CONFIG_NET_SCH_PRIO=m
+CONFIG_NET_SCH_RED=m
+CONFIG_NET_SCH_SFQ=m
+CONFIG_NET_SCH_TBF=m
+CONFIG_NET_SCH_TEQL=m
+CONFIG_NET_CLS=y
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_CLS_BASIC=m
+CONFIG_NET_CLS_CGROUP=y
+CONFIG_NET_CLS_FLOW=m
+CONFIG_NET_CLS_FW=m
+CONFIG_NET_CLS_IND=y
+CONFIG_NET_CLS_ROUTE4=m
+CONFIG_NET_CLS_ROUTE=y
+CONFIG_NET_CLS_RSVP=m
+CONFIG_NET_CLS_RSVP6=m
+CONFIG_NET_CLS_TCINDEX=m
+CONFIG_NET_CLS_U32=m
+CONFIG_CLS_U32_MARK=y
+CONFIG_CLS_U32_PERF=y
+CONFIG_NET_EMATCH=y
+CONFIG_NET_EMATCH_CMP=m
+CONFIG_NET_EMATCH_META=m
+CONFIG_NET_EMATCH_NBYTE=m
+CONFIG_NET_EMATCH_STACK=32
+CONFIG_NET_EMATCH_TEXT=m
+CONFIG_NET_EMATCH_U32=m
+
+CONFIG_NET_ACT_GACT=m
+CONFIG_GACT_PROB=y
+CONFIG_NET_ACT_IPT=m
+CONFIG_NET_ACT_MIRRED=m
+CONFIG_NET_ACT_NAT=m
+CONFIG_NET_ACT_PEDIT=m
+CONFIG_NET_ACT_POLICE=m
+CONFIG_NET_ACT_SIMP=m
+
+CONFIG_DCB=y
+
+#
+# Network testing
+#
+CONFIG_NET_PKTGEN=m
+# CONFIG_NET_TCPPROBE is not set
+CONFIG_NET_DROP_MONITOR=y
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+CONFIG_IFB=m
+CONFIG_DUMMY=m
+CONFIG_BONDING=m
+CONFIG_MACVLAN=m
+CONFIG_EQUALIZER=m
+CONFIG_TUN=m
+CONFIG_VETH=m
+CONFIG_NET_SB1000=m
+
+#
+# ATM
+#
+CONFIG_ATM_DRIVERS=y
+# CONFIG_ATM_DUMMY is not set
+CONFIG_ATM_CLIP=m
+CONFIG_ATM_LANE=m
+CONFIG_ATM_BR2684=m
+CONFIG_NET_SCH_ATM=m
+CONFIG_ATM_TCP=m
+# CONFIG_ATM_LANAI is not set
+CONFIG_ATM_ENI=m
+CONFIG_ATM_FIRESTREAM=m
+# CONFIG_ATM_ZATM is not set
+# CONFIG_ATM_IDT77252 is not set
+# CONFIG_ATM_AMBASSADOR is not set
+# CONFIG_ATM_HORIZON is not set
+# CONFIG_ATM_FORE200E is not set
+# CONFIG_ATM_FORE200E_USE_TASKLET is not set
+CONFIG_ATM_FORE200E_TX_RETRY=16
+CONFIG_ATM_FORE200E_DEBUG=0
+
+CONFIG_ATM_HE=m
+CONFIG_PPPOATM=m
+CONFIG_PPPOL2TP=m
+CONFIG_ATM_NICSTAR=m
+# CONFIG_ATM_IA is not set
+# CONFIG_ATM_CLIP_NO_ICMP is not set
+# CONFIG_ATM_MPOA is not set
+# CONFIG_ATM_BR2684_IPFILTER is not set
+# CONFIG_ATM_ENI_DEBUG is not set
+# CONFIG_ATM_ENI_TUNE_BURST is not set
+# CONFIG_ATM_ZATM_DEBUG is not set
+# CONFIG_ATM_IDT77252_DEBUG is not set
+# CONFIG_ATM_IDT77252_RCV_ALL is not set
+# CONFIG_ATM_AMBASSADOR_DEBUG is not set
+# CONFIG_ATM_HORIZON_DEBUG is not set
+# CONFIG_ATM_HE_USE_SUNI is not set
+# CONFIG_ATM_NICSTAR_USE_SUNI is not set
+# CONFIG_ATM_NICSTAR_USE_IDT77105 is not set
+# CONFIG_ATM_IA_DEBUG is not set
+CONFIG_ATM_SOLOS=m
+
+CONFIG_RFKILL=m
+CONFIG_RFKILL_INPUT=y
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NETDEV_1000=y
+CONFIG_NETDEV_10000=y
+CONFIG_NET_ETHERNET=y
+CONFIG_PHYLIB=m
+CONFIG_BROADCOM_PHY=m
+CONFIG_CICADA_PHY=m
+CONFIG_DAVICOM_PHY=m
+CONFIG_FIXED_PHY=y
+CONFIG_MDIO_BITBANG=m
+CONFIG_NATIONAL_PHY=m
+CONFIG_ICPLUS_PHY=m
+CONFIG_LSI_ET1011C_PHY=m
+CONFIG_LXT_PHY=m
+CONFIG_MARVELL_PHY=m
+CONFIG_QSEMI_PHY=m
+CONFIG_REALTEK_PHY=m
+CONFIG_SMSC_PHY=m
+CONFIG_STE10XP=m
+CONFIG_VITESSE_PHY=m
+
+CONFIG_MII=m
+CONFIG_HAPPYMEAL=m
+CONFIG_SUNGEM=m
+CONFIG_NET_VENDOR_3COM=y
+CONFIG_VORTEX=m
+CONFIG_TYPHOON=m
+CONFIG_DNET=m
+
+#
+# Tulip family network device support
+#
+CONFIG_NET_TULIP=y
+CONFIG_DE2104X=m
+CONFIG_DE2104X_DSL=0
+CONFIG_TULIP=m
+# CONFIG_TULIP_NAPI is not set
+# CONFIG_TULIP_MWI is not set
+CONFIG_TULIP_MMIO=y
+# CONFIG_NI5010 is not set
+CONFIG_DE4X5=m
+CONFIG_WINBOND_840=m
+CONFIG_DM9102=m
+CONFIG_PCMCIA_XIRCOM=m
+CONFIG_ULI526X=m
+# CONFIG_HP100 is not set
+CONFIG_LNE390=m
+CONFIG_NE3210=m
+CONFIG_ES3210=m
+CONFIG_NET_PCI=y
+CONFIG_PCNET32=m
+CONFIG_AMD8111_ETH=m
+CONFIG_ADAPTEC_STARFIRE=m
+CONFIG_B44=m
+CONFIG_B44_PCI=y
+CONFIG_BNX2=m
+CONFIG_CNIC=m
+CONFIG_QLA3XXX=m
+CONFIG_ATL1=m
+CONFIG_ATL1C=m
+CONFIG_ATL2=m
+CONFIG_ATL1E=m
+CONFIG_E100=m
+CONFIG_FEALNX=m
+CONFIG_FORCEDETH=m
+CONFIG_FORCEDETH_NAPI=y
+CONFIG_NATSEMI=m
+CONFIG_NE2K_PCI=m
+CONFIG_8139CP=m
+CONFIG_8139TOO=m
+# CONFIG_8139TOO_PIO is not set
+# CONFIG_8139TOO_TUNE_TWISTER is not set
+CONFIG_8139TOO_8129=y
+# CONFIG_8139_OLD_RX_RESET is not set
+CONFIG_SIS900=m
+CONFIG_SIS190=m
+CONFIG_EPIC100=m
+CONFIG_SC92031=m
+CONFIG_SMSC9420=m
+CONFIG_SUNDANCE=m
+# CONFIG_SUNDANCE_MMIO is not set
+CONFIG_TLAN=m
+CONFIG_VIA_RHINE=m
+CONFIG_VIA_RHINE_MMIO=y
+CONFIG_VIA_VELOCITY=m
+CONFIG_NET_POCKET=y
+CONFIG_ATP=m
+CONFIG_DE600=m
+CONFIG_DE620=m
+CONFIG_CASSINI=m
+CONFIG_ETHOC=m
+# CONFIG_KS8842 is not set
+# CONFIG_KS8851_MLL is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+CONFIG_ACENIC=m
+# CONFIG_ACENIC_OMIT_TIGON_I is not set
+CONFIG_DL2K=m
+CONFIG_E1000=m
+CONFIG_E1000E=m
+CONFIG_IGB=m
+CONFIG_IGB_DCA=y
+CONFIG_IGBVF=m
+CONFIG_NS83820=m
+CONFIG_HAMACHI=m
+CONFIG_YELLOWFIN=m
+CONFIG_R8169=m
+CONFIG_R8169_VLAN=y
+CONFIG_SKGE=m
+# CONFIG_SKGE_DEBUG is not set
+CONFIG_TIGON3=m
+CONFIG_SKY2=m
+# CONFIG_SKY2_DEBUG is not set
+CONFIG_JME=m
+
+#
+# Ethernet (10000 Mbit)
+#
+CONFIG_CHELSIO_T1=m
+CONFIG_CHELSIO_T1_1G=y
+CONFIG_CHELSIO_T3=m
+CONFIG_IP1000=m
+CONFIG_IXGB=m
+CONFIG_IXGBE=m
+CONFIG_IXGBE_DCA=y
+CONFIG_IXGBE_DCB=y
+CONFIG_MYRI10GE=m
+CONFIG_MYRI10GE_DCA=y
+CONFIG_NETXEN_NIC=m
+CONFIG_NIU=m
+CONFIG_S2IO=m
+CONFIG_VXGE=m
+# CONFIG_VXGE_DEBUG_TRACE_ALL is not set
+CONFIG_TEHUTI=m
+CONFIG_ENIC=m
+CONFIG_MLX4_EN=m
+# CONFIG_MLX4_DEBUG is not set
+CONFIG_QLGE=m
+CONFIG_SFC=m
+CONFIG_SFC_MTD=y
+CONFIG_BE2NET=m
+
+CONFIG_FDDI=y
+# CONFIG_DEFXX is not set
+CONFIG_SKFP=m
+# CONFIG_HIPPI is not set
+CONFIG_PLIP=m
+CONFIG_PPP=m
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_IPPP_FILTER=y
+# CONFIG_PPP_BSDCOMP is not set
+CONFIG_PPPOE=m
+CONFIG_PPP_MPPE=m
+CONFIG_SLIP=m
+CONFIG_SLIP_COMPRESSED=y
+CONFIG_SLIP_SMART=y
+# CONFIG_SLIP_MODE_SLIP6 is not set
+
+#
+# Wireless LAN
+#
+#
+CONFIG_WLAN=y
+# CONFIG_WLAN_PRE80211 is not set
+# CONFIG_STRIP is not set
+# CONFIG_ARLAN is not set
+CONFIG_PCMCIA_WAVELAN=m
+CONFIG_PCMCIA_NETWAVE=m
+CONFIG_WLAN_80211=y
+# CONFIG_PCMCIA_RAYCS is not set
+
+CONFIG_WIRELESS=y
+CONFIG_CFG80211=m
+# CONFIG_CFG80211_REG_DEBUG is not set
+CONFIG_CFG80211_DEBUGFS=y
+# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set
+CONFIG_CFG80211_DEFAULT_PS=y
+CONFIG_NL80211=y
+# CONFIG_NL80211_TESTMODE is not set
+# CONFIG_WIRELESS_OLD_REGULATORY is not set
+CONFIG_WIRELESS_EXT=y
+CONFIG_WIRELESS_EXT_SYSFS=y
+CONFIG_LIB80211=m
+CONFIG_LIB80211_CRYPT_WEP=m
+CONFIG_LIB80211_CRYPT_CCMP=m
+CONFIG_LIB80211_CRYPT_TKIP=m
+# CONFIG_LIB80211_DEBUG is not set
+
+CONFIG_MAC80211=m
+CONFIG_MAC80211_RC_MINSTREL=y
+# CONFIG_MAC80211_RC_DEFAULT_PID is not set
+CONFIG_MAC80211_RC_DEFAULT_MINSTREL=y
+CONFIG_MAC80211_RC_DEFAULT="minstrel"
+CONFIG_MAC80211_MESH=y
+CONFIG_MAC80211_LEDS=y
+CONFIG_MAC80211_DEBUGFS=y
+# CONFIG_MAC80211_DEBUG_MENU is not set
+
+CONFIG_WIMAX=m
+CONFIG_WIMAX_DEBUG_LEVEL=8
+CONFIG_WIMAX_I2400M_USB=m
+CONFIG_WIMAX_I2400M_SDIO=m
+CONFIG_WIMAX_I2400M_DEBUG_LEVEL=8
+
+CONFIG_ADM8211=m
+CONFIG_ATH_COMMON=m
+CONFIG_ATH5K=m
+CONFIG_ATH5K_DEBUG=y
+CONFIG_ATH9K=m
+# CONFIG_ATH9K_DEBUG is not set
+CONFIG_AT76C50X_USB=m
+CONFIG_AIRO=m
+CONFIG_AIRO_CS=m
+CONFIG_ATMEL=m
+CONFIG_B43=m
+CONFIG_B43_PCMCIA=y
+CONFIG_B43_SDIO=y
+# CONFIG_B43_DEBUG is not set
+CONFIG_B43_PHY_LP=y
+# CONFIG_B43_FORCE_PIO is not set
+CONFIG_B43LEGACY=m
+# CONFIG_B43LEGACY_DEBUG is not set
+CONFIG_B43LEGACY_DMA=y
+CONFIG_B43LEGACY_PIO=y
+CONFIG_B43LEGACY_DMA_AND_PIO_MODE=y
+# CONFIG_B43LEGACY_DMA_MODE is not set
+# CONFIG_B43LEGACY_PIO_MODE is not set
+CONFIG_HERMES=m
+CONFIG_HERMES_CACHE_FW_ON_INIT=y
+CONFIG_HOSTAP=m
+CONFIG_HOSTAP_PCI=m
+CONFIG_HOSTAP_PLX=m
+CONFIG_HOSTAP_FIRMWARE=y
+CONFIG_HOSTAP_FIRMWARE_NVRAM=y
+CONFIG_HOSTAP_CS=m
+# CONFIG_IPW2100 is not set
+# CONFIG_IPW2200 is not set
+# CONFIG_IPW2100_DEBUG is not set
+# CONFIG_IPW2200_DEBUG is not set
+# CONFIG_LIBIPW_DEBUG is not set
+CONFIG_LIBERTAS=m
+CONFIG_LIBERTAS_USB=m
+CONFIG_LIBERTAS_CS=m
+CONFIG_LIBERTAS_SDIO=m
+CONFIG_LIBERTAS_DEBUG=y
+CONFIG_LIBERTAS_THINFIRM=m
+CONFIG_LIBERTAS_THINFIRM_USB=m
+CONFIG_IWLWIFI=m
+CONFIG_IWLWIFI_LEDS=y
+CONFIG_IWLWIFI_DEBUG=y
+CONFIG_IWLWIFI_DEBUGFS=y
+CONFIG_IWLWIFI_SPECTRUM_MEASUREMENT=y
+CONFIG_IWLAGN=m
+CONFIG_IWL4965=y
+CONFIG_IWL5000=y
+CONFIG_IWL3945=m
+CONFIG_IWL3945_SPECTRUM_MEASUREMENT=y
+CONFIG_IWM=m
+# CONFIG_IWM_DEBUG is not set
+CONFIG_MAC80211_HWSIM=m
+CONFIG_NORTEL_HERMES=m
+CONFIG_P54_COMMON=m
+CONFIG_P54_USB=m
+CONFIG_P54_PCI=m
+CONFIG_PCI_HERMES=m
+CONFIG_PLX_HERMES=m
+CONFIG_PCI_ATMEL=m
+CONFIG_MWL8K=m
+# CONFIG_PRISM54 is not set
+CONFIG_PCMCIA_HERMES=m
+CONFIG_PCMCIA_SPECTRUM=m
+CONFIG_PCMCIA_ATMEL=m
+CONFIG_PCMCIA_WL3501=m
+CONFIG_RT2X00=m
+CONFIG_RT2X00_LIB_DEBUGFS=y
+# CONFIG_RT2X00_DEBUG is not set
+CONFIG_RT2400PCI=m
+CONFIG_RT2500PCI=m
+CONFIG_RT61PCI=m
+CONFIG_RT2500USB=m
+CONFIG_RT2800USB=m
+CONFIG_RT73USB=m
+CONFIG_RTL8180=m
+CONFIG_RTL8187=m
+CONFIG_TMD_HERMES=m
+CONFIG_USB_ZD1201=m
+CONFIG_USB_NET_RNDIS_WLAN=m
+CONFIG_ZD1211RW=m
+# CONFIG_ZD1211RW_DEBUG is not set
+CONFIG_AR9170_USB=m
+
+CONFIG_WL12XX=y
+CONFIG_WL1251=m
+CONFIG_WL1251_SPI=m
+CONFIG_WL1251_SDIO=m
+CONFIG_WL1271=m
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_IBMOL is not set
+# CONFIG_3C359 is not set
+# Broken with gcc4.1
+# CONFIG_TMS380TR is not set
+# CONFIG_TMSPCI is not set
+# CONFIG_ABYSS is not set
+# CONFIG_IBMLS is not set
+# CONFIG_PCMCIA_IBMTR is not set
+
+
+CONFIG_NET_FC=y
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# PCMCIA network device support
+#
+CONFIG_NET_PCMCIA=y
+CONFIG_PCMCIA_3C589=m
+CONFIG_PCMCIA_3C574=m
+CONFIG_PCMCIA_FMVJ18X=m
+CONFIG_PCMCIA_PCNET=m
+CONFIG_PCMCIA_NMCLAN=m
+CONFIG_PCMCIA_SMC91C92=m
+CONFIG_PCMCIA_XIRC2PS=m
+CONFIG_PCMCIA_AXNET=m
+
+#
+# Amateur Radio support
+#
+CONFIG_HAMRADIO=y
+CONFIG_AX25=m
+CONFIG_AX25_DAMA_SLAVE=y
+CONFIG_CAN=m
+CONFIG_CAN_RAW=m
+CONFIG_CAN_BCM=m
+CONFIG_CAN_VCAN=m
+CONFIG_CAN_DEV=m
+CONFIG_CAN_CALC_BITTIMING=y
+CONFIG_CAN_SJA1000=m
+CONFIG_CAN_SJA1000_ISA=m
+CONFIG_CAN_SJA1000_PLATFORM=m
+CONFIG_CAN_EMS_PCI=m
+CONFIG_CAN_EMS_USB=m
+CONFIG_CAN_KVASER_PCI=m
+CONFIG_NETROM=m
+CONFIG_ROSE=m
+CONFIG_MKISS=m
+CONFIG_6PACK=m
+CONFIG_BPQETHER=m
+CONFIG_SCC=m
+CONFIG_DMASCC=m
+# CONFIG_SCC_DELAY is not set
+CONFIG_SCC_TRXECHO=y
+CONFIG_BAYCOM_SER_FDX=m
+CONFIG_BAYCOM_SER_HDX=m
+CONFIG_BAYCOM_PAR=m
+CONFIG_BAYCOM_EPP=m
+CONFIG_YAM=m
+
+#
+# IrDA (infrared) support
+#
+CONFIG_IRDA=m
+# CONFIG_IRDA_DEBUG is not set
+CONFIG_IRLAN=m
+CONFIG_IRNET=m
+CONFIG_IRCOMM=m
+# CONFIG_IRDA_ULTRA is not set
+CONFIG_IRDA_CACHE_LAST_LSAP=y
+CONFIG_IRDA_FAST_RR=y
+CONFIG_IRTTY_SIR=m
+CONFIG_DONGLE=y
+CONFIG_ACTISYS_DONGLE=m
+CONFIG_ACT200L_DONGLE=m
+CONFIG_ESI_DONGLE=m
+CONFIG_GIRBIL_DONGLE=m
+CONFIG_KINGSUN_DONGLE=m
+CONFIG_KSDAZZLE_DONGLE=m
+CONFIG_KS959_DONGLE=m
+CONFIG_LITELINK_DONGLE=m
+CONFIG_MA600_DONGLE=m
+CONFIG_MCP2120_DONGLE=m
+CONFIG_OLD_BELKIN_DONGLE=m
+CONFIG_TEKRAM_DONGLE=m
+CONFIG_TOIM3232_DONGLE=m
+
+CONFIG_ALI_FIR=m
+CONFIG_MCS_FIR=m
+CONFIG_NSC_FIR=m
+CONFIG_SIGMATEL_FIR=m
+CONFIG_SMC_IRCC_FIR=m
+# CONFIG_TOSHIBA_FIR is not set
+CONFIG_USB_IRDA=m
+CONFIG_VLSI_FIR=m
+CONFIG_VIA_FIR=m
+CONFIG_WINBOND_FIR=m
+
+#
+# Bluetooth support
+#
+CONFIG_BT=m
+CONFIG_BT_L2CAP=m
+CONFIG_BT_SCO=m
+CONFIG_BT_CMTP=m
+CONFIG_BT_RFCOMM=m
+CONFIG_BT_RFCOMM_TTY=y
+CONFIG_BT_BNEP=m
+CONFIG_BT_BNEP_MC_FILTER=y
+CONFIG_BT_BNEP_PROTO_FILTER=y
+CONFIG_BT_HIDP=m
+
+#
+# Bluetooth device drivers
+#
+CONFIG_BT_HCIBTUSB=m
+# Disable the BT_HCIUSB driver.
+# It sucks more power than BT_HCIBTUSB which has the same functionality.
+CONFIG_BT_HCIUART=m
+CONFIG_BT_HCIUART_H4=y
+CONFIG_BT_HCIUART_BCSP=y
+CONFIG_BT_HCIDTL1=m
+CONFIG_BT_HCIBT3C=m
+CONFIG_BT_HCIBLUECARD=m
+CONFIG_BT_HCIBTUART=m
+CONFIG_BT_HCIVHCI=m
+CONFIG_BT_HCIBCM203X=m
+CONFIG_BT_HCIBFUSB=m
+CONFIG_BT_HCIBPA10X=m
+CONFIG_BT_HCIBTSDIO=m
+CONFIG_BT_HCIUART_LL=y
+CONFIG_BT_MRVL=m
+CONFIG_BT_MRVL_SDIO=m
+
+#
+# ISDN subsystem
+#
+CONFIG_ISDN=y
+CONFIG_MISDN=m
+CONFIG_MISDN_DSP=m
+CONFIG_MISDN_L1OIP=m
+CONFIG_MISDN_AVMFRITZ=m
+CONFIG_MISDN_SPEEDFAX=m
+CONFIG_MISDN_INFINEON=m
+CONFIG_MISDN_W6692=m
+CONFIG_MISDN_NETJET=m
+
+#
+# mISDN hardware drivers
+#
+CONFIG_MISDN_HFCPCI=m
+CONFIG_MISDN_HFCMULTI=m
+CONFIG_ISDN_I4L=m
+CONFIG_ISDN_DRV_AVMB1_B1PCI=m
+CONFIG_ISDN_DRV_AVMB1_B1PCMCIA=m
+CONFIG_ISDN_DRV_AVMB1_T1PCI=m
+CONFIG_ISDN_DRV_AVMB1_C4=m
+
+CONFIG_MISDN_HFCUSB=m
+
+CONFIG_ISDN_PPP=y
+CONFIG_ISDN_PPP_VJ=y
+CONFIG_ISDN_MPP=y
+# CONFIG_ISDN_PPP_BSDCOMP is not set
+CONFIG_ISDN_TTY_FAX=y
+CONFIG_DE_AOC=y
+
+CONFIG_ISDN_AUDIO=y
+
+CONFIG_ISDN_DRV_HISAX=m
+CONFIG_ISDN_DRV_AVMB1_B1PCIV4=y
+CONFIG_ISDN_DRV_AVMB1_AVM_CS=m
+
+CONFIG_ISDN_CAPI_CAPIDRV=m
+CONFIG_ISDN_DIVERSION=m
+
+CONFIG_HISAX_EURO=y
+CONFIG_HISAX_1TR6=y
+CONFIG_HISAX_NI1=y
+CONFIG_HISAX_MAX_CARDS=8
+CONFIG_HISAX_16_3=y
+CONFIG_HISAX_TELESPCI=y
+CONFIG_HISAX_S0BOX=y
+CONFIG_HISAX_FRITZPCI=y
+CONFIG_HISAX_AVM_A1_PCMCIA=y
+CONFIG_HISAX_ELSA=y
+CONFIG_HISAX_DIEHLDIVA=y
+CONFIG_HISAX_SEDLBAUER=y
+CONFIG_HISAX_NETJET=y
+CONFIG_HISAX_NETJET_U=y
+CONFIG_HISAX_NICCY=y
+CONFIG_HISAX_BKM_A4T=y
+CONFIG_HISAX_SCT_QUADRO=y
+CONFIG_HISAX_GAZEL=y
+CONFIG_HISAX_HFC_PCI=y
+CONFIG_HISAX_W6692=y
+CONFIG_HISAX_HFC_SX=y
+CONFIG_HISAX_ENTERNOW_PCI=y
+# CONFIG_HISAX_DEBUG is not set
+CONFIG_HISAX_AVM_A1_CS=m
+CONFIG_HISAX_ST5481=m
+# CONFIG_HISAX_HFCUSB is not set
+CONFIG_HISAX_FRITZ_PCIPNP=m
+CONFIG_HISAX_NO_SENDCOMPLETE=y
+CONFIG_HISAX_NO_LLC=y
+CONFIG_HISAX_NO_KEYPAD=y
+CONFIG_HISAX_SEDLBAUER_CS=m
+CONFIG_HISAX_ELSA_CS=m
+CONFIG_HISAX_TELES_CS=m
+CONFIG_HISAX_HFC4S8S=m
+
+CONFIG_ISDN_DRV_LOOP=m
+CONFIG_HYSDN=m
+CONFIG_HYSDN_CAPI=y
+
+
+#
+# CAPI subsystem
+#
+CONFIG_ISDN_CAPI=m
+# CONFIG_CAPI_TRACE is not set
+CONFIG_ISDN_DRV_AVMB1_VERBOSE_REASON=y
+CONFIG_ISDN_CAPI_MIDDLEWARE=y
+CONFIG_ISDN_CAPI_CAPI20=m
+CONFIG_ISDN_CAPI_CAPIFS_BOOL=y
+CONFIG_ISDN_CAPI_CAPIFS=m
+
+#
+# CAPI hardware drivers
+#
+
+#
+# Active AVM cards
+#
+CONFIG_CAPI_AVM=y
+
+#
+# Active Eicon DIVA Server cards
+#
+# CONFIG_CAPI_EICON is not set
+CONFIG_ISDN_DIVAS=m
+CONFIG_ISDN_DIVAS_BRIPCI=y
+CONFIG_ISDN_DIVAS_PRIPCI=y
+CONFIG_ISDN_DIVAS_DIVACAPI=m
+CONFIG_ISDN_DIVAS_USERIDI=m
+CONFIG_ISDN_DIVAS_MAINT=m
+
+CONFIG_ISDN_DRV_GIGASET=m
+CONFIG_GIGASET_BASE=m
+CONFIG_GIGASET_M101=m
+CONFIG_GIGASET_M105=m
+# CONFIG_GIGASET_DEBUG is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+
+#
+# Input device support
+#
+CONFIG_INPUT=y
+CONFIG_INPUT_SPARSEKMAP=m
+CONFIG_INPUT_FF_MEMLESS=m
+
+#
+# Userland interfaces
+#
+CONFIG_INPUT_MOUSEDEV=y
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+CONFIG_INPUT_JOYDEV=m
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_EVBUG is not set
+
+CONFIG_INPUT_TABLET=y
+CONFIG_TABLET_USB_ACECAD=m
+CONFIG_TABLET_USB_AIPTEK=m
+CONFIG_TABLET_USB_GTCO=m
+CONFIG_TABLET_USB_KBTAB=m
+CONFIG_TABLET_USB_WACOM=m
+
+CONFIG_INPUT_POWERMATE=m
+CONFIG_INPUT_YEALINK=m
+CONFIG_INPUT_CM109=m
+CONFIG_INPUT_POLLDEV=m
+
+#
+# Input I/O drivers
+#
+CONFIG_GAMEPORT=m
+CONFIG_GAMEPORT_NS558=m
+CONFIG_GAMEPORT_L4=m
+CONFIG_GAMEPORT_EMU10K1=m
+CONFIG_GAMEPORT_FM801=m
+CONFIG_SERIO=y
+CONFIG_SERIO_I8042=y
+CONFIG_SERIO_SERPORT=y
+CONFIG_SERIO_RAW=m
+
+# CONFIG_SERIO_CT82C710 is not set
+# CONFIG_SERIO_PARKBD is not set
+# CONFIG_SERIO_PCIPS2 is not set
+
+#
+# Input Device Drivers
+#
+CONFIG_INPUT_KEYBOARD=y
+CONFIG_KEYBOARD_ATKBD=y
+# CONFIG_KEYBOARD_SUNKBD is not set
+# CONFIG_KEYBOARD_XTKBD is not set
+# CONFIG_KEYBOARD_MATRIX is not set
+# CONFIG_KEYBOARD_NEWTON is not set
+# CONFIG_KEYBOARD_STOWAWAY is not set
+# CONFIG_KEYBOARD_LKKBD is not set
+# CONFIG_KEYBOARD_LM8323 is not set
+CONFIG_QT2160=m
+CONFIG_KEYBOARD_ADP5588=m
+CONFIG_KEYBOARD_MAX7359=m
+CONFIG_KEYBOARD_OPENCORES=m
+CONFIG_INPUT_MOUSE=y
+CONFIG_MOUSE_PS2=y
+# CONFIG_MOUSE_PS2_TOUCHKIT is not set
+CONFIG_MOUSE_PS2_ELANTECH=y
+CONFIG_MOUSE_PS2_SENTELIC=y
+CONFIG_MOUSE_SERIAL=m
+CONFIG_MOUSE_VSXXXAA=m
+CONFIG_MOUSE_APPLETOUCH=m
+CONFIG_MOUSE_BCM5974=m
+CONFIG_MOUSE_SYNAPTICS_I2C=m
+CONFIG_INPUT_JOYSTICK=y
+CONFIG_JOYSTICK_ANALOG=m
+CONFIG_JOYSTICK_A3D=m
+CONFIG_JOYSTICK_ADI=m
+CONFIG_JOYSTICK_COBRA=m
+CONFIG_JOYSTICK_GF2K=m
+CONFIG_JOYSTICK_GRIP=m
+CONFIG_JOYSTICK_GRIP_MP=m
+CONFIG_JOYSTICK_GUILLEMOT=m
+CONFIG_JOYSTICK_INTERACT=m
+CONFIG_JOYSTICK_SIDEWINDER=m
+CONFIG_JOYSTICK_TMDC=m
+CONFIG_JOYSTICK_IFORCE=m
+CONFIG_JOYSTICK_IFORCE_USB=y
+CONFIG_JOYSTICK_IFORCE_232=y
+CONFIG_JOYSTICK_WARRIOR=m
+CONFIG_JOYSTICK_MAGELLAN=m
+CONFIG_JOYSTICK_SPACEORB=m
+CONFIG_JOYSTICK_SPACEBALL=m
+CONFIG_JOYSTICK_STINGER=m
+CONFIG_JOYSTICK_DB9=m
+CONFIG_JOYSTICK_GAMECON=m
+CONFIG_JOYSTICK_TURBOGRAFX=m
+CONFIG_JOYSTICK_JOYDUMP=m
+CONFIG_JOYSTICK_TWIDJOY=m
+CONFIG_JOYSTICK_WALKERA0701=m
+CONFIG_JOYSTICK_XPAD=m
+CONFIG_JOYSTICK_XPAD_FF=y
+CONFIG_JOYSTICK_XPAD_LEDS=y
+CONFIG_JOYSTICK_ZHENHUA=m
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_GUNZE=m
+CONFIG_TOUCHSCREEN_ELO=m
+CONFIG_TOUCHSCREEN_FUJITSU=m
+CONFIG_TOUCHSCREEN_HTCPEN=m
+CONFIG_TOUCHSCREEN_INEXIO=m
+CONFIG_TOUCHSCREEN_MTOUCH=m
+CONFIG_TOUCHSCREEN_MK712=m
+CONFIG_TOUCHSCREEN_PENMOUNT=m
+CONFIG_TOUCHSCREEN_TSC2007=m
+CONFIG_TOUCHSCREEN_AD7879_I2C=m
+CONFIG_TOUCHSCREEN_TOUCHIT213=m
+CONFIG_TOUCHSCREEN_TOUCHRIGHT=m
+CONFIG_TOUCHSCREEN_TOUCHWIN=m
+CONFIG_TOUCHSCREEN_UCB1400=m
+CONFIG_TOUCHSCREEN_WACOM_W8001=m
+CONFIG_TOUCHSCREEN_USB_E2I=y
+CONFIG_TOUCHSCREEN_USB_COMPOSITE=m
+# CONFIG_TOUCHSCREEN_WM97XX is not set
+CONFIG_TOUCHSCREEN_EETI=m
+CONFIG_TOUCHSCREEN_W90X900=m
+CONFIG_TOUCHSCREEN_MCS5000=m
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_PCSPKR=m
+CONFIG_INPUT_UINPUT=m
+CONFIG_INPUT_WISTRON_BTNS=m
+CONFIG_INPUT_ATLAS_BTNS=m
+
+CONFIG_INPUT_ATI_REMOTE=m
+CONFIG_INPUT_ATI_REMOTE2=m
+CONFIG_INPUT_KEYSPAN_REMOTE=m
+CONFIG_INPUT_IMON=m
+
+CONFIG_MAC_EMUMOUSEBTN=y
+
+CONFIG_INPUT_WM831X_ON=m
+
+CONFIG_INPUT_APPLEIR=m
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_HW_CONSOLE=y
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_SERIAL_NONSTANDARD=y
+CONFIG_ROCKETPORT=m
+CONFIG_SYNCLINK=m
+CONFIG_SYNCLINKMP=m
+CONFIG_SYNCLINK_GT=m
+CONFIG_N_HDLC=m
+# CONFIG_STALDRV is not set
+# CONFIG_IBM_ASM is not set
+CONFIG_TIFM_CORE=m
+CONFIG_TIFM_7XX1=m
+CONFIG_TCG_TPM=m
+CONFIG_TCG_TIS=m
+CONFIG_TCG_NSC=m
+CONFIG_TCG_ATMEL=m
+# CONFIG_TCG_INFINEON is not set
+CONFIG_TELCLOCK=m
+
+#
+# Serial drivers
+#
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_CS=m
+CONFIG_SERIAL_8250_NR_UARTS=32
+CONFIG_SERIAL_8250_RUNTIME_UARTS=4
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_DETECT_IRQ=y
+CONFIG_SERIAL_8250_RSA=y
+# CONFIG_COMPUTONE is not set
+CONFIG_CYCLADES=m
+# CONFIG_CYZ_INTR is not set
+# CONFIG_DIGIEPCA is not set
+# CONFIG_ESPSERIAL is not set
+# CONFIG_MOXA_INTELLIO is not set
+# CONFIG_MOXA_SMARTIO is not set
+# CONFIG_ISI is not set
+# CONFIG_RISCOM8 is not set
+# CONFIG_SPECIALIX is not set
+# CONFIG_SX is not set
+# CONFIG_RIO is not set
+# CONFIG_STALLION is not set
+# CONFIG_ISTALLION is not set
+CONFIG_SERIAL_JSM=m
+
+#
+# Non-8250 serial port support
+#
+CONFIG_SERIAL_CORE=y
+CONFIG_SERIAL_CORE_CONSOLE=y
+CONFIG_UNIX98_PTYS=y
+CONFIG_DEVPTS_MULTIPLE_INSTANCES=y
+# CONFIG_LEGACY_PTYS is not set
+CONFIG_PRINTER=m
+CONFIG_LP_CONSOLE=y
+CONFIG_PPDEV=m
+
+#
+# I2C support
+#
+CONFIG_I2C=m
+CONFIG_I2C_COMPAT=y
+CONFIG_I2C_CHARDEV=m
+
+#
+# I2C Algorithms
+#
+# CONFIG_I2C_DEBUG_ALGO is not set
+CONFIG_I2C_HELPER_AUTO=y
+CONFIG_I2C_ALGOBIT=m
+CONFIG_I2C_ALGOPCF=m
+
+#
+# I2C Hardware Bus support
+#
+
+CONFIG_I2C_ALGOPCA=m
+# CONFIG_I2C_ALI1535 is not set
+# CONFIG_I2C_ALI1563 is not set
+# CONFIG_I2C_ALI15X3 is not set
+# CONFIG_I2C_AMD756 is not set
+# CONFIG_I2C_AMD756_S4882 is not set
+# CONFIG_I2C_AMD8111 is not set
+# CONFIG_I2C_DEBUG_CORE is not set
+# CONFIG_I2C_DEBUG_BUS is not set
+# CONFIG_I2C_DEBUG_CHIP is not set
+# CONFIG_I2C_ELEKTOR is not set
+# CONFIG_I2C_I801 is not set
+# CONFIG_I2C_ISCH is not set
+# CONFIG_I2C_NFORCE2_S4985 is not set
+
+CONFIG_EEPROM_AT24=m
+CONFIG_EEPROM_LEGACY=m
+CONFIG_EEPROM_93CX6=m
+CONFIG_EEPROM_MAX6875=m
+
+CONFIG_I2C_NFORCE2=m
+# CONFIG_I2C_OCORES is not set
+CONFIG_I2C_PARPORT=m
+CONFIG_I2C_PARPORT_LIGHT=m
+CONFIG_I2C_PASEMI=m
+CONFIG_I2C_PCA_ISA=m
+CONFIG_I2C_PCA_PLATFORM=m
+# CONFIG_I2C_PIIX4 is not set
+# CONFIG_SCx200_ACB is not set
+# CONFIG_I2C_SIS5595 is not set
+# CONFIG_I2C_SIS630 is not set
+# CONFIG_I2C_SIS96X is not set
+CONFIG_I2C_SIMTEC=m
+CONFIG_I2C_STUB=m
+CONFIG_I2C_TINY_USB=m
+# CONFIG_I2C_TAOS_EVM is not set
+# CONFIG_I2C_VIA is not set
+# CONFIG_I2C_VIAPRO is not set
+CONFIG_I2C_VOODOO3=m
+# CONFIG_I2C_DESIGNWARE is not set
+
+#
+# I2C Hardware Sensors Chip support
+#
+CONFIG_SENSORS_ATK0110=m
+CONFIG_SENSORS_ABITUGURU=m
+CONFIG_SENSORS_ABITUGURU3=m
+CONFIG_SENSORS_AD7414=m
+CONFIG_SENSORS_AD7418=m
+CONFIG_SENSORS_ADM1021=m
+CONFIG_SENSORS_ADM1025=m
+CONFIG_SENSORS_ADM1026=m
+CONFIG_SENSORS_ADM1029=m
+CONFIG_SENSORS_ADM1031=m
+CONFIG_SENSORS_ADM9240=m
+CONFIG_SENSORS_ADS7828=m
+CONFIG_SENSORS_ADT7462=m
+CONFIG_SENSORS_ADT7470=m
+CONFIG_SENSORS_ADT7473=m
+CONFIG_SENSORS_ADT7475=m
+CONFIG_SENSORS_APPLESMC=m
+CONFIG_SENSORS_ASB100=m
+CONFIG_SENSORS_ATXP1=m
+CONFIG_SENSORS_CORETEMP=m
+CONFIG_SENSORS_DME1737=m
+CONFIG_SENSORS_DS1621=m
+# CONFIG_DS1682 is not set
+CONFIG_SENSORS_F71805F=m
+CONFIG_SENSORS_F71882FG=m
+CONFIG_SENSORS_F75375S=m
+CONFIG_SENSORS_FSCHMD=m
+CONFIG_SENSORS_G760A=m
+CONFIG_SENSORS_GL518SM=m
+CONFIG_SENSORS_GL520SM=m
+CONFIG_SENSORS_HDAPS=m
+# CONFIG_SENSORS_I5K_AMB is not set
+# FIXME: IBMAEM x86 only?
+CONFIG_SENSORS_IBMAEM=m
+CONFIG_SENSORS_IBMPEX=m
+CONFIG_SENSORS_IT87=m
+CONFIG_SENSORS_K8TEMP=m
+CONFIG_SENSORS_LIS3LV02D=m
+CONFIG_SENSORS_LM63=m
+CONFIG_SENSORS_LM75=m
+CONFIG_SENSORS_LM77=m
+CONFIG_SENSORS_LM78=m
+CONFIG_SENSORS_LM80=m
+CONFIG_SENSORS_LM83=m
+CONFIG_SENSORS_LM85=m
+CONFIG_SENSORS_LM87=m
+CONFIG_SENSORS_LM90=m
+CONFIG_SENSORS_LM92=m
+CONFIG_SENSORS_LM93=m
+CONFIG_SENSORS_LTC4245=m
+CONFIG_SENSORS_MAX1619=m
+CONFIG_SENSORS_MAX6650=m
+CONFIG_SENSORS_PC87360=m
+CONFIG_SENSORS_PC87427=m
+CONFIG_SENSORS_PCF8591=m
+CONFIG_SENSORS_SHT15=m
+CONFIG_SENSORS_SIS5595=m
+CONFIG_SENSORS_SMSC47M1=m
+CONFIG_SENSORS_SMSC47M192=m
+CONFIG_SENSORS_SMSC47B397=m
+CONFIG_SENSORS_THMC50=m
+CONFIG_SENSORS_TMP401=m
+CONFIG_SENSORS_TSL2550=m
+CONFIG_SENSORS_VIA686A=m
+CONFIG_SENSORS_VIA_CPUTEMP=m
+CONFIG_SENSORS_VT1211=m
+CONFIG_SENSORS_VT8231=m
+CONFIG_SENSORS_W83627HF=m
+CONFIG_SENSORS_W83781D=m
+CONFIG_SENSORS_W83L785TS=m
+CONFIG_SENSORS_W83L786NG=m
+CONFIG_SENSORS_W83627EHF=m
+CONFIG_SENSORS_W83791D=m
+CONFIG_SENSORS_W83792D=m
+CONFIG_SENSORS_W83793=m
+CONFIG_SENSORS_LTC4215=m
+CONFIG_SENSORS_LM95241=m
+CONFIG_SENSORS_TMP421=m
+CONFIG_SENSORS_WM8350=m
+CONFIG_SENSORS_WM831X=m
+
+CONFIG_W1=m
+CONFIG_W1_CON=y
+# This is busted.
+# If we enable it, it steals Matrox cards, and the
+# framebuffer drivers stop working.
+# CONFIG_W1_MASTER_MATROX is not set
+CONFIG_W1_MASTER_DS2482=m
+CONFIG_W1_MASTER_DS2490=m
+CONFIG_W1_MASTER_DS1WM=m
+CONFIG_W1_SLAVE_THERM=m
+CONFIG_W1_SLAVE_SMEM=m
+CONFIG_W1_SLAVE_DS2431=m
+CONFIG_W1_SLAVE_DS2433=m
+CONFIG_W1_SLAVE_DS2433_CRC=y
+CONFIG_W1_SLAVE_DS2760=m
+#
+# Mice
+#
+
+#
+# IPMI
+#
+CONFIG_IPMI_HANDLER=m
+# CONFIG_IPMI_PANIC_EVENT is not set
+CONFIG_IPMI_DEVICE_INTERFACE=m
+CONFIG_IPMI_WATCHDOG=m
+CONFIG_IPMI_SI=m
+CONFIG_IPMI_POWEROFF=m
+
+#
+# Watchdog Cards
+#
+CONFIG_WATCHDOG=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+CONFIG_SOFT_WATCHDOG=m
+CONFIG_WDTPCI=m
+# CONFIG_ACQUIRE_WDT is not set
+# CONFIG_ADVANTECH_WDT is not set
+# CONFIG_EUROTECH_WDT is not set
+# CONFIG_IB700_WDT is not set
+# CONFIG_MIXCOMWD is not set
+# CONFIG_SCx200_WDT is not set
+# CONFIG_60XX_WDT is not set
+CONFIG_W83877F_WDT=m
+CONFIG_W83627HF_WDT=m
+CONFIG_MACHZ_WDT=m
+# CONFIG_SC520_WDT is not set
+CONFIG_ALIM7101_WDT=m
+CONFIG_ALIM1535_WDT=m
+CONFIG_ITCO_WDT=m
+CONFIG_ITCO_VENDOR_SUPPORT=y
+# CONFIG_SC1200_WDT is not set
+# CONFIG_PC87413_WDT is not set
+# CONFIG_WAFER_WDT is not set
+# CONFIG_CPU5_WDT is not set
+CONFIG_I6300ESB_WDT=m
+CONFIG_IT8712F_WDT=m
+# CONFIG_SBC8360_WDT is not set
+# CONFIG_SBC7240_WDT is not set
+CONFIG_SMSC_SCH311X_WDT=m
+CONFIG_W83977F_WDT=m
+CONFIG_PCIPCWATCHDOG=m
+CONFIG_USBPCWATCHDOG=m
+# CONFIG_SBC_EPX_C3_WATCHDOG is not set
+CONFIG_WM8350_WATCHDOG=m
+CONFIG_WM831X_WATCHDOG=m
+
+CONFIG_HW_RANDOM=y
+CONFIG_HW_RANDOM_TIMERIOMEM=m
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+# CONFIG_RTC_DEBUG is not set
+# CONFIG_GEN_RTC is not set
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_HCTOSYS=y
+CONFIG_RTC_HCTOSYS_DEVICE="rtc0"
+CONFIG_RTC_INTF_SYSFS=y
+CONFIG_RTC_INTF_PROC=y
+CONFIG_RTC_INTF_DEV=y
+# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set
+CONFIG_RTC_DRV_CMOS=y
+CONFIG_RTC_DRV_DS1307=m
+CONFIG_RTC_DRV_DS1553=m
+CONFIG_RTC_DRV_DS1672=m
+CONFIG_RTC_DRV_DS1742=m
+CONFIG_RTC_DRV_DS1374=m
+# CONFIG_RTC_DRV_EP93XX is not set
+CONFIG_RTC_DRV_FM3130=m
+CONFIG_RTC_DRV_ISL1208=m
+CONFIG_RTC_DRV_M41T80=m
+CONFIG_RTC_DRV_M41T80_WDT=y
+CONFIG_RTC_DRV_M48T59=m
+CONFIG_RTC_DRV_MAX6900=m
+# CONFIG_RTC_DRV_M48T86 is not set
+CONFIG_RTC_DRV_PCF8563=m
+CONFIG_RTC_DRV_PCF8583=m
+CONFIG_RTC_DRV_RS5C372=m
+# CONFIG_RTC_DRV_SA1100 is not set
+# CONFIG_RTC_DRV_TEST is not set
+CONFIG_RTC_DRV_X1205=m
+CONFIG_RTC_DRV_V3020=m
+CONFIG_RTC_DRV_STK17TA8=m
+# CONFIG_RTC_DRV_S35390A is not set
+CONFIG_RTC_DRV_RX8581=m
+CONFIG_RTC_DRV_RX8025=m
+CONFIG_RTC_DRV_DS1286=m
+CONFIG_RTC_DRV_M48T35=m
+CONFIG_RTC_DRV_BQ4802=m
+CONFIG_RTC_DRV_WM8350=m
+CONFIG_RTC_DRV_AB3100=m
+CONFIG_RTC_DRV_WM831X=m
+
+CONFIG_DTLK=m
+CONFIG_R3964=m
+# CONFIG_APPLICOM is not set
+# CONFIG_SONYPI is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+CONFIG_AGP=y
+CONFIG_AGP_ALI=y
+CONFIG_AGP_ATI=y
+CONFIG_AGP_AMD=y
+CONFIG_AGP_AMD64=y
+CONFIG_AGP_INTEL=y
+CONFIG_AGP_NVIDIA=y
+CONFIG_AGP_SIS=y
+CONFIG_AGP_SWORKS=y
+CONFIG_AGP_VIA=y
+CONFIG_AGP_EFFICEON=y
+CONFIG_VGA_ARB=y
+CONFIG_DRM=m
+CONFIG_DRM_TDFX=m
+CONFIG_DRM_R128=m
+CONFIG_DRM_RADEON=m
+CONFIG_DRM_RADEON_KMS=y
+CONFIG_DRM_I810=m
+CONFIG_DRM_I830=m
+CONFIG_DRM_MGA=m
+CONFIG_DRM_SIS=m
+CONFIG_DRM_SAVAGE=m
+CONFIG_DRM_I915=m
+CONFIG_DRM_I915_KMS=y
+CONFIG_DRM_VIA=m
+CONFIG_DRM_NOUVEAU=m
+CONFIG_DRM_NOUVEAU_KMS=y
+CONFIG_DRM_NOUVEAU_BACKLIGHT=y
+CONFIG_DRM_NOUVEAU_DEBUG=y
+CONFIG_DRM_I2C_CH7006=m
+
+#
+# PCMCIA character devices
+#
+# CONFIG_SYNCLINK_CS is not set
+
+CONFIG_CARDMAN_4000=m
+CONFIG_CARDMAN_4040=m
+
+CONFIG_MWAVE=m
+CONFIG_RAW_DRIVER=y
+CONFIG_MAX_RAW_DEVS=8192
+CONFIG_HANGCHECK_TIMER=m
+
+#
+# Multimedia devices
+#
+CONFIG_MEDIA_SUPPORT=m
+CONFIG_VIDEO_DEV=m
+# CONFIG_VIDEO_ADV_DEBUG is not set
+CONFIG_VIDEO_HELPER_CHIPS_AUTO=y
+CONFIG_VIDEO_ALLOW_V4L1=y
+CONFIG_VIDEO_V4L1_COMPAT=y
+CONFIG_VIDEO_V4L2=y
+# CONFIG_VIDEO_VIVI is not set
+
+#
+# Video For Linux
+#
+
+#
+# Video Adapters
+#
+CONFIG_V4L_USB_DRIVERS=y
+CONFIG_VIDEO_CAPTURE_DRIVERS=y
+CONFIG_VIDEO_AU0828=m
+CONFIG_VIDEO_BT848=m
+CONFIG_VIDEO_BT848_DVB=y
+CONFIG_VIDEO_BWQCAM=m
+# CONFIG_VIDEO_CAFE_CCIC is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA_PP is not set
+# CONFIG_VIDEO_CPIA_USB is not set
+CONFIG_VIDEO_CPIA2=m
+CONFIG_VIDEO_CQCAM=m
+CONFIG_VIDEO_CX23885=m
+CONFIG_VIDEO_CX18=m
+CONFIG_VIDEO_CX88=m
+CONFIG_VIDEO_CX88_DVB=m
+CONFIG_VIDEO_CX88_ALSA=m
+CONFIG_VIDEO_CX88_BLACKBIRD=m
+CONFIG_VIDEO_CX88_VP3054=m
+CONFIG_VIDEO_EM28XX=m
+CONFIG_VIDEO_EM28XX_ALSA=m
+CONFIG_VIDEO_EM28XX_DVB=m
+CONFIG_VIDEO_CX231XX=m
+CONFIG_VIDEO_CX231XX_ALSA=m
+CONFIG_VIDEO_CX231XX_DVB=m
+CONFIG_VIDEO_HEXIUM_ORION=m
+CONFIG_VIDEO_HEXIUM_GEMINI=m
+CONFIG_VIDEO_IVTV=m
+CONFIG_VIDEO_MEYE=m
+CONFIG_VIDEO_MXB=m
+# CONFIG_VIDEO_OVCAMCHIP is not set
+CONFIG_VIDEO_PVRUSB2_DVB=y
+CONFIG_VIDEO_HDPVR=m
+CONFIG_VIDEO_SAA5246A=m
+CONFIG_VIDEO_SAA5249=m
+CONFIG_VIDEO_SAA6588=m
+CONFIG_VIDEO_SAA7134=m
+CONFIG_VIDEO_SAA7134_ALSA=m
+CONFIG_VIDEO_SAA7134_DVB=m
+CONFIG_VIDEO_STRADIS=m
+CONFIG_VIDEO_USBVISION=m
+CONFIG_VIDEO_W9966=m
+CONFIG_VIDEO_ZORAN=m
+CONFIG_VIDEO_ZORAN_AVS6EYES=m
+CONFIG_VIDEO_ZORAN_BUZ=m
+CONFIG_VIDEO_ZORAN_DC10=m
+CONFIG_VIDEO_ZORAN_DC30=m
+CONFIG_VIDEO_ZORAN_LML33=m
+CONFIG_VIDEO_ZORAN_LML33R10=m
+CONFIG_VIDEO_ZORAN_ZR36060=m
+CONFIG_VIDEO_FB_IVTV=m
+CONFIG_VIDEO_SAA7164=m
+
+CONFIG_USB_VIDEO_CLASS=m
+CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV=y
+
+#
+# Radio Adapters
+#
+CONFIG_RADIO_GEMTEK_PCI=m
+CONFIG_RADIO_MAXIRADIO=m
+CONFIG_RADIO_MAESTRO=m
+
+CONFIG_MEDIA_ATTACH=y
+# CONFIG_MEDIA_TUNER_CUSTOMISE is not set
+
+#
+# Digital Video Broadcasting Devices
+#
+CONFIG_DVB_CAPTURE_DRIVERS=y
+CONFIG_DVB_CORE=m
+CONFIG_DVB_MAX_ADAPTERS=8
+CONFIG_DVB_DYNAMIC_MINORS=y
+
+CONFIG_DVB_FE_CUSTOMISE=y
+CONFIG_DVB_STB0899=m
+CONFIG_DVB_STB6100=m
+CONFIG_DVB_STV090x=m
+CONFIG_DVB_STV6110x=m
+CONFIG_DVB_CX24110=m
+CONFIG_DVB_CX24123=m
+CONFIG_DVB_MT312=m
+CONFIG_DVB_ZL10036=m
+CONFIG_DVB_ZL10039=m
+CONFIG_DVB_S5H1420=m
+CONFIG_DVB_STV0288=m
+CONFIG_DVB_STB6000=m
+CONFIG_DVB_STV0299=m
+CONFIG_DVB_STV6110=m
+CONFIG_DVB_STV0900=m
+CONFIG_DVB_TDA8083=m
+CONFIG_DVB_TDA10086=m
+CONFIG_DVB_TDA8261=m
+CONFIG_DVB_VES1X93=m
+CONFIG_DVB_TUNER_ITD1000=m
+CONFIG_DVB_TUNER_CX24113=m
+CONFIG_DVB_TDA826X=m
+CONFIG_DVB_TUA6100=m
+CONFIG_DVB_CX24116=m
+CONFIG_DVB_SI21XX=m
+CONFIG_DVB_SP8870=m
+CONFIG_DVB_SP887X=m
+CONFIG_DVB_CX22700=m
+CONFIG_DVB_CX22702=m
+CONFIG_DVB_L64781=m
+CONFIG_DVB_NXT6000=m
+CONFIG_DVB_MT352=m
+CONFIG_DVB_DIB7000M=m
+CONFIG_DVB_DIB7000P=m
+CONFIG_DVB_TDA10048=m
+CONFIG_DVB_VES1820=m
+CONFIG_DVB_TDA10021=m
+CONFIG_DVB_TDA10023=m
+CONFIG_DVB_STV0297=m
+CONFIG_DVB_NXT200X=m
+CONFIG_DVB_OR51211=m
+CONFIG_DVB_OR51132=m
+CONFIG_DVB_BCM3510=m
+CONFIG_DVB_LGDT330X=m
+CONFIG_DVB_LGDT3305=m
+CONFIG_DVB_S5H1409=m
+CONFIG_DVB_AU8522=m
+CONFIG_DVB_S5H1411=m
+CONFIG_DVB_DIB8000=m
+CONFIG_DVB_PLL=m
+CONFIG_DVB_TUNER_DIB0070=m
+CONFIG_DVB_LNBP21=m
+CONFIG_DVB_ISL6421=m
+CONFIG_DVB_ISL6423=m
+CONFIG_DVB_LGS8GXX=m
+
+#
+# Supported Frontend Modules
+#
+CONFIG_DVB_BT8XX=m
+CONFIG_DVB_BUDGET_CORE=m
+CONFIG_DVB_PLUTO2=m
+CONFIG_SMS_SIANO_MDTV=m
+CONFIG_SMS_USB_DRV=m
+CONFIG_SMS_SDIO_DRV=m
+CONFIG_DVB_TTUSB_DEC=m
+CONFIG_DVB_USB_DTV5100=m
+CONFIG_DVB_USB_AF9015=m
+CONFIG_DVB_USB_ANYSEE=m
+CONFIG_DVB_USB_DW2102=m
+CONFIG_DVB_USB_FRIIO=m
+CONFIG_DVB_DM1105=m
+CONFIG_DVB_DRX397XD=m
+CONFIG_DVB_LGDT3304=m
+CONFIG_DVB_S921=m
+CONFIG_DVB_ISL6405=m
+CONFIG_DVB_LGS8GL5=m
+CONFIG_DVB_DUMMY_FE=m
+
+#
+# Supported SAA7146 based PCI Adapters
+#
+CONFIG_DVB_AV7110=m
+CONFIG_DVB_AV7110_OSD=y
+CONFIG_DVB_BUDGET=m
+CONFIG_DVB_BUDGET_CI=m
+CONFIG_DVB_BUDGET_AV=m
+CONFIG_DVB_BUDGET_PATCH=m
+
+#
+# Supported USB Adapters
+#
+CONFIG_DVB_TTUSB_BUDGET=m
+
+#
+# Supported FlexCopII (B2C2) Adapters
+#
+CONFIG_DVB_USB_CINERGY_T2=m
+CONFIG_DVB_B2C2_FLEXCOP=m
+CONFIG_DVB_B2C2_FLEXCOP_PCI=m
+CONFIG_DVB_B2C2_FLEXCOP_USB=m
+# CONFIG_DVB_B2C2_FLEXCOP_DEBUG is not set
+CONFIG_DVB_USB=m
+# CONFIG_DVB_USB_DEBUG is not set
+CONFIG_DVB_USB_A800=m
+CONFIG_DVB_USB_AF9005=m
+CONFIG_DVB_USB_AF9005_REMOTE=m
+CONFIG_DVB_USB_AU6610=m
+CONFIG_DVB_USB_CXUSB=m
+CONFIG_DVB_USB_DIBUSB_MB=m
+# CONFIG_DVB_USB_DIBUSB_MB_FAULTY is not set
+CONFIG_DVB_USB_DIBUSB_MC=m
+CONFIG_DVB_USB_DIB0700=m
+CONFIG_DVB_USB_DIGITV=m
+CONFIG_DVB_USB_DTT200U=m
+CONFIG_DVB_USB_GL861=m
+CONFIG_DVB_USB_GP8PSK=m
+CONFIG_DVB_USB_M920X=m
+CONFIG_DVB_USB_NOVA_T_USB2=m
+CONFIG_DVB_USB_CE6230=m
+CONFIG_DVB_USB_OPERA1=m
+CONFIG_DVB_USB_TTUSB2=m
+CONFIG_DVB_USB_UMT_010=m
+CONFIG_DVB_USB_VP702X=m
+CONFIG_DVB_USB_VP7045=m
+
+CONFIG_DVB_PT1=m
+
+CONFIG_VIDEO_SAA7146=m
+CONFIG_VIDEO_SAA7146_VV=m
+CONFIG_VIDEO_TUNER=m
+CONFIG_VIDEO_BTCX=m
+CONFIG_VIDEO_PVRUSB2=m
+CONFIG_VIDEO_PVRUSB2_SYSFS=y
+# CONFIG_VIDEO_PVRUSB2_DEBUGIFC is not set
+
+#
+# Broadcom Crystal HD video decoder driver
+#
+CONFIG_CRYSTALHD=m
+
+#
+# Graphics support
+#
+
+CONFIG_DISPLAY_SUPPORT=m
+CONFIG_VIDEO_OUTPUT_CONTROL=m
+
+CONFIG_FB=y
+# CONFIG_FB_FOREIGN_ENDIAN is not set
+CONFIG_FB_3DFX=m
+CONFIG_FB_3DFX_ACCEL=y
+CONFIG_FB_3DFX_I2C=y
+# CONFIG_FB_ARC is not set
+# CONFIG_FB_ARK is not set
+CONFIG_FB_ATY128=m
+CONFIG_FB_ATY=m
+CONFIG_FB_ATY_CT=y
+CONFIG_FB_ATY_GX=y
+CONFIG_FB_ATY_GENERIC_LCD=y
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_CARMINE is not set
+CONFIG_FB_CIRRUS=m
+# CONFIG_FB_CYBER2000 is not set
+# CONFIG_FB_GEODE is not set
+# CONFIG_FB_HECUBA is not set
+# CONFIG_FB_HGA is not set
+CONFIG_FB_I810=m
+CONFIG_FB_I810_GTF=y
+CONFIG_FB_I810_I2C=y
+# CONFIG_FB_IMSTT is not set
+# CONFIG_FB_INTEL is not set
+# CONFIG_FB_INTEL_DEBUG is not set
+# CONFIG_FB_INTEL_I2C is not set
+CONFIG_FB_KYRO=m
+# CONFIG_FB_LE80578 is not set
+CONFIG_FB_MATROX=m
+CONFIG_FB_MATROX_MILLENIUM=y
+CONFIG_FB_MATROX_MYSTIQUE=y
+CONFIG_FB_MATROX_G=y
+CONFIG_FB_MATROX_I2C=m
+CONFIG_FB_MATROX_MAVEN=m
+CONFIG_FB_NEOMAGIC=m
+CONFIG_FB_NVIDIA=m
+# CONFIG_FB_NVIDIA_DEBUG is not set
+CONFIG_FB_NVIDIA_I2C=y
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_PM2_FIFO_DISCONNECT is not set
+# CONFIG_FB_PM3 is not set
+CONFIG_FB_RADEON=m
+# CONFIG_FB_RADEON_DEBUG is not set
+CONFIG_FB_RADEON_I2C=y
+CONFIG_FB_RIVA=m
+# CONFIG_FB_RIVA_DEBUG is not set
+# CONFIG_FB_RIVA_I2C is not set
+# CONFIG_FB_S1D13XXX is not set
+CONFIG_FB_S3=m
+CONFIG_FB_SAVAGE=m
+CONFIG_FB_SAVAGE_I2C=y
+CONFIG_FB_SAVAGE_ACCEL=y
+# CONFIG_FB_SIS is not set
+CONFIG_FB_SIS_300=y
+CONFIG_FB_SIS_315=y
+CONFIG_FB_SM501=m
+CONFIG_FB_TILEBLITTING=y
+CONFIG_FB_TRIDENT=m
+# CONFIG_FB_UVESA is not set
+CONFIG_FB_VESA=y
+CONFIG_FB_VGA16=m
+CONFIG_FB_VIRTUAL=m
+CONFIG_FB_VOODOO1=m
+# CONFIG_FB_VT8623 is not set
+CONFIG_FB_EFI=y
+CONFIG_FB_VIA=m
+CONFIG_FB_METRONOME=m
+CONFIG_FB_MB862XX=m
+CONFIG_FB_MB862XX_PCI_GDC=y
+CONFIG_FB_MB862XX_LIME=y
+# CONFIG_FB_PRE_INIT_FB is not set
+# CONFIG_FB_TMIO is not set
+# CONFIG_FB_BROADSHEET is not set
+
+# CONFIG_FIRMWARE_EDID is not set
+
+#
+# Console display driver support
+#
+CONFIG_VGA_CONSOLE=y
+CONFIG_VGACON_SOFT_SCROLLBACK=y
+CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64
+# CONFIG_MDA_CONSOLE is not set
+CONFIG_DUMMY_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
+# CONFIG_FONTS is not set
+
+#
+# Logo configuration
+#
+CONFIG_LOGO=y
+# CONFIG_LOGO_LINUX_MONO is not set
+# CONFIG_LOGO_LINUX_VGA16 is not set
+CONFIG_LOGO_LINUX_CLUT224=y
+
+#
+# Sound
+#
+CONFIG_SOUND=m
+
+#
+# Advanced Linux Sound Architecture
+#
+CONFIG_SND=y
+CONFIG_SOUND_OSS_CORE_PRECLAIM=y
+# CONFIG_SND_DEBUG_VERBOSE is not set
+CONFIG_SND_VERBOSE_PROCFS=y
+CONFIG_SND_SEQUENCER=y
+CONFIG_SND_HRTIMER=y
+CONFIG_SND_SEQ_HRTIMER_DEFAULT=y
+CONFIG_SND_SEQ_DUMMY=m
+CONFIG_SND_SEQUENCER_OSS=y
+CONFIG_SND_SEQ_RTCTIMER_DEFAULT=y
+CONFIG_SND_OSSEMUL=y
+CONFIG_SND_MIXER_OSS=y
+CONFIG_SND_PCM_OSS=y
+CONFIG_SND_PCM_OSS_PLUGINS=y
+CONFIG_SND_RTCTIMER=y
+CONFIG_SND_DYNAMIC_MINORS=y
+# CONFIG_SND_SUPPORT_OLD_API is not set
+
+#
+# Generic devices
+#
+CONFIG_SND_DUMMY=m
+CONFIG_SND_VIRMIDI=m
+CONFIG_SND_MTPAV=m
+CONFIG_SND_MTS64=m
+CONFIG_SND_SERIAL_U16550=m
+CONFIG_SND_MPU401=m
+CONFIG_SND_PORTMAN2X4=m
+CONFIG_SND_AC97_POWER_SAVE=y
+CONFIG_SND_AC97_POWER_SAVE_DEFAULT=0
+
+CONFIG_SND_DRIVERS=y
+
+#
+# ISA devices
+#
+CONFIG_SND_AD1889=m
+# CONFIG_SND_WAVEFRONT is not set
+# CONFIG_SND_MSND_PINNACLE is not set
+# CONFIG_SND_MSND_CLASSIC is not set
+
+#
+# PCI devices
+#
+CONFIG_SND_PCI=y
+CONFIG_SND_ADLIB=m
+CONFIG_SND_ALI5451=m
+CONFIG_SND_ALS300=m
+CONFIG_SND_ALS4000=m
+CONFIG_SND_ATIIXP=m
+CONFIG_SND_ATIIXP_MODEM=m
+CONFIG_SND_AU8810=m
+CONFIG_SND_AU8820=m
+CONFIG_SND_AU8830=m
+# CONFIG_SND_AW2 is not set
+CONFIG_SND_AZT3328=m
+CONFIG_SND_BT87X=m
+# CONFIG_SND_BT87X_OVERCLOCK is not set
+CONFIG_SND_CA0106=m
+CONFIG_SND_CMIPCI=m
+CONFIG_SND_CS46XX=m
+CONFIG_SND_CS46XX_NEW_DSP=y
+CONFIG_SND_CS4281=m
+CONFIG_SND_CS5530=m
+CONFIG_SND_CS5535AUDIO=m
+CONFIG_SND_EMU10K1=m
+CONFIG_SND_EMU10K1X=m
+CONFIG_SND_ENS1370=m
+CONFIG_SND_ENS1371=m
+CONFIG_SND_ES1938=m
+CONFIG_SND_ES1968=m
+CONFIG_SND_FM801=m
+CONFIG_SND_FM801_TEA575X_BOOL=y
+CONFIG_SND_CTXFI=m
+CONFIG_SND_LX6464ES=m
+CONFIG_SND_HDA_INTEL=y
+CONFIG_SND_HDA_INPUT_BEEP=y
+CONFIG_SND_HDA_INPUT_JACK=y
+CONFIG_SND_HDA_PATCH_LOADER=y
+CONFIG_SND_HDA_HWDEP=y
+CONFIG_SND_HDA_CODEC_REALTEK=y
+CONFIG_SND_HDA_CODEC_CA0110=y
+CONFIG_SND_HDA_CODEC_ANALOG=y
+CONFIG_SND_HDA_CODEC_SIGMATEL=y
+CONFIG_SND_HDA_CODEC_VIA=y
+CONFIG_SND_HDA_CODEC_ATIHDMI=y
+CONFIG_SND_HDA_CODEC_CIRRUS=y
+CONFIG_SND_HDA_CODEC_CONEXANT=y
+CONFIG_SND_HDA_CODEC_CMEDIA=y
+CONFIG_SND_HDA_CODEC_INTELHDMI=y
+CONFIG_SND_HDA_CODEC_SI3054=y
+CONFIG_SND_HDA_CODEC_NVHDMI=y
+CONFIG_SND_HDA_GENERIC=y
+CONFIG_SND_HDA_POWER_SAVE=y
+CONFIG_SND_HDA_POWER_SAVE_DEFAULT=0
+CONFIG_SND_HDA_RECONFIG=y
+CONFIG_SND_HDSPM=m
+CONFIG_SND_HIFIER=m
+CONFIG_SND_ICE1712=m
+CONFIG_SND_ICE1724=m
+CONFIG_SND_INTEL8X0=y
+CONFIG_SND_INTEL8X0M=m
+CONFIG_SND_KORG1212=m
+CONFIG_SND_MAESTRO3=m
+CONFIG_SND_MIRO=m
+CONFIG_SND_MIXART=m
+CONFIG_SND_NM256=m
+CONFIG_SND_OXYGEN=m
+CONFIG_SND_RME32=m
+CONFIG_SND_PCSP=m
+CONFIG_SND_PCXHR=m
+CONFIG_SND_RIPTIDE=m
+CONFIG_SND_RME96=m
+CONFIG_SND_RME9652=m
+CONFIG_SND_SC6000=m
+CONFIG_SND_SIS7019=m
+CONFIG_SND_SONICVIBES=m
+CONFIG_SND_HDSP=m
+CONFIG_SND_TRIDENT=m
+CONFIG_SND_VIA82XX=m
+CONFIG_SND_VIA82XX_MODEM=m
+CONFIG_SND_VIRTUOSO=m
+CONFIG_SND_VX222=m
+CONFIG_SND_YMFPCI=m
+
+#
+# ALSA USB devices
+#
+CONFIG_SND_USB=y
+CONFIG_SND_USB_AUDIO=m
+CONFIG_SND_USB_CAIAQ=m
+CONFIG_SND_USB_CAIAQ_INPUT=y
+CONFIG_SND_USB_USX2Y=m
+CONFIG_SND_USB_US122L=m
+
+#
+# PCMCIA devices
+#
+CONFIG_SND_PCMCIA=y
+CONFIG_SND_VXPOCKET=m
+CONFIG_SND_PDAUDIOCF=m
+
+#
+# Open Sound System
+#
+# CONFIG_SOUND_PRIME is not set
+
+#
+# USB support
+#
+CONFIG_USB=y
+CONFIG_USB_SUPPORT=y
+# CONFIG_USB_DEBUG is not set
+
+# DEPRECATED: See bug 362221. Fix udev.
+# CONFIG_USB_DEVICE_CLASS is not set
+
+
+#
+# Miscellaneous USB options
+#
+CONFIG_USB_DEVICEFS=y
+# CONFIG_USB_DYNAMIC_MINORS is not set
+CONFIG_USB_SUSPEND=y
+
+#
+# USB Host Controller Drivers
+#
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_EHCI_TT_NEWSCHED=y
+CONFIG_USB_OHCI_HCD=y
+# CONFIG_USB_OHCI_HCD_SSB is not set
+CONFIG_USB_UHCI_HCD=y
+# CONFIG_USB_SL811_CS is not set
+# CONFIG_USB_R8A66597_HCD is not set
+CONFIG_USB_XHCI_HCD=m
+# CONFIG_USB_XHCI_HCD_DEBUGGING is not set
+CONFIG_USB_ISP1362_HCD=m
+
+#
+# USB Device Class drivers
+#
+
+#
+# USB Bluetooth TTY can only be used with disabled Bluetooth subsystem
+#
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
+CONFIG_USB_WDM=m
+CONFIG_USB_TMC=m
+# CONFIG_BLK_DEV_UB is not set
+CONFIG_USB_STORAGE=m
+# CONFIG_USB_STORAGE_DEBUG is not set
+CONFIG_USB_STORAGE_CYPRESS_ATACB=y
+CONFIG_USB_STORAGE_DATAFAB=y
+CONFIG_USB_STORAGE_FREECOM=y
+CONFIG_USB_STORAGE_ISD200=y
+CONFIG_USB_STORAGE_SDDR09=y
+CONFIG_USB_STORAGE_SDDR55=y
+CONFIG_USB_STORAGE_JUMPSHOT=y
+CONFIG_USB_STORAGE_USBAT=y
+CONFIG_USB_STORAGE_ONETOUCH=y
+CONFIG_USB_STORAGE_ALAUDA=y
+CONFIG_USB_STORAGE_KARMA=y
+# CONFIG_USB_LIBUSUAL is not set
+
+#
+# USB Human Interface Devices (HID)
+#
+CONFIG_USB_HID=y
+
+CONFIG_HID_SUPPORT=y
+
+CONFIG_HID=m
+# debugging default is y upstream now
+CONFIG_HIDRAW=y
+CONFIG_HID_PID=y
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_PANTHERLORD_FF=y
+CONFIG_THRUSTMASTER_FF=y
+CONFIG_HID_WACOM=y
+CONFIG_ZEROPLUS_FF=y
+CONFIG_USB_HIDDEV=y
+CONFIG_USB_IDMOUSE=m
+CONFIG_DRAGONRISE_FF=y
+CONFIG_GREENASIA_FF=y
+CONFIG_SMARTJOYPLUS_FF=y
+
+#
+# USB Imaging devices
+#
+CONFIG_USB_MDC800=m
+CONFIG_USB_MICROTEK=m
+
+#
+# USB Multimedia devices
+#
+CONFIG_DAB=y
+CONFIG_USB_DABUSB=m
+
+CONFIG_USB_VICAM=m
+CONFIG_USB_DSBR=m
+# CONFIG_USB_ET61X251 is not set
+CONFIG_USB_M5602=m
+CONFIG_USB_STV06XX=m
+CONFIG_USB_GSPCA=m
+CONFIG_USB_GSPCA_MR97310A=m
+CONFIG_USB_GSPCA_BENQ=m
+CONFIG_USB_GSPCA_CONEX=m
+CONFIG_USB_GSPCA_CPIA1=m
+CONFIG_USB_GSPCA_ETOMS=m
+CONFIG_USB_GSPCA_FINEPIX=m
+CONFIG_USB_GSPCA_MARS=m
+CONFIG_USB_GSPCA_OV519=m
+CONFIG_USB_GSPCA_OV534=m
+CONFIG_USB_GSPCA_OV534_9=m
+CONFIG_USB_GSPCA_PAC207=m
+CONFIG_USB_GSPCA_PAC7302=m
+CONFIG_USB_GSPCA_PAC7311=m
+CONFIG_USB_GSPCA_SN9C2028=m
+CONFIG_USB_GSPCA_SN9C20X=m
+CONFIG_USB_GSPCA_SN9C20X_EVDEV=y
+CONFIG_USB_GSPCA_SONIXB=m
+CONFIG_USB_GSPCA_SONIXJ=m
+CONFIG_USB_GSPCA_SPCA500=m
+CONFIG_USB_GSPCA_SPCA501=m
+CONFIG_USB_GSPCA_SPCA505=m
+CONFIG_USB_GSPCA_SPCA506=m
+CONFIG_USB_GSPCA_SPCA508=m
+CONFIG_USB_GSPCA_SPCA561=m
+CONFIG_USB_GSPCA_STK014=m
+CONFIG_USB_GSPCA_STV0680=m
+CONFIG_USB_GSPCA_SUNPLUS=m
+CONFIG_USB_GSPCA_T613=m
+CONFIG_USB_GSPCA_TV8532=m
+CONFIG_USB_GSPCA_VC032X=m
+CONFIG_USB_GSPCA_ZC3XX=m
+CONFIG_USB_GSPCA_SQ905=m
+CONFIG_USB_GSPCA_SQ905C=m
+CONFIG_USB_GL860=m
+CONFIG_USB_GSPCA_JEILINJ=m
+
+CONFIG_USB_IBMCAM=m
+CONFIG_USB_KONICAWC=m
+# CONFIG_USB_OV511 is not set
+CONFIG_USB_S2255=m
+CONFIG_USB_SE401=m
+# CONFIG_VIDEO_SH_MOBILE_CEU is not set
+# CONFIG_USB_STV680 is not set
+# CONFIG_USB_SN9C102 is not set
+CONFIG_USB_ZR364XX=m
+CONFIG_SOC_CAMERA=m
+CONFIG_SOC_CAMERA_MT9M001=m
+CONFIG_SOC_CAMERA_MT9V022=m
+CONFIG_SOC_CAMERA_PLATFORM=m
+CONFIG_SOC_CAMERA_MT9M111=m
+CONFIG_SOC_CAMERA_MT9T031=m
+CONFIG_SOC_CAMERA_TW9910=m
+CONFIG_SOC_CAMERA_OV772X=m
+
+#
+# USB Network adaptors
+#
+CONFIG_USB_CATC=m
+CONFIG_USB_HSO=m
+CONFIG_USB_KAWETH=m
+CONFIG_USB_PEGASUS=m
+CONFIG_USB_RTL8150=m
+CONFIG_USB_USBNET=m
+CONFIG_USB_SPEEDTOUCH=m
+CONFIG_USB_NET_AX8817X=m
+CONFIG_USB_NET_DM9601=m
+CONFIG_USB_NET_SMSC95XX=m
+CONFIG_USB_NET_GL620A=m
+CONFIG_USB_NET_NET1080=m
+CONFIG_USB_NET_PLUSB=m
+CONFIG_USB_NET_MCS7830=m
+CONFIG_USB_NET_RNDIS_HOST=m
+CONFIG_USB_NET_CDC_SUBSET=m
+CONFIG_USB_NET_CDC_EEM=m
+CONFIG_USB_NET_ZAURUS=m
+CONFIG_USB_NET_INT51X1=m
+CONFIG_USB_CDC_PHONET=m
+
+#
+# USB Host-to-Host Cables
+#
+CONFIG_USB_AN2720=y
+CONFIG_USB_BELKIN=y
+
+#
+# Intelligent USB Devices/Gadgets
+#
+CONFIG_USB_ARMLINUX=y
+CONFIG_USB_EPSON2888=y
+CONFIG_USB_KC2190=y
+
+# CONFIG_USB_MUSB_HDRC is not set
+
+#
+# USB port drivers
+#
+CONFIG_USB_USS720=m
+
+#
+# USB Serial Converter support
+#
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_GENERIC=y
+CONFIG_USB_SERIAL_AIRCABLE=m
+CONFIG_USB_SERIAL_ARK3116=m
+CONFIG_USB_SERIAL_BELKIN=m
+CONFIG_USB_SERIAL_CH341=m
+CONFIG_USB_SERIAL_CYPRESS_M8=m
+CONFIG_USB_SERIAL_CYBERJACK=m
+CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m
+CONFIG_USB_SERIAL_CP210X=m
+CONFIG_USB_SERIAL_QUALCOMM=m
+CONFIG_USB_SERIAL_SYMBOL=m
+CONFIG_USB_SERIAL_EDGEPORT=m
+CONFIG_USB_SERIAL_EDGEPORT_TI=m
+CONFIG_USB_SERIAL_EMPEG=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+CONFIG_USB_SERIAL_FUNSOFT=m
+CONFIG_USB_SERIAL_GARMIN=m
+CONFIG_USB_SERIAL_HP4X=m
+CONFIG_USB_SERIAL_IPAQ=m
+CONFIG_USB_SERIAL_IPW=m
+CONFIG_USB_SERIAL_IR=m
+CONFIG_USB_SERIAL_IUU=m
+CONFIG_USB_SERIAL_KEYSPAN_PDA=m
+CONFIG_USB_SERIAL_KEYSPAN=m
+CONFIG_USB_SERIAL_KEYSPAN_MPR=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28X=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28XA=y
+CONFIG_USB_SERIAL_KEYSPAN_USA28XB=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19=y
+CONFIG_USB_SERIAL_KEYSPAN_USA18X=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19W=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19QW=y
+CONFIG_USB_SERIAL_KEYSPAN_USA19QI=y
+CONFIG_USB_SERIAL_KEYSPAN_USA49W=y
+CONFIG_USB_SERIAL_KEYSPAN_USA49WLC=y
+CONFIG_USB_SERIAL_KLSI=m
+CONFIG_USB_SERIAL_KOBIL_SCT=m
+CONFIG_USB_SERIAL_MCT_U232=m
+CONFIG_USB_SERIAL_MOS7720=m
+CONFIG_USB_SERIAL_MOS7840=m
+CONFIG_USB_SERIAL_MOTOROLA=m
+CONFIG_USB_SERIAL_NAVMAN=m
+CONFIG_USB_SERIAL_OPTION=y
+CONFIG_USB_SERIAL_OTI6858=m
+CONFIG_USB_SERIAL_OPTICON=m
+CONFIG_USB_SERIAL_OMNINET=m
+CONFIG_USB_SERIAL_PL2303=m
+CONFIG_USB_SERIAL_SAFE=m
+CONFIG_USB_SERIAL_SAFE_PADDED=y
+CONFIG_USB_SERIAL_SIERRAWIRELESS=m
+CONFIG_USB_SERIAL_SIEMENS_MPI=m
+CONFIG_USB_SERIAL_SPCP8X5=m
+CONFIG_USB_SERIAL_TI=m
+CONFIG_USB_SERIAL_VISOR=m
+CONFIG_USB_SERIAL_WHITEHEAT=m
+CONFIG_USB_SERIAL_XIRCOM=m
+CONFIG_USB_SERIAL_DEBUG=m
+
+CONFIG_USB_EZUSB=y
+CONFIG_USB_EMI62=m
+CONFIG_USB_LED=m
+# CONFIG_USB_CYPRESS_CY7C63 is not set
+CONFIG_USB_G_SERIAL=m
+
+#
+# USB Miscellaneous drivers
+#
+
+CONFIG_USB_ADUTUX=m
+CONFIG_USB_SEVSEG=m
+CONFIG_USB_ALI_M5632=y
+CONFIG_USB_APPLEDISPLAY=m
+CONFIG_USB_ATM=m
+CONFIG_USB_BERRY_CHARGE=m
+CONFIG_USB_CXACRU=m
+# CONFIG_USB_C67X00_HCD is not set
+# CONFIG_USB_CYTHERM is not set
+CONFIG_USB_EMI26=m
+CONFIG_USB_ETH=m
+CONFIG_USB_FTDI_ELAN=m
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+# CONFIG_USB_GADGET is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGETFS is not set
+# CONFIG_USB_ISP116X_HCD is not set
+# CONFIG_USB_ISP1760_HCD is not set
+# CONFIG_USB_OXU210HP_HCD is not set
+CONFIG_USB_IOWARRIOR=m
+CONFIG_USB_ISIGHTFW=m
+CONFIG_USB_VST=m
+CONFIG_USB_LCD=m
+CONFIG_USB_LD=m
+CONFIG_USB_LEGOTOWER=m
+CONFIG_USB_MON=y
+CONFIG_USB_PWC=m
+CONFIG_USB_PWC_INPUT_EVDEV=y
+# CONFIG_USB_PWC_DEBUG is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_QUICKCAM_MESSENGER is not set
+CONFIG_USB_SL811_HCD=m
+CONFIG_USB_SISUSBVGA=m
+CONFIG_USB_SISUSBVGA_CON=y
+CONFIG_RADIO_SI470X=y
+CONFIG_USB_SI470X=m
+CONFIG_I2C_SI470X=m
+CONFIG_RADIO_SI4713=m
+CONFIG_USB_MR800=m
+CONFIG_USB_STKWEBCAM=m
+# CONFIG_USB_TEST is not set
+CONFIG_USB_TRANCEVIBRATOR=m
+CONFIG_USB_U132_HCD=m
+CONFIG_USB_UEAGLEATM=m
+# CONFIG_USB_W9968CF is not set
+CONFIG_USB_XUSBATM=m
+# CONFIG_USB_ZC0301 is not set
+CONFIG_USB_ZERO=m
+
+CONFIG_USB_ANNOUNCE_NEW_DEVICES=y
+
+#
+# Sonics Silicon Backplane
+#
+CONFIG_SSB=m
+CONFIG_SSB_PCIHOST=y
+CONFIG_SSB_SDIOHOST=y
+CONFIG_SSB_PCMCIAHOST=y
+# CONFIG_SSB_SILENT is not set
+# CONFIG_SSB_DEBUG is not set
+CONFIG_SSB_DRIVER_PCICORE=y
+
+# Multifunction USB devices
+# CONFIG_MFD_PCF50633 is not set
+CONFIG_PCF50633_ADC=m
+CONFIG_PCF50633_GPIO=m
+CONFIG_AB3100_CORE=m
+CONFIG_INPUT_PCF50633_PMU=m
+CONFIG_INPUT_GPIO_ROTARY_ENCODER=m
+CONFIG_CHARGER_PCF50633=m
+CONFIG_REGULATOR_PCF50633=m
+CONFIG_RTC_DRV_PCF50633=m
+
+CONFIG_MFD_SM501=m
+CONFIG_MFD_SM501_GPIO=y
+# CONFIG_MFD_TC6393XB is not set
+CONFIG_MFD_WM8400=m
+CONFIG_MFD_WM8350_I2C=m
+CONFIG_MFD_WM8350=m
+CONFIG_MFD_WM831X=m
+CONFIG_AB3100_OTP=m
+
+#
+# File systems
+#
+CONFIG_MISC_FILESYSTEMS=y
+
+CONFIG_EXT2_FS=m
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT2_FS_XIP=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_DEFAULTS_TO_ORDERED=y
+CONFIG_EXT3_FS_XATTR=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_XATTR=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_JBD2=y
+CONFIG_FS_MBCACHE=y
+CONFIG_REISERFS_FS=m
+# CONFIG_REISERFS_CHECK is not set
+CONFIG_REISERFS_PROC_INFO=y
+CONFIG_REISERFS_FS_XATTR=y
+CONFIG_REISERFS_FS_POSIX_ACL=y
+CONFIG_REISERFS_FS_SECURITY=y
+CONFIG_JFS_FS=m
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
+CONFIG_JFS_POSIX_ACL=y
+CONFIG_JFS_SECURITY=y
+CONFIG_XFS_FS=m
+# CONFIG_XFS_DEBUG is not set
+# CONFIG_XFS_RT is not set
+CONFIG_XFS_QUOTA=y
+CONFIG_XFS_POSIX_ACL=y
+CONFIG_MINIX_FS=m
+CONFIG_ROMFS_FS=m
+CONFIG_QUOTA=y
+CONFIG_QUOTA_NETLINK_INTERFACE=y
+# CONFIG_PRINT_QUOTA_WARNING is not set
+# CONFIG_QFMT_V1 is not set
+CONFIG_QFMT_V2=y
+CONFIG_QUOTACTL=y
+CONFIG_DNOTIFY=y
+# Autofsv3 is obsolete.
+# CONFIG_AUTOFS_FS is not set
+CONFIG_AUTOFS4_FS=m
+CONFIG_EXOFS_FS=m
+# CONFIG_EXOFS_DEBUG is not set
+CONFIG_NILFS2_FS=m
+
+CONFIG_FSCACHE=m
+CONFIG_FSCACHE_STATS=y
+# CONFIG_FSCACHE_HISTOGRAM is not set
+# CONFIG_FSCACHE_DEBUG is not set
+CONFIG_FSCACHE_OBJECT_LIST=y
+
+CONFIG_CACHEFILES=m
+# CONFIG_CACHEFILES_DEBUG is not set
+# CONFIG_CACHEFILES_HISTOGRAM is not set
+
+#
+# CD-ROM/DVD Filesystems
+#
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_ZISOFS=y
+CONFIG_UDF_FS=m
+
+#
+# DOS/FAT/NT Filesystems
+#
+CONFIG_FAT_FS=m
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=437
+CONFIG_FAT_DEFAULT_IOCHARSET="ascii"
+# CONFIG_NTFS_FS is not set
+
+#
+# Pseudo filesystems
+#
+CONFIG_PROC_FS=y
+CONFIG_PROC_KCORE=y
+CONFIG_PROC_VMCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_HUGETLBFS=y
+CONFIG_HUGETLB_PAGE=y
+CONFIG_DEBUG_FS=y
+
+#
+# Miscellaneous filesystems
+#
+# CONFIG_ADFS_FS is not set
+CONFIG_AFFS_FS=m
+CONFIG_ECRYPT_FS=m
+CONFIG_HFS_FS=m
+CONFIG_HFSPLUS_FS=m
+CONFIG_BEFS_FS=m
+# CONFIG_BEFS_DEBUG is not set
+CONFIG_BFS_FS=m
+CONFIG_EFS_FS=m
+CONFIG_JFFS2_FS=m
+CONFIG_JFFS2_FS_DEBUG=0
+# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set
+CONFIG_JFFS2_FS_WRITEBUFFER=y
+# CONFIG_JFFS2_FS_WBUF_VERIFY is not set
+CONFIG_JFFS2_SUMMARY=y
+CONFIG_JFFS2_FS_XATTR=y
+CONFIG_JFFS2_FS_POSIX_ACL=y
+CONFIG_JFFS2_FS_SECURITY=y
+CONFIG_CRAMFS=m
+CONFIG_SQUASHFS=m
+# CONFIG_SQUASHFS_EMBEDDED is not set
+CONFIG_VXFS_FS=m
+# CONFIG_HPFS_FS is not set
+CONFIG_QNX4FS_FS=m
+CONFIG_SYSV_FS=m
+CONFIG_UFS_FS=m
+# CONFIG_UFS_FS_WRITE is not set
+# CONFIG_UFS_DEBUG is not set
+CONFIG_9P_FS=m
+CONFIG_9P_FSCACHE=y
+CONFIG_FUSE_FS=m
+CONFIG_OMFS_FS=m
+CONFIG_CUSE=m
+
+#
+# Network File Systems
+#
+CONFIG_NETWORK_FILESYSTEMS=y
+CONFIG_NFS_FS=m
+CONFIG_NFS_V3=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+# CONFIG_NFS_V4_1 is not set
+CONFIG_NFSD=m
+CONFIG_NFSD_V3=y
+CONFIG_NFSD_V3_ACL=y
+CONFIG_NFSD_V4=y
+CONFIG_NFS_FSCACHE=y
+CONFIG_LOCKD=m
+CONFIG_LOCKD_V4=y
+CONFIG_EXPORTFS=m
+CONFIG_SUNRPC=m
+CONFIG_SUNRPC_GSS=m
+CONFIG_SUNRPC_XPRT_RDMA=m
+CONFIG_RPCSEC_GSS_KRB5=m
+CONFIG_RPCSEC_GSS_SPKM3=m
+# CONFIG_SMB_FS is not set
+# CONFIG_SMB_NLS_DEFAULT is not set
+CONFIG_CIFS=m
+CONFIG_CIFS_STATS=y
+# CONFIG_CIFS_STATS2 is not set
+CONFIG_CIFS_EXPERIMENTAL=y
+CONFIG_CIFS_UPCALL=y
+CONFIG_CIFS_XATTR=y
+CONFIG_CIFS_POSIX=y
+CONFIG_CIFS_WEAK_PW_HASH=y
+# CONFIG_CIFS_DEBUG2 is not set
+CONFIG_CIFS_DFS_UPCALL=y
+CONFIG_NCP_FS=m
+CONFIG_NCPFS_PACKET_SIGNING=y
+CONFIG_NCPFS_IOCTL_LOCKING=y
+CONFIG_NCPFS_STRONG=y
+CONFIG_NCPFS_NFS_NS=y
+CONFIG_NCPFS_OS2_NS=y
+CONFIG_NCPFS_SMALLDOS=y
+CONFIG_NCPFS_NLS=y
+CONFIG_NCPFS_EXTRAS=y
+CONFIG_CODA_FS=m
+# CONFIG_AFS_FS is not set
+# CONFIG_AF_RXRPC is not set
+
+CONFIG_OCFS2_FS=m
+# CONFIG_OCFS2_DEBUG_FS is not set
+# CONFIG_OCFS2_DEBUG_MASKLOG is not set
+CONFIG_OCFS2_FS_O2CB=m
+CONFIG_OCFS2_FS_USERSPACE_CLUSTER=m
+# CONFIG_OCFS2_FS_STATS is not set
+CONFIG_OCFS2_FS_POSIX_ACL=y
+
+CONFIG_BTRFS_FS=m
+CONFIG_BTRFS_FS_POSIX_ACL=y
+
+CONFIG_CONFIGFS_FS=m
+
+CONFIG_DLM=m
+CONFIG_DLM_DEBUG=y
+CONFIG_GFS2_FS=m
+CONFIG_GFS2_FS_LOCKING_DLM=y
+
+CONFIG_UBIFS_FS=m
+CONFIG_UBIFS_FS_XATTR=y
+# CONFIG_UBIFS_FS_ADVANCED_COMPR is not set
+# CONFIG_UBIFS_FS_DEBUG is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_ACORN_PARTITION is not set
+CONFIG_AMIGA_PARTITION=y
+# CONFIG_ATARI_PARTITION is not set
+CONFIG_BSD_DISKLABEL=y
+CONFIG_EFI_PARTITION=y
+CONFIG_KARMA_PARTITION=y
+# CONFIG_LDM_PARTITION is not set
+CONFIG_MAC_PARTITION=y
+CONFIG_MSDOS_PARTITION=y
+CONFIG_MINIX_SUBPARTITION=y
+CONFIG_OSF_PARTITION=y
+CONFIG_SGI_PARTITION=y
+CONFIG_SOLARIS_X86_PARTITION=y
+CONFIG_SUN_PARTITION=y
+# CONFIG_SYSV68_PARTITION is not set
+CONFIG_UNIXWARE_DISKLABEL=y
+# CONFIG_ULTRIX_PARTITION is not set
+
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS_DEFAULT="utf8"
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_737=m
+CONFIG_NLS_CODEPAGE_775=m
+CONFIG_NLS_CODEPAGE_850=m
+CONFIG_NLS_CODEPAGE_852=m
+CONFIG_NLS_CODEPAGE_855=m
+CONFIG_NLS_CODEPAGE_857=m
+CONFIG_NLS_CODEPAGE_860=m
+CONFIG_NLS_CODEPAGE_861=m
+CONFIG_NLS_CODEPAGE_862=m
+CONFIG_NLS_CODEPAGE_863=m
+CONFIG_NLS_CODEPAGE_864=m
+CONFIG_NLS_CODEPAGE_865=m
+CONFIG_NLS_CODEPAGE_866=m
+CONFIG_NLS_CODEPAGE_869=m
+CONFIG_NLS_CODEPAGE_936=m
+CONFIG_NLS_CODEPAGE_950=m
+CONFIG_NLS_CODEPAGE_932=m
+CONFIG_NLS_CODEPAGE_949=m
+CONFIG_NLS_CODEPAGE_874=m
+CONFIG_NLS_ISO8859_8=m
+CONFIG_NLS_CODEPAGE_1250=m
+CONFIG_NLS_CODEPAGE_1251=m
+CONFIG_NLS_ISO8859_1=m
+CONFIG_NLS_ISO8859_2=m
+CONFIG_NLS_ISO8859_3=m
+CONFIG_NLS_ISO8859_4=m
+CONFIG_NLS_ISO8859_5=m
+CONFIG_NLS_ISO8859_6=m
+CONFIG_NLS_ISO8859_7=m
+CONFIG_NLS_ISO8859_9=m
+CONFIG_NLS_ISO8859_13=m
+CONFIG_NLS_ISO8859_14=m
+CONFIG_NLS_ISO8859_15=m
+CONFIG_NLS_KOI8_R=m
+CONFIG_NLS_KOI8_U=m
+CONFIG_NLS_UTF8=m
+CONFIG_NLS_ASCII=y
+
+#
+# Profiling support
+#
+CONFIG_PROFILING=y
+CONFIG_OPROFILE=m
+CONFIG_OPROFILE_EVENT_MULTIPLEX=y
+
+#
+# Kernel hacking
+#
+CONFIG_DEBUG_KERNEL=y
+CONFIG_FRAME_WARN=1024
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_INFO=y
+CONFIG_FRAME_POINTER=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+# CONFIG_DEBUG_DRIVER is not set
+CONFIG_HEADERS_CHECK=y
+# CONFIG_RCU_TORTURE_TEST is not set
+# CONFIG_RCU_TRACE is not set
+# CONFIG_LKDTM is not set
+
+# CONFIG_RT_MUTEX_TESTER is not set
+# CONFIG_DEBUG_LOCKDEP is not set
+# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
+
+CONFIG_KGDB=y
+CONFIG_KGDB_SERIAL_CONSOLE=y
+CONFIG_KGDB_TESTS=y
+# CONFIG_KGDB_TESTS_ON_BOOT is not set
+
+#
+# Security options
+#
+CONFIG_SECURITY=y
+CONFIG_SECURITY_NETWORK=y
+CONFIG_SECURITY_NETWORK_XFRM=y
+# CONFIG_SECURITY_PATH is not set
+CONFIG_SECURITY_FILE_CAPABILITIES=y
+# CONFIG_SECURITY_ROOTPLUG is not set
+CONFIG_SECURITY_SELINUX=y
+CONFIG_SECURITY_SELINUX_BOOTPARAM=y
+CONFIG_SECURITY_SELINUX_DISABLE=y
+CONFIG_SECURITY_SELINUX_DEVELOP=y
+CONFIG_SECURITY_SELINUX_BOOTPARAM_VALUE=1
+CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=1
+CONFIG_SECURITY_SELINUX_AVC_STATS=y
+# CONFIG_SECURITY_SMACK is not set
+# CONFIG_SECURITY_TOMOYO is not set
+CONFIG_AUDIT=y
+CONFIG_AUDITSYSCALL=y
+
+#
+# Cryptographic options
+#
+CONFIG_CRYPTO=y
+CONFIG_CRYPTO_FIPS=y
+CONFIG_CRYPTO_HW=y
+CONFIG_CRYPTO_BLKCIPHER=y
+CONFIG_CRYPTO_MANAGER=m
+# CONFIG_CRYPTO_CRYPTD is not set
+CONFIG_CRYPTO_AES=m
+CONFIG_CRYPTO_ARC4=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_AUTHENC=m
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_CAMELLIA=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_CBC=m
+CONFIG_CRYPTO_CCM=m
+CONFIG_CRYPTO_CRC32C=y
+CONFIG_CRYPTO_CTR=m
+CONFIG_CRYPTO_CTS=m
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_DES=m
+CONFIG_CRYPTO_ECB=m
+CONFIG_CRYPTO_FCRYPT=m
+CONFIG_CRYPTO_GCM=m
+CONFIG_CRYPTO_GF128MUL=m
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_LRW=m
+CONFIG_CRYPTO_LZO=m
+CONFIG_CRYPTO_MD4=m
+CONFIG_CRYPTO_MD5=m
+CONFIG_CRYPTO_MICHAEL_MIC=m
+CONFIG_CRYPTO_NULL=m
+CONFIG_CRYPTO_PCBC=m
+CONFIG_CRYPTO_RMD128=m
+CONFIG_CRYPTO_RMD160=m
+CONFIG_CRYPTO_RMD256=m
+CONFIG_CRYPTO_RMD320=m
+CONFIG_CRYPTO_SALSA20=m
+CONFIG_CRYPTO_SALSA20_586=m
+CONFIG_CRYPTO_SEED=m
+CONFIG_CRYPTO_SEQIV=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_SHA1=y
+CONFIG_CRYPTO_SHA256=m
+CONFIG_CRYPTO_SHA512=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_TGR192=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_WP512=m
+CONFIG_CRYPTO_XCBC=m
+CONFIG_CRYPTO_VMAC=m
+CONFIG_CRYPTO_XTS=m
+CONFIG_CRYPTO_TEST=m
+CONFIG_LIBCRC32C=m
+CONFIG_CRYPTO_CRC32C_INTEL=m
+CONFIG_CRYPTO_GHASH=m
+CONFIG_CRYPTO_ANSI_CPRNG=m
+CONFIG_CRYPTO_DEV_HIFN_795X=m
+CONFIG_CRYPTO_DEV_HIFN_795X_RNG=y
+
+# Random number generation
+
+#
+# Library routines
+#
+CONFIG_CRC16=y
+CONFIG_CRC32=m
+CONFIG_CRC_CCITT=m
+CONFIG_CRC_ITU_T=m
+CONFIG_CRC_T10DIF=m
+
+CONFIG_CRYPTO_ZLIB=m
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=m
+
+CONFIG_INITRAMFS_SOURCE=""
+CONFIG_KEYS=y
+CONFIG_KEYS_DEBUG_PROC_KEYS=y
+CONFIG_CDROM_PKTCDVD=m
+CONFIG_CDROM_PKTCDVD_BUFFERS=8
+# CONFIG_CDROM_PKTCDVD_WCACHE is not set
+
+CONFIG_ATA_OVER_ETH=m
+CONFIG_BACKLIGHT_LCD_SUPPORT=y
+CONFIG_BACKLIGHT_CLASS_DEVICE=m
+# CONFIG_BACKLIGHT_GENERIC is not set
+CONFIG_BACKLIGHT_PROGEAR=m
+CONFIG_FB_NVIDIA_BACKLIGHT=y
+CONFIG_FB_RIVA_BACKLIGHT=y
+CONFIG_FB_RADEON_BACKLIGHT=y
+CONFIG_FB_ATY128_BACKLIGHT=y
+CONFIG_FB_ATY_BACKLIGHT=y
+# CONFIG_BACKLIGHT_SAHARA is not set
+CONFIG_BACKLIGHT_WM831X=m
+
+CONFIG_LCD_CLASS_DEVICE=m
+CONFIG_LCD_PLATFORM=m
+
+CONFIG_SCHEDSTATS=y
+CONFIG_SCHED_DEBUG=y
+CONFIG_FAIR_GROUP_SCHED=y
+CONFIG_SCHED_OMIT_FRAME_POINTER=y
+CONFIG_GROUP_SCHED=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_CGROUP_SCHED=y
+CONFIG_CGROUP_MEM_RES_CTLR=y
+CONFIG_CGROUP_MEM_RES_CTLR_SWAP=y
+
+CONFIG_CPUSETS=y
+CONFIG_CGROUPS=y
+# CONFIG_CGROUP_DEBUG is not set
+CONFIG_CGROUP_NS=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_PROC_PID_CPUSET=y
+
+# CONFIG_SYSFS_DEPRECATED is not set
+# CONFIG_SYSFS_DEPRECATED_V2 is not set
+
+CONFIG_RELAY=y
+# CONFIG_PRINTK_TIME is not set
+
+CONFIG_ENABLE_MUST_CHECK=y
+# CONFIG_ENABLE_WARN_DEPRECATED is not set
+
+CONFIG_KEXEC=y
+
+CONFIG_HWMON=y
+# CONFIG_HWMON_DEBUG_CHIP is not set
+CONFIG_INOTIFY=y
+CONFIG_INOTIFY_USER=y
+
+CONFIG_CONNECTOR=y
+CONFIG_PROC_EVENTS=y
+
+CONFIG_IBMASR=m
+
+CONFIG_PM_DEBUG=y
+CONFIG_PM_TRACE=y
+# CONFIG_PM_VERBOSE is not set
+# CONFIG_PM_TEST_SUSPEND is not set
+CONFIG_PM_RUNTIME=y
+
+## BEGIN ISA Junk.
+
+CONFIG_I82365=m
+# CONFIG_TCIC is not set
+# CONFIG_PCMCIA_PROBE is not set
+# CONFIG_LTPC is not set
+# CONFIG_COPS is not set
+
+CONFIG_SCSI_AHA152X=m
+CONFIG_SCSI_AHA1542=m
+# CONFIG_SCSI_IN2000 is not set
+CONFIG_SCSI_ARCMSR=m
+CONFIG_SCSI_ARCMSR_AER=y
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_GENERIC_NCR5380_MMIO is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_ULTRASTOR is not set
+
+# CONFIG_EL1 is not set
+# CONFIG_EL2 is not set
+# CONFIG_ELPLUS is not set
+# CONFIG_EL16 is not set
+CONFIG_EL3=m
+# CONFIG_3C515 is not set
+# CONFIG_LANCE is not set
+CONFIG_NET_VENDOR_SMC=y
+# CONFIG_WD80x3 is not set
+CONFIG_ULTRA=m
+# CONFIG_SMC9194 is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_NI52 is not set
+# CONFIG_NI65 is not set
+# CONFIG_AT1700 is not set
+# CONFIG_DEPCA is not set
+CONFIG_NET_ISA=y
+CONFIG_NE2000=m
+# CONFIG_E2100 is not set
+CONFIG_EWRK3=m
+# CONFIG_EEXPRESS is not set
+# CONFIG_EEXPRESS_PRO is not set
+# CONFIG_HPLAN_PLUS is not set
+# CONFIG_HPLAN is not set
+# CONFIG_LP486E is not set
+# CONFIG_ETH16I is not set
+# CONFIG_ZNET is not set
+# CONFIG_SEEQ8005 is not set
+# CONFIG_AC3200 is not set
+# CONFIG_APRICOT is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_IBMTR is not set
+# CONFIG_SKISA is not set
+# CONFIG_PROTEON is not set
+# CONFIG_SMCTR is not set
+# CONFIG_WAVELAN is not set
+# CONFIG_HISAX_16_0 is not set
+# CONFIG_HISAX_AVM_A1 is not set
+# CONFIG_HISAX_IX1MICROR2 is not set
+# CONFIG_HISAX_ASUSCOM is not set
+# CONFIG_HISAX_TELEINT is not set
+# CONFIG_HISAX_HFCS is not set
+# CONFIG_HISAX_SPORTSTER is not set
+# CONFIG_HISAX_MIC is not set
+# CONFIG_HISAX_ISURF is not set
+# CONFIG_HISAX_HSTSAPHIR is not set
+# CONFIG_ISDN_DRV_ICN is not set
+# CONFIG_ISDN_DRV_PCBIT is not set
+# CONFIG_ISDN_DRV_SC is not set
+# CONFIG_ISDN_DRV_ACT2000 is not set
+# CONFIG_ISDN_DRV_AVMB1_B1ISA is not set
+# CONFIG_ISDN_DRV_AVMB1_T1ISA is not set
+
+# CONFIG_MOUSE_INPORT is not set
+# CONFIG_MOUSE_ATIXL is not set
+# CONFIG_MOUSE_LOGIBM is not set
+# CONFIG_MOUSE_PC110PAD is not set
+
+# CONFIG_SERIAL_8250_FOURPORT is not set
+# CONFIG_SERIAL_8250_ACCENT is not set
+# CONFIG_SERIAL_8250_BOCA is not set
+# CONFIG_SERIAL_8250_HUB6 is not set
+# CONFIG_SERIAL_8250_EXAR_ST16C554 is not set
+
+# CONFIG_PCWATCHDOG is not set
+# CONFIG_WDT is not set
+
+# CONFIG_VIDEO_PMS is not set
+CONFIG_RADIO_ADAPTERS=y
+# CONFIG_RADIO_CADET is not set
+# CONFIG_RADIO_RTRACK is not set
+# CONFIG_RADIO_RTRACK2 is not set
+# CONFIG_RADIO_AZTECH is not set
+# CONFIG_RADIO_GEMTEK is not set
+# CONFIG_RADIO_SF16FMI is not set
+# CONFIG_RADIO_SF16FMR2 is not set
+# CONFIG_RADIO_TERRATEC is not set
+# CONFIG_RADIO_TRUST is not set
+# CONFIG_RADIO_TEA5764 is not set
+# CONFIG_RADIO_TYPHOON is not set
+# CONFIG_RADIO_ZOLTRIX is not set
+
+# CONFIG_SND_OPL4_LIB is not set
+# CONFIG_SND_AD1816A is not set
+# CONFIG_SND_AD1848 is not set
+# CONFIG_SND_CS4231 is not set
+CONFIG_SND_CS4236=m
+# CONFIG_SND_ES968 is not set
+# CONFIG_SND_ES1688 is not set
+# CONFIG_SND_ES18XX is not set
+# CONFIG_SND_GUSCLASSIC is not set
+# CONFIG_SND_GUSEXTREME is not set
+# CONFIG_SND_GUSMAX is not set
+# CONFIG_SND_INTERWAVE is not set
+# CONFIG_SND_INTERWAVE_STB is not set
+# CONFIG_SND_OPTI92X_AD1848 is not set
+# CONFIG_SND_OPTI92X_CS4231 is not set
+# CONFIG_SND_OPTI93X is not set
+# CONFIG_SND_SB8 is not set
+CONFIG_SND_SB16=m
+CONFIG_SND_SBAWE=m
+# CONFIG_SND_SB16_CSP is not set
+# CONFIG_SND_ALS100 is not set
+# CONFIG_SND_AZT2320 is not set
+# CONFIG_SND_CMI8330 is not set
+# CONFIG_SND_DT019X is not set
+CONFIG_SND_OPL3SA2=m
+# CONFIG_SND_SGALAXY is not set
+# CONFIG_SND_SSCAPE is not set
+CONFIG_SND_DARLA20=m
+CONFIG_SND_GINA20=m
+CONFIG_SND_LAYLA20=m
+CONFIG_SND_DARLA24=m
+CONFIG_SND_GINA24=m
+CONFIG_SND_LAYLA24=m
+CONFIG_SND_MONA=m
+CONFIG_SND_MIA=m
+CONFIG_SND_ECHO3G=m
+CONFIG_SND_INDIGO=m
+CONFIG_SND_INDIGOIO=m
+CONFIG_SND_INDIGODJ=m
+CONFIG_SND_INDIGOIOX=m
+CONFIG_SND_INDIGODJX=m
+# CONFIG_SND_SOC is not set
+
+## END of ISA options.
+
+
+CONFIG_MIGRATION=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+# CONFIG_LEDS_AMS_DELTA is not set
+# CONFIG_LEDS_LOCOMO is not set
+# CONFIG_LEDS_NET48XX is not set
+# CONFIG_LEDS_PCA9532 is not set
+# CONFIG_LEDS_PCA955X is not set
+# CONFIG_LEDS_BD2802 is not set
+# CONFIG_LEDS_S3C24XX is not set
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=m
+CONFIG_LEDS_TRIGGER_IDE_DISK=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=m
+CONFIG_LEDS_TRIGGER_BACKLIGHT=m
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=m
+CONFIG_LEDS_ALIX2=m
+CONFIG_LEDS_WM8350=m
+CONFIG_LEDS_LP3944=m
+CONFIG_LEDS_WM831X_STATUS=m
+
+CONFIG_DMADEVICES=y
+CONFIG_DMA_ENGINE=y
+CONFIG_NET_DMA=y
+# CONFIG_DMATEST is not set
+CONFIG_ASYNC_TX_DMA=y
+
+CONFIG_UNUSED_SYMBOLS=y
+
+CONFIG_UTRACE=y
+
+CONFIG_FTRACE=y
+# CONFIG_IRQSOFF_TRACER is not set
+CONFIG_SCHED_TRACER=y
+CONFIG_CONTEXT_SWITCH_TRACER=y
+CONFIG_WORKQUEUE_TRACER=y
+CONFIG_FTRACE_SYSCALLS=y
+CONFIG_KMEMTRACE=y
+CONFIG_DYNAMIC_FTRACE=y
+CONFIG_FTRACE_MCOUNT_RECORD=y
+# CONFIG_FTRACE_STARTUP_TEST is not set
+# CONFIG_TRACE_BRANCH_PROFILING is not set
+CONFIG_FUNCTION_PROFILER=y
+CONFIG_RING_BUFFER_BENCHMARK=m
+
+CONFIG_KPROBES=y
+
+# CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX is not set
+
+CONFIG_HZ_1000=y
+
+CONFIG_TIMER_STATS=y
+
+# Auxillary displays
+CONFIG_KS0108=m
+CONFIG_KS0108_PORT=0x378
+CONFIG_KS0108_DELAY=2
+CONFIG_CFAG12864B=y
+CONFIG_CFAG12864B_RATE=20
+
+# CONFIG_PHANTOM is not set
+
+CONFIG_POWER_SUPPLY=m
+# CONFIG_POWER_SUPPLY_DEBUG is not set
+CONFIG_APM_POWER=m
+CONFIG_WM831X_POWER=m
+# CONFIG_BATTERY_DS2760 is not set
+# CONFIG_BATTERY_DS2782 is not set
+CONFIG_BATTERY_PMU=m
+CONFIG_BATTERY_BQ27x00=m
+CONFIG_BATTERY_MAX17040=m
+# CONFIG_PDA_POWER is not set
+
+CONFIG_AUXDISPLAY=y
+
+CONFIG_UIO=m
+CONFIG_UIO_CIF=m
+CONFIG_UIO_SMX=m
+CONFIG_UIO_PDRV=m
+CONFIG_UIO_PDRV_GENIRQ=m
+CONFIG_UIO_AEC=m
+CONFIG_UIO_SERCOS3=m
+CONFIG_UIO_PCI_GENERIC=m
+
+# CONFIG_CRC7 is not set
+
+
+# LIRC
+CONFIG_INPUT_LIRC=m
+CONFIG_LIRC_BT829=m
+CONFIG_LIRC_ENE0100=m
+CONFIG_LIRC_I2C=m
+CONFIG_LIRC_IGORPLUGUSB=m
+CONFIG_LIRC_IMON=m
+CONFIG_LIRC_IT87=m
+CONFIG_LIRC_ITE8709=m
+CONFIG_LIRC_MCEUSB=m
+CONFIG_LIRC_ZILOG=m
+CONFIG_LIRC_PARALLEL=m
+CONFIG_LIRC_SERIAL=m
+CONFIG_LIRC_SERIAL_TRANSMITTER=y
+CONFIG_LIRC_SASEM=m
+CONFIG_LIRC_SIR=m
+CONFIG_LIRC_STREAMZAP=m
+CONFIG_LIRC_TTUSBIR=m
+
+# CONFIG_SAMPLES is not set
+
+# CONFIG_DEVKMEM is not set
+
+CONFIG_PM_TRACE_RTC=y
+CONFIG_R6040=m
+
+CONFIG_BNX2X=m
+CONFIG_NOZOMI=m
+# CONFIG_TPS65010 is not set
+# CONFIG_DEBUG_SECTION_MISMATCH is not set
+# CONFIG_KPROBES_SANITY_TEST is not set
+# CONFIG_BACKTRACE_SELF_TEST is not set
+CONFIG_LATENCYTOP=y
+CONFIG_RESOURCE_COUNTERS=y
+# CONFIG_COMPAT_BRK is not set
+
+
+#FIXME: x86 generic?
+CONFIG_LEDS_CLEVO_MAIL=m
+CONFIG_I8K=m
+CONFIG_EDAC_I3000=m
+CONFIG_EDAC_X38=m
+CONFIG_INPUT_APANEL=m
+
+# CONFIG_INTEL_MENLOW is not set
+CONFIG_ENCLOSURE_SERVICES=m
+CONFIG_ISL29003=m
+CONFIG_IPWIRELESS=m
+CONFIG_RTC_DRV_DS1511=m
+
+# CONFIG_BLK_DEV_XIP is not set
+CONFIG_MEMSTICK=m
+# CONFIG_MEMSTICK_DEBUG is not set
+# CONFIG_MEMSTICK_UNSAFE_RESUME is not set
+CONFIG_MSPRO_BLOCK=m
+CONFIG_MEMSTICK_TIFM_MS=m
+CONFIG_MEMSTICK_JMICRON_38X=m
+
+CONFIG_ACCESSIBILITY=y
+CONFIG_A11Y_BRAILLE_CONSOLE=y
+
+# CONFIG_HTC_PASIC3 is not set
+
+# MT9V022_PCA9536_SWITCH is not set
+
+CONFIG_THERMAL_HWMON=y
+
+CONFIG_OPTIMIZE_INLINING=y
+
+# FIXME: This should be x86/ia64 only
+# CONFIG_HP_ILO is not set
+
+# CONFIG_GPIOLIB is not set
+
+
+CONFIG_NETFILTER_TPROXY=m
+CONFIG_NETFILTER_XT_TARGET_TPROXY=m
+CONFIG_NETFILTER_XT_MATCH_RECENT=m
+# CONFIG_NETFILTER_XT_MATCH_RECENT_PROC_COMPAT is not set
+CONFIG_NETFILTER_XT_MATCH_SOCKET=m
+
+# CONFIG_IP_VS_IPV6 is not set
+
+CONFIG_NET_DSA=y
+CONFIG_NET_DSA_MV88E6060=y
+CONFIG_NET_DSA_MV88E6131=y
+CONFIG_NET_DSA_MV88E6123_61_65=y
+
+CONFIG_NET_SCH_MULTIQ=m
+CONFIG_NET_ACT_SKBEDIT=m
+
+CONFIG_PHONET=m
+
+CONFIG_ICS932S401=m
+# CONFIG_C2PORT is not set
+CONFIG_W1_SLAVE_BQ27000=m
+
+
+CONFIG_IT87_WDT=m
+CONFIG_W83697UG_WDT=m
+
+CONFIG_REGULATOR=y
+# CONFIG_REGULATOR_DEBUG is not set
+CONFIG_REGULATOR_FIXED_VOLTAGE=m
+# CONFIG_REGULATOR_VIRTUAL_CONSUMER is not set
+CONFIG_REGULATOR_BQ24022=m
+CONFIG_REGULATOR_WM8350=m
+CONFIG_REGULATOR_WM8400=m
+CONFIG_REGULATOR_DA903X=m
+CONFIG_REGULATOR_USERSPACE_CONSUMER=m
+CONFIG_REGULATOR_MAX1586=m
+CONFIG_REGULATOR_LP3971=m
+CONFIG_REGULATOR_AB3100=m
+CONFIG_REGULATOR_TPS65023=m
+CONFIG_REGULATOR_TPS6507X=m
+CONFIG_REGULATOR_WM831X=m
+
+CONFIG_WM8350_POWER=m
+
+# CONFIG_VIDEO_FIXED_MINOR_RANGES is not set
+
+CONFIG_USB_WUSB=m
+CONFIG_USB_WUSB_CBAF=m
+# CONFIG_USB_WUSB_CBAF_DEBUG is not set
+CONFIG_USB_WHCI_HCD=m
+CONFIG_USB_HWA_HCD=m
+
+CONFIG_UWB=m
+CONFIG_UWB_HWA=m
+CONFIG_UWB_WHCI=m
+CONFIG_UWB_WLP=m
+CONFIG_UWB_I1480U=m
+CONFIG_UWB_I1480U_WLP=m
+
+CONFIG_STAGING=y
+# CONFIG_STAGING_EXCLUDE_BUILD is not set
+# CONFIG_ET131X is not set
+# CONFIG_SLICOSS is not set
+# CONFIG_VIDEO_GO7007 is not set
+# CONFIG_USB_IP_COMMON is not set
+# CONFIG_W35UND is not set
+# CONFIG_PRISM2_USB is not set
+# CONFIG_ECHO is not set
+CONFIG_USB_ATMEL=m
+# CONFIG_POCH is not set
+# CONFIG_OTUS is not set
+# CONFIG_RT2860 is not set
+# CONFIG_RT2870 is not set
+# CONFIG_COMEDI is not set
+# CONFIG_ASUS_OLED is not set
+# CONFIG_PANEL is not set
+# CONFIG_ALTERA_PCIE_CHDMA is not set
+# CONFIG_RTL8187SE is not set
+# CONFIG_INPUT_MIMIO is not set
+# CONFIG_TRANZPORT is not set
+# CONFIG_POHMELFS is not set
+# CONFIG_B3DFG is not set
+# CONFIG_DST is not set
+# CONFIG_IDE_PHISON is not set
+# CONFIG_PLAN9AUTH is not set
+# CONFIG_LINE6_USB is not set
+# CONFIG_RTL8192SU is not set
+# CONFIG_IIO is not set
+# CONFIG_VME_BUS is not set
+# CONFIG_RAR_REGISTER is not set
+# CONFIG_VT6656 is not set
+# CONFIG_USB_SERIAL_QUATECH_USB2 is not set
+# CONFIG_RTL8192E is not set
+# CONFIG_INPUT_GPIO is not set
+# CONFIG_VIDEO_CX25821 is not set
+# CONFIG_RT3090 is not set
+# CONFIG_HYPERV is not set
+
+#
+# Android
+#
+# CONFIG_ANDROID is not set
+# CONFIG_ANDROID_BINDER_IPC is not set
+# CONFIG_ANDROID_LOGGER is not set
+# CONFIG_ANDROID_RAM_CONSOLE is not set
+# CONFIG_ANDROID_LOW_MEMORY_KILLER is not set
+# CONFIG_ANDROID_TIMED_GPIO is not set
+
+# CONFIG_DEBUG_VIRTUAL is not set
+# CONFIG_RCU_CPU_STALL_DETECTOR is not set
+# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
+CONFIG_FUNCTION_TRACER=y
+# CONFIG_FUNCTION_GRAPH_TRACER is not set
+# CONFIG_BOOT_TRACER is not set
+CONFIG_STACK_TRACER=y
+CONFIG_EARLY_PRINTK_DBGP=y
+
+CONFIG_SECURITYFS=y
+
+CONFIG_SCSI_CXGB3_ISCSI=m
+CONFIG_LIBFC=m
+CONFIG_LIBFCOE=m
+CONFIG_FCOE=m
+CONFIG_FCOE_FNIC=m
+# CONFIG_SCSI_LPFC_DEBUG_FS is not set
+
+CONFIG_NOP_USB_XCEIV=m
+
+CONFIG_IMA=y
+CONFIG_IMA_MEASURE_PCR_IDX=10
+CONFIG_IMA_AUDIT=y
+CONFIG_IMA_LSM_RULES=y
+
+CONFIG_LSM_MMAP_MIN_ADDR=65536
+
+# CONFIG_PAGE_POISONING is not set
+
+CONFIG_SLOW_WORK=y
+CONFIG_SLOW_WORK_DEBUG=y
+
+# CONFIG_CRASH_DUMP is not set
+# CONFIG_CRASH is not set
+
+CONFIG_STRIP_ASM_SYMS=y
+
+# CONFIG_RCU_FANOUT_EXACT is not set
+
+CONFIG_KSM=y
+CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+
+CONFIG_FSNOTIFY=y
+
+CONFIG_IEEE802154=m
+CONFIG_IEEE802154_DRIVERS=m
+CONFIG_IEEE802154_FAKEHARD=m
+
+# CONFIG_GCOV_KERNEL is not set
+
+CONFIG_PPS=m
+# CONFIG_PPS_DEBUG is not set
+
+# CONFIG_USB_SERIAL_QUATECH2 is not set
+# CONFIG_VT6655 is not set
+# CONFIG_FB_UDL is not set
+
+# DEBUG options that don't get enabled/disabled with 'make debug/release'
+#
+# Kmemleak still produces a lot of false positives.
+# CONFIG_DEBUG_KMEMLEAK is not set
+#
+# This generates a huge amount of dmesg spew
+# CONFIG_DEBUG_KOBJECT is not set
+#
+#
+# These debug options are deliberatly left on (even in 'make release' kernels).
+# They aren't that much of a performance impact, and the value
+# from getting useful bug-reports makes it worth leaving them on.
+CONFIG_DYNAMIC_DEBUG=y
+CONFIG_DEBUG_HIGHMEM=y
+CONFIG_DEBUG_SPINLOCK_SLEEP=y
+CONFIG_BOOT_PRINTK_DELAY=y
+CONFIG_DEBUG_LIST=y
+CONFIG_DEBUG_SHIRQ=y
+CONFIG_DEBUG_DEVRES=y
+CONFIG_DEBUG_RODATA_TEST=y
+CONFIG_DEBUG_NX_TEST=m
+CONFIG_DEBUG_BOOT_PARAMS=y
+CONFIG_DETECT_SOFTLOCKUP=y
+# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
+CONFIG_DETECT_HUNG_TASK=y
+# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
+
+CONFIG_MEMORY_FAILURE=y
+CONFIG_HWPOISON_INJECT=m
diff --git a/config-i686-PAE b/config-i686-PAE
new file mode 100644
index 0000000..7f49f61
--- /dev/null
+++ b/config-i686-PAE
@@ -0,0 +1,37 @@
+# CONFIG_HIGHMEM4G is not set
+CONFIG_HIGHMEM64G=y
+
+CONFIG_XEN_DEV_EVTCHN=m
+CONFIG_XEN_SYS_HYPERVISOR=y
+
+CONFIG_LANGWELL_IPC=y
+# CONFIG_IMG_DOES_NOT_SUPPORT_MENLOW is not set
+CONFIG_PVR_RELEASE="release"
+CONFIG_PVR_SERVICES4=y
+CONFIG_PVR_XOPEN_SOURCE=600
+CONFIG_PVR2D_VALIDATE_INPUT_PARAMS=y
+CONFIG_PVR_DISPLAY_CONTROLLER="mrstlfb"
+CONFIG_PVR_SGX_CORE_REV=121
+CONFIG_PVR_SUPPORT_SVRINIT=y
+CONFIG_PVR_SUPPORT_SGX=y
+CONFIG_PVR_SUPPORT_PERCONTEXT_PB=y
+CONFIG_PVR_SUPPORT_LINUX_X86_WRITECOMBINE=y
+CONFIG_PVR_TRANSFER_QUEUE=y
+CONFIG_PVR_SUPPORT_DRI_DRM=y
+CONFIG_PVR_SYS_USING_INTERRUPTS=y
+CONFIG_PVR_SUPPORT_HW_RECOVERY=y
+CONFIG_PVR_SUPPORT_POWER_MANAGEMENT=y
+CONFIG_PVR_SECURE_HANDLES=y
+CONFIG_PVR_USE_PTHREADS=y
+CONFIG_PVR_SUPPORT_SGX_EVENT_OBJECT=y
+CONFIG_PVR_SUPPORT_SGX_HWPERF=y
+CONFIG_PVR_SUPPORT_SGX_LOW_LATENCY_SCHEDULING=y
+CONFIG_PVR_SUPPORT_LINUX_X86_PAT=y
+CONFIG_PVR_PROC_USE_SEQ_FILE=y
+CONFIG_PVR_SUPPORT_SGX535=y
+# CONFIG_PVR_SUPPORT_CACHEFLUSH_ON_ALLOC is not set
+# CONFIG_PVR_SUPPORT_MEMINFO_IDS is not set
+CONFIG_PVR_SUPPORT_CACHE_LINE_FLUSH=y
+CONFIG_PVR_SUPPORT_CPU_CACHED_BUFFERS=y
+CONFIG_PVR_DEBUG_MESA_OGL_TRACE=y
+
diff --git a/config-ia64-generic b/config-ia64-generic
new file mode 100644
index 0000000..51e1bc9
--- /dev/null
+++ b/config-ia64-generic
@@ -0,0 +1,206 @@
+#
+# Automatically generated make config: don't edit
+#
+
+#
+# Processor type and features
+#
+CONFIG_IA64=y
+CONFIG_64BIT=y
+# CONFIG_XEN is not set
+CONFIG_MMU=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_EFI=y
+# CONFIG_ITANIUM is not set
+CONFIG_MCKINLEY=y
+CONFIG_IA64_GENERIC=y
+# CONFIG_IA64_DIG is not set
+# CONFIG_IA64_HP_ZX1 is not set
+# CONFIG_IA64_SGI_SN2 is not set
+CONFIG_IA64_ESI=y
+CONFIG_IA64_HP_AML_NFW=y
+CONFIG_MSPEC=y
+# CONFIG_IA64_HP_SIM is not set
+# CONFIG_IA64_PAGE_SIZE_4KB is not set
+# CONFIG_IA64_PAGE_SIZE_8KB is not set
+CONFIG_IA64_PAGE_SIZE_16KB=y
+# CONFIG_IA64_PAGE_SIZE_64KB is not set
+CONFIG_IA64_L1_CACHE_SHIFT=7
+CONFIG_NUMA=y
+# CONFIG_VIRTUAL_MEM_MAP is not set
+CONFIG_SPARSEMEM_MANUAL=y
+CONFIG_SPARSEMEM=y
+CONFIG_MEMORY_HOTPLUG=y
+CONFIG_MEMORY_HOTREMOVE=y
+CONFIG_IA64_MCA_RECOVERY=m
+CONFIG_IA64_CYCLONE=y
+CONFIG_MMTIMER=y
+CONFIG_IOSAPIC=y
+CONFIG_FORCE_MAX_ZONEORDER=18
+CONFIG_NR_CPUS=1024
+# CONFIG_IA32_SUPPORT is not set
+# CONFIG_COMPAT is not set
+CONFIG_PERFMON=y
+CONFIG_IA64_PALINFO=y
+CONFIG_EFI_VARS=y
+CONFIG_SERIAL_8250_RUNTIME_UARTS=16
+CONFIG_EFI_PCDP=y
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+
+#
+# IDE chipset support/bugfixes
+#
+CONFIG_BLK_DEV_SGIIOC4=y
+
+#
+# Character devices
+#
+CONFIG_TCG_INFINEON=m
+
+#
+# Watchdog Cards
+#
+# CONFIG_HW_RANDOM is not set
+# CONFIG_GEN_RTC is not set
+CONFIG_EFI_RTC=y
+CONFIG_RTC_DRV_EFI=y
+
+
+#
+# AGP
+#
+CONFIG_AGP_I460=y
+CONFIG_AGP_HP_ZX1=y
+CONFIG_AGP_SGI_TIOCA=y
+
+#
+# HP Simulator drivers
+#
+# CONFIG_HP_SIMETH is not set
+# CONFIG_HP_SIMSERIAL is not set
+# CONFIG_HP_SIMSCSI is not set
+
+#
+# Kernel hacking
+#
+# CONFIG_IA64_PRINT_HAZARDS is not set
+# CONFIG_DISABLE_VHPT is not set
+# CONFIG_IA64_DEBUG_CMPXCHG is not set
+# CONFIG_IA64_DEBUG_IRQ is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# SGI
+#
+CONFIG_SGI_SNSC=y
+CONFIG_SGI_TIOCX=y
+CONFIG_SGI_MBCS=m
+CONFIG_SGI_IOC3=m
+CONFIG_SGI_IOC4=y
+CONFIG_SGI_XP=m
+CONFIG_SGI_GRU=m
+# CONFIG_SGI_GRU_DEBUG is not set
+CONFIG_SERIAL_SGI_L1_CONSOLE=y
+CONFIG_SERIAL_SGI_IOC3=m
+CONFIG_SERIAL_SGI_IOC4=m
+
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_SCSI_BUSLOGIC is not set
+
+#
+CONFIG_ACPI=y
+CONFIG_ACPI_AC=y
+# CONFIG_ACPI_ASUS is not set
+CONFIG_ACPI_PROCFS_POWER=y
+CONFIG_ACPI_SYSFS_POWER=y
+# CONFIG_ACPI_BATTERY is not set
+CONFIG_ACPI_BLACKLIST_YEAR=0
+CONFIG_ACPI_BUTTON=y
+# CONFIG_ACPI_DOCK is not set
+CONFIG_ACPI_FAN=y
+CONFIG_ACPI_HOTPLUG_MEMORY=y
+CONFIG_ACPI_NUMA=y
+CONFIG_ACPI_POWER=y
+CONFIG_ACPI_PROCESSOR=y
+CONFIG_ACPI_PROCFS=y
+CONFIG_ACPI_SLEEP=y
+CONFIG_ACPI_THERMAL=y
+# CONFIG_ACPI_TOSHIBA is not set
+CONFIG_ACPI_VIDEO=m
+# CONFIG_ACPI_PROC_EVENT is not set
+
+CONFIG_PM=y
+CONFIG_HOTPLUG_PCI=y
+# CONFIG_HPET is not set
+# CONFIG_HOTPLUG_PCI_CPCI is not set
+CONFIG_HOTPLUG_PCI_SHPC=m
+CONFIG_HOTPLUG_PCI_SGI=m
+CONFIG_PNPACPI=y
+
+CONFIG_SCHED_SMT=y
+
+CONFIG_ARCH_DISCONTIGMEM_ENABLE=y
+
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEBUG=y
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=m
+CONFIG_CPU_FREQ_GOV_ONDEMAND=m
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
+CONFIG_CPU_FREQ_STAT=m
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+
+CONFIG_IA64_ACPI_CPUFREQ=m
+
+# CONFIG_PERMIT_BSP_REMOVE is not set
+# CONFIG_FORCE_CPEI_RETARGET is not set
+
+CONFIG_NODES_SHIFT=10
+
+
+CONFIG_HW_RANDOM_INTEL=m
+
+CONFIG_CRASH_DUMP=y
+CONFIG_PROC_VMCORE=y
+
+# drivers/media/video/usbvision/usbvision-i2c.c:64:39: error: macro "outb" passed 4 arguments, but takes just 2
+# CONFIG_VIDEO_USBVISION is not set
+
+# CONFIG_IA64_MC_ERR_INJECT is not set
+
+CONFIG_DMIID=y
+
+CONFIG_SENSORS_I5K_AMB=m
+
+CONFIG_SPARSEMEM_VMEMMAP=y
+
+CONFIG_FRAME_WARN=2048
+
+CONFIG_VIRT_CPU_ACCOUNTING=y
+CONFIG_VIRTUALIZATION=y
+CONFIG_KVM=m
+CONFIG_KVM_INTEL=m
+
+CONFIG_HP_ILO=m
+
+CONFIG_PARAVIRT_GUEST=y
+CONFIG_PARAVIRT=y
+
+CONFIG_DMAR_DEFAULT_ON=y
+
+CONFIG_RCU_FANOUT=64
+
+CONFIG_ACPI_POWER_METER=m
+CONFIG_I2C_SCMI=m
diff --git a/config-nodebug b/config-nodebug
new file mode 100644
index 0000000..26f0fa0
--- /dev/null
+++ b/config-nodebug
@@ -0,0 +1,64 @@
+CONFIG_SND_VERBOSE_PRINTK=y
+CONFIG_SND_DEBUG=y
+CONFIG_SND_PCM_XRUN_DEBUG=y
+
+# CONFIG_DEBUG_MUTEXES is not set
+# CONFIG_DEBUG_RT_MUTEXES is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_DEBUG_VM is not set
+# CONFIG_DEBUG_SPINLOCK is not set
+
+# CONFIG_FAULT_INJECTION is not set
+# CONFIG_FAILSLAB is not set
+# CONFIG_FAIL_PAGE_ALLOC is not set
+# CONFIG_FAIL_MAKE_REQUEST is not set
+# CONFIG_FAULT_INJECTION_DEBUG_FS is not set
+# CONFIG_FAULT_INJECTION_STACKTRACE_FILTER is not set
+# CONFIG_FAIL_IO_TIMEOUT is not set
+
+# CONFIG_SLUB_DEBUG_ON is not set
+
+# CONFIG_LOCK_STAT is not set
+
+# CONFIG_DEBUG_STACK_USAGE is not set
+
+# CONFIG_ACPI_DEBUG is not set
+# CONFIG_ACPI_DEBUG_FUNC_TRACE is not set
+
+# CONFIG_DEBUG_SG is not set
+
+# CONFIG_DEBUG_PAGEALLOC is not set
+
+# CONFIG_DEBUG_WRITECOUNT is not set
+# CONFIG_DEBUG_OBJECTS is not set
+# CONFIG_DEBUG_OBJECTS_SELFTEST is not set
+# CONFIG_DEBUG_OBJECTS_FREE is not set
+# CONFIG_DEBUG_OBJECTS_TIMERS is not set
+CONFIG_DEBUG_OBJECTS_ENABLE_DEFAULT=1
+
+# CONFIG_X86_PTDUMP is not set
+
+# CONFIG_CAN_DEBUG_DEVICES is not set
+
+# CONFIG_MODULE_FORCE_UNLOAD is not set
+
+# CONFIG_SYSCTL_SYSCALL_CHECK is not set
+
+# CONFIG_DEBUG_NOTIFIERS is not set
+
+# CONFIG_DMA_API_DEBUG is not set
+
+# CONFIG_MMIOTRACE is not set
+
+# CONFIG_DEBUG_CREDENTIALS is not set
+
+# off in both production debug and nodebug builds,
+# on in rawhide nodebug builds
+# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
+
+# CONFIG_EXT4_DEBUG is not set
+
+# CONFIG_DEBUG_PERF_USE_VMALLOC is not set
+
+# CONFIG_JBD2_DEBUG is not set
diff --git a/config-powerpc-generic b/config-powerpc-generic
new file mode 100644
index 0000000..598fbb5
--- /dev/null
+++ b/config-powerpc-generic
@@ -0,0 +1,326 @@
+# Most PowerPC kernels we build are SMP
+CONFIG_SMP=y
+CONFIG_IRQ_ALL_CPUS=y
+CONFIG_PPC=y
+CONFIG_WATCHDOG_RTAS=m
+CONFIG_DEBUGGER=y
+CONFIG_GENERIC_NVRAM=y
+CONFIG_ALTIVEC=y
+
+CONFIG_TAU=y
+# CONFIG_TAU_INT is not set
+CONFIG_TAU_AVERAGE=y
+
+CONFIG_SECCOMP=y
+
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEBUG=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=m
+CONFIG_CPU_FREQ_GOV_ONDEMAND=m
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
+CONFIG_CPU_FREQ_TABLE=y
+CONFIG_CPU_FREQ_STAT=m
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+
+CONFIG_PM=y
+
+CONFIG_PM_STD_PARTITION=""
+
+CONFIG_SUSPEND=y
+CONFIG_HIBERNATION=y
+# CONFIG_RTC is not set
+# CONFIG_GEN_RTC is not set
+# CONFIG_GEN_RTC_X is not set
+CONFIG_RTC_DRV_GENERIC=y
+CONFIG_PROC_DEVICETREE=y
+# CONFIG_CMDLINE_BOOL is not set
+
+CONFIG_ADB=y
+CONFIG_ADB_PMU=y
+CONFIG_WINDFARM=y
+CONFIG_WINDFARM_PM112=y
+CONFIG_I2C_POWERMAC=y
+CONFIG_APPLE_AIRPORT=m
+CONFIG_SERIAL_PMACZILOG=m
+# CONFIG_SERIAL_PMACZILOG_TTYS is not set
+CONFIG_AGP_UNINORTH=y
+CONFIG_FB_OF=y
+# CONFIG_FB_CONTROL is not set
+CONFIG_FB_IBM_GXT4500=y
+CONFIG_FB_RADEON=y
+CONFIG_FB_MATROX=y
+CONFIG_FB_NVIDIA=m
+# CONFIG_FB_VGA16 is not set
+CONFIG_FB_ATY128_BACKLIGHT=y
+CONFIG_FB_ATY_BACKLIGHT=y
+CONFIG_FB_RADEON_BACKLIGHT=y
+CONFIG_FB_RIVA_BACKLIGHT=y
+CONFIG_FB_NVIDIA_BACKLIGHT=y
+
+CONFIG_SND_POWERMAC=m
+CONFIG_SND_POWERMAC_AUTO_DRC=y
+CONFIG_SND_AOA=m
+CONFIG_SND_AOA_SOUNDBUS=m
+CONFIG_SND_AOA_FABRIC_LAYOUT=m
+CONFIG_SND_AOA_ONYX=m
+CONFIG_SND_AOA_TAS=m
+CONFIG_SND_AOA_TOONIE=m
+CONFIG_SND_AOA_SOUNDBUS_I2S=m
+
+CONFIG_XMON=y
+# CONFIG_XMON_DEFAULT is not set
+CONFIG_XMON_DISASSEMBLY=y
+
+CONFIG_BOOTX_TEXT=y
+CONFIG_MAC_EMUMOUSEBTN=y
+CONFIG_CAPI_EICON=y
+
+CONFIG_NVRAM=y
+
+# CONFIG_PCMCIA_M8XX is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_IPS is not set
+# CONFIG_NI52 is not set
+# CONFIG_NI65 is not set
+# CONFIG_LANCE is not set
+# CONFIG_3C515 is not set
+# CONFIG_ELPLUS is not set
+
+CONFIG_MEMORY_HOTPLUG=y
+
+# Stuff which wants bus_to_virt() or virt_to_bus()
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_VIDEO_STRADIS is not set
+# CONFIG_VIDEO_ZORAN is not set
+# CONFIG_ATM_HORIZON is not set
+# CONFIG_ATM_FIRESTREAM is not set
+# CONFIG_ATM_AMBASSADOR is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+
+
+# CONFIG_PPC_EARLY_DEBUG is not set
+
+# CONFIG_PMAC_BACKLIGHT_LEGACY is not set
+CONFIG_LEDS_TRIGGER_TIMER=m
+CONFIG_LEDS_TRIGGER_HEARTBEAT=m
+CONFIG_LEDS_TRIGGER_GPIO=m
+
+# FIXME: Should depend on IA64/x86
+# CONFIG_SGI_IOC4 is not set
+
+CONFIG_PPC_EFIKA=y
+CONFIG_PPC_MEDIA5200=y
+
+# CONFIG_PPC_LITE5200 is not set
+CONFIG_PPC_BESTCOMM=y
+CONFIG_PMAC_RACKMETER=m
+CONFIG_USB_OHCI_HCD_PPC_SOC=y
+CONFIG_USB_OHCI_HCD_PCI=y
+CONFIG_USB_OHCI_HCD_PPC_OF=y
+CONFIG_USB_OHCI_HCD_PPC_OF_BE=y
+CONFIG_USB_OHCI_HCD_PPC_OF_LE=y
+
+CONFIG_SERIAL_UARTLITE=m
+CONFIG_SERIAL_UARTLITE_CONSOLE=y
+
+CONFIG_SENSORS_AMS=m
+CONFIG_SENSORS_AMS_PMU=y
+CONFIG_SENSORS_AMS_I2C=y
+
+CONFIG_IDE=y
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_IDE_SATA is not set
+# CONFIG_BLK_DEV_IDECS is not set
+CONFIG_BLK_DEV_IDECD=m
+# CONFIG_BLK_DEV_IDETAPE is not set
+CONFIG_IDE_TASK_IOCTL=y
+#
+# IDE chipset support/bugfixes
+#
+# CONFIG_IDE_GENERIC is not set
+# CONFIG_BLK_DEV_IDEPNP is not set
+# CONFIG_BLK_DEV_IDEPCI is not set
+# CONFIG_BLK_DEV_AEC62XX is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD74XX is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_TRIFLEX is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_CS5520 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_JMICRON is not set
+# CONFIG_BLK_DEV_SC1200 is not set
+# CONFIG_BLK_DEV_PIIX is not set
+# CONFIG_BLK_DEV_IT821X is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_PDC202XX_OLD is not set
+# CONFIG_BLK_DEV_PDC202XX_NEW is not set
+# CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SIIMAGE is not set
+# CONFIG_BLK_DEV_SL82C105 is not set
+# CONFIG_BLK_DEV_SLC90E66 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+CONFIG_BLK_DEV_IDE_PMAC=y
+CONFIG_BLK_DEV_IDE_PMAC_ATA100FIRST=y
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_BLK_DEV_HD is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+CONFIG_MTD_PHYSMAP_OF=m
+CONFIG_IDE_PROC_FS=y
+CONFIG_MACINTOSH_DRIVERS=y
+
+CONFIG_PPC_PASEMI_MDIO=m
+CONFIG_SPU_FS_64K_LS=y
+CONFIG_PPC_PASEMI_CPUFREQ=y
+CONFIG_PMAC_APM_EMU=m
+CONFIG_HW_RANDOM_PASEMI=m
+
+CONFIG_EDAC=y
+# CONFIG_EDAC_DEBUG is not set
+CONFIG_EDAC_MM_EDAC=m
+CONFIG_EDAC_PASEMI=m
+CONFIG_EDAC_AMD8131=m
+CONFIG_EDAC_AMD8111=m
+
+CONFIG_AXON_RAM=m
+CONFIG_OPROFILE_CELL=y
+
+CONFIG_SUSPEND_FREEZER=y
+# CONFIG_IDEPCI_PCIBUS_ORDER is not set
+CONFIG_PATA_PLATFORM=m
+CONFIG_PATA_OF_PLATFORM=m
+CONFIG_USB_EHCI_HCD_PPC_OF=y
+
+# CONFIG_MPC5121_ADS is not set
+# CONFIG_MPC5121_GENERIC is not set
+CONFIG_MTD_OF_PARTS=m
+# CONFIG_MTD_NAND_FSL_ELBC is not set
+CONFIG_THERMAL=y
+
+# CONFIG_MEMORY_HOTREMOVE is not set
+
+CONFIG_DMADEVICES=y
+# CONFIG_FSL_DMA is not set
+
+CONFIG_SND_PPC=y
+
+CONFIG_PPC_82xx=y
+CONFIG_PPC_83xx=y
+CONFIG_PPC_86xx=y
+CONFIG_EXTRA_TARGETS=""
+# CONFIG_CODE_PATCHING_SELFTEST is not set
+# CONFIG_FTR_FIXUP_SELFTEST is not set
+
+# CONFIG_MATH_EMULATION is not set
+# CONFIG_RAPIDIO is not set
+# CONFIG_FS_ENET is not set
+# CONFIG_UCC_GETH is not set
+# CONFIG_KEYBOARD_GPIO is not set
+# CONFIG_KEYBOARD_MATRIX is not set
+# CONFIG_MOUSE_GPIO is not set
+# CONFIG_SERIAL_CPM is not set
+# CONFIG_SERIAL_QE is not set
+# CONFIG_I2C_CPM is not set
+# CONFIG_I2C_GPIO is not set
+# CONFIG_W1_MASTER_GPIO is not set
+# CONFIG_LEDS_GPIO is not set
+# CONFIG_GPIO_SYSFS is not set
+# CONFIG_GPIO_MAX732X is not set
+
+
+CONFIG_SERIO_XILINX_XPS_PS2=m
+
+# CONFIG_PPC_SMLPAR is not set
+
+CONFIG_MGCOGE=y
+CONFIG_GEF_SBC610=y
+CONFIG_GEF_PPC9A=y
+CONFIG_GEF_SBC310=y
+
+CONFIG_QUICC_ENGINE=y
+CONFIG_QE_GPIO=y
+CONFIG_MPC8xxx_GPIO=y
+
+CONFIG_IDE_GD=y
+CONFIG_IDE_GD_ATA=y
+CONFIG_IDE_GD_ATAPI=y
+
+CONFIG_MCU_MPC8349EMITX=m
+
+CONFIG_GPIO_XILINX=y
+
+CONFIG_UCB1400_CORE=m
+
+CONFIG_PMIC_DA903X=y
+CONFIG_BACKLIGHT_DA903X=m
+CONFIG_LEDS_DA903X=m
+
+CONFIG_MSI_BITMAP_SELFTEST=y
+
+CONFIG_RELOCATABLE=y
+
+# CONFIG_HVC_UDBG is not set
+CONFIG_PRINT_STACK_DEPTH=64
+
+CONFIG_BATTERY_DA9030=m
+# CONFIG_TWL4030_CORE is not set
+
+CONFIG_BLK_DEV_IT8172=m
+CONFIG_TOUCHSCREEN_DA9034=m
+
+CONFIG_SIMPLE_GPIO=y
+
+CONFIG_FSL_PQ_MDIO=m
+
+CONFIG_PS3_VRAM=m
+CONFIG_MDIO_GPIO=m
+CONFIG_SERIAL_OF_PLATFORM_NWPSERIAL=m
+# CONFIG_DEBUG_GPIO is not set
+CONFIG_GPIO_PCA953X=m
+CONFIG_GPIO_PCF857X=m
+CONFIG_USB_GPIO_VBUS=m
+
+CONFIG_USB_FHCI_HCD=m
+# CONFIG_FHCI_DEBUG is not set
+
+# CONFIG_DRM_RADEON_KMS is not set
+
+# CONFIG_AMIGAONE is not set
+
+CONFIG_PPC_OF_BOOT_TRAMPOLINE=y
+
+CONFIG_DTL=y
+
+CONFIG_MMC_SDHCI_OF=m
+
+# CONFIG_CONSISTENT_SIZE_BOOL is not set
+
+CONFIG_CAN_SJA1000_OF_PLATFORM=m
+
+CONFIG_PPC_EMULATED_STATS=y
+
+CONFIG_SWIOTLB=y
+
+# CONFIG_RDS is not set
+
+CONFIG_PPC_DISABLE_WERROR=y
+
+CONFIG_XILINX_EMACLITE=m
+
+CONFIG_GPIO_WM831X=m
+# CONFIG_GPIO_LANGWELL is not set
+# CONFIG_GPIO_UCB1400 is not set
+CONFIG_EDAC_MPC85XX=m
+
diff --git a/config-powerpc32-generic b/config-powerpc32-generic
new file mode 100644
index 0000000..39f9f74
--- /dev/null
+++ b/config-powerpc32-generic
@@ -0,0 +1,184 @@
+# CONFIG_SMP is not set
+CONFIG_PPC32=y
+# CONFIG_PPC64 is not set
+# CONFIG_RTAS_PROC is not set
+# CONFIG_PCMCIA_M8XX is not set
+# CONFIG_HOTPLUG_PCI is not set
+CONFIG_CPU_FREQ_PMAC=y
+CONFIG_PPC_CHRP=y
+CONFIG_PPC_PMAC=y
+CONFIG_PPC_MPC52xx=y
+CONFIG_PPC_PREP=y
+
+# CONFIG_PPC_MPC5200_SIMPLE is not set
+CONFIG_SATA_FSL=m
+# CONFIG_SATA_NV is not set
+
+# busted in .28git1
+# ERROR: "cacheable_memzero" [drivers/net/gianfar_driver.ko] undefined!
+# CONFIG_GIANFAR is not set
+CONFIG_USB_EHCI_FSL=y
+
+CONFIG_PMAC_APM_EMU=y
+CONFIG_PMAC_BACKLIGHT=y
+
+CONFIG_HIGHMEM=y
+# CONFIG_HIGHMEM_START_BOOL is not set
+# CONFIG_LOWMEM_SIZE_BOOL is not set
+# CONFIG_TASK_SIZE_BOOL is not set
+# CONFIG_KERNEL_START_BOOL is not set
+# CONFIG_PPC601_SYNC_FIX is not set
+CONFIG_ADVANCED_OPTIONS=y
+CONFIG_SCSI_MESH=m
+CONFIG_SCSI_MESH_SYNC_RATE=5
+CONFIG_SCSI_MESH_RESET_DELAY_MS=4000
+
+CONFIG_SCSI_MAC53C94=m
+CONFIG_ADB_CUDA=y
+CONFIG_ADB_MACIO=y
+CONFIG_INPUT_ADBHID=y
+CONFIG_ADB_PMU_LED=y
+CONFIG_ADB_PMU_LED_IDE=y
+
+CONFIG_PMAC_MEDIABAY=y
+CONFIG_BMAC=m
+CONFIG_MACE=m
+# CONFIG_MACE_AAUI_PORT is not set
+CONFIG_MV643XX_ETH=m
+CONFIG_I2C_HYDRA=m
+CONFIG_I2C_MPC=m
+CONFIG_THERM_WINDTUNNEL=m
+CONFIG_THERM_ADT746X=m
+# CONFIG_ANSLCD is not set
+
+CONFIG_FB_PLATINUM=y
+CONFIG_FB_VALKYRIE=y
+CONFIG_FB_CT65550=y
+# CONFIG_BDI_SWITCH is not set
+
+CONFIG_MAC_FLOPPY=m
+# CONFIG_BLK_DEV_FD is not set
+
+CONFIG_FB_ATY128=y
+CONFIG_FB_ATY=y
+CONFIG_FB_MATROX=y
+# CONFIG_KEXEC is not set
+
+# CONFIG_HVC_RTAS is not set
+
+# CONFIG_UDBG_RTAS_CONSOLE is not set
+CONFIG_BRIQ_PANEL=m
+
+# CONFIG_ATA_PIIX is not set
+# CONFIG_PATA_AMD is not set
+# CONFIG_PATA_ATIIXP is not set
+CONFIG_PATA_MPC52xx=m
+# CONFIG_PATA_MPIIX is not set
+# CONFIG_PATA_OLDPIIX is not set
+# CONFIG_PATA_OPTI is not set
+# CONFIG_PATA_SERVERWORKS is not set
+
+CONFIG_SERIAL_MPC52xx=y
+CONFIG_SERIAL_MPC52xx_CONSOLE=y
+CONFIG_SERIAL_MPC52xx_CONSOLE_BAUD=115200
+# CONFIG_MPC5200_WDT is not set
+CONFIG_8xxx_WDT=m
+CONFIG_GEF_WDT=m
+
+CONFIG_PPC_MPC5200_BUGFIX=y
+CONFIG_FEC_MPC52xx=m
+#CHECK: This may later become a tristate.
+CONFIG_FEC_MPC52xx_MDIO=y
+CONFIG_PPC_MPC5200_GPIO=y
+CONFIG_MDIO_GPIO=m
+
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_DEBUG_STACKOVERFLOW=y
+
+# CONFIG_EMBEDDED6xx is not set
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+
+# CONFIG_BLK_DEV_PLATFORM is not set
+# CONFIG_BLK_DEV_4DRIVES is not set
+# CONFIG_BLK_DEV_ALI14XX is not set
+# CONFIG_BLK_DEV_DTC2278 is not set
+# CONFIG_BLK_DEV_HT6560B is not set
+# CONFIG_BLK_DEV_QD65XX is not set
+# CONFIG_BLK_DEV_UMC8672 is not set
+
+# CONFIG_VIRQ_DEBUG is not set
+
+CONFIG_PPC_BESTCOMM_ATA=m
+CONFIG_PPC_BESTCOMM_FEC=m
+CONFIG_PPC_BESTCOMM_GEN_BD=m
+
+CONFIG_FORCE_MAX_ZONEORDER=11
+# CONFIG_PAGE_OFFSET_BOOL is not set
+# CONFIG_FB_FSL_DIU is not set
+CONFIG_IRQSTACKS=y
+CONFIG_VIRTUALIZATION=y
+
+# CONFIG_DEBUG_GPIO is not set
+# CONFIG_GPIO_PCA953X is not set
+# CONFIG_GPIO_PCF857X is not set
+# CONFIG_HTC_EGPIO is not set
+
+# CONFIG_TIFM_CORE is not set
+
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_CISS_SCSI_TAPE is not set
+
+# CONFIG_I2C_NFORCE2 is not set
+
+# CONFIG_SND_INTEL8X0 is not set
+# CONFIG_SND_INTEL8X0M is not set
+
+# CONFIG_MEMSTICK is not set
+
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_TCG_TPM is not set
+
+# PPC gets sad with debug alloc (bz 448598)
+# CONFIG_DEBUG_PAGEALLOC is not set
+
+CONFIG_SND_ISA=y
+CONFIG_CRYPTO_DEV_TALITOS=m
+
+CONFIG_FSL_EMB_PERFMON=y
+CONFIG_MPC8272_ADS=y
+CONFIG_PQ2FADS=y
+CONFIG_EP8248E=y
+CONFIG_MPC831x_RDB=y
+CONFIG_MPC832x_MDS=y
+CONFIG_MPC832x_RDB=y
+CONFIG_MPC834x_MDS=y
+CONFIG_MPC834x_ITX=y
+CONFIG_MPC836x_MDS=y
+CONFIG_MPC836x_RDK=y
+CONFIG_MPC837x_MDS=y
+CONFIG_MPC837x_RDB=y
+CONFIG_SBC834x=y
+CONFIG_ASP834x=y
+CONFIG_KMETER1=y
+CONFIG_MPC8641_HPCN=y
+CONFIG_SBC8641D=y
+CONFIG_MPC8610_HPCD=y
+
+# CONFIG_USB_MUSB_HDRC is not set
+
+# busted in 2.6.27
+# drivers/mtd/maps/sbc8240.c: In function 'init_sbc8240_mtd':
+# drivers/mtd/maps/sbc8240.c:172: warning: passing argument 1 of 'simple_map_init' from incompatible pointer type
+# drivers/mtd/maps/sbc8240.c:177: error: 'struct mtd_info' has no member named 'module'
+
+CONFIG_MTD_NAND_FSL_UPM=m
+
+CONFIG_USB_GPIO_VBUS=m
+
+CONFIG_RCU_FANOUT=32
+
+CONFIG_PERF_COUNTERS=y
+CONFIG_PERF_EVENTS=y
+CONFIG_EVENT_PROFILE=y
+
diff --git a/config-powerpc32-smp b/config-powerpc32-smp
new file mode 100644
index 0000000..e60f59c
--- /dev/null
+++ b/config-powerpc32-smp
@@ -0,0 +1,4 @@
+CONFIG_SMP=y
+# CONFIG_HOTPLUG_CPU is not set
+CONFIG_NR_CPUS=4
+# CONFIG_BATTERY_PMU is not set
diff --git a/config-powerpc64 b/config-powerpc64
new file mode 100644
index 0000000..d9f7983
--- /dev/null
+++ b/config-powerpc64
@@ -0,0 +1,176 @@
+CONFIG_WINDFARM_PM81=y
+CONFIG_WINDFARM_PM91=y
+CONFIG_WINDFARM_PM121=y
+CONFIG_PPC_PMAC64=y
+CONFIG_PPC_MAPLE=y
+CONFIG_PPC_CELL=y
+CONFIG_PPC_IBM_CELL_BLADE=y
+CONFIG_PPC_ISERIES=y
+CONFIG_PPC_PSERIES=y
+CONFIG_PPC_PMAC=y
+CONFIG_PPC_PASEMI=y
+# CONFIG_PPC_PASEMI_IOMMU_DMA_FORCE is not set
+CONFIG_PPC_PS3=y
+CONFIG_PPC_CELLEB=y
+CONFIG_PPC_CELL_QPACE=y
+CONFIG_PS3_HTAB_SIZE=20
+# CONFIG_PS3_DYNAMIC_DMA is not set
+CONFIG_PS3_ADVANCED=y
+CONFIG_PS3_HTAB_SIZE=20
+# CONFIG_PS3_DYNAMIC_DMA is not set
+CONFIG_PS3_VUART=y
+CONFIG_PS3_PS3AV=y
+CONFIG_PS3_STORAGE=m
+CONFIG_PS3_DISK=m
+CONFIG_PS3_ROM=m
+CONFIG_PS3_FLASH=m
+CONFIG_PS3_LPM=y
+CONFIG_SND_PS3=m
+CONFIG_SND_PS3_DEFAULT_START_DELAY=1000
+CONFIG_GELIC_NET=m
+CONFIG_GELIC_WIRELESS=y
+CONFIG_GELIC_WIRELESS_OLD_PSK_INTERFACE=y
+CONFIG_CBE_THERM=m
+CONFIG_CBE_CPUFREQ=m
+CONFIG_CBE_CPUFREQ_PMI=m
+CONFIG_CBE_CPUFREQ_PMI_ENABLE=y
+CONFIG_PMAC_RACKMETER=m
+CONFIG_IBMEBUS=y
+CONFIG_SPU_FS=m
+CONFIG_RTAS_FLASH=y
+CONFIG_PPC_SPLPAR=y
+CONFIG_SCANLOG=y
+CONFIG_LPARCFG=y
+CONFIG_SERIAL_ICOM=m
+CONFIG_HVCS=m
+CONFIG_HVC_CONSOLE=y
+CONFIG_HOTPLUG_PCI=y
+CONFIG_THERM_PM72=y
+CONFIG_IBMVETH=m
+CONFIG_SCSI_IBMVSCSI=m
+# CONFIG_HOTPLUG_PCI_CPCI is not set
+CONFIG_HOTPLUG_PCI_SHPC=m
+CONFIG_HOTPLUG_PCI_RPA=m
+CONFIG_HOTPLUG_PCI_RPA_DLPAR=y
+CONFIG_ADB_PMU_LED=y
+CONFIG_ADB_PMU_LED_IDE=y
+CONFIG_PMAC_SMU=y
+CONFIG_CPU_FREQ_PMAC64=y
+CONFIG_SCSI_IPR=m
+CONFIG_SCSI_IPR_TRACE=y
+CONFIG_SCSI_IPR_DUMP=y
+CONFIG_SPIDER_NET=m
+CONFIG_HVC_RTAS=y
+CONFIG_HVC_ISERIES=y
+CONFIG_CBE_RAS=y
+
+# iSeries device drivers
+#
+CONFIG_ISERIES_VETH=m
+CONFIG_VIODASD=m
+CONFIG_VIOCD=m
+CONFIG_VIOTAPE=m
+
+CONFIG_PASEMI_MAC=m
+CONFIG_SERIAL_OF_PLATFORM=m
+
+CONFIG_PPC_PASEMI_IOMMU=y
+CONFIG_SERIAL_TXX9=y
+CONFIG_SERIAL_TXX9_NR_UARTS=6
+CONFIG_SERIAL_TXX9_CONSOLE=y
+
+CONFIG_HVC_BEAT=y
+
+CONFIG_FB_PS3=y
+CONFIG_FB_PS3_DEFAULT_SIZE_M=18
+
+CONFIG_PPC_PMI=m
+CONFIG_PS3_SYS_MANAGER=y
+# CONFIG_BLK_DEV_CELLEB is not set
+
+CONFIG_PATA_SCC=m
+
+CONFIG_APM_EMULATION=m
+
+CONFIG_PPC64=y
+CONFIG_VIRT_CPU_ACCOUNTING=y
+CONFIG_NR_CPUS=128
+# CONFIG_FB_PLATINUM is not set
+# CONFIG_FB_VALKYRIE is not set
+# CONFIG_FB_CT65550 is not set
+# CONFIG_FB_VGA16 is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_ATY is not set
+
+# CONFIG_POWER4_ONLY is not set
+
+CONFIG_RTAS_PROC=y
+CONFIG_IOMMU_VMERGE=y
+CONFIG_NUMA=y
+# CONFIG_PPC_64K_PAGES is not set
+CONFIG_SCHED_SMT=y
+
+# CONFIG_MV643XX_ETH is not set
+CONFIG_IRQSTACKS=y
+CONFIG_DEBUG_STACKOVERFLOW=y
+# CONFIG_INPUT_PCSPKR is not set
+
+CONFIG_EHEA=m
+CONFIG_INFINIBAND_EHCA=m
+
+CONFIG_HCALL_STATS=y
+
+CONFIG_XMON_DISASSEMBLY=y
+
+CONFIG_SCSI_IBMVSCSIS=m
+
+CONFIG_SECCOMP=y
+
+CONFIG_TUNE_CELL=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+# CONFIG_BLK_DEV_PLATFORM is not set
+CONFIG_IBM_NEW_EMAC=m
+CONFIG_IBM_NEW_EMAC_RXB=128
+CONFIG_IBM_NEW_EMAC_TXB=64
+CONFIG_IBM_NEW_EMAC_POLL_WEIGHT=32
+CONFIG_IBM_NEW_EMAC_RX_COPY_THRESHOLD=256
+CONFIG_IBM_NEW_EMAC_RX_SKB_HEADROOM=0
+# CONFIG_IBM_NEW_EMAC_DEBUG is not set
+
+# CONFIG_VIRQ_DEBUG is not set
+CONFIG_ELECTRA_CF=m
+
+CONFIG_SPARSEMEM_VMEMMAP=y
+
+CONFIG_MTD_NAND_PASEMI=m
+CONFIG_EDAC_CELL=m
+CONFIG_EDAC_CPC925=m
+CONFIG_FRAME_WARN=2048
+
+CONFIG_PHYP_DUMP=y
+CONFIG_FORCE_MAX_ZONEORDER=13
+CONFIG_VIRTUALIZATION=y
+
+CONFIG_VSX=y
+
+CONFIG_SCSI_IBMVFC=m
+# CONFIG_SCSI_IBMVFC_TRACE is not set
+CONFIG_IBM_BSR=m
+
+CONFIG_SERIO_XILINX_XPS_PS2=m
+
+CONFIG_PPC_IBM_CELL_RESETBUTTON=y
+CONFIG_PPC_IBM_CELL_POWERBUTTON=m
+CONFIG_CBE_CPUFREQ_SPU_GOVERNOR=m
+
+CONFIG_RTC_DRV_PS3=y
+
+CONFIG_CRASH_DUMP=y
+CONFIG_RELOCATABLE=y
+
+CONFIG_RCU_FANOUT=64
+
+CONFIG_PERF_COUNTERS=y
+CONFIG_PERF_EVENTS=y
+CONFIG_EVENT_PROFILE=y
diff --git a/config-rhel-generic b/config-rhel-generic
new file mode 100644
index 0000000..197ae83
--- /dev/null
+++ b/config-rhel-generic
@@ -0,0 +1,204 @@
+# CONFIG_ISA is not set
+# CONFIG_ISAPNP is not set
+# CONFIG_I2C_PCA_ISA is not set
+
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_DC390T is not set
+
+# CONFIG_ATALK is not set
+# CONFIG_DEV_APPLETALK is not set
+# CONFIG_LTPC is not set
+# CONFIG_COPS is not set
+# CONFIG_IPX is not set
+# CONFIG_IPDDP is not set
+# CONFIG_DECNET is not set
+# CONFIG_PLIP is not set
+
+# CONFIG_PCMCIA_AHA152X is not set
+# CONFIG_PCMCIA_NINJA_SCSI is not set
+# CONFIG_PCMCIA_QLOGIC is not set
+# CONFIG_PCMCIA_SYM53C500 is not set
+
+# CONFIG_EL2 is not set
+# CONFIG_ELPLUS is not set
+# CONFIG_WD80x3 is not set
+# CONFIG_I82092 is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_SUNDANCE is not set
+# CONFIG_ULTRA is not set
+# CONFIG_SKFP is not set
+# CONFIG_DE600 is not set
+# CONFIG_DE620 is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_AC3200 is not set
+# CONFIG_NI52 is not set
+# CONFIG_NI65 is not set
+# CONFIG_LANCE is not set
+# CONFIG_EL16 is not set
+# CONFIG_EL3 is not set
+# CONFIG_3C515 is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_HP100 is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_NET_SB1000 is not set
+# CONFIG_DEPCA is not set
+# CONFIG_ATP is not set
+
+# CONFIG_TR is not set
+
+# CONFIG_GAMEPORT is not set
+
+# CONFIG_SND_AD1816A is not set
+# CONFIG_SND_AD1848 is not set
+# CONFIG_SND_CS4231 is not set
+# CONFIG_SND_CS4236 is not set
+# CONFIG_SND_ES968 is not set
+# CONFIG_SND_ES1688 is not set
+# CONFIG_SND_ES18XX is not set
+# CONFIG_SND_GUSCLASSIC is not set
+# CONFIG_SND_GUSEXTREME is not set
+# CONFIG_SND_GUSMAX is not set
+# CONFIG_SND_INTERWAVE is not set
+# CONFIG_SND_INTERWAVE_STB is not set
+# CONFIG_SND_OPTI92X_AD1848 is not set
+# CONFIG_SND_OPTI92X_CS4231 is not set
+# CONFIG_SND_OPTI93X is not set
+# CONFIG_SND_MIRO is not set
+# CONFIG_SND_SB8 is not set
+# CONFIG_SND_SB16 is not set
+# CONFIG_SND_SBAWE is not set
+# CONFIG_SND_SB16_CSP is not set
+# CONFIG_SND_WAVEFRONT is not set
+# CONFIG_SND_ALS100 is not set
+# CONFIG_SND_AZT2320 is not set
+# CONFIG_SND_CMI8330 is not set
+# CONFIG_SND_DT019X is not set
+# CONFIG_SND_OPL3SA2 is not set
+# CONFIG_SND_SGALAXY is not set
+# CONFIG_SND_SSCAPE is not set
+
+# CONFIG_WAN_ROUTER is not set
+
+# CONFIG_BINFMT_AOUT is not set
+
+# CONFIG_DRM_TDFX is not set
+# CONFIG_DRM_SIS is not set
+
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+
+# CONFIG_PARIDE is not set
+
+# CONFIG_I2O is not set
+
+# CONFIG_MWAVE is not set
+
+# CONFIG_ROCKETPORT is not set
+# CONFIG_R3964 is not set
+
+# CONFIG_JOYSTICK_ANALOG is not set
+# CONFIG_JOYSTICK_A3D is not set
+# CONFIG_JOYSTICK_ADI is not set
+# CONFIG_JOYSTICK_COBRA is not set
+# CONFIG_JOYSTICK_GF2K is not set
+# CONFIG_JOYSTICK_GRIP is not set
+# CONFIG_JOYSTICK_GRIP_MP is not set
+# CONFIG_JOYSTICK_GUILLEMOT is not set
+# CONFIG_JOYSTICK_INTERACT is not set
+# CONFIG_JOYSTICK_SIDEWINDER is not set
+# CONFIG_JOYSTICK_TMDC is not set
+# CONFIG_JOYSTICK_IFORCE is not set
+# CONFIG_JOYSTICK_WARRIOR is not set
+# CONFIG_JOYSTICK_MAGELLAN is not set
+# CONFIG_JOYSTICK_SPACEORB is not set
+# CONFIG_JOYSTICK_SPACEBALL is not set
+# CONFIG_JOYSTICK_STINGER is not set
+# CONFIG_JOYSTICK_DB9 is not set
+# CONFIG_JOYSTICK_GAMECON is not set
+# CONFIG_JOYSTICK_TURBOGRAFX is not set
+
+# CONFIG_RADIO_CADET is not set
+# CONFIG_RADIO_RTRACK is not set
+# CONFIG_RADIO_RTRACK2 is not set
+# CONFIG_RADIO_AZTECH is not set
+# CONFIG_RADIO_GEMTEK is not set
+# CONFIG_RADIO_GEMTEK_PCI is not set
+# CONFIG_RADIO_MAXIRADIO is not set
+# CONFIG_RADIO_MAESTRO is not set
+# CONFIG_RADIO_SF16FMI is not set
+# CONFIG_RADIO_SF16FMR2 is not set
+# CONFIG_RADIO_TERRATEC is not set
+# CONFIG_RADIO_TRUST is not set
+# CONFIG_RADIO_TYPHOON is not set
+# CONFIG_RADIO_ZOLTRIX is not set
+
+
+# CONFIG_VIDEO_PMS is not set
+# CONFIG_VIDEO_BWQCAM is not set
+# CONFIG_VIDEO_CQCAM is not set
+# CONFIG_VIDEO_W9966 is not set
+# CONFIG_VIDEO_CPIA is not set
+# CONFIG_VIDEO_CPIA_PP is not set
+# CONFIG_VIDEO_CPIA_USB is not set
+# CONFIG_VIDEO_SAA5249 is not set
+# CONFIG_VIDEO_STRADIS is not set
+# CONFIG_VIDEO_ZORAN is not set
+# CONFIG_VIDEO_ZORAN_BUZ is not set
+# CONFIG_VIDEO_ZORAN_DC10 is not set
+# CONFIG_VIDEO_ZORAN_DC30 is not set
+# CONFIG_VIDEO_ZORAN_LML33 is not set
+# CONFIG_VIDEO_ZORAN_LML33R10 is not set
+# CONFIG_VIDEO_MEYE is not set
+# CONFIG_VIDEO_SAA7134 is not set
+# CONFIG_VIDEO_MXB is not set
+# CONFIG_VIDEO_HEXIUM_ORION is not set
+# CONFIG_VIDEO_HEXIUM_GEMINI is not set
+# CONFIG_VIDEO_CX88 is not set
+# CONFIG_VIDEO_SAA5246A is not set
+
+# CONFIG_INFTL is not set
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_PMC551 is not set
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PCI is not set
+
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_HGA is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_ASILIANT is not set
+# CONFIG_FB_HGA_ACCEL is not set
+# CONFIG_FB_3DFX_ACCEL is not set
+
+# CONFIG_JFS_FS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_CODA_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_BFS_FS is not set
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_SYSV_FS is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_ROMFS_FS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_9P_FS is not set
+# CONFIG_OCFS2_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+
diff --git a/config-s390x b/config-s390x
new file mode 100644
index 0000000..632983b
--- /dev/null
+++ b/config-s390x
@@ -0,0 +1,227 @@
+CONFIG_64BIT=y
+# CONFIG_MARCH_G5 is not set
+# CONFIG_MARCH_Z900 is not set
+CONFIG_MARCH_Z9_109=y
+# CONFIG_MARCH_Z990 is not set
+
+CONFIG_NR_CPUS=64
+CONFIG_COMPAT=y
+
+# See bug 496596
+CONFIG_HZ_100=y
+# See bug 496605
+# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
+
+CONFIG_MMU=y
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+
+CONFIG_LOG_BUF_SHIFT=16
+CONFIG_NO_IDLE_HZ=y
+
+CONFIG_SMP=y
+
+#
+# I/O subsystem configuration
+#
+CONFIG_QDIO=m
+
+#
+# Misc
+#
+CONFIG_IPL=y
+# CONFIG_IPL_TAPE is not set
+CONFIG_IPL_VM=y
+# CONFIG_PROCESS_DEBUG is not set
+CONFIG_PFAULT=y
+CONFIG_SHARED_KERNEL=y
+CONFIG_CMM=m
+CONFIG_CMM_PROC=y
+CONFIG_NETIUCV=m
+CONFIG_SMSGIUCV=m
+
+#
+# SCSI low-level drivers
+#
+CONFIG_ZFCP=m
+CONFIG_ZFCPDUMP=y
+CONFIG_CCW=y
+
+#
+# S/390 block device drivers
+#
+CONFIG_DCSSBLK=m
+CONFIG_BLK_DEV_XPRAM=m
+CONFIG_DASD=m
+CONFIG_DASD_PROFILE=y
+CONFIG_DASD_ECKD=m
+CONFIG_DASD_FBA=m
+CONFIG_DASD_DIAG=m
+CONFIG_DASD_EER=y
+
+#
+# S/390 character device drivers
+#
+CONFIG_TN3270=y
+CONFIG_TN3270_CONSOLE=y
+CONFIG_TN3215=y
+CONFIG_TN3215_CONSOLE=y
+CONFIG_CCW_CONSOLE=y
+CONFIG_SCLP_TTY=y
+CONFIG_SCLP_CONSOLE=y
+CONFIG_SCLP_VT220_TTY=y
+CONFIG_SCLP_VT220_CONSOLE=y
+CONFIG_SCLP_CPI=m
+CONFIG_SCLP_ASYNC=m
+CONFIG_S390_TAPE=m
+CONFIG_S390_TAPE_3590=m
+
+CONFIG_APPLDATA_BASE=y
+CONFIG_APPLDATA_MEM=m
+CONFIG_APPLDATA_OS=m
+CONFIG_APPLDATA_NET_SUM=m
+CONFIG_TN3270_TTY=y
+CONFIG_TN3270_FS=m
+
+
+#
+# S/390 tape interface support
+#
+CONFIG_S390_TAPE_BLOCK=y
+
+#
+# S/390 tape hardware support
+#
+CONFIG_S390_TAPE_34XX=m
+
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+
+#
+# Token Ring devices
+#
+CONFIG_TR=y
+CONFIG_NETCONSOLE=m
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# S/390 network device drivers
+#
+CONFIG_LCS=m
+CONFIG_CTC=m
+CONFIG_IUCV=m
+CONFIG_QETH=m
+CONFIG_QETH_IPV6=y
+CONFIG_CCWGROUP=m
+
+# CONFIG_IRDA is not set
+# CONFIG_BT is not set
+# CONFIG_WIRELESS_EXT is not set
+# CONFIG_WLAN_80211 is not set
+# CONFIG_MAC80211 is not set
+# CONFIG_B44 is not set
+
+#
+# Partition Types
+#
+CONFIG_PARTITION_ADVANCED=y
+# CONFIG_OSF_PARTITION is not set
+CONFIG_IBM_PARTITION=y
+# CONFIG_MAC_PARTITION is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_SGI_PARTITION is not set
+# CONFIG_SUN_PARTITION is not set
+
+
+#
+# S390 crypto hw
+#
+CONFIG_CRYPTO_SHA1_S390=m
+CONFIG_CRYPTO_SHA256_S390=m
+CONFIG_CRYPTO_DES_S390=m
+CONFIG_CRYPTO_AES_S390=m
+
+#
+# Kernel hacking
+#
+
+#
+# S390 specific stack options; needs gcc 3.5 so off for now
+#
+CONFIG_PACK_STACK=y
+CONFIG_CHECK_STACK=y
+# CONFIG_WARN_STACK is not set
+# CONFIG_SMALL_STACK is not set
+
+CONFIG_ZVM_WATCHDOG=m
+CONFIG_VMLOGRDR=m
+CONFIG_MONREADER=m
+
+CONFIG_VIRT_CPU_ACCOUNTING=y
+
+# CONFIG_CLAW is not set
+
+CONFIG_VMCP=m
+
+# CONFIG_ATMEL is not set
+
+# CONFIG_CDROM_PKTCDVD is not set
+# CONFIG_ATA_OVER_ETH is not set
+# CONFIG_MII is not set
+
+
+CONFIG_STACK_GUARD=256
+CONFIG_CMM_IUCV=y
+
+# CONFIG_DETECT_SOFTLOCKUP is not set
+
+CONFIG_S390_HYPFS_FS=y
+
+CONFIG_MONWRITER=m
+CONFIG_ZCRYPT=m
+CONFIG_ZCRYPT_MONOLITHIC=y
+
+CONFIG_S390_SWITCH_AMODE=y
+CONFIG_S390_EXEC_PROTECT=y
+CONFIG_AFIUCV=m
+CONFIG_S390_PRNG=m
+
+CONFIG_S390_VMUR=m
+
+# CONFIG_THERMAL is not set
+
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_CTCM=m
+CONFIG_QETH_L2=m
+CONFIG_QETH_L3=m
+CONFIG_CRYPTO_SHA512_S390=m
+CONFIG_VIRTUALIZATION=y
+CONFIG_KVM=m
+CONFIG_S390_GUEST=y
+
+
+CONFIG_MEMORY_HOTPLUG=y
+CONFIG_MEMORY_HOTREMOVE=y
+CONFIG_CHSC_SCH=m
+
+# drivers/isdn/hardware/mISDN/hfcmulti.c:5255:2: error: #error "not running on big endian machines now"
+# CONFIG_MISDN_HFCMULTI is not set
+
+CONFIG_HVC_IUCV=y
+
+CONFIG_RCU_FANOUT=64
+
+CONFIG_SECCOMP=y
+
+CONFIG_PM=y
+CONFIG_HIBERNATION=y
+CONFIG_PM_STD_PARTITION="/dev/jokes"
+
+CONFIG_PERF_COUNTERS=y
+CONFIG_PERF_EVENTS=y
+CONFIG_EVENT_PROFILE=y
+
diff --git a/config-sparc64-generic b/config-sparc64-generic
new file mode 100644
index 0000000..fc09d14
--- /dev/null
+++ b/config-sparc64-generic
@@ -0,0 +1,196 @@
+CONFIG_SMP=y
+CONFIG_SPARC=y
+CONFIG_SPARC64=y
+CONFIG_SECCOMP=y
+CONFIG_HZ_100=y
+# CONFIG_HZ_1000 is not set
+CONFIG_HZ=100
+
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_TABLE=m
+CONFIG_CPU_FREQ_DEBUG=y
+# CONFIG_CPU_FREQ_STAT is not set
+# CONFIG_CPU_FREQ_STAT_DETAILS is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=m
+CONFIG_CPU_FREQ_GOV_ONDEMAND=m
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
+CONFIG_US3_FREQ=m
+CONFIG_US2E_FREQ=m
+
+CONFIG_SUN_LDOMS=y
+CONFIG_SCHED_SMT=y
+CONFIG_SCHED_MC=y
+CONFIG_64BIT=y
+# CONFIG_BBC_I2C is not set
+CONFIG_HUGETLB_PAGE_SIZE_4MB=y
+# CONFIG_HUGETLB_PAGE_SIZE_512K is not set
+# CONFIG_HUGETLB_PAGE_SIZE_64K is not set
+CONFIG_NR_CPUS=256
+CONFIG_US3_FREQ=m
+CONFIG_US2E_FREQ=m
+CONFIG_SUN_OPENPROMFS=m
+CONFIG_COMPAT=y
+CONFIG_UID16=y
+CONFIG_BINFMT_ELF32=y
+CONFIG_ENVCTRL=m
+CONFIG_DISPLAY7SEG=m
+CONFIG_WATCHDOG_CP1XXX=m
+CONFIG_WATCHDOG_RIO=m
+# CONFIG_CMDLINE_BOOL is not set
+# CONFIG_PREVENT_FIRMWARE_BUILD is not set
+# CONFIG_PARPORT is not set
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_LIRC_PARALLEL is not set
+# CONFIG_I2C_NFORCE2 is not set
+# CONFIG_I2C_PARPORT_LIGHT is not set
+# CONFIG_I2C_SIMTEC is not set
+# CONFIG_I2C_VOODOO3 is not set
+CONFIG_I2C_ALI1535=m
+# CONFIG_VGASTATE is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_BW2 is not set
+CONFIG_FB_CG3=y
+CONFIG_FB_CG6=y
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_RADEON is not set
+CONFIG_FB_ATY=y
+# CONFIG_FB_S3 is not set
+# CONFIG_FB_SAVAGE is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_TRIDENT is not set
+CONFIG_FB_SBUS=y
+CONFIG_FB_FFB=y
+# CONFIG_FB_TCX is not set
+# CONFIG_FB_CG14 is not set
+CONFIG_FB_PM2=y
+CONFIG_FB_P9100=y
+# CONFIG_FB_LEO is not set
+CONFIG_FB_XVR500=y
+CONFIG_FB_XVR2500=y
+# CONFIG_VGASTATE is not set
+# CONFIG_FB_DDC is not set
+# CONFIG_FB_CIRRUS is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_KYRO is not set
+# CONFIG_AGP is not set
+# CONFIG_DRM_NOUVEAU is not set
+# CONFIG_MDA_CONSOLE is not set
+CONFIG_FONTS=y
+# CONFIG_FONT_8x8 is not set
+# CONFIG_FONT_8x16 is not set
+# CONFIG_FONT_7x14 is not set
+# CONFIG_FONT_10x18 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+CONFIG_FONT_SUN8x16=y
+CONFIG_FONT_SUN12x22=y
+# CONFIG_LOGO_LINUX_CLUT224 is not set
+# CONFIG_SERIAL_8250 is not set
+CONFIG_SERIAL_SUNZILOG=y
+CONFIG_SERIAL_SUNZILOG_CONSOLE=y
+CONFIG_SERIAL_SUNSU=y
+CONFIG_SERIAL_SUNSU_CONSOLE=y
+CONFIG_SERIAL_SUNSAB=y
+CONFIG_SERIAL_SUNSAB_CONSOLE=y
+CONFIG_SERIAL_SUNHV=y
+CONFIG_SUN_OPENPROMIO=y
+CONFIG_OBP_FLASH=m
+# CONFIG_SERIO_SERPORT is not set
+CONFIG_BLK_DEV_FD=y
+CONFIG_SUNVDC=m
+CONFIG_SUNVNET=m
+# CONFIG_BLK_DEV_AEC62XX is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_PDC202XX_OLD is not set
+# CONFIG_BLK_DEV_PDC202XX_NEW is not set
+# CONFIG_BLK_DEV_SIIMAGE is not set
+# CONFIG_BLK_DEV_SLC90E66 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+CONFIG_SCSI_QLOGICPTI=m
+CONFIG_SCSI_SUNESP=m
+CONFIG_SUNLANCE=m
+CONFIG_SUNBMAC=m
+CONFIG_SUNQE=m
+# CONFIG_DM9102 is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_R8169 is not set
+CONFIG_ATM_FORE200E_USE_TASKLET=y
+CONFIG_ATM_FORE200E_DEBUG=0
+CONFIG_ATM_FORE200E_TX_RETRY=16
+# CONFIG_DRM_TDFX is not set
+CONFIG_KEYBOARD_ATKBD=y
+CONFIG_KEYBOARD_SUNKBD=y
+# CONFIG_INPUT_PCSPKR is not set
+CONFIG_INPUT_SPARCSPKR=m
+# CONFIG_SOUND_PRIME is not set
+# CONFIG_SND_SUN_AMD7930 is not set
+CONFIG_SND_SUN_CS4231=m
+# CONFIG_SND_SUN_DBRI is not set
+CONFIG_PARPORT_SUNBPP=m
+CONFIG_LOGO_SUN_CLUT224=y
+CONFIG_MTD_SUN_UFLASH=m
+CONFIG_MYRI_SBUS=m
+# CONFIG_SGI_IOC4 is not set
+# CONFIG_VIDEO_ZORAN is not set
+# CONFIG_VIDEO_STRADIS is not set
+# CONFIG_IEEE1394_SBP2 is not set
+# CONFIG_USB_NET2280 is not set
+# CONFIG_DEBUG_BUGVERBOSE is not set
+# CONFIG_DEBUG_DCFLUSH is not set
+# CONFIG_DEBUG_BOOTMEM is not set
+# CONFIG_DEBUG_LOCK_ALLOC is not set
+# CONFIG_PROVE_LOCKING is not set
+# CONFIG_LOCK_STAT is not set
+# CONFIG_LOCKDEP is not set
+# CONFIG_STACK_DEBUG is not set
+
+CONFIG_SPARSEMEM_VMEMMAP=y
+
+# CONFIG_THERMAL is not set
+
+CONFIG_FRAME_WARN=2048
+
+CONFIG_NUMA=y
+
+CONFIG_SND_SPARC=y
+
+CONFIG_HW_RANDOM_N2RNG=m
+
+# drivers/isdn/hardware/mISDN/hfcmulti.c:5255:2: error: #error "not running on big endian machines now"
+# CONFIG_MISDN_HFCMULTI is not set
+
+CONFIG_US3_MC=y
+CONFIG_SENSORS_ULTRA45=m
+CONFIG_LEDS_SUNFIRE=m
+CONFIG_TADPOLE_TS102_UCTRL=m
+
+CONFIG_RCU_FANOUT=64
+
+CONFIG_LIRC_ENE0100=m
+# CONFIG_BATTERY_DS2782 is not set
+CONFIG_USB_GSPCA_SN9C20X=m
+CONFIG_USB_GSPCA_SN9C20X_EVDEV=y
+CONFIG_LSM_MMAP_MIN_ADDR=65536
+
+CONFIG_PERF_COUNTERS=y
+CONFIG_PERF_EVENTS=y
+CONFIG_EVENT_PROFILE=y
diff --git a/config-x86-generic b/config-x86-generic
new file mode 100644
index 0000000..e0f1d1e
--- /dev/null
+++ b/config-x86-generic
@@ -0,0 +1,476 @@
+CONFIG_UID16=y
+# CONFIG_64BIT is not set
+# CONFIG_KERNEL_LZMA is not set
+
+#
+# Processor type and features
+#
+#
+# Enable summit and co via the generic arch
+#
+CONFIG_X86_EXTENDED_PLATFORM=y
+CONFIG_X86_32_NON_STANDARD=y
+
+# CONFIG_X86_ELAN is not set
+# CONFIG_X86_NUMAQ is not set
+# CONFIG_X86_SUMMIT is not set
+CONFIG_X86_BIGSMP=y
+# CONFIG_X86_VISWS is not set
+# CONFIG_X86_RDC321X is not set
+# CONFIG_X86_ES7000 is not set
+# CONFIG_M386 is not set
+# CONFIG_M486 is not set
+# CONFIG_M586 is not set
+# CONFIG_M586TSC is not set
+# CONFIG_M586MMX is not set
+CONFIG_M686=y
+# CONFIG_MPENTIUMII is not set
+# CONFIG_MPENTIUMIII is not set
+# CONFIG_MPENTIUMM is not set
+# CONFIG_MPENTIUM4 is not set
+# CONFIG_MK6 is not set
+# CONFIG_MK7 is not set
+# CONFIG_MK8 is not set
+# CONFIG_MCRUSOE is not set
+# CONFIG_MWINCHIPC6 is not set
+# CONFIG_MWINCHIP3D is not set
+# CONFIG_MCYRIXIII is not set
+# CONFIG_MVIAC3_2 is not set
+CONFIG_SMP=y
+CONFIG_NR_CPUS=32
+CONFIG_X86_GENERIC=y
+CONFIG_X86_CMPXCHG=y
+CONFIG_X86_L1_CACHE_SHIFT=7
+CONFIG_RWSEM_XCHGADD_ALGORITHM=y
+CONFIG_X86_PPRO_FENCE=y
+CONFIG_X86_WP_WORKS_OK=y
+CONFIG_X86_INVLPG=y
+CONFIG_X86_BSWAP=y
+CONFIG_X86_POPAD_OK=y
+CONFIG_X86_INTEL_USERCOPY=y
+CONFIG_X86_USE_PPRO_CHECKSUM=y
+CONFIG_HPET=y
+CONFIG_HPET_TIMER=y
+# CONFIG_HPET_MMAP is not set
+CONFIG_X86_LOCAL_APIC=y
+CONFIG_X86_IO_APIC=y
+CONFIG_X86_TSC=y
+CONFIG_X86_MCE=y
+CONFIG_TOSHIBA=m
+CONFIG_I8K=m
+CONFIG_SONYPI=m
+CONFIG_SONYPI_COMPAT=y
+CONFIG_MICROCODE=m
+CONFIG_X86_MSR=y
+CONFIG_X86_CPUID=y
+# CONFIG_X86_CPU_DEBUG is not set
+CONFIG_EDD=m
+# CONFIG_EDD_OFF is not set
+# CONFIG_NUMA is not set
+
+# CONFIG_NOHIGHMEM is not set
+CONFIG_HIGHMEM4G=y
+# CONFIG_HIGHMEM64G is not set
+CONFIG_HIGHMEM=y
+CONFIG_HIGHPTE=y
+
+# CONFIG_MATH_EMULATION is not set
+CONFIG_MTRR=y
+CONFIG_X86_PAT=y
+CONFIG_X86_PM_TIMER=y
+
+CONFIG_EFI=y
+CONFIG_EFI_VARS=y
+CONFIG_EFI_PCDP=y
+CONFIG_FB_EFI=y
+# CONFIG_FB_N411 is not set
+
+CONFIG_DMAR=y
+CONFIG_DMAR_BROKEN_GFX_WA=y
+CONFIG_DMAR_FLOPPY_WA=y
+CONFIG_DMAR_DEFAULT_ON=y
+
+CONFIG_FB_GEODE=y
+CONFIG_FB_GEODE_LX=y
+CONFIG_FB_GEODE_GX=y
+# CONFIG_FB_GEODE_GX1 is not set
+
+# CONFIG_PCI_GOBIOS is not set
+# CONFIG_PCI_GODIRECT is not set
+# CONFIG_PCI_GOMMCONFIG is not set
+CONFIG_PCI_GOANY=y
+
+#
+# x86 specific drivers
+#
+CONFIG_PCMCIA_FDOMAIN=m
+CONFIG_SCSI_FUTURE_DOMAIN=m
+CONFIG_SCSI_ADVANSYS=m
+
+CONFIG_CC_STACKPROTECTOR=y
+
+CONFIG_SECCOMP=y
+
+CONFIG_CAPI_EICON=y
+
+CONFIG_I2O=m
+CONFIG_I2O_BLOCK=m
+CONFIG_I2O_SCSI=m
+CONFIG_I2O_PROC=m
+CONFIG_I2O_CONFIG=y
+CONFIG_I2O_EXT_ADAPTEC=y
+CONFIG_I2O_EXT_ADAPTEC_DMA64=y
+CONFIG_I2O_CONFIG_OLD_IOCTL=y
+CONFIG_I2O_BUS=m
+
+#
+# APM (Advanced Power Management) BIOS Support
+#
+CONFIG_APM=y
+# CONFIG_APM_IGNORE_USER_SUSPEND is not set
+# CONFIG_APM_DO_ENABLE is not set
+CONFIG_APM_CPU_IDLE=y
+# CONFIG_APM_DISPLAY_BLANK is not set
+# CONFIG_APM_ALLOW_INTS is not set
+
+#
+# Kernel debugging
+#
+CONFIG_X86_MPPARSE=y
+
+CONFIG_ACPI=y
+CONFIG_ACPI_AC=y
+# CONFIG_ACPI_ASUS is not set
+CONFIG_ACPI_PROCFS_POWER=y
+CONFIG_ACPI_SYSFS_POWER=y
+CONFIG_ACPI_BATTERY=y
+CONFIG_ACPI_BLACKLIST_YEAR=1999
+CONFIG_ACPI_BUTTON=y
+CONFIG_ACPI_CONTAINER=m
+CONFIG_ACPI_DOCK=y
+CONFIG_ACPI_FAN=y
+CONFIG_ACPI_NUMA=y
+CONFIG_ACPI_PROCESSOR=y
+CONFIG_ACPI_POWER=y
+CONFIG_ACPI_PROCFS=y
+CONFIG_ACPI_SBS=m
+CONFIG_ACPI_SLEEP=y
+CONFIG_ACPI_THERMAL=y
+CONFIG_TOPSTAR_LAPTOP=m
+CONFIG_ACPI_TOSHIBA=m
+CONFIG_ACPI_VIDEO=m
+# Disable in F9.
+CONFIG_ACPI_PROC_EVENT=y
+CONFIG_PNPACPI=y
+CONFIG_ACPI_POWER_METER=m
+CONFIG_ACPI_PROCESSOR_AGGREGATOR=m
+
+#
+# CPUFreq processor drivers
+#
+CONFIG_CPU_FREQ=y
+CONFIG_CPU_FREQ_DEBUG=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=m
+CONFIG_CPU_FREQ_GOV_ONDEMAND=m
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
+CONFIG_CPU_FREQ_TABLE=y
+CONFIG_CPU_FREQ_STAT=m
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+
+CONFIG_X86_ACPI_CPUFREQ=m
+# CONFIG_X86_POWERNOW_K6 is not set
+CONFIG_X86_POWERNOW_K7=y
+CONFIG_X86_POWERNOW_K8=m
+# CONFIG_X86_GX_SUSPMOD is not set
+# CONFIG_X86_SPEEDSTEP_CENTRINO is not set
+CONFIG_X86_SPEEDSTEP_ICH=y
+CONFIG_X86_SPEEDSTEP_SMI=y
+CONFIG_X86_SPEEDSTEP_LIB=y
+# CONFIG_X86_SPEEDSTEP_RELAXED_CAP_CHECK is not set
+CONFIG_X86_P4_CLOCKMOD=m
+CONFIG_X86_LONGRUN=y
+# CONFIG_X86_LONGHAUL is not set
+# CONFIG_X86_CPUFREQ_NFORCE2 is not set
+# e_powersaver is dangerous
+# CONFIG_X86_E_POWERSAVER is not set
+
+CONFIG_X86_HT=y
+CONFIG_X86_TRAMPOLINE=y
+
+#
+# various x86 specific drivers
+#
+CONFIG_NVRAM=y
+CONFIG_IBM_ASM=m
+CONFIG_CRYPTO_AES_586=m
+CONFIG_CRYPTO_TWOFISH_586=m
+CONFIG_CRYPTO_DEV_PADLOCK=m
+CONFIG_CRYPTO_DEV_PADLOCK_AES=m
+CONFIG_CRYPTO_DEV_PADLOCK_SHA=m
+
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_SCHED_SMT=y
+CONFIG_SUSPEND=y
+CONFIG_HIBERNATION=y
+CONFIG_PM_STD_PARTITION=""
+
+CONFIG_DEBUG_RODATA=y
+CONFIG_DEBUG_STACKOVERFLOW=y
+# CONFIG_4KSTACKS is not set
+CONFIG_DEBUG_NMI_TIMEOUT=5
+
+CONFIG_PCI_DIRECT=y
+CONFIG_PCI_MMCONFIG=y
+CONFIG_PCI_BIOS=y
+
+CONFIG_HOTPLUG_PCI=y
+CONFIG_HOTPLUG_PCI_COMPAQ=m
+# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set
+CONFIG_HOTPLUG_PCI_IBM=m
+# CONFIG_HOTPLUG_PCI_CPCI is not set
+# SHPC has half-arsed PCI probing, which makes it load on too many systems
+# CONFIG_HOTPLUG_PCI_SHPC is not set
+CONFIG_PM=y
+
+CONFIG_IPW2100=m
+CONFIG_IPW2100_MONITOR=y
+CONFIG_IPW2200=m
+CONFIG_IPW2200_MONITOR=y
+CONFIG_IPW2200_RADIOTAP=y
+CONFIG_IPW2200_PROMISCUOUS=y
+CONFIG_IPW2200_QOS=y
+
+CONFIG_BLK_DEV_AMD74XX=y
+
+CONFIG_I2C_ALI1535=m
+CONFIG_I2C_ALI15X3=m
+CONFIG_I2C_ALI1563=m
+CONFIG_I2C_AMD756=m
+CONFIG_I2C_AMD756_S4882=m
+CONFIG_I2C_AMD8111=m
+CONFIG_I2C_I801=m
+CONFIG_I2C_ISCH=m
+CONFIG_I2C_NFORCE2=m
+CONFIG_I2C_NFORCE2_S4985=m
+CONFIG_I2C_PIIX4=m
+CONFIG_I2C_SIS5595=m
+CONFIG_I2C_SIS630=m
+CONFIG_I2C_SIS96X=m
+
+CONFIG_I2C_VIA=m
+CONFIG_I2C_VIAPRO=m
+CONFIG_I2C_VOODOO3=m
+
+CONFIG_SCx200_ACB=m
+
+# CONFIG_X86_REBOOTFIXUPS is not set
+
+CONFIG_DELL_RBU=m
+CONFIG_DCDBAS=m
+
+CONFIG_PC8736x_GPIO=m
+# CONFIG_NSC_GPIO is not set
+CONFIG_CS5535_GPIO=m
+
+CONFIG_EDAC=y
+# CONFIG_EDAC_DEBUG is not set
+CONFIG_EDAC_MM_EDAC=m
+CONFIG_EDAC_AMD76X=m
+CONFIG_EDAC_E7XXX=m
+CONFIG_EDAC_E752X=m
+CONFIG_EDAC_I82860=m
+CONFIG_EDAC_I82875P=m
+CONFIG_EDAC_I82975X=m
+CONFIG_EDAC_I3000=m
+CONFIG_EDAC_I5000=m
+CONFIG_EDAC_I5100=m
+CONFIG_EDAC_I5400=m
+CONFIG_EDAC_R82600=m
+CONFIG_EDAC_AMD8131=m
+CONFIG_EDAC_AMD8111=m
+
+CONFIG_SCHED_MC=y
+
+CONFIG_SND_ISA=y
+CONFIG_SND_ES18XX=m
+
+CONFIG_TCG_INFINEON=m
+
+CONFIG_HW_RANDOM_INTEL=m
+CONFIG_HW_RANDOM_AMD=m
+CONFIG_HW_RANDOM_GEODE=m
+CONFIG_HW_RANDOM_VIA=m
+
+
+# CONFIG_COMPAT_VDSO is not set
+
+# CONFIG_SGI_IOC4 is not set
+
+CONFIG_X86_PLATFORM_DEVICES=y
+CONFIG_ASUS_LAPTOP=m
+CONFIG_COMPAL_LAPTOP=m
+CONFIG_EEEPC_LAPTOP=m
+CONFIG_FUJITSU_LAPTOP=m
+# CONFIG_FUJITSU_LAPTOP_DEBUG is not set
+CONFIG_MSI_LAPTOP=m
+CONFIG_SONY_LAPTOP=m
+CONFIG_DELL_LAPTOP=m
+CONFIG_ACPI_WMI=m
+CONFIG_ACER_WMI=m
+CONFIG_TC1100_WMI=m
+CONFIG_HP_WMI=m
+CONFIG_DELL_WMI=m
+
+# CONFIG_SMSC37B787_WDT is not set
+CONFIG_W83697HF_WDT=m
+CONFIG_IB700_WDT=m
+
+CONFIG_RELOCATABLE=y
+CONFIG_PHYSICAL_ALIGN=0x400000
+CONFIG_PHYSICAL_START=0x400000
+CONFIG_CRASH_DUMP=y
+# CONFIG_KEXEC_JUMP is not set
+CONFIG_PROC_VMCORE=y
+CONFIG_CRASH=m
+
+CONFIG_CRYPTO_DEV_GEODE=m
+
+CONFIG_VIDEO_CAFE_CCIC=m
+
+CONFIG_VIRTUALIZATION=y
+CONFIG_KVM=m
+CONFIG_KVM_INTEL=m
+CONFIG_KVM_AMD=m
+CONFIG_LGUEST=m
+
+CONFIG_PARAVIRT_GUEST=y
+CONFIG_PARAVIRT=y
+# CONFIG_PARAVIRT_DEBUG is not set
+
+# PARAVIRT_SPINLOCKS has a 5% perf hit
+# CONFIG_PARAVIRT_SPINLOCKS is not set
+CONFIG_KVM_CLOCK=y
+CONFIG_KVM_GUEST=y
+CONFIG_LGUEST_GUEST=y
+CONFIG_VMI=y
+
+CONFIG_XEN=y
+CONFIG_XEN_MAX_DOMAIN_MEMORY=8
+CONFIG_XEN_BALLOON=y
+CONFIG_XEN_SCRUB_PAGES=y
+CONFIG_XEN_SAVE_RESTORE=y
+CONFIG_HVC_XEN=y
+CONFIG_XEN_FBDEV_FRONTEND=y
+CONFIG_XEN_KBDDEV_FRONTEND=y
+CONFIG_XEN_BLKDEV_FRONTEND=m
+CONFIG_XEN_NETDEV_FRONTEND=m
+CONFIG_XENFS=m
+CONFIG_XEN_COMPAT_XENFS=y
+
+CONFIG_MTD_ESB2ROM=m
+CONFIG_MTD_CK804XROM=m
+CONFIG_MTD_NAND_CAFE=m
+
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_CPU_IDLE=y
+# CONFIG_CPU_IDLE_GOV_LADDER is not set
+CONFIG_CPU_IDLE_GOV_MENU=y
+
+CONFIG_THINKPAD_ACPI=m
+# CONFIG_THINKPAD_ACPI_DEBUG is not set
+# CONFIG_THINKPAD_ACPI_DEBUGFACILITIES is not set
+CONFIG_THINKPAD_ACPI_HOTKEY_POLL=y
+CONFIG_THINKPAD_ACPI_VIDEO=y
+# CONFIG_THINKPAD_ACPI_UNSAFE_LEDS is not set
+
+CONFIG_MACINTOSH_DRIVERS=y
+
+CONFIG_DMIID=y
+CONFIG_ISCSI_IBFT_FIND=y
+CONFIG_ISCSI_IBFT=m
+
+CONFIG_DMADEVICES=y
+CONFIG_INTEL_IOATDMA=m
+
+CONFIG_SENSORS_I5K_AMB=m
+
+# CONFIG_CPA_DEBUG is not set
+# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set
+
+CONFIG_HP_WATCHDOG=m
+
+CONFIG_OLPC=y
+CONFIG_BATTERY_OLPC=y
+CONFIG_MOUSE_PS2_OLPC=y
+
+CONFIG_STRICT_DEVMEM=y
+
+# CONFIG_MEMTEST is not set
+# CONFIG_MAXSMP is not set
+CONFIG_MTRR_SANITIZER=y
+CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT=1
+CONFIG_MTRR_SANITIZER_SPARE_REG_NR_DEFAULT=1
+CONFIG_SYSPROF_TRACER=y
+
+# CONFIG_X86_VERBOSE_BOOTUP is not set
+# CONFIG_MMIOTRACE_TEST is not set
+
+# CONFIG_DEBUG_PER_CPU_MAPS is not set
+
+CONFIG_HP_ILO=m
+
+CONFIG_BACKLIGHT_MBP_NVIDIA=m
+
+CONFIG_OPROFILE_IBS=y
+CONFIG_MICROCODE_INTEL=y
+CONFIG_MICROCODE_AMD=y
+
+# CONFIG_X86_CHECK_BIOS_CORRUPTION is not set
+CONFIG_X86_RESERVE_LOW_64K=y
+
+# CONFIG_CMDLINE_BOOL is not set
+
+CONFIG_PANASONIC_LAPTOP=m
+
+CONFIG_XEN_DEBUG_FS=y
+CONFIG_X86_PTRACE_BTS=y
+
+CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS=y
+
+CONFIG_POWER_TRACER=y
+CONFIG_HW_BRANCH_TRACER=y
+
+# CONFIG_SPARSE_IRQ is not set
+
+CONFIG_RCU_FANOUT=32
+
+# CONFIG_IOMMU_STRESS is not set
+
+CONFIG_PERF_COUNTERS=y
+CONFIG_PERF_EVENTS=y
+CONFIG_EVENT_PROFILE=y
+
+CONFIG_X86_MCE_INTEL=y
+CONFIG_X86_MCE_AMD=y
+# CONFIG_X86_ANCIENT_MCE is not set
+# CONFIG_X86_MCE_INJECT is not set
+
+# CONFIG_X86_MRST is not set
+CONFIG_SFI=y
+
+CONFIG_INPUT_WINBOND_CIR=m
+CONFIG_I2C_SCMI=m
+CONFIG_SBC_FITPC2_WATCHDOG=m
+CONFIG_EDAC_I3200=m
+CONFIG_EDAC_DECODE_MCE=m
+
+CONFIG_GPIO_LANGWELL=y
+
+# CONFIG_INTEL_TXT is not set
+
+CONFIG_ACERHDF=m
diff --git a/config-x86_64-generic b/config-x86_64-generic
new file mode 100644
index 0000000..175f57b
--- /dev/null
+++ b/config-x86_64-generic
@@ -0,0 +1,387 @@
+CONFIG_64BIT=y
+CONFIG_UID16=y
+# CONFIG_KERNEL_LZMA is not set
+
+# CONFIG_MK8 is not set
+# CONFIG_MPSC is not set
+CONFIG_GENERIC_CPU=y
+CONFIG_X86_EXTENDED_PLATFORM=y
+# CONFIG_X86_VSMP is not set
+# CONFIG_X86_UV is not set
+CONFIG_X86_MSR=y
+CONFIG_X86_CPUID=y
+# CONFIG_X86_CPU_DEBUG is not set
+CONFIG_MTRR=y
+CONFIG_NUMA=y
+CONFIG_K8_NUMA=y
+CONFIG_X86_64_ACPI_NUMA=y
+# CONFIG_NUMA_EMU is not set
+CONFIG_NR_CPUS=256
+CONFIG_X86_POWERNOW_K8=m
+CONFIG_X86_P4_CLOCKMOD=m
+CONFIG_IA32_EMULATION=y
+# CONFIG_IA32_AOUT is not set
+# CONFIG_IOMMU_DEBUG is not set
+CONFIG_DEBUG_RODATA=y
+CONFIG_MICROCODE=m
+CONFIG_SWIOTLB=y
+CONFIG_CALGARY_IOMMU=y
+CONFIG_CALGARY_IOMMU_ENABLED_BY_DEFAULT=y
+CONFIG_X86_PM_TIMER=y
+CONFIG_EDD=m
+# CONFIG_EDD_OFF is not set
+CONFIG_PCI_BIOS=y
+CONFIG_PCI_MMCONFIG=y
+CONFIG_DMAR=y
+CONFIG_DMAR_BROKEN_GFX_WA=y
+CONFIG_DMAR_FLOPPY_WA=y
+CONFIG_DMAR_DEFAULT_ON=y
+
+CONFIG_KEXEC_JUMP=y
+
+CONFIG_EFI=y
+CONFIG_EFI_VARS=y
+CONFIG_EFI_PCDP=y
+CONFIG_FB_EFI=y
+
+CONFIG_I2O=m
+CONFIG_I2O_BLOCK=m
+CONFIG_I2O_SCSI=m
+CONFIG_I2O_PROC=m
+CONFIG_I2O_CONFIG=y
+CONFIG_I2O_EXT_ADAPTEC=y
+CONFIG_I2O_EXT_ADAPTEC_DMA64=y
+CONFIG_I2O_CONFIG_OLD_IOCTL=y
+CONFIG_I2O_BUS=m
+
+CONFIG_SECCOMP=y
+
+CONFIG_CAPI_EICON=y
+
+CONFIG_GENERIC_ISA_DMA=y
+CONFIG_SCHED_SMT=y
+CONFIG_SUSPEND=y
+CONFIG_HIBERNATION=y
+CONFIG_PM_STD_PARTITION=""
+
+CONFIG_CPU_FREQ=y
+# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set
+CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y
+CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
+CONFIG_CPU_FREQ_GOV_POWERSAVE=m
+CONFIG_CPU_FREQ_GOV_USERSPACE=m
+CONFIG_CPU_FREQ_GOV_ONDEMAND=m
+CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m
+CONFIG_CPU_FREQ_TABLE=y
+CONFIG_CPU_FREQ_DEBUG=y
+# CONFIG_X86_SPEEDSTEP_CENTRINO is not set
+CONFIG_X86_ACPI_CPUFREQ=m
+CONFIG_CPU_FREQ_STAT=m
+CONFIG_CPU_FREQ_STAT_DETAILS=y
+
+CONFIG_ACPI=y
+CONFIG_ACPI_AC=y
+# CONFIG_ACPI_ASUS is not set
+CONFIG_ACPI_PROCFS_POWER=y
+CONFIG_ACPI_SYSFS_POWER=y
+CONFIG_ACPI_BATTERY=y
+CONFIG_ACPI_BLACKLIST_YEAR=0
+CONFIG_ACPI_BUTTON=y
+CONFIG_ACPI_CONTAINER=m
+CONFIG_ACPI_DOCK=y
+CONFIG_ACPI_FAN=y
+CONFIG_ACPI_HOTPLUG_MEMORY=m
+CONFIG_ACPI_NUMA=y
+CONFIG_ACPI_PROCESSOR=y
+CONFIG_ACPI_PROCFS=y
+CONFIG_ACPI_SBS=m
+CONFIG_ACPI_SLEEP=y
+CONFIG_ACPI_THERMAL=y
+CONFIG_ACPI_TOSHIBA=m
+CONFIG_ACPI_POWER=y
+CONFIG_ACPI_VIDEO=m
+# Disable in F9.
+CONFIG_ACPI_PROC_EVENT=y
+CONFIG_ACPI_POWER_METER=m
+CONFIG_ACPI_PROCESSOR_AGGREGATOR=m
+
+CONFIG_X86_PLATFORM_DEVICES=y
+CONFIG_ASUS_LAPTOP=m
+CONFIG_COMPAL_LAPTOP=m
+CONFIG_FUJITSU_LAPTOP=m
+# CONFIG_FUJITSU_LAPTOP_DEBUG is not set
+CONFIG_MSI_LAPTOP=m
+CONFIG_SONY_LAPTOP=m
+CONFIG_SONYPI_COMPAT=y
+CONFIG_EEEPC_LAPTOP=m
+CONFIG_DELL_LAPTOP=m
+CONFIG_ACPI_WMI=m
+CONFIG_ACER_WMI=m
+CONFIG_HP_WMI=m
+CONFIG_DELL_WMI=m
+
+CONFIG_THINKPAD_ACPI=m
+# CONFIG_THINKPAD_ACPI_DEBUG is not set
+# CONFIG_THINKPAD_ACPI_DEBUGFACILITIES is not set
+CONFIG_THINKPAD_ACPI_HOTKEY_POLL=y
+CONFIG_THINKPAD_ACPI_VIDEO=y
+# CONFIG_THINKPAD_ACPI_UNSAFE_LEDS is not set
+
+CONFIG_HOTPLUG_PCI=y
+CONFIG_HOTPLUG_PCI_COMPAQ=m
+# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set
+CONFIG_HOTPLUG_PCI_IBM=m
+# CONFIG_HOTPLUG_PCI_CPCI is not set
+# SHPC has half-arsed PCI probing, which makes it load on too many systems
+CONFIG_HOTPLUG_PCI_SHPC=m
+
+CONFIG_HPET=y
+# CONFIG_HPET_MMAP is not set
+CONFIG_PM=y
+
+CONFIG_IPW2100=m
+CONFIG_IPW2100_MONITOR=y
+CONFIG_IPW2200=m
+CONFIG_IPW2200_MONITOR=y
+CONFIG_IPW2200_RADIOTAP=y
+CONFIG_IPW2200_PROMISCUOUS=y
+CONFIG_IPW2200_QOS=y
+
+CONFIG_PNP=y
+CONFIG_PNPACPI=y
+
+CONFIG_BLK_DEV_AMD74XX=y
+CONFIG_CRYPTO_DEV_PADLOCK=m
+CONFIG_CRYPTO_DEV_PADLOCK_AES=m
+CONFIG_CRYPTO_DEV_PADLOCK_SHA=m
+# CONFIG_CRYPTO_AES is not set
+CONFIG_CRYPTO_AES_X86_64=m
+# CONFIG_CRYPTO_TWOFISH is not set
+CONFIG_CRYPTO_TWOFISH_X86_64=m
+# CONFIG_CRYPTO_SALSA20 is not set
+CONFIG_CRYPTO_SALSA20_X86_64=m
+CONFIG_CRYPTO_AES_NI_INTEL=m
+
+CONFIG_X86_MCE=y
+CONFIG_X86_MCE_INTEL=y
+CONFIG_X86_MCE_AMD=y
+
+# CONFIG_I2C_ALI1535 is not set
+# CONFIG_I2C_ALI1563 is not set
+# CONFIG_I2C_ALI15X3 is not set
+CONFIG_I2C_AMD756=m
+CONFIG_I2C_AMD756_S4882=m
+CONFIG_I2C_AMD8111=m
+CONFIG_I2C_I801=m
+CONFIG_I2C_ISCH=m
+CONFIG_I2C_NFORCE2_S4985=m
+CONFIG_I2C_PIIX4=m
+# CONFIG_I2C_SIS5595 is not set
+# CONFIG_I2C_SIS630 is not set
+
+CONFIG_I2C_SIS96X=m
+CONFIG_I2C_VIA=m
+CONFIG_I2C_VIAPRO=m
+
+CONFIG_DELL_RBU=m
+CONFIG_DCDBAS=m
+
+CONFIG_NVRAM=y
+
+CONFIG_EDAC=y
+# CONFIG_EDAC_DEBUG is not set
+CONFIG_EDAC_MM_EDAC=m
+CONFIG_EDAC_AMD76X=m
+CONFIG_EDAC_E7XXX=m
+CONFIG_EDAC_E752X=m
+CONFIG_EDAC_I5000=m
+CONFIG_EDAC_I5100=m
+CONFIG_EDAC_I5400=m
+CONFIG_EDAC_I82875P=m
+CONFIG_EDAC_I82860=m
+CONFIG_EDAC_I82975X=m
+CONFIG_EDAC_R82600=m
+CONFIG_EDAC_AMD8131=m
+CONFIG_EDAC_AMD8111=m
+CONFIG_EDAC_AMD64=m
+# CONFIG_EDAC_AMD64_ERROR_INJECTION is not set
+CONFIG_EDAC_DECODE_MCE=m
+
+CONFIG_SCHED_MC=y
+
+CONFIG_TCG_INFINEON=m
+
+CONFIG_HW_RANDOM_INTEL=m
+CONFIG_HW_RANDOM_AMD=m
+CONFIG_HW_RANDOM_VIA=m
+
+# CONFIG_HW_RANDOM_GEODE is not set
+
+
+CONFIG_DEBUG_STACKOVERFLOW=y
+CONFIG_DEBUG_NMI_TIMEOUT=5
+
+# CONFIG_PC8736x_GPIO is not set
+
+# CONFIG_DISCONTIGMEM_MANUAL is not set
+CONFIG_SPARSEMEM_MANUAL=y
+CONFIG_SPARSEMEM=y
+CONFIG_HAVE_MEMORY_PRESENT=y
+CONFIG_SPARSEMEM_EXTREME=y
+CONFIG_SPARSEMEM_VMEMMAP=y
+CONFIG_MEMORY_HOTPLUG=y
+CONFIG_MEMORY_HOTREMOVE=y
+
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+# CONFIG_BLK_DEV_TRIFLEX is not set
+# CONFIG_BLK_DEV_CS5520 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_CS5535 is not set
+
+CONFIG_CC_STACKPROTECTOR=y
+
+CONFIG_SGI_IOC4=m
+CONFIG_SGI_XP=m
+CONFIG_SGI_GRU=m
+# CONFIG_SGI_GRU_DEBUG is not set
+
+# CONFIG_SMSC37B787_WDT is not set
+CONFIG_W83697HF_WDT=m
+
+# CONFIG_VIDEO_CAFE_CCIC is not set
+
+CONFIG_MTD_ESB2ROM=m
+CONFIG_MTD_CK804XROM=m
+
+CONFIG_RELOCATABLE=y
+CONFIG_MACINTOSH_DRIVERS=y
+
+CONFIG_CRASH_DUMP=y
+CONFIG_PHYSICAL_START=0x1000000
+CONFIG_PROC_VMCORE=y
+CONFIG_CRASH=m
+
+CONFIG_DMIID=y
+CONFIG_ISCSI_IBFT_FIND=y
+CONFIG_ISCSI_IBFT=m
+
+
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_CPU_IDLE=y
+# CONFIG_CPU_IDLE_GOV_LADDER is not set
+CONFIG_CPU_IDLE_GOV_MENU=y
+
+CONFIG_VIRTUALIZATION=y
+CONFIG_KVM=m
+CONFIG_KVM_INTEL=m
+CONFIG_KVM_AMD=m
+
+CONFIG_PARAVIRT_GUEST=y
+CONFIG_PARAVIRT=y
+# CONFIG_PARAVIRT_DEBUG is not set
+# PARAVIRT_SPINLOCKS has a 5% perf hit
+# CONFIG_PARAVIRT_SPINLOCKS is not set
+CONFIG_KVM_CLOCK=y
+CONFIG_KVM_GUEST=y
+
+CONFIG_XEN=y
+CONFIG_XEN_MAX_DOMAIN_MEMORY=32
+CONFIG_XEN_BALLOON=y
+CONFIG_XEN_SCRUB_PAGES=y
+CONFIG_XEN_SAVE_RESTORE=y
+CONFIG_HVC_XEN=y
+CONFIG_XEN_FBDEV_FRONTEND=y
+CONFIG_XEN_KBDDEV_FRONTEND=y
+CONFIG_XEN_BLKDEV_FRONTEND=m
+CONFIG_XEN_NETDEV_FRONTEND=m
+CONFIG_XENFS=m
+CONFIG_XEN_COMPAT_XENFS=y
+CONFIG_XEN_DEV_EVTCHN=m
+CONFIG_XEN_SYS_HYPERVISOR=y
+
+CONFIG_DMADEVICES=y
+CONFIG_INTEL_IOATDMA=m
+
+CONFIG_SENSORS_I5K_AMB=m
+
+# CONFIG_COMPAT_VDSO is not set
+CONFIG_PROVIDE_OHCI1394_DMA_INIT=y
+# CONFIG_DEBUG_PER_CPU_MAPS is not set
+# CONFIG_CPA_DEBUG is not set
+
+CONFIG_HP_WATCHDOG=m
+
+CONFIG_FRAME_WARN=2048
+
+CONFIG_NODES_SHIFT=9
+CONFIG_X86_PAT=y
+# FIXME: These should be 32bit only
+# CONFIG_FB_N411 is not set
+CONFIG_STRICT_DEVMEM=y
+
+CONFIG_DIRECT_GBPAGES=y
+
+# CONFIG_MEMTEST is not set
+CONFIG_AMD_IOMMU=y
+CONFIG_AMD_IOMMU_STATS=y
+# CONFIG_MAXSMP is not set
+CONFIG_MTRR_SANITIZER=y
+CONFIG_MTRR_SANITIZER_ENABLE_DEFAULT=1
+CONFIG_MTRR_SANITIZER_SPARE_REG_NR_DEFAULT=1
+CONFIG_SYSPROF_TRACER=y
+# CONFIG_X86_VERBOSE_BOOTUP is not set
+# CONFIG_MMIOTRACE_TEST is not set
+
+CONFIG_X86_MPPARSE=y
+
+CONFIG_BACKLIGHT_MBP_NVIDIA=m
+
+CONFIG_OPROFILE_IBS=y
+CONFIG_MICROCODE_INTEL=y
+CONFIG_MICROCODE_AMD=y
+
+# CONFIG_X86_CHECK_BIOS_CORRUPTION is not set
+CONFIG_X86_RESERVE_LOW_64K=y
+
+# CONFIG_CMDLINE_BOOL is not set
+
+CONFIG_PANASONIC_LAPTOP=m
+
+CONFIG_XEN_DEBUG_FS=y
+CONFIG_X86_PTRACE_BTS=y
+
+CONFIG_I7300_IDLE=m
+CONFIG_INTR_REMAP=y
+
+CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS=y
+
+CONFIG_POWER_TRACER=y
+CONFIG_HW_BRANCH_TRACER=y
+
+CONFIG_X86_X2APIC=y
+CONFIG_SPARSE_IRQ=y
+
+CONFIG_RCU_FANOUT=64
+
+# CONFIG_IOMMU_STRESS is not set
+
+CONFIG_PERF_COUNTERS=y
+CONFIG_PERF_EVENTS=y
+CONFIG_EVENT_PROFILE=y
+
+# CONFIG_X86_MCE_INJECT is not set
+
+CONFIG_SFI=y
+CONFIG_INPUT_WINBOND_CIR=m
+CONFIG_I2C_SCMI=m
+CONFIG_SBC_FITPC2_WATCHDOG=m
+CONFIG_EDAC_I3200=m
+CONFIG_TOPSTAR_LAPTOP=m
+# CONFIG_INTEL_TXT is not set
+CONFIG_GPIO_LANGWELL=y
+
+CONFIG_FUNCTION_GRAPH_TRACER=y
+
+CONFIG_ACERHDF=m
diff --git a/crypto-add-async-hash-testing.patch b/crypto-add-async-hash-testing.patch
new file mode 100644
index 0000000..8df0ad4
--- /dev/null
+++ b/crypto-add-async-hash-testing.patch
@@ -0,0 +1,111 @@
+From e45009229be6a7fae49bdfa3459905668c0b0fb1 Mon Sep 17 00:00:00 2001
+From: David S. Miller <davem@davemloft.net>
+Date: Wed, 19 May 2010 14:12:03 +1000
+Subject: crypto: testmgr - Add testing for async hashing and update/final
+
+Extend testmgr such that it tests async hash algorithms,
+and that for both sync and async hashes it tests both
+->digest() and ->update()/->final() sequences.
+
+Signed-off-by: David S. Miller <davem@davemloft.net>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+---
+ crypto/testmgr.c | 66 +++++++++++++++++++++++++++++++++++++++--------------
+ 1 files changed, 48 insertions(+), 18 deletions(-)
+
+diff --git a/crypto/testmgr.c b/crypto/testmgr.c
+index c494d76..5c8aaa0 100644
+--- a/crypto/testmgr.c
++++ b/crypto/testmgr.c
+@@ -153,8 +153,21 @@ static void testmgr_free_buf(char *buf[XBUFSIZE])
+ free_page((unsigned long)buf[i]);
+ }
+
++static int do_one_async_hash_op(struct ahash_request *req,
++ struct tcrypt_result *tr,
++ int ret)
++{
++ if (ret == -EINPROGRESS || ret == -EBUSY) {
++ ret = wait_for_completion_interruptible(&tr->completion);
++ if (!ret)
++ ret = tr->err;
++ INIT_COMPLETION(tr->completion);
++ }
++ return ret;
++}
++
+ static int test_hash(struct crypto_ahash *tfm, struct hash_testvec *template,
+- unsigned int tcount)
++ unsigned int tcount, bool use_digest)
+ {
+ const char *algo = crypto_tfm_alg_driver_name(crypto_ahash_tfm(tfm));
+ unsigned int i, j, k, temp;
+@@ -206,23 +219,36 @@ static int test_hash(struct crypto_ahash *tfm, struct hash_testvec *template,
+ }
+
+ ahash_request_set_crypt(req, sg, result, template[i].psize);
+- ret = crypto_ahash_digest(req);
+- switch (ret) {
+- case 0:
+- break;
+- case -EINPROGRESS:
+- case -EBUSY:
+- ret = wait_for_completion_interruptible(
+- &tresult.completion);
+- if (!ret && !(ret = tresult.err)) {
+- INIT_COMPLETION(tresult.completion);
+- break;
++ if (use_digest) {
++ ret = do_one_async_hash_op(req, &tresult,
++ crypto_ahash_digest(req));
++ if (ret) {
++ pr_err("alg: hash: digest failed on test %d "
++ "for %s: ret=%d\n", j, algo, -ret);
++ goto out;
++ }
++ } else {
++ ret = do_one_async_hash_op(req, &tresult,
++ crypto_ahash_init(req));
++ if (ret) {
++ pr_err("alt: hash: init failed on test %d "
++ "for %s: ret=%d\n", j, algo, -ret);
++ goto out;
++ }
++ ret = do_one_async_hash_op(req, &tresult,
++ crypto_ahash_update(req));
++ if (ret) {
++ pr_err("alt: hash: update failed on test %d "
++ "for %s: ret=%d\n", j, algo, -ret);
++ goto out;
++ }
++ ret = do_one_async_hash_op(req, &tresult,
++ crypto_ahash_final(req));
++ if (ret) {
++ pr_err("alt: hash: final failed on test %d "
++ "for %s: ret=%d\n", j, algo, -ret);
++ goto out;
+ }
+- /* fall through */
+- default:
+- printk(KERN_ERR "alg: hash: digest failed on test %d "
+- "for %s: ret=%d\n", j, algo, -ret);
+- goto out;
+ }
+
+ if (memcmp(result, template[i].digest,
+@@ -1402,7 +1428,11 @@ static int alg_test_hash(const struct alg_test_desc *desc, const char *driver,
+ return PTR_ERR(tfm);
+ }
+
+- err = test_hash(tfm, desc->suite.hash.vecs, desc->suite.hash.count);
++ err = test_hash(tfm, desc->suite.hash.vecs,
++ desc->suite.hash.count, true);
++ if (!err)
++ err = test_hash(tfm, desc->suite.hash.vecs,
++ desc->suite.hash.count, false);
+
+ crypto_free_ahash(tfm);
+ return err;
+--
+1.7.0.1
+
diff --git a/crypto-testmgr-add-null-test-for-aesni.patch b/crypto-testmgr-add-null-test-for-aesni.patch
new file mode 100644
index 0000000..b38a6f9
--- /dev/null
+++ b/crypto-testmgr-add-null-test-for-aesni.patch
@@ -0,0 +1,138 @@
+From: Youquan, Song <youquan.song@intel.com>
+Date: Wed, 23 Dec 2009 11:45:20 +0000 (+0800)
+Subject: crypto: testmgr - Fix complain about lack test for internal used algorithm
+X-Git-Tag: v2.6.34-rc1~286^2~28
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=863b557a88f8c033f7419fabafef4712a5055f85
+
+crypto: testmgr - Fix complain about lack test for internal used algorithm
+
+When load aesni-intel and ghash_clmulni-intel driver,kernel will complain no
+ test for some internal used algorithm.
+The strange information as following:
+
+alg: No test for __aes-aesni (__driver-aes-aesni)
+alg: No test for __ecb-aes-aesni (__driver-ecb-aes-aesni)
+alg: No test for __cbc-aes-aesni (__driver-cbc-aes-aesni)
+alg: No test for __ecb-aes-aesni (cryptd(__driver-ecb-aes-aesni)
+alg: No test for __ghash (__ghash-pclmulqdqni)
+alg: No test for __ghash (cryptd(__ghash-pclmulqdqni))
+
+This patch add NULL test entries for these algorithm and driver.
+
+Signed-off-by: Youquan, Song <youquan.song@intel.com>
+Signed-off-by: Ying, Huang <ying.huang@intel.com>
+Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
+---
+
+diff --git a/crypto/testmgr.c b/crypto/testmgr.c
+index 7620bfc..c494d76 100644
+--- a/crypto/testmgr.c
++++ b/crypto/testmgr.c
+@@ -1477,9 +1477,54 @@ static int alg_test_cprng(const struct alg_test_desc *desc, const char *driver,
+ return err;
+ }
+
++static int alg_test_null(const struct alg_test_desc *desc,
++ const char *driver, u32 type, u32 mask)
++{
++ return 0;
++}
++
+ /* Please keep this list sorted by algorithm name. */
+ static const struct alg_test_desc alg_test_descs[] = {
+ {
++ .alg = "__driver-cbc-aes-aesni",
++ .test = alg_test_null,
++ .suite = {
++ .cipher = {
++ .enc = {
++ .vecs = NULL,
++ .count = 0
++ },
++ .dec = {
++ .vecs = NULL,
++ .count = 0
++ }
++ }
++ }
++ }, {
++ .alg = "__driver-ecb-aes-aesni",
++ .test = alg_test_null,
++ .suite = {
++ .cipher = {
++ .enc = {
++ .vecs = NULL,
++ .count = 0
++ },
++ .dec = {
++ .vecs = NULL,
++ .count = 0
++ }
++ }
++ }
++ }, {
++ .alg = "__ghash-pclmulqdqni",
++ .test = alg_test_null,
++ .suite = {
++ .hash = {
++ .vecs = NULL,
++ .count = 0
++ }
++ }
++ }, {
+ .alg = "ansi_cprng",
+ .test = alg_test_cprng,
+ .fips_allowed = 1,
+@@ -1623,6 +1668,30 @@ static const struct alg_test_desc alg_test_descs[] = {
+ }
+ }
+ }, {
++ .alg = "cryptd(__driver-ecb-aes-aesni)",
++ .test = alg_test_null,
++ .suite = {
++ .cipher = {
++ .enc = {
++ .vecs = NULL,
++ .count = 0
++ },
++ .dec = {
++ .vecs = NULL,
++ .count = 0
++ }
++ }
++ }
++ }, {
++ .alg = "cryptd(__ghash-pclmulqdqni)",
++ .test = alg_test_null,
++ .suite = {
++ .hash = {
++ .vecs = NULL,
++ .count = 0
++ }
++ }
++ }, {
+ .alg = "ctr(aes)",
+ .test = alg_test_skcipher,
+ .fips_allowed = 1,
+@@ -1669,6 +1738,21 @@ static const struct alg_test_desc alg_test_descs[] = {
+ }
+ }
+ }, {
++ .alg = "ecb(__aes-aesni)",
++ .test = alg_test_null,
++ .suite = {
++ .cipher = {
++ .enc = {
++ .vecs = NULL,
++ .count = 0
++ },
++ .dec = {
++ .vecs = NULL,
++ .count = 0
++ }
++ }
++ }
++ }, {
+ .alg = "ecb(aes)",
+ .test = alg_test_skcipher,
+ .fips_allowed = 1,
diff --git a/crystalhd-2.6.34-staging.patch b/crystalhd-2.6.34-staging.patch
new file mode 100644
index 0000000..671bd39
--- /dev/null
+++ b/crystalhd-2.6.34-staging.patch
@@ -0,0 +1,8287 @@
+Broadcom Crystal HD video decoder driver from upstream staging/linux-next.
+
+Signed-off-by: Jarod Wilson <jarod@redhat.com>
+
+---
+ drivers/staging/Kconfig | 2 +
+ drivers/staging/Makefile | 1 +
+ drivers/staging/crystalhd/Kconfig | 6 +
+ drivers/staging/crystalhd/Makefile | 6 +
+ drivers/staging/crystalhd/TODO | 16 +
+ drivers/staging/crystalhd/bc_dts_defs.h | 498 ++++++
+ drivers/staging/crystalhd/bc_dts_glob_lnx.h | 299 ++++
+ drivers/staging/crystalhd/bc_dts_types.h | 121 ++
+ drivers/staging/crystalhd/bcm_70012_regs.h | 757 +++++++++
+ drivers/staging/crystalhd/crystalhd_cmds.c | 1058 ++++++++++++
+ drivers/staging/crystalhd/crystalhd_cmds.h | 88 +
+ drivers/staging/crystalhd/crystalhd_fw_if.h | 369 ++++
+ drivers/staging/crystalhd/crystalhd_hw.c | 2395 +++++++++++++++++++++++++++
+ drivers/staging/crystalhd/crystalhd_hw.h | 398 +++++
+ drivers/staging/crystalhd/crystalhd_lnx.c | 780 +++++++++
+ drivers/staging/crystalhd/crystalhd_lnx.h | 95 ++
+ drivers/staging/crystalhd/crystalhd_misc.c | 1029 ++++++++++++
+ drivers/staging/crystalhd/crystalhd_misc.h | 229 +++
+ 18 files changed, 8147 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
+index 94eb863..61ec152 100644
+--- a/drivers/staging/Kconfig
++++ b/drivers/staging/Kconfig
+@@ -145,5 +145,7 @@ source "drivers/staging/netwave/Kconfig"
+
+ source "drivers/staging/iio/Kconfig"
+
++source "drivers/staging/crystalhd/Kconfig"
++
+ endif # !STAGING_EXCLUDE_BUILD
+ endif # STAGING
+diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile
+index b5e67b8..dc40493 100644
+--- a/drivers/staging/Makefile
++++ b/drivers/staging/Makefile
+@@ -53,3 +53,4 @@ obj-$(CONFIG_WAVELAN) += wavelan/
+ obj-$(CONFIG_RAR_REGISTER) += rar/
+ obj-$(CONFIG_DX_SEP) += sep/
+ obj-$(CONFIG_IIO) += iio/
++obj-$(CONFIG_CRYSTALHD) += crystalhd/
+diff --git a/drivers/staging/crystalhd/Kconfig b/drivers/staging/crystalhd/Kconfig
+new file mode 100644
+index 0000000..56b414b
+--- /dev/null
++++ b/drivers/staging/crystalhd/Kconfig
+@@ -0,0 +1,6 @@
++config CRYSTALHD
++ tristate "Broadcom Crystal HD video decoder support"
++ depends on PCI
++ default n
++ help
++ Support for the Broadcom Crystal HD video decoder chipset
+diff --git a/drivers/staging/crystalhd/Makefile b/drivers/staging/crystalhd/Makefile
+new file mode 100644
+index 0000000..e2af0ce
+--- /dev/null
++++ b/drivers/staging/crystalhd/Makefile
+@@ -0,0 +1,6 @@
++obj-$(CONFIG_CRYSTALHD) += crystalhd.o
++
++crystalhd-objs := crystalhd_cmds.o \
++ crystalhd_hw.o \
++ crystalhd_lnx.o \
++ crystalhd_misc.o
+diff --git a/drivers/staging/crystalhd/TODO b/drivers/staging/crystalhd/TODO
+new file mode 100644
+index 0000000..69be5d0
+--- /dev/null
++++ b/drivers/staging/crystalhd/TODO
+@@ -0,0 +1,16 @@
++- Testing
++- Cleanup return codes
++- Cleanup typedefs
++- Cleanup all WIN* references
++- Allocate an Accelerator device class specific Major number,
++ since we don't have any other open sourced accelerators, it is the only
++ one in that category for now.
++ A somewhat similar device is the DXR2/3
++
++Please send patches to:
++Greg Kroah-Hartman <greg@kroah.com>
++Naren Sankar <nsankar@broadcom.com>
++Jarod Wilson <jarod@wilsonet.com>
++Scott Davilla <davilla@4pi.com>
++Manu Abraham <abraham.manu@gmail.com>
++
+diff --git a/drivers/staging/crystalhd/bc_dts_defs.h b/drivers/staging/crystalhd/bc_dts_defs.h
+new file mode 100644
+index 0000000..c34cc07
+--- /dev/null
++++ b/drivers/staging/crystalhd/bc_dts_defs.h
+@@ -0,0 +1,498 @@
++/********************************************************************
++ * Copyright(c) 2006-2009 Broadcom Corporation.
++ *
++ * Name: bc_dts_defs.h
++ *
++ * Description: Common definitions for all components. Only types
++ * is allowed to be included from this file.
++ *
++ * AU
++ *
++ * HISTORY:
++ *
++ ********************************************************************
++ * This header is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU Lesser General Public License as published
++ * by the Free Software Foundation, either version 2.1 of the License.
++ *
++ * This header 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 Lesser General Public License for more details.
++ * You should have received a copy of the GNU Lesser General Public License
++ * along with this header. If not, see <http://www.gnu.org/licenses/>.
++ *******************************************************************/
++
++#ifndef _BC_DTS_DEFS_H_
++#define _BC_DTS_DEFS_H_
++
++#include "bc_dts_types.h"
++
++/* BIT Mask */
++#define BC_BIT(_x) (1 << (_x))
++
++typedef enum _BC_STATUS {
++ BC_STS_SUCCESS = 0,
++ BC_STS_INV_ARG = 1,
++ BC_STS_BUSY = 2,
++ BC_STS_NOT_IMPL = 3,
++ BC_STS_PGM_QUIT = 4,
++ BC_STS_NO_ACCESS = 5,
++ BC_STS_INSUFF_RES = 6,
++ BC_STS_IO_ERROR = 7,
++ BC_STS_NO_DATA = 8,
++ BC_STS_VER_MISMATCH = 9,
++ BC_STS_TIMEOUT = 10,
++ BC_STS_FW_CMD_ERR = 11,
++ BC_STS_DEC_NOT_OPEN = 12,
++ BC_STS_ERR_USAGE = 13,
++ BC_STS_IO_USER_ABORT = 14,
++ BC_STS_IO_XFR_ERROR = 15,
++ BC_STS_DEC_NOT_STARTED = 16,
++ BC_STS_FWHEX_NOT_FOUND = 17,
++ BC_STS_FMT_CHANGE = 18,
++ BC_STS_HIF_ACCESS = 19,
++ BC_STS_CMD_CANCELLED = 20,
++ BC_STS_FW_AUTH_FAILED = 21,
++ BC_STS_BOOTLOADER_FAILED = 22,
++ BC_STS_CERT_VERIFY_ERROR = 23,
++ BC_STS_DEC_EXIST_OPEN = 24,
++ BC_STS_PENDING = 25,
++ BC_STS_CLK_NOCHG = 26,
++
++ /* Must be the last one.*/
++ BC_STS_ERROR = -1
++} BC_STATUS;
++
++/*------------------------------------------------------*
++ * Registry Key Definitions *
++ *------------------------------------------------------*/
++#define BC_REG_KEY_MAIN_PATH "Software\\Broadcom\\MediaPC\\70010"
++#define BC_REG_KEY_FWPATH "FirmwareFilePath"
++#define BC_REG_KEY_SEC_OPT "DbgOptions"
++
++/*
++ * Options:
++ *
++ * b[5] = Enable RSA KEY in EEPROM Support
++ * b[6] = Enable Old PIB scheme. (0 = Use PIB with video scheme)
++ *
++ * b[12] = Enable send message to NotifyIcon
++ *
++ */
++
++typedef enum _BC_SW_OPTIONS {
++ BC_OPT_DOSER_OUT_ENCRYPT = BC_BIT(3),
++ BC_OPT_LINK_OUT_ENCRYPT = BC_BIT(29),
++} BC_SW_OPTIONS;
++
++typedef struct _BC_REG_CONFIG{
++ uint32_t DbgOptions;
++} BC_REG_CONFIG;
++
++#if defined(__KERNEL__) || defined(__LINUX_USER__)
++#else
++/* Align data structures */
++#define ALIGN(x) __declspec(align(x))
++#endif
++
++/* mode
++ * b[0]..b[7] = _DtsDeviceOpenMode
++ * b[8] = Load new FW
++ * b[9] = Load file play back FW
++ * b[10] = Disk format (0 for HD DVD and 1 for BLU ray)
++ * b[11]-b[15] = default output resolution
++ * b[16] = Skip TX CPB Buffer Check
++ * b[17] = Adaptive Output Encrypt/Scramble Scheme
++ * b[18]-b[31] = reserved for future use
++ */
++
++/* To allow multiple apps to open the device. */
++enum _DtsDeviceOpenMode {
++ DTS_PLAYBACK_MODE = 0,
++ DTS_DIAG_MODE,
++ DTS_MONITOR_MODE,
++ DTS_HWINIT_MODE
++};
++
++/* To enable the filter to selectively enable/disable fixes or erratas */
++enum _DtsDeviceFixMode {
++ DTS_LOAD_NEW_FW = BC_BIT(8),
++ DTS_LOAD_FILE_PLAY_FW = BC_BIT(9),
++ DTS_DISK_FMT_BD = BC_BIT(10),
++ /* b[11]-b[15] : Default output resolution */
++ DTS_SKIP_TX_CHK_CPB = BC_BIT(16),
++ DTS_ADAPTIVE_OUTPUT_PER = BC_BIT(17),
++ DTS_INTELLIMAP = BC_BIT(18),
++ /* b[19]-b[21] : select clock frequency */
++ DTS_PLAYBACK_DROP_RPT_MODE = BC_BIT(22)
++};
++
++#define DTS_DFLT_RESOLUTION(x) (x<<11)
++
++#define DTS_DFLT_CLOCK(x) (x<<19)
++
++/* F/W File Version corresponding to S/W Releases */
++enum _FW_FILE_VER {
++ /* S/W release: 02.04.02 F/W release 2.12.2.0 */
++ BC_FW_VER_020402 = ((12<<16) | (2<<8) | (0))
++};
++
++/*------------------------------------------------------*
++ * Stream Types for DtsOpenDecoder() *
++ *------------------------------------------------------*/
++enum _DtsOpenDecStreamTypes {
++ BC_STREAM_TYPE_ES = 0,
++ BC_STREAM_TYPE_PES = 1,
++ BC_STREAM_TYPE_TS = 2,
++ BC_STREAM_TYPE_ES_TSTAMP = 6,
++};
++
++/*------------------------------------------------------*
++ * Video Algorithms for DtsSetVideoParams() *
++ *------------------------------------------------------*/
++enum _DtsSetVideoParamsAlgo {
++ BC_VID_ALGO_H264 = 0,
++ BC_VID_ALGO_MPEG2 = 1,
++ BC_VID_ALGO_VC1 = 4,
++ BC_VID_ALGO_VC1MP = 7,
++};
++
++/*------------------------------------------------------*
++ * MPEG Extension to the PPB *
++ *------------------------------------------------------*/
++#define BC_MPEG_VALID_PANSCAN (1)
++
++typedef struct _BC_PIB_EXT_MPEG {
++ uint32_t valid;
++ /* Always valid, defaults to picture size if no
++ * sequence display extension in the stream. */
++ uint32_t display_horizontal_size;
++ uint32_t display_vertical_size;
++
++ /* MPEG_VALID_PANSCAN
++ * Offsets are a copy values from the MPEG stream. */
++ uint32_t offset_count;
++ int32_t horizontal_offset[3];
++ int32_t vertical_offset[3];
++
++} BC_PIB_EXT_MPEG;
++
++/*------------------------------------------------------*
++ * H.264 Extension to the PPB *
++ *------------------------------------------------------*/
++/* Bit definitions for 'other.h264.valid' field */
++#define H264_VALID_PANSCAN (1)
++#define H264_VALID_SPS_CROP (2)
++#define H264_VALID_VUI (4)
++
++typedef struct _BC_PIB_EXT_H264 {
++ /* 'valid' specifies which fields (or sets of
++ * fields) below are valid. If the corresponding
++ * bit in 'valid' is NOT set then that field(s)
++ * is (are) not initialized. */
++ uint32_t valid;
++
++ /* H264_VALID_PANSCAN */
++ uint32_t pan_scan_count;
++ int32_t pan_scan_left[3];
++ int32_t pan_scan_right[3];
++ int32_t pan_scan_top[3];
++ int32_t pan_scan_bottom[3];
++
++ /* H264_VALID_SPS_CROP */
++ int32_t sps_crop_left;
++ int32_t sps_crop_right;
++ int32_t sps_crop_top;
++ int32_t sps_crop_bottom;
++
++ /* H264_VALID_VUI */
++ uint32_t chroma_top;
++ uint32_t chroma_bottom;
++
++} BC_PIB_EXT_H264;
++
++/*------------------------------------------------------*
++ * VC1 Extension to the PPB *
++ *------------------------------------------------------*/
++#define VC1_VALID_PANSCAN (1)
++
++typedef struct _BC_PIB_EXT_VC1 {
++ uint32_t valid;
++
++ /* Always valid, defaults to picture size if no
++ * sequence display extension in the stream. */
++ uint32_t display_horizontal_size;
++ uint32_t display_vertical_size;
++
++ /* VC1 pan scan windows */
++ uint32_t num_panscan_windows;
++ int32_t ps_horiz_offset[4];
++ int32_t ps_vert_offset[4];
++ int32_t ps_width[4];
++ int32_t ps_height[4];
++
++} BC_PIB_EXT_VC1;
++
++
++/*------------------------------------------------------*
++ * Picture Information Block *
++ *------------------------------------------------------*/
++#if defined(_WIN32) || defined(_WIN64) || defined(__LINUX_USER__)
++/* Values for 'pulldown' field. '0' means no pulldown information
++ * was present for this picture. */
++enum {
++ vdecNoPulldownInfo = 0,
++ vdecTop = 1,
++ vdecBottom = 2,
++ vdecTopBottom = 3,
++ vdecBottomTop = 4,
++ vdecTopBottomTop = 5,
++ vdecBottomTopBottom = 6,
++ vdecFrame_X2 = 7,
++ vdecFrame_X3 = 8,
++ vdecFrame_X1 = 9,
++ vdecFrame_X4 = 10,
++};
++
++/* Values for the 'frame_rate' field. */
++enum {
++ vdecFrameRateUnknown = 0,
++ vdecFrameRate23_97,
++ vdecFrameRate24,
++ vdecFrameRate25,
++ vdecFrameRate29_97,
++ vdecFrameRate30,
++ vdecFrameRate50,
++ vdecFrameRate59_94,
++ vdecFrameRate60,
++};
++
++/* Values for the 'aspect_ratio' field. */
++enum {
++ vdecAspectRatioUnknown = 0,
++ vdecAspectRatioSquare,
++ vdecAspectRatio12_11,
++ vdecAspectRatio10_11,
++ vdecAspectRatio16_11,
++ vdecAspectRatio40_33,
++ vdecAspectRatio24_11,
++ vdecAspectRatio20_11,
++ vdecAspectRatio32_11,
++ vdecAspectRatio80_33,
++ vdecAspectRatio18_11,
++ vdecAspectRatio15_11,
++ vdecAspectRatio64_33,
++ vdecAspectRatio160_99,
++ vdecAspectRatio4_3,
++ vdecAspectRatio16_9,
++ vdecAspectRatio221_1,
++ vdecAspectRatioOther = 255,
++};
++
++/* Values for the 'colour_primaries' field. */
++enum {
++ vdecColourPrimariesUnknown = 0,
++ vdecColourPrimariesBT709,
++ vdecColourPrimariesUnspecified,
++ vdecColourPrimariesReserved,
++ vdecColourPrimariesBT470_2M = 4,
++ vdecColourPrimariesBT470_2BG,
++ vdecColourPrimariesSMPTE170M,
++ vdecColourPrimariesSMPTE240M,
++ vdecColourPrimariesGenericFilm,
++};
++
++enum {
++ vdecRESOLUTION_CUSTOM = 0x00000000, /* custom */
++ vdecRESOLUTION_480i = 0x00000001, /* 480i */
++ vdecRESOLUTION_1080i = 0x00000002, /* 1080i (1920x1080, 60i) */
++ vdecRESOLUTION_NTSC = 0x00000003, /* NTSC (720x483, 60i) */
++ vdecRESOLUTION_480p = 0x00000004, /* 480p (720x480, 60p) */
++ vdecRESOLUTION_720p = 0x00000005, /* 720p (1280x720, 60p) */
++ vdecRESOLUTION_PAL1 = 0x00000006, /* PAL_1 (720x576, 50i) */
++ vdecRESOLUTION_1080i25 = 0x00000007, /* 1080i25 (1920x1080, 50i) */
++ vdecRESOLUTION_720p50 = 0x00000008, /* 720p50 (1280x720, 50p) */
++ vdecRESOLUTION_576p = 0x00000009, /* 576p (720x576, 50p) */
++ vdecRESOLUTION_1080i29_97 = 0x0000000A, /* 1080i (1920x1080, 59.94i) */
++ vdecRESOLUTION_720p59_94 = 0x0000000B, /* 720p (1280x720, 59.94p) */
++ vdecRESOLUTION_SD_DVD = 0x0000000C, /* SD DVD (720x483, 60i) */
++ vdecRESOLUTION_480p656 = 0x0000000D, /* 480p (720x480, 60p), output bus width 8 bit, clock 74.25MHz */
++ vdecRESOLUTION_1080p23_976 = 0x0000000E, /* 1080p23_976 (1920x1080, 23.976p) */
++ vdecRESOLUTION_720p23_976 = 0x0000000F, /* 720p23_976 (1280x720p, 23.976p) */
++ vdecRESOLUTION_240p29_97 = 0x00000010, /* 240p (1440x240, 29.97p ) */
++ vdecRESOLUTION_240p30 = 0x00000011, /* 240p (1440x240, 30p) */
++ vdecRESOLUTION_288p25 = 0x00000012, /* 288p (1440x288p, 25p) */
++ vdecRESOLUTION_1080p29_97 = 0x00000013, /* 1080p29_97 (1920x1080, 29.97p) */
++ vdecRESOLUTION_1080p30 = 0x00000014, /* 1080p30 (1920x1080, 30p) */
++ vdecRESOLUTION_1080p24 = 0x00000015, /* 1080p24 (1920x1080, 24p) */
++ vdecRESOLUTION_1080p25 = 0x00000016, /* 1080p25 (1920x1080, 25p) */
++ vdecRESOLUTION_720p24 = 0x00000017, /* 720p24 (1280x720, 25p) */
++ vdecRESOLUTION_720p29_97 = 0x00000018, /* 720p29.97 (1280x720, 29.97p) */
++ vdecRESOLUTION_480p23_976 = 0x00000019, /* 480p23.976 (720*480, 23.976) */
++ vdecRESOLUTION_480p29_97 = 0x0000001A, /* 480p29.976 (720*480, 29.97p) */
++ vdecRESOLUTION_576p25 = 0x0000001B, /* 576p25 (720*576, 25p) */
++ /* For Zero Frame Rate */
++ vdecRESOLUTION_480p0 = 0x0000001C, /* 480p (720x480, 0p) */
++ vdecRESOLUTION_480i0 = 0x0000001D, /* 480i (720x480, 0i) */
++ vdecRESOLUTION_576p0 = 0x0000001E, /* 576p (720x576, 0p) */
++ vdecRESOLUTION_720p0 = 0x0000001F, /* 720p (1280x720, 0p) */
++ vdecRESOLUTION_1080p0 = 0x00000020, /* 1080p (1920x1080, 0p) */
++ vdecRESOLUTION_1080i0 = 0x00000021, /* 1080i (1920x1080, 0i) */
++};
++
++/* Bit definitions for 'flags' field */
++#define VDEC_FLAG_EOS (0x0004)
++
++#define VDEC_FLAG_FRAME (0x0000)
++#define VDEC_FLAG_FIELDPAIR (0x0008)
++#define VDEC_FLAG_TOPFIELD (0x0010)
++#define VDEC_FLAG_BOTTOMFIELD (0x0018)
++
++#define VDEC_FLAG_PROGRESSIVE_SRC (0x0000)
++#define VDEC_FLAG_INTERLACED_SRC (0x0020)
++#define VDEC_FLAG_UNKNOWN_SRC (0x0040)
++
++#define VDEC_FLAG_BOTTOM_FIRST (0x0080)
++#define VDEC_FLAG_LAST_PICTURE (0x0100)
++
++#define VDEC_FLAG_PICTURE_META_DATA_PRESENT (0x40000)
++
++#endif /* _WIN32 || _WIN64 */
++
++enum _BC_OUTPUT_FORMAT {
++ MODE420 = 0x0,
++ MODE422_YUY2 = 0x1,
++ MODE422_UYVY = 0x2,
++};
++
++typedef struct _BC_PIC_INFO_BLOCK {
++ /* Common fields. */
++ uint64_t timeStamp; /* Timestamp */
++ uint32_t picture_number; /* Ordinal display number */
++ uint32_t width; /* pixels */
++ uint32_t height; /* pixels */
++ uint32_t chroma_format; /* 0x420, 0x422 or 0x444 */
++ uint32_t pulldown;
++ uint32_t flags;
++ uint32_t frame_rate;
++ uint32_t aspect_ratio;
++ uint32_t colour_primaries;
++ uint32_t picture_meta_payload;
++ uint32_t sess_num;
++ uint32_t ycom;
++ uint32_t custom_aspect_ratio_width_height;
++ uint32_t n_drop; /* number of non-reference frames remaining to be dropped */
++
++ /* Protocol-specific extensions. */
++ union {
++ BC_PIB_EXT_H264 h264;
++ BC_PIB_EXT_MPEG mpeg;
++ BC_PIB_EXT_VC1 vc1;
++ } other;
++
++} BC_PIC_INFO_BLOCK, *PBC_PIC_INFO_BLOCK;
++
++/*------------------------------------------------------*
++ * ProcOut Info *
++ *------------------------------------------------------*/
++/* Optional flags for ProcOut Interface.*/
++enum _POUT_OPTIONAL_IN_FLAGS_{
++ /* Flags from App to Device */
++ BC_POUT_FLAGS_YV12 = 0x01, /* Copy Data in YV12 format */
++ BC_POUT_FLAGS_STRIDE = 0x02, /* Stride size is valid. */
++ BC_POUT_FLAGS_SIZE = 0x04, /* Take size information from Application */
++ BC_POUT_FLAGS_INTERLACED = 0x08, /* copy only half the bytes */
++ BC_POUT_FLAGS_INTERLEAVED = 0x10, /* interleaved frame */
++
++ /* Flags from Device to APP */
++ BC_POUT_FLAGS_FMT_CHANGE = 0x10000, /* Data is not VALID when this flag is set */
++ BC_POUT_FLAGS_PIB_VALID = 0x20000, /* PIB Information valid */
++ BC_POUT_FLAGS_ENCRYPTED = 0x40000, /* Data is encrypted. */
++ BC_POUT_FLAGS_FLD_BOT = 0x80000, /* Bottom Field data */
++};
++
++#if defined(__KERNEL__) || defined(__LINUX_USER__)
++typedef BC_STATUS(*dts_pout_callback)(void *shnd, uint32_t width, uint32_t height, uint32_t stride, void *pOut);
++#else
++typedef BC_STATUS(*dts_pout_callback)(void *shnd, uint32_t width, uint32_t height, uint32_t stride, struct _BC_DTS_PROC_OUT *pOut);
++#endif
++
++/* Line 21 Closed Caption */
++/* User Data */
++#define MAX_UD_SIZE 1792 /* 1920 - 128 */
++
++typedef struct _BC_DTS_PROC_OUT {
++ uint8_t *Ybuff; /* Caller Supplied buffer for Y data */
++ uint32_t YbuffSz; /* Caller Supplied Y buffer size */
++ uint32_t YBuffDoneSz; /* Transferred Y datasize */
++
++ uint8_t *UVbuff; /* Caller Supplied buffer for UV data */
++ uint32_t UVbuffSz; /* Caller Supplied UV buffer size */
++ uint32_t UVBuffDoneSz; /* Transferred UV data size */
++
++ uint32_t StrideSz; /* Caller supplied Stride Size */
++ uint32_t PoutFlags; /* Call IN Flags */
++
++ uint32_t discCnt; /* Picture discontinuity count */
++
++ BC_PIC_INFO_BLOCK PicInfo; /* Picture Information Block Data */
++
++ /* Line 21 Closed Caption */
++ /* User Data */
++ uint32_t UserDataSz;
++ uint8_t UserData[MAX_UD_SIZE];
++
++ void *hnd;
++ dts_pout_callback AppCallBack;
++ uint8_t DropFrames;
++ uint8_t b422Mode; /* Picture output Mode */
++ uint8_t bPibEnc; /* PIB encrypted */
++ uint8_t bRevertScramble;
++
++} BC_DTS_PROC_OUT;
++
++typedef struct _BC_DTS_STATUS {
++ uint8_t ReadyListCount; /* Number of frames in ready list (reported by driver) */
++ uint8_t FreeListCount; /* Number of frame buffers free. (reported by driver) */
++ uint8_t PowerStateChange; /* Number of active state power transitions (reported by driver) */
++ uint8_t reserved_[1];
++
++ uint32_t FramesDropped; /* Number of frames dropped. (reported by DIL) */
++ uint32_t FramesCaptured; /* Number of frames captured. (reported by DIL) */
++ uint32_t FramesRepeated; /* Number of frames repeated. (reported by DIL) */
++
++ uint32_t InputCount; /* Times compressed video has been sent to the HW.
++ * i.e. Successful DtsProcInput() calls (reported by DIL) */
++ uint64_t InputTotalSize; /* Amount of compressed video that has been sent to the HW.
++ * (reported by DIL) */
++ uint32_t InputBusyCount; /* Times compressed video has attempted to be sent to the HW
++ * but the input FIFO was full. (reported by DIL) */
++
++ uint32_t PIBMissCount; /* Amount of times a PIB is invalid. (reported by DIL) */
++
++ uint32_t cpbEmptySize; /* supported only for H.264, specifically changed for
++ * Adobe. Report size of CPB buffer available.
++ * Reported by DIL */
++ uint64_t NextTimeStamp; /* TimeStamp of the next picture that will be returned
++ * by a call to ProcOutput. Added for Adobe. Reported
++ * back from the driver */
++ uint8_t reserved__[16];
++
++} BC_DTS_STATUS;
++
++#define BC_SWAP32(_v) \
++ ((((_v) & 0xFF000000)>>24)| \
++ (((_v) & 0x00FF0000)>>8)| \
++ (((_v) & 0x0000FF00)<<8)| \
++ (((_v) & 0x000000FF)<<24))
++
++#define WM_AGENT_TRAYICON_DECODER_OPEN 10001
++#define WM_AGENT_TRAYICON_DECODER_CLOSE 10002
++#define WM_AGENT_TRAYICON_DECODER_START 10003
++#define WM_AGENT_TRAYICON_DECODER_STOP 10004
++#define WM_AGENT_TRAYICON_DECODER_RUN 10005
++#define WM_AGENT_TRAYICON_DECODER_PAUSE 10006
++
++
++#endif /* _BC_DTS_DEFS_H_ */
+diff --git a/drivers/staging/crystalhd/bc_dts_glob_lnx.h b/drivers/staging/crystalhd/bc_dts_glob_lnx.h
+new file mode 100644
+index 0000000..b3125e3
+--- /dev/null
++++ b/drivers/staging/crystalhd/bc_dts_glob_lnx.h
+@@ -0,0 +1,299 @@
++/********************************************************************
++ * Copyright(c) 2006-2009 Broadcom Corporation.
++ *
++ * Name: bc_dts_glob_lnx.h
++ *
++ * Description: Wrapper to Windows dts_glob.h for Link-Linux usage.
++ * The idea is to define additional Linux related defs
++ * in this file to avoid changes to existing Windows
++ * glob file.
++ *
++ * AU
++ *
++ * HISTORY:
++ *
++ ********************************************************************
++ * This header is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU Lesser General Public License as published
++ * by the Free Software Foundation, either version 2.1 of the License.
++ *
++ * This header 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 Lesser General Public License for more details.
++ * You should have received a copy of the GNU Lesser General Public License
++ * along with this header. If not, see <http://www.gnu.org/licenses/>.
++ *******************************************************************/
++
++#ifndef _BC_DTS_GLOB_LNX_H_
++#define _BC_DTS_GLOB_LNX_H_
++
++#ifdef __LINUX_USER__
++#include <stdio.h>
++#include <stdlib.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <ctype.h>
++#include <string.h>
++#include <errno.h>
++#include <netdb.h>
++#include <sys/time.h>
++#include <time.h>
++#include <arpa/inet.h>
++#include <asm/param.h>
++#include <linux/ioctl.h>
++#include <sys/select.h>
++
++#define DRVIFLIB_INT_API
++
++#endif
++
++#include "bc_dts_defs.h"
++#include "bcm_70012_regs.h" /* Link Register defs */
++
++#define CRYSTALHD_API_NAME "crystalhd"
++#define CRYSTALHD_API_DEV_NAME "/dev/crystalhd"
++
++/*
++ * These are SW stack tunable parameters shared
++ * between the driver and the application.
++ */
++enum _BC_DTS_GLOBALS {
++ BC_MAX_FW_CMD_BUFF_SZ = 0x40, /* FW passthrough cmd/rsp buffer size */
++ PCI_CFG_SIZE = 256, /* PCI config size buffer */
++ BC_IOCTL_DATA_POOL_SIZE = 8, /* BC_IOCTL_DATA Pool size */
++ BC_LINK_MAX_OPENS = 3, /* Maximum simultaneous opens*/
++ BC_LINK_MAX_SGLS = 1024, /* Maximum SG elements 4M/4K */
++ BC_TX_LIST_CNT = 2, /* Max Tx DMA Rings */
++ BC_RX_LIST_CNT = 8, /* Max Rx DMA Rings*/
++ BC_PROC_OUTPUT_TIMEOUT = 3000, /* Milliseconds */
++ BC_INFIFO_THRESHOLD = 0x10000,
++};
++
++typedef struct _BC_CMD_REG_ACC {
++ uint32_t Offset;
++ uint32_t Value;
++} BC_CMD_REG_ACC;
++
++typedef struct _BC_CMD_DEV_MEM {
++ uint32_t StartOff;
++ uint32_t NumDwords;
++ uint32_t Rsrd;
++} BC_CMD_DEV_MEM;
++
++/* FW Passthrough command structure */
++enum _bc_fw_cmd_flags {
++ BC_FW_CMD_FLAGS_NONE = 0,
++ BC_FW_CMD_PIB_QS = 0x01,
++};
++
++typedef struct _BC_FW_CMD {
++ uint32_t cmd[BC_MAX_FW_CMD_BUFF_SZ];
++ uint32_t rsp[BC_MAX_FW_CMD_BUFF_SZ];
++ uint32_t flags;
++ uint32_t add_data;
++} BC_FW_CMD, *PBC_FW_CMD;
++
++typedef struct _BC_HW_TYPE {
++ uint16_t PciDevId;
++ uint16_t PciVenId;
++ uint8_t HwRev;
++ uint8_t Align[3];
++} BC_HW_TYPE;
++
++typedef struct _BC_PCI_CFG {
++ uint32_t Size;
++ uint32_t Offset;
++ uint8_t pci_cfg_space[PCI_CFG_SIZE];
++} BC_PCI_CFG;
++
++typedef struct _BC_VERSION_INFO_ {
++ uint8_t DriverMajor;
++ uint8_t DriverMinor;
++ uint16_t DriverRevision;
++} BC_VERSION_INFO;
++
++typedef struct _BC_START_RX_CAP_ {
++ uint32_t Rsrd;
++ uint32_t StartDeliveryThsh;
++ uint32_t PauseThsh;
++ uint32_t ResumeThsh;
++} BC_START_RX_CAP;
++
++typedef struct _BC_FLUSH_RX_CAP_ {
++ uint32_t Rsrd;
++ uint32_t bDiscardOnly;
++} BC_FLUSH_RX_CAP;
++
++typedef struct _BC_DTS_STATS {
++ uint8_t drvRLL;
++ uint8_t drvFLL;
++ uint8_t eosDetected;
++ uint8_t pwr_state_change;
++
++ /* Stats from App */
++ uint32_t opFrameDropped;
++ uint32_t opFrameCaptured;
++ uint32_t ipSampleCnt;
++ uint64_t ipTotalSize;
++ uint32_t reptdFrames;
++ uint32_t pauseCount;
++ uint32_t pibMisses;
++ uint32_t discCounter;
++
++ /* Stats from Driver */
++ uint32_t TxFifoBsyCnt;
++ uint32_t intCount;
++ uint32_t DrvIgnIntrCnt;
++ uint32_t DrvTotalFrmDropped;
++ uint32_t DrvTotalHWErrs;
++ uint32_t DrvTotalPIBFlushCnt;
++ uint32_t DrvTotalFrmCaptured;
++ uint32_t DrvPIBMisses;
++ uint32_t DrvPauseTime;
++ uint32_t DrvRepeatedFrms;
++ uint32_t res1[13];
++
++} BC_DTS_STATS;
++
++typedef struct _BC_PROC_INPUT_ {
++ uint8_t *pDmaBuff;
++ uint32_t BuffSz;
++ uint8_t Mapped;
++ uint8_t Encrypted;
++ uint8_t Rsrd[2];
++ uint32_t DramOffset; /* For debug use only */
++} BC_PROC_INPUT, *PBC_PROC_INPUT;
++
++typedef struct _BC_DEC_YUV_BUFFS {
++ uint32_t b422Mode;
++ uint8_t *YuvBuff;
++ uint32_t YuvBuffSz;
++ uint32_t UVbuffOffset;
++ uint32_t YBuffDoneSz;
++ uint32_t UVBuffDoneSz;
++ uint32_t RefCnt;
++} BC_DEC_YUV_BUFFS;
++
++enum _DECOUT_COMPLETION_FLAGS{
++ COMP_FLAG_NO_INFO = 0x00,
++ COMP_FLAG_FMT_CHANGE = 0x01,
++ COMP_FLAG_PIB_VALID = 0x02,
++ COMP_FLAG_DATA_VALID = 0x04,
++ COMP_FLAG_DATA_ENC = 0x08,
++ COMP_FLAG_DATA_BOT = 0x10,
++};
++
++typedef struct _BC_DEC_OUT_BUFF{
++ BC_DEC_YUV_BUFFS OutPutBuffs;
++ BC_PIC_INFO_BLOCK PibInfo;
++ uint32_t Flags;
++ uint32_t BadFrCnt;
++} BC_DEC_OUT_BUFF;
++
++typedef struct _BC_NOTIFY_MODE {
++ uint32_t Mode;
++ uint32_t Rsvr[3];
++} BC_NOTIFY_MODE;
++
++typedef struct _BC_CLOCK {
++ uint32_t clk;
++ uint32_t Rsvr[3];
++} BC_CLOCK;
++
++typedef struct _BC_IOCTL_DATA {
++ BC_STATUS RetSts;
++ uint32_t IoctlDataSz;
++ uint32_t Timeout;
++ union {
++ BC_CMD_REG_ACC regAcc;
++ BC_CMD_DEV_MEM devMem;
++ BC_FW_CMD fwCmd;
++ BC_HW_TYPE hwType;
++ BC_PCI_CFG pciCfg;
++ BC_VERSION_INFO VerInfo;
++ BC_PROC_INPUT ProcInput;
++ BC_DEC_YUV_BUFFS RxBuffs;
++ BC_DEC_OUT_BUFF DecOutData;
++ BC_START_RX_CAP RxCap;
++ BC_FLUSH_RX_CAP FlushRxCap;
++ BC_DTS_STATS drvStat;
++ BC_NOTIFY_MODE NotifyMode;
++ BC_CLOCK clockValue;
++ } u;
++ struct _BC_IOCTL_DATA *next;
++} BC_IOCTL_DATA;
++
++typedef enum _BC_DRV_CMD{
++ DRV_CMD_VERSION = 0, /* Get SW version */
++ DRV_CMD_GET_HWTYPE, /* Get HW version and type Dozer/Tank */
++ DRV_CMD_REG_RD, /* Read Device Register */
++ DRV_CMD_REG_WR, /* Write Device Register */
++ DRV_CMD_FPGA_RD, /* Read FPGA Register */
++ DRV_CMD_FPGA_WR, /* Wrtie FPGA Reister */
++ DRV_CMD_MEM_RD, /* Read Device Memory */
++ DRV_CMD_MEM_WR, /* Write Device Memory */
++ DRV_CMD_RD_PCI_CFG, /* Read PCI Config Space */
++ DRV_CMD_WR_PCI_CFG, /* Write the PCI Configuration Space*/
++ DRV_CMD_FW_DOWNLOAD, /* Download Firmware */
++ DRV_ISSUE_FW_CMD, /* Issue FW Cmd (pass through mode) */
++ DRV_CMD_PROC_INPUT, /* Process Input Sample */
++ DRV_CMD_ADD_RXBUFFS, /* Add Rx side buffers to driver pool */
++ DRV_CMD_FETCH_RXBUFF, /* Get Rx DMAed buffer */
++ DRV_CMD_START_RX_CAP, /* Start Rx Buffer Capture */
++ DRV_CMD_FLUSH_RX_CAP, /* Stop the capture for now...we will enhance this later*/
++ DRV_CMD_GET_DRV_STAT, /* Get Driver Internal Statistics */
++ DRV_CMD_RST_DRV_STAT, /* Reset Driver Internal Statistics */
++ DRV_CMD_NOTIFY_MODE, /* Notify the Mode to driver in which the application is Operating*/
++ DRV_CMD_CHANGE_CLOCK, /* Change the core clock to either save power or improve performance */
++
++ /* MUST be the last one.. */
++ DRV_CMD_END, /* End of the List.. */
++} BC_DRV_CMD;
++
++#define BC_IOC_BASE 'b'
++#define BC_IOC_VOID _IOC_NONE
++#define BC_IOC_IOWR(nr, type) _IOWR(BC_IOC_BASE, nr, type)
++#define BC_IOCTL_MB BC_IOCTL_DATA
++
++#define BCM_IOC_GET_VERSION BC_IOC_IOWR(DRV_CMD_VERSION, BC_IOCTL_MB)
++#define BCM_IOC_GET_HWTYPE BC_IOC_IOWR(DRV_CMD_GET_HWTYPE, BC_IOCTL_MB)
++#define BCM_IOC_REG_RD BC_IOC_IOWR(DRV_CMD_REG_RD, BC_IOCTL_MB)
++#define BCM_IOC_REG_WR BC_IOC_IOWR(DRV_CMD_REG_WR, BC_IOCTL_MB)
++#define BCM_IOC_MEM_RD BC_IOC_IOWR(DRV_CMD_MEM_RD, BC_IOCTL_MB)
++#define BCM_IOC_MEM_WR BC_IOC_IOWR(DRV_CMD_MEM_WR, BC_IOCTL_MB)
++#define BCM_IOC_FPGA_RD BC_IOC_IOWR(DRV_CMD_FPGA_RD, BC_IOCTL_MB)
++#define BCM_IOC_FPGA_WR BC_IOC_IOWR(DRV_CMD_FPGA_WR, BC_IOCTL_MB)
++#define BCM_IOC_RD_PCI_CFG BC_IOC_IOWR(DRV_CMD_RD_PCI_CFG, BC_IOCTL_MB)
++#define BCM_IOC_WR_PCI_CFG BC_IOC_IOWR(DRV_CMD_WR_PCI_CFG, BC_IOCTL_MB)
++#define BCM_IOC_PROC_INPUT BC_IOC_IOWR(DRV_CMD_PROC_INPUT, BC_IOCTL_MB)
++#define BCM_IOC_ADD_RXBUFFS BC_IOC_IOWR(DRV_CMD_ADD_RXBUFFS, BC_IOCTL_MB)
++#define BCM_IOC_FETCH_RXBUFF BC_IOC_IOWR(DRV_CMD_FETCH_RXBUFF, BC_IOCTL_MB)
++#define BCM_IOC_FW_CMD BC_IOC_IOWR(DRV_ISSUE_FW_CMD, BC_IOCTL_MB)
++#define BCM_IOC_START_RX_CAP BC_IOC_IOWR(DRV_CMD_START_RX_CAP, BC_IOCTL_MB)
++#define BCM_IOC_FLUSH_RX_CAP BC_IOC_IOWR(DRV_CMD_FLUSH_RX_CAP, BC_IOCTL_MB)
++#define BCM_IOC_GET_DRV_STAT BC_IOC_IOWR(DRV_CMD_GET_DRV_STAT, BC_IOCTL_MB)
++#define BCM_IOC_RST_DRV_STAT BC_IOC_IOWR(DRV_CMD_RST_DRV_STAT, BC_IOCTL_MB)
++#define BCM_IOC_NOTIFY_MODE BC_IOC_IOWR(DRV_CMD_NOTIFY_MODE, BC_IOCTL_MB)
++#define BCM_IOC_FW_DOWNLOAD BC_IOC_IOWR(DRV_CMD_FW_DOWNLOAD, BC_IOCTL_MB)
++#define BCM_IOC_CHG_CLK BC_IOC_IOWR(DRV_CMD_CHANGE_CLOCK, BC_IOCTL_MB)
++#define BCM_IOC_END BC_IOC_VOID
++
++/* Wrapper for main IOCTL data */
++typedef struct _crystalhd_ioctl_data {
++ BC_IOCTL_DATA udata; /* IOCTL from App..*/
++ uint32_t u_id; /* Driver specific user ID */
++ uint32_t cmd; /* Cmd ID for driver's use. */
++ void *add_cdata; /* Additional command specific data..*/
++ uint32_t add_cdata_sz; /* Additional command specific data size */
++ struct _crystalhd_ioctl_data *next; /* List/Fifo management */
++} crystalhd_ioctl_data;
++
++
++enum _crystalhd_kmod_ver{
++ crystalhd_kmod_major = 0,
++ crystalhd_kmod_minor = 9,
++ crystalhd_kmod_rev = 27,
++};
++
++#endif
+diff --git a/drivers/staging/crystalhd/bc_dts_types.h b/drivers/staging/crystalhd/bc_dts_types.h
+new file mode 100644
+index 0000000..ac0c817
+--- /dev/null
++++ b/drivers/staging/crystalhd/bc_dts_types.h
+@@ -0,0 +1,121 @@
++/********************************************************************
++ * Copyright(c) 2006-2009 Broadcom Corporation.
++ *
++ * Name: bc_dts_types.h
++ *
++ * Description: Data types
++ *
++ * AU
++ *
++ * HISTORY:
++ *
++ ********************************************************************
++ * This header is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU Lesser General Public License as published
++ * by the Free Software Foundation, either version 2.1 of the License.
++ *
++ * This header 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 Lesser General Public License for more details.
++ * You should have received a copy of the GNU Lesser General Public License
++ * along with this header. If not, see <http://www.gnu.org/licenses/>.
++ *******************************************************************/
++
++#ifndef _BC_DTS_TYPES_H_
++#define _BC_DTS_TYPES_H_
++
++#ifdef __LINUX_USER__ // Don't include these for KERNEL..
++#include <stdint.h>
++#endif
++
++#if defined(_WIN64) || defined(_WIN32)
++typedef uint32_t U32;
++typedef int32_t S32;
++typedef uint16_t U16;
++typedef int16_t S16;
++typedef unsigned char U8;
++typedef char S8;
++#endif
++
++#ifndef PVOID
++typedef void *PVOID;
++#endif
++
++#ifndef BOOL
++typedef int BOOL;
++#endif
++
++#ifdef WIN32
++ typedef unsigned __int64 U64;
++#elif defined(_WIN64)
++ typedef uint64_t U64;
++#endif
++
++#ifdef _WIN64
++#if !(defined(POINTER_32))
++#define POINTER_32 __ptr32
++#endif
++#else /* _WIN32 */
++#define POINTER_32
++#endif
++
++#if defined(__KERNEL__) || defined(__LINUX_USER__)
++
++#ifdef __LINUX_USER__ /* Don't include these for KERNEL */
++typedef uint32_t ULONG;
++typedef int32_t LONG;
++typedef void *HANDLE;
++#ifndef VOID
++typedef void VOID;
++#endif
++typedef void *LPVOID;
++typedef uint32_t DWORD;
++typedef uint32_t UINT32;
++typedef uint32_t *LPDWORD;
++typedef unsigned char *PUCHAR;
++
++#ifndef TRUE
++ #define TRUE 1
++#endif
++
++#ifndef FALSE
++ #define FALSE 0
++#endif
++
++#define TEXT
++
++#else
++
++/* For Kernel usage.. */
++typedef bool bc_bool_t;
++#endif
++
++#else
++
++#ifndef uint64_t
++typedef struct _uint64_t {
++ uint32_t low_dw;
++ uint32_t hi_dw;
++} uint64_t;
++#endif
++
++#ifndef int32_t
++typedef signed long int32_t;
++#endif
++
++#ifndef uint32_t
++typedef unsigned long uint32_t;
++#endif
++
++#ifndef uint16_t
++typedef unsigned short uint16_t;
++#endif
++
++#ifndef uint8_t
++typedef unsigned char uint8_t;
++#endif
++#endif
++
++#endif
++
+diff --git a/drivers/staging/crystalhd/bcm_70012_regs.h b/drivers/staging/crystalhd/bcm_70012_regs.h
+new file mode 100644
+index 0000000..6922f54
+--- /dev/null
++++ b/drivers/staging/crystalhd/bcm_70012_regs.h
+@@ -0,0 +1,757 @@
++/***************************************************************************
++ * Copyright (c) 1999-2009, Broadcom Corporation.
++ *
++ * Name: bcm_70012_regs.h
++ *
++ * Description: BCM70012 registers
++ *
++ ********************************************************************
++ * This header is free software: you can redistribute it and/or modify
++ * it under the terms of the GNU Lesser General Public License as published
++ * by the Free Software Foundation, either version 2.1 of the License.
++ *
++ * This header 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 Lesser General Public License for more details.
++ * You should have received a copy of the GNU Lesser General Public License
++ * along with this header. If not, see <http://www.gnu.org/licenses/>.
++ ***************************************************************************/
++
++#ifndef MACFILE_H__
++#define MACFILE_H__
++
++/**
++ * m = memory, c = core, r = register, f = field, d = data.
++ */
++#if !defined(GET_FIELD) && !defined(SET_FIELD)
++#define BRCM_ALIGN(c,r,f) c##_##r##_##f##_ALIGN
++#define BRCM_BITS(c,r,f) c##_##r##_##f##_BITS
++#define BRCM_MASK(c,r,f) c##_##r##_##f##_MASK
++#define BRCM_SHIFT(c,r,f) c##_##r##_##f##_SHIFT
++
++#define GET_FIELD(m,c,r,f) \
++ ((((m) & BRCM_MASK(c,r,f)) >> BRCM_SHIFT(c,r,f)) << BRCM_ALIGN(c,r,f))
++
++#define SET_FIELD(m,c,r,f,d) \
++ ((m) = (((m) & ~BRCM_MASK(c,r,f)) | ((((d) >> BRCM_ALIGN(c,r,f)) << \
++ BRCM_SHIFT(c,r,f)) & BRCM_MASK(c,r,f))) \
++ )
++
++#define SET_TYPE_FIELD(m,c,r,f,d) SET_FIELD(m,c,r,f,c##_##d)
++#define SET_NAME_FIELD(m,c,r,f,d) SET_FIELD(m,c,r,f,c##_##r##_##f##_##d)
++#define SET_VALUE_FIELD(m,c,r,f,d) SET_FIELD(m,c,r,f,d)
++
++#endif /* GET & SET */
++
++/****************************************************************************
++ * Core Enums.
++ ***************************************************************************/
++/****************************************************************************
++ * Enums: AES_RGR_BRIDGE_RESET_CTRL
++ ***************************************************************************/
++#define AES_RGR_BRIDGE_RESET_CTRL_DEASSERT 0
++#define AES_RGR_BRIDGE_RESET_CTRL_ASSERT 1
++
++/****************************************************************************
++ * Enums: CCE_RGR_BRIDGE_RESET_CTRL
++ ***************************************************************************/
++#define CCE_RGR_BRIDGE_RESET_CTRL_DEASSERT 0
++#define CCE_RGR_BRIDGE_RESET_CTRL_ASSERT 1
++
++/****************************************************************************
++ * Enums: DBU_RGR_BRIDGE_RESET_CTRL
++ ***************************************************************************/
++#define DBU_RGR_BRIDGE_RESET_CTRL_DEASSERT 0
++#define DBU_RGR_BRIDGE_RESET_CTRL_ASSERT 1
++
++/****************************************************************************
++ * Enums: DCI_RGR_BRIDGE_RESET_CTRL
++ ***************************************************************************/
++#define DCI_RGR_BRIDGE_RESET_CTRL_DEASSERT 0
++#define DCI_RGR_BRIDGE_RESET_CTRL_ASSERT 1
++
++/****************************************************************************
++ * Enums: GISB_ARBITER_DEASSERT_ASSERT
++ ***************************************************************************/
++#define GISB_ARBITER_DEASSERT_ASSERT_DEASSERT 0
++#define GISB_ARBITER_DEASSERT_ASSERT_ASSERT 1
++
++/****************************************************************************
++ * Enums: GISB_ARBITER_UNMASK_MASK
++ ***************************************************************************/
++#define GISB_ARBITER_UNMASK_MASK_UNMASK 0
++#define GISB_ARBITER_UNMASK_MASK_MASK 1
++
++/****************************************************************************
++ * Enums: GISB_ARBITER_DISABLE_ENABLE
++ ***************************************************************************/
++#define GISB_ARBITER_DISABLE_ENABLE_DISABLE 0
++#define GISB_ARBITER_DISABLE_ENABLE_ENABLE 1
++
++/****************************************************************************
++ * Enums: I2C_GR_BRIDGE_RESET_CTRL
++ ***************************************************************************/
++#define I2C_GR_BRIDGE_RESET_CTRL_DEASSERT 0
++#define I2C_GR_BRIDGE_RESET_CTRL_ASSERT 1
++
++/****************************************************************************
++ * Enums: MISC_GR_BRIDGE_RESET_CTRL
++ ***************************************************************************/
++#define MISC_GR_BRIDGE_RESET_CTRL_DEASSERT 0
++#define MISC_GR_BRIDGE_RESET_CTRL_ASSERT 1
++
++/****************************************************************************
++ * Enums: OTP_GR_BRIDGE_RESET_CTRL
++ ***************************************************************************/
++#define OTP_GR_BRIDGE_RESET_CTRL_DEASSERT 0
++#define OTP_GR_BRIDGE_RESET_CTRL_ASSERT 1
++
++/****************************************************************************
++ * BCM70012_TGT_TOP_PCIE_CFG
++ ***************************************************************************/
++#define PCIE_CFG_DEVICE_VENDOR_ID 0x00000000 /* DEVICE_VENDOR_ID Register */
++#define PCIE_CFG_STATUS_COMMAND 0x00000004 /* STATUS_COMMAND Register */
++#define PCIE_CFG_PCI_CLASSCODE_AND_REVISION_ID 0x00000008 /* PCI_CLASSCODE_AND_REVISION_ID Register */
++#define PCIE_CFG_BIST_HEADER_TYPE_LATENCY_TIMER_CACHE_LINE_SIZE 0x0000000c /* BIST_HEADER_TYPE_LATENCY_TIMER_CACHE_LINE_SIZE Register */
++#define PCIE_CFG_BASE_ADDRESS_1 0x00000010 /* BASE_ADDRESS_1 Register */
++#define PCIE_CFG_BASE_ADDRESS_2 0x00000014 /* BASE_ADDRESS_2 Register */
++#define PCIE_CFG_BASE_ADDRESS_3 0x00000018 /* BASE_ADDRESS_3 Register */
++#define PCIE_CFG_BASE_ADDRESS_4 0x0000001c /* BASE_ADDRESS_4 Register */
++#define PCIE_CFG_CARDBUS_CIS_POINTER 0x00000028 /* CARDBUS_CIS_POINTER Register */
++#define PCIE_CFG_SUBSYSTEM_DEVICE_VENDOR_ID 0x0000002c /* SUBSYSTEM_DEVICE_VENDOR_ID Register */
++#define PCIE_CFG_EXPANSION_ROM_BASE_ADDRESS 0x00000030 /* EXPANSION_ROM_BASE_ADDRESS Register */
++#define PCIE_CFG_CAPABILITIES_POINTER 0x00000034 /* CAPABILITIES_POINTER Register */
++#define PCIE_CFG_INTERRUPT 0x0000003c /* INTERRUPT Register */
++#define PCIE_CFG_VPD_CAPABILITIES 0x00000040 /* VPD_CAPABILITIES Register */
++#define PCIE_CFG_VPD_DATA 0x00000044 /* VPD_DATA Register */
++#define PCIE_CFG_POWER_MANAGEMENT_CAPABILITY 0x00000048 /* POWER_MANAGEMENT_CAPABILITY Register */
++#define PCIE_CFG_POWER_MANAGEMENT_CONTROL_STATUS 0x0000004c /* POWER_MANAGEMENT_CONTROL_STATUS Register */
++#define PCIE_CFG_MSI_CAPABILITY_HEADER 0x00000050 /* MSI_CAPABILITY_HEADER Register */
++#define PCIE_CFG_MSI_LOWER_ADDRESS 0x00000054 /* MSI_LOWER_ADDRESS Register */
++#define PCIE_CFG_MSI_UPPER_ADDRESS_REGISTER 0x00000058 /* MSI_UPPER_ADDRESS_REGISTER Register */
++#define PCIE_CFG_MSI_DATA 0x0000005c /* MSI_DATA Register */
++#define PCIE_CFG_BROADCOM_VENDOR_SPECIFIC_CAPABILITY_HEADER 0x00000060 /* BROADCOM_VENDOR_SPECIFIC_CAPABILITY_HEADER Register */
++#define PCIE_CFG_RESET_COUNTERS_INITIAL_VALUES 0x00000064 /* RESET_COUNTERS_INITIAL_VALUES Register */
++#define PCIE_CFG_MISCELLANEOUS_HOST_CONTROL 0x00000068 /* MISCELLANEOUS_HOST_CONTROL Register */
++#define PCIE_CFG_SPARE 0x0000006c /* SPARE Register */
++#define PCIE_CFG_PCI_STATE 0x00000070 /* PCI_STATE Register */
++#define PCIE_CFG_CLOCK_CONTROL 0x00000074 /* CLOCK_CONTROL Register */
++#define PCIE_CFG_REGISTER_BASE 0x00000078 /* REGISTER_BASE Register */
++#define PCIE_CFG_MEMORY_BASE 0x0000007c /* MEMORY_BASE Register */
++#define PCIE_CFG_REGISTER_DATA 0x00000080 /* REGISTER_DATA Register */
++#define PCIE_CFG_MEMORY_DATA 0x00000084 /* MEMORY_DATA Register */
++#define PCIE_CFG_EXPANSION_ROM_BAR_SIZE 0x00000088 /* EXPANSION_ROM_BAR_SIZE Register */
++#define PCIE_CFG_EXPANSION_ROM_ADDRESS 0x0000008c /* EXPANSION_ROM_ADDRESS Register */
++#define PCIE_CFG_EXPANSION_ROM_DATA 0x00000090 /* EXPANSION_ROM_DATA Register */
++#define PCIE_CFG_VPD_INTERFACE 0x00000094 /* VPD_INTERFACE Register */
++#define PCIE_CFG_UNDI_RECEIVE_BD_STANDARD_PRODUCER_RING_PRODUCER_INDEX_MAILBOX_UPPER 0x00000098 /* UNDI_RECEIVE_BD_STANDARD_PRODUCER_RING_PRODUCER_INDEX_MAILBOX_UPPER Register */
++#define PCIE_CFG_UNDI_RECEIVE_BD_STANDARD_PRODUCER_RING_PRODUCER_INDEX_MAILBOX_LOWER 0x0000009c /* UNDI_RECEIVE_BD_STANDARD_PRODUCER_RING_PRODUCER_INDEX_MAILBOX_LOWER Register */
++#define PCIE_CFG_UNDI_RECEIVE_RETURN_RING_CONSUMER_INDEX_UPPER 0x000000a0 /* UNDI_RECEIVE_RETURN_RING_CONSUMER_INDEX_UPPER Register */
++#define PCIE_CFG_UNDI_RECEIVE_RETURN_RING_CONSUMER_INDEX_LOWER 0x000000a4 /* UNDI_RECEIVE_RETURN_RING_CONSUMER_INDEX_LOWER Register */
++#define PCIE_CFG_UNDI_SEND_BD_PRODUCER_INDEX_MAILBOX_UPPER 0x000000a8 /* UNDI_SEND_BD_PRODUCER_INDEX_MAILBOX_UPPER Register */
++#define PCIE_CFG_UNDI_SEND_BD_PRODUCER_INDEX_MAILBOX_LOWER 0x000000ac /* UNDI_SEND_BD_PRODUCER_INDEX_MAILBOX_LOWER Register */
++#define PCIE_CFG_INT_MAILBOX_UPPER 0x000000b0 /* INT_MAILBOX_UPPER Register */
++#define PCIE_CFG_INT_MAILBOX_LOWER 0x000000b4 /* INT_MAILBOX_LOWER Register */
++#define PCIE_CFG_PRODUCT_ID_AND_ASIC_REVISION 0x000000bc /* PRODUCT_ID_AND_ASIC_REVISION Register */
++#define PCIE_CFG_FUNCTION_EVENT 0x000000c0 /* FUNCTION_EVENT Register */
++#define PCIE_CFG_FUNCTION_EVENT_MASK 0x000000c4 /* FUNCTION_EVENT_MASK Register */
++#define PCIE_CFG_FUNCTION_PRESENT 0x000000c8 /* FUNCTION_PRESENT Register */
++#define PCIE_CFG_PCIE_CAPABILITIES 0x000000cc /* PCIE_CAPABILITIES Register */
++#define PCIE_CFG_DEVICE_CAPABILITIES 0x000000d0 /* DEVICE_CAPABILITIES Register */
++#define PCIE_CFG_DEVICE_STATUS_CONTROL 0x000000d4 /* DEVICE_STATUS_CONTROL Register */
++#define PCIE_CFG_LINK_CAPABILITY 0x000000d8 /* LINK_CAPABILITY Register */
++#define PCIE_CFG_LINK_STATUS_CONTROL 0x000000dc /* LINK_STATUS_CONTROL Register */
++#define PCIE_CFG_DEVICE_CAPABILITIES_2 0x000000f0 /* DEVICE_CAPABILITIES_2 Register */
++#define PCIE_CFG_DEVICE_STATUS_CONTROL_2 0x000000f4 /* DEVICE_STATUS_CONTROL_2 Register */
++#define PCIE_CFG_LINK_CAPABILITIES_2 0x000000f8 /* LINK_CAPABILITIES_2 Register */
++#define PCIE_CFG_LINK_STATUS_CONTROL_2 0x000000fc /* LINK_STATUS_CONTROL_2 Register */
++#define PCIE_CFG_ADVANCED_ERROR_REPORTING_ENHANCED_CAPABILITY_HEADER 0x00000100 /* ADVANCED_ERROR_REPORTING_ENHANCED_CAPABILITY_HEADER Register */
++#define PCIE_CFG_UNCORRECTABLE_ERROR_STATUS 0x00000104 /* UNCORRECTABLE_ERROR_STATUS Register */
++#define PCIE_CFG_UNCORRECTABLE_ERROR_MASK 0x00000108 /* UNCORRECTABLE_ERROR_MASK Register */
++#define PCIE_CFG_UNCORRECTABLE_ERROR_SEVERITY 0x0000010c /* UNCORRECTABLE_ERROR_SEVERITY Register */
++#define PCIE_CFG_CORRECTABLE_ERROR_STATUS 0x00000110 /* CORRECTABLE_ERROR_STATUS Register */
++#define PCIE_CFG_CORRECTABLE_ERROR_MASK 0x00000114 /* CORRECTABLE_ERROR_MASK Register */
++#define PCIE_CFG_ADVANCED_ERROR_CAPABILITIES_AND_CONTROL 0x00000118 /* ADVANCED_ERROR_CAPABILITIES_AND_CONTROL Register */
++#define PCIE_CFG_HEADER_LOG_1 0x0000011c /* HEADER_LOG_1 Register */
++#define PCIE_CFG_HEADER_LOG_2 0x00000120 /* HEADER_LOG_2 Register */
++#define PCIE_CFG_HEADER_LOG_3 0x00000124 /* HEADER_LOG_3 Register */
++#define PCIE_CFG_HEADER_LOG_4 0x00000128 /* HEADER_LOG_4 Register */
++#define PCIE_CFG_VIRTUAL_CHANNEL_ENHANCED_CAPABILITY_HEADER 0x0000013c /* VIRTUAL_CHANNEL_ENHANCED_CAPABILITY_HEADER Register */
++#define PCIE_CFG_PORT_VC_CAPABILITY 0x00000140 /* PORT_VC_CAPABILITY Register */
++#define PCIE_CFG_PORT_VC_CAPABILITY_2 0x00000144 /* PORT_VC_CAPABILITY_2 Register */
++#define PCIE_CFG_PORT_VC_STATUS_CONTROL 0x00000148 /* PORT_VC_STATUS_CONTROL Register */
++#define PCIE_CFG_VC_RESOURCE_CAPABILITY 0x0000014c /* VC_RESOURCE_CAPABILITY Register */
++#define PCIE_CFG_VC_RESOURCE_CONTROL 0x00000150 /* VC_RESOURCE_CONTROL Register */
++#define PCIE_CFG_VC_RESOURCE_STATUS 0x00000154 /* VC_RESOURCE_STATUS Register */
++#define PCIE_CFG_DEVICE_SERIAL_NO_ENHANCED_CAPABILITY_HEADER 0x00000160 /* DEVICE_SERIAL_NO_ENHANCED_CAPABILITY_HEADER Register */
++#define PCIE_CFG_DEVICE_SERIAL_NO_LOWER_DW 0x00000164 /* DEVICE_SERIAL_NO_LOWER_DW Register */
++#define PCIE_CFG_DEVICE_SERIAL_NO_UPPER_DW 0x00000168 /* DEVICE_SERIAL_NO_UPPER_DW Register */
++#define PCIE_CFG_POWER_BUDGETING_ENHANCED_CAPABILITY_HEADER 0x0000016c /* POWER_BUDGETING_ENHANCED_CAPABILITY_HEADER Register */
++#define PCIE_CFG_POWER_BUDGETING_DATA_SELECT 0x00000170 /* POWER_BUDGETING_DATA_SELECT Register */
++#define PCIE_CFG_POWER_BUDGETING_DATA 0x00000174 /* POWER_BUDGETING_DATA Register */
++#define PCIE_CFG_POWER_BUDGETING_CAPABILITY 0x00000178 /* POWER_BUDGETING_CAPABILITY Register */
++#define PCIE_CFG_FIRMWARE_POWER_BUDGETING_2_1 0x0000017c /* FIRMWARE_POWER_BUDGETING_2_1 Register */
++#define PCIE_CFG_FIRMWARE_POWER_BUDGETING_4_3 0x00000180 /* FIRMWARE_POWER_BUDGETING_4_3 Register */
++#define PCIE_CFG_FIRMWARE_POWER_BUDGETING_6_5 0x00000184 /* FIRMWARE_POWER_BUDGETING_6_5 Register */
++#define PCIE_CFG_FIRMWARE_POWER_BUDGETING_8_7 0x00000188 /* FIRMWARE_POWER_BUDGETING_8_7 Register */
++#define PCIE_CFG_PCIE_1_1_ADVISORY_NON_FATAL_ERROR_MASKING 0x0000018c /* PCIE_1_1_ADVISORY_NON_FATAL_ERROR_MASKING Register */
++
++
++/****************************************************************************
++ * BCM70012_TGT_TOP_PCIE_TL
++ ***************************************************************************/
++#define PCIE_TL_TL_CONTROL 0x00000400 /* TL_CONTROL Register */
++#define PCIE_TL_TRANSACTION_CONFIGURATION 0x00000404 /* TRANSACTION_CONFIGURATION Register */
++
++
++/****************************************************************************
++ * BCM70012_TGT_TOP_PCIE_DLL
++ ***************************************************************************/
++#define PCIE_DLL_DATA_LINK_CONTROL 0x00000500 /* DATA_LINK_CONTROL Register */
++#define PCIE_DLL_DATA_LINK_STATUS 0x00000504 /* DATA_LINK_STATUS Register */
++
++
++/****************************************************************************
++ * BCM70012_TGT_TOP_INTR
++ ***************************************************************************/
++#define INTR_INTR_STATUS 0x00000700 /* Interrupt Status Register */
++#define INTR_INTR_SET 0x00000704 /* Interrupt Set Register */
++#define INTR_INTR_CLR_REG 0x00000708 /* Interrupt Clear Register */
++#define INTR_INTR_MSK_STS_REG 0x0000070c /* Interrupt Mask Status Register */
++#define INTR_INTR_MSK_SET_REG 0x00000710 /* Interrupt Mask Set Register */
++#define INTR_INTR_MSK_CLR_REG 0x00000714 /* Interrupt Mask Clear Register */
++#define INTR_EOI_CTRL 0x00000720 /* End of interrupt control register */
++
++
++/****************************************************************************
++ * BCM70012_MISC_TOP_MISC1
++ ***************************************************************************/
++#define MISC1_TX_FIRST_DESC_L_ADDR_LIST0 0x00000c00 /* Tx DMA Descriptor List0 First Descriptor lower Address */
++#define MISC1_TX_FIRST_DESC_U_ADDR_LIST0 0x00000c04 /* Tx DMA Descriptor List0 First Descriptor Upper Address */
++#define MISC1_TX_FIRST_DESC_L_ADDR_LIST1 0x00000c08 /* Tx DMA Descriptor List1 First Descriptor Lower Address */
++#define MISC1_TX_FIRST_DESC_U_ADDR_LIST1 0x00000c0c /* Tx DMA Descriptor List1 First Descriptor Upper Address */
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS 0x00000c10 /* Tx DMA Software Descriptor List Control and Status */
++#define MISC1_TX_DMA_ERROR_STATUS 0x00000c18 /* Tx DMA Engine Error Status */
++#define MISC1_TX_DMA_LIST0_CUR_DESC_L_ADDR 0x00000c1c /* Tx DMA List0 Current Descriptor Lower Address */
++#define MISC1_TX_DMA_LIST0_CUR_DESC_U_ADDR 0x00000c20 /* Tx DMA List0 Current Descriptor Upper Address */
++#define MISC1_TX_DMA_LIST0_CUR_BYTE_CNT_REM 0x00000c24 /* Tx DMA List0 Current Descriptor Upper Address */
++#define MISC1_TX_DMA_LIST1_CUR_DESC_L_ADDR 0x00000c28 /* Tx DMA List1 Current Descriptor Lower Address */
++#define MISC1_TX_DMA_LIST1_CUR_DESC_U_ADDR 0x00000c2c /* Tx DMA List1 Current Descriptor Upper Address */
++#define MISC1_TX_DMA_LIST1_CUR_BYTE_CNT_REM 0x00000c30 /* Tx DMA List1 Current Descriptor Upper Address */
++#define MISC1_Y_RX_FIRST_DESC_L_ADDR_LIST0 0x00000c34 /* Y Rx Descriptor List0 First Descriptor Lower Address */
++#define MISC1_Y_RX_FIRST_DESC_U_ADDR_LIST0 0x00000c38 /* Y Rx Descriptor List0 First Descriptor Upper Address */
++#define MISC1_Y_RX_FIRST_DESC_L_ADDR_LIST1 0x00000c3c /* Y Rx Descriptor List1 First Descriptor Lower Address */
++#define MISC1_Y_RX_FIRST_DESC_U_ADDR_LIST1 0x00000c40 /* Y Rx Descriptor List1 First Descriptor Upper Address */
++#define MISC1_Y_RX_SW_DESC_LIST_CTRL_STS 0x00000c44 /* Y Rx Software Descriptor List Control and Status */
++#define MISC1_Y_RX_ERROR_STATUS 0x00000c4c /* Y Rx Engine Error Status */
++#define MISC1_Y_RX_LIST0_CUR_DESC_L_ADDR 0x00000c50 /* Y Rx List0 Current Descriptor Lower Address */
++#define MISC1_Y_RX_LIST0_CUR_DESC_U_ADDR 0x00000c54 /* Y Rx List0 Current Descriptor Upper Address */
++#define MISC1_Y_RX_LIST0_CUR_BYTE_CNT 0x00000c58 /* Y Rx List0 Current Descriptor Byte Count */
++#define MISC1_Y_RX_LIST1_CUR_DESC_L_ADDR 0x00000c5c /* Y Rx List1 Current Descriptor Lower address */
++#define MISC1_Y_RX_LIST1_CUR_DESC_U_ADDR 0x00000c60 /* Y Rx List1 Current Descriptor Upper address */
++#define MISC1_Y_RX_LIST1_CUR_BYTE_CNT 0x00000c64 /* Y Rx List1 Current Descriptor Byte Count */
++#define MISC1_UV_RX_FIRST_DESC_L_ADDR_LIST0 0x00000c68 /* UV Rx Descriptor List0 First Descriptor lower Address */
++#define MISC1_UV_RX_FIRST_DESC_U_ADDR_LIST0 0x00000c6c /* UV Rx Descriptor List0 First Descriptor Upper Address */
++#define MISC1_UV_RX_FIRST_DESC_L_ADDR_LIST1 0x00000c70 /* UV Rx Descriptor List1 First Descriptor Lower Address */
++#define MISC1_UV_RX_FIRST_DESC_U_ADDR_LIST1 0x00000c74 /* UV Rx Descriptor List1 First Descriptor Upper Address */
++#define MISC1_UV_RX_SW_DESC_LIST_CTRL_STS 0x00000c78 /* UV Rx Software Descriptor List Control and Status */
++#define MISC1_UV_RX_ERROR_STATUS 0x00000c7c /* UV Rx Engine Error Status */
++#define MISC1_UV_RX_LIST0_CUR_DESC_L_ADDR 0x00000c80 /* UV Rx List0 Current Descriptor Lower Address */
++#define MISC1_UV_RX_LIST0_CUR_DESC_U_ADDR 0x00000c84 /* UV Rx List0 Current Descriptor Upper Address */
++#define MISC1_UV_RX_LIST0_CUR_BYTE_CNT 0x00000c88 /* UV Rx List0 Current Descriptor Byte Count */
++#define MISC1_UV_RX_LIST1_CUR_DESC_L_ADDR 0x00000c8c /* UV Rx List1 Current Descriptor Lower Address */
++#define MISC1_UV_RX_LIST1_CUR_DESC_U_ADDR 0x00000c90 /* UV Rx List1 Current Descriptor Upper Address */
++#define MISC1_UV_RX_LIST1_CUR_BYTE_CNT 0x00000c94 /* UV Rx List1 Current Descriptor Byte Count */
++#define MISC1_DMA_DEBUG_OPTIONS_REG 0x00000c98 /* DMA Debug Options Register */
++#define MISC1_READ_CHANNEL_ERROR_STATUS 0x00000c9c /* Read Channel Error Status */
++#define MISC1_PCIE_DMA_CTRL 0x00000ca0 /* PCIE DMA Control Register */
++
++
++/****************************************************************************
++ * BCM70012_MISC_TOP_MISC2
++ ***************************************************************************/
++#define MISC2_GLOBAL_CTRL 0x00000d00 /* Global Control Register */
++#define MISC2_INTERNAL_STATUS 0x00000d04 /* Internal Status Register */
++#define MISC2_INTERNAL_STATUS_MUX_CTRL 0x00000d08 /* Internal Debug Mux Control */
++#define MISC2_DEBUG_FIFO_LENGTH 0x00000d0c /* Debug FIFO Length */
++
++
++/****************************************************************************
++ * BCM70012_MISC_TOP_MISC3
++ ***************************************************************************/
++#define MISC3_RESET_CTRL 0x00000e00 /* Reset Control Register */
++#define MISC3_BIST_CTRL 0x00000e04 /* BIST Control Register */
++#define MISC3_BIST_STATUS 0x00000e08 /* BIST Status Register */
++#define MISC3_RX_CHECKSUM 0x00000e0c /* Receive Checksum */
++#define MISC3_TX_CHECKSUM 0x00000e10 /* Transmit Checksum */
++#define MISC3_ECO_CTRL_CORE 0x00000e14 /* ECO Core Reset Control Register */
++#define MISC3_CSI_TEST_CTRL 0x00000e18 /* CSI Test Control Register */
++#define MISC3_HD_DVI_TEST_CTRL 0x00000e1c /* HD DVI Test Control Register */
++
++
++/****************************************************************************
++ * BCM70012_MISC_TOP_MISC_PERST
++ ***************************************************************************/
++#define MISC_PERST_ECO_CTRL_PERST 0x00000e80 /* ECO PCIE Reset Control Register */
++#define MISC_PERST_DECODER_CTRL 0x00000e84 /* Decoder Control Register */
++#define MISC_PERST_CCE_STATUS 0x00000e88 /* Config Copy Engine Status */
++#define MISC_PERST_PCIE_DEBUG 0x00000e8c /* PCIE Debug Control Register */
++#define MISC_PERST_PCIE_DEBUG_STATUS 0x00000e90 /* PCIE Debug Status Register */
++#define MISC_PERST_VREG_CTRL 0x00000e94 /* Voltage Regulator Control Register */
++#define MISC_PERST_MEM_CTRL 0x00000e98 /* Memory Control Register */
++#define MISC_PERST_CLOCK_CTRL 0x00000e9c /* Clock Control Register */
++
++
++/****************************************************************************
++ * BCM70012_MISC_TOP_GISB_ARBITER
++ ***************************************************************************/
++#define GISB_ARBITER_REVISION 0x00000f00 /* GISB ARBITER REVISION */
++#define GISB_ARBITER_SCRATCH 0x00000f04 /* GISB ARBITER Scratch Register */
++#define GISB_ARBITER_REQ_MASK 0x00000f08 /* GISB ARBITER Master Request Mask Register */
++#define GISB_ARBITER_TIMER 0x00000f0c /* GISB ARBITER Timer Value Register */
++
++
++/****************************************************************************
++ * BCM70012_OTP_TOP_OTP
++ ***************************************************************************/
++#define OTP_CONFIG_INFO 0x00001400 /* OTP Configuration Register */
++#define OTP_CMD 0x00001404 /* OTP Command Register */
++#define OTP_STATUS 0x00001408 /* OTP Status Register */
++#define OTP_CONTENT_MISC 0x0000140c /* Content : Miscellaneous Register */
++#define OTP_CONTENT_AES_0 0x00001410 /* Content : AES Key 0 Register */
++#define OTP_CONTENT_AES_1 0x00001414 /* Content : AES Key 1 Register */
++#define OTP_CONTENT_AES_2 0x00001418 /* Content : AES Key 2 Register */
++#define OTP_CONTENT_AES_3 0x0000141c /* Content : AES Key 3 Register */
++#define OTP_CONTENT_SHA_0 0x00001420 /* Content : SHA Key 0 Register */
++#define OTP_CONTENT_SHA_1 0x00001424 /* Content : SHA Key 1 Register */
++#define OTP_CONTENT_SHA_2 0x00001428 /* Content : SHA Key 2 Register */
++#define OTP_CONTENT_SHA_3 0x0000142c /* Content : SHA Key 3 Register */
++#define OTP_CONTENT_SHA_4 0x00001430 /* Content : SHA Key 4 Register */
++#define OTP_CONTENT_SHA_5 0x00001434 /* Content : SHA Key 5 Register */
++#define OTP_CONTENT_SHA_6 0x00001438 /* Content : SHA Key 6 Register */
++#define OTP_CONTENT_SHA_7 0x0000143c /* Content : SHA Key 7 Register */
++#define OTP_CONTENT_CHECKSUM 0x00001440 /* Content : Checksum Register */
++#define OTP_PROG_CTRL 0x00001444 /* Programming Control Register */
++#define OTP_PROG_STATUS 0x00001448 /* Programming Status Register */
++#define OTP_PROG_PULSE 0x0000144c /* Program Pulse Width Register */
++#define OTP_VERIFY_PULSE 0x00001450 /* Verify Pulse Width Register */
++#define OTP_PROG_MASK 0x00001454 /* Program Mask Register */
++#define OTP_DATA_INPUT 0x00001458 /* Data Input Register */
++#define OTP_DATA_OUTPUT 0x0000145c /* Data Output Register */
++
++
++/****************************************************************************
++ * BCM70012_AES_TOP_AES
++ ***************************************************************************/
++#define AES_CONFIG_INFO 0x00001800 /* AES Configuration Information Register */
++#define AES_CMD 0x00001804 /* AES Command Register */
++#define AES_STATUS 0x00001808 /* AES Status Register */
++#define AES_EEPROM_CONFIG 0x0000180c /* AES EEPROM Configuration Register */
++#define AES_EEPROM_DATA_0 0x00001810 /* AES EEPROM Data Register 0 */
++#define AES_EEPROM_DATA_1 0x00001814 /* AES EEPROM Data Register 1 */
++#define AES_EEPROM_DATA_2 0x00001818 /* AES EEPROM Data Register 2 */
++#define AES_EEPROM_DATA_3 0x0000181c /* AES EEPROM Data Register 3 */
++
++
++/****************************************************************************
++ * BCM70012_DCI_TOP_DCI
++ ***************************************************************************/
++#define DCI_CMD 0x00001c00 /* DCI Command Register */
++#define DCI_STATUS 0x00001c04 /* DCI Status Register */
++#define DCI_DRAM_BASE_ADDR 0x00001c08 /* DRAM Base Address Register */
++#define DCI_FIRMWARE_ADDR 0x00001c0c /* Firmware Address Register */
++#define DCI_FIRMWARE_DATA 0x00001c10 /* Firmware Data Register */
++#define DCI_SIGNATURE_DATA_0 0x00001c14 /* Signature Data Register 0 */
++#define DCI_SIGNATURE_DATA_1 0x00001c18 /* Signature Data Register 1 */
++#define DCI_SIGNATURE_DATA_2 0x00001c1c /* Signature Data Register 2 */
++#define DCI_SIGNATURE_DATA_3 0x00001c20 /* Signature Data Register 3 */
++#define DCI_SIGNATURE_DATA_4 0x00001c24 /* Signature Data Register 4 */
++#define DCI_SIGNATURE_DATA_5 0x00001c28 /* Signature Data Register 5 */
++#define DCI_SIGNATURE_DATA_6 0x00001c2c /* Signature Data Register 6 */
++#define DCI_SIGNATURE_DATA_7 0x00001c30 /* Signature Data Register 7 */
++
++
++/****************************************************************************
++ * BCM70012_TGT_TOP_INTR
++ ***************************************************************************/
++/****************************************************************************
++ * INTR :: INTR_STATUS
++ ***************************************************************************/
++/* INTR :: INTR_STATUS :: reserved0 [31:26] */
++#define INTR_INTR_STATUS_reserved0_MASK 0xfc000000
++#define INTR_INTR_STATUS_reserved0_ALIGN 0
++#define INTR_INTR_STATUS_reserved0_BITS 6
++#define INTR_INTR_STATUS_reserved0_SHIFT 26
++
++/* INTR :: INTR_STATUS :: PCIE_TGT_CA_ATTN [25:25] */
++#define INTR_INTR_STATUS_PCIE_TGT_CA_ATTN_MASK 0x02000000
++#define INTR_INTR_STATUS_PCIE_TGT_CA_ATTN_ALIGN 0
++#define INTR_INTR_STATUS_PCIE_TGT_CA_ATTN_BITS 1
++#define INTR_INTR_STATUS_PCIE_TGT_CA_ATTN_SHIFT 25
++
++/* INTR :: INTR_STATUS :: PCIE_TGT_UR_ATTN [24:24] */
++#define INTR_INTR_STATUS_PCIE_TGT_UR_ATTN_MASK 0x01000000
++#define INTR_INTR_STATUS_PCIE_TGT_UR_ATTN_ALIGN 0
++#define INTR_INTR_STATUS_PCIE_TGT_UR_ATTN_BITS 1
++#define INTR_INTR_STATUS_PCIE_TGT_UR_ATTN_SHIFT 24
++
++/* INTR :: INTR_STATUS :: reserved1 [23:14] */
++#define INTR_INTR_STATUS_reserved1_MASK 0x00ffc000
++#define INTR_INTR_STATUS_reserved1_ALIGN 0
++#define INTR_INTR_STATUS_reserved1_BITS 10
++#define INTR_INTR_STATUS_reserved1_SHIFT 14
++
++/* INTR :: INTR_STATUS :: L1_UV_RX_DMA_ERR_INTR [13:13] */
++#define INTR_INTR_STATUS_L1_UV_RX_DMA_ERR_INTR_MASK 0x00002000
++#define INTR_INTR_STATUS_L1_UV_RX_DMA_ERR_INTR_ALIGN 0
++#define INTR_INTR_STATUS_L1_UV_RX_DMA_ERR_INTR_BITS 1
++#define INTR_INTR_STATUS_L1_UV_RX_DMA_ERR_INTR_SHIFT 13
++
++/* INTR :: INTR_STATUS :: L1_UV_RX_DMA_DONE_INTR [12:12] */
++#define INTR_INTR_STATUS_L1_UV_RX_DMA_DONE_INTR_MASK 0x00001000
++#define INTR_INTR_STATUS_L1_UV_RX_DMA_DONE_INTR_ALIGN 0
++#define INTR_INTR_STATUS_L1_UV_RX_DMA_DONE_INTR_BITS 1
++#define INTR_INTR_STATUS_L1_UV_RX_DMA_DONE_INTR_SHIFT 12
++
++/* INTR :: INTR_STATUS :: L1_Y_RX_DMA_ERR_INTR [11:11] */
++#define INTR_INTR_STATUS_L1_Y_RX_DMA_ERR_INTR_MASK 0x00000800
++#define INTR_INTR_STATUS_L1_Y_RX_DMA_ERR_INTR_ALIGN 0
++#define INTR_INTR_STATUS_L1_Y_RX_DMA_ERR_INTR_BITS 1
++#define INTR_INTR_STATUS_L1_Y_RX_DMA_ERR_INTR_SHIFT 11
++
++/* INTR :: INTR_STATUS :: L1_Y_RX_DMA_DONE_INTR [10:10] */
++#define INTR_INTR_STATUS_L1_Y_RX_DMA_DONE_INTR_MASK 0x00000400
++#define INTR_INTR_STATUS_L1_Y_RX_DMA_DONE_INTR_ALIGN 0
++#define INTR_INTR_STATUS_L1_Y_RX_DMA_DONE_INTR_BITS 1
++#define INTR_INTR_STATUS_L1_Y_RX_DMA_DONE_INTR_SHIFT 10
++
++/* INTR :: INTR_STATUS :: L1_TX_DMA_ERR_INTR [09:09] */
++#define INTR_INTR_STATUS_L1_TX_DMA_ERR_INTR_MASK 0x00000200
++#define INTR_INTR_STATUS_L1_TX_DMA_ERR_INTR_ALIGN 0
++#define INTR_INTR_STATUS_L1_TX_DMA_ERR_INTR_BITS 1
++#define INTR_INTR_STATUS_L1_TX_DMA_ERR_INTR_SHIFT 9
++
++/* INTR :: INTR_STATUS :: L1_TX_DMA_DONE_INTR [08:08] */
++#define INTR_INTR_STATUS_L1_TX_DMA_DONE_INTR_MASK 0x00000100
++#define INTR_INTR_STATUS_L1_TX_DMA_DONE_INTR_ALIGN 0
++#define INTR_INTR_STATUS_L1_TX_DMA_DONE_INTR_BITS 1
++#define INTR_INTR_STATUS_L1_TX_DMA_DONE_INTR_SHIFT 8
++
++/* INTR :: INTR_STATUS :: reserved2 [07:06] */
++#define INTR_INTR_STATUS_reserved2_MASK 0x000000c0
++#define INTR_INTR_STATUS_reserved2_ALIGN 0
++#define INTR_INTR_STATUS_reserved2_BITS 2
++#define INTR_INTR_STATUS_reserved2_SHIFT 6
++
++/* INTR :: INTR_STATUS :: L0_UV_RX_DMA_ERR_INTR [05:05] */
++#define INTR_INTR_STATUS_L0_UV_RX_DMA_ERR_INTR_MASK 0x00000020
++#define INTR_INTR_STATUS_L0_UV_RX_DMA_ERR_INTR_ALIGN 0
++#define INTR_INTR_STATUS_L0_UV_RX_DMA_ERR_INTR_BITS 1
++#define INTR_INTR_STATUS_L0_UV_RX_DMA_ERR_INTR_SHIFT 5
++
++/* INTR :: INTR_STATUS :: L0_UV_RX_DMA_DONE_INTR [04:04] */
++#define INTR_INTR_STATUS_L0_UV_RX_DMA_DONE_INTR_MASK 0x00000010
++#define INTR_INTR_STATUS_L0_UV_RX_DMA_DONE_INTR_ALIGN 0
++#define INTR_INTR_STATUS_L0_UV_RX_DMA_DONE_INTR_BITS 1
++#define INTR_INTR_STATUS_L0_UV_RX_DMA_DONE_INTR_SHIFT 4
++
++/* INTR :: INTR_STATUS :: L0_Y_RX_DMA_ERR_INTR [03:03] */
++#define INTR_INTR_STATUS_L0_Y_RX_DMA_ERR_INTR_MASK 0x00000008
++#define INTR_INTR_STATUS_L0_Y_RX_DMA_ERR_INTR_ALIGN 0
++#define INTR_INTR_STATUS_L0_Y_RX_DMA_ERR_INTR_BITS 1
++#define INTR_INTR_STATUS_L0_Y_RX_DMA_ERR_INTR_SHIFT 3
++
++/* INTR :: INTR_STATUS :: L0_Y_RX_DMA_DONE_INTR [02:02] */
++#define INTR_INTR_STATUS_L0_Y_RX_DMA_DONE_INTR_MASK 0x00000004
++#define INTR_INTR_STATUS_L0_Y_RX_DMA_DONE_INTR_ALIGN 0
++#define INTR_INTR_STATUS_L0_Y_RX_DMA_DONE_INTR_BITS 1
++#define INTR_INTR_STATUS_L0_Y_RX_DMA_DONE_INTR_SHIFT 2
++
++/* INTR :: INTR_STATUS :: L0_TX_DMA_ERR_INTR [01:01] */
++#define INTR_INTR_STATUS_L0_TX_DMA_ERR_INTR_MASK 0x00000002
++#define INTR_INTR_STATUS_L0_TX_DMA_ERR_INTR_ALIGN 0
++#define INTR_INTR_STATUS_L0_TX_DMA_ERR_INTR_BITS 1
++#define INTR_INTR_STATUS_L0_TX_DMA_ERR_INTR_SHIFT 1
++
++/* INTR :: INTR_STATUS :: L0_TX_DMA_DONE_INTR [00:00] */
++#define INTR_INTR_STATUS_L0_TX_DMA_DONE_INTR_MASK 0x00000001
++#define INTR_INTR_STATUS_L0_TX_DMA_DONE_INTR_ALIGN 0
++#define INTR_INTR_STATUS_L0_TX_DMA_DONE_INTR_BITS 1
++#define INTR_INTR_STATUS_L0_TX_DMA_DONE_INTR_SHIFT 0
++
++
++/****************************************************************************
++ * MISC1 :: TX_SW_DESC_LIST_CTRL_STS
++ ***************************************************************************/
++/* MISC1 :: TX_SW_DESC_LIST_CTRL_STS :: reserved0 [31:04] */
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_reserved0_MASK 0xfffffff0
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_reserved0_ALIGN 0
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_reserved0_BITS 28
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_reserved0_SHIFT 4
++
++/* MISC1 :: TX_SW_DESC_LIST_CTRL_STS :: DMA_DATA_SERV_PTR [03:03] */
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_DMA_DATA_SERV_PTR_MASK 0x00000008
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_DMA_DATA_SERV_PTR_ALIGN 0
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_DMA_DATA_SERV_PTR_BITS 1
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_DMA_DATA_SERV_PTR_SHIFT 3
++
++/* MISC1 :: TX_SW_DESC_LIST_CTRL_STS :: DESC_SERV_PTR [02:02] */
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_DESC_SERV_PTR_MASK 0x00000004
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_DESC_SERV_PTR_ALIGN 0
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_DESC_SERV_PTR_BITS 1
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_DESC_SERV_PTR_SHIFT 2
++
++/* MISC1 :: TX_SW_DESC_LIST_CTRL_STS :: TX_DMA_HALT_ON_ERROR [01:01] */
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_TX_DMA_HALT_ON_ERROR_MASK 0x00000002
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_TX_DMA_HALT_ON_ERROR_ALIGN 0
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_TX_DMA_HALT_ON_ERROR_BITS 1
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_TX_DMA_HALT_ON_ERROR_SHIFT 1
++
++/* MISC1 :: TX_SW_DESC_LIST_CTRL_STS :: TX_DMA_RUN_STOP [00:00] */
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_TX_DMA_RUN_STOP_MASK 0x00000001
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_TX_DMA_RUN_STOP_ALIGN 0
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_TX_DMA_RUN_STOP_BITS 1
++#define MISC1_TX_SW_DESC_LIST_CTRL_STS_TX_DMA_RUN_STOP_SHIFT 0
++
++
++/****************************************************************************
++ * MISC1 :: TX_DMA_ERROR_STATUS
++ ***************************************************************************/
++/* MISC1 :: TX_DMA_ERROR_STATUS :: reserved0 [31:10] */
++#define MISC1_TX_DMA_ERROR_STATUS_reserved0_MASK 0xfffffc00
++#define MISC1_TX_DMA_ERROR_STATUS_reserved0_ALIGN 0
++#define MISC1_TX_DMA_ERROR_STATUS_reserved0_BITS 22
++#define MISC1_TX_DMA_ERROR_STATUS_reserved0_SHIFT 10
++
++/* MISC1 :: TX_DMA_ERROR_STATUS :: TX_L1_DESC_TX_ABORT_ERRORS [09:09] */
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L1_DESC_TX_ABORT_ERRORS_MASK 0x00000200
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L1_DESC_TX_ABORT_ERRORS_ALIGN 0
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L1_DESC_TX_ABORT_ERRORS_BITS 1
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L1_DESC_TX_ABORT_ERRORS_SHIFT 9
++
++/* MISC1 :: TX_DMA_ERROR_STATUS :: reserved1 [08:08] */
++#define MISC1_TX_DMA_ERROR_STATUS_reserved1_MASK 0x00000100
++#define MISC1_TX_DMA_ERROR_STATUS_reserved1_ALIGN 0
++#define MISC1_TX_DMA_ERROR_STATUS_reserved1_BITS 1
++#define MISC1_TX_DMA_ERROR_STATUS_reserved1_SHIFT 8
++
++/* MISC1 :: TX_DMA_ERROR_STATUS :: TX_L0_DESC_TX_ABORT_ERRORS [07:07] */
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L0_DESC_TX_ABORT_ERRORS_MASK 0x00000080
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L0_DESC_TX_ABORT_ERRORS_ALIGN 0
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L0_DESC_TX_ABORT_ERRORS_BITS 1
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L0_DESC_TX_ABORT_ERRORS_SHIFT 7
++
++/* MISC1 :: TX_DMA_ERROR_STATUS :: reserved2 [06:06] */
++#define MISC1_TX_DMA_ERROR_STATUS_reserved2_MASK 0x00000040
++#define MISC1_TX_DMA_ERROR_STATUS_reserved2_ALIGN 0
++#define MISC1_TX_DMA_ERROR_STATUS_reserved2_BITS 1
++#define MISC1_TX_DMA_ERROR_STATUS_reserved2_SHIFT 6
++
++/* MISC1 :: TX_DMA_ERROR_STATUS :: TX_L1_DMA_DATA_TX_ABORT_ERRORS [05:05] */
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L1_DMA_DATA_TX_ABORT_ERRORS_MASK 0x00000020
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L1_DMA_DATA_TX_ABORT_ERRORS_ALIGN 0
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L1_DMA_DATA_TX_ABORT_ERRORS_BITS 1
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L1_DMA_DATA_TX_ABORT_ERRORS_SHIFT 5
++
++/* MISC1 :: TX_DMA_ERROR_STATUS :: TX_L1_FIFO_FULL_ERRORS [04:04] */
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L1_FIFO_FULL_ERRORS_MASK 0x00000010
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L1_FIFO_FULL_ERRORS_ALIGN 0
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L1_FIFO_FULL_ERRORS_BITS 1
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L1_FIFO_FULL_ERRORS_SHIFT 4
++
++/* MISC1 :: TX_DMA_ERROR_STATUS :: reserved3 [03:03] */
++#define MISC1_TX_DMA_ERROR_STATUS_reserved3_MASK 0x00000008
++#define MISC1_TX_DMA_ERROR_STATUS_reserved3_ALIGN 0
++#define MISC1_TX_DMA_ERROR_STATUS_reserved3_BITS 1
++#define MISC1_TX_DMA_ERROR_STATUS_reserved3_SHIFT 3
++
++/* MISC1 :: TX_DMA_ERROR_STATUS :: TX_L0_DMA_DATA_TX_ABORT_ERRORS [02:02] */
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L0_DMA_DATA_TX_ABORT_ERRORS_MASK 0x00000004
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L0_DMA_DATA_TX_ABORT_ERRORS_ALIGN 0
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L0_DMA_DATA_TX_ABORT_ERRORS_BITS 1
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L0_DMA_DATA_TX_ABORT_ERRORS_SHIFT 2
++
++/* MISC1 :: TX_DMA_ERROR_STATUS :: TX_L0_FIFO_FULL_ERRORS [01:01] */
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L0_FIFO_FULL_ERRORS_MASK 0x00000002
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L0_FIFO_FULL_ERRORS_ALIGN 0
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L0_FIFO_FULL_ERRORS_BITS 1
++#define MISC1_TX_DMA_ERROR_STATUS_TX_L0_FIFO_FULL_ERRORS_SHIFT 1
++
++/* MISC1 :: TX_DMA_ERROR_STATUS :: reserved4 [00:00] */
++#define MISC1_TX_DMA_ERROR_STATUS_reserved4_MASK 0x00000001
++#define MISC1_TX_DMA_ERROR_STATUS_reserved4_ALIGN 0
++#define MISC1_TX_DMA_ERROR_STATUS_reserved4_BITS 1
++#define MISC1_TX_DMA_ERROR_STATUS_reserved4_SHIFT 0
++
++
++/****************************************************************************
++ * MISC1 :: Y_RX_ERROR_STATUS
++ ***************************************************************************/
++/* MISC1 :: Y_RX_ERROR_STATUS :: reserved0 [31:14] */
++#define MISC1_Y_RX_ERROR_STATUS_reserved0_MASK 0xffffc000
++#define MISC1_Y_RX_ERROR_STATUS_reserved0_ALIGN 0
++#define MISC1_Y_RX_ERROR_STATUS_reserved0_BITS 18
++#define MISC1_Y_RX_ERROR_STATUS_reserved0_SHIFT 14
++
++/* MISC1 :: Y_RX_ERROR_STATUS :: RX_L1_UNDERRUN_ERROR [13:13] */
++#define MISC1_Y_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_MASK 0x00002000
++#define MISC1_Y_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_ALIGN 0
++#define MISC1_Y_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_BITS 1
++#define MISC1_Y_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_SHIFT 13
++
++/* MISC1 :: Y_RX_ERROR_STATUS :: RX_L1_OVERRUN_ERROR [12:12] */
++#define MISC1_Y_RX_ERROR_STATUS_RX_L1_OVERRUN_ERROR_MASK 0x00001000
++#define MISC1_Y_RX_ERROR_STATUS_RX_L1_OVERRUN_ERROR_ALIGN 0
++#define MISC1_Y_RX_ERROR_STATUS_RX_L1_OVERRUN_ERROR_BITS 1
++#define MISC1_Y_RX_ERROR_STATUS_RX_L1_OVERRUN_ERROR_SHIFT 12
++
++/* MISC1 :: Y_RX_ERROR_STATUS :: RX_L0_UNDERRUN_ERROR [11:11] */
++#define MISC1_Y_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_MASK 0x00000800
++#define MISC1_Y_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_ALIGN 0
++#define MISC1_Y_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_BITS 1
++#define MISC1_Y_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_SHIFT 11
++
++/* MISC1 :: Y_RX_ERROR_STATUS :: RX_L0_OVERRUN_ERROR [10:10] */
++#define MISC1_Y_RX_ERROR_STATUS_RX_L0_OVERRUN_ERROR_MASK 0x00000400
++#define MISC1_Y_RX_ERROR_STATUS_RX_L0_OVERRUN_ERROR_ALIGN 0
++#define MISC1_Y_RX_ERROR_STATUS_RX_L0_OVERRUN_ERROR_BITS 1
++#define MISC1_Y_RX_ERROR_STATUS_RX_L0_OVERRUN_ERROR_SHIFT 10
++
++/* MISC1 :: Y_RX_ERROR_STATUS :: RX_L1_DESC_TX_ABORT_ERRORS [09:09] */
++#define MISC1_Y_RX_ERROR_STATUS_RX_L1_DESC_TX_ABORT_ERRORS_MASK 0x00000200
++#define MISC1_Y_RX_ERROR_STATUS_RX_L1_DESC_TX_ABORT_ERRORS_ALIGN 0
++#define MISC1_Y_RX_ERROR_STATUS_RX_L1_DESC_TX_ABORT_ERRORS_BITS 1
++#define MISC1_Y_RX_ERROR_STATUS_RX_L1_DESC_TX_ABORT_ERRORS_SHIFT 9
++
++/* MISC1 :: Y_RX_ERROR_STATUS :: reserved1 [08:08] */
++#define MISC1_Y_RX_ERROR_STATUS_reserved1_MASK 0x00000100
++#define MISC1_Y_RX_ERROR_STATUS_reserved1_ALIGN 0
++#define MISC1_Y_RX_ERROR_STATUS_reserved1_BITS 1
++#define MISC1_Y_RX_ERROR_STATUS_reserved1_SHIFT 8
++
++/* MISC1 :: Y_RX_ERROR_STATUS :: RX_L0_DESC_TX_ABORT_ERRORS [07:07] */
++#define MISC1_Y_RX_ERROR_STATUS_RX_L0_DESC_TX_ABORT_ERRORS_MASK 0x00000080
++#define MISC1_Y_RX_ERROR_STATUS_RX_L0_DESC_TX_ABORT_ERRORS_ALIGN 0
++#define MISC1_Y_RX_ERROR_STATUS_RX_L0_DESC_TX_ABORT_ERRORS_BITS 1
++#define MISC1_Y_RX_ERROR_STATUS_RX_L0_DESC_TX_ABORT_ERRORS_SHIFT 7
++
++/* MISC1 :: Y_RX_ERROR_STATUS :: reserved2 [06:05] */
++#define MISC1_Y_RX_ERROR_STATUS_reserved2_MASK 0x00000060
++#define MISC1_Y_RX_ERROR_STATUS_reserved2_ALIGN 0
++#define MISC1_Y_RX_ERROR_STATUS_reserved2_BITS 2
++#define MISC1_Y_RX_ERROR_STATUS_reserved2_SHIFT 5
++
++/* MISC1 :: Y_RX_ERROR_STATUS :: RX_L1_FIFO_FULL_ERRORS [04:04] */
++#define MISC1_Y_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_MASK 0x00000010
++#define MISC1_Y_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_ALIGN 0
++#define MISC1_Y_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_BITS 1
++#define MISC1_Y_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_SHIFT 4
++
++/* MISC1 :: Y_RX_ERROR_STATUS :: reserved3 [03:02] */
++#define MISC1_Y_RX_ERROR_STATUS_reserved3_MASK 0x0000000c
++#define MISC1_Y_RX_ERROR_STATUS_reserved3_ALIGN 0
++#define MISC1_Y_RX_ERROR_STATUS_reserved3_BITS 2
++#define MISC1_Y_RX_ERROR_STATUS_reserved3_SHIFT 2
++
++/* MISC1 :: Y_RX_ERROR_STATUS :: RX_L0_FIFO_FULL_ERRORS [01:01] */
++#define MISC1_Y_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_MASK 0x00000002
++#define MISC1_Y_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_ALIGN 0
++#define MISC1_Y_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_BITS 1
++#define MISC1_Y_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_SHIFT 1
++
++/* MISC1 :: Y_RX_ERROR_STATUS :: reserved4 [00:00] */
++#define MISC1_Y_RX_ERROR_STATUS_reserved4_MASK 0x00000001
++#define MISC1_Y_RX_ERROR_STATUS_reserved4_ALIGN 0
++#define MISC1_Y_RX_ERROR_STATUS_reserved4_BITS 1
++#define MISC1_Y_RX_ERROR_STATUS_reserved4_SHIFT 0
++
++
++/****************************************************************************
++ * MISC1 :: UV_RX_ERROR_STATUS
++ ***************************************************************************/
++/* MISC1 :: UV_RX_ERROR_STATUS :: reserved0 [31:14] */
++#define MISC1_UV_RX_ERROR_STATUS_reserved0_MASK 0xffffc000
++#define MISC1_UV_RX_ERROR_STATUS_reserved0_ALIGN 0
++#define MISC1_UV_RX_ERROR_STATUS_reserved0_BITS 18
++#define MISC1_UV_RX_ERROR_STATUS_reserved0_SHIFT 14
++
++/* MISC1 :: UV_RX_ERROR_STATUS :: RX_L1_UNDERRUN_ERROR [13:13] */
++#define MISC1_UV_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_MASK 0x00002000
++#define MISC1_UV_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_ALIGN 0
++#define MISC1_UV_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_BITS 1
++#define MISC1_UV_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_SHIFT 13
++
++/* MISC1 :: UV_RX_ERROR_STATUS :: RX_L1_OVERRUN_ERROR [12:12] */
++#define MISC1_UV_RX_ERROR_STATUS_RX_L1_OVERRUN_ERROR_MASK 0x00001000
++#define MISC1_UV_RX_ERROR_STATUS_RX_L1_OVERRUN_ERROR_ALIGN 0
++#define MISC1_UV_RX_ERROR_STATUS_RX_L1_OVERRUN_ERROR_BITS 1
++#define MISC1_UV_RX_ERROR_STATUS_RX_L1_OVERRUN_ERROR_SHIFT 12
++
++/* MISC1 :: UV_RX_ERROR_STATUS :: RX_L0_UNDERRUN_ERROR [11:11] */
++#define MISC1_UV_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_MASK 0x00000800
++#define MISC1_UV_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_ALIGN 0
++#define MISC1_UV_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_BITS 1
++#define MISC1_UV_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_SHIFT 11
++
++/* MISC1 :: UV_RX_ERROR_STATUS :: RX_L0_OVERRUN_ERROR [10:10] */
++#define MISC1_UV_RX_ERROR_STATUS_RX_L0_OVERRUN_ERROR_MASK 0x00000400
++#define MISC1_UV_RX_ERROR_STATUS_RX_L0_OVERRUN_ERROR_ALIGN 0
++#define MISC1_UV_RX_ERROR_STATUS_RX_L0_OVERRUN_ERROR_BITS 1
++#define MISC1_UV_RX_ERROR_STATUS_RX_L0_OVERRUN_ERROR_SHIFT 10
++
++/* MISC1 :: UV_RX_ERROR_STATUS :: RX_L1_DESC_TX_ABORT_ERRORS [09:09] */
++#define MISC1_UV_RX_ERROR_STATUS_RX_L1_DESC_TX_ABORT_ERRORS_MASK 0x00000200
++#define MISC1_UV_RX_ERROR_STATUS_RX_L1_DESC_TX_ABORT_ERRORS_ALIGN 0
++#define MISC1_UV_RX_ERROR_STATUS_RX_L1_DESC_TX_ABORT_ERRORS_BITS 1
++#define MISC1_UV_RX_ERROR_STATUS_RX_L1_DESC_TX_ABORT_ERRORS_SHIFT 9
++
++/* MISC1 :: UV_RX_ERROR_STATUS :: reserved1 [08:08] */
++#define MISC1_UV_RX_ERROR_STATUS_reserved1_MASK 0x00000100
++#define MISC1_UV_RX_ERROR_STATUS_reserved1_ALIGN 0
++#define MISC1_UV_RX_ERROR_STATUS_reserved1_BITS 1
++#define MISC1_UV_RX_ERROR_STATUS_reserved1_SHIFT 8
++
++/* MISC1 :: UV_RX_ERROR_STATUS :: RX_L0_DESC_TX_ABORT_ERRORS [07:07] */
++#define MISC1_UV_RX_ERROR_STATUS_RX_L0_DESC_TX_ABORT_ERRORS_MASK 0x00000080
++#define MISC1_UV_RX_ERROR_STATUS_RX_L0_DESC_TX_ABORT_ERRORS_ALIGN 0
++#define MISC1_UV_RX_ERROR_STATUS_RX_L0_DESC_TX_ABORT_ERRORS_BITS 1
++#define MISC1_UV_RX_ERROR_STATUS_RX_L0_DESC_TX_ABORT_ERRORS_SHIFT 7
++
++/* MISC1 :: UV_RX_ERROR_STATUS :: reserved2 [06:05] */
++#define MISC1_UV_RX_ERROR_STATUS_reserved2_MASK 0x00000060
++#define MISC1_UV_RX_ERROR_STATUS_reserved2_ALIGN 0
++#define MISC1_UV_RX_ERROR_STATUS_reserved2_BITS 2
++#define MISC1_UV_RX_ERROR_STATUS_reserved2_SHIFT 5
++
++/* MISC1 :: UV_RX_ERROR_STATUS :: RX_L1_FIFO_FULL_ERRORS [04:04] */
++#define MISC1_UV_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_MASK 0x00000010
++#define MISC1_UV_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_ALIGN 0
++#define MISC1_UV_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_BITS 1
++#define MISC1_UV_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_SHIFT 4
++
++/* MISC1 :: UV_RX_ERROR_STATUS :: reserved3 [03:02] */
++#define MISC1_UV_RX_ERROR_STATUS_reserved3_MASK 0x0000000c
++#define MISC1_UV_RX_ERROR_STATUS_reserved3_ALIGN 0
++#define MISC1_UV_RX_ERROR_STATUS_reserved3_BITS 2
++#define MISC1_UV_RX_ERROR_STATUS_reserved3_SHIFT 2
++
++/* MISC1 :: UV_RX_ERROR_STATUS :: RX_L0_FIFO_FULL_ERRORS [01:01] */
++#define MISC1_UV_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_MASK 0x00000002
++#define MISC1_UV_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_ALIGN 0
++#define MISC1_UV_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_BITS 1
++#define MISC1_UV_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_SHIFT 1
++
++/* MISC1 :: UV_RX_ERROR_STATUS :: reserved4 [00:00] */
++#define MISC1_UV_RX_ERROR_STATUS_reserved4_MASK 0x00000001
++#define MISC1_UV_RX_ERROR_STATUS_reserved4_ALIGN 0
++#define MISC1_UV_RX_ERROR_STATUS_reserved4_BITS 1
++#define MISC1_UV_RX_ERROR_STATUS_reserved4_SHIFT 0
++
++/****************************************************************************
++ * Datatype Definitions.
++ ***************************************************************************/
++#endif /* #ifndef MACFILE_H__ */
++
++/* End of File */
++
+diff --git a/drivers/staging/crystalhd/crystalhd_cmds.c b/drivers/staging/crystalhd/crystalhd_cmds.c
+new file mode 100644
+index 0000000..39c641d
+--- /dev/null
++++ b/drivers/staging/crystalhd/crystalhd_cmds.c
+@@ -0,0 +1,1058 @@
++/***************************************************************************
++ * Copyright (c) 2005-2009, Broadcom Corporation.
++ *
++ * Name: crystalhd_cmds . c
++ *
++ * Description:
++ * BCM70010 Linux driver user command interfaces.
++ *
++ * HISTORY:
++ *
++ **********************************************************************
++ * This file is part of the crystalhd device driver.
++ *
++ * This driver is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, version 2 of the License.
++ *
++ * This driver 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 driver. If not, see <http://www.gnu.org/licenses/>.
++ **********************************************************************/
++
++#include "crystalhd_cmds.h"
++#include "crystalhd_hw.h"
++
++static struct crystalhd_user *bc_cproc_get_uid(struct crystalhd_cmd *ctx)
++{
++ struct crystalhd_user *user = NULL;
++ int i;
++
++ for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
++ if (!ctx->user[i].in_use) {
++ user = &ctx->user[i];
++ break;
++ }
++ }
++
++ return user;
++}
++
++static int bc_cproc_get_user_count(struct crystalhd_cmd *ctx)
++{
++ int i, count = 0;
++
++ for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
++ if (ctx->user[i].in_use)
++ count++;
++ }
++
++ return count;
++}
++
++static void bc_cproc_mark_pwr_state(struct crystalhd_cmd *ctx)
++{
++ int i;
++
++ for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
++ if (!ctx->user[i].in_use)
++ continue;
++ if (ctx->user[i].mode == DTS_DIAG_MODE ||
++ ctx->user[i].mode == DTS_PLAYBACK_MODE) {
++ ctx->pwr_state_change = 1;
++ break;
++ }
++ }
++}
++
++static BC_STATUS bc_cproc_notify_mode(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata)
++{
++ int rc = 0, i = 0;
++
++ if (!ctx || !idata) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return BC_STS_INV_ARG;
++ }
++
++ if (ctx->user[idata->u_id].mode != DTS_MODE_INV) {
++ BCMLOG_ERR("Close the handle first..\n");
++ return BC_STS_ERR_USAGE;
++ }
++ if (idata->udata.u.NotifyMode.Mode == DTS_MONITOR_MODE) {
++ ctx->user[idata->u_id].mode = idata->udata.u.NotifyMode.Mode;
++ return BC_STS_SUCCESS;
++ }
++ if (ctx->state != BC_LINK_INVALID) {
++ BCMLOG_ERR("Link invalid state %d \n", ctx->state);
++ return BC_STS_ERR_USAGE;
++ }
++ /* Check for duplicate playback sessions..*/
++ for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
++ if (ctx->user[i].mode == DTS_DIAG_MODE ||
++ ctx->user[i].mode == DTS_PLAYBACK_MODE) {
++ BCMLOG_ERR("multiple playback sessions are not "
++ "supported..\n");
++ return BC_STS_ERR_USAGE;
++ }
++ }
++ ctx->cin_wait_exit = 0;
++ ctx->user[idata->u_id].mode = idata->udata.u.NotifyMode.Mode;
++ /* Setup mmap pool for uaddr sgl mapping..*/
++ rc = crystalhd_create_dio_pool(ctx->adp, BC_LINK_MAX_SGLS);
++ if (rc)
++ return BC_STS_ERROR;
++
++ /* Setup Hardware DMA rings */
++ return crystalhd_hw_setup_dma_rings(&ctx->hw_ctx);
++}
++
++static BC_STATUS bc_cproc_get_version(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata)
++{
++
++ if (!ctx || !idata) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return BC_STS_INV_ARG;
++ }
++ idata->udata.u.VerInfo.DriverMajor = crystalhd_kmod_major;
++ idata->udata.u.VerInfo.DriverMinor = crystalhd_kmod_minor;
++ idata->udata.u.VerInfo.DriverRevision = crystalhd_kmod_rev;
++ return BC_STS_SUCCESS;
++}
++
++
++static BC_STATUS bc_cproc_get_hwtype(struct crystalhd_cmd *ctx, crystalhd_ioctl_data *idata)
++{
++ if (!ctx || !idata) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return BC_STS_INV_ARG;
++ }
++
++ crystalhd_pci_cfg_rd(ctx->adp, 0, 2,
++ (uint32_t *)&idata->udata.u.hwType.PciVenId);
++ crystalhd_pci_cfg_rd(ctx->adp, 2, 2,
++ (uint32_t *)&idata->udata.u.hwType.PciDevId);
++ crystalhd_pci_cfg_rd(ctx->adp, 8, 1,
++ (uint32_t *)&idata->udata.u.hwType.HwRev);
++
++ return BC_STS_SUCCESS;
++}
++
++static BC_STATUS bc_cproc_reg_rd(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata)
++{
++ if (!ctx || !idata)
++ return BC_STS_INV_ARG;
++ idata->udata.u.regAcc.Value = bc_dec_reg_rd(ctx->adp,
++ idata->udata.u.regAcc.Offset);
++ return BC_STS_SUCCESS;
++}
++
++static BC_STATUS bc_cproc_reg_wr(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata)
++{
++ if (!ctx || !idata)
++ return BC_STS_INV_ARG;
++
++ bc_dec_reg_wr(ctx->adp, idata->udata.u.regAcc.Offset,
++ idata->udata.u.regAcc.Value);
++
++ return BC_STS_SUCCESS;
++}
++
++static BC_STATUS bc_cproc_link_reg_rd(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata)
++{
++ if (!ctx || !idata)
++ return BC_STS_INV_ARG;
++
++ idata->udata.u.regAcc.Value = crystalhd_reg_rd(ctx->adp,
++ idata->udata.u.regAcc.Offset);
++ return BC_STS_SUCCESS;
++}
++
++static BC_STATUS bc_cproc_link_reg_wr(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata)
++{
++ if (!ctx || !idata)
++ return BC_STS_INV_ARG;
++
++ crystalhd_reg_wr(ctx->adp, idata->udata.u.regAcc.Offset,
++ idata->udata.u.regAcc.Value);
++
++ return BC_STS_SUCCESS;
++}
++
++static BC_STATUS bc_cproc_mem_rd(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata)
++{
++ BC_STATUS sts = BC_STS_SUCCESS;
++
++ if (!ctx || !idata || !idata->add_cdata)
++ return BC_STS_INV_ARG;
++
++ if (idata->udata.u.devMem.NumDwords > (idata->add_cdata_sz / 4)) {
++ BCMLOG_ERR("insufficient buffer\n");
++ return BC_STS_INV_ARG;
++ }
++ sts = crystalhd_mem_rd(ctx->adp, idata->udata.u.devMem.StartOff,
++ idata->udata.u.devMem.NumDwords,
++ (uint32_t *)idata->add_cdata);
++ return sts;
++
++}
++
++static BC_STATUS bc_cproc_mem_wr(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata)
++{
++ BC_STATUS sts = BC_STS_SUCCESS;
++
++ if (!ctx || !idata || !idata->add_cdata)
++ return BC_STS_INV_ARG;
++
++ if (idata->udata.u.devMem.NumDwords > (idata->add_cdata_sz / 4)) {
++ BCMLOG_ERR("insufficient buffer\n");
++ return BC_STS_INV_ARG;
++ }
++
++ sts = crystalhd_mem_wr(ctx->adp, idata->udata.u.devMem.StartOff,
++ idata->udata.u.devMem.NumDwords,
++ (uint32_t *)idata->add_cdata);
++ return sts;
++}
++
++static BC_STATUS bc_cproc_cfg_rd(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata)
++{
++ uint32_t ix, cnt, off, len;
++ BC_STATUS sts = BC_STS_SUCCESS;
++ uint32_t *temp;
++
++ if (!ctx || !idata)
++ return BC_STS_INV_ARG;
++
++ temp = (uint32_t *) idata->udata.u.pciCfg.pci_cfg_space;
++ off = idata->udata.u.pciCfg.Offset;
++ len = idata->udata.u.pciCfg.Size;
++
++ if (len <= 4)
++ return crystalhd_pci_cfg_rd(ctx->adp, off, len, temp);
++
++ /* Truncate to dword alignment..*/
++ len = 4;
++ cnt = idata->udata.u.pciCfg.Size / len;
++ for (ix = 0; ix < cnt; ix++) {
++ sts = crystalhd_pci_cfg_rd(ctx->adp, off, len, &temp[ix]);
++ if (sts != BC_STS_SUCCESS) {
++ BCMLOG_ERR("config read : %d\n", sts);
++ return sts;
++ }
++ off += len;
++ }
++
++ return sts;
++}
++
++static BC_STATUS bc_cproc_cfg_wr(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata)
++{
++ uint32_t ix, cnt, off, len;
++ BC_STATUS sts = BC_STS_SUCCESS;
++ uint32_t *temp;
++
++ if (!ctx || !idata)
++ return BC_STS_INV_ARG;
++
++ temp = (uint32_t *) idata->udata.u.pciCfg.pci_cfg_space;
++ off = idata->udata.u.pciCfg.Offset;
++ len = idata->udata.u.pciCfg.Size;
++
++ if (len <= 4)
++ return crystalhd_pci_cfg_wr(ctx->adp, off, len, temp[0]);
++
++ /* Truncate to dword alignment..*/
++ len = 4;
++ cnt = idata->udata.u.pciCfg.Size / len;
++ for (ix = 0; ix < cnt; ix++) {
++ sts = crystalhd_pci_cfg_wr(ctx->adp, off, len, temp[ix]);
++ if (sts != BC_STS_SUCCESS) {
++ BCMLOG_ERR("config write : %d\n", sts);
++ return sts;
++ }
++ off += len;
++ }
++
++ return sts;
++}
++
++static BC_STATUS bc_cproc_download_fw(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata)
++{
++ BC_STATUS sts = BC_STS_SUCCESS;
++
++ if (!ctx || !idata || !idata->add_cdata || !idata->add_cdata_sz) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return BC_STS_INV_ARG;
++ }
++
++ if (ctx->state != BC_LINK_INVALID) {
++ BCMLOG_ERR("Link invalid state %d \n", ctx->state);
++ return BC_STS_ERR_USAGE;
++ }
++
++ sts = crystalhd_download_fw(ctx->adp, (uint8_t *)idata->add_cdata,
++ idata->add_cdata_sz);
++
++ if (sts != BC_STS_SUCCESS) {
++ BCMLOG_ERR("Firmware Download Failure!! - %d\n", sts);
++ } else
++ ctx->state |= BC_LINK_INIT;
++
++ return sts;
++}
++
++/*
++ * We use the FW_CMD interface to sync up playback state with application
++ * and firmware. This function will perform the required pre and post
++ * processing of the Firmware commands.
++ *
++ * Pause -
++ * Disable capture after decoder pause.
++ * Resume -
++ * First enable capture and issue decoder resume command.
++ * Flush -
++ * Abort pending input transfers and issue decoder flush command.
++ *
++ */
++static BC_STATUS bc_cproc_do_fw_cmd(struct crystalhd_cmd *ctx, crystalhd_ioctl_data *idata)
++{
++ BC_STATUS sts;
++ uint32_t *cmd;
++
++ if (!(ctx->state & BC_LINK_INIT)) {
++ BCMLOG_ERR("Link invalid state %d \n", ctx->state);
++ return BC_STS_ERR_USAGE;
++ }
++
++ cmd = idata->udata.u.fwCmd.cmd;
++
++ /* Pre-Process */
++ if (cmd[0] == eCMD_C011_DEC_CHAN_PAUSE) {
++ if (!cmd[3]) {
++ ctx->state &= ~BC_LINK_PAUSED;
++ crystalhd_hw_unpause(&ctx->hw_ctx);
++ }
++ } else if (cmd[0] == eCMD_C011_DEC_CHAN_FLUSH) {
++ BCMLOG(BCMLOG_INFO, "Flush issued\n");
++ if (cmd[3])
++ ctx->cin_wait_exit = 1;
++ }
++
++ sts = crystalhd_do_fw_cmd(&ctx->hw_ctx, &idata->udata.u.fwCmd);
++
++ if (sts != BC_STS_SUCCESS) {
++ BCMLOG(BCMLOG_INFO, "fw cmd %x failed\n", cmd[0]);
++ return sts;
++ }
++
++ /* Post-Process */
++ if (cmd[0] == eCMD_C011_DEC_CHAN_PAUSE) {
++ if (cmd[3]) {
++ ctx->state |= BC_LINK_PAUSED;
++ crystalhd_hw_pause(&ctx->hw_ctx);
++ }
++ }
++
++ return sts;
++}
++
++static void bc_proc_in_completion(crystalhd_dio_req *dio_hnd,
++ wait_queue_head_t *event, BC_STATUS sts)
++{
++ if (!dio_hnd || !event) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return;
++ }
++ if (sts == BC_STS_IO_USER_ABORT)
++ return;
++
++ dio_hnd->uinfo.comp_sts = sts;
++ dio_hnd->uinfo.ev_sts = 1;
++ crystalhd_set_event(event);
++}
++
++static BC_STATUS bc_cproc_codein_sleep(struct crystalhd_cmd *ctx)
++{
++ wait_queue_head_t sleep_ev;
++ int rc = 0;
++
++ if (ctx->state & BC_LINK_SUSPEND)
++ return BC_STS_IO_USER_ABORT;
++
++ if (ctx->cin_wait_exit) {
++ ctx->cin_wait_exit = 0;
++ return BC_STS_CMD_CANCELLED;
++ }
++ crystalhd_create_event(&sleep_ev);
++ crystalhd_wait_on_event(&sleep_ev, 0, 100, rc, 0);
++ if (rc == -EINTR)
++ return BC_STS_IO_USER_ABORT;
++
++ return BC_STS_SUCCESS;
++}
++
++static BC_STATUS bc_cproc_hw_txdma(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata,
++ crystalhd_dio_req *dio)
++{
++ uint32_t tx_listid = 0;
++ BC_STATUS sts = BC_STS_SUCCESS;
++ wait_queue_head_t event;
++ int rc = 0;
++
++ if (!ctx || !idata || !dio) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return BC_STS_INV_ARG;
++ }
++
++ crystalhd_create_event(&event);
++
++ ctx->tx_list_id = 0;
++ /* msleep_interruptible(2000); */
++ sts = crystalhd_hw_post_tx(&ctx->hw_ctx, dio, bc_proc_in_completion,
++ &event, &tx_listid,
++ idata->udata.u.ProcInput.Encrypted);
++
++ while (sts == BC_STS_BUSY) {
++ sts = bc_cproc_codein_sleep(ctx);
++ if (sts != BC_STS_SUCCESS)
++ break;
++ sts = crystalhd_hw_post_tx(&ctx->hw_ctx, dio,
++ bc_proc_in_completion,
++ &event, &tx_listid,
++ idata->udata.u.ProcInput.Encrypted);
++ }
++ if (sts != BC_STS_SUCCESS) {
++ BCMLOG(BCMLOG_DBG, "_hw_txdma returning sts:%d\n", sts);
++ return sts;
++ }
++ if (ctx->cin_wait_exit)
++ ctx->cin_wait_exit = 0;
++
++ ctx->tx_list_id = tx_listid;
++
++ /* _post() succeeded.. wait for the completion. */
++ crystalhd_wait_on_event(&event, (dio->uinfo.ev_sts), 3000, rc, 0);
++ ctx->tx_list_id = 0;
++ if (!rc) {
++ return dio->uinfo.comp_sts;
++ } else if (rc == -EBUSY) {
++ BCMLOG(BCMLOG_DBG, "_tx_post() T/O \n");
++ sts = BC_STS_TIMEOUT;
++ } else if (rc == -EINTR) {
++ BCMLOG(BCMLOG_DBG, "Tx Wait Signal int.\n");
++ sts = BC_STS_IO_USER_ABORT;
++ } else {
++ sts = BC_STS_IO_ERROR;
++ }
++
++ /* We are cancelling the IO from the same context as the _post().
++ * so no need to wait on the event again.. the return itself
++ * ensures the release of our resources.
++ */
++ crystalhd_hw_cancel_tx(&ctx->hw_ctx, tx_listid);
++
++ return sts;
++}
++
++/* Helper function to check on user buffers */
++static BC_STATUS bc_cproc_check_inbuffs(bool pin, void *ubuff, uint32_t ub_sz,
++ uint32_t uv_off, bool en_422)
++{
++ if (!ubuff || !ub_sz) {
++ BCMLOG_ERR("%s->Invalid Arg %p %x\n",
++ ((pin) ? "TX" : "RX"), ubuff, ub_sz);
++ return BC_STS_INV_ARG;
++ }
++
++ /* Check for alignment */
++ if (((uintptr_t)ubuff) & 0x03) {
++ BCMLOG_ERR("%s-->Un-aligned address not implemented yet.. %p \n",
++ ((pin) ? "TX" : "RX"), ubuff);
++ return BC_STS_NOT_IMPL;
++ }
++ if (pin)
++ return BC_STS_SUCCESS;
++
++ if (!en_422 && !uv_off) {
++ BCMLOG_ERR("Need UV offset for 420 mode.\n");
++ return BC_STS_INV_ARG;
++ }
++
++ if (en_422 && uv_off) {
++ BCMLOG_ERR("UV offset in 422 mode ??\n");
++ return BC_STS_INV_ARG;
++ }
++
++ return BC_STS_SUCCESS;
++}
++
++static BC_STATUS bc_cproc_proc_input(struct crystalhd_cmd *ctx, crystalhd_ioctl_data *idata)
++{
++ void *ubuff;
++ uint32_t ub_sz;
++ crystalhd_dio_req *dio_hnd = NULL;
++ BC_STATUS sts = BC_STS_SUCCESS;
++
++ if (!ctx || !idata) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return BC_STS_INV_ARG;
++ }
++
++ ubuff = idata->udata.u.ProcInput.pDmaBuff;
++ ub_sz = idata->udata.u.ProcInput.BuffSz;
++
++ sts = bc_cproc_check_inbuffs(1, ubuff, ub_sz, 0, 0);
++ if (sts != BC_STS_SUCCESS)
++ return sts;
++
++ sts = crystalhd_map_dio(ctx->adp, ubuff, ub_sz, 0, 0, 1, &dio_hnd);
++ if (sts != BC_STS_SUCCESS) {
++ BCMLOG_ERR("dio map - %d \n", sts);
++ return sts;
++ }
++
++ if (!dio_hnd)
++ return BC_STS_ERROR;
++
++ sts = bc_cproc_hw_txdma(ctx, idata, dio_hnd);
++
++ crystalhd_unmap_dio(ctx->adp, dio_hnd);
++
++ return sts;
++}
++
++static BC_STATUS bc_cproc_add_cap_buff(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata)
++{
++ void *ubuff;
++ uint32_t ub_sz, uv_off;
++ bool en_422;
++ crystalhd_dio_req *dio_hnd = NULL;
++ BC_STATUS sts = BC_STS_SUCCESS;
++
++ if (!ctx || !idata) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return BC_STS_INV_ARG;
++ }
++
++ ubuff = idata->udata.u.RxBuffs.YuvBuff;
++ ub_sz = idata->udata.u.RxBuffs.YuvBuffSz;
++ uv_off = idata->udata.u.RxBuffs.UVbuffOffset;
++ en_422 = idata->udata.u.RxBuffs.b422Mode;
++
++ sts = bc_cproc_check_inbuffs(0, ubuff, ub_sz, uv_off, en_422);
++ if (sts != BC_STS_SUCCESS)
++ return sts;
++
++ sts = crystalhd_map_dio(ctx->adp, ubuff, ub_sz, uv_off,
++ en_422, 0, &dio_hnd);
++ if (sts != BC_STS_SUCCESS) {
++ BCMLOG_ERR("dio map - %d \n", sts);
++ return sts;
++ }
++
++ if (!dio_hnd)
++ return BC_STS_ERROR;
++
++ sts = crystalhd_hw_add_cap_buffer(&ctx->hw_ctx, dio_hnd, (ctx->state == BC_LINK_READY));
++ if ((sts != BC_STS_SUCCESS) && (sts != BC_STS_BUSY)) {
++ crystalhd_unmap_dio(ctx->adp, dio_hnd);
++ return sts;
++ }
++
++ return BC_STS_SUCCESS;
++}
++
++static BC_STATUS bc_cproc_fmt_change(struct crystalhd_cmd *ctx,
++ crystalhd_dio_req *dio)
++{
++ BC_STATUS sts = BC_STS_SUCCESS;
++
++ sts = crystalhd_hw_add_cap_buffer(&ctx->hw_ctx, dio, 0);
++ if (sts != BC_STS_SUCCESS)
++ return sts;
++
++ ctx->state |= BC_LINK_FMT_CHG;
++ if (ctx->state == BC_LINK_READY)
++ sts = crystalhd_hw_start_capture(&ctx->hw_ctx);
++
++ return sts;
++}
++
++static BC_STATUS bc_cproc_fetch_frame(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata)
++{
++ crystalhd_dio_req *dio = NULL;
++ BC_STATUS sts = BC_STS_SUCCESS;
++ BC_DEC_OUT_BUFF *frame;
++
++ if (!ctx || !idata) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return BC_STS_INV_ARG;
++ }
++
++ if (!(ctx->state & BC_LINK_CAP_EN)) {
++ BCMLOG(BCMLOG_DBG, "Capture not enabled..%x\n", ctx->state);
++ return BC_STS_ERR_USAGE;
++ }
++
++ frame = &idata->udata.u.DecOutData;
++
++ sts = crystalhd_hw_get_cap_buffer(&ctx->hw_ctx, &frame->PibInfo, &dio);
++ if (sts != BC_STS_SUCCESS)
++ return (ctx->state & BC_LINK_SUSPEND) ? BC_STS_IO_USER_ABORT : sts;
++
++ frame->Flags = dio->uinfo.comp_flags;
++
++ if (frame->Flags & COMP_FLAG_FMT_CHANGE)
++ return bc_cproc_fmt_change(ctx, dio);
++
++ frame->OutPutBuffs.YuvBuff = dio->uinfo.xfr_buff;
++ frame->OutPutBuffs.YuvBuffSz = dio->uinfo.xfr_len;
++ frame->OutPutBuffs.UVbuffOffset = dio->uinfo.uv_offset;
++ frame->OutPutBuffs.b422Mode = dio->uinfo.b422mode;
++
++ frame->OutPutBuffs.YBuffDoneSz = dio->uinfo.y_done_sz;
++ frame->OutPutBuffs.UVBuffDoneSz = dio->uinfo.uv_done_sz;
++
++ crystalhd_unmap_dio(ctx->adp, dio);
++
++ return BC_STS_SUCCESS;
++}
++
++static BC_STATUS bc_cproc_start_capture(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata)
++{
++ ctx->state |= BC_LINK_CAP_EN;
++ if (ctx->state == BC_LINK_READY)
++ return crystalhd_hw_start_capture(&ctx->hw_ctx);
++
++ return BC_STS_SUCCESS;
++}
++
++static BC_STATUS bc_cproc_flush_cap_buffs(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata)
++{
++ crystalhd_dio_req *dio = NULL;
++ BC_STATUS sts = BC_STS_SUCCESS;
++ BC_DEC_OUT_BUFF *frame;
++ uint32_t count;
++
++ if (!ctx || !idata) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return BC_STS_INV_ARG;
++ }
++
++ if (!(ctx->state & BC_LINK_CAP_EN))
++ return BC_STS_ERR_USAGE;
++
++ /* We should ack flush even when we are in paused/suspend state */
++ if (!(ctx->state & BC_LINK_READY))
++ return crystalhd_hw_stop_capture(&ctx->hw_ctx);
++
++ ctx->state &= ~(BC_LINK_CAP_EN|BC_LINK_FMT_CHG);
++
++ frame = &idata->udata.u.DecOutData;
++ for (count = 0; count < BC_RX_LIST_CNT; count++) {
++
++ sts = crystalhd_hw_get_cap_buffer(&ctx->hw_ctx, &frame->PibInfo, &dio);
++ if (sts != BC_STS_SUCCESS)
++ break;
++
++ crystalhd_unmap_dio(ctx->adp, dio);
++ }
++
++ return crystalhd_hw_stop_capture(&ctx->hw_ctx);
++}
++
++static BC_STATUS bc_cproc_get_stats(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata)
++{
++ BC_DTS_STATS *stats;
++ struct crystalhd_hw_stats hw_stats;
++
++ if (!ctx || !idata) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return BC_STS_INV_ARG;
++ }
++
++ crystalhd_hw_stats(&ctx->hw_ctx, &hw_stats);
++
++ stats = &idata->udata.u.drvStat;
++ stats->drvRLL = hw_stats.rdyq_count;
++ stats->drvFLL = hw_stats.freeq_count;
++ stats->DrvTotalFrmDropped = hw_stats.rx_errors;
++ stats->DrvTotalHWErrs = hw_stats.rx_errors + hw_stats.tx_errors;
++ stats->intCount = hw_stats.num_interrupts;
++ stats->DrvIgnIntrCnt = hw_stats.num_interrupts -
++ hw_stats.dev_interrupts;
++ stats->TxFifoBsyCnt = hw_stats.cin_busy;
++ stats->pauseCount = hw_stats.pause_cnt;
++
++ if (ctx->pwr_state_change)
++ stats->pwr_state_change = 1;
++ if (ctx->state & BC_LINK_PAUSED)
++ stats->DrvPauseTime = 1;
++
++ return BC_STS_SUCCESS;
++}
++
++static BC_STATUS bc_cproc_reset_stats(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata)
++{
++ crystalhd_hw_stats(&ctx->hw_ctx, NULL);
++
++ return BC_STS_SUCCESS;
++}
++
++static BC_STATUS bc_cproc_chg_clk(struct crystalhd_cmd *ctx,
++ crystalhd_ioctl_data *idata)
++{
++ BC_CLOCK *clock;
++ uint32_t oldClk;
++ BC_STATUS sts = BC_STS_SUCCESS;
++
++ if (!ctx || !idata) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return BC_STS_INV_ARG;
++ }
++
++ clock = &idata->udata.u.clockValue;
++ oldClk = ctx->hw_ctx.core_clock_mhz;
++ ctx->hw_ctx.core_clock_mhz = clock->clk;
++
++ if (ctx->state & BC_LINK_READY) {
++ sts = crystalhd_hw_set_core_clock(&ctx->hw_ctx);
++ if (sts == BC_STS_CLK_NOCHG)
++ ctx->hw_ctx.core_clock_mhz = oldClk;
++ }
++
++ clock->clk = ctx->hw_ctx.core_clock_mhz;
++
++ return sts;
++}
++
++/*=============== Cmd Proc Table.. ======================================*/
++static const crystalhd_cmd_tbl_t g_crystalhd_cproc_tbl[] = {
++ { BCM_IOC_GET_VERSION, bc_cproc_get_version, 0},
++ { BCM_IOC_GET_HWTYPE, bc_cproc_get_hwtype, 0},
++ { BCM_IOC_REG_RD, bc_cproc_reg_rd, 0},
++ { BCM_IOC_REG_WR, bc_cproc_reg_wr, 0},
++ { BCM_IOC_FPGA_RD, bc_cproc_link_reg_rd, 0},
++ { BCM_IOC_FPGA_WR, bc_cproc_link_reg_wr, 0},
++ { BCM_IOC_MEM_RD, bc_cproc_mem_rd, 0},
++ { BCM_IOC_MEM_WR, bc_cproc_mem_wr, 0},
++ { BCM_IOC_RD_PCI_CFG, bc_cproc_cfg_rd, 0},
++ { BCM_IOC_WR_PCI_CFG, bc_cproc_cfg_wr, 1},
++ { BCM_IOC_FW_DOWNLOAD, bc_cproc_download_fw, 1},
++ { BCM_IOC_FW_CMD, bc_cproc_do_fw_cmd, 1},
++ { BCM_IOC_PROC_INPUT, bc_cproc_proc_input, 1},
++ { BCM_IOC_ADD_RXBUFFS, bc_cproc_add_cap_buff, 1},
++ { BCM_IOC_FETCH_RXBUFF, bc_cproc_fetch_frame, 1},
++ { BCM_IOC_START_RX_CAP, bc_cproc_start_capture, 1},
++ { BCM_IOC_FLUSH_RX_CAP, bc_cproc_flush_cap_buffs, 1},
++ { BCM_IOC_GET_DRV_STAT, bc_cproc_get_stats, 0},
++ { BCM_IOC_RST_DRV_STAT, bc_cproc_reset_stats, 0},
++ { BCM_IOC_NOTIFY_MODE, bc_cproc_notify_mode, 0},
++ { BCM_IOC_CHG_CLK, bc_cproc_chg_clk, 0},
++ { BCM_IOC_END, NULL},
++};
++
++/*=============== Cmd Proc Functions.. ===================================*/
++
++/**
++ * crystalhd_suspend - Power management suspend request.
++ * @ctx: Command layer context.
++ * @idata: Iodata - required for internal use.
++ *
++ * Return:
++ * status
++ *
++ * 1. Set the state to Suspend.
++ * 2. Flush the Rx Buffers it will unmap all the buffers and
++ * stop the RxDMA engine.
++ * 3. Cancel The TX Io and Stop Dma Engine.
++ * 4. Put the DDR in to deep sleep.
++ * 5. Stop the hardware putting it in to Reset State.
++ *
++ * Current gstreamer frame work does not provide any power management
++ * related notification to user mode decoder plug-in. As a work-around
++ * we pass on the power mangement notification to our plug-in by completing
++ * all outstanding requests with BC_STS_IO_USER_ABORT return code.
++ */
++BC_STATUS crystalhd_suspend(struct crystalhd_cmd *ctx, crystalhd_ioctl_data *idata)
++{
++ BC_STATUS sts = BC_STS_SUCCESS;
++
++ if (!ctx || !idata) {
++ BCMLOG_ERR("Invalid Parameters\n");
++ return BC_STS_ERROR;
++ }
++
++ if (ctx->state & BC_LINK_SUSPEND)
++ return BC_STS_SUCCESS;
++
++ if (ctx->state == BC_LINK_INVALID) {
++ BCMLOG(BCMLOG_DBG, "Nothing To Do Suspend Success\n");
++ return BC_STS_SUCCESS;
++ }
++
++ ctx->state |= BC_LINK_SUSPEND;
++
++ bc_cproc_mark_pwr_state(ctx);
++
++ if (ctx->state & BC_LINK_CAP_EN) {
++ sts = bc_cproc_flush_cap_buffs(ctx, idata);
++ if (sts != BC_STS_SUCCESS)
++ return sts;
++ }
++
++ if (ctx->tx_list_id) {
++ sts = crystalhd_hw_cancel_tx(&ctx->hw_ctx, ctx->tx_list_id);
++ if (sts != BC_STS_SUCCESS)
++ return sts;
++ }
++
++ sts = crystalhd_hw_suspend(&ctx->hw_ctx);
++ if (sts != BC_STS_SUCCESS)
++ return sts;
++
++ BCMLOG(BCMLOG_DBG, "BCM70012 suspend success\n");
++
++ return BC_STS_SUCCESS;
++}
++
++/**
++ * crystalhd_resume - Resume frame capture.
++ * @ctx: Command layer contextx.
++ *
++ * Return:
++ * status
++ *
++ *
++ * Resume frame capture.
++ *
++ * PM_Resume can't resume the playback state back to pre-suspend state
++ * because we don't keep video clip related information within driver.
++ * To get back to the pre-suspend state App will re-open the device and
++ * start a new playback session from the pre-suspend clip position.
++ *
++ */
++BC_STATUS crystalhd_resume(struct crystalhd_cmd *ctx)
++{
++ BCMLOG(BCMLOG_DBG, "crystalhd_resume Success %x\n", ctx->state);
++
++ bc_cproc_mark_pwr_state(ctx);
++
++ return BC_STS_SUCCESS;
++}
++
++/**
++ * crystalhd_user_open - Create application handle.
++ * @ctx: Command layer contextx.
++ * @user_ctx: User ID context.
++ *
++ * Return:
++ * status
++ *
++ * Creates an application specific UID and allocates
++ * application specific resources. HW layer initialization
++ * is done for the first open request.
++ */
++BC_STATUS crystalhd_user_open(struct crystalhd_cmd *ctx,
++ struct crystalhd_user **user_ctx)
++{
++ struct crystalhd_user *uc;
++
++ if (!ctx || !user_ctx) {
++ BCMLOG_ERR("Invalid arg..\n");
++ return BC_STS_INV_ARG;
++ }
++
++ uc = bc_cproc_get_uid(ctx);
++ if (!uc) {
++ BCMLOG(BCMLOG_INFO, "No free user context...\n");
++ return BC_STS_BUSY;
++ }
++
++ BCMLOG(BCMLOG_INFO, "Opening new user[%x] handle\n", uc->uid);
++
++ crystalhd_hw_open(&ctx->hw_ctx, ctx->adp);
++
++ uc->in_use = 1;
++
++ *user_ctx = uc;
++
++ return BC_STS_SUCCESS;
++}
++
++/**
++ * crystalhd_user_close - Close application handle.
++ * @ctx: Command layer contextx.
++ * @uc: User ID context.
++ *
++ * Return:
++ * status
++ *
++ * Closer aplication handle and release app specific
++ * resources.
++ */
++BC_STATUS crystalhd_user_close(struct crystalhd_cmd *ctx, struct crystalhd_user *uc)
++{
++ uint32_t mode = uc->mode;
++
++ ctx->user[uc->uid].mode = DTS_MODE_INV;
++ ctx->user[uc->uid].in_use = 0;
++ ctx->cin_wait_exit = 1;
++ ctx->pwr_state_change = 0;
++
++ BCMLOG(BCMLOG_INFO, "Closing user[%x] handle\n", uc->uid);
++
++ if ((mode == DTS_DIAG_MODE) || (mode == DTS_PLAYBACK_MODE)) {
++ crystalhd_hw_free_dma_rings(&ctx->hw_ctx);
++ crystalhd_destroy_dio_pool(ctx->adp);
++ } else if (bc_cproc_get_user_count(ctx)) {
++ return BC_STS_SUCCESS;
++ }
++
++ crystalhd_hw_close(&ctx->hw_ctx);
++
++ ctx->state = BC_LINK_INVALID;
++
++ return BC_STS_SUCCESS;
++}
++
++/**
++ * crystalhd_setup_cmd_context - Setup Command layer resources.
++ * @ctx: Command layer contextx.
++ * @adp: Adapter context
++ *
++ * Return:
++ * status
++ *
++ * Called at the time of driver load.
++ */
++BC_STATUS crystalhd_setup_cmd_context(struct crystalhd_cmd *ctx,
++ struct crystalhd_adp *adp)
++{
++ int i = 0;
++
++ if (!ctx || !adp) {
++ BCMLOG_ERR("Invalid arg!!\n");
++ return BC_STS_INV_ARG;
++ }
++
++ if (ctx->adp)
++ BCMLOG(BCMLOG_DBG, "Resetting Cmd context delete missing..\n");
++
++ ctx->adp = adp;
++ for (i = 0; i < BC_LINK_MAX_OPENS; i++) {
++ ctx->user[i].uid = i;
++ ctx->user[i].in_use = 0;
++ ctx->user[i].mode = DTS_MODE_INV;
++ }
++
++ /*Open and Close the Hardware to put it in to sleep state*/
++ crystalhd_hw_open(&ctx->hw_ctx, ctx->adp);
++ crystalhd_hw_close(&ctx->hw_ctx);
++ return BC_STS_SUCCESS;
++}
++
++/**
++ * crystalhd_delete_cmd_context - Release Command layer resources.
++ * @ctx: Command layer contextx.
++ *
++ * Return:
++ * status
++ *
++ * Called at the time of driver un-load.
++ */
++BC_STATUS crystalhd_delete_cmd_context(struct crystalhd_cmd *ctx)
++{
++ BCMLOG(BCMLOG_DBG, "Deleting Command context..\n");
++
++ ctx->adp = NULL;
++
++ return BC_STS_SUCCESS;
++}
++
++/**
++ * crystalhd_get_cmd_proc - Cproc table lookup.
++ * @ctx: Command layer contextx.
++ * @cmd: IOCTL command code.
++ * @uc: User ID context.
++ *
++ * Return:
++ * command proc function pointer
++ *
++ * This function checks the process context, application's
++ * mode of operation and returns the function pointer
++ * from the cproc table.
++ */
++crystalhd_cmd_proc crystalhd_get_cmd_proc(struct crystalhd_cmd *ctx, uint32_t cmd,
++ struct crystalhd_user *uc)
++{
++ crystalhd_cmd_proc cproc = NULL;
++ unsigned int i, tbl_sz;
++
++ if (!ctx) {
++ BCMLOG_ERR("Invalid arg.. Cmd[%d]\n", cmd);
++ return NULL;
++ }
++
++ if ((cmd != BCM_IOC_GET_DRV_STAT) && (ctx->state & BC_LINK_SUSPEND)) {
++ BCMLOG_ERR("Invalid State [suspend Set].. Cmd[%d]\n", cmd);
++ return NULL;
++ }
++
++ tbl_sz = sizeof(g_crystalhd_cproc_tbl) / sizeof(crystalhd_cmd_tbl_t);
++ for (i = 0; i < tbl_sz; i++) {
++ if (g_crystalhd_cproc_tbl[i].cmd_id == cmd) {
++ if ((uc->mode == DTS_MONITOR_MODE) &&
++ (g_crystalhd_cproc_tbl[i].block_mon)) {
++ BCMLOG(BCMLOG_INFO, "Blocking cmd %d \n", cmd);
++ break;
++ }
++ cproc = g_crystalhd_cproc_tbl[i].cmd_proc;
++ break;
++ }
++ }
++
++ return cproc;
++}
++
++/**
++ * crystalhd_cmd_interrupt - ISR entry point
++ * @ctx: Command layer contextx.
++ *
++ * Return:
++ * TRUE: If interrupt from bcm70012 device.
++ *
++ *
++ * ISR entry point from OS layer.
++ */
++bool crystalhd_cmd_interrupt(struct crystalhd_cmd *ctx)
++{
++ if (!ctx) {
++ BCMLOG_ERR("Invalid arg..\n");
++ return 0;
++ }
++
++ return crystalhd_hw_interrupt(ctx->adp, &ctx->hw_ctx);
++}
+diff --git a/drivers/staging/crystalhd/crystalhd_cmds.h b/drivers/staging/crystalhd/crystalhd_cmds.h
+new file mode 100644
+index 0000000..6b290ae
+--- /dev/null
++++ b/drivers/staging/crystalhd/crystalhd_cmds.h
+@@ -0,0 +1,88 @@
++/***************************************************************************
++ * Copyright (c) 2005-2009, Broadcom Corporation.
++ *
++ * Name: crystalhd_cmds . h
++ *
++ * Description:
++ * BCM70010 Linux driver user command interfaces.
++ *
++ * HISTORY:
++ *
++ **********************************************************************
++ * This file is part of the crystalhd device driver.
++ *
++ * This driver is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, version 2 of the License.
++ *
++ * This driver 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 driver. If not, see <http://www.gnu.org/licenses/>.
++ **********************************************************************/
++
++#ifndef _CRYSTALHD_CMDS_H_
++#define _CRYSTALHD_CMDS_H_
++
++/*
++ * NOTE:: This is the main interface file between the Linux layer
++ * and the harware layer. This file will use the definitions
++ * from _dts_glob and dts_defs etc.. which are defined for
++ * windows.
++ */
++#include "crystalhd_misc.h"
++#include "crystalhd_hw.h"
++
++enum _crystalhd_state{
++ BC_LINK_INVALID = 0x00,
++ BC_LINK_INIT = 0x01,
++ BC_LINK_CAP_EN = 0x02,
++ BC_LINK_FMT_CHG = 0x04,
++ BC_LINK_SUSPEND = 0x10,
++ BC_LINK_PAUSED = 0x20,
++ BC_LINK_READY = (BC_LINK_INIT | BC_LINK_CAP_EN | BC_LINK_FMT_CHG),
++};
++
++struct crystalhd_user {
++ uint32_t uid;
++ uint32_t in_use;
++ uint32_t mode;
++};
++
++#define DTS_MODE_INV (-1)
++
++struct crystalhd_cmd {
++ uint32_t state;
++ struct crystalhd_adp *adp;
++ struct crystalhd_user user[BC_LINK_MAX_OPENS];
++
++ spinlock_t ctx_lock;
++ uint32_t tx_list_id;
++ uint32_t cin_wait_exit;
++ uint32_t pwr_state_change;
++ struct crystalhd_hw hw_ctx;
++};
++
++typedef BC_STATUS (*crystalhd_cmd_proc)(struct crystalhd_cmd *, crystalhd_ioctl_data *);
++
++typedef struct _crystalhd_cmd_tbl {
++ uint32_t cmd_id;
++ const crystalhd_cmd_proc cmd_proc;
++ uint32_t block_mon;
++} crystalhd_cmd_tbl_t;
++
++
++BC_STATUS crystalhd_suspend(struct crystalhd_cmd *ctx, crystalhd_ioctl_data *idata);
++BC_STATUS crystalhd_resume(struct crystalhd_cmd *ctx);
++crystalhd_cmd_proc crystalhd_get_cmd_proc(struct crystalhd_cmd *ctx, uint32_t cmd,
++ struct crystalhd_user *uc);
++BC_STATUS crystalhd_user_open(struct crystalhd_cmd *ctx, struct crystalhd_user **user_ctx);
++BC_STATUS crystalhd_user_close(struct crystalhd_cmd *ctx, struct crystalhd_user *uc);
++BC_STATUS crystalhd_setup_cmd_context(struct crystalhd_cmd *ctx, struct crystalhd_adp *adp);
++BC_STATUS crystalhd_delete_cmd_context(struct crystalhd_cmd *ctx);
++bool crystalhd_cmd_interrupt(struct crystalhd_cmd *ctx);
++
++#endif
+diff --git a/drivers/staging/crystalhd/crystalhd_fw_if.h b/drivers/staging/crystalhd/crystalhd_fw_if.h
+new file mode 100644
+index 0000000..261cd19
+--- /dev/null
++++ b/drivers/staging/crystalhd/crystalhd_fw_if.h
+@@ -0,0 +1,369 @@
++/***************************************************************************
++ * Copyright (c) 2005-2009, Broadcom Corporation.
++ *
++ * Name: crystalhd_fw_if . h
++ *
++ * Description:
++ * BCM70012 Firmware interface definitions.
++ *
++ * HISTORY:
++ *
++ **********************************************************************
++ * This file is part of the crystalhd device driver.
++ *
++ * This driver is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, version 2 of the License.
++ *
++ * This driver 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 driver. If not, see <http://www.gnu.org/licenses/>.
++ **********************************************************************/
++
++#ifndef _CRYSTALHD_FW_IF_H_
++#define _CRYSTALHD_FW_IF_H_
++
++/* TBD: Pull in only required defs into this file.. */
++
++
++
++/* User Data Header */
++typedef struct user_data {
++ struct user_data *next;
++ uint32_t type;
++ uint32_t size;
++} UD_HDR;
++
++
++
++/*------------------------------------------------------*
++ * MPEG Extension to the PPB *
++ *------------------------------------------------------*/
++typedef struct {
++ uint32_t to_be_defined;
++ uint32_t valid;
++
++ /* Always valid, defaults to picture size if no
++ sequence display extension in the stream. */
++ uint32_t display_horizontal_size;
++ uint32_t display_vertical_size;
++
++ /* MPEG_VALID_PANSCAN
++ Offsets are a copy values from the MPEG stream. */
++ uint32_t offset_count;
++ int32_t horizontal_offset[3];
++ int32_t vertical_offset[3];
++
++ /* MPEG_VALID_USERDATA
++ User data is in the form of a linked list. */
++ int32_t userDataSize;
++ UD_HDR *userData;
++
++} PPB_MPEG;
++
++
++/*------------------------------------------------------*
++ * VC1 Extension to the PPB *
++ *------------------------------------------------------*/
++typedef struct {
++ uint32_t to_be_defined;
++ uint32_t valid;
++
++ /* Always valid, defaults to picture size if no
++ sequence display extension in the stream. */
++ uint32_t display_horizontal_size;
++ uint32_t display_vertical_size;
++
++ /* VC1 pan scan windows */
++ uint32_t num_panscan_windows;
++ int32_t ps_horiz_offset[4];
++ int32_t ps_vert_offset[4];
++ int32_t ps_width[4];
++ int32_t ps_height[4];
++
++ /* VC1_VALID_USERDATA
++ User data is in the form of a linked list. */
++ int32_t userDataSize;
++ UD_HDR *userData;
++
++} PPB_VC1;
++
++/*------------------------------------------------------*
++ * H.264 Extension to the PPB *
++ *------------------------------------------------------*/
++
++/**
++ * @brief Film grain SEI message.
++ *
++ * Content of the film grain SEI message.
++ */
++
++/* maximum number of model-values as for Thomson spec(standard says 5) */
++#define MAX_FGT_MODEL_VALUE (3)
++
++/* maximum number of intervals(as many as 256 intervals?) */
++#define MAX_FGT_VALUE_INTERVAL (256)
++
++typedef struct FGT_SEI {
++ struct FGT_SEI *next;
++ unsigned char model_values[3][MAX_FGT_VALUE_INTERVAL][MAX_FGT_MODEL_VALUE];
++ unsigned char upper_bound[3][MAX_FGT_VALUE_INTERVAL];
++ unsigned char lower_bound[3][MAX_FGT_VALUE_INTERVAL];
++
++ unsigned char cancel_flag; /* Cancel flag: 1 no film grain. */
++ unsigned char model_id; /* Model id. */
++
++ /* +unused SE based on Thomson spec */
++ unsigned char color_desc_flag; /* Separate color descrition flag. */
++ unsigned char bit_depth_luma; /* Bit depth luma minus 8. */
++ unsigned char bit_depth_chroma; /* Bit depth chroma minus 8. */
++ unsigned char full_range_flag; /* Full range flag. */
++ unsigned char color_primaries; /* Color primaries. */
++ unsigned char transfer_charact; /* Transfer characteristics. */
++ unsigned char matrix_coeff; /*< Matrix coefficients. */
++ /* -unused SE based on Thomson spec */
++
++ unsigned char blending_mode_id; /* Blending mode. */
++ unsigned char log2_scale_factor; /* Log2 scale factor (2-7). */
++ unsigned char comp_flag[3]; /* Components [0,2] parameters present flag. */
++ unsigned char num_intervals_minus1[3]; /* Number of intensity level intervals. */
++ unsigned char num_model_values[3]; /* Number of model values. */
++ uint16_t repetition_period; /* Repetition period (0-16384) */
++
++} FGT_SEI;
++
++typedef struct {
++ /* 'valid' specifies which fields (or sets of
++ * fields) below are valid. If the corresponding
++ * bit in 'valid' is NOT set then that field(s)
++ * is (are) not initialized. */
++ uint32_t valid;
++
++ int32_t poc_top; /* POC for Top Field/Frame */
++ int32_t poc_bottom; /* POC for Bottom Field */
++ uint32_t idr_pic_id;
++
++ /* H264_VALID_PANSCAN */
++ uint32_t pan_scan_count;
++ int32_t pan_scan_left[3];
++ int32_t pan_scan_right[3];
++ int32_t pan_scan_top[3];
++ int32_t pan_scan_bottom[3];
++
++ /* H264_VALID_CT_TYPE */
++ uint32_t ct_type_count;
++ uint32_t ct_type[3];
++
++ /* H264_VALID_SPS_CROP */
++ int32_t sps_crop_left;
++ int32_t sps_crop_right;
++ int32_t sps_crop_top;
++ int32_t sps_crop_bottom;
++
++ /* H264_VALID_VUI */
++ uint32_t chroma_top;
++ uint32_t chroma_bottom;
++
++ /* H264_VALID_USER */
++ uint32_t user_data_size;
++ UD_HDR *user_data;
++
++ /* H264 VALID FGT */
++ FGT_SEI *pfgt;
++
++} PPB_H264;
++
++typedef struct {
++ /* Common fields. */
++ uint32_t picture_number; /* Ordinal display number */
++ uint32_t video_buffer; /* Video (picbuf) number */
++ uint32_t video_address; /* Address of picbuf Y */
++ uint32_t video_address_uv; /* Address of picbuf UV */
++ uint32_t video_stripe; /* Picbuf stripe */
++ uint32_t video_width; /* Picbuf width */
++ uint32_t video_height; /* Picbuf height */
++
++ uint32_t channel_id; /* Decoder channel ID */
++ uint32_t status; /* reserved */
++ uint32_t width; /* pixels */
++ uint32_t height; /* pixels */
++ uint32_t chroma_format; /* see above */
++ uint32_t pulldown; /* see above */
++ uint32_t flags; /* see above */
++ uint32_t pts; /* 32 LSBs of PTS */
++ uint32_t protocol; /* protocolXXX (above) */
++
++ uint32_t frame_rate; /* see above */
++ uint32_t matrix_coeff; /* see above */
++ uint32_t aspect_ratio; /* see above */
++ uint32_t colour_primaries; /* see above */
++ uint32_t transfer_char; /* see above */
++ uint32_t pcr_offset; /* 45kHz if PCR type; else 27MHz */
++ uint32_t n_drop; /* Number of pictures to be dropped */
++
++ uint32_t custom_aspect_ratio_width_height;
++ /* upper 16-bits is Y and lower 16-bits is X */
++
++ uint32_t picture_tag; /* Indexing tag from BUD packets */
++ uint32_t picture_done_payload;
++ uint32_t picture_meta_payload;
++ uint32_t reserved[1];
++
++ /* Protocol-specific extensions. */
++ union {
++ PPB_H264 h264;
++ PPB_MPEG mpeg;
++ PPB_VC1 vc1;
++ } other;
++
++} PPB;
++
++typedef struct {
++ uint32_t bFormatChange;
++ uint32_t resolution;
++ uint32_t channelId;
++ uint32_t ppbPtr;
++ int32_t ptsStcOffset;
++ uint32_t zeroPanscanValid;
++ uint32_t dramOutBufAddr;
++ uint32_t yComponent;
++ PPB ppb;
++
++} C011_PIB;
++
++
++
++typedef struct {
++ uint32_t command;
++ uint32_t sequence;
++ uint32_t status;
++ uint32_t picBuf;
++ uint32_t picRelBuf;
++ uint32_t picInfoDeliveryQ;
++ uint32_t picInfoReleaseQ;
++ uint32_t channelStatus;
++ uint32_t userDataDeliveryQ;
++ uint32_t userDataReleaseQ;
++ uint32_t transportStreamCaptureAddr;
++ uint32_t asyncEventQ;
++
++} DecRspChannelStartVideo;
++
++#define eCMD_C011_CMD_BASE (0x73763000)
++
++/* host commands */
++typedef enum {
++ eCMD_TS_GET_NEXT_PIC = 0x7376F100, /* debug get next picture */
++ eCMD_TS_GET_LAST_PIC = 0x7376F102, /* debug get last pic status */
++ eCMD_TS_READ_WRITE_MEM = 0x7376F104, /* debug read write memory */
++
++ /* New API commands */
++ /* General commands */
++ eCMD_C011_INIT = eCMD_C011_CMD_BASE + 0x01,
++ eCMD_C011_RESET = eCMD_C011_CMD_BASE + 0x02,
++ eCMD_C011_SELF_TEST = eCMD_C011_CMD_BASE + 0x03,
++ eCMD_C011_GET_VERSION = eCMD_C011_CMD_BASE + 0x04,
++ eCMD_C011_GPIO = eCMD_C011_CMD_BASE + 0x05,
++ eCMD_C011_DEBUG_SETUP = eCMD_C011_CMD_BASE + 0x06,
++
++ /* Decoding commands */
++ eCMD_C011_DEC_CHAN_OPEN = eCMD_C011_CMD_BASE + 0x100,
++ eCMD_C011_DEC_CHAN_CLOSE = eCMD_C011_CMD_BASE + 0x101,
++ eCMD_C011_DEC_CHAN_ACTIVATE = eCMD_C011_CMD_BASE + 0x102,
++ eCMD_C011_DEC_CHAN_STATUS = eCMD_C011_CMD_BASE + 0x103,
++ eCMD_C011_DEC_CHAN_FLUSH = eCMD_C011_CMD_BASE + 0x104,
++ eCMD_C011_DEC_CHAN_TRICK_PLAY = eCMD_C011_CMD_BASE + 0x105,
++ eCMD_C011_DEC_CHAN_TS_PIDS = eCMD_C011_CMD_BASE + 0x106,
++ eCMD_C011_DEC_CHAN_PS_STREAM_ID = eCMD_C011_CMD_BASE + 0x107,
++ eCMD_C011_DEC_CHAN_INPUT_PARAMS = eCMD_C011_CMD_BASE + 0x108,
++ eCMD_C011_DEC_CHAN_VIDEO_OUTPUT = eCMD_C011_CMD_BASE + 0x109,
++ eCMD_C011_DEC_CHAN_OUTPUT_FORMAT = eCMD_C011_CMD_BASE + 0x10A,
++ eCMD_C011_DEC_CHAN_SCALING_FILTERS = eCMD_C011_CMD_BASE + 0x10B,
++ eCMD_C011_DEC_CHAN_OSD_MODE = eCMD_C011_CMD_BASE + 0x10D,
++ eCMD_C011_DEC_CHAN_DROP = eCMD_C011_CMD_BASE + 0x10E,
++ eCMD_C011_DEC_CHAN_RELEASE = eCMD_C011_CMD_BASE + 0x10F,
++ eCMD_C011_DEC_CHAN_STREAM_SETTINGS = eCMD_C011_CMD_BASE + 0x110,
++ eCMD_C011_DEC_CHAN_PAUSE_OUTPUT = eCMD_C011_CMD_BASE + 0x111,
++ eCMD_C011_DEC_CHAN_CHANGE = eCMD_C011_CMD_BASE + 0x112,
++ eCMD_C011_DEC_CHAN_SET_STC = eCMD_C011_CMD_BASE + 0x113,
++ eCMD_C011_DEC_CHAN_SET_PTS = eCMD_C011_CMD_BASE + 0x114,
++ eCMD_C011_DEC_CHAN_CC_MODE = eCMD_C011_CMD_BASE + 0x115,
++ eCMD_C011_DEC_CREATE_AUDIO_CONTEXT = eCMD_C011_CMD_BASE + 0x116,
++ eCMD_C011_DEC_COPY_AUDIO_CONTEXT = eCMD_C011_CMD_BASE + 0x117,
++ eCMD_C011_DEC_DELETE_AUDIO_CONTEXT = eCMD_C011_CMD_BASE + 0x118,
++ eCMD_C011_DEC_CHAN_SET_DECYPTION = eCMD_C011_CMD_BASE + 0x119,
++ eCMD_C011_DEC_CHAN_START_VIDEO = eCMD_C011_CMD_BASE + 0x11A,
++ eCMD_C011_DEC_CHAN_STOP_VIDEO = eCMD_C011_CMD_BASE + 0x11B,
++ eCMD_C011_DEC_CHAN_PIC_CAPTURE = eCMD_C011_CMD_BASE + 0x11C,
++ eCMD_C011_DEC_CHAN_PAUSE = eCMD_C011_CMD_BASE + 0x11D,
++ eCMD_C011_DEC_CHAN_PAUSE_STATE = eCMD_C011_CMD_BASE + 0x11E,
++ eCMD_C011_DEC_CHAN_SET_SLOWM_RATE = eCMD_C011_CMD_BASE + 0x11F,
++ eCMD_C011_DEC_CHAN_GET_SLOWM_RATE = eCMD_C011_CMD_BASE + 0x120,
++ eCMD_C011_DEC_CHAN_SET_FF_RATE = eCMD_C011_CMD_BASE + 0x121,
++ eCMD_C011_DEC_CHAN_GET_FF_RATE = eCMD_C011_CMD_BASE + 0x122,
++ eCMD_C011_DEC_CHAN_FRAME_ADVANCE = eCMD_C011_CMD_BASE + 0x123,
++ eCMD_C011_DEC_CHAN_SET_SKIP_PIC_MODE = eCMD_C011_CMD_BASE + 0x124,
++ eCMD_C011_DEC_CHAN_GET_SKIP_PIC_MODE = eCMD_C011_CMD_BASE + 0x125,
++ eCMD_C011_DEC_CHAN_FILL_PIC_BUF = eCMD_C011_CMD_BASE + 0x126,
++ eCMD_C011_DEC_CHAN_SET_CONTINUITY_CHECK = eCMD_C011_CMD_BASE + 0x127,
++ eCMD_C011_DEC_CHAN_GET_CONTINUITY_CHECK = eCMD_C011_CMD_BASE + 0x128,
++ eCMD_C011_DEC_CHAN_SET_BRCM_TRICK_MODE = eCMD_C011_CMD_BASE + 0x129,
++ eCMD_C011_DEC_CHAN_GET_BRCM_TRICK_MODE = eCMD_C011_CMD_BASE + 0x12A,
++ eCMD_C011_DEC_CHAN_REVERSE_FIELD_STATUS = eCMD_C011_CMD_BASE + 0x12B,
++ eCMD_C011_DEC_CHAN_I_PICTURE_FOUND = eCMD_C011_CMD_BASE + 0x12C,
++ eCMD_C011_DEC_CHAN_SET_PARAMETER = eCMD_C011_CMD_BASE + 0x12D,
++ eCMD_C011_DEC_CHAN_SET_USER_DATA_MODE = eCMD_C011_CMD_BASE + 0x12E,
++ eCMD_C011_DEC_CHAN_SET_PAUSE_DISPLAY_MODE = eCMD_C011_CMD_BASE + 0x12F,
++ eCMD_C011_DEC_CHAN_SET_SLOW_DISPLAY_MODE = eCMD_C011_CMD_BASE + 0x130,
++ eCMD_C011_DEC_CHAN_SET_FF_DISPLAY_MODE = eCMD_C011_CMD_BASE + 0x131,
++ eCMD_C011_DEC_CHAN_SET_DISPLAY_TIMING_MODE = eCMD_C011_CMD_BASE + 0x132,
++ eCMD_C011_DEC_CHAN_SET_DISPLAY_MODE = eCMD_C011_CMD_BASE + 0x133,
++ eCMD_C011_DEC_CHAN_GET_DISPLAY_MODE = eCMD_C011_CMD_BASE + 0x134,
++ eCMD_C011_DEC_CHAN_SET_REVERSE_FIELD = eCMD_C011_CMD_BASE + 0x135,
++ eCMD_C011_DEC_CHAN_STREAM_OPEN = eCMD_C011_CMD_BASE + 0x136,
++ eCMD_C011_DEC_CHAN_SET_PCR_PID = eCMD_C011_CMD_BASE + 0x137,
++ eCMD_C011_DEC_CHAN_SET_VID_PID = eCMD_C011_CMD_BASE + 0x138,
++ eCMD_C011_DEC_CHAN_SET_PAN_SCAN_MODE = eCMD_C011_CMD_BASE + 0x139,
++ eCMD_C011_DEC_CHAN_START_DISPLAY_AT_PTS = eCMD_C011_CMD_BASE + 0x140,
++ eCMD_C011_DEC_CHAN_STOP_DISPLAY_AT_PTS = eCMD_C011_CMD_BASE + 0x141,
++ eCMD_C011_DEC_CHAN_SET_DISPLAY_ORDER = eCMD_C011_CMD_BASE + 0x142,
++ eCMD_C011_DEC_CHAN_GET_DISPLAY_ORDER = eCMD_C011_CMD_BASE + 0x143,
++ eCMD_C011_DEC_CHAN_SET_HOST_TRICK_MODE = eCMD_C011_CMD_BASE + 0x144,
++ eCMD_C011_DEC_CHAN_SET_OPERATION_MODE = eCMD_C011_CMD_BASE + 0x145,
++ eCMD_C011_DEC_CHAN_DISPLAY_PAUSE_UNTO_PTS = eCMD_C011_CMD_BASE + 0x146,
++ eCMD_C011_DEC_CHAN_SET_PTS_STC_DIFF_THRESHOLD = eCMD_C011_CMD_BASE + 0x147,
++ eCMD_C011_DEC_CHAN_SEND_COMPRESSED_BUF = eCMD_C011_CMD_BASE + 0x148,
++ eCMD_C011_DEC_CHAN_SET_CLIPPING = eCMD_C011_CMD_BASE + 0x149,
++ eCMD_C011_DEC_CHAN_SET_PARAMETERS_FOR_HARD_RESET_INTERRUPT_TO_HOST
++ = eCMD_C011_CMD_BASE + 0x150,
++
++ /* Decoder RevD commands */
++ eCMD_C011_DEC_CHAN_SET_CSC = eCMD_C011_CMD_BASE + 0x180, /* color space conversion */
++ eCMD_C011_DEC_CHAN_SET_RANGE_REMAP = eCMD_C011_CMD_BASE + 0x181,
++ eCMD_C011_DEC_CHAN_SET_FGT = eCMD_C011_CMD_BASE + 0x182,
++ /* Note: 0x183 not implemented yet in Rev D main */
++ eCMD_C011_DEC_CHAN_SET_LASTPICTURE_PADDING = eCMD_C011_CMD_BASE + 0x183,
++
++ /* Decoder 7412 commands (7412-only) */
++ eCMD_C011_DEC_CHAN_SET_CONTENT_KEY = eCMD_C011_CMD_BASE + 0x190,
++ eCMD_C011_DEC_CHAN_SET_SESSION_KEY = eCMD_C011_CMD_BASE + 0x191,
++ eCMD_C011_DEC_CHAN_FMT_CHANGE_ACK = eCMD_C011_CMD_BASE + 0x192,
++
++ eCMD_C011_DEC_CHAN_CUSTOM_VIDOUT = eCMD_C011_CMD_BASE + 0x1FF,
++
++ /* Encoding commands */
++ eCMD_C011_ENC_CHAN_OPEN = eCMD_C011_CMD_BASE + 0x200,
++ eCMD_C011_ENC_CHAN_CLOSE = eCMD_C011_CMD_BASE + 0x201,
++ eCMD_C011_ENC_CHAN_ACTIVATE = eCMD_C011_CMD_BASE + 0x202,
++ eCMD_C011_ENC_CHAN_CONTROL = eCMD_C011_CMD_BASE + 0x203,
++ eCMD_C011_ENC_CHAN_STATISTICS = eCMD_C011_CMD_BASE + 0x204,
++
++ eNOTIFY_C011_ENC_CHAN_EVENT = eCMD_C011_CMD_BASE + 0x210,
++
++} eC011_TS_CMD;
++
++#endif
+diff --git a/drivers/staging/crystalhd/crystalhd_hw.c b/drivers/staging/crystalhd/crystalhd_hw.c
+new file mode 100644
+index 0000000..01819d3
+--- /dev/null
++++ b/drivers/staging/crystalhd/crystalhd_hw.c
+@@ -0,0 +1,2395 @@
++/***************************************************************************
++ * Copyright (c) 2005-2009, Broadcom Corporation.
++ *
++ * Name: crystalhd_hw . c
++ *
++ * Description:
++ * BCM70010 Linux driver HW layer.
++ *
++ **********************************************************************
++ * This file is part of the crystalhd device driver.
++ *
++ * This driver is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, version 2 of the License.
++ *
++ * This driver 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 driver. If not, see <http://www.gnu.org/licenses/>.
++ **********************************************************************/
++
++#include <linux/pci.h>
++#include <linux/delay.h>
++#include "crystalhd_hw.h"
++
++/* Functions internal to this file */
++
++static void crystalhd_enable_uarts(struct crystalhd_adp *adp)
++{
++ bc_dec_reg_wr(adp, UartSelectA, BSVS_UART_STREAM);
++ bc_dec_reg_wr(adp, UartSelectB, BSVS_UART_DEC_OUTER);
++}
++
++
++static void crystalhd_start_dram(struct crystalhd_adp *adp)
++{
++ bc_dec_reg_wr(adp, SDRAM_PARAM, ((40 / 5 - 1) << 0) |
++ /* tras (40ns tras)/(5ns period) -1 ((15/5 - 1) << 4) | // trcd */
++ ((15 / 5 - 1) << 7) | /* trp */
++ ((10 / 5 - 1) << 10) | /* trrd */
++ ((15 / 5 + 1) << 12) | /* twr */
++ ((2 + 1) << 16) | /* twtr */
++ ((70 / 5 - 2) << 19) | /* trfc */
++ (0 << 23));
++
++ bc_dec_reg_wr(adp, SDRAM_PRECHARGE, 0);
++ bc_dec_reg_wr(adp, SDRAM_EXT_MODE, 2);
++ bc_dec_reg_wr(adp, SDRAM_MODE, 0x132);
++ bc_dec_reg_wr(adp, SDRAM_PRECHARGE, 0);
++ bc_dec_reg_wr(adp, SDRAM_REFRESH, 0);
++ bc_dec_reg_wr(adp, SDRAM_REFRESH, 0);
++ bc_dec_reg_wr(adp, SDRAM_MODE, 0x32);
++ /* setting the refresh rate here */
++ bc_dec_reg_wr(adp, SDRAM_REF_PARAM, ((1 << 12) | 96));
++}
++
++
++static bool crystalhd_bring_out_of_rst(struct crystalhd_adp *adp)
++{
++ link_misc_perst_deco_ctrl rst_deco_cntrl;
++ link_misc_perst_clk_ctrl rst_clk_cntrl;
++ uint32_t temp;
++
++ /*
++ * Link clocks: MISC_PERST_CLOCK_CTRL Clear PLL power down bit,
++ * delay to allow PLL to lock Clear alternate clock, stop clock bits
++ */
++ rst_clk_cntrl.whole_reg = crystalhd_reg_rd(adp, MISC_PERST_CLOCK_CTRL);
++ rst_clk_cntrl.pll_pwr_dn = 0;
++ crystalhd_reg_wr(adp, MISC_PERST_CLOCK_CTRL, rst_clk_cntrl.whole_reg);
++ msleep_interruptible(50);
++
++ rst_clk_cntrl.whole_reg = crystalhd_reg_rd(adp, MISC_PERST_CLOCK_CTRL);
++ rst_clk_cntrl.stop_core_clk = 0;
++ rst_clk_cntrl.sel_alt_clk = 0;
++
++ crystalhd_reg_wr(adp, MISC_PERST_CLOCK_CTRL, rst_clk_cntrl.whole_reg);
++ msleep_interruptible(50);
++
++ /*
++ * Bus Arbiter Timeout: GISB_ARBITER_TIMER
++ * Set internal bus arbiter timeout to 40us based on core clock speed
++ * (63MHz * 40us = 0x9D8)
++ */
++ crystalhd_reg_wr(adp, GISB_ARBITER_TIMER, 0x9D8);
++
++ /*
++ * Decoder clocks: MISC_PERST_DECODER_CTRL
++ * Enable clocks while 7412 reset is asserted, delay
++ * De-assert 7412 reset
++ */
++ rst_deco_cntrl.whole_reg = crystalhd_reg_rd(adp, MISC_PERST_DECODER_CTRL);
++ rst_deco_cntrl.stop_bcm_7412_clk = 0;
++ rst_deco_cntrl.bcm7412_rst = 1;
++ crystalhd_reg_wr(adp, MISC_PERST_DECODER_CTRL, rst_deco_cntrl.whole_reg);
++ msleep_interruptible(10);
++
++ rst_deco_cntrl.whole_reg = crystalhd_reg_rd(adp, MISC_PERST_DECODER_CTRL);
++ rst_deco_cntrl.bcm7412_rst = 0;
++ crystalhd_reg_wr(adp, MISC_PERST_DECODER_CTRL, rst_deco_cntrl.whole_reg);
++ msleep_interruptible(50);
++
++ /* Disable OTP_CONTENT_MISC to 0 to disable all secure modes */
++ crystalhd_reg_wr(adp, OTP_CONTENT_MISC, 0);
++
++ /* Clear bit 29 of 0x404 */
++ temp = crystalhd_reg_rd(adp, PCIE_TL_TRANSACTION_CONFIGURATION);
++ temp &= ~BC_BIT(29);
++ crystalhd_reg_wr(adp, PCIE_TL_TRANSACTION_CONFIGURATION, temp);
++
++ /* 2.5V regulator must be set to 2.6 volts (+6%) */
++ /* FIXME: jarod: what's the point of this reg read? */
++ temp = crystalhd_reg_rd(adp, MISC_PERST_VREG_CTRL);
++ crystalhd_reg_wr(adp, MISC_PERST_VREG_CTRL, 0xF3);
++
++ return true;
++}
++
++static bool crystalhd_put_in_reset(struct crystalhd_adp *adp)
++{
++ link_misc_perst_deco_ctrl rst_deco_cntrl;
++ link_misc_perst_clk_ctrl rst_clk_cntrl;
++ uint32_t temp;
++
++ /*
++ * Decoder clocks: MISC_PERST_DECODER_CTRL
++ * Assert 7412 reset, delay
++ * Assert 7412 stop clock
++ */
++ rst_deco_cntrl.whole_reg = crystalhd_reg_rd(adp, MISC_PERST_DECODER_CTRL);
++ rst_deco_cntrl.stop_bcm_7412_clk = 1;
++ crystalhd_reg_wr(adp, MISC_PERST_DECODER_CTRL, rst_deco_cntrl.whole_reg);
++ msleep_interruptible(50);
++
++ /* Bus Arbiter Timeout: GISB_ARBITER_TIMER
++ * Set internal bus arbiter timeout to 40us based on core clock speed
++ * (6.75MHZ * 40us = 0x10E)
++ */
++ crystalhd_reg_wr(adp, GISB_ARBITER_TIMER, 0x10E);
++
++ /* Link clocks: MISC_PERST_CLOCK_CTRL
++ * Stop core clk, delay
++ * Set alternate clk, delay, set PLL power down
++ */
++ rst_clk_cntrl.whole_reg = crystalhd_reg_rd(adp, MISC_PERST_CLOCK_CTRL);
++ rst_clk_cntrl.stop_core_clk = 1;
++ rst_clk_cntrl.sel_alt_clk = 1;
++ crystalhd_reg_wr(adp, MISC_PERST_CLOCK_CTRL, rst_clk_cntrl.whole_reg);
++ msleep_interruptible(50);
++
++ rst_clk_cntrl.whole_reg = crystalhd_reg_rd(adp, MISC_PERST_CLOCK_CTRL);
++ rst_clk_cntrl.pll_pwr_dn = 1;
++ crystalhd_reg_wr(adp, MISC_PERST_CLOCK_CTRL, rst_clk_cntrl.whole_reg);
++
++ /*
++ * Read and restore the Transaction Configuration Register
++ * after core reset
++ */
++ temp = crystalhd_reg_rd(adp, PCIE_TL_TRANSACTION_CONFIGURATION);
++
++ /*
++ * Link core soft reset: MISC3_RESET_CTRL
++ * - Write BIT[0]=1 and read it back for core reset to take place
++ */
++ crystalhd_reg_wr(adp, MISC3_RESET_CTRL, 1);
++ rst_deco_cntrl.whole_reg = crystalhd_reg_rd(adp, MISC3_RESET_CTRL);
++ msleep_interruptible(50);
++
++ /* restore the transaction configuration register */
++ crystalhd_reg_wr(adp, PCIE_TL_TRANSACTION_CONFIGURATION, temp);
++
++ return true;
++}
++
++static void crystalhd_disable_interrupts(struct crystalhd_adp *adp)
++{
++ intr_mask_reg intr_mask;
++ intr_mask.whole_reg = crystalhd_reg_rd(adp, INTR_INTR_MSK_STS_REG);
++ intr_mask.mask_pcie_err = 1;
++ intr_mask.mask_pcie_rbusmast_err = 1;
++ intr_mask.mask_pcie_rgr_bridge = 1;
++ intr_mask.mask_rx_done = 1;
++ intr_mask.mask_rx_err = 1;
++ intr_mask.mask_tx_done = 1;
++ intr_mask.mask_tx_err = 1;
++ crystalhd_reg_wr(adp, INTR_INTR_MSK_SET_REG, intr_mask.whole_reg);
++
++ return;
++}
++
++static void crystalhd_enable_interrupts(struct crystalhd_adp *adp)
++{
++ intr_mask_reg intr_mask;
++ intr_mask.whole_reg = crystalhd_reg_rd(adp, INTR_INTR_MSK_STS_REG);
++ intr_mask.mask_pcie_err = 1;
++ intr_mask.mask_pcie_rbusmast_err = 1;
++ intr_mask.mask_pcie_rgr_bridge = 1;
++ intr_mask.mask_rx_done = 1;
++ intr_mask.mask_rx_err = 1;
++ intr_mask.mask_tx_done = 1;
++ intr_mask.mask_tx_err = 1;
++ crystalhd_reg_wr(adp, INTR_INTR_MSK_CLR_REG, intr_mask.whole_reg);
++
++ return;
++}
++
++static void crystalhd_clear_errors(struct crystalhd_adp *adp)
++{
++ uint32_t reg;
++
++ /* FIXME: jarod: wouldn't we want to write a 0 to the reg? Or does the write clear the bits specified? */
++ reg = crystalhd_reg_rd(adp, MISC1_Y_RX_ERROR_STATUS);
++ if (reg)
++ crystalhd_reg_wr(adp, MISC1_Y_RX_ERROR_STATUS, reg);
++
++ reg = crystalhd_reg_rd(adp, MISC1_UV_RX_ERROR_STATUS);
++ if (reg)
++ crystalhd_reg_wr(adp, MISC1_UV_RX_ERROR_STATUS, reg);
++
++ reg = crystalhd_reg_rd(adp, MISC1_TX_DMA_ERROR_STATUS);
++ if (reg)
++ crystalhd_reg_wr(adp, MISC1_TX_DMA_ERROR_STATUS, reg);
++}
++
++static void crystalhd_clear_interrupts(struct crystalhd_adp *adp)
++{
++ uint32_t intr_sts = crystalhd_reg_rd(adp, INTR_INTR_STATUS);
++
++ if (intr_sts) {
++ crystalhd_reg_wr(adp, INTR_INTR_CLR_REG, intr_sts);
++
++ /* Write End Of Interrupt for PCIE */
++ crystalhd_reg_wr(adp, INTR_EOI_CTRL, 1);
++ }
++}
++
++static void crystalhd_soft_rst(struct crystalhd_adp *adp)
++{
++ uint32_t val;
++
++ /* Assert c011 soft reset*/
++ bc_dec_reg_wr(adp, DecHt_HostSwReset, 0x00000001);
++ msleep_interruptible(50);
++
++ /* Release c011 soft reset*/
++ bc_dec_reg_wr(adp, DecHt_HostSwReset, 0x00000000);
++
++ /* Disable Stuffing..*/
++ val = crystalhd_reg_rd(adp, MISC2_GLOBAL_CTRL);
++ val |= BC_BIT(8);
++ crystalhd_reg_wr(adp, MISC2_GLOBAL_CTRL, val);
++}
++
++static bool crystalhd_load_firmware_config(struct crystalhd_adp *adp)
++{
++ uint32_t i = 0, reg;
++
++ crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (BC_DRAM_FW_CFG_ADDR >> 19));
++
++ crystalhd_reg_wr(adp, AES_CMD, 0);
++ crystalhd_reg_wr(adp, AES_CONFIG_INFO, (BC_DRAM_FW_CFG_ADDR & 0x7FFFF));
++ crystalhd_reg_wr(adp, AES_CMD, 0x1);
++
++ /* FIXME: jarod: I've seen this fail, and introducing extra delays helps... */
++ for (i = 0; i < 100; ++i) {
++ reg = crystalhd_reg_rd(adp, AES_STATUS);
++ if (reg & 0x1)
++ return true;
++ msleep_interruptible(10);
++ }
++
++ return false;
++}
++
++
++static bool crystalhd_start_device(struct crystalhd_adp *adp)
++{
++ uint32_t dbg_options, glb_cntrl = 0, reg_pwrmgmt = 0;
++
++ BCMLOG(BCMLOG_INFO, "Starting BCM70012 Device\n");
++
++ reg_pwrmgmt = crystalhd_reg_rd(adp, PCIE_DLL_DATA_LINK_CONTROL);
++ reg_pwrmgmt &= ~ASPM_L1_ENABLE;
++
++ crystalhd_reg_wr(adp, PCIE_DLL_DATA_LINK_CONTROL, reg_pwrmgmt);
++
++ if (!crystalhd_bring_out_of_rst(adp)) {
++ BCMLOG_ERR("Failed To Bring Link Out Of Reset\n");
++ return false;
++ }
++
++ crystalhd_disable_interrupts(adp);
++
++ crystalhd_clear_errors(adp);
++
++ crystalhd_clear_interrupts(adp);
++
++ crystalhd_enable_interrupts(adp);
++
++ /* Enable the option for getting the total no. of DWORDS
++ * that have been transfered by the RXDMA engine
++ */
++ dbg_options = crystalhd_reg_rd(adp, MISC1_DMA_DEBUG_OPTIONS_REG);
++ dbg_options |= 0x10;
++ crystalhd_reg_wr(adp, MISC1_DMA_DEBUG_OPTIONS_REG, dbg_options);
++
++ /* Enable PCI Global Control options */
++ glb_cntrl = crystalhd_reg_rd(adp, MISC2_GLOBAL_CTRL);
++ glb_cntrl |= 0x100;
++ glb_cntrl |= 0x8000;
++ crystalhd_reg_wr(adp, MISC2_GLOBAL_CTRL, glb_cntrl);
++
++ crystalhd_enable_interrupts(adp);
++
++ crystalhd_soft_rst(adp);
++ crystalhd_start_dram(adp);
++ crystalhd_enable_uarts(adp);
++
++ return true;
++}
++
++static bool crystalhd_stop_device(struct crystalhd_adp *adp)
++{
++ uint32_t reg;
++
++ BCMLOG(BCMLOG_INFO, "Stopping BCM70012 Device\n");
++ /* Clear and disable interrupts */
++ crystalhd_disable_interrupts(adp);
++ crystalhd_clear_errors(adp);
++ crystalhd_clear_interrupts(adp);
++
++ if (!crystalhd_put_in_reset(adp))
++ BCMLOG_ERR("Failed to Put Link To Reset State\n");
++
++ reg = crystalhd_reg_rd(adp, PCIE_DLL_DATA_LINK_CONTROL);
++ reg |= ASPM_L1_ENABLE;
++ crystalhd_reg_wr(adp, PCIE_DLL_DATA_LINK_CONTROL, reg);
++
++ /* Set PCI Clk Req */
++ reg = crystalhd_reg_rd(adp, PCIE_CLK_REQ_REG);
++ reg |= PCI_CLK_REQ_ENABLE;
++ crystalhd_reg_wr(adp, PCIE_CLK_REQ_REG, reg);
++
++ return true;
++}
++
++static crystalhd_rx_dma_pkt *crystalhd_hw_alloc_rx_pkt(struct crystalhd_hw *hw)
++{
++ unsigned long flags = 0;
++ crystalhd_rx_dma_pkt *temp = NULL;
++
++ if (!hw)
++ return NULL;
++
++ spin_lock_irqsave(&hw->lock, flags);
++ temp = hw->rx_pkt_pool_head;
++ if (temp) {
++ hw->rx_pkt_pool_head = hw->rx_pkt_pool_head->next;
++ temp->dio_req = NULL;
++ temp->pkt_tag = 0;
++ temp->flags = 0;
++ }
++ spin_unlock_irqrestore(&hw->lock, flags);
++
++ return temp;
++}
++
++static void crystalhd_hw_free_rx_pkt(struct crystalhd_hw *hw,
++ crystalhd_rx_dma_pkt *pkt)
++{
++ unsigned long flags = 0;
++
++ if (!hw || !pkt)
++ return;
++
++ spin_lock_irqsave(&hw->lock, flags);
++ pkt->next = hw->rx_pkt_pool_head;
++ hw->rx_pkt_pool_head = pkt;
++ spin_unlock_irqrestore(&hw->lock, flags);
++}
++
++/*
++ * Call back from TX - IOQ deletion.
++ *
++ * This routine will release the TX DMA rings allocated
++ * druing setup_dma rings interface.
++ *
++ * Memory is allocated per DMA ring basis. This is just
++ * a place holder to be able to create the dio queues.
++ */
++static void crystalhd_tx_desc_rel_call_back(void *context, void *data)
++{
++}
++
++/*
++ * Rx Packet release callback..
++ *
++ * Release All user mapped capture buffers and Our DMA packets
++ * back to our free pool. The actual cleanup of the DMA
++ * ring descriptors happen during dma ring release.
++ */
++static void crystalhd_rx_pkt_rel_call_back(void *context, void *data)
++{
++ struct crystalhd_hw *hw = (struct crystalhd_hw *)context;
++ crystalhd_rx_dma_pkt *pkt = (crystalhd_rx_dma_pkt *)data;
++
++ if (!pkt || !hw) {
++ BCMLOG_ERR("Invalid arg - %p %p\n", hw, pkt);
++ return;
++ }
++
++ if (pkt->dio_req)
++ crystalhd_unmap_dio(hw->adp, pkt->dio_req);
++ else
++ BCMLOG_ERR("Missing dio_req: 0x%x\n", pkt->pkt_tag);
++
++ crystalhd_hw_free_rx_pkt(hw, pkt);
++}
++
++#define crystalhd_hw_delete_ioq(adp, q) \
++ if (q) { \
++ crystalhd_delete_dioq(adp, q); \
++ q = NULL; \
++ }
++
++static void crystalhd_hw_delete_ioqs(struct crystalhd_hw *hw)
++{
++ if (!hw)
++ return;
++
++ BCMLOG(BCMLOG_DBG, "Deleting IOQs \n");
++ crystalhd_hw_delete_ioq(hw->adp, hw->tx_actq);
++ crystalhd_hw_delete_ioq(hw->adp, hw->tx_freeq);
++ crystalhd_hw_delete_ioq(hw->adp, hw->rx_actq);
++ crystalhd_hw_delete_ioq(hw->adp, hw->rx_freeq);
++ crystalhd_hw_delete_ioq(hw->adp, hw->rx_rdyq);
++}
++
++#define crystalhd_hw_create_ioq(sts, hw, q, cb) \
++do { \
++ sts = crystalhd_create_dioq(hw->adp, &q, cb, hw); \
++ if (sts != BC_STS_SUCCESS) \
++ goto hw_create_ioq_err; \
++} while (0)
++
++/*
++ * Create IOQs..
++ *
++ * TX - Active & Free
++ * RX - Active, Ready and Free.
++ */
++static BC_STATUS crystalhd_hw_create_ioqs(struct crystalhd_hw *hw)
++{
++ BC_STATUS sts = BC_STS_SUCCESS;
++
++ if (!hw) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return BC_STS_INV_ARG;
++ }
++
++ crystalhd_hw_create_ioq(sts, hw, hw->tx_freeq,
++ crystalhd_tx_desc_rel_call_back);
++ crystalhd_hw_create_ioq(sts, hw, hw->tx_actq,
++ crystalhd_tx_desc_rel_call_back);
++
++ crystalhd_hw_create_ioq(sts, hw, hw->rx_freeq,
++ crystalhd_rx_pkt_rel_call_back);
++ crystalhd_hw_create_ioq(sts, hw, hw->rx_rdyq,
++ crystalhd_rx_pkt_rel_call_back);
++ crystalhd_hw_create_ioq(sts, hw, hw->rx_actq,
++ crystalhd_rx_pkt_rel_call_back);
++
++ return sts;
++
++hw_create_ioq_err:
++ crystalhd_hw_delete_ioqs(hw);
++
++ return sts;
++}
++
++
++static bool crystalhd_code_in_full(struct crystalhd_adp *adp, uint32_t needed_sz,
++ bool b_188_byte_pkts, uint8_t flags)
++{
++ uint32_t base, end, writep, readp;
++ uint32_t cpbSize, cpbFullness, fifoSize;
++
++ if (flags & 0x02) { /* ASF Bit is set */
++ base = bc_dec_reg_rd(adp, REG_Dec_TsAudCDB2Base);
++ end = bc_dec_reg_rd(adp, REG_Dec_TsAudCDB2End);
++ writep = bc_dec_reg_rd(adp, REG_Dec_TsAudCDB2Wrptr);
++ readp = bc_dec_reg_rd(adp, REG_Dec_TsAudCDB2Rdptr);
++ } else if (b_188_byte_pkts) { /*Encrypted 188 byte packets*/
++ base = bc_dec_reg_rd(adp, REG_Dec_TsUser0Base);
++ end = bc_dec_reg_rd(adp, REG_Dec_TsUser0End);
++ writep = bc_dec_reg_rd(adp, REG_Dec_TsUser0Wrptr);
++ readp = bc_dec_reg_rd(adp, REG_Dec_TsUser0Rdptr);
++ } else {
++ base = bc_dec_reg_rd(adp, REG_DecCA_RegCinBase);
++ end = bc_dec_reg_rd(adp, REG_DecCA_RegCinEnd);
++ writep = bc_dec_reg_rd(adp, REG_DecCA_RegCinWrPtr);
++ readp = bc_dec_reg_rd(adp, REG_DecCA_RegCinRdPtr);
++ }
++
++ cpbSize = end - base;
++ if (writep >= readp)
++ cpbFullness = writep - readp;
++ else
++ cpbFullness = (end - base) - (readp - writep);
++
++ fifoSize = cpbSize - cpbFullness;
++
++ if (fifoSize < BC_INFIFO_THRESHOLD)
++ return true;
++
++ if (needed_sz > (fifoSize - BC_INFIFO_THRESHOLD))
++ return true;
++
++ return false;
++}
++
++static BC_STATUS crystalhd_hw_tx_req_complete(struct crystalhd_hw *hw,
++ uint32_t list_id, BC_STATUS cs)
++{
++ tx_dma_pkt *tx_req;
++
++ if (!hw || !list_id) {
++ BCMLOG_ERR("Invalid Arg..\n");
++ return BC_STS_INV_ARG;
++ }
++
++ hw->pwr_lock--;
++
++ tx_req = (tx_dma_pkt *)crystalhd_dioq_find_and_fetch(hw->tx_actq, list_id);
++ if (!tx_req) {
++ if (cs != BC_STS_IO_USER_ABORT)
++ BCMLOG_ERR("Find and Fetch Did not find req\n");
++ return BC_STS_NO_DATA;
++ }
++
++ if (tx_req->call_back) {
++ tx_req->call_back(tx_req->dio_req, tx_req->cb_event, cs);
++ tx_req->dio_req = NULL;
++ tx_req->cb_event = NULL;
++ tx_req->call_back = NULL;
++ } else {
++ BCMLOG(BCMLOG_DBG, "Missing Tx Callback - %X\n",
++ tx_req->list_tag);
++ }
++
++ /* Now put back the tx_list back in FreeQ */
++ tx_req->list_tag = 0;
++
++ return crystalhd_dioq_add(hw->tx_freeq, tx_req, false, 0);
++}
++
++static bool crystalhd_tx_list0_handler(struct crystalhd_hw *hw, uint32_t err_sts)
++{
++ uint32_t err_mask, tmp;
++ unsigned long flags = 0;
++
++ err_mask = MISC1_TX_DMA_ERROR_STATUS_TX_L0_DESC_TX_ABORT_ERRORS_MASK |
++ MISC1_TX_DMA_ERROR_STATUS_TX_L0_DMA_DATA_TX_ABORT_ERRORS_MASK |
++ MISC1_TX_DMA_ERROR_STATUS_TX_L0_FIFO_FULL_ERRORS_MASK;
++
++ if (!(err_sts & err_mask))
++ return false;
++
++ BCMLOG_ERR("Error on Tx-L0 %x \n", err_sts);
++
++ tmp = err_mask;
++
++ if (err_sts & MISC1_TX_DMA_ERROR_STATUS_TX_L0_FIFO_FULL_ERRORS_MASK)
++ tmp &= ~MISC1_TX_DMA_ERROR_STATUS_TX_L0_FIFO_FULL_ERRORS_MASK;
++
++ if (tmp) {
++ spin_lock_irqsave(&hw->lock, flags);
++ /* reset list index.*/
++ hw->tx_list_post_index = 0;
++ spin_unlock_irqrestore(&hw->lock, flags);
++ }
++
++ tmp = err_sts & err_mask;
++ crystalhd_reg_wr(hw->adp, MISC1_TX_DMA_ERROR_STATUS, tmp);
++
++ return true;
++}
++
++static bool crystalhd_tx_list1_handler(struct crystalhd_hw *hw, uint32_t err_sts)
++{
++ uint32_t err_mask, tmp;
++ unsigned long flags = 0;
++
++ err_mask = MISC1_TX_DMA_ERROR_STATUS_TX_L1_DESC_TX_ABORT_ERRORS_MASK |
++ MISC1_TX_DMA_ERROR_STATUS_TX_L1_DMA_DATA_TX_ABORT_ERRORS_MASK |
++ MISC1_TX_DMA_ERROR_STATUS_TX_L1_FIFO_FULL_ERRORS_MASK;
++
++ if (!(err_sts & err_mask))
++ return false;
++
++ BCMLOG_ERR("Error on Tx-L1 %x \n", err_sts);
++
++ tmp = err_mask;
++
++ if (err_sts & MISC1_TX_DMA_ERROR_STATUS_TX_L1_FIFO_FULL_ERRORS_MASK)
++ tmp &= ~MISC1_TX_DMA_ERROR_STATUS_TX_L1_FIFO_FULL_ERRORS_MASK;
++
++ if (tmp) {
++ spin_lock_irqsave(&hw->lock, flags);
++ /* reset list index.*/
++ hw->tx_list_post_index = 0;
++ spin_unlock_irqrestore(&hw->lock, flags);
++ }
++
++ tmp = err_sts & err_mask;
++ crystalhd_reg_wr(hw->adp, MISC1_TX_DMA_ERROR_STATUS, tmp);
++
++ return true;
++}
++
++static void crystalhd_tx_isr(struct crystalhd_hw *hw, uint32_t int_sts)
++{
++ uint32_t err_sts;
++
++ if (int_sts & INTR_INTR_STATUS_L0_TX_DMA_DONE_INTR_MASK)
++ crystalhd_hw_tx_req_complete(hw, hw->tx_ioq_tag_seed + 0,
++ BC_STS_SUCCESS);
++
++ if (int_sts & INTR_INTR_STATUS_L1_TX_DMA_DONE_INTR_MASK)
++ crystalhd_hw_tx_req_complete(hw, hw->tx_ioq_tag_seed + 1,
++ BC_STS_SUCCESS);
++
++ if (!(int_sts & (INTR_INTR_STATUS_L0_TX_DMA_ERR_INTR_MASK |
++ INTR_INTR_STATUS_L1_TX_DMA_ERR_INTR_MASK))) {
++ /* No error mask set.. */
++ return;
++ }
++
++ /* Handle Tx errors. */
++ err_sts = crystalhd_reg_rd(hw->adp, MISC1_TX_DMA_ERROR_STATUS);
++
++ if (crystalhd_tx_list0_handler(hw, err_sts))
++ crystalhd_hw_tx_req_complete(hw, hw->tx_ioq_tag_seed + 0,
++ BC_STS_ERROR);
++
++ if (crystalhd_tx_list1_handler(hw, err_sts))
++ crystalhd_hw_tx_req_complete(hw, hw->tx_ioq_tag_seed + 1,
++ BC_STS_ERROR);
++
++ hw->stats.tx_errors++;
++}
++
++static void crystalhd_hw_dump_desc(pdma_descriptor p_dma_desc,
++ uint32_t ul_desc_index, uint32_t cnt)
++{
++ uint32_t ix, ll = 0;
++
++ if (!p_dma_desc || !cnt)
++ return;
++
++ /* FIXME: jarod: perhaps a modparam desc_debug to enable this, rather than
++ * setting ll (log level, I presume) to non-zero? */
++ if (!ll)
++ return;
++
++ for (ix = ul_desc_index; ix < (ul_desc_index + cnt); ix++) {
++ BCMLOG(ll, "%s[%d] Buff[%x:%x] Next:[%x:%x] XferSz:%x Intr:%x,Last:%x\n",
++ ((p_dma_desc[ul_desc_index].dma_dir) ? "TDesc" : "RDesc"),
++ ul_desc_index,
++ p_dma_desc[ul_desc_index].buff_addr_high,
++ p_dma_desc[ul_desc_index].buff_addr_low,
++ p_dma_desc[ul_desc_index].next_desc_addr_high,
++ p_dma_desc[ul_desc_index].next_desc_addr_low,
++ p_dma_desc[ul_desc_index].xfer_size,
++ p_dma_desc[ul_desc_index].intr_enable,
++ p_dma_desc[ul_desc_index].last_rec_indicator);
++ }
++
++}
++
++static BC_STATUS crystalhd_hw_fill_desc(crystalhd_dio_req *ioreq,
++ dma_descriptor *desc,
++ dma_addr_t desc_paddr_base,
++ uint32_t sg_cnt, uint32_t sg_st_ix,
++ uint32_t sg_st_off, uint32_t xfr_sz)
++{
++ uint32_t count = 0, ix = 0, sg_ix = 0, len = 0, last_desc_ix = 0;
++ dma_addr_t desc_phy_addr = desc_paddr_base;
++ addr_64 addr_temp;
++
++ if (!ioreq || !desc || !desc_paddr_base || !xfr_sz ||
++ (!sg_cnt && !ioreq->uinfo.dir_tx)) {
++ BCMLOG_ERR("Invalid Args\n");
++ return BC_STS_INV_ARG;
++ }
++
++ for (ix = 0; ix < sg_cnt; ix++) {
++
++ /* Setup SGLE index. */
++ sg_ix = ix + sg_st_ix;
++
++ /* Get SGLE length */
++ len = crystalhd_get_sgle_len(ioreq, sg_ix);
++ if (len % 4) {
++ BCMLOG_ERR(" len in sg %d %d %d\n", len, sg_ix, sg_cnt);
++ return BC_STS_NOT_IMPL;
++ }
++ /* Setup DMA desc with Phy addr & Length at current index. */
++ addr_temp.full_addr = crystalhd_get_sgle_paddr(ioreq, sg_ix);
++ if (sg_ix == sg_st_ix) {
++ addr_temp.full_addr += sg_st_off;
++ len -= sg_st_off;
++ }
++ memset(&desc[ix], 0, sizeof(desc[ix]));
++ desc[ix].buff_addr_low = addr_temp.low_part;
++ desc[ix].buff_addr_high = addr_temp.high_part;
++ desc[ix].dma_dir = ioreq->uinfo.dir_tx;
++
++ /* Chain DMA descriptor. */
++ addr_temp.full_addr = desc_phy_addr + sizeof(dma_descriptor);
++ desc[ix].next_desc_addr_low = addr_temp.low_part;
++ desc[ix].next_desc_addr_high = addr_temp.high_part;
++
++ if ((count + len) > xfr_sz)
++ len = xfr_sz - count;
++
++ /* Debug.. */
++ if ((!len) || (len > crystalhd_get_sgle_len(ioreq, sg_ix))) {
++ BCMLOG_ERR("inv-len(%x) Ix(%d) count:%x xfr_sz:%x sg_cnt:%d\n",
++ len, ix, count, xfr_sz, sg_cnt);
++ return BC_STS_ERROR;
++ }
++ /* Length expects Multiple of 4 */
++ desc[ix].xfer_size = (len / 4);
++
++ crystalhd_hw_dump_desc(desc, ix, 1);
++
++ count += len;
++ desc_phy_addr += sizeof(dma_descriptor);
++ }
++
++ last_desc_ix = ix - 1;
++
++ if (ioreq->fb_size) {
++ memset(&desc[ix], 0, sizeof(desc[ix]));
++ addr_temp.full_addr = ioreq->fb_pa;
++ desc[ix].buff_addr_low = addr_temp.low_part;
++ desc[ix].buff_addr_high = addr_temp.high_part;
++ desc[ix].dma_dir = ioreq->uinfo.dir_tx;
++ desc[ix].xfer_size = 1;
++ desc[ix].fill_bytes = 4 - ioreq->fb_size;
++ count += ioreq->fb_size;
++ last_desc_ix++;
++ }
++
++ /* setup last descriptor..*/
++ desc[last_desc_ix].last_rec_indicator = 1;
++ desc[last_desc_ix].next_desc_addr_low = 0;
++ desc[last_desc_ix].next_desc_addr_high = 0;
++ desc[last_desc_ix].intr_enable = 1;
++
++ crystalhd_hw_dump_desc(desc, last_desc_ix, 1);
++
++ if (count != xfr_sz) {
++ BCMLOG_ERR("interal error sz curr:%x exp:%x\n", count, xfr_sz);
++ return BC_STS_ERROR;
++ }
++
++ return BC_STS_SUCCESS;
++}
++
++static BC_STATUS crystalhd_xlat_sgl_to_dma_desc(crystalhd_dio_req *ioreq,
++ pdma_desc_mem pdesc_mem,
++ uint32_t *uv_desc_index)
++{
++ dma_descriptor *desc = NULL;
++ dma_addr_t desc_paddr_base = 0;
++ uint32_t sg_cnt = 0, sg_st_ix = 0, sg_st_off = 0;
++ uint32_t xfr_sz = 0;
++ BC_STATUS sts = BC_STS_SUCCESS;
++
++ /* Check params.. */
++ if (!ioreq || !pdesc_mem || !uv_desc_index) {
++ BCMLOG_ERR("Invalid Args\n");
++ return BC_STS_INV_ARG;
++ }
++
++ if (!pdesc_mem->sz || !pdesc_mem->pdma_desc_start ||
++ !ioreq->sg || (!ioreq->sg_cnt && !ioreq->uinfo.dir_tx)) {
++ BCMLOG_ERR("Invalid Args\n");
++ return BC_STS_INV_ARG;
++ }
++
++ if ((ioreq->uinfo.dir_tx) && (ioreq->uinfo.uv_offset)) {
++ BCMLOG_ERR("UV offset for TX??\n");
++ return BC_STS_INV_ARG;
++
++ }
++
++ desc = pdesc_mem->pdma_desc_start;
++ desc_paddr_base = pdesc_mem->phy_addr;
++
++ if (ioreq->uinfo.dir_tx || (ioreq->uinfo.uv_offset == 0)) {
++ sg_cnt = ioreq->sg_cnt;
++ xfr_sz = ioreq->uinfo.xfr_len;
++ } else {
++ sg_cnt = ioreq->uinfo.uv_sg_ix + 1;
++ xfr_sz = ioreq->uinfo.uv_offset;
++ }
++
++ sts = crystalhd_hw_fill_desc(ioreq, desc, desc_paddr_base, sg_cnt,
++ sg_st_ix, sg_st_off, xfr_sz);
++
++ if ((sts != BC_STS_SUCCESS) || !ioreq->uinfo.uv_offset)
++ return sts;
++
++ /* Prepare for UV mapping.. */
++ desc = &pdesc_mem->pdma_desc_start[sg_cnt];
++ desc_paddr_base = pdesc_mem->phy_addr +
++ (sg_cnt * sizeof(dma_descriptor));
++
++ /* Done with desc addr.. now update sg stuff.*/
++ sg_cnt = ioreq->sg_cnt - ioreq->uinfo.uv_sg_ix;
++ xfr_sz = ioreq->uinfo.xfr_len - ioreq->uinfo.uv_offset;
++ sg_st_ix = ioreq->uinfo.uv_sg_ix;
++ sg_st_off = ioreq->uinfo.uv_sg_off;
++
++ sts = crystalhd_hw_fill_desc(ioreq, desc, desc_paddr_base, sg_cnt,
++ sg_st_ix, sg_st_off, xfr_sz);
++ if (sts != BC_STS_SUCCESS)
++ return sts;
++
++ *uv_desc_index = sg_st_ix;
++
++ return sts;
++}
++
++static void crystalhd_start_tx_dma_engine(struct crystalhd_hw *hw)
++{
++ uint32_t dma_cntrl;
++
++ dma_cntrl = crystalhd_reg_rd(hw->adp, MISC1_TX_SW_DESC_LIST_CTRL_STS);
++ if (!(dma_cntrl & DMA_START_BIT)) {
++ dma_cntrl |= DMA_START_BIT;
++ crystalhd_reg_wr(hw->adp, MISC1_TX_SW_DESC_LIST_CTRL_STS,
++ dma_cntrl);
++ }
++
++ return;
++}
++
++/* _CHECK_THIS_
++ *
++ * Verify if the Stop generates a completion interrupt or not.
++ * if it does not generate an interrupt, then add polling here.
++ */
++static BC_STATUS crystalhd_stop_tx_dma_engine(struct crystalhd_hw *hw)
++{
++ uint32_t dma_cntrl, cnt = 30;
++ uint32_t l1 = 1, l2 = 1;
++ unsigned long flags = 0;
++
++ dma_cntrl = crystalhd_reg_rd(hw->adp, MISC1_TX_SW_DESC_LIST_CTRL_STS);
++
++ BCMLOG(BCMLOG_DBG, "Stopping TX DMA Engine..\n");
++
++ /* FIXME: jarod: invert dma_ctrl and check bit? or are there missing parens? */
++ if (!dma_cntrl & DMA_START_BIT) {
++ BCMLOG(BCMLOG_DBG, "Already Stopped\n");
++ return BC_STS_SUCCESS;
++ }
++
++ crystalhd_disable_interrupts(hw->adp);
++
++ /* Issue stop to HW */
++ /* This bit when set gave problems. Please check*/
++ dma_cntrl &= ~DMA_START_BIT;
++ crystalhd_reg_wr(hw->adp, MISC1_TX_SW_DESC_LIST_CTRL_STS, dma_cntrl);
++
++ BCMLOG(BCMLOG_DBG, "Cleared the DMA Start bit\n");
++
++ /* Poll for 3seconds (30 * 100ms) on both the lists..*/
++ while ((l1 || l2) && cnt) {
++
++ if (l1) {
++ l1 = crystalhd_reg_rd(hw->adp, MISC1_TX_FIRST_DESC_L_ADDR_LIST0);
++ l1 &= DMA_START_BIT;
++ }
++
++ if (l2) {
++ l2 = crystalhd_reg_rd(hw->adp, MISC1_TX_FIRST_DESC_L_ADDR_LIST1);
++ l2 &= DMA_START_BIT;
++ }
++
++ msleep_interruptible(100);
++
++ cnt--;
++ }
++
++ if (!cnt) {
++ BCMLOG_ERR("Failed to stop TX DMA.. l1 %d, l2 %d\n", l1, l2);
++ crystalhd_enable_interrupts(hw->adp);
++ return BC_STS_ERROR;
++ }
++
++ spin_lock_irqsave(&hw->lock, flags);
++ hw->tx_list_post_index = 0;
++ spin_unlock_irqrestore(&hw->lock, flags);
++ BCMLOG(BCMLOG_DBG, "stopped TX DMA..\n");
++ crystalhd_enable_interrupts(hw->adp);
++
++ return BC_STS_SUCCESS;
++}
++
++static uint32_t crystalhd_get_pib_avail_cnt(struct crystalhd_hw *hw)
++{
++ /*
++ * Position of the PIB Entries can be found at
++ * 0th and the 1st location of the Circular list.
++ */
++ uint32_t Q_addr;
++ uint32_t pib_cnt, r_offset, w_offset;
++
++ Q_addr = hw->pib_del_Q_addr;
++
++ /* Get the Read Pointer */
++ crystalhd_mem_rd(hw->adp, Q_addr, 1, &r_offset);
++
++ /* Get the Write Pointer */
++ crystalhd_mem_rd(hw->adp, Q_addr + sizeof(uint32_t), 1, &w_offset);
++
++ if (r_offset == w_offset)
++ return 0; /* Queue is empty */
++
++ if (w_offset > r_offset)
++ pib_cnt = w_offset - r_offset;
++ else
++ pib_cnt = (w_offset + MAX_PIB_Q_DEPTH) -
++ (r_offset + MIN_PIB_Q_DEPTH);
++
++ if (pib_cnt > MAX_PIB_Q_DEPTH) {
++ BCMLOG_ERR("Invalid PIB Count (%u)\n", pib_cnt);
++ return 0;
++ }
++
++ return pib_cnt;
++}
++
++static uint32_t crystalhd_get_addr_from_pib_Q(struct crystalhd_hw *hw)
++{
++ uint32_t Q_addr;
++ uint32_t addr_entry, r_offset, w_offset;
++
++ Q_addr = hw->pib_del_Q_addr;
++
++ /* Get the Read Pointer 0Th Location is Read Pointer */
++ crystalhd_mem_rd(hw->adp, Q_addr, 1, &r_offset);
++
++ /* Get the Write Pointer 1st Location is Write pointer */
++ crystalhd_mem_rd(hw->adp, Q_addr + sizeof(uint32_t), 1, &w_offset);
++
++ /* Queue is empty */
++ if (r_offset == w_offset)
++ return 0;
++
++ if ((r_offset < MIN_PIB_Q_DEPTH) || (r_offset >= MAX_PIB_Q_DEPTH))
++ return 0;
++
++ /* Get the Actual Address of the PIB */
++ crystalhd_mem_rd(hw->adp, Q_addr + (r_offset * sizeof(uint32_t)),
++ 1, &addr_entry);
++
++ /* Increment the Read Pointer */
++ r_offset++;
++
++ if (MAX_PIB_Q_DEPTH == r_offset)
++ r_offset = MIN_PIB_Q_DEPTH;
++
++ /* Write back the read pointer to It's Location */
++ crystalhd_mem_wr(hw->adp, Q_addr, 1, &r_offset);
++
++ return addr_entry;
++}
++
++static bool crystalhd_rel_addr_to_pib_Q(struct crystalhd_hw *hw, uint32_t addr_to_rel)
++{
++ uint32_t Q_addr;
++ uint32_t r_offset, w_offset, n_offset;
++
++ Q_addr = hw->pib_rel_Q_addr;
++
++ /* Get the Read Pointer */
++ crystalhd_mem_rd(hw->adp, Q_addr, 1, &r_offset);
++
++ /* Get the Write Pointer */
++ crystalhd_mem_rd(hw->adp, Q_addr + sizeof(uint32_t), 1, &w_offset);
++
++ if ((r_offset < MIN_PIB_Q_DEPTH) ||
++ (r_offset >= MAX_PIB_Q_DEPTH))
++ return false;
++
++ n_offset = w_offset + 1;
++
++ if (MAX_PIB_Q_DEPTH == n_offset)
++ n_offset = MIN_PIB_Q_DEPTH;
++
++ if (r_offset == n_offset)
++ return false; /* should never happen */
++
++ /* Write the DRAM ADDR to the Queue at Next Offset */
++ crystalhd_mem_wr(hw->adp, Q_addr + (w_offset * sizeof(uint32_t)),
++ 1, &addr_to_rel);
++
++ /* Put the New value of the write pointer in Queue */
++ crystalhd_mem_wr(hw->adp, Q_addr + sizeof(uint32_t), 1, &n_offset);
++
++ return true;
++}
++
++static void cpy_pib_to_app(C011_PIB *src_pib, BC_PIC_INFO_BLOCK *dst_pib)
++{
++ if (!src_pib || !dst_pib) {
++ BCMLOG_ERR("Invalid Arguments\n");
++ return;
++ }
++
++ dst_pib->timeStamp = 0;
++ dst_pib->picture_number = src_pib->ppb.picture_number;
++ dst_pib->width = src_pib->ppb.width;
++ dst_pib->height = src_pib->ppb.height;
++ dst_pib->chroma_format = src_pib->ppb.chroma_format;
++ dst_pib->pulldown = src_pib->ppb.pulldown;
++ dst_pib->flags = src_pib->ppb.flags;
++ dst_pib->sess_num = src_pib->ptsStcOffset;
++ dst_pib->aspect_ratio = src_pib->ppb.aspect_ratio;
++ dst_pib->colour_primaries = src_pib->ppb.colour_primaries;
++ dst_pib->picture_meta_payload = src_pib->ppb.picture_meta_payload;
++ dst_pib->frame_rate = src_pib->resolution ;
++ return;
++}
++
++static void crystalhd_hw_proc_pib(struct crystalhd_hw *hw)
++{
++ unsigned int cnt;
++ C011_PIB src_pib;
++ uint32_t pib_addr, pib_cnt;
++ BC_PIC_INFO_BLOCK *AppPib;
++ crystalhd_rx_dma_pkt *rx_pkt = NULL;
++
++ pib_cnt = crystalhd_get_pib_avail_cnt(hw);
++
++ if (!pib_cnt)
++ return;
++
++ for (cnt = 0; cnt < pib_cnt; cnt++) {
++
++ pib_addr = crystalhd_get_addr_from_pib_Q(hw);
++ crystalhd_mem_rd(hw->adp, pib_addr, sizeof(C011_PIB) / 4,
++ (uint32_t *)&src_pib);
++
++ if (src_pib.bFormatChange) {
++ rx_pkt = (crystalhd_rx_dma_pkt *)crystalhd_dioq_fetch(hw->rx_freeq);
++ if (!rx_pkt)
++ return;
++ rx_pkt->flags = 0;
++ rx_pkt->flags |= COMP_FLAG_PIB_VALID | COMP_FLAG_FMT_CHANGE;
++ AppPib = &rx_pkt->pib;
++ cpy_pib_to_app(&src_pib, AppPib);
++
++ BCMLOG(BCMLOG_DBG,
++ "App PIB:%x %x %x %x %x %x %x %x %x %x\n",
++ rx_pkt->pib.picture_number,
++ rx_pkt->pib.aspect_ratio,
++ rx_pkt->pib.chroma_format,
++ rx_pkt->pib.colour_primaries,
++ rx_pkt->pib.frame_rate,
++ rx_pkt->pib.height,
++ rx_pkt->pib.height,
++ rx_pkt->pib.n_drop,
++ rx_pkt->pib.pulldown,
++ rx_pkt->pib.ycom);
++
++ crystalhd_dioq_add(hw->rx_rdyq, (void *)rx_pkt, true, rx_pkt->pkt_tag);
++
++ }
++
++ crystalhd_rel_addr_to_pib_Q(hw, pib_addr);
++ }
++}
++
++static void crystalhd_start_rx_dma_engine(struct crystalhd_hw *hw)
++{
++ uint32_t dma_cntrl;
++
++ dma_cntrl = crystalhd_reg_rd(hw->adp, MISC1_Y_RX_SW_DESC_LIST_CTRL_STS);
++ if (!(dma_cntrl & DMA_START_BIT)) {
++ dma_cntrl |= DMA_START_BIT;
++ crystalhd_reg_wr(hw->adp, MISC1_Y_RX_SW_DESC_LIST_CTRL_STS, dma_cntrl);
++ }
++
++ dma_cntrl = crystalhd_reg_rd(hw->adp, MISC1_UV_RX_SW_DESC_LIST_CTRL_STS);
++ if (!(dma_cntrl & DMA_START_BIT)) {
++ dma_cntrl |= DMA_START_BIT;
++ crystalhd_reg_wr(hw->adp, MISC1_UV_RX_SW_DESC_LIST_CTRL_STS, dma_cntrl);
++ }
++
++ return;
++}
++
++static void crystalhd_stop_rx_dma_engine(struct crystalhd_hw *hw)
++{
++ uint32_t dma_cntrl = 0, count = 30;
++ uint32_t l0y = 1, l0uv = 1, l1y = 1, l1uv = 1;
++
++ dma_cntrl = crystalhd_reg_rd(hw->adp, MISC1_Y_RX_SW_DESC_LIST_CTRL_STS);
++ if ((dma_cntrl & DMA_START_BIT)) {
++ dma_cntrl &= ~DMA_START_BIT;
++ crystalhd_reg_wr(hw->adp, MISC1_Y_RX_SW_DESC_LIST_CTRL_STS, dma_cntrl);
++ }
++
++ dma_cntrl = crystalhd_reg_rd(hw->adp, MISC1_UV_RX_SW_DESC_LIST_CTRL_STS);
++ if ((dma_cntrl & DMA_START_BIT)) {
++ dma_cntrl &= ~DMA_START_BIT;
++ crystalhd_reg_wr(hw->adp, MISC1_UV_RX_SW_DESC_LIST_CTRL_STS, dma_cntrl);
++ }
++
++ /* Poll for 3seconds (30 * 100ms) on both the lists..*/
++ while ((l0y || l0uv || l1y || l1uv) && count) {
++
++ if (l0y) {
++ l0y = crystalhd_reg_rd(hw->adp, MISC1_Y_RX_FIRST_DESC_L_ADDR_LIST0);
++ l0y &= DMA_START_BIT;
++ if (!l0y) {
++ hw->rx_list_sts[0] &= ~rx_waiting_y_intr;
++ }
++ }
++
++ if (l1y) {
++ l1y = crystalhd_reg_rd(hw->adp, MISC1_Y_RX_FIRST_DESC_L_ADDR_LIST1);
++ l1y &= DMA_START_BIT;
++ if (!l1y) {
++ hw->rx_list_sts[1] &= ~rx_waiting_y_intr;
++ }
++ }
++
++ if (l0uv) {
++ l0uv = crystalhd_reg_rd(hw->adp, MISC1_UV_RX_FIRST_DESC_L_ADDR_LIST0);
++ l0uv &= DMA_START_BIT;
++ if (!l0uv) {
++ hw->rx_list_sts[0] &= ~rx_waiting_uv_intr;
++ }
++ }
++
++ if (l1uv) {
++ l1uv = crystalhd_reg_rd(hw->adp, MISC1_UV_RX_FIRST_DESC_L_ADDR_LIST1);
++ l1uv &= DMA_START_BIT;
++ if (!l1uv) {
++ hw->rx_list_sts[1] &= ~rx_waiting_uv_intr;
++ }
++ }
++ msleep_interruptible(100);
++ count--;
++ }
++
++ hw->rx_list_post_index = 0;
++
++ BCMLOG(BCMLOG_SSTEP, "Capture Stop: %d List0:Sts:%x List1:Sts:%x\n",
++ count, hw->rx_list_sts[0], hw->rx_list_sts[1]);
++}
++
++static BC_STATUS crystalhd_hw_prog_rxdma(struct crystalhd_hw *hw, crystalhd_rx_dma_pkt *rx_pkt)
++{
++ uint32_t y_low_addr_reg, y_high_addr_reg;
++ uint32_t uv_low_addr_reg, uv_high_addr_reg;
++ addr_64 desc_addr;
++ unsigned long flags;
++
++ if (!hw || !rx_pkt) {
++ BCMLOG_ERR("Invalid Arguments\n");
++ return BC_STS_INV_ARG;
++ }
++
++ if (hw->rx_list_post_index >= DMA_ENGINE_CNT) {
++ BCMLOG_ERR("List Out Of bounds %x\n", hw->rx_list_post_index);
++ return BC_STS_INV_ARG;
++ }
++
++ spin_lock_irqsave(&hw->rx_lock, flags);
++ /* FIXME: jarod: sts_free is an enum for 0, in crystalhd_hw.h... yuk... */
++ if (sts_free != hw->rx_list_sts[hw->rx_list_post_index]) {
++ spin_unlock_irqrestore(&hw->rx_lock, flags);
++ return BC_STS_BUSY;
++ }
++
++ if (!hw->rx_list_post_index) {
++ y_low_addr_reg = MISC1_Y_RX_FIRST_DESC_L_ADDR_LIST0;
++ y_high_addr_reg = MISC1_Y_RX_FIRST_DESC_U_ADDR_LIST0;
++ uv_low_addr_reg = MISC1_UV_RX_FIRST_DESC_L_ADDR_LIST0;
++ uv_high_addr_reg = MISC1_UV_RX_FIRST_DESC_U_ADDR_LIST0;
++ } else {
++ y_low_addr_reg = MISC1_Y_RX_FIRST_DESC_L_ADDR_LIST1;
++ y_high_addr_reg = MISC1_Y_RX_FIRST_DESC_U_ADDR_LIST1;
++ uv_low_addr_reg = MISC1_UV_RX_FIRST_DESC_L_ADDR_LIST1;
++ uv_high_addr_reg = MISC1_UV_RX_FIRST_DESC_U_ADDR_LIST1;
++ }
++ rx_pkt->pkt_tag = hw->rx_pkt_tag_seed + hw->rx_list_post_index;
++ hw->rx_list_sts[hw->rx_list_post_index] |= rx_waiting_y_intr;
++ if (rx_pkt->uv_phy_addr)
++ hw->rx_list_sts[hw->rx_list_post_index] |= rx_waiting_uv_intr;
++ hw->rx_list_post_index = (hw->rx_list_post_index + 1) % DMA_ENGINE_CNT;
++ spin_unlock_irqrestore(&hw->rx_lock, flags);
++
++ crystalhd_dioq_add(hw->rx_actq, (void *)rx_pkt, false, rx_pkt->pkt_tag);
++
++ crystalhd_start_rx_dma_engine(hw);
++ /* Program the Y descriptor */
++ desc_addr.full_addr = rx_pkt->desc_mem.phy_addr;
++ crystalhd_reg_wr(hw->adp, y_high_addr_reg, desc_addr.high_part);
++ crystalhd_reg_wr(hw->adp, y_low_addr_reg, desc_addr.low_part | 0x01);
++
++ if (rx_pkt->uv_phy_addr) {
++ /* Program the UV descriptor */
++ desc_addr.full_addr = rx_pkt->uv_phy_addr;
++ crystalhd_reg_wr(hw->adp, uv_high_addr_reg, desc_addr.high_part);
++ crystalhd_reg_wr(hw->adp, uv_low_addr_reg, desc_addr.low_part | 0x01);
++ }
++
++ return BC_STS_SUCCESS;
++}
++
++static BC_STATUS crystalhd_hw_post_cap_buff(struct crystalhd_hw *hw,
++ crystalhd_rx_dma_pkt *rx_pkt)
++{
++ BC_STATUS sts = crystalhd_hw_prog_rxdma(hw, rx_pkt);
++
++ if (sts == BC_STS_BUSY)
++ crystalhd_dioq_add(hw->rx_freeq, (void *)rx_pkt,
++ false, rx_pkt->pkt_tag);
++
++ return sts;
++}
++
++static void crystalhd_get_dnsz(struct crystalhd_hw *hw, uint32_t list_index,
++ uint32_t *y_dw_dnsz, uint32_t *uv_dw_dnsz)
++{
++ uint32_t y_dn_sz_reg, uv_dn_sz_reg;
++
++ if (!list_index) {
++ y_dn_sz_reg = MISC1_Y_RX_LIST0_CUR_BYTE_CNT;
++ uv_dn_sz_reg = MISC1_UV_RX_LIST0_CUR_BYTE_CNT;
++ } else {
++ y_dn_sz_reg = MISC1_Y_RX_LIST1_CUR_BYTE_CNT;
++ uv_dn_sz_reg = MISC1_UV_RX_LIST1_CUR_BYTE_CNT;
++ }
++
++ *y_dw_dnsz = crystalhd_reg_rd(hw->adp, y_dn_sz_reg);
++ *uv_dw_dnsz = crystalhd_reg_rd(hw->adp, uv_dn_sz_reg);
++}
++
++/*
++ * This function should be called only after making sure that the two DMA
++ * lists are free. This function does not check if DMA's are active, before
++ * turning off the DMA.
++ */
++static void crystalhd_hw_finalize_pause(struct crystalhd_hw *hw)
++{
++ uint32_t dma_cntrl, aspm;
++
++ hw->stop_pending = 0;
++
++ dma_cntrl = crystalhd_reg_rd(hw->adp, MISC1_Y_RX_SW_DESC_LIST_CTRL_STS);
++ if (dma_cntrl & DMA_START_BIT) {
++ dma_cntrl &= ~DMA_START_BIT;
++ crystalhd_reg_wr(hw->adp, MISC1_Y_RX_SW_DESC_LIST_CTRL_STS, dma_cntrl);
++ }
++
++ dma_cntrl = crystalhd_reg_rd(hw->adp, MISC1_UV_RX_SW_DESC_LIST_CTRL_STS);
++ if (dma_cntrl & DMA_START_BIT) {
++ dma_cntrl &= ~DMA_START_BIT;
++ crystalhd_reg_wr(hw->adp, MISC1_UV_RX_SW_DESC_LIST_CTRL_STS, dma_cntrl);
++ }
++ hw->rx_list_post_index = 0;
++
++ aspm = crystalhd_reg_rd(hw->adp, PCIE_DLL_DATA_LINK_CONTROL);
++ aspm |= ASPM_L1_ENABLE;
++ /* NAREN BCMLOG(BCMLOG_INFO, "aspm on\n"); */
++ crystalhd_reg_wr(hw->adp, PCIE_DLL_DATA_LINK_CONTROL, aspm);
++}
++
++static BC_STATUS crystalhd_rx_pkt_done(struct crystalhd_hw *hw, uint32_t list_index,
++ BC_STATUS comp_sts)
++{
++ crystalhd_rx_dma_pkt *rx_pkt = NULL;
++ uint32_t y_dw_dnsz, uv_dw_dnsz;
++ BC_STATUS sts = BC_STS_SUCCESS;
++
++ if (!hw || list_index >= DMA_ENGINE_CNT) {
++ BCMLOG_ERR("Invalid Arguments\n");
++ return BC_STS_INV_ARG;
++ }
++
++ rx_pkt = crystalhd_dioq_find_and_fetch(hw->rx_actq,
++ hw->rx_pkt_tag_seed + list_index);
++ if (!rx_pkt) {
++ BCMLOG_ERR("Act-Q:PostIx:%x L0Sts:%x L1Sts:%x current L:%x tag:%x comp:%x\n",
++ hw->rx_list_post_index, hw->rx_list_sts[0],
++ hw->rx_list_sts[1], list_index,
++ hw->rx_pkt_tag_seed + list_index, comp_sts);
++ return BC_STS_INV_ARG;
++ }
++
++ if (comp_sts == BC_STS_SUCCESS) {
++ crystalhd_get_dnsz(hw, list_index, &y_dw_dnsz, &uv_dw_dnsz);
++ rx_pkt->dio_req->uinfo.y_done_sz = y_dw_dnsz;
++ rx_pkt->flags = COMP_FLAG_DATA_VALID;
++ if (rx_pkt->uv_phy_addr)
++ rx_pkt->dio_req->uinfo.uv_done_sz = uv_dw_dnsz;
++ crystalhd_dioq_add(hw->rx_rdyq, rx_pkt, true,
++ hw->rx_pkt_tag_seed + list_index);
++ return sts;
++ }
++
++ /* Check if we can post this DIO again. */
++ return crystalhd_hw_post_cap_buff(hw, rx_pkt);
++}
++
++static bool crystalhd_rx_list0_handler(struct crystalhd_hw *hw, uint32_t int_sts,
++ uint32_t y_err_sts, uint32_t uv_err_sts)
++{
++ uint32_t tmp;
++ list_sts tmp_lsts;
++
++ if (!(y_err_sts & GET_Y0_ERR_MSK) && !(uv_err_sts & GET_UV0_ERR_MSK))
++ return false;
++
++ tmp_lsts = hw->rx_list_sts[0];
++
++ /* Y0 - DMA */
++ tmp = y_err_sts & GET_Y0_ERR_MSK;
++ if (int_sts & INTR_INTR_STATUS_L0_Y_RX_DMA_DONE_INTR_MASK)
++ hw->rx_list_sts[0] &= ~rx_waiting_y_intr;
++
++ if (y_err_sts & MISC1_Y_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_MASK) {
++ hw->rx_list_sts[0] &= ~rx_waiting_y_intr;
++ tmp &= ~MISC1_Y_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_MASK;
++ }
++
++ if (y_err_sts & MISC1_Y_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_MASK) {
++ hw->rx_list_sts[0] &= ~rx_y_mask;
++ hw->rx_list_sts[0] |= rx_y_error;
++ tmp &= ~MISC1_Y_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_MASK;
++ }
++
++ if (tmp) {
++ hw->rx_list_sts[0] &= ~rx_y_mask;
++ hw->rx_list_sts[0] |= rx_y_error;
++ hw->rx_list_post_index = 0;
++ }
++
++ /* UV0 - DMA */
++ tmp = uv_err_sts & GET_UV0_ERR_MSK;
++ if (int_sts & INTR_INTR_STATUS_L0_UV_RX_DMA_DONE_INTR_MASK)
++ hw->rx_list_sts[0] &= ~rx_waiting_uv_intr;
++
++ if (uv_err_sts & MISC1_UV_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_MASK) {
++ hw->rx_list_sts[0] &= ~rx_waiting_uv_intr;
++ tmp &= ~MISC1_UV_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_MASK;
++ }
++
++ if (uv_err_sts & MISC1_UV_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_MASK) {
++ hw->rx_list_sts[0] &= ~rx_uv_mask;
++ hw->rx_list_sts[0] |= rx_uv_error;
++ tmp &= ~MISC1_UV_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_MASK;
++ }
++
++ if (tmp) {
++ hw->rx_list_sts[0] &= ~rx_uv_mask;
++ hw->rx_list_sts[0] |= rx_uv_error;
++ hw->rx_list_post_index = 0;
++ }
++
++ if (y_err_sts & GET_Y0_ERR_MSK) {
++ tmp = y_err_sts & GET_Y0_ERR_MSK;
++ crystalhd_reg_wr(hw->adp, MISC1_Y_RX_ERROR_STATUS, tmp);
++ }
++
++ if (uv_err_sts & GET_UV0_ERR_MSK) {
++ tmp = uv_err_sts & GET_UV0_ERR_MSK;
++ crystalhd_reg_wr(hw->adp, MISC1_UV_RX_ERROR_STATUS, tmp);
++ }
++
++ return (tmp_lsts != hw->rx_list_sts[0]);
++}
++
++static bool crystalhd_rx_list1_handler(struct crystalhd_hw *hw, uint32_t int_sts,
++ uint32_t y_err_sts, uint32_t uv_err_sts)
++{
++ uint32_t tmp;
++ list_sts tmp_lsts;
++
++ if (!(y_err_sts & GET_Y1_ERR_MSK) && !(uv_err_sts & GET_UV1_ERR_MSK))
++ return false;
++
++ tmp_lsts = hw->rx_list_sts[1];
++
++ /* Y1 - DMA */
++ tmp = y_err_sts & GET_Y1_ERR_MSK;
++ if (int_sts & INTR_INTR_STATUS_L1_Y_RX_DMA_DONE_INTR_MASK)
++ hw->rx_list_sts[1] &= ~rx_waiting_y_intr;
++
++ if (y_err_sts & MISC1_Y_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_MASK) {
++ hw->rx_list_sts[1] &= ~rx_waiting_y_intr;
++ tmp &= ~MISC1_Y_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_MASK;
++ }
++
++ if (y_err_sts & MISC1_Y_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_MASK) {
++ /* Add retry-support..*/
++ hw->rx_list_sts[1] &= ~rx_y_mask;
++ hw->rx_list_sts[1] |= rx_y_error;
++ tmp &= ~MISC1_Y_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_MASK;
++ }
++
++ if (tmp) {
++ hw->rx_list_sts[1] &= ~rx_y_mask;
++ hw->rx_list_sts[1] |= rx_y_error;
++ hw->rx_list_post_index = 0;
++ }
++
++ /* UV1 - DMA */
++ tmp = uv_err_sts & GET_UV1_ERR_MSK;
++ if (int_sts & INTR_INTR_STATUS_L1_UV_RX_DMA_DONE_INTR_MASK) {
++ hw->rx_list_sts[1] &= ~rx_waiting_uv_intr;
++ }
++
++ if (uv_err_sts & MISC1_UV_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_MASK) {
++ hw->rx_list_sts[1] &= ~rx_waiting_uv_intr;
++ tmp &= ~MISC1_UV_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_MASK;
++ }
++
++ if (uv_err_sts & MISC1_UV_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_MASK) {
++ /* Add retry-support*/
++ hw->rx_list_sts[1] &= ~rx_uv_mask;
++ hw->rx_list_sts[1] |= rx_uv_error;
++ tmp &= ~MISC1_UV_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_MASK;
++ }
++
++ if (tmp) {
++ hw->rx_list_sts[1] &= ~rx_uv_mask;
++ hw->rx_list_sts[1] |= rx_uv_error;
++ hw->rx_list_post_index = 0;
++ }
++
++ if (y_err_sts & GET_Y1_ERR_MSK) {
++ tmp = y_err_sts & GET_Y1_ERR_MSK;
++ crystalhd_reg_wr(hw->adp, MISC1_Y_RX_ERROR_STATUS, tmp);
++ }
++
++ if (uv_err_sts & GET_UV1_ERR_MSK) {
++ tmp = uv_err_sts & GET_UV1_ERR_MSK;
++ crystalhd_reg_wr(hw->adp, MISC1_UV_RX_ERROR_STATUS, tmp);
++ }
++
++ return (tmp_lsts != hw->rx_list_sts[1]);
++}
++
++
++static void crystalhd_rx_isr(struct crystalhd_hw *hw, uint32_t intr_sts)
++{
++ unsigned long flags;
++ uint32_t i, list_avail = 0;
++ BC_STATUS comp_sts = BC_STS_NO_DATA;
++ uint32_t y_err_sts, uv_err_sts, y_dn_sz = 0, uv_dn_sz = 0;
++ bool ret = 0;
++
++ if (!hw) {
++ BCMLOG_ERR("Invalid Arguments\n");
++ return;
++ }
++
++ if (!(intr_sts & GET_RX_INTR_MASK))
++ return;
++
++ y_err_sts = crystalhd_reg_rd(hw->adp, MISC1_Y_RX_ERROR_STATUS);
++ uv_err_sts = crystalhd_reg_rd(hw->adp, MISC1_UV_RX_ERROR_STATUS);
++
++ for (i = 0; i < DMA_ENGINE_CNT; i++) {
++ /* Update States..*/
++ spin_lock_irqsave(&hw->rx_lock, flags);
++ if (i == 0)
++ ret = crystalhd_rx_list0_handler(hw, intr_sts, y_err_sts, uv_err_sts);
++ else
++ ret = crystalhd_rx_list1_handler(hw, intr_sts, y_err_sts, uv_err_sts);
++ if (ret) {
++ switch (hw->rx_list_sts[i]) {
++ case sts_free:
++ comp_sts = BC_STS_SUCCESS;
++ list_avail = 1;
++ break;
++ case rx_y_error:
++ case rx_uv_error:
++ case rx_sts_error:
++ /* We got error on both or Y or uv. */
++ hw->stats.rx_errors++;
++ crystalhd_get_dnsz(hw, i, &y_dn_sz, &uv_dn_sz);
++ /* FIXME: jarod: this is where my mini pci-e card is tripping up */
++ BCMLOG(BCMLOG_DBG, "list_index:%x rx[%d] Y:%x "
++ "UV:%x Int:%x YDnSz:%x UVDnSz:%x\n",
++ i, hw->stats.rx_errors, y_err_sts,
++ uv_err_sts, intr_sts, y_dn_sz, uv_dn_sz);
++ hw->rx_list_sts[i] = sts_free;
++ comp_sts = BC_STS_ERROR;
++ break;
++ default:
++ /* Wait for completion..*/
++ comp_sts = BC_STS_NO_DATA;
++ break;
++ }
++ }
++ spin_unlock_irqrestore(&hw->rx_lock, flags);
++
++ /* handle completion...*/
++ if (comp_sts != BC_STS_NO_DATA) {
++ crystalhd_rx_pkt_done(hw, i, comp_sts);
++ comp_sts = BC_STS_NO_DATA;
++ }
++ }
++
++ if (list_avail) {
++ if (hw->stop_pending) {
++ if ((hw->rx_list_sts[0] == sts_free) &&
++ (hw->rx_list_sts[1] == sts_free))
++ crystalhd_hw_finalize_pause(hw);
++ } else {
++ crystalhd_hw_start_capture(hw);
++ }
++ }
++}
++
++static BC_STATUS crystalhd_fw_cmd_post_proc(struct crystalhd_hw *hw,
++ BC_FW_CMD *fw_cmd)
++{
++ BC_STATUS sts = BC_STS_SUCCESS;
++ DecRspChannelStartVideo *st_rsp = NULL;
++
++ switch (fw_cmd->cmd[0]) {
++ case eCMD_C011_DEC_CHAN_START_VIDEO:
++ st_rsp = (DecRspChannelStartVideo *)fw_cmd->rsp;
++ hw->pib_del_Q_addr = st_rsp->picInfoDeliveryQ;
++ hw->pib_rel_Q_addr = st_rsp->picInfoReleaseQ;
++ BCMLOG(BCMLOG_DBG, "DelQAddr:%x RelQAddr:%x\n",
++ hw->pib_del_Q_addr, hw->pib_rel_Q_addr);
++ break;
++ case eCMD_C011_INIT:
++ if (!(crystalhd_load_firmware_config(hw->adp))) {
++ BCMLOG_ERR("Invalid Params.\n");
++ sts = BC_STS_FW_AUTH_FAILED;
++ }
++ break;
++ default:
++ break;
++ }
++ return sts;
++}
++
++static BC_STATUS crystalhd_put_ddr2sleep(struct crystalhd_hw *hw)
++{
++ uint32_t reg;
++ link_misc_perst_decoder_ctrl rst_cntrl_reg;
++
++ /* Pulse reset pin of 7412 (MISC_PERST_DECODER_CTRL) */
++ rst_cntrl_reg.whole_reg = crystalhd_reg_rd(hw->adp, MISC_PERST_DECODER_CTRL);
++
++ rst_cntrl_reg.bcm_7412_rst = 1;
++ crystalhd_reg_wr(hw->adp, MISC_PERST_DECODER_CTRL, rst_cntrl_reg.whole_reg);
++ msleep_interruptible(50);
++
++ rst_cntrl_reg.bcm_7412_rst = 0;
++ crystalhd_reg_wr(hw->adp, MISC_PERST_DECODER_CTRL, rst_cntrl_reg.whole_reg);
++
++ /* Close all banks, put DDR in idle */
++ bc_dec_reg_wr(hw->adp, SDRAM_PRECHARGE, 0);
++
++ /* Set bit 25 (drop CKE pin of DDR) */
++ reg = bc_dec_reg_rd(hw->adp, SDRAM_PARAM);
++ reg |= 0x02000000;
++ bc_dec_reg_wr(hw->adp, SDRAM_PARAM, reg);
++
++ /* Reset the audio block */
++ bc_dec_reg_wr(hw->adp, AUD_DSP_MISC_SOFT_RESET, 0x1);
++
++ /* Power down Raptor PLL */
++ reg = bc_dec_reg_rd(hw->adp, DecHt_PllCCtl);
++ reg |= 0x00008000;
++ bc_dec_reg_wr(hw->adp, DecHt_PllCCtl, reg);
++
++ /* Power down all Audio PLL */
++ bc_dec_reg_wr(hw->adp, AIO_MISC_PLL_RESET, 0x1);
++
++ /* Power down video clock (75MHz) */
++ reg = bc_dec_reg_rd(hw->adp, DecHt_PllECtl);
++ reg |= 0x00008000;
++ bc_dec_reg_wr(hw->adp, DecHt_PllECtl, reg);
++
++ /* Power down video clock (75MHz) */
++ reg = bc_dec_reg_rd(hw->adp, DecHt_PllDCtl);
++ reg |= 0x00008000;
++ bc_dec_reg_wr(hw->adp, DecHt_PllDCtl, reg);
++
++ /* Power down core clock (200MHz) */
++ reg = bc_dec_reg_rd(hw->adp, DecHt_PllACtl);
++ reg |= 0x00008000;
++ bc_dec_reg_wr(hw->adp, DecHt_PllACtl, reg);
++
++ /* Power down core clock (200MHz) */
++ reg = bc_dec_reg_rd(hw->adp, DecHt_PllBCtl);
++ reg |= 0x00008000;
++ bc_dec_reg_wr(hw->adp, DecHt_PllBCtl, reg);
++
++ return BC_STS_SUCCESS;
++}
++
++/************************************************
++**
++*************************************************/
++
++BC_STATUS crystalhd_download_fw(struct crystalhd_adp *adp, void *buffer, uint32_t sz)
++{
++ uint32_t reg_data, cnt, *temp_buff;
++ uint32_t fw_sig_len = 36;
++ uint32_t dram_offset = BC_FWIMG_ST_ADDR, sig_reg;
++
++ BCMLOG_ENTER;
++
++ if (!adp || !buffer || !sz) {
++ BCMLOG_ERR("Invalid Params.\n");
++ return BC_STS_INV_ARG;
++ }
++
++ reg_data = crystalhd_reg_rd(adp, OTP_CMD);
++ if (!(reg_data & 0x02)) {
++ BCMLOG_ERR("Invalid hw config.. otp not programmed\n");
++ return BC_STS_ERROR;
++ }
++
++ reg_data = 0;
++ crystalhd_reg_wr(adp, DCI_CMD, 0);
++ reg_data |= BC_BIT(0);
++ crystalhd_reg_wr(adp, DCI_CMD, reg_data);
++
++ reg_data = 0;
++ cnt = 1000;
++ msleep_interruptible(10);
++
++ while (reg_data != BC_BIT(4)) {
++ reg_data = crystalhd_reg_rd(adp, DCI_STATUS);
++ reg_data &= BC_BIT(4);
++ if (--cnt == 0) {
++ BCMLOG_ERR("Firmware Download RDY Timeout.\n");
++ return BC_STS_TIMEOUT;
++ }
++ }
++
++ msleep_interruptible(10);
++ /* Load the FW to the FW_ADDR field in the DCI_FIRMWARE_ADDR */
++ crystalhd_reg_wr(adp, DCI_FIRMWARE_ADDR, dram_offset);
++ temp_buff = (uint32_t *)buffer;
++ for (cnt = 0; cnt < (sz - fw_sig_len); cnt += 4) {
++ crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (dram_offset >> 19));
++ crystalhd_reg_wr(adp, DCI_FIRMWARE_DATA, *temp_buff);
++ dram_offset += 4;
++ temp_buff++;
++ }
++ msleep_interruptible(10);
++
++ temp_buff++;
++
++ sig_reg = (uint32_t)DCI_SIGNATURE_DATA_7;
++ for (cnt = 0; cnt < 8; cnt++) {
++ uint32_t swapped_data = *temp_buff;
++ swapped_data = bswap_32_1(swapped_data);
++ crystalhd_reg_wr(adp, sig_reg, swapped_data);
++ sig_reg -= 4;
++ temp_buff++;
++ }
++ msleep_interruptible(10);
++
++ reg_data = 0;
++ reg_data |= BC_BIT(1);
++ crystalhd_reg_wr(adp, DCI_CMD, reg_data);
++ msleep_interruptible(10);
++
++ reg_data = 0;
++ reg_data = crystalhd_reg_rd(adp, DCI_STATUS);
++
++ if ((reg_data & BC_BIT(9)) == BC_BIT(9)) {
++ cnt = 1000;
++ while ((reg_data & BC_BIT(0)) != BC_BIT(0)) {
++ reg_data = crystalhd_reg_rd(adp, DCI_STATUS);
++ reg_data &= BC_BIT(0);
++ if (!(--cnt))
++ break;
++ msleep_interruptible(10);
++ }
++ reg_data = 0;
++ reg_data = crystalhd_reg_rd(adp, DCI_CMD);
++ reg_data |= BC_BIT(4);
++ crystalhd_reg_wr(adp, DCI_CMD, reg_data);
++
++ } else {
++ BCMLOG_ERR("F/w Signature mismatch\n");
++ return BC_STS_FW_AUTH_FAILED;
++ }
++
++ BCMLOG(BCMLOG_INFO, "Firmware Downloaded Successfully\n");
++ return BC_STS_SUCCESS;;
++}
++
++BC_STATUS crystalhd_do_fw_cmd(struct crystalhd_hw *hw, BC_FW_CMD *fw_cmd)
++{
++ uint32_t cnt = 0, cmd_res_addr;
++ uint32_t *cmd_buff, *res_buff;
++ wait_queue_head_t fw_cmd_event;
++ int rc = 0;
++ BC_STATUS sts;
++
++ crystalhd_create_event(&fw_cmd_event);
++
++ BCMLOG_ENTER;
++
++ if (!hw || !fw_cmd) {
++ BCMLOG_ERR("Invalid Arguments\n");
++ return BC_STS_INV_ARG;
++ }
++
++ cmd_buff = fw_cmd->cmd;
++ res_buff = fw_cmd->rsp;
++
++ if (!cmd_buff || !res_buff) {
++ BCMLOG_ERR("Invalid Parameters for F/W Command \n");
++ return BC_STS_INV_ARG;
++ }
++
++ hw->pwr_lock++;
++
++ hw->fwcmd_evt_sts = 0;
++ hw->pfw_cmd_event = &fw_cmd_event;
++
++ /*Write the command to the memory*/
++ crystalhd_mem_wr(hw->adp, TS_Host2CpuSnd, FW_CMD_BUFF_SZ, cmd_buff);
++
++ /*Memory Read for memory arbitrator flush*/
++ crystalhd_mem_rd(hw->adp, TS_Host2CpuSnd, 1, &cnt);
++
++ /* Write the command address to mailbox */
++ bc_dec_reg_wr(hw->adp, Hst2CpuMbx1, TS_Host2CpuSnd);
++ msleep_interruptible(50);
++
++ crystalhd_wait_on_event(&fw_cmd_event, hw->fwcmd_evt_sts, 20000, rc, 0);
++
++ if (!rc) {
++ sts = BC_STS_SUCCESS;
++ } else if (rc == -EBUSY) {
++ BCMLOG_ERR("Firmware command T/O\n");
++ sts = BC_STS_TIMEOUT;
++ } else if (rc == -EINTR) {
++ BCMLOG(BCMLOG_DBG, "FwCmd Wait Signal int.\n");
++ sts = BC_STS_IO_USER_ABORT;
++ } else {
++ BCMLOG_ERR("FwCmd IO Error.\n");
++ sts = BC_STS_IO_ERROR;
++ }
++
++ if (sts != BC_STS_SUCCESS) {
++ BCMLOG_ERR("FwCmd Failed.\n");
++ hw->pwr_lock--;
++ return sts;
++ }
++
++ /*Get the Responce Address*/
++ cmd_res_addr = bc_dec_reg_rd(hw->adp, Cpu2HstMbx1);
++
++ /*Read the Response*/
++ crystalhd_mem_rd(hw->adp, cmd_res_addr, FW_CMD_BUFF_SZ, res_buff);
++
++ hw->pwr_lock--;
++
++ if (res_buff[2] != C011_RET_SUCCESS) {
++ BCMLOG_ERR("res_buff[2] != C011_RET_SUCCESS\n");
++ return BC_STS_FW_CMD_ERR;
++ }
++
++ sts = crystalhd_fw_cmd_post_proc(hw, fw_cmd);
++ if (sts != BC_STS_SUCCESS)
++ BCMLOG_ERR("crystalhd_fw_cmd_post_proc Failed.\n");
++
++ return sts;
++}
++
++bool crystalhd_hw_interrupt(struct crystalhd_adp *adp, struct crystalhd_hw *hw)
++{
++ uint32_t intr_sts = 0;
++ uint32_t deco_intr = 0;
++ bool rc = 0;
++
++ if (!adp || !hw->dev_started)
++ return rc;
++
++ hw->stats.num_interrupts++;
++ hw->pwr_lock++;
++
++ deco_intr = bc_dec_reg_rd(adp, Stream2Host_Intr_Sts);
++ intr_sts = crystalhd_reg_rd(adp, INTR_INTR_STATUS);
++
++ if (intr_sts) {
++ /* let system know we processed interrupt..*/
++ rc = 1;
++ hw->stats.dev_interrupts++;
++ }
++
++ if (deco_intr && (deco_intr != 0xdeaddead)) {
++
++ if (deco_intr & 0x80000000) {
++ /*Set the Event and the status flag*/
++ if (hw->pfw_cmd_event) {
++ hw->fwcmd_evt_sts = 1;
++ crystalhd_set_event(hw->pfw_cmd_event);
++ }
++ }
++
++ if (deco_intr & BC_BIT(1))
++ crystalhd_hw_proc_pib(hw);
++
++ bc_dec_reg_wr(adp, Stream2Host_Intr_Sts, deco_intr);
++ /* FIXME: jarod: No udelay? might this be the real reason mini pci-e cards were stalling out? */
++ bc_dec_reg_wr(adp, Stream2Host_Intr_Sts, 0);
++ rc = 1;
++ }
++
++ /* Rx interrupts */
++ crystalhd_rx_isr(hw, intr_sts);
++
++ /* Tx interrupts*/
++ crystalhd_tx_isr(hw, intr_sts);
++
++ /* Clear interrupts */
++ if (rc) {
++ if (intr_sts)
++ crystalhd_reg_wr(adp, INTR_INTR_CLR_REG, intr_sts);
++
++ crystalhd_reg_wr(adp, INTR_EOI_CTRL, 1);
++ }
++
++ hw->pwr_lock--;
++
++ return rc;
++}
++
++BC_STATUS crystalhd_hw_open(struct crystalhd_hw *hw, struct crystalhd_adp *adp)
++{
++ if (!hw || !adp) {
++ BCMLOG_ERR("Invalid Arguments\n");
++ return BC_STS_INV_ARG;
++ }
++
++ if (hw->dev_started)
++ return BC_STS_SUCCESS;
++
++ memset(hw, 0, sizeof(struct crystalhd_hw));
++
++ hw->adp = adp;
++ spin_lock_init(&hw->lock);
++ spin_lock_init(&hw->rx_lock);
++ /* FIXME: jarod: what are these magic numbers?!? */
++ hw->tx_ioq_tag_seed = 0x70023070;
++ hw->rx_pkt_tag_seed = 0x70029070;
++
++ hw->stop_pending = 0;
++ crystalhd_start_device(hw->adp);
++ hw->dev_started = true;
++
++ /* set initial core clock */
++ hw->core_clock_mhz = CLOCK_PRESET;
++ hw->prev_n = 0;
++ hw->pwr_lock = 0;
++ crystalhd_hw_set_core_clock(hw);
++
++ return BC_STS_SUCCESS;
++}
++
++BC_STATUS crystalhd_hw_close(struct crystalhd_hw *hw)
++{
++ if (!hw) {
++ BCMLOG_ERR("Invalid Arguments\n");
++ return BC_STS_INV_ARG;
++ }
++
++ if (!hw->dev_started)
++ return BC_STS_SUCCESS;
++
++ /* Stop and DDR sleep will happen in here */
++ crystalhd_hw_suspend(hw);
++ hw->dev_started = false;
++
++ return BC_STS_SUCCESS;
++}
++
++BC_STATUS crystalhd_hw_setup_dma_rings(struct crystalhd_hw *hw)
++{
++ unsigned int i;
++ void *mem;
++ size_t mem_len;
++ dma_addr_t phy_addr;
++ BC_STATUS sts = BC_STS_SUCCESS;
++ crystalhd_rx_dma_pkt *rpkt;
++
++ if (!hw || !hw->adp) {
++ BCMLOG_ERR("Invalid Arguments\n");
++ return BC_STS_INV_ARG;
++ }
++
++ sts = crystalhd_hw_create_ioqs(hw);
++ if (sts != BC_STS_SUCCESS) {
++ BCMLOG_ERR("Failed to create IOQs..\n");
++ return sts;
++ }
++
++ mem_len = BC_LINK_MAX_SGLS * sizeof(dma_descriptor);
++
++ for (i = 0; i < BC_TX_LIST_CNT; i++) {
++ mem = bc_kern_dma_alloc(hw->adp, mem_len, &phy_addr);
++ if (mem) {
++ memset(mem, 0, mem_len);
++ } else {
++ BCMLOG_ERR("Insufficient Memory For TX\n");
++ crystalhd_hw_free_dma_rings(hw);
++ return BC_STS_INSUFF_RES;
++ }
++ /* rx_pkt_pool -- static memory allocation */
++ hw->tx_pkt_pool[i].desc_mem.pdma_desc_start = mem;
++ hw->tx_pkt_pool[i].desc_mem.phy_addr = phy_addr;
++ hw->tx_pkt_pool[i].desc_mem.sz = BC_LINK_MAX_SGLS *
++ sizeof(dma_descriptor);
++ hw->tx_pkt_pool[i].list_tag = 0;
++
++ /* Add TX dma requests to Free Queue..*/
++ sts = crystalhd_dioq_add(hw->tx_freeq,
++ &hw->tx_pkt_pool[i], false, 0);
++ if (sts != BC_STS_SUCCESS) {
++ crystalhd_hw_free_dma_rings(hw);
++ return sts;
++ }
++ }
++
++ for (i = 0; i < BC_RX_LIST_CNT; i++) {
++ rpkt = kzalloc(sizeof(*rpkt), GFP_KERNEL);
++ if (!rpkt) {
++ BCMLOG_ERR("Insufficient Memory For RX\n");
++ crystalhd_hw_free_dma_rings(hw);
++ return BC_STS_INSUFF_RES;
++ }
++
++ mem = bc_kern_dma_alloc(hw->adp, mem_len, &phy_addr);
++ if (mem) {
++ memset(mem, 0, mem_len);
++ } else {
++ BCMLOG_ERR("Insufficient Memory For RX\n");
++ crystalhd_hw_free_dma_rings(hw);
++ return BC_STS_INSUFF_RES;
++ }
++ rpkt->desc_mem.pdma_desc_start = mem;
++ rpkt->desc_mem.phy_addr = phy_addr;
++ rpkt->desc_mem.sz = BC_LINK_MAX_SGLS * sizeof(dma_descriptor);
++ rpkt->pkt_tag = hw->rx_pkt_tag_seed + i;
++ crystalhd_hw_free_rx_pkt(hw, rpkt);
++ }
++
++ return BC_STS_SUCCESS;
++}
++
++BC_STATUS crystalhd_hw_free_dma_rings(struct crystalhd_hw *hw)
++{
++ unsigned int i;
++ crystalhd_rx_dma_pkt *rpkt = NULL;
++
++ if (!hw || !hw->adp) {
++ BCMLOG_ERR("Invalid Arguments\n");
++ return BC_STS_INV_ARG;
++ }
++
++ /* Delete all IOQs.. */
++ crystalhd_hw_delete_ioqs(hw);
++
++ for (i = 0; i < BC_TX_LIST_CNT; i++) {
++ if (hw->tx_pkt_pool[i].desc_mem.pdma_desc_start) {
++ bc_kern_dma_free(hw->adp,
++ hw->tx_pkt_pool[i].desc_mem.sz,
++ hw->tx_pkt_pool[i].desc_mem.pdma_desc_start,
++ hw->tx_pkt_pool[i].desc_mem.phy_addr);
++
++ hw->tx_pkt_pool[i].desc_mem.pdma_desc_start = NULL;
++ }
++ }
++
++ BCMLOG(BCMLOG_DBG, "Releasing RX Pkt pool\n");
++ do {
++ rpkt = crystalhd_hw_alloc_rx_pkt(hw);
++ if (!rpkt)
++ break;
++ bc_kern_dma_free(hw->adp, rpkt->desc_mem.sz,
++ rpkt->desc_mem.pdma_desc_start,
++ rpkt->desc_mem.phy_addr);
++ kfree(rpkt);
++ } while (rpkt);
++
++ return BC_STS_SUCCESS;
++}
++
++BC_STATUS crystalhd_hw_post_tx(struct crystalhd_hw *hw, crystalhd_dio_req *ioreq,
++ hw_comp_callback call_back,
++ wait_queue_head_t *cb_event, uint32_t *list_id,
++ uint8_t data_flags)
++{
++ tx_dma_pkt *tx_dma_packet = NULL;
++ uint32_t first_desc_u_addr, first_desc_l_addr;
++ uint32_t low_addr, high_addr;
++ addr_64 desc_addr;
++ BC_STATUS sts, add_sts;
++ uint32_t dummy_index = 0;
++ unsigned long flags;
++ bool rc;
++
++ if (!hw || !ioreq || !call_back || !cb_event || !list_id) {
++ BCMLOG_ERR("Invalid Arguments\n");
++ return BC_STS_INV_ARG;
++ }
++
++ /*
++ * Since we hit code in busy condition very frequently,
++ * we will check the code in status first before
++ * checking the availability of free elem.
++ *
++ * This will avoid the Q fetch/add in normal condition.
++ */
++ rc = crystalhd_code_in_full(hw->adp, ioreq->uinfo.xfr_len,
++ false, data_flags);
++ if (rc) {
++ hw->stats.cin_busy++;
++ return BC_STS_BUSY;
++ }
++
++ /* Get a list from TxFreeQ */
++ tx_dma_packet = (tx_dma_pkt *)crystalhd_dioq_fetch(hw->tx_freeq);
++ if (!tx_dma_packet) {
++ BCMLOG_ERR("No empty elements..\n");
++ return BC_STS_ERR_USAGE;
++ }
++
++ sts = crystalhd_xlat_sgl_to_dma_desc(ioreq,
++ &tx_dma_packet->desc_mem,
++ &dummy_index);
++ if (sts != BC_STS_SUCCESS) {
++ add_sts = crystalhd_dioq_add(hw->tx_freeq, tx_dma_packet,
++ false, 0);
++ if (add_sts != BC_STS_SUCCESS)
++ BCMLOG_ERR("double fault..\n");
++
++ return sts;
++ }
++
++ hw->pwr_lock++;
++
++ desc_addr.full_addr = tx_dma_packet->desc_mem.phy_addr;
++ low_addr = desc_addr.low_part;
++ high_addr = desc_addr.high_part;
++
++ tx_dma_packet->call_back = call_back;
++ tx_dma_packet->cb_event = cb_event;
++ tx_dma_packet->dio_req = ioreq;
++
++ spin_lock_irqsave(&hw->lock, flags);
++
++ if (hw->tx_list_post_index == 0) {
++ first_desc_u_addr = MISC1_TX_FIRST_DESC_U_ADDR_LIST0;
++ first_desc_l_addr = MISC1_TX_FIRST_DESC_L_ADDR_LIST0;
++ } else {
++ first_desc_u_addr = MISC1_TX_FIRST_DESC_U_ADDR_LIST1;
++ first_desc_l_addr = MISC1_TX_FIRST_DESC_L_ADDR_LIST1;
++ }
++
++ *list_id = tx_dma_packet->list_tag = hw->tx_ioq_tag_seed +
++ hw->tx_list_post_index;
++
++ hw->tx_list_post_index = (hw->tx_list_post_index + 1) % DMA_ENGINE_CNT;
++
++ spin_unlock_irqrestore(&hw->lock, flags);
++
++
++ /* Insert in Active Q..*/
++ crystalhd_dioq_add(hw->tx_actq, tx_dma_packet, false,
++ tx_dma_packet->list_tag);
++
++ /*
++ * Interrupt will come as soon as you write
++ * the valid bit. So be ready for that. All
++ * the initialization should happen before that.
++ */
++ crystalhd_start_tx_dma_engine(hw);
++ crystalhd_reg_wr(hw->adp, first_desc_u_addr, desc_addr.high_part);
++
++ crystalhd_reg_wr(hw->adp, first_desc_l_addr, desc_addr.low_part | 0x01);
++ /* Be sure we set the valid bit ^^^^ */
++
++ return BC_STS_SUCCESS;
++}
++
++/*
++ * This is a force cancel and we are racing with ISR.
++ *
++ * Will try to remove the req from ActQ before ISR gets it.
++ * If ISR gets it first then the completion happens in the
++ * normal path and we will return _STS_NO_DATA from here.
++ *
++ * FIX_ME: Not Tested the actual condition..
++ */
++BC_STATUS crystalhd_hw_cancel_tx(struct crystalhd_hw *hw, uint32_t list_id)
++{
++ if (!hw || !list_id) {
++ BCMLOG_ERR("Invalid Arguments\n");
++ return BC_STS_INV_ARG;
++ }
++
++ crystalhd_stop_tx_dma_engine(hw);
++ crystalhd_hw_tx_req_complete(hw, list_id, BC_STS_IO_USER_ABORT);
++
++ return BC_STS_SUCCESS;
++}
++
++BC_STATUS crystalhd_hw_add_cap_buffer(struct crystalhd_hw *hw,
++ crystalhd_dio_req *ioreq, bool en_post)
++{
++ crystalhd_rx_dma_pkt *rpkt;
++ uint32_t tag, uv_desc_ix = 0;
++ BC_STATUS sts;
++
++ if (!hw || !ioreq) {
++ BCMLOG_ERR("Invalid Arguments\n");
++ return BC_STS_INV_ARG;
++ }
++
++ rpkt = crystalhd_hw_alloc_rx_pkt(hw);
++ if (!rpkt) {
++ BCMLOG_ERR("Insufficient resources\n");
++ return BC_STS_INSUFF_RES;
++ }
++
++ rpkt->dio_req = ioreq;
++ tag = rpkt->pkt_tag;
++
++ sts = crystalhd_xlat_sgl_to_dma_desc(ioreq, &rpkt->desc_mem, &uv_desc_ix);
++ if (sts != BC_STS_SUCCESS)
++ return sts;
++
++ rpkt->uv_phy_addr = 0;
++
++ /* Store the address of UV in the rx packet for post*/
++ if (uv_desc_ix)
++ rpkt->uv_phy_addr = rpkt->desc_mem.phy_addr +
++ (sizeof(dma_descriptor) * (uv_desc_ix + 1));
++
++ if (en_post)
++ sts = crystalhd_hw_post_cap_buff(hw, rpkt);
++ else
++ sts = crystalhd_dioq_add(hw->rx_freeq, rpkt, false, tag);
++
++ return sts;
++}
++
++BC_STATUS crystalhd_hw_get_cap_buffer(struct crystalhd_hw *hw,
++ BC_PIC_INFO_BLOCK *pib,
++ crystalhd_dio_req **ioreq)
++{
++ crystalhd_rx_dma_pkt *rpkt;
++ uint32_t timeout = BC_PROC_OUTPUT_TIMEOUT / 1000;
++ uint32_t sig_pending = 0;
++
++
++ if (!hw || !ioreq || !pib) {
++ BCMLOG_ERR("Invalid Arguments\n");
++ return BC_STS_INV_ARG;
++ }
++
++ rpkt = crystalhd_dioq_fetch_wait(hw->rx_rdyq, timeout, &sig_pending);
++ if (!rpkt) {
++ if (sig_pending) {
++ BCMLOG(BCMLOG_INFO, "wait on frame time out %d\n", sig_pending);
++ return BC_STS_IO_USER_ABORT;
++ } else {
++ return BC_STS_TIMEOUT;
++ }
++ }
++
++ rpkt->dio_req->uinfo.comp_flags = rpkt->flags;
++
++ if (rpkt->flags & COMP_FLAG_PIB_VALID)
++ memcpy(pib, &rpkt->pib, sizeof(*pib));
++
++ *ioreq = rpkt->dio_req;
++
++ crystalhd_hw_free_rx_pkt(hw, rpkt);
++
++ return BC_STS_SUCCESS;
++}
++
++BC_STATUS crystalhd_hw_start_capture(struct crystalhd_hw *hw)
++{
++ crystalhd_rx_dma_pkt *rx_pkt;
++ BC_STATUS sts;
++ uint32_t i;
++
++ if (!hw) {
++ BCMLOG_ERR("Invalid Arguments\n");
++ return BC_STS_INV_ARG;
++ }
++
++ /* This is start of capture.. Post to both the lists.. */
++ for (i = 0; i < DMA_ENGINE_CNT; i++) {
++ rx_pkt = crystalhd_dioq_fetch(hw->rx_freeq);
++ if (!rx_pkt)
++ return BC_STS_NO_DATA;
++ sts = crystalhd_hw_post_cap_buff(hw, rx_pkt);
++ if (BC_STS_SUCCESS != sts)
++ break;
++
++ }
++
++ return BC_STS_SUCCESS;
++}
++
++BC_STATUS crystalhd_hw_stop_capture(struct crystalhd_hw *hw)
++{
++ void *temp = NULL;
++
++ if (!hw) {
++ BCMLOG_ERR("Invalid Arguments\n");
++ return BC_STS_INV_ARG;
++ }
++
++ crystalhd_stop_rx_dma_engine(hw);
++
++ do {
++ temp = crystalhd_dioq_fetch(hw->rx_freeq);
++ if (temp)
++ crystalhd_rx_pkt_rel_call_back(hw, temp);
++ } while (temp);
++
++ return BC_STS_SUCCESS;
++}
++
++BC_STATUS crystalhd_hw_pause(struct crystalhd_hw *hw)
++{
++ hw->stats.pause_cnt++;
++ hw->stop_pending = 1;
++
++ if ((hw->rx_list_sts[0] == sts_free) &&
++ (hw->rx_list_sts[1] == sts_free))
++ crystalhd_hw_finalize_pause(hw);
++
++ return BC_STS_SUCCESS;
++}
++
++BC_STATUS crystalhd_hw_unpause(struct crystalhd_hw *hw)
++{
++ BC_STATUS sts;
++ uint32_t aspm;
++
++ hw->stop_pending = 0;
++
++ aspm = crystalhd_reg_rd(hw->adp, PCIE_DLL_DATA_LINK_CONTROL);
++ aspm &= ~ASPM_L1_ENABLE;
++/* NAREN BCMLOG(BCMLOG_INFO, "aspm off\n"); */
++ crystalhd_reg_wr(hw->adp, PCIE_DLL_DATA_LINK_CONTROL, aspm);
++
++ sts = crystalhd_hw_start_capture(hw);
++ return sts;
++}
++
++BC_STATUS crystalhd_hw_suspend(struct crystalhd_hw *hw)
++{
++ BC_STATUS sts;
++
++ if (!hw) {
++ BCMLOG_ERR("Invalid Arguments\n");
++ return BC_STS_INV_ARG;
++ }
++
++ sts = crystalhd_put_ddr2sleep(hw);
++ if (sts != BC_STS_SUCCESS) {
++ BCMLOG_ERR("Failed to Put DDR To Sleep!!\n");
++ return BC_STS_ERROR;
++ }
++
++ if (!crystalhd_stop_device(hw->adp)) {
++ BCMLOG_ERR("Failed to Stop Device!!\n");
++ return BC_STS_ERROR;
++ }
++
++ return BC_STS_SUCCESS;
++}
++
++void crystalhd_hw_stats(struct crystalhd_hw *hw, struct crystalhd_hw_stats *stats)
++{
++ if (!hw) {
++ BCMLOG_ERR("Invalid Arguments\n");
++ return;
++ }
++
++ /* if called w/NULL stats, its a req to zero out the stats */
++ if (!stats) {
++ memset(&hw->stats, 0, sizeof(hw->stats));
++ return;
++ }
++
++ hw->stats.freeq_count = crystalhd_dioq_count(hw->rx_freeq);
++ hw->stats.rdyq_count = crystalhd_dioq_count(hw->rx_rdyq);
++ memcpy(stats, &hw->stats, sizeof(*stats));
++}
++
++BC_STATUS crystalhd_hw_set_core_clock(struct crystalhd_hw *hw)
++{
++ uint32_t reg, n, i;
++ uint32_t vco_mg, refresh_reg;
++
++ if (!hw) {
++ BCMLOG_ERR("Invalid Arguments\n");
++ return BC_STS_INV_ARG;
++ }
++
++ /* FIXME: jarod: wha? */
++ /*n = (hw->core_clock_mhz * 3) / 20 + 1; */
++ n = hw->core_clock_mhz/5;
++
++ if (n == hw->prev_n)
++ return BC_STS_CLK_NOCHG;
++
++ if (hw->pwr_lock > 0) {
++ /* BCMLOG(BCMLOG_INFO,"pwr_lock is %u\n", hw->pwr_lock) */
++ return BC_STS_CLK_NOCHG;
++ }
++
++ i = n * 27;
++ if (i < 560)
++ vco_mg = 0;
++ else if (i < 900)
++ vco_mg = 1;
++ else if (i < 1030)
++ vco_mg = 2;
++ else
++ vco_mg = 3;
++
++ reg = bc_dec_reg_rd(hw->adp, DecHt_PllACtl);
++
++ reg &= 0xFFFFCFC0;
++ reg |= n;
++ reg |= vco_mg << 12;
++
++ BCMLOG(BCMLOG_INFO, "clock is moving to %d with n %d with vco_mg %d\n",
++ hw->core_clock_mhz, n, vco_mg);
++
++ /* Change the DRAM refresh rate to accomodate the new frequency */
++ /* refresh reg = ((refresh_rate * clock_rate)/16) - 1; rounding up*/
++ refresh_reg = (7 * hw->core_clock_mhz / 16);
++ bc_dec_reg_wr(hw->adp, SDRAM_REF_PARAM, ((1 << 12) | refresh_reg));
++
++ bc_dec_reg_wr(hw->adp, DecHt_PllACtl, reg);
++
++ i = 0;
++
++ for (i = 0; i < 10; i++) {
++ reg = bc_dec_reg_rd(hw->adp, DecHt_PllACtl);
++
++ if (reg & 0x00020000) {
++ hw->prev_n = n;
++ /* FIXME: jarod: outputting a random "C" is... confusing... */
++ BCMLOG(BCMLOG_INFO, "C");
++ return BC_STS_SUCCESS;
++ } else {
++ msleep_interruptible(10);
++ }
++ }
++ BCMLOG(BCMLOG_INFO, "clk change failed\n");
++ return BC_STS_CLK_NOCHG;
++}
+diff --git a/drivers/staging/crystalhd/crystalhd_hw.h b/drivers/staging/crystalhd/crystalhd_hw.h
+new file mode 100644
+index 0000000..1c6318e
+--- /dev/null
++++ b/drivers/staging/crystalhd/crystalhd_hw.h
+@@ -0,0 +1,398 @@
++/***************************************************************************
++ * Copyright (c) 2005-2009, Broadcom Corporation.
++ *
++ * Name: crystalhd_hw . h
++ *
++ * Description:
++ * BCM70012 Linux driver hardware layer.
++ *
++ * HISTORY:
++ *
++ **********************************************************************
++ * This file is part of the crystalhd device driver.
++ *
++ * This driver is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, version 2 of the License.
++ *
++ * This driver 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 driver. If not, see <http://www.gnu.org/licenses/>.
++ **********************************************************************/
++
++#ifndef _CRYSTALHD_HW_H_
++#define _CRYSTALHD_HW_H_
++
++#include "crystalhd_misc.h"
++#include "crystalhd_fw_if.h"
++
++/* HW constants..*/
++#define DMA_ENGINE_CNT 2
++#define MAX_PIB_Q_DEPTH 64
++#define MIN_PIB_Q_DEPTH 2
++#define WR_POINTER_OFF 4
++
++#define ASPM_L1_ENABLE (BC_BIT(27))
++
++/*************************************************
++ 7412 Decoder Registers.
++**************************************************/
++#define FW_CMD_BUFF_SZ 64
++#define TS_Host2CpuSnd 0x00000100
++#define Hst2CpuMbx1 0x00100F00
++#define Cpu2HstMbx1 0x00100F04
++#define MbxStat1 0x00100F08
++#define Stream2Host_Intr_Sts 0x00100F24
++#define C011_RET_SUCCESS 0x0 /* Reutrn status of firmware command. */
++
++/* TS input status register */
++#define TS_StreamAFIFOStatus 0x0010044C
++#define TS_StreamBFIFOStatus 0x0010084C
++
++/*UART Selection definitions*/
++#define UartSelectA 0x00100300
++#define UartSelectB 0x00100304
++
++#define BSVS_UART_DEC_NONE 0x00
++#define BSVS_UART_DEC_OUTER 0x01
++#define BSVS_UART_DEC_INNER 0x02
++#define BSVS_UART_STREAM 0x03
++
++/* Code-In fifo */
++#define REG_DecCA_RegCinCTL 0xa00
++#define REG_DecCA_RegCinBase 0xa0c
++#define REG_DecCA_RegCinEnd 0xa10
++#define REG_DecCA_RegCinWrPtr 0xa04
++#define REG_DecCA_RegCinRdPtr 0xa08
++
++#define REG_Dec_TsUser0Base 0x100864
++#define REG_Dec_TsUser0Rdptr 0x100868
++#define REG_Dec_TsUser0Wrptr 0x10086C
++#define REG_Dec_TsUser0End 0x100874
++
++/* ASF Case ...*/
++#define REG_Dec_TsAudCDB2Base 0x10036c
++#define REG_Dec_TsAudCDB2Rdptr 0x100378
++#define REG_Dec_TsAudCDB2Wrptr 0x100374
++#define REG_Dec_TsAudCDB2End 0x100370
++
++/* DRAM bringup Registers */
++#define SDRAM_PARAM 0x00040804
++#define SDRAM_PRECHARGE 0x000408B0
++#define SDRAM_EXT_MODE 0x000408A4
++#define SDRAM_MODE 0x000408A0
++#define SDRAM_REFRESH 0x00040890
++#define SDRAM_REF_PARAM 0x00040808
++
++#define DecHt_PllACtl 0x34000C
++#define DecHt_PllBCtl 0x340010
++#define DecHt_PllCCtl 0x340014
++#define DecHt_PllDCtl 0x340034
++#define DecHt_PllECtl 0x340038
++#define AUD_DSP_MISC_SOFT_RESET 0x00240104
++#define AIO_MISC_PLL_RESET 0x0026000C
++#define PCIE_CLK_REQ_REG 0xDC
++#define PCI_CLK_REQ_ENABLE (BC_BIT(8))
++
++/*************************************************
++ F/W Copy engine definitions..
++**************************************************/
++#define BC_FWIMG_ST_ADDR 0x00000000
++/* FIXME: jarod: there's a kernel function that'll do this for us... */
++#define rotr32_1(x, n) (((x) >> n) | ((x) << (32 - n)))
++#define bswap_32_1(x) ((rotr32_1((x), 24) & 0x00ff00ff) | (rotr32_1((x), 8) & 0xff00ff00))
++
++#define DecHt_HostSwReset 0x340000
++#define BC_DRAM_FW_CFG_ADDR 0x001c2000
++
++typedef union _addr_64_ {
++ struct {
++ uint32_t low_part;
++ uint32_t high_part;
++ };
++
++ uint64_t full_addr;
++
++} addr_64;
++
++typedef union _intr_mask_reg_ {
++ struct {
++ uint32_t mask_tx_done:1;
++ uint32_t mask_tx_err:1;
++ uint32_t mask_rx_done:1;
++ uint32_t mask_rx_err:1;
++ uint32_t mask_pcie_err:1;
++ uint32_t mask_pcie_rbusmast_err:1;
++ uint32_t mask_pcie_rgr_bridge:1;
++ uint32_t reserved:25;
++ };
++
++ uint32_t whole_reg;
++
++} intr_mask_reg;
++
++typedef union _link_misc_perst_deco_ctrl_ {
++ struct {
++ uint32_t bcm7412_rst:1; /* 1 -> BCM7412 is held in reset. Reset value 1.*/
++ uint32_t reserved0:3; /* Reserved.No Effect*/
++ uint32_t stop_bcm_7412_clk:1; /* 1 ->Stops branch of 27MHz clk used to clk BCM7412*/
++ uint32_t reserved1:27; /* Reseved. No Effect*/
++ };
++
++ uint32_t whole_reg;
++
++} link_misc_perst_deco_ctrl;
++
++typedef union _link_misc_perst_clk_ctrl_ {
++ struct {
++ uint32_t sel_alt_clk:1; /* When set, selects a 6.75MHz clock as the source of core_clk */
++ uint32_t stop_core_clk:1; /* When set, stops the branch of core_clk that is not needed for low power operation */
++ uint32_t pll_pwr_dn:1; /* When set, powers down the main PLL. The alternate clock bit should be set
++ to select an alternate clock before setting this bit.*/
++ uint32_t reserved0:5; /* Reserved */
++ uint32_t pll_mult:8; /* This setting controls the multiplier for the PLL. */
++ uint32_t pll_div:4; /* This setting controls the divider for the PLL. */
++ uint32_t reserved1:12; /* Reserved */
++ };
++
++ uint32_t whole_reg;
++
++} link_misc_perst_clk_ctrl;
++
++
++typedef union _link_misc_perst_decoder_ctrl_ {
++ struct {
++ uint32_t bcm_7412_rst:1; /* 1 -> BCM7412 is held in reset. Reset value 1.*/
++ uint32_t res0:3; /* Reserved.No Effect*/
++ uint32_t stop_7412_clk:1; /* 1 ->Stops branch of 27MHz clk used to clk BCM7412*/
++ uint32_t res1:27; /* Reseved. No Effect */
++ };
++
++ uint32_t whole_reg;
++
++} link_misc_perst_decoder_ctrl;
++
++
++typedef union _desc_low_addr_reg_ {
++ struct {
++ uint32_t list_valid:1;
++ uint32_t reserved:4;
++ uint32_t low_addr:27;
++ };
++
++ uint32_t whole_reg;
++
++} desc_low_addr_reg;
++
++typedef struct _dma_descriptor_ { /* 8 32-bit values */
++ /* 0th u32 */
++ uint32_t sdram_buff_addr:28; /* bits 0-27: SDRAM Address */
++ uint32_t res0:4; /* bits 28-31: Reserved */
++
++ /* 1st u32 */
++ uint32_t buff_addr_low; /* 1 buffer address low */
++ uint32_t buff_addr_high; /* 2 buffer address high */
++
++ /* 3rd u32 */
++ uint32_t res2:2; /* 0-1 - Reserved */
++ uint32_t xfer_size:23; /* 2-24 = Xfer size in words */
++ uint32_t res3:6; /* 25-30 reserved */
++ uint32_t intr_enable:1; /* 31 - Interrupt After this desc */
++
++ /* 4th u32 */
++ uint32_t endian_xlat_align:2; /* 0-1 Endian Translation */
++ uint32_t next_desc_cont:1; /* 2 - Next desc is in contig memory */
++ uint32_t res4:25; /* 3 - 27 Reserved bits */
++ uint32_t fill_bytes:2; /* 28-29 Bits Fill Bytes */
++ uint32_t dma_dir:1; /* 30 bit DMA Direction */
++ uint32_t last_rec_indicator:1; /* 31 bit Last Record Indicator */
++
++ /* 5th u32 */
++ uint32_t next_desc_addr_low; /* 32-bits Next Desc Addr lower */
++
++ /* 6th u32 */
++ uint32_t next_desc_addr_high; /* 32-bits Next Desc Addr Higher */
++
++ /* 7th u32 */
++ uint32_t res8; /* Last 32bits reserved */
++
++} dma_descriptor, *pdma_descriptor;
++
++/*
++ * We will allocate the memory in 4K pages
++ * the linked list will be a list of 32 byte descriptors.
++ * The virtual address will determine what should be freed.
++ */
++typedef struct _dma_desc_mem_ {
++ pdma_descriptor pdma_desc_start; /* 32-bytes for dma descriptor. should be first element */
++ dma_addr_t phy_addr; /* physical address of each DMA desc */
++ uint32_t sz;
++ struct _dma_desc_mem_ *Next; /* points to Next Descriptor in chain */
++
++} dma_desc_mem, *pdma_desc_mem;
++
++
++
++typedef enum _list_sts_ {
++ sts_free = 0,
++
++ /* RX-Y Bits 0:7 */
++ rx_waiting_y_intr = 0x00000001,
++ rx_y_error = 0x00000004,
++
++ /* RX-UV Bits 8:16 */
++ rx_waiting_uv_intr = 0x0000100,
++ rx_uv_error = 0x0000400,
++
++ rx_sts_waiting = (rx_waiting_y_intr|rx_waiting_uv_intr),
++ rx_sts_error = (rx_y_error|rx_uv_error),
++
++ rx_y_mask = 0x000000FF,
++ rx_uv_mask = 0x0000FF00,
++
++} list_sts;
++
++typedef struct _tx_dma_pkt_ {
++ dma_desc_mem desc_mem;
++ hw_comp_callback call_back;
++ crystalhd_dio_req *dio_req;
++ wait_queue_head_t *cb_event;
++ uint32_t list_tag;
++
++} tx_dma_pkt;
++
++typedef struct _crystalhd_rx_dma_pkt {
++ dma_desc_mem desc_mem;
++ crystalhd_dio_req *dio_req;
++ uint32_t pkt_tag;
++ uint32_t flags;
++ BC_PIC_INFO_BLOCK pib;
++ dma_addr_t uv_phy_addr;
++ struct _crystalhd_rx_dma_pkt *next;
++
++} crystalhd_rx_dma_pkt;
++
++struct crystalhd_hw_stats{
++ uint32_t rx_errors;
++ uint32_t tx_errors;
++ uint32_t freeq_count;
++ uint32_t rdyq_count;
++ uint32_t num_interrupts;
++ uint32_t dev_interrupts;
++ uint32_t cin_busy;
++ uint32_t pause_cnt;
++};
++
++struct crystalhd_hw {
++ tx_dma_pkt tx_pkt_pool[DMA_ENGINE_CNT];
++ spinlock_t lock;
++
++ uint32_t tx_ioq_tag_seed;
++ uint32_t tx_list_post_index;
++
++ crystalhd_rx_dma_pkt *rx_pkt_pool_head;
++ uint32_t rx_pkt_tag_seed;
++
++ bool dev_started;
++ void *adp;
++
++ wait_queue_head_t *pfw_cmd_event;
++ int fwcmd_evt_sts;
++
++ uint32_t pib_del_Q_addr;
++ uint32_t pib_rel_Q_addr;
++
++ crystalhd_dioq_t *tx_freeq;
++ crystalhd_dioq_t *tx_actq;
++
++ /* Rx DMA Engine Specific Locks */
++ spinlock_t rx_lock;
++ uint32_t rx_list_post_index;
++ list_sts rx_list_sts[DMA_ENGINE_CNT];
++ crystalhd_dioq_t *rx_rdyq;
++ crystalhd_dioq_t *rx_freeq;
++ crystalhd_dioq_t *rx_actq;
++ uint32_t stop_pending;
++
++ /* HW counters.. */
++ struct crystalhd_hw_stats stats;
++
++ /* Core clock in MHz */
++ uint32_t core_clock_mhz;
++ uint32_t prev_n;
++ uint32_t pwr_lock;
++};
++
++/* Clock defines for power control */
++#define CLOCK_PRESET 175
++
++/* DMA engine register BIT mask wrappers.. */
++#define DMA_START_BIT MISC1_TX_SW_DESC_LIST_CTRL_STS_TX_DMA_RUN_STOP_MASK
++
++#define GET_RX_INTR_MASK (INTR_INTR_STATUS_L1_UV_RX_DMA_ERR_INTR_MASK | \
++ INTR_INTR_STATUS_L1_UV_RX_DMA_DONE_INTR_MASK | \
++ INTR_INTR_STATUS_L1_Y_RX_DMA_ERR_INTR_MASK | \
++ INTR_INTR_STATUS_L1_Y_RX_DMA_DONE_INTR_MASK | \
++ INTR_INTR_STATUS_L0_UV_RX_DMA_ERR_INTR_MASK | \
++ INTR_INTR_STATUS_L0_UV_RX_DMA_DONE_INTR_MASK | \
++ INTR_INTR_STATUS_L0_Y_RX_DMA_ERR_INTR_MASK | \
++ INTR_INTR_STATUS_L0_Y_RX_DMA_DONE_INTR_MASK)
++
++#define GET_Y0_ERR_MSK (MISC1_Y_RX_ERROR_STATUS_RX_L0_OVERRUN_ERROR_MASK | \
++ MISC1_Y_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_MASK | \
++ MISC1_Y_RX_ERROR_STATUS_RX_L0_DESC_TX_ABORT_ERRORS_MASK | \
++ MISC1_Y_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_MASK)
++
++#define GET_UV0_ERR_MSK (MISC1_UV_RX_ERROR_STATUS_RX_L0_OVERRUN_ERROR_MASK | \
++ MISC1_UV_RX_ERROR_STATUS_RX_L0_UNDERRUN_ERROR_MASK | \
++ MISC1_UV_RX_ERROR_STATUS_RX_L0_DESC_TX_ABORT_ERRORS_MASK | \
++ MISC1_UV_RX_ERROR_STATUS_RX_L0_FIFO_FULL_ERRORS_MASK)
++
++#define GET_Y1_ERR_MSK (MISC1_Y_RX_ERROR_STATUS_RX_L1_OVERRUN_ERROR_MASK | \
++ MISC1_Y_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_MASK | \
++ MISC1_Y_RX_ERROR_STATUS_RX_L1_DESC_TX_ABORT_ERRORS_MASK | \
++ MISC1_Y_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_MASK)
++
++#define GET_UV1_ERR_MSK (MISC1_UV_RX_ERROR_STATUS_RX_L1_OVERRUN_ERROR_MASK | \
++ MISC1_UV_RX_ERROR_STATUS_RX_L1_UNDERRUN_ERROR_MASK | \
++ MISC1_UV_RX_ERROR_STATUS_RX_L1_DESC_TX_ABORT_ERRORS_MASK | \
++ MISC1_UV_RX_ERROR_STATUS_RX_L1_FIFO_FULL_ERRORS_MASK)
++
++
++/**** API Exposed to the other layers ****/
++BC_STATUS crystalhd_download_fw(struct crystalhd_adp *adp,
++ void *buffer, uint32_t sz);
++BC_STATUS crystalhd_do_fw_cmd(struct crystalhd_hw *hw, BC_FW_CMD *fw_cmd);
++bool crystalhd_hw_interrupt(struct crystalhd_adp *adp, struct crystalhd_hw *hw);
++BC_STATUS crystalhd_hw_open(struct crystalhd_hw *, struct crystalhd_adp *);
++BC_STATUS crystalhd_hw_close(struct crystalhd_hw *);
++BC_STATUS crystalhd_hw_setup_dma_rings(struct crystalhd_hw *);
++BC_STATUS crystalhd_hw_free_dma_rings(struct crystalhd_hw *);
++
++
++BC_STATUS crystalhd_hw_post_tx(struct crystalhd_hw *hw, crystalhd_dio_req *ioreq,
++ hw_comp_callback call_back,
++ wait_queue_head_t *cb_event,
++ uint32_t *list_id, uint8_t data_flags);
++
++BC_STATUS crystalhd_hw_pause(struct crystalhd_hw *hw);
++BC_STATUS crystalhd_hw_unpause(struct crystalhd_hw *hw);
++BC_STATUS crystalhd_hw_suspend(struct crystalhd_hw *hw);
++BC_STATUS crystalhd_hw_cancel_tx(struct crystalhd_hw *hw, uint32_t list_id);
++BC_STATUS crystalhd_hw_add_cap_buffer(struct crystalhd_hw *hw,
++ crystalhd_dio_req *ioreq, bool en_post);
++BC_STATUS crystalhd_hw_get_cap_buffer(struct crystalhd_hw *hw,
++ BC_PIC_INFO_BLOCK *pib,
++ crystalhd_dio_req **ioreq);
++BC_STATUS crystalhd_hw_stop_capture(struct crystalhd_hw *hw);
++BC_STATUS crystalhd_hw_start_capture(struct crystalhd_hw *hw);
++void crystalhd_hw_stats(struct crystalhd_hw *hw, struct crystalhd_hw_stats *stats);
++
++/* API to program the core clock on the decoder */
++BC_STATUS crystalhd_hw_set_core_clock(struct crystalhd_hw *);
++
++#endif
+diff --git a/drivers/staging/crystalhd/crystalhd_lnx.c b/drivers/staging/crystalhd/crystalhd_lnx.c
+new file mode 100644
+index 0000000..1f36b4d
+--- /dev/null
++++ b/drivers/staging/crystalhd/crystalhd_lnx.c
+@@ -0,0 +1,780 @@
++/***************************************************************************
++ * Copyright (c) 2005-2009, Broadcom Corporation.
++ *
++ * Name: crystalhd_lnx . c
++ *
++ * Description:
++ * BCM70010 Linux driver
++ *
++ * HISTORY:
++ *
++ **********************************************************************
++ * This file is part of the crystalhd device driver.
++ *
++ * This driver is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, version 2 of the License.
++ *
++ * This driver 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 driver. If not, see <http://www.gnu.org/licenses/>.
++ **********************************************************************/
++
++#include <linux/version.h>
++
++#include "crystalhd_lnx.h"
++
++static struct class *crystalhd_class;
++
++static struct crystalhd_adp *g_adp_info;
++
++static irqreturn_t chd_dec_isr(int irq, void *arg)
++{
++ struct crystalhd_adp *adp = (struct crystalhd_adp *) arg;
++ int rc = 0;
++ if (adp)
++ rc = crystalhd_cmd_interrupt(&adp->cmds);
++
++ return IRQ_RETVAL(rc);
++}
++
++static int chd_dec_enable_int(struct crystalhd_adp *adp)
++{
++ int rc = 0;
++
++ if (!adp || !adp->pdev) {
++ BCMLOG_ERR("Invalid arg!!\n");
++ return -EINVAL;
++ }
++
++ if (adp->pdev->msi_enabled)
++ adp->msi = 1;
++ else
++ adp->msi = pci_enable_msi(adp->pdev);
++
++ rc = request_irq(adp->pdev->irq, chd_dec_isr, IRQF_SHARED,
++ adp->name, (void *)adp);
++ if (rc) {
++ BCMLOG_ERR("Interrupt request failed.. \n");
++ pci_disable_msi(adp->pdev);
++ }
++
++ return rc;
++}
++
++static int chd_dec_disable_int(struct crystalhd_adp *adp)
++{
++ if (!adp || !adp->pdev) {
++ BCMLOG_ERR("Invalid arg!!\n");
++ return -EINVAL;
++ }
++
++ free_irq(adp->pdev->irq, adp);
++
++ if (adp->msi)
++ pci_disable_msi(adp->pdev);
++
++ return 0;
++}
++
++crystalhd_ioctl_data *chd_dec_alloc_iodata(struct crystalhd_adp *adp, bool isr)
++{
++ unsigned long flags = 0;
++ crystalhd_ioctl_data *temp;
++
++ if (!adp)
++ return NULL;
++
++ spin_lock_irqsave(&adp->lock, flags);
++
++ temp = adp->idata_free_head;
++ if (temp) {
++ adp->idata_free_head = adp->idata_free_head->next;
++ memset(temp, 0, sizeof(*temp));
++ }
++
++ spin_unlock_irqrestore(&adp->lock, flags);
++ return temp;
++}
++
++void chd_dec_free_iodata(struct crystalhd_adp *adp, crystalhd_ioctl_data *iodata,
++ bool isr)
++{
++ unsigned long flags = 0;
++
++ if (!adp || !iodata)
++ return;
++
++ spin_lock_irqsave(&adp->lock, flags);
++ iodata->next = adp->idata_free_head;
++ adp->idata_free_head = iodata;
++ spin_unlock_irqrestore(&adp->lock, flags);
++}
++
++static inline int crystalhd_user_data(unsigned long ud, void *dr, int size, int set)
++{
++ int rc;
++
++ if (!ud || !dr) {
++ BCMLOG_ERR("Invalid arg \n");
++ return -EINVAL;
++ }
++
++ if (set)
++ rc = copy_to_user((void *)ud, dr, size);
++ else
++ rc = copy_from_user(dr, (void *)ud, size);
++
++ if (rc) {
++ BCMLOG_ERR("Invalid args for command \n");
++ rc = -EFAULT;
++ }
++
++ return rc;
++}
++
++static int chd_dec_fetch_cdata(struct crystalhd_adp *adp, crystalhd_ioctl_data *io,
++ uint32_t m_sz, unsigned long ua)
++{
++ unsigned long ua_off;
++ int rc = 0;
++
++ if (!adp || !io || !ua || !m_sz) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return -EINVAL;
++ }
++
++ io->add_cdata = vmalloc(m_sz);
++ if (!io->add_cdata) {
++ BCMLOG_ERR("kalloc fail for sz:%x\n", m_sz);
++ return -ENOMEM;
++ }
++
++ io->add_cdata_sz = m_sz;
++ ua_off = ua + sizeof(io->udata);
++ rc = crystalhd_user_data(ua_off, io->add_cdata, io->add_cdata_sz, 0);
++ if (rc) {
++ BCMLOG_ERR("failed to pull add_cdata sz:%x ua_off:%x\n",
++ io->add_cdata_sz, (unsigned int)ua_off);
++ if (io->add_cdata) {
++ kfree(io->add_cdata);
++ io->add_cdata = NULL;
++ }
++ return -ENODATA;
++ }
++
++ return rc;
++}
++
++static int chd_dec_release_cdata(struct crystalhd_adp *adp,
++ crystalhd_ioctl_data *io, unsigned long ua)
++{
++ unsigned long ua_off;
++ int rc;
++
++ if (!adp || !io || !ua) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return -EINVAL;
++ }
++
++ if (io->cmd != BCM_IOC_FW_DOWNLOAD) {
++ ua_off = ua + sizeof(io->udata);
++ rc = crystalhd_user_data(ua_off, io->add_cdata,
++ io->add_cdata_sz, 1);
++ if (rc) {
++ BCMLOG_ERR("failed to push add_cdata sz:%x ua_off:%x\n",
++ io->add_cdata_sz, (unsigned int)ua_off);
++ return -ENODATA;
++ }
++ }
++
++ if (io->add_cdata) {
++ vfree(io->add_cdata);
++ io->add_cdata = NULL;
++ }
++
++ return 0;
++}
++
++static int chd_dec_proc_user_data(struct crystalhd_adp *adp,
++ crystalhd_ioctl_data *io,
++ unsigned long ua, int set)
++{
++ int rc;
++ uint32_t m_sz = 0;
++
++ if (!adp || !io || !ua) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return -EINVAL;
++ }
++
++ rc = crystalhd_user_data(ua, &io->udata, sizeof(io->udata), set);
++ if (rc) {
++ BCMLOG_ERR("failed to %s iodata \n", (set ? "set" : "get"));
++ return rc;
++ }
++
++ switch (io->cmd) {
++ case BCM_IOC_MEM_RD:
++ case BCM_IOC_MEM_WR:
++ case BCM_IOC_FW_DOWNLOAD:
++ m_sz = io->udata.u.devMem.NumDwords * 4;
++ if (set)
++ rc = chd_dec_release_cdata(adp, io, ua);
++ else
++ rc = chd_dec_fetch_cdata(adp, io, m_sz, ua);
++ break;
++ default:
++ break;
++ }
++
++ return rc;
++}
++
++static int chd_dec_api_cmd(struct crystalhd_adp *adp, unsigned long ua,
++ uint32_t uid, uint32_t cmd, crystalhd_cmd_proc func)
++{
++ int rc;
++ crystalhd_ioctl_data *temp;
++ BC_STATUS sts = BC_STS_SUCCESS;
++
++ temp = chd_dec_alloc_iodata(adp, 0);
++ if (!temp) {
++ BCMLOG_ERR("Failed to get iodata..\n");
++ return -EINVAL;
++ }
++
++ temp->u_id = uid;
++ temp->cmd = cmd;
++
++ rc = chd_dec_proc_user_data(adp, temp, ua, 0);
++ if (!rc) {
++ sts = func(&adp->cmds, temp);
++ if (sts == BC_STS_PENDING)
++ sts = BC_STS_NOT_IMPL;
++ temp->udata.RetSts = sts;
++ rc = chd_dec_proc_user_data(adp, temp, ua, 1);
++ }
++
++ if (temp) {
++ chd_dec_free_iodata(adp, temp, 0);
++ temp = NULL;
++ }
++
++ return rc;
++}
++
++/* ========================= API interfaces =================================*/
++static int chd_dec_ioctl(struct inode *in, struct file *fd,
++ unsigned int cmd, unsigned long ua)
++{
++ struct crystalhd_adp *adp = chd_get_adp();
++ crystalhd_cmd_proc cproc;
++ struct crystalhd_user *uc;
++
++ if (!adp || !fd) {
++ BCMLOG_ERR("Invalid adp\n");
++ return -EINVAL;
++ }
++
++ uc = (struct crystalhd_user *)fd->private_data;
++ if (!uc) {
++ BCMLOG_ERR("Failed to get uc\n");
++ return -ENODATA;
++ }
++
++ cproc = crystalhd_get_cmd_proc(&adp->cmds, cmd, uc);
++ if (!cproc) {
++ BCMLOG_ERR("Unhandled command: %d\n", cmd);
++ return -EINVAL;
++ }
++
++ return chd_dec_api_cmd(adp, ua, uc->uid, cmd, cproc);
++}
++
++static int chd_dec_open(struct inode *in, struct file *fd)
++{
++ struct crystalhd_adp *adp = chd_get_adp();
++ int rc = 0;
++ BC_STATUS sts = BC_STS_SUCCESS;
++ struct crystalhd_user *uc = NULL;
++
++ BCMLOG_ENTER;
++ if (!adp) {
++ BCMLOG_ERR("Invalid adp\n");
++ return -EINVAL;
++ }
++
++ if (adp->cfg_users >= BC_LINK_MAX_OPENS) {
++ BCMLOG(BCMLOG_INFO, "Already in use.%d\n", adp->cfg_users);
++ return -EBUSY;
++ }
++
++ sts = crystalhd_user_open(&adp->cmds, &uc);
++ if (sts != BC_STS_SUCCESS) {
++ BCMLOG_ERR("cmd_user_open - %d \n", sts);
++ rc = -EBUSY;
++ }
++
++ adp->cfg_users++;
++
++ fd->private_data = uc;
++
++ return rc;
++}
++
++static int chd_dec_close(struct inode *in, struct file *fd)
++{
++ struct crystalhd_adp *adp = chd_get_adp();
++ struct crystalhd_user *uc;
++
++ BCMLOG_ENTER;
++ if (!adp) {
++ BCMLOG_ERR("Invalid adp \n");
++ return -EINVAL;
++ }
++
++ uc = (struct crystalhd_user *)fd->private_data;
++ if (!uc) {
++ BCMLOG_ERR("Failed to get uc\n");
++ return -ENODATA;
++ }
++
++ crystalhd_user_close(&adp->cmds, uc);
++
++ adp->cfg_users--;
++
++ return 0;
++}
++
++static const struct file_operations chd_dec_fops = {
++ .owner = THIS_MODULE,
++ .ioctl = chd_dec_ioctl,
++ .open = chd_dec_open,
++ .release = chd_dec_close,
++};
++
++static int chd_dec_init_chdev(struct crystalhd_adp *adp)
++{
++ crystalhd_ioctl_data *temp;
++ struct device *dev;
++ int rc = -ENODEV, i = 0;
++
++ if (!adp)
++ goto fail;
++
++ adp->chd_dec_major = register_chrdev(0, CRYSTALHD_API_NAME,
++ &chd_dec_fops);
++ if (adp->chd_dec_major < 0) {
++ BCMLOG_ERR("Failed to create config dev\n");
++ rc = adp->chd_dec_major;
++ goto fail;
++ }
++
++ /* register crystalhd class */
++ crystalhd_class = class_create(THIS_MODULE, "crystalhd");
++ if (IS_ERR(crystalhd_class)) {
++ BCMLOG_ERR("failed to create class\n");
++ goto fail;
++ }
++
++ dev = device_create(crystalhd_class, NULL, MKDEV(adp->chd_dec_major, 0),
++ NULL, "crystalhd");
++ if (!dev) {
++ BCMLOG_ERR("failed to create device\n");
++ goto device_create_fail;
++ }
++
++ rc = crystalhd_create_elem_pool(adp, BC_LINK_ELEM_POOL_SZ);
++ if (rc) {
++ BCMLOG_ERR("failed to create device\n");
++ goto elem_pool_fail;
++ }
++
++ /* Allocate general purpose ioctl pool. */
++ for (i = 0; i < CHD_IODATA_POOL_SZ; i++) {
++ /* FIXME: jarod: why atomic? */
++ temp = kzalloc(sizeof(crystalhd_ioctl_data), GFP_ATOMIC);
++ if (!temp) {
++ BCMLOG_ERR("ioctl data pool kzalloc failed\n");
++ rc = -ENOMEM;
++ goto kzalloc_fail;
++ }
++ /* Add to global pool.. */
++ chd_dec_free_iodata(adp, temp, 0);
++ }
++
++ return 0;
++
++kzalloc_fail:
++ crystalhd_delete_elem_pool(adp);
++elem_pool_fail:
++ device_destroy(crystalhd_class, MKDEV(adp->chd_dec_major, 0));
++device_create_fail:
++ class_destroy(crystalhd_class);
++fail:
++ return rc;
++}
++
++static void chd_dec_release_chdev(struct crystalhd_adp *adp)
++{
++ crystalhd_ioctl_data *temp = NULL;
++ if (!adp)
++ return;
++
++ if (adp->chd_dec_major > 0) {
++ /* unregister crystalhd class */
++ device_destroy(crystalhd_class, MKDEV(adp->chd_dec_major, 0));
++ unregister_chrdev(adp->chd_dec_major, CRYSTALHD_API_NAME);
++ BCMLOG(BCMLOG_INFO, "released api device - %d\n",
++ adp->chd_dec_major);
++ class_destroy(crystalhd_class);
++ }
++ adp->chd_dec_major = 0;
++
++ /* Clear iodata pool.. */
++ do {
++ temp = chd_dec_alloc_iodata(adp, 0);
++ if (temp)
++ kfree(temp);
++ } while (temp);
++
++ crystalhd_delete_elem_pool(adp);
++}
++
++static int chd_pci_reserve_mem(struct crystalhd_adp *pinfo)
++{
++ int rc;
++ unsigned long bar2 = pci_resource_start(pinfo->pdev, 2);
++ uint32_t mem_len = pci_resource_len(pinfo->pdev, 2);
++ unsigned long bar0 = pci_resource_start(pinfo->pdev, 0);
++ uint32_t i2o_len = pci_resource_len(pinfo->pdev, 0);
++
++ BCMLOG(BCMLOG_SSTEP, "bar2:0x%lx-0x%08x bar0:0x%lx-0x%08x\n",
++ bar2, mem_len, bar0, i2o_len);
++
++ rc = check_mem_region(bar2, mem_len);
++ if (rc) {
++ BCMLOG_ERR("No valid mem region...\n");
++ return -ENOMEM;
++ }
++
++ pinfo->addr = ioremap_nocache(bar2, mem_len);
++ if (!pinfo->addr) {
++ BCMLOG_ERR("Failed to remap mem region...\n");
++ return -ENOMEM;
++ }
++
++ pinfo->pci_mem_start = bar2;
++ pinfo->pci_mem_len = mem_len;
++
++ rc = check_mem_region(bar0, i2o_len);
++ if (rc) {
++ BCMLOG_ERR("No valid mem region...\n");
++ return -ENOMEM;
++ }
++
++ pinfo->i2o_addr = ioremap_nocache(bar0, i2o_len);
++ if (!pinfo->i2o_addr) {
++ BCMLOG_ERR("Failed to remap mem region...\n");
++ return -ENOMEM;
++ }
++
++ pinfo->pci_i2o_start = bar0;
++ pinfo->pci_i2o_len = i2o_len;
++
++ rc = pci_request_regions(pinfo->pdev, pinfo->name);
++ if (rc < 0) {
++ BCMLOG_ERR("Region request failed: %d\n", rc);
++ return rc;
++ }
++
++ BCMLOG(BCMLOG_SSTEP, "Mapped addr:0x%08lx i2o_addr:0x%08lx\n",
++ (unsigned long)pinfo->addr, (unsigned long)pinfo->i2o_addr);
++
++ return 0;
++}
++
++static void chd_pci_release_mem(struct crystalhd_adp *pinfo)
++{
++ if (!pinfo)
++ return;
++
++ if (pinfo->addr)
++ iounmap(pinfo->addr);
++
++ if (pinfo->i2o_addr)
++ iounmap(pinfo->i2o_addr);
++
++ pci_release_regions(pinfo->pdev);
++}
++
++
++static void chd_dec_pci_remove(struct pci_dev *pdev)
++{
++ struct crystalhd_adp *pinfo;
++ BC_STATUS sts = BC_STS_SUCCESS;
++
++ BCMLOG_ENTER;
++
++ pinfo = (struct crystalhd_adp *) pci_get_drvdata(pdev);
++ if (!pinfo) {
++ BCMLOG_ERR("could not get adp\n");
++ return;
++ }
++
++ sts = crystalhd_delete_cmd_context(&pinfo->cmds);
++ if (sts != BC_STS_SUCCESS)
++ BCMLOG_ERR("cmd delete :%d \n", sts);
++
++ chd_dec_release_chdev(pinfo);
++
++ chd_dec_disable_int(pinfo);
++
++ chd_pci_release_mem(pinfo);
++ pci_disable_device(pinfo->pdev);
++
++ kfree(pinfo);
++ g_adp_info = NULL;
++}
++
++static int chd_dec_pci_probe(struct pci_dev *pdev,
++ const struct pci_device_id *entry)
++{
++ struct crystalhd_adp *pinfo;
++ int rc;
++ BC_STATUS sts = BC_STS_SUCCESS;
++
++ BCMLOG(BCMLOG_DBG, "PCI_INFO: Vendor:0x%04x Device:0x%04x "
++ "s_vendor:0x%04x s_device: 0x%04x\n",
++ pdev->vendor, pdev->device, pdev->subsystem_vendor,
++ pdev->subsystem_device);
++
++ /* FIXME: jarod: why atomic? */
++ pinfo = kzalloc(sizeof(struct crystalhd_adp), GFP_ATOMIC);
++ if (!pinfo) {
++ BCMLOG_ERR("Failed to allocate memory\n");
++ return -ENOMEM;
++ }
++
++ pinfo->pdev = pdev;
++
++ rc = pci_enable_device(pdev);
++ if (rc) {
++ BCMLOG_ERR("Failed to enable PCI device\n");
++ return rc;
++ }
++
++ snprintf(pinfo->name, 31, "crystalhd_pci_e:%d:%d:%d",
++ pdev->bus->number, PCI_SLOT(pdev->devfn),
++ PCI_FUNC(pdev->devfn));
++
++ rc = chd_pci_reserve_mem(pinfo);
++ if (rc) {
++ BCMLOG_ERR("Failed to setup memory regions.\n");
++ return -ENOMEM;
++ }
++
++ pinfo->present = 1;
++ pinfo->drv_data = entry->driver_data;
++
++ /* Setup adapter level lock.. */
++ spin_lock_init(&pinfo->lock);
++
++ /* setup api stuff.. */
++ chd_dec_init_chdev(pinfo);
++ rc = chd_dec_enable_int(pinfo);
++ if (rc) {
++ BCMLOG_ERR("_enable_int err:%d \n", rc);
++ pci_disable_device(pdev);
++ return -ENODEV;
++ }
++
++ /* Set dma mask... */
++ if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) {
++ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64));
++ pinfo->dmabits = 64;
++ } else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) {
++ pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32));
++ pinfo->dmabits = 32;
++ } else {
++ BCMLOG_ERR("Unabled to setup DMA %d\n", rc);
++ pci_disable_device(pdev);
++ return -ENODEV;
++ }
++
++ sts = crystalhd_setup_cmd_context(&pinfo->cmds, pinfo);
++ if (sts != BC_STS_SUCCESS) {
++ BCMLOG_ERR("cmd setup :%d \n", sts);
++ pci_disable_device(pdev);
++ return -ENODEV;
++ }
++
++ pci_set_master(pdev);
++
++ pci_set_drvdata(pdev, pinfo);
++
++ g_adp_info = pinfo;
++
++ return 0;
++
++}
++
++#ifdef CONFIG_PM
++int chd_dec_pci_suspend(struct pci_dev *pdev, pm_message_t state)
++{
++ struct crystalhd_adp *adp;
++ crystalhd_ioctl_data *temp;
++ BC_STATUS sts = BC_STS_SUCCESS;
++
++ adp = (struct crystalhd_adp *)pci_get_drvdata(pdev);
++ if (!adp) {
++ BCMLOG_ERR("could not get adp\n");
++ return -ENODEV;
++ }
++
++ temp = chd_dec_alloc_iodata(adp, false);
++ if (!temp) {
++ BCMLOG_ERR("could not get ioctl data\n");
++ return -ENODEV;
++ }
++
++ sts = crystalhd_suspend(&adp->cmds, temp);
++ if (sts != BC_STS_SUCCESS) {
++ BCMLOG_ERR("BCM70012 Suspend %d\n", sts);
++ return -ENODEV;
++ }
++
++ chd_dec_free_iodata(adp, temp, false);
++ chd_dec_disable_int(adp);
++ pci_save_state(pdev);
++
++ /* Disable IO/bus master/irq router */
++ pci_disable_device(pdev);
++ pci_set_power_state(pdev, pci_choose_state(pdev, state));
++ return 0;
++}
++
++int chd_dec_pci_resume(struct pci_dev *pdev)
++{
++ struct crystalhd_adp *adp;
++ BC_STATUS sts = BC_STS_SUCCESS;
++ int rc;
++
++ adp = (struct crystalhd_adp *)pci_get_drvdata(pdev);
++ if (!adp) {
++ BCMLOG_ERR("could not get adp\n");
++ return -ENODEV;
++ }
++
++ pci_set_power_state(pdev, PCI_D0);
++ pci_restore_state(pdev);
++
++ /* device's irq possibly is changed, driver should take care */
++ if (pci_enable_device(pdev)) {
++ BCMLOG_ERR("Failed to enable PCI device\n");
++ return 1;
++ }
++
++ pci_set_master(pdev);
++
++ rc = chd_dec_enable_int(adp);
++ if (rc) {
++ BCMLOG_ERR("_enable_int err:%d \n", rc);
++ pci_disable_device(pdev);
++ return -ENODEV;
++ }
++
++ sts = crystalhd_resume(&adp->cmds);
++ if (sts != BC_STS_SUCCESS) {
++ BCMLOG_ERR("BCM70012 Resume %d\n", sts);
++ pci_disable_device(pdev);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++#endif
++
++static struct pci_device_id chd_dec_pci_id_table[] = {
++/* vendor, device, subvendor, subdevice, class, classmask, driver_data */
++ { 0x14e4, 0x1612, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 8 },
++ { 0, },
++};
++
++struct pci_driver bc_chd_70012_driver = {
++ .name = "Broadcom 70012 Decoder",
++ .probe = chd_dec_pci_probe,
++ .remove = chd_dec_pci_remove,
++ .id_table = chd_dec_pci_id_table,
++#ifdef CONFIG_PM
++ .suspend = chd_dec_pci_suspend,
++ .resume = chd_dec_pci_resume
++#endif
++};
++MODULE_DEVICE_TABLE(pci, chd_dec_pci_id_table);
++
++
++void chd_set_log_level(struct crystalhd_adp *adp, char *arg)
++{
++ if ((!arg) || (strlen(arg) < 3))
++ g_linklog_level = BCMLOG_ERROR | BCMLOG_DATA;
++ else if (!strncmp(arg, "sstep", 5))
++ g_linklog_level = BCMLOG_INFO | BCMLOG_DATA | BCMLOG_DBG |
++ BCMLOG_SSTEP | BCMLOG_ERROR;
++ else if (!strncmp(arg, "info", 4))
++ g_linklog_level = BCMLOG_ERROR | BCMLOG_DATA | BCMLOG_INFO;
++ else if (!strncmp(arg, "debug", 5))
++ g_linklog_level = BCMLOG_ERROR | BCMLOG_DATA | BCMLOG_INFO |
++ BCMLOG_DBG;
++ else if (!strncmp(arg, "pball", 5))
++ g_linklog_level = 0xFFFFFFFF & ~(BCMLOG_SPINLOCK);
++ else if (!strncmp(arg, "silent", 6))
++ g_linklog_level = 0;
++ else
++ g_linklog_level = 0;
++}
++
++struct crystalhd_adp *chd_get_adp(void)
++{
++ return g_adp_info;
++}
++
++int __init chd_dec_module_init(void)
++{
++ int rc;
++
++ chd_set_log_level(NULL, "debug");
++ BCMLOG(BCMLOG_DATA, "Loading crystalhd %d.%d.%d \n",
++ crystalhd_kmod_major, crystalhd_kmod_minor, crystalhd_kmod_rev);
++
++ rc = pci_register_driver(&bc_chd_70012_driver);
++
++ if (rc < 0)
++ BCMLOG_ERR("Could not find any devices. err:%d \n", rc);
++
++ return rc;
++}
++
++void __exit chd_dec_module_cleanup(void)
++{
++ BCMLOG(BCMLOG_DATA, "unloading crystalhd %d.%d.%d \n",
++ crystalhd_kmod_major, crystalhd_kmod_minor, crystalhd_kmod_rev);
++
++ pci_unregister_driver(&bc_chd_70012_driver);
++}
++
++
++MODULE_AUTHOR("Naren Sankar <nsankar@broadcom.com>");
++MODULE_AUTHOR("Prasad Bolisetty <prasadb@broadcom.com>");
++MODULE_DESCRIPTION(CRYSTAL_HD_NAME);
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("bcm70012");
++
++module_init(chd_dec_module_init);
++module_exit(chd_dec_module_cleanup);
++
+diff --git a/drivers/staging/crystalhd/crystalhd_lnx.h b/drivers/staging/crystalhd/crystalhd_lnx.h
+new file mode 100644
+index 0000000..d3f9fc4
+--- /dev/null
++++ b/drivers/staging/crystalhd/crystalhd_lnx.h
+@@ -0,0 +1,96 @@
++/***************************************************************************
++ * Copyright (c) 2005-2009, Broadcom Corporation.
++ *
++ * Name: crystalhd_lnx . c
++ *
++ * Description:
++ * BCM70012 Linux driver
++ *
++ * HISTORY:
++ *
++ **********************************************************************
++ * This file is part of the crystalhd device driver.
++ *
++ * This driver is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, version 2 of the License.
++ *
++ * This driver 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 driver. If not, see <http://www.gnu.org/licenses/>.
++ **********************************************************************/
++
++#ifndef _CRYSTALHD_LNX_H_
++#define _CRYSTALHD_LNX_H_
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/mm.h>
++#include <linux/tty.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/fb.h>
++#include <linux/pci.h>
++#include <linux/init.h>
++#include <linux/interrupt.h>
++#include <linux/pagemap.h>
++#include <linux/vmalloc.h>
++
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/pgtable.h>
++#include <asm/system.h>
++#include <asm/uaccess.h>
++
++#include "crystalhd_cmds.h"
++
++#define CRYSTAL_HD_NAME "Broadcom Crystal HD Decoder (BCM70012) Driver"
++
++
++/* OS specific PCI information structure and adapter information. */
++struct crystalhd_adp {
++ /* Hardware borad/PCI specifics */
++ char name[32];
++ struct pci_dev *pdev;
++
++ unsigned long pci_mem_start;
++ uint32_t pci_mem_len;
++ void *addr;
++
++ unsigned long pci_i2o_start;
++ uint32_t pci_i2o_len;
++ void *i2o_addr;
++
++ unsigned int drv_data;
++ unsigned int dmabits; /* 32 | 64 */
++ unsigned int registered;
++ unsigned int present;
++ unsigned int msi;
++
++ spinlock_t lock;
++
++ /* API Related */
++ unsigned int chd_dec_major;
++ unsigned int cfg_users;
++
++ crystalhd_ioctl_data *idata_free_head; /* ioctl data pool */
++ crystalhd_elem_t *elem_pool_head; /* Queue element pool */
++
++ struct crystalhd_cmd cmds;
++
++ crystalhd_dio_req *ua_map_free_head;
++ struct pci_pool *fill_byte_pool;
++};
++
++
++struct crystalhd_adp *chd_get_adp(void);
++void chd_set_log_level(struct crystalhd_adp *adp, char *arg);
++
++#endif
++
+diff --git a/drivers/staging/crystalhd/crystalhd_misc.c b/drivers/staging/crystalhd/crystalhd_misc.c
+new file mode 100644
+index 0000000..32e632c
+--- /dev/null
++++ b/drivers/staging/crystalhd/crystalhd_misc.c
+@@ -0,0 +1,1029 @@
++/***************************************************************************
++ * Copyright (c) 2005-2009, Broadcom Corporation.
++ *
++ * Name: crystalhd_misc . c
++ *
++ * Description:
++ * BCM70012 Linux driver misc routines.
++ *
++ * HISTORY:
++ *
++ **********************************************************************
++ * This file is part of the crystalhd device driver.
++ *
++ * This driver is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, version 2 of the License.
++ *
++ * This driver 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 driver. If not, see <http://www.gnu.org/licenses/>.
++ **********************************************************************/
++
++#include "crystalhd_misc.h"
++#include "crystalhd_lnx.h"
++
++uint32_t g_linklog_level;
++
++static inline uint32_t crystalhd_dram_rd(struct crystalhd_adp *adp, uint32_t mem_off)
++{
++ crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (mem_off >> 19));
++ return bc_dec_reg_rd(adp, (0x00380000 | (mem_off & 0x0007FFFF)));
++}
++
++static inline void crystalhd_dram_wr(struct crystalhd_adp *adp, uint32_t mem_off, uint32_t val)
++{
++ crystalhd_reg_wr(adp, DCI_DRAM_BASE_ADDR, (mem_off >> 19));
++ bc_dec_reg_wr(adp, (0x00380000 | (mem_off & 0x0007FFFF)), val);
++}
++
++static inline BC_STATUS bc_chk_dram_range(struct crystalhd_adp *adp, uint32_t start_off, uint32_t cnt)
++{
++ return BC_STS_SUCCESS;
++}
++
++static crystalhd_dio_req *crystalhd_alloc_dio(struct crystalhd_adp *adp)
++{
++ unsigned long flags = 0;
++ crystalhd_dio_req *temp = NULL;
++
++ if (!adp) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return temp;
++ }
++
++ spin_lock_irqsave(&adp->lock, flags);
++ temp = adp->ua_map_free_head;
++ if (temp)
++ adp->ua_map_free_head = adp->ua_map_free_head->next;
++ spin_unlock_irqrestore(&adp->lock, flags);
++
++ return temp;
++}
++
++static void crystalhd_free_dio(struct crystalhd_adp *adp, crystalhd_dio_req *dio)
++{
++ unsigned long flags = 0;
++
++ if (!adp || !dio)
++ return;
++ spin_lock_irqsave(&adp->lock, flags);
++ dio->sig = crystalhd_dio_inv;
++ dio->page_cnt = 0;
++ dio->fb_size = 0;
++ memset(&dio->uinfo, 0, sizeof(dio->uinfo));
++ dio->next = adp->ua_map_free_head;
++ adp->ua_map_free_head = dio;
++ spin_unlock_irqrestore(&adp->lock, flags);
++}
++
++static crystalhd_elem_t *crystalhd_alloc_elem(struct crystalhd_adp *adp)
++{
++ unsigned long flags = 0;
++ crystalhd_elem_t *temp = NULL;
++
++ if (!adp)
++ return temp;
++ spin_lock_irqsave(&adp->lock, flags);
++ temp = adp->elem_pool_head;
++ if (temp) {
++ adp->elem_pool_head = adp->elem_pool_head->flink;
++ memset(temp, 0, sizeof(*temp));
++ }
++ spin_unlock_irqrestore(&adp->lock, flags);
++
++ return temp;
++}
++static void crystalhd_free_elem(struct crystalhd_adp *adp, crystalhd_elem_t *elem)
++{
++ unsigned long flags = 0;
++
++ if (!adp || !elem)
++ return;
++ spin_lock_irqsave(&adp->lock, flags);
++ elem->flink = adp->elem_pool_head;
++ adp->elem_pool_head = elem;
++ spin_unlock_irqrestore(&adp->lock, flags);
++}
++
++static inline void crystalhd_set_sg(struct scatterlist *sg, struct page *page,
++ unsigned int len, unsigned int offset)
++{
++ sg_set_page(sg, page, len, offset);
++#ifdef CONFIG_X86_64
++ sg->dma_length = len;
++#endif
++}
++
++static inline void crystalhd_init_sg(struct scatterlist *sg, unsigned int entries)
++{
++ /* http://lkml.org/lkml/2007/11/27/68 */
++ sg_init_table(sg, entries);
++}
++
++/*========================== Extern ========================================*/
++/**
++ * bc_dec_reg_rd - Read 7412's device register.
++ * @adp: Adapter instance
++ * @reg_off: Register offset.
++ *
++ * Return:
++ * 32bit value read
++ *
++ * 7412's device register read routine. This interface use
++ * 7412's device access range mapped from BAR-2 (4M) of PCIe
++ * configuration space.
++ */
++uint32_t bc_dec_reg_rd(struct crystalhd_adp *adp, uint32_t reg_off)
++{
++ if (!adp || (reg_off > adp->pci_mem_len)) {
++ BCMLOG_ERR("dec_rd_reg_off outof range: 0x%08x\n", reg_off);
++ return 0;
++ }
++
++ return readl(adp->addr + reg_off);
++}
++
++/**
++ * bc_dec_reg_wr - Write 7412's device register
++ * @adp: Adapter instance
++ * @reg_off: Register offset.
++ * @val: Dword value to be written.
++ *
++ * Return:
++ * none.
++ *
++ * 7412's device register write routine. This interface use
++ * 7412's device access range mapped from BAR-2 (4M) of PCIe
++ * configuration space.
++ */
++void bc_dec_reg_wr(struct crystalhd_adp *adp, uint32_t reg_off, uint32_t val)
++{
++ if (!adp || (reg_off > adp->pci_mem_len)) {
++ BCMLOG_ERR("dec_wr_reg_off outof range: 0x%08x\n", reg_off);
++ return;
++ }
++ writel(val, adp->addr + reg_off);
++ udelay(8);
++}
++
++/**
++ * crystalhd_reg_rd - Read Link's device register.
++ * @adp: Adapter instance
++ * @reg_off: Register offset.
++ *
++ * Return:
++ * 32bit value read
++ *
++ * Link device register read routine. This interface use
++ * Link's device access range mapped from BAR-1 (64K) of PCIe
++ * configuration space.
++ *
++ */
++uint32_t crystalhd_reg_rd(struct crystalhd_adp *adp, uint32_t reg_off)
++{
++ if (!adp || (reg_off > adp->pci_i2o_len)) {
++ BCMLOG_ERR("link_rd_reg_off outof range: 0x%08x\n", reg_off);
++ return 0;
++ }
++ return readl(adp->i2o_addr + reg_off);
++}
++
++/**
++ * crystalhd_reg_wr - Write Link's device register
++ * @adp: Adapter instance
++ * @reg_off: Register offset.
++ * @val: Dword value to be written.
++ *
++ * Return:
++ * none.
++ *
++ * Link device register write routine. This interface use
++ * Link's device access range mapped from BAR-1 (64K) of PCIe
++ * configuration space.
++ *
++ */
++void crystalhd_reg_wr(struct crystalhd_adp *adp, uint32_t reg_off, uint32_t val)
++{
++ if (!adp || (reg_off > adp->pci_i2o_len)) {
++ BCMLOG_ERR("link_wr_reg_off outof range: 0x%08x\n", reg_off);
++ return;
++ }
++ writel(val, adp->i2o_addr + reg_off);
++}
++
++/**
++ * crystalhd_mem_rd - Read data from 7412's DRAM area.
++ * @adp: Adapter instance
++ * @start_off: Start offset.
++ * @dw_cnt: Count in dwords.
++ * @rd_buff: Buffer to copy the data from dram.
++ *
++ * Return:
++ * Status.
++ *
++ * 7412's Dram read routine.
++ */
++BC_STATUS crystalhd_mem_rd(struct crystalhd_adp *adp, uint32_t start_off,
++ uint32_t dw_cnt, uint32_t *rd_buff)
++{
++ uint32_t ix = 0;
++
++ if (!adp || !rd_buff ||
++ (bc_chk_dram_range(adp, start_off, dw_cnt) != BC_STS_SUCCESS)) {
++ BCMLOG_ERR("Invalid arg \n");
++ return BC_STS_INV_ARG;
++ }
++ for (ix = 0; ix < dw_cnt; ix++)
++ rd_buff[ix] = crystalhd_dram_rd(adp, (start_off + (ix * 4)));
++
++ return BC_STS_SUCCESS;
++}
++
++/**
++ * crystalhd_mem_wr - Write data to 7412's DRAM area.
++ * @adp: Adapter instance
++ * @start_off: Start offset.
++ * @dw_cnt: Count in dwords.
++ * @wr_buff: Data Buffer to be written.
++ *
++ * Return:
++ * Status.
++ *
++ * 7412's Dram write routine.
++ */
++BC_STATUS crystalhd_mem_wr(struct crystalhd_adp *adp, uint32_t start_off,
++ uint32_t dw_cnt, uint32_t *wr_buff)
++{
++ uint32_t ix = 0;
++
++ if (!adp || !wr_buff ||
++ (bc_chk_dram_range(adp, start_off, dw_cnt) != BC_STS_SUCCESS)) {
++ BCMLOG_ERR("Invalid arg \n");
++ return BC_STS_INV_ARG;
++ }
++
++ for (ix = 0; ix < dw_cnt; ix++)
++ crystalhd_dram_wr(adp, (start_off + (ix * 4)), wr_buff[ix]);
++
++ return BC_STS_SUCCESS;
++}
++/**
++ * crystalhd_pci_cfg_rd - PCIe config read
++ * @adp: Adapter instance
++ * @off: PCI config space offset.
++ * @len: Size -- Byte, Word & dword.
++ * @val: Value read
++ *
++ * Return:
++ * Status.
++ *
++ * Get value from Link's PCIe config space.
++ */
++BC_STATUS crystalhd_pci_cfg_rd(struct crystalhd_adp *adp, uint32_t off,
++ uint32_t len, uint32_t *val)
++{
++ BC_STATUS sts = BC_STS_SUCCESS;
++ int rc = 0;
++
++ if (!adp || !val) {
++ BCMLOG_ERR("Invalid arg \n");
++ return BC_STS_INV_ARG;
++ }
++
++ switch (len) {
++ case 1:
++ rc = pci_read_config_byte(adp->pdev, off, (u8 *)val);
++ break;
++ case 2:
++ rc = pci_read_config_word(adp->pdev, off, (u16 *)val);
++ break;
++ case 4:
++ rc = pci_read_config_dword(adp->pdev, off, (u32 *)val);
++ break;
++ default:
++ rc = -EINVAL;
++ sts = BC_STS_INV_ARG;
++ BCMLOG_ERR("Invalid len:%d\n", len);
++ };
++
++ if (rc && (sts == BC_STS_SUCCESS))
++ sts = BC_STS_ERROR;
++
++ return sts;
++}
++
++/**
++ * crystalhd_pci_cfg_wr - PCIe config write
++ * @adp: Adapter instance
++ * @off: PCI config space offset.
++ * @len: Size -- Byte, Word & dword.
++ * @val: Value to be written
++ *
++ * Return:
++ * Status.
++ *
++ * Set value to Link's PCIe config space.
++ */
++BC_STATUS crystalhd_pci_cfg_wr(struct crystalhd_adp *adp, uint32_t off,
++ uint32_t len, uint32_t val)
++{
++ BC_STATUS sts = BC_STS_SUCCESS;
++ int rc = 0;
++
++ if (!adp || !val) {
++ BCMLOG_ERR("Invalid arg \n");
++ return BC_STS_INV_ARG;
++ }
++
++ switch (len) {
++ case 1:
++ rc = pci_write_config_byte(adp->pdev, off, (u8)val);
++ break;
++ case 2:
++ rc = pci_write_config_word(adp->pdev, off, (u16)val);
++ break;
++ case 4:
++ rc = pci_write_config_dword(adp->pdev, off, val);
++ break;
++ default:
++ rc = -EINVAL;
++ sts = BC_STS_INV_ARG;
++ BCMLOG_ERR("Invalid len:%d\n", len);
++ };
++
++ if (rc && (sts == BC_STS_SUCCESS))
++ sts = BC_STS_ERROR;
++
++ return sts;
++}
++
++/**
++ * bc_kern_dma_alloc - Allocate memory for Dma rings
++ * @adp: Adapter instance
++ * @sz: Size of the memory to allocate.
++ * @phy_addr: Physical address of the memory allocated.
++ * Typedef to system's dma_addr_t (u64)
++ *
++ * Return:
++ * Pointer to allocated memory..
++ *
++ * Wrapper to Linux kernel interface.
++ *
++ */
++void *bc_kern_dma_alloc(struct crystalhd_adp *adp, uint32_t sz,
++ dma_addr_t *phy_addr)
++{
++ void *temp = NULL;
++
++ if (!adp || !sz || !phy_addr) {
++ BCMLOG_ERR("Invalide Arg..\n");
++ return temp;
++ }
++
++ temp = pci_alloc_consistent(adp->pdev, sz, phy_addr);
++ if (temp)
++ memset(temp, 0, sz);
++
++ return temp;
++}
++
++/**
++ * bc_kern_dma_free - Release Dma ring memory.
++ * @adp: Adapter instance
++ * @sz: Size of the memory to allocate.
++ * @ka: Kernel virtual address returned during _dio_alloc()
++ * @phy_addr: Physical address of the memory allocated.
++ * Typedef to system's dma_addr_t (u64)
++ *
++ * Return:
++ * none.
++ */
++void bc_kern_dma_free(struct crystalhd_adp *adp, uint32_t sz, void *ka,
++ dma_addr_t phy_addr)
++{
++ if (!adp || !ka || !sz || !phy_addr) {
++ BCMLOG_ERR("Invalide Arg..\n");
++ return;
++ }
++
++ pci_free_consistent(adp->pdev, sz, ka, phy_addr);
++}
++
++/**
++ * crystalhd_create_dioq - Create Generic DIO queue
++ * @adp: Adapter instance
++ * @dioq_hnd: Handle to the dio queue created
++ * @cb : Optional - Call back To free the element.
++ * @cbctx: Context to pass to callback.
++ *
++ * Return:
++ * status
++ *
++ * Initialize Generic DIO queue to hold any data. Callback
++ * will be used to free elements while deleting the queue.
++ */
++BC_STATUS crystalhd_create_dioq(struct crystalhd_adp *adp,
++ crystalhd_dioq_t **dioq_hnd,
++ crystalhd_data_free_cb cb, void *cbctx)
++{
++ crystalhd_dioq_t *dioq = NULL;
++
++ if (!adp || !dioq_hnd) {
++ BCMLOG_ERR("Invalid arg!!\n");
++ return BC_STS_INV_ARG;
++ }
++
++ dioq = kzalloc(sizeof(*dioq), GFP_KERNEL);
++ if (!dioq)
++ return BC_STS_INSUFF_RES;
++
++ spin_lock_init(&dioq->lock);
++ dioq->sig = BC_LINK_DIOQ_SIG;
++ dioq->head = (crystalhd_elem_t *)&dioq->head;
++ dioq->tail = (crystalhd_elem_t *)&dioq->head;
++ crystalhd_create_event(&dioq->event);
++ dioq->adp = adp;
++ dioq->data_rel_cb = cb;
++ dioq->cb_context = cbctx;
++ *dioq_hnd = dioq;
++
++ return BC_STS_SUCCESS;
++}
++
++/**
++ * crystalhd_delete_dioq - Delete Generic DIO queue
++ * @adp: Adapter instance
++ * @dioq: DIOQ instance..
++ *
++ * Return:
++ * None.
++ *
++ * Release Generic DIO queue. This function will remove
++ * all the entries from the Queue and will release data
++ * by calling the call back provided during creation.
++ *
++ */
++void crystalhd_delete_dioq(struct crystalhd_adp *adp, crystalhd_dioq_t *dioq)
++{
++ void *temp;
++
++ if (!dioq || (dioq->sig != BC_LINK_DIOQ_SIG))
++ return;
++
++ do {
++ temp = crystalhd_dioq_fetch(dioq);
++ if (temp && dioq->data_rel_cb)
++ dioq->data_rel_cb(dioq->cb_context, temp);
++ } while (temp);
++ dioq->sig = 0;
++ kfree(dioq);
++}
++
++/**
++ * crystalhd_dioq_add - Add new DIO request element.
++ * @ioq: DIO queue instance
++ * @t: DIO request to be added.
++ * @wake: True - Wake up suspended process.
++ * @tag: Special tag to assign - For search and get.
++ *
++ * Return:
++ * Status.
++ *
++ * Insert new element to Q tail.
++ */
++BC_STATUS crystalhd_dioq_add(crystalhd_dioq_t *ioq, void *data,
++ bool wake, uint32_t tag)
++{
++ unsigned long flags = 0;
++ crystalhd_elem_t *tmp;
++
++ if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG) || !data) {
++ BCMLOG_ERR("Invalid arg!!\n");
++ return BC_STS_INV_ARG;
++ }
++
++ tmp = crystalhd_alloc_elem(ioq->adp);
++ if (!tmp) {
++ BCMLOG_ERR("No free elements.\n");
++ return BC_STS_INSUFF_RES;
++ }
++
++ tmp->data = data;
++ tmp->tag = tag;
++ spin_lock_irqsave(&ioq->lock, flags);
++ tmp->flink = (crystalhd_elem_t *)&ioq->head;
++ tmp->blink = ioq->tail;
++ tmp->flink->blink = tmp;
++ tmp->blink->flink = tmp;
++ ioq->count++;
++ spin_unlock_irqrestore(&ioq->lock, flags);
++
++ if (wake)
++ crystalhd_set_event(&ioq->event);
++
++ return BC_STS_SUCCESS;
++}
++
++/**
++ * crystalhd_dioq_fetch - Fetch element from head.
++ * @ioq: DIO queue instance
++ *
++ * Return:
++ * data element from the head..
++ *
++ * Remove an element from Queue.
++ */
++void *crystalhd_dioq_fetch(crystalhd_dioq_t *ioq)
++{
++ unsigned long flags = 0;
++ crystalhd_elem_t *tmp;
++ crystalhd_elem_t *ret = NULL;
++ void *data = NULL;
++
++ if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG)) {
++ BCMLOG_ERR("Invalid arg!!\n");
++ return data;
++ }
++
++ spin_lock_irqsave(&ioq->lock, flags);
++ tmp = ioq->head;
++ if (tmp != (crystalhd_elem_t *)&ioq->head) {
++ ret = tmp;
++ tmp->flink->blink = tmp->blink;
++ tmp->blink->flink = tmp->flink;
++ ioq->count--;
++ }
++ spin_unlock_irqrestore(&ioq->lock, flags);
++ if (ret) {
++ data = ret->data;
++ crystalhd_free_elem(ioq->adp, ret);
++ }
++
++ return data;
++}
++/**
++ * crystalhd_dioq_find_and_fetch - Search the tag and Fetch element
++ * @ioq: DIO queue instance
++ * @tag: Tag to search for.
++ *
++ * Return:
++ * element from the head..
++ *
++ * Search TAG and remove the element.
++ */
++void *crystalhd_dioq_find_and_fetch(crystalhd_dioq_t *ioq, uint32_t tag)
++{
++ unsigned long flags = 0;
++ crystalhd_elem_t *tmp;
++ crystalhd_elem_t *ret = NULL;
++ void *data = NULL;
++
++ if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG)) {
++ BCMLOG_ERR("Invalid arg!!\n");
++ return data;
++ }
++
++ spin_lock_irqsave(&ioq->lock, flags);
++ tmp = ioq->head;
++ while (tmp != (crystalhd_elem_t *)&ioq->head) {
++ if (tmp->tag == tag) {
++ ret = tmp;
++ tmp->flink->blink = tmp->blink;
++ tmp->blink->flink = tmp->flink;
++ ioq->count--;
++ break;
++ }
++ tmp = tmp->flink;
++ }
++ spin_unlock_irqrestore(&ioq->lock, flags);
++
++ if (ret) {
++ data = ret->data;
++ crystalhd_free_elem(ioq->adp, ret);
++ }
++
++ return data;
++}
++
++/**
++ * crystalhd_dioq_fetch_wait - Fetch element from Head.
++ * @ioq: DIO queue instance
++ * @to_secs: Wait timeout in seconds..
++ *
++ * Return:
++ * element from the head..
++ *
++ * Return element from head if Q is not empty. Wait for new element
++ * if Q is empty for Timeout seconds.
++ */
++void *crystalhd_dioq_fetch_wait(crystalhd_dioq_t *ioq, uint32_t to_secs,
++ uint32_t *sig_pend)
++{
++ unsigned long flags = 0;
++ int rc = 0, count;
++ void *tmp = NULL;
++
++ if (!ioq || (ioq->sig != BC_LINK_DIOQ_SIG) || !to_secs || !sig_pend) {
++ BCMLOG_ERR("Invalid arg!!\n");
++ return tmp;
++ }
++
++ count = to_secs;
++ spin_lock_irqsave(&ioq->lock, flags);
++ while ((ioq->count == 0) && count) {
++ spin_unlock_irqrestore(&ioq->lock, flags);
++
++ crystalhd_wait_on_event(&ioq->event, (ioq->count > 0), 1000, rc, 0);
++ if (rc == 0) {
++ goto out;
++ } else if (rc == -EINTR) {
++ BCMLOG(BCMLOG_INFO, "Cancelling fetch wait\n");
++ *sig_pend = 1;
++ return tmp;
++ }
++ spin_lock_irqsave(&ioq->lock, flags);
++ count--;
++ }
++ spin_unlock_irqrestore(&ioq->lock, flags);
++
++out:
++ return crystalhd_dioq_fetch(ioq);
++}
++
++/**
++ * crystalhd_map_dio - Map user address for DMA
++ * @adp: Adapter instance
++ * @ubuff: User buffer to map.
++ * @ubuff_sz: User buffer size.
++ * @uv_offset: UV buffer offset.
++ * @en_422mode: TRUE:422 FALSE:420 Capture mode.
++ * @dir_tx: TRUE for Tx (To device from host)
++ * @dio_hnd: Handle to mapped DIO request.
++ *
++ * Return:
++ * Status.
++ *
++ * This routine maps user address and lock pages for DMA.
++ *
++ */
++BC_STATUS crystalhd_map_dio(struct crystalhd_adp *adp, void *ubuff,
++ uint32_t ubuff_sz, uint32_t uv_offset,
++ bool en_422mode, bool dir_tx,
++ crystalhd_dio_req **dio_hnd)
++{
++ crystalhd_dio_req *dio;
++ /* FIXME: jarod: should some of these unsigned longs be uint32_t or uintptr_t? */
++ unsigned long start = 0, end = 0, uaddr = 0, count = 0;
++ unsigned long spsz = 0, uv_start = 0;
++ int i = 0, rw = 0, res = 0, nr_pages = 0, skip_fb_sg = 0;
++
++ if (!adp || !ubuff || !ubuff_sz || !dio_hnd) {
++ BCMLOG_ERR("Invalid arg \n");
++ return BC_STS_INV_ARG;
++ }
++ /* Compute pages */
++ uaddr = (unsigned long)ubuff;
++ count = (unsigned long)ubuff_sz;
++ end = (uaddr + count + PAGE_SIZE - 1) >> PAGE_SHIFT;
++ start = uaddr >> PAGE_SHIFT;
++ nr_pages = end - start;
++
++ if (!count || ((uaddr + count) < uaddr)) {
++ BCMLOG_ERR("User addr overflow!!\n");
++ return BC_STS_INV_ARG;
++ }
++
++ dio = crystalhd_alloc_dio(adp);
++ if (!dio) {
++ BCMLOG_ERR("dio pool empty..\n");
++ return BC_STS_INSUFF_RES;
++ }
++
++ if (dir_tx) {
++ rw = WRITE;
++ dio->direction = DMA_TO_DEVICE;
++ } else {
++ rw = READ;
++ dio->direction = DMA_FROM_DEVICE;
++ }
++
++ if (nr_pages > dio->max_pages) {
++ BCMLOG_ERR("max_pages(%d) exceeded(%d)!!\n",
++ dio->max_pages, nr_pages);
++ crystalhd_unmap_dio(adp, dio);
++ return BC_STS_INSUFF_RES;
++ }
++
++ if (uv_offset) {
++ uv_start = (uaddr + (unsigned long)uv_offset) >> PAGE_SHIFT;
++ dio->uinfo.uv_sg_ix = uv_start - start;
++ dio->uinfo.uv_sg_off = ((uaddr + (unsigned long)uv_offset) & ~PAGE_MASK);
++ }
++
++ dio->fb_size = ubuff_sz & 0x03;
++ if (dio->fb_size) {
++ res = copy_from_user(dio->fb_va,
++ (void *)(uaddr + count - dio->fb_size),
++ dio->fb_size);
++ if (res) {
++ BCMLOG_ERR("failed %d to copy %u fill bytes from %p\n",
++ res, dio->fb_size,
++ (void *)(uaddr + count-dio->fb_size));
++ crystalhd_unmap_dio(adp, dio);
++ return BC_STS_INSUFF_RES;
++ }
++ }
++
++ down_read(&current->mm->mmap_sem);
++ res = get_user_pages(current, current->mm, uaddr, nr_pages, rw == READ,
++ 0, dio->pages, NULL);
++ up_read(&current->mm->mmap_sem);
++
++ /* Save for release..*/
++ dio->sig = crystalhd_dio_locked;
++ if (res < nr_pages) {
++ BCMLOG_ERR("get pages failed: %d-%d\n", nr_pages, res);
++ dio->page_cnt = res;
++ crystalhd_unmap_dio(adp, dio);
++ return BC_STS_ERROR;
++ }
++
++ dio->page_cnt = nr_pages;
++ /* Get scatter/gather */
++ crystalhd_init_sg(dio->sg, dio->page_cnt);
++ crystalhd_set_sg(&dio->sg[0], dio->pages[0], 0, uaddr & ~PAGE_MASK);
++ if (nr_pages > 1) {
++ dio->sg[0].length = PAGE_SIZE - dio->sg[0].offset;
++
++#ifdef CONFIG_X86_64
++ dio->sg[0].dma_length = dio->sg[0].length;
++#endif
++ count -= dio->sg[0].length;
++ for (i = 1; i < nr_pages; i++) {
++ if (count < 4) {
++ spsz = count;
++ skip_fb_sg = 1;
++ } else {
++ spsz = (count < PAGE_SIZE) ?
++ (count & ~0x03) : PAGE_SIZE;
++ }
++ crystalhd_set_sg(&dio->sg[i], dio->pages[i], spsz, 0);
++ count -= spsz;
++ }
++ } else {
++ if (count < 4) {
++ dio->sg[0].length = count;
++ skip_fb_sg = 1;
++ } else {
++ dio->sg[0].length = count - dio->fb_size;
++ }
++#ifdef CONFIG_X86_64
++ dio->sg[0].dma_length = dio->sg[0].length;
++#endif
++ }
++ dio->sg_cnt = pci_map_sg(adp->pdev, dio->sg,
++ dio->page_cnt, dio->direction);
++ if (dio->sg_cnt <= 0) {
++ BCMLOG_ERR("sg map %d-%d \n", dio->sg_cnt, dio->page_cnt);
++ crystalhd_unmap_dio(adp, dio);
++ return BC_STS_ERROR;
++ }
++ if (dio->sg_cnt && skip_fb_sg)
++ dio->sg_cnt -= 1;
++ dio->sig = crystalhd_dio_sg_mapped;
++ /* Fill in User info.. */
++ dio->uinfo.xfr_len = ubuff_sz;
++ dio->uinfo.xfr_buff = ubuff;
++ dio->uinfo.uv_offset = uv_offset;
++ dio->uinfo.b422mode = en_422mode;
++ dio->uinfo.dir_tx = dir_tx;
++
++ *dio_hnd = dio;
++
++ return BC_STS_SUCCESS;
++}
++
++/**
++ * crystalhd_unmap_sgl - Release mapped resources
++ * @adp: Adapter instance
++ * @dio: DIO request instance
++ *
++ * Return:
++ * Status.
++ *
++ * This routine is to unmap the user buffer pages.
++ */
++BC_STATUS crystalhd_unmap_dio(struct crystalhd_adp *adp, crystalhd_dio_req *dio)
++{
++ struct page *page = NULL;
++ int j = 0;
++
++ if (!adp || !dio) {
++ BCMLOG_ERR("Invalid arg \n");
++ return BC_STS_INV_ARG;
++ }
++
++ if ((dio->page_cnt > 0) && (dio->sig != crystalhd_dio_inv)) {
++ for (j = 0; j < dio->page_cnt; j++) {
++ page = dio->pages[j];
++ if (page) {
++ if (!PageReserved(page) &&
++ (dio->direction == DMA_FROM_DEVICE))
++ SetPageDirty(page);
++ page_cache_release(page);
++ }
++ }
++ }
++ if (dio->sig == crystalhd_dio_sg_mapped)
++ pci_unmap_sg(adp->pdev, dio->sg, dio->page_cnt, dio->direction);
++
++ crystalhd_free_dio(adp, dio);
++
++ return BC_STS_SUCCESS;
++}
++
++/**
++ * crystalhd_create_dio_pool - Allocate mem pool for DIO management.
++ * @adp: Adapter instance
++ * @max_pages: Max pages for size calculation.
++ *
++ * Return:
++ * system error.
++ *
++ * This routine creates a memory pool to hold dio context for
++ * for HW Direct IO operation.
++ */
++int crystalhd_create_dio_pool(struct crystalhd_adp *adp, uint32_t max_pages)
++{
++ uint32_t asz = 0, i = 0;
++ uint8_t *temp;
++ crystalhd_dio_req *dio;
++
++ if (!adp || !max_pages) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return -EINVAL;
++ }
++
++ /* Get dma memory for fill byte handling..*/
++ adp->fill_byte_pool = pci_pool_create("crystalhd_fbyte",
++ adp->pdev, 8, 8, 0);
++ if (!adp->fill_byte_pool) {
++ BCMLOG_ERR("failed to create fill byte pool\n");
++ return -ENOMEM;
++ }
++
++ /* Get the max size from user based on 420/422 modes */
++ asz = (sizeof(*dio->pages) * max_pages) +
++ (sizeof(*dio->sg) * max_pages) + sizeof(*dio);
++
++ BCMLOG(BCMLOG_DBG, "Initializing Dio pool %d %d %x %p\n",
++ BC_LINK_SG_POOL_SZ, max_pages, asz, adp->fill_byte_pool);
++
++ for (i = 0; i < BC_LINK_SG_POOL_SZ; i++) {
++ temp = (uint8_t *)kzalloc(asz, GFP_KERNEL);
++ if ((temp) == NULL) {
++ BCMLOG_ERR("Failed to alloc %d mem\n", asz);
++ return -ENOMEM;
++ }
++
++ dio = (crystalhd_dio_req *)temp;
++ temp += sizeof(*dio);
++ dio->pages = (struct page **)temp;
++ temp += (sizeof(*dio->pages) * max_pages);
++ dio->sg = (struct scatterlist *)temp;
++ dio->max_pages = max_pages;
++ dio->fb_va = pci_pool_alloc(adp->fill_byte_pool, GFP_KERNEL,
++ &dio->fb_pa);
++ if (!dio->fb_va) {
++ BCMLOG_ERR("fill byte alloc failed.\n");
++ return -ENOMEM;
++ }
++
++ crystalhd_free_dio(adp, dio);
++ }
++
++ return 0;
++}
++
++/**
++ * crystalhd_destroy_dio_pool - Release DIO mem pool.
++ * @adp: Adapter instance
++ *
++ * Return:
++ * none.
++ *
++ * This routine releases dio memory pool during close.
++ */
++void crystalhd_destroy_dio_pool(struct crystalhd_adp *adp)
++{
++ crystalhd_dio_req *dio;
++ int count = 0;
++
++ if (!adp) {
++ BCMLOG_ERR("Invalid Arg!!\n");
++ return;
++ }
++
++ do {
++ dio = crystalhd_alloc_dio(adp);
++ if (dio) {
++ if (dio->fb_va)
++ pci_pool_free(adp->fill_byte_pool,
++ dio->fb_va, dio->fb_pa);
++ count++;
++ kfree(dio);
++ }
++ } while (dio);
++
++ if (adp->fill_byte_pool) {
++ pci_pool_destroy(adp->fill_byte_pool);
++ adp->fill_byte_pool = NULL;
++ }
++
++ BCMLOG(BCMLOG_DBG, "Released dio pool %d \n", count);
++}
++
++/**
++ * crystalhd_create_elem_pool - List element pool creation.
++ * @adp: Adapter instance
++ * @pool_size: Number of elements in the pool.
++ *
++ * Return:
++ * 0 - success, <0 error
++ *
++ * Create general purpose list element pool to hold pending,
++ * and active requests.
++ */
++int crystalhd_create_elem_pool(struct crystalhd_adp *adp, uint32_t pool_size)
++{
++ uint32_t i;
++ crystalhd_elem_t *temp;
++
++ if (!adp || !pool_size)
++ return -EINVAL;
++
++ for (i = 0; i < pool_size; i++) {
++ temp = kzalloc(sizeof(*temp), GFP_KERNEL);
++ if (!temp) {
++ BCMLOG_ERR("kalloc failed \n");
++ return -ENOMEM;
++ }
++ crystalhd_free_elem(adp, temp);
++ }
++ BCMLOG(BCMLOG_DBG, "allocated %d elem\n", pool_size);
++ return 0;
++}
++
++/**
++ * crystalhd_delete_elem_pool - List element pool deletion.
++ * @adp: Adapter instance
++ *
++ * Return:
++ * none
++ *
++ * Delete general purpose list element pool.
++ */
++void crystalhd_delete_elem_pool(struct crystalhd_adp *adp)
++{
++ crystalhd_elem_t *temp;
++ int dbg_cnt = 0;
++
++ if (!adp)
++ return;
++
++ do {
++ temp = crystalhd_alloc_elem(adp);
++ if (temp) {
++ kfree(temp);
++ dbg_cnt++;
++ }
++ } while (temp);
++
++ BCMLOG(BCMLOG_DBG, "released %d elem\n", dbg_cnt);
++}
++
++/*================ Debug support routines.. ================================*/
++void crystalhd_show_buffer(uint32_t off, uint8_t *buff, uint32_t dwcount)
++{
++ uint32_t i, k = 1;
++
++ for (i = 0; i < dwcount; i++) {
++ if (k == 1)
++ BCMLOG(BCMLOG_DATA, "0x%08X : ", off);
++
++ BCMLOG(BCMLOG_DATA, " 0x%08X ", *((uint32_t *)buff));
++
++ buff += sizeof(uint32_t);
++ off += sizeof(uint32_t);
++ k++;
++ if ((i == dwcount - 1) || (k > 4)) {
++ BCMLOG(BCMLOG_DATA, "\n");
++ k = 1;
++ }
++ }
++}
+diff --git a/drivers/staging/crystalhd/crystalhd_misc.h b/drivers/staging/crystalhd/crystalhd_misc.h
+new file mode 100644
+index 0000000..a2aa6ad
+--- /dev/null
++++ b/drivers/staging/crystalhd/crystalhd_misc.h
+@@ -0,0 +1,229 @@
++/***************************************************************************
++ * Copyright (c) 2005-2009, Broadcom Corporation.
++ *
++ * Name: crystalhd_misc . h
++ *
++ * Description:
++ * BCM70012 Linux driver general purpose routines.
++ * Includes reg/mem read and write routines.
++ *
++ * HISTORY:
++ *
++ **********************************************************************
++ * This file is part of the crystalhd device driver.
++ *
++ * This driver is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation, version 2 of the License.
++ *
++ * This driver 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 driver. If not, see <http://www.gnu.org/licenses/>.
++ **********************************************************************/
++
++#ifndef _CRYSTALHD_MISC_H_
++#define _CRYSTALHD_MISC_H_
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/ioctl.h>
++#include <linux/dma-mapping.h>
++#include <linux/version.h>
++#include <linux/sched.h>
++#include <asm/system.h>
++#include "bc_dts_glob_lnx.h"
++
++/* Global log level variable defined in crystal_misc.c file */
++extern uint32_t g_linklog_level;
++
++/* Global element pool for all Queue management.
++ * TX: Active = BC_TX_LIST_CNT, Free = BC_TX_LIST_CNT.
++ * RX: Free = BC_RX_LIST_CNT, Active = 2
++ * FW-CMD: 4
++ */
++#define BC_LINK_ELEM_POOL_SZ ((BC_TX_LIST_CNT * 2) + BC_RX_LIST_CNT + 2 + 4)
++
++/* Driver's IODATA pool count */
++#define CHD_IODATA_POOL_SZ (BC_IOCTL_DATA_POOL_SIZE * BC_LINK_MAX_OPENS)
++
++/* Scatter Gather memory pool size for Tx and Rx */
++#define BC_LINK_SG_POOL_SZ (BC_TX_LIST_CNT + BC_RX_LIST_CNT)
++
++enum _crystalhd_dio_sig {
++ crystalhd_dio_inv = 0,
++ crystalhd_dio_locked,
++ crystalhd_dio_sg_mapped,
++};
++
++struct crystalhd_dio_user_info {
++ void *xfr_buff;
++ uint32_t xfr_len;
++ uint32_t uv_offset;
++ bool dir_tx;
++
++ uint32_t uv_sg_ix;
++ uint32_t uv_sg_off;
++ int comp_sts;
++ int ev_sts;
++ uint32_t y_done_sz;
++ uint32_t uv_done_sz;
++ uint32_t comp_flags;
++ bool b422mode;
++};
++
++typedef struct _crystalhd_dio_req {
++ uint32_t sig;
++ uint32_t max_pages;
++ struct page **pages;
++ struct scatterlist *sg;
++ int sg_cnt;
++ int page_cnt;
++ int direction;
++ struct crystalhd_dio_user_info uinfo;
++ void *fb_va;
++ uint32_t fb_size;
++ dma_addr_t fb_pa;
++ struct _crystalhd_dio_req *next;
++} crystalhd_dio_req;
++
++#define BC_LINK_DIOQ_SIG (0x09223280)
++
++typedef struct _crystalhd_elem_s {
++ struct _crystalhd_elem_s *flink;
++ struct _crystalhd_elem_s *blink;
++ void *data;
++ uint32_t tag;
++} crystalhd_elem_t;
++
++typedef void (*crystalhd_data_free_cb)(void *context, void *data);
++
++typedef struct _crystalhd_dioq_s {
++ uint32_t sig;
++ struct crystalhd_adp *adp;
++ crystalhd_elem_t *head;
++ crystalhd_elem_t *tail;
++ uint32_t count;
++ spinlock_t lock;
++ wait_queue_head_t event;
++ crystalhd_data_free_cb data_rel_cb;
++ void *cb_context;
++} crystalhd_dioq_t;
++
++typedef void (*hw_comp_callback)(crystalhd_dio_req *,
++ wait_queue_head_t *event, BC_STATUS sts);
++
++/*========= Decoder (7412) register access routines.================= */
++uint32_t bc_dec_reg_rd(struct crystalhd_adp *, uint32_t);
++void bc_dec_reg_wr(struct crystalhd_adp *, uint32_t, uint32_t);
++
++/*========= Link (70012) register access routines.. =================*/
++uint32_t crystalhd_reg_rd(struct crystalhd_adp *, uint32_t);
++void crystalhd_reg_wr(struct crystalhd_adp *, uint32_t, uint32_t);
++
++/*========= Decoder (7412) memory access routines..=================*/
++BC_STATUS crystalhd_mem_rd(struct crystalhd_adp *, uint32_t, uint32_t, uint32_t *);
++BC_STATUS crystalhd_mem_wr(struct crystalhd_adp *, uint32_t, uint32_t, uint32_t *);
++
++/*==========Link (70012) PCIe Config access routines.================*/
++BC_STATUS crystalhd_pci_cfg_rd(struct crystalhd_adp *, uint32_t, uint32_t, uint32_t *);
++BC_STATUS crystalhd_pci_cfg_wr(struct crystalhd_adp *, uint32_t, uint32_t, uint32_t);
++
++/*========= Linux Kernel Interface routines. ======================= */
++void *bc_kern_dma_alloc(struct crystalhd_adp *, uint32_t, dma_addr_t *);
++void bc_kern_dma_free(struct crystalhd_adp *, uint32_t,
++ void *, dma_addr_t);
++#define crystalhd_create_event(_ev) init_waitqueue_head(_ev)
++#define crystalhd_set_event(_ev) wake_up_interruptible(_ev)
++#define crystalhd_wait_on_event(ev, condition, timeout, ret, nosig) \
++do { \
++ DECLARE_WAITQUEUE(entry, current); \
++ unsigned long end = jiffies + ((timeout * HZ) / 1000); \
++ ret = 0; \
++ add_wait_queue(ev, &entry); \
++ for (;;) { \
++ __set_current_state(TASK_INTERRUPTIBLE); \
++ if (condition) { \
++ break; \
++ } \
++ if (time_after_eq(jiffies, end)) { \
++ ret = -EBUSY; \
++ break; \
++ } \
++ schedule_timeout((HZ / 100 > 1) ? HZ / 100 : 1); \
++ if (!nosig && signal_pending(current)) { \
++ ret = -EINTR; \
++ break; \
++ } \
++ } \
++ __set_current_state(TASK_RUNNING); \
++ remove_wait_queue(ev, &entry); \
++} while (0)
++
++/*================ Direct IO mapping routines ==================*/
++extern int crystalhd_create_dio_pool(struct crystalhd_adp *, uint32_t);
++extern void crystalhd_destroy_dio_pool(struct crystalhd_adp *);
++extern BC_STATUS crystalhd_map_dio(struct crystalhd_adp *, void *, uint32_t,
++ uint32_t, bool, bool, crystalhd_dio_req**);
++
++extern BC_STATUS crystalhd_unmap_dio(struct crystalhd_adp *, crystalhd_dio_req*);
++#define crystalhd_get_sgle_paddr(_dio, _ix) (cpu_to_le64(sg_dma_address(&_dio->sg[_ix])))
++#define crystalhd_get_sgle_len(_dio, _ix) (cpu_to_le32(sg_dma_len(&_dio->sg[_ix])))
++
++/*================ General Purpose Queues ==================*/
++extern BC_STATUS crystalhd_create_dioq(struct crystalhd_adp *, crystalhd_dioq_t **, crystalhd_data_free_cb , void *);
++extern void crystalhd_delete_dioq(struct crystalhd_adp *, crystalhd_dioq_t *);
++extern BC_STATUS crystalhd_dioq_add(crystalhd_dioq_t *ioq, void *data, bool wake, uint32_t tag);
++extern void *crystalhd_dioq_fetch(crystalhd_dioq_t *ioq);
++extern void *crystalhd_dioq_find_and_fetch(crystalhd_dioq_t *ioq, uint32_t tag);
++extern void *crystalhd_dioq_fetch_wait(crystalhd_dioq_t *ioq, uint32_t to_secs, uint32_t *sig_pend);
++
++#define crystalhd_dioq_count(_ioq) ((_ioq) ? _ioq->count : 0)
++
++extern int crystalhd_create_elem_pool(struct crystalhd_adp *, uint32_t);
++extern void crystalhd_delete_elem_pool(struct crystalhd_adp *);
++
++
++/*================ Debug routines/macros .. ================================*/
++extern void crystalhd_show_buffer(uint32_t off, uint8_t *buff, uint32_t dwcount);
++
++enum _chd_log_levels {
++ BCMLOG_ERROR = 0x80000000, /* Don't disable this option */
++ BCMLOG_DATA = 0x40000000, /* Data, enable by default */
++ BCMLOG_SPINLOCK = 0x20000000, /* Spcial case for Spin locks*/
++
++ /* Following are allowed only in debug mode */
++ BCMLOG_INFO = 0x00000001, /* Generic informational */
++ BCMLOG_DBG = 0x00000002, /* First level Debug info */
++ BCMLOG_SSTEP = 0x00000004, /* Stepping information */
++ BCMLOG_ENTER_LEAVE = 0x00000008, /* stack tracking */
++};
++
++#define BCMLOG_ENTER \
++if (g_linklog_level & BCMLOG_ENTER_LEAVE) { \
++ printk("Entered %s\n", __func__); \
++}
++
++#define BCMLOG_LEAVE \
++if (g_linklog_level & BCMLOG_ENTER_LEAVE) { \
++ printk("Leaving %s\n", __func__); \
++}
++
++#define BCMLOG(trace, fmt, args...) \
++if (g_linklog_level & trace) { \
++ printk(fmt, ##args); \
++}
++
++#define BCMLOG_ERR(fmt, args...) \
++do { \
++ if (g_linklog_level & BCMLOG_ERROR) { \
++ printk("*ERR*:%s:%d: "fmt, __FILE__, __LINE__, ##args); \
++ } \
++} while (0);
++
++#endif
diff --git a/die-floppy-die.patch b/die-floppy-die.patch
new file mode 100644
index 0000000..26beabf
--- /dev/null
+++ b/die-floppy-die.patch
@@ -0,0 +1,18 @@
+Kill the floppy.ko pnp modalias. We were surviving just fine without
+autoloading floppy drivers, tyvm.
+
+Please feel free to register all complaints in the wastepaper bin.
+
+diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
+index 91b7530..2ea84a6 100644
+--- a/drivers/block/floppy.c
++++ b/drivers/block/floppy.c
+@@ -4631,7 +4631,7 @@ static const struct pnp_device_id floppy_pnpids[] = {
+ { "PNP0700", 0 },
+ { }
+ };
+-MODULE_DEVICE_TABLE(pnp, floppy_pnpids);
++/* MODULE_DEVICE_TABLE(pnp, floppy_pnpids); */
+
+ #else
+
diff --git a/drm-i915-add-reclaimable-to-page-allocations.patch b/drm-i915-add-reclaimable-to-page-allocations.patch
new file mode 100644
index 0000000..6014f2c
--- /dev/null
+++ b/drm-i915-add-reclaimable-to-page-allocations.patch
@@ -0,0 +1,48 @@
+From: Linus Torvalds <torvalds@linux-foundation.org>
+Date: Sun, 18 Jul 2010 16:44:37 +0000 (-0700)
+Subject: drm/i915: add 'reclaimable' to i915 self-reclaimable page allocations
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=cd9f040df6ce46573760a507cb88192d05d27d86
+
+drm/i915: add 'reclaimable' to i915 self-reclaimable page allocations
+
+The hibernate issues that got fixed in commit 985b823b9192 ("drm/i915:
+fix hibernation since i915 self-reclaim fixes") turn out to have been
+incomplete. Vefa Bicakci tested lots of hibernate cycles, and without
+the __GFP_RECLAIMABLE flag the system eventually fails to resume.
+
+With the flag added, Vefa can apparently hibernate forever (or until he
+gets bored running his automated scripts, whichever comes first).
+
+The reclaimable flag was there originally, and was one of the flags that
+were dropped (unintentionally) by commit 4bdadb978569 ("drm/i915:
+Selectively enable self-reclaim") that introduced all these problems,
+but I didn't want to just blindly add back all the flags in commit
+985b823b9192, and it looked like __GFP_RECLAIM wasn't necessary. It
+clearly was.
+
+I still suspect that there is some subtle reason we're missing that
+causes the problems, but __GFP_RECLAIMABLE is certainly not wrong to use
+in this context, and is what the code historically used. And we have no
+idea what the causes the corruption without it.
+
+Reported-and-tested-by: M. Vefa Bicakci <bicave@superonline.com>
+Cc: Dave Airlie <airlied@gmail.com>
+Cc: Chris Wilson <chris@chris-wilson.co.uk>
+Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
+Cc: Hugh Dickins <hugh.dickins@tiscali.co.uk>
+Cc: stable@kernel.org
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+---
+
+diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
+index 0743858..8757ecf 100644
+--- a/drivers/gpu/drm/i915/i915_gem.c
++++ b/drivers/gpu/drm/i915/i915_gem.c
+@@ -2241,6 +2241,7 @@ i915_gem_object_get_pages(struct drm_gem_object *obj,
+ page = read_cache_page_gfp(mapping, i,
+ GFP_HIGHUSER |
+ __GFP_COLD |
++ __GFP_RECLAIMABLE |
+ gfpmask);
+ if (IS_ERR(page))
+ goto err_pages;
diff --git a/drm-i915-fix-hibernate-memory-corruption.patch b/drm-i915-fix-hibernate-memory-corruption.patch
new file mode 100644
index 0000000..a9c2c18
--- /dev/null
+++ b/drm-i915-fix-hibernate-memory-corruption.patch
@@ -0,0 +1,41 @@
+From: Linus Torvalds <torvalds@linux-foundation.org>
+Date: Fri, 2 Jul 2010 00:04:42 +0000 (+1000)
+Subject: drm/i915: fix hibernation since i915 self-reclaim fixes
+X-Git-Tag: v2.6.35-rc4~13
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=985b823b919273fe1327d56d2196b4f92e5d0fae
+
+drm/i915: fix hibernation since i915 self-reclaim fixes
+
+Since commit 4bdadb9785696439c6e2b3efe34aa76df1149c83 ("drm/i915:
+Selectively enable self-reclaim"), we've been passing GFP_MOVABLE to the
+i915 page allocator where we weren't before due to some over-eager
+removal of the page mapping gfp_flags games the code used to play.
+
+This caused hibernate on Intel hardware to result in a lot of memory
+corruptions on resume. See for example
+
+ http://bugzilla.kernel.org/show_bug.cgi?id=13811
+
+Reported-by: Evengi Golov (in bugzilla)
+Signed-off-by: Dave Airlie <airlied@redhat.com>
+Tested-by: M. Vefa Bicakci <bicave@superonline.com>
+Cc: stable@kernel.org
+Cc: Chris Wilson <chris@chris-wilson.co.uk>
+Cc: KOSAKI Motohiro <kosaki.motohiro@jp.fujitsu.com>
+Cc: Hugh Dickins <hugh.dickins@tiscali.co.uk>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+---
+
+diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
+index 9ded3da..0743858 100644
+--- a/drivers/gpu/drm/i915/i915_gem.c
++++ b/drivers/gpu/drm/i915/i915_gem.c
+@@ -2239,7 +2239,7 @@ i915_gem_object_get_pages(struct drm_gem_object *obj,
+ mapping = inode->i_mapping;
+ for (i = 0; i < page_count; i++) {
+ page = read_cache_page_gfp(mapping, i,
+- mapping_gfp_mask (mapping) |
++ GFP_HIGHUSER |
+ __GFP_COLD |
+ gfpmask);
+ if (IS_ERR(page))
diff --git a/drm-i915-resume-force-mode.patch b/drm-i915-resume-force-mode.patch
new file mode 100644
index 0000000..3e6c648
--- /dev/null
+++ b/drm-i915-resume-force-mode.patch
@@ -0,0 +1,50 @@
+http://lists.freedesktop.org/archives/intel-gfx/2009-February/001313.html
+
+--- a/drivers/gpu/drm/i915/i915_suspend.c.orig 2009-02-18 22:59:19.000000000 -0500
++++ b/drivers/gpu/drm/i915/i915_suspend.c 2009-02-18 22:59:58.000000000 -0500
+@@ -28,6 +28,7 @@
+ #include "drm.h"
+ #include "i915_drm.h"
+ #include "i915_drv.h"
++#include <drm/drm_crtc_helper.h>
+
+ static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe)
+ {
+@@ -519,6 +520,8 @@
+
+ i915_restore_vga(dev);
+
++ drm_helper_resume_force_mode(dev);
++
+ return 0;
+ }
+
+From f5192bce8be69e5b33d7579bc282fef4d673e2c1 Mon Sep 17 00:00:00 2001
+From: Lubomir Rintel <lkundrak@v3.sk>
+Date: Sun, 15 Mar 2009 13:55:55 +0100
+Subject: [PATCH] Fix i915 nomodeset NULL deref. during PM resume
+
+drm_helper_resume_force_mode() would crash while attempting to
+iterate through crtc_list, which is uninitialized when is modesetting
+disabled.
+---
+ drivers/gpu/drm/i915/i915_suspend.c | 3 ++-
+ 1 files changed, 2 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c
+index ef5fb6e..b138032 100644
+--- a/drivers/gpu/drm/i915/i915_suspend.c
++++ b/drivers/gpu/drm/i915/i915_suspend.c
+@@ -520,7 +520,8 @@ int i915_restore_state(struct drm_device *dev)
+
+ i915_restore_vga(dev);
+
+- drm_helper_resume_force_mode(dev);
++ if (drm_core_check_feature(dev, DRIVER_MODESET))
++ drm_helper_resume_force_mode(dev);
+
+ return 0;
+ }
+--
+1.6.2
+
diff --git a/drm-intel-945gm-stability-fixes.patch b/drm-intel-945gm-stability-fixes.patch
new file mode 100644
index 0000000..ff661cf
--- /dev/null
+++ b/drm-intel-945gm-stability-fixes.patch
@@ -0,0 +1,117 @@
+upstream commit 944001201ca0196bcdb088129e5866a9f379d08c
+(plus some defines)
+[2.6.32 backport]
+
+diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
+index 0d05c6f..b87f65d 100644
+--- a/drivers/gpu/drm/i915/i915_gem.c
++++ b/drivers/gpu/drm/i915/i915_gem.c
+@@ -4967,6 +4967,16 @@ i915_gem_load(struct drm_device *dev)
+ list_add(&dev_priv->mm.shrink_list, &shrink_list);
+ spin_unlock(&shrink_list_lock);
+
++ /* On GEN3 we really need to make sure the ARB C3 LP bit is set */
++ if (IS_GEN3(dev)) {
++ u32 tmp = I915_READ(MI_ARB_STATE);
++ if (!(tmp & MI_ARB_C3_LP_WRITE_ENABLE)) {
++ /* arb state is a masked write, so set bit + bit in mask */
++ tmp = MI_ARB_C3_LP_WRITE_ENABLE | (MI_ARB_C3_LP_WRITE_ENABLE << MI_ARB_MASK_SHIFT);
++ I915_WRITE(MI_ARB_STATE, tmp);
++ }
++ }
++
+ /* Old X drivers will take 0-2 for front, back, depth buffers */
+ dev_priv->fence_reg_start = 3;
+
+diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
+index 4cbc521..4543975 100644
+--- a/drivers/gpu/drm/i915/i915_reg.h
++++ b/drivers/gpu/drm/i915/i915_reg.h
+@@ -357,6 +357,70 @@
+ #define LM_BURST_LENGTH 0x00000700
+ #define LM_FIFO_WATERMARK 0x0000001F
+ #define MI_ARB_STATE 0x020e4 /* 915+ only */
++#define MI_ARB_MASK_SHIFT 16 /* shift for enable bits */
++
++/* Make render/texture TLB fetches lower priorty than associated data
++ * fetches. This is not turned on by default
++ */
++#define MI_ARB_RENDER_TLB_LOW_PRIORITY (1 << 15)
++
++/* Isoch request wait on GTT enable (Display A/B/C streams).
++ * Make isoch requests stall on the TLB update. May cause
++ * display underruns (test mode only)
++ */
++#define MI_ARB_ISOCH_WAIT_GTT (1 << 14)
++
++/* Block grant count for isoch requests when block count is
++ * set to a finite value.
++ */
++#define MI_ARB_BLOCK_GRANT_MASK (3 << 12)
++#define MI_ARB_BLOCK_GRANT_8 (0 << 12) /* for 3 display planes */
++#define MI_ARB_BLOCK_GRANT_4 (1 << 12) /* for 2 display planes */
++#define MI_ARB_BLOCK_GRANT_2 (2 << 12) /* for 1 display plane */
++#define MI_ARB_BLOCK_GRANT_0 (3 << 12) /* don't use */
++
++/* Enable render writes to complete in C2/C3/C4 power states.
++ * If this isn't enabled, render writes are prevented in low
++ * power states. That seems bad to me.
++ */
++#define MI_ARB_C3_LP_WRITE_ENABLE (1 << 11)
++
++/* This acknowledges an async flip immediately instead
++ * of waiting for 2TLB fetches.
++ */
++#define MI_ARB_ASYNC_FLIP_ACK_IMMEDIATE (1 << 10)
++
++/* Enables non-sequential data reads through arbiter
++ */
++#define MI_ARB_DUAL_DATA_PHASE_DISABLE (1 << 9)
++
++/* Disable FSB snooping of cacheable write cycles from binner/render
++ * command stream
++ */
++#define MI_ARB_CACHE_SNOOP_DISABLE (1 << 8)
++
++/* Arbiter time slice for non-isoch streams */
++#define MI_ARB_TIME_SLICE_MASK (7 << 5)
++#define MI_ARB_TIME_SLICE_1 (0 << 5)
++#define MI_ARB_TIME_SLICE_2 (1 << 5)
++#define MI_ARB_TIME_SLICE_4 (2 << 5)
++#define MI_ARB_TIME_SLICE_6 (3 << 5)
++#define MI_ARB_TIME_SLICE_8 (4 << 5)
++#define MI_ARB_TIME_SLICE_10 (5 << 5)
++#define MI_ARB_TIME_SLICE_14 (6 << 5)
++#define MI_ARB_TIME_SLICE_16 (7 << 5)
++
++/* Low priority grace period page size */
++#define MI_ARB_LOW_PRIORITY_GRACE_4KB (0 << 4) /* default */
++#define MI_ARB_LOW_PRIORITY_GRACE_8KB (1 << 4)
++
++/* Disable display A/B trickle feed */
++#define MI_ARB_DISPLAY_TRICKLE_FEED_DISABLE (1 << 2)
++
++/* Set display plane priority */
++#define MI_ARB_DISPLAY_PRIORITY_A_B (0 << 0) /* display A > display B */
++#define MI_ARB_DISPLAY_PRIORITY_B_A (1 << 0) /* display B > display A */
++
+ #define CACHE_MODE_0 0x02120 /* 915+ only */
+ #define CM0_MASK_SHIFT 16
+ #define CM0_IZ_OPT_DISABLE (1<<6)
+--- a/drivers/gpu/drm/i915/i915_drv.h
++++ b/drivers/gpu/drm/i915/i915_drv.h
+@@ -1045,6 +1045,13 @@ extern int i915_wait_ring(struct drm_dev
+ #define IS_I9XX(dev) (INTEL_INFO(dev)->is_i9xx)
+ #define IS_MOBILE(dev) (INTEL_INFO(dev)->is_mobile)
+
++#define IS_GEN3(dev) (IS_I915G(dev) || \
++ IS_I915GM(dev) || \
++ IS_I945G(dev) || \
++ IS_I945GM(dev) || \
++ IS_G33(dev) || \
++ IS_PINEVIEW(dev))
++
+ #define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws)
+
+ /* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte
+--
diff --git a/drm-intel-acpi-populate-didl.patch b/drm-intel-acpi-populate-didl.patch
new file mode 100644
index 0000000..069f276
--- /dev/null
+++ b/drm-intel-acpi-populate-didl.patch
@@ -0,0 +1,70 @@
+diff -up linux-2.6.33.noarch/drivers/gpu/drm/i915/i915_opregion.c.orig linux-2.6.33.noarch/drivers/gpu/drm/i915/i915_opregion.c
+--- linux-2.6.33.noarch/drivers/gpu/drm/i915/i915_opregion.c.orig 2010-02-24 13:52:17.000000000 -0500
++++ linux-2.6.33.noarch/drivers/gpu/drm/i915/i915_opregion.c 2010-04-01 10:35:35.249121262 -0400
+@@ -382,8 +382,54 @@ static void intel_didl_outputs(struct dr
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_opregion *opregion = &dev_priv->opregion;
+ struct drm_connector *connector;
++ acpi_handle handle;
++ struct acpi_device *acpi_dev, *acpi_cdev, *acpi_video_bus = NULL;
++ unsigned long long device_id;
++ acpi_status status;
+ int i = 0;
+
++ handle = DEVICE_ACPI_HANDLE(&dev->pdev->dev);
++ if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev)))
++ return;
++
++ if (acpi_is_video_device(acpi_dev))
++ acpi_video_bus = acpi_dev;
++ else {
++ list_for_each_entry(acpi_cdev, &acpi_dev->children, node) {
++ if (acpi_is_video_device(acpi_cdev)) {
++ acpi_video_bus = acpi_cdev;
++ break;
++ }
++ }
++ }
++
++ if (!acpi_video_bus)
++ goto blind_set;
++
++ list_for_each_entry(acpi_cdev, &acpi_video_bus->children, node) {
++ if (i >= 8) {
++ dev_printk (KERN_ERR, &dev->pdev->dev,
++ "More than 8 outputs detected\n");
++ return;
++ }
++ status = acpi_evaluate_integer(acpi_cdev->handle, "_ADR",
++ NULL, &device_id);
++ if (ACPI_SUCCESS(status)) {
++ if (!device_id)
++ goto blind_set;
++ opregion->acpi->didl[i] = (u32)(device_id & 0x0f0f);
++ i++;
++ }
++ }
++
++end:
++ /* If fewer than 8 outputs, the list must be null terminated */
++ if (i < 8)
++ opregion->acpi->didl[i] = 0;
++ return;
++
++blind_set:
++ i = 0;
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ int output_type = ACPI_OTHER_OUTPUT;
+ if (i >= 8) {
+@@ -416,10 +462,7 @@ static void intel_didl_outputs(struct dr
+ opregion->acpi->didl[i] |= (1<<31) | output_type | i;
+ i++;
+ }
+-
+- /* If fewer than 8 outputs, the list must be null terminated */
+- if (i < 8)
+- opregion->acpi->didl[i] = 0;
++ goto end;
+ }
+
+ int intel_opregion_init(struct drm_device *dev, int resume)
diff --git a/drm-intel-big-hammer.patch b/drm-intel-big-hammer.patch
new file mode 100644
index 0000000..e704750
--- /dev/null
+++ b/drm-intel-big-hammer.patch
@@ -0,0 +1,16 @@
+diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
+index 37427e4..08af9db 100644
+--- a/drivers/gpu/drm/i915/i915_gem.c
++++ b/drivers/gpu/drm/i915/i915_gem.c
+@@ -2553,6 +2553,11 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
+
+ mutex_lock(&dev->struct_mutex);
+
++ /* We don't get the flushing right for these chipsets, use the
++ * big hamer for now to avoid random crashiness. */
++ if (IS_I855(dev) || IS_I865G(dev))
++ wbinvd();
++
+ i915_verify_inactive(dev, __FILE__, __LINE__);
+
+ if (dev_priv->mm.wedged) {
diff --git a/drm-intel-make-lvds-work.patch b/drm-intel-make-lvds-work.patch
new file mode 100644
index 0000000..af3fd8f
--- /dev/null
+++ b/drm-intel-make-lvds-work.patch
@@ -0,0 +1,19 @@
+diff -up linux-2.6.33.noarch/drivers/gpu/drm/i915/intel_display.c.old linux-2.6.33.noarch/drivers/gpu/drm/i915/intel_display.c
+--- linux-2.6.33.noarch/drivers/gpu/drm/i915/intel_display.c.old 2010-03-31 15:51:11.798876290 -0400
++++ linux-2.6.33.noarch/drivers/gpu/drm/i915/intel_display.c 2010-03-31 16:01:18.342747791 -0400
+@@ -3742,7 +3742,6 @@ struct drm_crtc *intel_get_load_detect_p
+ void intel_release_load_detect_pipe(struct intel_output *intel_output, int dpms_mode)
+ {
+ struct drm_encoder *encoder = &intel_output->enc;
+- struct drm_device *dev = encoder->dev;
+ struct drm_crtc *crtc = encoder->crtc;
+ struct drm_encoder_helper_funcs *encoder_funcs = encoder->helper_private;
+ struct drm_crtc_helper_funcs *crtc_funcs = crtc->helper_private;
+@@ -3752,7 +3751,6 @@ void intel_release_load_detect_pipe(stru
+ intel_output->base.encoder = NULL;
+ intel_output->load_detect_temp = false;
+ crtc->enabled = drm_helper_crtc_in_use(crtc);
+- drm_helper_disable_unused_functions(dev);
+ }
+
+ /* Switch crtc and output back off if necessary */
diff --git a/drm-intel-next.patch b/drm-intel-next.patch
new file mode 100644
index 0000000..c6cac69
--- /dev/null
+++ b/drm-intel-next.patch
@@ -0,0 +1 @@
+empty
diff --git a/drm-intel-no-tv-hotplug.patch b/drm-intel-no-tv-hotplug.patch
new file mode 100644
index 0000000..9de59b3
--- /dev/null
+++ b/drm-intel-no-tv-hotplug.patch
@@ -0,0 +1,11 @@
+diff -up linux-2.6.31.noarch/drivers/gpu/drm/i915/i915_reg.h.jx linux-2.6.31.noarch/drivers/gpu/drm/i915/i915_reg.h
+--- linux-2.6.31.noarch/drivers/gpu/drm/i915/i915_reg.h.jx 2009-09-16 13:36:20.000000000 -0400
++++ linux-2.6.31.noarch/drivers/gpu/drm/i915/i915_reg.h 2009-09-16 13:40:32.000000000 -0400
+@@ -836,7 +836,6 @@
+ HDMID_HOTPLUG_INT_EN | \
+ SDVOB_HOTPLUG_INT_EN | \
+ SDVOC_HOTPLUG_INT_EN | \
+- TV_HOTPLUG_INT_EN | \
+ CRT_HOTPLUG_INT_EN)
+
+
diff --git a/drm-nouveau-d620.patch b/drm-nouveau-d620.patch
new file mode 100644
index 0000000..601e200
--- /dev/null
+++ b/drm-nouveau-d620.patch
@@ -0,0 +1,121 @@
+diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
+index 6b6c303..a81c738 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
++++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
+@@ -3198,7 +3198,6 @@ static int run_lvds_table(struct drm_device *dev, struct dcb_entry *dcbent, int
+ struct nvbios *bios = &dev_priv->vbios;
+ unsigned int outputset = (dcbent->or == 4) ? 1 : 0;
+ uint16_t scriptptr = 0, clktable;
+- uint8_t clktableptr = 0;
+
+ /*
+ * For now we assume version 3.0 table - g80 support will need some
+@@ -3217,26 +3216,29 @@ static int run_lvds_table(struct drm_device *dev, struct dcb_entry *dcbent, int
+ scriptptr = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 11 + outputset * 2]);
+ break;
+ case LVDS_RESET:
++ clktable = bios->fp.lvdsmanufacturerpointer + 15;
++ if (dcbent->or == 4)
++ clktable += 8;
++
+ if (dcbent->lvdsconf.use_straps_for_mode) {
+ if (bios->fp.dual_link)
+- clktableptr += 2;
+- if (bios->fp.BITbit1)
+- clktableptr++;
++ clktable += 4;
++ if (bios->fp.if_is_24bit)
++ clktable += 2;
+ } else {
+ /* using EDID */
+- uint8_t fallback = bios->data[bios->fp.lvdsmanufacturerpointer + 4];
+- int fallbackcmpval = (dcbent->or == 4) ? 4 : 1;
++ int cmpval_24bit = (dcbent->or == 4) ? 4 : 1;
+
+ if (bios->fp.dual_link) {
+- clktableptr += 2;
+- fallbackcmpval *= 2;
++ clktable += 4;
++ cmpval_24bit <<= 1;
+ }
+- if (fallbackcmpval & fallback)
+- clktableptr++;
++
++ if (bios->fp.strapless_is_24bit & cmpval_24bit)
++ clktable += 2;
+ }
+
+- /* adding outputset * 8 may not be correct */
+- clktable = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 15 + clktableptr * 2 + outputset * 8]);
++ clktable = ROM16(bios->data[clktable]);
+ if (!clktable) {
+ NV_ERROR(dev, "Pixel clock comparison table not found\n");
+ return -ENOENT;
+@@ -3638,37 +3640,40 @@ int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, b
+ *if_is_24bit = bios->data[lvdsofs] & 16;
+ break;
+ case 0x30:
+- /*
+- * My money would be on there being a 24 bit interface bit in
+- * this table, but I have no example of a laptop bios with a
+- * 24 bit panel to confirm that. Hence we shout loudly if any
+- * bit other than bit 0 is set (I've not even seen bit 1)
+- */
+- if (bios->data[lvdsofs] > 1)
+- NV_ERROR(dev,
+- "You have a very unusual laptop display; please report it\n");
++ case 0x40:
+ /*
+ * No sign of the "power off for reset" or "reset for panel
+ * on" bits, but it's safer to assume we should
+ */
+ bios->fp.power_off_for_reset = true;
+ bios->fp.reset_after_pclk_change = true;
++
+ /*
+ * It's ok lvdsofs is wrong for nv4x edid case; dual_link is
+- * over-written, and BITbit1 isn't used
++ * over-written, and if_is_24bit isn't used
+ */
+ bios->fp.dual_link = bios->data[lvdsofs] & 1;
+- bios->fp.BITbit1 = bios->data[lvdsofs] & 2;
+- bios->fp.duallink_transition_clk = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 5]) * 10;
+- break;
+- case 0x40:
+- bios->fp.dual_link = bios->data[lvdsofs] & 1;
+ bios->fp.if_is_24bit = bios->data[lvdsofs] & 2;
+ bios->fp.strapless_is_24bit = bios->data[bios->fp.lvdsmanufacturerpointer + 4];
+ bios->fp.duallink_transition_clk = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 5]) * 10;
+ break;
+ }
+
++ /* Dell Latitude D620 reports a too-high value for the dual-link
++ * transition freq, causing us to program the panel incorrectly.
++ *
++ * It doesn't appear the VBIOS actually uses its transition freq
++ * (90000kHz), instead it uses the "Number of LVDS channels" field
++ * out of the panel ID structure (http://www.spwg.org/).
++ *
++ * For the moment, a quirk will do :)
++ */
++ if ((dev->pdev->device == 0x01d7) &&
++ (dev->pdev->subsystem_vendor == 0x1028) &&
++ (dev->pdev->subsystem_device == 0x01c2)) {
++ bios->fp.duallink_transition_clk = 80000;
++ }
++
+ /* set dual_link flag for EDID case */
+ if (pxclk && (chip_version < 0x25 || chip_version > 0x28))
+ bios->fp.dual_link = (pxclk >= bios->fp.duallink_transition_clk);
+diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h
+index 4f88e69..fd6274a 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_bios.h
++++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
+@@ -267,7 +267,6 @@ struct nvbios {
+ bool reset_after_pclk_change;
+ bool dual_link;
+ bool link_c_increment;
+- bool BITbit1;
+ bool if_is_24bit;
+ int duallink_transition_clk;
+ uint8_t strapless_is_24bit;
diff --git a/drm-nouveau-g80-ctxprog.patch b/drm-nouveau-g80-ctxprog.patch
new file mode 100644
index 0000000..299aa64
--- /dev/null
+++ b/drm-nouveau-g80-ctxprog.patch
@@ -0,0 +1,10306 @@
+From c771c11d2febd217b9ee11156211296c7aad77cc Mon Sep 17 00:00:00 2001
+From: Ben Skeggs <bskeggs@redhat.com>
+Date: Fri, 11 Dec 2009 14:04:08 +1000
+Subject: [PATCH 1/6] drm/nouveau: add ctxprog/ctxvals firmware for >=G80 chipsets
+
+---
+ firmware/Makefile | 12 +
+ firmware/nouveau/nv50.ctxprog.ihex | 100 ++
+ firmware/nouveau/nv50.ctxvals.ihex | 1335 ++++++++++++++++++++++++++
+ firmware/nouveau/nv84.ctxprog.ihex | 91 ++
+ firmware/nouveau/nv84.ctxvals.ihex | 557 +++++++++++
+ firmware/nouveau/nv86.ctxprog.ihex | 58 ++
+ firmware/nouveau/nv86.ctxvals.ihex | 340 +++++++
+ firmware/nouveau/nv92.ctxprog.ihex | 103 ++
+ firmware/nouveau/nv92.ctxvals.ihex | 1232 ++++++++++++++++++++++++
+ firmware/nouveau/nv94.ctxprog.ihex | 79 ++
+ firmware/nouveau/nv94.ctxvals.ihex | 761 +++++++++++++++
+ firmware/nouveau/nv96.ctxprog.ihex | 79 ++
+ firmware/nouveau/nv96.ctxvals.ihex | 761 +++++++++++++++
+ firmware/nouveau/nv98.ctxprog.ihex | 57 ++
+ firmware/nouveau/nv98.ctxvals.ihex | 326 +++++++
+ firmware/nouveau/nva0.ctxprog.ihex | 89 ++
+ firmware/nouveau/nva0.ctxvals.ihex | 1836 ++++++++++++++++++++++++++++++++++++
+ firmware/nouveau/nva5.ctxprog.ihex | 77 ++
+ firmware/nouveau/nva5.ctxvals.ihex | 772 +++++++++++++++
+ firmware/nouveau/nva8.ctxprog.ihex | 75 ++
+ firmware/nouveau/nva8.ctxvals.ihex | 475 ++++++++++
+ firmware/nouveau/nvaa.ctxprog.ihex | 79 ++
+ firmware/nouveau/nvaa.ctxvals.ihex | 356 +++++++
+ firmware/nouveau/nvac.ctxprog.ihex | 79 ++
+ firmware/nouveau/nvac.ctxvals.ihex | 362 +++++++
+ 25 files changed, 10091 insertions(+), 0 deletions(-)
+ create mode 100644 firmware/nouveau/nv50.ctxprog.ihex
+ create mode 100644 firmware/nouveau/nv50.ctxvals.ihex
+ create mode 100644 firmware/nouveau/nv84.ctxprog.ihex
+ create mode 100644 firmware/nouveau/nv84.ctxvals.ihex
+ create mode 100644 firmware/nouveau/nv86.ctxprog.ihex
+ create mode 100644 firmware/nouveau/nv86.ctxvals.ihex
+ create mode 100644 firmware/nouveau/nv92.ctxprog.ihex
+ create mode 100644 firmware/nouveau/nv92.ctxvals.ihex
+ create mode 100644 firmware/nouveau/nv94.ctxprog.ihex
+ create mode 100644 firmware/nouveau/nv94.ctxvals.ihex
+ create mode 100644 firmware/nouveau/nv96.ctxprog.ihex
+ create mode 100644 firmware/nouveau/nv96.ctxvals.ihex
+ create mode 100644 firmware/nouveau/nv98.ctxprog.ihex
+ create mode 100644 firmware/nouveau/nv98.ctxvals.ihex
+ create mode 100644 firmware/nouveau/nva0.ctxprog.ihex
+ create mode 100644 firmware/nouveau/nva0.ctxvals.ihex
+ create mode 100644 firmware/nouveau/nva5.ctxprog.ihex
+ create mode 100644 firmware/nouveau/nva5.ctxvals.ihex
+ create mode 100644 firmware/nouveau/nva8.ctxprog.ihex
+ create mode 100644 firmware/nouveau/nva8.ctxvals.ihex
+ create mode 100644 firmware/nouveau/nvaa.ctxprog.ihex
+ create mode 100644 firmware/nouveau/nvaa.ctxvals.ihex
+ create mode 100644 firmware/nouveau/nvac.ctxprog.ihex
+ create mode 100644 firmware/nouveau/nvac.ctxvals.ihex
+
+diff --git a/firmware/Makefile b/firmware/Makefile
+index f4ca0c7..7766c96 100644
+--- a/firmware/Makefile
++++ b/firmware/Makefile
+@@ -62,6 +62,18 @@ fw-shipped-$(CONFIG_DRM_RADEON) += radeon/R100_cp.bin radeon/R200_cp.bin \
+ radeon/RV770_pfp.bin radeon/RV770_me.bin \
+ radeon/RV730_pfp.bin radeon/RV730_me.bin \
+ radeon/RV710_pfp.bin radeon/RV710_me.bin
++fw-shipped-$(CONFIG_DRM_NOUVEAU) += nouveau/nv50.ctxprog nouveau/nv50.ctxvals \
++ nouveau/nv84.ctxprog nouveau/nv84.ctxvals \
++ nouveau/nv86.ctxprog nouveau/nv86.ctxvals \
++ nouveau/nv92.ctxprog nouveau/nv92.ctxvals \
++ nouveau/nv94.ctxprog nouveau/nv94.ctxvals \
++ nouveau/nv96.ctxprog nouveau/nv96.ctxvals \
++ nouveau/nv98.ctxprog nouveau/nv98.ctxvals \
++ nouveau/nva0.ctxprog nouveau/nva0.ctxvals \
++ nouveau/nva5.ctxprog nouveau/nva5.ctxvals \
++ nouveau/nva8.ctxprog nouveau/nva8.ctxvals \
++ nouveau/nvaa.ctxprog nouveau/nvaa.ctxvals \
++ nouveau/nvac.ctxprog nouveau/nvac.ctxvals
+ fw-shipped-$(CONFIG_DVB_AV7110) += av7110/bootcode.bin
+ fw-shipped-$(CONFIG_DVB_TTUSB_BUDGET) += ttusb-budget/dspbootcode.bin
+ fw-shipped-$(CONFIG_E100) += e100/d101m_ucode.bin e100/d101s_ucode.bin \
+diff --git a/firmware/nouveau/nv50.ctxprog.ihex b/firmware/nouveau/nv50.ctxprog.ihex
+new file mode 100644
+index 0000000..684781a
+--- /dev/null
++++ b/firmware/nouveau/nv50.ctxprog.ihex
+@@ -0,0 +1,100 @@
++:100000004E564350008A018E0070009C0070002004
++:10001000002000080060004C005000890E400000E5
++:100020000020000700600000003000FF00C000005A
++:10003000002000FF008000090070004D874100444F
++:100040001E4000051E40000D1E40000663410005D5
++:10005000006000C5154000110060000B1C4000FF4F
++:10006000FF9000FFFF910020002000080060004C7E
++:1000700000500009006000456341004D7E41009D35
++:10008000007000CF2240009F0070009F005000C011
++:100090001F400080002000080060004F1F4000C08B
++:1000A0001F4000CC254000810070000000200006A9
++:1000B00000600000007000FC1B1100830070000055
++:1000C0000030000059210007006000010BC0001C37
++:1000D00000200001008000CB005000FF00C0008025
++:1000E0000070008300700047002000060060000AD6
++:1000F000021100C00520000700600000003000FF72
++:1001000000C000FF00C800076E410027262000FF46
++:100110000080008C454000CB0050003F02A0004012
++:10012000002000060060000F007000020217000AA5
++:10013000021100320020000D02100042021B0002DA
++:1001400003120002041400000518000905130050F2
++:100150000515000506110007061E00000711000026
++:1001600009110002091100000A1100020B160028F3
++:100170000B11002B0B1400010C11000014110005D1
++:1001800014110007141100091411000B141100EAD6
++:10019000002000001510000F6D40004B6D40000066
++:1001A0003721000700600040042000FF0088008F16
++:1001B0000070008C6D4000CB00500000000000F883
++:1001C0001811002B002000051A1000001C13000459
++:1001D0001C1100201C1400251C1100401C1300449D
++:1001E0001C1100601C1400651C1100801C1300848D
++:1001F0001C1100A01C1400A51C1100C01C1300C47D
++:100200001C1100E01C1400E51C1100001D1300046B
++:100210001D1100201D1400251D1100401D13004458
++:100220001D1100601D1400651D1100001F1300400A
++:100230001F1900E0A74000170220000600600044DC
++:1002400000200080201000C6201100C9201500D019
++:100250002019000021120003211200002216000FB5
++:100260009F40004B9F400000372100070060004086
++:10027000042000FF0088008F0070008C9F4000CB9E
++:100280000050000000000007221200802211000030
++:1002900023110002231100802312008B23110094EC
++:1002A000231900E1B94000850220000600600044E7
++:1002B00000200080241000C6241100C9241500D09D
++:1002C000241900002512000325120000261600073D
++:1002D000261200802611000027110002271100803D
++:1002E0002712008B27110094271900E2D54000F354
++:1002F000022000060060004400200080281000C694
++:10030000281100C92815000FC940004BC940000042
++:100310003721000700600040042000FF0088008FA4
++:100320000070008CC94000CB00500000000000D0DD
++:100330002819000029120003291200002A160007BC
++:100340002A1200802A1100002B1100022B110080BC
++:100350002B12008B2B1100942B1900E3E740006156
++:100360000320000600600044002000802C1000C61E
++:100370002C1100C92C1500D02C1900002D120003DF
++:100380002D1200002E1600072E1200802E110000E4
++:100390002F1100022F1100802F12008B2F110094BB
++:1003A0002F1900E4034100CF032000060060004441
++:1003B000002000803010000FF540004BF540000099
++:1003C0003721000700600040042000FF0088008FF4
++:1003D0000070008CF54000CB00500000000000C60B
++:1003E000301100C9301500D030190000311200035F
++:1003F0003112000032160007321200803211000064
++:1004000033110002331100803312008B331100943A
++:10041000331900E51541003D04200006006000444A
++:1004200000200080341000C6341100C9341500D0FB
++:10043000341900003512000335120000361600078B
++:10044000361200803611000037110002371100808B
++:100450003712008B37110094371900E6314100AB99
++:100460000420000600600044002000803810000FC7
++:100470002341004B2341000037210007006000406A
++:10048000042000FF0088008F0070008C234100CB07
++:1004900000500000000000C6381100C9381500D017
++:1004A0003819000039120003391200003A1600070B
++:1004B0003A1200803A1100003B1100023B1100800B
++:1004C0003B12008B3B1100943B1900E7434100199C
++:1004D0000520000600600044002000803C1000C69B
++:1004E0003C1100C93C1500D03C1900003D1200032E
++:1004F0003D1200003E1600073E1200803E11000033
++:100500003F1100023F1100803F12008B3F11009409
++:100510003F1900000000000F4A4100CB00500000CE
++:100520003721000700600040042000FF008800CB56
++:10053000005000874D41000A0060000000000000EC
++:100540005C4100A000700080007000C00520000722
++:1005500000600004002000FF00C000FF008000CB0E
++:10056000005000000070000000200006006000FE47
++:100570001B11004D7E4100000070000000200006AD
++:10058000006000FE1B1100800070001D0070004D17
++:1005900011400081007000040060004A0050008893
++:1005A0006841000B006000000020000600600000B1
++:1005B0000070000B7E4100FD1B11004D37400027ED
++:1005C000262000FD008000CB0050000200C000C0CB
++:1005D000052000070060005F01200002008000CBC2
++:1005E0000050000218C000C824200002008000CB88
++:1005F0000050004D4340000B0060004D7C41000165
++:100600000070000300700006824100058341000D68
++:10061000006000050070000D007000060070000B07
++:0F0620000070000E0070001C0070000C006000E5
++:00000001FF
+diff --git a/firmware/nouveau/nv50.ctxvals.ihex b/firmware/nouveau/nv50.ctxvals.ihex
+new file mode 100644
+index 0000000..9c82e73
+--- /dev/null
++++ b/firmware/nouveau/nv50.ctxvals.ihex
+@@ -0,0 +1,1335 @@
++:100000004E56435600690A000043000000300000CD
++:100010000048000000400040FF490000008000F060
++:10002000FF4A0000009000F7FF4B000000A806E820
++:10003000FF4C0000000200000075000000030000FB
++:10004000007600000000100000850000000CFE009B
++:10005000008A000000001000009500000087FD01EC
++:10006000009A000000181000009B000000FF000034
++:1000700000A900000004000000AA0000005F0001C9
++:1000800000AC00000000060000AD0000000600000B
++:1000900000B2000000FF000000B4000000000400F7
++:1000A00000B900000001000000BA0000008000302C
++:1000B00000BB00000004000000C2000000020000BD
++:1000C00000C300000001000000C6000000010000A5
++:1000D00000C700000000010000CD00000002000089
++:1000E00000CE00000001000000CF00000001000071
++:1000F00000D300000001000000D4000000FFFF3F1B
++:1001000000D5000000FF1F0000D700000001000024
++:1001100000D800000001000000DA0000000100002B
++:1001200000DB00000001000000DC00000001000016
++:1001300000DD00000004000000DE000000010000FF
++:1001400000DF00000001000000E0000000010000EE
++:1001500000E100000007000000E2000000010000D4
++:1001600000E300000007000000E4000000010000C0
++:1001700000E500000001000000E6000000010000B2
++:1001800000EB00000001000000EC00000001000096
++:1001900000EE00000001000000EF0000000A000077
++:1001A00000F300000040000000F500000002000025
++:1001B00000F600000000010000F700000001000050
++:1001C00000F800000000010000FF00000001000036
++:1001D0000008010000040000000E01000001000002
++:1001E000000F0100000001000011010000010000EB
++:1001F00000140100000001000015010000010000D2
++:1002000000160100000001000018010000010000BC
++:10021000001B010000000100001C010000010000A3
++:10022000001E01000001000000210100000200008A
++:100230000023010000010000002501000001000072
++:10024000002A01000003000000310100000400004A
++:100250000032010000700000003301000080000047
++:1002600000380100000C0000003901000008000007
++:10027000003A010000140000003B010000260000CD
++:10028000003E010000010000003F010000020000EC
++:1002900000400100000300000041010000040000D4
++:1002A00000420100000500000043010000060000BC
++:1002B00000440100000700000045010000010000AB
++:1002C0000056010000CF0000006101000080000026
++:1002D000006201000004000000630100000400004F
++:1002E000006401000001000000660100001200002F
++:1002F000006701000010000000680100000C000011
++:100300000069010000010000006D01000004000010
++:10031000006E010000020000006F010000040000F8
++:100320000072010000FFFF3F0073010000FF1F008B
++:1003300000750100000400000076010000140000B8
++:100340000077010000010000007A010000020000B7
++:10035000007D010000010000007F0100000200009C
++:100360000080010000001000008201000001000078
++:100370000083010000010000008401000001000072
++:10038000008501000001000000860100000100005E
++:10039000008A010000000200008C01000001000042
++:1003A000008D010000700000008E01000080000040
++:1003B00000910100000100000092010000700000A7
++:1003C0000093010000800000009701000001000080
++:1003D0000098010000CF0000009A01000001000019
++:1003E000009E010000CF000000A0010000020000FC
++:1003F00000A201000001000000A4010000010000B3
++:1004000000A6010000CF000000A7010000CF0000FF
++:1004100000A801000001000000AB010000800F00F7
++:1004200000BD01000080007F00CC01000080007F43
++:1004300000D501000020F8741BD6010000018005E2
++:1004400089D9010000FA107C02DA010000C0000026
++:1004500000DB010000802089B7DE01000020F87475
++:100460001BDF01000001800589E2010000FA107C19
++:1004700002E3010000C0000000E4010000802089C8
++:10048000B7E701000020F8741BE8010000018005B7
++:1004900089EB010000FA107C02EC010000C00000B2
++:1004A00000ED010000802089B7F001000020F87401
++:1004B0001BF101000001800589F4010000FA107CA5
++:1004C00002F5010000C0000000F601000080208954
++:1004D000B7F901000020F8741BFA01000001800543
++:1004E00089FD010000FA107C02FE010000C000003E
++:1004F00000FF010000802089B70202000020F8748C
++:100500001B030200000180058906020000FA107C2E
++:100510000207020000C000000008020000802089DD
++:10052000B70B020000400001000D02000022000095
++:100530000010020000400001001102000022000033
++:1005400000170200000000800118020000000016E1
++:100550000019020000000080011D020000FFFF03DF
++:10056000001E020000000008002702000001040134
++:100570000029020000400000002B020000BF000024
++:10058000002D020000101200002E0200008000006A
++:100590000037020000000080013802000000001651
++:1005A0000039020000000080013D020000FFFF034F
++:1005B000003E0200000000080047020000010401A4
++:1005C0000049020000400000004B020000BF000094
++:1005D000004D020000101200004E020000800000DA
++:1005E0000058020000707000005B020000FFFF0373
++:1005F00000610200000704120062020000071509F2
++:10060000056302000002020105640200000102030A
++:10061000006B020000400000006C0200000A0B0C9E
++:100620000D6D020000101214006E020000F00100B7
++:10063000006F0200000100000070020000030000D3
++:1006400000710200000080000073020000009E03A1
++:100650000074020000400000007502000000380035
++:10066000007602000040404000770200000AFF00D0
++:10067000007902000005F077007A020000FF7F0099
++:10068000007D020000FF0300007E02000003000066
++:10069000007F0200000300000080020000FF010054
++:1006A00000810200001F000000820200000F000015
++:1006B00000830200000F000000850200000000809F
++:1006C0000186020000000016008702000000008082
++:1006D000018B020000FFFF03008C020000000008F5
++:1006E0000095020000010401009702000040000094
++:1006F0000099020000BF0000009B020000101200E1
++:10070000009C02000080000000A5020000000080A4
++:1007100001A602000000001600A7020000000080F1
++:1007200001AB020000FFFF0300AC02000000000864
++:1007300000B502000001040100B702000040000003
++:1007400000B9020000BF000000BB02000010120050
++:1007500000BC02000080000000C6020000707000B3
++:1007600000C9020000FFFF0300CF020000070412CF
++:1007700000D002000007150905D1020000020201A5
++:1007800005D202000001020300D90200004000006F
++:1007900000DA0200000A0B0C0DDB0200001012143C
++:1007A00000DC020000F0010000DD0200000100009A
++:1007B00000DE02000003000000DF020000008000F5
++:1007C00000E1020000009E0300E202000040000081
++:1007D00000E302000000380000E402000040404056
++:1007E00000E50200000AFF0000E702000005F077C4
++:1007F00000E8020000FF7F0000EB020000FF0300A2
++:1008000000EC02000003000000ED02000003000005
++:1008100000EE020000FF010000EF0200001F0000D8
++:1008200000F00200000F000000F10200000F0000C5
++:1008300000F302000000008001F402000000001636
++:1008400000F502000000008001F9020000FFFF0334
++:1008500000FA020000000008000303000001040188
++:1008600000050300004000000007030000BF000077
++:100870000009030000101200000A030000800000BD
++:1008800000130300000000800114030000000016A4
++:1008900000150300000000800119030000FFFF03A2
++:1008A000001A0300000000080023030000010401F7
++:1008B00000250300004000000027030000BF0000E7
++:1008C0000029030000101200002A0300008000002D
++:1008D00000340300007070000037030000FFFF03C6
++:1008E000003D030000070412003E03000007150945
++:1008F000053F03000002020105400300000102035E
++:10090000004703000040000000480300000A0B0CF1
++:100910000D49030000101214004A030000F001000A
++:10092000004B030000010000004C03000003000026
++:10093000004D030000008000004F030000009E03F4
++:100940000050030000400000005103000000380088
++:10095000005203000040404000530300000AFF0023
++:10096000005503000005F0770056030000FF7F00EC
++:100970000059030000FF0300005A030000030000B9
++:10098000005B030000030000005C030000FF0100A7
++:10099000005D0300001F0000005E0300000F000068
++:1009A000005F0300000F00000061030000000080F2
++:1009B00001620300000000160063030000000080D5
++:1009C0000167030000FFFF03006803000000000848
++:1009D00000710300000104010073030000400000E7
++:1009E0000075030000BF0000007703000010120034
++:1009F00000780300008000000081030000000080F8
++:100A00000182030000000016008303000000008044
++:100A10000187030000FFFF030088030000000008B7
++:100A20000091030000010401009303000040000056
++:100A30000095030000BF00000097030000101200A3
++:100A4000009803000080000000A203000070700006
++:100A500000A5030000FFFF0300AB03000007041222
++:100A600000AC03000007150905AD030000020201F8
++:100A700005AE03000001020300B5030000400000C2
++:100A800000B60300000A0B0C0DB70300001012148F
++:100A900000B8030000F0010000B9030000010000ED
++:100AA00000BA03000003000000BB03000000800048
++:100AB00000BD030000009E0300BE030000400000D4
++:100AC00000BF03000000380000C0030000404040A9
++:100AD00000C10300000AFF0000C303000005F07717
++:100AE00000C4030000FF7F0000C7030000FF0300F5
++:100AF00000C803000003000000C903000003000059
++:100B000000CA030000FF010000CB0300001F00002B
++:100B100000CC0300000F000000CD0300000F000018
++:100B200000CF03000000008001D003000000001689
++:100B300000D103000000008001D5030000FFFF0387
++:100B400000D603000000000800DF030000010401DC
++:100B500000E103000040000000E3030000BF0000CC
++:100B600000E503000010120000E603000080000012
++:100B700000EF03000000008001F0030000000016F9
++:100B800000F103000000008001F5030000FFFF03F7
++:100B900000F603000000000800FF0300000104014C
++:100BA00000010400004000000003040000BF00003A
++:100BB0000005040000101200000604000080000080
++:100BC00000100400007070000013040000FFFF0319
++:100BD0000019040000070412001A04000007150998
++:100BE000051B040000020201051C040000010203B1
++:100BF000002304000040000000240400000A0B0C45
++:100C00000D250400001012140026040000F001005D
++:100C10000027040000010000002804000003000079
++:100C20000029040000008000002B040000009E0347
++:100C3000002C040000400000002D040000003800DB
++:100C4000002E040000404040002F0400000AFF0076
++:100C5000003104000005F0770032040000FF7F003F
++:100C60000035040000FF030000360400000300000C
++:100C700000370400000300000038040000FF0100FA
++:100C800000390400001F0000003A0400000F0000BB
++:100C9000003B0400000F0000003D04000000008045
++:100CA000013E040000000016003F04000000008028
++:100CB0000143040000FFFF0300440400000000089B
++:100CC000004D040000010401004F0400004000003A
++:100CD0000051040000BF0000005304000010120087
++:100CE0000054040000800000005D0400000000804B
++:100CF000015E040000000016005F04000000008098
++:100D00000163040000FFFF0300640400000000080A
++:100D1000006D040000010401006F040000400000A9
++:100D20000071040000BF00000073040000101200F6
++:100D30000074040000800000007E04000070700059
++:100D40000081040000FFFF03008704000007041275
++:100D5000008804000007150905890400000202014B
++:100D6000058A040000010203009104000040000015
++:100D700000920400000A0B0C0D93040000101214E2
++:100D80000094040000F00100009504000001000040
++:100D9000009604000003000000970400000080009B
++:100DA0000099040000009E03009A04000040000027
++:100DB000009B040000003800009C040000404040FC
++:100DC000009D0400000AFF00009F04000005F0776A
++:100DD00000A0040000FF7F0000A3040000FF030048
++:100DE00000A404000003000000A5040000030000AC
++:100DF00000A6040000FF010000A70400001F00007F
++:100E000000A80400000F000000A90400000F00006B
++:100E100000AB04000000008001AC040000000016DC
++:100E200000AD04000000008001B1040000FFFF03DA
++:100E300000B204000000000800BB0400000104012F
++:100E400000BD04000040000000BF040000BF00001F
++:100E500000C104000010120000C204000080000065
++:100E600000CB04000000008001CC0400000000164C
++:100E700000CD04000000008001D1040000FFFF034A
++:100E800000D204000000000800DB0400000104019F
++:100E900000DD04000040000000DF040000BF00008F
++:100EA00000E104000010120000E2040000800000D5
++:100EB00000EC04000070700000EF040000FFFF036E
++:100EC00000F504000007041200F6040000071509ED
++:100ED00005F704000002020105F804000001020306
++:100EE00000FF04000040000000000500000A0B0C99
++:100EF0000D010500001012140002050000F00100B1
++:100F000000030500000100000004050000030000CC
++:100F100000050500000080000007050000009E039A
++:100F2000000805000040000000090500000038002E
++:100F3000000A050000404040000B0500000AFF00C9
++:100F4000000D05000005F077000E050000FF7F0092
++:100F50000011050000FF030000120500000300005F
++:100F600000130500000300000014050000FF01004D
++:100F700000150500001F000000160500000F00000E
++:100F800000170500000F0000001905000000008098
++:100F9000011A050000000016001B0500000000807B
++:100FA000011F050000FFFF030020050000000008EE
++:100FB0000029050000010401002B0500004000008D
++:100FC000002D050000BF0000002F050000101200DA
++:100FD000003005000080000000390500000000809E
++:100FE000013A050000000016003B050000000080EB
++:100FF000013F050000FFFF0300400500000000085E
++:101000000049050000010401004B050000400000FC
++:10101000004D050000BF0000004F05000010120049
++:101020000050050000800000005A050000707000AC
++:10103000005D050000FFFF030063050000070412C8
++:10104000006405000007150905650500000202019E
++:101050000566050000010203006D05000040000068
++:10106000006E0500000A0B0C0D6F05000010121435
++:101070000070050000F00100007105000001000093
++:1010800000720500000300000073050000008000EE
++:101090000075050000009E0300760500004000007A
++:1010A000007705000000380000780500004040404F
++:1010B00000790500000AFF00007B05000005F077BD
++:1010C000007C050000FF7F00007F050000FF03009B
++:1010D00000800500000300000081050000030000FF
++:1010E0000082050000FF010000830500001F0000D2
++:1010F00000840500000F000000850500000F0000BF
++:1011000000CC05000004000000CD05000004000034
++:1011100000CE05000004000000CF05000004000020
++:1011200000E10500000F00000003060000020000BF
++:1011300000090600002000000013060000603E00C9
++:10114000007B060000010000007C06000004000097
++:10115000007D060000040000007E06000004000080
++:10116000007F06000004000000810600001A000055
++:101170000083060000100000008406000004000048
++:101180000085060000040000008606000004000040
++:101190000087060000040000008C060000808060CC
++:1011A000008D060000808060008E06000080806058
++:1011B000008F060000808060009B06000001000098
++:1011C00000B406000004000000B5060000040000A2
++:1011D00000B606000004000000B70600000400008E
++:1011E00000CC06000004000000CD06000004000052
++:1011F00000CE06000004000000CF0600000400003E
++:1012000000D306000002000000D406000004000025
++:1012100000D506000004000000D60600000400000F
++:1012200000D706000004000000DC0600008000007B
++:1012300000DD06000080000000DE060000800000E7
++:1012400000DF06000080000000E40600000010003F
++:1012500000E506000000100000E606000000100097
++:1012600000E706000000100000EC0600000400008B
++:1012700000ED06000004000000EE0600000400007F
++:1012800000EF06000004000000F10600000400006A
++:1012900000F9060000040000000907000004000037
++:1012A0000011070000080000001B070000010000FB
++:1012B0000021070000FF03000023070000100000CA
++:1012C000003307000001000000890700000F000044
++:1012D00000D3070000100000001408000004000004
++:1012E00000150800000400000016080000040000BB
++:1012F0000017080000040000001C08000080000027
++:10130000001D080000800000001E08000080000092
++:10131000001F0800008000000024080000040000F6
++:10132000002508000004000000260800000400005A
++:101330000027080000040000002C08000000010243
++:10134000032D080000000102032E08000000010226
++:10135000032F08000000010203340800000300000E
++:1013600000350800000300000036080000030000FC
++:101370000037080000030000003C080000001000D7
++:10138000003D080000001000003E080000001000B2
++:10139000003F0800000010000044080000040000A6
++:1013A000004508000004000000460800000400009A
++:1013B0000047080000040000005B080000000080F7
++:1013C0003F630800000000803F6B080000000080C1
++:1013D0003F6C080000040000006D080000040000DD
++:1013E000006E080000040000006F08000004000008
++:1013F00000730800000000803F7408000003000034
++:1014000000750800000300000076080000030000DB
++:101410000077080000030000007B08000000008047
++:101420003F830800000000803F8B08000000008020
++:101430003F930800000000803F9408000004000073
++:101440000095080000040000009608000004000059
++:101450000097080000040000009B080000000080C6
++:101460003FA30800000000803FAB080000000080A0
++:101470003FB30800000000803FBB08000000008070
++:101480003FC30800000000803FCB08000000008040
++:101490003FD30800000000803FA3090000100000B7
++:1014A00000B30900003F000000EB0900000100004C
++:1014B00000FB090000010000000B0A000001000011
++:1014C00000730A000011000000B30A00000F0000C2
++:1014D00000D00A000021000000D80A00000100002E
++:1014E00000E00A000002000000E80A00000001001D
++:1014F00000F00A000000010000F30A0000110000E3
++:1015000000F80A000001000000100B0000010000BC
++:1015100000180B0000020000001B0B00000100007F
++:1015200000200B000000010000230B000001000060
++:1015300000280B0000000100002B0B000001000040
++:1015400000300B000001000000330B00000200001F
++:10155000003B0B000001000000430B0000020000F4
++:10156000004B0B0000010000005B0B0000603E0020
++:10157000006B0B00008168AC0F1B0C000001000029
++:1015800000230C0000020000002B0C0000010000F2
++:1015900000330C0000010000003B0C0000020000C2
++:1015A00000430C0000010000004B0C000001000093
++:1015B000006B0C000011000000730C000001000023
++:1015C00000930F000002000000A30F0000603E0027
++:1015D000000B1000000100000013100000100000BC
++:1015E000002B10000001000000631000000200004A
++:1015F00000AB10000001000000B31000001000005C
++:1016000000B91000000F000000C31000000100002E
++:1016100000C910000001000000631100001000006C
++:1016200000EB1100000000803FF31100000000807B
++:101630003FFB1100000000803F031200000000800B
++:101640003F0B1200000000803F13120000000080DA
++:101650003F1B1200000000803F23120000000080AA
++:101660003F2B1200000000803F331200000000807A
++:101670003F3B1200000000803F431200000000804A
++:101680003F4B1200000000803F531200000000801A
++:101690003F5B1200000000803F63120000000080EA
++:1016A0003F3313000010000000431300003F000010
++:1016B000007B130000010000008B130000010000FC
++:1016C000009B13000001000000A11300000F0000A8
++:1016D000000314000011000000431400000F00007C
++:1016E000008314000011000000AB14000001000092
++:1016F00000B314000001000000BB14000001000052
++:1017000000C314000002000000CB14000001000020
++:1017100000D314000002000000DB140000010000F0
++:1017200000EB140000603E0000FB1400008168AC78
++:101730000FAB15000001000000B31500000200000F
++:1017400000BB15000001000000C3150000010000EF
++:1017500000CB15000002000000D3150000010000BE
++:1017600000DB15000001000000FB15000011000067
++:101770000003160000010000002319000002000011
++:101780000033190000603E00009B190000010000BA
++:1017900000A319000010000000BB190000010000A8
++:1017A00000F3190000020000003B1A0000010000D5
++:1017B00000431A000010000000531A00000100004E
++:1017C00000F31A0000100000007B1B0000000080E6
++:1017D0003F831B00000000803F8B1B000000008047
++:1017E0003F931B00000000803F9B1B000000008017
++:1017F0003FA31B00000000803FAB1B0000000080E7
++:101800003FB31B00000000803FBB1B0000000080B6
++:101810003FC31B00000000803FCB1B000000008086
++:101820003FD31B00000000803FDB1B000000008056
++:101830003FE31B00000000803FEB1B000000008026
++:101840003FF31B00000000803FC31C00001000009D
++:1018500000D31C00003F0000000B1D000001000031
++:10186000001B1D0000010000002B1D0000010000F6
++:1018700000931D000011000000D31D00000F0000A8
++:1018800000131E0000110000003B1E0000010000BC
++:1018900000431E0000010000004B1E00000100007C
++:1018A00000531E0000020000005B1E00000100004B
++:1018B00000631E0000020000006B1E00000100001B
++:1018C000007B1E0000603E00008B1E00008168ACA3
++:1018D0000F3B1F000001000000431F00000200003A
++:1018E000004B1F000001000000531F00000100001A
++:1018F000005B1F000002000000631F0000010000E9
++:10190000006B1F0000010000008B1F000011000091
++:1019100000931F000001000000B32200000200003D
++:1019200000C3220000603E00002B230000010000E5
++:101930000033230000100000004B230000010000D2
++:10194000008323000002000000CB23000001000000
++:1019500000D323000010000000E32300000100007A
++:101960000083240000100000000B25000000008010
++:101970003F132500000000803F1B25000000008071
++:101980003F232500000000803F2B25000000008041
++:101990003F332500000000803F3B25000000008011
++:1019A0003F432500000000803F4B250000000080E1
++:1019B0003F532500000000803F5B250000000080B1
++:1019C0003F632500000000803F6B25000000008081
++:1019D0003F732500000000803F7B25000000008051
++:1019E0003F832500000000803F53260000100000C8
++:1019F00000632600003F0000009B2600000100005D
++:101A000000AB26000001000000BB26000001000022
++:101A1000002327000011000000632700000F0000D2
++:101A200000A327000011000000CB270000010000E8
++:101A300000D327000001000000DB270000010000A8
++:101A400000E327000002000000EB27000001000077
++:101A500000F327000002000000FB27000001000047
++:101A6000000B280000603E00001B2800008168ACCD
++:101A70000FCB28000001000000D328000002000066
++:101A800000DB28000001000000E328000001000046
++:101A900000EB28000002000000F328000001000015
++:101AA00000FB280000010000001B290000110000BD
++:101AB000002329000001000000432C000002000068
++:101AC00000532C0000603E0000BB2C000001000011
++:101AD00000C32C000010000000DB2C0000010000FF
++:101AE00000132D0000020000005B2D00000100002B
++:101AF00000632D000010000000732D0000010000A5
++:101B000000132E0000100000009B2E00000000803B
++:101B10003FA32E00000000803FAB2E00000000809D
++:101B20003FB32E00000000803FBB2E00000000806D
++:101B30003FC32E00000000803FCB2E00000000803D
++:101B40003FD32E00000000803FDB2E00000000800D
++:101B50003FE32E00000000803FEB2E0000000080DD
++:101B60003FF32E00000000803FFB2E0000000080AD
++:101B70003F032F00000000803F0B2F00000000807B
++:101B80003F132F00000000803FE32F0000100000F3
++:101B900000F32F00003F0000002B30000001000088
++:101BA000003B300000010000004B3000000100004D
++:101BB00000B330000011000000F33000000F0000FF
++:101BC0000033310000110000005B31000001000013
++:101BD0000063310000010000006B310000010000D3
++:101BE00000733100000200000078310000040000A2
++:101BF000007B310000010000008031000004000083
++:101C00000083310000020000008B31000001000061
++:101C1000009B310000603E0000AB3100008168ACE9
++:101C20000F5B320000010000006332000002000080
++:101C3000006B320000010000007332000001000060
++:101C4000007B32000002000000833200000100002F
++:101C5000008B32000001000000AB320000110000D8
++:101C600000B332000001000000D335000002000084
++:101C700000E3350000603E00004B3600000100002C
++:101C80000053360000100000006B36000001000019
++:101C900000A336000002000000EB36000001000047
++:101CA00000F33600001000000003370000010000C0
++:101CB00000A3370000100000002B38000000008057
++:101CC0003F333800000000803F3B380000000080B8
++:101CD0003F433800000000803F4B38000000008088
++:101CE0003F533800000000803F5B38000000008058
++:101CF0003F633800000000803F6B38000000008028
++:101D00003F733800000000803F7B380000000080F7
++:101D10003F833800000000803F8B380000000080C7
++:101D20003F933800000000803F9B38000000008097
++:101D30003FA33800000000803F733900001000000E
++:101D400000833900003F000000BB390000010000A3
++:101D500000CB39000001000000DB39000001000069
++:101D600000433A000011000000833A00000F000019
++:101D700000C33A000011000000EB3A00000100002F
++:101D800000F33A000001000000FB3A0000010000EF
++:101D900000033B0000020000000B3B0000010000BC
++:101DA00000133B0000020000001B3B00000100008C
++:101DB000002B3B0000603E00003B3B00008168AC14
++:101DC0000FEB3B000001000000F33B0000020000AD
++:101DD00000FB3B000001000000033C00000100008C
++:101DE000000B3C000002000000133C00000100005A
++:101DF000001B3C0000010000003B3C000011000003
++:101E000000433C000001000000495500000F0000A5
++:101E100000D955000001000000E15500000100005C
++:101E200000F155000001000000F95500000001001C
++:101E300000015600000001000009560000110000DA
++:101E4000001956000008000000495600000100007B
++:101E5000005956000001000000615600000100001A
++:101E600000695600000100000071560000CF00001C
++:101E7000007956000002000000B156000001000089
++:101E800000C156000001000000C95600000100001A
++:101E900000D156000001000000F9560000040000C7
++:101EA0000009570000010000001157000015000054
++:101EB00000315700008044440411590000120C10F6
++:101EC00008395900000001000051590000010001CB
++:101ED0000061590000010001006959000001000083
++:101EE0000071590000010001007959000001000053
++:101EF0000081590000040000008959000002000020
++:101F000000BC59000004000000BD5900000400009E
++:101F100000BE59000004000000BF5900000400008A
++:101F200000C2590000FFFF3F00C459000003000039
++:101F300000C559000003000000C65900000300005E
++:101F400000C759000003000000DA590000FF1F001D
++:101F500000525A00000000803F545A00000F000059
++:101F600000555A00000F000000565A00000F0000F4
++:101F700000575A00000F0000008A5A0000040000B9
++:101F800000925A00001A000000AA5A000001000046
++:101F900000B45A000004000000B55A00000400001C
++:101FA00000B65A000004000000B75A000004000008
++:101FB00000BC5A0000FFFF0000BD5A0000FFFF00F8
++:101FC00000BE5A0000FFFF0000BF5A0000FFFF00E4
++:101FD00000C45A0000FFFF0000C55A0000FFFF00C8
++:101FE00000C65A0000FFFF0000C75A0000FFFF00B4
++:101FF00000CC5A0000FFFF0000CD5A0000FFFF0098
++:1020000000CE5A0000FFFF0000CF5A0000FFFF0083
++:1020100000D45A0000FFFF0000D55A0000FFFF0067
++:1020200000D65A0000FFFF0000D75A0000FFFF0053
++:10203000001C5B0000010000001D5B0000010000AF
++:10204000001E5B0000010000001F5B00000100009B
++:10205000003C5B0000010000003D5B00000100004F
++:10206000003E5B0000010000003F5B00000100003B
++:1020700000425B000000FFFF006C5B0000010000FD
++:10208000006D5B0000010000006E5B0000010000BD
++:10209000006F5B0000010000007A5B00000F000091
++:1020A00000BA5B00008168AC0FC25B000011000049
++:1020B00000D45B000001000000D55B0000010000BF
++:1020C00000D65B000001000000D75B0000010000AB
++:1020D00000DC5B000001000000DD5B00000100008F
++:1020E00000DE5B000001000000DF5B00000100007B
++:1020F00000E45B000002000000E55B00000200005D
++:1021000000E65B000002000000E75B000002000048
++:1021100000EC5B000001000000ED5B00000100002E
++:1021200000EE5B000001000000EF5B00000100001A
++:1021300000F45B000001000000F55B0000010000FE
++:1021400000F65B000001000000F75B0000010000EA
++:1021500000FC5B000002000000FD5B0000020000CC
++:1021600000FE5B000002000000FF5B0000020000B8
++:1021700000045C000001000000055C00000100009C
++:1021800000065C000001000000075C000001000088
++:1021900000145C000011000000155C00001100003C
++:1021A00000165C000011000000175C000011000028
++:1021B00000425C000004000000545C00008168AC38
++:1021C0000F555C00008168AC0F565C00008168AC64
++:1021D0000F575C00008168AC0F6A5C0000020000D1
++:1021E000006C5C0000040000006D5C000004000056
++:1021F000006E5C0000040000006F5C000004000042
++:1022000000725C0000000000047A5C000000000026
++:10221000048C5C0000110000008D5C0000110000C7
++:10222000008E5C0000110000008F5C0000110000B7
++:1022300000945C000001000000955C0000010000BB
++:1022400000965C000001000000975C0000010000A7
++:10225000009A5C000005000000A25C000052000033
++:1022600000A45C0000CF000000A55C0000CF0000CF
++:1022700000A65C0000CF000000A75C0000CF0000BB
++:1022800000AC5C0000CF000000AD5C0000CF00009F
++:1022900000AE5C0000CF000000AF5C0000CF00008B
++:1022A00000B45C0000CF000000B55C0000CF00006F
++:1022B00000B65C0000CF000000B75C0000CF00005B
++:1022C000000C5D0000010000000D5D000001000039
++:1022D000000E5D0000010000000F5D000001000025
++:1022E00000145D000001000000155D000001000009
++:1022F00000165D000001000000175D0000010000F5
++:10230000001C5D0000020000001D5D0000020000D6
++:10231000001E5D0000020000001F5D0000020000C2
++:1023200000245D000001000000255D0000010000A8
++:1023300000265D000001000000275D000001000094
++:10234000002C5D0000010000002D5D000001000078
++:10235000002E5D0000010000002F5D000001000064
++:1023600000345D000002000000355D000002000046
++:1023700000365D000002000000375D000002000032
++:10238000003C5D0000010000003D5D000001000018
++:10239000003E5D0000010000003F5D000001000004
++:1023A00000425D00000000803F4A5D0000000080A8
++:1023B0003F4C5D0000010000004D5D000001000089
++:1023C000004E5D0000010000004F5D0000010000B4
++:1023D00000525D00000000803F545D0000010000DD
++:1023E00000555D000001000000565D000001000086
++:1023F00000575D0000010000005A5D0000000080F1
++:102400003F5C5D0000010000005D5D000001000018
++:10241000005E5D0000010000005F5D000001000043
++:1024200000625D00000000803F645D00000100006C
++:1024300000655D000001000000665D000001000015
++:1024400000675D0000010000006A5D000000008080
++:102450003F6C5D0000010000006D5D0000010000A8
++:10246000006E5D0000010000006F5D0000010000D3
++:1024700000725D00000000803F745D0000010000FC
++:1024800000755D000001000000765D0000010000A5
++:1024900000775D0000010000007A5D000000008010
++:1024A0003F7C5D0000010000007D5D000001000038
++:1024B000007E5D0000010000007F5D000001000063
++:1024C00000825D00000000803F845D00000100008C
++:1024D00000855D000001000000865D000001000035
++:1024E00000875D0000010000008A5D0000000080A0
++:1024F0003F8C5D0000110000008D5D0000110000A8
++:10250000008E5D0000110000008F5D0000110000D2
++:1025100000925D00000000803F9A5D000000008096
++:102520003FA25D00000000803FAA5D000000008027
++:102530003FB25D00000000803FBA5D0000000080F7
++:102540003FC25D000010000000CC5D00008168AC5F
++:102550000FCD5D00008168AC0FCE5D00008168ACDE
++:102560000FCF5D00008168AC0FD45D00000F00004C
++:1025700000D55D00000F000000D65D00000F0000D8
++:1025800000D75D00000F000000145E0000603E00F8
++:1025900000155E0000603E0000165E0000603E0018
++:1025A00000175E0000603E00002C5E00001100007D
++:1025B000002D5E0000110000002E5E0000110000E2
++:1025C000002F5E000011000000345E0000010000DA
++:1025D00000355E000001000000365E0000010000D2
++:1025E00000375E000001000000445E0000040000AF
++:1025F00000455E000004000000465E00000400008C
++:1026000000475E000004000000745E00000100004E
++:1026100000755E000001000000765E000001000011
++:1026200000775E0000010000009C5E0000110000C9
++:10263000009D5E0000110000009E5E000011000081
++:10264000009F5E000011000000DC5E00008168ACAD
++:102650000FDD5E00008168AC0FDE5E00008168ACBB
++:102660000FDF5E00008168AC0FFA5E0000120C10F4
++:1026700008FC5E000011000000FD5E00001100007B
++:1026800000FE5E000011000000FF5E00001100006F
++:1026900000025F000005000000045F000001000070
++:1026A00000055F000001000000065F00000100005F
++:1026B00000075F000001000000145F00000100003F
++:1026C00000155F000001000000165F00000100001F
++:1026D00000175F0000010000001A5F000001000009
++:1026E00000245F000001000000255F0000010000E1
++:1026F00000265F000001000000275F0000010000CD
++:10270000002A5F0000FFFF0000325F0000FFFF00B3
++:1027100000345F0000FF030000355F0000FF03008E
++:1027200000365F0000FF030000375F0000FF03007A
++:10273000003A5F0000FFFF0000425F0000FFFF0063
++:1027400000445F000001000000455F000001000040
++:1027500000465F000001000000475F00000100002C
++:1027600000545F000001000000555F000001000000
++:1027700000565F000001000000575F0000010000EC
++:10278000004260000000FFFF004A6000001A0000E5
++:1027900000AC60000008000000AD60000008000010
++:1027A00000AE60000008000000AF600000080000FC
++:1027B00000B460000008000000B5600000080000E0
++:1027C00000B660000008000000B7600000080000CC
++:1027D00000BC60000008000000BD600000080000B0
++:1027E00000BE60000008000000BF6000000800009C
++:1027F00000C460000008000000C560000008000080
++:1028000000C660000008000000C76000000800006B
++:1028100000CC60000008000000CD6000000800004F
++:1028200000CE60000008000000CF6000000800003B
++:1028300000D460000008000000D56000000800001F
++:1028400000D660000008000000D76000000800000B
++:1028500000DC60000008000000DD600000080000EF
++:1028600000DE60000008000000DF600000080000DB
++:1028700000E460000008000000E5600000080000BF
++:1028800000E660000008000000E7600000080000AB
++:1028900000EC60000011000000ED6000001100007D
++:1028A00000EE60000011000000EF60000011000069
++:1028B000002C6100008168AC0F2D6100008168ACC4
++:1028C0000F2E6100008168AC0F2F6100008168ACA1
++:1028D0000F346100000004000035610000000400B6
++:1028E00000366100000004000037610000000400B1
++:1028F000003C610000000400003D61000000040095
++:10290000003E610000000400003F61000000040080
++:102910000044610000000400004561000000040064
++:102920000046610000000400004761000000040050
++:10293000004C610000000400004D61000000040034
++:10294000004E610000000400004F61000000040020
++:102950000054610000000400005561000000040004
++:1029600000566100000004000057610000000400F0
++:10297000005C610000000400005D610000000400D4
++:10298000005E610000000400005F610000000400C0
++:1029900000646100000004000065610000000400A4
++:1029A0000066610000000400006761000000040090
++:1029B0000069610000DFBFE3046C610000000400F7
++:1029C000006D610000000400006E61000000040062
++:1029D000006F6100000004000071610000DFBFE3D0
++:1029E0000474610000000300007561000000030032
++:1029F0000076610000000300007761000000030022
++:102A0000007C610000000300007D61000000030005
++:102A1000007E610000000300007F610000000300F1
++:102A200000826100000201000084610000000300D8
++:102A300000856100000003000086610000000300C3
++:102A40000087610000000300008C610000000300AB
++:102A5000008D610000000300008E61000000030093
++:102A6000008F61000000030000926100000400007C
++:102A70000094610000000300009561000000030065
++:102A80000096610000000300009761000000030051
++:102A900000996100008168AC0F9A61000004000099
++:102AA000009C610000000300009D61000000030025
++:102AB000009E610000000300009F61000000030011
++:102AC00000A261000004000000A4610000000300F7
++:102AD00000A561000000030000A6610000000300E3
++:102AE00000A761000000030000AA610000040000CC
++:102AF00000AC61000000030000AD610000000300B5
++:102B000000AE61000000030000AF610000000300A0
++:102B100000B261000004000000B461000001000088
++:102B200000B561000001000000B661000001000076
++:102B300000B761000001000000BA6100000400005D
++:102B400000BC6100000F000000BD6100000F00002C
++:102B500000BE6100000F000000BF6100000F000018
++:102B600000CA610000FF030000DA610000020100FA
++:102B700000F9610000DFBFE304FC610000200000F9
++:102B800000FD61000020000000FE61000020000048
++:102B900000FF6100002000000001620000DFBFE3D1
++:102BA0000404620000110000000562000011000032
++:102BB0000006620000110000000762000011000022
++:102BC000000C620000000100000D62000000010026
++:102BD000000E620000000100000F62000000010012
++:102BE000001C620000010000001D620000010000E6
++:102BF000001E620000010000001F620000010000D2
++:102C0000002A62000004000000326200000400009C
++:102C10000034620000400000003562000040000007
++:102C200000366200004000000037620000400000F3
++:102C3000003A620000040000003C62000000010055
++:102C4000003D620000000100003E62000000010043
++:102C5000003F62000000010000426200000400002A
++:102C6000004C620000030000004D62000003000001
++:102C7000004E620000030000004F620000030000ED
++:102C80000074620000603E000075620000603E005B
++:102C90000076620000603E000077620000603E0047
++:102CA0000094620000020000009562000002000033
++:102CB000009662000002000000976200000200001F
++:102CC000009C6200008168AC0F9D6200008168ACCE
++:102CD0000F9E6200008168AC0F9F6200008168ACAB
++:102CE0000FEC62000001000000ED62000001000036
++:102CF00000EE62000001000000EF62000001000031
++:102D000000146300000400000015630000040000CC
++:102D100000166300000400000017630000040000B8
++:102D20000024630000010000002563000001000092
++:102D3000002663000001000000276300000100007E
++:102D4000002C630000000400002D6300000004005C
++:102D5000002E630000000400002F63000000040048
++:102D6000003463000000030000356300000003002E
++:102D7000003663000000030000376300000003001A
++:102D8000003C630000011000003D630000011000E2
++:102D9000003E630000011000003F630000011000CE
++:102DA000005C630000110000005D63000011000082
++:102DB000005E630000110000005F6300001100006E
++:102DC000009C6300008168AC0F9D6300008168ACCB
++:102DD0000F9E6300008168AC0F9F6300008168ACA8
++:102DE0000FA46300000F000000A56300000F0000A7
++:102DF00000A66300000F000000A76300000F0000A2
++:102E000000B263000004000000BA63000004000088
++:102E100000CA63000010000000EA6300000408001C
++:102E200000F263000001000000FA6300001A0000D5
++:102E3000000A6400000100000012640000140C0885
++:102E40000022640000120C10082A64000004000034
++:102E50000032640000040000004264000010000022
++:102E600000626400000100000064640000603E0035
++:102E70000065640000603E000066640000603E0083
++:102E80000067640000603E00006A640000120C10DD
++:102E90000884640000110000008564000011000037
++:102EA000008664000011000000876400001100002B
++:102EB000009C640000040000009D64000004000009
++:102EC000009E640000040000009F640000040000F5
++:102ED00000A2640000FF030000AA640000140C08B4
++:102EE00000AC64000001000000AD640000010000BF
++:102EF00000AE64000001000000AF640000010000AB
++:102F000000B464000001000000B56400000100008E
++:102F100000B664000001000000B76400000100007A
++:102F200000D464000001000000D56400000100002E
++:102F300000D664000001000000D76400000100001A
++:102F400000F464000001000000F5640000010000CE
++:102F500000F664000001000000F7640000010000BA
++:102F6000000465000001000000056500000100008C
++:102F70000006650000010000000765000001000078
++:102F8000001C6500008824712A1D650000882471DA
++:102F90002A1E6500008824712A1F6500008824719C
++:102FA0002A2C65000000C085402D65000000C0850A
++:102FB000402E65000000C085402F65000000C085E0
++:102FC000403465000040000000356500004000000E
++:102FD000003665000040000000376500004000003A
++:102FE000003C650000000100003D6500000001009C
++:102FF000003E650000000100003F65000000010088
++:103000000044650000000101004565000000010169
++:103010000046650000000101004765000000010155
++:10302000004C650000000080024D6500000000803B
++:10303000024E650000000080024F65000000008025
++:1030400002CC650000DFBFE304CD650000DFBFE315
++:1030500004CE650000DFBFE304CF650000DFBFE3FF
++:1030600004D4650000DFBFE304D5650000DFBFE3E3
++:1030700004D6650000DFBFE304D7650000DFBFE3CF
++:1030800004DC65000001000000DD650000010000B7
++:1030900000DE65000001000000DF650000010000A7
++:1030A00000EC65000000FFFF00ED65000000FFFF81
++:1030B00000EE65000000FFFF00EF65000000FFFF6D
++:1030C00000F465000001000000F56500000100004B
++:1030D00000F665000001000000F765000001000037
++:1030E000000C66000000FFFF000D66000000FFFFFF
++:1030F000000E66000000FFFF000F66000000FFFFEB
++:103100000054660000010000005566000001000048
++:103110000056660000010000005766000001000034
++:103120000064660000010000006566000001000008
++:1031300000666600000100000067660000010000F4
++:10314000006C660000001020306D6600000010204A
++:10315000306E660000001020306F66000000102006
++:10316000307266000001000000746600004050608C
++:1031700070756600004050607076660000405060D8
++:103180007077660000405060707C6600008898A8E8
++:10319000B87D6600008898A8B87E6600008898A868
++:1031A000B87F6600008898A8B884660000C8D8E890
++:1031B000F885660000C8D8E8F886660000C8D8E838
++:1031C000F887660000C8D8E8F88A6600001000009A
++:1031D00000946600001A000000956600001A0000C6
++:1031E00000966600001A000000976600001A0000B2
++:1031F00000A466000004000000A5660000040000B2
++:1032000000A666000004000000A76600000400009D
++:10321000005467000004000000556700000400002F
++:10322000005667000004000000576700000400001B
++:10323000005C670000040000005D670000040000FF
++:10324000005E670000040000005F670000040000EB
++:103250000064670000808060006567000080806017
++:103260000066670000808060006767000080806003
++:10327000008C670000040000008D6700000400005F
++:10328000008E670000040000008F6700000400004B
++:1032900000A467000004000000A56700000400000F
++:1032A00000A667000004000000A7670000040000FB
++:1032B00000AC67000004000000AD670000040000DF
++:1032C00000AE67000004000000AF670000040000CB
++:1032D00000B467000080000000B5670000800000B7
++:1032E00000B667000080000000B7670000800000A3
++:1032F00000BC67000000100000BD67000000100067
++:1033000000BE67000000100000BF67000000100052
++:1033100000C467000004000000C56700000400004E
++:1033200000C667000004000000C76700000400003A
++:103330000052680000880000005A68000088000001
++:10334000007268000004000000EC68000004000047
++:1033500000ED68000004000000EE680000040000BA
++:1033600000EF68000004000000F468000080000026
++:1033700000F568000080000000F668000080000092
++:1033800000F768000080000000FC680000040000F6
++:1033900000FD68000004000000FE6800000400005A
++:1033A00000FF680000040000000469000000010242
++:1033B0000305690000000102030669000000010224
++:1033C0000307690000000102030C6900000300000C
++:1033D000000D690000030000000E690000030000FA
++:1033E000000F6900000300000014690000001000D5
++:1033F00000156900000010000016690000001000B0
++:103400000017690000001000001C690000040000A3
++:10341000001D690000040000001E69000004000097
++:10342000001F690000040000002A69000026000057
++:1034300000426900000000803F4469000004000071
++:103440000045690000040000004669000004000017
++:103450000047690000040000004C69000003000000
++:10346000004D690000030000004E690000030000E9
++:10347000004F69000003000000626900001A0000AC
++:10348000006A690000100000006C69000004000080
++:10349000006D690000040000006E69000004000077
++:1034A000006F690000040000009A6A0000520000EA
++:1034B00000AA6A000026000000BA6A0000040000AA
++:1034C00000C26A000004000000D26A00001A000076
++:1034D00000EA6A000000FFFF00FA6A000004000032
++:1034E00000026B000004000000126B00008000006E
++:1034F000001A6B000004000000226B0000140C088E
++:1035000000326B0000FF0300004A95000004000039
++:103510000052950000040000006295000080000049
++:10352000006A950000040000007295000001000090
++:103530000082950000270000009295000026000000
++:1035400000B295000000000004BA950000000000E1
++:1035500004C295000000000004CA950000000000AD
++:1035600004D295000000000004DA9500000000007D
++:1035700004E295000000000004EA9500000000004D
++:1035800004F295000000000004FA9500000000001D
++:103590000402960000000000040A960000000000EB
++:1035A0000412960000000000041A960000000000BB
++:1035B0000422960000000000042A9600000000008B
++:1035C0000452970000DFBFE3045A970000DFBFE317
++:1035D000047297000021FE010094BA00000400006C
++:1035E0000095BA00000400000096BA000004000034
++:1035F0000097BA0000040000009CBA00000300001D
++:10360000009DBA0000030000009EBA000003000005
++:10361000009FBA0000030000002CBB00000F000058
++:10362000002DBB00000F0000002EBB00000F0000AB
++:10363000002FBB00000F0000008CBB000004000046
++:10364000008DBB0000040000008EBB0000040000E1
++:10365000008FBB00000400000094BB0000FFFF00CF
++:103660000095BB0000FFFF000096BB0000FFFF00BD
++:103670000097BB0000FFFF00009CBB0000FFFF00A5
++:10368000009DBB0000FFFF00009EBB0000FFFF008D
++:10369000009FBB0000FFFF0000A4BB0000FFFF0075
++:1036A00000A5BB0000FFFF0000A6BB0000FFFF005D
++:1036B00000A7BB0000FFFF0000ACBB0000FFFF0045
++:1036C00000ADBB0000FFFF0000AEBB0000FFFF002D
++:1036D00000AFBB0000FFFF0000F4BB0000010000D2
++:1036E00000F5BB000001000000F6BB000001000077
++:1036F00000F7BB00000100000014BC000001000046
++:103700000015BC00000100000016BC000001000014
++:103710000017BC00000100000044BC0000010000D4
++:103720000045BC00000100000046BC000001000094
++:103730000047BC000001000000ACBC00000100001C
++:1037400000ADBC000001000000AEBC0000010000A4
++:1037500000AFBC000001000000B4BC00000100008C
++:1037600000B5BC000001000000B6BC000001000074
++:1037700000B7BC000001000000BCBC00000200005B
++:1037800000BDBC000002000000BEBC000002000042
++:1037900000BFBC000002000000C4BC00000100002B
++:1037A00000C5BC000001000000C6BC000001000014
++:1037B00000C7BC000001000000CCBC0000010000FC
++:1037C00000CDBC000001000000CEBC0000010000E4
++:1037D00000CFBC000001000000D4BC0000020000CB
++:1037E00000D5BC000002000000D6BC0000020000B2
++:1037F00000D7BC000002000000DCBC00000100009B
++:1038000000DDBC000001000000DEBC000001000083
++:1038100000DFBC000001000000ECBC000011000053
++:1038200000EDBC000011000000EEBC000011000023
++:1038300000EFBC0000110000002CBD00008168AC4E
++:103840000F2DBD00008168AC0F2EBD00008168AC5B
++:103850000F2FBD00008168AC0F44BD0000040000C4
++:103860000045BD00000400000046BD00000400004B
++:103870000047BD00000400000064BD00001100000E
++:103880000065BD00001100000066BD0000110000D1
++:103890000067BD0000110000006CBD0000010000C9
++:1038A000006DBD0000010000006EBD0000010000C1
++:1038B000006FBD0000010000007CBD0000CF0000D3
++:1038C000007DBD0000CF0000007EBD0000CF0000E5
++:1038D000007FBD0000CF00000084BD0000CF0000CD
++:1038E0000085BD0000CF00000086BD0000CF0000B5
++:1038F0000087BD0000CF0000008CBD0000CF00009D
++:10390000008DBD0000CF0000008EBD0000CF000084
++:10391000008FBD0000CF000000E4BD0000010000EA
++:1039200000E5BD000001000000E6BD000001000050
++:1039300000E7BD000001000000ECBD000001000038
++:1039400000EDBD000001000000EEBD000001000020
++:1039500000EFBD000001000000F4BD000002000007
++:1039600000F5BD000002000000F6BD0000020000EE
++:1039700000F7BD000002000000FCBD0000010000D7
++:1039800000FDBD000001000000FEBD0000010000C0
++:1039900000FFBD00000100000004BE0000010000A7
++:1039A0000005BE00000100000006BE00000100008E
++:1039B0000007BE0000010000000CBE000002000075
++:1039C000000DBE0000020000000EBE00000200005C
++:1039D000000FBE00000200000014BE000001000045
++:1039E0000015BE00000100000016BE00000100002E
++:1039F0000017BE00000100000024BE00000100000E
++:103A00000025BE00000100000026BE0000010000ED
++:103A10000027BE0000010000002CBE0000010000D5
++:103A2000002DBE0000010000002EBE0000010000BD
++:103A3000002FBE00000100000034BE0000010000A5
++:103A40000035BE00000100000036BE00000100008D
++:103A50000037BE0000010000003CBE000001000075
++:103A6000003DBE0000010000003EBE00000100005D
++:103A7000003FBE00000100000044BE000001000045
++:103A80000045BE00000100000046BE00000100002D
++:103A90000047BE0000010000004CBE000001000015
++:103AA000004DBE0000010000004EBE0000010000FD
++:103AB000004FBE00000100000054BE0000010000E5
++:103AC0000055BE00000100000056BE0000010000CD
++:103AD0000057BE0000010000005CBE0000010000B5
++:103AE000005DBE0000010000005EBE00000100009D
++:103AF000005FBE00000100000064BE000011000075
++:103B00000065BE00001100000066BE00001100004C
++:103B10000067BE000011000000A4BE00008168AC78
++:103B20000FA5BE00008168AC0FA6BE00008168AC86
++:103B30000FA7BE00008168AC0FACBE00000F0000F4
++:103B400000ADBE00000F000000AEBE00000F000080
++:103B500000AFBE00000F000000ECBE0000603E00A1
++:103B600000EDBE0000603E0000EEBE0000603E00C2
++:103B700000EFBE0000603E000004BF000011000026
++:103B80000005BF00001100000006BF00001100008A
++:103B90000007BF0000110000000CBF000001000082
++:103BA000000DBF0000010000000EBF00000100007A
++:103BB000000FBF0000010000001CBF000004000057
++:103BC000001DBF0000040000001EBF000004000034
++:103BD000001FBF0000040000004CBF0000010000F7
++:103BE000004DBF0000010000004EBF0000010000BA
++:103BF000004FBF00000100000074BF000011000072
++:103C00000075BF00001100000076BF000011000029
++:103C10000077BF000011000000B4BF00008168AC55
++:103C20000FB5BF00008168AC0FB6BF00008168AC63
++:103C30000FB7BF00008168AC0FD4BF0000110000B7
++:103C400000D5BF000011000000D6BF000011000029
++:103C500000D7BF000011000000DCBF000001000021
++:103C600000DDBF000001000000DEBF000001000019
++:103C700000DFBF000001000000ECBF0000010000F9
++:103C800000EDBF000001000000EEBF0000010000D9
++:103C900000EFBF000001000000FCBF0000010000B9
++:103CA00000FDBF000001000000FEBF000001000099
++:103CB00000FFBF0000010000000CC00000FF030077
++:103CC000000DC00000FF0300000EC00000FF030055
++:103CD000000FC00000FF0300001CC0000001000036
++:103CE000001DC00000010000001EC0000001000017
++:103CF000001FC00000010000002CC00000010000F7
++:103D0000002DC00000010000002EC00000010000D6
++:103D1000002FC000000100000084C1000008000066
++:103D20000085C100000800000086C10000080000F6
++:103D30000087C10000080000008CC10000080000DE
++:103D4000008DC10000080000008EC10000080000C6
++:103D5000008FC100000800000094C10000080000AE
++:103D60000095C100000800000096C1000008000096
++:103D70000097C10000080000009CC100000800007E
++:103D8000009DC10000080000009EC1000008000066
++:103D9000009FC1000008000000A4C100000800004E
++:103DA00000A5C1000008000000A6C1000008000036
++:103DB00000A7C1000008000000ACC100000800001E
++:103DC00000ADC1000008000000AEC1000008000006
++:103DD00000AFC1000008000000B4C10000080000EE
++:103DE00000B5C1000008000000B6C10000080000D6
++:103DF00000B7C1000008000000BCC10000080000BE
++:103E000000BDC1000008000000BEC10000080000A5
++:103E100000BFC1000008000000C4C1000011000084
++:103E200000C5C1000011000000C6C1000011000063
++:103E300000C7C100001100000004C200008168AC8E
++:103E40000F05C200008168AC0F06C200008168AC9B
++:103E50000F07C200008168AC0F0CC2000000040014
++:103E6000000DC20000000400000EC20000000400AB
++:103E7000000FC200000004000014C2000000040093
++:103E80000015C200000004000016C200000004007B
++:103E90000017C20000000400001CC2000000040063
++:103EA000001DC20000000400001EC200000004004B
++:103EB000001FC200000004000024C2000000040033
++:103EC0000025C200000004000026C200000004001B
++:103ED0000027C20000000400002CC2000000040003
++:103EE000002DC20000000400002EC20000000400EB
++:103EF000002FC200000004000034C20000000400D3
++:103F00000035C200000004000036C20000000400BA
++:103F10000037C20000000400003CC20000000400A2
++:103F2000003DC20000000400003EC200000004008A
++:103F3000003FC200000004000044C2000000040072
++:103F40000045C200000004000046C200000004005A
++:103F50000047C20000000400004CC2000000030043
++:103F6000004DC20000000300004EC200000003002C
++:103F7000004FC200000003000054C2000000030014
++:103F80000055C200000003000056C20000000300FC
++:103F90000057C20000000300005CC20000000300E4
++:103FA000005DC20000000300005EC20000000300CC
++:103FB000005FC200000003000064C20000000300B4
++:103FC0000065C200000003000066C200000003009C
++:103FD0000067C20000000300006CC2000000030084
++:103FE000006DC20000000300006EC200000003006C
++:103FF000006FC200000003000074C2000000030054
++:104000000075C200000003000076C200000003003B
++:104010000077C20000000300007CC2000000030023
++:10402000007DC20000000300007EC200000003000B
++:10403000007FC200000003000084C20000000300F3
++:104040000085C200000003000086C20000000300DB
++:104050000087C20000000300008CC20000010000C5
++:10406000008DC20000010000008EC20000010000AF
++:10407000008FC200000100000094C200000F000089
++:104080000095C200000F00000096C200000F000063
++:104090000097C200000F000000D4C2000020000002
++:1040A00000D5C2000020000000D6C20000200000A1
++:1040B00000D7C2000020000000DCC2000011000098
++:1040C00000DDC2000011000000DEC200001100008F
++:1040D00000DFC2000011000000E4C2000000010087
++:1040E00000E5C2000000010000E6C200000001007F
++:1040F00000E7C2000000010000F4C200000100005F
++:1041000000F5C2000001000000F6C200000100003E
++:1041100000F7C20000010000000CC30000400000D6
++:10412000000DC30000400000000EC300004000006E
++:10413000000FC300004000000014C3000000010095
++:104140000015C300000001000016C30000000100BC
++:104150000017C300000001000024C300000300009A
++:104160000025C300000300000026C3000003000078
++:104170000027C30000030000004CC30000603E00A5
++:10418000004DC30000603E00004EC30000603E00D2
++:10419000004FC30000603E00006CC300000200003E
++:1041A000006DC30000020000006EC30000020000AA
++:1041B000006FC300000200000074C300008168ACFF
++:1041C0000F75C300008168AC0F76C300008168AC36
++:1041D0000F77C300008168AC0FC4C300000100006A
++:1041E00000C5C3000001000000C6C30000010000BC
++:1041F00000C7C3000001000000ECC3000004000081
++:1042000000EDC3000004000000EEC3000004000045
++:1042100000EFC3000004000000FCC3000001000028
++:1042200000FDC3000001000000FEC300000100000B
++:1042300000FFC300000100000004C40000000400EF
++:104240000005C400000004000006C40000000400D3
++:104250000007C40000000400000CC40000000300BC
++:10426000000DC40000000300000EC40000000300A5
++:10427000000FC400000003000014C400000110007F
++:104280000015C400000110000016C4000001100059
++:104290000017C400000110000034C4000011000029
++:1042A0000035C400001100000036C40000110000F9
++:1042B0000037C400001100000074C400008168AC25
++:1042C0000F75C400008168AC0F76C400008168AC33
++:1042D0000F77C400008168AC0F7CC400000F0000A1
++:1042E000007DC400000F0000007EC400000F00002D
++:1042F000007FC400000F0000003CC50000603E00CD
++:10430000003DC50000603E00003EC50000603E006C
++:10431000003FC50000603E00005CC50000110000C9
++:10432000005DC50000110000005EC5000011000026
++:10433000005FC500001100000074C500000400000B
++:104340000075C500000400000076C50000040000F0
++:104350000077C500000400000084C50000010000D3
++:104360000085C500000100000086C50000010000B6
++:104370000087C50000010000008CC500000100009E
++:10438000008DC50000010000008EC5000001000086
++:10439000008FC5000001000000ACC5000001000056
++:1043A00000ADC5000001000000AEC5000001000026
++:1043B00000AFC5000001000000CCC50000010000F6
++:1043C00000CDC5000001000000CEC50000010000C6
++:1043D00000CFC5000001000000DCC50000010000A6
++:1043E00000DDC5000001000000DEC5000001000086
++:1043F00000DFC5000001000000F4C5000088247142
++:104400002AF5C500008824712AF6C50000882471A9
++:104410002AF7C500008824712A04C6000000C08560
++:104420004005C6000000C0854006C6000000C085EB
++:104430004007C6000000C085400CC60000400000D8
++:10444000000DC60000400000000EC6000040000045
++:10445000000FC600004000000014C600000001006C
++:104460000015C600000001000016C6000000010093
++:104470000017C60000000100001CC600000001017A
++:10448000001DC60000000101001EC6000000010161
++:10449000001FC600000001010024C60000000080CB
++:1044A0000225C600000000800226C6000000008031
++:1044B0000227C6000000008002A4C60000DFBFE3A0
++:1044C00004A5C60000DFBFE304A6C60000DFBFE30B
++:1044D00004A7C60000DFBFE304ACC60000DFBFE3F3
++:1044E00004ADC60000DFBFE304AEC60000DFBFE3DB
++:1044F00004AFC60000DFBFE304B4C6000001000043
++:1045000000B5C6000001000000B6C60000010000B2
++:1045100000B7C6000001000000C4C6000000FFFF95
++:1045200000C5C6000000FFFF00C6C6000000FFFF78
++:1045300000C7C6000000FFFF00CCC600000100005D
++:1045400000CDC6000001000000CEC6000001000042
++:1045500000CFC6000001000000E4C6000000FFFF1D
++:1045600000E5C6000000FFFF00E6C6000000FFFFF8
++:1045700000E7C6000000FFFF002CC700000100009C
++:10458000002DC70000010000002EC7000001000040
++:10459000002FC70000010000003CC7000001000020
++:1045A000003DC70000010000003EC7000001000000
++:1045B000003FC700000100000044C70000001020B9
++:1045C0003045C700000010203046C7000000102012
++:1045D0003047C70000001020304CC700004050603A
++:1045E000704DC70000405060704EC70000405060E2
++:1045F000704FC700004050607054C700008898A8F2
++:10460000B855C700008898A8B856C700008898A871
++:10461000B857C700008898A8B85CC70000C8D8E899
++:10462000F85DC70000C8D8E8F85EC70000C8D8E841
++:10463000F85FC70000C8D8E8F86CC700001A00008F
++:10464000006DC700001A0000006EC700001A0000CD
++:10465000006FC700001A000000E013010004000012
++:1046600000E813010004000000F0130100120C1018
++:104670000800140100120C100810140100140C089A
++:1046800000181401000100000020140100140C089F
++:104690000038140100120C1008401401002700001B
++:1046A0000058140100010000006832010001000000
++:1046B0000020330100120C100880330100000000BC
++:1046C00004883301000000000498330100800000DA
++:1046D00000B833010080000000C83301003F000033
++:1046E0000020340100020000002834010000000016
++:1046F000043034010000000004783401000400009C
++:104700000098340100040000002035010001000081
++:1047100000283501000110000030350100FFFF00C6
++:104720000038350100FFFF000040350100FFFF00A9
++:104730000048350100FFFF000050360100000080F6
++:104740003F583601000000803F60360100000080C5
++:104750003F683601000000803F7036010000008095
++:104760003F783601000000803F8036010000008065
++:104770003F883601000000803F9036010000008035
++:104780003F983601000000803FA036010000008005
++:104790003FA83601000000803FB0360100000080D5
++:1047A0003FB83601000000803FC0360100000080A5
++:1047B0003FC83601000000803FD0360100100000E5
++:1047C00000E03601000300000000370100120C1069
++:1047D0000809370100800000000A3701008000004E
++:1047E000000B370100800000000C37010080000042
++:1047F000000D370100800000000E3701008000002E
++:10480000000F370100800000001037010080000019
++:10481000001137010004700080123701000470009D
++:104820008013370100047000801437010004700009
++:1048300080153701000470008016370100047000F5
++:1048400080173701000470008018370100047000E1
++:104850008019370100000400041A37010000040029
++:10486000041B370100000400041C37010000040091
++:10487000041D370100000400041E3701000004007D
++:10488000041F370100000400042037010000040069
++:104890000421370100001000002237010000100041
++:1048A0000023370100001000002437010000100031
++:1048B000002537010000100000263701000010001D
++:1048C0000027370100001000002837010000100009
++:1048D0000039370100010000003A370100010000F3
++:1048E000003B370100010000003C370100010000DF
++:1048F000003D370100010000003E370100010000CB
++:10490000003F3701000100000040370100010000B6
++:104910000051370100001000005237010000100064
++:104920000053370100001000005437010000100050
++:10493000005537010000100000563701000010003C
++:104940000057370100001000005837010000100028
++:104950000059370100001000005A37010000100014
++:10496000005B370100001000005C37010000100000
++:10497000005D370100001000005E370100001000EC
++:10498000005F3701000010000060370100001000D8
++:1049900000613701000100000062370100010000E2
++:1049A00000633701000100000064370100010000CE
++:1049B00000653701000100000066370100010000BA
++:1049C00000673701000100000068370100010000A6
++:1049D000007137010004000000723701000400007C
++:1049E0000073370100040000007437010004000068
++:1049F0000075370100040000007637010004000054
++:104A0000007737010004000000783701000400003F
++:104A10000079370100020000007A3701000200002F
++:104A2000007B370100020000007C3701000200001B
++:104A3000007D370100020000007E37010002000007
++:104A4000007F3701000200000080370100020000F3
++:104A500000D937010080000000DA37010080000033
++:104A600000DB37010080000000DC3701008000001F
++:104A700000DD37010080000000DE3701008000000B
++:104A800000DF37010080000000E0370100800000F7
++:104A900000E137010004700080E23701000470007B
++:104AA00080E337010004700080E4370100047000E7
++:104AB00080E537010004700080E6370100047000D3
++:104AC00080E737010004700080E8370100047000BF
++:104AD00080E937010000040004EA37010000040007
++:104AE00004EB37010000040004EC3701000004006F
++:104AF00004ED37010000040004EE3701000004005B
++:104B000004EF37010000040004F037010000040046
++:104B100004F137010000100000F23701000010001E
++:104B200000F337010000100000F43701000010000E
++:104B300000F537010000100000F6370100001000FA
++:104B400000F737010000100000F8370100001000E6
++:104B50000009380100010000000A380100010000CE
++:104B6000000B380100010000000C380100010000BA
++:104B7000000D380100010000000E380100010000A6
++:104B8000000F380100010000001038010001000092
++:104B90000021380100001000002238010000100040
++:104BA000002338010000100000243801000010002C
++:104BB0000025380100001000002638010000100018
++:104BC0000027380100001000002838010000100004
++:104BD0000029380100001000002A380100001000F0
++:104BE000002B380100001000002C380100001000DC
++:104BF000002D380100001000002E380100001000C8
++:104C0000002F3801000010000030380100001000B3
++:104C100000313801000100000032380100010000BD
++:104C200000333801000100000034380100010000A9
++:104C30000035380100010000003638010001000095
++:104C40000037380100010000003838010001000081
++:104C50000041380100040000004238010004000057
++:104C60000043380100040000004438010004000043
++:104C7000004538010004000000463801000400002F
++:104C8000004738010004000000483801000400001B
++:104C90000049380100020000004A3801000200000B
++:104CA000004B380100020000004C380100020000F7
++:104CB000004D380100020000004E380100020000E3
++:104CC000004F3801000200000050380100020000CF
++:104CD00000A1380100120C1008A2380100120C10BB
++:104CE00008A3380100120C1008A4380100120C109F
++:104CF00008A5380100120C1008A6380100120C108B
++:104D000008A7380100120C1008A8380100120C1076
++:104D100008D9380100FFFF0000DA380100FFFF006A
++:104D200000DB380100FFFF0000DC380100FFFF005E
++:104D300000DD380100FFFF0000DE380100FFFF004A
++:104D400000DF380100FFFF0000E0380100FFFF0036
++:104D500000E1380100FFFF0000E2380100FFFF0022
++:104D600000E3380100FFFF0000E4380100FFFF000E
++:104D700000E5380100FFFF0000E6380100FFFF00FA
++:104D800000E7380100FFFF0000E8380100FFFF00E6
++:104D900000E9380100FFFF0000EA380100FFFF00D2
++:104DA00000EB380100FFFF0000EC380100FFFF00BE
++:104DB00000ED380100FFFF0000EE380100FFFF00AA
++:104DC00000EF380100FFFF0000F0380100FFFF0096
++:104DD00000F1380100FFFF0000F2380100FFFF0082
++:104DE00000F3380100FFFF0000F4380100FFFF006E
++:104DF00000F5380100FFFF0000F6380100FFFF005A
++:104E000000F7380100FFFF0000F8380100FFFF0045
++:104E100000F938010001000000FA3801000100002B
++:104E200000FB38010001000000FC38010001000017
++:104E300000FD38010001000000FE38010001000003
++:104E400000FF3801000100000000390100010000EE
++:104E500000013901000100010002390100010001D7
++:104E600000033901000100010004390100010001C3
++:104E700000053901000100010006390100010001AF
++:104E8000000739010001000100083901000100019B
++:104E90000009390100010001000A39010001000187
++:104EA000000B390100010001000C39010001000173
++:104EB000000D390100010001000E3901000100015F
++:104EC000000F39010001000100103901000100014B
++:104ED0000011390100010000001239010001000039
++:104EE0000013390100010000001439010001000025
++:104EF0000015390100010000001639010001000011
++:104F000000173901000100000018390100010000FC
++:104F1000002139010021FE01002239010021FE019A
++:104F2000002339010021FE01002439010021FE0186
++:104F3000002539010021FE01002639010021FE0172
++:104F4000002739010021FE01002839010021FE015E
++:104F50000051390100120C100852390100120C10D6
++:104F60000853390100120C100854390100120C10BA
++:104F70000855390100120C100856390100120C10A6
++:104F80000857390100120C100858390100120C1092
++:104F90000859390100040000005A390100040000DA
++:104FA000005B390100040000005C390100040000CE
++:104FB000005D390100040000005E390100040000BA
++:104FC000005F3901000400000060390100040000A6
++:104FD0000069390100020000006A39010002000086
++:104FE000006B390100020000006C39010002000072
++:104FF000006D390100020000006E3901000200005E
++:10500000006F390100020000007039010002000049
++:105010000071390100110000007239010011000017
++:105020000073390100110000007439010011000003
++:1050300000753901001100000076390100110000EF
++:1050400000773901001100000078390100110000DB
++:1050500000B93901008168AC0FBA3901008168AC30
++:105060000FBB3901008168AC0FBC3901008168AC0D
++:105070000FBD3901008168AC0FBE3901008168ACF9
++:105080000FBF3901008168AC0FC03901008168ACE5
++:105090000FE139010004000000E2390100040000C2
++:1050A00000E339010004000000E4390100040000BD
++:1050B00000E539010004000000E6390100040000A9
++:1050C00000E739010004000000E839010004000095
++:1050D00000313A010002000000323A0100020000F3
++:1050E00000333A010002000000343A0100020000DF
++:1050F00000353A010002000000363A0100020000CB
++:1051000000373A010002000000383A0100020000B6
++:1051100000393A0100010000003A3A0100010000A4
++:10512000003B3A0100010000003C3A010001000090
++:10513000003D3A0100010000003E3A01000100007C
++:10514000003F3A010001000000403A010001000068
++:1051500000413A010001000000423A010001000054
++:1051600000433A010001000000443A010001000040
++:1051700000453A010001000000463A01000100002C
++:1051800000473A010001000000483A010001000018
++:1051900000493A0100020000004A3A010002000002
++:1051A000004B3A0100020000004C3A0100020000EE
++:1051B000004D3A0100020000004E3A0100020000DA
++:1051C000004F3A010002000000503A0100020000C6
++:1051D00000513A010001000000523A0100010000B4
++:1051E00000533A010001000000543A0100010000A0
++:1051F00000553A010001000000563A01000100008C
++:1052000000573A010001000000583A010001000077
++:1052100000593A0100010000005A3A010001000063
++:10522000005B3A0100010000005C3A01000100004F
++:10523000005D3A0100010000005E3A01000100003B
++:10524000005F3A010001000000603A010001000027
++:1052500000613A010001000000623A010001000013
++:1052600000633A010001000000643A0100010000FF
++:1052700000653A010001000000663A0100010000EB
++:1052800000673A010001000000683A0100010000D7
++:1052900000713A010004000000723A0100040000AD
++:1052A00000733A010004000000743A010004000099
++:1052B00000753A010004000000763A010004000085
++:1052C00000773A010004000000783A010004000071
++:1052D0000079570100110000007A57010011000009
++:1052E000007B570100110000007C570100110000F5
++:1052F000007D570100110000007E570100110000E1
++:10530000007F5701001100000080570100110000CC
++:105310000089570100010000008A570100010000C8
++:10532000008B570100010000008C570100010000B4
++:10533000008D570100010000008E570100010000A0
++:10534000008F57010001000000905701000100008C
++:01535000005C
++:00000001FF
+diff --git a/firmware/nouveau/nv84.ctxprog.ihex b/firmware/nouveau/nv84.ctxprog.ihex
+new file mode 100644
+index 0000000..3152784
+--- /dev/null
++++ b/firmware/nouveau/nv84.ctxprog.ihex
+@@ -0,0 +1,91 @@
++:100000004E5643500066018E0070009C0070002028
++:10001000002000080060004C005000890E400000E5
++:100020000020000700600000003000FF00C000005A
++:10003000002000FF008000090070004D6341004473
++:10004000294000052940000D294000063E410005D9
++:10005000006000C5154000110060000B274000C57E
++:100060002140000000700081007000040060004A20
++:10007000005000406F2100070060000128C0002EE2
++:1000800000200001008000CB005000FFFF9000FF27
++:10009000FF910020002000080060004C0050000983
++:1000A000006000453E41004D5941009D007000CF69
++:1000B0002D40009F0070009F005000C02A400000AB
++:1000C000022000080060004F2A4000C02A4000CCF7
++:1000D0003040008100700000002000060060000039
++:1000E000007000FC1B110083007000000030004015
++:1000F0006F210007006000010BC0001E00200001FE
++:10010000008000CB005000FF00C000800070008322
++:1001100000700047002000060060000A0211008005
++:100120000420000700600000003000FF00C000FF56
++:1001300000C8000749410016292000FF0080008CFC
++:10014000504000CB0050003F02A0004000200006BD
++:100150000060000F007000020217000A0211003256
++:100160000020000D02100042021C000203120002D7
++:1001700004140000051800090513005005150005BA
++:100180000611000F002000070610000007110000F4
++:1001900009110002091200000A1100020B160028C2
++:1001A0000B12002B0B1400010C11000014110005A0
++:1001B00014110007141100091411000B141100CBC5
++:1001C000002000001510000F7940004B79400040DE
++:1001D0004D2100070060003E042000FF0088008FD2
++:1001E0000070008C794000CB005000000000002B14
++:1001F000002000051A1000001C1300041C1200202F
++:100200001C1400251C1100401C1300441C1200602B
++:100210001C1400651C1100801C1300841C1200A01B
++:100220001C1400A51C1100C01C1300C41C1200E00B
++:100230001C1400E51C1100001F1300401F1900E0F2
++:10024000A14000ED01200006006000440020008075
++:10025000201000C6201100C9201500D02019000070
++:100260002112000321120000221600072212008032
++:100270002211000023110002231100802312008BA1
++:10028000231100942311009C231100E1BE4000546F
++:100290000220000600600044002000802410000FAF
++:1002A000AF40004BAF4000404D2100070060003ED2
++:1002B000042000FF0088008F0070008CAF4000CB4E
++:1002C00000500000000000C6241100C9241500D011
++:1002D000241900002512000325120000261600072D
++:1002E000261200802611000027110002271100802D
++:1002F0002712008B271100942711009C271100E280
++:10030000D14000BB022000060060004400200080B5
++:10031000281000C6281100C9281500D0281900008F
++:1003200029120003291200002A1600072A12008051
++:100330002A1100002B1100022B1100802B12008BC0
++:100340002B1100942B11009C2B1100E3EE40002296
++:100350000320000600600044002000802C10000FE5
++:10036000DF40004BDF4000404D2100070060003EB1
++:10037000042000FF0088008F0070008CDF4000CB5D
++:1003800000500000000000C62C1100C92C1500D040
++:100390002C1900002D1200032D1200002E1600074C
++:1003A0002E1200802E1100002F1100022F1100804C
++:1003B0002F12008B2F1100942F11009C2F1100E49D
++:1003C00001410089032000060060004400200080F5
++:1003D000301000C6301100C9301500D030190000AF
++:1003E0003112000331120000321600073212008071
++:1003F0003211000033110002331100803312008BE0
++:10040000331100943311009C331100E51E4100F0BC
++:100410000320000600600044002000803410000F1C
++:100420000F41004B0F4100404D2100070060003E8E
++:10043000042000FF0088008F0070008C0F4100CB6B
++:1004400000500000000000C6341100C9341500D06F
++:10045000341900003512000335120000361600076B
++:10046000361200803611000037110002371100806B
++:100470003712008B371100943711009C37110000A0
++:100480000000000F254100CB005000404D21000727
++:100490000060003E042000FF008800CB0050008771
++:1004A0002841000A0060000000000000374100A061
++:1004B00000700080007000800420000700600004CD
++:1004C000002000FF00C000FF008000CB00500000B3
++:1004D0000070000000200006006000FE1B11004DAF
++:1004E000594100000070000000200006006000FE7E
++:1004F0001B1100800070001D0070004D1140008134
++:10050000007000040060004A005000884341000B66
++:100510000060000000200006006000000070000B7A
++:10052000594100FD1B11004D42400016292000FDDD
++:10053000008000CB0050000200C0008004200007B3
++:100540000060006001200002008000CB005000022B
++:1005500018C000B627200002008000CB0050004DDC
++:100560004E40000B0060004D57410001007000CF6D
++:1005700000500003007000065E4100055F41000D61
++:10058000006000050070000D007000060070000B98
++:0F0590000070000E0070001C0070000C00600076
++:00000001FF
+diff --git a/firmware/nouveau/nv84.ctxvals.ihex b/firmware/nouveau/nv84.ctxvals.ihex
+new file mode 100644
+index 0000000..6b69a50
+--- /dev/null
++++ b/firmware/nouveau/nv84.ctxvals.ihex
+@@ -0,0 +1,557 @@
++:100000004E564356005504000043000000300000E7
++:10001000004C00000002000000750000000300001A
++:10002000007600000000100000860000000CFE00BA
++:10003000008B000000001000009600000087010007
++:10004000009B000000181000009C000000FF000052
++:1000500000AB00000004000000AC000000DF004D19
++:1000600004AE00000000060000B400000000000024
++:1000700001B5000000FF000000B700000000040010
++:1000800000BD00000001000000BE00000080000E66
++:1000900000BF00000004000000C6000000020000D5
++:1000A00000C700000001000000CA000000010000BD
++:1000B00000CB00000000010000D1000000020000A1
++:1000C00000D200000001000000D300000001000089
++:1000D00000D700000001000000D8000000FFFF3F33
++:1000E00000D9000000FF1F0000DB0000000100003D
++:1000F00000DC00000001000000DE00000001000044
++:1001000000DF00000001000000E00000000100002E
++:1001100000E100000004000000E200000001000017
++:1001200000E300000001000000E400000001000006
++:1001300000E500000007000000E6000000010000EC
++:1001400000E700000007000000E8000000010000D8
++:1001500000E900000001000000EA000000010000CA
++:1001600000EF00000001000000F0000000000100AE
++:1001700000F200000001000000F500000000010096
++:1001800000F600000001000000F700000000010080
++:1001900000F900000001000000FC00000000010068
++:1001A00000010100000400000002010000700000D6
++:1001B000000301000080000000080100000C0000A6
++:1001C000000A010000080000000B010000140000FC
++:1001D000000D010000290000000E010000270000B2
++:1001E000000F0100002600000010010000080000C0
++:1001F00000110100000400000012010000270000AF
++:1002000000150100000100000016010000020000BE
++:1002100000170100000300000018010000040000A6
++:100220000019010000050000001A0100000600008E
++:10023000001B010000070000001C0100000100007D
++:10024000002D010000CF00000039010000800000F7
++:10025000003A010000040000003B0100000400001F
++:10026000003C010000030000003D0100000100000F
++:1002700000400100001200000041010000100000D9
++:1002800000420100000C00000043010000010000DA
++:1002900000470100000400000048010000020000C7
++:1002A0000049010000040000004C010000FFFF3F76
++:1002B000004D010000FF1F00005701000004000076
++:1002C0000058010000140000005901000001000066
++:1002D000005C010000020000005F0100000100005E
++:1002E0000061010000020000006201000000100037
++:1002F0000063010000000E00006401000000100017
++:100300000065010000001E00006701000001000000
++:100310000068010000010000006901000001000008
++:10032000006A010000010000006B010000010000F4
++:10033000006F0100000002000071010000010000D8
++:1003400000720100007000000073010000800000D6
++:10035000007601000001000000770100007000003D
++:100360000078010000800000007C01000001000016
++:10037000007D010000CF0000007F010000010000AF
++:100380000083010000CF0000008501000002000092
++:100390000087010000010000008901000001000049
++:1003A000008B010000CF0000008C010000CF000096
++:1003B000008D010000010000008F010000800F008F
++:1003C00000A101000080007F00B001000080007FDC
++:1003D00000B901000021F8743BBA0100000180055A
++:1003E00089BC01000000100000BD0100001F0000DA
++:1003F00000BE010000FA107C02BF010000C0000036
++:1004000040C0010000802089B7C301000021F874BA
++:100410003BC401000001800589C6010000001000F6
++:1004200000C70100001F000000C8010000FA107C96
++:1004300002C9010000C0000040CA010000802089FC
++:10044000B7CD01000021F8743BCE0100000180050A
++:1004500089D001000000100000D10100001F000041
++:1004600000D2010000FA107C02D3010000C000009D
++:1004700040D4010000802089B7D701000021F87422
++:100480003BD801000001800589DA0100000010005E
++:1004900000DB0100001F000000DC010000FA107CFE
++:1004A00002DD010000C0000040DE01000080208964
++:1004B000B7E101000040000100E30100002200005C
++:1004C00000E601000040000100E7010000220000FA
++:1004D00000ED01000000008001EE010000000016A8
++:1004E00000EF01000000008001F3010000FFFF03A6
++:1004F00000F401000000008800FD0100000104017B
++:1005000000FF0100007800000001020000BF0000B1
++:10051000000302000010120000040200008000002E
++:10052000080D020000000080010E0200000000160D
++:10053000000F0200000000800113020000FFFF0313
++:100540000014020000000088001D020000010401E8
++:10055000001F0200007800000021020000BF000020
++:10056000002302000010120000240200008000009E
++:10057000082E0200007070020031020000FFFFFF31
++:1005800003370200000704120038020000071509B3
++:100590000539020000020210053A020000010203C0
++:1005A000004102000040000000420200000A0B0C63
++:1005B0000D430200001012140044020000F001007C
++:1005C0000045020000010000004602000003000098
++:1005D0000049020000009E03004A020000000100E2
++:1005E000004B020000003800004C02000040404078
++:1005F000004D0200000AFF00004F02000005F077E6
++:100600000050020000FF7F3F005402000000008005
++:100610000155020000000016005602000000008094
++:10062000015A020000FFFF03005B02000000008887
++:10063000006402000001040100660200007800006E
++:100640000068020000BF0000006A020000101200F3
++:10065000006B0200008000000874020000000080AF
++:100660000175020000000016007602000000008004
++:10067000017A020000FFFF03007B020000000088F7
++:1006800000840200000104010086020000780000DE
++:100690000088020000BF0000008A02000010120063
++:1006A000008B0200008000000895020000707002BC
++:1006B0000098020000FFFFFF039E020000070412E3
++:1006C000009F02000007150905A0020000020210A9
++:1006D00005A102000001020300A802000040000082
++:1006E00000A90200000A0B0C0DAA0200001012144F
++:1006F00000AB020000F0010000AC020000010000AD
++:1007000000AD02000003000000B0020000009E03E4
++:1007100000B102000000010000B202000000380039
++:1007200000B302000040404000B40200000AFF0095
++:1007300000B602000005F07700B7020000FF7F3F1F
++:1007400000BB02000000008001BC02000000001697
++:1007500000BD02000000008001C1020000FFFF0395
++:1007600000C202000000008800CB0200000104016A
++:1007700000CD02000078000000CF020000BF0000A2
++:1007800000D102000010120000D202000080000020
++:1007900008DB02000000008001DC020000000016FF
++:1007A00000DD02000000008001E1020000FFFF0305
++:1007B00000E202000000008800EB020000010401DA
++:1007C00000ED02000078000000EF020000BF000012
++:1007D00000F102000010120000F202000080000090
++:1007E00008FC02000070700200FF020000FFFFFF23
++:1007F00003050300000704120006030000071509A3
++:1008000005070300000202100508030000010203AF
++:10081000000F03000040000000100300000A0B0C52
++:100820000D110300001012140012030000F001006B
++:100830000013030000010000001403000003000087
++:100840000017030000009E030018030000000100D1
++:100850000019030000003800001A03000040404067
++:10086000001B0300000AFF00001D03000005F077D5
++:10087000001E030000FF7F3F0022030000000080F5
++:100880000123030000000016002403000000008084
++:100890000128030000FFFF03002903000000008877
++:1008A000003203000001040100340300007800005E
++:1008B0000036030000BF00000038030000101200E3
++:1008C000003903000080000008420300000000809F
++:1008D00001430300000000160044030000000080F4
++:1008E0000148030000FFFF030049030000000088E7
++:1008F00000520300000104010054030000780000CE
++:100900000056030000BF0000005803000010120052
++:1009100000590300008000000863030000707002AB
++:100920000066030000FFFFFF036C030000070412D2
++:10093000006D030000071509056E03000002021098
++:10094000056F030000010203007603000040000071
++:1009500000770300000A0B0C0D780300001012143E
++:100960000079030000F00100007A0300000100009C
++:10097000007B030000030000007E030000009E03D4
++:10098000007F030000000100008003000000380029
++:10099000008103000040404000820300000AFF0085
++:1009A000008403000005F0770085030000FF7F3F0F
++:1009B0000089030000000080018A03000000001687
++:1009C000008B030000000080018F030000FFFF0385
++:1009D000009003000000008800990300000104015A
++:1009E000009B030000780000009D030000BF000092
++:1009F000009F03000010120000A003000080000010
++:100A000008A903000000008001AA030000000016EE
++:100A100000AB03000000008001AF030000FFFF03F4
++:100A200000B003000000008800B9030000010401C9
++:100A300000BB03000078000000BD030000BF000001
++:100A400000BF03000010120000C00300008000007F
++:100A500008CA03000070700200CD030000FFFFFF12
++:100A600003D303000007041200D403000007150994
++:100A700005D503000002021005D6030000010203A1
++:100A800000DD03000040000000DE0300000A0B0C44
++:100A90000DDF03000010121400E0030000F001005D
++:100AA00000E103000001000000E203000003000079
++:100AB00000E5030000009E0300E6030000000100C3
++:100AC00000E703000000380000E803000040404059
++:100AD00000E90300000AFF0000EB03000005F077C7
++:100AE00000EC030000FF7F3F00F0030000000080E7
++:100AF00001F103000000001600F203000000008076
++:100B000001F6030000FFFF0300F703000000008868
++:100B1000000004000001040100020400007800004D
++:100B20000004040000BF00000006040000101200D2
++:100B3000000704000080000008100400000000808E
++:100B400001110400000000160012040000000080E3
++:100B50000116040000FFFF030017040000000088D6
++:100B600000200400000104010022040000780000BD
++:100B70000024040000BF0000002604000010120042
++:100B8000002704000080000008310400007070029B
++:100B90000034040000FFFFFF033A040000070412C2
++:100BA000003B040000071509053C04000002021088
++:100BB000053D040000010203004404000040000061
++:100BC00000450400000A0B0C0D460400001012142E
++:100BD0000047040000F0010000480400000100008C
++:100BE0000049040000030000004C040000009E03C4
++:100BF000004D040000000100004E04000000380019
++:100C0000004F04000040404000500400000AFF0074
++:100C1000005204000005F0770053040000FF7F3FFE
++:100C2000008C04000004000000A10400000F00007C
++:100C300000C304000002000000C9040000200000FE
++:100C400000D304000067FE1F003B05000001000008
++:100C5000003C05000004000000410500001A0000EF
++:100C600000430500001000000044050000040000DF
++:100C7000004C050000808060005B05000001000062
++:100C80000074050000040000008C05000004000052
++:100C9000009305000002000000940500000400001D
++:100CA000009C05000080000000A405000004000076
++:100CB00000B105000004000000B9050000040000B8
++:100CC00000C905000004000000D105000008000074
++:100CD00000DB05000001000000E1050000FF070047
++:100CE00000E305000010000000F305000001000013
++:100CF00000490600000F00000093060000100000ED
++:100D000000CC06000004000000D4060000800000B3
++:100D100000DC06000004000000E406000000010200
++:100D200003EC06000003000000F4060000040000CD
++:100D3000001B0700000000803F1C070000040000AB
++:100D400000230700000000803F240700000300008C
++:100D5000002B0700000000803F33070000000080E8
++:100D60003F3B0700000000803F4307000000008079
++:100D70003F44070000040000004B07000000008013
++:100D80003F530700000000803F5B07000000008029
++:100D90003F630700000000803F6B070000000080F9
++:100DA0003F730700000000803F7B070000000080C9
++:100DB0003F830700000000803F8B07000000008099
++:100DC0003F930700000000803F6308000010000010
++:100DD00000730800003F000000AB080000010000A5
++:100DE00000BB08000001000000CB0800000100006B
++:100DF000003309000011000000730900000F00001B
++:100E000000B309000011000000EB09000001000020
++:100E100000F309000001000000FB090000010000D0
++:100E200000030A0000020000000B0A00000100009D
++:100E300000130A0000020000001B0A00000100006D
++:100E4000002B0A000067FE1F003B0A00008168AC0F
++:100E50000FEB0A000001000000F30A00000200008E
++:100E600000FB0A000001000000030B00000100006D
++:100E7000000B0B000002000000130B00000100003B
++:100E8000001B0B0000010000003B0B0000110000E4
++:100E900000430B000001000000630E000002000090
++:100EA00000730E000067FE1F00DB0E000001000053
++:100EB00000E30E000010000000FB0E000001000027
++:100EC00000330F0000020000007B0F000001000053
++:100ED00000810F00000F000000830F0000100000D1
++:100EE00000910F000001000000930F0000010000BE
++:100EF000003310000010000000BB10000000008054
++:100F00003FC31000000000803FCB100000000080B5
++:100F10003FD31000000000803FDB10000000008085
++:100F20003FE31000000000803FEB10000000008055
++:100F30003FF31000000000803FFB10000000008025
++:100F40003F031100000000803F0B110000000080F3
++:100F50003F131100000000803F1B110000000080C3
++:100F60003F231100000000803F2B11000000008093
++:100F70003F331100000000803F031200001000000A
++:100F800000131200003F0000004B1200000100009F
++:100F9000005B12000001000000691200000F000059
++:100FA000006B12000001000000D3120000110000CD
++:100FB00000131300000F0000005313000011000085
++:100FC000008B1300000100000093130000010000DB
++:100FD000009B13000001000000A3130000020000AA
++:100FE00000AB13000001000000B31300000200007A
++:100FF00000BB13000001000000CB13000067FE1FC0
++:1010000000DB1300008168AC0F8B140000010000AE
++:101010000093140000020000009B14000001000077
++:1010200000A314000001000000AB14000002000047
++:1010300000B314000001000000BB14000001000018
++:1010400000DB14000011000000E3140000010000A8
++:1010500000E820000021000000F020000001000056
++:1010600000F8200000020000000021000000010044
++:101070000008210000000100001021000001000014
++:1010800000282100000100000030210000020000C3
++:101090000038210000000100004021000000010094
++:1010A00000482100000100000090470000040000FB
++:1010B000009847000004000000115400000F0000D9
++:1010C00000B954000001000000C1540000000100FC
++:1010D00000C954000000010000D1540000110000BC
++:1010E00000E154000008000000115500000100005C
++:1010F00000215500000100000029550000010000FA
++:1011000000315500000100000039550000CF0000FB
++:101110000041550000020000007955000001000068
++:1011200000895500000100000091550000010000F9
++:10113000009955000001000000C1550000040000A6
++:1011400000D155000001000000D955000015000035
++:1011500000F955000080444404D9570000120C10D7
++:1011600008015800000001000019580000010001AA
++:101170000029580000010001003158000001000062
++:101180000039580000010001004158000001000032
++:1011900000495800000400000051580000020000FF
++:1011A000006C5800000400000072580000FFFF3F70
++:1011B0000074580000030000008A580000FF1F0060
++:1011C00000025900000000803F045900000F000099
++:1011D000003A59000004000000425900001A0000C3
++:1011E000005A59000001000000645900000400008A
++:1011F000006C590000FFFF000074590000FFFF0061
++:10120000007C590000FFFF000084590000FFFF0030
++:1012100000CC59000001000000EC59000001000062
++:1012200000F259000000FFFF001C5A0000010000FE
++:10123000002A5A00000F0000006A5A00008168ACC2
++:101240000F725A000011000000845A0000010000D3
++:10125000008C5A000001000000945A0000020000B7
++:10126000009C5A000001000000A45A000001000088
++:1012700000AC5A000002000000B45A000001000057
++:1012800000C45A000011000000F25A0000040000DF
++:1012900000045B00008168AC0F1A5B0000020000D4
++:1012A000001C5B000004000000225B000000000046
++:1012B000042A5B0000000000043C5B0000110000F9
++:1012C00000445B0000010000004A5B0000050000D4
++:1012D00000525B000052000000545B0000CF000091
++:1012E000005C5B0000CF000000645B0000CF0000EA
++:1012F000007A5B000001000000BC5B000001000000
++:1013000000C45B000001000000CC5B000002000094
++:1013100000D45B000001000000DC5B000001000065
++:1013200000E45B000002000000EC5B000001000034
++:1013300000FC5B000001000000025C000000008077
++:101340003F045C0000010000000A5C000000008017
++:101350003F0C5C000001000000125C0000000080F7
++:101360003F145C0000010000001A5C0000000080D7
++:101370003F1C5C000001000000225C0000000080B7
++:101380003F245C0000010000002A5C000000008097
++:101390003F2C5C000001000000325C000000008077
++:1013A0003F345C0000010000003A5C000000008057
++:1013B0003F3C5C000011000000425C000000008027
++:1013C0003F4A5C00000000803F525C00000000804B
++:1013D0003F5A5C00000000803F625C00000000801B
++:1013E0003F6A5C00000000803F725C0000000080EB
++:1013F0003F7A5C00000000803F7C5C00008168ACAC
++:101400000F825C000010000000845C00000F0000F0
++:1014100000C45C000067FE1F00DC5C0000110000DF
++:1014200000E45C000001000000045D000004000016
++:1014300000345D0000010000005C5D000011000050
++:10144000009C5D00008168AC0FBA5D0000120C10BA
++:1014500008BC5D000011000000C25D000005000036
++:1014600000C45D000001000000D45D000001000028
++:1014700000DA5D000001000000E45D0000010000F2
++:1014800000EA5D0000FFFF0000F25D0000FFFF00CA
++:1014900000F45D0000FF070000FA5D0000FFFF00A0
++:1014A00000025E0000FFFF0000045E00000100007B
++:1014B000000A5E000003000000145E00000100004E
++:1014C00000F15E0000DFBFE304F95E0000DFBFE370
++:1014D000040A5F000000FFFF00125F00001A000016
++:1014E00000215F00008168AC0F225F000003000054
++:1014F000006C5F000008000000745F00000800003E
++:10150000007C5F000008000000815F0000DFBFE397
++:1015100004845F000008000000895F0000DFBFE373
++:10152000048C5F000008000000945F0000080000C9
++:10153000009C5F000008000000A45F00000800009D
++:1015400000AC5F000011000000EC5F00008168AC9F
++:101550000FF45F000000040000FC5F0000000400C6
++:101560000004600000000400000C600000000400A3
++:101570000014600000000400001C60000000040073
++:101580000024600000000400002C60000000040043
++:101590000034600000000300003C60000000030015
++:1015A0000044600000000300004C600000000300E5
++:1015B0000054600000000300005A600000020100B7
++:1015C000005C600000000300006460000000030095
++:1015D000006A600000040000006C6000000003006E
++:1015E0000072600000040000007460000001000050
++:1015F000007A600000040000007C6000000F000022
++:101600000082600000040000008A60000004000006
++:10161000009260000004000000A2600000FF0700CC
++:1016200000B260000002010000BC60000020000069
++:1016300000C460000011000000CC60000000010048
++:1016400000DC60000001000000F4600000400000C9
++:1016500000FC6000000001000002610000040000C6
++:10166000000A610000040000000C6100000300009B
++:101670000012610000040000001A61000004000074
++:10168000003461000067FE1F00546100000200008A
++:10169000005C6100008168AC0FAC610000010000DB
++:1016A00000D461000004000000E4610000010000BB
++:1016B00000EC61000000040000F461000000030081
++:1016C00000FC610000011000001C6200001100001D
++:1016D000005C6200008168AC0F646200000F0000D3
++:1016E00000AA620000140C0800C262000004080096
++:1016F00000D262000004000000DA62000004000072
++:1017000000E2620000120C1008F262000004000007
++:1017100000FA620000040000000A630000100000EC
++:10172000002463000067FE1F00326300000408000D
++:10173000003A63000001000000426300001A00004C
++:101740000044630000110000004A6300007F0000B5
++:10175000005A630000010000005C63000004000008
++:101760000062630000140C08006C630000010000BC
++:101770000072630000120C10087463000001000086
++:10178000007A63000004000000826300000400008F
++:10179000009263000010000000946300000100004C
++:1017A00000B263000001000000BA630000120C10D8
++:1017B00008BC63000001000000CC630000010000D1
++:1017C00000EC6300008824712AF2630000FF070028
++:1017D00000FA630000140C0800FC63000000C085E0
++:1017E0004004640000400000000C640000000100A0
++:1017F0000014640000000101001C6400000000806F
++:1018000002B4640000DFBFE304BC640000DFBFE398
++:1018100004C464000001000000D464000000FFFF65
++:1018200000DC64000001000000F464000000FFFF21
++:10183000003C650000010000004C65000001000054
++:101840000054650000001020305C650000405060CE
++:1018500070646500008898A8B86C650000C8D8E876
++:10186000F87C6500001A0000008C65000004000090
++:1018700000C265000001000000DA650000100000F1
++:10188000003C660000040000004466000004000004
++:10189000004C660000808060007466000004000058
++:1018A000008C660000040000009466000004000044
++:1018B000009C66000080000000A466000004000098
++:1018C00000A267000088000000AA670000880000EE
++:1018D00000C267000004000000CC670000040000A4
++:1018E00000D467000080000000DC670000040000F6
++:1018F00000E467000000010203EC67000003000041
++:1019000000F4670000040000001C680000040000F0
++:101910000024680000030000004468000004000088
++:10192000007A680000260000009268000000008035
++:101930003FB26800001A000000BA68000010000002
++:1019400000026A000052000000126A000026000037
++:1019500000226A0000040000002A6A00000400005F
++:10196000003A6A00001A000000526A000000FFFFFF
++:1019700000626A0000040000006A6A0000040000BF
++:10198000007A6A000080000000826A000004000003
++:10199000008A6A0000140C08009A6A0000FF070021
++:1019A00000B2B4000004000000BAB400000400005B
++:1019B00000CAB4000080000000D2B400000400009F
++:1019C00000DAB4000001000000EAB40000270000C3
++:1019D00000FAB40000260000001AB5000000000064
++:1019E0000422B50000000000042AB5000000000039
++:1019F0000432B50000000000043AB5000000000009
++:101A00000442B50000000000044AB50000000000D8
++:101A10000452B50000000000045AB50000000000A8
++:101A20000462B50000000000046AB5000000000078
++:101A30000472B50000000000047AB5000000000048
++:101A40000482B50000000000048AB5000000000018
++:101A50000492B5000000000004BAB60000DFBFE346
++:101A600004C2B60000DFBFE304DAB6000021FE01C5
++:101A7000006CB900000400000074B900000300000D
++:101A80000004BA00000F00000064BA000004000067
++:101A9000006CBA0000FFFF000074BA0000FFFF00F6
++:101AA000007CBA0000FFFF000084BA0000FFFF00C6
++:101AB00000CCBA000001000000ECBA0000010000F8
++:101AC000001CBB00000100000084BB0000010000FE
++:101AD000008CBB00000100000094BB00000200006D
++:101AE000009CBB000001000000A4BB00000100003E
++:101AF00000ACBB000002000000B4BB00000100000D
++:101B000000C4BB00001100000004BC00008168ACF0
++:101B10000F1CBC0000040000003CBC0000110000D1
++:101B20000044BC00000100000054BC0000CF0000D5
++:101B3000005CBC0000CF00000064BC0000CF0000CF
++:101B400000BCBC000001000000C4BC00000100009B
++:101B500000CCBC000002000000D4BC00000100006A
++:101B600000DCBC000001000000E4BC00000200003A
++:101B700000ECBC000001000000FCBC000001000003
++:101B80000004BD0000010000000CBD0000010000C9
++:101B90000014BD0000010000001CBD000001000099
++:101BA0000024BD0000010000002CBD000001000069
++:101BB0000034BD0000010000003CBD000011000029
++:101BC000007CBD00008168AC0F84BD00000F0000E8
++:101BD00000C4BD000067FE1F00DCBD000011000056
++:101BE00000E4BD00000100000004BE00000400008D
++:101BF0000034BE0000010000005CBE0000110000C7
++:101C0000009CBE00008168AC0FBCBE00001100004B
++:101C100000C4BE000001000000D4BE0000010000AE
++:101C200000E4BE000001000000F4BE0000FF070059
++:101C30000004BF00000100000014BF00000100000C
++:101C4000006CC000000800000074C0000008000024
++:101C5000007CC000000800000084C00000080000F4
++:101C6000008CC000000800000094C00000080000C4
++:101C7000009CC0000008000000A4C0000008000094
++:101C800000ACC0000011000000ECC000008168AC96
++:101C90000FF4C0000000040000FCC00000000400BD
++:101CA0000004C10000000400000CC100000004009A
++:101CB0000014C10000000400001CC100000004006A
++:101CC0000024C10000000400002CC100000004003A
++:101CD0000034C10000000300003CC100000003000C
++:101CE0000044C10000000300004CC10000000300DC
++:101CF0000054C10000000300005CC10000000300AC
++:101D00000064C10000000300006CC100000003007B
++:101D10000074C10000010000007CC100000F000041
++:101D200000BCC1000020000000C4C1000011000080
++:101D300000CCC1000000010000DCC1000001000077
++:101D400000F4C1000040000000FCC10000000100E0
++:101D5000000CC200000300000034C2000067FE1F38
++:101D60000054C20000020000005CC200008168ACA8
++:101D70000FACC2000001000000D4C200000400004B
++:101D800000E4C2000001000000ECC20000000400FA
++:101D900000F4C2000000030000FCC20000011000BB
++:101DA000001CC30000110000005CC300008168AC8F
++:101DB0000F64C300000F00000024C4000067FE1F72
++:101DC0000044C40000110000005CC40000040000D6
++:101DD000006CC400000100000074C4000001000099
++:101DE0000094C4000001000000BCC4000001000019
++:101DF00000CCC4000001000000ECC4000088247185
++:101E00002AFCC4000000C0854004C500004000005A
++:101E1000000CC500000001000014C5000000010115
++:101E2000001CC5000000008002B4C50000DFBFE355
++:101E300004BCC50000DFBFE304C4C500000100000E
++:101E400000D4C5000000FFFF00DCC5000001000059
++:101E500000F4C5000000FFFF003CC60000010000C8
++:101E6000004CC600000100000054C6000000102015
++:101E7000305CC600004050607064C600008898A8BE
++:101E8000B86CC60000C8D8E8F87CC600001A00008C
++:101E900000F829010004000000002A0100040000ED
++:101EA00000082A0100120C1008102A01000300008B
++:101EB00000202A0100120C1008302A0100140C081E
++:101EC00000382A010001000000402A0100140C081B
++:101ED00000582A0100120C1008602A010027000097
++:101EE00000782A010001000000884801000100007C
++:101EF0000040490100120C1008A849010000000030
++:101F000004B049010000000004C049010080000045
++:101F100000E049010080000000F04901003F00009E
++:101F200000484A010002000000504A010000000081
++:101F300004584A010000000004A04A010004000007
++:101F400000C04A010004000000504B0100010000E5
++:101F500000584B010001100000604B0100FFFF0022
++:101F600000684B0100FFFF0000704B0100FFFF0005
++:101F700000784B0100FFFF0000804C010000008052
++:101F80003F884C01000000803F904C010000008021
++:101F90003F984C01000000803FA04C0100000080F1
++:101FA0003FA84C01000000803FB04C0100000080C1
++:101FB0003FB84C01000000803FC04C010000008091
++:101FC0003FC84C01000000803FD04C010000008061
++:101FD0003FD84C01000000803FE04C010000008031
++:101FE0003FE84C01000000803FF04C010000008001
++:101FF0003FF84C01000000803F004D010010000040
++:1020000000184D010003000000404D0100120C10AB
++:1020100008494D010080000000504D010080000083
++:1020200000514D010004700080584D010004700003
++:1020300080594D010000040004604D0100000400BF
++:1020400004614D010000100000684D010000100007
++:1020500000794D010001000000804D0100010000E9
++:1020600000914D010001000000984D0100010000A9
++:1020700000A14D010004000000A84D010004000073
++:1020800000A94D010002000000B04D010002000057
++:1020900000094E010080000000104E010080000089
++:1020A00000114E010004700080184E010004700001
++:1020B00080194E010000040004204E0100000400BD
++:1020C00004214E010000100000284E010000100005
++:1020D00000394E010001000000404E0100010000E7
++:1020E00000514E010001000000584E0100010000A7
++:1020F00000614E010004000000684E010004000071
++:1021000000694E010002000000704E010002000054
++:1021100000C14E0100120C1008C84E0100120C1034
++:1021200008F94E0100FFFF0000004F0100FFFF0013
++:1021300000014F0100FFFF0000084F0100FFFF00FA
++:1021400000094F0100FFFF0000104F0100FFFF00DA
++:1021500000114F0100FFFF0000184F0100FFFF00BA
++:1021600000194F010001000000204F010001000094
++:1021700000214F010001000100284F010001000172
++:1021800000294F010001000100304F010001000152
++:1021900000314F010001000000384F010001000034
++:1021A00000414F010021FE0100484F010021FE01C6
++:1021B00000714F0100120C1008784F0100120C1032
++:1021C00008794F010004000000804F010004000066
++:1021D00000894F010002000000904F010002000042
++:1021E00000914F010011000000984F010011000004
++:1021F00000D94F01008168AC0FE04F01008168AC4D
++:102200000F0150010004000000085001000400000C
++:10221000005150010002000000585001000200006F
++:102220000059500100010000006050010001000051
++:102230000061500100010000006850010001000031
++:10224000006950010002000000705001000200000F
++:1022500000715001000100000078500100010000F1
++:1022600000795001000100000080500100010000D1
++:1022700000815001000100000088500100010000B1
++:10228000009150010004000000985001000400007B
++:1022900000A96D010011000000B06D0100110000E7
++:1022A00000B96D010001000000C06D0100010000D7
++:0122B000002D
++:00000001FF
+diff --git a/firmware/nouveau/nv86.ctxprog.ihex b/firmware/nouveau/nv86.ctxprog.ihex
+new file mode 100644
+index 0000000..c341adf
+--- /dev/null
++++ b/firmware/nouveau/nv86.ctxprog.ihex
+@@ -0,0 +1,58 @@
++:100000004E56435000DF008E0070009C00700020B0
++:10001000002000080060004C005000890E400000E5
++:100020000020000700600000003000FF00C000005A
++:10003000002000FF008000090070004DDD400044FA
++:10004000294000052940000D29400006B94000055F
++:10005000006000C5154000110060000B274000C57E
++:100060002140000000700081007000040060004A20
++:10007000005000806D2100070060000128C0002EA4
++:1000800000200001008000CB005000FFFF9000FF27
++:10009000FF910020002000080060004C0050000983
++:1000A00000600045B940004DD440009D007000CF75
++:1000B0002D40009F0070009F005000C02A400000AB
++:1000C000022000080060004F2A4000C02A4000CCF7
++:1000D0003040008100700000002000060060000039
++:1000E000007000FC1B1100830070000000300080D5
++:1000F0006D210007006000010BC0001E0020000100
++:10010000008000CB005000FF00C000800070008322
++:1001100000700047002000060060000A0211008005
++:100120000220000700600000003000FF00C000FF58
++:1001300000C80007C4400016292000FF0080008C82
++:10014000504000CB0050003F02A0004000200006BD
++:100150000060000F007000020217000A0211003256
++:100160000020000D02100042021C000203120002D7
++:1001700004140000051800090513005005150005BA
++:100180000611000F002000070610000007110000F4
++:1001900009110002091200000A1100020B160028C2
++:1001A0000B12002B0B1400010C11000014110005A0
++:1001B00014110007141100091411000B141100CBC5
++:1001C000002000001510000F7940004B79400040DE
++:1001D0004B21000700600042042000FF0088008FD0
++:1001E0000070008C794000CB005000000000002B14
++:1001F000002000051A1000001C1300041C1200202F
++:100200001C1400251C1100401C1300441C1200602B
++:100210001C1400651C1100001F1300401F1900E092
++:10022000994000D9012000060060004400200080B1
++:10023000201000C6201100C9201500D02019000090
++:100240002112000321120000221600072212008052
++:100250002211000023110002231100802312008BC1
++:10026000231100942311009C231100000000000FB3
++:10027000A04000CB005000404B210007006000422E
++:10028000042000FF008800CB00500087A340000A34
++:100290000060000000000000B24000A0007000807C
++:1002A000007000800220000700600004002000FFB2
++:1002B00000C000FF008000CB005000000070000074
++:1002C00000200006006000FE1B11004DD44000001D
++:1002D0000070000000200006006000FE1B1100807E
++:1002E0000070001D0070004D11400081007000047E
++:1002F0000060004A00500088BE40000B0060000013
++:1003000000200006006000000070000BD44000FDDB
++:100310001B11004D42400016292000FD008000CB3B
++:100320000050000200C00080022000070060006052
++:1003300001200002008000CB0050000218C000B66F
++:1003400027200002008000CB0050004D4E40000BE3
++:100350000060004DD24000010070000300700006F4
++:10036000D8400005D940000D006000050070000D68
++:10037000007000060070000B0070000E0070000C92
++:030380000060001A
++:00000001FF
+diff --git a/firmware/nouveau/nv86.ctxvals.ihex b/firmware/nouveau/nv86.ctxvals.ihex
+new file mode 100644
+index 0000000..e8c1b3e
+--- /dev/null
++++ b/firmware/nouveau/nv86.ctxvals.ihex
+@@ -0,0 +1,340 @@
++:100000004E56435600A4020000430000003000009A
++:1000100000750000000300000076000000001000E2
++:1000200000860000000CFE00008B000000001000A5
++:100030000096000000870100009B000000181000DF
++:10004000009C000000FF000000AB00000004000066
++:1000500000AC000000DF004D04AE00000000060010
++:1000600000B400000000000001B5000000FF000027
++:1000700000B700000000040000BD00000001000007
++:1000800000BE00000080000000BF0000000400006F
++:1000900000C600000002000000C7000000010000D0
++:1000A00000CA00000001000000CB000000000100B9
++:1000B00000D100000002000000D20000000100009A
++:1000C00000D300000001000000D700000001000084
++:1000D00000D8000000FFFF3F00D9000000FF1F0014
++:1000E00000DB00000001000000DC00000001000057
++:1000F00000DE00000001000000DF00000001000041
++:1001000000E000000001000000E100000004000029
++:1001100000E200000001000000E300000001000018
++:1001200000E400000001000000E5000000070000FE
++:1001300000E600000001000000E7000000070000EA
++:1001400000E800000001000000E9000000010000DC
++:1001500000EA00000001000000EF000000010000C4
++:1001600000F000000000010000F2000000010000AB
++:1001700000F500000000010000F600000001000092
++:1001800000F700000000010000F90000000100007D
++:1001900000FC00000000010000010100000400005C
++:1001A0000002010000700000000301000080000058
++:1001B00000080100000C0000000A01000008000017
++:1001C000000B010000140000000D010000290000D8
++:1001D000000E010000270000000F010000260000B3
++:1001E00000100100000800000011010000040000E0
++:1001F00000120100002700000015010000010000AE
++:1002000000160100000200000017010000030000BA
++:1002100000180100000400000019010000050000A2
++:10022000001A010000060000001B0100000700008A
++:10023000001C010000010000002D010000CF0000A3
++:100240000039010000800000003A010000040000B5
++:10025000003B010000040000003C0100000300001E
++:10026000003D0100000100000040010000120000FC
++:10027000004101000010000000420100000C0000DD
++:1002800000430100000100000047010000040000DD
++:1002900000480100000200000049010000040000C5
++:1002A000004C010000FFFF3F004D010000FF1F0058
++:1002B0000057010000040000005801000014000075
++:1002C0000059010000010000005C01000002000074
++:1002D000005F010000010000006101000002000059
++:1002E00000620100000010000063010000000E0029
++:1002F00000640100000010000065010000001E0005
++:10030000006701000001000000680100000100001A
++:100310000069010000010000006A01000001000006
++:10032000006B010000010000006F010000000200EE
++:100330000071010000010000007201000070000067
++:100340000073010000800000007601000001000041
++:1003500000770100007000000078010000800000BC
++:10036000007C010000010000007D010000CF0000C2
++:10037000007F0100000100000083010000CF0000A9
++:10038000008501000002000000870100000100005C
++:100390000089010000010000008B010000CF000077
++:1003A000008C010000CF0000008D01000001000062
++:1003B000008F010000800F0000A101000080007F7D
++:1003C00000B001000080007F00B901000021F87436
++:1003D0003BBA01000001800589BC0100000010004B
++:1003E00000BD0100001F000000BE010000FA107CEB
++:1003F00002BF010000C0000040C001000080208951
++:10040000B7C301000021F8743BC40100000180055E
++:1004100089C601000000100000C70100001F000095
++:1004200000C8010000FA107C02C9010000C00000F1
++:1004300040CA010000802089B7CD010000400001C2
++:1004400000CF01000022000000D2010000400001A6
++:1004500000D301000022000000D90100000000804C
++:1004600001DA01000000001600DB0100000000803E
++:1004700001DF010000FFFF0300E001000000008C2D
++:1004800000E901000001040100EB01000078000018
++:1004900000ED010000BF000000EF0100001012009D
++:1004A00000F001000080000008F901000000008059
++:1004B00001FA01000000001600FB010000000080AE
++:1004C00001FF010000FFFF03000002000000008C9C
++:1004D0000009020000010401000B02000078000086
++:1004E000000D020000BF0000000F0200001012000B
++:1004F0000010020000800000081A02000070700264
++:10050000001D020000FFFFFF03230200000704128A
++:10051000002402000007150905250200000202015F
++:100520000526020000010203002D02000040000029
++:10053000002E0200000A0B0C0D2F020000101214F6
++:100540000030020000F00100003102000001000054
++:1005500000320200000300000035020000009E038C
++:1005600000360200000001000037020000003800E1
++:10057000003802000040404000390200000AFF003D
++:10058000003B02000005F077003C020000FF7F3FC7
++:10059000008C02000004000000A10200000F000017
++:1005A00000C302000002000000C902000020000099
++:1005B00000D302000067FE1F003B030000010000A3
++:1005C000003C03000004000000410300001A00008A
++:1005D000004303000010000000440300000400007A
++:1005E000004C030000808060005B030000010000FD
++:1005F00000740300000400000083030000040000F6
++:10060000008B030000000400008C030000040000C5
++:1006100000930300000003000094030000040000A6
++:10062000009B030000011000009C030000800000FC
++:1006300000A303000015000000A4030000001E003A
++:1006400000AC03000004000000B10300000400003F
++:1006500000B903000004000000C90300000400000A
++:1006600000D103000008000000D3030000020000D6
++:1006700000E1030000FF0700001B04000001000070
++:1006800000230400001000000033040000010000FB
++:1006900000490400000F000000D304000010000017
++:1006A00000D404000004000000DC0400008000000E
++:1006B00000E404000004000000EC0400000001025B
++:1006C00003F404000003000000FC040000001E000E
++:1006D0000004050000040000002C050000040000D8
++:1006E0000034050000030000005405000004000071
++:1006F000005B0500000000803F63050000000080F3
++:100700003F6B0500000000803F7305000000008083
++:100710003F7B0500000000803F8305000000008053
++:100720003F8B0500000000803F9305000000008023
++:100730003F9B0500000000803FA3050000000080F3
++:100740003FAB0500000000803FB3050000000080C3
++:100750003FBB0500000000803FC305000000008093
++:100760003FCB0500000000803FD305000000008063
++:100770003FA306000010000000B30600003F000089
++:1007800000EB06000001000000FB06000001000075
++:10079000000B0700000100000073070000110000BB
++:1007A00000B30700000F000000F307000011000075
++:1007B000002B0800000100000033080000010000C9
++:1007C000003B080000010000004308000002000098
++:1007D000004B080000010000005308000002000068
++:1007E000005B080000010000006B08000067FE1FAE
++:1007F000007B0800008168AC0F2B0900000100009D
++:100800000033090000020000003B09000001000065
++:100810000043090000010000004B09000002000035
++:100820000053090000010000005B09000001000006
++:10083000007B090000110000008309000001000096
++:1008400000A30C000002000000B30C000067FE1FB4
++:10085000001B0D000001000000230D00001000002F
++:10086000003B0D000001000000630D0000040000CB
++:10087000006B0D000000040000730D000000030079
++:10088000007B0D000001100000810D00000F000032
++:1008900000830D000015000000910D000001000014
++:1008A00000B30D000002000000FB0D00000100007D
++:1008B00000030E000010000000130E0000010000F5
++:1008C00000B30E0000100000003B0F00000000808D
++:1008D0003F430F00000000803F4B0F0000000080EE
++:1008E0003F530F00000000803F5B0F0000000080BE
++:1008F0003F630F00000000803F6B0F00000000808E
++:100900003F730F00000000803F7B0F00000000805D
++:100910003F830F00000000803F8B0F00000000802D
++:100920003F930F00000000803F9B0F0000000080FD
++:100930003FA30F00000000803FAB0F0000000080CD
++:100940003FB30F00000000803F691000000F00005F
++:10095000008310000010000000931000003F000012
++:1009600000CB10000001000000DB100000010000BF
++:1009700000EB100000010000005311000011000006
++:1009800000931100000F000000D3110000110000BF
++:10099000000B120000010000001312000001000013
++:1009A000001B1200000100000023120000020000E2
++:1009B000002B1200000100000033120000020000B2
++:1009C000003B120000010000004B12000067FE1FF8
++:1009D000005B1200008168AC0F0B130000010000E7
++:1009E0000013130000020000001B130000010000B0
++:1009F0000023130000010000002B13000002000080
++:100A00000033130000010000003B13000001000050
++:100A1000005B1300001100000063130000010000E0
++:100A200000E81E000021000000F01E000001000090
++:100A300000F81E000002000000001F00000001007E
++:100A400000081F000000010000101F00000100004E
++:100A500000281F000001000000301F0000020000FD
++:100A600000381F000000010000401F0000000100CE
++:100A700000481F0000010000009045000004000035
++:100A8000009845000004000000115200000F000013
++:100A900000B952000001000000C152000000010036
++:100AA00000C952000000010000D1520000110000F6
++:100AB00000E1520000080000001153000001000096
++:100AC0000021530000010000002953000001000034
++:100AD00000315300000100000039530000CF000036
++:100AE00000415300000200000079530000010000A3
++:100AF0000089530000010000009153000001000034
++:100B0000009953000001000000C1530000040000E0
++:100B100000D153000001000000D95300001500006F
++:100B200000F953000080444404D9550000120C1011
++:100B300008015600000001000019560000010001E4
++:100B4000002956000001000100315600000100009C
++:100B5000003956000001000100415600000100006C
++:100B60000049560000040000005156000002000039
++:100B70000052560000FFFF3F006A560000FF1F00B2
++:100B8000007C5600000400000084560000030000B2
++:100B900000E25600000000803F145700000F0000E4
++:100BA000001A57000004000000225700001A00003D
++:100BB000003A5700000100000074570000040000D4
++:100BC000007C570000FFFF000084570000FFFF007B
++:100BD000008C570000FFFF000094570000FFFF004B
++:100BE00000D257000000FFFF00DC570000010000AA
++:100BF00000FC570000010000000A5800000F000030
++:100C0000002C580000010000004A5800008168AC28
++:100C10000F5258000011000000945800000100001D
++:100C2000009C58000001000000A4580000020000D1
++:100C300000AC58000001000000B4580000010000A2
++:100C400000BC58000002000000C458000001000071
++:100C500000D258000004000000D458000011000029
++:100C600000FA5800000200000002590000000000D5
++:100C7000040A59000000000004145900008168AC07
++:100C80000F2A590000050000002C59000004000044
++:100C90000032590000520000004C590000110000C1
++:100CA0000054590000010000005A590000010000E2
++:100CB0000064590000CF0000006C590000CF000014
++:100CC0000074590000CF000000B1590000DFBFE3FD
++:100CD00004B9590000DFBFE304CC59000001000053
++:100CE00000D459000001000000DC5900000200009F
++:100CF00000E15900008168AC0FE25900000000805B
++:100D00003FE459000001000000EA590000000080A3
++:100D10003FEC59000001000000F259000000008083
++:100D20003FF459000002000000FA59000000008062
++:100D30003FFC59000001000000025A000000008042
++:100D40003F0A5A00000000803F0C5A0000010000DA
++:100D500000125A00000000803F145A0000010000F9
++:100D6000001A5A00000000803F1C5A0000010000D9
++:100D700000225A00000000803F245A0000010000B9
++:100D8000002A5A00000000803F2C5A000001000099
++:100D900000325A00000000803F345A000001000079
++:100DA000003A5A00000000803F3C5A000001000059
++:100DB00000415A0000DFBFE304425A0000000080F7
++:100DC0003F445A000001000000495A0000DFBFE321
++:100DD000044A5A00000000803F4C5A0000110000F5
++:100DE00000525A00000000803F5A5A000000008064
++:100DF0003F625A0000100000008C5A00008168AC6D
++:100E00000F945A00000F000000D45A000067FE1F24
++:100E100000EC5A000011000000F45A00000100002C
++:100E200000145B000004000000445B0000010000AF
++:100E3000006C5B0000110000009A5B0000120C10B7
++:100E400008A25B000005000000AC5B00008168ACFC
++:100E50000FBA5B000001000000CA5B0000FFFF004A
++:100E600000CC5B000011000000D25B0000FFFF001F
++:100E700000D45B000001000000DA5B0000FFFF000F
++:100E800000E25B0000FFFF0000E45B0000010000E7
++:100E900000EA5B000003000000F45B0000010000BA
++:100EA00000045C0000FF070000145C00000100006B
++:100EB00000245C000001000000EA5C000000FFFF6D
++:100EC00000F25C00001A000000025D000003000058
++:100ED000007C5D000008000000845D000008000048
++:100EE000008C5D000008000000945D000008000018
++:100EF000009C5D000008000000A45D0000080000E8
++:100F000000AC5D000008000000B45D0000080000B7
++:100F100000BC5D000011000000FC5D00008168ACB9
++:100F20000F045E0000000400000C5E0000000400DE
++:100F300000145E0000000400001C5E0000000400BD
++:100F400000245E0000000400002C5E00000004008D
++:100F500000345E0000000400003A5E000002010060
++:100F6000003C5E000000040000445E00000003003E
++:100F7000004A5E0000040000004C5E000000030018
++:100F800000525E000004000000545E0000000300F8
++:100F9000005A5E0000040000005C5E0000000300D8
++:100FA00000625E000004000000645E0000000300B8
++:100FB000006A5E0000040000006C5E000000030098
++:100FC00000725E000004000000745E000000030078
++:100FD000007C5E000000030000825E0000FF07004E
++:100FE00000845E0000010000008C5E00000F000025
++:100FF00000925E000002010000CC5E0000200000B4
++:1010000000D45E000011000000DC5E000000010062
++:1010100000E25E000004000000EA5E000004000040
++:1010200000EC5E000001000000F25E000004000021
++:1010300000FA5E000004000000045F0000400000B1
++:10104000000C5F0000000100001C5F0000030000B6
++:1010500000445F000067FE1F00645F0000020000A4
++:10106000006C5F00008168AC0FBC5F0000010000F5
++:1010700000E45F000004000000F45F0000010000D5
++:1010800000FC5F000000040000046000000003009A
++:10109000000C600000011000002C60000011000036
++:1010A000006C6000008168AC0F746000000F0000ED
++:1010B000008A600000140C0800A260000004080010
++:1010C00000B260000004000000BA600000040000EC
++:1010D00000C2600000120C1008D260000004000082
++:1010E00000DA60000004000000EA60000010000068
++:1010F0000012610000040800001A610000010000F5
++:1011000000226100001A0000002A6100007F000038
++:10111000003461000067FE1F003A6100000100001A
++:101120000042610000140C080052610000120C1013
++:101130000854610000110000005A61000004000022
++:101140000062610000040000006C61000004000007
++:101150000072610000100000007C610000010000CE
++:1011600000846100000100000092610000010000A5
++:10117000009A610000120C1008A461000001000038
++:1011800000CC61000001000000D2610000FF0700F8
++:1011900000DA610000140C0800DC610000010000AE
++:1011A00000FC6100008824712A0C62000000C085E8
++:1011B0004014620000400000001C620000000100BA
++:1011C0000024620000000101002C62000000008089
++:1011D00002C4620000DFBFE304CC620000DFBFE3B3
++:1011E00004D462000001000000E462000000FFFF80
++:1011F00000EC620000010000000463000000FFFF3B
++:10120000004C630000010000005C6300000100006E
++:101210000064630000001020306C630000405060E8
++:1012200070746300008898A8B87C630000C8D8E890
++:10123000F88C6300001A000000A2630000010000A7
++:1012400000BA630000100000008265000088000002
++:10125000008A65000088000000A26500000400000C
++:10126000005A660000260000007266000000008040
++:101270003F926600001A0000009A6600001000000D
++:1012800000E267000052000000F267000026000044
++:101290000002680000040000000A6800000400006A
++:1012A000001A6800001A0000003268000000FFFF0A
++:1012B0000042680000040000004A680000040000CA
++:1012C000005A68000080000000626800000400000E
++:1012D000006A680000140C08007A680000FF07002C
++:1012E0000092920000040000009A920000040000A6
++:1012F00000AA92000080000000B2920000040000EA
++:1013000000BA92000001000000CA9200002700000D
++:1013100000DA92000026000000FA920000000000AF
++:101320000402930000000000040A93000000000083
++:101330000412930000000000041A93000000000053
++:101340000422930000000000042A93000000000023
++:101350000432930000000000043A930000000000F3
++:101360000442930000000000044A930000000000C3
++:101370000452930000000000045A93000000000093
++:101380000462930000000000046A93000000000063
++:101390000472930000000000049A940000DFBFE391
++:1013A00004A2940000DFBFE304BA94000021FE0110
++:1013B00000F82701000400000000280100040000DC
++:1013C0000008280100120C1008102801000300007A
++:1013D0000020280100120C100830280100140C080D
++:1013E00000382801000100000040280100140C080A
++:1013F0000058280100120C10086028010027000086
++:10140000007828010001000000884601000100006A
++:101410000040470100120C1008A84701000000001E
++:1014200004B047010000000004C047010080000034
++:1014300000E047010080000000F04701003F00008D
++:101440000048480100020000005048010000000070
++:10145000045848010000000004A0480100040000F6
++:1014600000C04801000400000050490100010000D4
++:1014700000584901000110000060490100FFFF0011
++:101480000068490100FFFF000070490100FFFF00F4
++:101490000078490100FFFF0000804A010000008041
++:1014A0003F884A01000000803F904A010000008010
++:1014B0003F984A01000000803FA04A0100000080E0
++:1014C0003FA84A01000000803FB04A0100000080B0
++:1014D0003FB84A01000000803FC04A010000008080
++:1014E0003FC84A01000000803FD04A010000008050
++:1014F0003FD84A01000000803FE04A010000008020
++:101500003FE84A01000000803FF04A0100000080EF
++:101510003FF84A01000000803F004B01001000002E
++:0915200000184B0100030000005B
++:00000001FF
+diff --git a/firmware/nouveau/nv92.ctxprog.ihex b/firmware/nouveau/nv92.ctxprog.ihex
+new file mode 100644
+index 0000000..01b9fc2
+--- /dev/null
++++ b/firmware/nouveau/nv92.ctxprog.ihex
+@@ -0,0 +1,103 @@
++:100000004E5643500095018E0070009C00700020F9
++:10001000002000080060004C005000890E400000E5
++:100020000020000700600000003000FF00C000005A
++:10003000002000FF008000090070004D9241004444
++:10004000294000052940000D294000066E410005A9
++:10005000006000C5154000110060000B274000C57E
++:100060002140000000700081007000040060004A20
++:1000700000500000962100070060000127C0002EFC
++:1000800000200001008000CB005000FFFF9000FF27
++:10009000FF910020002000080060004C0050000983
++:1000A000006000456E41004D8941009D007000CF09
++:1000B0002D40009F0070009F005000C02A4000802B
++:1000C000002000080060004F2A4000C02A4000CCF9
++:1000D0003040008100700000002000060060000039
++:1000E000007000FC1B110083007000000030000055
++:1000F00096210007006000010AC0001E00200001D8
++:10010000008000CB005000FF00C000800070008322
++:1001100000700047002000060060000A0211004045
++:100120000520000700600000003000FF00C000FF55
++:1001300000C80007794100D22D2000FF0080008C0C
++:10014000504000CB0050003F02A0004000200006BD
++:100150000060000F007000020217000A0211003256
++:100160000020000D02100042021C000203120002D7
++:1001700004140000051800090513005005150005BA
++:100180000611000F002000070610000007110000F4
++:1001900009110002091200000A1100020B160028C2
++:1001A0000B12002B0B1400010C11000014110005A0
++:1001B00014110007141100091411000B141100CBC5
++:1001C000002000001510000F7940004B794000001E
++:1001D000742100070060003E042000FF0088008FAB
++:1001E0000070008C794000CB00500000000000053A
++:1001F0001A14000C1A1300001C1300041C12002017
++:100200001C1400251C1100401C1300441C1200602B
++:100210001C1400651C1100801C1300841C1200A01B
++:100220001C1400A51C1100C01C1300C41C1200E00B
++:100230001C1400E51C1100001F1300401F1900E0F2
++:10024000A14000C901200006006000440020008099
++:10025000201000C6201100C9201500D02019000070
++:100260002112000321120000221600072212008032
++:100270002211000023110002231100802312008BA1
++:10028000231100942311009C231100E1BE40003093
++:100290000220000600600044002000802410000FAF
++:1002A000AF40004BAF400000742100070060003EEB
++:1002B000042000FF0088008F0070008CAF4000CB4E
++:1002C00000500000000000C6241100C9241500D011
++:1002D000241900002512000325120000261600072D
++:1002E000261200802611000027110002271100802D
++:1002F0002712008B271100942711009C271100E280
++:10030000D1400097022000060060004400200080D9
++:10031000281000C6281100C9281500D0281900008F
++:1003200029120003291200002A1600072A12008051
++:100330002A1100002B1100022B1100802B12008BC0
++:100340002B1100942B11009C2B1100E3EE4000FEBA
++:100350000220000600600044002000802C10000FE6
++:10036000DF40004BDF400000742100070060003ECA
++:10037000042000FF0088008F0070008CDF4000CB5D
++:1003800000500000000000C62C1100C92C1500D040
++:100390002C1900002D1200032D1200002E1600074C
++:1003A0002E1200802E1100002F1100022F1100804C
++:1003B0002F12008B2F1100942F11009C2F1100E49D
++:1003C0000141006503200006006000440020008019
++:1003D000301000C6301100C9301500D030190000AF
++:1003E0003112000331120000321600073212008071
++:1003F0003211000033110002331100803312008BE0
++:10040000331100943311009C331100E51E4100CCE0
++:100410000320000600600044002000803410000F1C
++:100420000F41004B0F410000742100070060003EA7
++:10043000042000FF0088008F0070008C0F4100CB6B
++:1004400000500000000000C6341100C9341500D06F
++:10045000341900003512000335120000361600076B
++:10046000361200803611000037110002371100806B
++:100470003712008B371100943711009C371100E6BA
++:100480003141003304200006006000440020008059
++:10049000381000C6381100C9381500D038190000CE
++:1004A00039120003391200003A1600073A12008090
++:1004B0003A1100003B1100023B1100803B12008BFF
++:1004C0003B1100943B11009C3B1100E74E41009A08
++:1004D0000420000600600044002000803C10000F53
++:1004E0003F41004B3F410000742100070060003E87
++:1004F000042000FF0088008F0070008C3F4100CB7B
++:1005000000500000000000C63C1100C93C1500D09E
++:100510003C1900003D1200033D1200003E1600078A
++:100520003E1200803E1100003F1100023F1100808A
++:100530003F12008B3F1100943F11009C3F110000BF
++:100540000000000F554100CB00500000742100074F
++:100550000060003E042000FF008800CB00500087B0
++:100560005841000A0060000000000000674100A040
++:10057000007000800070004005200007006000044B
++:10058000002000FF00C000FF008000CB00500000F2
++:100590000070000000200006006000FE1B11004DEE
++:1005A000894100000070000000200006006000FE8D
++:1005B0001B1100800070001D0070004D1140008173
++:1005C000007000040060004A005000887341000B76
++:1005D0000060000000200006006000000070000BBA
++:1005E000894100FD1B11004D424000D22D2000FD2D
++:1005F000008000CB0050000200C000400520000732
++:100600000060006001200002008000CB005000026A
++:1006100018C000722C200002008000CB0050004D5A
++:100620004E40000B0060004D874100010070000348
++:10063000007000068D4100058E41000D0060000530
++:100640000070000D007000060070000B0070000EBE
++:0B0650000070001C0070000C00600037
++:00000001FF
+diff --git a/firmware/nouveau/nv92.ctxvals.ihex b/firmware/nouveau/nv92.ctxvals.ihex
+new file mode 100644
+index 0000000..31c8e18
+--- /dev/null
++++ b/firmware/nouveau/nv92.ctxvals.ihex
+@@ -0,0 +1,1232 @@
++:100000004E564356009C090000430000003000009B
++:1000100000750000000300000076000000001000E2
++:1000200000860000000CFE00008B000000001000A5
++:100030000096000000870100009B000000181000DF
++:10004000009C000000FF000000AB00000004000066
++:1000500000AC000000DF002504AE00000000060038
++:1000600000B400000000000001B5000000FF000027
++:1000700000B700000000040000BD00000001000007
++:1000800000BE00000080000000BF0000000400006F
++:1000900000C600000002000000C7000000010000D0
++:1000A00000CA00000001000000CB000000000100B9
++:1000B00000D100000002000000D20000000100009A
++:1000C00000D300000001000000D700000001000084
++:1000D00000D8000000FFFF3F00D9000000FF1F0014
++:1000E00000DB00000001000000DC00000001000057
++:1000F00000DE00000001000000DF00000001000041
++:1001000000E000000001000000E100000004000029
++:1001100000E200000001000000E300000001000018
++:1001200000E400000001000000E5000000070000FE
++:1001300000E600000001000000E7000000070000EA
++:1001400000E800000001000000E9000000010000DC
++:1001500000EA00000001000000EF000000010000C4
++:1001600000F000000000010000F2000000010000AB
++:1001700000F500000000010000F600000001000092
++:1001800000F700000000010000F90000000100007D
++:1001900000FC00000000010000010100000400005C
++:1001A0000002010000700000000301000080000058
++:1001B00000080100000C0000000A01000008000017
++:1001C000000B010000140000000D010000290000D8
++:1001D000000E010000270000000F010000260000B3
++:1001E00000100100000800000011010000040000E0
++:1001F00000120100002700000015010000010000AE
++:1002000000160100000200000017010000030000BA
++:1002100000180100000400000019010000050000A2
++:10022000001A010000060000001B0100000700008A
++:10023000001C010000010000002D010000CF0000A3
++:100240000039010000800000003A010000040000B5
++:10025000003B010000040000003C0100000300001E
++:10026000003D0100000100000040010000120000FC
++:10027000004101000010000000420100000C0000DD
++:1002800000430100000100000047010000040000DD
++:1002900000480100000200000049010000040000C5
++:1002A000004C010000FFFF3F004D010000FF1F0058
++:1002B0000057010000040000005801000014000075
++:1002C0000059010000010000005C01000002000074
++:1002D000005F010000010000006101000002000059
++:1002E00000620100000010000063010000000E0029
++:1002F00000640100000010000065010000001E0005
++:10030000006701000001000000680100000100001A
++:100310000069010000010000006A01000001000006
++:10032000006B010000010000006F010000000200EE
++:100330000071010000010000007201000070000067
++:100340000073010000800000007601000001000041
++:1003500000770100007000000078010000800000BC
++:10036000007C010000010000007D010000CF0000C2
++:10037000007F0100000100000083010000CF0000A9
++:10038000008501000002000000870100000100005C
++:100390000089010000010000008B010000CF000077
++:1003A000008C010000CF0000008D01000001000062
++:1003B000008F010000801F00009501000021F874EB
++:1003C0003B960100000180058998010000001000A3
++:1003D00000990100001F0000009A010000FA107C43
++:1003E000029B010000C00000409C010000802089A9
++:1003F000B79F01000021F8743BA0010000018005B7
++:1004000089A201000000100000A30100001F0000ED
++:1004100000A4010000FA107C02A5010000C0000049
++:1004200040A6010000802089B7A901000021F874CE
++:100430003BAA01000001800589AC0100000010000A
++:1004400000AD0100001F000000AE010000FA107CAA
++:1004500002AF010000C0000040B001000080208910
++:10046000B7B301000021F8743BB40100000180051E
++:1004700089B601000000100000B70100001F000055
++:1004800000B8010000FA107C02B9010000C00000B1
++:1004900040BA010000802089B7BD0100004000394A
++:1004A00000BF01000022000000C20100004000392E
++:1004B00000C301000022000000C90100000000800C
++:1004C00001CA01000000001600CB010000000080FE
++:1004D00001CF010000FFFF0300D001000000008CED
++:1004E00011D901000001040100DB010000780000C7
++:1004F00000DD010000BF000000DF0100001012005D
++:1005000000E001000080000008E901000000008018
++:1005100001EA01000000001600EB0100000000806D
++:1005200001EF010000FFFF0300F001000000008C5C
++:1005300011F901000001040100FB01000078000036
++:1005400000FD010000BF000000FF010000101200CC
++:100550000000020000800000080A02000070700223
++:10056000000D020000FFFFFF03130200000704124A
++:10057000001402000007150905150200000202011F
++:100580000516020000010203001D020000400000E9
++:10059000001E0200000A0B0C0D1F020000101214B6
++:1005A0000020020000F00100002102000001000014
++:1005B00000220200000300000025020000009E034C
++:1005C00000260200000001000027020000003800A1
++:1005D000002802000040404000290200000AFF00FD
++:1005E000002B02000005F077002C020000FF7F3F87
++:1005F00000300200000000800131020000000016FF
++:1006000000320200000000800136020000FFFF03FC
++:10061000003702000000008C1140020000010401BC
++:1006200000420200007800000044020000BF000009
++:100630000046020000101200004702000080000087
++:100640000850020000000080015102000000001666
++:1006500000520200000000800156020000FFFF036C
++:10066000005702000000008C11600200000104012C
++:1006700000620200007800000064020000BF000079
++:1006800000660200001012000067020000800000F7
++:1006900008710200007070020074020000FFFFFF8A
++:1006A000037A020000070412007B0200000715090C
++:1006B000057C020000020201057D02000001020328
++:1006C000008402000040000000850200000A0B0CBC
++:1006D0000D860200001012140087020000F00100D5
++:1006E00000880200000100000089020000030000F1
++:1006F000008C020000009E03008D0200000001003B
++:10070000008E020000003800008F020000404040D0
++:1007100000900200000AFF00009202000005F0773E
++:100720000093020000FF7F3F00970200000000805E
++:1007300001980200000000160099020000000080ED
++:10074000019D020000FFFF03009E02000000008CDC
++:1007500011A702000001040100A9020000780000B6
++:1007600000AB020000BF000000AD0200001012004C
++:1007700000AE02000080000008B702000000008008
++:1007800001B802000000001600B90200000000805D
++:1007900001BD020000FFFF0300BE02000000008C4C
++:1007A00011C702000001040100C902000078000026
++:1007B00000CB020000BF000000CD020000101200BC
++:1007C00000CE02000080000008D802000070700215
++:1007D00000DB020000FFFFFF03E10200000704123C
++:1007E00000E202000007150905E302000002020111
++:1007F00005E402000001020300EB020000400000DB
++:1008000000EC0200000A0B0C0DED020000101214A7
++:1008100000EE020000F0010000EF02000001000005
++:1008200000F002000003000000F3020000009E033D
++:1008300000F402000000010000F502000000380092
++:1008400000F602000040404000F70200000AFF00EE
++:1008500000F902000005F07700FA020000FF7F3F78
++:1008600000FE02000000008001FF020000000016F0
++:1008700000000300000000800104030000FFFF03EC
++:10088000000503000000008C110E030000010401AC
++:1008900000100300007800000012030000BF0000F9
++:1008A0000014030000101200001503000080000077
++:1008B000081E030000000080011F03000000001656
++:1008C00000200300000000800124030000FFFF035C
++:1008D000002503000000008C112E0300000104011C
++:1008E00000300300007800000032030000BF000069
++:1008F00000340300001012000035030000800000E7
++:10090000083F0300007070020042030000FFFFFF79
++:1009100003480300000704120049030000071509FB
++:10092000054A030000020201054B03000001020317
++:10093000005203000040000000530300000A0B0CAB
++:100940000D540300001012140055030000F00100C4
++:1009500000560300000100000057030000030000E0
++:10096000005A030000009E03005B0300000001002A
++:10097000005C030000003800005D030000404040C0
++:10098000005E0300000AFF00006003000005F0772E
++:100990000061030000FF7F3F00650300000000804E
++:1009A00001660300000000160067030000000080DD
++:1009B000016B030000FFFF03006C03000000008CCC
++:1009C00011750300000104010077030000780000A6
++:1009D0000079030000BF0000007B0300001012003C
++:1009E000007C0300008000000885030000000080F8
++:1009F000018603000000001600870300000000804D
++:100A0000018B030000FFFF03008C03000000008C3B
++:100A10001195030000010401009703000078000015
++:100A20000099030000BF0000009B030000101200AB
++:100A3000009C03000080000008A603000070700204
++:100A400000A9030000FFFFFF03AF0300000704122B
++:100A500000B003000007150905B103000002020100
++:100A600005B203000001020300B9030000400000CA
++:100A700000BA0300000A0B0C0DBB03000010121497
++:100A800000BC030000F0010000BD030000010000F5
++:100A900000BE03000003000000C1030000009E032D
++:100AA00000C203000000010000C303000000380082
++:100AB00000C403000040404000C50300000AFF00DE
++:100AC00000C703000005F07700C8030000FF7F3F68
++:100AD00000CC03000000008001CD030000000016E0
++:100AE00000CE03000000008001D2030000FFFF03DE
++:100AF00000D303000000008C11DC0300000104019E
++:100B000000DE03000078000000E0030000BF0000EA
++:100B100000E203000010120000E303000080000068
++:100B200008EC03000000008001ED03000000001647
++:100B300000EE03000000008001F2030000FFFF034D
++:100B400000F303000000008C11FC0300000104010D
++:100B500000FE0300007800000000040000BF000059
++:100B600000020400001012000003040000800000D6
++:100B7000080D0400007070020010040000FFFFFF69
++:100B800003160400000704120017040000071509EB
++:100B90000518040000020201051904000001020307
++:100BA000002004000040000000210400000A0B0C9B
++:100BB0000D220400001012140023040000F00100B4
++:100BC00000240400000100000025040000030000D0
++:100BD0000028040000009E0300290400000001001A
++:100BE000002A040000003800002B040000404040B0
++:100BF000002C0400000AFF00002E04000005F0771E
++:100C0000002F040000FF7F3F00330400000000803D
++:100C100001340400000000160035040000000080CC
++:100C20000139040000FFFF03003A04000000008CBB
++:100C30001143040000010401004504000078000095
++:100C40000047040000BF000000490400001012002B
++:100C5000004A0400008000000853040000000080E7
++:100C6000015404000000001600550400000000803C
++:100C70000159040000FFFF03005A04000000008C2B
++:100C80001163040000010401006504000078000005
++:100C90000067040000BF000000690400001012009B
++:100CA000006A0400008000000874040000707002F4
++:100CB0000077040000FFFFFF037D0400000704121B
++:100CC000007E040000071509057F040000020201F0
++:100CD00005800400000102030087040000400000BA
++:100CE00000880400000A0B0C0D8904000010121487
++:100CF000008A040000F00100008B040000010000E5
++:100D0000008C040000030000008F040000009E031C
++:100D10000090040000000100009104000000380071
++:100D2000009204000040404000930400000AFF00CD
++:100D3000009504000005F0770096040000FF7F3F57
++:100D4000009A040000000080019B040000000016CF
++:100D5000009C04000000008001A0040000FFFF03CD
++:100D600000A104000000008C11AA0400000104018D
++:100D700000AC04000078000000AE040000BF0000DA
++:100D800000B004000010120000B104000080000058
++:100D900008BA04000000008001BB04000000001637
++:100DA00000BC04000000008001C0040000FFFF033D
++:100DB00000C104000000008C11CA040000010401FD
++:100DC00000CC04000078000000CE040000BF00004A
++:100DD00000D004000010120000D1040000800000C8
++:100DE00008DB04000070700200DE040000FFFFFF5B
++:100DF00003E404000007041200E5040000071509DD
++:100E000005E604000002020105E7040000010203F8
++:100E100000EE04000040000000EF0400000A0B0C8C
++:100E20000DF004000010121400F1040000F00100A5
++:100E300000F204000001000000F3040000030000C1
++:100E400000F6040000009E0300F70400000001000B
++:100E500000F804000000380000F9040000404040A1
++:100E600000FA0400000AFF0000FC04000005F0770F
++:100E700000FD040000FF7F3F004C0500000400005F
++:100E8000004D050000040000004E050000040000B5
++:100E9000004F05000004000000610500000F000085
++:100EA000008305000002000000890500002000000A
++:100EB000009305000067FE1F00FB05000001000015
++:100EC00000FC05000004000000FD05000004000017
++:100ED00000FE05000004000000FF05000004000003
++:100EE00000010600001A00000003060000100000C8
++:100EF00000040600000400000005060000040000D5
++:100F000000060600000400000007060000040000C0
++:100F1000000C060000808060000D060000808060EC
++:100F2000000E060000808060000F060000808060D8
++:100F3000001B060000010000003406000004000051
++:100F40000035060000040000003606000004000022
++:100F50000037060000040000004306000004000003
++:100F6000004B060000000400004C060000040000D6
++:100F7000004D060000040000004E060000040000C2
++:100F8000004F0600000400000053060000000300AC
++:100F90000054060000040000005506000004000094
++:100FA0000056060000040000005706000004000080
++:100FB000005B060000011000005C060000800000DD
++:100FC000005D060000800000005E0600008000005A
++:100FD000005F0600008000000063060000150000AE
++:100FE0000064060000040000006506000004000024
++:100FF0000066060000040000006706000004000010
++:1010000000710600000400000079060000040000E2
++:10101000008906000004000000910600000800009E
++:10102000009306000002000000A1060000FF070078
++:1010300000DB06000001000000E3060000100000D5
++:1010400000F306000001000000090700000F000087
++:10105000008C070000040000008D07000004000061
++:10106000008E070000040000008F0700000400004D
++:1010700000930700001000000094070000800000AB
++:101080000095070000800000009607000080000027
++:101090000097070000800000009C0700000400008B
++:1010A000009D070000040000009E070000040000EF
++:1010B000009F07000004000000A4070000000102D8
++:1010C00003A507000000010203A6070000000102BB
++:1010D00003A707000000010203AC070000030000A3
++:1010E00000AD07000003000000AE07000003000091
++:1010F00000AF07000003000000B407000004000078
++:1011000000B507000004000000B60700000400005E
++:1011100000B707000004000000DC07000004000026
++:1011200000DD07000004000000DE070000040000EE
++:1011300000DF07000004000000E4070000030000D7
++:1011400000E507000003000000E6070000030000C0
++:1011500000E707000003000000040800000400008E
++:10116000000508000004000000060800000400005C
++:101170000007080000040000001B080000000080B9
++:101180003F230800000000803F2B08000000008083
++:101190003F330800000000803F3B08000000008053
++:1011A0003F430800000000803F4B08000000008023
++:1011B0003F530800000000803F5B080000000080F3
++:1011C0003F630800000000803F6B080000000080C3
++:1011D0003F730800000000803F7B08000000008093
++:1011E0003F830800000000803F8B08000000008063
++:1011F0003F930800000000803F63090000100000DA
++:1012000000730900003F000000AB0900000100006E
++:1012100000BB09000001000000CB09000001000034
++:1012200000330A000011000000730A00000F0000E4
++:1012300000B30A000011000000EB0A0000010000EA
++:1012400000F30A000001000000FB0A00000100009A
++:1012500000030B0000020000000B0B000001000067
++:1012600000130B0000020000001B0B000001000037
++:10127000002B0B000067FE1F003B0B00008168ACD9
++:101280000FEB0B000001000000F30B000002000058
++:1012900000FB0B000001000000030C000001000037
++:1012A000000B0C000002000000130C000001000005
++:1012B000001B0C0000010000003B0C0000110000AE
++:1012C00000430C000001000000630F00000200005A
++:1012D00000730F000067FE1F00DB0F00000100001D
++:1012E00000E30F000010000000FB0F0000010000F1
++:1012F0000023100000040000002B10000000040078
++:101300000033100000000300003B1000000110003B
++:1013100000411000000F0000004310000015000005
++:1013200000511000000100000073100000020000D6
++:1013300000BB10000001000000C3100000100000FE
++:1013400000D3100000010000007311000010000025
++:1013500000FB1100000000803F031200000000802D
++:101360003F0B1200000000803F13120000000080BD
++:101370003F1B1200000000803F231200000000808D
++:101380003F2B1200000000803F331200000000805D
++:101390003F3B1200000000803F431200000000802D
++:1013A0003F4B1200000000803F53120000000080FD
++:1013B0003F5B1200000000803F63120000000080CD
++:1013C0003F6B1200000000803F731200000000809D
++:1013D0003F291300000F000000431300001000001D
++:1013E00000531300003F0000008B130000010000B9
++:1013F000009B13000001000000AB1300000100007F
++:10140000001314000011000000531400000F00002E
++:10141000009314000011000000CB14000001000034
++:1014200000D314000001000000DB140000010000E4
++:1014300000E314000002000000EB140000010000B3
++:1014400000F314000002000000FB14000001000083
++:10145000000B15000067FE1F001B1500008168AC23
++:101460000FCB15000001000000D3150000020000A2
++:1014700000DB15000001000000E315000001000082
++:1014800000EB15000002000000F315000001000051
++:1014900000FB150000010000001B160000110000F9
++:1014A00000231600000100000043190000020000A4
++:1014B000005319000067FE1F00BB19000001000067
++:1014C00000C319000010000000DB1900000100003B
++:1014D00000031A0000040000000B1A0000000400C2
++:1014E00000131A0000000300001B1A000001100086
++:1014F00000231A000015000000531A00000200002B
++:10150000009B1A000001000000A31A000010000058
++:1015100000B31A000001000000531B00001000007F
++:1015200000DB1B00000000803FE31B000000008088
++:101530003FEB1B00000000803FF31B000000008019
++:101540003FFB1B00000000803F031C0000000080E8
++:101550003F0B1C00000000803F131C0000000080B7
++:101560003F1B1C00000000803F231C000000008087
++:101570003F2B1C00000000803F331C000000008057
++:101580003F3B1C00000000803F431C000000008027
++:101590003F4B1C00000000803F531C0000000080F7
++:1015A0003F231D000010000000331D00003F00001D
++:1015B000006B1D0000010000007B1D000001000009
++:1015C000008B1D000001000000F31D000011000051
++:1015D00000331E00000F000000731E000011000009
++:1015E00000AB1E000001000000B31E00000100005F
++:1015F00000BB1E000001000000C31E00000200002E
++:1016000000CB1E000001000000D31E0000020000FD
++:1016100000DB1E000001000000EB1E000067FE1F43
++:1016200000FB1E00008168AC0FAB1F000001000032
++:1016300000B31F000002000000BB1F0000010000FB
++:1016400000C31F000001000000CB1F0000020000CB
++:1016500000D31F000001000000DB1F00000100009C
++:1016600000FB1F000011000000032000000100002B
++:1016700000882100002100000090210000010000EE
++:10168000009821000002000000A0210000000100DD
++:1016900000A821000000010000B0210000010000AE
++:1016A00000C821000001000000D02100000200005D
++:1016B00000D821000000010000E02100000001002E
++:1016C00000E82100000100000023230000020000C8
++:1016D000003323000067FE1F009B23000001000071
++:1016E00000A323000010000000BB23000001000045
++:1016F00000E323000004000000EB230000000400CE
++:1017000000F323000000030000FB23000001100091
++:101710000003240000150000003324000002000034
++:10172000007B240000010000008324000010000062
++:101730000093240000010000003325000010000089
++:1017400000BB2500000000803FC325000000008092
++:101750003FCB2500000000803FD325000000008023
++:101760003FDB2500000000803FE3250000000080F3
++:101770003FEB2500000000803FF3250000000080C3
++:101780003FFB2500000000803F0326000000008092
++:101790003F0B2600000000803F1326000000008061
++:1017A0003F1B2600000000803F2326000000008031
++:1017B0003F2B2600000000803F3326000000008001
++:1017C0003F0327000010000000132700003F000027
++:1017D000004B270000010000005B27000001000013
++:1017E000006B27000001000000D32700001100005B
++:1017F00000132800000F0000005328000011000013
++:10180000008B280000010000009328000001000068
++:10181000009B28000001000000A328000002000037
++:1018200000AB28000001000000B328000002000007
++:1018300000BB28000001000000CB28000067FE1F4D
++:1018400000DB2800008168AC0F8B2900000100003C
++:101850000093290000020000009B29000001000005
++:1018600000A329000001000000AB290000020000D5
++:1018700000B329000001000000BB290000010000A6
++:1018800000DB29000011000000E329000001000036
++:1018900000D15400000F0000007955000001000045
++:1018A0000081550000000100008955000000010082
++:1018B000009155000011000000A155000008000033
++:1018C00000D155000001000000E1550000010000BA
++:1018D00000E955000001000000F155000001000082
++:1018E00000F9550000CF0000000156000002000082
++:1018F00000395600000100000049560000010000B8
++:10190000005156000001000000595600000100007F
++:101910000081560000040000009156000001000004
++:10192000009956000015000000B95600008044449C
++:101930000499580000120C1008C158000000010062
++:1019400000D958000001000100E958000001000121
++:1019500000F158000001000000F9580000010001EA
++:1019600000015900000100000009590000040000B6
++:101970000011590000020000002C59000004000072
++:10198000002D590000040000002E59000004000042
++:10199000002F59000004000000345900000300002B
++:1019A0000035590000030000003659000003000014
++:1019B00000375900000300000042590000FFFF3FBC
++:1019C000005A590000FF1F0000C45900000F00001A
++:1019D00000C55900000F000000C65900000F0000AC
++:1019E00000C75900000F000000D25900000000801D
++:1019F0003F0A5A000004000000125A00001A0000BA
++:101A000000245A000004000000255A0000040000D1
++:101A100000265A000004000000275A0000040000BD
++:101A2000002A5A0000010000002C5A0000FFFF00AD
++:101A3000002D5A0000FFFF00002E5A0000FFFF009B
++:101A4000002F5A0000FFFF0000345A0000FFFF0083
++:101A500000355A0000FFFF0000365A0000FFFF006B
++:101A600000375A0000FFFF00003C5A0000FFFF0053
++:101A7000003D5A0000FFFF00003E5A0000FFFF003B
++:101A8000003F5A0000FFFF0000445A0000FFFF0023
++:101A900000455A0000FFFF0000465A0000FFFF000B
++:101AA00000475A0000FFFF00008C5A0000010000B0
++:101AB000008D5A0000010000008E5A000001000055
++:101AC000008F5A000001000000AC5A000001000025
++:101AD00000AD5A000001000000AE5A0000010000F5
++:101AE00000AF5A000001000000C25A000000FFFFD2
++:101AF00000DC5A000001000000DD5A000001000077
++:101B000000DE5A000001000000DF5A000001000062
++:101B100000FA5A00000F0000003A5B00008168AC38
++:101B20000F425B000011000000445B000001000058
++:101B300000455B000001000000465B000001000062
++:101B400000475B0000010000004C5B00000100004A
++:101B5000004D5B0000010000004E5B000001000032
++:101B6000004F5B000001000000545B000002000019
++:101B700000555B000002000000565B000002000000
++:101B800000575B0000020000005C5B0000010000E9
++:101B9000005D5B0000010000005E5B0000010000D2
++:101BA000005F5B000001000000645B0000010000BA
++:101BB00000655B000001000000665B0000010000A2
++:101BC00000675B0000010000006C5B000002000089
++:101BD000006D5B0000020000006E5B000002000070
++:101BE000006F5B000002000000745B000001000059
++:101BF00000755B000001000000765B000001000042
++:101C000000775B000001000000845B000011000011
++:101C100000855B000011000000865B0000110000E1
++:101C200000875B000011000000C25B0000040000A0
++:101C300000C45B00008168AC0FC55B00008168AC2C
++:101C40000FC65B00008168AC0FC75B00008168AC09
++:101C50000FDC5B000004000000DD5B0000040000FE
++:101C600000DE5B000004000000DF5B0000040000F9
++:101C700000EA5B000002000000F25B0000000000D0
++:101C800004FA5B000000000004FC5B00001100008F
++:101C900000FD5B000011000000FE5B000011000071
++:101CA00000FF5B000011000000045C000001000068
++:101CB00000055C000001000000065C00000100005F
++:101CC00000075C000001000000145C0000CF000071
++:101CD00000155C0000CF000000165C0000CF000083
++:101CE00000175C0000CF0000001A5C000005000037
++:101CF000001C5C0000CF0000001D5C0000CF000055
++:101D0000001E5C0000CF0000001F5C0000CF000040
++:101D100000225C000052000000245C0000CF0000A4
++:101D200000255C0000CF000000265C0000CF000012
++:101D300000275C0000CF0000004A5C0000010000AA
++:101D4000007C5C0000010000007D5C0000010000E0
++:101D5000007E5C0000010000007F5C0000010000CC
++:101D600000845C000001000000855C0000010000B0
++:101D700000865C000001000000875C00000100009C
++:101D8000008C5C0000020000008D5C00000200007E
++:101D9000008E5C0000020000008F5C00000200006A
++:101DA00000945C000001000000955C000001000050
++:101DB00000965C000001000000975C00000100003C
++:101DC000009C5C0000010000009D5C000001000020
++:101DD000009E5C0000010000009F5C00000100000C
++:101DE00000A45C000002000000A55C0000020000EE
++:101DF00000A65C000002000000A75C0000020000DA
++:101E000000AC5C000001000000AD5C0000010000BF
++:101E100000AE5C000001000000AF5C0000010000AB
++:101E200000BC5C000001000000BD5C00000100007F
++:101E300000BE5C000001000000BF5C00000100006B
++:101E400000C45C000001000000C55C00000100004F
++:101E500000C65C000001000000C75C00000100003B
++:101E600000CC5C000001000000CD5C00000100001F
++:101E700000CE5C000001000000CF5C00000100000B
++:101E800000D25C00000000803FD45C000001000034
++:101E900000D55C000001000000D65C0000010000DD
++:101EA00000D75C000001000000DA5C000000008048
++:101EB0003FDC5C000001000000DD5C000001000070
++:101EC00000DE5C000001000000DF5C00000100009B
++:101ED00000E25C00000000803FE45C0000010000C4
++:101EE00000E55C000001000000E65C00000100006D
++:101EF00000E75C000001000000EA5C0000000080D8
++:101F00003FEC5C000001000000ED5C0000010000FF
++:101F100000EE5C000001000000EF5C00000100002A
++:101F200000F25C00000000803FF45C000001000053
++:101F300000F55C000001000000F65C0000010000FC
++:101F400000F75C000001000000FA5C000000008067
++:101F50003FFC5C000011000000FD5C00001100006F
++:101F600000FE5C000011000000FF5C00001100009A
++:101F700000025D00000000803F0A5D00000000805C
++:101F80003F125D00000000803F1A5D0000000080ED
++:101F90003F225D00000000803F2A5D0000000080BD
++:101FA0003F325D00000000803F3A5D00000000808D
++:101FB0003F3C5D00008168AC0F3D5D00008168AC76
++:101FC0000F3E5D00008168AC0F3F5D00008168AC92
++:101FD0000F425D00000000803F445D00000F0000E4
++:101FE00000455D00000F000000465D00000F00008E
++:101FF00000475D00000F0000004A5D000000008007
++:102000003F525D000010000000845D000067FE1F6D
++:1020100000855D000067FE1F00865D000067FE1FF3
++:1020200000875D000067FE1F009C5D00001100003E
++:10203000009D5D0000110000009E5D000011000089
++:10204000009F5D000011000000A45D000001000081
++:1020500000A55D000001000000A65D000001000079
++:1020600000A75D000001000000C45D000004000046
++:1020700000C55D000004000000C65D000004000013
++:1020800000C75D000004000000F45D0000010000D6
++:1020900000F55D000001000000F65D000001000099
++:1020A00000F75D0000010000001C5E000011000050
++:1020B000001D5E0000110000001E5E000011000007
++:1020C000001F5E0000110000005C5E00008168AC33
++:1020D0000F5D5E00008168AC0F5E5E00008168AC41
++:1020E0000F5F5E00008168AC0F7C5E000011000095
++:1020F000007D5E0000110000007E5E000011000007
++:10210000007F5E000011000000845E0000010000FE
++:1021100000855E000001000000865E0000010000F6
++:1021200000875E0000010000008A5E0000120C10B3
++:1021300008925E000005000000945E0000010000AF
++:1021400000955E000001000000965E0000010000A6
++:1021500000975E000001000000A45E000001000086
++:1021600000A55E000001000000A65E000001000066
++:1021700000A75E000001000000AA5E000001000050
++:1021800000B45E0000FF070000B55E0000FF07001E
++:1021900000B65E0000FF070000B75E0000FF07000A
++:1021A00000BA5E0000FFFF0000C25E0000FFFF00FB
++:1021B00000C45E000001000000C55E0000010000D8
++:1021C00000C65E000001000000C75E0000010000C4
++:1021D00000CA5E0000FFFF0000D25E0000FFFF00AB
++:1021E00000D45E000001000000D55E000001000088
++:1021F00000D65E000001000000D75E000001000074
++:1022000000DA5E000003000000DA5F000000FFFF5C
++:1022100000E25F00001A000000F25F00000300000F
++:10222000002C600000080000002D60000008000085
++:10223000002E600000080000002F60000008000071
++:102240000034600000080000003560000008000055
++:102250000036600000080000003760000008000041
++:10226000003C600000080000003D60000008000025
++:10227000003E600000080000003F60000008000011
++:1022800000446000000800000045600000080000F5
++:1022900000466000000800000047600000080000E1
++:1022A000004C600000080000004D600000080000C5
++:1022B000004E600000080000004F600000080000B1
++:1022C0000054600000080000005560000008000095
++:1022D0000056600000080000005760000008000081
++:1022E000005C600000080000005D60000008000065
++:1022F000005E600000080000005F60000008000051
++:102300000064600000080000006560000008000034
++:102310000066600000080000006760000008000020
++:10232000006C600000110000006D600000110000F2
++:10233000006E600000110000006F600000110000DE
++:1023400000AC6000008168AC0FAD6000008168AC3B
++:102350000FAE6000008168AC0FAF6000008168AC18
++:102360000FB460000000040000B56000000004002D
++:1023700000B660000000040000B760000000040028
++:1023800000BC60000000040000BD6000000004000C
++:1023900000BE60000000040000BF600000000400F8
++:1023A00000C460000000040000C5600000000400DC
++:1023B00000C660000000040000C7600000000400C8
++:1023C00000CC60000000040000CD600000000400AC
++:1023D00000CE60000000040000CF60000000040098
++:1023E00000D460000000040000D56000000004007C
++:1023F00000D660000000040000D760000000040068
++:1024000000DC60000000040000DD6000000004004B
++:1024100000DE60000000040000DF60000000040037
++:1024200000E460000000040000E56000000004001B
++:1024300000E660000000040000E760000000040007
++:1024400000EC60000000040000ED600000000400EB
++:1024500000EE60000000040000EF600000000400D7
++:1024600000F1600000DFBFE304F46000000003003F
++:1024700000F560000000030000F6600000000300AB
++:1024800000F760000000030000F9600000DFBFE318
++:1024900004FC60000000030000FD60000000030079
++:1024A00000FE60000000030000FF60000000030069
++:1024B000000461000000030000056100000003004B
++:1024C0000006610000000300000761000000030037
++:1024D000000C610000000300000D6100000003001B
++:1024E000000E610000000300000F61000000030007
++:1024F00000146100000003000015610000000300EB
++:1025000000166100000003000017610000000300D6
++:10251000001C610000000300001D610000000300BA
++:10252000001E610000000300001F610000000300A6
++:1025300000216100008168AC0F24610000000300ED
++:102540000025610000000300002661000000030078
++:102550000027610000000300002A61000002010062
++:10256000002C610000000300002D6100000003004A
++:10257000002E610000000300002F61000000030036
++:10258000003461000001000000356100000100001E
++:10259000003661000001000000376100000100000A
++:1025A000003A610000040000003C6100000F0000E0
++:1025B000003D6100000F0000003E6100000F0000C0
++:1025C000003F6100000F00000042610000040000B5
++:1025D000004A610000040000005261000004000095
++:1025E000005A610000040000006261000004000065
++:1025F0000072610000FF0700007C61000020000005
++:10260000007D610000200000007E610000200000CD
++:10261000007F6100002000000081610000DFBFE357
++:1026200004826100000201000084610000110000CA
++:1026300000856100001100000086610000110000AB
++:1026400000876100001100000089610000DFBFE326
++:10265000048C610000000100008D61000000010099
++:10266000008E610000000100008F61000000010089
++:10267000009C610000010000009D6100000100005D
++:10268000009E610000010000009F61000001000049
++:1026900000B461000040000000B56100004000008F
++:1026A00000B661000040000000B76100004000007B
++:1026B00000BC61000000010000BD610000000100DD
++:1026C00000BE61000000010000BF610000000100C9
++:1026D00000CC61000003000000CD61000003000099
++:1026E00000CE61000003000000CF61000003000085
++:1026F00000D261000004000000DA61000004000064
++:1027000000E261000004000000EA61000004000033
++:1027100000F461000067FE1F00F561000067FE1F06
++:1027200000F661000067FE1F00F761000067FE1FF2
++:1027300000146200000200000015620000020000A8
++:102740000016620000020000001762000002000094
++:10275000001C6200008168AC0F1D6200008168AC43
++:102760000F1E6200008168AC0F1F6200008168AC20
++:102770000F6C620000010000006D620000010000AB
++:10278000006E620000010000006F620000010000A6
++:102790000094620000040000009562000004000044
++:1027A0000096620000040000009762000004000030
++:1027B00000A462000001000000A56200000100000A
++:1027C00000A662000001000000A7620000010000F6
++:1027D00000AC62000000040000AD620000000400D4
++:1027E00000AE62000000040000AF620000000400C0
++:1027F00000B462000000030000B5620000000300A6
++:1028000000B662000000030000B762000000030091
++:1028100000BC62000001100000BD62000001100059
++:1028200000BE62000001100000BF62000001100045
++:1028300000DC62000011000000DD620000110000F9
++:1028400000DE62000011000000DF620000110000E5
++:10285000001C6300008168AC0F1D6300008168AC40
++:102860000F1E6300008168AC0F1F6300008168AC1D
++:102870000F246300000F000000256300000F00001C
++:1028800000266300000F000000276300000F000017
++:10289000007A630000140C08009263000004080032
++:1028A00000A263000004000000AA6300000400000E
++:1028B00000B2630000120C1008C2630000040000A4
++:1028C00000CA63000004000000DA6300001000008A
++:1028D00000E463000067FE1F00E563000067FE1F61
++:1028E00000E663000067FE1F00E763000067FE1F4D
++:1028F00000026400000408000004640000110000ED
++:1029000000056400001100000006640000110000D2
++:102910000007640000110000000A640000010000CC
++:1029200000126400001A0000001A6400007F00001A
++:10293000001C640000040000001D6400000400008E
++:10294000001E640000040000001F6400000400007A
++:10295000002A640000010000002C64000001000057
++:10296000002D640000010000002E64000001000042
++:10297000002F6400000100000032640000140C0805
++:102980000034640000010000003564000001000014
++:102990000036640000010000003764000001000000
++:1029A0000042640000120C10084A64000004000099
++:1029B00000526400000400000054640000010000A4
++:1029C0000055640000010000005664000001000092
++:1029D0000057640000010000006264000010000065
++:1029E000007C640000010000007D64000001000024
++:1029F000007E640000010000007F64000001000010
++:102A00000082640000010000008A640000120C10C3
++:102A1000088C640000010000008D640000010000CB
++:102A2000008E640000010000008F640000010000BF
++:102A300000AC6400008824712AAD64000088247111
++:102A40002AAE6400008824712AAF640000882471D3
++:102A50002ABC64000000C08540BD64000000C08541
++:102A600040BE64000000C08540BF64000000C08517
++:102A700040C2640000FF070000C464000040000082
++:102A800000C564000040000000C664000040000073
++:102A900000C764000040000000CA640000140C0875
++:102AA00000CC64000000010000CD640000000100C3
++:102AB00000CE64000000010000CF640000000100AF
++:102AC00000D464000000010100D564000000010191
++:102AD00000D664000000010100D76400000001017D
++:102AE00000DC64000000008002DD64000000008063
++:102AF00002DE64000000008002DF6400000000804D
++:102B00000274650000DFBFE30475650000DFBFE30A
++:102B10000476650000DFBFE30477650000DFBFE3F4
++:102B2000047C650000DFBFE3047D650000DFBFE3D8
++:102B3000047E650000DFBFE3047F650000DFBFE3C4
++:102B400004846500000100000085650000010000AC
++:102B5000008665000001000000876500000100009C
++:102B6000009465000000FFFF009565000000FFFF76
++:102B7000009665000000FFFF009765000000FFFF62
++:102B8000009C650000010000009D65000001000040
++:102B9000009E650000010000009F6500000100002C
++:102BA00000B465000000FFFF00B565000000FFFFF6
++:102BB00000B665000000FFFF00B765000000FFFFE2
++:102BC00000FC65000001000000FD65000001000040
++:102BD00000FE65000001000000FF6500000100002C
++:102BE000000C660000010000000D660000010000FE
++:102BF000000E660000010000000F660000010000EA
++:102C0000001466000000102030156600000010203F
++:102C100030166600000010203017660000001020FB
++:102C2000301C660000405060701D6600004050601F
++:102C3000701E660000405060701F660000405060CB
++:102C400070246600008898A8B8256600008898A8B7
++:102C5000B8266600008898A8B8276600008898A85B
++:102C6000B82C660000C8D8E8F82D660000C8D8E87F
++:102C7000F82E660000C8D8E8F82F660000C8D8E82B
++:102C8000F83C6600001A0000003D6600001A0000D3
++:102C9000003E6600001A0000003F6600001A0000B7
++:102CA000004C660000040000004D660000040000B7
++:102CB000004E660000040000004F660000040000A3
++:102CC000009266000001000000AA660000100000EB
++:102CD00000FC66000004000000FD66000004000027
++:102CE00000FE66000004000000FF66000004000013
++:102CF00000046700000400000005670000040000F5
++:102D000000066700000400000007670000040000E0
++:102D1000000C670000808060000D6700008080600C
++:102D2000000E670000808060000F670000808060F8
++:102D30000034670000040000003567000004000054
++:102D40000036670000040000003767000004000040
++:102D5000004C670000040000004D67000004000004
++:102D6000004E670000040000004F670000040000F0
++:102D700000546700000400000055670000040000D4
++:102D800000566700000400000057670000040000C0
++:102D9000005C670000800000005D670000800000AC
++:102DA000005E670000800000005F67000080000098
++:102DB0000064670000040000006567000004000074
++:102DC0000066670000040000006767000004000060
++:102DD0000072680000880000007A68000088000027
++:102DE000008C680000040000008D680000040000F2
++:102DF000008E680000040000008F680000040000DE
++:102E00000092680000040000009468000080000048
++:102E100000956800008000000096680000800000B7
++:102E20000097680000800000009C6800000400001B
++:102E3000009D680000040000009E6800000400007F
++:102E4000009F68000004000000A468000000010268
++:102E500003A568000000010203A66800000001024B
++:102E600003A768000000010203AC68000003000033
++:102E700000AD68000003000000AE68000003000021
++:102E800000AF68000003000000B468000004000008
++:102E900000B568000004000000B6680000040000EF
++:102EA00000B768000004000000DC680000040000B7
++:102EB00000DD68000004000000DE6800000400007F
++:102EC00000DF68000004000000E468000003000068
++:102ED00000E568000003000000E668000003000051
++:102EE00000E768000003000000046900000400001F
++:102EF00000056900000400000006690000040000ED
++:102F00000007690000040000004A69000026000074
++:102F100000626900000000803F826900001A000022
++:102F2000008A69000010000000D26A000052000010
++:102F300000E26A000026000000F26A0000040000BF
++:102F400000FA6A0000040000000A6B00001A00008A
++:102F500000226B000000FFFF00326B000004000045
++:102F6000003A6B0000040000004A6B000080000083
++:102F700000526B0000040000005A6B0000140C08A3
++:102F8000006A6B0000FF070000306E0000040000C4
++:102F900000386E00000400000082B500000400004C
++:102FA000008AB50000040000009AB500008000000F
++:102FB00000A2B5000004000000AAB5000001000056
++:102FC00000BAB5000027000000CAB50000260000C6
++:102FD00000EAB5000000000004F2B50000000000A7
++:102FE00004FAB500000000000402B6000000000072
++:102FF000040AB600000000000412B6000000000041
++:10300000041AB600000000000422B6000000000010
++:10301000042AB600000000000432B60000000000E0
++:10302000043AB600000000000442B60000000000B0
++:10303000044AB600000000000452B6000000000080
++:10304000045AB600000000000462B6000000000050
++:10305000048AB70000DFBFE30492B70000DFBFE3DC
++:1030600004AAB7000021FE01002CBA0000040000F1
++:10307000002DBA0000040000002EBA000004000079
++:10308000002FBA00000400000034BA000003000062
++:103090000035BA00000300000036BA00000300004B
++:1030A0000037BA000003000000C4BA00000F00009F
++:1030B00000C5BA00000F000000C6BA00000F0000F3
++:1030C00000C7BA00000F00000024BB00000400008D
++:1030D0000025BB00000400000026BB000004000027
++:1030E0000027BB0000040000002CBB0000FFFF0015
++:1030F000002DBB0000FFFF00002EBB0000FFFF0003
++:10310000002FBB0000FFFF000034BB0000FFFF00EA
++:103110000035BB0000FFFF000036BB0000FFFF00D2
++:103120000037BB0000FFFF00003CBB0000FFFF00BA
++:10313000003DBB0000FFFF00003EBB0000FFFF00A2
++:10314000003FBB0000FFFF000044BB0000FFFF008A
++:103150000045BB0000FFFF000046BB0000FFFF0072
++:103160000047BB0000FFFF00008CBB000001000017
++:10317000008DBB0000010000008EBB0000010000BC
++:10318000008FBB000001000000ACBB00000100008C
++:1031900000ADBB000001000000AEBB00000100005C
++:1031A00000AFBB000001000000DCBB00000100001C
++:1031B00000DDBB000001000000DEBB0000010000DC
++:1031C00000DFBB00000100000044BC000001000063
++:1031D0000045BC00000100000046BC0000010000EA
++:1031E0000047BC0000010000004CBC0000010000D2
++:1031F000004DBC0000010000004EBC0000010000BA
++:10320000004FBC00000100000054BC0000020000A0
++:103210000055BC00000200000056BC000002000087
++:103220000057BC0000020000005CBC000001000070
++:10323000005DBC0000010000005EBC000001000059
++:10324000005FBC00000100000064BC000001000041
++:103250000065BC00000100000066BC000001000029
++:103260000067BC0000010000006CBC000002000010
++:10327000006DBC0000020000006EBC0000020000F7
++:10328000006FBC00000200000074BC0000010000E0
++:103290000075BC00000100000076BC0000010000C9
++:1032A0000077BC00000100000084BC000011000099
++:1032B0000085BC00001100000086BC000011000069
++:1032C0000087BC000011000000C4BC00008168AC95
++:1032D0000FC5BC00008168AC0FC6BC00008168ACA3
++:1032E0000FC7BC00008168AC0FDCBC00000400000C
++:1032F00000DDBC000004000000DEBC000004000093
++:1033000000DFBC000004000000FCBC000011000055
++:1033100000FDBC000011000000FEBC000011000018
++:1033200000FFBC00001100000004BD00000100000F
++:103330000005BD00000100000006BD000001000006
++:103340000007BD00000100000014BD0000CF000018
++:103350000015BD0000CF00000016BD0000CF00002A
++:103360000017BD0000CF0000001CBD0000CF000012
++:10337000001DBD0000CF0000001EBD0000CF0000FA
++:10338000001FBD0000CF00000024BD0000CF0000E2
++:103390000025BD0000CF00000026BD0000CF0000CA
++:1033A0000027BD0000CF0000007CBD000001000030
++:1033B000007DBD0000010000007EBD000001000096
++:1033C000007FBD00000100000084BD00000100007E
++:1033D0000085BD00000100000086BD000001000066
++:1033E0000087BD0000010000008CBD00000200004D
++:1033F000008DBD0000020000008EBD000002000034
++:10340000008FBD00000200000094BD00000100001C
++:103410000095BD00000100000096BD000001000005
++:103420000097BD0000010000009CBD0000010000ED
++:10343000009DBD0000010000009EBD0000010000D5
++:10344000009FBD000001000000A4BD0000020000BC
++:1034500000A5BD000002000000A6BD0000020000A3
++:1034600000A7BD000002000000ACBD00000100008C
++:1034700000ADBD000001000000AEBD000001000075
++:1034800000AFBD000001000000BCBD000001000055
++:1034900000BDBD000001000000BEBD000001000035
++:1034A00000BFBD000001000000C4BD00000100001D
++:1034B00000C5BD000001000000C6BD000001000005
++:1034C00000C7BD000001000000CCBD0000010000ED
++:1034D00000CDBD000001000000CEBD0000010000D5
++:1034E00000CFBD000001000000D4BD0000010000BD
++:1034F00000D5BD000001000000D6BD0000010000A5
++:1035000000D7BD000001000000DCBD00000100008C
++:1035100000DDBD000001000000DEBD000001000074
++:1035200000DFBD000001000000E4BD00000100005C
++:1035300000E5BD000001000000E6BD000001000044
++:1035400000E7BD000001000000ECBD00000100002C
++:1035500000EDBD000001000000EEBD000001000014
++:1035600000EFBD000001000000F4BD0000010000FC
++:1035700000F5BD000001000000F6BD0000010000E4
++:1035800000F7BD000001000000FCBD0000110000BC
++:1035900000FDBD000011000000FEBD000011000094
++:1035A00000FFBD0000110000003CBE00008168ACBF
++:1035B0000F3DBE00008168AC0F3EBE00008168ACCC
++:1035C0000F3FBE00008168AC0F44BE00000F00003A
++:1035D0000045BE00000F00000046BE00000F0000C6
++:1035E0000047BE00000F00000084BE000067FE1F01
++:1035F0000085BE000067FE1F0086BE000067FE1F3C
++:103600000087BE000067FE1F009CBE000011000086
++:10361000009DBE0000110000009EBE0000110000D1
++:10362000009FBE000011000000A4BE0000010000C9
++:1036300000A5BE000001000000A6BE0000010000C1
++:1036400000A7BE000001000000C4BE00000400008E
++:1036500000C5BE000004000000C6BE00000400005B
++:1036600000C7BE000004000000F4BE00000100001E
++:1036700000F5BE000001000000F6BE0000010000E1
++:1036800000F7BE0000010000001CBF000011000098
++:10369000001DBF0000110000001EBF00001100004F
++:1036A000001FBF0000110000005CBF00008168AC7B
++:1036B0000F5DBF00008168AC0F5EBF00008168AC89
++:1036C0000F5FBF00008168AC0F7CBF0000110000DD
++:1036D000007DBF0000110000007EBF00001100004F
++:1036E000007FBF00001100000084BF000001000047
++:1036F0000085BF00000100000086BF00000100003F
++:103700000087BF00000100000094BF00000100001E
++:103710000095BF00000100000096BF0000010000FE
++:103720000097BF000001000000A4BF0000010000DE
++:1037300000A5BF000001000000A6BF0000010000BE
++:1037400000A7BF000001000000B4BF0000FF070099
++:1037500000B5BF0000FF070000B6BF0000FF070074
++:1037600000B7BF0000FF070000C4BF000001000059
++:1037700000C5BF000001000000C6BF00000100003E
++:1037800000C7BF000001000000D4BF00000100001E
++:1037900000D5BF000001000000D6BF0000010000FE
++:1037A00000D7BF0000010000002CC100000800008D
++:1037B000002DC10000080000002EC100000800001C
++:1037C000002FC100000800000034C1000008000004
++:1037D0000035C100000800000036C10000080000EC
++:1037E0000037C10000080000003CC10000080000D4
++:1037F000003DC10000080000003EC10000080000BC
++:10380000003FC100000800000044C10000080000A3
++:103810000045C100000800000046C100000800008B
++:103820000047C10000080000004CC1000008000073
++:10383000004DC10000080000004EC100000800005B
++:10384000004FC100000800000054C1000008000043
++:103850000055C100000800000056C100000800002B
++:103860000057C10000080000005CC1000008000013
++:10387000005DC10000080000005EC10000080000FB
++:10388000005FC100000800000064C10000080000E3
++:103890000065C100000800000066C10000080000CB
++:1038A0000067C10000080000006CC10000110000AA
++:1038B000006DC10000110000006EC1000011000089
++:1038C000006FC1000011000000ACC100008168ACB5
++:1038D0000FADC100008168AC0FAEC100008168ACC3
++:1038E0000FAFC100008168AC0FB4C100000004003C
++:1038F00000B5C1000000040000B6C10000000400D3
++:1039000000B7C1000000040000BCC10000000400BA
++:1039100000BDC1000000040000BEC10000000400A2
++:1039200000BFC1000000040000C4C100000004008A
++:1039300000C5C1000000040000C6C1000000040072
++:1039400000C7C1000000040000CCC100000004005A
++:1039500000CDC1000000040000CEC1000000040042
++:1039600000CFC1000000040000D4C100000004002A
++:1039700000D5C1000000040000D6C1000000040012
++:1039800000D7C1000000040000DCC10000000400FA
++:1039900000DDC1000000040000DEC10000000400E2
++:1039A00000DFC1000000040000E4C10000000400CA
++:1039B00000E5C1000000040000E6C10000000400B2
++:1039C00000E7C1000000040000ECC100000004009A
++:1039D00000EDC1000000040000EEC1000000040082
++:1039E00000EFC1000000040000F4C100000003006B
++:1039F00000F5C1000000030000F6C1000000030054
++:103A000000F7C1000000030000FCC100000003003B
++:103A100000FDC1000000030000FEC1000000030023
++:103A200000FFC100000003000004C200000003000A
++:103A30000005C200000003000006C20000000300F1
++:103A40000007C20000000300000CC20000000300D9
++:103A5000000DC20000000300000EC20000000300C1
++:103A6000000FC200000003000014C20000000300A9
++:103A70000015C200000003000016C2000000030091
++:103A80000017C20000000300001CC2000000030079
++:103A9000001DC20000000300001EC2000000030061
++:103AA000001FC200000003000024C2000000030049
++:103AB0000025C200000003000026C2000000030031
++:103AC0000027C20000000300002CC2000000030019
++:103AD000002DC20000000300002EC2000000030001
++:103AE000002FC200000003000034C20000010000EB
++:103AF0000035C200000100000036C20000010000D5
++:103B00000037C20000010000003CC200000F0000AE
++:103B1000003DC200000F0000003EC200000F000088
++:103B2000003FC200000F0000007CC2000020000027
++:103B3000007DC20000200000007EC20000200000C6
++:103B4000007FC200002000000084C20000110000BD
++:103B50000085C200001100000086C20000110000B4
++:103B60000087C20000110000008CC20000000100AC
++:103B7000008DC20000000100008EC20000000100A4
++:103B8000008FC20000000100009CC2000001000084
++:103B9000009DC20000010000009EC2000001000064
++:103BA000009FC2000001000000B4C20000400000FD
++:103BB00000B5C2000040000000B6C2000040000096
++:103BC00000B7C2000040000000BCC20000000100BD
++:103BD00000BDC2000000010000BEC20000000100E4
++:103BE00000BFC2000000010000CCC20000030000C2
++:103BF00000CDC2000003000000CEC20000030000A0
++:103C000000CFC2000003000000F4C2000067FE1FE6
++:103C100000F5C2000067FE1F00F6C2000067FE1F2D
++:103C200000F7C2000067FE1F0014C300000200007E
++:103C30000015C300000200000016C30000020000CF
++:103C40000017C30000020000001CC300008168AC24
++:103C50000F1DC300008168AC0F1EC300008168AC5B
++:103C60000F1FC300008168AC0F6CC300000100008F
++:103C7000006DC30000010000006EC30000010000E1
++:103C8000006FC300000100000094C30000040000A6
++:103C90000095C300000400000096C300000400006B
++:103CA0000097C3000004000000A4C300000100004E
++:103CB00000A5C3000001000000A6C3000001000031
++:103CC00000A7C3000001000000ACC3000000040016
++:103CD00000ADC3000000040000AEC30000000400FB
++:103CE00000AFC3000000040000B4C30000000300E4
++:103CF00000B5C3000000030000B6C30000000300CD
++:103D000000B7C3000000030000BCC30000011000A6
++:103D100000BDC3000001100000BEC3000001100080
++:103D200000BFC3000001100000DCC3000011000050
++:103D300000DDC3000011000000DEC3000011000020
++:103D400000DFC30000110000001CC400008168AC4B
++:103D50000F1DC400008168AC0F1EC400008168AC58
++:103D60000F1FC400008168AC0F24C400000F0000C6
++:103D70000025C400000F00000026C400000F000052
++:103D80000027C400000F000000E4C4000067FE1F0D
++:103D900000E5C4000067FE1F00E6C4000067FE1FC8
++:103DA00000E7C4000067FE1F0004C500001100000A
++:103DB0000005C500001100000006C500001100004C
++:103DC0000007C50000110000001CC5000004000031
++:103DD000001DC50000040000001EC5000004000016
++:103DE000001FC50000040000002CC50000010000F9
++:103DF000002DC50000010000002EC50000010000DC
++:103E0000002FC500000100000034C50000010000C3
++:103E10000035C500000100000036C50000010000AB
++:103E20000037C500000100000054C500000100007B
++:103E30000055C500000100000056C500000100004B
++:103E40000057C50000010000007CC5000001000013
++:103E5000007DC50000010000007EC50000010000DB
++:103E6000007FC50000010000008CC50000010000BB
++:103E7000008DC50000010000008EC500000100009B
++:103E8000008FC5000001000000ACC500008824714F
++:103E90002AADC500008824712AAEC50000882471AF
++:103EA0002AAFC500008824712ABCC5000000C08567
++:103EB00040BDC5000000C08540BEC5000000C085F3
++:103EC00040BFC5000000C08540C4C50000400000E0
++:103ED00000C5C5000040000000C6C500004000004D
++:103EE00000C7C5000040000000CCC5000000010074
++:103EF00000CDC5000000010000CEC500000001009B
++:103F000000CFC5000000010000D4C5000000010181
++:103F100000D5C5000000010100D6C5000000010168
++:103F200000D7C5000000010100DCC50000000080D2
++:103F300002DDC5000000008002DEC5000000008038
++:103F400002DFC500000000800274C60000DFBFE38E
++:103F50000475C60000DFBFE30476C60000DFBFE3E0
++:103F60000477C60000DFBFE3047CC60000DFBFE3C8
++:103F7000047DC60000DFBFE3047EC60000DFBFE3B0
++:103F8000047FC60000DFBFE30484C6000001000018
++:103F90000085C600000100000086C6000001000088
++:103FA0000087C600000100000094C6000000FFFF6B
++:103FB0000095C6000000FFFF0096C6000000FFFF4E
++:103FC0000097C6000000FFFF009CC6000001000033
++:103FD000009DC60000010000009EC6000001000018
++:103FE000009FC6000001000000B4C6000000FFFFF3
++:103FF00000B5C6000000FFFF00B6C6000000FFFFCE
++:1040000000B7C6000000FFFF00FCC6000001000072
++:1040100000FDC6000001000000FEC6000001000017
++:1040200000FFC60000010000000CC70000010000F6
++:10403000000DC70000010000000EC70000010000D5
++:10404000000FC700000100000014C700000010208E
++:104050003015C700000010203016C70000001020E7
++:104060003017C70000001020301CC700004050600F
++:10407000701DC70000405060701EC70000405060B7
++:10408000701FC700004050607024C700008898A8C7
++:10409000B825C700008898A8B826C700008898A847
++:1040A000B827C700008898A8B82CC70000C8D8E86F
++:1040B000F82DC70000C8D8E8F82EC70000C8D8E817
++:1040C000F82FC70000C8D8E8F83CC700001A000065
++:1040D000003DC700001A0000003EC700001A0000A3
++:1040E000003FC700001A00000098500100040000C3
++:1040F00000A050010004000000A8500100120C10A4
++:1041000008B050010003000000C0500100120C1064
++:1041100008D0500100140C0800D850010001000024
++:1041200000E0500100140C0800F8500100120C10BF
++:104130000800510100270000001851010001000093
++:1041400000286F010001000000E06F0100120C1058
++:1041500008487001000000000450700100000000D9
++:104160000460700100800000008070010080000089
++:1041700000907001003F000000E8700100020000A4
++:1041800000F070010000000004F870010000000061
++:10419000044071010004000000607101000400008F
++:1041A00000F071010001000000F871010001100031
++:1041B0000000720100FFFF000008720100FFFF0015
++:1041C0000010720100FFFF000018720100FFFF00E5
++:1041D00000207301000000803F2873010000008070
++:1041E0003F307301000000803F3873010000008001
++:1041F0003F407301000000803F48730100000080D1
++:104200003F507301000000803F58730100000080A0
++:104210003F607301000000803F6873010000008070
++:104220003F707301000000803F7873010000008040
++:104230003F807301000000803F8873010000008010
++:104240003F907301000000803F98730100000080E0
++:104250003FA073010010000000B8730100030000CC
++:104260000000740100120C100809740100800000A5
++:10427000000A740100800000000B7401008000003F
++:10428000000C740100800000000D7401008000002B
++:10429000000E740100800000000F74010080000017
++:1042A000001074010080000000117401000470000F
++:1042B0008012740100047000801374010004700007
++:1042C00080147401000470008015740100047000F3
++:1042D00080167401000470008017740100047000DF
++:1042E000801874010004700080197401000004003B
++:1042F000041A740100000400041B7401000004008F
++:10430000041C740100000400041D7401000004007A
++:10431000041E740100000400041F74010000040066
++:104320000420740100000400042174010000100046
++:10433000002274010000100000237401000010002E
++:10434000002474010000100000257401000010001A
++:104350000026740100001000002774010000100006
++:1043600000287401000010000039740100010000F1
++:10437000003A740100010000003B740100010000DC
++:10438000003C740100010000003D740100010000C8
++:10439000003E740100010000003F740100010000B4
++:1043A0000040740100010000005174010001000090
++:1043B000005274010001000000537401000100006C
++:1043C0000054740100010000005574010001000058
++:1043D0000056740100010000005774010001000044
++:1043E0000058740100010000006174010004000025
++:1043F0000062740100040000006374010004000006
++:1044000000647401000400000065740100040000F1
++:1044100000667401000400000067740100040000DD
++:1044200000687401000400000069740100020000CB
++:10443000006A740100020000006B740100020000B9
++:10444000006C740100020000006D740100020000A5
++:10445000006E740100020000006F74010002000091
++:10446000007074010002000000C9740100800000A7
++:1044700000CA74010080000000CB740100800000BD
++:1044800000CC74010080000000CD740100800000A9
++:1044900000CE74010080000000CF74010080000095
++:1044A00000D074010080000000D17401000470008D
++:1044B00080D274010004700080D374010004700085
++:1044C00080D474010004700080D574010004700071
++:1044D00080D674010004700080D77401000470005D
++:1044E00080D874010004700080D9740100000400B9
++:1044F00004DA74010000040004DB7401000004000D
++:1045000004DC74010000040004DD740100000400F8
++:1045100004DE74010000040004DF740100000400E4
++:1045200004E074010000040004E1740100001000C4
++:1045300000E274010000100000E3740100001000AC
++:1045400000E474010000100000E574010000100098
++:1045500000E674010000100000E774010000100084
++:1045600000E874010000100000F97401000100006F
++:1045700000FA74010001000000FB7401000100005A
++:1045800000FC74010001000000FD74010001000046
++:1045900000FE74010001000000FF74010001000032
++:1045A000000075010001000000117501000100000C
++:1045B00000127501000100000013750100010000E8
++:1045C00000147501000100000015750100010000D4
++:1045D00000167501000100000017750100010000C0
++:1045E00000187501000100000021750100040000A1
++:1045F0000022750100040000002375010004000082
++:10460000002475010004000000257501000400006D
++:104610000026750100040000002775010004000059
++:104620000028750100040000002975010002000047
++:10463000002A750100020000002B75010002000035
++:10464000002C750100020000002D75010002000021
++:10465000002E750100020000002F7501000200000D
++:1046600000307501000200000081750100120C107D
++:104670000882750100120C100883750100120C10DD
++:104680000884750100120C100885750100120C10C9
++:104690000886750100120C100887750100120C10B5
++:1046A0000888750100120C1008B9750100FFFF00A1
++:1046B00000BA750100FFFF0000BB750100FFFF009D
++:1046C00000BC750100FFFF0000BD750100FFFF0089
++:1046D00000BE750100FFFF0000BF750100FFFF0075
++:1046E00000C0750100FFFF0000C1750100FFFF0061
++:1046F00000C2750100FFFF0000C3750100FFFF004D
++:1047000000C4750100FFFF0000C5750100FFFF0038
++:1047100000C6750100FFFF0000C7750100FFFF0024
++:1047200000C8750100FFFF0000C9750100FFFF0010
++:1047300000CA750100FFFF0000CB750100FFFF00FC
++:1047400000CC750100FFFF0000CD750100FFFF00E8
++:1047500000CE750100FFFF0000CF750100FFFF00D4
++:1047600000D0750100FFFF0000D1750100FFFF00C0
++:1047700000D2750100FFFF0000D3750100FFFF00AC
++:1047800000D4750100FFFF0000D5750100FFFF0098
++:1047900000D6750100FFFF0000D7750100FFFF0084
++:1047A00000D8750100FFFF0000D97501000100006D
++:1047B00000DA75010001000000DB75010001000056
++:1047C00000DC75010001000000DD75010001000042
++:1047D00000DE75010001000000DF7501000100002E
++:1047E00000E075010001000000E175010001000119
++:1047F00000E275010001000100E375010001000104
++:1048000000E475010001000100E5750100010001EF
++:1048100000E675010001000100E7750100010001DB
++:1048200000E875010001000100E9750100010001C7
++:1048300000EA75010001000100EB750100010001B3
++:1048400000EC75010001000100ED7501000100019F
++:1048500000EE75010001000100EF7501000100018B
++:1048600000F075010001000100F175010001000078
++:1048700000F275010001000000F375010001000065
++:1048800000F475010001000000F575010001000051
++:1048900000F675010001000000F77501000100003D
++:1048A00000F8750100010000000176010021FE0101
++:1048B000000276010021FE01000376010021FE01C5
++:1048C000000476010021FE01000576010021FE01B1
++:1048D000000676010021FE01000776010021FE019D
++:1048E000000876010021FE010031760100120C1053
++:1048F0000832760100120C100833760100120C10F9
++:104900000834760100120C100835760100120C10E4
++:104910000836760100120C100837760100120C10D0
++:104920000838760100120C100839760100040000E6
++:10493000003A760100040000003B7601000400000C
++:10494000003C760100040000003D760100040000F8
++:10495000003E760100040000003F760100040000E4
++:1049600000407601000400000049760100020000CA
++:10497000004A760100020000004B760100020000B0
++:10498000004C760100020000004D7601000200009C
++:10499000004E760100020000004F76010002000088
++:1049A0000050760100020000005176010011000065
++:1049B0000052760100110000005376010011000042
++:1049C000005476010011000000557601001100002E
++:1049D000005676010011000000577601001100001A
++:1049E000005876010011000000997601008168AC42
++:1049F0000F9A7601008168AC0F9B7601008168AC4C
++:104A00000F9C7601008168AC0F9D7601008168AC37
++:104A10000F9E7601008168AC0F9F7601008168AC23
++:104A20000FA07601008168AC0FC176010004000080
++:104A300000C276010004000000C3760100040000FB
++:104A400000C476010004000000C5760100040000E7
++:104A500000C676010004000000C7760100040000D3
++:104A600000C8760100040000001177010002000078
++:104A7000001277010002000000137701000200001D
++:104A80000014770100020000001577010002000009
++:104A900000167701000200000017770100020000F5
++:104AA00000187701000200000019770100010000E2
++:104AB000001A770100010000001B770100010000CF
++:104AC000001C770100010000001D770100010000BB
++:104AD000001E770100010000001F770100010000A7
++:104AE0000020770100010000002177010001000093
++:104AF000002277010001000000237701000100007F
++:104B0000002477010001000000257701000100006A
++:104B10000026770100010000002777010001000056
++:104B20000028770100010000002977010002000041
++:104B3000002A770100020000002B7701000200002C
++:104B4000002C770100020000002D77010002000018
++:104B5000002E770100020000002F77010002000004
++:104B600000307701000200000031770100010000F1
++:104B700000327701000100000033770100010000DE
++:104B800000347701000100000035770100010000CA
++:104B900000367701000100000037770100010000B6
++:104BA00000387701000100000039770100010000A2
++:104BB000003A770100010000003B7701000100008E
++:104BC000003C770100010000003D7701000100007A
++:104BD000003E770100010000003F77010001000066
++:104BE0000040770100010000004177010001000052
++:104BF000004277010001000000437701000100003E
++:104C00000044770100010000004577010001000029
++:104C10000046770100010000004777010001000015
++:104C200000487701000100000051770100040000F6
++:104C300000527701000400000053770100040000D7
++:104C400000547701000400000055770100040000C3
++:104C500000567701000400000057770100040000AF
++:104C60000058770100040000006994010011000061
++:104C7000006A940100110000006B94010011000013
++:104C8000006C940100110000006D940100110000FF
++:104C9000006E940100110000006F940100110000EB
++:104CA00000709401001100000079940100010000DF
++:104CB000007A940100010000007B940100010000D3
++:104CC000007C940100010000007D940100010000BF
++:104CD000007E940100010000007F940100010000AB
++:094CE000008094010001000000B5
++:00000001FF
+diff --git a/firmware/nouveau/nv94.ctxprog.ihex b/firmware/nouveau/nv94.ctxprog.ihex
+new file mode 100644
+index 0000000..af11ea0
+--- /dev/null
++++ b/firmware/nouveau/nv94.ctxprog.ihex
+@@ -0,0 +1,79 @@
++:100000004E5643500035018E0070009C0070002059
++:10001000002000080060004C005000890E400000E5
++:100020000020000700600000003000FF00C000005A
++:10003000002000FF008000090070004D32410044A4
++:10004000294000052940000D294000060E41000509
++:10005000006000C5154000110060000B274000C57E
++:100060002140000000700081007000040060004A20
++:1000700000500040942100070060000127C0002EBE
++:1000800000200001008000CB005000FFFF9000FF27
++:10009000FF910020002000080060004C0050000983
++:1000A000006000450E41004D2941009D007000CFC9
++:1000B0002D40009F0070009F005000C02A4000802B
++:1000C000002000080060004F2A4000C02A4000CCF9
++:1000D0003040008100700000002000060060000039
++:1000E000007000FC1B110083007000000030004015
++:1000F00094210007006000010AC0001E00200001DA
++:10010000008000CB005000FF00C000800070008322
++:1001100000700047002000060060000A0211008005
++:100120000320000700600000003000FF00C000FF57
++:1001300000C80007194100D22D2000FF0080008C6C
++:10014000504000CB0050003F02A0004000200006BD
++:100150000060000F007000020217000A0211003256
++:100160000020000D02100042021C000203120002D7
++:1001700004140000051800090513005005150005BA
++:100180000611000F002000070610000007110000F4
++:1001900009110002091200000A1100020B160028C2
++:1001A0000B12002B0B1400010C11000014110005A0
++:1001B00014110007141100091411000B141100CCC4
++:1001C000002000001510000F7940004B79400040DE
++:1001D000722100070060003F042000FF0088008FAC
++:1001E0000070008C794000CB00500000000000053A
++:1001F0001A14000C1A1300001C1300041C12002017
++:100200001C1400251C1100401C1300441C1200602B
++:100210001C1400651C1100801C1300841C1200A01B
++:100220001C1400A51C1100C01C1300C41C1200E00B
++:100230001C1400E51C1100001F1300401F1900E0F2
++:10024000A14000CA01200006006000440020008098
++:10025000201000C6201100C9201500D02019000070
++:100260002112000321120000221600072212008032
++:100270002211000023110002231100802312008BA1
++:10028000231100942311009C231100E1BE40003192
++:100290000220000600600044002000802410000FAF
++:1002A000AF40004BAF400040722100070060003FAC
++:1002B000042000FF0088008F0070008CAF4000CB4E
++:1002C00000500000000000C6241100C9241500D011
++:1002D000241900002512000325120000261600072D
++:1002E000261200802611000027110002271100802D
++:1002F0002712008B271100942711009C271100E280
++:10030000D1400098022000060060004400200080D8
++:10031000281000C6281100C9281500D0281900008F
++:1003200029120003291200002A1600072A12008051
++:100330002A1100002B1100022B1100802B12008BC0
++:100340002B1100942B11009C2B1100E3EE4000FFB9
++:100350000220000600600044002000802C10000FE6
++:10036000DF40004BDF400040722100070060003F8B
++:10037000042000FF0088008F0070008CDF4000CB5D
++:1003800000500000000000C62C1100C92C1500D040
++:100390002C1900002D1200032D1200002E1600074C
++:1003A0002E1200802E1100002F1100022F1100804C
++:1003B0002F12008B2F1100942F11009C2F11000081
++:1003C0000000000FF54000CB0050004072210007F4
++:1003D0000060003F042000FF008800CB0050008731
++:1003E000F840000A0060000000000000074100A083
++:1003F000007000800070008003200007006000048F
++:10040000002000FF00C000FF008000CB0050000073
++:100410000070000000200006006000FE1B11004D6F
++:10042000294100000070000000200006006000FE6E
++:100430001B1100800070001D0070004D11400081F4
++:10044000007000040060004A005000881341000B57
++:100450000060000000200006006000000070000B3B
++:10046000294100FD1B11004D424000D22D2000FD0E
++:10047000008000CB0050000200C000800320000775
++:100480000060006001200002008000CB00500002EC
++:1004900018C000722C200002008000CB0050004DDC
++:1004A0004E40000B0060004D27410001007000032A
++:1004B000007000062D4100052E41000D0060000572
++:1004C0000070000D007000060070000B0070000E40
++:0B04D0000070001C0070000C006000B9
++:00000001FF
+diff --git a/firmware/nouveau/nv94.ctxvals.ihex b/firmware/nouveau/nv94.ctxvals.ihex
+new file mode 100644
+index 0000000..debe8cc
+--- /dev/null
++++ b/firmware/nouveau/nv94.ctxvals.ihex
+@@ -0,0 +1,761 @@
++:100000004E56435600ED050000430000003000004E
++:10001000004C00000002000000750000000300001A
++:10002000007600000000100000860000000CFE00BA
++:10003000008B000000001000009600000087010007
++:10004000009B000000181000009C000000FF000052
++:1000500000AB00000004000000AC000000DF004D19
++:1000600004AE00000000060000B400000000000024
++:1000700001B5000000FF000000B700000000040010
++:1000800000BD00000001000000BE00000080000074
++:1000900000BF00000004000000C6000000020000D5
++:1000A00000C700000001000000CB000000010000BC
++:1000B00000CC00000000010000D20000000200009F
++:1000C00000D300000001000000D400000001000087
++:1000D00000D800000001000000D9000000FFFF3F31
++:1000E00000DA000000FF1F0000DC0000000100003B
++:1000F00000DD00000001000000DF00000001000042
++:1001000000E000000001000000E10000000100002C
++:1001100000E200000004000000E300000001000015
++:1001200000E400000001000000E500000001000004
++:1001300000E600000007000000E7000000010000EA
++:1001400000E800000007000000E9000000010000D6
++:1001500000EA00000001000000EB000000010000C8
++:1001600000F000000001000000F1000000000100AC
++:1001700000F300000001000000F600000000010094
++:1001800000F700000001000000F80000000001007E
++:1001900000FA00000001000000FD00000000010066
++:1001A00000020100000400000003010000700000D4
++:1001B000000401000080000000090100000C0000A4
++:1001C000000B010000080000000C010000140000FA
++:1001D000000E010000290000000F010000270000B0
++:1001E00000100100002600000011010000080000BE
++:1001F00000120100000400000013010000270000AD
++:1002000000160100000100000017010000020000BC
++:1002100000180100000300000019010000040000A4
++:10022000001A010000050000001B0100000600008C
++:10023000001C010000070000001D0100000100007B
++:10024000002E010000CF0000003A010000800000F5
++:10025000003B010000040000003C0100000400001D
++:10026000003D010000030000003E0100000100000D
++:1002700000410100001200000042010000100000D7
++:1002800000430100000C00000044010000010000D8
++:1002900000480100000400000049010000020000C5
++:1002A000004A010000040000004D010000FFFF3F74
++:1002B000004E010000FF1F00005801000004000074
++:1002C0000059010000140000005A01000001000064
++:1002D000005D01000002000000600100000100005C
++:1002E0000062010000020000006301000000100035
++:1002F0000064010000000E00006501000000100015
++:100300000066010000001E000068010000010000FE
++:100310000069010000010000006A01000001000006
++:10032000006B010000010000006C010000010000F2
++:1003300000700100000002000072010000010000D6
++:1003400000730100007000000074010000800000D4
++:10035000007701000001000000780100007000003B
++:100360000079010000800000007D01000001000014
++:10037000007E010000CF00000080010000010000AD
++:100380000084010000CF0000008601000002000090
++:100390000088010000010000008A01000001000047
++:1003A000008C010000CF0000008D010000CF000094
++:1003B000008E0100000100000090010000801F007D
++:1003C000009601000021F8743B97010000018005B0
++:1003D0008999010000001000009A0100001F000030
++:1003E000009B010000FA107C029C010000C000008C
++:1003F000409D010000802089B7A001000021F87411
++:100400003BA101000001800589A30100000010004C
++:1004100000A40100001F000000A5010000FA107CEC
++:1004200002A6010000C0000040A701000080208952
++:10043000B7AA01000021F8743BAB01000001800560
++:1004400089AD01000000100000AE0100001F000097
++:1004500000AF010000FA107C02B0010000C00000F3
++:1004600040B1010000802089B7B401000021F87478
++:100470003BB501000001800589B7010000001000B4
++:1004800000B80100001F000000B9010000FA107C54
++:1004900002BA010000C0000040BB010000802089BA
++:1004A000B7BE01000040003900C00100002200007A
++:1004B00000C301000040003900C401000022000018
++:1004C00000CA01000000008001CB010000000016FE
++:1004D00000CC01000000008001D0010000FFFF03FC
++:1004E00000D101000000008810DA010000010401C1
++:1004F00000DC01000078000000DE010000BF000009
++:1005000000E001000010120000E101000080000086
++:1005100008EA01000000008001EB01000000001665
++:1005200000EC01000000008001F0010000FFFF036B
++:1005300000F101000000008810FA01000001040130
++:1005400000FC01000078000000FE010000BF000078
++:1005500000000200001012000001020000800000F4
++:10056000080B020000707002000E020000FFFFFF87
++:100570000314020000070412001502000007150909
++:100580000516020000020201051702000001020325
++:10059000001E020000400000001F0200000A0B0CB9
++:1005A0000D200200001012140021020000F00100D2
++:1005B00000220200000100000023020000030000EE
++:1005C0000026020000009E03002702000000010038
++:1005D00000280200000038000029020000404040CE
++:1005E000002A0200000AFF00002C02000005F0773C
++:1005F000002D020000FF7F3F00310200000000805C
++:1006000001320200000000160033020000000080EA
++:100610000137020000FFFF030038020000000088DD
++:1006200010410200000104010043020000780000B4
++:100630000045020000BF0000004702000010120049
++:100640000048020000800000085102000000008005
++:10065000015202000000001600530200000000805A
++:100660000157020000FFFF0300580200000000884D
++:100670001061020000010401006302000078000024
++:100680000065020000BF00000067020000101200B9
++:100690000068020000800000087202000070700212
++:1006A0000075020000FFFFFF037B02000007041239
++:1006B000007C020000071509057D0200000202010E
++:1006C000057E0200000102030085020000400000D8
++:1006D00000860200000A0B0C0D87020000101214A5
++:1006E0000088020000F00100008902000001000003
++:1006F000008A020000030000008D020000009E033B
++:10070000008E020000000100008F0200000038008F
++:10071000009002000040404000910200000AFF00EB
++:10072000009302000005F0770094020000FF7F3F75
++:1007300000980200000000800199020000000016ED
++:10074000009A020000000080019E020000FFFF03EB
++:10075000009F02000000008810A8020000010401B0
++:1007600000AA02000078000000AC020000BF0000F8
++:1007700000AE02000010120000AF02000080000076
++:1007800008B802000000008001B902000000001655
++:1007900000BA02000000008001BE020000FFFF035B
++:1007A00000BF02000000008810C802000001040120
++:1007B00000CA02000078000000CC020000BF000068
++:1007C00000CE02000010120000CF020000800000E6
++:1007D00008D902000070700200DC020000FFFFFF79
++:1007E00003E202000007041200E3020000071509FB
++:1007F00005E402000002020105E502000001020317
++:1008000000EC02000040000000ED0200000A0B0CAA
++:100810000DEE02000010121400EF020000F00100C3
++:1008200000F002000001000000F1020000030000DF
++:1008300000F4020000009E0300F502000000010029
++:1008400000F602000000380000F7020000404040BF
++:1008500000F80200000AFF0000FA02000005F0772D
++:1008600000FB020000FF7F3F00FF0200000000804D
++:1008700001000300000000160001030000000080DA
++:100880000105030000FFFF030006030000000088CD
++:10089000100F0300000104010011030000780000A4
++:1008A0000013030000BF0000001503000010120039
++:1008B0000016030000800000081F030000000080F5
++:1008C000012003000000001600210300000000804A
++:1008D0000125030000FFFF0300260300000000883D
++:1008E000102F030000010401003103000078000014
++:1008F0000033030000BF00000035030000101200A9
++:100900000036030000800000084003000070700201
++:100910000043030000FFFFFF034903000007041228
++:10092000004A030000071509054B030000020201FD
++:10093000054C0300000102030053030000400000C7
++:1009400000540300000A0B0C0D5503000010121494
++:100950000056030000F001000057030000010000F2
++:100960000058030000030000005B030000009E032A
++:10097000005C030000000100005D0300000038007F
++:10098000005E030000404040005F0300000AFF00DB
++:10099000006103000005F0770062030000FF7F3F65
++:1009A000008C030000040000008D03000004000020
++:1009B00000A10300000F000000C3030000020000BC
++:1009C00000C903000020000000D303000067FE1FE1
++:1009D000003B040000010000003C04000004000093
++:1009E000003D04000004000000410400001A000063
++:1009F0000043040000100000004404000004000054
++:100A00000045040000040000004C040000808060E9
++:100A1000004D040000808060005B040000010000C5
++:100A200000740400000400000075040000040000CD
++:100A3000008C040000040000008D0400000400008D
++:100A40000093040000020000009404000004000071
++:100A50000095040000040000009C040000800000D9
++:100A6000009D04000080000000A4040000040000B9
++:100A700000A504000004000000B104000004000010
++:100A800000B904000004000000C9040000040000D4
++:100A900000D104000008000000DB04000001000099
++:100AA00000E1040000FF070000E304000010000064
++:100AB00000F304000001000000490500000F0000E1
++:100AC000009305000010000000CC050000040000A9
++:100AD00000CD05000004000000D4050000800000E7
++:100AE00000D505000080000000DC050000040000C7
++:100AF00000DD05000004000000E405000000010224
++:100B000003E505000000010203EC050000030000FE
++:100B100000ED05000003000000F4050000040000E3
++:100B200000F5050000040000001B06000000008026
++:100B30003F1C060000040000001D06000004000029
++:100B400000230600000000803F2406000003000090
++:100B50000025060000030000002B060000000080B6
++:100B60003F330600000000803F3B0600000000808D
++:100B70003F430600000000803F44060000040000E0
++:100B80000045060000040000004B06000000008045
++:100B90003F530600000000803F5B0600000000801D
++:100BA0003F630600000000803F6B060000000080ED
++:100BB0003F730600000000803F7B060000000080BD
++:100BC0003F830600000000803F8B0600000000808D
++:100BD0003F930600000000803F6307000010000004
++:100BE00000730700003F000000AB07000001000099
++:100BF00000BB07000001000000CB0700000100005F
++:100C0000003308000011000000730800000F00000E
++:100C100000B308000011000000EB08000001000014
++:100C200000F308000001000000FB080000010000C4
++:100C30000003090000020000000B09000001000091
++:100C40000013090000020000001B09000001000061
++:100C5000002B09000067FE1F003B0900008168AC03
++:100C60000FEB09000001000000F309000002000082
++:100C700000FB09000001000000030A000001000061
++:100C8000000B0A000002000000130A00000100002F
++:100C9000001B0A0000010000003B0A0000110000D8
++:100CA00000430A000001000000630D000002000084
++:100CB00000730D000067FE1F00DB0D000001000047
++:100CC00000E30D000010000000FB0D00000100001B
++:100CD00000330E0000020000007B0E000001000047
++:100CE00000810E00000F000000830E0000100000C5
++:100CF00000910E000001000000930E0000010000B2
++:100D000000330F000010000000BB0F000000008047
++:100D10003FC30F00000000803FCB0F0000000080A9
++:100D20003FD30F00000000803FDB0F000000008079
++:100D30003FE30F00000000803FEB0F000000008049
++:100D40003FF30F00000000803FFB0F000000008019
++:100D50003F031000000000803F0B100000000080E7
++:100D60003F131000000000803F1B100000000080B7
++:100D70003F231000000000803F2B10000000008087
++:100D80003F331000000000803F03110000100000FE
++:100D900000131100003F0000004B11000001000093
++:100DA000005B11000001000000691100000F00004D
++:100DB000006B11000001000000D3110000110000C1
++:100DC00000131200000F0000005312000011000079
++:100DD000008B1200000100000093120000010000CF
++:100DE000009B12000001000000A31200000200009E
++:100DF00000AB12000001000000B31200000200006E
++:100E000000BB12000001000000CB12000067FE1FB3
++:100E100000DB1200008168AC0F8B130000010000A2
++:100E20000093130000020000009B1300000100006B
++:100E300000A313000001000000AB1300000200003B
++:100E400000B313000001000000BB1300000100000C
++:100E500000DB13000011000000E31300000100009C
++:100E60000003170000020000001317000067FE1FB8
++:100E7000007B170000010000008317000010000035
++:100E8000009B17000001000000D3170000020000C3
++:100E9000001B1800000100000023180000100000D3
++:100EA000003318000001000000D3180000100000FB
++:100EB000005B1900000000803F6319000000008003
++:100EC0003F6B1900000000803F7319000000008094
++:100ED0003F7B1900000000803F8319000000008064
++:100EE0003F8B1900000000803F9319000000008034
++:100EF0003F9B1900000000803FA319000000008004
++:100F00003FAB1900000000803FB3190000000080D3
++:100F10003FBB1900000000803FC3190000000080A3
++:100F20003FCB1900000000803FD319000000008073
++:100F30003FA31A000010000000B31A00003F000099
++:100F400000EB1A000001000000FB1A000001000085
++:100F5000000B1B000001000000731B0000110000CB
++:100F600000B31B00000F000000F31B000011000085
++:100F7000002B1C000001000000331C0000010000D9
++:100F8000003B1C000001000000431C0000020000A8
++:100F9000004B1C000001000000531C000002000078
++:100FA000005B1C0000010000006B1C000067FE1FBE
++:100FB000007B1C00008168AC0F2B1D0000010000AD
++:100FC00000331D0000020000003B1D000001000076
++:100FD00000431D0000010000004B1D000002000046
++:100FE00000531D0000010000005B1D000001000017
++:100FF000007B1D000011000000831D0000010000A7
++:1010000000C81F000021000000D01F0000010000E8
++:1010100000D81F000002000000E01F0000000100D7
++:1010200000E81F000000010000F01F0000010000A8
++:101030000008200000010000001020000002000055
++:101040000018200000000100002020000000010026
++:10105000002820000001000000A320000002000082
++:1010600000B320000067FE1F001B210000010000EC
++:101070000023210000100000003B210000010000BF
++:10108000007321000002000000BB210000010000ED
++:1010900000C321000010000000D321000001000067
++:1010A000007322000010000000FB220000000080FE
++:1010B0003F032300000000803F0B2300000000805E
++:1010C0003F132300000000803F1B2300000000802E
++:1010D0003F232300000000803F2B230000000080FE
++:1010E0003F332300000000803F3B230000000080CE
++:1010F0003F432300000000803F4B2300000000809E
++:101100003F532300000000803F5B2300000000806D
++:101110003F632300000000803F6B2300000000803D
++:101120003F732300000000803F43240000100000B4
++:1011300000532400003F0000008B24000001000049
++:10114000009B24000001000000AB2400000100000F
++:10115000001325000011000000532500000F0000BF
++:10116000009325000011000000CB250000010000C5
++:1011700000D325000001000000DB25000001000075
++:1011800000E325000002000000EB25000001000044
++:1011900000F325000002000000FB25000001000014
++:1011A000000B26000067FE1F001B2600008168ACB4
++:1011B0000FCB26000001000000D326000002000033
++:1011C00000DB26000001000000E326000001000013
++:1011D00000EB26000002000000F3260000010000E2
++:1011E00000FB260000010000001B2700001100008A
++:1011F000002327000001000000115300000F000031
++:1012000000B953000001000000C1530000000100BC
++:1012100000C953000000010000D15300001100007C
++:1012200000E153000008000000115400000100001C
++:1012300000215400000100000029540000010000BA
++:1012400000315400000100000039540000CF0000BC
++:101250000041540000020000007954000001000029
++:1012600000895400000100000091540000010000BA
++:10127000009954000001000000C154000004000067
++:1012800000D154000001000000D9540000150000F6
++:1012900000F954000080444404D9560000120C1098
++:1012A000080157000000010000195700000100016B
++:1012B0000029570000010001003157000001000023
++:1012C00000395700000100010041570000010000F3
++:1012D00000495700000400000051570000020000C0
++:1012E0000062570000FFFF3F007A570000FF1F0019
++:1012F00000F25700000000803F2A58000004000060
++:1013000000325800001A0000004A58000001000096
++:1013100000E258000000FFFF001A5900000F000013
++:10132000005A5900008168AC0F625900001100009A
++:1013300000E2590000040000000A5A000002000008
++:1013400000125A0000000000041A5A0000000000B9
++:10135000043A5A000005000000425A000052000002
++:10136000006A5A000001000000F25A0000000080EC
++:101370003FFA5A00000000803F025B00000000803E
++:101380003F0A5B00000000803F125B00000000800D
++:101390003F1A5B00000000803F225B0000000080DD
++:1013A0003F2A5B00000000803F325B0000000080AD
++:1013B0003F3A5B00000000803F425B00000000807D
++:1013C0003F4A5B00000000803F525B00000000804D
++:1013D0003F5A5B00000000803F625B00000000801D
++:1013E0003F6A5B00000000803F725B00001000005D
++:1013F00000AA5C0000120C1008B15C0000DFBFE323
++:1014000004B25C000005000000B95C0000DFBFE32F
++:1014100004CA5C000001000000DA5C0000FFFF006D
++:1014200000E15C00008168AC0FE25C0000FFFF009F
++:1014300000EA5C0000FFFF0000F25C0000FFFF001C
++:1014400000FA5C000003000000415D0000DFBFE324
++:1014500004495D0000DFBFE304FA5D000000FFFF08
++:1014600000025E00001A000000125E00000300008F
++:10147000004A5F0000020100005A5F000004000003
++:1014800000625F0000040000006A5F0000040000CA
++:1014900000725F0000040000007A5F00000400009A
++:1014A00000825F000004000000925F0000FF070060
++:1014B00000A25F000002010000F25F0000040000D3
++:1014C00000FA5F0000040000000260000004000059
++:1014D000000A600000040000009A610000140C087B
++:1014E00000B261000004080000C2610000040000B6
++:1014F00000CA61000004000000D2610000120C105C
++:1015000008E261000004000000EA6100000400003D
++:1015100000FA6100001000000022620000040800D0
++:10152000002A62000001000000326200001A000080
++:10153000003A6200007F0000004A620000010000E3
++:101540000052620000140C080062620000120C10CD
++:10155000086A6200000400000072620000040000DB
++:10156000008262000010000000A262000001000082
++:1015700000AA620000120C1008E2620000FF0700DF
++:1015800000EA620000140C0800B2640000010000D0
++:1015900000CA64000010000000926600008800008D
++:1015A000009A66000088000000B266000004000097
++:1015B000006A6700002600000082670000000080CB
++:1015C0003FA26700001A000000AA67000010000098
++:1015D00000F26800005200000002690000260000CE
++:1015E0000012690000040000001A690000040000F5
++:1015F000002A6900001A0000004269000000FFFF95
++:101600000052690000040000005A69000004000054
++:10161000006A690000800000007269000004000098
++:10162000007A690000140C08008A690000FF0700B6
++:1016300000706C000004000000786C0000040000E2
++:10164000006C870000040000006D870000040000AB
++:10165000007487000003000000758700000300008D
++:1016600000048800000F000000058800000F000043
++:101670000064880000040000006588000004000089
++:10168000006C880000FFFF00006D880000FFFF0075
++:101690000074880000FFFF000075880000FFFF0055
++:1016A000007C880000FFFF00007D880000FFFF0035
++:1016B0000084880000FFFF000085880000FFFF0015
++:1016C00000CC88000001000000CD8800000100006F
++:1016D00000EC88000001000000ED8800000100001F
++:1016E000001C890000010000001D890000010000AD
++:1016F00000848900000100000085890000010000CD
++:10170000008C890000010000008D890000010000AC
++:10171000009489000002000000958900000200008A
++:10172000009C890000010000009D8900000100006C
++:1017300000A489000001000000A58900000100004C
++:1017400000AC89000002000000AD8900000200002A
++:1017500000B489000001000000B58900000100000C
++:1017600000C489000011000000C5890000110000BC
++:1017700000048A00008168AC0F058A00008168AC13
++:101780000F1C8A0000040000001D8A0000040000F5
++:10179000003C8A0000110000003D8A00001100009A
++:1017A00000448A000001000000458A00000100009A
++:1017B00000548A0000CF000000558A0000CF0000CE
++:1017C000005C8A0000CF0000005D8A0000CF0000AE
++:1017D00000648A0000CF000000658A0000CF00008E
++:1017E00000BC8A000001000000BD8A00000100006A
++:1017F00000C48A000001000000C58A00000100004A
++:1018000000CC8A000002000000CD8A000002000027
++:1018100000D48A000001000000D58A000001000009
++:1018200000DC8A000001000000DD8A0000010000E9
++:1018300000E48A000002000000E58A0000020000C7
++:1018400000EC8A000001000000ED8A0000010000A9
++:1018500000FC8A000001000000FD8A000001000079
++:1018600000048B000001000000058B000001000057
++:10187000000C8B0000010000000D8B000001000037
++:1018800000148B000001000000158B000001000017
++:10189000001C8B0000010000001D8B0000010000F7
++:1018A00000248B000001000000258B0000010000D7
++:1018B000002C8B0000010000002D8B0000010000B7
++:1018C00000348B000001000000358B000001000097
++:1018D000003C8B0000110000003D8B000011000057
++:1018E000007C8B00008168AC0F7D8B00008168ACB0
++:1018F0000F848B00000F000000858B00000F00009C
++:1019000000C48B000067FE1F00C58B000067FE1F30
++:1019100000DC8B000011000000DD8B0000110000D6
++:1019200000E48B000001000000E58B0000010000D6
++:1019300000048C000004000000058C00000400007E
++:1019400000348C000001000000358C000001000014
++:10195000005C8C0000110000005D8C000011000094
++:10196000009C8C00008168AC0F9D8C00008168ACED
++:101970000FBC8C000011000000BD8C0000110000A5
++:1019800000C48C000001000000C58C0000010000B4
++:1019900000D48C000001000000D58C000001000084
++:1019A00000E48C000001000000E58C000001000054
++:1019B00000F48C0000FF070000F58C0000FF07001A
++:1019C00000048D000001000000058D0000010000F2
++:1019D00000148D000001000000158D0000010000C2
++:1019E000006C8E0000080000006D8E0000080000F2
++:1019F00000748E000008000000758E0000080000D2
++:101A0000007C8E0000080000007D8E0000080000B1
++:101A100000848E000008000000858E000008000091
++:101A2000008C8E0000080000008D8E000008000071
++:101A300000948E000008000000958E000008000051
++:101A4000009C8E0000080000009D8E000008000031
++:101A500000A48E000008000000A58E000008000011
++:101A600000AC8E000011000000AD8E0000110000DF
++:101A700000EC8E00008168AC0FED8E00008168AC38
++:101A80000FF48E000000040000F58E00000004003A
++:101A900000FC8E000000040000FD8E000000040029
++:101AA00000048F000000040000058F000000040007
++:101AB000000C8F0000000400000D8F0000000400E7
++:101AC00000148F000000040000158F0000000400C7
++:101AD000001C8F0000000400001D8F0000000400A7
++:101AE00000248F000000040000258F000000040087
++:101AF000002C8F0000000400002D8F000000040067
++:101B000000348F000000030000358F000000030048
++:101B1000003C8F0000000300003D8F000000030028
++:101B200000448F000000030000458F000000030008
++:101B3000004C8F0000000300004D8F0000000300E8
++:101B400000548F000000030000558F0000000300C8
++:101B5000005C8F0000000300005D8F0000000300A8
++:101B600000648F000000030000658F000000030088
++:101B7000006C8F0000000300006D8F000000030068
++:101B800000748F000001000000758F00000100004C
++:101B9000007C8F00000F0000007D8F00000F000010
++:101BA00000BC8F000020000000BD8F00002000005E
++:101BB00000C48F000011000000C58F00001100005C
++:101BC00000CC8F000000010000CD8F00000001005C
++:101BD00000DC8F000001000000DD8F00000100002C
++:101BE00000F48F000040000000F58F00004000006E
++:101BF00000FC8F000000010000FD8F0000000100CC
++:101C0000000C900000030000000D90000003000095
++:101C1000003490000067FE1F003590000067FE1F33
++:101C200000549000000200000055900000020000E7
++:101C3000005C9000008168AC0F5D9000008168AC92
++:101C40000FAC90000001000000AD9000000100000A
++:101C500000D490000004000000D5900000040000B3
++:101C600000E490000001000000E590000001000089
++:101C700000EC90000000040000ED90000000040063
++:101C800000F490000000030000F590000000030045
++:101C900000FC90000001100000FD90000001100009
++:101CA000001C910000110000001D910000110000B7
++:101CB000005C9100008168AC0F5D9100008168AC10
++:101CC0000F649100000F000000659100000F0000FC
++:101CD000002492000067FE1F002592000067FE1F8F
++:101CE0000044920000110000004592000011000025
++:101CF000005C920000040000005D920000040000FF
++:101D0000006C920000010000006D920000010000D4
++:101D100000749200000100000075920000010000B4
++:101D20000094920000010000009592000001000064
++:101D300000BC92000001000000BD92000001000004
++:101D400000CC92000001000000CD920000010000D4
++:101D500000EC9200008824712AED92000088247122
++:101D60002AFC92000000C08540FD92000000C08562
++:101D70004004930000400000000593000040000074
++:101D8000000C930000000100000D93000000010012
++:101D900000149300000001010015930000000101F0
++:101DA000001C930000000080021D930000000080D2
++:101DB00002B4930000DFBFE304B5930000DFBFE38C
++:101DC00004BC930000DFBFE304BD930000DFBFE36A
++:101DD00004C493000001000000C59300000100004E
++:101DE00000D493000000FFFF00D593000000FFFF28
++:101DF00000DC93000001000000DD93000001000002
++:101E000000F493000000FFFF00F593000000FFFFC7
++:101E1000003C940000010000003D9400000100001F
++:101E2000004C940000010000004D940000010000EF
++:101E30000054940000001020305594000000102041
++:101E4000305C940000405060705D94000040506031
++:101E500070649400008898A8B8659400008898A8D9
++:101E6000B86C940000C8D8E8F86D940000C8D8E8B1
++:101E7000F87C9400001A0000007D9400001A000015
++:101E8000008C940000040000008D94000004000009
++:101E9000003C950000040000003D95000004000097
++:101EA0000044950000040000004595000004000077
++:101EB000004C950000808060004D9500008080609F
++:101EC00000749500000400000075950000040000F7
++:101ED000008C950000040000008D950000040000B7
++:101EE0000094950000040000009595000004000097
++:101EF000009C950000800000009D9500008000007F
++:101F000000A495000004000000A595000004000056
++:101F100000CC96000004000000CD960000040000F4
++:101F200000D496000080000000D5960000800000DC
++:101F300000DC96000004000000DD960000040000B4
++:101F400000E496000000010203E596000000010293
++:101F500003EC96000003000000ED96000003000073
++:101F600000F496000004000000F596000004000054
++:101F7000001C970000040000001D970000040000F2
++:101F800000249700000300000025970000030000D4
++:101F90000044970000040000004597000004000082
++:101FA00000A2B3000004000000AAB3000004000077
++:101FB00000BAB3000080000000C2B30000040000BB
++:101FC00000CAB3000001000000DAB30000270000DF
++:101FD00000EAB30000260000000AB4000000000080
++:101FE0000412B40000000000041AB4000000000055
++:101FF0000422B40000000000042AB4000000000025
++:102000000432B40000000000043AB40000000000F4
++:102010000442B40000000000044AB40000000000C4
++:102020000452B40000000000045AB4000000000094
++:102030000462B40000000000046AB4000000000064
++:102040000472B40000000000047AB4000000000034
++:102050000482B4000000000004AAB50000DFBFE362
++:1020600004B2B50000DFBFE304CAB5000021FE01E1
++:10207000006C180100040000006D1801000400004D
++:10208000007418010003000000751801000300002F
++:1020900000041901000F000000051901000F0000E5
++:1020A000006419010004000000651901000400002B
++:1020B000006C190100FFFF00006D190100FFFF0017
++:1020C0000074190100FFFF000075190100FFFF00F7
++:1020D000007C190100FFFF00007D190100FFFF00D7
++:1020E0000084190100FFFF000085190100FFFF00B7
++:1020F00000CC19010001000000CD19010001000011
++:1021000000EC19010001000000ED190100010000C0
++:10211000001C1A0100010000001D1A01000100004E
++:1021200000841A010001000000851A01000100006E
++:10213000008C1A0100010000008D1A01000100004E
++:1021400000941A010002000000951A01000200002C
++:10215000009C1A0100010000009D1A01000100000E
++:1021600000A41A010001000000A51A0100010000EE
++:1021700000AC1A010002000000AD1A0100020000CC
++:1021800000B41A010001000000B51A0100010000AE
++:1021900000C41A010011000000C51A01001100005E
++:1021A00000041B01008168AC0F051B01008168ACB5
++:1021B0000F1C1B0100040000001D1B010004000097
++:1021C000003C1B0100110000003D1B01001100003C
++:1021D00000441B010001000000451B01000100003C
++:1021E00000541B0100CF000000551B0100CF000070
++:1021F000005C1B0100CF0000005D1B0100CF000050
++:1022000000641B0100CF000000651B0100CF00002F
++:1022100000BC1B010001000000BD1B01000100000B
++:1022200000C41B010001000000C51B0100010000EB
++:1022300000CC1B010002000000CD1B0100020000C9
++:1022400000D41B010001000000D51B0100010000AB
++:1022500000DC1B010001000000DD1B01000100008B
++:1022600000E41B010002000000E51B010002000069
++:1022700000EC1B010001000000ED1B01000100004B
++:1022800000FC1B010001000000FD1B01000100001B
++:1022900000041C010001000000051C0100010000F9
++:1022A000000C1C0100010000000D1C0100010000D9
++:1022B00000141C010001000000151C0100010000B9
++:1022C000001C1C0100010000001D1C010001000099
++:1022D00000241C010001000000251C010001000079
++:1022E000002C1C0100010000002D1C010001000059
++:1022F00000341C010001000000351C010001000039
++:10230000003C1C0100110000003D1C0100110000F8
++:10231000007C1C01008168AC0F7D1C01008168AC51
++:102320000F841C01000F000000851C01000F00003D
++:1023300000C41C010067FE1F00C51C010067FE1FD2
++:1023400000DC1C010011000000DD1C010011000078
++:1023500000E41C010001000000E51C010001000078
++:1023600000041D010004000000051D010004000020
++:1023700000341D010001000000351D0100010000B6
++:10238000005C1D0100110000005D1D010011000036
++:10239000009C1D01008168AC0F9D1D01008168AC8F
++:1023A0000FBC1D010011000000BD1D010011000047
++:1023B00000C41D010001000000C51D010001000056
++:1023C00000D41D010001000000D51D010001000026
++:1023D00000E41D010001000000E51D0100010000F6
++:1023E00000F41D0100FF070000F51D0100FF0700BC
++:1023F00000041E010001000000051E010001000094
++:1024000000141E010001000000151E010001000063
++:10241000006C1F0100080000006D1F010008000093
++:1024200000741F010008000000751F010008000073
++:10243000007C1F0100080000007D1F010008000053
++:1024400000841F010008000000851F010008000033
++:10245000008C1F0100080000008D1F010008000013
++:1024600000941F010008000000951F0100080000F3
++:10247000009C1F0100080000009D1F0100080000D3
++:1024800000A41F010008000000A51F0100080000B3
++:1024900000AC1F010011000000AD1F010011000081
++:1024A00000EC1F01008168AC0FED1F01008168ACDA
++:1024B0000FF41F010000040000F51F0100000400DC
++:1024C00000FC1F010000040000FD1F0100000400CB
++:1024D00000042001000004000005200100000400A9
++:1024E000000C200100000400000D20010000040089
++:1024F0000014200100000400001520010000040069
++:10250000001C200100000400001D20010000040048
++:102510000024200100000400002520010000040028
++:10252000002C200100000400002D20010000040008
++:1025300000342001000003000035200100000300EA
++:10254000003C200100000300003D200100000300CA
++:1025500000442001000003000045200100000300AA
++:10256000004C200100000300004D2001000003008A
++:10257000005420010000030000552001000003006A
++:10258000005C200100000300005D2001000003004A
++:10259000006420010000030000652001000003002A
++:1025A000006C200100000300006D2001000003000A
++:1025B00000742001000100000075200100010000EE
++:1025C000007C2001000F0000007D2001000F0000B2
++:1025D00000BC20010020000000BD20010020000000
++:1025E00000C420010011000000C5200100110000FE
++:1025F00000CC20010000010000CD200100000100FE
++:1026000000DC20010001000000DD200100010000CD
++:1026100000F420010040000000F52001004000000F
++:1026200000FC20010000010000FD2001000001006D
++:10263000000C210100030000000D21010003000037
++:10264000003421010067FE1F003521010067FE1FD5
++:102650000054210100020000005521010002000089
++:10266000005C2101008168AC0F5D2101008168AC34
++:102670000FAC21010001000000AD210100010000AC
++:1026800000D421010004000000D521010004000055
++:1026900000E421010001000000E52101000100002B
++:1026A00000EC21010000040000ED21010000040005
++:1026B00000F421010000030000F5210100000300E7
++:1026C00000FC21010001100000FD210100011000AB
++:1026D000001C220100110000001D22010011000059
++:1026E000005C2201008168AC0F5D2201008168ACB2
++:1026F0000F642201000F000000652201000F00009E
++:10270000002423010067FE1F002523010067FE1F30
++:1027100000442301001100000045230100110000C6
++:10272000005C230100040000005D230100040000A0
++:10273000006C230100010000006D23010001000076
++:102740000074230100010000007523010001000056
++:102750000094230100010000009523010001000006
++:1027600000BC23010001000000BD230100010000A6
++:1027700000CC23010001000000CD23010001000076
++:1027800000EC2301008824712AED230100882471C4
++:102790002AFC23010000C08540FD23010000C08504
++:1027A0004004240100400000000524010040000016
++:1027B000000C240100000100000D240100000100B4
++:1027C0000014240100000101001524010000010192
++:1027D000001C240100000080021D24010000008074
++:1027E00002B4240100DFBFE304B5240100DFBFE32E
++:1027F00004BC240100DFBFE304BD240100DFBFE30C
++:1028000004C424010001000000C5240100010000EF
++:1028100000D424010000FFFF00D524010000FFFFC9
++:1028200000DC24010001000000DD240100010000A3
++:1028300000F424010000FFFF00F524010000FFFF69
++:10284000003C250100010000003D250100010000C1
++:10285000004C250100010000004D25010001000091
++:1028600000542501000010203055250100001020E3
++:10287000305C250100405060705D250100405060D3
++:1028800070642501008898A8B8652501008898A87B
++:10289000B86C250100C8D8E8F86D250100C8D8E853
++:1028A000F87C2501001A0000007D2501001A0000B7
++:1028B00000D84E010004000000E04E0100040000BA
++:1028C00000E84E0100120C1008F04E010003000059
++:1028D00000004F0100120C1008104F0100140C08EA
++:1028E00000184F010001000000204F0100140C08E7
++:1028F00000384F0100120C1008404F010027000063
++:1029000000584F010001000000686D010001000047
++:1029100000206E0100120C1008886E0100000000FB
++:1029200004906E010000000004A06E010080000011
++:1029300000C06E010080000000D06E01003F00006A
++:1029400000286F010002000000306F01000000004D
++:1029500004386F010000000004806F0100040000D3
++:1029600000A06F01000400000030700100010000B1
++:1029700000387001000110000040700100FFFF00EE
++:102980000048700100FFFF000050700100FFFF00D1
++:102990000058700100FFFF0000607101000000801E
++:1029A0003F687101000000803F70710100000080ED
++:1029B0003F787101000000803F80710100000080BD
++:1029C0003F887101000000803F907101000000808D
++:1029D0003F987101000000803FA07101000000805D
++:1029E0003FA87101000000803FB07101000000802D
++:1029F0003FB87101000000803FC0710100000080FD
++:102A00003FC87101000000803FD0710100000080CC
++:102A10003FD87101000000803FE07101001000000C
++:102A200000F87101000300000040720100120C1058
++:102A30000849720100800000004A72010080000015
++:102A4000004B720100800000005072010080000005
++:102A50000051720100047000805272010004700085
++:102A600080537201000470008058720100047000ED
++:102A70008059720100000400045A72010000040031
++:102A8000045B720100000400046072010000040095
++:102A90000461720100001000006272010000100069
++:102AA0000063720100001000006872010000100055
++:102AB0000079720100010000007A7201000100003B
++:102AC000007B720100010000008072010001000023
++:102AD00000917201000100000092720100010000EB
++:102AE00000937201000100000098720100010000D3
++:102AF00000A172010004000000A2720100040000A5
++:102B000000A372010004000000A87201000400008C
++:102B100000A972010002000000AA72010002000078
++:102B200000AB72010002000000B072010002000060
++:102B30000009730100800000000A7301008000009A
++:102B4000000B730100800000001073010080000082
++:102B50000011730100047000801273010004700002
++:102B6000801373010004700080187301000470006A
++:102B70008019730100000400041A730100000400AE
++:102B8000041B730100000400042073010000040012
++:102B900004217301000010000022730100001000E6
++:102BA00000237301000010000028730100001000D2
++:102BB0000039730100010000003A730100010000B8
++:102BC000003B7301000100000040730100010000A0
++:102BD0000051730100010000005273010001000068
++:102BE0000053730100010000005873010001000050
++:102BF0000061730100040000006273010004000022
++:102C00000063730100040000006873010004000009
++:102C10000069730100020000006A730100020000F5
++:102C2000006B7301000200000070730100020000DD
++:102C300000C1730100120C1008C2730100120C10C5
++:102C400008C3730100120C1008C8730100120C10A5
++:102C500008F9730100FFFF0000FA730100FFFF0095
++:102C600000FB730100FFFF000000740100FFFF0084
++:102C70000001740100FFFF000002740100FFFF006B
++:102C80000003740100FFFF000008740100FFFF0053
++:102C90000009740100FFFF00000A740100FFFF003B
++:102CA000000B740100FFFF000010740100FFFF0023
++:102CB0000011740100FFFF000012740100FFFF000B
++:102CC0000013740100FFFF000018740100FFFF00F3
++:102CD0000019740100010000001A740100010000D5
++:102CE000001B7401000100000020740100010000BD
++:102CF00000217401000100010022740100010001A3
++:102D0000002374010001000100287401000100018A
++:102D10000029740100010001002A74010001000172
++:102D2000002B74010001000100307401000100015A
++:102D30000031740100010000003274010001000044
++:102D4000003374010001000000387401000100002C
++:102D5000004174010021FE01004274010021FE01C6
++:102D6000004374010021FE01004874010021FE01AE
++:102D70000071740100120C100872740100120C1022
++:102D80000873740100120C100878740100120C1002
++:102D90000879740100040000007A74010004000046
++:102DA000007B740100040000008074010004000036
++:102DB0000089740100020000008A74010002000012
++:102DC000008B7401000200000090740100020000FA
++:102DD00000917401001100000092740100110000C4
++:102DE00000937401001100000098740100110000AC
++:102DF00000D97401008168AC0FDA7401008168ACFD
++:102E00000FDB7401008168AC0FE07401008168ACD5
++:102E10000F017501000400000002750100040000AC
++:102E200000037501000400000008750100040000A3
++:102E300000517501000200000052750100020000FF
++:102E400000537501000200000058750100020000E7
++:102E50000059750100010000005A750100010000D1
++:102E6000005B7501000100000060750100010000B9
++:102E700000617501000100000062750100010000A1
++:102E80000063750100010000006875010001000089
++:102E90000069750100020000006A7501000200006F
++:102EA000006B750100020000007075010002000057
++:102EB0000071750100010000007275010001000041
++:102EC0000073750100010000007875010001000029
++:102ED0000079750100010000007A75010001000011
++:102EE000007B7501000100000080750100010000F9
++:102EF00000817501000100000082750100010000E1
++:102F000000837501000100000088750100010000C8
++:102F1000009175010004000000927501000400009A
++:102F20000093750100040000009875010004000082
++:102F300000B192010011000000B2920100110000E6
++:102F400000B392010011000000B8920100110000CE
++:102F500000C192010001000000C2920100010000C6
++:102F600000C392010001000000C8920100010000AE
++:012F70000060
++:00000001FF
+diff --git a/firmware/nouveau/nv96.ctxprog.ihex b/firmware/nouveau/nv96.ctxprog.ihex
+new file mode 100644
+index 0000000..af11ea0
+--- /dev/null
++++ b/firmware/nouveau/nv96.ctxprog.ihex
+@@ -0,0 +1,79 @@
++:100000004E5643500035018E0070009C0070002059
++:10001000002000080060004C005000890E400000E5
++:100020000020000700600000003000FF00C000005A
++:10003000002000FF008000090070004D32410044A4
++:10004000294000052940000D294000060E41000509
++:10005000006000C5154000110060000B274000C57E
++:100060002140000000700081007000040060004A20
++:1000700000500040942100070060000127C0002EBE
++:1000800000200001008000CB005000FFFF9000FF27
++:10009000FF910020002000080060004C0050000983
++:1000A000006000450E41004D2941009D007000CFC9
++:1000B0002D40009F0070009F005000C02A4000802B
++:1000C000002000080060004F2A4000C02A4000CCF9
++:1000D0003040008100700000002000060060000039
++:1000E000007000FC1B110083007000000030004015
++:1000F00094210007006000010AC0001E00200001DA
++:10010000008000CB005000FF00C000800070008322
++:1001100000700047002000060060000A0211008005
++:100120000320000700600000003000FF00C000FF57
++:1001300000C80007194100D22D2000FF0080008C6C
++:10014000504000CB0050003F02A0004000200006BD
++:100150000060000F007000020217000A0211003256
++:100160000020000D02100042021C000203120002D7
++:1001700004140000051800090513005005150005BA
++:100180000611000F002000070610000007110000F4
++:1001900009110002091200000A1100020B160028C2
++:1001A0000B12002B0B1400010C11000014110005A0
++:1001B00014110007141100091411000B141100CCC4
++:1001C000002000001510000F7940004B79400040DE
++:1001D000722100070060003F042000FF0088008FAC
++:1001E0000070008C794000CB00500000000000053A
++:1001F0001A14000C1A1300001C1300041C12002017
++:100200001C1400251C1100401C1300441C1200602B
++:100210001C1400651C1100801C1300841C1200A01B
++:100220001C1400A51C1100C01C1300C41C1200E00B
++:100230001C1400E51C1100001F1300401F1900E0F2
++:10024000A14000CA01200006006000440020008098
++:10025000201000C6201100C9201500D02019000070
++:100260002112000321120000221600072212008032
++:100270002211000023110002231100802312008BA1
++:10028000231100942311009C231100E1BE40003192
++:100290000220000600600044002000802410000FAF
++:1002A000AF40004BAF400040722100070060003FAC
++:1002B000042000FF0088008F0070008CAF4000CB4E
++:1002C00000500000000000C6241100C9241500D011
++:1002D000241900002512000325120000261600072D
++:1002E000261200802611000027110002271100802D
++:1002F0002712008B271100942711009C271100E280
++:10030000D1400098022000060060004400200080D8
++:10031000281000C6281100C9281500D0281900008F
++:1003200029120003291200002A1600072A12008051
++:100330002A1100002B1100022B1100802B12008BC0
++:100340002B1100942B11009C2B1100E3EE4000FFB9
++:100350000220000600600044002000802C10000FE6
++:10036000DF40004BDF400040722100070060003F8B
++:10037000042000FF0088008F0070008CDF4000CB5D
++:1003800000500000000000C62C1100C92C1500D040
++:100390002C1900002D1200032D1200002E1600074C
++:1003A0002E1200802E1100002F1100022F1100804C
++:1003B0002F12008B2F1100942F11009C2F11000081
++:1003C0000000000FF54000CB0050004072210007F4
++:1003D0000060003F042000FF008800CB0050008731
++:1003E000F840000A0060000000000000074100A083
++:1003F000007000800070008003200007006000048F
++:10040000002000FF00C000FF008000CB0050000073
++:100410000070000000200006006000FE1B11004D6F
++:10042000294100000070000000200006006000FE6E
++:100430001B1100800070001D0070004D11400081F4
++:10044000007000040060004A005000881341000B57
++:100450000060000000200006006000000070000B3B
++:10046000294100FD1B11004D424000D22D2000FD0E
++:10047000008000CB0050000200C000800320000775
++:100480000060006001200002008000CB00500002EC
++:1004900018C000722C200002008000CB0050004DDC
++:1004A0004E40000B0060004D27410001007000032A
++:1004B000007000062D4100052E41000D0060000572
++:1004C0000070000D007000060070000B0070000E40
++:0B04D0000070001C0070000C006000B9
++:00000001FF
+diff --git a/firmware/nouveau/nv96.ctxvals.ihex b/firmware/nouveau/nv96.ctxvals.ihex
+new file mode 100644
+index 0000000..6b78611
+--- /dev/null
++++ b/firmware/nouveau/nv96.ctxvals.ihex
+@@ -0,0 +1,761 @@
++:100000004E56435600ED050000430000003000004E
++:10001000004C00000002000000750000000300001A
++:10002000007600000000100000860000000CFE00BA
++:10003000008B000000001000009600000087010007
++:10004000009B000000181000009C000000FF000052
++:1000500000AB00000004000000AC000000DF002541
++:1000600004AE00000000060000B400000000000024
++:1000700001B5000000FF000000B700000000040010
++:1000800000BD00000001000000BE00000080000074
++:1000900000BF00000004000000C6000000020000D5
++:1000A00000C700000001000000CB000000010000BC
++:1000B00000CC00000000010000D20000000200009F
++:1000C00000D300000001000000D400000001000087
++:1000D00000D800000001000000D9000000FFFF3F31
++:1000E00000DA000000FF1F0000DC0000000100003B
++:1000F00000DD00000001000000DF00000001000042
++:1001000000E000000001000000E10000000100002C
++:1001100000E200000004000000E300000001000015
++:1001200000E400000001000000E500000001000004
++:1001300000E600000007000000E7000000010000EA
++:1001400000E800000007000000E9000000010000D6
++:1001500000EA00000001000000EB000000010000C8
++:1001600000F000000001000000F1000000000100AC
++:1001700000F300000001000000F600000000010094
++:1001800000F700000001000000F80000000001007E
++:1001900000FA00000001000000FD00000000010066
++:1001A00000020100000400000003010000700000D4
++:1001B000000401000080000000090100000C0000A4
++:1001C000000B010000080000000C010000140000FA
++:1001D000000E010000290000000F010000270000B0
++:1001E00000100100002600000011010000080000BE
++:1001F00000120100000400000013010000270000AD
++:1002000000160100000100000017010000020000BC
++:1002100000180100000300000019010000040000A4
++:10022000001A010000050000001B0100000600008C
++:10023000001C010000070000001D0100000100007B
++:10024000002E010000CF0000003A010000800000F5
++:10025000003B010000040000003C0100000400001D
++:10026000003D010000030000003E0100000100000D
++:1002700000410100001200000042010000100000D7
++:1002800000430100000C00000044010000010000D8
++:1002900000480100000400000049010000020000C5
++:1002A000004A010000040000004D010000FFFF3F74
++:1002B000004E010000FF1F00005801000004000074
++:1002C0000059010000140000005A01000001000064
++:1002D000005D01000002000000600100000100005C
++:1002E0000062010000020000006301000000100035
++:1002F0000064010000000E00006501000000100015
++:100300000066010000001E000068010000010000FE
++:100310000069010000010000006A01000001000006
++:10032000006B010000010000006C010000010000F2
++:1003300000700100000002000072010000010000D6
++:1003400000730100007000000074010000800000D4
++:10035000007701000001000000780100007000003B
++:100360000079010000800000007D01000001000014
++:10037000007E010000CF00000080010000010000AD
++:100380000084010000CF0000008601000002000090
++:100390000088010000010000008A01000001000047
++:1003A000008C010000CF0000008D010000CF000094
++:1003B000008E0100000100000090010000801F007D
++:1003C000009601000021F8743B97010000018005B0
++:1003D0008999010000001000009A0100001F000030
++:1003E000009B010000FA107C029C010000C000008C
++:1003F000409D010000802089B7A001000021F87411
++:100400003BA101000001800589A30100000010004C
++:1004100000A40100001F000000A5010000FA107CEC
++:1004200002A6010000C0000040A701000080208952
++:10043000B7AA01000021F8743BAB01000001800560
++:1004400089AD01000000100000AE0100001F000097
++:1004500000AF010000FA107C02B0010000C00000F3
++:1004600040B1010000802089B7B401000021F87478
++:100470003BB501000001800589B7010000001000B4
++:1004800000B80100001F000000B9010000FA107C54
++:1004900002BA010000C0000040BB010000802089BA
++:1004A000B7BE01000040003900C00100002200007A
++:1004B00000C301000040003900C401000022000018
++:1004C00000CA01000000008001CB010000000016FE
++:1004D00000CC01000000008001D0010000FFFF03FC
++:1004E00000D101000000008C11DA010000010401BC
++:1004F00000DC01000078000000DE010000BF000009
++:1005000000E001000010120000E101000080000086
++:1005100008EA01000000008001EB01000000001665
++:1005200000EC01000000008001F0010000FFFF036B
++:1005300000F101000000008C11FA0100000104012B
++:1005400000FC01000078000000FE010000BF000078
++:1005500000000200001012000001020000800000F4
++:10056000080B020000707002000E020000FFFFFF87
++:100570000314020000070412001502000007150909
++:100580000516020000020201051702000001020325
++:10059000001E020000400000001F0200000A0B0CB9
++:1005A0000D200200001012140021020000F00100D2
++:1005B00000220200000100000023020000030000EE
++:1005C0000026020000009E03002702000000010038
++:1005D00000280200000038000029020000404040CE
++:1005E000002A0200000AFF00002C02000005F0773C
++:1005F000002D020000FF7F3F00310200000000805C
++:1006000001320200000000160033020000000080EA
++:100610000137020000FFFF03003802000000008CD9
++:1006200011410200000104010043020000780000B3
++:100630000045020000BF0000004702000010120049
++:100640000048020000800000085102000000008005
++:10065000015202000000001600530200000000805A
++:100660000157020000FFFF03005802000000008C49
++:100670001161020000010401006302000078000023
++:100680000065020000BF00000067020000101200B9
++:100690000068020000800000087202000070700212
++:1006A0000075020000FFFFFF037B02000007041239
++:1006B000007C020000071509057D0200000202010E
++:1006C000057E0200000102030085020000400000D8
++:1006D00000860200000A0B0C0D87020000101214A5
++:1006E0000088020000F00100008902000001000003
++:1006F000008A020000030000008D020000009E033B
++:10070000008E020000000100008F0200000038008F
++:10071000009002000040404000910200000AFF00EB
++:10072000009302000005F0770094020000FF7F3F75
++:1007300000980200000000800199020000000016ED
++:10074000009A020000000080019E020000FFFF03EB
++:10075000009F02000000008C11A8020000010401AB
++:1007600000AA02000078000000AC020000BF0000F8
++:1007700000AE02000010120000AF02000080000076
++:1007800008B802000000008001B902000000001655
++:1007900000BA02000000008001BE020000FFFF035B
++:1007A00000BF02000000008C11C80200000104011B
++:1007B00000CA02000078000000CC020000BF000068
++:1007C00000CE02000010120000CF020000800000E6
++:1007D00008D902000070700200DC020000FFFFFF79
++:1007E00003E202000007041200E3020000071509FB
++:1007F00005E402000002020105E502000001020317
++:1008000000EC02000040000000ED0200000A0B0CAA
++:100810000DEE02000010121400EF020000F00100C3
++:1008200000F002000001000000F1020000030000DF
++:1008300000F4020000009E0300F502000000010029
++:1008400000F602000000380000F7020000404040BF
++:1008500000F80200000AFF0000FA02000005F0772D
++:1008600000FB020000FF7F3F00FF0200000000804D
++:1008700001000300000000160001030000000080DA
++:100880000105030000FFFF03000603000000008CC9
++:10089000110F0300000104010011030000780000A3
++:1008A0000013030000BF0000001503000010120039
++:1008B0000016030000800000081F030000000080F5
++:1008C000012003000000001600210300000000804A
++:1008D0000125030000FFFF03002603000000008C39
++:1008E000112F030000010401003103000078000013
++:1008F0000033030000BF00000035030000101200A9
++:100900000036030000800000084003000070700201
++:100910000043030000FFFFFF034903000007041228
++:10092000004A030000071509054B030000020201FD
++:10093000054C0300000102030053030000400000C7
++:1009400000540300000A0B0C0D5503000010121494
++:100950000056030000F001000057030000010000F2
++:100960000058030000030000005B030000009E032A
++:10097000005C030000000100005D0300000038007F
++:10098000005E030000404040005F0300000AFF00DB
++:10099000006103000005F0770062030000FF7F3F65
++:1009A000008C030000040000008D03000004000020
++:1009B00000A10300000F000000C3030000020000BC
++:1009C00000C903000020000000D303000067FE1FE1
++:1009D000003B040000010000003C04000004000093
++:1009E000003D04000004000000410400001A000063
++:1009F0000043040000100000004404000004000054
++:100A00000045040000040000004C040000808060E9
++:100A1000004D040000808060005B040000010000C5
++:100A200000740400000400000075040000040000CD
++:100A3000008C040000040000008D0400000400008D
++:100A40000093040000020000009404000004000071
++:100A50000095040000040000009C040000800000D9
++:100A6000009D04000080000000A4040000040000B9
++:100A700000A504000004000000B104000004000010
++:100A800000B904000004000000C9040000040000D4
++:100A900000D104000008000000DB04000001000099
++:100AA00000E1040000FF070000E304000010000064
++:100AB00000F304000001000000490500000F0000E1
++:100AC000009305000010000000CC050000040000A9
++:100AD00000CD05000004000000D4050000800000E7
++:100AE00000D505000080000000DC050000040000C7
++:100AF00000DD05000004000000E405000000010224
++:100B000003E505000000010203EC050000030000FE
++:100B100000ED05000003000000F4050000040000E3
++:100B200000F5050000040000001B06000000008026
++:100B30003F1C060000040000001D06000004000029
++:100B400000230600000000803F2406000003000090
++:100B50000025060000030000002B060000000080B6
++:100B60003F330600000000803F3B0600000000808D
++:100B70003F430600000000803F44060000040000E0
++:100B80000045060000040000004B06000000008045
++:100B90003F530600000000803F5B0600000000801D
++:100BA0003F630600000000803F6B060000000080ED
++:100BB0003F730600000000803F7B060000000080BD
++:100BC0003F830600000000803F8B0600000000808D
++:100BD0003F930600000000803F6307000010000004
++:100BE00000730700003F000000AB07000001000099
++:100BF00000BB07000001000000CB0700000100005F
++:100C0000003308000011000000730800000F00000E
++:100C100000B308000011000000EB08000001000014
++:100C200000F308000001000000FB080000010000C4
++:100C30000003090000020000000B09000001000091
++:100C40000013090000020000001B09000001000061
++:100C5000002B09000067FE1F003B0900008168AC03
++:100C60000FEB09000001000000F309000002000082
++:100C700000FB09000001000000030A000001000061
++:100C8000000B0A000002000000130A00000100002F
++:100C9000001B0A0000010000003B0A0000110000D8
++:100CA00000430A000001000000630D000002000084
++:100CB00000730D000067FE1F00DB0D000001000047
++:100CC00000E30D000010000000FB0D00000100001B
++:100CD00000330E0000020000007B0E000001000047
++:100CE00000810E00000F000000830E0000100000C5
++:100CF00000910E000001000000930E0000010000B2
++:100D000000330F000010000000BB0F000000008047
++:100D10003FC30F00000000803FCB0F0000000080A9
++:100D20003FD30F00000000803FDB0F000000008079
++:100D30003FE30F00000000803FEB0F000000008049
++:100D40003FF30F00000000803FFB0F000000008019
++:100D50003F031000000000803F0B100000000080E7
++:100D60003F131000000000803F1B100000000080B7
++:100D70003F231000000000803F2B10000000008087
++:100D80003F331000000000803F03110000100000FE
++:100D900000131100003F0000004B11000001000093
++:100DA000005B11000001000000691100000F00004D
++:100DB000006B11000001000000D3110000110000C1
++:100DC00000131200000F0000005312000011000079
++:100DD000008B1200000100000093120000010000CF
++:100DE000009B12000001000000A31200000200009E
++:100DF00000AB12000001000000B31200000200006E
++:100E000000BB12000001000000CB12000067FE1FB3
++:100E100000DB1200008168AC0F8B130000010000A2
++:100E20000093130000020000009B1300000100006B
++:100E300000A313000001000000AB1300000200003B
++:100E400000B313000001000000BB1300000100000C
++:100E500000DB13000011000000E31300000100009C
++:100E60000003170000020000001317000067FE1FB8
++:100E7000007B170000010000008317000010000035
++:100E8000009B17000001000000D3170000020000C3
++:100E9000001B1800000100000023180000100000D3
++:100EA000003318000001000000D3180000100000FB
++:100EB000005B1900000000803F6319000000008003
++:100EC0003F6B1900000000803F7319000000008094
++:100ED0003F7B1900000000803F8319000000008064
++:100EE0003F8B1900000000803F9319000000008034
++:100EF0003F9B1900000000803FA319000000008004
++:100F00003FAB1900000000803FB3190000000080D3
++:100F10003FBB1900000000803FC3190000000080A3
++:100F20003FCB1900000000803FD319000000008073
++:100F30003FA31A000010000000B31A00003F000099
++:100F400000EB1A000001000000FB1A000001000085
++:100F5000000B1B000001000000731B0000110000CB
++:100F600000B31B00000F000000F31B000011000085
++:100F7000002B1C000001000000331C0000010000D9
++:100F8000003B1C000001000000431C0000020000A8
++:100F9000004B1C000001000000531C000002000078
++:100FA000005B1C0000010000006B1C000067FE1FBE
++:100FB000007B1C00008168AC0F2B1D0000010000AD
++:100FC00000331D0000020000003B1D000001000076
++:100FD00000431D0000010000004B1D000002000046
++:100FE00000531D0000010000005B1D000001000017
++:100FF000007B1D000011000000831D0000010000A7
++:1010000000C81F000021000000D01F0000010000E8
++:1010100000D81F000002000000E01F0000000100D7
++:1010200000E81F000000010000F01F0000010000A8
++:101030000008200000010000001020000002000055
++:101040000018200000000100002020000000010026
++:10105000002820000001000000A320000002000082
++:1010600000B320000067FE1F001B210000010000EC
++:101070000023210000100000003B210000010000BF
++:10108000007321000002000000BB210000010000ED
++:1010900000C321000010000000D321000001000067
++:1010A000007322000010000000FB220000000080FE
++:1010B0003F032300000000803F0B2300000000805E
++:1010C0003F132300000000803F1B2300000000802E
++:1010D0003F232300000000803F2B230000000080FE
++:1010E0003F332300000000803F3B230000000080CE
++:1010F0003F432300000000803F4B2300000000809E
++:101100003F532300000000803F5B2300000000806D
++:101110003F632300000000803F6B2300000000803D
++:101120003F732300000000803F43240000100000B4
++:1011300000532400003F0000008B24000001000049
++:10114000009B24000001000000AB2400000100000F
++:10115000001325000011000000532500000F0000BF
++:10116000009325000011000000CB250000010000C5
++:1011700000D325000001000000DB25000001000075
++:1011800000E325000002000000EB25000001000044
++:1011900000F325000002000000FB25000001000014
++:1011A000000B26000067FE1F001B2600008168ACB4
++:1011B0000FCB26000001000000D326000002000033
++:1011C00000DB26000001000000E326000001000013
++:1011D00000EB26000002000000F3260000010000E2
++:1011E00000FB260000010000001B2700001100008A
++:1011F000002327000001000000115300000F000031
++:1012000000B953000001000000C1530000000100BC
++:1012100000C953000000010000D15300001100007C
++:1012200000E153000008000000115400000100001C
++:1012300000215400000100000029540000010000BA
++:1012400000315400000100000039540000CF0000BC
++:101250000041540000020000007954000001000029
++:1012600000895400000100000091540000010000BA
++:10127000009954000001000000C154000004000067
++:1012800000D154000001000000D9540000150000F6
++:1012900000F954000080444404D9560000120C1098
++:1012A000080157000000010000195700000100016B
++:1012B0000029570000010001003157000001000023
++:1012C00000395700000100010041570000010000F3
++:1012D00000495700000400000051570000020000C0
++:1012E0000062570000FFFF3F007A570000FF1F0019
++:1012F00000F25700000000803F2A58000004000060
++:1013000000325800001A0000004A58000001000096
++:1013100000E258000000FFFF001A5900000F000013
++:10132000005A5900008168AC0F625900001100009A
++:1013300000E2590000040000000A5A000002000008
++:1013400000125A0000000000041A5A0000000000B9
++:10135000043A5A000005000000425A000052000002
++:10136000006A5A000001000000F25A0000000080EC
++:101370003FFA5A00000000803F025B00000000803E
++:101380003F0A5B00000000803F125B00000000800D
++:101390003F1A5B00000000803F225B0000000080DD
++:1013A0003F2A5B00000000803F325B0000000080AD
++:1013B0003F3A5B00000000803F425B00000000807D
++:1013C0003F4A5B00000000803F525B00000000804D
++:1013D0003F5A5B00000000803F625B00000000801D
++:1013E0003F6A5B00000000803F725B00001000005D
++:1013F00000AA5C0000120C1008B15C0000DFBFE323
++:1014000004B25C000005000000B95C0000DFBFE32F
++:1014100004CA5C000001000000DA5C0000FFFF006D
++:1014200000E15C00008168AC0FE25C0000FFFF009F
++:1014300000EA5C0000FFFF0000F25C0000FFFF001C
++:1014400000FA5C000003000000415D0000DFBFE324
++:1014500004495D0000DFBFE304FA5D000000FFFF08
++:1014600000025E00001A000000125E00000300008F
++:10147000004A5F0000020100005A5F000004000003
++:1014800000625F0000040000006A5F0000040000CA
++:1014900000725F0000040000007A5F00000400009A
++:1014A00000825F000004000000925F0000FF070060
++:1014B00000A25F000002010000F25F0000040000D3
++:1014C00000FA5F0000040000000260000004000059
++:1014D000000A600000040000009A610000140C087B
++:1014E00000B261000004080000C2610000040000B6
++:1014F00000CA61000004000000D2610000120C105C
++:1015000008E261000004000000EA6100000400003D
++:1015100000FA6100001000000022620000040800D0
++:10152000002A62000001000000326200001A000080
++:10153000003A6200007F0000004A620000010000E3
++:101540000052620000140C080062620000120C10CD
++:10155000086A6200000400000072620000040000DB
++:10156000008262000010000000A262000001000082
++:1015700000AA620000120C1008E2620000FF0700DF
++:1015800000EA620000140C0800B2640000010000D0
++:1015900000CA64000010000000926600008800008D
++:1015A000009A66000088000000B266000004000097
++:1015B000006A6700002600000082670000000080CB
++:1015C0003FA26700001A000000AA67000010000098
++:1015D00000F26800005200000002690000260000CE
++:1015E0000012690000040000001A690000040000F5
++:1015F000002A6900001A0000004269000000FFFF95
++:101600000052690000040000005A69000004000054
++:10161000006A690000800000007269000004000098
++:10162000007A690000140C08008A690000FF0700B6
++:1016300000706C000004000000786C0000040000E2
++:10164000006C870000040000006D870000040000AB
++:10165000007487000003000000758700000300008D
++:1016600000048800000F000000058800000F000043
++:101670000064880000040000006588000004000089
++:10168000006C880000FFFF00006D880000FFFF0075
++:101690000074880000FFFF000075880000FFFF0055
++:1016A000007C880000FFFF00007D880000FFFF0035
++:1016B0000084880000FFFF000085880000FFFF0015
++:1016C00000CC88000001000000CD8800000100006F
++:1016D00000EC88000001000000ED8800000100001F
++:1016E000001C890000010000001D890000010000AD
++:1016F00000848900000100000085890000010000CD
++:10170000008C890000010000008D890000010000AC
++:10171000009489000002000000958900000200008A
++:10172000009C890000010000009D8900000100006C
++:1017300000A489000001000000A58900000100004C
++:1017400000AC89000002000000AD8900000200002A
++:1017500000B489000001000000B58900000100000C
++:1017600000C489000011000000C5890000110000BC
++:1017700000048A00008168AC0F058A00008168AC13
++:101780000F1C8A0000040000001D8A0000040000F5
++:10179000003C8A0000110000003D8A00001100009A
++:1017A00000448A000001000000458A00000100009A
++:1017B00000548A0000CF000000558A0000CF0000CE
++:1017C000005C8A0000CF0000005D8A0000CF0000AE
++:1017D00000648A0000CF000000658A0000CF00008E
++:1017E00000BC8A000001000000BD8A00000100006A
++:1017F00000C48A000001000000C58A00000100004A
++:1018000000CC8A000002000000CD8A000002000027
++:1018100000D48A000001000000D58A000001000009
++:1018200000DC8A000001000000DD8A0000010000E9
++:1018300000E48A000002000000E58A0000020000C7
++:1018400000EC8A000001000000ED8A0000010000A9
++:1018500000FC8A000001000000FD8A000001000079
++:1018600000048B000001000000058B000001000057
++:10187000000C8B0000010000000D8B000001000037
++:1018800000148B000001000000158B000001000017
++:10189000001C8B0000010000001D8B0000010000F7
++:1018A00000248B000001000000258B0000010000D7
++:1018B000002C8B0000010000002D8B0000010000B7
++:1018C00000348B000001000000358B000001000097
++:1018D000003C8B0000110000003D8B000011000057
++:1018E000007C8B00008168AC0F7D8B00008168ACB0
++:1018F0000F848B00000F000000858B00000F00009C
++:1019000000C48B000067FE1F00C58B000067FE1F30
++:1019100000DC8B000011000000DD8B0000110000D6
++:1019200000E48B000001000000E58B0000010000D6
++:1019300000048C000004000000058C00000400007E
++:1019400000348C000001000000358C000001000014
++:10195000005C8C0000110000005D8C000011000094
++:10196000009C8C00008168AC0F9D8C00008168ACED
++:101970000FBC8C000011000000BD8C0000110000A5
++:1019800000C48C000001000000C58C0000010000B4
++:1019900000D48C000001000000D58C000001000084
++:1019A00000E48C000001000000E58C000001000054
++:1019B00000F48C0000FF070000F58C0000FF07001A
++:1019C00000048D000001000000058D0000010000F2
++:1019D00000148D000001000000158D0000010000C2
++:1019E000006C8E0000080000006D8E0000080000F2
++:1019F00000748E000008000000758E0000080000D2
++:101A0000007C8E0000080000007D8E0000080000B1
++:101A100000848E000008000000858E000008000091
++:101A2000008C8E0000080000008D8E000008000071
++:101A300000948E000008000000958E000008000051
++:101A4000009C8E0000080000009D8E000008000031
++:101A500000A48E000008000000A58E000008000011
++:101A600000AC8E000011000000AD8E0000110000DF
++:101A700000EC8E00008168AC0FED8E00008168AC38
++:101A80000FF48E000000040000F58E00000004003A
++:101A900000FC8E000000040000FD8E000000040029
++:101AA00000048F000000040000058F000000040007
++:101AB000000C8F0000000400000D8F0000000400E7
++:101AC00000148F000000040000158F0000000400C7
++:101AD000001C8F0000000400001D8F0000000400A7
++:101AE00000248F000000040000258F000000040087
++:101AF000002C8F0000000400002D8F000000040067
++:101B000000348F000000030000358F000000030048
++:101B1000003C8F0000000300003D8F000000030028
++:101B200000448F000000030000458F000000030008
++:101B3000004C8F0000000300004D8F0000000300E8
++:101B400000548F000000030000558F0000000300C8
++:101B5000005C8F0000000300005D8F0000000300A8
++:101B600000648F000000030000658F000000030088
++:101B7000006C8F0000000300006D8F000000030068
++:101B800000748F000001000000758F00000100004C
++:101B9000007C8F00000F0000007D8F00000F000010
++:101BA00000BC8F000020000000BD8F00002000005E
++:101BB00000C48F000011000000C58F00001100005C
++:101BC00000CC8F000000010000CD8F00000001005C
++:101BD00000DC8F000001000000DD8F00000100002C
++:101BE00000F48F000040000000F58F00004000006E
++:101BF00000FC8F000000010000FD8F0000000100CC
++:101C0000000C900000030000000D90000003000095
++:101C1000003490000067FE1F003590000067FE1F33
++:101C200000549000000200000055900000020000E7
++:101C3000005C9000008168AC0F5D9000008168AC92
++:101C40000FAC90000001000000AD9000000100000A
++:101C500000D490000004000000D5900000040000B3
++:101C600000E490000001000000E590000001000089
++:101C700000EC90000000040000ED90000000040063
++:101C800000F490000000030000F590000000030045
++:101C900000FC90000001100000FD90000001100009
++:101CA000001C910000110000001D910000110000B7
++:101CB000005C9100008168AC0F5D9100008168AC10
++:101CC0000F649100000F000000659100000F0000FC
++:101CD000002492000067FE1F002592000067FE1F8F
++:101CE0000044920000110000004592000011000025
++:101CF000005C920000040000005D920000040000FF
++:101D0000006C920000010000006D920000010000D4
++:101D100000749200000100000075920000010000B4
++:101D20000094920000010000009592000001000064
++:101D300000BC92000001000000BD92000001000004
++:101D400000CC92000001000000CD920000010000D4
++:101D500000EC9200008824712AED92000088247122
++:101D60002AFC92000000C08540FD92000000C08562
++:101D70004004930000400000000593000040000074
++:101D8000000C930000000100000D93000000010012
++:101D900000149300000001010015930000000101F0
++:101DA000001C930000000080021D930000000080D2
++:101DB00002B4930000DFBFE304B5930000DFBFE38C
++:101DC00004BC930000DFBFE304BD930000DFBFE36A
++:101DD00004C493000001000000C59300000100004E
++:101DE00000D493000000FFFF00D593000000FFFF28
++:101DF00000DC93000001000000DD93000001000002
++:101E000000F493000000FFFF00F593000000FFFFC7
++:101E1000003C940000010000003D9400000100001F
++:101E2000004C940000010000004D940000010000EF
++:101E30000054940000001020305594000000102041
++:101E4000305C940000405060705D94000040506031
++:101E500070649400008898A8B8659400008898A8D9
++:101E6000B86C940000C8D8E8F86D940000C8D8E8B1
++:101E7000F87C9400001A0000007D9400001A000015
++:101E8000008C940000040000008D94000004000009
++:101E9000003C950000040000003D95000004000097
++:101EA0000044950000040000004595000004000077
++:101EB000004C950000808060004D9500008080609F
++:101EC00000749500000400000075950000040000F7
++:101ED000008C950000040000008D950000040000B7
++:101EE0000094950000040000009595000004000097
++:101EF000009C950000800000009D9500008000007F
++:101F000000A495000004000000A595000004000056
++:101F100000CC96000004000000CD960000040000F4
++:101F200000D496000080000000D5960000800000DC
++:101F300000DC96000004000000DD960000040000B4
++:101F400000E496000000010203E596000000010293
++:101F500003EC96000003000000ED96000003000073
++:101F600000F496000004000000F596000004000054
++:101F7000001C970000040000001D970000040000F2
++:101F800000249700000300000025970000030000D4
++:101F90000044970000040000004597000004000082
++:101FA00000A2B3000004000000AAB3000004000077
++:101FB00000BAB3000080000000C2B30000040000BB
++:101FC00000CAB3000001000000DAB30000270000DF
++:101FD00000EAB30000260000000AB4000000000080
++:101FE0000412B40000000000041AB4000000000055
++:101FF0000422B40000000000042AB4000000000025
++:102000000432B40000000000043AB40000000000F4
++:102010000442B40000000000044AB40000000000C4
++:102020000452B40000000000045AB4000000000094
++:102030000462B40000000000046AB4000000000064
++:102040000472B40000000000047AB4000000000034
++:102050000482B4000000000004AAB50000DFBFE362
++:1020600004B2B50000DFBFE304CAB5000021FE01E1
++:10207000006C180100040000006D1801000400004D
++:10208000007418010003000000751801000300002F
++:1020900000041901000F000000051901000F0000E5
++:1020A000006419010004000000651901000400002B
++:1020B000006C190100FFFF00006D190100FFFF0017
++:1020C0000074190100FFFF000075190100FFFF00F7
++:1020D000007C190100FFFF00007D190100FFFF00D7
++:1020E0000084190100FFFF000085190100FFFF00B7
++:1020F00000CC19010001000000CD19010001000011
++:1021000000EC19010001000000ED190100010000C0
++:10211000001C1A0100010000001D1A01000100004E
++:1021200000841A010001000000851A01000100006E
++:10213000008C1A0100010000008D1A01000100004E
++:1021400000941A010002000000951A01000200002C
++:10215000009C1A0100010000009D1A01000100000E
++:1021600000A41A010001000000A51A0100010000EE
++:1021700000AC1A010002000000AD1A0100020000CC
++:1021800000B41A010001000000B51A0100010000AE
++:1021900000C41A010011000000C51A01001100005E
++:1021A00000041B01008168AC0F051B01008168ACB5
++:1021B0000F1C1B0100040000001D1B010004000097
++:1021C000003C1B0100110000003D1B01001100003C
++:1021D00000441B010001000000451B01000100003C
++:1021E00000541B0100CF000000551B0100CF000070
++:1021F000005C1B0100CF0000005D1B0100CF000050
++:1022000000641B0100CF000000651B0100CF00002F
++:1022100000BC1B010001000000BD1B01000100000B
++:1022200000C41B010001000000C51B0100010000EB
++:1022300000CC1B010002000000CD1B0100020000C9
++:1022400000D41B010001000000D51B0100010000AB
++:1022500000DC1B010001000000DD1B01000100008B
++:1022600000E41B010002000000E51B010002000069
++:1022700000EC1B010001000000ED1B01000100004B
++:1022800000FC1B010001000000FD1B01000100001B
++:1022900000041C010001000000051C0100010000F9
++:1022A000000C1C0100010000000D1C0100010000D9
++:1022B00000141C010001000000151C0100010000B9
++:1022C000001C1C0100010000001D1C010001000099
++:1022D00000241C010001000000251C010001000079
++:1022E000002C1C0100010000002D1C010001000059
++:1022F00000341C010001000000351C010001000039
++:10230000003C1C0100110000003D1C0100110000F8
++:10231000007C1C01008168AC0F7D1C01008168AC51
++:102320000F841C01000F000000851C01000F00003D
++:1023300000C41C010067FE1F00C51C010067FE1FD2
++:1023400000DC1C010011000000DD1C010011000078
++:1023500000E41C010001000000E51C010001000078
++:1023600000041D010004000000051D010004000020
++:1023700000341D010001000000351D0100010000B6
++:10238000005C1D0100110000005D1D010011000036
++:10239000009C1D01008168AC0F9D1D01008168AC8F
++:1023A0000FBC1D010011000000BD1D010011000047
++:1023B00000C41D010001000000C51D010001000056
++:1023C00000D41D010001000000D51D010001000026
++:1023D00000E41D010001000000E51D0100010000F6
++:1023E00000F41D0100FF070000F51D0100FF0700BC
++:1023F00000041E010001000000051E010001000094
++:1024000000141E010001000000151E010001000063
++:10241000006C1F0100080000006D1F010008000093
++:1024200000741F010008000000751F010008000073
++:10243000007C1F0100080000007D1F010008000053
++:1024400000841F010008000000851F010008000033
++:10245000008C1F0100080000008D1F010008000013
++:1024600000941F010008000000951F0100080000F3
++:10247000009C1F0100080000009D1F0100080000D3
++:1024800000A41F010008000000A51F0100080000B3
++:1024900000AC1F010011000000AD1F010011000081
++:1024A00000EC1F01008168AC0FED1F01008168ACDA
++:1024B0000FF41F010000040000F51F0100000400DC
++:1024C00000FC1F010000040000FD1F0100000400CB
++:1024D00000042001000004000005200100000400A9
++:1024E000000C200100000400000D20010000040089
++:1024F0000014200100000400001520010000040069
++:10250000001C200100000400001D20010000040048
++:102510000024200100000400002520010000040028
++:10252000002C200100000400002D20010000040008
++:1025300000342001000003000035200100000300EA
++:10254000003C200100000300003D200100000300CA
++:1025500000442001000003000045200100000300AA
++:10256000004C200100000300004D2001000003008A
++:10257000005420010000030000552001000003006A
++:10258000005C200100000300005D2001000003004A
++:10259000006420010000030000652001000003002A
++:1025A000006C200100000300006D2001000003000A
++:1025B00000742001000100000075200100010000EE
++:1025C000007C2001000F0000007D2001000F0000B2
++:1025D00000BC20010020000000BD20010020000000
++:1025E00000C420010011000000C5200100110000FE
++:1025F00000CC20010000010000CD200100000100FE
++:1026000000DC20010001000000DD200100010000CD
++:1026100000F420010040000000F52001004000000F
++:1026200000FC20010000010000FD2001000001006D
++:10263000000C210100030000000D21010003000037
++:10264000003421010067FE1F003521010067FE1FD5
++:102650000054210100020000005521010002000089
++:10266000005C2101008168AC0F5D2101008168AC34
++:102670000FAC21010001000000AD210100010000AC
++:1026800000D421010004000000D521010004000055
++:1026900000E421010001000000E52101000100002B
++:1026A00000EC21010000040000ED21010000040005
++:1026B00000F421010000030000F5210100000300E7
++:1026C00000FC21010001100000FD210100011000AB
++:1026D000001C220100110000001D22010011000059
++:1026E000005C2201008168AC0F5D2201008168ACB2
++:1026F0000F642201000F000000652201000F00009E
++:10270000002423010067FE1F002523010067FE1F30
++:1027100000442301001100000045230100110000C6
++:10272000005C230100040000005D230100040000A0
++:10273000006C230100010000006D23010001000076
++:102740000074230100010000007523010001000056
++:102750000094230100010000009523010001000006
++:1027600000BC23010001000000BD230100010000A6
++:1027700000CC23010001000000CD23010001000076
++:1027800000EC2301008824712AED230100882471C4
++:102790002AFC23010000C08540FD23010000C08504
++:1027A0004004240100400000000524010040000016
++:1027B000000C240100000100000D240100000100B4
++:1027C0000014240100000101001524010000010192
++:1027D000001C240100000080021D24010000008074
++:1027E00002B4240100DFBFE304B5240100DFBFE32E
++:1027F00004BC240100DFBFE304BD240100DFBFE30C
++:1028000004C424010001000000C5240100010000EF
++:1028100000D424010000FFFF00D524010000FFFFC9
++:1028200000DC24010001000000DD240100010000A3
++:1028300000F424010000FFFF00F524010000FFFF69
++:10284000003C250100010000003D250100010000C1
++:10285000004C250100010000004D25010001000091
++:1028600000542501000010203055250100001020E3
++:10287000305C250100405060705D250100405060D3
++:1028800070642501008898A8B8652501008898A87B
++:10289000B86C250100C8D8E8F86D250100C8D8E853
++:1028A000F87C2501001A0000007D2501001A0000B7
++:1028B00000D84E010004000000E04E0100040000BA
++:1028C00000E84E0100120C1008F04E010003000059
++:1028D00000004F0100120C1008104F0100140C08EA
++:1028E00000184F010001000000204F0100140C08E7
++:1028F00000384F0100120C1008404F010027000063
++:1029000000584F010001000000686D010001000047
++:1029100000206E0100120C1008886E0100000000FB
++:1029200004906E010000000004A06E010080000011
++:1029300000C06E010080000000D06E01003F00006A
++:1029400000286F010002000000306F01000000004D
++:1029500004386F010000000004806F0100040000D3
++:1029600000A06F01000400000030700100010000B1
++:1029700000387001000110000040700100FFFF00EE
++:102980000048700100FFFF000050700100FFFF00D1
++:102990000058700100FFFF0000607101000000801E
++:1029A0003F687101000000803F70710100000080ED
++:1029B0003F787101000000803F80710100000080BD
++:1029C0003F887101000000803F907101000000808D
++:1029D0003F987101000000803FA07101000000805D
++:1029E0003FA87101000000803FB07101000000802D
++:1029F0003FB87101000000803FC0710100000080FD
++:102A00003FC87101000000803FD0710100000080CC
++:102A10003FD87101000000803FE07101001000000C
++:102A200000F87101000300000040720100120C1058
++:102A30000849720100800000004A72010080000015
++:102A4000004B720100800000005072010080000005
++:102A50000051720100047000805272010004700085
++:102A600080537201000470008058720100047000ED
++:102A70008059720100000400045A72010000040031
++:102A8000045B720100000400046072010000040095
++:102A90000461720100001000006272010000100069
++:102AA0000063720100001000006872010000100055
++:102AB0000079720100010000007A7201000100003B
++:102AC000007B720100010000008072010001000023
++:102AD00000917201000100000092720100010000EB
++:102AE00000937201000100000098720100010000D3
++:102AF00000A172010004000000A2720100040000A5
++:102B000000A372010004000000A87201000400008C
++:102B100000A972010002000000AA72010002000078
++:102B200000AB72010002000000B072010002000060
++:102B30000009730100800000000A7301008000009A
++:102B4000000B730100800000001073010080000082
++:102B50000011730100047000801273010004700002
++:102B6000801373010004700080187301000470006A
++:102B70008019730100000400041A730100000400AE
++:102B8000041B730100000400042073010000040012
++:102B900004217301000010000022730100001000E6
++:102BA00000237301000010000028730100001000D2
++:102BB0000039730100010000003A730100010000B8
++:102BC000003B7301000100000040730100010000A0
++:102BD0000051730100010000005273010001000068
++:102BE0000053730100010000005873010001000050
++:102BF0000061730100040000006273010004000022
++:102C00000063730100040000006873010004000009
++:102C10000069730100020000006A730100020000F5
++:102C2000006B7301000200000070730100020000DD
++:102C300000C1730100120C1008C2730100120C10C5
++:102C400008C3730100120C1008C8730100120C10A5
++:102C500008F9730100FFFF0000FA730100FFFF0095
++:102C600000FB730100FFFF000000740100FFFF0084
++:102C70000001740100FFFF000002740100FFFF006B
++:102C80000003740100FFFF000008740100FFFF0053
++:102C90000009740100FFFF00000A740100FFFF003B
++:102CA000000B740100FFFF000010740100FFFF0023
++:102CB0000011740100FFFF000012740100FFFF000B
++:102CC0000013740100FFFF000018740100FFFF00F3
++:102CD0000019740100010000001A740100010000D5
++:102CE000001B7401000100000020740100010000BD
++:102CF00000217401000100010022740100010001A3
++:102D0000002374010001000100287401000100018A
++:102D10000029740100010001002A74010001000172
++:102D2000002B74010001000100307401000100015A
++:102D30000031740100010000003274010001000044
++:102D4000003374010001000000387401000100002C
++:102D5000004174010021FE01004274010021FE01C6
++:102D6000004374010021FE01004874010021FE01AE
++:102D70000071740100120C100872740100120C1022
++:102D80000873740100120C100878740100120C1002
++:102D90000879740100040000007A74010004000046
++:102DA000007B740100040000008074010004000036
++:102DB0000089740100020000008A74010002000012
++:102DC000008B7401000200000090740100020000FA
++:102DD00000917401001100000092740100110000C4
++:102DE00000937401001100000098740100110000AC
++:102DF00000D97401008168AC0FDA7401008168ACFD
++:102E00000FDB7401008168AC0FE07401008168ACD5
++:102E10000F017501000400000002750100040000AC
++:102E200000037501000400000008750100040000A3
++:102E300000517501000200000052750100020000FF
++:102E400000537501000200000058750100020000E7
++:102E50000059750100010000005A750100010000D1
++:102E6000005B7501000100000060750100010000B9
++:102E700000617501000100000062750100010000A1
++:102E80000063750100010000006875010001000089
++:102E90000069750100020000006A7501000200006F
++:102EA000006B750100020000007075010002000057
++:102EB0000071750100010000007275010001000041
++:102EC0000073750100010000007875010001000029
++:102ED0000079750100010000007A75010001000011
++:102EE000007B7501000100000080750100010000F9
++:102EF00000817501000100000082750100010000E1
++:102F000000837501000100000088750100010000C8
++:102F1000009175010004000000927501000400009A
++:102F20000093750100040000009875010004000082
++:102F300000B192010011000000B2920100110000E6
++:102F400000B392010011000000B8920100110000CE
++:102F500000C192010001000000C2920100010000C6
++:102F600000C392010001000000C8920100010000AE
++:012F70000060
++:00000001FF
+diff --git a/firmware/nouveau/nv98.ctxprog.ihex b/firmware/nouveau/nv98.ctxprog.ihex
+new file mode 100644
+index 0000000..a3fa595
+--- /dev/null
++++ b/firmware/nouveau/nv98.ctxprog.ihex
+@@ -0,0 +1,57 @@
++:100000004E56435000DC008E0070009C00700020B3
++:10001000002000080060004C005000890E400000E5
++:100020000020000700600000003000FF00C000005A
++:10003000002000FF008000090070004DD9400044FE
++:10004000294000052940000D29400006B540000563
++:10005000006000C5154000110060000B274000C57E
++:100060002140000000700081007000040060004A20
++:10007000005000406C2100070060000127C0002EE6
++:1000800000200001008000CB005000FFFF9000FF27
++:10009000FF910020002000080060004C0050000983
++:1000A00000600045B540004DD040009D007000CF7D
++:1000B0002D40009F0070009F005000C02A4000802B
++:1000C000002000080060004F2A4000C02A4000CCF9
++:1000D0003040008100700000002000060060000039
++:1000E000007000FC1B110083007000000030004015
++:1000F0006C210007006000010AC0001E0020000102
++:10010000008000CB005000FF00C000800070008322
++:1001100000700047002000060060000A0211004045
++:100120000220000700600000003000FF00C000FF58
++:1001300000C80007C0400012292000FF0080008C8A
++:10014000504000CB0050003F02A0004000200006BD
++:100150000060000F007000020217000A0211003256
++:100160000020000D02100042021C000203120002D7
++:1001700004140000051800090513005005150005BA
++:100180000611000F002000070610000007110000F4
++:1001900009110002091200000A1100020B160028C2
++:1001A0000B12002B0B1400010C11000014110005A0
++:1001B00014110007141100091411000B141100CCC4
++:1001C000002000001510000F7940004B794000001E
++:1001D0004B21000700600025042000FF0088008FED
++:1001E0000070008C794000CB00500000000000053A
++:1001F0001A14000C1A1300001C1300041C12002017
++:100200001C1400251C1100001F1300401F1900E0E2
++:10021000954000AC012000060060004400200080F2
++:10022000201000C6201100C9201500D020190000A0
++:100230002112000321120000221600072212008062
++:100240002211000023110002231100802312008BD1
++:10025000231100942311009C231100000000000FC3
++:100260009C4000CB005000004B210007006000259F
++:10027000042000FF008800CB005000879F40000A48
++:100280000060000000000000AE4000A00070008090
++:10029000007000400220000700600004002000FF02
++:1002A00000C000FF008000CB005000000070000084
++:1002B00000200006006000FE1B11004DD040000031
++:1002C0000070000000200006006000FE1B1100808E
++:1002D0000070001D0070004D11400081007000048E
++:1002E0000060004A00500088BA40000B0060000027
++:1002F00000200006006000000070000BD04000FDF0
++:100300001B11004D42400012292000FD008000CB4F
++:100310000050000200C000400220000700600060A2
++:1003200001200002008000CB0050000218C000B283
++:1003300027200002008000CB0050004D4E40000BF3
++:100340000060004DCE400001007000030070000608
++:10035000D4400005D540000D006000050070000D80
++:10036000007000060070000B0070000E0070001C92
++:070370000070000C006000AA
++:00000001FF
+diff --git a/firmware/nouveau/nv98.ctxvals.ihex b/firmware/nouveau/nv98.ctxvals.ihex
+new file mode 100644
+index 0000000..9eb39ff
+--- /dev/null
++++ b/firmware/nouveau/nv98.ctxvals.ihex
+@@ -0,0 +1,326 @@
++:100000004E564356008702000043000000300000B7
++:10001000004C00000002000000750000000300001A
++:10002000007600000000100000860000000CFE00BA
++:10003000008B000000001000009600000087010007
++:10004000009B000000181000009C000000FF000052
++:1000500000AB00000004000000AC000000DF002541
++:1000600004AE00000000060000B400000000000024
++:1000700001B5000000FF000000B700000000040010
++:1000800000BD00000001000000BE00000080000074
++:1000900000BF00000004000000C6000000020000D5
++:1000A00000C700000001000000CB000000010000BC
++:1000B00000CC00000000010000D20000000200009F
++:1000C00000D300000001000000D400000001000087
++:1000D00000D800000001000000D9000000FFFF3F31
++:1000E00000DA000000FF1F0000DC0000000100003B
++:1000F00000DD00000001000000DF00000001000042
++:1001000000E000000001000000E10000000100002C
++:1001100000E200000004000000E300000001000015
++:1001200000E400000001000000E500000001000004
++:1001300000E600000007000000E7000000010000EA
++:1001400000E800000007000000E9000000010000D6
++:1001500000EA00000001000000EB000000010000C8
++:1001600000F000000001000000F1000000000100AC
++:1001700000F300000001000000F600000000010094
++:1001800000F700000001000000F80000000001007E
++:1001900000FA00000001000000FD00000000010066
++:1001A00000020100000400000003010000700000D4
++:1001B000000401000080000000090100000C0000A4
++:1001C000000B010000080000000C010000140000FA
++:1001D000000E010000290000000F010000270000B0
++:1001E00000100100002600000011010000080000BE
++:1001F00000120100000400000013010000270000AD
++:1002000000160100000100000017010000020000BC
++:1002100000180100000300000019010000040000A4
++:10022000001A010000050000001B0100000600008C
++:10023000001C010000070000001D0100000100007B
++:10024000002E010000CF0000003A010000800000F5
++:10025000003B010000040000003C0100000400001D
++:10026000003D010000030000003E0100000100000D
++:1002700000410100001200000042010000100000D7
++:1002800000430100000C00000044010000010000D8
++:1002900000480100000400000049010000020000C5
++:1002A000004A010000040000004D010000FFFF3F74
++:1002B000004E010000FF1F00005801000004000074
++:1002C0000059010000140000005A01000001000064
++:1002D000005D01000002000000600100000100005C
++:1002E0000062010000020000006301000000100035
++:1002F0000064010000000E00006501000000100015
++:100300000066010000001E000068010000010000FE
++:100310000069010000010000006A01000001000006
++:10032000006B010000010000006C010000010000F2
++:1003300000700100000002000072010000010000D6
++:1003400000730100007000000074010000800000D4
++:10035000007701000001000000780100007000003B
++:100360000079010000800000007D01000001000014
++:10037000007E010000CF00000080010000010000AD
++:100380000084010000CF0000008601000002000090
++:100390000088010000010000008A01000001000047
++:1003A000008C010000CF0000008D010000CF000094
++:1003B000008E0100000100000090010000800F008D
++:1003C000009601000021F8743B97010000018005B0
++:1003D0008999010000001000009A0100001F000030
++:1003E000009B010000FA107C029C010000C000008C
++:1003F000409D010000802089B7A001000040003925
++:1004000000A201000022000000A501000040003809
++:1004100000AC01000000008001AD010000000016EA
++:1004200000AE01000000008001B2010000FFFF03E8
++:1004300000B301000000008C11BC010000010401A8
++:1004400000BE01000078000000C0010000BF0000F5
++:1004500000C201000010120000C301000080000073
++:1004600008CC01000000008001CD01000000001652
++:1004700000CE01000000008001D2010000FFFF0358
++:1004800000D301000000008C11DC01000001040118
++:1004900000DE01000078000000E0010000BF000065
++:1004A00000E201000010120000E3010000800000E3
++:1004B00008ED01000070700200F0010000FFFFFF76
++:1004C00003F601000007041200F7010000071509F8
++:1004D00005F801000002020105F901000001020314
++:1004E000000002000040000000010200000A0B0CA6
++:1004F0000D020200001012140003020000F00100BF
++:1005000000040200000100000005020000030000DA
++:100510000008020000009E03000902000000010024
++:10052000000A020000003800000B020000404040BA
++:10053000000C0200000AFF00000E02000005F07728
++:10054000000F020000FF7F3F004C0200000400008B
++:1005500000610200000F00000083020000020000A2
++:100560000089020000200000009302000067FE1FC7
++:1005700000FB02000001000000FC0200000400007B
++:1005800000010300001A0000000303000010000037
++:100590000004030000040000000C030000808060E1
++:1005A000001B0300000100000034030000040000F1
++:1005B0000043030000040000004B0300000004009F
++:1005C000004C03000004000000530300000003007F
++:1005D0000054030000040000005B03000001100051
++:1005E000005C0300008000000063030000150000B1
++:1005F0000064030000001E00006C03000004000003
++:1006000000710300000400000079030000040000F2
++:1006100000890300000400000091030000080000AE
++:10062000009303000002000000A1030000FF070088
++:1006300000DB03000001000000E3030000100000E5
++:1006400000F303000001000000090400000F000097
++:100650000093040000100000009404000004000057
++:10066000009C04000080000000A4040000040000BE
++:1006700000AC04000000010203B404000003000009
++:1006800000BC040000001E0000C4040000040000C0
++:1006900000EC04000004000000F40400000300006B
++:1006A0000014050000040000001B0500000000808D
++:1006B0003F230500000000803F2B05000000008064
++:1006C0003F330500000000803F3B05000000008034
++:1006D0003F430500000000803F4B05000000008004
++:1006E0003F530500000000803F5B050000000080D4
++:1006F0003F630500000000803F6B050000000080A4
++:100700003F730500000000803F7B05000000008073
++:100710003F830500000000803F8B05000000008043
++:100720003F930500000000803F63060000100000BA
++:1007300000730600003F000000AB0600000100004F
++:1007400000BB06000001000000CB06000001000015
++:10075000003307000011000000730700000F0000C5
++:1007600000B307000011000000EB070000010000CB
++:1007700000F307000001000000FB0700000100007B
++:100780000003080000020000000B08000001000048
++:100790000013080000020000001B08000001000018
++:1007A000002B08000067FE1F003B0800008168ACBA
++:1007B0000FEB08000001000000F308000002000039
++:1007C00000FB080000010000000309000001000018
++:1007D000000B0900000200000013090000010000E6
++:1007E000001B090000010000003B0900001100008F
++:1007F000004309000001000000410D00000F00004F
++:1008000000510D000001000000291000000F000041
++:1008100000881E000021000000901E000001000062
++:1008200000981E000002000000A01E000000010051
++:1008300000A81E000000010000B01E000001000022
++:1008400000C81E000001000000D01E0000020000D1
++:1008500000D81E000000010000E01E0000000100A2
++:1008600000E81E0000010000003045000004000008
++:10087000003845000004000000D15100000F0000C6
++:1008800000795200000100000081520000000100C8
++:100890000089520000000100009152000011000088
++:1008A00000A152000008000000D152000001000029
++:1008B00000E152000001000000E9520000010000C8
++:1008C00000F152000001000000F9520000CF0000CA
++:1008D0000001530000020000003953000001000035
++:1008E00000495300000100000051530000010000C6
++:1008F0000059530000010000008153000004000073
++:100900000091530000010000009953000015000001
++:1009100000B95300008044440499550000120C10A3
++:1009200008C155000000010000D955000001000178
++:1009300000E955000001000100F155000001000030
++:1009400000F95500000100010001560000010000FF
++:1009500000095600000400000011560000020000CB
++:100960000012560000FFFF3F002A560000FF1F0044
++:10097000003C560000040000004456000003000044
++:1009800000A25600000000803FD45600000F000077
++:1009900000DA56000004000000E25600001A0000D1
++:1009A00000FA560000010000003457000004000067
++:1009B000003C570000FFFF000044570000FFFF000D
++:1009C000004C570000FFFF000054570000FFFF00DD
++:1009D000009257000000FFFF009C5700000100003C
++:1009E00000BC57000001000000CA5700000F0000C3
++:1009F00000EC570000010000000A5800008168ACBC
++:100A00000F125800001100000054580000010000AF
++:100A1000005C580000010000006458000002000063
++:100A2000006C580000010000007458000001000034
++:100A3000007C580000020000008458000001000003
++:100A400000925800000400000094580000110000BB
++:100A500000BA58000002000000C258000000000068
++:100A600004CA58000000000004D45800008168AC9B
++:100A70000FEA58000005000000EC580000040000D8
++:100A800000F2580000520000000C59000011000054
++:100A90000014590000010000001A59000001000074
++:100AA0000024590000CF0000002C590000CF0000A6
++:100AB0000034590000CF00000071590000DFBFE38F
++:100AC0000479590000DFBFE3048C590000010000E5
++:100AD0000094590000010000009C59000002000031
++:100AE00000A15900008168AC0FA2590000000080ED
++:100AF0003FA459000001000000AA59000000008036
++:100B00003FAC59000001000000B259000000008015
++:100B10003FB459000002000000BA590000000080F4
++:100B20003FBC59000001000000C2590000000080D5
++:100B30003FCA5900000000803FCC5900000100006E
++:100B400000D25900000000803FD45900000100008D
++:100B500000DA5900000000803FDC5900000100006D
++:100B600000E25900000000803FE45900000100004D
++:100B700000EA5900000000803FEC5900000100002D
++:100B800000F25900000000803FF45900000100000D
++:100B900000FA5900000000803FFC590000010000ED
++:100BA00000015A0000DFBFE304025A000000008089
++:100BB0003F045A000001000000095A0000DFBFE3B3
++:100BC000040A5A00000000803F0C5A000011000087
++:100BD00000125A00000000803F1A5A0000000080F6
++:100BE0003F225A0000100000004C5A00008168ACFF
++:100BF0000F545A00000F000000945A000067FE1FB7
++:100C000000AC5A000011000000B45A0000010000BE
++:100C100000D45A000004000000045B000001000042
++:100C2000002C5B0000110000005A5B0000120C1049
++:100C300008625B0000050000006C5B00008168AC8E
++:100C40000F7A5B0000010000008A5B0000FFFF00DC
++:100C5000008C5B000011000000925B0000FFFF00B1
++:100C600000945B0000010000009A5B0000FFFF00A1
++:100C700000A25B0000FFFF0000A45B000001000079
++:100C800000AA5B000003000000B45B00000100004C
++:100C900000C45B0000FF070000D45B0000010000FF
++:100CA00000E45B000001000000AA5C000000FFFF00
++:100CB00000B25C00001A000000C25C0000030000EB
++:100CC000003C5D000008000000445D0000080000DA
++:100CD000004C5D000008000000545D0000080000AA
++:100CE000005C5D000008000000645D00000800007A
++:100CF000006C5D000008000000745D00000800004A
++:100D0000007C5D000011000000BC5D00008168AC4B
++:100D10000FC45D000000040000CC5D000000040072
++:100D200000D45D000000040000DC5D000000040051
++:100D300000E45D000000040000EC5D000000040021
++:100D400000F45D000000040000FA5D0000020100F4
++:100D500000FC5D000000040000045E0000000300D1
++:100D6000000A5E0000040000000C5E0000000300AA
++:100D700000125E000004000000145E00000003008A
++:100D8000001A5E0000040000001C5E00000003006A
++:100D900000225E000004000000245E00000003004A
++:100DA000002A5E0000040000002C5E00000003002A
++:100DB00000325E000004000000345E00000003000A
++:100DC000003C5E000000030000425E0000FF0700E0
++:100DD00000445E0000010000004C5E00000F0000B7
++:100DE00000525E0000020100008C5E000020000046
++:100DF00000945E0000110000009C5E0000000100F5
++:100E000000A25E000004000000AA5E0000040000D2
++:100E100000AC5E000001000000B25E0000040000B3
++:100E200000BA5E000004000000C45E000040000044
++:100E300000CC5E000000010000DC5E00000300004A
++:100E400000045F000067FE1F00245F000002000036
++:100E5000002C5F00008168AC0F7C5F000001000087
++:100E600000A45F000004000000B45F000001000067
++:100E700000BC5F000000040000C45F00000003002D
++:100E800000CC5F000001100000EC5F0000110000CA
++:100E9000002C6000008168AC0F346000000F00007F
++:100EA000004A600000140C080062600000040800A2
++:100EB0000072600000040000007A6000000400007E
++:100EC0000082600000120C10089260000004000014
++:100ED000009A60000004000000AA600000100000FA
++:100EE00000D260000004080000DA60000001000089
++:100EF00000E26000001A000000EA6000007F0000CD
++:100F000000F460000067FE1F00FA600000010000AE
++:100F10000002610000140C080012610000120C10A5
++:100F20000814610000110000001A610000040000B4
++:100F30000022610000040000002C61000004000099
++:100F40000032610000100000003C61000001000060
++:100F50000044610000010000005261000001000037
++:100F6000005A610000120C100864610000010000CA
++:100F7000008C6100000100000092610000FF07008A
++:100F8000009A610000140C08009C61000001000040
++:100F900000BC6100008824712ACC61000000C0857B
++:100FA00040D461000040000000DC6100000001004E
++:100FB00000E461000000010100EC6100000000801D
++:100FC0000284620000DFBFE3048C620000DFBFE345
++:100FD000049462000001000000A462000000FFFF12
++:100FE00000AC62000001000000C462000000FFFFCE
++:100FF000000C630000010000001C63000001000001
++:101000000024630000001020302C6300004050607A
++:1010100070346300008898A8B83C630000C8D8E822
++:10102000F84C6300001A0000006263000001000039
++:10103000007A630000100000004265000088000094
++:10104000004A65000088000000626500000400009E
++:10105000001A6600002600000032660000000080D2
++:101060003F526600001A0000005A6600001000009F
++:1010700000A267000052000000B2670000260000D6
++:1010800000C267000004000000CA670000040000FE
++:1010900000DA6700001A000000F267000000FFFF9E
++:1010A0000002680000040000000A6800000400005C
++:1010B000001A6800008000000022680000040000A0
++:1010C000002A680000140C08003A680000FF0700BE
++:1010D0000052920000040000005A92000004000038
++:1010E000006A92000080000000729200000400007C
++:1010F000007A920000010000008A920000270000A0
++:10110000009A92000026000000BA92000000000041
++:1011100004C292000000000004CA92000000000017
++:1011200004D292000000000004DA920000000000E7
++:1011300004E292000000000004EA920000000000B7
++:1011400004F292000000000004FA92000000000087
++:101150000402930000000000040A93000000000055
++:101160000412930000000000041A93000000000025
++:101170000422930000000000042A930000000000F5
++:101180000432930000000000045A940000DFBFE323
++:101190000462940000DFBFE3047A94000021FE01A2
++:1011A000009827010004000000A0270100040000AF
++:1011B00000A8270100120C1008B02701000300004E
++:1011C00000C0270100120C1008D0270100140C08E1
++:1011D00000D827010001000000E0270100140C08DE
++:1011E00000F8270100120C10080028010027000059
++:1011F000001828010001000000284601000100003D
++:1012000000E0460100120C100848470100000000F1
++:101210000450470100000000046047010080000006
++:10122000008047010080000000904701003F00005F
++:1012300000E847010002000000F047010000000044
++:1012400004F84701000000000440480100040000C9
++:10125000006048010004000000F0480100010000A7
++:1012600000F84801000110000000490100FFFF00E4
++:101270000008490100FFFF000010490100FFFF00C6
++:101280000018490100FFFF0000204A010000008013
++:101290003F284A01000000803F304A0100000080E2
++:1012A0003F384A01000000803F404A0100000080B2
++:1012B0003F484A01000000803F504A010000008082
++:1012C0003F584A01000000803F604A010000008052
++:1012D0003F684A01000000803F704A010000008022
++:1012E0003F784A01000000803F804A0100000080F2
++:1012F0003F884A01000000803F904A0100000080C2
++:101300003F984A01000000803FA04A010010000001
++:1013100000B84A010003000000004B0100120C104D
++:1013200008104B010080000000184B010004700001
++:1013300080204B010000040004284B010000100035
++:1013400000404B0100000E0000484B0100001E0051
++:1013500000504B010001000000684B01000100003B
++:1013600000784B010004000000804B0100020000E7
++:1013700000D84B0100120C1008104C0100FFFF00B8
++:1013800000184C0100FFFF0000204C0100FFFF008F
++:1013900000284C0100FFFF0000304C01000100005C
++:1013A00000384C010001000100404C010001000127
++:1013B00000484C010001000000584C010021FE01D2
++:1013C00000884C0100120C1008904C010004000031
++:1013D00000A04C010002000000A84C010011000018
++:1013E00000F04C01008168AC0F184D0100040000B2
++:1013F00000684D010002000000704D010001000076
++:1014000000784D010001000000804D010002000045
++:1014100000884D010001000000904D010001000016
++:1014200000984D010001000000A84D0100040000DB
++:1014300000A86A010011000000B86A010001000064
++:0114400000AB
++:00000001FF
+diff --git a/firmware/nouveau/nva0.ctxprog.ihex b/firmware/nouveau/nva0.ctxprog.ihex
+new file mode 100644
+index 0000000..f1133e3
+--- /dev/null
++++ b/firmware/nouveau/nva0.ctxprog.ihex
+@@ -0,0 +1,89 @@
++:100000004E564350005D019C007000000030000916
++:100010002C4500092D400051204100440A400005B4
++:100020000A40000D0A40008E0070004D1240009DF5
++:100030000070004D3B450097007000213C4500A139
++:100040004644004D9144004D9D44001D0070000643
++:1000500018400005006000454444008B30440045D2
++:100060001840004D23400081007000CF1C40009FCD
++:100070000070009F0050004D174500170070000BE6
++:100080002340004D984400213D4500A1564400A066
++:10009000007000010070000300700006274000059A
++:1000A0002840000D006000050070000D0070000683
++:1000B000007000020070000B0070000E0070001C49
++:1000C0000070000C00600000000000FFFF9000FFC7
++:1000D000FF91004D0F4500090060004D004800965B
++:1000E000007000CF3A40009F0070009F0050005108
++:1000F000204100C036400080002000080060004F12
++:10010000364000C0364000CC3E400051364000161C
++:100110000070004D004800110060004D0048004D87
++:100120003644008E007000810070004D8B44004DFD
++:100130009844008300700000003000807C2100079C
++:10014000006000010AC0002200200001008000CBF6
++:10015000005000FF00C0004D5E44004D0048000804
++:100160000945004D8E44004DA644004D5E44004DAF
++:100170001D45004D9144004D9D44004D00480083B5
++:100180000070004D3E45003F02A0004000200006E8
++:100190000060004D3745004DA844002B0220006050
++:1001A000EF4400BA0220000100300061EF44004932
++:1001B0000320000200300062EF4400D80320000357
++:1001C00000300063EF440067042000040030006446
++:1001D000EF4400F60420000500300065EF44008580
++:1001E0000520000600300066EF44001406200007DA
++:1001F00000300067EF4400A30620000800300068CC
++:10020000EF4400320720000900300069EF4400008D
++:10021000082000FFFF38004D04450000003000CBEF
++:100220000050004D564500CB005000070B45004DD7
++:100230000048004D944400FC1B11004D0048004D47
++:10024000944400FD1B11004D0048004D944400FEF5
++:100250001B11004D00480000002000000070000647
++:100260000060004D00480001002000060060004DC5
++:100270003745000A0211004D00480000003000FF21
++:10028000FFC30000002000070060000000700008AD
++:10029000002000FF008000CB0050004D004800000F
++:1002A0000000004D0048000000000002021700326C
++:1002B0000020000D02100042021E00C002110002C8
++:1002C0000312000204150000051800090513005070
++:1002D0000515000506110013002000070610000098
++:1002E0000711000009110002091200000A110002A2
++:1002F0000B1600280B12002B0B1400010C1100012F
++:100300000D1100001411000514110007141100094B
++:100310001411000B141100D400200000151000056A
++:100320001A14000C1A1300001C1300041C130020E4
++:100330001C1400251C1300401C1300441C130060F7
++:100340001C1400651C1300801C1300841C1300A0E7
++:100350001C1400A51C1300C01C1300C41C1300E0D7
++:100360001C1400E51C1300001D1300041D130020C5
++:100370001D1400251D1300401D1300441D130060B3
++:100380001D1400651D1300801D1300841D1300A0A3
++:100390001D1400A51D1300C01D1300C41D1300E093
++:1003A0001D1400E51D1300001F1300041F13000897
++:1003B0001F11000B1F110015002000401F10004DE1
++:1003C000004800060060004D564500202011002224
++:1003D0002011006000200040201000C0201500C83F
++:1003E000201100CA201400CF201B0000211200039E
++:1003F000211200402116004721120053211200A0B3
++:10040000211100C0211200CB211100D4211100D8EC
++:100410002115004D0048000000000000007000069B
++:100420000060004D3745004D0048000B0060004D56
++:100430000048000A0060004D0048000B0060004DBD
++:100440000D410020002000080060004C0050004DCD
++:10045000004800E8032000080060004C0050004DF8
++:10046000004800040060004A0050004D004800FFB2
++:1004700000C000FF00C8004D004800FF00C000FFA2
++:1004800000C8004D004800160070008E0070008209
++:10049000007000410050004D13450095007000D1E0
++:1004A000005000160060005200500002007000155D
++:1004B0000070004D2840008E0070004D0F45000078
++:1004C0000020000700600000003000FF00C00000B6
++:1004D000002000FF008000090070000E0070004D39
++:1004E0000048008000700017004800000070004DB8
++:1004F0000048004D0048004D0048004D0048008E67
++:100500000070004D0F4500830070004D1A45004DEE
++:100510004745000F0070008C464100CB0050004D55
++:100520000048000008200007006000874B45004D90
++:10053000004800000000006C2120004D374500FFFE
++:100540000080004D0048008013210007006000205B
++:100550000D20004D374500FF0088004D0048000F7A
++:100560000048004B0048004D5045008F0070008C43
++:0B057000004800CB0050004D00480088
++:00000001FF
+diff --git a/firmware/nouveau/nva0.ctxvals.ihex b/firmware/nouveau/nva0.ctxvals.ihex
+new file mode 100644
+index 0000000..19c647e
+--- /dev/null
++++ b/firmware/nouveau/nva0.ctxvals.ihex
+@@ -0,0 +1,1836 @@
++:100000004E56435600530E000043000000300000DF
++:10001000004B00000002000000740000000300001C
++:10002000007500000000100000880000000CFE00B9
++:10003000008E000000001000009900000087010001
++:10004000009E000000181000009F000000FF00004C
++:1000500000B200000004000000B3000000DF002533
++:1000600004B500000000060000BB00000000000016
++:1000700001BC000000FF000000BE000000000800FE
++:1000800000C400000001000000C5000000010000E5
++:1000900000C600000080000E00C700000004000041
++:1000A00000CE00000002000000CF000000010000B0
++:1000B00000D300000001000000D400000000010097
++:1000C00000DA00000002000000DB00000001000078
++:1000D00000DC00000001000000E000000001000062
++:1000E00000E1000000FFFF3F00E2000000FF1F00F2
++:1000F00000E400000001000000E500000001000035
++:1001000000E700000001000000E80000000100001E
++:1001100000E900000001000000EA00000004000007
++:1001200000EB00000001000000EC000000010000F6
++:1001300000ED00000001000000EE000000070000DC
++:1001400000EF00000001000000F0000000070000C8
++:1001500000F100000001000000F2000000010000BA
++:1001600000F300000001000000F8000000010000A2
++:1001700000F900000000010000FB00000001000089
++:1001800000FE00000000010000FF00000001000070
++:100190000000010000000100000201000001000059
++:1001A0000005010000000100000A01000004000039
++:1001B000000B010000700000000C01000080000036
++:1001C00000110100000C00000013010000080000F5
++:1001D00000140100001400000016010000290000B6
++:1001E0000017010000270000001801000026000091
++:1001F0000019010000080000001A010000040000BE
++:10020000001B010000270000001E0100000100008B
++:10021000001F010000020000002001000003000098
++:100220000021010000040000002201000005000080
++:100230000023010000060000002401000007000068
++:1002400000250100000100000036010000CF000081
++:100250000042010000800000004301000004000093
++:1002600000440100000400000045010000030000FC
++:1002700000460100000100000049010000120000DA
++:10028000004A010000100000004B0100000C0000BB
++:10029000004C0100000100000050010000040000BB
++:1002A00000510100000200000052010000040000A3
++:1002B0000056010000FFFF3F0057010000FF1F0034
++:1002C0000061010000010000006201000001000067
++:1002D000006301000002000000650100000400004E
++:1002E000006601000014000000670100000100002A
++:1002F000006A010000020000006F01000001000020
++:1003000000710100000200000072010000001000F6
++:100310000073010000000E000074010000001000D6
++:100320000075010000001E000077010000010000C0
++:1003300000780100000100000079010000010000C8
++:10034000007A010000010000007B010000010000B4
++:10035000007F010000000200008101000001000098
++:100360000082010000F000000083010000FF000097
++:1003700000860100000100000087010000F000007D
++:100380000088010000FF0000008A01000009000051
++:10039000008D010000010000008E010000CF000070
++:1003A00000900100000100000094010000CF000057
++:1003B000009601000002000000980100000100000A
++:1003C000009A010000010000009C010000CF000025
++:1003D000009D010000CF0000009E01000001000010
++:1003E00000A0010000801F0000A601000021F87499
++:1003F0003BA701000001800589A901000000100051
++:1004000000AA010000FF000000AC010000FA107C0F
++:1004100002AD010000C0000040AE01000080208954
++:10042000B7B301000021F8743BB40100000180055E
++:1004300089B601000000100000B7010000FF0000B5
++:1004400000B9010000FA107C02BA010000C00000EF
++:1004500040BB010000802089B7C001000021F87472
++:100460003BC101000001800589C3010000001000AC
++:1004700000C4010000FF000000C6010000FA107C6B
++:1004800002C7010000C0000040C8010000802089B0
++:10049000B7CD01000021F8743BCE010000018005BA
++:1004A00089D001000000100000D1010000FF000011
++:1004B00000D3010000FA107C02D4010000C000004B
++:1004C00040D5010000802089B7DA01000021F874CE
++:1004D0003BDB01000001800589DD01000000100008
++:1004E00000DE010000FF000000E0010000FA107CC7
++:1004F00002E1010000C0000040E20100008020890C
++:10050000B7E701000021F8743BE801000001800515
++:1005100089EA01000000100000EB010000FF00006C
++:1005200000ED010000FA107C02EE010000C00000A6
++:1005300040EF010000802089B7F401000021F87429
++:100540003BF501000001800589F701000000100063
++:1005500000F8010000FF000000FA010000FA107C22
++:1005600002FB010000C0000040FC01000080208967
++:10057000B70102000021F8743B020200000180056F
++:1005800089040200000010000005020000FF0000C6
++:100590000007020000FA107C0208020000C0000000
++:1005A0004009020000802089B70E02000040003D93
++:1005B0000010020000220000001802000040003D70
++:1005C0000019020000220000002B0200000AFF00B8
++:1005D000002D020000000080012E02000000001625
++:1005E000002F0200000000800133020000FFFF0323
++:1005F000003402000000000C313D02000001040143
++:10060000003F0200007800000041020000BF00002F
++:1006100000430200001012000044020000800000AD
++:1006200008470200003E0000004D0200000000806C
++:10063000014E020000000016004F02000000008082
++:100640000153020000FFFF03005402000000000CF1
++:10065000315D020000010401005F0200007800002B
++:100660000061020000BF00000063020000101200E1
++:10067000006402000080000008670200003E0000E5
++:10068000006D020000000080016E020000000016F4
++:10069000006F0200000000800173020000FFFF03F2
++:1006A000007402000000000C317D02000001040112
++:1006B000007F0200007800000081020000BF0000FF
++:1006C000008302000010120000840200008000007D
++:1006D00008870200003E0000008E020000707012C9
++:1006E0000192020000FFFFFF0799020000070412B9
++:1006F000009A020000071509059B02000002020192
++:10070000059C02000001020300A30200004000005B
++:1007100000A40200000A0B0C0DA502000010121428
++:1007200000A6020000F0010000A702000001000086
++:1007300000A802000003000000AB020000009E03BE
++:1007400000AC02000000010000AD02000000380013
++:1007500000AE02000006E03F00AF02000000E03FF4
++:1007600000B002000040404000B102000007F0F776
++:100770000CB2020000FF7FBF02BA0200000AFF00B5
++:1007800000BC02000000008001BD02000000001655
++:1007900000BE02000000008001C2020000FFFF0353
++:1007A00000C302000000000C31CC02000001040173
++:1007B00000CE02000078000000D0020000BF000060
++:1007C00000D202000010120000D3020000800000DE
++:1007D00008D60200003E000000DC0200000000809D
++:1007E00001DD02000000001600DE020000000080B3
++:1007F00001E2020000FFFF0300E302000000000C22
++:1008000031EC02000001040100EE0200007800005B
++:1008100000F0020000BF000000F202000010120011
++:1008200000F302000080000008F60200003E000015
++:1008300000FC02000000008001FD02000000001624
++:1008400000FE0200000000800102030000FFFF0321
++:10085000000303000000000C310C03000001040140
++:10086000000E0300007800000010030000BF00002D
++:1008700000120300001012000013030000800000AB
++:1008800008160300003E0000001D030000707012F7
++:100890000121030000FFFFFF0728030000070412E7
++:1008A0000029030000071509052A030000020201C0
++:1008B000052B03000001020300320300004000008A
++:1008C00000330300000A0B0C0D3403000010121457
++:1008D0000035030000F001000036030000010000B5
++:1008E0000037030000030000003A030000009E03ED
++:1008F000003B030000000100003C03000000380042
++:10090000003D03000006E03F003E03000000E03F22
++:10091000003F030000404040004003000007F0F7A4
++:100920000C41030000FF7FBF02490300000AFF00E3
++:10093000004B030000000080014C03000000001683
++:10094000004D0300000000800151030000FFFF0381
++:10095000005203000000000C315B030000010401A1
++:10096000005D030000780000005F030000BF00008E
++:10097000006103000010120000620300008000000C
++:1009800008650300003E0000006B030000000080CB
++:10099000016C030000000016006D030000000080E1
++:1009A0000171030000FFFF03007203000000000C50
++:1009B000317B030000010401007D0300007800008A
++:1009C000007F030000BF0000008103000010120040
++:1009D000008203000080000008850300003E000044
++:1009E000008B030000000080018C03000000001653
++:1009F000008D0300000000800191030000FFFF0351
++:100A0000009203000000000C319B03000001040170
++:100A1000009D030000780000009F030000BF00005D
++:100A200000A103000010120000A2030000800000DB
++:100A300008A50300003E000000AC03000070701227
++:100A400001B0030000FFFFFF07B703000007041217
++:100A500000B803000007150905B9030000020201F0
++:100A600005BA03000001020300C1030000400000BA
++:100A700000C20300000A0B0C0DC303000010121487
++:100A800000C4030000F0010000C5030000010000E5
++:100A900000C603000003000000C9030000009E031D
++:100AA00000CA03000000010000CB03000000380072
++:100AB00000CC03000006E03F00CD03000000E03F53
++:100AC00000CE03000040404000CF03000007F0F7D5
++:100AD0000CD0030000FF7FBF02D80300000AFF0014
++:100AE00000DA03000000008001DB030000000016B4
++:100AF00000DC03000000008001E0030000FFFF03B2
++:100B000000E103000000000C31EA030000010401D1
++:100B100000EC03000078000000EE030000BF0000BE
++:100B200000F003000010120000F10300008000003C
++:100B300008F40300003E000000FA030000000080FB
++:100B400001FB03000000001600FC03000000008011
++:100B50000100040000FFFF03000104000000000C7E
++:100B6000310A040000010401000C040000780000B8
++:100B7000000E040000BF000000100400001012006E
++:100B8000001104000080000008140400003E000072
++:100B9000001A040000000080011B04000000001681
++:100BA000001C0400000000800120040000FFFF037F
++:100BB000002104000000000C312A0400000104019F
++:100BC000002C040000780000002E040000BF00008C
++:100BD000003004000010120000310400008000000A
++:100BE00008340400003E0000003B04000070701256
++:100BF000013F040000FFFFFF074604000007041246
++:100C0000004704000007150905480400000202011E
++:100C100005490400000102030050040000400000E8
++:100C200000510400000A0B0C0D52040000101214B5
++:100C30000053040000F00100005404000001000013
++:100C400000550400000300000058040000009E034B
++:100C50000059040000000100005A040000003800A0
++:100C6000005B04000006E03F005C04000000E03F81
++:100C7000005D040000404040005E04000007F0F703
++:100C80000C5F040000FF7FBF02670400000AFF0042
++:100C90000069040000000080016A040000000016E2
++:100CA000006B040000000080016F040000FFFF03E0
++:100CB000007004000000000C317904000001040100
++:100CC000007B040000780000007D040000BF0000ED
++:100CD000007F04000010120000800400008000006B
++:100CE00008830400003E000000890400000000802A
++:100CF000018A040000000016008B04000000008040
++:100D0000018F040000FFFF03009004000000000CAE
++:100D10003199040000010401009B040000780000E8
++:100D2000009D040000BF0000009F0400001012009E
++:100D300000A004000080000008A30400003E0000A2
++:100D400000A904000000008001AA040000000016B1
++:100D500000AB04000000008001AF040000FFFF03AF
++:100D600000B004000000000C31B9040000010401CF
++:100D700000BB04000078000000BD040000BF0000BC
++:100D800000BF04000010120000C00400008000003A
++:100D900008C30400003E000000CA04000070701286
++:100DA00001CE040000FFFFFF07D504000007041276
++:100DB00000D604000007150905D70400000202014F
++:100DC00005D804000001020300DF04000040000019
++:100DD00000E00400000A0B0C0DE1040000101214E6
++:100DE00000E2040000F0010000E304000001000044
++:100DF00000E404000003000000E7040000009E037C
++:100E000000E804000000010000E9040000003800D0
++:100E100000EA04000006E03F00EB04000000E03FB1
++:100E200000EC04000040404000ED04000007F0F733
++:100E30000CEE040000FF7FBF02F60400000AFF0072
++:100E400000F804000000008001F904000000001612
++:100E500000FA04000000008001FE040000FFFF0310
++:100E600000FF04000000000C31080500000104012F
++:100E7000000A050000780000000C050000BF00001B
++:100E8000000E050000101200000F05000080000099
++:100E900008120500003E0000001805000000008058
++:100EA0000119050000000016001A0500000000806E
++:100EB000011E050000FFFF03001F05000000000CDD
++:100EC0003128050000010401002A05000078000017
++:100ED000002C050000BF0000002E050000101200CD
++:100EE000002F05000080000008320500003E0000D1
++:100EF00000380500000000800139050000000016E0
++:100F0000003A050000000080013E050000FFFF03DD
++:100F1000003F05000000000C3148050000010401FD
++:100F2000004A050000780000004C050000BF0000EA
++:100F3000004E050000101200004F05000080000068
++:100F400008520500003E00000059050000707012B4
++:100F5000015D050000FFFFFF0764050000070412A4
++:100F6000006505000007150905660500000202017D
++:100F70000567050000010203006E05000040000047
++:100F8000006F0500000A0B0C0D7005000010121414
++:100F90000071050000F00100007205000001000072
++:100FA00000730500000300000076050000009E03AA
++:100FB00000770500000001000078050000003800FF
++:100FC000007905000006E03F007A05000000E03FE0
++:100FD000007B050000404040007C05000007F0F762
++:100FE0000C7D050000FF7FBF02850500000AFF00A1
++:100FF0000087050000000080018805000000001641
++:101000000089050000000080018D050000FFFF033E
++:10101000008E05000000000C31970500000104015E
++:101020000099050000780000009B050000BF00004B
++:10103000009D050000101200009E050000800000C9
++:1010400008A10500003E000000A705000000008088
++:1010500001A805000000001600A90500000000809E
++:1010600001AD050000FFFF0300AE05000000000C0D
++:1010700031B705000001040100B905000078000047
++:1010800000BB050000BF000000BD050000101200FD
++:1010900000BE05000080000008C10500003E000001
++:1010A00000C705000000008001C805000000001610
++:1010B00000C905000000008001CD050000FFFF030E
++:1010C00000CE05000000000C31D70500000104012E
++:1010D00000D905000078000000DB050000BF00001B
++:1010E00000DD05000010120000DE05000080000099
++:1010F00008E10500003E000000E8050000707012E5
++:1011000001EC050000FFFFFF07F3050000070412D4
++:1011100000F405000007150905F5050000020201AD
++:1011200005F605000001020300FD05000040000077
++:1011300000FE0500000A0B0C0DFF05000010121444
++:101140000000060000F001000001060000010000A0
++:1011500000020600000300000005060000009E03D8
++:10116000000606000000010000070600000038002D
++:10117000000806000006E03F000906000000E03F0E
++:10118000000A060000404040000B06000007F0F790
++:101190000C0C060000FF7FBF02140600000AFF00CF
++:1011A000001606000000008001170600000000166F
++:1011B0000018060000000080011C060000FFFF036D
++:1011C000001D06000000000C31260600000104018D
++:1011D0000028060000780000002A060000BF00007A
++:1011E000002C060000101200002D060000800000F8
++:1011F00008300600003E00000036060000000080B7
++:1012000001370600000000160038060000000080CC
++:10121000013C060000FFFF03003D06000000000C3B
++:101220003146060000010401004806000078000075
++:10123000004A060000BF0000004C0600001012002B
++:10124000004D06000080000008500600003E00002F
++:10125000005606000000008001570600000000163E
++:101260000058060000000080015C060000FFFF033C
++:10127000005D06000000000C31660600000104015C
++:101280000068060000780000006A060000BF000049
++:10129000006C060000101200006D060000800000C7
++:1012A00008700600003E0000007706000070701213
++:1012B000017B060000FFFFFF078206000007041203
++:1012C00000830600000715090584060000020201DC
++:1012D0000585060000010203008C060000400000A6
++:1012E000008D0600000A0B0C0D8E06000010121473
++:1012F000008F060000F001000090060000010000D1
++:1013000000910600000300000094060000009E0308
++:10131000009506000000010000960600000038005D
++:10132000009706000006E03F009806000000E03F3E
++:101330000099060000404040009A06000007F0F7C0
++:101340000C9B060000FF7FBF02A30600000AFF00FF
++:1013500000A506000000008001A60600000000169F
++:1013600000A706000000008001AB060000FFFF039D
++:1013700000AC06000000000C31B5060000010401BD
++:1013800000B706000078000000B9060000BF0000AA
++:1013900000BB06000010120000BC06000080000028
++:1013A00008BF0600003E000000C5060000000080E7
++:1013B00001C606000000001600C7060000000080FD
++:1013C00001CB060000FFFF0300CC06000000000C6C
++:1013D00031D506000001040100D7060000780000A6
++:1013E00000D9060000BF000000DB0600001012005C
++:1013F00000DC06000080000008DF0600003E000060
++:1014000000E506000000008001E60600000000166E
++:1014100000E706000000008001EB060000FFFF036C
++:1014200000EC06000000000C31F50600000104018C
++:1014300000F706000078000000F9060000BF000079
++:1014400000FB06000010120000FC060000800000F7
++:1014500008FF0600003E0000000607000070701242
++:10146000010A070000FFFFFF071107000007041231
++:10147000001207000007150905130700000202010A
++:101480000514070000010203001B070000400000D4
++:10149000001C0700000A0B0C0D1D070000101214A1
++:1014A000001E070000F00100001F070000010000FF
++:1014B00000200700000300000023070000009E0337
++:1014C000002407000000010000250700000038008C
++:1014D000002607000006E03F002707000000E03F6D
++:1014E0000028070000404040002907000007F0F7EF
++:1014F0000C2A070000FF7FBF02320700000AFF002E
++:1015000000340700000000800135070000000016CD
++:101510000036070000000080013A070000FFFF03CB
++:10152000003B07000000000C3144070000010401EB
++:1015300000460700007800000048070000BF0000D8
++:10154000004A070000101200004B07000080000056
++:10155000084E0700003E0000005407000000008015
++:10156000015507000000001600560700000000802B
++:10157000015A070000FFFF03005B07000000000C9A
++:1015800031640700000104010066070000780000D4
++:101590000068070000BF0000006A0700001012008A
++:1015A000006B070000800000086E0700003E00008E
++:1015B000007407000000008001750700000000169D
++:1015C0000076070000000080017A070000FFFF039B
++:1015D000007B07000000000C3184070000010401BB
++:1015E00000860700007800000088070000BF0000A8
++:1015F000008A070000101200008B07000080000026
++:10160000088E0700003E0000009507000070701271
++:101610000199070000FFFFFF07A007000007041261
++:1016200000A107000007150905A20700000202013A
++:1016300005A307000001020300AA07000040000004
++:1016400000AB0700000A0B0C0DAC070000101214D1
++:1016500000AD070000F0010000AE0700000100002F
++:1016600000AF07000003000000B2070000009E0367
++:1016700000B307000000010000B4070000003800BC
++:1016800000B507000006E03F00B607000000E03F9D
++:1016900000B707000040404000B807000007F0F71F
++:1016A0000CB9070000FF7FBF020108000004000022
++:1016B00000060800003F00000009080000040000C8
++:1016C000000B080000010000000D0800008168AC5C
++:1016D0000F19080000800000001F08000001000032
++:1016E00000210800000400000029080000140C0874
++:1016F000002A080000140C08002F08000001000058
++:1017000000330800000F00000039080000FF070048
++:101710000042080000040800004F080000882471FF
++:101720002A52080000040000005A080000040000CB
++:10173000005B080000200000005E080000020000BE
++:10174000005F08000000C0854062080000120C1015
++:101750000866080000000000046708000040000060
++:10176000006D080000DFBFE3046E08000000000009
++:10177000046F08000000010000720800000400006F
++:101780000075080000DFBFE30477080000000101D6
++:10179000007A080000040000007F080000000080BC
++:1017A000028A080000100000009D080000DFBFE36F
++:1017B00004A5080000DFBFE304B20800000408002D
++:1017C00000B608000004000000BA08000001000094
++:1017D00000C20800001A000000CA0800007F0000D4
++:1017E00000D608000004000000DA08000001000034
++:1017F00000DB0800001A000000E2080000140C08DA
++:1018000000F2080000120C1008FA080000040000A2
++:1018100000020900000400000005090000020000A9
++:101820000012090000100000001509000067FE1FEB
++:101830000032090000010000003A090000120C10FB
++:10184000084B0900000400000053090000040000D8
++:10185000005F0900000F00000063090000040000A1
++:101860000066090000010000006B0900000800008C
++:10187000006E0900000110000072090000FF07005F
++:1018800000750900001500000076090000FFFF0048
++:10189000007A090000140C08007B090000FF070013
++:1018A000007E090000FFFF00008509000001000024
++:1018B0000086090000FFFF00008D090000100000F5
++:1018C000008E090000FFFF0000A5090000010000D4
++:1018D00000BF09000004000000C7090000FFFF006E
++:1018E00000CD09000004000000CF090000FFFF0048
++:1018F00000D509000000040000D7090000FFFF0028
++:1019000000DD09000000030000DF090000FFFF0008
++:1019100000E30900000F000000E5090000011000CD
++:1019200000150A000002000000270A000001000064
++:1019300000470A0000010000005D0A0000010000ED
++:1019400000650A000010000000750A000001000098
++:1019500000770A0000010000008F0A00000100006B
++:1019600000960A00000000803F9E0A0000000080F0
++:101970003FA60A00000000803FAE0A000000008081
++:101980003FB60A00000000803FBE0A000000008051
++:101990003FC60A00000000803FCE0A000000008021
++:1019A0003FD60A00000000803FDE0A0000000080F1
++:1019B0003FE60A00000000803FE70A000001000047
++:1019C00000EE0A00000000803FEF0A000001000066
++:1019D00000F60A00000000803FF70A000002000045
++:1019E00000FE0A00000000803FFF0A000001000026
++:1019F00000060B00000000803F070B000001000004
++:101A0000000E0B00000000803F0F0B0000020000E2
++:101A100000150B000010000000160B000010000065
++:101A200000170B000001000000270B000011000050
++:101A3000002E0B000003000000420B00000100001C
++:101A4000005A0B000010000000670B00008168AC1A
++:101A50000F7F0B0000040000009D0B0000000080C1
++:101A60003F9E0B0000000000049F0B0000110000CF
++:101A700000A50B00000000803FA60B000000000046
++:101A800004A70B000001000000AD0B000000008067
++:101A90003FB50B00000000803FB60B000080000047
++:101AA00000B70B0000CF000000BD0B00000000805D
++:101AB0003FBF0B0000CF000000C50B0000000080FE
++:101AC0003FC70B0000CF000000CD0B0000000080DE
++:101AD0003FD50B00000000803FD60B0000800000C7
++:101AE00000DD0B00000000803FE50B0000000080DF
++:101AF0003FED0B00000000803FF50B000000008070
++:101B00003FFD0B00000000803F050C00000000803E
++:101B10003F0D0C00000000803F0E0C000001000093
++:101B200000150C00000000803F160C0000000100B2
++:101B3000001E0C0000000100001F0C00000100004E
++:101B400000260C000011000000270C00000100001E
++:101B5000002F0C000002000000360C0000080000FE
++:101B600000370C0000010000003F0C0000010000E5
++:101B700000470C0000020000004F0C0000010000B4
++:101B8000005F0C000001000000660C000001000076
++:101B900000670C0000010000006F0C000001000055
++:101BA00000760C000001000000770C00000100002E
++:101BB000007E0C0000010000007F0C00000100000E
++:101BC00000860C000001000000870C0000010000EE
++:101BD000008E0C0000CF0000008F0C000001000000
++:101BE00000960C000002000000970C0000010000AD
++:101BF000009F0C000011000000CE0C00000100004E
++:101C000000DE0C000001000000DF0C00008168AC69
++:101C10000FE50C000010000000E60C0000010000C1
++:101C200000E70C00000F000000EE0C0000010000B7
++:101C300000F50C00003F000000160D00000400003D
++:101C400000220D000088000000260D0000010000A9
++:101C500000270D000067FE1F002A0D00008800000D
++:101C6000002D0D0000010000002E0D0000150000E9
++:101C7000003D0D0000010000003F0D0000110000BC
++:101C800000420D000004000000470D0000010000AC
++:101C9000004D0D0000010000004E0D000080444486
++:101CA00004650D000001100000670D000004000035
++:101CB00000970D000001000000BF0D0000110000A2
++:101CC00000C50D000011000000FA0D000026000004
++:101CD00000FF0D00008168AC0F050E00000F000032
++:101CE00000120E00000000803F1F0E0000110000D7
++:101CF00000270E000001000000320E00001A000054
++:101D000000370E0000010000003A0E000010000035
++:101D100000450E000011000000470E000001000009
++:101D200000570E0000FF070000670E0000010000D2
++:101D300000770E0000010000007D0E000001000091
++:101D400000850E0000010000008D0E000001000063
++:101D500000950E0000020000009D0E000001000032
++:101D600000A50E000002000000AD0E000001000002
++:101D700000BD0E000067FE1F00CD0E00008168ACA4
++:101D80000F260F000001000000360F0000010000C8
++:101D900000560F00008824712A660F000000C085DD
++:101DA000406E0F000040000000760F0000000100B0
++:101DB000007E0F000000010100820F0000520000B1
++:101DC00000860F000000008002920F000026000035
++:101DD00000A20F000004000000AA0F000004000091
++:101DE00000AD0F000001000000B50F000002000070
++:101DF00000BA0F00001A000000BD0F000001000033
++:101E000000C50F000001000000CD0F00000200001F
++:101E100000CF0F000008000000D20F000000FFFFFD
++:101E200000D50F000001000000D70F0000080000DF
++:101E300000DD0F000001000000DF0F0000080000BF
++:101E400000E70F000008000000EF0F00000800008E
++:101E500000F70F000008000000FF0F00000800005E
++:101E6000000510000011000000071000000800002D
++:101E7000000D100000010000000F10000011000014
++:101E8000004F1000008168AC0F57100000000400E4
++:101E9000005F10000000040000661000000F00004A
++:101EA0000067100000000400006F10000000040034
++:101EB0000077100000000400007F10000000040004
++:101EC0000087100000000400008F100000000400D4
++:101ED0000097100000000300009F100000000300A6
++:101EE00000A710000000030000AF10000000030076
++:101EF00000B710000000030000BF10000000030046
++:101F000000C610000004000000C71000000003001D
++:101F100000CE100000FFFF0000CF10000000030003
++:101F200000D6100000FFFF0000D7100000010000E5
++:101F300000DE100000FFFF0000DF1000000F0000B7
++:101F400000E6100000FFFF00001F1100002000004D
++:101F50000027110000110000002E110000010000F8
++:101F6000002F110000000100003F110000010000DF
++:101F7000004E110000010000005711000040000059
++:101F8000005F110000000100006F1100000300005D
++:101F9000007E11000001000000921100000400000A
++:101FA0000096110000010000009711000067FE1F5D
++:101FB000009A11000004000000B7110000020000A8
++:101FC00000BF1100008168AC0FEE1100000100009D
++:101FD00000F611000001000000FE110000020000E8
++:101FE0000006120000010000000E120000010000B7
++:101FF000000F120000010000001612000002000095
++:10200000001E120000010000002E1200001100004E
++:102010000037120000040000004712000001000019
++:10202000004F1200000004000057120000000300DF
++:10203000005F120000011000006E1200008168AC09
++:102040000F7F120000110000008612000004000043
++:1020500000A612000011000000AE120000010000F6
++:1020600000BE120000CF000000BF1200008168AC6B
++:102070000FC6120000CF000000C71200000F0000C2
++:1020800000CE120000CF0000000B1300000F000074
++:10209000001B1300000100000026130000010000D7
++:1020A000002D130000020000002E130000010000AC
++:1020B0000036130000020000003D13000067FE1F01
++:1020C000003E130000010000004613000001000064
++:1020D000004E130000020000005613000001000033
++:1020E0000066130000010000006E130000010000F4
++:1020F0000076130000010000007E130000010000C4
++:10210000008613000001000000871300008168AC06
++:102110000F8E130000010000008F13000067FE1FE8
++:102120000096130000010000009D13000015000040
++:10213000009E13000001000000A613000011000023
++:1021400000AD13000001000000AF130000110000FB
++:1021500000B513000010000000C7130000040000C9
++:1021600000CD13000001000000D7130000010000A3
++:1021700000DF13000001000000E61300008168ACDE
++:102180000FEE1300000F000000F513000004000024
++:1021900000FD13000000040000FF13000001000018
++:1021A0000005140000000300000D140000011000E1
++:1021B00000271400008168AC0F2E14000067FE1F7A
++:1021C000003D140000020000004614000011000051
++:1021D000004E140000010000004F14000004000035
++:1021E000005F1400000100000067140000010000FF
++:1021F000006E140000040000007F140000010000C5
++:102200000085140000010000008D14000010000083
++:10221000009D140000010000009E14000001000059
++:1022200000C614000011000000D7140000010000D7
++:1022300000DF14000001000000E7140000010000AE
++:1022400000EF14000001000000F71400000100007E
++:1022500000FF14000001000000061500008168ACBA
++:102260000F07150000010000000F1500000100001D
++:1022700000171500001100000026150000110000D5
++:10228000002E150000010000003D150000100000A8
++:10229000003E150000010000004E15000001000086
++:1022A00000571500008168AC0F5E150000FF0700A5
++:1022B000005F1500000F0000006E15000001000017
++:1022C000007E150000010000009F150000110000B5
++:1022D00000A715000001000000C5150000000080E7
++:1022E0003FCD1500000000803FD5150000000080A4
++:1022F0003FDD1500000000803FE515000000008074
++:102300003FED1500000000803FF31500000F0000B6
++:1023100000F51500000000803FFD15000000008062
++:102320003F051600000000803F07160000DFBFE3F6
++:10233000040D1600000000803F0F160000DFBFE311
++:1023400004151600000000803F1716000001000071
++:10235000001D1600000000803F25160000000080D0
++:102360003F2716000000FFFF002D16000000008030
++:102370003F2F16000001000000351600000000800D
++:102380003F3D1600000000803F4716000000FFFFA1
++:10239000008F160000010000009F160000010000E1
++:1023A00000A716000000102030AF1600004050605B
++:1023B00070B71600008898A8B8BF160000C8D8E803
++:1023C000F8CF1600001A000000D616000008000022
++:1023D00000DE16000008000000DF16000004000008
++:1023E00000E616000008000000EE160000080000DD
++:1023F00000F616000008000000FE160000080000AD
++:102400000006170000080000000D17000010000073
++:10241000000E170000080000001617000011000051
++:10242000001D1700003F00000055170000010000CC
++:1024300000561700008168AC0F5E17000000040012
++:10244000006517000001000000661700000004008E
++:10245000006E170000000400007517000001000066
++:102460000076170000000400007E17000000040042
++:102470000086170000000400008717000004000019
++:10248000008D170000011000008E170000000400EE
++:10249000008F1700000400000096170000000400E1
++:1024A0000097170000000102039E170000000300C0
++:1024B00000A617000000030000AE17000000030094
++:1024C00000B617000000030000BE17000000030064
++:1024D00000BF17000004000000C617000000030042
++:1024E00000CE17000000030000D617000000030014
++:1024F00000D717000004000000DE170000010000F4
++:1025000000DF17000004000000E61700000F0000C5
++:1025100000E717000080000000ED17000011000028
++:1025200000EF170000040000002618000020000043
++:10253000002D1800000F0000002E180000110000F0
++:1025400000361800000001000046180000010000DD
++:10255000005E180000400000006618000000010046
++:10256000006D180000110000007618000003000044
++:10257000009E18000067FE1F00A518000001000063
++:1025800000AD18000001000000B5180000010000B7
++:1025900000BD18000002000000BE1800000200008C
++:1025A00000C518000001000000C61800008168ACDA
++:1025B0000FCD18000002000000D518000001000037
++:1025C00000E518000067FE1F00F51800008168ACE8
++:1025D0000F16190000010000001719000004000088
++:1025E000001F1900008000000027190000040000EF
++:1025F000002F19000000010203371900000300003A
++:10260000003E190000040000003F19000004000013
++:10261000004E1900000100000056190000000400DF
++:10262000005E1900000003000066190000011000A0
++:102630000067190000040000006F1900000300008B
++:102640000086190000110000008F1900000400002E
++:1026500000C61900008168AC0FCE1900000F000001
++:1026600000D519000001000000DD19000002000083
++:1026700000E519000001000000ED19000001000054
++:1026800000F519000002000000FD19000001000023
++:1026900000051A0000010000002D1A0000110000C2
++:1026A00000351A0000010000008E1A00008168AC9D
++:1026B0000F961A000067FE1F00B61A0000110000F6
++:1026C00000CE1A000004000000DE1A000001000025
++:1026D00000E61A000001000000061B0000010000D7
++:1026E000002E1B00008168AC0F561B000004000088
++:1026F00000661B0000010000006E1B0000010000CE
++:1027000000861B000001000000DE1B00000100002D
++:1027100000E61B000001000000EE1B0000010000AD
++:1027200000F61B000001000000FE1B00000100007D
++:1027300000061C0000010000000E1C00000100004B
++:1027400000161C0000010000001E1C00001100000B
++:10275000005E1C00008168AC0F661C00000F0000CA
++:1027600000A61C000011000000AE1C0000010000CB
++:10277000000E1D0000DFBFE304161D0000DFBFE3F5
++:10278000041E1D0000010000002E1D000000FFFFC0
++:1027900000361D0000010000004E1D000000FFFF7C
++:1027A00000551D000002000000651D000067FE1FAF
++:1027B00000961D000001000000A61D0000010000A1
++:1027C00000AE1D000000102030B61D00004050601B
++:1027D00070BE1D00008898A8B8C51D000015000037
++:1027E00000C61D0000C8D8E8F8D51D000001000093
++:1027F00000D61D00001A000000DD1D0000100000C2
++:1028000000E61D000004000000F51D0000010000AE
++:10281000001D1E000004000000251E000000040032
++:10282000002D1E000000030000351E0000011000F6
++:1028300000651E0000020000008E1E000004000063
++:1028400000961E0000040000009E1E000000010211
++:1028500003AD1E000001000000B51E0000100000C6
++:1028600000C51E000001000000C61E00000400009C
++:1028700000DE1E000004000000E61E000004000050
++:1028800000EE1E000080000000F61E0000040000A4
++:1028900000651F000010000000ED1F000000008018
++:1028A0003FF51F00000000803FFD1F00000000807A
++:1028B0003F052000000000803F0D20000000008048
++:1028C0003F152000000000803F1D20000000008018
++:1028D0003F1E2000000400000025200000000080B2
++:1028E0003F26200000800000002D20000000008016
++:1028F0003F2E200000040000003520000000008072
++:102900003F36200000000102033D2000000000804F
++:102910003F3E200000030000004520000000008032
++:102920003F46200000040000004D20000000008011
++:102930003F552000000000803F5D20000000008027
++:102940003F652000000000803F6E20000004000072
++:102950000076200000030000009620000004000024
++:10296000003521000010000000452100003F00005C
++:10297000007D210000010000008D21000001000009
++:10298000009D21000001000000B5210000011000A1
++:10299000001522000011000000552200000F000069
++:1029A000009522000011000000CD2200000100006F
++:1029B00000D522000001000000DD2200000100001F
++:1029C00000E522000002000000ED220000010000EE
++:1029D00000F522000002000000FD220000010000BE
++:1029E000000D23000067FE1F001D2300008168AC5E
++:1029F0000FFD23000001000000052400000200007C
++:102A0000000D24000001000000152400000100005A
++:102A1000001D240000020000002524000001000029
++:102A2000002D2400000100000055240000110000CA
++:102A3000005D240000010000006824000021000067
++:102A40000070240000010000007824000002000053
++:102A50000080240000000100008824000000010024
++:102A6000009024000001000000A8240000010000E4
++:102A700000B024000002000000B8240000000100A3
++:102A800000C024000000010000C824000001000074
++:102A9000007D270000020000008D27000067FE1F58
++:102AA00000ED27000015000000FD270000010000D8
++:102AB0000005280000100000001D28000001000093
++:102AC0000045280000040000004D2800000004001C
++:102AD0000055280000000300005D280000011000E0
++:102AE000008D28000002000000D528000001000031
++:102AF00000DD28000010000000ED280000010000AB
++:102B0000008D29000010000000152A000000008040
++:102B10003F1D2A00000000803F252A0000000080A1
++:102B20003F2D2A00000000803F352A000000008071
++:102B30003F372A0000040000003D2A00000000800A
++:102B40003F3F2A000003000000452A0000000080EB
++:102B50003F4D2A00000000803F552A000000008001
++:102B60003F5D2A00000000803F652A0000000080D1
++:102B70003F6D2A00000000803F6F2A000001000026
++:102B800000752A00000000803F7D2A0000000080C0
++:102B90003F7F2A000001000000852A00000000801D
++:102BA0003F8D2A00000000803F9F2A00008824718A
++:102BB0002AAF2A000000C08540B72A00004000006C
++:102BC00000BF2A000000010000C72A000000010128
++:102BD00000CF2A0000000080025D2B0000100000E2
++:102BE000006D2B00003F000000A52B00000100003D
++:102BF00000AF2B00000F000000B52B00000100000B
++:102C000000C52B000001000000DD2B0000011000BA
++:102C1000000F2C000004000000172C0000FFFF0034
++:102C2000001F2C0000FFFF0000272C0000FFFF000A
++:102C3000002F2C0000FFFF00003D2C0000110000C1
++:102C400000772C0000010000007D2C00000F000028
++:102C500000972C000001000000BD2C0000110000B6
++:102C600000C72C000001000000DF2C000001000064
++:102C700000F52C000001000000FD2C000001000008
++:102C800000052D0000010000000D2D0000020000D5
++:102C900000152D0000010000001D2D0000020000A5
++:102CA00000252D000001000000352D000067FE1FEB
++:102CB00000372D0000010000003F2D000001000042
++:102CC00000452D00008168AC0F472D000002000078
++:102CD000004F2D000001000000572D0000010000F2
++:102CE000005F2D000002000000672D0000010000C1
++:102CF00000772D000011000000B72D00008168ACA6
++:102D00000FCF2D000004000000EF2D000011000087
++:102D100000F72D000001000000072E0000CF00008A
++:102D2000000F2E0000CF000000172E0000CF000083
++:102D300000252E0000010000002D2E0000020000E2
++:102D400000352E0000010000003D2E0000010000B3
++:102D500000452E0000020000004D2E000001000082
++:102D600000552E0000010000006F2E000001000041
++:102D700000772E0000010000007D2E0000110000F1
++:102D8000007F2E000002000000852E0000010000E0
++:102D900000872E0000010000008F2E0000010000BF
++:102DA00000972E0000020000009F2E00000100008E
++:102DB00000AF2E000001000000B72E00000100004F
++:102DC00000BF2E000001000000C72E00000100001F
++:102DD00000CF2E000001000000D72E0000010000EF
++:102DE00000DF2E000001000000E72E0000010000BF
++:102DF00000EF2E0000110000002F2F00008168ACB2
++:102E00000F372F00000F000000772F000067FE1F14
++:102E1000008F2F000011000000972F00000100001C
++:102E200000B72F000004000000E72F0000010000A1
++:102E3000000F300000110000004F3000008168AC2E
++:102E40000F6F30000011000000773000000100001B
++:102E500000873000000100000097300000010000F2
++:102E600000A7300000FF070000B73000000100009D
++:102E700000C7300000010000003E310000040000E7
++:102E80000046310000030000007631000001000020
++:102E9000008631000001000000A5310000020000A2
++:102EA00000A63100008824712AB531000067FE1F9A
++:102EB00000B631000000C08540BE31000040000077
++:102EC00000C631000000010000CE31000000010109
++:102ED00000D631000000008002153200001500000D
++:102EE000001F320000080000002532000001000031
++:102EF0000027320000080000002D32000010000002
++:102F0000002F3200000800000037320000080000E7
++:102F1000003F3200000800000045320000010000C0
++:102F20000047320000080000004F32000008000097
++:102F30000057320000080000005F3200001100005E
++:102F4000006D320000040000007532000000040033
++:102F5000007D3200000003000085320000011000F7
++:102F6000009F3200008168AC0FA73200000004000F
++:102F700000AF32000000040000B532000002000083
++:102F800000B63200000F000000B73200000004005D
++:102F900000BF32000000040000C73200000004003F
++:102FA00000CF32000000040000D73200000004000F
++:102FB00000DF32000000040000E7320000000300E0
++:102FC00000EF32000000030000F7320000000300B1
++:102FD00000FD32000001000000FF3200000003008D
++:102FE000000533000010000000073300000003005C
++:102FF000000F330000000300001533000001000043
++:103000000016330000040000001733000000030026
++:10301000001E330000FFFF00001F3300000003000C
++:103020000026330000FFFF000027330000010000EE
++:10303000002E330000FFFF00002F3300000F0000C0
++:103040000036330000FFFF00006F33000020000057
++:103050000077330000110000007E33000001000003
++:10306000007F330000000100008F330000010000EA
++:10307000009E33000001000000A733000040000064
++:1030800000AF33000000010000B533000010000065
++:1030900000BF33000003000000CE33000001000039
++:1030A00000E633000001000000E733000067FE1F68
++:1030B0000007340000020000000F3400008168ACFB
++:1030C0000F3D3400000000803F3E3400000100004E
++:1030D00000453400000000803F463400000100003D
++:1030E000004D3400000000803F4E3400000200001C
++:1030F00000553400000000803F56340000010000FD
++:10310000005D3400000000803F5E340000010000DC
++:10311000005F340000010000006534000000008002
++:103120003F66340000020000006D340000000080A3
++:103130003F6E340000010000007534000000008084
++:103140003F7D3400000000803F7E3400001100000D
++:1031500000853400000000803F8734000004000038
++:10316000008D3400000000803F9534000000008096
++:103170003F97340000010000009D340000000080F3
++:103180003F9F34000000040000A5340000000080D0
++:103190003FA734000000030000AD340000000080B1
++:1031A0003FAF34000001100000B534000000008083
++:1031B0003FBE3400008168AC0FCF34000011000026
++:1031C00000D634000004000000F6340000110000B6
++:1031D00000FE340000010000000E350000CF0000AA
++:1031E000000F3500008168AC0F16350000CF0000DD
++:1031F00000173500000F0000001E350000CF000052
++:103200000076350000010000007E3500000100005E
++:103210000085350000100000008635000002000027
++:10322000008E35000001000000953500003F0000D1
++:103230000096350000010000009E350000020000ED
++:1032400000A635000001000000B6350000010000B6
++:1032500000BE35000001000000C63500000100007E
++:1032600000CD35000001000000CE35000001000057
++:1032700000D635000001000000D73500008168ACA1
++:103280000FDD35000001000000DE35000001000008
++:1032900000DF35000067FE1F00E63500000100007A
++:1032A00000ED35000001000000EE350000010000D7
++:1032B00000F635000011000000FF3500001100008D
++:1032C0000005360000011000001736000004000061
++:1032D0000027360000010000002F3600000100002A
++:1032E00000363600008168AC0F3E3600000F00004B
++:1032F000004F36000001000000653600001100009C
++:1033000000773600008168AC0F7E36000067FE1F34
++:103310000096360000110000009E360000010000FB
++:10332000009F36000004000000A53600000F0000DA
++:1033300000AF36000001000000B7360000010000B9
++:1033400000BE36000004000000CF3600000100007F
++:1033500000E536000011000000EE3600000100001C
++:103360000016370000110000001D370000010000AA
++:103370000025370000010000002737000001000091
++:10338000002D370000010000002F37000001000071
++:103390000035370000020000003737000001000050
++:1033A000003D370000010000003F37000001000031
++:1033B0000045370000020000004737000001000010
++:1033C000004D370000010000004F370000010000F1
++:1033D00000563700008168AC0F573700000100002D
++:1033E000005D37000067FE1F005F3700000100002E
++:1033F0000067370000110000006D3700008168ACE5
++:103400000F76370000110000007E37000001000039
++:10341000008E370000010000009E37000001000010
++:1034200000A73700008168AC0FAE370000FF07002F
++:1034300000AF3700000F000000BE370000010000A1
++:1034400000CE37000001000000EF3700001100003F
++:1034500000F7370000010000004D380000010000B7
++:1034600000553800000200000057380000DFBFE3BD
++:10347000045D380000010000005F380000DFBFE39A
++:1034800004653800000100000067380000010000FA
++:10349000006D3800000200000075380000010000D7
++:1034A000007738000000FFFF007D380000010000B9
++:1034B000007F380000010000009738000000FFFF87
++:1034C00000A538000011000000AD38000001000028
++:1034D00000DF38000001000000EF380000010000AC
++:1034E00000F738000000102030FF38000040506026
++:1034F00070073900008898A8B80F390000C8D8E8CC
++:10350000F81F3900001A00000026390000080000EA
++:10351000002E390000080000002F390000040000D0
++:103520000036390000080000003E390000080000A5
++:103530000046390000080000004E39000008000075
++:103540000056390000080000005E39000008000045
++:10355000006639000011000000A63900008168AC47
++:103560000FAE39000000040000B63900000004006E
++:1035700000BE39000000040000C63900000004004D
++:1035800000CE39000000040000D63900000004001D
++:1035900000D739000004000000DE390000000400FC
++:1035A00000DF39000004000000E6390000000400DC
++:1035B00000E739000000010203EE390000000300BB
++:1035C00000F639000000030000FE3900000003008F
++:1035D00000063A0000000300000E3A00000003005D
++:1035E000000F3A000004000000163A00000003003B
++:1035F000001E3A000000030000263A00000003000D
++:1036000000273A0000040000002E3A0000010000EC
++:10361000002F3A000004000000363A00000F0000BE
++:1036200000373A0000800000003F3A00000400002C
++:1036300000763A0000200000007E3A0000110000F1
++:1036400000863A000000010000963A0000010000E8
++:1036500000AE3A000040000000B63A000000010051
++:1036600000C63A000003000000EE3A000067FE1FAB
++:10367000000E3B000002000000163B00008168AC19
++:103680000F663B000001000000673B0000040000E3
++:10369000006F3B000080000000773B00000400004A
++:1036A000007F3B000000010203873B000003000095
++:1036B000008E3B0000040000008F3B00000400006F
++:1036C000009E3B000001000000A63B00000004003B
++:1036D00000AE3B000000030000B63B0000011000FC
++:1036E00000B73B000004000000BF3B0000030000E7
++:1036F00000CD3B000002000000D63B00001100009E
++:1037000000DD3B000067FE1F00DF3B0000040000FF
++:1037100000163C00008168AC0F1E3C00000F00004A
++:10372000003D3C0000150000004D3C000001000081
++:1037300000553C0000100000006D3C00000100003E
++:1037400000953C0000040000009D3C0000000400C7
++:1037500000A53C000000030000AD3C00000110008B
++:1037600000DD3C000002000000DE3C00008168AC8F
++:103770000FE63C000067FE1F00063D000011000040
++:10378000001E3D000004000000253D000001000077
++:10379000002D3D0000100000002E3D000001000043
++:1037A00000363D0000010000003D3D00000100002A
++:1037B00000563D0000010000007E3D00008168AC25
++:1037C0000FA63D000004000000B63D00000100000F
++:1037D00000BE3D000001000000D63D0000010000D9
++:1037E00000DD3D0000100000002E3E000001000042
++:1037F00000363E0000010000003E3E0000010000D7
++:1038000000463E0000010000004E3E0000010000A6
++:1038100000563E0000010000005E3E000001000076
++:1038200000653E00000000803F663E000001000091
++:10383000006D3E00000000803F6E3E000011000061
++:1038400000753E00000000803F7D3E0000000080CB
++:103850003F853E00000000803F8D3E00000000805C
++:103860003F953E00000000803F9D3E00000000802C
++:103870003FA53E00000000803FAD3E0000000080FC
++:103880003FAE3E00008168AC0FB53E0000000080F6
++:103890003FB63E00000F000000BD3E00000000806B
++:1038A0003FC53E00000000803FCD3E00000000808C
++:1038B0003FD53E00000000803FDD3E00000000805C
++:1038C0003FF63E000011000000FE3E000001000037
++:1038D000005E3F0000DFBFE304663F0000DFBFE3A0
++:1038E000046E3F0000010000007E3F000000FFFF6B
++:1038F00000863F0000010000009E3F000000FFFF27
++:1039000000AD3F000010000000BD3F00003F000080
++:1039100000E63F000001000000F53F00000100004C
++:1039200000F63F000001000000FE3F0000001020F4
++:1039300030054000000100000006400000405060DB
++:10394000700E4000008898A8B815400000010000E3
++:103950000016400000C8D8E8F8264000001A000011
++:10396000002D40000001100000364000000400005F
++:10397000008D40000011000000CD4000000F00004D
++:1039800000DE40000004000000E6400000040000EB
++:1039900000EE400000000102030D41000011000094
++:1039A0000016410000040000002E41000004000049
++:1039B0000036410000040000003E4100008000008D
++:1039C00000454100000100000046410000040000E5
++:1039D000004D4100000100000055410000010000C1
++:1039E000005D410000020000006541000001000090
++:1039F000006D410000020000007541000001000060
++:103A0000008541000067FE1F00954100008168AC01
++:103A10000F6E42000004000000754200000100002B
++:103A20000076420000800000007D4200000200009D
++:103A3000007E4200000400000085420000010000FA
++:103A40000086420000000102038D420000010000D8
++:103A5000008E4200000300000095420000020000BA
++:103A60000096420000040000009D4200000100009A
++:103A700000A542000001000000BE4200000400005A
++:103A800000C642000003000000CD4200001100000B
++:103A900000D542000001000000E6420000040000E2
++:103AA00000F5450000020000000546000067FE1F0B
++:103AB000006546000015000000754600000100008A
++:103AC000007D460000100000009546000001000047
++:103AD00000BD46000004000000C5460000000400D0
++:103AE00000CD46000000030000D546000001100094
++:103AF0000005470000020000004D470000010000E3
++:103B0000005547000010000000654700000100005C
++:103B10000005480000100000008D480000000080F3
++:103B20003F954800000000803F9D48000000008055
++:103B30003FA54800000000803FAD48000000008025
++:103B40003FB54800000000803FBD480000000080F5
++:103B50003FC54800000000803FCD480000000080C5
++:103B60003FD54800000000803FDD48000000008095
++:103B70003FE54800000000803FED48000000008065
++:103B80003FF54800000000803FFD48000000008035
++:103B90003F054900000000803FD5490000100000AB
++:103BA00000E54900003F0000001D4A000001000040
++:103BB000002D4A0000010000003D4A000001000005
++:103BC00000554A000001100000B54A000011000035
++:103BD00000F54A00000F000000354B000011000006
++:103BE000006D4B000001000000754B00000100005B
++:103BF000007D4B000001000000854B00000200002A
++:103C0000008D4B000001000000954B0000020000F9
++:103C1000009D4B000001000000AD4B000067FE1F3F
++:103C200000BD4B00008168AC0F874C000004000011
++:103C3000008F4C0000030000009D4C0000010000BC
++:103C400000A54C000002000000AD4C000001000087
++:103C500000B54C000001000000BD4C000002000057
++:103C600000BF4C000001000000C54C000001000036
++:103C700000CD4C000001000000CF4C00000100000E
++:103C800000EF4C00008824712AF54C000011000060
++:103C900000FD4C000001000000FF4C000000C0854A
++:103CA00040074D0000400000000F4D0000000100E3
++:103CB00000174D0000000101001F4D0000000080B2
++:103CC00002FF4D00000F0000005F4E0000040000E6
++:103CD00000674E0000FFFF00006F4E0000FFFF0076
++:103CE00000774E0000FFFF00007F4E0000FFFF0046
++:103CF00000C74E000001000000E74E000001000078
++:103D000000174F0000010000002F4F0000010000CD
++:103D100000874F0000010000008F4F0000010000ED
++:103D200000974F0000020000009F4F0000010000BC
++:103D300000A74F000001000000AF4F00000200008C
++:103D400000B74F000001000000C74F000011000045
++:103D500000075000008168AC0F1D500000020000F9
++:103D6000001F500000040000002D50000067FE1FDF
++:103D7000003F50000011000000475000000100000B
++:103D80000057500000CF0000005F500000CF00003F
++:103D90000067500000CF0000008D500000150000AB
++:103DA000009D50000001000000A550000010000020
++:103DB00000BD50000001000000BF500000010000E5
++:103DC00000C750000001000000CF500000020000BA
++:103DD00000D750000001000000DF5000000100008B
++:103DE00000E550000004000000E750000002000061
++:103DF00000ED50000000040000EF50000001000042
++:103E000000F550000000030000FD5000000110000C
++:103E100000FF5000000100000007510000010000F9
++:103E2000000F5100000100000017510000010000C8
++:103E3000001F510000010000002751000001000098
++:103E4000002D510000020000002F51000001000071
++:103E50000037510000010000003F51000011000038
++:103E60000075510000010000007D510000100000AD
++:103E7000007F5100008168AC0F875100000F0000E7
++:103E8000008D51000001000000C751000067FE1FB7
++:103E900000DF51000011000000E7510000010000A8
++:103EA0000007520000040000002D52000010000026
++:103EB0000037520000010000005F520000110000B6
++:103EC000009F5200008168AC0FB5520000000080D6
++:103ED0003FBD5200000000803FBF520000110000B3
++:103EE00000C55200000000803FC7520000010000E2
++:103EF00000CD5200000000803FD55200000000803D
++:103F00003FD752000001000000DD52000000008099
++:103F10003FE55200000000803FE752000001000032
++:103F200000ED5200000000803FF5520000000080CC
++:103F30003FF7520000FF070000FD52000000008024
++:103F40003F055300000000803F07530000010000C0
++:103F5000000D5300000000803F155300000000805A
++:103F60003F17530000010000001D530000000080B7
++:103F70003F255300000000803F2D530000000080CB
++:103F80003F8E530000040000009653000003000021
++:103F900000C653000001000000D6530000010000DD
++:103FA00000F65300008824712AFD53000010000021
++:103FB000000654000000C085400D5400003F000082
++:103FC000000E5400004000000016540000000100E4
++:103FD000001E540000000101002654000000008073
++:103FE000024554000001000000555400000100008B
++:103FF0000065540000010000006F5400000800003C
++:104000000077540000080000007D540000011000FB
++:10401000007F5400000800000087540000080000E2
++:10402000008F5400000800000097540000080000B2
++:10403000009F54000008000000A754000008000082
++:1040400000AF54000011000000DD5400001100001A
++:1040500000EF5400008168AC0FF75400000004002A
++:1040600000FF54000000040000065500000F00008F
++:104070000007550000000400000F55000000040078
++:104080000017550000000400001D5500000F00003F
++:10409000001F550000000400002755000000040028
++:1040A000002F5500000004000037550000000300F9
++:1040B000003F5500000003000047550000000300CA
++:1040C000004F55000000030000575500000003009A
++:1040D000005D550000110000005F55000000030066
++:1040E0000066550000040000006755000000030052
++:1040F000006E550000FFFF00006F55000000030038
++:104100000076550000FFFF00007755000001000019
++:10411000007E550000FFFF00007F5500000F0000EB
++:104120000086550000FFFF000095550000010000CB
++:10413000009D55000001000000A555000001000091
++:1041400000AD55000002000000B555000001000060
++:1041500000BD55000002000000BF55000020000017
++:1041600000C555000001000000C755000011000007
++:1041700000CE55000001000000CF550000000100F6
++:1041800000D555000067FE1F00DF5500000100004C
++:1041900000E55500008168AC0FEE550000010000FD
++:1041A00000F755000040000000FF5500000001002E
++:1041B000000F560000030000001E56000001000022
++:1041C0000036560000010000003756000067FE1F51
++:1041D0000057560000020000005F5600008168ACE6
++:1041E0000F8E5600000100000096560000010000EE
++:1041F000009E56000002000000A6560000010000CC
++:1042000000AE56000001000000AF560000010000A3
++:1042100000B656000002000000BE5600000100007B
++:1042200000C556000001000000CD5600000200004D
++:1042300000CE56000011000000D55600000100001D
++:1042400000D756000004000000DD56000001000009
++:1042500000E556000002000000E7560000010000E3
++:1042600000ED56000001000000EF560000000400C1
++:1042700000F556000001000000F7560000000300A2
++:1042800000FF560000011000000E5700008168ACCE
++:104290000F1D570000110000001F57000011000003
++:1042A0000025570000010000002657000004000010
++:1042B0000046570000110000004E570000010000AA
++:1042C000005E570000CF0000005F5700008168AC1F
++:1042D0000F66570000CF000000675700000F000076
++:1042E000006E570000CF000000C65700000100001C
++:1042F00000CE57000001000000D657000002000069
++:1043000000DE57000001000000E657000001000039
++:1043100000EE57000002000000F657000001000008
++:104320000006580000010000000E580000010000C7
++:104330000016580000010000001E58000001000097
++:10434000002658000001000000275800008168ACDA
++:104350000F2E580000010000002F58000067FE1FBC
++:104360000036580000010000003E58000001000027
++:104370000046580000110000004F580000110000D6
++:10438000006758000004000000775800000100009A
++:10439000007F58000001000000865800008168ACD2
++:1043A0000F8E5800000F0000009F58000001000011
++:1043B00000C75800008168AC0FCE58000067FE1F90
++:1043C00000E658000011000000EE58000001000057
++:1043D00000EF58000004000000FF5800000100003A
++:1043E0000007590000010000000E59000004000001
++:1043F000001F590000010000003E590000010000AC
++:10440000006659000011000000775900000100000B
++:10441000007F5900000100000087590000010000E2
++:10442000008F5900000100000097590000010000B2
++:10443000009F59000001000000A65900008168ACEF
++:104440000FA759000001000000AF59000001000053
++:1044500000B759000011000000C65900001100000B
++:1044600000CE59000001000000DE590000010000EC
++:1044700000EE59000001000000F75900008168AC0F
++:104480000FFE590000FF070000FF5900000F000059
++:10449000000E5A0000010000001E5A00000100003A
++:1044A000003F5A000011000000475A0000010000C0
++:1044B00000A75A0000DFBFE304AF5A0000DFBFE3EC
++:1044C00004B75A000001000000C75A000000FFFFB7
++:1044D00000CF5A000001000000E75A000000FFFF73
++:1044E000002F5B0000010000003F5B0000010000A6
++:1044F00000475B0000001020304F5B000040506020
++:1045000070575B00008898A8B85F5B0000C8D8E8C7
++:10451000F86F5B00001A000000765B0000080000E6
++:10452000007E5B0000080000007F5B0000040000CC
++:1045300000865B0000080000008E5B0000080000A1
++:1045400000965B0000080000009E5B000008000071
++:1045500000A65B000008000000AE5B000008000041
++:1045600000B65B000011000000F65B00008168AC43
++:104570000FFE5B000000040000045C0000FFFF3F32
++:1045800000065C0000000400000E5C000000040057
++:1045900000165C0000000400001C5C0000FF1F000F
++:1045A000001E5C000000040000265C000000040007
++:1045B00000275C0000040000002E5C0000000400E6
++:1045C000002F5C000004000000365C0000000400C6
++:1045D00000375C0000000102033E5C0000000300A5
++:1045E00000465C0000000300004E5C000000030079
++:1045F00000565C0000000300005E5C000000030049
++:10460000005F5C000004000000665C000000030026
++:10461000006E5C000000030000765C0000000300F8
++:1046200000775C0000040000007E5C0000010000D8
++:10463000007F5C000004000000865C00000F0000AA
++:1046400000875C0000800000008F5C000004000018
++:1046500000C65C000020000000CE5C0000110000DD
++:1046600000D65C000000010000E65C0000010000D4
++:1046700000FE5C000040000000065D00000001003C
++:1046800000165D0000030000003E5D000067FE1F95
++:10469000005E5D000002000000665D00008168AC05
++:1046A0000FB65D000001000000B75D0000040000CF
++:1046B00000BF5D000080000000C75D000004000036
++:1046C00000CF5D000000010203D75D000003000081
++:1046D00000DE5D000004000000DF5D00000400005B
++:1046E00000EE5D000001000000F65D000000040027
++:1046F00000FE5D000000030000065E0000011000E7
++:1047000000075E0000040000000F5E0000030000D0
++:1047100000265E0000110000002F5E000004000073
++:1047200000665E00008168AC0F6E5E00000F000046
++:10473000002E5F00008168AC0F365F000067FE1F2F
++:1047400000565F0000110000006E5F0000040000D2
++:10475000007E5F000001000000865F000001000095
++:1047600000A65F000001000000CE5F00008168AC81
++:104770000FF65F000004000000066000000100006A
++:10478000000E600000010000002660000001000033
++:10479000007E600000010000008660000001000053
++:1047A000008E600000010000009660000001000023
++:1047B000009E60000001000000A6600000010000F3
++:1047C00000AE60000001000000B6600000010000C3
++:1047D00000BE60000011000000FE6000008168ACB7
++:1047E0000F066100000F000000466100001100008C
++:1047F000004E61000001000000AE610000DFBFE379
++:1048000004B6610000DFBFE304BE610000010000E8
++:1048100000CE61000000FFFF00D661000001000033
++:1048200000EE61000000FFFF0036620000010000A2
++:104830000046620000010000004E620000001020EF
++:104840003056620000405060705E6200008898A898
++:10485000B866620000C8D8E8F8766200001A000066
++:104860000086620000040000002E630000040000C7
++:104870000036630000040000003E630000000102F7
++:104880000366630000040000007E63000004000073
++:104890000086630000040000008E630000800000BA
++:1048A000009663000004000000BE640000040000E5
++:1048B00000C664000080000000CE64000004000018
++:1048C00000D664000000010203DE64000003000063
++:1048D00000E6640000040000000E65000004000013
++:1048E00000166500000300000036650000040000AB
++:1048F00000D76E000004000000DF6E00000300001F
++:10490000000F6F0000010000001F6F000001000099
++:10491000003F6F00008824712A4F6F000000C0859F
++:1049200040576F0000400000005F6F000000010072
++:1049300000676F0000000101006F6F000000008041
++:1049400002706F000000000004786F00000000009B
++:1049500004806F000000000004886F000000000069
++:1049600004906F000000000004986F000000000039
++:1049700004A06F000000000004A86F000000000009
++:1049800004B06F000000000004B86F0000000000D9
++:1049900004C06F000000000004C86F0000000000A9
++:1049A00004D06F000000000004D86F000000000079
++:1049B00004E06F000000000004E86F000000000049
++:1049C000044F7000000F000000AF700000040000F2
++:1049D00000B7700000FFFF0000BF700000FFFF0085
++:1049E00000C7700000FFFF0000CF700000FFFF0055
++:1049F0000010710000DFBFE3041771000001000028
++:104A00000018710000DFBFE3043071000021FE01D7
++:104A1000003771000001000000387100008168ACAF
++:104A20000F60710000120C10086771000001000097
++:104A3000007F71000001000000887100000001008B
++:104A400000A071000001000100B071000001000130
++:104A500000B871000001000000C0710000010001F9
++:104A600000C871000001000000D0710000040000C7
++:104A700000D771000001000000D8710000020000A2
++:104A800000DF71000001000000E77100000200007B
++:104A900000EF71000001000000F77100000100004C
++:104AA00000FF71000002000000077200000100001A
++:104AB000001772000011000000577200008168ACFE
++:104AC0000F6F720000040000008F720000110000E0
++:104AD000009772000001000000A7720000CF0000E4
++:104AE00000AF720000CF000000B7720000CF0000DE
++:104AF000000F7300000100000017730000010000A8
++:104B0000001F730000020000002773000001000076
++:104B1000002F730000010000003773000002000046
++:104B2000003F730000010000004F7300000100000F
++:104B30000057730000010000005F730000010000D7
++:104B40000067730000010000006F730000010000A7
++:104B50000077730000010000007F73000001000077
++:104B60000087730000010000008F73000011000037
++:104B700000CF7300008168AC0FD77300000F0000F6
++:104B8000001774000067FE1F002F74000011000062
++:104B9000003774000001000000577400000400009A
++:104BA000008774000001000000AF740000110000D5
++:104BB00000EF7400008168AC0F0F75000011000059
++:104BC00000177500000100000027750000010000BB
++:104BD00000377500000100000047750000FF070066
++:104BE000005775000001000000677500000100001B
++:104BF00000DE75000004000000E675000003000000
++:104C0000001676000001000000267600000100007A
++:104C100000467600008824712A5676000000C08580
++:104C2000405E760000400000006676000000010053
++:104C3000006E760000000101007676000000008022
++:104C400002BF76000008000000C7760000080000E0
++:104C500000CF76000008000000D7760000080000B2
++:104C600000DF76000008000000E776000008000082
++:104C700000EF76000008000000F776000008000052
++:104C800000FF760000110000003F7700008168AC53
++:104C90000F47770000000400004F77000000040079
++:104CA00000567700000F0000005777000000040056
++:104CB000005F770000000400006777000000040038
++:104CC000006F770000000400007777000000040008
++:104CD000007F7700000004000087770000000300D9
++:104CE000008F7700000003000097770000000300AA
++:104CF000009F77000000030000A77700000003007A
++:104D000000AF77000000030000B677000004000049
++:104D100000B777000000030000BE770000FFFF002F
++:104D200000BF77000000030000C6770000FFFF000F
++:104D300000C777000001000000CE770000FFFF00F1
++:104D400000CF7700000F000000D6770000FFFF00C3
++:104D5000000F78000020000000177800001100000C
++:104D6000001E780000010000001F78000000010014
++:104D7000002F780000010000003E780000010000D4
++:104D80000047780000400000004F7800000001005C
++:104D9000005F780000030000006E78000001000052
++:104DA0000086780000010000008778000067FE1F81
++:104DB00000A778000002000000AF7800008168AC16
++:104DC0000FDE78000001000000E67800000100001E
++:104DD00000EE78000002000000F6780000010000FC
++:104DE00000FE78000001000000FF780000010000D4
++:104DF0000006790000020000000E790000010000AA
++:104E0000001E790000110000002779000004000056
++:104E10000037790000010000003F79000000040025
++:104E20000047790000000300004F790000011000E6
++:104E3000005E7900008168AC0F6F790000110000FE
++:104E4000007679000004000000967900001100004F
++:104E5000009E79000001000000AE790000CF000044
++:104E600000AF7900008168AC0FB6790000CF000078
++:104E700000B77900000F000000BE790000CF0000ED
++:104E800000167A0000010000001E7A0000010000F8
++:104E900000267A0000020000002E7A0000010000C7
++:104EA00000367A0000010000003E7A000002000097
++:104EB00000467A000001000000567A000001000060
++:104EC000005E7A000001000000667A000001000028
++:104ED000006E7A000001000000767A0000010000F8
++:104EE00000777A00008168AC0F7E7A000001000034
++:104EF000007F7A000067FE1F00867A000001000034
++:104F0000008E7A000001000000967A000011000077
++:104F1000009F7A000011000000B77A000004000032
++:104F200000C77A000001000000CF7A0000010000F5
++:104F300000D67A00008168AC0FDE7A00000F000016
++:104F400000EF7A000001000000177B00008168ACD0
++:104F50000F1E7B000067FE1F00367B000011000063
++:104F6000003E7B0000010000003F7B0000040000C9
++:104F7000004F7B000001000000577B000001000093
++:104F8000005E7B0000040000006F7B000001000059
++:104F9000008E7B000001000000B67B0000110000C5
++:104FA00000C77B000001000000CF7B000001000073
++:104FB00000D77B000001000000DF7B000001000043
++:104FC00000E77B000001000000EF7B000001000013
++:104FD00000F67B00008168AC0FF77B000001000049
++:104FE00000FF7B000001000000077C0000110000B2
++:104FF00000167C0000110000001E7C000001000073
++:10500000002E7C0000010000003E7C00000100003A
++:1050100000477C00008168AC0F4E7C0000FF070059
++:10502000004F7C00000F0000005E7C0000010000CB
++:10503000006E7C0000010000008F7C000011000069
++:1050400000977C000001000000F77C0000DFBFE358
++:1050500004FF7C0000DFBFE304077D0000010000C7
++:1050600000177D000000FFFF001F7D000001000011
++:1050700000377D000000FFFF007F7D000001000081
++:10508000008F7D000001000000977D0000001020CF
++:10509000309F7D000040506070A77D00008898A878
++:1050A000B8AF7D0000C8D8E8F8BF7D00001A000046
++:1050B00000C67D000008000000CE7D000008000052
++:1050C00000CF7D000004000000D67D000008000035
++:1050D00000DE7D000008000000E67D000008000002
++:1050E00000EE7D000008000000F67D0000080000D2
++:1050F00000FE7D000008000000067E000011000098
++:1051000000287E00000000803F467E00008168ACE1
++:105110000F4E7E000000040000567E0000000400D8
++:10512000005E7E000000040000607E0000040000BD
++:1051300000667E000000040000687E00001A000087
++:10514000006E7E000000040000767E000000040077
++:1051500000777E0000040000007E7E000000040056
++:10516000007F7E000004000000807E00000100003F
++:1051700000867E000000040000877E00000001021F
++:10518000038E7E000000030000967E0000000300F6
++:10519000009E7E000000030000A67E0000000300C9
++:1051A00000AE7E000000030000AF7E00000400009F
++:1051B00000B67E000000030000BE7E000000030079
++:1051C00000C67E000000030000C77E00000400004F
++:1051D00000CE7E000001000000CF7E000004000031
++:1051E00000D67E00000F000000D77E000080000087
++:1051F00000DF7E000004000000167F000020000099
++:1052000000187F000000FFFF001E7F00001100005B
++:1052100000267F000000010000367F000001000032
++:10522000004E7F000040000000507F00000F000093
++:1052300000567F000000010000667F0000030000B0
++:10524000008E7F000067FE1F00907F00008168AC29
++:105250000F987F000011000000AE7F0000020000E8
++:1052600000B67F00008168AC0F06800000010000DE
++:105270000007800000040000000F80000080000094
++:1052800000178000000400000018800000040000E7
++:10529000001F8000000001020327800000030000BF
++:1052A000002E800000040000002F80000004000099
++:1052B0000030800000010000003E8000000100007E
++:1052C000004680000000040000488000000200004A
++:1052D000004E80000000030000508000000000002D
++:1052E00004568000000110000057800000040000F8
++:1052F0000058800000000000045F800000030000F0
++:105300000076800000110000007880000005000099
++:10531000007F800000040000008080000052000038
++:1053200000A880000001000000B68000008168AC89
++:105330000FBE8000000F00000030810000000080E0
++:105340003F388100000000803F4081000000008065
++:105350003F488100000000803F5081000000008035
++:105360003F588100000000803F6081000000008005
++:105370003F688100000000803F70810000000080D5
++:105380003F788100000000803F7E8100008168AC92
++:105390000F808100000000803F8681000067FE1FB3
++:1053A00000888100000000803F90810000000080A4
++:1053B0003F988100000000803FA081000000008035
++:1053C0003FA681000011000000A8810000000080BD
++:1053D0003FB081000010000000BE8100000400000A
++:1053E00000CE81000001000000D681000001000015
++:1053F00000F6810000010000001E8200008168AC00
++:105400000F468200000400000056820000010000E8
++:10541000005E8200000100000076820000010000B2
++:1054200000CE82000001000000D6820000010000D2
++:1054300000DE82000001000000E6820000010000A2
++:1054400000E8820000120C1008EE8200000100004B
++:1054500000F082000005000000F68200000100005C
++:1054600000FE820000010000000683000001000031
++:105470000008830000010000000E830000110000FE
++:105480000018830000FFFF000020830000FFFF00E2
++:105490000028830000FFFF000030830000FFFF00B2
++:1054A0000038830000030000004E8300008168ACD8
++:1054B0000F568300000F0000005883000000FFFF1C
++:1054C00000608300001A00000070830000030000E9
++:1054D0000096830000110000009E83000001000080
++:1054E00000FE830000DFBFE30406840000DFBFE3AB
++:1054F000040E840000010000001E84000000FFFF75
++:105500000026840000010000003E84000000FFFF30
++:105510000086840000010000009684000001000065
++:10552000009E84000000102030A6840000405060DF
++:1055300070AE8400008898A8B8B6840000C8D8E887
++:10554000F8C68400001A000000D6840000040000A1
++:10555000005885000002010000688500000400007A
++:105560000070850000040000007885000004000041
++:10557000007E85000004000000808500000400001B
++:105580000086850000040000008E850000000102F6
++:1055900003B685000004000000C885000004000078
++:1055A00000CE85000004000000D08500000400004B
++:1055B00000D685000004000000DE850000800000A9
++:1055C00000E0850000FF070000E685000004000001
++:1055D00000F0850000020100004086000004000089
++:1055E000004886000004000000508600000400000F
++:1055F0000058860000040000000E87000004000030
++:105600000016870000800000001E870000040000D4
++:105610000026870000000102032E8700000300001F
++:105620000036870000040000005E870000040000D0
++:105630000066870000030000008687000004000069
++:105640000027910000040000002F910000030000DB
++:10565000005F910000010000006F91000001000058
++:10566000008F9100008824712A9F91000000C0855E
++:1056700040A791000040000000AF91000000010031
++:1056800000B791000000010100BF91000000008000
++:10569000029F9200000F000000FF92000004000033
++:1056A0000007930000FFFF00000F930000FFFF00C2
++:1056B0000017930000FFFF00001F930000FFFF0092
++:1056C00000679300000100000087930000010000C4
++:1056D00000B793000001000000CF9300000100001C
++:1056E0000027940000010000002F9400000100003A
++:1056F0000037940000020000003F94000001000009
++:105700000047940000010000004F940000020000D8
++:105710000057940000010000006794000011000091
++:1057200000A79400008168AC0FBF94000004000043
++:1057300000DF94000011000000E794000001000069
++:1057400000F7940000CF000000FF940000CF00009D
++:105750000007950000CF0000005F950000010000E9
++:105760000067950000010000006F95000002000036
++:105770000077950000010000007F95000001000007
++:105780000087950000020000008F950000010000D6
++:10579000009F95000001000000A795000001000097
++:1057A00000AF95000001000000B795000001000067
++:1057B00000BF95000001000000C795000001000037
++:1057C00000CF95000001000000D795000001000007
++:1057D00000DF950000110000001F9600008168ACFA
++:1057E0000F279600000F0000006796000067FE1F5D
++:1057F000007F960000110000008796000001000065
++:1058000000A796000004000000D7960000010000E9
++:1058100000FF960000110000003F9700008168AC77
++:105820000F5F970000110000006797000001000063
++:10583000007797000001000000879700000100003A
++:105840000097970000FF070000A7970000010000E5
++:1058500000B7970000010000002E9800000400002F
++:105860000036980000030000000F990000080000B7
++:105870000017990000080000001F990000080000B0
++:105880000027990000080000002F99000008000080
++:105890000037990000080000003F99000008000050
++:1058A0000047990000080000004F99000011000017
++:1058B000008F9900008168AC0F97990000000400E8
++:1058C000009F99000000040000A799000000040058
++:1058D00000AF99000000040000B799000000040028
++:1058E00000BF99000000040000C7990000000400F8
++:1058F00000CF99000000040000D7990000000300C9
++:1059000000DF99000000030000E799000000030099
++:1059100000EF99000000030000F799000000030069
++:1059200000FF99000000030000079A000000030038
++:10593000000F9A000000030000179A000001000009
++:10594000001F9A00000F0000005F9A000020000076
++:1059500000679A0000110000006F9A00000001002B
++:10596000007F9A000001000000979A0000400000AC
++:10597000009F9A000000010000AF9A0000030000A1
++:1059800000D79A000067FE1F00F79A00000200008F
++:1059900000FF9A00008168AC0F4F9B0000010000DF
++:1059A00000779B000004000000879B0000010000BE
++:1059B000008F9B000000040000979B000000030084
++:1059C000009F9B000001100000BF9B000011000021
++:1059D00000FF9B00008168AC0F079C00000F0000D7
++:1059E00000C79C00008168AC0FCF9C000067FE1FC1
++:1059F00000EF9C000011000000079D000004000063
++:105A000000179D0000010000001F9D000001000024
++:105A1000003F9D000001000000679D00008168AC10
++:105A20000F8F9D0000040000009F9D0000010000FA
++:105A300000A79D000001000000BF9D0000010000C4
++:105A400000179E0000010000001F9E0000010000E2
++:105A500000279E0000010000002F9E0000010000B2
++:105A600000379E0000010000003F9E000001000082
++:105A700000479E0000010000004F9E000001000052
++:105A800000579E000011000000979E00008168AC46
++:105A90000F9F9E00000F000000DF9E00001100001D
++:105AA00000E79E000001000000479F0000DFBFE309
++:105AB000044F9F0000DFBFE304579F000001000078
++:105AC00000679F000000FFFF006F9F0000010000C3
++:105AD00000879F000000FFFF00CF9F000001000033
++:105AE00000DF9F000001000000E79F000000102081
++:105AF00030EF9F000040506070F79F00008898A82A
++:105B0000B8FF9F0000C8D8E8F80FA000001A0000F6
++:105B1000001FA0000004000000C7A0000004000057
++:105B200000CFA0000004000000D7A0000000010288
++:105B300003FFA000000400000017A1000004000003
++:105B4000001FA100000400000027A1000080000049
++:105B5000002FA100000400000057A2000004000074
++:105B6000005FA200008000000067A20000040000A7
++:105B7000006FA200000001020377A20000030000F2
++:105B8000007FA2000004000000A7A20000040000A3
++:105B900000AFA2000003000000CFA200000400003C
++:105BA0000077B30000040000007FB3000003000092
++:105BB00000AFB3000001000000BFB300000100000F
++:105BC00000DFB300008824712AEFB3000000C08515
++:105BD00040F7B3000040000000FFB30000000100E8
++:105BE0000007B40000000101000FB40000000080B5
++:105BF00002EFB400000F0000004FB50000040000E9
++:105C00000057B50000FFFF00005FB50000FFFF0078
++:105C10000067B50000FFFF00006FB50000FFFF0048
++:105C200000B7B5000001000000D7B500000100007A
++:105C30000007B60000010000001FB60000010000D0
++:105C40000077B60000010000007FB60000010000F0
++:105C50000087B60000020000008FB60000010000BF
++:105C60000097B60000010000009FB600000200008F
++:105C700000A7B6000001000000B7B6000011000048
++:105C800000F7B600008168AC0F0FB70000040000F9
++:105C9000002FB700001100000037B700000100001E
++:105CA0000047B70000CF0000004FB70000CF000052
++:105CB0000057B70000CF000000AFB70000010000A0
++:105CC00000B7B7000001000000BFB70000020000ED
++:105CD00000C7B7000001000000CFB70000010000BE
++:105CE00000D7B7000002000000DFB700000100008D
++:105CF00000EFB7000001000000F7B700000100004E
++:105D000000FFB700000100000007B800000100001C
++:105D1000000FB800000100000017B80000010000EB
++:105D2000001FB800000100000027B80000010000BB
++:105D3000002FB80000110000006FB800008168ACAF
++:105D40000F77B800000F000000B7B8000067FE1F13
++:105D500000CFB8000011000000D7B800000100001B
++:105D600000F7B800000400000027B900000100009F
++:105D7000004FB90000110000008FB900008168AC2D
++:105D80000FAFB9000011000000B7B900000100001A
++:105D900000C7B9000001000000D7B90000010000F1
++:105DA00000E7B90000FF070000F7B900000100009C
++:105DB0000007BA0000010000005FBB0000080000FF
++:105DC0000067BB0000080000006FBB000008000077
++:105DD0000077BB0000080000007FBB000008000047
++:105DE0000087BB0000080000008FBB000008000017
++:105DF0000097BB0000080000009FBB0000110000DE
++:105E000000DFBB00008168AC0FE7BB0000000400AE
++:105E100000EFBB000000040000F7BB00000004001E
++:105E200000FFBB00000004000007BC0000000400ED
++:105E3000000FBC00000004000017BC0000000400BC
++:105E4000001FBC00000004000027BC00000003008D
++:105E5000002FBC00000003000037BC00000003005E
++:105E6000003FBC00000003000047BC00000003002E
++:105E7000004FBC00000003000057BC0000000300FE
++:105E8000005FBC00000003000067BC0000010000D0
++:105E9000006FBC00000F000000AFBC00002000003D
++:105EA00000B7BC000011000000BFBC0000000100F2
++:105EB00000CFBC000001000000E7BC000040000073
++:105EC00000EFBC000000010000FFBC000003000068
++:105ED0000027BD000067FE1F0047BD000002000054
++:105EE000004FBD00008168AC0F9FBD0000010000A5
++:105EF00000C7BD000004000000D7BD000001000085
++:105F000000DFBD000000040000E7BD00000003004A
++:105F100000EFBD0000011000000FBE0000110000E6
++:105F2000004FBE00008168AC0F57BE00000F00009C
++:105F30000017BF00008168AC0F1FBF000067FE1F85
++:105F4000003FBF00001100000057BF000004000028
++:105F50000067BF0000010000006FBF0000010000EB
++:105F6000008FBF000001000000B7BF00008168ACD7
++:105F70000FDFBF000004000000EFBF0000010000C1
++:105F800000F7BF0000010000000FC000000100008A
++:105F90000067C00000010000006FC00000010000A9
++:105FA0000077C00000010000007FC0000001000079
++:105FB0000087C00000010000008FC0000001000049
++:105FC0000097C00000010000009FC0000001000019
++:105FD00000A7C0000011000000E7C000008168AC0D
++:105FE0000FEFC000000F0000002FC10000110000E3
++:105FF0000037C100000100000097C10000DFBFE3CF
++:10600000049FC10000DFBFE304A7C100000100003E
++:1060100000B7C1000000FFFF00BFC1000001000089
++:1060200000D7C1000000FFFF001FC20000010000F8
++:10603000002FC200000100000037C2000000102045
++:10604000303FC200004050607047C200008898A8EE
++:10605000B84FC20000C8D8E8F85FC200001A0000BC
++:10606000006FC200000400000017C300000400001D
++:10607000001FC300000400000027C300000001024D
++:10608000034FC300000400000067C30000040000C9
++:10609000006FC300000400000077C3000080000010
++:1060A000007FC3000004000000A7C400000400003B
++:1060B00000AFC4000080000000B7C400000400006E
++:1060C00000BFC4000000010203C7C40000030000B9
++:1060D00000CFC4000004000000F7C400000400006A
++:1060E00000FFC40000030000001FC5000004000002
++:1060F00000C7D5000004000000CFD5000003000059
++:106100000093D900000F000000FAF3000004000023
++:106110000002F40000040000000AF40000120C1059
++:106120000812F400000300000022F40000120C101A
++:106130000832F40000140C08003AF40000010000DA
++:106140000042F40000040000004AF40000040000D3
++:106150000052F40000140C08006AF40000120C1045
++:106160000872F40000270000008AF400000100001B
++:1061700000510A010004000000590A010004000057
++:1061800000690A010080000000710A01000400009B
++:1061900000790A010001000000890A0100270000BF
++:1061A00000990A0100260000009A12010001000077
++:1061B0000052130100120C100880130100120C1081
++:1061C0000889130100800000008A1301008000008C
++:1061D000008B13010080000000901301008000007C
++:1061E00000911301000470008092130100047000FC
++:1061F0008093130100047000809813010004700064
++:106200008099130100000400049A130100000400A7
++:10621000049B13010000040004A01301000004000B
++:1062200004A1130100C0000000A2130100C000007F
++:1062300000A3130100C0000000A8130100C000006B
++:1062400000A913010000100000AA130100001000B3
++:1062500000AB13010000100000B01301000010009B
++:1062600000C113010001000000C213010001000081
++:1062700000C313010001000000C813010001000069
++:1062800000D913010001000000DA13010001000031
++:1062900000DB13010001000000E013010001000019
++:1062A00000E913010004000000EA130100040000EB
++:1062B00000EB13010004000000F0130100040000D3
++:1062C00000F113010002000000F2130100020000BF
++:1062D00000F313010002000000F8130100020000A7
++:1062E00000611401008000000062140100800000C1
++:1062F00000631401008000000068140100800000A9
++:106300000069140100047000806A14010004700028
++:10631000806B140100047000807014010004700090
++:1063200080711401000004000472140100000400D4
++:106330000473140100000400047814010000040038
++:106340000479140100C00000007A140100C00000AC
++:10635000007B140100C000000080140100C0000098
++:1063600000811401000010000082140100001000E0
++:1063700000831401000010000088140100001000C8
++:106380000099140100010000009A140100010000AE
++:10639000009B14010001000000A014010001000096
++:1063A00000B114010001000000B21401000100005E
++:1063B00000B314010001000000B814010001000046
++:1063C00000C114010004000000C214010004000018
++:1063D00000C314010004000000C814010004000000
++:1063E00000C914010002000000CA140100020000EC
++:1063F00000CB14010002000000D0140100020000D4
++:106400000039150100800000003A150100800000ED
++:10641000003B1501008000000040150100800000D5
++:106420000041150100047000804215010004700055
++:1064300080431501000470008048150100047000BD
++:106440008049150100000400044A15010000040001
++:10645000044B150100000400045015010000040065
++:106460000451150100C000000052150100C00000D9
++:106470000053150100C000000058150100C00000C5
++:106480000059150100001000005A1501000010000D
++:10649000005B1501000010000060150100001000F5
++:1064A00000711501000100000072150100010000DB
++:1064B00000731501000100000078150100010000C3
++:1064C0000089150100010000008A1501000100008B
++:1064D000008B150100010000009015010001000073
++:1064E0000099150100040000009A15010004000045
++:1064F000009B15010004000000A01501000400002D
++:1065000000A115010002000000A215010002000018
++:1065100000A315010002000000A815010002000000
++:106520000009160100120C10080A160100120C10C6
++:10653000080B160100120C100810160100120C10A6
++:10654000081916010021FE01001A16010021FE01A2
++:10655000001B16010021FE01002016010021FE0192
++:106560000049160100FFFF00004A160100FFFF006E
++:10657000004B160100FFFF000050160100FFFF0056
++:106580000051160100FFFF000052160100FFFF003E
++:106590000053160100FFFF000058160100FFFF0026
++:1065A0000059160100FFFF00005A160100FFFF000E
++:1065B000005B160100FFFF000060160100FFFF00F6
++:1065C0000061160100FFFF000062160100FFFF00DE
++:1065D0000063160100FFFF000068160100FFFF00C6
++:1065E0000069160100010000006A160100010000A8
++:1065F000006B160100010000007016010001000090
++:106600000071160100010001007216010001000175
++:10661000007316010001000100781601000100015D
++:106620000079160100010001007A16010001000145
++:10663000007B16010001000100801601000100012D
++:106640000081160100010000008216010001000017
++:1066500000831601000100000088160100010000FF
++:10666000009116010021FE01009216010021FE0199
++:10667000009316010021FE01009816010021FE0181
++:1066800000C1160100120C1008C2160100120C10F5
++:1066900008C3160100120C1008C8160100120C10D5
++:1066A00008C916010004000000CA16010004000019
++:1066B00000CB16010004000000D016010004000009
++:1066C00000D916010002000000DA160100020000E5
++:1066D00000DB16010002000000E0160100020000CD
++:1066E00000E116010011000000E216010011000097
++:1066F00000E316010011000000E81601001100007F
++:1067000000291701008168AC0F2A1701008168ACCD
++:106710000F2B1701008168AC0F301701008168ACA6
++:106720000F5117010004000000521701000400007F
++:106730000053170100040000005817010004000076
++:1067400000A117010002000000A2170100020000D2
++:1067500000A317010002000000A8170100020000BA
++:1067600000A917010001000000AA170100010000A4
++:1067700000AB17010001000000B01701000100008C
++:1067800000B117010001000000B217010001000074
++:1067900000B317010001000000B81701000100005C
++:1067A00000B917010002000000BA17010002000042
++:1067B00000BB17010002000000C01701000200002A
++:1067C00000C117010001000000C217010001000014
++:1067D00000C317010001000000C8170100010000FC
++:1067E00000C917010001000000CA170100010000E4
++:1067F00000CB17010001000000D0170100010000CC
++:1068000000D117010001000000D2170100010000B3
++:1068100000D317010001000000D81701000100009B
++:1068200000E117010004000000E21701000400006D
++:1068300000E317010004000000E817010004000055
++:1068400000013501001100000002350100110000B7
++:10685000000335010011000000083501001100009F
++:106860000011350100010000001235010001000097
++:10687000001335010001000000183501000100007F
++:106880000089360100800000008A36010080000087
++:10689000008B36010080000000903601008000006F
++:1068A00000913601000470008092360100047000EF
++:1068B0008093360100047000809836010004700057
++:1068C0008099360100000400049A3601000004009B
++:1068D000049B36010000040004A0360100000400FF
++:1068E00004A1360100C0000000A2360100C0000073
++:1068F00000A3360100C0000000A8360100C000005F
++:1069000000A936010000100000AA360100001000A6
++:1069100000AB36010000100000B03601000010008E
++:1069200000C136010001000000C236010001000074
++:1069300000C336010001000000C83601000100005C
++:1069400000D936010001000000DA36010001000024
++:1069500000DB36010001000000E03601000100000C
++:1069600000E936010004000000EA360100040000DE
++:1069700000EB36010004000000F0360100040000C6
++:1069800000F136010002000000F2360100020000B2
++:1069900000F336010002000000F83601000200009A
++:1069A00000613701008000000062370100800000B4
++:1069B000006337010080000000683701008000009C
++:1069C0000069370100047000806A3701000470001C
++:1069D000806B370100047000807037010004700084
++:1069E00080713701000004000472370100000400C8
++:1069F000047337010000040004783701000004002C
++:106A00000479370100C00000007A370100C000009F
++:106A1000007B370100C000000080370100C000008B
++:106A200000813701000010000082370100001000D3
++:106A300000833701000010000088370100001000BB
++:106A40000099370100010000009A370100010000A1
++:106A5000009B37010001000000A037010001000089
++:106A600000B137010001000000B237010001000051
++:106A700000B337010001000000B837010001000039
++:106A800000C137010004000000C23701000400000B
++:106A900000C337010004000000C8370100040000F3
++:106AA00000C937010002000000CA370100020000DF
++:106AB00000CB37010002000000D0370100020000C7
++:106AC0000039380100800000003A380100800000E1
++:106AD000003B3801008000000040380100800000C9
++:106AE0000041380100047000804238010004700049
++:106AF00080433801000470008048380100047000B1
++:106B00008049380100000400044A380100000400F4
++:106B1000044B380100000400045038010000040058
++:106B20000451380100C000000052380100C00000CC
++:106B30000053380100C000000058380100C00000B8
++:106B40000059380100001000005A38010000100000
++:106B5000005B3801000010000060380100001000E8
++:106B600000713801000100000072380100010000CE
++:106B700000733801000100000078380100010000B6
++:106B80000089380100010000008A3801000100007E
++:106B9000008B380100010000009038010001000066
++:106BA0000099380100040000009A38010004000038
++:106BB000009B38010004000000A038010004000020
++:106BC00000A138010002000000A23801000200000C
++:106BD00000A338010002000000A8380100020000F4
++:106BE0000009390100120C10080A390100120C10BA
++:106BF000080B390100120C100810390100120C109A
++:106C0000081939010021FE01001A39010021FE0195
++:106C1000001B39010021FE01002039010021FE0185
++:106C20000049390100FFFF00004A390100FFFF0061
++:106C3000004B390100FFFF000050390100FFFF0049
++:106C40000051390100FFFF000052390100FFFF0031
++:106C50000053390100FFFF000058390100FFFF0019
++:106C60000059390100FFFF00005A390100FFFF0001
++:106C7000005B390100FFFF000060390100FFFF00E9
++:106C80000061390100FFFF000062390100FFFF00D1
++:106C90000063390100FFFF000068390100FFFF00B9
++:106CA0000069390100010000006A3901000100009B
++:106CB000006B390100010000007039010001000083
++:106CC0000071390100010001007239010001000169
++:106CD0000073390100010001007839010001000151
++:106CE0000079390100010001007A39010001000139
++:106CF000007B390100010001008039010001000121
++:106D0000008139010001000000823901000100000A
++:106D100000833901000100000088390100010000F2
++:106D2000009139010021FE01009239010021FE018C
++:106D3000009339010021FE01009839010021FE0174
++:106D400000C1390100120C1008C2390100120C10E8
++:106D500008C3390100120C1008C8390100120C10C8
++:106D600008C939010004000000CA3901000400000C
++:106D700000CB39010004000000D0390100040000FC
++:106D800000D939010002000000DA390100020000D8
++:106D900000DB39010002000000E0390100020000C0
++:106DA00000E139010011000000E23901001100008A
++:106DB00000E339010011000000E839010011000072
++:106DC00000293A01008168AC0F2A3A01008168ACC1
++:106DD0000F2B3A01008168AC0F303A01008168AC9A
++:106DE0000F513A010004000000523A010004000073
++:106DF00000533A010004000000583A01000400006A
++:106E000000A13A010002000000A23A0100020000C5
++:106E100000A33A010002000000A83A0100020000AD
++:106E200000A93A010001000000AA3A010001000097
++:106E300000AB3A010001000000B03A01000100007F
++:106E400000B13A010001000000B23A010001000067
++:106E500000B33A010001000000B83A01000100004F
++:106E600000B93A010002000000BA3A010002000035
++:106E700000BB3A010002000000C03A01000200001D
++:106E800000C13A010001000000C23A010001000007
++:106E900000C33A010001000000C83A0100010000EF
++:106EA00000C93A010001000000CA3A0100010000D7
++:106EB00000CB3A010001000000D03A0100010000BF
++:106EC00000D13A010001000000D23A0100010000A7
++:106ED00000D33A010001000000D83A01000100008F
++:106EE00000E13A010004000000E23A010004000061
++:106EF00000E33A010004000000E83A010004000049
++:106F000000015801001100000002580100110000AA
++:106F10000003580100110000000858010011000092
++:106F2000001158010001000000125801000100008A
++:106F30000013580100010000001858010001000072
++:106F4000008A590100800000008B59010080000078
++:106F500000925901000470008093590100047000F0
++:106F6000809A590100000400049B590100000400AC
++:106F700004A2590100C0000000A3590100C0000094
++:106F800000AA59010000100000AB590100001000D8
++:106F900000C259010001000000C3590100010000B6
++:106FA00000DA59010001000000DB59010001000076
++:106FB00000EA59010004000000EB59010004000040
++:106FC00000F259010002000000F359010002000024
++:106FD00000625A010080000000635A010080000036
++:106FE000006A5A0100047000806B5A0100047000AE
++:106FF00080725A010000040004735A01000004006A
++:10700000047A5A0100C00000007B5A0100C0000051
++:1070100000825A010000100000835A010000100095
++:10702000009A5A0100010000009B5A010001000073
++:1070300000B25A010001000000B35A010001000033
++:1070400000C25A010004000000C35A0100040000FD
++:1070500000CA5A010002000000CB5A0100020000E1
++:10706000003A5B0100800000003B5B0100800000F3
++:1070700000425B010004700080435B01000470006B
++:10708000804A5B0100000400044B5B010000040027
++:1070900004525B0100C0000000535B0100C000000F
++:1070A000005A5B0100001000005B5B010000100053
++:1070B00000725B010001000000735B010001000031
++:1070C000008A5B0100010000008B5B0100010000F1
++:1070D000009A5B0100040000009B5B0100040000BB
++:1070E00000A25B010002000000A35B01000200009F
++:1070F000000A5C0100120C10080B5C0100120C105D
++:10710000081A5C010021FE01001B5C010021FE0148
++:10711000004A5C0100FFFF00004B5C0100FFFF0024
++:1071200000525C0100FFFF0000535C0100FFFF0004
++:10713000005A5C0100FFFF00005B5C0100FFFF00E4
++:1071400000625C0100FFFF0000635C0100FFFF00C4
++:10715000006A5C0100010000006B5C01000100009E
++:1071600000725C010001000100735C01000100017C
++:10717000007A5C0100010001007B5C01000100015C
++:1071800000825C010001000000835C01000100003E
++:1071900000925C010021FE0100935C010021FE01D0
++:1071A00000C25C0100120C1008C35C0100120C103C
++:1071B00008CA5C010004000000CB5C010004000070
++:1071C00000DA5C010002000000DB5C01000200004C
++:1071D00000E25C010011000000E35C01001100000E
++:1071E000002A5D01008168AC0F2B5D01008168AC55
++:1071F0000F525D010004000000535D010004000017
++:1072000000A25D010002000000A35D010002000079
++:1072100000AA5D010001000000AB5D01000100005B
++:1072200000B25D010001000000B35D01000100003B
++:1072300000BA5D010002000000BB5D010002000019
++:1072400000C25D010001000000C35D0100010000FB
++:1072500000CA5D010001000000CB5D0100010000DB
++:1072600000D25D010001000000D35D0100010000BB
++:1072700000E25D010004000000E35D010004000085
++:1072800000027B010011000000037B0100110000DF
++:1072900000127B010001000000137B0100010000CF
++:0172A00000ED
++:00000001FF
+diff --git a/firmware/nouveau/nva5.ctxprog.ihex b/firmware/nouveau/nva5.ctxprog.ihex
+new file mode 100644
+index 0000000..57a9369
+--- /dev/null
++++ b/firmware/nouveau/nva5.ctxprog.ihex
+@@ -0,0 +1,77 @@
++:100000004E564350002C019C007000000030000947
++:10001000FB4400092D400051EF4000440A40000518
++:100020000A40000D0A40008E0070004D1240009DF5
++:100030000070004D0A450097007000210B4500A19B
++:100040004644004D7944004D8544001D0070000673
++:1000500018400005006000454444008B30440045D2
++:100060001840004D23400081007000CF1C40009FCD
++:100070000070009F0050004DE64400170070000B18
++:100080002340004D804400210C4500A1564400A0AF
++:10009000007000010070000300700006274000059A
++:1000A0002840000D006000050070000D0070000683
++:1000B000007000020070000B0070000E0070001C49
++:1000C0000070000C00600000000000FFFF9000FFC7
++:1000D000FF91004DDE4400090060004D004800968D
++:1000E000007000CF3A40009F0070009F0050005108
++:1000F000EF4000C036400080002000080060004F44
++:10010000364000C0364000CC3E400051364000161C
++:100110000070004D004800110060004D0048004D87
++:100120003644008E007000810070004D7344004D15
++:10013000804400830070000000300000512100075F
++:10014000006000010AC0002200200001008000CBF6
++:10015000005000FF00C0004D5E44004D0048000804
++:10016000D844004D7644004D8E44004D5E44004D11
++:10017000EC44004D7944004D8544004D0048008317
++:100180000070004D0D45003F02A000400020000619
++:100190000060004D0645004D904400E701200060DE
++:1001A000C14400780220000100300061C1440040D9
++:1001B000032000FFFF38004DD6440000003000CB84
++:1001C0000050004D254500CB00500007DA44004D9B
++:1001D0000048004D7C4400FC1B11004D0048004DC0
++:1001E0007C4400FD1B11004D0048004D7C4400FE86
++:1001F0001B11004D004800000020000000700006A8
++:100200000060004D00480001002000060060004D25
++:100210000645000A0211004D00480000003000FFB2
++:10022000FFC300000020000700600000007000080D
++:10023000002000FF008000CB0050004D004800006F
++:100240000000004D004800000000000202170032CC
++:100250000020000D02100042021E00C0021100C466
++:10026000021100C80211000203120002041500006E
++:1002700005180009051300500515000506110013A7
++:1002800000200007061000000711000009110002FD
++:10029000091200000A1100020B1600280B12002B95
++:1002A0000B1400010C1100010D1100DA00200000F8
++:1002B0001210000014110005141100071411000998
++:1002C0001411000B141100051A14000C1A1300006D
++:1002D0001C1300041C1300201C1400251C140040D7
++:1002E0001C1300441C1300601C1400651C14000047
++:1002F0001F1300041F1300081F11000B1F1100150E
++:10030000002000401F10004D004800060060004D16
++:10031000254500202011002220110060002000400F
++:10032000201000C0201500C8201100CA201400CFE2
++:10033000201D000021120003211200402116004759
++:1003400021120053211200A0211100C0211200CB64
++:10035000211100D4211100D82115004D00480000C2
++:100360000000004D0048000B0060004D0048000AEE
++:100370000060004D0048000B0060004DDC40002094
++:10038000002000080060004C0050004D004800E8CC
++:10039000032000080060004C0050004D004800049D
++:1003A0000060004A0050004D004800FF00C000FF00
++:1003B00000C8004D004800FF00C000FF00C8004D0D
++:1003C000004800160070008E00700082007000412E
++:1003D0000050004DE2440095007000D1005000161E
++:1003E0000060005200500002007000150070004DC7
++:1003F0002840008E0070004DDE4400000020000701
++:1004000000600000003000FF00C00000002000FF7E
++:10041000008000090070000E0070004D0048008050
++:1004200000700017004800000070004D0048004DAB
++:100430000048004D0048004D0048008E0070004DFF
++:10044000DE4400830070004DE944004D1645000F66
++:100450000070008C154100CB0050004D004800405A
++:1004600003200007006000871A45004D0048000087
++:10047000000000722020004D064500FF0080004D66
++:1004800000480000072100070060003B0920004DE4
++:10049000064500FF0088004D0048000F0048004B53
++:1004A0000048004D1F45008F0070008C004800CBB5
++:0704B0000050004D00480060
++:00000001FF
+diff --git a/firmware/nouveau/nva5.ctxvals.ihex b/firmware/nouveau/nva5.ctxvals.ihex
+new file mode 100644
+index 0000000..a57f2cd
+--- /dev/null
++++ b/firmware/nouveau/nva5.ctxvals.ihex
+@@ -0,0 +1,772 @@
++:100000004E56435600040600004300000030000036
++:10001000004B000000020000005F00000000100024
++:1000200000740000000300000075000000001000D4
++:1000300000880000009D620100890000009D6201AF
++:10004000008A0000000CFE0000900000000040004C
++:10005000009B00000087010000A0000000181000B5
++:1000600000A1000000FF000000B400000004000038
++:1000700000B5000000DF002514B7000000000600F6
++:1000800000BD00000000000001BE000000FF0000F5
++:1000900000C000000000040000C6000000010000D5
++:1000A00000C700000001000000CB000000020000BB
++:1000B00000CC00000001000000D0000000010000A2
++:1000C00000D100000000010000D700000002000085
++:1000D00000D800000001000000D90000000100006D
++:1000E00000DD00000001000000DE000000FFFF3F17
++:1000F00000DF000000FF1F0000E100000001000021
++:1001000000E200000001000000E400000001000027
++:1001100000E500000001000000E600000001000012
++:1001200000E700000004000000E8000000010000FB
++:1001300000EA00000001000000EB000000010000E8
++:1001400000EC00000007000000ED000000010000CE
++:1001500000EE00000007000000EF000000010000BA
++:1001600000F000000001000000F1000000010000AC
++:1001700000F600000001000000F700000000010090
++:1001800000F900000001000000FC00000000010078
++:1001900000FD00000001000000FE00000000010062
++:1001A0000000010000010000000301000000010048
++:1001B00000080100000400000009010000700000B8
++:1001C000000A01000080000000100100000C000087
++:1001D00000120100000800000013010000140000DC
++:1001E0000015010000290000001601000027000092
++:1001F00000170100002600000018010000080000A0
++:100200000019010000040000001A0100002700008E
++:10021000001D010000010000001E0100000200009E
++:10022000001F010000030000002001000004000086
++:10023000002101000005000000220100000600006E
++:10024000002301000007000000240100000100005D
++:100250000035010000CF00000041010000800000D7
++:1002600000420100000400000043010000040000FF
++:1002700000440100000300000046010000030000EC
++:100280000047010000010000004A010000120000C8
++:10029000004B010000100000004C0100000C0000A9
++:1002A000004D0100000100000051010000040000A9
++:1002B0000052010000020000005301000004000091
++:1002C0000057010000FFFF3F0058010000FF1F0022
++:1002D0000062010000010000006301000001000055
++:1002E000006401000002000000660100000400003C
++:1002F0000067010000140000006801000001000018
++:10030000006C01000002000000710100000100000B
++:1003100000730100000200000074010000001000E2
++:100320000075010000000E000076010000001000C2
++:100330000077010000001E000079010000010000AC
++:10034000007A010000010000007B010000010000B4
++:10035000007C010000010000007D010000010000A0
++:100360000081010000000200008201000000020084
++:1003700000840100000100000085010000F0000081
++:100380000086010000FF000000890100000100005C
++:10039000008A010000F00000008B010000FF000057
++:1003A000008D010000090000009001000001000024
++:1003B0000091010000CF0000009301000001000047
++:1003C0000097010000CF000000990100000200002A
++:1003D000009B010000010000009D010000010000E1
++:1003E000009F010000CF000000A0010000CF00002E
++:1003F00000A101000001000000A201000080000E29
++:1004000000A301000004000000A8010000801F00FC
++:1004100000A901000030000000AE01000021F874C6
++:100420007BAF01000001800589B1010000001000D0
++:1004300000B2010000FF010000B4010000FA107CCE
++:1004400082B5010000C0000040B601000080208994
++:10045000B7BB01000001000000BC01000021F874DE
++:100460007BBD01000001800589BF01000000100074
++:1004700000C0010000FF010000C2010000FA107C72
++:1004800082C3010000C0000040C401000080208938
++:10049000B7C901000001000000CA01000040003D92
++:1004A00000CC01000022000000D401000040003D0B
++:1004B00000D501000022000000E70100000AFF0053
++:1004C00000E901000000008001EA010000000016C0
++:1004D00000EB01000000008001EF010000FFFF03BE
++:1004E00000F001000000000C31F9010000010401DE
++:1004F00000FB01000078000000FD010000BF0000CB
++:1005000000FF010000101200000002000080000047
++:1005100008030200003E0000000902000000008005
++:10052000010A020000000016000B0200000000801B
++:10053000010F020000FFFF03001002000000000C8A
++:100540003119020000010401001B020000780000C4
++:10055000001D020000BF0000001F0200001012007A
++:10056000002002000080000008230200003E00007E
++:100570000029020000000080012A0200000000168D
++:10058000002B020000000080012F020000FFFF038B
++:10059000003002000000000C3139020000010401AB
++:1005A000003B020000780000003D020000BF000098
++:1005B000003F020000101200004002000080000016
++:1005C00008430200003E0000004A02000070701262
++:1005D000054E020000FFFFFF07550200000704124E
++:1005E000005602000007150905570200000202012B
++:1005F00005580200000102030061020000400000F3
++:1006000000620200000A0B0C0D63020000101214BD
++:100610000064020000F0010000650200000100001B
++:1006200000660200000300000069020000009E0353
++:10063000006A020000000100006B020000003800A8
++:10064000006C02000007E03F006D02000000E03F88
++:10065000006E020000404040006F02000007F0F70B
++:100660006C70020000FF7FBF02780200000AFF00EA
++:10067000007A020000000080017B020000000016EA
++:10068000007C0200000000800180020000FFFF03E8
++:10069000008102000000000C318A02000001040108
++:1006A000008C020000780000008E020000BF0000F5
++:1006B0000090020000101200009102000080000073
++:1006C00008940200003E0000009A02000000008032
++:1006D000019B020000000016009C02000000008048
++:1006E00001A0020000FFFF0300A102000000000CB7
++:1006F00031AA02000001040100AC020000780000F1
++:1007000000AE020000BF000000B0020000101200A6
++:1007100000B102000080000008B40200003E0000AA
++:1007200000BA02000000008001BB020000000016B9
++:1007300000BC02000000008001C0020000FFFF03B7
++:1007400000C102000000000C31CA020000010401D7
++:1007500000CC02000078000000CE020000BF0000C4
++:1007600000D002000010120000D102000080000042
++:1007700008D40200003E000000DB0200007070128E
++:1007800005DF020000FFFFFF07E60200000704127A
++:1007900000E702000007150905E802000002020157
++:1007A00005E902000001020300F20200004000001F
++:1007B00000F30200000A0B0C0DF4020000101214EA
++:1007C00000F5020000F0010000F602000001000048
++:1007D00000F702000003000000FA020000009E0380
++:1007E00000FB02000000010000FC020000003800D5
++:1007F00000FD02000007E03F00FE02000000E03FB5
++:1008000000FF020000404040000003000007F0F736
++:100810006C01030000FF7FBF0241030000040000E1
++:1008200000460300003F00000049030000040000F0
++:10083000004B030000010000004D0300008168AC84
++:100840000F59030000800000006103000004000055
++:100850000069030000140C08006F030000140C086A
++:1008600000730300000F00000079030000FF070081
++:100870000087030000040800009703000004000044
++:10088000009E030000020000009F0300000400001F
++:1008900000A603000000000004A7030000120C10D3
++:1008A00008AD030000DFBFE304AE0300000000005A
++:1008B00004B5030000DFBFE304B703000004000039
++:1008C00000BB03000020000000BF03000004000084
++:1008D00000CF03000010000000D50300001100004D
++:1008E00000E5030000DFBFE304ED030000DFBFE32A
++:1008F00004F603000004000000F7030000040800F1
++:1009000000FF0300000100000005040000110000CA
++:1009100000070400001A0000000F0400007F000020
++:100920000016040000040000001F04000001000085
++:100930000027040000140C080037040000120C10FB
++:10094000083F040000040000004704000004000009
++:10095000004B0400001A00000055040000020000D3
++:1009600000570400001000000065040000677E08C6
++:100970000077040000010000007F040000120C104A
++:10098000088D04000001000000A604000001000022
++:1009900000AE04000001100000B6040000FFFF00DC
++:1009A00000B7040000FF070000BB040000040000C3
++:1009B00000BE040000FFFF0000BF040000140C088C
++:1009C00000C304000004000000C6040000FFFF0094
++:1009D00000CD04000015000000CE040000FFFF0061
++:1009E00000D304000004000000DB04000008000045
++:1009F00000DD04000001000000E50400001000001C
++:100A000000EB040000FF070000F2040000040000F7
++:100A100000FA04000004000000FD040000010000D2
++:100A20000025050000040000002D05000000040062
++:100A30000035050000000300003D05000001100026
++:100A400000530500000F00000075050000020000C3
++:100A500000BD05000001000000C5050000100000F9
++:100A600000D505000001000000D605000000008050
++:100A70003FDE0500000000803FE60500000000802A
++:100A80003FEE0500000000803FF6050000000080FA
++:100A90003FFE0500000000803F06060000000080C9
++:100AA0003F0E0600000000803F1606000000008098
++:100AB0003F1E0600000000803F2606000000008068
++:100AC0003F2E0600000000803F3606000000008038
++:100AD0003F3E0600000000803F4606000000008008
++:100AE0003F4E0600000000803F5606000010000048
++:100AF000006E0600000300000075060000100000F4
++:100B00000087060000010000009F060000100000A2
++:100B100000DE06000000000004E606000000000001
++:100B200004F606000080000000FD060000000080C2
++:100B30003F050700000000803F0D07000000008017
++:100B40003F150700000000803F16070000800000EE
++:100B5000001D0700000000803F2507000000008006
++:100B60003F2D0700000000803F3507000000008097
++:100B70003F3D0700000000803F4507000000008067
++:100B80003F4D0700000000803F4E070000010000BD
++:100B900000550700000000803F56070000000100DC
++:100BA000005D0700000000803F5E070000000100BC
++:100BB00000650700000000803F660700001100008C
++:100BC000006D0700000000803F75070000000080F6
++:100BD0003F7607000008000000A6070000010000A3
++:100BE00000B607000001000000BE07000001000081
++:100BF00000C607000001000000CE070000CF000083
++:100C000000D6070000020000000E080000010000EE
++:100C1000001E08000001000000260800000100007E
++:100C2000002E080000010000004508000010000030
++:100C300000550800003F00000056080000040000B6
++:100C4000006608000001000000670800008800003E
++:100C5000006E080000150000006F0800008800000A
++:100C60000087080000040000008D0800000100005B
++:100C7000008E080000804444049D0800000100002C
++:100C800000AD08000001000000C5080000011000D0
++:100C90000025090000110000003F090000260000A7
++:100CA00000570900000000803F650900000F0000A8
++:100CB000007F0900001A00000087090000100000F2
++:100CC00000A509000011000000DD0900000100007E
++:100CD00000E509000001000000ED0900000100002E
++:100CE00000F509000002000000FD090000010000FD
++:100CF00000050A0000020000000D0A0000010000CB
++:100D0000001D0A0000677E08002D0A00008168AC03
++:100D10000F3D0A000001000000450A00000100002C
++:100D2000004D0A000001000000550A00000100000B
++:100D3000005D0A000001000000650A0000010000DB
++:100D400000660A0000010000006D0A0000010000BA
++:100D500000750A000001000000760A000001000092
++:100D6000007D0A000001000000850A00000100006B
++:100D7000008D0A000001000000950A00000100003B
++:100D800000960A00008824712A9D0A0000010000D4
++:100D900000A50A000001000000A60A000000C085AE
++:100DA00040AD0A000001000000AE0A000040000053
++:100DB00000B50A000001000000B60A0000000100B2
++:100DC00000BD0A000001000000BE0A000000010191
++:100DD00000C50A000001000000C60A0000000080F3
++:100DE00002CD0A000001000000CF0A0000520000FE
++:100DF00000D50A000001000000DD0A00000100002B
++:100E000000DF0A000026000000E50A0000010000E3
++:100E100000ED0A000001000000EF0A0000040000DD
++:100E200000F50A000001000000F70A0000040000BD
++:100E300000FD0A000002000000050B000002000097
++:100E400000070B00001A0000000D0B00000200005C
++:100E500000150B0000020000001D0B000002000046
++:100E6000001F0B000000FFFF00250B000002000028
++:100E7000002D0B000002000000350B0000020000F6
++:100E8000003D0B000001000000450B0000010000C8
++:100E900000460B0000010000004D0B0000010000A7
++:100EA00000550B0000010000005D0B000001000078
++:100EB00000650B0000010000006D0B000001000048
++:100EC00000750B0000010000007D0B000002000017
++:100ED00000850B0000020000008D0B0000020000E6
++:100EE00000950B0000020000009D0B0000020000B6
++:100EF00000A50B000002000000AD0B000002000086
++:100F000000AE0B00000F000000B50B000002000057
++:100F100000BD0B000001000000C50B000001000037
++:100F200000CD0B000001000000D50B000001000007
++:100F300000DD0B000001000000E50B0000010000D7
++:100F400000ED0B000001000000F50B0000010000A7
++:100F5000000E0C000004000000150C000001000051
++:100F600000160C0000FFFF00001E0C0000FFFF0039
++:100F700000260C0000FFFF00002E0C0000FFFF0009
++:100F800000450C000001000000760C00000100008C
++:100F900000960C000001000000C60C0000010000DB
++:100FA00000DE0C000003000000FD0C00000100004A
++:100FB00000050D0000020000000D0D000001000002
++:100FC00000150D0000010000001D0D0000020000D2
++:100FD00000250D0000010000002D0D0000010000A3
++:100FE000003E0D000001000000460D000001000061
++:100FF000004E0D000002000000550D000001000031
++:1010000000560D0000010000005D0D000001000011
++:10101000005E0D000001000000650D0000010000F1
++:1010200000660D0000020000006D0D0000010000D0
++:1010300000750D000001000000760D0000010000A9
++:10104000007D0D0000010000007E0D000001000089
++:1010500000850D000001000000860D000001000069
++:10106000008D0D0000010000008E0D000001000049
++:1010700000950D000001000000960D000001000029
++:10108000009D0D0000010000009E0D000001000009
++:1010900000A50D000001000000A60D0000010000E9
++:1010A00000AD0D000001000000AE0D0000010000C9
++:1010B00000B50D000001000000B60D0000010000A9
++:1010C00000BD0D000001000000BE0D000001000089
++:1010D00000C50D000001000000C60D000001000069
++:1010E00000CD0D000001000000CE0D000001000049
++:1010F00000D50D000002000000D60D000001000028
++:1011000000DD0D000002000000DE0D000001000007
++:1011100000E50D000002000000E60D0000010000E7
++:1011200000ED0D000002000000EE0D0000010000C7
++:1011300000F50D000002000000F60D0000010000A7
++:1011400000FD0D000002000000FE0D000001000087
++:1011500000050E000002000000060E000001000065
++:10116000000D0E0000020000000E0E000001000045
++:1011700000150E000001000000160E000001000026
++:10118000001D0E0000010000001E0E000001000006
++:1011900000250E000001000000260E0000010000E6
++:1011A000002D0E0000010000002E0E0000010000C6
++:1011B00000350E000001000000360E0000020000A5
++:1011C000003D0E0000010000003E0E000002000085
++:1011D00000450E000001000000460E000002000065
++:1011E000004D0E0000010000004E0E000002000045
++:1011F00000550E000001000000560E000002000025
++:10120000005D0E0000010000005E0E000002000004
++:1012100000650E000001000000660E0000020000E4
++:10122000006D0E0000010000006E0E0000020000C4
++:1012300000750E000001000000760E0000010000A5
++:10124000007D0E0000010000007E0E000001000085
++:1012500000850E000001000000860E000001000065
++:10126000008D0E0000010000008E0E000001000045
++:1012700000950E000002000000960E000001000024
++:10128000009D0E0000020000009E0E000001000004
++:1012900000A50E000002000000A60E0000010000E4
++:1012A00000AD0E000002000000AE0E0000010000C4
++:1012B00000B50E000002000000B60E0000020000A3
++:1012C00000BD0E000002000000BE0E000002000083
++:1012D00000C50E000002000000C60E000002000063
++:1012E00000CD0E000002000000CE0E000002000043
++:1012F00000D50E000001000000D60E000002000024
++:1013000000DD0E000001000000DE0E000002000003
++:1013100000E50E000001000000E60E0000020000E3
++:1013200000ED0E000001000000EE0E0000020000C3
++:1013300000F50E000001000000F60E0000010000A4
++:1013400000FD0E000001000000FE0E000001000084
++:1013500000050F000001000000060F000001000062
++:10136000000D0F0000010000000E0F000001000042
++:1013700000160F0000010000001E0F000001000019
++:1013800000260F0000010000002D0F0000110000DA
++:10139000002E0F000001000000350F0000010000CA
++:1013A000003E0F0000010000004E0F000011000081
++:1013B000008E0F00008168AC0FA60F000004000033
++:1013C00000C60F000011000000CE0F000001000059
++:1013D00000DE0F0000CF000000E60F0000CF00008D
++:1013E00000EE0F0000CF000000F60F00000100002B
++:1013F000004E100000010000005610000001000027
++:10140000005E1000000200000066100000010000F5
++:10141000006E1000000100000076100000020000C5
++:10142000007E100000010000008E1000000100008E
++:101430000096100000010000009E10000001000056
++:1014400000A610000001000000AE10000001000026
++:1014500000B610000001000000BE100000010000F6
++:1014600000C610000001000000CE100000110000B6
++:10147000000E1100008168AC0F161100000F000073
++:101480000056110000677E08006E11000011000078
++:101490000076110000010000007E11000001000034
++:1014A000009E11000004000000CE110000010000A9
++:1014B00000F611000011000000361200008168AC37
++:1014C0000F5512000002000000561200001100002B
++:1014D000005E1200000100000065120000677E0837
++:1014E000006E120000010000007E120000010000EA
++:1014F000008D120000010000008E120000FF0700A6
++:10150000009E12000001000000AE12000001000069
++:1015100000C612000001000000CD120000150000FE
++:1015200000DD12000001000000E5120000100000C4
++:1015300000FD12000001000000251300000400005F
++:10154000002D13000000040000351300000003000C
++:10155000003D1300000110000075130000020000A0
++:1015600000BD13000001000000C5130000100000C2
++:1015700000D5130000010000000E14000008000058
++:101580000016140000080000001E140000080000EF
++:101590000026140000080000002E140000080000BF
++:1015A0000036140000080000003E1400000800008F
++:1015B0000046140000080000004E14000011000056
++:1015C0000075140000100000008E1400008168AC4B
++:1015D0000F96140000000400009E14000000040098
++:1015E00000A614000000040000AE14000000040077
++:1015F00000B614000000040000BE14000000040047
++:1016000000C614000000040000CE14000000040016
++:1016100000D614000000030000DE140000000300E8
++:1016200000E614000000030000EE140000000300B8
++:1016300000F614000000030000FD1400000000800C
++:101640003FFE1400000003000005150000000080AC
++:101650003F06150000000300000D1500000000808B
++:101660003F0E15000000030000131500000F0000DE
++:1016700000151500000000803F1615000001000055
++:10168000001D1500000000803F1E1500000F000027
++:1016900000251500000000803F2D1500000000808F
++:1016A0003F331500000100000035150000000080E8
++:1016B0003F3D1500000000803F4515000000008000
++:1016C0003F4D1500000000803F55150000000080D0
++:1016D0003F5D1500000000803F5E15000020000007
++:1016E00000651500000000803F6615000011000035
++:1016F000006D1500000000803F6E15000000010025
++:1017000000751500000000803F7E150000010000FC
++:101710000096150000400000009E1500000001002A
++:1017200000AE15000003000000D615000001000007
++:1017300000DE150000677E0800FE150000020000B4
++:1017400000061600008168AC0F451600001000006E
++:1017500000551600003F0000005616000001000072
++:10176000007E160000040000008D1600000100003D
++:10177000008E160000010000009616000000040014
++:10178000009D160000010000009E160000000300EE
++:1017900000A616000001100000AD160000010000B8
++:1017A00000C516000001100000CE16000011000058
++:1017B000000E1700008168AC0F161700000F000024
++:1017C000002517000011000000651700000F000041
++:1017D00000A517000011000000C61700000100005E
++:1017E00000DD17000001000000E517000001000007
++:1017F00000E61700008168AC0FED17000001000043
++:1018000000EE170000677E0800F5170000020000D8
++:1018100000FD170000010000000518000002000094
++:10182000000D180000010000000E1800001100005B
++:10183000001D180000677E08002618000004000044
++:10184000002D1800008168AC0F3618000001000060
++:10185000003D180000010000003E180000010000DB
++:101860000045180000010000004D180000010000B4
++:101870000055180000010000005D18000001000084
++:10188000005E180000010000006518000001000063
++:101890000066180000010000006D18000001000043
++:1018A0000075180000010000007D18000001000014
++:1018B0000085180000010000008D180000010000E4
++:1018C000009518000001000000961800008168AC27
++:1018D0000F9D18000001000000A518000001000085
++:1018E00000AD18000001000000B518000001000064
++:1018F00000BD18000001000000BE18000004000038
++:1019000000C518000001000000CD18000001000013
++:1019100000CE18000001000000D5180000010000F2
++:1019200000D618000001000000DD180000010000D2
++:1019300000E518000001000000ED180000010000A3
++:1019400000EE18000001000000F518000001000082
++:1019500000FD180000020000000519000002000050
++:10196000000D19000002000000151900000200001F
++:10197000001D1900000200000025190000020000EF
++:10198000002D1900000200000035190000020000BF
++:10199000003D190000010000004519000001000091
++:1019A000004D190000010000004E19000001000068
++:1019B0000055190000010000005619000001000048
++:1019C000005D190000010000005E19000001000028
++:1019D0000065190000010000006619000001000008
++:1019E000006D190000010000006E190000010000E8
++:1019F00000751900000100000076190000010000C8
++:101A0000007D190000020000007E190000010000A6
++:101A10000085190000020000008619000001000086
++:101A2000008D190000020000008E19000011000056
++:101A30000095190000020000009D1900000200003E
++:101A400000A519000002000000AD1900000200000E
++:101A500000B519000002000000BD190000010000DF
++:101A600000C519000001000000CD190000010000B0
++:101A700000CE1900008168AC0FD5190000010000EC
++:101A800000D61900000F000000DD19000001000061
++:101A900000E519000001000000ED19000001000040
++:101AA00000F519000001000000151A0000010000F7
++:101AB00000161A0000110000001E1A0000010000AC
++:101AC00000431A00000F000000451A00000100004A
++:101AD00000561A0000010000008E1A0000DFBFE36C
++:101AE00004961A0000DFBFE3049E1A000001000004
++:101AF00000AE1A000000FFFF00B61A00000100004F
++:101B000000BE1A000001000000D61A000000FFFF0E
++:101B100000FD1A000001000000051B00000200008B
++:101B2000000D1B000001000000151B00000100005B
++:101B3000001D1B0000020000001E1B000001000031
++:101B400000251B0000010000002D1B00000100000B
++:101B5000002E1B000001000000361B0000001020BA
++:101B6000303E1B000040506070461B00008898A863
++:101B7000B84E1B0000C8D8E8F8551B000001000053
++:101B8000005D1B0000010000005E1B00001A000049
++:101B900000651B0000010000006D1B00000100003B
++:101BA000006E1B000004000000751B000001000017
++:101BB000007D1B000001000000851B0000010000EB
++:101BC000008D1B000001000000951B0000010000BB
++:101BD000009D1B000001000000A51B00000100008B
++:101BE00000AD1B000001000000B51B00000100005B
++:101BF00000BD1B000001000000C51B00000100002B
++:101C000000CD1B000001000000D51B0000020000F9
++:101C100000DD1B000002000000E51B0000020000C8
++:101C200000ED1B000002000000F51B000002000098
++:101C300000FD1B000002000000051C000002000067
++:101C4000000D1C000002000000151C000001000037
++:101C5000001D1C000001000000251C000001000008
++:101C6000002D1C000001000000351C0000010000D8
++:101C7000003D1C000001000000451C0000010000A8
++:101C8000004D1C000001000000551C000001000078
++:101C9000005D1C000001000000651C000001000048
++:101CA000006D1C000001000000751C000001000018
++:101CB000007D1C000001000000851C0000010000E8
++:101CC000008D1C000001000000951C0000020000B7
++:101CD00000961C0000040000009D1C000002000093
++:101CE000009E1C000004000000A51C000002000073
++:101CF00000A61C000000010203AD1C000002000051
++:101D000000B51C000002000000BD1C000002000025
++:101D100000C51C000002000000CD1C0000020000F5
++:101D200000CE1C000004000000D51C0000010000D3
++:101D300000DD1C000001000000E51C0000010000A7
++:101D400000E61C000004000000ED1C000001000083
++:101D500000EE1C000004000000F51C000001000063
++:101D600000F61C000080000000FD1C0000010000C7
++:101D700000FE1C000004000000051D000001000022
++:101D8000000D1D0000010000002D1D0000110000CD
++:101D900000351D000001000000261E0000040000A8
++:101DA000002E1E000080000000361E00000400000F
++:101DB000003E1E000000010203461E00000300005A
++:101DC000004E1E000004000000761E00000400000B
++:101DD000007E1E0000030000009E1E0000040000A4
++:101DE00000B01F000021000000B81F00000100002B
++:101DF00000C01F000002000000C81F00000001001A
++:101E000000D01F000000010000D81F0000010000EA
++:101E100000F01F000001000000F81F000002000099
++:101E20000000200000000100000820000000010068
++:101E3000001020000001000000463F0000040000E8
++:101E4000004E3F0000030000007E3F000001000044
++:101E5000008E3F000001000000AE3F0000882471AA
++:101E60002ABE3F000000C08540C63F000040000081
++:101E700000CE3F000000010000D63F00000001013D
++:101E800000DE3F0000000080025E40000001000014
++:101E900000C64000000F00000026410000040000C2
++:101EA000002E410000FFFF000036410000FFFF0050
++:101EB000003E410000FFFF000046410000FFFF0020
++:101EC000008E41000001000000AE41000001000052
++:101ED00000DE41000001000000F6410000030000A8
++:101EE0000056420000010000005E420000010000B8
++:101EF0000066420000020000006E42000001000087
++:101F00000076420000010000007E42000002000056
++:101F1000008E420000010000009642000001000017
++:101F2000009E42000001000000A6420000010000E7
++:101F300000AE42000001000000B6420000010000B7
++:101F400000BE42000001000000C642000001000087
++:101F500000CE42000001000000D642000001000057
++:101F600000DE42000001000000E642000001000027
++:101F700000EE42000001000000F6420000010000F7
++:101F800000FE4200000100000006430000010000C6
++:101F9000000E430000010000001643000001000095
++:101FA000001E430000010000002643000001000065
++:101FB000002E430000010000003643000001000035
++:101FC000003E430000010000004643000001000005
++:101FD000004E4300000200000056430000020000D3
++:101FE000005E4300000200000066430000020000A3
++:101FF000006E430000020000007643000002000073
++:10200000007E430000020000008643000002000042
++:10201000008E430000010000009643000001000014
++:10202000009E43000001000000A6430000010000E4
++:1020300000AE43000001000000B6430000010000B4
++:1020400000BE43000001000000C643000001000084
++:1020500000CE43000002000000D643000002000052
++:1020600000DE43000002000000E643000002000022
++:1020700000EE43000002000000F6430000020000F2
++:1020800000FE4300000200000006440000020000C1
++:10209000000E440000010000001644000001000092
++:1020A000001E440000010000002644000001000062
++:1020B000002E440000010000003644000001000032
++:1020C000003E440000010000004644000001000002
++:1020D00000564400000100000066440000110000AA
++:1020E00000A64400008168AC0FBE4400000400005C
++:1020F00000DE44000011000000E644000001000082
++:1021000000F6440000CF000000FE440000CF0000B5
++:102110000006450000CF0000000E45000001000051
++:102120000066450000010000006E4500000100004F
++:102130000076450000020000007E4500000100001E
++:102140000086450000010000008E450000020000EE
++:1021500000914500000400000096450000010000C9
++:10216000009945000004000000A6450000010000A1
++:1021700000A945000080000000AE450000010000FD
++:1021800000B145000004000000B645000001000059
++:1021900000B945000001000000BE4500000100003C
++:1021A00000C645000001000000C9450000270000EE
++:1021B00000CE45000001000000D6450000010000EF
++:1021C00000D945000026000000DE450000010000A7
++:1021D00000E645000011000000264600008168ACC2
++:1021E0000F2E4600000F0000006E460000677E08BC
++:1021F0000086460000110000008E4600000100002D
++:10220000009646000001000000B6460000040000F1
++:1022100000E6460000010000000E4700001100002B
++:10222000004E4700008168AC0F6E470000110000AF
++:102230000076470000010000008647000001000012
++:10224000009647000001000000A6470000FF0700BD
++:1022500000B647000001000000C647000001000072
++:1022600000DE4700000100000026490000080000D1
++:10227000002E490000080000003649000008000058
++:10228000003E490000080000004649000008000028
++:10229000004E4900000800000056490000080000F8
++:1022A000005E4900000800000066490000110000BF
++:1022B00000A64900008168AC0FAE49000000040090
++:1022C00000B649000000040000BE49000000040000
++:1022D00000C649000000040000CE490000000400D0
++:1022E00000D649000000040000DE490000000400A0
++:1022F00000E649000000040000EE49000000030071
++:1023000000F649000000030000FE49000000030041
++:1023100000064A0000000300000E4A00000003000F
++:1023200000164A0000000300001E4A0000000300DF
++:1023300000264A0000000300002E4A0000010000B1
++:1023400000364A00000F000000764A00002000001E
++:10235000007E4A000011000000864A0000000100D3
++:1023600000964A000001000000AE4A000040000054
++:1023700000B64A000000010000C64A000003000049
++:1023800000EE4A000001000000F64A0000677E08E7
++:1023900000164B0000020000001E4B00008168ACDC
++:1023A0000F6E4B000001000000964B00000400007F
++:1023B00000A64B000001000000AE4B00000004002E
++:1023C00000B64B000000030000BE4B0000011000EF
++:1023D00000E64B000011000000264C00008168ACB4
++:1023E0000F2E4C00000F000000DE4C00000100002A
++:1023F00000FE4C00008168AC0F064D0000677E08AF
++:1024000000264D0000110000003E4D0000040000B9
++:10241000004E4D000001000000564D00000100007C
++:1024200000764D0000010000007E4D00000100001C
++:1024300000AE4D00008168AC0FD64D0000040000D6
++:1024400000E64D000001000000EE4D00000100001C
++:1024500000064E000001000000664E000001000072
++:10246000006E4E000001000000764E0000010000EA
++:10247000007E4E000001000000864E0000010000BA
++:10248000008E4E000001000000964E00000100008A
++:10249000009E4E000001000000A64E00001100004A
++:1024A00000E64E00008168AC0FEE4E00000F000009
++:1024B000002E4F000011000000364F000001000008
++:1024C000006E4F000001000000A64F0000DFBFE3D8
++:1024D00004AE4F0000DFBFE304B64F000001000070
++:1024E00000C64F000000FFFF00CE4F0000010000BB
++:1024F00000D64F000001000000EE4F000000FFFF7B
++:1025000000365000000100000046500000010000AD
++:10251000004E500000001020305650000040506027
++:10252000705E5000008898A8B866500000C8D8E8CF
++:10253000F8765000001A00000086500000040000E9
++:1025400000AE51000004000000B65100000400007D
++:1025500000BE51000000010203E65100000400002B
++:1025600000FE5100000400000006520000040000BC
++:10257000000E52000080000000165200000400000F
++:10258000003E53000004000000465300008000009D
++:10259000004E5300000400000056530000000102EA
++:1025A000035E5300000300000066530000040000B7
++:1025B000008E53000004000000965300000300004A
++:1025C00000B65300000400000014570000FFFF3F56
++:1025D000002C570000FF1F00001B6000000F0000D0
++:1025E00000B86A000000000004C06A00000000009B
++:1025F00004C86A000000000004D06A000000000067
++:1026000004D86A000000000004E06A000000000036
++:1026100004E86A000000000004F06A000000000006
++:1026200004F86A000000000004006B0000000000D5
++:1026300004086B000000000004106B0000000000A4
++:1026400004186B000000000004206B000000000074
++:1026500004286B000000000004306B000000000044
++:1026600004586C0000DFBFE304606C0000DFBFE3D0
++:1026700004786C000021FE0100806C00008168ACD1
++:102680000F886C000001000000C86C0000120C10E4
++:1026900008F06C000000010000086D00000100015E
++:1026A00000186D000001000100206D000001000015
++:1026B00000286D000001000100306D0000010000E5
++:1026C00000386D000004000000406D0000020000B2
++:1026D00000107200000000803F48720000040000FB
++:1026E00000507200001A0000006872000001000033
++:1026F000000073000000FFFF00387300000F0000AF
++:1027000000787300008168AC0F8073000011000036
++:1027100000007400000400000018740000030000B2
++:102720000030740000020000003874000000000057
++:102730000440740000000000045E74000004000007
++:1027400000607400000500000066740000030000D3
++:102750000068740000520000009074000001000046
++:1027600000207500000000803F28750000000080F8
++:102770003F307500000000803F3875000000008089
++:102780003F407500000000803F4875000000008059
++:102790003F507500000000803F5875000000008029
++:1027A0003F607500000000803F68750000000080F9
++:1027B0003F707500000000803F78750000000080C9
++:1027C0003F807500000000803F8875000000008099
++:1027D0003F907500000000803F9875000000008069
++:1027E0003FA075000010000000D8760000120C1009
++:1027F00008E076000005000000F876000001000007
++:102800000008770000FFFF000010770000FFFF00C6
++:102810000018770000FFFF000020770000FFFF0096
++:102820000028770000030000009077000000FFFF01
++:1028300000987700001A000000A87700000300004D
++:10284000009079000002010000A07900000400005F
++:1028500000A879000004000000B079000004000026
++:1028600000B879000004000000007A0000040000B5
++:1028700000087A000004000000187A0000FF07003A
++:1028800000287A000002010000787A0000040000AD
++:1028900000807A000004000000887A000004000034
++:1028A00000907A0000040000006AE70000040000C5
++:1028B0000072E70000040000007AE70000120C102C
++:1028C0000882E700000300000092E70000120C10ED
++:1028D00008A2E70000140C0800AAE70000010000AD
++:1028E00000B2E7000004000000BAE70000040000A6
++:1028F00000C2E70000140C0800DAE70000120C1018
++:1029000008E2E7000027000000FAE70000010000ED
++:10291000000A06010001000000C2060100120C10AE
++:102920000800070100120C100810070100800000C9
++:102930000018070100047000802007010000040057
++:102940000428070100C0000000300701000010004B
++:1029500000480701000100000060070100010000BD
++:102960000070070100040000007807010002000069
++:1029700000E807010080000000F00701000470007B
++:1029800080F80701000004000400080100C00000F6
++:1029900000080801000010000020080100010000EC
++:1029A0000038080100010000004808010004000090
++:1029B000005008010002000000C008010080000073
++:1029C00000C808010004700080D008010000040065
++:1029D00004D8080100C0000000E008010000100059
++:1029E00000F80801000100000010090100010000CA
++:1029F0000020090100040000002809010002000075
++:102A00000090090100120C1008A009010021FE012C
++:102A100000D0090100FFFF0000D8090100FFFF00FE
++:102A200000E0090100FFFF0000E8090100FFFF00CE
++:102A300000F009010001000000F809010001000197
++:102A400000000A010001000100080A010001000065
++:102A500000180A010021FE0100280A0100010000FF
++:102A600000500A0100120C1008580A01000400006E
++:102A700000680A010002000000700A010011000055
++:102A800000B80A01008168AC0FC80A010003000009
++:102A900000E80A010004000000380B0100020000F9
++:102AA00000400B010001000000480B010001000084
++:102AB00000500B010002000000580B010001000053
++:102AC00000600B010001000000680B010001000024
++:102AD00000780B010002000000800B0100020000E2
++:102AE00000880B010002000000900B0100020000B2
++:102AF00000980B010002000000A00B010002000082
++:102B000000A80B010002000000B00B010002000051
++:102B100000B80B010001000000C00B010001000023
++:102B200000C80B010001000000D00B0100010000F3
++:102B300000D80B010001000000E00B0100010000C3
++:102B400000E80B010001000000F00B010001000093
++:102B500000F80B010001000000000C010001000062
++:102B600000080C010001000000100C010001000031
++:102B700000180C010001000000200C010001000001
++:102B800000280C010001000000300C0100010000D1
++:102B900000380C010002000000400C01000200009F
++:102BA00000480C010002000000500C01000200006F
++:102BB00000580C010002000000600C01000200003F
++:102BC00000680C010002000000700C01000200000F
++:102BD00000780C010001000000800C0100010000E1
++:102BE00000880C010001000000900C0100010000B1
++:102BF00000980C010001000000A00C010001000081
++:102C000000A80C010001000000B00C010001000050
++:102C100000B80C010001000000C00C010001000020
++:102C200000C80C010001000000D00C0100010000F0
++:102C300000D80C010001000000E00C0100010000C0
++:102C400000E80C010001000000F00C010001000090
++:102C500000F80C010001000000000D01000100005F
++:102C600000080D010001000000100D01000100002E
++:102C700000180D010001000000200D0100010000FE
++:102C800000280D010001000000300D0100010000CE
++:102C900000500D010004000000702A010011000026
++:102CA00000802A010001000000F82B0100800000D4
++:102CB00000002C010004700080082C0100000400BA
++:102CC00004102C0100C0000000182C0100001000AE
++:102CD00000302C010001000000482C010001000020
++:102CE00000582C010004000000602C0100020000CC
++:102CF00000D02C010080000000D82C0100047000DE
++:102D000080E02C010000040004E82C0100C0000059
++:102D100000F02C010000100000082D01000100004F
++:102D200000202D010001000000302D0100040000F2
++:102D300000382D010002000000A82D0100800000D5
++:102D400000B02D010004700080B82D0100000400C7
++:102D500004C02D0100C0000000C82D0100001000BB
++:102D600000E02D010001000000F82D01000100002D
++:102D700000082E010004000000102E0100020000D7
++:102D800000782E0100120C1008882E010021FE018F
++:102D900000B82E0100FFFF0000C02E0100FFFF0061
++:102DA00000C82E0100FFFF0000D02E0100FFFF0031
++:102DB00000D82E010001000000E02E0100010001FA
++:102DC00000E82E010001000100F02E0100010000CA
++:102DD00000002F010021FE0100102F010001000062
++:102DE00000382F0100120C1008402F0100040000D1
++:102DF00000502F010002000000582F0100110000B8
++:102E000000A02F01008168AC0FB02F01000300006B
++:102E100000D02F010004000000203001000200005B
++:102E200000283001000100000030300100010000E6
++:102E300000383001000200000040300100010000B5
++:102E40000048300100010000005030010001000086
++:102E50000060300100020000006830010002000044
++:102E60000070300100020000007830010002000014
++:102E700000803001000200000088300100020000E4
++:102E800000903001000200000098300100020000B4
++:102E900000A030010001000000A830010001000086
++:102EA00000B030010001000000B830010001000056
++:102EB00000C030010001000000C830010001000026
++:102EC00000D030010001000000D8300100010000F6
++:102ED00000E030010001000000E8300100010000C6
++:102EE00000F030010001000000F830010001000096
++:102EF0000000310100010000000831010001000064
++:102F00000010310100010000001831010001000033
++:102F10000020310100020000002831010002000001
++:102F200000303101000200000038310100020000D1
++:102F300000403101000200000048310100020000A1
++:102F40000050310100020000005831010002000071
++:102F50000060310100010000006831010001000043
++:102F60000070310100010000007831010001000013
++:102F700000803101000100000088310100010000E3
++:102F800000903101000100000098310100010000B3
++:102F900000A031010001000000A831010001000083
++:102FA00000B031010001000000B831010001000053
++:102FB00000C031010001000000C831010001000023
++:102FC00000D031010001000000D8310100010000F3
++:102FD00000E031010001000000E8310100010000C3
++:102FE00000F031010001000000F831010001000093
++:102FF0000000320100010000000832010001000061
++:103000000010320100010000001832010001000030
++:10301000003832010004000000584F010011000088
++:0930200000684F010001000000EE
++:00000001FF
+diff --git a/firmware/nouveau/nva8.ctxprog.ihex b/firmware/nouveau/nva8.ctxprog.ihex
+new file mode 100644
+index 0000000..59b32f6
+--- /dev/null
++++ b/firmware/nouveau/nva8.ctxprog.ihex
+@@ -0,0 +1,75 @@
++:100000004E5643500026019C00700000003000094D
++:10001000F54400092D400051E94000440A40000524
++:100020000A40000D0A40008E0070004D1240009DF5
++:100030000070004D0445009700700021054500A1A7
++:100040004744004D7744004D8344001D0070000676
++:1000500018400005006000454544008B30440045D1
++:100060001840004D23400081007000CF1C40009FCD
++:100070000070009F0050004DE04400170070000B1E
++:100080002340004D7E440021064500A1574400A0B6
++:10009000007000010070000300700006274000059A
++:1000A0002840000D006000050070000D0070000683
++:1000B000007000020070000B0070000E0070001C49
++:1000C0000070000C00600000000000FFFF9000FFC7
++:1000D000FF91004DD84400090060004D0048001910
++:1000E00000480096007000CF3B40009F0070009FCA
++:1000F00000500051E94000C0364000800020000858
++:100100000060004F364000C0364000CC3F400051F8
++:10011000364000160070004D004800110060004D90
++:100120000048004D3644008E007000810070004D84
++:100130007144004D7E440083007000000030008058
++:100140002A210007006000010AC0002200200001EF
++:10015000008000CB005000FF00C0004D5F44004D08
++:1001600000480008D244004D7444004D8C44004DBA
++:100170005F44004DE644004D7744004D8344004DFC
++:10018000004800830070004D0745003F02A000407A
++:10019000002000060060004D0045004D8E4400D94F
++:1001A00001200060BB440080022000FFFF38004DAA
++:1001B000D0440000003000CB0050004D1F4500CB64
++:1001C00000500007D444004D0048004D7A4400FC24
++:1001D0001B11004D0048004D7A4400FD1B11004DDD
++:1001E0000048004D7A4400FE1B11004D00480000FD
++:1001F00000200000007000060060004D0048000173
++:10020000002000060060004D0045000A0211004D6C
++:1002100000480000003000FFFFC30000002000077E
++:100220000060000000700008002000FF008000CB8C
++:100230000050004D004800000000004D0048000044
++:1002400000000002021700320020000D02100042E0
++:10025000021E00C0021100C4021100C802110002F7
++:1002600003120002041500000518000905130050D0
++:1002700005150005061100130020000706100000F8
++:100280000711000009110002091200000A11000202
++:100290000B1600280B12002B0B1400010C1100018F
++:1002A0000D1100DA002000001210000014110005EA
++:1002B00014110007141100091411000B141100058A
++:1002C0001A14000C1A1300001C1300041C13002045
++:1002D0001C1400251C1400001F1300041F13000829
++:1002E0001F11000B1F110015002000401F10004DB2
++:1002F000004800060060004D1F450020201100222C
++:100300002011004000200040201000C0201500C82F
++:10031000201100CA201400CF201D0000211200036C
++:10032000211200402116004721120053211200A083
++:10033000211100C0211200CB211100D4211100D8BD
++:100340002115004D004800000000004D0048000B42
++:100350000060004D0048000A0060004D0048000B9E
++:100360000060004DD6400020002000080060004CD6
++:100370000050004D004800E8032000080060004CD9
++:100380000050004D004800040060004A0050004D3D
++:10039000004800FF00C000FF00C8004D004800FFFB
++:1003A00000C000FF00C8004D004800160070008E1D
++:1003B00000700082007000410050004DDC44009548
++:1003C000007000D100500016006000520050000282
++:1003D000007000150070004D2840008E0070004D28
++:1003E000D84400000020000700600000003000FF3B
++:1003F00000C00000002000FF008000090070000E17
++:100400000070004D00480080007000170048000098
++:100410000070004D0048004D0048004D0048004D60
++:100420000048008E0070004DD84400830070004DDD
++:10043000E344004D1045000F0070008C0F4100CBCD
++:100440000050004D00480080022000070060008737
++:100450001445004D00480000000000722020004DAF
++:10046000004500FF0080004D0048004006210007C5
++:10047000006000870420004D004500FF0088004D0B
++:100480000048000F0048004B0048004D1945008F00
++:0F0490000070008C004800CB0050004D00480069
++:00000001FF
+diff --git a/firmware/nouveau/nva8.ctxvals.ihex b/firmware/nouveau/nva8.ctxvals.ihex
+new file mode 100644
+index 0000000..da03cb4
+--- /dev/null
++++ b/firmware/nouveau/nva8.ctxvals.ihex
+@@ -0,0 +1,475 @@
++:100000004E56435600B1030000430000003000008C
++:10001000004B000000020000005F00000000100024
++:1000200000740000000300000075000000001000D4
++:1000300000880000009D620100890000009D6201AF
++:10004000008A0000000CFE0000900000000040004C
++:10005000009B00000087010000A0000000181000B5
++:1000600000A1000000FF000000B400000004000038
++:1000700000B5000000DF002514B7000000000600F6
++:1000800000BD00000000000001BE000000FF0000F5
++:1000900000C000000000040000C6000000010000D5
++:1000A00000C700000001000000CB000000020000BB
++:1000B00000CC00000001000000D0000000010000A2
++:1000C00000D100000000010000D700000002000085
++:1000D00000D800000001000000D90000000100006D
++:1000E00000DD00000001000000DE000000FFFF3F17
++:1000F00000DF000000FF1F0000E100000001000021
++:1001000000E200000001000000E400000001000027
++:1001100000E500000001000000E600000001000012
++:1001200000E700000004000000E8000000010000FB
++:1001300000EA00000001000000EB000000010000E8
++:1001400000EC00000007000000ED000000010000CE
++:1001500000EE00000007000000EF000000010000BA
++:1001600000F000000001000000F1000000010000AC
++:1001700000F600000001000000F700000000010090
++:1001800000F900000001000000FC00000000010078
++:1001900000FD00000001000000FE00000000010062
++:1001A0000000010000010000000301000000010048
++:1001B00000080100000400000009010000700000B8
++:1001C000000A01000080000000100100000C000087
++:1001D00000120100000800000013010000140000DC
++:1001E0000015010000290000001601000027000092
++:1001F00000170100002600000018010000080000A0
++:100200000019010000040000001A0100002700008E
++:10021000001D010000010000001E0100000200009E
++:10022000001F010000030000002001000004000086
++:10023000002101000005000000220100000600006E
++:10024000002301000007000000240100000100005D
++:100250000035010000CF00000041010000800000D7
++:1002600000420100000400000043010000040000FF
++:1002700000440100000300000046010000030000EC
++:100280000047010000010000004A010000120000C8
++:10029000004B010000100000004C0100000C0000A9
++:1002A000004D0100000100000051010000040000A9
++:1002B0000052010000020000005301000004000091
++:1002C0000057010000FFFF3F0058010000FF1F0022
++:1002D0000062010000010000006301000001000055
++:1002E000006401000002000000660100000400003C
++:1002F0000067010000140000006801000001000018
++:10030000006C01000002000000710100000100000B
++:1003100000730100000200000074010000001000E2
++:100320000075010000000E000076010000001000C2
++:100330000077010000001E000079010000010000AC
++:10034000007A010000010000007B010000010000B4
++:10035000007C010000010000007D010000010000A0
++:100360000081010000000200008201000000020084
++:1003700000840100000100000085010000F0000081
++:100380000086010000FF000000890100000100005C
++:10039000008A010000F00000008B010000FF000057
++:1003A000008D010000090000009001000001000024
++:1003B0000091010000CF0000009301000001000047
++:1003C0000097010000CF000000990100000200002A
++:1003D000009B010000010000009D010000010000E1
++:1003E000009F010000CF000000A0010000CF00002E
++:1003F00000A101000001000000A201000080000E29
++:1004000000A301000004000000A8010000801F00FC
++:1004100000A901000030000000AE01000021F874C6
++:100420003BAF01000001800589B101000000100010
++:1004300000B2010000FF000000B4010000FA107CCF
++:1004400002B5010000C0000040B601000080208914
++:10045000B7BB01000001000000BC01000040003DEE
++:1004600000BE01000022000000C601000040003D67
++:1004700000C701000022000000D90100000AFF00AF
++:1004800000DB01000000008001DC0100000000161C
++:1004900000DD01000000008001E1010000FFFF031A
++:1004A00000E201000000000C30EB0100000104013B
++:1004B00000ED01000078000000EF010000BF000027
++:1004C00000F101000010120000F2010000800000A5
++:1004D00008F50100003E000000FB01000000008064
++:1004E00001FC01000000001600FD0100000000807A
++:1004F0000101020000FFFF03000202000000000CE7
++:10050000300B020000010401000D02000078000021
++:10051000000F020000BF00000011020000101200D6
++:10052000001202000080000008150200003E0000DA
++:10053000001C0200007070120520020000FFFFFF87
++:10054000072702000007041200280200000715090F
++:100550000529020000020201052A0200000102032F
++:10056000003302000040000000340200000A0B0CBF
++:100570000D350200001012140036020000F00100D8
++:1005800000370200000100000038020000030000F4
++:10059000003B020000009E03003C0200000001003E
++:1005A000003D020000003800003E02000007E03F6E
++:1005B000003F02000000E03F0040020000404040D9
++:1005C000004102000007F0FF6C42020000FF7FBF05
++:1005D000028102000004000000860200003F0000CB
++:1005E0000089020000040000008B020000010000EE
++:1005F000008D0200008168AC0F99020000800000AD
++:1006000000A102000004000000A9020000140C0870
++:1006100000AF020000140C0800B30200000F00003D
++:1006200000B9020000FF070000C702000004080034
++:1006300000D702000004000000DE020000020000FB
++:1006400000DF02000004000000E6020000000000DD
++:1006500004E7020000120C1008ED020000DFBFE307
++:1006600004EE02000000000004F5020000DFBFE31A
++:1006700004F702000004000000FB0200002000005C
++:1006800000FF020000040000000F03000010000043
++:1006900000150300001100000025030000DFBFE388
++:1006A000042D030000DFBFE3043603000004000054
++:1006B0000037030000040800003F030000010000B1
++:1006C000004503000011000000470300001A00006D
++:1006D000004F0300007F00000056030000040000EC
++:1006E000005F0300000100000067030000140C0815
++:1006F0000077030000120C10087F030000040000C4
++:100700000087030000040000008B0300001A0000B3
++:100710000095030000020000009703000010000095
++:1007200000A5030000677E0800B703000001000079
++:1007300000BF030000120C1008CD030000010000F0
++:1007400000E603000001000000EE030000011000BD
++:1007500000F6030000FFFF0000F7030000FF0700A2
++:1007600000FB03000004000000FE030000FFFF0088
++:1007700000FF030000140C08000304000004000044
++:100780000006040000FFFF00000D0400001500003B
++:10079000000E040000FFFF0000130400000400002E
++:1007A000001B040000080000001D04000001000000
++:1007B0000025040000100000002B040000FF0700CB
++:1007C00000320400000400000033040000001E009A
++:1007D000003A040000040000003D04000001000095
++:1007E0000065040000040000006D04000000040027
++:1007F0000075040000000300007D040000011000EB
++:10080000009B0400000F000000B50400000200007F
++:1008100000FD0400000100000005050000100000BC
++:100820000015050000010000001605000000008012
++:100830003F1E0500000000803F26050000000080EC
++:100840003F2E0500000000803F36050000000080BC
++:100850003F3E0500000000803F460500000000808C
++:100860003F4E0500000000803F560500000000805C
++:100870003F5E0500000000803F660500000000802C
++:100880003F6E0500000000803F76050000000080FC
++:100890003F7E0500000000803F86050000000080CC
++:1008A0003F8E0500000000803F960500001000000C
++:1008B00000AE05000003000000B5050000100000B8
++:1008C00000C705000001000000DF05000010000067
++:1008D000001E0600000000000426060000000000C4
++:1008E0000436060000800000003D06000000008085
++:1008F0003F450600000000803F4D060000000080DC
++:100900003F550600000000803F56060000800000B2
++:10091000005D0600000000803F65060000000080CA
++:100920003F6D0600000000803F750600000000805B
++:100930003F7D0600000000803F850600000000802B
++:100940003F8D0600000000803F8E06000001000081
++:1009500000950600000000803F96060000000100A0
++:10096000009D0600000000803F9E06000000010080
++:1009700000A50600000000803FA606000011000050
++:1009800000AD0600000000803FB5060000000080BA
++:100990003FB606000008000000E606000001000067
++:1009A00000F606000001000000FE06000001000045
++:1009B0000006070000010000000E070000CF000045
++:1009C0000016070000020000004E070000010000B2
++:1009D000005E070000010000006607000001000043
++:1009E000006E0700000100000085070000100000F5
++:1009F00000950700003F000000960700000400007B
++:100A000000A607000001000000A707000088000002
++:100A100000AE07000015000000AF070000880000CE
++:100A200000C707000004000000CD0700000100001F
++:100A300000CE07000080444404DD070000010000F0
++:100A400000ED070000010000000508000001100093
++:100A50000065080000110000007F0800002600006B
++:100A600000970800000000803FA50800000F00006C
++:100A700000BF0800001A000000C7080000100000B6
++:100A800000E5080000110000001D09000001000041
++:100A90000025090000010000002D090000010000F0
++:100AA0000035090000020000003D090000010000BF
++:100AB0000045090000020000004D0900000100008F
++:100AC000005D090000677E08006D0900008168ACC8
++:100AD0000F7D0900000100000085090000010000F1
++:100AE000008D0900000100000095090000010000D0
++:100AF000009D09000001000000A5090000010000A0
++:100B000000A609000001000000AD0900000100007E
++:100B100000B509000001000000B609000001000056
++:100B200000BD09000001000000C50900000100002F
++:100B300000CD09000001000000D5090000010000FF
++:100B400000D60900008824712ADD09000001000098
++:100B500000E509000001000000E609000000C08572
++:100B600040ED09000001000000EE09000040000017
++:100B700000F509000001000000F609000000010076
++:100B800000FD09000001000000FE09000000010155
++:100B900000050A000001000000060A0000000080B5
++:100BA000020D0A0000010000000F0A0000520000C0
++:100BB00000150A0000010000001D0A0000010000ED
++:100BC000001F0A000026000000250A0000010000A6
++:100BD000002D0A0000010000002F0A0000040000A0
++:100BE00000350A000001000000370A000004000080
++:100BF000003D0A000002000000450A00000200005B
++:100C000000470A00001A0000004D0A000002000020
++:100C100000550A0000020000005D0A00000200000A
++:100C2000005F0A000000FFFF00650A0000020000EC
++:100C3000006D0A000002000000750A0000020000BA
++:100C4000007D0A000001000000850A00000100008C
++:100C500000860A0000010000008D0A00000100006B
++:100C600000950A0000010000009D0A00000100003C
++:100C700000A50A000001000000AD0A00000100000C
++:100C800000B50A000001000000BD0A0000020000DB
++:100C900000C50A000002000000CD0A0000020000AA
++:100CA00000D50A000002000000DD0A00000200007A
++:100CB00000E50A000002000000ED0A00000200004A
++:100CC00000EE0A00000F000000F50A00000200001C
++:100CD00000FD0A000001000000050B0000010000FB
++:100CE000000D0B000001000000150B0000010000CA
++:100CF000001D0B000001000000250B00000100009A
++:100D0000002D0B000001000000350B000001000069
++:100D1000004E0B000004000000550B000001000015
++:100D200000560B0000FFFF00005E0B0000FFFF00FD
++:100D300000660B0000FFFF00006E0B0000FFFF00CD
++:100D400000850B000001000000B60B000001000050
++:100D500000D60B000001000000060C00000100009E
++:100D6000001E0C0000030000003D0C00000100000C
++:100D700000450C0000020000004D0C0000010000C6
++:100D800000550C0000010000005D0C000002000096
++:100D900000650C0000010000006D0C000001000067
++:100DA000007E0C000001000000860C000001000025
++:100DB000008E0C000002000000950C0000010000F5
++:100DC00000960C0000010000009D0C0000010000D6
++:100DD000009E0C000001000000A50C0000010000B6
++:100DE00000A60C000002000000AD0C000001000095
++:100DF00000B50C000001000000B60C00000100006E
++:100E000000BD0C000001000000BE0C00000100004D
++:100E100000C50C000001000000C60C00000100002D
++:100E200000CD0C000001000000CE0C00000100000D
++:100E300000D50C000001000000D60C0000010000ED
++:100E400000DD0C000001000000DE0C0000010000CD
++:100E500000E50C000001000000E60C0000010000AD
++:100E600000ED0C000001000000EE0C00000100008D
++:100E700000F50C000001000000F60C00000100006D
++:100E800000FD0C000001000000FE0C00000100004D
++:100E900000050D000001000000060D00000100002B
++:100EA000000D0D0000010000000E0D00000100000B
++:100EB00000150D000002000000160D0000010000EA
++:100EC000001D0D0000020000001E0D0000010000CA
++:100ED00000250D000002000000260D0000010000AA
++:100EE000002D0D0000020000002E0D00000100008A
++:100EF00000350D000002000000360D00000100006A
++:100F0000003D0D0000020000003E0D000001000049
++:100F100000450D000002000000460D000001000029
++:100F2000004D0D0000020000004E0D000001000009
++:100F300000550D000001000000560D0000010000EA
++:100F4000005D0D0000010000005E0D0000010000CA
++:100F500000650D000001000000660D0000010000AA
++:100F6000006D0D0000010000006E0D00000100008A
++:100F700000750D000001000000760D000002000069
++:100F8000007D0D0000010000007E0D000002000049
++:100F900000850D000001000000860D000002000029
++:100FA000008D0D0000010000008E0D000002000009
++:100FB00000950D000001000000960D0000020000E9
++:100FC000009D0D0000010000009E0D0000020000C9
++:100FD00000A50D000001000000A60D0000020000A9
++:100FE00000AD0D000001000000AE0D000002000089
++:100FF00000B50D000001000000B60D00000100006A
++:1010000000BD0D000001000000BE0D000001000049
++:1010100000C50D000001000000C60D000001000029
++:1010200000CD0D000001000000CE0D000001000009
++:1010300000D50D000002000000D60D0000010000E8
++:1010400000DD0D000002000000DE0D0000010000C8
++:1010500000E50D000002000000E60D0000010000A8
++:1010600000ED0D000002000000EE0D000001000088
++:1010700000F50D000002000000F60D000002000067
++:1010800000FD0D000002000000FE0D000002000047
++:1010900000050E000002000000060E000002000025
++:1010A000000D0E0000020000000E0E000002000005
++:1010B00000150E000001000000160E0000020000E6
++:1010C000001D0E0000010000001E0E0000020000C6
++:1010D00000250E000001000000260E0000020000A6
++:1010E000002D0E0000010000002E0E000002000086
++:1010F00000350E000001000000360E000001000067
++:10110000003D0E0000010000003E0E000001000046
++:1011100000450E000001000000460E000001000026
++:10112000004D0E0000010000004E0E000001000006
++:1011300000560E0000010000005E0E0000010000DD
++:1011400000660E0000010000006D0E00001100009E
++:10115000006E0E000001000000750E00000100008E
++:10116000007E0E0000010000008E0E000011000045
++:1011700000CE0E00008168AC0FE60E0000040000F7
++:1011800000060F0000110000000E0F00000100001B
++:10119000001E0F0000CF000000260F0000CF00004F
++:1011A000002E0F0000CF000000360F0000010000ED
++:1011B000008E0F000001000000960F0000010000EB
++:1011C000009E0F000002000000A60F0000010000BA
++:1011D00000AE0F000001000000B60F00000200008A
++:1011E00000BE0F000001000000CE0F000001000053
++:1011F00000D60F000001000000DE0F00000100001B
++:1012000000E60F000001000000EE0F0000010000EA
++:1012100000F60F000001000000FE0F0000010000BA
++:101220000006100000010000000E10000011000078
++:10123000004E1000008168AC0F561000000F000037
++:101240000096100000677E0800AE1000001100003C
++:1012500000B610000001000000BE100000010000F8
++:1012600000DE100000040000000E1100000100006C
++:10127000003611000011000000761100008168ACFA
++:101280000F96110000110000009E110000010000E7
++:1012900000AE11000001000000BE110000010000BE
++:1012A00000CE110000FF070000DE11000001000069
++:1012B00000EE110000010000000612000001000015
++:1012C000004E130000080000005613000008000044
++:1012D000005E130000080000006613000008000014
++:1012E000006E1300000800000076130000080000E4
++:1012F000007E1300000800000086130000080000B4
++:10130000008E13000011000000CE1300008168ACB5
++:101310000FD613000000040000DE130000000400DC
++:1013200000E613000000040000EE130000000400BB
++:1013300000F613000000040000FE1300000004008B
++:101340000006140000000400000E14000000040059
++:101350000016140000000300001E1400000003002B
++:101360000026140000000300002E140000000300FB
++:101370000036140000000300003E140000000300CB
++:101380000046140000000300004E1400000003009B
++:101390000056140000010000005B1400000F000064
++:1013A000005E1400000F0000007B1400000100002C
++:1013B000009E14000020000000A614000011000090
++:1013C00000AE14000000010000BE14000001000087
++:1013D00000D614000040000000DE140000000100F0
++:1013E00000EE1400000300000016150000010000CC
++:1013F000001E150000677E08003E15000002000078
++:1014000000461500008168AC0F9615000001000031
++:1014100000BE15000004000000CE15000001000011
++:1014200000D615000000040000DE150000000300D7
++:1014300000E6150000011000000E1600001100006B
++:10144000004E1600008168AC0F561600000F000019
++:10145000000617000001000000261700008168AC9C
++:101460000F2E170000677E08004E170000110000C5
++:10147000006617000004000000761700000100005D
++:10148000007E170000010000009E17000001000010
++:1014900000A617000001000000D61700008168AC0C
++:1014A0000FFE170000040000000E180000010000ED
++:1014B0000016180000010000002E180000010000B6
++:1014C000008E1800000100000096180000010000C6
++:1014D000009E18000001000000A618000001000096
++:1014E00000AE18000001000000B618000001000066
++:1014F00000BE18000001000000C618000001000036
++:1015000000CE180000110000000E1900008168AC28
++:101510000F161900000F00000056190000110000FE
++:10152000005E190000010000008B1900000F000090
++:10153000009619000001000000CE190000DFBFE393
++:1015400004D6190000DFBFE304DE1900000100002B
++:1015500000EE19000000FFFF00F619000001000076
++:1015600000FE19000001000000161A000000FFFF35
++:10157000005E1A0000010000006E1A000001000069
++:1015800000761A0000001020307E1A0000405060E3
++:1015900070861A00008898A8B88E1A0000C8D8E88B
++:1015A000F89E1A00001A000000AE1A0000040000A5
++:1015B00000D61B000004000000DE1B000004000039
++:1015C00000E61B0000000102030E1C0000040000E6
++:1015D00000261C0000040000002E1C000004000077
++:1015E00000361C0000800000003E1C0000001E00B1
++:1015F00000461C0000040000006E1D0000040000F6
++:1016000000761D0000800000007E1D000004000028
++:1016100000861D0000000102038E1D000003000073
++:1016200000961D0000001E00009E1D00000400002A
++:1016300000C61D000004000000CE1D0000030000D5
++:1016400000EE1D000004000000F01E00002100005C
++:1016500000F81E000001000000001F000002000052
++:1016600000081F000000010000101F000000010022
++:1016700000181F000001000000301F0000010000E2
++:1016800000381F000002000000401F0000000100A1
++:1016900000481F000000010000501F000001000072
++:1016A00000963E0000040000009E3E000003000083
++:1016B00000D144000004000000D9440000040000F0
++:1016C00000E944000080000000F144000004000034
++:1016D00000F9440000010000000945000027000057
++:1016E00000194500002600000054560000FFFF3F8F
++:1016F000006C560000FF1F0000635F00000F000039
++:1017000000F869000000000004006A00000000000A
++:1017100004086A000000000004106A0000000000D5
++:1017200004186A000000000004206A0000000000A5
++:1017300004286A000000000004306A000000000075
++:1017400004386A000000000004406A000000000045
++:1017500004486A000000000004506A000000000015
++:1017600004586A000000000004606A0000000000E5
++:1017700004686A000000000004706A0000000000B5
++:1017800004986B0000DFBFE304A06B0000DFBFE341
++:1017900004B86B000021FE0100C06B00008168AC42
++:1017A0000FC86B000001000000086C0000120C1054
++:1017B00008306C000000010000486C0000010001CE
++:1017C00000586C000001000100606C000001000086
++:1017D00000686C000001000100706C000001000056
++:1017E00000786C000004000000806C000002000023
++:1017F00000F06F00000000803F287000000400002F
++:1018000000307000001A0000004870000001000065
++:1018100000E070000000FFFF00187100000F0000E2
++:1018200000587100008168AC0F6071000011000069
++:1018300000E071000004000000F8710000030000E7
++:10184000001072000002000000187200000000008A
++:101850000420720000000000044072000005000037
++:101860000048720000520000007072000001000089
++:1018700000007300000000803F087300000000803B
++:101880003F107300000000803F18730000000080CC
++:101890003F207300000000803F287300000000809C
++:1018A0003F307300000000803F387300000000806C
++:1018B0003F407300000000803F487300000000803C
++:1018C0003F507300000000803F587300000000800C
++:1018D0003F607300000000803F68730000000080DC
++:1018E0003F707300000000803F78730000000080AC
++:1018F0003F8073000010000000B8740000120C104C
++:1019000008C074000005000000D874000001000049
++:1019100000E8740000FFFF0000F0740000FFFF000B
++:1019200000F8740000FFFF000000750000FFFF00DA
++:101930000008750000030000007075000000FFFF44
++:1019400000787500001A0000008875000003000090
++:1019500000707700000201000080770000040000A2
++:101960000088770000040000009077000004000069
++:10197000009877000004000000E0770000040000F9
++:1019800000E877000004000000F8770000FF07007F
++:1019900000087800000201000058780000040000F0
++:1019A0000060780000040000006878000004000077
++:1019B000007078000004000000AAE60000040000A7
++:1019C00000B2E6000004000000BAE60000120C10AD
++:1019D00008C2E6000003000000D2E60000120C106E
++:1019E00008E2E60000140C0800EAE600000100002E
++:1019F00000F2E6000004000000FAE6000004000027
++:101A00000002E70000140C08001AE70000120C1096
++:101A10000822E70000270000003AE700000100006C
++:101A2000004A0501000100000002060100120C102E
++:101A30000840060100120C1008500601008000004A
++:101A400000580601000470008060060100000400D8
++:101A50000468060100C000000070060100001000CC
++:101A60000088060100000E000090060100001E0024
++:101A7000009806010001000000B00601000100000E
++:101A800000C006010004000000C8060100020000BA
++:101A900000380701008000000040070100047000CA
++:101AA00080480701000004000450070100C0000046
++:101AB00000580701000010000070070100000E0030
++:101AC0000078070100001E000080070100010000EF
++:101AD000009807010001000000A8070100040000B1
++:101AE00000B00701000200000018080100120C10ED
++:101AF000082808010021FE010058080100FFFF002E
++:101B00000060080100FFFF000068080100FFFF00FF
++:101B10000070080100FFFF000078080100010000CC
++:101B20000080080100010001008808010001000197
++:101B3000009008010001000000A008010021FE0142
++:101B400000B008010001000000D8080100120C10CC
++:101B500008E008010004000000F008010002000095
++:101B600000F808010011000000400901008168AC84
++:101B70000F5009010003000000700901000400007B
++:101B800000C009010002000000C8090100010000B6
++:101B900000D009010001000000D809010002000086
++:101BA00000E009010001000000E809010001000057
++:101BB00000F009010001000000000A01000200001D
++:101BC00000080A010002000000100A0100020000E3
++:101BD00000180A010002000000200A0100020000B3
++:101BE00000280A010002000000300A010002000083
++:101BF00000380A010002000000400A010001000054
++:101C000000480A010001000000500A010001000024
++:101C100000580A010001000000600A0100010000F4
++:101C200000680A010001000000700A0100010000C4
++:101C300000780A010001000000800A010001000094
++:101C400000880A010001000000900A010001000064
++:101C500000980A010001000000A00A010001000034
++:101C600000A80A010001000000B00A010001000004
++:101C700000B80A010001000000C00A0100020000D3
++:101C800000C80A010002000000D00A0100020000A2
++:101C900000D80A010002000000E00A010002000072
++:101CA00000E80A010002000000F00A010002000042
++:101CB00000F80A010002000000000B010001000012
++:101CC00000080B010001000000100B0100010000E2
++:101CD00000180B010001000000200B0100010000B2
++:101CE00000280B010001000000300B010001000082
++:101CF00000380B010001000000400B010001000052
++:101D000000480B010001000000500B010001000021
++:101D100000580B010001000000600B0100010000F1
++:101D200000680B010001000000700B0100010000C1
++:101D300000780B010001000000800B010001000091
++:101D400000880B010001000000900B010001000061
++:101D500000980B010001000000A00B010001000031
++:101D600000A80B010001000000B00B010001000001
++:101D700000B80B010001000000D80B0100040000B6
++:101D800000F82801001100000008290100010000EE
++:011D90000052
++:00000001FF
+diff --git a/firmware/nouveau/nvaa.ctxprog.ihex b/firmware/nouveau/nvaa.ctxprog.ihex
+new file mode 100644
+index 0000000..cd811f0
+--- /dev/null
++++ b/firmware/nouveau/nvaa.ctxprog.ihex
+@@ -0,0 +1,79 @@
++:100000004E5643500034019C00700000003000093F
++:10001000F14400092D400051E54000440A4000052C
++:100020000A40000D0A40008E0070004D1240009DF5
++:100030000070004D0045009700700021014500A1AF
++:100040004644004D7644004D8244001D0070000679
++:1000500018400005006000454444008B30440045D2
++:100060001840004D23400081007000CF1C40009FCD
++:100070000070009F0050004DDC4400170070000B22
++:100080002340004D7D440021024500A1564400A0BC
++:10009000007000010070000300700006274000059A
++:1000A0002840000D006000050070000D0070000683
++:1000B000007000020070000B0070000E0070001C49
++:1000C0000070000C00600000000000FFFF9000FFC7
++:1000D000FF91004DD44400090060004D0048009697
++:1000E000007000CF3A40009F0070009F0050005108
++:1000F000E54000C036400080002000080060004F4E
++:10010000364000C0364000CC3E400051364000161C
++:100110000070004D004800110060004D0048004D87
++:100120003644008E007000810070004D7044004D18
++:100130007D4400830070000000300040272100074C
++:10014000006000010BC0002200200001008000CBF5
++:10015000005000FF00C0004D5E44004D0048000804
++:10016000CE44004D7344004D8B44004D5E44004D21
++:10017000E244004D7644004D8244004D0048008327
++:100180000070004D0345003F02A000400020000623
++:100190000060004DFC44004D8D4400D00120006003
++:1001A000B8440080022000FFFF38004DCC4400001E
++:1001B000003000CB0050004D1C4500CB0050000724
++:1001C000D044004D0048004D794400FC1B11004D07
++:1001D0000048004D794400FD1B11004D0048004DC2
++:1001E000794400FE1B11004D004800000020000073
++:1001F000007000060060004D00480001002000066D
++:100200000060004DFC44000A0211004D004800004F
++:10021000003000FFFFC30000002000070060000066
++:1002200000700008002000FF008000CB0050004D4F
++:10023000004800000000004D0048000000000002DF
++:10024000021700320020000D02100042021E00C002
++:100250000211000203120002041500000518000933
++:1002600005130050051500050611001300200007B6
++:100270000610000007110000091100020912000019
++:100280000A1100020B1600280B12002B0B140001A0
++:100290000C1100010D1100001411000514110007CC
++:1002A000141100091411000B141100D400200000D7
++:1002B000151000051A14000C1A1300001C1300047A
++:1002C0001C1300201C1400251C1300001F13000425
++:1002D0001F1300081F11000B1F1100150020004004
++:1002E0001F10004D004800060060004D1C45002016
++:1002F000201100222011008500200040201000C89D
++:10030000201100CA201400CF201B0000211200037E
++:10031000211200402116004721120053211200A093
++:10032000211100C0211200CB211100D4211100D8CD
++:100330002115004D004800000000004D0048000B52
++:100340000060004D0048000A0060004D0048000BAE
++:100350000060004DD2400020002000080060004CEA
++:100360000050004D004800E8032000080060004CE9
++:100370000050004D004800040060004A0050004D4D
++:10038000004800FF00C000FF00C8004D004800FF0B
++:1003900000C000FF00C8004D004800160070008E2D
++:1003A00000700082007000410050004DD84400955C
++:1003B000007000D100500016006000520050000292
++:1003C000007000150070004D2840008E0070004D38
++:1003D000D44400000020000700600000003000FF4F
++:1003E00000C00000002000FF008000090070000E27
++:1003F0000070004D004800800070001700480000A9
++:100400000070004D0048004D0048004D0048004D70
++:100410000048008E0070004DD44400830070004DF1
++:10042000DF44004D0C45000F0070008C0B4100CBE9
++:100430000050004D004800800220000700600007C7
++:10044000234500871145004D004800000000007062
++:100450002020004DFC4400FF0080004D00480000BB
++:1004600006210007006000280420004DFC4400FF26
++:100470000088004D0048000F0048004B0048004D28
++:100480001645008F0070008C004800CB0050004DD6
++:10049000004800702020004DFC4400FD008000CB8F
++:1004A0000050000200C000800220000700600061D0
++:1004B0000120004DFC440002008000CB00500002EF
++:1004C00000C0000E1F20004DFC440002008000CB45
++:0704D0000050004D00480040
++:00000001FF
+diff --git a/firmware/nouveau/nvaa.ctxvals.ihex b/firmware/nouveau/nvaa.ctxvals.ihex
+new file mode 100644
+index 0000000..9e7fb6a
+--- /dev/null
++++ b/firmware/nouveau/nvaa.ctxvals.ihex
+@@ -0,0 +1,356 @@
++:100000004E56435600C4020000430000003000007A
++:10001000004B00000002000000740000000300001C
++:10002000007500000000100000880000000CFE00B9
++:10003000008E000000001000009900000087010001
++:10004000009E000000181000009F000000FF00004C
++:1000500000B200000004000000B3000000DF002533
++:1000600004B500000000060000BB00000000000016
++:1000700001BC000000FF000000BE000000000800FE
++:1000800000C400000001000000C5000000010000E5
++:1000900000C600000080000E00C700000004000041
++:1000A00000CE00000002000000CF000000010000B0
++:1000B00000D300000001000000D400000000010097
++:1000C00000DA00000002000000DB00000001000078
++:1000D00000DC00000001000000E000000001000062
++:1000E00000E1000000FFFF3F00E2000000FF1F00F2
++:1000F00000E400000001000000E500000001000035
++:1001000000E700000001000000E80000000100001E
++:1001100000E900000001000000EA00000004000007
++:1001200000EB00000001000000EC000000010000F6
++:1001300000ED00000001000000EE000000070000DC
++:1001400000EF00000001000000F0000000070000C8
++:1001500000F100000001000000F2000000010000BA
++:1001600000F300000001000000F8000000010000A2
++:1001700000F900000000010000FB00000001000089
++:1001800000FE00000000010000FF00000001000070
++:100190000000010000000100000201000001000059
++:1001A0000005010000000100000A01000004000039
++:1001B000000B010000700000000C01000080000036
++:1001C00000110100000C00000013010000080000F5
++:1001D00000140100001400000016010000290000B6
++:1001E0000017010000270000001801000026000091
++:1001F0000019010000080000001A010000040000BE
++:10020000001B010000270000001E0100000100008B
++:10021000001F010000020000002001000003000098
++:100220000021010000040000002201000005000080
++:100230000023010000060000002401000007000068
++:1002400000250100000100000036010000CF000081
++:100250000042010000800000004301000004000093
++:1002600000440100000400000045010000030000FC
++:1002700000460100000100000049010000120000DA
++:10028000004A010000100000004B0100000C0000BB
++:10029000004C0100000100000050010000040000BB
++:1002A00000510100000200000052010000040000A3
++:1002B0000056010000FFFF3F0057010000FF1F0034
++:1002C0000061010000010000006201000001000067
++:1002D000006301000002000000650100000400004E
++:1002E000006601000014000000670100000100002A
++:1002F000006A010000020000006F01000001000020
++:1003000000710100000200000072010000001000F6
++:100310000073010000000E000074010000001000D6
++:100320000075010000001E000077010000010000C0
++:1003300000780100000100000079010000010000C8
++:10034000007A010000010000007B010000010000B4
++:10035000007F010000000200008101000001000098
++:100360000082010000F000000083010000FF000097
++:1003700000860100000100000087010000F000007D
++:100380000088010000FF0000008A01000009000051
++:10039000008D010000010000008E010000CF000070
++:1003A00000900100000100000094010000CF000057
++:1003B000009601000002000000980100000100000A
++:1003C000009A010000010000009C010000CF000025
++:1003D000009D010000CF0000009E01000001000010
++:1003E00000A0010000801F0000A601000021F87499
++:1003F0003BA701000001800589A901000000100051
++:1004000000AA010000FF000000AC010000FA107C0F
++:1004100002AD010000C0000040AE01000080208954
++:10042000B7B301000040003D00B50100002200000C
++:1004300000BD01000040003D00BE010000220000A0
++:1004400000D00100000AFF0000D20100000000807F
++:1004500001D301000000001600D40100000000805C
++:1004600001D8010000FFFF0300D901000000000CCB
++:1004700030E201000001040100E401000078000006
++:1004800000E6010000BF000000E8010000101200BB
++:1004900000E901000080000008EC0100003E0000BF
++:1004A00000F201000000008001F3010000000016CE
++:1004B00000F401000000008001F8010000FFFF03CC
++:1004C00000F901000000000C3002020000010401EC
++:1004D00000040200007800000006020000BF0000D7
++:1004E0000008020000101200000902000080000055
++:1004F000080C0200003E0000001202000000008014
++:100500000113020000000016001402000000008029
++:100510000118020000FFFF03001902000000000C98
++:1005200030220200000104010024020000780000D3
++:100530000026020000BF0000002802000010120088
++:100540000029020000800000082C0200003E00008C
++:10055000003202000000008001330200000000169B
++:1005600000340200000000800138020000FFFF0399
++:10057000003902000000000C3042020000010401BA
++:1005800000440200007800000046020000BF0000A6
++:100590000048020000101200004902000080000024
++:1005A000084C0200003E0000005302000070701270
++:1005B0000157020000FFFFFF075E02000007041260
++:1005C000005F020000071509056002000002020139
++:1005D0000561020000010203006802000040000003
++:1005E00000690200000A0B0C0D6A020000101214D0
++:1005F000006B020000F00100006C0200000100002E
++:10060000006D020000030000006E02000000800088
++:100610000070020000009E03007102000000010053
++:100620000072020000003800007302000006E03F84
++:10063000007402000000E03F0075020000404040EE
++:10064000007602000007F0F70C77020000FF7FBF82
++:10065000028102000004000000860200003F00004A
++:100660000089020000040000008B0200000100006D
++:10067000008D0200008168AC0F990200008000002C
++:1006800000A102000004000000A9020000140C08F0
++:1006900000AF020000140C0800B30200000F0000BD
++:1006A00000B9020000FF070000C7020000040800B4
++:1006B00000D702000004000000DB02000020000060
++:1006C00000DE02000002000000DF02000004000063
++:1006D00000E602000000000004E7020000120C1017
++:1006E00008ED020000DFBFE304EE0200000000009E
++:1006F00004F5020000DFBFE304F70200000400007D
++:1007000000FF020000040000000F030000100000C2
++:10071000001D030000DFBFE30425030000DFBFE38B
++:100720000436030000040000003703000004080042
++:10073000003F03000001000000470300001A000012
++:10074000004F0300007F000000560300000400007B
++:10075000005B0300001A0000005F030000010000BE
++:100760000067030000140C080077030000120C104F
++:10077000087F030000040000008503000002000061
++:100780000087030000040000009503000067FE1FBF
++:10079000009703000010000000B7030000010000F4
++:1007A00000BF030000120C1008CB0300000400007F
++:1007B00000D303000004000000E303000004000075
++:1007C00000E603000001000000EB03000008000049
++:1007D00000EE03000001100000F6030000FFFF0020
++:1007E00000F7030000FF070000FB030000FF070005
++:1007F00000FD03000001000000FE030000FFFF00F9
++:1008000000FF030000140C080005040000100000A5
++:100810000006040000FFFF00000E040000FFFF00C0
++:10082000001D04000001000000320400000400006C
++:10083000003A040000040000004504000004000029
++:10084000004D0400000004000055040000000300F7
++:10085000005D04000001100000630400000F0000B0
++:10086000006504000015000000950400000200006F
++:1008700000DD04000001000000E50400001000009D
++:1008800000F50400000100000016050000000080D3
++:100890003F1E0500000000803F260500000000808C
++:1008A0003F2E0500000000803F360500000000805C
++:1008B0003F3E0500000000803F460500000000802C
++:1008C0003F4E0500000000803F56050000000080FC
++:1008D0003F5E0500000000803F66050000000080CC
++:1008E0003F6E0500000000803F760500000000809C
++:1008F0003F7E0500000000803F860500000000806C
++:100900003F8E0500000000803F95050000100000AC
++:10091000009605000010000000AE05000003000076
++:1009200000C705000001000000DF05000010000006
++:10093000001D0600000000803F1E060000000000B1
++:1009400004250600000000803F260600000000008D
++:10095000042D0600000000803F35060000000080E6
++:100960003F36060000800000003D060000000080C9
++:100970003F450600000000803F4D0600000000805B
++:100980003F550600000000803F5606000080000032
++:10099000005D0600000000803F650600000000804A
++:1009A0003F6D0600000000803F75060000000080DB
++:1009B0003F7D0600000000803F85060000000080AB
++:1009C0003F8D0600000000803F8E06000001000001
++:1009D00000950600000000803F9606000000010020
++:1009E000009E06000000010000A6060000110000A5
++:1009F00000B606000008000000E606000001000046
++:100A000000F606000001000000FE060000010000E4
++:100A10000006070000010000000E070000CF0000E4
++:100A20000016070000020000004E07000001000051
++:100A3000005E0700000100000065070000100000D4
++:100A40000066070000010000006E070000010000C2
++:100A500000750700003F000000960700000400003A
++:100A600000A607000001000000A7070000880000A2
++:100A700000AD07000001000000AE070000150000F7
++:100A800000AF07000088000000BD07000001000063
++:100A900000C707000004000000CD070000010000AF
++:100AA00000CE07000080444404E507000001100068
++:100AB0000045080000110000007F0800002600002B
++:100AC00000850800000F000000970800000000806B
++:100AD0003FB70800001A000000BF08000010000027
++:100AE00000C508000011000000FD08000001000022
++:100AF0000005090000010000000D090000010000D0
++:100B00000015090000020000001D0900000100009E
++:100B10000025090000020000002D0900000100006E
++:100B2000003D09000067FE1F004D0900008168AC10
++:100B30000FA609000001000000B609000001000036
++:100B400000D60900008824712AE609000000C0854B
++:100B500040EE09000040000000F60900000001001E
++:100B600000FE09000000010100060A0000000080EC
++:100B700002070A000052000000170A0000260000C9
++:100B800000270A0000040000002D0A0000010000F8
++:100B9000002F0A000004000000350A0000020000D7
++:100BA000003D0A0000010000003F0A00001A00009A
++:100BB00000450A0000010000004D0A00000200008C
++:100BC00000550A000001000000570A000000FFFF66
++:100BD000005D0A000001000000850A00001100000D
++:100BE000008D0A000001000000D60A00000F00007E
++:100BF00000360B0000040000003E0B0000FFFF0069
++:100C000000460B0000FFFF00004E0B0000FFFF003E
++:100C100000560B0000FFFF00009E0B0000010000CB
++:100C200000BE0B000001000000EE0B000001000000
++:100C300000060C0000010000005E0C000001000036
++:100C400000660C0000010000006E0C0000020000B5
++:100C500000760C0000010000007E0C000001000086
++:100C600000860C0000020000008E0C000001000055
++:100C7000009E0C000011000000DE0C00008168AC3A
++:100C80000FF60C000004000000160D00001100001B
++:100C9000001E0D0000010000002E0D0000CF00001E
++:100CA00000360D0000CF0000003E0D0000CF000018
++:100CB000008B0D00000F000000960D0000010000E9
++:100CC000009B0D0000010000009E0D0000010000CF
++:100CD00000A60D000002000000AE0D0000010000A3
++:100CE00000B60D000001000000BE0D000002000073
++:100CF00000C60D000001000000D60D00000100003C
++:100D000000DE0D000001000000E60D000001000003
++:100D100000EE0D000001000000F60D0000010000D3
++:100D200000FE0D000001000000060E0000010000A2
++:100D3000000E0E000001000000160E000011000061
++:100D400000560E00008168AC0F5E0E00000F000020
++:100D5000009E0E000067FE1F00B60E00001100008E
++:100D600000BE0E000001000000DE0E0000040000C6
++:100D7000000E0F000001000000360F0000110000FF
++:100D800000760F00008168AC0F960F000011000084
++:100D9000009E0F000001000000AE0F0000010000E7
++:100DA00000BE0F000001000000CE0F0000FF070092
++:100DB00000DE0F000001000000EE0F000001000047
++:100DC00000731000000F0000004611000008000032
++:100DD000004E11000008000000561100000800003D
++:100DE000005E11000008000000661100000800000D
++:100DF000006E1100000800000076110000080000DD
++:100E0000007E1100000800000086110000110000A3
++:100E100000C61100008168AC0FCE11000000040074
++:100E200000D611000000040000DE110000000400E4
++:100E300000E611000000040000EE110000000400B4
++:100E400000F611000000040000FE11000000040084
++:100E50000006120000000400000E12000000030053
++:100E60000016120000000300001E12000000030024
++:100E70000026120000000300002E120000000300F4
++:100E80000036120000000300003E120000000300C4
++:100E90000046120000000300004E12000001000096
++:100EA00000561200000F0000009612000020000003
++:100EB000009E12000011000000A6120000000100B8
++:100EC00000B612000001000000CE12000040000039
++:100ED00000D612000000010000E61200000300002E
++:100EE000000E13000067FE1F002E1300000200001A
++:100EF00000361300008168AC0F861300000100006B
++:100F000000AE13000004000000BE1300000100004A
++:100F100000C613000000040000CE13000000030010
++:100F200000D613000001100000F6130000110000AD
++:100F300000361400008168AC0F3E1400000F000062
++:100F400000FE1400008168AC0F0615000067FE1F4C
++:100F50000026150000110000003E150000040000EE
++:100F6000004E1500000100000056150000010000B1
++:100F70000076150000010000009E1500008168AC9D
++:100F80000FC615000004000000D615000001000087
++:100F900000DE15000001000000F615000001000051
++:100FA000004E16000001000000561600000100006F
++:100FB000005E16000001000000661600000100003F
++:100FC000006E16000001000000761600000100000F
++:100FD000007E1600000100000086160000010000DF
++:100FE000008E16000011000000CE1600008168ACD3
++:100FF0000FD61600000F00000016170000110000A9
++:10100000001E170000010000007E170000DFBFE394
++:101010000486170000DFBFE3048E17000001000004
++:10102000009E17000000FFFF00A61700000100004F
++:1010300000BE17000000FFFF0006180000010000BE
++:101040000016180000010000001E1800000010200B
++:101050003026180000405060702E1800008898A8B4
++:10106000B836180000C8D8E8F8461800001A000082
++:10107000005618000004000000FE180000040000E4
++:101080000006190000040000000E19000000010213
++:101090000336190000040000004E1900000400008F
++:1010A0000056190000040000005E190000800000D6
++:1010B0000066190000001E00006E19000004000008
++:1010C00000961A0000040000009E1A000080000034
++:1010D00000A61A000004000000AE1A000000010281
++:1010E00003B61A000003000000BE1A0000001E0034
++:1010F00000C61A000004000000EE1A000004000000
++:1011000000F61A000003000000161B000004000097
++:1011100000E81E000021000000F01E000001000099
++:1011200000F81E000002000000001F000000010087
++:1011300000081F000000010000101F000001000057
++:1011400000281F000001000000301F000002000006
++:1011500000381F000000010000401F0000000100D7
++:1011600000481F000001000000BE2B00000400002A
++:1011700000C62B000003000000D12C00000400007A
++:1011800000D92C000004000000E92C0000800000C1
++:1011900000F12C000004000000F92C000001000008
++:1011A00000092D000027000000192D000026000076
++:1011B00000135200000F00000054560000FFFF3FD4
++:1011C000006C560000FF1F0000F0690000000000E6
++:1011D00004F869000000000004006A00000000003C
++:1011E00004086A000000000004106A00000000000B
++:1011F00004186A000000000004206A0000000000DB
++:1012000004286A000000000004306A0000000000AA
++:1012100004386A000000000004406A00000000007A
++:1012200004486A000000000004506A00000000004A
++:1012300004586A000000000004606A00000000001A
++:1012400004686A000000000004906B0000DFBFE348
++:1012500004986B0000DFBFE304B06B000021FE01C7
++:1012600000B86B00008168AC0FE06B0000120C103E
++:1012700008086C000000010000206C000001000163
++:1012800000306C000001000100386C00000100001B
++:1012900000406C000001000100486C0000010000EB
++:1012A00000506C000004000000586C0000020000B8
++:1012B00000886F00000000803FC06F000004000045
++:1012C00000C86F00001A000000E06F00000100007D
++:1012D000007870000000FFFF00B07000000F0000F9
++:1012E00000F07000008168AC0FF870000011000081
++:1012F00000787100000400000090710000010000FF
++:1013000000A871000002000000B0710000000000A1
++:1013100004B871000000000004D87100000500004E
++:1013200000E071000052000000087200000100009F
++:1013300000907200000000803F9872000000008062
++:101340003FA07200000000803FA8720000000080F3
++:101350003FB07200000000803FB8720000000080C3
++:101360003FC07200000000803FC872000000008093
++:101370003FD07200000000803FD872000000008063
++:101380003FE07200000000803FE872000000008033
++:101390003FF07200000000803FF872000000008003
++:1013A0003F007300000000803F08730000000080D1
++:1013B0003F107300001000000048740000120C1071
++:1013C000085074000005000000687400000100006F
++:1013D0000078740000FFFF000080740000FFFF0031
++:1013E0000088740000FFFF000090740000FFFF0001
++:1013F000009874000003000000B874000000FFFFB4
++:1014000000C07400001A000000D074000003000047
++:1014100000B876000002010000C876000004000059
++:1014200000D076000004000000D876000004000020
++:1014300000E07600000400000028770000040000AF
++:1014400000307700000400000040770000FF070034
++:10145000005077000002010000A0770000040000A7
++:1014600000A877000004000000B07700000400002E
++:1014700000B8770000040000009AE60000040000B5
++:1014800000A2E6000004000000AAE60000120C1012
++:1014900008B2E6000003000000C2E60000120C10D3
++:1014A00008D2E60000140C0800DAE6000001000093
++:1014B00000E2E6000004000000EAE600000400008C
++:1014C00000F2E60000140C08000AE70000120C10FD
++:1014D0000812E70000270000002AE70000010000D2
++:1014E000003A05010001000000F2050100120C1095
++:1014F0000800060100120C10081006010080000010
++:10150000001806010004700080200601000004009D
++:101510000428060100C00000003006010000100091
++:101520000048060100000E000050060100001E00E9
++:1015300000580601000100000070060100010000D3
++:10154000008006010004000000880601000200007F
++:1015500000E8060100120C1008F806010021FE0147
++:101560000028070100FFFF000030070100FFFF0017
++:101570000038070100FFFF000040070100FFFF00E7
++:1015800000480701000100000050070100010001B0
++:101590000058070100010001006007010001000080
++:1015A000007007010021FE0100A0070100120C10CD
++:1015B00008A807010004000000B8070100020000AD
++:1015C00000C007010011000000080801008168AC9C
++:1015D0000F30080100040000008008010002000034
++:1015E00000880801000100000090080100010000CF
++:1015F000009808010002000000A00801000100009E
++:1016000000A808010001000000B00801000100006E
++:1016100000C008010004000000C025010011000006
++:0916200000D025010001000000CA
++:00000001FF
+diff --git a/firmware/nouveau/nvac.ctxprog.ihex b/firmware/nouveau/nvac.ctxprog.ihex
+new file mode 100644
+index 0000000..41b1bea
+--- /dev/null
++++ b/firmware/nouveau/nvac.ctxprog.ihex
+@@ -0,0 +1,79 @@
++:100000004E5643500034019C00700000003000093F
++:10001000F14400092D400051E54000440A4000052C
++:100020000A40000D0A40008E0070004D1240009DF5
++:100030000070004D0045009700700021014500A1AF
++:100040004644004D7644004D8244001D0070000679
++:1000500018400005006000454444008B30440045D2
++:100060001840004D23400081007000CF1C40009FCD
++:100070000070009F0050004DDC4400170070000B22
++:100080002340004D7D440021024500A1564400A0BC
++:10009000007000010070000300700006274000059A
++:1000A0002840000D006000050070000D0070000683
++:1000B000007000020070000B0070000E0070001C49
++:1000C0000070000C00600000000000FFFF9000FFC7
++:1000D000FF91004DD44400090060004D0048009697
++:1000E000007000CF3A40009F0070009F0050005108
++:1000F000E54000C036400080002000080060004F4E
++:10010000364000C0364000CC3E400051364000161C
++:100110000070004D004800110060004D0048004D87
++:100120003644008E007000810070004D7044004D18
++:100130007D4400830070000000300040282100074B
++:10014000006000010BC0002200200001008000CBF5
++:10015000005000FF00C0004D5E44004D0048000804
++:10016000CE44004D7344004D8B44004D5E44004D21
++:10017000E244004D7644004D8244004D0048008327
++:100180000070004D0345003F02A000400020000623
++:100190000060004DFC44004D8D4400D00120006003
++:1001A000B8440080022000FFFF38004DCC4400001E
++:1001B000003000CB0050004D1C4500CB0050000724
++:1001C000D044004D0048004D794400FC1B11004D07
++:1001D0000048004D794400FD1B11004D0048004DC2
++:1001E000794400FE1B11004D004800000020000073
++:1001F000007000060060004D00480001002000066D
++:100200000060004DFC44000A0211004D004800004F
++:10021000003000FFFFC30000002000070060000066
++:1002200000700008002000FF008000CB0050004D4F
++:10023000004800000000004D0048000000000002DF
++:10024000021700320020000D02100042021E00C002
++:100250000211000203120002041500000518000933
++:1002600005130050051500050611001300200007B6
++:100270000610000007110000091100020912000019
++:100280000A1100020B1600280B12002B0B140001A0
++:100290000C1100010D1100001411000514110007CC
++:1002A000141100091411000B141100D400200000D7
++:1002B000151000051A14000C1A1300001C1300047A
++:1002C0001C1300201C1400251C1300001F13000425
++:1002D0001F1300081F11000B1F1100150020004004
++:1002E0001F10004D004800060060004D1C45002016
++:1002F000201100222011008500200040201000C89D
++:10030000201100CA201400CF201B0000211200037E
++:10031000211200402116004721120053211200A093
++:10032000211100C0211200CB211100D4211100D8CD
++:100330002115004D004800000000004D0048000B52
++:100340000060004D0048000A0060004D0048000BAE
++:100350000060004DD2400020002000080060004CEA
++:100360000050004D004800E8032000080060004CE9
++:100370000050004D004800040060004A0050004D4D
++:10038000004800FF00C000FF00C8004D004800FF0B
++:1003900000C000FF00C8004D004800160070008E2D
++:1003A00000700082007000410050004DD84400955C
++:1003B000007000D100500016006000520050000292
++:1003C000007000150070004D2840008E0070004D38
++:1003D000D44400000020000700600000003000FF4F
++:1003E00000C00000002000FF008000090070000E27
++:1003F0000070004D004800800070001700480000A9
++:100400000070004D0048004D0048004D0048004D70
++:100410000048008E0070004DD44400830070004DF1
++:10042000DF44004D0C45000F0070008C0B4100CBE9
++:100430000050004D004800800220000700600007C7
++:10044000234500871145004D004800000000007062
++:100450002020004DFC4400FF0080004D00480000BB
++:1004600006210007006000480420004DFC4400FF06
++:100470000088004D0048000F0048004B0048004D28
++:100480001645008F0070008C004800CB0050004DD6
++:10049000004800702020004DFC4400FD008000CB8F
++:1004A0000050000200C000800220000700600061D0
++:1004B0000120004DFC440002008000CB00500002EF
++:1004C00000C0000E1F20004DFC440002008000CB45
++:0704D0000050004D00480040
++:00000001FF
+diff --git a/firmware/nouveau/nvac.ctxvals.ihex b/firmware/nouveau/nvac.ctxvals.ihex
+new file mode 100644
+index 0000000..fb35ebd
+--- /dev/null
++++ b/firmware/nouveau/nvac.ctxvals.ihex
+@@ -0,0 +1,362 @@
++:100000004E56435600CF020000430000003000006F
++:10001000004B00000002000000740000000300001C
++:10002000007500000000100000880000000CFE00B9
++:10003000008E000000001000009900000087010001
++:10004000009E000000181000009F000000FF00004C
++:1000500000B200000004000000B3000000DF002533
++:1000600004B500000000060000BB00000000000016
++:1000700001BC000000FF000000BE000000000800FE
++:1000800000C400000001000000C5000000010000E5
++:1000900000C600000080000E00C700000004000041
++:1000A00000CE00000002000000CF000000010000B0
++:1000B00000D300000001000000D400000000010097
++:1000C00000DA00000002000000DB00000001000078
++:1000D00000DC00000001000000E000000001000062
++:1000E00000E1000000FFFF3F00E2000000FF1F00F2
++:1000F00000E400000001000000E500000001000035
++:1001000000E700000001000000E80000000100001E
++:1001100000E900000001000000EA00000004000007
++:1001200000EB00000001000000EC000000010000F6
++:1001300000ED00000001000000EE000000070000DC
++:1001400000EF00000001000000F0000000070000C8
++:1001500000F100000001000000F2000000010000BA
++:1001600000F300000001000000F8000000010000A2
++:1001700000F900000000010000FB00000001000089
++:1001800000FE00000000010000FF00000001000070
++:100190000000010000000100000201000001000059
++:1001A0000005010000000100000A01000004000039
++:1001B000000B010000700000000C01000080000036
++:1001C00000110100000C00000013010000080000F5
++:1001D00000140100001400000016010000290000B6
++:1001E0000017010000270000001801000026000091
++:1001F0000019010000080000001A010000040000BE
++:10020000001B010000270000001E0100000100008B
++:10021000001F010000020000002001000003000098
++:100220000021010000040000002201000005000080
++:100230000023010000060000002401000007000068
++:1002400000250100000100000036010000CF000081
++:100250000042010000800000004301000004000093
++:1002600000440100000400000045010000030000FC
++:1002700000460100000100000049010000120000DA
++:10028000004A010000100000004B0100000C0000BB
++:10029000004C0100000100000050010000040000BB
++:1002A00000510100000200000052010000040000A3
++:1002B0000056010000FFFF3F0057010000FF1F0034
++:1002C0000061010000010000006201000001000067
++:1002D000006301000002000000650100000400004E
++:1002E000006601000014000000670100000100002A
++:1002F000006A010000020000006F01000001000020
++:1003000000710100000200000072010000001000F6
++:100310000073010000000E000074010000001000D6
++:100320000075010000001E000077010000010000C0
++:1003300000780100000100000079010000010000C8
++:10034000007A010000010000007B010000010000B4
++:10035000007F010000000200008101000001000098
++:100360000082010000F000000083010000FF000097
++:1003700000860100000100000087010000F000007D
++:100380000088010000FF0000008A01000009000051
++:10039000008D010000010000008E010000CF000070
++:1003A00000900100000100000094010000CF000057
++:1003B000009601000002000000980100000100000A
++:1003C000009A010000010000009C010000CF000025
++:1003D000009D010000CF0000009E01000001000010
++:1003E00000A0010000801F0000A601000021F87499
++:1003F0003BA701000001800589A901000000100051
++:1004000000AA010000FF000000AC010000FA107C0F
++:1004100002AD010000C0000040AE01000080208954
++:10042000B7B301000040003D00B50100002200000C
++:1004300000BD01000040003D00BE010000220000A0
++:1004400000D00100000AFF0000D20100000000807F
++:1004500001D301000000001600D40100000000805C
++:1004600001D8010000FFFF0300D901000000000CCB
++:1004700030E201000001040100E401000078000006
++:1004800000E6010000BF000000E8010000101200BB
++:1004900000E901000080000008EC0100003E0000BF
++:1004A00000F201000000008001F3010000000016CE
++:1004B00000F401000000008001F8010000FFFF03CC
++:1004C00000F901000000000C3002020000010401EC
++:1004D00000040200007800000006020000BF0000D7
++:1004E0000008020000101200000902000080000055
++:1004F000080C0200003E0000001202000000008014
++:100500000113020000000016001402000000008029
++:100510000118020000FFFF03001902000000000C98
++:1005200030220200000104010024020000780000D3
++:100530000026020000BF0000002802000010120088
++:100540000029020000800000082C0200003E00008C
++:10055000003202000000008001330200000000169B
++:1005600000340200000000800138020000FFFF0399
++:10057000003902000000000C3042020000010401BA
++:1005800000440200007800000046020000BF0000A6
++:100590000048020000101200004902000080000024
++:1005A000084C0200003E0000005302000070701270
++:1005B0000157020000FFFFFF075E02000007041260
++:1005C000005F020000071509056002000002020139
++:1005D0000561020000010203006802000040000003
++:1005E00000690200000A0B0C0D6A020000101214D0
++:1005F000006B020000F00100006C0200000100002E
++:10060000006D020000030000006E02000000800088
++:100610000070020000009E03007102000000010053
++:100620000072020000003800007302000006E03F84
++:10063000007402000000E03F0075020000404040EE
++:10064000007602000007F0FF0C77020000FF7FBF7A
++:10065000028102000004000000860200003F00004A
++:100660000089020000040000008B0200000100006D
++:10067000008D0200008168AC0F990200008000002C
++:1006800000A102000004000000A9020000140C08F0
++:1006900000AF020000140C0800B30200000F0000BD
++:1006A00000B9020000FF070000C7020000040800B4
++:1006B00000D702000004000000DB02000020000060
++:1006C00000DE02000002000000DF02000004000063
++:1006D00000E602000000000004E7020000120C1017
++:1006E00008ED020000DFBFE304EE0200000000009E
++:1006F00004F5020000DFBFE304F70200000400007D
++:1007000000FF020000040000000F030000100000C2
++:10071000001D030000DFBFE30425030000DFBFE38B
++:100720000436030000040000003703000004080042
++:10073000003F03000001000000470300001A000012
++:10074000004F0300007F000000560300000400007B
++:10075000005B0300001A0000005F030000010000BE
++:100760000067030000140C080077030000120C104F
++:10077000087F030000040000008503000002000061
++:100780000087030000040000009503000067FE1FBF
++:10079000009703000010000000B7030000010000F4
++:1007A00000BF030000120C1008CB0300000400007F
++:1007B00000D303000004000000E303000004000075
++:1007C00000E603000001000000EB03000008000049
++:1007D00000EE03000001100000F6030000FFFF0020
++:1007E00000F7030000FF070000FB030000FF070005
++:1007F00000FD03000001000000FE030000FFFF00F9
++:1008000000FF030000140C080005040000100000A5
++:100810000006040000FFFF00000E040000FFFF00C0
++:10082000001D04000001000000320400000400006C
++:10083000003A040000040000004504000004000029
++:10084000004D0400000004000055040000000300F7
++:10085000005D04000001100000630400000F0000B0
++:10086000006504000015000000950400000200006F
++:1008700000DD04000001000000E50400001000009D
++:1008800000F50400000100000016050000000080D3
++:100890003F1E0500000000803F260500000000808C
++:1008A0003F2E0500000000803F360500000000805C
++:1008B0003F3E0500000000803F460500000000802C
++:1008C0003F4E0500000000803F56050000000080FC
++:1008D0003F5E0500000000803F66050000000080CC
++:1008E0003F6E0500000000803F760500000000809C
++:1008F0003F7E0500000000803F860500000000806C
++:100900003F8E0500000000803F95050000100000AC
++:10091000009605000010000000AE05000003000076
++:1009200000C705000001000000DF05000010000006
++:10093000001D0600000000803F1E060000000000B1
++:1009400004250600000000803F260600000000008D
++:10095000042D0600000000803F35060000000080E6
++:100960003F36060000800000003D060000000080C9
++:100970003F450600000000803F4D0600000000805B
++:100980003F550600000000803F5606000080000032
++:10099000005D0600000000803F650600000000804A
++:1009A0003F6D0600000000803F75060000000080DB
++:1009B0003F7D0600000000803F85060000000080AB
++:1009C0003F8D0600000000803F8E06000001000001
++:1009D00000950600000000803F9606000000010020
++:1009E000009E06000000010000A6060000110000A5
++:1009F00000B606000008000000E606000001000046
++:100A000000F606000001000000FE060000010000E4
++:100A10000006070000010000000E070000CF0000E4
++:100A20000016070000020000004E07000001000051
++:100A3000005E0700000100000065070000100000D4
++:100A40000066070000010000006E070000010000C2
++:100A500000750700003F000000960700000400003A
++:100A600000A607000001000000A7070000880000A2
++:100A700000AD07000001000000AE070000150000F7
++:100A800000AF07000088000000BD07000001000063
++:100A900000C707000004000000CD070000010000AF
++:100AA00000CE07000080444404E507000001100068
++:100AB0000045080000110000007F0800002600002B
++:100AC00000850800000F000000970800000000806B
++:100AD0003FB70800001A000000BF08000010000027
++:100AE00000C508000011000000FD08000001000022
++:100AF0000005090000010000000D090000010000D0
++:100B00000015090000020000001D0900000100009E
++:100B10000025090000020000002D0900000100006E
++:100B2000003D09000067FE1F004D0900008168AC10
++:100B30000FA609000001000000B609000001000036
++:100B400000D60900008824712AE609000000C0854B
++:100B500040EE09000040000000F60900000001001E
++:100B600000FE09000000010100060A0000000080EC
++:100B700002070A000052000000170A0000260000C9
++:100B800000270A0000040000002D0A0000010000F8
++:100B9000002F0A000004000000350A0000020000D7
++:100BA000003D0A0000010000003F0A00001A00009A
++:100BB00000450A0000010000004D0A00000200008C
++:100BC00000550A000001000000570A000000FFFF66
++:100BD000005D0A000001000000850A00001100000D
++:100BE000008D0A000001000000D60A00000F00007E
++:100BF00000360B0000040000003E0B0000FFFF0069
++:100C000000460B0000FFFF00004E0B0000FFFF003E
++:100C100000560B0000FFFF00009E0B0000010000CB
++:100C200000BE0B000001000000EE0B000001000000
++:100C300000060C0000010000005E0C000001000036
++:100C400000660C0000010000006E0C0000020000B5
++:100C500000760C0000010000007E0C000001000086
++:100C600000860C0000020000008E0C000001000055
++:100C7000009E0C000011000000DE0C00008168AC3A
++:100C80000FF60C000004000000160D00001100001B
++:100C9000001E0D0000010000002E0D0000CF00001E
++:100CA00000360D0000CF0000003E0D0000CF000018
++:100CB000008B0D00000F000000960D0000010000E9
++:100CC000009B0D0000010000009E0D0000010000CF
++:100CD00000A60D000002000000AE0D0000010000A3
++:100CE00000B60D000001000000BE0D000002000073
++:100CF00000C60D000001000000D60D00000100003C
++:100D000000DE0D000001000000E60D000001000003
++:100D100000EE0D000001000000F60D0000010000D3
++:100D200000FE0D000001000000060E0000010000A2
++:100D3000000E0E000001000000160E000011000061
++:100D400000560E00008168AC0F5E0E00000F000020
++:100D5000009E0E000067FE1F00B60E00001100008E
++:100D600000BE0E000001000000DE0E0000040000C6
++:100D7000000E0F000001000000360F0000110000FF
++:100D800000760F00008168AC0F960F000011000084
++:100D9000009E0F000001000000AE0F0000010000E7
++:100DA00000BE0F000001000000CE0F0000FF070092
++:100DB00000DE0F000001000000EE0F000001000047
++:100DC00000731000000F0000004611000008000032
++:100DD000004E11000008000000561100000800003D
++:100DE000005E11000008000000661100000800000D
++:100DF000006E1100000800000076110000080000DD
++:100E0000007E1100000800000086110000110000A3
++:100E100000C61100008168AC0FCE11000000040074
++:100E200000D611000000040000DE110000000400E4
++:100E300000E611000000040000EE110000000400B4
++:100E400000F611000000040000FE11000000040084
++:100E50000006120000000400000E12000000030053
++:100E60000016120000000300001E12000000030024
++:100E70000026120000000300002E120000000300F4
++:100E80000036120000000300003E120000000300C4
++:100E90000046120000000300004E12000001000096
++:100EA00000561200000F0000009612000020000003
++:100EB000009E12000011000000A6120000000100B8
++:100EC00000B612000001000000CE12000040000039
++:100ED00000D612000000010000E61200000300002E
++:100EE000000E13000067FE1F002E1300000200001A
++:100EF00000361300008168AC0F861300000100006B
++:100F000000AE13000004000000BE1300000100004A
++:100F100000C613000000040000CE13000000030010
++:100F200000D613000001100000F6130000110000AD
++:100F300000361400008168AC0F3E1400000F000062
++:100F400000FE1400008168AC0F0615000067FE1F4C
++:100F50000026150000110000003E150000040000EE
++:100F6000004E1500000100000056150000010000B1
++:100F70000076150000010000009E1500008168AC9D
++:100F80000FC615000004000000D615000001000087
++:100F900000DE15000001000000F615000001000051
++:100FA000004E16000001000000561600000100006F
++:100FB000005E16000001000000661600000100003F
++:100FC000006E16000001000000761600000100000F
++:100FD000007E1600000100000086160000010000DF
++:100FE000008E16000011000000CE1600008168ACD3
++:100FF0000FD61600000F00000016170000110000A9
++:10100000001E170000010000007E170000DFBFE394
++:101010000486170000DFBFE3048E17000001000004
++:10102000009E17000000FFFF00A61700000100004F
++:1010300000BE17000000FFFF0006180000010000BE
++:101040000016180000010000001E1800000010200B
++:101050003026180000405060702E1800008898A8B4
++:10106000B836180000C8D8E8F8461800001A000082
++:10107000005618000004000000FE180000040000E4
++:101080000006190000040000000E19000000010213
++:101090000336190000040000004E1900000400008F
++:1010A0000056190000040000005E190000800000D6
++:1010B0000066190000001E00006E19000004000008
++:1010C00000961A0000040000009E1A000080000034
++:1010D00000A61A000004000000AE1A000000010281
++:1010E00003B61A000003000000BE1A0000001E0034
++:1010F00000C61A000004000000EE1A000004000000
++:1011000000F61A000003000000161B000004000097
++:1011100000E81E000021000000F01E000001000099
++:1011200000F81E000002000000001F000000010087
++:1011300000081F000000010000101F000001000057
++:1011400000281F000001000000301F000002000006
++:1011500000381F000000010000401F0000000100D7
++:1011600000481F000001000000BE2B00000400002A
++:1011700000C62B000003000000D12C00000400007A
++:1011800000D92C000004000000E92C0000800000C1
++:1011900000F12C000004000000F92C000001000008
++:1011A00000092D000027000000192D000026000076
++:1011B00000135200000F00000054560000FFFF3FD4
++:1011C000006C560000FF1F0000F0690000000000E6
++:1011D00004F869000000000004006A00000000003C
++:1011E00004086A000000000004106A00000000000B
++:1011F00004186A000000000004206A0000000000DB
++:1012000004286A000000000004306A0000000000AA
++:1012100004386A000000000004406A00000000007A
++:1012200004486A000000000004506A00000000004A
++:1012300004586A000000000004606A00000000001A
++:1012400004686A000000000004906B0000DFBFE348
++:1012500004986B0000DFBFE304B06B000021FE01C7
++:1012600000B86B00008168AC0FE06B0000120C103E
++:1012700008086C000000010000206C000001000163
++:1012800000306C000001000100386C00000100001B
++:1012900000406C000001000100486C0000010000EB
++:1012A00000506C000004000000586C0000020000B8
++:1012B00000C86F00000000803F00700000040000C4
++:1012C00000087000001A00000020700000010000FB
++:1012D00000B870000000FFFF00F07000000F000079
++:1012E00000307100008168AC0F38710000110000FF
++:1012F00000B871000004000000D07100000100007F
++:1013000000E871000002000000F071000000000021
++:1013100004F87100000000000418720000050000CD
++:10132000002072000052000000487200000100001E
++:1013300000D07200000000803FD8720000000080E2
++:101340003FE07200000000803FE872000000008073
++:101350003FF07200000000803FF872000000008043
++:101360003F007300000000803F0873000000008011
++:101370003F107300000000803F18730000000080E1
++:101380003F207300000000803F28730000000080B1
++:101390003F307300000000803F3873000000008081
++:1013A0003F407300000000803F4873000000008051
++:1013B0003F507300001000000088740000120C10F1
++:1013C000089074000005000000A8740000010000EF
++:1013D00000B8740000FFFF0000C0740000FFFF00B1
++:1013E00000C8740000FFFF0000D0740000FFFF0081
++:1013F00000D874000003000000F874000000FFFF34
++:1014000000007500001A00000010750000030000C5
++:1014100000F87600000201000008770000040000D8
++:10142000001077000004000000187700000400009E
++:10143000002077000004000000687700000400002E
++:1014400000707700000400000080770000FF0700B4
++:10145000009077000002010000E077000004000027
++:1014600000E877000004000000F0770000040000AE
++:1014700000F8770000040000009AE6000004000075
++:1014800000A2E6000004000000AAE60000120C1012
++:1014900008B2E6000003000000C2E60000120C10D3
++:1014A00008D2E60000140C0800DAE6000001000093
++:1014B00000E2E6000004000000EAE600000400008C
++:1014C00000F2E60000140C08000AE70000120C10FD
++:1014D0000812E70000270000002AE70000010000D2
++:1014E000003A05010001000000F2050100120C1095
++:1014F0000800060100120C10081006010080000010
++:10150000001806010004700080200601000004009D
++:101510000428060100C00000003006010000100091
++:101520000048060100000E000050060100001E00E9
++:1015300000580601000100000070060100010000D3
++:10154000008006010004000000880601000200007F
++:1015500000F006010080000000F8060100047000A1
++:1015600080000701000004000408070100C000001B
++:1015700000100701000010000028070100000E0005
++:101580000030070100001E000038070100010000C4
++:101590000050070100010000006007010004000086
++:1015A000006807010002000000C8070100120C10CB
++:1015B00008D807010021FE010008080100FFFF0014
++:1015C0000010080100FFFF000018080100FFFF00E5
++:1015D0000020080100FFFF000028080100010000B2
++:1015E000003008010001000100380801000100017D
++:1015F0000040080100010000005008010021FE0128
++:101600000080080100120C10088808010004000086
++:10161000009808010002000000A00801001100006D
++:1016200000E80801008168AC0F1009010004000007
++:1016300000600901000200000068090100010000CB
++:10164000007009010001000000780901000200009B
++:10165000008009010001000000880901000100006C
++:10166000009009010001000000A009010004000031
++:1016700000C026010011000000D02601000100007A
++:011680000069
++:00000001FF
+--
+1.6.5.2
+
diff --git a/drm-nouveau-kconfig.patch b/drm-nouveau-kconfig.patch
new file mode 100644
index 0000000..208400b
--- /dev/null
+++ b/drm-nouveau-kconfig.patch
@@ -0,0 +1,11 @@
+--- a/drivers/staging/Kconfig 2010-01-12 13:37:11.000000000 +1000
++++ b/drivers/staging/Kconfig 2010-01-12 13:37:24.000000000 +1000
+@@ -103,6 +103,8 @@
+
+ source "drivers/staging/line6/Kconfig"
+
++source "drivers/gpu/drm/nouveau/Kconfig"
++
+ source "drivers/staging/octeon/Kconfig"
+
+ source "drivers/staging/serqt_usb2/Kconfig"
diff --git a/drm-nouveau-mutex.patch b/drm-nouveau-mutex.patch
new file mode 100644
index 0000000..4bc1fc1
--- /dev/null
+++ b/drm-nouveau-mutex.patch
@@ -0,0 +1,56 @@
+From 967c89306de560a6da9539d24a0d63cb036d58ed Mon Sep 17 00:00:00 2001
+From: Ben Skeggs <bskeggs@redhat.com>
+Date: Tue, 16 Feb 2010 11:14:14 +1000
+Subject: [PATCH] drm/nouveau: use mutex for vbios lock
+
+Signed-off-by: Ben Skeggs <bskeggs@redhat.com>
+---
+ drivers/gpu/drm/nouveau/nouveau_bios.c | 7 +++----
+ drivers/gpu/drm/nouveau/nouveau_bios.h | 2 +-
+ 2 files changed, 4 insertions(+), 5 deletions(-)
+
+diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
+index 2cd0fad..0e9cd1d 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
++++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
+@@ -5861,13 +5861,12 @@ nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table,
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nvbios *bios = &dev_priv->VBIOS;
+ struct init_exec iexec = { true, false };
+- unsigned long flags;
+
+- spin_lock_irqsave(&bios->lock, flags);
++ mutex_lock(&bios->lock);
+ bios->display.output = dcbent;
+ parse_init_table(bios, table, &iexec);
+ bios->display.output = NULL;
+- spin_unlock_irqrestore(&bios->lock, flags);
++ mutex_unlock(&bios->lock);
+ }
+
+ static bool NVInitVBIOS(struct drm_device *dev)
+@@ -5876,7 +5875,7 @@ static bool NVInitVBIOS(struct drm_device *dev)
+ struct nvbios *bios = &dev_priv->VBIOS;
+
+ memset(bios, 0, sizeof(struct nvbios));
+- spin_lock_init(&bios->lock);
++ mutex_init(&bios->lock);
+ bios->dev = dev;
+
+ if (!NVShadowVBIOS(dev, bios->data))
+diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h
+index 68446fd..fd94bd6 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_bios.h
++++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
+@@ -205,7 +205,7 @@ struct nvbios {
+ struct drm_device *dev;
+ struct nouveau_bios_info pub;
+
+- spinlock_t lock;
++ struct mutex lock;
+
+ uint8_t data[NV_PROM_SIZE];
+ unsigned int length;
+--
+1.6.6.1
+
diff --git a/drm-nouveau-safetile-getparam.patch b/drm-nouveau-safetile-getparam.patch
new file mode 100644
index 0000000..09f9734
--- /dev/null
+++ b/drm-nouveau-safetile-getparam.patch
@@ -0,0 +1,26 @@
+From 2aa78d8442bd947637ed81da00fd9c22232d2ed0 Mon Sep 17 00:00:00 2001
+From: Ben Skeggs <bskeggs@redhat.com>
+Date: Wed, 9 Sep 2009 16:16:44 +1000
+Subject: [PATCH 5/6] f12: add getparam to know scanout tile_flags is safe
+
+---
+ drivers/gpu/drm/nouveau/nouveau_state.c | 3 +++
+ 1 files changed, 3 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
+index 36f8268..27c4d48 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_state.c
++++ b/drivers/gpu/drm/nouveau/nouveau_state.c
+@@ -861,6 +861,9 @@ int nouveau_ioctl_getparam(struct drm_device *dev, void *data,
+
+ getparam->value = dev_priv->vm_vram_base;
+ break;
++ case 0xdeadcafe00000003: /* NOUVEAU_GETPARAM_SCANOUT_TILEFLAGS */
++ getparam->value = 1;
++ break;
+ case NOUVEAU_GETPARAM_GRAPH_UNITS:
+ /* NV40 and NV50 versions are quite different, but register
+ * address is the same. User is supposed to know the card
+--
+1.6.5.2
+
diff --git a/drm-nouveau-tvout-disable.patch b/drm-nouveau-tvout-disable.patch
new file mode 100644
index 0000000..b04de64
--- /dev/null
+++ b/drm-nouveau-tvout-disable.patch
@@ -0,0 +1,57 @@
+From 1c48b3294c0efe6b28ef84139eb838725bb34ccc Mon Sep 17 00:00:00 2001
+From: Ben Skeggs <bskeggs@redhat.com>
+Date: Tue, 8 Sep 2009 13:57:50 +1000
+Subject: [PATCH 4/6] drm/nouveau: disable tv-out by default for the moment
+
+---
+ drivers/gpu/drm/nouveau/nouveau_drv.c | 4 ++++
+ drivers/gpu/drm/nouveau/nouveau_drv.h | 1 +
+ drivers/gpu/drm/nouveau/nv04_display.c | 5 +++++
+ 3 files changed, 10 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c
+index 06eb993..d117ab5 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_drv.c
++++ b/drivers/gpu/drm/nouveau/nouveau_drv.c
+@@ -71,6 +71,10 @@ MODULE_PARM_DESC(uscript_tmds, "TMDS output script table ID (>=GeForce 8)");
+ int nouveau_nofbaccel = 0;
+ module_param_named(nofbaccel, nouveau_nofbaccel, int, 0400);
+
++MODULE_PARM_DESC(tv, "Enable TV-out support (<GeForce 8)");
++int nouveau_tv = 0;
++module_param_named(tv, nouveau_tv, int, 0400);
++
+ MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
+ "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, NTSC-M, NTSC-J,\n"
+ "\t\t\thd480i, hd480p, hd576i, hd576p, hd720p, hd1080i.\n"
+diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
+index bf9acc6..0941725 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
++++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
+@@ -648,6 +648,7 @@ extern int nouveau_uscript_tmds;
+ extern int nouveau_vram_pushbuf;
+ extern int nouveau_vram_notify;
+ extern int nouveau_fbpercrtc;
++extern int nouveau_tv;
+ extern char *nouveau_tv_norm;
+ extern int nouveau_reg_debug;
+ extern char *nouveau_vbios;
+diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c
+index ef77215..dfae228 100644
+--- a/drivers/gpu/drm/nouveau/nv04_display.c
++++ b/drivers/gpu/drm/nouveau/nv04_display.c
+@@ -142,6 +142,11 @@ nv04_display_create(struct drm_device *dev)
+ ret = nv04_dfp_create(dev, dcbent);
+ break;
+ case OUTPUT_TV:
++ if (!nouveau_tv) {
++ NV_INFO(dev, "Enable TV-Out with tv module option\n");
++ continue;
++ }
++
+ if (dcbent->location == DCB_LOC_ON_CHIP)
+ ret = nv17_tv_create(dev, dcbent);
+ else
+--
+1.6.5.2
+
diff --git a/drm-nouveau-update.patch b/drm-nouveau-update.patch
new file mode 100644
index 0000000..6eaa1e1
--- /dev/null
+++ b/drm-nouveau-update.patch
@@ -0,0 +1,306 @@
+diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
+index 5445cef..1c15ef3 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_drv.h
++++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
+@@ -583,6 +583,7 @@ struct drm_nouveau_private {
+ uint64_t vm_end;
+ struct nouveau_gpuobj *vm_vram_pt[NV50_VM_VRAM_NR];
+ int vm_vram_pt_nr;
++ uint64_t vram_sys_base;
+
+ /* the mtrr covering the FB */
+ int fb_mtrr;
+diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
+index 8f3a12f..2dc09db 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_mem.c
++++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
+@@ -285,53 +285,50 @@ nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size,
+ uint32_t flags, uint64_t phys)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+- struct nouveau_gpuobj **pgt;
+- unsigned psz, pfl, pages;
+-
+- if (virt >= dev_priv->vm_gart_base &&
+- (virt + size) < (dev_priv->vm_gart_base + dev_priv->vm_gart_size)) {
+- psz = 12;
+- pgt = &dev_priv->gart_info.sg_ctxdma;
+- pfl = 0x21;
+- virt -= dev_priv->vm_gart_base;
+- } else
+- if (virt >= dev_priv->vm_vram_base &&
+- (virt + size) < (dev_priv->vm_vram_base + dev_priv->vm_vram_size)) {
+- psz = 16;
+- pgt = dev_priv->vm_vram_pt;
+- pfl = 0x01;
+- virt -= dev_priv->vm_vram_base;
+- } else {
+- NV_ERROR(dev, "Invalid address: 0x%16llx-0x%16llx\n",
+- virt, virt + size - 1);
+- return -EINVAL;
+- }
++ struct nouveau_gpuobj *pgt;
++ unsigned block;
++ int i;
+
+- pages = size >> psz;
++ virt = ((virt - dev_priv->vm_vram_base) >> 16) << 1;
++ size = (size >> 16) << 1;
++
++ phys |= ((uint64_t)flags << 32);
++ phys |= 1;
++ if (dev_priv->vram_sys_base) {
++ phys += dev_priv->vram_sys_base;
++ phys |= 0x30;
++ }
+
+ dev_priv->engine.instmem.prepare_access(dev, true);
+- if (flags & 0x80000000) {
+- while (pages--) {
+- struct nouveau_gpuobj *pt = pgt[virt >> 29];
+- unsigned pte = ((virt & 0x1fffffffULL) >> psz) << 1;
++ while (size) {
++ unsigned offset_h = upper_32_bits(phys);
++ unsigned offset_l = lower_32_bits(phys);
++ unsigned pte, end;
++
++ for (i = 7; i >= 0; i--) {
++ block = 1 << (i + 1);
++ if (size >= block && !(virt & (block - 1)))
++ break;
++ }
++ offset_l |= (i << 7);
+
+- nv_wo32(dev, pt, pte++, 0x00000000);
+- nv_wo32(dev, pt, pte++, 0x00000000);
++ phys += block << 15;
++ size -= block;
+
+- virt += (1 << psz);
+- }
+- } else {
+- while (pages--) {
+- struct nouveau_gpuobj *pt = pgt[virt >> 29];
+- unsigned pte = ((virt & 0x1fffffffULL) >> psz) << 1;
+- unsigned offset_h = upper_32_bits(phys) & 0xff;
+- unsigned offset_l = lower_32_bits(phys);
++ while (block) {
++ pgt = dev_priv->vm_vram_pt[virt >> 14];
++ pte = virt & 0x3ffe;
+
+- nv_wo32(dev, pt, pte++, offset_l | pfl);
+- nv_wo32(dev, pt, pte++, offset_h | flags);
++ end = pte + block;
++ if (end > 16384)
++ end = 16384;
++ block -= (end - pte);
++ virt += (end - pte);
+
+- phys += (1 << psz);
+- virt += (1 << psz);
++ while (pte < end) {
++ nv_wo32(dev, pgt, pte++, offset_l);
++ nv_wo32(dev, pgt, pte++, offset_h);
++ }
+ }
+ }
+ dev_priv->engine.instmem.finish_access(dev);
+@@ -356,7 +353,41 @@ nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size,
+ void
+ nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size)
+ {
+- nv50_mem_vm_bind_linear(dev, virt, size, 0x80000000, 0);
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *pgt;
++ unsigned pages, pte, end;
++
++ virt -= dev_priv->vm_vram_base;
++ pages = (size >> 16) << 1;
++
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ while (pages) {
++ pgt = dev_priv->vm_vram_pt[virt >> 29];
++ pte = (virt & 0x1ffe0000ULL) >> 15;
++
++ end = pte + pages;
++ if (end > 16384)
++ end = 16384;
++ pages -= (end - pte);
++ virt += (end - pte) << 15;
++
++ while (pte < end)
++ nv_wo32(dev, pgt, pte++, 0);
++ }
++ dev_priv->engine.instmem.finish_access(dev);
++
++ nv_wr32(dev, 0x100c80, 0x00050001);
++ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
++ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
++ NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
++ return;
++ }
++
++ nv_wr32(dev, 0x100c80, 0x00000001);
++ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
++ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
++ NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
++ }
+ }
+
+ /*
+diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c b/drivers/gpu/drm/nouveau/nv04_dac.c
+index d0e038d..1d73b15 100644
+--- a/drivers/gpu/drm/nouveau/nv04_dac.c
++++ b/drivers/gpu/drm/nouveau/nv04_dac.c
+@@ -119,7 +119,7 @@ static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder,
+ struct drm_connector *connector)
+ {
+ struct drm_device *dev = encoder->dev;
+- uint8_t saved_seq1, saved_pi, saved_rpc1;
++ uint8_t saved_seq1, saved_pi, saved_rpc1, saved_cr_mode;
+ uint8_t saved_palette0[3], saved_palette_mask;
+ uint32_t saved_rtest_ctrl, saved_rgen_ctrl;
+ int i;
+@@ -135,6 +135,9 @@ static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder,
+ /* only implemented for head A for now */
+ NVSetOwner(dev, 0);
+
++ saved_cr_mode = NVReadVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX);
++ NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode | 0x80);
++
+ saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX);
+ NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20);
+
+@@ -203,6 +206,7 @@ out:
+ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi);
+ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1);
+ NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1);
++ NVWriteVgaCrtc(dev, 0, NV_CIO_CR_MODE_INDEX, saved_cr_mode);
+
+ if (blue == 0x18) {
+ NV_INFO(dev, "Load detected on head A\n");
+diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c
+index 94400f7..f0dc4e3 100644
+--- a/drivers/gpu/drm/nouveau/nv50_instmem.c
++++ b/drivers/gpu/drm/nouveau/nv50_instmem.c
+@@ -76,6 +76,11 @@ nv50_instmem_init(struct drm_device *dev)
+ for (i = 0x1700; i <= 0x1710; i += 4)
+ priv->save1700[(i-0x1700)/4] = nv_rd32(dev, i);
+
++ if (dev_priv->chipset == 0xaa || dev_priv->chipset == 0xac)
++ dev_priv->vram_sys_base = nv_rd32(dev, 0x100e10) << 12;
++ else
++ dev_priv->vram_sys_base = 0;
++
+ /* Reserve the last MiB of VRAM, we should probably try to avoid
+ * setting up the below tables over the top of the VBIOS image at
+ * some point.
+@@ -172,16 +177,28 @@ nv50_instmem_init(struct drm_device *dev)
+ * We map the entire fake channel into the start of the PRAMIN BAR
+ */
+ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pt_size, 0x1000,
+- 0, &priv->pramin_pt);
++ 0, &priv->pramin_pt);
+ if (ret)
+ return ret;
+
+- for (i = 0, v = c_offset; i < pt_size; i += 8, v += 0x1000) {
+- if (v < (c_offset + c_size))
+- BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, v | 1);
+- else
+- BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, 0x00000009);
++ v = c_offset | 1;
++ if (dev_priv->vram_sys_base) {
++ v += dev_priv->vram_sys_base;
++ v |= 0x30;
++ }
++
++ i = 0;
++ while (v < dev_priv->vram_sys_base + c_offset + c_size) {
++ BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, v);
++ BAR0_WI32(priv->pramin_pt->gpuobj, i + 4, 0x00000000);
++ v += 0x1000;
++ i += 8;
++ }
++
++ while (i < pt_size) {
++ BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, 0x00000000);
+ BAR0_WI32(priv->pramin_pt->gpuobj, i + 4, 0x00000000);
++ i += 8;
+ }
+
+ BAR0_WI32(chan->vm_pd, 0x00, priv->pramin_pt->instance | 0x63);
+@@ -416,7 +433,9 @@ nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+ {
+ struct drm_nouveau_private *dev_priv = dev->dev_private;
+ struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
+- uint32_t pte, pte_end, vram;
++ struct nouveau_gpuobj *pramin_pt = priv->pramin_pt->gpuobj;
++ uint32_t pte, pte_end;
++ uint64_t vram;
+
+ if (!gpuobj->im_backing || !gpuobj->im_pramin || gpuobj->im_bound)
+ return -EINVAL;
+@@ -424,20 +443,24 @@ nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+ NV_DEBUG(dev, "st=0x%0llx sz=0x%0llx\n",
+ gpuobj->im_pramin->start, gpuobj->im_pramin->size);
+
+- pte = (gpuobj->im_pramin->start >> 12) << 3;
+- pte_end = ((gpuobj->im_pramin->size >> 12) << 3) + pte;
++ pte = (gpuobj->im_pramin->start >> 12) << 1;
++ pte_end = ((gpuobj->im_pramin->size >> 12) << 1) + pte;
+ vram = gpuobj->im_backing_start;
+
+ NV_DEBUG(dev, "pramin=0x%llx, pte=%d, pte_end=%d\n",
+ gpuobj->im_pramin->start, pte, pte_end);
+ NV_DEBUG(dev, "first vram page: 0x%08x\n", gpuobj->im_backing_start);
+
++ vram |= 1;
++ if (dev_priv->vram_sys_base) {
++ vram += dev_priv->vram_sys_base;
++ vram |= 0x30;
++ }
++
+ dev_priv->engine.instmem.prepare_access(dev, true);
+ while (pte < pte_end) {
+- nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 0)/4, vram | 1);
+- nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 4)/4, 0x00000000);
+-
+- pte += 8;
++ nv_wo32(dev, pramin_pt, pte++, lower_32_bits(vram));
++ nv_wo32(dev, pramin_pt, pte++, upper_32_bits(vram));
+ vram += NV50_INSTMEM_PAGE_SIZE;
+ }
+ dev_priv->engine.instmem.finish_access(dev);
+@@ -470,14 +493,13 @@ nv50_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
+ if (gpuobj->im_bound == 0)
+ return -EINVAL;
+
+- pte = (gpuobj->im_pramin->start >> 12) << 3;
+- pte_end = ((gpuobj->im_pramin->size >> 12) << 3) + pte;
++ pte = (gpuobj->im_pramin->start >> 12) << 1;
++ pte_end = ((gpuobj->im_pramin->size >> 12) << 1) + pte;
+
+ dev_priv->engine.instmem.prepare_access(dev, true);
+ while (pte < pte_end) {
+- nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 0)/4, 0x00000009);
+- nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 4)/4, 0x00000000);
+- pte += 8;
++ nv_wo32(dev, priv->pramin_pt->gpuobj, pte++, 0x00000000);
++ nv_wo32(dev, priv->pramin_pt->gpuobj, pte++, 0x00000000);
+ }
+ dev_priv->engine.instmem.finish_access(dev);
+
+diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
+index bcf843f..71247da 100644
+--- a/drivers/gpu/drm/nouveau/nouveau_bios.c
++++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
+@@ -3726,7 +3726,7 @@ nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent,
+ }
+ table = &bios->data[bios->display.dp_table_ptr];
+
+- if (table[0] != 0x21) {
++ if (table[0] != 0x20 && table[0] != 0x21) {
+ NV_ERROR(dev, "DisplayPort table version 0x%02x unknown\n",
+ table[0]);
+ return NULL;
diff --git a/drm-radeon-pm.patch b/drm-radeon-pm.patch
new file mode 100644
index 0000000..df12ec8
--- /dev/null
+++ b/drm-radeon-pm.patch
@@ -0,0 +1,586 @@
+diff -up linux-2.6.28.x86_64/drivers/gpu/drm/radeon/atombios_crtc.c.mjg linux-2.6.28.x86_64/drivers/gpu/drm/radeon/atombios_crtc.c
+--- linux-2.6.28.x86_64/drivers/gpu/drm/radeon/atombios_crtc.c.mjg 2009-03-03 19:41:48.000000000 +0000
++++ linux-2.6.28.x86_64/drivers/gpu/drm/radeon/atombios_crtc.c 2009-03-03 20:53:05.000000000 +0000
+@@ -441,14 +441,23 @@ static bool atombios_crtc_mode_fixup(str
+
+ static void atombios_crtc_prepare(struct drm_crtc *crtc)
+ {
++ struct drm_device *dev = crtc->dev;
++ struct drm_radeon_private *dev_priv = dev->dev_private;
++
++ mutex_lock(&dev_priv->mode_info.power.pll_mutex);
++
+ atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+ atombios_lock_crtc(crtc, 1);
+ }
+
+ static void atombios_crtc_commit(struct drm_crtc *crtc)
+ {
++ struct drm_device *dev = crtc->dev;
++ struct drm_radeon_private *dev_priv = dev->dev_private;
++
+ atombios_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
+ atombios_lock_crtc(crtc, 0);
++ mutex_unlock(&dev_priv->mode_info.power.pll_mutex);
+ }
+
+ static const struct drm_crtc_helper_funcs atombios_helper_funcs = {
+diff -up linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_atombios.c.mjg linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_atombios.c
+--- linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_atombios.c.mjg 2009-03-03 19:41:48.000000000 +0000
++++ linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_atombios.c 2009-03-03 20:53:05.000000000 +0000
+@@ -620,6 +620,34 @@ void radeon_atom_static_pwrmgt_setup(str
+ atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args);
+ }
+
++void radeon_atom_get_mc_arb_info(struct drm_device *dev)
++{
++ struct drm_radeon_private *dev_priv = dev->dev_private;
++ struct radeon_mode_info *mode_info = &dev_priv->mode_info;
++ struct atom_context *ctx = mode_info->atom_context;
++ int index = GetIndexIntoMasterTable(DATA, MC_InitParameter);
++ uint8_t frev, crev;
++ uint16_t size, data_offset;
++
++ atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset);
++ dev_priv->mode_info.power.mc_arb_init_values =
++ kmalloc(size*sizeof(int), GFP_KERNEL);
++ memcpy(dev_priv->mode_info.power.mc_arb_init_values,
++ ctx->bios + data_offset, size * sizeof(int));
++}
++
++void radeon_atom_get_engine_clock(struct drm_device *dev, int *engine_clock)
++{
++ struct drm_radeon_private *dev_priv = dev->dev_private;
++ struct radeon_mode_info *mode_info = &dev_priv->mode_info;
++ struct atom_context *ctx = mode_info->atom_context;
++ GET_ENGINE_CLOCK_PS_ALLOCATION args;
++ int index = GetIndexIntoMasterTable(COMMAND, GetEngineClock);
++
++ atom_execute_table(ctx, index, (uint32_t *)&args);
++ *engine_clock = args.ulReturnEngineClock;
++}
++
+ void radeon_atom_set_engine_clock(struct drm_device *dev, int eng_clock)
+ {
+ struct drm_radeon_private *dev_priv = dev->dev_private;
+@@ -633,6 +661,18 @@ void radeon_atom_set_engine_clock(struct
+ atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args);
+ }
+
++void radeon_atom_get_memory_clock(struct drm_device *dev, int *mem_clock)
++{
++ struct drm_radeon_private *dev_priv = dev->dev_private;
++ struct radeon_mode_info *mode_info = &dev_priv->mode_info;
++ struct atom_context *ctx = mode_info->atom_context;
++ GET_MEMORY_CLOCK_PS_ALLOCATION args;
++ int index = GetIndexIntoMasterTable(COMMAND, GetMemoryClock);
++
++ atom_execute_table(ctx, index, (uint32_t *)&args);
++ *mem_clock = args.ulReturnMemoryClock;
++}
++
+ void radeon_atom_set_memory_clock(struct drm_device *dev, int mem_clock)
+ {
+ struct drm_radeon_private *dev_priv = dev->dev_private;
+@@ -646,6 +686,16 @@ void radeon_atom_set_memory_clock(struct
+ atom_execute_table(dev_priv->mode_info.atom_context, index, (uint32_t *)&args);
+ }
+
++void radeon_atom_initialize_memory_controller(struct drm_device *dev)
++{
++ struct drm_radeon_private *dev_priv = dev->dev_private;
++ struct atom_context *ctx = dev_priv->mode_info.atom_context;
++ int index = GetIndexIntoMasterTable(COMMAND, MemoryDeviceInit);
++ MEMORY_PLLINIT_PS_ALLOCATION args;
++
++ atom_execute_table(ctx, index, (uint32_t *)&args);
++}
++
+ void radeon_atom_initialize_bios_scratch_regs(struct drm_device *dev)
+ {
+ struct drm_radeon_private *dev_priv = dev->dev_private;
+diff -up linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_cp.c.mjg linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_cp.c
+--- linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_cp.c.mjg 2009-03-03 19:41:48.000000000 +0000
++++ linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_cp.c 2009-03-03 20:53:05.000000000 +0000
+@@ -3223,6 +3223,8 @@ int radeon_driver_load(struct drm_device
+ if (ret)
+ goto modeset_fail;
+
++ mutex_init(&dev_priv->mode_info.power.pll_mutex);
++
+ radeon_modeset_init(dev);
+
+ radeon_modeset_cp_init(dev);
+@@ -3231,7 +3233,7 @@ int radeon_driver_load(struct drm_device
+ drm_irq_install(dev);
+ }
+
+-
++ radeon_pm_init(dev);
+ return ret;
+ modeset_fail:
+ dev->driver->driver_features &= ~DRIVER_MODESET;
+@@ -3303,6 +3305,8 @@ int radeon_driver_unload(struct drm_devi
+ {
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+
++ radeon_pm_exit(dev);
++
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ drm_irq_uninstall(dev);
+ radeon_modeset_cleanup(dev);
+diff -up linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_cs.c.mjg linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_cs.c
+--- linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_cs.c.mjg 2009-03-03 19:41:48.000000000 +0000
++++ linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_cs.c 2009-03-03 20:53:05.000000000 +0000
+@@ -41,6 +41,8 @@ int radeon_cs_ioctl(struct drm_device *d
+ long size;
+ int r, i;
+
++ radeon_pm_timer_reset(dev);
++
+ mutex_lock(&dev_priv->cs.cs_mutex);
+ /* set command stream id to 0 which is fake id */
+ cs_id = 0;
+diff -up linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_drv.h.mjg linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_drv.h
+--- linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_drv.h.mjg 2009-03-03 19:41:48.000000000 +0000
++++ linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_drv.h 2009-03-03 20:53:05.000000000 +0000
+@@ -612,6 +612,9 @@ extern int radeon_modeset_cp_resume(stru
+ /* radeon_pm.c */
+ int radeon_suspend(struct drm_device *dev, pm_message_t state);
+ int radeon_resume(struct drm_device *dev);
++void radeon_pm_init(struct drm_device *dev);
++void radeon_pm_exit(struct drm_device *dev);
++void radeon_pm_timer_reset(struct drm_device *dev);
+
+ /* Flags for stats.boxes
+ */
+diff -up linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_irq.c.mjg linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_irq.c
+--- linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_irq.c.mjg 2009-03-03 19:41:48.000000000 +0000
++++ linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_irq.c 2009-03-03 20:53:05.000000000 +0000
+@@ -185,8 +185,10 @@ irqreturn_t radeon_driver_irq_handler(DR
+ struct drm_device *dev = (struct drm_device *) arg;
+ drm_radeon_private_t *dev_priv =
+ (drm_radeon_private_t *) dev->dev_private;
++ struct radeon_powermanagement_info *power = &dev_priv->mode_info.power;
+ u32 stat;
+ u32 r500_disp_int;
++ unsigned long flags;
+
+ /* Only consider the bits we're interested in - others could be used
+ * outside the DRM
+@@ -206,15 +208,47 @@ irqreturn_t radeon_driver_irq_handler(DR
+
+ /* VBLANK interrupt */
+ if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_RS690) {
+- if (r500_disp_int & R500_D1_VBLANK_INTERRUPT)
++ if (r500_disp_int & R500_D1_VBLANK_INTERRUPT) {
++ spin_lock_irqsave(&power->power_lock, flags);
++ if (power->reclock_head & 1) {
++ power->reclock_head &= ~1;
++ schedule_work(&power->reclock_work);
++ drm_vblank_put(dev, 0);
++ }
++ spin_unlock_irqrestore(&power->power_lock, flags);
+ drm_handle_vblank(dev, 0);
+- if (r500_disp_int & R500_D2_VBLANK_INTERRUPT)
++ }
++ if (r500_disp_int & R500_D2_VBLANK_INTERRUPT) {
++ spin_lock_irqsave(&power->power_lock, flags);
++ if (power->reclock_head & 2) {
++ power->reclock_head &= ~2;
++ schedule_work(&power->reclock_work);
++ drm_vblank_put(dev, 1);
++ }
++ spin_unlock_irqrestore(&power->power_lock, flags);
+ drm_handle_vblank(dev, 1);
++ }
+ } else {
+- if (stat & RADEON_CRTC_VBLANK_STAT)
++ if (stat & RADEON_CRTC_VBLANK_STAT) {
++ spin_lock_irqsave(&power->power_lock, flags);
++ if (power->reclock_head & 1) {
++ power->reclock_head &= ~1;
++ schedule_work(&power->reclock_work);
++ drm_vblank_put(dev, 0);
++ }
++ spin_unlock_irqrestore(&power->power_lock, flags);
+ drm_handle_vblank(dev, 0);
+- if (stat & RADEON_CRTC2_VBLANK_STAT)
++ }
++ if (stat & RADEON_CRTC2_VBLANK_STAT) {
++ spin_lock_irqsave(&power->power_lock, flags);
++ if (power->reclock_head & 2) {
++ power->reclock_head &= ~2;
++ schedule_work(&power->reclock_work);
++ drm_vblank_put(dev, 1);
++ }
++ spin_unlock_irqrestore(&power->power_lock, flags);
+ drm_handle_vblank(dev, 1);
++ }
+ }
+ return IRQ_HANDLED;
+ }
+diff -up linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_mode.h.mjg linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_mode.h
+--- linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_mode.h.mjg 2009-03-03 19:41:48.000000000 +0000
++++ linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_mode.h 2009-03-03 20:53:05.000000000 +0000
+@@ -173,6 +173,22 @@ struct radeon_i2c_chan {
+ struct radeon_i2c_bus_rec rec;
+ };
+
++struct radeon_powermanagement_info {
++ struct timer_list idle_power_timer;
++ struct work_struct reclock_work;
++ struct drm_device *dev;
++ uint32_t orig_memory_clock;
++ uint32_t orig_engine_clock;
++ uint32_t *mc_arb_init_values;
++ uint8_t orig_fbdiv;
++ int new_mem_clock;
++ int new_engine_clock;
++ int current_clock_state;
++ int reclock_head;
++ struct mutex pll_mutex;
++ spinlock_t power_lock;
++};
++
+ struct radeon_mode_info {
+ struct atom_context *atom_context;
+ struct radeon_bios_connector bios_connector[RADEON_MAX_BIOS_CONNECTOR];
+@@ -182,6 +198,9 @@ struct radeon_mode_info {
+ struct radeon_pll mpll;
+ uint32_t mclk;
+ uint32_t sclk;
++
++ /* power management */
++ struct radeon_powermanagement_info power;
+ };
+
+ struct radeon_crtc {
+@@ -307,6 +326,12 @@ extern int radeon_crtc_cursor_move(struc
+
+ extern bool radeon_atom_get_clock_info(struct drm_device *dev);
+ extern bool radeon_combios_get_clock_info(struct drm_device *dev);
++extern void radeon_atom_get_engine_clock(struct drm_device *dev, int *engine_clock);
++extern void radeon_atom_get_memory_clock(struct drm_device *dev, int *memory_clock);
++extern void radeon_atom_set_engine_clock(struct drm_device *dev, int engine_clock);
++extern void radeon_atom_set_memory_clock(struct drm_device *dev, int memory_clock);
++extern void radeon_atom_initialize_memory_controller(struct drm_device *dev);
++extern void radeon_atom_get_mc_arb_info(struct drm_device *dev);
+ extern void radeon_atombios_get_lvds_info(struct radeon_encoder *encoder);
+ extern void radeon_atombios_get_tmds_info(struct radeon_encoder *encoder);
+ extern bool radeon_combios_get_lvds_info(struct radeon_encoder *encoder);
+diff -up linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_pm.c.mjg linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_pm.c
+--- linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_pm.c.mjg 2009-03-03 19:41:48.000000000 +0000
++++ linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_pm.c 2009-03-03 20:53:05.000000000 +0000
+@@ -31,6 +31,8 @@
+
+ #include "drm_crtc_helper.h"
+
++#define RADEON_DOWNCLOCK_IDLE_MS 30
++
+ int radeon_suspend(struct drm_device *dev, pm_message_t state)
+ {
+ struct drm_radeon_private *dev_priv = dev->dev_private;
+@@ -255,3 +257,214 @@ bool radeon_set_pcie_lanes(struct drm_de
+ return false;
+ }
+
++static void radeon_pm_set_engine_clock(struct drm_device *dev, int freq)
++{
++ drm_radeon_private_t *dev_priv = dev->dev_private;
++
++ if (dev_priv->is_atom_bios)
++ radeon_atom_set_engine_clock(dev, freq);
++}
++
++static void radeon_pm_set_memory_clock(struct drm_device *dev, int freq)
++{
++ drm_radeon_private_t *dev_priv = dev->dev_private;
++ struct radeon_powermanagement_info *power = &dev_priv->mode_info.power;
++
++ mutex_lock(&power->pll_mutex);
++ radeon_do_cp_idle(dev_priv);
++ if (dev_priv->is_atom_bios) {
++ int mpll, spll, hclk, sclk, fbdiv, index, factor;
++ switch (dev_priv->chip_family) {
++ case CHIP_R520:
++ case CHIP_RV530:
++ case CHIP_RV560:
++ case CHIP_RV570:
++ case CHIP_R580:
++ mpll = RADEON_READ_PLL(dev_priv, MPLL_FUNC_CNTL);
++ fbdiv = (mpll & 0x1fe0) >> 5;
++
++ /* Set new fbdiv */
++ factor = power->orig_memory_clock / freq;
++ fbdiv = power->orig_fbdiv / factor;
++
++ mpll &= ~0x1fe0;
++ mpll |= ((fbdiv << 5) | (1 << 24));
++ mpll &= ~(1 << 25);
++
++ spll = RADEON_READ_PLL(dev_priv, SPLL_FUNC_CNTL);
++
++ hclk = fbdiv << 5;
++ hclk += 0x20;
++ hclk *= 8;
++
++ sclk = spll & 0x1fe0;
++ sclk += 0x20;
++ sclk *= 6;
++ sclk = sclk >> 5;
++
++ index = (hclk/sclk);
++
++ R500_WRITE_MCIND(R530_MC_ARB_RATIO_CLK_SEQ,
++ power->mc_arb_init_values[index]);
++ RADEON_WRITE_PLL(dev_priv, MPLL_FUNC_CNTL, mpll);
++ radeon_atom_initialize_memory_controller(dev);
++ break;
++ }
++ }
++
++ mutex_unlock(&power->pll_mutex);
++}
++
++static int radeon_pm_get_active_crtcs(struct drm_device *dev, int *crtcs)
++{
++ struct drm_crtc *crtc;
++ int count = 0;
++ struct radeon_crtc *radeon_crtc;
++
++ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
++ radeon_crtc = to_radeon_crtc(crtc);
++ if (crtc->enabled) {
++ count++;
++ *crtcs |= (1 << radeon_crtc->crtc_id);
++ }
++ }
++ return count;
++}
++
++
++static void radeon_pm_perform_transition(struct drm_device *dev)
++{
++ drm_radeon_private_t *dev_priv = dev->dev_private;
++ struct radeon_powermanagement_info *power = &dev_priv->mode_info.power;
++ int crtcs = 0, count;
++ unsigned long flags;
++
++ count = radeon_pm_get_active_crtcs(dev, &crtcs);
++
++ spin_lock_irqsave(&power->power_lock, flags);
++ switch (count) {
++ case 0:
++ schedule_work(&power->reclock_work);
++ break;
++ case 1:
++ if (power->reclock_head)
++ break;
++ if (crtcs & 1) {
++ power->reclock_head |= 1;
++ drm_vblank_get(dev, 0);
++ } else {
++ power->reclock_head |= 2;
++ drm_vblank_get(dev, 1);
++ }
++ break;
++ default:
++ /* Too many active heads */
++ break;
++ }
++ spin_unlock_irqrestore(&power->power_lock, flags);
++}
++
++
++static int radeon_pm_set_runtime_power(struct drm_device *dev, int value)
++{
++ drm_radeon_private_t *dev_priv = dev->dev_private;
++ struct radeon_powermanagement_info *power = &dev_priv->mode_info.power;
++
++ if (power->current_clock_state == value)
++ return 1;
++
++ switch (value) {
++ case 0:
++ power->new_engine_clock = 100*100;
++ power->new_mem_clock = 100*100;
++ break;
++ case 1:
++ power->new_engine_clock = power->orig_engine_clock;
++ power->new_mem_clock = power->orig_memory_clock;
++ break;
++ }
++
++ power->current_clock_state = value;
++ radeon_pm_perform_transition(dev);
++
++ return 0;
++}
++
++static void radeon_pm_idle_timeout(unsigned long d)
++{
++ struct drm_device *dev = (struct drm_device *)d;
++ drm_radeon_private_t *dev_priv = dev->dev_private;
++
++ radeon_pm_set_runtime_power(dev, 0);
++}
++
++static void radeon_pm_reclock_callback(struct work_struct *work)
++{
++ struct radeon_powermanagement_info *power =
++ container_of(work, struct radeon_powermanagement_info,
++ reclock_work);
++ struct drm_device *dev = power->dev;
++ drm_radeon_private_t *dev_priv = dev->dev_private;
++
++ mutex_lock(&dev_priv->cs.cs_mutex);
++ radeon_pm_set_memory_clock(dev, power->new_mem_clock);
++ radeon_pm_set_engine_clock(dev, power->new_engine_clock);
++ mutex_unlock(&dev_priv->cs.cs_mutex);
++}
++
++void radeon_pm_timer_reset(struct drm_device *dev)
++{
++ drm_radeon_private_t *dev_priv = dev->dev_private;
++ struct radeon_powermanagement_info *power = &dev_priv->mode_info.power;
++
++ if (!drm_core_check_feature(dev, DRIVER_MODESET))
++ return;
++
++ radeon_pm_set_runtime_power(dev, 1);
++
++ mod_timer(&power->idle_power_timer,
++ jiffies + msecs_to_jiffies(RADEON_DOWNCLOCK_IDLE_MS));
++}
++
++void radeon_pm_init(struct drm_device *dev)
++{
++ drm_radeon_private_t *dev_priv = dev->dev_private;
++ struct radeon_powermanagement_info *power = &dev_priv->mode_info.power;
++
++ power->dev = dev;
++
++ if (!drm_core_check_feature(dev, DRIVER_MODESET))
++ return;
++
++ if (dev_priv->is_atom_bios) {
++ int mpll;
++ radeon_atom_get_mc_arb_info(dev);
++ radeon_atom_get_engine_clock(dev, &power->orig_engine_clock);
++ radeon_atom_get_memory_clock(dev, &power->orig_memory_clock);
++
++ mpll = RADEON_READ_PLL(dev_priv, MPLL_FUNC_CNTL);
++ dev_priv->mode_info.power.orig_fbdiv = (mpll & 0x1fe0) >> 5;
++ }
++
++ setup_timer(&power->idle_power_timer, radeon_pm_idle_timeout,
++ (unsigned long)dev);
++ INIT_WORK(&power->reclock_work, radeon_pm_reclock_callback);
++
++ spin_lock_init(&power->power_lock);
++
++ power->current_clock_state = 1;
++ power->reclock_head = 0;
++
++ radeon_pm_timer_reset(dev);
++}
++
++void radeon_pm_exit(struct drm_device *dev)
++{
++ drm_radeon_private_t *dev_priv = dev->dev_private;
++ struct radeon_powermanagement_info *power = &dev_priv->mode_info.power;
++
++ if (!drm_core_check_feature(dev, DRIVER_MODESET))
++ return;
++
++ del_timer_sync(&power->idle_power_timer);
++}
+diff -up linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_reg.h.mjg linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_reg.h
+--- linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_reg.h.mjg 2009-03-03 19:41:48.000000000 +0000
++++ linux-2.6.28.x86_64/drivers/gpu/drm/radeon/radeon_reg.h 2009-03-03 20:53:05.000000000 +0000
+@@ -303,6 +303,28 @@
+ # define RADEON_PLL_WR_EN (1 << 7)
+ # define RADEON_PLL_DIV_SEL (3 << 8)
+ # define RADEON_PLL2_DIV_SEL_MASK ~(3 << 8)
++#define SPLL_FUNC_CNTL 0x0000
++#define MPLL_FUNC_CNTL 0x0004
++#define GENERAL_PWRMGT 0x0008
++# define RADEON_GLOBAL_PWRMGT_EN (1 << 0)
++#define SCLK_PWRMGT_CNTL 0x0009
++# define RADEON_SCLK_PWRMGT_OFF (1 << 0)
++#define MCLK_PWRMGT_CNTL 0x000a
++# define RADEON_MCLK_PWRMGT_OFF (1 << 0)
++#define DYN_PWRMGT_SCLK_CNTL 0x000b
++# define RADEON_ENGINE_DYNCLK_MODE (1 << 0)
++# define RADEON_STATIC_SCREEN_EN (1 << 20)
++# define RADEON_CLIENT_SELECT_POWER_EN (1 << 21)
++#define DYN_SCLK_PWMEN_PIPE 0x000d
++# define RADEON_PIPE_3D_NOT_AUTO (1 << 8)
++#define DYN_SCLK_VOL_CNTL 0x000e
++# define RADEON_IO_CG_VOLTAGE_DROP (1 << 0)
++# define RADEON_VOLTAGE_DROP_SYNC (1 << 2)
++#define CP_DYN_CNTL 0x000f
++# define RADEON_CP_FORCEON (1 << 0)
++# define RADEON_CP_LOWER_POWER_IGNORE (1 << 20)
++# define RADEON_CP_NORMAL_POWER_IGNORE (1 << 21)
++# define RADEON_CP_NORMAL_POWER_BUSY (1 << 24)
+ #define RADEON_CLK_PWRMGT_CNTL 0x0014
+ # define RADEON_ENGIN_DYNCLK_MODE (1 << 12)
+ # define RADEON_ACTIVE_HILO_LAT_MASK (3 << 13)
+@@ -3961,7 +3983,48 @@
+ # define AVIVO_I2C_RESET (1 << 8)
+
+ #define R600_GENERAL_PWRMGT 0x618
++# define R600_GLOBAL_PWRMGT_EN (1 << 0)
++# define R600_STATIC_PM_EN (1 << 1)
++# define R600_MOBILE_SU (1 << 2)
++# define R600_THERMAL_PROTECTION_DIS (1 << 3)
++# define R600_THERMAL_PROTECTION_TYPE (1 << 4)
++# define R600_ENABLE_GEN2PCIE (1 << 5)
++# define R600_SW_GPIO_INDEX (1 << 6)
++# define R600_LOW_VOLT_D2_ACPI (1 << 8)
++# define R600_LOW_VOLT_D3_ACPI (1 << 9)
++# define R600_VOLT_PWRMGT_EN (1 << 10)
+ # define R600_OPEN_DRAIN_PADS (1 << 11)
++# define R600_AVP_SCLK_EN (1 << 12)
++# define R600_IDCT_SCLK_EN (1 << 13)
++# define R600_GPU_COUNTER_ACPI (1 << 14)
++# define R600_COUNTER_CLK (1 << 15)
++# define R600_BACKBIAS_PAD_EN (1 << 16)
++# define R600_BACKBIAS_VALUE (1 << 17)
++# define R600_BACKBIAS_DPM_CNTL (1 << 18)
++# define R600_SPREAD_SPECTRUM_INDEX (1 << 19)
++# define R600_DYN_SPREAD_SPECTRUM_EN (1 << 21)
++
++#define R600_SCLK_PWRMGT_CNTL 0x620
++# define R600_SCLK_PWRMGT_OFF (1 << 0)
++# define R600_SCLK_TURNOFF (1 << 1)
++# define R600_SPLL_TURNOFF (1 << 2)
++# define R600_SU_SCLK_USE_BCLK (1 << 3)
++# define R600_DYNAMIC_GFX_ISLAND_PWR_DOWN (1 << 4)
++# define R600_DYNAMIC_GFX_ISLAND_LP (1 << 5)
++# define R600_CLK_TURN_ON_STAGGER (1 << 6)
++# define R600_CLK_TURN_OFF_STAGGER (1 << 7)
++# define R600_FIR_FORCE_TREND_SEL (1 << 8)
++# define R600_FIR_TREND_MODE (1 << 9)
++# define R600_DYN_GFX_CLK_OFF_EN (1 << 10)
++# define R600_VDDC3D_TURNOFF_D1 (1 << 11)
++# define R600_VDDC3D_TURNOFF_D2 (1 << 12)
++# define R600_VDDC3D_TURNOFF_D3 (1 << 13)
++# define R600_SPLL_TURNOFF_D2 (1 << 14)
++# define R600_SCLK_LOW_D1 (1 << 15)
++# define R600_DYN_GFX_CLK_OFF_MC_EN (1 << 16)
++
++#define R600_MCLK_PWRMGT_CNTL 0x624
++# define R600_MPLL_PWRMGT_OFF (1 << 0)
+
+ #define R600_LOWER_GPIO_ENABLE 0x710
+ #define R600_CTXSW_VID_LOWER_GPIO_CNTL 0x718
+@@ -5331,5 +5394,6 @@
+ # define R500_RS_IP_OFFSET_EN (1 << 31)
+
+ #define R500_DYN_SCLK_PWMEM_PIPE 0x000d /* PLL */
++#define R530_MC_ARB_RATIO_CLK_SEQ 0x0016 /* MC */
+
+ #endif
diff --git a/drm-upgrayedd.patch b/drm-upgrayedd.patch
new file mode 100644
index 0000000..b6bb414
--- /dev/null
+++ b/drm-upgrayedd.patch
@@ -0,0 +1,85406 @@
+commit 8f6aa16e3a85c732763458c07db65035e75e5906
+Author: Jerome Glisse <jglisse@redhat.com>
+Date: Mon Feb 15 21:36:33 2010 +0100
+
+ drm/radeon/kms: fix bo's fence association
+
+ Previous code did associate fence to bo before the fence was emited
+ and it also didn't lock protected access to ttm sync_obj member.
+ Both of this flaw leads to possible race between different code
+ path. This patch fix this by associating fence only once the fence
+ is emitted and properly lock protect access to sync_obj member of
+ ttm.
+
+ Fix:
+ https://bugs.freedesktop.org/show_bug.cgi?id=26438
+ and likely similar others bugs
+ Signed-off-by: Jerome Glisse <jglisse@redhat.com>
+ Signed-off-by: Dave Airlie <airlied@redhat.com>
+
+commit 3a84c7bf19624ef6c5927eaf742adf346be1f34d
+Author: Jerome Glisse <jglisse@redhat.com>
+Date: Mon Feb 15 21:36:13 2010 +0100
+
+ drm/radeon/kms: fix indirect buffer management V2
+
+ There is 3 different distinct states for an indirect buffer (IB) :
+ 1- free with no fence
+ 2- free with a fence
+ 3- non free (fence doesn't matter)
+ Previous code mixed case 2 & 3 in a single one leading to possible
+ catastrophique failure. This patch rework the handling and properly
+ separate each case. So when you get ib we set the ib as non free and
+ fence status doesn't matter. Fence become active (ie has a meaning
+ for the ib code) once the ib is scheduled or free. This patch also
+ get rid of the alloc bitmap as it was overkill, we know go through
+ IB pool list like in a ring buffer as the oldest IB is the first
+ one the will be free.
+
+ Fix :
+ https://bugs.freedesktop.org/show_bug.cgi?id=26438
+ and likely other bugs.
+
+ V2 remove the scheduled list, it's useless now, fix free ib scanning
+
+ Signed-off-by: Jerome Glisse <jglisse@redhat.com>
+ Signed-off-by: Dave Airlie <airlied@redhat.com>
+
+commit c4716196dd78a7adfd400612d1c8465bf0bd3fb5
+Author: Dave Airlie <airlied@redhat.com>
+Date: Mon Feb 15 15:54:45 2010 +1000
+
+ drm/radeon/kms: flush HDP cache on GART table updates.
+
+ Suggested by Alex Deucher @ AMD
+
+ Signed-off-by: Dave Airlie <airlied@redhat.com>
+
+commit 0f5980d95024ff0cf2e495d977db9c26e1a6765b
+Author: Dave Airlie <airlied@redhat.com>
+Date: Thu Feb 11 15:38:23 2010 +1000
+
+ drm/radeon/kms: check for valid PCI bios and not OF rom
+
+ stops us trying to treat a OF rom as a PCI rom.
+
+ Signed-off-by: Dave Airlie <airlied@redhat.com>
+
+commit b1bd3bb9f2f5522415497a075313c8a2ba1cc16c
+Author: Dave Airlie <airlied@redhat.com>
+Date: Sun Jan 31 07:07:14 2010 +1000
+
+ drm/radeon/kms: use udelay for short delays
+
+ For usec delays use udelay instead of scheduling, this should
+ allow reclocking to happen faster.
+
+ Signed-off-by: Dave Airlie <airlied@redhat.com>
+
+commit ce2440648005a532bffd3fbe8590a085d8869620
+Author: Alex Deucher <alexdeucher@gmail.com>
+Date: Thu Feb 4 11:01:52 2010 -0500
+
+ drm/radeon/kms/r600: reduce gpu cache flushing
+
+ Only flush the gpu caches before we submit a fence.
+ This leads to a small performance boost when we take
+ the extra gpu cache flushes out of the ddx and mesa.
+ Once this patch is in and the drm version is bumped,
+ we can remove the flushes from the ddx and drm.
+
+ Also, remove the extra cache flushes from the blit
+ routine.
+
+ Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
+ Signed-off-by: Dave Airlie <airlied@redhat.com>
+
+commit 03b6599497a283874236163c63f8f50045e5f8f1
+Author: Alex Deucher <alexdeucher@gmail.com>
+Date: Fri Feb 5 04:21:19 2010 -0500
+
+ drm/radeon/kms: add support for hardcoded edids in rom (v2)
+
+ Some servers hardcode an edid in rom so that they will
+ work properly with KVMs. This is a port of the relevant
+ code from the ddx.
+
+ [airlied: reworked to validate edid at boot stage - and
+ remove special quirk, if there is a valid EDID in the BIOS rom
+ we'll just try and use it.]
+
+ Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
+ Signed-off-by: Dave Airlie <airlied@redhat.com>
+
+commit 92d87a03788e038b3028653e736d4c51090317ac
+Author: Alex Deucher <alexdeucher@gmail.com>
+Date: Fri Feb 5 03:34:16 2010 -0500
+
+ drm/radeon/kms: add workaround for rn50/rv100 servers
+
+ Some servers have two VGA ports but only report
+ one in the bios connector tables. On these systems
+ always set up the TV DAC so that it displays properly
+ even if the bios is wrong.
+
+ Signed-off-by: Alex Deucher <alexdeucher@gmail.com>
+ Signed-off-by: Dave Airlie <airlied@redhat.com>
+
+commit b3ba69acb361aa6396ad9e55c557fb9b47a0f43e
+Author: Dave Airlie <airlied@redhat.com>
+Date: Wed Feb 17 10:34:58 2010 +1000
+
+ drm: backport 2.6.33-rc8 drm
+
+commit ffff5f5e2d9de68dc454ec85bafa9da94f10f204
+Author: Dave Chinner <david@fromorbit.com>
+Date: Tue Jan 12 17:39:16 2010 +1100
+
+ lib: Introduce generic list_sort function
+
+ There are two copies of list_sort() in the tree already, one in the DRM
+ code, another in ubifs. Now XFS needs this as well. Create a generic
+ list_sort() function from the ubifs version and convert existing users
+ to it so we don't end up with yet another copy in the tree.
+
+ Signed-off-by: Dave Chinner <david@fromorbit.com>
+ Acked-by: Dave Airlie <airlied@redhat.com>
+ Acked-by: Artem Bityutskiy <dedekind@infradead.org>
+ Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+diff --git a/drivers/gpu/drm/Kconfig b/drivers/gpu/drm/Kconfig
+index 96eddd1..305c590 100644
+--- a/drivers/gpu/drm/Kconfig
++++ b/drivers/gpu/drm/Kconfig
+@@ -66,6 +66,8 @@ config DRM_RADEON
+
+ If M is selected, the module will be called radeon.
+
++source "drivers/gpu/drm/radeon/Kconfig"
++
+ config DRM_I810
+ tristate "Intel I810"
+ depends on DRM && AGP && AGP_INTEL
+diff --git a/drivers/gpu/drm/Makefile b/drivers/gpu/drm/Makefile
+index 3c8827a..39c5aa7 100644
+--- a/drivers/gpu/drm/Makefile
++++ b/drivers/gpu/drm/Makefile
+@@ -15,7 +15,7 @@ drm-y := drm_auth.o drm_bufs.o drm_cache.o \
+
+ drm-$(CONFIG_COMPAT) += drm_ioc32.o
+
+-drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o
++drm_kms_helper-y := drm_fb_helper.o drm_crtc_helper.o drm_dp_i2c_helper.o
+
+ obj-$(CONFIG_DRM_KMS_HELPER) += drm_kms_helper.o
+
+@@ -30,4 +30,7 @@ obj-$(CONFIG_DRM_I830) += i830/
+ obj-$(CONFIG_DRM_I915) += i915/
+ obj-$(CONFIG_DRM_SIS) += sis/
+ obj-$(CONFIG_DRM_SAVAGE)+= savage/
++obj-$(CONFIG_DRM_VMWGFX)+= vmwgfx/
+ obj-$(CONFIG_DRM_VIA) +=via/
++obj-$(CONFIG_DRM_NOUVEAU) +=nouveau/
++obj-y += i2c/
+diff --git a/drivers/gpu/drm/ati_pcigart.c b/drivers/gpu/drm/ati_pcigart.c
+index a1fce68..17be051 100644
+--- a/drivers/gpu/drm/ati_pcigart.c
++++ b/drivers/gpu/drm/ati_pcigart.c
+@@ -113,7 +113,7 @@ int drm_ati_pcigart_init(struct drm_device *dev, struct drm_ati_pcigart_info *ga
+
+ if (pci_set_dma_mask(dev->pdev, gart_info->table_mask)) {
+ DRM_ERROR("fail to set dma mask to 0x%Lx\n",
+- gart_info->table_mask);
++ (unsigned long long)gart_info->table_mask);
+ ret = 1;
+ goto done;
+ }
+diff --git a/drivers/gpu/drm/drm_crtc.c b/drivers/gpu/drm/drm_crtc.c
+index 5cae0b3..d91fb8c 100644
+--- a/drivers/gpu/drm/drm_crtc.c
++++ b/drivers/gpu/drm/drm_crtc.c
+@@ -125,6 +125,15 @@ static struct drm_prop_enum_list drm_tv_subconnector_enum_list[] =
+ DRM_ENUM_NAME_FN(drm_get_tv_subconnector_name,
+ drm_tv_subconnector_enum_list)
+
++static struct drm_prop_enum_list drm_dirty_info_enum_list[] = {
++ { DRM_MODE_DIRTY_OFF, "Off" },
++ { DRM_MODE_DIRTY_ON, "On" },
++ { DRM_MODE_DIRTY_ANNOTATE, "Annotate" },
++};
++
++DRM_ENUM_NAME_FN(drm_get_dirty_info_name,
++ drm_dirty_info_enum_list)
++
+ struct drm_conn_prop_enum_list {
+ int type;
+ char *name;
+@@ -149,6 +158,7 @@ static struct drm_conn_prop_enum_list drm_connector_enum_list[] =
+ { DRM_MODE_CONNECTOR_HDMIA, "HDMI Type A", 0 },
+ { DRM_MODE_CONNECTOR_HDMIB, "HDMI Type B", 0 },
+ { DRM_MODE_CONNECTOR_TV, "TV", 0 },
++ { DRM_MODE_CONNECTOR_eDP, "Embedded DisplayPort", 0 },
+ };
+
+ static struct drm_prop_enum_list drm_encoder_enum_list[] =
+@@ -247,7 +257,8 @@ static void drm_mode_object_put(struct drm_device *dev,
+ mutex_unlock(&dev->mode_config.idr_mutex);
+ }
+
+-void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type)
++struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
++ uint32_t id, uint32_t type)
+ {
+ struct drm_mode_object *obj = NULL;
+
+@@ -272,7 +283,7 @@ EXPORT_SYMBOL(drm_mode_object_find);
+ * functions & device file and adds it to the master fd list.
+ *
+ * RETURNS:
+- * Zero on success, error code on falure.
++ * Zero on success, error code on failure.
+ */
+ int drm_framebuffer_init(struct drm_device *dev, struct drm_framebuffer *fb,
+ const struct drm_framebuffer_funcs *funcs)
+@@ -802,6 +813,36 @@ int drm_mode_create_dithering_property(struct drm_device *dev)
+ EXPORT_SYMBOL(drm_mode_create_dithering_property);
+
+ /**
++ * drm_mode_create_dirty_property - create dirty property
++ * @dev: DRM device
++ *
++ * Called by a driver the first time it's needed, must be attached to desired
++ * connectors.
++ */
++int drm_mode_create_dirty_info_property(struct drm_device *dev)
++{
++ struct drm_property *dirty_info;
++ int i;
++
++ if (dev->mode_config.dirty_info_property)
++ return 0;
++
++ dirty_info =
++ drm_property_create(dev, DRM_MODE_PROP_ENUM |
++ DRM_MODE_PROP_IMMUTABLE,
++ "dirty",
++ ARRAY_SIZE(drm_dirty_info_enum_list));
++ for (i = 0; i < ARRAY_SIZE(drm_dirty_info_enum_list); i++)
++ drm_property_add_enum(dirty_info, i,
++ drm_dirty_info_enum_list[i].type,
++ drm_dirty_info_enum_list[i].name);
++ dev->mode_config.dirty_info_property = dirty_info;
++
++ return 0;
++}
++EXPORT_SYMBOL(drm_mode_create_dirty_info_property);
++
++/**
+ * drm_mode_config_init - initialize DRM mode_configuration structure
+ * @dev: DRM device
+ *
+@@ -1753,6 +1794,71 @@ out:
+ return ret;
+ }
+
++int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
++ void *data, struct drm_file *file_priv)
++{
++ struct drm_clip_rect __user *clips_ptr;
++ struct drm_clip_rect *clips = NULL;
++ struct drm_mode_fb_dirty_cmd *r = data;
++ struct drm_mode_object *obj;
++ struct drm_framebuffer *fb;
++ unsigned flags;
++ int num_clips;
++ int ret = 0;
++
++ mutex_lock(&dev->mode_config.mutex);
++ obj = drm_mode_object_find(dev, r->fb_id, DRM_MODE_OBJECT_FB);
++ if (!obj) {
++ DRM_ERROR("invalid framebuffer id\n");
++ ret = -EINVAL;
++ goto out_err1;
++ }
++ fb = obj_to_fb(obj);
++
++ num_clips = r->num_clips;
++ clips_ptr = (struct drm_clip_rect *)(unsigned long)r->clips_ptr;
++
++ if (!num_clips != !clips_ptr) {
++ ret = -EINVAL;
++ goto out_err1;
++ }
++
++ flags = DRM_MODE_FB_DIRTY_FLAGS & r->flags;
++
++ /* If userspace annotates copy, clips must come in pairs */
++ if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY && (num_clips % 2)) {
++ ret = -EINVAL;
++ goto out_err1;
++ }
++
++ if (num_clips && clips_ptr) {
++ clips = kzalloc(num_clips * sizeof(*clips), GFP_KERNEL);
++ if (!clips) {
++ ret = -ENOMEM;
++ goto out_err1;
++ }
++
++ ret = copy_from_user(clips, clips_ptr,
++ num_clips * sizeof(*clips));
++ if (ret)
++ goto out_err2;
++ }
++
++ if (fb->funcs->dirty) {
++ ret = fb->funcs->dirty(fb, flags, r->color, clips, num_clips);
++ } else {
++ ret = -ENOSYS;
++ goto out_err2;
++ }
++
++out_err2:
++ kfree(clips);
++out_err1:
++ mutex_unlock(&dev->mode_config.mutex);
++ return ret;
++}
++
++
+ /**
+ * drm_fb_release - remove and free the FBs on this file
+ * @filp: file * from the ioctl
+@@ -2328,7 +2434,7 @@ int drm_mode_connector_property_set_ioctl(struct drm_device *dev,
+ } else if (connector->funcs->set_property)
+ ret = connector->funcs->set_property(connector, property, out_resp->value);
+
+- /* store the property value if succesful */
++ /* store the property value if successful */
+ if (!ret)
+ drm_connector_property_set_value(connector, property, out_resp->value);
+ out:
+@@ -2478,3 +2584,72 @@ out:
+ mutex_unlock(&dev->mode_config.mutex);
+ return ret;
+ }
++
++int drm_mode_page_flip_ioctl(struct drm_device *dev,
++ void *data, struct drm_file *file_priv)
++{
++ struct drm_mode_crtc_page_flip *page_flip = data;
++ struct drm_mode_object *obj;
++ struct drm_crtc *crtc;
++ struct drm_framebuffer *fb;
++ struct drm_pending_vblank_event *e = NULL;
++ unsigned long flags;
++ int ret = -EINVAL;
++
++ if (page_flip->flags & ~DRM_MODE_PAGE_FLIP_FLAGS ||
++ page_flip->reserved != 0)
++ return -EINVAL;
++
++ mutex_lock(&dev->mode_config.mutex);
++ obj = drm_mode_object_find(dev, page_flip->crtc_id, DRM_MODE_OBJECT_CRTC);
++ if (!obj)
++ goto out;
++ crtc = obj_to_crtc(obj);
++
++ if (crtc->funcs->page_flip == NULL)
++ goto out;
++
++ obj = drm_mode_object_find(dev, page_flip->fb_id, DRM_MODE_OBJECT_FB);
++ if (!obj)
++ goto out;
++ fb = obj_to_fb(obj);
++
++ if (page_flip->flags & DRM_MODE_PAGE_FLIP_EVENT) {
++ ret = -ENOMEM;
++ spin_lock_irqsave(&dev->event_lock, flags);
++ if (file_priv->event_space < sizeof e->event) {
++ spin_unlock_irqrestore(&dev->event_lock, flags);
++ goto out;
++ }
++ file_priv->event_space -= sizeof e->event;
++ spin_unlock_irqrestore(&dev->event_lock, flags);
++
++ e = kzalloc(sizeof *e, GFP_KERNEL);
++ if (e == NULL) {
++ spin_lock_irqsave(&dev->event_lock, flags);
++ file_priv->event_space += sizeof e->event;
++ spin_unlock_irqrestore(&dev->event_lock, flags);
++ goto out;
++ }
++
++ e->event.base.type = DRM_EVENT_FLIP_COMPLETE;
++ e->event.base.length = sizeof e->event;
++ e->event.user_data = page_flip->user_data;
++ e->base.event = &e->event.base;
++ e->base.file_priv = file_priv;
++ e->base.destroy =
++ (void (*) (struct drm_pending_event *)) kfree;
++ }
++
++ ret = crtc->funcs->page_flip(crtc, fb, e);
++ if (ret) {
++ spin_lock_irqsave(&dev->event_lock, flags);
++ file_priv->event_space += sizeof e->event;
++ spin_unlock_irqrestore(&dev->event_lock, flags);
++ kfree(e);
++ }
++
++out:
++ mutex_unlock(&dev->mode_config.mutex);
++ return ret;
++}
+diff --git a/drivers/gpu/drm/drm_crtc_helper.c b/drivers/gpu/drm/drm_crtc_helper.c
+index afed886..7d0f00a 100644
+--- a/drivers/gpu/drm/drm_crtc_helper.c
++++ b/drivers/gpu/drm/drm_crtc_helper.c
+@@ -109,7 +109,7 @@ int drm_helper_probe_single_connector_modes(struct drm_connector *connector,
+
+ count = (*connector_funcs->get_modes)(connector);
+ if (!count) {
+- count = drm_add_modes_noedid(connector, 800, 600);
++ count = drm_add_modes_noedid(connector, 1024, 768);
+ if (!count)
+ return 0;
+ }
+@@ -216,7 +216,7 @@ bool drm_helper_crtc_in_use(struct drm_crtc *crtc)
+ EXPORT_SYMBOL(drm_helper_crtc_in_use);
+
+ /**
+- * drm_disable_unused_functions - disable unused objects
++ * drm_helper_disable_unused_functions - disable unused objects
+ * @dev: DRM device
+ *
+ * LOCKING:
+@@ -702,7 +702,7 @@ bool drm_crtc_helper_set_mode(struct drm_crtc *crtc,
+ if (encoder->crtc != crtc)
+ continue;
+
+- DRM_INFO("%s: set mode %s %x\n", drm_get_encoder_name(encoder),
++ DRM_DEBUG("%s: set mode %s %x\n", drm_get_encoder_name(encoder),
+ mode->name, mode->base.id);
+ encoder_funcs = encoder->helper_private;
+ encoder_funcs->mode_set(encoder, mode, adjusted_mode);
+@@ -1032,7 +1032,8 @@ bool drm_helper_initial_config(struct drm_device *dev)
+ /*
+ * we shouldn't end up with no modes here.
+ */
+- WARN(!count, "No connectors reported connected with modes\n");
++ if (count == 0)
++ printk(KERN_INFO "No connectors reported connected with modes\n");
+
+ drm_setup_crtcs(dev);
+
+@@ -1162,6 +1163,9 @@ EXPORT_SYMBOL(drm_helper_mode_fill_fb_struct);
+ int drm_helper_resume_force_mode(struct drm_device *dev)
+ {
+ struct drm_crtc *crtc;
++ struct drm_encoder *encoder;
++ struct drm_encoder_helper_funcs *encoder_funcs;
++ struct drm_crtc_helper_funcs *crtc_funcs;
+ int ret;
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+@@ -1174,6 +1178,25 @@ int drm_helper_resume_force_mode(struct drm_device *dev)
+
+ if (ret == false)
+ DRM_ERROR("failed to set mode on crtc %p\n", crtc);
++
++ /* Turn off outputs that were already powered off */
++ if (drm_helper_choose_crtc_dpms(crtc)) {
++ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
++
++ if(encoder->crtc != crtc)
++ continue;
++
++ encoder_funcs = encoder->helper_private;
++ if (encoder_funcs->dpms)
++ (*encoder_funcs->dpms) (encoder,
++ drm_helper_choose_encoder_dpms(encoder));
++
++ crtc_funcs = crtc->helper_private;
++ if (crtc_funcs->dpms)
++ (*crtc_funcs->dpms) (crtc,
++ drm_helper_choose_crtc_dpms(crtc));
++ }
++ }
+ }
+ /* disable the unused connectors while restoring the modesetting */
+ drm_helper_disable_unused_functions(dev);
+diff --git a/drivers/gpu/drm/drm_dp_i2c_helper.c b/drivers/gpu/drm/drm_dp_i2c_helper.c
+new file mode 100644
+index 0000000..548887c
+--- /dev/null
++++ b/drivers/gpu/drm/drm_dp_i2c_helper.c
+@@ -0,0 +1,209 @@
++/*
++ * Copyright © 2009 Keith Packard
++ *
++ * Permission to use, copy, modify, distribute, and sell this software and its
++ * documentation for any purpose is hereby granted without fee, provided that
++ * the above copyright notice appear in all copies and that both that copyright
++ * notice and this permission notice appear in supporting documentation, and
++ * that the name of the copyright holders not be used in advertising or
++ * publicity pertaining to distribution of the software without specific,
++ * written prior permission. The copyright holders make no representations
++ * about the suitability of this software for any purpose. It is provided "as
++ * is" without express or implied warranty.
++ *
++ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
++ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
++ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
++ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
++ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
++ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
++ * OF THIS SOFTWARE.
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/errno.h>
++#include <linux/sched.h>
++#include <linux/i2c.h>
++#include "drm_dp_helper.h"
++#include "drmP.h"
++
++/* Run a single AUX_CH I2C transaction, writing/reading data as necessary */
++static int
++i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode,
++ uint8_t write_byte, uint8_t *read_byte)
++{
++ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
++ int ret;
++
++ ret = (*algo_data->aux_ch)(adapter, mode,
++ write_byte, read_byte);
++ return ret;
++}
++
++/*
++ * I2C over AUX CH
++ */
++
++/*
++ * Send the address. If the I2C link is running, this 'restarts'
++ * the connection with the new address, this is used for doing
++ * a write followed by a read (as needed for DDC)
++ */
++static int
++i2c_algo_dp_aux_address(struct i2c_adapter *adapter, u16 address, bool reading)
++{
++ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
++ int mode = MODE_I2C_START;
++ int ret;
++
++ if (reading)
++ mode |= MODE_I2C_READ;
++ else
++ mode |= MODE_I2C_WRITE;
++ algo_data->address = address;
++ algo_data->running = true;
++ ret = i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL);
++ return ret;
++}
++
++/*
++ * Stop the I2C transaction. This closes out the link, sending
++ * a bare address packet with the MOT bit turned off
++ */
++static void
++i2c_algo_dp_aux_stop(struct i2c_adapter *adapter, bool reading)
++{
++ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
++ int mode = MODE_I2C_STOP;
++
++ if (reading)
++ mode |= MODE_I2C_READ;
++ else
++ mode |= MODE_I2C_WRITE;
++ if (algo_data->running) {
++ (void) i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL);
++ algo_data->running = false;
++ }
++}
++
++/*
++ * Write a single byte to the current I2C address, the
++ * the I2C link must be running or this returns -EIO
++ */
++static int
++i2c_algo_dp_aux_put_byte(struct i2c_adapter *adapter, u8 byte)
++{
++ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
++ int ret;
++
++ if (!algo_data->running)
++ return -EIO;
++
++ ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_WRITE, byte, NULL);
++ return ret;
++}
++
++/*
++ * Read a single byte from the current I2C address, the
++ * I2C link must be running or this returns -EIO
++ */
++static int
++i2c_algo_dp_aux_get_byte(struct i2c_adapter *adapter, u8 *byte_ret)
++{
++ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
++ int ret;
++
++ if (!algo_data->running)
++ return -EIO;
++
++ ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_READ, 0, byte_ret);
++ return ret;
++}
++
++static int
++i2c_algo_dp_aux_xfer(struct i2c_adapter *adapter,
++ struct i2c_msg *msgs,
++ int num)
++{
++ int ret = 0;
++ bool reading = false;
++ int m;
++ int b;
++
++ for (m = 0; m < num; m++) {
++ u16 len = msgs[m].len;
++ u8 *buf = msgs[m].buf;
++ reading = (msgs[m].flags & I2C_M_RD) != 0;
++ ret = i2c_algo_dp_aux_address(adapter, msgs[m].addr, reading);
++ if (ret < 0)
++ break;
++ if (reading) {
++ for (b = 0; b < len; b++) {
++ ret = i2c_algo_dp_aux_get_byte(adapter, &buf[b]);
++ if (ret < 0)
++ break;
++ }
++ } else {
++ for (b = 0; b < len; b++) {
++ ret = i2c_algo_dp_aux_put_byte(adapter, buf[b]);
++ if (ret < 0)
++ break;
++ }
++ }
++ if (ret < 0)
++ break;
++ }
++ if (ret >= 0)
++ ret = num;
++ i2c_algo_dp_aux_stop(adapter, reading);
++ DRM_DEBUG_KMS("dp_aux_xfer return %d\n", ret);
++ return ret;
++}
++
++static u32
++i2c_algo_dp_aux_functionality(struct i2c_adapter *adapter)
++{
++ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
++ I2C_FUNC_SMBUS_READ_BLOCK_DATA |
++ I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
++ I2C_FUNC_10BIT_ADDR;
++}
++
++static const struct i2c_algorithm i2c_dp_aux_algo = {
++ .master_xfer = i2c_algo_dp_aux_xfer,
++ .functionality = i2c_algo_dp_aux_functionality,
++};
++
++static void
++i2c_dp_aux_reset_bus(struct i2c_adapter *adapter)
++{
++ (void) i2c_algo_dp_aux_address(adapter, 0, false);
++ (void) i2c_algo_dp_aux_stop(adapter, false);
++
++}
++
++static int
++i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter)
++{
++ adapter->algo = &i2c_dp_aux_algo;
++ adapter->retries = 3;
++ i2c_dp_aux_reset_bus(adapter);
++ return 0;
++}
++
++int
++i2c_dp_aux_add_bus(struct i2c_adapter *adapter)
++{
++ int error;
++
++ error = i2c_dp_aux_prepare_bus(adapter);
++ if (error)
++ return error;
++ error = i2c_add_adapter(adapter);
++ return error;
++}
++EXPORT_SYMBOL(i2c_dp_aux_add_bus);
+diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
+index a75ca63..766c468 100644
+--- a/drivers/gpu/drm/drm_drv.c
++++ b/drivers/gpu/drm/drm_drv.c
+@@ -145,6 +145,8 @@ static struct drm_ioctl_desc drm_ioctls[] = {
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETFB, drm_mode_getfb, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_MASTER|DRM_CONTROL_ALLOW),
+ DRM_IOCTL_DEF(DRM_IOCTL_MODE_RMFB, drm_mode_rmfb, DRM_MASTER|DRM_CONTROL_ALLOW),
++ DRM_IOCTL_DEF(DRM_IOCTL_MODE_PAGE_FLIP, drm_mode_page_flip_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW),
++ DRM_IOCTL_DEF(DRM_IOCTL_MODE_DIRTYFB, drm_mode_dirtyfb_ioctl, DRM_MASTER|DRM_CONTROL_ALLOW)
+ };
+
+ #define DRM_CORE_IOCTL_COUNT ARRAY_SIZE( drm_ioctls )
+@@ -366,6 +368,29 @@ module_init(drm_core_init);
+ module_exit(drm_core_exit);
+
+ /**
++ * Copy and IOCTL return string to user space
++ */
++static int drm_copy_field(char *buf, size_t *buf_len, const char *value)
++{
++ int len;
++
++ /* don't overflow userbuf */
++ len = strlen(value);
++ if (len > *buf_len)
++ len = *buf_len;
++
++ /* let userspace know exact length of driver value (which could be
++ * larger than the userspace-supplied buffer) */
++ *buf_len = strlen(value);
++
++ /* finally, try filling in the userbuf */
++ if (len && buf)
++ if (copy_to_user(buf, value, len))
++ return -EFAULT;
++ return 0;
++}
++
++/**
+ * Get version information
+ *
+ * \param inode device inode.
+@@ -380,16 +405,21 @@ static int drm_version(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+ {
+ struct drm_version *version = data;
+- int len;
++ int err;
+
+ version->version_major = dev->driver->major;
+ version->version_minor = dev->driver->minor;
+ version->version_patchlevel = dev->driver->patchlevel;
+- DRM_COPY(version->name, dev->driver->name);
+- DRM_COPY(version->date, dev->driver->date);
+- DRM_COPY(version->desc, dev->driver->desc);
+-
+- return 0;
++ err = drm_copy_field(version->name, &version->name_len,
++ dev->driver->name);
++ if (!err)
++ err = drm_copy_field(version->date, &version->date_len,
++ dev->driver->date);
++ if (!err)
++ err = drm_copy_field(version->desc, &version->desc_len,
++ dev->driver->desc);
++
++ return err;
+ }
+
+ /**
+@@ -404,11 +434,11 @@ static int drm_version(struct drm_device *dev, void *data,
+ * Looks up the ioctl function in the ::ioctls table, checking for root
+ * previleges if so required, and dispatches to the respective function.
+ */
+-int drm_ioctl(struct inode *inode, struct file *filp,
++long drm_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg)
+ {
+ struct drm_file *file_priv = filp->private_data;
+- struct drm_device *dev = file_priv->minor->dev;
++ struct drm_device *dev;
+ struct drm_ioctl_desc *ioctl;
+ drm_ioctl_t *func;
+ unsigned int nr = DRM_IOCTL_NR(cmd);
+@@ -416,6 +446,7 @@ int drm_ioctl(struct inode *inode, struct file *filp,
+ char stack_kdata[128];
+ char *kdata = NULL;
+
++ dev = file_priv->minor->dev;
+ atomic_inc(&dev->ioctl_count);
+ atomic_inc(&dev->counts[_DRM_STAT_IOCTLS]);
+ ++file_priv->ioctl_count;
+@@ -471,7 +502,13 @@ int drm_ioctl(struct inode *inode, struct file *filp,
+ goto err_i1;
+ }
+ }
+- retcode = func(dev, kdata, file_priv);
++ if (ioctl->flags & DRM_UNLOCKED)
++ retcode = func(dev, kdata, file_priv);
++ else {
++ lock_kernel();
++ retcode = func(dev, kdata, file_priv);
++ unlock_kernel();
++ }
+
+ if (cmd & IOC_OUT) {
+ if (copy_to_user((void __user *)arg, kdata,
+diff --git a/drivers/gpu/drm/drm_edid.c b/drivers/gpu/drm/drm_edid.c
+index b54ba63..f41e91c 100644
+--- a/drivers/gpu/drm/drm_edid.c
++++ b/drivers/gpu/drm/drm_edid.c
+@@ -60,8 +60,7 @@
+ #define EDID_QUIRK_FIRST_DETAILED_PREFERRED (1 << 5)
+ /* use +hsync +vsync for detailed mode */
+ #define EDID_QUIRK_DETAILED_SYNC_PP (1 << 6)
+-/* define the number of Extension EDID block */
+-#define MAX_EDID_EXT_NUM 4
++
+
+ #define LEVEL_DMT 0
+ #define LEVEL_GTF 1
+@@ -114,27 +113,29 @@ static const u8 edid_header[] = {
+ };
+
+ /**
+- * edid_is_valid - sanity check EDID data
++ * drm_edid_is_valid - sanity check EDID data
+ * @edid: EDID data
+ *
+ * Sanity check the EDID block by looking at the header, the version number
+ * and the checksum. Return 0 if the EDID doesn't check out, or 1 if it's
+ * valid.
+ */
+-static bool edid_is_valid(struct edid *edid)
++bool drm_edid_is_valid(struct edid *edid)
+ {
+- int i;
++ int i, score = 0;
+ u8 csum = 0;
+ u8 *raw_edid = (u8 *)edid;
+
+- if (memcmp(edid->header, edid_header, sizeof(edid_header)))
+- goto bad;
+- if (edid->version != 1) {
+- DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version);
++ for (i = 0; i < sizeof(edid_header); i++)
++ if (raw_edid[i] == edid_header[i])
++ score++;
++
++ if (score == 8) ;
++ else if (score >= 6) {
++ DRM_DEBUG("Fixing EDID header, your hardware may be failing\n");
++ memcpy(raw_edid, edid_header, sizeof(edid_header));
++ } else
+ goto bad;
+- }
+- if (edid->revision > 4)
+- DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n");
+
+ for (i = 0; i < EDID_LENGTH; i++)
+ csum += raw_edid[i];
+@@ -143,6 +144,14 @@ static bool edid_is_valid(struct edid *edid)
+ goto bad;
+ }
+
++ if (edid->version != 1) {
++ DRM_ERROR("EDID has major version %d, instead of 1\n", edid->version);
++ goto bad;
++ }
++
++ if (edid->revision > 4)
++ DRM_DEBUG("EDID minor > 4, assuming backward compatibility\n");
++
+ return 1;
+
+ bad:
+@@ -153,6 +162,7 @@ bad:
+ }
+ return 0;
+ }
++EXPORT_SYMBOL(drm_edid_is_valid);
+
+ /**
+ * edid_vendor - match a string against EDID's obfuscated vendor field
+@@ -481,16 +491,17 @@ static struct drm_display_mode drm_dmt_modes[] = {
+ 3048, 3536, 0, 1600, 1603, 1609, 1682, 0,
+ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
+ };
++static const int drm_num_dmt_modes =
++ sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode);
+
+ static struct drm_display_mode *drm_find_dmt(struct drm_device *dev,
+ int hsize, int vsize, int fresh)
+ {
+- int i, count;
++ int i;
+ struct drm_display_mode *ptr, *mode;
+
+- count = sizeof(drm_dmt_modes) / sizeof(struct drm_display_mode);
+ mode = NULL;
+- for (i = 0; i < count; i++) {
++ for (i = 0; i < drm_num_dmt_modes; i++) {
+ ptr = &drm_dmt_modes[i];
+ if (hsize == ptr->hdisplay &&
+ vsize == ptr->vdisplay &&
+@@ -622,8 +633,7 @@ static struct drm_display_mode *drm_mode_detailed(struct drm_device *dev,
+ return NULL;
+ }
+ if (!(pt->misc & DRM_EDID_PT_SEPARATE_SYNC)) {
+- printk(KERN_WARNING "integrated sync not supported\n");
+- return NULL;
++ printk(KERN_WARNING "composite sync not supported\n");
+ }
+
+ /* it is incorrect if hsync/vsync width is zero */
+@@ -834,8 +844,169 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid
+ return modes;
+ }
+
++/*
++ * XXX fix this for:
++ * - GTF secondary curve formula
++ * - EDID 1.4 range offsets
++ * - CVT extended bits
++ */
++static bool
++mode_in_range(struct drm_display_mode *mode, struct detailed_timing *timing)
++{
++ struct detailed_data_monitor_range *range;
++ int hsync, vrefresh;
++
++ range = &timing->data.other_data.data.range;
++
++ hsync = drm_mode_hsync(mode);
++ vrefresh = drm_mode_vrefresh(mode);
++
++ if (hsync < range->min_hfreq_khz || hsync > range->max_hfreq_khz)
++ return false;
++
++ if (vrefresh < range->min_vfreq || vrefresh > range->max_vfreq)
++ return false;
++
++ if (range->pixel_clock_mhz && range->pixel_clock_mhz != 0xff) {
++ /* be forgiving since it's in units of 10MHz */
++ int max_clock = range->pixel_clock_mhz * 10 + 9;
++ max_clock *= 1000;
++ if (mode->clock > max_clock)
++ return false;
++ }
++
++ return true;
++}
++
++/*
++ * XXX If drm_dmt_modes ever regrows the CVT-R modes (and it will) this will
++ * need to account for them.
++ */
++static int drm_gtf_modes_for_range(struct drm_connector *connector,
++ struct detailed_timing *timing)
++{
++ int i, modes = 0;
++ struct drm_display_mode *newmode;
++ struct drm_device *dev = connector->dev;
++
++ for (i = 0; i < drm_num_dmt_modes; i++) {
++ if (mode_in_range(drm_dmt_modes + i, timing)) {
++ newmode = drm_mode_duplicate(dev, &drm_dmt_modes[i]);
++ if (newmode) {
++ drm_mode_probed_add(connector, newmode);
++ modes++;
++ }
++ }
++ }
++
++ return modes;
++}
++
++static int drm_cvt_modes(struct drm_connector *connector,
++ struct detailed_timing *timing)
++{
++ int i, j, modes = 0;
++ struct drm_display_mode *newmode;
++ struct drm_device *dev = connector->dev;
++ struct cvt_timing *cvt;
++ const int rates[] = { 60, 85, 75, 60, 50 };
++ const u8 empty[3] = { 0, 0, 0 };
++
++ for (i = 0; i < 4; i++) {
++ int uninitialized_var(width), height;
++ cvt = &(timing->data.other_data.data.cvt[i]);
++
++ if (!memcmp(cvt->code, empty, 3))
++ continue;
++
++ height = (cvt->code[0] + ((cvt->code[1] & 0xf0) << 4) + 1) * 2;
++ switch (cvt->code[1] & 0x0c) {
++ case 0x00:
++ width = height * 4 / 3;
++ break;
++ case 0x04:
++ width = height * 16 / 9;
++ break;
++ case 0x08:
++ width = height * 16 / 10;
++ break;
++ case 0x0c:
++ width = height * 15 / 9;
++ break;
++ }
++
++ for (j = 1; j < 5; j++) {
++ if (cvt->code[2] & (1 << j)) {
++ newmode = drm_cvt_mode(dev, width, height,
++ rates[j], j == 0,
++ false, false);
++ if (newmode) {
++ drm_mode_probed_add(connector, newmode);
++ modes++;
++ }
++ }
++ }
++ }
++
++ return modes;
++}
++
++static int add_detailed_modes(struct drm_connector *connector,
++ struct detailed_timing *timing,
++ struct edid *edid, u32 quirks, int preferred)
++{
++ int i, modes = 0;
++ struct detailed_non_pixel *data = &timing->data.other_data;
++ int timing_level = standard_timing_level(edid);
++ int gtf = (edid->features & DRM_EDID_FEATURE_DEFAULT_GTF);
++ struct drm_display_mode *newmode;
++ struct drm_device *dev = connector->dev;
++
++ if (timing->pixel_clock) {
++ newmode = drm_mode_detailed(dev, edid, timing, quirks);
++ if (!newmode)
++ return 0;
++
++ if (preferred)
++ newmode->type |= DRM_MODE_TYPE_PREFERRED;
++
++ drm_mode_probed_add(connector, newmode);
++ return 1;
++ }
++
++ /* other timing types */
++ switch (data->type) {
++ case EDID_DETAIL_MONITOR_RANGE:
++ if (gtf)
++ modes += drm_gtf_modes_for_range(connector, timing);
++ break;
++ case EDID_DETAIL_STD_MODES:
++ /* Six modes per detailed section */
++ for (i = 0; i < 6; i++) {
++ struct std_timing *std;
++ struct drm_display_mode *newmode;
++
++ std = &data->data.timings[i];
++ newmode = drm_mode_std(dev, std, edid->revision,
++ timing_level);
++ if (newmode) {
++ drm_mode_probed_add(connector, newmode);
++ modes++;
++ }
++ }
++ break;
++ case EDID_DETAIL_CVT_3BYTE:
++ modes += drm_cvt_modes(connector, timing);
++ break;
++ default:
++ break;
++ }
++
++ return modes;
++}
++
+ /**
+- * add_detailed_modes - get detailed mode info from EDID data
++ * add_detailed_info - get detailed mode info from EDID data
+ * @connector: attached connector
+ * @edid: EDID block to scan
+ * @quirks: quirks to apply
+@@ -846,67 +1017,24 @@ static int add_standard_modes(struct drm_connector *connector, struct edid *edid
+ static int add_detailed_info(struct drm_connector *connector,
+ struct edid *edid, u32 quirks)
+ {
+- struct drm_device *dev = connector->dev;
+- int i, j, modes = 0;
+- int timing_level;
+-
+- timing_level = standard_timing_level(edid);
++ int i, modes = 0;
+
+ for (i = 0; i < EDID_DETAILED_TIMINGS; i++) {
+ struct detailed_timing *timing = &edid->detailed_timings[i];
+- struct detailed_non_pixel *data = &timing->data.other_data;
+- struct drm_display_mode *newmode;
++ int preferred = (i == 0) && (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
+
+- /* X server check is version 1.1 or higher */
+- if (edid->version == 1 && edid->revision >= 1 &&
+- !timing->pixel_clock) {
+- /* Other timing or info */
+- switch (data->type) {
+- case EDID_DETAIL_MONITOR_SERIAL:
+- break;
+- case EDID_DETAIL_MONITOR_STRING:
+- break;
+- case EDID_DETAIL_MONITOR_RANGE:
+- /* Get monitor range data */
+- break;
+- case EDID_DETAIL_MONITOR_NAME:
+- break;
+- case EDID_DETAIL_MONITOR_CPDATA:
+- break;
+- case EDID_DETAIL_STD_MODES:
+- for (j = 0; j < 6; i++) {
+- struct std_timing *std;
+- struct drm_display_mode *newmode;
+-
+- std = &data->data.timings[j];
+- newmode = drm_mode_std(dev, std,
+- edid->revision,
+- timing_level);
+- if (newmode) {
+- drm_mode_probed_add(connector, newmode);
+- modes++;
+- }
+- }
+- break;
+- default:
+- break;
+- }
+- } else {
+- newmode = drm_mode_detailed(dev, edid, timing, quirks);
+- if (!newmode)
+- continue;
+-
+- /* First detailed mode is preferred */
+- if (i == 0 && (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING))
+- newmode->type |= DRM_MODE_TYPE_PREFERRED;
+- drm_mode_probed_add(connector, newmode);
++ /* In 1.0, only timings are allowed */
++ if (!timing->pixel_clock && edid->version == 1 &&
++ edid->revision == 0)
++ continue;
+
+- modes++;
+- }
++ modes += add_detailed_modes(connector, timing, edid, quirks,
++ preferred);
+ }
+
+ return modes;
+ }
++
+ /**
+ * add_detailed_mode_eedid - get detailed mode info from addtional timing
+ * EDID block
+@@ -920,12 +1048,9 @@ static int add_detailed_info(struct drm_connector *connector,
+ static int add_detailed_info_eedid(struct drm_connector *connector,
+ struct edid *edid, u32 quirks)
+ {
+- struct drm_device *dev = connector->dev;
+- int i, j, modes = 0;
++ int i, modes = 0;
+ char *edid_ext = NULL;
+ struct detailed_timing *timing;
+- struct detailed_non_pixel *data;
+- struct drm_display_mode *newmode;
+ int edid_ext_num;
+ int start_offset, end_offset;
+ int timing_level;
+@@ -944,8 +1069,8 @@ static int add_detailed_info_eedid(struct drm_connector *connector,
+ }
+
+ /* Chose real EDID extension number */
+- edid_ext_num = edid->extensions > MAX_EDID_EXT_NUM ?
+- MAX_EDID_EXT_NUM : edid->extensions;
++ edid_ext_num = edid->extensions > DRM_MAX_EDID_EXT_NUM ?
++ DRM_MAX_EDID_EXT_NUM : edid->extensions;
+
+ /* Find CEA extension */
+ for (i = 0; i < edid_ext_num; i++) {
+@@ -976,51 +1101,7 @@ static int add_detailed_info_eedid(struct drm_connector *connector,
+ for (i = start_offset; i < end_offset;
+ i += sizeof(struct detailed_timing)) {
+ timing = (struct detailed_timing *)(edid_ext + i);
+- data = &timing->data.other_data;
+- /* Detailed mode timing */
+- if (timing->pixel_clock) {
+- newmode = drm_mode_detailed(dev, edid, timing, quirks);
+- if (!newmode)
+- continue;
+-
+- drm_mode_probed_add(connector, newmode);
+-
+- modes++;
+- continue;
+- }
+-
+- /* Other timing or info */
+- switch (data->type) {
+- case EDID_DETAIL_MONITOR_SERIAL:
+- break;
+- case EDID_DETAIL_MONITOR_STRING:
+- break;
+- case EDID_DETAIL_MONITOR_RANGE:
+- /* Get monitor range data */
+- break;
+- case EDID_DETAIL_MONITOR_NAME:
+- break;
+- case EDID_DETAIL_MONITOR_CPDATA:
+- break;
+- case EDID_DETAIL_STD_MODES:
+- /* Five modes per detailed section */
+- for (j = 0; j < 5; i++) {
+- struct std_timing *std;
+- struct drm_display_mode *newmode;
+-
+- std = &data->data.timings[j];
+- newmode = drm_mode_std(dev, std,
+- edid->revision,
+- timing_level);
+- if (newmode) {
+- drm_mode_probed_add(connector, newmode);
+- modes++;
+- }
+- }
+- break;
+- default:
+- break;
+- }
++ modes += add_detailed_modes(connector, timing, edid, quirks, 0);
+ }
+
+ return modes;
+@@ -1066,19 +1147,19 @@ static int drm_ddc_read_edid(struct drm_connector *connector,
+ struct i2c_adapter *adapter,
+ char *buf, int len)
+ {
+- int ret;
++ int i;
+
+- ret = drm_do_probe_ddc_edid(adapter, buf, len);
+- if (ret != 0) {
+- goto end;
+- }
+- if (!edid_is_valid((struct edid *)buf)) {
+- dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
+- drm_get_connector_name(connector));
+- ret = -1;
++ for (i = 0; i < 4; i++) {
++ if (drm_do_probe_ddc_edid(adapter, buf, len))
++ return -1;
++ if (drm_edid_is_valid((struct edid *)buf))
++ return 0;
+ }
+-end:
+- return ret;
++
++ /* repeated checksum failures; warn, but carry on */
++ dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
++ drm_get_connector_name(connector));
++ return -1;
+ }
+
+ /**
+@@ -1096,7 +1177,7 @@ struct edid *drm_get_edid(struct drm_connector *connector,
+ int ret;
+ struct edid *edid;
+
+- edid = kmalloc(EDID_LENGTH * (MAX_EDID_EXT_NUM + 1),
++ edid = kmalloc(EDID_LENGTH * (DRM_MAX_EDID_EXT_NUM + 1),
+ GFP_KERNEL);
+ if (edid == NULL) {
+ dev_warn(&connector->dev->pdev->dev,
+@@ -1114,14 +1195,14 @@ struct edid *drm_get_edid(struct drm_connector *connector,
+ if (edid->extensions != 0) {
+ int edid_ext_num = edid->extensions;
+
+- if (edid_ext_num > MAX_EDID_EXT_NUM) {
++ if (edid_ext_num > DRM_MAX_EDID_EXT_NUM) {
+ dev_warn(&connector->dev->pdev->dev,
+ "The number of extension(%d) is "
+ "over max (%d), actually read number (%d)\n",
+- edid_ext_num, MAX_EDID_EXT_NUM,
+- MAX_EDID_EXT_NUM);
++ edid_ext_num, DRM_MAX_EDID_EXT_NUM,
++ DRM_MAX_EDID_EXT_NUM);
+ /* Reset EDID extension number to be read */
+- edid_ext_num = MAX_EDID_EXT_NUM;
++ edid_ext_num = DRM_MAX_EDID_EXT_NUM;
+ }
+ /* Read EDID including extensions too */
+ ret = drm_ddc_read_edid(connector, adapter, (char *)edid,
+@@ -1164,8 +1245,8 @@ bool drm_detect_hdmi_monitor(struct edid *edid)
+ goto end;
+
+ /* Chose real EDID extension number */
+- edid_ext_num = edid->extensions > MAX_EDID_EXT_NUM ?
+- MAX_EDID_EXT_NUM : edid->extensions;
++ edid_ext_num = edid->extensions > DRM_MAX_EDID_EXT_NUM ?
++ DRM_MAX_EDID_EXT_NUM : edid->extensions;
+
+ /* Find CEA extension */
+ for (i = 0; i < edid_ext_num; i++) {
+@@ -1222,7 +1303,7 @@ int drm_add_edid_modes(struct drm_connector *connector, struct edid *edid)
+ if (edid == NULL) {
+ return 0;
+ }
+- if (!edid_is_valid(edid)) {
++ if (!drm_edid_is_valid(edid)) {
+ dev_warn(&connector->dev->pdev->dev, "%s: EDID invalid.\n",
+ drm_get_connector_name(connector));
+ return 0;
+@@ -1296,6 +1377,8 @@ int drm_add_modes_noedid(struct drm_connector *connector,
+ ptr->vdisplay > vdisplay)
+ continue;
+ }
++ if (drm_mode_vrefresh(ptr) > 61)
++ continue;
+ mode = drm_mode_duplicate(dev, ptr);
+ if (mode) {
+ drm_mode_probed_add(connector, mode);
+diff --git a/drivers/gpu/drm/drm_fb_helper.c b/drivers/gpu/drm/drm_fb_helper.c
+index 65ef011..0f9e905 100644
+--- a/drivers/gpu/drm/drm_fb_helper.c
++++ b/drivers/gpu/drm/drm_fb_helper.c
+@@ -156,7 +156,7 @@ static bool drm_fb_helper_connector_parse_command_line(struct drm_connector *con
+ force = DRM_FORCE_ON;
+ break;
+ case 'D':
+- if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) ||
++ if ((connector->connector_type != DRM_MODE_CONNECTOR_DVII) &&
+ (connector->connector_type != DRM_MODE_CONNECTOR_HDMIB))
+ force = DRM_FORCE_ON;
+ else
+@@ -373,11 +373,9 @@ static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
+ mutex_unlock(&dev->mode_config.mutex);
+ }
+ }
+- if (dpms_mode == DRM_MODE_DPMS_OFF) {
+- mutex_lock(&dev->mode_config.mutex);
+- crtc_funcs->dpms(crtc, dpms_mode);
+- mutex_unlock(&dev->mode_config.mutex);
+- }
++ mutex_lock(&dev->mode_config.mutex);
++ crtc_funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
++ mutex_unlock(&dev->mode_config.mutex);
+ }
+ }
+ }
+@@ -385,18 +383,23 @@ static void drm_fb_helper_off(struct fb_info *info, int dpms_mode)
+ int drm_fb_helper_blank(int blank, struct fb_info *info)
+ {
+ switch (blank) {
++ /* Display: On; HSync: On, VSync: On */
+ case FB_BLANK_UNBLANK:
+ drm_fb_helper_on(info);
+ break;
++ /* Display: Off; HSync: On, VSync: On */
+ case FB_BLANK_NORMAL:
+ drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
+ break;
++ /* Display: Off; HSync: Off, VSync: On */
+ case FB_BLANK_HSYNC_SUSPEND:
+ drm_fb_helper_off(info, DRM_MODE_DPMS_STANDBY);
+ break;
++ /* Display: Off; HSync: On, VSync: Off */
+ case FB_BLANK_VSYNC_SUSPEND:
+ drm_fb_helper_off(info, DRM_MODE_DPMS_SUSPEND);
+ break;
++ /* Display: Off; HSync: Off, VSync: Off */
+ case FB_BLANK_POWERDOWN:
+ drm_fb_helper_off(info, DRM_MODE_DPMS_OFF);
+ break;
+@@ -603,11 +606,10 @@ int drm_fb_helper_check_var(struct fb_var_screeninfo *var,
+ return -EINVAL;
+
+ /* Need to resize the fb object !!! */
+- if (var->xres > fb->width || var->yres > fb->height) {
+- DRM_ERROR("Requested width/height is greater than current fb "
+- "object %dx%d > %dx%d\n", var->xres, var->yres,
+- fb->width, fb->height);
+- DRM_ERROR("Need resizing code.\n");
++ if (var->bits_per_pixel > fb->bits_per_pixel || var->xres > fb->width || var->yres > fb->height) {
++ DRM_DEBUG("fb userspace requested width/height/bpp is greater than current fb "
++ "object %dx%d-%d > %dx%d-%d\n", var->xres, var->yres, var->bits_per_pixel,
++ fb->width, fb->height, fb->bits_per_pixel);
+ return -EINVAL;
+ }
+
+@@ -905,8 +907,13 @@ int drm_fb_helper_single_fb_probe(struct drm_device *dev,
+
+ if (new_fb) {
+ info->var.pixclock = 0;
+- if (register_framebuffer(info) < 0)
++ ret = fb_alloc_cmap(&info->cmap, modeset->crtc->gamma_size, 0);
++ if (ret)
++ return ret;
++ if (register_framebuffer(info) < 0) {
++ fb_dealloc_cmap(&info->cmap);
+ return -EINVAL;
++ }
+ } else {
+ drm_fb_helper_set_par(info);
+ }
+@@ -936,6 +943,7 @@ void drm_fb_helper_free(struct drm_fb_helper *helper)
+ unregister_sysrq_key('v', &sysrq_drm_fb_helper_restore_op);
+ }
+ drm_fb_helper_crtc_free(helper);
++ fb_dealloc_cmap(&helper->fb->fbdev->cmap);
+ }
+ EXPORT_SYMBOL(drm_fb_helper_free);
+
+diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
+index 251bc0e..08d14df 100644
+--- a/drivers/gpu/drm/drm_fops.c
++++ b/drivers/gpu/drm/drm_fops.c
+@@ -257,6 +257,9 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
+
+ INIT_LIST_HEAD(&priv->lhead);
+ INIT_LIST_HEAD(&priv->fbs);
++ INIT_LIST_HEAD(&priv->event_list);
++ init_waitqueue_head(&priv->event_wait);
++ priv->event_space = 4096; /* set aside 4k for event buffer */
+
+ if (dev->driver->driver_features & DRIVER_GEM)
+ drm_gem_open(dev, priv);
+@@ -297,6 +300,18 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
+ goto out_free;
+ }
+ }
++ mutex_lock(&dev->struct_mutex);
++ if (dev->driver->master_set) {
++ ret = dev->driver->master_set(dev, priv, true);
++ if (ret) {
++ /* drop both references if this fails */
++ drm_master_put(&priv->minor->master);
++ drm_master_put(&priv->master);
++ mutex_unlock(&dev->struct_mutex);
++ goto out_free;
++ }
++ }
++ mutex_unlock(&dev->struct_mutex);
+ } else {
+ /* get a reference to the master */
+ priv->master = drm_master_get(priv->minor->master);
+@@ -413,6 +428,30 @@ static void drm_master_release(struct drm_device *dev, struct file *filp)
+ }
+ }
+
++static void drm_events_release(struct drm_file *file_priv)
++{
++ struct drm_device *dev = file_priv->minor->dev;
++ struct drm_pending_event *e, *et;
++ struct drm_pending_vblank_event *v, *vt;
++ unsigned long flags;
++
++ spin_lock_irqsave(&dev->event_lock, flags);
++
++ /* Remove pending flips */
++ list_for_each_entry_safe(v, vt, &dev->vblank_event_list, base.link)
++ if (v->base.file_priv == file_priv) {
++ list_del(&v->base.link);
++ drm_vblank_put(dev, v->pipe);
++ v->base.destroy(&v->base);
++ }
++
++ /* Remove unconsumed events */
++ list_for_each_entry_safe(e, et, &file_priv->event_list, link)
++ e->destroy(e);
++
++ spin_unlock_irqrestore(&dev->event_lock, flags);
++}
++
+ /**
+ * Release file.
+ *
+@@ -451,6 +490,8 @@ int drm_release(struct inode *inode, struct file *filp)
+ if (file_priv->minor->master)
+ drm_master_release(dev, filp);
+
++ drm_events_release(file_priv);
++
+ if (dev->driver->driver_features & DRIVER_GEM)
+ drm_gem_release(dev, file_priv);
+
+@@ -504,6 +545,8 @@ int drm_release(struct inode *inode, struct file *filp)
+
+ if (file_priv->minor->master == file_priv->master) {
+ /* drop the reference held my the minor */
++ if (dev->driver->master_drop)
++ dev->driver->master_drop(dev, file_priv, true);
+ drm_master_put(&file_priv->minor->master);
+ }
+ }
+@@ -544,9 +587,74 @@ int drm_release(struct inode *inode, struct file *filp)
+ }
+ EXPORT_SYMBOL(drm_release);
+
+-/** No-op. */
++static bool
++drm_dequeue_event(struct drm_file *file_priv,
++ size_t total, size_t max, struct drm_pending_event **out)
++{
++ struct drm_device *dev = file_priv->minor->dev;
++ struct drm_pending_event *e;
++ unsigned long flags;
++ bool ret = false;
++
++ spin_lock_irqsave(&dev->event_lock, flags);
++
++ *out = NULL;
++ if (list_empty(&file_priv->event_list))
++ goto out;
++ e = list_first_entry(&file_priv->event_list,
++ struct drm_pending_event, link);
++ if (e->event->length + total > max)
++ goto out;
++
++ file_priv->event_space += e->event->length;
++ list_del(&e->link);
++ *out = e;
++ ret = true;
++
++out:
++ spin_unlock_irqrestore(&dev->event_lock, flags);
++ return ret;
++}
++
++ssize_t drm_read(struct file *filp, char __user *buffer,
++ size_t count, loff_t *offset)
++{
++ struct drm_file *file_priv = filp->private_data;
++ struct drm_pending_event *e;
++ size_t total;
++ ssize_t ret;
++
++ ret = wait_event_interruptible(file_priv->event_wait,
++ !list_empty(&file_priv->event_list));
++ if (ret < 0)
++ return ret;
++
++ total = 0;
++ while (drm_dequeue_event(file_priv, total, count, &e)) {
++ if (copy_to_user(buffer + total,
++ e->event, e->event->length)) {
++ total = -EFAULT;
++ break;
++ }
++
++ total += e->event->length;
++ e->destroy(e);
++ }
++
++ return total;
++}
++EXPORT_SYMBOL(drm_read);
++
+ unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait)
+ {
+- return 0;
++ struct drm_file *file_priv = filp->private_data;
++ unsigned int mask = 0;
++
++ poll_wait(filp, &file_priv->event_wait, wait);
++
++ if (!list_empty(&file_priv->event_list))
++ mask |= POLLIN | POLLRDNORM;
++
++ return mask;
+ }
+ EXPORT_SYMBOL(drm_poll);
+diff --git a/drivers/gpu/drm/drm_ioc32.c b/drivers/gpu/drm/drm_ioc32.c
+index 282d9fd..d61d185 100644
+--- a/drivers/gpu/drm/drm_ioc32.c
++++ b/drivers/gpu/drm/drm_ioc32.c
+@@ -104,7 +104,7 @@ static int compat_drm_version(struct file *file, unsigned int cmd,
+ &version->desc))
+ return -EFAULT;
+
+- err = drm_ioctl(file->f_path.dentry->d_inode, file,
++ err = drm_ioctl(file,
+ DRM_IOCTL_VERSION, (unsigned long)version);
+ if (err)
+ return err;
+@@ -145,8 +145,7 @@ static int compat_drm_getunique(struct file *file, unsigned int cmd,
+ &u->unique))
+ return -EFAULT;
+
+- err = drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_GET_UNIQUE, (unsigned long)u);
++ err = drm_ioctl(file, DRM_IOCTL_GET_UNIQUE, (unsigned long)u);
+ if (err)
+ return err;
+
+@@ -174,8 +173,7 @@ static int compat_drm_setunique(struct file *file, unsigned int cmd,
+ &u->unique))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_SET_UNIQUE, (unsigned long)u);
++ return drm_ioctl(file, DRM_IOCTL_SET_UNIQUE, (unsigned long)u);
+ }
+
+ typedef struct drm_map32 {
+@@ -205,8 +203,7 @@ static int compat_drm_getmap(struct file *file, unsigned int cmd,
+ if (__put_user(idx, &map->offset))
+ return -EFAULT;
+
+- err = drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_GET_MAP, (unsigned long)map);
++ err = drm_ioctl(file, DRM_IOCTL_GET_MAP, (unsigned long)map);
+ if (err)
+ return err;
+
+@@ -246,8 +243,7 @@ static int compat_drm_addmap(struct file *file, unsigned int cmd,
+ || __put_user(m32.flags, &map->flags))
+ return -EFAULT;
+
+- err = drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_ADD_MAP, (unsigned long)map);
++ err = drm_ioctl(file, DRM_IOCTL_ADD_MAP, (unsigned long)map);
+ if (err)
+ return err;
+
+@@ -284,8 +280,7 @@ static int compat_drm_rmmap(struct file *file, unsigned int cmd,
+ if (__put_user((void *)(unsigned long)handle, &map->handle))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_RM_MAP, (unsigned long)map);
++ return drm_ioctl(file, DRM_IOCTL_RM_MAP, (unsigned long)map);
+ }
+
+ typedef struct drm_client32 {
+@@ -314,8 +309,7 @@ static int compat_drm_getclient(struct file *file, unsigned int cmd,
+ if (__put_user(idx, &client->idx))
+ return -EFAULT;
+
+- err = drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_GET_CLIENT, (unsigned long)client);
++ err = drm_ioctl(file, DRM_IOCTL_GET_CLIENT, (unsigned long)client);
+ if (err)
+ return err;
+
+@@ -351,8 +345,7 @@ static int compat_drm_getstats(struct file *file, unsigned int cmd,
+ if (!access_ok(VERIFY_WRITE, stats, sizeof(*stats)))
+ return -EFAULT;
+
+- err = drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_GET_STATS, (unsigned long)stats);
++ err = drm_ioctl(file, DRM_IOCTL_GET_STATS, (unsigned long)stats);
+ if (err)
+ return err;
+
+@@ -395,8 +388,7 @@ static int compat_drm_addbufs(struct file *file, unsigned int cmd,
+ || __put_user(agp_start, &buf->agp_start))
+ return -EFAULT;
+
+- err = drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_ADD_BUFS, (unsigned long)buf);
++ err = drm_ioctl(file, DRM_IOCTL_ADD_BUFS, (unsigned long)buf);
+ if (err)
+ return err;
+
+@@ -427,8 +419,7 @@ static int compat_drm_markbufs(struct file *file, unsigned int cmd,
+ || __put_user(b32.high_mark, &buf->high_mark))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_MARK_BUFS, (unsigned long)buf);
++ return drm_ioctl(file, DRM_IOCTL_MARK_BUFS, (unsigned long)buf);
+ }
+
+ typedef struct drm_buf_info32 {
+@@ -469,8 +460,7 @@ static int compat_drm_infobufs(struct file *file, unsigned int cmd,
+ || __put_user(list, &request->list))
+ return -EFAULT;
+
+- err = drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_INFO_BUFS, (unsigned long)request);
++ err = drm_ioctl(file, DRM_IOCTL_INFO_BUFS, (unsigned long)request);
+ if (err)
+ return err;
+
+@@ -531,8 +521,7 @@ static int compat_drm_mapbufs(struct file *file, unsigned int cmd,
+ || __put_user(list, &request->list))
+ return -EFAULT;
+
+- err = drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_MAP_BUFS, (unsigned long)request);
++ err = drm_ioctl(file, DRM_IOCTL_MAP_BUFS, (unsigned long)request);
+ if (err)
+ return err;
+
+@@ -578,8 +567,7 @@ static int compat_drm_freebufs(struct file *file, unsigned int cmd,
+ &request->list))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_FREE_BUFS, (unsigned long)request);
++ return drm_ioctl(file, DRM_IOCTL_FREE_BUFS, (unsigned long)request);
+ }
+
+ typedef struct drm_ctx_priv_map32 {
+@@ -605,8 +593,7 @@ static int compat_drm_setsareactx(struct file *file, unsigned int cmd,
+ &request->handle))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_SET_SAREA_CTX, (unsigned long)request);
++ return drm_ioctl(file, DRM_IOCTL_SET_SAREA_CTX, (unsigned long)request);
+ }
+
+ static int compat_drm_getsareactx(struct file *file, unsigned int cmd,
+@@ -628,8 +615,7 @@ static int compat_drm_getsareactx(struct file *file, unsigned int cmd,
+ if (__put_user(ctx_id, &request->ctx_id))
+ return -EFAULT;
+
+- err = drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_GET_SAREA_CTX, (unsigned long)request);
++ err = drm_ioctl(file, DRM_IOCTL_GET_SAREA_CTX, (unsigned long)request);
+ if (err)
+ return err;
+
+@@ -664,8 +650,7 @@ static int compat_drm_resctx(struct file *file, unsigned int cmd,
+ &res->contexts))
+ return -EFAULT;
+
+- err = drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_RES_CTX, (unsigned long)res);
++ err = drm_ioctl(file, DRM_IOCTL_RES_CTX, (unsigned long)res);
+ if (err)
+ return err;
+
+@@ -718,8 +703,7 @@ static int compat_drm_dma(struct file *file, unsigned int cmd,
+ &d->request_sizes))
+ return -EFAULT;
+
+- err = drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_DMA, (unsigned long)d);
++ err = drm_ioctl(file, DRM_IOCTL_DMA, (unsigned long)d);
+ if (err)
+ return err;
+
+@@ -751,8 +735,7 @@ static int compat_drm_agp_enable(struct file *file, unsigned int cmd,
+ if (put_user(m32.mode, &mode->mode))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_AGP_ENABLE, (unsigned long)mode);
++ return drm_ioctl(file, DRM_IOCTL_AGP_ENABLE, (unsigned long)mode);
+ }
+
+ typedef struct drm_agp_info32 {
+@@ -781,8 +764,7 @@ static int compat_drm_agp_info(struct file *file, unsigned int cmd,
+ if (!access_ok(VERIFY_WRITE, info, sizeof(*info)))
+ return -EFAULT;
+
+- err = drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_AGP_INFO, (unsigned long)info);
++ err = drm_ioctl(file, DRM_IOCTL_AGP_INFO, (unsigned long)info);
+ if (err)
+ return err;
+
+@@ -827,16 +809,14 @@ static int compat_drm_agp_alloc(struct file *file, unsigned int cmd,
+ || __put_user(req32.type, &request->type))
+ return -EFAULT;
+
+- err = drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_AGP_ALLOC, (unsigned long)request);
++ err = drm_ioctl(file, DRM_IOCTL_AGP_ALLOC, (unsigned long)request);
+ if (err)
+ return err;
+
+ if (__get_user(req32.handle, &request->handle)
+ || __get_user(req32.physical, &request->physical)
+ || copy_to_user(argp, &req32, sizeof(req32))) {
+- drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_AGP_FREE, (unsigned long)request);
++ drm_ioctl(file, DRM_IOCTL_AGP_FREE, (unsigned long)request);
+ return -EFAULT;
+ }
+
+@@ -856,8 +836,7 @@ static int compat_drm_agp_free(struct file *file, unsigned int cmd,
+ || __put_user(handle, &request->handle))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_AGP_FREE, (unsigned long)request);
++ return drm_ioctl(file, DRM_IOCTL_AGP_FREE, (unsigned long)request);
+ }
+
+ typedef struct drm_agp_binding32 {
+@@ -881,8 +860,7 @@ static int compat_drm_agp_bind(struct file *file, unsigned int cmd,
+ || __put_user(req32.offset, &request->offset))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_AGP_BIND, (unsigned long)request);
++ return drm_ioctl(file, DRM_IOCTL_AGP_BIND, (unsigned long)request);
+ }
+
+ static int compat_drm_agp_unbind(struct file *file, unsigned int cmd,
+@@ -898,8 +876,7 @@ static int compat_drm_agp_unbind(struct file *file, unsigned int cmd,
+ || __put_user(handle, &request->handle))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_AGP_UNBIND, (unsigned long)request);
++ return drm_ioctl(file, DRM_IOCTL_AGP_UNBIND, (unsigned long)request);
+ }
+ #endif /* __OS_HAS_AGP */
+
+@@ -923,8 +900,7 @@ static int compat_drm_sg_alloc(struct file *file, unsigned int cmd,
+ || __put_user(x, &request->size))
+ return -EFAULT;
+
+- err = drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_SG_ALLOC, (unsigned long)request);
++ err = drm_ioctl(file, DRM_IOCTL_SG_ALLOC, (unsigned long)request);
+ if (err)
+ return err;
+
+@@ -950,8 +926,7 @@ static int compat_drm_sg_free(struct file *file, unsigned int cmd,
+ || __put_user(x << PAGE_SHIFT, &request->handle))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_SG_FREE, (unsigned long)request);
++ return drm_ioctl(file, DRM_IOCTL_SG_FREE, (unsigned long)request);
+ }
+
+ #if defined(CONFIG_X86) || defined(CONFIG_IA64)
+@@ -981,8 +956,7 @@ static int compat_drm_update_draw(struct file *file, unsigned int cmd,
+ __put_user(update32.data, &request->data))
+ return -EFAULT;
+
+- err = drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_UPDATE_DRAW, (unsigned long)request);
++ err = drm_ioctl(file, DRM_IOCTL_UPDATE_DRAW, (unsigned long)request);
+ return err;
+ }
+ #endif
+@@ -1023,8 +997,7 @@ static int compat_drm_wait_vblank(struct file *file, unsigned int cmd,
+ || __put_user(req32.request.signal, &request->request.signal))
+ return -EFAULT;
+
+- err = drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_WAIT_VBLANK, (unsigned long)request);
++ err = drm_ioctl(file, DRM_IOCTL_WAIT_VBLANK, (unsigned long)request);
+ if (err)
+ return err;
+
+@@ -1094,16 +1067,14 @@ long drm_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+ * than always failing.
+ */
+ if (nr >= ARRAY_SIZE(drm_compat_ioctls))
+- return drm_ioctl(filp->f_dentry->d_inode, filp, cmd, arg);
++ return drm_ioctl(filp, cmd, arg);
+
+ fn = drm_compat_ioctls[nr];
+
+- lock_kernel(); /* XXX for now */
+ if (fn != NULL)
+ ret = (*fn) (filp, cmd, arg);
+ else
+- ret = drm_ioctl(filp->f_path.dentry->d_inode, filp, cmd, arg);
+- unlock_kernel();
++ ret = drm_ioctl(filp, cmd, arg);
+
+ return ret;
+ }
+diff --git a/drivers/gpu/drm/drm_irq.c b/drivers/gpu/drm/drm_irq.c
+index 332d743..b98384d 100644
+--- a/drivers/gpu/drm/drm_irq.c
++++ b/drivers/gpu/drm/drm_irq.c
+@@ -115,6 +115,7 @@ void drm_vblank_cleanup(struct drm_device *dev)
+
+ dev->num_crtcs = 0;
+ }
++EXPORT_SYMBOL(drm_vblank_cleanup);
+
+ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
+ {
+@@ -163,7 +164,6 @@ int drm_vblank_init(struct drm_device *dev, int num_crtcs)
+ }
+
+ dev->vblank_disable_allowed = 0;
+-
+ return 0;
+
+ err:
+@@ -493,6 +493,9 @@ EXPORT_SYMBOL(drm_vblank_off);
+ */
+ void drm_vblank_pre_modeset(struct drm_device *dev, int crtc)
+ {
++ /* vblank is not initialized (IRQ not installed ?) */
++ if (!dev->num_crtcs)
++ return;
+ /*
+ * To avoid all the problems that might happen if interrupts
+ * were enabled/disabled around or between these calls, we just
+@@ -568,6 +571,63 @@ out:
+ return ret;
+ }
+
++static int drm_queue_vblank_event(struct drm_device *dev, int pipe,
++ union drm_wait_vblank *vblwait,
++ struct drm_file *file_priv)
++{
++ struct drm_pending_vblank_event *e;
++ struct timeval now;
++ unsigned long flags;
++ unsigned int seq;
++
++ e = kzalloc(sizeof *e, GFP_KERNEL);
++ if (e == NULL)
++ return -ENOMEM;
++
++ e->pipe = pipe;
++ e->event.base.type = DRM_EVENT_VBLANK;
++ e->event.base.length = sizeof e->event;
++ e->event.user_data = vblwait->request.signal;
++ e->base.event = &e->event.base;
++ e->base.file_priv = file_priv;
++ e->base.destroy = (void (*) (struct drm_pending_event *)) kfree;
++
++ do_gettimeofday(&now);
++ spin_lock_irqsave(&dev->event_lock, flags);
++
++ if (file_priv->event_space < sizeof e->event) {
++ spin_unlock_irqrestore(&dev->event_lock, flags);
++ kfree(e);
++ return -ENOMEM;
++ }
++
++ file_priv->event_space -= sizeof e->event;
++ seq = drm_vblank_count(dev, pipe);
++ if ((vblwait->request.type & _DRM_VBLANK_NEXTONMISS) &&
++ (seq - vblwait->request.sequence) <= (1 << 23)) {
++ vblwait->request.sequence = seq + 1;
++ vblwait->reply.sequence = vblwait->request.sequence;
++ }
++
++ DRM_DEBUG("event on vblank count %d, current %d, crtc %d\n",
++ vblwait->request.sequence, seq, pipe);
++
++ e->event.sequence = vblwait->request.sequence;
++ if ((seq - vblwait->request.sequence) <= (1 << 23)) {
++ e->event.tv_sec = now.tv_sec;
++ e->event.tv_usec = now.tv_usec;
++ drm_vblank_put(dev, e->pipe);
++ list_add_tail(&e->base.link, &e->base.file_priv->event_list);
++ wake_up_interruptible(&e->base.file_priv->event_wait);
++ } else {
++ list_add_tail(&e->base.link, &dev->vblank_event_list);
++ }
++
++ spin_unlock_irqrestore(&dev->event_lock, flags);
++
++ return 0;
++}
++
+ /**
+ * Wait for VBLANK.
+ *
+@@ -627,6 +687,9 @@ int drm_wait_vblank(struct drm_device *dev, void *data,
+ goto done;
+ }
+
++ if (flags & _DRM_VBLANK_EVENT)
++ return drm_queue_vblank_event(dev, crtc, vblwait, file_priv);
++
+ if ((flags & _DRM_VBLANK_NEXTONMISS) &&
+ (seq - vblwait->request.sequence) <= (1<<23)) {
+ vblwait->request.sequence = seq + 1;
+@@ -659,6 +722,38 @@ done:
+ return ret;
+ }
+
++void drm_handle_vblank_events(struct drm_device *dev, int crtc)
++{
++ struct drm_pending_vblank_event *e, *t;
++ struct timeval now;
++ unsigned long flags;
++ unsigned int seq;
++
++ do_gettimeofday(&now);
++ seq = drm_vblank_count(dev, crtc);
++
++ spin_lock_irqsave(&dev->event_lock, flags);
++
++ list_for_each_entry_safe(e, t, &dev->vblank_event_list, base.link) {
++ if (e->pipe != crtc)
++ continue;
++ if ((seq - e->event.sequence) > (1<<23))
++ continue;
++
++ DRM_DEBUG("vblank event on %d, current %d\n",
++ e->event.sequence, seq);
++
++ e->event.sequence = seq;
++ e->event.tv_sec = now.tv_sec;
++ e->event.tv_usec = now.tv_usec;
++ drm_vblank_put(dev, e->pipe);
++ list_move_tail(&e->base.link, &e->base.file_priv->event_list);
++ wake_up_interruptible(&e->base.file_priv->event_wait);
++ }
++
++ spin_unlock_irqrestore(&dev->event_lock, flags);
++}
++
+ /**
+ * drm_handle_vblank - handle a vblank event
+ * @dev: DRM device
+@@ -669,7 +764,11 @@ done:
+ */
+ void drm_handle_vblank(struct drm_device *dev, int crtc)
+ {
++ if (!dev->num_crtcs)
++ return;
++
+ atomic_inc(&dev->_vblank_count[crtc]);
+ DRM_WAKEUP(&dev->vbl_queue[crtc]);
++ drm_handle_vblank_events(dev, crtc);
+ }
+ EXPORT_SYMBOL(drm_handle_vblank);
+diff --git a/drivers/gpu/drm/drm_mm.c b/drivers/gpu/drm/drm_mm.c
+index 97dc5a4..2ac074c 100644
+--- a/drivers/gpu/drm/drm_mm.c
++++ b/drivers/gpu/drm/drm_mm.c
+@@ -226,6 +226,44 @@ struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *node,
+ }
+ EXPORT_SYMBOL(drm_mm_get_block_generic);
+
++struct drm_mm_node *drm_mm_get_block_range_generic(struct drm_mm_node *node,
++ unsigned long size,
++ unsigned alignment,
++ unsigned long start,
++ unsigned long end,
++ int atomic)
++{
++ struct drm_mm_node *align_splitoff = NULL;
++ unsigned tmp = 0;
++ unsigned wasted = 0;
++
++ if (node->start < start)
++ wasted += start - node->start;
++ if (alignment)
++ tmp = ((node->start + wasted) % alignment);
++
++ if (tmp)
++ wasted += alignment - tmp;
++ if (wasted) {
++ align_splitoff = drm_mm_split_at_start(node, wasted, atomic);
++ if (unlikely(align_splitoff == NULL))
++ return NULL;
++ }
++
++ if (node->size == size) {
++ list_del_init(&node->fl_entry);
++ node->free = 0;
++ } else {
++ node = drm_mm_split_at_start(node, size, atomic);
++ }
++
++ if (align_splitoff)
++ drm_mm_put_block(align_splitoff);
++
++ return node;
++}
++EXPORT_SYMBOL(drm_mm_get_block_range_generic);
++
+ /*
+ * Put a block. Merge with the previous and / or next block if they are free.
+ * Otherwise add to the free stack.
+@@ -320,7 +358,7 @@ struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm,
+ if (entry->size >= size + wasted) {
+ if (!best_match)
+ return entry;
+- if (size < best_size) {
++ if (entry->size < best_size) {
+ best = entry;
+ best_size = entry->size;
+ }
+@@ -331,6 +369,57 @@ struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm,
+ }
+ EXPORT_SYMBOL(drm_mm_search_free);
+
++struct drm_mm_node *drm_mm_search_free_in_range(const struct drm_mm *mm,
++ unsigned long size,
++ unsigned alignment,
++ unsigned long start,
++ unsigned long end,
++ int best_match)
++{
++ struct list_head *list;
++ const struct list_head *free_stack = &mm->fl_entry;
++ struct drm_mm_node *entry;
++ struct drm_mm_node *best;
++ unsigned long best_size;
++ unsigned wasted;
++
++ best = NULL;
++ best_size = ~0UL;
++
++ list_for_each(list, free_stack) {
++ entry = list_entry(list, struct drm_mm_node, fl_entry);
++ wasted = 0;
++
++ if (entry->size < size)
++ continue;
++
++ if (entry->start > end || (entry->start+entry->size) < start)
++ continue;
++
++ if (entry->start < start)
++ wasted += start - entry->start;
++
++ if (alignment) {
++ register unsigned tmp = (entry->start + wasted) % alignment;
++ if (tmp)
++ wasted += alignment - tmp;
++ }
++
++ if (entry->size >= size + wasted &&
++ (entry->start + wasted + size) <= end) {
++ if (!best_match)
++ return entry;
++ if (entry->size < best_size) {
++ best = entry;
++ best_size = entry->size;
++ }
++ }
++ }
++
++ return best;
++}
++EXPORT_SYMBOL(drm_mm_search_free_in_range);
++
+ int drm_mm_clean(struct drm_mm * mm)
+ {
+ struct list_head *head = &mm->ml_entry;
+@@ -381,6 +470,26 @@ void drm_mm_takedown(struct drm_mm * mm)
+ }
+ EXPORT_SYMBOL(drm_mm_takedown);
+
++void drm_mm_debug_table(struct drm_mm *mm, const char *prefix)
++{
++ struct drm_mm_node *entry;
++ int total_used = 0, total_free = 0, total = 0;
++
++ list_for_each_entry(entry, &mm->ml_entry, ml_entry) {
++ printk(KERN_DEBUG "%s 0x%08lx-0x%08lx: %8ld: %s\n",
++ prefix, entry->start, entry->start + entry->size,
++ entry->size, entry->free ? "free" : "used");
++ total += entry->size;
++ if (entry->free)
++ total_free += entry->size;
++ else
++ total_used += entry->size;
++ }
++ printk(KERN_DEBUG "%s total: %d, used %d free %d\n", prefix, total,
++ total_used, total_free);
++}
++EXPORT_SYMBOL(drm_mm_debug_table);
++
+ #if defined(CONFIG_DEBUG_FS)
+ int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
+ {
+@@ -395,7 +504,7 @@ int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm)
+ else
+ total_used += entry->size;
+ }
+- seq_printf(m, "total: %d, used %d free %d\n", total, total_free, total_used);
++ seq_printf(m, "total: %d, used %d free %d\n", total, total_used, total_free);
+ return 0;
+ }
+ EXPORT_SYMBOL(drm_mm_dump_table);
+diff --git a/drivers/gpu/drm/drm_modes.c b/drivers/gpu/drm/drm_modes.c
+index 51f6772..76d6339 100644
+--- a/drivers/gpu/drm/drm_modes.c
++++ b/drivers/gpu/drm/drm_modes.c
+@@ -1,9 +1,4 @@
+ /*
+- * The list_sort function is (presumably) licensed under the GPL (see the
+- * top level "COPYING" file for details).
+- *
+- * The remainder of this file is:
+- *
+ * Copyright © 1997-2003 by The XFree86 Project, Inc.
+ * Copyright © 2007 Dave Airlie
+ * Copyright © 2007-2008 Intel Corporation
+@@ -36,6 +31,7 @@
+ */
+
+ #include <linux/list.h>
++#include <linux/list_sort.h>
+ #include "drmP.h"
+ #include "drm.h"
+ #include "drm_crtc.h"
+@@ -553,6 +549,32 @@ int drm_mode_height(struct drm_display_mode *mode)
+ }
+ EXPORT_SYMBOL(drm_mode_height);
+
++/** drm_mode_hsync - get the hsync of a mode
++ * @mode: mode
++ *
++ * LOCKING:
++ * None.
++ *
++ * Return @modes's hsync rate in kHz, rounded to the nearest int.
++ */
++int drm_mode_hsync(struct drm_display_mode *mode)
++{
++ unsigned int calc_val;
++
++ if (mode->hsync)
++ return mode->hsync;
++
++ if (mode->htotal < 0)
++ return 0;
++
++ calc_val = (mode->clock * 1000) / mode->htotal; /* hsync in Hz */
++ calc_val += 500; /* round to 1000Hz */
++ calc_val /= 1000; /* truncate to kHz */
++
++ return calc_val;
++}
++EXPORT_SYMBOL(drm_mode_hsync);
++
+ /**
+ * drm_mode_vrefresh - get the vrefresh of a mode
+ * @mode: mode
+@@ -560,7 +582,7 @@ EXPORT_SYMBOL(drm_mode_height);
+ * LOCKING:
+ * None.
+ *
+- * Return @mode's vrefresh rate or calculate it if necessary.
++ * Return @mode's vrefresh rate in Hz or calculate it if necessary.
+ *
+ * FIXME: why is this needed? shouldn't vrefresh be set already?
+ *
+@@ -829,6 +851,7 @@ EXPORT_SYMBOL(drm_mode_prune_invalid);
+
+ /**
+ * drm_mode_compare - compare modes for favorability
++ * @priv: unused
+ * @lh_a: list_head for first mode
+ * @lh_b: list_head for second mode
+ *
+@@ -842,7 +865,7 @@ EXPORT_SYMBOL(drm_mode_prune_invalid);
+ * Negative if @lh_a is better than @lh_b, zero if they're equivalent, or
+ * positive if @lh_b is better than @lh_a.
+ */
+-static int drm_mode_compare(struct list_head *lh_a, struct list_head *lh_b)
++static int drm_mode_compare(void *priv, struct list_head *lh_a, struct list_head *lh_b)
+ {
+ struct drm_display_mode *a = list_entry(lh_a, struct drm_display_mode, head);
+ struct drm_display_mode *b = list_entry(lh_b, struct drm_display_mode, head);
+@@ -859,85 +882,6 @@ static int drm_mode_compare(struct list_head *lh_a, struct list_head *lh_b)
+ return diff;
+ }
+
+-/* FIXME: what we don't have a list sort function? */
+-/* list sort from Mark J Roberts (mjr@znex.org) */
+-void list_sort(struct list_head *head,
+- int (*cmp)(struct list_head *a, struct list_head *b))
+-{
+- struct list_head *p, *q, *e, *list, *tail, *oldhead;
+- int insize, nmerges, psize, qsize, i;
+-
+- list = head->next;
+- list_del(head);
+- insize = 1;
+- for (;;) {
+- p = oldhead = list;
+- list = tail = NULL;
+- nmerges = 0;
+-
+- while (p) {
+- nmerges++;
+- q = p;
+- psize = 0;
+- for (i = 0; i < insize; i++) {
+- psize++;
+- q = q->next == oldhead ? NULL : q->next;
+- if (!q)
+- break;
+- }
+-
+- qsize = insize;
+- while (psize > 0 || (qsize > 0 && q)) {
+- if (!psize) {
+- e = q;
+- q = q->next;
+- qsize--;
+- if (q == oldhead)
+- q = NULL;
+- } else if (!qsize || !q) {
+- e = p;
+- p = p->next;
+- psize--;
+- if (p == oldhead)
+- p = NULL;
+- } else if (cmp(p, q) <= 0) {
+- e = p;
+- p = p->next;
+- psize--;
+- if (p == oldhead)
+- p = NULL;
+- } else {
+- e = q;
+- q = q->next;
+- qsize--;
+- if (q == oldhead)
+- q = NULL;
+- }
+- if (tail)
+- tail->next = e;
+- else
+- list = e;
+- e->prev = tail;
+- tail = e;
+- }
+- p = q;
+- }
+-
+- tail->next = list;
+- list->prev = tail;
+-
+- if (nmerges <= 1)
+- break;
+-
+- insize *= 2;
+- }
+-
+- head->next = list;
+- head->prev = list->prev;
+- list->prev->next = head;
+- list->prev = head;
+-}
+-
+ /**
+ * drm_mode_sort - sort mode list
+ * @mode_list: list to sort
+@@ -949,7 +893,7 @@ void list_sort(struct list_head *head,
+ */
+ void drm_mode_sort(struct list_head *mode_list)
+ {
+- list_sort(mode_list, drm_mode_compare);
++ list_sort(NULL, mode_list, drm_mode_compare);
+ }
+ EXPORT_SYMBOL(drm_mode_sort);
+
+diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
+index 55bb8a8..ad73e14 100644
+--- a/drivers/gpu/drm/drm_stub.c
++++ b/drivers/gpu/drm/drm_stub.c
+@@ -128,6 +128,7 @@ struct drm_master *drm_master_get(struct drm_master *master)
+ kref_get(&master->refcount);
+ return master;
+ }
++EXPORT_SYMBOL(drm_master_get);
+
+ static void drm_master_destroy(struct kref *kref)
+ {
+@@ -170,10 +171,13 @@ void drm_master_put(struct drm_master **master)
+ kref_put(&(*master)->refcount, drm_master_destroy);
+ *master = NULL;
+ }
++EXPORT_SYMBOL(drm_master_put);
+
+ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv)
+ {
++ int ret = 0;
++
+ if (file_priv->is_master)
+ return 0;
+
+@@ -188,6 +192,13 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
+ mutex_lock(&dev->struct_mutex);
+ file_priv->minor->master = drm_master_get(file_priv->master);
+ file_priv->is_master = 1;
++ if (dev->driver->master_set) {
++ ret = dev->driver->master_set(dev, file_priv, false);
++ if (unlikely(ret != 0)) {
++ file_priv->is_master = 0;
++ drm_master_put(&file_priv->minor->master);
++ }
++ }
+ mutex_unlock(&dev->struct_mutex);
+ }
+
+@@ -204,6 +215,8 @@ int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
+ return -EINVAL;
+
+ mutex_lock(&dev->struct_mutex);
++ if (dev->driver->master_drop)
++ dev->driver->master_drop(dev, file_priv, false);
+ drm_master_put(&file_priv->minor->master);
+ file_priv->is_master = 0;
+ mutex_unlock(&dev->struct_mutex);
+@@ -220,9 +233,11 @@ static int drm_fill_in_dev(struct drm_device * dev, struct pci_dev *pdev,
+ INIT_LIST_HEAD(&dev->ctxlist);
+ INIT_LIST_HEAD(&dev->vmalist);
+ INIT_LIST_HEAD(&dev->maplist);
++ INIT_LIST_HEAD(&dev->vblank_event_list);
+
+ spin_lock_init(&dev->count_lock);
+ spin_lock_init(&dev->drw_lock);
++ spin_lock_init(&dev->event_lock);
+ init_timer(&dev->timer);
+ mutex_init(&dev->struct_mutex);
+ mutex_init(&dev->ctxlist_mutex);
+diff --git a/drivers/gpu/drm/i2c/Makefile b/drivers/gpu/drm/i2c/Makefile
+new file mode 100644
+index 0000000..6d2abaf
+--- /dev/null
++++ b/drivers/gpu/drm/i2c/Makefile
+@@ -0,0 +1,4 @@
++ccflags-y := -Iinclude/drm
++
++ch7006-y := ch7006_drv.o ch7006_mode.o
++obj-$(CONFIG_DRM_I2C_CH7006) += ch7006.o
+diff --git a/drivers/gpu/drm/i2c/ch7006_drv.c b/drivers/gpu/drm/i2c/ch7006_drv.c
+new file mode 100644
+index 0000000..81681a0
+--- /dev/null
++++ b/drivers/gpu/drm/i2c/ch7006_drv.c
+@@ -0,0 +1,536 @@
++/*
++ * Copyright (C) 2009 Francisco Jerez.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "ch7006_priv.h"
++
++/* DRM encoder functions */
++
++static void ch7006_encoder_set_config(struct drm_encoder *encoder,
++ void *params)
++{
++ struct ch7006_priv *priv = to_ch7006_priv(encoder);
++
++ priv->params = params;
++}
++
++static void ch7006_encoder_destroy(struct drm_encoder *encoder)
++{
++ struct ch7006_priv *priv = to_ch7006_priv(encoder);
++
++ drm_property_destroy(encoder->dev, priv->scale_property);
++
++ kfree(priv);
++ to_encoder_slave(encoder)->slave_priv = NULL;
++
++ drm_i2c_encoder_destroy(encoder);
++}
++
++static void ch7006_encoder_dpms(struct drm_encoder *encoder, int mode)
++{
++ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
++ struct ch7006_priv *priv = to_ch7006_priv(encoder);
++ struct ch7006_state *state = &priv->state;
++
++ ch7006_dbg(client, "\n");
++
++ if (mode == priv->last_dpms)
++ return;
++ priv->last_dpms = mode;
++
++ ch7006_setup_power_state(encoder);
++
++ ch7006_load_reg(client, state, CH7006_POWER);
++}
++
++static void ch7006_encoder_save(struct drm_encoder *encoder)
++{
++ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
++ struct ch7006_priv *priv = to_ch7006_priv(encoder);
++
++ ch7006_dbg(client, "\n");
++
++ ch7006_state_save(client, &priv->saved_state);
++}
++
++static void ch7006_encoder_restore(struct drm_encoder *encoder)
++{
++ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
++ struct ch7006_priv *priv = to_ch7006_priv(encoder);
++
++ ch7006_dbg(client, "\n");
++
++ ch7006_state_load(client, &priv->saved_state);
++}
++
++static bool ch7006_encoder_mode_fixup(struct drm_encoder *encoder,
++ struct drm_display_mode *mode,
++ struct drm_display_mode *adjusted_mode)
++{
++ struct ch7006_priv *priv = to_ch7006_priv(encoder);
++
++ /* The ch7006 is painfully picky with the input timings so no
++ * custom modes for now... */
++
++ priv->mode = ch7006_lookup_mode(encoder, mode);
++
++ return !!priv->mode;
++}
++
++static int ch7006_encoder_mode_valid(struct drm_encoder *encoder,
++ struct drm_display_mode *mode)
++{
++ if (ch7006_lookup_mode(encoder, mode))
++ return MODE_OK;
++ else
++ return MODE_BAD;
++}
++
++static void ch7006_encoder_mode_set(struct drm_encoder *encoder,
++ struct drm_display_mode *drm_mode,
++ struct drm_display_mode *adjusted_mode)
++{
++ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
++ struct ch7006_priv *priv = to_ch7006_priv(encoder);
++ struct ch7006_encoder_params *params = priv->params;
++ struct ch7006_state *state = &priv->state;
++ uint8_t *regs = state->regs;
++ struct ch7006_mode *mode = priv->mode;
++ struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
++ int start_active;
++
++ ch7006_dbg(client, "\n");
++
++ regs[CH7006_DISPMODE] = norm->dispmode | mode->dispmode;
++ regs[CH7006_BWIDTH] = 0;
++ regs[CH7006_INPUT_FORMAT] = bitf(CH7006_INPUT_FORMAT_FORMAT,
++ params->input_format);
++
++ regs[CH7006_CLKMODE] = CH7006_CLKMODE_SUBC_LOCK
++ | bitf(CH7006_CLKMODE_XCM, params->xcm)
++ | bitf(CH7006_CLKMODE_PCM, params->pcm);
++ if (params->clock_mode)
++ regs[CH7006_CLKMODE] |= CH7006_CLKMODE_MASTER;
++ if (params->clock_edge)
++ regs[CH7006_CLKMODE] |= CH7006_CLKMODE_POS_EDGE;
++
++ start_active = (drm_mode->htotal & ~0x7) - (drm_mode->hsync_start & ~0x7);
++ regs[CH7006_POV] = bitf(CH7006_POV_START_ACTIVE_8, start_active);
++ regs[CH7006_START_ACTIVE] = bitf(CH7006_START_ACTIVE_0, start_active);
++
++ regs[CH7006_INPUT_SYNC] = 0;
++ if (params->sync_direction)
++ regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_OUTPUT;
++ if (params->sync_encoding)
++ regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_EMBEDDED;
++ if (drm_mode->flags & DRM_MODE_FLAG_PVSYNC)
++ regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PVSYNC;
++ if (drm_mode->flags & DRM_MODE_FLAG_PHSYNC)
++ regs[CH7006_INPUT_SYNC] |= CH7006_INPUT_SYNC_PHSYNC;
++
++ regs[CH7006_DETECT] = 0;
++ regs[CH7006_BCLKOUT] = 0;
++
++ regs[CH7006_SUBC_INC3] = 0;
++ if (params->pout_level)
++ regs[CH7006_SUBC_INC3] |= CH7006_SUBC_INC3_POUT_3_3V;
++
++ regs[CH7006_SUBC_INC4] = 0;
++ if (params->active_detect)
++ regs[CH7006_SUBC_INC4] |= CH7006_SUBC_INC4_DS_INPUT;
++
++ regs[CH7006_PLL_CONTROL] = priv->saved_state.regs[CH7006_PLL_CONTROL];
++
++ ch7006_setup_levels(encoder);
++ ch7006_setup_subcarrier(encoder);
++ ch7006_setup_pll(encoder);
++ ch7006_setup_power_state(encoder);
++ ch7006_setup_properties(encoder);
++
++ ch7006_state_load(client, state);
++}
++
++static enum drm_connector_status ch7006_encoder_detect(struct drm_encoder *encoder,
++ struct drm_connector *connector)
++{
++ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
++ struct ch7006_priv *priv = to_ch7006_priv(encoder);
++ struct ch7006_state *state = &priv->state;
++ int det;
++
++ ch7006_dbg(client, "\n");
++
++ ch7006_save_reg(client, state, CH7006_DETECT);
++ ch7006_save_reg(client, state, CH7006_POWER);
++ ch7006_save_reg(client, state, CH7006_CLKMODE);
++
++ ch7006_write(client, CH7006_POWER, CH7006_POWER_RESET |
++ bitfs(CH7006_POWER_LEVEL, NORMAL));
++ ch7006_write(client, CH7006_CLKMODE, CH7006_CLKMODE_MASTER);
++
++ ch7006_write(client, CH7006_DETECT, CH7006_DETECT_SENSE);
++
++ ch7006_write(client, CH7006_DETECT, 0);
++
++ det = ch7006_read(client, CH7006_DETECT);
++
++ ch7006_load_reg(client, state, CH7006_CLKMODE);
++ ch7006_load_reg(client, state, CH7006_POWER);
++ ch7006_load_reg(client, state, CH7006_DETECT);
++
++ if ((det & (CH7006_DETECT_SVIDEO_Y_TEST|
++ CH7006_DETECT_SVIDEO_C_TEST|
++ CH7006_DETECT_CVBS_TEST)) == 0)
++ priv->subconnector = DRM_MODE_SUBCONNECTOR_SCART;
++ else if ((det & (CH7006_DETECT_SVIDEO_Y_TEST|
++ CH7006_DETECT_SVIDEO_C_TEST)) == 0)
++ priv->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO;
++ else if ((det & CH7006_DETECT_CVBS_TEST) == 0)
++ priv->subconnector = DRM_MODE_SUBCONNECTOR_Composite;
++ else
++ priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
++
++ drm_connector_property_set_value(connector,
++ encoder->dev->mode_config.tv_subconnector_property,
++ priv->subconnector);
++
++ return priv->subconnector ? connector_status_connected :
++ connector_status_disconnected;
++}
++
++static int ch7006_encoder_get_modes(struct drm_encoder *encoder,
++ struct drm_connector *connector)
++{
++ struct ch7006_priv *priv = to_ch7006_priv(encoder);
++ struct ch7006_mode *mode;
++ int n = 0;
++
++ for (mode = ch7006_modes; mode->mode.clock; mode++) {
++ if (~mode->valid_scales & 1<<priv->scale ||
++ ~mode->valid_norms & 1<<priv->norm)
++ continue;
++
++ drm_mode_probed_add(connector,
++ drm_mode_duplicate(encoder->dev, &mode->mode));
++
++ n++;
++ }
++
++ return n;
++}
++
++static int ch7006_encoder_create_resources(struct drm_encoder *encoder,
++ struct drm_connector *connector)
++{
++ struct ch7006_priv *priv = to_ch7006_priv(encoder);
++ struct drm_device *dev = encoder->dev;
++ struct drm_mode_config *conf = &dev->mode_config;
++
++ drm_mode_create_tv_properties(dev, NUM_TV_NORMS, ch7006_tv_norm_names);
++
++ priv->scale_property = drm_property_create(dev, DRM_MODE_PROP_RANGE,
++ "scale", 2);
++ priv->scale_property->values[0] = 0;
++ priv->scale_property->values[1] = 2;
++
++ drm_connector_attach_property(connector, conf->tv_select_subconnector_property,
++ priv->select_subconnector);
++ drm_connector_attach_property(connector, conf->tv_subconnector_property,
++ priv->subconnector);
++ drm_connector_attach_property(connector, conf->tv_left_margin_property,
++ priv->hmargin);
++ drm_connector_attach_property(connector, conf->tv_bottom_margin_property,
++ priv->vmargin);
++ drm_connector_attach_property(connector, conf->tv_mode_property,
++ priv->norm);
++ drm_connector_attach_property(connector, conf->tv_brightness_property,
++ priv->brightness);
++ drm_connector_attach_property(connector, conf->tv_contrast_property,
++ priv->contrast);
++ drm_connector_attach_property(connector, conf->tv_flicker_reduction_property,
++ priv->flicker);
++ drm_connector_attach_property(connector, priv->scale_property,
++ priv->scale);
++
++ return 0;
++}
++
++static int ch7006_encoder_set_property(struct drm_encoder *encoder,
++ struct drm_connector *connector,
++ struct drm_property *property,
++ uint64_t val)
++{
++ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
++ struct ch7006_priv *priv = to_ch7006_priv(encoder);
++ struct ch7006_state *state = &priv->state;
++ struct drm_mode_config *conf = &encoder->dev->mode_config;
++ struct drm_crtc *crtc = encoder->crtc;
++ bool modes_changed = false;
++
++ ch7006_dbg(client, "\n");
++
++ if (property == conf->tv_select_subconnector_property) {
++ priv->select_subconnector = val;
++
++ ch7006_setup_power_state(encoder);
++
++ ch7006_load_reg(client, state, CH7006_POWER);
++
++ } else if (property == conf->tv_left_margin_property) {
++ priv->hmargin = val;
++
++ ch7006_setup_properties(encoder);
++
++ ch7006_load_reg(client, state, CH7006_POV);
++ ch7006_load_reg(client, state, CH7006_HPOS);
++
++ } else if (property == conf->tv_bottom_margin_property) {
++ priv->vmargin = val;
++
++ ch7006_setup_properties(encoder);
++
++ ch7006_load_reg(client, state, CH7006_POV);
++ ch7006_load_reg(client, state, CH7006_VPOS);
++
++ } else if (property == conf->tv_mode_property) {
++ if (connector->dpms != DRM_MODE_DPMS_OFF)
++ return -EINVAL;
++
++ priv->norm = val;
++
++ modes_changed = true;
++
++ } else if (property == conf->tv_brightness_property) {
++ priv->brightness = val;
++
++ ch7006_setup_levels(encoder);
++
++ ch7006_load_reg(client, state, CH7006_BLACK_LEVEL);
++
++ } else if (property == conf->tv_contrast_property) {
++ priv->contrast = val;
++
++ ch7006_setup_properties(encoder);
++
++ ch7006_load_reg(client, state, CH7006_CONTRAST);
++
++ } else if (property == conf->tv_flicker_reduction_property) {
++ priv->flicker = val;
++
++ ch7006_setup_properties(encoder);
++
++ ch7006_load_reg(client, state, CH7006_FFILTER);
++
++ } else if (property == priv->scale_property) {
++ if (connector->dpms != DRM_MODE_DPMS_OFF)
++ return -EINVAL;
++
++ priv->scale = val;
++
++ modes_changed = true;
++
++ } else {
++ return -EINVAL;
++ }
++
++ if (modes_changed) {
++ drm_helper_probe_single_connector_modes(connector, 0, 0);
++
++ /* Disable the crtc to ensure a full modeset is
++ * performed whenever it's turned on again. */
++ if (crtc) {
++ struct drm_mode_set modeset = {
++ .crtc = crtc,
++ };
++
++ crtc->funcs->set_config(&modeset);
++ }
++ }
++
++ return 0;
++}
++
++static struct drm_encoder_slave_funcs ch7006_encoder_funcs = {
++ .set_config = ch7006_encoder_set_config,
++ .destroy = ch7006_encoder_destroy,
++ .dpms = ch7006_encoder_dpms,
++ .save = ch7006_encoder_save,
++ .restore = ch7006_encoder_restore,
++ .mode_fixup = ch7006_encoder_mode_fixup,
++ .mode_valid = ch7006_encoder_mode_valid,
++ .mode_set = ch7006_encoder_mode_set,
++ .detect = ch7006_encoder_detect,
++ .get_modes = ch7006_encoder_get_modes,
++ .create_resources = ch7006_encoder_create_resources,
++ .set_property = ch7006_encoder_set_property,
++};
++
++
++/* I2C driver functions */
++
++static int ch7006_probe(struct i2c_client *client, const struct i2c_device_id *id)
++{
++ uint8_t addr = CH7006_VERSION_ID;
++ uint8_t val;
++ int ret;
++
++ ch7006_dbg(client, "\n");
++
++ ret = i2c_master_send(client, &addr, sizeof(addr));
++ if (ret < 0)
++ goto fail;
++
++ ret = i2c_master_recv(client, &val, sizeof(val));
++ if (ret < 0)
++ goto fail;
++
++ ch7006_info(client, "Detected version ID: %x\n", val);
++
++ /* I don't know what this is for, but otherwise I get no
++ * signal.
++ */
++ ch7006_write(client, 0x3d, 0x0);
++
++ return 0;
++
++fail:
++ ch7006_err(client, "Error %d reading version ID\n", ret);
++
++ return -ENODEV;
++}
++
++static int ch7006_remove(struct i2c_client *client)
++{
++ ch7006_dbg(client, "\n");
++
++ return 0;
++}
++
++static int ch7006_encoder_init(struct i2c_client *client,
++ struct drm_device *dev,
++ struct drm_encoder_slave *encoder)
++{
++ struct ch7006_priv *priv;
++ int i;
++
++ ch7006_dbg(client, "\n");
++
++ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++
++ encoder->slave_priv = priv;
++ encoder->slave_funcs = &ch7006_encoder_funcs;
++
++ priv->norm = TV_NORM_PAL;
++ priv->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic;
++ priv->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
++ priv->scale = 1;
++ priv->contrast = 50;
++ priv->brightness = 50;
++ priv->flicker = 50;
++ priv->hmargin = 50;
++ priv->vmargin = 50;
++ priv->last_dpms = -1;
++
++ if (ch7006_tv_norm) {
++ for (i = 0; i < NUM_TV_NORMS; i++) {
++ if (!strcmp(ch7006_tv_norm_names[i], ch7006_tv_norm)) {
++ priv->norm = i;
++ break;
++ }
++ }
++
++ if (i == NUM_TV_NORMS)
++ ch7006_err(client, "Invalid TV norm setting \"%s\".\n",
++ ch7006_tv_norm);
++ }
++
++ if (ch7006_scale >= 0 && ch7006_scale <= 2)
++ priv->scale = ch7006_scale;
++ else
++ ch7006_err(client, "Invalid scale setting \"%d\".\n",
++ ch7006_scale);
++
++ return 0;
++}
++
++static struct i2c_device_id ch7006_ids[] = {
++ { "ch7006", 0 },
++ { }
++};
++MODULE_DEVICE_TABLE(i2c, ch7006_ids);
++
++static struct drm_i2c_encoder_driver ch7006_driver = {
++ .i2c_driver = {
++ .probe = ch7006_probe,
++ .remove = ch7006_remove,
++
++ .driver = {
++ .name = "ch7006",
++ },
++
++ .id_table = ch7006_ids,
++ },
++
++ .encoder_init = ch7006_encoder_init,
++};
++
++
++/* Module initialization */
++
++static int __init ch7006_init(void)
++{
++ return drm_i2c_encoder_register(THIS_MODULE, &ch7006_driver);
++}
++
++static void __exit ch7006_exit(void)
++{
++ drm_i2c_encoder_unregister(&ch7006_driver);
++}
++
++int ch7006_debug;
++module_param_named(debug, ch7006_debug, int, 0600);
++MODULE_PARM_DESC(debug, "Enable debug output.");
++
++char *ch7006_tv_norm;
++module_param_named(tv_norm, ch7006_tv_norm, charp, 0600);
++MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
++ "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, PAL-60, NTSC-M, NTSC-J.\n"
++ "\t\tDefault: PAL");
++
++int ch7006_scale = 1;
++module_param_named(scale, ch7006_scale, int, 0600);
++MODULE_PARM_DESC(scale, "Default scale.\n"
++ "\t\tSupported: 0 -> Select video modes with a higher blanking ratio.\n"
++ "\t\t\t1 -> Select default video modes.\n"
++ "\t\t\t2 -> Select video modes with a lower blanking ratio.");
++
++MODULE_AUTHOR("Francisco Jerez <currojerez@riseup.net>");
++MODULE_DESCRIPTION("Chrontel ch7006 TV encoder driver");
++MODULE_LICENSE("GPL and additional rights");
++
++module_init(ch7006_init);
++module_exit(ch7006_exit);
+diff --git a/drivers/gpu/drm/i2c/ch7006_mode.c b/drivers/gpu/drm/i2c/ch7006_mode.c
+new file mode 100644
+index 0000000..e447dfb
+--- /dev/null
++++ b/drivers/gpu/drm/i2c/ch7006_mode.c
+@@ -0,0 +1,468 @@
++/*
++ * Copyright (C) 2009 Francisco Jerez.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "ch7006_priv.h"
++
++char *ch7006_tv_norm_names[] = {
++ [TV_NORM_PAL] = "PAL",
++ [TV_NORM_PAL_M] = "PAL-M",
++ [TV_NORM_PAL_N] = "PAL-N",
++ [TV_NORM_PAL_NC] = "PAL-Nc",
++ [TV_NORM_PAL_60] = "PAL-60",
++ [TV_NORM_NTSC_M] = "NTSC-M",
++ [TV_NORM_NTSC_J] = "NTSC-J",
++};
++
++#define NTSC_LIKE_TIMINGS .vrefresh = 60 * fixed1/1.001, \
++ .vdisplay = 480, \
++ .vtotal = 525, \
++ .hvirtual = 660
++
++#define PAL_LIKE_TIMINGS .vrefresh = 50 * fixed1, \
++ .vdisplay = 576, \
++ .vtotal = 625, \
++ .hvirtual = 810
++
++struct ch7006_tv_norm_info ch7006_tv_norms[] = {
++ [TV_NORM_NTSC_M] = {
++ NTSC_LIKE_TIMINGS,
++ .black_level = 0.339 * fixed1,
++ .subc_freq = 3579545 * fixed1,
++ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC),
++ .voffset = 0,
++ },
++ [TV_NORM_NTSC_J] = {
++ NTSC_LIKE_TIMINGS,
++ .black_level = 0.286 * fixed1,
++ .subc_freq = 3579545 * fixed1,
++ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, NTSC_J),
++ .voffset = 0,
++ },
++ [TV_NORM_PAL] = {
++ PAL_LIKE_TIMINGS,
++ .black_level = 0.3 * fixed1,
++ .subc_freq = 4433618.75 * fixed1,
++ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
++ .voffset = 0,
++ },
++ [TV_NORM_PAL_M] = {
++ NTSC_LIKE_TIMINGS,
++ .black_level = 0.339 * fixed1,
++ .subc_freq = 3575611.433 * fixed1,
++ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M),
++ .voffset = 16,
++ },
++
++ /* The following modes seem to work right but they're
++ * undocumented */
++
++ [TV_NORM_PAL_N] = {
++ PAL_LIKE_TIMINGS,
++ .black_level = 0.339 * fixed1,
++ .subc_freq = 4433618.75 * fixed1,
++ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
++ .voffset = 0,
++ },
++ [TV_NORM_PAL_NC] = {
++ PAL_LIKE_TIMINGS,
++ .black_level = 0.3 * fixed1,
++ .subc_freq = 3582056.25 * fixed1,
++ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL),
++ .voffset = 0,
++ },
++ [TV_NORM_PAL_60] = {
++ NTSC_LIKE_TIMINGS,
++ .black_level = 0.3 * fixed1,
++ .subc_freq = 4433618.75 * fixed1,
++ .dispmode = bitfs(CH7006_DISPMODE_OUTPUT_STD, PAL_M),
++ .voffset = 16,
++ },
++};
++
++#define __MODE(f, hd, vd, ht, vt, hsynp, vsynp, \
++ subc, scale, scale_mask, norm_mask, e_hd, e_vd) { \
++ .mode = { \
++ .name = #hd "x" #vd, \
++ .status = 0, \
++ .type = DRM_MODE_TYPE_DRIVER, \
++ .clock = f, \
++ .hdisplay = hd, \
++ .hsync_start = e_hd + 16, \
++ .hsync_end = e_hd + 80, \
++ .htotal = ht, \
++ .hskew = 0, \
++ .vdisplay = vd, \
++ .vsync_start = vd + 10, \
++ .vsync_end = vd + 26, \
++ .vtotal = vt, \
++ .vscan = 0, \
++ .flags = DRM_MODE_FLAG_##hsynp##HSYNC | \
++ DRM_MODE_FLAG_##vsynp##VSYNC, \
++ .vrefresh = 0, \
++ }, \
++ .enc_hdisp = e_hd, \
++ .enc_vdisp = e_vd, \
++ .subc_coeff = subc * fixed1, \
++ .dispmode = bitfs(CH7006_DISPMODE_SCALING_RATIO, scale) | \
++ bitfs(CH7006_DISPMODE_INPUT_RES, e_hd##x##e_vd), \
++ .valid_scales = scale_mask, \
++ .valid_norms = norm_mask \
++ }
++
++#define MODE(f, hd, vd, ht, vt, hsynp, vsynp, \
++ subc, scale, scale_mask, norm_mask) \
++ __MODE(f, hd, vd, ht, vt, hsynp, vsynp, subc, scale, \
++ scale_mask, norm_mask, hd, vd)
++
++#define NTSC_LIKE (1 << TV_NORM_NTSC_M | 1 << TV_NORM_NTSC_J | \
++ 1 << TV_NORM_PAL_M | 1 << TV_NORM_PAL_60)
++
++#define PAL_LIKE (1 << TV_NORM_PAL | 1 << TV_NORM_PAL_N | 1 << TV_NORM_PAL_NC)
++
++struct ch7006_mode ch7006_modes[] = {
++ MODE(21000, 512, 384, 840, 500, N, N, 181.797557582, 5_4, 0x6, PAL_LIKE),
++ MODE(26250, 512, 384, 840, 625, N, N, 145.438046066, 1_1, 0x1, PAL_LIKE),
++ MODE(20140, 512, 384, 800, 420, N, N, 213.257083791, 5_4, 0x4, NTSC_LIKE),
++ MODE(24671, 512, 384, 784, 525, N, N, 174.0874153, 1_1, 0x3, NTSC_LIKE),
++ MODE(28125, 720, 400, 1125, 500, N, N, 135.742176298, 5_4, 0x6, PAL_LIKE),
++ MODE(34875, 720, 400, 1116, 625, N, N, 109.469496898, 1_1, 0x1, PAL_LIKE),
++ MODE(23790, 720, 400, 945, 420, N, N, 160.475642016, 5_4, 0x4, NTSC_LIKE),
++ MODE(29455, 720, 400, 936, 525, N, N, 129.614941843, 1_1, 0x3, NTSC_LIKE),
++ MODE(25000, 640, 400, 1000, 500, N, N, 152.709948279, 5_4, 0x6, PAL_LIKE),
++ MODE(31500, 640, 400, 1008, 625, N, N, 121.198371646, 1_1, 0x1, PAL_LIKE),
++ MODE(21147, 640, 400, 840, 420, N, N, 180.535097338, 5_4, 0x4, NTSC_LIKE),
++ MODE(26434, 640, 400, 840, 525, N, N, 144.42807787, 1_1, 0x2, NTSC_LIKE),
++ MODE(30210, 640, 400, 840, 600, N, N, 126.374568276, 7_8, 0x1, NTSC_LIKE),
++ MODE(21000, 640, 480, 840, 500, N, N, 181.797557582, 5_4, 0x4, PAL_LIKE),
++ MODE(26250, 640, 480, 840, 625, N, N, 145.438046066, 1_1, 0x2, PAL_LIKE),
++ MODE(31500, 640, 480, 840, 750, N, N, 121.198371646, 5_6, 0x1, PAL_LIKE),
++ MODE(24671, 640, 480, 784, 525, N, N, 174.0874153, 1_1, 0x4, NTSC_LIKE),
++ MODE(28196, 640, 480, 784, 600, N, N, 152.326488422, 7_8, 0x2, NTSC_LIKE),
++ MODE(30210, 640, 480, 800, 630, N, N, 142.171389101, 5_6, 0x1, NTSC_LIKE),
++ __MODE(29500, 720, 576, 944, 625, P, P, 145.592111636, 1_1, 0x7, PAL_LIKE, 800, 600),
++ MODE(36000, 800, 600, 960, 750, P, P, 119.304647022, 5_6, 0x6, PAL_LIKE),
++ MODE(39000, 800, 600, 936, 836, P, P, 110.127366499, 3_4, 0x1, PAL_LIKE),
++ MODE(39273, 800, 600, 1040, 630, P, P, 145.816809399, 5_6, 0x4, NTSC_LIKE),
++ MODE(43636, 800, 600, 1040, 700, P, P, 131.235128487, 3_4, 0x2, NTSC_LIKE),
++ MODE(47832, 800, 600, 1064, 750, P, P, 119.723275165, 7_10, 0x1, NTSC_LIKE),
++ {}
++};
++
++struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
++ struct drm_display_mode *drm_mode)
++{
++ struct ch7006_priv *priv = to_ch7006_priv(encoder);
++ struct ch7006_mode *mode;
++
++ for (mode = ch7006_modes; mode->mode.clock; mode++) {
++
++ if (~mode->valid_norms & 1<<priv->norm)
++ continue;
++
++ if (mode->mode.hdisplay != drm_mode->hdisplay ||
++ mode->mode.vdisplay != drm_mode->vdisplay ||
++ mode->mode.vtotal != drm_mode->vtotal ||
++ mode->mode.htotal != drm_mode->htotal ||
++ mode->mode.clock != drm_mode->clock)
++ continue;
++
++ return mode;
++ }
++
++ return NULL;
++}
++
++/* Some common HW state calculation code */
++
++void ch7006_setup_levels(struct drm_encoder *encoder)
++{
++ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
++ struct ch7006_priv *priv = to_ch7006_priv(encoder);
++ uint8_t *regs = priv->state.regs;
++ struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
++ int gain;
++ int black_level;
++
++ /* Set DAC_GAIN if the voltage drop between white and black is
++ * high enough. */
++ if (norm->black_level < 339*fixed1/1000) {
++ gain = 76;
++
++ regs[CH7006_INPUT_FORMAT] |= CH7006_INPUT_FORMAT_DAC_GAIN;
++ } else {
++ gain = 71;
++
++ regs[CH7006_INPUT_FORMAT] &= ~CH7006_INPUT_FORMAT_DAC_GAIN;
++ }
++
++ black_level = round_fixed(norm->black_level*26625)/gain;
++
++ /* Correct it with the specified brightness. */
++ black_level = interpolate(90, black_level, 208, priv->brightness);
++
++ regs[CH7006_BLACK_LEVEL] = bitf(CH7006_BLACK_LEVEL_0, black_level);
++
++ ch7006_dbg(client, "black level: %d\n", black_level);
++}
++
++void ch7006_setup_subcarrier(struct drm_encoder *encoder)
++{
++ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
++ struct ch7006_priv *priv = to_ch7006_priv(encoder);
++ struct ch7006_state *state = &priv->state;
++ struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
++ struct ch7006_mode *mode = priv->mode;
++ uint32_t subc_inc;
++
++ subc_inc = round_fixed((mode->subc_coeff >> 8)
++ * (norm->subc_freq >> 24));
++
++ setbitf(state, CH7006_SUBC_INC0, 28, subc_inc);
++ setbitf(state, CH7006_SUBC_INC1, 24, subc_inc);
++ setbitf(state, CH7006_SUBC_INC2, 20, subc_inc);
++ setbitf(state, CH7006_SUBC_INC3, 16, subc_inc);
++ setbitf(state, CH7006_SUBC_INC4, 12, subc_inc);
++ setbitf(state, CH7006_SUBC_INC5, 8, subc_inc);
++ setbitf(state, CH7006_SUBC_INC6, 4, subc_inc);
++ setbitf(state, CH7006_SUBC_INC7, 0, subc_inc);
++
++ ch7006_dbg(client, "subcarrier inc: %u\n", subc_inc);
++}
++
++void ch7006_setup_pll(struct drm_encoder *encoder)
++{
++ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
++ struct ch7006_priv *priv = to_ch7006_priv(encoder);
++ uint8_t *regs = priv->state.regs;
++ struct ch7006_mode *mode = priv->mode;
++ int n, best_n = 0;
++ int m, best_m = 0;
++ int freq, best_freq = 0;
++
++ for (n = 0; n < CH7006_MAXN; n++) {
++ for (m = 0; m < CH7006_MAXM; m++) {
++ freq = CH7006_FREQ0*(n+2)/(m+2);
++
++ if (abs(freq - mode->mode.clock) <
++ abs(best_freq - mode->mode.clock)) {
++ best_freq = freq;
++ best_n = n;
++ best_m = m;
++ }
++ }
++ }
++
++ regs[CH7006_PLLOV] = bitf(CH7006_PLLOV_N_8, best_n) |
++ bitf(CH7006_PLLOV_M_8, best_m);
++
++ regs[CH7006_PLLM] = bitf(CH7006_PLLM_0, best_m);
++ regs[CH7006_PLLN] = bitf(CH7006_PLLN_0, best_n);
++
++ if (best_n < 108)
++ regs[CH7006_PLL_CONTROL] |= CH7006_PLL_CONTROL_CAPACITOR;
++ else
++ regs[CH7006_PLL_CONTROL] &= ~CH7006_PLL_CONTROL_CAPACITOR;
++
++ ch7006_dbg(client, "n=%d m=%d f=%d c=%d\n",
++ best_n, best_m, best_freq, best_n < 108);
++}
++
++void ch7006_setup_power_state(struct drm_encoder *encoder)
++{
++ struct ch7006_priv *priv = to_ch7006_priv(encoder);
++ uint8_t *power = &priv->state.regs[CH7006_POWER];
++ int subconnector;
++
++ subconnector = priv->select_subconnector ? priv->select_subconnector :
++ priv->subconnector;
++
++ *power = CH7006_POWER_RESET;
++
++ if (priv->last_dpms == DRM_MODE_DPMS_ON) {
++ switch (subconnector) {
++ case DRM_MODE_SUBCONNECTOR_SVIDEO:
++ *power |= bitfs(CH7006_POWER_LEVEL, CVBS_OFF);
++ break;
++ case DRM_MODE_SUBCONNECTOR_Composite:
++ *power |= bitfs(CH7006_POWER_LEVEL, SVIDEO_OFF);
++ break;
++ case DRM_MODE_SUBCONNECTOR_SCART:
++ *power |= bitfs(CH7006_POWER_LEVEL, NORMAL) |
++ CH7006_POWER_SCART;
++ break;
++ }
++
++ } else {
++ *power |= bitfs(CH7006_POWER_LEVEL, FULL_POWER_OFF);
++ }
++}
++
++void ch7006_setup_properties(struct drm_encoder *encoder)
++{
++ struct i2c_client *client = drm_i2c_encoder_get_client(encoder);
++ struct ch7006_priv *priv = to_ch7006_priv(encoder);
++ struct ch7006_state *state = &priv->state;
++ struct ch7006_tv_norm_info *norm = &ch7006_tv_norms[priv->norm];
++ struct ch7006_mode *ch_mode = priv->mode;
++ struct drm_display_mode *mode = &ch_mode->mode;
++ uint8_t *regs = state->regs;
++ int flicker, contrast, hpos, vpos;
++ uint64_t scale, aspect;
++
++ flicker = interpolate(0, 2, 3, priv->flicker);
++ regs[CH7006_FFILTER] = bitf(CH7006_FFILTER_TEXT, flicker) |
++ bitf(CH7006_FFILTER_LUMA, flicker) |
++ bitf(CH7006_FFILTER_CHROMA, 1);
++
++ contrast = interpolate(0, 5, 7, priv->contrast);
++ regs[CH7006_CONTRAST] = bitf(CH7006_CONTRAST_0, contrast);
++
++ scale = norm->vtotal*fixed1;
++ do_div(scale, mode->vtotal);
++
++ aspect = ch_mode->enc_hdisp*fixed1;
++ do_div(aspect, ch_mode->enc_vdisp);
++
++ hpos = round_fixed((norm->hvirtual * aspect - mode->hdisplay * scale)
++ * priv->hmargin * mode->vtotal) / norm->vtotal / 100 / 4;
++
++ setbitf(state, CH7006_POV, HPOS_8, hpos);
++ setbitf(state, CH7006_HPOS, 0, hpos);
++
++ vpos = max(0, norm->vdisplay - round_fixed(mode->vdisplay*scale)
++ + norm->voffset) * priv->vmargin / 100 / 2;
++
++ setbitf(state, CH7006_POV, VPOS_8, vpos);
++ setbitf(state, CH7006_VPOS, 0, vpos);
++
++ ch7006_dbg(client, "hpos: %d, vpos: %d\n", hpos, vpos);
++}
++
++/* HW access functions */
++
++void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val)
++{
++ uint8_t buf[] = {addr, val};
++ int ret;
++
++ ret = i2c_master_send(client, buf, ARRAY_SIZE(buf));
++ if (ret < 0)
++ ch7006_err(client, "Error %d writing to subaddress 0x%x\n",
++ ret, addr);
++}
++
++uint8_t ch7006_read(struct i2c_client *client, uint8_t addr)
++{
++ uint8_t val;
++ int ret;
++
++ ret = i2c_master_send(client, &addr, sizeof(addr));
++ if (ret < 0)
++ goto fail;
++
++ ret = i2c_master_recv(client, &val, sizeof(val));
++ if (ret < 0)
++ goto fail;
++
++ return val;
++
++fail:
++ ch7006_err(client, "Error %d reading from subaddress 0x%x\n",
++ ret, addr);
++ return 0;
++}
++
++void ch7006_state_load(struct i2c_client *client,
++ struct ch7006_state *state)
++{
++ ch7006_load_reg(client, state, CH7006_POWER);
++
++ ch7006_load_reg(client, state, CH7006_DISPMODE);
++ ch7006_load_reg(client, state, CH7006_FFILTER);
++ ch7006_load_reg(client, state, CH7006_BWIDTH);
++ ch7006_load_reg(client, state, CH7006_INPUT_FORMAT);
++ ch7006_load_reg(client, state, CH7006_CLKMODE);
++ ch7006_load_reg(client, state, CH7006_START_ACTIVE);
++ ch7006_load_reg(client, state, CH7006_POV);
++ ch7006_load_reg(client, state, CH7006_BLACK_LEVEL);
++ ch7006_load_reg(client, state, CH7006_HPOS);
++ ch7006_load_reg(client, state, CH7006_VPOS);
++ ch7006_load_reg(client, state, CH7006_INPUT_SYNC);
++ ch7006_load_reg(client, state, CH7006_DETECT);
++ ch7006_load_reg(client, state, CH7006_CONTRAST);
++ ch7006_load_reg(client, state, CH7006_PLLOV);
++ ch7006_load_reg(client, state, CH7006_PLLM);
++ ch7006_load_reg(client, state, CH7006_PLLN);
++ ch7006_load_reg(client, state, CH7006_BCLKOUT);
++ ch7006_load_reg(client, state, CH7006_SUBC_INC0);
++ ch7006_load_reg(client, state, CH7006_SUBC_INC1);
++ ch7006_load_reg(client, state, CH7006_SUBC_INC2);
++ ch7006_load_reg(client, state, CH7006_SUBC_INC3);
++ ch7006_load_reg(client, state, CH7006_SUBC_INC4);
++ ch7006_load_reg(client, state, CH7006_SUBC_INC5);
++ ch7006_load_reg(client, state, CH7006_SUBC_INC6);
++ ch7006_load_reg(client, state, CH7006_SUBC_INC7);
++ ch7006_load_reg(client, state, CH7006_PLL_CONTROL);
++ ch7006_load_reg(client, state, CH7006_CALC_SUBC_INC0);
++}
++
++void ch7006_state_save(struct i2c_client *client,
++ struct ch7006_state *state)
++{
++ ch7006_save_reg(client, state, CH7006_POWER);
++
++ ch7006_save_reg(client, state, CH7006_DISPMODE);
++ ch7006_save_reg(client, state, CH7006_FFILTER);
++ ch7006_save_reg(client, state, CH7006_BWIDTH);
++ ch7006_save_reg(client, state, CH7006_INPUT_FORMAT);
++ ch7006_save_reg(client, state, CH7006_CLKMODE);
++ ch7006_save_reg(client, state, CH7006_START_ACTIVE);
++ ch7006_save_reg(client, state, CH7006_POV);
++ ch7006_save_reg(client, state, CH7006_BLACK_LEVEL);
++ ch7006_save_reg(client, state, CH7006_HPOS);
++ ch7006_save_reg(client, state, CH7006_VPOS);
++ ch7006_save_reg(client, state, CH7006_INPUT_SYNC);
++ ch7006_save_reg(client, state, CH7006_DETECT);
++ ch7006_save_reg(client, state, CH7006_CONTRAST);
++ ch7006_save_reg(client, state, CH7006_PLLOV);
++ ch7006_save_reg(client, state, CH7006_PLLM);
++ ch7006_save_reg(client, state, CH7006_PLLN);
++ ch7006_save_reg(client, state, CH7006_BCLKOUT);
++ ch7006_save_reg(client, state, CH7006_SUBC_INC0);
++ ch7006_save_reg(client, state, CH7006_SUBC_INC1);
++ ch7006_save_reg(client, state, CH7006_SUBC_INC2);
++ ch7006_save_reg(client, state, CH7006_SUBC_INC3);
++ ch7006_save_reg(client, state, CH7006_SUBC_INC4);
++ ch7006_save_reg(client, state, CH7006_SUBC_INC5);
++ ch7006_save_reg(client, state, CH7006_SUBC_INC6);
++ ch7006_save_reg(client, state, CH7006_SUBC_INC7);
++ ch7006_save_reg(client, state, CH7006_PLL_CONTROL);
++ ch7006_save_reg(client, state, CH7006_CALC_SUBC_INC0);
++
++ state->regs[CH7006_FFILTER] = (state->regs[CH7006_FFILTER] & 0xf0) |
++ (state->regs[CH7006_FFILTER] & 0x0c) >> 2 |
++ (state->regs[CH7006_FFILTER] & 0x03) << 2;
++}
+diff --git a/drivers/gpu/drm/i2c/ch7006_priv.h b/drivers/gpu/drm/i2c/ch7006_priv.h
+new file mode 100644
+index 0000000..b06d3d9
+--- /dev/null
++++ b/drivers/gpu/drm/i2c/ch7006_priv.h
+@@ -0,0 +1,344 @@
++/*
++ * Copyright (C) 2009 Francisco Jerez.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#ifndef __DRM_I2C_CH7006_PRIV_H__
++#define __DRM_I2C_CH7006_PRIV_H__
++
++#include "drmP.h"
++#include "drm_crtc_helper.h"
++#include "drm_encoder_slave.h"
++#include "i2c/ch7006.h"
++
++typedef int64_t fixed;
++#define fixed1 (1LL << 32)
++
++enum ch7006_tv_norm {
++ TV_NORM_PAL,
++ TV_NORM_PAL_M,
++ TV_NORM_PAL_N,
++ TV_NORM_PAL_NC,
++ TV_NORM_PAL_60,
++ TV_NORM_NTSC_M,
++ TV_NORM_NTSC_J,
++ NUM_TV_NORMS
++};
++
++struct ch7006_tv_norm_info {
++ fixed vrefresh;
++ int vdisplay;
++ int vtotal;
++ int hvirtual;
++
++ fixed subc_freq;
++ fixed black_level;
++
++ uint32_t dispmode;
++ int voffset;
++};
++
++struct ch7006_mode {
++ struct drm_display_mode mode;
++
++ int enc_hdisp;
++ int enc_vdisp;
++
++ fixed subc_coeff;
++ uint32_t dispmode;
++
++ uint32_t valid_scales;
++ uint32_t valid_norms;
++};
++
++struct ch7006_state {
++ uint8_t regs[0x26];
++};
++
++struct ch7006_priv {
++ struct ch7006_encoder_params *params;
++ struct ch7006_mode *mode;
++
++ struct ch7006_state state;
++ struct ch7006_state saved_state;
++
++ struct drm_property *scale_property;
++
++ int select_subconnector;
++ int subconnector;
++ int hmargin;
++ int vmargin;
++ enum ch7006_tv_norm norm;
++ int brightness;
++ int contrast;
++ int flicker;
++ int scale;
++
++ int last_dpms;
++};
++
++#define to_ch7006_priv(x) \
++ ((struct ch7006_priv *)to_encoder_slave(x)->slave_priv)
++
++extern int ch7006_debug;
++extern char *ch7006_tv_norm;
++extern int ch7006_scale;
++
++extern char *ch7006_tv_norm_names[];
++extern struct ch7006_tv_norm_info ch7006_tv_norms[];
++extern struct ch7006_mode ch7006_modes[];
++
++struct ch7006_mode *ch7006_lookup_mode(struct drm_encoder *encoder,
++ struct drm_display_mode *drm_mode);
++
++void ch7006_setup_levels(struct drm_encoder *encoder);
++void ch7006_setup_subcarrier(struct drm_encoder *encoder);
++void ch7006_setup_pll(struct drm_encoder *encoder);
++void ch7006_setup_power_state(struct drm_encoder *encoder);
++void ch7006_setup_properties(struct drm_encoder *encoder);
++
++void ch7006_write(struct i2c_client *client, uint8_t addr, uint8_t val);
++uint8_t ch7006_read(struct i2c_client *client, uint8_t addr);
++
++void ch7006_state_load(struct i2c_client *client,
++ struct ch7006_state *state);
++void ch7006_state_save(struct i2c_client *client,
++ struct ch7006_state *state);
++
++/* Some helper macros */
++
++#define ch7006_dbg(client, format, ...) do { \
++ if (ch7006_debug) \
++ dev_printk(KERN_DEBUG, &client->dev, \
++ "%s: " format, __func__, ## __VA_ARGS__); \
++ } while (0)
++#define ch7006_info(client, format, ...) \
++ dev_info(&client->dev, format, __VA_ARGS__)
++#define ch7006_err(client, format, ...) \
++ dev_err(&client->dev, format, __VA_ARGS__)
++
++#define __mask(src, bitfield) \
++ (((2 << (1 ? bitfield)) - 1) & ~((1 << (0 ? bitfield)) - 1))
++#define mask(bitfield) __mask(bitfield)
++
++#define __bitf(src, bitfield, x) \
++ (((x) >> (src) << (0 ? bitfield)) & __mask(src, bitfield))
++#define bitf(bitfield, x) __bitf(bitfield, x)
++#define bitfs(bitfield, s) __bitf(bitfield, bitfield##_##s)
++#define setbitf(state, reg, bitfield, x) \
++ state->regs[reg] = (state->regs[reg] & ~mask(reg##_##bitfield)) \
++ | bitf(reg##_##bitfield, x)
++
++#define __unbitf(src, bitfield, x) \
++ ((x & __mask(src, bitfield)) >> (0 ? bitfield) << (src))
++#define unbitf(bitfield, x) __unbitf(bitfield, x)
++
++static inline int interpolate(int y0, int y1, int y2, int x)
++{
++ return y1 + (x < 50 ? y1 - y0 : y2 - y1) * (x - 50) / 50;
++}
++
++static inline int32_t round_fixed(fixed x)
++{
++ return (x + fixed1/2) >> 32;
++}
++
++#define ch7006_load_reg(client, state, reg) ch7006_write(client, reg, state->regs[reg])
++#define ch7006_save_reg(client, state, reg) state->regs[reg] = ch7006_read(client, reg)
++
++/* Fixed hardware specs */
++
++#define CH7006_FREQ0 14318
++#define CH7006_MAXN 650
++#define CH7006_MAXM 315
++
++/* Register definitions */
++
++#define CH7006_DISPMODE 0x00
++#define CH7006_DISPMODE_INPUT_RES 0, 7:5
++#define CH7006_DISPMODE_INPUT_RES_512x384 0x0
++#define CH7006_DISPMODE_INPUT_RES_720x400 0x1
++#define CH7006_DISPMODE_INPUT_RES_640x400 0x2
++#define CH7006_DISPMODE_INPUT_RES_640x480 0x3
++#define CH7006_DISPMODE_INPUT_RES_800x600 0x4
++#define CH7006_DISPMODE_INPUT_RES_NATIVE 0x5
++#define CH7006_DISPMODE_OUTPUT_STD 0, 4:3
++#define CH7006_DISPMODE_OUTPUT_STD_PAL 0x0
++#define CH7006_DISPMODE_OUTPUT_STD_NTSC 0x1
++#define CH7006_DISPMODE_OUTPUT_STD_PAL_M 0x2
++#define CH7006_DISPMODE_OUTPUT_STD_NTSC_J 0x3
++#define CH7006_DISPMODE_SCALING_RATIO 0, 2:0
++#define CH7006_DISPMODE_SCALING_RATIO_5_4 0x0
++#define CH7006_DISPMODE_SCALING_RATIO_1_1 0x1
++#define CH7006_DISPMODE_SCALING_RATIO_7_8 0x2
++#define CH7006_DISPMODE_SCALING_RATIO_5_6 0x3
++#define CH7006_DISPMODE_SCALING_RATIO_3_4 0x4
++#define CH7006_DISPMODE_SCALING_RATIO_7_10 0x5
++
++#define CH7006_FFILTER 0x01
++#define CH7006_FFILTER_TEXT 0, 5:4
++#define CH7006_FFILTER_LUMA 0, 3:2
++#define CH7006_FFILTER_CHROMA 0, 1:0
++#define CH7006_FFILTER_CHROMA_NO_DCRAWL 0x3
++
++#define CH7006_BWIDTH 0x03
++#define CH7006_BWIDTH_5L_FFILER (1 << 7)
++#define CH7006_BWIDTH_CVBS_NO_CHROMA (1 << 6)
++#define CH7006_BWIDTH_CHROMA 0, 5:4
++#define CH7006_BWIDTH_SVIDEO_YPEAK (1 << 3)
++#define CH7006_BWIDTH_SVIDEO_LUMA 0, 2:1
++#define CH7006_BWIDTH_CVBS_LUMA 0, 0:0
++
++#define CH7006_INPUT_FORMAT 0x04
++#define CH7006_INPUT_FORMAT_DAC_GAIN (1 << 6)
++#define CH7006_INPUT_FORMAT_RGB_PASS_THROUGH (1 << 5)
++#define CH7006_INPUT_FORMAT_FORMAT 0, 3:0
++#define CH7006_INPUT_FORMAT_FORMAT_RGB16 0x0
++#define CH7006_INPUT_FORMAT_FORMAT_YCrCb24m16 0x1
++#define CH7006_INPUT_FORMAT_FORMAT_RGB24m16 0x2
++#define CH7006_INPUT_FORMAT_FORMAT_RGB15 0x3
++#define CH7006_INPUT_FORMAT_FORMAT_RGB24m12C 0x4
++#define CH7006_INPUT_FORMAT_FORMAT_RGB24m12I 0x5
++#define CH7006_INPUT_FORMAT_FORMAT_RGB24m8 0x6
++#define CH7006_INPUT_FORMAT_FORMAT_RGB16m8 0x7
++#define CH7006_INPUT_FORMAT_FORMAT_RGB15m8 0x8
++#define CH7006_INPUT_FORMAT_FORMAT_YCrCb24m8 0x9
++
++#define CH7006_CLKMODE 0x06
++#define CH7006_CLKMODE_SUBC_LOCK (1 << 7)
++#define CH7006_CLKMODE_MASTER (1 << 6)
++#define CH7006_CLKMODE_POS_EDGE (1 << 4)
++#define CH7006_CLKMODE_XCM 0, 3:2
++#define CH7006_CLKMODE_PCM 0, 1:0
++
++#define CH7006_START_ACTIVE 0x07
++#define CH7006_START_ACTIVE_0 0, 7:0
++
++#define CH7006_POV 0x08
++#define CH7006_POV_START_ACTIVE_8 8, 2:2
++#define CH7006_POV_HPOS_8 8, 1:1
++#define CH7006_POV_VPOS_8 8, 0:0
++
++#define CH7006_BLACK_LEVEL 0x09
++#define CH7006_BLACK_LEVEL_0 0, 7:0
++
++#define CH7006_HPOS 0x0a
++#define CH7006_HPOS_0 0, 7:0
++
++#define CH7006_VPOS 0x0b
++#define CH7006_VPOS_0 0, 7:0
++
++#define CH7006_INPUT_SYNC 0x0d
++#define CH7006_INPUT_SYNC_EMBEDDED (1 << 3)
++#define CH7006_INPUT_SYNC_OUTPUT (1 << 2)
++#define CH7006_INPUT_SYNC_PVSYNC (1 << 1)
++#define CH7006_INPUT_SYNC_PHSYNC (1 << 0)
++
++#define CH7006_POWER 0x0e
++#define CH7006_POWER_SCART (1 << 4)
++#define CH7006_POWER_RESET (1 << 3)
++#define CH7006_POWER_LEVEL 0, 2:0
++#define CH7006_POWER_LEVEL_CVBS_OFF 0x0
++#define CH7006_POWER_LEVEL_POWER_OFF 0x1
++#define CH7006_POWER_LEVEL_SVIDEO_OFF 0x2
++#define CH7006_POWER_LEVEL_NORMAL 0x3
++#define CH7006_POWER_LEVEL_FULL_POWER_OFF 0x4
++
++#define CH7006_DETECT 0x10
++#define CH7006_DETECT_SVIDEO_Y_TEST (1 << 3)
++#define CH7006_DETECT_SVIDEO_C_TEST (1 << 2)
++#define CH7006_DETECT_CVBS_TEST (1 << 1)
++#define CH7006_DETECT_SENSE (1 << 0)
++
++#define CH7006_CONTRAST 0x11
++#define CH7006_CONTRAST_0 0, 2:0
++
++#define CH7006_PLLOV 0x13
++#define CH7006_PLLOV_N_8 8, 2:1
++#define CH7006_PLLOV_M_8 8, 0:0
++
++#define CH7006_PLLM 0x14
++#define CH7006_PLLM_0 0, 7:0
++
++#define CH7006_PLLN 0x15
++#define CH7006_PLLN_0 0, 7:0
++
++#define CH7006_BCLKOUT 0x17
++
++#define CH7006_SUBC_INC0 0x18
++#define CH7006_SUBC_INC0_28 28, 3:0
++
++#define CH7006_SUBC_INC1 0x19
++#define CH7006_SUBC_INC1_24 24, 3:0
++
++#define CH7006_SUBC_INC2 0x1a
++#define CH7006_SUBC_INC2_20 20, 3:0
++
++#define CH7006_SUBC_INC3 0x1b
++#define CH7006_SUBC_INC3_GPIO1_VAL (1 << 7)
++#define CH7006_SUBC_INC3_GPIO0_VAL (1 << 6)
++#define CH7006_SUBC_INC3_POUT_3_3V (1 << 5)
++#define CH7006_SUBC_INC3_POUT_INV (1 << 4)
++#define CH7006_SUBC_INC3_16 16, 3:0
++
++#define CH7006_SUBC_INC4 0x1c
++#define CH7006_SUBC_INC4_GPIO1_IN (1 << 7)
++#define CH7006_SUBC_INC4_GPIO0_IN (1 << 6)
++#define CH7006_SUBC_INC4_DS_INPUT (1 << 4)
++#define CH7006_SUBC_INC4_12 12, 3:0
++
++#define CH7006_SUBC_INC5 0x1d
++#define CH7006_SUBC_INC5_8 8, 3:0
++
++#define CH7006_SUBC_INC6 0x1e
++#define CH7006_SUBC_INC6_4 4, 3:0
++
++#define CH7006_SUBC_INC7 0x1f
++#define CH7006_SUBC_INC7_0 0, 3:0
++
++#define CH7006_PLL_CONTROL 0x20
++#define CH7006_PLL_CONTROL_CPI (1 << 5)
++#define CH7006_PLL_CONTROL_CAPACITOR (1 << 4)
++#define CH7006_PLL_CONTROL_7STAGES (1 << 3)
++#define CH7006_PLL_CONTROL_DIGITAL_5V (1 << 2)
++#define CH7006_PLL_CONTROL_ANALOG_5V (1 << 1)
++#define CH7006_PLL_CONTROL_MEMORY_5V (1 << 0)
++
++#define CH7006_CALC_SUBC_INC0 0x21
++#define CH7006_CALC_SUBC_INC0_24 24, 4:3
++#define CH7006_CALC_SUBC_INC0_HYST 0, 2:1
++#define CH7006_CALC_SUBC_INC0_AUTO (1 << 0)
++
++#define CH7006_CALC_SUBC_INC1 0x22
++#define CH7006_CALC_SUBC_INC1_16 16, 7:0
++
++#define CH7006_CALC_SUBC_INC2 0x23
++#define CH7006_CALC_SUBC_INC2_8 8, 7:0
++
++#define CH7006_CALC_SUBC_INC3 0x24
++#define CH7006_CALC_SUBC_INC3_0 0, 7:0
++
++#define CH7006_VERSION_ID 0x25
++
++#endif
+diff --git a/drivers/gpu/drm/i810/i810_dma.c b/drivers/gpu/drm/i810/i810_dma.c
+index 7d1d88c..de32d22 100644
+--- a/drivers/gpu/drm/i810/i810_dma.c
++++ b/drivers/gpu/drm/i810/i810_dma.c
+@@ -115,7 +115,7 @@ static int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma)
+ static const struct file_operations i810_buffer_fops = {
+ .open = drm_open,
+ .release = drm_release,
+- .ioctl = drm_ioctl,
++ .unlocked_ioctl = drm_ioctl,
+ .mmap = i810_mmap_buffers,
+ .fasync = drm_fasync,
+ };
+diff --git a/drivers/gpu/drm/i810/i810_drv.c b/drivers/gpu/drm/i810/i810_drv.c
+index fabb9a8..c1e0275 100644
+--- a/drivers/gpu/drm/i810/i810_drv.c
++++ b/drivers/gpu/drm/i810/i810_drv.c
+@@ -59,7 +59,7 @@ static struct drm_driver driver = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+- .ioctl = drm_ioctl,
++ .unlocked_ioctl = drm_ioctl,
+ .mmap = drm_mmap,
+ .poll = drm_poll,
+ .fasync = drm_fasync,
+diff --git a/drivers/gpu/drm/i830/i830_dma.c b/drivers/gpu/drm/i830/i830_dma.c
+index 877bf6c..06bd732 100644
+--- a/drivers/gpu/drm/i830/i830_dma.c
++++ b/drivers/gpu/drm/i830/i830_dma.c
+@@ -117,7 +117,7 @@ static int i830_mmap_buffers(struct file *filp, struct vm_area_struct *vma)
+ static const struct file_operations i830_buffer_fops = {
+ .open = drm_open,
+ .release = drm_release,
+- .ioctl = drm_ioctl,
++ .unlocked_ioctl = drm_ioctl,
+ .mmap = i830_mmap_buffers,
+ .fasync = drm_fasync,
+ };
+diff --git a/drivers/gpu/drm/i830/i830_drv.c b/drivers/gpu/drm/i830/i830_drv.c
+index 389597e..44f990b 100644
+--- a/drivers/gpu/drm/i830/i830_drv.c
++++ b/drivers/gpu/drm/i830/i830_drv.c
+@@ -70,7 +70,7 @@ static struct drm_driver driver = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+- .ioctl = drm_ioctl,
++ .unlocked_ioctl = drm_ioctl,
+ .mmap = drm_mmap,
+ .poll = drm_poll,
+ .fasync = drm_fasync,
+diff --git a/drivers/gpu/drm/i915/Makefile b/drivers/gpu/drm/i915/Makefile
+index fa7b9be..9929f84 100644
+--- a/drivers/gpu/drm/i915/Makefile
++++ b/drivers/gpu/drm/i915/Makefile
+@@ -15,7 +15,6 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
+ intel_lvds.o \
+ intel_bios.o \
+ intel_dp.o \
+- intel_dp_i2c.o \
+ intel_hdmi.o \
+ intel_sdvo.o \
+ intel_modes.o \
+@@ -23,6 +22,7 @@ i915-y := i915_drv.o i915_dma.o i915_irq.o i915_mem.o \
+ intel_fb.o \
+ intel_tv.o \
+ intel_dvo.o \
++ intel_overlay.o \
+ dvo_ch7xxx.o \
+ dvo_ch7017.o \
+ dvo_ivch.o \
+diff --git a/drivers/gpu/drm/i915/dvo_ch7017.c b/drivers/gpu/drm/i915/dvo_ch7017.c
+index 621815b..1184c14 100644
+--- a/drivers/gpu/drm/i915/dvo_ch7017.c
++++ b/drivers/gpu/drm/i915/dvo_ch7017.c
+@@ -249,7 +249,8 @@ static bool ch7017_init(struct intel_dvo_device *dvo,
+ if (val != CH7017_DEVICE_ID_VALUE &&
+ val != CH7018_DEVICE_ID_VALUE &&
+ val != CH7019_DEVICE_ID_VALUE) {
+- DRM_DEBUG("ch701x not detected, got %d: from %s Slave %d.\n",
++ DRM_DEBUG_KMS("ch701x not detected, got %d: from %s "
++ "Slave %d.\n",
+ val, i2cbus->adapter.name,dvo->slave_addr);
+ goto fail;
+ }
+@@ -284,7 +285,7 @@ static void ch7017_mode_set(struct intel_dvo_device *dvo,
+ uint8_t horizontal_active_pixel_output, vertical_active_line_output;
+ uint8_t active_input_line_output;
+
+- DRM_DEBUG("Registers before mode setting\n");
++ DRM_DEBUG_KMS("Registers before mode setting\n");
+ ch7017_dump_regs(dvo);
+
+ /* LVDS PLL settings from page 75 of 7017-7017ds.pdf*/
+@@ -346,7 +347,7 @@ static void ch7017_mode_set(struct intel_dvo_device *dvo,
+ /* Turn the LVDS back on with new settings. */
+ ch7017_write(dvo, CH7017_LVDS_POWER_DOWN, lvds_power_down);
+
+- DRM_DEBUG("Registers after mode setting\n");
++ DRM_DEBUG_KMS("Registers after mode setting\n");
+ ch7017_dump_regs(dvo);
+ }
+
+@@ -386,7 +387,7 @@ static void ch7017_dump_regs(struct intel_dvo_device *dvo)
+ #define DUMP(reg) \
+ do { \
+ ch7017_read(dvo, reg, &val); \
+- DRM_DEBUG(#reg ": %02x\n", val); \
++ DRM_DEBUG_KMS(#reg ": %02x\n", val); \
+ } while (0)
+
+ DUMP(CH7017_HORIZONTAL_ACTIVE_PIXEL_INPUT);
+diff --git a/drivers/gpu/drm/i915/dvo_ch7xxx.c b/drivers/gpu/drm/i915/dvo_ch7xxx.c
+index a9b8962..d56ff5c 100644
+--- a/drivers/gpu/drm/i915/dvo_ch7xxx.c
++++ b/drivers/gpu/drm/i915/dvo_ch7xxx.c
+@@ -152,7 +152,7 @@ static bool ch7xxx_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
+ };
+
+ if (!ch7xxx->quiet) {
+- DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
++ DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
+ addr, i2cbus->adapter.name, dvo->slave_addr);
+ }
+ return false;
+@@ -179,7 +179,7 @@ static bool ch7xxx_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
+ return true;
+
+ if (!ch7xxx->quiet) {
+- DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
++ DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
+ addr, i2cbus->adapter.name, dvo->slave_addr);
+ }
+
+@@ -207,7 +207,8 @@ static bool ch7xxx_init(struct intel_dvo_device *dvo,
+
+ name = ch7xxx_get_id(vendor);
+ if (!name) {
+- DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n",
++ DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
++ "slave %d.\n",
+ vendor, adapter->name, dvo->slave_addr);
+ goto out;
+ }
+@@ -217,13 +218,14 @@ static bool ch7xxx_init(struct intel_dvo_device *dvo,
+ goto out;
+
+ if (device != CH7xxx_DID) {
+- DRM_DEBUG("ch7xxx not detected; got 0x%02x from %s slave %d.\n",
++ DRM_DEBUG_KMS("ch7xxx not detected; got 0x%02x from %s "
++ "slave %d.\n",
+ vendor, adapter->name, dvo->slave_addr);
+ goto out;
+ }
+
+ ch7xxx->quiet = false;
+- DRM_DEBUG("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
++ DRM_DEBUG_KMS("Detected %s chipset, vendor/device ID 0x%02x/0x%02x\n",
+ name, vendor, device);
+ return true;
+ out:
+@@ -315,8 +317,8 @@ static void ch7xxx_dump_regs(struct intel_dvo_device *dvo)
+
+ for (i = 0; i < CH7xxx_NUM_REGS; i++) {
+ if ((i % 8) == 0 )
+- DRM_DEBUG("\n %02X: ", i);
+- DRM_DEBUG("%02X ", ch7xxx->mode_reg.regs[i]);
++ DRM_LOG_KMS("\n %02X: ", i);
++ DRM_LOG_KMS("%02X ", ch7xxx->mode_reg.regs[i]);
+ }
+ }
+
+diff --git a/drivers/gpu/drm/i915/dvo_ivch.c b/drivers/gpu/drm/i915/dvo_ivch.c
+index aa176f9..24169e5 100644
+--- a/drivers/gpu/drm/i915/dvo_ivch.c
++++ b/drivers/gpu/drm/i915/dvo_ivch.c
+@@ -202,7 +202,8 @@ static bool ivch_read(struct intel_dvo_device *dvo, int addr, uint16_t *data)
+ };
+
+ if (!priv->quiet) {
+- DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
++ DRM_DEBUG_KMS("Unable to read register 0x%02x from "
++ "%s:%02x.\n",
+ addr, i2cbus->adapter.name, dvo->slave_addr);
+ }
+ return false;
+@@ -230,7 +231,7 @@ static bool ivch_write(struct intel_dvo_device *dvo, int addr, uint16_t data)
+ return true;
+
+ if (!priv->quiet) {
+- DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
++ DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
+ addr, i2cbus->adapter.name, dvo->slave_addr);
+ }
+
+@@ -261,7 +262,7 @@ static bool ivch_init(struct intel_dvo_device *dvo,
+ * the address it's responding on.
+ */
+ if ((temp & VR00_BASE_ADDRESS_MASK) != dvo->slave_addr) {
+- DRM_DEBUG("ivch detect failed due to address mismatch "
++ DRM_DEBUG_KMS("ivch detect failed due to address mismatch "
+ "(%d vs %d)\n",
+ (temp & VR00_BASE_ADDRESS_MASK), dvo->slave_addr);
+ goto out;
+@@ -367,41 +368,41 @@ static void ivch_dump_regs(struct intel_dvo_device *dvo)
+ uint16_t val;
+
+ ivch_read(dvo, VR00, &val);
+- DRM_DEBUG("VR00: 0x%04x\n", val);
++ DRM_LOG_KMS("VR00: 0x%04x\n", val);
+ ivch_read(dvo, VR01, &val);
+- DRM_DEBUG("VR01: 0x%04x\n", val);
++ DRM_LOG_KMS("VR01: 0x%04x\n", val);
+ ivch_read(dvo, VR30, &val);
+- DRM_DEBUG("VR30: 0x%04x\n", val);
++ DRM_LOG_KMS("VR30: 0x%04x\n", val);
+ ivch_read(dvo, VR40, &val);
+- DRM_DEBUG("VR40: 0x%04x\n", val);
++ DRM_LOG_KMS("VR40: 0x%04x\n", val);
+
+ /* GPIO registers */
+ ivch_read(dvo, VR80, &val);
+- DRM_DEBUG("VR80: 0x%04x\n", val);
++ DRM_LOG_KMS("VR80: 0x%04x\n", val);
+ ivch_read(dvo, VR81, &val);
+- DRM_DEBUG("VR81: 0x%04x\n", val);
++ DRM_LOG_KMS("VR81: 0x%04x\n", val);
+ ivch_read(dvo, VR82, &val);
+- DRM_DEBUG("VR82: 0x%04x\n", val);
++ DRM_LOG_KMS("VR82: 0x%04x\n", val);
+ ivch_read(dvo, VR83, &val);
+- DRM_DEBUG("VR83: 0x%04x\n", val);
++ DRM_LOG_KMS("VR83: 0x%04x\n", val);
+ ivch_read(dvo, VR84, &val);
+- DRM_DEBUG("VR84: 0x%04x\n", val);
++ DRM_LOG_KMS("VR84: 0x%04x\n", val);
+ ivch_read(dvo, VR85, &val);
+- DRM_DEBUG("VR85: 0x%04x\n", val);
++ DRM_LOG_KMS("VR85: 0x%04x\n", val);
+ ivch_read(dvo, VR86, &val);
+- DRM_DEBUG("VR86: 0x%04x\n", val);
++ DRM_LOG_KMS("VR86: 0x%04x\n", val);
+ ivch_read(dvo, VR87, &val);
+- DRM_DEBUG("VR87: 0x%04x\n", val);
++ DRM_LOG_KMS("VR87: 0x%04x\n", val);
+ ivch_read(dvo, VR88, &val);
+- DRM_DEBUG("VR88: 0x%04x\n", val);
++ DRM_LOG_KMS("VR88: 0x%04x\n", val);
+
+ /* Scratch register 0 - AIM Panel type */
+ ivch_read(dvo, VR8E, &val);
+- DRM_DEBUG("VR8E: 0x%04x\n", val);
++ DRM_LOG_KMS("VR8E: 0x%04x\n", val);
+
+ /* Scratch register 1 - Status register */
+ ivch_read(dvo, VR8F, &val);
+- DRM_DEBUG("VR8F: 0x%04x\n", val);
++ DRM_LOG_KMS("VR8F: 0x%04x\n", val);
+ }
+
+ static void ivch_save(struct intel_dvo_device *dvo)
+diff --git a/drivers/gpu/drm/i915/dvo_sil164.c b/drivers/gpu/drm/i915/dvo_sil164.c
+index e1c1f73..0001c13 100644
+--- a/drivers/gpu/drm/i915/dvo_sil164.c
++++ b/drivers/gpu/drm/i915/dvo_sil164.c
+@@ -105,7 +105,7 @@ static bool sil164_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
+ };
+
+ if (!sil->quiet) {
+- DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
++ DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
+ addr, i2cbus->adapter.name, dvo->slave_addr);
+ }
+ return false;
+@@ -131,7 +131,7 @@ static bool sil164_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
+ return true;
+
+ if (!sil->quiet) {
+- DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
++ DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
+ addr, i2cbus->adapter.name, dvo->slave_addr);
+ }
+
+@@ -158,7 +158,7 @@ static bool sil164_init(struct intel_dvo_device *dvo,
+ goto out;
+
+ if (ch != (SIL164_VID & 0xff)) {
+- DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n",
++ DRM_DEBUG_KMS("sil164 not detected got %d: from %s Slave %d.\n",
+ ch, adapter->name, dvo->slave_addr);
+ goto out;
+ }
+@@ -167,13 +167,13 @@ static bool sil164_init(struct intel_dvo_device *dvo,
+ goto out;
+
+ if (ch != (SIL164_DID & 0xff)) {
+- DRM_DEBUG("sil164 not detected got %d: from %s Slave %d.\n",
++ DRM_DEBUG_KMS("sil164 not detected got %d: from %s Slave %d.\n",
+ ch, adapter->name, dvo->slave_addr);
+ goto out;
+ }
+ sil->quiet = false;
+
+- DRM_DEBUG("init sil164 dvo controller successfully!\n");
++ DRM_DEBUG_KMS("init sil164 dvo controller successfully!\n");
+ return true;
+
+ out:
+@@ -241,15 +241,15 @@ static void sil164_dump_regs(struct intel_dvo_device *dvo)
+ uint8_t val;
+
+ sil164_readb(dvo, SIL164_FREQ_LO, &val);
+- DRM_DEBUG("SIL164_FREQ_LO: 0x%02x\n", val);
++ DRM_LOG_KMS("SIL164_FREQ_LO: 0x%02x\n", val);
+ sil164_readb(dvo, SIL164_FREQ_HI, &val);
+- DRM_DEBUG("SIL164_FREQ_HI: 0x%02x\n", val);
++ DRM_LOG_KMS("SIL164_FREQ_HI: 0x%02x\n", val);
+ sil164_readb(dvo, SIL164_REG8, &val);
+- DRM_DEBUG("SIL164_REG8: 0x%02x\n", val);
++ DRM_LOG_KMS("SIL164_REG8: 0x%02x\n", val);
+ sil164_readb(dvo, SIL164_REG9, &val);
+- DRM_DEBUG("SIL164_REG9: 0x%02x\n", val);
++ DRM_LOG_KMS("SIL164_REG9: 0x%02x\n", val);
+ sil164_readb(dvo, SIL164_REGC, &val);
+- DRM_DEBUG("SIL164_REGC: 0x%02x\n", val);
++ DRM_LOG_KMS("SIL164_REGC: 0x%02x\n", val);
+ }
+
+ static void sil164_save(struct intel_dvo_device *dvo)
+diff --git a/drivers/gpu/drm/i915/dvo_tfp410.c b/drivers/gpu/drm/i915/dvo_tfp410.c
+index 9ecc907..c7c391b 100644
+--- a/drivers/gpu/drm/i915/dvo_tfp410.c
++++ b/drivers/gpu/drm/i915/dvo_tfp410.c
+@@ -130,7 +130,7 @@ static bool tfp410_readb(struct intel_dvo_device *dvo, int addr, uint8_t *ch)
+ };
+
+ if (!tfp->quiet) {
+- DRM_DEBUG("Unable to read register 0x%02x from %s:%02x.\n",
++ DRM_DEBUG_KMS("Unable to read register 0x%02x from %s:%02x.\n",
+ addr, i2cbus->adapter.name, dvo->slave_addr);
+ }
+ return false;
+@@ -156,7 +156,7 @@ static bool tfp410_writeb(struct intel_dvo_device *dvo, int addr, uint8_t ch)
+ return true;
+
+ if (!tfp->quiet) {
+- DRM_DEBUG("Unable to write register 0x%02x to %s:%d.\n",
++ DRM_DEBUG_KMS("Unable to write register 0x%02x to %s:%d.\n",
+ addr, i2cbus->adapter.name, dvo->slave_addr);
+ }
+
+@@ -191,13 +191,15 @@ static bool tfp410_init(struct intel_dvo_device *dvo,
+ tfp->quiet = true;
+
+ if ((id = tfp410_getid(dvo, TFP410_VID_LO)) != TFP410_VID) {
+- DRM_DEBUG("tfp410 not detected got VID %X: from %s Slave %d.\n",
++ DRM_DEBUG_KMS("tfp410 not detected got VID %X: from %s "
++ "Slave %d.\n",
+ id, adapter->name, dvo->slave_addr);
+ goto out;
+ }
+
+ if ((id = tfp410_getid(dvo, TFP410_DID_LO)) != TFP410_DID) {
+- DRM_DEBUG("tfp410 not detected got DID %X: from %s Slave %d.\n",
++ DRM_DEBUG_KMS("tfp410 not detected got DID %X: from %s "
++ "Slave %d.\n",
+ id, adapter->name, dvo->slave_addr);
+ goto out;
+ }
+@@ -262,33 +264,33 @@ static void tfp410_dump_regs(struct intel_dvo_device *dvo)
+ uint8_t val, val2;
+
+ tfp410_readb(dvo, TFP410_REV, &val);
+- DRM_DEBUG("TFP410_REV: 0x%02X\n", val);
++ DRM_LOG_KMS("TFP410_REV: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_CTL_1, &val);
+- DRM_DEBUG("TFP410_CTL1: 0x%02X\n", val);
++ DRM_LOG_KMS("TFP410_CTL1: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_CTL_2, &val);
+- DRM_DEBUG("TFP410_CTL2: 0x%02X\n", val);
++ DRM_LOG_KMS("TFP410_CTL2: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_CTL_3, &val);
+- DRM_DEBUG("TFP410_CTL3: 0x%02X\n", val);
++ DRM_LOG_KMS("TFP410_CTL3: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_USERCFG, &val);
+- DRM_DEBUG("TFP410_USERCFG: 0x%02X\n", val);
++ DRM_LOG_KMS("TFP410_USERCFG: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_DE_DLY, &val);
+- DRM_DEBUG("TFP410_DE_DLY: 0x%02X\n", val);
++ DRM_LOG_KMS("TFP410_DE_DLY: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_DE_CTL, &val);
+- DRM_DEBUG("TFP410_DE_CTL: 0x%02X\n", val);
++ DRM_LOG_KMS("TFP410_DE_CTL: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_DE_TOP, &val);
+- DRM_DEBUG("TFP410_DE_TOP: 0x%02X\n", val);
++ DRM_LOG_KMS("TFP410_DE_TOP: 0x%02X\n", val);
+ tfp410_readb(dvo, TFP410_DE_CNT_LO, &val);
+ tfp410_readb(dvo, TFP410_DE_CNT_HI, &val2);
+- DRM_DEBUG("TFP410_DE_CNT: 0x%02X%02X\n", val2, val);
++ DRM_LOG_KMS("TFP410_DE_CNT: 0x%02X%02X\n", val2, val);
+ tfp410_readb(dvo, TFP410_DE_LIN_LO, &val);
+ tfp410_readb(dvo, TFP410_DE_LIN_HI, &val2);
+- DRM_DEBUG("TFP410_DE_LIN: 0x%02X%02X\n", val2, val);
++ DRM_LOG_KMS("TFP410_DE_LIN: 0x%02X%02X\n", val2, val);
+ tfp410_readb(dvo, TFP410_H_RES_LO, &val);
+ tfp410_readb(dvo, TFP410_H_RES_HI, &val2);
+- DRM_DEBUG("TFP410_H_RES: 0x%02X%02X\n", val2, val);
++ DRM_LOG_KMS("TFP410_H_RES: 0x%02X%02X\n", val2, val);
+ tfp410_readb(dvo, TFP410_V_RES_LO, &val);
+ tfp410_readb(dvo, TFP410_V_RES_HI, &val2);
+- DRM_DEBUG("TFP410_V_RES: 0x%02X%02X\n", val2, val);
++ DRM_LOG_KMS("TFP410_V_RES: 0x%02X%02X\n", val2, val);
+ }
+
+ static void tfp410_save(struct intel_dvo_device *dvo)
+diff --git a/drivers/gpu/drm/i915/i915_debugfs.c b/drivers/gpu/drm/i915/i915_debugfs.c
+index af655e8..a894ade 100644
+--- a/drivers/gpu/drm/i915/i915_debugfs.c
++++ b/drivers/gpu/drm/i915/i915_debugfs.c
+@@ -27,6 +27,7 @@
+ */
+
+ #include <linux/seq_file.h>
++#include <linux/debugfs.h>
+ #include "drmP.h"
+ #include "drm.h"
+ #include "i915_drm.h"
+@@ -96,13 +97,14 @@ static int i915_gem_object_list_info(struct seq_file *m, void *data)
+ {
+ struct drm_gem_object *obj = obj_priv->obj;
+
+- seq_printf(m, " %p: %s %8zd %08x %08x %d %s",
++ seq_printf(m, " %p: %s %8zd %08x %08x %d%s%s",
+ obj,
+ get_pin_flag(obj_priv),
+ obj->size,
+ obj->read_domains, obj->write_domain,
+ obj_priv->last_rendering_seqno,
+- obj_priv->dirty ? "dirty" : "");
++ obj_priv->dirty ? " dirty" : "",
++ obj_priv->madv == I915_MADV_DONTNEED ? " purgeable" : "");
+
+ if (obj->name)
+ seq_printf(m, " (name: %d)", obj->name);
+@@ -160,7 +162,7 @@ static int i915_interrupt_info(struct seq_file *m, void *data)
+ struct drm_device *dev = node->minor->dev;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+- if (!IS_IGDNG(dev)) {
++ if (!IS_IRONLAKE(dev)) {
+ seq_printf(m, "Interrupt enable: %08x\n",
+ I915_READ(IER));
+ seq_printf(m, "Interrupt identity: %08x\n",
+@@ -270,7 +272,7 @@ static void i915_dump_pages(struct seq_file *m, struct page **pages, int page_co
+ mem = kmap_atomic(pages[page], KM_USER0);
+ for (i = 0; i < PAGE_SIZE; i += 4)
+ seq_printf(m, "%08x : %08x\n", i, mem[i / 4]);
+- kunmap_atomic(pages[page], KM_USER0);
++ kunmap_atomic(mem, KM_USER0);
+ }
+ }
+
+@@ -384,37 +386,111 @@ out:
+ return 0;
+ }
+
+-static int i915_registers_info(struct seq_file *m, void *data) {
+- struct drm_info_node *node = (struct drm_info_node *) m->private;
+- struct drm_device *dev = node->minor->dev;
++static int
++i915_wedged_open(struct inode *inode,
++ struct file *filp)
++{
++ filp->private_data = inode->i_private;
++ return 0;
++}
++
++static ssize_t
++i915_wedged_read(struct file *filp,
++ char __user *ubuf,
++ size_t max,
++ loff_t *ppos)
++{
++ struct drm_device *dev = filp->private_data;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+- uint32_t reg;
+-
+-#define DUMP_RANGE(start, end) \
+- for (reg=start; reg < end; reg += 4) \
+- seq_printf(m, "%08x\t%08x\n", reg, I915_READ(reg));
+-
+- DUMP_RANGE(0x00000, 0x00fff); /* VGA registers */
+- DUMP_RANGE(0x02000, 0x02fff); /* instruction, memory, interrupt control registers */
+- DUMP_RANGE(0x03000, 0x031ff); /* FENCE and PPGTT control registers */
+- DUMP_RANGE(0x03200, 0x03fff); /* frame buffer compression registers */
+- DUMP_RANGE(0x05000, 0x05fff); /* I/O control registers */
+- DUMP_RANGE(0x06000, 0x06fff); /* clock control registers */
+- DUMP_RANGE(0x07000, 0x07fff); /* 3D internal debug registers */
+- DUMP_RANGE(0x07400, 0x088ff); /* GPE debug registers */
+- DUMP_RANGE(0x0a000, 0x0afff); /* display palette registers */
+- DUMP_RANGE(0x10000, 0x13fff); /* MMIO MCHBAR */
+- DUMP_RANGE(0x30000, 0x3ffff); /* overlay registers */
+- DUMP_RANGE(0x60000, 0x6ffff); /* display engine pipeline registers */
+- DUMP_RANGE(0x70000, 0x72fff); /* display and cursor registers */
+- DUMP_RANGE(0x73000, 0x73fff); /* performance counters */
++ char buf[80];
++ int len;
++
++ len = snprintf(buf, sizeof (buf),
++ "wedged : %d\n",
++ atomic_read(&dev_priv->mm.wedged));
++
++ return simple_read_from_buffer(ubuf, max, ppos, buf, len);
++}
++
++static ssize_t
++i915_wedged_write(struct file *filp,
++ const char __user *ubuf,
++ size_t cnt,
++ loff_t *ppos)
++{
++ struct drm_device *dev = filp->private_data;
++ drm_i915_private_t *dev_priv = dev->dev_private;
++ char buf[20];
++ int val = 1;
++
++ if (cnt > 0) {
++ if (cnt > sizeof (buf) - 1)
++ return -EINVAL;
++
++ if (copy_from_user(buf, ubuf, cnt))
++ return -EFAULT;
++ buf[cnt] = 0;
++
++ val = simple_strtoul(buf, NULL, 0);
++ }
++
++ DRM_INFO("Manually setting wedged to %d\n", val);
++
++ atomic_set(&dev_priv->mm.wedged, val);
++ if (val) {
++ DRM_WAKEUP(&dev_priv->irq_queue);
++ queue_work(dev_priv->wq, &dev_priv->error_work);
++ }
++
++ return cnt;
++}
++
++static const struct file_operations i915_wedged_fops = {
++ .owner = THIS_MODULE,
++ .open = i915_wedged_open,
++ .read = i915_wedged_read,
++ .write = i915_wedged_write,
++};
++
++/* As the drm_debugfs_init() routines are called before dev->dev_private is
++ * allocated we need to hook into the minor for release. */
++static int
++drm_add_fake_info_node(struct drm_minor *minor,
++ struct dentry *ent,
++ const void *key)
++{
++ struct drm_info_node *node;
++
++ node = kmalloc(sizeof(struct drm_info_node), GFP_KERNEL);
++ if (node == NULL) {
++ debugfs_remove(ent);
++ return -ENOMEM;
++ }
++
++ node->minor = minor;
++ node->dent = ent;
++ node->info_ent = (void *) key;
++ list_add(&node->list, &minor->debugfs_nodes.list);
+
+ return 0;
+ }
+
++static int i915_wedged_create(struct dentry *root, struct drm_minor *minor)
++{
++ struct drm_device *dev = minor->dev;
++ struct dentry *ent;
++
++ ent = debugfs_create_file("i915_wedged",
++ S_IRUGO | S_IWUSR,
++ root, dev,
++ &i915_wedged_fops);
++ if (IS_ERR(ent))
++ return PTR_ERR(ent);
++
++ return drm_add_fake_info_node(minor, ent, &i915_wedged_fops);
++}
+
+ static struct drm_info_list i915_debugfs_list[] = {
+- {"i915_regs", i915_registers_info, 0},
+ {"i915_gem_active", i915_gem_object_list_info, 0, (void *) ACTIVE_LIST},
+ {"i915_gem_flushing", i915_gem_object_list_info, 0, (void *) FLUSHING_LIST},
+ {"i915_gem_inactive", i915_gem_object_list_info, 0, (void *) INACTIVE_LIST},
+@@ -432,6 +508,12 @@ static struct drm_info_list i915_debugfs_list[] = {
+
+ int i915_debugfs_init(struct drm_minor *minor)
+ {
++ int ret;
++
++ ret = i915_wedged_create(minor->debugfs_root, minor);
++ if (ret)
++ return ret;
++
+ return drm_debugfs_create_files(i915_debugfs_list,
+ I915_DEBUGFS_ENTRIES,
+ minor->debugfs_root, minor);
+@@ -441,7 +523,8 @@ void i915_debugfs_cleanup(struct drm_minor *minor)
+ {
+ drm_debugfs_remove_files(i915_debugfs_list,
+ I915_DEBUGFS_ENTRIES, minor);
++ drm_debugfs_remove_files((struct drm_info_list *) &i915_wedged_fops,
++ 1, minor);
+ }
+
+ #endif /* CONFIG_DEBUG_FS */
+-
+diff --git a/drivers/gpu/drm/i915/i915_dma.c b/drivers/gpu/drm/i915/i915_dma.c
+index eaa1893..2307f98 100644
+--- a/drivers/gpu/drm/i915/i915_dma.c
++++ b/drivers/gpu/drm/i915/i915_dma.c
+@@ -134,6 +134,10 @@ static int i915_init_phys_hws(struct drm_device *dev)
+
+ memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
+
++ if (IS_I965G(dev))
++ dev_priv->dma_status_page |= (dev_priv->dma_status_page >> 28) &
++ 0xf0;
++
+ I915_WRITE(HWS_PGA, dev_priv->dma_status_page);
+ DRM_DEBUG_DRIVER("Enabled hardware status page\n");
+ return 0;
+@@ -731,8 +735,10 @@ static int i915_cmdbuffer(struct drm_device *dev, void *data,
+ if (cmdbuf->num_cliprects) {
+ cliprects = kcalloc(cmdbuf->num_cliprects,
+ sizeof(struct drm_clip_rect), GFP_KERNEL);
+- if (cliprects == NULL)
++ if (cliprects == NULL) {
++ ret = -ENOMEM;
+ goto fail_batch_free;
++ }
+
+ ret = copy_from_user(cliprects, cmdbuf->cliprects,
+ cmdbuf->num_cliprects *
+@@ -807,9 +813,19 @@ static int i915_getparam(struct drm_device *dev, void *data,
+ case I915_PARAM_NUM_FENCES_AVAIL:
+ value = dev_priv->num_fence_regs - dev_priv->fence_reg_start;
+ break;
++ case I915_PARAM_HAS_OVERLAY:
++ value = dev_priv->overlay ? 1 : 0;
++ break;
++ case I915_PARAM_HAS_PAGEFLIPPING:
++ value = 1;
++ break;
++ case I915_PARAM_HAS_EXECBUF2:
++ /* depends on GEM */
++ value = dev_priv->has_gem;
++ break;
+ default:
+ DRM_DEBUG_DRIVER("Unknown parameter %d\n",
+- param->param);
++ param->param);
+ return -EINVAL;
+ }
+
+@@ -962,7 +978,7 @@ static int i915_probe_agp(struct drm_device *dev, uint32_t *aperture_size,
+ * Some of the preallocated space is taken by the GTT
+ * and popup. GTT is 1K per MB of aperture size, and popup is 4K.
+ */
+- if (IS_G4X(dev) || IS_IGD(dev) || IS_IGDNG(dev))
++ if (IS_G4X(dev) || IS_PINEVIEW(dev) || IS_IRONLAKE(dev))
+ overhead = 4096;
+ else
+ overhead = (*aperture_size / 1024) + 4096;
+@@ -1048,7 +1064,7 @@ static unsigned long i915_gtt_to_phys(struct drm_device *dev,
+ int gtt_offset, gtt_size;
+
+ if (IS_I965G(dev)) {
+- if (IS_G4X(dev) || IS_IGDNG(dev)) {
++ if (IS_G4X(dev) || IS_IRONLAKE(dev)) {
+ gtt_offset = 2*1024*1024;
+ gtt_size = 2*1024*1024;
+ } else {
+@@ -1070,7 +1086,7 @@ static unsigned long i915_gtt_to_phys(struct drm_device *dev,
+
+ entry = *(volatile u32 *)(gtt + (gtt_addr / 1024));
+
+- DRM_DEBUG("GTT addr: 0x%08lx, PTE: 0x%08lx\n", gtt_addr, entry);
++ DRM_DEBUG_DRIVER("GTT addr: 0x%08lx, PTE: 0x%08lx\n", gtt_addr, entry);
+
+ /* Mask out these reserved bits on this hardware. */
+ if (!IS_I9XX(dev) || IS_I915G(dev) || IS_I915GM(dev) ||
+@@ -1096,7 +1112,7 @@ static unsigned long i915_gtt_to_phys(struct drm_device *dev,
+ phys =(entry & PTE_ADDRESS_MASK) |
+ ((uint64_t)(entry & PTE_ADDRESS_MASK_HIGH) << (32 - 4));
+
+- DRM_DEBUG("GTT addr: 0x%08lx, phys addr: 0x%08lx\n", gtt_addr, phys);
++ DRM_DEBUG_DRIVER("GTT addr: 0x%08lx, phys addr: 0x%08lx\n", gtt_addr, phys);
+
+ return phys;
+ }
+@@ -1195,14 +1211,6 @@ static int i915_load_modeset_init(struct drm_device *dev,
+ dev->mode_config.fb_base = drm_get_resource_start(dev, fb_bar) &
+ 0xff000000;
+
+- if (IS_MOBILE(dev) || IS_I9XX(dev))
+- dev_priv->cursor_needs_physical = true;
+- else
+- dev_priv->cursor_needs_physical = false;
+-
+- if (IS_I965G(dev) || IS_G33(dev))
+- dev_priv->cursor_needs_physical = false;
+-
+ /* Basic memrange allocator for stolen space (aka vram) */
+ drm_mm_init(&dev_priv->vram, 0, prealloc_size);
+ DRM_INFO("set up %ldM of stolen space\n", prealloc_size / (1024*1024));
+@@ -1307,7 +1315,7 @@ static void i915_get_mem_freq(struct drm_device *dev)
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ u32 tmp;
+
+- if (!IS_IGD(dev))
++ if (!IS_PINEVIEW(dev))
+ return;
+
+ tmp = I915_READ(CLKCFG);
+@@ -1355,7 +1363,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
+ {
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ resource_size_t base, size;
+- int ret = 0, mmio_bar = IS_I9XX(dev) ? 0 : 1;
++ int ret = 0, mmio_bar;
+ uint32_t agp_size, prealloc_size, prealloc_start;
+
+ /* i915 has 4 more counters */
+@@ -1371,8 +1379,10 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
+
+ dev->dev_private = (void *)dev_priv;
+ dev_priv->dev = dev;
++ dev_priv->info = (struct intel_device_info *) flags;
+
+ /* Add register map (needed for suspend/resume) */
++ mmio_bar = IS_I9XX(dev) ? 0 : 1;
+ base = drm_get_resource_start(dev, mmio_bar);
+ size = drm_get_resource_len(dev, mmio_bar);
+
+@@ -1414,7 +1424,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
+ if (ret)
+ goto out_iomapfree;
+
+- dev_priv->wq = create_workqueue("i915");
++ dev_priv->wq = create_singlethread_workqueue("i915");
+ if (dev_priv->wq == NULL) {
+ DRM_ERROR("Failed to create our workqueue.\n");
+ ret = -ENOMEM;
+@@ -1435,7 +1445,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
+
+ dev->driver->get_vblank_counter = i915_get_vblank_counter;
+ dev->max_vblank_count = 0xffffff; /* only 24 bits of frame count */
+- if (IS_G4X(dev) || IS_IGDNG(dev)) {
++ if (IS_G4X(dev) || IS_IRONLAKE(dev)) {
+ dev->max_vblank_count = 0xffffffff; /* full 32 bit counter */
+ dev->driver->get_vblank_counter = gm45_get_vblank_counter;
+ }
+@@ -1490,9 +1500,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
+ }
+
+ /* Must be done after probing outputs */
+- /* FIXME: verify on IGDNG */
+- if (!IS_IGDNG(dev))
+- intel_opregion_init(dev, 0);
++ intel_opregion_init(dev, 0);
+
+ setup_timer(&dev_priv->hangcheck_timer, i915_hangcheck_elapsed,
+ (unsigned long) dev);
+@@ -1526,6 +1534,15 @@ int i915_driver_unload(struct drm_device *dev)
+ }
+
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
++ /*
++ * free the memory space allocated for the child device
++ * config parsed from VBT
++ */
++ if (dev_priv->child_dev && dev_priv->child_dev_num) {
++ kfree(dev_priv->child_dev);
++ dev_priv->child_dev = NULL;
++ dev_priv->child_dev_num = 0;
++ }
+ drm_irq_uninstall(dev);
+ vga_client_register(dev->pdev, NULL, NULL, NULL);
+ }
+@@ -1536,8 +1553,7 @@ int i915_driver_unload(struct drm_device *dev)
+ if (dev_priv->regs != NULL)
+ iounmap(dev_priv->regs);
+
+- if (!IS_IGDNG(dev))
+- intel_opregion_free(dev, 0);
++ intel_opregion_free(dev, 0);
+
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ intel_modeset_cleanup(dev);
+@@ -1549,6 +1565,8 @@ int i915_driver_unload(struct drm_device *dev)
+ mutex_unlock(&dev->struct_mutex);
+ drm_mm_takedown(&dev_priv->vram);
+ i915_gem_lastclose(dev);
++
++ intel_cleanup_overlay(dev);
+ }
+
+ pci_dev_put(dev_priv->bridge_dev);
+@@ -1639,6 +1657,7 @@ struct drm_ioctl_desc i915_ioctls[] = {
+ DRM_IOCTL_DEF(DRM_I915_HWS_ADDR, i915_set_status_page, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_I915_GEM_INIT, i915_gem_init_ioctl, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_I915_GEM_EXECBUFFER, i915_gem_execbuffer, DRM_AUTH),
++ DRM_IOCTL_DEF(DRM_I915_GEM_EXECBUFFER2, i915_gem_execbuffer2, DRM_AUTH),
+ DRM_IOCTL_DEF(DRM_I915_GEM_PIN, i915_gem_pin_ioctl, DRM_AUTH|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_I915_GEM_UNPIN, i915_gem_unpin_ioctl, DRM_AUTH|DRM_ROOT_ONLY),
+ DRM_IOCTL_DEF(DRM_I915_GEM_BUSY, i915_gem_busy_ioctl, DRM_AUTH),
+@@ -1657,6 +1676,8 @@ struct drm_ioctl_desc i915_ioctls[] = {
+ DRM_IOCTL_DEF(DRM_I915_GEM_GET_APERTURE, i915_gem_get_aperture_ioctl, 0),
+ DRM_IOCTL_DEF(DRM_I915_GET_PIPE_FROM_CRTC_ID, intel_get_pipe_from_crtc_id, 0),
+ DRM_IOCTL_DEF(DRM_I915_GEM_MADVISE, i915_gem_madvise_ioctl, 0),
++ DRM_IOCTL_DEF(DRM_I915_OVERLAY_PUT_IMAGE, intel_overlay_put_image, DRM_MASTER|DRM_CONTROL_ALLOW),
++ DRM_IOCTL_DEF(DRM_I915_OVERLAY_ATTRS, intel_overlay_attrs, DRM_MASTER|DRM_CONTROL_ALLOW),
+ };
+
+ int i915_max_ioctl = DRM_ARRAY_SIZE(i915_ioctls);
+diff --git a/drivers/gpu/drm/i915/i915_drv.c b/drivers/gpu/drm/i915/i915_drv.c
+index 7f436ec..79beffc 100644
+--- a/drivers/gpu/drm/i915/i915_drv.c
++++ b/drivers/gpu/drm/i915/i915_drv.c
+@@ -33,7 +33,6 @@
+ #include "i915_drm.h"
+ #include "i915_drv.h"
+
+-#include "drm_pciids.h"
+ #include <linux/console.h>
+ #include "drm_crtc_helper.h"
+
+@@ -46,88 +45,229 @@ module_param_named(fbpercrtc, i915_fbpercrtc, int, 0400);
+ unsigned int i915_powersave = 1;
+ module_param_named(powersave, i915_powersave, int, 0400);
+
++unsigned int i915_lvds_downclock = 0;
++module_param_named(lvds_downclock, i915_lvds_downclock, int, 0400);
++
+ static struct drm_driver driver;
+
+-static struct pci_device_id pciidlist[] = {
+- i915_PCI_IDS
++#define INTEL_VGA_DEVICE(id, info) { \
++ .class = PCI_CLASS_DISPLAY_VGA << 8, \
++ .class_mask = 0xffff00, \
++ .vendor = 0x8086, \
++ .device = id, \
++ .subvendor = PCI_ANY_ID, \
++ .subdevice = PCI_ANY_ID, \
++ .driver_data = (unsigned long) info }
++
++const static struct intel_device_info intel_i830_info = {
++ .is_i8xx = 1, .is_mobile = 1, .cursor_needs_physical = 1,
++};
++
++const static struct intel_device_info intel_845g_info = {
++ .is_i8xx = 1,
++};
++
++const static struct intel_device_info intel_i85x_info = {
++ .is_i8xx = 1, .is_mobile = 1, .cursor_needs_physical = 1,
++};
++
++const static struct intel_device_info intel_i865g_info = {
++ .is_i8xx = 1,
++};
++
++const static struct intel_device_info intel_i915g_info = {
++ .is_i915g = 1, .is_i9xx = 1, .cursor_needs_physical = 1,
++};
++const static struct intel_device_info intel_i915gm_info = {
++ .is_i9xx = 1, .is_mobile = 1, .has_fbc = 1,
++ .cursor_needs_physical = 1,
++};
++const static struct intel_device_info intel_i945g_info = {
++ .is_i9xx = 1, .has_hotplug = 1, .cursor_needs_physical = 1,
++};
++const static struct intel_device_info intel_i945gm_info = {
++ .is_i945gm = 1, .is_i9xx = 1, .is_mobile = 1, .has_fbc = 1,
++ .has_hotplug = 1, .cursor_needs_physical = 1,
++};
++
++const static struct intel_device_info intel_i965g_info = {
++ .is_i965g = 1, .is_i9xx = 1, .has_hotplug = 1,
++};
++
++const static struct intel_device_info intel_i965gm_info = {
++ .is_i965g = 1, .is_mobile = 1, .is_i965gm = 1, .is_i9xx = 1,
++ .is_mobile = 1, .has_fbc = 1, .has_rc6 = 1,
++ .has_hotplug = 1,
++};
++
++const static struct intel_device_info intel_g33_info = {
++ .is_g33 = 1, .is_i9xx = 1, .need_gfx_hws = 1,
++ .has_hotplug = 1,
++};
++
++const static struct intel_device_info intel_g45_info = {
++ .is_i965g = 1, .is_g4x = 1, .is_i9xx = 1, .need_gfx_hws = 1,
++ .has_pipe_cxsr = 1,
++ .has_hotplug = 1,
++};
++
++const static struct intel_device_info intel_gm45_info = {
++ .is_i965g = 1, .is_mobile = 1, .is_g4x = 1, .is_i9xx = 1,
++ .is_mobile = 1, .need_gfx_hws = 1, .has_fbc = 1, .has_rc6 = 1,
++ .has_pipe_cxsr = 1,
++ .has_hotplug = 1,
++};
++
++const static struct intel_device_info intel_pineview_info = {
++ .is_g33 = 1, .is_pineview = 1, .is_mobile = 1, .is_i9xx = 1,
++ .need_gfx_hws = 1,
++ .has_hotplug = 1,
++};
++
++const static struct intel_device_info intel_ironlake_d_info = {
++ .is_ironlake = 1, .is_i965g = 1, .is_i9xx = 1, .need_gfx_hws = 1,
++ .has_pipe_cxsr = 1,
++ .has_hotplug = 1,
++};
++
++const static struct intel_device_info intel_ironlake_m_info = {
++ .is_ironlake = 1, .is_mobile = 1, .is_i965g = 1, .is_i9xx = 1,
++ .need_gfx_hws = 1, .has_rc6 = 1,
++ .has_hotplug = 1,
++};
++
++const static struct pci_device_id pciidlist[] = {
++ INTEL_VGA_DEVICE(0x3577, &intel_i830_info),
++ INTEL_VGA_DEVICE(0x2562, &intel_845g_info),
++ INTEL_VGA_DEVICE(0x3582, &intel_i85x_info),
++ INTEL_VGA_DEVICE(0x35e8, &intel_i85x_info),
++ INTEL_VGA_DEVICE(0x2572, &intel_i865g_info),
++ INTEL_VGA_DEVICE(0x2582, &intel_i915g_info),
++ INTEL_VGA_DEVICE(0x258a, &intel_i915g_info),
++ INTEL_VGA_DEVICE(0x2592, &intel_i915gm_info),
++ INTEL_VGA_DEVICE(0x2772, &intel_i945g_info),
++ INTEL_VGA_DEVICE(0x27a2, &intel_i945gm_info),
++ INTEL_VGA_DEVICE(0x27ae, &intel_i945gm_info),
++ INTEL_VGA_DEVICE(0x2972, &intel_i965g_info),
++ INTEL_VGA_DEVICE(0x2982, &intel_i965g_info),
++ INTEL_VGA_DEVICE(0x2992, &intel_i965g_info),
++ INTEL_VGA_DEVICE(0x29a2, &intel_i965g_info),
++ INTEL_VGA_DEVICE(0x29b2, &intel_g33_info),
++ INTEL_VGA_DEVICE(0x29c2, &intel_g33_info),
++ INTEL_VGA_DEVICE(0x29d2, &intel_g33_info),
++ INTEL_VGA_DEVICE(0x2a02, &intel_i965gm_info),
++ INTEL_VGA_DEVICE(0x2a12, &intel_i965gm_info),
++ INTEL_VGA_DEVICE(0x2a42, &intel_gm45_info),
++ INTEL_VGA_DEVICE(0x2e02, &intel_g45_info),
++ INTEL_VGA_DEVICE(0x2e12, &intel_g45_info),
++ INTEL_VGA_DEVICE(0x2e22, &intel_g45_info),
++ INTEL_VGA_DEVICE(0x2e32, &intel_g45_info),
++ INTEL_VGA_DEVICE(0x2e42, &intel_g45_info),
++ INTEL_VGA_DEVICE(0xa001, &intel_pineview_info),
++ INTEL_VGA_DEVICE(0xa011, &intel_pineview_info),
++ INTEL_VGA_DEVICE(0x0042, &intel_ironlake_d_info),
++ INTEL_VGA_DEVICE(0x0046, &intel_ironlake_m_info),
++ {0, 0, 0}
+ };
+
+ #if defined(CONFIG_DRM_I915_KMS)
+ MODULE_DEVICE_TABLE(pci, pciidlist);
+ #endif
+
+-static int i915_suspend(struct drm_device *dev, pm_message_t state)
++static int i915_drm_freeze(struct drm_device *dev)
+ {
+- struct drm_i915_private *dev_priv = dev->dev_private;
+-
+- if (!dev || !dev_priv) {
+- DRM_ERROR("dev: %p, dev_priv: %p\n", dev, dev_priv);
+- DRM_ERROR("DRM not initialized, aborting suspend.\n");
+- return -ENODEV;
+- }
+-
+- if (state.event == PM_EVENT_PRETHAW)
+- return 0;
+-
+ pci_save_state(dev->pdev);
+
+ /* If KMS is active, we do the leavevt stuff here */
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+- if (i915_gem_idle(dev))
++ int error = i915_gem_idle(dev);
++ if (error) {
+ dev_err(&dev->pdev->dev,
+- "GEM idle failed, resume may fail\n");
++ "GEM idle failed, resume might fail\n");
++ return error;
++ }
+ drm_irq_uninstall(dev);
+ }
+
+ i915_save_state(dev);
+
++ return 0;
++}
++
++static void i915_drm_suspend(struct drm_device *dev)
++{
++ struct drm_i915_private *dev_priv = dev->dev_private;
++
+ intel_opregion_free(dev, 1);
+
++ /* Modeset on resume, not lid events */
++ dev_priv->modeset_on_lid = 0;
++}
++
++static int i915_suspend(struct drm_device *dev, pm_message_t state)
++{
++ int error;
++
++ if (!dev || !dev->dev_private) {
++ DRM_ERROR("dev: %p\n", dev);
++ DRM_ERROR("DRM not initialized, aborting suspend.\n");
++ return -ENODEV;
++ }
++
++ if (state.event == PM_EVENT_PRETHAW)
++ return 0;
++
++ error = i915_drm_freeze(dev);
++ if (error)
++ return error;
++
++ i915_drm_suspend(dev);
++
+ if (state.event == PM_EVENT_SUSPEND) {
+ /* Shut down the device */
+ pci_disable_device(dev->pdev);
+ pci_set_power_state(dev->pdev, PCI_D3hot);
+ }
+
+- /* Modeset on resume, not lid events */
+- dev_priv->modeset_on_lid = 0;
+-
+ return 0;
+ }
+
+-static int i915_resume(struct drm_device *dev)
++static int i915_drm_thaw(struct drm_device *dev)
+ {
+ struct drm_i915_private *dev_priv = dev->dev_private;
+- int ret = 0;
+-
+- if (pci_enable_device(dev->pdev))
+- return -1;
+- pci_set_master(dev->pdev);
+-
+- i915_restore_state(dev);
+-
+- intel_opregion_init(dev, 1);
++ int error = 0;
+
+ /* KMS EnterVT equivalent */
+ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
+ mutex_lock(&dev->struct_mutex);
+ dev_priv->mm.suspended = 0;
+
+- ret = i915_gem_init_ringbuffer(dev);
+- if (ret != 0)
+- ret = -1;
++ error = i915_gem_init_ringbuffer(dev);
+ mutex_unlock(&dev->struct_mutex);
+
+ drm_irq_install(dev);
+- }
+- if (drm_core_check_feature(dev, DRIVER_MODESET)) {
++
+ /* Resume the modeset for every activated CRTC */
+ drm_helper_resume_force_mode(dev);
+ }
+
+ dev_priv->modeset_on_lid = 0;
+
+- return ret;
++ return error;
++}
++
++static int i915_resume(struct drm_device *dev)
++{
++ if (pci_enable_device(dev->pdev))
++ return -EIO;
++
++ pci_set_master(dev->pdev);
++
++ i915_restore_state(dev);
++
++ intel_opregion_init(dev, 1);
++
++ return i915_drm_thaw(dev);
+ }
+
+ /**
+@@ -268,22 +408,80 @@ i915_pci_remove(struct pci_dev *pdev)
+ drm_put_dev(dev);
+ }
+
+-static int
+-i915_pci_suspend(struct pci_dev *pdev, pm_message_t state)
++static int i915_pm_suspend(struct device *dev)
+ {
+- struct drm_device *dev = pci_get_drvdata(pdev);
++ struct pci_dev *pdev = to_pci_dev(dev);
++ struct drm_device *drm_dev = pci_get_drvdata(pdev);
++ int error;
+
+- return i915_suspend(dev, state);
++ if (!drm_dev || !drm_dev->dev_private) {
++ dev_err(dev, "DRM not initialized, aborting suspend.\n");
++ return -ENODEV;
++ }
++
++ error = i915_drm_freeze(drm_dev);
++ if (error)
++ return error;
++
++ i915_drm_suspend(drm_dev);
++
++ pci_disable_device(pdev);
++ pci_set_power_state(pdev, PCI_D3hot);
++
++ return 0;
+ }
+
+-static int
+-i915_pci_resume(struct pci_dev *pdev)
++static int i915_pm_resume(struct device *dev)
+ {
+- struct drm_device *dev = pci_get_drvdata(pdev);
++ struct pci_dev *pdev = to_pci_dev(dev);
++ struct drm_device *drm_dev = pci_get_drvdata(pdev);
++
++ return i915_resume(drm_dev);
++}
++
++static int i915_pm_freeze(struct device *dev)
++{
++ struct pci_dev *pdev = to_pci_dev(dev);
++ struct drm_device *drm_dev = pci_get_drvdata(pdev);
++
++ if (!drm_dev || !drm_dev->dev_private) {
++ dev_err(dev, "DRM not initialized, aborting suspend.\n");
++ return -ENODEV;
++ }
+
+- return i915_resume(dev);
++ return i915_drm_freeze(drm_dev);
+ }
+
++static int i915_pm_thaw(struct device *dev)
++{
++ struct pci_dev *pdev = to_pci_dev(dev);
++ struct drm_device *drm_dev = pci_get_drvdata(pdev);
++
++ return i915_drm_thaw(drm_dev);
++}
++
++static int i915_pm_poweroff(struct device *dev)
++{
++ struct pci_dev *pdev = to_pci_dev(dev);
++ struct drm_device *drm_dev = pci_get_drvdata(pdev);
++ int error;
++
++ error = i915_drm_freeze(drm_dev);
++ if (!error)
++ i915_drm_suspend(drm_dev);
++
++ return error;
++}
++
++const struct dev_pm_ops i915_pm_ops = {
++ .suspend = i915_pm_suspend,
++ .resume = i915_pm_resume,
++ .freeze = i915_pm_freeze,
++ .thaw = i915_pm_thaw,
++ .poweroff = i915_pm_poweroff,
++ .restore = i915_pm_resume,
++};
++
+ static struct vm_operations_struct i915_gem_vm_ops = {
+ .fault = i915_gem_fault,
+ .open = drm_gem_vm_open,
+@@ -303,8 +501,11 @@ static struct drm_driver driver = {
+ .lastclose = i915_driver_lastclose,
+ .preclose = i915_driver_preclose,
+ .postclose = i915_driver_postclose,
++
++ /* Used in place of i915_pm_ops for non-DRIVER_MODESET */
+ .suspend = i915_suspend,
+ .resume = i915_resume,
++
+ .device_is_agp = i915_driver_device_is_agp,
+ .enable_vblank = i915_enable_vblank,
+ .disable_vblank = i915_disable_vblank,
+@@ -329,10 +530,11 @@ static struct drm_driver driver = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+- .ioctl = drm_ioctl,
++ .unlocked_ioctl = drm_ioctl,
+ .mmap = drm_gem_mmap,
+ .poll = drm_poll,
+ .fasync = drm_fasync,
++ .read = drm_read,
+ #ifdef CONFIG_COMPAT
+ .compat_ioctl = i915_compat_ioctl,
+ #endif
+@@ -343,10 +545,7 @@ static struct drm_driver driver = {
+ .id_table = pciidlist,
+ .probe = i915_pci_probe,
+ .remove = i915_pci_remove,
+-#ifdef CONFIG_PM
+- .resume = i915_pci_resume,
+- .suspend = i915_pci_suspend,
+-#endif
++ .driver.pm = &i915_pm_ops,
+ },
+
+ .name = DRIVER_NAME,
+diff --git a/drivers/gpu/drm/i915/i915_drv.h b/drivers/gpu/drm/i915/i915_drv.h
+index 7277246..b99b6a8 100644
+--- a/drivers/gpu/drm/i915/i915_drv.h
++++ b/drivers/gpu/drm/i915/i915_drv.h
+@@ -170,9 +170,33 @@ struct drm_i915_display_funcs {
+ /* clock gating init */
+ };
+
++struct intel_overlay;
++
++struct intel_device_info {
++ u8 is_mobile : 1;
++ u8 is_i8xx : 1;
++ u8 is_i915g : 1;
++ u8 is_i9xx : 1;
++ u8 is_i945gm : 1;
++ u8 is_i965g : 1;
++ u8 is_i965gm : 1;
++ u8 is_g33 : 1;
++ u8 need_gfx_hws : 1;
++ u8 is_g4x : 1;
++ u8 is_pineview : 1;
++ u8 is_ironlake : 1;
++ u8 has_fbc : 1;
++ u8 has_rc6 : 1;
++ u8 has_pipe_cxsr : 1;
++ u8 has_hotplug : 1;
++ u8 cursor_needs_physical : 1;
++};
++
+ typedef struct drm_i915_private {
+ struct drm_device *dev;
+
++ const struct intel_device_info *info;
++
+ int has_gem;
+
+ void __iomem *regs;
+@@ -187,6 +211,7 @@ typedef struct drm_i915_private {
+ unsigned int status_gfx_addr;
+ drm_local_map_t hws_map;
+ struct drm_gem_object *hws_obj;
++ struct drm_gem_object *pwrctx;
+
+ struct resource mch_res;
+
+@@ -206,11 +231,13 @@ typedef struct drm_i915_private {
+ /** Cached value of IMR to avoid reads in updating the bitfield */
+ u32 irq_mask_reg;
+ u32 pipestat[2];
+- /** splitted irq regs for graphics and display engine on IGDNG,
++ /** splitted irq regs for graphics and display engine on Ironlake,
+ irq_mask_reg is still used for display irq. */
+ u32 gt_irq_mask_reg;
+ u32 gt_irq_enable_reg;
+ u32 de_irq_enable_reg;
++ u32 pch_irq_mask_reg;
++ u32 pch_irq_enable_reg;
+
+ u32 hotplug_supported_mask;
+ struct work_struct hotplug_work;
+@@ -227,8 +254,6 @@ typedef struct drm_i915_private {
+ int hangcheck_count;
+ uint32_t last_acthd;
+
+- bool cursor_needs_physical;
+-
+ struct drm_mm vram;
+
+ unsigned long cfb_size;
+@@ -240,6 +265,9 @@ typedef struct drm_i915_private {
+
+ struct intel_opregion opregion;
+
++ /* overlay */
++ struct intel_overlay *overlay;
++
+ /* LVDS info */
+ int backlight_duty_cycle; /* restore backlight to this value */
+ bool panel_wants_dither;
+@@ -255,10 +283,11 @@ typedef struct drm_i915_private {
+ unsigned int lvds_use_ssc:1;
+ unsigned int edp_support:1;
+ int lvds_ssc_freq;
++ int edp_bpp;
+
+ struct notifier_block lid_notifier;
+
+- int crt_ddc_bus; /* -1 = unknown, else GPIO to use for CRT DDC */
++ int crt_ddc_bus; /* 0 = unknown, else GPIO to use for CRT DDC */
+ struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */
+ int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */
+ int num_fence_regs; /* 8 on pre-965, 16 otherwise */
+@@ -279,7 +308,6 @@ typedef struct drm_i915_private {
+ u32 saveDSPACNTR;
+ u32 saveDSPBCNTR;
+ u32 saveDSPARB;
+- u32 saveRENDERSTANDBY;
+ u32 saveHWS;
+ u32 savePIPEACONF;
+ u32 savePIPEBCONF;
+@@ -374,8 +402,6 @@ typedef struct drm_i915_private {
+ u32 saveFDI_RXA_IMR;
+ u32 saveFDI_RXB_IMR;
+ u32 saveCACHE_MODE_0;
+- u32 saveD_STATE;
+- u32 saveDSPCLK_GATE_D;
+ u32 saveMI_ARB_STATE;
+ u32 saveSWF0[16];
+ u32 saveSWF1[16];
+@@ -467,6 +493,15 @@ typedef struct drm_i915_private {
+ struct list_head flushing_list;
+
+ /**
++ * List of objects currently pending a GPU write flush.
++ *
++ * All elements on this list will belong to either the
++ * active_list or flushing_list, last_rendering_seqno can
++ * be used to differentiate between the two elements.
++ */
++ struct list_head gpu_write_list;
++
++ /**
+ * LRU list of objects which are not in the ringbuffer and
+ * are ready to unbind, but are still in the GTT.
+ *
+@@ -539,13 +574,21 @@ typedef struct drm_i915_private {
+ /* indicate whether the LVDS_BORDER should be enabled or not */
+ unsigned int lvds_border_bits;
+
++ struct drm_crtc *plane_to_crtc_mapping[2];
++ struct drm_crtc *pipe_to_crtc_mapping[2];
++ wait_queue_head_t pending_flip_queue;
++
+ /* Reclocking support */
+ bool render_reclock_avail;
+ bool lvds_downclock_avail;
++ /* indicates the reduced downclock for LVDS*/
++ int lvds_downclock;
+ struct work_struct idle_work;
+ struct timer_list idle_timer;
+ bool busy;
+ u16 orig_clock;
++ int child_dev_num;
++ struct child_device_config *child_dev;
+ struct drm_connector *int_lvds_connector;
+ } drm_i915_private_t;
+
+@@ -558,6 +601,8 @@ struct drm_i915_gem_object {
+
+ /** This object's place on the active/flushing/inactive lists */
+ struct list_head list;
++ /** This object's place on GPU write list */
++ struct list_head gpu_write_list;
+
+ /** This object's place on the fenced object LRU */
+ struct list_head fence_list;
+@@ -639,6 +684,13 @@ struct drm_i915_gem_object {
+ * Advice: are the backing pages purgeable?
+ */
+ int madv;
++
++ /**
++ * Number of crtcs where this object is currently the fb, but
++ * will be page flipped away on the next vblank. When it
++ * reaches 0, dev_priv->pending_flip_queue will be woken up.
++ */
++ atomic_t pending_flip;
+ };
+
+ /**
+@@ -682,6 +734,7 @@ extern struct drm_ioctl_desc i915_ioctls[];
+ extern int i915_max_ioctl;
+ extern unsigned int i915_fbpercrtc;
+ extern unsigned int i915_powersave;
++extern unsigned int i915_lvds_downclock;
+
+ extern void i915_save_display(struct drm_device *dev);
+ extern void i915_restore_display(struct drm_device *dev);
+@@ -739,6 +792,8 @@ i915_enable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);
+ void
+ i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask);
+
++void intel_enable_asle (struct drm_device *dev);
++
+
+ /* i915_mem.c */
+ extern int i915_mem_alloc(struct drm_device *dev, void *data,
+@@ -771,6 +826,8 @@ int i915_gem_sw_finish_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+ int i915_gem_execbuffer(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
++int i915_gem_execbuffer2(struct drm_device *dev, void *data,
++ struct drm_file *file_priv);
+ int i915_gem_pin_ioctl(struct drm_device *dev, void *data,
+ struct drm_file *file_priv);
+ int i915_gem_unpin_ioctl(struct drm_device *dev, void *data,
+@@ -814,9 +871,13 @@ void i915_gem_cleanup_ringbuffer(struct drm_device *dev);
+ int i915_gem_do_init(struct drm_device *dev, unsigned long start,
+ unsigned long end);
+ int i915_gem_idle(struct drm_device *dev);
++uint32_t i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
++ uint32_t flush_domains);
++int i915_do_wait_request(struct drm_device *dev, uint32_t seqno, int interruptible);
+ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
+ int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj,
+ int write);
++int i915_gem_object_set_to_display_plane(struct drm_gem_object *obj);
+ int i915_gem_attach_phys_object(struct drm_device *dev,
+ struct drm_gem_object *obj, int id);
+ void i915_gem_detach_phys_object(struct drm_device *dev,
+@@ -825,6 +886,7 @@ void i915_gem_free_all_phys_object(struct drm_device *dev);
+ int i915_gem_object_get_pages(struct drm_gem_object *obj, gfp_t gfpmask);
+ void i915_gem_object_put_pages(struct drm_gem_object *obj);
+ void i915_gem_release(struct drm_device * dev, struct drm_file *file_priv);
++void i915_gem_object_flush_write_domain(struct drm_gem_object *obj);
+
+ void i915_gem_shrinker_init(void);
+ void i915_gem_shrinker_exit(void);
+@@ -833,6 +895,9 @@ void i915_gem_shrinker_exit(void);
+ void i915_gem_detect_bit_6_swizzle(struct drm_device *dev);
+ void i915_gem_object_do_bit_17_swizzle(struct drm_gem_object *obj);
+ void i915_gem_object_save_bit_17_swizzle(struct drm_gem_object *obj);
++bool i915_tiling_ok(struct drm_device *dev, int stride, int size,
++ int tiling_mode);
++bool i915_obj_fenceable(struct drm_device *dev, struct drm_gem_object *obj);
+
+ /* i915_gem_debug.c */
+ void i915_gem_dump_object(struct drm_gem_object *obj, int len,
+@@ -864,11 +929,13 @@ extern int i915_restore_state(struct drm_device *dev);
+ extern int intel_opregion_init(struct drm_device *dev, int resume);
+ extern void intel_opregion_free(struct drm_device *dev, int suspend);
+ extern void opregion_asle_intr(struct drm_device *dev);
++extern void ironlake_opregion_gse_intr(struct drm_device *dev);
+ extern void opregion_enable_asle(struct drm_device *dev);
+ #else
+ static inline int intel_opregion_init(struct drm_device *dev, int resume) { return 0; }
+ static inline void intel_opregion_free(struct drm_device *dev, int suspend) { return; }
+ static inline void opregion_asle_intr(struct drm_device *dev) { return; }
++static inline void ironlake_opregion_gse_intr(struct drm_device *dev) { return; }
+ static inline void opregion_enable_asle(struct drm_device *dev) { return; }
+ #endif
+
+@@ -953,89 +1020,52 @@ extern void g4x_disable_fbc(struct drm_device *dev);
+ extern int i915_wrap_ring(struct drm_device * dev);
+ extern int i915_wait_ring(struct drm_device * dev, int n, const char *caller);
+
+-#define IS_I830(dev) ((dev)->pci_device == 0x3577)
+-#define IS_845G(dev) ((dev)->pci_device == 0x2562)
+-#define IS_I85X(dev) ((dev)->pci_device == 0x3582)
+-#define IS_I855(dev) ((dev)->pci_device == 0x3582)
+-#define IS_I865G(dev) ((dev)->pci_device == 0x2572)
+-#define IS_I8XX(dev) (IS_I830(dev) || IS_845G(dev) || IS_I85X(dev) || IS_I865G(dev))
+-
+-#define IS_I915G(dev) ((dev)->pci_device == 0x2582 || (dev)->pci_device == 0x258a)
+-#define IS_I915GM(dev) ((dev)->pci_device == 0x2592)
+-#define IS_I945G(dev) ((dev)->pci_device == 0x2772)
+-#define IS_I945GM(dev) ((dev)->pci_device == 0x27A2 ||\
+- (dev)->pci_device == 0x27AE)
+-#define IS_I965G(dev) ((dev)->pci_device == 0x2972 || \
+- (dev)->pci_device == 0x2982 || \
+- (dev)->pci_device == 0x2992 || \
+- (dev)->pci_device == 0x29A2 || \
+- (dev)->pci_device == 0x2A02 || \
+- (dev)->pci_device == 0x2A12 || \
+- (dev)->pci_device == 0x2A42 || \
+- (dev)->pci_device == 0x2E02 || \
+- (dev)->pci_device == 0x2E12 || \
+- (dev)->pci_device == 0x2E22 || \
+- (dev)->pci_device == 0x2E32 || \
+- (dev)->pci_device == 0x2E42 || \
+- (dev)->pci_device == 0x0042 || \
+- (dev)->pci_device == 0x0046)
+-
+-#define IS_I965GM(dev) ((dev)->pci_device == 0x2A02 || \
+- (dev)->pci_device == 0x2A12)
+-
+-#define IS_GM45(dev) ((dev)->pci_device == 0x2A42)
+-
+-#define IS_G4X(dev) ((dev)->pci_device == 0x2E02 || \
+- (dev)->pci_device == 0x2E12 || \
+- (dev)->pci_device == 0x2E22 || \
+- (dev)->pci_device == 0x2E32 || \
+- (dev)->pci_device == 0x2E42 || \
+- IS_GM45(dev))
+-
+-#define IS_IGDG(dev) ((dev)->pci_device == 0xa001)
+-#define IS_IGDGM(dev) ((dev)->pci_device == 0xa011)
+-#define IS_IGD(dev) (IS_IGDG(dev) || IS_IGDGM(dev))
+-
+-#define IS_G33(dev) ((dev)->pci_device == 0x29C2 || \
+- (dev)->pci_device == 0x29B2 || \
+- (dev)->pci_device == 0x29D2 || \
+- (IS_IGD(dev)))
+-
+-#define IS_IGDNG_D(dev) ((dev)->pci_device == 0x0042)
+-#define IS_IGDNG_M(dev) ((dev)->pci_device == 0x0046)
+-#define IS_IGDNG(dev) (IS_IGDNG_D(dev) || IS_IGDNG_M(dev))
+-
+-#define IS_I9XX(dev) (IS_I915G(dev) || IS_I915GM(dev) || IS_I945G(dev) || \
+- IS_I945GM(dev) || IS_I965G(dev) || IS_G33(dev) || \
+- IS_IGDNG(dev))
+-
+-#define IS_MOBILE(dev) (IS_I830(dev) || IS_I85X(dev) || IS_I915GM(dev) || \
+- IS_I945GM(dev) || IS_I965GM(dev) || IS_GM45(dev) || \
+- IS_IGD(dev) || IS_IGDNG_M(dev))
+-
+-#define I915_NEED_GFX_HWS(dev) (IS_G33(dev) || IS_GM45(dev) || IS_G4X(dev) || \
+- IS_IGDNG(dev))
++#define INTEL_INFO(dev) (((struct drm_i915_private *) (dev)->dev_private)->info)
++
++#define IS_I830(dev) ((dev)->pci_device == 0x3577)
++#define IS_845G(dev) ((dev)->pci_device == 0x2562)
++#define IS_I85X(dev) ((dev)->pci_device == 0x3582)
++#define IS_I865G(dev) ((dev)->pci_device == 0x2572)
++#define IS_I8XX(dev) (INTEL_INFO(dev)->is_i8xx)
++#define IS_I915G(dev) (INTEL_INFO(dev)->is_i915g)
++#define IS_I915GM(dev) ((dev)->pci_device == 0x2592)
++#define IS_I945G(dev) ((dev)->pci_device == 0x2772)
++#define IS_I945GM(dev) (INTEL_INFO(dev)->is_i945gm)
++#define IS_I965G(dev) (INTEL_INFO(dev)->is_i965g)
++#define IS_I965GM(dev) (INTEL_INFO(dev)->is_i965gm)
++#define IS_GM45(dev) ((dev)->pci_device == 0x2A42)
++#define IS_G4X(dev) (INTEL_INFO(dev)->is_g4x)
++#define IS_PINEVIEW_G(dev) ((dev)->pci_device == 0xa001)
++#define IS_PINEVIEW_M(dev) ((dev)->pci_device == 0xa011)
++#define IS_PINEVIEW(dev) (INTEL_INFO(dev)->is_pineview)
++#define IS_G33(dev) (INTEL_INFO(dev)->is_g33)
++#define IS_IRONLAKE_D(dev) ((dev)->pci_device == 0x0042)
++#define IS_IRONLAKE_M(dev) ((dev)->pci_device == 0x0046)
++#define IS_IRONLAKE(dev) (INTEL_INFO(dev)->is_ironlake)
++#define IS_I9XX(dev) (INTEL_INFO(dev)->is_i9xx)
++#define IS_MOBILE(dev) (INTEL_INFO(dev)->is_mobile)
++
++#define I915_NEED_GFX_HWS(dev) (INTEL_INFO(dev)->need_gfx_hws)
++
+ /* With the 945 and later, Y tiling got adjusted so that it was 32 128-byte
+ * rows, which changed the alignment requirements and fence programming.
+ */
+ #define HAS_128_BYTE_Y_TILING(dev) (IS_I9XX(dev) && !(IS_I915G(dev) || \
+ IS_I915GM(dev)))
+-#define SUPPORTS_DIGITAL_OUTPUTS(dev) (IS_I9XX(dev) && !IS_IGD(dev))
+-#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev) || IS_IGDNG(dev))
+-#define SUPPORTS_INTEGRATED_DP(dev) (IS_G4X(dev) || IS_IGDNG(dev))
+-#define SUPPORTS_EDP(dev) (IS_IGDNG_M(dev))
++#define SUPPORTS_DIGITAL_OUTPUTS(dev) (IS_I9XX(dev) && !IS_PINEVIEW(dev))
++#define SUPPORTS_INTEGRATED_HDMI(dev) (IS_G4X(dev) || IS_IRONLAKE(dev))
++#define SUPPORTS_INTEGRATED_DP(dev) (IS_G4X(dev) || IS_IRONLAKE(dev))
++#define SUPPORTS_EDP(dev) (IS_IRONLAKE_M(dev))
+ #define SUPPORTS_TV(dev) (IS_I9XX(dev) && IS_MOBILE(dev) && \
+- !IS_IGDNG(dev) && !IS_IGD(dev))
+-#define I915_HAS_HOTPLUG(dev) (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev) || IS_I965G(dev))
++ !IS_IRONLAKE(dev) && !IS_PINEVIEW(dev))
++#define I915_HAS_HOTPLUG(dev) (INTEL_INFO(dev)->has_hotplug)
+ /* dsparb controlled by hw only */
+-#define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IGDNG(dev))
+-
+-#define HAS_FW_BLC(dev) (IS_I9XX(dev) || IS_G4X(dev) || IS_IGDNG(dev))
+-#define HAS_PIPE_CXSR(dev) (IS_G4X(dev) || IS_IGDNG(dev))
+-#define I915_HAS_FBC(dev) (IS_MOBILE(dev) && \
+- (IS_I9XX(dev) || IS_GM45(dev)) && \
+- !IS_IGD(dev) && \
+- !IS_IGDNG(dev))
++#define DSPARB_HWCONTROL(dev) (IS_G4X(dev) || IS_IRONLAKE(dev))
++
++#define HAS_FW_BLC(dev) (IS_I9XX(dev) || IS_G4X(dev) || IS_IRONLAKE(dev))
++#define HAS_PIPE_CXSR(dev) (INTEL_INFO(dev)->has_pipe_cxsr)
++#define I915_HAS_FBC(dev) (INTEL_INFO(dev)->has_fbc)
++#define I915_HAS_RC6(dev) (INTEL_INFO(dev)->has_rc6)
+
+ #define PRIMARY_RINGBUFFER_SIZE (128*1024)
+
+diff --git a/drivers/gpu/drm/i915/i915_gem.c b/drivers/gpu/drm/i915/i915_gem.c
+index 8ad244a..ec8a0d7 100644
+--- a/drivers/gpu/drm/i915/i915_gem.c
++++ b/drivers/gpu/drm/i915/i915_gem.c
+@@ -1294,7 +1294,7 @@ out_free_list:
+ * i915_gem_release_mmap - remove physical page mappings
+ * @obj: obj in question
+ *
+- * Preserve the reservation of the mmaping with the DRM core code, but
++ * Preserve the reservation of the mmapping with the DRM core code, but
+ * relinquish ownership of the pages back to the system.
+ *
+ * It is vital that we remove the page mapping if we have mapped a tiled
+@@ -1552,6 +1552,8 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj)
+ else
+ list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list);
+
++ BUG_ON(!list_empty(&obj_priv->gpu_write_list));
++
+ obj_priv->last_rendering_seqno = 0;
+ if (obj_priv->active) {
+ obj_priv->active = 0;
+@@ -1568,7 +1570,7 @@ i915_gem_object_move_to_inactive(struct drm_gem_object *obj)
+ *
+ * Returned sequence numbers are nonzero on success.
+ */
+-static uint32_t
++uint32_t
+ i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
+ uint32_t flush_domains)
+ {
+@@ -1602,7 +1604,7 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
+ OUT_RING(MI_USER_INTERRUPT);
+ ADVANCE_LP_RING();
+
+- DRM_DEBUG("%d\n", seqno);
++ DRM_DEBUG_DRIVER("%d\n", seqno);
+
+ request->seqno = seqno;
+ request->emitted_jiffies = jiffies;
+@@ -1622,7 +1624,8 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
+ struct drm_i915_gem_object *obj_priv, *next;
+
+ list_for_each_entry_safe(obj_priv, next,
+- &dev_priv->mm.flushing_list, list) {
++ &dev_priv->mm.gpu_write_list,
++ gpu_write_list) {
+ struct drm_gem_object *obj = obj_priv->obj;
+
+ if ((obj->write_domain & flush_domains) ==
+@@ -1630,6 +1633,7 @@ i915_add_request(struct drm_device *dev, struct drm_file *file_priv,
+ uint32_t old_write_domain = obj->write_domain;
+
+ obj->write_domain = 0;
++ list_del_init(&obj_priv->gpu_write_list);
+ i915_gem_object_move_to_active(obj, seqno);
+
+ trace_i915_gem_object_change_domain(obj,
+@@ -1805,12 +1809,8 @@ i915_gem_retire_work_handler(struct work_struct *work)
+ mutex_unlock(&dev->struct_mutex);
+ }
+
+-/**
+- * Waits for a sequence number to be signaled, and cleans up the
+- * request and object lists appropriately for that event.
+- */
+-static int
+-i915_wait_request(struct drm_device *dev, uint32_t seqno)
++int
++i915_do_wait_request(struct drm_device *dev, uint32_t seqno, int interruptible)
+ {
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ u32 ier;
+@@ -1822,7 +1822,7 @@ i915_wait_request(struct drm_device *dev, uint32_t seqno)
+ return -EIO;
+
+ if (!i915_seqno_passed(i915_get_gem_seqno(dev), seqno)) {
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ ier = I915_READ(DEIER) | I915_READ(GTIER);
+ else
+ ier = I915_READ(IER);
+@@ -1837,10 +1837,15 @@ i915_wait_request(struct drm_device *dev, uint32_t seqno)
+
+ dev_priv->mm.waiting_gem_seqno = seqno;
+ i915_user_irq_get(dev);
+- ret = wait_event_interruptible(dev_priv->irq_queue,
+- i915_seqno_passed(i915_get_gem_seqno(dev),
+- seqno) ||
+- atomic_read(&dev_priv->mm.wedged));
++ if (interruptible)
++ ret = wait_event_interruptible(dev_priv->irq_queue,
++ i915_seqno_passed(i915_get_gem_seqno(dev), seqno) ||
++ atomic_read(&dev_priv->mm.wedged));
++ else
++ wait_event(dev_priv->irq_queue,
++ i915_seqno_passed(i915_get_gem_seqno(dev), seqno) ||
++ atomic_read(&dev_priv->mm.wedged));
++
+ i915_user_irq_put(dev);
+ dev_priv->mm.waiting_gem_seqno = 0;
+
+@@ -1864,6 +1869,16 @@ i915_wait_request(struct drm_device *dev, uint32_t seqno)
+ return ret;
+ }
+
++/**
++ * Waits for a sequence number to be signaled, and cleans up the
++ * request and object lists appropriately for that event.
++ */
++static int
++i915_wait_request(struct drm_device *dev, uint32_t seqno)
++{
++ return i915_do_wait_request(dev, seqno, 1);
++}
++
+ static void
+ i915_gem_flush(struct drm_device *dev,
+ uint32_t invalidate_domains,
+@@ -1932,7 +1947,7 @@ i915_gem_flush(struct drm_device *dev,
+ #endif
+ BEGIN_LP_RING(2);
+ OUT_RING(cmd);
+- OUT_RING(0); /* noop */
++ OUT_RING(MI_NOOP);
+ ADVANCE_LP_RING();
+ }
+ }
+@@ -2073,8 +2088,8 @@ static int
+ i915_gem_evict_everything(struct drm_device *dev)
+ {
+ drm_i915_private_t *dev_priv = dev->dev_private;
+- uint32_t seqno;
+ int ret;
++ uint32_t seqno;
+ bool lists_empty;
+
+ spin_lock(&dev_priv->mm.active_list_lock);
+@@ -2096,6 +2111,8 @@ i915_gem_evict_everything(struct drm_device *dev)
+ if (ret)
+ return ret;
+
++ BUG_ON(!list_empty(&dev_priv->mm.flushing_list));
++
+ ret = i915_gem_evict_from_inactive_list(dev);
+ if (ret)
+ return ret;
+@@ -2690,7 +2707,7 @@ i915_gem_object_flush_gpu_write_domain(struct drm_gem_object *obj)
+ old_write_domain = obj->write_domain;
+ i915_gem_flush(dev, 0, obj->write_domain);
+ seqno = i915_add_request(dev, NULL, obj->write_domain);
+- obj->write_domain = 0;
++ BUG_ON(obj->write_domain);
+ i915_gem_object_move_to_active(obj, seqno);
+
+ trace_i915_gem_object_change_domain(obj,
+@@ -2739,6 +2756,22 @@ i915_gem_object_flush_cpu_write_domain(struct drm_gem_object *obj)
+ old_write_domain);
+ }
+
++void
++i915_gem_object_flush_write_domain(struct drm_gem_object *obj)
++{
++ switch (obj->write_domain) {
++ case I915_GEM_DOMAIN_GTT:
++ i915_gem_object_flush_gtt_write_domain(obj);
++ break;
++ case I915_GEM_DOMAIN_CPU:
++ i915_gem_object_flush_cpu_write_domain(obj);
++ break;
++ default:
++ i915_gem_object_flush_gpu_write_domain(obj);
++ break;
++ }
++}
++
+ /**
+ * Moves a single object to the GTT read, and possibly write domain.
+ *
+@@ -2790,6 +2823,57 @@ i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj, int write)
+ return 0;
+ }
+
++/*
++ * Prepare buffer for display plane. Use uninterruptible for possible flush
++ * wait, as in modesetting process we're not supposed to be interrupted.
++ */
++int
++i915_gem_object_set_to_display_plane(struct drm_gem_object *obj)
++{
++ struct drm_device *dev = obj->dev;
++ struct drm_i915_gem_object *obj_priv = obj->driver_private;
++ uint32_t old_write_domain, old_read_domains;
++ int ret;
++
++ /* Not valid to be called on unbound objects. */
++ if (obj_priv->gtt_space == NULL)
++ return -EINVAL;
++
++ i915_gem_object_flush_gpu_write_domain(obj);
++
++ /* Wait on any GPU rendering and flushing to occur. */
++ if (obj_priv->active) {
++#if WATCH_BUF
++ DRM_INFO("%s: object %p wait for seqno %08x\n",
++ __func__, obj, obj_priv->last_rendering_seqno);
++#endif
++ ret = i915_do_wait_request(dev, obj_priv->last_rendering_seqno, 0);
++ if (ret != 0)
++ return ret;
++ }
++
++ old_write_domain = obj->write_domain;
++ old_read_domains = obj->read_domains;
++
++ obj->read_domains &= I915_GEM_DOMAIN_GTT;
++
++ i915_gem_object_flush_cpu_write_domain(obj);
++
++ /* It should now be out of any other write domains, and we can update
++ * the domain values for our changes.
++ */
++ BUG_ON((obj->write_domain & ~I915_GEM_DOMAIN_GTT) != 0);
++ obj->read_domains |= I915_GEM_DOMAIN_GTT;
++ obj->write_domain = I915_GEM_DOMAIN_GTT;
++ obj_priv->dirty = 1;
++
++ trace_i915_gem_object_change_domain(obj,
++ old_read_domains,
++ old_write_domain);
++
++ return 0;
++}
++
+ /**
+ * Moves a single object to the CPU read, and possibly write domain.
+ *
+@@ -3149,7 +3233,7 @@ i915_gem_object_set_cpu_read_domain_range(struct drm_gem_object *obj,
+ static int
+ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
+ struct drm_file *file_priv,
+- struct drm_i915_gem_exec_object *entry,
++ struct drm_i915_gem_exec_object2 *entry,
+ struct drm_i915_gem_relocation_entry *relocs)
+ {
+ struct drm_device *dev = obj->dev;
+@@ -3157,12 +3241,35 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
+ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ int i, ret;
+ void __iomem *reloc_page;
++ bool need_fence;
++
++ need_fence = entry->flags & EXEC_OBJECT_NEEDS_FENCE &&
++ obj_priv->tiling_mode != I915_TILING_NONE;
++
++ /* Check fence reg constraints and rebind if necessary */
++ if (need_fence && !i915_obj_fenceable(dev, obj))
++ i915_gem_object_unbind(obj);
+
+ /* Choose the GTT offset for our buffer and put it there. */
+ ret = i915_gem_object_pin(obj, (uint32_t) entry->alignment);
+ if (ret)
+ return ret;
+
++ /*
++ * Pre-965 chips need a fence register set up in order to
++ * properly handle blits to/from tiled surfaces.
++ */
++ if (need_fence) {
++ ret = i915_gem_object_get_fence_reg(obj);
++ if (ret != 0) {
++ if (ret != -EBUSY && ret != -ERESTARTSYS)
++ DRM_ERROR("Failure to install fence: %d\n",
++ ret);
++ i915_gem_object_unpin(obj);
++ return ret;
++ }
++ }
++
+ entry->offset = obj_priv->gtt_offset;
+
+ /* Apply the relocations, using the GTT aperture to avoid cache
+@@ -3324,7 +3431,7 @@ i915_gem_object_pin_and_relocate(struct drm_gem_object *obj,
+ */
+ static int
+ i915_dispatch_gem_execbuffer(struct drm_device *dev,
+- struct drm_i915_gem_execbuffer *exec,
++ struct drm_i915_gem_execbuffer2 *exec,
+ struct drm_clip_rect *cliprects,
+ uint64_t exec_offset)
+ {
+@@ -3414,7 +3521,7 @@ i915_gem_ring_throttle(struct drm_device *dev, struct drm_file *file_priv)
+ }
+
+ static int
+-i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object *exec_list,
++i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object2 *exec_list,
+ uint32_t buffer_count,
+ struct drm_i915_gem_relocation_entry **relocs)
+ {
+@@ -3429,8 +3536,10 @@ i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object *exec_list,
+ }
+
+ *relocs = drm_calloc_large(reloc_count, sizeof(**relocs));
+- if (*relocs == NULL)
++ if (*relocs == NULL) {
++ DRM_ERROR("failed to alloc relocs, count %d\n", reloc_count);
+ return -ENOMEM;
++ }
+
+ for (i = 0; i < buffer_count; i++) {
+ struct drm_i915_gem_relocation_entry __user *user_relocs;
+@@ -3454,13 +3563,16 @@ i915_gem_get_relocs_from_user(struct drm_i915_gem_exec_object *exec_list,
+ }
+
+ static int
+-i915_gem_put_relocs_to_user(struct drm_i915_gem_exec_object *exec_list,
++i915_gem_put_relocs_to_user(struct drm_i915_gem_exec_object2 *exec_list,
+ uint32_t buffer_count,
+ struct drm_i915_gem_relocation_entry *relocs)
+ {
+ uint32_t reloc_count = 0, i;
+ int ret = 0;
+
++ if (relocs == NULL)
++ return 0;
++
+ for (i = 0; i < buffer_count; i++) {
+ struct drm_i915_gem_relocation_entry __user *user_relocs;
+ int unwritten;
+@@ -3487,7 +3599,7 @@ err:
+ }
+
+ static int
+-i915_gem_check_execbuffer (struct drm_i915_gem_execbuffer *exec,
++i915_gem_check_execbuffer (struct drm_i915_gem_execbuffer2 *exec,
+ uint64_t exec_offset)
+ {
+ uint32_t exec_start, exec_len;
+@@ -3504,22 +3616,57 @@ i915_gem_check_execbuffer (struct drm_i915_gem_execbuffer *exec,
+ return 0;
+ }
+
++static int
++i915_gem_wait_for_pending_flip(struct drm_device *dev,
++ struct drm_gem_object **object_list,
++ int count)
++{
++ drm_i915_private_t *dev_priv = dev->dev_private;
++ struct drm_i915_gem_object *obj_priv;
++ DEFINE_WAIT(wait);
++ int i, ret = 0;
++
++ for (;;) {
++ prepare_to_wait(&dev_priv->pending_flip_queue,
++ &wait, TASK_INTERRUPTIBLE);
++ for (i = 0; i < count; i++) {
++ obj_priv = object_list[i]->driver_private;
++ if (atomic_read(&obj_priv->pending_flip) > 0)
++ break;
++ }
++ if (i == count)
++ break;
++
++ if (!signal_pending(current)) {
++ mutex_unlock(&dev->struct_mutex);
++ schedule();
++ mutex_lock(&dev->struct_mutex);
++ continue;
++ }
++ ret = -ERESTARTSYS;
++ break;
++ }
++ finish_wait(&dev_priv->pending_flip_queue, &wait);
++
++ return ret;
++}
++
+ int
+-i915_gem_execbuffer(struct drm_device *dev, void *data,
+- struct drm_file *file_priv)
++i915_gem_do_execbuffer(struct drm_device *dev, void *data,
++ struct drm_file *file_priv,
++ struct drm_i915_gem_execbuffer2 *args,
++ struct drm_i915_gem_exec_object2 *exec_list)
+ {
+ drm_i915_private_t *dev_priv = dev->dev_private;
+- struct drm_i915_gem_execbuffer *args = data;
+- struct drm_i915_gem_exec_object *exec_list = NULL;
+ struct drm_gem_object **object_list = NULL;
+ struct drm_gem_object *batch_obj;
+ struct drm_i915_gem_object *obj_priv;
+ struct drm_clip_rect *cliprects = NULL;
+- struct drm_i915_gem_relocation_entry *relocs;
+- int ret, ret2, i, pinned = 0;
++ struct drm_i915_gem_relocation_entry *relocs = NULL;
++ int ret = 0, ret2, i, pinned = 0;
+ uint64_t exec_offset;
+ uint32_t seqno, flush_domains, reloc_index;
+- int pin_tries;
++ int pin_tries, flips;
+
+ #if WATCH_EXEC
+ DRM_INFO("buffers_ptr %d buffer_count %d len %08x\n",
+@@ -3530,31 +3677,21 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
+ DRM_ERROR("execbuf with %d buffers\n", args->buffer_count);
+ return -EINVAL;
+ }
+- /* Copy in the exec list from userland */
+- exec_list = drm_calloc_large(sizeof(*exec_list), args->buffer_count);
+- object_list = drm_calloc_large(sizeof(*object_list), args->buffer_count);
+- if (exec_list == NULL || object_list == NULL) {
+- DRM_ERROR("Failed to allocate exec or object list "
+- "for %d buffers\n",
++ object_list = drm_malloc_ab(sizeof(*object_list), args->buffer_count);
++ if (object_list == NULL) {
++ DRM_ERROR("Failed to allocate object list for %d buffers\n",
+ args->buffer_count);
+ ret = -ENOMEM;
+ goto pre_mutex_err;
+ }
+- ret = copy_from_user(exec_list,
+- (struct drm_i915_relocation_entry __user *)
+- (uintptr_t) args->buffers_ptr,
+- sizeof(*exec_list) * args->buffer_count);
+- if (ret != 0) {
+- DRM_ERROR("copy %d exec entries failed %d\n",
+- args->buffer_count, ret);
+- goto pre_mutex_err;
+- }
+
+ if (args->num_cliprects != 0) {
+ cliprects = kcalloc(args->num_cliprects, sizeof(*cliprects),
+ GFP_KERNEL);
+- if (cliprects == NULL)
++ if (cliprects == NULL) {
++ ret = -ENOMEM;
+ goto pre_mutex_err;
++ }
+
+ ret = copy_from_user(cliprects,
+ (struct drm_clip_rect __user *)
+@@ -3577,26 +3714,27 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
+ i915_verify_inactive(dev, __FILE__, __LINE__);
+
+ if (atomic_read(&dev_priv->mm.wedged)) {
+- DRM_ERROR("Execbuf while wedged\n");
+ mutex_unlock(&dev->struct_mutex);
+ ret = -EIO;
+ goto pre_mutex_err;
+ }
+
+ if (dev_priv->mm.suspended) {
+- DRM_ERROR("Execbuf while VT-switched.\n");
+ mutex_unlock(&dev->struct_mutex);
+ ret = -EBUSY;
+ goto pre_mutex_err;
+ }
+
+ /* Look up object handles */
++ flips = 0;
+ for (i = 0; i < args->buffer_count; i++) {
+ object_list[i] = drm_gem_object_lookup(dev, file_priv,
+ exec_list[i].handle);
+ if (object_list[i] == NULL) {
+ DRM_ERROR("Invalid object handle %d at index %d\n",
+ exec_list[i].handle, i);
++ /* prevent error path from reading uninitialized data */
++ args->buffer_count = i + 1;
+ ret = -EBADF;
+ goto err;
+ }
+@@ -3605,10 +3743,20 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
+ if (obj_priv->in_execbuffer) {
+ DRM_ERROR("Object %p appears more than once in object list\n",
+ object_list[i]);
++ /* prevent error path from reading uninitialized data */
++ args->buffer_count = i + 1;
+ ret = -EBADF;
+ goto err;
+ }
+ obj_priv->in_execbuffer = true;
++ flips += atomic_read(&obj_priv->pending_flip);
++ }
++
++ if (flips > 0) {
++ ret = i915_gem_wait_for_pending_flip(dev, object_list,
++ args->buffer_count);
++ if (ret)
++ goto err;
+ }
+
+ /* Pin and relocate */
+@@ -3710,16 +3858,23 @@ i915_gem_execbuffer(struct drm_device *dev, void *data,
+ i915_gem_flush(dev,
+ dev->invalidate_domains,
+ dev->flush_domains);
+- if (dev->flush_domains)
++ if (dev->flush_domains & I915_GEM_GPU_DOMAINS)
+ (void)i915_add_request(dev, file_priv,
+ dev->flush_domains);
+ }
+
+ for (i = 0; i < args->buffer_count; i++) {
+ struct drm_gem_object *obj = object_list[i];
++ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ uint32_t old_write_domain = obj->write_domain;
+
+ obj->write_domain = obj->pending_write_domain;
++ if (obj->write_domain)
++ list_move_tail(&obj_priv->gpu_write_list,
++ &dev_priv->mm.gpu_write_list);
++ else
++ list_del_init(&obj_priv->gpu_write_list);
++
+ trace_i915_gem_object_change_domain(obj,
+ obj->read_domains,
+ old_write_domain);
+@@ -3793,8 +3948,101 @@ err:
+
+ mutex_unlock(&dev->struct_mutex);
+
++pre_mutex_err:
++ /* Copy the updated relocations out regardless of current error
++ * state. Failure to update the relocs would mean that the next
++ * time userland calls execbuf, it would do so with presumed offset
++ * state that didn't match the actual object state.
++ */
++ ret2 = i915_gem_put_relocs_to_user(exec_list, args->buffer_count,
++ relocs);
++ if (ret2 != 0) {
++ DRM_ERROR("Failed to copy relocations back out: %d\n", ret2);
++
++ if (ret == 0)
++ ret = ret2;
++ }
++
++ drm_free_large(object_list);
++ kfree(cliprects);
++
++ return ret;
++}
++
++/*
++ * Legacy execbuffer just creates an exec2 list from the original exec object
++ * list array and passes it to the real function.
++ */
++int
++i915_gem_execbuffer(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_i915_gem_execbuffer *args = data;
++ struct drm_i915_gem_execbuffer2 exec2;
++ struct drm_i915_gem_exec_object *exec_list = NULL;
++ struct drm_i915_gem_exec_object2 *exec2_list = NULL;
++ int ret, i;
++
++#if WATCH_EXEC
++ DRM_INFO("buffers_ptr %d buffer_count %d len %08x\n",
++ (int) args->buffers_ptr, args->buffer_count, args->batch_len);
++#endif
++
++ if (args->buffer_count < 1) {
++ DRM_ERROR("execbuf with %d buffers\n", args->buffer_count);
++ return -EINVAL;
++ }
++
++ /* Copy in the exec list from userland */
++ exec_list = drm_malloc_ab(sizeof(*exec_list), args->buffer_count);
++ exec2_list = drm_malloc_ab(sizeof(*exec2_list), args->buffer_count);
++ if (exec_list == NULL || exec2_list == NULL) {
++ DRM_ERROR("Failed to allocate exec list for %d buffers\n",
++ args->buffer_count);
++ drm_free_large(exec_list);
++ drm_free_large(exec2_list);
++ return -ENOMEM;
++ }
++ ret = copy_from_user(exec_list,
++ (struct drm_i915_relocation_entry __user *)
++ (uintptr_t) args->buffers_ptr,
++ sizeof(*exec_list) * args->buffer_count);
++ if (ret != 0) {
++ DRM_ERROR("copy %d exec entries failed %d\n",
++ args->buffer_count, ret);
++ drm_free_large(exec_list);
++ drm_free_large(exec2_list);
++ return -EFAULT;
++ }
++
++ for (i = 0; i < args->buffer_count; i++) {
++ exec2_list[i].handle = exec_list[i].handle;
++ exec2_list[i].relocation_count = exec_list[i].relocation_count;
++ exec2_list[i].relocs_ptr = exec_list[i].relocs_ptr;
++ exec2_list[i].alignment = exec_list[i].alignment;
++ exec2_list[i].offset = exec_list[i].offset;
++ if (!IS_I965G(dev))
++ exec2_list[i].flags = EXEC_OBJECT_NEEDS_FENCE;
++ else
++ exec2_list[i].flags = 0;
++ }
++
++ exec2.buffers_ptr = args->buffers_ptr;
++ exec2.buffer_count = args->buffer_count;
++ exec2.batch_start_offset = args->batch_start_offset;
++ exec2.batch_len = args->batch_len;
++ exec2.DR1 = args->DR1;
++ exec2.DR4 = args->DR4;
++ exec2.num_cliprects = args->num_cliprects;
++ exec2.cliprects_ptr = args->cliprects_ptr;
++ exec2.flags = 0;
++
++ ret = i915_gem_do_execbuffer(dev, data, file_priv, &exec2, exec2_list);
+ if (!ret) {
+ /* Copy the new buffer offsets back to the user's exec list. */
++ for (i = 0; i < args->buffer_count; i++)
++ exec_list[i].offset = exec2_list[i].offset;
++ /* ... and back out to userspace */
+ ret = copy_to_user((struct drm_i915_relocation_entry __user *)
+ (uintptr_t) args->buffers_ptr,
+ exec_list,
+@@ -3807,25 +4055,62 @@ err:
+ }
+ }
+
+- /* Copy the updated relocations out regardless of current error
+- * state. Failure to update the relocs would mean that the next
+- * time userland calls execbuf, it would do so with presumed offset
+- * state that didn't match the actual object state.
+- */
+- ret2 = i915_gem_put_relocs_to_user(exec_list, args->buffer_count,
+- relocs);
+- if (ret2 != 0) {
+- DRM_ERROR("Failed to copy relocations back out: %d\n", ret2);
++ drm_free_large(exec_list);
++ drm_free_large(exec2_list);
++ return ret;
++}
+
+- if (ret == 0)
+- ret = ret2;
++int
++i915_gem_execbuffer2(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_i915_gem_execbuffer2 *args = data;
++ struct drm_i915_gem_exec_object2 *exec2_list = NULL;
++ int ret;
++
++#if WATCH_EXEC
++ DRM_INFO("buffers_ptr %d buffer_count %d len %08x\n",
++ (int) args->buffers_ptr, args->buffer_count, args->batch_len);
++#endif
++
++ if (args->buffer_count < 1) {
++ DRM_ERROR("execbuf2 with %d buffers\n", args->buffer_count);
++ return -EINVAL;
+ }
+
+-pre_mutex_err:
+- drm_free_large(object_list);
+- drm_free_large(exec_list);
+- kfree(cliprects);
++ exec2_list = drm_malloc_ab(sizeof(*exec2_list), args->buffer_count);
++ if (exec2_list == NULL) {
++ DRM_ERROR("Failed to allocate exec list for %d buffers\n",
++ args->buffer_count);
++ return -ENOMEM;
++ }
++ ret = copy_from_user(exec2_list,
++ (struct drm_i915_relocation_entry __user *)
++ (uintptr_t) args->buffers_ptr,
++ sizeof(*exec2_list) * args->buffer_count);
++ if (ret != 0) {
++ DRM_ERROR("copy %d exec entries failed %d\n",
++ args->buffer_count, ret);
++ drm_free_large(exec2_list);
++ return -EFAULT;
++ }
+
++ ret = i915_gem_do_execbuffer(dev, data, file_priv, args, exec2_list);
++ if (!ret) {
++ /* Copy the new buffer offsets back to the user's exec list. */
++ ret = copy_to_user((struct drm_i915_relocation_entry __user *)
++ (uintptr_t) args->buffers_ptr,
++ exec2_list,
++ sizeof(*exec2_list) * args->buffer_count);
++ if (ret) {
++ ret = -EFAULT;
++ DRM_ERROR("failed to copy %d exec entries "
++ "back to user (%d)\n",
++ args->buffer_count, ret);
++ }
++ }
++
++ drm_free_large(exec2_list);
+ return ret;
+ }
+
+@@ -3842,19 +4127,7 @@ i915_gem_object_pin(struct drm_gem_object *obj, uint32_t alignment)
+ if (ret)
+ return ret;
+ }
+- /*
+- * Pre-965 chips need a fence register set up in order to
+- * properly handle tiled surfaces.
+- */
+- if (!IS_I965G(dev) && obj_priv->tiling_mode != I915_TILING_NONE) {
+- ret = i915_gem_object_get_fence_reg(obj);
+- if (ret != 0) {
+- if (ret != -EBUSY && ret != -ERESTARTSYS)
+- DRM_ERROR("Failure to install fence: %d\n",
+- ret);
+- return ret;
+- }
+- }
++
+ obj_priv->pin_count++;
+
+ /* If the object is not active and not pending a flush,
+@@ -4112,6 +4385,7 @@ int i915_gem_init_object(struct drm_gem_object *obj)
+ obj_priv->obj = obj;
+ obj_priv->fence_reg = I915_FENCE_REG_NONE;
+ INIT_LIST_HEAD(&obj_priv->list);
++ INIT_LIST_HEAD(&obj_priv->gpu_write_list);
+ INIT_LIST_HEAD(&obj_priv->fence_list);
+ obj_priv->madv = I915_MADV_WILLNEED;
+
+@@ -4335,7 +4609,7 @@ i915_gem_init_hws(struct drm_device *dev)
+ memset(dev_priv->hw_status_page, 0, PAGE_SIZE);
+ I915_WRITE(HWS_PGA, dev_priv->status_gfx_addr);
+ I915_READ(HWS_PGA); /* posting read */
+- DRM_DEBUG("hws offset: 0x%08x\n", dev_priv->status_gfx_addr);
++ DRM_DEBUG_DRIVER("hws offset: 0x%08x\n", dev_priv->status_gfx_addr);
+
+ return 0;
+ }
+@@ -4563,6 +4837,7 @@ i915_gem_load(struct drm_device *dev)
+ spin_lock_init(&dev_priv->mm.active_list_lock);
+ INIT_LIST_HEAD(&dev_priv->mm.active_list);
+ INIT_LIST_HEAD(&dev_priv->mm.flushing_list);
++ INIT_LIST_HEAD(&dev_priv->mm.gpu_write_list);
+ INIT_LIST_HEAD(&dev_priv->mm.inactive_list);
+ INIT_LIST_HEAD(&dev_priv->mm.request_list);
+ INIT_LIST_HEAD(&dev_priv->mm.fence_list);
+@@ -4593,8 +4868,8 @@ i915_gem_load(struct drm_device *dev)
+ for (i = 0; i < 8; i++)
+ I915_WRITE(FENCE_REG_945_8 + (i * 4), 0);
+ }
+-
+ i915_gem_detect_bit_6_swizzle(dev);
++ init_waitqueue_head(&dev_priv->pending_flip_queue);
+ }
+
+ /*
+@@ -4769,7 +5044,7 @@ i915_gem_phys_pwrite(struct drm_device *dev, struct drm_gem_object *obj,
+ user_data = (char __user *) (uintptr_t) args->data_ptr;
+ obj_addr = obj_priv->phys_obj->handle->vaddr + args->offset;
+
+- DRM_DEBUG("obj_addr %p, %lld\n", obj_addr, args->size);
++ DRM_DEBUG_DRIVER("obj_addr %p, %lld\n", obj_addr, args->size);
+ ret = copy_from_user(obj_addr, user_data, args->size);
+ if (ret)
+ return -EFAULT;
+diff --git a/drivers/gpu/drm/i915/i915_gem_tiling.c b/drivers/gpu/drm/i915/i915_gem_tiling.c
+index 200e398..df278b2 100644
+--- a/drivers/gpu/drm/i915/i915_gem_tiling.c
++++ b/drivers/gpu/drm/i915/i915_gem_tiling.c
+@@ -121,7 +121,7 @@ intel_alloc_mchbar_resource(struct drm_device *dev)
+ 0, pcibios_align_resource,
+ dev_priv->bridge_dev);
+ if (ret) {
+- DRM_DEBUG("failed bus alloc: %d\n", ret);
++ DRM_DEBUG_DRIVER("failed bus alloc: %d\n", ret);
+ dev_priv->mch_res.start = 0;
+ goto out;
+ }
+@@ -209,8 +209,8 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
+ uint32_t swizzle_y = I915_BIT_6_SWIZZLE_UNKNOWN;
+ bool need_disable;
+
+- if (IS_IGDNG(dev)) {
+- /* On IGDNG whatever DRAM config, GPU always do
++ if (IS_IRONLAKE(dev)) {
++ /* On Ironlake whatever DRAM config, GPU always do
+ * same swizzling setup.
+ */
+ swizzle_x = I915_BIT_6_SWIZZLE_9_10;
+@@ -304,35 +304,39 @@ i915_gem_detect_bit_6_swizzle(struct drm_device *dev)
+
+
+ /**
+- * Returns the size of the fence for a tiled object of the given size.
++ * Returns whether an object is currently fenceable. If not, it may need
++ * to be unbound and have its pitch adjusted.
+ */
+-static int
+-i915_get_fence_size(struct drm_device *dev, int size)
++bool
++i915_obj_fenceable(struct drm_device *dev, struct drm_gem_object *obj)
+ {
+- int i;
+- int start;
++ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+
+ if (IS_I965G(dev)) {
+ /* The 965 can have fences at any page boundary. */
+- return ALIGN(size, 4096);
++ if (obj->size & 4095)
++ return false;
++ return true;
++ } else if (IS_I9XX(dev)) {
++ if (obj_priv->gtt_offset & ~I915_FENCE_START_MASK)
++ return false;
+ } else {
+- /* Align the size to a power of two greater than the smallest
+- * fence size.
+- */
+- if (IS_I9XX(dev))
+- start = 1024 * 1024;
+- else
+- start = 512 * 1024;
++ if (obj_priv->gtt_offset & ~I830_FENCE_START_MASK)
++ return false;
++ }
+
+- for (i = start; i < size; i <<= 1)
+- ;
++ /* Power of two sized... */
++ if (obj->size & (obj->size - 1))
++ return false;
+
+- return i;
+- }
++ /* Objects must be size aligned as well */
++ if (obj_priv->gtt_offset & (obj->size - 1))
++ return false;
++ return true;
+ }
+
+ /* Check pitch constriants for all chips & tiling formats */
+-static bool
++bool
+ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode)
+ {
+ int tile_width;
+@@ -384,12 +388,6 @@ i915_tiling_ok(struct drm_device *dev, int stride, int size, int tiling_mode)
+ if (stride & (stride - 1))
+ return false;
+
+- /* We don't 0handle the aperture area covered by the fence being bigger
+- * than the object size.
+- */
+- if (i915_get_fence_size(dev, size) != size)
+- return false;
+-
+ return true;
+ }
+
+diff --git a/drivers/gpu/drm/i915/i915_ioc32.c b/drivers/gpu/drm/i915/i915_ioc32.c
+index 1fe68a2..13b0289 100644
+--- a/drivers/gpu/drm/i915/i915_ioc32.c
++++ b/drivers/gpu/drm/i915/i915_ioc32.c
+@@ -66,8 +66,7 @@ static int compat_i915_batchbuffer(struct file *file, unsigned int cmd,
+ &batchbuffer->cliprects))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_I915_BATCHBUFFER,
++ return drm_ioctl(file, DRM_IOCTL_I915_BATCHBUFFER,
+ (unsigned long)batchbuffer);
+ }
+
+@@ -102,8 +101,8 @@ static int compat_i915_cmdbuffer(struct file *file, unsigned int cmd,
+ &cmdbuffer->cliprects))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_I915_CMDBUFFER, (unsigned long)cmdbuffer);
++ return drm_ioctl(file, DRM_IOCTL_I915_CMDBUFFER,
++ (unsigned long)cmdbuffer);
+ }
+
+ typedef struct drm_i915_irq_emit32 {
+@@ -125,8 +124,8 @@ static int compat_i915_irq_emit(struct file *file, unsigned int cmd,
+ &request->irq_seq))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_I915_IRQ_EMIT, (unsigned long)request);
++ return drm_ioctl(file, DRM_IOCTL_I915_IRQ_EMIT,
++ (unsigned long)request);
+ }
+ typedef struct drm_i915_getparam32 {
+ int param;
+@@ -149,8 +148,8 @@ static int compat_i915_getparam(struct file *file, unsigned int cmd,
+ &request->value))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_I915_GETPARAM, (unsigned long)request);
++ return drm_ioctl(file, DRM_IOCTL_I915_GETPARAM,
++ (unsigned long)request);
+ }
+
+ typedef struct drm_i915_mem_alloc32 {
+@@ -178,8 +177,8 @@ static int compat_i915_alloc(struct file *file, unsigned int cmd,
+ &request->region_offset))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_I915_ALLOC, (unsigned long)request);
++ return drm_ioctl(file, DRM_IOCTL_I915_ALLOC,
++ (unsigned long)request);
+ }
+
+ drm_ioctl_compat_t *i915_compat_ioctls[] = {
+@@ -211,12 +210,10 @@ long i915_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+ if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(i915_compat_ioctls))
+ fn = i915_compat_ioctls[nr - DRM_COMMAND_BASE];
+
+- lock_kernel(); /* XXX for now */
+ if (fn != NULL)
+ ret = (*fn) (filp, cmd, arg);
+ else
+- ret = drm_ioctl(filp->f_path.dentry->d_inode, filp, cmd, arg);
+- unlock_kernel();
++ ret = drm_ioctl(filp, cmd, arg);
+
+ return ret;
+ }
+diff --git a/drivers/gpu/drm/i915/i915_irq.c b/drivers/gpu/drm/i915/i915_irq.c
+index 63f28ad..a17d6bd 100644
+--- a/drivers/gpu/drm/i915/i915_irq.c
++++ b/drivers/gpu/drm/i915/i915_irq.c
+@@ -43,10 +43,13 @@
+ * we leave them always unmasked in IMR and then control enabling them through
+ * PIPESTAT alone.
+ */
+-#define I915_INTERRUPT_ENABLE_FIX (I915_ASLE_INTERRUPT | \
+- I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \
+- I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | \
+- I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
++#define I915_INTERRUPT_ENABLE_FIX \
++ (I915_ASLE_INTERRUPT | \
++ I915_DISPLAY_PIPE_A_EVENT_INTERRUPT | \
++ I915_DISPLAY_PIPE_B_EVENT_INTERRUPT | \
++ I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT | \
++ I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT | \
++ I915_RENDER_COMMAND_PARSER_ERROR_INTERRUPT)
+
+ /** Interrupts that we mask and unmask at runtime. */
+ #define I915_INTERRUPT_ENABLE_VAR (I915_USER_INTERRUPT)
+@@ -61,7 +64,7 @@
+ DRM_I915_VBLANK_PIPE_B)
+
+ void
+-igdng_enable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
++ironlake_enable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
+ {
+ if ((dev_priv->gt_irq_mask_reg & mask) != 0) {
+ dev_priv->gt_irq_mask_reg &= ~mask;
+@@ -71,7 +74,7 @@ igdng_enable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
+ }
+
+ static inline void
+-igdng_disable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
++ironlake_disable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
+ {
+ if ((dev_priv->gt_irq_mask_reg & mask) != mask) {
+ dev_priv->gt_irq_mask_reg |= mask;
+@@ -82,7 +85,7 @@ igdng_disable_graphics_irq(drm_i915_private_t *dev_priv, u32 mask)
+
+ /* For display hotplug interrupt */
+ void
+-igdng_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
++ironlake_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
+ {
+ if ((dev_priv->irq_mask_reg & mask) != 0) {
+ dev_priv->irq_mask_reg &= ~mask;
+@@ -92,7 +95,7 @@ igdng_enable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
+ }
+
+ static inline void
+-igdng_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
++ironlake_disable_display_irq(drm_i915_private_t *dev_priv, u32 mask)
+ {
+ if ((dev_priv->irq_mask_reg & mask) != mask) {
+ dev_priv->irq_mask_reg |= mask;
+@@ -157,6 +160,20 @@ i915_disable_pipestat(drm_i915_private_t *dev_priv, int pipe, u32 mask)
+ }
+
+ /**
++ * intel_enable_asle - enable ASLE interrupt for OpRegion
++ */
++void intel_enable_asle (struct drm_device *dev)
++{
++ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
++
++ if (IS_IRONLAKE(dev))
++ ironlake_enable_display_irq(dev_priv, DE_GSE);
++ else
++ i915_enable_pipestat(dev_priv, 1,
++ I915_LEGACY_BLC_EVENT_ENABLE);
++}
++
++/**
+ * i915_pipe_enabled - check if a pipe is enabled
+ * @dev: DRM device
+ * @pipe: pipe to check
+@@ -191,7 +208,8 @@ u32 i915_get_vblank_counter(struct drm_device *dev, int pipe)
+ low_frame = pipe ? PIPEBFRAMEPIXEL : PIPEAFRAMEPIXEL;
+
+ if (!i915_pipe_enabled(dev, pipe)) {
+- DRM_DEBUG("trying to get vblank count for disabled pipe %d\n", pipe);
++ DRM_DEBUG_DRIVER("trying to get vblank count for disabled "
++ "pipe %d\n", pipe);
+ return 0;
+ }
+
+@@ -220,7 +238,8 @@ u32 gm45_get_vblank_counter(struct drm_device *dev, int pipe)
+ int reg = pipe ? PIPEB_FRMCOUNT_GM45 : PIPEA_FRMCOUNT_GM45;
+
+ if (!i915_pipe_enabled(dev, pipe)) {
+- DRM_DEBUG("trying to get vblank count for disabled pipe %d\n", pipe);
++ DRM_DEBUG_DRIVER("trying to get vblank count for disabled "
++ "pipe %d\n", pipe);
+ return 0;
+ }
+
+@@ -250,11 +269,11 @@ static void i915_hotplug_work_func(struct work_struct *work)
+ drm_sysfs_hotplug_event(dev);
+ }
+
+-irqreturn_t igdng_irq_handler(struct drm_device *dev)
++irqreturn_t ironlake_irq_handler(struct drm_device *dev)
+ {
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ int ret = IRQ_NONE;
+- u32 de_iir, gt_iir, de_ier;
++ u32 de_iir, gt_iir, de_ier, pch_iir;
+ struct drm_i915_master_private *master_priv;
+
+ /* disable master interrupt before clearing iir */
+@@ -264,8 +283,9 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev)
+
+ de_iir = I915_READ(DEIIR);
+ gt_iir = I915_READ(GTIIR);
++ pch_iir = I915_READ(SDEIIR);
+
+- if (de_iir == 0 && gt_iir == 0)
++ if (de_iir == 0 && gt_iir == 0 && pch_iir == 0)
+ goto done;
+
+ ret = IRQ_HANDLED;
+@@ -286,6 +306,33 @@ irqreturn_t igdng_irq_handler(struct drm_device *dev)
+ mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD);
+ }
+
++ if (de_iir & DE_GSE)
++ ironlake_opregion_gse_intr(dev);
++
++ if (de_iir & DE_PLANEA_FLIP_DONE) {
++ intel_prepare_page_flip(dev, 0);
++ intel_finish_page_flip(dev, 0);
++ }
++
++ if (de_iir & DE_PLANEB_FLIP_DONE) {
++ intel_prepare_page_flip(dev, 1);
++ intel_finish_page_flip(dev, 1);
++ }
++
++ if (de_iir & DE_PIPEA_VBLANK)
++ drm_handle_vblank(dev, 0);
++
++ if (de_iir & DE_PIPEB_VBLANK)
++ drm_handle_vblank(dev, 1);
++
++ /* check event from PCH */
++ if ((de_iir & DE_PCH_EVENT) &&
++ (pch_iir & SDE_HOTPLUG_MASK)) {
++ queue_work(dev_priv->wq, &dev_priv->hotplug_work);
++ }
++
++ /* should clear PCH hotplug event before clear CPU irq */
++ I915_WRITE(SDEIIR, pch_iir);
+ I915_WRITE(GTIIR, gt_iir);
+ I915_WRITE(DEIIR, de_iir);
+
+@@ -312,19 +359,19 @@ static void i915_error_work_func(struct work_struct *work)
+ char *reset_event[] = { "RESET=1", NULL };
+ char *reset_done_event[] = { "ERROR=0", NULL };
+
+- DRM_DEBUG("generating error event\n");
++ DRM_DEBUG_DRIVER("generating error event\n");
+ kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, error_event);
+
+ if (atomic_read(&dev_priv->mm.wedged)) {
+ if (IS_I965G(dev)) {
+- DRM_DEBUG("resetting chip\n");
++ DRM_DEBUG_DRIVER("resetting chip\n");
+ kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_event);
+ if (!i965_reset(dev, GDRST_RENDER)) {
+ atomic_set(&dev_priv->mm.wedged, 0);
+ kobject_uevent_env(&dev->primary->kdev.kobj, KOBJ_CHANGE, reset_done_event);
+ }
+ } else {
+- printk("reboot required\n");
++ DRM_DEBUG_DRIVER("reboot required\n");
+ }
+ }
+ }
+@@ -350,7 +397,7 @@ static void i915_capture_error_state(struct drm_device *dev)
+
+ error = kmalloc(sizeof(*error), GFP_ATOMIC);
+ if (!error) {
+- DRM_DEBUG("out ot memory, not capturing error state\n");
++ DRM_DEBUG_DRIVER("out ot memory, not capturing error state\n");
+ goto out;
+ }
+
+@@ -507,7 +554,6 @@ static void i915_handle_error(struct drm_device *dev, bool wedged)
+ /*
+ * Wakeup waiting processes so they don't hang
+ */
+- printk("i915: Waking up sleeping processes\n");
+ DRM_WAKEUP(&dev_priv->irq_queue);
+ }
+
+@@ -530,8 +576,8 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
+
+ atomic_inc(&dev_priv->irq_received);
+
+- if (IS_IGDNG(dev))
+- return igdng_irq_handler(dev);
++ if (IS_IRONLAKE(dev))
++ return ironlake_irq_handler(dev);
+
+ iir = I915_READ(IIR);
+
+@@ -563,14 +609,14 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
+ */
+ if (pipea_stats & 0x8000ffff) {
+ if (pipea_stats & PIPE_FIFO_UNDERRUN_STATUS)
+- DRM_DEBUG("pipe a underrun\n");
++ DRM_DEBUG_DRIVER("pipe a underrun\n");
+ I915_WRITE(PIPEASTAT, pipea_stats);
+ irq_received = 1;
+ }
+
+ if (pipeb_stats & 0x8000ffff) {
+ if (pipeb_stats & PIPE_FIFO_UNDERRUN_STATUS)
+- DRM_DEBUG("pipe b underrun\n");
++ DRM_DEBUG_DRIVER("pipe b underrun\n");
+ I915_WRITE(PIPEBSTAT, pipeb_stats);
+ irq_received = 1;
+ }
+@@ -586,7 +632,7 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
+ (iir & I915_DISPLAY_PORT_INTERRUPT)) {
+ u32 hotplug_status = I915_READ(PORT_HOTPLUG_STAT);
+
+- DRM_DEBUG("hotplug event received, stat 0x%08x\n",
++ DRM_DEBUG_DRIVER("hotplug event received, stat 0x%08x\n",
+ hotplug_status);
+ if (hotplug_status & dev_priv->hotplug_supported_mask)
+ queue_work(dev_priv->wq,
+@@ -594,27 +640,6 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
+
+ I915_WRITE(PORT_HOTPLUG_STAT, hotplug_status);
+ I915_READ(PORT_HOTPLUG_STAT);
+-
+- /* EOS interrupts occurs */
+- if (IS_IGD(dev) &&
+- (hotplug_status & CRT_EOS_INT_STATUS)) {
+- u32 temp;
+-
+- DRM_DEBUG("EOS interrupt occurs\n");
+- /* status is already cleared */
+- temp = I915_READ(ADPA);
+- temp &= ~ADPA_DAC_ENABLE;
+- I915_WRITE(ADPA, temp);
+-
+- temp = I915_READ(PORT_HOTPLUG_EN);
+- temp &= ~CRT_EOS_INT_EN;
+- I915_WRITE(PORT_HOTPLUG_EN, temp);
+-
+- temp = I915_READ(PORT_HOTPLUG_STAT);
+- if (temp & CRT_EOS_INT_STATUS)
+- I915_WRITE(PORT_HOTPLUG_STAT,
+- CRT_EOS_INT_STATUS);
+- }
+ }
+
+ I915_WRITE(IIR, iir);
+@@ -636,14 +661,22 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
+ mod_timer(&dev_priv->hangcheck_timer, jiffies + DRM_I915_HANGCHECK_PERIOD);
+ }
+
++ if (iir & I915_DISPLAY_PLANE_A_FLIP_PENDING_INTERRUPT)
++ intel_prepare_page_flip(dev, 0);
++
++ if (iir & I915_DISPLAY_PLANE_B_FLIP_PENDING_INTERRUPT)
++ intel_prepare_page_flip(dev, 1);
++
+ if (pipea_stats & vblank_status) {
+ vblank++;
+ drm_handle_vblank(dev, 0);
++ intel_finish_page_flip(dev, 0);
+ }
+
+ if (pipeb_stats & vblank_status) {
+ vblank++;
+ drm_handle_vblank(dev, 1);
++ intel_finish_page_flip(dev, 1);
+ }
+
+ if ((pipeb_stats & I915_LEGACY_BLC_EVENT_STATUS) ||
+@@ -679,7 +712,7 @@ static int i915_emit_irq(struct drm_device * dev)
+
+ i915_kernel_lost_context(dev);
+
+- DRM_DEBUG("\n");
++ DRM_DEBUG_DRIVER("\n");
+
+ dev_priv->counter++;
+ if (dev_priv->counter > 0x7FFFFFFFUL)
+@@ -704,8 +737,8 @@ void i915_user_irq_get(struct drm_device *dev)
+
+ spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
+ if (dev->irq_enabled && (++dev_priv->user_irq_refcount == 1)) {
+- if (IS_IGDNG(dev))
+- igdng_enable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
++ if (IS_IRONLAKE(dev))
++ ironlake_enable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
+ else
+ i915_enable_irq(dev_priv, I915_USER_INTERRUPT);
+ }
+@@ -720,8 +753,8 @@ void i915_user_irq_put(struct drm_device *dev)
+ spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
+ BUG_ON(dev->irq_enabled && dev_priv->user_irq_refcount <= 0);
+ if (dev->irq_enabled && (--dev_priv->user_irq_refcount == 0)) {
+- if (IS_IGDNG(dev))
+- igdng_disable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
++ if (IS_IRONLAKE(dev))
++ ironlake_disable_graphics_irq(dev_priv, GT_USER_INTERRUPT);
+ else
+ i915_disable_irq(dev_priv, I915_USER_INTERRUPT);
+ }
+@@ -744,7 +777,7 @@ static int i915_wait_irq(struct drm_device * dev, int irq_nr)
+ struct drm_i915_master_private *master_priv = dev->primary->master->driver_priv;
+ int ret = 0;
+
+- DRM_DEBUG("irq_nr=%d breadcrumb=%d\n", irq_nr,
++ DRM_DEBUG_DRIVER("irq_nr=%d breadcrumb=%d\n", irq_nr,
+ READ_BREADCRUMB(dev_priv));
+
+ if (READ_BREADCRUMB(dev_priv) >= irq_nr) {
+@@ -827,11 +860,11 @@ int i915_enable_vblank(struct drm_device *dev, int pipe)
+ if (!(pipeconf & PIPEACONF_ENABLE))
+ return -EINVAL;
+
+- if (IS_IGDNG(dev))
+- return 0;
+-
+ spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
+- if (IS_I965G(dev))
++ if (IS_IRONLAKE(dev))
++ ironlake_enable_display_irq(dev_priv, (pipe == 0) ?
++ DE_PIPEA_VBLANK: DE_PIPEB_VBLANK);
++ else if (IS_I965G(dev))
+ i915_enable_pipestat(dev_priv, pipe,
+ PIPE_START_VBLANK_INTERRUPT_ENABLE);
+ else
+@@ -849,13 +882,14 @@ void i915_disable_vblank(struct drm_device *dev, int pipe)
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ unsigned long irqflags;
+
+- if (IS_IGDNG(dev))
+- return;
+-
+ spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
+- i915_disable_pipestat(dev_priv, pipe,
+- PIPE_VBLANK_INTERRUPT_ENABLE |
+- PIPE_START_VBLANK_INTERRUPT_ENABLE);
++ if (IS_IRONLAKE(dev))
++ ironlake_disable_display_irq(dev_priv, (pipe == 0) ?
++ DE_PIPEA_VBLANK: DE_PIPEB_VBLANK);
++ else
++ i915_disable_pipestat(dev_priv, pipe,
++ PIPE_VBLANK_INTERRUPT_ENABLE |
++ PIPE_START_VBLANK_INTERRUPT_ENABLE);
+ spin_unlock_irqrestore(&dev_priv->user_irq_lock, irqflags);
+ }
+
+@@ -863,7 +897,7 @@ void i915_enable_interrupt (struct drm_device *dev)
+ {
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+- if (!IS_IGDNG(dev))
++ if (!IS_IRONLAKE(dev))
+ opregion_enable_asle(dev);
+ dev_priv->irq_enabled = 1;
+ }
+@@ -971,7 +1005,7 @@ void i915_hangcheck_elapsed(unsigned long data)
+
+ /* drm_dma.h hooks
+ */
+-static void igdng_irq_preinstall(struct drm_device *dev)
++static void ironlake_irq_preinstall(struct drm_device *dev)
+ {
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+
+@@ -987,17 +1021,25 @@ static void igdng_irq_preinstall(struct drm_device *dev)
+ I915_WRITE(GTIMR, 0xffffffff);
+ I915_WRITE(GTIER, 0x0);
+ (void) I915_READ(GTIER);
++
++ /* south display irq */
++ I915_WRITE(SDEIMR, 0xffffffff);
++ I915_WRITE(SDEIER, 0x0);
++ (void) I915_READ(SDEIER);
+ }
+
+-static int igdng_irq_postinstall(struct drm_device *dev)
++static int ironlake_irq_postinstall(struct drm_device *dev)
+ {
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ /* enable kind of interrupts always enabled */
+- u32 display_mask = DE_MASTER_IRQ_CONTROL /*| DE_PCH_EVENT */;
++ u32 display_mask = DE_MASTER_IRQ_CONTROL | DE_GSE | DE_PCH_EVENT |
++ DE_PLANEA_FLIP_DONE | DE_PLANEB_FLIP_DONE;
+ u32 render_mask = GT_USER_INTERRUPT;
++ u32 hotplug_mask = SDE_CRT_HOTPLUG | SDE_PORTB_HOTPLUG |
++ SDE_PORTC_HOTPLUG | SDE_PORTD_HOTPLUG;
+
+ dev_priv->irq_mask_reg = ~display_mask;
+- dev_priv->de_irq_enable_reg = display_mask;
++ dev_priv->de_irq_enable_reg = display_mask | DE_PIPEA_VBLANK | DE_PIPEB_VBLANK;
+
+ /* should always can generate irq */
+ I915_WRITE(DEIIR, I915_READ(DEIIR));
+@@ -1014,6 +1056,14 @@ static int igdng_irq_postinstall(struct drm_device *dev)
+ I915_WRITE(GTIER, dev_priv->gt_irq_enable_reg);
+ (void) I915_READ(GTIER);
+
++ dev_priv->pch_irq_mask_reg = ~hotplug_mask;
++ dev_priv->pch_irq_enable_reg = hotplug_mask;
++
++ I915_WRITE(SDEIIR, I915_READ(SDEIIR));
++ I915_WRITE(SDEIMR, dev_priv->pch_irq_mask_reg);
++ I915_WRITE(SDEIER, dev_priv->pch_irq_enable_reg);
++ (void) I915_READ(SDEIER);
++
+ return 0;
+ }
+
+@@ -1026,8 +1076,8 @@ void i915_driver_irq_preinstall(struct drm_device * dev)
+ INIT_WORK(&dev_priv->hotplug_work, i915_hotplug_work_func);
+ INIT_WORK(&dev_priv->error_work, i915_error_work_func);
+
+- if (IS_IGDNG(dev)) {
+- igdng_irq_preinstall(dev);
++ if (IS_IRONLAKE(dev)) {
++ ironlake_irq_preinstall(dev);
+ return;
+ }
+
+@@ -1058,8 +1108,8 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
+
+ dev_priv->vblank_pipe = DRM_I915_VBLANK_PIPE_A | DRM_I915_VBLANK_PIPE_B;
+
+- if (IS_IGDNG(dev))
+- return igdng_irq_postinstall(dev);
++ if (IS_IRONLAKE(dev))
++ return ironlake_irq_postinstall(dev);
+
+ /* Unmask the interrupts that we always want on. */
+ dev_priv->irq_mask_reg = ~I915_INTERRUPT_ENABLE_FIX;
+@@ -1123,7 +1173,7 @@ int i915_driver_irq_postinstall(struct drm_device *dev)
+ return 0;
+ }
+
+-static void igdng_irq_uninstall(struct drm_device *dev)
++static void ironlake_irq_uninstall(struct drm_device *dev)
+ {
+ drm_i915_private_t *dev_priv = (drm_i915_private_t *) dev->dev_private;
+ I915_WRITE(HWSTAM, 0xffffffff);
+@@ -1146,8 +1196,8 @@ void i915_driver_irq_uninstall(struct drm_device * dev)
+
+ dev_priv->vblank_pipe = 0;
+
+- if (IS_IGDNG(dev)) {
+- igdng_irq_uninstall(dev);
++ if (IS_IRONLAKE(dev)) {
++ ironlake_irq_uninstall(dev);
+ return;
+ }
+
+diff --git a/drivers/gpu/drm/i915/i915_opregion.c b/drivers/gpu/drm/i915/i915_opregion.c
+index 2d51935..7cc8410 100644
+--- a/drivers/gpu/drm/i915/i915_opregion.c
++++ b/drivers/gpu/drm/i915/i915_opregion.c
+@@ -118,6 +118,10 @@ struct opregion_asle {
+ #define ASLE_BACKLIGHT_FAIL (2<<12)
+ #define ASLE_PFIT_FAIL (2<<14)
+ #define ASLE_PWM_FREQ_FAIL (2<<16)
++#define ASLE_ALS_ILLUM_FAILED (1<<10)
++#define ASLE_BACKLIGHT_FAILED (1<<12)
++#define ASLE_PFIT_FAILED (1<<14)
++#define ASLE_PWM_FREQ_FAILED (1<<16)
+
+ /* ASLE backlight brightness to set */
+ #define ASLE_BCLP_VALID (1<<31)
+@@ -163,7 +167,7 @@ static u32 asle_set_backlight(struct drm_device *dev, u32 bclp)
+ if (IS_I965G(dev) && (blc_pwm_ctl2 & BLM_COMBINATION_MODE))
+ pci_write_config_dword(dev->pdev, PCI_LBPC, bclp);
+ else {
+- if (IS_IGD(dev)) {
++ if (IS_PINEVIEW(dev)) {
+ blc_pwm_ctl &= ~(BACKLIGHT_DUTY_CYCLE_MASK - 1);
+ max_backlight = (blc_pwm_ctl & BACKLIGHT_MODULATION_FREQ_MASK) >>
+ BACKLIGHT_MODULATION_FREQ_SHIFT;
+@@ -224,7 +228,7 @@ void opregion_asle_intr(struct drm_device *dev)
+ asle_req = asle->aslc & ASLE_REQ_MSK;
+
+ if (!asle_req) {
+- DRM_DEBUG("non asle set request??\n");
++ DRM_DEBUG_DRIVER("non asle set request??\n");
+ return;
+ }
+
+@@ -243,6 +247,73 @@ void opregion_asle_intr(struct drm_device *dev)
+ asle->aslc = asle_stat;
+ }
+
++static u32 asle_set_backlight_ironlake(struct drm_device *dev, u32 bclp)
++{
++ struct drm_i915_private *dev_priv = dev->dev_private;
++ struct opregion_asle *asle = dev_priv->opregion.asle;
++ u32 cpu_pwm_ctl, pch_pwm_ctl2;
++ u32 max_backlight, level;
++
++ if (!(bclp & ASLE_BCLP_VALID))
++ return ASLE_BACKLIGHT_FAILED;
++
++ bclp &= ASLE_BCLP_MSK;
++ if (bclp < 0 || bclp > 255)
++ return ASLE_BACKLIGHT_FAILED;
++
++ cpu_pwm_ctl = I915_READ(BLC_PWM_CPU_CTL);
++ pch_pwm_ctl2 = I915_READ(BLC_PWM_PCH_CTL2);
++ /* get the max PWM frequency */
++ max_backlight = (pch_pwm_ctl2 >> 16) & BACKLIGHT_DUTY_CYCLE_MASK;
++ /* calculate the expected PMW frequency */
++ level = (bclp * max_backlight) / 255;
++ /* reserve the high 16 bits */
++ cpu_pwm_ctl &= ~(BACKLIGHT_DUTY_CYCLE_MASK);
++ /* write the updated PWM frequency */
++ I915_WRITE(BLC_PWM_CPU_CTL, cpu_pwm_ctl | level);
++
++ asle->cblv = (bclp*0x64)/0xff | ASLE_CBLV_VALID;
++
++ return 0;
++}
++
++void ironlake_opregion_gse_intr(struct drm_device *dev)
++{
++ struct drm_i915_private *dev_priv = dev->dev_private;
++ struct opregion_asle *asle = dev_priv->opregion.asle;
++ u32 asle_stat = 0;
++ u32 asle_req;
++
++ if (!asle)
++ return;
++
++ asle_req = asle->aslc & ASLE_REQ_MSK;
++
++ if (!asle_req) {
++ DRM_DEBUG_DRIVER("non asle set request??\n");
++ return;
++ }
++
++ if (asle_req & ASLE_SET_ALS_ILLUM) {
++ DRM_DEBUG_DRIVER("Illum is not supported\n");
++ asle_stat |= ASLE_ALS_ILLUM_FAILED;
++ }
++
++ if (asle_req & ASLE_SET_BACKLIGHT)
++ asle_stat |= asle_set_backlight_ironlake(dev, asle->bclp);
++
++ if (asle_req & ASLE_SET_PFIT) {
++ DRM_DEBUG_DRIVER("Pfit is not supported\n");
++ asle_stat |= ASLE_PFIT_FAILED;
++ }
++
++ if (asle_req & ASLE_SET_PWM_FREQ) {
++ DRM_DEBUG_DRIVER("PWM freq is not supported\n");
++ asle_stat |= ASLE_PWM_FREQ_FAILED;
++ }
++
++ asle->aslc = asle_stat;
++}
+ #define ASLE_ALS_EN (1<<0)
+ #define ASLE_BLC_EN (1<<1)
+ #define ASLE_PFIT_EN (1<<2)
+@@ -258,8 +329,7 @@ void opregion_enable_asle(struct drm_device *dev)
+ unsigned long irqflags;
+
+ spin_lock_irqsave(&dev_priv->user_irq_lock, irqflags);
+- i915_enable_pipestat(dev_priv, 1,
+- I915_LEGACY_BLC_EVENT_ENABLE);
++ intel_enable_asle(dev);
+ spin_unlock_irqrestore(&dev_priv->user_irq_lock,
+ irqflags);
+ }
+@@ -361,9 +431,9 @@ int intel_opregion_init(struct drm_device *dev, int resume)
+ int err = 0;
+
+ pci_read_config_dword(dev->pdev, PCI_ASLS, &asls);
+- DRM_DEBUG("graphic opregion physical addr: 0x%x\n", asls);
++ DRM_DEBUG_DRIVER("graphic opregion physical addr: 0x%x\n", asls);
+ if (asls == 0) {
+- DRM_DEBUG("ACPI OpRegion not supported!\n");
++ DRM_DEBUG_DRIVER("ACPI OpRegion not supported!\n");
+ return -ENOTSUPP;
+ }
+
+@@ -373,30 +443,30 @@ int intel_opregion_init(struct drm_device *dev, int resume)
+
+ opregion->header = base;
+ if (memcmp(opregion->header->signature, OPREGION_SIGNATURE, 16)) {
+- DRM_DEBUG("opregion signature mismatch\n");
++ DRM_DEBUG_DRIVER("opregion signature mismatch\n");
+ err = -EINVAL;
+ goto err_out;
+ }
+
+ mboxes = opregion->header->mboxes;
+ if (mboxes & MBOX_ACPI) {
+- DRM_DEBUG("Public ACPI methods supported\n");
++ DRM_DEBUG_DRIVER("Public ACPI methods supported\n");
+ opregion->acpi = base + OPREGION_ACPI_OFFSET;
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ intel_didl_outputs(dev);
+ } else {
+- DRM_DEBUG("Public ACPI methods not supported\n");
++ DRM_DEBUG_DRIVER("Public ACPI methods not supported\n");
+ err = -ENOTSUPP;
+ goto err_out;
+ }
+ opregion->enabled = 1;
+
+ if (mboxes & MBOX_SWSCI) {
+- DRM_DEBUG("SWSCI supported\n");
++ DRM_DEBUG_DRIVER("SWSCI supported\n");
+ opregion->swsci = base + OPREGION_SWSCI_OFFSET;
+ }
+ if (mboxes & MBOX_ASLE) {
+- DRM_DEBUG("ASLE supported\n");
++ DRM_DEBUG_DRIVER("ASLE supported\n");
+ opregion->asle = base + OPREGION_ASLE_OFFSET;
+ opregion_enable_asle(dev);
+ }
+diff --git a/drivers/gpu/drm/i915/i915_reg.h b/drivers/gpu/drm/i915/i915_reg.h
+index fd537f4..ab1bd2d 100644
+--- a/drivers/gpu/drm/i915/i915_reg.h
++++ b/drivers/gpu/drm/i915/i915_reg.h
+@@ -140,6 +140,7 @@
+ #define MI_NOOP MI_INSTR(0, 0)
+ #define MI_USER_INTERRUPT MI_INSTR(0x02, 0)
+ #define MI_WAIT_FOR_EVENT MI_INSTR(0x03, 0)
++#define MI_WAIT_FOR_OVERLAY_FLIP (1<<16)
+ #define MI_WAIT_FOR_PLANE_B_FLIP (1<<6)
+ #define MI_WAIT_FOR_PLANE_A_FLIP (1<<2)
+ #define MI_WAIT_FOR_PLANE_A_SCANLINES (1<<1)
+@@ -151,7 +152,13 @@
+ #define MI_END_SCENE (1 << 4) /* flush binner and incr scene count */
+ #define MI_BATCH_BUFFER_END MI_INSTR(0x0a, 0)
+ #define MI_REPORT_HEAD MI_INSTR(0x07, 0)
++#define MI_OVERLAY_FLIP MI_INSTR(0x11,0)
++#define MI_OVERLAY_CONTINUE (0x0<<21)
++#define MI_OVERLAY_ON (0x1<<21)
++#define MI_OVERLAY_OFF (0x2<<21)
+ #define MI_LOAD_SCAN_LINES_INCL MI_INSTR(0x12, 0)
++#define MI_DISPLAY_FLIP MI_INSTR(0x14, 2)
++#define MI_DISPLAY_FLIP_PLANE(n) ((n) << 20)
+ #define MI_STORE_DWORD_IMM MI_INSTR(0x20, 1)
+ #define MI_MEM_VIRTUAL (1 << 22) /* 965+ only */
+ #define MI_STORE_DWORD_INDEX MI_INSTR(0x21, 1)
+@@ -260,6 +267,8 @@
+ #define HWS_PGA 0x02080
+ #define HWS_ADDRESS_MASK 0xfffff000
+ #define HWS_START_ADDRESS_SHIFT 4
++#define PWRCTXA 0x2088 /* 965GM+ only */
++#define PWRCTX_EN (1<<0)
+ #define IPEIR 0x02088
+ #define IPEHR 0x0208c
+ #define INSTDONE 0x02090
+@@ -329,6 +338,7 @@
+ #define FBC_CTL_PERIODIC (1<<30)
+ #define FBC_CTL_INTERVAL_SHIFT (16)
+ #define FBC_CTL_UNCOMPRESSIBLE (1<<14)
++#define FBC_C3_IDLE (1<<13)
+ #define FBC_CTL_STRIDE_SHIFT (5)
+ #define FBC_CTL_FENCENO (1<<0)
+ #define FBC_COMMAND 0x0320c
+@@ -405,6 +415,13 @@
+ # define GPIO_DATA_VAL_IN (1 << 12)
+ # define GPIO_DATA_PULLUP_DISABLE (1 << 13)
+
++#define GMBUS0 0x5100
++#define GMBUS1 0x5104
++#define GMBUS2 0x5108
++#define GMBUS3 0x510c
++#define GMBUS4 0x5110
++#define GMBUS5 0x5120
++
+ /*
+ * Clock control & power management
+ */
+@@ -435,7 +452,7 @@
+ #define DPLLB_LVDS_P2_CLOCK_DIV_7 (1 << 24) /* i915 */
+ #define DPLL_P2_CLOCK_DIV_MASK 0x03000000 /* i915 */
+ #define DPLL_FPA01_P1_POST_DIV_MASK 0x00ff0000 /* i915 */
+-#define DPLL_FPA01_P1_POST_DIV_MASK_IGD 0x00ff8000 /* IGD */
++#define DPLL_FPA01_P1_POST_DIV_MASK_PINEVIEW 0x00ff8000 /* Pineview */
+
+ #define I915_FIFO_UNDERRUN_STATUS (1UL<<31)
+ #define I915_CRC_ERROR_ENABLE (1UL<<29)
+@@ -512,7 +529,7 @@
+ */
+ #define DPLL_FPA01_P1_POST_DIV_MASK_I830_LVDS 0x003f0000
+ #define DPLL_FPA01_P1_POST_DIV_SHIFT 16
+-#define DPLL_FPA01_P1_POST_DIV_SHIFT_IGD 15
++#define DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW 15
+ /* i830, required in DVO non-gang */
+ #define PLL_P2_DIVIDE_BY_4 (1 << 23)
+ #define PLL_P1_DIVIDE_BY_TWO (1 << 21) /* i830 */
+@@ -522,7 +539,7 @@
+ #define PLLB_REF_INPUT_SPREADSPECTRUMIN (3 << 13)
+ #define PLL_REF_INPUT_MASK (3 << 13)
+ #define PLL_LOAD_PULSE_PHASE_SHIFT 9
+-/* IGDNG */
++/* Ironlake */
+ # define PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT 9
+ # define PLL_REF_SDVO_HDMI_MULTIPLIER_MASK (7 << 9)
+ # define PLL_REF_SDVO_HDMI_MULTIPLIER(x) (((x)-1) << 9)
+@@ -586,12 +603,12 @@
+ #define FPB0 0x06048
+ #define FPB1 0x0604c
+ #define FP_N_DIV_MASK 0x003f0000
+-#define FP_N_IGD_DIV_MASK 0x00ff0000
++#define FP_N_PINEVIEW_DIV_MASK 0x00ff0000
+ #define FP_N_DIV_SHIFT 16
+ #define FP_M1_DIV_MASK 0x00003f00
+ #define FP_M1_DIV_SHIFT 8
+ #define FP_M2_DIV_MASK 0x0000003f
+-#define FP_M2_IGD_DIV_MASK 0x000000ff
++#define FP_M2_PINEVIEW_DIV_MASK 0x000000ff
+ #define FP_M2_DIV_SHIFT 0
+ #define DPLL_TEST 0x606c
+ #define DPLLB_TEST_SDVO_DIV_1 (0 << 22)
+@@ -769,7 +786,8 @@
+
+ /** GM965 GM45 render standby register */
+ #define MCHBAR_RENDER_STANDBY 0x111B8
+-
++#define RCX_SW_EXIT (1<<23)
++#define RSX_STATUS_MASK 0x00700000
+ #define PEG_BAND_GAP_DATA 0x14d68
+
+ /*
+@@ -844,7 +862,6 @@
+ #define SDVOB_HOTPLUG_INT_EN (1 << 26)
+ #define SDVOC_HOTPLUG_INT_EN (1 << 25)
+ #define TV_HOTPLUG_INT_EN (1 << 18)
+-#define CRT_EOS_INT_EN (1 << 10)
+ #define CRT_HOTPLUG_INT_EN (1 << 9)
+ #define CRT_HOTPLUG_FORCE_DETECT (1 << 3)
+ #define CRT_HOTPLUG_ACTIVATION_PERIOD_32 (0 << 8)
+@@ -871,7 +888,6 @@
+ #define DPC_HOTPLUG_INT_STATUS (1 << 28)
+ #define HDMID_HOTPLUG_INT_STATUS (1 << 27)
+ #define DPD_HOTPLUG_INT_STATUS (1 << 27)
+-#define CRT_EOS_INT_STATUS (1 << 12)
+ #define CRT_HOTPLUG_INT_STATUS (1 << 11)
+ #define TV_HOTPLUG_INT_STATUS (1 << 10)
+ #define CRT_HOTPLUG_MONITOR_MASK (3 << 8)
+@@ -1614,7 +1630,7 @@
+ #define DP_CLOCK_OUTPUT_ENABLE (1 << 13)
+
+ #define DP_SCRAMBLING_DISABLE (1 << 12)
+-#define DP_SCRAMBLING_DISABLE_IGDNG (1 << 7)
++#define DP_SCRAMBLING_DISABLE_IRONLAKE (1 << 7)
+
+ /** limit RGB values to avoid confusing TVs */
+ #define DP_COLOR_RANGE_16_235 (1 << 8)
+@@ -1800,11 +1816,11 @@
+ #define DSPFW_PLANEB_SHIFT 8
+ #define DSPFW2 0x70038
+ #define DSPFW_CURSORA_MASK 0x00003f00
+-#define DSPFW_CURSORA_SHIFT 16
++#define DSPFW_CURSORA_SHIFT 8
+ #define DSPFW3 0x7003c
+ #define DSPFW_HPLL_SR_EN (1<<31)
+ #define DSPFW_CURSOR_SR_SHIFT 24
+-#define IGD_SELF_REFRESH_EN (1<<30)
++#define PINEVIEW_SELF_REFRESH_EN (1<<30)
+
+ /* FIFO watermark sizes etc */
+ #define G4X_FIFO_LINE_SIZE 64
+@@ -1820,16 +1836,16 @@
+ #define G4X_MAX_WM 0x3f
+ #define I915_MAX_WM 0x3f
+
+-#define IGD_DISPLAY_FIFO 512 /* in 64byte unit */
+-#define IGD_FIFO_LINE_SIZE 64
+-#define IGD_MAX_WM 0x1ff
+-#define IGD_DFT_WM 0x3f
+-#define IGD_DFT_HPLLOFF_WM 0
+-#define IGD_GUARD_WM 10
+-#define IGD_CURSOR_FIFO 64
+-#define IGD_CURSOR_MAX_WM 0x3f
+-#define IGD_CURSOR_DFT_WM 0
+-#define IGD_CURSOR_GUARD_WM 5
++#define PINEVIEW_DISPLAY_FIFO 512 /* in 64byte unit */
++#define PINEVIEW_FIFO_LINE_SIZE 64
++#define PINEVIEW_MAX_WM 0x1ff
++#define PINEVIEW_DFT_WM 0x3f
++#define PINEVIEW_DFT_HPLLOFF_WM 0
++#define PINEVIEW_GUARD_WM 10
++#define PINEVIEW_CURSOR_FIFO 64
++#define PINEVIEW_CURSOR_MAX_WM 0x3f
++#define PINEVIEW_CURSOR_DFT_WM 0
++#define PINEVIEW_CURSOR_GUARD_WM 5
+
+ /*
+ * The two pipe frame counter registers are not synchronized, so
+@@ -1903,6 +1919,7 @@
+ #define DISPPLANE_16BPP (0x5<<26)
+ #define DISPPLANE_32BPP_NO_ALPHA (0x6<<26)
+ #define DISPPLANE_32BPP (0x7<<26)
++#define DISPPLANE_32BPP_30BIT_NO_ALPHA (0xa<<26)
+ #define DISPPLANE_STEREO_ENABLE (1<<25)
+ #define DISPPLANE_STEREO_DISABLE 0
+ #define DISPPLANE_SEL_PIPE_MASK (1<<24)
+@@ -1914,7 +1931,7 @@
+ #define DISPPLANE_NO_LINE_DOUBLE 0
+ #define DISPPLANE_STEREO_POLARITY_FIRST 0
+ #define DISPPLANE_STEREO_POLARITY_SECOND (1<<18)
+-#define DISPPLANE_TRICKLE_FEED_DISABLE (1<<14) /* IGDNG */
++#define DISPPLANE_TRICKLE_FEED_DISABLE (1<<14) /* Ironlake */
+ #define DISPPLANE_TILED (1<<10)
+ #define DSPAADDR 0x70184
+ #define DSPASTRIDE 0x70188
+@@ -1967,7 +1984,7 @@
+ # define VGA_2X_MODE (1 << 30)
+ # define VGA_PIPE_B_SELECT (1 << 29)
+
+-/* IGDNG */
++/* Ironlake */
+
+ #define CPU_VGACNTRL 0x41000
+
+@@ -2113,6 +2130,7 @@
+ #define SDE_PORTC_HOTPLUG (1 << 9)
+ #define SDE_PORTB_HOTPLUG (1 << 8)
+ #define SDE_SDVOB_HOTPLUG (1 << 6)
++#define SDE_HOTPLUG_MASK (0xf << 8)
+
+ #define SDEISR 0xc4000
+ #define SDEIMR 0xc4004
+@@ -2153,6 +2171,13 @@
+ #define PCH_GPIOE 0xc5020
+ #define PCH_GPIOF 0xc5024
+
++#define PCH_GMBUS0 0xc5100
++#define PCH_GMBUS1 0xc5104
++#define PCH_GMBUS2 0xc5108
++#define PCH_GMBUS3 0xc510c
++#define PCH_GMBUS4 0xc5110
++#define PCH_GMBUS5 0xc5120
++
+ #define PCH_DPLL_A 0xc6014
+ #define PCH_DPLL_B 0xc6018
+
+@@ -2288,7 +2313,7 @@
+ #define FDI_DP_PORT_WIDTH_X3 (2<<19)
+ #define FDI_DP_PORT_WIDTH_X4 (3<<19)
+ #define FDI_TX_ENHANCE_FRAME_ENABLE (1<<18)
+-/* IGDNG: hardwired to 1 */
++/* Ironlake: hardwired to 1 */
+ #define FDI_TX_PLL_ENABLE (1<<14)
+ /* both Tx and Rx */
+ #define FDI_SCRAMBLING_ENABLE (0<<7)
+diff --git a/drivers/gpu/drm/i915/i915_suspend.c b/drivers/gpu/drm/i915/i915_suspend.c
+index 6eec817..a3b90c9 100644
+--- a/drivers/gpu/drm/i915/i915_suspend.c
++++ b/drivers/gpu/drm/i915/i915_suspend.c
+@@ -27,14 +27,14 @@
+ #include "drmP.h"
+ #include "drm.h"
+ #include "i915_drm.h"
+-#include "i915_drv.h"
++#include "intel_drv.h"
+
+ static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe)
+ {
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 dpll_reg;
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ dpll_reg = (pipe == PIPE_A) ? PCH_DPLL_A: PCH_DPLL_B;
+ } else {
+ dpll_reg = (pipe == PIPE_A) ? DPLL_A: DPLL_B;
+@@ -53,7 +53,7 @@ static void i915_save_palette(struct drm_device *dev, enum pipe pipe)
+ if (!i915_pipe_enabled(dev, pipe))
+ return;
+
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ reg = (pipe == PIPE_A) ? LGC_PALETTE_A : LGC_PALETTE_B;
+
+ if (pipe == PIPE_A)
+@@ -75,7 +75,7 @@ static void i915_restore_palette(struct drm_device *dev, enum pipe pipe)
+ if (!i915_pipe_enabled(dev, pipe))
+ return;
+
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ reg = (pipe == PIPE_A) ? LGC_PALETTE_A : LGC_PALETTE_B;
+
+ if (pipe == PIPE_A)
+@@ -239,7 +239,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return;
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ dev_priv->savePCH_DREF_CONTROL = I915_READ(PCH_DREF_CONTROL);
+ dev_priv->saveDISP_ARB_CTL = I915_READ(DISP_ARB_CTL);
+ }
+@@ -247,7 +247,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
+ /* Pipe & plane A info */
+ dev_priv->savePIPEACONF = I915_READ(PIPEACONF);
+ dev_priv->savePIPEASRC = I915_READ(PIPEASRC);
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ dev_priv->saveFPA0 = I915_READ(PCH_FPA0);
+ dev_priv->saveFPA1 = I915_READ(PCH_FPA1);
+ dev_priv->saveDPLL_A = I915_READ(PCH_DPLL_A);
+@@ -256,7 +256,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
+ dev_priv->saveFPA1 = I915_READ(FPA1);
+ dev_priv->saveDPLL_A = I915_READ(DPLL_A);
+ }
+- if (IS_I965G(dev) && !IS_IGDNG(dev))
++ if (IS_I965G(dev) && !IS_IRONLAKE(dev))
+ dev_priv->saveDPLL_A_MD = I915_READ(DPLL_A_MD);
+ dev_priv->saveHTOTAL_A = I915_READ(HTOTAL_A);
+ dev_priv->saveHBLANK_A = I915_READ(HBLANK_A);
+@@ -264,10 +264,10 @@ static void i915_save_modeset_reg(struct drm_device *dev)
+ dev_priv->saveVTOTAL_A = I915_READ(VTOTAL_A);
+ dev_priv->saveVBLANK_A = I915_READ(VBLANK_A);
+ dev_priv->saveVSYNC_A = I915_READ(VSYNC_A);
+- if (!IS_IGDNG(dev))
++ if (!IS_IRONLAKE(dev))
+ dev_priv->saveBCLRPAT_A = I915_READ(BCLRPAT_A);
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ dev_priv->savePIPEA_DATA_M1 = I915_READ(PIPEA_DATA_M1);
+ dev_priv->savePIPEA_DATA_N1 = I915_READ(PIPEA_DATA_N1);
+ dev_priv->savePIPEA_LINK_M1 = I915_READ(PIPEA_LINK_M1);
+@@ -304,7 +304,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
+ /* Pipe & plane B info */
+ dev_priv->savePIPEBCONF = I915_READ(PIPEBCONF);
+ dev_priv->savePIPEBSRC = I915_READ(PIPEBSRC);
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ dev_priv->saveFPB0 = I915_READ(PCH_FPB0);
+ dev_priv->saveFPB1 = I915_READ(PCH_FPB1);
+ dev_priv->saveDPLL_B = I915_READ(PCH_DPLL_B);
+@@ -313,7 +313,7 @@ static void i915_save_modeset_reg(struct drm_device *dev)
+ dev_priv->saveFPB1 = I915_READ(FPB1);
+ dev_priv->saveDPLL_B = I915_READ(DPLL_B);
+ }
+- if (IS_I965G(dev) && !IS_IGDNG(dev))
++ if (IS_I965G(dev) && !IS_IRONLAKE(dev))
+ dev_priv->saveDPLL_B_MD = I915_READ(DPLL_B_MD);
+ dev_priv->saveHTOTAL_B = I915_READ(HTOTAL_B);
+ dev_priv->saveHBLANK_B = I915_READ(HBLANK_B);
+@@ -321,10 +321,10 @@ static void i915_save_modeset_reg(struct drm_device *dev)
+ dev_priv->saveVTOTAL_B = I915_READ(VTOTAL_B);
+ dev_priv->saveVBLANK_B = I915_READ(VBLANK_B);
+ dev_priv->saveVSYNC_B = I915_READ(VSYNC_B);
+- if (!IS_IGDNG(dev))
++ if (!IS_IRONLAKE(dev))
+ dev_priv->saveBCLRPAT_B = I915_READ(BCLRPAT_B);
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ dev_priv->savePIPEB_DATA_M1 = I915_READ(PIPEB_DATA_M1);
+ dev_priv->savePIPEB_DATA_N1 = I915_READ(PIPEB_DATA_N1);
+ dev_priv->savePIPEB_LINK_M1 = I915_READ(PIPEB_LINK_M1);
+@@ -369,7 +369,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
+ if (drm_core_check_feature(dev, DRIVER_MODESET))
+ return;
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ dpll_a_reg = PCH_DPLL_A;
+ dpll_b_reg = PCH_DPLL_B;
+ fpa0_reg = PCH_FPA0;
+@@ -385,7 +385,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
+ fpb1_reg = FPB1;
+ }
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ I915_WRITE(PCH_DREF_CONTROL, dev_priv->savePCH_DREF_CONTROL);
+ I915_WRITE(DISP_ARB_CTL, dev_priv->saveDISP_ARB_CTL);
+ }
+@@ -402,7 +402,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
+ /* Actually enable it */
+ I915_WRITE(dpll_a_reg, dev_priv->saveDPLL_A);
+ DRM_UDELAY(150);
+- if (IS_I965G(dev) && !IS_IGDNG(dev))
++ if (IS_I965G(dev) && !IS_IRONLAKE(dev))
+ I915_WRITE(DPLL_A_MD, dev_priv->saveDPLL_A_MD);
+ DRM_UDELAY(150);
+
+@@ -413,10 +413,10 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
+ I915_WRITE(VTOTAL_A, dev_priv->saveVTOTAL_A);
+ I915_WRITE(VBLANK_A, dev_priv->saveVBLANK_A);
+ I915_WRITE(VSYNC_A, dev_priv->saveVSYNC_A);
+- if (!IS_IGDNG(dev))
++ if (!IS_IRONLAKE(dev))
+ I915_WRITE(BCLRPAT_A, dev_priv->saveBCLRPAT_A);
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ I915_WRITE(PIPEA_DATA_M1, dev_priv->savePIPEA_DATA_M1);
+ I915_WRITE(PIPEA_DATA_N1, dev_priv->savePIPEA_DATA_N1);
+ I915_WRITE(PIPEA_LINK_M1, dev_priv->savePIPEA_LINK_M1);
+@@ -467,7 +467,7 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
+ /* Actually enable it */
+ I915_WRITE(dpll_b_reg, dev_priv->saveDPLL_B);
+ DRM_UDELAY(150);
+- if (IS_I965G(dev) && !IS_IGDNG(dev))
++ if (IS_I965G(dev) && !IS_IRONLAKE(dev))
+ I915_WRITE(DPLL_B_MD, dev_priv->saveDPLL_B_MD);
+ DRM_UDELAY(150);
+
+@@ -478,10 +478,10 @@ static void i915_restore_modeset_reg(struct drm_device *dev)
+ I915_WRITE(VTOTAL_B, dev_priv->saveVTOTAL_B);
+ I915_WRITE(VBLANK_B, dev_priv->saveVBLANK_B);
+ I915_WRITE(VSYNC_B, dev_priv->saveVSYNC_B);
+- if (!IS_IGDNG(dev))
++ if (!IS_IRONLAKE(dev))
+ I915_WRITE(BCLRPAT_B, dev_priv->saveBCLRPAT_B);
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ I915_WRITE(PIPEB_DATA_M1, dev_priv->savePIPEB_DATA_M1);
+ I915_WRITE(PIPEB_DATA_N1, dev_priv->savePIPEB_DATA_N1);
+ I915_WRITE(PIPEB_LINK_M1, dev_priv->savePIPEB_LINK_M1);
+@@ -546,14 +546,14 @@ void i915_save_display(struct drm_device *dev)
+ dev_priv->saveCURSIZE = I915_READ(CURSIZE);
+
+ /* CRT state */
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ dev_priv->saveADPA = I915_READ(PCH_ADPA);
+ } else {
+ dev_priv->saveADPA = I915_READ(ADPA);
+ }
+
+ /* LVDS state */
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ dev_priv->savePP_CONTROL = I915_READ(PCH_PP_CONTROL);
+ dev_priv->saveBLC_PWM_CTL = I915_READ(BLC_PWM_PCH_CTL1);
+ dev_priv->saveBLC_PWM_CTL2 = I915_READ(BLC_PWM_PCH_CTL2);
+@@ -571,10 +571,10 @@ void i915_save_display(struct drm_device *dev)
+ dev_priv->saveLVDS = I915_READ(LVDS);
+ }
+
+- if (!IS_I830(dev) && !IS_845G(dev) && !IS_IGDNG(dev))
++ if (!IS_I830(dev) && !IS_845G(dev) && !IS_IRONLAKE(dev))
+ dev_priv->savePFIT_CONTROL = I915_READ(PFIT_CONTROL);
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ dev_priv->savePP_ON_DELAYS = I915_READ(PCH_PP_ON_DELAYS);
+ dev_priv->savePP_OFF_DELAYS = I915_READ(PCH_PP_OFF_DELAYS);
+ dev_priv->savePP_DIVISOR = I915_READ(PCH_PP_DIVISOR);
+@@ -614,7 +614,7 @@ void i915_save_display(struct drm_device *dev)
+ dev_priv->saveVGA0 = I915_READ(VGA0);
+ dev_priv->saveVGA1 = I915_READ(VGA1);
+ dev_priv->saveVGA_PD = I915_READ(VGA_PD);
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ dev_priv->saveVGACNTRL = I915_READ(CPU_VGACNTRL);
+ else
+ dev_priv->saveVGACNTRL = I915_READ(VGACNTRL);
+@@ -656,24 +656,24 @@ void i915_restore_display(struct drm_device *dev)
+ I915_WRITE(CURSIZE, dev_priv->saveCURSIZE);
+
+ /* CRT state */
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ I915_WRITE(PCH_ADPA, dev_priv->saveADPA);
+ else
+ I915_WRITE(ADPA, dev_priv->saveADPA);
+
+ /* LVDS state */
+- if (IS_I965G(dev) && !IS_IGDNG(dev))
++ if (IS_I965G(dev) && !IS_IRONLAKE(dev))
+ I915_WRITE(BLC_PWM_CTL2, dev_priv->saveBLC_PWM_CTL2);
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ I915_WRITE(PCH_LVDS, dev_priv->saveLVDS);
+ } else if (IS_MOBILE(dev) && !IS_I830(dev))
+ I915_WRITE(LVDS, dev_priv->saveLVDS);
+
+- if (!IS_I830(dev) && !IS_845G(dev) && !IS_IGDNG(dev))
++ if (!IS_I830(dev) && !IS_845G(dev) && !IS_IRONLAKE(dev))
+ I915_WRITE(PFIT_CONTROL, dev_priv->savePFIT_CONTROL);
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ I915_WRITE(BLC_PWM_PCH_CTL1, dev_priv->saveBLC_PWM_CTL);
+ I915_WRITE(BLC_PWM_PCH_CTL2, dev_priv->saveBLC_PWM_CTL2);
+ I915_WRITE(BLC_PWM_CPU_CTL, dev_priv->saveBLC_CPU_PWM_CTL);
+@@ -713,7 +713,7 @@ void i915_restore_display(struct drm_device *dev)
+ }
+
+ /* VGA state */
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ I915_WRITE(CPU_VGACNTRL, dev_priv->saveVGACNTRL);
+ else
+ I915_WRITE(VGACNTRL, dev_priv->saveVGACNTRL);
+@@ -732,17 +732,13 @@ int i915_save_state(struct drm_device *dev)
+
+ pci_read_config_byte(dev->pdev, LBB, &dev_priv->saveLBB);
+
+- /* Render Standby */
+- if (IS_I965G(dev) && IS_MOBILE(dev))
+- dev_priv->saveRENDERSTANDBY = I915_READ(MCHBAR_RENDER_STANDBY);
+-
+ /* Hardware status page */
+ dev_priv->saveHWS = I915_READ(HWS_PGA);
+
+ i915_save_display(dev);
+
+ /* Interrupt state */
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ dev_priv->saveDEIER = I915_READ(DEIER);
+ dev_priv->saveDEIMR = I915_READ(DEIMR);
+ dev_priv->saveGTIER = I915_READ(GTIER);
+@@ -754,10 +750,6 @@ int i915_save_state(struct drm_device *dev)
+ dev_priv->saveIMR = I915_READ(IMR);
+ }
+
+- /* Clock gating state */
+- dev_priv->saveD_STATE = I915_READ(D_STATE);
+- dev_priv->saveDSPCLK_GATE_D = I915_READ(DSPCLK_GATE_D); /* Not sure about this */
+-
+ /* Cache mode state */
+ dev_priv->saveCACHE_MODE_0 = I915_READ(CACHE_MODE_0);
+
+@@ -795,10 +787,6 @@ int i915_restore_state(struct drm_device *dev)
+
+ pci_write_config_byte(dev->pdev, LBB, dev_priv->saveLBB);
+
+- /* Render Standby */
+- if (IS_I965G(dev) && IS_MOBILE(dev))
+- I915_WRITE(MCHBAR_RENDER_STANDBY, dev_priv->saveRENDERSTANDBY);
+-
+ /* Hardware status page */
+ I915_WRITE(HWS_PGA, dev_priv->saveHWS);
+
+@@ -817,7 +805,7 @@ int i915_restore_state(struct drm_device *dev)
+ i915_restore_display(dev);
+
+ /* Interrupt state */
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ I915_WRITE(DEIER, dev_priv->saveDEIER);
+ I915_WRITE(DEIMR, dev_priv->saveDEIMR);
+ I915_WRITE(GTIER, dev_priv->saveGTIER);
+@@ -830,8 +818,7 @@ int i915_restore_state(struct drm_device *dev)
+ }
+
+ /* Clock gating state */
+- I915_WRITE (D_STATE, dev_priv->saveD_STATE);
+- I915_WRITE (DSPCLK_GATE_D, dev_priv->saveDSPCLK_GATE_D);
++ intel_init_clock_gating(dev);
+
+ /* Cache mode state */
+ I915_WRITE (CACHE_MODE_0, dev_priv->saveCACHE_MODE_0 | 0xffff0000);
+@@ -846,6 +833,9 @@ int i915_restore_state(struct drm_device *dev)
+ for (i = 0; i < 3; i++)
+ I915_WRITE(SWF30 + (i << 2), dev_priv->saveSWF2[i]);
+
++ /* I2C state */
++ intel_i2c_reset_gmbus(dev);
++
+ return 0;
+ }
+
+diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c
+index 96cd256..15fbc1b 100644
+--- a/drivers/gpu/drm/i915/intel_bios.c
++++ b/drivers/gpu/drm/i915/intel_bios.c
+@@ -33,6 +33,8 @@
+ #define SLAVE_ADDR1 0x70
+ #define SLAVE_ADDR2 0x72
+
++static int panel_type;
++
+ static void *
+ find_section(struct bdb_header *bdb, int section_id)
+ {
+@@ -114,6 +116,8 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
+ struct lvds_dvo_timing *dvo_timing;
+ struct drm_display_mode *panel_fixed_mode;
+ int lfp_data_size, dvo_timing_offset;
++ int i, temp_downclock;
++ struct drm_display_mode *temp_mode;
+
+ /* Defaults if we can't find VBT info */
+ dev_priv->lvds_dither = 0;
+@@ -126,6 +130,7 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
+ dev_priv->lvds_dither = lvds_options->pixel_dither;
+ if (lvds_options->panel_type == 0xff)
+ return;
++ panel_type = lvds_options->panel_type;
+
+ lvds_lfp_data = find_section(bdb, BDB_LVDS_LFP_DATA);
+ if (!lvds_lfp_data)
+@@ -159,9 +164,50 @@ parse_lfp_panel_data(struct drm_i915_private *dev_priv,
+
+ dev_priv->lfp_lvds_vbt_mode = panel_fixed_mode;
+
+- DRM_DEBUG("Found panel mode in BIOS VBT tables:\n");
++ DRM_DEBUG_KMS("Found panel mode in BIOS VBT tables:\n");
+ drm_mode_debug_printmodeline(panel_fixed_mode);
+
++ temp_mode = kzalloc(sizeof(*temp_mode), GFP_KERNEL);
++ temp_downclock = panel_fixed_mode->clock;
++ /*
++ * enumerate the LVDS panel timing info entry in VBT to check whether
++ * the LVDS downclock is found.
++ */
++ for (i = 0; i < 16; i++) {
++ entry = (struct bdb_lvds_lfp_data_entry *)
++ ((uint8_t *)lvds_lfp_data->data + (lfp_data_size * i));
++ dvo_timing = (struct lvds_dvo_timing *)
++ ((unsigned char *)entry + dvo_timing_offset);
++
++ fill_detail_timing_data(temp_mode, dvo_timing);
++
++ if (temp_mode->hdisplay == panel_fixed_mode->hdisplay &&
++ temp_mode->hsync_start == panel_fixed_mode->hsync_start &&
++ temp_mode->hsync_end == panel_fixed_mode->hsync_end &&
++ temp_mode->htotal == panel_fixed_mode->htotal &&
++ temp_mode->vdisplay == panel_fixed_mode->vdisplay &&
++ temp_mode->vsync_start == panel_fixed_mode->vsync_start &&
++ temp_mode->vsync_end == panel_fixed_mode->vsync_end &&
++ temp_mode->vtotal == panel_fixed_mode->vtotal &&
++ temp_mode->clock < temp_downclock) {
++ /*
++ * downclock is already found. But we expect
++ * to find the lower downclock.
++ */
++ temp_downclock = temp_mode->clock;
++ }
++ /* clear it to zero */
++ memset(temp_mode, 0, sizeof(*temp_mode));
++ }
++ kfree(temp_mode);
++ if (temp_downclock < panel_fixed_mode->clock &&
++ i915_lvds_downclock) {
++ dev_priv->lvds_downclock_avail = 1;
++ dev_priv->lvds_downclock = temp_downclock;
++ DRM_DEBUG_KMS("LVDS downclock is found in VBT. ",
++ "Normal Clock %dKHz, downclock %dKHz\n",
++ temp_downclock, panel_fixed_mode->clock);
++ }
+ return;
+ }
+
+@@ -217,7 +263,7 @@ parse_general_features(struct drm_i915_private *dev_priv,
+ if (IS_I85X(dev_priv->dev))
+ dev_priv->lvds_ssc_freq =
+ general->ssc_freq ? 66 : 48;
+- else if (IS_IGDNG(dev_priv->dev))
++ else if (IS_IRONLAKE(dev_priv->dev))
+ dev_priv->lvds_ssc_freq =
+ general->ssc_freq ? 100 : 120;
+ else
+@@ -241,22 +287,18 @@ parse_general_definitions(struct drm_i915_private *dev_priv,
+ GPIOF,
+ };
+
+- /* Set sensible defaults in case we can't find the general block
+- or it is the wrong chipset */
+- dev_priv->crt_ddc_bus = -1;
+-
+ general = find_section(bdb, BDB_GENERAL_DEFINITIONS);
+ if (general) {
+ u16 block_size = get_blocksize(general);
+ if (block_size >= sizeof(*general)) {
+ int bus_pin = general->crt_ddc_gmbus_pin;
+- DRM_DEBUG("crt_ddc_bus_pin: %d\n", bus_pin);
++ DRM_DEBUG_KMS("crt_ddc_bus_pin: %d\n", bus_pin);
+ if ((bus_pin >= 1) && (bus_pin <= 6)) {
+ dev_priv->crt_ddc_bus =
+ crt_bus_map_table[bus_pin-1];
+ }
+ } else {
+- DRM_DEBUG("BDB_GD too small (%d). Invalid.\n",
++ DRM_DEBUG_KMS("BDB_GD too small (%d). Invalid.\n",
+ block_size);
+ }
+ }
+@@ -274,7 +316,7 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
+
+ p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
+ if (!p_defs) {
+- DRM_DEBUG("No general definition block is found\n");
++ DRM_DEBUG_KMS("No general definition block is found\n");
+ return;
+ }
+ /* judge whether the size of child device meets the requirements.
+@@ -284,7 +326,7 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
+ */
+ if (p_defs->child_dev_size != sizeof(*p_child)) {
+ /* different child dev size . Ignore it */
+- DRM_DEBUG("different child size is found. Invalid.\n");
++ DRM_DEBUG_KMS("different child size is found. Invalid.\n");
+ return;
+ }
+ /* get the block size of general definitions */
+@@ -310,11 +352,11 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
+ if (p_child->dvo_port != DEVICE_PORT_DVOB &&
+ p_child->dvo_port != DEVICE_PORT_DVOC) {
+ /* skip the incorrect SDVO port */
+- DRM_DEBUG("Incorrect SDVO port. Skip it \n");
++ DRM_DEBUG_KMS("Incorrect SDVO port. Skip it \n");
+ continue;
+ }
+- DRM_DEBUG("the SDVO device with slave addr %2x is found on "
+- "%s port\n",
++ DRM_DEBUG_KMS("the SDVO device with slave addr %2x is found on"
++ " %s port\n",
+ p_child->slave_addr,
+ (p_child->dvo_port == DEVICE_PORT_DVOB) ?
+ "SDVOB" : "SDVOC");
+@@ -325,21 +367,21 @@ parse_sdvo_device_mapping(struct drm_i915_private *dev_priv,
+ p_mapping->dvo_wiring = p_child->dvo_wiring;
+ p_mapping->initialized = 1;
+ } else {
+- DRM_DEBUG("Maybe one SDVO port is shared by "
++ DRM_DEBUG_KMS("Maybe one SDVO port is shared by "
+ "two SDVO device.\n");
+ }
+ if (p_child->slave2_addr) {
+ /* Maybe this is a SDVO device with multiple inputs */
+ /* And the mapping info is not added */
+- DRM_DEBUG("there exists the slave2_addr. Maybe this "
+- "is a SDVO device with multiple inputs.\n");
++ DRM_DEBUG_KMS("there exists the slave2_addr. Maybe this"
++ " is a SDVO device with multiple inputs.\n");
+ }
+ count++;
+ }
+
+ if (!count) {
+ /* No SDVO device info is found */
+- DRM_DEBUG("No SDVO device info is found in VBT\n");
++ DRM_DEBUG_KMS("No SDVO device info is found in VBT\n");
+ }
+ return;
+ }
+@@ -366,6 +408,98 @@ parse_driver_features(struct drm_i915_private *dev_priv,
+ dev_priv->render_reclock_avail = true;
+ }
+
++static void
++parse_edp(struct drm_i915_private *dev_priv, struct bdb_header *bdb)
++{
++ struct bdb_edp *edp;
++
++ edp = find_section(bdb, BDB_EDP);
++ if (!edp) {
++ if (SUPPORTS_EDP(dev_priv->dev) && dev_priv->edp_support) {
++ DRM_DEBUG_KMS("No eDP BDB found but eDP panel supported,\
++ assume 18bpp panel color depth.\n");
++ dev_priv->edp_bpp = 18;
++ }
++ return;
++ }
++
++ switch ((edp->color_depth >> (panel_type * 2)) & 3) {
++ case EDP_18BPP:
++ dev_priv->edp_bpp = 18;
++ break;
++ case EDP_24BPP:
++ dev_priv->edp_bpp = 24;
++ break;
++ case EDP_30BPP:
++ dev_priv->edp_bpp = 30;
++ break;
++ }
++}
++
++static void
++parse_device_mapping(struct drm_i915_private *dev_priv,
++ struct bdb_header *bdb)
++{
++ struct bdb_general_definitions *p_defs;
++ struct child_device_config *p_child, *child_dev_ptr;
++ int i, child_device_num, count;
++ u16 block_size;
++
++ p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS);
++ if (!p_defs) {
++ DRM_DEBUG_KMS("No general definition block is found\n");
++ return;
++ }
++ /* judge whether the size of child device meets the requirements.
++ * If the child device size obtained from general definition block
++ * is different with sizeof(struct child_device_config), skip the
++ * parsing of sdvo device info
++ */
++ if (p_defs->child_dev_size != sizeof(*p_child)) {
++ /* different child dev size . Ignore it */
++ DRM_DEBUG_KMS("different child size is found. Invalid.\n");
++ return;
++ }
++ /* get the block size of general definitions */
++ block_size = get_blocksize(p_defs);
++ /* get the number of child device */
++ child_device_num = (block_size - sizeof(*p_defs)) /
++ sizeof(*p_child);
++ count = 0;
++ /* get the number of child device that is present */
++ for (i = 0; i < child_device_num; i++) {
++ p_child = &(p_defs->devices[i]);
++ if (!p_child->device_type) {
++ /* skip the device block if device type is invalid */
++ continue;
++ }
++ count++;
++ }
++ if (!count) {
++ DRM_DEBUG_KMS("no child dev is parsed from VBT \n");
++ return;
++ }
++ dev_priv->child_dev = kzalloc(sizeof(*p_child) * count, GFP_KERNEL);
++ if (!dev_priv->child_dev) {
++ DRM_DEBUG_KMS("No memory space for child device\n");
++ return;
++ }
++
++ dev_priv->child_dev_num = count;
++ count = 0;
++ for (i = 0; i < child_device_num; i++) {
++ p_child = &(p_defs->devices[i]);
++ if (!p_child->device_type) {
++ /* skip the device block if device type is invalid */
++ continue;
++ }
++ child_dev_ptr = dev_priv->child_dev + count;
++ count++;
++ memcpy((void *)child_dev_ptr, (void *)p_child,
++ sizeof(*p_child));
++ }
++ return;
++}
+ /**
+ * intel_init_bios - initialize VBIOS settings & find VBT
+ * @dev: DRM device
+@@ -417,7 +551,9 @@ intel_init_bios(struct drm_device *dev)
+ parse_lfp_panel_data(dev_priv, bdb);
+ parse_sdvo_panel_data(dev_priv, bdb);
+ parse_sdvo_device_mapping(dev_priv, bdb);
++ parse_device_mapping(dev_priv, bdb);
+ parse_driver_features(dev_priv, bdb);
++ parse_edp(dev_priv, bdb);
+
+ pci_unmap_rom(pdev, bios);
+
+diff --git a/drivers/gpu/drm/i915/intel_bios.h b/drivers/gpu/drm/i915/intel_bios.h
+index 0f8e5f6..4c18514 100644
+--- a/drivers/gpu/drm/i915/intel_bios.h
++++ b/drivers/gpu/drm/i915/intel_bios.h
+@@ -98,6 +98,7 @@ struct vbios_data {
+ #define BDB_SDVO_LVDS_PNP_IDS 24
+ #define BDB_SDVO_LVDS_POWER_SEQ 25
+ #define BDB_TV_OPTIONS 26
++#define BDB_EDP 27
+ #define BDB_LVDS_OPTIONS 40
+ #define BDB_LVDS_LFP_DATA_PTRS 41
+ #define BDB_LVDS_LFP_DATA 42
+@@ -426,6 +427,45 @@ struct bdb_driver_features {
+ u8 custom_vbt_version;
+ } __attribute__((packed));
+
++#define EDP_18BPP 0
++#define EDP_24BPP 1
++#define EDP_30BPP 2
++#define EDP_RATE_1_62 0
++#define EDP_RATE_2_7 1
++#define EDP_LANE_1 0
++#define EDP_LANE_2 1
++#define EDP_LANE_4 3
++#define EDP_PREEMPHASIS_NONE 0
++#define EDP_PREEMPHASIS_3_5dB 1
++#define EDP_PREEMPHASIS_6dB 2
++#define EDP_PREEMPHASIS_9_5dB 3
++#define EDP_VSWING_0_4V 0
++#define EDP_VSWING_0_6V 1
++#define EDP_VSWING_0_8V 2
++#define EDP_VSWING_1_2V 3
++
++struct edp_power_seq {
++ u16 t3;
++ u16 t7;
++ u16 t9;
++ u16 t10;
++ u16 t12;
++} __attribute__ ((packed));
++
++struct edp_link_params {
++ u8 rate:4;
++ u8 lanes:4;
++ u8 preemphasis:4;
++ u8 vswing:4;
++} __attribute__ ((packed));
++
++struct bdb_edp {
++ struct edp_power_seq power_seqs[16];
++ u32 color_depth;
++ u32 sdrrs_msa_timing_delay;
++ struct edp_link_params link_params[16];
++} __attribute__ ((packed));
++
+ bool intel_init_bios(struct drm_device *dev);
+
+ /*
+@@ -549,4 +589,21 @@ bool intel_init_bios(struct drm_device *dev);
+ #define SWF14_APM_STANDBY 0x1
+ #define SWF14_APM_RESTORE 0x0
+
++/* Add the device class for LFP, TV, HDMI */
++#define DEVICE_TYPE_INT_LFP 0x1022
++#define DEVICE_TYPE_INT_TV 0x1009
++#define DEVICE_TYPE_HDMI 0x60D2
++#define DEVICE_TYPE_DP 0x68C6
++#define DEVICE_TYPE_eDP 0x78C6
++
++/* define the DVO port for HDMI output type */
++#define DVO_B 1
++#define DVO_C 2
++#define DVO_D 3
++
++/* define the PORT for DP output type */
++#define PORT_IDPB 7
++#define PORT_IDPC 8
++#define PORT_IDPD 9
++
+ #endif /* _I830_BIOS_H_ */
+diff --git a/drivers/gpu/drm/i915/intel_crt.c b/drivers/gpu/drm/i915/intel_crt.c
+index 6d3730f..79dd402 100644
+--- a/drivers/gpu/drm/i915/intel_crt.c
++++ b/drivers/gpu/drm/i915/intel_crt.c
+@@ -39,7 +39,7 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 temp, reg;
+
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ reg = PCH_ADPA;
+ else
+ reg = ADPA;
+@@ -64,34 +64,6 @@ static void intel_crt_dpms(struct drm_encoder *encoder, int mode)
+ }
+
+ I915_WRITE(reg, temp);
+-
+- if (IS_IGD(dev)) {
+- if (mode == DRM_MODE_DPMS_OFF) {
+- /* turn off DAC */
+- temp = I915_READ(PORT_HOTPLUG_EN);
+- temp &= ~CRT_EOS_INT_EN;
+- I915_WRITE(PORT_HOTPLUG_EN, temp);
+-
+- temp = I915_READ(PORT_HOTPLUG_STAT);
+- if (temp & CRT_EOS_INT_STATUS)
+- I915_WRITE(PORT_HOTPLUG_STAT,
+- CRT_EOS_INT_STATUS);
+- } else {
+- /* turn on DAC. EOS interrupt must be enabled after DAC
+- * is enabled, so it sounds not good to enable it in
+- * i915_driver_irq_postinstall()
+- * wait 12.5ms after DAC is enabled
+- */
+- msleep(13);
+- temp = I915_READ(PORT_HOTPLUG_STAT);
+- if (temp & CRT_EOS_INT_STATUS)
+- I915_WRITE(PORT_HOTPLUG_STAT,
+- CRT_EOS_INT_STATUS);
+- temp = I915_READ(PORT_HOTPLUG_EN);
+- temp |= CRT_EOS_INT_EN;
+- I915_WRITE(PORT_HOTPLUG_EN, temp);
+- }
+- }
+ }
+
+ static int intel_crt_mode_valid(struct drm_connector *connector,
+@@ -141,7 +113,7 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
+ else
+ dpll_md_reg = DPLL_B_MD;
+
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ adpa_reg = PCH_ADPA;
+ else
+ adpa_reg = ADPA;
+@@ -150,7 +122,7 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
+ * Disable separate mode multiplier used when cloning SDVO to CRT
+ * XXX this needs to be adjusted when we really are cloning
+ */
+- if (IS_I965G(dev) && !IS_IGDNG(dev)) {
++ if (IS_I965G(dev) && !IS_IRONLAKE(dev)) {
+ dpll_md = I915_READ(dpll_md_reg);
+ I915_WRITE(dpll_md_reg,
+ dpll_md & ~DPLL_MD_UDI_MULTIPLIER_MASK);
+@@ -164,18 +136,18 @@ static void intel_crt_mode_set(struct drm_encoder *encoder,
+
+ if (intel_crtc->pipe == 0) {
+ adpa |= ADPA_PIPE_A_SELECT;
+- if (!IS_IGDNG(dev))
++ if (!IS_IRONLAKE(dev))
+ I915_WRITE(BCLRPAT_A, 0);
+ } else {
+ adpa |= ADPA_PIPE_B_SELECT;
+- if (!IS_IGDNG(dev))
++ if (!IS_IRONLAKE(dev))
+ I915_WRITE(BCLRPAT_B, 0);
+ }
+
+ I915_WRITE(adpa_reg, adpa);
+ }
+
+-static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector)
++static bool intel_ironlake_crt_detect_hotplug(struct drm_connector *connector)
+ {
+ struct drm_device *dev = connector->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+@@ -185,6 +157,9 @@ static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector)
+ adpa = I915_READ(PCH_ADPA);
+
+ adpa &= ~ADPA_CRT_HOTPLUG_MASK;
++ /* disable HPD first */
++ I915_WRITE(PCH_ADPA, adpa);
++ (void)I915_READ(PCH_ADPA);
+
+ adpa |= (ADPA_CRT_HOTPLUG_PERIOD_128 |
+ ADPA_CRT_HOTPLUG_WARMUP_10MS |
+@@ -194,7 +169,7 @@ static bool intel_igdng_crt_detect_hotplug(struct drm_connector *connector)
+ ADPA_CRT_HOTPLUG_ENABLE |
+ ADPA_CRT_HOTPLUG_FORCE_TRIGGER);
+
+- DRM_DEBUG("pch crt adpa 0x%x", adpa);
++ DRM_DEBUG_KMS("pch crt adpa 0x%x", adpa);
+ I915_WRITE(PCH_ADPA, adpa);
+
+ while ((I915_READ(PCH_ADPA) & ADPA_CRT_HOTPLUG_FORCE_TRIGGER) != 0)
+@@ -227,8 +202,8 @@ static bool intel_crt_detect_hotplug(struct drm_connector *connector)
+ u32 hotplug_en;
+ int i, tries = 0;
+
+- if (IS_IGDNG(dev))
+- return intel_igdng_crt_detect_hotplug(connector);
++ if (IS_IRONLAKE(dev))
++ return intel_ironlake_crt_detect_hotplug(connector);
+
+ /*
+ * On 4 series desktop, CRT detect sequence need to be done twice
+@@ -549,12 +524,12 @@ void intel_crt_init(struct drm_device *dev)
+ &intel_output->enc);
+
+ /* Set up the DDC bus. */
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ i2c_reg = PCH_GPIOA;
+ else {
+ i2c_reg = GPIOA;
+ /* Use VBT information for CRT DDC if available */
+- if (dev_priv->crt_ddc_bus != -1)
++ if (dev_priv->crt_ddc_bus != 0)
+ i2c_reg = dev_priv->crt_ddc_bus;
+ }
+ intel_output->ddc_bus = intel_i2c_create(dev, i2c_reg, "CRTDDC_A");
+diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c
+index 601415d..b27202d 100644
+--- a/drivers/gpu/drm/i915/intel_display.c
++++ b/drivers/gpu/drm/i915/intel_display.c
+@@ -32,7 +32,7 @@
+ #include "intel_drv.h"
+ #include "i915_drm.h"
+ #include "i915_drv.h"
+-#include "intel_dp.h"
++#include "drm_dp_helper.h"
+
+ #include "drm_crtc_helper.h"
+
+@@ -70,8 +70,6 @@ struct intel_limit {
+ intel_p2_t p2;
+ bool (* find_pll)(const intel_limit_t *, struct drm_crtc *,
+ int, int, intel_clock_t *);
+- bool (* find_reduced_pll)(const intel_limit_t *, struct drm_crtc *,
+- int, int, intel_clock_t *);
+ };
+
+ #define I8XX_DOT_MIN 25000
+@@ -102,32 +100,32 @@ struct intel_limit {
+ #define I9XX_DOT_MAX 400000
+ #define I9XX_VCO_MIN 1400000
+ #define I9XX_VCO_MAX 2800000
+-#define IGD_VCO_MIN 1700000
+-#define IGD_VCO_MAX 3500000
++#define PINEVIEW_VCO_MIN 1700000
++#define PINEVIEW_VCO_MAX 3500000
+ #define I9XX_N_MIN 1
+ #define I9XX_N_MAX 6
+-/* IGD's Ncounter is a ring counter */
+-#define IGD_N_MIN 3
+-#define IGD_N_MAX 6
++/* Pineview's Ncounter is a ring counter */
++#define PINEVIEW_N_MIN 3
++#define PINEVIEW_N_MAX 6
+ #define I9XX_M_MIN 70
+ #define I9XX_M_MAX 120
+-#define IGD_M_MIN 2
+-#define IGD_M_MAX 256
++#define PINEVIEW_M_MIN 2
++#define PINEVIEW_M_MAX 256
+ #define I9XX_M1_MIN 10
+ #define I9XX_M1_MAX 22
+ #define I9XX_M2_MIN 5
+ #define I9XX_M2_MAX 9
+-/* IGD M1 is reserved, and must be 0 */
+-#define IGD_M1_MIN 0
+-#define IGD_M1_MAX 0
+-#define IGD_M2_MIN 0
+-#define IGD_M2_MAX 254
++/* Pineview M1 is reserved, and must be 0 */
++#define PINEVIEW_M1_MIN 0
++#define PINEVIEW_M1_MAX 0
++#define PINEVIEW_M2_MIN 0
++#define PINEVIEW_M2_MAX 254
+ #define I9XX_P_SDVO_DAC_MIN 5
+ #define I9XX_P_SDVO_DAC_MAX 80
+ #define I9XX_P_LVDS_MIN 7
+ #define I9XX_P_LVDS_MAX 98
+-#define IGD_P_LVDS_MIN 7
+-#define IGD_P_LVDS_MAX 112
++#define PINEVIEW_P_LVDS_MIN 7
++#define PINEVIEW_P_LVDS_MAX 112
+ #define I9XX_P1_MIN 1
+ #define I9XX_P1_MAX 8
+ #define I9XX_P2_SDVO_DAC_SLOW 10
+@@ -234,53 +232,108 @@ struct intel_limit {
+ #define G4X_P2_DISPLAY_PORT_FAST 10
+ #define G4X_P2_DISPLAY_PORT_LIMIT 0
+
+-/* IGDNG */
++/* Ironlake */
+ /* as we calculate clock using (register_value + 2) for
+ N/M1/M2, so here the range value for them is (actual_value-2).
+ */
+-#define IGDNG_DOT_MIN 25000
+-#define IGDNG_DOT_MAX 350000
+-#define IGDNG_VCO_MIN 1760000
+-#define IGDNG_VCO_MAX 3510000
+-#define IGDNG_N_MIN 1
+-#define IGDNG_N_MAX 5
+-#define IGDNG_M_MIN 79
+-#define IGDNG_M_MAX 118
+-#define IGDNG_M1_MIN 12
+-#define IGDNG_M1_MAX 23
+-#define IGDNG_M2_MIN 5
+-#define IGDNG_M2_MAX 9
+-#define IGDNG_P_SDVO_DAC_MIN 5
+-#define IGDNG_P_SDVO_DAC_MAX 80
+-#define IGDNG_P_LVDS_MIN 28
+-#define IGDNG_P_LVDS_MAX 112
+-#define IGDNG_P1_MIN 1
+-#define IGDNG_P1_MAX 8
+-#define IGDNG_P2_SDVO_DAC_SLOW 10
+-#define IGDNG_P2_SDVO_DAC_FAST 5
+-#define IGDNG_P2_LVDS_SLOW 14 /* single channel */
+-#define IGDNG_P2_LVDS_FAST 7 /* double channel */
+-#define IGDNG_P2_DOT_LIMIT 225000 /* 225Mhz */
++#define IRONLAKE_DOT_MIN 25000
++#define IRONLAKE_DOT_MAX 350000
++#define IRONLAKE_VCO_MIN 1760000
++#define IRONLAKE_VCO_MAX 3510000
++#define IRONLAKE_M1_MIN 12
++#define IRONLAKE_M1_MAX 22
++#define IRONLAKE_M2_MIN 5
++#define IRONLAKE_M2_MAX 9
++#define IRONLAKE_P2_DOT_LIMIT 225000 /* 225Mhz */
++
++/* We have parameter ranges for different type of outputs. */
++
++/* DAC & HDMI Refclk 120Mhz */
++#define IRONLAKE_DAC_N_MIN 1
++#define IRONLAKE_DAC_N_MAX 5
++#define IRONLAKE_DAC_M_MIN 79
++#define IRONLAKE_DAC_M_MAX 127
++#define IRONLAKE_DAC_P_MIN 5
++#define IRONLAKE_DAC_P_MAX 80
++#define IRONLAKE_DAC_P1_MIN 1
++#define IRONLAKE_DAC_P1_MAX 8
++#define IRONLAKE_DAC_P2_SLOW 10
++#define IRONLAKE_DAC_P2_FAST 5
++
++/* LVDS single-channel 120Mhz refclk */
++#define IRONLAKE_LVDS_S_N_MIN 1
++#define IRONLAKE_LVDS_S_N_MAX 3
++#define IRONLAKE_LVDS_S_M_MIN 79
++#define IRONLAKE_LVDS_S_M_MAX 118
++#define IRONLAKE_LVDS_S_P_MIN 28
++#define IRONLAKE_LVDS_S_P_MAX 112
++#define IRONLAKE_LVDS_S_P1_MIN 2
++#define IRONLAKE_LVDS_S_P1_MAX 8
++#define IRONLAKE_LVDS_S_P2_SLOW 14
++#define IRONLAKE_LVDS_S_P2_FAST 14
++
++/* LVDS dual-channel 120Mhz refclk */
++#define IRONLAKE_LVDS_D_N_MIN 1
++#define IRONLAKE_LVDS_D_N_MAX 3
++#define IRONLAKE_LVDS_D_M_MIN 79
++#define IRONLAKE_LVDS_D_M_MAX 127
++#define IRONLAKE_LVDS_D_P_MIN 14
++#define IRONLAKE_LVDS_D_P_MAX 56
++#define IRONLAKE_LVDS_D_P1_MIN 2
++#define IRONLAKE_LVDS_D_P1_MAX 8
++#define IRONLAKE_LVDS_D_P2_SLOW 7
++#define IRONLAKE_LVDS_D_P2_FAST 7
++
++/* LVDS single-channel 100Mhz refclk */
++#define IRONLAKE_LVDS_S_SSC_N_MIN 1
++#define IRONLAKE_LVDS_S_SSC_N_MAX 2
++#define IRONLAKE_LVDS_S_SSC_M_MIN 79
++#define IRONLAKE_LVDS_S_SSC_M_MAX 126
++#define IRONLAKE_LVDS_S_SSC_P_MIN 28
++#define IRONLAKE_LVDS_S_SSC_P_MAX 112
++#define IRONLAKE_LVDS_S_SSC_P1_MIN 2
++#define IRONLAKE_LVDS_S_SSC_P1_MAX 8
++#define IRONLAKE_LVDS_S_SSC_P2_SLOW 14
++#define IRONLAKE_LVDS_S_SSC_P2_FAST 14
++
++/* LVDS dual-channel 100Mhz refclk */
++#define IRONLAKE_LVDS_D_SSC_N_MIN 1
++#define IRONLAKE_LVDS_D_SSC_N_MAX 3
++#define IRONLAKE_LVDS_D_SSC_M_MIN 79
++#define IRONLAKE_LVDS_D_SSC_M_MAX 126
++#define IRONLAKE_LVDS_D_SSC_P_MIN 14
++#define IRONLAKE_LVDS_D_SSC_P_MAX 42
++#define IRONLAKE_LVDS_D_SSC_P1_MIN 2
++#define IRONLAKE_LVDS_D_SSC_P1_MAX 6
++#define IRONLAKE_LVDS_D_SSC_P2_SLOW 7
++#define IRONLAKE_LVDS_D_SSC_P2_FAST 7
++
++/* DisplayPort */
++#define IRONLAKE_DP_N_MIN 1
++#define IRONLAKE_DP_N_MAX 2
++#define IRONLAKE_DP_M_MIN 81
++#define IRONLAKE_DP_M_MAX 90
++#define IRONLAKE_DP_P_MIN 10
++#define IRONLAKE_DP_P_MAX 20
++#define IRONLAKE_DP_P2_FAST 10
++#define IRONLAKE_DP_P2_SLOW 10
++#define IRONLAKE_DP_P2_LIMIT 0
++#define IRONLAKE_DP_P1_MIN 1
++#define IRONLAKE_DP_P1_MAX 2
+
+ static bool
+ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+ int target, int refclk, intel_clock_t *best_clock);
+ static bool
+-intel_find_best_reduced_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+- int target, int refclk, intel_clock_t *best_clock);
+-static bool
+ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+ int target, int refclk, intel_clock_t *best_clock);
+-static bool
+-intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+- int target, int refclk, intel_clock_t *best_clock);
+
+ static bool
+ intel_find_pll_g4x_dp(const intel_limit_t *, struct drm_crtc *crtc,
+ int target, int refclk, intel_clock_t *best_clock);
+ static bool
+-intel_find_pll_igdng_dp(const intel_limit_t *, struct drm_crtc *crtc,
+- int target, int refclk, intel_clock_t *best_clock);
++intel_find_pll_ironlake_dp(const intel_limit_t *, struct drm_crtc *crtc,
++ int target, int refclk, intel_clock_t *best_clock);
+
+ static const intel_limit_t intel_limits_i8xx_dvo = {
+ .dot = { .min = I8XX_DOT_MIN, .max = I8XX_DOT_MAX },
+@@ -294,7 +347,6 @@ static const intel_limit_t intel_limits_i8xx_dvo = {
+ .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
+ .p2_slow = I8XX_P2_SLOW, .p2_fast = I8XX_P2_FAST },
+ .find_pll = intel_find_best_PLL,
+- .find_reduced_pll = intel_find_best_reduced_PLL,
+ };
+
+ static const intel_limit_t intel_limits_i8xx_lvds = {
+@@ -309,7 +361,6 @@ static const intel_limit_t intel_limits_i8xx_lvds = {
+ .p2 = { .dot_limit = I8XX_P2_SLOW_LIMIT,
+ .p2_slow = I8XX_P2_LVDS_SLOW, .p2_fast = I8XX_P2_LVDS_FAST },
+ .find_pll = intel_find_best_PLL,
+- .find_reduced_pll = intel_find_best_reduced_PLL,
+ };
+
+ static const intel_limit_t intel_limits_i9xx_sdvo = {
+@@ -324,7 +375,6 @@ static const intel_limit_t intel_limits_i9xx_sdvo = {
+ .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
+ .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST },
+ .find_pll = intel_find_best_PLL,
+- .find_reduced_pll = intel_find_best_reduced_PLL,
+ };
+
+ static const intel_limit_t intel_limits_i9xx_lvds = {
+@@ -342,7 +392,6 @@ static const intel_limit_t intel_limits_i9xx_lvds = {
+ .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
+ .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_FAST },
+ .find_pll = intel_find_best_PLL,
+- .find_reduced_pll = intel_find_best_reduced_PLL,
+ };
+
+ /* below parameter and function is for G4X Chipset Family*/
+@@ -360,7 +409,6 @@ static const intel_limit_t intel_limits_g4x_sdvo = {
+ .p2_fast = G4X_P2_SDVO_FAST
+ },
+ .find_pll = intel_g4x_find_best_PLL,
+- .find_reduced_pll = intel_g4x_find_best_PLL,
+ };
+
+ static const intel_limit_t intel_limits_g4x_hdmi = {
+@@ -377,7 +425,6 @@ static const intel_limit_t intel_limits_g4x_hdmi = {
+ .p2_fast = G4X_P2_HDMI_DAC_FAST
+ },
+ .find_pll = intel_g4x_find_best_PLL,
+- .find_reduced_pll = intel_g4x_find_best_PLL,
+ };
+
+ static const intel_limit_t intel_limits_g4x_single_channel_lvds = {
+@@ -402,7 +449,6 @@ static const intel_limit_t intel_limits_g4x_single_channel_lvds = {
+ .p2_fast = G4X_P2_SINGLE_CHANNEL_LVDS_FAST
+ },
+ .find_pll = intel_g4x_find_best_PLL,
+- .find_reduced_pll = intel_g4x_find_best_PLL,
+ };
+
+ static const intel_limit_t intel_limits_g4x_dual_channel_lvds = {
+@@ -427,7 +473,6 @@ static const intel_limit_t intel_limits_g4x_dual_channel_lvds = {
+ .p2_fast = G4X_P2_DUAL_CHANNEL_LVDS_FAST
+ },
+ .find_pll = intel_g4x_find_best_PLL,
+- .find_reduced_pll = intel_g4x_find_best_PLL,
+ };
+
+ static const intel_limit_t intel_limits_g4x_display_port = {
+@@ -453,74 +498,162 @@ static const intel_limit_t intel_limits_g4x_display_port = {
+ .find_pll = intel_find_pll_g4x_dp,
+ };
+
+-static const intel_limit_t intel_limits_igd_sdvo = {
++static const intel_limit_t intel_limits_pineview_sdvo = {
+ .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX},
+- .vco = { .min = IGD_VCO_MIN, .max = IGD_VCO_MAX },
+- .n = { .min = IGD_N_MIN, .max = IGD_N_MAX },
+- .m = { .min = IGD_M_MIN, .max = IGD_M_MAX },
+- .m1 = { .min = IGD_M1_MIN, .max = IGD_M1_MAX },
+- .m2 = { .min = IGD_M2_MIN, .max = IGD_M2_MAX },
++ .vco = { .min = PINEVIEW_VCO_MIN, .max = PINEVIEW_VCO_MAX },
++ .n = { .min = PINEVIEW_N_MIN, .max = PINEVIEW_N_MAX },
++ .m = { .min = PINEVIEW_M_MIN, .max = PINEVIEW_M_MAX },
++ .m1 = { .min = PINEVIEW_M1_MIN, .max = PINEVIEW_M1_MAX },
++ .m2 = { .min = PINEVIEW_M2_MIN, .max = PINEVIEW_M2_MAX },
+ .p = { .min = I9XX_P_SDVO_DAC_MIN, .max = I9XX_P_SDVO_DAC_MAX },
+ .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
+ .p2 = { .dot_limit = I9XX_P2_SDVO_DAC_SLOW_LIMIT,
+ .p2_slow = I9XX_P2_SDVO_DAC_SLOW, .p2_fast = I9XX_P2_SDVO_DAC_FAST },
+ .find_pll = intel_find_best_PLL,
+- .find_reduced_pll = intel_find_best_reduced_PLL,
+ };
+
+-static const intel_limit_t intel_limits_igd_lvds = {
++static const intel_limit_t intel_limits_pineview_lvds = {
+ .dot = { .min = I9XX_DOT_MIN, .max = I9XX_DOT_MAX },
+- .vco = { .min = IGD_VCO_MIN, .max = IGD_VCO_MAX },
+- .n = { .min = IGD_N_MIN, .max = IGD_N_MAX },
+- .m = { .min = IGD_M_MIN, .max = IGD_M_MAX },
+- .m1 = { .min = IGD_M1_MIN, .max = IGD_M1_MAX },
+- .m2 = { .min = IGD_M2_MIN, .max = IGD_M2_MAX },
+- .p = { .min = IGD_P_LVDS_MIN, .max = IGD_P_LVDS_MAX },
++ .vco = { .min = PINEVIEW_VCO_MIN, .max = PINEVIEW_VCO_MAX },
++ .n = { .min = PINEVIEW_N_MIN, .max = PINEVIEW_N_MAX },
++ .m = { .min = PINEVIEW_M_MIN, .max = PINEVIEW_M_MAX },
++ .m1 = { .min = PINEVIEW_M1_MIN, .max = PINEVIEW_M1_MAX },
++ .m2 = { .min = PINEVIEW_M2_MIN, .max = PINEVIEW_M2_MAX },
++ .p = { .min = PINEVIEW_P_LVDS_MIN, .max = PINEVIEW_P_LVDS_MAX },
+ .p1 = { .min = I9XX_P1_MIN, .max = I9XX_P1_MAX },
+- /* IGD only supports single-channel mode. */
++ /* Pineview only supports single-channel mode. */
+ .p2 = { .dot_limit = I9XX_P2_LVDS_SLOW_LIMIT,
+ .p2_slow = I9XX_P2_LVDS_SLOW, .p2_fast = I9XX_P2_LVDS_SLOW },
+ .find_pll = intel_find_best_PLL,
+- .find_reduced_pll = intel_find_best_reduced_PLL,
+ };
+
+-static const intel_limit_t intel_limits_igdng_sdvo = {
+- .dot = { .min = IGDNG_DOT_MIN, .max = IGDNG_DOT_MAX },
+- .vco = { .min = IGDNG_VCO_MIN, .max = IGDNG_VCO_MAX },
+- .n = { .min = IGDNG_N_MIN, .max = IGDNG_N_MAX },
+- .m = { .min = IGDNG_M_MIN, .max = IGDNG_M_MAX },
+- .m1 = { .min = IGDNG_M1_MIN, .max = IGDNG_M1_MAX },
+- .m2 = { .min = IGDNG_M2_MIN, .max = IGDNG_M2_MAX },
+- .p = { .min = IGDNG_P_SDVO_DAC_MIN, .max = IGDNG_P_SDVO_DAC_MAX },
+- .p1 = { .min = IGDNG_P1_MIN, .max = IGDNG_P1_MAX },
+- .p2 = { .dot_limit = IGDNG_P2_DOT_LIMIT,
+- .p2_slow = IGDNG_P2_SDVO_DAC_SLOW,
+- .p2_fast = IGDNG_P2_SDVO_DAC_FAST },
+- .find_pll = intel_igdng_find_best_PLL,
++static const intel_limit_t intel_limits_ironlake_dac = {
++ .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
++ .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
++ .n = { .min = IRONLAKE_DAC_N_MIN, .max = IRONLAKE_DAC_N_MAX },
++ .m = { .min = IRONLAKE_DAC_M_MIN, .max = IRONLAKE_DAC_M_MAX },
++ .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
++ .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
++ .p = { .min = IRONLAKE_DAC_P_MIN, .max = IRONLAKE_DAC_P_MAX },
++ .p1 = { .min = IRONLAKE_DAC_P1_MIN, .max = IRONLAKE_DAC_P1_MAX },
++ .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
++ .p2_slow = IRONLAKE_DAC_P2_SLOW,
++ .p2_fast = IRONLAKE_DAC_P2_FAST },
++ .find_pll = intel_g4x_find_best_PLL,
+ };
+
+-static const intel_limit_t intel_limits_igdng_lvds = {
+- .dot = { .min = IGDNG_DOT_MIN, .max = IGDNG_DOT_MAX },
+- .vco = { .min = IGDNG_VCO_MIN, .max = IGDNG_VCO_MAX },
+- .n = { .min = IGDNG_N_MIN, .max = IGDNG_N_MAX },
+- .m = { .min = IGDNG_M_MIN, .max = IGDNG_M_MAX },
+- .m1 = { .min = IGDNG_M1_MIN, .max = IGDNG_M1_MAX },
+- .m2 = { .min = IGDNG_M2_MIN, .max = IGDNG_M2_MAX },
+- .p = { .min = IGDNG_P_LVDS_MIN, .max = IGDNG_P_LVDS_MAX },
+- .p1 = { .min = IGDNG_P1_MIN, .max = IGDNG_P1_MAX },
+- .p2 = { .dot_limit = IGDNG_P2_DOT_LIMIT,
+- .p2_slow = IGDNG_P2_LVDS_SLOW,
+- .p2_fast = IGDNG_P2_LVDS_FAST },
+- .find_pll = intel_igdng_find_best_PLL,
++static const intel_limit_t intel_limits_ironlake_single_lvds = {
++ .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
++ .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
++ .n = { .min = IRONLAKE_LVDS_S_N_MIN, .max = IRONLAKE_LVDS_S_N_MAX },
++ .m = { .min = IRONLAKE_LVDS_S_M_MIN, .max = IRONLAKE_LVDS_S_M_MAX },
++ .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
++ .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
++ .p = { .min = IRONLAKE_LVDS_S_P_MIN, .max = IRONLAKE_LVDS_S_P_MAX },
++ .p1 = { .min = IRONLAKE_LVDS_S_P1_MIN, .max = IRONLAKE_LVDS_S_P1_MAX },
++ .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
++ .p2_slow = IRONLAKE_LVDS_S_P2_SLOW,
++ .p2_fast = IRONLAKE_LVDS_S_P2_FAST },
++ .find_pll = intel_g4x_find_best_PLL,
+ };
+
+-static const intel_limit_t *intel_igdng_limit(struct drm_crtc *crtc)
++static const intel_limit_t intel_limits_ironlake_dual_lvds = {
++ .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
++ .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
++ .n = { .min = IRONLAKE_LVDS_D_N_MIN, .max = IRONLAKE_LVDS_D_N_MAX },
++ .m = { .min = IRONLAKE_LVDS_D_M_MIN, .max = IRONLAKE_LVDS_D_M_MAX },
++ .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
++ .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
++ .p = { .min = IRONLAKE_LVDS_D_P_MIN, .max = IRONLAKE_LVDS_D_P_MAX },
++ .p1 = { .min = IRONLAKE_LVDS_D_P1_MIN, .max = IRONLAKE_LVDS_D_P1_MAX },
++ .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
++ .p2_slow = IRONLAKE_LVDS_D_P2_SLOW,
++ .p2_fast = IRONLAKE_LVDS_D_P2_FAST },
++ .find_pll = intel_g4x_find_best_PLL,
++};
++
++static const intel_limit_t intel_limits_ironlake_single_lvds_100m = {
++ .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
++ .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
++ .n = { .min = IRONLAKE_LVDS_S_SSC_N_MIN, .max = IRONLAKE_LVDS_S_SSC_N_MAX },
++ .m = { .min = IRONLAKE_LVDS_S_SSC_M_MIN, .max = IRONLAKE_LVDS_S_SSC_M_MAX },
++ .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
++ .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
++ .p = { .min = IRONLAKE_LVDS_S_SSC_P_MIN, .max = IRONLAKE_LVDS_S_SSC_P_MAX },
++ .p1 = { .min = IRONLAKE_LVDS_S_SSC_P1_MIN,.max = IRONLAKE_LVDS_S_SSC_P1_MAX },
++ .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
++ .p2_slow = IRONLAKE_LVDS_S_SSC_P2_SLOW,
++ .p2_fast = IRONLAKE_LVDS_S_SSC_P2_FAST },
++ .find_pll = intel_g4x_find_best_PLL,
++};
++
++static const intel_limit_t intel_limits_ironlake_dual_lvds_100m = {
++ .dot = { .min = IRONLAKE_DOT_MIN, .max = IRONLAKE_DOT_MAX },
++ .vco = { .min = IRONLAKE_VCO_MIN, .max = IRONLAKE_VCO_MAX },
++ .n = { .min = IRONLAKE_LVDS_D_SSC_N_MIN, .max = IRONLAKE_LVDS_D_SSC_N_MAX },
++ .m = { .min = IRONLAKE_LVDS_D_SSC_M_MIN, .max = IRONLAKE_LVDS_D_SSC_M_MAX },
++ .m1 = { .min = IRONLAKE_M1_MIN, .max = IRONLAKE_M1_MAX },
++ .m2 = { .min = IRONLAKE_M2_MIN, .max = IRONLAKE_M2_MAX },
++ .p = { .min = IRONLAKE_LVDS_D_SSC_P_MIN, .max = IRONLAKE_LVDS_D_SSC_P_MAX },
++ .p1 = { .min = IRONLAKE_LVDS_D_SSC_P1_MIN,.max = IRONLAKE_LVDS_D_SSC_P1_MAX },
++ .p2 = { .dot_limit = IRONLAKE_P2_DOT_LIMIT,
++ .p2_slow = IRONLAKE_LVDS_D_SSC_P2_SLOW,
++ .p2_fast = IRONLAKE_LVDS_D_SSC_P2_FAST },
++ .find_pll = intel_g4x_find_best_PLL,
++};
++
++static const intel_limit_t intel_limits_ironlake_display_port = {
++ .dot = { .min = IRONLAKE_DOT_MIN,
++ .max = IRONLAKE_DOT_MAX },
++ .vco = { .min = IRONLAKE_VCO_MIN,
++ .max = IRONLAKE_VCO_MAX},
++ .n = { .min = IRONLAKE_DP_N_MIN,
++ .max = IRONLAKE_DP_N_MAX },
++ .m = { .min = IRONLAKE_DP_M_MIN,
++ .max = IRONLAKE_DP_M_MAX },
++ .m1 = { .min = IRONLAKE_M1_MIN,
++ .max = IRONLAKE_M1_MAX },
++ .m2 = { .min = IRONLAKE_M2_MIN,
++ .max = IRONLAKE_M2_MAX },
++ .p = { .min = IRONLAKE_DP_P_MIN,
++ .max = IRONLAKE_DP_P_MAX },
++ .p1 = { .min = IRONLAKE_DP_P1_MIN,
++ .max = IRONLAKE_DP_P1_MAX},
++ .p2 = { .dot_limit = IRONLAKE_DP_P2_LIMIT,
++ .p2_slow = IRONLAKE_DP_P2_SLOW,
++ .p2_fast = IRONLAKE_DP_P2_FAST },
++ .find_pll = intel_find_pll_ironlake_dp,
++};
++
++static const intel_limit_t *intel_ironlake_limit(struct drm_crtc *crtc)
+ {
++ struct drm_device *dev = crtc->dev;
++ struct drm_i915_private *dev_priv = dev->dev_private;
+ const intel_limit_t *limit;
+- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+- limit = &intel_limits_igdng_lvds;
++ int refclk = 120;
++
++ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
++ if (dev_priv->lvds_use_ssc && dev_priv->lvds_ssc_freq == 100)
++ refclk = 100;
++
++ if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) ==
++ LVDS_CLKB_POWER_UP) {
++ /* LVDS dual channel */
++ if (refclk == 100)
++ limit = &intel_limits_ironlake_dual_lvds_100m;
++ else
++ limit = &intel_limits_ironlake_dual_lvds;
++ } else {
++ if (refclk == 100)
++ limit = &intel_limits_ironlake_single_lvds_100m;
++ else
++ limit = &intel_limits_ironlake_single_lvds;
++ }
++ } else if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT) ||
++ HAS_eDP)
++ limit = &intel_limits_ironlake_display_port;
+ else
+- limit = &intel_limits_igdng_sdvo;
++ limit = &intel_limits_ironlake_dac;
+
+ return limit;
+ }
+@@ -557,20 +690,20 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
+ struct drm_device *dev = crtc->dev;
+ const intel_limit_t *limit;
+
+- if (IS_IGDNG(dev))
+- limit = intel_igdng_limit(crtc);
++ if (IS_IRONLAKE(dev))
++ limit = intel_ironlake_limit(crtc);
+ else if (IS_G4X(dev)) {
+ limit = intel_g4x_limit(crtc);
+- } else if (IS_I9XX(dev) && !IS_IGD(dev)) {
++ } else if (IS_I9XX(dev) && !IS_PINEVIEW(dev)) {
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+ limit = &intel_limits_i9xx_lvds;
+ else
+ limit = &intel_limits_i9xx_sdvo;
+- } else if (IS_IGD(dev)) {
++ } else if (IS_PINEVIEW(dev)) {
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+- limit = &intel_limits_igd_lvds;
++ limit = &intel_limits_pineview_lvds;
+ else
+- limit = &intel_limits_igd_sdvo;
++ limit = &intel_limits_pineview_sdvo;
+ } else {
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS))
+ limit = &intel_limits_i8xx_lvds;
+@@ -580,8 +713,8 @@ static const intel_limit_t *intel_limit(struct drm_crtc *crtc)
+ return limit;
+ }
+
+-/* m1 is reserved as 0 in IGD, n is a ring counter */
+-static void igd_clock(int refclk, intel_clock_t *clock)
++/* m1 is reserved as 0 in Pineview, n is a ring counter */
++static void pineview_clock(int refclk, intel_clock_t *clock)
+ {
+ clock->m = clock->m2 + 2;
+ clock->p = clock->p1 * clock->p2;
+@@ -591,8 +724,8 @@ static void igd_clock(int refclk, intel_clock_t *clock)
+
+ static void intel_clock(struct drm_device *dev, int refclk, intel_clock_t *clock)
+ {
+- if (IS_IGD(dev)) {
+- igd_clock(refclk, clock);
++ if (IS_PINEVIEW(dev)) {
++ pineview_clock(refclk, clock);
+ return;
+ }
+ clock->m = 5 * (clock->m1 + 2) + (clock->m2 + 2);
+@@ -657,7 +790,7 @@ static bool intel_PLL_is_valid(struct drm_crtc *crtc, intel_clock_t *clock)
+ INTELPllInvalid ("m2 out of range\n");
+ if (clock->m1 < limit->m1.min || limit->m1.max < clock->m1)
+ INTELPllInvalid ("m1 out of range\n");
+- if (clock->m1 <= clock->m2 && !IS_IGD(dev))
++ if (clock->m1 <= clock->m2 && !IS_PINEVIEW(dev))
+ INTELPllInvalid ("m1 <= m2\n");
+ if (clock->m < limit->m.min || limit->m.max < clock->m)
+ INTELPllInvalid ("m out of range\n");
+@@ -706,16 +839,17 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+
+ memset (best_clock, 0, sizeof (*best_clock));
+
+- for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) {
+- for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max;
+- clock.m1++) {
+- for (clock.m2 = limit->m2.min;
+- clock.m2 <= limit->m2.max; clock.m2++) {
+- /* m1 is always 0 in IGD */
+- if (clock.m2 >= clock.m1 && !IS_IGD(dev))
+- break;
+- for (clock.n = limit->n.min;
+- clock.n <= limit->n.max; clock.n++) {
++ for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max;
++ clock.m1++) {
++ for (clock.m2 = limit->m2.min;
++ clock.m2 <= limit->m2.max; clock.m2++) {
++ /* m1 is always 0 in Pineview */
++ if (clock.m2 >= clock.m1 && !IS_PINEVIEW(dev))
++ break;
++ for (clock.n = limit->n.min;
++ clock.n <= limit->n.max; clock.n++) {
++ for (clock.p1 = limit->p1.min;
++ clock.p1 <= limit->p1.max; clock.p1++) {
+ int this_err;
+
+ intel_clock(dev, refclk, &clock);
+@@ -736,46 +870,6 @@ intel_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+ return (err != target);
+ }
+
+-
+-static bool
+-intel_find_best_reduced_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+- int target, int refclk, intel_clock_t *best_clock)
+-
+-{
+- struct drm_device *dev = crtc->dev;
+- intel_clock_t clock;
+- int err = target;
+- bool found = false;
+-
+- memcpy(&clock, best_clock, sizeof(intel_clock_t));
+-
+- for (clock.m1 = limit->m1.min; clock.m1 <= limit->m1.max; clock.m1++) {
+- for (clock.m2 = limit->m2.min; clock.m2 <= limit->m2.max; clock.m2++) {
+- /* m1 is always 0 in IGD */
+- if (clock.m2 >= clock.m1 && !IS_IGD(dev))
+- break;
+- for (clock.n = limit->n.min; clock.n <= limit->n.max;
+- clock.n++) {
+- int this_err;
+-
+- intel_clock(dev, refclk, &clock);
+-
+- if (!intel_PLL_is_valid(crtc, &clock))
+- continue;
+-
+- this_err = abs(clock.dot - target);
+- if (this_err < err) {
+- *best_clock = clock;
+- err = this_err;
+- found = true;
+- }
+- }
+- }
+- }
+-
+- return found;
+-}
+-
+ static bool
+ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+ int target, int refclk, intel_clock_t *best_clock)
+@@ -790,7 +884,13 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+ found = false;
+
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+- if ((I915_READ(LVDS) & LVDS_CLKB_POWER_MASK) ==
++ int lvds_reg;
++
++ if (IS_IRONLAKE(dev))
++ lvds_reg = PCH_LVDS;
++ else
++ lvds_reg = LVDS;
++ if ((I915_READ(lvds_reg) & LVDS_CLKB_POWER_MASK) ==
+ LVDS_CLKB_POWER_UP)
+ clock.p2 = limit->p2.p2_fast;
+ else
+@@ -833,11 +933,16 @@ intel_g4x_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+ }
+
+ static bool
+-intel_find_pll_igdng_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
+- int target, int refclk, intel_clock_t *best_clock)
++intel_find_pll_ironlake_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
++ int target, int refclk, intel_clock_t *best_clock)
+ {
+ struct drm_device *dev = crtc->dev;
+ intel_clock_t clock;
++
++ /* return directly when it is eDP */
++ if (HAS_eDP)
++ return true;
++
+ if (target < 200000) {
+ clock.n = 1;
+ clock.p1 = 2;
+@@ -856,68 +961,6 @@ intel_find_pll_igdng_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
+ return true;
+ }
+
+-static bool
+-intel_igdng_find_best_PLL(const intel_limit_t *limit, struct drm_crtc *crtc,
+- int target, int refclk, intel_clock_t *best_clock)
+-{
+- struct drm_device *dev = crtc->dev;
+- struct drm_i915_private *dev_priv = dev->dev_private;
+- intel_clock_t clock;
+- int err_most = 47;
+- int err_min = 10000;
+-
+- /* eDP has only 2 clock choice, no n/m/p setting */
+- if (HAS_eDP)
+- return true;
+-
+- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_DISPLAYPORT))
+- return intel_find_pll_igdng_dp(limit, crtc, target,
+- refclk, best_clock);
+-
+- if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+- if ((I915_READ(PCH_LVDS) & LVDS_CLKB_POWER_MASK) ==
+- LVDS_CLKB_POWER_UP)
+- clock.p2 = limit->p2.p2_fast;
+- else
+- clock.p2 = limit->p2.p2_slow;
+- } else {
+- if (target < limit->p2.dot_limit)
+- clock.p2 = limit->p2.p2_slow;
+- else
+- clock.p2 = limit->p2.p2_fast;
+- }
+-
+- memset(best_clock, 0, sizeof(*best_clock));
+- for (clock.p1 = limit->p1.max; clock.p1 >= limit->p1.min; clock.p1--) {
+- /* based on hardware requriment prefer smaller n to precision */
+- for (clock.n = limit->n.min; clock.n <= limit->n.max; clock.n++) {
+- /* based on hardware requirment prefere larger m1,m2 */
+- for (clock.m1 = limit->m1.max;
+- clock.m1 >= limit->m1.min; clock.m1--) {
+- for (clock.m2 = limit->m2.max;
+- clock.m2 >= limit->m2.min; clock.m2--) {
+- int this_err;
+-
+- intel_clock(dev, refclk, &clock);
+- if (!intel_PLL_is_valid(crtc, &clock))
+- continue;
+- this_err = abs((10000 - (target*10000/clock.dot)));
+- if (this_err < err_most) {
+- *best_clock = clock;
+- /* found on first matching */
+- goto out;
+- } else if (this_err < err_min) {
+- *best_clock = clock;
+- err_min = this_err;
+- }
+- }
+- }
+- }
+- }
+-out:
+- return true;
+-}
+-
+ /* DisplayPort has only two frequencies, 162MHz and 270MHz */
+ static bool
+ intel_find_pll_g4x_dp(const intel_limit_t *limit, struct drm_crtc *crtc,
+@@ -949,7 +992,7 @@ void
+ intel_wait_for_vblank(struct drm_device *dev)
+ {
+ /* Wait for 20ms, i.e. one cycle at 50hz. */
+- mdelay(20);
++ msleep(20);
+ }
+
+ /* Parameters have changed, update FBC info */
+@@ -988,13 +1031,15 @@ static void i8xx_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
+
+ /* enable it... */
+ fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC;
++ if (IS_I945GM(dev))
++ fbc_ctl |= FBC_C3_IDLE; /* 945 needs special SR handling */
+ fbc_ctl |= (dev_priv->cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT;
+ fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT;
+ if (obj_priv->tiling_mode != I915_TILING_NONE)
+ fbc_ctl |= dev_priv->cfb_fence;
+ I915_WRITE(FBC_CONTROL, fbc_ctl);
+
+- DRM_DEBUG("enabled FBC, pitch %ld, yoff %d, plane %d, ",
++ DRM_DEBUG_KMS("enabled FBC, pitch %ld, yoff %d, plane %d, ",
+ dev_priv->cfb_pitch, crtc->y, dev_priv->cfb_plane);
+ }
+
+@@ -1017,7 +1062,7 @@ void i8xx_disable_fbc(struct drm_device *dev)
+
+ intel_wait_for_vblank(dev);
+
+- DRM_DEBUG("disabled FBC\n");
++ DRM_DEBUG_KMS("disabled FBC\n");
+ }
+
+ static bool i8xx_fbc_enabled(struct drm_crtc *crtc)
+@@ -1062,7 +1107,7 @@ static void g4x_enable_fbc(struct drm_crtc *crtc, unsigned long interval)
+ /* enable it... */
+ I915_WRITE(DPFC_CONTROL, I915_READ(DPFC_CONTROL) | DPFC_CTL_EN);
+
+- DRM_DEBUG("enabled fbc on plane %d\n", intel_crtc->plane);
++ DRM_DEBUG_KMS("enabled fbc on plane %d\n", intel_crtc->plane);
+ }
+
+ void g4x_disable_fbc(struct drm_device *dev)
+@@ -1076,7 +1121,7 @@ void g4x_disable_fbc(struct drm_device *dev)
+ I915_WRITE(DPFC_CONTROL, dpfc_ctl);
+ intel_wait_for_vblank(dev);
+
+- DRM_DEBUG("disabled FBC\n");
++ DRM_DEBUG_KMS("disabled FBC\n");
+ }
+
+ static bool g4x_fbc_enabled(struct drm_crtc *crtc)
+@@ -1141,25 +1186,27 @@ static void intel_update_fbc(struct drm_crtc *crtc,
+ * - going to an unsupported config (interlace, pixel multiply, etc.)
+ */
+ if (intel_fb->obj->size > dev_priv->cfb_size) {
+- DRM_DEBUG("framebuffer too large, disabling compression\n");
++ DRM_DEBUG_KMS("framebuffer too large, disabling "
++ "compression\n");
+ goto out_disable;
+ }
+ if ((mode->flags & DRM_MODE_FLAG_INTERLACE) ||
+ (mode->flags & DRM_MODE_FLAG_DBLSCAN)) {
+- DRM_DEBUG("mode incompatible with compression, disabling\n");
++ DRM_DEBUG_KMS("mode incompatible with compression, "
++ "disabling\n");
+ goto out_disable;
+ }
+ if ((mode->hdisplay > 2048) ||
+ (mode->vdisplay > 1536)) {
+- DRM_DEBUG("mode too large for compression, disabling\n");
++ DRM_DEBUG_KMS("mode too large for compression, disabling\n");
+ goto out_disable;
+ }
+ if ((IS_I915GM(dev) || IS_I945GM(dev)) && plane != 0) {
+- DRM_DEBUG("plane not 0, disabling compression\n");
++ DRM_DEBUG_KMS("plane not 0, disabling compression\n");
+ goto out_disable;
+ }
+ if (obj_priv->tiling_mode != I915_TILING_X) {
+- DRM_DEBUG("framebuffer not tiled, disabling compression\n");
++ DRM_DEBUG_KMS("framebuffer not tiled, disabling compression\n");
+ goto out_disable;
+ }
+
+@@ -1181,13 +1228,57 @@ static void intel_update_fbc(struct drm_crtc *crtc,
+ return;
+
+ out_disable:
+- DRM_DEBUG("unsupported config, disabling FBC\n");
++ DRM_DEBUG_KMS("unsupported config, disabling FBC\n");
+ /* Multiple disables should be harmless */
+ if (dev_priv->display.fbc_enabled(crtc))
+ dev_priv->display.disable_fbc(dev);
+ }
+
+ static int
++intel_pin_and_fence_fb_obj(struct drm_device *dev, struct drm_gem_object *obj)
++{
++ struct drm_i915_gem_object *obj_priv = obj->driver_private;
++ u32 alignment;
++ int ret;
++
++ switch (obj_priv->tiling_mode) {
++ case I915_TILING_NONE:
++ alignment = 64 * 1024;
++ break;
++ case I915_TILING_X:
++ /* pin() will align the object as required by fence */
++ alignment = 0;
++ break;
++ case I915_TILING_Y:
++ /* FIXME: Is this true? */
++ DRM_ERROR("Y tiled not allowed for scan out buffers\n");
++ return -EINVAL;
++ default:
++ BUG();
++ }
++
++ ret = i915_gem_object_pin(obj, alignment);
++ if (ret != 0)
++ return ret;
++
++ /* Install a fence for tiled scan-out. Pre-i965 always needs a
++ * fence, whereas 965+ only requires a fence if using
++ * framebuffer compression. For simplicity, we always install
++ * a fence as the cost is not that onerous.
++ */
++ if (obj_priv->fence_reg == I915_FENCE_REG_NONE &&
++ obj_priv->tiling_mode != I915_TILING_NONE) {
++ ret = i915_gem_object_get_fence_reg(obj);
++ if (ret != 0) {
++ i915_gem_object_unpin(obj);
++ return ret;
++ }
++ }
++
++ return 0;
++}
++
++static int
+ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb)
+ {
+@@ -1206,12 +1297,12 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
+ int dspstride = (plane == 0) ? DSPASTRIDE : DSPBSTRIDE;
+ int dsptileoff = (plane == 0 ? DSPATILEOFF : DSPBTILEOFF);
+ int dspcntr_reg = (plane == 0) ? DSPACNTR : DSPBCNTR;
+- u32 dspcntr, alignment;
++ u32 dspcntr;
+ int ret;
+
+ /* no fb bound */
+ if (!crtc->fb) {
+- DRM_DEBUG("No FB bound\n");
++ DRM_DEBUG_KMS("No FB bound\n");
+ return 0;
+ }
+
+@@ -1228,50 +1319,20 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
+ obj = intel_fb->obj;
+ obj_priv = obj->driver_private;
+
+- switch (obj_priv->tiling_mode) {
+- case I915_TILING_NONE:
+- alignment = 64 * 1024;
+- break;
+- case I915_TILING_X:
+- /* pin() will align the object as required by fence */
+- alignment = 0;
+- break;
+- case I915_TILING_Y:
+- /* FIXME: Is this true? */
+- DRM_ERROR("Y tiled not allowed for scan out buffers\n");
+- return -EINVAL;
+- default:
+- BUG();
+- }
+-
+ mutex_lock(&dev->struct_mutex);
+- ret = i915_gem_object_pin(obj, alignment);
++ ret = intel_pin_and_fence_fb_obj(dev, obj);
+ if (ret != 0) {
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
+
+- ret = i915_gem_object_set_to_gtt_domain(obj, 1);
++ ret = i915_gem_object_set_to_display_plane(obj);
+ if (ret != 0) {
+ i915_gem_object_unpin(obj);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
+
+- /* Install a fence for tiled scan-out. Pre-i965 always needs a fence,
+- * whereas 965+ only requires a fence if using framebuffer compression.
+- * For simplicity, we always install a fence as the cost is not that onerous.
+- */
+- if (obj_priv->fence_reg == I915_FENCE_REG_NONE &&
+- obj_priv->tiling_mode != I915_TILING_NONE) {
+- ret = i915_gem_object_get_fence_reg(obj);
+- if (ret != 0) {
+- i915_gem_object_unpin(obj);
+- mutex_unlock(&dev->struct_mutex);
+- return ret;
+- }
+- }
+-
+ dspcntr = I915_READ(dspcntr_reg);
+ /* Mask out pixel format bits in case we change it */
+ dspcntr &= ~DISPPLANE_PIXFORMAT_MASK;
+@@ -1287,7 +1348,10 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
+ break;
+ case 24:
+ case 32:
+- dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
++ if (crtc->fb->depth == 30)
++ dspcntr |= DISPPLANE_32BPP_30BIT_NO_ALPHA;
++ else
++ dspcntr |= DISPPLANE_32BPP_NO_ALPHA;
+ break;
+ default:
+ DRM_ERROR("Unknown color depth\n");
+@@ -1302,7 +1366,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
+ dspcntr &= ~DISPPLANE_TILED;
+ }
+
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ /* must disable */
+ dspcntr |= DISPPLANE_TRICKLE_FEED_DISABLE;
+
+@@ -1311,7 +1375,7 @@ intel_pipe_set_base(struct drm_crtc *crtc, int x, int y,
+ Start = obj_priv->gtt_offset;
+ Offset = y * crtc->fb->pitch + x * (crtc->fb->bits_per_pixel / 8);
+
+- DRM_DEBUG("Writing base %08lX %08lX %d %d\n", Start, Offset, x, y);
++ DRM_DEBUG_KMS("Writing base %08lX %08lX %d %d\n", Start, Offset, x, y);
+ I915_WRITE(dspstride, crtc->fb->pitch);
+ if (IS_I965G(dev)) {
+ I915_WRITE(dspbase, Offset);
+@@ -1363,7 +1427,7 @@ static void i915_disable_vga (struct drm_device *dev)
+ u8 sr1;
+ u32 vga_reg;
+
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ vga_reg = CPU_VGACNTRL;
+ else
+ vga_reg = VGACNTRL;
+@@ -1379,19 +1443,19 @@ static void i915_disable_vga (struct drm_device *dev)
+ I915_WRITE(vga_reg, VGA_DISP_DISABLE);
+ }
+
+-static void igdng_disable_pll_edp (struct drm_crtc *crtc)
++static void ironlake_disable_pll_edp (struct drm_crtc *crtc)
+ {
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 dpa_ctl;
+
+- DRM_DEBUG("\n");
++ DRM_DEBUG_KMS("\n");
+ dpa_ctl = I915_READ(DP_A);
+ dpa_ctl &= ~DP_PLL_ENABLE;
+ I915_WRITE(DP_A, dpa_ctl);
+ }
+
+-static void igdng_enable_pll_edp (struct drm_crtc *crtc)
++static void ironlake_enable_pll_edp (struct drm_crtc *crtc)
+ {
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+@@ -1404,13 +1468,13 @@ static void igdng_enable_pll_edp (struct drm_crtc *crtc)
+ }
+
+
+-static void igdng_set_pll_edp (struct drm_crtc *crtc, int clock)
++static void ironlake_set_pll_edp (struct drm_crtc *crtc, int clock)
+ {
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 dpa_ctl;
+
+- DRM_DEBUG("eDP PLL enable for clock %d\n", clock);
++ DRM_DEBUG_KMS("eDP PLL enable for clock %d\n", clock);
+ dpa_ctl = I915_READ(DP_A);
+ dpa_ctl &= ~DP_PLL_FREQ_MASK;
+
+@@ -1440,7 +1504,7 @@ static void igdng_set_pll_edp (struct drm_crtc *crtc, int clock)
+ udelay(500);
+ }
+
+-static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
++static void ironlake_crtc_dpms(struct drm_crtc *crtc, int mode)
+ {
+ struct drm_device *dev = crtc->dev;
+ struct drm_i915_private *dev_priv = dev->dev_private;
+@@ -1485,7 +1549,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
+ case DRM_MODE_DPMS_ON:
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+- DRM_DEBUG("crtc %d dpms on\n", pipe);
++ DRM_DEBUG_KMS("crtc %d dpms on\n", pipe);
+
+ if (intel_pipe_has_type(crtc, INTEL_OUTPUT_LVDS)) {
+ temp = I915_READ(PCH_LVDS);
+@@ -1497,7 +1561,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
+
+ if (HAS_eDP) {
+ /* enable eDP PLL */
+- igdng_enable_pll_edp(crtc);
++ ironlake_enable_pll_edp(crtc);
+ } else {
+ /* enable PCH DPLL */
+ temp = I915_READ(pch_dpll_reg);
+@@ -1520,7 +1584,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
+ I915_READ(fdi_rx_reg);
+ udelay(200);
+
+- /* Enable CPU FDI TX PLL, always on for IGDNG */
++ /* Enable CPU FDI TX PLL, always on for Ironlake */
+ temp = I915_READ(fdi_tx_reg);
+ if ((temp & FDI_TX_PLL_ENABLE) == 0) {
+ I915_WRITE(fdi_tx_reg, temp | FDI_TX_PLL_ENABLE);
+@@ -1587,12 +1651,13 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
+ udelay(150);
+
+ temp = I915_READ(fdi_rx_iir_reg);
+- DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
++ DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp);
+
+ if ((temp & FDI_RX_BIT_LOCK) == 0) {
+ for (j = 0; j < tries; j++) {
+ temp = I915_READ(fdi_rx_iir_reg);
+- DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
++ DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n",
++ temp);
+ if (temp & FDI_RX_BIT_LOCK)
+ break;
+ udelay(200);
+@@ -1601,11 +1666,11 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
+ I915_WRITE(fdi_rx_iir_reg,
+ temp | FDI_RX_BIT_LOCK);
+ else
+- DRM_DEBUG("train 1 fail\n");
++ DRM_DEBUG_KMS("train 1 fail\n");
+ } else {
+ I915_WRITE(fdi_rx_iir_reg,
+ temp | FDI_RX_BIT_LOCK);
+- DRM_DEBUG("train 1 ok 2!\n");
++ DRM_DEBUG_KMS("train 1 ok 2!\n");
+ }
+ temp = I915_READ(fdi_tx_reg);
+ temp &= ~FDI_LINK_TRAIN_NONE;
+@@ -1620,12 +1685,13 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
+ udelay(150);
+
+ temp = I915_READ(fdi_rx_iir_reg);
+- DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
++ DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n", temp);
+
+ if ((temp & FDI_RX_SYMBOL_LOCK) == 0) {
+ for (j = 0; j < tries; j++) {
+ temp = I915_READ(fdi_rx_iir_reg);
+- DRM_DEBUG("FDI_RX_IIR 0x%x\n", temp);
++ DRM_DEBUG_KMS("FDI_RX_IIR 0x%x\n",
++ temp);
+ if (temp & FDI_RX_SYMBOL_LOCK)
+ break;
+ udelay(200);
+@@ -1633,15 +1699,15 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
+ if (j != tries) {
+ I915_WRITE(fdi_rx_iir_reg,
+ temp | FDI_RX_SYMBOL_LOCK);
+- DRM_DEBUG("train 2 ok 1!\n");
++ DRM_DEBUG_KMS("train 2 ok 1!\n");
+ } else
+- DRM_DEBUG("train 2 fail\n");
++ DRM_DEBUG_KMS("train 2 fail\n");
+ } else {
+ I915_WRITE(fdi_rx_iir_reg,
+ temp | FDI_RX_SYMBOL_LOCK);
+- DRM_DEBUG("train 2 ok 2!\n");
++ DRM_DEBUG_KMS("train 2 ok 2!\n");
+ }
+- DRM_DEBUG("train done\n");
++ DRM_DEBUG_KMS("train done\n");
+
+ /* set transcoder timing */
+ I915_WRITE(trans_htot_reg, I915_READ(cpu_htot_reg));
+@@ -1689,8 +1755,9 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
+
+ break;
+ case DRM_MODE_DPMS_OFF:
+- DRM_DEBUG("crtc %d dpms off\n", pipe);
++ DRM_DEBUG_KMS("crtc %d dpms off\n", pipe);
+
++ drm_vblank_off(dev, pipe);
+ /* Disable display plane */
+ temp = I915_READ(dspcntr_reg);
+ if ((temp & DISPLAY_PLANE_ENABLE) != 0) {
+@@ -1715,12 +1782,13 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
+ udelay(500);
+ continue;
+ } else {
+- DRM_DEBUG("pipe %d off delay\n", pipe);
++ DRM_DEBUG_KMS("pipe %d off delay\n",
++ pipe);
+ break;
+ }
+ }
+ } else
+- DRM_DEBUG("crtc %d is disabled\n", pipe);
++ DRM_DEBUG_KMS("crtc %d is disabled\n", pipe);
+
+ udelay(100);
+
+@@ -1779,7 +1847,8 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
+ udelay(500);
+ continue;
+ } else {
+- DRM_DEBUG("transcoder %d off delay\n", pipe);
++ DRM_DEBUG_KMS("transcoder %d off "
++ "delay\n", pipe);
+ break;
+ }
+ }
+@@ -1800,7 +1869,7 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
+ }
+
+ if (HAS_eDP) {
+- igdng_disable_pll_edp(crtc);
++ ironlake_disable_pll_edp(crtc);
+ }
+
+ temp = I915_READ(fdi_rx_reg);
+@@ -1827,6 +1896,37 @@ static void igdng_crtc_dpms(struct drm_crtc *crtc, int mode)
+ }
+ }
+
++static void intel_crtc_dpms_overlay(struct intel_crtc *intel_crtc, bool enable)
++{
++ struct intel_overlay *overlay;
++ int ret;
++
++ if (!enable && intel_crtc->overlay) {
++ overlay = intel_crtc->overlay;
++ mutex_lock(&overlay->dev->struct_mutex);
++ for (;;) {
++ ret = intel_overlay_switch_off(overlay);
++ if (ret == 0)
++ break;
++
++ ret = intel_overlay_recover_from_interrupt(overlay, 0);
++ if (ret != 0) {
++ /* overlay doesn't react anymore. Usually
++ * results in a black screen and an unkillable
++ * X server. */
++ BUG();
++ overlay->hw_wedged = HW_WEDGED;
++ break;
++ }
++ }
++ mutex_unlock(&overlay->dev->struct_mutex);
++ }
++ /* Let userspace switch the overlay on again. In most cases userspace
++ * has to recompute where to put it anyway. */
++
++ return;
++}
++
+ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
+ {
+ struct drm_device *dev = crtc->dev;
+@@ -1885,12 +1985,13 @@ static void i9xx_crtc_dpms(struct drm_crtc *crtc, int mode)
+ intel_update_fbc(crtc, &crtc->mode);
+
+ /* Give the overlay scaler a chance to enable if it's on this pipe */
+- //intel_crtc_dpms_video(crtc, true); TODO
++ intel_crtc_dpms_overlay(intel_crtc, true);
+ break;
+ case DRM_MODE_DPMS_OFF:
+ intel_update_watermarks(dev);
++
+ /* Give the overlay scaler a chance to disable if it's on this pipe */
+- //intel_crtc_dpms_video(crtc, FALSE); TODO
++ intel_crtc_dpms_overlay(intel_crtc, false);
+ drm_vblank_off(dev, pipe);
+
+ if (dev_priv->cfb_plane == plane &&
+@@ -2010,7 +2111,7 @@ static bool intel_crtc_mode_fixup(struct drm_crtc *crtc,
+ struct drm_display_mode *adjusted_mode)
+ {
+ struct drm_device *dev = crtc->dev;
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ /* FDI link clock is fixed at 2.7G */
+ if (mode->clock * 3 > 27000 * 4)
+ return MODE_CLOCK_HIGH;
+@@ -2086,7 +2187,7 @@ static int i830_get_display_clock_speed(struct drm_device *dev)
+ * Return the pipe currently connected to the panel fitter,
+ * or -1 if the panel fitter is not present or not in use
+ */
+-static int intel_panel_fitter_pipe (struct drm_device *dev)
++int intel_panel_fitter_pipe (struct drm_device *dev)
+ {
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 pfit_control;
+@@ -2130,9 +2231,8 @@ fdi_reduce_ratio(u32 *num, u32 *den)
+ #define LINK_N 0x80000
+
+ static void
+-igdng_compute_m_n(int bits_per_pixel, int nlanes,
+- int pixel_clock, int link_clock,
+- struct fdi_m_n *m_n)
++ironlake_compute_m_n(int bits_per_pixel, int nlanes, int pixel_clock,
++ int link_clock, struct fdi_m_n *m_n)
+ {
+ u64 temp;
+
+@@ -2160,34 +2260,34 @@ struct intel_watermark_params {
+ unsigned long cacheline_size;
+ };
+
+-/* IGD has different values for various configs */
+-static struct intel_watermark_params igd_display_wm = {
+- IGD_DISPLAY_FIFO,
+- IGD_MAX_WM,
+- IGD_DFT_WM,
+- IGD_GUARD_WM,
+- IGD_FIFO_LINE_SIZE
++/* Pineview has different values for various configs */
++static struct intel_watermark_params pineview_display_wm = {
++ PINEVIEW_DISPLAY_FIFO,
++ PINEVIEW_MAX_WM,
++ PINEVIEW_DFT_WM,
++ PINEVIEW_GUARD_WM,
++ PINEVIEW_FIFO_LINE_SIZE
+ };
+-static struct intel_watermark_params igd_display_hplloff_wm = {
+- IGD_DISPLAY_FIFO,
+- IGD_MAX_WM,
+- IGD_DFT_HPLLOFF_WM,
+- IGD_GUARD_WM,
+- IGD_FIFO_LINE_SIZE
++static struct intel_watermark_params pineview_display_hplloff_wm = {
++ PINEVIEW_DISPLAY_FIFO,
++ PINEVIEW_MAX_WM,
++ PINEVIEW_DFT_HPLLOFF_WM,
++ PINEVIEW_GUARD_WM,
++ PINEVIEW_FIFO_LINE_SIZE
+ };
+-static struct intel_watermark_params igd_cursor_wm = {
+- IGD_CURSOR_FIFO,
+- IGD_CURSOR_MAX_WM,
+- IGD_CURSOR_DFT_WM,
+- IGD_CURSOR_GUARD_WM,
+- IGD_FIFO_LINE_SIZE,
++static struct intel_watermark_params pineview_cursor_wm = {
++ PINEVIEW_CURSOR_FIFO,
++ PINEVIEW_CURSOR_MAX_WM,
++ PINEVIEW_CURSOR_DFT_WM,
++ PINEVIEW_CURSOR_GUARD_WM,
++ PINEVIEW_FIFO_LINE_SIZE,
+ };
+-static struct intel_watermark_params igd_cursor_hplloff_wm = {
+- IGD_CURSOR_FIFO,
+- IGD_CURSOR_MAX_WM,
+- IGD_CURSOR_DFT_WM,
+- IGD_CURSOR_GUARD_WM,
+- IGD_FIFO_LINE_SIZE
++static struct intel_watermark_params pineview_cursor_hplloff_wm = {
++ PINEVIEW_CURSOR_FIFO,
++ PINEVIEW_CURSOR_MAX_WM,
++ PINEVIEW_CURSOR_DFT_WM,
++ PINEVIEW_CURSOR_GUARD_WM,
++ PINEVIEW_FIFO_LINE_SIZE
+ };
+ static struct intel_watermark_params g4x_wm_info = {
+ G4X_FIFO_SIZE,
+@@ -2260,11 +2360,11 @@ static unsigned long intel_calculate_wm(unsigned long clock_in_khz,
+ 1000;
+ entries_required /= wm->cacheline_size;
+
+- DRM_DEBUG("FIFO entries required for mode: %d\n", entries_required);
++ DRM_DEBUG_KMS("FIFO entries required for mode: %d\n", entries_required);
+
+ wm_size = wm->fifo_size - (entries_required + wm->guard_size);
+
+- DRM_DEBUG("FIFO watermark level: %d\n", wm_size);
++ DRM_DEBUG_KMS("FIFO watermark level: %d\n", wm_size);
+
+ /* Don't promote wm_size to unsigned... */
+ if (wm_size > (long)wm->max_wm)
+@@ -2326,50 +2426,50 @@ static struct cxsr_latency *intel_get_cxsr_latency(int is_desktop, int fsb,
+ return latency;
+ }
+
+- DRM_DEBUG("Unknown FSB/MEM found, disable CxSR\n");
++ DRM_DEBUG_KMS("Unknown FSB/MEM found, disable CxSR\n");
+
+ return NULL;
+ }
+
+-static void igd_disable_cxsr(struct drm_device *dev)
++static void pineview_disable_cxsr(struct drm_device *dev)
+ {
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 reg;
+
+ /* deactivate cxsr */
+ reg = I915_READ(DSPFW3);
+- reg &= ~(IGD_SELF_REFRESH_EN);
++ reg &= ~(PINEVIEW_SELF_REFRESH_EN);
+ I915_WRITE(DSPFW3, reg);
+ DRM_INFO("Big FIFO is disabled\n");
+ }
+
+-static void igd_enable_cxsr(struct drm_device *dev, unsigned long clock,
+- int pixel_size)
++static void pineview_enable_cxsr(struct drm_device *dev, unsigned long clock,
++ int pixel_size)
+ {
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 reg;
+ unsigned long wm;
+ struct cxsr_latency *latency;
+
+- latency = intel_get_cxsr_latency(IS_IGDG(dev), dev_priv->fsb_freq,
++ latency = intel_get_cxsr_latency(IS_PINEVIEW_G(dev), dev_priv->fsb_freq,
+ dev_priv->mem_freq);
+ if (!latency) {
+- DRM_DEBUG("Unknown FSB/MEM found, disable CxSR\n");
+- igd_disable_cxsr(dev);
++ DRM_DEBUG_KMS("Unknown FSB/MEM found, disable CxSR\n");
++ pineview_disable_cxsr(dev);
+ return;
+ }
+
+ /* Display SR */
+- wm = intel_calculate_wm(clock, &igd_display_wm, pixel_size,
++ wm = intel_calculate_wm(clock, &pineview_display_wm, pixel_size,
+ latency->display_sr);
+ reg = I915_READ(DSPFW1);
+ reg &= 0x7fffff;
+ reg |= wm << 23;
+ I915_WRITE(DSPFW1, reg);
+- DRM_DEBUG("DSPFW1 register is %x\n", reg);
++ DRM_DEBUG_KMS("DSPFW1 register is %x\n", reg);
+
+ /* cursor SR */
+- wm = intel_calculate_wm(clock, &igd_cursor_wm, pixel_size,
++ wm = intel_calculate_wm(clock, &pineview_cursor_wm, pixel_size,
+ latency->cursor_sr);
+ reg = I915_READ(DSPFW3);
+ reg &= ~(0x3f << 24);
+@@ -2377,7 +2477,7 @@ static void igd_enable_cxsr(struct drm_device *dev, unsigned long clock,
+ I915_WRITE(DSPFW3, reg);
+
+ /* Display HPLL off SR */
+- wm = intel_calculate_wm(clock, &igd_display_hplloff_wm,
++ wm = intel_calculate_wm(clock, &pineview_display_hplloff_wm,
+ latency->display_hpll_disable, I915_FIFO_LINE_SIZE);
+ reg = I915_READ(DSPFW3);
+ reg &= 0xfffffe00;
+@@ -2385,17 +2485,17 @@ static void igd_enable_cxsr(struct drm_device *dev, unsigned long clock,
+ I915_WRITE(DSPFW3, reg);
+
+ /* cursor HPLL off SR */
+- wm = intel_calculate_wm(clock, &igd_cursor_hplloff_wm, pixel_size,
++ wm = intel_calculate_wm(clock, &pineview_cursor_hplloff_wm, pixel_size,
+ latency->cursor_hpll_disable);
+ reg = I915_READ(DSPFW3);
+ reg &= ~(0x3f << 16);
+ reg |= (wm & 0x3f) << 16;
+ I915_WRITE(DSPFW3, reg);
+- DRM_DEBUG("DSPFW3 register is %x\n", reg);
++ DRM_DEBUG_KMS("DSPFW3 register is %x\n", reg);
+
+ /* activate cxsr */
+ reg = I915_READ(DSPFW3);
+- reg |= IGD_SELF_REFRESH_EN;
++ reg |= PINEVIEW_SELF_REFRESH_EN;
+ I915_WRITE(DSPFW3, reg);
+
+ DRM_INFO("Big FIFO is enabled\n");
+@@ -2417,7 +2517,7 @@ static void igd_enable_cxsr(struct drm_device *dev, unsigned long clock,
+ * A value of 5us seems to be a good balance; safe for very low end
+ * platforms but not overly aggressive on lower latency configs.
+ */
+-const static int latency_ns = 5000;
++static const int latency_ns = 5000;
+
+ static int i9xx_get_fifo_size(struct drm_device *dev, int plane)
+ {
+@@ -2431,8 +2531,8 @@ static int i9xx_get_fifo_size(struct drm_device *dev, int plane)
+ size = ((dsparb >> DSPARB_CSTART_SHIFT) & 0x7f) -
+ (dsparb & 0x7f);
+
+- DRM_DEBUG("FIFO size - (0x%08x) %s: %d\n", dsparb, plane ? "B" : "A",
+- size);
++ DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
++ plane ? "B" : "A", size);
+
+ return size;
+ }
+@@ -2450,8 +2550,8 @@ static int i85x_get_fifo_size(struct drm_device *dev, int plane)
+ (dsparb & 0x1ff);
+ size >>= 1; /* Convert to cachelines */
+
+- DRM_DEBUG("FIFO size - (0x%08x) %s: %d\n", dsparb, plane ? "B" : "A",
+- size);
++ DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
++ plane ? "B" : "A", size);
+
+ return size;
+ }
+@@ -2465,7 +2565,8 @@ static int i845_get_fifo_size(struct drm_device *dev, int plane)
+ size = dsparb & 0x7f;
+ size >>= 2; /* Convert to cachelines */
+
+- DRM_DEBUG("FIFO size - (0x%08x) %s: %d\n", dsparb, plane ? "B" : "A",
++ DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
++ plane ? "B" : "A",
+ size);
+
+ return size;
+@@ -2480,8 +2581,8 @@ static int i830_get_fifo_size(struct drm_device *dev, int plane)
+ size = dsparb & 0x7f;
+ size >>= 1; /* Convert to cachelines */
+
+- DRM_DEBUG("FIFO size - (0x%08x) %s: %d\n", dsparb, plane ? "B" : "A",
+- size);
++ DRM_DEBUG_KMS("FIFO size - (0x%08x) %s: %d\n", dsparb,
++ plane ? "B" : "A", size);
+
+ return size;
+ }
+@@ -2527,7 +2628,7 @@ static void g4x_update_wm(struct drm_device *dev, int planea_clock,
+ /* Calc sr entries for one plane configs */
+ if (sr_hdisplay && (!planea_clock || !planeb_clock)) {
+ /* self-refresh has much higher latency */
+- const static int sr_latency_ns = 12000;
++ static const int sr_latency_ns = 12000;
+
+ sr_clock = planea_clock ? planea_clock : planeb_clock;
+ line_time_us = ((sr_hdisplay * 1000) / sr_clock);
+@@ -2538,6 +2639,10 @@ static void g4x_update_wm(struct drm_device *dev, int planea_clock,
+ sr_entries = roundup(sr_entries / cacheline_size, 1);
+ DRM_DEBUG("self-refresh entries: %d\n", sr_entries);
+ I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN);
++ } else {
++ /* Turn off self refresh if both pipes are enabled */
++ I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
++ & ~FW_BLC_SELF_EN);
+ }
+
+ DRM_DEBUG("Setting FIFO watermarks - A: %d, B: %d, SR %d\n",
+@@ -2556,15 +2661,43 @@ static void g4x_update_wm(struct drm_device *dev, int planea_clock,
+ (cursor_sr << DSPFW_CURSOR_SR_SHIFT));
+ }
+
+-static void i965_update_wm(struct drm_device *dev, int unused, int unused2,
+- int unused3, int unused4)
++static void i965_update_wm(struct drm_device *dev, int planea_clock,
++ int planeb_clock, int sr_hdisplay, int pixel_size)
+ {
+ struct drm_i915_private *dev_priv = dev->dev_private;
++ unsigned long line_time_us;
++ int sr_clock, sr_entries, srwm = 1;
++
++ /* Calc sr entries for one plane configs */
++ if (sr_hdisplay && (!planea_clock || !planeb_clock)) {
++ /* self-refresh has much higher latency */
++ static const int sr_latency_ns = 12000;
++
++ sr_clock = planea_clock ? planea_clock : planeb_clock;
++ line_time_us = ((sr_hdisplay * 1000) / sr_clock);
++
++ /* Use ns/us then divide to preserve precision */
++ sr_entries = (((sr_latency_ns / line_time_us) + 1) *
++ pixel_size * sr_hdisplay) / 1000;
++ sr_entries = roundup(sr_entries / I915_FIFO_LINE_SIZE, 1);
++ DRM_DEBUG("self-refresh entries: %d\n", sr_entries);
++ srwm = I945_FIFO_SIZE - sr_entries;
++ if (srwm < 0)
++ srwm = 1;
++ srwm &= 0x3f;
++ I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN);
++ } else {
++ /* Turn off self refresh if both pipes are enabled */
++ I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
++ & ~FW_BLC_SELF_EN);
++ }
+
+- DRM_DEBUG("Setting FIFO watermarks - A: 8, B: 8, C: 8, SR 8\n");
++ DRM_DEBUG_KMS("Setting FIFO watermarks - A: 8, B: 8, C: 8, SR %d\n",
++ srwm);
+
+ /* 965 has limitations... */
+- I915_WRITE(DSPFW1, (8 << 16) | (8 << 8) | (8 << 0));
++ I915_WRITE(DSPFW1, (srwm << DSPFW_SR_SHIFT) | (8 << 16) | (8 << 8) |
++ (8 << 0));
+ I915_WRITE(DSPFW2, (8 << 8) | (8 << 0));
+ }
+
+@@ -2600,7 +2733,7 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
+ pixel_size, latency_ns);
+ planeb_wm = intel_calculate_wm(planeb_clock, &planeb_params,
+ pixel_size, latency_ns);
+- DRM_DEBUG("FIFO watermarks - A: %d, B: %d\n", planea_wm, planeb_wm);
++ DRM_DEBUG_KMS("FIFO watermarks - A: %d, B: %d\n", planea_wm, planeb_wm);
+
+ /*
+ * Overlay gets an aggressive default since video jitter is bad.
+@@ -2611,7 +2744,7 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
+ if (HAS_FW_BLC(dev) && sr_hdisplay &&
+ (!planea_clock || !planeb_clock)) {
+ /* self-refresh has much higher latency */
+- const static int sr_latency_ns = 6000;
++ static const int sr_latency_ns = 6000;
+
+ sr_clock = planea_clock ? planea_clock : planeb_clock;
+ line_time_us = ((sr_hdisplay * 1000) / sr_clock);
+@@ -2620,14 +2753,18 @@ static void i9xx_update_wm(struct drm_device *dev, int planea_clock,
+ sr_entries = (((sr_latency_ns / line_time_us) + 1) *
+ pixel_size * sr_hdisplay) / 1000;
+ sr_entries = roundup(sr_entries / cacheline_size, 1);
+- DRM_DEBUG("self-refresh entries: %d\n", sr_entries);
++ DRM_DEBUG_KMS("self-refresh entries: %d\n", sr_entries);
+ srwm = total_size - sr_entries;
+ if (srwm < 0)
+ srwm = 1;
+ I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN | (srwm & 0x3f));
++ } else {
++ /* Turn off self refresh if both pipes are enabled */
++ I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
++ & ~FW_BLC_SELF_EN);
+ }
+
+- DRM_DEBUG("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n",
++ DRM_DEBUG_KMS("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n",
+ planea_wm, planeb_wm, cwm, srwm);
+
+ fwater_lo = ((planeb_wm & 0x3f) << 16) | (planea_wm & 0x3f);
+@@ -2654,7 +2791,7 @@ static void i830_update_wm(struct drm_device *dev, int planea_clock, int unused,
+ pixel_size, latency_ns);
+ fwater_lo |= (3<<8) | planea_wm;
+
+- DRM_DEBUG("Setting FIFO watermarks - A: %d\n", planea_wm);
++ DRM_DEBUG_KMS("Setting FIFO watermarks - A: %d\n", planea_wm);
+
+ I915_WRITE(FW_BLC, fwater_lo);
+ }
+@@ -2708,11 +2845,11 @@ static void intel_update_watermarks(struct drm_device *dev)
+ if (crtc->enabled) {
+ enabled++;
+ if (intel_crtc->plane == 0) {
+- DRM_DEBUG("plane A (pipe %d) clock: %d\n",
++ DRM_DEBUG_KMS("plane A (pipe %d) clock: %d\n",
+ intel_crtc->pipe, crtc->mode.clock);
+ planea_clock = crtc->mode.clock;
+ } else {
+- DRM_DEBUG("plane B (pipe %d) clock: %d\n",
++ DRM_DEBUG_KMS("plane B (pipe %d) clock: %d\n",
+ intel_crtc->pipe, crtc->mode.clock);
+ planeb_clock = crtc->mode.clock;
+ }
+@@ -2729,10 +2866,10 @@ static void intel_update_watermarks(struct drm_device *dev)
+ return;
+
+ /* Single plane configs can enable self refresh */
+- if (enabled == 1 && IS_IGD(dev))
+- igd_enable_cxsr(dev, sr_clock, pixel_size);
+- else if (IS_IGD(dev))
+- igd_disable_cxsr(dev);
++ if (enabled == 1 && IS_PINEVIEW(dev))
++ pineview_enable_cxsr(dev, sr_clock, pixel_size);
++ else if (IS_PINEVIEW(dev))
++ pineview_disable_cxsr(dev);
+
+ dev_priv->display.update_wm(dev, planea_clock, planeb_clock,
+ sr_hdisplay, pixel_size);
+@@ -2826,10 +2963,11 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
+
+ if (is_lvds && dev_priv->lvds_use_ssc && num_outputs < 2) {
+ refclk = dev_priv->lvds_ssc_freq * 1000;
+- DRM_DEBUG("using SSC reference clock of %d MHz\n", refclk / 1000);
++ DRM_DEBUG_KMS("using SSC reference clock of %d MHz\n",
++ refclk / 1000);
+ } else if (IS_I9XX(dev)) {
+ refclk = 96000;
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ refclk = 120000; /* 120Mhz refclk */
+ } else {
+ refclk = 48000;
+@@ -2849,14 +2987,23 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
+ return -EINVAL;
+ }
+
+- if (limit->find_reduced_pll && dev_priv->lvds_downclock_avail) {
+- memcpy(&reduced_clock, &clock, sizeof(intel_clock_t));
+- has_reduced_clock = limit->find_reduced_pll(limit, crtc,
+- (adjusted_mode->clock*3/4),
++ if (is_lvds && dev_priv->lvds_downclock_avail) {
++ has_reduced_clock = limit->find_pll(limit, crtc,
++ dev_priv->lvds_downclock,
+ refclk,
+ &reduced_clock);
++ if (has_reduced_clock && (clock.p != reduced_clock.p)) {
++ /*
++ * If the different P is found, it means that we can't
++ * switch the display clock by using the FP0/FP1.
++ * In such case we will disable the LVDS downclock
++ * feature.
++ */
++ DRM_DEBUG_KMS("Different P is found for "
++ "LVDS clock/downclock\n");
++ has_reduced_clock = 0;
++ }
+ }
+-
+ /* SDVO TV has fixed PLL values depend on its clock range,
+ this mirrors vbios setting. */
+ if (is_sdvo && is_tv) {
+@@ -2878,7 +3025,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
+ }
+
+ /* FDI link */
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ int lane, link_bw, bpp;
+ /* eDP doesn't require FDI link, so just set DP M/N
+ according to current link config */
+@@ -2909,6 +3056,21 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
+ temp |= PIPE_8BPC;
+ else
+ temp |= PIPE_6BPC;
++ } else if (is_edp) {
++ switch (dev_priv->edp_bpp/3) {
++ case 8:
++ temp |= PIPE_8BPC;
++ break;
++ case 10:
++ temp |= PIPE_10BPC;
++ break;
++ case 6:
++ temp |= PIPE_6BPC;
++ break;
++ case 12:
++ temp |= PIPE_12BPC;
++ break;
++ }
+ } else
+ temp |= PIPE_8BPC;
+ I915_WRITE(pipeconf_reg, temp);
+@@ -2932,8 +3094,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
+ bpp = 24;
+ }
+
+- igdng_compute_m_n(bpp, lane, target_clock,
+- link_bw, &m_n);
++ ironlake_compute_m_n(bpp, lane, target_clock, link_bw, &m_n);
+ }
+
+ /* Ironlake: try to setup display ref clock before DPLL
+@@ -2941,7 +3102,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
+ * PCH B stepping, previous chipset stepping should be
+ * ignoring this setting.
+ */
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ temp = I915_READ(PCH_DREF_CONTROL);
+ /* Always enable nonspread source */
+ temp &= ~DREF_NONSPREAD_SOURCE_MASK;
+@@ -2976,7 +3137,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
+ }
+ }
+
+- if (IS_IGD(dev)) {
++ if (IS_PINEVIEW(dev)) {
+ fp = (1 << clock.n) << 16 | clock.m1 << 8 | clock.m2;
+ if (has_reduced_clock)
+ fp2 = (1 << reduced_clock.n) << 16 |
+@@ -2988,7 +3149,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
+ reduced_clock.m2;
+ }
+
+- if (!IS_IGDNG(dev))
++ if (!IS_IRONLAKE(dev))
+ dpll = DPLL_VGA_MODE_DIS;
+
+ if (IS_I9XX(dev)) {
+@@ -3001,19 +3162,19 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
+ sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+ if (IS_I945G(dev) || IS_I945GM(dev) || IS_G33(dev))
+ dpll |= (sdvo_pixel_multiply - 1) << SDVO_MULTIPLIER_SHIFT_HIRES;
+- else if (IS_IGDNG(dev))
++ else if (IS_IRONLAKE(dev))
+ dpll |= (sdvo_pixel_multiply - 1) << PLL_REF_SDVO_HDMI_MULTIPLIER_SHIFT;
+ }
+ if (is_dp)
+ dpll |= DPLL_DVO_HIGH_SPEED;
+
+ /* compute bitmask from p1 value */
+- if (IS_IGD(dev))
+- dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_IGD;
++ if (IS_PINEVIEW(dev))
++ dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW;
+ else {
+ dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA01_P1_POST_DIV_SHIFT;
+ /* also FPA1 */
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ dpll |= (1 << (clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
+ if (IS_G4X(dev) && has_reduced_clock)
+ dpll |= (1 << (reduced_clock.p1 - 1)) << DPLL_FPA1_P1_POST_DIV_SHIFT;
+@@ -3032,7 +3193,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
+ dpll |= DPLLB_LVDS_P2_CLOCK_DIV_14;
+ break;
+ }
+- if (IS_I965G(dev) && !IS_IGDNG(dev))
++ if (IS_I965G(dev) && !IS_IRONLAKE(dev))
+ dpll |= (6 << PLL_LOAD_PULSE_PHASE_SHIFT);
+ } else {
+ if (is_lvds) {
+@@ -3064,9 +3225,9 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
+ /* Set up the display plane register */
+ dspcntr = DISPPLANE_GAMMA_ENABLE;
+
+- /* IGDNG's plane is forced to pipe, bit 24 is to
++ /* Ironlake's plane is forced to pipe, bit 24 is to
+ enable color space conversion */
+- if (!IS_IGDNG(dev)) {
++ if (!IS_IRONLAKE(dev)) {
+ if (pipe == 0)
+ dspcntr &= ~DISPPLANE_SEL_PIPE_MASK;
+ else
+@@ -3093,20 +3254,20 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
+
+
+ /* Disable the panel fitter if it was on our pipe */
+- if (!IS_IGDNG(dev) && intel_panel_fitter_pipe(dev) == pipe)
++ if (!IS_IRONLAKE(dev) && intel_panel_fitter_pipe(dev) == pipe)
+ I915_WRITE(PFIT_CONTROL, 0);
+
+- DRM_DEBUG("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
++ DRM_DEBUG_KMS("Mode for pipe %c:\n", pipe == 0 ? 'A' : 'B');
+ drm_mode_debug_printmodeline(mode);
+
+- /* assign to IGDNG registers */
+- if (IS_IGDNG(dev)) {
++ /* assign to Ironlake registers */
++ if (IS_IRONLAKE(dev)) {
+ fp_reg = pch_fp_reg;
+ dpll_reg = pch_dpll_reg;
+ }
+
+ if (is_edp) {
+- igdng_disable_pll_edp(crtc);
++ ironlake_disable_pll_edp(crtc);
+ } else if ((dpll & DPLL_VCO_ENABLE)) {
+ I915_WRITE(fp_reg, fp);
+ I915_WRITE(dpll_reg, dpll & ~DPLL_VCO_ENABLE);
+@@ -3121,7 +3282,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
+ if (is_lvds) {
+ u32 lvds;
+
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ lvds_reg = PCH_LVDS;
+
+ lvds = I915_READ(lvds_reg);
+@@ -3143,12 +3304,12 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
+ /* set the dithering flag */
+ if (IS_I965G(dev)) {
+ if (dev_priv->lvds_dither) {
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ pipeconf |= PIPE_ENABLE_DITHER;
+ else
+ lvds |= LVDS_ENABLE_DITHER;
+ } else {
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ pipeconf &= ~PIPE_ENABLE_DITHER;
+ else
+ lvds &= ~LVDS_ENABLE_DITHER;
+@@ -3167,7 +3328,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
+ /* Wait for the clocks to stabilize. */
+ udelay(150);
+
+- if (IS_I965G(dev) && !IS_IGDNG(dev)) {
++ if (IS_I965G(dev) && !IS_IRONLAKE(dev)) {
+ if (is_sdvo) {
+ sdvo_pixel_multiply = adjusted_mode->clock / mode->clock;
+ I915_WRITE(dpll_md_reg, (0 << DPLL_MD_UDI_DIVIDER_SHIFT) |
+@@ -3187,14 +3348,14 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
+ I915_WRITE(fp_reg + 4, fp2);
+ intel_crtc->lowfreq_avail = true;
+ if (HAS_PIPE_CXSR(dev)) {
+- DRM_DEBUG("enabling CxSR downclocking\n");
++ DRM_DEBUG_KMS("enabling CxSR downclocking\n");
+ pipeconf |= PIPECONF_CXSR_DOWNCLOCK;
+ }
+ } else {
+ I915_WRITE(fp_reg + 4, fp);
+ intel_crtc->lowfreq_avail = false;
+ if (HAS_PIPE_CXSR(dev)) {
+- DRM_DEBUG("disabling CxSR downclocking\n");
++ DRM_DEBUG_KMS("disabling CxSR downclocking\n");
+ pipeconf &= ~PIPECONF_CXSR_DOWNCLOCK;
+ }
+ }
+@@ -3214,21 +3375,21 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
+ /* pipesrc and dspsize control the size that is scaled from, which should
+ * always be the user's requested size.
+ */
+- if (!IS_IGDNG(dev)) {
++ if (!IS_IRONLAKE(dev)) {
+ I915_WRITE(dspsize_reg, ((mode->vdisplay - 1) << 16) |
+ (mode->hdisplay - 1));
+ I915_WRITE(dsppos_reg, 0);
+ }
+ I915_WRITE(pipesrc_reg, ((mode->hdisplay - 1) << 16) | (mode->vdisplay - 1));
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ I915_WRITE(data_m1_reg, TU_SIZE(m_n.tu) | m_n.gmch_m);
+ I915_WRITE(data_n1_reg, TU_SIZE(m_n.tu) | m_n.gmch_n);
+ I915_WRITE(link_m1_reg, m_n.link_m);
+ I915_WRITE(link_n1_reg, m_n.link_n);
+
+ if (is_edp) {
+- igdng_set_pll_edp(crtc, adjusted_mode->clock);
++ ironlake_set_pll_edp(crtc, adjusted_mode->clock);
+ } else {
+ /* enable FDI RX PLL too */
+ temp = I915_READ(fdi_rx_reg);
+@@ -3242,7 +3403,7 @@ static int intel_crtc_mode_set(struct drm_crtc *crtc,
+
+ intel_wait_for_vblank(dev);
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ /* enable address swizzle for tiling buffer */
+ temp = I915_READ(DISP_ARB_CTL);
+ I915_WRITE(DISP_ARB_CTL, temp | DISP_TILE_SURFACE_SWIZZLING);
+@@ -3276,8 +3437,8 @@ void intel_crtc_load_lut(struct drm_crtc *crtc)
+ if (!crtc->enabled)
+ return;
+
+- /* use legacy palette for IGDNG */
+- if (IS_IGDNG(dev))
++ /* use legacy palette for Ironlake */
++ if (IS_IRONLAKE(dev))
+ palreg = (intel_crtc->pipe == 0) ? LGC_PALETTE_A :
+ LGC_PALETTE_B;
+
+@@ -3306,11 +3467,11 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
+ size_t addr;
+ int ret;
+
+- DRM_DEBUG("\n");
++ DRM_DEBUG_KMS("\n");
+
+ /* if we want to turn off the cursor ignore width and height */
+ if (!handle) {
+- DRM_DEBUG("cursor off\n");
++ DRM_DEBUG_KMS("cursor off\n");
+ if (IS_MOBILE(dev) || IS_I9XX(dev)) {
+ temp &= ~(CURSOR_MODE | MCURSOR_GAMMA_ENABLE);
+ temp |= CURSOR_MODE_DISABLE;
+@@ -3343,7 +3504,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
+
+ /* we only need to pin inside GTT if cursor is non-phy */
+ mutex_lock(&dev->struct_mutex);
+- if (!dev_priv->cursor_needs_physical) {
++ if (!dev_priv->info->cursor_needs_physical) {
+ ret = i915_gem_object_pin(bo, PAGE_SIZE);
+ if (ret) {
+ DRM_ERROR("failed to pin cursor bo\n");
+@@ -3378,7 +3539,7 @@ static int intel_crtc_cursor_set(struct drm_crtc *crtc,
+ I915_WRITE(base, addr);
+
+ if (intel_crtc->cursor_bo) {
+- if (dev_priv->cursor_needs_physical) {
++ if (dev_priv->info->cursor_needs_physical) {
+ if (intel_crtc->cursor_bo != bo)
+ i915_gem_detach_phys_object(dev, intel_crtc->cursor_bo);
+ } else
+@@ -3618,18 +3779,18 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
+ fp = I915_READ((pipe == 0) ? FPA1 : FPB1);
+
+ clock.m1 = (fp & FP_M1_DIV_MASK) >> FP_M1_DIV_SHIFT;
+- if (IS_IGD(dev)) {
+- clock.n = ffs((fp & FP_N_IGD_DIV_MASK) >> FP_N_DIV_SHIFT) - 1;
+- clock.m2 = (fp & FP_M2_IGD_DIV_MASK) >> FP_M2_DIV_SHIFT;
++ if (IS_PINEVIEW(dev)) {
++ clock.n = ffs((fp & FP_N_PINEVIEW_DIV_MASK) >> FP_N_DIV_SHIFT) - 1;
++ clock.m2 = (fp & FP_M2_PINEVIEW_DIV_MASK) >> FP_M2_DIV_SHIFT;
+ } else {
+ clock.n = (fp & FP_N_DIV_MASK) >> FP_N_DIV_SHIFT;
+ clock.m2 = (fp & FP_M2_DIV_MASK) >> FP_M2_DIV_SHIFT;
+ }
+
+ if (IS_I9XX(dev)) {
+- if (IS_IGD(dev))
+- clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_IGD) >>
+- DPLL_FPA01_P1_POST_DIV_SHIFT_IGD);
++ if (IS_PINEVIEW(dev))
++ clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK_PINEVIEW) >>
++ DPLL_FPA01_P1_POST_DIV_SHIFT_PINEVIEW);
+ else
+ clock.p1 = ffs((dpll & DPLL_FPA01_P1_POST_DIV_MASK) >>
+ DPLL_FPA01_P1_POST_DIV_SHIFT);
+@@ -3644,7 +3805,7 @@ static int intel_crtc_clock_get(struct drm_device *dev, struct drm_crtc *crtc)
+ 7 : 14;
+ break;
+ default:
+- DRM_DEBUG("Unknown DPLL mode %08x in programmed "
++ DRM_DEBUG_KMS("Unknown DPLL mode %08x in programmed "
+ "mode\n", (int)(dpll & DPLL_MODE_MASK));
+ return 0;
+ }
+@@ -3730,7 +3891,7 @@ static void intel_gpu_idle_timer(unsigned long arg)
+ struct drm_device *dev = (struct drm_device *)arg;
+ drm_i915_private_t *dev_priv = dev->dev_private;
+
+- DRM_DEBUG("idle timer fired, downclocking\n");
++ DRM_DEBUG_DRIVER("idle timer fired, downclocking\n");
+
+ dev_priv->busy = false;
+
+@@ -3745,7 +3906,7 @@ static void intel_crtc_idle_timer(unsigned long arg)
+ struct drm_crtc *crtc = &intel_crtc->base;
+ drm_i915_private_t *dev_priv = crtc->dev->dev_private;
+
+- DRM_DEBUG("idle timer fired, downclocking\n");
++ DRM_DEBUG_DRIVER("idle timer fired, downclocking\n");
+
+ intel_crtc->busy = false;
+
+@@ -3761,14 +3922,14 @@ static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule)
+ int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
+ int dpll = I915_READ(dpll_reg);
+
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ return;
+
+ if (!dev_priv->lvds_downclock_avail)
+ return;
+
+ if (!HAS_PIPE_CXSR(dev) && (dpll & DISPLAY_RATE_SELECT_FPA1)) {
+- DRM_DEBUG("upclocking LVDS\n");
++ DRM_DEBUG_DRIVER("upclocking LVDS\n");
+
+ /* Unlock panel regs */
+ I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | (0xabcd << 16));
+@@ -3779,7 +3940,7 @@ static void intel_increase_pllclock(struct drm_crtc *crtc, bool schedule)
+ intel_wait_for_vblank(dev);
+ dpll = I915_READ(dpll_reg);
+ if (dpll & DISPLAY_RATE_SELECT_FPA1)
+- DRM_DEBUG("failed to upclock LVDS!\n");
++ DRM_DEBUG_DRIVER("failed to upclock LVDS!\n");
+
+ /* ...and lock them again */
+ I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & 0x3);
+@@ -3800,7 +3961,7 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
+ int dpll_reg = (pipe == 0) ? DPLL_A : DPLL_B;
+ int dpll = I915_READ(dpll_reg);
+
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ return;
+
+ if (!dev_priv->lvds_downclock_avail)
+@@ -3811,7 +3972,7 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
+ * the manual case.
+ */
+ if (!HAS_PIPE_CXSR(dev) && intel_crtc->lowfreq_avail) {
+- DRM_DEBUG("downclocking LVDS\n");
++ DRM_DEBUG_DRIVER("downclocking LVDS\n");
+
+ /* Unlock panel regs */
+ I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) | (0xabcd << 16));
+@@ -3822,7 +3983,7 @@ static void intel_decrease_pllclock(struct drm_crtc *crtc)
+ intel_wait_for_vblank(dev);
+ dpll = I915_READ(dpll_reg);
+ if (!(dpll & DISPLAY_RATE_SELECT_FPA1))
+- DRM_DEBUG("failed to downclock LVDS!\n");
++ DRM_DEBUG_DRIVER("failed to downclock LVDS!\n");
+
+ /* ...and lock them again */
+ I915_WRITE(PP_CONTROL, I915_READ(PP_CONTROL) & 0x3);
+@@ -3883,7 +4044,11 @@ void intel_mark_busy(struct drm_device *dev, struct drm_gem_object *obj)
+ if (!drm_core_check_feature(dev, DRIVER_MODESET))
+ return;
+
+- dev_priv->busy = true;
++ if (!dev_priv->busy)
++ dev_priv->busy = true;
++ else
++ mod_timer(&dev_priv->idle_timer, jiffies +
++ msecs_to_jiffies(GPU_IDLE_TIMEOUT));
+
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ if (!crtc->fb)
+@@ -3913,6 +4078,180 @@ static void intel_crtc_destroy(struct drm_crtc *crtc)
+ kfree(intel_crtc);
+ }
+
++struct intel_unpin_work {
++ struct work_struct work;
++ struct drm_device *dev;
++ struct drm_gem_object *old_fb_obj;
++ struct drm_gem_object *pending_flip_obj;
++ struct drm_pending_vblank_event *event;
++ int pending;
++};
++
++static void intel_unpin_work_fn(struct work_struct *__work)
++{
++ struct intel_unpin_work *work =
++ container_of(__work, struct intel_unpin_work, work);
++
++ mutex_lock(&work->dev->struct_mutex);
++ i915_gem_object_unpin(work->old_fb_obj);
++ drm_gem_object_unreference(work->pending_flip_obj);
++ drm_gem_object_unreference(work->old_fb_obj);
++ mutex_unlock(&work->dev->struct_mutex);
++ kfree(work);
++}
++
++void intel_finish_page_flip(struct drm_device *dev, int pipe)
++{
++ drm_i915_private_t *dev_priv = dev->dev_private;
++ struct drm_crtc *crtc = dev_priv->pipe_to_crtc_mapping[pipe];
++ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
++ struct intel_unpin_work *work;
++ struct drm_i915_gem_object *obj_priv;
++ struct drm_pending_vblank_event *e;
++ struct timeval now;
++ unsigned long flags;
++
++ /* Ignore early vblank irqs */
++ if (intel_crtc == NULL)
++ return;
++
++ spin_lock_irqsave(&dev->event_lock, flags);
++ work = intel_crtc->unpin_work;
++ if (work == NULL || !work->pending) {
++ if (work && !work->pending) {
++ obj_priv = work->pending_flip_obj->driver_private;
++ DRM_DEBUG_DRIVER("flip finish: %p (%d) not pending?\n",
++ obj_priv,
++ atomic_read(&obj_priv->pending_flip));
++ }
++ spin_unlock_irqrestore(&dev->event_lock, flags);
++ return;
++ }
++
++ intel_crtc->unpin_work = NULL;
++ drm_vblank_put(dev, intel_crtc->pipe);
++
++ if (work->event) {
++ e = work->event;
++ do_gettimeofday(&now);
++ e->event.sequence = drm_vblank_count(dev, intel_crtc->pipe);
++ e->event.tv_sec = now.tv_sec;
++ e->event.tv_usec = now.tv_usec;
++ list_add_tail(&e->base.link,
++ &e->base.file_priv->event_list);
++ wake_up_interruptible(&e->base.file_priv->event_wait);
++ }
++
++ spin_unlock_irqrestore(&dev->event_lock, flags);
++
++ obj_priv = work->pending_flip_obj->driver_private;
++
++ /* Initial scanout buffer will have a 0 pending flip count */
++ if ((atomic_read(&obj_priv->pending_flip) == 0) ||
++ atomic_dec_and_test(&obj_priv->pending_flip))
++ DRM_WAKEUP(&dev_priv->pending_flip_queue);
++ schedule_work(&work->work);
++}
++
++void intel_prepare_page_flip(struct drm_device *dev, int plane)
++{
++ drm_i915_private_t *dev_priv = dev->dev_private;
++ struct intel_crtc *intel_crtc =
++ to_intel_crtc(dev_priv->plane_to_crtc_mapping[plane]);
++ unsigned long flags;
++
++ spin_lock_irqsave(&dev->event_lock, flags);
++ if (intel_crtc->unpin_work) {
++ intel_crtc->unpin_work->pending = 1;
++ } else {
++ DRM_DEBUG_DRIVER("preparing flip with no unpin work?\n");
++ }
++ spin_unlock_irqrestore(&dev->event_lock, flags);
++}
++
++static int intel_crtc_page_flip(struct drm_crtc *crtc,
++ struct drm_framebuffer *fb,
++ struct drm_pending_vblank_event *event)
++{
++ struct drm_device *dev = crtc->dev;
++ struct drm_i915_private *dev_priv = dev->dev_private;
++ struct intel_framebuffer *intel_fb;
++ struct drm_i915_gem_object *obj_priv;
++ struct drm_gem_object *obj;
++ struct intel_crtc *intel_crtc = to_intel_crtc(crtc);
++ struct intel_unpin_work *work;
++ unsigned long flags;
++ int pipesrc_reg = (intel_crtc->pipe == 0) ? PIPEASRC : PIPEBSRC;
++ int ret, pipesrc;
++ RING_LOCALS;
++
++ work = kzalloc(sizeof *work, GFP_KERNEL);
++ if (work == NULL)
++ return -ENOMEM;
++
++ mutex_lock(&dev->struct_mutex);
++
++ work->event = event;
++ work->dev = crtc->dev;
++ intel_fb = to_intel_framebuffer(crtc->fb);
++ work->old_fb_obj = intel_fb->obj;
++ INIT_WORK(&work->work, intel_unpin_work_fn);
++
++ /* We borrow the event spin lock for protecting unpin_work */
++ spin_lock_irqsave(&dev->event_lock, flags);
++ if (intel_crtc->unpin_work) {
++ DRM_DEBUG_DRIVER("flip queue: crtc already busy\n");
++ spin_unlock_irqrestore(&dev->event_lock, flags);
++ kfree(work);
++ mutex_unlock(&dev->struct_mutex);
++ return -EBUSY;
++ }
++ intel_crtc->unpin_work = work;
++ spin_unlock_irqrestore(&dev->event_lock, flags);
++
++ intel_fb = to_intel_framebuffer(fb);
++ obj = intel_fb->obj;
++
++ ret = intel_pin_and_fence_fb_obj(dev, obj);
++ if (ret != 0) {
++ DRM_DEBUG_DRIVER("flip queue: %p pin & fence failed\n",
++ obj->driver_private);
++ kfree(work);
++ intel_crtc->unpin_work = NULL;
++ mutex_unlock(&dev->struct_mutex);
++ return ret;
++ }
++
++ /* Reference the objects for the scheduled work. */
++ drm_gem_object_reference(work->old_fb_obj);
++ drm_gem_object_reference(obj);
++
++ crtc->fb = fb;
++ i915_gem_object_flush_write_domain(obj);
++ drm_vblank_get(dev, intel_crtc->pipe);
++ obj_priv = obj->driver_private;
++ atomic_inc(&obj_priv->pending_flip);
++ work->pending_flip_obj = obj;
++
++ BEGIN_LP_RING(4);
++ OUT_RING(MI_DISPLAY_FLIP |
++ MI_DISPLAY_FLIP_PLANE(intel_crtc->plane));
++ OUT_RING(fb->pitch);
++ if (IS_I965G(dev)) {
++ OUT_RING(obj_priv->gtt_offset | obj_priv->tiling_mode);
++ pipesrc = I915_READ(pipesrc_reg);
++ OUT_RING(pipesrc & 0x0fff0fff);
++ } else {
++ OUT_RING(obj_priv->gtt_offset);
++ OUT_RING(MI_NOOP);
++ }
++ ADVANCE_LP_RING();
++
++ mutex_unlock(&dev->struct_mutex);
++
++ return 0;
++}
++
+ static const struct drm_crtc_helper_funcs intel_helper_funcs = {
+ .dpms = intel_crtc_dpms,
+ .mode_fixup = intel_crtc_mode_fixup,
+@@ -3929,11 +4268,13 @@ static const struct drm_crtc_funcs intel_crtc_funcs = {
+ .gamma_set = intel_crtc_gamma_set,
+ .set_config = drm_crtc_helper_set_config,
+ .destroy = intel_crtc_destroy,
++ .page_flip = intel_crtc_page_flip,
+ };
+
+
+ static void intel_crtc_init(struct drm_device *dev, int pipe)
+ {
++ drm_i915_private_t *dev_priv = dev->dev_private;
+ struct intel_crtc *intel_crtc;
+ int i;
+
+@@ -3956,10 +4297,15 @@ static void intel_crtc_init(struct drm_device *dev, int pipe)
+ intel_crtc->pipe = pipe;
+ intel_crtc->plane = pipe;
+ if (IS_MOBILE(dev) && (IS_I9XX(dev) && !IS_I965G(dev))) {
+- DRM_DEBUG("swapping pipes & planes for FBC\n");
++ DRM_DEBUG_KMS("swapping pipes & planes for FBC\n");
+ intel_crtc->plane = ((pipe == 0) ? 1 : 0);
+ }
+
++ BUG_ON(pipe >= ARRAY_SIZE(dev_priv->plane_to_crtc_mapping) ||
++ dev_priv->plane_to_crtc_mapping[intel_crtc->plane] != NULL);
++ dev_priv->plane_to_crtc_mapping[intel_crtc->plane] = &intel_crtc->base;
++ dev_priv->pipe_to_crtc_mapping[intel_crtc->pipe] = &intel_crtc->base;
++
+ intel_crtc->cursor_addr = 0;
+ intel_crtc->dpms_mode = DRM_MODE_DPMS_OFF;
+ drm_crtc_helper_add(&intel_crtc->base, &intel_helper_funcs);
+@@ -4036,7 +4382,7 @@ static void intel_setup_outputs(struct drm_device *dev)
+ if (IS_MOBILE(dev) && !IS_I830(dev))
+ intel_lvds_init(dev);
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ int found;
+
+ if (IS_MOBILE(dev) && (I915_READ(DP_A) & DP_DETECTED))
+@@ -4209,6 +4555,42 @@ static const struct drm_mode_config_funcs intel_mode_funcs = {
+ .fb_changed = intelfb_probe,
+ };
+
++static struct drm_gem_object *
++intel_alloc_power_context(struct drm_device *dev)
++{
++ struct drm_gem_object *pwrctx;
++ int ret;
++
++ pwrctx = drm_gem_object_alloc(dev, 4096);
++ if (!pwrctx) {
++ DRM_DEBUG("failed to alloc power context, RC6 disabled\n");
++ return NULL;
++ }
++
++ mutex_lock(&dev->struct_mutex);
++ ret = i915_gem_object_pin(pwrctx, 4096);
++ if (ret) {
++ DRM_ERROR("failed to pin power context: %d\n", ret);
++ goto err_unref;
++ }
++
++ ret = i915_gem_object_set_to_gtt_domain(pwrctx, 1);
++ if (ret) {
++ DRM_ERROR("failed to set-domain on power context: %d\n", ret);
++ goto err_unpin;
++ }
++ mutex_unlock(&dev->struct_mutex);
++
++ return pwrctx;
++
++err_unpin:
++ i915_gem_object_unpin(pwrctx);
++err_unref:
++ drm_gem_object_unreference(pwrctx);
++ mutex_unlock(&dev->struct_mutex);
++ return NULL;
++}
++
+ void intel_init_clock_gating(struct drm_device *dev)
+ {
+ struct drm_i915_private *dev_priv = dev->dev_private;
+@@ -4217,7 +4599,7 @@ void intel_init_clock_gating(struct drm_device *dev)
+ * Disable clock gating reported to work incorrectly according to the
+ * specs, but enable as much else as we can.
+ */
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ return;
+ } else if (IS_G4X(dev)) {
+ uint32_t dspclk_gate;
+@@ -4251,11 +4633,37 @@ void intel_init_clock_gating(struct drm_device *dev)
+ dstate |= DSTATE_PLL_D3_OFF | DSTATE_GFX_CLOCK_GATING |
+ DSTATE_DOT_CLOCK_GATING;
+ I915_WRITE(D_STATE, dstate);
+- } else if (IS_I855(dev) || IS_I865G(dev)) {
++ } else if (IS_I85X(dev) || IS_I865G(dev)) {
+ I915_WRITE(RENCLK_GATE_D1, SV_CLOCK_GATE_DISABLE);
+ } else if (IS_I830(dev)) {
+ I915_WRITE(DSPCLK_GATE_D, OVRUNIT_CLOCK_GATE_DISABLE);
+ }
++
++ /*
++ * GPU can automatically power down the render unit if given a page
++ * to save state.
++ */
++ if (I915_HAS_RC6(dev) && drm_core_check_feature(dev, DRIVER_MODESET)) {
++ struct drm_i915_gem_object *obj_priv = NULL;
++
++ if (dev_priv->pwrctx) {
++ obj_priv = dev_priv->pwrctx->driver_private;
++ } else {
++ struct drm_gem_object *pwrctx;
++
++ pwrctx = intel_alloc_power_context(dev);
++ if (pwrctx) {
++ dev_priv->pwrctx = pwrctx;
++ obj_priv = pwrctx->driver_private;
++ }
++ }
++
++ if (obj_priv) {
++ I915_WRITE(PWRCTXA, obj_priv->gtt_offset | PWRCTX_EN);
++ I915_WRITE(MCHBAR_RENDER_STANDBY,
++ I915_READ(MCHBAR_RENDER_STANDBY) & ~RCX_SW_EXIT);
++ }
++ }
+ }
+
+ /* Set up chip specific display functions */
+@@ -4264,8 +4672,8 @@ static void intel_init_display(struct drm_device *dev)
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ /* We always want a DPMS function */
+- if (IS_IGDNG(dev))
+- dev_priv->display.dpms = igdng_crtc_dpms;
++ if (IS_IRONLAKE(dev))
++ dev_priv->display.dpms = ironlake_crtc_dpms;
+ else
+ dev_priv->display.dpms = i9xx_crtc_dpms;
+
+@@ -4284,13 +4692,13 @@ static void intel_init_display(struct drm_device *dev)
+ }
+
+ /* Returns the core display clock speed */
+- if (IS_I945G(dev))
++ if (IS_I945G(dev) || (IS_G33(dev) && ! IS_PINEVIEW_M(dev)))
+ dev_priv->display.get_display_clock_speed =
+ i945_get_display_clock_speed;
+ else if (IS_I915G(dev))
+ dev_priv->display.get_display_clock_speed =
+ i915_get_display_clock_speed;
+- else if (IS_I945GM(dev) || IS_845G(dev) || IS_IGDGM(dev))
++ else if (IS_I945GM(dev) || IS_845G(dev) || IS_PINEVIEW_M(dev))
+ dev_priv->display.get_display_clock_speed =
+ i9xx_misc_get_display_clock_speed;
+ else if (IS_I915GM(dev))
+@@ -4299,7 +4707,7 @@ static void intel_init_display(struct drm_device *dev)
+ else if (IS_I865G(dev))
+ dev_priv->display.get_display_clock_speed =
+ i865_get_display_clock_speed;
+- else if (IS_I855(dev))
++ else if (IS_I85X(dev))
+ dev_priv->display.get_display_clock_speed =
+ i855_get_display_clock_speed;
+ else /* 852, 830 */
+@@ -4307,7 +4715,7 @@ static void intel_init_display(struct drm_device *dev)
+ i830_get_display_clock_speed;
+
+ /* For FIFO watermark updates */
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ dev_priv->display.update_wm = NULL;
+ else if (IS_G4X(dev))
+ dev_priv->display.update_wm = g4x_update_wm;
+@@ -4363,7 +4771,7 @@ void intel_modeset_init(struct drm_device *dev)
+ num_pipe = 2;
+ else
+ num_pipe = 1;
+- DRM_DEBUG("%d display pipe%s available.\n",
++ DRM_DEBUG_KMS("%d display pipe%s available.\n",
+ num_pipe, num_pipe > 1 ? "s" : "");
+
+ if (IS_I85X(dev))
+@@ -4382,6 +4790,15 @@ void intel_modeset_init(struct drm_device *dev)
+ INIT_WORK(&dev_priv->idle_work, intel_idle_update);
+ setup_timer(&dev_priv->idle_timer, intel_gpu_idle_timer,
+ (unsigned long)dev);
++
++ intel_setup_overlay(dev);
++
++ if (IS_PINEVIEW(dev) && !intel_get_cxsr_latency(IS_PINEVIEW_G(dev),
++ dev_priv->fsb_freq,
++ dev_priv->mem_freq))
++ DRM_INFO("failed to find known CxSR latency "
++ "(found fsb freq %d, mem freq %d), disabling CxSR\n",
++ dev_priv->fsb_freq, dev_priv->mem_freq);
+ }
+
+ void intel_modeset_cleanup(struct drm_device *dev)
+@@ -4404,11 +4821,21 @@ void intel_modeset_cleanup(struct drm_device *dev)
+
+ del_timer_sync(&dev_priv->idle_timer);
+
+- mutex_unlock(&dev->struct_mutex);
+-
+ if (dev_priv->display.disable_fbc)
+ dev_priv->display.disable_fbc(dev);
+
++ if (dev_priv->pwrctx) {
++ struct drm_i915_gem_object *obj_priv;
++
++ obj_priv = dev_priv->pwrctx->driver_private;
++ I915_WRITE(PWRCTXA, obj_priv->gtt_offset &~ PWRCTX_EN);
++ I915_READ(PWRCTXA);
++ i915_gem_object_unpin(dev_priv->pwrctx);
++ drm_gem_object_unreference(dev_priv->pwrctx);
++ }
++
++ mutex_unlock(&dev->struct_mutex);
++
+ drm_mode_config_cleanup(dev);
+ }
+
+diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c
+index d487771..439506c 100644
+--- a/drivers/gpu/drm/i915/intel_dp.c
++++ b/drivers/gpu/drm/i915/intel_dp.c
+@@ -33,7 +33,8 @@
+ #include "intel_drv.h"
+ #include "i915_drm.h"
+ #include "i915_drv.h"
+-#include "intel_dp.h"
++#include "drm_dp_helper.h"
++
+
+ #define DP_LINK_STATUS_SIZE 6
+ #define DP_LINK_CHECK_TIMEOUT (10 * 1000)
+@@ -124,9 +125,15 @@ intel_dp_link_clock(uint8_t link_bw)
+
+ /* I think this is a fiction */
+ static int
+-intel_dp_link_required(int pixel_clock)
++intel_dp_link_required(struct drm_device *dev,
++ struct intel_output *intel_output, int pixel_clock)
+ {
+- return pixel_clock * 3;
++ struct drm_i915_private *dev_priv = dev->dev_private;
++
++ if (IS_eDP(intel_output))
++ return (pixel_clock * dev_priv->edp_bpp) / 8;
++ else
++ return pixel_clock * 3;
+ }
+
+ static int
+@@ -137,7 +144,8 @@ intel_dp_mode_valid(struct drm_connector *connector,
+ int max_link_clock = intel_dp_link_clock(intel_dp_max_link_bw(intel_output));
+ int max_lanes = intel_dp_max_lane_count(intel_output);
+
+- if (intel_dp_link_required(mode->clock) > max_link_clock * max_lanes)
++ if (intel_dp_link_required(connector->dev, intel_output, mode->clock)
++ > max_link_clock * max_lanes)
+ return MODE_CLOCK_HIGH;
+
+ if (mode->clock < 10000)
+@@ -223,8 +231,8 @@ intel_dp_aux_ch(struct intel_output *intel_output,
+ */
+ if (IS_eDP(intel_output))
+ aux_clock_divider = 225; /* eDP input clock at 450Mhz */
+- else if (IS_IGDNG(dev))
+- aux_clock_divider = 62; /* IGDNG: input clock fixed at 125Mhz */
++ else if (IS_IRONLAKE(dev))
++ aux_clock_divider = 62; /* IRL input clock fixed at 125Mhz */
+ else
+ aux_clock_divider = intel_hrawclk(dev) / 2;
+
+@@ -282,7 +290,7 @@ intel_dp_aux_ch(struct intel_output *intel_output,
+ /* Timeouts occur when the device isn't connected, so they're
+ * "normal" -- don't fill the kernel log with these */
+ if (status & DP_AUX_CH_CTL_TIME_OUT_ERROR) {
+- DRM_DEBUG("dp_aux_ch timeout status 0x%08x\n", status);
++ DRM_DEBUG_KMS("dp_aux_ch timeout status 0x%08x\n", status);
+ return -ETIMEDOUT;
+ }
+
+@@ -382,17 +390,77 @@ intel_dp_aux_native_read(struct intel_output *intel_output,
+ }
+
+ static int
+-intel_dp_i2c_aux_ch(struct i2c_adapter *adapter,
+- uint8_t *send, int send_bytes,
+- uint8_t *recv, int recv_bytes)
++intel_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
++ uint8_t write_byte, uint8_t *read_byte)
+ {
++ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+ struct intel_dp_priv *dp_priv = container_of(adapter,
+ struct intel_dp_priv,
+ adapter);
+ struct intel_output *intel_output = dp_priv->intel_output;
++ uint16_t address = algo_data->address;
++ uint8_t msg[5];
++ uint8_t reply[2];
++ int msg_bytes;
++ int reply_bytes;
++ int ret;
++
++ /* Set up the command byte */
++ if (mode & MODE_I2C_READ)
++ msg[0] = AUX_I2C_READ << 4;
++ else
++ msg[0] = AUX_I2C_WRITE << 4;
++
++ if (!(mode & MODE_I2C_STOP))
++ msg[0] |= AUX_I2C_MOT << 4;
++
++ msg[1] = address >> 8;
++ msg[2] = address;
++
++ switch (mode) {
++ case MODE_I2C_WRITE:
++ msg[3] = 0;
++ msg[4] = write_byte;
++ msg_bytes = 5;
++ reply_bytes = 1;
++ break;
++ case MODE_I2C_READ:
++ msg[3] = 0;
++ msg_bytes = 4;
++ reply_bytes = 2;
++ break;
++ default:
++ msg_bytes = 3;
++ reply_bytes = 1;
++ break;
++ }
+
+- return intel_dp_aux_ch(intel_output,
+- send, send_bytes, recv, recv_bytes);
++ for (;;) {
++ ret = intel_dp_aux_ch(intel_output,
++ msg, msg_bytes,
++ reply, reply_bytes);
++ if (ret < 0) {
++ DRM_DEBUG_KMS("aux_ch failed %d\n", ret);
++ return ret;
++ }
++ switch (reply[0] & AUX_I2C_REPLY_MASK) {
++ case AUX_I2C_REPLY_ACK:
++ if (mode == MODE_I2C_READ) {
++ *read_byte = reply[1];
++ }
++ return reply_bytes - 1;
++ case AUX_I2C_REPLY_NACK:
++ DRM_DEBUG_KMS("aux_ch nack\n");
++ return -EREMOTEIO;
++ case AUX_I2C_REPLY_DEFER:
++ DRM_DEBUG_KMS("aux_ch defer\n");
++ udelay(100);
++ break;
++ default:
++ DRM_ERROR("aux_ch invalid reply 0x%02x\n", reply[0]);
++ return -EREMOTEIO;
++ }
++ }
+ }
+
+ static int
+@@ -431,11 +499,13 @@ intel_dp_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
+ for (clock = 0; clock <= max_clock; clock++) {
+ int link_avail = intel_dp_link_clock(bws[clock]) * lane_count;
+
+- if (intel_dp_link_required(mode->clock) <= link_avail) {
++ if (intel_dp_link_required(encoder->dev, intel_output, mode->clock)
++ <= link_avail) {
+ dp_priv->link_bw = bws[clock];
+ dp_priv->lane_count = lane_count;
+ adjusted_mode->clock = intel_dp_link_clock(dp_priv->link_bw);
+- DRM_DEBUG("Display port link bw %02x lane count %d clock %d\n",
++ DRM_DEBUG_KMS("Display port link bw %02x lane "
++ "count %d clock %d\n",
+ dp_priv->link_bw, dp_priv->lane_count,
+ adjusted_mode->clock);
+ return true;
+@@ -514,7 +584,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
+ intel_dp_compute_m_n(3, lane_count,
+ mode->clock, adjusted_mode->clock, &m_n);
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ if (intel_crtc->pipe == 0) {
+ I915_WRITE(TRANSA_DATA_M1,
+ ((m_n.tu - 1) << PIPE_GMCH_DATA_M_TU_SIZE_SHIFT) |
+@@ -606,23 +676,23 @@ intel_dp_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
+ }
+ }
+
+-static void igdng_edp_backlight_on (struct drm_device *dev)
++static void ironlake_edp_backlight_on (struct drm_device *dev)
+ {
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 pp;
+
+- DRM_DEBUG("\n");
++ DRM_DEBUG_KMS("\n");
+ pp = I915_READ(PCH_PP_CONTROL);
+ pp |= EDP_BLC_ENABLE;
+ I915_WRITE(PCH_PP_CONTROL, pp);
+ }
+
+-static void igdng_edp_backlight_off (struct drm_device *dev)
++static void ironlake_edp_backlight_off (struct drm_device *dev)
+ {
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 pp;
+
+- DRM_DEBUG("\n");
++ DRM_DEBUG_KMS("\n");
+ pp = I915_READ(PCH_PP_CONTROL);
+ pp &= ~EDP_BLC_ENABLE;
+ I915_WRITE(PCH_PP_CONTROL, pp);
+@@ -641,13 +711,13 @@ intel_dp_dpms(struct drm_encoder *encoder, int mode)
+ if (dp_reg & DP_PORT_EN) {
+ intel_dp_link_down(intel_output, dp_priv->DP);
+ if (IS_eDP(intel_output))
+- igdng_edp_backlight_off(dev);
++ ironlake_edp_backlight_off(dev);
+ }
+ } else {
+ if (!(dp_reg & DP_PORT_EN)) {
+ intel_dp_link_train(intel_output, dp_priv->DP, dp_priv->link_configuration);
+ if (IS_eDP(intel_output))
+- igdng_edp_backlight_on(dev);
++ ironlake_edp_backlight_on(dev);
+ }
+ }
+ dp_priv->dpms_mode = mode;
+@@ -1010,7 +1080,7 @@ intel_dp_link_down(struct intel_output *intel_output, uint32_t DP)
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+
+- DRM_DEBUG("\n");
++ DRM_DEBUG_KMS("\n");
+
+ if (IS_eDP(intel_output)) {
+ DP &= ~DP_PLL_ENABLE;
+@@ -1071,7 +1141,7 @@ intel_dp_check_link_status(struct intel_output *intel_output)
+ }
+
+ static enum drm_connector_status
+-igdng_dp_detect(struct drm_connector *connector)
++ironlake_dp_detect(struct drm_connector *connector)
+ {
+ struct intel_output *intel_output = to_intel_output(connector);
+ struct intel_dp_priv *dp_priv = intel_output->dev_priv;
+@@ -1106,8 +1176,8 @@ intel_dp_detect(struct drm_connector *connector)
+
+ dp_priv->has_audio = false;
+
+- if (IS_IGDNG(dev))
+- return igdng_dp_detect(connector);
++ if (IS_IRONLAKE(dev))
++ return ironlake_dp_detect(connector);
+
+ temp = I915_READ(PORT_HOTPLUG_EN);
+
+@@ -1261,11 +1331,10 @@ intel_dp_init(struct drm_device *dev, int output_reg)
+ else if (output_reg == DP_D || output_reg == PCH_DP_D)
+ intel_output->clone_mask = (1 << INTEL_DP_D_CLONE_BIT);
+
+- if (IS_eDP(intel_output)) {
+- intel_output->crtc_mask = (1 << 1);
++ if (IS_eDP(intel_output))
+ intel_output->clone_mask = (1 << INTEL_EDP_CLONE_BIT);
+- } else
+- intel_output->crtc_mask = (1 << 0) | (1 << 1);
++
++ intel_output->crtc_mask = (1 << 0) | (1 << 1);
+ connector->interlace_allowed = true;
+ connector->doublescan_allowed = 0;
+
+diff --git a/drivers/gpu/drm/i915/intel_dp.h b/drivers/gpu/drm/i915/intel_dp.h
+deleted file mode 100644
+index 2b38054..0000000
+--- a/drivers/gpu/drm/i915/intel_dp.h
++++ /dev/null
+@@ -1,144 +0,0 @@
+-/*
+- * Copyright © 2008 Keith Packard
+- *
+- * Permission to use, copy, modify, distribute, and sell this software and its
+- * documentation for any purpose is hereby granted without fee, provided that
+- * the above copyright notice appear in all copies and that both that copyright
+- * notice and this permission notice appear in supporting documentation, and
+- * that the name of the copyright holders not be used in advertising or
+- * publicity pertaining to distribution of the software without specific,
+- * written prior permission. The copyright holders make no representations
+- * about the suitability of this software for any purpose. It is provided "as
+- * is" without express or implied warranty.
+- *
+- * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+- * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+- * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+- * OF THIS SOFTWARE.
+- */
+-
+-#ifndef _INTEL_DP_H_
+-#define _INTEL_DP_H_
+-
+-/* From the VESA DisplayPort spec */
+-
+-#define AUX_NATIVE_WRITE 0x8
+-#define AUX_NATIVE_READ 0x9
+-#define AUX_I2C_WRITE 0x0
+-#define AUX_I2C_READ 0x1
+-#define AUX_I2C_STATUS 0x2
+-#define AUX_I2C_MOT 0x4
+-
+-#define AUX_NATIVE_REPLY_ACK (0x0 << 4)
+-#define AUX_NATIVE_REPLY_NACK (0x1 << 4)
+-#define AUX_NATIVE_REPLY_DEFER (0x2 << 4)
+-#define AUX_NATIVE_REPLY_MASK (0x3 << 4)
+-
+-#define AUX_I2C_REPLY_ACK (0x0 << 6)
+-#define AUX_I2C_REPLY_NACK (0x1 << 6)
+-#define AUX_I2C_REPLY_DEFER (0x2 << 6)
+-#define AUX_I2C_REPLY_MASK (0x3 << 6)
+-
+-/* AUX CH addresses */
+-#define DP_LINK_BW_SET 0x100
+-# define DP_LINK_BW_1_62 0x06
+-# define DP_LINK_BW_2_7 0x0a
+-
+-#define DP_LANE_COUNT_SET 0x101
+-# define DP_LANE_COUNT_MASK 0x0f
+-# define DP_LANE_COUNT_ENHANCED_FRAME_EN (1 << 7)
+-
+-#define DP_TRAINING_PATTERN_SET 0x102
+-
+-# define DP_TRAINING_PATTERN_DISABLE 0
+-# define DP_TRAINING_PATTERN_1 1
+-# define DP_TRAINING_PATTERN_2 2
+-# define DP_TRAINING_PATTERN_MASK 0x3
+-
+-# define DP_LINK_QUAL_PATTERN_DISABLE (0 << 2)
+-# define DP_LINK_QUAL_PATTERN_D10_2 (1 << 2)
+-# define DP_LINK_QUAL_PATTERN_ERROR_RATE (2 << 2)
+-# define DP_LINK_QUAL_PATTERN_PRBS7 (3 << 2)
+-# define DP_LINK_QUAL_PATTERN_MASK (3 << 2)
+-
+-# define DP_RECOVERED_CLOCK_OUT_EN (1 << 4)
+-# define DP_LINK_SCRAMBLING_DISABLE (1 << 5)
+-
+-# define DP_SYMBOL_ERROR_COUNT_BOTH (0 << 6)
+-# define DP_SYMBOL_ERROR_COUNT_DISPARITY (1 << 6)
+-# define DP_SYMBOL_ERROR_COUNT_SYMBOL (2 << 6)
+-# define DP_SYMBOL_ERROR_COUNT_MASK (3 << 6)
+-
+-#define DP_TRAINING_LANE0_SET 0x103
+-#define DP_TRAINING_LANE1_SET 0x104
+-#define DP_TRAINING_LANE2_SET 0x105
+-#define DP_TRAINING_LANE3_SET 0x106
+-
+-# define DP_TRAIN_VOLTAGE_SWING_MASK 0x3
+-# define DP_TRAIN_VOLTAGE_SWING_SHIFT 0
+-# define DP_TRAIN_MAX_SWING_REACHED (1 << 2)
+-# define DP_TRAIN_VOLTAGE_SWING_400 (0 << 0)
+-# define DP_TRAIN_VOLTAGE_SWING_600 (1 << 0)
+-# define DP_TRAIN_VOLTAGE_SWING_800 (2 << 0)
+-# define DP_TRAIN_VOLTAGE_SWING_1200 (3 << 0)
+-
+-# define DP_TRAIN_PRE_EMPHASIS_MASK (3 << 3)
+-# define DP_TRAIN_PRE_EMPHASIS_0 (0 << 3)
+-# define DP_TRAIN_PRE_EMPHASIS_3_5 (1 << 3)
+-# define DP_TRAIN_PRE_EMPHASIS_6 (2 << 3)
+-# define DP_TRAIN_PRE_EMPHASIS_9_5 (3 << 3)
+-
+-# define DP_TRAIN_PRE_EMPHASIS_SHIFT 3
+-# define DP_TRAIN_MAX_PRE_EMPHASIS_REACHED (1 << 5)
+-
+-#define DP_DOWNSPREAD_CTRL 0x107
+-# define DP_SPREAD_AMP_0_5 (1 << 4)
+-
+-#define DP_MAIN_LINK_CHANNEL_CODING_SET 0x108
+-# define DP_SET_ANSI_8B10B (1 << 0)
+-
+-#define DP_LANE0_1_STATUS 0x202
+-#define DP_LANE2_3_STATUS 0x203
+-
+-# define DP_LANE_CR_DONE (1 << 0)
+-# define DP_LANE_CHANNEL_EQ_DONE (1 << 1)
+-# define DP_LANE_SYMBOL_LOCKED (1 << 2)
+-
+-#define DP_LANE_ALIGN_STATUS_UPDATED 0x204
+-
+-#define DP_INTERLANE_ALIGN_DONE (1 << 0)
+-#define DP_DOWNSTREAM_PORT_STATUS_CHANGED (1 << 6)
+-#define DP_LINK_STATUS_UPDATED (1 << 7)
+-
+-#define DP_SINK_STATUS 0x205
+-
+-#define DP_RECEIVE_PORT_0_STATUS (1 << 0)
+-#define DP_RECEIVE_PORT_1_STATUS (1 << 1)
+-
+-#define DP_ADJUST_REQUEST_LANE0_1 0x206
+-#define DP_ADJUST_REQUEST_LANE2_3 0x207
+-
+-#define DP_ADJUST_VOLTAGE_SWING_LANE0_MASK 0x03
+-#define DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT 0
+-#define DP_ADJUST_PRE_EMPHASIS_LANE0_MASK 0x0c
+-#define DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT 2
+-#define DP_ADJUST_VOLTAGE_SWING_LANE1_MASK 0x30
+-#define DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT 4
+-#define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK 0xc0
+-#define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT 6
+-
+-struct i2c_algo_dp_aux_data {
+- bool running;
+- u16 address;
+- int (*aux_ch) (struct i2c_adapter *adapter,
+- uint8_t *send, int send_bytes,
+- uint8_t *recv, int recv_bytes);
+-};
+-
+-int
+-i2c_dp_aux_add_bus(struct i2c_adapter *adapter);
+-
+-#endif /* _INTEL_DP_H_ */
+diff --git a/drivers/gpu/drm/i915/intel_dp_i2c.c b/drivers/gpu/drm/i915/intel_dp_i2c.c
+deleted file mode 100644
+index a63b6f5..0000000
+--- a/drivers/gpu/drm/i915/intel_dp_i2c.c
++++ /dev/null
+@@ -1,273 +0,0 @@
+-/*
+- * Copyright © 2009 Keith Packard
+- *
+- * Permission to use, copy, modify, distribute, and sell this software and its
+- * documentation for any purpose is hereby granted without fee, provided that
+- * the above copyright notice appear in all copies and that both that copyright
+- * notice and this permission notice appear in supporting documentation, and
+- * that the name of the copyright holders not be used in advertising or
+- * publicity pertaining to distribution of the software without specific,
+- * written prior permission. The copyright holders make no representations
+- * about the suitability of this software for any purpose. It is provided "as
+- * is" without express or implied warranty.
+- *
+- * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+- * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+- * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+- * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+- * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+- * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
+- * OF THIS SOFTWARE.
+- */
+-
+-#include <linux/kernel.h>
+-#include <linux/module.h>
+-#include <linux/delay.h>
+-#include <linux/slab.h>
+-#include <linux/init.h>
+-#include <linux/errno.h>
+-#include <linux/sched.h>
+-#include <linux/i2c.h>
+-#include "intel_dp.h"
+-#include "drmP.h"
+-
+-/* Run a single AUX_CH I2C transaction, writing/reading data as necessary */
+-
+-#define MODE_I2C_START 1
+-#define MODE_I2C_WRITE 2
+-#define MODE_I2C_READ 4
+-#define MODE_I2C_STOP 8
+-
+-static int
+-i2c_algo_dp_aux_transaction(struct i2c_adapter *adapter, int mode,
+- uint8_t write_byte, uint8_t *read_byte)
+-{
+- struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+- uint16_t address = algo_data->address;
+- uint8_t msg[5];
+- uint8_t reply[2];
+- int msg_bytes;
+- int reply_bytes;
+- int ret;
+-
+- /* Set up the command byte */
+- if (mode & MODE_I2C_READ)
+- msg[0] = AUX_I2C_READ << 4;
+- else
+- msg[0] = AUX_I2C_WRITE << 4;
+-
+- if (!(mode & MODE_I2C_STOP))
+- msg[0] |= AUX_I2C_MOT << 4;
+-
+- msg[1] = address >> 8;
+- msg[2] = address;
+-
+- switch (mode) {
+- case MODE_I2C_WRITE:
+- msg[3] = 0;
+- msg[4] = write_byte;
+- msg_bytes = 5;
+- reply_bytes = 1;
+- break;
+- case MODE_I2C_READ:
+- msg[3] = 0;
+- msg_bytes = 4;
+- reply_bytes = 2;
+- break;
+- default:
+- msg_bytes = 3;
+- reply_bytes = 1;
+- break;
+- }
+-
+- for (;;) {
+- ret = (*algo_data->aux_ch)(adapter,
+- msg, msg_bytes,
+- reply, reply_bytes);
+- if (ret < 0) {
+- DRM_DEBUG("aux_ch failed %d\n", ret);
+- return ret;
+- }
+- switch (reply[0] & AUX_I2C_REPLY_MASK) {
+- case AUX_I2C_REPLY_ACK:
+- if (mode == MODE_I2C_READ) {
+- *read_byte = reply[1];
+- }
+- return reply_bytes - 1;
+- case AUX_I2C_REPLY_NACK:
+- DRM_DEBUG("aux_ch nack\n");
+- return -EREMOTEIO;
+- case AUX_I2C_REPLY_DEFER:
+- DRM_DEBUG("aux_ch defer\n");
+- udelay(100);
+- break;
+- default:
+- DRM_ERROR("aux_ch invalid reply 0x%02x\n", reply[0]);
+- return -EREMOTEIO;
+- }
+- }
+-}
+-
+-/*
+- * I2C over AUX CH
+- */
+-
+-/*
+- * Send the address. If the I2C link is running, this 'restarts'
+- * the connection with the new address, this is used for doing
+- * a write followed by a read (as needed for DDC)
+- */
+-static int
+-i2c_algo_dp_aux_address(struct i2c_adapter *adapter, u16 address, bool reading)
+-{
+- struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+- int mode = MODE_I2C_START;
+- int ret;
+-
+- if (reading)
+- mode |= MODE_I2C_READ;
+- else
+- mode |= MODE_I2C_WRITE;
+- algo_data->address = address;
+- algo_data->running = true;
+- ret = i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL);
+- return ret;
+-}
+-
+-/*
+- * Stop the I2C transaction. This closes out the link, sending
+- * a bare address packet with the MOT bit turned off
+- */
+-static void
+-i2c_algo_dp_aux_stop(struct i2c_adapter *adapter, bool reading)
+-{
+- struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+- int mode = MODE_I2C_STOP;
+-
+- if (reading)
+- mode |= MODE_I2C_READ;
+- else
+- mode |= MODE_I2C_WRITE;
+- if (algo_data->running) {
+- (void) i2c_algo_dp_aux_transaction(adapter, mode, 0, NULL);
+- algo_data->running = false;
+- }
+-}
+-
+-/*
+- * Write a single byte to the current I2C address, the
+- * the I2C link must be running or this returns -EIO
+- */
+-static int
+-i2c_algo_dp_aux_put_byte(struct i2c_adapter *adapter, u8 byte)
+-{
+- struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+- int ret;
+-
+- if (!algo_data->running)
+- return -EIO;
+-
+- ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_WRITE, byte, NULL);
+- return ret;
+-}
+-
+-/*
+- * Read a single byte from the current I2C address, the
+- * I2C link must be running or this returns -EIO
+- */
+-static int
+-i2c_algo_dp_aux_get_byte(struct i2c_adapter *adapter, u8 *byte_ret)
+-{
+- struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
+- int ret;
+-
+- if (!algo_data->running)
+- return -EIO;
+-
+- ret = i2c_algo_dp_aux_transaction(adapter, MODE_I2C_READ, 0, byte_ret);
+- return ret;
+-}
+-
+-static int
+-i2c_algo_dp_aux_xfer(struct i2c_adapter *adapter,
+- struct i2c_msg *msgs,
+- int num)
+-{
+- int ret = 0;
+- bool reading = false;
+- int m;
+- int b;
+-
+- for (m = 0; m < num; m++) {
+- u16 len = msgs[m].len;
+- u8 *buf = msgs[m].buf;
+- reading = (msgs[m].flags & I2C_M_RD) != 0;
+- ret = i2c_algo_dp_aux_address(adapter, msgs[m].addr, reading);
+- if (ret < 0)
+- break;
+- if (reading) {
+- for (b = 0; b < len; b++) {
+- ret = i2c_algo_dp_aux_get_byte(adapter, &buf[b]);
+- if (ret < 0)
+- break;
+- }
+- } else {
+- for (b = 0; b < len; b++) {
+- ret = i2c_algo_dp_aux_put_byte(adapter, buf[b]);
+- if (ret < 0)
+- break;
+- }
+- }
+- if (ret < 0)
+- break;
+- }
+- if (ret >= 0)
+- ret = num;
+- i2c_algo_dp_aux_stop(adapter, reading);
+- DRM_DEBUG("dp_aux_xfer return %d\n", ret);
+- return ret;
+-}
+-
+-static u32
+-i2c_algo_dp_aux_functionality(struct i2c_adapter *adapter)
+-{
+- return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL |
+- I2C_FUNC_SMBUS_READ_BLOCK_DATA |
+- I2C_FUNC_SMBUS_BLOCK_PROC_CALL |
+- I2C_FUNC_10BIT_ADDR;
+-}
+-
+-static const struct i2c_algorithm i2c_dp_aux_algo = {
+- .master_xfer = i2c_algo_dp_aux_xfer,
+- .functionality = i2c_algo_dp_aux_functionality,
+-};
+-
+-static void
+-i2c_dp_aux_reset_bus(struct i2c_adapter *adapter)
+-{
+- (void) i2c_algo_dp_aux_address(adapter, 0, false);
+- (void) i2c_algo_dp_aux_stop(adapter, false);
+-
+-}
+-
+-static int
+-i2c_dp_aux_prepare_bus(struct i2c_adapter *adapter)
+-{
+- adapter->algo = &i2c_dp_aux_algo;
+- adapter->retries = 3;
+- i2c_dp_aux_reset_bus(adapter);
+- return 0;
+-}
+-
+-int
+-i2c_dp_aux_add_bus(struct i2c_adapter *adapter)
+-{
+- int error;
+-
+- error = i2c_dp_aux_prepare_bus(adapter);
+- if (error)
+- return error;
+- error = i2c_add_adapter(adapter);
+- return error;
+-}
+-EXPORT_SYMBOL(i2c_dp_aux_add_bus);
+diff --git a/drivers/gpu/drm/i915/intel_drv.h b/drivers/gpu/drm/i915/intel_drv.h
+index ef61fe9..a51573d 100644
+--- a/drivers/gpu/drm/i915/intel_drv.h
++++ b/drivers/gpu/drm/i915/intel_drv.h
+@@ -110,6 +110,32 @@ struct intel_output {
+ int clone_mask;
+ };
+
++struct intel_crtc;
++struct intel_overlay {
++ struct drm_device *dev;
++ struct intel_crtc *crtc;
++ struct drm_i915_gem_object *vid_bo;
++ struct drm_i915_gem_object *old_vid_bo;
++ int active;
++ int pfit_active;
++ u32 pfit_vscale_ratio; /* shifted-point number, (1<<12) == 1.0 */
++ u32 color_key;
++ u32 brightness, contrast, saturation;
++ u32 old_xscale, old_yscale;
++ /* register access */
++ u32 flip_addr;
++ struct drm_i915_gem_object *reg_bo;
++ void *virt_addr;
++ /* flip handling */
++ uint32_t last_flip_req;
++ int hw_wedged;
++#define HW_WEDGED 1
++#define NEEDS_WAIT_FOR_FLIP 2
++#define RELEASE_OLD_VID 3
++#define SWITCH_OFF_STAGE_1 4
++#define SWITCH_OFF_STAGE_2 5
++};
++
+ struct intel_crtc {
+ struct drm_crtc base;
+ enum pipe pipe;
+@@ -121,6 +147,8 @@ struct intel_crtc {
+ bool busy; /* is scanout buffer being updated frequently? */
+ struct timer_list idle_timer;
+ bool lowfreq_avail;
++ struct intel_overlay *overlay;
++ struct intel_unpin_work *unpin_work;
+ };
+
+ #define to_intel_crtc(x) container_of(x, struct intel_crtc, base)
+@@ -134,6 +162,8 @@ void intel_i2c_destroy(struct i2c_adapter *adapter);
+ int intel_ddc_get_modes(struct intel_output *intel_output);
+ extern bool intel_ddc_probe(struct intel_output *intel_output);
+ void intel_i2c_quirk_set(struct drm_device *dev, bool enable);
++void intel_i2c_reset_gmbus(struct drm_device *dev);
++
+ extern void intel_crt_init(struct drm_device *dev);
+ extern void intel_hdmi_init(struct drm_device *dev, int sdvox_reg);
+ extern bool intel_sdvo_init(struct drm_device *dev, int output_device);
+@@ -148,6 +178,7 @@ intel_dp_set_m_n(struct drm_crtc *crtc, struct drm_display_mode *mode,
+ extern void intel_edp_link_config (struct intel_output *, int *, int *);
+
+
++extern int intel_panel_fitter_pipe (struct drm_device *dev);
+ extern void intel_crtc_load_lut(struct drm_crtc *crtc);
+ extern void intel_encoder_prepare (struct drm_encoder *encoder);
+ extern void intel_encoder_commit (struct drm_encoder *encoder);
+@@ -177,10 +208,23 @@ extern void intel_crtc_fb_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
+ u16 blue, int regno);
+ extern void intel_crtc_fb_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
+ u16 *blue, int regno);
++extern void intel_init_clock_gating(struct drm_device *dev);
+
+ extern int intel_framebuffer_create(struct drm_device *dev,
+ struct drm_mode_fb_cmd *mode_cmd,
+ struct drm_framebuffer **fb,
+ struct drm_gem_object *obj);
+
++extern void intel_prepare_page_flip(struct drm_device *dev, int plane);
++extern void intel_finish_page_flip(struct drm_device *dev, int pipe);
++
++extern void intel_setup_overlay(struct drm_device *dev);
++extern void intel_cleanup_overlay(struct drm_device *dev);
++extern int intel_overlay_switch_off(struct intel_overlay *overlay);
++extern int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay,
++ int interruptible);
++extern int intel_overlay_put_image(struct drm_device *dev, void *data,
++ struct drm_file *file_priv);
++extern int intel_overlay_attrs(struct drm_device *dev, void *data,
++ struct drm_file *file_priv);
+ #endif /* __INTEL_DRV_H__ */
+diff --git a/drivers/gpu/drm/i915/intel_fb.c b/drivers/gpu/drm/i915/intel_fb.c
+index 2b0fe54..aaabbcb 100644
+--- a/drivers/gpu/drm/i915/intel_fb.c
++++ b/drivers/gpu/drm/i915/intel_fb.c
+@@ -70,7 +70,7 @@ static struct drm_fb_helper_funcs intel_fb_helper_funcs = {
+
+
+ /**
+- * Curretly it is assumed that the old framebuffer is reused.
++ * Currently it is assumed that the old framebuffer is reused.
+ *
+ * LOCKING
+ * caller should hold the mode config lock.
+@@ -148,7 +148,7 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
+
+ mutex_lock(&dev->struct_mutex);
+
+- ret = i915_gem_object_pin(fbo, PAGE_SIZE);
++ ret = i915_gem_object_pin(fbo, 64*1024);
+ if (ret) {
+ DRM_ERROR("failed to pin fb: %d\n", ret);
+ goto out_unref;
+@@ -230,8 +230,9 @@ static int intelfb_create(struct drm_device *dev, uint32_t fb_width,
+ par->intel_fb = intel_fb;
+
+ /* To allow resizeing without swapping buffers */
+- DRM_DEBUG("allocated %dx%d fb: 0x%08x, bo %p\n", intel_fb->base.width,
+- intel_fb->base.height, obj_priv->gtt_offset, fbo);
++ DRM_DEBUG_KMS("allocated %dx%d fb: 0x%08x, bo %p\n",
++ intel_fb->base.width, intel_fb->base.height,
++ obj_priv->gtt_offset, fbo);
+
+ mutex_unlock(&dev->struct_mutex);
+ return 0;
+@@ -249,7 +250,7 @@ int intelfb_probe(struct drm_device *dev)
+ {
+ int ret;
+
+- DRM_DEBUG("\n");
++ DRM_DEBUG_KMS("\n");
+ ret = drm_fb_helper_single_fb_probe(dev, 32, intelfb_create);
+ return ret;
+ }
+diff --git a/drivers/gpu/drm/i915/intel_hdmi.c b/drivers/gpu/drm/i915/intel_hdmi.c
+index 85760bf..0e268de 100644
+--- a/drivers/gpu/drm/i915/intel_hdmi.c
++++ b/drivers/gpu/drm/i915/intel_hdmi.c
+@@ -82,7 +82,7 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
+ /* HW workaround, need to toggle enable bit off and on for 12bpc, but
+ * we do this anyway which shows more stable in testing.
+ */
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ I915_WRITE(hdmi_priv->sdvox_reg, temp & ~SDVO_ENABLE);
+ POSTING_READ(hdmi_priv->sdvox_reg);
+ }
+@@ -99,7 +99,7 @@ static void intel_hdmi_dpms(struct drm_encoder *encoder, int mode)
+ /* HW workaround, need to write this twice for issue that may result
+ * in first write getting masked.
+ */
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ I915_WRITE(hdmi_priv->sdvox_reg, temp);
+ POSTING_READ(hdmi_priv->sdvox_reg);
+ }
+@@ -225,7 +225,6 @@ static const struct drm_encoder_funcs intel_hdmi_enc_funcs = {
+ .destroy = intel_hdmi_enc_destroy,
+ };
+
+-
+ void intel_hdmi_init(struct drm_device *dev, int sdvox_reg)
+ {
+ struct drm_i915_private *dev_priv = dev->dev_private;
+diff --git a/drivers/gpu/drm/i915/intel_i2c.c b/drivers/gpu/drm/i915/intel_i2c.c
+index c7eab72..8673c73 100644
+--- a/drivers/gpu/drm/i915/intel_i2c.c
++++ b/drivers/gpu/drm/i915/intel_i2c.c
+@@ -39,7 +39,7 @@ void intel_i2c_quirk_set(struct drm_device *dev, bool enable)
+ struct drm_i915_private *dev_priv = dev->dev_private;
+
+ /* When using bit bashing for I2C, this bit needs to be set to 1 */
+- if (!IS_IGD(dev))
++ if (!IS_PINEVIEW(dev))
+ return;
+ if (enable)
+ I915_WRITE(DSPCLK_GATE_D,
+@@ -118,6 +118,23 @@ static void set_data(void *data, int state_high)
+ udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */
+ }
+
++/* Clears the GMBUS setup. Our driver doesn't make use of the GMBUS I2C
++ * engine, but if the BIOS leaves it enabled, then that can break our use
++ * of the bit-banging I2C interfaces. This is notably the case with the
++ * Mac Mini in EFI mode.
++ */
++void
++intel_i2c_reset_gmbus(struct drm_device *dev)
++{
++ struct drm_i915_private *dev_priv = dev->dev_private;
++
++ if (IS_IRONLAKE(dev)) {
++ I915_WRITE(PCH_GMBUS0, 0);
++ } else {
++ I915_WRITE(GMBUS0, 0);
++ }
++}
++
+ /**
+ * intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg
+ * @dev: DRM device
+@@ -168,6 +185,8 @@ struct i2c_adapter *intel_i2c_create(struct drm_device *dev, const u32 reg,
+ if(i2c_bit_add_bus(&chan->adapter))
+ goto out_free;
+
++ intel_i2c_reset_gmbus(dev);
++
+ /* JJJ: raise SCL and SDA? */
+ intel_i2c_quirk_set(dev, true);
+ set_data(chan, 1);
+diff --git a/drivers/gpu/drm/i915/intel_lvds.c b/drivers/gpu/drm/i915/intel_lvds.c
+index 0e0e4b4..b1d0acb 100644
+--- a/drivers/gpu/drm/i915/intel_lvds.c
++++ b/drivers/gpu/drm/i915/intel_lvds.c
+@@ -56,7 +56,7 @@ static void intel_lvds_set_backlight(struct drm_device *dev, int level)
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 blc_pwm_ctl, reg;
+
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ reg = BLC_PWM_CPU_CTL;
+ else
+ reg = BLC_PWM_CTL;
+@@ -74,7 +74,7 @@ static u32 intel_lvds_get_max_backlight(struct drm_device *dev)
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 reg;
+
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ reg = BLC_PWM_PCH_CTL2;
+ else
+ reg = BLC_PWM_CTL;
+@@ -91,7 +91,7 @@ static void intel_lvds_set_power(struct drm_device *dev, bool on)
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 pp_status, ctl_reg, status_reg;
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ ctl_reg = PCH_PP_CONTROL;
+ status_reg = PCH_PP_STATUS;
+ } else {
+@@ -137,7 +137,7 @@ static void intel_lvds_save(struct drm_connector *connector)
+ u32 pp_on_reg, pp_off_reg, pp_ctl_reg, pp_div_reg;
+ u32 pwm_ctl_reg;
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ pp_on_reg = PCH_PP_ON_DELAYS;
+ pp_off_reg = PCH_PP_OFF_DELAYS;
+ pp_ctl_reg = PCH_PP_CONTROL;
+@@ -174,7 +174,7 @@ static void intel_lvds_restore(struct drm_connector *connector)
+ u32 pp_on_reg, pp_off_reg, pp_ctl_reg, pp_div_reg;
+ u32 pwm_ctl_reg;
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ pp_on_reg = PCH_PP_ON_DELAYS;
+ pp_off_reg = PCH_PP_OFF_DELAYS;
+ pp_ctl_reg = PCH_PP_CONTROL;
+@@ -297,7 +297,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
+ }
+
+ /* full screen scale for now */
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ goto out;
+
+ /* 965+ wants fuzzy fitting */
+@@ -327,7 +327,7 @@ static bool intel_lvds_mode_fixup(struct drm_encoder *encoder,
+ * to register description and PRM.
+ * Change the value here to see the borders for debugging
+ */
+- if (!IS_IGDNG(dev)) {
++ if (!IS_IRONLAKE(dev)) {
+ I915_WRITE(BCLRPAT_A, 0);
+ I915_WRITE(BCLRPAT_B, 0);
+ }
+@@ -548,7 +548,7 @@ static void intel_lvds_prepare(struct drm_encoder *encoder)
+ struct drm_i915_private *dev_priv = dev->dev_private;
+ u32 reg;
+
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ reg = BLC_PWM_CPU_CTL;
+ else
+ reg = BLC_PWM_CTL;
+@@ -587,7 +587,7 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder,
+ * settings.
+ */
+
+- if (IS_IGDNG(dev))
++ if (IS_IRONLAKE(dev))
+ return;
+
+ /*
+@@ -602,12 +602,40 @@ static void intel_lvds_mode_set(struct drm_encoder *encoder,
+ /* Some lid devices report incorrect lid status, assume they're connected */
+ static const struct dmi_system_id bad_lid_status[] = {
+ {
++ .ident = "Compaq nx9020",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
++ DMI_MATCH(DMI_BOARD_NAME, "3084"),
++ },
++ },
++ {
++ .ident = "Samsung SX20S",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Samsung Electronics"),
++ DMI_MATCH(DMI_BOARD_NAME, "SX20S"),
++ },
++ },
++ {
+ .ident = "Aspire One",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire one"),
+ },
+ },
++ {
++ .ident = "Aspire 1810T",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire 1810T"),
++ },
++ },
++ {
++ .ident = "PC-81005",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "MALATA"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "PC-81005"),
++ },
++ },
+ { }
+ };
+
+@@ -622,7 +650,7 @@ static enum drm_connector_status intel_lvds_detect(struct drm_connector *connect
+ if (IS_I8XX(dev))
+ return connector_status_connected;
+
+- if (!acpi_lid_open() && !dmi_check_system(bad_lid_status))
++ if (!dmi_check_system(bad_lid_status) && !acpi_lid_open())
+ status = connector_status_disconnected;
+
+ return status;
+@@ -861,64 +889,101 @@ static const struct dmi_system_id intel_no_lvds[] = {
+ { } /* terminating entry */
+ };
+
+-#ifdef CONFIG_ACPI
+-/*
+- * check_lid_device -- check whether @handle is an ACPI LID device.
+- * @handle: ACPI device handle
+- * @level : depth in the ACPI namespace tree
+- * @context: the number of LID device when we find the device
+- * @rv: a return value to fill if desired (Not use)
++/**
++ * intel_find_lvds_downclock - find the reduced downclock for LVDS in EDID
++ * @dev: drm device
++ * @connector: LVDS connector
++ *
++ * Find the reduced downclock for LVDS in EDID.
+ */
+-static acpi_status
+-check_lid_device(acpi_handle handle, u32 level, void *context,
+- void **return_value)
++static void intel_find_lvds_downclock(struct drm_device *dev,
++ struct drm_connector *connector)
+ {
+- struct acpi_device *acpi_dev;
+- int *lid_present = context;
+-
+- acpi_dev = NULL;
+- /* Get the acpi device for device handle */
+- if (acpi_bus_get_device(handle, &acpi_dev) || !acpi_dev) {
+- /* If there is no ACPI device for handle, return */
+- return AE_OK;
+- }
++ struct drm_i915_private *dev_priv = dev->dev_private;
++ struct drm_display_mode *scan, *panel_fixed_mode;
++ int temp_downclock;
+
+- if (!strncmp(acpi_device_hid(acpi_dev), "PNP0C0D", 7))
+- *lid_present = 1;
++ panel_fixed_mode = dev_priv->panel_fixed_mode;
++ temp_downclock = panel_fixed_mode->clock;
+
+- return AE_OK;
++ mutex_lock(&dev->mode_config.mutex);
++ list_for_each_entry(scan, &connector->probed_modes, head) {
++ /*
++ * If one mode has the same resolution with the fixed_panel
++ * mode while they have the different refresh rate, it means
++ * that the reduced downclock is found for the LVDS. In such
++ * case we can set the different FPx0/1 to dynamically select
++ * between low and high frequency.
++ */
++ if (scan->hdisplay == panel_fixed_mode->hdisplay &&
++ scan->hsync_start == panel_fixed_mode->hsync_start &&
++ scan->hsync_end == panel_fixed_mode->hsync_end &&
++ scan->htotal == panel_fixed_mode->htotal &&
++ scan->vdisplay == panel_fixed_mode->vdisplay &&
++ scan->vsync_start == panel_fixed_mode->vsync_start &&
++ scan->vsync_end == panel_fixed_mode->vsync_end &&
++ scan->vtotal == panel_fixed_mode->vtotal) {
++ if (scan->clock < temp_downclock) {
++ /*
++ * The downclock is already found. But we
++ * expect to find the lower downclock.
++ */
++ temp_downclock = scan->clock;
++ }
++ }
++ }
++ mutex_unlock(&dev->mode_config.mutex);
++ if (temp_downclock < panel_fixed_mode->clock &&
++ i915_lvds_downclock) {
++ /* We found the downclock for LVDS. */
++ dev_priv->lvds_downclock_avail = 1;
++ dev_priv->lvds_downclock = temp_downclock;
++ DRM_DEBUG_KMS("LVDS downclock is found in EDID. "
++ "Normal clock %dKhz, downclock %dKhz\n",
++ panel_fixed_mode->clock, temp_downclock);
++ }
++ return;
+ }
+
+-/**
+- * check whether there exists the ACPI LID device by enumerating the ACPI
+- * device tree.
++/*
++ * Enumerate the child dev array parsed from VBT to check whether
++ * the LVDS is present.
++ * If it is present, return 1.
++ * If it is not present, return false.
++ * If no child dev is parsed from VBT, it assumes that the LVDS is present.
++ * Note: The addin_offset should also be checked for LVDS panel.
++ * Only when it is non-zero, it is assumed that it is present.
+ */
+-static int intel_lid_present(void)
++static int lvds_is_present_in_vbt(struct drm_device *dev)
+ {
+- int lid_present = 0;
++ struct drm_i915_private *dev_priv = dev->dev_private;
++ struct child_device_config *p_child;
++ int i, ret;
+
+- if (acpi_disabled) {
+- /* If ACPI is disabled, there is no ACPI device tree to
+- * check, so assume the LID device would have been present.
+- */
++ if (!dev_priv->child_dev_num)
+ return 1;
+- }
+
+- acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+- ACPI_UINT32_MAX,
+- check_lid_device, &lid_present, NULL);
++ ret = 0;
++ for (i = 0; i < dev_priv->child_dev_num; i++) {
++ p_child = dev_priv->child_dev + i;
++ /*
++ * If the device type is not LFP, continue.
++ * If the device type is 0x22, it is also regarded as LFP.
++ */
++ if (p_child->device_type != DEVICE_TYPE_INT_LFP &&
++ p_child->device_type != DEVICE_TYPE_LFP)
++ continue;
+
+- return lid_present;
+-}
+-#else
+-static int intel_lid_present(void)
+-{
+- /* In the absence of ACPI built in, assume that the LID device would
+- * have been present.
+- */
+- return 1;
++ /* The addin_offset should be checked. Only when it is
++ * non-zero, it is regarded as present.
++ */
++ if (p_child->addin_offset) {
++ ret = 1;
++ break;
++ }
++ }
++ return ret;
+ }
+-#endif
+
+ /**
+ * intel_lvds_init - setup LVDS connectors on this device
+@@ -943,21 +1008,16 @@ void intel_lvds_init(struct drm_device *dev)
+ if (dmi_check_system(intel_no_lvds))
+ return;
+
+- /* Assume that any device without an ACPI LID device also doesn't
+- * have an integrated LVDS. We would be better off parsing the BIOS
+- * to get a reliable indicator, but that code isn't written yet.
+- *
+- * In the case of all-in-one desktops using LVDS that we've seen,
+- * they're using SDVO LVDS.
+- */
+- if (!intel_lid_present())
++ if (!lvds_is_present_in_vbt(dev)) {
++ DRM_DEBUG_KMS("LVDS is not present in VBT\n");
+ return;
++ }
+
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ if ((I915_READ(PCH_LVDS) & LVDS_DETECTED) == 0)
+ return;
+ if (dev_priv->edp_support) {
+- DRM_DEBUG("disable LVDS for eDP support\n");
++ DRM_DEBUG_KMS("disable LVDS for eDP support\n");
+ return;
+ }
+ gpio = PCH_GPIOC;
+@@ -1030,6 +1090,7 @@ void intel_lvds_init(struct drm_device *dev)
+ dev_priv->panel_fixed_mode =
+ drm_mode_duplicate(dev, scan);
+ mutex_unlock(&dev->mode_config.mutex);
++ intel_find_lvds_downclock(dev, connector);
+ goto out;
+ }
+ mutex_unlock(&dev->mode_config.mutex);
+@@ -1054,8 +1115,8 @@ void intel_lvds_init(struct drm_device *dev)
+ * correct mode.
+ */
+
+- /* IGDNG: FIXME if still fail, not try pipe mode now */
+- if (IS_IGDNG(dev))
++ /* Ironlake: FIXME if still fail, not try pipe mode now */
++ if (IS_IRONLAKE(dev))
+ goto failed;
+
+ lvds = I915_READ(LVDS);
+@@ -1076,7 +1137,7 @@ void intel_lvds_init(struct drm_device *dev)
+ goto failed;
+
+ out:
+- if (IS_IGDNG(dev)) {
++ if (IS_IRONLAKE(dev)) {
+ u32 pwm;
+ /* make sure PWM is enabled */
+ pwm = I915_READ(BLC_PWM_CPU_CTL2);
+@@ -1089,7 +1150,7 @@ out:
+ }
+ dev_priv->lid_notifier.notifier_call = intel_lid_notify;
+ if (acpi_lid_notifier_register(&dev_priv->lid_notifier)) {
+- DRM_DEBUG("lid notifier registration failed\n");
++ DRM_DEBUG_KMS("lid notifier registration failed\n");
+ dev_priv->lid_notifier.notifier_call = NULL;
+ }
+ /* keep the LVDS connector */
+@@ -1102,5 +1163,6 @@ failed:
+ if (intel_output->ddc_bus)
+ intel_i2c_destroy(intel_output->ddc_bus);
+ drm_connector_cleanup(connector);
++ drm_encoder_cleanup(encoder);
+ kfree(intel_output);
+ }
+diff --git a/drivers/gpu/drm/i915/intel_overlay.c b/drivers/gpu/drm/i915/intel_overlay.c
+new file mode 100644
+index 0000000..2639591
+--- /dev/null
++++ b/drivers/gpu/drm/i915/intel_overlay.c
+@@ -0,0 +1,1416 @@
++/*
++ * Copyright © 2009
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ *
++ * Authors:
++ * Daniel Vetter <daniel@ffwll.ch>
++ *
++ * Derived from Xorg ddx, xf86-video-intel, src/i830_video.c
++ */
++#include "drmP.h"
++#include "drm.h"
++#include "i915_drm.h"
++#include "i915_drv.h"
++#include "i915_reg.h"
++#include "intel_drv.h"
++
++/* Limits for overlay size. According to intel doc, the real limits are:
++ * Y width: 4095, UV width (planar): 2047, Y height: 2047,
++ * UV width (planar): * 1023. But the xorg thinks 2048 for height and width. Use
++ * the mininum of both. */
++#define IMAGE_MAX_WIDTH 2048
++#define IMAGE_MAX_HEIGHT 2046 /* 2 * 1023 */
++/* on 830 and 845 these large limits result in the card hanging */
++#define IMAGE_MAX_WIDTH_LEGACY 1024
++#define IMAGE_MAX_HEIGHT_LEGACY 1088
++
++/* overlay register definitions */
++/* OCMD register */
++#define OCMD_TILED_SURFACE (0x1<<19)
++#define OCMD_MIRROR_MASK (0x3<<17)
++#define OCMD_MIRROR_MODE (0x3<<17)
++#define OCMD_MIRROR_HORIZONTAL (0x1<<17)
++#define OCMD_MIRROR_VERTICAL (0x2<<17)
++#define OCMD_MIRROR_BOTH (0x3<<17)
++#define OCMD_BYTEORDER_MASK (0x3<<14) /* zero for YUYV or FOURCC YUY2 */
++#define OCMD_UV_SWAP (0x1<<14) /* YVYU */
++#define OCMD_Y_SWAP (0x2<<14) /* UYVY or FOURCC UYVY */
++#define OCMD_Y_AND_UV_SWAP (0x3<<14) /* VYUY */
++#define OCMD_SOURCE_FORMAT_MASK (0xf<<10)
++#define OCMD_RGB_888 (0x1<<10) /* not in i965 Intel docs */
++#define OCMD_RGB_555 (0x2<<10) /* not in i965 Intel docs */
++#define OCMD_RGB_565 (0x3<<10) /* not in i965 Intel docs */
++#define OCMD_YUV_422_PACKED (0x8<<10)
++#define OCMD_YUV_411_PACKED (0x9<<10) /* not in i965 Intel docs */
++#define OCMD_YUV_420_PLANAR (0xc<<10)
++#define OCMD_YUV_422_PLANAR (0xd<<10)
++#define OCMD_YUV_410_PLANAR (0xe<<10) /* also 411 */
++#define OCMD_TVSYNCFLIP_PARITY (0x1<<9)
++#define OCMD_TVSYNCFLIP_ENABLE (0x1<<7)
++#define OCMD_BUF_TYPE_MASK (Ox1<<5)
++#define OCMD_BUF_TYPE_FRAME (0x0<<5)
++#define OCMD_BUF_TYPE_FIELD (0x1<<5)
++#define OCMD_TEST_MODE (0x1<<4)
++#define OCMD_BUFFER_SELECT (0x3<<2)
++#define OCMD_BUFFER0 (0x0<<2)
++#define OCMD_BUFFER1 (0x1<<2)
++#define OCMD_FIELD_SELECT (0x1<<2)
++#define OCMD_FIELD0 (0x0<<1)
++#define OCMD_FIELD1 (0x1<<1)
++#define OCMD_ENABLE (0x1<<0)
++
++/* OCONFIG register */
++#define OCONF_PIPE_MASK (0x1<<18)
++#define OCONF_PIPE_A (0x0<<18)
++#define OCONF_PIPE_B (0x1<<18)
++#define OCONF_GAMMA2_ENABLE (0x1<<16)
++#define OCONF_CSC_MODE_BT601 (0x0<<5)
++#define OCONF_CSC_MODE_BT709 (0x1<<5)
++#define OCONF_CSC_BYPASS (0x1<<4)
++#define OCONF_CC_OUT_8BIT (0x1<<3)
++#define OCONF_TEST_MODE (0x1<<2)
++#define OCONF_THREE_LINE_BUFFER (0x1<<0)
++#define OCONF_TWO_LINE_BUFFER (0x0<<0)
++
++/* DCLRKM (dst-key) register */
++#define DST_KEY_ENABLE (0x1<<31)
++#define CLK_RGB24_MASK 0x0
++#define CLK_RGB16_MASK 0x070307
++#define CLK_RGB15_MASK 0x070707
++#define CLK_RGB8I_MASK 0xffffff
++
++#define RGB16_TO_COLORKEY(c) \
++ (((c & 0xF800) << 8) | ((c & 0x07E0) << 5) | ((c & 0x001F) << 3))
++#define RGB15_TO_COLORKEY(c) \
++ (((c & 0x7c00) << 9) | ((c & 0x03E0) << 6) | ((c & 0x001F) << 3))
++
++/* overlay flip addr flag */
++#define OFC_UPDATE 0x1
++
++/* polyphase filter coefficients */
++#define N_HORIZ_Y_TAPS 5
++#define N_VERT_Y_TAPS 3
++#define N_HORIZ_UV_TAPS 3
++#define N_VERT_UV_TAPS 3
++#define N_PHASES 17
++#define MAX_TAPS 5
++
++/* memory bufferd overlay registers */
++struct overlay_registers {
++ u32 OBUF_0Y;
++ u32 OBUF_1Y;
++ u32 OBUF_0U;
++ u32 OBUF_0V;
++ u32 OBUF_1U;
++ u32 OBUF_1V;
++ u32 OSTRIDE;
++ u32 YRGB_VPH;
++ u32 UV_VPH;
++ u32 HORZ_PH;
++ u32 INIT_PHS;
++ u32 DWINPOS;
++ u32 DWINSZ;
++ u32 SWIDTH;
++ u32 SWIDTHSW;
++ u32 SHEIGHT;
++ u32 YRGBSCALE;
++ u32 UVSCALE;
++ u32 OCLRC0;
++ u32 OCLRC1;
++ u32 DCLRKV;
++ u32 DCLRKM;
++ u32 SCLRKVH;
++ u32 SCLRKVL;
++ u32 SCLRKEN;
++ u32 OCONFIG;
++ u32 OCMD;
++ u32 RESERVED1; /* 0x6C */
++ u32 OSTART_0Y;
++ u32 OSTART_1Y;
++ u32 OSTART_0U;
++ u32 OSTART_0V;
++ u32 OSTART_1U;
++ u32 OSTART_1V;
++ u32 OTILEOFF_0Y;
++ u32 OTILEOFF_1Y;
++ u32 OTILEOFF_0U;
++ u32 OTILEOFF_0V;
++ u32 OTILEOFF_1U;
++ u32 OTILEOFF_1V;
++ u32 FASTHSCALE; /* 0xA0 */
++ u32 UVSCALEV; /* 0xA4 */
++ u32 RESERVEDC[(0x200 - 0xA8) / 4]; /* 0xA8 - 0x1FC */
++ u16 Y_VCOEFS[N_VERT_Y_TAPS * N_PHASES]; /* 0x200 */
++ u16 RESERVEDD[0x100 / 2 - N_VERT_Y_TAPS * N_PHASES];
++ u16 Y_HCOEFS[N_HORIZ_Y_TAPS * N_PHASES]; /* 0x300 */
++ u16 RESERVEDE[0x200 / 2 - N_HORIZ_Y_TAPS * N_PHASES];
++ u16 UV_VCOEFS[N_VERT_UV_TAPS * N_PHASES]; /* 0x500 */
++ u16 RESERVEDF[0x100 / 2 - N_VERT_UV_TAPS * N_PHASES];
++ u16 UV_HCOEFS[N_HORIZ_UV_TAPS * N_PHASES]; /* 0x600 */
++ u16 RESERVEDG[0x100 / 2 - N_HORIZ_UV_TAPS * N_PHASES];
++};
++
++/* overlay flip addr flag */
++#define OFC_UPDATE 0x1
++
++#define OVERLAY_NONPHYSICAL(dev) (IS_G33(dev) || IS_I965G(dev))
++#define OVERLAY_EXISTS(dev) (!IS_G4X(dev) && !IS_IRONLAKE(dev))
++
++
++static struct overlay_registers *intel_overlay_map_regs_atomic(struct intel_overlay *overlay)
++{
++ drm_i915_private_t *dev_priv = overlay->dev->dev_private;
++ struct overlay_registers *regs;
++
++ /* no recursive mappings */
++ BUG_ON(overlay->virt_addr);
++
++ if (OVERLAY_NONPHYSICAL(overlay->dev)) {
++ regs = io_mapping_map_atomic_wc(dev_priv->mm.gtt_mapping,
++ overlay->reg_bo->gtt_offset);
++
++ if (!regs) {
++ DRM_ERROR("failed to map overlay regs in GTT\n");
++ return NULL;
++ }
++ } else
++ regs = overlay->reg_bo->phys_obj->handle->vaddr;
++
++ return overlay->virt_addr = regs;
++}
++
++static void intel_overlay_unmap_regs_atomic(struct intel_overlay *overlay)
++{
++ struct drm_device *dev = overlay->dev;
++ drm_i915_private_t *dev_priv = dev->dev_private;
++
++ if (OVERLAY_NONPHYSICAL(overlay->dev))
++ io_mapping_unmap_atomic(overlay->virt_addr);
++
++ overlay->virt_addr = NULL;
++
++ I915_READ(OVADD); /* flush wc cashes */
++
++ return;
++}
++
++/* overlay needs to be disable in OCMD reg */
++static int intel_overlay_on(struct intel_overlay *overlay)
++{
++ struct drm_device *dev = overlay->dev;
++ drm_i915_private_t *dev_priv = dev->dev_private;
++ int ret;
++ RING_LOCALS;
++
++ BUG_ON(overlay->active);
++
++ overlay->active = 1;
++ overlay->hw_wedged = NEEDS_WAIT_FOR_FLIP;
++
++ BEGIN_LP_RING(6);
++ OUT_RING(MI_FLUSH);
++ OUT_RING(MI_NOOP);
++ OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_ON);
++ OUT_RING(overlay->flip_addr | OFC_UPDATE);
++ OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
++ OUT_RING(MI_NOOP);
++ ADVANCE_LP_RING();
++
++ overlay->last_flip_req = i915_add_request(dev, NULL, 0);
++ if (overlay->last_flip_req == 0)
++ return -ENOMEM;
++
++ ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
++ if (ret != 0)
++ return ret;
++
++ overlay->hw_wedged = 0;
++ overlay->last_flip_req = 0;
++ return 0;
++}
++
++/* overlay needs to be enabled in OCMD reg */
++static void intel_overlay_continue(struct intel_overlay *overlay,
++ bool load_polyphase_filter)
++{
++ struct drm_device *dev = overlay->dev;
++ drm_i915_private_t *dev_priv = dev->dev_private;
++ u32 flip_addr = overlay->flip_addr;
++ u32 tmp;
++ RING_LOCALS;
++
++ BUG_ON(!overlay->active);
++
++ if (load_polyphase_filter)
++ flip_addr |= OFC_UPDATE;
++
++ /* check for underruns */
++ tmp = I915_READ(DOVSTA);
++ if (tmp & (1 << 17))
++ DRM_DEBUG("overlay underrun, DOVSTA: %x\n", tmp);
++
++ BEGIN_LP_RING(4);
++ OUT_RING(MI_FLUSH);
++ OUT_RING(MI_NOOP);
++ OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
++ OUT_RING(flip_addr);
++ ADVANCE_LP_RING();
++
++ overlay->last_flip_req = i915_add_request(dev, NULL, 0);
++}
++
++static int intel_overlay_wait_flip(struct intel_overlay *overlay)
++{
++ struct drm_device *dev = overlay->dev;
++ drm_i915_private_t *dev_priv = dev->dev_private;
++ int ret;
++ u32 tmp;
++ RING_LOCALS;
++
++ if (overlay->last_flip_req != 0) {
++ ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
++ if (ret == 0) {
++ overlay->last_flip_req = 0;
++
++ tmp = I915_READ(ISR);
++
++ if (!(tmp & I915_OVERLAY_PLANE_FLIP_PENDING_INTERRUPT))
++ return 0;
++ }
++ }
++
++ /* synchronous slowpath */
++ overlay->hw_wedged = RELEASE_OLD_VID;
++
++ BEGIN_LP_RING(2);
++ OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
++ OUT_RING(MI_NOOP);
++ ADVANCE_LP_RING();
++
++ overlay->last_flip_req = i915_add_request(dev, NULL, 0);
++ if (overlay->last_flip_req == 0)
++ return -ENOMEM;
++
++ ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
++ if (ret != 0)
++ return ret;
++
++ overlay->hw_wedged = 0;
++ overlay->last_flip_req = 0;
++ return 0;
++}
++
++/* overlay needs to be disabled in OCMD reg */
++static int intel_overlay_off(struct intel_overlay *overlay)
++{
++ u32 flip_addr = overlay->flip_addr;
++ struct drm_device *dev = overlay->dev;
++ drm_i915_private_t *dev_priv = dev->dev_private;
++ int ret;
++ RING_LOCALS;
++
++ BUG_ON(!overlay->active);
++
++ /* According to intel docs the overlay hw may hang (when switching
++ * off) without loading the filter coeffs. It is however unclear whether
++ * this applies to the disabling of the overlay or to the switching off
++ * of the hw. Do it in both cases */
++ flip_addr |= OFC_UPDATE;
++
++ /* wait for overlay to go idle */
++ overlay->hw_wedged = SWITCH_OFF_STAGE_1;
++
++ BEGIN_LP_RING(6);
++ OUT_RING(MI_FLUSH);
++ OUT_RING(MI_NOOP);
++ OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_CONTINUE);
++ OUT_RING(flip_addr);
++ OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
++ OUT_RING(MI_NOOP);
++ ADVANCE_LP_RING();
++
++ overlay->last_flip_req = i915_add_request(dev, NULL, 0);
++ if (overlay->last_flip_req == 0)
++ return -ENOMEM;
++
++ ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
++ if (ret != 0)
++ return ret;
++
++ /* turn overlay off */
++ overlay->hw_wedged = SWITCH_OFF_STAGE_2;
++
++ BEGIN_LP_RING(6);
++ OUT_RING(MI_FLUSH);
++ OUT_RING(MI_NOOP);
++ OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF);
++ OUT_RING(flip_addr);
++ OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
++ OUT_RING(MI_NOOP);
++ ADVANCE_LP_RING();
++
++ overlay->last_flip_req = i915_add_request(dev, NULL, 0);
++ if (overlay->last_flip_req == 0)
++ return -ENOMEM;
++
++ ret = i915_do_wait_request(dev, overlay->last_flip_req, 1);
++ if (ret != 0)
++ return ret;
++
++ overlay->hw_wedged = 0;
++ overlay->last_flip_req = 0;
++ return ret;
++}
++
++static void intel_overlay_off_tail(struct intel_overlay *overlay)
++{
++ struct drm_gem_object *obj;
++
++ /* never have the overlay hw on without showing a frame */
++ BUG_ON(!overlay->vid_bo);
++ obj = overlay->vid_bo->obj;
++
++ i915_gem_object_unpin(obj);
++ drm_gem_object_unreference(obj);
++ overlay->vid_bo = NULL;
++
++ overlay->crtc->overlay = NULL;
++ overlay->crtc = NULL;
++ overlay->active = 0;
++}
++
++/* recover from an interruption due to a signal
++ * We have to be careful not to repeat work forever an make forward progess. */
++int intel_overlay_recover_from_interrupt(struct intel_overlay *overlay,
++ int interruptible)
++{
++ struct drm_device *dev = overlay->dev;
++ drm_i915_private_t *dev_priv = dev->dev_private;
++ struct drm_gem_object *obj;
++ u32 flip_addr;
++ int ret;
++ RING_LOCALS;
++
++ if (overlay->hw_wedged == HW_WEDGED)
++ return -EIO;
++
++ if (overlay->last_flip_req == 0) {
++ overlay->last_flip_req = i915_add_request(dev, NULL, 0);
++ if (overlay->last_flip_req == 0)
++ return -ENOMEM;
++ }
++
++ ret = i915_do_wait_request(dev, overlay->last_flip_req, interruptible);
++ if (ret != 0)
++ return ret;
++
++ switch (overlay->hw_wedged) {
++ case RELEASE_OLD_VID:
++ obj = overlay->old_vid_bo->obj;
++ i915_gem_object_unpin(obj);
++ drm_gem_object_unreference(obj);
++ overlay->old_vid_bo = NULL;
++ break;
++ case SWITCH_OFF_STAGE_1:
++ flip_addr = overlay->flip_addr;
++ flip_addr |= OFC_UPDATE;
++
++ overlay->hw_wedged = SWITCH_OFF_STAGE_2;
++
++ BEGIN_LP_RING(6);
++ OUT_RING(MI_FLUSH);
++ OUT_RING(MI_NOOP);
++ OUT_RING(MI_OVERLAY_FLIP | MI_OVERLAY_OFF);
++ OUT_RING(flip_addr);
++ OUT_RING(MI_WAIT_FOR_EVENT | MI_WAIT_FOR_OVERLAY_FLIP);
++ OUT_RING(MI_NOOP);
++ ADVANCE_LP_RING();
++
++ overlay->last_flip_req = i915_add_request(dev, NULL, 0);
++ if (overlay->last_flip_req == 0)
++ return -ENOMEM;
++
++ ret = i915_do_wait_request(dev, overlay->last_flip_req,
++ interruptible);
++ if (ret != 0)
++ return ret;
++
++ case SWITCH_OFF_STAGE_2:
++ intel_overlay_off_tail(overlay);
++ break;
++ default:
++ BUG_ON(overlay->hw_wedged != NEEDS_WAIT_FOR_FLIP);
++ }
++
++ overlay->hw_wedged = 0;
++ overlay->last_flip_req = 0;
++ return 0;
++}
++
++/* Wait for pending overlay flip and release old frame.
++ * Needs to be called before the overlay register are changed
++ * via intel_overlay_(un)map_regs_atomic */
++static int intel_overlay_release_old_vid(struct intel_overlay *overlay)
++{
++ int ret;
++ struct drm_gem_object *obj;
++
++ /* only wait if there is actually an old frame to release to
++ * guarantee forward progress */
++ if (!overlay->old_vid_bo)
++ return 0;
++
++ ret = intel_overlay_wait_flip(overlay);
++ if (ret != 0)
++ return ret;
++
++ obj = overlay->old_vid_bo->obj;
++ i915_gem_object_unpin(obj);
++ drm_gem_object_unreference(obj);
++ overlay->old_vid_bo = NULL;
++
++ return 0;
++}
++
++struct put_image_params {
++ int format;
++ short dst_x;
++ short dst_y;
++ short dst_w;
++ short dst_h;
++ short src_w;
++ short src_scan_h;
++ short src_scan_w;
++ short src_h;
++ short stride_Y;
++ short stride_UV;
++ int offset_Y;
++ int offset_U;
++ int offset_V;
++};
++
++static int packed_depth_bytes(u32 format)
++{
++ switch (format & I915_OVERLAY_DEPTH_MASK) {
++ case I915_OVERLAY_YUV422:
++ return 4;
++ case I915_OVERLAY_YUV411:
++ /* return 6; not implemented */
++ default:
++ return -EINVAL;
++ }
++}
++
++static int packed_width_bytes(u32 format, short width)
++{
++ switch (format & I915_OVERLAY_DEPTH_MASK) {
++ case I915_OVERLAY_YUV422:
++ return width << 1;
++ default:
++ return -EINVAL;
++ }
++}
++
++static int uv_hsubsampling(u32 format)
++{
++ switch (format & I915_OVERLAY_DEPTH_MASK) {
++ case I915_OVERLAY_YUV422:
++ case I915_OVERLAY_YUV420:
++ return 2;
++ case I915_OVERLAY_YUV411:
++ case I915_OVERLAY_YUV410:
++ return 4;
++ default:
++ return -EINVAL;
++ }
++}
++
++static int uv_vsubsampling(u32 format)
++{
++ switch (format & I915_OVERLAY_DEPTH_MASK) {
++ case I915_OVERLAY_YUV420:
++ case I915_OVERLAY_YUV410:
++ return 2;
++ case I915_OVERLAY_YUV422:
++ case I915_OVERLAY_YUV411:
++ return 1;
++ default:
++ return -EINVAL;
++ }
++}
++
++static u32 calc_swidthsw(struct drm_device *dev, u32 offset, u32 width)
++{
++ u32 mask, shift, ret;
++ if (IS_I9XX(dev)) {
++ mask = 0x3f;
++ shift = 6;
++ } else {
++ mask = 0x1f;
++ shift = 5;
++ }
++ ret = ((offset + width + mask) >> shift) - (offset >> shift);
++ if (IS_I9XX(dev))
++ ret <<= 1;
++ ret -=1;
++ return ret << 2;
++}
++
++static const u16 y_static_hcoeffs[N_HORIZ_Y_TAPS * N_PHASES] = {
++ 0x3000, 0xb4a0, 0x1930, 0x1920, 0xb4a0,
++ 0x3000, 0xb500, 0x19d0, 0x1880, 0xb440,
++ 0x3000, 0xb540, 0x1a88, 0x2f80, 0xb3e0,
++ 0x3000, 0xb580, 0x1b30, 0x2e20, 0xb380,
++ 0x3000, 0xb5c0, 0x1bd8, 0x2cc0, 0xb320,
++ 0x3020, 0xb5e0, 0x1c60, 0x2b80, 0xb2c0,
++ 0x3020, 0xb5e0, 0x1cf8, 0x2a20, 0xb260,
++ 0x3020, 0xb5e0, 0x1d80, 0x28e0, 0xb200,
++ 0x3020, 0xb5c0, 0x1e08, 0x3f40, 0xb1c0,
++ 0x3020, 0xb580, 0x1e78, 0x3ce0, 0xb160,
++ 0x3040, 0xb520, 0x1ed8, 0x3aa0, 0xb120,
++ 0x3040, 0xb4a0, 0x1f30, 0x3880, 0xb0e0,
++ 0x3040, 0xb400, 0x1f78, 0x3680, 0xb0a0,
++ 0x3020, 0xb340, 0x1fb8, 0x34a0, 0xb060,
++ 0x3020, 0xb240, 0x1fe0, 0x32e0, 0xb040,
++ 0x3020, 0xb140, 0x1ff8, 0x3160, 0xb020,
++ 0xb000, 0x3000, 0x0800, 0x3000, 0xb000};
++static const u16 uv_static_hcoeffs[N_HORIZ_UV_TAPS * N_PHASES] = {
++ 0x3000, 0x1800, 0x1800, 0xb000, 0x18d0, 0x2e60,
++ 0xb000, 0x1990, 0x2ce0, 0xb020, 0x1a68, 0x2b40,
++ 0xb040, 0x1b20, 0x29e0, 0xb060, 0x1bd8, 0x2880,
++ 0xb080, 0x1c88, 0x3e60, 0xb0a0, 0x1d28, 0x3c00,
++ 0xb0c0, 0x1db8, 0x39e0, 0xb0e0, 0x1e40, 0x37e0,
++ 0xb100, 0x1eb8, 0x3620, 0xb100, 0x1f18, 0x34a0,
++ 0xb100, 0x1f68, 0x3360, 0xb0e0, 0x1fa8, 0x3240,
++ 0xb0c0, 0x1fe0, 0x3140, 0xb060, 0x1ff0, 0x30a0,
++ 0x3000, 0x0800, 0x3000};
++
++static void update_polyphase_filter(struct overlay_registers *regs)
++{
++ memcpy(regs->Y_HCOEFS, y_static_hcoeffs, sizeof(y_static_hcoeffs));
++ memcpy(regs->UV_HCOEFS, uv_static_hcoeffs, sizeof(uv_static_hcoeffs));
++}
++
++static bool update_scaling_factors(struct intel_overlay *overlay,
++ struct overlay_registers *regs,
++ struct put_image_params *params)
++{
++ /* fixed point with a 12 bit shift */
++ u32 xscale, yscale, xscale_UV, yscale_UV;
++#define FP_SHIFT 12
++#define FRACT_MASK 0xfff
++ bool scale_changed = false;
++ int uv_hscale = uv_hsubsampling(params->format);
++ int uv_vscale = uv_vsubsampling(params->format);
++
++ if (params->dst_w > 1)
++ xscale = ((params->src_scan_w - 1) << FP_SHIFT)
++ /(params->dst_w);
++ else
++ xscale = 1 << FP_SHIFT;
++
++ if (params->dst_h > 1)
++ yscale = ((params->src_scan_h - 1) << FP_SHIFT)
++ /(params->dst_h);
++ else
++ yscale = 1 << FP_SHIFT;
++
++ /*if (params->format & I915_OVERLAY_YUV_PLANAR) {*/
++ xscale_UV = xscale/uv_hscale;
++ yscale_UV = yscale/uv_vscale;
++ /* make the Y scale to UV scale ratio an exact multiply */
++ xscale = xscale_UV * uv_hscale;
++ yscale = yscale_UV * uv_vscale;
++ /*} else {
++ xscale_UV = 0;
++ yscale_UV = 0;
++ }*/
++
++ if (xscale != overlay->old_xscale || yscale != overlay->old_yscale)
++ scale_changed = true;
++ overlay->old_xscale = xscale;
++ overlay->old_yscale = yscale;
++
++ regs->YRGBSCALE = ((yscale & FRACT_MASK) << 20)
++ | ((xscale >> FP_SHIFT) << 16)
++ | ((xscale & FRACT_MASK) << 3);
++ regs->UVSCALE = ((yscale_UV & FRACT_MASK) << 20)
++ | ((xscale_UV >> FP_SHIFT) << 16)
++ | ((xscale_UV & FRACT_MASK) << 3);
++ regs->UVSCALEV = ((yscale >> FP_SHIFT) << 16)
++ | ((yscale_UV >> FP_SHIFT) << 0);
++
++ if (scale_changed)
++ update_polyphase_filter(regs);
++
++ return scale_changed;
++}
++
++static void update_colorkey(struct intel_overlay *overlay,
++ struct overlay_registers *regs)
++{
++ u32 key = overlay->color_key;
++ switch (overlay->crtc->base.fb->bits_per_pixel) {
++ case 8:
++ regs->DCLRKV = 0;
++ regs->DCLRKM = CLK_RGB8I_MASK | DST_KEY_ENABLE;
++ case 16:
++ if (overlay->crtc->base.fb->depth == 15) {
++ regs->DCLRKV = RGB15_TO_COLORKEY(key);
++ regs->DCLRKM = CLK_RGB15_MASK | DST_KEY_ENABLE;
++ } else {
++ regs->DCLRKV = RGB16_TO_COLORKEY(key);
++ regs->DCLRKM = CLK_RGB16_MASK | DST_KEY_ENABLE;
++ }
++ case 24:
++ case 32:
++ regs->DCLRKV = key;
++ regs->DCLRKM = CLK_RGB24_MASK | DST_KEY_ENABLE;
++ }
++}
++
++static u32 overlay_cmd_reg(struct put_image_params *params)
++{
++ u32 cmd = OCMD_ENABLE | OCMD_BUF_TYPE_FRAME | OCMD_BUFFER0;
++
++ if (params->format & I915_OVERLAY_YUV_PLANAR) {
++ switch (params->format & I915_OVERLAY_DEPTH_MASK) {
++ case I915_OVERLAY_YUV422:
++ cmd |= OCMD_YUV_422_PLANAR;
++ break;
++ case I915_OVERLAY_YUV420:
++ cmd |= OCMD_YUV_420_PLANAR;
++ break;
++ case I915_OVERLAY_YUV411:
++ case I915_OVERLAY_YUV410:
++ cmd |= OCMD_YUV_410_PLANAR;
++ break;
++ }
++ } else { /* YUV packed */
++ switch (params->format & I915_OVERLAY_DEPTH_MASK) {
++ case I915_OVERLAY_YUV422:
++ cmd |= OCMD_YUV_422_PACKED;
++ break;
++ case I915_OVERLAY_YUV411:
++ cmd |= OCMD_YUV_411_PACKED;
++ break;
++ }
++
++ switch (params->format & I915_OVERLAY_SWAP_MASK) {
++ case I915_OVERLAY_NO_SWAP:
++ break;
++ case I915_OVERLAY_UV_SWAP:
++ cmd |= OCMD_UV_SWAP;
++ break;
++ case I915_OVERLAY_Y_SWAP:
++ cmd |= OCMD_Y_SWAP;
++ break;
++ case I915_OVERLAY_Y_AND_UV_SWAP:
++ cmd |= OCMD_Y_AND_UV_SWAP;
++ break;
++ }
++ }
++
++ return cmd;
++}
++
++int intel_overlay_do_put_image(struct intel_overlay *overlay,
++ struct drm_gem_object *new_bo,
++ struct put_image_params *params)
++{
++ int ret, tmp_width;
++ struct overlay_registers *regs;
++ bool scale_changed = false;
++ struct drm_i915_gem_object *bo_priv = new_bo->driver_private;
++ struct drm_device *dev = overlay->dev;
++
++ BUG_ON(!mutex_is_locked(&dev->struct_mutex));
++ BUG_ON(!mutex_is_locked(&dev->mode_config.mutex));
++ BUG_ON(!overlay);
++
++ ret = intel_overlay_release_old_vid(overlay);
++ if (ret != 0)
++ return ret;
++
++ ret = i915_gem_object_pin(new_bo, PAGE_SIZE);
++ if (ret != 0)
++ return ret;
++
++ ret = i915_gem_object_set_to_gtt_domain(new_bo, 0);
++ if (ret != 0)
++ goto out_unpin;
++
++ if (!overlay->active) {
++ regs = intel_overlay_map_regs_atomic(overlay);
++ if (!regs) {
++ ret = -ENOMEM;
++ goto out_unpin;
++ }
++ regs->OCONFIG = OCONF_CC_OUT_8BIT;
++ if (IS_I965GM(overlay->dev))
++ regs->OCONFIG |= OCONF_CSC_MODE_BT709;
++ regs->OCONFIG |= overlay->crtc->pipe == 0 ?
++ OCONF_PIPE_A : OCONF_PIPE_B;
++ intel_overlay_unmap_regs_atomic(overlay);
++
++ ret = intel_overlay_on(overlay);
++ if (ret != 0)
++ goto out_unpin;
++ }
++
++ regs = intel_overlay_map_regs_atomic(overlay);
++ if (!regs) {
++ ret = -ENOMEM;
++ goto out_unpin;
++ }
++
++ regs->DWINPOS = (params->dst_y << 16) | params->dst_x;
++ regs->DWINSZ = (params->dst_h << 16) | params->dst_w;
++
++ if (params->format & I915_OVERLAY_YUV_PACKED)
++ tmp_width = packed_width_bytes(params->format, params->src_w);
++ else
++ tmp_width = params->src_w;
++
++ regs->SWIDTH = params->src_w;
++ regs->SWIDTHSW = calc_swidthsw(overlay->dev,
++ params->offset_Y, tmp_width);
++ regs->SHEIGHT = params->src_h;
++ regs->OBUF_0Y = bo_priv->gtt_offset + params-> offset_Y;
++ regs->OSTRIDE = params->stride_Y;
++
++ if (params->format & I915_OVERLAY_YUV_PLANAR) {
++ int uv_hscale = uv_hsubsampling(params->format);
++ int uv_vscale = uv_vsubsampling(params->format);
++ u32 tmp_U, tmp_V;
++ regs->SWIDTH |= (params->src_w/uv_hscale) << 16;
++ tmp_U = calc_swidthsw(overlay->dev, params->offset_U,
++ params->src_w/uv_hscale);
++ tmp_V = calc_swidthsw(overlay->dev, params->offset_V,
++ params->src_w/uv_hscale);
++ regs->SWIDTHSW |= max_t(u32, tmp_U, tmp_V) << 16;
++ regs->SHEIGHT |= (params->src_h/uv_vscale) << 16;
++ regs->OBUF_0U = bo_priv->gtt_offset + params->offset_U;
++ regs->OBUF_0V = bo_priv->gtt_offset + params->offset_V;
++ regs->OSTRIDE |= params->stride_UV << 16;
++ }
++
++ scale_changed = update_scaling_factors(overlay, regs, params);
++
++ update_colorkey(overlay, regs);
++
++ regs->OCMD = overlay_cmd_reg(params);
++
++ intel_overlay_unmap_regs_atomic(overlay);
++
++ intel_overlay_continue(overlay, scale_changed);
++
++ overlay->old_vid_bo = overlay->vid_bo;
++ overlay->vid_bo = new_bo->driver_private;
++
++ return 0;
++
++out_unpin:
++ i915_gem_object_unpin(new_bo);
++ return ret;
++}
++
++int intel_overlay_switch_off(struct intel_overlay *overlay)
++{
++ int ret;
++ struct overlay_registers *regs;
++ struct drm_device *dev = overlay->dev;
++
++ BUG_ON(!mutex_is_locked(&dev->struct_mutex));
++ BUG_ON(!mutex_is_locked(&dev->mode_config.mutex));
++
++ if (overlay->hw_wedged) {
++ ret = intel_overlay_recover_from_interrupt(overlay, 1);
++ if (ret != 0)
++ return ret;
++ }
++
++ if (!overlay->active)
++ return 0;
++
++ ret = intel_overlay_release_old_vid(overlay);
++ if (ret != 0)
++ return ret;
++
++ regs = intel_overlay_map_regs_atomic(overlay);
++ regs->OCMD = 0;
++ intel_overlay_unmap_regs_atomic(overlay);
++
++ ret = intel_overlay_off(overlay);
++ if (ret != 0)
++ return ret;
++
++ intel_overlay_off_tail(overlay);
++
++ return 0;
++}
++
++static int check_overlay_possible_on_crtc(struct intel_overlay *overlay,
++ struct intel_crtc *crtc)
++{
++ drm_i915_private_t *dev_priv = overlay->dev->dev_private;
++ u32 pipeconf;
++ int pipeconf_reg = (crtc->pipe == 0) ? PIPEACONF : PIPEBCONF;
++
++ if (!crtc->base.enabled || crtc->dpms_mode != DRM_MODE_DPMS_ON)
++ return -EINVAL;
++
++ pipeconf = I915_READ(pipeconf_reg);
++
++ /* can't use the overlay with double wide pipe */
++ if (!IS_I965G(overlay->dev) && pipeconf & PIPEACONF_DOUBLE_WIDE)
++ return -EINVAL;
++
++ return 0;
++}
++
++static void update_pfit_vscale_ratio(struct intel_overlay *overlay)
++{
++ struct drm_device *dev = overlay->dev;
++ drm_i915_private_t *dev_priv = dev->dev_private;
++ u32 ratio;
++ u32 pfit_control = I915_READ(PFIT_CONTROL);
++
++ /* XXX: This is not the same logic as in the xorg driver, but more in
++ * line with the intel documentation for the i965 */
++ if (!IS_I965G(dev) && (pfit_control & VERT_AUTO_SCALE)) {
++ ratio = I915_READ(PFIT_AUTO_RATIOS) >> PFIT_VERT_SCALE_SHIFT;
++ } else { /* on i965 use the PGM reg to read out the autoscaler values */
++ ratio = I915_READ(PFIT_PGM_RATIOS);
++ if (IS_I965G(dev))
++ ratio >>= PFIT_VERT_SCALE_SHIFT_965;
++ else
++ ratio >>= PFIT_VERT_SCALE_SHIFT;
++ }
++
++ overlay->pfit_vscale_ratio = ratio;
++}
++
++static int check_overlay_dst(struct intel_overlay *overlay,
++ struct drm_intel_overlay_put_image *rec)
++{
++ struct drm_display_mode *mode = &overlay->crtc->base.mode;
++
++ if ((rec->dst_x < mode->crtc_hdisplay)
++ && (rec->dst_x + rec->dst_width
++ <= mode->crtc_hdisplay)
++ && (rec->dst_y < mode->crtc_vdisplay)
++ && (rec->dst_y + rec->dst_height
++ <= mode->crtc_vdisplay))
++ return 0;
++ else
++ return -EINVAL;
++}
++
++static int check_overlay_scaling(struct put_image_params *rec)
++{
++ u32 tmp;
++
++ /* downscaling limit is 8.0 */
++ tmp = ((rec->src_scan_h << 16) / rec->dst_h) >> 16;
++ if (tmp > 7)
++ return -EINVAL;
++ tmp = ((rec->src_scan_w << 16) / rec->dst_w) >> 16;
++ if (tmp > 7)
++ return -EINVAL;
++
++ return 0;
++}
++
++static int check_overlay_src(struct drm_device *dev,
++ struct drm_intel_overlay_put_image *rec,
++ struct drm_gem_object *new_bo)
++{
++ u32 stride_mask;
++ int depth;
++ int uv_hscale = uv_hsubsampling(rec->flags);
++ int uv_vscale = uv_vsubsampling(rec->flags);
++ size_t tmp;
++
++ /* check src dimensions */
++ if (IS_845G(dev) || IS_I830(dev)) {
++ if (rec->src_height > IMAGE_MAX_HEIGHT_LEGACY
++ || rec->src_width > IMAGE_MAX_WIDTH_LEGACY)
++ return -EINVAL;
++ } else {
++ if (rec->src_height > IMAGE_MAX_HEIGHT
++ || rec->src_width > IMAGE_MAX_WIDTH)
++ return -EINVAL;
++ }
++ /* better safe than sorry, use 4 as the maximal subsampling ratio */
++ if (rec->src_height < N_VERT_Y_TAPS*4
++ || rec->src_width < N_HORIZ_Y_TAPS*4)
++ return -EINVAL;
++
++ /* check alingment constrains */
++ switch (rec->flags & I915_OVERLAY_TYPE_MASK) {
++ case I915_OVERLAY_RGB:
++ /* not implemented */
++ return -EINVAL;
++ case I915_OVERLAY_YUV_PACKED:
++ depth = packed_depth_bytes(rec->flags);
++ if (uv_vscale != 1)
++ return -EINVAL;
++ if (depth < 0)
++ return depth;
++ /* ignore UV planes */
++ rec->stride_UV = 0;
++ rec->offset_U = 0;
++ rec->offset_V = 0;
++ /* check pixel alignment */
++ if (rec->offset_Y % depth)
++ return -EINVAL;
++ break;
++ case I915_OVERLAY_YUV_PLANAR:
++ if (uv_vscale < 0 || uv_hscale < 0)
++ return -EINVAL;
++ /* no offset restrictions for planar formats */
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ if (rec->src_width % uv_hscale)
++ return -EINVAL;
++
++ /* stride checking */
++ stride_mask = 63;
++
++ if (rec->stride_Y & stride_mask || rec->stride_UV & stride_mask)
++ return -EINVAL;
++ if (IS_I965G(dev) && rec->stride_Y < 512)
++ return -EINVAL;
++
++ tmp = (rec->flags & I915_OVERLAY_TYPE_MASK) == I915_OVERLAY_YUV_PLANAR ?
++ 4 : 8;
++ if (rec->stride_Y > tmp*1024 || rec->stride_UV > 2*1024)
++ return -EINVAL;
++
++ /* check buffer dimensions */
++ switch (rec->flags & I915_OVERLAY_TYPE_MASK) {
++ case I915_OVERLAY_RGB:
++ case I915_OVERLAY_YUV_PACKED:
++ /* always 4 Y values per depth pixels */
++ if (packed_width_bytes(rec->flags, rec->src_width)
++ > rec->stride_Y)
++ return -EINVAL;
++
++ tmp = rec->stride_Y*rec->src_height;
++ if (rec->offset_Y + tmp > new_bo->size)
++ return -EINVAL;
++ break;
++ case I915_OVERLAY_YUV_PLANAR:
++ if (rec->src_width > rec->stride_Y)
++ return -EINVAL;
++ if (rec->src_width/uv_hscale > rec->stride_UV)
++ return -EINVAL;
++
++ tmp = rec->stride_Y*rec->src_height;
++ if (rec->offset_Y + tmp > new_bo->size)
++ return -EINVAL;
++ tmp = rec->stride_UV*rec->src_height;
++ tmp /= uv_vscale;
++ if (rec->offset_U + tmp > new_bo->size
++ || rec->offset_V + tmp > new_bo->size)
++ return -EINVAL;
++ break;
++ }
++
++ return 0;
++}
++
++int intel_overlay_put_image(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_intel_overlay_put_image *put_image_rec = data;
++ drm_i915_private_t *dev_priv = dev->dev_private;
++ struct intel_overlay *overlay;
++ struct drm_mode_object *drmmode_obj;
++ struct intel_crtc *crtc;
++ struct drm_gem_object *new_bo;
++ struct put_image_params *params;
++ int ret;
++
++ if (!dev_priv) {
++ DRM_ERROR("called with no initialization\n");
++ return -EINVAL;
++ }
++
++ overlay = dev_priv->overlay;
++ if (!overlay) {
++ DRM_DEBUG("userspace bug: no overlay\n");
++ return -ENODEV;
++ }
++
++ if (!(put_image_rec->flags & I915_OVERLAY_ENABLE)) {
++ mutex_lock(&dev->mode_config.mutex);
++ mutex_lock(&dev->struct_mutex);
++
++ ret = intel_overlay_switch_off(overlay);
++
++ mutex_unlock(&dev->struct_mutex);
++ mutex_unlock(&dev->mode_config.mutex);
++
++ return ret;
++ }
++
++ params = kmalloc(sizeof(struct put_image_params), GFP_KERNEL);
++ if (!params)
++ return -ENOMEM;
++
++ drmmode_obj = drm_mode_object_find(dev, put_image_rec->crtc_id,
++ DRM_MODE_OBJECT_CRTC);
++ if (!drmmode_obj)
++ return -ENOENT;
++ crtc = to_intel_crtc(obj_to_crtc(drmmode_obj));
++
++ new_bo = drm_gem_object_lookup(dev, file_priv,
++ put_image_rec->bo_handle);
++ if (!new_bo)
++ return -ENOENT;
++
++ mutex_lock(&dev->mode_config.mutex);
++ mutex_lock(&dev->struct_mutex);
++
++ if (overlay->hw_wedged) {
++ ret = intel_overlay_recover_from_interrupt(overlay, 1);
++ if (ret != 0)
++ goto out_unlock;
++ }
++
++ if (overlay->crtc != crtc) {
++ struct drm_display_mode *mode = &crtc->base.mode;
++ ret = intel_overlay_switch_off(overlay);
++ if (ret != 0)
++ goto out_unlock;
++
++ ret = check_overlay_possible_on_crtc(overlay, crtc);
++ if (ret != 0)
++ goto out_unlock;
++
++ overlay->crtc = crtc;
++ crtc->overlay = overlay;
++
++ if (intel_panel_fitter_pipe(dev) == crtc->pipe
++ /* and line to wide, i.e. one-line-mode */
++ && mode->hdisplay > 1024) {
++ overlay->pfit_active = 1;
++ update_pfit_vscale_ratio(overlay);
++ } else
++ overlay->pfit_active = 0;
++ }
++
++ ret = check_overlay_dst(overlay, put_image_rec);
++ if (ret != 0)
++ goto out_unlock;
++
++ if (overlay->pfit_active) {
++ params->dst_y = ((((u32)put_image_rec->dst_y) << 12) /
++ overlay->pfit_vscale_ratio);
++ /* shifting right rounds downwards, so add 1 */
++ params->dst_h = ((((u32)put_image_rec->dst_height) << 12) /
++ overlay->pfit_vscale_ratio) + 1;
++ } else {
++ params->dst_y = put_image_rec->dst_y;
++ params->dst_h = put_image_rec->dst_height;
++ }
++ params->dst_x = put_image_rec->dst_x;
++ params->dst_w = put_image_rec->dst_width;
++
++ params->src_w = put_image_rec->src_width;
++ params->src_h = put_image_rec->src_height;
++ params->src_scan_w = put_image_rec->src_scan_width;
++ params->src_scan_h = put_image_rec->src_scan_height;
++ if (params->src_scan_h > params->src_h
++ || params->src_scan_w > params->src_w) {
++ ret = -EINVAL;
++ goto out_unlock;
++ }
++
++ ret = check_overlay_src(dev, put_image_rec, new_bo);
++ if (ret != 0)
++ goto out_unlock;
++ params->format = put_image_rec->flags & ~I915_OVERLAY_FLAGS_MASK;
++ params->stride_Y = put_image_rec->stride_Y;
++ params->stride_UV = put_image_rec->stride_UV;
++ params->offset_Y = put_image_rec->offset_Y;
++ params->offset_U = put_image_rec->offset_U;
++ params->offset_V = put_image_rec->offset_V;
++
++ /* Check scaling after src size to prevent a divide-by-zero. */
++ ret = check_overlay_scaling(params);
++ if (ret != 0)
++ goto out_unlock;
++
++ ret = intel_overlay_do_put_image(overlay, new_bo, params);
++ if (ret != 0)
++ goto out_unlock;
++
++ mutex_unlock(&dev->struct_mutex);
++ mutex_unlock(&dev->mode_config.mutex);
++
++ kfree(params);
++
++ return 0;
++
++out_unlock:
++ mutex_unlock(&dev->struct_mutex);
++ mutex_unlock(&dev->mode_config.mutex);
++ drm_gem_object_unreference(new_bo);
++ kfree(params);
++
++ return ret;
++}
++
++static void update_reg_attrs(struct intel_overlay *overlay,
++ struct overlay_registers *regs)
++{
++ regs->OCLRC0 = (overlay->contrast << 18) | (overlay->brightness & 0xff);
++ regs->OCLRC1 = overlay->saturation;
++}
++
++static bool check_gamma_bounds(u32 gamma1, u32 gamma2)
++{
++ int i;
++
++ if (gamma1 & 0xff000000 || gamma2 & 0xff000000)
++ return false;
++
++ for (i = 0; i < 3; i++) {
++ if (((gamma1 >> i * 8) & 0xff) >= ((gamma2 >> i*8) & 0xff))
++ return false;
++ }
++
++ return true;
++}
++
++static bool check_gamma5_errata(u32 gamma5)
++{
++ int i;
++
++ for (i = 0; i < 3; i++) {
++ if (((gamma5 >> i*8) & 0xff) == 0x80)
++ return false;
++ }
++
++ return true;
++}
++
++static int check_gamma(struct drm_intel_overlay_attrs *attrs)
++{
++ if (!check_gamma_bounds(0, attrs->gamma0)
++ || !check_gamma_bounds(attrs->gamma0, attrs->gamma1)
++ || !check_gamma_bounds(attrs->gamma1, attrs->gamma2)
++ || !check_gamma_bounds(attrs->gamma2, attrs->gamma3)
++ || !check_gamma_bounds(attrs->gamma3, attrs->gamma4)
++ || !check_gamma_bounds(attrs->gamma4, attrs->gamma5)
++ || !check_gamma_bounds(attrs->gamma5, 0x00ffffff))
++ return -EINVAL;
++ if (!check_gamma5_errata(attrs->gamma5))
++ return -EINVAL;
++ return 0;
++}
++
++int intel_overlay_attrs(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_intel_overlay_attrs *attrs = data;
++ drm_i915_private_t *dev_priv = dev->dev_private;
++ struct intel_overlay *overlay;
++ struct overlay_registers *regs;
++ int ret;
++
++ if (!dev_priv) {
++ DRM_ERROR("called with no initialization\n");
++ return -EINVAL;
++ }
++
++ overlay = dev_priv->overlay;
++ if (!overlay) {
++ DRM_DEBUG("userspace bug: no overlay\n");
++ return -ENODEV;
++ }
++
++ mutex_lock(&dev->mode_config.mutex);
++ mutex_lock(&dev->struct_mutex);
++
++ if (!(attrs->flags & I915_OVERLAY_UPDATE_ATTRS)) {
++ attrs->color_key = overlay->color_key;
++ attrs->brightness = overlay->brightness;
++ attrs->contrast = overlay->contrast;
++ attrs->saturation = overlay->saturation;
++
++ if (IS_I9XX(dev)) {
++ attrs->gamma0 = I915_READ(OGAMC0);
++ attrs->gamma1 = I915_READ(OGAMC1);
++ attrs->gamma2 = I915_READ(OGAMC2);
++ attrs->gamma3 = I915_READ(OGAMC3);
++ attrs->gamma4 = I915_READ(OGAMC4);
++ attrs->gamma5 = I915_READ(OGAMC5);
++ }
++ ret = 0;
++ } else {
++ overlay->color_key = attrs->color_key;
++ if (attrs->brightness >= -128 && attrs->brightness <= 127) {
++ overlay->brightness = attrs->brightness;
++ } else {
++ ret = -EINVAL;
++ goto out_unlock;
++ }
++ if (attrs->contrast <= 255) {
++ overlay->contrast = attrs->contrast;
++ } else {
++ ret = -EINVAL;
++ goto out_unlock;
++ }
++ if (attrs->saturation <= 1023) {
++ overlay->saturation = attrs->saturation;
++ } else {
++ ret = -EINVAL;
++ goto out_unlock;
++ }
++
++ regs = intel_overlay_map_regs_atomic(overlay);
++ if (!regs) {
++ ret = -ENOMEM;
++ goto out_unlock;
++ }
++
++ update_reg_attrs(overlay, regs);
++
++ intel_overlay_unmap_regs_atomic(overlay);
++
++ if (attrs->flags & I915_OVERLAY_UPDATE_GAMMA) {
++ if (!IS_I9XX(dev)) {
++ ret = -EINVAL;
++ goto out_unlock;
++ }
++
++ if (overlay->active) {
++ ret = -EBUSY;
++ goto out_unlock;
++ }
++
++ ret = check_gamma(attrs);
++ if (ret != 0)
++ goto out_unlock;
++
++ I915_WRITE(OGAMC0, attrs->gamma0);
++ I915_WRITE(OGAMC1, attrs->gamma1);
++ I915_WRITE(OGAMC2, attrs->gamma2);
++ I915_WRITE(OGAMC3, attrs->gamma3);
++ I915_WRITE(OGAMC4, attrs->gamma4);
++ I915_WRITE(OGAMC5, attrs->gamma5);
++ }
++ ret = 0;
++ }
++
++out_unlock:
++ mutex_unlock(&dev->struct_mutex);
++ mutex_unlock(&dev->mode_config.mutex);
++
++ return ret;
++}
++
++void intel_setup_overlay(struct drm_device *dev)
++{
++ drm_i915_private_t *dev_priv = dev->dev_private;
++ struct intel_overlay *overlay;
++ struct drm_gem_object *reg_bo;
++ struct overlay_registers *regs;
++ int ret;
++
++ if (!OVERLAY_EXISTS(dev))
++ return;
++
++ overlay = kzalloc(sizeof(struct intel_overlay), GFP_KERNEL);
++ if (!overlay)
++ return;
++ overlay->dev = dev;
++
++ reg_bo = drm_gem_object_alloc(dev, PAGE_SIZE);
++ if (!reg_bo)
++ goto out_free;
++ overlay->reg_bo = reg_bo->driver_private;
++
++ if (OVERLAY_NONPHYSICAL(dev)) {
++ ret = i915_gem_object_pin(reg_bo, PAGE_SIZE);
++ if (ret) {
++ DRM_ERROR("failed to pin overlay register bo\n");
++ goto out_free_bo;
++ }
++ overlay->flip_addr = overlay->reg_bo->gtt_offset;
++ } else {
++ ret = i915_gem_attach_phys_object(dev, reg_bo,
++ I915_GEM_PHYS_OVERLAY_REGS);
++ if (ret) {
++ DRM_ERROR("failed to attach phys overlay regs\n");
++ goto out_free_bo;
++ }
++ overlay->flip_addr = overlay->reg_bo->phys_obj->handle->busaddr;
++ }
++
++ /* init all values */
++ overlay->color_key = 0x0101fe;
++ overlay->brightness = -19;
++ overlay->contrast = 75;
++ overlay->saturation = 146;
++
++ regs = intel_overlay_map_regs_atomic(overlay);
++ if (!regs)
++ goto out_free_bo;
++
++ memset(regs, 0, sizeof(struct overlay_registers));
++ update_polyphase_filter(regs);
++
++ update_reg_attrs(overlay, regs);
++
++ intel_overlay_unmap_regs_atomic(overlay);
++
++ dev_priv->overlay = overlay;
++ DRM_INFO("initialized overlay support\n");
++ return;
++
++out_free_bo:
++ drm_gem_object_unreference(reg_bo);
++out_free:
++ kfree(overlay);
++ return;
++}
++
++void intel_cleanup_overlay(struct drm_device *dev)
++{
++ drm_i915_private_t *dev_priv = dev->dev_private;
++
++ if (dev_priv->overlay) {
++ /* The bo's should be free'd by the generic code already.
++ * Furthermore modesetting teardown happens beforehand so the
++ * hardware should be off already */
++ BUG_ON(dev_priv->overlay->active);
++
++ kfree(dev_priv->overlay);
++ }
++}
+diff --git a/drivers/gpu/drm/i915/intel_sdvo.c b/drivers/gpu/drm/i915/intel_sdvo.c
+index 3f5aaf1..82678d3 100644
+--- a/drivers/gpu/drm/i915/intel_sdvo.c
++++ b/drivers/gpu/drm/i915/intel_sdvo.c
+@@ -36,8 +36,6 @@
+ #include "intel_sdvo_regs.h"
+ #include <linux/dmi.h>
+
+-#undef SDVO_DEBUG
+-
+ static char *tv_format_names[] = {
+ "NTSC_M" , "NTSC_J" , "NTSC_443",
+ "PAL_B" , "PAL_D" , "PAL_G" ,
+@@ -356,7 +354,6 @@ static const struct _sdvo_cmd_name {
+ #define SDVO_NAME(dev_priv) ((dev_priv)->output_device == SDVOB ? "SDVOB" : "SDVOC")
+ #define SDVO_PRIV(output) ((struct intel_sdvo_priv *) (output)->dev_priv)
+
+-#ifdef SDVO_DEBUG
+ static void intel_sdvo_debug_write(struct intel_output *intel_output, u8 cmd,
+ void *args, int args_len)
+ {
+@@ -379,9 +376,6 @@ static void intel_sdvo_debug_write(struct intel_output *intel_output, u8 cmd,
+ DRM_LOG_KMS("(%02X)", cmd);
+ DRM_LOG_KMS("\n");
+ }
+-#else
+-#define intel_sdvo_debug_write(o, c, a, l)
+-#endif
+
+ static void intel_sdvo_write_cmd(struct intel_output *intel_output, u8 cmd,
+ void *args, int args_len)
+@@ -398,7 +392,6 @@ static void intel_sdvo_write_cmd(struct intel_output *intel_output, u8 cmd,
+ intel_sdvo_write_byte(intel_output, SDVO_I2C_OPCODE, cmd);
+ }
+
+-#ifdef SDVO_DEBUG
+ static const char *cmd_status_names[] = {
+ "Power on",
+ "Success",
+@@ -427,9 +420,6 @@ static void intel_sdvo_debug_response(struct intel_output *intel_output,
+ DRM_LOG_KMS("(??? %d)", status);
+ DRM_LOG_KMS("\n");
+ }
+-#else
+-#define intel_sdvo_debug_response(o, r, l, s)
+-#endif
+
+ static u8 intel_sdvo_read_response(struct intel_output *intel_output,
+ void *response, int response_len)
+@@ -1702,6 +1692,10 @@ static enum drm_connector_status intel_sdvo_detect(struct drm_connector *connect
+
+ intel_sdvo_write_cmd(intel_output,
+ SDVO_CMD_GET_ATTACHED_DISPLAYS, NULL, 0);
++ if (sdvo_priv->is_tv) {
++ /* add 30ms delay when the output type is SDVO-TV */
++ mdelay(30);
++ }
+ status = intel_sdvo_read_response(intel_output, &response, 2);
+
+ DRM_DEBUG_KMS("SDVO response %d %d\n", response & 0xff, response >> 8);
+@@ -2351,6 +2345,14 @@ intel_sdvo_output_setup(struct intel_output *intel_output, uint16_t flags)
+ connector->connector_type = DRM_MODE_CONNECTOR_VGA;
+ intel_output->clone_mask = (1 << INTEL_SDVO_NON_TV_CLONE_BIT) |
+ (1 << INTEL_ANALOG_CLONE_BIT);
++ } else if (flags & SDVO_OUTPUT_CVBS0) {
++
++ sdvo_priv->controlled_output = SDVO_OUTPUT_CVBS0;
++ encoder->encoder_type = DRM_MODE_ENCODER_TVDAC;
++ connector->connector_type = DRM_MODE_CONNECTOR_SVIDEO;
++ sdvo_priv->is_tv = true;
++ intel_output->needs_tv_clock = true;
++ intel_output->clone_mask = 1 << INTEL_SDVO_TV_CLONE_BIT;
+ } else if (flags & SDVO_OUTPUT_LVDS0) {
+
+ sdvo_priv->controlled_output = SDVO_OUTPUT_LVDS0;
+@@ -2804,7 +2806,7 @@ bool intel_sdvo_init(struct drm_device *dev, int output_device)
+ /* Wrap with our custom algo which switches to DDC mode */
+ intel_output->ddc_bus->algo = &intel_sdvo_i2c_bit_algo;
+
+- /* In defaut case sdvo lvds is false */
++ /* In default case sdvo lvds is false */
+ intel_sdvo_get_capabilities(intel_output, &sdvo_priv->caps);
+
+ if (intel_sdvo_output_setup(intel_output,
+diff --git a/drivers/gpu/drm/i915/intel_tv.c b/drivers/gpu/drm/i915/intel_tv.c
+index ce026f0..552ec11 100644
+--- a/drivers/gpu/drm/i915/intel_tv.c
++++ b/drivers/gpu/drm/i915/intel_tv.c
+@@ -1413,16 +1413,16 @@ intel_tv_detect_type (struct drm_crtc *crtc, struct intel_output *intel_output)
+ * 0 0 0 Component
+ */
+ if ((tv_dac & TVDAC_SENSE_MASK) == (TVDAC_B_SENSE | TVDAC_C_SENSE)) {
+- DRM_DEBUG("Detected Composite TV connection\n");
++ DRM_DEBUG_KMS("Detected Composite TV connection\n");
+ type = DRM_MODE_CONNECTOR_Composite;
+ } else if ((tv_dac & (TVDAC_A_SENSE|TVDAC_B_SENSE)) == TVDAC_A_SENSE) {
+- DRM_DEBUG("Detected S-Video TV connection\n");
++ DRM_DEBUG_KMS("Detected S-Video TV connection\n");
+ type = DRM_MODE_CONNECTOR_SVIDEO;
+ } else if ((tv_dac & TVDAC_SENSE_MASK) == 0) {
+- DRM_DEBUG("Detected Component TV connection\n");
++ DRM_DEBUG_KMS("Detected Component TV connection\n");
+ type = DRM_MODE_CONNECTOR_Component;
+ } else {
+- DRM_DEBUG("No TV connection detected\n");
++ DRM_DEBUG_KMS("No TV connection detected\n");
+ type = -1;
+ }
+
+@@ -1699,6 +1699,41 @@ static const struct drm_encoder_funcs intel_tv_enc_funcs = {
+ .destroy = intel_tv_enc_destroy,
+ };
+
++/*
++ * Enumerate the child dev array parsed from VBT to check whether
++ * the integrated TV is present.
++ * If it is present, return 1.
++ * If it is not present, return false.
++ * If no child dev is parsed from VBT, it assumes that the TV is present.
++ */
++static int tv_is_present_in_vbt(struct drm_device *dev)
++{
++ struct drm_i915_private *dev_priv = dev->dev_private;
++ struct child_device_config *p_child;
++ int i, ret;
++
++ if (!dev_priv->child_dev_num)
++ return 1;
++
++ ret = 0;
++ for (i = 0; i < dev_priv->child_dev_num; i++) {
++ p_child = dev_priv->child_dev + i;
++ /*
++ * If the device type is not TV, continue.
++ */
++ if (p_child->device_type != DEVICE_TYPE_INT_TV &&
++ p_child->device_type != DEVICE_TYPE_TV)
++ continue;
++ /* Only when the addin_offset is non-zero, it is regarded
++ * as present.
++ */
++ if (p_child->addin_offset) {
++ ret = 1;
++ break;
++ }
++ }
++ return ret;
++}
+
+ void
+ intel_tv_init(struct drm_device *dev)
+@@ -1714,6 +1749,10 @@ intel_tv_init(struct drm_device *dev)
+ if ((I915_READ(TV_CTL) & TV_FUSE_STATE_MASK) == TV_FUSE_STATE_DISABLED)
+ return;
+
++ if (!tv_is_present_in_vbt(dev)) {
++ DRM_DEBUG_KMS("Integrated TV is not present.\n");
++ return;
++ }
+ /* Even if we have an encoder we may not have a connector */
+ if (!dev_priv->int_tv_support)
+ return;
+@@ -1801,8 +1840,6 @@ intel_tv_init(struct drm_device *dev)
+ drm_connector_attach_property(connector,
+ dev->mode_config.tv_bottom_margin_property,
+ tv_priv->margin[TV_MARGIN_BOTTOM]);
+-
+- dev_priv->hotplug_supported_mask |= TV_HOTPLUG_INT_STATUS;
+ out:
+ drm_sysfs_connector_add(connector);
+ }
+diff --git a/drivers/gpu/drm/mga/mga_drv.c b/drivers/gpu/drm/mga/mga_drv.c
+index 97ee566..ddfe161 100644
+--- a/drivers/gpu/drm/mga/mga_drv.c
++++ b/drivers/gpu/drm/mga/mga_drv.c
+@@ -68,7 +68,7 @@ static struct drm_driver driver = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+- .ioctl = drm_ioctl,
++ .unlocked_ioctl = drm_ioctl,
+ .mmap = drm_mmap,
+ .poll = drm_poll,
+ .fasync = drm_fasync,
+diff --git a/drivers/gpu/drm/mga/mga_ioc32.c b/drivers/gpu/drm/mga/mga_ioc32.c
+index 30d0047..c1f877b 100644
+--- a/drivers/gpu/drm/mga/mga_ioc32.c
++++ b/drivers/gpu/drm/mga/mga_ioc32.c
+@@ -100,8 +100,7 @@ static int compat_mga_init(struct file *file, unsigned int cmd,
+ if (err)
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_MGA_INIT, (unsigned long)init);
++ return drm_ioctl(file, DRM_IOCTL_MGA_INIT, (unsigned long)init);
+ }
+
+ typedef struct drm_mga_getparam32 {
+@@ -125,8 +124,7 @@ static int compat_mga_getparam(struct file *file, unsigned int cmd,
+ &getparam->value))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_MGA_GETPARAM, (unsigned long)getparam);
++ return drm_ioctl(file, DRM_IOCTL_MGA_GETPARAM, (unsigned long)getparam);
+ }
+
+ typedef struct drm_mga_drm_bootstrap32 {
+@@ -166,8 +164,7 @@ static int compat_mga_dma_bootstrap(struct file *file, unsigned int cmd,
+ || __put_user(dma_bootstrap32.agp_size, &dma_bootstrap->agp_size))
+ return -EFAULT;
+
+- err = drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_MGA_DMA_BOOTSTRAP,
++ err = drm_ioctl(file, DRM_IOCTL_MGA_DMA_BOOTSTRAP,
+ (unsigned long)dma_bootstrap);
+ if (err)
+ return err;
+@@ -220,12 +217,10 @@ long mga_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+ if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(mga_compat_ioctls))
+ fn = mga_compat_ioctls[nr - DRM_COMMAND_BASE];
+
+- lock_kernel(); /* XXX for now */
+ if (fn != NULL)
+ ret = (*fn) (filp, cmd, arg);
+ else
+- ret = drm_ioctl(filp->f_path.dentry->d_inode, filp, cmd, arg);
+- unlock_kernel();
++ ret = drm_ioctl(filp, cmd, arg);
+
+ return ret;
+ }
+diff --git a/drivers/gpu/drm/nouveau/Kconfig b/drivers/gpu/drm/nouveau/Kconfig
+new file mode 100644
+index 0000000..1175429
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/Kconfig
+@@ -0,0 +1,44 @@
++config DRM_NOUVEAU
++ tristate "Nouveau (nVidia) cards"
++ depends on DRM
++ select FW_LOADER
++ select DRM_KMS_HELPER
++ select DRM_TTM
++ select FB_CFB_FILLRECT
++ select FB_CFB_COPYAREA
++ select FB_CFB_IMAGEBLIT
++ select FB
++ select FRAMEBUFFER_CONSOLE if !EMBEDDED
++ select FB_BACKLIGHT if DRM_NOUVEAU_BACKLIGHT
++ help
++ Choose this option for open-source nVidia support.
++
++config DRM_NOUVEAU_BACKLIGHT
++ bool "Support for backlight control"
++ depends on DRM_NOUVEAU
++ default y
++ help
++ Say Y here if you want to control the backlight of your display
++ (e.g. a laptop panel).
++
++config DRM_NOUVEAU_DEBUG
++ bool "Build in Nouveau's debugfs support"
++ depends on DRM_NOUVEAU && DEBUG_FS
++ default y
++ help
++ Say Y here if you want Nouveau to output debugging information
++ via debugfs.
++
++menu "I2C encoder or helper chips"
++ depends on DRM && DRM_KMS_HELPER && I2C
++
++config DRM_I2C_CH7006
++ tristate "Chrontel ch7006 TV encoder"
++ default m if DRM_NOUVEAU
++ help
++ Support for Chrontel ch7006 and similar TV encoders, found
++ on some nVidia video cards.
++
++ This driver is currently only useful if you're also using
++ the nouveau driver.
++endmenu
+diff --git a/drivers/gpu/drm/nouveau/Makefile b/drivers/gpu/drm/nouveau/Makefile
+new file mode 100644
+index 0000000..48c290b
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/Makefile
+@@ -0,0 +1,32 @@
++#
++# Makefile for the drm device driver. This driver provides support for the
++# Direct Rendering Infrastructure (DRI) in XFree86 4.1.0 and higher.
++
++ccflags-y := -Iinclude/drm
++nouveau-y := nouveau_drv.o nouveau_state.o nouveau_channel.o nouveau_mem.o \
++ nouveau_object.o nouveau_irq.o nouveau_notifier.o \
++ nouveau_sgdma.o nouveau_dma.o \
++ nouveau_bo.o nouveau_fence.o nouveau_gem.o nouveau_ttm.o \
++ nouveau_hw.o nouveau_calc.o nouveau_bios.o nouveau_i2c.o \
++ nouveau_display.o nouveau_connector.o nouveau_fbcon.o \
++ nouveau_dp.o nouveau_grctx.o \
++ nv04_timer.o \
++ nv04_mc.o nv40_mc.o nv50_mc.o \
++ nv04_fb.o nv10_fb.o nv40_fb.o \
++ nv04_fifo.o nv10_fifo.o nv40_fifo.o nv50_fifo.o \
++ nv04_graph.o nv10_graph.o nv20_graph.o \
++ nv40_graph.o nv50_graph.o \
++ nv40_grctx.o \
++ nv04_instmem.o nv50_instmem.o \
++ nv50_crtc.o nv50_dac.o nv50_sor.o \
++ nv50_cursor.o nv50_display.o nv50_fbcon.o \
++ nv04_dac.o nv04_dfp.o nv04_tv.o nv17_tv.o nv17_tv_modes.o \
++ nv04_crtc.o nv04_display.o nv04_cursor.o nv04_fbcon.o \
++ nv17_gpio.o
++
++nouveau-$(CONFIG_DRM_NOUVEAU_DEBUG) += nouveau_debugfs.o
++nouveau-$(CONFIG_COMPAT) += nouveau_ioc32.o
++nouveau-$(CONFIG_DRM_NOUVEAU_BACKLIGHT) += nouveau_backlight.o
++nouveau-$(CONFIG_ACPI) += nouveau_acpi.o
++
++obj-$(CONFIG_DRM_NOUVEAU)+= nouveau.o
+diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
+new file mode 100644
+index 0000000..48227e7
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
+@@ -0,0 +1,125 @@
++#include <linux/pci.h>
++#include <linux/acpi.h>
++#include <acpi/acpi_drivers.h>
++#include <acpi/acpi_bus.h>
++
++#include "drmP.h"
++#include "drm.h"
++#include "drm_sarea.h"
++#include "drm_crtc_helper.h"
++#include "nouveau_drv.h"
++#include "nouveau_drm.h"
++#include "nv50_display.h"
++
++#define NOUVEAU_DSM_SUPPORTED 0x00
++#define NOUVEAU_DSM_SUPPORTED_FUNCTIONS 0x00
++
++#define NOUVEAU_DSM_ACTIVE 0x01
++#define NOUVEAU_DSM_ACTIVE_QUERY 0x00
++
++#define NOUVEAU_DSM_LED 0x02
++#define NOUVEAU_DSM_LED_STATE 0x00
++#define NOUVEAU_DSM_LED_OFF 0x10
++#define NOUVEAU_DSM_LED_STAMINA 0x11
++#define NOUVEAU_DSM_LED_SPEED 0x12
++
++#define NOUVEAU_DSM_POWER 0x03
++#define NOUVEAU_DSM_POWER_STATE 0x00
++#define NOUVEAU_DSM_POWER_SPEED 0x01
++#define NOUVEAU_DSM_POWER_STAMINA 0x02
++
++static int nouveau_dsm(struct drm_device *dev, int func, int arg, int *result)
++{
++ static char muid[] = {
++ 0xA0, 0xA0, 0x95, 0x9D, 0x60, 0x00, 0x48, 0x4D,
++ 0xB3, 0x4D, 0x7E, 0x5F, 0xEA, 0x12, 0x9F, 0xD4,
++ };
++
++ struct pci_dev *pdev = dev->pdev;
++ struct acpi_handle *handle;
++ struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
++ struct acpi_object_list input;
++ union acpi_object params[4];
++ union acpi_object *obj;
++ int err;
++
++ handle = DEVICE_ACPI_HANDLE(&pdev->dev);
++
++ if (!handle)
++ return -ENODEV;
++
++ input.count = 4;
++ input.pointer = params;
++ params[0].type = ACPI_TYPE_BUFFER;
++ params[0].buffer.length = sizeof(muid);
++ params[0].buffer.pointer = (char *)muid;
++ params[1].type = ACPI_TYPE_INTEGER;
++ params[1].integer.value = 0x00000102;
++ params[2].type = ACPI_TYPE_INTEGER;
++ params[2].integer.value = func;
++ params[3].type = ACPI_TYPE_INTEGER;
++ params[3].integer.value = arg;
++
++ err = acpi_evaluate_object(handle, "_DSM", &input, &output);
++ if (err) {
++ NV_INFO(dev, "failed to evaluate _DSM: %d\n", err);
++ return err;
++ }
++
++ obj = (union acpi_object *)output.pointer;
++
++ if (obj->type == ACPI_TYPE_INTEGER)
++ if (obj->integer.value == 0x80000002)
++ return -ENODEV;
++
++ if (obj->type == ACPI_TYPE_BUFFER) {
++ if (obj->buffer.length == 4 && result) {
++ *result = 0;
++ *result |= obj->buffer.pointer[0];
++ *result |= (obj->buffer.pointer[1] << 8);
++ *result |= (obj->buffer.pointer[2] << 16);
++ *result |= (obj->buffer.pointer[3] << 24);
++ }
++ }
++
++ kfree(output.pointer);
++ return 0;
++}
++
++int nouveau_hybrid_setup(struct drm_device *dev)
++{
++ int result;
++
++ if (nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STATE,
++ &result))
++ return -ENODEV;
++
++ NV_INFO(dev, "_DSM hardware status gave 0x%x\n", result);
++
++ if (result) { /* Ensure that the external GPU is enabled */
++ nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_SPEED, NULL);
++ nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_SPEED,
++ NULL);
++ } else { /* Stamina mode - disable the external GPU */
++ nouveau_dsm(dev, NOUVEAU_DSM_LED, NOUVEAU_DSM_LED_STAMINA,
++ NULL);
++ nouveau_dsm(dev, NOUVEAU_DSM_POWER, NOUVEAU_DSM_POWER_STAMINA,
++ NULL);
++ }
++
++ return 0;
++}
++
++bool nouveau_dsm_probe(struct drm_device *dev)
++{
++ int support = 0;
++
++ if (nouveau_dsm(dev, NOUVEAU_DSM_SUPPORTED,
++ NOUVEAU_DSM_SUPPORTED_FUNCTIONS, &support))
++ return false;
++
++ if (!support)
++ return false;
++
++ return true;
++}
+diff --git a/drivers/gpu/drm/nouveau/nouveau_backlight.c b/drivers/gpu/drm/nouveau/nouveau_backlight.c
+new file mode 100644
+index 0000000..20564f8
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_backlight.c
+@@ -0,0 +1,155 @@
++/*
++ * Copyright (C) 2009 Red Hat <mjg@redhat.com>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++/*
++ * Authors:
++ * Matthew Garrett <mjg@redhat.com>
++ *
++ * Register locations derived from NVClock by Roderick Colenbrander
++ */
++
++#include <linux/backlight.h>
++
++#include "drmP.h"
++#include "nouveau_drv.h"
++#include "nouveau_drm.h"
++#include "nouveau_reg.h"
++
++static int nv40_get_intensity(struct backlight_device *bd)
++{
++ struct drm_device *dev = bl_get_data(bd);
++ int val = (nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK)
++ >> 16;
++
++ return val;
++}
++
++static int nv40_set_intensity(struct backlight_device *bd)
++{
++ struct drm_device *dev = bl_get_data(bd);
++ int val = bd->props.brightness;
++ int reg = nv_rd32(dev, NV40_PMC_BACKLIGHT);
++
++ nv_wr32(dev, NV40_PMC_BACKLIGHT,
++ (val << 16) | (reg & ~NV40_PMC_BACKLIGHT_MASK));
++
++ return 0;
++}
++
++static struct backlight_ops nv40_bl_ops = {
++ .options = BL_CORE_SUSPENDRESUME,
++ .get_brightness = nv40_get_intensity,
++ .update_status = nv40_set_intensity,
++};
++
++static int nv50_get_intensity(struct backlight_device *bd)
++{
++ struct drm_device *dev = bl_get_data(bd);
++
++ return nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT);
++}
++
++static int nv50_set_intensity(struct backlight_device *bd)
++{
++ struct drm_device *dev = bl_get_data(bd);
++ int val = bd->props.brightness;
++
++ nv_wr32(dev, NV50_PDISPLAY_SOR_BACKLIGHT,
++ val | NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE);
++ return 0;
++}
++
++static struct backlight_ops nv50_bl_ops = {
++ .options = BL_CORE_SUSPENDRESUME,
++ .get_brightness = nv50_get_intensity,
++ .update_status = nv50_set_intensity,
++};
++
++static int nouveau_nv40_backlight_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct backlight_device *bd;
++
++ if (!(nv_rd32(dev, NV40_PMC_BACKLIGHT) & NV40_PMC_BACKLIGHT_MASK))
++ return 0;
++
++ bd = backlight_device_register("nv_backlight", &dev->pdev->dev, dev,
++ &nv40_bl_ops);
++ if (IS_ERR(bd))
++ return PTR_ERR(bd);
++
++ dev_priv->backlight = bd;
++ bd->props.max_brightness = 31;
++ bd->props.brightness = nv40_get_intensity(bd);
++ backlight_update_status(bd);
++
++ return 0;
++}
++
++static int nouveau_nv50_backlight_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct backlight_device *bd;
++
++ if (!nv_rd32(dev, NV50_PDISPLAY_SOR_BACKLIGHT))
++ return 0;
++
++ bd = backlight_device_register("nv_backlight", &dev->pdev->dev, dev,
++ &nv50_bl_ops);
++ if (IS_ERR(bd))
++ return PTR_ERR(bd);
++
++ dev_priv->backlight = bd;
++ bd->props.max_brightness = 1025;
++ bd->props.brightness = nv50_get_intensity(bd);
++ backlight_update_status(bd);
++ return 0;
++}
++
++int nouveau_backlight_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ switch (dev_priv->card_type) {
++ case NV_40:
++ return nouveau_nv40_backlight_init(dev);
++ case NV_50:
++ return nouveau_nv50_backlight_init(dev);
++ default:
++ break;
++ }
++
++ return 0;
++}
++
++void nouveau_backlight_exit(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ if (dev_priv->backlight) {
++ backlight_device_unregister(dev_priv->backlight);
++ dev_priv->backlight = NULL;
++ }
++}
+diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.c b/drivers/gpu/drm/nouveau/nouveau_bios.c
+new file mode 100644
+index 0000000..2cd0fad
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_bios.c
+@@ -0,0 +1,6052 @@
++/*
++ * Copyright 2005-2006 Erik Waling
++ * Copyright 2006 Stephane Marchesin
++ * Copyright 2007-2009 Stuart Bennett
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
++ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ */
++
++#include "drmP.h"
++#define NV_DEBUG_NOTRACE
++#include "nouveau_drv.h"
++#include "nouveau_hw.h"
++
++/* these defines are made up */
++#define NV_CIO_CRE_44_HEADA 0x0
++#define NV_CIO_CRE_44_HEADB 0x3
++#define FEATURE_MOBILE 0x10 /* also FEATURE_QUADRO for BMP */
++#define LEGACY_I2C_CRT 0x80
++#define LEGACY_I2C_PANEL 0x81
++#define LEGACY_I2C_TV 0x82
++
++#define EDID1_LEN 128
++
++#define BIOSLOG(sip, fmt, arg...) NV_DEBUG(sip->dev, fmt, ##arg)
++#define LOG_OLD_VALUE(x)
++
++#define ROM16(x) le16_to_cpu(*(uint16_t *)&(x))
++#define ROM32(x) le32_to_cpu(*(uint32_t *)&(x))
++
++struct init_exec {
++ bool execute;
++ bool repeat;
++};
++
++static bool nv_cksum(const uint8_t *data, unsigned int length)
++{
++ /*
++ * There's a few checksums in the BIOS, so here's a generic checking
++ * function.
++ */
++ int i;
++ uint8_t sum = 0;
++
++ for (i = 0; i < length; i++)
++ sum += data[i];
++
++ if (sum)
++ return true;
++
++ return false;
++}
++
++static int
++score_vbios(struct drm_device *dev, const uint8_t *data, const bool writeable)
++{
++ if (!(data[0] == 0x55 && data[1] == 0xAA)) {
++ NV_TRACEWARN(dev, "... BIOS signature not found\n");
++ return 0;
++ }
++
++ if (nv_cksum(data, data[2] * 512)) {
++ NV_TRACEWARN(dev, "... BIOS checksum invalid\n");
++ /* if a ro image is somewhat bad, it's probably all rubbish */
++ return writeable ? 2 : 1;
++ } else
++ NV_TRACE(dev, "... appears to be valid\n");
++
++ return 3;
++}
++
++static void load_vbios_prom(struct drm_device *dev, uint8_t *data)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t pci_nv_20, save_pci_nv_20;
++ int pcir_ptr;
++ int i;
++
++ if (dev_priv->card_type >= NV_50)
++ pci_nv_20 = 0x88050;
++ else
++ pci_nv_20 = NV_PBUS_PCI_NV_20;
++
++ /* enable ROM access */
++ save_pci_nv_20 = nvReadMC(dev, pci_nv_20);
++ nvWriteMC(dev, pci_nv_20,
++ save_pci_nv_20 & ~NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED);
++
++ /* bail if no rom signature */
++ if (nv_rd08(dev, NV_PROM_OFFSET) != 0x55 ||
++ nv_rd08(dev, NV_PROM_OFFSET + 1) != 0xaa)
++ goto out;
++
++ /* additional check (see note below) - read PCI record header */
++ pcir_ptr = nv_rd08(dev, NV_PROM_OFFSET + 0x18) |
++ nv_rd08(dev, NV_PROM_OFFSET + 0x19) << 8;
++ if (nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr) != 'P' ||
++ nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 1) != 'C' ||
++ nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 2) != 'I' ||
++ nv_rd08(dev, NV_PROM_OFFSET + pcir_ptr + 3) != 'R')
++ goto out;
++
++ /* on some 6600GT/6800LE prom reads are messed up. nvclock alleges a
++ * a good read may be obtained by waiting or re-reading (cargocult: 5x)
++ * each byte. we'll hope pramin has something usable instead
++ */
++ for (i = 0; i < NV_PROM_SIZE; i++)
++ data[i] = nv_rd08(dev, NV_PROM_OFFSET + i);
++
++out:
++ /* disable ROM access */
++ nvWriteMC(dev, pci_nv_20,
++ save_pci_nv_20 | NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED);
++}
++
++static void load_vbios_pramin(struct drm_device *dev, uint8_t *data)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t old_bar0_pramin = 0;
++ int i;
++
++ if (dev_priv->card_type >= NV_50) {
++ uint32_t vbios_vram = (nv_rd32(dev, 0x619f04) & ~0xff) << 8;
++
++ if (!vbios_vram)
++ vbios_vram = (nv_rd32(dev, 0x1700) << 16) + 0xf0000;
++
++ old_bar0_pramin = nv_rd32(dev, 0x1700);
++ nv_wr32(dev, 0x1700, vbios_vram >> 16);
++ }
++
++ /* bail if no rom signature */
++ if (nv_rd08(dev, NV_PRAMIN_OFFSET) != 0x55 ||
++ nv_rd08(dev, NV_PRAMIN_OFFSET + 1) != 0xaa)
++ goto out;
++
++ for (i = 0; i < NV_PROM_SIZE; i++)
++ data[i] = nv_rd08(dev, NV_PRAMIN_OFFSET + i);
++
++out:
++ if (dev_priv->card_type >= NV_50)
++ nv_wr32(dev, 0x1700, old_bar0_pramin);
++}
++
++static void load_vbios_pci(struct drm_device *dev, uint8_t *data)
++{
++ void __iomem *rom = NULL;
++ size_t rom_len;
++ int ret;
++
++ ret = pci_enable_rom(dev->pdev);
++ if (ret)
++ return;
++
++ rom = pci_map_rom(dev->pdev, &rom_len);
++ if (!rom)
++ goto out;
++ memcpy_fromio(data, rom, rom_len);
++ pci_unmap_rom(dev->pdev, rom);
++
++out:
++ pci_disable_rom(dev->pdev);
++}
++
++struct methods {
++ const char desc[8];
++ void (*loadbios)(struct drm_device *, uint8_t *);
++ const bool rw;
++};
++
++static struct methods nv04_methods[] = {
++ { "PROM", load_vbios_prom, false },
++ { "PRAMIN", load_vbios_pramin, true },
++ { "PCIROM", load_vbios_pci, true },
++};
++
++static struct methods nv50_methods[] = {
++ { "PRAMIN", load_vbios_pramin, true },
++ { "PROM", load_vbios_prom, false },
++ { "PCIROM", load_vbios_pci, true },
++};
++
++#define METHODCNT 3
++
++static bool NVShadowVBIOS(struct drm_device *dev, uint8_t *data)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct methods *methods;
++ int i;
++ int testscore = 3;
++ int scores[METHODCNT];
++
++ if (nouveau_vbios) {
++ methods = nv04_methods;
++ for (i = 0; i < METHODCNT; i++)
++ if (!strcasecmp(nouveau_vbios, methods[i].desc))
++ break;
++
++ if (i < METHODCNT) {
++ NV_INFO(dev, "Attempting to use BIOS image from %s\n",
++ methods[i].desc);
++
++ methods[i].loadbios(dev, data);
++ if (score_vbios(dev, data, methods[i].rw))
++ return true;
++ }
++
++ NV_ERROR(dev, "VBIOS source \'%s\' invalid\n", nouveau_vbios);
++ }
++
++ if (dev_priv->card_type < NV_50)
++ methods = nv04_methods;
++ else
++ methods = nv50_methods;
++
++ for (i = 0; i < METHODCNT; i++) {
++ NV_TRACE(dev, "Attempting to load BIOS image from %s\n",
++ methods[i].desc);
++ data[0] = data[1] = 0; /* avoid reuse of previous image */
++ methods[i].loadbios(dev, data);
++ scores[i] = score_vbios(dev, data, methods[i].rw);
++ if (scores[i] == testscore)
++ return true;
++ }
++
++ while (--testscore > 0) {
++ for (i = 0; i < METHODCNT; i++) {
++ if (scores[i] == testscore) {
++ NV_TRACE(dev, "Using BIOS image from %s\n",
++ methods[i].desc);
++ methods[i].loadbios(dev, data);
++ return true;
++ }
++ }
++ }
++
++ NV_ERROR(dev, "No valid BIOS image found\n");
++ return false;
++}
++
++struct init_tbl_entry {
++ char *name;
++ uint8_t id;
++ int (*handler)(struct nvbios *, uint16_t, struct init_exec *);
++};
++
++struct bit_entry {
++ uint8_t id[2];
++ uint16_t length;
++ uint16_t offset;
++};
++
++static int parse_init_table(struct nvbios *, unsigned int, struct init_exec *);
++
++#define MACRO_INDEX_SIZE 2
++#define MACRO_SIZE 8
++#define CONDITION_SIZE 12
++#define IO_FLAG_CONDITION_SIZE 9
++#define IO_CONDITION_SIZE 5
++#define MEM_INIT_SIZE 66
++
++static void still_alive(void)
++{
++#if 0
++ sync();
++ msleep(2);
++#endif
++}
++
++static uint32_t
++munge_reg(struct nvbios *bios, uint32_t reg)
++{
++ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
++ struct dcb_entry *dcbent = bios->display.output;
++
++ if (dev_priv->card_type < NV_50)
++ return reg;
++
++ if (reg & 0x40000000) {
++ BUG_ON(!dcbent);
++
++ reg += (ffs(dcbent->or) - 1) * 0x800;
++ if ((reg & 0x20000000) && !(dcbent->sorconf.link & 1))
++ reg += 0x00000080;
++ }
++
++ reg &= ~0x60000000;
++ return reg;
++}
++
++static int
++valid_reg(struct nvbios *bios, uint32_t reg)
++{
++ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
++ struct drm_device *dev = bios->dev;
++
++ /* C51 has misaligned regs on purpose. Marvellous */
++ if (reg & 0x2 ||
++ (reg & 0x1 && dev_priv->VBIOS.pub.chip_version != 0x51))
++ NV_ERROR(dev, "======= misaligned reg 0x%08X =======\n", reg);
++
++ /* warn on C51 regs that haven't been verified accessible in tracing */
++ if (reg & 0x1 && dev_priv->VBIOS.pub.chip_version == 0x51 &&
++ reg != 0x130d && reg != 0x1311 && reg != 0x60081d)
++ NV_WARN(dev, "=== C51 misaligned reg 0x%08X not verified ===\n",
++ reg);
++
++ if (reg >= (8*1024*1024)) {
++ NV_ERROR(dev, "=== reg 0x%08x out of mapped bounds ===\n", reg);
++ return 0;
++ }
++
++ return 1;
++}
++
++static bool
++valid_idx_port(struct nvbios *bios, uint16_t port)
++{
++ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
++ struct drm_device *dev = bios->dev;
++
++ /*
++ * If adding more ports here, the read/write functions below will need
++ * updating so that the correct mmio range (PRMCIO, PRMDIO, PRMVIO) is
++ * used for the port in question
++ */
++ if (dev_priv->card_type < NV_50) {
++ if (port == NV_CIO_CRX__COLOR)
++ return true;
++ if (port == NV_VIO_SRX)
++ return true;
++ } else {
++ if (port == NV_CIO_CRX__COLOR)
++ return true;
++ }
++
++ NV_ERROR(dev, "========== unknown indexed io port 0x%04X ==========\n",
++ port);
++
++ return false;
++}
++
++static bool
++valid_port(struct nvbios *bios, uint16_t port)
++{
++ struct drm_device *dev = bios->dev;
++
++ /*
++ * If adding more ports here, the read/write functions below will need
++ * updating so that the correct mmio range (PRMCIO, PRMDIO, PRMVIO) is
++ * used for the port in question
++ */
++ if (port == NV_VIO_VSE2)
++ return true;
++
++ NV_ERROR(dev, "========== unknown io port 0x%04X ==========\n", port);
++
++ return false;
++}
++
++static uint32_t
++bios_rd32(struct nvbios *bios, uint32_t reg)
++{
++ uint32_t data;
++
++ reg = munge_reg(bios, reg);
++ if (!valid_reg(bios, reg))
++ return 0;
++
++ /*
++ * C51 sometimes uses regs with bit0 set in the address. For these
++ * cases there should exist a translation in a BIOS table to an IO
++ * port address which the BIOS uses for accessing the reg
++ *
++ * These only seem to appear for the power control regs to a flat panel,
++ * and the GPIO regs at 0x60081*. In C51 mmio traces the normal regs
++ * for 0x1308 and 0x1310 are used - hence the mask below. An S3
++ * suspend-resume mmio trace from a C51 will be required to see if this
++ * is true for the power microcode in 0x14.., or whether the direct IO
++ * port access method is needed
++ */
++ if (reg & 0x1)
++ reg &= ~0x1;
++
++ data = nv_rd32(bios->dev, reg);
++
++ BIOSLOG(bios, " Read: Reg: 0x%08X, Data: 0x%08X\n", reg, data);
++
++ return data;
++}
++
++static void
++bios_wr32(struct nvbios *bios, uint32_t reg, uint32_t data)
++{
++ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
++
++ reg = munge_reg(bios, reg);
++ if (!valid_reg(bios, reg))
++ return;
++
++ /* see note in bios_rd32 */
++ if (reg & 0x1)
++ reg &= 0xfffffffe;
++
++ LOG_OLD_VALUE(bios_rd32(bios, reg));
++ BIOSLOG(bios, " Write: Reg: 0x%08X, Data: 0x%08X\n", reg, data);
++
++ if (dev_priv->VBIOS.execute) {
++ still_alive();
++ nv_wr32(bios->dev, reg, data);
++ }
++}
++
++static uint8_t
++bios_idxprt_rd(struct nvbios *bios, uint16_t port, uint8_t index)
++{
++ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
++ struct drm_device *dev = bios->dev;
++ uint8_t data;
++
++ if (!valid_idx_port(bios, port))
++ return 0;
++
++ if (dev_priv->card_type < NV_50) {
++ if (port == NV_VIO_SRX)
++ data = NVReadVgaSeq(dev, bios->state.crtchead, index);
++ else /* assume NV_CIO_CRX__COLOR */
++ data = NVReadVgaCrtc(dev, bios->state.crtchead, index);
++ } else {
++ uint32_t data32;
++
++ data32 = bios_rd32(bios, NV50_PDISPLAY_VGACRTC(index & ~3));
++ data = (data32 >> ((index & 3) << 3)) & 0xff;
++ }
++
++ BIOSLOG(bios, " Indexed IO read: Port: 0x%04X, Index: 0x%02X, "
++ "Head: 0x%02X, Data: 0x%02X\n",
++ port, index, bios->state.crtchead, data);
++ return data;
++}
++
++static void
++bios_idxprt_wr(struct nvbios *bios, uint16_t port, uint8_t index, uint8_t data)
++{
++ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
++ struct drm_device *dev = bios->dev;
++
++ if (!valid_idx_port(bios, port))
++ return;
++
++ /*
++ * The current head is maintained in the nvbios member state.crtchead.
++ * We trap changes to CR44 and update the head variable and hence the
++ * register set written.
++ * As CR44 only exists on CRTC0, we update crtchead to head0 in advance
++ * of the write, and to head1 after the write
++ */
++ if (port == NV_CIO_CRX__COLOR && index == NV_CIO_CRE_44 &&
++ data != NV_CIO_CRE_44_HEADB)
++ bios->state.crtchead = 0;
++
++ LOG_OLD_VALUE(bios_idxprt_rd(bios, port, index));
++ BIOSLOG(bios, " Indexed IO write: Port: 0x%04X, Index: 0x%02X, "
++ "Head: 0x%02X, Data: 0x%02X\n",
++ port, index, bios->state.crtchead, data);
++
++ if (bios->execute && dev_priv->card_type < NV_50) {
++ still_alive();
++ if (port == NV_VIO_SRX)
++ NVWriteVgaSeq(dev, bios->state.crtchead, index, data);
++ else /* assume NV_CIO_CRX__COLOR */
++ NVWriteVgaCrtc(dev, bios->state.crtchead, index, data);
++ } else
++ if (bios->execute) {
++ uint32_t data32, shift = (index & 3) << 3;
++
++ still_alive();
++
++ data32 = bios_rd32(bios, NV50_PDISPLAY_VGACRTC(index & ~3));
++ data32 &= ~(0xff << shift);
++ data32 |= (data << shift);
++ bios_wr32(bios, NV50_PDISPLAY_VGACRTC(index & ~3), data32);
++ }
++
++ if (port == NV_CIO_CRX__COLOR &&
++ index == NV_CIO_CRE_44 && data == NV_CIO_CRE_44_HEADB)
++ bios->state.crtchead = 1;
++}
++
++static uint8_t
++bios_port_rd(struct nvbios *bios, uint16_t port)
++{
++ uint8_t data, head = bios->state.crtchead;
++
++ if (!valid_port(bios, port))
++ return 0;
++
++ data = NVReadPRMVIO(bios->dev, head, NV_PRMVIO0_OFFSET + port);
++
++ BIOSLOG(bios, " IO read: Port: 0x%04X, Head: 0x%02X, Data: 0x%02X\n",
++ port, head, data);
++
++ return data;
++}
++
++static void
++bios_port_wr(struct nvbios *bios, uint16_t port, uint8_t data)
++{
++ int head = bios->state.crtchead;
++
++ if (!valid_port(bios, port))
++ return;
++
++ LOG_OLD_VALUE(bios_port_rd(bios, port));
++ BIOSLOG(bios, " IO write: Port: 0x%04X, Head: 0x%02X, Data: 0x%02X\n",
++ port, head, data);
++
++ if (!bios->execute)
++ return;
++
++ still_alive();
++ NVWritePRMVIO(bios->dev, head, NV_PRMVIO0_OFFSET + port, data);
++}
++
++static bool
++io_flag_condition_met(struct nvbios *bios, uint16_t offset, uint8_t cond)
++{
++ /*
++ * The IO flag condition entry has 2 bytes for the CRTC port; 1 byte
++ * for the CRTC index; 1 byte for the mask to apply to the value
++ * retrieved from the CRTC; 1 byte for the shift right to apply to the
++ * masked CRTC value; 2 bytes for the offset to the flag array, to
++ * which the shifted value is added; 1 byte for the mask applied to the
++ * value read from the flag array; and 1 byte for the value to compare
++ * against the masked byte from the flag table.
++ */
++
++ uint16_t condptr = bios->io_flag_condition_tbl_ptr + cond * IO_FLAG_CONDITION_SIZE;
++ uint16_t crtcport = ROM16(bios->data[condptr]);
++ uint8_t crtcindex = bios->data[condptr + 2];
++ uint8_t mask = bios->data[condptr + 3];
++ uint8_t shift = bios->data[condptr + 4];
++ uint16_t flagarray = ROM16(bios->data[condptr + 5]);
++ uint8_t flagarraymask = bios->data[condptr + 7];
++ uint8_t cmpval = bios->data[condptr + 8];
++ uint8_t data;
++
++ BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, "
++ "Shift: 0x%02X, FlagArray: 0x%04X, FAMask: 0x%02X, "
++ "Cmpval: 0x%02X\n",
++ offset, crtcport, crtcindex, mask, shift, flagarray, flagarraymask, cmpval);
++
++ data = bios_idxprt_rd(bios, crtcport, crtcindex);
++
++ data = bios->data[flagarray + ((data & mask) >> shift)];
++ data &= flagarraymask;
++
++ BIOSLOG(bios, "0x%04X: Checking if 0x%02X equals 0x%02X\n",
++ offset, data, cmpval);
++
++ return (data == cmpval);
++}
++
++static bool
++bios_condition_met(struct nvbios *bios, uint16_t offset, uint8_t cond)
++{
++ /*
++ * The condition table entry has 4 bytes for the address of the
++ * register to check, 4 bytes for a mask to apply to the register and
++ * 4 for a test comparison value
++ */
++
++ uint16_t condptr = bios->condition_tbl_ptr + cond * CONDITION_SIZE;
++ uint32_t reg = ROM32(bios->data[condptr]);
++ uint32_t mask = ROM32(bios->data[condptr + 4]);
++ uint32_t cmpval = ROM32(bios->data[condptr + 8]);
++ uint32_t data;
++
++ BIOSLOG(bios, "0x%04X: Cond: 0x%02X, Reg: 0x%08X, Mask: 0x%08X\n",
++ offset, cond, reg, mask);
++
++ data = bios_rd32(bios, reg) & mask;
++
++ BIOSLOG(bios, "0x%04X: Checking if 0x%08X equals 0x%08X\n",
++ offset, data, cmpval);
++
++ return (data == cmpval);
++}
++
++static bool
++io_condition_met(struct nvbios *bios, uint16_t offset, uint8_t cond)
++{
++ /*
++ * The IO condition entry has 2 bytes for the IO port address; 1 byte
++ * for the index to write to io_port; 1 byte for the mask to apply to
++ * the byte read from io_port+1; and 1 byte for the value to compare
++ * against the masked byte.
++ */
++
++ uint16_t condptr = bios->io_condition_tbl_ptr + cond * IO_CONDITION_SIZE;
++ uint16_t io_port = ROM16(bios->data[condptr]);
++ uint8_t port_index = bios->data[condptr + 2];
++ uint8_t mask = bios->data[condptr + 3];
++ uint8_t cmpval = bios->data[condptr + 4];
++
++ uint8_t data = bios_idxprt_rd(bios, io_port, port_index) & mask;
++
++ BIOSLOG(bios, "0x%04X: Checking if 0x%02X equals 0x%02X\n",
++ offset, data, cmpval);
++
++ return (data == cmpval);
++}
++
++static int
++nv50_pll_set(struct drm_device *dev, uint32_t reg, uint32_t clk)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t reg0 = nv_rd32(dev, reg + 0);
++ uint32_t reg1 = nv_rd32(dev, reg + 4);
++ struct nouveau_pll_vals pll;
++ struct pll_lims pll_limits;
++ int ret;
++
++ ret = get_pll_limits(dev, reg, &pll_limits);
++ if (ret)
++ return ret;
++
++ clk = nouveau_calc_pll_mnp(dev, &pll_limits, clk, &pll);
++ if (!clk)
++ return -ERANGE;
++
++ reg0 = (reg0 & 0xfff8ffff) | (pll.log2P << 16);
++ reg1 = (reg1 & 0xffff0000) | (pll.N1 << 8) | pll.M1;
++
++ if (dev_priv->VBIOS.execute) {
++ still_alive();
++ nv_wr32(dev, reg + 4, reg1);
++ nv_wr32(dev, reg + 0, reg0);
++ }
++
++ return 0;
++}
++
++static int
++setPLL(struct nvbios *bios, uint32_t reg, uint32_t clk)
++{
++ struct drm_device *dev = bios->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ /* clk in kHz */
++ struct pll_lims pll_lim;
++ struct nouveau_pll_vals pllvals;
++ int ret;
++
++ if (dev_priv->card_type >= NV_50)
++ return nv50_pll_set(dev, reg, clk);
++
++ /* high regs (such as in the mac g5 table) are not -= 4 */
++ ret = get_pll_limits(dev, reg > 0x405c ? reg : reg - 4, &pll_lim);
++ if (ret)
++ return ret;
++
++ clk = nouveau_calc_pll_mnp(dev, &pll_lim, clk, &pllvals);
++ if (!clk)
++ return -ERANGE;
++
++ if (bios->execute) {
++ still_alive();
++ nouveau_hw_setpll(dev, reg, &pllvals);
++ }
++
++ return 0;
++}
++
++static int dcb_entry_idx_from_crtchead(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++
++ /*
++ * For the results of this function to be correct, CR44 must have been
++ * set (using bios_idxprt_wr to set crtchead), CR58 set for CR57 = 0,
++ * and the DCB table parsed, before the script calling the function is
++ * run. run_digital_op_script is example of how to do such setup
++ */
++
++ uint8_t dcb_entry = NVReadVgaCrtc5758(dev, bios->state.crtchead, 0);
++
++ if (dcb_entry > bios->bdcb.dcb.entries) {
++ NV_ERROR(dev, "CR58 doesn't have a valid DCB entry currently "
++ "(%02X)\n", dcb_entry);
++ dcb_entry = 0x7f; /* unused / invalid marker */
++ }
++
++ return dcb_entry;
++}
++
++static struct nouveau_i2c_chan *
++init_i2c_device_find(struct drm_device *dev, int i2c_index)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct bios_parsed_dcb *bdcb = &dev_priv->VBIOS.bdcb;
++
++ if (i2c_index == 0xff) {
++ /* note: dcb_entry_idx_from_crtchead needs pre-script set-up */
++ int idx = dcb_entry_idx_from_crtchead(dev), shift = 0;
++ int default_indices = bdcb->i2c_default_indices;
++
++ if (idx != 0x7f && bdcb->dcb.entry[idx].i2c_upper_default)
++ shift = 4;
++
++ i2c_index = (default_indices >> shift) & 0xf;
++ }
++ if (i2c_index == 0x80) /* g80+ */
++ i2c_index = bdcb->i2c_default_indices & 0xf;
++
++ return nouveau_i2c_find(dev, i2c_index);
++}
++
++static uint32_t get_tmds_index_reg(struct drm_device *dev, uint8_t mlv)
++{
++ /*
++ * For mlv < 0x80, it is an index into a table of TMDS base addresses.
++ * For mlv == 0x80 use the "or" value of the dcb_entry indexed by
++ * CR58 for CR57 = 0 to index a table of offsets to the basic
++ * 0x6808b0 address.
++ * For mlv == 0x81 use the "or" value of the dcb_entry indexed by
++ * CR58 for CR57 = 0 to index a table of offsets to the basic
++ * 0x6808b0 address, and then flip the offset by 8.
++ */
++
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ const int pramdac_offset[13] = {
++ 0, 0, 0x8, 0, 0x2000, 0, 0, 0, 0x2008, 0, 0, 0, 0x2000 };
++ const uint32_t pramdac_table[4] = {
++ 0x6808b0, 0x6808b8, 0x6828b0, 0x6828b8 };
++
++ if (mlv >= 0x80) {
++ int dcb_entry, dacoffset;
++
++ /* note: dcb_entry_idx_from_crtchead needs pre-script set-up */
++ dcb_entry = dcb_entry_idx_from_crtchead(dev);
++ if (dcb_entry == 0x7f)
++ return 0;
++ dacoffset = pramdac_offset[
++ dev_priv->VBIOS.bdcb.dcb.entry[dcb_entry].or];
++ if (mlv == 0x81)
++ dacoffset ^= 8;
++ return 0x6808b0 + dacoffset;
++ } else {
++ if (mlv > ARRAY_SIZE(pramdac_table)) {
++ NV_ERROR(dev, "Magic Lookup Value too big (%02X)\n",
++ mlv);
++ return 0;
++ }
++ return pramdac_table[mlv];
++ }
++}
++
++static int
++init_io_restrict_prog(struct nvbios *bios, uint16_t offset,
++ struct init_exec *iexec)
++{
++ /*
++ * INIT_IO_RESTRICT_PROG opcode: 0x32 ('2')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (16 bit): CRTC port
++ * offset + 3 (8 bit): CRTC index
++ * offset + 4 (8 bit): mask
++ * offset + 5 (8 bit): shift
++ * offset + 6 (8 bit): count
++ * offset + 7 (32 bit): register
++ * offset + 11 (32 bit): configuration 1
++ * ...
++ *
++ * Starting at offset + 11 there are "count" 32 bit values.
++ * To find out which value to use read index "CRTC index" on "CRTC
++ * port", AND this value with "mask" and then bit shift right "shift"
++ * bits. Read the appropriate value using this index and write to
++ * "register"
++ */
++
++ uint16_t crtcport = ROM16(bios->data[offset + 1]);
++ uint8_t crtcindex = bios->data[offset + 3];
++ uint8_t mask = bios->data[offset + 4];
++ uint8_t shift = bios->data[offset + 5];
++ uint8_t count = bios->data[offset + 6];
++ uint32_t reg = ROM32(bios->data[offset + 7]);
++ uint8_t config;
++ uint32_t configval;
++ int len = 11 + count * 4;
++
++ if (!iexec->execute)
++ return len;
++
++ BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, "
++ "Shift: 0x%02X, Count: 0x%02X, Reg: 0x%08X\n",
++ offset, crtcport, crtcindex, mask, shift, count, reg);
++
++ config = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) >> shift;
++ if (config > count) {
++ NV_ERROR(bios->dev,
++ "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n",
++ offset, config, count);
++ return 0;
++ }
++
++ configval = ROM32(bios->data[offset + 11 + config * 4]);
++
++ BIOSLOG(bios, "0x%04X: Writing config %02X\n", offset, config);
++
++ bios_wr32(bios, reg, configval);
++
++ return len;
++}
++
++static int
++init_repeat(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_REPEAT opcode: 0x33 ('3')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): count
++ *
++ * Execute script following this opcode up to INIT_REPEAT_END
++ * "count" times
++ */
++
++ uint8_t count = bios->data[offset + 1];
++ uint8_t i;
++
++ /* no iexec->execute check by design */
++
++ BIOSLOG(bios, "0x%04X: Repeating following segment %d times\n",
++ offset, count);
++
++ iexec->repeat = true;
++
++ /*
++ * count - 1, as the script block will execute once when we leave this
++ * opcode -- this is compatible with bios behaviour as:
++ * a) the block is always executed at least once, even if count == 0
++ * b) the bios interpreter skips to the op following INIT_END_REPEAT,
++ * while we don't
++ */
++ for (i = 0; i < count - 1; i++)
++ parse_init_table(bios, offset + 2, iexec);
++
++ iexec->repeat = false;
++
++ return 2;
++}
++
++static int
++init_io_restrict_pll(struct nvbios *bios, uint16_t offset,
++ struct init_exec *iexec)
++{
++ /*
++ * INIT_IO_RESTRICT_PLL opcode: 0x34 ('4')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (16 bit): CRTC port
++ * offset + 3 (8 bit): CRTC index
++ * offset + 4 (8 bit): mask
++ * offset + 5 (8 bit): shift
++ * offset + 6 (8 bit): IO flag condition index
++ * offset + 7 (8 bit): count
++ * offset + 8 (32 bit): register
++ * offset + 12 (16 bit): frequency 1
++ * ...
++ *
++ * Starting at offset + 12 there are "count" 16 bit frequencies (10kHz).
++ * Set PLL register "register" to coefficients for frequency n,
++ * selected by reading index "CRTC index" of "CRTC port" ANDed with
++ * "mask" and shifted right by "shift".
++ *
++ * If "IO flag condition index" > 0, and condition met, double
++ * frequency before setting it.
++ */
++
++ uint16_t crtcport = ROM16(bios->data[offset + 1]);
++ uint8_t crtcindex = bios->data[offset + 3];
++ uint8_t mask = bios->data[offset + 4];
++ uint8_t shift = bios->data[offset + 5];
++ int8_t io_flag_condition_idx = bios->data[offset + 6];
++ uint8_t count = bios->data[offset + 7];
++ uint32_t reg = ROM32(bios->data[offset + 8]);
++ uint8_t config;
++ uint16_t freq;
++ int len = 12 + count * 2;
++
++ if (!iexec->execute)
++ return len;
++
++ BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, "
++ "Shift: 0x%02X, IO Flag Condition: 0x%02X, "
++ "Count: 0x%02X, Reg: 0x%08X\n",
++ offset, crtcport, crtcindex, mask, shift,
++ io_flag_condition_idx, count, reg);
++
++ config = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) >> shift;
++ if (config > count) {
++ NV_ERROR(bios->dev,
++ "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n",
++ offset, config, count);
++ return 0;
++ }
++
++ freq = ROM16(bios->data[offset + 12 + config * 2]);
++
++ if (io_flag_condition_idx > 0) {
++ if (io_flag_condition_met(bios, offset, io_flag_condition_idx)) {
++ BIOSLOG(bios, "0x%04X: Condition fulfilled -- "
++ "frequency doubled\n", offset);
++ freq *= 2;
++ } else
++ BIOSLOG(bios, "0x%04X: Condition not fulfilled -- "
++ "frequency unchanged\n", offset);
++ }
++
++ BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Config: 0x%02X, Freq: %d0kHz\n",
++ offset, reg, config, freq);
++
++ setPLL(bios, reg, freq * 10);
++
++ return len;
++}
++
++static int
++init_end_repeat(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_END_REPEAT opcode: 0x36 ('6')
++ *
++ * offset (8 bit): opcode
++ *
++ * Marks the end of the block for INIT_REPEAT to repeat
++ */
++
++ /* no iexec->execute check by design */
++
++ /*
++ * iexec->repeat flag necessary to go past INIT_END_REPEAT opcode when
++ * we're not in repeat mode
++ */
++ if (iexec->repeat)
++ return 0;
++
++ return 1;
++}
++
++static int
++init_copy(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_COPY opcode: 0x37 ('7')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (32 bit): register
++ * offset + 5 (8 bit): shift
++ * offset + 6 (8 bit): srcmask
++ * offset + 7 (16 bit): CRTC port
++ * offset + 9 (8 bit): CRTC index
++ * offset + 10 (8 bit): mask
++ *
++ * Read index "CRTC index" on "CRTC port", AND with "mask", OR with
++ * (REGVAL("register") >> "shift" & "srcmask") and write-back to CRTC
++ * port
++ */
++
++ uint32_t reg = ROM32(bios->data[offset + 1]);
++ uint8_t shift = bios->data[offset + 5];
++ uint8_t srcmask = bios->data[offset + 6];
++ uint16_t crtcport = ROM16(bios->data[offset + 7]);
++ uint8_t crtcindex = bios->data[offset + 9];
++ uint8_t mask = bios->data[offset + 10];
++ uint32_t data;
++ uint8_t crtcdata;
++
++ if (!iexec->execute)
++ return 11;
++
++ BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Shift: 0x%02X, SrcMask: 0x%02X, "
++ "Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X\n",
++ offset, reg, shift, srcmask, crtcport, crtcindex, mask);
++
++ data = bios_rd32(bios, reg);
++
++ if (shift < 0x80)
++ data >>= shift;
++ else
++ data <<= (0x100 - shift);
++
++ data &= srcmask;
++
++ crtcdata = bios_idxprt_rd(bios, crtcport, crtcindex) & mask;
++ crtcdata |= (uint8_t)data;
++ bios_idxprt_wr(bios, crtcport, crtcindex, crtcdata);
++
++ return 11;
++}
++
++static int
++init_not(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_NOT opcode: 0x38 ('8')
++ *
++ * offset (8 bit): opcode
++ *
++ * Invert the current execute / no-execute condition (i.e. "else")
++ */
++ if (iexec->execute)
++ BIOSLOG(bios, "0x%04X: ------ Skipping following commands ------\n", offset);
++ else
++ BIOSLOG(bios, "0x%04X: ------ Executing following commands ------\n", offset);
++
++ iexec->execute = !iexec->execute;
++ return 1;
++}
++
++static int
++init_io_flag_condition(struct nvbios *bios, uint16_t offset,
++ struct init_exec *iexec)
++{
++ /*
++ * INIT_IO_FLAG_CONDITION opcode: 0x39 ('9')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): condition number
++ *
++ * Check condition "condition number" in the IO flag condition table.
++ * If condition not met skip subsequent opcodes until condition is
++ * inverted (INIT_NOT), or we hit INIT_RESUME
++ */
++
++ uint8_t cond = bios->data[offset + 1];
++
++ if (!iexec->execute)
++ return 2;
++
++ if (io_flag_condition_met(bios, offset, cond))
++ BIOSLOG(bios, "0x%04X: Condition fulfilled -- continuing to execute\n", offset);
++ else {
++ BIOSLOG(bios, "0x%04X: Condition not fulfilled -- skipping following commands\n", offset);
++ iexec->execute = false;
++ }
++
++ return 2;
++}
++
++static int
++init_idx_addr_latched(struct nvbios *bios, uint16_t offset,
++ struct init_exec *iexec)
++{
++ /*
++ * INIT_INDEX_ADDRESS_LATCHED opcode: 0x49 ('I')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (32 bit): control register
++ * offset + 5 (32 bit): data register
++ * offset + 9 (32 bit): mask
++ * offset + 13 (32 bit): data
++ * offset + 17 (8 bit): count
++ * offset + 18 (8 bit): address 1
++ * offset + 19 (8 bit): data 1
++ * ...
++ *
++ * For each of "count" address and data pairs, write "data n" to
++ * "data register", read the current value of "control register",
++ * and write it back once ANDed with "mask", ORed with "data",
++ * and ORed with "address n"
++ */
++
++ uint32_t controlreg = ROM32(bios->data[offset + 1]);
++ uint32_t datareg = ROM32(bios->data[offset + 5]);
++ uint32_t mask = ROM32(bios->data[offset + 9]);
++ uint32_t data = ROM32(bios->data[offset + 13]);
++ uint8_t count = bios->data[offset + 17];
++ int len = 18 + count * 2;
++ uint32_t value;
++ int i;
++
++ if (!iexec->execute)
++ return len;
++
++ BIOSLOG(bios, "0x%04X: ControlReg: 0x%08X, DataReg: 0x%08X, "
++ "Mask: 0x%08X, Data: 0x%08X, Count: 0x%02X\n",
++ offset, controlreg, datareg, mask, data, count);
++
++ for (i = 0; i < count; i++) {
++ uint8_t instaddress = bios->data[offset + 18 + i * 2];
++ uint8_t instdata = bios->data[offset + 19 + i * 2];
++
++ BIOSLOG(bios, "0x%04X: Address: 0x%02X, Data: 0x%02X\n",
++ offset, instaddress, instdata);
++
++ bios_wr32(bios, datareg, instdata);
++ value = bios_rd32(bios, controlreg) & mask;
++ value |= data;
++ value |= instaddress;
++ bios_wr32(bios, controlreg, value);
++ }
++
++ return len;
++}
++
++static int
++init_io_restrict_pll2(struct nvbios *bios, uint16_t offset,
++ struct init_exec *iexec)
++{
++ /*
++ * INIT_IO_RESTRICT_PLL2 opcode: 0x4A ('J')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (16 bit): CRTC port
++ * offset + 3 (8 bit): CRTC index
++ * offset + 4 (8 bit): mask
++ * offset + 5 (8 bit): shift
++ * offset + 6 (8 bit): count
++ * offset + 7 (32 bit): register
++ * offset + 11 (32 bit): frequency 1
++ * ...
++ *
++ * Starting at offset + 11 there are "count" 32 bit frequencies (kHz).
++ * Set PLL register "register" to coefficients for frequency n,
++ * selected by reading index "CRTC index" of "CRTC port" ANDed with
++ * "mask" and shifted right by "shift".
++ */
++
++ uint16_t crtcport = ROM16(bios->data[offset + 1]);
++ uint8_t crtcindex = bios->data[offset + 3];
++ uint8_t mask = bios->data[offset + 4];
++ uint8_t shift = bios->data[offset + 5];
++ uint8_t count = bios->data[offset + 6];
++ uint32_t reg = ROM32(bios->data[offset + 7]);
++ int len = 11 + count * 4;
++ uint8_t config;
++ uint32_t freq;
++
++ if (!iexec->execute)
++ return len;
++
++ BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, "
++ "Shift: 0x%02X, Count: 0x%02X, Reg: 0x%08X\n",
++ offset, crtcport, crtcindex, mask, shift, count, reg);
++
++ if (!reg)
++ return len;
++
++ config = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) >> shift;
++ if (config > count) {
++ NV_ERROR(bios->dev,
++ "0x%04X: Config 0x%02X exceeds maximal bound 0x%02X\n",
++ offset, config, count);
++ return 0;
++ }
++
++ freq = ROM32(bios->data[offset + 11 + config * 4]);
++
++ BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Config: 0x%02X, Freq: %dkHz\n",
++ offset, reg, config, freq);
++
++ setPLL(bios, reg, freq);
++
++ return len;
++}
++
++static int
++init_pll2(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_PLL2 opcode: 0x4B ('K')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (32 bit): register
++ * offset + 5 (32 bit): freq
++ *
++ * Set PLL register "register" to coefficients for frequency "freq"
++ */
++
++ uint32_t reg = ROM32(bios->data[offset + 1]);
++ uint32_t freq = ROM32(bios->data[offset + 5]);
++
++ if (!iexec->execute)
++ return 9;
++
++ BIOSLOG(bios, "0x%04X: Reg: 0x%04X, Freq: %dkHz\n",
++ offset, reg, freq);
++
++ setPLL(bios, reg, freq);
++ return 9;
++}
++
++static int
++init_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_I2C_BYTE opcode: 0x4C ('L')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): DCB I2C table entry index
++ * offset + 2 (8 bit): I2C slave address
++ * offset + 3 (8 bit): count
++ * offset + 4 (8 bit): I2C register 1
++ * offset + 5 (8 bit): mask 1
++ * offset + 6 (8 bit): data 1
++ * ...
++ *
++ * For each of "count" registers given by "I2C register n" on the device
++ * addressed by "I2C slave address" on the I2C bus given by
++ * "DCB I2C table entry index", read the register, AND the result with
++ * "mask n" and OR it with "data n" before writing it back to the device
++ */
++
++ uint8_t i2c_index = bios->data[offset + 1];
++ uint8_t i2c_address = bios->data[offset + 2];
++ uint8_t count = bios->data[offset + 3];
++ int len = 4 + count * 3;
++ struct nouveau_i2c_chan *chan;
++ struct i2c_msg msg;
++ int i;
++
++ if (!iexec->execute)
++ return len;
++
++ BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X, "
++ "Count: 0x%02X\n",
++ offset, i2c_index, i2c_address, count);
++
++ chan = init_i2c_device_find(bios->dev, i2c_index);
++ if (!chan)
++ return 0;
++
++ for (i = 0; i < count; i++) {
++ uint8_t i2c_reg = bios->data[offset + 4 + i * 3];
++ uint8_t mask = bios->data[offset + 5 + i * 3];
++ uint8_t data = bios->data[offset + 6 + i * 3];
++ uint8_t value;
++
++ msg.addr = i2c_address;
++ msg.flags = I2C_M_RD;
++ msg.len = 1;
++ msg.buf = &value;
++ if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
++ return 0;
++
++ BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Value: 0x%02X, "
++ "Mask: 0x%02X, Data: 0x%02X\n",
++ offset, i2c_reg, value, mask, data);
++
++ value = (value & mask) | data;
++
++ if (bios->execute) {
++ msg.addr = i2c_address;
++ msg.flags = 0;
++ msg.len = 1;
++ msg.buf = &value;
++ if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
++ return 0;
++ }
++ }
++
++ return len;
++}
++
++static int
++init_zm_i2c_byte(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_ZM_I2C_BYTE opcode: 0x4D ('M')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): DCB I2C table entry index
++ * offset + 2 (8 bit): I2C slave address
++ * offset + 3 (8 bit): count
++ * offset + 4 (8 bit): I2C register 1
++ * offset + 5 (8 bit): data 1
++ * ...
++ *
++ * For each of "count" registers given by "I2C register n" on the device
++ * addressed by "I2C slave address" on the I2C bus given by
++ * "DCB I2C table entry index", set the register to "data n"
++ */
++
++ uint8_t i2c_index = bios->data[offset + 1];
++ uint8_t i2c_address = bios->data[offset + 2];
++ uint8_t count = bios->data[offset + 3];
++ int len = 4 + count * 2;
++ struct nouveau_i2c_chan *chan;
++ struct i2c_msg msg;
++ int i;
++
++ if (!iexec->execute)
++ return len;
++
++ BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X, "
++ "Count: 0x%02X\n",
++ offset, i2c_index, i2c_address, count);
++
++ chan = init_i2c_device_find(bios->dev, i2c_index);
++ if (!chan)
++ return 0;
++
++ for (i = 0; i < count; i++) {
++ uint8_t i2c_reg = bios->data[offset + 4 + i * 2];
++ uint8_t data = bios->data[offset + 5 + i * 2];
++
++ BIOSLOG(bios, "0x%04X: I2CReg: 0x%02X, Data: 0x%02X\n",
++ offset, i2c_reg, data);
++
++ if (bios->execute) {
++ msg.addr = i2c_address;
++ msg.flags = 0;
++ msg.len = 1;
++ msg.buf = &data;
++ if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
++ return 0;
++ }
++ }
++
++ return len;
++}
++
++static int
++init_zm_i2c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_ZM_I2C opcode: 0x4E ('N')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): DCB I2C table entry index
++ * offset + 2 (8 bit): I2C slave address
++ * offset + 3 (8 bit): count
++ * offset + 4 (8 bit): data 1
++ * ...
++ *
++ * Send "count" bytes ("data n") to the device addressed by "I2C slave
++ * address" on the I2C bus given by "DCB I2C table entry index"
++ */
++
++ uint8_t i2c_index = bios->data[offset + 1];
++ uint8_t i2c_address = bios->data[offset + 2];
++ uint8_t count = bios->data[offset + 3];
++ int len = 4 + count;
++ struct nouveau_i2c_chan *chan;
++ struct i2c_msg msg;
++ uint8_t data[256];
++ int i;
++
++ if (!iexec->execute)
++ return len;
++
++ BIOSLOG(bios, "0x%04X: DCBI2CIndex: 0x%02X, I2CAddress: 0x%02X, "
++ "Count: 0x%02X\n",
++ offset, i2c_index, i2c_address, count);
++
++ chan = init_i2c_device_find(bios->dev, i2c_index);
++ if (!chan)
++ return 0;
++
++ for (i = 0; i < count; i++) {
++ data[i] = bios->data[offset + 4 + i];
++
++ BIOSLOG(bios, "0x%04X: Data: 0x%02X\n", offset, data[i]);
++ }
++
++ if (bios->execute) {
++ msg.addr = i2c_address;
++ msg.flags = 0;
++ msg.len = count;
++ msg.buf = data;
++ if (i2c_transfer(&chan->adapter, &msg, 1) != 1)
++ return 0;
++ }
++
++ return len;
++}
++
++static int
++init_tmds(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_TMDS opcode: 0x4F ('O') (non-canon name)
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): magic lookup value
++ * offset + 2 (8 bit): TMDS address
++ * offset + 3 (8 bit): mask
++ * offset + 4 (8 bit): data
++ *
++ * Read the data reg for TMDS address "TMDS address", AND it with mask
++ * and OR it with data, then write it back
++ * "magic lookup value" determines which TMDS base address register is
++ * used -- see get_tmds_index_reg()
++ */
++
++ uint8_t mlv = bios->data[offset + 1];
++ uint32_t tmdsaddr = bios->data[offset + 2];
++ uint8_t mask = bios->data[offset + 3];
++ uint8_t data = bios->data[offset + 4];
++ uint32_t reg, value;
++
++ if (!iexec->execute)
++ return 5;
++
++ BIOSLOG(bios, "0x%04X: MagicLookupValue: 0x%02X, TMDSAddr: 0x%02X, "
++ "Mask: 0x%02X, Data: 0x%02X\n",
++ offset, mlv, tmdsaddr, mask, data);
++
++ reg = get_tmds_index_reg(bios->dev, mlv);
++ if (!reg)
++ return 0;
++
++ bios_wr32(bios, reg,
++ tmdsaddr | NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE);
++ value = (bios_rd32(bios, reg + 4) & mask) | data;
++ bios_wr32(bios, reg + 4, value);
++ bios_wr32(bios, reg, tmdsaddr);
++
++ return 5;
++}
++
++static int
++init_zm_tmds_group(struct nvbios *bios, uint16_t offset,
++ struct init_exec *iexec)
++{
++ /*
++ * INIT_ZM_TMDS_GROUP opcode: 0x50 ('P') (non-canon name)
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): magic lookup value
++ * offset + 2 (8 bit): count
++ * offset + 3 (8 bit): addr 1
++ * offset + 4 (8 bit): data 1
++ * ...
++ *
++ * For each of "count" TMDS address and data pairs write "data n" to
++ * "addr n". "magic lookup value" determines which TMDS base address
++ * register is used -- see get_tmds_index_reg()
++ */
++
++ uint8_t mlv = bios->data[offset + 1];
++ uint8_t count = bios->data[offset + 2];
++ int len = 3 + count * 2;
++ uint32_t reg;
++ int i;
++
++ if (!iexec->execute)
++ return len;
++
++ BIOSLOG(bios, "0x%04X: MagicLookupValue: 0x%02X, Count: 0x%02X\n",
++ offset, mlv, count);
++
++ reg = get_tmds_index_reg(bios->dev, mlv);
++ if (!reg)
++ return 0;
++
++ for (i = 0; i < count; i++) {
++ uint8_t tmdsaddr = bios->data[offset + 3 + i * 2];
++ uint8_t tmdsdata = bios->data[offset + 4 + i * 2];
++
++ bios_wr32(bios, reg + 4, tmdsdata);
++ bios_wr32(bios, reg, tmdsaddr);
++ }
++
++ return len;
++}
++
++static int
++init_cr_idx_adr_latch(struct nvbios *bios, uint16_t offset,
++ struct init_exec *iexec)
++{
++ /*
++ * INIT_CR_INDEX_ADDRESS_LATCHED opcode: 0x51 ('Q')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): CRTC index1
++ * offset + 2 (8 bit): CRTC index2
++ * offset + 3 (8 bit): baseaddr
++ * offset + 4 (8 bit): count
++ * offset + 5 (8 bit): data 1
++ * ...
++ *
++ * For each of "count" address and data pairs, write "baseaddr + n" to
++ * "CRTC index1" and "data n" to "CRTC index2"
++ * Once complete, restore initial value read from "CRTC index1"
++ */
++ uint8_t crtcindex1 = bios->data[offset + 1];
++ uint8_t crtcindex2 = bios->data[offset + 2];
++ uint8_t baseaddr = bios->data[offset + 3];
++ uint8_t count = bios->data[offset + 4];
++ int len = 5 + count;
++ uint8_t oldaddr, data;
++ int i;
++
++ if (!iexec->execute)
++ return len;
++
++ BIOSLOG(bios, "0x%04X: Index1: 0x%02X, Index2: 0x%02X, "
++ "BaseAddr: 0x%02X, Count: 0x%02X\n",
++ offset, crtcindex1, crtcindex2, baseaddr, count);
++
++ oldaddr = bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, crtcindex1);
++
++ for (i = 0; i < count; i++) {
++ bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex1,
++ baseaddr + i);
++ data = bios->data[offset + 5 + i];
++ bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex2, data);
++ }
++
++ bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex1, oldaddr);
++
++ return len;
++}
++
++static int
++init_cr(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_CR opcode: 0x52 ('R')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): CRTC index
++ * offset + 2 (8 bit): mask
++ * offset + 3 (8 bit): data
++ *
++ * Assign the value of at "CRTC index" ANDed with mask and ORed with
++ * data back to "CRTC index"
++ */
++
++ uint8_t crtcindex = bios->data[offset + 1];
++ uint8_t mask = bios->data[offset + 2];
++ uint8_t data = bios->data[offset + 3];
++ uint8_t value;
++
++ if (!iexec->execute)
++ return 4;
++
++ BIOSLOG(bios, "0x%04X: Index: 0x%02X, Mask: 0x%02X, Data: 0x%02X\n",
++ offset, crtcindex, mask, data);
++
++ value = bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, crtcindex) & mask;
++ value |= data;
++ bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex, value);
++
++ return 4;
++}
++
++static int
++init_zm_cr(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_ZM_CR opcode: 0x53 ('S')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): CRTC index
++ * offset + 2 (8 bit): value
++ *
++ * Assign "value" to CRTC register with index "CRTC index".
++ */
++
++ uint8_t crtcindex = ROM32(bios->data[offset + 1]);
++ uint8_t data = bios->data[offset + 2];
++
++ if (!iexec->execute)
++ return 3;
++
++ bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, crtcindex, data);
++
++ return 3;
++}
++
++static int
++init_zm_cr_group(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_ZM_CR_GROUP opcode: 0x54 ('T')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): count
++ * offset + 2 (8 bit): CRTC index 1
++ * offset + 3 (8 bit): value 1
++ * ...
++ *
++ * For "count", assign "value n" to CRTC register with index
++ * "CRTC index n".
++ */
++
++ uint8_t count = bios->data[offset + 1];
++ int len = 2 + count * 2;
++ int i;
++
++ if (!iexec->execute)
++ return len;
++
++ for (i = 0; i < count; i++)
++ init_zm_cr(bios, offset + 2 + 2 * i - 1, iexec);
++
++ return len;
++}
++
++static int
++init_condition_time(struct nvbios *bios, uint16_t offset,
++ struct init_exec *iexec)
++{
++ /*
++ * INIT_CONDITION_TIME opcode: 0x56 ('V')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): condition number
++ * offset + 2 (8 bit): retries / 50
++ *
++ * Check condition "condition number" in the condition table.
++ * Bios code then sleeps for 2ms if the condition is not met, and
++ * repeats up to "retries" times, but on one C51 this has proved
++ * insufficient. In mmiotraces the driver sleeps for 20ms, so we do
++ * this, and bail after "retries" times, or 2s, whichever is less.
++ * If still not met after retries, clear execution flag for this table.
++ */
++
++ uint8_t cond = bios->data[offset + 1];
++ uint16_t retries = bios->data[offset + 2] * 50;
++ unsigned cnt;
++
++ if (!iexec->execute)
++ return 3;
++
++ if (retries > 100)
++ retries = 100;
++
++ BIOSLOG(bios, "0x%04X: Condition: 0x%02X, Retries: 0x%02X\n",
++ offset, cond, retries);
++
++ if (!bios->execute) /* avoid 2s delays when "faking" execution */
++ retries = 1;
++
++ for (cnt = 0; cnt < retries; cnt++) {
++ if (bios_condition_met(bios, offset, cond)) {
++ BIOSLOG(bios, "0x%04X: Condition met, continuing\n",
++ offset);
++ break;
++ } else {
++ BIOSLOG(bios, "0x%04X: "
++ "Condition not met, sleeping for 20ms\n",
++ offset);
++ msleep(20);
++ }
++ }
++
++ if (!bios_condition_met(bios, offset, cond)) {
++ NV_WARN(bios->dev,
++ "0x%04X: Condition still not met after %dms, "
++ "skipping following opcodes\n", offset, 20 * retries);
++ iexec->execute = false;
++ }
++
++ return 3;
++}
++
++static int
++init_zm_reg_sequence(struct nvbios *bios, uint16_t offset,
++ struct init_exec *iexec)
++{
++ /*
++ * INIT_ZM_REG_SEQUENCE opcode: 0x58 ('X')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (32 bit): base register
++ * offset + 5 (8 bit): count
++ * offset + 6 (32 bit): value 1
++ * ...
++ *
++ * Starting at offset + 6 there are "count" 32 bit values.
++ * For "count" iterations set "base register" + 4 * current_iteration
++ * to "value current_iteration"
++ */
++
++ uint32_t basereg = ROM32(bios->data[offset + 1]);
++ uint32_t count = bios->data[offset + 5];
++ int len = 6 + count * 4;
++ int i;
++
++ if (!iexec->execute)
++ return len;
++
++ BIOSLOG(bios, "0x%04X: BaseReg: 0x%08X, Count: 0x%02X\n",
++ offset, basereg, count);
++
++ for (i = 0; i < count; i++) {
++ uint32_t reg = basereg + i * 4;
++ uint32_t data = ROM32(bios->data[offset + 6 + i * 4]);
++
++ bios_wr32(bios, reg, data);
++ }
++
++ return len;
++}
++
++static int
++init_sub_direct(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_SUB_DIRECT opcode: 0x5B ('[')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (16 bit): subroutine offset (in bios)
++ *
++ * Calls a subroutine that will execute commands until INIT_DONE
++ * is found.
++ */
++
++ uint16_t sub_offset = ROM16(bios->data[offset + 1]);
++
++ if (!iexec->execute)
++ return 3;
++
++ BIOSLOG(bios, "0x%04X: Executing subroutine at 0x%04X\n",
++ offset, sub_offset);
++
++ parse_init_table(bios, sub_offset, iexec);
++
++ BIOSLOG(bios, "0x%04X: End of 0x%04X subroutine\n", offset, sub_offset);
++
++ return 3;
++}
++
++static int
++init_copy_nv_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_COPY_NV_REG opcode: 0x5F ('_')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (32 bit): src reg
++ * offset + 5 (8 bit): shift
++ * offset + 6 (32 bit): src mask
++ * offset + 10 (32 bit): xor
++ * offset + 14 (32 bit): dst reg
++ * offset + 18 (32 bit): dst mask
++ *
++ * Shift REGVAL("src reg") right by (signed) "shift", AND result with
++ * "src mask", then XOR with "xor". Write this OR'd with
++ * (REGVAL("dst reg") AND'd with "dst mask") to "dst reg"
++ */
++
++ uint32_t srcreg = *((uint32_t *)(&bios->data[offset + 1]));
++ uint8_t shift = bios->data[offset + 5];
++ uint32_t srcmask = *((uint32_t *)(&bios->data[offset + 6]));
++ uint32_t xor = *((uint32_t *)(&bios->data[offset + 10]));
++ uint32_t dstreg = *((uint32_t *)(&bios->data[offset + 14]));
++ uint32_t dstmask = *((uint32_t *)(&bios->data[offset + 18]));
++ uint32_t srcvalue, dstvalue;
++
++ if (!iexec->execute)
++ return 22;
++
++ BIOSLOG(bios, "0x%04X: SrcReg: 0x%08X, Shift: 0x%02X, SrcMask: 0x%08X, "
++ "Xor: 0x%08X, DstReg: 0x%08X, DstMask: 0x%08X\n",
++ offset, srcreg, shift, srcmask, xor, dstreg, dstmask);
++
++ srcvalue = bios_rd32(bios, srcreg);
++
++ if (shift < 0x80)
++ srcvalue >>= shift;
++ else
++ srcvalue <<= (0x100 - shift);
++
++ srcvalue = (srcvalue & srcmask) ^ xor;
++
++ dstvalue = bios_rd32(bios, dstreg) & dstmask;
++
++ bios_wr32(bios, dstreg, dstvalue | srcvalue);
++
++ return 22;
++}
++
++static int
++init_zm_index_io(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_ZM_INDEX_IO opcode: 0x62 ('b')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (16 bit): CRTC port
++ * offset + 3 (8 bit): CRTC index
++ * offset + 4 (8 bit): data
++ *
++ * Write "data" to index "CRTC index" of "CRTC port"
++ */
++ uint16_t crtcport = ROM16(bios->data[offset + 1]);
++ uint8_t crtcindex = bios->data[offset + 3];
++ uint8_t data = bios->data[offset + 4];
++
++ if (!iexec->execute)
++ return 5;
++
++ bios_idxprt_wr(bios, crtcport, crtcindex, data);
++
++ return 5;
++}
++
++static int
++init_compute_mem(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_COMPUTE_MEM opcode: 0x63 ('c')
++ *
++ * offset (8 bit): opcode
++ *
++ * This opcode is meant to set NV_PFB_CFG0 (0x100200) appropriately so
++ * that the hardware can correctly calculate how much VRAM it has
++ * (and subsequently report that value in NV_PFB_CSTATUS (0x10020C))
++ *
++ * The implementation of this opcode in general consists of two parts:
++ * 1) determination of the memory bus width
++ * 2) determination of how many of the card's RAM pads have ICs attached
++ *
++ * 1) is done by a cunning combination of writes to offsets 0x1c and
++ * 0x3c in the framebuffer, and seeing whether the written values are
++ * read back correctly. This then affects bits 4-7 of NV_PFB_CFG0
++ *
++ * 2) is done by a cunning combination of writes to an offset slightly
++ * less than the maximum memory reported by NV_PFB_CSTATUS, then seeing
++ * if the test pattern can be read back. This then affects bits 12-15 of
++ * NV_PFB_CFG0
++ *
++ * In this context a "cunning combination" may include multiple reads
++ * and writes to varying locations, often alternating the test pattern
++ * and 0, doubtless to make sure buffers are filled, residual charges
++ * on tracks are removed etc.
++ *
++ * Unfortunately, the "cunning combination"s mentioned above, and the
++ * changes to the bits in NV_PFB_CFG0 differ with nearly every bios
++ * trace I have.
++ *
++ * Therefore, we cheat and assume the value of NV_PFB_CFG0 with which
++ * we started was correct, and use that instead
++ */
++
++ /* no iexec->execute check by design */
++
++ /*
++ * This appears to be a NOP on G8x chipsets, both io logs of the VBIOS
++ * and kmmio traces of the binary driver POSTing the card show nothing
++ * being done for this opcode. why is it still listed in the table?!
++ */
++
++ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
++
++ if (dev_priv->card_type >= NV_40)
++ return 1;
++
++ /*
++ * On every card I've seen, this step gets done for us earlier in
++ * the init scripts
++ uint8_t crdata = bios_idxprt_rd(dev, NV_VIO_SRX, 0x01);
++ bios_idxprt_wr(dev, NV_VIO_SRX, 0x01, crdata | 0x20);
++ */
++
++ /*
++ * This also has probably been done in the scripts, but an mmio trace of
++ * s3 resume shows nvidia doing it anyway (unlike the NV_VIO_SRX write)
++ */
++ bios_wr32(bios, NV_PFB_REFCTRL, NV_PFB_REFCTRL_VALID_1);
++
++ /* write back the saved configuration value */
++ bios_wr32(bios, NV_PFB_CFG0, bios->state.saved_nv_pfb_cfg0);
++
++ return 1;
++}
++
++static int
++init_reset(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_RESET opcode: 0x65 ('e')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (32 bit): register
++ * offset + 5 (32 bit): value1
++ * offset + 9 (32 bit): value2
++ *
++ * Assign "value1" to "register", then assign "value2" to "register"
++ */
++
++ uint32_t reg = ROM32(bios->data[offset + 1]);
++ uint32_t value1 = ROM32(bios->data[offset + 5]);
++ uint32_t value2 = ROM32(bios->data[offset + 9]);
++ uint32_t pci_nv_19, pci_nv_20;
++
++ /* no iexec->execute check by design */
++
++ pci_nv_19 = bios_rd32(bios, NV_PBUS_PCI_NV_19);
++ bios_wr32(bios, NV_PBUS_PCI_NV_19, 0);
++ bios_wr32(bios, reg, value1);
++
++ udelay(10);
++
++ bios_wr32(bios, reg, value2);
++ bios_wr32(bios, NV_PBUS_PCI_NV_19, pci_nv_19);
++
++ pci_nv_20 = bios_rd32(bios, NV_PBUS_PCI_NV_20);
++ pci_nv_20 &= ~NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED; /* 0xfffffffe */
++ bios_wr32(bios, NV_PBUS_PCI_NV_20, pci_nv_20);
++
++ return 13;
++}
++
++static int
++init_configure_mem(struct nvbios *bios, uint16_t offset,
++ struct init_exec *iexec)
++{
++ /*
++ * INIT_CONFIGURE_MEM opcode: 0x66 ('f')
++ *
++ * offset (8 bit): opcode
++ *
++ * Equivalent to INIT_DONE on bios version 3 or greater.
++ * For early bios versions, sets up the memory registers, using values
++ * taken from the memory init table
++ */
++
++ /* no iexec->execute check by design */
++
++ uint16_t meminitoffs = bios->legacy.mem_init_tbl_ptr + MEM_INIT_SIZE * (bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_SCRATCH4__INDEX) >> 4);
++ uint16_t seqtbloffs = bios->legacy.sdr_seq_tbl_ptr, meminitdata = meminitoffs + 6;
++ uint32_t reg, data;
++
++ if (bios->major_version > 2)
++ return 0;
++
++ bios_idxprt_wr(bios, NV_VIO_SRX, NV_VIO_SR_CLOCK_INDEX, bios_idxprt_rd(
++ bios, NV_VIO_SRX, NV_VIO_SR_CLOCK_INDEX) | 0x20);
++
++ if (bios->data[meminitoffs] & 1)
++ seqtbloffs = bios->legacy.ddr_seq_tbl_ptr;
++
++ for (reg = ROM32(bios->data[seqtbloffs]);
++ reg != 0xffffffff;
++ reg = ROM32(bios->data[seqtbloffs += 4])) {
++
++ switch (reg) {
++ case NV_PFB_PRE:
++ data = NV_PFB_PRE_CMD_PRECHARGE;
++ break;
++ case NV_PFB_PAD:
++ data = NV_PFB_PAD_CKE_NORMAL;
++ break;
++ case NV_PFB_REF:
++ data = NV_PFB_REF_CMD_REFRESH;
++ break;
++ default:
++ data = ROM32(bios->data[meminitdata]);
++ meminitdata += 4;
++ if (data == 0xffffffff)
++ continue;
++ }
++
++ bios_wr32(bios, reg, data);
++ }
++
++ return 1;
++}
++
++static int
++init_configure_clk(struct nvbios *bios, uint16_t offset,
++ struct init_exec *iexec)
++{
++ /*
++ * INIT_CONFIGURE_CLK opcode: 0x67 ('g')
++ *
++ * offset (8 bit): opcode
++ *
++ * Equivalent to INIT_DONE on bios version 3 or greater.
++ * For early bios versions, sets up the NVClk and MClk PLLs, using
++ * values taken from the memory init table
++ */
++
++ /* no iexec->execute check by design */
++
++ uint16_t meminitoffs = bios->legacy.mem_init_tbl_ptr + MEM_INIT_SIZE * (bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_SCRATCH4__INDEX) >> 4);
++ int clock;
++
++ if (bios->major_version > 2)
++ return 0;
++
++ clock = ROM16(bios->data[meminitoffs + 4]) * 10;
++ setPLL(bios, NV_PRAMDAC_NVPLL_COEFF, clock);
++
++ clock = ROM16(bios->data[meminitoffs + 2]) * 10;
++ if (bios->data[meminitoffs] & 1) /* DDR */
++ clock *= 2;
++ setPLL(bios, NV_PRAMDAC_MPLL_COEFF, clock);
++
++ return 1;
++}
++
++static int
++init_configure_preinit(struct nvbios *bios, uint16_t offset,
++ struct init_exec *iexec)
++{
++ /*
++ * INIT_CONFIGURE_PREINIT opcode: 0x68 ('h')
++ *
++ * offset (8 bit): opcode
++ *
++ * Equivalent to INIT_DONE on bios version 3 or greater.
++ * For early bios versions, does early init, loading ram and crystal
++ * configuration from straps into CR3C
++ */
++
++ /* no iexec->execute check by design */
++
++ uint32_t straps = bios_rd32(bios, NV_PEXTDEV_BOOT_0);
++ uint8_t cr3c = ((straps << 2) & 0xf0) | (straps & (1 << 6));
++
++ if (bios->major_version > 2)
++ return 0;
++
++ bios_idxprt_wr(bios, NV_CIO_CRX__COLOR,
++ NV_CIO_CRE_SCRATCH4__INDEX, cr3c);
++
++ return 1;
++}
++
++static int
++init_io(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_IO opcode: 0x69 ('i')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (16 bit): CRTC port
++ * offset + 3 (8 bit): mask
++ * offset + 4 (8 bit): data
++ *
++ * Assign ((IOVAL("crtc port") & "mask") | "data") to "crtc port"
++ */
++
++ struct drm_nouveau_private *dev_priv = bios->dev->dev_private;
++ uint16_t crtcport = ROM16(bios->data[offset + 1]);
++ uint8_t mask = bios->data[offset + 3];
++ uint8_t data = bios->data[offset + 4];
++
++ if (!iexec->execute)
++ return 5;
++
++ BIOSLOG(bios, "0x%04X: Port: 0x%04X, Mask: 0x%02X, Data: 0x%02X\n",
++ offset, crtcport, mask, data);
++
++ /*
++ * I have no idea what this does, but NVIDIA do this magic sequence
++ * in the places where this INIT_IO happens..
++ */
++ if (dev_priv->card_type >= NV_50 && crtcport == 0x3c3 && data == 1) {
++ int i;
++
++ bios_wr32(bios, 0x614100, (bios_rd32(
++ bios, 0x614100) & 0x0fffffff) | 0x00800000);
++
++ bios_wr32(bios, 0x00e18c, bios_rd32(
++ bios, 0x00e18c) | 0x00020000);
++
++ bios_wr32(bios, 0x614900, (bios_rd32(
++ bios, 0x614900) & 0x0fffffff) | 0x00800000);
++
++ bios_wr32(bios, 0x000200, bios_rd32(
++ bios, 0x000200) & ~0x40000000);
++
++ mdelay(10);
++
++ bios_wr32(bios, 0x00e18c, bios_rd32(
++ bios, 0x00e18c) & ~0x00020000);
++
++ bios_wr32(bios, 0x000200, bios_rd32(
++ bios, 0x000200) | 0x40000000);
++
++ bios_wr32(bios, 0x614100, 0x00800018);
++ bios_wr32(bios, 0x614900, 0x00800018);
++
++ mdelay(10);
++
++ bios_wr32(bios, 0x614100, 0x10000018);
++ bios_wr32(bios, 0x614900, 0x10000018);
++
++ for (i = 0; i < 3; i++)
++ bios_wr32(bios, 0x614280 + (i*0x800), bios_rd32(
++ bios, 0x614280 + (i*0x800)) & 0xf0f0f0f0);
++
++ for (i = 0; i < 2; i++)
++ bios_wr32(bios, 0x614300 + (i*0x800), bios_rd32(
++ bios, 0x614300 + (i*0x800)) & 0xfffff0f0);
++
++ for (i = 0; i < 3; i++)
++ bios_wr32(bios, 0x614380 + (i*0x800), bios_rd32(
++ bios, 0x614380 + (i*0x800)) & 0xfffff0f0);
++
++ for (i = 0; i < 2; i++)
++ bios_wr32(bios, 0x614200 + (i*0x800), bios_rd32(
++ bios, 0x614200 + (i*0x800)) & 0xfffffff0);
++
++ for (i = 0; i < 2; i++)
++ bios_wr32(bios, 0x614108 + (i*0x800), bios_rd32(
++ bios, 0x614108 + (i*0x800)) & 0x0fffffff);
++ return 5;
++ }
++
++ bios_port_wr(bios, crtcport, (bios_port_rd(bios, crtcport) & mask) |
++ data);
++ return 5;
++}
++
++static int
++init_sub(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_SUB opcode: 0x6B ('k')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): script number
++ *
++ * Execute script number "script number", as a subroutine
++ */
++
++ uint8_t sub = bios->data[offset + 1];
++
++ if (!iexec->execute)
++ return 2;
++
++ BIOSLOG(bios, "0x%04X: Calling script %d\n", offset, sub);
++
++ parse_init_table(bios,
++ ROM16(bios->data[bios->init_script_tbls_ptr + sub * 2]),
++ iexec);
++
++ BIOSLOG(bios, "0x%04X: End of script %d\n", offset, sub);
++
++ return 2;
++}
++
++static int
++init_ram_condition(struct nvbios *bios, uint16_t offset,
++ struct init_exec *iexec)
++{
++ /*
++ * INIT_RAM_CONDITION opcode: 0x6D ('m')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): mask
++ * offset + 2 (8 bit): cmpval
++ *
++ * Test if (NV_PFB_BOOT_0 & "mask") equals "cmpval".
++ * If condition not met skip subsequent opcodes until condition is
++ * inverted (INIT_NOT), or we hit INIT_RESUME
++ */
++
++ uint8_t mask = bios->data[offset + 1];
++ uint8_t cmpval = bios->data[offset + 2];
++ uint8_t data;
++
++ if (!iexec->execute)
++ return 3;
++
++ data = bios_rd32(bios, NV_PFB_BOOT_0) & mask;
++
++ BIOSLOG(bios, "0x%04X: Checking if 0x%08X equals 0x%08X\n",
++ offset, data, cmpval);
++
++ if (data == cmpval)
++ BIOSLOG(bios, "0x%04X: Condition fulfilled -- continuing to execute\n", offset);
++ else {
++ BIOSLOG(bios, "0x%04X: Condition not fulfilled -- skipping following commands\n", offset);
++ iexec->execute = false;
++ }
++
++ return 3;
++}
++
++static int
++init_nv_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_NV_REG opcode: 0x6E ('n')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (32 bit): register
++ * offset + 5 (32 bit): mask
++ * offset + 9 (32 bit): data
++ *
++ * Assign ((REGVAL("register") & "mask") | "data") to "register"
++ */
++
++ uint32_t reg = ROM32(bios->data[offset + 1]);
++ uint32_t mask = ROM32(bios->data[offset + 5]);
++ uint32_t data = ROM32(bios->data[offset + 9]);
++
++ if (!iexec->execute)
++ return 13;
++
++ BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Mask: 0x%08X, Data: 0x%08X\n",
++ offset, reg, mask, data);
++
++ bios_wr32(bios, reg, (bios_rd32(bios, reg) & mask) | data);
++
++ return 13;
++}
++
++static int
++init_macro(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_MACRO opcode: 0x6F ('o')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): macro number
++ *
++ * Look up macro index "macro number" in the macro index table.
++ * The macro index table entry has 1 byte for the index in the macro
++ * table, and 1 byte for the number of times to repeat the macro.
++ * The macro table entry has 4 bytes for the register address and
++ * 4 bytes for the value to write to that register
++ */
++
++ uint8_t macro_index_tbl_idx = bios->data[offset + 1];
++ uint16_t tmp = bios->macro_index_tbl_ptr + (macro_index_tbl_idx * MACRO_INDEX_SIZE);
++ uint8_t macro_tbl_idx = bios->data[tmp];
++ uint8_t count = bios->data[tmp + 1];
++ uint32_t reg, data;
++ int i;
++
++ if (!iexec->execute)
++ return 2;
++
++ BIOSLOG(bios, "0x%04X: Macro: 0x%02X, MacroTableIndex: 0x%02X, "
++ "Count: 0x%02X\n",
++ offset, macro_index_tbl_idx, macro_tbl_idx, count);
++
++ for (i = 0; i < count; i++) {
++ uint16_t macroentryptr = bios->macro_tbl_ptr + (macro_tbl_idx + i) * MACRO_SIZE;
++
++ reg = ROM32(bios->data[macroentryptr]);
++ data = ROM32(bios->data[macroentryptr + 4]);
++
++ bios_wr32(bios, reg, data);
++ }
++
++ return 2;
++}
++
++static int
++init_done(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_DONE opcode: 0x71 ('q')
++ *
++ * offset (8 bit): opcode
++ *
++ * End the current script
++ */
++
++ /* mild retval abuse to stop parsing this table */
++ return 0;
++}
++
++static int
++init_resume(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_RESUME opcode: 0x72 ('r')
++ *
++ * offset (8 bit): opcode
++ *
++ * End the current execute / no-execute condition
++ */
++
++ if (iexec->execute)
++ return 1;
++
++ iexec->execute = true;
++ BIOSLOG(bios, "0x%04X: ---- Executing following commands ----\n", offset);
++
++ return 1;
++}
++
++static int
++init_time(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_TIME opcode: 0x74 ('t')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (16 bit): time
++ *
++ * Sleep for "time" microseconds.
++ */
++
++ unsigned time = ROM16(bios->data[offset + 1]);
++
++ if (!iexec->execute)
++ return 3;
++
++ BIOSLOG(bios, "0x%04X: Sleeping for 0x%04X microseconds\n",
++ offset, time);
++
++ if (time < 1000)
++ udelay(time);
++ else
++ msleep((time + 900) / 1000);
++
++ return 3;
++}
++
++static int
++init_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_CONDITION opcode: 0x75 ('u')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): condition number
++ *
++ * Check condition "condition number" in the condition table.
++ * If condition not met skip subsequent opcodes until condition is
++ * inverted (INIT_NOT), or we hit INIT_RESUME
++ */
++
++ uint8_t cond = bios->data[offset + 1];
++
++ if (!iexec->execute)
++ return 2;
++
++ BIOSLOG(bios, "0x%04X: Condition: 0x%02X\n", offset, cond);
++
++ if (bios_condition_met(bios, offset, cond))
++ BIOSLOG(bios, "0x%04X: Condition fulfilled -- continuing to execute\n", offset);
++ else {
++ BIOSLOG(bios, "0x%04X: Condition not fulfilled -- skipping following commands\n", offset);
++ iexec->execute = false;
++ }
++
++ return 2;
++}
++
++static int
++init_io_condition(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_IO_CONDITION opcode: 0x76
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): condition number
++ *
++ * Check condition "condition number" in the io condition table.
++ * If condition not met skip subsequent opcodes until condition is
++ * inverted (INIT_NOT), or we hit INIT_RESUME
++ */
++
++ uint8_t cond = bios->data[offset + 1];
++
++ if (!iexec->execute)
++ return 2;
++
++ BIOSLOG(bios, "0x%04X: IO condition: 0x%02X\n", offset, cond);
++
++ if (io_condition_met(bios, offset, cond))
++ BIOSLOG(bios, "0x%04X: Condition fulfilled -- continuing to execute\n", offset);
++ else {
++ BIOSLOG(bios, "0x%04X: Condition not fulfilled -- skipping following commands\n", offset);
++ iexec->execute = false;
++ }
++
++ return 2;
++}
++
++static int
++init_index_io(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_INDEX_IO opcode: 0x78 ('x')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (16 bit): CRTC port
++ * offset + 3 (8 bit): CRTC index
++ * offset + 4 (8 bit): mask
++ * offset + 5 (8 bit): data
++ *
++ * Read value at index "CRTC index" on "CRTC port", AND with "mask",
++ * OR with "data", write-back
++ */
++
++ uint16_t crtcport = ROM16(bios->data[offset + 1]);
++ uint8_t crtcindex = bios->data[offset + 3];
++ uint8_t mask = bios->data[offset + 4];
++ uint8_t data = bios->data[offset + 5];
++ uint8_t value;
++
++ if (!iexec->execute)
++ return 6;
++
++ BIOSLOG(bios, "0x%04X: Port: 0x%04X, Index: 0x%02X, Mask: 0x%02X, "
++ "Data: 0x%02X\n",
++ offset, crtcport, crtcindex, mask, data);
++
++ value = (bios_idxprt_rd(bios, crtcport, crtcindex) & mask) | data;
++ bios_idxprt_wr(bios, crtcport, crtcindex, value);
++
++ return 6;
++}
++
++static int
++init_pll(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_PLL opcode: 0x79 ('y')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (32 bit): register
++ * offset + 5 (16 bit): freq
++ *
++ * Set PLL register "register" to coefficients for frequency (10kHz)
++ * "freq"
++ */
++
++ uint32_t reg = ROM32(bios->data[offset + 1]);
++ uint16_t freq = ROM16(bios->data[offset + 5]);
++
++ if (!iexec->execute)
++ return 7;
++
++ BIOSLOG(bios, "0x%04X: Reg: 0x%08X, Freq: %d0kHz\n", offset, reg, freq);
++
++ setPLL(bios, reg, freq * 10);
++
++ return 7;
++}
++
++static int
++init_zm_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_ZM_REG opcode: 0x7A ('z')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (32 bit): register
++ * offset + 5 (32 bit): value
++ *
++ * Assign "value" to "register"
++ */
++
++ uint32_t reg = ROM32(bios->data[offset + 1]);
++ uint32_t value = ROM32(bios->data[offset + 5]);
++
++ if (!iexec->execute)
++ return 9;
++
++ if (reg == 0x000200)
++ value |= 1;
++
++ bios_wr32(bios, reg, value);
++
++ return 9;
++}
++
++static int
++init_ram_restrict_pll(struct nvbios *bios, uint16_t offset,
++ struct init_exec *iexec)
++{
++ /*
++ * INIT_RAM_RESTRICT_PLL opcode: 0x87 ('')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (8 bit): PLL type
++ * offset + 2 (32 bit): frequency 0
++ *
++ * Uses the RAMCFG strap of PEXTDEV_BOOT as an index into the table at
++ * ram_restrict_table_ptr. The value read from there is used to select
++ * a frequency from the table starting at 'frequency 0' to be
++ * programmed into the PLL corresponding to 'type'.
++ *
++ * The PLL limits table on cards using this opcode has a mapping of
++ * 'type' to the relevant registers.
++ */
++
++ struct drm_device *dev = bios->dev;
++ uint32_t strap = (bios_rd32(bios, NV_PEXTDEV_BOOT_0) & 0x0000003c) >> 2;
++ uint8_t index = bios->data[bios->ram_restrict_tbl_ptr + strap];
++ uint8_t type = bios->data[offset + 1];
++ uint32_t freq = ROM32(bios->data[offset + 2 + (index * 4)]);
++ uint8_t *pll_limits = &bios->data[bios->pll_limit_tbl_ptr], *entry;
++ int len = 2 + bios->ram_restrict_group_count * 4;
++ int i;
++
++ if (!iexec->execute)
++ return len;
++
++ if (!bios->pll_limit_tbl_ptr || (pll_limits[0] & 0xf0) != 0x30) {
++ NV_ERROR(dev, "PLL limits table not version 3.x\n");
++ return len; /* deliberate, allow default clocks to remain */
++ }
++
++ entry = pll_limits + pll_limits[1];
++ for (i = 0; i < pll_limits[3]; i++, entry += pll_limits[2]) {
++ if (entry[0] == type) {
++ uint32_t reg = ROM32(entry[3]);
++
++ BIOSLOG(bios, "0x%04X: "
++ "Type %02x Reg 0x%08x Freq %dKHz\n",
++ offset, type, reg, freq);
++
++ setPLL(bios, reg, freq);
++ return len;
++ }
++ }
++
++ NV_ERROR(dev, "PLL type 0x%02x not found in PLL limits table", type);
++ return len;
++}
++
++static int
++init_8c(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_8C opcode: 0x8C ('')
++ *
++ * NOP so far....
++ *
++ */
++
++ return 1;
++}
++
++static int
++init_8d(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_8D opcode: 0x8D ('')
++ *
++ * NOP so far....
++ *
++ */
++
++ return 1;
++}
++
++static int
++init_gpio(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_GPIO opcode: 0x8E ('')
++ *
++ * offset (8 bit): opcode
++ *
++ * Loop over all entries in the DCB GPIO table, and initialise
++ * each GPIO according to various values listed in each entry
++ */
++
++ const uint32_t nv50_gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 };
++ const uint32_t nv50_gpio_ctl[2] = { 0xe100, 0xe28c };
++ const uint8_t *gpio_table = &bios->data[bios->bdcb.gpio_table_ptr];
++ const uint8_t *gpio_entry;
++ int i;
++
++ if (!iexec->execute)
++ return 1;
++
++ if (bios->bdcb.version != 0x40) {
++ NV_ERROR(bios->dev, "DCB table not version 4.0\n");
++ return 0;
++ }
++
++ if (!bios->bdcb.gpio_table_ptr) {
++ NV_WARN(bios->dev, "Invalid pointer to INIT_8E table\n");
++ return 0;
++ }
++
++ gpio_entry = gpio_table + gpio_table[1];
++ for (i = 0; i < gpio_table[2]; i++, gpio_entry += gpio_table[3]) {
++ uint32_t entry = ROM32(gpio_entry[0]), r, s, v;
++ int line = (entry & 0x0000001f);
++
++ BIOSLOG(bios, "0x%04X: Entry: 0x%08X\n", offset, entry);
++
++ if ((entry & 0x0000ff00) == 0x0000ff00)
++ continue;
++
++ r = nv50_gpio_reg[line >> 3];
++ s = (line & 0x07) << 2;
++ v = bios_rd32(bios, r) & ~(0x00000003 << s);
++ if (entry & 0x01000000)
++ v |= (((entry & 0x60000000) >> 29) ^ 2) << s;
++ else
++ v |= (((entry & 0x18000000) >> 27) ^ 2) << s;
++ bios_wr32(bios, r, v);
++
++ r = nv50_gpio_ctl[line >> 4];
++ s = (line & 0x0f);
++ v = bios_rd32(bios, r) & ~(0x00010001 << s);
++ switch ((entry & 0x06000000) >> 25) {
++ case 1:
++ v |= (0x00000001 << s);
++ break;
++ case 2:
++ v |= (0x00010000 << s);
++ break;
++ default:
++ break;
++ }
++ bios_wr32(bios, r, v);
++ }
++
++ return 1;
++}
++
++static int
++init_ram_restrict_zm_reg_group(struct nvbios *bios, uint16_t offset,
++ struct init_exec *iexec)
++{
++ /*
++ * INIT_RAM_RESTRICT_ZM_REG_GROUP opcode: 0x8F ('')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (32 bit): reg
++ * offset + 5 (8 bit): regincrement
++ * offset + 6 (8 bit): count
++ * offset + 7 (32 bit): value 1,1
++ * ...
++ *
++ * Use the RAMCFG strap of PEXTDEV_BOOT as an index into the table at
++ * ram_restrict_table_ptr. The value read from here is 'n', and
++ * "value 1,n" gets written to "reg". This repeats "count" times and on
++ * each iteration 'm', "reg" increases by "regincrement" and
++ * "value m,n" is used. The extent of n is limited by a number read
++ * from the 'M' BIT table, herein called "blocklen"
++ */
++
++ uint32_t reg = ROM32(bios->data[offset + 1]);
++ uint8_t regincrement = bios->data[offset + 5];
++ uint8_t count = bios->data[offset + 6];
++ uint32_t strap_ramcfg, data;
++ /* previously set by 'M' BIT table */
++ uint16_t blocklen = bios->ram_restrict_group_count * 4;
++ int len = 7 + count * blocklen;
++ uint8_t index;
++ int i;
++
++
++ if (!iexec->execute)
++ return len;
++
++ if (!blocklen) {
++ NV_ERROR(bios->dev,
++ "0x%04X: Zero block length - has the M table "
++ "been parsed?\n", offset);
++ return 0;
++ }
++
++ strap_ramcfg = (bios_rd32(bios, NV_PEXTDEV_BOOT_0) >> 2) & 0xf;
++ index = bios->data[bios->ram_restrict_tbl_ptr + strap_ramcfg];
++
++ BIOSLOG(bios, "0x%04X: Reg: 0x%08X, RegIncrement: 0x%02X, "
++ "Count: 0x%02X, StrapRamCfg: 0x%02X, Index: 0x%02X\n",
++ offset, reg, regincrement, count, strap_ramcfg, index);
++
++ for (i = 0; i < count; i++) {
++ data = ROM32(bios->data[offset + 7 + index * 4 + blocklen * i]);
++
++ bios_wr32(bios, reg, data);
++
++ reg += regincrement;
++ }
++
++ return len;
++}
++
++static int
++init_copy_zm_reg(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_COPY_ZM_REG opcode: 0x90 ('')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (32 bit): src reg
++ * offset + 5 (32 bit): dst reg
++ *
++ * Put contents of "src reg" into "dst reg"
++ */
++
++ uint32_t srcreg = ROM32(bios->data[offset + 1]);
++ uint32_t dstreg = ROM32(bios->data[offset + 5]);
++
++ if (!iexec->execute)
++ return 9;
++
++ bios_wr32(bios, dstreg, bios_rd32(bios, srcreg));
++
++ return 9;
++}
++
++static int
++init_zm_reg_group_addr_latched(struct nvbios *bios, uint16_t offset,
++ struct init_exec *iexec)
++{
++ /*
++ * INIT_ZM_REG_GROUP_ADDRESS_LATCHED opcode: 0x91 ('')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (32 bit): dst reg
++ * offset + 5 (8 bit): count
++ * offset + 6 (32 bit): data 1
++ * ...
++ *
++ * For each of "count" values write "data n" to "dst reg"
++ */
++
++ uint32_t reg = ROM32(bios->data[offset + 1]);
++ uint8_t count = bios->data[offset + 5];
++ int len = 6 + count * 4;
++ int i;
++
++ if (!iexec->execute)
++ return len;
++
++ for (i = 0; i < count; i++) {
++ uint32_t data = ROM32(bios->data[offset + 6 + 4 * i]);
++ bios_wr32(bios, reg, data);
++ }
++
++ return len;
++}
++
++static int
++init_reserved(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_RESERVED opcode: 0x92 ('')
++ *
++ * offset (8 bit): opcode
++ *
++ * Seemingly does nothing
++ */
++
++ return 1;
++}
++
++static int
++init_96(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_96 opcode: 0x96 ('')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (32 bit): sreg
++ * offset + 5 (8 bit): sshift
++ * offset + 6 (8 bit): smask
++ * offset + 7 (8 bit): index
++ * offset + 8 (32 bit): reg
++ * offset + 12 (32 bit): mask
++ * offset + 16 (8 bit): shift
++ *
++ */
++
++ uint16_t xlatptr = bios->init96_tbl_ptr + (bios->data[offset + 7] * 2);
++ uint32_t reg = ROM32(bios->data[offset + 8]);
++ uint32_t mask = ROM32(bios->data[offset + 12]);
++ uint32_t val;
++
++ val = bios_rd32(bios, ROM32(bios->data[offset + 1]));
++ if (bios->data[offset + 5] < 0x80)
++ val >>= bios->data[offset + 5];
++ else
++ val <<= (0x100 - bios->data[offset + 5]);
++ val &= bios->data[offset + 6];
++
++ val = bios->data[ROM16(bios->data[xlatptr]) + val];
++ val <<= bios->data[offset + 16];
++
++ if (!iexec->execute)
++ return 17;
++
++ bios_wr32(bios, reg, (bios_rd32(bios, reg) & mask) | val);
++ return 17;
++}
++
++static int
++init_97(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_97 opcode: 0x97 ('')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (32 bit): register
++ * offset + 5 (32 bit): mask
++ * offset + 9 (32 bit): value
++ *
++ * Adds "value" to "register" preserving the fields specified
++ * by "mask"
++ */
++
++ uint32_t reg = ROM32(bios->data[offset + 1]);
++ uint32_t mask = ROM32(bios->data[offset + 5]);
++ uint32_t add = ROM32(bios->data[offset + 9]);
++ uint32_t val;
++
++ val = bios_rd32(bios, reg);
++ val = (val & mask) | ((val + add) & ~mask);
++
++ if (!iexec->execute)
++ return 13;
++
++ bios_wr32(bios, reg, val);
++ return 13;
++}
++
++static int
++init_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_AUXCH opcode: 0x98 ('')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (32 bit): address
++ * offset + 5 (8 bit): count
++ * offset + 6 (8 bit): mask 0
++ * offset + 7 (8 bit): data 0
++ * ...
++ *
++ */
++
++ struct drm_device *dev = bios->dev;
++ struct nouveau_i2c_chan *auxch;
++ uint32_t addr = ROM32(bios->data[offset + 1]);
++ uint8_t count = bios->data[offset + 5];
++ int len = 6 + count * 2;
++ int ret, i;
++
++ if (!bios->display.output) {
++ NV_ERROR(dev, "INIT_AUXCH: no active output\n");
++ return 0;
++ }
++
++ auxch = init_i2c_device_find(dev, bios->display.output->i2c_index);
++ if (!auxch) {
++ NV_ERROR(dev, "INIT_AUXCH: couldn't get auxch %d\n",
++ bios->display.output->i2c_index);
++ return 0;
++ }
++
++ if (!iexec->execute)
++ return len;
++
++ offset += 6;
++ for (i = 0; i < count; i++, offset += 2) {
++ uint8_t data;
++
++ ret = nouveau_dp_auxch(auxch, 9, addr, &data, 1);
++ if (ret) {
++ NV_ERROR(dev, "INIT_AUXCH: rd auxch fail %d\n", ret);
++ return 0;
++ }
++
++ data &= bios->data[offset + 0];
++ data |= bios->data[offset + 1];
++
++ ret = nouveau_dp_auxch(auxch, 8, addr, &data, 1);
++ if (ret) {
++ NV_ERROR(dev, "INIT_AUXCH: wr auxch fail %d\n", ret);
++ return 0;
++ }
++ }
++
++ return len;
++}
++
++static int
++init_zm_auxch(struct nvbios *bios, uint16_t offset, struct init_exec *iexec)
++{
++ /*
++ * INIT_ZM_AUXCH opcode: 0x99 ('')
++ *
++ * offset (8 bit): opcode
++ * offset + 1 (32 bit): address
++ * offset + 5 (8 bit): count
++ * offset + 6 (8 bit): data 0
++ * ...
++ *
++ */
++
++ struct drm_device *dev = bios->dev;
++ struct nouveau_i2c_chan *auxch;
++ uint32_t addr = ROM32(bios->data[offset + 1]);
++ uint8_t count = bios->data[offset + 5];
++ int len = 6 + count;
++ int ret, i;
++
++ if (!bios->display.output) {
++ NV_ERROR(dev, "INIT_ZM_AUXCH: no active output\n");
++ return 0;
++ }
++
++ auxch = init_i2c_device_find(dev, bios->display.output->i2c_index);
++ if (!auxch) {
++ NV_ERROR(dev, "INIT_ZM_AUXCH: couldn't get auxch %d\n",
++ bios->display.output->i2c_index);
++ return 0;
++ }
++
++ if (!iexec->execute)
++ return len;
++
++ offset += 6;
++ for (i = 0; i < count; i++, offset++) {
++ ret = nouveau_dp_auxch(auxch, 8, addr, &bios->data[offset], 1);
++ if (ret) {
++ NV_ERROR(dev, "INIT_ZM_AUXCH: wr auxch fail %d\n", ret);
++ return 0;
++ }
++ }
++
++ return len;
++}
++
++static struct init_tbl_entry itbl_entry[] = {
++ /* command name , id , length , offset , mult , command handler */
++ /* INIT_PROG (0x31, 15, 10, 4) removed due to no example of use */
++ { "INIT_IO_RESTRICT_PROG" , 0x32, init_io_restrict_prog },
++ { "INIT_REPEAT" , 0x33, init_repeat },
++ { "INIT_IO_RESTRICT_PLL" , 0x34, init_io_restrict_pll },
++ { "INIT_END_REPEAT" , 0x36, init_end_repeat },
++ { "INIT_COPY" , 0x37, init_copy },
++ { "INIT_NOT" , 0x38, init_not },
++ { "INIT_IO_FLAG_CONDITION" , 0x39, init_io_flag_condition },
++ { "INIT_INDEX_ADDRESS_LATCHED" , 0x49, init_idx_addr_latched },
++ { "INIT_IO_RESTRICT_PLL2" , 0x4A, init_io_restrict_pll2 },
++ { "INIT_PLL2" , 0x4B, init_pll2 },
++ { "INIT_I2C_BYTE" , 0x4C, init_i2c_byte },
++ { "INIT_ZM_I2C_BYTE" , 0x4D, init_zm_i2c_byte },
++ { "INIT_ZM_I2C" , 0x4E, init_zm_i2c },
++ { "INIT_TMDS" , 0x4F, init_tmds },
++ { "INIT_ZM_TMDS_GROUP" , 0x50, init_zm_tmds_group },
++ { "INIT_CR_INDEX_ADDRESS_LATCHED" , 0x51, init_cr_idx_adr_latch },
++ { "INIT_CR" , 0x52, init_cr },
++ { "INIT_ZM_CR" , 0x53, init_zm_cr },
++ { "INIT_ZM_CR_GROUP" , 0x54, init_zm_cr_group },
++ { "INIT_CONDITION_TIME" , 0x56, init_condition_time },
++ { "INIT_ZM_REG_SEQUENCE" , 0x58, init_zm_reg_sequence },
++ /* INIT_INDIRECT_REG (0x5A, 7, 0, 0) removed due to no example of use */
++ { "INIT_SUB_DIRECT" , 0x5B, init_sub_direct },
++ { "INIT_COPY_NV_REG" , 0x5F, init_copy_nv_reg },
++ { "INIT_ZM_INDEX_IO" , 0x62, init_zm_index_io },
++ { "INIT_COMPUTE_MEM" , 0x63, init_compute_mem },
++ { "INIT_RESET" , 0x65, init_reset },
++ { "INIT_CONFIGURE_MEM" , 0x66, init_configure_mem },
++ { "INIT_CONFIGURE_CLK" , 0x67, init_configure_clk },
++ { "INIT_CONFIGURE_PREINIT" , 0x68, init_configure_preinit },
++ { "INIT_IO" , 0x69, init_io },
++ { "INIT_SUB" , 0x6B, init_sub },
++ { "INIT_RAM_CONDITION" , 0x6D, init_ram_condition },
++ { "INIT_NV_REG" , 0x6E, init_nv_reg },
++ { "INIT_MACRO" , 0x6F, init_macro },
++ { "INIT_DONE" , 0x71, init_done },
++ { "INIT_RESUME" , 0x72, init_resume },
++ /* INIT_RAM_CONDITION2 (0x73, 9, 0, 0) removed due to no example of use */
++ { "INIT_TIME" , 0x74, init_time },
++ { "INIT_CONDITION" , 0x75, init_condition },
++ { "INIT_IO_CONDITION" , 0x76, init_io_condition },
++ { "INIT_INDEX_IO" , 0x78, init_index_io },
++ { "INIT_PLL" , 0x79, init_pll },
++ { "INIT_ZM_REG" , 0x7A, init_zm_reg },
++ { "INIT_RAM_RESTRICT_PLL" , 0x87, init_ram_restrict_pll },
++ { "INIT_8C" , 0x8C, init_8c },
++ { "INIT_8D" , 0x8D, init_8d },
++ { "INIT_GPIO" , 0x8E, init_gpio },
++ { "INIT_RAM_RESTRICT_ZM_REG_GROUP" , 0x8F, init_ram_restrict_zm_reg_group },
++ { "INIT_COPY_ZM_REG" , 0x90, init_copy_zm_reg },
++ { "INIT_ZM_REG_GROUP_ADDRESS_LATCHED" , 0x91, init_zm_reg_group_addr_latched },
++ { "INIT_RESERVED" , 0x92, init_reserved },
++ { "INIT_96" , 0x96, init_96 },
++ { "INIT_97" , 0x97, init_97 },
++ { "INIT_AUXCH" , 0x98, init_auxch },
++ { "INIT_ZM_AUXCH" , 0x99, init_zm_auxch },
++ { NULL , 0 , NULL }
++};
++
++#define MAX_TABLE_OPS 1000
++
++static int
++parse_init_table(struct nvbios *bios, unsigned int offset,
++ struct init_exec *iexec)
++{
++ /*
++ * Parses all commands in an init table.
++ *
++ * We start out executing all commands found in the init table. Some
++ * opcodes may change the status of iexec->execute to SKIP, which will
++ * cause the following opcodes to perform no operation until the value
++ * is changed back to EXECUTE.
++ */
++
++ int count = 0, i, res;
++ uint8_t id;
++
++ /*
++ * Loop until INIT_DONE causes us to break out of the loop
++ * (or until offset > bios length just in case... )
++ * (and no more than MAX_TABLE_OPS iterations, just in case... )
++ */
++ while ((offset < bios->length) && (count++ < MAX_TABLE_OPS)) {
++ id = bios->data[offset];
++
++ /* Find matching id in itbl_entry */
++ for (i = 0; itbl_entry[i].name && (itbl_entry[i].id != id); i++)
++ ;
++
++ if (itbl_entry[i].name) {
++ BIOSLOG(bios, "0x%04X: [ (0x%02X) - %s ]\n",
++ offset, itbl_entry[i].id, itbl_entry[i].name);
++
++ /* execute eventual command handler */
++ res = (*itbl_entry[i].handler)(bios, offset, iexec);
++ if (!res)
++ break;
++ /*
++ * Add the offset of the current command including all data
++ * of that command. The offset will then be pointing on the
++ * next op code.
++ */
++ offset += res;
++ } else {
++ NV_ERROR(bios->dev,
++ "0x%04X: Init table command not found: "
++ "0x%02X\n", offset, id);
++ return -ENOENT;
++ }
++ }
++
++ if (offset >= bios->length)
++ NV_WARN(bios->dev,
++ "Offset 0x%04X greater than known bios image length. "
++ "Corrupt image?\n", offset);
++ if (count >= MAX_TABLE_OPS)
++ NV_WARN(bios->dev,
++ "More than %d opcodes to a table is unlikely, "
++ "is the bios image corrupt?\n", MAX_TABLE_OPS);
++
++ return 0;
++}
++
++static void
++parse_init_tables(struct nvbios *bios)
++{
++ /* Loops and calls parse_init_table() for each present table. */
++
++ int i = 0;
++ uint16_t table;
++ struct init_exec iexec = {true, false};
++
++ if (bios->old_style_init) {
++ if (bios->init_script_tbls_ptr)
++ parse_init_table(bios, bios->init_script_tbls_ptr, &iexec);
++ if (bios->extra_init_script_tbl_ptr)
++ parse_init_table(bios, bios->extra_init_script_tbl_ptr, &iexec);
++
++ return;
++ }
++
++ while ((table = ROM16(bios->data[bios->init_script_tbls_ptr + i]))) {
++ NV_INFO(bios->dev,
++ "Parsing VBIOS init table %d at offset 0x%04X\n",
++ i / 2, table);
++ BIOSLOG(bios, "0x%04X: ------ Executing following commands ------\n", table);
++
++ parse_init_table(bios, table, &iexec);
++ i += 2;
++ }
++}
++
++static uint16_t clkcmptable(struct nvbios *bios, uint16_t clktable, int pxclk)
++{
++ int compare_record_len, i = 0;
++ uint16_t compareclk, scriptptr = 0;
++
++ if (bios->major_version < 5) /* pre BIT */
++ compare_record_len = 3;
++ else
++ compare_record_len = 4;
++
++ do {
++ compareclk = ROM16(bios->data[clktable + compare_record_len * i]);
++ if (pxclk >= compareclk * 10) {
++ if (bios->major_version < 5) {
++ uint8_t tmdssub = bios->data[clktable + 2 + compare_record_len * i];
++ scriptptr = ROM16(bios->data[bios->init_script_tbls_ptr + tmdssub * 2]);
++ } else
++ scriptptr = ROM16(bios->data[clktable + 2 + compare_record_len * i]);
++ break;
++ }
++ i++;
++ } while (compareclk);
++
++ return scriptptr;
++}
++
++static void
++run_digital_op_script(struct drm_device *dev, uint16_t scriptptr,
++ struct dcb_entry *dcbent, int head, bool dl)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ struct init_exec iexec = {true, false};
++
++ NV_TRACE(dev, "0x%04X: Parsing digital output script table\n",
++ scriptptr);
++ bios_idxprt_wr(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_44,
++ head ? NV_CIO_CRE_44_HEADB : NV_CIO_CRE_44_HEADA);
++ /* note: if dcb entries have been merged, index may be misleading */
++ NVWriteVgaCrtc5758(dev, head, 0, dcbent->index);
++ parse_init_table(bios, scriptptr, &iexec);
++
++ nv04_dfp_bind_head(dev, dcbent, head, dl);
++}
++
++static int call_lvds_manufacturer_script(struct drm_device *dev, struct dcb_entry *dcbent, int head, enum LVDS_script script)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ uint8_t sub = bios->data[bios->fp.xlated_entry + script] + (bios->fp.link_c_increment && dcbent->or & OUTPUT_C ? 1 : 0);
++ uint16_t scriptofs = ROM16(bios->data[bios->init_script_tbls_ptr + sub * 2]);
++
++ if (!bios->fp.xlated_entry || !sub || !scriptofs)
++ return -EINVAL;
++
++ run_digital_op_script(dev, scriptofs, dcbent, head, bios->fp.dual_link);
++
++ if (script == LVDS_PANEL_OFF) {
++ /* off-on delay in ms */
++ msleep(ROM16(bios->data[bios->fp.xlated_entry + 7]));
++ }
++#ifdef __powerpc__
++ /* Powerbook specific quirks */
++ if ((dev->pci_device & 0xffff) == 0x0179 ||
++ (dev->pci_device & 0xffff) == 0x0189 ||
++ (dev->pci_device & 0xffff) == 0x0329) {
++ if (script == LVDS_RESET) {
++ nv_write_tmds(dev, dcbent->or, 0, 0x02, 0x72);
++
++ } else if (script == LVDS_PANEL_ON) {
++ bios_wr32(bios, NV_PBUS_DEBUG_DUALHEAD_CTL,
++ bios_rd32(bios, NV_PBUS_DEBUG_DUALHEAD_CTL)
++ | (1 << 31));
++ bios_wr32(bios, NV_PCRTC_GPIO_EXT,
++ bios_rd32(bios, NV_PCRTC_GPIO_EXT) | 1);
++
++ } else if (script == LVDS_PANEL_OFF) {
++ bios_wr32(bios, NV_PBUS_DEBUG_DUALHEAD_CTL,
++ bios_rd32(bios, NV_PBUS_DEBUG_DUALHEAD_CTL)
++ & ~(1 << 31));
++ bios_wr32(bios, NV_PCRTC_GPIO_EXT,
++ bios_rd32(bios, NV_PCRTC_GPIO_EXT) & ~3);
++ }
++ }
++#endif
++
++ return 0;
++}
++
++static int run_lvds_table(struct drm_device *dev, struct dcb_entry *dcbent, int head, enum LVDS_script script, int pxclk)
++{
++ /*
++ * The BIT LVDS table's header has the information to setup the
++ * necessary registers. Following the standard 4 byte header are:
++ * A bitmask byte and a dual-link transition pxclk value for use in
++ * selecting the init script when not using straps; 4 script pointers
++ * for panel power, selected by output and on/off; and 8 table pointers
++ * for panel init, the needed one determined by output, and bits in the
++ * conf byte. These tables are similar to the TMDS tables, consisting
++ * of a list of pxclks and script pointers.
++ */
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ unsigned int outputset = (dcbent->or == 4) ? 1 : 0;
++ uint16_t scriptptr = 0, clktable;
++ uint8_t clktableptr = 0;
++
++ /*
++ * For now we assume version 3.0 table - g80 support will need some
++ * changes
++ */
++
++ switch (script) {
++ case LVDS_INIT:
++ return -ENOSYS;
++ case LVDS_BACKLIGHT_ON:
++ case LVDS_PANEL_ON:
++ scriptptr = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 7 + outputset * 2]);
++ break;
++ case LVDS_BACKLIGHT_OFF:
++ case LVDS_PANEL_OFF:
++ scriptptr = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 11 + outputset * 2]);
++ break;
++ case LVDS_RESET:
++ if (dcbent->lvdsconf.use_straps_for_mode) {
++ if (bios->fp.dual_link)
++ clktableptr += 2;
++ if (bios->fp.BITbit1)
++ clktableptr++;
++ } else {
++ /* using EDID */
++ uint8_t fallback = bios->data[bios->fp.lvdsmanufacturerpointer + 4];
++ int fallbackcmpval = (dcbent->or == 4) ? 4 : 1;
++
++ if (bios->fp.dual_link) {
++ clktableptr += 2;
++ fallbackcmpval *= 2;
++ }
++ if (fallbackcmpval & fallback)
++ clktableptr++;
++ }
++
++ /* adding outputset * 8 may not be correct */
++ clktable = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 15 + clktableptr * 2 + outputset * 8]);
++ if (!clktable) {
++ NV_ERROR(dev, "Pixel clock comparison table not found\n");
++ return -ENOENT;
++ }
++ scriptptr = clkcmptable(bios, clktable, pxclk);
++ }
++
++ if (!scriptptr) {
++ NV_ERROR(dev, "LVDS output init script not found\n");
++ return -ENOENT;
++ }
++ run_digital_op_script(dev, scriptptr, dcbent, head, bios->fp.dual_link);
++
++ return 0;
++}
++
++int call_lvds_script(struct drm_device *dev, struct dcb_entry *dcbent, int head, enum LVDS_script script, int pxclk)
++{
++ /*
++ * LVDS operations are multiplexed in an effort to present a single API
++ * which works with two vastly differing underlying structures.
++ * This acts as the demux
++ */
++
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ uint8_t lvds_ver = bios->data[bios->fp.lvdsmanufacturerpointer];
++ uint32_t sel_clk_binding, sel_clk;
++ int ret;
++
++ if (bios->fp.last_script_invoc == (script << 1 | head) || !lvds_ver ||
++ (lvds_ver >= 0x30 && script == LVDS_INIT))
++ return 0;
++
++ if (!bios->fp.lvds_init_run) {
++ bios->fp.lvds_init_run = true;
++ call_lvds_script(dev, dcbent, head, LVDS_INIT, pxclk);
++ }
++
++ if (script == LVDS_PANEL_ON && bios->fp.reset_after_pclk_change)
++ call_lvds_script(dev, dcbent, head, LVDS_RESET, pxclk);
++ if (script == LVDS_RESET && bios->fp.power_off_for_reset)
++ call_lvds_script(dev, dcbent, head, LVDS_PANEL_OFF, pxclk);
++
++ NV_TRACE(dev, "Calling LVDS script %d:\n", script);
++
++ /* don't let script change pll->head binding */
++ sel_clk_binding = bios_rd32(bios, NV_PRAMDAC_SEL_CLK) & 0x50000;
++
++ if (lvds_ver < 0x30)
++ ret = call_lvds_manufacturer_script(dev, dcbent, head, script);
++ else
++ ret = run_lvds_table(dev, dcbent, head, script, pxclk);
++
++ bios->fp.last_script_invoc = (script << 1 | head);
++
++ sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK) & ~0x50000;
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, sel_clk | sel_clk_binding);
++ /* some scripts set a value in NV_PBUS_POWERCTRL_2 and break video overlay */
++ nvWriteMC(dev, NV_PBUS_POWERCTRL_2, 0);
++
++ return ret;
++}
++
++struct lvdstableheader {
++ uint8_t lvds_ver, headerlen, recordlen;
++};
++
++static int parse_lvds_manufacturer_table_header(struct drm_device *dev, struct nvbios *bios, struct lvdstableheader *lth)
++{
++ /*
++ * BMP version (0xa) LVDS table has a simple header of version and
++ * record length. The BIT LVDS table has the typical BIT table header:
++ * version byte, header length byte, record length byte, and a byte for
++ * the maximum number of records that can be held in the table.
++ */
++
++ uint8_t lvds_ver, headerlen, recordlen;
++
++ memset(lth, 0, sizeof(struct lvdstableheader));
++
++ if (bios->fp.lvdsmanufacturerpointer == 0x0) {
++ NV_ERROR(dev, "Pointer to LVDS manufacturer table invalid\n");
++ return -EINVAL;
++ }
++
++ lvds_ver = bios->data[bios->fp.lvdsmanufacturerpointer];
++
++ switch (lvds_ver) {
++ case 0x0a: /* pre NV40 */
++ headerlen = 2;
++ recordlen = bios->data[bios->fp.lvdsmanufacturerpointer + 1];
++ break;
++ case 0x30: /* NV4x */
++ headerlen = bios->data[bios->fp.lvdsmanufacturerpointer + 1];
++ if (headerlen < 0x1f) {
++ NV_ERROR(dev, "LVDS table header not understood\n");
++ return -EINVAL;
++ }
++ recordlen = bios->data[bios->fp.lvdsmanufacturerpointer + 2];
++ break;
++ case 0x40: /* G80/G90 */
++ headerlen = bios->data[bios->fp.lvdsmanufacturerpointer + 1];
++ if (headerlen < 0x7) {
++ NV_ERROR(dev, "LVDS table header not understood\n");
++ return -EINVAL;
++ }
++ recordlen = bios->data[bios->fp.lvdsmanufacturerpointer + 2];
++ break;
++ default:
++ NV_ERROR(dev,
++ "LVDS table revision %d.%d not currently supported\n",
++ lvds_ver >> 4, lvds_ver & 0xf);
++ return -ENOSYS;
++ }
++
++ lth->lvds_ver = lvds_ver;
++ lth->headerlen = headerlen;
++ lth->recordlen = recordlen;
++
++ return 0;
++}
++
++static int
++get_fp_strap(struct drm_device *dev, struct nvbios *bios)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ /*
++ * The fp strap is normally dictated by the "User Strap" in
++ * PEXTDEV_BOOT_0[20:16], but on BMP cards when bit 2 of the
++ * Internal_Flags struct at 0x48 is set, the user strap gets overriden
++ * by the PCI subsystem ID during POST, but not before the previous user
++ * strap has been committed to CR58 for CR57=0xf on head A, which may be
++ * read and used instead
++ */
++
++ if (bios->major_version < 5 && bios->data[0x48] & 0x4)
++ return NVReadVgaCrtc5758(dev, 0, 0xf) & 0xf;
++
++ if (dev_priv->card_type >= NV_50)
++ return (bios_rd32(bios, NV_PEXTDEV_BOOT_0) >> 24) & 0xf;
++ else
++ return (bios_rd32(bios, NV_PEXTDEV_BOOT_0) >> 16) & 0xf;
++}
++
++static int parse_fp_mode_table(struct drm_device *dev, struct nvbios *bios)
++{
++ uint8_t *fptable;
++ uint8_t fptable_ver, headerlen = 0, recordlen, fpentries = 0xf, fpindex;
++ int ret, ofs, fpstrapping;
++ struct lvdstableheader lth;
++
++ if (bios->fp.fptablepointer == 0x0) {
++ /* Apple cards don't have the fp table; the laptops use DDC */
++ /* The table is also missing on some x86 IGPs */
++#ifndef __powerpc__
++ NV_ERROR(dev, "Pointer to flat panel table invalid\n");
++#endif
++ bios->pub.digital_min_front_porch = 0x4b;
++ return 0;
++ }
++
++ fptable = &bios->data[bios->fp.fptablepointer];
++ fptable_ver = fptable[0];
++
++ switch (fptable_ver) {
++ /*
++ * BMP version 0x5.0x11 BIOSen have version 1 like tables, but no
++ * version field, and miss one of the spread spectrum/PWM bytes.
++ * This could affect early GF2Go parts (not seen any appropriate ROMs
++ * though). Here we assume that a version of 0x05 matches this case
++ * (combining with a BMP version check would be better), as the
++ * common case for the panel type field is 0x0005, and that is in
++ * fact what we are reading the first byte of.
++ */
++ case 0x05: /* some NV10, 11, 15, 16 */
++ recordlen = 42;
++ ofs = -1;
++ break;
++ case 0x10: /* some NV15/16, and NV11+ */
++ recordlen = 44;
++ ofs = 0;
++ break;
++ case 0x20: /* NV40+ */
++ headerlen = fptable[1];
++ recordlen = fptable[2];
++ fpentries = fptable[3];
++ /*
++ * fptable[4] is the minimum
++ * RAMDAC_FP_HCRTC -> RAMDAC_FP_HSYNC_START gap
++ */
++ bios->pub.digital_min_front_porch = fptable[4];
++ ofs = -7;
++ break;
++ default:
++ NV_ERROR(dev,
++ "FP table revision %d.%d not currently supported\n",
++ fptable_ver >> 4, fptable_ver & 0xf);
++ return -ENOSYS;
++ }
++
++ if (!bios->is_mobile) /* !mobile only needs digital_min_front_porch */
++ return 0;
++
++ ret = parse_lvds_manufacturer_table_header(dev, bios, &lth);
++ if (ret)
++ return ret;
++
++ if (lth.lvds_ver == 0x30 || lth.lvds_ver == 0x40) {
++ bios->fp.fpxlatetableptr = bios->fp.lvdsmanufacturerpointer +
++ lth.headerlen + 1;
++ bios->fp.xlatwidth = lth.recordlen;
++ }
++ if (bios->fp.fpxlatetableptr == 0x0) {
++ NV_ERROR(dev, "Pointer to flat panel xlat table invalid\n");
++ return -EINVAL;
++ }
++
++ fpstrapping = get_fp_strap(dev, bios);
++
++ fpindex = bios->data[bios->fp.fpxlatetableptr +
++ fpstrapping * bios->fp.xlatwidth];
++
++ if (fpindex > fpentries) {
++ NV_ERROR(dev, "Bad flat panel table index\n");
++ return -ENOENT;
++ }
++
++ /* nv4x cards need both a strap value and fpindex of 0xf to use DDC */
++ if (lth.lvds_ver > 0x10)
++ bios->pub.fp_no_ddc = fpstrapping != 0xf || fpindex != 0xf;
++
++ /*
++ * If either the strap or xlated fpindex value are 0xf there is no
++ * panel using a strap-derived bios mode present. this condition
++ * includes, but is different from, the DDC panel indicator above
++ */
++ if (fpstrapping == 0xf || fpindex == 0xf)
++ return 0;
++
++ bios->fp.mode_ptr = bios->fp.fptablepointer + headerlen +
++ recordlen * fpindex + ofs;
++
++ NV_TRACE(dev, "BIOS FP mode: %dx%d (%dkHz pixel clock)\n",
++ ROM16(bios->data[bios->fp.mode_ptr + 11]) + 1,
++ ROM16(bios->data[bios->fp.mode_ptr + 25]) + 1,
++ ROM16(bios->data[bios->fp.mode_ptr + 7]) * 10);
++
++ return 0;
++}
++
++bool nouveau_bios_fp_mode(struct drm_device *dev, struct drm_display_mode *mode)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ uint8_t *mode_entry = &bios->data[bios->fp.mode_ptr];
++
++ if (!mode) /* just checking whether we can produce a mode */
++ return bios->fp.mode_ptr;
++
++ memset(mode, 0, sizeof(struct drm_display_mode));
++ /*
++ * For version 1.0 (version in byte 0):
++ * bytes 1-2 are "panel type", including bits on whether Colour/mono,
++ * single/dual link, and type (TFT etc.)
++ * bytes 3-6 are bits per colour in RGBX
++ */
++ mode->clock = ROM16(mode_entry[7]) * 10;
++ /* bytes 9-10 is HActive */
++ mode->hdisplay = ROM16(mode_entry[11]) + 1;
++ /*
++ * bytes 13-14 is HValid Start
++ * bytes 15-16 is HValid End
++ */
++ mode->hsync_start = ROM16(mode_entry[17]) + 1;
++ mode->hsync_end = ROM16(mode_entry[19]) + 1;
++ mode->htotal = ROM16(mode_entry[21]) + 1;
++ /* bytes 23-24, 27-30 similarly, but vertical */
++ mode->vdisplay = ROM16(mode_entry[25]) + 1;
++ mode->vsync_start = ROM16(mode_entry[31]) + 1;
++ mode->vsync_end = ROM16(mode_entry[33]) + 1;
++ mode->vtotal = ROM16(mode_entry[35]) + 1;
++ mode->flags |= (mode_entry[37] & 0x10) ?
++ DRM_MODE_FLAG_PHSYNC : DRM_MODE_FLAG_NHSYNC;
++ mode->flags |= (mode_entry[37] & 0x1) ?
++ DRM_MODE_FLAG_PVSYNC : DRM_MODE_FLAG_NVSYNC;
++ /*
++ * bytes 38-39 relate to spread spectrum settings
++ * bytes 40-43 are something to do with PWM
++ */
++
++ mode->status = MODE_OK;
++ mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
++ drm_mode_set_name(mode);
++ return bios->fp.mode_ptr;
++}
++
++int nouveau_bios_parse_lvds_table(struct drm_device *dev, int pxclk, bool *dl, bool *if_is_24bit)
++{
++ /*
++ * The LVDS table header is (mostly) described in
++ * parse_lvds_manufacturer_table_header(): the BIT header additionally
++ * contains the dual-link transition pxclk (in 10s kHz), at byte 5 - if
++ * straps are not being used for the panel, this specifies the frequency
++ * at which modes should be set up in the dual link style.
++ *
++ * Following the header, the BMP (ver 0xa) table has several records,
++ * indexed by a seperate xlat table, indexed in turn by the fp strap in
++ * EXTDEV_BOOT. Each record had a config byte, followed by 6 script
++ * numbers for use by INIT_SUB which controlled panel init and power,
++ * and finally a dword of ms to sleep between power off and on
++ * operations.
++ *
++ * In the BIT versions, the table following the header serves as an
++ * integrated config and xlat table: the records in the table are
++ * indexed by the FP strap nibble in EXTDEV_BOOT, and each record has
++ * two bytes - the first as a config byte, the second for indexing the
++ * fp mode table pointed to by the BIT 'D' table
++ *
++ * DDC is not used until after card init, so selecting the correct table
++ * entry and setting the dual link flag for EDID equipped panels,
++ * requiring tests against the native-mode pixel clock, cannot be done
++ * until later, when this function should be called with non-zero pxclk
++ */
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ int fpstrapping = get_fp_strap(dev, bios), lvdsmanufacturerindex = 0;
++ struct lvdstableheader lth;
++ uint16_t lvdsofs;
++ int ret, chip_version = bios->pub.chip_version;
++
++ ret = parse_lvds_manufacturer_table_header(dev, bios, &lth);
++ if (ret)
++ return ret;
++
++ switch (lth.lvds_ver) {
++ case 0x0a: /* pre NV40 */
++ lvdsmanufacturerindex = bios->data[
++ bios->fp.fpxlatemanufacturertableptr +
++ fpstrapping];
++
++ /* we're done if this isn't the EDID panel case */
++ if (!pxclk)
++ break;
++
++ if (chip_version < 0x25) {
++ /* nv17 behaviour
++ *
++ * It seems the old style lvds script pointer is reused
++ * to select 18/24 bit colour depth for EDID panels.
++ */
++ lvdsmanufacturerindex =
++ (bios->legacy.lvds_single_a_script_ptr & 1) ?
++ 2 : 0;
++ if (pxclk >= bios->fp.duallink_transition_clk)
++ lvdsmanufacturerindex++;
++ } else if (chip_version < 0x30) {
++ /* nv28 behaviour (off-chip encoder)
++ *
++ * nv28 does a complex dance of first using byte 121 of
++ * the EDID to choose the lvdsmanufacturerindex, then
++ * later attempting to match the EDID manufacturer and
++ * product IDs in a table (signature 'pidt' (panel id
++ * table?)), setting an lvdsmanufacturerindex of 0 and
++ * an fp strap of the match index (or 0xf if none)
++ */
++ lvdsmanufacturerindex = 0;
++ } else {
++ /* nv31, nv34 behaviour */
++ lvdsmanufacturerindex = 0;
++ if (pxclk >= bios->fp.duallink_transition_clk)
++ lvdsmanufacturerindex = 2;
++ if (pxclk >= 140000)
++ lvdsmanufacturerindex = 3;
++ }
++
++ /*
++ * nvidia set the high nibble of (cr57=f, cr58) to
++ * lvdsmanufacturerindex in this case; we don't
++ */
++ break;
++ case 0x30: /* NV4x */
++ case 0x40: /* G80/G90 */
++ lvdsmanufacturerindex = fpstrapping;
++ break;
++ default:
++ NV_ERROR(dev, "LVDS table revision not currently supported\n");
++ return -ENOSYS;
++ }
++
++ lvdsofs = bios->fp.xlated_entry = bios->fp.lvdsmanufacturerpointer + lth.headerlen + lth.recordlen * lvdsmanufacturerindex;
++ switch (lth.lvds_ver) {
++ case 0x0a:
++ bios->fp.power_off_for_reset = bios->data[lvdsofs] & 1;
++ bios->fp.reset_after_pclk_change = bios->data[lvdsofs] & 2;
++ bios->fp.dual_link = bios->data[lvdsofs] & 4;
++ bios->fp.link_c_increment = bios->data[lvdsofs] & 8;
++ *if_is_24bit = bios->data[lvdsofs] & 16;
++ break;
++ case 0x30:
++ /*
++ * My money would be on there being a 24 bit interface bit in
++ * this table, but I have no example of a laptop bios with a
++ * 24 bit panel to confirm that. Hence we shout loudly if any
++ * bit other than bit 0 is set (I've not even seen bit 1)
++ */
++ if (bios->data[lvdsofs] > 1)
++ NV_ERROR(dev,
++ "You have a very unusual laptop display; please report it\n");
++ /*
++ * No sign of the "power off for reset" or "reset for panel
++ * on" bits, but it's safer to assume we should
++ */
++ bios->fp.power_off_for_reset = true;
++ bios->fp.reset_after_pclk_change = true;
++ /*
++ * It's ok lvdsofs is wrong for nv4x edid case; dual_link is
++ * over-written, and BITbit1 isn't used
++ */
++ bios->fp.dual_link = bios->data[lvdsofs] & 1;
++ bios->fp.BITbit1 = bios->data[lvdsofs] & 2;
++ bios->fp.duallink_transition_clk = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 5]) * 10;
++ break;
++ case 0x40:
++ bios->fp.dual_link = bios->data[lvdsofs] & 1;
++ bios->fp.if_is_24bit = bios->data[lvdsofs] & 2;
++ bios->fp.strapless_is_24bit = bios->data[bios->fp.lvdsmanufacturerpointer + 4];
++ bios->fp.duallink_transition_clk = ROM16(bios->data[bios->fp.lvdsmanufacturerpointer + 5]) * 10;
++ break;
++ }
++
++ /* set dual_link flag for EDID case */
++ if (pxclk && (chip_version < 0x25 || chip_version > 0x28))
++ bios->fp.dual_link = (pxclk >= bios->fp.duallink_transition_clk);
++
++ *dl = bios->fp.dual_link;
++
++ return 0;
++}
++
++static uint8_t *
++bios_output_config_match(struct drm_device *dev, struct dcb_entry *dcbent,
++ uint16_t record, int record_len, int record_nr)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ uint32_t entry;
++ uint16_t table;
++ int i, v;
++
++ for (i = 0; i < record_nr; i++, record += record_len) {
++ table = ROM16(bios->data[record]);
++ if (!table)
++ continue;
++ entry = ROM32(bios->data[table]);
++
++ v = (entry & 0x000f0000) >> 16;
++ if (!(v & dcbent->or))
++ continue;
++
++ v = (entry & 0x000000f0) >> 4;
++ if (v != dcbent->location)
++ continue;
++
++ v = (entry & 0x0000000f);
++ if (v != dcbent->type)
++ continue;
++
++ return &bios->data[table];
++ }
++
++ return NULL;
++}
++
++void *
++nouveau_bios_dp_table(struct drm_device *dev, struct dcb_entry *dcbent,
++ int *length)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ uint8_t *table;
++
++ if (!bios->display.dp_table_ptr) {
++ NV_ERROR(dev, "No pointer to DisplayPort table\n");
++ return NULL;
++ }
++ table = &bios->data[bios->display.dp_table_ptr];
++
++ if (table[0] != 0x21) {
++ NV_ERROR(dev, "DisplayPort table version 0x%02x unknown\n",
++ table[0]);
++ return NULL;
++ }
++
++ *length = table[4];
++ return bios_output_config_match(dev, dcbent,
++ bios->display.dp_table_ptr + table[1],
++ table[2], table[3]);
++}
++
++int
++nouveau_bios_run_display_table(struct drm_device *dev, struct dcb_entry *dcbent,
++ uint32_t sub, int pxclk)
++{
++ /*
++ * The display script table is located by the BIT 'U' table.
++ *
++ * It contains an array of pointers to various tables describing
++ * a particular output type. The first 32-bits of the output
++ * tables contains similar information to a DCB entry, and is
++ * used to decide whether that particular table is suitable for
++ * the output you want to access.
++ *
++ * The "record header length" field here seems to indicate the
++ * offset of the first configuration entry in the output tables.
++ * This is 10 on most cards I've seen, but 12 has been witnessed
++ * on DP cards, and there's another script pointer within the
++ * header.
++ *
++ * offset + 0 ( 8 bits): version
++ * offset + 1 ( 8 bits): header length
++ * offset + 2 ( 8 bits): record length
++ * offset + 3 ( 8 bits): number of records
++ * offset + 4 ( 8 bits): record header length
++ * offset + 5 (16 bits): pointer to first output script table
++ */
++
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ uint8_t *table = &bios->data[bios->display.script_table_ptr];
++ uint8_t *otable = NULL;
++ uint16_t script;
++ int i = 0;
++
++ if (!bios->display.script_table_ptr) {
++ NV_ERROR(dev, "No pointer to output script table\n");
++ return 1;
++ }
++
++ /*
++ * Nothing useful has been in any of the pre-2.0 tables I've seen,
++ * so until they are, we really don't need to care.
++ */
++ if (table[0] < 0x20)
++ return 1;
++
++ if (table[0] != 0x20 && table[0] != 0x21) {
++ NV_ERROR(dev, "Output script table version 0x%02x unknown\n",
++ table[0]);
++ return 1;
++ }
++
++ /*
++ * The output script tables describing a particular output type
++ * look as follows:
++ *
++ * offset + 0 (32 bits): output this table matches (hash of DCB)
++ * offset + 4 ( 8 bits): unknown
++ * offset + 5 ( 8 bits): number of configurations
++ * offset + 6 (16 bits): pointer to some script
++ * offset + 8 (16 bits): pointer to some script
++ *
++ * headerlen == 10
++ * offset + 10 : configuration 0
++ *
++ * headerlen == 12
++ * offset + 10 : pointer to some script
++ * offset + 12 : configuration 0
++ *
++ * Each config entry is as follows:
++ *
++ * offset + 0 (16 bits): unknown, assumed to be a match value
++ * offset + 2 (16 bits): pointer to script table (clock set?)
++ * offset + 4 (16 bits): pointer to script table (reset?)
++ *
++ * There doesn't appear to be a count value to say how many
++ * entries exist in each script table, instead, a 0 value in
++ * the first 16-bit word seems to indicate both the end of the
++ * list and the default entry. The second 16-bit word in the
++ * script tables is a pointer to the script to execute.
++ */
++
++ NV_DEBUG_KMS(dev, "Searching for output entry for %d %d %d\n",
++ dcbent->type, dcbent->location, dcbent->or);
++ otable = bios_output_config_match(dev, dcbent, table[1] +
++ bios->display.script_table_ptr,
++ table[2], table[3]);
++ if (!otable) {
++ NV_ERROR(dev, "Couldn't find matching output script table\n");
++ return 1;
++ }
++
++ if (pxclk < -2 || pxclk > 0) {
++ /* Try to find matching script table entry */
++ for (i = 0; i < otable[5]; i++) {
++ if (ROM16(otable[table[4] + i*6]) == sub)
++ break;
++ }
++
++ if (i == otable[5]) {
++ NV_ERROR(dev, "Table 0x%04x not found for %d/%d, "
++ "using first\n",
++ sub, dcbent->type, dcbent->or);
++ i = 0;
++ }
++ }
++
++ if (pxclk == 0) {
++ script = ROM16(otable[6]);
++ if (!script) {
++ NV_DEBUG_KMS(dev, "output script 0 not found\n");
++ return 1;
++ }
++
++ NV_TRACE(dev, "0x%04X: parsing output script 0\n", script);
++ nouveau_bios_run_init_table(dev, script, dcbent);
++ } else
++ if (pxclk == -1) {
++ script = ROM16(otable[8]);
++ if (!script) {
++ NV_DEBUG_KMS(dev, "output script 1 not found\n");
++ return 1;
++ }
++
++ NV_TRACE(dev, "0x%04X: parsing output script 1\n", script);
++ nouveau_bios_run_init_table(dev, script, dcbent);
++ } else
++ if (pxclk == -2) {
++ if (table[4] >= 12)
++ script = ROM16(otable[10]);
++ else
++ script = 0;
++ if (!script) {
++ NV_DEBUG_KMS(dev, "output script 2 not found\n");
++ return 1;
++ }
++
++ NV_TRACE(dev, "0x%04X: parsing output script 2\n", script);
++ nouveau_bios_run_init_table(dev, script, dcbent);
++ } else
++ if (pxclk > 0) {
++ script = ROM16(otable[table[4] + i*6 + 2]);
++ if (script)
++ script = clkcmptable(bios, script, pxclk);
++ if (!script) {
++ NV_ERROR(dev, "clock script 0 not found\n");
++ return 1;
++ }
++
++ NV_TRACE(dev, "0x%04X: parsing clock script 0\n", script);
++ nouveau_bios_run_init_table(dev, script, dcbent);
++ } else
++ if (pxclk < 0) {
++ script = ROM16(otable[table[4] + i*6 + 4]);
++ if (script)
++ script = clkcmptable(bios, script, -pxclk);
++ if (!script) {
++ NV_DEBUG_KMS(dev, "clock script 1 not found\n");
++ return 1;
++ }
++
++ NV_TRACE(dev, "0x%04X: parsing clock script 1\n", script);
++ nouveau_bios_run_init_table(dev, script, dcbent);
++ }
++
++ return 0;
++}
++
++
++int run_tmds_table(struct drm_device *dev, struct dcb_entry *dcbent, int head, int pxclk)
++{
++ /*
++ * the pxclk parameter is in kHz
++ *
++ * This runs the TMDS regs setting code found on BIT bios cards
++ *
++ * For ffs(or) == 1 use the first table, for ffs(or) == 2 and
++ * ffs(or) == 3, use the second.
++ */
++
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ int cv = bios->pub.chip_version;
++ uint16_t clktable = 0, scriptptr;
++ uint32_t sel_clk_binding, sel_clk;
++
++ /* pre-nv17 off-chip tmds uses scripts, post nv17 doesn't */
++ if (cv >= 0x17 && cv != 0x1a && cv != 0x20 &&
++ dcbent->location != DCB_LOC_ON_CHIP)
++ return 0;
++
++ switch (ffs(dcbent->or)) {
++ case 1:
++ clktable = bios->tmds.output0_script_ptr;
++ break;
++ case 2:
++ case 3:
++ clktable = bios->tmds.output1_script_ptr;
++ break;
++ }
++
++ if (!clktable) {
++ NV_ERROR(dev, "Pixel clock comparison table not found\n");
++ return -EINVAL;
++ }
++
++ scriptptr = clkcmptable(bios, clktable, pxclk);
++
++ if (!scriptptr) {
++ NV_ERROR(dev, "TMDS output init script not found\n");
++ return -ENOENT;
++ }
++
++ /* don't let script change pll->head binding */
++ sel_clk_binding = bios_rd32(bios, NV_PRAMDAC_SEL_CLK) & 0x50000;
++ run_digital_op_script(dev, scriptptr, dcbent, head, pxclk >= 165000);
++ sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK) & ~0x50000;
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, sel_clk | sel_clk_binding);
++
++ return 0;
++}
++
++int get_pll_limits(struct drm_device *dev, uint32_t limit_match, struct pll_lims *pll_lim)
++{
++ /*
++ * PLL limits table
++ *
++ * Version 0x10: NV30, NV31
++ * One byte header (version), one record of 24 bytes
++ * Version 0x11: NV36 - Not implemented
++ * Seems to have same record style as 0x10, but 3 records rather than 1
++ * Version 0x20: Found on Geforce 6 cards
++ * Trivial 4 byte BIT header. 31 (0x1f) byte record length
++ * Version 0x21: Found on Geforce 7, 8 and some Geforce 6 cards
++ * 5 byte header, fifth byte of unknown purpose. 35 (0x23) byte record
++ * length in general, some (integrated) have an extra configuration byte
++ * Version 0x30: Found on Geforce 8, separates the register mapping
++ * from the limits tables.
++ */
++
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ int cv = bios->pub.chip_version, pllindex = 0;
++ uint8_t pll_lim_ver = 0, headerlen = 0, recordlen = 0, entries = 0;
++ uint32_t crystal_strap_mask, crystal_straps;
++
++ if (!bios->pll_limit_tbl_ptr) {
++ if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
++ cv >= 0x40) {
++ NV_ERROR(dev, "Pointer to PLL limits table invalid\n");
++ return -EINVAL;
++ }
++ } else
++ pll_lim_ver = bios->data[bios->pll_limit_tbl_ptr];
++
++ crystal_strap_mask = 1 << 6;
++ /* open coded dev->twoHeads test */
++ if (cv > 0x10 && cv != 0x15 && cv != 0x1a && cv != 0x20)
++ crystal_strap_mask |= 1 << 22;
++ crystal_straps = nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) &
++ crystal_strap_mask;
++
++ switch (pll_lim_ver) {
++ /*
++ * We use version 0 to indicate a pre limit table bios (single stage
++ * pll) and load the hard coded limits instead.
++ */
++ case 0:
++ break;
++ case 0x10:
++ case 0x11:
++ /*
++ * Strictly v0x11 has 3 entries, but the last two don't seem
++ * to get used.
++ */
++ headerlen = 1;
++ recordlen = 0x18;
++ entries = 1;
++ pllindex = 0;
++ break;
++ case 0x20:
++ case 0x21:
++ case 0x30:
++ case 0x40:
++ headerlen = bios->data[bios->pll_limit_tbl_ptr + 1];
++ recordlen = bios->data[bios->pll_limit_tbl_ptr + 2];
++ entries = bios->data[bios->pll_limit_tbl_ptr + 3];
++ break;
++ default:
++ NV_ERROR(dev, "PLL limits table revision 0x%X not currently "
++ "supported\n", pll_lim_ver);
++ return -ENOSYS;
++ }
++
++ /* initialize all members to zero */
++ memset(pll_lim, 0, sizeof(struct pll_lims));
++
++ if (pll_lim_ver == 0x10 || pll_lim_ver == 0x11) {
++ uint8_t *pll_rec = &bios->data[bios->pll_limit_tbl_ptr + headerlen + recordlen * pllindex];
++
++ pll_lim->vco1.minfreq = ROM32(pll_rec[0]);
++ pll_lim->vco1.maxfreq = ROM32(pll_rec[4]);
++ pll_lim->vco2.minfreq = ROM32(pll_rec[8]);
++ pll_lim->vco2.maxfreq = ROM32(pll_rec[12]);
++ pll_lim->vco1.min_inputfreq = ROM32(pll_rec[16]);
++ pll_lim->vco2.min_inputfreq = ROM32(pll_rec[20]);
++ pll_lim->vco1.max_inputfreq = pll_lim->vco2.max_inputfreq = INT_MAX;
++
++ /* these values taken from nv30/31/36 */
++ pll_lim->vco1.min_n = 0x1;
++ if (cv == 0x36)
++ pll_lim->vco1.min_n = 0x5;
++ pll_lim->vco1.max_n = 0xff;
++ pll_lim->vco1.min_m = 0x1;
++ pll_lim->vco1.max_m = 0xd;
++ pll_lim->vco2.min_n = 0x4;
++ /*
++ * On nv30, 31, 36 (i.e. all cards with two stage PLLs with this
++ * table version (apart from nv35)), N2 is compared to
++ * maxN2 (0x46) and 10 * maxM2 (0x4), so set maxN2 to 0x28 and
++ * save a comparison
++ */
++ pll_lim->vco2.max_n = 0x28;
++ if (cv == 0x30 || cv == 0x35)
++ /* only 5 bits available for N2 on nv30/35 */
++ pll_lim->vco2.max_n = 0x1f;
++ pll_lim->vco2.min_m = 0x1;
++ pll_lim->vco2.max_m = 0x4;
++ pll_lim->max_log2p = 0x7;
++ pll_lim->max_usable_log2p = 0x6;
++ } else if (pll_lim_ver == 0x20 || pll_lim_ver == 0x21) {
++ uint16_t plloffs = bios->pll_limit_tbl_ptr + headerlen;
++ uint32_t reg = 0; /* default match */
++ uint8_t *pll_rec;
++ int i;
++
++ /*
++ * First entry is default match, if nothing better. warn if
++ * reg field nonzero
++ */
++ if (ROM32(bios->data[plloffs]))
++ NV_WARN(dev, "Default PLL limit entry has non-zero "
++ "register field\n");
++
++ if (limit_match > MAX_PLL_TYPES)
++ /* we've been passed a reg as the match */
++ reg = limit_match;
++ else /* limit match is a pll type */
++ for (i = 1; i < entries && !reg; i++) {
++ uint32_t cmpreg = ROM32(bios->data[plloffs + recordlen * i]);
++
++ if (limit_match == NVPLL &&
++ (cmpreg == NV_PRAMDAC_NVPLL_COEFF || cmpreg == 0x4000))
++ reg = cmpreg;
++ if (limit_match == MPLL &&
++ (cmpreg == NV_PRAMDAC_MPLL_COEFF || cmpreg == 0x4020))
++ reg = cmpreg;
++ if (limit_match == VPLL1 &&
++ (cmpreg == NV_PRAMDAC_VPLL_COEFF || cmpreg == 0x4010))
++ reg = cmpreg;
++ if (limit_match == VPLL2 &&
++ (cmpreg == NV_RAMDAC_VPLL2 || cmpreg == 0x4018))
++ reg = cmpreg;
++ }
++
++ for (i = 1; i < entries; i++)
++ if (ROM32(bios->data[plloffs + recordlen * i]) == reg) {
++ pllindex = i;
++ break;
++ }
++
++ pll_rec = &bios->data[plloffs + recordlen * pllindex];
++
++ BIOSLOG(bios, "Loading PLL limits for reg 0x%08x\n",
++ pllindex ? reg : 0);
++
++ /*
++ * Frequencies are stored in tables in MHz, kHz are more
++ * useful, so we convert.
++ */
++
++ /* What output frequencies can each VCO generate? */
++ pll_lim->vco1.minfreq = ROM16(pll_rec[4]) * 1000;
++ pll_lim->vco1.maxfreq = ROM16(pll_rec[6]) * 1000;
++ pll_lim->vco2.minfreq = ROM16(pll_rec[8]) * 1000;
++ pll_lim->vco2.maxfreq = ROM16(pll_rec[10]) * 1000;
++
++ /* What input frequencies they accept (past the m-divider)? */
++ pll_lim->vco1.min_inputfreq = ROM16(pll_rec[12]) * 1000;
++ pll_lim->vco2.min_inputfreq = ROM16(pll_rec[14]) * 1000;
++ pll_lim->vco1.max_inputfreq = ROM16(pll_rec[16]) * 1000;
++ pll_lim->vco2.max_inputfreq = ROM16(pll_rec[18]) * 1000;
++
++ /* What values are accepted as multiplier and divider? */
++ pll_lim->vco1.min_n = pll_rec[20];
++ pll_lim->vco1.max_n = pll_rec[21];
++ pll_lim->vco1.min_m = pll_rec[22];
++ pll_lim->vco1.max_m = pll_rec[23];
++ pll_lim->vco2.min_n = pll_rec[24];
++ pll_lim->vco2.max_n = pll_rec[25];
++ pll_lim->vco2.min_m = pll_rec[26];
++ pll_lim->vco2.max_m = pll_rec[27];
++
++ pll_lim->max_usable_log2p = pll_lim->max_log2p = pll_rec[29];
++ if (pll_lim->max_log2p > 0x7)
++ /* pll decoding in nv_hw.c assumes never > 7 */
++ NV_WARN(dev, "Max log2 P value greater than 7 (%d)\n",
++ pll_lim->max_log2p);
++ if (cv < 0x60)
++ pll_lim->max_usable_log2p = 0x6;
++ pll_lim->log2p_bias = pll_rec[30];
++
++ if (recordlen > 0x22)
++ pll_lim->refclk = ROM32(pll_rec[31]);
++
++ if (recordlen > 0x23 && pll_rec[35])
++ NV_WARN(dev,
++ "Bits set in PLL configuration byte (%x)\n",
++ pll_rec[35]);
++
++ /* C51 special not seen elsewhere */
++ if (cv == 0x51 && !pll_lim->refclk) {
++ uint32_t sel_clk = bios_rd32(bios, NV_PRAMDAC_SEL_CLK);
++
++ if (((limit_match == NV_PRAMDAC_VPLL_COEFF || limit_match == VPLL1) && sel_clk & 0x20) ||
++ ((limit_match == NV_RAMDAC_VPLL2 || limit_match == VPLL2) && sel_clk & 0x80)) {
++ if (bios_idxprt_rd(bios, NV_CIO_CRX__COLOR, NV_CIO_CRE_CHIP_ID_INDEX) < 0xa3)
++ pll_lim->refclk = 200000;
++ else
++ pll_lim->refclk = 25000;
++ }
++ }
++ } else if (pll_lim_ver == 0x30) { /* ver 0x30 */
++ uint8_t *entry = &bios->data[bios->pll_limit_tbl_ptr + headerlen];
++ uint8_t *record = NULL;
++ int i;
++
++ BIOSLOG(bios, "Loading PLL limits for register 0x%08x\n",
++ limit_match);
++
++ for (i = 0; i < entries; i++, entry += recordlen) {
++ if (ROM32(entry[3]) == limit_match) {
++ record = &bios->data[ROM16(entry[1])];
++ break;
++ }
++ }
++
++ if (!record) {
++ NV_ERROR(dev, "Register 0x%08x not found in PLL "
++ "limits table", limit_match);
++ return -ENOENT;
++ }
++
++ pll_lim->vco1.minfreq = ROM16(record[0]) * 1000;
++ pll_lim->vco1.maxfreq = ROM16(record[2]) * 1000;
++ pll_lim->vco2.minfreq = ROM16(record[4]) * 1000;
++ pll_lim->vco2.maxfreq = ROM16(record[6]) * 1000;
++ pll_lim->vco1.min_inputfreq = ROM16(record[8]) * 1000;
++ pll_lim->vco2.min_inputfreq = ROM16(record[10]) * 1000;
++ pll_lim->vco1.max_inputfreq = ROM16(record[12]) * 1000;
++ pll_lim->vco2.max_inputfreq = ROM16(record[14]) * 1000;
++ pll_lim->vco1.min_n = record[16];
++ pll_lim->vco1.max_n = record[17];
++ pll_lim->vco1.min_m = record[18];
++ pll_lim->vco1.max_m = record[19];
++ pll_lim->vco2.min_n = record[20];
++ pll_lim->vco2.max_n = record[21];
++ pll_lim->vco2.min_m = record[22];
++ pll_lim->vco2.max_m = record[23];
++ pll_lim->max_usable_log2p = pll_lim->max_log2p = record[25];
++ pll_lim->log2p_bias = record[27];
++ pll_lim->refclk = ROM32(record[28]);
++ } else if (pll_lim_ver) { /* ver 0x40 */
++ uint8_t *entry = &bios->data[bios->pll_limit_tbl_ptr + headerlen];
++ uint8_t *record = NULL;
++ int i;
++
++ BIOSLOG(bios, "Loading PLL limits for register 0x%08x\n",
++ limit_match);
++
++ for (i = 0; i < entries; i++, entry += recordlen) {
++ if (ROM32(entry[3]) == limit_match) {
++ record = &bios->data[ROM16(entry[1])];
++ break;
++ }
++ }
++
++ if (!record) {
++ NV_ERROR(dev, "Register 0x%08x not found in PLL "
++ "limits table", limit_match);
++ return -ENOENT;
++ }
++
++ pll_lim->vco1.minfreq = ROM16(record[0]) * 1000;
++ pll_lim->vco1.maxfreq = ROM16(record[2]) * 1000;
++ pll_lim->vco1.min_inputfreq = ROM16(record[4]) * 1000;
++ pll_lim->vco1.max_inputfreq = ROM16(record[6]) * 1000;
++ pll_lim->vco1.min_m = record[8];
++ pll_lim->vco1.max_m = record[9];
++ pll_lim->vco1.min_n = record[10];
++ pll_lim->vco1.max_n = record[11];
++ pll_lim->min_p = record[12];
++ pll_lim->max_p = record[13];
++ /* where did this go to?? */
++ if (limit_match == 0x00614100 || limit_match == 0x00614900)
++ pll_lim->refclk = 27000;
++ else
++ pll_lim->refclk = 100000;
++ }
++
++ /*
++ * By now any valid limit table ought to have set a max frequency for
++ * vco1, so if it's zero it's either a pre limit table bios, or one
++ * with an empty limit table (seen on nv18)
++ */
++ if (!pll_lim->vco1.maxfreq) {
++ pll_lim->vco1.minfreq = bios->fminvco;
++ pll_lim->vco1.maxfreq = bios->fmaxvco;
++ pll_lim->vco1.min_inputfreq = 0;
++ pll_lim->vco1.max_inputfreq = INT_MAX;
++ pll_lim->vco1.min_n = 0x1;
++ pll_lim->vco1.max_n = 0xff;
++ pll_lim->vco1.min_m = 0x1;
++ if (crystal_straps == 0) {
++ /* nv05 does this, nv11 doesn't, nv10 unknown */
++ if (cv < 0x11)
++ pll_lim->vco1.min_m = 0x7;
++ pll_lim->vco1.max_m = 0xd;
++ } else {
++ if (cv < 0x11)
++ pll_lim->vco1.min_m = 0x8;
++ pll_lim->vco1.max_m = 0xe;
++ }
++ if (cv < 0x17 || cv == 0x1a || cv == 0x20)
++ pll_lim->max_log2p = 4;
++ else
++ pll_lim->max_log2p = 5;
++ pll_lim->max_usable_log2p = pll_lim->max_log2p;
++ }
++
++ if (!pll_lim->refclk)
++ switch (crystal_straps) {
++ case 0:
++ pll_lim->refclk = 13500;
++ break;
++ case (1 << 6):
++ pll_lim->refclk = 14318;
++ break;
++ case (1 << 22):
++ pll_lim->refclk = 27000;
++ break;
++ case (1 << 22 | 1 << 6):
++ pll_lim->refclk = 25000;
++ break;
++ }
++
++#if 0 /* for easy debugging */
++ ErrorF("pll.vco1.minfreq: %d\n", pll_lim->vco1.minfreq);
++ ErrorF("pll.vco1.maxfreq: %d\n", pll_lim->vco1.maxfreq);
++ ErrorF("pll.vco2.minfreq: %d\n", pll_lim->vco2.minfreq);
++ ErrorF("pll.vco2.maxfreq: %d\n", pll_lim->vco2.maxfreq);
++
++ ErrorF("pll.vco1.min_inputfreq: %d\n", pll_lim->vco1.min_inputfreq);
++ ErrorF("pll.vco1.max_inputfreq: %d\n", pll_lim->vco1.max_inputfreq);
++ ErrorF("pll.vco2.min_inputfreq: %d\n", pll_lim->vco2.min_inputfreq);
++ ErrorF("pll.vco2.max_inputfreq: %d\n", pll_lim->vco2.max_inputfreq);
++
++ ErrorF("pll.vco1.min_n: %d\n", pll_lim->vco1.min_n);
++ ErrorF("pll.vco1.max_n: %d\n", pll_lim->vco1.max_n);
++ ErrorF("pll.vco1.min_m: %d\n", pll_lim->vco1.min_m);
++ ErrorF("pll.vco1.max_m: %d\n", pll_lim->vco1.max_m);
++ ErrorF("pll.vco2.min_n: %d\n", pll_lim->vco2.min_n);
++ ErrorF("pll.vco2.max_n: %d\n", pll_lim->vco2.max_n);
++ ErrorF("pll.vco2.min_m: %d\n", pll_lim->vco2.min_m);
++ ErrorF("pll.vco2.max_m: %d\n", pll_lim->vco2.max_m);
++
++ ErrorF("pll.max_log2p: %d\n", pll_lim->max_log2p);
++ ErrorF("pll.log2p_bias: %d\n", pll_lim->log2p_bias);
++
++ ErrorF("pll.refclk: %d\n", pll_lim->refclk);
++#endif
++
++ return 0;
++}
++
++static void parse_bios_version(struct drm_device *dev, struct nvbios *bios, uint16_t offset)
++{
++ /*
++ * offset + 0 (8 bits): Micro version
++ * offset + 1 (8 bits): Minor version
++ * offset + 2 (8 bits): Chip version
++ * offset + 3 (8 bits): Major version
++ */
++
++ bios->major_version = bios->data[offset + 3];
++ bios->pub.chip_version = bios->data[offset + 2];
++ NV_TRACE(dev, "Bios version %02x.%02x.%02x.%02x\n",
++ bios->data[offset + 3], bios->data[offset + 2],
++ bios->data[offset + 1], bios->data[offset]);
++}
++
++static void parse_script_table_pointers(struct nvbios *bios, uint16_t offset)
++{
++ /*
++ * Parses the init table segment for pointers used in script execution.
++ *
++ * offset + 0 (16 bits): init script tables pointer
++ * offset + 2 (16 bits): macro index table pointer
++ * offset + 4 (16 bits): macro table pointer
++ * offset + 6 (16 bits): condition table pointer
++ * offset + 8 (16 bits): io condition table pointer
++ * offset + 10 (16 bits): io flag condition table pointer
++ * offset + 12 (16 bits): init function table pointer
++ */
++
++ bios->init_script_tbls_ptr = ROM16(bios->data[offset]);
++ bios->macro_index_tbl_ptr = ROM16(bios->data[offset + 2]);
++ bios->macro_tbl_ptr = ROM16(bios->data[offset + 4]);
++ bios->condition_tbl_ptr = ROM16(bios->data[offset + 6]);
++ bios->io_condition_tbl_ptr = ROM16(bios->data[offset + 8]);
++ bios->io_flag_condition_tbl_ptr = ROM16(bios->data[offset + 10]);
++ bios->init_function_tbl_ptr = ROM16(bios->data[offset + 12]);
++}
++
++static int parse_bit_A_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
++{
++ /*
++ * Parses the load detect values for g80 cards.
++ *
++ * offset + 0 (16 bits): loadval table pointer
++ */
++
++ uint16_t load_table_ptr;
++ uint8_t version, headerlen, entrylen, num_entries;
++
++ if (bitentry->length != 3) {
++ NV_ERROR(dev, "Do not understand BIT A table\n");
++ return -EINVAL;
++ }
++
++ load_table_ptr = ROM16(bios->data[bitentry->offset]);
++
++ if (load_table_ptr == 0x0) {
++ NV_ERROR(dev, "Pointer to BIT loadval table invalid\n");
++ return -EINVAL;
++ }
++
++ version = bios->data[load_table_ptr];
++
++ if (version != 0x10) {
++ NV_ERROR(dev, "BIT loadval table version %d.%d not supported\n",
++ version >> 4, version & 0xF);
++ return -ENOSYS;
++ }
++
++ headerlen = bios->data[load_table_ptr + 1];
++ entrylen = bios->data[load_table_ptr + 2];
++ num_entries = bios->data[load_table_ptr + 3];
++
++ if (headerlen != 4 || entrylen != 4 || num_entries != 2) {
++ NV_ERROR(dev, "Do not understand BIT loadval table\n");
++ return -EINVAL;
++ }
++
++ /* First entry is normal dac, 2nd tv-out perhaps? */
++ bios->pub.dactestval = ROM32(bios->data[load_table_ptr + headerlen]) & 0x3ff;
++
++ return 0;
++}
++
++static int parse_bit_C_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
++{
++ /*
++ * offset + 8 (16 bits): PLL limits table pointer
++ *
++ * There's more in here, but that's unknown.
++ */
++
++ if (bitentry->length < 10) {
++ NV_ERROR(dev, "Do not understand BIT C table\n");
++ return -EINVAL;
++ }
++
++ bios->pll_limit_tbl_ptr = ROM16(bios->data[bitentry->offset + 8]);
++
++ return 0;
++}
++
++static int parse_bit_display_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
++{
++ /*
++ * Parses the flat panel table segment that the bit entry points to.
++ * Starting at bitentry->offset:
++ *
++ * offset + 0 (16 bits): ??? table pointer - seems to have 18 byte
++ * records beginning with a freq.
++ * offset + 2 (16 bits): mode table pointer
++ */
++
++ if (bitentry->length != 4) {
++ NV_ERROR(dev, "Do not understand BIT display table\n");
++ return -EINVAL;
++ }
++
++ bios->fp.fptablepointer = ROM16(bios->data[bitentry->offset + 2]);
++
++ return 0;
++}
++
++static int parse_bit_init_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
++{
++ /*
++ * Parses the init table segment that the bit entry points to.
++ *
++ * See parse_script_table_pointers for layout
++ */
++
++ if (bitentry->length < 14) {
++ NV_ERROR(dev, "Do not understand init table\n");
++ return -EINVAL;
++ }
++
++ parse_script_table_pointers(bios, bitentry->offset);
++
++ if (bitentry->length >= 16)
++ bios->some_script_ptr = ROM16(bios->data[bitentry->offset + 14]);
++ if (bitentry->length >= 18)
++ bios->init96_tbl_ptr = ROM16(bios->data[bitentry->offset + 16]);
++
++ return 0;
++}
++
++static int parse_bit_i_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
++{
++ /*
++ * BIT 'i' (info?) table
++ *
++ * offset + 0 (32 bits): BIOS version dword (as in B table)
++ * offset + 5 (8 bits): BIOS feature byte (same as for BMP?)
++ * offset + 13 (16 bits): pointer to table containing DAC load
++ * detection comparison values
++ *
++ * There's other things in the table, purpose unknown
++ */
++
++ uint16_t daccmpoffset;
++ uint8_t dacver, dacheaderlen;
++
++ if (bitentry->length < 6) {
++ NV_ERROR(dev, "BIT i table too short for needed information\n");
++ return -EINVAL;
++ }
++
++ parse_bios_version(dev, bios, bitentry->offset);
++
++ /*
++ * bit 4 seems to indicate a mobile bios (doesn't suffer from BMP's
++ * Quadro identity crisis), other bits possibly as for BMP feature byte
++ */
++ bios->feature_byte = bios->data[bitentry->offset + 5];
++ bios->is_mobile = bios->feature_byte & FEATURE_MOBILE;
++
++ if (bitentry->length < 15) {
++ NV_WARN(dev, "BIT i table not long enough for DAC load "
++ "detection comparison table\n");
++ return -EINVAL;
++ }
++
++ daccmpoffset = ROM16(bios->data[bitentry->offset + 13]);
++
++ /* doesn't exist on g80 */
++ if (!daccmpoffset)
++ return 0;
++
++ /*
++ * The first value in the table, following the header, is the
++ * comparison value, the second entry is a comparison value for
++ * TV load detection.
++ */
++
++ dacver = bios->data[daccmpoffset];
++ dacheaderlen = bios->data[daccmpoffset + 1];
++
++ if (dacver != 0x00 && dacver != 0x10) {
++ NV_WARN(dev, "DAC load detection comparison table version "
++ "%d.%d not known\n", dacver >> 4, dacver & 0xf);
++ return -ENOSYS;
++ }
++
++ bios->pub.dactestval = ROM32(bios->data[daccmpoffset + dacheaderlen]);
++ bios->pub.tvdactestval = ROM32(bios->data[daccmpoffset + dacheaderlen + 4]);
++
++ return 0;
++}
++
++static int parse_bit_lvds_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
++{
++ /*
++ * Parses the LVDS table segment that the bit entry points to.
++ * Starting at bitentry->offset:
++ *
++ * offset + 0 (16 bits): LVDS strap xlate table pointer
++ */
++
++ if (bitentry->length != 2) {
++ NV_ERROR(dev, "Do not understand BIT LVDS table\n");
++ return -EINVAL;
++ }
++
++ /*
++ * No idea if it's still called the LVDS manufacturer table, but
++ * the concept's close enough.
++ */
++ bios->fp.lvdsmanufacturerpointer = ROM16(bios->data[bitentry->offset]);
++
++ return 0;
++}
++
++static int
++parse_bit_M_tbl_entry(struct drm_device *dev, struct nvbios *bios,
++ struct bit_entry *bitentry)
++{
++ /*
++ * offset + 2 (8 bits): number of options in an
++ * INIT_RAM_RESTRICT_ZM_REG_GROUP opcode option set
++ * offset + 3 (16 bits): pointer to strap xlate table for RAM
++ * restrict option selection
++ *
++ * There's a bunch of bits in this table other than the RAM restrict
++ * stuff that we don't use - their use currently unknown
++ */
++
++ /*
++ * Older bios versions don't have a sufficiently long table for
++ * what we want
++ */
++ if (bitentry->length < 0x5)
++ return 0;
++
++ if (bitentry->id[1] < 2) {
++ bios->ram_restrict_group_count = bios->data[bitentry->offset + 2];
++ bios->ram_restrict_tbl_ptr = ROM16(bios->data[bitentry->offset + 3]);
++ } else {
++ bios->ram_restrict_group_count = bios->data[bitentry->offset + 0];
++ bios->ram_restrict_tbl_ptr = ROM16(bios->data[bitentry->offset + 1]);
++ }
++
++ return 0;
++}
++
++static int parse_bit_tmds_tbl_entry(struct drm_device *dev, struct nvbios *bios, struct bit_entry *bitentry)
++{
++ /*
++ * Parses the pointer to the TMDS table
++ *
++ * Starting at bitentry->offset:
++ *
++ * offset + 0 (16 bits): TMDS table pointer
++ *
++ * The TMDS table is typically found just before the DCB table, with a
++ * characteristic signature of 0x11,0x13 (1.1 being version, 0x13 being
++ * length?)
++ *
++ * At offset +7 is a pointer to a script, which I don't know how to
++ * run yet.
++ * At offset +9 is a pointer to another script, likewise
++ * Offset +11 has a pointer to a table where the first word is a pxclk
++ * frequency and the second word a pointer to a script, which should be
++ * run if the comparison pxclk frequency is less than the pxclk desired.
++ * This repeats for decreasing comparison frequencies
++ * Offset +13 has a pointer to a similar table
++ * The selection of table (and possibly +7/+9 script) is dictated by
++ * "or" from the DCB.
++ */
++
++ uint16_t tmdstableptr, script1, script2;
++
++ if (bitentry->length != 2) {
++ NV_ERROR(dev, "Do not understand BIT TMDS table\n");
++ return -EINVAL;
++ }
++
++ tmdstableptr = ROM16(bios->data[bitentry->offset]);
++
++ if (tmdstableptr == 0x0) {
++ NV_ERROR(dev, "Pointer to TMDS table invalid\n");
++ return -EINVAL;
++ }
++
++ /* nv50+ has v2.0, but we don't parse it atm */
++ if (bios->data[tmdstableptr] != 0x11) {
++ NV_WARN(dev,
++ "TMDS table revision %d.%d not currently supported\n",
++ bios->data[tmdstableptr] >> 4, bios->data[tmdstableptr] & 0xf);
++ return -ENOSYS;
++ }
++
++ /*
++ * These two scripts are odd: they don't seem to get run even when
++ * they are not stubbed.
++ */
++ script1 = ROM16(bios->data[tmdstableptr + 7]);
++ script2 = ROM16(bios->data[tmdstableptr + 9]);
++ if (bios->data[script1] != 'q' || bios->data[script2] != 'q')
++ NV_WARN(dev, "TMDS table script pointers not stubbed\n");
++
++ bios->tmds.output0_script_ptr = ROM16(bios->data[tmdstableptr + 11]);
++ bios->tmds.output1_script_ptr = ROM16(bios->data[tmdstableptr + 13]);
++
++ return 0;
++}
++
++static int
++parse_bit_U_tbl_entry(struct drm_device *dev, struct nvbios *bios,
++ struct bit_entry *bitentry)
++{
++ /*
++ * Parses the pointer to the G80 output script tables
++ *
++ * Starting at bitentry->offset:
++ *
++ * offset + 0 (16 bits): output script table pointer
++ */
++
++ uint16_t outputscripttableptr;
++
++ if (bitentry->length != 3) {
++ NV_ERROR(dev, "Do not understand BIT U table\n");
++ return -EINVAL;
++ }
++
++ outputscripttableptr = ROM16(bios->data[bitentry->offset]);
++ bios->display.script_table_ptr = outputscripttableptr;
++ return 0;
++}
++
++static int
++parse_bit_displayport_tbl_entry(struct drm_device *dev, struct nvbios *bios,
++ struct bit_entry *bitentry)
++{
++ bios->display.dp_table_ptr = ROM16(bios->data[bitentry->offset]);
++ return 0;
++}
++
++struct bit_table {
++ const char id;
++ int (* const parse_fn)(struct drm_device *, struct nvbios *, struct bit_entry *);
++};
++
++#define BIT_TABLE(id, funcid) ((struct bit_table){ id, parse_bit_##funcid##_tbl_entry })
++
++static int
++parse_bit_table(struct nvbios *bios, const uint16_t bitoffset,
++ struct bit_table *table)
++{
++ struct drm_device *dev = bios->dev;
++ uint8_t maxentries = bios->data[bitoffset + 4];
++ int i, offset;
++ struct bit_entry bitentry;
++
++ for (i = 0, offset = bitoffset + 6; i < maxentries; i++, offset += 6) {
++ bitentry.id[0] = bios->data[offset];
++
++ if (bitentry.id[0] != table->id)
++ continue;
++
++ bitentry.id[1] = bios->data[offset + 1];
++ bitentry.length = ROM16(bios->data[offset + 2]);
++ bitentry.offset = ROM16(bios->data[offset + 4]);
++
++ return table->parse_fn(dev, bios, &bitentry);
++ }
++
++ NV_INFO(dev, "BIT table '%c' not found\n", table->id);
++ return -ENOSYS;
++}
++
++static int
++parse_bit_structure(struct nvbios *bios, const uint16_t bitoffset)
++{
++ int ret;
++
++ /*
++ * The only restriction on parsing order currently is having 'i' first
++ * for use of bios->*_version or bios->feature_byte while parsing;
++ * functions shouldn't be actually *doing* anything apart from pulling
++ * data from the image into the bios struct, thus no interdependencies
++ */
++ ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('i', i));
++ if (ret) /* info? */
++ return ret;
++ if (bios->major_version >= 0x60) /* g80+ */
++ parse_bit_table(bios, bitoffset, &BIT_TABLE('A', A));
++ ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('C', C));
++ if (ret)
++ return ret;
++ parse_bit_table(bios, bitoffset, &BIT_TABLE('D', display));
++ ret = parse_bit_table(bios, bitoffset, &BIT_TABLE('I', init));
++ if (ret)
++ return ret;
++ parse_bit_table(bios, bitoffset, &BIT_TABLE('M', M)); /* memory? */
++ parse_bit_table(bios, bitoffset, &BIT_TABLE('L', lvds));
++ parse_bit_table(bios, bitoffset, &BIT_TABLE('T', tmds));
++ parse_bit_table(bios, bitoffset, &BIT_TABLE('U', U));
++ parse_bit_table(bios, bitoffset, &BIT_TABLE('d', displayport));
++
++ return 0;
++}
++
++static int parse_bmp_structure(struct drm_device *dev, struct nvbios *bios, unsigned int offset)
++{
++ /*
++ * Parses the BMP structure for useful things, but does not act on them
++ *
++ * offset + 5: BMP major version
++ * offset + 6: BMP minor version
++ * offset + 9: BMP feature byte
++ * offset + 10: BCD encoded BIOS version
++ *
++ * offset + 18: init script table pointer (for bios versions < 5.10h)
++ * offset + 20: extra init script table pointer (for bios
++ * versions < 5.10h)
++ *
++ * offset + 24: memory init table pointer (used on early bios versions)
++ * offset + 26: SDR memory sequencing setup data table
++ * offset + 28: DDR memory sequencing setup data table
++ *
++ * offset + 54: index of I2C CRTC pair to use for CRT output
++ * offset + 55: index of I2C CRTC pair to use for TV output
++ * offset + 56: index of I2C CRTC pair to use for flat panel output
++ * offset + 58: write CRTC index for I2C pair 0
++ * offset + 59: read CRTC index for I2C pair 0
++ * offset + 60: write CRTC index for I2C pair 1
++ * offset + 61: read CRTC index for I2C pair 1
++ *
++ * offset + 67: maximum internal PLL frequency (single stage PLL)
++ * offset + 71: minimum internal PLL frequency (single stage PLL)
++ *
++ * offset + 75: script table pointers, as described in
++ * parse_script_table_pointers
++ *
++ * offset + 89: TMDS single link output A table pointer
++ * offset + 91: TMDS single link output B table pointer
++ * offset + 95: LVDS single link output A table pointer
++ * offset + 105: flat panel timings table pointer
++ * offset + 107: flat panel strapping translation table pointer
++ * offset + 117: LVDS manufacturer panel config table pointer
++ * offset + 119: LVDS manufacturer strapping translation table pointer
++ *
++ * offset + 142: PLL limits table pointer
++ *
++ * offset + 156: minimum pixel clock for LVDS dual link
++ */
++
++ uint8_t *bmp = &bios->data[offset], bmp_version_major, bmp_version_minor;
++ uint16_t bmplength;
++ uint16_t legacy_scripts_offset, legacy_i2c_offset;
++
++ /* load needed defaults in case we can't parse this info */
++ bios->bdcb.dcb.i2c[0].write = NV_CIO_CRE_DDC_WR__INDEX;
++ bios->bdcb.dcb.i2c[0].read = NV_CIO_CRE_DDC_STATUS__INDEX;
++ bios->bdcb.dcb.i2c[1].write = NV_CIO_CRE_DDC0_WR__INDEX;
++ bios->bdcb.dcb.i2c[1].read = NV_CIO_CRE_DDC0_STATUS__INDEX;
++ bios->pub.digital_min_front_porch = 0x4b;
++ bios->fmaxvco = 256000;
++ bios->fminvco = 128000;
++ bios->fp.duallink_transition_clk = 90000;
++
++ bmp_version_major = bmp[5];
++ bmp_version_minor = bmp[6];
++
++ NV_TRACE(dev, "BMP version %d.%d\n",
++ bmp_version_major, bmp_version_minor);
++
++ /*
++ * Make sure that 0x36 is blank and can't be mistaken for a DCB
++ * pointer on early versions
++ */
++ if (bmp_version_major < 5)
++ *(uint16_t *)&bios->data[0x36] = 0;
++
++ /*
++ * Seems that the minor version was 1 for all major versions prior
++ * to 5. Version 6 could theoretically exist, but I suspect BIT
++ * happened instead.
++ */
++ if ((bmp_version_major < 5 && bmp_version_minor != 1) || bmp_version_major > 5) {
++ NV_ERROR(dev, "You have an unsupported BMP version. "
++ "Please send in your bios\n");
++ return -ENOSYS;
++ }
++
++ if (bmp_version_major == 0)
++ /* nothing that's currently useful in this version */
++ return 0;
++ else if (bmp_version_major == 1)
++ bmplength = 44; /* exact for 1.01 */
++ else if (bmp_version_major == 2)
++ bmplength = 48; /* exact for 2.01 */
++ else if (bmp_version_major == 3)
++ bmplength = 54;
++ /* guessed - mem init tables added in this version */
++ else if (bmp_version_major == 4 || bmp_version_minor < 0x1)
++ /* don't know if 5.0 exists... */
++ bmplength = 62;
++ /* guessed - BMP I2C indices added in version 4*/
++ else if (bmp_version_minor < 0x6)
++ bmplength = 67; /* exact for 5.01 */
++ else if (bmp_version_minor < 0x10)
++ bmplength = 75; /* exact for 5.06 */
++ else if (bmp_version_minor == 0x10)
++ bmplength = 89; /* exact for 5.10h */
++ else if (bmp_version_minor < 0x14)
++ bmplength = 118; /* exact for 5.11h */
++ else if (bmp_version_minor < 0x24)
++ /*
++ * Not sure of version where pll limits came in;
++ * certainly exist by 0x24 though.
++ */
++ /* length not exact: this is long enough to get lvds members */
++ bmplength = 123;
++ else if (bmp_version_minor < 0x27)
++ /*
++ * Length not exact: this is long enough to get pll limit
++ * member
++ */
++ bmplength = 144;
++ else
++ /*
++ * Length not exact: this is long enough to get dual link
++ * transition clock.
++ */
++ bmplength = 158;
++
++ /* checksum */
++ if (nv_cksum(bmp, 8)) {
++ NV_ERROR(dev, "Bad BMP checksum\n");
++ return -EINVAL;
++ }
++
++ /*
++ * Bit 4 seems to indicate either a mobile bios or a quadro card --
++ * mobile behaviour consistent (nv11+), quadro only seen nv18gl-nv36gl
++ * (not nv10gl), bit 5 that the flat panel tables are present, and
++ * bit 6 a tv bios.
++ */
++ bios->feature_byte = bmp[9];
++
++ parse_bios_version(dev, bios, offset + 10);
++
++ if (bmp_version_major < 5 || bmp_version_minor < 0x10)
++ bios->old_style_init = true;
++ legacy_scripts_offset = 18;
++ if (bmp_version_major < 2)
++ legacy_scripts_offset -= 4;
++ bios->init_script_tbls_ptr = ROM16(bmp[legacy_scripts_offset]);
++ bios->extra_init_script_tbl_ptr = ROM16(bmp[legacy_scripts_offset + 2]);
++
++ if (bmp_version_major > 2) { /* appears in BMP 3 */
++ bios->legacy.mem_init_tbl_ptr = ROM16(bmp[24]);
++ bios->legacy.sdr_seq_tbl_ptr = ROM16(bmp[26]);
++ bios->legacy.ddr_seq_tbl_ptr = ROM16(bmp[28]);
++ }
++
++ legacy_i2c_offset = 0x48; /* BMP version 2 & 3 */
++ if (bmplength > 61)
++ legacy_i2c_offset = offset + 54;
++ bios->legacy.i2c_indices.crt = bios->data[legacy_i2c_offset];
++ bios->legacy.i2c_indices.tv = bios->data[legacy_i2c_offset + 1];
++ bios->legacy.i2c_indices.panel = bios->data[legacy_i2c_offset + 2];
++ bios->bdcb.dcb.i2c[0].write = bios->data[legacy_i2c_offset + 4];
++ bios->bdcb.dcb.i2c[0].read = bios->data[legacy_i2c_offset + 5];
++ bios->bdcb.dcb.i2c[1].write = bios->data[legacy_i2c_offset + 6];
++ bios->bdcb.dcb.i2c[1].read = bios->data[legacy_i2c_offset + 7];
++
++ if (bmplength > 74) {
++ bios->fmaxvco = ROM32(bmp[67]);
++ bios->fminvco = ROM32(bmp[71]);
++ }
++ if (bmplength > 88)
++ parse_script_table_pointers(bios, offset + 75);
++ if (bmplength > 94) {
++ bios->tmds.output0_script_ptr = ROM16(bmp[89]);
++ bios->tmds.output1_script_ptr = ROM16(bmp[91]);
++ /*
++ * Never observed in use with lvds scripts, but is reused for
++ * 18/24 bit panel interface default for EDID equipped panels
++ * (if_is_24bit not set directly to avoid any oscillation).
++ */
++ bios->legacy.lvds_single_a_script_ptr = ROM16(bmp[95]);
++ }
++ if (bmplength > 108) {
++ bios->fp.fptablepointer = ROM16(bmp[105]);
++ bios->fp.fpxlatetableptr = ROM16(bmp[107]);
++ bios->fp.xlatwidth = 1;
++ }
++ if (bmplength > 120) {
++ bios->fp.lvdsmanufacturerpointer = ROM16(bmp[117]);
++ bios->fp.fpxlatemanufacturertableptr = ROM16(bmp[119]);
++ }
++ if (bmplength > 143)
++ bios->pll_limit_tbl_ptr = ROM16(bmp[142]);
++
++ if (bmplength > 157)
++ bios->fp.duallink_transition_clk = ROM16(bmp[156]) * 10;
++
++ return 0;
++}
++
++static uint16_t findstr(uint8_t *data, int n, const uint8_t *str, int len)
++{
++ int i, j;
++
++ for (i = 0; i <= (n - len); i++) {
++ for (j = 0; j < len; j++)
++ if (data[i + j] != str[j])
++ break;
++ if (j == len)
++ return i;
++ }
++
++ return 0;
++}
++
++static int
++read_dcb_i2c_entry(struct drm_device *dev, int dcb_version, uint8_t *i2ctable, int index, struct dcb_i2c_entry *i2c)
++{
++ uint8_t dcb_i2c_ver = dcb_version, headerlen = 0, entry_len = 4;
++ int i2c_entries = DCB_MAX_NUM_I2C_ENTRIES;
++ int recordoffset = 0, rdofs = 1, wrofs = 0;
++ uint8_t port_type = 0;
++
++ if (!i2ctable)
++ return -EINVAL;
++
++ if (dcb_version >= 0x30) {
++ if (i2ctable[0] != dcb_version) /* necessary? */
++ NV_WARN(dev,
++ "DCB I2C table version mismatch (%02X vs %02X)\n",
++ i2ctable[0], dcb_version);
++ dcb_i2c_ver = i2ctable[0];
++ headerlen = i2ctable[1];
++ if (i2ctable[2] <= DCB_MAX_NUM_I2C_ENTRIES)
++ i2c_entries = i2ctable[2];
++ else
++ NV_WARN(dev,
++ "DCB I2C table has more entries than indexable "
++ "(%d entries, max index 15)\n", i2ctable[2]);
++ entry_len = i2ctable[3];
++ /* [4] is i2c_default_indices, read in parse_dcb_table() */
++ }
++ /*
++ * It's your own fault if you call this function on a DCB 1.1 BIOS --
++ * the test below is for DCB 1.2
++ */
++ if (dcb_version < 0x14) {
++ recordoffset = 2;
++ rdofs = 0;
++ wrofs = 1;
++ }
++
++ if (index == 0xf)
++ return 0;
++ if (index > i2c_entries) {
++ NV_ERROR(dev, "DCB I2C index too big (%d > %d)\n",
++ index, i2ctable[2]);
++ return -ENOENT;
++ }
++ if (i2ctable[headerlen + entry_len * index + 3] == 0xff) {
++ NV_ERROR(dev, "DCB I2C entry invalid\n");
++ return -EINVAL;
++ }
++
++ if (dcb_i2c_ver >= 0x30) {
++ port_type = i2ctable[headerlen + recordoffset + 3 + entry_len * index];
++
++ /*
++ * Fixup for chips using same address offset for read and
++ * write.
++ */
++ if (port_type == 4) /* seen on C51 */
++ rdofs = wrofs = 1;
++ if (port_type >= 5) /* G80+ */
++ rdofs = wrofs = 0;
++ }
++
++ if (dcb_i2c_ver >= 0x40 && port_type != 5 && port_type != 6)
++ NV_WARN(dev, "DCB I2C table has port type %d\n", port_type);
++
++ i2c->port_type = port_type;
++ i2c->read = i2ctable[headerlen + recordoffset + rdofs + entry_len * index];
++ i2c->write = i2ctable[headerlen + recordoffset + wrofs + entry_len * index];
++
++ return 0;
++}
++
++static struct dcb_gpio_entry *
++new_gpio_entry(struct nvbios *bios)
++{
++ struct parsed_dcb_gpio *gpio = &bios->bdcb.gpio;
++
++ return &gpio->entry[gpio->entries++];
++}
++
++struct dcb_gpio_entry *
++nouveau_bios_gpio_entry(struct drm_device *dev, enum dcb_gpio_tag tag)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ int i;
++
++ for (i = 0; i < bios->bdcb.gpio.entries; i++) {
++ if (bios->bdcb.gpio.entry[i].tag != tag)
++ continue;
++
++ return &bios->bdcb.gpio.entry[i];
++ }
++
++ return NULL;
++}
++
++static void
++parse_dcb30_gpio_entry(struct nvbios *bios, uint16_t offset)
++{
++ struct dcb_gpio_entry *gpio;
++ uint16_t ent = ROM16(bios->data[offset]);
++ uint8_t line = ent & 0x1f,
++ tag = ent >> 5 & 0x3f,
++ flags = ent >> 11 & 0x1f;
++
++ if (tag == 0x3f)
++ return;
++
++ gpio = new_gpio_entry(bios);
++
++ gpio->tag = tag;
++ gpio->line = line;
++ gpio->invert = flags != 4;
++}
++
++static void
++parse_dcb40_gpio_entry(struct nvbios *bios, uint16_t offset)
++{
++ struct dcb_gpio_entry *gpio;
++ uint32_t ent = ROM32(bios->data[offset]);
++ uint8_t line = ent & 0x1f,
++ tag = ent >> 8 & 0xff;
++
++ if (tag == 0xff)
++ return;
++
++ gpio = new_gpio_entry(bios);
++
++ /* Currently unused, we may need more fields parsed at some
++ * point. */
++ gpio->tag = tag;
++ gpio->line = line;
++}
++
++static void
++parse_dcb_gpio_table(struct nvbios *bios)
++{
++ struct drm_device *dev = bios->dev;
++ uint16_t gpio_table_ptr = bios->bdcb.gpio_table_ptr;
++ uint8_t *gpio_table = &bios->data[gpio_table_ptr];
++ int header_len = gpio_table[1],
++ entries = gpio_table[2],
++ entry_len = gpio_table[3];
++ void (*parse_entry)(struct nvbios *, uint16_t) = NULL;
++ int i;
++
++ if (bios->bdcb.version >= 0x40) {
++ if (gpio_table_ptr && entry_len != 4) {
++ NV_WARN(dev, "Invalid DCB GPIO table entry length.\n");
++ return;
++ }
++
++ parse_entry = parse_dcb40_gpio_entry;
++
++ } else if (bios->bdcb.version >= 0x30) {
++ if (gpio_table_ptr && entry_len != 2) {
++ NV_WARN(dev, "Invalid DCB GPIO table entry length.\n");
++ return;
++ }
++
++ parse_entry = parse_dcb30_gpio_entry;
++
++ } else if (bios->bdcb.version >= 0x22) {
++ /*
++ * DCBs older than v3.0 don't really have a GPIO
++ * table, instead they keep some GPIO info at fixed
++ * locations.
++ */
++ uint16_t dcbptr = ROM16(bios->data[0x36]);
++ uint8_t *tvdac_gpio = &bios->data[dcbptr - 5];
++
++ if (tvdac_gpio[0] & 1) {
++ struct dcb_gpio_entry *gpio = new_gpio_entry(bios);
++
++ gpio->tag = DCB_GPIO_TVDAC0;
++ gpio->line = tvdac_gpio[1] >> 4;
++ gpio->invert = tvdac_gpio[0] & 2;
++ }
++ }
++
++ if (!gpio_table_ptr)
++ return;
++
++ if (entries > DCB_MAX_NUM_GPIO_ENTRIES) {
++ NV_WARN(dev, "Too many entries in the DCB GPIO table.\n");
++ entries = DCB_MAX_NUM_GPIO_ENTRIES;
++ }
++
++ for (i = 0; i < entries; i++)
++ parse_entry(bios, gpio_table_ptr + header_len + entry_len * i);
++}
++
++struct dcb_connector_table_entry *
++nouveau_bios_connector_entry(struct drm_device *dev, int index)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ struct dcb_connector_table_entry *cte;
++
++ if (index >= bios->bdcb.connector.entries)
++ return NULL;
++
++ cte = &bios->bdcb.connector.entry[index];
++ if (cte->type == 0xff)
++ return NULL;
++
++ return cte;
++}
++
++static void
++parse_dcb_connector_table(struct nvbios *bios)
++{
++ struct drm_device *dev = bios->dev;
++ struct dcb_connector_table *ct = &bios->bdcb.connector;
++ struct dcb_connector_table_entry *cte;
++ uint8_t *conntab = &bios->data[bios->bdcb.connector_table_ptr];
++ uint8_t *entry;
++ int i;
++
++ if (!bios->bdcb.connector_table_ptr) {
++ NV_DEBUG_KMS(dev, "No DCB connector table present\n");
++ return;
++ }
++
++ NV_INFO(dev, "DCB connector table: VHER 0x%02x %d %d %d\n",
++ conntab[0], conntab[1], conntab[2], conntab[3]);
++ if ((conntab[0] != 0x30 && conntab[0] != 0x40) ||
++ (conntab[3] != 2 && conntab[3] != 4)) {
++ NV_ERROR(dev, " Unknown! Please report.\n");
++ return;
++ }
++
++ ct->entries = conntab[2];
++
++ entry = conntab + conntab[1];
++ cte = &ct->entry[0];
++ for (i = 0; i < conntab[2]; i++, entry += conntab[3], cte++) {
++ if (conntab[3] == 2)
++ cte->entry = ROM16(entry[0]);
++ else
++ cte->entry = ROM32(entry[0]);
++ cte->type = (cte->entry & 0x000000ff) >> 0;
++ cte->index = (cte->entry & 0x00000f00) >> 8;
++ switch (cte->entry & 0x00033000) {
++ case 0x00001000:
++ cte->gpio_tag = 0x07;
++ break;
++ case 0x00002000:
++ cte->gpio_tag = 0x08;
++ break;
++ case 0x00010000:
++ cte->gpio_tag = 0x51;
++ break;
++ case 0x00020000:
++ cte->gpio_tag = 0x52;
++ break;
++ default:
++ cte->gpio_tag = 0xff;
++ break;
++ }
++
++ if (cte->type == 0xff)
++ continue;
++
++ NV_INFO(dev, " %d: 0x%08x: type 0x%02x idx %d tag 0x%02x\n",
++ i, cte->entry, cte->type, cte->index, cte->gpio_tag);
++ }
++}
++
++static struct dcb_entry *new_dcb_entry(struct parsed_dcb *dcb)
++{
++ struct dcb_entry *entry = &dcb->entry[dcb->entries];
++
++ memset(entry, 0, sizeof(struct dcb_entry));
++ entry->index = dcb->entries++;
++
++ return entry;
++}
++
++static void fabricate_vga_output(struct parsed_dcb *dcb, int i2c, int heads)
++{
++ struct dcb_entry *entry = new_dcb_entry(dcb);
++
++ entry->type = 0;
++ entry->i2c_index = i2c;
++ entry->heads = heads;
++ entry->location = DCB_LOC_ON_CHIP;
++ /* "or" mostly unused in early gen crt modesetting, 0 is fine */
++}
++
++static void fabricate_dvi_i_output(struct parsed_dcb *dcb, bool twoHeads)
++{
++ struct dcb_entry *entry = new_dcb_entry(dcb);
++
++ entry->type = 2;
++ entry->i2c_index = LEGACY_I2C_PANEL;
++ entry->heads = twoHeads ? 3 : 1;
++ entry->location = !DCB_LOC_ON_CHIP; /* ie OFF CHIP */
++ entry->or = 1; /* means |0x10 gets set on CRE_LCD__INDEX */
++ entry->duallink_possible = false; /* SiI164 and co. are single link */
++
++#if 0
++ /*
++ * For dvi-a either crtc probably works, but my card appears to only
++ * support dvi-d. "nvidia" still attempts to program it for dvi-a,
++ * doing the full fp output setup (program 0x6808.. fp dimension regs,
++ * setting 0x680848 to 0x10000111 to enable, maybe setting 0x680880);
++ * the monitor picks up the mode res ok and lights up, but no pixel
++ * data appears, so the board manufacturer probably connected up the
++ * sync lines, but missed the video traces / components
++ *
++ * with this introduction, dvi-a left as an exercise for the reader.
++ */
++ fabricate_vga_output(dcb, LEGACY_I2C_PANEL, entry->heads);
++#endif
++}
++
++static void fabricate_tv_output(struct parsed_dcb *dcb, bool twoHeads)
++{
++ struct dcb_entry *entry = new_dcb_entry(dcb);
++
++ entry->type = 1;
++ entry->i2c_index = LEGACY_I2C_TV;
++ entry->heads = twoHeads ? 3 : 1;
++ entry->location = !DCB_LOC_ON_CHIP; /* ie OFF CHIP */
++}
++
++static bool
++parse_dcb20_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
++ uint32_t conn, uint32_t conf, struct dcb_entry *entry)
++{
++ entry->type = conn & 0xf;
++ entry->i2c_index = (conn >> 4) & 0xf;
++ entry->heads = (conn >> 8) & 0xf;
++ if (bdcb->version >= 0x40)
++ entry->connector = (conn >> 12) & 0xf;
++ entry->bus = (conn >> 16) & 0xf;
++ entry->location = (conn >> 20) & 0x3;
++ entry->or = (conn >> 24) & 0xf;
++ /*
++ * Normal entries consist of a single bit, but dual link has the
++ * next most significant bit set too
++ */
++ entry->duallink_possible =
++ ((1 << (ffs(entry->or) - 1)) * 3 == entry->or);
++
++ switch (entry->type) {
++ case OUTPUT_ANALOG:
++ /*
++ * Although the rest of a CRT conf dword is usually
++ * zeros, mac biosen have stuff there so we must mask
++ */
++ entry->crtconf.maxfreq = (bdcb->version < 0x30) ?
++ (conf & 0xffff) * 10 :
++ (conf & 0xff) * 10000;
++ break;
++ case OUTPUT_LVDS:
++ {
++ uint32_t mask;
++ if (conf & 0x1)
++ entry->lvdsconf.use_straps_for_mode = true;
++ if (bdcb->version < 0x22) {
++ mask = ~0xd;
++ /*
++ * The laptop in bug 14567 lies and claims to not use
++ * straps when it does, so assume all DCB 2.0 laptops
++ * use straps, until a broken EDID using one is produced
++ */
++ entry->lvdsconf.use_straps_for_mode = true;
++ /*
++ * Both 0x4 and 0x8 show up in v2.0 tables; assume they
++ * mean the same thing (probably wrong, but might work)
++ */
++ if (conf & 0x4 || conf & 0x8)
++ entry->lvdsconf.use_power_scripts = true;
++ } else {
++ mask = ~0x5;
++ if (conf & 0x4)
++ entry->lvdsconf.use_power_scripts = true;
++ }
++ if (conf & mask) {
++ /*
++ * Until we even try to use these on G8x, it's
++ * useless reporting unknown bits. They all are.
++ */
++ if (bdcb->version >= 0x40)
++ break;
++
++ NV_ERROR(dev, "Unknown LVDS configuration bits, "
++ "please report\n");
++ }
++ break;
++ }
++ case OUTPUT_TV:
++ {
++ if (bdcb->version >= 0x30)
++ entry->tvconf.has_component_output = conf & (0x8 << 4);
++ else
++ entry->tvconf.has_component_output = false;
++
++ break;
++ }
++ case OUTPUT_DP:
++ entry->dpconf.sor.link = (conf & 0x00000030) >> 4;
++ entry->dpconf.link_bw = (conf & 0x00e00000) >> 21;
++ switch ((conf & 0x0f000000) >> 24) {
++ case 0xf:
++ entry->dpconf.link_nr = 4;
++ break;
++ case 0x3:
++ entry->dpconf.link_nr = 2;
++ break;
++ default:
++ entry->dpconf.link_nr = 1;
++ break;
++ }
++ break;
++ case OUTPUT_TMDS:
++ entry->tmdsconf.sor.link = (conf & 0x00000030) >> 4;
++ break;
++ case 0xe:
++ /* weird g80 mobile type that "nv" treats as a terminator */
++ bdcb->dcb.entries--;
++ return false;
++ }
++
++ /* unsure what DCB version introduces this, 3.0? */
++ if (conf & 0x100000)
++ entry->i2c_upper_default = true;
++
++ return true;
++}
++
++static bool
++parse_dcb15_entry(struct drm_device *dev, struct parsed_dcb *dcb,
++ uint32_t conn, uint32_t conf, struct dcb_entry *entry)
++{
++ switch (conn & 0x0000000f) {
++ case 0:
++ entry->type = OUTPUT_ANALOG;
++ break;
++ case 1:
++ entry->type = OUTPUT_TV;
++ break;
++ case 2:
++ case 3:
++ entry->type = OUTPUT_LVDS;
++ break;
++ case 4:
++ switch ((conn & 0x000000f0) >> 4) {
++ case 0:
++ entry->type = OUTPUT_TMDS;
++ break;
++ case 1:
++ entry->type = OUTPUT_LVDS;
++ break;
++ default:
++ NV_ERROR(dev, "Unknown DCB subtype 4/%d\n",
++ (conn & 0x000000f0) >> 4);
++ return false;
++ }
++ break;
++ default:
++ NV_ERROR(dev, "Unknown DCB type %d\n", conn & 0x0000000f);
++ return false;
++ }
++
++ entry->i2c_index = (conn & 0x0003c000) >> 14;
++ entry->heads = ((conn & 0x001c0000) >> 18) + 1;
++ entry->or = entry->heads; /* same as heads, hopefully safe enough */
++ entry->location = (conn & 0x01e00000) >> 21;
++ entry->bus = (conn & 0x0e000000) >> 25;
++ entry->duallink_possible = false;
++
++ switch (entry->type) {
++ case OUTPUT_ANALOG:
++ entry->crtconf.maxfreq = (conf & 0xffff) * 10;
++ break;
++ case OUTPUT_TV:
++ entry->tvconf.has_component_output = false;
++ break;
++ case OUTPUT_TMDS:
++ /*
++ * Invent a DVI-A output, by copying the fields of the DVI-D
++ * output; reported to work by math_b on an NV20(!).
++ */
++ fabricate_vga_output(dcb, entry->i2c_index, entry->heads);
++ break;
++ case OUTPUT_LVDS:
++ if ((conn & 0x00003f00) != 0x10)
++ entry->lvdsconf.use_straps_for_mode = true;
++ entry->lvdsconf.use_power_scripts = true;
++ break;
++ default:
++ break;
++ }
++
++ return true;
++}
++
++static bool parse_dcb_entry(struct drm_device *dev, struct bios_parsed_dcb *bdcb,
++ uint32_t conn, uint32_t conf)
++{
++ struct dcb_entry *entry = new_dcb_entry(&bdcb->dcb);
++ bool ret;
++
++ if (bdcb->version >= 0x20)
++ ret = parse_dcb20_entry(dev, bdcb, conn, conf, entry);
++ else
++ ret = parse_dcb15_entry(dev, &bdcb->dcb, conn, conf, entry);
++ if (!ret)
++ return ret;
++
++ read_dcb_i2c_entry(dev, bdcb->version, bdcb->i2c_table,
++ entry->i2c_index, &bdcb->dcb.i2c[entry->i2c_index]);
++
++ return true;
++}
++
++static
++void merge_like_dcb_entries(struct drm_device *dev, struct parsed_dcb *dcb)
++{
++ /*
++ * DCB v2.0 lists each output combination separately.
++ * Here we merge compatible entries to have fewer outputs, with
++ * more options
++ */
++
++ int i, newentries = 0;
++
++ for (i = 0; i < dcb->entries; i++) {
++ struct dcb_entry *ient = &dcb->entry[i];
++ int j;
++
++ for (j = i + 1; j < dcb->entries; j++) {
++ struct dcb_entry *jent = &dcb->entry[j];
++
++ if (jent->type == 100) /* already merged entry */
++ continue;
++
++ /* merge heads field when all other fields the same */
++ if (jent->i2c_index == ient->i2c_index &&
++ jent->type == ient->type &&
++ jent->location == ient->location &&
++ jent->or == ient->or) {
++ NV_TRACE(dev, "Merging DCB entries %d and %d\n",
++ i, j);
++ ient->heads |= jent->heads;
++ jent->type = 100; /* dummy value */
++ }
++ }
++ }
++
++ /* Compact entries merged into others out of dcb */
++ for (i = 0; i < dcb->entries; i++) {
++ if (dcb->entry[i].type == 100)
++ continue;
++
++ if (newentries != i) {
++ dcb->entry[newentries] = dcb->entry[i];
++ dcb->entry[newentries].index = newentries;
++ }
++ newentries++;
++ }
++
++ dcb->entries = newentries;
++}
++
++static int
++parse_dcb_table(struct drm_device *dev, struct nvbios *bios, bool twoHeads)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct bios_parsed_dcb *bdcb = &bios->bdcb;
++ struct parsed_dcb *dcb;
++ uint16_t dcbptr = 0, i2ctabptr = 0;
++ uint8_t *dcbtable;
++ uint8_t headerlen = 0x4, entries = DCB_MAX_NUM_ENTRIES;
++ bool configblock = true;
++ int recordlength = 8, confofs = 4;
++ int i;
++
++ dcb = bios->pub.dcb = &bdcb->dcb;
++ dcb->entries = 0;
++
++ /* get the offset from 0x36 */
++ if (dev_priv->card_type > NV_04) {
++ dcbptr = ROM16(bios->data[0x36]);
++ if (dcbptr == 0x0000)
++ NV_WARN(dev, "No output data (DCB) found in BIOS\n");
++ }
++
++ /* this situation likely means a really old card, pre DCB */
++ if (dcbptr == 0x0) {
++ NV_INFO(dev, "Assuming a CRT output exists\n");
++ fabricate_vga_output(dcb, LEGACY_I2C_CRT, 1);
++
++ if (nv04_tv_identify(dev, bios->legacy.i2c_indices.tv) >= 0)
++ fabricate_tv_output(dcb, twoHeads);
++
++ return 0;
++ }
++
++ dcbtable = &bios->data[dcbptr];
++
++ /* get DCB version */
++ bdcb->version = dcbtable[0];
++ NV_TRACE(dev, "Found Display Configuration Block version %d.%d\n",
++ bdcb->version >> 4, bdcb->version & 0xf);
++
++ if (bdcb->version >= 0x20) { /* NV17+ */
++ uint32_t sig;
++
++ if (bdcb->version >= 0x30) { /* NV40+ */
++ headerlen = dcbtable[1];
++ entries = dcbtable[2];
++ recordlength = dcbtable[3];
++ i2ctabptr = ROM16(dcbtable[4]);
++ sig = ROM32(dcbtable[6]);
++ bdcb->gpio_table_ptr = ROM16(dcbtable[10]);
++ bdcb->connector_table_ptr = ROM16(dcbtable[20]);
++ } else {
++ i2ctabptr = ROM16(dcbtable[2]);
++ sig = ROM32(dcbtable[4]);
++ headerlen = 8;
++ }
++
++ if (sig != 0x4edcbdcb) {
++ NV_ERROR(dev, "Bad Display Configuration Block "
++ "signature (%08X)\n", sig);
++ return -EINVAL;
++ }
++ } else if (bdcb->version >= 0x15) { /* some NV11 and NV20 */
++ char sig[8] = { 0 };
++
++ strncpy(sig, (char *)&dcbtable[-7], 7);
++ i2ctabptr = ROM16(dcbtable[2]);
++ recordlength = 10;
++ confofs = 6;
++
++ if (strcmp(sig, "DEV_REC")) {
++ NV_ERROR(dev, "Bad Display Configuration Block "
++ "signature (%s)\n", sig);
++ return -EINVAL;
++ }
++ } else {
++ /*
++ * v1.4 (some NV15/16, NV11+) seems the same as v1.5, but always
++ * has the same single (crt) entry, even when tv-out present, so
++ * the conclusion is this version cannot really be used.
++ * v1.2 tables (some NV6/10, and NV15+) normally have the same
++ * 5 entries, which are not specific to the card and so no use.
++ * v1.2 does have an I2C table that read_dcb_i2c_table can
++ * handle, but cards exist (nv11 in #14821) with a bad i2c table
++ * pointer, so use the indices parsed in parse_bmp_structure.
++ * v1.1 (NV5+, maybe some NV4) is entirely unhelpful
++ */
++ NV_TRACEWARN(dev, "No useful information in BIOS output table; "
++ "adding all possible outputs\n");
++ fabricate_vga_output(dcb, LEGACY_I2C_CRT, 1);
++
++ /*
++ * Attempt to detect TV before DVI because the test
++ * for the former is more accurate and it rules the
++ * latter out.
++ */
++ if (nv04_tv_identify(dev,
++ bios->legacy.i2c_indices.tv) >= 0)
++ fabricate_tv_output(dcb, twoHeads);
++
++ else if (bios->tmds.output0_script_ptr ||
++ bios->tmds.output1_script_ptr)
++ fabricate_dvi_i_output(dcb, twoHeads);
++
++ return 0;
++ }
++
++ if (!i2ctabptr)
++ NV_WARN(dev, "No pointer to DCB I2C port table\n");
++ else {
++ bdcb->i2c_table = &bios->data[i2ctabptr];
++ if (bdcb->version >= 0x30)
++ bdcb->i2c_default_indices = bdcb->i2c_table[4];
++ }
++
++ parse_dcb_gpio_table(bios);
++ parse_dcb_connector_table(bios);
++
++ if (entries > DCB_MAX_NUM_ENTRIES)
++ entries = DCB_MAX_NUM_ENTRIES;
++
++ for (i = 0; i < entries; i++) {
++ uint32_t connection, config = 0;
++
++ connection = ROM32(dcbtable[headerlen + recordlength * i]);
++ if (configblock)
++ config = ROM32(dcbtable[headerlen + confofs + recordlength * i]);
++
++ /* seen on an NV11 with DCB v1.5 */
++ if (connection == 0x00000000)
++ break;
++
++ /* seen on an NV17 with DCB v2.0 */
++ if (connection == 0xffffffff)
++ break;
++
++ if ((connection & 0x0000000f) == 0x0000000f)
++ continue;
++
++ NV_TRACEWARN(dev, "Raw DCB entry %d: %08x %08x\n",
++ dcb->entries, connection, config);
++
++ if (!parse_dcb_entry(dev, bdcb, connection, config))
++ break;
++ }
++
++ /*
++ * apart for v2.1+ not being known for requiring merging, this
++ * guarantees dcbent->index is the index of the entry in the rom image
++ */
++ if (bdcb->version < 0x21)
++ merge_like_dcb_entries(dev, dcb);
++
++ return dcb->entries ? 0 : -ENXIO;
++}
++
++static void
++fixup_legacy_connector(struct nvbios *bios)
++{
++ struct bios_parsed_dcb *bdcb = &bios->bdcb;
++ struct parsed_dcb *dcb = &bdcb->dcb;
++ int high = 0, i;
++
++ /*
++ * DCB 3.0 also has the table in most cases, but there are some cards
++ * where the table is filled with stub entries, and the DCB entriy
++ * indices are all 0. We don't need the connector indices on pre-G80
++ * chips (yet?) so limit the use to DCB 4.0 and above.
++ */
++ if (bdcb->version >= 0x40)
++ return;
++
++ /*
++ * No known connector info before v3.0, so make it up. the rule here
++ * is: anything on the same i2c bus is considered to be on the same
++ * connector. any output without an associated i2c bus is assigned
++ * its own unique connector index.
++ */
++ for (i = 0; i < dcb->entries; i++) {
++ if (dcb->entry[i].i2c_index == 0xf)
++ continue;
++
++ /*
++ * Ignore the I2C index for on-chip TV-out, as there
++ * are cards with bogus values (nv31m in bug 23212),
++ * and it's otherwise useless.
++ */
++ if (dcb->entry[i].type == OUTPUT_TV &&
++ dcb->entry[i].location == DCB_LOC_ON_CHIP) {
++ dcb->entry[i].i2c_index = 0xf;
++ continue;
++ }
++
++ dcb->entry[i].connector = dcb->entry[i].i2c_index;
++ if (dcb->entry[i].connector > high)
++ high = dcb->entry[i].connector;
++ }
++
++ for (i = 0; i < dcb->entries; i++) {
++ if (dcb->entry[i].i2c_index != 0xf)
++ continue;
++
++ dcb->entry[i].connector = ++high;
++ }
++}
++
++static void
++fixup_legacy_i2c(struct nvbios *bios)
++{
++ struct parsed_dcb *dcb = &bios->bdcb.dcb;
++ int i;
++
++ for (i = 0; i < dcb->entries; i++) {
++ if (dcb->entry[i].i2c_index == LEGACY_I2C_CRT)
++ dcb->entry[i].i2c_index = bios->legacy.i2c_indices.crt;
++ if (dcb->entry[i].i2c_index == LEGACY_I2C_PANEL)
++ dcb->entry[i].i2c_index = bios->legacy.i2c_indices.panel;
++ if (dcb->entry[i].i2c_index == LEGACY_I2C_TV)
++ dcb->entry[i].i2c_index = bios->legacy.i2c_indices.tv;
++ }
++}
++
++static int load_nv17_hwsq_ucode_entry(struct drm_device *dev, struct nvbios *bios, uint16_t hwsq_offset, int entry)
++{
++ /*
++ * The header following the "HWSQ" signature has the number of entries,
++ * and the entry size
++ *
++ * An entry consists of a dword to write to the sequencer control reg
++ * (0x00001304), followed by the ucode bytes, written sequentially,
++ * starting at reg 0x00001400
++ */
++
++ uint8_t bytes_to_write;
++ uint16_t hwsq_entry_offset;
++ int i;
++
++ if (bios->data[hwsq_offset] <= entry) {
++ NV_ERROR(dev, "Too few entries in HW sequencer table for "
++ "requested entry\n");
++ return -ENOENT;
++ }
++
++ bytes_to_write = bios->data[hwsq_offset + 1];
++
++ if (bytes_to_write != 36) {
++ NV_ERROR(dev, "Unknown HW sequencer entry size\n");
++ return -EINVAL;
++ }
++
++ NV_TRACE(dev, "Loading NV17 power sequencing microcode\n");
++
++ hwsq_entry_offset = hwsq_offset + 2 + entry * bytes_to_write;
++
++ /* set sequencer control */
++ bios_wr32(bios, 0x00001304, ROM32(bios->data[hwsq_entry_offset]));
++ bytes_to_write -= 4;
++
++ /* write ucode */
++ for (i = 0; i < bytes_to_write; i += 4)
++ bios_wr32(bios, 0x00001400 + i, ROM32(bios->data[hwsq_entry_offset + i + 4]));
++
++ /* twiddle NV_PBUS_DEBUG_4 */
++ bios_wr32(bios, NV_PBUS_DEBUG_4, bios_rd32(bios, NV_PBUS_DEBUG_4) | 0x18);
++
++ return 0;
++}
++
++static int load_nv17_hw_sequencer_ucode(struct drm_device *dev,
++ struct nvbios *bios)
++{
++ /*
++ * BMP based cards, from NV17, need a microcode loading to correctly
++ * control the GPIO etc for LVDS panels
++ *
++ * BIT based cards seem to do this directly in the init scripts
++ *
++ * The microcode entries are found by the "HWSQ" signature.
++ */
++
++ const uint8_t hwsq_signature[] = { 'H', 'W', 'S', 'Q' };
++ const int sz = sizeof(hwsq_signature);
++ int hwsq_offset;
++
++ hwsq_offset = findstr(bios->data, bios->length, hwsq_signature, sz);
++ if (!hwsq_offset)
++ return 0;
++
++ /* always use entry 0? */
++ return load_nv17_hwsq_ucode_entry(dev, bios, hwsq_offset + sz, 0);
++}
++
++uint8_t *nouveau_bios_embedded_edid(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ const uint8_t edid_sig[] = {
++ 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x00 };
++ uint16_t offset = 0;
++ uint16_t newoffset;
++ int searchlen = NV_PROM_SIZE;
++
++ if (bios->fp.edid)
++ return bios->fp.edid;
++
++ while (searchlen) {
++ newoffset = findstr(&bios->data[offset], searchlen,
++ edid_sig, 8);
++ if (!newoffset)
++ return NULL;
++ offset += newoffset;
++ if (!nv_cksum(&bios->data[offset], EDID1_LEN))
++ break;
++
++ searchlen -= offset;
++ offset++;
++ }
++
++ NV_TRACE(dev, "Found EDID in BIOS\n");
++
++ return bios->fp.edid = &bios->data[offset];
++}
++
++void
++nouveau_bios_run_init_table(struct drm_device *dev, uint16_t table,
++ struct dcb_entry *dcbent)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ struct init_exec iexec = { true, false };
++ unsigned long flags;
++
++ spin_lock_irqsave(&bios->lock, flags);
++ bios->display.output = dcbent;
++ parse_init_table(bios, table, &iexec);
++ bios->display.output = NULL;
++ spin_unlock_irqrestore(&bios->lock, flags);
++}
++
++static bool NVInitVBIOS(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++
++ memset(bios, 0, sizeof(struct nvbios));
++ spin_lock_init(&bios->lock);
++ bios->dev = dev;
++
++ if (!NVShadowVBIOS(dev, bios->data))
++ return false;
++
++ bios->length = NV_PROM_SIZE;
++ return true;
++}
++
++static int nouveau_parse_vbios_struct(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ const uint8_t bit_signature[] = { 0xff, 0xb8, 'B', 'I', 'T' };
++ const uint8_t bmp_signature[] = { 0xff, 0x7f, 'N', 'V', 0x0 };
++ int offset;
++
++ offset = findstr(bios->data, bios->length,
++ bit_signature, sizeof(bit_signature));
++ if (offset) {
++ NV_TRACE(dev, "BIT BIOS found\n");
++ return parse_bit_structure(bios, offset + 6);
++ }
++
++ offset = findstr(bios->data, bios->length,
++ bmp_signature, sizeof(bmp_signature));
++ if (offset) {
++ NV_TRACE(dev, "BMP BIOS found\n");
++ return parse_bmp_structure(dev, bios, offset);
++ }
++
++ NV_ERROR(dev, "No known BIOS signature found\n");
++ return -ENODEV;
++}
++
++int
++nouveau_run_vbios_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ int i, ret = 0;
++
++ NVLockVgaCrtcs(dev, false);
++ if (nv_two_heads(dev))
++ NVSetOwner(dev, bios->state.crtchead);
++
++ if (bios->major_version < 5) /* BMP only */
++ load_nv17_hw_sequencer_ucode(dev, bios);
++
++ if (bios->execute) {
++ bios->fp.last_script_invoc = 0;
++ bios->fp.lvds_init_run = false;
++ }
++
++ parse_init_tables(bios);
++
++ /*
++ * Runs some additional script seen on G8x VBIOSen. The VBIOS'
++ * parser will run this right after the init tables, the binary
++ * driver appears to run it at some point later.
++ */
++ if (bios->some_script_ptr) {
++ struct init_exec iexec = {true, false};
++
++ NV_INFO(dev, "Parsing VBIOS init table at offset 0x%04X\n",
++ bios->some_script_ptr);
++ parse_init_table(bios, bios->some_script_ptr, &iexec);
++ }
++
++ if (dev_priv->card_type >= NV_50) {
++ for (i = 0; i < bios->bdcb.dcb.entries; i++) {
++ nouveau_bios_run_display_table(dev,
++ &bios->bdcb.dcb.entry[i],
++ 0, 0);
++ }
++ }
++
++ NVLockVgaCrtcs(dev, true);
++
++ return ret;
++}
++
++static void
++nouveau_bios_i2c_devices_takedown(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ struct dcb_i2c_entry *entry;
++ int i;
++
++ entry = &bios->bdcb.dcb.i2c[0];
++ for (i = 0; i < DCB_MAX_NUM_I2C_ENTRIES; i++, entry++)
++ nouveau_i2c_fini(dev, entry);
++}
++
++int
++nouveau_bios_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ uint32_t saved_nv_pextdev_boot_0;
++ bool was_locked;
++ int ret;
++
++ dev_priv->vbios = &bios->pub;
++
++ if (!NVInitVBIOS(dev))
++ return -ENODEV;
++
++ ret = nouveau_parse_vbios_struct(dev);
++ if (ret)
++ return ret;
++
++ ret = parse_dcb_table(dev, bios, nv_two_heads(dev));
++ if (ret)
++ return ret;
++
++ fixup_legacy_i2c(bios);
++ fixup_legacy_connector(bios);
++
++ if (!bios->major_version) /* we don't run version 0 bios */
++ return 0;
++
++ /* these will need remembering across a suspend */
++ saved_nv_pextdev_boot_0 = bios_rd32(bios, NV_PEXTDEV_BOOT_0);
++ bios->state.saved_nv_pfb_cfg0 = bios_rd32(bios, NV_PFB_CFG0);
++
++ /* init script execution disabled */
++ bios->execute = false;
++
++ /* ... unless card isn't POSTed already */
++ if (dev_priv->card_type >= NV_10 &&
++ NVReadVgaCrtc(dev, 0, 0x00) == 0 &&
++ NVReadVgaCrtc(dev, 0, 0x1a) == 0) {
++ NV_INFO(dev, "Adaptor not initialised\n");
++ if (dev_priv->card_type < NV_50) {
++ NV_ERROR(dev, "Unable to POST this chipset\n");
++ return -ENODEV;
++ }
++
++ NV_INFO(dev, "Running VBIOS init tables\n");
++ bios->execute = true;
++ }
++
++ bios_wr32(bios, NV_PEXTDEV_BOOT_0, saved_nv_pextdev_boot_0);
++
++ ret = nouveau_run_vbios_init(dev);
++ if (ret) {
++ dev_priv->vbios = NULL;
++ return ret;
++ }
++
++ /* feature_byte on BMP is poor, but init always sets CR4B */
++ was_locked = NVLockVgaCrtcs(dev, false);
++ if (bios->major_version < 5)
++ bios->is_mobile = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_4B) & 0x40;
++
++ /* all BIT systems need p_f_m_t for digital_min_front_porch */
++ if (bios->is_mobile || bios->major_version >= 5)
++ ret = parse_fp_mode_table(dev, bios);
++ NVLockVgaCrtcs(dev, was_locked);
++
++ /* allow subsequent scripts to execute */
++ bios->execute = true;
++
++ return 0;
++}
++
++void
++nouveau_bios_takedown(struct drm_device *dev)
++{
++ nouveau_bios_i2c_devices_takedown(dev);
++}
+diff --git a/drivers/gpu/drm/nouveau/nouveau_bios.h b/drivers/gpu/drm/nouveau/nouveau_bios.h
+new file mode 100644
+index 0000000..68446fd
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_bios.h
+@@ -0,0 +1,292 @@
++/*
++ * Copyright 2007-2008 Nouveau Project
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef __NOUVEAU_BIOS_H__
++#define __NOUVEAU_BIOS_H__
++
++#include "nvreg.h"
++#include "nouveau_i2c.h"
++
++#define DCB_MAX_NUM_ENTRIES 16
++#define DCB_MAX_NUM_I2C_ENTRIES 16
++#define DCB_MAX_NUM_GPIO_ENTRIES 32
++#define DCB_MAX_NUM_CONNECTOR_ENTRIES 16
++
++#define DCB_LOC_ON_CHIP 0
++
++struct dcb_entry {
++ int index; /* may not be raw dcb index if merging has happened */
++ uint8_t type;
++ uint8_t i2c_index;
++ uint8_t heads;
++ uint8_t connector;
++ uint8_t bus;
++ uint8_t location;
++ uint8_t or;
++ bool duallink_possible;
++ union {
++ struct sor_conf {
++ int link;
++ } sorconf;
++ struct {
++ int maxfreq;
++ } crtconf;
++ struct {
++ struct sor_conf sor;
++ bool use_straps_for_mode;
++ bool use_power_scripts;
++ } lvdsconf;
++ struct {
++ bool has_component_output;
++ } tvconf;
++ struct {
++ struct sor_conf sor;
++ int link_nr;
++ int link_bw;
++ } dpconf;
++ struct {
++ struct sor_conf sor;
++ } tmdsconf;
++ };
++ bool i2c_upper_default;
++};
++
++struct dcb_i2c_entry {
++ uint8_t port_type;
++ uint8_t read, write;
++ struct nouveau_i2c_chan *chan;
++};
++
++struct parsed_dcb {
++ int entries;
++ struct dcb_entry entry[DCB_MAX_NUM_ENTRIES];
++ struct dcb_i2c_entry i2c[DCB_MAX_NUM_I2C_ENTRIES];
++};
++
++enum dcb_gpio_tag {
++ DCB_GPIO_TVDAC0 = 0xc,
++ DCB_GPIO_TVDAC1 = 0x2d,
++};
++
++struct dcb_gpio_entry {
++ enum dcb_gpio_tag tag;
++ int line;
++ bool invert;
++};
++
++struct parsed_dcb_gpio {
++ int entries;
++ struct dcb_gpio_entry entry[DCB_MAX_NUM_GPIO_ENTRIES];
++};
++
++struct dcb_connector_table_entry {
++ uint32_t entry;
++ uint8_t type;
++ uint8_t index;
++ uint8_t gpio_tag;
++};
++
++struct dcb_connector_table {
++ int entries;
++ struct dcb_connector_table_entry entry[DCB_MAX_NUM_CONNECTOR_ENTRIES];
++};
++
++struct bios_parsed_dcb {
++ uint8_t version;
++
++ struct parsed_dcb dcb;
++
++ uint8_t *i2c_table;
++ uint8_t i2c_default_indices;
++
++ uint16_t gpio_table_ptr;
++ struct parsed_dcb_gpio gpio;
++ uint16_t connector_table_ptr;
++ struct dcb_connector_table connector;
++};
++
++enum nouveau_encoder_type {
++ OUTPUT_ANALOG = 0,
++ OUTPUT_TV = 1,
++ OUTPUT_TMDS = 2,
++ OUTPUT_LVDS = 3,
++ OUTPUT_DP = 6,
++ OUTPUT_ANY = -1
++};
++
++enum nouveau_or {
++ OUTPUT_A = (1 << 0),
++ OUTPUT_B = (1 << 1),
++ OUTPUT_C = (1 << 2)
++};
++
++enum LVDS_script {
++ /* Order *does* matter here */
++ LVDS_INIT = 1,
++ LVDS_RESET,
++ LVDS_BACKLIGHT_ON,
++ LVDS_BACKLIGHT_OFF,
++ LVDS_PANEL_ON,
++ LVDS_PANEL_OFF
++};
++
++/* changing these requires matching changes to reg tables in nv_get_clock */
++#define MAX_PLL_TYPES 4
++enum pll_types {
++ NVPLL,
++ MPLL,
++ VPLL1,
++ VPLL2
++};
++
++struct pll_lims {
++ struct {
++ int minfreq;
++ int maxfreq;
++ int min_inputfreq;
++ int max_inputfreq;
++
++ uint8_t min_m;
++ uint8_t max_m;
++ uint8_t min_n;
++ uint8_t max_n;
++ } vco1, vco2;
++
++ uint8_t max_log2p;
++ /*
++ * for most pre nv50 cards setting a log2P of 7 (the common max_log2p
++ * value) is no different to 6 (at least for vplls) so allowing the MNP
++ * calc to use 7 causes the generated clock to be out by a factor of 2.
++ * however, max_log2p cannot be fixed-up during parsing as the
++ * unmodified max_log2p value is still needed for setting mplls, hence
++ * an additional max_usable_log2p member
++ */
++ uint8_t max_usable_log2p;
++ uint8_t log2p_bias;
++
++ uint8_t min_p;
++ uint8_t max_p;
++
++ int refclk;
++};
++
++struct nouveau_bios_info {
++ struct parsed_dcb *dcb;
++
++ uint8_t chip_version;
++
++ uint32_t dactestval;
++ uint32_t tvdactestval;
++ uint8_t digital_min_front_porch;
++ bool fp_no_ddc;
++};
++
++struct nvbios {
++ struct drm_device *dev;
++ struct nouveau_bios_info pub;
++
++ spinlock_t lock;
++
++ uint8_t data[NV_PROM_SIZE];
++ unsigned int length;
++ bool execute;
++
++ uint8_t major_version;
++ uint8_t feature_byte;
++ bool is_mobile;
++
++ uint32_t fmaxvco, fminvco;
++
++ bool old_style_init;
++ uint16_t init_script_tbls_ptr;
++ uint16_t extra_init_script_tbl_ptr;
++ uint16_t macro_index_tbl_ptr;
++ uint16_t macro_tbl_ptr;
++ uint16_t condition_tbl_ptr;
++ uint16_t io_condition_tbl_ptr;
++ uint16_t io_flag_condition_tbl_ptr;
++ uint16_t init_function_tbl_ptr;
++
++ uint16_t pll_limit_tbl_ptr;
++ uint16_t ram_restrict_tbl_ptr;
++ uint8_t ram_restrict_group_count;
++
++ uint16_t some_script_ptr; /* BIT I + 14 */
++ uint16_t init96_tbl_ptr; /* BIT I + 16 */
++
++ struct bios_parsed_dcb bdcb;
++
++ struct {
++ int crtchead;
++ /* these need remembering across suspend */
++ uint32_t saved_nv_pfb_cfg0;
++ } state;
++
++ struct {
++ struct dcb_entry *output;
++ uint16_t script_table_ptr;
++ uint16_t dp_table_ptr;
++ } display;
++
++ struct {
++ uint16_t fptablepointer; /* also used by tmds */
++ uint16_t fpxlatetableptr;
++ int xlatwidth;
++ uint16_t lvdsmanufacturerpointer;
++ uint16_t fpxlatemanufacturertableptr;
++ uint16_t mode_ptr;
++ uint16_t xlated_entry;
++ bool power_off_for_reset;
++ bool reset_after_pclk_change;
++ bool dual_link;
++ bool link_c_increment;
++ bool BITbit1;
++ bool if_is_24bit;
++ int duallink_transition_clk;
++ uint8_t strapless_is_24bit;
++ uint8_t *edid;
++
++ /* will need resetting after suspend */
++ int last_script_invoc;
++ bool lvds_init_run;
++ } fp;
++
++ struct {
++ uint16_t output0_script_ptr;
++ uint16_t output1_script_ptr;
++ } tmds;
++
++ struct {
++ uint16_t mem_init_tbl_ptr;
++ uint16_t sdr_seq_tbl_ptr;
++ uint16_t ddr_seq_tbl_ptr;
++
++ struct {
++ uint8_t crt, tv, panel;
++ } i2c_indices;
++
++ uint16_t lvds_single_a_script_ptr;
++ } legacy;
++};
++
++#endif
+diff --git a/drivers/gpu/drm/nouveau/nouveau_bo.c b/drivers/gpu/drm/nouveau/nouveau_bo.c
+new file mode 100644
+index 0000000..028719f
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_bo.c
+@@ -0,0 +1,773 @@
++/*
++ * Copyright 2007 Dave Airlied
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ */
++/*
++ * Authors: Dave Airlied <airlied@linux.ie>
++ * Ben Skeggs <darktama@iinet.net.au>
++ * Jeremy Kolb <jkolb@brandeis.edu>
++ */
++
++#include "drmP.h"
++
++#include "nouveau_drm.h"
++#include "nouveau_drv.h"
++#include "nouveau_dma.h"
++
++#include <linux/log2.h>
++
++static void
++nouveau_bo_del_ttm(struct ttm_buffer_object *bo)
++{
++ struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
++ struct drm_device *dev = dev_priv->dev;
++ struct nouveau_bo *nvbo = nouveau_bo(bo);
++
++ ttm_bo_kunmap(&nvbo->kmap);
++
++ if (unlikely(nvbo->gem))
++ DRM_ERROR("bo %p still attached to GEM object\n", bo);
++
++ if (nvbo->tile)
++ nv10_mem_expire_tiling(dev, nvbo->tile, NULL);
++
++ spin_lock(&dev_priv->ttm.bo_list_lock);
++ list_del(&nvbo->head);
++ spin_unlock(&dev_priv->ttm.bo_list_lock);
++ kfree(nvbo);
++}
++
++static void
++nouveau_bo_fixup_align(struct drm_device *dev,
++ uint32_t tile_mode, uint32_t tile_flags,
++ int *align, int *size)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ /*
++ * Some of the tile_flags have a periodic structure of N*4096 bytes,
++ * align to to that as well as the page size. Align the size to the
++ * appropriate boundaries. This does imply that sizes are rounded up
++ * 3-7 pages, so be aware of this and do not waste memory by allocating
++ * many small buffers.
++ */
++ if (dev_priv->card_type == NV_50) {
++ uint32_t block_size = nouveau_mem_fb_amount(dev) >> 15;
++ int i;
++
++ switch (tile_flags) {
++ case 0x1800:
++ case 0x2800:
++ case 0x4800:
++ case 0x7a00:
++ if (is_power_of_2(block_size)) {
++ for (i = 1; i < 10; i++) {
++ *align = 12 * i * block_size;
++ if (!(*align % 65536))
++ break;
++ }
++ } else {
++ for (i = 1; i < 10; i++) {
++ *align = 8 * i * block_size;
++ if (!(*align % 65536))
++ break;
++ }
++ }
++ *size = roundup(*size, *align);
++ break;
++ default:
++ break;
++ }
++
++ } else {
++ if (tile_mode) {
++ if (dev_priv->chipset >= 0x40) {
++ *align = 65536;
++ *size = roundup(*size, 64 * tile_mode);
++
++ } else if (dev_priv->chipset >= 0x30) {
++ *align = 32768;
++ *size = roundup(*size, 64 * tile_mode);
++
++ } else if (dev_priv->chipset >= 0x20) {
++ *align = 16384;
++ *size = roundup(*size, 64 * tile_mode);
++
++ } else if (dev_priv->chipset >= 0x10) {
++ *align = 16384;
++ *size = roundup(*size, 32 * tile_mode);
++ }
++ }
++ }
++
++ /* ALIGN works only on powers of two. */
++ *size = roundup(*size, PAGE_SIZE);
++
++ if (dev_priv->card_type == NV_50) {
++ *size = roundup(*size, 65536);
++ *align = max(65536, *align);
++ }
++}
++
++int
++nouveau_bo_new(struct drm_device *dev, struct nouveau_channel *chan,
++ int size, int align, uint32_t flags, uint32_t tile_mode,
++ uint32_t tile_flags, bool no_vm, bool mappable,
++ struct nouveau_bo **pnvbo)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_bo *nvbo;
++ int ret = 0;
++
++ nvbo = kzalloc(sizeof(struct nouveau_bo), GFP_KERNEL);
++ if (!nvbo)
++ return -ENOMEM;
++ INIT_LIST_HEAD(&nvbo->head);
++ INIT_LIST_HEAD(&nvbo->entry);
++ nvbo->mappable = mappable;
++ nvbo->no_vm = no_vm;
++ nvbo->tile_mode = tile_mode;
++ nvbo->tile_flags = tile_flags;
++
++ nouveau_bo_fixup_align(dev, tile_mode, tile_flags, &align, &size);
++ align >>= PAGE_SHIFT;
++
++ nvbo->placement.fpfn = 0;
++ nvbo->placement.lpfn = mappable ? dev_priv->fb_mappable_pages : 0;
++ nouveau_bo_placement_set(nvbo, flags);
++
++ nvbo->channel = chan;
++ ret = ttm_bo_init(&dev_priv->ttm.bdev, &nvbo->bo, size,
++ ttm_bo_type_device, &nvbo->placement, align, 0,
++ false, NULL, size, nouveau_bo_del_ttm);
++ nvbo->channel = NULL;
++ if (ret) {
++ /* ttm will call nouveau_bo_del_ttm if it fails.. */
++ return ret;
++ }
++
++ spin_lock(&dev_priv->ttm.bo_list_lock);
++ list_add_tail(&nvbo->head, &dev_priv->ttm.bo_list);
++ spin_unlock(&dev_priv->ttm.bo_list_lock);
++ *pnvbo = nvbo;
++ return 0;
++}
++
++void
++nouveau_bo_placement_set(struct nouveau_bo *nvbo, uint32_t memtype)
++{
++ int n = 0;
++
++ if (memtype & TTM_PL_FLAG_VRAM)
++ nvbo->placements[n++] = TTM_PL_FLAG_VRAM | TTM_PL_MASK_CACHING;
++ if (memtype & TTM_PL_FLAG_TT)
++ nvbo->placements[n++] = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
++ if (memtype & TTM_PL_FLAG_SYSTEM)
++ nvbo->placements[n++] = TTM_PL_FLAG_SYSTEM | TTM_PL_MASK_CACHING;
++ nvbo->placement.placement = nvbo->placements;
++ nvbo->placement.busy_placement = nvbo->placements;
++ nvbo->placement.num_placement = n;
++ nvbo->placement.num_busy_placement = n;
++
++ if (nvbo->pin_refcnt) {
++ while (n--)
++ nvbo->placements[n] |= TTM_PL_FLAG_NO_EVICT;
++ }
++}
++
++int
++nouveau_bo_pin(struct nouveau_bo *nvbo, uint32_t memtype)
++{
++ struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev);
++ struct ttm_buffer_object *bo = &nvbo->bo;
++ int ret, i;
++
++ if (nvbo->pin_refcnt && !(memtype & (1 << bo->mem.mem_type))) {
++ NV_ERROR(nouveau_bdev(bo->bdev)->dev,
++ "bo %p pinned elsewhere: 0x%08x vs 0x%08x\n", bo,
++ 1 << bo->mem.mem_type, memtype);
++ return -EINVAL;
++ }
++
++ if (nvbo->pin_refcnt++)
++ return 0;
++
++ ret = ttm_bo_reserve(bo, false, false, false, 0);
++ if (ret)
++ goto out;
++
++ nouveau_bo_placement_set(nvbo, memtype);
++ for (i = 0; i < nvbo->placement.num_placement; i++)
++ nvbo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
++
++ ret = ttm_bo_validate(bo, &nvbo->placement, false, false);
++ if (ret == 0) {
++ switch (bo->mem.mem_type) {
++ case TTM_PL_VRAM:
++ dev_priv->fb_aper_free -= bo->mem.size;
++ break;
++ case TTM_PL_TT:
++ dev_priv->gart_info.aper_free -= bo->mem.size;
++ break;
++ default:
++ break;
++ }
++ }
++ ttm_bo_unreserve(bo);
++out:
++ if (unlikely(ret))
++ nvbo->pin_refcnt--;
++ return ret;
++}
++
++int
++nouveau_bo_unpin(struct nouveau_bo *nvbo)
++{
++ struct drm_nouveau_private *dev_priv = nouveau_bdev(nvbo->bo.bdev);
++ struct ttm_buffer_object *bo = &nvbo->bo;
++ int ret, i;
++
++ if (--nvbo->pin_refcnt)
++ return 0;
++
++ ret = ttm_bo_reserve(bo, false, false, false, 0);
++ if (ret)
++ return ret;
++
++ for (i = 0; i < nvbo->placement.num_placement; i++)
++ nvbo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT;
++
++ ret = ttm_bo_validate(bo, &nvbo->placement, false, false);
++ if (ret == 0) {
++ switch (bo->mem.mem_type) {
++ case TTM_PL_VRAM:
++ dev_priv->fb_aper_free += bo->mem.size;
++ break;
++ case TTM_PL_TT:
++ dev_priv->gart_info.aper_free += bo->mem.size;
++ break;
++ default:
++ break;
++ }
++ }
++
++ ttm_bo_unreserve(bo);
++ return ret;
++}
++
++int
++nouveau_bo_map(struct nouveau_bo *nvbo)
++{
++ int ret;
++
++ ret = ttm_bo_reserve(&nvbo->bo, false, false, false, 0);
++ if (ret)
++ return ret;
++
++ ret = ttm_bo_kmap(&nvbo->bo, 0, nvbo->bo.mem.num_pages, &nvbo->kmap);
++ ttm_bo_unreserve(&nvbo->bo);
++ return ret;
++}
++
++void
++nouveau_bo_unmap(struct nouveau_bo *nvbo)
++{
++ ttm_bo_kunmap(&nvbo->kmap);
++}
++
++u16
++nouveau_bo_rd16(struct nouveau_bo *nvbo, unsigned index)
++{
++ bool is_iomem;
++ u16 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem);
++ mem = &mem[index];
++ if (is_iomem)
++ return ioread16_native((void __force __iomem *)mem);
++ else
++ return *mem;
++}
++
++void
++nouveau_bo_wr16(struct nouveau_bo *nvbo, unsigned index, u16 val)
++{
++ bool is_iomem;
++ u16 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem);
++ mem = &mem[index];
++ if (is_iomem)
++ iowrite16_native(val, (void __force __iomem *)mem);
++ else
++ *mem = val;
++}
++
++u32
++nouveau_bo_rd32(struct nouveau_bo *nvbo, unsigned index)
++{
++ bool is_iomem;
++ u32 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem);
++ mem = &mem[index];
++ if (is_iomem)
++ return ioread32_native((void __force __iomem *)mem);
++ else
++ return *mem;
++}
++
++void
++nouveau_bo_wr32(struct nouveau_bo *nvbo, unsigned index, u32 val)
++{
++ bool is_iomem;
++ u32 *mem = ttm_kmap_obj_virtual(&nvbo->kmap, &is_iomem);
++ mem = &mem[index];
++ if (is_iomem)
++ iowrite32_native(val, (void __force __iomem *)mem);
++ else
++ *mem = val;
++}
++
++static struct ttm_backend *
++nouveau_bo_create_ttm_backend_entry(struct ttm_bo_device *bdev)
++{
++ struct drm_nouveau_private *dev_priv = nouveau_bdev(bdev);
++ struct drm_device *dev = dev_priv->dev;
++
++ switch (dev_priv->gart_info.type) {
++#if __OS_HAS_AGP
++ case NOUVEAU_GART_AGP:
++ return ttm_agp_backend_init(bdev, dev->agp->bridge);
++#endif
++ case NOUVEAU_GART_SGDMA:
++ return nouveau_sgdma_init_ttm(dev);
++ default:
++ NV_ERROR(dev, "Unknown GART type %d\n",
++ dev_priv->gart_info.type);
++ break;
++ }
++
++ return NULL;
++}
++
++static int
++nouveau_bo_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags)
++{
++ /* We'll do this from user space. */
++ return 0;
++}
++
++static int
++nouveau_bo_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
++ struct ttm_mem_type_manager *man)
++{
++ struct drm_nouveau_private *dev_priv = nouveau_bdev(bdev);
++ struct drm_device *dev = dev_priv->dev;
++
++ switch (type) {
++ case TTM_PL_SYSTEM:
++ man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
++ man->available_caching = TTM_PL_MASK_CACHING;
++ man->default_caching = TTM_PL_FLAG_CACHED;
++ break;
++ case TTM_PL_VRAM:
++ man->flags = TTM_MEMTYPE_FLAG_FIXED |
++ TTM_MEMTYPE_FLAG_MAPPABLE |
++ TTM_MEMTYPE_FLAG_NEEDS_IOREMAP;
++ man->available_caching = TTM_PL_FLAG_UNCACHED |
++ TTM_PL_FLAG_WC;
++ man->default_caching = TTM_PL_FLAG_WC;
++
++ man->io_addr = NULL;
++ man->io_offset = drm_get_resource_start(dev, 1);
++ man->io_size = drm_get_resource_len(dev, 1);
++ if (man->io_size > nouveau_mem_fb_amount(dev))
++ man->io_size = nouveau_mem_fb_amount(dev);
++
++ man->gpu_offset = dev_priv->vm_vram_base;
++ break;
++ case TTM_PL_TT:
++ switch (dev_priv->gart_info.type) {
++ case NOUVEAU_GART_AGP:
++ man->flags = TTM_MEMTYPE_FLAG_MAPPABLE |
++ TTM_MEMTYPE_FLAG_NEEDS_IOREMAP;
++ man->available_caching = TTM_PL_FLAG_UNCACHED;
++ man->default_caching = TTM_PL_FLAG_UNCACHED;
++ break;
++ case NOUVEAU_GART_SGDMA:
++ man->flags = TTM_MEMTYPE_FLAG_MAPPABLE |
++ TTM_MEMTYPE_FLAG_CMA;
++ man->available_caching = TTM_PL_MASK_CACHING;
++ man->default_caching = TTM_PL_FLAG_CACHED;
++ break;
++ default:
++ NV_ERROR(dev, "Unknown GART type: %d\n",
++ dev_priv->gart_info.type);
++ return -EINVAL;
++ }
++
++ man->io_offset = dev_priv->gart_info.aper_base;
++ man->io_size = dev_priv->gart_info.aper_size;
++ man->io_addr = NULL;
++ man->gpu_offset = dev_priv->vm_gart_base;
++ break;
++ default:
++ NV_ERROR(dev, "Unsupported memory type %u\n", (unsigned)type);
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static void
++nouveau_bo_evict_flags(struct ttm_buffer_object *bo, struct ttm_placement *pl)
++{
++ struct nouveau_bo *nvbo = nouveau_bo(bo);
++
++ switch (bo->mem.mem_type) {
++ case TTM_PL_VRAM:
++ nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_TT |
++ TTM_PL_FLAG_SYSTEM);
++ break;
++ default:
++ nouveau_bo_placement_set(nvbo, TTM_PL_FLAG_SYSTEM);
++ break;
++ }
++
++ *pl = nvbo->placement;
++}
++
++
++/* GPU-assisted copy using NV_MEMORY_TO_MEMORY_FORMAT, can access
++ * TTM_PL_{VRAM,TT} directly.
++ */
++
++static int
++nouveau_bo_move_accel_cleanup(struct nouveau_channel *chan,
++ struct nouveau_bo *nvbo, bool evict, bool no_wait,
++ struct ttm_mem_reg *new_mem)
++{
++ struct nouveau_fence *fence = NULL;
++ int ret;
++
++ ret = nouveau_fence_new(chan, &fence, true);
++ if (ret)
++ return ret;
++
++ ret = ttm_bo_move_accel_cleanup(&nvbo->bo, fence, NULL,
++ evict, no_wait, new_mem);
++ if (nvbo->channel && nvbo->channel != chan)
++ ret = nouveau_fence_wait(fence, NULL, false, false);
++ nouveau_fence_unref((void *)&fence);
++ return ret;
++}
++
++static inline uint32_t
++nouveau_bo_mem_ctxdma(struct nouveau_bo *nvbo, struct nouveau_channel *chan,
++ struct ttm_mem_reg *mem)
++{
++ if (chan == nouveau_bdev(nvbo->bo.bdev)->channel) {
++ if (mem->mem_type == TTM_PL_TT)
++ return NvDmaGART;
++ return NvDmaVRAM;
++ }
++
++ if (mem->mem_type == TTM_PL_TT)
++ return chan->gart_handle;
++ return chan->vram_handle;
++}
++
++static int
++nouveau_bo_move_m2mf(struct ttm_buffer_object *bo, int evict, bool intr,
++ int no_wait, struct ttm_mem_reg *new_mem)
++{
++ struct nouveau_bo *nvbo = nouveau_bo(bo);
++ struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
++ struct ttm_mem_reg *old_mem = &bo->mem;
++ struct nouveau_channel *chan;
++ uint64_t src_offset, dst_offset;
++ uint32_t page_count;
++ int ret;
++
++ chan = nvbo->channel;
++ if (!chan || nvbo->tile_flags || nvbo->no_vm)
++ chan = dev_priv->channel;
++
++ src_offset = old_mem->mm_node->start << PAGE_SHIFT;
++ dst_offset = new_mem->mm_node->start << PAGE_SHIFT;
++ if (chan != dev_priv->channel) {
++ if (old_mem->mem_type == TTM_PL_TT)
++ src_offset += dev_priv->vm_gart_base;
++ else
++ src_offset += dev_priv->vm_vram_base;
++
++ if (new_mem->mem_type == TTM_PL_TT)
++ dst_offset += dev_priv->vm_gart_base;
++ else
++ dst_offset += dev_priv->vm_vram_base;
++ }
++
++ ret = RING_SPACE(chan, 3);
++ if (ret)
++ return ret;
++ BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_SOURCE, 2);
++ OUT_RING(chan, nouveau_bo_mem_ctxdma(nvbo, chan, old_mem));
++ OUT_RING(chan, nouveau_bo_mem_ctxdma(nvbo, chan, new_mem));
++
++ if (dev_priv->card_type >= NV_50) {
++ ret = RING_SPACE(chan, 4);
++ if (ret)
++ return ret;
++ BEGIN_RING(chan, NvSubM2MF, 0x0200, 1);
++ OUT_RING(chan, 1);
++ BEGIN_RING(chan, NvSubM2MF, 0x021c, 1);
++ OUT_RING(chan, 1);
++ }
++
++ page_count = new_mem->num_pages;
++ while (page_count) {
++ int line_count = (page_count > 2047) ? 2047 : page_count;
++
++ if (dev_priv->card_type >= NV_50) {
++ ret = RING_SPACE(chan, 3);
++ if (ret)
++ return ret;
++ BEGIN_RING(chan, NvSubM2MF, 0x0238, 2);
++ OUT_RING(chan, upper_32_bits(src_offset));
++ OUT_RING(chan, upper_32_bits(dst_offset));
++ }
++ ret = RING_SPACE(chan, 11);
++ if (ret)
++ return ret;
++ BEGIN_RING(chan, NvSubM2MF,
++ NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN, 8);
++ OUT_RING(chan, lower_32_bits(src_offset));
++ OUT_RING(chan, lower_32_bits(dst_offset));
++ OUT_RING(chan, PAGE_SIZE); /* src_pitch */
++ OUT_RING(chan, PAGE_SIZE); /* dst_pitch */
++ OUT_RING(chan, PAGE_SIZE); /* line_length */
++ OUT_RING(chan, line_count);
++ OUT_RING(chan, (1<<8)|(1<<0));
++ OUT_RING(chan, 0);
++ BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NOP, 1);
++ OUT_RING(chan, 0);
++
++ page_count -= line_count;
++ src_offset += (PAGE_SIZE * line_count);
++ dst_offset += (PAGE_SIZE * line_count);
++ }
++
++ return nouveau_bo_move_accel_cleanup(chan, nvbo, evict, no_wait, new_mem);
++}
++
++static int
++nouveau_bo_move_flipd(struct ttm_buffer_object *bo, bool evict, bool intr,
++ bool no_wait, struct ttm_mem_reg *new_mem)
++{
++ u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
++ struct ttm_placement placement;
++ struct ttm_mem_reg tmp_mem;
++ int ret;
++
++ placement.fpfn = placement.lpfn = 0;
++ placement.num_placement = placement.num_busy_placement = 1;
++ placement.placement = placement.busy_placement = &placement_memtype;
++
++ tmp_mem = *new_mem;
++ tmp_mem.mm_node = NULL;
++ ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, intr, no_wait);
++ if (ret)
++ return ret;
++
++ ret = ttm_tt_bind(bo->ttm, &tmp_mem);
++ if (ret)
++ goto out;
++
++ ret = nouveau_bo_move_m2mf(bo, true, intr, no_wait, &tmp_mem);
++ if (ret)
++ goto out;
++
++ ret = ttm_bo_move_ttm(bo, evict, no_wait, new_mem);
++out:
++ if (tmp_mem.mm_node) {
++ spin_lock(&bo->bdev->glob->lru_lock);
++ drm_mm_put_block(tmp_mem.mm_node);
++ spin_unlock(&bo->bdev->glob->lru_lock);
++ }
++
++ return ret;
++}
++
++static int
++nouveau_bo_move_flips(struct ttm_buffer_object *bo, bool evict, bool intr,
++ bool no_wait, struct ttm_mem_reg *new_mem)
++{
++ u32 placement_memtype = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
++ struct ttm_placement placement;
++ struct ttm_mem_reg tmp_mem;
++ int ret;
++
++ placement.fpfn = placement.lpfn = 0;
++ placement.num_placement = placement.num_busy_placement = 1;
++ placement.placement = placement.busy_placement = &placement_memtype;
++
++ tmp_mem = *new_mem;
++ tmp_mem.mm_node = NULL;
++ ret = ttm_bo_mem_space(bo, &placement, &tmp_mem, intr, no_wait);
++ if (ret)
++ return ret;
++
++ ret = ttm_bo_move_ttm(bo, evict, no_wait, &tmp_mem);
++ if (ret)
++ goto out;
++
++ ret = nouveau_bo_move_m2mf(bo, evict, intr, no_wait, new_mem);
++ if (ret)
++ goto out;
++
++out:
++ if (tmp_mem.mm_node) {
++ spin_lock(&bo->bdev->glob->lru_lock);
++ drm_mm_put_block(tmp_mem.mm_node);
++ spin_unlock(&bo->bdev->glob->lru_lock);
++ }
++
++ return ret;
++}
++
++static int
++nouveau_bo_vm_bind(struct ttm_buffer_object *bo, struct ttm_mem_reg *new_mem,
++ struct nouveau_tile_reg **new_tile)
++{
++ struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
++ struct drm_device *dev = dev_priv->dev;
++ struct nouveau_bo *nvbo = nouveau_bo(bo);
++ uint64_t offset;
++ int ret;
++
++ if (nvbo->no_vm || new_mem->mem_type != TTM_PL_VRAM) {
++ /* Nothing to do. */
++ *new_tile = NULL;
++ return 0;
++ }
++
++ offset = new_mem->mm_node->start << PAGE_SHIFT;
++
++ if (dev_priv->card_type == NV_50) {
++ ret = nv50_mem_vm_bind_linear(dev,
++ offset + dev_priv->vm_vram_base,
++ new_mem->size, nvbo->tile_flags,
++ offset);
++ if (ret)
++ return ret;
++
++ } else if (dev_priv->card_type >= NV_10) {
++ *new_tile = nv10_mem_set_tiling(dev, offset, new_mem->size,
++ nvbo->tile_mode);
++ }
++
++ return 0;
++}
++
++static void
++nouveau_bo_vm_cleanup(struct ttm_buffer_object *bo,
++ struct nouveau_tile_reg *new_tile,
++ struct nouveau_tile_reg **old_tile)
++{
++ struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
++ struct drm_device *dev = dev_priv->dev;
++
++ if (dev_priv->card_type >= NV_10 &&
++ dev_priv->card_type < NV_50) {
++ if (*old_tile)
++ nv10_mem_expire_tiling(dev, *old_tile, bo->sync_obj);
++
++ *old_tile = new_tile;
++ }
++}
++
++static int
++nouveau_bo_move(struct ttm_buffer_object *bo, bool evict, bool intr,
++ bool no_wait, struct ttm_mem_reg *new_mem)
++{
++ struct drm_nouveau_private *dev_priv = nouveau_bdev(bo->bdev);
++ struct nouveau_bo *nvbo = nouveau_bo(bo);
++ struct ttm_mem_reg *old_mem = &bo->mem;
++ struct nouveau_tile_reg *new_tile = NULL;
++ int ret = 0;
++
++ ret = nouveau_bo_vm_bind(bo, new_mem, &new_tile);
++ if (ret)
++ return ret;
++
++ /* Software copy if the card isn't up and running yet. */
++ if (dev_priv->init_state != NOUVEAU_CARD_INIT_DONE ||
++ !dev_priv->channel) {
++ ret = ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
++ goto out;
++ }
++
++ /* Fake bo copy. */
++ if (old_mem->mem_type == TTM_PL_SYSTEM && !bo->ttm) {
++ BUG_ON(bo->mem.mm_node != NULL);
++ bo->mem = *new_mem;
++ new_mem->mm_node = NULL;
++ goto out;
++ }
++
++ /* Hardware assisted copy. */
++ if (new_mem->mem_type == TTM_PL_SYSTEM)
++ ret = nouveau_bo_move_flipd(bo, evict, intr, no_wait, new_mem);
++ else if (old_mem->mem_type == TTM_PL_SYSTEM)
++ ret = nouveau_bo_move_flips(bo, evict, intr, no_wait, new_mem);
++ else
++ ret = nouveau_bo_move_m2mf(bo, evict, intr, no_wait, new_mem);
++
++ if (!ret)
++ goto out;
++
++ /* Fallback to software copy. */
++ ret = ttm_bo_move_memcpy(bo, evict, no_wait, new_mem);
++
++out:
++ if (ret)
++ nouveau_bo_vm_cleanup(bo, NULL, &new_tile);
++ else
++ nouveau_bo_vm_cleanup(bo, new_tile, &nvbo->tile);
++
++ return ret;
++}
++
++static int
++nouveau_bo_verify_access(struct ttm_buffer_object *bo, struct file *filp)
++{
++ return 0;
++}
++
++struct ttm_bo_driver nouveau_bo_driver = {
++ .create_ttm_backend_entry = nouveau_bo_create_ttm_backend_entry,
++ .invalidate_caches = nouveau_bo_invalidate_caches,
++ .init_mem_type = nouveau_bo_init_mem_type,
++ .evict_flags = nouveau_bo_evict_flags,
++ .move = nouveau_bo_move,
++ .verify_access = nouveau_bo_verify_access,
++ .sync_obj_signaled = nouveau_fence_signalled,
++ .sync_obj_wait = nouveau_fence_wait,
++ .sync_obj_flush = nouveau_fence_flush,
++ .sync_obj_unref = nouveau_fence_unref,
++ .sync_obj_ref = nouveau_fence_ref,
++};
++
+diff --git a/drivers/gpu/drm/nouveau/nouveau_calc.c b/drivers/gpu/drm/nouveau/nouveau_calc.c
+new file mode 100644
+index 0000000..ee2b845
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_calc.c
+@@ -0,0 +1,478 @@
++/*
++ * Copyright 1993-2003 NVIDIA, Corporation
++ * Copyright 2007-2009 Stuart Bennett
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
++ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ */
++
++#include "drmP.h"
++#include "nouveau_drv.h"
++#include "nouveau_hw.h"
++
++/****************************************************************************\
++* *
++* The video arbitration routines calculate some "magic" numbers. Fixes *
++* the snow seen when accessing the framebuffer without it. *
++* It just works (I hope). *
++* *
++\****************************************************************************/
++
++struct nv_fifo_info {
++ int lwm;
++ int burst;
++};
++
++struct nv_sim_state {
++ int pclk_khz;
++ int mclk_khz;
++ int nvclk_khz;
++ int bpp;
++ int mem_page_miss;
++ int mem_latency;
++ int memory_type;
++ int memory_width;
++ int two_heads;
++};
++
++static void
++nv04_calc_arb(struct nv_fifo_info *fifo, struct nv_sim_state *arb)
++{
++ int pagemiss, cas, width, bpp;
++ int nvclks, mclks, pclks, crtpagemiss;
++ int found, mclk_extra, mclk_loop, cbs, m1, p1;
++ int mclk_freq, pclk_freq, nvclk_freq;
++ int us_m, us_n, us_p, crtc_drain_rate;
++ int cpm_us, us_crt, clwm;
++
++ pclk_freq = arb->pclk_khz;
++ mclk_freq = arb->mclk_khz;
++ nvclk_freq = arb->nvclk_khz;
++ pagemiss = arb->mem_page_miss;
++ cas = arb->mem_latency;
++ width = arb->memory_width >> 6;
++ bpp = arb->bpp;
++ cbs = 128;
++
++ pclks = 2;
++ nvclks = 10;
++ mclks = 13 + cas;
++ mclk_extra = 3;
++ found = 0;
++
++ while (!found) {
++ found = 1;
++
++ mclk_loop = mclks + mclk_extra;
++ us_m = mclk_loop * 1000 * 1000 / mclk_freq;
++ us_n = nvclks * 1000 * 1000 / nvclk_freq;
++ us_p = nvclks * 1000 * 1000 / pclk_freq;
++
++ crtc_drain_rate = pclk_freq * bpp / 8;
++ crtpagemiss = 2;
++ crtpagemiss += 1;
++ cpm_us = crtpagemiss * pagemiss * 1000 * 1000 / mclk_freq;
++ us_crt = cpm_us + us_m + us_n + us_p;
++ clwm = us_crt * crtc_drain_rate / (1000 * 1000);
++ clwm++;
++
++ m1 = clwm + cbs - 512;
++ p1 = m1 * pclk_freq / mclk_freq;
++ p1 = p1 * bpp / 8;
++ if ((p1 < m1 && m1 > 0) || clwm > 519) {
++ found = !mclk_extra;
++ mclk_extra--;
++ }
++ if (clwm < 384)
++ clwm = 384;
++
++ fifo->lwm = clwm;
++ fifo->burst = cbs;
++ }
++}
++
++static void
++nv10_calc_arb(struct nv_fifo_info *fifo, struct nv_sim_state *arb)
++{
++ int fill_rate, drain_rate;
++ int pclks, nvclks, mclks, xclks;
++ int pclk_freq, nvclk_freq, mclk_freq;
++ int fill_lat, extra_lat;
++ int max_burst_o, max_burst_l;
++ int fifo_len, min_lwm, max_lwm;
++ const int burst_lat = 80; /* Maximum allowable latency due
++ * to the CRTC FIFO burst. (ns) */
++
++ pclk_freq = arb->pclk_khz;
++ nvclk_freq = arb->nvclk_khz;
++ mclk_freq = arb->mclk_khz;
++
++ fill_rate = mclk_freq * arb->memory_width / 8; /* kB/s */
++ drain_rate = pclk_freq * arb->bpp / 8; /* kB/s */
++
++ fifo_len = arb->two_heads ? 1536 : 1024; /* B */
++
++ /* Fixed FIFO refill latency. */
++
++ pclks = 4; /* lwm detect. */
++
++ nvclks = 3 /* lwm -> sync. */
++ + 2 /* fbi bus cycles (1 req + 1 busy) */
++ + 1 /* 2 edge sync. may be very close to edge so
++ * just put one. */
++ + 1 /* fbi_d_rdv_n */
++ + 1 /* Fbi_d_rdata */
++ + 1; /* crtfifo load */
++
++ mclks = 1 /* 2 edge sync. may be very close to edge so
++ * just put one. */
++ + 1 /* arb_hp_req */
++ + 5 /* tiling pipeline */
++ + 2 /* latency fifo */
++ + 2 /* memory request to fbio block */
++ + 7; /* data returned from fbio block */
++
++ /* Need to accumulate 256 bits for read */
++ mclks += (arb->memory_type == 0 ? 2 : 1)
++ * arb->memory_width / 32;
++
++ fill_lat = mclks * 1000 * 1000 / mclk_freq /* minimum mclk latency */
++ + nvclks * 1000 * 1000 / nvclk_freq /* nvclk latency */
++ + pclks * 1000 * 1000 / pclk_freq; /* pclk latency */
++
++ /* Conditional FIFO refill latency. */
++
++ xclks = 2 * arb->mem_page_miss + mclks /* Extra latency due to
++ * the overlay. */
++ + 2 * arb->mem_page_miss /* Extra pagemiss latency. */
++ + (arb->bpp == 32 ? 8 : 4); /* Margin of error. */
++
++ extra_lat = xclks * 1000 * 1000 / mclk_freq;
++
++ if (arb->two_heads)
++ /* Account for another CRTC. */
++ extra_lat += fill_lat + extra_lat + burst_lat;
++
++ /* FIFO burst */
++
++ /* Max burst not leading to overflows. */
++ max_burst_o = (1 + fifo_len - extra_lat * drain_rate / (1000 * 1000))
++ * (fill_rate / 1000) / ((fill_rate - drain_rate) / 1000);
++ fifo->burst = min(max_burst_o, 1024);
++
++ /* Max burst value with an acceptable latency. */
++ max_burst_l = burst_lat * fill_rate / (1000 * 1000);
++ fifo->burst = min(max_burst_l, fifo->burst);
++
++ fifo->burst = rounddown_pow_of_two(fifo->burst);
++
++ /* FIFO low watermark */
++
++ min_lwm = (fill_lat + extra_lat) * drain_rate / (1000 * 1000) + 1;
++ max_lwm = fifo_len - fifo->burst
++ + fill_lat * drain_rate / (1000 * 1000)
++ + fifo->burst * drain_rate / fill_rate;
++
++ fifo->lwm = min_lwm + 10 * (max_lwm - min_lwm) / 100; /* Empirical. */
++}
++
++static void
++nv04_update_arb(struct drm_device *dev, int VClk, int bpp,
++ int *burst, int *lwm)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv_fifo_info fifo_data;
++ struct nv_sim_state sim_data;
++ int MClk = nouveau_hw_get_clock(dev, MPLL);
++ int NVClk = nouveau_hw_get_clock(dev, NVPLL);
++ uint32_t cfg1 = nvReadFB(dev, NV_PFB_CFG1);
++
++ sim_data.pclk_khz = VClk;
++ sim_data.mclk_khz = MClk;
++ sim_data.nvclk_khz = NVClk;
++ sim_data.bpp = bpp;
++ sim_data.two_heads = nv_two_heads(dev);
++ if ((dev->pci_device & 0xffff) == 0x01a0 /*CHIPSET_NFORCE*/ ||
++ (dev->pci_device & 0xffff) == 0x01f0 /*CHIPSET_NFORCE2*/) {
++ uint32_t type;
++
++ pci_read_config_dword(pci_get_bus_and_slot(0, 1), 0x7c, &type);
++
++ sim_data.memory_type = (type >> 12) & 1;
++ sim_data.memory_width = 64;
++ sim_data.mem_latency = 3;
++ sim_data.mem_page_miss = 10;
++ } else {
++ sim_data.memory_type = nvReadFB(dev, NV_PFB_CFG0) & 0x1;
++ sim_data.memory_width = (nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) & 0x10) ? 128 : 64;
++ sim_data.mem_latency = cfg1 & 0xf;
++ sim_data.mem_page_miss = ((cfg1 >> 4) & 0xf) + ((cfg1 >> 31) & 0x1);
++ }
++
++ if (dev_priv->card_type == NV_04)
++ nv04_calc_arb(&fifo_data, &sim_data);
++ else
++ nv10_calc_arb(&fifo_data, &sim_data);
++
++ *burst = ilog2(fifo_data.burst >> 4);
++ *lwm = fifo_data.lwm >> 3;
++}
++
++static void
++nv30_update_arb(int *burst, int *lwm)
++{
++ unsigned int fifo_size, burst_size, graphics_lwm;
++
++ fifo_size = 2048;
++ burst_size = 512;
++ graphics_lwm = fifo_size - burst_size;
++
++ *burst = ilog2(burst_size >> 5);
++ *lwm = graphics_lwm >> 3;
++}
++
++void
++nouveau_calc_arb(struct drm_device *dev, int vclk, int bpp, int *burst, int *lwm)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ if (dev_priv->card_type < NV_30)
++ nv04_update_arb(dev, vclk, bpp, burst, lwm);
++ else if ((dev->pci_device & 0xfff0) == 0x0240 /*CHIPSET_C51*/ ||
++ (dev->pci_device & 0xfff0) == 0x03d0 /*CHIPSET_C512*/) {
++ *burst = 128;
++ *lwm = 0x0480;
++ } else
++ nv30_update_arb(burst, lwm);
++}
++
++static int
++getMNP_single(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
++ struct nouveau_pll_vals *bestpv)
++{
++ /* Find M, N and P for a single stage PLL
++ *
++ * Note that some bioses (NV3x) have lookup tables of precomputed MNP
++ * values, but we're too lazy to use those atm
++ *
++ * "clk" parameter in kHz
++ * returns calculated clock
++ */
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int cv = dev_priv->vbios->chip_version;
++ int minvco = pll_lim->vco1.minfreq, maxvco = pll_lim->vco1.maxfreq;
++ int minM = pll_lim->vco1.min_m, maxM = pll_lim->vco1.max_m;
++ int minN = pll_lim->vco1.min_n, maxN = pll_lim->vco1.max_n;
++ int minU = pll_lim->vco1.min_inputfreq;
++ int maxU = pll_lim->vco1.max_inputfreq;
++ int minP = pll_lim->max_p ? pll_lim->min_p : 0;
++ int maxP = pll_lim->max_p ? pll_lim->max_p : pll_lim->max_usable_log2p;
++ int crystal = pll_lim->refclk;
++ int M, N, thisP, P;
++ int clkP, calcclk;
++ int delta, bestdelta = INT_MAX;
++ int bestclk = 0;
++
++ /* this division verified for nv20, nv18, nv28 (Haiku), and nv34 */
++ /* possibly correlated with introduction of 27MHz crystal */
++ if (dev_priv->card_type < NV_50) {
++ if (cv < 0x17 || cv == 0x1a || cv == 0x20) {
++ if (clk > 250000)
++ maxM = 6;
++ if (clk > 340000)
++ maxM = 2;
++ } else if (cv < 0x40) {
++ if (clk > 150000)
++ maxM = 6;
++ if (clk > 200000)
++ maxM = 4;
++ if (clk > 340000)
++ maxM = 2;
++ }
++ }
++
++ P = pll_lim->max_p ? maxP : (1 << maxP);
++ if ((clk * P) < minvco) {
++ minvco = clk * maxP;
++ maxvco = minvco * 2;
++ }
++
++ if (clk + clk/200 > maxvco) /* +0.5% */
++ maxvco = clk + clk/200;
++
++ /* NV34 goes maxlog2P->0, NV20 goes 0->maxlog2P */
++ for (thisP = minP; thisP <= maxP; thisP++) {
++ P = pll_lim->max_p ? thisP : (1 << thisP);
++ clkP = clk * P;
++
++ if (clkP < minvco)
++ continue;
++ if (clkP > maxvco)
++ return bestclk;
++
++ for (M = minM; M <= maxM; M++) {
++ if (crystal/M < minU)
++ return bestclk;
++ if (crystal/M > maxU)
++ continue;
++
++ /* add crystal/2 to round better */
++ N = (clkP * M + crystal/2) / crystal;
++
++ if (N < minN)
++ continue;
++ if (N > maxN)
++ break;
++
++ /* more rounding additions */
++ calcclk = ((N * crystal + P/2) / P + M/2) / M;
++ delta = abs(calcclk - clk);
++ /* we do an exhaustive search rather than terminating
++ * on an optimality condition...
++ */
++ if (delta < bestdelta) {
++ bestdelta = delta;
++ bestclk = calcclk;
++ bestpv->N1 = N;
++ bestpv->M1 = M;
++ bestpv->log2P = thisP;
++ if (delta == 0) /* except this one */
++ return bestclk;
++ }
++ }
++ }
++
++ return bestclk;
++}
++
++static int
++getMNP_double(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
++ struct nouveau_pll_vals *bestpv)
++{
++ /* Find M, N and P for a two stage PLL
++ *
++ * Note that some bioses (NV30+) have lookup tables of precomputed MNP
++ * values, but we're too lazy to use those atm
++ *
++ * "clk" parameter in kHz
++ * returns calculated clock
++ */
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int chip_version = dev_priv->vbios->chip_version;
++ int minvco1 = pll_lim->vco1.minfreq, maxvco1 = pll_lim->vco1.maxfreq;
++ int minvco2 = pll_lim->vco2.minfreq, maxvco2 = pll_lim->vco2.maxfreq;
++ int minU1 = pll_lim->vco1.min_inputfreq, minU2 = pll_lim->vco2.min_inputfreq;
++ int maxU1 = pll_lim->vco1.max_inputfreq, maxU2 = pll_lim->vco2.max_inputfreq;
++ int minM1 = pll_lim->vco1.min_m, maxM1 = pll_lim->vco1.max_m;
++ int minN1 = pll_lim->vco1.min_n, maxN1 = pll_lim->vco1.max_n;
++ int minM2 = pll_lim->vco2.min_m, maxM2 = pll_lim->vco2.max_m;
++ int minN2 = pll_lim->vco2.min_n, maxN2 = pll_lim->vco2.max_n;
++ int maxlog2P = pll_lim->max_usable_log2p;
++ int crystal = pll_lim->refclk;
++ bool fixedgain2 = (minM2 == maxM2 && minN2 == maxN2);
++ int M1, N1, M2, N2, log2P;
++ int clkP, calcclk1, calcclk2, calcclkout;
++ int delta, bestdelta = INT_MAX;
++ int bestclk = 0;
++
++ int vco2 = (maxvco2 - maxvco2/200) / 2;
++ for (log2P = 0; clk && log2P < maxlog2P && clk <= (vco2 >> log2P); log2P++)
++ ;
++ clkP = clk << log2P;
++
++ if (maxvco2 < clk + clk/200) /* +0.5% */
++ maxvco2 = clk + clk/200;
++
++ for (M1 = minM1; M1 <= maxM1; M1++) {
++ if (crystal/M1 < minU1)
++ return bestclk;
++ if (crystal/M1 > maxU1)
++ continue;
++
++ for (N1 = minN1; N1 <= maxN1; N1++) {
++ calcclk1 = crystal * N1 / M1;
++ if (calcclk1 < minvco1)
++ continue;
++ if (calcclk1 > maxvco1)
++ break;
++
++ for (M2 = minM2; M2 <= maxM2; M2++) {
++ if (calcclk1/M2 < minU2)
++ break;
++ if (calcclk1/M2 > maxU2)
++ continue;
++
++ /* add calcclk1/2 to round better */
++ N2 = (clkP * M2 + calcclk1/2) / calcclk1;
++ if (N2 < minN2)
++ continue;
++ if (N2 > maxN2)
++ break;
++
++ if (!fixedgain2) {
++ if (chip_version < 0x60)
++ if (N2/M2 < 4 || N2/M2 > 10)
++ continue;
++
++ calcclk2 = calcclk1 * N2 / M2;
++ if (calcclk2 < minvco2)
++ break;
++ if (calcclk2 > maxvco2)
++ continue;
++ } else
++ calcclk2 = calcclk1;
++
++ calcclkout = calcclk2 >> log2P;
++ delta = abs(calcclkout - clk);
++ /* we do an exhaustive search rather than terminating
++ * on an optimality condition...
++ */
++ if (delta < bestdelta) {
++ bestdelta = delta;
++ bestclk = calcclkout;
++ bestpv->N1 = N1;
++ bestpv->M1 = M1;
++ bestpv->N2 = N2;
++ bestpv->M2 = M2;
++ bestpv->log2P = log2P;
++ if (delta == 0) /* except this one */
++ return bestclk;
++ }
++ }
++ }
++ }
++
++ return bestclk;
++}
++
++int
++nouveau_calc_pll_mnp(struct drm_device *dev, struct pll_lims *pll_lim, int clk,
++ struct nouveau_pll_vals *pv)
++{
++ int outclk;
++
++ if (!pll_lim->vco2.maxfreq)
++ outclk = getMNP_single(dev, pll_lim, clk, pv);
++ else
++ outclk = getMNP_double(dev, pll_lim, clk, pv);
++
++ if (!outclk)
++ NV_ERROR(dev, "Could not find a compatible set of PLL values\n");
++
++ return outclk;
++}
+diff --git a/drivers/gpu/drm/nouveau/nouveau_channel.c b/drivers/gpu/drm/nouveau/nouveau_channel.c
+new file mode 100644
+index 0000000..2281f99
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_channel.c
+@@ -0,0 +1,430 @@
++/*
++ * Copyright 2005-2006 Stephane Marchesin
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++#include "nouveau_drm.h"
++#include "nouveau_dma.h"
++
++static int
++nouveau_channel_pushbuf_ctxdma_init(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_bo *pb = chan->pushbuf_bo;
++ struct nouveau_gpuobj *pushbuf = NULL;
++ uint32_t start = pb->bo.mem.mm_node->start << PAGE_SHIFT;
++ int ret;
++
++ if (pb->bo.mem.mem_type == TTM_PL_TT) {
++ ret = nouveau_gpuobj_gart_dma_new(chan, 0,
++ dev_priv->gart_info.aper_size,
++ NV_DMA_ACCESS_RO, &pushbuf,
++ NULL);
++ chan->pushbuf_base = start;
++ } else
++ if (dev_priv->card_type != NV_04) {
++ ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, 0,
++ dev_priv->fb_available_size,
++ NV_DMA_ACCESS_RO,
++ NV_DMA_TARGET_VIDMEM, &pushbuf);
++ chan->pushbuf_base = start;
++ } else {
++ /* NV04 cmdbuf hack, from original ddx.. not sure of it's
++ * exact reason for existing :) PCI access to cmdbuf in
++ * VRAM.
++ */
++ ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
++ drm_get_resource_start(dev, 1),
++ dev_priv->fb_available_size,
++ NV_DMA_ACCESS_RO,
++ NV_DMA_TARGET_PCI, &pushbuf);
++ chan->pushbuf_base = start;
++ }
++
++ ret = nouveau_gpuobj_ref_add(dev, chan, 0, pushbuf, &chan->pushbuf);
++ if (ret) {
++ NV_ERROR(dev, "Error referencing pushbuf ctxdma: %d\n", ret);
++ if (pushbuf != dev_priv->gart_info.sg_ctxdma)
++ nouveau_gpuobj_del(dev, &pushbuf);
++ return ret;
++ }
++
++ return 0;
++}
++
++static struct nouveau_bo *
++nouveau_channel_user_pushbuf_alloc(struct drm_device *dev)
++{
++ struct nouveau_bo *pushbuf = NULL;
++ int location, ret;
++
++ if (nouveau_vram_pushbuf)
++ location = TTM_PL_FLAG_VRAM;
++ else
++ location = TTM_PL_FLAG_TT;
++
++ ret = nouveau_bo_new(dev, NULL, 65536, 0, location, 0, 0x0000, false,
++ true, &pushbuf);
++ if (ret) {
++ NV_ERROR(dev, "error allocating DMA push buffer: %d\n", ret);
++ return NULL;
++ }
++
++ ret = nouveau_bo_pin(pushbuf, location);
++ if (ret) {
++ NV_ERROR(dev, "error pinning DMA push buffer: %d\n", ret);
++ nouveau_bo_ref(NULL, &pushbuf);
++ return NULL;
++ }
++
++ return pushbuf;
++}
++
++/* allocates and initializes a fifo for user space consumption */
++int
++nouveau_channel_alloc(struct drm_device *dev, struct nouveau_channel **chan_ret,
++ struct drm_file *file_priv,
++ uint32_t vram_handle, uint32_t tt_handle)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
++ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
++ struct nouveau_channel *chan;
++ int channel, user;
++ int ret;
++
++ /*
++ * Alright, here is the full story
++ * Nvidia cards have multiple hw fifo contexts (praise them for that,
++ * no complicated crash-prone context switches)
++ * We allocate a new context for each app and let it write to it
++ * directly (woo, full userspace command submission !)
++ * When there are no more contexts, you lost
++ */
++ for (channel = 0; channel < pfifo->channels; channel++) {
++ if (dev_priv->fifos[channel] == NULL)
++ break;
++ }
++
++ /* no more fifos. you lost. */
++ if (channel == pfifo->channels)
++ return -EINVAL;
++
++ dev_priv->fifos[channel] = kzalloc(sizeof(struct nouveau_channel),
++ GFP_KERNEL);
++ if (!dev_priv->fifos[channel])
++ return -ENOMEM;
++ dev_priv->fifo_alloc_count++;
++ chan = dev_priv->fifos[channel];
++ INIT_LIST_HEAD(&chan->nvsw.vbl_wait);
++ INIT_LIST_HEAD(&chan->fence.pending);
++ chan->dev = dev;
++ chan->id = channel;
++ chan->file_priv = file_priv;
++ chan->vram_handle = vram_handle;
++ chan->gart_handle = tt_handle;
++
++ NV_INFO(dev, "Allocating FIFO number %d\n", channel);
++
++ /* Allocate DMA push buffer */
++ chan->pushbuf_bo = nouveau_channel_user_pushbuf_alloc(dev);
++ if (!chan->pushbuf_bo) {
++ ret = -ENOMEM;
++ NV_ERROR(dev, "pushbuf %d\n", ret);
++ nouveau_channel_free(chan);
++ return ret;
++ }
++
++ nouveau_dma_pre_init(chan);
++
++ /* Locate channel's user control regs */
++ if (dev_priv->card_type < NV_40)
++ user = NV03_USER(channel);
++ else
++ if (dev_priv->card_type < NV_50)
++ user = NV40_USER(channel);
++ else
++ user = NV50_USER(channel);
++
++ chan->user = ioremap(pci_resource_start(dev->pdev, 0) + user,
++ PAGE_SIZE);
++ if (!chan->user) {
++ NV_ERROR(dev, "ioremap of regs failed.\n");
++ nouveau_channel_free(chan);
++ return -ENOMEM;
++ }
++ chan->user_put = 0x40;
++ chan->user_get = 0x44;
++
++ /* Allocate space for per-channel fixed notifier memory */
++ ret = nouveau_notifier_init_channel(chan);
++ if (ret) {
++ NV_ERROR(dev, "ntfy %d\n", ret);
++ nouveau_channel_free(chan);
++ return ret;
++ }
++
++ /* Setup channel's default objects */
++ ret = nouveau_gpuobj_channel_init(chan, vram_handle, tt_handle);
++ if (ret) {
++ NV_ERROR(dev, "gpuobj %d\n", ret);
++ nouveau_channel_free(chan);
++ return ret;
++ }
++
++ /* Create a dma object for the push buffer */
++ ret = nouveau_channel_pushbuf_ctxdma_init(chan);
++ if (ret) {
++ NV_ERROR(dev, "pbctxdma %d\n", ret);
++ nouveau_channel_free(chan);
++ return ret;
++ }
++
++ /* disable the fifo caches */
++ pfifo->reassign(dev, false);
++
++ /* Create a graphics context for new channel */
++ ret = pgraph->create_context(chan);
++ if (ret) {
++ nouveau_channel_free(chan);
++ return ret;
++ }
++
++ /* Construct inital RAMFC for new channel */
++ ret = pfifo->create_context(chan);
++ if (ret) {
++ nouveau_channel_free(chan);
++ return ret;
++ }
++
++ pfifo->reassign(dev, true);
++
++ ret = nouveau_dma_init(chan);
++ if (!ret)
++ ret = nouveau_fence_init(chan);
++ if (ret) {
++ nouveau_channel_free(chan);
++ return ret;
++ }
++
++ nouveau_debugfs_channel_init(chan);
++
++ NV_INFO(dev, "%s: initialised FIFO %d\n", __func__, channel);
++ *chan_ret = chan;
++ return 0;
++}
++
++/* stops a fifo */
++void
++nouveau_channel_free(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
++ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
++ unsigned long flags;
++ int ret;
++
++ NV_INFO(dev, "%s: freeing fifo %d\n", __func__, chan->id);
++
++ nouveau_debugfs_channel_fini(chan);
++
++ /* Give outstanding push buffers a chance to complete */
++ spin_lock_irqsave(&chan->fence.lock, flags);
++ nouveau_fence_update(chan);
++ spin_unlock_irqrestore(&chan->fence.lock, flags);
++ if (chan->fence.sequence != chan->fence.sequence_ack) {
++ struct nouveau_fence *fence = NULL;
++
++ ret = nouveau_fence_new(chan, &fence, true);
++ if (ret == 0) {
++ ret = nouveau_fence_wait(fence, NULL, false, false);
++ nouveau_fence_unref((void *)&fence);
++ }
++
++ if (ret)
++ NV_ERROR(dev, "Failed to idle channel %d.\n", chan->id);
++ }
++
++ /* Ensure all outstanding fences are signaled. They should be if the
++ * above attempts at idling were OK, but if we failed this'll tell TTM
++ * we're done with the buffers.
++ */
++ nouveau_fence_fini(chan);
++
++ /* Ensure the channel is no longer active on the GPU */
++ pfifo->reassign(dev, false);
++
++ pgraph->fifo_access(dev, false);
++ if (pgraph->channel(dev) == chan)
++ pgraph->unload_context(dev);
++ pgraph->destroy_context(chan);
++ pgraph->fifo_access(dev, true);
++
++ if (pfifo->channel_id(dev) == chan->id) {
++ pfifo->disable(dev);
++ pfifo->unload_context(dev);
++ pfifo->enable(dev);
++ }
++ pfifo->destroy_context(chan);
++
++ pfifo->reassign(dev, true);
++
++ /* Release the channel's resources */
++ nouveau_gpuobj_ref_del(dev, &chan->pushbuf);
++ if (chan->pushbuf_bo) {
++ nouveau_bo_unpin(chan->pushbuf_bo);
++ nouveau_bo_ref(NULL, &chan->pushbuf_bo);
++ }
++ nouveau_gpuobj_channel_takedown(chan);
++ nouveau_notifier_takedown_channel(chan);
++ if (chan->user)
++ iounmap(chan->user);
++
++ dev_priv->fifos[chan->id] = NULL;
++ dev_priv->fifo_alloc_count--;
++ kfree(chan);
++}
++
++/* cleans up all the fifos from file_priv */
++void
++nouveau_channel_cleanup(struct drm_device *dev, struct drm_file *file_priv)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_engine *engine = &dev_priv->engine;
++ int i;
++
++ NV_DEBUG(dev, "clearing FIFO enables from file_priv\n");
++ for (i = 0; i < engine->fifo.channels; i++) {
++ struct nouveau_channel *chan = dev_priv->fifos[i];
++
++ if (chan && chan->file_priv == file_priv)
++ nouveau_channel_free(chan);
++ }
++}
++
++int
++nouveau_channel_owner(struct drm_device *dev, struct drm_file *file_priv,
++ int channel)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_engine *engine = &dev_priv->engine;
++
++ if (channel >= engine->fifo.channels)
++ return 0;
++ if (dev_priv->fifos[channel] == NULL)
++ return 0;
++
++ return (dev_priv->fifos[channel]->file_priv == file_priv);
++}
++
++/***********************************
++ * ioctls wrapping the functions
++ ***********************************/
++
++static int
++nouveau_ioctl_fifo_alloc(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct drm_nouveau_channel_alloc *init = data;
++ struct nouveau_channel *chan;
++ int ret;
++
++ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
++
++ if (dev_priv->engine.graph.accel_blocked)
++ return -ENODEV;
++
++ if (init->fb_ctxdma_handle == ~0 || init->tt_ctxdma_handle == ~0)
++ return -EINVAL;
++
++ ret = nouveau_channel_alloc(dev, &chan, file_priv,
++ init->fb_ctxdma_handle,
++ init->tt_ctxdma_handle);
++ if (ret)
++ return ret;
++ init->channel = chan->id;
++
++ init->subchan[0].handle = NvM2MF;
++ if (dev_priv->card_type < NV_50)
++ init->subchan[0].grclass = 0x0039;
++ else
++ init->subchan[0].grclass = 0x5039;
++ init->subchan[1].handle = NvSw;
++ init->subchan[1].grclass = NV_SW;
++ init->nr_subchan = 2;
++
++ /* Named memory object area */
++ ret = drm_gem_handle_create(file_priv, chan->notifier_bo->gem,
++ &init->notifier_handle);
++ if (ret) {
++ nouveau_channel_free(chan);
++ return ret;
++ }
++
++ return 0;
++}
++
++static int
++nouveau_ioctl_fifo_free(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_nouveau_channel_free *cfree = data;
++ struct nouveau_channel *chan;
++
++ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
++ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(cfree->channel, file_priv, chan);
++
++ nouveau_channel_free(chan);
++ return 0;
++}
++
++/***********************************
++ * finally, the ioctl table
++ ***********************************/
++
++struct drm_ioctl_desc nouveau_ioctls[] = {
++ DRM_IOCTL_DEF(DRM_NOUVEAU_CARD_INIT, nouveau_ioctl_card_init, DRM_AUTH),
++ DRM_IOCTL_DEF(DRM_NOUVEAU_GETPARAM, nouveau_ioctl_getparam, DRM_AUTH),
++ DRM_IOCTL_DEF(DRM_NOUVEAU_SETPARAM, nouveau_ioctl_setparam, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY),
++ DRM_IOCTL_DEF(DRM_NOUVEAU_CHANNEL_ALLOC, nouveau_ioctl_fifo_alloc, DRM_AUTH),
++ DRM_IOCTL_DEF(DRM_NOUVEAU_CHANNEL_FREE, nouveau_ioctl_fifo_free, DRM_AUTH),
++ DRM_IOCTL_DEF(DRM_NOUVEAU_GROBJ_ALLOC, nouveau_ioctl_grobj_alloc, DRM_AUTH),
++ DRM_IOCTL_DEF(DRM_NOUVEAU_NOTIFIEROBJ_ALLOC, nouveau_ioctl_notifier_alloc, DRM_AUTH),
++ DRM_IOCTL_DEF(DRM_NOUVEAU_GPUOBJ_FREE, nouveau_ioctl_gpuobj_free, DRM_AUTH),
++ DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_NEW, nouveau_gem_ioctl_new, DRM_AUTH),
++ DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF, nouveau_gem_ioctl_pushbuf, DRM_AUTH),
++ DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF_CALL, nouveau_gem_ioctl_pushbuf_call, DRM_AUTH),
++ DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PIN, nouveau_gem_ioctl_pin, DRM_AUTH),
++ DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_UNPIN, nouveau_gem_ioctl_unpin, DRM_AUTH),
++ DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_CPU_PREP, nouveau_gem_ioctl_cpu_prep, DRM_AUTH),
++ DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_CPU_FINI, nouveau_gem_ioctl_cpu_fini, DRM_AUTH),
++ DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_INFO, nouveau_gem_ioctl_info, DRM_AUTH),
++ DRM_IOCTL_DEF(DRM_NOUVEAU_GEM_PUSHBUF_CALL2, nouveau_gem_ioctl_pushbuf_call2, DRM_AUTH),
++};
++
++int nouveau_max_ioctl = DRM_ARRAY_SIZE(nouveau_ioctls);
+diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.c b/drivers/gpu/drm/nouveau/nouveau_connector.c
+new file mode 100644
+index 0000000..d2f6335
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_connector.c
+@@ -0,0 +1,846 @@
++/*
++ * Copyright (C) 2008 Maarten Maathuis.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include <acpi/button.h>
++
++#include "drmP.h"
++#include "drm_edid.h"
++#include "drm_crtc_helper.h"
++
++#include "nouveau_reg.h"
++#include "nouveau_drv.h"
++#include "nouveau_encoder.h"
++#include "nouveau_crtc.h"
++#include "nouveau_connector.h"
++#include "nouveau_hw.h"
++
++static inline struct drm_encoder_slave_funcs *
++get_slave_funcs(struct nouveau_encoder *enc)
++{
++ return to_encoder_slave(to_drm_encoder(enc))->slave_funcs;
++}
++
++static struct nouveau_encoder *
++find_encoder_by_type(struct drm_connector *connector, int type)
++{
++ struct drm_device *dev = connector->dev;
++ struct nouveau_encoder *nv_encoder;
++ struct drm_mode_object *obj;
++ int i, id;
++
++ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
++ id = connector->encoder_ids[i];
++ if (!id)
++ break;
++
++ obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
++ if (!obj)
++ continue;
++ nv_encoder = nouveau_encoder(obj_to_encoder(obj));
++
++ if (type == OUTPUT_ANY || nv_encoder->dcb->type == type)
++ return nv_encoder;
++ }
++
++ return NULL;
++}
++
++struct nouveau_connector *
++nouveau_encoder_connector_get(struct nouveau_encoder *encoder)
++{
++ struct drm_device *dev = to_drm_encoder(encoder)->dev;
++ struct drm_connector *drm_connector;
++
++ list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head) {
++ if (drm_connector->encoder == to_drm_encoder(encoder))
++ return nouveau_connector(drm_connector);
++ }
++
++ return NULL;
++}
++
++
++static void
++nouveau_connector_destroy(struct drm_connector *drm_connector)
++{
++ struct nouveau_connector *nv_connector =
++ nouveau_connector(drm_connector);
++ struct drm_device *dev;
++
++ if (!nv_connector)
++ return;
++
++ dev = nv_connector->base.dev;
++ NV_DEBUG_KMS(dev, "\n");
++
++ kfree(nv_connector->edid);
++ drm_sysfs_connector_remove(drm_connector);
++ drm_connector_cleanup(drm_connector);
++ kfree(drm_connector);
++}
++
++static void
++nouveau_connector_ddc_prepare(struct drm_connector *connector, int *flags)
++{
++ struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
++
++ if (dev_priv->card_type >= NV_50)
++ return;
++
++ *flags = 0;
++ if (NVLockVgaCrtcs(dev_priv->dev, false))
++ *flags |= 1;
++ if (nv_heads_tied(dev_priv->dev))
++ *flags |= 2;
++
++ if (*flags & 2)
++ NVSetOwner(dev_priv->dev, 0); /* necessary? */
++}
++
++static void
++nouveau_connector_ddc_finish(struct drm_connector *connector, int flags)
++{
++ struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
++
++ if (dev_priv->card_type >= NV_50)
++ return;
++
++ if (flags & 2)
++ NVSetOwner(dev_priv->dev, 4);
++ if (flags & 1)
++ NVLockVgaCrtcs(dev_priv->dev, true);
++}
++
++static struct nouveau_i2c_chan *
++nouveau_connector_ddc_detect(struct drm_connector *connector,
++ struct nouveau_encoder **pnv_encoder)
++{
++ struct drm_device *dev = connector->dev;
++ uint8_t out_buf[] = { 0x0, 0x0}, buf[2];
++ int ret, flags, i;
++
++ struct i2c_msg msgs[] = {
++ {
++ .addr = 0x50,
++ .flags = 0,
++ .len = 1,
++ .buf = out_buf,
++ },
++ {
++ .addr = 0x50,
++ .flags = I2C_M_RD,
++ .len = 1,
++ .buf = buf,
++ }
++ };
++
++ for (i = 0; i < DRM_CONNECTOR_MAX_ENCODER; i++) {
++ struct nouveau_i2c_chan *i2c = NULL;
++ struct nouveau_encoder *nv_encoder;
++ struct drm_mode_object *obj;
++ int id;
++
++ id = connector->encoder_ids[i];
++ if (!id)
++ break;
++
++ obj = drm_mode_object_find(dev, id, DRM_MODE_OBJECT_ENCODER);
++ if (!obj)
++ continue;
++ nv_encoder = nouveau_encoder(obj_to_encoder(obj));
++
++ if (nv_encoder->dcb->i2c_index < 0xf)
++ i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
++ if (!i2c)
++ continue;
++
++ nouveau_connector_ddc_prepare(connector, &flags);
++ ret = i2c_transfer(&i2c->adapter, msgs, 2);
++ nouveau_connector_ddc_finish(connector, flags);
++
++ if (ret == 2) {
++ *pnv_encoder = nv_encoder;
++ return i2c;
++ }
++ }
++
++ return NULL;
++}
++
++static void
++nouveau_connector_set_encoder(struct drm_connector *connector,
++ struct nouveau_encoder *nv_encoder)
++{
++ struct nouveau_connector *nv_connector = nouveau_connector(connector);
++ struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
++ struct drm_device *dev = connector->dev;
++
++ if (nv_connector->detected_encoder == nv_encoder)
++ return;
++ nv_connector->detected_encoder = nv_encoder;
++
++ if (nv_encoder->dcb->type == OUTPUT_LVDS ||
++ nv_encoder->dcb->type == OUTPUT_TMDS) {
++ connector->doublescan_allowed = false;
++ connector->interlace_allowed = false;
++ } else {
++ connector->doublescan_allowed = true;
++ if (dev_priv->card_type == NV_20 ||
++ (dev_priv->card_type == NV_10 &&
++ (dev->pci_device & 0x0ff0) != 0x0100 &&
++ (dev->pci_device & 0x0ff0) != 0x0150))
++ /* HW is broken */
++ connector->interlace_allowed = false;
++ else
++ connector->interlace_allowed = true;
++ }
++
++ if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
++ drm_connector_property_set_value(connector,
++ dev->mode_config.dvi_i_subconnector_property,
++ nv_encoder->dcb->type == OUTPUT_TMDS ?
++ DRM_MODE_SUBCONNECTOR_DVID :
++ DRM_MODE_SUBCONNECTOR_DVIA);
++ }
++}
++
++static enum drm_connector_status
++nouveau_connector_detect(struct drm_connector *connector)
++{
++ struct drm_device *dev = connector->dev;
++ struct nouveau_connector *nv_connector = nouveau_connector(connector);
++ struct nouveau_encoder *nv_encoder = NULL;
++ struct nouveau_i2c_chan *i2c;
++ int type, flags;
++
++ if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
++ nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
++ if (nv_encoder && nv_connector->native_mode) {
++#ifdef CONFIG_ACPI
++ if (!nouveau_ignorelid && !acpi_lid_open())
++ return connector_status_disconnected;
++#endif
++ nouveau_connector_set_encoder(connector, nv_encoder);
++ return connector_status_connected;
++ }
++
++ /* Cleanup the previous EDID block. */
++ if (nv_connector->edid) {
++ drm_mode_connector_update_edid_property(connector, NULL);
++ kfree(nv_connector->edid);
++ nv_connector->edid = NULL;
++ }
++
++ i2c = nouveau_connector_ddc_detect(connector, &nv_encoder);
++ if (i2c) {
++ nouveau_connector_ddc_prepare(connector, &flags);
++ nv_connector->edid = drm_get_edid(connector, &i2c->adapter);
++ nouveau_connector_ddc_finish(connector, flags);
++ drm_mode_connector_update_edid_property(connector,
++ nv_connector->edid);
++ if (!nv_connector->edid) {
++ NV_ERROR(dev, "DDC responded, but no EDID for %s\n",
++ drm_get_connector_name(connector));
++ goto detect_analog;
++ }
++
++ if (nv_encoder->dcb->type == OUTPUT_DP &&
++ !nouveau_dp_detect(to_drm_encoder(nv_encoder))) {
++ NV_ERROR(dev, "Detected %s, but failed init\n",
++ drm_get_connector_name(connector));
++ return connector_status_disconnected;
++ }
++
++ /* Override encoder type for DVI-I based on whether EDID
++ * says the display is digital or analog, both use the
++ * same i2c channel so the value returned from ddc_detect
++ * isn't necessarily correct.
++ */
++ if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
++ if (nv_connector->edid->input & DRM_EDID_INPUT_DIGITAL)
++ type = OUTPUT_TMDS;
++ else
++ type = OUTPUT_ANALOG;
++
++ nv_encoder = find_encoder_by_type(connector, type);
++ if (!nv_encoder) {
++ NV_ERROR(dev, "Detected %d encoder on %s, "
++ "but no object!\n", type,
++ drm_get_connector_name(connector));
++ return connector_status_disconnected;
++ }
++ }
++
++ nouveau_connector_set_encoder(connector, nv_encoder);
++ return connector_status_connected;
++ }
++
++detect_analog:
++ nv_encoder = find_encoder_by_type(connector, OUTPUT_ANALOG);
++ if (!nv_encoder)
++ nv_encoder = find_encoder_by_type(connector, OUTPUT_TV);
++ if (nv_encoder) {
++ struct drm_encoder *encoder = to_drm_encoder(nv_encoder);
++ struct drm_encoder_helper_funcs *helper =
++ encoder->helper_private;
++
++ if (helper->detect(encoder, connector) ==
++ connector_status_connected) {
++ nouveau_connector_set_encoder(connector, nv_encoder);
++ return connector_status_connected;
++ }
++
++ }
++
++ return connector_status_disconnected;
++}
++
++static void
++nouveau_connector_force(struct drm_connector *connector)
++{
++ struct drm_device *dev = connector->dev;
++ struct nouveau_encoder *nv_encoder;
++ int type;
++
++ if (connector->connector_type == DRM_MODE_CONNECTOR_DVII) {
++ if (connector->force == DRM_FORCE_ON_DIGITAL)
++ type = OUTPUT_TMDS;
++ else
++ type = OUTPUT_ANALOG;
++ } else
++ type = OUTPUT_ANY;
++
++ nv_encoder = find_encoder_by_type(connector, type);
++ if (!nv_encoder) {
++ NV_ERROR(dev, "can't find encoder to force %s on!\n",
++ drm_get_connector_name(connector));
++ connector->status = connector_status_disconnected;
++ return;
++ }
++
++ nouveau_connector_set_encoder(connector, nv_encoder);
++}
++
++static int
++nouveau_connector_set_property(struct drm_connector *connector,
++ struct drm_property *property, uint64_t value)
++{
++ struct nouveau_connector *nv_connector = nouveau_connector(connector);
++ struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
++ struct drm_device *dev = connector->dev;
++ int ret;
++
++ /* Scaling mode */
++ if (property == dev->mode_config.scaling_mode_property) {
++ struct nouveau_crtc *nv_crtc = NULL;
++ bool modeset = false;
++
++ switch (value) {
++ case DRM_MODE_SCALE_NONE:
++ case DRM_MODE_SCALE_FULLSCREEN:
++ case DRM_MODE_SCALE_CENTER:
++ case DRM_MODE_SCALE_ASPECT:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* LVDS always needs gpu scaling */
++ if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS &&
++ value == DRM_MODE_SCALE_NONE)
++ return -EINVAL;
++
++ /* Changing between GPU and panel scaling requires a full
++ * modeset
++ */
++ if ((nv_connector->scaling_mode == DRM_MODE_SCALE_NONE) ||
++ (value == DRM_MODE_SCALE_NONE))
++ modeset = true;
++ nv_connector->scaling_mode = value;
++
++ if (connector->encoder && connector->encoder->crtc)
++ nv_crtc = nouveau_crtc(connector->encoder->crtc);
++ if (!nv_crtc)
++ return 0;
++
++ if (modeset || !nv_crtc->set_scale) {
++ ret = drm_crtc_helper_set_mode(&nv_crtc->base,
++ &nv_crtc->base.mode,
++ nv_crtc->base.x,
++ nv_crtc->base.y, NULL);
++ if (!ret)
++ return -EINVAL;
++ } else {
++ ret = nv_crtc->set_scale(nv_crtc, value, true);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++ }
++
++ /* Dithering */
++ if (property == dev->mode_config.dithering_mode_property) {
++ struct nouveau_crtc *nv_crtc = NULL;
++
++ if (value == DRM_MODE_DITHERING_ON)
++ nv_connector->use_dithering = true;
++ else
++ nv_connector->use_dithering = false;
++
++ if (connector->encoder && connector->encoder->crtc)
++ nv_crtc = nouveau_crtc(connector->encoder->crtc);
++
++ if (!nv_crtc || !nv_crtc->set_dither)
++ return 0;
++
++ return nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering,
++ true);
++ }
++
++ if (nv_encoder && nv_encoder->dcb->type == OUTPUT_TV)
++ return get_slave_funcs(nv_encoder)->
++ set_property(to_drm_encoder(nv_encoder), connector, property, value);
++
++ return -EINVAL;
++}
++
++static struct drm_display_mode *
++nouveau_connector_native_mode(struct nouveau_connector *connector)
++{
++ struct drm_device *dev = connector->base.dev;
++ struct drm_display_mode *mode, *largest = NULL;
++ int high_w = 0, high_h = 0, high_v = 0;
++
++ /* Use preferred mode if there is one.. */
++ list_for_each_entry(mode, &connector->base.probed_modes, head) {
++ if (mode->type & DRM_MODE_TYPE_PREFERRED) {
++ NV_DEBUG_KMS(dev, "native mode from preferred\n");
++ return drm_mode_duplicate(dev, mode);
++ }
++ }
++
++ /* Otherwise, take the resolution with the largest width, then height,
++ * then vertical refresh
++ */
++ list_for_each_entry(mode, &connector->base.probed_modes, head) {
++ if (mode->hdisplay < high_w)
++ continue;
++
++ if (mode->hdisplay == high_w && mode->vdisplay < high_h)
++ continue;
++
++ if (mode->hdisplay == high_w && mode->vdisplay == high_h &&
++ mode->vrefresh < high_v)
++ continue;
++
++ high_w = mode->hdisplay;
++ high_h = mode->vdisplay;
++ high_v = mode->vrefresh;
++ largest = mode;
++ }
++
++ NV_DEBUG_KMS(dev, "native mode from largest: %dx%d@%d\n",
++ high_w, high_h, high_v);
++ return largest ? drm_mode_duplicate(dev, largest) : NULL;
++}
++
++struct moderec {
++ int hdisplay;
++ int vdisplay;
++};
++
++static struct moderec scaler_modes[] = {
++ { 1920, 1200 },
++ { 1920, 1080 },
++ { 1680, 1050 },
++ { 1600, 1200 },
++ { 1400, 1050 },
++ { 1280, 1024 },
++ { 1280, 960 },
++ { 1152, 864 },
++ { 1024, 768 },
++ { 800, 600 },
++ { 720, 400 },
++ { 640, 480 },
++ { 640, 400 },
++ { 640, 350 },
++ {}
++};
++
++static int
++nouveau_connector_scaler_modes_add(struct drm_connector *connector)
++{
++ struct nouveau_connector *nv_connector = nouveau_connector(connector);
++ struct drm_display_mode *native = nv_connector->native_mode, *m;
++ struct drm_device *dev = connector->dev;
++ struct moderec *mode = &scaler_modes[0];
++ int modes = 0;
++
++ if (!native)
++ return 0;
++
++ while (mode->hdisplay) {
++ if (mode->hdisplay <= native->hdisplay &&
++ mode->vdisplay <= native->vdisplay) {
++ m = drm_cvt_mode(dev, mode->hdisplay, mode->vdisplay,
++ drm_mode_vrefresh(native), false,
++ false, false);
++ if (!m)
++ continue;
++
++ m->type |= DRM_MODE_TYPE_DRIVER;
++
++ drm_mode_probed_add(connector, m);
++ modes++;
++ }
++
++ mode++;
++ }
++
++ return modes;
++}
++
++static int
++nouveau_connector_get_modes(struct drm_connector *connector)
++{
++ struct drm_device *dev = connector->dev;
++ struct nouveau_connector *nv_connector = nouveau_connector(connector);
++ struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
++ int ret = 0;
++
++ /* If we're not LVDS, destroy the previous native mode, the attached
++ * monitor could have changed.
++ */
++ if (connector->connector_type != DRM_MODE_CONNECTOR_LVDS &&
++ nv_connector->native_mode) {
++ drm_mode_destroy(dev, nv_connector->native_mode);
++ nv_connector->native_mode = NULL;
++ }
++
++ if (nv_connector->edid)
++ ret = drm_add_edid_modes(connector, nv_connector->edid);
++
++ /* Find the native mode if this is a digital panel, if we didn't
++ * find any modes through DDC previously add the native mode to
++ * the list of modes.
++ */
++ if (!nv_connector->native_mode)
++ nv_connector->native_mode =
++ nouveau_connector_native_mode(nv_connector);
++ if (ret == 0 && nv_connector->native_mode) {
++ struct drm_display_mode *mode;
++
++ mode = drm_mode_duplicate(dev, nv_connector->native_mode);
++ drm_mode_probed_add(connector, mode);
++ ret = 1;
++ }
++
++ if (nv_encoder->dcb->type == OUTPUT_TV)
++ ret = get_slave_funcs(nv_encoder)->
++ get_modes(to_drm_encoder(nv_encoder), connector);
++
++ if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS)
++ ret += nouveau_connector_scaler_modes_add(connector);
++
++ return ret;
++}
++
++static int
++nouveau_connector_mode_valid(struct drm_connector *connector,
++ struct drm_display_mode *mode)
++{
++ struct drm_nouveau_private *dev_priv = connector->dev->dev_private;
++ struct nouveau_connector *nv_connector = nouveau_connector(connector);
++ struct nouveau_encoder *nv_encoder = nv_connector->detected_encoder;
++ unsigned min_clock = 25000, max_clock = min_clock;
++ unsigned clock = mode->clock;
++
++ switch (nv_encoder->dcb->type) {
++ case OUTPUT_LVDS:
++ BUG_ON(!nv_connector->native_mode);
++ if (mode->hdisplay > nv_connector->native_mode->hdisplay ||
++ mode->vdisplay > nv_connector->native_mode->vdisplay)
++ return MODE_PANEL;
++
++ min_clock = 0;
++ max_clock = 400000;
++ break;
++ case OUTPUT_TMDS:
++ if ((dev_priv->card_type >= NV_50 && !nouveau_duallink) ||
++ (dev_priv->card_type < NV_50 &&
++ !nv_encoder->dcb->duallink_possible))
++ max_clock = 165000;
++ else
++ max_clock = 330000;
++ break;
++ case OUTPUT_ANALOG:
++ max_clock = nv_encoder->dcb->crtconf.maxfreq;
++ if (!max_clock)
++ max_clock = 350000;
++ break;
++ case OUTPUT_TV:
++ return get_slave_funcs(nv_encoder)->
++ mode_valid(to_drm_encoder(nv_encoder), mode);
++ case OUTPUT_DP:
++ if (nv_encoder->dp.link_bw == DP_LINK_BW_2_7)
++ max_clock = nv_encoder->dp.link_nr * 270000;
++ else
++ max_clock = nv_encoder->dp.link_nr * 162000;
++
++ clock *= 3;
++ break;
++ }
++
++ if (clock < min_clock)
++ return MODE_CLOCK_LOW;
++
++ if (clock > max_clock)
++ return MODE_CLOCK_HIGH;
++
++ return MODE_OK;
++}
++
++static struct drm_encoder *
++nouveau_connector_best_encoder(struct drm_connector *connector)
++{
++ struct nouveau_connector *nv_connector = nouveau_connector(connector);
++
++ if (nv_connector->detected_encoder)
++ return to_drm_encoder(nv_connector->detected_encoder);
++
++ return NULL;
++}
++
++static const struct drm_connector_helper_funcs
++nouveau_connector_helper_funcs = {
++ .get_modes = nouveau_connector_get_modes,
++ .mode_valid = nouveau_connector_mode_valid,
++ .best_encoder = nouveau_connector_best_encoder,
++};
++
++static const struct drm_connector_funcs
++nouveau_connector_funcs = {
++ .dpms = drm_helper_connector_dpms,
++ .save = NULL,
++ .restore = NULL,
++ .detect = nouveau_connector_detect,
++ .destroy = nouveau_connector_destroy,
++ .fill_modes = drm_helper_probe_single_connector_modes,
++ .set_property = nouveau_connector_set_property,
++ .force = nouveau_connector_force
++};
++
++static int
++nouveau_connector_create_lvds(struct drm_device *dev,
++ struct drm_connector *connector)
++{
++ struct nouveau_connector *nv_connector = nouveau_connector(connector);
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_i2c_chan *i2c = NULL;
++ struct nouveau_encoder *nv_encoder;
++ struct drm_display_mode native, *mode, *temp;
++ bool dummy, if_is_24bit = false;
++ int ret, flags;
++
++ nv_encoder = find_encoder_by_type(connector, OUTPUT_LVDS);
++ if (!nv_encoder)
++ return -ENODEV;
++
++ ret = nouveau_bios_parse_lvds_table(dev, 0, &dummy, &if_is_24bit);
++ if (ret) {
++ NV_ERROR(dev, "Error parsing LVDS table, disabling LVDS\n");
++ return ret;
++ }
++ nv_connector->use_dithering = !if_is_24bit;
++
++ /* Firstly try getting EDID over DDC, if allowed and I2C channel
++ * is available.
++ */
++ if (!dev_priv->VBIOS.pub.fp_no_ddc && nv_encoder->dcb->i2c_index < 0xf)
++ i2c = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
++
++ if (i2c) {
++ nouveau_connector_ddc_prepare(connector, &flags);
++ nv_connector->edid = drm_get_edid(connector, &i2c->adapter);
++ nouveau_connector_ddc_finish(connector, flags);
++ }
++
++ /* If no EDID found above, and the VBIOS indicates a hardcoded
++ * modeline is avalilable for the panel, set it as the panel's
++ * native mode and exit.
++ */
++ if (!nv_connector->edid && nouveau_bios_fp_mode(dev, &native) &&
++ (nv_encoder->dcb->lvdsconf.use_straps_for_mode ||
++ dev_priv->VBIOS.pub.fp_no_ddc)) {
++ nv_connector->native_mode = drm_mode_duplicate(dev, &native);
++ goto out;
++ }
++
++ /* Still nothing, some VBIOS images have a hardcoded EDID block
++ * stored for the panel stored in them.
++ */
++ if (!nv_connector->edid && !nv_connector->native_mode &&
++ !dev_priv->VBIOS.pub.fp_no_ddc) {
++ struct edid *edid =
++ (struct edid *)nouveau_bios_embedded_edid(dev);
++ if (edid) {
++ nv_connector->edid = kmalloc(EDID_LENGTH, GFP_KERNEL);
++ *(nv_connector->edid) = *edid;
++ }
++ }
++
++ if (!nv_connector->edid)
++ goto out;
++
++ /* We didn't find/use a panel mode from the VBIOS, so parse the EDID
++ * block and look for the preferred mode there.
++ */
++ ret = drm_add_edid_modes(connector, nv_connector->edid);
++ if (ret == 0)
++ goto out;
++ nv_connector->detected_encoder = nv_encoder;
++ nv_connector->native_mode = nouveau_connector_native_mode(nv_connector);
++ list_for_each_entry_safe(mode, temp, &connector->probed_modes, head)
++ drm_mode_remove(connector, mode);
++
++out:
++ if (!nv_connector->native_mode) {
++ NV_ERROR(dev, "LVDS present in DCB table, but couldn't "
++ "determine its native mode. Disabling.\n");
++ return -ENODEV;
++ }
++
++ drm_mode_connector_update_edid_property(connector, nv_connector->edid);
++ return 0;
++}
++
++int
++nouveau_connector_create(struct drm_device *dev, int index, int type)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_connector *nv_connector = NULL;
++ struct drm_connector *connector;
++ struct drm_encoder *encoder;
++ int ret;
++
++ NV_DEBUG_KMS(dev, "\n");
++
++ nv_connector = kzalloc(sizeof(*nv_connector), GFP_KERNEL);
++ if (!nv_connector)
++ return -ENOMEM;
++ nv_connector->dcb = nouveau_bios_connector_entry(dev, index);
++ connector = &nv_connector->base;
++
++ switch (type) {
++ case DRM_MODE_CONNECTOR_VGA:
++ NV_INFO(dev, "Detected a VGA connector\n");
++ break;
++ case DRM_MODE_CONNECTOR_DVID:
++ NV_INFO(dev, "Detected a DVI-D connector\n");
++ break;
++ case DRM_MODE_CONNECTOR_DVII:
++ NV_INFO(dev, "Detected a DVI-I connector\n");
++ break;
++ case DRM_MODE_CONNECTOR_LVDS:
++ NV_INFO(dev, "Detected a LVDS connector\n");
++ break;
++ case DRM_MODE_CONNECTOR_TV:
++ NV_INFO(dev, "Detected a TV connector\n");
++ break;
++ case DRM_MODE_CONNECTOR_DisplayPort:
++ NV_INFO(dev, "Detected a DisplayPort connector\n");
++ break;
++ default:
++ NV_ERROR(dev, "Unknown connector, this is not good.\n");
++ break;
++ }
++
++ /* defaults, will get overridden in detect() */
++ connector->interlace_allowed = false;
++ connector->doublescan_allowed = false;
++
++ drm_connector_init(dev, connector, &nouveau_connector_funcs, type);
++ drm_connector_helper_add(connector, &nouveau_connector_helper_funcs);
++
++ /* Init DVI-I specific properties */
++ if (type == DRM_MODE_CONNECTOR_DVII) {
++ drm_mode_create_dvi_i_properties(dev);
++ drm_connector_attach_property(connector, dev->mode_config.dvi_i_subconnector_property, 0);
++ drm_connector_attach_property(connector, dev->mode_config.dvi_i_select_subconnector_property, 0);
++ }
++
++ if (type != DRM_MODE_CONNECTOR_LVDS)
++ nv_connector->use_dithering = false;
++
++ if (type == DRM_MODE_CONNECTOR_DVID ||
++ type == DRM_MODE_CONNECTOR_DVII ||
++ type == DRM_MODE_CONNECTOR_LVDS ||
++ type == DRM_MODE_CONNECTOR_DisplayPort) {
++ nv_connector->scaling_mode = DRM_MODE_SCALE_FULLSCREEN;
++
++ drm_connector_attach_property(connector, dev->mode_config.scaling_mode_property,
++ nv_connector->scaling_mode);
++ drm_connector_attach_property(connector, dev->mode_config.dithering_mode_property,
++ nv_connector->use_dithering ? DRM_MODE_DITHERING_ON
++ : DRM_MODE_DITHERING_OFF);
++
++ } else {
++ nv_connector->scaling_mode = DRM_MODE_SCALE_NONE;
++
++ if (type == DRM_MODE_CONNECTOR_VGA &&
++ dev_priv->card_type >= NV_50) {
++ drm_connector_attach_property(connector,
++ dev->mode_config.scaling_mode_property,
++ nv_connector->scaling_mode);
++ }
++ }
++
++ /* attach encoders */
++ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++
++ if (nv_encoder->dcb->connector != index)
++ continue;
++
++ if (get_slave_funcs(nv_encoder))
++ get_slave_funcs(nv_encoder)->create_resources(encoder, connector);
++
++ drm_mode_connector_attach_encoder(connector, encoder);
++ }
++
++ drm_sysfs_connector_add(connector);
++
++ if (connector->connector_type == DRM_MODE_CONNECTOR_LVDS) {
++ ret = nouveau_connector_create_lvds(dev, connector);
++ if (ret) {
++ connector->funcs->destroy(connector);
++ return ret;
++ }
++ }
++
++ return 0;
++}
+diff --git a/drivers/gpu/drm/nouveau/nouveau_connector.h b/drivers/gpu/drm/nouveau/nouveau_connector.h
+new file mode 100644
+index 0000000..728b809
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_connector.h
+@@ -0,0 +1,54 @@
++/*
++ * Copyright (C) 2008 Maarten Maathuis.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#ifndef __NOUVEAU_CONNECTOR_H__
++#define __NOUVEAU_CONNECTOR_H__
++
++#include "drm_edid.h"
++#include "nouveau_i2c.h"
++
++struct nouveau_connector {
++ struct drm_connector base;
++
++ struct dcb_connector_table_entry *dcb;
++
++ int scaling_mode;
++ bool use_dithering;
++
++ struct nouveau_encoder *detected_encoder;
++ struct edid *edid;
++ struct drm_display_mode *native_mode;
++};
++
++static inline struct nouveau_connector *nouveau_connector(
++ struct drm_connector *con)
++{
++ return container_of(con, struct nouveau_connector, base);
++}
++
++int nouveau_connector_create(struct drm_device *dev, int i2c_index, int type);
++
++#endif /* __NOUVEAU_CONNECTOR_H__ */
+diff --git a/drivers/gpu/drm/nouveau/nouveau_crtc.h b/drivers/gpu/drm/nouveau/nouveau_crtc.h
+new file mode 100644
+index 0000000..49fa7b2
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_crtc.h
+@@ -0,0 +1,95 @@
++/*
++ * Copyright (C) 2008 Maarten Maathuis.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#ifndef __NOUVEAU_CRTC_H__
++#define __NOUVEAU_CRTC_H__
++
++struct nouveau_crtc {
++ struct drm_crtc base;
++
++ int index;
++
++ struct drm_display_mode *mode;
++
++ uint32_t dpms_saved_fp_control;
++ uint32_t fp_users;
++ int saturation;
++ int sharpness;
++ int last_dpms;
++
++ struct {
++ int cpp;
++ bool blanked;
++ uint32_t offset;
++ uint32_t tile_flags;
++ } fb;
++
++ struct {
++ struct nouveau_bo *nvbo;
++ bool visible;
++ uint32_t offset;
++ void (*set_offset)(struct nouveau_crtc *, uint32_t offset);
++ void (*set_pos)(struct nouveau_crtc *, int x, int y);
++ void (*hide)(struct nouveau_crtc *, bool update);
++ void (*show)(struct nouveau_crtc *, bool update);
++ } cursor;
++
++ struct {
++ struct nouveau_bo *nvbo;
++ uint16_t r[256];
++ uint16_t g[256];
++ uint16_t b[256];
++ int depth;
++ } lut;
++
++ int (*set_dither)(struct nouveau_crtc *crtc, bool on, bool update);
++ int (*set_scale)(struct nouveau_crtc *crtc, int mode, bool update);
++};
++
++static inline struct nouveau_crtc *nouveau_crtc(struct drm_crtc *crtc)
++{
++ return container_of(crtc, struct nouveau_crtc, base);
++}
++
++static inline struct drm_crtc *to_drm_crtc(struct nouveau_crtc *crtc)
++{
++ return &crtc->base;
++}
++
++int nv50_crtc_create(struct drm_device *dev, int index);
++int nv50_cursor_init(struct nouveau_crtc *);
++void nv50_cursor_fini(struct nouveau_crtc *);
++int nv50_crtc_cursor_set(struct drm_crtc *drm_crtc, struct drm_file *file_priv,
++ uint32_t buffer_handle, uint32_t width,
++ uint32_t height);
++int nv50_crtc_cursor_move(struct drm_crtc *drm_crtc, int x, int y);
++
++int nv04_cursor_init(struct nouveau_crtc *);
++
++struct nouveau_connector *
++nouveau_crtc_connector_get(struct nouveau_crtc *crtc);
++
++#endif /* __NOUVEAU_CRTC_H__ */
+diff --git a/drivers/gpu/drm/nouveau/nouveau_debugfs.c b/drivers/gpu/drm/nouveau/nouveau_debugfs.c
+new file mode 100644
+index 0000000..d79db36
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_debugfs.c
+@@ -0,0 +1,155 @@
++/*
++ * Copyright (C) 2009 Red Hat <bskeggs@redhat.com>
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++/*
++ * Authors:
++ * Ben Skeggs <bskeggs@redhat.com>
++ */
++
++#include <linux/debugfs.h>
++
++#include "drmP.h"
++#include "nouveau_drv.h"
++
++static int
++nouveau_debugfs_channel_info(struct seq_file *m, void *data)
++{
++ struct drm_info_node *node = (struct drm_info_node *) m->private;
++ struct nouveau_channel *chan = node->info_ent->data;
++
++ seq_printf(m, "channel id : %d\n", chan->id);
++
++ seq_printf(m, "cpu fifo state:\n");
++ seq_printf(m, " base: 0x%08x\n", chan->pushbuf_base);
++ seq_printf(m, " max: 0x%08x\n", chan->dma.max << 2);
++ seq_printf(m, " cur: 0x%08x\n", chan->dma.cur << 2);
++ seq_printf(m, " put: 0x%08x\n", chan->dma.put << 2);
++ seq_printf(m, " free: 0x%08x\n", chan->dma.free << 2);
++
++ seq_printf(m, "gpu fifo state:\n");
++ seq_printf(m, " get: 0x%08x\n",
++ nvchan_rd32(chan, chan->user_get));
++ seq_printf(m, " put: 0x%08x\n",
++ nvchan_rd32(chan, chan->user_put));
++
++ seq_printf(m, "last fence : %d\n", chan->fence.sequence);
++ seq_printf(m, "last signalled: %d\n", chan->fence.sequence_ack);
++ return 0;
++}
++
++int
++nouveau_debugfs_channel_init(struct nouveau_channel *chan)
++{
++ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
++ struct drm_minor *minor = chan->dev->primary;
++ int ret;
++
++ if (!dev_priv->debugfs.channel_root) {
++ dev_priv->debugfs.channel_root =
++ debugfs_create_dir("channel", minor->debugfs_root);
++ if (!dev_priv->debugfs.channel_root)
++ return -ENOENT;
++ }
++
++ snprintf(chan->debugfs.name, 32, "%d", chan->id);
++ chan->debugfs.info.name = chan->debugfs.name;
++ chan->debugfs.info.show = nouveau_debugfs_channel_info;
++ chan->debugfs.info.driver_features = 0;
++ chan->debugfs.info.data = chan;
++
++ ret = drm_debugfs_create_files(&chan->debugfs.info, 1,
++ dev_priv->debugfs.channel_root,
++ chan->dev->primary);
++ if (ret == 0)
++ chan->debugfs.active = true;
++ return ret;
++}
++
++void
++nouveau_debugfs_channel_fini(struct nouveau_channel *chan)
++{
++ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
++
++ if (!chan->debugfs.active)
++ return;
++
++ drm_debugfs_remove_files(&chan->debugfs.info, 1, chan->dev->primary);
++ chan->debugfs.active = false;
++
++ if (chan == dev_priv->channel) {
++ debugfs_remove(dev_priv->debugfs.channel_root);
++ dev_priv->debugfs.channel_root = NULL;
++ }
++}
++
++static int
++nouveau_debugfs_chipset_info(struct seq_file *m, void *data)
++{
++ struct drm_info_node *node = (struct drm_info_node *) m->private;
++ struct drm_minor *minor = node->minor;
++ struct drm_device *dev = minor->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t ppci_0;
++
++ ppci_0 = nv_rd32(dev, dev_priv->chipset >= 0x40 ? 0x88000 : 0x1800);
++
++ seq_printf(m, "PMC_BOOT_0: 0x%08x\n", nv_rd32(dev, NV03_PMC_BOOT_0));
++ seq_printf(m, "PCI ID : 0x%04x:0x%04x\n",
++ ppci_0 & 0xffff, ppci_0 >> 16);
++ return 0;
++}
++
++static int
++nouveau_debugfs_memory_info(struct seq_file *m, void *data)
++{
++ struct drm_info_node *node = (struct drm_info_node *) m->private;
++ struct drm_minor *minor = node->minor;
++ struct drm_device *dev = minor->dev;
++
++ seq_printf(m, "VRAM total: %dKiB\n",
++ (int)(nouveau_mem_fb_amount(dev) >> 10));
++ return 0;
++}
++
++static struct drm_info_list nouveau_debugfs_list[] = {
++ { "chipset", nouveau_debugfs_chipset_info, 0, NULL },
++ { "memory", nouveau_debugfs_memory_info, 0, NULL },
++};
++#define NOUVEAU_DEBUGFS_ENTRIES ARRAY_SIZE(nouveau_debugfs_list)
++
++int
++nouveau_debugfs_init(struct drm_minor *minor)
++{
++ drm_debugfs_create_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES,
++ minor->debugfs_root, minor);
++ return 0;
++}
++
++void
++nouveau_debugfs_takedown(struct drm_minor *minor)
++{
++ drm_debugfs_remove_files(nouveau_debugfs_list, NOUVEAU_DEBUGFS_ENTRIES,
++ minor);
++}
+diff --git a/drivers/gpu/drm/nouveau/nouveau_display.c b/drivers/gpu/drm/nouveau/nouveau_display.c
+new file mode 100644
+index 0000000..dfc9439
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_display.c
+@@ -0,0 +1,115 @@
++/*
++ * Copyright (C) 2008 Maarten Maathuis.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "drm_crtc_helper.h"
++#include "nouveau_drv.h"
++#include "nouveau_fb.h"
++#include "nouveau_fbcon.h"
++
++static void
++nouveau_user_framebuffer_destroy(struct drm_framebuffer *drm_fb)
++{
++ struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
++ struct drm_device *dev = drm_fb->dev;
++
++ if (drm_fb->fbdev)
++ nouveau_fbcon_remove(dev, drm_fb);
++
++ if (fb->nvbo) {
++ mutex_lock(&dev->struct_mutex);
++ drm_gem_object_unreference(fb->nvbo->gem);
++ mutex_unlock(&dev->struct_mutex);
++ }
++
++ drm_framebuffer_cleanup(drm_fb);
++ kfree(fb);
++}
++
++static int
++nouveau_user_framebuffer_create_handle(struct drm_framebuffer *drm_fb,
++ struct drm_file *file_priv,
++ unsigned int *handle)
++{
++ struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
++
++ return drm_gem_handle_create(file_priv, fb->nvbo->gem, handle);
++}
++
++static const struct drm_framebuffer_funcs nouveau_framebuffer_funcs = {
++ .destroy = nouveau_user_framebuffer_destroy,
++ .create_handle = nouveau_user_framebuffer_create_handle,
++};
++
++struct drm_framebuffer *
++nouveau_framebuffer_create(struct drm_device *dev, struct nouveau_bo *nvbo,
++ struct drm_mode_fb_cmd *mode_cmd)
++{
++ struct nouveau_framebuffer *fb;
++ int ret;
++
++ fb = kzalloc(sizeof(struct nouveau_framebuffer), GFP_KERNEL);
++ if (!fb)
++ return NULL;
++
++ ret = drm_framebuffer_init(dev, &fb->base, &nouveau_framebuffer_funcs);
++ if (ret) {
++ kfree(fb);
++ return NULL;
++ }
++
++ drm_helper_mode_fill_fb_struct(&fb->base, mode_cmd);
++
++ fb->nvbo = nvbo;
++ return &fb->base;
++}
++
++static struct drm_framebuffer *
++nouveau_user_framebuffer_create(struct drm_device *dev,
++ struct drm_file *file_priv,
++ struct drm_mode_fb_cmd *mode_cmd)
++{
++ struct drm_framebuffer *fb;
++ struct drm_gem_object *gem;
++
++ gem = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle);
++ if (!gem)
++ return NULL;
++
++ fb = nouveau_framebuffer_create(dev, nouveau_gem_object(gem), mode_cmd);
++ if (!fb) {
++ drm_gem_object_unreference(gem);
++ return NULL;
++ }
++
++ return fb;
++}
++
++const struct drm_mode_config_funcs nouveau_mode_config_funcs = {
++ .fb_create = nouveau_user_framebuffer_create,
++ .fb_changed = nouveau_fbcon_probe,
++};
++
+diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.c b/drivers/gpu/drm/nouveau/nouveau_dma.c
+new file mode 100644
+index 0000000..50d9e67
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_dma.c
+@@ -0,0 +1,244 @@
++/*
++ * Copyright (C) 2007 Ben Skeggs.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++#include "nouveau_dma.h"
++
++void
++nouveau_dma_pre_init(struct nouveau_channel *chan)
++{
++ chan->dma.max = (chan->pushbuf_bo->bo.mem.size >> 2) - 2;
++ chan->dma.put = 0;
++ chan->dma.cur = chan->dma.put;
++ chan->dma.free = chan->dma.max - chan->dma.cur;
++}
++
++int
++nouveau_dma_init(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *m2mf = NULL;
++ struct nouveau_gpuobj *nvsw = NULL;
++ int ret, i;
++
++ /* Create NV_MEMORY_TO_MEMORY_FORMAT for buffer moves */
++ ret = nouveau_gpuobj_gr_new(chan, dev_priv->card_type < NV_50 ?
++ 0x0039 : 0x5039, &m2mf);
++ if (ret)
++ return ret;
++
++ ret = nouveau_gpuobj_ref_add(dev, chan, NvM2MF, m2mf, NULL);
++ if (ret)
++ return ret;
++
++ /* Create an NV_SW object for various sync purposes */
++ ret = nouveau_gpuobj_sw_new(chan, NV_SW, &nvsw);
++ if (ret)
++ return ret;
++
++ ret = nouveau_gpuobj_ref_add(dev, chan, NvSw, nvsw, NULL);
++ if (ret)
++ return ret;
++
++ /* NV_MEMORY_TO_MEMORY_FORMAT requires a notifier object */
++ ret = nouveau_notifier_alloc(chan, NvNotify0, 32, &chan->m2mf_ntfy);
++ if (ret)
++ return ret;
++
++ /* Map push buffer */
++ ret = nouveau_bo_map(chan->pushbuf_bo);
++ if (ret)
++ return ret;
++
++ /* Map M2MF notifier object - fbcon. */
++ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
++ ret = nouveau_bo_map(chan->notifier_bo);
++ if (ret)
++ return ret;
++ }
++
++ /* Insert NOPS for NOUVEAU_DMA_SKIPS */
++ ret = RING_SPACE(chan, NOUVEAU_DMA_SKIPS);
++ if (ret)
++ return ret;
++
++ for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
++ OUT_RING(chan, 0);
++
++ /* Initialise NV_MEMORY_TO_MEMORY_FORMAT */
++ ret = RING_SPACE(chan, 4);
++ if (ret)
++ return ret;
++ BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_NAME, 1);
++ OUT_RING(chan, NvM2MF);
++ BEGIN_RING(chan, NvSubM2MF, NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY, 1);
++ OUT_RING(chan, NvNotify0);
++
++ /* Initialise NV_SW */
++ ret = RING_SPACE(chan, 2);
++ if (ret)
++ return ret;
++ BEGIN_RING(chan, NvSubSw, 0, 1);
++ OUT_RING(chan, NvSw);
++
++ /* Sit back and pray the channel works.. */
++ FIRE_RING(chan);
++
++ return 0;
++}
++
++void
++OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords)
++{
++ bool is_iomem;
++ u32 *mem = ttm_kmap_obj_virtual(&chan->pushbuf_bo->kmap, &is_iomem);
++ mem = &mem[chan->dma.cur];
++ if (is_iomem)
++ memcpy_toio((void __force __iomem *)mem, data, nr_dwords * 4);
++ else
++ memcpy(mem, data, nr_dwords * 4);
++ chan->dma.cur += nr_dwords;
++}
++
++/* Fetch and adjust GPU GET pointer
++ *
++ * Returns:
++ * value >= 0, the adjusted GET pointer
++ * -EINVAL if GET pointer currently outside main push buffer
++ * -EBUSY if timeout exceeded
++ */
++static inline int
++READ_GET(struct nouveau_channel *chan, uint32_t *prev_get, uint32_t *timeout)
++{
++ uint32_t val;
++
++ val = nvchan_rd32(chan, chan->user_get);
++
++ /* reset counter as long as GET is still advancing, this is
++ * to avoid misdetecting a GPU lockup if the GPU happens to
++ * just be processing an operation that takes a long time
++ */
++ if (val != *prev_get) {
++ *prev_get = val;
++ *timeout = 0;
++ }
++
++ if ((++*timeout & 0xff) == 0) {
++ DRM_UDELAY(1);
++ if (*timeout > 100000)
++ return -EBUSY;
++ }
++
++ if (val < chan->pushbuf_base ||
++ val > chan->pushbuf_base + (chan->dma.max << 2))
++ return -EINVAL;
++
++ return (val - chan->pushbuf_base) >> 2;
++}
++
++int
++nouveau_dma_wait(struct nouveau_channel *chan, int size)
++{
++ uint32_t prev_get = 0, cnt = 0;
++ int get;
++
++ while (chan->dma.free < size) {
++ get = READ_GET(chan, &prev_get, &cnt);
++ if (unlikely(get == -EBUSY))
++ return -EBUSY;
++
++ /* loop until we have a usable GET pointer. the value
++ * we read from the GPU may be outside the main ring if
++ * PFIFO is processing a buffer called from the main ring,
++ * discard these values until something sensible is seen.
++ *
++ * the other case we discard GET is while the GPU is fetching
++ * from the SKIPS area, so the code below doesn't have to deal
++ * with some fun corner cases.
++ */
++ if (unlikely(get == -EINVAL) || get < NOUVEAU_DMA_SKIPS)
++ continue;
++
++ if (get <= chan->dma.cur) {
++ /* engine is fetching behind us, or is completely
++ * idle (GET == PUT) so we have free space up until
++ * the end of the push buffer
++ *
++ * we can only hit that path once per call due to
++ * looping back to the beginning of the push buffer,
++ * we'll hit the fetching-ahead-of-us path from that
++ * point on.
++ *
++ * the *one* exception to that rule is if we read
++ * GET==PUT, in which case the below conditional will
++ * always succeed and break us out of the wait loop.
++ */
++ chan->dma.free = chan->dma.max - chan->dma.cur;
++ if (chan->dma.free >= size)
++ break;
++
++ /* not enough space left at the end of the push buffer,
++ * instruct the GPU to jump back to the start right
++ * after processing the currently pending commands.
++ */
++ OUT_RING(chan, chan->pushbuf_base | 0x20000000);
++
++ /* wait for GET to depart from the skips area.
++ * prevents writing GET==PUT and causing a race
++ * condition that causes us to think the GPU is
++ * idle when it's not.
++ */
++ do {
++ get = READ_GET(chan, &prev_get, &cnt);
++ if (unlikely(get == -EBUSY))
++ return -EBUSY;
++ if (unlikely(get == -EINVAL))
++ continue;
++ } while (get <= NOUVEAU_DMA_SKIPS);
++ WRITE_PUT(NOUVEAU_DMA_SKIPS);
++
++ /* we're now submitting commands at the start of
++ * the push buffer.
++ */
++ chan->dma.cur =
++ chan->dma.put = NOUVEAU_DMA_SKIPS;
++ }
++
++ /* engine fetching ahead of us, we have space up until the
++ * current GET pointer. the "- 1" is to ensure there's
++ * space left to emit a jump back to the beginning of the
++ * push buffer if we require it. we can never get GET == PUT
++ * here, so this is safe.
++ */
++ chan->dma.free = get - chan->dma.cur - 1;
++ }
++
++ return 0;
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nouveau_dma.h b/drivers/gpu/drm/nouveau/nouveau_dma.h
+new file mode 100644
+index 0000000..dabfd65
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_dma.h
+@@ -0,0 +1,159 @@
++/*
++ * Copyright (C) 2007 Ben Skeggs.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#ifndef __NOUVEAU_DMA_H__
++#define __NOUVEAU_DMA_H__
++
++#ifndef NOUVEAU_DMA_DEBUG
++#define NOUVEAU_DMA_DEBUG 0
++#endif
++
++/*
++ * There's a hw race condition where you can't jump to your PUT offset,
++ * to avoid this we jump to offset + SKIPS and fill the difference with
++ * NOPs.
++ *
++ * xf86-video-nv configures the DMA fetch size to 32 bytes, and uses
++ * a SKIPS value of 8. Lets assume that the race condition is to do
++ * with writing into the fetch area, we configure a fetch size of 128
++ * bytes so we need a larger SKIPS value.
++ */
++#define NOUVEAU_DMA_SKIPS (128 / 4)
++
++/* Hardcoded object assignments to subchannels (subchannel id). */
++enum {
++ NvSubM2MF = 0,
++ NvSubSw = 1,
++ NvSub2D = 2,
++ NvSubCtxSurf2D = 2,
++ NvSubGdiRect = 3,
++ NvSubImageBlit = 4
++};
++
++/* Object handles. */
++enum {
++ NvM2MF = 0x80000001,
++ NvDmaFB = 0x80000002,
++ NvDmaTT = 0x80000003,
++ NvDmaVRAM = 0x80000004,
++ NvDmaGART = 0x80000005,
++ NvNotify0 = 0x80000006,
++ Nv2D = 0x80000007,
++ NvCtxSurf2D = 0x80000008,
++ NvRop = 0x80000009,
++ NvImagePatt = 0x8000000a,
++ NvClipRect = 0x8000000b,
++ NvGdiRect = 0x8000000c,
++ NvImageBlit = 0x8000000d,
++ NvSw = 0x8000000e,
++
++ /* G80+ display objects */
++ NvEvoVRAM = 0x01000000,
++ NvEvoFB16 = 0x01000001,
++ NvEvoFB32 = 0x01000002
++};
++
++#define NV_MEMORY_TO_MEMORY_FORMAT 0x00000039
++#define NV_MEMORY_TO_MEMORY_FORMAT_NAME 0x00000000
++#define NV_MEMORY_TO_MEMORY_FORMAT_SET_REF 0x00000050
++#define NV_MEMORY_TO_MEMORY_FORMAT_NOP 0x00000100
++#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY 0x00000104
++#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY_STYLE_WRITE 0x00000000
++#define NV_MEMORY_TO_MEMORY_FORMAT_NOTIFY_STYLE_WRITE_LE_AWAKEN 0x00000001
++#define NV_MEMORY_TO_MEMORY_FORMAT_DMA_NOTIFY 0x00000180
++#define NV_MEMORY_TO_MEMORY_FORMAT_DMA_SOURCE 0x00000184
++#define NV_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN 0x0000030c
++
++#define NV50_MEMORY_TO_MEMORY_FORMAT 0x00005039
++#define NV50_MEMORY_TO_MEMORY_FORMAT_UNK200 0x00000200
++#define NV50_MEMORY_TO_MEMORY_FORMAT_UNK21C 0x0000021c
++#define NV50_MEMORY_TO_MEMORY_FORMAT_OFFSET_IN_HIGH 0x00000238
++#define NV50_MEMORY_TO_MEMORY_FORMAT_OFFSET_OUT_HIGH 0x0000023c
++
++static __must_check inline int
++RING_SPACE(struct nouveau_channel *chan, int size)
++{
++ if (chan->dma.free < size) {
++ int ret;
++
++ ret = nouveau_dma_wait(chan, size);
++ if (ret)
++ return ret;
++ }
++
++ chan->dma.free -= size;
++ return 0;
++}
++
++static inline void
++OUT_RING(struct nouveau_channel *chan, int data)
++{
++ if (NOUVEAU_DMA_DEBUG) {
++ NV_INFO(chan->dev, "Ch%d/0x%08x: 0x%08x\n",
++ chan->id, chan->dma.cur << 2, data);
++ }
++
++ nouveau_bo_wr32(chan->pushbuf_bo, chan->dma.cur++, data);
++}
++
++extern void
++OUT_RINGp(struct nouveau_channel *chan, const void *data, unsigned nr_dwords);
++
++static inline void
++BEGIN_RING(struct nouveau_channel *chan, int subc, int mthd, int size)
++{
++ OUT_RING(chan, (subc << 13) | (size << 18) | mthd);
++}
++
++#define WRITE_PUT(val) do { \
++ DRM_MEMORYBARRIER(); \
++ nouveau_bo_rd32(chan->pushbuf_bo, 0); \
++ nvchan_wr32(chan, chan->user_put, ((val) << 2) + chan->pushbuf_base); \
++} while (0)
++
++static inline void
++FIRE_RING(struct nouveau_channel *chan)
++{
++ if (NOUVEAU_DMA_DEBUG) {
++ NV_INFO(chan->dev, "Ch%d/0x%08x: PUSH!\n",
++ chan->id, chan->dma.cur << 2);
++ }
++
++ if (chan->dma.cur == chan->dma.put)
++ return;
++ chan->accel_done = true;
++
++ WRITE_PUT(chan->dma.cur);
++ chan->dma.put = chan->dma.cur;
++}
++
++static inline void
++WIND_RING(struct nouveau_channel *chan)
++{
++ chan->dma.cur = chan->dma.put;
++}
++
++#endif
+diff --git a/drivers/gpu/drm/nouveau/nouveau_dp.c b/drivers/gpu/drm/nouveau/nouveau_dp.c
+new file mode 100644
+index 0000000..f954ad9
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_dp.c
+@@ -0,0 +1,575 @@
++/*
++ * Copyright 2009 Red Hat Inc.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ *
++ * Authors: Ben Skeggs
++ */
++
++#include "drmP.h"
++#include "nouveau_drv.h"
++#include "nouveau_i2c.h"
++#include "nouveau_encoder.h"
++
++static int
++auxch_rd(struct drm_encoder *encoder, int address, uint8_t *buf, int size)
++{
++ struct drm_device *dev = encoder->dev;
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct nouveau_i2c_chan *auxch;
++ int ret;
++
++ auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
++ if (!auxch)
++ return -ENODEV;
++
++ ret = nouveau_dp_auxch(auxch, 9, address, buf, size);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++static int
++auxch_wr(struct drm_encoder *encoder, int address, uint8_t *buf, int size)
++{
++ struct drm_device *dev = encoder->dev;
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct nouveau_i2c_chan *auxch;
++ int ret;
++
++ auxch = nouveau_i2c_find(dev, nv_encoder->dcb->i2c_index);
++ if (!auxch)
++ return -ENODEV;
++
++ ret = nouveau_dp_auxch(auxch, 8, address, buf, size);
++ return ret;
++}
++
++static int
++nouveau_dp_lane_count_set(struct drm_encoder *encoder, uint8_t cmd)
++{
++ struct drm_device *dev = encoder->dev;
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ uint32_t tmp;
++ int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1);
++
++ tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
++ tmp &= ~(NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED |
++ NV50_SOR_DP_CTRL_LANE_MASK);
++ tmp |= ((1 << (cmd & DP_LANE_COUNT_MASK)) - 1) << 16;
++ if (cmd & DP_LANE_COUNT_ENHANCED_FRAME_EN)
++ tmp |= NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED;
++ nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp);
++
++ return auxch_wr(encoder, DP_LANE_COUNT_SET, &cmd, 1);
++}
++
++static int
++nouveau_dp_link_bw_set(struct drm_encoder *encoder, uint8_t cmd)
++{
++ struct drm_device *dev = encoder->dev;
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ uint32_t tmp;
++ int reg = 0x614300 + (nv_encoder->or * 0x800);
++
++ tmp = nv_rd32(dev, reg);
++ tmp &= 0xfff3ffff;
++ if (cmd == DP_LINK_BW_2_7)
++ tmp |= 0x00040000;
++ nv_wr32(dev, reg, tmp);
++
++ return auxch_wr(encoder, DP_LINK_BW_SET, &cmd, 1);
++}
++
++static int
++nouveau_dp_link_train_set(struct drm_encoder *encoder, int pattern)
++{
++ struct drm_device *dev = encoder->dev;
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ uint32_t tmp;
++ uint8_t cmd;
++ int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1);
++ int ret;
++
++ tmp = nv_rd32(dev, NV50_SOR_DP_CTRL(or, link));
++ tmp &= ~NV50_SOR_DP_CTRL_TRAINING_PATTERN;
++ tmp |= (pattern << 24);
++ nv_wr32(dev, NV50_SOR_DP_CTRL(or, link), tmp);
++
++ ret = auxch_rd(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1);
++ if (ret)
++ return ret;
++ cmd &= ~DP_TRAINING_PATTERN_MASK;
++ cmd |= (pattern & DP_TRAINING_PATTERN_MASK);
++ return auxch_wr(encoder, DP_TRAINING_PATTERN_SET, &cmd, 1);
++}
++
++static int
++nouveau_dp_max_voltage_swing(struct drm_encoder *encoder)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_device *dev = encoder->dev;
++ struct bit_displayport_encoder_table_entry *dpse;
++ struct bit_displayport_encoder_table *dpe;
++ int i, dpe_headerlen, max_vs = 0;
++
++ dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
++ if (!dpe)
++ return false;
++ dpse = (void *)((char *)dpe + dpe_headerlen);
++
++ for (i = 0; i < dpe_headerlen; i++, dpse++) {
++ if (dpse->vs_level > max_vs)
++ max_vs = dpse->vs_level;
++ }
++
++ return max_vs;
++}
++
++static int
++nouveau_dp_max_pre_emphasis(struct drm_encoder *encoder, int vs)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_device *dev = encoder->dev;
++ struct bit_displayport_encoder_table_entry *dpse;
++ struct bit_displayport_encoder_table *dpe;
++ int i, dpe_headerlen, max_pre = 0;
++
++ dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
++ if (!dpe)
++ return false;
++ dpse = (void *)((char *)dpe + dpe_headerlen);
++
++ for (i = 0; i < dpe_headerlen; i++, dpse++) {
++ if (dpse->vs_level != vs)
++ continue;
++
++ if (dpse->pre_level > max_pre)
++ max_pre = dpse->pre_level;
++ }
++
++ return max_pre;
++}
++
++static bool
++nouveau_dp_link_train_adjust(struct drm_encoder *encoder, uint8_t *config)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_device *dev = encoder->dev;
++ struct bit_displayport_encoder_table_entry *dpse;
++ struct bit_displayport_encoder_table *dpe;
++ int ret, i, dpe_headerlen, vs = 0, pre = 0;
++ uint8_t request[2];
++
++ dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
++ if (!dpe)
++ return false;
++ dpse = (void *)((char *)dpe + dpe_headerlen);
++
++ ret = auxch_rd(encoder, DP_ADJUST_REQUEST_LANE0_1, request, 2);
++ if (ret)
++ return false;
++
++ NV_DEBUG_KMS(dev, "\t\tadjust 0x%02x 0x%02x\n", request[0], request[1]);
++
++ /* Keep all lanes at the same level.. */
++ for (i = 0; i < nv_encoder->dp.link_nr; i++) {
++ int lane_req = (request[i >> 1] >> ((i & 1) << 2)) & 0xf;
++ int lane_vs = lane_req & 3;
++ int lane_pre = (lane_req >> 2) & 3;
++
++ if (lane_vs > vs)
++ vs = lane_vs;
++ if (lane_pre > pre)
++ pre = lane_pre;
++ }
++
++ if (vs >= nouveau_dp_max_voltage_swing(encoder)) {
++ vs = nouveau_dp_max_voltage_swing(encoder);
++ vs |= 4;
++ }
++
++ if (pre >= nouveau_dp_max_pre_emphasis(encoder, vs & 3)) {
++ pre = nouveau_dp_max_pre_emphasis(encoder, vs & 3);
++ pre |= 4;
++ }
++
++ /* Update the configuration for all lanes.. */
++ for (i = 0; i < nv_encoder->dp.link_nr; i++)
++ config[i] = (pre << 3) | vs;
++
++ return true;
++}
++
++static bool
++nouveau_dp_link_train_commit(struct drm_encoder *encoder, uint8_t *config)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_device *dev = encoder->dev;
++ struct bit_displayport_encoder_table_entry *dpse;
++ struct bit_displayport_encoder_table *dpe;
++ int or = nv_encoder->or, link = !(nv_encoder->dcb->sorconf.link & 1);
++ int dpe_headerlen, ret, i;
++
++ NV_DEBUG_KMS(dev, "\t\tconfig 0x%02x 0x%02x 0x%02x 0x%02x\n",
++ config[0], config[1], config[2], config[3]);
++
++ dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
++ if (!dpe)
++ return false;
++ dpse = (void *)((char *)dpe + dpe_headerlen);
++
++ for (i = 0; i < dpe->record_nr; i++, dpse++) {
++ if (dpse->vs_level == (config[0] & 3) &&
++ dpse->pre_level == ((config[0] >> 3) & 3))
++ break;
++ }
++ BUG_ON(i == dpe->record_nr);
++
++ for (i = 0; i < nv_encoder->dp.link_nr; i++) {
++ const int shift[4] = { 16, 8, 0, 24 };
++ uint32_t mask = 0xff << shift[i];
++ uint32_t reg0, reg1, reg2;
++
++ reg0 = nv_rd32(dev, NV50_SOR_DP_UNK118(or, link)) & ~mask;
++ reg0 |= (dpse->reg0 << shift[i]);
++ reg1 = nv_rd32(dev, NV50_SOR_DP_UNK120(or, link)) & ~mask;
++ reg1 |= (dpse->reg1 << shift[i]);
++ reg2 = nv_rd32(dev, NV50_SOR_DP_UNK130(or, link)) & 0xffff00ff;
++ reg2 |= (dpse->reg2 << 8);
++ nv_wr32(dev, NV50_SOR_DP_UNK118(or, link), reg0);
++ nv_wr32(dev, NV50_SOR_DP_UNK120(or, link), reg1);
++ nv_wr32(dev, NV50_SOR_DP_UNK130(or, link), reg2);
++ }
++
++ ret = auxch_wr(encoder, DP_TRAINING_LANE0_SET, config, 4);
++ if (ret)
++ return false;
++
++ return true;
++}
++
++bool
++nouveau_dp_link_train(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ uint8_t config[4];
++ uint8_t status[3];
++ bool cr_done, cr_max_vs, eq_done;
++ int ret = 0, i, tries, voltage;
++
++ NV_DEBUG_KMS(dev, "link training!!\n");
++train:
++ cr_done = eq_done = false;
++
++ /* set link configuration */
++ NV_DEBUG_KMS(dev, "\tbegin train: bw %d, lanes %d\n",
++ nv_encoder->dp.link_bw, nv_encoder->dp.link_nr);
++
++ ret = nouveau_dp_link_bw_set(encoder, nv_encoder->dp.link_bw);
++ if (ret)
++ return false;
++
++ config[0] = nv_encoder->dp.link_nr;
++ if (nv_encoder->dp.dpcd_version >= 0x11)
++ config[0] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
++
++ ret = nouveau_dp_lane_count_set(encoder, config[0]);
++ if (ret)
++ return false;
++
++ /* clock recovery */
++ NV_DEBUG_KMS(dev, "\tbegin cr\n");
++ ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_1);
++ if (ret)
++ goto stop;
++
++ tries = 0;
++ voltage = -1;
++ memset(config, 0x00, sizeof(config));
++ for (;;) {
++ if (!nouveau_dp_link_train_commit(encoder, config))
++ break;
++
++ udelay(100);
++
++ ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 2);
++ if (ret)
++ break;
++ NV_DEBUG_KMS(dev, "\t\tstatus: 0x%02x 0x%02x\n",
++ status[0], status[1]);
++
++ cr_done = true;
++ cr_max_vs = false;
++ for (i = 0; i < nv_encoder->dp.link_nr; i++) {
++ int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf;
++
++ if (!(lane & DP_LANE_CR_DONE)) {
++ cr_done = false;
++ if (config[i] & DP_TRAIN_MAX_PRE_EMPHASIS_REACHED)
++ cr_max_vs = true;
++ break;
++ }
++ }
++
++ if ((config[0] & DP_TRAIN_VOLTAGE_SWING_MASK) != voltage) {
++ voltage = config[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
++ tries = 0;
++ }
++
++ if (cr_done || cr_max_vs || (++tries == 5))
++ break;
++
++ if (!nouveau_dp_link_train_adjust(encoder, config))
++ break;
++ }
++
++ if (!cr_done)
++ goto stop;
++
++ /* channel equalisation */
++ NV_DEBUG_KMS(dev, "\tbegin eq\n");
++ ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_2);
++ if (ret)
++ goto stop;
++
++ for (tries = 0; tries <= 5; tries++) {
++ udelay(400);
++
++ ret = auxch_rd(encoder, DP_LANE0_1_STATUS, status, 3);
++ if (ret)
++ break;
++ NV_DEBUG_KMS(dev, "\t\tstatus: 0x%02x 0x%02x\n",
++ status[0], status[1]);
++
++ eq_done = true;
++ if (!(status[2] & DP_INTERLANE_ALIGN_DONE))
++ eq_done = false;
++
++ for (i = 0; eq_done && i < nv_encoder->dp.link_nr; i++) {
++ int lane = (status[i >> 1] >> ((i & 1) * 4)) & 0xf;
++
++ if (!(lane & DP_LANE_CR_DONE)) {
++ cr_done = false;
++ break;
++ }
++
++ if (!(lane & DP_LANE_CHANNEL_EQ_DONE) ||
++ !(lane & DP_LANE_SYMBOL_LOCKED)) {
++ eq_done = false;
++ break;
++ }
++ }
++
++ if (eq_done || !cr_done)
++ break;
++
++ if (!nouveau_dp_link_train_adjust(encoder, config) ||
++ !nouveau_dp_link_train_commit(encoder, config))
++ break;
++ }
++
++stop:
++ /* end link training */
++ ret = nouveau_dp_link_train_set(encoder, DP_TRAINING_PATTERN_DISABLE);
++ if (ret)
++ return false;
++
++ /* retry at a lower setting, if possible */
++ if (!ret && !(eq_done && cr_done)) {
++ NV_DEBUG_KMS(dev, "\twe failed\n");
++ if (nv_encoder->dp.link_bw != DP_LINK_BW_1_62) {
++ NV_DEBUG_KMS(dev, "retry link training at low rate\n");
++ nv_encoder->dp.link_bw = DP_LINK_BW_1_62;
++ goto train;
++ }
++ }
++
++ return eq_done;
++}
++
++bool
++nouveau_dp_detect(struct drm_encoder *encoder)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_device *dev = encoder->dev;
++ uint8_t dpcd[4];
++ int ret;
++
++ ret = auxch_rd(encoder, 0x0000, dpcd, 4);
++ if (ret)
++ return false;
++
++ NV_DEBUG_KMS(dev, "encoder: link_bw %d, link_nr %d\n"
++ "display: link_bw %d, link_nr %d version 0x%02x\n",
++ nv_encoder->dcb->dpconf.link_bw,
++ nv_encoder->dcb->dpconf.link_nr,
++ dpcd[1], dpcd[2] & 0x0f, dpcd[0]);
++
++ nv_encoder->dp.dpcd_version = dpcd[0];
++
++ nv_encoder->dp.link_bw = dpcd[1];
++ if (nv_encoder->dp.link_bw != DP_LINK_BW_1_62 &&
++ !nv_encoder->dcb->dpconf.link_bw)
++ nv_encoder->dp.link_bw = DP_LINK_BW_1_62;
++
++ nv_encoder->dp.link_nr = dpcd[2] & 0xf;
++ if (nv_encoder->dp.link_nr > nv_encoder->dcb->dpconf.link_nr)
++ nv_encoder->dp.link_nr = nv_encoder->dcb->dpconf.link_nr;
++
++ return true;
++}
++
++int
++nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
++ uint8_t *data, int data_nr)
++{
++ struct drm_device *dev = auxch->dev;
++ uint32_t tmp, ctrl, stat = 0, data32[4] = {};
++ int ret = 0, i, index = auxch->rd;
++
++ NV_DEBUG_KMS(dev, "ch %d cmd %d addr 0x%x len %d\n", index, cmd, addr, data_nr);
++
++ tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
++ nv_wr32(dev, NV50_AUXCH_CTRL(auxch->rd), tmp | 0x00100000);
++ tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
++ if (!(tmp & 0x01000000)) {
++ NV_ERROR(dev, "expected bit 24 == 1, got 0x%08x\n", tmp);
++ ret = -EIO;
++ goto out;
++ }
++
++ for (i = 0; i < 3; i++) {
++ tmp = nv_rd32(dev, NV50_AUXCH_STAT(auxch->rd));
++ if (tmp & NV50_AUXCH_STAT_STATE_READY)
++ break;
++ udelay(100);
++ }
++
++ if (i == 3) {
++ ret = -EBUSY;
++ goto out;
++ }
++
++ if (!(cmd & 1)) {
++ memcpy(data32, data, data_nr);
++ for (i = 0; i < 4; i++) {
++ NV_DEBUG_KMS(dev, "wr %d: 0x%08x\n", i, data32[i]);
++ nv_wr32(dev, NV50_AUXCH_DATA_OUT(index, i), data32[i]);
++ }
++ }
++
++ nv_wr32(dev, NV50_AUXCH_ADDR(index), addr);
++ ctrl = nv_rd32(dev, NV50_AUXCH_CTRL(index));
++ ctrl &= ~(NV50_AUXCH_CTRL_CMD | NV50_AUXCH_CTRL_LEN);
++ ctrl |= (cmd << NV50_AUXCH_CTRL_CMD_SHIFT);
++ ctrl |= ((data_nr - 1) << NV50_AUXCH_CTRL_LEN_SHIFT);
++
++ for (;;) {
++ nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x80000000);
++ nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl);
++ nv_wr32(dev, NV50_AUXCH_CTRL(index), ctrl | 0x00010000);
++ if (!nv_wait(NV50_AUXCH_CTRL(index), 0x00010000, 0x00000000)) {
++ NV_ERROR(dev, "expected bit 16 == 0, got 0x%08x\n",
++ nv_rd32(dev, NV50_AUXCH_CTRL(index)));
++ ret = -EBUSY;
++ goto out;
++ }
++
++ udelay(400);
++
++ stat = nv_rd32(dev, NV50_AUXCH_STAT(index));
++ if ((stat & NV50_AUXCH_STAT_REPLY_AUX) !=
++ NV50_AUXCH_STAT_REPLY_AUX_DEFER)
++ break;
++ }
++
++ if (cmd & 1) {
++ if ((stat & NV50_AUXCH_STAT_COUNT) != data_nr) {
++ ret = -EREMOTEIO;
++ goto out;
++ }
++
++ for (i = 0; i < 4; i++) {
++ data32[i] = nv_rd32(dev, NV50_AUXCH_DATA_IN(index, i));
++ NV_DEBUG_KMS(dev, "rd %d: 0x%08x\n", i, data32[i]);
++ }
++ memcpy(data, data32, data_nr);
++ }
++
++out:
++ tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
++ nv_wr32(dev, NV50_AUXCH_CTRL(auxch->rd), tmp & ~0x00100000);
++ tmp = nv_rd32(dev, NV50_AUXCH_CTRL(auxch->rd));
++ if (tmp & 0x01000000) {
++ NV_ERROR(dev, "expected bit 24 == 0, got 0x%08x\n", tmp);
++ ret = -EIO;
++ }
++
++ udelay(400);
++
++ return ret ? ret : (stat & NV50_AUXCH_STAT_REPLY);
++}
++
++int
++nouveau_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
++ uint8_t write_byte, uint8_t *read_byte)
++{
++ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
++ struct nouveau_i2c_chan *auxch = (struct nouveau_i2c_chan *)adapter;
++ struct drm_device *dev = auxch->dev;
++ int ret = 0, cmd, addr = algo_data->address;
++ uint8_t *buf;
++
++ if (mode == MODE_I2C_READ) {
++ cmd = AUX_I2C_READ;
++ buf = read_byte;
++ } else {
++ cmd = (mode & MODE_I2C_READ) ? AUX_I2C_READ : AUX_I2C_WRITE;
++ buf = &write_byte;
++ }
++
++ if (!(mode & MODE_I2C_STOP))
++ cmd |= AUX_I2C_MOT;
++
++ if (mode & MODE_I2C_START)
++ return 1;
++
++ for (;;) {
++ ret = nouveau_dp_auxch(auxch, cmd, addr, buf, 1);
++ if (ret < 0)
++ return ret;
++
++ switch (ret & NV50_AUXCH_STAT_REPLY_I2C) {
++ case NV50_AUXCH_STAT_REPLY_I2C_ACK:
++ return 1;
++ case NV50_AUXCH_STAT_REPLY_I2C_NACK:
++ return -EREMOTEIO;
++ case NV50_AUXCH_STAT_REPLY_I2C_DEFER:
++ udelay(100);
++ break;
++ default:
++ NV_ERROR(dev, "invalid auxch status: 0x%08x\n", ret);
++ return -EREMOTEIO;
++ }
++ }
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.c b/drivers/gpu/drm/nouveau/nouveau_drv.c
+new file mode 100644
+index 0000000..da3b93b
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_drv.c
+@@ -0,0 +1,421 @@
++/*
++ * Copyright 2005 Stephane Marchesin.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++#include <linux/console.h>
++
++#include "drmP.h"
++#include "drm.h"
++#include "drm_crtc_helper.h"
++#include "nouveau_drv.h"
++#include "nouveau_hw.h"
++#include "nouveau_fb.h"
++#include "nouveau_fbcon.h"
++#include "nv50_display.h"
++
++#include "drm_pciids.h"
++
++MODULE_PARM_DESC(ctxfw, "Use external firmware blob for grctx init (NV40)");
++int nouveau_ctxfw = 0;
++module_param_named(ctxfw, nouveau_ctxfw, int, 0400);
++
++MODULE_PARM_DESC(noagp, "Disable AGP");
++int nouveau_noagp;
++module_param_named(noagp, nouveau_noagp, int, 0400);
++
++MODULE_PARM_DESC(modeset, "Enable kernel modesetting");
++static int nouveau_modeset = -1; /* kms */
++module_param_named(modeset, nouveau_modeset, int, 0400);
++
++MODULE_PARM_DESC(vbios, "Override default VBIOS location");
++char *nouveau_vbios;
++module_param_named(vbios, nouveau_vbios, charp, 0400);
++
++MODULE_PARM_DESC(vram_pushbuf, "Force DMA push buffers to be in VRAM");
++int nouveau_vram_pushbuf;
++module_param_named(vram_pushbuf, nouveau_vram_pushbuf, int, 0400);
++
++MODULE_PARM_DESC(vram_notify, "Force DMA notifiers to be in VRAM");
++int nouveau_vram_notify = 1;
++module_param_named(vram_notify, nouveau_vram_notify, int, 0400);
++
++MODULE_PARM_DESC(duallink, "Allow dual-link TMDS (>=GeForce 8)");
++int nouveau_duallink = 1;
++module_param_named(duallink, nouveau_duallink, int, 0400);
++
++MODULE_PARM_DESC(uscript_lvds, "LVDS output script table ID (>=GeForce 8)");
++int nouveau_uscript_lvds = -1;
++module_param_named(uscript_lvds, nouveau_uscript_lvds, int, 0400);
++
++MODULE_PARM_DESC(uscript_tmds, "TMDS output script table ID (>=GeForce 8)");
++int nouveau_uscript_tmds = -1;
++module_param_named(uscript_tmds, nouveau_uscript_tmds, int, 0400);
++
++MODULE_PARM_DESC(ignorelid, "Ignore ACPI lid status");
++int nouveau_ignorelid = 0;
++module_param_named(ignorelid, nouveau_ignorelid, int, 0400);
++
++MODULE_PARM_DESC(noagp, "Disable all acceleration");
++int nouveau_noaccel = 0;
++module_param_named(noaccel, nouveau_noaccel, int, 0400);
++
++MODULE_PARM_DESC(noagp, "Disable fbcon acceleration");
++int nouveau_nofbaccel = 0;
++module_param_named(nofbaccel, nouveau_nofbaccel, int, 0400);
++
++MODULE_PARM_DESC(tv_norm, "Default TV norm.\n"
++ "\t\tSupported: PAL, PAL-M, PAL-N, PAL-Nc, NTSC-M, NTSC-J,\n"
++ "\t\t\thd480i, hd480p, hd576i, hd576p, hd720p, hd1080i.\n"
++ "\t\tDefault: PAL\n"
++ "\t\t*NOTE* Ignored for cards with external TV encoders.");
++char *nouveau_tv_norm;
++module_param_named(tv_norm, nouveau_tv_norm, charp, 0400);
++
++MODULE_PARM_DESC(reg_debug, "Register access debug bitmask:\n"
++ "\t\t0x1 mc, 0x2 video, 0x4 fb, 0x8 extdev,\n"
++ "\t\t0x10 crtc, 0x20 ramdac, 0x40 vgacrtc, 0x80 rmvio,\n"
++ "\t\t0x100 vgaattr, 0x200 EVO (G80+). ");
++int nouveau_reg_debug;
++module_param_named(reg_debug, nouveau_reg_debug, int, 0600);
++
++int nouveau_fbpercrtc;
++#if 0
++module_param_named(fbpercrtc, nouveau_fbpercrtc, int, 0400);
++#endif
++
++static struct pci_device_id pciidlist[] = {
++ {
++ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),
++ .class = PCI_BASE_CLASS_DISPLAY << 16,
++ .class_mask = 0xff << 16,
++ },
++ {
++ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA_SGS, PCI_ANY_ID),
++ .class = PCI_BASE_CLASS_DISPLAY << 16,
++ .class_mask = 0xff << 16,
++ },
++ {}
++};
++
++MODULE_DEVICE_TABLE(pci, pciidlist);
++
++static struct drm_driver driver;
++
++static int __devinit
++nouveau_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
++{
++ return drm_get_dev(pdev, ent, &driver);
++}
++
++static void
++nouveau_pci_remove(struct pci_dev *pdev)
++{
++ struct drm_device *dev = pci_get_drvdata(pdev);
++
++ drm_put_dev(dev);
++}
++
++static int
++nouveau_pci_suspend(struct pci_dev *pdev, pm_message_t pm_state)
++{
++ struct drm_device *dev = pci_get_drvdata(pdev);
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_instmem_engine *pinstmem = &dev_priv->engine.instmem;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
++ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
++ struct nouveau_channel *chan;
++ struct drm_crtc *crtc;
++ uint32_t fbdev_flags;
++ int ret, i;
++
++ if (!drm_core_check_feature(dev, DRIVER_MODESET))
++ return -ENODEV;
++
++ if (pm_state.event == PM_EVENT_PRETHAW)
++ return 0;
++
++ fbdev_flags = dev_priv->fbdev_info->flags;
++ dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
++
++ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
++ struct nouveau_framebuffer *nouveau_fb;
++
++ nouveau_fb = nouveau_framebuffer(crtc->fb);
++ if (!nouveau_fb || !nouveau_fb->nvbo)
++ continue;
++
++ nouveau_bo_unpin(nouveau_fb->nvbo);
++ }
++
++ NV_INFO(dev, "Evicting buffers...\n");
++ ttm_bo_evict_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM);
++
++ NV_INFO(dev, "Idling channels...\n");
++ for (i = 0; i < pfifo->channels; i++) {
++ struct nouveau_fence *fence = NULL;
++
++ chan = dev_priv->fifos[i];
++ if (!chan || (dev_priv->card_type >= NV_50 &&
++ chan == dev_priv->fifos[0]))
++ continue;
++
++ ret = nouveau_fence_new(chan, &fence, true);
++ if (ret == 0) {
++ ret = nouveau_fence_wait(fence, NULL, false, false);
++ nouveau_fence_unref((void *)&fence);
++ }
++
++ if (ret) {
++ NV_ERROR(dev, "Failed to idle channel %d for suspend\n",
++ chan->id);
++ }
++ }
++
++ pgraph->fifo_access(dev, false);
++ nouveau_wait_for_idle(dev);
++ pfifo->reassign(dev, false);
++ pfifo->disable(dev);
++ pfifo->unload_context(dev);
++ pgraph->unload_context(dev);
++
++ NV_INFO(dev, "Suspending GPU objects...\n");
++ ret = nouveau_gpuobj_suspend(dev);
++ if (ret) {
++ NV_ERROR(dev, "... failed: %d\n", ret);
++ goto out_abort;
++ }
++
++ ret = pinstmem->suspend(dev);
++ if (ret) {
++ NV_ERROR(dev, "... failed: %d\n", ret);
++ nouveau_gpuobj_suspend_cleanup(dev);
++ goto out_abort;
++ }
++
++ NV_INFO(dev, "And we're gone!\n");
++ pci_save_state(pdev);
++ if (pm_state.event == PM_EVENT_SUSPEND) {
++ pci_disable_device(pdev);
++ pci_set_power_state(pdev, PCI_D3hot);
++ }
++
++ acquire_console_sem();
++ fb_set_suspend(dev_priv->fbdev_info, 1);
++ release_console_sem();
++ dev_priv->fbdev_info->flags = fbdev_flags;
++ return 0;
++
++out_abort:
++ NV_INFO(dev, "Re-enabling acceleration..\n");
++ pfifo->enable(dev);
++ pfifo->reassign(dev, true);
++ pgraph->fifo_access(dev, true);
++ return ret;
++}
++
++static int
++nouveau_pci_resume(struct pci_dev *pdev)
++{
++ struct drm_device *dev = pci_get_drvdata(pdev);
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_engine *engine = &dev_priv->engine;
++ struct drm_crtc *crtc;
++ uint32_t fbdev_flags;
++ int ret, i;
++
++ if (!drm_core_check_feature(dev, DRIVER_MODESET))
++ return -ENODEV;
++
++ fbdev_flags = dev_priv->fbdev_info->flags;
++ dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
++
++ NV_INFO(dev, "We're back, enabling device...\n");
++ pci_set_power_state(pdev, PCI_D0);
++ pci_restore_state(pdev);
++ if (pci_enable_device(pdev))
++ return -1;
++ pci_set_master(dev->pdev);
++
++ NV_INFO(dev, "POSTing device...\n");
++ ret = nouveau_run_vbios_init(dev);
++ if (ret)
++ return ret;
++
++ if (dev_priv->gart_info.type == NOUVEAU_GART_AGP) {
++ ret = nouveau_mem_init_agp(dev);
++ if (ret) {
++ NV_ERROR(dev, "error reinitialising AGP: %d\n", ret);
++ return ret;
++ }
++ }
++
++ NV_INFO(dev, "Reinitialising engines...\n");
++ engine->instmem.resume(dev);
++ engine->mc.init(dev);
++ engine->timer.init(dev);
++ engine->fb.init(dev);
++ engine->graph.init(dev);
++ engine->fifo.init(dev);
++
++ NV_INFO(dev, "Restoring GPU objects...\n");
++ nouveau_gpuobj_resume(dev);
++
++ nouveau_irq_postinstall(dev);
++
++ /* Re-write SKIPS, they'll have been lost over the suspend */
++ if (nouveau_vram_pushbuf) {
++ struct nouveau_channel *chan;
++ int j;
++
++ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
++ chan = dev_priv->fifos[i];
++ if (!chan || !chan->pushbuf_bo)
++ continue;
++
++ for (j = 0; j < NOUVEAU_DMA_SKIPS; j++)
++ nouveau_bo_wr32(chan->pushbuf_bo, i, 0);
++ }
++ }
++
++ NV_INFO(dev, "Restoring mode...\n");
++ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
++ struct nouveau_framebuffer *nouveau_fb;
++
++ nouveau_fb = nouveau_framebuffer(crtc->fb);
++ if (!nouveau_fb || !nouveau_fb->nvbo)
++ continue;
++
++ nouveau_bo_pin(nouveau_fb->nvbo, TTM_PL_FLAG_VRAM);
++ }
++
++ if (dev_priv->card_type < NV_50) {
++ nv04_display_restore(dev);
++ NVLockVgaCrtcs(dev, false);
++ } else
++ nv50_display_init(dev);
++
++ /* Force CLUT to get re-loaded during modeset */
++ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++
++ nv_crtc->lut.depth = 0;
++ }
++
++ acquire_console_sem();
++ fb_set_suspend(dev_priv->fbdev_info, 0);
++ release_console_sem();
++
++ nouveau_fbcon_zfill(dev);
++
++ drm_helper_resume_force_mode(dev);
++ dev_priv->fbdev_info->flags = fbdev_flags;
++ return 0;
++}
++
++static struct drm_driver driver = {
++ .driver_features =
++ DRIVER_USE_AGP | DRIVER_PCI_DMA | DRIVER_SG |
++ DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED | DRIVER_GEM,
++ .load = nouveau_load,
++ .firstopen = nouveau_firstopen,
++ .lastclose = nouveau_lastclose,
++ .unload = nouveau_unload,
++ .preclose = nouveau_preclose,
++#if defined(CONFIG_DRM_NOUVEAU_DEBUG)
++ .debugfs_init = nouveau_debugfs_init,
++ .debugfs_cleanup = nouveau_debugfs_takedown,
++#endif
++ .irq_preinstall = nouveau_irq_preinstall,
++ .irq_postinstall = nouveau_irq_postinstall,
++ .irq_uninstall = nouveau_irq_uninstall,
++ .irq_handler = nouveau_irq_handler,
++ .reclaim_buffers = drm_core_reclaim_buffers,
++ .get_map_ofs = drm_core_get_map_ofs,
++ .get_reg_ofs = drm_core_get_reg_ofs,
++ .ioctls = nouveau_ioctls,
++ .fops = {
++ .owner = THIS_MODULE,
++ .open = drm_open,
++ .release = drm_release,
++ .unlocked_ioctl = drm_ioctl,
++ .mmap = nouveau_ttm_mmap,
++ .poll = drm_poll,
++ .fasync = drm_fasync,
++#if defined(CONFIG_COMPAT)
++ .compat_ioctl = nouveau_compat_ioctl,
++#endif
++ },
++ .pci_driver = {
++ .name = DRIVER_NAME,
++ .id_table = pciidlist,
++ .probe = nouveau_pci_probe,
++ .remove = nouveau_pci_remove,
++ .suspend = nouveau_pci_suspend,
++ .resume = nouveau_pci_resume
++ },
++
++ .gem_init_object = nouveau_gem_object_new,
++ .gem_free_object = nouveau_gem_object_del,
++
++ .name = DRIVER_NAME,
++ .desc = DRIVER_DESC,
++#ifdef GIT_REVISION
++ .date = GIT_REVISION,
++#else
++ .date = DRIVER_DATE,
++#endif
++ .major = DRIVER_MAJOR,
++ .minor = DRIVER_MINOR,
++ .patchlevel = DRIVER_PATCHLEVEL,
++};
++
++static int __init nouveau_init(void)
++{
++ driver.num_ioctls = nouveau_max_ioctl;
++
++ if (nouveau_modeset == -1) {
++#ifdef CONFIG_VGA_CONSOLE
++ if (vgacon_text_force())
++ nouveau_modeset = 0;
++ else
++#endif
++ nouveau_modeset = 1;
++ }
++
++ if (nouveau_modeset == 1)
++ driver.driver_features |= DRIVER_MODESET;
++
++ return drm_init(&driver);
++}
++
++static void __exit nouveau_exit(void)
++{
++ drm_exit(&driver);
++}
++
++module_init(nouveau_init);
++module_exit(nouveau_exit);
++
++MODULE_AUTHOR(DRIVER_AUTHOR);
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_LICENSE("GPL and additional rights");
+diff --git a/drivers/gpu/drm/nouveau/nouveau_drv.h b/drivers/gpu/drm/nouveau/nouveau_drv.h
+new file mode 100644
+index 0000000..5445cef
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_drv.h
+@@ -0,0 +1,1350 @@
++/*
++ * Copyright 2005 Stephane Marchesin.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef __NOUVEAU_DRV_H__
++#define __NOUVEAU_DRV_H__
++
++#define DRIVER_AUTHOR "Stephane Marchesin"
++#define DRIVER_EMAIL "dri-devel@lists.sourceforge.net"
++
++#define DRIVER_NAME "nouveau"
++#define DRIVER_DESC "nVidia Riva/TNT/GeForce"
++#define DRIVER_DATE "20090420"
++
++#define DRIVER_MAJOR 0
++#define DRIVER_MINOR 0
++#define DRIVER_PATCHLEVEL 15
++
++#define NOUVEAU_FAMILY 0x0000FFFF
++#define NOUVEAU_FLAGS 0xFFFF0000
++
++#include "ttm/ttm_bo_api.h"
++#include "ttm/ttm_bo_driver.h"
++#include "ttm/ttm_placement.h"
++#include "ttm/ttm_memory.h"
++#include "ttm/ttm_module.h"
++
++struct nouveau_fpriv {
++ struct ttm_object_file *tfile;
++};
++
++#define DRM_FILE_PAGE_OFFSET (0x100000000ULL >> PAGE_SHIFT)
++
++#include "nouveau_drm.h"
++#include "nouveau_reg.h"
++#include "nouveau_bios.h"
++struct nouveau_grctx;
++
++#define MAX_NUM_DCB_ENTRIES 16
++
++#define NOUVEAU_MAX_CHANNEL_NR 128
++#define NOUVEAU_MAX_TILE_NR 15
++
++#define NV50_VM_MAX_VRAM (2*1024*1024*1024ULL)
++#define NV50_VM_BLOCK (512*1024*1024ULL)
++#define NV50_VM_VRAM_NR (NV50_VM_MAX_VRAM / NV50_VM_BLOCK)
++
++struct nouveau_tile_reg {
++ struct nouveau_fence *fence;
++ uint32_t addr;
++ uint32_t size;
++ bool used;
++};
++
++struct nouveau_bo {
++ struct ttm_buffer_object bo;
++ struct ttm_placement placement;
++ u32 placements[3];
++ struct ttm_bo_kmap_obj kmap;
++ struct list_head head;
++
++ /* protected by ttm_bo_reserve() */
++ struct drm_file *reserved_by;
++ struct list_head entry;
++ int pbbo_index;
++
++ struct nouveau_channel *channel;
++
++ bool mappable;
++ bool no_vm;
++
++ uint32_t tile_mode;
++ uint32_t tile_flags;
++ struct nouveau_tile_reg *tile;
++
++ struct drm_gem_object *gem;
++ struct drm_file *cpu_filp;
++ int pin_refcnt;
++};
++
++static inline struct nouveau_bo *
++nouveau_bo(struct ttm_buffer_object *bo)
++{
++ return container_of(bo, struct nouveau_bo, bo);
++}
++
++static inline struct nouveau_bo *
++nouveau_gem_object(struct drm_gem_object *gem)
++{
++ return gem ? gem->driver_private : NULL;
++}
++
++/* TODO: submit equivalent to TTM generic API upstream? */
++static inline void __iomem *
++nvbo_kmap_obj_iovirtual(struct nouveau_bo *nvbo)
++{
++ bool is_iomem;
++ void __iomem *ioptr = (void __force __iomem *)ttm_kmap_obj_virtual(
++ &nvbo->kmap, &is_iomem);
++ WARN_ON_ONCE(ioptr && !is_iomem);
++ return ioptr;
++}
++
++struct mem_block {
++ struct mem_block *next;
++ struct mem_block *prev;
++ uint64_t start;
++ uint64_t size;
++ struct drm_file *file_priv; /* NULL: free, -1: heap, other: real files */
++};
++
++enum nouveau_flags {
++ NV_NFORCE = 0x10000000,
++ NV_NFORCE2 = 0x20000000
++};
++
++#define NVOBJ_ENGINE_SW 0
++#define NVOBJ_ENGINE_GR 1
++#define NVOBJ_ENGINE_DISPLAY 2
++#define NVOBJ_ENGINE_INT 0xdeadbeef
++
++#define NVOBJ_FLAG_ALLOW_NO_REFS (1 << 0)
++#define NVOBJ_FLAG_ZERO_ALLOC (1 << 1)
++#define NVOBJ_FLAG_ZERO_FREE (1 << 2)
++#define NVOBJ_FLAG_FAKE (1 << 3)
++struct nouveau_gpuobj {
++ struct list_head list;
++
++ struct nouveau_channel *im_channel;
++ struct mem_block *im_pramin;
++ struct nouveau_bo *im_backing;
++ uint32_t im_backing_start;
++ uint32_t *im_backing_suspend;
++ int im_bound;
++
++ uint32_t flags;
++ int refcount;
++
++ uint32_t engine;
++ uint32_t class;
++
++ void (*dtor)(struct drm_device *, struct nouveau_gpuobj *);
++ void *priv;
++};
++
++struct nouveau_gpuobj_ref {
++ struct list_head list;
++
++ struct nouveau_gpuobj *gpuobj;
++ uint32_t instance;
++
++ struct nouveau_channel *channel;
++ int handle;
++};
++
++struct nouveau_channel {
++ struct drm_device *dev;
++ int id;
++
++ /* owner of this fifo */
++ struct drm_file *file_priv;
++ /* mapping of the fifo itself */
++ struct drm_local_map *map;
++
++ /* mapping of the regs controling the fifo */
++ void __iomem *user;
++ uint32_t user_get;
++ uint32_t user_put;
++
++ /* Fencing */
++ struct {
++ /* lock protects the pending list only */
++ spinlock_t lock;
++ struct list_head pending;
++ uint32_t sequence;
++ uint32_t sequence_ack;
++ uint32_t last_sequence_irq;
++ } fence;
++
++ /* DMA push buffer */
++ struct nouveau_gpuobj_ref *pushbuf;
++ struct nouveau_bo *pushbuf_bo;
++ uint32_t pushbuf_base;
++
++ /* Notifier memory */
++ struct nouveau_bo *notifier_bo;
++ struct mem_block *notifier_heap;
++
++ /* PFIFO context */
++ struct nouveau_gpuobj_ref *ramfc;
++ struct nouveau_gpuobj_ref *cache;
++
++ /* PGRAPH context */
++ /* XXX may be merge 2 pointers as private data ??? */
++ struct nouveau_gpuobj_ref *ramin_grctx;
++ void *pgraph_ctx;
++
++ /* NV50 VM */
++ struct nouveau_gpuobj *vm_pd;
++ struct nouveau_gpuobj_ref *vm_gart_pt;
++ struct nouveau_gpuobj_ref *vm_vram_pt[NV50_VM_VRAM_NR];
++
++ /* Objects */
++ struct nouveau_gpuobj_ref *ramin; /* Private instmem */
++ struct mem_block *ramin_heap; /* Private PRAMIN heap */
++ struct nouveau_gpuobj_ref *ramht; /* Hash table */
++ struct list_head ramht_refs; /* Objects referenced by RAMHT */
++
++ /* GPU object info for stuff used in-kernel (mm_enabled) */
++ uint32_t m2mf_ntfy;
++ uint32_t vram_handle;
++ uint32_t gart_handle;
++ bool accel_done;
++
++ /* Push buffer state (only for drm's channel on !mm_enabled) */
++ struct {
++ int max;
++ int free;
++ int cur;
++ int put;
++ /* access via pushbuf_bo */
++ } dma;
++
++ uint32_t sw_subchannel[8];
++
++ struct {
++ struct nouveau_gpuobj *vblsem;
++ uint32_t vblsem_offset;
++ uint32_t vblsem_rval;
++ struct list_head vbl_wait;
++ } nvsw;
++
++ struct {
++ bool active;
++ char name[32];
++ struct drm_info_list info;
++ } debugfs;
++};
++
++struct nouveau_instmem_engine {
++ void *priv;
++
++ int (*init)(struct drm_device *dev);
++ void (*takedown)(struct drm_device *dev);
++ int (*suspend)(struct drm_device *dev);
++ void (*resume)(struct drm_device *dev);
++
++ int (*populate)(struct drm_device *, struct nouveau_gpuobj *,
++ uint32_t *size);
++ void (*clear)(struct drm_device *, struct nouveau_gpuobj *);
++ int (*bind)(struct drm_device *, struct nouveau_gpuobj *);
++ int (*unbind)(struct drm_device *, struct nouveau_gpuobj *);
++ void (*prepare_access)(struct drm_device *, bool write);
++ void (*finish_access)(struct drm_device *);
++};
++
++struct nouveau_mc_engine {
++ int (*init)(struct drm_device *dev);
++ void (*takedown)(struct drm_device *dev);
++};
++
++struct nouveau_timer_engine {
++ int (*init)(struct drm_device *dev);
++ void (*takedown)(struct drm_device *dev);
++ uint64_t (*read)(struct drm_device *dev);
++};
++
++struct nouveau_fb_engine {
++ int num_tiles;
++
++ int (*init)(struct drm_device *dev);
++ void (*takedown)(struct drm_device *dev);
++
++ void (*set_region_tiling)(struct drm_device *dev, int i, uint32_t addr,
++ uint32_t size, uint32_t pitch);
++};
++
++struct nouveau_fifo_engine {
++ void *priv;
++
++ int channels;
++
++ int (*init)(struct drm_device *);
++ void (*takedown)(struct drm_device *);
++
++ void (*disable)(struct drm_device *);
++ void (*enable)(struct drm_device *);
++ bool (*reassign)(struct drm_device *, bool enable);
++ bool (*cache_flush)(struct drm_device *dev);
++ bool (*cache_pull)(struct drm_device *dev, bool enable);
++
++ int (*channel_id)(struct drm_device *);
++
++ int (*create_context)(struct nouveau_channel *);
++ void (*destroy_context)(struct nouveau_channel *);
++ int (*load_context)(struct nouveau_channel *);
++ int (*unload_context)(struct drm_device *);
++};
++
++struct nouveau_pgraph_object_method {
++ int id;
++ int (*exec)(struct nouveau_channel *chan, int grclass, int mthd,
++ uint32_t data);
++};
++
++struct nouveau_pgraph_object_class {
++ int id;
++ bool software;
++ struct nouveau_pgraph_object_method *methods;
++};
++
++struct nouveau_pgraph_engine {
++ struct nouveau_pgraph_object_class *grclass;
++ bool accel_blocked;
++ void *ctxprog;
++ void *ctxvals;
++ int grctx_size;
++
++ int (*init)(struct drm_device *);
++ void (*takedown)(struct drm_device *);
++
++ void (*fifo_access)(struct drm_device *, bool);
++
++ struct nouveau_channel *(*channel)(struct drm_device *);
++ int (*create_context)(struct nouveau_channel *);
++ void (*destroy_context)(struct nouveau_channel *);
++ int (*load_context)(struct nouveau_channel *);
++ int (*unload_context)(struct drm_device *);
++
++ void (*set_region_tiling)(struct drm_device *dev, int i, uint32_t addr,
++ uint32_t size, uint32_t pitch);
++};
++
++struct nouveau_engine {
++ struct nouveau_instmem_engine instmem;
++ struct nouveau_mc_engine mc;
++ struct nouveau_timer_engine timer;
++ struct nouveau_fb_engine fb;
++ struct nouveau_pgraph_engine graph;
++ struct nouveau_fifo_engine fifo;
++};
++
++struct nouveau_pll_vals {
++ union {
++ struct {
++#ifdef __BIG_ENDIAN
++ uint8_t N1, M1, N2, M2;
++#else
++ uint8_t M1, N1, M2, N2;
++#endif
++ };
++ struct {
++ uint16_t NM1, NM2;
++ } __attribute__((packed));
++ };
++ int log2P;
++
++ int refclk;
++};
++
++enum nv04_fp_display_regs {
++ FP_DISPLAY_END,
++ FP_TOTAL,
++ FP_CRTC,
++ FP_SYNC_START,
++ FP_SYNC_END,
++ FP_VALID_START,
++ FP_VALID_END
++};
++
++struct nv04_crtc_reg {
++ unsigned char MiscOutReg; /* */
++ uint8_t CRTC[0x9f];
++ uint8_t CR58[0x10];
++ uint8_t Sequencer[5];
++ uint8_t Graphics[9];
++ uint8_t Attribute[21];
++ unsigned char DAC[768]; /* Internal Colorlookuptable */
++
++ /* PCRTC regs */
++ uint32_t fb_start;
++ uint32_t crtc_cfg;
++ uint32_t cursor_cfg;
++ uint32_t gpio_ext;
++ uint32_t crtc_830;
++ uint32_t crtc_834;
++ uint32_t crtc_850;
++ uint32_t crtc_eng_ctrl;
++
++ /* PRAMDAC regs */
++ uint32_t nv10_cursync;
++ struct nouveau_pll_vals pllvals;
++ uint32_t ramdac_gen_ctrl;
++ uint32_t ramdac_630;
++ uint32_t ramdac_634;
++ uint32_t tv_setup;
++ uint32_t tv_vtotal;
++ uint32_t tv_vskew;
++ uint32_t tv_vsync_delay;
++ uint32_t tv_htotal;
++ uint32_t tv_hskew;
++ uint32_t tv_hsync_delay;
++ uint32_t tv_hsync_delay2;
++ uint32_t fp_horiz_regs[7];
++ uint32_t fp_vert_regs[7];
++ uint32_t dither;
++ uint32_t fp_control;
++ uint32_t dither_regs[6];
++ uint32_t fp_debug_0;
++ uint32_t fp_debug_1;
++ uint32_t fp_debug_2;
++ uint32_t fp_margin_color;
++ uint32_t ramdac_8c0;
++ uint32_t ramdac_a20;
++ uint32_t ramdac_a24;
++ uint32_t ramdac_a34;
++ uint32_t ctv_regs[38];
++};
++
++struct nv04_output_reg {
++ uint32_t output;
++ int head;
++};
++
++struct nv04_mode_state {
++ uint32_t bpp;
++ uint32_t width;
++ uint32_t height;
++ uint32_t interlace;
++ uint32_t repaint0;
++ uint32_t repaint1;
++ uint32_t screen;
++ uint32_t scale;
++ uint32_t dither;
++ uint32_t extra;
++ uint32_t fifo;
++ uint32_t pixel;
++ uint32_t horiz;
++ int arbitration0;
++ int arbitration1;
++ uint32_t pll;
++ uint32_t pllB;
++ uint32_t vpll;
++ uint32_t vpll2;
++ uint32_t vpllB;
++ uint32_t vpll2B;
++ uint32_t pllsel;
++ uint32_t sel_clk;
++ uint32_t general;
++ uint32_t crtcOwner;
++ uint32_t head;
++ uint32_t head2;
++ uint32_t cursorConfig;
++ uint32_t cursor0;
++ uint32_t cursor1;
++ uint32_t cursor2;
++ uint32_t timingH;
++ uint32_t timingV;
++ uint32_t displayV;
++ uint32_t crtcSync;
++
++ struct nv04_crtc_reg crtc_reg[2];
++};
++
++enum nouveau_card_type {
++ NV_04 = 0x00,
++ NV_10 = 0x10,
++ NV_20 = 0x20,
++ NV_30 = 0x30,
++ NV_40 = 0x40,
++ NV_50 = 0x50,
++};
++
++struct drm_nouveau_private {
++ struct drm_device *dev;
++ enum {
++ NOUVEAU_CARD_INIT_DOWN,
++ NOUVEAU_CARD_INIT_DONE,
++ NOUVEAU_CARD_INIT_FAILED
++ } init_state;
++
++ /* the card type, takes NV_* as values */
++ enum nouveau_card_type card_type;
++ /* exact chipset, derived from NV_PMC_BOOT_0 */
++ int chipset;
++ int flags;
++
++ void __iomem *mmio;
++ void __iomem *ramin;
++ uint32_t ramin_size;
++
++ struct nouveau_bo *vga_ram;
++
++ struct workqueue_struct *wq;
++ struct work_struct irq_work;
++
++ struct list_head vbl_waiting;
++
++ struct {
++ struct ttm_global_reference mem_global_ref;
++ struct ttm_bo_global_ref bo_global_ref;
++ struct ttm_bo_device bdev;
++ spinlock_t bo_list_lock;
++ struct list_head bo_list;
++ atomic_t validate_sequence;
++ } ttm;
++
++ struct fb_info *fbdev_info;
++
++ int fifo_alloc_count;
++ struct nouveau_channel *fifos[NOUVEAU_MAX_CHANNEL_NR];
++
++ struct nouveau_engine engine;
++ struct nouveau_channel *channel;
++
++ /* RAMIN configuration, RAMFC, RAMHT and RAMRO offsets */
++ struct nouveau_gpuobj *ramht;
++ uint32_t ramin_rsvd_vram;
++ uint32_t ramht_offset;
++ uint32_t ramht_size;
++ uint32_t ramht_bits;
++ uint32_t ramfc_offset;
++ uint32_t ramfc_size;
++ uint32_t ramro_offset;
++ uint32_t ramro_size;
++
++ /* base physical adresses */
++ uint64_t fb_phys;
++ uint64_t fb_available_size;
++ uint64_t fb_mappable_pages;
++ uint64_t fb_aper_free;
++
++ struct {
++ enum {
++ NOUVEAU_GART_NONE = 0,
++ NOUVEAU_GART_AGP,
++ NOUVEAU_GART_SGDMA
++ } type;
++ uint64_t aper_base;
++ uint64_t aper_size;
++ uint64_t aper_free;
++
++ struct nouveau_gpuobj *sg_ctxdma;
++ struct page *sg_dummy_page;
++ dma_addr_t sg_dummy_bus;
++
++ /* nottm hack */
++ struct drm_ttm_backend *sg_be;
++ unsigned long sg_handle;
++ } gart_info;
++
++ /* nv10-nv40 tiling regions */
++ struct {
++ struct nouveau_tile_reg reg[NOUVEAU_MAX_TILE_NR];
++ spinlock_t lock;
++ } tile;
++
++ /* G8x/G9x virtual address space */
++ uint64_t vm_gart_base;
++ uint64_t vm_gart_size;
++ uint64_t vm_vram_base;
++ uint64_t vm_vram_size;
++ uint64_t vm_end;
++ struct nouveau_gpuobj *vm_vram_pt[NV50_VM_VRAM_NR];
++ int vm_vram_pt_nr;
++
++ /* the mtrr covering the FB */
++ int fb_mtrr;
++
++ struct mem_block *ramin_heap;
++
++ /* context table pointed to be NV_PGRAPH_CHANNEL_CTX_TABLE (0x400780) */
++ uint32_t ctx_table_size;
++ struct nouveau_gpuobj_ref *ctx_table;
++
++ struct list_head gpuobj_list;
++
++ struct nvbios VBIOS;
++ struct nouveau_bios_info *vbios;
++
++ struct nv04_mode_state mode_reg;
++ struct nv04_mode_state saved_reg;
++ uint32_t saved_vga_font[4][16384];
++ uint32_t crtc_owner;
++ uint32_t dac_users[4];
++
++ struct nouveau_suspend_resume {
++ uint32_t fifo_mode;
++ uint32_t graph_ctx_control;
++ uint32_t graph_state;
++ uint32_t *ramin_copy;
++ uint64_t ramin_size;
++ } susres;
++
++ struct backlight_device *backlight;
++ bool acpi_dsm;
++
++ struct nouveau_channel *evo;
++
++ struct {
++ struct dentry *channel_root;
++ } debugfs;
++};
++
++static inline struct drm_nouveau_private *
++nouveau_bdev(struct ttm_bo_device *bd)
++{
++ return container_of(bd, struct drm_nouveau_private, ttm.bdev);
++}
++
++static inline int
++nouveau_bo_ref(struct nouveau_bo *ref, struct nouveau_bo **pnvbo)
++{
++ struct nouveau_bo *prev;
++
++ if (!pnvbo)
++ return -EINVAL;
++ prev = *pnvbo;
++
++ *pnvbo = ref ? nouveau_bo(ttm_bo_reference(&ref->bo)) : NULL;
++ if (prev) {
++ struct ttm_buffer_object *bo = &prev->bo;
++
++ ttm_bo_unref(&bo);
++ }
++
++ return 0;
++}
++
++#define NOUVEAU_CHECK_INITIALISED_WITH_RETURN do { \
++ struct drm_nouveau_private *nv = dev->dev_private; \
++ if (nv->init_state != NOUVEAU_CARD_INIT_DONE) { \
++ NV_ERROR(dev, "called without init\n"); \
++ return -EINVAL; \
++ } \
++} while (0)
++
++#define NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(id, cl, ch) do { \
++ struct drm_nouveau_private *nv = dev->dev_private; \
++ if (!nouveau_channel_owner(dev, (cl), (id))) { \
++ NV_ERROR(dev, "pid %d doesn't own channel %d\n", \
++ DRM_CURRENTPID, (id)); \
++ return -EPERM; \
++ } \
++ (ch) = nv->fifos[(id)]; \
++} while (0)
++
++/* nouveau_drv.c */
++extern int nouveau_noagp;
++extern int nouveau_duallink;
++extern int nouveau_uscript_lvds;
++extern int nouveau_uscript_tmds;
++extern int nouveau_vram_pushbuf;
++extern int nouveau_vram_notify;
++extern int nouveau_fbpercrtc;
++extern char *nouveau_tv_norm;
++extern int nouveau_reg_debug;
++extern char *nouveau_vbios;
++extern int nouveau_ctxfw;
++extern int nouveau_ignorelid;
++extern int nouveau_nofbaccel;
++extern int nouveau_noaccel;
++
++/* nouveau_state.c */
++extern void nouveau_preclose(struct drm_device *dev, struct drm_file *);
++extern int nouveau_load(struct drm_device *, unsigned long flags);
++extern int nouveau_firstopen(struct drm_device *);
++extern void nouveau_lastclose(struct drm_device *);
++extern int nouveau_unload(struct drm_device *);
++extern int nouveau_ioctl_getparam(struct drm_device *, void *data,
++ struct drm_file *);
++extern int nouveau_ioctl_setparam(struct drm_device *, void *data,
++ struct drm_file *);
++extern bool nouveau_wait_until(struct drm_device *, uint64_t timeout,
++ uint32_t reg, uint32_t mask, uint32_t val);
++extern bool nouveau_wait_for_idle(struct drm_device *);
++extern int nouveau_card_init(struct drm_device *);
++extern int nouveau_ioctl_card_init(struct drm_device *, void *data,
++ struct drm_file *);
++extern int nouveau_ioctl_suspend(struct drm_device *, void *data,
++ struct drm_file *);
++extern int nouveau_ioctl_resume(struct drm_device *, void *data,
++ struct drm_file *);
++
++/* nouveau_mem.c */
++extern int nouveau_mem_init_heap(struct mem_block **, uint64_t start,
++ uint64_t size);
++extern struct mem_block *nouveau_mem_alloc_block(struct mem_block *,
++ uint64_t size, int align2,
++ struct drm_file *, int tail);
++extern void nouveau_mem_takedown(struct mem_block **heap);
++extern void nouveau_mem_free_block(struct mem_block *);
++extern uint64_t nouveau_mem_fb_amount(struct drm_device *);
++extern void nouveau_mem_release(struct drm_file *, struct mem_block *heap);
++extern int nouveau_mem_init(struct drm_device *);
++extern int nouveau_mem_init_agp(struct drm_device *);
++extern void nouveau_mem_close(struct drm_device *);
++extern struct nouveau_tile_reg *nv10_mem_set_tiling(struct drm_device *dev,
++ uint32_t addr,
++ uint32_t size,
++ uint32_t pitch);
++extern void nv10_mem_expire_tiling(struct drm_device *dev,
++ struct nouveau_tile_reg *tile,
++ struct nouveau_fence *fence);
++extern int nv50_mem_vm_bind_linear(struct drm_device *, uint64_t virt,
++ uint32_t size, uint32_t flags,
++ uint64_t phys);
++extern void nv50_mem_vm_unbind(struct drm_device *, uint64_t virt,
++ uint32_t size);
++
++/* nouveau_notifier.c */
++extern int nouveau_notifier_init_channel(struct nouveau_channel *);
++extern void nouveau_notifier_takedown_channel(struct nouveau_channel *);
++extern int nouveau_notifier_alloc(struct nouveau_channel *, uint32_t handle,
++ int cout, uint32_t *offset);
++extern int nouveau_notifier_offset(struct nouveau_gpuobj *, uint32_t *);
++extern int nouveau_ioctl_notifier_alloc(struct drm_device *, void *data,
++ struct drm_file *);
++extern int nouveau_ioctl_notifier_free(struct drm_device *, void *data,
++ struct drm_file *);
++
++/* nouveau_channel.c */
++extern struct drm_ioctl_desc nouveau_ioctls[];
++extern int nouveau_max_ioctl;
++extern void nouveau_channel_cleanup(struct drm_device *, struct drm_file *);
++extern int nouveau_channel_owner(struct drm_device *, struct drm_file *,
++ int channel);
++extern int nouveau_channel_alloc(struct drm_device *dev,
++ struct nouveau_channel **chan,
++ struct drm_file *file_priv,
++ uint32_t fb_ctxdma, uint32_t tt_ctxdma);
++extern void nouveau_channel_free(struct nouveau_channel *);
++
++/* nouveau_object.c */
++extern int nouveau_gpuobj_early_init(struct drm_device *);
++extern int nouveau_gpuobj_init(struct drm_device *);
++extern void nouveau_gpuobj_takedown(struct drm_device *);
++extern void nouveau_gpuobj_late_takedown(struct drm_device *);
++extern int nouveau_gpuobj_suspend(struct drm_device *dev);
++extern void nouveau_gpuobj_suspend_cleanup(struct drm_device *dev);
++extern void nouveau_gpuobj_resume(struct drm_device *dev);
++extern int nouveau_gpuobj_channel_init(struct nouveau_channel *,
++ uint32_t vram_h, uint32_t tt_h);
++extern void nouveau_gpuobj_channel_takedown(struct nouveau_channel *);
++extern int nouveau_gpuobj_new(struct drm_device *, struct nouveau_channel *,
++ uint32_t size, int align, uint32_t flags,
++ struct nouveau_gpuobj **);
++extern int nouveau_gpuobj_del(struct drm_device *, struct nouveau_gpuobj **);
++extern int nouveau_gpuobj_ref_add(struct drm_device *, struct nouveau_channel *,
++ uint32_t handle, struct nouveau_gpuobj *,
++ struct nouveau_gpuobj_ref **);
++extern int nouveau_gpuobj_ref_del(struct drm_device *,
++ struct nouveau_gpuobj_ref **);
++extern int nouveau_gpuobj_ref_find(struct nouveau_channel *, uint32_t handle,
++ struct nouveau_gpuobj_ref **ref_ret);
++extern int nouveau_gpuobj_new_ref(struct drm_device *,
++ struct nouveau_channel *alloc_chan,
++ struct nouveau_channel *ref_chan,
++ uint32_t handle, uint32_t size, int align,
++ uint32_t flags, struct nouveau_gpuobj_ref **);
++extern int nouveau_gpuobj_new_fake(struct drm_device *,
++ uint32_t p_offset, uint32_t b_offset,
++ uint32_t size, uint32_t flags,
++ struct nouveau_gpuobj **,
++ struct nouveau_gpuobj_ref**);
++extern int nouveau_gpuobj_dma_new(struct nouveau_channel *, int class,
++ uint64_t offset, uint64_t size, int access,
++ int target, struct nouveau_gpuobj **);
++extern int nouveau_gpuobj_gart_dma_new(struct nouveau_channel *,
++ uint64_t offset, uint64_t size,
++ int access, struct nouveau_gpuobj **,
++ uint32_t *o_ret);
++extern int nouveau_gpuobj_gr_new(struct nouveau_channel *, int class,
++ struct nouveau_gpuobj **);
++extern int nouveau_gpuobj_sw_new(struct nouveau_channel *, int class,
++ struct nouveau_gpuobj **);
++extern int nouveau_ioctl_grobj_alloc(struct drm_device *, void *data,
++ struct drm_file *);
++extern int nouveau_ioctl_gpuobj_free(struct drm_device *, void *data,
++ struct drm_file *);
++
++/* nouveau_irq.c */
++extern irqreturn_t nouveau_irq_handler(DRM_IRQ_ARGS);
++extern void nouveau_irq_preinstall(struct drm_device *);
++extern int nouveau_irq_postinstall(struct drm_device *);
++extern void nouveau_irq_uninstall(struct drm_device *);
++
++/* nouveau_sgdma.c */
++extern int nouveau_sgdma_init(struct drm_device *);
++extern void nouveau_sgdma_takedown(struct drm_device *);
++extern int nouveau_sgdma_get_page(struct drm_device *, uint32_t offset,
++ uint32_t *page);
++extern struct ttm_backend *nouveau_sgdma_init_ttm(struct drm_device *);
++
++/* nouveau_debugfs.c */
++#if defined(CONFIG_DRM_NOUVEAU_DEBUG)
++extern int nouveau_debugfs_init(struct drm_minor *);
++extern void nouveau_debugfs_takedown(struct drm_minor *);
++extern int nouveau_debugfs_channel_init(struct nouveau_channel *);
++extern void nouveau_debugfs_channel_fini(struct nouveau_channel *);
++#else
++static inline int
++nouveau_debugfs_init(struct drm_minor *minor)
++{
++ return 0;
++}
++
++static inline void nouveau_debugfs_takedown(struct drm_minor *minor)
++{
++}
++
++static inline int
++nouveau_debugfs_channel_init(struct nouveau_channel *chan)
++{
++ return 0;
++}
++
++static inline void
++nouveau_debugfs_channel_fini(struct nouveau_channel *chan)
++{
++}
++#endif
++
++/* nouveau_dma.c */
++extern void nouveau_dma_pre_init(struct nouveau_channel *);
++extern int nouveau_dma_init(struct nouveau_channel *);
++extern int nouveau_dma_wait(struct nouveau_channel *, int size);
++
++/* nouveau_acpi.c */
++#ifdef CONFIG_ACPI
++extern int nouveau_hybrid_setup(struct drm_device *dev);
++extern bool nouveau_dsm_probe(struct drm_device *dev);
++#else
++static inline int nouveau_hybrid_setup(struct drm_device *dev)
++{
++ return 0;
++}
++static inline bool nouveau_dsm_probe(struct drm_device *dev)
++{
++ return false;
++}
++#endif
++
++/* nouveau_backlight.c */
++#ifdef CONFIG_DRM_NOUVEAU_BACKLIGHT
++extern int nouveau_backlight_init(struct drm_device *);
++extern void nouveau_backlight_exit(struct drm_device *);
++#else
++static inline int nouveau_backlight_init(struct drm_device *dev)
++{
++ return 0;
++}
++
++static inline void nouveau_backlight_exit(struct drm_device *dev) { }
++#endif
++
++/* nouveau_bios.c */
++extern int nouveau_bios_init(struct drm_device *);
++extern void nouveau_bios_takedown(struct drm_device *dev);
++extern int nouveau_run_vbios_init(struct drm_device *);
++extern void nouveau_bios_run_init_table(struct drm_device *, uint16_t table,
++ struct dcb_entry *);
++extern struct dcb_gpio_entry *nouveau_bios_gpio_entry(struct drm_device *,
++ enum dcb_gpio_tag);
++extern struct dcb_connector_table_entry *
++nouveau_bios_connector_entry(struct drm_device *, int index);
++extern int get_pll_limits(struct drm_device *, uint32_t limit_match,
++ struct pll_lims *);
++extern int nouveau_bios_run_display_table(struct drm_device *,
++ struct dcb_entry *,
++ uint32_t script, int pxclk);
++extern void *nouveau_bios_dp_table(struct drm_device *, struct dcb_entry *,
++ int *length);
++extern bool nouveau_bios_fp_mode(struct drm_device *, struct drm_display_mode *);
++extern uint8_t *nouveau_bios_embedded_edid(struct drm_device *);
++extern int nouveau_bios_parse_lvds_table(struct drm_device *, int pxclk,
++ bool *dl, bool *if_is_24bit);
++extern int run_tmds_table(struct drm_device *, struct dcb_entry *,
++ int head, int pxclk);
++extern int call_lvds_script(struct drm_device *, struct dcb_entry *, int head,
++ enum LVDS_script, int pxclk);
++
++/* nouveau_ttm.c */
++int nouveau_ttm_global_init(struct drm_nouveau_private *);
++void nouveau_ttm_global_release(struct drm_nouveau_private *);
++int nouveau_ttm_mmap(struct file *, struct vm_area_struct *);
++
++/* nouveau_dp.c */
++int nouveau_dp_auxch(struct nouveau_i2c_chan *auxch, int cmd, int addr,
++ uint8_t *data, int data_nr);
++bool nouveau_dp_detect(struct drm_encoder *);
++bool nouveau_dp_link_train(struct drm_encoder *);
++
++/* nv04_fb.c */
++extern int nv04_fb_init(struct drm_device *);
++extern void nv04_fb_takedown(struct drm_device *);
++
++/* nv10_fb.c */
++extern int nv10_fb_init(struct drm_device *);
++extern void nv10_fb_takedown(struct drm_device *);
++extern void nv10_fb_set_region_tiling(struct drm_device *, int, uint32_t,
++ uint32_t, uint32_t);
++
++/* nv40_fb.c */
++extern int nv40_fb_init(struct drm_device *);
++extern void nv40_fb_takedown(struct drm_device *);
++extern void nv40_fb_set_region_tiling(struct drm_device *, int, uint32_t,
++ uint32_t, uint32_t);
++
++/* nv04_fifo.c */
++extern int nv04_fifo_init(struct drm_device *);
++extern void nv04_fifo_disable(struct drm_device *);
++extern void nv04_fifo_enable(struct drm_device *);
++extern bool nv04_fifo_reassign(struct drm_device *, bool);
++extern bool nv04_fifo_cache_flush(struct drm_device *);
++extern bool nv04_fifo_cache_pull(struct drm_device *, bool);
++extern int nv04_fifo_channel_id(struct drm_device *);
++extern int nv04_fifo_create_context(struct nouveau_channel *);
++extern void nv04_fifo_destroy_context(struct nouveau_channel *);
++extern int nv04_fifo_load_context(struct nouveau_channel *);
++extern int nv04_fifo_unload_context(struct drm_device *);
++
++/* nv10_fifo.c */
++extern int nv10_fifo_init(struct drm_device *);
++extern int nv10_fifo_channel_id(struct drm_device *);
++extern int nv10_fifo_create_context(struct nouveau_channel *);
++extern void nv10_fifo_destroy_context(struct nouveau_channel *);
++extern int nv10_fifo_load_context(struct nouveau_channel *);
++extern int nv10_fifo_unload_context(struct drm_device *);
++
++/* nv40_fifo.c */
++extern int nv40_fifo_init(struct drm_device *);
++extern int nv40_fifo_create_context(struct nouveau_channel *);
++extern void nv40_fifo_destroy_context(struct nouveau_channel *);
++extern int nv40_fifo_load_context(struct nouveau_channel *);
++extern int nv40_fifo_unload_context(struct drm_device *);
++
++/* nv50_fifo.c */
++extern int nv50_fifo_init(struct drm_device *);
++extern void nv50_fifo_takedown(struct drm_device *);
++extern int nv50_fifo_channel_id(struct drm_device *);
++extern int nv50_fifo_create_context(struct nouveau_channel *);
++extern void nv50_fifo_destroy_context(struct nouveau_channel *);
++extern int nv50_fifo_load_context(struct nouveau_channel *);
++extern int nv50_fifo_unload_context(struct drm_device *);
++
++/* nv04_graph.c */
++extern struct nouveau_pgraph_object_class nv04_graph_grclass[];
++extern int nv04_graph_init(struct drm_device *);
++extern void nv04_graph_takedown(struct drm_device *);
++extern void nv04_graph_fifo_access(struct drm_device *, bool);
++extern struct nouveau_channel *nv04_graph_channel(struct drm_device *);
++extern int nv04_graph_create_context(struct nouveau_channel *);
++extern void nv04_graph_destroy_context(struct nouveau_channel *);
++extern int nv04_graph_load_context(struct nouveau_channel *);
++extern int nv04_graph_unload_context(struct drm_device *);
++extern void nv04_graph_context_switch(struct drm_device *);
++
++/* nv10_graph.c */
++extern struct nouveau_pgraph_object_class nv10_graph_grclass[];
++extern int nv10_graph_init(struct drm_device *);
++extern void nv10_graph_takedown(struct drm_device *);
++extern struct nouveau_channel *nv10_graph_channel(struct drm_device *);
++extern int nv10_graph_create_context(struct nouveau_channel *);
++extern void nv10_graph_destroy_context(struct nouveau_channel *);
++extern int nv10_graph_load_context(struct nouveau_channel *);
++extern int nv10_graph_unload_context(struct drm_device *);
++extern void nv10_graph_context_switch(struct drm_device *);
++extern void nv10_graph_set_region_tiling(struct drm_device *, int, uint32_t,
++ uint32_t, uint32_t);
++
++/* nv20_graph.c */
++extern struct nouveau_pgraph_object_class nv20_graph_grclass[];
++extern struct nouveau_pgraph_object_class nv30_graph_grclass[];
++extern int nv20_graph_create_context(struct nouveau_channel *);
++extern void nv20_graph_destroy_context(struct nouveau_channel *);
++extern int nv20_graph_load_context(struct nouveau_channel *);
++extern int nv20_graph_unload_context(struct drm_device *);
++extern int nv20_graph_init(struct drm_device *);
++extern void nv20_graph_takedown(struct drm_device *);
++extern int nv30_graph_init(struct drm_device *);
++extern void nv20_graph_set_region_tiling(struct drm_device *, int, uint32_t,
++ uint32_t, uint32_t);
++
++/* nv40_graph.c */
++extern struct nouveau_pgraph_object_class nv40_graph_grclass[];
++extern int nv40_graph_init(struct drm_device *);
++extern void nv40_graph_takedown(struct drm_device *);
++extern struct nouveau_channel *nv40_graph_channel(struct drm_device *);
++extern int nv40_graph_create_context(struct nouveau_channel *);
++extern void nv40_graph_destroy_context(struct nouveau_channel *);
++extern int nv40_graph_load_context(struct nouveau_channel *);
++extern int nv40_graph_unload_context(struct drm_device *);
++extern void nv40_grctx_init(struct nouveau_grctx *);
++extern void nv40_graph_set_region_tiling(struct drm_device *, int, uint32_t,
++ uint32_t, uint32_t);
++
++/* nv50_graph.c */
++extern struct nouveau_pgraph_object_class nv50_graph_grclass[];
++extern int nv50_graph_init(struct drm_device *);
++extern void nv50_graph_takedown(struct drm_device *);
++extern void nv50_graph_fifo_access(struct drm_device *, bool);
++extern struct nouveau_channel *nv50_graph_channel(struct drm_device *);
++extern int nv50_graph_create_context(struct nouveau_channel *);
++extern void nv50_graph_destroy_context(struct nouveau_channel *);
++extern int nv50_graph_load_context(struct nouveau_channel *);
++extern int nv50_graph_unload_context(struct drm_device *);
++extern void nv50_graph_context_switch(struct drm_device *);
++
++/* nouveau_grctx.c */
++extern int nouveau_grctx_prog_load(struct drm_device *);
++extern void nouveau_grctx_vals_load(struct drm_device *,
++ struct nouveau_gpuobj *);
++extern void nouveau_grctx_fini(struct drm_device *);
++
++/* nv04_instmem.c */
++extern int nv04_instmem_init(struct drm_device *);
++extern void nv04_instmem_takedown(struct drm_device *);
++extern int nv04_instmem_suspend(struct drm_device *);
++extern void nv04_instmem_resume(struct drm_device *);
++extern int nv04_instmem_populate(struct drm_device *, struct nouveau_gpuobj *,
++ uint32_t *size);
++extern void nv04_instmem_clear(struct drm_device *, struct nouveau_gpuobj *);
++extern int nv04_instmem_bind(struct drm_device *, struct nouveau_gpuobj *);
++extern int nv04_instmem_unbind(struct drm_device *, struct nouveau_gpuobj *);
++extern void nv04_instmem_prepare_access(struct drm_device *, bool write);
++extern void nv04_instmem_finish_access(struct drm_device *);
++
++/* nv50_instmem.c */
++extern int nv50_instmem_init(struct drm_device *);
++extern void nv50_instmem_takedown(struct drm_device *);
++extern int nv50_instmem_suspend(struct drm_device *);
++extern void nv50_instmem_resume(struct drm_device *);
++extern int nv50_instmem_populate(struct drm_device *, struct nouveau_gpuobj *,
++ uint32_t *size);
++extern void nv50_instmem_clear(struct drm_device *, struct nouveau_gpuobj *);
++extern int nv50_instmem_bind(struct drm_device *, struct nouveau_gpuobj *);
++extern int nv50_instmem_unbind(struct drm_device *, struct nouveau_gpuobj *);
++extern void nv50_instmem_prepare_access(struct drm_device *, bool write);
++extern void nv50_instmem_finish_access(struct drm_device *);
++
++/* nv04_mc.c */
++extern int nv04_mc_init(struct drm_device *);
++extern void nv04_mc_takedown(struct drm_device *);
++
++/* nv40_mc.c */
++extern int nv40_mc_init(struct drm_device *);
++extern void nv40_mc_takedown(struct drm_device *);
++
++/* nv50_mc.c */
++extern int nv50_mc_init(struct drm_device *);
++extern void nv50_mc_takedown(struct drm_device *);
++
++/* nv04_timer.c */
++extern int nv04_timer_init(struct drm_device *);
++extern uint64_t nv04_timer_read(struct drm_device *);
++extern void nv04_timer_takedown(struct drm_device *);
++
++extern long nouveau_compat_ioctl(struct file *file, unsigned int cmd,
++ unsigned long arg);
++
++/* nv04_dac.c */
++extern int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry);
++extern uint32_t nv17_dac_sample_load(struct drm_encoder *encoder);
++extern int nv04_dac_output_offset(struct drm_encoder *encoder);
++extern void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable);
++
++/* nv04_dfp.c */
++extern int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry);
++extern int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_entry *dcbent);
++extern void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_entry *dcbent,
++ int head, bool dl);
++extern void nv04_dfp_disable(struct drm_device *dev, int head);
++extern void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode);
++
++/* nv04_tv.c */
++extern int nv04_tv_identify(struct drm_device *dev, int i2c_index);
++extern int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry);
++
++/* nv17_tv.c */
++extern int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry);
++
++/* nv04_display.c */
++extern int nv04_display_create(struct drm_device *);
++extern void nv04_display_destroy(struct drm_device *);
++extern void nv04_display_restore(struct drm_device *);
++
++/* nv04_crtc.c */
++extern int nv04_crtc_create(struct drm_device *, int index);
++
++/* nouveau_bo.c */
++extern struct ttm_bo_driver nouveau_bo_driver;
++extern int nouveau_bo_new(struct drm_device *, struct nouveau_channel *,
++ int size, int align, uint32_t flags,
++ uint32_t tile_mode, uint32_t tile_flags,
++ bool no_vm, bool mappable, struct nouveau_bo **);
++extern int nouveau_bo_pin(struct nouveau_bo *, uint32_t flags);
++extern int nouveau_bo_unpin(struct nouveau_bo *);
++extern int nouveau_bo_map(struct nouveau_bo *);
++extern void nouveau_bo_unmap(struct nouveau_bo *);
++extern void nouveau_bo_placement_set(struct nouveau_bo *, uint32_t memtype);
++extern u16 nouveau_bo_rd16(struct nouveau_bo *nvbo, unsigned index);
++extern void nouveau_bo_wr16(struct nouveau_bo *nvbo, unsigned index, u16 val);
++extern u32 nouveau_bo_rd32(struct nouveau_bo *nvbo, unsigned index);
++extern void nouveau_bo_wr32(struct nouveau_bo *nvbo, unsigned index, u32 val);
++
++/* nouveau_fence.c */
++struct nouveau_fence;
++extern int nouveau_fence_init(struct nouveau_channel *);
++extern void nouveau_fence_fini(struct nouveau_channel *);
++extern void nouveau_fence_update(struct nouveau_channel *);
++extern int nouveau_fence_new(struct nouveau_channel *, struct nouveau_fence **,
++ bool emit);
++extern int nouveau_fence_emit(struct nouveau_fence *);
++struct nouveau_channel *nouveau_fence_channel(struct nouveau_fence *);
++extern bool nouveau_fence_signalled(void *obj, void *arg);
++extern int nouveau_fence_wait(void *obj, void *arg, bool lazy, bool intr);
++extern int nouveau_fence_flush(void *obj, void *arg);
++extern void nouveau_fence_unref(void **obj);
++extern void *nouveau_fence_ref(void *obj);
++extern void nouveau_fence_handler(struct drm_device *dev, int channel);
++
++/* nouveau_gem.c */
++extern int nouveau_gem_new(struct drm_device *, struct nouveau_channel *,
++ int size, int align, uint32_t flags,
++ uint32_t tile_mode, uint32_t tile_flags,
++ bool no_vm, bool mappable, struct nouveau_bo **);
++extern int nouveau_gem_object_new(struct drm_gem_object *);
++extern void nouveau_gem_object_del(struct drm_gem_object *);
++extern int nouveau_gem_ioctl_new(struct drm_device *, void *,
++ struct drm_file *);
++extern int nouveau_gem_ioctl_pushbuf(struct drm_device *, void *,
++ struct drm_file *);
++extern int nouveau_gem_ioctl_pushbuf_call(struct drm_device *, void *,
++ struct drm_file *);
++extern int nouveau_gem_ioctl_pushbuf_call2(struct drm_device *, void *,
++ struct drm_file *);
++extern int nouveau_gem_ioctl_pin(struct drm_device *, void *,
++ struct drm_file *);
++extern int nouveau_gem_ioctl_unpin(struct drm_device *, void *,
++ struct drm_file *);
++extern int nouveau_gem_ioctl_tile(struct drm_device *, void *,
++ struct drm_file *);
++extern int nouveau_gem_ioctl_cpu_prep(struct drm_device *, void *,
++ struct drm_file *);
++extern int nouveau_gem_ioctl_cpu_fini(struct drm_device *, void *,
++ struct drm_file *);
++extern int nouveau_gem_ioctl_info(struct drm_device *, void *,
++ struct drm_file *);
++
++/* nv17_gpio.c */
++int nv17_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag);
++int nv17_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state);
++
++#ifndef ioread32_native
++#ifdef __BIG_ENDIAN
++#define ioread16_native ioread16be
++#define iowrite16_native iowrite16be
++#define ioread32_native ioread32be
++#define iowrite32_native iowrite32be
++#else /* def __BIG_ENDIAN */
++#define ioread16_native ioread16
++#define iowrite16_native iowrite16
++#define ioread32_native ioread32
++#define iowrite32_native iowrite32
++#endif /* def __BIG_ENDIAN else */
++#endif /* !ioread32_native */
++
++/* channel control reg access */
++static inline u32 nvchan_rd32(struct nouveau_channel *chan, unsigned reg)
++{
++ return ioread32_native(chan->user + reg);
++}
++
++static inline void nvchan_wr32(struct nouveau_channel *chan,
++ unsigned reg, u32 val)
++{
++ iowrite32_native(val, chan->user + reg);
++}
++
++/* register access */
++static inline u32 nv_rd32(struct drm_device *dev, unsigned reg)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ return ioread32_native(dev_priv->mmio + reg);
++}
++
++static inline void nv_wr32(struct drm_device *dev, unsigned reg, u32 val)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ iowrite32_native(val, dev_priv->mmio + reg);
++}
++
++static inline u8 nv_rd08(struct drm_device *dev, unsigned reg)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ return ioread8(dev_priv->mmio + reg);
++}
++
++static inline void nv_wr08(struct drm_device *dev, unsigned reg, u8 val)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ iowrite8(val, dev_priv->mmio + reg);
++}
++
++#define nv_wait(reg, mask, val) \
++ nouveau_wait_until(dev, 2000000000ULL, (reg), (mask), (val))
++
++/* PRAMIN access */
++static inline u32 nv_ri32(struct drm_device *dev, unsigned offset)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ return ioread32_native(dev_priv->ramin + offset);
++}
++
++static inline void nv_wi32(struct drm_device *dev, unsigned offset, u32 val)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ iowrite32_native(val, dev_priv->ramin + offset);
++}
++
++/* object access */
++static inline u32 nv_ro32(struct drm_device *dev, struct nouveau_gpuobj *obj,
++ unsigned index)
++{
++ return nv_ri32(dev, obj->im_pramin->start + index * 4);
++}
++
++static inline void nv_wo32(struct drm_device *dev, struct nouveau_gpuobj *obj,
++ unsigned index, u32 val)
++{
++ nv_wi32(dev, obj->im_pramin->start + index * 4, val);
++}
++
++/*
++ * Logging
++ * Argument d is (struct drm_device *).
++ */
++#define NV_PRINTK(level, d, fmt, arg...) \
++ printk(level "[" DRM_NAME "] " DRIVER_NAME " %s: " fmt, \
++ pci_name(d->pdev), ##arg)
++#ifndef NV_DEBUG_NOTRACE
++#define NV_DEBUG(d, fmt, arg...) do { \
++ if (drm_debug & DRM_UT_DRIVER) { \
++ NV_PRINTK(KERN_DEBUG, d, "%s:%d - " fmt, __func__, \
++ __LINE__, ##arg); \
++ } \
++} while (0)
++#define NV_DEBUG_KMS(d, fmt, arg...) do { \
++ if (drm_debug & DRM_UT_KMS) { \
++ NV_PRINTK(KERN_DEBUG, d, "%s:%d - " fmt, __func__, \
++ __LINE__, ##arg); \
++ } \
++} while (0)
++#else
++#define NV_DEBUG(d, fmt, arg...) do { \
++ if (drm_debug & DRM_UT_DRIVER) \
++ NV_PRINTK(KERN_DEBUG, d, fmt, ##arg); \
++} while (0)
++#define NV_DEBUG_KMS(d, fmt, arg...) do { \
++ if (drm_debug & DRM_UT_KMS) \
++ NV_PRINTK(KERN_DEBUG, d, fmt, ##arg); \
++} while (0)
++#endif
++#define NV_ERROR(d, fmt, arg...) NV_PRINTK(KERN_ERR, d, fmt, ##arg)
++#define NV_INFO(d, fmt, arg...) NV_PRINTK(KERN_INFO, d, fmt, ##arg)
++#define NV_TRACEWARN(d, fmt, arg...) NV_PRINTK(KERN_NOTICE, d, fmt, ##arg)
++#define NV_TRACE(d, fmt, arg...) NV_PRINTK(KERN_INFO, d, fmt, ##arg)
++#define NV_WARN(d, fmt, arg...) NV_PRINTK(KERN_WARNING, d, fmt, ##arg)
++
++/* nouveau_reg_debug bitmask */
++enum {
++ NOUVEAU_REG_DEBUG_MC = 0x1,
++ NOUVEAU_REG_DEBUG_VIDEO = 0x2,
++ NOUVEAU_REG_DEBUG_FB = 0x4,
++ NOUVEAU_REG_DEBUG_EXTDEV = 0x8,
++ NOUVEAU_REG_DEBUG_CRTC = 0x10,
++ NOUVEAU_REG_DEBUG_RAMDAC = 0x20,
++ NOUVEAU_REG_DEBUG_VGACRTC = 0x40,
++ NOUVEAU_REG_DEBUG_RMVIO = 0x80,
++ NOUVEAU_REG_DEBUG_VGAATTR = 0x100,
++ NOUVEAU_REG_DEBUG_EVO = 0x200,
++};
++
++#define NV_REG_DEBUG(type, dev, fmt, arg...) do { \
++ if (nouveau_reg_debug & NOUVEAU_REG_DEBUG_##type) \
++ NV_PRINTK(KERN_DEBUG, dev, "%s: " fmt, __func__, ##arg); \
++} while (0)
++
++static inline bool
++nv_two_heads(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ const int impl = dev->pci_device & 0x0ff0;
++
++ if (dev_priv->card_type >= NV_10 && impl != 0x0100 &&
++ impl != 0x0150 && impl != 0x01a0 && impl != 0x0200)
++ return true;
++
++ return false;
++}
++
++static inline bool
++nv_gf4_disp_arch(struct drm_device *dev)
++{
++ return nv_two_heads(dev) && (dev->pci_device & 0x0ff0) != 0x0110;
++}
++
++static inline bool
++nv_two_reg_pll(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ const int impl = dev->pci_device & 0x0ff0;
++
++ if (impl == 0x0310 || impl == 0x0340 || dev_priv->card_type >= NV_40)
++ return true;
++ return false;
++}
++
++#define NV_SW 0x0000506e
++#define NV_SW_DMA_SEMAPHORE 0x00000060
++#define NV_SW_SEMAPHORE_OFFSET 0x00000064
++#define NV_SW_SEMAPHORE_ACQUIRE 0x00000068
++#define NV_SW_SEMAPHORE_RELEASE 0x0000006c
++#define NV_SW_DMA_VBLSEM 0x0000018c
++#define NV_SW_VBLSEM_OFFSET 0x00000400
++#define NV_SW_VBLSEM_RELEASE_VALUE 0x00000404
++#define NV_SW_VBLSEM_RELEASE 0x00000408
++
++#endif /* __NOUVEAU_DRV_H__ */
+diff --git a/drivers/gpu/drm/nouveau/nouveau_encoder.h b/drivers/gpu/drm/nouveau/nouveau_encoder.h
+new file mode 100644
+index 0000000..bc4a240
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_encoder.h
+@@ -0,0 +1,91 @@
++/*
++ * Copyright (C) 2008 Maarten Maathuis.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#ifndef __NOUVEAU_ENCODER_H__
++#define __NOUVEAU_ENCODER_H__
++
++#include "drm_encoder_slave.h"
++#include "nouveau_drv.h"
++
++#define NV_DPMS_CLEARED 0x80
++
++struct nouveau_encoder {
++ struct drm_encoder_slave base;
++
++ struct dcb_entry *dcb;
++ int or;
++
++ struct drm_display_mode mode;
++ int last_dpms;
++
++ struct nv04_output_reg restore;
++
++ void (*disconnect)(struct nouveau_encoder *encoder);
++
++ union {
++ struct {
++ int dpcd_version;
++ int link_nr;
++ int link_bw;
++ } dp;
++ };
++};
++
++static inline struct nouveau_encoder *nouveau_encoder(struct drm_encoder *enc)
++{
++ struct drm_encoder_slave *slave = to_encoder_slave(enc);
++
++ return container_of(slave, struct nouveau_encoder, base);
++}
++
++static inline struct drm_encoder *to_drm_encoder(struct nouveau_encoder *enc)
++{
++ return &enc->base.base;
++}
++
++struct nouveau_connector *
++nouveau_encoder_connector_get(struct nouveau_encoder *encoder);
++int nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry);
++int nv50_dac_create(struct drm_device *dev, struct dcb_entry *entry);
++
++struct bit_displayport_encoder_table {
++ uint32_t match;
++ uint8_t record_nr;
++ uint8_t unknown;
++ uint16_t script0;
++ uint16_t script1;
++ uint16_t unknown_table;
++} __attribute__ ((packed));
++
++struct bit_displayport_encoder_table_entry {
++ uint8_t vs_level;
++ uint8_t pre_level;
++ uint8_t reg0;
++ uint8_t reg1;
++ uint8_t reg2;
++} __attribute__ ((packed));
++
++#endif /* __NOUVEAU_ENCODER_H__ */
+diff --git a/drivers/gpu/drm/nouveau/nouveau_fb.h b/drivers/gpu/drm/nouveau/nouveau_fb.h
+new file mode 100644
+index 0000000..4a3f31a
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_fb.h
+@@ -0,0 +1,47 @@
++/*
++ * Copyright (C) 2008 Maarten Maathuis.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#ifndef __NOUVEAU_FB_H__
++#define __NOUVEAU_FB_H__
++
++struct nouveau_framebuffer {
++ struct drm_framebuffer base;
++ struct nouveau_bo *nvbo;
++};
++
++static inline struct nouveau_framebuffer *
++nouveau_framebuffer(struct drm_framebuffer *fb)
++{
++ return container_of(fb, struct nouveau_framebuffer, base);
++}
++
++extern const struct drm_mode_config_funcs nouveau_mode_config_funcs;
++
++struct drm_framebuffer *
++nouveau_framebuffer_create(struct drm_device *, struct nouveau_bo *,
++ struct drm_mode_fb_cmd *);
++
++#endif /* __NOUVEAU_FB_H__ */
+diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.c b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+new file mode 100644
+index 0000000..ea879a2
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.c
+@@ -0,0 +1,423 @@
++/*
++ * Copyright © 2007 David Airlie
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ *
++ * Authors:
++ * David Airlie
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/string.h>
++#include <linux/mm.h>
++#include <linux/tty.h>
++#include <linux/slab.h>
++#include <linux/sysrq.h>
++#include <linux/delay.h>
++#include <linux/fb.h>
++#include <linux/init.h>
++#include <linux/screen_info.h>
++
++#include "drmP.h"
++#include "drm.h"
++#include "drm_crtc.h"
++#include "drm_crtc_helper.h"
++#include "drm_fb_helper.h"
++#include "nouveau_drv.h"
++#include "nouveau_drm.h"
++#include "nouveau_crtc.h"
++#include "nouveau_fb.h"
++#include "nouveau_fbcon.h"
++#include "nouveau_dma.h"
++
++static int
++nouveau_fbcon_sync(struct fb_info *info)
++{
++ struct nouveau_fbcon_par *par = info->par;
++ struct drm_device *dev = par->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *chan = dev_priv->channel;
++ int ret, i;
++
++ if (!chan || !chan->accel_done ||
++ info->state != FBINFO_STATE_RUNNING ||
++ info->flags & FBINFO_HWACCEL_DISABLED)
++ return 0;
++
++ if (RING_SPACE(chan, 4)) {
++ nouveau_fbcon_gpu_lockup(info);
++ return 0;
++ }
++
++ BEGIN_RING(chan, 0, 0x0104, 1);
++ OUT_RING(chan, 0);
++ BEGIN_RING(chan, 0, 0x0100, 1);
++ OUT_RING(chan, 0);
++ nouveau_bo_wr32(chan->notifier_bo, chan->m2mf_ntfy + 3, 0xffffffff);
++ FIRE_RING(chan);
++
++ ret = -EBUSY;
++ for (i = 0; i < 100000; i++) {
++ if (!nouveau_bo_rd32(chan->notifier_bo, chan->m2mf_ntfy + 3)) {
++ ret = 0;
++ break;
++ }
++ DRM_UDELAY(1);
++ }
++
++ if (ret) {
++ nouveau_fbcon_gpu_lockup(info);
++ return 0;
++ }
++
++ chan->accel_done = false;
++ return 0;
++}
++
++static struct fb_ops nouveau_fbcon_ops = {
++ .owner = THIS_MODULE,
++ .fb_check_var = drm_fb_helper_check_var,
++ .fb_set_par = drm_fb_helper_set_par,
++ .fb_setcolreg = drm_fb_helper_setcolreg,
++ .fb_fillrect = cfb_fillrect,
++ .fb_copyarea = cfb_copyarea,
++ .fb_imageblit = cfb_imageblit,
++ .fb_sync = nouveau_fbcon_sync,
++ .fb_pan_display = drm_fb_helper_pan_display,
++ .fb_blank = drm_fb_helper_blank,
++ .fb_setcmap = drm_fb_helper_setcmap,
++};
++
++static struct fb_ops nv04_fbcon_ops = {
++ .owner = THIS_MODULE,
++ .fb_check_var = drm_fb_helper_check_var,
++ .fb_set_par = drm_fb_helper_set_par,
++ .fb_setcolreg = drm_fb_helper_setcolreg,
++ .fb_fillrect = nv04_fbcon_fillrect,
++ .fb_copyarea = nv04_fbcon_copyarea,
++ .fb_imageblit = nv04_fbcon_imageblit,
++ .fb_sync = nouveau_fbcon_sync,
++ .fb_pan_display = drm_fb_helper_pan_display,
++ .fb_blank = drm_fb_helper_blank,
++ .fb_setcmap = drm_fb_helper_setcmap,
++};
++
++static struct fb_ops nv50_fbcon_ops = {
++ .owner = THIS_MODULE,
++ .fb_check_var = drm_fb_helper_check_var,
++ .fb_set_par = drm_fb_helper_set_par,
++ .fb_setcolreg = drm_fb_helper_setcolreg,
++ .fb_fillrect = nv50_fbcon_fillrect,
++ .fb_copyarea = nv50_fbcon_copyarea,
++ .fb_imageblit = nv50_fbcon_imageblit,
++ .fb_sync = nouveau_fbcon_sync,
++ .fb_pan_display = drm_fb_helper_pan_display,
++ .fb_blank = drm_fb_helper_blank,
++ .fb_setcmap = drm_fb_helper_setcmap,
++};
++
++static void nouveau_fbcon_gamma_set(struct drm_crtc *crtc, u16 red, u16 green,
++ u16 blue, int regno)
++{
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++
++ nv_crtc->lut.r[regno] = red;
++ nv_crtc->lut.g[regno] = green;
++ nv_crtc->lut.b[regno] = blue;
++}
++
++static void nouveau_fbcon_gamma_get(struct drm_crtc *crtc, u16 *red, u16 *green,
++ u16 *blue, int regno)
++{
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++
++ *red = nv_crtc->lut.r[regno];
++ *green = nv_crtc->lut.g[regno];
++ *blue = nv_crtc->lut.b[regno];
++}
++
++static struct drm_fb_helper_funcs nouveau_fbcon_helper_funcs = {
++ .gamma_set = nouveau_fbcon_gamma_set,
++ .gamma_get = nouveau_fbcon_gamma_get
++};
++
++#if defined(__i386__) || defined(__x86_64__)
++static bool
++nouveau_fbcon_has_vesafb_or_efifb(struct drm_device *dev)
++{
++ struct pci_dev *pdev = dev->pdev;
++ int ramin;
++
++ if (screen_info.orig_video_isVGA != VIDEO_TYPE_VLFB &&
++ screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
++ return false;
++
++ if (screen_info.lfb_base < pci_resource_start(pdev, 1))
++ goto not_fb;
++
++ if (screen_info.lfb_base + screen_info.lfb_size >=
++ pci_resource_start(pdev, 1) + pci_resource_len(pdev, 1))
++ goto not_fb;
++
++ return true;
++not_fb:
++ ramin = 2;
++ if (pci_resource_len(pdev, ramin) == 0) {
++ ramin = 3;
++ if (pci_resource_len(pdev, ramin) == 0)
++ return false;
++ }
++
++ if (screen_info.lfb_base < pci_resource_start(pdev, ramin))
++ return false;
++
++ if (screen_info.lfb_base + screen_info.lfb_size >=
++ pci_resource_start(pdev, ramin) + pci_resource_len(pdev, ramin))
++ return false;
++
++ return true;
++}
++#endif
++
++void
++nouveau_fbcon_zfill(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct fb_info *info = dev_priv->fbdev_info;
++ struct fb_fillrect rect;
++
++ /* Clear the entire fbcon. The drm will program every connector
++ * with it's preferred mode. If the sizes differ, one display will
++ * quite likely have garbage around the console.
++ */
++ rect.dx = rect.dy = 0;
++ rect.width = info->var.xres_virtual;
++ rect.height = info->var.yres_virtual;
++ rect.color = 0;
++ rect.rop = ROP_COPY;
++ info->fbops->fb_fillrect(info, &rect);
++}
++
++static int
++nouveau_fbcon_create(struct drm_device *dev, uint32_t fb_width,
++ uint32_t fb_height, uint32_t surface_width,
++ uint32_t surface_height, uint32_t surface_depth,
++ uint32_t surface_bpp, struct drm_framebuffer **pfb)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct fb_info *info;
++ struct nouveau_fbcon_par *par;
++ struct drm_framebuffer *fb;
++ struct nouveau_framebuffer *nouveau_fb;
++ struct nouveau_bo *nvbo;
++ struct drm_mode_fb_cmd mode_cmd;
++ struct device *device = &dev->pdev->dev;
++ int size, ret;
++
++ mode_cmd.width = surface_width;
++ mode_cmd.height = surface_height;
++
++ mode_cmd.bpp = surface_bpp;
++ mode_cmd.pitch = mode_cmd.width * (mode_cmd.bpp >> 3);
++ mode_cmd.pitch = roundup(mode_cmd.pitch, 256);
++ mode_cmd.depth = surface_depth;
++
++ size = mode_cmd.pitch * mode_cmd.height;
++ size = roundup(size, PAGE_SIZE);
++
++ ret = nouveau_gem_new(dev, dev_priv->channel, size, 0, TTM_PL_FLAG_VRAM,
++ 0, 0x0000, false, true, &nvbo);
++ if (ret) {
++ NV_ERROR(dev, "failed to allocate framebuffer\n");
++ goto out;
++ }
++
++ ret = nouveau_bo_pin(nvbo, TTM_PL_FLAG_VRAM);
++ if (ret) {
++ NV_ERROR(dev, "failed to pin fb: %d\n", ret);
++ nouveau_bo_ref(NULL, &nvbo);
++ goto out;
++ }
++
++ ret = nouveau_bo_map(nvbo);
++ if (ret) {
++ NV_ERROR(dev, "failed to map fb: %d\n", ret);
++ nouveau_bo_unpin(nvbo);
++ nouveau_bo_ref(NULL, &nvbo);
++ goto out;
++ }
++
++ mutex_lock(&dev->struct_mutex);
++
++ fb = nouveau_framebuffer_create(dev, nvbo, &mode_cmd);
++ if (!fb) {
++ ret = -ENOMEM;
++ NV_ERROR(dev, "failed to allocate fb.\n");
++ goto out_unref;
++ }
++
++ list_add(&fb->filp_head, &dev->mode_config.fb_kernel_list);
++
++ nouveau_fb = nouveau_framebuffer(fb);
++ *pfb = fb;
++
++ info = framebuffer_alloc(sizeof(struct nouveau_fbcon_par), device);
++ if (!info) {
++ ret = -ENOMEM;
++ goto out_unref;
++ }
++
++ par = info->par;
++ par->helper.funcs = &nouveau_fbcon_helper_funcs;
++ par->helper.dev = dev;
++ ret = drm_fb_helper_init_crtc_count(&par->helper, 2, 4);
++ if (ret)
++ goto out_unref;
++ dev_priv->fbdev_info = info;
++
++ strcpy(info->fix.id, "nouveaufb");
++ if (nouveau_nofbaccel)
++ info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_DISABLED;
++ else
++ info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA |
++ FBINFO_HWACCEL_FILLRECT |
++ FBINFO_HWACCEL_IMAGEBLIT;
++ info->fbops = &nouveau_fbcon_ops;
++ info->fix.smem_start = dev->mode_config.fb_base + nvbo->bo.offset -
++ dev_priv->vm_vram_base;
++ info->fix.smem_len = size;
++
++ info->screen_base = nvbo_kmap_obj_iovirtual(nouveau_fb->nvbo);
++ info->screen_size = size;
++
++ drm_fb_helper_fill_fix(info, fb->pitch, fb->depth);
++ drm_fb_helper_fill_var(info, fb, fb_width, fb_height);
++
++ /* FIXME: we really shouldn't expose mmio space at all */
++ info->fix.mmio_start = pci_resource_start(dev->pdev, 1);
++ info->fix.mmio_len = pci_resource_len(dev->pdev, 1);
++
++ /* Set aperture base/size for vesafb takeover */
++#if defined(__i386__) || defined(__x86_64__)
++ if (nouveau_fbcon_has_vesafb_or_efifb(dev)) {
++ /* Some NVIDIA VBIOS' are stupid and decide to put the
++ * framebuffer in the middle of the PRAMIN BAR for
++ * whatever reason. We need to know the exact lfb_base
++ * to get vesafb kicked off, and the only reliable way
++ * we have left is to find out lfb_base the same way
++ * vesafb did.
++ */
++ info->aperture_base = screen_info.lfb_base;
++ info->aperture_size = screen_info.lfb_size;
++ if (screen_info.orig_video_isVGA == VIDEO_TYPE_VLFB)
++ info->aperture_size *= 65536;
++ } else
++#endif
++ {
++ info->aperture_base = info->fix.mmio_start;
++ info->aperture_size = info->fix.mmio_len;
++ }
++
++ info->pixmap.size = 64*1024;
++ info->pixmap.buf_align = 8;
++ info->pixmap.access_align = 32;
++ info->pixmap.flags = FB_PIXMAP_SYSTEM;
++ info->pixmap.scan_align = 1;
++
++ fb->fbdev = info;
++
++ par->nouveau_fb = nouveau_fb;
++ par->dev = dev;
++
++ if (dev_priv->channel && !nouveau_nofbaccel) {
++ switch (dev_priv->card_type) {
++ case NV_50:
++ nv50_fbcon_accel_init(info);
++ info->fbops = &nv50_fbcon_ops;
++ break;
++ default:
++ nv04_fbcon_accel_init(info);
++ info->fbops = &nv04_fbcon_ops;
++ break;
++ };
++ }
++
++ nouveau_fbcon_zfill(dev);
++
++ /* To allow resizeing without swapping buffers */
++ NV_INFO(dev, "allocated %dx%d fb: 0x%lx, bo %p\n",
++ nouveau_fb->base.width,
++ nouveau_fb->base.height,
++ nvbo->bo.offset, nvbo);
++
++ mutex_unlock(&dev->struct_mutex);
++ return 0;
++
++out_unref:
++ mutex_unlock(&dev->struct_mutex);
++out:
++ return ret;
++}
++
++int
++nouveau_fbcon_probe(struct drm_device *dev)
++{
++ NV_DEBUG_KMS(dev, "\n");
++
++ return drm_fb_helper_single_fb_probe(dev, 32, nouveau_fbcon_create);
++}
++
++int
++nouveau_fbcon_remove(struct drm_device *dev, struct drm_framebuffer *fb)
++{
++ struct nouveau_framebuffer *nouveau_fb = nouveau_framebuffer(fb);
++ struct fb_info *info;
++
++ if (!fb)
++ return -EINVAL;
++
++ info = fb->fbdev;
++ if (info) {
++ struct nouveau_fbcon_par *par = info->par;
++
++ unregister_framebuffer(info);
++ nouveau_bo_unmap(nouveau_fb->nvbo);
++ mutex_lock(&dev->struct_mutex);
++ drm_gem_object_unreference(nouveau_fb->nvbo->gem);
++ nouveau_fb->nvbo = NULL;
++ mutex_unlock(&dev->struct_mutex);
++ if (par)
++ drm_fb_helper_free(&par->helper);
++ framebuffer_release(info);
++ }
++
++ return 0;
++}
++
++void nouveau_fbcon_gpu_lockup(struct fb_info *info)
++{
++ struct nouveau_fbcon_par *par = info->par;
++ struct drm_device *dev = par->dev;
++
++ NV_ERROR(dev, "GPU lockup - switching to software fbcon\n");
++ info->flags |= FBINFO_HWACCEL_DISABLED;
++}
+diff --git a/drivers/gpu/drm/nouveau/nouveau_fbcon.h b/drivers/gpu/drm/nouveau/nouveau_fbcon.h
+new file mode 100644
+index 0000000..f9c34e1
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_fbcon.h
+@@ -0,0 +1,54 @@
++/*
++ * Copyright (C) 2008 Maarten Maathuis.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#ifndef __NOUVEAU_FBCON_H__
++#define __NOUVEAU_FBCON_H__
++
++#include "drm_fb_helper.h"
++
++struct nouveau_fbcon_par {
++ struct drm_fb_helper helper;
++ struct drm_device *dev;
++ struct nouveau_framebuffer *nouveau_fb;
++};
++
++int nouveau_fbcon_probe(struct drm_device *dev);
++int nouveau_fbcon_remove(struct drm_device *dev, struct drm_framebuffer *fb);
++void nouveau_fbcon_restore(void);
++void nouveau_fbcon_zfill(struct drm_device *dev);
++
++void nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region);
++void nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
++void nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image);
++int nv04_fbcon_accel_init(struct fb_info *info);
++void nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
++void nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region);
++void nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image);
++int nv50_fbcon_accel_init(struct fb_info *info);
++
++void nouveau_fbcon_gpu_lockup(struct fb_info *info);
++#endif /* __NV50_FBCON_H__ */
++
+diff --git a/drivers/gpu/drm/nouveau/nouveau_fence.c b/drivers/gpu/drm/nouveau/nouveau_fence.c
+new file mode 100644
+index 0000000..faddf53
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_fence.c
+@@ -0,0 +1,262 @@
++/*
++ * Copyright (C) 2007 Ben Skeggs.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "drm.h"
++
++#include "nouveau_drv.h"
++#include "nouveau_dma.h"
++
++#define USE_REFCNT (dev_priv->card_type >= NV_10)
++
++struct nouveau_fence {
++ struct nouveau_channel *channel;
++ struct kref refcount;
++ struct list_head entry;
++
++ uint32_t sequence;
++ bool signalled;
++};
++
++static inline struct nouveau_fence *
++nouveau_fence(void *sync_obj)
++{
++ return (struct nouveau_fence *)sync_obj;
++}
++
++static void
++nouveau_fence_del(struct kref *ref)
++{
++ struct nouveau_fence *fence =
++ container_of(ref, struct nouveau_fence, refcount);
++
++ kfree(fence);
++}
++
++void
++nouveau_fence_update(struct nouveau_channel *chan)
++{
++ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
++ struct list_head *entry, *tmp;
++ struct nouveau_fence *fence;
++ uint32_t sequence;
++
++ if (USE_REFCNT)
++ sequence = nvchan_rd32(chan, 0x48);
++ else
++ sequence = chan->fence.last_sequence_irq;
++
++ if (chan->fence.sequence_ack == sequence)
++ return;
++ chan->fence.sequence_ack = sequence;
++
++ list_for_each_safe(entry, tmp, &chan->fence.pending) {
++ fence = list_entry(entry, struct nouveau_fence, entry);
++
++ sequence = fence->sequence;
++ fence->signalled = true;
++ list_del(&fence->entry);
++ kref_put(&fence->refcount, nouveau_fence_del);
++
++ if (sequence == chan->fence.sequence_ack)
++ break;
++ }
++}
++
++int
++nouveau_fence_new(struct nouveau_channel *chan, struct nouveau_fence **pfence,
++ bool emit)
++{
++ struct nouveau_fence *fence;
++ int ret = 0;
++
++ fence = kzalloc(sizeof(*fence), GFP_KERNEL);
++ if (!fence)
++ return -ENOMEM;
++ kref_init(&fence->refcount);
++ fence->channel = chan;
++
++ if (emit)
++ ret = nouveau_fence_emit(fence);
++
++ if (ret)
++ nouveau_fence_unref((void *)&fence);
++ *pfence = fence;
++ return ret;
++}
++
++struct nouveau_channel *
++nouveau_fence_channel(struct nouveau_fence *fence)
++{
++ return fence ? fence->channel : NULL;
++}
++
++int
++nouveau_fence_emit(struct nouveau_fence *fence)
++{
++ struct drm_nouveau_private *dev_priv = fence->channel->dev->dev_private;
++ struct nouveau_channel *chan = fence->channel;
++ unsigned long flags;
++ int ret;
++
++ ret = RING_SPACE(chan, 2);
++ if (ret)
++ return ret;
++
++ if (unlikely(chan->fence.sequence == chan->fence.sequence_ack - 1)) {
++ spin_lock_irqsave(&chan->fence.lock, flags);
++ nouveau_fence_update(chan);
++ spin_unlock_irqrestore(&chan->fence.lock, flags);
++
++ BUG_ON(chan->fence.sequence ==
++ chan->fence.sequence_ack - 1);
++ }
++
++ fence->sequence = ++chan->fence.sequence;
++
++ kref_get(&fence->refcount);
++ spin_lock_irqsave(&chan->fence.lock, flags);
++ list_add_tail(&fence->entry, &chan->fence.pending);
++ spin_unlock_irqrestore(&chan->fence.lock, flags);
++
++ BEGIN_RING(chan, NvSubSw, USE_REFCNT ? 0x0050 : 0x0150, 1);
++ OUT_RING(chan, fence->sequence);
++ FIRE_RING(chan);
++
++ return 0;
++}
++
++void
++nouveau_fence_unref(void **sync_obj)
++{
++ struct nouveau_fence *fence = nouveau_fence(*sync_obj);
++
++ if (fence)
++ kref_put(&fence->refcount, nouveau_fence_del);
++ *sync_obj = NULL;
++}
++
++void *
++nouveau_fence_ref(void *sync_obj)
++{
++ struct nouveau_fence *fence = nouveau_fence(sync_obj);
++
++ kref_get(&fence->refcount);
++ return sync_obj;
++}
++
++bool
++nouveau_fence_signalled(void *sync_obj, void *sync_arg)
++{
++ struct nouveau_fence *fence = nouveau_fence(sync_obj);
++ struct nouveau_channel *chan = fence->channel;
++ unsigned long flags;
++
++ if (fence->signalled)
++ return true;
++
++ spin_lock_irqsave(&chan->fence.lock, flags);
++ nouveau_fence_update(chan);
++ spin_unlock_irqrestore(&chan->fence.lock, flags);
++ return fence->signalled;
++}
++
++int
++nouveau_fence_wait(void *sync_obj, void *sync_arg, bool lazy, bool intr)
++{
++ unsigned long timeout = jiffies + (3 * DRM_HZ);
++ int ret = 0;
++
++ __set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
++
++ while (1) {
++ if (nouveau_fence_signalled(sync_obj, sync_arg))
++ break;
++
++ if (time_after_eq(jiffies, timeout)) {
++ ret = -EBUSY;
++ break;
++ }
++
++ if (lazy)
++ schedule_timeout(1);
++
++ if (intr && signal_pending(current)) {
++ ret = -ERESTARTSYS;
++ break;
++ }
++ }
++
++ __set_current_state(TASK_RUNNING);
++
++ return ret;
++}
++
++int
++nouveau_fence_flush(void *sync_obj, void *sync_arg)
++{
++ return 0;
++}
++
++void
++nouveau_fence_handler(struct drm_device *dev, int channel)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *chan = NULL;
++
++ if (channel >= 0 && channel < dev_priv->engine.fifo.channels)
++ chan = dev_priv->fifos[channel];
++
++ if (chan) {
++ spin_lock_irq(&chan->fence.lock);
++ nouveau_fence_update(chan);
++ spin_unlock_irq(&chan->fence.lock);
++ }
++}
++
++int
++nouveau_fence_init(struct nouveau_channel *chan)
++{
++ INIT_LIST_HEAD(&chan->fence.pending);
++ spin_lock_init(&chan->fence.lock);
++ return 0;
++}
++
++void
++nouveau_fence_fini(struct nouveau_channel *chan)
++{
++ struct list_head *entry, *tmp;
++ struct nouveau_fence *fence;
++
++ list_for_each_safe(entry, tmp, &chan->fence.pending) {
++ fence = list_entry(entry, struct nouveau_fence, entry);
++
++ fence->signalled = true;
++ list_del(&fence->entry);
++ kref_put(&fence->refcount, nouveau_fence_del);
++ }
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nouveau_gem.c b/drivers/gpu/drm/nouveau/nouveau_gem.c
+new file mode 100644
+index 0000000..70cc308
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_gem.c
+@@ -0,0 +1,994 @@
++/*
++ * Copyright (C) 2008 Ben Skeggs.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++#include "drmP.h"
++#include "drm.h"
++
++#include "nouveau_drv.h"
++#include "nouveau_drm.h"
++#include "nouveau_dma.h"
++
++#define nouveau_gem_pushbuf_sync(chan) 0
++
++int
++nouveau_gem_object_new(struct drm_gem_object *gem)
++{
++ return 0;
++}
++
++void
++nouveau_gem_object_del(struct drm_gem_object *gem)
++{
++ struct nouveau_bo *nvbo = gem->driver_private;
++ struct ttm_buffer_object *bo = &nvbo->bo;
++
++ if (!nvbo)
++ return;
++ nvbo->gem = NULL;
++
++ if (unlikely(nvbo->cpu_filp))
++ ttm_bo_synccpu_write_release(bo);
++
++ if (unlikely(nvbo->pin_refcnt)) {
++ nvbo->pin_refcnt = 1;
++ nouveau_bo_unpin(nvbo);
++ }
++
++ ttm_bo_unref(&bo);
++}
++
++int
++nouveau_gem_new(struct drm_device *dev, struct nouveau_channel *chan,
++ int size, int align, uint32_t flags, uint32_t tile_mode,
++ uint32_t tile_flags, bool no_vm, bool mappable,
++ struct nouveau_bo **pnvbo)
++{
++ struct nouveau_bo *nvbo;
++ int ret;
++
++ ret = nouveau_bo_new(dev, chan, size, align, flags, tile_mode,
++ tile_flags, no_vm, mappable, pnvbo);
++ if (ret)
++ return ret;
++ nvbo = *pnvbo;
++
++ nvbo->gem = drm_gem_object_alloc(dev, nvbo->bo.mem.size);
++ if (!nvbo->gem) {
++ nouveau_bo_ref(NULL, pnvbo);
++ return -ENOMEM;
++ }
++
++ nvbo->bo.persistant_swap_storage = nvbo->gem->filp;
++ nvbo->gem->driver_private = nvbo;
++ return 0;
++}
++
++static int
++nouveau_gem_info(struct drm_gem_object *gem, struct drm_nouveau_gem_info *rep)
++{
++ struct nouveau_bo *nvbo = nouveau_gem_object(gem);
++
++ if (nvbo->bo.mem.mem_type == TTM_PL_TT)
++ rep->domain = NOUVEAU_GEM_DOMAIN_GART;
++ else
++ rep->domain = NOUVEAU_GEM_DOMAIN_VRAM;
++
++ rep->size = nvbo->bo.mem.num_pages << PAGE_SHIFT;
++ rep->offset = nvbo->bo.offset;
++ rep->map_handle = nvbo->mappable ? nvbo->bo.addr_space_offset : 0;
++ rep->tile_mode = nvbo->tile_mode;
++ rep->tile_flags = nvbo->tile_flags;
++ return 0;
++}
++
++static bool
++nouveau_gem_tile_flags_valid(struct drm_device *dev, uint32_t tile_flags) {
++ switch (tile_flags) {
++ case 0x0000:
++ case 0x1800:
++ case 0x2800:
++ case 0x4800:
++ case 0x7000:
++ case 0x7400:
++ case 0x7a00:
++ case 0xe000:
++ break;
++ default:
++ NV_ERROR(dev, "bad page flags: 0x%08x\n", tile_flags);
++ return false;
++ }
++
++ return true;
++}
++
++int
++nouveau_gem_ioctl_new(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct drm_nouveau_gem_new *req = data;
++ struct nouveau_bo *nvbo = NULL;
++ struct nouveau_channel *chan = NULL;
++ uint32_t flags = 0;
++ int ret = 0;
++
++ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
++
++ if (unlikely(dev_priv->ttm.bdev.dev_mapping == NULL))
++ dev_priv->ttm.bdev.dev_mapping = dev_priv->dev->dev_mapping;
++
++ if (req->channel_hint) {
++ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel_hint,
++ file_priv, chan);
++ }
++
++ if (req->info.domain & NOUVEAU_GEM_DOMAIN_VRAM)
++ flags |= TTM_PL_FLAG_VRAM;
++ if (req->info.domain & NOUVEAU_GEM_DOMAIN_GART)
++ flags |= TTM_PL_FLAG_TT;
++ if (!flags || req->info.domain & NOUVEAU_GEM_DOMAIN_CPU)
++ flags |= TTM_PL_FLAG_SYSTEM;
++
++ if (!nouveau_gem_tile_flags_valid(dev, req->info.tile_flags))
++ return -EINVAL;
++
++ ret = nouveau_gem_new(dev, chan, req->info.size, req->align, flags,
++ req->info.tile_mode, req->info.tile_flags, false,
++ (req->info.domain & NOUVEAU_GEM_DOMAIN_MAPPABLE),
++ &nvbo);
++ if (ret)
++ return ret;
++
++ ret = nouveau_gem_info(nvbo->gem, &req->info);
++ if (ret)
++ goto out;
++
++ ret = drm_gem_handle_create(file_priv, nvbo->gem, &req->info.handle);
++out:
++ mutex_lock(&dev->struct_mutex);
++ drm_gem_object_handle_unreference(nvbo->gem);
++ mutex_unlock(&dev->struct_mutex);
++
++ if (ret)
++ drm_gem_object_unreference(nvbo->gem);
++ return ret;
++}
++
++static int
++nouveau_gem_set_domain(struct drm_gem_object *gem, uint32_t read_domains,
++ uint32_t write_domains, uint32_t valid_domains)
++{
++ struct nouveau_bo *nvbo = gem->driver_private;
++ struct ttm_buffer_object *bo = &nvbo->bo;
++ uint64_t flags;
++
++ if (!valid_domains || (!read_domains && !write_domains))
++ return -EINVAL;
++
++ if (write_domains) {
++ if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
++ (write_domains & NOUVEAU_GEM_DOMAIN_VRAM))
++ flags = TTM_PL_FLAG_VRAM;
++ else
++ if ((valid_domains & NOUVEAU_GEM_DOMAIN_GART) &&
++ (write_domains & NOUVEAU_GEM_DOMAIN_GART))
++ flags = TTM_PL_FLAG_TT;
++ else
++ return -EINVAL;
++ } else {
++ if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
++ (read_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
++ bo->mem.mem_type == TTM_PL_VRAM)
++ flags = TTM_PL_FLAG_VRAM;
++ else
++ if ((valid_domains & NOUVEAU_GEM_DOMAIN_GART) &&
++ (read_domains & NOUVEAU_GEM_DOMAIN_GART) &&
++ bo->mem.mem_type == TTM_PL_TT)
++ flags = TTM_PL_FLAG_TT;
++ else
++ if ((valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
++ (read_domains & NOUVEAU_GEM_DOMAIN_VRAM))
++ flags = TTM_PL_FLAG_VRAM;
++ else
++ flags = TTM_PL_FLAG_TT;
++ }
++
++ nouveau_bo_placement_set(nvbo, flags);
++ return 0;
++}
++
++struct validate_op {
++ struct list_head vram_list;
++ struct list_head gart_list;
++ struct list_head both_list;
++};
++
++static void
++validate_fini_list(struct list_head *list, struct nouveau_fence *fence)
++{
++ struct list_head *entry, *tmp;
++ struct nouveau_bo *nvbo;
++
++ list_for_each_safe(entry, tmp, list) {
++ nvbo = list_entry(entry, struct nouveau_bo, entry);
++ if (likely(fence)) {
++ struct nouveau_fence *prev_fence;
++
++ spin_lock(&nvbo->bo.lock);
++ prev_fence = nvbo->bo.sync_obj;
++ nvbo->bo.sync_obj = nouveau_fence_ref(fence);
++ spin_unlock(&nvbo->bo.lock);
++ nouveau_fence_unref((void *)&prev_fence);
++ }
++
++ list_del(&nvbo->entry);
++ nvbo->reserved_by = NULL;
++ ttm_bo_unreserve(&nvbo->bo);
++ drm_gem_object_unreference(nvbo->gem);
++ }
++}
++
++static void
++validate_fini(struct validate_op *op, struct nouveau_fence* fence)
++{
++ validate_fini_list(&op->vram_list, fence);
++ validate_fini_list(&op->gart_list, fence);
++ validate_fini_list(&op->both_list, fence);
++}
++
++static int
++validate_init(struct nouveau_channel *chan, struct drm_file *file_priv,
++ struct drm_nouveau_gem_pushbuf_bo *pbbo,
++ int nr_buffers, struct validate_op *op)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t sequence;
++ int trycnt = 0;
++ int ret, i;
++
++ sequence = atomic_add_return(1, &dev_priv->ttm.validate_sequence);
++retry:
++ if (++trycnt > 100000) {
++ NV_ERROR(dev, "%s failed and gave up.\n", __func__);
++ return -EINVAL;
++ }
++
++ for (i = 0; i < nr_buffers; i++) {
++ struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[i];
++ struct drm_gem_object *gem;
++ struct nouveau_bo *nvbo;
++
++ gem = drm_gem_object_lookup(dev, file_priv, b->handle);
++ if (!gem) {
++ NV_ERROR(dev, "Unknown handle 0x%08x\n", b->handle);
++ validate_fini(op, NULL);
++ return -EINVAL;
++ }
++ nvbo = gem->driver_private;
++
++ if (nvbo->reserved_by && nvbo->reserved_by == file_priv) {
++ NV_ERROR(dev, "multiple instances of buffer %d on "
++ "validation list\n", b->handle);
++ validate_fini(op, NULL);
++ return -EINVAL;
++ }
++
++ ret = ttm_bo_reserve(&nvbo->bo, false, false, true, sequence);
++ if (ret) {
++ validate_fini(op, NULL);
++ if (ret == -EAGAIN)
++ ret = ttm_bo_wait_unreserved(&nvbo->bo, false);
++ drm_gem_object_unreference(gem);
++ if (ret)
++ return ret;
++ goto retry;
++ }
++
++ nvbo->reserved_by = file_priv;
++ nvbo->pbbo_index = i;
++ if ((b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM) &&
++ (b->valid_domains & NOUVEAU_GEM_DOMAIN_GART))
++ list_add_tail(&nvbo->entry, &op->both_list);
++ else
++ if (b->valid_domains & NOUVEAU_GEM_DOMAIN_VRAM)
++ list_add_tail(&nvbo->entry, &op->vram_list);
++ else
++ if (b->valid_domains & NOUVEAU_GEM_DOMAIN_GART)
++ list_add_tail(&nvbo->entry, &op->gart_list);
++ else {
++ NV_ERROR(dev, "invalid valid domains: 0x%08x\n",
++ b->valid_domains);
++ list_add_tail(&nvbo->entry, &op->both_list);
++ validate_fini(op, NULL);
++ return -EINVAL;
++ }
++
++ if (unlikely(atomic_read(&nvbo->bo.cpu_writers) > 0)) {
++ validate_fini(op, NULL);
++
++ if (nvbo->cpu_filp == file_priv) {
++ NV_ERROR(dev, "bo %p mapped by process trying "
++ "to validate it!\n", nvbo);
++ return -EINVAL;
++ }
++
++ ret = ttm_bo_wait_cpu(&nvbo->bo, false);
++ if (ret)
++ return ret;
++ goto retry;
++ }
++ }
++
++ return 0;
++}
++
++static int
++validate_list(struct nouveau_channel *chan, struct list_head *list,
++ struct drm_nouveau_gem_pushbuf_bo *pbbo, uint64_t user_pbbo_ptr)
++{
++ struct drm_nouveau_gem_pushbuf_bo __user *upbbo =
++ (void __force __user *)(uintptr_t)user_pbbo_ptr;
++ struct nouveau_bo *nvbo;
++ int ret, relocs = 0;
++
++ list_for_each_entry(nvbo, list, entry) {
++ struct drm_nouveau_gem_pushbuf_bo *b = &pbbo[nvbo->pbbo_index];
++ struct nouveau_fence *prev_fence = nvbo->bo.sync_obj;
++
++ if (prev_fence && nouveau_fence_channel(prev_fence) != chan) {
++ spin_lock(&nvbo->bo.lock);
++ ret = ttm_bo_wait(&nvbo->bo, false, false, false);
++ spin_unlock(&nvbo->bo.lock);
++ if (unlikely(ret))
++ return ret;
++ }
++
++ ret = nouveau_gem_set_domain(nvbo->gem, b->read_domains,
++ b->write_domains,
++ b->valid_domains);
++ if (unlikely(ret))
++ return ret;
++
++ nvbo->channel = chan;
++ ret = ttm_bo_validate(&nvbo->bo, &nvbo->placement,
++ false, false);
++ nvbo->channel = NULL;
++ if (unlikely(ret))
++ return ret;
++
++ if (nvbo->bo.offset == b->presumed_offset &&
++ ((nvbo->bo.mem.mem_type == TTM_PL_VRAM &&
++ b->presumed_domain & NOUVEAU_GEM_DOMAIN_VRAM) ||
++ (nvbo->bo.mem.mem_type == TTM_PL_TT &&
++ b->presumed_domain & NOUVEAU_GEM_DOMAIN_GART)))
++ continue;
++
++ if (nvbo->bo.mem.mem_type == TTM_PL_TT)
++ b->presumed_domain = NOUVEAU_GEM_DOMAIN_GART;
++ else
++ b->presumed_domain = NOUVEAU_GEM_DOMAIN_VRAM;
++ b->presumed_offset = nvbo->bo.offset;
++ b->presumed_ok = 0;
++ relocs++;
++
++ if (DRM_COPY_TO_USER(&upbbo[nvbo->pbbo_index], b, sizeof(*b)))
++ return -EFAULT;
++ }
++
++ return relocs;
++}
++
++static int
++nouveau_gem_pushbuf_validate(struct nouveau_channel *chan,
++ struct drm_file *file_priv,
++ struct drm_nouveau_gem_pushbuf_bo *pbbo,
++ uint64_t user_buffers, int nr_buffers,
++ struct validate_op *op, int *apply_relocs)
++{
++ int ret, relocs = 0;
++
++ INIT_LIST_HEAD(&op->vram_list);
++ INIT_LIST_HEAD(&op->gart_list);
++ INIT_LIST_HEAD(&op->both_list);
++
++ if (nr_buffers == 0)
++ return 0;
++
++ ret = validate_init(chan, file_priv, pbbo, nr_buffers, op);
++ if (unlikely(ret))
++ return ret;
++
++ ret = validate_list(chan, &op->vram_list, pbbo, user_buffers);
++ if (unlikely(ret < 0)) {
++ validate_fini(op, NULL);
++ return ret;
++ }
++ relocs += ret;
++
++ ret = validate_list(chan, &op->gart_list, pbbo, user_buffers);
++ if (unlikely(ret < 0)) {
++ validate_fini(op, NULL);
++ return ret;
++ }
++ relocs += ret;
++
++ ret = validate_list(chan, &op->both_list, pbbo, user_buffers);
++ if (unlikely(ret < 0)) {
++ validate_fini(op, NULL);
++ return ret;
++ }
++ relocs += ret;
++
++ *apply_relocs = relocs;
++ return 0;
++}
++
++static inline void *
++u_memcpya(uint64_t user, unsigned nmemb, unsigned size)
++{
++ void *mem;
++ void __user *userptr = (void __force __user *)(uintptr_t)user;
++
++ mem = kmalloc(nmemb * size, GFP_KERNEL);
++ if (!mem)
++ return ERR_PTR(-ENOMEM);
++
++ if (DRM_COPY_FROM_USER(mem, userptr, nmemb * size)) {
++ kfree(mem);
++ return ERR_PTR(-EFAULT);
++ }
++
++ return mem;
++}
++
++static int
++nouveau_gem_pushbuf_reloc_apply(struct nouveau_channel *chan, int nr_bo,
++ struct drm_nouveau_gem_pushbuf_bo *bo,
++ unsigned nr_relocs, uint64_t ptr_relocs,
++ unsigned nr_dwords, unsigned first_dword,
++ uint32_t *pushbuf, bool is_iomem)
++{
++ struct drm_nouveau_gem_pushbuf_reloc *reloc = NULL;
++ struct drm_device *dev = chan->dev;
++ int ret = 0;
++ unsigned i;
++
++ reloc = u_memcpya(ptr_relocs, nr_relocs, sizeof(*reloc));
++ if (IS_ERR(reloc))
++ return PTR_ERR(reloc);
++
++ for (i = 0; i < nr_relocs; i++) {
++ struct drm_nouveau_gem_pushbuf_reloc *r = &reloc[i];
++ struct drm_nouveau_gem_pushbuf_bo *b;
++ uint32_t data;
++
++ if (r->bo_index >= nr_bo || r->reloc_index < first_dword ||
++ r->reloc_index >= first_dword + nr_dwords) {
++ NV_ERROR(dev, "Bad relocation %d\n", i);
++ NV_ERROR(dev, " bo: %d max %d\n", r->bo_index, nr_bo);
++ NV_ERROR(dev, " id: %d max %d\n", r->reloc_index, nr_dwords);
++ ret = -EINVAL;
++ break;
++ }
++
++ b = &bo[r->bo_index];
++ if (b->presumed_ok)
++ continue;
++
++ if (r->flags & NOUVEAU_GEM_RELOC_LOW)
++ data = b->presumed_offset + r->data;
++ else
++ if (r->flags & NOUVEAU_GEM_RELOC_HIGH)
++ data = (b->presumed_offset + r->data) >> 32;
++ else
++ data = r->data;
++
++ if (r->flags & NOUVEAU_GEM_RELOC_OR) {
++ if (b->presumed_domain == NOUVEAU_GEM_DOMAIN_GART)
++ data |= r->tor;
++ else
++ data |= r->vor;
++ }
++
++ if (is_iomem)
++ iowrite32_native(data, (void __force __iomem *)
++ &pushbuf[r->reloc_index]);
++ else
++ pushbuf[r->reloc_index] = data;
++ }
++
++ kfree(reloc);
++ return ret;
++}
++
++int
++nouveau_gem_ioctl_pushbuf(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_nouveau_gem_pushbuf *req = data;
++ struct drm_nouveau_gem_pushbuf_bo *bo = NULL;
++ struct nouveau_channel *chan;
++ struct validate_op op;
++ struct nouveau_fence* fence = 0;
++ uint32_t *pushbuf = NULL;
++ int ret = 0, do_reloc = 0, i;
++
++ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
++ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel, file_priv, chan);
++
++ if (req->nr_dwords >= chan->dma.max ||
++ req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS ||
++ req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS) {
++ NV_ERROR(dev, "Pushbuf config exceeds limits:\n");
++ NV_ERROR(dev, " dwords : %d max %d\n", req->nr_dwords,
++ chan->dma.max - 1);
++ NV_ERROR(dev, " buffers: %d max %d\n", req->nr_buffers,
++ NOUVEAU_GEM_MAX_BUFFERS);
++ NV_ERROR(dev, " relocs : %d max %d\n", req->nr_relocs,
++ NOUVEAU_GEM_MAX_RELOCS);
++ return -EINVAL;
++ }
++
++ pushbuf = u_memcpya(req->dwords, req->nr_dwords, sizeof(uint32_t));
++ if (IS_ERR(pushbuf))
++ return PTR_ERR(pushbuf);
++
++ bo = u_memcpya(req->buffers, req->nr_buffers, sizeof(*bo));
++ if (IS_ERR(bo)) {
++ kfree(pushbuf);
++ return PTR_ERR(bo);
++ }
++
++ mutex_lock(&dev->struct_mutex);
++
++ /* Validate buffer list */
++ ret = nouveau_gem_pushbuf_validate(chan, file_priv, bo, req->buffers,
++ req->nr_buffers, &op, &do_reloc);
++ if (ret)
++ goto out;
++
++ /* Apply any relocations that are required */
++ if (do_reloc) {
++ ret = nouveau_gem_pushbuf_reloc_apply(chan, req->nr_buffers,
++ bo, req->nr_relocs,
++ req->relocs,
++ req->nr_dwords, 0,
++ pushbuf, false);
++ if (ret)
++ goto out;
++ }
++
++ /* Emit push buffer to the hw
++ */
++ ret = RING_SPACE(chan, req->nr_dwords);
++ if (ret)
++ goto out;
++
++ OUT_RINGp(chan, pushbuf, req->nr_dwords);
++
++ ret = nouveau_fence_new(chan, &fence, true);
++ if (ret) {
++ NV_ERROR(dev, "error fencing pushbuf: %d\n", ret);
++ WIND_RING(chan);
++ goto out;
++ }
++
++ if (nouveau_gem_pushbuf_sync(chan)) {
++ ret = nouveau_fence_wait(fence, NULL, false, false);
++ if (ret) {
++ for (i = 0; i < req->nr_dwords; i++)
++ NV_ERROR(dev, "0x%08x\n", pushbuf[i]);
++ NV_ERROR(dev, "^^ above push buffer is fail :(\n");
++ }
++ }
++
++out:
++ validate_fini(&op, fence);
++ nouveau_fence_unref((void**)&fence);
++ mutex_unlock(&dev->struct_mutex);
++ kfree(pushbuf);
++ kfree(bo);
++ return ret;
++}
++
++#define PUSHBUF_CAL (dev_priv->card_type >= NV_20)
++
++int
++nouveau_gem_ioctl_pushbuf_call(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct drm_nouveau_gem_pushbuf_call *req = data;
++ struct drm_nouveau_gem_pushbuf_bo *bo = NULL;
++ struct nouveau_channel *chan;
++ struct drm_gem_object *gem;
++ struct nouveau_bo *pbbo;
++ struct validate_op op;
++ struct nouveau_fence* fence = 0;
++ int i, ret = 0, do_reloc = 0;
++
++ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
++ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(req->channel, file_priv, chan);
++
++ if (unlikely(req->handle == 0))
++ goto out_next;
++
++ if (req->nr_buffers > NOUVEAU_GEM_MAX_BUFFERS ||
++ req->nr_relocs > NOUVEAU_GEM_MAX_RELOCS) {
++ NV_ERROR(dev, "Pushbuf config exceeds limits:\n");
++ NV_ERROR(dev, " buffers: %d max %d\n", req->nr_buffers,
++ NOUVEAU_GEM_MAX_BUFFERS);
++ NV_ERROR(dev, " relocs : %d max %d\n", req->nr_relocs,
++ NOUVEAU_GEM_MAX_RELOCS);
++ return -EINVAL;
++ }
++
++ bo = u_memcpya(req->buffers, req->nr_buffers, sizeof(*bo));
++ if (IS_ERR(bo))
++ return PTR_ERR(bo);
++
++ mutex_lock(&dev->struct_mutex);
++
++ /* Validate buffer list */
++ ret = nouveau_gem_pushbuf_validate(chan, file_priv, bo, req->buffers,
++ req->nr_buffers, &op, &do_reloc);
++ if (ret) {
++ NV_ERROR(dev, "validate: %d\n", ret);
++ goto out;
++ }
++
++ /* Validate DMA push buffer */
++ gem = drm_gem_object_lookup(dev, file_priv, req->handle);
++ if (!gem) {
++ NV_ERROR(dev, "Unknown pb handle 0x%08x\n", req->handle);
++ ret = -EINVAL;
++ goto out;
++ }
++ pbbo = nouveau_gem_object(gem);
++
++ if ((req->offset & 3) || req->nr_dwords < 2 ||
++ (unsigned long)req->offset > (unsigned long)pbbo->bo.mem.size ||
++ (unsigned long)req->nr_dwords >
++ ((unsigned long)(pbbo->bo.mem.size - req->offset ) >> 2)) {
++ NV_ERROR(dev, "pb call misaligned or out of bounds: "
++ "%d + %d * 4 > %ld\n",
++ req->offset, req->nr_dwords, pbbo->bo.mem.size);
++ ret = -EINVAL;
++ drm_gem_object_unreference(gem);
++ goto out;
++ }
++
++ ret = ttm_bo_reserve(&pbbo->bo, false, false, true,
++ chan->fence.sequence);
++ if (ret) {
++ NV_ERROR(dev, "resv pb: %d\n", ret);
++ drm_gem_object_unreference(gem);
++ goto out;
++ }
++
++ nouveau_bo_placement_set(pbbo, 1 << chan->pushbuf_bo->bo.mem.mem_type);
++ ret = ttm_bo_validate(&pbbo->bo, &pbbo->placement, false, false);
++ if (ret) {
++ NV_ERROR(dev, "validate pb: %d\n", ret);
++ ttm_bo_unreserve(&pbbo->bo);
++ drm_gem_object_unreference(gem);
++ goto out;
++ }
++
++ list_add_tail(&pbbo->entry, &op.both_list);
++
++ /* If presumed return address doesn't match, we need to map the
++ * push buffer and fix it..
++ */
++ if (!PUSHBUF_CAL) {
++ uint32_t retaddy;
++
++ if (chan->dma.free < 4 + NOUVEAU_DMA_SKIPS) {
++ ret = nouveau_dma_wait(chan, 4 + NOUVEAU_DMA_SKIPS);
++ if (ret) {
++ NV_ERROR(dev, "jmp_space: %d\n", ret);
++ goto out;
++ }
++ }
++
++ retaddy = chan->pushbuf_base + ((chan->dma.cur + 2) << 2);
++ retaddy |= 0x20000000;
++ if (retaddy != req->suffix0) {
++ req->suffix0 = retaddy;
++ do_reloc = 1;
++ }
++ }
++
++ /* Apply any relocations that are required */
++ if (do_reloc) {
++ void *pbvirt;
++ bool is_iomem;
++ ret = ttm_bo_kmap(&pbbo->bo, 0, pbbo->bo.mem.num_pages,
++ &pbbo->kmap);
++ if (ret) {
++ NV_ERROR(dev, "kmap pb: %d\n", ret);
++ goto out;
++ }
++
++ pbvirt = ttm_kmap_obj_virtual(&pbbo->kmap, &is_iomem);
++ ret = nouveau_gem_pushbuf_reloc_apply(chan, req->nr_buffers, bo,
++ req->nr_relocs,
++ req->relocs,
++ req->nr_dwords,
++ req->offset / 4,
++ pbvirt, is_iomem);
++
++ if (!PUSHBUF_CAL) {
++ nouveau_bo_wr32(pbbo,
++ req->offset / 4 + req->nr_dwords - 2,
++ req->suffix0);
++ }
++
++ ttm_bo_kunmap(&pbbo->kmap);
++ if (ret) {
++ NV_ERROR(dev, "reloc apply: %d\n", ret);
++ goto out;
++ }
++ }
++
++ if (PUSHBUF_CAL) {
++ ret = RING_SPACE(chan, 2);
++ if (ret) {
++ NV_ERROR(dev, "cal_space: %d\n", ret);
++ goto out;
++ }
++ OUT_RING(chan, ((pbbo->bo.mem.mm_node->start << PAGE_SHIFT) +
++ req->offset) | 2);
++ OUT_RING(chan, 0);
++ } else {
++ ret = RING_SPACE(chan, 2 + NOUVEAU_DMA_SKIPS);
++ if (ret) {
++ NV_ERROR(dev, "jmp_space: %d\n", ret);
++ goto out;
++ }
++ OUT_RING(chan, ((pbbo->bo.mem.mm_node->start << PAGE_SHIFT) +
++ req->offset) | 0x20000000);
++ OUT_RING(chan, 0);
++
++ /* Space the jumps apart with NOPs. */
++ for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
++ OUT_RING(chan, 0);
++ }
++
++ ret = nouveau_fence_new(chan, &fence, true);
++ if (ret) {
++ NV_ERROR(dev, "error fencing pushbuf: %d\n", ret);
++ WIND_RING(chan);
++ goto out;
++ }
++
++out:
++ validate_fini(&op, fence);
++ nouveau_fence_unref((void**)&fence);
++ mutex_unlock(&dev->struct_mutex);
++ kfree(bo);
++
++out_next:
++ if (PUSHBUF_CAL) {
++ req->suffix0 = 0x00020000;
++ req->suffix1 = 0x00000000;
++ } else {
++ req->suffix0 = 0x20000000 |
++ (chan->pushbuf_base + ((chan->dma.cur + 2) << 2));
++ req->suffix1 = 0x00000000;
++ }
++
++ return ret;
++}
++
++int
++nouveau_gem_ioctl_pushbuf_call2(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct drm_nouveau_gem_pushbuf_call *req = data;
++
++ req->vram_available = dev_priv->fb_aper_free;
++ req->gart_available = dev_priv->gart_info.aper_free;
++
++ return nouveau_gem_ioctl_pushbuf_call(dev, data, file_priv);
++}
++
++static inline uint32_t
++domain_to_ttm(struct nouveau_bo *nvbo, uint32_t domain)
++{
++ uint32_t flags = 0;
++
++ if (domain & NOUVEAU_GEM_DOMAIN_VRAM)
++ flags |= TTM_PL_FLAG_VRAM;
++ if (domain & NOUVEAU_GEM_DOMAIN_GART)
++ flags |= TTM_PL_FLAG_TT;
++
++ return flags;
++}
++
++int
++nouveau_gem_ioctl_pin(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_nouveau_gem_pin *req = data;
++ struct drm_gem_object *gem;
++ struct nouveau_bo *nvbo;
++ int ret = 0;
++
++ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
++
++ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
++ NV_ERROR(dev, "pin only allowed without kernel modesetting\n");
++ return -EINVAL;
++ }
++
++ if (!DRM_SUSER(DRM_CURPROC))
++ return -EPERM;
++
++ gem = drm_gem_object_lookup(dev, file_priv, req->handle);
++ if (!gem)
++ return -EINVAL;
++ nvbo = nouveau_gem_object(gem);
++
++ ret = nouveau_bo_pin(nvbo, domain_to_ttm(nvbo, req->domain));
++ if (ret)
++ goto out;
++
++ req->offset = nvbo->bo.offset;
++ if (nvbo->bo.mem.mem_type == TTM_PL_TT)
++ req->domain = NOUVEAU_GEM_DOMAIN_GART;
++ else
++ req->domain = NOUVEAU_GEM_DOMAIN_VRAM;
++
++out:
++ mutex_lock(&dev->struct_mutex);
++ drm_gem_object_unreference(gem);
++ mutex_unlock(&dev->struct_mutex);
++
++ return ret;
++}
++
++int
++nouveau_gem_ioctl_unpin(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_nouveau_gem_pin *req = data;
++ struct drm_gem_object *gem;
++ int ret;
++
++ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
++
++ if (drm_core_check_feature(dev, DRIVER_MODESET))
++ return -EINVAL;
++
++ gem = drm_gem_object_lookup(dev, file_priv, req->handle);
++ if (!gem)
++ return -EINVAL;
++
++ ret = nouveau_bo_unpin(nouveau_gem_object(gem));
++
++ mutex_lock(&dev->struct_mutex);
++ drm_gem_object_unreference(gem);
++ mutex_unlock(&dev->struct_mutex);
++
++ return ret;
++}
++
++int
++nouveau_gem_ioctl_cpu_prep(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_nouveau_gem_cpu_prep *req = data;
++ struct drm_gem_object *gem;
++ struct nouveau_bo *nvbo;
++ bool no_wait = !!(req->flags & NOUVEAU_GEM_CPU_PREP_NOWAIT);
++ int ret = -EINVAL;
++
++ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
++
++ gem = drm_gem_object_lookup(dev, file_priv, req->handle);
++ if (!gem)
++ return ret;
++ nvbo = nouveau_gem_object(gem);
++
++ if (nvbo->cpu_filp) {
++ if (nvbo->cpu_filp == file_priv)
++ goto out;
++
++ ret = ttm_bo_wait_cpu(&nvbo->bo, no_wait);
++ if (ret)
++ goto out;
++ }
++
++ if (req->flags & NOUVEAU_GEM_CPU_PREP_NOBLOCK) {
++ spin_lock(&nvbo->bo.lock);
++ ret = ttm_bo_wait(&nvbo->bo, false, false, no_wait);
++ spin_unlock(&nvbo->bo.lock);
++ } else {
++ ret = ttm_bo_synccpu_write_grab(&nvbo->bo, no_wait);
++ if (ret == 0)
++ nvbo->cpu_filp = file_priv;
++ }
++
++out:
++ mutex_lock(&dev->struct_mutex);
++ drm_gem_object_unreference(gem);
++ mutex_unlock(&dev->struct_mutex);
++ return ret;
++}
++
++int
++nouveau_gem_ioctl_cpu_fini(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_nouveau_gem_cpu_prep *req = data;
++ struct drm_gem_object *gem;
++ struct nouveau_bo *nvbo;
++ int ret = -EINVAL;
++
++ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
++
++ gem = drm_gem_object_lookup(dev, file_priv, req->handle);
++ if (!gem)
++ return ret;
++ nvbo = nouveau_gem_object(gem);
++
++ if (nvbo->cpu_filp != file_priv)
++ goto out;
++ nvbo->cpu_filp = NULL;
++
++ ttm_bo_synccpu_write_release(&nvbo->bo);
++ ret = 0;
++
++out:
++ mutex_lock(&dev->struct_mutex);
++ drm_gem_object_unreference(gem);
++ mutex_unlock(&dev->struct_mutex);
++ return ret;
++}
++
++int
++nouveau_gem_ioctl_info(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_nouveau_gem_info *req = data;
++ struct drm_gem_object *gem;
++ int ret;
++
++ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
++
++ gem = drm_gem_object_lookup(dev, file_priv, req->handle);
++ if (!gem)
++ return -EINVAL;
++
++ ret = nouveau_gem_info(gem, req);
++ mutex_lock(&dev->struct_mutex);
++ drm_gem_object_unreference(gem);
++ mutex_unlock(&dev->struct_mutex);
++ return ret;
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nouveau_grctx.c b/drivers/gpu/drm/nouveau/nouveau_grctx.c
+new file mode 100644
+index 0000000..c7ebec6
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_grctx.c
+@@ -0,0 +1,161 @@
++/*
++ * Copyright 2009 Red Hat Inc.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ *
++ * Authors: Ben Skeggs
++ */
++
++#include <linux/firmware.h>
++
++#include "drmP.h"
++#include "nouveau_drv.h"
++
++struct nouveau_ctxprog {
++ uint32_t signature;
++ uint8_t version;
++ uint16_t length;
++ uint32_t data[];
++} __attribute__ ((packed));
++
++struct nouveau_ctxvals {
++ uint32_t signature;
++ uint8_t version;
++ uint32_t length;
++ struct {
++ uint32_t offset;
++ uint32_t value;
++ } data[];
++} __attribute__ ((packed));
++
++int
++nouveau_grctx_prog_load(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
++ const int chipset = dev_priv->chipset;
++ const struct firmware *fw;
++ const struct nouveau_ctxprog *cp;
++ const struct nouveau_ctxvals *cv;
++ char name[32];
++ int ret, i;
++
++ if (pgraph->accel_blocked)
++ return -ENODEV;
++
++ if (!pgraph->ctxprog) {
++ sprintf(name, "nouveau/nv%02x.ctxprog", chipset);
++ ret = request_firmware(&fw, name, &dev->pdev->dev);
++ if (ret) {
++ NV_ERROR(dev, "No ctxprog for NV%02x\n", chipset);
++ return ret;
++ }
++
++ pgraph->ctxprog = kmalloc(fw->size, GFP_KERNEL);
++ if (!pgraph->ctxprog) {
++ NV_ERROR(dev, "OOM copying ctxprog\n");
++ release_firmware(fw);
++ return -ENOMEM;
++ }
++ memcpy(pgraph->ctxprog, fw->data, fw->size);
++
++ cp = pgraph->ctxprog;
++ if (le32_to_cpu(cp->signature) != 0x5043564e ||
++ cp->version != 0 ||
++ le16_to_cpu(cp->length) != ((fw->size - 7) / 4)) {
++ NV_ERROR(dev, "ctxprog invalid\n");
++ release_firmware(fw);
++ nouveau_grctx_fini(dev);
++ return -EINVAL;
++ }
++ release_firmware(fw);
++ }
++
++ if (!pgraph->ctxvals) {
++ sprintf(name, "nouveau/nv%02x.ctxvals", chipset);
++ ret = request_firmware(&fw, name, &dev->pdev->dev);
++ if (ret) {
++ NV_ERROR(dev, "No ctxvals for NV%02x\n", chipset);
++ nouveau_grctx_fini(dev);
++ return ret;
++ }
++
++ pgraph->ctxvals = kmalloc(fw->size, GFP_KERNEL);
++ if (!pgraph->ctxvals) {
++ NV_ERROR(dev, "OOM copying ctxvals\n");
++ release_firmware(fw);
++ nouveau_grctx_fini(dev);
++ return -ENOMEM;
++ }
++ memcpy(pgraph->ctxvals, fw->data, fw->size);
++
++ cv = (void *)pgraph->ctxvals;
++ if (le32_to_cpu(cv->signature) != 0x5643564e ||
++ cv->version != 0 ||
++ le32_to_cpu(cv->length) != ((fw->size - 9) / 8)) {
++ NV_ERROR(dev, "ctxvals invalid\n");
++ release_firmware(fw);
++ nouveau_grctx_fini(dev);
++ return -EINVAL;
++ }
++ release_firmware(fw);
++ }
++
++ cp = pgraph->ctxprog;
++
++ nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0);
++ for (i = 0; i < le16_to_cpu(cp->length); i++)
++ nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA,
++ le32_to_cpu(cp->data[i]));
++
++ return 0;
++}
++
++void
++nouveau_grctx_fini(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
++
++ if (pgraph->ctxprog) {
++ kfree(pgraph->ctxprog);
++ pgraph->ctxprog = NULL;
++ }
++
++ if (pgraph->ctxvals) {
++ kfree(pgraph->ctxprog);
++ pgraph->ctxvals = NULL;
++ }
++}
++
++void
++nouveau_grctx_vals_load(struct drm_device *dev, struct nouveau_gpuobj *ctx)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
++ struct nouveau_ctxvals *cv = pgraph->ctxvals;
++ int i;
++
++ if (!cv)
++ return;
++
++ for (i = 0; i < le32_to_cpu(cv->length); i++)
++ nv_wo32(dev, ctx, le32_to_cpu(cv->data[i].offset),
++ le32_to_cpu(cv->data[i].value));
++}
+diff --git a/drivers/gpu/drm/nouveau/nouveau_grctx.h b/drivers/gpu/drm/nouveau/nouveau_grctx.h
+new file mode 100644
+index 0000000..5d39c4c
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_grctx.h
+@@ -0,0 +1,133 @@
++#ifndef __NOUVEAU_GRCTX_H__
++#define __NOUVEAU_GRCTX_H__
++
++struct nouveau_grctx {
++ struct drm_device *dev;
++
++ enum {
++ NOUVEAU_GRCTX_PROG,
++ NOUVEAU_GRCTX_VALS
++ } mode;
++ void *data;
++
++ uint32_t ctxprog_max;
++ uint32_t ctxprog_len;
++ uint32_t ctxprog_reg;
++ int ctxprog_label[32];
++ uint32_t ctxvals_pos;
++ uint32_t ctxvals_base;
++};
++
++#ifdef CP_CTX
++static inline void
++cp_out(struct nouveau_grctx *ctx, uint32_t inst)
++{
++ uint32_t *ctxprog = ctx->data;
++
++ if (ctx->mode != NOUVEAU_GRCTX_PROG)
++ return;
++
++ BUG_ON(ctx->ctxprog_len == ctx->ctxprog_max);
++ ctxprog[ctx->ctxprog_len++] = inst;
++}
++
++static inline void
++cp_lsr(struct nouveau_grctx *ctx, uint32_t val)
++{
++ cp_out(ctx, CP_LOAD_SR | val);
++}
++
++static inline void
++cp_ctx(struct nouveau_grctx *ctx, uint32_t reg, uint32_t length)
++{
++ ctx->ctxprog_reg = (reg - 0x00400000) >> 2;
++
++ ctx->ctxvals_base = ctx->ctxvals_pos;
++ ctx->ctxvals_pos = ctx->ctxvals_base + length;
++
++ if (length > (CP_CTX_COUNT >> CP_CTX_COUNT_SHIFT)) {
++ cp_lsr(ctx, length);
++ length = 0;
++ }
++
++ cp_out(ctx, CP_CTX | (length << CP_CTX_COUNT_SHIFT) | ctx->ctxprog_reg);
++}
++
++static inline void
++cp_name(struct nouveau_grctx *ctx, int name)
++{
++ uint32_t *ctxprog = ctx->data;
++ int i;
++
++ if (ctx->mode != NOUVEAU_GRCTX_PROG)
++ return;
++
++ ctx->ctxprog_label[name] = ctx->ctxprog_len;
++ for (i = 0; i < ctx->ctxprog_len; i++) {
++ if ((ctxprog[i] & 0xfff00000) != 0xff400000)
++ continue;
++ if ((ctxprog[i] & CP_BRA_IP) != ((name) << CP_BRA_IP_SHIFT))
++ continue;
++ ctxprog[i] = (ctxprog[i] & 0x00ff00ff) |
++ (ctx->ctxprog_len << CP_BRA_IP_SHIFT);
++ }
++}
++
++static inline void
++_cp_bra(struct nouveau_grctx *ctx, u32 mod, int flag, int state, int name)
++{
++ int ip = 0;
++
++ if (mod != 2) {
++ ip = ctx->ctxprog_label[name] << CP_BRA_IP_SHIFT;
++ if (ip == 0)
++ ip = 0xff000000 | (name << CP_BRA_IP_SHIFT);
++ }
++
++ cp_out(ctx, CP_BRA | (mod << 18) | ip | flag |
++ (state ? 0 : CP_BRA_IF_CLEAR));
++}
++#define cp_bra(c,f,s,n) _cp_bra((c), 0, CP_FLAG_##f, CP_FLAG_##f##_##s, n)
++#ifdef CP_BRA_MOD
++#define cp_cal(c,f,s,n) _cp_bra((c), 1, CP_FLAG_##f, CP_FLAG_##f##_##s, n)
++#define cp_ret(c,f,s) _cp_bra((c), 2, CP_FLAG_##f, CP_FLAG_##f##_##s, 0)
++#endif
++
++static inline void
++_cp_wait(struct nouveau_grctx *ctx, int flag, int state)
++{
++ cp_out(ctx, CP_WAIT | flag | (state ? CP_WAIT_SET : 0));
++}
++#define cp_wait(c,f,s) _cp_wait((c), CP_FLAG_##f, CP_FLAG_##f##_##s)
++
++static inline void
++_cp_set(struct nouveau_grctx *ctx, int flag, int state)
++{
++ cp_out(ctx, CP_SET | flag | (state ? CP_SET_1 : 0));
++}
++#define cp_set(c,f,s) _cp_set((c), CP_FLAG_##f, CP_FLAG_##f##_##s)
++
++static inline void
++cp_pos(struct nouveau_grctx *ctx, int offset)
++{
++ ctx->ctxvals_pos = offset;
++ ctx->ctxvals_base = ctx->ctxvals_pos;
++
++ cp_lsr(ctx, ctx->ctxvals_pos);
++ cp_out(ctx, CP_SET_CONTEXT_POINTER);
++}
++
++static inline void
++gr_def(struct nouveau_grctx *ctx, uint32_t reg, uint32_t val)
++{
++ if (ctx->mode != NOUVEAU_GRCTX_VALS)
++ return;
++
++ reg = (reg - 0x00400000) / 4;
++ reg = (reg - ctx->ctxprog_reg) + ctx->ctxvals_base;
++
++ nv_wo32(ctx->dev, ctx->data, reg, val);
++}
++#endif
++
++#endif
+diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.c b/drivers/gpu/drm/nouveau/nouveau_hw.c
+new file mode 100644
+index 0000000..dc46792
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_hw.c
+@@ -0,0 +1,1080 @@
++/*
++ * Copyright 2006 Dave Airlie
++ * Copyright 2007 Maarten Maathuis
++ * Copyright 2007-2009 Stuart Bennett
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
++ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ */
++
++#include "drmP.h"
++#include "nouveau_drv.h"
++#include "nouveau_hw.h"
++
++#define CHIPSET_NFORCE 0x01a0
++#define CHIPSET_NFORCE2 0x01f0
++
++/*
++ * misc hw access wrappers/control functions
++ */
++
++void
++NVWriteVgaSeq(struct drm_device *dev, int head, uint8_t index, uint8_t value)
++{
++ NVWritePRMVIO(dev, head, NV_PRMVIO_SRX, index);
++ NVWritePRMVIO(dev, head, NV_PRMVIO_SR, value);
++}
++
++uint8_t
++NVReadVgaSeq(struct drm_device *dev, int head, uint8_t index)
++{
++ NVWritePRMVIO(dev, head, NV_PRMVIO_SRX, index);
++ return NVReadPRMVIO(dev, head, NV_PRMVIO_SR);
++}
++
++void
++NVWriteVgaGr(struct drm_device *dev, int head, uint8_t index, uint8_t value)
++{
++ NVWritePRMVIO(dev, head, NV_PRMVIO_GRX, index);
++ NVWritePRMVIO(dev, head, NV_PRMVIO_GX, value);
++}
++
++uint8_t
++NVReadVgaGr(struct drm_device *dev, int head, uint8_t index)
++{
++ NVWritePRMVIO(dev, head, NV_PRMVIO_GRX, index);
++ return NVReadPRMVIO(dev, head, NV_PRMVIO_GX);
++}
++
++/* CR44 takes values 0 (head A), 3 (head B) and 4 (heads tied)
++ * it affects only the 8 bit vga io regs, which we access using mmio at
++ * 0xc{0,2}3c*, 0x60{1,3}3*, and 0x68{1,3}3d*
++ * in general, the set value of cr44 does not matter: reg access works as
++ * expected and values can be set for the appropriate head by using a 0x2000
++ * offset as required
++ * however:
++ * a) pre nv40, the head B range of PRMVIO regs at 0xc23c* was not exposed and
++ * cr44 must be set to 0 or 3 for accessing values on the correct head
++ * through the common 0xc03c* addresses
++ * b) in tied mode (4) head B is programmed to the values set on head A, and
++ * access using the head B addresses can have strange results, ergo we leave
++ * tied mode in init once we know to what cr44 should be restored on exit
++ *
++ * the owner parameter is slightly abused:
++ * 0 and 1 are treated as head values and so the set value is (owner * 3)
++ * other values are treated as literal values to set
++ */
++void
++NVSetOwner(struct drm_device *dev, int owner)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ if (owner == 1)
++ owner *= 3;
++
++ if (dev_priv->chipset == 0x11) {
++ /* This might seem stupid, but the blob does it and
++ * omitting it often locks the system up.
++ */
++ NVReadVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX);
++ NVReadVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX);
++ }
++
++ /* CR44 is always changed on CRTC0 */
++ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_44, owner);
++
++ if (dev_priv->chipset == 0x11) { /* set me harder */
++ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner);
++ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_2E, owner);
++ }
++}
++
++void
++NVBlankScreen(struct drm_device *dev, int head, bool blank)
++{
++ unsigned char seq1;
++
++ if (nv_two_heads(dev))
++ NVSetOwner(dev, head);
++
++ seq1 = NVReadVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX);
++
++ NVVgaSeqReset(dev, head, true);
++ if (blank)
++ NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 | 0x20);
++ else
++ NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 & ~0x20);
++ NVVgaSeqReset(dev, head, false);
++}
++
++/*
++ * PLL setting
++ */
++
++static int
++powerctrl_1_shift(int chip_version, int reg)
++{
++ int shift = -4;
++
++ if (chip_version < 0x17 || chip_version == 0x1a || chip_version == 0x20)
++ return shift;
++
++ switch (reg) {
++ case NV_RAMDAC_VPLL2:
++ shift += 4;
++ case NV_PRAMDAC_VPLL_COEFF:
++ shift += 4;
++ case NV_PRAMDAC_MPLL_COEFF:
++ shift += 4;
++ case NV_PRAMDAC_NVPLL_COEFF:
++ shift += 4;
++ }
++
++ /*
++ * the shift for vpll regs is only used for nv3x chips with a single
++ * stage pll
++ */
++ if (shift > 4 && (chip_version < 0x32 || chip_version == 0x35 ||
++ chip_version == 0x36 || chip_version >= 0x40))
++ shift = -4;
++
++ return shift;
++}
++
++static void
++setPLL_single(struct drm_device *dev, uint32_t reg, struct nouveau_pll_vals *pv)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int chip_version = dev_priv->vbios->chip_version;
++ uint32_t oldpll = NVReadRAMDAC(dev, 0, reg);
++ int oldN = (oldpll >> 8) & 0xff, oldM = oldpll & 0xff;
++ uint32_t pll = (oldpll & 0xfff80000) | pv->log2P << 16 | pv->NM1;
++ uint32_t saved_powerctrl_1 = 0;
++ int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg);
++
++ if (oldpll == pll)
++ return; /* already set */
++
++ if (shift_powerctrl_1 >= 0) {
++ saved_powerctrl_1 = nvReadMC(dev, NV_PBUS_POWERCTRL_1);
++ nvWriteMC(dev, NV_PBUS_POWERCTRL_1,
++ (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
++ 1 << shift_powerctrl_1);
++ }
++
++ if (oldM && pv->M1 && (oldN / oldM < pv->N1 / pv->M1))
++ /* upclock -- write new post divider first */
++ NVWriteRAMDAC(dev, 0, reg, pv->log2P << 16 | (oldpll & 0xffff));
++ else
++ /* downclock -- write new NM first */
++ NVWriteRAMDAC(dev, 0, reg, (oldpll & 0xffff0000) | pv->NM1);
++
++ if (chip_version < 0x17 && chip_version != 0x11)
++ /* wait a bit on older chips */
++ msleep(64);
++ NVReadRAMDAC(dev, 0, reg);
++
++ /* then write the other half as well */
++ NVWriteRAMDAC(dev, 0, reg, pll);
++
++ if (shift_powerctrl_1 >= 0)
++ nvWriteMC(dev, NV_PBUS_POWERCTRL_1, saved_powerctrl_1);
++}
++
++static uint32_t
++new_ramdac580(uint32_t reg1, bool ss, uint32_t ramdac580)
++{
++ bool head_a = (reg1 == NV_PRAMDAC_VPLL_COEFF);
++
++ if (ss) /* single stage pll mode */
++ ramdac580 |= head_a ? NV_RAMDAC_580_VPLL1_ACTIVE :
++ NV_RAMDAC_580_VPLL2_ACTIVE;
++ else
++ ramdac580 &= head_a ? ~NV_RAMDAC_580_VPLL1_ACTIVE :
++ ~NV_RAMDAC_580_VPLL2_ACTIVE;
++
++ return ramdac580;
++}
++
++static void
++setPLL_double_highregs(struct drm_device *dev, uint32_t reg1,
++ struct nouveau_pll_vals *pv)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int chip_version = dev_priv->vbios->chip_version;
++ bool nv3035 = chip_version == 0x30 || chip_version == 0x35;
++ uint32_t reg2 = reg1 + ((reg1 == NV_RAMDAC_VPLL2) ? 0x5c : 0x70);
++ uint32_t oldpll1 = NVReadRAMDAC(dev, 0, reg1);
++ uint32_t oldpll2 = !nv3035 ? NVReadRAMDAC(dev, 0, reg2) : 0;
++ uint32_t pll1 = (oldpll1 & 0xfff80000) | pv->log2P << 16 | pv->NM1;
++ uint32_t pll2 = (oldpll2 & 0x7fff0000) | 1 << 31 | pv->NM2;
++ uint32_t oldramdac580 = 0, ramdac580 = 0;
++ bool single_stage = !pv->NM2 || pv->N2 == pv->M2; /* nv41+ only */
++ uint32_t saved_powerctrl_1 = 0, savedc040 = 0;
++ int shift_powerctrl_1 = powerctrl_1_shift(chip_version, reg1);
++
++ /* model specific additions to generic pll1 and pll2 set up above */
++ if (nv3035) {
++ pll1 = (pll1 & 0xfcc7ffff) | (pv->N2 & 0x18) << 21 |
++ (pv->N2 & 0x7) << 19 | 8 << 4 | (pv->M2 & 7) << 4;
++ pll2 = 0;
++ }
++ if (chip_version > 0x40 && reg1 >= NV_PRAMDAC_VPLL_COEFF) { /* !nv40 */
++ oldramdac580 = NVReadRAMDAC(dev, 0, NV_PRAMDAC_580);
++ ramdac580 = new_ramdac580(reg1, single_stage, oldramdac580);
++ if (oldramdac580 != ramdac580)
++ oldpll1 = ~0; /* force mismatch */
++ if (single_stage)
++ /* magic value used by nvidia in single stage mode */
++ pll2 |= 0x011f;
++ }
++ if (chip_version > 0x70)
++ /* magic bits set by the blob (but not the bios) on g71-73 */
++ pll1 = (pll1 & 0x7fffffff) | (single_stage ? 0x4 : 0xc) << 28;
++
++ if (oldpll1 == pll1 && oldpll2 == pll2)
++ return; /* already set */
++
++ if (shift_powerctrl_1 >= 0) {
++ saved_powerctrl_1 = nvReadMC(dev, NV_PBUS_POWERCTRL_1);
++ nvWriteMC(dev, NV_PBUS_POWERCTRL_1,
++ (saved_powerctrl_1 & ~(0xf << shift_powerctrl_1)) |
++ 1 << shift_powerctrl_1);
++ }
++
++ if (chip_version >= 0x40) {
++ int shift_c040 = 14;
++
++ switch (reg1) {
++ case NV_PRAMDAC_MPLL_COEFF:
++ shift_c040 += 2;
++ case NV_PRAMDAC_NVPLL_COEFF:
++ shift_c040 += 2;
++ case NV_RAMDAC_VPLL2:
++ shift_c040 += 2;
++ case NV_PRAMDAC_VPLL_COEFF:
++ shift_c040 += 2;
++ }
++
++ savedc040 = nvReadMC(dev, 0xc040);
++ if (shift_c040 != 14)
++ nvWriteMC(dev, 0xc040, savedc040 & ~(3 << shift_c040));
++ }
++
++ if (oldramdac580 != ramdac580)
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_580, ramdac580);
++
++ if (!nv3035)
++ NVWriteRAMDAC(dev, 0, reg2, pll2);
++ NVWriteRAMDAC(dev, 0, reg1, pll1);
++
++ if (shift_powerctrl_1 >= 0)
++ nvWriteMC(dev, NV_PBUS_POWERCTRL_1, saved_powerctrl_1);
++ if (chip_version >= 0x40)
++ nvWriteMC(dev, 0xc040, savedc040);
++}
++
++static void
++setPLL_double_lowregs(struct drm_device *dev, uint32_t NMNMreg,
++ struct nouveau_pll_vals *pv)
++{
++ /* When setting PLLs, there is a merry game of disabling and enabling
++ * various bits of hardware during the process. This function is a
++ * synthesis of six nv4x traces, nearly each card doing a subtly
++ * different thing. With luck all the necessary bits for each card are
++ * combined herein. Without luck it deviates from each card's formula
++ * so as to not work on any :)
++ */
++
++ uint32_t Preg = NMNMreg - 4;
++ bool mpll = Preg == 0x4020;
++ uint32_t oldPval = nvReadMC(dev, Preg);
++ uint32_t NMNM = pv->NM2 << 16 | pv->NM1;
++ uint32_t Pval = (oldPval & (mpll ? ~(0x11 << 16) : ~(1 << 16))) |
++ 0xc << 28 | pv->log2P << 16;
++ uint32_t saved4600 = 0;
++ /* some cards have different maskc040s */
++ uint32_t maskc040 = ~(3 << 14), savedc040;
++ bool single_stage = !pv->NM2 || pv->N2 == pv->M2;
++
++ if (nvReadMC(dev, NMNMreg) == NMNM && (oldPval & 0xc0070000) == Pval)
++ return;
++
++ if (Preg == 0x4000)
++ maskc040 = ~0x333;
++ if (Preg == 0x4058)
++ maskc040 = ~(0xc << 24);
++
++ if (mpll) {
++ struct pll_lims pll_lim;
++ uint8_t Pval2;
++
++ if (get_pll_limits(dev, Preg, &pll_lim))
++ return;
++
++ Pval2 = pv->log2P + pll_lim.log2p_bias;
++ if (Pval2 > pll_lim.max_log2p)
++ Pval2 = pll_lim.max_log2p;
++ Pval |= 1 << 28 | Pval2 << 20;
++
++ saved4600 = nvReadMC(dev, 0x4600);
++ nvWriteMC(dev, 0x4600, saved4600 | 8 << 28);
++ }
++ if (single_stage)
++ Pval |= mpll ? 1 << 12 : 1 << 8;
++
++ nvWriteMC(dev, Preg, oldPval | 1 << 28);
++ nvWriteMC(dev, Preg, Pval & ~(4 << 28));
++ if (mpll) {
++ Pval |= 8 << 20;
++ nvWriteMC(dev, 0x4020, Pval & ~(0xc << 28));
++ nvWriteMC(dev, 0x4038, Pval & ~(0xc << 28));
++ }
++
++ savedc040 = nvReadMC(dev, 0xc040);
++ nvWriteMC(dev, 0xc040, savedc040 & maskc040);
++
++ nvWriteMC(dev, NMNMreg, NMNM);
++ if (NMNMreg == 0x4024)
++ nvWriteMC(dev, 0x403c, NMNM);
++
++ nvWriteMC(dev, Preg, Pval);
++ if (mpll) {
++ Pval &= ~(8 << 20);
++ nvWriteMC(dev, 0x4020, Pval);
++ nvWriteMC(dev, 0x4038, Pval);
++ nvWriteMC(dev, 0x4600, saved4600);
++ }
++
++ nvWriteMC(dev, 0xc040, savedc040);
++
++ if (mpll) {
++ nvWriteMC(dev, 0x4020, Pval & ~(1 << 28));
++ nvWriteMC(dev, 0x4038, Pval & ~(1 << 28));
++ }
++}
++
++void
++nouveau_hw_setpll(struct drm_device *dev, uint32_t reg1,
++ struct nouveau_pll_vals *pv)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int cv = dev_priv->vbios->chip_version;
++
++ if (cv == 0x30 || cv == 0x31 || cv == 0x35 || cv == 0x36 ||
++ cv >= 0x40) {
++ if (reg1 > 0x405c)
++ setPLL_double_highregs(dev, reg1, pv);
++ else
++ setPLL_double_lowregs(dev, reg1, pv);
++ } else
++ setPLL_single(dev, reg1, pv);
++}
++
++/*
++ * PLL getting
++ */
++
++static void
++nouveau_hw_decode_pll(struct drm_device *dev, uint32_t reg1, uint32_t pll1,
++ uint32_t pll2, struct nouveau_pll_vals *pllvals)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ /* to force parsing as single stage (i.e. nv40 vplls) pass pll2 as 0 */
++
++ /* log2P is & 0x7 as never more than 7, and nv30/35 only uses 3 bits */
++ pllvals->log2P = (pll1 >> 16) & 0x7;
++ pllvals->N2 = pllvals->M2 = 1;
++
++ if (reg1 <= 0x405c) {
++ pllvals->NM1 = pll2 & 0xffff;
++ /* single stage NVPLL and VPLLs use 1 << 8, MPLL uses 1 << 12 */
++ if (!(pll1 & 0x1100))
++ pllvals->NM2 = pll2 >> 16;
++ } else {
++ pllvals->NM1 = pll1 & 0xffff;
++ if (nv_two_reg_pll(dev) && pll2 & NV31_RAMDAC_ENABLE_VCO2)
++ pllvals->NM2 = pll2 & 0xffff;
++ else if (dev_priv->chipset == 0x30 || dev_priv->chipset == 0x35) {
++ pllvals->M1 &= 0xf; /* only 4 bits */
++ if (pll1 & NV30_RAMDAC_ENABLE_VCO2) {
++ pllvals->M2 = (pll1 >> 4) & 0x7;
++ pllvals->N2 = ((pll1 >> 21) & 0x18) |
++ ((pll1 >> 19) & 0x7);
++ }
++ }
++ }
++}
++
++int
++nouveau_hw_get_pllvals(struct drm_device *dev, enum pll_types plltype,
++ struct nouveau_pll_vals *pllvals)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ const uint32_t nv04_regs[MAX_PLL_TYPES] = { NV_PRAMDAC_NVPLL_COEFF,
++ NV_PRAMDAC_MPLL_COEFF,
++ NV_PRAMDAC_VPLL_COEFF,
++ NV_RAMDAC_VPLL2 };
++ const uint32_t nv40_regs[MAX_PLL_TYPES] = { 0x4000,
++ 0x4020,
++ NV_PRAMDAC_VPLL_COEFF,
++ NV_RAMDAC_VPLL2 };
++ uint32_t reg1, pll1, pll2 = 0;
++ struct pll_lims pll_lim;
++ int ret;
++
++ if (dev_priv->card_type < NV_40)
++ reg1 = nv04_regs[plltype];
++ else
++ reg1 = nv40_regs[plltype];
++
++ pll1 = nvReadMC(dev, reg1);
++
++ if (reg1 <= 0x405c)
++ pll2 = nvReadMC(dev, reg1 + 4);
++ else if (nv_two_reg_pll(dev)) {
++ uint32_t reg2 = reg1 + (reg1 == NV_RAMDAC_VPLL2 ? 0x5c : 0x70);
++
++ pll2 = nvReadMC(dev, reg2);
++ }
++
++ if (dev_priv->card_type == 0x40 && reg1 >= NV_PRAMDAC_VPLL_COEFF) {
++ uint32_t ramdac580 = NVReadRAMDAC(dev, 0, NV_PRAMDAC_580);
++
++ /* check whether vpll has been forced into single stage mode */
++ if (reg1 == NV_PRAMDAC_VPLL_COEFF) {
++ if (ramdac580 & NV_RAMDAC_580_VPLL1_ACTIVE)
++ pll2 = 0;
++ } else
++ if (ramdac580 & NV_RAMDAC_580_VPLL2_ACTIVE)
++ pll2 = 0;
++ }
++
++ nouveau_hw_decode_pll(dev, reg1, pll1, pll2, pllvals);
++
++ ret = get_pll_limits(dev, plltype, &pll_lim);
++ if (ret)
++ return ret;
++
++ pllvals->refclk = pll_lim.refclk;
++
++ return 0;
++}
++
++int
++nouveau_hw_pllvals_to_clk(struct nouveau_pll_vals *pv)
++{
++ /* Avoid divide by zero if called at an inappropriate time */
++ if (!pv->M1 || !pv->M2)
++ return 0;
++
++ return pv->N1 * pv->N2 * pv->refclk / (pv->M1 * pv->M2) >> pv->log2P;
++}
++
++int
++nouveau_hw_get_clock(struct drm_device *dev, enum pll_types plltype)
++{
++ struct nouveau_pll_vals pllvals;
++
++ if (plltype == MPLL && (dev->pci_device & 0x0ff0) == CHIPSET_NFORCE) {
++ uint32_t mpllP;
++
++ pci_read_config_dword(pci_get_bus_and_slot(0, 3), 0x6c, &mpllP);
++ if (!mpllP)
++ mpllP = 4;
++
++ return 400000 / mpllP;
++ } else
++ if (plltype == MPLL && (dev->pci_device & 0xff0) == CHIPSET_NFORCE2) {
++ uint32_t clock;
++
++ pci_read_config_dword(pci_get_bus_and_slot(0, 5), 0x4c, &clock);
++ return clock;
++ }
++
++ nouveau_hw_get_pllvals(dev, plltype, &pllvals);
++
++ return nouveau_hw_pllvals_to_clk(&pllvals);
++}
++
++static void
++nouveau_hw_fix_bad_vpll(struct drm_device *dev, int head)
++{
++ /* the vpll on an unused head can come up with a random value, way
++ * beyond the pll limits. for some reason this causes the chip to
++ * lock up when reading the dac palette regs, so set a valid pll here
++ * when such a condition detected. only seen on nv11 to date
++ */
++
++ struct pll_lims pll_lim;
++ struct nouveau_pll_vals pv;
++ uint32_t pllreg = head ? NV_RAMDAC_VPLL2 : NV_PRAMDAC_VPLL_COEFF;
++
++ if (get_pll_limits(dev, head ? VPLL2 : VPLL1, &pll_lim))
++ return;
++ nouveau_hw_get_pllvals(dev, head ? VPLL2 : VPLL1, &pv);
++
++ if (pv.M1 >= pll_lim.vco1.min_m && pv.M1 <= pll_lim.vco1.max_m &&
++ pv.N1 >= pll_lim.vco1.min_n && pv.N1 <= pll_lim.vco1.max_n &&
++ pv.log2P <= pll_lim.max_log2p)
++ return;
++
++ NV_WARN(dev, "VPLL %d outwith limits, attempting to fix\n", head + 1);
++
++ /* set lowest clock within static limits */
++ pv.M1 = pll_lim.vco1.max_m;
++ pv.N1 = pll_lim.vco1.min_n;
++ pv.log2P = pll_lim.max_usable_log2p;
++ nouveau_hw_setpll(dev, pllreg, &pv);
++}
++
++/*
++ * vga font save/restore
++ */
++
++static void nouveau_vga_font_io(struct drm_device *dev,
++ void __iomem *iovram,
++ bool save, unsigned plane)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ unsigned i;
++
++ NVWriteVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX, 1 << plane);
++ NVWriteVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX, plane);
++ for (i = 0; i < 16384; i++) {
++ if (save) {
++ dev_priv->saved_vga_font[plane][i] =
++ ioread32_native(iovram + i * 4);
++ } else {
++ iowrite32_native(dev_priv->saved_vga_font[plane][i],
++ iovram + i * 4);
++ }
++ }
++}
++
++void
++nouveau_hw_save_vga_fonts(struct drm_device *dev, bool save)
++{
++ uint8_t misc, gr4, gr5, gr6, seq2, seq4;
++ bool graphicsmode;
++ unsigned plane;
++ void __iomem *iovram;
++
++ if (nv_two_heads(dev))
++ NVSetOwner(dev, 0);
++
++ NVSetEnablePalette(dev, 0, true);
++ graphicsmode = NVReadVgaAttr(dev, 0, NV_CIO_AR_MODE_INDEX) & 1;
++ NVSetEnablePalette(dev, 0, false);
++
++ if (graphicsmode) /* graphics mode => framebuffer => no need to save */
++ return;
++
++ NV_INFO(dev, "%sing VGA fonts\n", save ? "Sav" : "Restor");
++
++ /* map first 64KiB of VRAM, holds VGA fonts etc */
++ iovram = ioremap(pci_resource_start(dev->pdev, 1), 65536);
++ if (!iovram) {
++ NV_ERROR(dev, "Failed to map VRAM, "
++ "cannot save/restore VGA fonts.\n");
++ return;
++ }
++
++ if (nv_two_heads(dev))
++ NVBlankScreen(dev, 1, true);
++ NVBlankScreen(dev, 0, true);
++
++ /* save control regs */
++ misc = NVReadPRMVIO(dev, 0, NV_PRMVIO_MISC__READ);
++ seq2 = NVReadVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX);
++ seq4 = NVReadVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX);
++ gr4 = NVReadVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX);
++ gr5 = NVReadVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX);
++ gr6 = NVReadVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX);
++
++ NVWritePRMVIO(dev, 0, NV_PRMVIO_MISC__WRITE, 0x67);
++ NVWriteVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX, 0x6);
++ NVWriteVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX, 0x0);
++ NVWriteVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX, 0x5);
++
++ /* store font in planes 0..3 */
++ for (plane = 0; plane < 4; plane++)
++ nouveau_vga_font_io(dev, iovram, save, plane);
++
++ /* restore control regs */
++ NVWritePRMVIO(dev, 0, NV_PRMVIO_MISC__WRITE, misc);
++ NVWriteVgaGr(dev, 0, NV_VIO_GX_READ_MAP_INDEX, gr4);
++ NVWriteVgaGr(dev, 0, NV_VIO_GX_MODE_INDEX, gr5);
++ NVWriteVgaGr(dev, 0, NV_VIO_GX_MISC_INDEX, gr6);
++ NVWriteVgaSeq(dev, 0, NV_VIO_SR_PLANE_MASK_INDEX, seq2);
++ NVWriteVgaSeq(dev, 0, NV_VIO_SR_MEM_MODE_INDEX, seq4);
++
++ if (nv_two_heads(dev))
++ NVBlankScreen(dev, 1, false);
++ NVBlankScreen(dev, 0, false);
++
++ iounmap(iovram);
++}
++
++/*
++ * mode state save/load
++ */
++
++static void
++rd_cio_state(struct drm_device *dev, int head,
++ struct nv04_crtc_reg *crtcstate, int index)
++{
++ crtcstate->CRTC[index] = NVReadVgaCrtc(dev, head, index);
++}
++
++static void
++wr_cio_state(struct drm_device *dev, int head,
++ struct nv04_crtc_reg *crtcstate, int index)
++{
++ NVWriteVgaCrtc(dev, head, index, crtcstate->CRTC[index]);
++}
++
++static void
++nv_save_state_ramdac(struct drm_device *dev, int head,
++ struct nv04_mode_state *state)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv04_crtc_reg *regp = &state->crtc_reg[head];
++ int i;
++
++ if (dev_priv->card_type >= NV_10)
++ regp->nv10_cursync = NVReadRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC);
++
++ nouveau_hw_get_pllvals(dev, head ? VPLL2 : VPLL1, &regp->pllvals);
++ state->pllsel = NVReadRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT);
++ if (nv_two_heads(dev))
++ state->sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK);
++ if (dev_priv->chipset == 0x11)
++ regp->dither = NVReadRAMDAC(dev, head, NV_RAMDAC_DITHER_NV11);
++
++ regp->ramdac_gen_ctrl = NVReadRAMDAC(dev, head, NV_PRAMDAC_GENERAL_CONTROL);
++
++ if (nv_gf4_disp_arch(dev))
++ regp->ramdac_630 = NVReadRAMDAC(dev, head, NV_PRAMDAC_630);
++ if (dev_priv->chipset >= 0x30)
++ regp->ramdac_634 = NVReadRAMDAC(dev, head, NV_PRAMDAC_634);
++
++ regp->tv_setup = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP);
++ regp->tv_vtotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VTOTAL);
++ regp->tv_vskew = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VSKEW);
++ regp->tv_vsync_delay = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_VSYNC_DELAY);
++ regp->tv_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HTOTAL);
++ regp->tv_hskew = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSKEW);
++ regp->tv_hsync_delay = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY);
++ regp->tv_hsync_delay2 = NVReadRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY2);
++
++ for (i = 0; i < 7; i++) {
++ uint32_t ramdac_reg = NV_PRAMDAC_FP_VDISPLAY_END + (i * 4);
++ regp->fp_vert_regs[i] = NVReadRAMDAC(dev, head, ramdac_reg);
++ regp->fp_horiz_regs[i] = NVReadRAMDAC(dev, head, ramdac_reg + 0x20);
++ }
++
++ if (nv_gf4_disp_arch(dev)) {
++ regp->dither = NVReadRAMDAC(dev, head, NV_RAMDAC_FP_DITHER);
++ for (i = 0; i < 3; i++) {
++ regp->dither_regs[i] = NVReadRAMDAC(dev, head, NV_PRAMDAC_850 + i * 4);
++ regp->dither_regs[i + 3] = NVReadRAMDAC(dev, head, NV_PRAMDAC_85C + i * 4);
++ }
++ }
++
++ regp->fp_control = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL);
++ regp->fp_debug_0 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_0);
++ if (!nv_gf4_disp_arch(dev) && head == 0) {
++ /* early chips don't allow access to PRAMDAC_TMDS_* without
++ * the head A FPCLK on (nv11 even locks up) */
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_FP_DEBUG_0, regp->fp_debug_0 &
++ ~NV_PRAMDAC_FP_DEBUG_0_PWRDOWN_FPCLK);
++ }
++ regp->fp_debug_1 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1);
++ regp->fp_debug_2 = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_2);
++
++ regp->fp_margin_color = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_MARGIN_COLOR);
++
++ if (nv_gf4_disp_arch(dev))
++ regp->ramdac_8c0 = NVReadRAMDAC(dev, head, NV_PRAMDAC_8C0);
++
++ if (dev_priv->card_type == NV_40) {
++ regp->ramdac_a20 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A20);
++ regp->ramdac_a24 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A24);
++ regp->ramdac_a34 = NVReadRAMDAC(dev, head, NV_PRAMDAC_A34);
++
++ for (i = 0; i < 38; i++)
++ regp->ctv_regs[i] = NVReadRAMDAC(dev, head,
++ NV_PRAMDAC_CTV + 4*i);
++ }
++}
++
++static void
++nv_load_state_ramdac(struct drm_device *dev, int head,
++ struct nv04_mode_state *state)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv04_crtc_reg *regp = &state->crtc_reg[head];
++ uint32_t pllreg = head ? NV_RAMDAC_VPLL2 : NV_PRAMDAC_VPLL_COEFF;
++ int i;
++
++ if (dev_priv->card_type >= NV_10)
++ NVWriteRAMDAC(dev, head, NV_RAMDAC_NV10_CURSYNC, regp->nv10_cursync);
++
++ nouveau_hw_setpll(dev, pllreg, &regp->pllvals);
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel);
++ if (nv_two_heads(dev))
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, state->sel_clk);
++ if (dev_priv->chipset == 0x11)
++ NVWriteRAMDAC(dev, head, NV_RAMDAC_DITHER_NV11, regp->dither);
++
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_GENERAL_CONTROL, regp->ramdac_gen_ctrl);
++
++ if (nv_gf4_disp_arch(dev))
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_630, regp->ramdac_630);
++ if (dev_priv->chipset >= 0x30)
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_634, regp->ramdac_634);
++
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP, regp->tv_setup);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VTOTAL, regp->tv_vtotal);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VSKEW, regp->tv_vskew);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_VSYNC_DELAY, regp->tv_vsync_delay);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HTOTAL, regp->tv_htotal);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSKEW, regp->tv_hskew);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY, regp->tv_hsync_delay);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_HSYNC_DELAY2, regp->tv_hsync_delay2);
++
++ for (i = 0; i < 7; i++) {
++ uint32_t ramdac_reg = NV_PRAMDAC_FP_VDISPLAY_END + (i * 4);
++
++ NVWriteRAMDAC(dev, head, ramdac_reg, regp->fp_vert_regs[i]);
++ NVWriteRAMDAC(dev, head, ramdac_reg + 0x20, regp->fp_horiz_regs[i]);
++ }
++
++ if (nv_gf4_disp_arch(dev)) {
++ NVWriteRAMDAC(dev, head, NV_RAMDAC_FP_DITHER, regp->dither);
++ for (i = 0; i < 3; i++) {
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_850 + i * 4, regp->dither_regs[i]);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_85C + i * 4, regp->dither_regs[i + 3]);
++ }
++ }
++
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, regp->fp_control);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_0, regp->fp_debug_0);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regp->fp_debug_1);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_2, regp->fp_debug_2);
++
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_MARGIN_COLOR, regp->fp_margin_color);
++
++ if (nv_gf4_disp_arch(dev))
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_8C0, regp->ramdac_8c0);
++
++ if (dev_priv->card_type == NV_40) {
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_A20, regp->ramdac_a20);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_A24, regp->ramdac_a24);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_A34, regp->ramdac_a34);
++
++ for (i = 0; i < 38; i++)
++ NVWriteRAMDAC(dev, head,
++ NV_PRAMDAC_CTV + 4*i, regp->ctv_regs[i]);
++ }
++}
++
++static void
++nv_save_state_vga(struct drm_device *dev, int head,
++ struct nv04_mode_state *state)
++{
++ struct nv04_crtc_reg *regp = &state->crtc_reg[head];
++ int i;
++
++ regp->MiscOutReg = NVReadPRMVIO(dev, head, NV_PRMVIO_MISC__READ);
++
++ for (i = 0; i < 25; i++)
++ rd_cio_state(dev, head, regp, i);
++
++ NVSetEnablePalette(dev, head, true);
++ for (i = 0; i < 21; i++)
++ regp->Attribute[i] = NVReadVgaAttr(dev, head, i);
++ NVSetEnablePalette(dev, head, false);
++
++ for (i = 0; i < 9; i++)
++ regp->Graphics[i] = NVReadVgaGr(dev, head, i);
++
++ for (i = 0; i < 5; i++)
++ regp->Sequencer[i] = NVReadVgaSeq(dev, head, i);
++}
++
++static void
++nv_load_state_vga(struct drm_device *dev, int head,
++ struct nv04_mode_state *state)
++{
++ struct nv04_crtc_reg *regp = &state->crtc_reg[head];
++ int i;
++
++ NVWritePRMVIO(dev, head, NV_PRMVIO_MISC__WRITE, regp->MiscOutReg);
++
++ for (i = 0; i < 5; i++)
++ NVWriteVgaSeq(dev, head, i, regp->Sequencer[i]);
++
++ nv_lock_vga_crtc_base(dev, head, false);
++ for (i = 0; i < 25; i++)
++ wr_cio_state(dev, head, regp, i);
++ nv_lock_vga_crtc_base(dev, head, true);
++
++ for (i = 0; i < 9; i++)
++ NVWriteVgaGr(dev, head, i, regp->Graphics[i]);
++
++ NVSetEnablePalette(dev, head, true);
++ for (i = 0; i < 21; i++)
++ NVWriteVgaAttr(dev, head, i, regp->Attribute[i]);
++ NVSetEnablePalette(dev, head, false);
++}
++
++static void
++nv_save_state_ext(struct drm_device *dev, int head,
++ struct nv04_mode_state *state)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv04_crtc_reg *regp = &state->crtc_reg[head];
++ int i;
++
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_LCD__INDEX);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_RPC0_INDEX);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_RPC1_INDEX);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_LSR_INDEX);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_PIXEL_INDEX);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_HEB__INDEX);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_ENH_INDEX);
++
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_21);
++ if (dev_priv->card_type >= NV_30)
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_47);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_49);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_ILACE__INDEX);
++
++ if (dev_priv->card_type >= NV_10) {
++ regp->crtc_830 = NVReadCRTC(dev, head, NV_PCRTC_830);
++ regp->crtc_834 = NVReadCRTC(dev, head, NV_PCRTC_834);
++
++ if (dev_priv->card_type >= NV_30)
++ regp->gpio_ext = NVReadCRTC(dev, head, NV_PCRTC_GPIO_EXT);
++
++ if (dev_priv->card_type == NV_40)
++ regp->crtc_850 = NVReadCRTC(dev, head, NV_PCRTC_850);
++
++ if (nv_two_heads(dev))
++ regp->crtc_eng_ctrl = NVReadCRTC(dev, head, NV_PCRTC_ENGINE_CTRL);
++ regp->cursor_cfg = NVReadCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG);
++ }
++
++ regp->crtc_cfg = NVReadCRTC(dev, head, NV_PCRTC_CONFIG);
++
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH3__INDEX);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH4__INDEX);
++ if (dev_priv->card_type >= NV_10) {
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_EBR_INDEX);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_CSB);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_4B);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_TVOUT_LATENCY);
++ }
++ /* NV11 and NV20 don't have this, they stop at 0x52. */
++ if (nv_gf4_disp_arch(dev)) {
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_53);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_54);
++
++ for (i = 0; i < 0x10; i++)
++ regp->CR58[i] = NVReadVgaCrtc5758(dev, head, i);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_59);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_5B);
++
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_85);
++ rd_cio_state(dev, head, regp, NV_CIO_CRE_86);
++ }
++
++ regp->fb_start = NVReadCRTC(dev, head, NV_PCRTC_START);
++}
++
++static void
++nv_load_state_ext(struct drm_device *dev, int head,
++ struct nv04_mode_state *state)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv04_crtc_reg *regp = &state->crtc_reg[head];
++ uint32_t reg900;
++ int i;
++
++ if (dev_priv->card_type >= NV_10) {
++ if (nv_two_heads(dev))
++ /* setting ENGINE_CTRL (EC) *must* come before
++ * CIO_CRE_LCD, as writing CRE_LCD sets bits 16 & 17 in
++ * EC that should not be overwritten by writing stale EC
++ */
++ NVWriteCRTC(dev, head, NV_PCRTC_ENGINE_CTRL, regp->crtc_eng_ctrl);
++
++ nvWriteVIDEO(dev, NV_PVIDEO_STOP, 1);
++ nvWriteVIDEO(dev, NV_PVIDEO_INTR_EN, 0);
++ nvWriteVIDEO(dev, NV_PVIDEO_OFFSET_BUFF(0), 0);
++ nvWriteVIDEO(dev, NV_PVIDEO_OFFSET_BUFF(1), 0);
++ nvWriteVIDEO(dev, NV_PVIDEO_LIMIT(0), dev_priv->fb_available_size - 1);
++ nvWriteVIDEO(dev, NV_PVIDEO_LIMIT(1), dev_priv->fb_available_size - 1);
++ nvWriteVIDEO(dev, NV_PVIDEO_UVPLANE_LIMIT(0), dev_priv->fb_available_size - 1);
++ nvWriteVIDEO(dev, NV_PVIDEO_UVPLANE_LIMIT(1), dev_priv->fb_available_size - 1);
++ nvWriteMC(dev, NV_PBUS_POWERCTRL_2, 0);
++
++ NVWriteCRTC(dev, head, NV_PCRTC_CURSOR_CONFIG, regp->cursor_cfg);
++ NVWriteCRTC(dev, head, NV_PCRTC_830, regp->crtc_830);
++ NVWriteCRTC(dev, head, NV_PCRTC_834, regp->crtc_834);
++
++ if (dev_priv->card_type >= NV_30)
++ NVWriteCRTC(dev, head, NV_PCRTC_GPIO_EXT, regp->gpio_ext);
++
++ if (dev_priv->card_type == NV_40) {
++ NVWriteCRTC(dev, head, NV_PCRTC_850, regp->crtc_850);
++
++ reg900 = NVReadRAMDAC(dev, head, NV_PRAMDAC_900);
++ if (regp->crtc_cfg == NV_PCRTC_CONFIG_START_ADDRESS_HSYNC)
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_900, reg900 | 0x10000);
++ else
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_900, reg900 & ~0x10000);
++ }
++ }
++
++ NVWriteCRTC(dev, head, NV_PCRTC_CONFIG, regp->crtc_cfg);
++
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_RPC0_INDEX);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_RPC1_INDEX);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_LSR_INDEX);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_PIXEL_INDEX);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_LCD__INDEX);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_HEB__INDEX);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_ENH_INDEX);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_FF_INDEX);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_FFLWM__INDEX);
++ if (dev_priv->card_type >= NV_30)
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_47);
++
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_49);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX);
++ if (dev_priv->card_type == NV_40)
++ nv_fix_nv40_hw_cursor(dev, head);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_ILACE__INDEX);
++
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH3__INDEX);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_SCRATCH4__INDEX);
++ if (dev_priv->card_type >= NV_10) {
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_EBR_INDEX);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_CSB);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_4B);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_TVOUT_LATENCY);
++ }
++ /* NV11 and NV20 stop at 0x52. */
++ if (nv_gf4_disp_arch(dev)) {
++ if (dev_priv->card_type == NV_10) {
++ /* Not waiting for vertical retrace before modifying
++ CRE_53/CRE_54 causes lockups. */
++ nouveau_wait_until(dev, 650000000, NV_PRMCIO_INP0__COLOR, 0x8, 0x8);
++ nouveau_wait_until(dev, 650000000, NV_PRMCIO_INP0__COLOR, 0x8, 0x0);
++ }
++
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_53);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_54);
++
++ for (i = 0; i < 0x10; i++)
++ NVWriteVgaCrtc5758(dev, head, i, regp->CR58[i]);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_59);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_5B);
++
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_85);
++ wr_cio_state(dev, head, regp, NV_CIO_CRE_86);
++ }
++
++ NVWriteCRTC(dev, head, NV_PCRTC_START, regp->fb_start);
++
++ /* Setting 1 on this value gives you interrupts for every vblank period. */
++ NVWriteCRTC(dev, head, NV_PCRTC_INTR_EN_0, 0);
++ NVWriteCRTC(dev, head, NV_PCRTC_INTR_0, NV_PCRTC_INTR_0_VBLANK);
++}
++
++static void
++nv_save_state_palette(struct drm_device *dev, int head,
++ struct nv04_mode_state *state)
++{
++ int head_offset = head * NV_PRMDIO_SIZE, i;
++
++ nv_wr08(dev, NV_PRMDIO_PIXEL_MASK + head_offset,
++ NV_PRMDIO_PIXEL_MASK_MASK);
++ nv_wr08(dev, NV_PRMDIO_READ_MODE_ADDRESS + head_offset, 0x0);
++
++ for (i = 0; i < 768; i++) {
++ state->crtc_reg[head].DAC[i] = nv_rd08(dev,
++ NV_PRMDIO_PALETTE_DATA + head_offset);
++ }
++
++ NVSetEnablePalette(dev, head, false);
++}
++
++void
++nouveau_hw_load_state_palette(struct drm_device *dev, int head,
++ struct nv04_mode_state *state)
++{
++ int head_offset = head * NV_PRMDIO_SIZE, i;
++
++ nv_wr08(dev, NV_PRMDIO_PIXEL_MASK + head_offset,
++ NV_PRMDIO_PIXEL_MASK_MASK);
++ nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS + head_offset, 0x0);
++
++ for (i = 0; i < 768; i++) {
++ nv_wr08(dev, NV_PRMDIO_PALETTE_DATA + head_offset,
++ state->crtc_reg[head].DAC[i]);
++ }
++
++ NVSetEnablePalette(dev, head, false);
++}
++
++void nouveau_hw_save_state(struct drm_device *dev, int head,
++ struct nv04_mode_state *state)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ if (dev_priv->chipset == 0x11)
++ /* NB: no attempt is made to restore the bad pll later on */
++ nouveau_hw_fix_bad_vpll(dev, head);
++ nv_save_state_ramdac(dev, head, state);
++ nv_save_state_vga(dev, head, state);
++ nv_save_state_palette(dev, head, state);
++ nv_save_state_ext(dev, head, state);
++}
++
++void nouveau_hw_load_state(struct drm_device *dev, int head,
++ struct nv04_mode_state *state)
++{
++ NVVgaProtect(dev, head, true);
++ nv_load_state_ramdac(dev, head, state);
++ nv_load_state_ext(dev, head, state);
++ nouveau_hw_load_state_palette(dev, head, state);
++ nv_load_state_vga(dev, head, state);
++ NVVgaProtect(dev, head, false);
++}
+diff --git a/drivers/gpu/drm/nouveau/nouveau_hw.h b/drivers/gpu/drm/nouveau/nouveau_hw.h
+new file mode 100644
+index 0000000..869130f
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_hw.h
+@@ -0,0 +1,455 @@
++/*
++ * Copyright 2008 Stuart Bennett
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
++ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ */
++
++#ifndef __NOUVEAU_HW_H__
++#define __NOUVEAU_HW_H__
++
++#include "drmP.h"
++#include "nouveau_drv.h"
++
++#define MASK(field) ( \
++ (0xffffffff >> (31 - ((1 ? field) - (0 ? field)))) << (0 ? field))
++
++#define XLATE(src, srclowbit, outfield) ( \
++ (((src) >> (srclowbit)) << (0 ? outfield)) & MASK(outfield))
++
++void NVWriteVgaSeq(struct drm_device *, int head, uint8_t index, uint8_t value);
++uint8_t NVReadVgaSeq(struct drm_device *, int head, uint8_t index);
++void NVWriteVgaGr(struct drm_device *, int head, uint8_t index, uint8_t value);
++uint8_t NVReadVgaGr(struct drm_device *, int head, uint8_t index);
++void NVSetOwner(struct drm_device *, int owner);
++void NVBlankScreen(struct drm_device *, int head, bool blank);
++void nouveau_hw_setpll(struct drm_device *, uint32_t reg1,
++ struct nouveau_pll_vals *pv);
++int nouveau_hw_get_pllvals(struct drm_device *, enum pll_types plltype,
++ struct nouveau_pll_vals *pllvals);
++int nouveau_hw_pllvals_to_clk(struct nouveau_pll_vals *pllvals);
++int nouveau_hw_get_clock(struct drm_device *, enum pll_types plltype);
++void nouveau_hw_save_vga_fonts(struct drm_device *, bool save);
++void nouveau_hw_save_state(struct drm_device *, int head,
++ struct nv04_mode_state *state);
++void nouveau_hw_load_state(struct drm_device *, int head,
++ struct nv04_mode_state *state);
++void nouveau_hw_load_state_palette(struct drm_device *, int head,
++ struct nv04_mode_state *state);
++
++/* nouveau_calc.c */
++extern void nouveau_calc_arb(struct drm_device *, int vclk, int bpp,
++ int *burst, int *lwm);
++extern int nouveau_calc_pll_mnp(struct drm_device *, struct pll_lims *pll_lim,
++ int clk, struct nouveau_pll_vals *pv);
++
++static inline uint32_t
++nvReadMC(struct drm_device *dev, uint32_t reg)
++{
++ uint32_t val = nv_rd32(dev, reg);
++ NV_REG_DEBUG(MC, dev, "reg %08x val %08x\n", reg, val);
++ return val;
++}
++
++static inline void
++nvWriteMC(struct drm_device *dev, uint32_t reg, uint32_t val)
++{
++ NV_REG_DEBUG(MC, dev, "reg %08x val %08x\n", reg, val);
++ nv_wr32(dev, reg, val);
++}
++
++static inline uint32_t
++nvReadVIDEO(struct drm_device *dev, uint32_t reg)
++{
++ uint32_t val = nv_rd32(dev, reg);
++ NV_REG_DEBUG(VIDEO, dev, "reg %08x val %08x\n", reg, val);
++ return val;
++}
++
++static inline void
++nvWriteVIDEO(struct drm_device *dev, uint32_t reg, uint32_t val)
++{
++ NV_REG_DEBUG(VIDEO, dev, "reg %08x val %08x\n", reg, val);
++ nv_wr32(dev, reg, val);
++}
++
++static inline uint32_t
++nvReadFB(struct drm_device *dev, uint32_t reg)
++{
++ uint32_t val = nv_rd32(dev, reg);
++ NV_REG_DEBUG(FB, dev, "reg %08x val %08x\n", reg, val);
++ return val;
++}
++
++static inline void
++nvWriteFB(struct drm_device *dev, uint32_t reg, uint32_t val)
++{
++ NV_REG_DEBUG(FB, dev, "reg %08x val %08x\n", reg, val);
++ nv_wr32(dev, reg, val);
++}
++
++static inline uint32_t
++nvReadEXTDEV(struct drm_device *dev, uint32_t reg)
++{
++ uint32_t val = nv_rd32(dev, reg);
++ NV_REG_DEBUG(EXTDEV, dev, "reg %08x val %08x\n", reg, val);
++ return val;
++}
++
++static inline void
++nvWriteEXTDEV(struct drm_device *dev, uint32_t reg, uint32_t val)
++{
++ NV_REG_DEBUG(EXTDEV, dev, "reg %08x val %08x\n", reg, val);
++ nv_wr32(dev, reg, val);
++}
++
++static inline uint32_t NVReadCRTC(struct drm_device *dev,
++ int head, uint32_t reg)
++{
++ uint32_t val;
++ if (head)
++ reg += NV_PCRTC0_SIZE;
++ val = nv_rd32(dev, reg);
++ NV_REG_DEBUG(CRTC, dev, "head %d reg %08x val %08x\n", head, reg, val);
++ return val;
++}
++
++static inline void NVWriteCRTC(struct drm_device *dev,
++ int head, uint32_t reg, uint32_t val)
++{
++ if (head)
++ reg += NV_PCRTC0_SIZE;
++ NV_REG_DEBUG(CRTC, dev, "head %d reg %08x val %08x\n", head, reg, val);
++ nv_wr32(dev, reg, val);
++}
++
++static inline uint32_t NVReadRAMDAC(struct drm_device *dev,
++ int head, uint32_t reg)
++{
++ uint32_t val;
++ if (head)
++ reg += NV_PRAMDAC0_SIZE;
++ val = nv_rd32(dev, reg);
++ NV_REG_DEBUG(RAMDAC, dev, "head %d reg %08x val %08x\n",
++ head, reg, val);
++ return val;
++}
++
++static inline void NVWriteRAMDAC(struct drm_device *dev,
++ int head, uint32_t reg, uint32_t val)
++{
++ if (head)
++ reg += NV_PRAMDAC0_SIZE;
++ NV_REG_DEBUG(RAMDAC, dev, "head %d reg %08x val %08x\n",
++ head, reg, val);
++ nv_wr32(dev, reg, val);
++}
++
++static inline uint8_t nv_read_tmds(struct drm_device *dev,
++ int or, int dl, uint8_t address)
++{
++ int ramdac = (or & OUTPUT_C) >> 2;
++
++ NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL + dl * 8,
++ NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | address);
++ return NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA + dl * 8);
++}
++
++static inline void nv_write_tmds(struct drm_device *dev,
++ int or, int dl, uint8_t address,
++ uint8_t data)
++{
++ int ramdac = (or & OUTPUT_C) >> 2;
++
++ NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA + dl * 8, data);
++ NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL + dl * 8, address);
++}
++
++static inline void NVWriteVgaCrtc(struct drm_device *dev,
++ int head, uint8_t index, uint8_t value)
++{
++ NV_REG_DEBUG(VGACRTC, dev, "head %d index 0x%02x data 0x%02x\n",
++ head, index, value);
++ nv_wr08(dev, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index);
++ nv_wr08(dev, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE, value);
++}
++
++static inline uint8_t NVReadVgaCrtc(struct drm_device *dev,
++ int head, uint8_t index)
++{
++ uint8_t val;
++ nv_wr08(dev, NV_PRMCIO_CRX__COLOR + head * NV_PRMCIO_SIZE, index);
++ val = nv_rd08(dev, NV_PRMCIO_CR__COLOR + head * NV_PRMCIO_SIZE);
++ NV_REG_DEBUG(VGACRTC, dev, "head %d index 0x%02x data 0x%02x\n",
++ head, index, val);
++ return val;
++}
++
++/* CR57 and CR58 are a fun pair of regs. CR57 provides an index (0-0xf) for CR58
++ * I suspect they in fact do nothing, but are merely a way to carry useful
++ * per-head variables around
++ *
++ * Known uses:
++ * CR57 CR58
++ * 0x00 index to the appropriate dcb entry (or 7f for inactive)
++ * 0x02 dcb entry's "or" value (or 00 for inactive)
++ * 0x03 bit0 set for dual link (LVDS, possibly elsewhere too)
++ * 0x08 or 0x09 pxclk in MHz
++ * 0x0f laptop panel info - low nibble for PEXTDEV_BOOT_0 strap
++ * high nibble for xlat strap value
++ */
++
++static inline void
++NVWriteVgaCrtc5758(struct drm_device *dev, int head, uint8_t index, uint8_t value)
++{
++ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_57, index);
++ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_58, value);
++}
++
++static inline uint8_t NVReadVgaCrtc5758(struct drm_device *dev, int head, uint8_t index)
++{
++ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_57, index);
++ return NVReadVgaCrtc(dev, head, NV_CIO_CRE_58);
++}
++
++static inline uint8_t NVReadPRMVIO(struct drm_device *dev,
++ int head, uint32_t reg)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint8_t val;
++
++ /* Only NV4x have two pvio ranges; other twoHeads cards MUST call
++ * NVSetOwner for the relevant head to be programmed */
++ if (head && dev_priv->card_type == NV_40)
++ reg += NV_PRMVIO_SIZE;
++
++ val = nv_rd08(dev, reg);
++ NV_REG_DEBUG(RMVIO, dev, "head %d reg %08x val %02x\n", head, reg, val);
++ return val;
++}
++
++static inline void NVWritePRMVIO(struct drm_device *dev,
++ int head, uint32_t reg, uint8_t value)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ /* Only NV4x have two pvio ranges; other twoHeads cards MUST call
++ * NVSetOwner for the relevant head to be programmed */
++ if (head && dev_priv->card_type == NV_40)
++ reg += NV_PRMVIO_SIZE;
++
++ NV_REG_DEBUG(RMVIO, dev, "head %d reg %08x val %02x\n",
++ head, reg, value);
++ nv_wr08(dev, reg, value);
++}
++
++static inline void NVSetEnablePalette(struct drm_device *dev, int head, bool enable)
++{
++ nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
++ nv_wr08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, enable ? 0 : 0x20);
++}
++
++static inline bool NVGetEnablePalette(struct drm_device *dev, int head)
++{
++ nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
++ return !(nv_rd08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE) & 0x20);
++}
++
++static inline void NVWriteVgaAttr(struct drm_device *dev,
++ int head, uint8_t index, uint8_t value)
++{
++ if (NVGetEnablePalette(dev, head))
++ index &= ~0x20;
++ else
++ index |= 0x20;
++
++ nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
++ NV_REG_DEBUG(VGAATTR, dev, "head %d index 0x%02x data 0x%02x\n",
++ head, index, value);
++ nv_wr08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index);
++ nv_wr08(dev, NV_PRMCIO_AR__WRITE + head * NV_PRMCIO_SIZE, value);
++}
++
++static inline uint8_t NVReadVgaAttr(struct drm_device *dev,
++ int head, uint8_t index)
++{
++ uint8_t val;
++ if (NVGetEnablePalette(dev, head))
++ index &= ~0x20;
++ else
++ index |= 0x20;
++
++ nv_rd08(dev, NV_PRMCIO_INP0__COLOR + head * NV_PRMCIO_SIZE);
++ nv_wr08(dev, NV_PRMCIO_ARX + head * NV_PRMCIO_SIZE, index);
++ val = nv_rd08(dev, NV_PRMCIO_AR__READ + head * NV_PRMCIO_SIZE);
++ NV_REG_DEBUG(VGAATTR, dev, "head %d index 0x%02x data 0x%02x\n",
++ head, index, val);
++ return val;
++}
++
++static inline void NVVgaSeqReset(struct drm_device *dev, int head, bool start)
++{
++ NVWriteVgaSeq(dev, head, NV_VIO_SR_RESET_INDEX, start ? 0x1 : 0x3);
++}
++
++static inline void NVVgaProtect(struct drm_device *dev, int head, bool protect)
++{
++ uint8_t seq1 = NVReadVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX);
++
++ if (protect) {
++ NVVgaSeqReset(dev, head, true);
++ NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 | 0x20);
++ } else {
++ /* Reenable sequencer, then turn on screen */
++ NVWriteVgaSeq(dev, head, NV_VIO_SR_CLOCK_INDEX, seq1 & ~0x20); /* reenable display */
++ NVVgaSeqReset(dev, head, false);
++ }
++ NVSetEnablePalette(dev, head, protect);
++}
++
++static inline bool
++nv_heads_tied(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ if (dev_priv->chipset == 0x11)
++ return !!(nvReadMC(dev, NV_PBUS_DEBUG_1) & (1 << 28));
++
++ return NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44) & 0x4;
++}
++
++/* makes cr0-7 on the specified head read-only */
++static inline bool
++nv_lock_vga_crtc_base(struct drm_device *dev, int head, bool lock)
++{
++ uint8_t cr11 = NVReadVgaCrtc(dev, head, NV_CIO_CR_VRE_INDEX);
++ bool waslocked = cr11 & 0x80;
++
++ if (lock)
++ cr11 |= 0x80;
++ else
++ cr11 &= ~0x80;
++ NVWriteVgaCrtc(dev, head, NV_CIO_CR_VRE_INDEX, cr11);
++
++ return waslocked;
++}
++
++static inline void
++nv_lock_vga_crtc_shadow(struct drm_device *dev, int head, int lock)
++{
++ /* shadow lock: connects 0x60?3d? regs to "real" 0x3d? regs
++ * bit7: unlocks HDT, HBS, HBE, HRS, HRE, HEB
++ * bit6: seems to have some effect on CR09 (double scan, VBS_9)
++ * bit5: unlocks HDE
++ * bit4: unlocks VDE
++ * bit3: unlocks VDT, OVL, VRS, ?VRE?, VBS, VBE, LSR, EBR
++ * bit2: same as bit 1 of 0x60?804
++ * bit0: same as bit 0 of 0x60?804
++ */
++
++ uint8_t cr21 = lock;
++
++ if (lock < 0)
++ /* 0xfa is generic "unlock all" mask */
++ cr21 = NVReadVgaCrtc(dev, head, NV_CIO_CRE_21) | 0xfa;
++
++ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_21, cr21);
++}
++
++/* renders the extended crtc regs (cr19+) on all crtcs impervious:
++ * immutable and unreadable
++ */
++static inline bool
++NVLockVgaCrtcs(struct drm_device *dev, bool lock)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ bool waslocked = !NVReadVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX);
++
++ NVWriteVgaCrtc(dev, 0, NV_CIO_SR_LOCK_INDEX,
++ lock ? NV_CIO_SR_LOCK_VALUE : NV_CIO_SR_UNLOCK_RW_VALUE);
++ /* NV11 has independently lockable extended crtcs, except when tied */
++ if (dev_priv->chipset == 0x11 && !nv_heads_tied(dev))
++ NVWriteVgaCrtc(dev, 1, NV_CIO_SR_LOCK_INDEX,
++ lock ? NV_CIO_SR_LOCK_VALUE :
++ NV_CIO_SR_UNLOCK_RW_VALUE);
++
++ return waslocked;
++}
++
++/* nv04 cursor max dimensions of 32x32 (A1R5G5B5) */
++#define NV04_CURSOR_SIZE 32
++/* limit nv10 cursors to 64x64 (ARGB8) (we could go to 64x255) */
++#define NV10_CURSOR_SIZE 64
++
++static inline int nv_cursor_width(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ return dev_priv->card_type >= NV_10 ? NV10_CURSOR_SIZE : NV04_CURSOR_SIZE;
++}
++
++static inline void
++nv_fix_nv40_hw_cursor(struct drm_device *dev, int head)
++{
++ /* on some nv40 (such as the "true" (in the NV_PFB_BOOT_0 sense) nv40,
++ * the gf6800gt) a hardware bug requires a write to PRAMDAC_CURSOR_POS
++ * for changes to the CRTC CURCTL regs to take effect, whether changing
++ * the pixmap location, or just showing/hiding the cursor
++ */
++ uint32_t curpos = NVReadRAMDAC(dev, head, NV_PRAMDAC_CU_START_POS);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_CU_START_POS, curpos);
++}
++
++static inline void
++nv_show_cursor(struct drm_device *dev, int head, bool show)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint8_t *curctl1 =
++ &dev_priv->mode_reg.crtc_reg[head].CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX];
++
++ if (show)
++ *curctl1 |= MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE);
++ else
++ *curctl1 &= ~MASK(NV_CIO_CRE_HCUR_ADDR1_ENABLE);
++ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_HCUR_ADDR1_INDEX, *curctl1);
++
++ if (dev_priv->card_type == NV_40)
++ nv_fix_nv40_hw_cursor(dev, head);
++}
++
++static inline uint32_t
++nv_pitch_align(struct drm_device *dev, uint32_t width, int bpp)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int mask;
++
++ if (bpp == 15)
++ bpp = 16;
++ if (bpp == 24)
++ bpp = 8;
++
++ /* Alignment requirements taken from the Haiku driver */
++ if (dev_priv->card_type == NV_04)
++ mask = 128 / bpp - 1;
++ else
++ mask = 512 / bpp - 1;
++
++ return (width + mask) & ~mask;
++}
++
++#endif /* __NOUVEAU_HW_H__ */
+diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.c b/drivers/gpu/drm/nouveau/nouveau_i2c.c
+new file mode 100644
+index 0000000..70e994d
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_i2c.c
+@@ -0,0 +1,269 @@
++/*
++ * Copyright 2009 Red Hat Inc.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ *
++ * Authors: Ben Skeggs
++ */
++
++#include "drmP.h"
++#include "nouveau_drv.h"
++#include "nouveau_i2c.h"
++#include "nouveau_hw.h"
++
++static void
++nv04_i2c_setscl(void *data, int state)
++{
++ struct nouveau_i2c_chan *i2c = data;
++ struct drm_device *dev = i2c->dev;
++ uint8_t val;
++
++ val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xd0) | (state ? 0x20 : 0);
++ NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01);
++}
++
++static void
++nv04_i2c_setsda(void *data, int state)
++{
++ struct nouveau_i2c_chan *i2c = data;
++ struct drm_device *dev = i2c->dev;
++ uint8_t val;
++
++ val = (NVReadVgaCrtc(dev, 0, i2c->wr) & 0xe0) | (state ? 0x10 : 0);
++ NVWriteVgaCrtc(dev, 0, i2c->wr, val | 0x01);
++}
++
++static int
++nv04_i2c_getscl(void *data)
++{
++ struct nouveau_i2c_chan *i2c = data;
++ struct drm_device *dev = i2c->dev;
++
++ return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 4);
++}
++
++static int
++nv04_i2c_getsda(void *data)
++{
++ struct nouveau_i2c_chan *i2c = data;
++ struct drm_device *dev = i2c->dev;
++
++ return !!(NVReadVgaCrtc(dev, 0, i2c->rd) & 8);
++}
++
++static void
++nv4e_i2c_setscl(void *data, int state)
++{
++ struct nouveau_i2c_chan *i2c = data;
++ struct drm_device *dev = i2c->dev;
++ uint8_t val;
++
++ val = (nv_rd32(dev, i2c->wr) & 0xd0) | (state ? 0x20 : 0);
++ nv_wr32(dev, i2c->wr, val | 0x01);
++}
++
++static void
++nv4e_i2c_setsda(void *data, int state)
++{
++ struct nouveau_i2c_chan *i2c = data;
++ struct drm_device *dev = i2c->dev;
++ uint8_t val;
++
++ val = (nv_rd32(dev, i2c->wr) & 0xe0) | (state ? 0x10 : 0);
++ nv_wr32(dev, i2c->wr, val | 0x01);
++}
++
++static int
++nv4e_i2c_getscl(void *data)
++{
++ struct nouveau_i2c_chan *i2c = data;
++ struct drm_device *dev = i2c->dev;
++
++ return !!((nv_rd32(dev, i2c->rd) >> 16) & 4);
++}
++
++static int
++nv4e_i2c_getsda(void *data)
++{
++ struct nouveau_i2c_chan *i2c = data;
++ struct drm_device *dev = i2c->dev;
++
++ return !!((nv_rd32(dev, i2c->rd) >> 16) & 8);
++}
++
++static int
++nv50_i2c_getscl(void *data)
++{
++ struct nouveau_i2c_chan *i2c = data;
++ struct drm_device *dev = i2c->dev;
++
++ return !!(nv_rd32(dev, i2c->rd) & 1);
++}
++
++
++static int
++nv50_i2c_getsda(void *data)
++{
++ struct nouveau_i2c_chan *i2c = data;
++ struct drm_device *dev = i2c->dev;
++
++ return !!(nv_rd32(dev, i2c->rd) & 2);
++}
++
++static void
++nv50_i2c_setscl(void *data, int state)
++{
++ struct nouveau_i2c_chan *i2c = data;
++ struct drm_device *dev = i2c->dev;
++
++ nv_wr32(dev, i2c->wr, 4 | (i2c->data ? 2 : 0) | (state ? 1 : 0));
++}
++
++static void
++nv50_i2c_setsda(void *data, int state)
++{
++ struct nouveau_i2c_chan *i2c = data;
++ struct drm_device *dev = i2c->dev;
++
++ nv_wr32(dev, i2c->wr,
++ (nv_rd32(dev, i2c->rd) & 1) | 4 | (state ? 2 : 0));
++ i2c->data = state;
++}
++
++static const uint32_t nv50_i2c_port[] = {
++ 0x00e138, 0x00e150, 0x00e168, 0x00e180,
++ 0x00e254, 0x00e274, 0x00e764, 0x00e780,
++ 0x00e79c, 0x00e7b8
++};
++#define NV50_I2C_PORTS ARRAY_SIZE(nv50_i2c_port)
++
++int
++nouveau_i2c_init(struct drm_device *dev, struct dcb_i2c_entry *entry, int index)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_i2c_chan *i2c;
++ int ret;
++
++ if (entry->chan)
++ return -EEXIST;
++
++ if (dev_priv->card_type == NV_50 && entry->read >= NV50_I2C_PORTS) {
++ NV_ERROR(dev, "unknown i2c port %d\n", entry->read);
++ return -EINVAL;
++ }
++
++ i2c = kzalloc(sizeof(*i2c), GFP_KERNEL);
++ if (i2c == NULL)
++ return -ENOMEM;
++
++ switch (entry->port_type) {
++ case 0:
++ i2c->algo.bit.setsda = nv04_i2c_setsda;
++ i2c->algo.bit.setscl = nv04_i2c_setscl;
++ i2c->algo.bit.getsda = nv04_i2c_getsda;
++ i2c->algo.bit.getscl = nv04_i2c_getscl;
++ i2c->rd = entry->read;
++ i2c->wr = entry->write;
++ break;
++ case 4:
++ i2c->algo.bit.setsda = nv4e_i2c_setsda;
++ i2c->algo.bit.setscl = nv4e_i2c_setscl;
++ i2c->algo.bit.getsda = nv4e_i2c_getsda;
++ i2c->algo.bit.getscl = nv4e_i2c_getscl;
++ i2c->rd = 0x600800 + entry->read;
++ i2c->wr = 0x600800 + entry->write;
++ break;
++ case 5:
++ i2c->algo.bit.setsda = nv50_i2c_setsda;
++ i2c->algo.bit.setscl = nv50_i2c_setscl;
++ i2c->algo.bit.getsda = nv50_i2c_getsda;
++ i2c->algo.bit.getscl = nv50_i2c_getscl;
++ i2c->rd = nv50_i2c_port[entry->read];
++ i2c->wr = i2c->rd;
++ break;
++ case 6:
++ i2c->rd = entry->read;
++ i2c->wr = entry->write;
++ break;
++ default:
++ NV_ERROR(dev, "DCB I2C port type %d unknown\n",
++ entry->port_type);
++ kfree(i2c);
++ return -EINVAL;
++ }
++
++ snprintf(i2c->adapter.name, sizeof(i2c->adapter.name),
++ "nouveau-%s-%d", pci_name(dev->pdev), index);
++ i2c->adapter.owner = THIS_MODULE;
++ i2c->adapter.dev.parent = &dev->pdev->dev;
++ i2c->dev = dev;
++ i2c_set_adapdata(&i2c->adapter, i2c);
++
++ if (entry->port_type < 6) {
++ i2c->adapter.algo_data = &i2c->algo.bit;
++ i2c->algo.bit.udelay = 40;
++ i2c->algo.bit.timeout = usecs_to_jiffies(5000);
++ i2c->algo.bit.data = i2c;
++ ret = i2c_bit_add_bus(&i2c->adapter);
++ } else {
++ i2c->adapter.algo_data = &i2c->algo.dp;
++ i2c->algo.dp.running = false;
++ i2c->algo.dp.address = 0;
++ i2c->algo.dp.aux_ch = nouveau_dp_i2c_aux_ch;
++ ret = i2c_dp_aux_add_bus(&i2c->adapter);
++ }
++
++ if (ret) {
++ NV_ERROR(dev, "Failed to register i2c %d\n", index);
++ kfree(i2c);
++ return ret;
++ }
++
++ entry->chan = i2c;
++ return 0;
++}
++
++void
++nouveau_i2c_fini(struct drm_device *dev, struct dcb_i2c_entry *entry)
++{
++ if (!entry->chan)
++ return;
++
++ i2c_del_adapter(&entry->chan->adapter);
++ kfree(entry->chan);
++ entry->chan = NULL;
++}
++
++struct nouveau_i2c_chan *
++nouveau_i2c_find(struct drm_device *dev, int index)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nvbios *bios = &dev_priv->VBIOS;
++
++ if (index > DCB_MAX_NUM_I2C_ENTRIES)
++ return NULL;
++
++ if (!bios->bdcb.dcb.i2c[index].chan) {
++ if (nouveau_i2c_init(dev, &bios->bdcb.dcb.i2c[index], index))
++ return NULL;
++ }
++
++ return bios->bdcb.dcb.i2c[index].chan;
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nouveau_i2c.h b/drivers/gpu/drm/nouveau/nouveau_i2c.h
+new file mode 100644
+index 0000000..c8eaf7a
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_i2c.h
+@@ -0,0 +1,52 @@
++/*
++ * Copyright 2009 Red Hat Inc.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef __NOUVEAU_I2C_H__
++#define __NOUVEAU_I2C_H__
++
++#include <linux/i2c.h>
++#include <linux/i2c-id.h>
++#include <linux/i2c-algo-bit.h>
++#include "drm_dp_helper.h"
++
++struct dcb_i2c_entry;
++
++struct nouveau_i2c_chan {
++ struct i2c_adapter adapter;
++ struct drm_device *dev;
++ union {
++ struct i2c_algo_bit_data bit;
++ struct i2c_algo_dp_aux_data dp;
++ } algo;
++ unsigned rd;
++ unsigned wr;
++ unsigned data;
++};
++
++int nouveau_i2c_init(struct drm_device *, struct dcb_i2c_entry *, int index);
++void nouveau_i2c_fini(struct drm_device *, struct dcb_i2c_entry *);
++struct nouveau_i2c_chan *nouveau_i2c_find(struct drm_device *, int index);
++
++int nouveau_dp_i2c_aux_ch(struct i2c_adapter *, int mode, uint8_t write_byte,
++ uint8_t *read_byte);
++
++#endif /* __NOUVEAU_I2C_H__ */
+diff --git a/drivers/gpu/drm/nouveau/nouveau_ioc32.c b/drivers/gpu/drm/nouveau/nouveau_ioc32.c
+new file mode 100644
+index 0000000..475ba81
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_ioc32.c
+@@ -0,0 +1,70 @@
++/**
++ * \file mga_ioc32.c
++ *
++ * 32-bit ioctl compatibility routines for the MGA DRM.
++ *
++ * \author Dave Airlie <airlied@linux.ie> with code from patches by Egbert Eich
++ *
++ *
++ * Copyright (C) Paul Mackerras 2005
++ * Copyright (C) Egbert Eich 2003,2004
++ * Copyright (C) Dave Airlie 2005
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
++ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
++ * IN THE SOFTWARE.
++ */
++
++#include <linux/compat.h>
++
++#include "drmP.h"
++#include "drm.h"
++
++#include "nouveau_drv.h"
++
++/**
++ * Called whenever a 32-bit process running under a 64-bit kernel
++ * performs an ioctl on /dev/dri/card<n>.
++ *
++ * \param filp file pointer.
++ * \param cmd command.
++ * \param arg user argument.
++ * \return zero on success or negative number on failure.
++ */
++long nouveau_compat_ioctl(struct file *filp, unsigned int cmd,
++ unsigned long arg)
++{
++ unsigned int nr = DRM_IOCTL_NR(cmd);
++ drm_ioctl_compat_t *fn = NULL;
++ int ret;
++
++ if (nr < DRM_COMMAND_BASE)
++ return drm_compat_ioctl(filp, cmd, arg);
++
++#if 0
++ if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(mga_compat_ioctls))
++ fn = nouveau_compat_ioctls[nr - DRM_COMMAND_BASE];
++#endif
++ if (fn != NULL)
++ ret = (*fn)(filp, cmd, arg);
++ else
++ ret = drm_ioctl(filp, cmd, arg);
++
++ return ret;
++}
+diff --git a/drivers/gpu/drm/nouveau/nouveau_irq.c b/drivers/gpu/drm/nouveau/nouveau_irq.c
+new file mode 100644
+index 0000000..447f9f6
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_irq.c
+@@ -0,0 +1,737 @@
++/*
++ * Copyright (C) 2006 Ben Skeggs.
++ *
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++/*
++ * Authors:
++ * Ben Skeggs <darktama@iinet.net.au>
++ */
++
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drm.h"
++#include "nouveau_drv.h"
++#include "nouveau_reg.h"
++#include <linux/ratelimit.h>
++
++/* needed for hotplug irq */
++#include "nouveau_connector.h"
++#include "nv50_display.h"
++
++void
++nouveau_irq_preinstall(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ /* Master disable */
++ nv_wr32(dev, NV03_PMC_INTR_EN_0, 0);
++
++ if (dev_priv->card_type == NV_50) {
++ INIT_WORK(&dev_priv->irq_work, nv50_display_irq_handler_bh);
++ INIT_LIST_HEAD(&dev_priv->vbl_waiting);
++ }
++}
++
++int
++nouveau_irq_postinstall(struct drm_device *dev)
++{
++ /* Master enable */
++ nv_wr32(dev, NV03_PMC_INTR_EN_0, NV_PMC_INTR_EN_0_MASTER_ENABLE);
++ return 0;
++}
++
++void
++nouveau_irq_uninstall(struct drm_device *dev)
++{
++ /* Master disable */
++ nv_wr32(dev, NV03_PMC_INTR_EN_0, 0);
++}
++
++static int
++nouveau_call_method(struct nouveau_channel *chan, int class, int mthd, int data)
++{
++ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
++ struct nouveau_pgraph_object_method *grm;
++ struct nouveau_pgraph_object_class *grc;
++
++ grc = dev_priv->engine.graph.grclass;
++ while (grc->id) {
++ if (grc->id == class)
++ break;
++ grc++;
++ }
++
++ if (grc->id != class || !grc->methods)
++ return -ENOENT;
++
++ grm = grc->methods;
++ while (grm->id) {
++ if (grm->id == mthd)
++ return grm->exec(chan, class, mthd, data);
++ grm++;
++ }
++
++ return -ENOENT;
++}
++
++static bool
++nouveau_fifo_swmthd(struct nouveau_channel *chan, uint32_t addr, uint32_t data)
++{
++ struct drm_device *dev = chan->dev;
++ const int subc = (addr >> 13) & 0x7;
++ const int mthd = addr & 0x1ffc;
++
++ if (mthd == 0x0000) {
++ struct nouveau_gpuobj_ref *ref = NULL;
++
++ if (nouveau_gpuobj_ref_find(chan, data, &ref))
++ return false;
++
++ if (ref->gpuobj->engine != NVOBJ_ENGINE_SW)
++ return false;
++
++ chan->sw_subchannel[subc] = ref->gpuobj->class;
++ nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_rd32(dev,
++ NV04_PFIFO_CACHE1_ENGINE) & ~(0xf << subc * 4));
++ return true;
++ }
++
++ /* hw object */
++ if (nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE) & (1 << (subc*4)))
++ return false;
++
++ if (nouveau_call_method(chan, chan->sw_subchannel[subc], mthd, data))
++ return false;
++
++ return true;
++}
++
++static void
++nouveau_fifo_irq_handler(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_engine *engine = &dev_priv->engine;
++ uint32_t status, reassign;
++ int cnt = 0;
++
++ reassign = nv_rd32(dev, NV03_PFIFO_CACHES) & 1;
++ while ((status = nv_rd32(dev, NV03_PFIFO_INTR_0)) && (cnt++ < 100)) {
++ struct nouveau_channel *chan = NULL;
++ uint32_t chid, get;
++
++ nv_wr32(dev, NV03_PFIFO_CACHES, 0);
++
++ chid = engine->fifo.channel_id(dev);
++ if (chid >= 0 && chid < engine->fifo.channels)
++ chan = dev_priv->fifos[chid];
++ get = nv_rd32(dev, NV03_PFIFO_CACHE1_GET);
++
++ if (status & NV_PFIFO_INTR_CACHE_ERROR) {
++ uint32_t mthd, data;
++ int ptr;
++
++ /* NV_PFIFO_CACHE1_GET actually goes to 0xffc before
++ * wrapping on my G80 chips, but CACHE1 isn't big
++ * enough for this much data.. Tests show that it
++ * wraps around to the start at GET=0x800.. No clue
++ * as to why..
++ */
++ ptr = (get & 0x7ff) >> 2;
++
++ if (dev_priv->card_type < NV_40) {
++ mthd = nv_rd32(dev,
++ NV04_PFIFO_CACHE1_METHOD(ptr));
++ data = nv_rd32(dev,
++ NV04_PFIFO_CACHE1_DATA(ptr));
++ } else {
++ mthd = nv_rd32(dev,
++ NV40_PFIFO_CACHE1_METHOD(ptr));
++ data = nv_rd32(dev,
++ NV40_PFIFO_CACHE1_DATA(ptr));
++ }
++
++ if (!chan || !nouveau_fifo_swmthd(chan, mthd, data)) {
++ NV_INFO(dev, "PFIFO_CACHE_ERROR - Ch %d/%d "
++ "Mthd 0x%04x Data 0x%08x\n",
++ chid, (mthd >> 13) & 7, mthd & 0x1ffc,
++ data);
++ }
++
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 0);
++ nv_wr32(dev, NV03_PFIFO_INTR_0,
++ NV_PFIFO_INTR_CACHE_ERROR);
++
++ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0,
++ nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH0) & ~1);
++ nv_wr32(dev, NV03_PFIFO_CACHE1_GET, get + 4);
++ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0,
++ nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH0) | 1);
++ nv_wr32(dev, NV04_PFIFO_CACHE1_HASH, 0);
++
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH,
++ nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUSH) | 1);
++ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1);
++
++ status &= ~NV_PFIFO_INTR_CACHE_ERROR;
++ }
++
++ if (status & NV_PFIFO_INTR_DMA_PUSHER) {
++ NV_INFO(dev, "PFIFO_DMA_PUSHER - Ch %d\n", chid);
++
++ status &= ~NV_PFIFO_INTR_DMA_PUSHER;
++ nv_wr32(dev, NV03_PFIFO_INTR_0,
++ NV_PFIFO_INTR_DMA_PUSHER);
++
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, 0x00000000);
++ if (nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT) != get)
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET,
++ get + 4);
++ }
++
++ if (status & NV_PFIFO_INTR_SEMAPHORE) {
++ uint32_t sem;
++
++ status &= ~NV_PFIFO_INTR_SEMAPHORE;
++ nv_wr32(dev, NV03_PFIFO_INTR_0,
++ NV_PFIFO_INTR_SEMAPHORE);
++
++ sem = nv_rd32(dev, NV10_PFIFO_CACHE1_SEMAPHORE);
++ nv_wr32(dev, NV10_PFIFO_CACHE1_SEMAPHORE, sem | 0x1);
++
++ nv_wr32(dev, NV03_PFIFO_CACHE1_GET, get + 4);
++ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1);
++ }
++
++ if (status) {
++ NV_INFO(dev, "PFIFO_INTR 0x%08x - Ch %d\n",
++ status, chid);
++ nv_wr32(dev, NV03_PFIFO_INTR_0, status);
++ status = 0;
++ }
++
++ nv_wr32(dev, NV03_PFIFO_CACHES, reassign);
++ }
++
++ if (status) {
++ NV_INFO(dev, "PFIFO still angry after %d spins, halt\n", cnt);
++ nv_wr32(dev, 0x2140, 0);
++ nv_wr32(dev, 0x140, 0);
++ }
++
++ nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PFIFO_PENDING);
++}
++
++struct nouveau_bitfield_names {
++ uint32_t mask;
++ const char *name;
++};
++
++static struct nouveau_bitfield_names nstatus_names[] =
++{
++ { NV04_PGRAPH_NSTATUS_STATE_IN_USE, "STATE_IN_USE" },
++ { NV04_PGRAPH_NSTATUS_INVALID_STATE, "INVALID_STATE" },
++ { NV04_PGRAPH_NSTATUS_BAD_ARGUMENT, "BAD_ARGUMENT" },
++ { NV04_PGRAPH_NSTATUS_PROTECTION_FAULT, "PROTECTION_FAULT" }
++};
++
++static struct nouveau_bitfield_names nstatus_names_nv10[] =
++{
++ { NV10_PGRAPH_NSTATUS_STATE_IN_USE, "STATE_IN_USE" },
++ { NV10_PGRAPH_NSTATUS_INVALID_STATE, "INVALID_STATE" },
++ { NV10_PGRAPH_NSTATUS_BAD_ARGUMENT, "BAD_ARGUMENT" },
++ { NV10_PGRAPH_NSTATUS_PROTECTION_FAULT, "PROTECTION_FAULT" }
++};
++
++static struct nouveau_bitfield_names nsource_names[] =
++{
++ { NV03_PGRAPH_NSOURCE_NOTIFICATION, "NOTIFICATION" },
++ { NV03_PGRAPH_NSOURCE_DATA_ERROR, "DATA_ERROR" },
++ { NV03_PGRAPH_NSOURCE_PROTECTION_ERROR, "PROTECTION_ERROR" },
++ { NV03_PGRAPH_NSOURCE_RANGE_EXCEPTION, "RANGE_EXCEPTION" },
++ { NV03_PGRAPH_NSOURCE_LIMIT_COLOR, "LIMIT_COLOR" },
++ { NV03_PGRAPH_NSOURCE_LIMIT_ZETA, "LIMIT_ZETA" },
++ { NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD, "ILLEGAL_MTHD" },
++ { NV03_PGRAPH_NSOURCE_DMA_R_PROTECTION, "DMA_R_PROTECTION" },
++ { NV03_PGRAPH_NSOURCE_DMA_W_PROTECTION, "DMA_W_PROTECTION" },
++ { NV03_PGRAPH_NSOURCE_FORMAT_EXCEPTION, "FORMAT_EXCEPTION" },
++ { NV03_PGRAPH_NSOURCE_PATCH_EXCEPTION, "PATCH_EXCEPTION" },
++ { NV03_PGRAPH_NSOURCE_STATE_INVALID, "STATE_INVALID" },
++ { NV03_PGRAPH_NSOURCE_DOUBLE_NOTIFY, "DOUBLE_NOTIFY" },
++ { NV03_PGRAPH_NSOURCE_NOTIFY_IN_USE, "NOTIFY_IN_USE" },
++ { NV03_PGRAPH_NSOURCE_METHOD_CNT, "METHOD_CNT" },
++ { NV03_PGRAPH_NSOURCE_BFR_NOTIFICATION, "BFR_NOTIFICATION" },
++ { NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION, "DMA_VTX_PROTECTION" },
++ { NV03_PGRAPH_NSOURCE_DMA_WIDTH_A, "DMA_WIDTH_A" },
++ { NV03_PGRAPH_NSOURCE_DMA_WIDTH_B, "DMA_WIDTH_B" },
++};
++
++static void
++nouveau_print_bitfield_names_(uint32_t value,
++ const struct nouveau_bitfield_names *namelist,
++ const int namelist_len)
++{
++ /*
++ * Caller must have already printed the KERN_* log level for us.
++ * Also the caller is responsible for adding the newline.
++ */
++ int i;
++ for (i = 0; i < namelist_len; ++i) {
++ uint32_t mask = namelist[i].mask;
++ if (value & mask) {
++ printk(" %s", namelist[i].name);
++ value &= ~mask;
++ }
++ }
++ if (value)
++ printk(" (unknown bits 0x%08x)", value);
++}
++#define nouveau_print_bitfield_names(val, namelist) \
++ nouveau_print_bitfield_names_((val), (namelist), ARRAY_SIZE(namelist))
++
++
++static int
++nouveau_graph_chid_from_grctx(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t inst;
++ int i;
++
++ if (dev_priv->card_type < NV_40)
++ return dev_priv->engine.fifo.channels;
++ else
++ if (dev_priv->card_type < NV_50) {
++ inst = (nv_rd32(dev, 0x40032c) & 0xfffff) << 4;
++
++ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
++ struct nouveau_channel *chan = dev_priv->fifos[i];
++
++ if (!chan || !chan->ramin_grctx)
++ continue;
++
++ if (inst == chan->ramin_grctx->instance)
++ break;
++ }
++ } else {
++ inst = (nv_rd32(dev, 0x40032c) & 0xfffff) << 12;
++
++ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
++ struct nouveau_channel *chan = dev_priv->fifos[i];
++
++ if (!chan || !chan->ramin)
++ continue;
++
++ if (inst == chan->ramin->instance)
++ break;
++ }
++ }
++
++
++ return i;
++}
++
++static int
++nouveau_graph_trapped_channel(struct drm_device *dev, int *channel_ret)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_engine *engine = &dev_priv->engine;
++ int channel;
++
++ if (dev_priv->card_type < NV_10)
++ channel = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 24) & 0xf;
++ else
++ if (dev_priv->card_type < NV_40)
++ channel = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 20) & 0x1f;
++ else
++ channel = nouveau_graph_chid_from_grctx(dev);
++
++ if (channel >= engine->fifo.channels || !dev_priv->fifos[channel]) {
++ NV_ERROR(dev, "AIII, invalid/inactive channel id %d\n", channel);
++ return -EINVAL;
++ }
++
++ *channel_ret = channel;
++ return 0;
++}
++
++struct nouveau_pgraph_trap {
++ int channel;
++ int class;
++ int subc, mthd, size;
++ uint32_t data, data2;
++ uint32_t nsource, nstatus;
++};
++
++static void
++nouveau_graph_trap_info(struct drm_device *dev,
++ struct nouveau_pgraph_trap *trap)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t address;
++
++ trap->nsource = trap->nstatus = 0;
++ if (dev_priv->card_type < NV_50) {
++ trap->nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE);
++ trap->nstatus = nv_rd32(dev, NV03_PGRAPH_NSTATUS);
++ }
++
++ if (nouveau_graph_trapped_channel(dev, &trap->channel))
++ trap->channel = -1;
++ address = nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR);
++
++ trap->mthd = address & 0x1FFC;
++ trap->data = nv_rd32(dev, NV04_PGRAPH_TRAPPED_DATA);
++ if (dev_priv->card_type < NV_10) {
++ trap->subc = (address >> 13) & 0x7;
++ } else {
++ trap->subc = (address >> 16) & 0x7;
++ trap->data2 = nv_rd32(dev, NV10_PGRAPH_TRAPPED_DATA_HIGH);
++ }
++
++ if (dev_priv->card_type < NV_10)
++ trap->class = nv_rd32(dev, 0x400180 + trap->subc*4) & 0xFF;
++ else if (dev_priv->card_type < NV_40)
++ trap->class = nv_rd32(dev, 0x400160 + trap->subc*4) & 0xFFF;
++ else if (dev_priv->card_type < NV_50)
++ trap->class = nv_rd32(dev, 0x400160 + trap->subc*4) & 0xFFFF;
++ else
++ trap->class = nv_rd32(dev, 0x400814);
++}
++
++static void
++nouveau_graph_dump_trap_info(struct drm_device *dev, const char *id,
++ struct nouveau_pgraph_trap *trap)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t nsource = trap->nsource, nstatus = trap->nstatus;
++
++ NV_INFO(dev, "%s - nSource:", id);
++ nouveau_print_bitfield_names(nsource, nsource_names);
++ printk(", nStatus:");
++ if (dev_priv->card_type < NV_10)
++ nouveau_print_bitfield_names(nstatus, nstatus_names);
++ else
++ nouveau_print_bitfield_names(nstatus, nstatus_names_nv10);
++ printk("\n");
++
++ NV_INFO(dev, "%s - Ch %d/%d Class 0x%04x Mthd 0x%04x "
++ "Data 0x%08x:0x%08x\n",
++ id, trap->channel, trap->subc,
++ trap->class, trap->mthd,
++ trap->data2, trap->data);
++}
++
++static int
++nouveau_pgraph_intr_swmthd(struct drm_device *dev,
++ struct nouveau_pgraph_trap *trap)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ if (trap->channel < 0 ||
++ trap->channel >= dev_priv->engine.fifo.channels ||
++ !dev_priv->fifos[trap->channel])
++ return -ENODEV;
++
++ return nouveau_call_method(dev_priv->fifos[trap->channel],
++ trap->class, trap->mthd, trap->data);
++}
++
++static inline void
++nouveau_pgraph_intr_notify(struct drm_device *dev, uint32_t nsource)
++{
++ struct nouveau_pgraph_trap trap;
++ int unhandled = 0;
++
++ nouveau_graph_trap_info(dev, &trap);
++
++ if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) {
++ if (nouveau_pgraph_intr_swmthd(dev, &trap))
++ unhandled = 1;
++ } else {
++ unhandled = 1;
++ }
++
++ if (unhandled)
++ nouveau_graph_dump_trap_info(dev, "PGRAPH_NOTIFY", &trap);
++}
++
++static DEFINE_RATELIMIT_STATE(nouveau_ratelimit_state, 3 * HZ, 20);
++
++static int nouveau_ratelimit(void)
++{
++ return __ratelimit(&nouveau_ratelimit_state);
++}
++
++
++static inline void
++nouveau_pgraph_intr_error(struct drm_device *dev, uint32_t nsource)
++{
++ struct nouveau_pgraph_trap trap;
++ int unhandled = 0;
++
++ nouveau_graph_trap_info(dev, &trap);
++ trap.nsource = nsource;
++
++ if (nsource & NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD) {
++ if (nouveau_pgraph_intr_swmthd(dev, &trap))
++ unhandled = 1;
++ } else if (nsource & NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION) {
++ uint32_t v = nv_rd32(dev, 0x402000);
++ nv_wr32(dev, 0x402000, v);
++
++ /* dump the error anyway for now: it's useful for
++ Gallium development */
++ unhandled = 1;
++ } else {
++ unhandled = 1;
++ }
++
++ if (unhandled && nouveau_ratelimit())
++ nouveau_graph_dump_trap_info(dev, "PGRAPH_ERROR", &trap);
++}
++
++static inline void
++nouveau_pgraph_intr_context_switch(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_engine *engine = &dev_priv->engine;
++ uint32_t chid;
++
++ chid = engine->fifo.channel_id(dev);
++ NV_DEBUG(dev, "PGRAPH context switch interrupt channel %x\n", chid);
++
++ switch (dev_priv->card_type) {
++ case NV_04:
++ nv04_graph_context_switch(dev);
++ break;
++ case NV_10:
++ nv10_graph_context_switch(dev);
++ break;
++ default:
++ NV_ERROR(dev, "Context switch not implemented\n");
++ break;
++ }
++}
++
++static void
++nouveau_pgraph_irq_handler(struct drm_device *dev)
++{
++ uint32_t status;
++
++ while ((status = nv_rd32(dev, NV03_PGRAPH_INTR))) {
++ uint32_t nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE);
++
++ if (status & NV_PGRAPH_INTR_NOTIFY) {
++ nouveau_pgraph_intr_notify(dev, nsource);
++
++ status &= ~NV_PGRAPH_INTR_NOTIFY;
++ nv_wr32(dev, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_NOTIFY);
++ }
++
++ if (status & NV_PGRAPH_INTR_ERROR) {
++ nouveau_pgraph_intr_error(dev, nsource);
++
++ status &= ~NV_PGRAPH_INTR_ERROR;
++ nv_wr32(dev, NV03_PGRAPH_INTR, NV_PGRAPH_INTR_ERROR);
++ }
++
++ if (status & NV_PGRAPH_INTR_CONTEXT_SWITCH) {
++ nouveau_pgraph_intr_context_switch(dev);
++
++ status &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH;
++ nv_wr32(dev, NV03_PGRAPH_INTR,
++ NV_PGRAPH_INTR_CONTEXT_SWITCH);
++ }
++
++ if (status) {
++ NV_INFO(dev, "Unhandled PGRAPH_INTR - 0x%08x\n", status);
++ nv_wr32(dev, NV03_PGRAPH_INTR, status);
++ }
++
++ if ((nv_rd32(dev, NV04_PGRAPH_FIFO) & (1 << 0)) == 0)
++ nv_wr32(dev, NV04_PGRAPH_FIFO, 1);
++ }
++
++ nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PGRAPH_PENDING);
++}
++
++static void
++nv50_pgraph_irq_handler(struct drm_device *dev)
++{
++ uint32_t status;
++
++ while ((status = nv_rd32(dev, NV03_PGRAPH_INTR))) {
++ uint32_t nsource = nv_rd32(dev, NV03_PGRAPH_NSOURCE);
++
++ if (status & 0x00000001) {
++ nouveau_pgraph_intr_notify(dev, nsource);
++ status &= ~0x00000001;
++ nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000001);
++ }
++
++ if (status & 0x00000010) {
++ nouveau_pgraph_intr_error(dev, nsource |
++ NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD);
++
++ status &= ~0x00000010;
++ nv_wr32(dev, NV03_PGRAPH_INTR, 0x00000010);
++ }
++
++ if (status & 0x00001000) {
++ nv_wr32(dev, 0x400500, 0x00000000);
++ nv_wr32(dev, NV03_PGRAPH_INTR,
++ NV_PGRAPH_INTR_CONTEXT_SWITCH);
++ nv_wr32(dev, NV40_PGRAPH_INTR_EN, nv_rd32(dev,
++ NV40_PGRAPH_INTR_EN) &
++ ~NV_PGRAPH_INTR_CONTEXT_SWITCH);
++ nv_wr32(dev, 0x400500, 0x00010001);
++
++ nv50_graph_context_switch(dev);
++
++ status &= ~NV_PGRAPH_INTR_CONTEXT_SWITCH;
++ }
++
++ if (status & 0x00100000) {
++ nouveau_pgraph_intr_error(dev, nsource |
++ NV03_PGRAPH_NSOURCE_DATA_ERROR);
++
++ status &= ~0x00100000;
++ nv_wr32(dev, NV03_PGRAPH_INTR, 0x00100000);
++ }
++
++ if (status & 0x00200000) {
++ int r;
++
++ nouveau_pgraph_intr_error(dev, nsource |
++ NV03_PGRAPH_NSOURCE_PROTECTION_ERROR);
++
++ NV_ERROR(dev, "magic set 1:\n");
++ for (r = 0x408900; r <= 0x408910; r += 4)
++ NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r,
++ nv_rd32(dev, r));
++ nv_wr32(dev, 0x408900,
++ nv_rd32(dev, 0x408904) | 0xc0000000);
++ for (r = 0x408e08; r <= 0x408e24; r += 4)
++ NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r,
++ nv_rd32(dev, r));
++ nv_wr32(dev, 0x408e08,
++ nv_rd32(dev, 0x408e08) | 0xc0000000);
++
++ NV_ERROR(dev, "magic set 2:\n");
++ for (r = 0x409900; r <= 0x409910; r += 4)
++ NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r,
++ nv_rd32(dev, r));
++ nv_wr32(dev, 0x409900,
++ nv_rd32(dev, 0x409904) | 0xc0000000);
++ for (r = 0x409e08; r <= 0x409e24; r += 4)
++ NV_ERROR(dev, "\t0x%08x: 0x%08x\n", r,
++ nv_rd32(dev, r));
++ nv_wr32(dev, 0x409e08,
++ nv_rd32(dev, 0x409e08) | 0xc0000000);
++
++ status &= ~0x00200000;
++ nv_wr32(dev, NV03_PGRAPH_NSOURCE, nsource);
++ nv_wr32(dev, NV03_PGRAPH_INTR, 0x00200000);
++ }
++
++ if (status) {
++ NV_INFO(dev, "Unhandled PGRAPH_INTR - 0x%08x\n",
++ status);
++ nv_wr32(dev, NV03_PGRAPH_INTR, status);
++ }
++
++ {
++ const int isb = (1 << 16) | (1 << 0);
++
++ if ((nv_rd32(dev, 0x400500) & isb) != isb)
++ nv_wr32(dev, 0x400500,
++ nv_rd32(dev, 0x400500) | isb);
++ }
++ }
++
++ nv_wr32(dev, NV03_PMC_INTR_0, NV_PMC_INTR_0_PGRAPH_PENDING);
++ nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) & ~(1 << 31));
++}
++
++static void
++nouveau_crtc_irq_handler(struct drm_device *dev, int crtc)
++{
++ if (crtc & 1)
++ nv_wr32(dev, NV_CRTC0_INTSTAT, NV_CRTC_INTR_VBLANK);
++
++ if (crtc & 2)
++ nv_wr32(dev, NV_CRTC1_INTSTAT, NV_CRTC_INTR_VBLANK);
++}
++
++irqreturn_t
++nouveau_irq_handler(DRM_IRQ_ARGS)
++{
++ struct drm_device *dev = (struct drm_device *)arg;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t status, fbdev_flags = 0;
++
++ status = nv_rd32(dev, NV03_PMC_INTR_0);
++ if (!status)
++ return IRQ_NONE;
++
++ if (dev_priv->fbdev_info) {
++ fbdev_flags = dev_priv->fbdev_info->flags;
++ dev_priv->fbdev_info->flags |= FBINFO_HWACCEL_DISABLED;
++ }
++
++ if (status & NV_PMC_INTR_0_PFIFO_PENDING) {
++ nouveau_fifo_irq_handler(dev);
++ status &= ~NV_PMC_INTR_0_PFIFO_PENDING;
++ }
++
++ if (status & NV_PMC_INTR_0_PGRAPH_PENDING) {
++ if (dev_priv->card_type >= NV_50)
++ nv50_pgraph_irq_handler(dev);
++ else
++ nouveau_pgraph_irq_handler(dev);
++
++ status &= ~NV_PMC_INTR_0_PGRAPH_PENDING;
++ }
++
++ if (status & NV_PMC_INTR_0_CRTCn_PENDING) {
++ nouveau_crtc_irq_handler(dev, (status>>24)&3);
++ status &= ~NV_PMC_INTR_0_CRTCn_PENDING;
++ }
++
++ if (status & (NV_PMC_INTR_0_NV50_DISPLAY_PENDING |
++ NV_PMC_INTR_0_NV50_I2C_PENDING)) {
++ nv50_display_irq_handler(dev);
++ status &= ~(NV_PMC_INTR_0_NV50_DISPLAY_PENDING |
++ NV_PMC_INTR_0_NV50_I2C_PENDING);
++ }
++
++ if (status)
++ NV_ERROR(dev, "Unhandled PMC INTR status bits 0x%08x\n", status);
++
++ if (dev_priv->fbdev_info)
++ dev_priv->fbdev_info->flags = fbdev_flags;
++
++ return IRQ_HANDLED;
++}
+diff --git a/drivers/gpu/drm/nouveau/nouveau_mem.c b/drivers/gpu/drm/nouveau/nouveau_mem.c
+new file mode 100644
+index 0000000..8f3a12f
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_mem.c
+@@ -0,0 +1,668 @@
++/*
++ * Copyright (C) The Weather Channel, Inc. 2002. All Rights Reserved.
++ * Copyright 2005 Stephane Marchesin
++ *
++ * The Weather Channel (TM) funded Tungsten Graphics to develop the
++ * initial release of the Radeon 8500 driver under the XFree86 license.
++ * This notice must be preserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ *
++ * Authors:
++ * Keith Whitwell <keith@tungstengraphics.com>
++ */
++
++
++#include "drmP.h"
++#include "drm.h"
++#include "drm_sarea.h"
++#include "nouveau_drv.h"
++
++static struct mem_block *
++split_block(struct mem_block *p, uint64_t start, uint64_t size,
++ struct drm_file *file_priv)
++{
++ /* Maybe cut off the start of an existing block */
++ if (start > p->start) {
++ struct mem_block *newblock =
++ kmalloc(sizeof(*newblock), GFP_KERNEL);
++ if (!newblock)
++ goto out;
++ newblock->start = start;
++ newblock->size = p->size - (start - p->start);
++ newblock->file_priv = NULL;
++ newblock->next = p->next;
++ newblock->prev = p;
++ p->next->prev = newblock;
++ p->next = newblock;
++ p->size -= newblock->size;
++ p = newblock;
++ }
++
++ /* Maybe cut off the end of an existing block */
++ if (size < p->size) {
++ struct mem_block *newblock =
++ kmalloc(sizeof(*newblock), GFP_KERNEL);
++ if (!newblock)
++ goto out;
++ newblock->start = start + size;
++ newblock->size = p->size - size;
++ newblock->file_priv = NULL;
++ newblock->next = p->next;
++ newblock->prev = p;
++ p->next->prev = newblock;
++ p->next = newblock;
++ p->size = size;
++ }
++
++out:
++ /* Our block is in the middle */
++ p->file_priv = file_priv;
++ return p;
++}
++
++struct mem_block *
++nouveau_mem_alloc_block(struct mem_block *heap, uint64_t size,
++ int align2, struct drm_file *file_priv, int tail)
++{
++ struct mem_block *p;
++ uint64_t mask = (1 << align2) - 1;
++
++ if (!heap)
++ return NULL;
++
++ if (tail) {
++ list_for_each_prev(p, heap) {
++ uint64_t start = ((p->start + p->size) - size) & ~mask;
++
++ if (p->file_priv == NULL && start >= p->start &&
++ start + size <= p->start + p->size)
++ return split_block(p, start, size, file_priv);
++ }
++ } else {
++ list_for_each(p, heap) {
++ uint64_t start = (p->start + mask) & ~mask;
++
++ if (p->file_priv == NULL &&
++ start + size <= p->start + p->size)
++ return split_block(p, start, size, file_priv);
++ }
++ }
++
++ return NULL;
++}
++
++void nouveau_mem_free_block(struct mem_block *p)
++{
++ p->file_priv = NULL;
++
++ /* Assumes a single contiguous range. Needs a special file_priv in
++ * 'heap' to stop it being subsumed.
++ */
++ if (p->next->file_priv == NULL) {
++ struct mem_block *q = p->next;
++ p->size += q->size;
++ p->next = q->next;
++ p->next->prev = p;
++ kfree(q);
++ }
++
++ if (p->prev->file_priv == NULL) {
++ struct mem_block *q = p->prev;
++ q->size += p->size;
++ q->next = p->next;
++ q->next->prev = q;
++ kfree(p);
++ }
++}
++
++/* Initialize. How to check for an uninitialized heap?
++ */
++int nouveau_mem_init_heap(struct mem_block **heap, uint64_t start,
++ uint64_t size)
++{
++ struct mem_block *blocks = kmalloc(sizeof(*blocks), GFP_KERNEL);
++
++ if (!blocks)
++ return -ENOMEM;
++
++ *heap = kmalloc(sizeof(**heap), GFP_KERNEL);
++ if (!*heap) {
++ kfree(blocks);
++ return -ENOMEM;
++ }
++
++ blocks->start = start;
++ blocks->size = size;
++ blocks->file_priv = NULL;
++ blocks->next = blocks->prev = *heap;
++
++ memset(*heap, 0, sizeof(**heap));
++ (*heap)->file_priv = (struct drm_file *) -1;
++ (*heap)->next = (*heap)->prev = blocks;
++ return 0;
++}
++
++/*
++ * Free all blocks associated with the releasing file_priv
++ */
++void nouveau_mem_release(struct drm_file *file_priv, struct mem_block *heap)
++{
++ struct mem_block *p;
++
++ if (!heap || !heap->next)
++ return;
++
++ list_for_each(p, heap) {
++ if (p->file_priv == file_priv)
++ p->file_priv = NULL;
++ }
++
++ /* Assumes a single contiguous range. Needs a special file_priv in
++ * 'heap' to stop it being subsumed.
++ */
++ list_for_each(p, heap) {
++ while ((p->file_priv == NULL) &&
++ (p->next->file_priv == NULL) &&
++ (p->next != heap)) {
++ struct mem_block *q = p->next;
++ p->size += q->size;
++ p->next = q->next;
++ p->next->prev = p;
++ kfree(q);
++ }
++ }
++}
++
++/*
++ * NV10-NV40 tiling helpers
++ */
++
++static void
++nv10_mem_set_region_tiling(struct drm_device *dev, int i, uint32_t addr,
++ uint32_t size, uint32_t pitch)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
++ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
++ struct nouveau_tile_reg *tile = &dev_priv->tile.reg[i];
++
++ tile->addr = addr;
++ tile->size = size;
++ tile->used = !!pitch;
++ nouveau_fence_unref((void **)&tile->fence);
++
++ if (!pfifo->cache_flush(dev))
++ return;
++
++ pfifo->reassign(dev, false);
++ pfifo->cache_flush(dev);
++ pfifo->cache_pull(dev, false);
++
++ nouveau_wait_for_idle(dev);
++
++ pgraph->set_region_tiling(dev, i, addr, size, pitch);
++ pfb->set_region_tiling(dev, i, addr, size, pitch);
++
++ pfifo->cache_pull(dev, true);
++ pfifo->reassign(dev, true);
++}
++
++struct nouveau_tile_reg *
++nv10_mem_set_tiling(struct drm_device *dev, uint32_t addr, uint32_t size,
++ uint32_t pitch)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
++ struct nouveau_tile_reg *tile = dev_priv->tile.reg, *found = NULL;
++ int i;
++
++ spin_lock(&dev_priv->tile.lock);
++
++ for (i = 0; i < pfb->num_tiles; i++) {
++ if (tile[i].used)
++ /* Tile region in use. */
++ continue;
++
++ if (tile[i].fence &&
++ !nouveau_fence_signalled(tile[i].fence, NULL))
++ /* Pending tile region. */
++ continue;
++
++ if (max(tile[i].addr, addr) <
++ min(tile[i].addr + tile[i].size, addr + size))
++ /* Kill an intersecting tile region. */
++ nv10_mem_set_region_tiling(dev, i, 0, 0, 0);
++
++ if (pitch && !found) {
++ /* Free tile region. */
++ nv10_mem_set_region_tiling(dev, i, addr, size, pitch);
++ found = &tile[i];
++ }
++ }
++
++ spin_unlock(&dev_priv->tile.lock);
++
++ return found;
++}
++
++void
++nv10_mem_expire_tiling(struct drm_device *dev, struct nouveau_tile_reg *tile,
++ struct nouveau_fence *fence)
++{
++ if (fence) {
++ /* Mark it as pending. */
++ tile->fence = fence;
++ nouveau_fence_ref(fence);
++ }
++
++ tile->used = false;
++}
++
++/*
++ * NV50 VM helpers
++ */
++int
++nv50_mem_vm_bind_linear(struct drm_device *dev, uint64_t virt, uint32_t size,
++ uint32_t flags, uint64_t phys)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj **pgt;
++ unsigned psz, pfl, pages;
++
++ if (virt >= dev_priv->vm_gart_base &&
++ (virt + size) < (dev_priv->vm_gart_base + dev_priv->vm_gart_size)) {
++ psz = 12;
++ pgt = &dev_priv->gart_info.sg_ctxdma;
++ pfl = 0x21;
++ virt -= dev_priv->vm_gart_base;
++ } else
++ if (virt >= dev_priv->vm_vram_base &&
++ (virt + size) < (dev_priv->vm_vram_base + dev_priv->vm_vram_size)) {
++ psz = 16;
++ pgt = dev_priv->vm_vram_pt;
++ pfl = 0x01;
++ virt -= dev_priv->vm_vram_base;
++ } else {
++ NV_ERROR(dev, "Invalid address: 0x%16llx-0x%16llx\n",
++ virt, virt + size - 1);
++ return -EINVAL;
++ }
++
++ pages = size >> psz;
++
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ if (flags & 0x80000000) {
++ while (pages--) {
++ struct nouveau_gpuobj *pt = pgt[virt >> 29];
++ unsigned pte = ((virt & 0x1fffffffULL) >> psz) << 1;
++
++ nv_wo32(dev, pt, pte++, 0x00000000);
++ nv_wo32(dev, pt, pte++, 0x00000000);
++
++ virt += (1 << psz);
++ }
++ } else {
++ while (pages--) {
++ struct nouveau_gpuobj *pt = pgt[virt >> 29];
++ unsigned pte = ((virt & 0x1fffffffULL) >> psz) << 1;
++ unsigned offset_h = upper_32_bits(phys) & 0xff;
++ unsigned offset_l = lower_32_bits(phys);
++
++ nv_wo32(dev, pt, pte++, offset_l | pfl);
++ nv_wo32(dev, pt, pte++, offset_h | flags);
++
++ phys += (1 << psz);
++ virt += (1 << psz);
++ }
++ }
++ dev_priv->engine.instmem.finish_access(dev);
++
++ nv_wr32(dev, 0x100c80, 0x00050001);
++ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
++ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
++ NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
++ return -EBUSY;
++ }
++
++ nv_wr32(dev, 0x100c80, 0x00000001);
++ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
++ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
++ NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
++ return -EBUSY;
++ }
++
++ return 0;
++}
++
++void
++nv50_mem_vm_unbind(struct drm_device *dev, uint64_t virt, uint32_t size)
++{
++ nv50_mem_vm_bind_linear(dev, virt, size, 0x80000000, 0);
++}
++
++/*
++ * Cleanup everything
++ */
++void nouveau_mem_takedown(struct mem_block **heap)
++{
++ struct mem_block *p;
++
++ if (!*heap)
++ return;
++
++ for (p = (*heap)->next; p != *heap;) {
++ struct mem_block *q = p;
++ p = p->next;
++ kfree(q);
++ }
++
++ kfree(*heap);
++ *heap = NULL;
++}
++
++void nouveau_mem_close(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ nouveau_bo_unpin(dev_priv->vga_ram);
++ nouveau_bo_ref(NULL, &dev_priv->vga_ram);
++
++ ttm_bo_device_release(&dev_priv->ttm.bdev);
++
++ nouveau_ttm_global_release(dev_priv);
++
++ if (drm_core_has_AGP(dev) && dev->agp &&
++ drm_core_check_feature(dev, DRIVER_MODESET)) {
++ struct drm_agp_mem *entry, *tempe;
++
++ /* Remove AGP resources, but leave dev->agp
++ intact until drv_cleanup is called. */
++ list_for_each_entry_safe(entry, tempe, &dev->agp->memory, head) {
++ if (entry->bound)
++ drm_unbind_agp(entry->memory);
++ drm_free_agp(entry->memory, entry->pages);
++ kfree(entry);
++ }
++ INIT_LIST_HEAD(&dev->agp->memory);
++
++ if (dev->agp->acquired)
++ drm_agp_release(dev);
++
++ dev->agp->acquired = 0;
++ dev->agp->enabled = 0;
++ }
++
++ if (dev_priv->fb_mtrr) {
++ drm_mtrr_del(dev_priv->fb_mtrr, drm_get_resource_start(dev, 1),
++ drm_get_resource_len(dev, 1), DRM_MTRR_WC);
++ dev_priv->fb_mtrr = 0;
++ }
++}
++
++/*XXX won't work on BSD because of pci_read_config_dword */
++static uint32_t
++nouveau_mem_fb_amount_igp(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct pci_dev *bridge;
++ uint32_t mem;
++
++ bridge = pci_get_bus_and_slot(0, PCI_DEVFN(0, 1));
++ if (!bridge) {
++ NV_ERROR(dev, "no bridge device\n");
++ return 0;
++ }
++
++ if (dev_priv->flags&NV_NFORCE) {
++ pci_read_config_dword(bridge, 0x7C, &mem);
++ return (uint64_t)(((mem >> 6) & 31) + 1)*1024*1024;
++ } else
++ if (dev_priv->flags&NV_NFORCE2) {
++ pci_read_config_dword(bridge, 0x84, &mem);
++ return (uint64_t)(((mem >> 4) & 127) + 1)*1024*1024;
++ }
++
++ NV_ERROR(dev, "impossible!\n");
++ return 0;
++}
++
++/* returns the amount of FB ram in bytes */
++uint64_t nouveau_mem_fb_amount(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t boot0;
++
++ switch (dev_priv->card_type) {
++ case NV_04:
++ boot0 = nv_rd32(dev, NV03_BOOT_0);
++ if (boot0 & 0x00000100)
++ return (((boot0 >> 12) & 0xf) * 2 + 2) * 1024 * 1024;
++
++ switch (boot0 & NV03_BOOT_0_RAM_AMOUNT) {
++ case NV04_BOOT_0_RAM_AMOUNT_32MB:
++ return 32 * 1024 * 1024;
++ case NV04_BOOT_0_RAM_AMOUNT_16MB:
++ return 16 * 1024 * 1024;
++ case NV04_BOOT_0_RAM_AMOUNT_8MB:
++ return 8 * 1024 * 1024;
++ case NV04_BOOT_0_RAM_AMOUNT_4MB:
++ return 4 * 1024 * 1024;
++ }
++ break;
++ case NV_10:
++ case NV_20:
++ case NV_30:
++ case NV_40:
++ case NV_50:
++ default:
++ if (dev_priv->flags & (NV_NFORCE | NV_NFORCE2)) {
++ return nouveau_mem_fb_amount_igp(dev);
++ } else {
++ uint64_t mem;
++ mem = (nv_rd32(dev, NV04_FIFO_DATA) &
++ NV10_FIFO_DATA_RAM_AMOUNT_MB_MASK) >>
++ NV10_FIFO_DATA_RAM_AMOUNT_MB_SHIFT;
++ return mem * 1024 * 1024;
++ }
++ break;
++ }
++
++ NV_ERROR(dev,
++ "Unable to detect video ram size. Please report your setup to "
++ DRIVER_EMAIL "\n");
++ return 0;
++}
++
++#if __OS_HAS_AGP
++static void nouveau_mem_reset_agp(struct drm_device *dev)
++{
++ uint32_t saved_pci_nv_1, saved_pci_nv_19, pmc_enable;
++
++ saved_pci_nv_1 = nv_rd32(dev, NV04_PBUS_PCI_NV_1);
++ saved_pci_nv_19 = nv_rd32(dev, NV04_PBUS_PCI_NV_19);
++
++ /* clear busmaster bit */
++ nv_wr32(dev, NV04_PBUS_PCI_NV_1, saved_pci_nv_1 & ~0x4);
++ /* clear SBA and AGP bits */
++ nv_wr32(dev, NV04_PBUS_PCI_NV_19, saved_pci_nv_19 & 0xfffff0ff);
++
++ /* power cycle pgraph, if enabled */
++ pmc_enable = nv_rd32(dev, NV03_PMC_ENABLE);
++ if (pmc_enable & NV_PMC_ENABLE_PGRAPH) {
++ nv_wr32(dev, NV03_PMC_ENABLE,
++ pmc_enable & ~NV_PMC_ENABLE_PGRAPH);
++ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
++ NV_PMC_ENABLE_PGRAPH);
++ }
++
++ /* and restore (gives effect of resetting AGP) */
++ nv_wr32(dev, NV04_PBUS_PCI_NV_19, saved_pci_nv_19);
++ nv_wr32(dev, NV04_PBUS_PCI_NV_1, saved_pci_nv_1);
++}
++#endif
++
++int
++nouveau_mem_init_agp(struct drm_device *dev)
++{
++#if __OS_HAS_AGP
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct drm_agp_info info;
++ struct drm_agp_mode mode;
++ int ret;
++
++ if (nouveau_noagp)
++ return 0;
++
++ nouveau_mem_reset_agp(dev);
++
++ if (!dev->agp->acquired) {
++ ret = drm_agp_acquire(dev);
++ if (ret) {
++ NV_ERROR(dev, "Unable to acquire AGP: %d\n", ret);
++ return ret;
++ }
++ }
++
++ ret = drm_agp_info(dev, &info);
++ if (ret) {
++ NV_ERROR(dev, "Unable to get AGP info: %d\n", ret);
++ return ret;
++ }
++
++ /* see agp.h for the AGPSTAT_* modes available */
++ mode.mode = info.mode;
++ ret = drm_agp_enable(dev, mode);
++ if (ret) {
++ NV_ERROR(dev, "Unable to enable AGP: %d\n", ret);
++ return ret;
++ }
++
++ dev_priv->gart_info.type = NOUVEAU_GART_AGP;
++ dev_priv->gart_info.aper_base = info.aperture_base;
++ dev_priv->gart_info.aper_size = info.aperture_size;
++#endif
++ return 0;
++}
++
++int
++nouveau_mem_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct ttm_bo_device *bdev = &dev_priv->ttm.bdev;
++ int ret, dma_bits = 32;
++
++ dev_priv->fb_phys = drm_get_resource_start(dev, 1);
++ dev_priv->gart_info.type = NOUVEAU_GART_NONE;
++
++ if (dev_priv->card_type >= NV_50 &&
++ pci_dma_supported(dev->pdev, DMA_BIT_MASK(40)))
++ dma_bits = 40;
++
++ ret = pci_set_dma_mask(dev->pdev, DMA_BIT_MASK(dma_bits));
++ if (ret) {
++ NV_ERROR(dev, "Error setting DMA mask: %d\n", ret);
++ return ret;
++ }
++
++ ret = nouveau_ttm_global_init(dev_priv);
++ if (ret)
++ return ret;
++
++ ret = ttm_bo_device_init(&dev_priv->ttm.bdev,
++ dev_priv->ttm.bo_global_ref.ref.object,
++ &nouveau_bo_driver, DRM_FILE_PAGE_OFFSET,
++ dma_bits <= 32 ? true : false);
++ if (ret) {
++ NV_ERROR(dev, "Error initialising bo driver: %d\n", ret);
++ return ret;
++ }
++
++ INIT_LIST_HEAD(&dev_priv->ttm.bo_list);
++ spin_lock_init(&dev_priv->ttm.bo_list_lock);
++ spin_lock_init(&dev_priv->tile.lock);
++
++ dev_priv->fb_available_size = nouveau_mem_fb_amount(dev);
++
++ dev_priv->fb_mappable_pages = dev_priv->fb_available_size;
++ if (dev_priv->fb_mappable_pages > drm_get_resource_len(dev, 1))
++ dev_priv->fb_mappable_pages = drm_get_resource_len(dev, 1);
++ dev_priv->fb_mappable_pages >>= PAGE_SHIFT;
++
++ NV_INFO(dev, "%d MiB VRAM\n", (int)(dev_priv->fb_available_size >> 20));
++
++ /* remove reserved space at end of vram from available amount */
++ dev_priv->fb_available_size -= dev_priv->ramin_rsvd_vram;
++ dev_priv->fb_aper_free = dev_priv->fb_available_size;
++
++ /* mappable vram */
++ ret = ttm_bo_init_mm(bdev, TTM_PL_VRAM,
++ dev_priv->fb_available_size >> PAGE_SHIFT);
++ if (ret) {
++ NV_ERROR(dev, "Failed VRAM mm init: %d\n", ret);
++ return ret;
++ }
++
++ ret = nouveau_bo_new(dev, NULL, 256*1024, 0, TTM_PL_FLAG_VRAM,
++ 0, 0, true, true, &dev_priv->vga_ram);
++ if (ret == 0)
++ ret = nouveau_bo_pin(dev_priv->vga_ram, TTM_PL_FLAG_VRAM);
++ if (ret) {
++ NV_WARN(dev, "failed to reserve VGA memory\n");
++ nouveau_bo_ref(NULL, &dev_priv->vga_ram);
++ }
++
++ /* GART */
++#if !defined(__powerpc__) && !defined(__ia64__)
++ if (drm_device_is_agp(dev) && dev->agp) {
++ ret = nouveau_mem_init_agp(dev);
++ if (ret)
++ NV_ERROR(dev, "Error initialising AGP: %d\n", ret);
++ }
++#endif
++
++ if (dev_priv->gart_info.type == NOUVEAU_GART_NONE) {
++ ret = nouveau_sgdma_init(dev);
++ if (ret) {
++ NV_ERROR(dev, "Error initialising PCI(E): %d\n", ret);
++ return ret;
++ }
++ }
++
++ NV_INFO(dev, "%d MiB GART (aperture)\n",
++ (int)(dev_priv->gart_info.aper_size >> 20));
++ dev_priv->gart_info.aper_free = dev_priv->gart_info.aper_size;
++
++ ret = ttm_bo_init_mm(bdev, TTM_PL_TT,
++ dev_priv->gart_info.aper_size >> PAGE_SHIFT);
++ if (ret) {
++ NV_ERROR(dev, "Failed TT mm init: %d\n", ret);
++ return ret;
++ }
++
++ dev_priv->fb_mtrr = drm_mtrr_add(drm_get_resource_start(dev, 1),
++ drm_get_resource_len(dev, 1),
++ DRM_MTRR_WC);
++
++ return 0;
++}
++
++
+diff --git a/drivers/gpu/drm/nouveau/nouveau_notifier.c b/drivers/gpu/drm/nouveau/nouveau_notifier.c
+new file mode 100644
+index 0000000..d99dc08
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_notifier.c
+@@ -0,0 +1,203 @@
++/*
++ * Copyright (C) 2007 Ben Skeggs.
++ *
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++
++int
++nouveau_notifier_init_channel(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct nouveau_bo *ntfy = NULL;
++ uint32_t flags;
++ int ret;
++
++ if (nouveau_vram_notify)
++ flags = TTM_PL_FLAG_VRAM;
++ else
++ flags = TTM_PL_FLAG_TT;
++
++ ret = nouveau_gem_new(dev, NULL, PAGE_SIZE, 0, flags,
++ 0, 0x0000, false, true, &ntfy);
++ if (ret)
++ return ret;
++
++ ret = nouveau_bo_pin(ntfy, flags);
++ if (ret)
++ goto out_err;
++
++ ret = nouveau_bo_map(ntfy);
++ if (ret)
++ goto out_err;
++
++ ret = nouveau_mem_init_heap(&chan->notifier_heap, 0, ntfy->bo.mem.size);
++ if (ret)
++ goto out_err;
++
++ chan->notifier_bo = ntfy;
++out_err:
++ if (ret) {
++ mutex_lock(&dev->struct_mutex);
++ drm_gem_object_unreference(ntfy->gem);
++ mutex_unlock(&dev->struct_mutex);
++ }
++
++ return ret;
++}
++
++void
++nouveau_notifier_takedown_channel(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++
++ if (!chan->notifier_bo)
++ return;
++
++ nouveau_bo_unmap(chan->notifier_bo);
++ mutex_lock(&dev->struct_mutex);
++ nouveau_bo_unpin(chan->notifier_bo);
++ drm_gem_object_unreference(chan->notifier_bo->gem);
++ mutex_unlock(&dev->struct_mutex);
++ nouveau_mem_takedown(&chan->notifier_heap);
++}
++
++static void
++nouveau_notifier_gpuobj_dtor(struct drm_device *dev,
++ struct nouveau_gpuobj *gpuobj)
++{
++ NV_DEBUG(dev, "\n");
++
++ if (gpuobj->priv)
++ nouveau_mem_free_block(gpuobj->priv);
++}
++
++int
++nouveau_notifier_alloc(struct nouveau_channel *chan, uint32_t handle,
++ int size, uint32_t *b_offset)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *nobj = NULL;
++ struct mem_block *mem;
++ uint32_t offset;
++ int target, ret;
++
++ if (!chan->notifier_heap) {
++ NV_ERROR(dev, "Channel %d doesn't have a notifier heap!\n",
++ chan->id);
++ return -EINVAL;
++ }
++
++ mem = nouveau_mem_alloc_block(chan->notifier_heap, size, 0,
++ (struct drm_file *)-2, 0);
++ if (!mem) {
++ NV_ERROR(dev, "Channel %d notifier block full\n", chan->id);
++ return -ENOMEM;
++ }
++
++ offset = chan->notifier_bo->bo.mem.mm_node->start << PAGE_SHIFT;
++ if (chan->notifier_bo->bo.mem.mem_type == TTM_PL_VRAM) {
++ target = NV_DMA_TARGET_VIDMEM;
++ } else
++ if (chan->notifier_bo->bo.mem.mem_type == TTM_PL_TT) {
++ if (dev_priv->gart_info.type == NOUVEAU_GART_SGDMA &&
++ dev_priv->card_type < NV_50) {
++ ret = nouveau_sgdma_get_page(dev, offset, &offset);
++ if (ret)
++ return ret;
++ target = NV_DMA_TARGET_PCI;
++ } else {
++ target = NV_DMA_TARGET_AGP;
++ if (dev_priv->card_type >= NV_50)
++ offset += dev_priv->vm_gart_base;
++ }
++ } else {
++ NV_ERROR(dev, "Bad DMA target, mem_type %d!\n",
++ chan->notifier_bo->bo.mem.mem_type);
++ return -EINVAL;
++ }
++ offset += mem->start;
++
++ ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY, offset,
++ mem->size, NV_DMA_ACCESS_RW, target,
++ &nobj);
++ if (ret) {
++ nouveau_mem_free_block(mem);
++ NV_ERROR(dev, "Error creating notifier ctxdma: %d\n", ret);
++ return ret;
++ }
++ nobj->dtor = nouveau_notifier_gpuobj_dtor;
++ nobj->priv = mem;
++
++ ret = nouveau_gpuobj_ref_add(dev, chan, handle, nobj, NULL);
++ if (ret) {
++ nouveau_gpuobj_del(dev, &nobj);
++ nouveau_mem_free_block(mem);
++ NV_ERROR(dev, "Error referencing notifier ctxdma: %d\n", ret);
++ return ret;
++ }
++
++ *b_offset = mem->start;
++ return 0;
++}
++
++int
++nouveau_notifier_offset(struct nouveau_gpuobj *nobj, uint32_t *poffset)
++{
++ if (!nobj || nobj->dtor != nouveau_notifier_gpuobj_dtor)
++ return -EINVAL;
++
++ if (poffset) {
++ struct mem_block *mem = nobj->priv;
++
++ if (*poffset >= mem->size)
++ return false;
++
++ *poffset += mem->start;
++ }
++
++ return 0;
++}
++
++int
++nouveau_ioctl_notifier_alloc(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_nouveau_notifierobj_alloc *na = data;
++ struct nouveau_channel *chan;
++ int ret;
++
++ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
++ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(na->channel, file_priv, chan);
++
++ ret = nouveau_notifier_alloc(chan, na->handle, na->size, &na->offset);
++ if (ret)
++ return ret;
++
++ return 0;
++}
+diff --git a/drivers/gpu/drm/nouveau/nouveau_object.c b/drivers/gpu/drm/nouveau/nouveau_object.c
+new file mode 100644
+index 0000000..e7c100b
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_object.c
+@@ -0,0 +1,1295 @@
++/*
++ * Copyright (C) 2006 Ben Skeggs.
++ *
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++/*
++ * Authors:
++ * Ben Skeggs <darktama@iinet.net.au>
++ */
++
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++#include "nouveau_drm.h"
++
++/* NVidia uses context objects to drive drawing operations.
++
++ Context objects can be selected into 8 subchannels in the FIFO,
++ and then used via DMA command buffers.
++
++ A context object is referenced by a user defined handle (CARD32). The HW
++ looks up graphics objects in a hash table in the instance RAM.
++
++ An entry in the hash table consists of 2 CARD32. The first CARD32 contains
++ the handle, the second one a bitfield, that contains the address of the
++ object in instance RAM.
++
++ The format of the second CARD32 seems to be:
++
++ NV4 to NV30:
++
++ 15: 0 instance_addr >> 4
++ 17:16 engine (here uses 1 = graphics)
++ 28:24 channel id (here uses 0)
++ 31 valid (use 1)
++
++ NV40:
++
++ 15: 0 instance_addr >> 4 (maybe 19-0)
++ 21:20 engine (here uses 1 = graphics)
++ I'm unsure about the other bits, but using 0 seems to work.
++
++ The key into the hash table depends on the object handle and channel id and
++ is given as:
++*/
++static uint32_t
++nouveau_ramht_hash_handle(struct drm_device *dev, int channel, uint32_t handle)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t hash = 0;
++ int i;
++
++ NV_DEBUG(dev, "ch%d handle=0x%08x\n", channel, handle);
++
++ for (i = 32; i > 0; i -= dev_priv->ramht_bits) {
++ hash ^= (handle & ((1 << dev_priv->ramht_bits) - 1));
++ handle >>= dev_priv->ramht_bits;
++ }
++
++ if (dev_priv->card_type < NV_50)
++ hash ^= channel << (dev_priv->ramht_bits - 4);
++ hash <<= 3;
++
++ NV_DEBUG(dev, "hash=0x%08x\n", hash);
++ return hash;
++}
++
++static int
++nouveau_ramht_entry_valid(struct drm_device *dev, struct nouveau_gpuobj *ramht,
++ uint32_t offset)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t ctx = nv_ro32(dev, ramht, (offset + 4)/4);
++
++ if (dev_priv->card_type < NV_40)
++ return ((ctx & NV_RAMHT_CONTEXT_VALID) != 0);
++ return (ctx != 0);
++}
++
++static int
++nouveau_ramht_insert(struct drm_device *dev, struct nouveau_gpuobj_ref *ref)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
++ struct nouveau_channel *chan = ref->channel;
++ struct nouveau_gpuobj *ramht = chan->ramht ? chan->ramht->gpuobj : NULL;
++ uint32_t ctx, co, ho;
++
++ if (!ramht) {
++ NV_ERROR(dev, "No hash table!\n");
++ return -EINVAL;
++ }
++
++ if (dev_priv->card_type < NV_40) {
++ ctx = NV_RAMHT_CONTEXT_VALID | (ref->instance >> 4) |
++ (chan->id << NV_RAMHT_CONTEXT_CHANNEL_SHIFT) |
++ (ref->gpuobj->engine << NV_RAMHT_CONTEXT_ENGINE_SHIFT);
++ } else
++ if (dev_priv->card_type < NV_50) {
++ ctx = (ref->instance >> 4) |
++ (chan->id << NV40_RAMHT_CONTEXT_CHANNEL_SHIFT) |
++ (ref->gpuobj->engine << NV40_RAMHT_CONTEXT_ENGINE_SHIFT);
++ } else {
++ if (ref->gpuobj->engine == NVOBJ_ENGINE_DISPLAY) {
++ ctx = (ref->instance << 10) | 2;
++ } else {
++ ctx = (ref->instance >> 4) |
++ ((ref->gpuobj->engine <<
++ NV40_RAMHT_CONTEXT_ENGINE_SHIFT));
++ }
++ }
++
++ instmem->prepare_access(dev, true);
++ co = ho = nouveau_ramht_hash_handle(dev, chan->id, ref->handle);
++ do {
++ if (!nouveau_ramht_entry_valid(dev, ramht, co)) {
++ NV_DEBUG(dev,
++ "insert ch%d 0x%08x: h=0x%08x, c=0x%08x\n",
++ chan->id, co, ref->handle, ctx);
++ nv_wo32(dev, ramht, (co + 0)/4, ref->handle);
++ nv_wo32(dev, ramht, (co + 4)/4, ctx);
++
++ list_add_tail(&ref->list, &chan->ramht_refs);
++ instmem->finish_access(dev);
++ return 0;
++ }
++ NV_DEBUG(dev, "collision ch%d 0x%08x: h=0x%08x\n",
++ chan->id, co, nv_ro32(dev, ramht, co/4));
++
++ co += 8;
++ if (co >= dev_priv->ramht_size)
++ co = 0;
++ } while (co != ho);
++ instmem->finish_access(dev);
++
++ NV_ERROR(dev, "RAMHT space exhausted. ch=%d\n", chan->id);
++ return -ENOMEM;
++}
++
++static void
++nouveau_ramht_remove(struct drm_device *dev, struct nouveau_gpuobj_ref *ref)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
++ struct nouveau_channel *chan = ref->channel;
++ struct nouveau_gpuobj *ramht = chan->ramht ? chan->ramht->gpuobj : NULL;
++ uint32_t co, ho;
++
++ if (!ramht) {
++ NV_ERROR(dev, "No hash table!\n");
++ return;
++ }
++
++ instmem->prepare_access(dev, true);
++ co = ho = nouveau_ramht_hash_handle(dev, chan->id, ref->handle);
++ do {
++ if (nouveau_ramht_entry_valid(dev, ramht, co) &&
++ (ref->handle == nv_ro32(dev, ramht, (co/4)))) {
++ NV_DEBUG(dev,
++ "remove ch%d 0x%08x: h=0x%08x, c=0x%08x\n",
++ chan->id, co, ref->handle,
++ nv_ro32(dev, ramht, (co + 4)));
++ nv_wo32(dev, ramht, (co + 0)/4, 0x00000000);
++ nv_wo32(dev, ramht, (co + 4)/4, 0x00000000);
++
++ list_del(&ref->list);
++ instmem->finish_access(dev);
++ return;
++ }
++
++ co += 8;
++ if (co >= dev_priv->ramht_size)
++ co = 0;
++ } while (co != ho);
++ list_del(&ref->list);
++ instmem->finish_access(dev);
++
++ NV_ERROR(dev, "RAMHT entry not found. ch=%d, handle=0x%08x\n",
++ chan->id, ref->handle);
++}
++
++int
++nouveau_gpuobj_new(struct drm_device *dev, struct nouveau_channel *chan,
++ uint32_t size, int align, uint32_t flags,
++ struct nouveau_gpuobj **gpuobj_ret)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_engine *engine = &dev_priv->engine;
++ struct nouveau_gpuobj *gpuobj;
++ struct mem_block *pramin = NULL;
++ int ret;
++
++ NV_DEBUG(dev, "ch%d size=%u align=%d flags=0x%08x\n",
++ chan ? chan->id : -1, size, align, flags);
++
++ if (!dev_priv || !gpuobj_ret || *gpuobj_ret != NULL)
++ return -EINVAL;
++
++ gpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL);
++ if (!gpuobj)
++ return -ENOMEM;
++ NV_DEBUG(dev, "gpuobj %p\n", gpuobj);
++ gpuobj->flags = flags;
++ gpuobj->im_channel = chan;
++
++ list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
++
++ /* Choose between global instmem heap, and per-channel private
++ * instmem heap. On <NV50 allow requests for private instmem
++ * to be satisfied from global heap if no per-channel area
++ * available.
++ */
++ if (chan) {
++ if (chan->ramin_heap) {
++ NV_DEBUG(dev, "private heap\n");
++ pramin = chan->ramin_heap;
++ } else
++ if (dev_priv->card_type < NV_50) {
++ NV_DEBUG(dev, "global heap fallback\n");
++ pramin = dev_priv->ramin_heap;
++ }
++ } else {
++ NV_DEBUG(dev, "global heap\n");
++ pramin = dev_priv->ramin_heap;
++ }
++
++ if (!pramin) {
++ NV_ERROR(dev, "No PRAMIN heap!\n");
++ return -EINVAL;
++ }
++
++ if (!chan) {
++ ret = engine->instmem.populate(dev, gpuobj, &size);
++ if (ret) {
++ nouveau_gpuobj_del(dev, &gpuobj);
++ return ret;
++ }
++ }
++
++ /* Allocate a chunk of the PRAMIN aperture */
++ gpuobj->im_pramin = nouveau_mem_alloc_block(pramin, size,
++ drm_order(align),
++ (struct drm_file *)-2, 0);
++ if (!gpuobj->im_pramin) {
++ nouveau_gpuobj_del(dev, &gpuobj);
++ return -ENOMEM;
++ }
++
++ if (!chan) {
++ ret = engine->instmem.bind(dev, gpuobj);
++ if (ret) {
++ nouveau_gpuobj_del(dev, &gpuobj);
++ return ret;
++ }
++ }
++
++ if (gpuobj->flags & NVOBJ_FLAG_ZERO_ALLOC) {
++ int i;
++
++ engine->instmem.prepare_access(dev, true);
++ for (i = 0; i < gpuobj->im_pramin->size; i += 4)
++ nv_wo32(dev, gpuobj, i/4, 0);
++ engine->instmem.finish_access(dev);
++ }
++
++ *gpuobj_ret = gpuobj;
++ return 0;
++}
++
++int
++nouveau_gpuobj_early_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ NV_DEBUG(dev, "\n");
++
++ INIT_LIST_HEAD(&dev_priv->gpuobj_list);
++
++ return 0;
++}
++
++int
++nouveau_gpuobj_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int ret;
++
++ NV_DEBUG(dev, "\n");
++
++ if (dev_priv->card_type < NV_50) {
++ ret = nouveau_gpuobj_new_fake(dev,
++ dev_priv->ramht_offset, ~0, dev_priv->ramht_size,
++ NVOBJ_FLAG_ZERO_ALLOC | NVOBJ_FLAG_ALLOW_NO_REFS,
++ &dev_priv->ramht, NULL);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++
++void
++nouveau_gpuobj_takedown(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ NV_DEBUG(dev, "\n");
++
++ nouveau_gpuobj_del(dev, &dev_priv->ramht);
++}
++
++void
++nouveau_gpuobj_late_takedown(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *gpuobj = NULL;
++ struct list_head *entry, *tmp;
++
++ NV_DEBUG(dev, "\n");
++
++ list_for_each_safe(entry, tmp, &dev_priv->gpuobj_list) {
++ gpuobj = list_entry(entry, struct nouveau_gpuobj, list);
++
++ NV_ERROR(dev, "gpuobj %p still exists at takedown, refs=%d\n",
++ gpuobj, gpuobj->refcount);
++ gpuobj->refcount = 0;
++ nouveau_gpuobj_del(dev, &gpuobj);
++ }
++}
++
++int
++nouveau_gpuobj_del(struct drm_device *dev, struct nouveau_gpuobj **pgpuobj)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_engine *engine = &dev_priv->engine;
++ struct nouveau_gpuobj *gpuobj;
++ int i;
++
++ NV_DEBUG(dev, "gpuobj %p\n", pgpuobj ? *pgpuobj : NULL);
++
++ if (!dev_priv || !pgpuobj || !(*pgpuobj))
++ return -EINVAL;
++ gpuobj = *pgpuobj;
++
++ if (gpuobj->refcount != 0) {
++ NV_ERROR(dev, "gpuobj refcount is %d\n", gpuobj->refcount);
++ return -EINVAL;
++ }
++
++ if (gpuobj->im_pramin && (gpuobj->flags & NVOBJ_FLAG_ZERO_FREE)) {
++ engine->instmem.prepare_access(dev, true);
++ for (i = 0; i < gpuobj->im_pramin->size; i += 4)
++ nv_wo32(dev, gpuobj, i/4, 0);
++ engine->instmem.finish_access(dev);
++ }
++
++ if (gpuobj->dtor)
++ gpuobj->dtor(dev, gpuobj);
++
++ if (gpuobj->im_backing && !(gpuobj->flags & NVOBJ_FLAG_FAKE))
++ engine->instmem.clear(dev, gpuobj);
++
++ if (gpuobj->im_pramin) {
++ if (gpuobj->flags & NVOBJ_FLAG_FAKE)
++ kfree(gpuobj->im_pramin);
++ else
++ nouveau_mem_free_block(gpuobj->im_pramin);
++ }
++
++ list_del(&gpuobj->list);
++
++ *pgpuobj = NULL;
++ kfree(gpuobj);
++ return 0;
++}
++
++static int
++nouveau_gpuobj_instance_get(struct drm_device *dev,
++ struct nouveau_channel *chan,
++ struct nouveau_gpuobj *gpuobj, uint32_t *inst)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *cpramin;
++
++ /* <NV50 use PRAMIN address everywhere */
++ if (dev_priv->card_type < NV_50) {
++ *inst = gpuobj->im_pramin->start;
++ return 0;
++ }
++
++ if (chan && gpuobj->im_channel != chan) {
++ NV_ERROR(dev, "Channel mismatch: obj %d, ref %d\n",
++ gpuobj->im_channel->id, chan->id);
++ return -EINVAL;
++ }
++
++ /* NV50 channel-local instance */
++ if (chan) {
++ cpramin = chan->ramin->gpuobj;
++ *inst = gpuobj->im_pramin->start - cpramin->im_pramin->start;
++ return 0;
++ }
++
++ /* NV50 global (VRAM) instance */
++ if (!gpuobj->im_channel) {
++ /* ...from global heap */
++ if (!gpuobj->im_backing) {
++ NV_ERROR(dev, "AII, no VRAM backing gpuobj\n");
++ return -EINVAL;
++ }
++ *inst = gpuobj->im_backing_start;
++ return 0;
++ } else {
++ /* ...from local heap */
++ cpramin = gpuobj->im_channel->ramin->gpuobj;
++ *inst = cpramin->im_backing_start +
++ (gpuobj->im_pramin->start - cpramin->im_pramin->start);
++ return 0;
++ }
++
++ return -EINVAL;
++}
++
++int
++nouveau_gpuobj_ref_add(struct drm_device *dev, struct nouveau_channel *chan,
++ uint32_t handle, struct nouveau_gpuobj *gpuobj,
++ struct nouveau_gpuobj_ref **ref_ret)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj_ref *ref;
++ uint32_t instance;
++ int ret;
++
++ NV_DEBUG(dev, "ch%d h=0x%08x gpuobj=%p\n",
++ chan ? chan->id : -1, handle, gpuobj);
++
++ if (!dev_priv || !gpuobj || (ref_ret && *ref_ret != NULL))
++ return -EINVAL;
++
++ if (!chan && !ref_ret)
++ return -EINVAL;
++
++ if (gpuobj->engine == NVOBJ_ENGINE_SW && !gpuobj->im_pramin) {
++ /* sw object */
++ instance = 0x40;
++ } else {
++ ret = nouveau_gpuobj_instance_get(dev, chan, gpuobj, &instance);
++ if (ret)
++ return ret;
++ }
++
++ ref = kzalloc(sizeof(*ref), GFP_KERNEL);
++ if (!ref)
++ return -ENOMEM;
++ INIT_LIST_HEAD(&ref->list);
++ ref->gpuobj = gpuobj;
++ ref->channel = chan;
++ ref->instance = instance;
++
++ if (!ref_ret) {
++ ref->handle = handle;
++
++ ret = nouveau_ramht_insert(dev, ref);
++ if (ret) {
++ kfree(ref);
++ return ret;
++ }
++ } else {
++ ref->handle = ~0;
++ *ref_ret = ref;
++ }
++
++ ref->gpuobj->refcount++;
++ return 0;
++}
++
++int nouveau_gpuobj_ref_del(struct drm_device *dev, struct nouveau_gpuobj_ref **pref)
++{
++ struct nouveau_gpuobj_ref *ref;
++
++ NV_DEBUG(dev, "ref %p\n", pref ? *pref : NULL);
++
++ if (!dev || !pref || *pref == NULL)
++ return -EINVAL;
++ ref = *pref;
++
++ if (ref->handle != ~0)
++ nouveau_ramht_remove(dev, ref);
++
++ if (ref->gpuobj) {
++ ref->gpuobj->refcount--;
++
++ if (ref->gpuobj->refcount == 0) {
++ if (!(ref->gpuobj->flags & NVOBJ_FLAG_ALLOW_NO_REFS))
++ nouveau_gpuobj_del(dev, &ref->gpuobj);
++ }
++ }
++
++ *pref = NULL;
++ kfree(ref);
++ return 0;
++}
++
++int
++nouveau_gpuobj_new_ref(struct drm_device *dev,
++ struct nouveau_channel *oc, struct nouveau_channel *rc,
++ uint32_t handle, uint32_t size, int align,
++ uint32_t flags, struct nouveau_gpuobj_ref **ref)
++{
++ struct nouveau_gpuobj *gpuobj = NULL;
++ int ret;
++
++ ret = nouveau_gpuobj_new(dev, oc, size, align, flags, &gpuobj);
++ if (ret)
++ return ret;
++
++ ret = nouveau_gpuobj_ref_add(dev, rc, handle, gpuobj, ref);
++ if (ret) {
++ nouveau_gpuobj_del(dev, &gpuobj);
++ return ret;
++ }
++
++ return 0;
++}
++
++int
++nouveau_gpuobj_ref_find(struct nouveau_channel *chan, uint32_t handle,
++ struct nouveau_gpuobj_ref **ref_ret)
++{
++ struct nouveau_gpuobj_ref *ref;
++ struct list_head *entry, *tmp;
++
++ list_for_each_safe(entry, tmp, &chan->ramht_refs) {
++ ref = list_entry(entry, struct nouveau_gpuobj_ref, list);
++
++ if (ref->handle == handle) {
++ if (ref_ret)
++ *ref_ret = ref;
++ return 0;
++ }
++ }
++
++ return -EINVAL;
++}
++
++int
++nouveau_gpuobj_new_fake(struct drm_device *dev, uint32_t p_offset,
++ uint32_t b_offset, uint32_t size,
++ uint32_t flags, struct nouveau_gpuobj **pgpuobj,
++ struct nouveau_gpuobj_ref **pref)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *gpuobj = NULL;
++ int i;
++
++ NV_DEBUG(dev,
++ "p_offset=0x%08x b_offset=0x%08x size=0x%08x flags=0x%08x\n",
++ p_offset, b_offset, size, flags);
++
++ gpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL);
++ if (!gpuobj)
++ return -ENOMEM;
++ NV_DEBUG(dev, "gpuobj %p\n", gpuobj);
++ gpuobj->im_channel = NULL;
++ gpuobj->flags = flags | NVOBJ_FLAG_FAKE;
++
++ list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
++
++ if (p_offset != ~0) {
++ gpuobj->im_pramin = kzalloc(sizeof(struct mem_block),
++ GFP_KERNEL);
++ if (!gpuobj->im_pramin) {
++ nouveau_gpuobj_del(dev, &gpuobj);
++ return -ENOMEM;
++ }
++ gpuobj->im_pramin->start = p_offset;
++ gpuobj->im_pramin->size = size;
++ }
++
++ if (b_offset != ~0) {
++ gpuobj->im_backing = (struct nouveau_bo *)-1;
++ gpuobj->im_backing_start = b_offset;
++ }
++
++ if (gpuobj->flags & NVOBJ_FLAG_ZERO_ALLOC) {
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ for (i = 0; i < gpuobj->im_pramin->size; i += 4)
++ nv_wo32(dev, gpuobj, i/4, 0);
++ dev_priv->engine.instmem.finish_access(dev);
++ }
++
++ if (pref) {
++ i = nouveau_gpuobj_ref_add(dev, NULL, 0, gpuobj, pref);
++ if (i) {
++ nouveau_gpuobj_del(dev, &gpuobj);
++ return i;
++ }
++ }
++
++ if (pgpuobj)
++ *pgpuobj = gpuobj;
++ return 0;
++}
++
++
++static uint32_t
++nouveau_gpuobj_class_instmem_size(struct drm_device *dev, int class)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ /*XXX: dodgy hack for now */
++ if (dev_priv->card_type >= NV_50)
++ return 24;
++ if (dev_priv->card_type >= NV_40)
++ return 32;
++ return 16;
++}
++
++/*
++ DMA objects are used to reference a piece of memory in the
++ framebuffer, PCI or AGP address space. Each object is 16 bytes big
++ and looks as follows:
++
++ entry[0]
++ 11:0 class (seems like I can always use 0 here)
++ 12 page table present?
++ 13 page entry linear?
++ 15:14 access: 0 rw, 1 ro, 2 wo
++ 17:16 target: 0 NV memory, 1 NV memory tiled, 2 PCI, 3 AGP
++ 31:20 dma adjust (bits 0-11 of the address)
++ entry[1]
++ dma limit (size of transfer)
++ entry[X]
++ 1 0 readonly, 1 readwrite
++ 31:12 dma frame address of the page (bits 12-31 of the address)
++ entry[N]
++ page table terminator, same value as the first pte, as does nvidia
++ rivatv uses 0xffffffff
++
++ Non linear page tables need a list of frame addresses afterwards,
++ the rivatv project has some info on this.
++
++ The method below creates a DMA object in instance RAM and returns a handle
++ to it that can be used to set up context objects.
++*/
++int
++nouveau_gpuobj_dma_new(struct nouveau_channel *chan, int class,
++ uint64_t offset, uint64_t size, int access,
++ int target, struct nouveau_gpuobj **gpuobj)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
++ int ret;
++
++ NV_DEBUG(dev, "ch%d class=0x%04x offset=0x%llx size=0x%llx\n",
++ chan->id, class, offset, size);
++ NV_DEBUG(dev, "access=%d target=%d\n", access, target);
++
++ switch (target) {
++ case NV_DMA_TARGET_AGP:
++ offset += dev_priv->gart_info.aper_base;
++ break;
++ default:
++ break;
++ }
++
++ ret = nouveau_gpuobj_new(dev, chan,
++ nouveau_gpuobj_class_instmem_size(dev, class),
++ 16, NVOBJ_FLAG_ZERO_ALLOC |
++ NVOBJ_FLAG_ZERO_FREE, gpuobj);
++ if (ret) {
++ NV_ERROR(dev, "Error creating gpuobj: %d\n", ret);
++ return ret;
++ }
++
++ instmem->prepare_access(dev, true);
++
++ if (dev_priv->card_type < NV_50) {
++ uint32_t frame, adjust, pte_flags = 0;
++
++ if (access != NV_DMA_ACCESS_RO)
++ pte_flags |= (1<<1);
++ adjust = offset & 0x00000fff;
++ frame = offset & ~0x00000fff;
++
++ nv_wo32(dev, *gpuobj, 0, ((1<<12) | (1<<13) |
++ (adjust << 20) |
++ (access << 14) |
++ (target << 16) |
++ class));
++ nv_wo32(dev, *gpuobj, 1, size - 1);
++ nv_wo32(dev, *gpuobj, 2, frame | pte_flags);
++ nv_wo32(dev, *gpuobj, 3, frame | pte_flags);
++ } else {
++ uint64_t limit = offset + size - 1;
++ uint32_t flags0, flags5;
++
++ if (target == NV_DMA_TARGET_VIDMEM) {
++ flags0 = 0x00190000;
++ flags5 = 0x00010000;
++ } else {
++ flags0 = 0x7fc00000;
++ flags5 = 0x00080000;
++ }
++
++ nv_wo32(dev, *gpuobj, 0, flags0 | class);
++ nv_wo32(dev, *gpuobj, 1, lower_32_bits(limit));
++ nv_wo32(dev, *gpuobj, 2, lower_32_bits(offset));
++ nv_wo32(dev, *gpuobj, 3, ((upper_32_bits(limit) & 0xff) << 24) |
++ (upper_32_bits(offset) & 0xff));
++ nv_wo32(dev, *gpuobj, 5, flags5);
++ }
++
++ instmem->finish_access(dev);
++
++ (*gpuobj)->engine = NVOBJ_ENGINE_SW;
++ (*gpuobj)->class = class;
++ return 0;
++}
++
++int
++nouveau_gpuobj_gart_dma_new(struct nouveau_channel *chan,
++ uint64_t offset, uint64_t size, int access,
++ struct nouveau_gpuobj **gpuobj,
++ uint32_t *o_ret)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int ret;
++
++ if (dev_priv->gart_info.type == NOUVEAU_GART_AGP ||
++ (dev_priv->card_type >= NV_50 &&
++ dev_priv->gart_info.type == NOUVEAU_GART_SGDMA)) {
++ ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
++ offset + dev_priv->vm_gart_base,
++ size, access, NV_DMA_TARGET_AGP,
++ gpuobj);
++ if (o_ret)
++ *o_ret = 0;
++ } else
++ if (dev_priv->gart_info.type == NOUVEAU_GART_SGDMA) {
++ *gpuobj = dev_priv->gart_info.sg_ctxdma;
++ if (offset & ~0xffffffffULL) {
++ NV_ERROR(dev, "obj offset exceeds 32-bits\n");
++ return -EINVAL;
++ }
++ if (o_ret)
++ *o_ret = (uint32_t)offset;
++ ret = (*gpuobj != NULL) ? 0 : -EINVAL;
++ } else {
++ NV_ERROR(dev, "Invalid GART type %d\n", dev_priv->gart_info.type);
++ return -EINVAL;
++ }
++
++ return ret;
++}
++
++/* Context objects in the instance RAM have the following structure.
++ * On NV40 they are 32 byte long, on NV30 and smaller 16 bytes.
++
++ NV4 - NV30:
++
++ entry[0]
++ 11:0 class
++ 12 chroma key enable
++ 13 user clip enable
++ 14 swizzle enable
++ 17:15 patch config:
++ scrcopy_and, rop_and, blend_and, scrcopy, srccopy_pre, blend_pre
++ 18 synchronize enable
++ 19 endian: 1 big, 0 little
++ 21:20 dither mode
++ 23 single step enable
++ 24 patch status: 0 invalid, 1 valid
++ 25 context_surface 0: 1 valid
++ 26 context surface 1: 1 valid
++ 27 context pattern: 1 valid
++ 28 context rop: 1 valid
++ 29,30 context beta, beta4
++ entry[1]
++ 7:0 mono format
++ 15:8 color format
++ 31:16 notify instance address
++ entry[2]
++ 15:0 dma 0 instance address
++ 31:16 dma 1 instance address
++ entry[3]
++ dma method traps
++
++ NV40:
++ No idea what the exact format is. Here's what can be deducted:
++
++ entry[0]:
++ 11:0 class (maybe uses more bits here?)
++ 17 user clip enable
++ 21:19 patch config
++ 25 patch status valid ?
++ entry[1]:
++ 15:0 DMA notifier (maybe 20:0)
++ entry[2]:
++ 15:0 DMA 0 instance (maybe 20:0)
++ 24 big endian
++ entry[3]:
++ 15:0 DMA 1 instance (maybe 20:0)
++ entry[4]:
++ entry[5]:
++ set to 0?
++*/
++int
++nouveau_gpuobj_gr_new(struct nouveau_channel *chan, int class,
++ struct nouveau_gpuobj **gpuobj)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int ret;
++
++ NV_DEBUG(dev, "ch%d class=0x%04x\n", chan->id, class);
++
++ ret = nouveau_gpuobj_new(dev, chan,
++ nouveau_gpuobj_class_instmem_size(dev, class),
++ 16,
++ NVOBJ_FLAG_ZERO_ALLOC | NVOBJ_FLAG_ZERO_FREE,
++ gpuobj);
++ if (ret) {
++ NV_ERROR(dev, "Error creating gpuobj: %d\n", ret);
++ return ret;
++ }
++
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ if (dev_priv->card_type >= NV_50) {
++ nv_wo32(dev, *gpuobj, 0, class);
++ nv_wo32(dev, *gpuobj, 5, 0x00010000);
++ } else {
++ switch (class) {
++ case NV_CLASS_NULL:
++ nv_wo32(dev, *gpuobj, 0, 0x00001030);
++ nv_wo32(dev, *gpuobj, 1, 0xFFFFFFFF);
++ break;
++ default:
++ if (dev_priv->card_type >= NV_40) {
++ nv_wo32(dev, *gpuobj, 0, class);
++#ifdef __BIG_ENDIAN
++ nv_wo32(dev, *gpuobj, 2, 0x01000000);
++#endif
++ } else {
++#ifdef __BIG_ENDIAN
++ nv_wo32(dev, *gpuobj, 0, class | 0x00080000);
++#else
++ nv_wo32(dev, *gpuobj, 0, class);
++#endif
++ }
++ }
++ }
++ dev_priv->engine.instmem.finish_access(dev);
++
++ (*gpuobj)->engine = NVOBJ_ENGINE_GR;
++ (*gpuobj)->class = class;
++ return 0;
++}
++
++int
++nouveau_gpuobj_sw_new(struct nouveau_channel *chan, int class,
++ struct nouveau_gpuobj **gpuobj_ret)
++{
++ struct drm_nouveau_private *dev_priv;
++ struct nouveau_gpuobj *gpuobj;
++
++ if (!chan || !gpuobj_ret || *gpuobj_ret != NULL)
++ return -EINVAL;
++ dev_priv = chan->dev->dev_private;
++
++ gpuobj = kzalloc(sizeof(*gpuobj), GFP_KERNEL);
++ if (!gpuobj)
++ return -ENOMEM;
++ gpuobj->engine = NVOBJ_ENGINE_SW;
++ gpuobj->class = class;
++
++ list_add_tail(&gpuobj->list, &dev_priv->gpuobj_list);
++ *gpuobj_ret = gpuobj;
++ return 0;
++}
++
++static int
++nouveau_gpuobj_channel_init_pramin(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *pramin = NULL;
++ uint32_t size;
++ uint32_t base;
++ int ret;
++
++ NV_DEBUG(dev, "ch%d\n", chan->id);
++
++ /* Base amount for object storage (4KiB enough?) */
++ size = 0x1000;
++ base = 0;
++
++ /* PGRAPH context */
++
++ if (dev_priv->card_type == NV_50) {
++ /* Various fixed table thingos */
++ size += 0x1400; /* mostly unknown stuff */
++ size += 0x4000; /* vm pd */
++ base = 0x6000;
++ /* RAMHT, not sure about setting size yet, 32KiB to be safe */
++ size += 0x8000;
++ /* RAMFC */
++ size += 0x1000;
++ /* PGRAPH context */
++ size += 0x70000;
++ }
++
++ NV_DEBUG(dev, "ch%d PRAMIN size: 0x%08x bytes, base alloc=0x%08x\n",
++ chan->id, size, base);
++ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, size, 0x1000, 0,
++ &chan->ramin);
++ if (ret) {
++ NV_ERROR(dev, "Error allocating channel PRAMIN: %d\n", ret);
++ return ret;
++ }
++ pramin = chan->ramin->gpuobj;
++
++ ret = nouveau_mem_init_heap(&chan->ramin_heap,
++ pramin->im_pramin->start + base, size);
++ if (ret) {
++ NV_ERROR(dev, "Error creating PRAMIN heap: %d\n", ret);
++ nouveau_gpuobj_ref_del(dev, &chan->ramin);
++ return ret;
++ }
++
++ return 0;
++}
++
++int
++nouveau_gpuobj_channel_init(struct nouveau_channel *chan,
++ uint32_t vram_h, uint32_t tt_h)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
++ struct nouveau_gpuobj *vram = NULL, *tt = NULL;
++ int ret, i;
++
++ INIT_LIST_HEAD(&chan->ramht_refs);
++
++ NV_DEBUG(dev, "ch%d vram=0x%08x tt=0x%08x\n", chan->id, vram_h, tt_h);
++
++ /* Reserve a block of PRAMIN for the channel
++ *XXX: maybe on <NV50 too at some point
++ */
++ if (0 || dev_priv->card_type == NV_50) {
++ ret = nouveau_gpuobj_channel_init_pramin(chan);
++ if (ret) {
++ NV_ERROR(dev, "init pramin\n");
++ return ret;
++ }
++ }
++
++ /* NV50 VM
++ * - Allocate per-channel page-directory
++ * - Map GART and VRAM into the channel's address space at the
++ * locations determined during init.
++ */
++ if (dev_priv->card_type >= NV_50) {
++ uint32_t vm_offset, pde;
++
++ instmem->prepare_access(dev, true);
++
++ vm_offset = (dev_priv->chipset & 0xf0) == 0x50 ? 0x1400 : 0x200;
++ vm_offset += chan->ramin->gpuobj->im_pramin->start;
++
++ ret = nouveau_gpuobj_new_fake(dev, vm_offset, ~0, 0x4000,
++ 0, &chan->vm_pd, NULL);
++ if (ret) {
++ instmem->finish_access(dev);
++ return ret;
++ }
++ for (i = 0; i < 0x4000; i += 8) {
++ nv_wo32(dev, chan->vm_pd, (i+0)/4, 0x00000000);
++ nv_wo32(dev, chan->vm_pd, (i+4)/4, 0xdeadcafe);
++ }
++
++ pde = (dev_priv->vm_gart_base / (512*1024*1024)) * 2;
++ ret = nouveau_gpuobj_ref_add(dev, NULL, 0,
++ dev_priv->gart_info.sg_ctxdma,
++ &chan->vm_gart_pt);
++ if (ret) {
++ instmem->finish_access(dev);
++ return ret;
++ }
++ nv_wo32(dev, chan->vm_pd, pde++,
++ chan->vm_gart_pt->instance | 0x03);
++ nv_wo32(dev, chan->vm_pd, pde++, 0x00000000);
++
++ pde = (dev_priv->vm_vram_base / (512*1024*1024)) * 2;
++ for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) {
++ ret = nouveau_gpuobj_ref_add(dev, NULL, 0,
++ dev_priv->vm_vram_pt[i],
++ &chan->vm_vram_pt[i]);
++ if (ret) {
++ instmem->finish_access(dev);
++ return ret;
++ }
++
++ nv_wo32(dev, chan->vm_pd, pde++,
++ chan->vm_vram_pt[i]->instance | 0x61);
++ nv_wo32(dev, chan->vm_pd, pde++, 0x00000000);
++ }
++
++ instmem->finish_access(dev);
++ }
++
++ /* RAMHT */
++ if (dev_priv->card_type < NV_50) {
++ ret = nouveau_gpuobj_ref_add(dev, NULL, 0, dev_priv->ramht,
++ &chan->ramht);
++ if (ret)
++ return ret;
++ } else {
++ ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0,
++ 0x8000, 16,
++ NVOBJ_FLAG_ZERO_ALLOC,
++ &chan->ramht);
++ if (ret)
++ return ret;
++ }
++
++ /* VRAM ctxdma */
++ if (dev_priv->card_type >= NV_50) {
++ ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
++ 0, dev_priv->vm_end,
++ NV_DMA_ACCESS_RW,
++ NV_DMA_TARGET_AGP, &vram);
++ if (ret) {
++ NV_ERROR(dev, "Error creating VRAM ctxdma: %d\n", ret);
++ return ret;
++ }
++ } else {
++ ret = nouveau_gpuobj_dma_new(chan, NV_CLASS_DMA_IN_MEMORY,
++ 0, dev_priv->fb_available_size,
++ NV_DMA_ACCESS_RW,
++ NV_DMA_TARGET_VIDMEM, &vram);
++ if (ret) {
++ NV_ERROR(dev, "Error creating VRAM ctxdma: %d\n", ret);
++ return ret;
++ }
++ }
++
++ ret = nouveau_gpuobj_ref_add(dev, chan, vram_h, vram, NULL);
++ if (ret) {
++ NV_ERROR(dev, "Error referencing VRAM ctxdma: %d\n", ret);
++ return ret;
++ }
++
++ /* TT memory ctxdma */
++ if (dev_priv->card_type >= NV_50) {
++ tt = vram;
++ } else
++ if (dev_priv->gart_info.type != NOUVEAU_GART_NONE) {
++ ret = nouveau_gpuobj_gart_dma_new(chan, 0,
++ dev_priv->gart_info.aper_size,
++ NV_DMA_ACCESS_RW, &tt, NULL);
++ } else {
++ NV_ERROR(dev, "Invalid GART type %d\n", dev_priv->gart_info.type);
++ ret = -EINVAL;
++ }
++
++ if (ret) {
++ NV_ERROR(dev, "Error creating TT ctxdma: %d\n", ret);
++ return ret;
++ }
++
++ ret = nouveau_gpuobj_ref_add(dev, chan, tt_h, tt, NULL);
++ if (ret) {
++ NV_ERROR(dev, "Error referencing TT ctxdma: %d\n", ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++void
++nouveau_gpuobj_channel_takedown(struct nouveau_channel *chan)
++{
++ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
++ struct drm_device *dev = chan->dev;
++ struct list_head *entry, *tmp;
++ struct nouveau_gpuobj_ref *ref;
++ int i;
++
++ NV_DEBUG(dev, "ch%d\n", chan->id);
++
++ if (!chan->ramht_refs.next)
++ return;
++
++ list_for_each_safe(entry, tmp, &chan->ramht_refs) {
++ ref = list_entry(entry, struct nouveau_gpuobj_ref, list);
++
++ nouveau_gpuobj_ref_del(dev, &ref);
++ }
++
++ nouveau_gpuobj_ref_del(dev, &chan->ramht);
++
++ nouveau_gpuobj_del(dev, &chan->vm_pd);
++ nouveau_gpuobj_ref_del(dev, &chan->vm_gart_pt);
++ for (i = 0; i < dev_priv->vm_vram_pt_nr; i++)
++ nouveau_gpuobj_ref_del(dev, &chan->vm_vram_pt[i]);
++
++ if (chan->ramin_heap)
++ nouveau_mem_takedown(&chan->ramin_heap);
++ if (chan->ramin)
++ nouveau_gpuobj_ref_del(dev, &chan->ramin);
++
++}
++
++int
++nouveau_gpuobj_suspend(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *gpuobj;
++ int i;
++
++ if (dev_priv->card_type < NV_50) {
++ dev_priv->susres.ramin_copy = vmalloc(dev_priv->ramin_rsvd_vram);
++ if (!dev_priv->susres.ramin_copy)
++ return -ENOMEM;
++
++ for (i = 0; i < dev_priv->ramin_rsvd_vram; i += 4)
++ dev_priv->susres.ramin_copy[i/4] = nv_ri32(dev, i);
++ return 0;
++ }
++
++ list_for_each_entry(gpuobj, &dev_priv->gpuobj_list, list) {
++ if (!gpuobj->im_backing || (gpuobj->flags & NVOBJ_FLAG_FAKE))
++ continue;
++
++ gpuobj->im_backing_suspend = vmalloc(gpuobj->im_pramin->size);
++ if (!gpuobj->im_backing_suspend) {
++ nouveau_gpuobj_resume(dev);
++ return -ENOMEM;
++ }
++
++ dev_priv->engine.instmem.prepare_access(dev, false);
++ for (i = 0; i < gpuobj->im_pramin->size / 4; i++)
++ gpuobj->im_backing_suspend[i] = nv_ro32(dev, gpuobj, i);
++ dev_priv->engine.instmem.finish_access(dev);
++ }
++
++ return 0;
++}
++
++void
++nouveau_gpuobj_suspend_cleanup(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *gpuobj;
++
++ if (dev_priv->card_type < NV_50) {
++ vfree(dev_priv->susres.ramin_copy);
++ dev_priv->susres.ramin_copy = NULL;
++ return;
++ }
++
++ list_for_each_entry(gpuobj, &dev_priv->gpuobj_list, list) {
++ if (!gpuobj->im_backing_suspend)
++ continue;
++
++ vfree(gpuobj->im_backing_suspend);
++ gpuobj->im_backing_suspend = NULL;
++ }
++}
++
++void
++nouveau_gpuobj_resume(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *gpuobj;
++ int i;
++
++ if (dev_priv->card_type < NV_50) {
++ for (i = 0; i < dev_priv->ramin_rsvd_vram; i += 4)
++ nv_wi32(dev, i, dev_priv->susres.ramin_copy[i/4]);
++ nouveau_gpuobj_suspend_cleanup(dev);
++ return;
++ }
++
++ list_for_each_entry(gpuobj, &dev_priv->gpuobj_list, list) {
++ if (!gpuobj->im_backing_suspend)
++ continue;
++
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ for (i = 0; i < gpuobj->im_pramin->size / 4; i++)
++ nv_wo32(dev, gpuobj, i, gpuobj->im_backing_suspend[i]);
++ dev_priv->engine.instmem.finish_access(dev);
++ }
++
++ nouveau_gpuobj_suspend_cleanup(dev);
++}
++
++int nouveau_ioctl_grobj_alloc(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct drm_nouveau_grobj_alloc *init = data;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
++ struct nouveau_pgraph_object_class *grc;
++ struct nouveau_gpuobj *gr = NULL;
++ struct nouveau_channel *chan;
++ int ret;
++
++ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
++ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(init->channel, file_priv, chan);
++
++ if (init->handle == ~0)
++ return -EINVAL;
++
++ grc = pgraph->grclass;
++ while (grc->id) {
++ if (grc->id == init->class)
++ break;
++ grc++;
++ }
++
++ if (!grc->id) {
++ NV_ERROR(dev, "Illegal object class: 0x%x\n", init->class);
++ return -EPERM;
++ }
++
++ if (nouveau_gpuobj_ref_find(chan, init->handle, NULL) == 0)
++ return -EEXIST;
++
++ if (!grc->software)
++ ret = nouveau_gpuobj_gr_new(chan, grc->id, &gr);
++ else
++ ret = nouveau_gpuobj_sw_new(chan, grc->id, &gr);
++
++ if (ret) {
++ NV_ERROR(dev, "Error creating object: %d (%d/0x%08x)\n",
++ ret, init->channel, init->handle);
++ return ret;
++ }
++
++ ret = nouveau_gpuobj_ref_add(dev, chan, init->handle, gr, NULL);
++ if (ret) {
++ NV_ERROR(dev, "Error referencing object: %d (%d/0x%08x)\n",
++ ret, init->channel, init->handle);
++ nouveau_gpuobj_del(dev, &gr);
++ return ret;
++ }
++
++ return 0;
++}
++
++int nouveau_ioctl_gpuobj_free(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_nouveau_gpuobj_free *objfree = data;
++ struct nouveau_gpuobj_ref *ref;
++ struct nouveau_channel *chan;
++ int ret;
++
++ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
++ NOUVEAU_GET_USER_CHANNEL_WITH_RETURN(objfree->channel, file_priv, chan);
++
++ ret = nouveau_gpuobj_ref_find(chan, objfree->handle, &ref);
++ if (ret)
++ return ret;
++ nouveau_gpuobj_ref_del(dev, &ref);
++
++ return 0;
++}
+diff --git a/drivers/gpu/drm/nouveau/nouveau_reg.h b/drivers/gpu/drm/nouveau/nouveau_reg.h
+new file mode 100644
+index 0000000..aa9b310
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_reg.h
+@@ -0,0 +1,837 @@
++
++
++#define NV03_BOOT_0 0x00100000
++# define NV03_BOOT_0_RAM_AMOUNT 0x00000003
++# define NV03_BOOT_0_RAM_AMOUNT_8MB 0x00000000
++# define NV03_BOOT_0_RAM_AMOUNT_2MB 0x00000001
++# define NV03_BOOT_0_RAM_AMOUNT_4MB 0x00000002
++# define NV03_BOOT_0_RAM_AMOUNT_8MB_SDRAM 0x00000003
++# define NV04_BOOT_0_RAM_AMOUNT_32MB 0x00000000
++# define NV04_BOOT_0_RAM_AMOUNT_4MB 0x00000001
++# define NV04_BOOT_0_RAM_AMOUNT_8MB 0x00000002
++# define NV04_BOOT_0_RAM_AMOUNT_16MB 0x00000003
++
++#define NV04_FIFO_DATA 0x0010020c
++# define NV10_FIFO_DATA_RAM_AMOUNT_MB_MASK 0xfff00000
++# define NV10_FIFO_DATA_RAM_AMOUNT_MB_SHIFT 20
++
++#define NV_RAMIN 0x00700000
++
++#define NV_RAMHT_HANDLE_OFFSET 0
++#define NV_RAMHT_CONTEXT_OFFSET 4
++# define NV_RAMHT_CONTEXT_VALID (1<<31)
++# define NV_RAMHT_CONTEXT_CHANNEL_SHIFT 24
++# define NV_RAMHT_CONTEXT_ENGINE_SHIFT 16
++# define NV_RAMHT_CONTEXT_ENGINE_SOFTWARE 0
++# define NV_RAMHT_CONTEXT_ENGINE_GRAPHICS 1
++# define NV_RAMHT_CONTEXT_INSTANCE_SHIFT 0
++# define NV40_RAMHT_CONTEXT_CHANNEL_SHIFT 23
++# define NV40_RAMHT_CONTEXT_ENGINE_SHIFT 20
++# define NV40_RAMHT_CONTEXT_INSTANCE_SHIFT 0
++
++/* DMA object defines */
++#define NV_DMA_ACCESS_RW 0
++#define NV_DMA_ACCESS_RO 1
++#define NV_DMA_ACCESS_WO 2
++#define NV_DMA_TARGET_VIDMEM 0
++#define NV_DMA_TARGET_PCI 2
++#define NV_DMA_TARGET_AGP 3
++/* The following is not a real value used by the card, it's changed by
++ * nouveau_object_dma_create */
++#define NV_DMA_TARGET_PCI_NONLINEAR 8
++
++/* Some object classes we care about in the drm */
++#define NV_CLASS_DMA_FROM_MEMORY 0x00000002
++#define NV_CLASS_DMA_TO_MEMORY 0x00000003
++#define NV_CLASS_NULL 0x00000030
++#define NV_CLASS_DMA_IN_MEMORY 0x0000003D
++
++#define NV03_USER(i) (0x00800000+(i*NV03_USER_SIZE))
++#define NV03_USER__SIZE 16
++#define NV10_USER__SIZE 32
++#define NV03_USER_SIZE 0x00010000
++#define NV03_USER_DMA_PUT(i) (0x00800040+(i*NV03_USER_SIZE))
++#define NV03_USER_DMA_PUT__SIZE 16
++#define NV10_USER_DMA_PUT__SIZE 32
++#define NV03_USER_DMA_GET(i) (0x00800044+(i*NV03_USER_SIZE))
++#define NV03_USER_DMA_GET__SIZE 16
++#define NV10_USER_DMA_GET__SIZE 32
++#define NV03_USER_REF_CNT(i) (0x00800048+(i*NV03_USER_SIZE))
++#define NV03_USER_REF_CNT__SIZE 16
++#define NV10_USER_REF_CNT__SIZE 32
++
++#define NV40_USER(i) (0x00c00000+(i*NV40_USER_SIZE))
++#define NV40_USER_SIZE 0x00001000
++#define NV40_USER_DMA_PUT(i) (0x00c00040+(i*NV40_USER_SIZE))
++#define NV40_USER_DMA_PUT__SIZE 32
++#define NV40_USER_DMA_GET(i) (0x00c00044+(i*NV40_USER_SIZE))
++#define NV40_USER_DMA_GET__SIZE 32
++#define NV40_USER_REF_CNT(i) (0x00c00048+(i*NV40_USER_SIZE))
++#define NV40_USER_REF_CNT__SIZE 32
++
++#define NV50_USER(i) (0x00c00000+(i*NV50_USER_SIZE))
++#define NV50_USER_SIZE 0x00002000
++#define NV50_USER_DMA_PUT(i) (0x00c00040+(i*NV50_USER_SIZE))
++#define NV50_USER_DMA_PUT__SIZE 128
++#define NV50_USER_DMA_GET(i) (0x00c00044+(i*NV50_USER_SIZE))
++#define NV50_USER_DMA_GET__SIZE 128
++#define NV50_USER_REF_CNT(i) (0x00c00048+(i*NV50_USER_SIZE))
++#define NV50_USER_REF_CNT__SIZE 128
++
++#define NV03_FIFO_SIZE 0x8000UL
++
++#define NV03_PMC_BOOT_0 0x00000000
++#define NV03_PMC_BOOT_1 0x00000004
++#define NV03_PMC_INTR_0 0x00000100
++# define NV_PMC_INTR_0_PFIFO_PENDING (1<<8)
++# define NV_PMC_INTR_0_PGRAPH_PENDING (1<<12)
++# define NV_PMC_INTR_0_NV50_I2C_PENDING (1<<21)
++# define NV_PMC_INTR_0_CRTC0_PENDING (1<<24)
++# define NV_PMC_INTR_0_CRTC1_PENDING (1<<25)
++# define NV_PMC_INTR_0_NV50_DISPLAY_PENDING (1<<26)
++# define NV_PMC_INTR_0_CRTCn_PENDING (3<<24)
++#define NV03_PMC_INTR_EN_0 0x00000140
++# define NV_PMC_INTR_EN_0_MASTER_ENABLE (1<<0)
++#define NV03_PMC_ENABLE 0x00000200
++# define NV_PMC_ENABLE_PFIFO (1<<8)
++# define NV_PMC_ENABLE_PGRAPH (1<<12)
++/* Disabling the below bit breaks newer (G7X only?) mobile chipsets,
++ * the card will hang early on in the X init process.
++ */
++# define NV_PMC_ENABLE_UNK13 (1<<13)
++#define NV40_PMC_GRAPH_UNITS 0x00001540
++#define NV40_PMC_BACKLIGHT 0x000015f0
++# define NV40_PMC_BACKLIGHT_MASK 0x001f0000
++#define NV40_PMC_1700 0x00001700
++#define NV40_PMC_1704 0x00001704
++#define NV40_PMC_1708 0x00001708
++#define NV40_PMC_170C 0x0000170C
++
++/* probably PMC ? */
++#define NV50_PUNK_BAR0_PRAMIN 0x00001700
++#define NV50_PUNK_BAR_CFG_BASE 0x00001704
++#define NV50_PUNK_BAR_CFG_BASE_VALID (1<<30)
++#define NV50_PUNK_BAR1_CTXDMA 0x00001708
++#define NV50_PUNK_BAR1_CTXDMA_VALID (1<<31)
++#define NV50_PUNK_BAR3_CTXDMA 0x0000170C
++#define NV50_PUNK_BAR3_CTXDMA_VALID (1<<31)
++#define NV50_PUNK_UNK1710 0x00001710
++
++#define NV04_PBUS_PCI_NV_1 0x00001804
++#define NV04_PBUS_PCI_NV_19 0x0000184C
++#define NV04_PBUS_PCI_NV_20 0x00001850
++# define NV04_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED (0 << 0)
++# define NV04_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED (1 << 0)
++
++#define NV04_PTIMER_INTR_0 0x00009100
++#define NV04_PTIMER_INTR_EN_0 0x00009140
++#define NV04_PTIMER_NUMERATOR 0x00009200
++#define NV04_PTIMER_DENOMINATOR 0x00009210
++#define NV04_PTIMER_TIME_0 0x00009400
++#define NV04_PTIMER_TIME_1 0x00009410
++#define NV04_PTIMER_ALARM_0 0x00009420
++
++#define NV04_PFB_CFG0 0x00100200
++#define NV04_PFB_CFG1 0x00100204
++#define NV40_PFB_020C 0x0010020C
++#define NV10_PFB_TILE(i) (0x00100240 + (i*16))
++#define NV10_PFB_TILE__SIZE 8
++#define NV10_PFB_TLIMIT(i) (0x00100244 + (i*16))
++#define NV10_PFB_TSIZE(i) (0x00100248 + (i*16))
++#define NV10_PFB_TSTATUS(i) (0x0010024C + (i*16))
++#define NV10_PFB_CLOSE_PAGE2 0x0010033C
++#define NV40_PFB_TILE(i) (0x00100600 + (i*16))
++#define NV40_PFB_TILE__SIZE_0 12
++#define NV40_PFB_TILE__SIZE_1 15
++#define NV40_PFB_TLIMIT(i) (0x00100604 + (i*16))
++#define NV40_PFB_TSIZE(i) (0x00100608 + (i*16))
++#define NV40_PFB_TSTATUS(i) (0x0010060C + (i*16))
++#define NV40_PFB_UNK_800 0x00100800
++
++#define NV04_PGRAPH_DEBUG_0 0x00400080
++#define NV04_PGRAPH_DEBUG_1 0x00400084
++#define NV04_PGRAPH_DEBUG_2 0x00400088
++#define NV04_PGRAPH_DEBUG_3 0x0040008c
++#define NV10_PGRAPH_DEBUG_4 0x00400090
++#define NV03_PGRAPH_INTR 0x00400100
++#define NV03_PGRAPH_NSTATUS 0x00400104
++# define NV04_PGRAPH_NSTATUS_STATE_IN_USE (1<<11)
++# define NV04_PGRAPH_NSTATUS_INVALID_STATE (1<<12)
++# define NV04_PGRAPH_NSTATUS_BAD_ARGUMENT (1<<13)
++# define NV04_PGRAPH_NSTATUS_PROTECTION_FAULT (1<<14)
++# define NV10_PGRAPH_NSTATUS_STATE_IN_USE (1<<23)
++# define NV10_PGRAPH_NSTATUS_INVALID_STATE (1<<24)
++# define NV10_PGRAPH_NSTATUS_BAD_ARGUMENT (1<<25)
++# define NV10_PGRAPH_NSTATUS_PROTECTION_FAULT (1<<26)
++#define NV03_PGRAPH_NSOURCE 0x00400108
++# define NV03_PGRAPH_NSOURCE_NOTIFICATION (1<<0)
++# define NV03_PGRAPH_NSOURCE_DATA_ERROR (1<<1)
++# define NV03_PGRAPH_NSOURCE_PROTECTION_ERROR (1<<2)
++# define NV03_PGRAPH_NSOURCE_RANGE_EXCEPTION (1<<3)
++# define NV03_PGRAPH_NSOURCE_LIMIT_COLOR (1<<4)
++# define NV03_PGRAPH_NSOURCE_LIMIT_ZETA (1<<5)
++# define NV03_PGRAPH_NSOURCE_ILLEGAL_MTHD (1<<6)
++# define NV03_PGRAPH_NSOURCE_DMA_R_PROTECTION (1<<7)
++# define NV03_PGRAPH_NSOURCE_DMA_W_PROTECTION (1<<8)
++# define NV03_PGRAPH_NSOURCE_FORMAT_EXCEPTION (1<<9)
++# define NV03_PGRAPH_NSOURCE_PATCH_EXCEPTION (1<<10)
++# define NV03_PGRAPH_NSOURCE_STATE_INVALID (1<<11)
++# define NV03_PGRAPH_NSOURCE_DOUBLE_NOTIFY (1<<12)
++# define NV03_PGRAPH_NSOURCE_NOTIFY_IN_USE (1<<13)
++# define NV03_PGRAPH_NSOURCE_METHOD_CNT (1<<14)
++# define NV03_PGRAPH_NSOURCE_BFR_NOTIFICATION (1<<15)
++# define NV03_PGRAPH_NSOURCE_DMA_VTX_PROTECTION (1<<16)
++# define NV03_PGRAPH_NSOURCE_DMA_WIDTH_A (1<<17)
++# define NV03_PGRAPH_NSOURCE_DMA_WIDTH_B (1<<18)
++#define NV03_PGRAPH_INTR_EN 0x00400140
++#define NV40_PGRAPH_INTR_EN 0x0040013C
++# define NV_PGRAPH_INTR_NOTIFY (1<<0)
++# define NV_PGRAPH_INTR_MISSING_HW (1<<4)
++# define NV_PGRAPH_INTR_CONTEXT_SWITCH (1<<12)
++# define NV_PGRAPH_INTR_BUFFER_NOTIFY (1<<16)
++# define NV_PGRAPH_INTR_ERROR (1<<20)
++#define NV10_PGRAPH_CTX_CONTROL 0x00400144
++#define NV10_PGRAPH_CTX_USER 0x00400148
++#define NV10_PGRAPH_CTX_SWITCH1 0x0040014C
++#define NV10_PGRAPH_CTX_SWITCH2 0x00400150
++#define NV10_PGRAPH_CTX_SWITCH3 0x00400154
++#define NV10_PGRAPH_CTX_SWITCH4 0x00400158
++#define NV10_PGRAPH_CTX_SWITCH5 0x0040015C
++#define NV04_PGRAPH_CTX_SWITCH1 0x00400160
++#define NV10_PGRAPH_CTX_CACHE1 0x00400160
++#define NV04_PGRAPH_CTX_SWITCH2 0x00400164
++#define NV04_PGRAPH_CTX_SWITCH3 0x00400168
++#define NV04_PGRAPH_CTX_SWITCH4 0x0040016C
++#define NV04_PGRAPH_CTX_CONTROL 0x00400170
++#define NV04_PGRAPH_CTX_USER 0x00400174
++#define NV04_PGRAPH_CTX_CACHE1 0x00400180
++#define NV10_PGRAPH_CTX_CACHE2 0x00400180
++#define NV03_PGRAPH_CTX_CONTROL 0x00400190
++#define NV03_PGRAPH_CTX_USER 0x00400194
++#define NV04_PGRAPH_CTX_CACHE2 0x004001A0
++#define NV10_PGRAPH_CTX_CACHE3 0x004001A0
++#define NV04_PGRAPH_CTX_CACHE3 0x004001C0
++#define NV10_PGRAPH_CTX_CACHE4 0x004001C0
++#define NV04_PGRAPH_CTX_CACHE4 0x004001E0
++#define NV10_PGRAPH_CTX_CACHE5 0x004001E0
++#define NV40_PGRAPH_CTXCTL_0304 0x00400304
++#define NV40_PGRAPH_CTXCTL_0304_XFER_CTX 0x00000001
++#define NV40_PGRAPH_CTXCTL_UCODE_STAT 0x00400308
++#define NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_MASK 0xff000000
++#define NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_SHIFT 24
++#define NV40_PGRAPH_CTXCTL_UCODE_STAT_OP_MASK 0x00ffffff
++#define NV40_PGRAPH_CTXCTL_0310 0x00400310
++#define NV40_PGRAPH_CTXCTL_0310_XFER_SAVE 0x00000020
++#define NV40_PGRAPH_CTXCTL_0310_XFER_LOAD 0x00000040
++#define NV40_PGRAPH_CTXCTL_030C 0x0040030c
++#define NV40_PGRAPH_CTXCTL_UCODE_INDEX 0x00400324
++#define NV40_PGRAPH_CTXCTL_UCODE_DATA 0x00400328
++#define NV40_PGRAPH_CTXCTL_CUR 0x0040032c
++#define NV40_PGRAPH_CTXCTL_CUR_LOADED 0x01000000
++#define NV40_PGRAPH_CTXCTL_CUR_INSTANCE 0x000FFFFF
++#define NV40_PGRAPH_CTXCTL_NEXT 0x00400330
++#define NV40_PGRAPH_CTXCTL_NEXT_INSTANCE 0x000fffff
++#define NV50_PGRAPH_CTXCTL_CUR 0x0040032c
++#define NV50_PGRAPH_CTXCTL_CUR_LOADED 0x80000000
++#define NV50_PGRAPH_CTXCTL_CUR_INSTANCE 0x00ffffff
++#define NV50_PGRAPH_CTXCTL_NEXT 0x00400330
++#define NV50_PGRAPH_CTXCTL_NEXT_INSTANCE 0x00ffffff
++#define NV03_PGRAPH_ABS_X_RAM 0x00400400
++#define NV03_PGRAPH_ABS_Y_RAM 0x00400480
++#define NV03_PGRAPH_X_MISC 0x00400500
++#define NV03_PGRAPH_Y_MISC 0x00400504
++#define NV04_PGRAPH_VALID1 0x00400508
++#define NV04_PGRAPH_SOURCE_COLOR 0x0040050C
++#define NV04_PGRAPH_MISC24_0 0x00400510
++#define NV03_PGRAPH_XY_LOGIC_MISC0 0x00400514
++#define NV03_PGRAPH_XY_LOGIC_MISC1 0x00400518
++#define NV03_PGRAPH_XY_LOGIC_MISC2 0x0040051C
++#define NV03_PGRAPH_XY_LOGIC_MISC3 0x00400520
++#define NV03_PGRAPH_CLIPX_0 0x00400524
++#define NV03_PGRAPH_CLIPX_1 0x00400528
++#define NV03_PGRAPH_CLIPY_0 0x0040052C
++#define NV03_PGRAPH_CLIPY_1 0x00400530
++#define NV03_PGRAPH_ABS_ICLIP_XMAX 0x00400534
++#define NV03_PGRAPH_ABS_ICLIP_YMAX 0x00400538
++#define NV03_PGRAPH_ABS_UCLIP_XMIN 0x0040053C
++#define NV03_PGRAPH_ABS_UCLIP_YMIN 0x00400540
++#define NV03_PGRAPH_ABS_UCLIP_XMAX 0x00400544
++#define NV03_PGRAPH_ABS_UCLIP_YMAX 0x00400548
++#define NV03_PGRAPH_ABS_UCLIPA_XMIN 0x00400560
++#define NV03_PGRAPH_ABS_UCLIPA_YMIN 0x00400564
++#define NV03_PGRAPH_ABS_UCLIPA_XMAX 0x00400568
++#define NV03_PGRAPH_ABS_UCLIPA_YMAX 0x0040056C
++#define NV04_PGRAPH_MISC24_1 0x00400570
++#define NV04_PGRAPH_MISC24_2 0x00400574
++#define NV04_PGRAPH_VALID2 0x00400578
++#define NV04_PGRAPH_PASSTHRU_0 0x0040057C
++#define NV04_PGRAPH_PASSTHRU_1 0x00400580
++#define NV04_PGRAPH_PASSTHRU_2 0x00400584
++#define NV10_PGRAPH_DIMX_TEXTURE 0x00400588
++#define NV10_PGRAPH_WDIMX_TEXTURE 0x0040058C
++#define NV04_PGRAPH_COMBINE_0_ALPHA 0x00400590
++#define NV04_PGRAPH_COMBINE_0_COLOR 0x00400594
++#define NV04_PGRAPH_COMBINE_1_ALPHA 0x00400598
++#define NV04_PGRAPH_COMBINE_1_COLOR 0x0040059C
++#define NV04_PGRAPH_FORMAT_0 0x004005A8
++#define NV04_PGRAPH_FORMAT_1 0x004005AC
++#define NV04_PGRAPH_FILTER_0 0x004005B0
++#define NV04_PGRAPH_FILTER_1 0x004005B4
++#define NV03_PGRAPH_MONO_COLOR0 0x00400600
++#define NV04_PGRAPH_ROP3 0x00400604
++#define NV04_PGRAPH_BETA_AND 0x00400608
++#define NV04_PGRAPH_BETA_PREMULT 0x0040060C
++#define NV04_PGRAPH_LIMIT_VIOL_PIX 0x00400610
++#define NV04_PGRAPH_FORMATS 0x00400618
++#define NV10_PGRAPH_DEBUG_2 0x00400620
++#define NV04_PGRAPH_BOFFSET0 0x00400640
++#define NV04_PGRAPH_BOFFSET1 0x00400644
++#define NV04_PGRAPH_BOFFSET2 0x00400648
++#define NV04_PGRAPH_BOFFSET3 0x0040064C
++#define NV04_PGRAPH_BOFFSET4 0x00400650
++#define NV04_PGRAPH_BOFFSET5 0x00400654
++#define NV04_PGRAPH_BBASE0 0x00400658
++#define NV04_PGRAPH_BBASE1 0x0040065C
++#define NV04_PGRAPH_BBASE2 0x00400660
++#define NV04_PGRAPH_BBASE3 0x00400664
++#define NV04_PGRAPH_BBASE4 0x00400668
++#define NV04_PGRAPH_BBASE5 0x0040066C
++#define NV04_PGRAPH_BPITCH0 0x00400670
++#define NV04_PGRAPH_BPITCH1 0x00400674
++#define NV04_PGRAPH_BPITCH2 0x00400678
++#define NV04_PGRAPH_BPITCH3 0x0040067C
++#define NV04_PGRAPH_BPITCH4 0x00400680
++#define NV04_PGRAPH_BLIMIT0 0x00400684
++#define NV04_PGRAPH_BLIMIT1 0x00400688
++#define NV04_PGRAPH_BLIMIT2 0x0040068C
++#define NV04_PGRAPH_BLIMIT3 0x00400690
++#define NV04_PGRAPH_BLIMIT4 0x00400694
++#define NV04_PGRAPH_BLIMIT5 0x00400698
++#define NV04_PGRAPH_BSWIZZLE2 0x0040069C
++#define NV04_PGRAPH_BSWIZZLE5 0x004006A0
++#define NV03_PGRAPH_STATUS 0x004006B0
++#define NV04_PGRAPH_STATUS 0x00400700
++#define NV04_PGRAPH_TRAPPED_ADDR 0x00400704
++#define NV04_PGRAPH_TRAPPED_DATA 0x00400708
++#define NV04_PGRAPH_SURFACE 0x0040070C
++#define NV10_PGRAPH_TRAPPED_DATA_HIGH 0x0040070C
++#define NV04_PGRAPH_STATE 0x00400710
++#define NV10_PGRAPH_SURFACE 0x00400710
++#define NV04_PGRAPH_NOTIFY 0x00400714
++#define NV10_PGRAPH_STATE 0x00400714
++#define NV10_PGRAPH_NOTIFY 0x00400718
++
++#define NV04_PGRAPH_FIFO 0x00400720
++
++#define NV04_PGRAPH_BPIXEL 0x00400724
++#define NV10_PGRAPH_RDI_INDEX 0x00400750
++#define NV04_PGRAPH_FFINTFC_ST2 0x00400754
++#define NV10_PGRAPH_RDI_DATA 0x00400754
++#define NV04_PGRAPH_DMA_PITCH 0x00400760
++#define NV10_PGRAPH_FFINTFC_ST2 0x00400764
++#define NV04_PGRAPH_DVD_COLORFMT 0x00400764
++#define NV04_PGRAPH_SCALED_FORMAT 0x00400768
++#define NV10_PGRAPH_DMA_PITCH 0x00400770
++#define NV10_PGRAPH_DVD_COLORFMT 0x00400774
++#define NV10_PGRAPH_SCALED_FORMAT 0x00400778
++#define NV20_PGRAPH_CHANNEL_CTX_TABLE 0x00400780
++#define NV20_PGRAPH_CHANNEL_CTX_POINTER 0x00400784
++#define NV20_PGRAPH_CHANNEL_CTX_XFER 0x00400788
++#define NV20_PGRAPH_CHANNEL_CTX_XFER_LOAD 0x00000001
++#define NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE 0x00000002
++#define NV04_PGRAPH_PATT_COLOR0 0x00400800
++#define NV04_PGRAPH_PATT_COLOR1 0x00400804
++#define NV04_PGRAPH_PATTERN 0x00400808
++#define NV04_PGRAPH_PATTERN_SHAPE 0x00400810
++#define NV04_PGRAPH_CHROMA 0x00400814
++#define NV04_PGRAPH_CONTROL0 0x00400818
++#define NV04_PGRAPH_CONTROL1 0x0040081C
++#define NV04_PGRAPH_CONTROL2 0x00400820
++#define NV04_PGRAPH_BLEND 0x00400824
++#define NV04_PGRAPH_STORED_FMT 0x00400830
++#define NV04_PGRAPH_PATT_COLORRAM 0x00400900
++#define NV20_PGRAPH_TILE(i) (0x00400900 + (i*16))
++#define NV20_PGRAPH_TLIMIT(i) (0x00400904 + (i*16))
++#define NV20_PGRAPH_TSIZE(i) (0x00400908 + (i*16))
++#define NV20_PGRAPH_TSTATUS(i) (0x0040090C + (i*16))
++#define NV10_PGRAPH_TILE(i) (0x00400B00 + (i*16))
++#define NV10_PGRAPH_TLIMIT(i) (0x00400B04 + (i*16))
++#define NV10_PGRAPH_TSIZE(i) (0x00400B08 + (i*16))
++#define NV10_PGRAPH_TSTATUS(i) (0x00400B0C + (i*16))
++#define NV04_PGRAPH_U_RAM 0x00400D00
++#define NV47_PGRAPH_TILE(i) (0x00400D00 + (i*16))
++#define NV47_PGRAPH_TLIMIT(i) (0x00400D04 + (i*16))
++#define NV47_PGRAPH_TSIZE(i) (0x00400D08 + (i*16))
++#define NV47_PGRAPH_TSTATUS(i) (0x00400D0C + (i*16))
++#define NV04_PGRAPH_V_RAM 0x00400D40
++#define NV04_PGRAPH_W_RAM 0x00400D80
++#define NV10_PGRAPH_COMBINER0_IN_ALPHA 0x00400E40
++#define NV10_PGRAPH_COMBINER1_IN_ALPHA 0x00400E44
++#define NV10_PGRAPH_COMBINER0_IN_RGB 0x00400E48
++#define NV10_PGRAPH_COMBINER1_IN_RGB 0x00400E4C
++#define NV10_PGRAPH_COMBINER_COLOR0 0x00400E50
++#define NV10_PGRAPH_COMBINER_COLOR1 0x00400E54
++#define NV10_PGRAPH_COMBINER0_OUT_ALPHA 0x00400E58
++#define NV10_PGRAPH_COMBINER1_OUT_ALPHA 0x00400E5C
++#define NV10_PGRAPH_COMBINER0_OUT_RGB 0x00400E60
++#define NV10_PGRAPH_COMBINER1_OUT_RGB 0x00400E64
++#define NV10_PGRAPH_COMBINER_FINAL0 0x00400E68
++#define NV10_PGRAPH_COMBINER_FINAL1 0x00400E6C
++#define NV10_PGRAPH_WINDOWCLIP_HORIZONTAL 0x00400F00
++#define NV10_PGRAPH_WINDOWCLIP_VERTICAL 0x00400F20
++#define NV10_PGRAPH_XFMODE0 0x00400F40
++#define NV10_PGRAPH_XFMODE1 0x00400F44
++#define NV10_PGRAPH_GLOBALSTATE0 0x00400F48
++#define NV10_PGRAPH_GLOBALSTATE1 0x00400F4C
++#define NV10_PGRAPH_PIPE_ADDRESS 0x00400F50
++#define NV10_PGRAPH_PIPE_DATA 0x00400F54
++#define NV04_PGRAPH_DMA_START_0 0x00401000
++#define NV04_PGRAPH_DMA_START_1 0x00401004
++#define NV04_PGRAPH_DMA_LENGTH 0x00401008
++#define NV04_PGRAPH_DMA_MISC 0x0040100C
++#define NV04_PGRAPH_DMA_DATA_0 0x00401020
++#define NV04_PGRAPH_DMA_DATA_1 0x00401024
++#define NV04_PGRAPH_DMA_RM 0x00401030
++#define NV04_PGRAPH_DMA_A_XLATE_INST 0x00401040
++#define NV04_PGRAPH_DMA_A_CONTROL 0x00401044
++#define NV04_PGRAPH_DMA_A_LIMIT 0x00401048
++#define NV04_PGRAPH_DMA_A_TLB_PTE 0x0040104C
++#define NV04_PGRAPH_DMA_A_TLB_TAG 0x00401050
++#define NV04_PGRAPH_DMA_A_ADJ_OFFSET 0x00401054
++#define NV04_PGRAPH_DMA_A_OFFSET 0x00401058
++#define NV04_PGRAPH_DMA_A_SIZE 0x0040105C
++#define NV04_PGRAPH_DMA_A_Y_SIZE 0x00401060
++#define NV04_PGRAPH_DMA_B_XLATE_INST 0x00401080
++#define NV04_PGRAPH_DMA_B_CONTROL 0x00401084
++#define NV04_PGRAPH_DMA_B_LIMIT 0x00401088
++#define NV04_PGRAPH_DMA_B_TLB_PTE 0x0040108C
++#define NV04_PGRAPH_DMA_B_TLB_TAG 0x00401090
++#define NV04_PGRAPH_DMA_B_ADJ_OFFSET 0x00401094
++#define NV04_PGRAPH_DMA_B_OFFSET 0x00401098
++#define NV04_PGRAPH_DMA_B_SIZE 0x0040109C
++#define NV04_PGRAPH_DMA_B_Y_SIZE 0x004010A0
++#define NV40_PGRAPH_TILE1(i) (0x00406900 + (i*16))
++#define NV40_PGRAPH_TLIMIT1(i) (0x00406904 + (i*16))
++#define NV40_PGRAPH_TSIZE1(i) (0x00406908 + (i*16))
++#define NV40_PGRAPH_TSTATUS1(i) (0x0040690C + (i*16))
++
++
++/* It's a guess that this works on NV03. Confirmed on NV04, though */
++#define NV04_PFIFO_DELAY_0 0x00002040
++#define NV04_PFIFO_DMA_TIMESLICE 0x00002044
++#define NV04_PFIFO_NEXT_CHANNEL 0x00002050
++#define NV03_PFIFO_INTR_0 0x00002100
++#define NV03_PFIFO_INTR_EN_0 0x00002140
++# define NV_PFIFO_INTR_CACHE_ERROR (1<<0)
++# define NV_PFIFO_INTR_RUNOUT (1<<4)
++# define NV_PFIFO_INTR_RUNOUT_OVERFLOW (1<<8)
++# define NV_PFIFO_INTR_DMA_PUSHER (1<<12)
++# define NV_PFIFO_INTR_DMA_PT (1<<16)
++# define NV_PFIFO_INTR_SEMAPHORE (1<<20)
++# define NV_PFIFO_INTR_ACQUIRE_TIMEOUT (1<<24)
++#define NV03_PFIFO_RAMHT 0x00002210
++#define NV03_PFIFO_RAMFC 0x00002214
++#define NV03_PFIFO_RAMRO 0x00002218
++#define NV40_PFIFO_RAMFC 0x00002220
++#define NV03_PFIFO_CACHES 0x00002500
++#define NV04_PFIFO_MODE 0x00002504
++#define NV04_PFIFO_DMA 0x00002508
++#define NV04_PFIFO_SIZE 0x0000250c
++#define NV50_PFIFO_CTX_TABLE(c) (0x2600+(c)*4)
++#define NV50_PFIFO_CTX_TABLE__SIZE 128
++#define NV50_PFIFO_CTX_TABLE_CHANNEL_ENABLED (1<<31)
++#define NV50_PFIFO_CTX_TABLE_UNK30_BAD (1<<30)
++#define NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G80 0x0FFFFFFF
++#define NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G84 0x00FFFFFF
++#define NV03_PFIFO_CACHE0_PUSH0 0x00003000
++#define NV03_PFIFO_CACHE0_PULL0 0x00003040
++#define NV04_PFIFO_CACHE0_PULL0 0x00003050
++#define NV04_PFIFO_CACHE0_PULL1 0x00003054
++#define NV03_PFIFO_CACHE1_PUSH0 0x00003200
++#define NV03_PFIFO_CACHE1_PUSH1 0x00003204
++#define NV03_PFIFO_CACHE1_PUSH1_DMA (1<<8)
++#define NV40_PFIFO_CACHE1_PUSH1_DMA (1<<16)
++#define NV03_PFIFO_CACHE1_PUSH1_CHID_MASK 0x0000000f
++#define NV10_PFIFO_CACHE1_PUSH1_CHID_MASK 0x0000001f
++#define NV50_PFIFO_CACHE1_PUSH1_CHID_MASK 0x0000007f
++#define NV03_PFIFO_CACHE1_PUT 0x00003210
++#define NV04_PFIFO_CACHE1_DMA_PUSH 0x00003220
++#define NV04_PFIFO_CACHE1_DMA_FETCH 0x00003224
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_8_BYTES 0x00000000
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_16_BYTES 0x00000008
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_24_BYTES 0x00000010
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_32_BYTES 0x00000018
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_40_BYTES 0x00000020
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_48_BYTES 0x00000028
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_56_BYTES 0x00000030
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_64_BYTES 0x00000038
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_72_BYTES 0x00000040
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_80_BYTES 0x00000048
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_88_BYTES 0x00000050
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_96_BYTES 0x00000058
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_104_BYTES 0x00000060
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_112_BYTES 0x00000068
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_120_BYTES 0x00000070
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES 0x00000078
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_136_BYTES 0x00000080
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_144_BYTES 0x00000088
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_152_BYTES 0x00000090
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_160_BYTES 0x00000098
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_168_BYTES 0x000000A0
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_176_BYTES 0x000000A8
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_184_BYTES 0x000000B0
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_192_BYTES 0x000000B8
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_200_BYTES 0x000000C0
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_208_BYTES 0x000000C8
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_216_BYTES 0x000000D0
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_224_BYTES 0x000000D8
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_232_BYTES 0x000000E0
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_240_BYTES 0x000000E8
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_248_BYTES 0x000000F0
++# define NV_PFIFO_CACHE1_DMA_FETCH_TRIG_256_BYTES 0x000000F8
++# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE 0x0000E000
++# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_32_BYTES 0x00000000
++# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_64_BYTES 0x00002000
++# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_96_BYTES 0x00004000
++# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES 0x00006000
++# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_160_BYTES 0x00008000
++# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_192_BYTES 0x0000A000
++# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_224_BYTES 0x0000C000
++# define NV_PFIFO_CACHE1_DMA_FETCH_SIZE_256_BYTES 0x0000E000
++# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS 0x001F0000
++# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_0 0x00000000
++# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_1 0x00010000
++# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_2 0x00020000
++# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_3 0x00030000
++# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_4 0x00040000
++# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_5 0x00050000
++# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_6 0x00060000
++# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_7 0x00070000
++# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 0x00080000
++# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_9 0x00090000
++# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_10 0x000A0000
++# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_11 0x000B0000
++# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_12 0x000C0000
++# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_13 0x000D0000
++# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_14 0x000E0000
++# define NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_15 0x000F0000
++# define NV_PFIFO_CACHE1_ENDIAN 0x80000000
++# define NV_PFIFO_CACHE1_LITTLE_ENDIAN 0x7FFFFFFF
++# define NV_PFIFO_CACHE1_BIG_ENDIAN 0x80000000
++#define NV04_PFIFO_CACHE1_DMA_STATE 0x00003228
++#define NV04_PFIFO_CACHE1_DMA_INSTANCE 0x0000322c
++#define NV04_PFIFO_CACHE1_DMA_CTL 0x00003230
++#define NV04_PFIFO_CACHE1_DMA_PUT 0x00003240
++#define NV04_PFIFO_CACHE1_DMA_GET 0x00003244
++#define NV10_PFIFO_CACHE1_REF_CNT 0x00003248
++#define NV10_PFIFO_CACHE1_DMA_SUBROUTINE 0x0000324C
++#define NV03_PFIFO_CACHE1_PULL0 0x00003240
++#define NV04_PFIFO_CACHE1_PULL0 0x00003250
++#define NV03_PFIFO_CACHE1_PULL1 0x00003250
++#define NV04_PFIFO_CACHE1_PULL1 0x00003254
++#define NV04_PFIFO_CACHE1_HASH 0x00003258
++#define NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT 0x00003260
++#define NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP 0x00003264
++#define NV10_PFIFO_CACHE1_ACQUIRE_VALUE 0x00003268
++#define NV10_PFIFO_CACHE1_SEMAPHORE 0x0000326C
++#define NV03_PFIFO_CACHE1_GET 0x00003270
++#define NV04_PFIFO_CACHE1_ENGINE 0x00003280
++#define NV04_PFIFO_CACHE1_DMA_DCOUNT 0x000032A0
++#define NV40_PFIFO_GRCTX_INSTANCE 0x000032E0
++#define NV40_PFIFO_UNK32E4 0x000032E4
++#define NV04_PFIFO_CACHE1_METHOD(i) (0x00003800+(i*8))
++#define NV04_PFIFO_CACHE1_DATA(i) (0x00003804+(i*8))
++#define NV40_PFIFO_CACHE1_METHOD(i) (0x00090000+(i*8))
++#define NV40_PFIFO_CACHE1_DATA(i) (0x00090004+(i*8))
++
++#define NV_CRTC0_INTSTAT 0x00600100
++#define NV_CRTC0_INTEN 0x00600140
++#define NV_CRTC1_INTSTAT 0x00602100
++#define NV_CRTC1_INTEN 0x00602140
++# define NV_CRTC_INTR_VBLANK (1<<0)
++
++#define NV04_PRAMIN 0x00700000
++
++/* Fifo commands. These are not regs, neither masks */
++#define NV03_FIFO_CMD_JUMP 0x20000000
++#define NV03_FIFO_CMD_JUMP_OFFSET_MASK 0x1ffffffc
++#define NV03_FIFO_CMD_REWIND (NV03_FIFO_CMD_JUMP | (0 & NV03_FIFO_CMD_JUMP_OFFSET_MASK))
++
++/* This is a partial import from rules-ng, a few things may be duplicated.
++ * Eventually we should completely import everything from rules-ng.
++ * For the moment check rules-ng for docs.
++ */
++
++#define NV50_PMC 0x00000000
++#define NV50_PMC__LEN 0x1
++#define NV50_PMC__ESIZE 0x2000
++# define NV50_PMC_BOOT_0 0x00000000
++# define NV50_PMC_BOOT_0_REVISION 0x000000ff
++# define NV50_PMC_BOOT_0_REVISION__SHIFT 0
++# define NV50_PMC_BOOT_0_ARCH 0x0ff00000
++# define NV50_PMC_BOOT_0_ARCH__SHIFT 20
++# define NV50_PMC_INTR_0 0x00000100
++# define NV50_PMC_INTR_0_PFIFO (1<<8)
++# define NV50_PMC_INTR_0_PGRAPH (1<<12)
++# define NV50_PMC_INTR_0_PTIMER (1<<20)
++# define NV50_PMC_INTR_0_HOTPLUG (1<<21)
++# define NV50_PMC_INTR_0_DISPLAY (1<<26)
++# define NV50_PMC_INTR_EN_0 0x00000140
++# define NV50_PMC_INTR_EN_0_MASTER (1<<0)
++# define NV50_PMC_INTR_EN_0_MASTER_DISABLED (0<<0)
++# define NV50_PMC_INTR_EN_0_MASTER_ENABLED (1<<0)
++# define NV50_PMC_ENABLE 0x00000200
++# define NV50_PMC_ENABLE_PFIFO (1<<8)
++# define NV50_PMC_ENABLE_PGRAPH (1<<12)
++
++#define NV50_PCONNECTOR 0x0000e000
++#define NV50_PCONNECTOR__LEN 0x1
++#define NV50_PCONNECTOR__ESIZE 0x1000
++# define NV50_PCONNECTOR_HOTPLUG_INTR 0x0000e050
++# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C0 (1<<0)
++# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C1 (1<<1)
++# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C2 (1<<2)
++# define NV50_PCONNECTOR_HOTPLUG_INTR_PLUG_I2C3 (1<<3)
++# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C0 (1<<16)
++# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C1 (1<<17)
++# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C2 (1<<18)
++# define NV50_PCONNECTOR_HOTPLUG_INTR_UNPLUG_I2C3 (1<<19)
++# define NV50_PCONNECTOR_HOTPLUG_CTRL 0x0000e054
++# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C0 (1<<0)
++# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C1 (1<<1)
++# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C2 (1<<2)
++# define NV50_PCONNECTOR_HOTPLUG_CTRL_PLUG_I2C3 (1<<3)
++# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C0 (1<<16)
++# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C1 (1<<17)
++# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C2 (1<<18)
++# define NV50_PCONNECTOR_HOTPLUG_CTRL_UNPLUG_I2C3 (1<<19)
++# define NV50_PCONNECTOR_HOTPLUG_STATE 0x0000e104
++# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C0 (1<<2)
++# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C1 (1<<6)
++# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C2 (1<<10)
++# define NV50_PCONNECTOR_HOTPLUG_STATE_PIN_CONNECTED_I2C3 (1<<14)
++# define NV50_PCONNECTOR_I2C_PORT_0 0x0000e138
++# define NV50_PCONNECTOR_I2C_PORT_1 0x0000e150
++# define NV50_PCONNECTOR_I2C_PORT_2 0x0000e168
++# define NV50_PCONNECTOR_I2C_PORT_3 0x0000e180
++# define NV50_PCONNECTOR_I2C_PORT_4 0x0000e240
++# define NV50_PCONNECTOR_I2C_PORT_5 0x0000e258
++
++#define NV50_AUXCH_DATA_OUT(i,n) ((n) * 4 + (i) * 0x50 + 0x0000e4c0)
++#define NV50_AUXCH_DATA_OUT__SIZE 4
++#define NV50_AUXCH_DATA_IN(i,n) ((n) * 4 + (i) * 0x50 + 0x0000e4d0)
++#define NV50_AUXCH_DATA_IN__SIZE 4
++#define NV50_AUXCH_ADDR(i) ((i) * 0x50 + 0x0000e4e0)
++#define NV50_AUXCH_CTRL(i) ((i) * 0x50 + 0x0000e4e4)
++#define NV50_AUXCH_CTRL_LINKSTAT 0x01000000
++#define NV50_AUXCH_CTRL_LINKSTAT_NOT_READY 0x00000000
++#define NV50_AUXCH_CTRL_LINKSTAT_READY 0x01000000
++#define NV50_AUXCH_CTRL_LINKEN 0x00100000
++#define NV50_AUXCH_CTRL_LINKEN_DISABLED 0x00000000
++#define NV50_AUXCH_CTRL_LINKEN_ENABLED 0x00100000
++#define NV50_AUXCH_CTRL_EXEC 0x00010000
++#define NV50_AUXCH_CTRL_EXEC_COMPLETE 0x00000000
++#define NV50_AUXCH_CTRL_EXEC_IN_PROCESS 0x00010000
++#define NV50_AUXCH_CTRL_CMD 0x0000f000
++#define NV50_AUXCH_CTRL_CMD_SHIFT 12
++#define NV50_AUXCH_CTRL_LEN 0x0000000f
++#define NV50_AUXCH_CTRL_LEN_SHIFT 0
++#define NV50_AUXCH_STAT(i) ((i) * 0x50 + 0x0000e4e8)
++#define NV50_AUXCH_STAT_STATE 0x10000000
++#define NV50_AUXCH_STAT_STATE_NOT_READY 0x00000000
++#define NV50_AUXCH_STAT_STATE_READY 0x10000000
++#define NV50_AUXCH_STAT_REPLY 0x000f0000
++#define NV50_AUXCH_STAT_REPLY_AUX 0x00030000
++#define NV50_AUXCH_STAT_REPLY_AUX_ACK 0x00000000
++#define NV50_AUXCH_STAT_REPLY_AUX_NACK 0x00010000
++#define NV50_AUXCH_STAT_REPLY_AUX_DEFER 0x00020000
++#define NV50_AUXCH_STAT_REPLY_I2C 0x000c0000
++#define NV50_AUXCH_STAT_REPLY_I2C_ACK 0x00000000
++#define NV50_AUXCH_STAT_REPLY_I2C_NACK 0x00040000
++#define NV50_AUXCH_STAT_REPLY_I2C_DEFER 0x00080000
++#define NV50_AUXCH_STAT_COUNT 0x0000001f
++
++#define NV50_PBUS 0x00088000
++#define NV50_PBUS__LEN 0x1
++#define NV50_PBUS__ESIZE 0x1000
++# define NV50_PBUS_PCI_ID 0x00088000
++# define NV50_PBUS_PCI_ID_VENDOR_ID 0x0000ffff
++# define NV50_PBUS_PCI_ID_VENDOR_ID__SHIFT 0
++# define NV50_PBUS_PCI_ID_DEVICE_ID 0xffff0000
++# define NV50_PBUS_PCI_ID_DEVICE_ID__SHIFT 16
++
++#define NV50_PFB 0x00100000
++#define NV50_PFB__LEN 0x1
++#define NV50_PFB__ESIZE 0x1000
++
++#define NV50_PEXTDEV 0x00101000
++#define NV50_PEXTDEV__LEN 0x1
++#define NV50_PEXTDEV__ESIZE 0x1000
++
++#define NV50_PROM 0x00300000
++#define NV50_PROM__LEN 0x1
++#define NV50_PROM__ESIZE 0x10000
++
++#define NV50_PGRAPH 0x00400000
++#define NV50_PGRAPH__LEN 0x1
++#define NV50_PGRAPH__ESIZE 0x10000
++
++#define NV50_PDISPLAY 0x00610000
++#define NV50_PDISPLAY_OBJECTS 0x00610010
++#define NV50_PDISPLAY_INTR_0 0x00610020
++#define NV50_PDISPLAY_INTR_1 0x00610024
++#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC 0x0000000c
++#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_SHIFT 2
++#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(n) (1 << ((n) + 2))
++#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_0 0x00000004
++#define NV50_PDISPLAY_INTR_1_VBLANK_CRTC_1 0x00000008
++#define NV50_PDISPLAY_INTR_1_CLK_UNK10 0x00000010
++#define NV50_PDISPLAY_INTR_1_CLK_UNK20 0x00000020
++#define NV50_PDISPLAY_INTR_1_CLK_UNK40 0x00000040
++#define NV50_PDISPLAY_INTR_EN 0x0061002c
++#define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC 0x0000000c
++#define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_(n) (1 << ((n) + 2))
++#define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_0 0x00000004
++#define NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_1 0x00000008
++#define NV50_PDISPLAY_INTR_EN_CLK_UNK10 0x00000010
++#define NV50_PDISPLAY_INTR_EN_CLK_UNK20 0x00000020
++#define NV50_PDISPLAY_INTR_EN_CLK_UNK40 0x00000040
++#define NV50_PDISPLAY_UNK30_CTRL 0x00610030
++#define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK0 0x00000200
++#define NV50_PDISPLAY_UNK30_CTRL_UPDATE_VCLK1 0x00000400
++#define NV50_PDISPLAY_UNK30_CTRL_PENDING 0x80000000
++#define NV50_PDISPLAY_TRAPPED_ADDR 0x00610080
++#define NV50_PDISPLAY_TRAPPED_DATA 0x00610084
++#define NV50_PDISPLAY_CHANNEL_STAT(i) ((i) * 0x10 + 0x00610200)
++#define NV50_PDISPLAY_CHANNEL_STAT_DMA 0x00000010
++#define NV50_PDISPLAY_CHANNEL_STAT_DMA_DISABLED 0x00000000
++#define NV50_PDISPLAY_CHANNEL_STAT_DMA_ENABLED 0x00000010
++#define NV50_PDISPLAY_CHANNEL_DMA_CB(i) ((i) * 0x10 + 0x00610204)
++#define NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION 0x00000002
++#define NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION_VRAM 0x00000000
++#define NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION_SYSTEM 0x00000002
++#define NV50_PDISPLAY_CHANNEL_DMA_CB_VALID 0x00000001
++#define NV50_PDISPLAY_CHANNEL_UNK2(i) ((i) * 0x10 + 0x00610208)
++#define NV50_PDISPLAY_CHANNEL_UNK3(i) ((i) * 0x10 + 0x0061020c)
++
++#define NV50_PDISPLAY_CURSOR 0x00610270
++#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i) ((i) * 0x10 + 0x00610270)
++#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON 0x00000001
++#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS 0x00030000
++#define NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE 0x00010000
++
++#define NV50_PDISPLAY_CTRL_STATE 0x00610300
++#define NV50_PDISPLAY_CTRL_STATE_PENDING 0x80000000
++#define NV50_PDISPLAY_CTRL_STATE_METHOD 0x00001ffc
++#define NV50_PDISPLAY_CTRL_STATE_ENABLE 0x00000001
++#define NV50_PDISPLAY_CTRL_VAL 0x00610304
++#define NV50_PDISPLAY_UNK_380 0x00610380
++#define NV50_PDISPLAY_RAM_AMOUNT 0x00610384
++#define NV50_PDISPLAY_UNK_388 0x00610388
++#define NV50_PDISPLAY_UNK_38C 0x0061038c
++
++#define NV50_PDISPLAY_CRTC_P(i, r) ((i) * 0x540 + NV50_PDISPLAY_CRTC_##r)
++#define NV50_PDISPLAY_CRTC_C(i, r) (4 + (i) * 0x540 + NV50_PDISPLAY_CRTC_##r)
++#define NV50_PDISPLAY_CRTC_UNK_0A18 /* mthd 0x0900 */ 0x00610a18
++#define NV50_PDISPLAY_CRTC_CLUT_MODE 0x00610a24
++#define NV50_PDISPLAY_CRTC_INTERLACE 0x00610a48
++#define NV50_PDISPLAY_CRTC_SCALE_CTRL 0x00610a50
++#define NV50_PDISPLAY_CRTC_CURSOR_CTRL 0x00610a58
++#define NV50_PDISPLAY_CRTC_UNK0A78 /* mthd 0x0904 */ 0x00610a78
++#define NV50_PDISPLAY_CRTC_UNK0AB8 0x00610ab8
++#define NV50_PDISPLAY_CRTC_DEPTH 0x00610ac8
++#define NV50_PDISPLAY_CRTC_CLOCK 0x00610ad0
++#define NV50_PDISPLAY_CRTC_COLOR_CTRL 0x00610ae0
++#define NV50_PDISPLAY_CRTC_SYNC_START_TO_BLANK_END 0x00610ae8
++#define NV50_PDISPLAY_CRTC_MODE_UNK1 0x00610af0
++#define NV50_PDISPLAY_CRTC_DISPLAY_TOTAL 0x00610af8
++#define NV50_PDISPLAY_CRTC_SYNC_DURATION 0x00610b00
++#define NV50_PDISPLAY_CRTC_MODE_UNK2 0x00610b08
++#define NV50_PDISPLAY_CRTC_UNK_0B10 /* mthd 0x0828 */ 0x00610b10
++#define NV50_PDISPLAY_CRTC_FB_SIZE 0x00610b18
++#define NV50_PDISPLAY_CRTC_FB_PITCH 0x00610b20
++#define NV50_PDISPLAY_CRTC_FB_PITCH_LINEAR 0x00100000
++#define NV50_PDISPLAY_CRTC_FB_POS 0x00610b28
++#define NV50_PDISPLAY_CRTC_SCALE_CENTER_OFFSET 0x00610b38
++#define NV50_PDISPLAY_CRTC_REAL_RES 0x00610b40
++#define NV50_PDISPLAY_CRTC_SCALE_RES1 0x00610b48
++#define NV50_PDISPLAY_CRTC_SCALE_RES2 0x00610b50
++
++#define NV50_PDISPLAY_DAC_MODE_CTRL_P(i) (0x00610b58 + (i) * 0x8)
++#define NV50_PDISPLAY_DAC_MODE_CTRL_C(i) (0x00610b5c + (i) * 0x8)
++#define NV50_PDISPLAY_SOR_MODE_CTRL_P(i) (0x00610b70 + (i) * 0x8)
++#define NV50_PDISPLAY_SOR_MODE_CTRL_C(i) (0x00610b74 + (i) * 0x8)
++#define NV50_PDISPLAY_DAC_MODE_CTRL2_P(i) (0x00610bdc + (i) * 0x8)
++#define NV50_PDISPLAY_DAC_MODE_CTRL2_C(i) (0x00610be0 + (i) * 0x8)
++
++#define NV90_PDISPLAY_SOR_MODE_CTRL_P(i) (0x00610794 + (i) * 0x8)
++#define NV90_PDISPLAY_SOR_MODE_CTRL_C(i) (0x00610798 + (i) * 0x8)
++#define NV90_PDISPLAY_DAC_MODE_CTRL_P(i) (0x00610b58 + (i) * 0x8)
++#define NV90_PDISPLAY_DAC_MODE_CTRL_C(i) (0x00610b5c + (i) * 0x8)
++#define NV90_PDISPLAY_DAC_MODE_CTRL2_P(i) (0x00610b80 + (i) * 0x8)
++#define NV90_PDISPLAY_DAC_MODE_CTRL2_C(i) (0x00610b84 + (i) * 0x8)
++
++#define NV50_PDISPLAY_CRTC_CLK 0x00614000
++#define NV50_PDISPLAY_CRTC_CLK_CTRL1(i) ((i) * 0x800 + 0x614100)
++#define NV50_PDISPLAY_CRTC_CLK_CTRL1_CONNECTED 0x00000600
++#define NV50_PDISPLAY_CRTC_CLK_VPLL_A(i) ((i) * 0x800 + 0x614104)
++#define NV50_PDISPLAY_CRTC_CLK_VPLL_B(i) ((i) * 0x800 + 0x614108)
++#define NV50_PDISPLAY_CRTC_CLK_CTRL2(i) ((i) * 0x800 + 0x614200)
++
++#define NV50_PDISPLAY_DAC_CLK 0x00614000
++#define NV50_PDISPLAY_DAC_CLK_CTRL2(i) ((i) * 0x800 + 0x614280)
++
++#define NV50_PDISPLAY_SOR_CLK 0x00614000
++#define NV50_PDISPLAY_SOR_CLK_CTRL2(i) ((i) * 0x800 + 0x614300)
++
++#define NV50_PDISPLAY_VGACRTC(r) ((r) + 0x619400)
++
++#define NV50_PDISPLAY_DAC 0x0061a000
++#define NV50_PDISPLAY_DAC_DPMS_CTRL(i) (0x0061a004 + (i) * 0x800)
++#define NV50_PDISPLAY_DAC_DPMS_CTRL_HSYNC_OFF 0x00000001
++#define NV50_PDISPLAY_DAC_DPMS_CTRL_VSYNC_OFF 0x00000004
++#define NV50_PDISPLAY_DAC_DPMS_CTRL_BLANKED 0x00000010
++#define NV50_PDISPLAY_DAC_DPMS_CTRL_OFF 0x00000040
++#define NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING 0x80000000
++#define NV50_PDISPLAY_DAC_LOAD_CTRL(i) (0x0061a00c + (i) * 0x800)
++#define NV50_PDISPLAY_DAC_LOAD_CTRL_ACTIVE 0x00100000
++#define NV50_PDISPLAY_DAC_LOAD_CTRL_PRESENT 0x38000000
++#define NV50_PDISPLAY_DAC_LOAD_CTRL_DONE 0x80000000
++#define NV50_PDISPLAY_DAC_CLK_CTRL1(i) (0x0061a010 + (i) * 0x800)
++#define NV50_PDISPLAY_DAC_CLK_CTRL1_CONNECTED 0x00000600
++
++#define NV50_PDISPLAY_SOR 0x0061c000
++#define NV50_PDISPLAY_SOR_DPMS_CTRL(i) (0x0061c004 + (i) * 0x800)
++#define NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING 0x80000000
++#define NV50_PDISPLAY_SOR_DPMS_CTRL_ON 0x00000001
++#define NV50_PDISPLAY_SOR_CLK_CTRL1(i) (0x0061c008 + (i) * 0x800)
++#define NV50_PDISPLAY_SOR_CLK_CTRL1_CONNECTED 0x00000600
++#define NV50_PDISPLAY_SOR_DPMS_STATE(i) (0x0061c030 + (i) * 0x800)
++#define NV50_PDISPLAY_SOR_DPMS_STATE_ACTIVE 0x00030000
++#define NV50_PDISPLAY_SOR_DPMS_STATE_BLANKED 0x00080000
++#define NV50_PDISPLAY_SOR_DPMS_STATE_WAIT 0x10000000
++#define NV50_PDISPLAY_SOR_BACKLIGHT 0x0061c084
++#define NV50_PDISPLAY_SOR_BACKLIGHT_ENABLE 0x80000000
++#define NV50_PDISPLAY_SOR_BACKLIGHT_LEVEL 0x00000fff
++#define NV50_SOR_DP_CTRL(i,l) (0x0061c10c + (i) * 0x800 + (l) * 0x80)
++#define NV50_SOR_DP_CTRL_ENHANCED_FRAME_ENABLED 0x00004000
++#define NV50_SOR_DP_CTRL_LANE_MASK 0x001f0000
++#define NV50_SOR_DP_CTRL_LANE_0_ENABLED 0x00010000
++#define NV50_SOR_DP_CTRL_LANE_1_ENABLED 0x00020000
++#define NV50_SOR_DP_CTRL_LANE_2_ENABLED 0x00040000
++#define NV50_SOR_DP_CTRL_LANE_3_ENABLED 0x00080000
++#define NV50_SOR_DP_CTRL_TRAINING_PATTERN 0x0f000000
++#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_DISABLED 0x00000000
++#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_1 0x01000000
++#define NV50_SOR_DP_CTRL_TRAINING_PATTERN_2 0x02000000
++#define NV50_SOR_DP_UNK118(i,l) (0x0061c118 + (i) * 0x800 + (l) * 0x80)
++#define NV50_SOR_DP_UNK120(i,l) (0x0061c120 + (i) * 0x800 + (l) * 0x80)
++#define NV50_SOR_DP_UNK130(i,l) (0x0061c130 + (i) * 0x800 + (l) * 0x80)
++
++#define NV50_PDISPLAY_USER(i) ((i) * 0x1000 + 0x00640000)
++#define NV50_PDISPLAY_USER_PUT(i) ((i) * 0x1000 + 0x00640000)
++#define NV50_PDISPLAY_USER_GET(i) ((i) * 0x1000 + 0x00640004)
++
++#define NV50_PDISPLAY_CURSOR_USER 0x00647000
++#define NV50_PDISPLAY_CURSOR_USER_POS_CTRL(i) ((i) * 0x1000 + 0x00647080)
++#define NV50_PDISPLAY_CURSOR_USER_POS(i) ((i) * 0x1000 + 0x00647084)
+diff --git a/drivers/gpu/drm/nouveau/nouveau_sgdma.c b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
+new file mode 100644
+index 0000000..ed15905
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_sgdma.c
+@@ -0,0 +1,322 @@
++#include "drmP.h"
++#include "nouveau_drv.h"
++#include <linux/pagemap.h>
++
++#define NV_CTXDMA_PAGE_SHIFT 12
++#define NV_CTXDMA_PAGE_SIZE (1 << NV_CTXDMA_PAGE_SHIFT)
++#define NV_CTXDMA_PAGE_MASK (NV_CTXDMA_PAGE_SIZE - 1)
++
++struct nouveau_sgdma_be {
++ struct ttm_backend backend;
++ struct drm_device *dev;
++
++ dma_addr_t *pages;
++ unsigned nr_pages;
++
++ unsigned pte_start;
++ bool bound;
++};
++
++static int
++nouveau_sgdma_populate(struct ttm_backend *be, unsigned long num_pages,
++ struct page **pages, struct page *dummy_read_page)
++{
++ struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
++ struct drm_device *dev = nvbe->dev;
++
++ NV_DEBUG(nvbe->dev, "num_pages = %ld\n", num_pages);
++
++ if (nvbe->pages)
++ return -EINVAL;
++
++ nvbe->pages = kmalloc(sizeof(dma_addr_t) * num_pages, GFP_KERNEL);
++ if (!nvbe->pages)
++ return -ENOMEM;
++
++ nvbe->nr_pages = 0;
++ while (num_pages--) {
++ nvbe->pages[nvbe->nr_pages] =
++ pci_map_page(dev->pdev, pages[nvbe->nr_pages], 0,
++ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
++ if (pci_dma_mapping_error(dev->pdev,
++ nvbe->pages[nvbe->nr_pages])) {
++ be->func->clear(be);
++ return -EFAULT;
++ }
++
++ nvbe->nr_pages++;
++ }
++
++ return 0;
++}
++
++static void
++nouveau_sgdma_clear(struct ttm_backend *be)
++{
++ struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
++ struct drm_device *dev;
++
++ if (nvbe && nvbe->pages) {
++ dev = nvbe->dev;
++ NV_DEBUG(dev, "\n");
++
++ if (nvbe->bound)
++ be->func->unbind(be);
++
++ while (nvbe->nr_pages--) {
++ pci_unmap_page(dev->pdev, nvbe->pages[nvbe->nr_pages],
++ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
++ }
++ kfree(nvbe->pages);
++ nvbe->pages = NULL;
++ nvbe->nr_pages = 0;
++ }
++}
++
++static inline unsigned
++nouveau_sgdma_pte(struct drm_device *dev, uint64_t offset)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ unsigned pte = (offset >> NV_CTXDMA_PAGE_SHIFT);
++
++ if (dev_priv->card_type < NV_50)
++ return pte + 2;
++
++ return pte << 1;
++}
++
++static int
++nouveau_sgdma_bind(struct ttm_backend *be, struct ttm_mem_reg *mem)
++{
++ struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
++ struct drm_device *dev = nvbe->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma;
++ unsigned i, j, pte;
++
++ NV_DEBUG(dev, "pg=0x%lx\n", mem->mm_node->start);
++
++ dev_priv->engine.instmem.prepare_access(nvbe->dev, true);
++ pte = nouveau_sgdma_pte(nvbe->dev, mem->mm_node->start << PAGE_SHIFT);
++ nvbe->pte_start = pte;
++ for (i = 0; i < nvbe->nr_pages; i++) {
++ dma_addr_t dma_offset = nvbe->pages[i];
++ uint32_t offset_l = lower_32_bits(dma_offset);
++ uint32_t offset_h = upper_32_bits(dma_offset);
++
++ for (j = 0; j < PAGE_SIZE / NV_CTXDMA_PAGE_SIZE; j++) {
++ if (dev_priv->card_type < NV_50)
++ nv_wo32(dev, gpuobj, pte++, offset_l | 3);
++ else {
++ nv_wo32(dev, gpuobj, pte++, offset_l | 0x21);
++ nv_wo32(dev, gpuobj, pte++, offset_h & 0xff);
++ }
++
++ dma_offset += NV_CTXDMA_PAGE_SIZE;
++ }
++ }
++ dev_priv->engine.instmem.finish_access(nvbe->dev);
++
++ if (dev_priv->card_type == NV_50) {
++ nv_wr32(dev, 0x100c80, 0x00050001);
++ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
++ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
++ NV_ERROR(dev, "0x100c80 = 0x%08x\n",
++ nv_rd32(dev, 0x100c80));
++ return -EBUSY;
++ }
++
++ nv_wr32(dev, 0x100c80, 0x00000001);
++ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
++ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
++ NV_ERROR(dev, "0x100c80 = 0x%08x\n",
++ nv_rd32(dev, 0x100c80));
++ return -EBUSY;
++ }
++ }
++
++ nvbe->bound = true;
++ return 0;
++}
++
++static int
++nouveau_sgdma_unbind(struct ttm_backend *be)
++{
++ struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
++ struct drm_device *dev = nvbe->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma;
++ unsigned i, j, pte;
++
++ NV_DEBUG(dev, "\n");
++
++ if (!nvbe->bound)
++ return 0;
++
++ dev_priv->engine.instmem.prepare_access(nvbe->dev, true);
++ pte = nvbe->pte_start;
++ for (i = 0; i < nvbe->nr_pages; i++) {
++ dma_addr_t dma_offset = dev_priv->gart_info.sg_dummy_bus;
++
++ for (j = 0; j < PAGE_SIZE / NV_CTXDMA_PAGE_SIZE; j++) {
++ if (dev_priv->card_type < NV_50)
++ nv_wo32(dev, gpuobj, pte++, dma_offset | 3);
++ else {
++ nv_wo32(dev, gpuobj, pte++, dma_offset | 0x21);
++ nv_wo32(dev, gpuobj, pte++, 0x00000000);
++ }
++
++ dma_offset += NV_CTXDMA_PAGE_SIZE;
++ }
++ }
++ dev_priv->engine.instmem.finish_access(nvbe->dev);
++
++ nvbe->bound = false;
++ return 0;
++}
++
++static void
++nouveau_sgdma_destroy(struct ttm_backend *be)
++{
++ struct nouveau_sgdma_be *nvbe = (struct nouveau_sgdma_be *)be;
++
++ if (be) {
++ NV_DEBUG(nvbe->dev, "\n");
++
++ if (nvbe) {
++ if (nvbe->pages)
++ be->func->clear(be);
++ kfree(nvbe);
++ }
++ }
++}
++
++static struct ttm_backend_func nouveau_sgdma_backend = {
++ .populate = nouveau_sgdma_populate,
++ .clear = nouveau_sgdma_clear,
++ .bind = nouveau_sgdma_bind,
++ .unbind = nouveau_sgdma_unbind,
++ .destroy = nouveau_sgdma_destroy
++};
++
++struct ttm_backend *
++nouveau_sgdma_init_ttm(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_sgdma_be *nvbe;
++
++ if (!dev_priv->gart_info.sg_ctxdma)
++ return NULL;
++
++ nvbe = kzalloc(sizeof(*nvbe), GFP_KERNEL);
++ if (!nvbe)
++ return NULL;
++
++ nvbe->dev = dev;
++
++ nvbe->backend.func = &nouveau_sgdma_backend;
++
++ return &nvbe->backend;
++}
++
++int
++nouveau_sgdma_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *gpuobj = NULL;
++ uint32_t aper_size, obj_size;
++ int i, ret;
++
++ if (dev_priv->card_type < NV_50) {
++ aper_size = (64 * 1024 * 1024);
++ obj_size = (aper_size >> NV_CTXDMA_PAGE_SHIFT) * 4;
++ obj_size += 8; /* ctxdma header */
++ } else {
++ /* 1 entire VM page table */
++ aper_size = (512 * 1024 * 1024);
++ obj_size = (aper_size >> NV_CTXDMA_PAGE_SHIFT) * 8;
++ }
++
++ ret = nouveau_gpuobj_new(dev, NULL, obj_size, 16,
++ NVOBJ_FLAG_ALLOW_NO_REFS |
++ NVOBJ_FLAG_ZERO_ALLOC |
++ NVOBJ_FLAG_ZERO_FREE, &gpuobj);
++ if (ret) {
++ NV_ERROR(dev, "Error creating sgdma object: %d\n", ret);
++ return ret;
++ }
++
++ dev_priv->gart_info.sg_dummy_page =
++ alloc_page(GFP_KERNEL|__GFP_DMA32);
++ set_bit(PG_locked, &dev_priv->gart_info.sg_dummy_page->flags);
++ dev_priv->gart_info.sg_dummy_bus =
++ pci_map_page(dev->pdev, dev_priv->gart_info.sg_dummy_page, 0,
++ PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
++
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ if (dev_priv->card_type < NV_50) {
++ /* Maybe use NV_DMA_TARGET_AGP for PCIE? NVIDIA do this, and
++ * confirmed to work on c51. Perhaps means NV_DMA_TARGET_PCIE
++ * on those cards? */
++ nv_wo32(dev, gpuobj, 0, NV_CLASS_DMA_IN_MEMORY |
++ (1 << 12) /* PT present */ |
++ (0 << 13) /* PT *not* linear */ |
++ (NV_DMA_ACCESS_RW << 14) |
++ (NV_DMA_TARGET_PCI << 16));
++ nv_wo32(dev, gpuobj, 1, aper_size - 1);
++ for (i = 2; i < 2 + (aper_size >> 12); i++) {
++ nv_wo32(dev, gpuobj, i,
++ dev_priv->gart_info.sg_dummy_bus | 3);
++ }
++ } else {
++ for (i = 0; i < obj_size; i += 8) {
++ nv_wo32(dev, gpuobj, (i+0)/4,
++ dev_priv->gart_info.sg_dummy_bus | 0x21);
++ nv_wo32(dev, gpuobj, (i+4)/4, 0);
++ }
++ }
++ dev_priv->engine.instmem.finish_access(dev);
++
++ dev_priv->gart_info.type = NOUVEAU_GART_SGDMA;
++ dev_priv->gart_info.aper_base = 0;
++ dev_priv->gart_info.aper_size = aper_size;
++ dev_priv->gart_info.sg_ctxdma = gpuobj;
++ return 0;
++}
++
++void
++nouveau_sgdma_takedown(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ if (dev_priv->gart_info.sg_dummy_page) {
++ pci_unmap_page(dev->pdev, dev_priv->gart_info.sg_dummy_bus,
++ NV_CTXDMA_PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
++ unlock_page(dev_priv->gart_info.sg_dummy_page);
++ __free_page(dev_priv->gart_info.sg_dummy_page);
++ dev_priv->gart_info.sg_dummy_page = NULL;
++ dev_priv->gart_info.sg_dummy_bus = 0;
++ }
++
++ nouveau_gpuobj_del(dev, &dev_priv->gart_info.sg_ctxdma);
++}
++
++int
++nouveau_sgdma_get_page(struct drm_device *dev, uint32_t offset, uint32_t *page)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *gpuobj = dev_priv->gart_info.sg_ctxdma;
++ struct nouveau_instmem_engine *instmem = &dev_priv->engine.instmem;
++ int pte;
++
++ pte = (offset >> NV_CTXDMA_PAGE_SHIFT);
++ if (dev_priv->card_type < NV_50) {
++ instmem->prepare_access(dev, false);
++ *page = nv_ro32(dev, gpuobj, (pte + 2)) & ~NV_CTXDMA_PAGE_MASK;
++ instmem->finish_access(dev);
++ return 0;
++ }
++
++ NV_ERROR(dev, "Unimplemented on NV50\n");
++ return -EINVAL;
++}
+diff --git a/drivers/gpu/drm/nouveau/nouveau_state.c b/drivers/gpu/drm/nouveau/nouveau_state.c
+new file mode 100644
+index 0000000..a4851af
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_state.c
+@@ -0,0 +1,897 @@
++/*
++ * Copyright 2005 Stephane Marchesin
++ * Copyright 2008 Stuart Bennett
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#include <linux/swab.h>
++#include "drmP.h"
++#include "drm.h"
++#include "drm_sarea.h"
++#include "drm_crtc_helper.h"
++#include <linux/vgaarb.h>
++
++#include "nouveau_drv.h"
++#include "nouveau_drm.h"
++#include "nv50_display.h"
++
++static int nouveau_stub_init(struct drm_device *dev) { return 0; }
++static void nouveau_stub_takedown(struct drm_device *dev) {}
++
++static int nouveau_init_engine_ptrs(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_engine *engine = &dev_priv->engine;
++
++ switch (dev_priv->chipset & 0xf0) {
++ case 0x00:
++ engine->instmem.init = nv04_instmem_init;
++ engine->instmem.takedown = nv04_instmem_takedown;
++ engine->instmem.suspend = nv04_instmem_suspend;
++ engine->instmem.resume = nv04_instmem_resume;
++ engine->instmem.populate = nv04_instmem_populate;
++ engine->instmem.clear = nv04_instmem_clear;
++ engine->instmem.bind = nv04_instmem_bind;
++ engine->instmem.unbind = nv04_instmem_unbind;
++ engine->instmem.prepare_access = nv04_instmem_prepare_access;
++ engine->instmem.finish_access = nv04_instmem_finish_access;
++ engine->mc.init = nv04_mc_init;
++ engine->mc.takedown = nv04_mc_takedown;
++ engine->timer.init = nv04_timer_init;
++ engine->timer.read = nv04_timer_read;
++ engine->timer.takedown = nv04_timer_takedown;
++ engine->fb.init = nv04_fb_init;
++ engine->fb.takedown = nv04_fb_takedown;
++ engine->graph.grclass = nv04_graph_grclass;
++ engine->graph.init = nv04_graph_init;
++ engine->graph.takedown = nv04_graph_takedown;
++ engine->graph.fifo_access = nv04_graph_fifo_access;
++ engine->graph.channel = nv04_graph_channel;
++ engine->graph.create_context = nv04_graph_create_context;
++ engine->graph.destroy_context = nv04_graph_destroy_context;
++ engine->graph.load_context = nv04_graph_load_context;
++ engine->graph.unload_context = nv04_graph_unload_context;
++ engine->fifo.channels = 16;
++ engine->fifo.init = nv04_fifo_init;
++ engine->fifo.takedown = nouveau_stub_takedown;
++ engine->fifo.disable = nv04_fifo_disable;
++ engine->fifo.enable = nv04_fifo_enable;
++ engine->fifo.reassign = nv04_fifo_reassign;
++ engine->fifo.cache_flush = nv04_fifo_cache_flush;
++ engine->fifo.cache_pull = nv04_fifo_cache_pull;
++ engine->fifo.channel_id = nv04_fifo_channel_id;
++ engine->fifo.create_context = nv04_fifo_create_context;
++ engine->fifo.destroy_context = nv04_fifo_destroy_context;
++ engine->fifo.load_context = nv04_fifo_load_context;
++ engine->fifo.unload_context = nv04_fifo_unload_context;
++ break;
++ case 0x10:
++ engine->instmem.init = nv04_instmem_init;
++ engine->instmem.takedown = nv04_instmem_takedown;
++ engine->instmem.suspend = nv04_instmem_suspend;
++ engine->instmem.resume = nv04_instmem_resume;
++ engine->instmem.populate = nv04_instmem_populate;
++ engine->instmem.clear = nv04_instmem_clear;
++ engine->instmem.bind = nv04_instmem_bind;
++ engine->instmem.unbind = nv04_instmem_unbind;
++ engine->instmem.prepare_access = nv04_instmem_prepare_access;
++ engine->instmem.finish_access = nv04_instmem_finish_access;
++ engine->mc.init = nv04_mc_init;
++ engine->mc.takedown = nv04_mc_takedown;
++ engine->timer.init = nv04_timer_init;
++ engine->timer.read = nv04_timer_read;
++ engine->timer.takedown = nv04_timer_takedown;
++ engine->fb.init = nv10_fb_init;
++ engine->fb.takedown = nv10_fb_takedown;
++ engine->fb.set_region_tiling = nv10_fb_set_region_tiling;
++ engine->graph.grclass = nv10_graph_grclass;
++ engine->graph.init = nv10_graph_init;
++ engine->graph.takedown = nv10_graph_takedown;
++ engine->graph.channel = nv10_graph_channel;
++ engine->graph.create_context = nv10_graph_create_context;
++ engine->graph.destroy_context = nv10_graph_destroy_context;
++ engine->graph.fifo_access = nv04_graph_fifo_access;
++ engine->graph.load_context = nv10_graph_load_context;
++ engine->graph.unload_context = nv10_graph_unload_context;
++ engine->graph.set_region_tiling = nv10_graph_set_region_tiling;
++ engine->fifo.channels = 32;
++ engine->fifo.init = nv10_fifo_init;
++ engine->fifo.takedown = nouveau_stub_takedown;
++ engine->fifo.disable = nv04_fifo_disable;
++ engine->fifo.enable = nv04_fifo_enable;
++ engine->fifo.reassign = nv04_fifo_reassign;
++ engine->fifo.cache_flush = nv04_fifo_cache_flush;
++ engine->fifo.cache_pull = nv04_fifo_cache_pull;
++ engine->fifo.channel_id = nv10_fifo_channel_id;
++ engine->fifo.create_context = nv10_fifo_create_context;
++ engine->fifo.destroy_context = nv10_fifo_destroy_context;
++ engine->fifo.load_context = nv10_fifo_load_context;
++ engine->fifo.unload_context = nv10_fifo_unload_context;
++ break;
++ case 0x20:
++ engine->instmem.init = nv04_instmem_init;
++ engine->instmem.takedown = nv04_instmem_takedown;
++ engine->instmem.suspend = nv04_instmem_suspend;
++ engine->instmem.resume = nv04_instmem_resume;
++ engine->instmem.populate = nv04_instmem_populate;
++ engine->instmem.clear = nv04_instmem_clear;
++ engine->instmem.bind = nv04_instmem_bind;
++ engine->instmem.unbind = nv04_instmem_unbind;
++ engine->instmem.prepare_access = nv04_instmem_prepare_access;
++ engine->instmem.finish_access = nv04_instmem_finish_access;
++ engine->mc.init = nv04_mc_init;
++ engine->mc.takedown = nv04_mc_takedown;
++ engine->timer.init = nv04_timer_init;
++ engine->timer.read = nv04_timer_read;
++ engine->timer.takedown = nv04_timer_takedown;
++ engine->fb.init = nv10_fb_init;
++ engine->fb.takedown = nv10_fb_takedown;
++ engine->fb.set_region_tiling = nv10_fb_set_region_tiling;
++ engine->graph.grclass = nv20_graph_grclass;
++ engine->graph.init = nv20_graph_init;
++ engine->graph.takedown = nv20_graph_takedown;
++ engine->graph.channel = nv10_graph_channel;
++ engine->graph.create_context = nv20_graph_create_context;
++ engine->graph.destroy_context = nv20_graph_destroy_context;
++ engine->graph.fifo_access = nv04_graph_fifo_access;
++ engine->graph.load_context = nv20_graph_load_context;
++ engine->graph.unload_context = nv20_graph_unload_context;
++ engine->graph.set_region_tiling = nv20_graph_set_region_tiling;
++ engine->fifo.channels = 32;
++ engine->fifo.init = nv10_fifo_init;
++ engine->fifo.takedown = nouveau_stub_takedown;
++ engine->fifo.disable = nv04_fifo_disable;
++ engine->fifo.enable = nv04_fifo_enable;
++ engine->fifo.reassign = nv04_fifo_reassign;
++ engine->fifo.cache_flush = nv04_fifo_cache_flush;
++ engine->fifo.cache_pull = nv04_fifo_cache_pull;
++ engine->fifo.channel_id = nv10_fifo_channel_id;
++ engine->fifo.create_context = nv10_fifo_create_context;
++ engine->fifo.destroy_context = nv10_fifo_destroy_context;
++ engine->fifo.load_context = nv10_fifo_load_context;
++ engine->fifo.unload_context = nv10_fifo_unload_context;
++ break;
++ case 0x30:
++ engine->instmem.init = nv04_instmem_init;
++ engine->instmem.takedown = nv04_instmem_takedown;
++ engine->instmem.suspend = nv04_instmem_suspend;
++ engine->instmem.resume = nv04_instmem_resume;
++ engine->instmem.populate = nv04_instmem_populate;
++ engine->instmem.clear = nv04_instmem_clear;
++ engine->instmem.bind = nv04_instmem_bind;
++ engine->instmem.unbind = nv04_instmem_unbind;
++ engine->instmem.prepare_access = nv04_instmem_prepare_access;
++ engine->instmem.finish_access = nv04_instmem_finish_access;
++ engine->mc.init = nv04_mc_init;
++ engine->mc.takedown = nv04_mc_takedown;
++ engine->timer.init = nv04_timer_init;
++ engine->timer.read = nv04_timer_read;
++ engine->timer.takedown = nv04_timer_takedown;
++ engine->fb.init = nv10_fb_init;
++ engine->fb.takedown = nv10_fb_takedown;
++ engine->fb.set_region_tiling = nv10_fb_set_region_tiling;
++ engine->graph.grclass = nv30_graph_grclass;
++ engine->graph.init = nv30_graph_init;
++ engine->graph.takedown = nv20_graph_takedown;
++ engine->graph.fifo_access = nv04_graph_fifo_access;
++ engine->graph.channel = nv10_graph_channel;
++ engine->graph.create_context = nv20_graph_create_context;
++ engine->graph.destroy_context = nv20_graph_destroy_context;
++ engine->graph.load_context = nv20_graph_load_context;
++ engine->graph.unload_context = nv20_graph_unload_context;
++ engine->graph.set_region_tiling = nv20_graph_set_region_tiling;
++ engine->fifo.channels = 32;
++ engine->fifo.init = nv10_fifo_init;
++ engine->fifo.takedown = nouveau_stub_takedown;
++ engine->fifo.disable = nv04_fifo_disable;
++ engine->fifo.enable = nv04_fifo_enable;
++ engine->fifo.reassign = nv04_fifo_reassign;
++ engine->fifo.cache_flush = nv04_fifo_cache_flush;
++ engine->fifo.cache_pull = nv04_fifo_cache_pull;
++ engine->fifo.channel_id = nv10_fifo_channel_id;
++ engine->fifo.create_context = nv10_fifo_create_context;
++ engine->fifo.destroy_context = nv10_fifo_destroy_context;
++ engine->fifo.load_context = nv10_fifo_load_context;
++ engine->fifo.unload_context = nv10_fifo_unload_context;
++ break;
++ case 0x40:
++ case 0x60:
++ engine->instmem.init = nv04_instmem_init;
++ engine->instmem.takedown = nv04_instmem_takedown;
++ engine->instmem.suspend = nv04_instmem_suspend;
++ engine->instmem.resume = nv04_instmem_resume;
++ engine->instmem.populate = nv04_instmem_populate;
++ engine->instmem.clear = nv04_instmem_clear;
++ engine->instmem.bind = nv04_instmem_bind;
++ engine->instmem.unbind = nv04_instmem_unbind;
++ engine->instmem.prepare_access = nv04_instmem_prepare_access;
++ engine->instmem.finish_access = nv04_instmem_finish_access;
++ engine->mc.init = nv40_mc_init;
++ engine->mc.takedown = nv40_mc_takedown;
++ engine->timer.init = nv04_timer_init;
++ engine->timer.read = nv04_timer_read;
++ engine->timer.takedown = nv04_timer_takedown;
++ engine->fb.init = nv40_fb_init;
++ engine->fb.takedown = nv40_fb_takedown;
++ engine->fb.set_region_tiling = nv40_fb_set_region_tiling;
++ engine->graph.grclass = nv40_graph_grclass;
++ engine->graph.init = nv40_graph_init;
++ engine->graph.takedown = nv40_graph_takedown;
++ engine->graph.fifo_access = nv04_graph_fifo_access;
++ engine->graph.channel = nv40_graph_channel;
++ engine->graph.create_context = nv40_graph_create_context;
++ engine->graph.destroy_context = nv40_graph_destroy_context;
++ engine->graph.load_context = nv40_graph_load_context;
++ engine->graph.unload_context = nv40_graph_unload_context;
++ engine->graph.set_region_tiling = nv40_graph_set_region_tiling;
++ engine->fifo.channels = 32;
++ engine->fifo.init = nv40_fifo_init;
++ engine->fifo.takedown = nouveau_stub_takedown;
++ engine->fifo.disable = nv04_fifo_disable;
++ engine->fifo.enable = nv04_fifo_enable;
++ engine->fifo.reassign = nv04_fifo_reassign;
++ engine->fifo.cache_flush = nv04_fifo_cache_flush;
++ engine->fifo.cache_pull = nv04_fifo_cache_pull;
++ engine->fifo.channel_id = nv10_fifo_channel_id;
++ engine->fifo.create_context = nv40_fifo_create_context;
++ engine->fifo.destroy_context = nv40_fifo_destroy_context;
++ engine->fifo.load_context = nv40_fifo_load_context;
++ engine->fifo.unload_context = nv40_fifo_unload_context;
++ break;
++ case 0x50:
++ case 0x80: /* gotta love NVIDIA's consistency.. */
++ case 0x90:
++ case 0xA0:
++ engine->instmem.init = nv50_instmem_init;
++ engine->instmem.takedown = nv50_instmem_takedown;
++ engine->instmem.suspend = nv50_instmem_suspend;
++ engine->instmem.resume = nv50_instmem_resume;
++ engine->instmem.populate = nv50_instmem_populate;
++ engine->instmem.clear = nv50_instmem_clear;
++ engine->instmem.bind = nv50_instmem_bind;
++ engine->instmem.unbind = nv50_instmem_unbind;
++ engine->instmem.prepare_access = nv50_instmem_prepare_access;
++ engine->instmem.finish_access = nv50_instmem_finish_access;
++ engine->mc.init = nv50_mc_init;
++ engine->mc.takedown = nv50_mc_takedown;
++ engine->timer.init = nv04_timer_init;
++ engine->timer.read = nv04_timer_read;
++ engine->timer.takedown = nv04_timer_takedown;
++ engine->fb.init = nouveau_stub_init;
++ engine->fb.takedown = nouveau_stub_takedown;
++ engine->graph.grclass = nv50_graph_grclass;
++ engine->graph.init = nv50_graph_init;
++ engine->graph.takedown = nv50_graph_takedown;
++ engine->graph.fifo_access = nv50_graph_fifo_access;
++ engine->graph.channel = nv50_graph_channel;
++ engine->graph.create_context = nv50_graph_create_context;
++ engine->graph.destroy_context = nv50_graph_destroy_context;
++ engine->graph.load_context = nv50_graph_load_context;
++ engine->graph.unload_context = nv50_graph_unload_context;
++ engine->fifo.channels = 128;
++ engine->fifo.init = nv50_fifo_init;
++ engine->fifo.takedown = nv50_fifo_takedown;
++ engine->fifo.disable = nv04_fifo_disable;
++ engine->fifo.enable = nv04_fifo_enable;
++ engine->fifo.reassign = nv04_fifo_reassign;
++ engine->fifo.channel_id = nv50_fifo_channel_id;
++ engine->fifo.create_context = nv50_fifo_create_context;
++ engine->fifo.destroy_context = nv50_fifo_destroy_context;
++ engine->fifo.load_context = nv50_fifo_load_context;
++ engine->fifo.unload_context = nv50_fifo_unload_context;
++ break;
++ default:
++ NV_ERROR(dev, "NV%02x unsupported\n", dev_priv->chipset);
++ return 1;
++ }
++
++ return 0;
++}
++
++static unsigned int
++nouveau_vga_set_decode(void *priv, bool state)
++{
++ struct drm_device *dev = priv;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ if (dev_priv->chipset >= 0x40)
++ nv_wr32(dev, 0x88054, state);
++ else
++ nv_wr32(dev, 0x1854, state);
++
++ if (state)
++ return VGA_RSRC_LEGACY_IO | VGA_RSRC_LEGACY_MEM |
++ VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
++ else
++ return VGA_RSRC_NORMAL_IO | VGA_RSRC_NORMAL_MEM;
++}
++
++static int
++nouveau_card_init_channel(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *gpuobj;
++ int ret;
++
++ ret = nouveau_channel_alloc(dev, &dev_priv->channel,
++ (struct drm_file *)-2,
++ NvDmaFB, NvDmaTT);
++ if (ret)
++ return ret;
++
++ gpuobj = NULL;
++ ret = nouveau_gpuobj_dma_new(dev_priv->channel, NV_CLASS_DMA_IN_MEMORY,
++ 0, nouveau_mem_fb_amount(dev),
++ NV_DMA_ACCESS_RW, NV_DMA_TARGET_VIDMEM,
++ &gpuobj);
++ if (ret)
++ goto out_err;
++
++ ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, NvDmaVRAM,
++ gpuobj, NULL);
++ if (ret)
++ goto out_err;
++
++ gpuobj = NULL;
++ ret = nouveau_gpuobj_gart_dma_new(dev_priv->channel, 0,
++ dev_priv->gart_info.aper_size,
++ NV_DMA_ACCESS_RW, &gpuobj, NULL);
++ if (ret)
++ goto out_err;
++
++ ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, NvDmaGART,
++ gpuobj, NULL);
++ if (ret)
++ goto out_err;
++
++ return 0;
++out_err:
++ nouveau_gpuobj_del(dev, &gpuobj);
++ nouveau_channel_free(dev_priv->channel);
++ dev_priv->channel = NULL;
++ return ret;
++}
++
++int
++nouveau_card_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_engine *engine;
++ int ret;
++
++ NV_DEBUG(dev, "prev state = %d\n", dev_priv->init_state);
++
++ if (dev_priv->init_state == NOUVEAU_CARD_INIT_DONE)
++ return 0;
++
++ vga_client_register(dev->pdev, dev, NULL, nouveau_vga_set_decode);
++
++ /* Initialise internal driver API hooks */
++ ret = nouveau_init_engine_ptrs(dev);
++ if (ret)
++ goto out;
++ engine = &dev_priv->engine;
++ dev_priv->init_state = NOUVEAU_CARD_INIT_FAILED;
++
++ /* Parse BIOS tables / Run init tables if card not POSTed */
++ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
++ ret = nouveau_bios_init(dev);
++ if (ret)
++ goto out;
++ }
++
++ ret = nouveau_gpuobj_early_init(dev);
++ if (ret)
++ goto out_bios;
++
++ /* Initialise instance memory, must happen before mem_init so we
++ * know exactly how much VRAM we're able to use for "normal"
++ * purposes.
++ */
++ ret = engine->instmem.init(dev);
++ if (ret)
++ goto out_gpuobj_early;
++
++ /* Setup the memory manager */
++ ret = nouveau_mem_init(dev);
++ if (ret)
++ goto out_instmem;
++
++ ret = nouveau_gpuobj_init(dev);
++ if (ret)
++ goto out_mem;
++
++ /* PMC */
++ ret = engine->mc.init(dev);
++ if (ret)
++ goto out_gpuobj;
++
++ /* PTIMER */
++ ret = engine->timer.init(dev);
++ if (ret)
++ goto out_mc;
++
++ /* PFB */
++ ret = engine->fb.init(dev);
++ if (ret)
++ goto out_timer;
++
++ if (nouveau_noaccel)
++ engine->graph.accel_blocked = true;
++ else {
++ /* PGRAPH */
++ ret = engine->graph.init(dev);
++ if (ret)
++ goto out_fb;
++
++ /* PFIFO */
++ ret = engine->fifo.init(dev);
++ if (ret)
++ goto out_graph;
++ }
++
++ /* this call irq_preinstall, register irq handler and
++ * call irq_postinstall
++ */
++ ret = drm_irq_install(dev);
++ if (ret)
++ goto out_fifo;
++
++ ret = drm_vblank_init(dev, 0);
++ if (ret)
++ goto out_irq;
++
++ /* what about PVIDEO/PCRTC/PRAMDAC etc? */
++
++ if (!engine->graph.accel_blocked) {
++ ret = nouveau_card_init_channel(dev);
++ if (ret)
++ goto out_irq;
++ }
++
++ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
++ if (dev_priv->card_type >= NV_50)
++ ret = nv50_display_create(dev);
++ else
++ ret = nv04_display_create(dev);
++ if (ret)
++ goto out_irq;
++ }
++
++ ret = nouveau_backlight_init(dev);
++ if (ret)
++ NV_ERROR(dev, "Error %d registering backlight\n", ret);
++
++ dev_priv->init_state = NOUVEAU_CARD_INIT_DONE;
++
++ if (drm_core_check_feature(dev, DRIVER_MODESET))
++ drm_helper_initial_config(dev);
++
++ return 0;
++
++out_irq:
++ drm_irq_uninstall(dev);
++out_fifo:
++ if (!nouveau_noaccel)
++ engine->fifo.takedown(dev);
++out_graph:
++ if (!nouveau_noaccel)
++ engine->graph.takedown(dev);
++out_fb:
++ engine->fb.takedown(dev);
++out_timer:
++ engine->timer.takedown(dev);
++out_mc:
++ engine->mc.takedown(dev);
++out_gpuobj:
++ nouveau_gpuobj_takedown(dev);
++out_mem:
++ nouveau_mem_close(dev);
++out_instmem:
++ engine->instmem.takedown(dev);
++out_gpuobj_early:
++ nouveau_gpuobj_late_takedown(dev);
++out_bios:
++ nouveau_bios_takedown(dev);
++out:
++ vga_client_register(dev->pdev, NULL, NULL, NULL);
++ return ret;
++}
++
++static void nouveau_card_takedown(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_engine *engine = &dev_priv->engine;
++
++ NV_DEBUG(dev, "prev state = %d\n", dev_priv->init_state);
++
++ if (dev_priv->init_state != NOUVEAU_CARD_INIT_DOWN) {
++ nouveau_backlight_exit(dev);
++
++ if (dev_priv->channel) {
++ nouveau_channel_free(dev_priv->channel);
++ dev_priv->channel = NULL;
++ }
++
++ if (!nouveau_noaccel) {
++ engine->fifo.takedown(dev);
++ engine->graph.takedown(dev);
++ }
++ engine->fb.takedown(dev);
++ engine->timer.takedown(dev);
++ engine->mc.takedown(dev);
++
++ mutex_lock(&dev->struct_mutex);
++ ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_VRAM);
++ ttm_bo_clean_mm(&dev_priv->ttm.bdev, TTM_PL_TT);
++ mutex_unlock(&dev->struct_mutex);
++ nouveau_sgdma_takedown(dev);
++
++ nouveau_gpuobj_takedown(dev);
++ nouveau_mem_close(dev);
++ engine->instmem.takedown(dev);
++
++ if (drm_core_check_feature(dev, DRIVER_MODESET))
++ drm_irq_uninstall(dev);
++
++ nouveau_gpuobj_late_takedown(dev);
++ nouveau_bios_takedown(dev);
++
++ vga_client_register(dev->pdev, NULL, NULL, NULL);
++
++ dev_priv->init_state = NOUVEAU_CARD_INIT_DOWN;
++ }
++}
++
++/* here a client dies, release the stuff that was allocated for its
++ * file_priv */
++void nouveau_preclose(struct drm_device *dev, struct drm_file *file_priv)
++{
++ nouveau_channel_cleanup(dev, file_priv);
++}
++
++/* first module load, setup the mmio/fb mapping */
++/* KMS: we need mmio at load time, not when the first drm client opens. */
++int nouveau_firstopen(struct drm_device *dev)
++{
++ return 0;
++}
++
++/* if we have an OF card, copy vbios to RAMIN */
++static void nouveau_OF_copy_vbios_to_ramin(struct drm_device *dev)
++{
++#if defined(__powerpc__)
++ int size, i;
++ const uint32_t *bios;
++ struct device_node *dn = pci_device_to_OF_node(dev->pdev);
++ if (!dn) {
++ NV_INFO(dev, "Unable to get the OF node\n");
++ return;
++ }
++
++ bios = of_get_property(dn, "NVDA,BMP", &size);
++ if (bios) {
++ for (i = 0; i < size; i += 4)
++ nv_wi32(dev, i, bios[i/4]);
++ NV_INFO(dev, "OF bios successfully copied (%d bytes)\n", size);
++ } else {
++ NV_INFO(dev, "Unable to get the OF bios\n");
++ }
++#endif
++}
++
++int nouveau_load(struct drm_device *dev, unsigned long flags)
++{
++ struct drm_nouveau_private *dev_priv;
++ uint32_t reg0;
++ resource_size_t mmio_start_offs;
++
++ dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
++ if (!dev_priv)
++ return -ENOMEM;
++ dev->dev_private = dev_priv;
++ dev_priv->dev = dev;
++
++ dev_priv->flags = flags & NOUVEAU_FLAGS;
++ dev_priv->init_state = NOUVEAU_CARD_INIT_DOWN;
++
++ NV_DEBUG(dev, "vendor: 0x%X device: 0x%X class: 0x%X\n",
++ dev->pci_vendor, dev->pci_device, dev->pdev->class);
++
++ dev_priv->acpi_dsm = nouveau_dsm_probe(dev);
++
++ if (dev_priv->acpi_dsm)
++ nouveau_hybrid_setup(dev);
++
++ dev_priv->wq = create_workqueue("nouveau");
++ if (!dev_priv->wq)
++ return -EINVAL;
++
++ /* resource 0 is mmio regs */
++ /* resource 1 is linear FB */
++ /* resource 2 is RAMIN (mmio regs + 0x1000000) */
++ /* resource 6 is bios */
++
++ /* map the mmio regs */
++ mmio_start_offs = pci_resource_start(dev->pdev, 0);
++ dev_priv->mmio = ioremap(mmio_start_offs, 0x00800000);
++ if (!dev_priv->mmio) {
++ NV_ERROR(dev, "Unable to initialize the mmio mapping. "
++ "Please report your setup to " DRIVER_EMAIL "\n");
++ return -EINVAL;
++ }
++ NV_DEBUG(dev, "regs mapped ok at 0x%llx\n",
++ (unsigned long long)mmio_start_offs);
++
++#ifdef __BIG_ENDIAN
++ /* Put the card in BE mode if it's not */
++ if (nv_rd32(dev, NV03_PMC_BOOT_1))
++ nv_wr32(dev, NV03_PMC_BOOT_1, 0x00000001);
++
++ DRM_MEMORYBARRIER();
++#endif
++
++ /* Time to determine the card architecture */
++ reg0 = nv_rd32(dev, NV03_PMC_BOOT_0);
++
++ /* We're dealing with >=NV10 */
++ if ((reg0 & 0x0f000000) > 0) {
++ /* Bit 27-20 contain the architecture in hex */
++ dev_priv->chipset = (reg0 & 0xff00000) >> 20;
++ /* NV04 or NV05 */
++ } else if ((reg0 & 0xff00fff0) == 0x20004000) {
++ if (reg0 & 0x00f00000)
++ dev_priv->chipset = 0x05;
++ else
++ dev_priv->chipset = 0x04;
++ } else
++ dev_priv->chipset = 0xff;
++
++ switch (dev_priv->chipset & 0xf0) {
++ case 0x00:
++ case 0x10:
++ case 0x20:
++ case 0x30:
++ dev_priv->card_type = dev_priv->chipset & 0xf0;
++ break;
++ case 0x40:
++ case 0x60:
++ dev_priv->card_type = NV_40;
++ break;
++ case 0x50:
++ case 0x80:
++ case 0x90:
++ case 0xa0:
++ dev_priv->card_type = NV_50;
++ break;
++ default:
++ NV_INFO(dev, "Unsupported chipset 0x%08x\n", reg0);
++ return -EINVAL;
++ }
++
++ NV_INFO(dev, "Detected an NV%2x generation card (0x%08x)\n",
++ dev_priv->card_type, reg0);
++
++ /* map larger RAMIN aperture on NV40 cards */
++ dev_priv->ramin = NULL;
++ if (dev_priv->card_type >= NV_40) {
++ int ramin_bar = 2;
++ if (pci_resource_len(dev->pdev, ramin_bar) == 0)
++ ramin_bar = 3;
++
++ dev_priv->ramin_size = pci_resource_len(dev->pdev, ramin_bar);
++ dev_priv->ramin = ioremap(
++ pci_resource_start(dev->pdev, ramin_bar),
++ dev_priv->ramin_size);
++ if (!dev_priv->ramin) {
++ NV_ERROR(dev, "Failed to init RAMIN mapping, "
++ "limited instance memory available\n");
++ }
++ }
++
++ /* On older cards (or if the above failed), create a map covering
++ * the BAR0 PRAMIN aperture */
++ if (!dev_priv->ramin) {
++ dev_priv->ramin_size = 1 * 1024 * 1024;
++ dev_priv->ramin = ioremap(mmio_start_offs + NV_RAMIN,
++ dev_priv->ramin_size);
++ if (!dev_priv->ramin) {
++ NV_ERROR(dev, "Failed to map BAR0 PRAMIN.\n");
++ return -ENOMEM;
++ }
++ }
++
++ nouveau_OF_copy_vbios_to_ramin(dev);
++
++ /* Special flags */
++ if (dev->pci_device == 0x01a0)
++ dev_priv->flags |= NV_NFORCE;
++ else if (dev->pci_device == 0x01f0)
++ dev_priv->flags |= NV_NFORCE2;
++
++ /* For kernel modesetting, init card now and bring up fbcon */
++ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
++ int ret = nouveau_card_init(dev);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++}
++
++static void nouveau_close(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ /* In the case of an error dev_priv may not be allocated yet */
++ if (dev_priv)
++ nouveau_card_takedown(dev);
++}
++
++/* KMS: we need mmio at load time, not when the first drm client opens. */
++void nouveau_lastclose(struct drm_device *dev)
++{
++ if (drm_core_check_feature(dev, DRIVER_MODESET))
++ return;
++
++ nouveau_close(dev);
++}
++
++int nouveau_unload(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ if (drm_core_check_feature(dev, DRIVER_MODESET)) {
++ if (dev_priv->card_type >= NV_50)
++ nv50_display_destroy(dev);
++ else
++ nv04_display_destroy(dev);
++ nouveau_close(dev);
++ }
++
++ iounmap(dev_priv->mmio);
++ iounmap(dev_priv->ramin);
++
++ kfree(dev_priv);
++ dev->dev_private = NULL;
++ return 0;
++}
++
++int
++nouveau_ioctl_card_init(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ return nouveau_card_init(dev);
++}
++
++int nouveau_ioctl_getparam(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct drm_nouveau_getparam *getparam = data;
++
++ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
++
++ switch (getparam->param) {
++ case NOUVEAU_GETPARAM_CHIPSET_ID:
++ getparam->value = dev_priv->chipset;
++ break;
++ case NOUVEAU_GETPARAM_PCI_VENDOR:
++ getparam->value = dev->pci_vendor;
++ break;
++ case NOUVEAU_GETPARAM_PCI_DEVICE:
++ getparam->value = dev->pci_device;
++ break;
++ case NOUVEAU_GETPARAM_BUS_TYPE:
++ if (drm_device_is_agp(dev))
++ getparam->value = NV_AGP;
++ else if (drm_device_is_pcie(dev))
++ getparam->value = NV_PCIE;
++ else
++ getparam->value = NV_PCI;
++ break;
++ case NOUVEAU_GETPARAM_FB_PHYSICAL:
++ getparam->value = dev_priv->fb_phys;
++ break;
++ case NOUVEAU_GETPARAM_AGP_PHYSICAL:
++ getparam->value = dev_priv->gart_info.aper_base;
++ break;
++ case NOUVEAU_GETPARAM_PCI_PHYSICAL:
++ if (dev->sg) {
++ getparam->value = (unsigned long)dev->sg->virtual;
++ } else {
++ NV_ERROR(dev, "Requested PCIGART address, "
++ "while no PCIGART was created\n");
++ return -EINVAL;
++ }
++ break;
++ case NOUVEAU_GETPARAM_FB_SIZE:
++ getparam->value = dev_priv->fb_available_size;
++ break;
++ case NOUVEAU_GETPARAM_AGP_SIZE:
++ getparam->value = dev_priv->gart_info.aper_size;
++ break;
++ case NOUVEAU_GETPARAM_VM_VRAM_BASE:
++ getparam->value = dev_priv->vm_vram_base;
++ break;
++ case NOUVEAU_GETPARAM_GRAPH_UNITS:
++ /* NV40 and NV50 versions are quite different, but register
++ * address is the same. User is supposed to know the card
++ * family anyway... */
++ if (dev_priv->chipset >= 0x40) {
++ getparam->value = nv_rd32(dev, NV40_PMC_GRAPH_UNITS);
++ break;
++ }
++ /* FALLTHRU */
++ default:
++ NV_ERROR(dev, "unknown parameter %lld\n", getparam->param);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++int
++nouveau_ioctl_setparam(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_nouveau_setparam *setparam = data;
++
++ NOUVEAU_CHECK_INITIALISED_WITH_RETURN;
++
++ switch (setparam->param) {
++ default:
++ NV_ERROR(dev, "unknown parameter %lld\n", setparam->param);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++/* Wait until (value(reg) & mask) == val, up until timeout has hit */
++bool nouveau_wait_until(struct drm_device *dev, uint64_t timeout,
++ uint32_t reg, uint32_t mask, uint32_t val)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
++ uint64_t start = ptimer->read(dev);
++
++ do {
++ if ((nv_rd32(dev, reg) & mask) == val)
++ return true;
++ } while (ptimer->read(dev) - start < timeout);
++
++ return false;
++}
++
++/* Waits for PGRAPH to go completely idle */
++bool nouveau_wait_for_idle(struct drm_device *dev)
++{
++ if (!nv_wait(NV04_PGRAPH_STATUS, 0xffffffff, 0x00000000)) {
++ NV_ERROR(dev, "PGRAPH idle timed out with status 0x%08x\n",
++ nv_rd32(dev, NV04_PGRAPH_STATUS));
++ return false;
++ }
++
++ return true;
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nouveau_ttm.c b/drivers/gpu/drm/nouveau/nouveau_ttm.c
+new file mode 100644
+index 0000000..c385d50
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nouveau_ttm.c
+@@ -0,0 +1,103 @@
++/*
++ * Copyright (c) 2007-2008 Tungsten Graphics, Inc., Cedar Park, TX., USA,
++ * All Rights Reserved.
++ * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA,
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sub license,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++#include "drmP.h"
++
++#include "nouveau_drv.h"
++
++int
++nouveau_ttm_mmap(struct file *filp, struct vm_area_struct *vma)
++{
++ struct drm_file *file_priv = filp->private_data;
++ struct drm_nouveau_private *dev_priv =
++ file_priv->minor->dev->dev_private;
++
++ if (unlikely(vma->vm_pgoff < DRM_FILE_PAGE_OFFSET))
++ return drm_mmap(filp, vma);
++
++ return ttm_bo_mmap(filp, vma, &dev_priv->ttm.bdev);
++}
++
++static int
++nouveau_ttm_mem_global_init(struct ttm_global_reference *ref)
++{
++ return ttm_mem_global_init(ref->object);
++}
++
++static void
++nouveau_ttm_mem_global_release(struct ttm_global_reference *ref)
++{
++ ttm_mem_global_release(ref->object);
++}
++
++int
++nouveau_ttm_global_init(struct drm_nouveau_private *dev_priv)
++{
++ struct ttm_global_reference *global_ref;
++ int ret;
++
++ global_ref = &dev_priv->ttm.mem_global_ref;
++ global_ref->global_type = TTM_GLOBAL_TTM_MEM;
++ global_ref->size = sizeof(struct ttm_mem_global);
++ global_ref->init = &nouveau_ttm_mem_global_init;
++ global_ref->release = &nouveau_ttm_mem_global_release;
++
++ ret = ttm_global_item_ref(global_ref);
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("Failed setting up TTM memory accounting\n");
++ dev_priv->ttm.mem_global_ref.release = NULL;
++ return ret;
++ }
++
++ dev_priv->ttm.bo_global_ref.mem_glob = global_ref->object;
++ global_ref = &dev_priv->ttm.bo_global_ref.ref;
++ global_ref->global_type = TTM_GLOBAL_TTM_BO;
++ global_ref->size = sizeof(struct ttm_bo_global);
++ global_ref->init = &ttm_bo_global_init;
++ global_ref->release = &ttm_bo_global_release;
++
++ ret = ttm_global_item_ref(global_ref);
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("Failed setting up TTM BO subsystem\n");
++ ttm_global_item_unref(&dev_priv->ttm.mem_global_ref);
++ dev_priv->ttm.mem_global_ref.release = NULL;
++ return ret;
++ }
++
++ return 0;
++}
++
++void
++nouveau_ttm_global_release(struct drm_nouveau_private *dev_priv)
++{
++ if (dev_priv->ttm.mem_global_ref.release == NULL)
++ return;
++
++ ttm_global_item_unref(&dev_priv->ttm.bo_global_ref.ref);
++ ttm_global_item_unref(&dev_priv->ttm.mem_global_ref);
++ dev_priv->ttm.mem_global_ref.release = NULL;
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nv04_crtc.c b/drivers/gpu/drm/nouveau/nv04_crtc.c
+new file mode 100644
+index 0000000..d2f143e
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv04_crtc.c
+@@ -0,0 +1,1002 @@
++/*
++ * Copyright 1993-2003 NVIDIA, Corporation
++ * Copyright 2006 Dave Airlie
++ * Copyright 2007 Maarten Maathuis
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#include "drmP.h"
++#include "drm_crtc_helper.h"
++
++#include "nouveau_drv.h"
++#include "nouveau_encoder.h"
++#include "nouveau_connector.h"
++#include "nouveau_crtc.h"
++#include "nouveau_fb.h"
++#include "nouveau_hw.h"
++#include "nvreg.h"
++
++static int
++nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
++ struct drm_framebuffer *old_fb);
++
++static void
++crtc_wr_cio_state(struct drm_crtc *crtc, struct nv04_crtc_reg *crtcstate, int index)
++{
++ NVWriteVgaCrtc(crtc->dev, nouveau_crtc(crtc)->index, index,
++ crtcstate->CRTC[index]);
++}
++
++static void nv_crtc_set_digital_vibrance(struct drm_crtc *crtc, int level)
++{
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
++ struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
++
++ regp->CRTC[NV_CIO_CRE_CSB] = nv_crtc->saturation = level;
++ if (nv_crtc->saturation && nv_gf4_disp_arch(crtc->dev)) {
++ regp->CRTC[NV_CIO_CRE_CSB] = 0x80;
++ regp->CRTC[NV_CIO_CRE_5B] = nv_crtc->saturation << 2;
++ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_5B);
++ }
++ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_CSB);
++}
++
++static void nv_crtc_set_image_sharpening(struct drm_crtc *crtc, int level)
++{
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
++ struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
++
++ nv_crtc->sharpness = level;
++ if (level < 0) /* blur is in hw range 0x3f -> 0x20 */
++ level += 0x40;
++ regp->ramdac_634 = level;
++ NVWriteRAMDAC(crtc->dev, nv_crtc->index, NV_PRAMDAC_634, regp->ramdac_634);
++}
++
++#define PLLSEL_VPLL1_MASK \
++ (NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL \
++ | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2)
++#define PLLSEL_VPLL2_MASK \
++ (NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2 \
++ | NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2)
++#define PLLSEL_TV_MASK \
++ (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \
++ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1 \
++ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \
++ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2)
++
++/* NV4x 0x40.. pll notes:
++ * gpu pll: 0x4000 + 0x4004
++ * ?gpu? pll: 0x4008 + 0x400c
++ * vpll1: 0x4010 + 0x4014
++ * vpll2: 0x4018 + 0x401c
++ * mpll: 0x4020 + 0x4024
++ * mpll: 0x4038 + 0x403c
++ *
++ * the first register of each pair has some unknown details:
++ * bits 0-7: redirected values from elsewhere? (similar to PLL_SETUP_CONTROL?)
++ * bits 20-23: (mpll) something to do with post divider?
++ * bits 28-31: related to single stage mode? (bit 8/12)
++ */
++
++static void nv_crtc_calc_state_ext(struct drm_crtc *crtc, struct drm_display_mode * mode, int dot_clock)
++{
++ struct drm_device *dev = crtc->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ struct nv04_mode_state *state = &dev_priv->mode_reg;
++ struct nv04_crtc_reg *regp = &state->crtc_reg[nv_crtc->index];
++ struct nouveau_pll_vals *pv = &regp->pllvals;
++ struct pll_lims pll_lim;
++
++ if (get_pll_limits(dev, nv_crtc->index ? VPLL2 : VPLL1, &pll_lim))
++ return;
++
++ /* NM2 == 0 is used to determine single stage mode on two stage plls */
++ pv->NM2 = 0;
++
++ /* for newer nv4x the blob uses only the first stage of the vpll below a
++ * certain clock. for a certain nv4b this is 150MHz. since the max
++ * output frequency of the first stage for this card is 300MHz, it is
++ * assumed the threshold is given by vco1 maxfreq/2
++ */
++ /* for early nv4x, specifically nv40 and *some* nv43 (devids 0 and 6,
++ * not 8, others unknown), the blob always uses both plls. no problem
++ * has yet been observed in allowing the use a single stage pll on all
++ * nv43 however. the behaviour of single stage use is untested on nv40
++ */
++ if (dev_priv->chipset > 0x40 && dot_clock <= (pll_lim.vco1.maxfreq / 2))
++ memset(&pll_lim.vco2, 0, sizeof(pll_lim.vco2));
++
++ if (!nouveau_calc_pll_mnp(dev, &pll_lim, dot_clock, pv))
++ return;
++
++ state->pllsel &= PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK;
++
++ /* The blob uses this always, so let's do the same */
++ if (dev_priv->card_type == NV_40)
++ state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE;
++ /* again nv40 and some nv43 act more like nv3x as described above */
++ if (dev_priv->chipset < 0x41)
++ state->pllsel |= NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL |
++ NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL;
++ state->pllsel |= nv_crtc->index ? PLLSEL_VPLL2_MASK : PLLSEL_VPLL1_MASK;
++
++ if (pv->NM2)
++ NV_DEBUG_KMS(dev, "vpll: n1 %d n2 %d m1 %d m2 %d log2p %d\n",
++ pv->N1, pv->N2, pv->M1, pv->M2, pv->log2P);
++ else
++ NV_DEBUG_KMS(dev, "vpll: n %d m %d log2p %d\n",
++ pv->N1, pv->M1, pv->log2P);
++
++ nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset);
++}
++
++static void
++nv_crtc_dpms(struct drm_crtc *crtc, int mode)
++{
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ struct drm_device *dev = crtc->dev;
++ unsigned char seq1 = 0, crtc17 = 0;
++ unsigned char crtc1A;
++
++ NV_DEBUG_KMS(dev, "Setting dpms mode %d on CRTC %d\n", mode,
++ nv_crtc->index);
++
++ if (nv_crtc->last_dpms == mode) /* Don't do unnecesary mode changes. */
++ return;
++
++ nv_crtc->last_dpms = mode;
++
++ if (nv_two_heads(dev))
++ NVSetOwner(dev, nv_crtc->index);
++
++ /* nv4ref indicates these two RPC1 bits inhibit h/v sync */
++ crtc1A = NVReadVgaCrtc(dev, nv_crtc->index,
++ NV_CIO_CRE_RPC1_INDEX) & ~0xC0;
++ switch (mode) {
++ case DRM_MODE_DPMS_STANDBY:
++ /* Screen: Off; HSync: Off, VSync: On -- Not Supported */
++ seq1 = 0x20;
++ crtc17 = 0x80;
++ crtc1A |= 0x80;
++ break;
++ case DRM_MODE_DPMS_SUSPEND:
++ /* Screen: Off; HSync: On, VSync: Off -- Not Supported */
++ seq1 = 0x20;
++ crtc17 = 0x80;
++ crtc1A |= 0x40;
++ break;
++ case DRM_MODE_DPMS_OFF:
++ /* Screen: Off; HSync: Off, VSync: Off */
++ seq1 = 0x20;
++ crtc17 = 0x00;
++ crtc1A |= 0xC0;
++ break;
++ case DRM_MODE_DPMS_ON:
++ default:
++ /* Screen: On; HSync: On, VSync: On */
++ seq1 = 0x00;
++ crtc17 = 0x80;
++ break;
++ }
++
++ NVVgaSeqReset(dev, nv_crtc->index, true);
++ /* Each head has it's own sequencer, so we can turn it off when we want */
++ seq1 |= (NVReadVgaSeq(dev, nv_crtc->index, NV_VIO_SR_CLOCK_INDEX) & ~0x20);
++ NVWriteVgaSeq(dev, nv_crtc->index, NV_VIO_SR_CLOCK_INDEX, seq1);
++ crtc17 |= (NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CR_MODE_INDEX) & ~0x80);
++ mdelay(10);
++ NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CR_MODE_INDEX, crtc17);
++ NVVgaSeqReset(dev, nv_crtc->index, false);
++
++ NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RPC1_INDEX, crtc1A);
++}
++
++static bool
++nv_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode,
++ struct drm_display_mode *adjusted_mode)
++{
++ return true;
++}
++
++static void
++nv_crtc_mode_set_vga(struct drm_crtc *crtc, struct drm_display_mode *mode)
++{
++ struct drm_device *dev = crtc->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
++ struct drm_framebuffer *fb = crtc->fb;
++
++ /* Calculate our timings */
++ int horizDisplay = (mode->crtc_hdisplay >> 3) - 1;
++ int horizStart = (mode->crtc_hsync_start >> 3) - 1;
++ int horizEnd = (mode->crtc_hsync_end >> 3) - 1;
++ int horizTotal = (mode->crtc_htotal >> 3) - 5;
++ int horizBlankStart = (mode->crtc_hdisplay >> 3) - 1;
++ int horizBlankEnd = (mode->crtc_htotal >> 3) - 1;
++ int vertDisplay = mode->crtc_vdisplay - 1;
++ int vertStart = mode->crtc_vsync_start - 1;
++ int vertEnd = mode->crtc_vsync_end - 1;
++ int vertTotal = mode->crtc_vtotal - 2;
++ int vertBlankStart = mode->crtc_vdisplay - 1;
++ int vertBlankEnd = mode->crtc_vtotal - 1;
++
++ struct drm_encoder *encoder;
++ bool fp_output = false;
++
++ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++
++ if (encoder->crtc == crtc &&
++ (nv_encoder->dcb->type == OUTPUT_LVDS ||
++ nv_encoder->dcb->type == OUTPUT_TMDS))
++ fp_output = true;
++ }
++
++ if (fp_output) {
++ vertStart = vertTotal - 3;
++ vertEnd = vertTotal - 2;
++ vertBlankStart = vertStart;
++ horizStart = horizTotal - 5;
++ horizEnd = horizTotal - 2;
++ horizBlankEnd = horizTotal + 4;
++#if 0
++ if (dev->overlayAdaptor && dev_priv->card_type >= NV_10)
++ /* This reportedly works around some video overlay bandwidth problems */
++ horizTotal += 2;
++#endif
++ }
++
++ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
++ vertTotal |= 1;
++
++#if 0
++ ErrorF("horizDisplay: 0x%X \n", horizDisplay);
++ ErrorF("horizStart: 0x%X \n", horizStart);
++ ErrorF("horizEnd: 0x%X \n", horizEnd);
++ ErrorF("horizTotal: 0x%X \n", horizTotal);
++ ErrorF("horizBlankStart: 0x%X \n", horizBlankStart);
++ ErrorF("horizBlankEnd: 0x%X \n", horizBlankEnd);
++ ErrorF("vertDisplay: 0x%X \n", vertDisplay);
++ ErrorF("vertStart: 0x%X \n", vertStart);
++ ErrorF("vertEnd: 0x%X \n", vertEnd);
++ ErrorF("vertTotal: 0x%X \n", vertTotal);
++ ErrorF("vertBlankStart: 0x%X \n", vertBlankStart);
++ ErrorF("vertBlankEnd: 0x%X \n", vertBlankEnd);
++#endif
++
++ /*
++ * compute correct Hsync & Vsync polarity
++ */
++ if ((mode->flags & (DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NHSYNC))
++ && (mode->flags & (DRM_MODE_FLAG_PVSYNC | DRM_MODE_FLAG_NVSYNC))) {
++
++ regp->MiscOutReg = 0x23;
++ if (mode->flags & DRM_MODE_FLAG_NHSYNC)
++ regp->MiscOutReg |= 0x40;
++ if (mode->flags & DRM_MODE_FLAG_NVSYNC)
++ regp->MiscOutReg |= 0x80;
++ } else {
++ int vdisplay = mode->vdisplay;
++ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
++ vdisplay *= 2;
++ if (mode->vscan > 1)
++ vdisplay *= mode->vscan;
++ if (vdisplay < 400)
++ regp->MiscOutReg = 0xA3; /* +hsync -vsync */
++ else if (vdisplay < 480)
++ regp->MiscOutReg = 0x63; /* -hsync +vsync */
++ else if (vdisplay < 768)
++ regp->MiscOutReg = 0xE3; /* -hsync -vsync */
++ else
++ regp->MiscOutReg = 0x23; /* +hsync +vsync */
++ }
++
++ regp->MiscOutReg |= (mode->clock_index & 0x03) << 2;
++
++ /*
++ * Time Sequencer
++ */
++ regp->Sequencer[NV_VIO_SR_RESET_INDEX] = 0x00;
++ /* 0x20 disables the sequencer */
++ if (mode->flags & DRM_MODE_FLAG_CLKDIV2)
++ regp->Sequencer[NV_VIO_SR_CLOCK_INDEX] = 0x29;
++ else
++ regp->Sequencer[NV_VIO_SR_CLOCK_INDEX] = 0x21;
++ regp->Sequencer[NV_VIO_SR_PLANE_MASK_INDEX] = 0x0F;
++ regp->Sequencer[NV_VIO_SR_CHAR_MAP_INDEX] = 0x00;
++ regp->Sequencer[NV_VIO_SR_MEM_MODE_INDEX] = 0x0E;
++
++ /*
++ * CRTC
++ */
++ regp->CRTC[NV_CIO_CR_HDT_INDEX] = horizTotal;
++ regp->CRTC[NV_CIO_CR_HDE_INDEX] = horizDisplay;
++ regp->CRTC[NV_CIO_CR_HBS_INDEX] = horizBlankStart;
++ regp->CRTC[NV_CIO_CR_HBE_INDEX] = (1 << 7) |
++ XLATE(horizBlankEnd, 0, NV_CIO_CR_HBE_4_0);
++ regp->CRTC[NV_CIO_CR_HRS_INDEX] = horizStart;
++ regp->CRTC[NV_CIO_CR_HRE_INDEX] = XLATE(horizBlankEnd, 5, NV_CIO_CR_HRE_HBE_5) |
++ XLATE(horizEnd, 0, NV_CIO_CR_HRE_4_0);
++ regp->CRTC[NV_CIO_CR_VDT_INDEX] = vertTotal;
++ regp->CRTC[NV_CIO_CR_OVL_INDEX] = XLATE(vertStart, 9, NV_CIO_CR_OVL_VRS_9) |
++ XLATE(vertDisplay, 9, NV_CIO_CR_OVL_VDE_9) |
++ XLATE(vertTotal, 9, NV_CIO_CR_OVL_VDT_9) |
++ (1 << 4) |
++ XLATE(vertBlankStart, 8, NV_CIO_CR_OVL_VBS_8) |
++ XLATE(vertStart, 8, NV_CIO_CR_OVL_VRS_8) |
++ XLATE(vertDisplay, 8, NV_CIO_CR_OVL_VDE_8) |
++ XLATE(vertTotal, 8, NV_CIO_CR_OVL_VDT_8);
++ regp->CRTC[NV_CIO_CR_RSAL_INDEX] = 0x00;
++ regp->CRTC[NV_CIO_CR_CELL_HT_INDEX] = ((mode->flags & DRM_MODE_FLAG_DBLSCAN) ? MASK(NV_CIO_CR_CELL_HT_SCANDBL) : 0) |
++ 1 << 6 |
++ XLATE(vertBlankStart, 9, NV_CIO_CR_CELL_HT_VBS_9);
++ regp->CRTC[NV_CIO_CR_CURS_ST_INDEX] = 0x00;
++ regp->CRTC[NV_CIO_CR_CURS_END_INDEX] = 0x00;
++ regp->CRTC[NV_CIO_CR_SA_HI_INDEX] = 0x00;
++ regp->CRTC[NV_CIO_CR_SA_LO_INDEX] = 0x00;
++ regp->CRTC[NV_CIO_CR_TCOFF_HI_INDEX] = 0x00;
++ regp->CRTC[NV_CIO_CR_TCOFF_LO_INDEX] = 0x00;
++ regp->CRTC[NV_CIO_CR_VRS_INDEX] = vertStart;
++ regp->CRTC[NV_CIO_CR_VRE_INDEX] = 1 << 5 | XLATE(vertEnd, 0, NV_CIO_CR_VRE_3_0);
++ regp->CRTC[NV_CIO_CR_VDE_INDEX] = vertDisplay;
++ /* framebuffer can be larger than crtc scanout area. */
++ regp->CRTC[NV_CIO_CR_OFFSET_INDEX] = fb->pitch / 8;
++ regp->CRTC[NV_CIO_CR_ULINE_INDEX] = 0x00;
++ regp->CRTC[NV_CIO_CR_VBS_INDEX] = vertBlankStart;
++ regp->CRTC[NV_CIO_CR_VBE_INDEX] = vertBlankEnd;
++ regp->CRTC[NV_CIO_CR_MODE_INDEX] = 0x43;
++ regp->CRTC[NV_CIO_CR_LCOMP_INDEX] = 0xff;
++
++ /*
++ * Some extended CRTC registers (they are not saved with the rest of the vga regs).
++ */
++
++ /* framebuffer can be larger than crtc scanout area. */
++ regp->CRTC[NV_CIO_CRE_RPC0_INDEX] = XLATE(fb->pitch / 8, 8, NV_CIO_CRE_RPC0_OFFSET_10_8);
++ regp->CRTC[NV_CIO_CRE_RPC1_INDEX] = mode->crtc_hdisplay < 1280 ?
++ MASK(NV_CIO_CRE_RPC1_LARGE) : 0x00;
++ regp->CRTC[NV_CIO_CRE_LSR_INDEX] = XLATE(horizBlankEnd, 6, NV_CIO_CRE_LSR_HBE_6) |
++ XLATE(vertBlankStart, 10, NV_CIO_CRE_LSR_VBS_10) |
++ XLATE(vertStart, 10, NV_CIO_CRE_LSR_VRS_10) |
++ XLATE(vertDisplay, 10, NV_CIO_CRE_LSR_VDE_10) |
++ XLATE(vertTotal, 10, NV_CIO_CRE_LSR_VDT_10);
++ regp->CRTC[NV_CIO_CRE_HEB__INDEX] = XLATE(horizStart, 8, NV_CIO_CRE_HEB_HRS_8) |
++ XLATE(horizBlankStart, 8, NV_CIO_CRE_HEB_HBS_8) |
++ XLATE(horizDisplay, 8, NV_CIO_CRE_HEB_HDE_8) |
++ XLATE(horizTotal, 8, NV_CIO_CRE_HEB_HDT_8);
++ regp->CRTC[NV_CIO_CRE_EBR_INDEX] = XLATE(vertBlankStart, 11, NV_CIO_CRE_EBR_VBS_11) |
++ XLATE(vertStart, 11, NV_CIO_CRE_EBR_VRS_11) |
++ XLATE(vertDisplay, 11, NV_CIO_CRE_EBR_VDE_11) |
++ XLATE(vertTotal, 11, NV_CIO_CRE_EBR_VDT_11);
++
++ if (mode->flags & DRM_MODE_FLAG_INTERLACE) {
++ horizTotal = (horizTotal >> 1) & ~1;
++ regp->CRTC[NV_CIO_CRE_ILACE__INDEX] = horizTotal;
++ regp->CRTC[NV_CIO_CRE_HEB__INDEX] |= XLATE(horizTotal, 8, NV_CIO_CRE_HEB_ILC_8);
++ } else
++ regp->CRTC[NV_CIO_CRE_ILACE__INDEX] = 0xff; /* interlace off */
++
++ /*
++ * Graphics Display Controller
++ */
++ regp->Graphics[NV_VIO_GX_SR_INDEX] = 0x00;
++ regp->Graphics[NV_VIO_GX_SREN_INDEX] = 0x00;
++ regp->Graphics[NV_VIO_GX_CCOMP_INDEX] = 0x00;
++ regp->Graphics[NV_VIO_GX_ROP_INDEX] = 0x00;
++ regp->Graphics[NV_VIO_GX_READ_MAP_INDEX] = 0x00;
++ regp->Graphics[NV_VIO_GX_MODE_INDEX] = 0x40; /* 256 color mode */
++ regp->Graphics[NV_VIO_GX_MISC_INDEX] = 0x05; /* map 64k mem + graphic mode */
++ regp->Graphics[NV_VIO_GX_DONT_CARE_INDEX] = 0x0F;
++ regp->Graphics[NV_VIO_GX_BIT_MASK_INDEX] = 0xFF;
++
++ regp->Attribute[0] = 0x00; /* standard colormap translation */
++ regp->Attribute[1] = 0x01;
++ regp->Attribute[2] = 0x02;
++ regp->Attribute[3] = 0x03;
++ regp->Attribute[4] = 0x04;
++ regp->Attribute[5] = 0x05;
++ regp->Attribute[6] = 0x06;
++ regp->Attribute[7] = 0x07;
++ regp->Attribute[8] = 0x08;
++ regp->Attribute[9] = 0x09;
++ regp->Attribute[10] = 0x0A;
++ regp->Attribute[11] = 0x0B;
++ regp->Attribute[12] = 0x0C;
++ regp->Attribute[13] = 0x0D;
++ regp->Attribute[14] = 0x0E;
++ regp->Attribute[15] = 0x0F;
++ regp->Attribute[NV_CIO_AR_MODE_INDEX] = 0x01; /* Enable graphic mode */
++ /* Non-vga */
++ regp->Attribute[NV_CIO_AR_OSCAN_INDEX] = 0x00;
++ regp->Attribute[NV_CIO_AR_PLANE_INDEX] = 0x0F; /* enable all color planes */
++ regp->Attribute[NV_CIO_AR_HPP_INDEX] = 0x00;
++ regp->Attribute[NV_CIO_AR_CSEL_INDEX] = 0x00;
++}
++
++/**
++ * Sets up registers for the given mode/adjusted_mode pair.
++ *
++ * The clocks, CRTCs and outputs attached to this CRTC must be off.
++ *
++ * This shouldn't enable any clocks, CRTCs, or outputs, but they should
++ * be easily turned on/off after this.
++ */
++static void
++nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
++{
++ struct drm_device *dev = crtc->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
++ struct nv04_crtc_reg *savep = &dev_priv->saved_reg.crtc_reg[nv_crtc->index];
++ struct drm_encoder *encoder;
++ bool lvds_output = false, tmds_output = false, tv_output = false,
++ off_chip_digital = false;
++
++ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ bool digital = false;
++
++ if (encoder->crtc != crtc)
++ continue;
++
++ if (nv_encoder->dcb->type == OUTPUT_LVDS)
++ digital = lvds_output = true;
++ if (nv_encoder->dcb->type == OUTPUT_TV)
++ tv_output = true;
++ if (nv_encoder->dcb->type == OUTPUT_TMDS)
++ digital = tmds_output = true;
++ if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP && digital)
++ off_chip_digital = true;
++ }
++
++ /* Registers not directly related to the (s)vga mode */
++
++ /* What is the meaning of this register? */
++ /* A few popular values are 0x18, 0x1c, 0x38, 0x3c */
++ regp->CRTC[NV_CIO_CRE_ENH_INDEX] = savep->CRTC[NV_CIO_CRE_ENH_INDEX] & ~(1<<5);
++
++ regp->crtc_eng_ctrl = 0;
++ /* Except for rare conditions I2C is enabled on the primary crtc */
++ if (nv_crtc->index == 0)
++ regp->crtc_eng_ctrl |= NV_CRTC_FSEL_I2C;
++#if 0
++ /* Set overlay to desired crtc. */
++ if (dev->overlayAdaptor) {
++ NVPortPrivPtr pPriv = GET_OVERLAY_PRIVATE(dev);
++ if (pPriv->overlayCRTC == nv_crtc->index)
++ regp->crtc_eng_ctrl |= NV_CRTC_FSEL_OVERLAY;
++ }
++#endif
++
++ /* ADDRESS_SPACE_PNVM is the same as setting HCUR_ASI */
++ regp->cursor_cfg = NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64 |
++ NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64 |
++ NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM;
++ if (dev_priv->chipset >= 0x11)
++ regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32;
++ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
++ regp->cursor_cfg |= NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE;
++
++ /* Unblock some timings */
++ regp->CRTC[NV_CIO_CRE_53] = 0;
++ regp->CRTC[NV_CIO_CRE_54] = 0;
++
++ /* 0x00 is disabled, 0x11 is lvds, 0x22 crt and 0x88 tmds */
++ if (lvds_output)
++ regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x11;
++ else if (tmds_output)
++ regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x88;
++ else
++ regp->CRTC[NV_CIO_CRE_SCRATCH3__INDEX] = 0x22;
++
++ /* These values seem to vary */
++ /* This register seems to be used by the bios to make certain decisions on some G70 cards? */
++ regp->CRTC[NV_CIO_CRE_SCRATCH4__INDEX] = savep->CRTC[NV_CIO_CRE_SCRATCH4__INDEX];
++
++ nv_crtc_set_digital_vibrance(crtc, nv_crtc->saturation);
++
++ /* probably a scratch reg, but kept for cargo-cult purposes:
++ * bit0: crtc0?, head A
++ * bit6: lvds, head A
++ * bit7: (only in X), head A
++ */
++ if (nv_crtc->index == 0)
++ regp->CRTC[NV_CIO_CRE_4B] = savep->CRTC[NV_CIO_CRE_4B] | 0x80;
++
++ /* The blob seems to take the current value from crtc 0, add 4 to that
++ * and reuse the old value for crtc 1 */
++ regp->CRTC[NV_CIO_CRE_TVOUT_LATENCY] = dev_priv->saved_reg.crtc_reg[0].CRTC[NV_CIO_CRE_TVOUT_LATENCY];
++ if (!nv_crtc->index)
++ regp->CRTC[NV_CIO_CRE_TVOUT_LATENCY] += 4;
++
++ /* the blob sometimes sets |= 0x10 (which is the same as setting |=
++ * 1 << 30 on 0x60.830), for no apparent reason */
++ regp->CRTC[NV_CIO_CRE_59] = off_chip_digital;
++
++ regp->crtc_830 = mode->crtc_vdisplay - 3;
++ regp->crtc_834 = mode->crtc_vdisplay - 1;
++
++ if (dev_priv->card_type == NV_40)
++ /* This is what the blob does */
++ regp->crtc_850 = NVReadCRTC(dev, 0, NV_PCRTC_850);
++
++ if (dev_priv->card_type >= NV_30)
++ regp->gpio_ext = NVReadCRTC(dev, 0, NV_PCRTC_GPIO_EXT);
++
++ regp->crtc_cfg = NV_PCRTC_CONFIG_START_ADDRESS_HSYNC;
++
++ /* Some misc regs */
++ if (dev_priv->card_type == NV_40) {
++ regp->CRTC[NV_CIO_CRE_85] = 0xFF;
++ regp->CRTC[NV_CIO_CRE_86] = 0x1;
++ }
++
++ regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] = (crtc->fb->depth + 1) / 8;
++ /* Enable slaved mode (called MODE_TV in nv4ref.h) */
++ if (lvds_output || tmds_output || tv_output)
++ regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (1 << 7);
++
++ /* Generic PRAMDAC regs */
++
++ if (dev_priv->card_type >= NV_10)
++ /* Only bit that bios and blob set. */
++ regp->nv10_cursync = (1 << 25);
++
++ regp->ramdac_gen_ctrl = NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS |
++ NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL |
++ NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON;
++ if (crtc->fb->depth == 16)
++ regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL;
++ if (dev_priv->chipset >= 0x11)
++ regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG;
++
++ regp->ramdac_630 = 0; /* turn off green mode (tv test pattern?) */
++ regp->tv_setup = 0;
++
++ nv_crtc_set_image_sharpening(crtc, nv_crtc->sharpness);
++
++ /* Some values the blob sets */
++ regp->ramdac_8c0 = 0x100;
++ regp->ramdac_a20 = 0x0;
++ regp->ramdac_a24 = 0xfffff;
++ regp->ramdac_a34 = 0x1;
++}
++
++/**
++ * Sets up registers for the given mode/adjusted_mode pair.
++ *
++ * The clocks, CRTCs and outputs attached to this CRTC must be off.
++ *
++ * This shouldn't enable any clocks, CRTCs, or outputs, but they should
++ * be easily turned on/off after this.
++ */
++static int
++nv_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
++ struct drm_display_mode *adjusted_mode,
++ int x, int y, struct drm_framebuffer *old_fb)
++{
++ struct drm_device *dev = crtc->dev;
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ NV_DEBUG_KMS(dev, "CTRC mode on CRTC %d:\n", nv_crtc->index);
++ drm_mode_debug_printmodeline(adjusted_mode);
++
++ /* unlock must come after turning off FP_TG_CONTROL in output_prepare */
++ nv_lock_vga_crtc_shadow(dev, nv_crtc->index, -1);
++
++ nv_crtc_mode_set_vga(crtc, adjusted_mode);
++ /* calculated in nv04_dfp_prepare, nv40 needs it written before calculating PLLs */
++ if (dev_priv->card_type == NV_40)
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, dev_priv->mode_reg.sel_clk);
++ nv_crtc_mode_set_regs(crtc, adjusted_mode);
++ nv_crtc_calc_state_ext(crtc, mode, adjusted_mode->clock);
++ return 0;
++}
++
++static void nv_crtc_save(struct drm_crtc *crtc)
++{
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
++ struct nv04_mode_state *state = &dev_priv->mode_reg;
++ struct nv04_crtc_reg *crtc_state = &state->crtc_reg[nv_crtc->index];
++ struct nv04_mode_state *saved = &dev_priv->saved_reg;
++ struct nv04_crtc_reg *crtc_saved = &saved->crtc_reg[nv_crtc->index];
++
++ if (nv_two_heads(crtc->dev))
++ NVSetOwner(crtc->dev, nv_crtc->index);
++
++ nouveau_hw_save_state(crtc->dev, nv_crtc->index, saved);
++
++ /* init some state to saved value */
++ state->sel_clk = saved->sel_clk & ~(0x5 << 16);
++ crtc_state->CRTC[NV_CIO_CRE_LCD__INDEX] = crtc_saved->CRTC[NV_CIO_CRE_LCD__INDEX];
++ state->pllsel = saved->pllsel & ~(PLLSEL_VPLL1_MASK | PLLSEL_VPLL2_MASK | PLLSEL_TV_MASK);
++ crtc_state->gpio_ext = crtc_saved->gpio_ext;
++}
++
++static void nv_crtc_restore(struct drm_crtc *crtc)
++{
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
++ int head = nv_crtc->index;
++ uint8_t saved_cr21 = dev_priv->saved_reg.crtc_reg[head].CRTC[NV_CIO_CRE_21];
++
++ if (nv_two_heads(crtc->dev))
++ NVSetOwner(crtc->dev, head);
++
++ nouveau_hw_load_state(crtc->dev, head, &dev_priv->saved_reg);
++ nv_lock_vga_crtc_shadow(crtc->dev, head, saved_cr21);
++
++ nv_crtc->last_dpms = NV_DPMS_CLEARED;
++}
++
++static void nv_crtc_prepare(struct drm_crtc *crtc)
++{
++ struct drm_device *dev = crtc->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ struct drm_crtc_helper_funcs *funcs = crtc->helper_private;
++
++ if (nv_two_heads(dev))
++ NVSetOwner(dev, nv_crtc->index);
++
++ funcs->dpms(crtc, DRM_MODE_DPMS_OFF);
++
++ NVBlankScreen(dev, nv_crtc->index, true);
++
++ /* Some more preperation. */
++ NVWriteCRTC(dev, nv_crtc->index, NV_PCRTC_CONFIG, NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA);
++ if (dev_priv->card_type == NV_40) {
++ uint32_t reg900 = NVReadRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900);
++ NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_900, reg900 & ~0x10000);
++ }
++}
++
++static void nv_crtc_commit(struct drm_crtc *crtc)
++{
++ struct drm_device *dev = crtc->dev;
++ struct drm_crtc_helper_funcs *funcs = crtc->helper_private;
++ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++
++ nouveau_hw_load_state(dev, nv_crtc->index, &dev_priv->mode_reg);
++ nv04_crtc_mode_set_base(crtc, crtc->x, crtc->y, NULL);
++
++#ifdef __BIG_ENDIAN
++ /* turn on LFB swapping */
++ {
++ uint8_t tmp = NVReadVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR);
++ tmp |= MASK(NV_CIO_CRE_RCR_ENDIAN_BIG);
++ NVWriteVgaCrtc(dev, nv_crtc->index, NV_CIO_CRE_RCR, tmp);
++ }
++#endif
++
++ funcs->dpms(crtc, DRM_MODE_DPMS_ON);
++}
++
++static void nv_crtc_destroy(struct drm_crtc *crtc)
++{
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++
++ NV_DEBUG_KMS(crtc->dev, "\n");
++
++ if (!nv_crtc)
++ return;
++
++ drm_crtc_cleanup(crtc);
++
++ nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
++ kfree(nv_crtc);
++}
++
++static void
++nv_crtc_gamma_load(struct drm_crtc *crtc)
++{
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ struct drm_device *dev = nv_crtc->base.dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct rgb { uint8_t r, g, b; } __attribute__((packed)) *rgbs;
++ int i;
++
++ rgbs = (struct rgb *)dev_priv->mode_reg.crtc_reg[nv_crtc->index].DAC;
++ for (i = 0; i < 256; i++) {
++ rgbs[i].r = nv_crtc->lut.r[i] >> 8;
++ rgbs[i].g = nv_crtc->lut.g[i] >> 8;
++ rgbs[i].b = nv_crtc->lut.b[i] >> 8;
++ }
++
++ nouveau_hw_load_state_palette(dev, nv_crtc->index, &dev_priv->mode_reg);
++}
++
++static void
++nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t size)
++{
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ int i;
++
++ if (size != 256)
++ return;
++
++ for (i = 0; i < 256; i++) {
++ nv_crtc->lut.r[i] = r[i];
++ nv_crtc->lut.g[i] = g[i];
++ nv_crtc->lut.b[i] = b[i];
++ }
++
++ /* We need to know the depth before we upload, but it's possible to
++ * get called before a framebuffer is bound. If this is the case,
++ * mark the lut values as dirty by setting depth==0, and it'll be
++ * uploaded on the first mode_set_base()
++ */
++ if (!nv_crtc->base.fb) {
++ nv_crtc->lut.depth = 0;
++ return;
++ }
++
++ nv_crtc_gamma_load(crtc);
++}
++
++static int
++nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
++ struct drm_framebuffer *old_fb)
++{
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ struct drm_device *dev = crtc->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
++ struct drm_framebuffer *drm_fb = nv_crtc->base.fb;
++ struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
++ int arb_burst, arb_lwm;
++ int ret;
++
++ ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM);
++ if (ret)
++ return ret;
++
++ if (old_fb) {
++ struct nouveau_framebuffer *ofb = nouveau_framebuffer(old_fb);
++ nouveau_bo_unpin(ofb->nvbo);
++ }
++
++ nv_crtc->fb.offset = fb->nvbo->bo.offset;
++
++ if (nv_crtc->lut.depth != drm_fb->depth) {
++ nv_crtc->lut.depth = drm_fb->depth;
++ nv_crtc_gamma_load(crtc);
++ }
++
++ /* Update the framebuffer format. */
++ regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] &= ~3;
++ regp->CRTC[NV_CIO_CRE_PIXEL_INDEX] |= (crtc->fb->depth + 1) / 8;
++ regp->ramdac_gen_ctrl &= ~NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL;
++ if (crtc->fb->depth == 16)
++ regp->ramdac_gen_ctrl |= NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL;
++ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_PIXEL_INDEX);
++ NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_GENERAL_CONTROL,
++ regp->ramdac_gen_ctrl);
++
++ regp->CRTC[NV_CIO_CR_OFFSET_INDEX] = drm_fb->pitch >> 3;
++ regp->CRTC[NV_CIO_CRE_RPC0_INDEX] =
++ XLATE(drm_fb->pitch >> 3, 8, NV_CIO_CRE_RPC0_OFFSET_10_8);
++ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_RPC0_INDEX);
++ crtc_wr_cio_state(crtc, regp, NV_CIO_CR_OFFSET_INDEX);
++
++ /* Update the framebuffer location. */
++ regp->fb_start = nv_crtc->fb.offset & ~3;
++ regp->fb_start += (y * drm_fb->pitch) + (x * drm_fb->bits_per_pixel / 8);
++ NVWriteCRTC(dev, nv_crtc->index, NV_PCRTC_START, regp->fb_start);
++
++ /* Update the arbitration parameters. */
++ nouveau_calc_arb(dev, crtc->mode.clock, drm_fb->bits_per_pixel,
++ &arb_burst, &arb_lwm);
++
++ regp->CRTC[NV_CIO_CRE_FF_INDEX] = arb_burst;
++ regp->CRTC[NV_CIO_CRE_FFLWM__INDEX] = arb_lwm & 0xff;
++ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FF_INDEX);
++ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_FFLWM__INDEX);
++
++ if (dev_priv->card_type >= NV_30) {
++ regp->CRTC[NV_CIO_CRE_47] = arb_lwm >> 8;
++ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_47);
++ }
++
++ return 0;
++}
++
++static void nv04_cursor_upload(struct drm_device *dev, struct nouveau_bo *src,
++ struct nouveau_bo *dst)
++{
++ int width = nv_cursor_width(dev);
++ uint32_t pixel;
++ int i, j;
++
++ for (i = 0; i < width; i++) {
++ for (j = 0; j < width; j++) {
++ pixel = nouveau_bo_rd32(src, i*64 + j);
++
++ nouveau_bo_wr16(dst, i*width + j, (pixel & 0x80000000) >> 16
++ | (pixel & 0xf80000) >> 9
++ | (pixel & 0xf800) >> 6
++ | (pixel & 0xf8) >> 3);
++ }
++ }
++}
++
++static void nv11_cursor_upload(struct drm_device *dev, struct nouveau_bo *src,
++ struct nouveau_bo *dst)
++{
++ uint32_t pixel;
++ int alpha, i;
++
++ /* nv11+ supports premultiplied (PM), or non-premultiplied (NPM) alpha
++ * cursors (though NPM in combination with fp dithering may not work on
++ * nv11, from "nv" driver history)
++ * NPM mode needs NV_PCRTC_CURSOR_CONFIG_ALPHA_BLEND set and is what the
++ * blob uses, however we get given PM cursors so we use PM mode
++ */
++ for (i = 0; i < 64 * 64; i++) {
++ pixel = nouveau_bo_rd32(src, i);
++
++ /* hw gets unhappy if alpha <= rgb values. for a PM image "less
++ * than" shouldn't happen; fix "equal to" case by adding one to
++ * alpha channel (slightly inaccurate, but so is attempting to
++ * get back to NPM images, due to limits of integer precision)
++ */
++ alpha = pixel >> 24;
++ if (alpha > 0 && alpha < 255)
++ pixel = (pixel & 0x00ffffff) | ((alpha + 1) << 24);
++
++#ifdef __BIG_ENDIAN
++ {
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ if (dev_priv->chipset == 0x11) {
++ pixel = ((pixel & 0x000000ff) << 24) |
++ ((pixel & 0x0000ff00) << 8) |
++ ((pixel & 0x00ff0000) >> 8) |
++ ((pixel & 0xff000000) >> 24);
++ }
++ }
++#endif
++
++ nouveau_bo_wr32(dst, i, pixel);
++ }
++}
++
++static int
++nv04_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
++ uint32_t buffer_handle, uint32_t width, uint32_t height)
++{
++ struct drm_nouveau_private *dev_priv = crtc->dev->dev_private;
++ struct drm_device *dev = dev_priv->dev;
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ struct nouveau_bo *cursor = NULL;
++ struct drm_gem_object *gem;
++ int ret = 0;
++
++ if (width != 64 || height != 64)
++ return -EINVAL;
++
++ if (!buffer_handle) {
++ nv_crtc->cursor.hide(nv_crtc, true);
++ return 0;
++ }
++
++ gem = drm_gem_object_lookup(dev, file_priv, buffer_handle);
++ if (!gem)
++ return -EINVAL;
++ cursor = nouveau_gem_object(gem);
++
++ ret = nouveau_bo_map(cursor);
++ if (ret)
++ goto out;
++
++ if (dev_priv->chipset >= 0x11)
++ nv11_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo);
++ else
++ nv04_cursor_upload(dev, cursor, nv_crtc->cursor.nvbo);
++
++ nouveau_bo_unmap(cursor);
++ nv_crtc->cursor.offset = nv_crtc->cursor.nvbo->bo.offset;
++ nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.offset);
++ nv_crtc->cursor.show(nv_crtc, true);
++out:
++ mutex_lock(&dev->struct_mutex);
++ drm_gem_object_unreference(gem);
++ mutex_unlock(&dev->struct_mutex);
++ return ret;
++}
++
++static int
++nv04_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
++{
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++
++ nv_crtc->cursor.set_pos(nv_crtc, x, y);
++ return 0;
++}
++
++static const struct drm_crtc_funcs nv04_crtc_funcs = {
++ .save = nv_crtc_save,
++ .restore = nv_crtc_restore,
++ .cursor_set = nv04_crtc_cursor_set,
++ .cursor_move = nv04_crtc_cursor_move,
++ .gamma_set = nv_crtc_gamma_set,
++ .set_config = drm_crtc_helper_set_config,
++ .destroy = nv_crtc_destroy,
++};
++
++static const struct drm_crtc_helper_funcs nv04_crtc_helper_funcs = {
++ .dpms = nv_crtc_dpms,
++ .prepare = nv_crtc_prepare,
++ .commit = nv_crtc_commit,
++ .mode_fixup = nv_crtc_mode_fixup,
++ .mode_set = nv_crtc_mode_set,
++ .mode_set_base = nv04_crtc_mode_set_base,
++ .load_lut = nv_crtc_gamma_load,
++};
++
++int
++nv04_crtc_create(struct drm_device *dev, int crtc_num)
++{
++ struct nouveau_crtc *nv_crtc;
++ int ret, i;
++
++ nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL);
++ if (!nv_crtc)
++ return -ENOMEM;
++
++ for (i = 0; i < 256; i++) {
++ nv_crtc->lut.r[i] = i << 8;
++ nv_crtc->lut.g[i] = i << 8;
++ nv_crtc->lut.b[i] = i << 8;
++ }
++ nv_crtc->lut.depth = 0;
++
++ nv_crtc->index = crtc_num;
++ nv_crtc->last_dpms = NV_DPMS_CLEARED;
++
++ drm_crtc_init(dev, &nv_crtc->base, &nv04_crtc_funcs);
++ drm_crtc_helper_add(&nv_crtc->base, &nv04_crtc_helper_funcs);
++ drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256);
++
++ ret = nouveau_bo_new(dev, NULL, 64*64*4, 0x100, TTM_PL_FLAG_VRAM,
++ 0, 0x0000, false, true, &nv_crtc->cursor.nvbo);
++ if (!ret) {
++ ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM);
++ if (!ret)
++ ret = nouveau_bo_map(nv_crtc->cursor.nvbo);
++ if (ret)
++ nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
++ }
++
++ nv04_cursor_init(nv_crtc);
++
++ return 0;
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nv04_cursor.c b/drivers/gpu/drm/nouveau/nv04_cursor.c
+new file mode 100644
+index 0000000..89a91b9
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv04_cursor.c
+@@ -0,0 +1,70 @@
++#include "drmP.h"
++#include "drm_mode.h"
++#include "nouveau_reg.h"
++#include "nouveau_drv.h"
++#include "nouveau_crtc.h"
++#include "nouveau_hw.h"
++
++static void
++nv04_cursor_show(struct nouveau_crtc *nv_crtc, bool update)
++{
++ nv_show_cursor(nv_crtc->base.dev, nv_crtc->index, true);
++}
++
++static void
++nv04_cursor_hide(struct nouveau_crtc *nv_crtc, bool update)
++{
++ nv_show_cursor(nv_crtc->base.dev, nv_crtc->index, false);
++}
++
++static void
++nv04_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y)
++{
++ NVWriteRAMDAC(nv_crtc->base.dev, nv_crtc->index,
++ NV_PRAMDAC_CU_START_POS,
++ XLATE(y, 0, NV_PRAMDAC_CU_START_POS_Y) |
++ XLATE(x, 0, NV_PRAMDAC_CU_START_POS_X));
++}
++
++static void
++crtc_wr_cio_state(struct drm_crtc *crtc, struct nv04_crtc_reg *crtcstate, int index)
++{
++ NVWriteVgaCrtc(crtc->dev, nouveau_crtc(crtc)->index, index,
++ crtcstate->CRTC[index]);
++}
++
++static void
++nv04_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset)
++{
++ struct drm_device *dev = nv_crtc->base.dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
++ struct drm_crtc *crtc = &nv_crtc->base;
++
++ regp->CRTC[NV_CIO_CRE_HCUR_ADDR0_INDEX] =
++ MASK(NV_CIO_CRE_HCUR_ASI) |
++ XLATE(offset, 17, NV_CIO_CRE_HCUR_ADDR0_ADR);
++ regp->CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX] =
++ XLATE(offset, 11, NV_CIO_CRE_HCUR_ADDR1_ADR);
++ if (crtc->mode.flags & DRM_MODE_FLAG_DBLSCAN)
++ regp->CRTC[NV_CIO_CRE_HCUR_ADDR1_INDEX] |=
++ MASK(NV_CIO_CRE_HCUR_ADDR1_CUR_DBL);
++ regp->CRTC[NV_CIO_CRE_HCUR_ADDR2_INDEX] = offset >> 24;
++
++ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR0_INDEX);
++ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR1_INDEX);
++ crtc_wr_cio_state(crtc, regp, NV_CIO_CRE_HCUR_ADDR2_INDEX);
++ if (dev_priv->card_type == NV_40)
++ nv_fix_nv40_hw_cursor(dev, nv_crtc->index);
++}
++
++int
++nv04_cursor_init(struct nouveau_crtc *crtc)
++{
++ crtc->cursor.set_offset = nv04_cursor_set_offset;
++ crtc->cursor.set_pos = nv04_cursor_set_pos;
++ crtc->cursor.hide = nv04_cursor_hide;
++ crtc->cursor.show = nv04_cursor_show;
++ return 0;
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nv04_dac.c b/drivers/gpu/drm/nouveau/nv04_dac.c
+new file mode 100644
+index 0000000..d0e038d
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv04_dac.c
+@@ -0,0 +1,527 @@
++/*
++ * Copyright 2003 NVIDIA, Corporation
++ * Copyright 2006 Dave Airlie
++ * Copyright 2007 Maarten Maathuis
++ * Copyright 2007-2009 Stuart Bennett
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#include "drmP.h"
++#include "drm_crtc_helper.h"
++
++#include "nouveau_drv.h"
++#include "nouveau_encoder.h"
++#include "nouveau_connector.h"
++#include "nouveau_crtc.h"
++#include "nouveau_hw.h"
++#include "nvreg.h"
++
++int nv04_dac_output_offset(struct drm_encoder *encoder)
++{
++ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
++ int offset = 0;
++
++ if (dcb->or & (8 | OUTPUT_C))
++ offset += 0x68;
++ if (dcb->or & (8 | OUTPUT_B))
++ offset += 0x2000;
++
++ return offset;
++}
++
++/*
++ * arbitrary limit to number of sense oscillations tolerated in one sample
++ * period (observed to be at least 13 in "nvidia")
++ */
++#define MAX_HBLANK_OSC 20
++
++/*
++ * arbitrary limit to number of conflicting sample pairs to tolerate at a
++ * voltage step (observed to be at least 5 in "nvidia")
++ */
++#define MAX_SAMPLE_PAIRS 10
++
++static int sample_load_twice(struct drm_device *dev, bool sense[2])
++{
++ int i;
++
++ for (i = 0; i < 2; i++) {
++ bool sense_a, sense_b, sense_b_prime;
++ int j = 0;
++
++ /*
++ * wait for bit 0 clear -- out of hblank -- (say reg value 0x4),
++ * then wait for transition 0x4->0x5->0x4: enter hblank, leave
++ * hblank again
++ * use a 10ms timeout (guards against crtc being inactive, in
++ * which case blank state would never change)
++ */
++ if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
++ 0x00000001, 0x00000000))
++ return -EBUSY;
++ if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
++ 0x00000001, 0x00000001))
++ return -EBUSY;
++ if (!nouveau_wait_until(dev, 10000000, NV_PRMCIO_INP0__COLOR,
++ 0x00000001, 0x00000000))
++ return -EBUSY;
++
++ udelay(100);
++ /* when level triggers, sense is _LO_ */
++ sense_a = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
++
++ /* take another reading until it agrees with sense_a... */
++ do {
++ udelay(100);
++ sense_b = nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
++ if (sense_a != sense_b) {
++ sense_b_prime =
++ nv_rd08(dev, NV_PRMCIO_INP0) & 0x10;
++ if (sense_b == sense_b_prime) {
++ /* ... unless two consecutive subsequent
++ * samples agree; sense_a is replaced */
++ sense_a = sense_b;
++ /* force mis-match so we loop */
++ sense_b = !sense_a;
++ }
++ }
++ } while ((sense_a != sense_b) && ++j < MAX_HBLANK_OSC);
++
++ if (j == MAX_HBLANK_OSC)
++ /* with so much oscillation, default to sense:LO */
++ sense[i] = false;
++ else
++ sense[i] = sense_a;
++ }
++
++ return 0;
++}
++
++static enum drm_connector_status nv04_dac_detect(struct drm_encoder *encoder,
++ struct drm_connector *connector)
++{
++ struct drm_device *dev = encoder->dev;
++ uint8_t saved_seq1, saved_pi, saved_rpc1;
++ uint8_t saved_palette0[3], saved_palette_mask;
++ uint32_t saved_rtest_ctrl, saved_rgen_ctrl;
++ int i;
++ uint8_t blue;
++ bool sense = true;
++
++ /*
++ * for this detection to work, there needs to be a mode set up on the
++ * CRTC. this is presumed to be the case
++ */
++
++ if (nv_two_heads(dev))
++ /* only implemented for head A for now */
++ NVSetOwner(dev, 0);
++
++ saved_seq1 = NVReadVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX);
++ NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1 & ~0x20);
++
++ saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL);
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL,
++ saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
++
++ msleep(10);
++
++ saved_pi = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX);
++ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX,
++ saved_pi & ~(0x80 | MASK(NV_CIO_CRE_PIXEL_FORMAT)));
++ saved_rpc1 = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX);
++ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1 & ~0xc0);
++
++ nv_wr08(dev, NV_PRMDIO_READ_MODE_ADDRESS, 0x0);
++ for (i = 0; i < 3; i++)
++ saved_palette0[i] = nv_rd08(dev, NV_PRMDIO_PALETTE_DATA);
++ saved_palette_mask = nv_rd08(dev, NV_PRMDIO_PIXEL_MASK);
++ nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, 0);
++
++ saved_rgen_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL);
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL,
++ (saved_rgen_ctrl & ~(NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS |
++ NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM)) |
++ NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON);
++
++ blue = 8; /* start of test range */
++
++ do {
++ bool sense_pair[2];
++
++ nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
++ nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0);
++ nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, 0);
++ /* testing blue won't find monochrome monitors. I don't care */
++ nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, blue);
++
++ i = 0;
++ /* take sample pairs until both samples in the pair agree */
++ do {
++ if (sample_load_twice(dev, sense_pair))
++ goto out;
++ } while ((sense_pair[0] != sense_pair[1]) &&
++ ++i < MAX_SAMPLE_PAIRS);
++
++ if (i == MAX_SAMPLE_PAIRS)
++ /* too much oscillation defaults to LO */
++ sense = false;
++ else
++ sense = sense_pair[0];
++
++ /*
++ * if sense goes LO before blue ramps to 0x18, monitor is not connected.
++ * ergo, if blue gets to 0x18, monitor must be connected
++ */
++ } while (++blue < 0x18 && sense);
++
++out:
++ nv_wr08(dev, NV_PRMDIO_PIXEL_MASK, saved_palette_mask);
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_GENERAL_CONTROL, saved_rgen_ctrl);
++ nv_wr08(dev, NV_PRMDIO_WRITE_MODE_ADDRESS, 0);
++ for (i = 0; i < 3; i++)
++ nv_wr08(dev, NV_PRMDIO_PALETTE_DATA, saved_palette0[i]);
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL, saved_rtest_ctrl);
++ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX, saved_pi);
++ NVWriteVgaCrtc(dev, 0, NV_CIO_CRE_RPC1_INDEX, saved_rpc1);
++ NVWriteVgaSeq(dev, 0, NV_VIO_SR_CLOCK_INDEX, saved_seq1);
++
++ if (blue == 0x18) {
++ NV_INFO(dev, "Load detected on head A\n");
++ return connector_status_connected;
++ }
++
++ return connector_status_disconnected;
++}
++
++uint32_t nv17_dac_sample_load(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
++ uint32_t sample, testval, regoffset = nv04_dac_output_offset(encoder);
++ uint32_t saved_powerctrl_2 = 0, saved_powerctrl_4 = 0, saved_routput,
++ saved_rtest_ctrl, saved_gpio0, saved_gpio1, temp, routput;
++ int head;
++
++#define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20)
++ if (dcb->type == OUTPUT_TV) {
++ testval = RGB_TEST_DATA(0xa0, 0xa0, 0xa0);
++
++ if (dev_priv->vbios->tvdactestval)
++ testval = dev_priv->vbios->tvdactestval;
++ } else {
++ testval = RGB_TEST_DATA(0x140, 0x140, 0x140); /* 0x94050140 */
++
++ if (dev_priv->vbios->dactestval)
++ testval = dev_priv->vbios->dactestval;
++ }
++
++ saved_rtest_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset,
++ saved_rtest_ctrl & ~NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF);
++
++ saved_powerctrl_2 = nvReadMC(dev, NV_PBUS_POWERCTRL_2);
++
++ nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2 & 0xd7ffffff);
++ if (regoffset == 0x68) {
++ saved_powerctrl_4 = nvReadMC(dev, NV_PBUS_POWERCTRL_4);
++ nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4 & 0xffffffcf);
++ }
++
++ saved_gpio1 = nv17_gpio_get(dev, DCB_GPIO_TVDAC1);
++ saved_gpio0 = nv17_gpio_get(dev, DCB_GPIO_TVDAC0);
++
++ nv17_gpio_set(dev, DCB_GPIO_TVDAC1, dcb->type == OUTPUT_TV);
++ nv17_gpio_set(dev, DCB_GPIO_TVDAC0, dcb->type == OUTPUT_TV);
++
++ msleep(4);
++
++ saved_routput = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
++ head = (saved_routput & 0x100) >> 8;
++#if 0
++ /* if there's a spare crtc, using it will minimise flicker for the case
++ * where the in-use crtc is in use by an off-chip tmds encoder */
++ if (xf86_config->crtc[head]->enabled && !xf86_config->crtc[head ^ 1]->enabled)
++ head ^= 1;
++#endif
++ /* nv driver and nv31 use 0xfffffeee, nv34 and 6600 use 0xfffffece */
++ routput = (saved_routput & 0xfffffece) | head << 8;
++
++ if (dev_priv->card_type >= NV_40) {
++ if (dcb->type == OUTPUT_TV)
++ routput |= 0x1a << 16;
++ else
++ routput &= ~(0x1a << 16);
++ }
++
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, routput);
++ msleep(1);
++
++ temp = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, temp | 1);
++
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA,
++ NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK | testval);
++ temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
++ temp | NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED);
++ msleep(5);
++
++ sample = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
++
++ temp = NVReadRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TEST_CONTROL,
++ temp & ~NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TESTPOINT_DATA, 0);
++
++ /* bios does something more complex for restoring, but I think this is good enough */
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, saved_routput);
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, saved_rtest_ctrl);
++ if (regoffset == 0x68)
++ nvWriteMC(dev, NV_PBUS_POWERCTRL_4, saved_powerctrl_4);
++ nvWriteMC(dev, NV_PBUS_POWERCTRL_2, saved_powerctrl_2);
++
++ nv17_gpio_set(dev, DCB_GPIO_TVDAC1, saved_gpio1);
++ nv17_gpio_set(dev, DCB_GPIO_TVDAC0, saved_gpio0);
++
++ return sample;
++}
++
++static enum drm_connector_status
++nv17_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
++{
++ struct drm_device *dev = encoder->dev;
++ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
++ uint32_t sample = nv17_dac_sample_load(encoder);
++
++ if (sample & NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI) {
++ NV_INFO(dev, "Load detected on output %c\n",
++ '@' + ffs(dcb->or));
++ return connector_status_connected;
++ } else {
++ return connector_status_disconnected;
++ }
++}
++
++static bool nv04_dac_mode_fixup(struct drm_encoder *encoder,
++ struct drm_display_mode *mode,
++ struct drm_display_mode *adjusted_mode)
++{
++ return true;
++}
++
++static void nv04_dac_prepare(struct drm_encoder *encoder)
++{
++ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
++ struct drm_device *dev = encoder->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int head = nouveau_crtc(encoder->crtc)->index;
++ struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
++
++ helper->dpms(encoder, DRM_MODE_DPMS_OFF);
++
++ nv04_dfp_disable(dev, head);
++
++ /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
++ * at LCD__INDEX which we don't alter
++ */
++ if (!(crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] & 0x44))
++ crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX] = 0;
++}
++
++
++static void nv04_dac_mode_set(struct drm_encoder *encoder,
++ struct drm_display_mode *mode,
++ struct drm_display_mode *adjusted_mode)
++{
++ struct drm_device *dev = encoder->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int head = nouveau_crtc(encoder->crtc)->index;
++
++ if (nv_gf4_disp_arch(dev)) {
++ struct drm_encoder *rebind;
++ uint32_t dac_offset = nv04_dac_output_offset(encoder);
++ uint32_t otherdac;
++
++ /* bit 16-19 are bits that are set on some G70 cards,
++ * but don't seem to have much effect */
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset,
++ head << 8 | NV_PRAMDAC_DACCLK_SEL_DACCLK);
++ /* force any other vga encoders to bind to the other crtc */
++ list_for_each_entry(rebind, &dev->mode_config.encoder_list, head) {
++ if (rebind == encoder
++ || nouveau_encoder(rebind)->dcb->type != OUTPUT_ANALOG)
++ continue;
++
++ dac_offset = nv04_dac_output_offset(rebind);
++ otherdac = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset);
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + dac_offset,
++ (otherdac & ~0x0100) | (head ^ 1) << 8);
++ }
++ }
++
++ /* This could use refinement for flatpanels, but it should work this way */
++ if (dev_priv->chipset < 0x44)
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000);
++ else
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000);
++}
++
++static void nv04_dac_commit(struct drm_encoder *encoder)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_device *dev = encoder->dev;
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
++ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
++
++ helper->dpms(encoder, DRM_MODE_DPMS_ON);
++
++ NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
++ drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
++ nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
++}
++
++void nv04_dac_update_dacclk(struct drm_encoder *encoder, bool enable)
++{
++ struct drm_device *dev = encoder->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
++
++ if (nv_gf4_disp_arch(dev)) {
++ uint32_t *dac_users = &dev_priv->dac_users[ffs(dcb->or) - 1];
++ int dacclk_off = NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder);
++ uint32_t dacclk = NVReadRAMDAC(dev, 0, dacclk_off);
++
++ if (enable) {
++ *dac_users |= 1 << dcb->index;
++ NVWriteRAMDAC(dev, 0, dacclk_off, dacclk | NV_PRAMDAC_DACCLK_SEL_DACCLK);
++
++ } else {
++ *dac_users &= ~(1 << dcb->index);
++ if (!*dac_users)
++ NVWriteRAMDAC(dev, 0, dacclk_off,
++ dacclk & ~NV_PRAMDAC_DACCLK_SEL_DACCLK);
++ }
++ }
++}
++
++static void nv04_dac_dpms(struct drm_encoder *encoder, int mode)
++{
++ struct drm_device *dev = encoder->dev;
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++
++ if (nv_encoder->last_dpms == mode)
++ return;
++ nv_encoder->last_dpms = mode;
++
++ NV_INFO(dev, "Setting dpms mode %d on vga encoder (output %d)\n",
++ mode, nv_encoder->dcb->index);
++
++ nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON);
++}
++
++static void nv04_dac_save(struct drm_encoder *encoder)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_device *dev = encoder->dev;
++
++ if (nv_gf4_disp_arch(dev))
++ nv_encoder->restore.output = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK +
++ nv04_dac_output_offset(encoder));
++}
++
++static void nv04_dac_restore(struct drm_encoder *encoder)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_device *dev = encoder->dev;
++
++ if (nv_gf4_disp_arch(dev))
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + nv04_dac_output_offset(encoder),
++ nv_encoder->restore.output);
++
++ nv_encoder->last_dpms = NV_DPMS_CLEARED;
++}
++
++static void nv04_dac_destroy(struct drm_encoder *encoder)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++
++ NV_DEBUG_KMS(encoder->dev, "\n");
++
++ drm_encoder_cleanup(encoder);
++ kfree(nv_encoder);
++}
++
++static const struct drm_encoder_helper_funcs nv04_dac_helper_funcs = {
++ .dpms = nv04_dac_dpms,
++ .save = nv04_dac_save,
++ .restore = nv04_dac_restore,
++ .mode_fixup = nv04_dac_mode_fixup,
++ .prepare = nv04_dac_prepare,
++ .commit = nv04_dac_commit,
++ .mode_set = nv04_dac_mode_set,
++ .detect = nv04_dac_detect
++};
++
++static const struct drm_encoder_helper_funcs nv17_dac_helper_funcs = {
++ .dpms = nv04_dac_dpms,
++ .save = nv04_dac_save,
++ .restore = nv04_dac_restore,
++ .mode_fixup = nv04_dac_mode_fixup,
++ .prepare = nv04_dac_prepare,
++ .commit = nv04_dac_commit,
++ .mode_set = nv04_dac_mode_set,
++ .detect = nv17_dac_detect
++};
++
++static const struct drm_encoder_funcs nv04_dac_funcs = {
++ .destroy = nv04_dac_destroy,
++};
++
++int nv04_dac_create(struct drm_device *dev, struct dcb_entry *entry)
++{
++ const struct drm_encoder_helper_funcs *helper;
++ struct drm_encoder *encoder;
++ struct nouveau_encoder *nv_encoder = NULL;
++
++ nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
++ if (!nv_encoder)
++ return -ENOMEM;
++
++ encoder = to_drm_encoder(nv_encoder);
++
++ nv_encoder->dcb = entry;
++ nv_encoder->or = ffs(entry->or) - 1;
++
++ if (nv_gf4_disp_arch(dev))
++ helper = &nv17_dac_helper_funcs;
++ else
++ helper = &nv04_dac_helper_funcs;
++
++ drm_encoder_init(dev, encoder, &nv04_dac_funcs, DRM_MODE_ENCODER_DAC);
++ drm_encoder_helper_add(encoder, helper);
++
++ encoder->possible_crtcs = entry->heads;
++ encoder->possible_clones = 0;
++
++ return 0;
++}
+diff --git a/drivers/gpu/drm/nouveau/nv04_dfp.c b/drivers/gpu/drm/nouveau/nv04_dfp.c
+new file mode 100644
+index 0000000..483f875
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv04_dfp.c
+@@ -0,0 +1,623 @@
++/*
++ * Copyright 2003 NVIDIA, Corporation
++ * Copyright 2006 Dave Airlie
++ * Copyright 2007 Maarten Maathuis
++ * Copyright 2007-2009 Stuart Bennett
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#include "drmP.h"
++#include "drm_crtc_helper.h"
++
++#include "nouveau_drv.h"
++#include "nouveau_encoder.h"
++#include "nouveau_connector.h"
++#include "nouveau_crtc.h"
++#include "nouveau_hw.h"
++#include "nvreg.h"
++
++#define FP_TG_CONTROL_ON (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS | \
++ NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS | \
++ NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS)
++#define FP_TG_CONTROL_OFF (NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE | \
++ NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE | \
++ NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE)
++
++static inline bool is_fpc_off(uint32_t fpc)
++{
++ return ((fpc & (FP_TG_CONTROL_ON | FP_TG_CONTROL_OFF)) ==
++ FP_TG_CONTROL_OFF);
++}
++
++int nv04_dfp_get_bound_head(struct drm_device *dev, struct dcb_entry *dcbent)
++{
++ /* special case of nv_read_tmds to find crtc associated with an output.
++ * this does not give a correct answer for off-chip dvi, but there's no
++ * use for such an answer anyway
++ */
++ int ramdac = (dcbent->or & OUTPUT_C) >> 2;
++
++ NVWriteRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_CONTROL,
++ NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE | 0x4);
++ return ((NVReadRAMDAC(dev, ramdac, NV_PRAMDAC_FP_TMDS_DATA) & 0x8) >> 3) ^ ramdac;
++}
++
++void nv04_dfp_bind_head(struct drm_device *dev, struct dcb_entry *dcbent,
++ int head, bool dl)
++{
++ /* The BIOS scripts don't do this for us, sadly
++ * Luckily we do know the values ;-)
++ *
++ * head < 0 indicates we wish to force a setting with the overrideval
++ * (for VT restore etc.)
++ */
++
++ int ramdac = (dcbent->or & OUTPUT_C) >> 2;
++ uint8_t tmds04 = 0x80;
++
++ if (head != ramdac)
++ tmds04 = 0x88;
++
++ if (dcbent->type == OUTPUT_LVDS)
++ tmds04 |= 0x01;
++
++ nv_write_tmds(dev, dcbent->or, 0, 0x04, tmds04);
++
++ if (dl) /* dual link */
++ nv_write_tmds(dev, dcbent->or, 1, 0x04, tmds04 ^ 0x08);
++}
++
++void nv04_dfp_disable(struct drm_device *dev, int head)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
++
++ if (NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL) &
++ FP_TG_CONTROL_ON) {
++ /* digital remnants must be cleaned before new crtc
++ * values programmed. delay is time for the vga stuff
++ * to realise it's in control again
++ */
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL,
++ FP_TG_CONTROL_OFF);
++ msleep(50);
++ }
++ /* don't inadvertently turn it on when state written later */
++ crtcstate[head].fp_control = FP_TG_CONTROL_OFF;
++}
++
++void nv04_dfp_update_fp_control(struct drm_encoder *encoder, int mode)
++{
++ struct drm_device *dev = encoder->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct drm_crtc *crtc;
++ struct nouveau_crtc *nv_crtc;
++ uint32_t *fpc;
++
++ if (mode == DRM_MODE_DPMS_ON) {
++ nv_crtc = nouveau_crtc(encoder->crtc);
++ fpc = &dev_priv->mode_reg.crtc_reg[nv_crtc->index].fp_control;
++
++ if (is_fpc_off(*fpc)) {
++ /* using saved value is ok, as (is_digital && dpms_on &&
++ * fp_control==OFF) is (at present) *only* true when
++ * fpc's most recent change was by below "off" code
++ */
++ *fpc = nv_crtc->dpms_saved_fp_control;
++ }
++
++ nv_crtc->fp_users |= 1 << nouveau_encoder(encoder)->dcb->index;
++ NVWriteRAMDAC(dev, nv_crtc->index, NV_PRAMDAC_FP_TG_CONTROL, *fpc);
++ } else {
++ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
++ nv_crtc = nouveau_crtc(crtc);
++ fpc = &dev_priv->mode_reg.crtc_reg[nv_crtc->index].fp_control;
++
++ nv_crtc->fp_users &= ~(1 << nouveau_encoder(encoder)->dcb->index);
++ if (!is_fpc_off(*fpc) && !nv_crtc->fp_users) {
++ nv_crtc->dpms_saved_fp_control = *fpc;
++ /* cut the FP output */
++ *fpc &= ~FP_TG_CONTROL_ON;
++ *fpc |= FP_TG_CONTROL_OFF;
++ NVWriteRAMDAC(dev, nv_crtc->index,
++ NV_PRAMDAC_FP_TG_CONTROL, *fpc);
++ }
++ }
++ }
++}
++
++static bool nv04_dfp_mode_fixup(struct drm_encoder *encoder,
++ struct drm_display_mode *mode,
++ struct drm_display_mode *adjusted_mode)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct nouveau_connector *nv_connector = nouveau_encoder_connector_get(nv_encoder);
++
++ /* For internal panels and gpu scaling on DVI we need the native mode */
++ if (nv_connector->scaling_mode != DRM_MODE_SCALE_NONE) {
++ if (!nv_connector->native_mode)
++ return false;
++ nv_encoder->mode = *nv_connector->native_mode;
++ adjusted_mode->clock = nv_connector->native_mode->clock;
++ } else {
++ nv_encoder->mode = *adjusted_mode;
++ }
++
++ return true;
++}
++
++static void nv04_dfp_prepare_sel_clk(struct drm_device *dev,
++ struct nouveau_encoder *nv_encoder, int head)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv04_mode_state *state = &dev_priv->mode_reg;
++ uint32_t bits1618 = nv_encoder->dcb->or & OUTPUT_A ? 0x10000 : 0x40000;
++
++ if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP)
++ return;
++
++ /* SEL_CLK is only used on the primary ramdac
++ * It toggles spread spectrum PLL output and sets the bindings of PLLs
++ * to heads on digital outputs
++ */
++ if (head)
++ state->sel_clk |= bits1618;
++ else
++ state->sel_clk &= ~bits1618;
++
++ /* nv30:
++ * bit 0 NVClk spread spectrum on/off
++ * bit 2 MemClk spread spectrum on/off
++ * bit 4 PixClk1 spread spectrum on/off toggle
++ * bit 6 PixClk2 spread spectrum on/off toggle
++ *
++ * nv40 (observations from bios behaviour and mmio traces):
++ * bits 4&6 as for nv30
++ * bits 5&7 head dependent as for bits 4&6, but do not appear with 4&6;
++ * maybe a different spread mode
++ * bits 8&10 seen on dual-link dvi outputs, purpose unknown (set by POST scripts)
++ * The logic behind turning spread spectrum on/off in the first place,
++ * and which bit-pair to use, is unclear on nv40 (for earlier cards, the fp table
++ * entry has the necessary info)
++ */
++ if (nv_encoder->dcb->type == OUTPUT_LVDS && dev_priv->saved_reg.sel_clk & 0xf0) {
++ int shift = (dev_priv->saved_reg.sel_clk & 0x50) ? 0 : 1;
++
++ state->sel_clk &= ~0xf0;
++ state->sel_clk |= (head ? 0x40 : 0x10) << shift;
++ }
++}
++
++static void nv04_dfp_prepare(struct drm_encoder *encoder)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
++ struct drm_device *dev = encoder->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int head = nouveau_crtc(encoder->crtc)->index;
++ struct nv04_crtc_reg *crtcstate = dev_priv->mode_reg.crtc_reg;
++ uint8_t *cr_lcd = &crtcstate[head].CRTC[NV_CIO_CRE_LCD__INDEX];
++ uint8_t *cr_lcd_oth = &crtcstate[head ^ 1].CRTC[NV_CIO_CRE_LCD__INDEX];
++
++ helper->dpms(encoder, DRM_MODE_DPMS_OFF);
++
++ nv04_dfp_prepare_sel_clk(dev, nv_encoder, head);
++
++ /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
++ * at LCD__INDEX which we don't alter
++ */
++ if (!(*cr_lcd & 0x44)) {
++ *cr_lcd = 0x3;
++
++ if (nv_two_heads(dev)) {
++ if (nv_encoder->dcb->location == DCB_LOC_ON_CHIP)
++ *cr_lcd |= head ? 0x0 : 0x8;
++ else {
++ *cr_lcd |= (nv_encoder->dcb->or << 4) & 0x30;
++ if (nv_encoder->dcb->type == OUTPUT_LVDS)
++ *cr_lcd |= 0x30;
++ if ((*cr_lcd & 0x30) == (*cr_lcd_oth & 0x30)) {
++ /* avoid being connected to both crtcs */
++ *cr_lcd_oth &= ~0x30;
++ NVWriteVgaCrtc(dev, head ^ 1,
++ NV_CIO_CRE_LCD__INDEX,
++ *cr_lcd_oth);
++ }
++ }
++ }
++ }
++}
++
++
++static void nv04_dfp_mode_set(struct drm_encoder *encoder,
++ struct drm_display_mode *mode,
++ struct drm_display_mode *adjusted_mode)
++{
++ struct drm_device *dev = encoder->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
++ struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
++ struct nv04_crtc_reg *savep = &dev_priv->saved_reg.crtc_reg[nv_crtc->index];
++ struct nouveau_connector *nv_connector = nouveau_crtc_connector_get(nv_crtc);
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_display_mode *output_mode = &nv_encoder->mode;
++ uint32_t mode_ratio, panel_ratio;
++
++ NV_DEBUG_KMS(dev, "Output mode on CRTC %d:\n", nv_crtc->index);
++ drm_mode_debug_printmodeline(output_mode);
++
++ /* Initialize the FP registers in this CRTC. */
++ regp->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1;
++ regp->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1;
++ if (!nv_gf4_disp_arch(dev) ||
++ (output_mode->hsync_start - output_mode->hdisplay) >=
++ dev_priv->vbios->digital_min_front_porch)
++ regp->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay;
++ else
++ regp->fp_horiz_regs[FP_CRTC] = output_mode->hsync_start - dev_priv->vbios->digital_min_front_porch - 1;
++ regp->fp_horiz_regs[FP_SYNC_START] = output_mode->hsync_start - 1;
++ regp->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1;
++ regp->fp_horiz_regs[FP_VALID_START] = output_mode->hskew;
++ regp->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - 1;
++
++ regp->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1;
++ regp->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1;
++ regp->fp_vert_regs[FP_CRTC] = output_mode->vtotal - 5 - 1;
++ regp->fp_vert_regs[FP_SYNC_START] = output_mode->vsync_start - 1;
++ regp->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1;
++ regp->fp_vert_regs[FP_VALID_START] = 0;
++ regp->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - 1;
++
++ /* bit26: a bit seen on some g7x, no as yet discernable purpose */
++ regp->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |
++ (savep->fp_control & (1 << 26 | NV_PRAMDAC_FP_TG_CONTROL_READ_PROG));
++ /* Deal with vsync/hsync polarity */
++ /* LVDS screens do set this, but modes with +ve syncs are very rare */
++ if (output_mode->flags & DRM_MODE_FLAG_PVSYNC)
++ regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS;
++ if (output_mode->flags & DRM_MODE_FLAG_PHSYNC)
++ regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS;
++ /* panel scaling first, as native would get set otherwise */
++ if (nv_connector->scaling_mode == DRM_MODE_SCALE_NONE ||
++ nv_connector->scaling_mode == DRM_MODE_SCALE_CENTER) /* panel handles it */
++ regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER;
++ else if (adjusted_mode->hdisplay == output_mode->hdisplay &&
++ adjusted_mode->vdisplay == output_mode->vdisplay) /* native mode */
++ regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE;
++ else /* gpu needs to scale */
++ regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE;
++ if (nvReadEXTDEV(dev, NV_PEXTDEV_BOOT_0) & NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT)
++ regp->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12;
++ if (nv_encoder->dcb->location != DCB_LOC_ON_CHIP &&
++ output_mode->clock > 165000)
++ regp->fp_control |= (2 << 24);
++ if (nv_encoder->dcb->type == OUTPUT_LVDS) {
++ bool duallink, dummy;
++
++ nouveau_bios_parse_lvds_table(dev, nv_connector->native_mode->
++ clock, &duallink, &dummy);
++ if (duallink)
++ regp->fp_control |= (8 << 28);
++ } else
++ if (output_mode->clock > 165000)
++ regp->fp_control |= (8 << 28);
++
++ regp->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND |
++ NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND |
++ NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR |
++ NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR |
++ NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED |
++ NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE |
++ NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE;
++
++ /* We want automatic scaling */
++ regp->fp_debug_1 = 0;
++ /* This can override HTOTAL and VTOTAL */
++ regp->fp_debug_2 = 0;
++
++ /* Use 20.12 fixed point format to avoid floats */
++ mode_ratio = (1 << 12) * adjusted_mode->hdisplay / adjusted_mode->vdisplay;
++ panel_ratio = (1 << 12) * output_mode->hdisplay / output_mode->vdisplay;
++ /* if ratios are equal, SCALE_ASPECT will automatically (and correctly)
++ * get treated the same as SCALE_FULLSCREEN */
++ if (nv_connector->scaling_mode == DRM_MODE_SCALE_ASPECT &&
++ mode_ratio != panel_ratio) {
++ uint32_t diff, scale;
++ bool divide_by_2 = nv_gf4_disp_arch(dev);
++
++ if (mode_ratio < panel_ratio) {
++ /* vertical needs to expand to glass size (automatic)
++ * horizontal needs to be scaled at vertical scale factor
++ * to maintain aspect */
++
++ scale = (1 << 12) * adjusted_mode->vdisplay / output_mode->vdisplay;
++ regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE |
++ XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE);
++
++ /* restrict area of screen used, horizontally */
++ diff = output_mode->hdisplay -
++ output_mode->vdisplay * mode_ratio / (1 << 12);
++ regp->fp_horiz_regs[FP_VALID_START] += diff / 2;
++ regp->fp_horiz_regs[FP_VALID_END] -= diff / 2;
++ }
++
++ if (mode_ratio > panel_ratio) {
++ /* horizontal needs to expand to glass size (automatic)
++ * vertical needs to be scaled at horizontal scale factor
++ * to maintain aspect */
++
++ scale = (1 << 12) * adjusted_mode->hdisplay / output_mode->hdisplay;
++ regp->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE |
++ XLATE(scale, divide_by_2, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE);
++
++ /* restrict area of screen used, vertically */
++ diff = output_mode->vdisplay -
++ (1 << 12) * output_mode->hdisplay / mode_ratio;
++ regp->fp_vert_regs[FP_VALID_START] += diff / 2;
++ regp->fp_vert_regs[FP_VALID_END] -= diff / 2;
++ }
++ }
++
++ /* Output property. */
++ if (nv_connector->use_dithering) {
++ if (dev_priv->chipset == 0x11)
++ regp->dither = savep->dither | 0x00010000;
++ else {
++ int i;
++ regp->dither = savep->dither | 0x00000001;
++ for (i = 0; i < 3; i++) {
++ regp->dither_regs[i] = 0xe4e4e4e4;
++ regp->dither_regs[i + 3] = 0x44444444;
++ }
++ }
++ } else {
++ if (dev_priv->chipset != 0x11) {
++ /* reset them */
++ int i;
++ for (i = 0; i < 3; i++) {
++ regp->dither_regs[i] = savep->dither_regs[i];
++ regp->dither_regs[i + 3] = savep->dither_regs[i + 3];
++ }
++ }
++ regp->dither = savep->dither;
++ }
++
++ regp->fp_margin_color = 0;
++}
++
++static void nv04_dfp_commit(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct dcb_entry *dcbe = nv_encoder->dcb;
++ int head = nouveau_crtc(encoder->crtc)->index;
++
++ NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
++ drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
++ nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
++
++ if (dcbe->type == OUTPUT_TMDS)
++ run_tmds_table(dev, dcbe, head, nv_encoder->mode.clock);
++ else if (dcbe->type == OUTPUT_LVDS)
++ call_lvds_script(dev, dcbe, head, LVDS_RESET, nv_encoder->mode.clock);
++
++ /* update fp_control state for any changes made by scripts,
++ * so correct value is written at DPMS on */
++ dev_priv->mode_reg.crtc_reg[head].fp_control =
++ NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL);
++
++ /* This could use refinement for flatpanels, but it should work this way */
++ if (dev_priv->chipset < 0x44)
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0xf0000000);
++ else
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + nv04_dac_output_offset(encoder), 0x00100000);
++
++ helper->dpms(encoder, DRM_MODE_DPMS_ON);
++
++ NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
++ drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base),
++ nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
++}
++
++static inline bool is_powersaving_dpms(int mode)
++{
++ return (mode != DRM_MODE_DPMS_ON);
++}
++
++static void nv04_lvds_dpms(struct drm_encoder *encoder, int mode)
++{
++ struct drm_device *dev = encoder->dev;
++ struct drm_crtc *crtc = encoder->crtc;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ bool was_powersaving = is_powersaving_dpms(nv_encoder->last_dpms);
++
++ if (nv_encoder->last_dpms == mode)
++ return;
++ nv_encoder->last_dpms = mode;
++
++ NV_INFO(dev, "Setting dpms mode %d on lvds encoder (output %d)\n",
++ mode, nv_encoder->dcb->index);
++
++ if (was_powersaving && is_powersaving_dpms(mode))
++ return;
++
++ if (nv_encoder->dcb->lvdsconf.use_power_scripts) {
++ struct nouveau_connector *nv_connector = nouveau_encoder_connector_get(nv_encoder);
++
++ /* when removing an output, crtc may not be set, but PANEL_OFF
++ * must still be run
++ */
++ int head = crtc ? nouveau_crtc(crtc)->index :
++ nv04_dfp_get_bound_head(dev, nv_encoder->dcb);
++
++ if (mode == DRM_MODE_DPMS_ON) {
++ if (!nv_connector->native_mode) {
++ NV_ERROR(dev, "Not turning on LVDS without native mode\n");
++ return;
++ }
++ call_lvds_script(dev, nv_encoder->dcb, head,
++ LVDS_PANEL_ON, nv_connector->native_mode->clock);
++ } else
++ /* pxclk of 0 is fine for PANEL_OFF, and for a
++ * disconnected LVDS encoder there is no native_mode
++ */
++ call_lvds_script(dev, nv_encoder->dcb, head,
++ LVDS_PANEL_OFF, 0);
++ }
++
++ nv04_dfp_update_fp_control(encoder, mode);
++
++ if (mode == DRM_MODE_DPMS_ON)
++ nv04_dfp_prepare_sel_clk(dev, nv_encoder, nouveau_crtc(crtc)->index);
++ else {
++ dev_priv->mode_reg.sel_clk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK);
++ dev_priv->mode_reg.sel_clk &= ~0xf0;
++ }
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_SEL_CLK, dev_priv->mode_reg.sel_clk);
++}
++
++static void nv04_tmds_dpms(struct drm_encoder *encoder, int mode)
++{
++ struct drm_device *dev = encoder->dev;
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++
++ if (nv_encoder->last_dpms == mode)
++ return;
++ nv_encoder->last_dpms = mode;
++
++ NV_INFO(dev, "Setting dpms mode %d on tmds encoder (output %d)\n",
++ mode, nv_encoder->dcb->index);
++
++ nv04_dfp_update_fp_control(encoder, mode);
++}
++
++static void nv04_dfp_save(struct drm_encoder *encoder)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_device *dev = encoder->dev;
++
++ if (nv_two_heads(dev))
++ nv_encoder->restore.head =
++ nv04_dfp_get_bound_head(dev, nv_encoder->dcb);
++}
++
++static void nv04_dfp_restore(struct drm_encoder *encoder)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_device *dev = encoder->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int head = nv_encoder->restore.head;
++
++ if (nv_encoder->dcb->type == OUTPUT_LVDS) {
++ struct drm_display_mode *native_mode = nouveau_encoder_connector_get(nv_encoder)->native_mode;
++ if (native_mode)
++ call_lvds_script(dev, nv_encoder->dcb, head, LVDS_PANEL_ON,
++ native_mode->clock);
++ else
++ NV_ERROR(dev, "Not restoring LVDS without native mode\n");
++
++ } else if (nv_encoder->dcb->type == OUTPUT_TMDS) {
++ int clock = nouveau_hw_pllvals_to_clk
++ (&dev_priv->saved_reg.crtc_reg[head].pllvals);
++
++ run_tmds_table(dev, nv_encoder->dcb, head, clock);
++ }
++
++ nv_encoder->last_dpms = NV_DPMS_CLEARED;
++}
++
++static void nv04_dfp_destroy(struct drm_encoder *encoder)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++
++ NV_DEBUG_KMS(encoder->dev, "\n");
++
++ drm_encoder_cleanup(encoder);
++ kfree(nv_encoder);
++}
++
++static const struct drm_encoder_helper_funcs nv04_lvds_helper_funcs = {
++ .dpms = nv04_lvds_dpms,
++ .save = nv04_dfp_save,
++ .restore = nv04_dfp_restore,
++ .mode_fixup = nv04_dfp_mode_fixup,
++ .prepare = nv04_dfp_prepare,
++ .commit = nv04_dfp_commit,
++ .mode_set = nv04_dfp_mode_set,
++ .detect = NULL,
++};
++
++static const struct drm_encoder_helper_funcs nv04_tmds_helper_funcs = {
++ .dpms = nv04_tmds_dpms,
++ .save = nv04_dfp_save,
++ .restore = nv04_dfp_restore,
++ .mode_fixup = nv04_dfp_mode_fixup,
++ .prepare = nv04_dfp_prepare,
++ .commit = nv04_dfp_commit,
++ .mode_set = nv04_dfp_mode_set,
++ .detect = NULL,
++};
++
++static const struct drm_encoder_funcs nv04_dfp_funcs = {
++ .destroy = nv04_dfp_destroy,
++};
++
++int nv04_dfp_create(struct drm_device *dev, struct dcb_entry *entry)
++{
++ const struct drm_encoder_helper_funcs *helper;
++ struct drm_encoder *encoder;
++ struct nouveau_encoder *nv_encoder = NULL;
++ int type;
++
++ switch (entry->type) {
++ case OUTPUT_TMDS:
++ type = DRM_MODE_ENCODER_TMDS;
++ helper = &nv04_tmds_helper_funcs;
++ break;
++ case OUTPUT_LVDS:
++ type = DRM_MODE_ENCODER_LVDS;
++ helper = &nv04_lvds_helper_funcs;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
++ if (!nv_encoder)
++ return -ENOMEM;
++
++ encoder = to_drm_encoder(nv_encoder);
++
++ nv_encoder->dcb = entry;
++ nv_encoder->or = ffs(entry->or) - 1;
++
++ drm_encoder_init(dev, encoder, &nv04_dfp_funcs, type);
++ drm_encoder_helper_add(encoder, helper);
++
++ encoder->possible_crtcs = entry->heads;
++ encoder->possible_clones = 0;
++
++ return 0;
++}
+diff --git a/drivers/gpu/drm/nouveau/nv04_display.c b/drivers/gpu/drm/nouveau/nv04_display.c
+new file mode 100644
+index 0000000..ef77215
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv04_display.c
+@@ -0,0 +1,287 @@
++/*
++ * Copyright 2009 Red Hat Inc.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ *
++ * Author: Ben Skeggs
++ */
++
++#include "drmP.h"
++#include "drm.h"
++#include "drm_crtc_helper.h"
++
++#include "nouveau_drv.h"
++#include "nouveau_fb.h"
++#include "nouveau_hw.h"
++#include "nouveau_encoder.h"
++#include "nouveau_connector.h"
++
++#define MULTIPLE_ENCODERS(e) (e & (e - 1))
++
++static void
++nv04_display_store_initial_head_owner(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ if (dev_priv->chipset != 0x11) {
++ dev_priv->crtc_owner = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_44);
++ goto ownerknown;
++ }
++
++ /* reading CR44 is broken on nv11, so we attempt to infer it */
++ if (nvReadMC(dev, NV_PBUS_DEBUG_1) & (1 << 28)) /* heads tied, restore both */
++ dev_priv->crtc_owner = 0x4;
++ else {
++ uint8_t slaved_on_A, slaved_on_B;
++ bool tvA = false;
++ bool tvB = false;
++
++ NVLockVgaCrtcs(dev, false);
++
++ slaved_on_B = NVReadVgaCrtc(dev, 1, NV_CIO_CRE_PIXEL_INDEX) &
++ 0x80;
++ if (slaved_on_B)
++ tvB = !(NVReadVgaCrtc(dev, 1, NV_CIO_CRE_LCD__INDEX) &
++ MASK(NV_CIO_CRE_LCD_LCD_SELECT));
++
++ slaved_on_A = NVReadVgaCrtc(dev, 0, NV_CIO_CRE_PIXEL_INDEX) &
++ 0x80;
++ if (slaved_on_A)
++ tvA = !(NVReadVgaCrtc(dev, 0, NV_CIO_CRE_LCD__INDEX) &
++ MASK(NV_CIO_CRE_LCD_LCD_SELECT));
++
++ NVLockVgaCrtcs(dev, true);
++
++ if (slaved_on_A && !tvA)
++ dev_priv->crtc_owner = 0x0;
++ else if (slaved_on_B && !tvB)
++ dev_priv->crtc_owner = 0x3;
++ else if (slaved_on_A)
++ dev_priv->crtc_owner = 0x0;
++ else if (slaved_on_B)
++ dev_priv->crtc_owner = 0x3;
++ else
++ dev_priv->crtc_owner = 0x0;
++ }
++
++ownerknown:
++ NV_INFO(dev, "Initial CRTC_OWNER is %d\n", dev_priv->crtc_owner);
++
++ /* we need to ensure the heads are not tied henceforth, or reading any
++ * 8 bit reg on head B will fail
++ * setting a single arbitrary head solves that */
++ NVSetOwner(dev, 0);
++}
++
++int
++nv04_display_create(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct parsed_dcb *dcb = dev_priv->vbios->dcb;
++ struct drm_encoder *encoder;
++ struct drm_crtc *crtc;
++ uint16_t connector[16] = { 0 };
++ int i, ret;
++
++ NV_DEBUG_KMS(dev, "\n");
++
++ if (nv_two_heads(dev))
++ nv04_display_store_initial_head_owner(dev);
++ nouveau_hw_save_vga_fonts(dev, 1);
++
++ drm_mode_config_init(dev);
++ drm_mode_create_scaling_mode_property(dev);
++ drm_mode_create_dithering_property(dev);
++
++ dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs;
++
++ dev->mode_config.min_width = 0;
++ dev->mode_config.min_height = 0;
++ switch (dev_priv->card_type) {
++ case NV_04:
++ dev->mode_config.max_width = 2048;
++ dev->mode_config.max_height = 2048;
++ break;
++ default:
++ dev->mode_config.max_width = 4096;
++ dev->mode_config.max_height = 4096;
++ break;
++ }
++
++ dev->mode_config.fb_base = dev_priv->fb_phys;
++
++ nv04_crtc_create(dev, 0);
++ if (nv_two_heads(dev))
++ nv04_crtc_create(dev, 1);
++
++ for (i = 0; i < dcb->entries; i++) {
++ struct dcb_entry *dcbent = &dcb->entry[i];
++
++ switch (dcbent->type) {
++ case OUTPUT_ANALOG:
++ ret = nv04_dac_create(dev, dcbent);
++ break;
++ case OUTPUT_LVDS:
++ case OUTPUT_TMDS:
++ ret = nv04_dfp_create(dev, dcbent);
++ break;
++ case OUTPUT_TV:
++ if (dcbent->location == DCB_LOC_ON_CHIP)
++ ret = nv17_tv_create(dev, dcbent);
++ else
++ ret = nv04_tv_create(dev, dcbent);
++ break;
++ default:
++ NV_WARN(dev, "DCB type %d not known\n", dcbent->type);
++ continue;
++ }
++
++ if (ret)
++ continue;
++
++ connector[dcbent->connector] |= (1 << dcbent->type);
++ }
++
++ for (i = 0; i < dcb->entries; i++) {
++ struct dcb_entry *dcbent = &dcb->entry[i];
++ uint16_t encoders;
++ int type;
++
++ encoders = connector[dcbent->connector];
++ if (!(encoders & (1 << dcbent->type)))
++ continue;
++ connector[dcbent->connector] = 0;
++
++ switch (dcbent->type) {
++ case OUTPUT_ANALOG:
++ if (!MULTIPLE_ENCODERS(encoders))
++ type = DRM_MODE_CONNECTOR_VGA;
++ else
++ type = DRM_MODE_CONNECTOR_DVII;
++ break;
++ case OUTPUT_TMDS:
++ if (!MULTIPLE_ENCODERS(encoders))
++ type = DRM_MODE_CONNECTOR_DVID;
++ else
++ type = DRM_MODE_CONNECTOR_DVII;
++ break;
++ case OUTPUT_LVDS:
++ type = DRM_MODE_CONNECTOR_LVDS;
++#if 0
++ /* don't create i2c adapter when lvds ddc not allowed */
++ if (dcbent->lvdsconf.use_straps_for_mode ||
++ dev_priv->vbios->fp_no_ddc)
++ i2c_index = 0xf;
++#endif
++ break;
++ case OUTPUT_TV:
++ type = DRM_MODE_CONNECTOR_TV;
++ break;
++ default:
++ type = DRM_MODE_CONNECTOR_Unknown;
++ continue;
++ }
++
++ nouveau_connector_create(dev, dcbent->connector, type);
++ }
++
++ /* Save previous state */
++ NVLockVgaCrtcs(dev, false);
++
++ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
++ crtc->funcs->save(crtc);
++
++ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
++ struct drm_encoder_helper_funcs *func = encoder->helper_private;
++
++ func->save(encoder);
++ }
++
++ return 0;
++}
++
++void
++nv04_display_destroy(struct drm_device *dev)
++{
++ struct drm_encoder *encoder;
++ struct drm_crtc *crtc;
++
++ NV_DEBUG_KMS(dev, "\n");
++
++ /* Turn every CRTC off. */
++ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
++ struct drm_mode_set modeset = {
++ .crtc = crtc,
++ };
++
++ crtc->funcs->set_config(&modeset);
++ }
++
++ /* Restore state */
++ NVLockVgaCrtcs(dev, false);
++
++ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
++ struct drm_encoder_helper_funcs *func = encoder->helper_private;
++
++ func->restore(encoder);
++ }
++
++ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
++ crtc->funcs->restore(crtc);
++
++ drm_mode_config_cleanup(dev);
++
++ nouveau_hw_save_vga_fonts(dev, 0);
++}
++
++void
++nv04_display_restore(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct drm_encoder *encoder;
++ struct drm_crtc *crtc;
++
++ NVLockVgaCrtcs(dev, false);
++
++ /* meh.. modeset apparently doesn't setup all the regs and depends
++ * on pre-existing state, for now load the state of the card *before*
++ * nouveau was loaded, and then do a modeset.
++ *
++ * best thing to do probably is to make save/restore routines not
++ * save/restore "pre-load" state, but more general so we can save
++ * on suspend too.
++ */
++ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
++ struct drm_encoder_helper_funcs *func = encoder->helper_private;
++
++ func->restore(encoder);
++ }
++
++ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head)
++ crtc->funcs->restore(crtc);
++
++ if (nv_two_heads(dev)) {
++ NV_INFO(dev, "Restoring CRTC_OWNER to %d.\n",
++ dev_priv->crtc_owner);
++ NVSetOwner(dev, dev_priv->crtc_owner);
++ }
++
++ NVLockVgaCrtcs(dev, true);
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nv04_fb.c b/drivers/gpu/drm/nouveau/nv04_fb.c
+new file mode 100644
+index 0000000..638cf60
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv04_fb.c
+@@ -0,0 +1,21 @@
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++#include "nouveau_drm.h"
++
++int
++nv04_fb_init(struct drm_device *dev)
++{
++ /* This is what the DDX did for NV_ARCH_04, but a mmio-trace shows
++ * nvidia reading PFB_CFG_0, then writing back its original value.
++ * (which was 0x701114 in this case)
++ */
++
++ nv_wr32(dev, NV04_PFB_CFG0, 0x1114);
++ return 0;
++}
++
++void
++nv04_fb_takedown(struct drm_device *dev)
++{
++}
+diff --git a/drivers/gpu/drm/nouveau/nv04_fbcon.c b/drivers/gpu/drm/nouveau/nv04_fbcon.c
+new file mode 100644
+index 0000000..fd01caa
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv04_fbcon.c
+@@ -0,0 +1,312 @@
++/*
++ * Copyright 2009 Ben Skeggs
++ * Copyright 2008 Stuart Bennett
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
++ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
++ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#include "drmP.h"
++#include "nouveau_drv.h"
++#include "nouveau_dma.h"
++#include "nouveau_fbcon.h"
++
++void
++nv04_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region)
++{
++ struct nouveau_fbcon_par *par = info->par;
++ struct drm_device *dev = par->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *chan = dev_priv->channel;
++
++ if (info->state != FBINFO_STATE_RUNNING)
++ return;
++
++ if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 4)) {
++ nouveau_fbcon_gpu_lockup(info);
++ }
++
++ if (info->flags & FBINFO_HWACCEL_DISABLED) {
++ cfb_copyarea(info, region);
++ return;
++ }
++
++ BEGIN_RING(chan, NvSubImageBlit, 0x0300, 3);
++ OUT_RING(chan, (region->sy << 16) | region->sx);
++ OUT_RING(chan, (region->dy << 16) | region->dx);
++ OUT_RING(chan, (region->height << 16) | region->width);
++ FIRE_RING(chan);
++}
++
++void
++nv04_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
++{
++ struct nouveau_fbcon_par *par = info->par;
++ struct drm_device *dev = par->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *chan = dev_priv->channel;
++
++ if (info->state != FBINFO_STATE_RUNNING)
++ return;
++
++ if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 7)) {
++ nouveau_fbcon_gpu_lockup(info);
++ }
++
++ if (info->flags & FBINFO_HWACCEL_DISABLED) {
++ cfb_fillrect(info, rect);
++ return;
++ }
++
++ BEGIN_RING(chan, NvSubGdiRect, 0x02fc, 1);
++ OUT_RING(chan, (rect->rop != ROP_COPY) ? 1 : 3);
++ BEGIN_RING(chan, NvSubGdiRect, 0x03fc, 1);
++ if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
++ info->fix.visual == FB_VISUAL_DIRECTCOLOR)
++ OUT_RING(chan, ((uint32_t *)info->pseudo_palette)[rect->color]);
++ else
++ OUT_RING(chan, rect->color);
++ BEGIN_RING(chan, NvSubGdiRect, 0x0400, 2);
++ OUT_RING(chan, (rect->dx << 16) | rect->dy);
++ OUT_RING(chan, (rect->width << 16) | rect->height);
++ FIRE_RING(chan);
++}
++
++void
++nv04_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
++{
++ struct nouveau_fbcon_par *par = info->par;
++ struct drm_device *dev = par->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *chan = dev_priv->channel;
++ uint32_t fg;
++ uint32_t bg;
++ uint32_t dsize;
++ uint32_t width;
++ uint32_t *data = (uint32_t *)image->data;
++
++ if (info->state != FBINFO_STATE_RUNNING)
++ return;
++
++ if (image->depth != 1) {
++ cfb_imageblit(info, image);
++ return;
++ }
++
++ if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 8)) {
++ nouveau_fbcon_gpu_lockup(info);
++ }
++
++ if (info->flags & FBINFO_HWACCEL_DISABLED) {
++ cfb_imageblit(info, image);
++ return;
++ }
++
++ width = (image->width + 31) & ~31;
++ dsize = (width * image->height) >> 5;
++
++ if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
++ info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
++ fg = ((uint32_t *) info->pseudo_palette)[image->fg_color];
++ bg = ((uint32_t *) info->pseudo_palette)[image->bg_color];
++ } else {
++ fg = image->fg_color;
++ bg = image->bg_color;
++ }
++
++ BEGIN_RING(chan, NvSubGdiRect, 0x0be4, 7);
++ OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff));
++ OUT_RING(chan, ((image->dy + image->height) << 16) |
++ ((image->dx + image->width) & 0xffff));
++ OUT_RING(chan, bg);
++ OUT_RING(chan, fg);
++ OUT_RING(chan, (image->height << 16) | image->width);
++ OUT_RING(chan, (image->height << 16) | width);
++ OUT_RING(chan, (image->dy << 16) | (image->dx & 0xffff));
++
++ while (dsize) {
++ int iter_len = dsize > 128 ? 128 : dsize;
++
++ if (RING_SPACE(chan, iter_len + 1)) {
++ nouveau_fbcon_gpu_lockup(info);
++ cfb_imageblit(info, image);
++ return;
++ }
++
++ BEGIN_RING(chan, NvSubGdiRect, 0x0c00, iter_len);
++ OUT_RINGp(chan, data, iter_len);
++ data += iter_len;
++ dsize -= iter_len;
++ }
++
++ FIRE_RING(chan);
++}
++
++static int
++nv04_fbcon_grobj_new(struct drm_device *dev, int class, uint32_t handle)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *obj = NULL;
++ int ret;
++
++ ret = nouveau_gpuobj_gr_new(dev_priv->channel, class, &obj);
++ if (ret)
++ return ret;
++
++ ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, handle, obj, NULL);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++int
++nv04_fbcon_accel_init(struct fb_info *info)
++{
++ struct nouveau_fbcon_par *par = info->par;
++ struct drm_device *dev = par->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *chan = dev_priv->channel;
++ const int sub = NvSubCtxSurf2D;
++ int surface_fmt, pattern_fmt, rect_fmt;
++ int ret;
++
++ switch (info->var.bits_per_pixel) {
++ case 8:
++ surface_fmt = 1;
++ pattern_fmt = 3;
++ rect_fmt = 3;
++ break;
++ case 16:
++ surface_fmt = 4;
++ pattern_fmt = 1;
++ rect_fmt = 1;
++ break;
++ case 32:
++ switch (info->var.transp.length) {
++ case 0: /* depth 24 */
++ case 8: /* depth 32 */
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ surface_fmt = 6;
++ pattern_fmt = 3;
++ rect_fmt = 3;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ ret = nv04_fbcon_grobj_new(dev, dev_priv->card_type >= NV_10 ?
++ 0x0062 : 0x0042, NvCtxSurf2D);
++ if (ret)
++ return ret;
++
++ ret = nv04_fbcon_grobj_new(dev, 0x0019, NvClipRect);
++ if (ret)
++ return ret;
++
++ ret = nv04_fbcon_grobj_new(dev, 0x0043, NvRop);
++ if (ret)
++ return ret;
++
++ ret = nv04_fbcon_grobj_new(dev, 0x0044, NvImagePatt);
++ if (ret)
++ return ret;
++
++ ret = nv04_fbcon_grobj_new(dev, 0x004a, NvGdiRect);
++ if (ret)
++ return ret;
++
++ ret = nv04_fbcon_grobj_new(dev, dev_priv->card_type >= NV_10 ?
++ 0x009f : 0x005f, NvImageBlit);
++ if (ret)
++ return ret;
++
++ if (RING_SPACE(chan, 49)) {
++ nouveau_fbcon_gpu_lockup(info);
++ return 0;
++ }
++
++ BEGIN_RING(chan, sub, 0x0000, 1);
++ OUT_RING(chan, NvCtxSurf2D);
++ BEGIN_RING(chan, sub, 0x0184, 2);
++ OUT_RING(chan, NvDmaFB);
++ OUT_RING(chan, NvDmaFB);
++ BEGIN_RING(chan, sub, 0x0300, 4);
++ OUT_RING(chan, surface_fmt);
++ OUT_RING(chan, info->fix.line_length | (info->fix.line_length << 16));
++ OUT_RING(chan, info->fix.smem_start - dev->mode_config.fb_base);
++ OUT_RING(chan, info->fix.smem_start - dev->mode_config.fb_base);
++
++ BEGIN_RING(chan, sub, 0x0000, 1);
++ OUT_RING(chan, NvRop);
++ BEGIN_RING(chan, sub, 0x0300, 1);
++ OUT_RING(chan, 0x55);
++
++ BEGIN_RING(chan, sub, 0x0000, 1);
++ OUT_RING(chan, NvImagePatt);
++ BEGIN_RING(chan, sub, 0x0300, 8);
++ OUT_RING(chan, pattern_fmt);
++#ifdef __BIG_ENDIAN
++ OUT_RING(chan, 2);
++#else
++ OUT_RING(chan, 1);
++#endif
++ OUT_RING(chan, 0);
++ OUT_RING(chan, 1);
++ OUT_RING(chan, ~0);
++ OUT_RING(chan, ~0);
++ OUT_RING(chan, ~0);
++ OUT_RING(chan, ~0);
++
++ BEGIN_RING(chan, sub, 0x0000, 1);
++ OUT_RING(chan, NvClipRect);
++ BEGIN_RING(chan, sub, 0x0300, 2);
++ OUT_RING(chan, 0);
++ OUT_RING(chan, (info->var.yres_virtual << 16) | info->var.xres_virtual);
++
++ BEGIN_RING(chan, NvSubImageBlit, 0x0000, 1);
++ OUT_RING(chan, NvImageBlit);
++ BEGIN_RING(chan, NvSubImageBlit, 0x019c, 1);
++ OUT_RING(chan, NvCtxSurf2D);
++ BEGIN_RING(chan, NvSubImageBlit, 0x02fc, 1);
++ OUT_RING(chan, 3);
++
++ BEGIN_RING(chan, NvSubGdiRect, 0x0000, 1);
++ OUT_RING(chan, NvGdiRect);
++ BEGIN_RING(chan, NvSubGdiRect, 0x0198, 1);
++ OUT_RING(chan, NvCtxSurf2D);
++ BEGIN_RING(chan, NvSubGdiRect, 0x0188, 2);
++ OUT_RING(chan, NvImagePatt);
++ OUT_RING(chan, NvRop);
++ BEGIN_RING(chan, NvSubGdiRect, 0x0304, 1);
++ OUT_RING(chan, 1);
++ BEGIN_RING(chan, NvSubGdiRect, 0x0300, 1);
++ OUT_RING(chan, rect_fmt);
++ BEGIN_RING(chan, NvSubGdiRect, 0x02fc, 1);
++ OUT_RING(chan, 3);
++
++ FIRE_RING(chan);
++
++ return 0;
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nv04_fifo.c b/drivers/gpu/drm/nouveau/nv04_fifo.c
+new file mode 100644
+index 0000000..f31347b
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv04_fifo.c
+@@ -0,0 +1,305 @@
++/*
++ * Copyright (C) 2007 Ben Skeggs.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++
++#define NV04_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV04_RAMFC__SIZE))
++#define NV04_RAMFC__SIZE 32
++#define NV04_RAMFC_DMA_PUT 0x00
++#define NV04_RAMFC_DMA_GET 0x04
++#define NV04_RAMFC_DMA_INSTANCE 0x08
++#define NV04_RAMFC_DMA_STATE 0x0C
++#define NV04_RAMFC_DMA_FETCH 0x10
++#define NV04_RAMFC_ENGINE 0x14
++#define NV04_RAMFC_PULL1_ENGINE 0x18
++
++#define RAMFC_WR(offset, val) nv_wo32(dev, chan->ramfc->gpuobj, \
++ NV04_RAMFC_##offset/4, (val))
++#define RAMFC_RD(offset) nv_ro32(dev, chan->ramfc->gpuobj, \
++ NV04_RAMFC_##offset/4)
++
++void
++nv04_fifo_disable(struct drm_device *dev)
++{
++ uint32_t tmp;
++
++ tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUSH);
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, tmp & ~1);
++ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 0);
++ tmp = nv_rd32(dev, NV03_PFIFO_CACHE1_PULL1);
++ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, tmp & ~1);
++}
++
++void
++nv04_fifo_enable(struct drm_device *dev)
++{
++ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH0, 1);
++ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, 1);
++}
++
++bool
++nv04_fifo_reassign(struct drm_device *dev, bool enable)
++{
++ uint32_t reassign = nv_rd32(dev, NV03_PFIFO_CACHES);
++
++ nv_wr32(dev, NV03_PFIFO_CACHES, enable ? 1 : 0);
++ return (reassign == 1);
++}
++
++bool
++nv04_fifo_cache_flush(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
++ uint64_t start = ptimer->read(dev);
++
++ do {
++ if (nv_rd32(dev, NV03_PFIFO_CACHE1_GET) ==
++ nv_rd32(dev, NV03_PFIFO_CACHE1_PUT))
++ return true;
++
++ } while (ptimer->read(dev) - start < 100000000);
++
++ NV_ERROR(dev, "Timeout flushing the PFIFO cache.\n");
++
++ return false;
++}
++
++bool
++nv04_fifo_cache_pull(struct drm_device *dev, bool enable)
++{
++ uint32_t pull = nv_rd32(dev, NV04_PFIFO_CACHE1_PULL0);
++
++ if (enable) {
++ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, pull | 1);
++ } else {
++ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL0, pull & ~1);
++ nv_wr32(dev, NV04_PFIFO_CACHE1_HASH, 0);
++ }
++
++ return !!(pull & 1);
++}
++
++int
++nv04_fifo_channel_id(struct drm_device *dev)
++{
++ return nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) &
++ NV03_PFIFO_CACHE1_PUSH1_CHID_MASK;
++}
++
++int
++nv04_fifo_create_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int ret;
++
++ ret = nouveau_gpuobj_new_fake(dev, NV04_RAMFC(chan->id), ~0,
++ NV04_RAMFC__SIZE,
++ NVOBJ_FLAG_ZERO_ALLOC |
++ NVOBJ_FLAG_ZERO_FREE,
++ NULL, &chan->ramfc);
++ if (ret)
++ return ret;
++
++ /* Setup initial state */
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ RAMFC_WR(DMA_PUT, chan->pushbuf_base);
++ RAMFC_WR(DMA_GET, chan->pushbuf_base);
++ RAMFC_WR(DMA_INSTANCE, chan->pushbuf->instance >> 4);
++ RAMFC_WR(DMA_FETCH, (NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
++ NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
++ NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 |
++#ifdef __BIG_ENDIAN
++ NV_PFIFO_CACHE1_BIG_ENDIAN |
++#endif
++ 0));
++ dev_priv->engine.instmem.finish_access(dev);
++
++ /* enable the fifo dma operation */
++ nv_wr32(dev, NV04_PFIFO_MODE,
++ nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
++ return 0;
++}
++
++void
++nv04_fifo_destroy_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++
++ nv_wr32(dev, NV04_PFIFO_MODE,
++ nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id));
++
++ nouveau_gpuobj_ref_del(dev, &chan->ramfc);
++}
++
++static void
++nv04_fifo_do_load_context(struct drm_device *dev, int chid)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t fc = NV04_RAMFC(chid), tmp;
++
++ dev_priv->engine.instmem.prepare_access(dev, false);
++
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUT, nv_ri32(dev, fc + 0));
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, nv_ri32(dev, fc + 4));
++ tmp = nv_ri32(dev, fc + 8);
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE, tmp & 0xFFFF);
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT, tmp >> 16);
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, nv_ri32(dev, fc + 12));
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_FETCH, nv_ri32(dev, fc + 16));
++ nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_ri32(dev, fc + 20));
++ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL1, nv_ri32(dev, fc + 24));
++
++ dev_priv->engine.instmem.finish_access(dev);
++
++ nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
++ nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
++}
++
++int
++nv04_fifo_load_context(struct nouveau_channel *chan)
++{
++ uint32_t tmp;
++
++ nv_wr32(chan->dev, NV03_PFIFO_CACHE1_PUSH1,
++ NV03_PFIFO_CACHE1_PUSH1_DMA | chan->id);
++ nv04_fifo_do_load_context(chan->dev, chan->id);
++ nv_wr32(chan->dev, NV04_PFIFO_CACHE1_DMA_PUSH, 1);
++
++ /* Reset NV04_PFIFO_CACHE1_DMA_CTL_AT_INFO to INVALID */
++ tmp = nv_rd32(chan->dev, NV04_PFIFO_CACHE1_DMA_CTL) & ~(1 << 31);
++ nv_wr32(chan->dev, NV04_PFIFO_CACHE1_DMA_CTL, tmp);
++
++ return 0;
++}
++
++int
++nv04_fifo_unload_context(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
++ struct nouveau_channel *chan = NULL;
++ uint32_t tmp;
++ int chid;
++
++ chid = pfifo->channel_id(dev);
++ if (chid < 0 || chid >= dev_priv->engine.fifo.channels)
++ return 0;
++
++ chan = dev_priv->fifos[chid];
++ if (!chan) {
++ NV_ERROR(dev, "Inactive channel on PFIFO: %d\n", chid);
++ return -EINVAL;
++ }
++
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ RAMFC_WR(DMA_PUT, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
++ RAMFC_WR(DMA_GET, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
++ tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT) << 16;
++ tmp |= nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE);
++ RAMFC_WR(DMA_INSTANCE, tmp);
++ RAMFC_WR(DMA_STATE, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_STATE));
++ RAMFC_WR(DMA_FETCH, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_FETCH));
++ RAMFC_WR(ENGINE, nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE));
++ RAMFC_WR(PULL1_ENGINE, nv_rd32(dev, NV04_PFIFO_CACHE1_PULL1));
++ dev_priv->engine.instmem.finish_access(dev);
++
++ nv04_fifo_do_load_context(dev, pfifo->channels - 1);
++ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
++ return 0;
++}
++
++static void
++nv04_fifo_init_reset(struct drm_device *dev)
++{
++ nv_wr32(dev, NV03_PMC_ENABLE,
++ nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PFIFO);
++ nv_wr32(dev, NV03_PMC_ENABLE,
++ nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PFIFO);
++
++ nv_wr32(dev, 0x003224, 0x000f0078);
++ nv_wr32(dev, 0x002044, 0x0101ffff);
++ nv_wr32(dev, 0x002040, 0x000000ff);
++ nv_wr32(dev, 0x002500, 0x00000000);
++ nv_wr32(dev, 0x003000, 0x00000000);
++ nv_wr32(dev, 0x003050, 0x00000000);
++ nv_wr32(dev, 0x003200, 0x00000000);
++ nv_wr32(dev, 0x003250, 0x00000000);
++ nv_wr32(dev, 0x003220, 0x00000000);
++
++ nv_wr32(dev, 0x003250, 0x00000000);
++ nv_wr32(dev, 0x003270, 0x00000000);
++ nv_wr32(dev, 0x003210, 0x00000000);
++}
++
++static void
++nv04_fifo_init_ramxx(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
++ ((dev_priv->ramht_bits - 9) << 16) |
++ (dev_priv->ramht_offset >> 8));
++ nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro_offset>>8);
++ nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc_offset >> 8);
++}
++
++static void
++nv04_fifo_init_intr(struct drm_device *dev)
++{
++ nv_wr32(dev, 0x002100, 0xffffffff);
++ nv_wr32(dev, 0x002140, 0xffffffff);
++}
++
++int
++nv04_fifo_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
++ int i;
++
++ nv04_fifo_init_reset(dev);
++ nv04_fifo_init_ramxx(dev);
++
++ nv04_fifo_do_load_context(dev, pfifo->channels - 1);
++ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
++
++ nv04_fifo_init_intr(dev);
++ pfifo->enable(dev);
++
++ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
++ if (dev_priv->fifos[i]) {
++ uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE);
++ nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i));
++ }
++ }
++
++ return 0;
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nv04_graph.c b/drivers/gpu/drm/nouveau/nv04_graph.c
+new file mode 100644
+index 0000000..e260986
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv04_graph.c
+@@ -0,0 +1,584 @@
++/*
++ * Copyright 2007 Stephane Marchesin
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drm.h"
++#include "nouveau_drv.h"
++
++static uint32_t nv04_graph_ctx_regs[] = {
++ 0x0040053c,
++ 0x00400544,
++ 0x00400540,
++ 0x00400548,
++ NV04_PGRAPH_CTX_SWITCH1,
++ NV04_PGRAPH_CTX_SWITCH2,
++ NV04_PGRAPH_CTX_SWITCH3,
++ NV04_PGRAPH_CTX_SWITCH4,
++ NV04_PGRAPH_CTX_CACHE1,
++ NV04_PGRAPH_CTX_CACHE2,
++ NV04_PGRAPH_CTX_CACHE3,
++ NV04_PGRAPH_CTX_CACHE4,
++ 0x00400184,
++ 0x004001a4,
++ 0x004001c4,
++ 0x004001e4,
++ 0x00400188,
++ 0x004001a8,
++ 0x004001c8,
++ 0x004001e8,
++ 0x0040018c,
++ 0x004001ac,
++ 0x004001cc,
++ 0x004001ec,
++ 0x00400190,
++ 0x004001b0,
++ 0x004001d0,
++ 0x004001f0,
++ 0x00400194,
++ 0x004001b4,
++ 0x004001d4,
++ 0x004001f4,
++ 0x00400198,
++ 0x004001b8,
++ 0x004001d8,
++ 0x004001f8,
++ 0x0040019c,
++ 0x004001bc,
++ 0x004001dc,
++ 0x004001fc,
++ 0x00400174,
++ NV04_PGRAPH_DMA_START_0,
++ NV04_PGRAPH_DMA_START_1,
++ NV04_PGRAPH_DMA_LENGTH,
++ NV04_PGRAPH_DMA_MISC,
++ NV04_PGRAPH_DMA_PITCH,
++ NV04_PGRAPH_BOFFSET0,
++ NV04_PGRAPH_BBASE0,
++ NV04_PGRAPH_BLIMIT0,
++ NV04_PGRAPH_BOFFSET1,
++ NV04_PGRAPH_BBASE1,
++ NV04_PGRAPH_BLIMIT1,
++ NV04_PGRAPH_BOFFSET2,
++ NV04_PGRAPH_BBASE2,
++ NV04_PGRAPH_BLIMIT2,
++ NV04_PGRAPH_BOFFSET3,
++ NV04_PGRAPH_BBASE3,
++ NV04_PGRAPH_BLIMIT3,
++ NV04_PGRAPH_BOFFSET4,
++ NV04_PGRAPH_BBASE4,
++ NV04_PGRAPH_BLIMIT4,
++ NV04_PGRAPH_BOFFSET5,
++ NV04_PGRAPH_BBASE5,
++ NV04_PGRAPH_BLIMIT5,
++ NV04_PGRAPH_BPITCH0,
++ NV04_PGRAPH_BPITCH1,
++ NV04_PGRAPH_BPITCH2,
++ NV04_PGRAPH_BPITCH3,
++ NV04_PGRAPH_BPITCH4,
++ NV04_PGRAPH_SURFACE,
++ NV04_PGRAPH_STATE,
++ NV04_PGRAPH_BSWIZZLE2,
++ NV04_PGRAPH_BSWIZZLE5,
++ NV04_PGRAPH_BPIXEL,
++ NV04_PGRAPH_NOTIFY,
++ NV04_PGRAPH_PATT_COLOR0,
++ NV04_PGRAPH_PATT_COLOR1,
++ NV04_PGRAPH_PATT_COLORRAM+0x00,
++ NV04_PGRAPH_PATT_COLORRAM+0x04,
++ NV04_PGRAPH_PATT_COLORRAM+0x08,
++ NV04_PGRAPH_PATT_COLORRAM+0x0c,
++ NV04_PGRAPH_PATT_COLORRAM+0x10,
++ NV04_PGRAPH_PATT_COLORRAM+0x14,
++ NV04_PGRAPH_PATT_COLORRAM+0x18,
++ NV04_PGRAPH_PATT_COLORRAM+0x1c,
++ NV04_PGRAPH_PATT_COLORRAM+0x20,
++ NV04_PGRAPH_PATT_COLORRAM+0x24,
++ NV04_PGRAPH_PATT_COLORRAM+0x28,
++ NV04_PGRAPH_PATT_COLORRAM+0x2c,
++ NV04_PGRAPH_PATT_COLORRAM+0x30,
++ NV04_PGRAPH_PATT_COLORRAM+0x34,
++ NV04_PGRAPH_PATT_COLORRAM+0x38,
++ NV04_PGRAPH_PATT_COLORRAM+0x3c,
++ NV04_PGRAPH_PATT_COLORRAM+0x40,
++ NV04_PGRAPH_PATT_COLORRAM+0x44,
++ NV04_PGRAPH_PATT_COLORRAM+0x48,
++ NV04_PGRAPH_PATT_COLORRAM+0x4c,
++ NV04_PGRAPH_PATT_COLORRAM+0x50,
++ NV04_PGRAPH_PATT_COLORRAM+0x54,
++ NV04_PGRAPH_PATT_COLORRAM+0x58,
++ NV04_PGRAPH_PATT_COLORRAM+0x5c,
++ NV04_PGRAPH_PATT_COLORRAM+0x60,
++ NV04_PGRAPH_PATT_COLORRAM+0x64,
++ NV04_PGRAPH_PATT_COLORRAM+0x68,
++ NV04_PGRAPH_PATT_COLORRAM+0x6c,
++ NV04_PGRAPH_PATT_COLORRAM+0x70,
++ NV04_PGRAPH_PATT_COLORRAM+0x74,
++ NV04_PGRAPH_PATT_COLORRAM+0x78,
++ NV04_PGRAPH_PATT_COLORRAM+0x7c,
++ NV04_PGRAPH_PATT_COLORRAM+0x80,
++ NV04_PGRAPH_PATT_COLORRAM+0x84,
++ NV04_PGRAPH_PATT_COLORRAM+0x88,
++ NV04_PGRAPH_PATT_COLORRAM+0x8c,
++ NV04_PGRAPH_PATT_COLORRAM+0x90,
++ NV04_PGRAPH_PATT_COLORRAM+0x94,
++ NV04_PGRAPH_PATT_COLORRAM+0x98,
++ NV04_PGRAPH_PATT_COLORRAM+0x9c,
++ NV04_PGRAPH_PATT_COLORRAM+0xa0,
++ NV04_PGRAPH_PATT_COLORRAM+0xa4,
++ NV04_PGRAPH_PATT_COLORRAM+0xa8,
++ NV04_PGRAPH_PATT_COLORRAM+0xac,
++ NV04_PGRAPH_PATT_COLORRAM+0xb0,
++ NV04_PGRAPH_PATT_COLORRAM+0xb4,
++ NV04_PGRAPH_PATT_COLORRAM+0xb8,
++ NV04_PGRAPH_PATT_COLORRAM+0xbc,
++ NV04_PGRAPH_PATT_COLORRAM+0xc0,
++ NV04_PGRAPH_PATT_COLORRAM+0xc4,
++ NV04_PGRAPH_PATT_COLORRAM+0xc8,
++ NV04_PGRAPH_PATT_COLORRAM+0xcc,
++ NV04_PGRAPH_PATT_COLORRAM+0xd0,
++ NV04_PGRAPH_PATT_COLORRAM+0xd4,
++ NV04_PGRAPH_PATT_COLORRAM+0xd8,
++ NV04_PGRAPH_PATT_COLORRAM+0xdc,
++ NV04_PGRAPH_PATT_COLORRAM+0xe0,
++ NV04_PGRAPH_PATT_COLORRAM+0xe4,
++ NV04_PGRAPH_PATT_COLORRAM+0xe8,
++ NV04_PGRAPH_PATT_COLORRAM+0xec,
++ NV04_PGRAPH_PATT_COLORRAM+0xf0,
++ NV04_PGRAPH_PATT_COLORRAM+0xf4,
++ NV04_PGRAPH_PATT_COLORRAM+0xf8,
++ NV04_PGRAPH_PATT_COLORRAM+0xfc,
++ NV04_PGRAPH_PATTERN,
++ 0x0040080c,
++ NV04_PGRAPH_PATTERN_SHAPE,
++ 0x00400600,
++ NV04_PGRAPH_ROP3,
++ NV04_PGRAPH_CHROMA,
++ NV04_PGRAPH_BETA_AND,
++ NV04_PGRAPH_BETA_PREMULT,
++ NV04_PGRAPH_CONTROL0,
++ NV04_PGRAPH_CONTROL1,
++ NV04_PGRAPH_CONTROL2,
++ NV04_PGRAPH_BLEND,
++ NV04_PGRAPH_STORED_FMT,
++ NV04_PGRAPH_SOURCE_COLOR,
++ 0x00400560,
++ 0x00400568,
++ 0x00400564,
++ 0x0040056c,
++ 0x00400400,
++ 0x00400480,
++ 0x00400404,
++ 0x00400484,
++ 0x00400408,
++ 0x00400488,
++ 0x0040040c,
++ 0x0040048c,
++ 0x00400410,
++ 0x00400490,
++ 0x00400414,
++ 0x00400494,
++ 0x00400418,
++ 0x00400498,
++ 0x0040041c,
++ 0x0040049c,
++ 0x00400420,
++ 0x004004a0,
++ 0x00400424,
++ 0x004004a4,
++ 0x00400428,
++ 0x004004a8,
++ 0x0040042c,
++ 0x004004ac,
++ 0x00400430,
++ 0x004004b0,
++ 0x00400434,
++ 0x004004b4,
++ 0x00400438,
++ 0x004004b8,
++ 0x0040043c,
++ 0x004004bc,
++ 0x00400440,
++ 0x004004c0,
++ 0x00400444,
++ 0x004004c4,
++ 0x00400448,
++ 0x004004c8,
++ 0x0040044c,
++ 0x004004cc,
++ 0x00400450,
++ 0x004004d0,
++ 0x00400454,
++ 0x004004d4,
++ 0x00400458,
++ 0x004004d8,
++ 0x0040045c,
++ 0x004004dc,
++ 0x00400460,
++ 0x004004e0,
++ 0x00400464,
++ 0x004004e4,
++ 0x00400468,
++ 0x004004e8,
++ 0x0040046c,
++ 0x004004ec,
++ 0x00400470,
++ 0x004004f0,
++ 0x00400474,
++ 0x004004f4,
++ 0x00400478,
++ 0x004004f8,
++ 0x0040047c,
++ 0x004004fc,
++ 0x00400534,
++ 0x00400538,
++ 0x00400514,
++ 0x00400518,
++ 0x0040051c,
++ 0x00400520,
++ 0x00400524,
++ 0x00400528,
++ 0x0040052c,
++ 0x00400530,
++ 0x00400d00,
++ 0x00400d40,
++ 0x00400d80,
++ 0x00400d04,
++ 0x00400d44,
++ 0x00400d84,
++ 0x00400d08,
++ 0x00400d48,
++ 0x00400d88,
++ 0x00400d0c,
++ 0x00400d4c,
++ 0x00400d8c,
++ 0x00400d10,
++ 0x00400d50,
++ 0x00400d90,
++ 0x00400d14,
++ 0x00400d54,
++ 0x00400d94,
++ 0x00400d18,
++ 0x00400d58,
++ 0x00400d98,
++ 0x00400d1c,
++ 0x00400d5c,
++ 0x00400d9c,
++ 0x00400d20,
++ 0x00400d60,
++ 0x00400da0,
++ 0x00400d24,
++ 0x00400d64,
++ 0x00400da4,
++ 0x00400d28,
++ 0x00400d68,
++ 0x00400da8,
++ 0x00400d2c,
++ 0x00400d6c,
++ 0x00400dac,
++ 0x00400d30,
++ 0x00400d70,
++ 0x00400db0,
++ 0x00400d34,
++ 0x00400d74,
++ 0x00400db4,
++ 0x00400d38,
++ 0x00400d78,
++ 0x00400db8,
++ 0x00400d3c,
++ 0x00400d7c,
++ 0x00400dbc,
++ 0x00400590,
++ 0x00400594,
++ 0x00400598,
++ 0x0040059c,
++ 0x004005a8,
++ 0x004005ac,
++ 0x004005b0,
++ 0x004005b4,
++ 0x004005c0,
++ 0x004005c4,
++ 0x004005c8,
++ 0x004005cc,
++ 0x004005d0,
++ 0x004005d4,
++ 0x004005d8,
++ 0x004005dc,
++ 0x004005e0,
++ NV04_PGRAPH_PASSTHRU_0,
++ NV04_PGRAPH_PASSTHRU_1,
++ NV04_PGRAPH_PASSTHRU_2,
++ NV04_PGRAPH_DVD_COLORFMT,
++ NV04_PGRAPH_SCALED_FORMAT,
++ NV04_PGRAPH_MISC24_0,
++ NV04_PGRAPH_MISC24_1,
++ NV04_PGRAPH_MISC24_2,
++ 0x00400500,
++ 0x00400504,
++ NV04_PGRAPH_VALID1,
++ NV04_PGRAPH_VALID2,
++ NV04_PGRAPH_DEBUG_3
++};
++
++struct graph_state {
++ int nv04[ARRAY_SIZE(nv04_graph_ctx_regs)];
++};
++
++struct nouveau_channel *
++nv04_graph_channel(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int chid = dev_priv->engine.fifo.channels;
++
++ if (nv_rd32(dev, NV04_PGRAPH_CTX_CONTROL) & 0x00010000)
++ chid = nv_rd32(dev, NV04_PGRAPH_CTX_USER) >> 24;
++
++ if (chid >= dev_priv->engine.fifo.channels)
++ return NULL;
++
++ return dev_priv->fifos[chid];
++}
++
++void
++nv04_graph_context_switch(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
++ struct nouveau_channel *chan = NULL;
++ int chid;
++
++ pgraph->fifo_access(dev, false);
++ nouveau_wait_for_idle(dev);
++
++ /* If previous context is valid, we need to save it */
++ pgraph->unload_context(dev);
++
++ /* Load context for next channel */
++ chid = dev_priv->engine.fifo.channel_id(dev);
++ chan = dev_priv->fifos[chid];
++ if (chan)
++ nv04_graph_load_context(chan);
++
++ pgraph->fifo_access(dev, true);
++}
++
++static uint32_t *ctx_reg(struct graph_state *ctx, uint32_t reg)
++{
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(nv04_graph_ctx_regs); i++) {
++ if (nv04_graph_ctx_regs[i] == reg)
++ return &ctx->nv04[i];
++ }
++
++ return NULL;
++}
++
++int nv04_graph_create_context(struct nouveau_channel *chan)
++{
++ struct graph_state *pgraph_ctx;
++ NV_DEBUG(chan->dev, "nv04_graph_context_create %d\n", chan->id);
++
++ chan->pgraph_ctx = pgraph_ctx = kzalloc(sizeof(*pgraph_ctx),
++ GFP_KERNEL);
++ if (pgraph_ctx == NULL)
++ return -ENOMEM;
++
++ *ctx_reg(pgraph_ctx, NV04_PGRAPH_DEBUG_3) = 0xfad4ff31;
++
++ return 0;
++}
++
++void nv04_graph_destroy_context(struct nouveau_channel *chan)
++{
++ struct graph_state *pgraph_ctx = chan->pgraph_ctx;
++
++ kfree(pgraph_ctx);
++ chan->pgraph_ctx = NULL;
++}
++
++int nv04_graph_load_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct graph_state *pgraph_ctx = chan->pgraph_ctx;
++ uint32_t tmp;
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(nv04_graph_ctx_regs); i++)
++ nv_wr32(dev, nv04_graph_ctx_regs[i], pgraph_ctx->nv04[i]);
++
++ nv_wr32(dev, NV04_PGRAPH_CTX_CONTROL, 0x10010100);
++
++ tmp = nv_rd32(dev, NV04_PGRAPH_CTX_USER) & 0x00ffffff;
++ nv_wr32(dev, NV04_PGRAPH_CTX_USER, tmp | chan->id << 24);
++
++ tmp = nv_rd32(dev, NV04_PGRAPH_FFINTFC_ST2);
++ nv_wr32(dev, NV04_PGRAPH_FFINTFC_ST2, tmp & 0x000fffff);
++
++ return 0;
++}
++
++int
++nv04_graph_unload_context(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
++ struct nouveau_channel *chan = NULL;
++ struct graph_state *ctx;
++ uint32_t tmp;
++ int i;
++
++ chan = pgraph->channel(dev);
++ if (!chan)
++ return 0;
++ ctx = chan->pgraph_ctx;
++
++ for (i = 0; i < ARRAY_SIZE(nv04_graph_ctx_regs); i++)
++ ctx->nv04[i] = nv_rd32(dev, nv04_graph_ctx_regs[i]);
++
++ nv_wr32(dev, NV04_PGRAPH_CTX_CONTROL, 0x10000000);
++ tmp = nv_rd32(dev, NV04_PGRAPH_CTX_USER) & 0x00ffffff;
++ tmp |= (dev_priv->engine.fifo.channels - 1) << 24;
++ nv_wr32(dev, NV04_PGRAPH_CTX_USER, tmp);
++ return 0;
++}
++
++int nv04_graph_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t tmp;
++
++ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) &
++ ~NV_PMC_ENABLE_PGRAPH);
++ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
++ NV_PMC_ENABLE_PGRAPH);
++
++ /* Enable PGRAPH interrupts */
++ nv_wr32(dev, NV03_PGRAPH_INTR, 0xFFFFFFFF);
++ nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
++
++ nv_wr32(dev, NV04_PGRAPH_VALID1, 0);
++ nv_wr32(dev, NV04_PGRAPH_VALID2, 0);
++ /*nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x000001FF);
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x001FFFFF);*/
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x1231c000);
++ /*1231C000 blob, 001 haiku*/
++ //*V_WRITE(NV04_PGRAPH_DEBUG_1, 0xf2d91100);*/
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x72111100);
++ /*0x72111100 blob , 01 haiku*/
++ /*nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x11d5f870);*/
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x11d5f071);
++ /*haiku same*/
++
++ /*nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xfad4ff31);*/
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xf0d4ff31);
++ /*haiku and blob 10d4*/
++
++ nv_wr32(dev, NV04_PGRAPH_STATE , 0xFFFFFFFF);
++ nv_wr32(dev, NV04_PGRAPH_CTX_CONTROL , 0x10000100);
++ tmp = nv_rd32(dev, NV04_PGRAPH_CTX_USER) & 0x00ffffff;
++ tmp |= (dev_priv->engine.fifo.channels - 1) << 24;
++ nv_wr32(dev, NV04_PGRAPH_CTX_USER, tmp);
++
++ /* These don't belong here, they're part of a per-channel context */
++ nv_wr32(dev, NV04_PGRAPH_PATTERN_SHAPE, 0x00000000);
++ nv_wr32(dev, NV04_PGRAPH_BETA_AND , 0xFFFFFFFF);
++
++ return 0;
++}
++
++void nv04_graph_takedown(struct drm_device *dev)
++{
++}
++
++void
++nv04_graph_fifo_access(struct drm_device *dev, bool enabled)
++{
++ if (enabled)
++ nv_wr32(dev, NV04_PGRAPH_FIFO,
++ nv_rd32(dev, NV04_PGRAPH_FIFO) | 1);
++ else
++ nv_wr32(dev, NV04_PGRAPH_FIFO,
++ nv_rd32(dev, NV04_PGRAPH_FIFO) & ~1);
++}
++
++static int
++nv04_graph_mthd_set_ref(struct nouveau_channel *chan, int grclass,
++ int mthd, uint32_t data)
++{
++ chan->fence.last_sequence_irq = data;
++ nouveau_fence_handler(chan->dev, chan->id);
++ return 0;
++}
++
++static int
++nv04_graph_mthd_set_operation(struct nouveau_channel *chan, int grclass,
++ int mthd, uint32_t data)
++{
++ struct drm_device *dev = chan->dev;
++ uint32_t instance = (nv_rd32(dev, NV04_PGRAPH_CTX_SWITCH4) & 0xffff) << 4;
++ int subc = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 13) & 0x7;
++ uint32_t tmp;
++
++ tmp = nv_ri32(dev, instance);
++ tmp &= ~0x00038000;
++ tmp |= ((data & 7) << 15);
++
++ nv_wi32(dev, instance, tmp);
++ nv_wr32(dev, NV04_PGRAPH_CTX_SWITCH1, tmp);
++ nv_wr32(dev, NV04_PGRAPH_CTX_CACHE1 + (subc<<2), tmp);
++ return 0;
++}
++
++static struct nouveau_pgraph_object_method nv04_graph_mthds_sw[] = {
++ { 0x0150, nv04_graph_mthd_set_ref },
++ {}
++};
++
++static struct nouveau_pgraph_object_method nv04_graph_mthds_set_operation[] = {
++ { 0x02fc, nv04_graph_mthd_set_operation },
++ {},
++};
++
++struct nouveau_pgraph_object_class nv04_graph_grclass[] = {
++ { 0x0039, false, NULL },
++ { 0x004a, false, nv04_graph_mthds_set_operation }, /* gdirect */
++ { 0x005f, false, nv04_graph_mthds_set_operation }, /* imageblit */
++ { 0x0061, false, nv04_graph_mthds_set_operation }, /* ifc */
++ { 0x0077, false, nv04_graph_mthds_set_operation }, /* sifm */
++ { 0x0030, false, NULL }, /* null */
++ { 0x0042, false, NULL }, /* surf2d */
++ { 0x0043, false, NULL }, /* rop */
++ { 0x0012, false, NULL }, /* beta1 */
++ { 0x0072, false, NULL }, /* beta4 */
++ { 0x0019, false, NULL }, /* cliprect */
++ { 0x0044, false, NULL }, /* pattern */
++ { 0x0052, false, NULL }, /* swzsurf */
++ { 0x0053, false, NULL }, /* surf3d */
++ { 0x0054, false, NULL }, /* tex_tri */
++ { 0x0055, false, NULL }, /* multitex_tri */
++ { 0x506e, true, nv04_graph_mthds_sw },
++ {}
++};
++
+diff --git a/drivers/gpu/drm/nouveau/nv04_instmem.c b/drivers/gpu/drm/nouveau/nv04_instmem.c
+new file mode 100644
+index 0000000..a3b9563
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv04_instmem.c
+@@ -0,0 +1,208 @@
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++
++/* returns the size of fifo context */
++static int
++nouveau_fifo_ctx_size(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ if (dev_priv->chipset >= 0x40)
++ return 128;
++ else
++ if (dev_priv->chipset >= 0x17)
++ return 64;
++
++ return 32;
++}
++
++static void
++nv04_instmem_determine_amount(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int i;
++
++ /* Figure out how much instance memory we need */
++ if (dev_priv->card_type >= NV_40) {
++ /* We'll want more instance memory than this on some NV4x cards.
++ * There's a 16MB aperture to play with that maps onto the end
++ * of vram. For now, only reserve a small piece until we know
++ * more about what each chipset requires.
++ */
++ switch (dev_priv->chipset) {
++ case 0x40:
++ case 0x47:
++ case 0x49:
++ case 0x4b:
++ dev_priv->ramin_rsvd_vram = (2 * 1024 * 1024);
++ break;
++ default:
++ dev_priv->ramin_rsvd_vram = (1 * 1024 * 1024);
++ break;
++ }
++ } else {
++ /*XXX: what *are* the limits on <NV40 cards?
++ */
++ dev_priv->ramin_rsvd_vram = (512 * 1024);
++ }
++ NV_DEBUG(dev, "RAMIN size: %dKiB\n", dev_priv->ramin_rsvd_vram >> 10);
++
++ /* Clear all of it, except the BIOS image that's in the first 64KiB */
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ for (i = 64 * 1024; i < dev_priv->ramin_rsvd_vram; i += 4)
++ nv_wi32(dev, i, 0x00000000);
++ dev_priv->engine.instmem.finish_access(dev);
++}
++
++static void
++nv04_instmem_configure_fixed_tables(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_engine *engine = &dev_priv->engine;
++
++ /* FIFO hash table (RAMHT)
++ * use 4k hash table at RAMIN+0x10000
++ * TODO: extend the hash table
++ */
++ dev_priv->ramht_offset = 0x10000;
++ dev_priv->ramht_bits = 9;
++ dev_priv->ramht_size = (1 << dev_priv->ramht_bits); /* nr entries */
++ dev_priv->ramht_size *= 8; /* 2 32-bit values per entry in RAMHT */
++ NV_DEBUG(dev, "RAMHT offset=0x%x, size=%d\n", dev_priv->ramht_offset,
++ dev_priv->ramht_size);
++
++ /* FIFO runout table (RAMRO) - 512k at 0x11200 */
++ dev_priv->ramro_offset = 0x11200;
++ dev_priv->ramro_size = 512;
++ NV_DEBUG(dev, "RAMRO offset=0x%x, size=%d\n", dev_priv->ramro_offset,
++ dev_priv->ramro_size);
++
++ /* FIFO context table (RAMFC)
++ * NV40 : Not sure exactly how to position RAMFC on some cards,
++ * 0x30002 seems to position it at RAMIN+0x20000 on these
++ * cards. RAMFC is 4kb (32 fifos, 128byte entries).
++ * Others: Position RAMFC at RAMIN+0x11400
++ */
++ dev_priv->ramfc_size = engine->fifo.channels *
++ nouveau_fifo_ctx_size(dev);
++ switch (dev_priv->card_type) {
++ case NV_40:
++ dev_priv->ramfc_offset = 0x20000;
++ break;
++ case NV_30:
++ case NV_20:
++ case NV_10:
++ case NV_04:
++ default:
++ dev_priv->ramfc_offset = 0x11400;
++ break;
++ }
++ NV_DEBUG(dev, "RAMFC offset=0x%x, size=%d\n", dev_priv->ramfc_offset,
++ dev_priv->ramfc_size);
++}
++
++int nv04_instmem_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t offset;
++ int ret = 0;
++
++ nv04_instmem_determine_amount(dev);
++ nv04_instmem_configure_fixed_tables(dev);
++
++ /* Create a heap to manage RAMIN allocations, we don't allocate
++ * the space that was reserved for RAMHT/FC/RO.
++ */
++ offset = dev_priv->ramfc_offset + dev_priv->ramfc_size;
++
++ /* It appears RAMRO (or something?) is controlled by 0x2220/0x2230
++ * on certain NV4x chipsets as well as RAMFC. When 0x2230 == 0
++ * ("new style" control) the upper 16-bits of 0x2220 points at this
++ * other mysterious table that's clobbering important things.
++ *
++ * We're now pointing this at RAMIN+0x30000 to avoid RAMFC getting
++ * smashed to pieces on us, so reserve 0x30000-0x40000 too..
++ */
++ if (dev_priv->card_type >= NV_40) {
++ if (offset < 0x40000)
++ offset = 0x40000;
++ }
++
++ ret = nouveau_mem_init_heap(&dev_priv->ramin_heap,
++ offset, dev_priv->ramin_rsvd_vram - offset);
++ if (ret) {
++ dev_priv->ramin_heap = NULL;
++ NV_ERROR(dev, "Failed to init RAMIN heap\n");
++ }
++
++ return ret;
++}
++
++void
++nv04_instmem_takedown(struct drm_device *dev)
++{
++}
++
++int
++nv04_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj, uint32_t *sz)
++{
++ if (gpuobj->im_backing)
++ return -EINVAL;
++
++ return 0;
++}
++
++void
++nv04_instmem_clear(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ if (gpuobj && gpuobj->im_backing) {
++ if (gpuobj->im_bound)
++ dev_priv->engine.instmem.unbind(dev, gpuobj);
++ gpuobj->im_backing = NULL;
++ }
++}
++
++int
++nv04_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
++{
++ if (!gpuobj->im_pramin || gpuobj->im_bound)
++ return -EINVAL;
++
++ gpuobj->im_bound = 1;
++ return 0;
++}
++
++int
++nv04_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
++{
++ if (gpuobj->im_bound == 0)
++ return -EINVAL;
++
++ gpuobj->im_bound = 0;
++ return 0;
++}
++
++void
++nv04_instmem_prepare_access(struct drm_device *dev, bool write)
++{
++}
++
++void
++nv04_instmem_finish_access(struct drm_device *dev)
++{
++}
++
++int
++nv04_instmem_suspend(struct drm_device *dev)
++{
++ return 0;
++}
++
++void
++nv04_instmem_resume(struct drm_device *dev)
++{
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nv04_mc.c b/drivers/gpu/drm/nouveau/nv04_mc.c
+new file mode 100644
+index 0000000..617ed1e
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv04_mc.c
+@@ -0,0 +1,20 @@
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++#include "nouveau_drm.h"
++
++int
++nv04_mc_init(struct drm_device *dev)
++{
++ /* Power up everything, resetting each individual unit will
++ * be done later if needed.
++ */
++
++ nv_wr32(dev, NV03_PMC_ENABLE, 0xFFFFFFFF);
++ return 0;
++}
++
++void
++nv04_mc_takedown(struct drm_device *dev)
++{
++}
+diff --git a/drivers/gpu/drm/nouveau/nv04_timer.c b/drivers/gpu/drm/nouveau/nv04_timer.c
+new file mode 100644
+index 0000000..1d09ddd
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv04_timer.c
+@@ -0,0 +1,51 @@
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++#include "nouveau_drm.h"
++
++int
++nv04_timer_init(struct drm_device *dev)
++{
++ nv_wr32(dev, NV04_PTIMER_INTR_EN_0, 0x00000000);
++ nv_wr32(dev, NV04_PTIMER_INTR_0, 0xFFFFFFFF);
++
++ /* Just use the pre-existing values when possible for now; these regs
++ * are not written in nv (driver writer missed a /4 on the address), and
++ * writing 8 and 3 to the correct regs breaks the timings on the LVDS
++ * hardware sequencing microcode.
++ * A correct solution (involving calculations with the GPU PLL) can
++ * be done when kernel modesetting lands
++ */
++ if (!nv_rd32(dev, NV04_PTIMER_NUMERATOR) ||
++ !nv_rd32(dev, NV04_PTIMER_DENOMINATOR)) {
++ nv_wr32(dev, NV04_PTIMER_NUMERATOR, 0x00000008);
++ nv_wr32(dev, NV04_PTIMER_DENOMINATOR, 0x00000003);
++ }
++
++ return 0;
++}
++
++uint64_t
++nv04_timer_read(struct drm_device *dev)
++{
++ uint32_t low;
++ /* From kmmio dumps on nv28 this looks like how the blob does this.
++ * It reads the high dword twice, before and after.
++ * The only explanation seems to be that the 64-bit timer counter
++ * advances between high and low dword reads and may corrupt the
++ * result. Not confirmed.
++ */
++ uint32_t high2 = nv_rd32(dev, NV04_PTIMER_TIME_1);
++ uint32_t high1;
++ do {
++ high1 = high2;
++ low = nv_rd32(dev, NV04_PTIMER_TIME_0);
++ high2 = nv_rd32(dev, NV04_PTIMER_TIME_1);
++ } while (high1 != high2);
++ return (((uint64_t)high2) << 32) | (uint64_t)low;
++}
++
++void
++nv04_timer_takedown(struct drm_device *dev)
++{
++}
+diff --git a/drivers/gpu/drm/nouveau/nv04_tv.c b/drivers/gpu/drm/nouveau/nv04_tv.c
+new file mode 100644
+index 0000000..9c63099
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv04_tv.c
+@@ -0,0 +1,305 @@
++/*
++ * Copyright (C) 2009 Francisco Jerez.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "nouveau_drv.h"
++#include "nouveau_encoder.h"
++#include "nouveau_connector.h"
++#include "nouveau_crtc.h"
++#include "nouveau_hw.h"
++#include "drm_crtc_helper.h"
++
++#include "i2c/ch7006.h"
++
++static struct {
++ struct i2c_board_info board_info;
++ struct drm_encoder_funcs funcs;
++ struct drm_encoder_helper_funcs hfuncs;
++ void *params;
++
++} nv04_tv_encoder_info[] = {
++ {
++ .board_info = { I2C_BOARD_INFO("ch7006", 0x75) },
++ .params = &(struct ch7006_encoder_params) {
++ CH7006_FORMAT_RGB24m12I, CH7006_CLOCK_MASTER,
++ 0, 0, 0,
++ CH7006_SYNC_SLAVE, CH7006_SYNC_SEPARATED,
++ CH7006_POUT_3_3V, CH7006_ACTIVE_HSYNC
++ },
++ },
++};
++
++static bool probe_i2c_addr(struct i2c_adapter *adapter, int addr)
++{
++ struct i2c_msg msg = {
++ .addr = addr,
++ .len = 0,
++ };
++
++ return i2c_transfer(adapter, &msg, 1) == 1;
++}
++
++int nv04_tv_identify(struct drm_device *dev, int i2c_index)
++{
++ struct nouveau_i2c_chan *i2c;
++ bool was_locked;
++ int i, ret;
++
++ NV_TRACE(dev, "Probing TV encoders on I2C bus: %d\n", i2c_index);
++
++ i2c = nouveau_i2c_find(dev, i2c_index);
++ if (!i2c)
++ return -ENODEV;
++
++ was_locked = NVLockVgaCrtcs(dev, false);
++
++ for (i = 0; i < ARRAY_SIZE(nv04_tv_encoder_info); i++) {
++ if (probe_i2c_addr(&i2c->adapter,
++ nv04_tv_encoder_info[i].board_info.addr)) {
++ ret = i;
++ break;
++ }
++ }
++
++ if (i < ARRAY_SIZE(nv04_tv_encoder_info)) {
++ NV_TRACE(dev, "Detected TV encoder: %s\n",
++ nv04_tv_encoder_info[i].board_info.type);
++
++ } else {
++ NV_TRACE(dev, "No TV encoders found.\n");
++ i = -ENODEV;
++ }
++
++ NVLockVgaCrtcs(dev, was_locked);
++ return i;
++}
++
++#define PLLSEL_TV_CRTC1_MASK \
++ (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 \
++ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1)
++#define PLLSEL_TV_CRTC2_MASK \
++ (NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 \
++ | NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2)
++
++static void nv04_tv_dpms(struct drm_encoder *encoder, int mode)
++{
++ struct drm_device *dev = encoder->dev;
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv04_mode_state *state = &dev_priv->mode_reg;
++ uint8_t crtc1A;
++
++ NV_INFO(dev, "Setting dpms mode %d on TV encoder (output %d)\n",
++ mode, nv_encoder->dcb->index);
++
++ state->pllsel &= ~(PLLSEL_TV_CRTC1_MASK | PLLSEL_TV_CRTC2_MASK);
++
++ if (mode == DRM_MODE_DPMS_ON) {
++ int head = nouveau_crtc(encoder->crtc)->index;
++ crtc1A = NVReadVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX);
++
++ state->pllsel |= head ? PLLSEL_TV_CRTC2_MASK :
++ PLLSEL_TV_CRTC1_MASK;
++
++ /* Inhibit hsync */
++ crtc1A |= 0x80;
++
++ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_RPC1_INDEX, crtc1A);
++ }
++
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_PLL_COEFF_SELECT, state->pllsel);
++
++ to_encoder_slave(encoder)->slave_funcs->dpms(encoder, mode);
++}
++
++static void nv04_tv_bind(struct drm_device *dev, int head, bool bind)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv04_crtc_reg *state = &dev_priv->mode_reg.crtc_reg[head];
++
++ state->tv_setup = 0;
++
++ if (bind) {
++ state->CRTC[NV_CIO_CRE_LCD__INDEX] = 0;
++ state->CRTC[NV_CIO_CRE_49] |= 0x10;
++ } else {
++ state->CRTC[NV_CIO_CRE_49] &= ~0x10;
++ }
++
++ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_LCD__INDEX,
++ state->CRTC[NV_CIO_CRE_LCD__INDEX]);
++ NVWriteVgaCrtc(dev, head, NV_CIO_CRE_49,
++ state->CRTC[NV_CIO_CRE_49]);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_TV_SETUP,
++ state->tv_setup);
++}
++
++static void nv04_tv_prepare(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++ int head = nouveau_crtc(encoder->crtc)->index;
++ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
++
++ helper->dpms(encoder, DRM_MODE_DPMS_OFF);
++
++ nv04_dfp_disable(dev, head);
++
++ if (nv_two_heads(dev))
++ nv04_tv_bind(dev, head ^ 1, false);
++
++ nv04_tv_bind(dev, head, true);
++}
++
++static void nv04_tv_mode_set(struct drm_encoder *encoder,
++ struct drm_display_mode *mode,
++ struct drm_display_mode *adjusted_mode)
++{
++ struct drm_device *dev = encoder->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
++ struct nv04_crtc_reg *regp = &dev_priv->mode_reg.crtc_reg[nv_crtc->index];
++
++ regp->tv_htotal = adjusted_mode->htotal;
++ regp->tv_vtotal = adjusted_mode->vtotal;
++
++ /* These delay the TV signals with respect to the VGA port,
++ * they might be useful if we ever allow a CRTC to drive
++ * multiple outputs.
++ */
++ regp->tv_hskew = 1;
++ regp->tv_hsync_delay = 1;
++ regp->tv_hsync_delay2 = 64;
++ regp->tv_vskew = 1;
++ regp->tv_vsync_delay = 1;
++
++ to_encoder_slave(encoder)->slave_funcs->mode_set(encoder, mode, adjusted_mode);
++}
++
++static void nv04_tv_commit(struct drm_encoder *encoder)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_device *dev = encoder->dev;
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
++ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
++
++ helper->dpms(encoder, DRM_MODE_DPMS_ON);
++
++ NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
++ drm_get_connector_name(&nouveau_encoder_connector_get(nv_encoder)->base), nv_crtc->index,
++ '@' + ffs(nv_encoder->dcb->or));
++}
++
++static void nv04_tv_destroy(struct drm_encoder *encoder)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++
++ to_encoder_slave(encoder)->slave_funcs->destroy(encoder);
++
++ drm_encoder_cleanup(encoder);
++
++ kfree(nv_encoder);
++}
++
++int nv04_tv_create(struct drm_device *dev, struct dcb_entry *entry)
++{
++ struct nouveau_encoder *nv_encoder;
++ struct drm_encoder *encoder;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct i2c_adapter *adap;
++ struct drm_encoder_funcs *funcs = NULL;
++ struct drm_encoder_helper_funcs *hfuncs = NULL;
++ struct drm_encoder_slave_funcs *sfuncs = NULL;
++ int i2c_index = entry->i2c_index;
++ int type, ret;
++ bool was_locked;
++
++ /* Ensure that we can talk to this encoder */
++ type = nv04_tv_identify(dev, i2c_index);
++ if (type < 0)
++ return type;
++
++ /* Allocate the necessary memory */
++ nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
++ if (!nv_encoder)
++ return -ENOMEM;
++
++ /* Initialize the common members */
++ encoder = to_drm_encoder(nv_encoder);
++
++ funcs = &nv04_tv_encoder_info[type].funcs;
++ hfuncs = &nv04_tv_encoder_info[type].hfuncs;
++
++ drm_encoder_init(dev, encoder, funcs, DRM_MODE_ENCODER_TVDAC);
++ drm_encoder_helper_add(encoder, hfuncs);
++
++ encoder->possible_crtcs = entry->heads;
++ encoder->possible_clones = 0;
++
++ nv_encoder->dcb = entry;
++ nv_encoder->or = ffs(entry->or) - 1;
++
++ /* Run the slave-specific initialization */
++ adap = &dev_priv->vbios->dcb->i2c[i2c_index].chan->adapter;
++
++ was_locked = NVLockVgaCrtcs(dev, false);
++
++ ret = drm_i2c_encoder_init(encoder->dev, to_encoder_slave(encoder), adap,
++ &nv04_tv_encoder_info[type].board_info);
++
++ NVLockVgaCrtcs(dev, was_locked);
++
++ if (ret < 0)
++ goto fail;
++
++ /* Fill the function pointers */
++ sfuncs = to_encoder_slave(encoder)->slave_funcs;
++
++ *funcs = (struct drm_encoder_funcs) {
++ .destroy = nv04_tv_destroy,
++ };
++
++ *hfuncs = (struct drm_encoder_helper_funcs) {
++ .dpms = nv04_tv_dpms,
++ .save = sfuncs->save,
++ .restore = sfuncs->restore,
++ .mode_fixup = sfuncs->mode_fixup,
++ .prepare = nv04_tv_prepare,
++ .commit = nv04_tv_commit,
++ .mode_set = nv04_tv_mode_set,
++ .detect = sfuncs->detect,
++ };
++
++ /* Set the slave encoder configuration */
++ sfuncs->set_config(encoder, nv04_tv_encoder_info[type].params);
++
++ return 0;
++
++fail:
++ drm_encoder_cleanup(encoder);
++
++ kfree(nv_encoder);
++ return ret;
++}
+diff --git a/drivers/gpu/drm/nouveau/nv10_fb.c b/drivers/gpu/drm/nouveau/nv10_fb.c
+new file mode 100644
+index 0000000..cc5cda4
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv10_fb.c
+@@ -0,0 +1,44 @@
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++#include "nouveau_drm.h"
++
++void
++nv10_fb_set_region_tiling(struct drm_device *dev, int i, uint32_t addr,
++ uint32_t size, uint32_t pitch)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t limit = max(1u, addr + size) - 1;
++
++ if (pitch) {
++ if (dev_priv->card_type >= NV_20)
++ addr |= 1;
++ else
++ addr |= 1 << 31;
++ }
++
++ nv_wr32(dev, NV10_PFB_TLIMIT(i), limit);
++ nv_wr32(dev, NV10_PFB_TSIZE(i), pitch);
++ nv_wr32(dev, NV10_PFB_TILE(i), addr);
++}
++
++int
++nv10_fb_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
++ int i;
++
++ pfb->num_tiles = NV10_PFB_TILE__SIZE;
++
++ /* Turn all the tiling regions off. */
++ for (i = 0; i < pfb->num_tiles; i++)
++ pfb->set_region_tiling(dev, i, 0, 0, 0);
++
++ return 0;
++}
++
++void
++nv10_fb_takedown(struct drm_device *dev)
++{
++}
+diff --git a/drivers/gpu/drm/nouveau/nv10_fifo.c b/drivers/gpu/drm/nouveau/nv10_fifo.c
+new file mode 100644
+index 0000000..7aeabf2
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv10_fifo.c
+@@ -0,0 +1,260 @@
++/*
++ * Copyright (C) 2007 Ben Skeggs.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++
++#define NV10_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV10_RAMFC__SIZE))
++#define NV10_RAMFC__SIZE ((dev_priv->chipset) >= 0x17 ? 64 : 32)
++
++int
++nv10_fifo_channel_id(struct drm_device *dev)
++{
++ return nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) &
++ NV10_PFIFO_CACHE1_PUSH1_CHID_MASK;
++}
++
++int
++nv10_fifo_create_context(struct nouveau_channel *chan)
++{
++ struct drm_nouveau_private *dev_priv = chan->dev->dev_private;
++ struct drm_device *dev = chan->dev;
++ uint32_t fc = NV10_RAMFC(chan->id);
++ int ret;
++
++ ret = nouveau_gpuobj_new_fake(dev, NV10_RAMFC(chan->id), ~0,
++ NV10_RAMFC__SIZE, NVOBJ_FLAG_ZERO_ALLOC |
++ NVOBJ_FLAG_ZERO_FREE, NULL, &chan->ramfc);
++ if (ret)
++ return ret;
++
++ /* Fill entries that are seen filled in dumps of nvidia driver just
++ * after channel's is put into DMA mode
++ */
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ nv_wi32(dev, fc + 0, chan->pushbuf_base);
++ nv_wi32(dev, fc + 4, chan->pushbuf_base);
++ nv_wi32(dev, fc + 12, chan->pushbuf->instance >> 4);
++ nv_wi32(dev, fc + 20, NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
++ NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
++ NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 |
++#ifdef __BIG_ENDIAN
++ NV_PFIFO_CACHE1_BIG_ENDIAN |
++#endif
++ 0);
++ dev_priv->engine.instmem.finish_access(dev);
++
++ /* enable the fifo dma operation */
++ nv_wr32(dev, NV04_PFIFO_MODE,
++ nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
++ return 0;
++}
++
++void
++nv10_fifo_destroy_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++
++ nv_wr32(dev, NV04_PFIFO_MODE,
++ nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id));
++
++ nouveau_gpuobj_ref_del(dev, &chan->ramfc);
++}
++
++static void
++nv10_fifo_do_load_context(struct drm_device *dev, int chid)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t fc = NV10_RAMFC(chid), tmp;
++
++ dev_priv->engine.instmem.prepare_access(dev, false);
++
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUT, nv_ri32(dev, fc + 0));
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, nv_ri32(dev, fc + 4));
++ nv_wr32(dev, NV10_PFIFO_CACHE1_REF_CNT, nv_ri32(dev, fc + 8));
++
++ tmp = nv_ri32(dev, fc + 12);
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE, tmp & 0xFFFF);
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT, tmp >> 16);
++
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, nv_ri32(dev, fc + 16));
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_FETCH, nv_ri32(dev, fc + 20));
++ nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_ri32(dev, fc + 24));
++ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL1, nv_ri32(dev, fc + 28));
++
++ if (dev_priv->chipset < 0x17)
++ goto out;
++
++ nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE, nv_ri32(dev, fc + 32));
++ tmp = nv_ri32(dev, fc + 36);
++ nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP, tmp);
++ nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT, nv_ri32(dev, fc + 40));
++ nv_wr32(dev, NV10_PFIFO_CACHE1_SEMAPHORE, nv_ri32(dev, fc + 44));
++ nv_wr32(dev, NV10_PFIFO_CACHE1_DMA_SUBROUTINE, nv_ri32(dev, fc + 48));
++
++out:
++ dev_priv->engine.instmem.finish_access(dev);
++
++ nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
++ nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
++}
++
++int
++nv10_fifo_load_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ uint32_t tmp;
++
++ nv10_fifo_do_load_context(dev, chan->id);
++
++ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1,
++ NV03_PFIFO_CACHE1_PUSH1_DMA | chan->id);
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 1);
++
++ /* Reset NV04_PFIFO_CACHE1_DMA_CTL_AT_INFO to INVALID */
++ tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_CTL) & ~(1 << 31);
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_CTL, tmp);
++
++ return 0;
++}
++
++int
++nv10_fifo_unload_context(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
++ uint32_t fc, tmp;
++ int chid;
++
++ chid = pfifo->channel_id(dev);
++ if (chid < 0 || chid >= dev_priv->engine.fifo.channels)
++ return 0;
++ fc = NV10_RAMFC(chid);
++
++ dev_priv->engine.instmem.prepare_access(dev, true);
++
++ nv_wi32(dev, fc + 0, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
++ nv_wi32(dev, fc + 4, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
++ nv_wi32(dev, fc + 8, nv_rd32(dev, NV10_PFIFO_CACHE1_REF_CNT));
++ tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE) & 0xFFFF;
++ tmp |= (nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT) << 16);
++ nv_wi32(dev, fc + 12, tmp);
++ nv_wi32(dev, fc + 16, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_STATE));
++ nv_wi32(dev, fc + 20, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_FETCH));
++ nv_wi32(dev, fc + 24, nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE));
++ nv_wi32(dev, fc + 28, nv_rd32(dev, NV04_PFIFO_CACHE1_PULL1));
++
++ if (dev_priv->chipset < 0x17)
++ goto out;
++
++ nv_wi32(dev, fc + 32, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE));
++ tmp = nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP);
++ nv_wi32(dev, fc + 36, tmp);
++ nv_wi32(dev, fc + 40, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT));
++ nv_wi32(dev, fc + 44, nv_rd32(dev, NV10_PFIFO_CACHE1_SEMAPHORE));
++ nv_wi32(dev, fc + 48, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
++
++out:
++ dev_priv->engine.instmem.finish_access(dev);
++
++ nv10_fifo_do_load_context(dev, pfifo->channels - 1);
++ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
++ return 0;
++}
++
++static void
++nv10_fifo_init_reset(struct drm_device *dev)
++{
++ nv_wr32(dev, NV03_PMC_ENABLE,
++ nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PFIFO);
++ nv_wr32(dev, NV03_PMC_ENABLE,
++ nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PFIFO);
++
++ nv_wr32(dev, 0x003224, 0x000f0078);
++ nv_wr32(dev, 0x002044, 0x0101ffff);
++ nv_wr32(dev, 0x002040, 0x000000ff);
++ nv_wr32(dev, 0x002500, 0x00000000);
++ nv_wr32(dev, 0x003000, 0x00000000);
++ nv_wr32(dev, 0x003050, 0x00000000);
++
++ nv_wr32(dev, 0x003258, 0x00000000);
++ nv_wr32(dev, 0x003210, 0x00000000);
++ nv_wr32(dev, 0x003270, 0x00000000);
++}
++
++static void
++nv10_fifo_init_ramxx(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
++ ((dev_priv->ramht_bits - 9) << 16) |
++ (dev_priv->ramht_offset >> 8));
++ nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro_offset>>8);
++
++ if (dev_priv->chipset < 0x17) {
++ nv_wr32(dev, NV03_PFIFO_RAMFC, dev_priv->ramfc_offset >> 8);
++ } else {
++ nv_wr32(dev, NV03_PFIFO_RAMFC, (dev_priv->ramfc_offset >> 8) |
++ (1 << 16) /* 64 Bytes entry*/);
++ /* XXX nvidia blob set bit 18, 21,23 for nv20 & nv30 */
++ }
++}
++
++static void
++nv10_fifo_init_intr(struct drm_device *dev)
++{
++ nv_wr32(dev, 0x002100, 0xffffffff);
++ nv_wr32(dev, 0x002140, 0xffffffff);
++}
++
++int
++nv10_fifo_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
++ int i;
++
++ nv10_fifo_init_reset(dev);
++ nv10_fifo_init_ramxx(dev);
++
++ nv10_fifo_do_load_context(dev, pfifo->channels - 1);
++ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
++
++ nv10_fifo_init_intr(dev);
++ pfifo->enable(dev);
++ pfifo->reassign(dev, true);
++
++ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
++ if (dev_priv->fifos[i]) {
++ uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE);
++ nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i));
++ }
++ }
++
++ return 0;
++}
+diff --git a/drivers/gpu/drm/nouveau/nv10_graph.c b/drivers/gpu/drm/nouveau/nv10_graph.c
+new file mode 100644
+index 0000000..fcf2cdd
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv10_graph.c
+@@ -0,0 +1,1009 @@
++/*
++ * Copyright 2007 Matthieu CASTET <castet.matthieu@free.fr>
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
++ * DEALINGS IN THE SOFTWARE.
++ */
++
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drm.h"
++#include "nouveau_drv.h"
++
++#define NV10_FIFO_NUMBER 32
++
++struct pipe_state {
++ uint32_t pipe_0x0000[0x040/4];
++ uint32_t pipe_0x0040[0x010/4];
++ uint32_t pipe_0x0200[0x0c0/4];
++ uint32_t pipe_0x4400[0x080/4];
++ uint32_t pipe_0x6400[0x3b0/4];
++ uint32_t pipe_0x6800[0x2f0/4];
++ uint32_t pipe_0x6c00[0x030/4];
++ uint32_t pipe_0x7000[0x130/4];
++ uint32_t pipe_0x7400[0x0c0/4];
++ uint32_t pipe_0x7800[0x0c0/4];
++};
++
++static int nv10_graph_ctx_regs[] = {
++ NV10_PGRAPH_CTX_SWITCH1,
++ NV10_PGRAPH_CTX_SWITCH2,
++ NV10_PGRAPH_CTX_SWITCH3,
++ NV10_PGRAPH_CTX_SWITCH4,
++ NV10_PGRAPH_CTX_SWITCH5,
++ NV10_PGRAPH_CTX_CACHE1, /* 8 values from 0x400160 to 0x40017c */
++ NV10_PGRAPH_CTX_CACHE2, /* 8 values from 0x400180 to 0x40019c */
++ NV10_PGRAPH_CTX_CACHE3, /* 8 values from 0x4001a0 to 0x4001bc */
++ NV10_PGRAPH_CTX_CACHE4, /* 8 values from 0x4001c0 to 0x4001dc */
++ NV10_PGRAPH_CTX_CACHE5, /* 8 values from 0x4001e0 to 0x4001fc */
++ 0x00400164,
++ 0x00400184,
++ 0x004001a4,
++ 0x004001c4,
++ 0x004001e4,
++ 0x00400168,
++ 0x00400188,
++ 0x004001a8,
++ 0x004001c8,
++ 0x004001e8,
++ 0x0040016c,
++ 0x0040018c,
++ 0x004001ac,
++ 0x004001cc,
++ 0x004001ec,
++ 0x00400170,
++ 0x00400190,
++ 0x004001b0,
++ 0x004001d0,
++ 0x004001f0,
++ 0x00400174,
++ 0x00400194,
++ 0x004001b4,
++ 0x004001d4,
++ 0x004001f4,
++ 0x00400178,
++ 0x00400198,
++ 0x004001b8,
++ 0x004001d8,
++ 0x004001f8,
++ 0x0040017c,
++ 0x0040019c,
++ 0x004001bc,
++ 0x004001dc,
++ 0x004001fc,
++ NV10_PGRAPH_CTX_USER,
++ NV04_PGRAPH_DMA_START_0,
++ NV04_PGRAPH_DMA_START_1,
++ NV04_PGRAPH_DMA_LENGTH,
++ NV04_PGRAPH_DMA_MISC,
++ NV10_PGRAPH_DMA_PITCH,
++ NV04_PGRAPH_BOFFSET0,
++ NV04_PGRAPH_BBASE0,
++ NV04_PGRAPH_BLIMIT0,
++ NV04_PGRAPH_BOFFSET1,
++ NV04_PGRAPH_BBASE1,
++ NV04_PGRAPH_BLIMIT1,
++ NV04_PGRAPH_BOFFSET2,
++ NV04_PGRAPH_BBASE2,
++ NV04_PGRAPH_BLIMIT2,
++ NV04_PGRAPH_BOFFSET3,
++ NV04_PGRAPH_BBASE3,
++ NV04_PGRAPH_BLIMIT3,
++ NV04_PGRAPH_BOFFSET4,
++ NV04_PGRAPH_BBASE4,
++ NV04_PGRAPH_BLIMIT4,
++ NV04_PGRAPH_BOFFSET5,
++ NV04_PGRAPH_BBASE5,
++ NV04_PGRAPH_BLIMIT5,
++ NV04_PGRAPH_BPITCH0,
++ NV04_PGRAPH_BPITCH1,
++ NV04_PGRAPH_BPITCH2,
++ NV04_PGRAPH_BPITCH3,
++ NV04_PGRAPH_BPITCH4,
++ NV10_PGRAPH_SURFACE,
++ NV10_PGRAPH_STATE,
++ NV04_PGRAPH_BSWIZZLE2,
++ NV04_PGRAPH_BSWIZZLE5,
++ NV04_PGRAPH_BPIXEL,
++ NV10_PGRAPH_NOTIFY,
++ NV04_PGRAPH_PATT_COLOR0,
++ NV04_PGRAPH_PATT_COLOR1,
++ NV04_PGRAPH_PATT_COLORRAM, /* 64 values from 0x400900 to 0x4009fc */
++ 0x00400904,
++ 0x00400908,
++ 0x0040090c,
++ 0x00400910,
++ 0x00400914,
++ 0x00400918,
++ 0x0040091c,
++ 0x00400920,
++ 0x00400924,
++ 0x00400928,
++ 0x0040092c,
++ 0x00400930,
++ 0x00400934,
++ 0x00400938,
++ 0x0040093c,
++ 0x00400940,
++ 0x00400944,
++ 0x00400948,
++ 0x0040094c,
++ 0x00400950,
++ 0x00400954,
++ 0x00400958,
++ 0x0040095c,
++ 0x00400960,
++ 0x00400964,
++ 0x00400968,
++ 0x0040096c,
++ 0x00400970,
++ 0x00400974,
++ 0x00400978,
++ 0x0040097c,
++ 0x00400980,
++ 0x00400984,
++ 0x00400988,
++ 0x0040098c,
++ 0x00400990,
++ 0x00400994,
++ 0x00400998,
++ 0x0040099c,
++ 0x004009a0,
++ 0x004009a4,
++ 0x004009a8,
++ 0x004009ac,
++ 0x004009b0,
++ 0x004009b4,
++ 0x004009b8,
++ 0x004009bc,
++ 0x004009c0,
++ 0x004009c4,
++ 0x004009c8,
++ 0x004009cc,
++ 0x004009d0,
++ 0x004009d4,
++ 0x004009d8,
++ 0x004009dc,
++ 0x004009e0,
++ 0x004009e4,
++ 0x004009e8,
++ 0x004009ec,
++ 0x004009f0,
++ 0x004009f4,
++ 0x004009f8,
++ 0x004009fc,
++ NV04_PGRAPH_PATTERN, /* 2 values from 0x400808 to 0x40080c */
++ 0x0040080c,
++ NV04_PGRAPH_PATTERN_SHAPE,
++ NV03_PGRAPH_MONO_COLOR0,
++ NV04_PGRAPH_ROP3,
++ NV04_PGRAPH_CHROMA,
++ NV04_PGRAPH_BETA_AND,
++ NV04_PGRAPH_BETA_PREMULT,
++ 0x00400e70,
++ 0x00400e74,
++ 0x00400e78,
++ 0x00400e7c,
++ 0x00400e80,
++ 0x00400e84,
++ 0x00400e88,
++ 0x00400e8c,
++ 0x00400ea0,
++ 0x00400ea4,
++ 0x00400ea8,
++ 0x00400e90,
++ 0x00400e94,
++ 0x00400e98,
++ 0x00400e9c,
++ NV10_PGRAPH_WINDOWCLIP_HORIZONTAL, /* 8 values from 0x400f00-0x400f1c */
++ NV10_PGRAPH_WINDOWCLIP_VERTICAL, /* 8 values from 0x400f20-0x400f3c */
++ 0x00400f04,
++ 0x00400f24,
++ 0x00400f08,
++ 0x00400f28,
++ 0x00400f0c,
++ 0x00400f2c,
++ 0x00400f10,
++ 0x00400f30,
++ 0x00400f14,
++ 0x00400f34,
++ 0x00400f18,
++ 0x00400f38,
++ 0x00400f1c,
++ 0x00400f3c,
++ NV10_PGRAPH_XFMODE0,
++ NV10_PGRAPH_XFMODE1,
++ NV10_PGRAPH_GLOBALSTATE0,
++ NV10_PGRAPH_GLOBALSTATE1,
++ NV04_PGRAPH_STORED_FMT,
++ NV04_PGRAPH_SOURCE_COLOR,
++ NV03_PGRAPH_ABS_X_RAM, /* 32 values from 0x400400 to 0x40047c */
++ NV03_PGRAPH_ABS_Y_RAM, /* 32 values from 0x400480 to 0x4004fc */
++ 0x00400404,
++ 0x00400484,
++ 0x00400408,
++ 0x00400488,
++ 0x0040040c,
++ 0x0040048c,
++ 0x00400410,
++ 0x00400490,
++ 0x00400414,
++ 0x00400494,
++ 0x00400418,
++ 0x00400498,
++ 0x0040041c,
++ 0x0040049c,
++ 0x00400420,
++ 0x004004a0,
++ 0x00400424,
++ 0x004004a4,
++ 0x00400428,
++ 0x004004a8,
++ 0x0040042c,
++ 0x004004ac,
++ 0x00400430,
++ 0x004004b0,
++ 0x00400434,
++ 0x004004b4,
++ 0x00400438,
++ 0x004004b8,
++ 0x0040043c,
++ 0x004004bc,
++ 0x00400440,
++ 0x004004c0,
++ 0x00400444,
++ 0x004004c4,
++ 0x00400448,
++ 0x004004c8,
++ 0x0040044c,
++ 0x004004cc,
++ 0x00400450,
++ 0x004004d0,
++ 0x00400454,
++ 0x004004d4,
++ 0x00400458,
++ 0x004004d8,
++ 0x0040045c,
++ 0x004004dc,
++ 0x00400460,
++ 0x004004e0,
++ 0x00400464,
++ 0x004004e4,
++ 0x00400468,
++ 0x004004e8,
++ 0x0040046c,
++ 0x004004ec,
++ 0x00400470,
++ 0x004004f0,
++ 0x00400474,
++ 0x004004f4,
++ 0x00400478,
++ 0x004004f8,
++ 0x0040047c,
++ 0x004004fc,
++ NV03_PGRAPH_ABS_UCLIP_XMIN,
++ NV03_PGRAPH_ABS_UCLIP_XMAX,
++ NV03_PGRAPH_ABS_UCLIP_YMIN,
++ NV03_PGRAPH_ABS_UCLIP_YMAX,
++ 0x00400550,
++ 0x00400558,
++ 0x00400554,
++ 0x0040055c,
++ NV03_PGRAPH_ABS_UCLIPA_XMIN,
++ NV03_PGRAPH_ABS_UCLIPA_XMAX,
++ NV03_PGRAPH_ABS_UCLIPA_YMIN,
++ NV03_PGRAPH_ABS_UCLIPA_YMAX,
++ NV03_PGRAPH_ABS_ICLIP_XMAX,
++ NV03_PGRAPH_ABS_ICLIP_YMAX,
++ NV03_PGRAPH_XY_LOGIC_MISC0,
++ NV03_PGRAPH_XY_LOGIC_MISC1,
++ NV03_PGRAPH_XY_LOGIC_MISC2,
++ NV03_PGRAPH_XY_LOGIC_MISC3,
++ NV03_PGRAPH_CLIPX_0,
++ NV03_PGRAPH_CLIPX_1,
++ NV03_PGRAPH_CLIPY_0,
++ NV03_PGRAPH_CLIPY_1,
++ NV10_PGRAPH_COMBINER0_IN_ALPHA,
++ NV10_PGRAPH_COMBINER1_IN_ALPHA,
++ NV10_PGRAPH_COMBINER0_IN_RGB,
++ NV10_PGRAPH_COMBINER1_IN_RGB,
++ NV10_PGRAPH_COMBINER_COLOR0,
++ NV10_PGRAPH_COMBINER_COLOR1,
++ NV10_PGRAPH_COMBINER0_OUT_ALPHA,
++ NV10_PGRAPH_COMBINER1_OUT_ALPHA,
++ NV10_PGRAPH_COMBINER0_OUT_RGB,
++ NV10_PGRAPH_COMBINER1_OUT_RGB,
++ NV10_PGRAPH_COMBINER_FINAL0,
++ NV10_PGRAPH_COMBINER_FINAL1,
++ 0x00400e00,
++ 0x00400e04,
++ 0x00400e08,
++ 0x00400e0c,
++ 0x00400e10,
++ 0x00400e14,
++ 0x00400e18,
++ 0x00400e1c,
++ 0x00400e20,
++ 0x00400e24,
++ 0x00400e28,
++ 0x00400e2c,
++ 0x00400e30,
++ 0x00400e34,
++ 0x00400e38,
++ 0x00400e3c,
++ NV04_PGRAPH_PASSTHRU_0,
++ NV04_PGRAPH_PASSTHRU_1,
++ NV04_PGRAPH_PASSTHRU_2,
++ NV10_PGRAPH_DIMX_TEXTURE,
++ NV10_PGRAPH_WDIMX_TEXTURE,
++ NV10_PGRAPH_DVD_COLORFMT,
++ NV10_PGRAPH_SCALED_FORMAT,
++ NV04_PGRAPH_MISC24_0,
++ NV04_PGRAPH_MISC24_1,
++ NV04_PGRAPH_MISC24_2,
++ NV03_PGRAPH_X_MISC,
++ NV03_PGRAPH_Y_MISC,
++ NV04_PGRAPH_VALID1,
++ NV04_PGRAPH_VALID2,
++};
++
++static int nv17_graph_ctx_regs[] = {
++ NV10_PGRAPH_DEBUG_4,
++ 0x004006b0,
++ 0x00400eac,
++ 0x00400eb0,
++ 0x00400eb4,
++ 0x00400eb8,
++ 0x00400ebc,
++ 0x00400ec0,
++ 0x00400ec4,
++ 0x00400ec8,
++ 0x00400ecc,
++ 0x00400ed0,
++ 0x00400ed4,
++ 0x00400ed8,
++ 0x00400edc,
++ 0x00400ee0,
++ 0x00400a00,
++ 0x00400a04,
++};
++
++struct graph_state {
++ int nv10[ARRAY_SIZE(nv10_graph_ctx_regs)];
++ int nv17[ARRAY_SIZE(nv17_graph_ctx_regs)];
++ struct pipe_state pipe_state;
++ uint32_t lma_window[4];
++};
++
++#define PIPE_SAVE(dev, state, addr) \
++ do { \
++ int __i; \
++ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, addr); \
++ for (__i = 0; __i < ARRAY_SIZE(state); __i++) \
++ state[__i] = nv_rd32(dev, NV10_PGRAPH_PIPE_DATA); \
++ } while (0)
++
++#define PIPE_RESTORE(dev, state, addr) \
++ do { \
++ int __i; \
++ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, addr); \
++ for (__i = 0; __i < ARRAY_SIZE(state); __i++) \
++ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, state[__i]); \
++ } while (0)
++
++static void nv10_graph_save_pipe(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct graph_state *pgraph_ctx = chan->pgraph_ctx;
++ struct pipe_state *pipe = &pgraph_ctx->pipe_state;
++
++ PIPE_SAVE(dev, pipe->pipe_0x4400, 0x4400);
++ PIPE_SAVE(dev, pipe->pipe_0x0200, 0x0200);
++ PIPE_SAVE(dev, pipe->pipe_0x6400, 0x6400);
++ PIPE_SAVE(dev, pipe->pipe_0x6800, 0x6800);
++ PIPE_SAVE(dev, pipe->pipe_0x6c00, 0x6c00);
++ PIPE_SAVE(dev, pipe->pipe_0x7000, 0x7000);
++ PIPE_SAVE(dev, pipe->pipe_0x7400, 0x7400);
++ PIPE_SAVE(dev, pipe->pipe_0x7800, 0x7800);
++ PIPE_SAVE(dev, pipe->pipe_0x0040, 0x0040);
++ PIPE_SAVE(dev, pipe->pipe_0x0000, 0x0000);
++}
++
++static void nv10_graph_load_pipe(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct graph_state *pgraph_ctx = chan->pgraph_ctx;
++ struct pipe_state *pipe = &pgraph_ctx->pipe_state;
++ uint32_t xfmode0, xfmode1;
++ int i;
++
++ nouveau_wait_for_idle(dev);
++ /* XXX check haiku comments */
++ xfmode0 = nv_rd32(dev, NV10_PGRAPH_XFMODE0);
++ xfmode1 = nv_rd32(dev, NV10_PGRAPH_XFMODE1);
++ nv_wr32(dev, NV10_PGRAPH_XFMODE0, 0x10000000);
++ nv_wr32(dev, NV10_PGRAPH_XFMODE1, 0x00000000);
++ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x000064c0);
++ for (i = 0; i < 4; i++)
++ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x3f800000);
++ for (i = 0; i < 4; i++)
++ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000000);
++
++ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00006ab0);
++ for (i = 0; i < 3; i++)
++ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x3f800000);
++
++ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00006a80);
++ for (i = 0; i < 3; i++)
++ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000000);
++
++ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00000040);
++ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000008);
++
++
++ PIPE_RESTORE(dev, pipe->pipe_0x0200, 0x0200);
++ nouveau_wait_for_idle(dev);
++
++ /* restore XFMODE */
++ nv_wr32(dev, NV10_PGRAPH_XFMODE0, xfmode0);
++ nv_wr32(dev, NV10_PGRAPH_XFMODE1, xfmode1);
++ PIPE_RESTORE(dev, pipe->pipe_0x6400, 0x6400);
++ PIPE_RESTORE(dev, pipe->pipe_0x6800, 0x6800);
++ PIPE_RESTORE(dev, pipe->pipe_0x6c00, 0x6c00);
++ PIPE_RESTORE(dev, pipe->pipe_0x7000, 0x7000);
++ PIPE_RESTORE(dev, pipe->pipe_0x7400, 0x7400);
++ PIPE_RESTORE(dev, pipe->pipe_0x7800, 0x7800);
++ PIPE_RESTORE(dev, pipe->pipe_0x4400, 0x4400);
++ PIPE_RESTORE(dev, pipe->pipe_0x0000, 0x0000);
++ PIPE_RESTORE(dev, pipe->pipe_0x0040, 0x0040);
++ nouveau_wait_for_idle(dev);
++}
++
++static void nv10_graph_create_pipe(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct graph_state *pgraph_ctx = chan->pgraph_ctx;
++ struct pipe_state *fifo_pipe_state = &pgraph_ctx->pipe_state;
++ uint32_t *fifo_pipe_state_addr;
++ int i;
++#define PIPE_INIT(addr) \
++ do { \
++ fifo_pipe_state_addr = fifo_pipe_state->pipe_##addr; \
++ } while (0)
++#define PIPE_INIT_END(addr) \
++ do { \
++ uint32_t *__end_addr = fifo_pipe_state->pipe_##addr + \
++ ARRAY_SIZE(fifo_pipe_state->pipe_##addr); \
++ if (fifo_pipe_state_addr != __end_addr) \
++ NV_ERROR(dev, "incomplete pipe init for 0x%x : %p/%p\n", \
++ addr, fifo_pipe_state_addr, __end_addr); \
++ } while (0)
++#define NV_WRITE_PIPE_INIT(value) *(fifo_pipe_state_addr++) = value
++
++ PIPE_INIT(0x0200);
++ for (i = 0; i < 48; i++)
++ NV_WRITE_PIPE_INIT(0x00000000);
++ PIPE_INIT_END(0x0200);
++
++ PIPE_INIT(0x6400);
++ for (i = 0; i < 211; i++)
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x3f800000);
++ NV_WRITE_PIPE_INIT(0x40000000);
++ NV_WRITE_PIPE_INIT(0x40000000);
++ NV_WRITE_PIPE_INIT(0x40000000);
++ NV_WRITE_PIPE_INIT(0x40000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x3f800000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x3f000000);
++ NV_WRITE_PIPE_INIT(0x3f000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x3f800000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x3f800000);
++ NV_WRITE_PIPE_INIT(0x3f800000);
++ NV_WRITE_PIPE_INIT(0x3f800000);
++ NV_WRITE_PIPE_INIT(0x3f800000);
++ PIPE_INIT_END(0x6400);
++
++ PIPE_INIT(0x6800);
++ for (i = 0; i < 162; i++)
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x3f800000);
++ for (i = 0; i < 25; i++)
++ NV_WRITE_PIPE_INIT(0x00000000);
++ PIPE_INIT_END(0x6800);
++
++ PIPE_INIT(0x6c00);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0xbf800000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ PIPE_INIT_END(0x6c00);
++
++ PIPE_INIT(0x7000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x7149f2ca);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x7149f2ca);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x7149f2ca);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x7149f2ca);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x7149f2ca);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x7149f2ca);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x7149f2ca);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x00000000);
++ NV_WRITE_PIPE_INIT(0x7149f2ca);
++ for (i = 0; i < 35; i++)
++ NV_WRITE_PIPE_INIT(0x00000000);
++ PIPE_INIT_END(0x7000);
++
++ PIPE_INIT(0x7400);
++ for (i = 0; i < 48; i++)
++ NV_WRITE_PIPE_INIT(0x00000000);
++ PIPE_INIT_END(0x7400);
++
++ PIPE_INIT(0x7800);
++ for (i = 0; i < 48; i++)
++ NV_WRITE_PIPE_INIT(0x00000000);
++ PIPE_INIT_END(0x7800);
++
++ PIPE_INIT(0x4400);
++ for (i = 0; i < 32; i++)
++ NV_WRITE_PIPE_INIT(0x00000000);
++ PIPE_INIT_END(0x4400);
++
++ PIPE_INIT(0x0000);
++ for (i = 0; i < 16; i++)
++ NV_WRITE_PIPE_INIT(0x00000000);
++ PIPE_INIT_END(0x0000);
++
++ PIPE_INIT(0x0040);
++ for (i = 0; i < 4; i++)
++ NV_WRITE_PIPE_INIT(0x00000000);
++ PIPE_INIT_END(0x0040);
++
++#undef PIPE_INIT
++#undef PIPE_INIT_END
++#undef NV_WRITE_PIPE_INIT
++}
++
++static int nv10_graph_ctx_regs_find_offset(struct drm_device *dev, int reg)
++{
++ int i;
++ for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++) {
++ if (nv10_graph_ctx_regs[i] == reg)
++ return i;
++ }
++ NV_ERROR(dev, "unknow offset nv10_ctx_regs %d\n", reg);
++ return -1;
++}
++
++static int nv17_graph_ctx_regs_find_offset(struct drm_device *dev, int reg)
++{
++ int i;
++ for (i = 0; i < ARRAY_SIZE(nv17_graph_ctx_regs); i++) {
++ if (nv17_graph_ctx_regs[i] == reg)
++ return i;
++ }
++ NV_ERROR(dev, "unknow offset nv17_ctx_regs %d\n", reg);
++ return -1;
++}
++
++int nv10_graph_load_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct graph_state *pgraph_ctx = chan->pgraph_ctx;
++ uint32_t tmp;
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++)
++ nv_wr32(dev, nv10_graph_ctx_regs[i], pgraph_ctx->nv10[i]);
++ if (dev_priv->chipset >= 0x17) {
++ for (i = 0; i < ARRAY_SIZE(nv17_graph_ctx_regs); i++)
++ nv_wr32(dev, nv17_graph_ctx_regs[i],
++ pgraph_ctx->nv17[i]);
++ }
++
++ nv10_graph_load_pipe(chan);
++
++ nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100);
++ tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER);
++ nv_wr32(dev, NV10_PGRAPH_CTX_USER, (tmp & 0xffffff) | chan->id << 24);
++ tmp = nv_rd32(dev, NV10_PGRAPH_FFINTFC_ST2);
++ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2, tmp & 0xcfffffff);
++ return 0;
++}
++
++int
++nv10_graph_unload_context(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
++ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
++ struct nouveau_channel *chan;
++ struct graph_state *ctx;
++ uint32_t tmp;
++ int i;
++
++ chan = pgraph->channel(dev);
++ if (!chan)
++ return 0;
++ ctx = chan->pgraph_ctx;
++
++ for (i = 0; i < ARRAY_SIZE(nv10_graph_ctx_regs); i++)
++ ctx->nv10[i] = nv_rd32(dev, nv10_graph_ctx_regs[i]);
++
++ if (dev_priv->chipset >= 0x17) {
++ for (i = 0; i < ARRAY_SIZE(nv17_graph_ctx_regs); i++)
++ ctx->nv17[i] = nv_rd32(dev, nv17_graph_ctx_regs[i]);
++ }
++
++ nv10_graph_save_pipe(chan);
++
++ nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000);
++ tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff;
++ tmp |= (pfifo->channels - 1) << 24;
++ nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp);
++ return 0;
++}
++
++void
++nv10_graph_context_switch(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
++ struct nouveau_channel *chan = NULL;
++ int chid;
++
++ pgraph->fifo_access(dev, false);
++ nouveau_wait_for_idle(dev);
++
++ /* If previous context is valid, we need to save it */
++ nv10_graph_unload_context(dev);
++
++ /* Load context for next channel */
++ chid = (nv_rd32(dev, NV04_PGRAPH_TRAPPED_ADDR) >> 20) & 0x1f;
++ chan = dev_priv->fifos[chid];
++ if (chan)
++ nv10_graph_load_context(chan);
++
++ pgraph->fifo_access(dev, true);
++}
++
++#define NV_WRITE_CTX(reg, val) do { \
++ int offset = nv10_graph_ctx_regs_find_offset(dev, reg); \
++ if (offset > 0) \
++ pgraph_ctx->nv10[offset] = val; \
++ } while (0)
++
++#define NV17_WRITE_CTX(reg, val) do { \
++ int offset = nv17_graph_ctx_regs_find_offset(dev, reg); \
++ if (offset > 0) \
++ pgraph_ctx->nv17[offset] = val; \
++ } while (0)
++
++struct nouveau_channel *
++nv10_graph_channel(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int chid = dev_priv->engine.fifo.channels;
++
++ if (nv_rd32(dev, NV10_PGRAPH_CTX_CONTROL) & 0x00010000)
++ chid = nv_rd32(dev, NV10_PGRAPH_CTX_USER) >> 24;
++
++ if (chid >= dev_priv->engine.fifo.channels)
++ return NULL;
++
++ return dev_priv->fifos[chid];
++}
++
++int nv10_graph_create_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct graph_state *pgraph_ctx;
++
++ NV_DEBUG(dev, "nv10_graph_context_create %d\n", chan->id);
++
++ chan->pgraph_ctx = pgraph_ctx = kzalloc(sizeof(*pgraph_ctx),
++ GFP_KERNEL);
++ if (pgraph_ctx == NULL)
++ return -ENOMEM;
++
++
++ NV_WRITE_CTX(0x00400e88, 0x08000000);
++ NV_WRITE_CTX(0x00400e9c, 0x4b7fffff);
++ NV_WRITE_CTX(NV03_PGRAPH_XY_LOGIC_MISC0, 0x0001ffff);
++ NV_WRITE_CTX(0x00400e10, 0x00001000);
++ NV_WRITE_CTX(0x00400e14, 0x00001000);
++ NV_WRITE_CTX(0x00400e30, 0x00080008);
++ NV_WRITE_CTX(0x00400e34, 0x00080008);
++ if (dev_priv->chipset >= 0x17) {
++ /* is it really needed ??? */
++ NV17_WRITE_CTX(NV10_PGRAPH_DEBUG_4,
++ nv_rd32(dev, NV10_PGRAPH_DEBUG_4));
++ NV17_WRITE_CTX(0x004006b0, nv_rd32(dev, 0x004006b0));
++ NV17_WRITE_CTX(0x00400eac, 0x0fff0000);
++ NV17_WRITE_CTX(0x00400eb0, 0x0fff0000);
++ NV17_WRITE_CTX(0x00400ec0, 0x00000080);
++ NV17_WRITE_CTX(0x00400ed0, 0x00000080);
++ }
++ NV_WRITE_CTX(NV10_PGRAPH_CTX_USER, chan->id << 24);
++
++ nv10_graph_create_pipe(chan);
++ return 0;
++}
++
++void nv10_graph_destroy_context(struct nouveau_channel *chan)
++{
++ struct graph_state *pgraph_ctx = chan->pgraph_ctx;
++
++ kfree(pgraph_ctx);
++ chan->pgraph_ctx = NULL;
++}
++
++void
++nv10_graph_set_region_tiling(struct drm_device *dev, int i, uint32_t addr,
++ uint32_t size, uint32_t pitch)
++{
++ uint32_t limit = max(1u, addr + size) - 1;
++
++ if (pitch)
++ addr |= 1 << 31;
++
++ nv_wr32(dev, NV10_PGRAPH_TLIMIT(i), limit);
++ nv_wr32(dev, NV10_PGRAPH_TSIZE(i), pitch);
++ nv_wr32(dev, NV10_PGRAPH_TILE(i), addr);
++}
++
++int nv10_graph_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t tmp;
++ int i;
++
++ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) &
++ ~NV_PMC_ENABLE_PGRAPH);
++ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
++ NV_PMC_ENABLE_PGRAPH);
++
++ nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF);
++ nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
++
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF);
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000);
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x00118700);
++ /* nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x24E00810); */ /* 0x25f92ad9 */
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_2, 0x25f92ad9);
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0x55DE0830 |
++ (1<<29) |
++ (1<<31));
++ if (dev_priv->chipset >= 0x17) {
++ nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x1f000000);
++ nv_wr32(dev, 0x400a10, 0x3ff3fb6);
++ nv_wr32(dev, 0x400838, 0x2f8684);
++ nv_wr32(dev, 0x40083c, 0x115f3f);
++ nv_wr32(dev, 0x004006b0, 0x40000020);
++ } else
++ nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00000000);
++
++ /* Turn all the tiling regions off. */
++ for (i = 0; i < NV10_PFB_TILE__SIZE; i++)
++ nv10_graph_set_region_tiling(dev, i, 0, 0, 0);
++
++ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH1, 0x00000000);
++ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH2, 0x00000000);
++ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH3, 0x00000000);
++ nv_wr32(dev, NV10_PGRAPH_CTX_SWITCH4, 0x00000000);
++ nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF);
++
++ tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff;
++ tmp |= (dev_priv->engine.fifo.channels - 1) << 24;
++ nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp);
++ nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000100);
++ nv_wr32(dev, NV10_PGRAPH_FFINTFC_ST2, 0x08000000);
++
++ return 0;
++}
++
++void nv10_graph_takedown(struct drm_device *dev)
++{
++}
++
++static int
++nv17_graph_mthd_lma_window(struct nouveau_channel *chan, int grclass,
++ int mthd, uint32_t data)
++{
++ struct drm_device *dev = chan->dev;
++ struct graph_state *ctx = chan->pgraph_ctx;
++ struct pipe_state *pipe = &ctx->pipe_state;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
++ uint32_t pipe_0x0040[1], pipe_0x64c0[8], pipe_0x6a80[3], pipe_0x6ab0[3];
++ uint32_t xfmode0, xfmode1;
++ int i;
++
++ ctx->lma_window[(mthd - 0x1638) / 4] = data;
++
++ if (mthd != 0x1644)
++ return 0;
++
++ nouveau_wait_for_idle(dev);
++
++ PIPE_SAVE(dev, pipe_0x0040, 0x0040);
++ PIPE_SAVE(dev, pipe->pipe_0x0200, 0x0200);
++
++ PIPE_RESTORE(dev, ctx->lma_window, 0x6790);
++
++ nouveau_wait_for_idle(dev);
++
++ xfmode0 = nv_rd32(dev, NV10_PGRAPH_XFMODE0);
++ xfmode1 = nv_rd32(dev, NV10_PGRAPH_XFMODE1);
++
++ PIPE_SAVE(dev, pipe->pipe_0x4400, 0x4400);
++ PIPE_SAVE(dev, pipe_0x64c0, 0x64c0);
++ PIPE_SAVE(dev, pipe_0x6ab0, 0x6ab0);
++ PIPE_SAVE(dev, pipe_0x6a80, 0x6a80);
++
++ nouveau_wait_for_idle(dev);
++
++ nv_wr32(dev, NV10_PGRAPH_XFMODE0, 0x10000000);
++ nv_wr32(dev, NV10_PGRAPH_XFMODE1, 0x00000000);
++ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x000064c0);
++ for (i = 0; i < 4; i++)
++ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x3f800000);
++ for (i = 0; i < 4; i++)
++ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000000);
++
++ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00006ab0);
++ for (i = 0; i < 3; i++)
++ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x3f800000);
++
++ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00006a80);
++ for (i = 0; i < 3; i++)
++ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000000);
++
++ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x00000040);
++ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000008);
++
++ PIPE_RESTORE(dev, pipe->pipe_0x0200, 0x0200);
++
++ nouveau_wait_for_idle(dev);
++
++ PIPE_RESTORE(dev, pipe_0x0040, 0x0040);
++
++ nv_wr32(dev, NV10_PGRAPH_XFMODE0, xfmode0);
++ nv_wr32(dev, NV10_PGRAPH_XFMODE1, xfmode1);
++
++ PIPE_RESTORE(dev, pipe_0x64c0, 0x64c0);
++ PIPE_RESTORE(dev, pipe_0x6ab0, 0x6ab0);
++ PIPE_RESTORE(dev, pipe_0x6a80, 0x6a80);
++ PIPE_RESTORE(dev, pipe->pipe_0x4400, 0x4400);
++
++ nv_wr32(dev, NV10_PGRAPH_PIPE_ADDRESS, 0x000000c0);
++ nv_wr32(dev, NV10_PGRAPH_PIPE_DATA, 0x00000000);
++
++ nouveau_wait_for_idle(dev);
++
++ pgraph->fifo_access(dev, true);
++
++ return 0;
++}
++
++static int
++nv17_graph_mthd_lma_enable(struct nouveau_channel *chan, int grclass,
++ int mthd, uint32_t data)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
++
++ nouveau_wait_for_idle(dev);
++
++ nv_wr32(dev, NV10_PGRAPH_DEBUG_4,
++ nv_rd32(dev, NV10_PGRAPH_DEBUG_4) | 0x1 << 8);
++ nv_wr32(dev, 0x004006b0,
++ nv_rd32(dev, 0x004006b0) | 0x8 << 24);
++
++ pgraph->fifo_access(dev, true);
++
++ return 0;
++}
++
++static struct nouveau_pgraph_object_method nv17_graph_celsius_mthds[] = {
++ { 0x1638, nv17_graph_mthd_lma_window },
++ { 0x163c, nv17_graph_mthd_lma_window },
++ { 0x1640, nv17_graph_mthd_lma_window },
++ { 0x1644, nv17_graph_mthd_lma_window },
++ { 0x1658, nv17_graph_mthd_lma_enable },
++ {}
++};
++
++struct nouveau_pgraph_object_class nv10_graph_grclass[] = {
++ { 0x0030, false, NULL }, /* null */
++ { 0x0039, false, NULL }, /* m2mf */
++ { 0x004a, false, NULL }, /* gdirect */
++ { 0x005f, false, NULL }, /* imageblit */
++ { 0x009f, false, NULL }, /* imageblit (nv12) */
++ { 0x008a, false, NULL }, /* ifc */
++ { 0x0089, false, NULL }, /* sifm */
++ { 0x0062, false, NULL }, /* surf2d */
++ { 0x0043, false, NULL }, /* rop */
++ { 0x0012, false, NULL }, /* beta1 */
++ { 0x0072, false, NULL }, /* beta4 */
++ { 0x0019, false, NULL }, /* cliprect */
++ { 0x0044, false, NULL }, /* pattern */
++ { 0x0052, false, NULL }, /* swzsurf */
++ { 0x0093, false, NULL }, /* surf3d */
++ { 0x0094, false, NULL }, /* tex_tri */
++ { 0x0095, false, NULL }, /* multitex_tri */
++ { 0x0056, false, NULL }, /* celcius (nv10) */
++ { 0x0096, false, NULL }, /* celcius (nv11) */
++ { 0x0099, false, nv17_graph_celsius_mthds }, /* celcius (nv17) */
++ {}
++};
+diff --git a/drivers/gpu/drm/nouveau/nv17_gpio.c b/drivers/gpu/drm/nouveau/nv17_gpio.c
+new file mode 100644
+index 0000000..2e58c33
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv17_gpio.c
+@@ -0,0 +1,92 @@
++/*
++ * Copyright (C) 2009 Francisco Jerez.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "nouveau_drv.h"
++#include "nouveau_hw.h"
++
++static bool
++get_gpio_location(struct dcb_gpio_entry *ent, uint32_t *reg, uint32_t *shift,
++ uint32_t *mask)
++{
++ if (ent->line < 2) {
++ *reg = NV_PCRTC_GPIO;
++ *shift = ent->line * 16;
++ *mask = 0x11;
++
++ } else if (ent->line < 10) {
++ *reg = NV_PCRTC_GPIO_EXT;
++ *shift = (ent->line - 2) * 4;
++ *mask = 0x3;
++
++ } else if (ent->line < 14) {
++ *reg = NV_PCRTC_850;
++ *shift = (ent->line - 10) * 4;
++ *mask = 0x3;
++
++ } else {
++ return false;
++ }
++
++ return true;
++}
++
++int
++nv17_gpio_get(struct drm_device *dev, enum dcb_gpio_tag tag)
++{
++ struct dcb_gpio_entry *ent = nouveau_bios_gpio_entry(dev, tag);
++ uint32_t reg, shift, mask, value;
++
++ if (!ent)
++ return -ENODEV;
++
++ if (!get_gpio_location(ent, &reg, &shift, &mask))
++ return -ENODEV;
++
++ value = NVReadCRTC(dev, 0, reg) >> shift;
++
++ return (ent->invert ? 1 : 0) ^ (value & 1);
++}
++
++int
++nv17_gpio_set(struct drm_device *dev, enum dcb_gpio_tag tag, int state)
++{
++ struct dcb_gpio_entry *ent = nouveau_bios_gpio_entry(dev, tag);
++ uint32_t reg, shift, mask, value;
++
++ if (!ent)
++ return -ENODEV;
++
++ if (!get_gpio_location(ent, &reg, &shift, &mask))
++ return -ENODEV;
++
++ value = ((ent->invert ? 1 : 0) ^ (state ? 1 : 0)) << shift;
++ mask = ~(mask << shift);
++
++ NVWriteCRTC(dev, 0, reg, value | (NVReadCRTC(dev, 0, reg) & mask));
++
++ return 0;
++}
+diff --git a/drivers/gpu/drm/nouveau/nv17_tv.c b/drivers/gpu/drm/nouveau/nv17_tv.c
+new file mode 100644
+index 0000000..58b917c
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv17_tv.c
+@@ -0,0 +1,776 @@
++/*
++ * Copyright (C) 2009 Francisco Jerez.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "drm_crtc_helper.h"
++#include "nouveau_drv.h"
++#include "nouveau_encoder.h"
++#include "nouveau_connector.h"
++#include "nouveau_crtc.h"
++#include "nouveau_hw.h"
++#include "nv17_tv.h"
++
++static uint32_t nv42_tv_sample_load(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t testval, regoffset = nv04_dac_output_offset(encoder);
++ uint32_t gpio0, gpio1, fp_htotal, fp_hsync_start, fp_hsync_end,
++ fp_control, test_ctrl, dacclk, ctv_14, ctv_1c, ctv_6c;
++ uint32_t sample = 0;
++ int head;
++
++#define RGB_TEST_DATA(r, g, b) (r << 0 | g << 10 | b << 20)
++ testval = RGB_TEST_DATA(0x82, 0xeb, 0x82);
++ if (dev_priv->vbios->tvdactestval)
++ testval = dev_priv->vbios->tvdactestval;
++
++ dacclk = NVReadRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset);
++ head = (dacclk & 0x100) >> 8;
++
++ /* Save the previous state. */
++ gpio1 = nv17_gpio_get(dev, DCB_GPIO_TVDAC1);
++ gpio0 = nv17_gpio_get(dev, DCB_GPIO_TVDAC0);
++ fp_htotal = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL);
++ fp_hsync_start = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START);
++ fp_hsync_end = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END);
++ fp_control = NVReadRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL);
++ test_ctrl = NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset);
++ ctv_1c = NVReadRAMDAC(dev, head, 0x680c1c);
++ ctv_14 = NVReadRAMDAC(dev, head, 0x680c14);
++ ctv_6c = NVReadRAMDAC(dev, head, 0x680c6c);
++
++ /* Prepare the DAC for load detection. */
++ nv17_gpio_set(dev, DCB_GPIO_TVDAC1, true);
++ nv17_gpio_set(dev, DCB_GPIO_TVDAC0, true);
++
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, 1343);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, 1047);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END, 1183);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL,
++ NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |
++ NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12 |
++ NV_PRAMDAC_FP_TG_CONTROL_READ_PROG |
++ NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS |
++ NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS);
++
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, 0);
++
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset,
++ (dacclk & ~0xff) | 0x22);
++ msleep(1);
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset,
++ (dacclk & ~0xff) | 0x21);
++
++ NVWriteRAMDAC(dev, head, 0x680c1c, 1 << 20);
++ NVWriteRAMDAC(dev, head, 0x680c14, 4 << 16);
++
++ /* Sample pin 0x4 (usually S-video luma). */
++ NVWriteRAMDAC(dev, head, 0x680c6c, testval >> 10 & 0x3ff);
++ msleep(20);
++ sample |= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset)
++ & 0x4 << 28;
++
++ /* Sample the remaining pins. */
++ NVWriteRAMDAC(dev, head, 0x680c6c, testval & 0x3ff);
++ msleep(20);
++ sample |= NVReadRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset)
++ & 0xa << 28;
++
++ /* Restore the previous state. */
++ NVWriteRAMDAC(dev, head, 0x680c1c, ctv_1c);
++ NVWriteRAMDAC(dev, head, 0x680c14, ctv_14);
++ NVWriteRAMDAC(dev, head, 0x680c6c, ctv_6c);
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK + regoffset, dacclk);
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL + regoffset, test_ctrl);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_TG_CONTROL, fp_control);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_END, fp_hsync_end);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HSYNC_START, fp_hsync_start);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HTOTAL, fp_htotal);
++ nv17_gpio_set(dev, DCB_GPIO_TVDAC1, gpio1);
++ nv17_gpio_set(dev, DCB_GPIO_TVDAC0, gpio0);
++
++ return sample;
++}
++
++static enum drm_connector_status
++nv17_tv_detect(struct drm_encoder *encoder, struct drm_connector *connector)
++{
++ struct drm_device *dev = encoder->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct drm_mode_config *conf = &dev->mode_config;
++ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
++ struct dcb_entry *dcb = tv_enc->base.dcb;
++
++ if (dev_priv->chipset == 0x42 ||
++ dev_priv->chipset == 0x43)
++ tv_enc->pin_mask = nv42_tv_sample_load(encoder) >> 28 & 0xe;
++ else
++ tv_enc->pin_mask = nv17_dac_sample_load(encoder) >> 28 & 0xe;
++
++ switch (tv_enc->pin_mask) {
++ case 0x2:
++ case 0x4:
++ tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Composite;
++ break;
++ case 0xc:
++ tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SVIDEO;
++ break;
++ case 0xe:
++ if (dcb->tvconf.has_component_output)
++ tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Component;
++ else
++ tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_SCART;
++ break;
++ default:
++ tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
++ break;
++ }
++
++ drm_connector_property_set_value(connector,
++ conf->tv_subconnector_property,
++ tv_enc->subconnector);
++
++ if (tv_enc->subconnector) {
++ NV_INFO(dev, "Load detected on output %c\n",
++ '@' + ffs(dcb->or));
++ return connector_status_connected;
++ } else {
++ return connector_status_disconnected;
++ }
++}
++
++static const struct {
++ int hdisplay;
++ int vdisplay;
++} modes[] = {
++ { 640, 400 },
++ { 640, 480 },
++ { 720, 480 },
++ { 720, 576 },
++ { 800, 600 },
++ { 1024, 768 },
++ { 1280, 720 },
++ { 1280, 1024 },
++ { 1920, 1080 }
++};
++
++static int nv17_tv_get_modes(struct drm_encoder *encoder,
++ struct drm_connector *connector)
++{
++ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
++ struct drm_display_mode *mode;
++ struct drm_display_mode *output_mode;
++ int n = 0;
++ int i;
++
++ if (tv_norm->kind != CTV_ENC_MODE) {
++ struct drm_display_mode *tv_mode;
++
++ for (tv_mode = nv17_tv_modes; tv_mode->hdisplay; tv_mode++) {
++ mode = drm_mode_duplicate(encoder->dev, tv_mode);
++
++ mode->clock = tv_norm->tv_enc_mode.vrefresh *
++ mode->htotal / 1000 *
++ mode->vtotal / 1000;
++
++ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
++ mode->clock *= 2;
++
++ if (mode->hdisplay == tv_norm->tv_enc_mode.hdisplay &&
++ mode->vdisplay == tv_norm->tv_enc_mode.vdisplay)
++ mode->type |= DRM_MODE_TYPE_PREFERRED;
++
++ drm_mode_probed_add(connector, mode);
++ n++;
++ }
++ return n;
++ }
++
++ /* tv_norm->kind == CTV_ENC_MODE */
++ output_mode = &tv_norm->ctv_enc_mode.mode;
++ for (i = 0; i < ARRAY_SIZE(modes); i++) {
++ if (modes[i].hdisplay > output_mode->hdisplay ||
++ modes[i].vdisplay > output_mode->vdisplay)
++ continue;
++
++ if (modes[i].hdisplay == output_mode->hdisplay &&
++ modes[i].vdisplay == output_mode->vdisplay) {
++ mode = drm_mode_duplicate(encoder->dev, output_mode);
++ mode->type |= DRM_MODE_TYPE_PREFERRED;
++ } else {
++ mode = drm_cvt_mode(encoder->dev, modes[i].hdisplay,
++ modes[i].vdisplay, 60, false,
++ output_mode->flags & DRM_MODE_FLAG_INTERLACE,
++ false);
++ }
++
++ /* CVT modes are sometimes unsuitable... */
++ if (output_mode->hdisplay <= 720
++ || output_mode->hdisplay >= 1920) {
++ mode->htotal = output_mode->htotal;
++ mode->hsync_start = (mode->hdisplay + (mode->htotal
++ - mode->hdisplay) * 9 / 10) & ~7;
++ mode->hsync_end = mode->hsync_start + 8;
++ }
++ if (output_mode->vdisplay >= 1024) {
++ mode->vtotal = output_mode->vtotal;
++ mode->vsync_start = output_mode->vsync_start;
++ mode->vsync_end = output_mode->vsync_end;
++ }
++
++ mode->type |= DRM_MODE_TYPE_DRIVER;
++ drm_mode_probed_add(connector, mode);
++ n++;
++ }
++ return n;
++}
++
++static int nv17_tv_mode_valid(struct drm_encoder *encoder,
++ struct drm_display_mode *mode)
++{
++ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
++
++ if (tv_norm->kind == CTV_ENC_MODE) {
++ struct drm_display_mode *output_mode =
++ &tv_norm->ctv_enc_mode.mode;
++
++ if (mode->clock > 400000)
++ return MODE_CLOCK_HIGH;
++
++ if (mode->hdisplay > output_mode->hdisplay ||
++ mode->vdisplay > output_mode->vdisplay)
++ return MODE_BAD;
++
++ if ((mode->flags & DRM_MODE_FLAG_INTERLACE) !=
++ (output_mode->flags & DRM_MODE_FLAG_INTERLACE))
++ return MODE_NO_INTERLACE;
++
++ if (mode->flags & DRM_MODE_FLAG_DBLSCAN)
++ return MODE_NO_DBLESCAN;
++
++ } else {
++ const int vsync_tolerance = 600;
++
++ if (mode->clock > 70000)
++ return MODE_CLOCK_HIGH;
++
++ if (abs(drm_mode_vrefresh(mode) * 1000 -
++ tv_norm->tv_enc_mode.vrefresh) > vsync_tolerance)
++ return MODE_VSYNC;
++
++ /* The encoder takes care of the actual interlacing */
++ if (mode->flags & DRM_MODE_FLAG_INTERLACE)
++ return MODE_NO_INTERLACE;
++ }
++
++ return MODE_OK;
++}
++
++static bool nv17_tv_mode_fixup(struct drm_encoder *encoder,
++ struct drm_display_mode *mode,
++ struct drm_display_mode *adjusted_mode)
++{
++ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
++
++ if (tv_norm->kind == CTV_ENC_MODE)
++ adjusted_mode->clock = tv_norm->ctv_enc_mode.mode.clock;
++ else
++ adjusted_mode->clock = 90000;
++
++ return true;
++}
++
++static void nv17_tv_dpms(struct drm_encoder *encoder, int mode)
++{
++ struct drm_device *dev = encoder->dev;
++ struct nv17_tv_state *regs = &to_tv_enc(encoder)->state;
++ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
++
++ if (nouveau_encoder(encoder)->last_dpms == mode)
++ return;
++ nouveau_encoder(encoder)->last_dpms = mode;
++
++ NV_INFO(dev, "Setting dpms mode %d on TV encoder (output %d)\n",
++ mode, nouveau_encoder(encoder)->dcb->index);
++
++ regs->ptv_200 &= ~1;
++
++ if (tv_norm->kind == CTV_ENC_MODE) {
++ nv04_dfp_update_fp_control(encoder, mode);
++
++ } else {
++ nv04_dfp_update_fp_control(encoder, DRM_MODE_DPMS_OFF);
++
++ if (mode == DRM_MODE_DPMS_ON)
++ regs->ptv_200 |= 1;
++ }
++
++ nv_load_ptv(dev, regs, 200);
++
++ nv17_gpio_set(dev, DCB_GPIO_TVDAC1, mode == DRM_MODE_DPMS_ON);
++ nv17_gpio_set(dev, DCB_GPIO_TVDAC0, mode == DRM_MODE_DPMS_ON);
++
++ nv04_dac_update_dacclk(encoder, mode == DRM_MODE_DPMS_ON);
++}
++
++static void nv17_tv_prepare(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
++ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
++ int head = nouveau_crtc(encoder->crtc)->index;
++ uint8_t *cr_lcd = &dev_priv->mode_reg.crtc_reg[head].CRTC[
++ NV_CIO_CRE_LCD__INDEX];
++ uint32_t dacclk_off = NV_PRAMDAC_DACCLK +
++ nv04_dac_output_offset(encoder);
++ uint32_t dacclk;
++
++ helper->dpms(encoder, DRM_MODE_DPMS_OFF);
++
++ nv04_dfp_disable(dev, head);
++
++ /* Unbind any FP encoders from this head if we need the FP
++ * stuff enabled. */
++ if (tv_norm->kind == CTV_ENC_MODE) {
++ struct drm_encoder *enc;
++
++ list_for_each_entry(enc, &dev->mode_config.encoder_list, head) {
++ struct dcb_entry *dcb = nouveau_encoder(enc)->dcb;
++
++ if ((dcb->type == OUTPUT_TMDS ||
++ dcb->type == OUTPUT_LVDS) &&
++ !enc->crtc &&
++ nv04_dfp_get_bound_head(dev, dcb) == head) {
++ nv04_dfp_bind_head(dev, dcb, head ^ 1,
++ dev_priv->VBIOS.fp.dual_link);
++ }
++ }
++
++ }
++
++ /* Some NV4x have unknown values (0x3f, 0x50, 0x54, 0x6b, 0x79, 0x7f)
++ * at LCD__INDEX which we don't alter
++ */
++ if (!(*cr_lcd & 0x44)) {
++ if (tv_norm->kind == CTV_ENC_MODE)
++ *cr_lcd = 0x1 | (head ? 0x0 : 0x8);
++ else
++ *cr_lcd = 0;
++ }
++
++ /* Set the DACCLK register */
++ dacclk = (NVReadRAMDAC(dev, 0, dacclk_off) & ~0x30) | 0x1;
++
++ if (dev_priv->card_type == NV_40)
++ dacclk |= 0x1a << 16;
++
++ if (tv_norm->kind == CTV_ENC_MODE) {
++ dacclk |= 0x20;
++
++ if (head)
++ dacclk |= 0x100;
++ else
++ dacclk &= ~0x100;
++
++ } else {
++ dacclk |= 0x10;
++
++ }
++
++ NVWriteRAMDAC(dev, 0, dacclk_off, dacclk);
++}
++
++static void nv17_tv_mode_set(struct drm_encoder *encoder,
++ struct drm_display_mode *drm_mode,
++ struct drm_display_mode *adjusted_mode)
++{
++ struct drm_device *dev = encoder->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int head = nouveau_crtc(encoder->crtc)->index;
++ struct nv04_crtc_reg *regs = &dev_priv->mode_reg.crtc_reg[head];
++ struct nv17_tv_state *tv_regs = &to_tv_enc(encoder)->state;
++ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
++ int i;
++
++ regs->CRTC[NV_CIO_CRE_53] = 0x40; /* FP_HTIMING */
++ regs->CRTC[NV_CIO_CRE_54] = 0; /* FP_VTIMING */
++ regs->ramdac_630 = 0x2; /* turn off green mode (tv test pattern?) */
++ regs->tv_setup = 1;
++ regs->ramdac_8c0 = 0x0;
++
++ if (tv_norm->kind == TV_ENC_MODE) {
++ tv_regs->ptv_200 = 0x13111100;
++ if (head)
++ tv_regs->ptv_200 |= 0x10;
++
++ tv_regs->ptv_20c = 0x808010;
++ tv_regs->ptv_304 = 0x2d00000;
++ tv_regs->ptv_600 = 0x0;
++ tv_regs->ptv_60c = 0x0;
++ tv_regs->ptv_610 = 0x1e00000;
++
++ if (tv_norm->tv_enc_mode.vdisplay == 576) {
++ tv_regs->ptv_508 = 0x1200000;
++ tv_regs->ptv_614 = 0x33;
++
++ } else if (tv_norm->tv_enc_mode.vdisplay == 480) {
++ tv_regs->ptv_508 = 0xf00000;
++ tv_regs->ptv_614 = 0x13;
++ }
++
++ if (dev_priv->card_type >= NV_30) {
++ tv_regs->ptv_500 = 0xe8e0;
++ tv_regs->ptv_504 = 0x1710;
++ tv_regs->ptv_604 = 0x0;
++ tv_regs->ptv_608 = 0x0;
++ } else {
++ if (tv_norm->tv_enc_mode.vdisplay == 576) {
++ tv_regs->ptv_604 = 0x20;
++ tv_regs->ptv_608 = 0x10;
++ tv_regs->ptv_500 = 0x19710;
++ tv_regs->ptv_504 = 0x68f0;
++
++ } else if (tv_norm->tv_enc_mode.vdisplay == 480) {
++ tv_regs->ptv_604 = 0x10;
++ tv_regs->ptv_608 = 0x20;
++ tv_regs->ptv_500 = 0x4b90;
++ tv_regs->ptv_504 = 0x1b480;
++ }
++ }
++
++ for (i = 0; i < 0x40; i++)
++ tv_regs->tv_enc[i] = tv_norm->tv_enc_mode.tv_enc[i];
++
++ } else {
++ struct drm_display_mode *output_mode =
++ &tv_norm->ctv_enc_mode.mode;
++
++ /* The registers in PRAMDAC+0xc00 control some timings and CSC
++ * parameters for the CTV encoder (It's only used for "HD" TV
++ * modes, I don't think I have enough working to guess what
++ * they exactly mean...), it's probably connected at the
++ * output of the FP encoder, but it also needs the analog
++ * encoder in its OR enabled and routed to the head it's
++ * using. It's enabled with the DACCLK register, bits [5:4].
++ */
++ for (i = 0; i < 38; i++)
++ regs->ctv_regs[i] = tv_norm->ctv_enc_mode.ctv_regs[i];
++
++ regs->fp_horiz_regs[FP_DISPLAY_END] = output_mode->hdisplay - 1;
++ regs->fp_horiz_regs[FP_TOTAL] = output_mode->htotal - 1;
++ regs->fp_horiz_regs[FP_SYNC_START] =
++ output_mode->hsync_start - 1;
++ regs->fp_horiz_regs[FP_SYNC_END] = output_mode->hsync_end - 1;
++ regs->fp_horiz_regs[FP_CRTC] = output_mode->hdisplay +
++ max((output_mode->hdisplay-600)/40 - 1, 1);
++
++ regs->fp_vert_regs[FP_DISPLAY_END] = output_mode->vdisplay - 1;
++ regs->fp_vert_regs[FP_TOTAL] = output_mode->vtotal - 1;
++ regs->fp_vert_regs[FP_SYNC_START] =
++ output_mode->vsync_start - 1;
++ regs->fp_vert_regs[FP_SYNC_END] = output_mode->vsync_end - 1;
++ regs->fp_vert_regs[FP_CRTC] = output_mode->vdisplay - 1;
++
++ regs->fp_control = NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS |
++ NV_PRAMDAC_FP_TG_CONTROL_READ_PROG |
++ NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12;
++
++ if (output_mode->flags & DRM_MODE_FLAG_PVSYNC)
++ regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS;
++ if (output_mode->flags & DRM_MODE_FLAG_PHSYNC)
++ regs->fp_control |= NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS;
++
++ regs->fp_debug_0 = NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND |
++ NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND |
++ NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR |
++ NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR |
++ NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED |
++ NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE |
++ NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE;
++
++ regs->fp_debug_2 = 0;
++
++ regs->fp_margin_color = 0x801080;
++
++ }
++}
++
++static void nv17_tv_commit(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(encoder->crtc);
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_encoder_helper_funcs *helper = encoder->helper_private;
++
++ if (get_tv_norm(encoder)->kind == TV_ENC_MODE) {
++ nv17_tv_update_rescaler(encoder);
++ nv17_tv_update_properties(encoder);
++ } else {
++ nv17_ctv_update_rescaler(encoder);
++ }
++
++ nv17_tv_state_load(dev, &to_tv_enc(encoder)->state);
++
++ /* This could use refinement for flatpanels, but it should work */
++ if (dev_priv->chipset < 0x44)
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
++ nv04_dac_output_offset(encoder),
++ 0xf0000000);
++ else
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_TEST_CONTROL +
++ nv04_dac_output_offset(encoder),
++ 0x00100000);
++
++ helper->dpms(encoder, DRM_MODE_DPMS_ON);
++
++ NV_INFO(dev, "Output %s is running on CRTC %d using output %c\n",
++ drm_get_connector_name(
++ &nouveau_encoder_connector_get(nv_encoder)->base),
++ nv_crtc->index, '@' + ffs(nv_encoder->dcb->or));
++}
++
++static void nv17_tv_save(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
++
++ nouveau_encoder(encoder)->restore.output =
++ NVReadRAMDAC(dev, 0,
++ NV_PRAMDAC_DACCLK +
++ nv04_dac_output_offset(encoder));
++
++ nv17_tv_state_save(dev, &tv_enc->saved_state);
++
++ tv_enc->state.ptv_200 = tv_enc->saved_state.ptv_200;
++}
++
++static void nv17_tv_restore(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++
++ NVWriteRAMDAC(dev, 0, NV_PRAMDAC_DACCLK +
++ nv04_dac_output_offset(encoder),
++ nouveau_encoder(encoder)->restore.output);
++
++ nv17_tv_state_load(dev, &to_tv_enc(encoder)->saved_state);
++}
++
++static int nv17_tv_create_resources(struct drm_encoder *encoder,
++ struct drm_connector *connector)
++{
++ struct drm_device *dev = encoder->dev;
++ struct drm_mode_config *conf = &dev->mode_config;
++ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
++ struct dcb_entry *dcb = nouveau_encoder(encoder)->dcb;
++ int num_tv_norms = dcb->tvconf.has_component_output ? NUM_TV_NORMS :
++ NUM_LD_TV_NORMS;
++ int i;
++
++ if (nouveau_tv_norm) {
++ for (i = 0; i < num_tv_norms; i++) {
++ if (!strcmp(nv17_tv_norm_names[i], nouveau_tv_norm)) {
++ tv_enc->tv_norm = i;
++ break;
++ }
++ }
++
++ if (i == num_tv_norms)
++ NV_WARN(dev, "Invalid TV norm setting \"%s\"\n",
++ nouveau_tv_norm);
++ }
++
++ drm_mode_create_tv_properties(dev, num_tv_norms, nv17_tv_norm_names);
++
++ drm_connector_attach_property(connector,
++ conf->tv_select_subconnector_property,
++ tv_enc->select_subconnector);
++ drm_connector_attach_property(connector,
++ conf->tv_subconnector_property,
++ tv_enc->subconnector);
++ drm_connector_attach_property(connector,
++ conf->tv_mode_property,
++ tv_enc->tv_norm);
++ drm_connector_attach_property(connector,
++ conf->tv_flicker_reduction_property,
++ tv_enc->flicker);
++ drm_connector_attach_property(connector,
++ conf->tv_saturation_property,
++ tv_enc->saturation);
++ drm_connector_attach_property(connector,
++ conf->tv_hue_property,
++ tv_enc->hue);
++ drm_connector_attach_property(connector,
++ conf->tv_overscan_property,
++ tv_enc->overscan);
++
++ return 0;
++}
++
++static int nv17_tv_set_property(struct drm_encoder *encoder,
++ struct drm_connector *connector,
++ struct drm_property *property,
++ uint64_t val)
++{
++ struct drm_mode_config *conf = &encoder->dev->mode_config;
++ struct drm_crtc *crtc = encoder->crtc;
++ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
++ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
++ bool modes_changed = false;
++
++ if (property == conf->tv_overscan_property) {
++ tv_enc->overscan = val;
++ if (encoder->crtc) {
++ if (tv_norm->kind == CTV_ENC_MODE)
++ nv17_ctv_update_rescaler(encoder);
++ else
++ nv17_tv_update_rescaler(encoder);
++ }
++
++ } else if (property == conf->tv_saturation_property) {
++ if (tv_norm->kind != TV_ENC_MODE)
++ return -EINVAL;
++
++ tv_enc->saturation = val;
++ nv17_tv_update_properties(encoder);
++
++ } else if (property == conf->tv_hue_property) {
++ if (tv_norm->kind != TV_ENC_MODE)
++ return -EINVAL;
++
++ tv_enc->hue = val;
++ nv17_tv_update_properties(encoder);
++
++ } else if (property == conf->tv_flicker_reduction_property) {
++ if (tv_norm->kind != TV_ENC_MODE)
++ return -EINVAL;
++
++ tv_enc->flicker = val;
++ if (encoder->crtc)
++ nv17_tv_update_rescaler(encoder);
++
++ } else if (property == conf->tv_mode_property) {
++ if (connector->dpms != DRM_MODE_DPMS_OFF)
++ return -EINVAL;
++
++ tv_enc->tv_norm = val;
++
++ modes_changed = true;
++
++ } else if (property == conf->tv_select_subconnector_property) {
++ if (tv_norm->kind != TV_ENC_MODE)
++ return -EINVAL;
++
++ tv_enc->select_subconnector = val;
++ nv17_tv_update_properties(encoder);
++
++ } else {
++ return -EINVAL;
++ }
++
++ if (modes_changed) {
++ drm_helper_probe_single_connector_modes(connector, 0, 0);
++
++ /* Disable the crtc to ensure a full modeset is
++ * performed whenever it's turned on again. */
++ if (crtc) {
++ struct drm_mode_set modeset = {
++ .crtc = crtc,
++ };
++
++ crtc->funcs->set_config(&modeset);
++ }
++ }
++
++ return 0;
++}
++
++static void nv17_tv_destroy(struct drm_encoder *encoder)
++{
++ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
++
++ NV_DEBUG_KMS(encoder->dev, "\n");
++
++ drm_encoder_cleanup(encoder);
++ kfree(tv_enc);
++}
++
++static struct drm_encoder_helper_funcs nv17_tv_helper_funcs = {
++ .dpms = nv17_tv_dpms,
++ .save = nv17_tv_save,
++ .restore = nv17_tv_restore,
++ .mode_fixup = nv17_tv_mode_fixup,
++ .prepare = nv17_tv_prepare,
++ .commit = nv17_tv_commit,
++ .mode_set = nv17_tv_mode_set,
++ .detect = nv17_tv_detect,
++};
++
++static struct drm_encoder_slave_funcs nv17_tv_slave_funcs = {
++ .get_modes = nv17_tv_get_modes,
++ .mode_valid = nv17_tv_mode_valid,
++ .create_resources = nv17_tv_create_resources,
++ .set_property = nv17_tv_set_property,
++};
++
++static struct drm_encoder_funcs nv17_tv_funcs = {
++ .destroy = nv17_tv_destroy,
++};
++
++int nv17_tv_create(struct drm_device *dev, struct dcb_entry *entry)
++{
++ struct drm_encoder *encoder;
++ struct nv17_tv_encoder *tv_enc = NULL;
++
++ tv_enc = kzalloc(sizeof(*tv_enc), GFP_KERNEL);
++ if (!tv_enc)
++ return -ENOMEM;
++
++ tv_enc->overscan = 50;
++ tv_enc->flicker = 50;
++ tv_enc->saturation = 50;
++ tv_enc->hue = 0;
++ tv_enc->tv_norm = TV_NORM_PAL;
++ tv_enc->subconnector = DRM_MODE_SUBCONNECTOR_Unknown;
++ tv_enc->select_subconnector = DRM_MODE_SUBCONNECTOR_Automatic;
++ tv_enc->pin_mask = 0;
++
++ encoder = to_drm_encoder(&tv_enc->base);
++
++ tv_enc->base.dcb = entry;
++ tv_enc->base.or = ffs(entry->or) - 1;
++
++ drm_encoder_init(dev, encoder, &nv17_tv_funcs, DRM_MODE_ENCODER_TVDAC);
++ drm_encoder_helper_add(encoder, &nv17_tv_helper_funcs);
++ to_encoder_slave(encoder)->slave_funcs = &nv17_tv_slave_funcs;
++
++ encoder->possible_crtcs = entry->heads;
++ encoder->possible_clones = 0;
++
++ return 0;
++}
+diff --git a/drivers/gpu/drm/nouveau/nv17_tv.h b/drivers/gpu/drm/nouveau/nv17_tv.h
+new file mode 100644
+index 0000000..c00977c
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv17_tv.h
+@@ -0,0 +1,156 @@
++/*
++ * Copyright (C) 2009 Francisco Jerez.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#ifndef __NV17_TV_H__
++#define __NV17_TV_H__
++
++struct nv17_tv_state {
++ uint8_t tv_enc[0x40];
++
++ uint32_t hfilter[4][7];
++ uint32_t hfilter2[4][7];
++ uint32_t vfilter[4][7];
++
++ uint32_t ptv_200;
++ uint32_t ptv_204;
++ uint32_t ptv_208;
++ uint32_t ptv_20c;
++ uint32_t ptv_304;
++ uint32_t ptv_500;
++ uint32_t ptv_504;
++ uint32_t ptv_508;
++ uint32_t ptv_600;
++ uint32_t ptv_604;
++ uint32_t ptv_608;
++ uint32_t ptv_60c;
++ uint32_t ptv_610;
++ uint32_t ptv_614;
++};
++
++enum nv17_tv_norm{
++ TV_NORM_PAL,
++ TV_NORM_PAL_M,
++ TV_NORM_PAL_N,
++ TV_NORM_PAL_NC,
++ TV_NORM_NTSC_M,
++ TV_NORM_NTSC_J,
++ NUM_LD_TV_NORMS,
++ TV_NORM_HD480I = NUM_LD_TV_NORMS,
++ TV_NORM_HD480P,
++ TV_NORM_HD576I,
++ TV_NORM_HD576P,
++ TV_NORM_HD720P,
++ TV_NORM_HD1080I,
++ NUM_TV_NORMS
++};
++
++struct nv17_tv_encoder {
++ struct nouveau_encoder base;
++
++ struct nv17_tv_state state;
++ struct nv17_tv_state saved_state;
++
++ int overscan;
++ int flicker;
++ int saturation;
++ int hue;
++ enum nv17_tv_norm tv_norm;
++ int subconnector;
++ int select_subconnector;
++ uint32_t pin_mask;
++};
++#define to_tv_enc(x) container_of(nouveau_encoder(x), \
++ struct nv17_tv_encoder, base)
++
++extern char *nv17_tv_norm_names[NUM_TV_NORMS];
++
++extern struct nv17_tv_norm_params {
++ enum {
++ TV_ENC_MODE,
++ CTV_ENC_MODE,
++ } kind;
++
++ union {
++ struct {
++ int hdisplay;
++ int vdisplay;
++ int vrefresh; /* mHz */
++
++ uint8_t tv_enc[0x40];
++ } tv_enc_mode;
++
++ struct {
++ struct drm_display_mode mode;
++
++ uint32_t ctv_regs[38];
++ } ctv_enc_mode;
++ };
++
++} nv17_tv_norms[NUM_TV_NORMS];
++#define get_tv_norm(enc) (&nv17_tv_norms[to_tv_enc(enc)->tv_norm])
++
++extern struct drm_display_mode nv17_tv_modes[];
++
++static inline int interpolate(int y0, int y1, int y2, int x)
++{
++ return y1 + (x < 50 ? y1 - y0 : y2 - y1) * (x - 50) / 50;
++}
++
++void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state);
++void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state);
++void nv17_tv_update_properties(struct drm_encoder *encoder);
++void nv17_tv_update_rescaler(struct drm_encoder *encoder);
++void nv17_ctv_update_rescaler(struct drm_encoder *encoder);
++
++/* TV hardware access functions */
++
++static inline void nv_write_ptv(struct drm_device *dev, uint32_t reg, uint32_t val)
++{
++ nv_wr32(dev, reg, val);
++}
++
++static inline uint32_t nv_read_ptv(struct drm_device *dev, uint32_t reg)
++{
++ return nv_rd32(dev, reg);
++}
++
++static inline void nv_write_tv_enc(struct drm_device *dev, uint8_t reg, uint8_t val)
++{
++ nv_write_ptv(dev, NV_PTV_TV_INDEX, reg);
++ nv_write_ptv(dev, NV_PTV_TV_DATA, val);
++}
++
++static inline uint8_t nv_read_tv_enc(struct drm_device *dev, uint8_t reg)
++{
++ nv_write_ptv(dev, NV_PTV_TV_INDEX, reg);
++ return nv_read_ptv(dev, NV_PTV_TV_DATA);
++}
++
++#define nv_load_ptv(dev, state, reg) nv_write_ptv(dev, NV_PTV_OFFSET + 0x##reg, state->ptv_##reg)
++#define nv_save_ptv(dev, state, reg) state->ptv_##reg = nv_read_ptv(dev, NV_PTV_OFFSET + 0x##reg)
++#define nv_load_tv_enc(dev, state, reg) nv_write_tv_enc(dev, 0x##reg, state->tv_enc[0x##reg])
++
++#endif
+diff --git a/drivers/gpu/drm/nouveau/nv17_tv_modes.c b/drivers/gpu/drm/nouveau/nv17_tv_modes.c
+new file mode 100644
+index 0000000..d64683d
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv17_tv_modes.c
+@@ -0,0 +1,583 @@
++/*
++ * Copyright (C) 2009 Francisco Jerez.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "drm_crtc_helper.h"
++#include "nouveau_drv.h"
++#include "nouveau_encoder.h"
++#include "nouveau_crtc.h"
++#include "nouveau_hw.h"
++#include "nv17_tv.h"
++
++char *nv17_tv_norm_names[NUM_TV_NORMS] = {
++ [TV_NORM_PAL] = "PAL",
++ [TV_NORM_PAL_M] = "PAL-M",
++ [TV_NORM_PAL_N] = "PAL-N",
++ [TV_NORM_PAL_NC] = "PAL-Nc",
++ [TV_NORM_NTSC_M] = "NTSC-M",
++ [TV_NORM_NTSC_J] = "NTSC-J",
++ [TV_NORM_HD480I] = "hd480i",
++ [TV_NORM_HD480P] = "hd480p",
++ [TV_NORM_HD576I] = "hd576i",
++ [TV_NORM_HD576P] = "hd576p",
++ [TV_NORM_HD720P] = "hd720p",
++ [TV_NORM_HD1080I] = "hd1080i"
++};
++
++/* TV standard specific parameters */
++
++struct nv17_tv_norm_params nv17_tv_norms[NUM_TV_NORMS] = {
++ [TV_NORM_PAL] = { TV_ENC_MODE, {
++ .tv_enc_mode = { 720, 576, 50000, {
++ 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18,
++ 0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3,
++ 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
++ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3,
++ 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5,
++ 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
++ 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
++ 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0
++ } } } },
++
++ [TV_NORM_PAL_M] = { TV_ENC_MODE, {
++ .tv_enc_mode = { 720, 480, 59940, {
++ 0x21, 0xe6, 0xef, 0xe3, 0x0, 0x0, 0xb, 0x18,
++ 0x7e, 0x44, 0x76, 0x32, 0x25, 0x0, 0x3c, 0x0,
++ 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
++ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
++ 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5,
++ 0x0, 0x18, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
++ 0x0, 0xb4, 0x0, 0x15, 0x40, 0x10, 0x0, 0x9c,
++ 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
++ } } } },
++
++ [TV_NORM_PAL_N] = { TV_ENC_MODE, {
++ .tv_enc_mode = { 720, 576, 50000, {
++ 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18,
++ 0x7e, 0x40, 0x8a, 0x32, 0x25, 0x0, 0x3c, 0x0,
++ 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
++ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
++ 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5,
++ 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
++ 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
++ 0xbd, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
++ } } } },
++
++ [TV_NORM_PAL_NC] = { TV_ENC_MODE, {
++ .tv_enc_mode = { 720, 576, 50000, {
++ 0x21, 0xf6, 0x94, 0x46, 0x0, 0x0, 0xb, 0x18,
++ 0x7e, 0x44, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3,
++ 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
++ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3,
++ 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5,
++ 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
++ 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
++ 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0
++ } } } },
++
++ [TV_NORM_NTSC_M] = { TV_ENC_MODE, {
++ .tv_enc_mode = { 720, 480, 59940, {
++ 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18,
++ 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x3c, 0x0,
++ 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
++ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
++ 0xc5, 0x4, 0xc5, 0x1, 0x2, 0x0, 0xa, 0x5,
++ 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
++ 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0x9c,
++ 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
++ } } } },
++
++ [TV_NORM_NTSC_J] = { TV_ENC_MODE, {
++ .tv_enc_mode = { 720, 480, 59940, {
++ 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18,
++ 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0,
++ 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
++ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
++ 0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5,
++ 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
++ 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4,
++ 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
++ } } } },
++
++ [TV_NORM_HD480I] = { TV_ENC_MODE, {
++ .tv_enc_mode = { 720, 480, 59940, {
++ 0x21, 0xf0, 0x7c, 0x1f, 0x0, 0x0, 0xb, 0x18,
++ 0x7e, 0x44, 0x76, 0x48, 0x0, 0x0, 0x32, 0x0,
++ 0x3c, 0x0, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x83,
++ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x1,
++ 0xcf, 0x4, 0xcf, 0x1, 0x2, 0x0, 0xa, 0x5,
++ 0x0, 0x16, 0xff, 0x3, 0x20, 0xf, 0x78, 0x0,
++ 0x0, 0xb4, 0x0, 0x15, 0x4, 0x10, 0x0, 0xa4,
++ 0xc8, 0x15, 0x5, 0x15, 0x3c, 0x0, 0x0, 0x0
++ } } } },
++
++ [TV_NORM_HD576I] = { TV_ENC_MODE, {
++ .tv_enc_mode = { 720, 576, 50000, {
++ 0x2a, 0x9, 0x8a, 0xcb, 0x0, 0x0, 0xb, 0x18,
++ 0x7e, 0x40, 0x8a, 0x35, 0x27, 0x0, 0x34, 0x3,
++ 0x3e, 0x3, 0x17, 0x21, 0x1b, 0x1b, 0x24, 0x9c,
++ 0x1, 0x0, 0xf, 0xf, 0x60, 0x5, 0xd3, 0x3,
++ 0xd3, 0x4, 0xd4, 0x1, 0x2, 0x0, 0xa, 0x5,
++ 0x0, 0x1a, 0xff, 0x3, 0x18, 0xf, 0x78, 0x0,
++ 0x0, 0xb4, 0x0, 0x15, 0x49, 0x10, 0x0, 0x9b,
++ 0xbd, 0x15, 0x5, 0x15, 0x3e, 0x3, 0x0, 0x0
++ } } } },
++
++
++ [TV_NORM_HD480P] = { CTV_ENC_MODE, {
++ .ctv_enc_mode = {
++ .mode = { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 27000,
++ 720, 735, 743, 858, 0, 480, 490, 494, 525, 0,
++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ .ctv_regs = { 0x3540000, 0x0, 0x0, 0x314,
++ 0x354003a, 0x40000, 0x6f0344, 0x18100000,
++ 0x10160004, 0x10060005, 0x1006000c, 0x10060020,
++ 0x10060021, 0x140e0022, 0x10060202, 0x1802020a,
++ 0x1810020b, 0x10000fff, 0x10000fff, 0x10000fff,
++ 0x10000fff, 0x10000fff, 0x10000fff, 0x70,
++ 0x3ff0000, 0x57, 0x2e001e, 0x258012c,
++ 0xa0aa04ec, 0x30, 0x80960019, 0x12c0300,
++ 0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400
++ } } } },
++
++ [TV_NORM_HD576P] = { CTV_ENC_MODE, {
++ .ctv_enc_mode = {
++ .mode = { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 27000,
++ 720, 730, 738, 864, 0, 576, 581, 585, 625, 0,
++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ .ctv_regs = { 0x3540000, 0x0, 0x0, 0x314,
++ 0x354003a, 0x40000, 0x6f0344, 0x18100000,
++ 0x10060001, 0x10060009, 0x10060026, 0x10060027,
++ 0x140e0028, 0x10060268, 0x1810026d, 0x10000fff,
++ 0x10000fff, 0x10000fff, 0x10000fff, 0x10000fff,
++ 0x10000fff, 0x10000fff, 0x10000fff, 0x69,
++ 0x3ff0000, 0x57, 0x2e001e, 0x258012c,
++ 0xa0aa04ec, 0x30, 0x80960019, 0x12c0300,
++ 0x2019, 0x600, 0x32060019, 0x0, 0x0, 0x400
++ } } } },
++
++ [TV_NORM_HD720P] = { CTV_ENC_MODE, {
++ .ctv_enc_mode = {
++ .mode = { DRM_MODE("1280x720", DRM_MODE_TYPE_DRIVER, 74250,
++ 1280, 1349, 1357, 1650, 0, 720, 725, 730, 750, 0,
++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ .ctv_regs = { 0x1260394, 0x0, 0x0, 0x622,
++ 0x66b0021, 0x6004a, 0x1210626, 0x8170000,
++ 0x70004, 0x70016, 0x70017, 0x40f0018,
++ 0x702e8, 0x81702ed, 0xfff, 0xfff,
++ 0xfff, 0xfff, 0xfff, 0xfff,
++ 0xfff, 0xfff, 0xfff, 0x0,
++ 0x2e40001, 0x58, 0x2e001e, 0x258012c,
++ 0xa0aa04ec, 0x30, 0x810c0039, 0x12c0300,
++ 0xc0002039, 0x600, 0x32060039, 0x0, 0x0, 0x0
++ } } } },
++
++ [TV_NORM_HD1080I] = { CTV_ENC_MODE, {
++ .ctv_enc_mode = {
++ .mode = { DRM_MODE("1920x1080", DRM_MODE_TYPE_DRIVER, 74250,
++ 1920, 1961, 2049, 2200, 0, 1080, 1084, 1088, 1125, 0,
++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC
++ | DRM_MODE_FLAG_INTERLACE) },
++ .ctv_regs = { 0xac0420, 0x44c0478, 0x4a4, 0x4fc0868,
++ 0x8940028, 0x60054, 0xe80870, 0xbf70000,
++ 0xbc70004, 0x70005, 0x70012, 0x70013,
++ 0x40f0014, 0x70230, 0xbf70232, 0xbf70233,
++ 0x1c70237, 0x70238, 0x70244, 0x70245,
++ 0x40f0246, 0x70462, 0x1f70464, 0x0,
++ 0x2e40001, 0x58, 0x2e001e, 0x258012c,
++ 0xa0aa04ec, 0x30, 0x815f004c, 0x12c0300,
++ 0xc000204c, 0x600, 0x3206004c, 0x0, 0x0, 0x0
++ } } } }
++};
++
++/*
++ * The following is some guesswork on how the TV encoder flicker
++ * filter/rescaler works:
++ *
++ * It seems to use some sort of resampling filter, it is controlled
++ * through the registers at NV_PTV_HFILTER and NV_PTV_VFILTER, they
++ * control the horizontal and vertical stage respectively, there is
++ * also NV_PTV_HFILTER2 the blob fills identically to NV_PTV_HFILTER,
++ * but they seem to do nothing. A rough guess might be that they could
++ * be used to independently control the filtering of each interlaced
++ * field, but I don't know how they are enabled. The whole filtering
++ * process seems to be disabled with bits 26:27 of PTV_200, but we
++ * aren't doing that.
++ *
++ * The layout of both register sets is the same:
++ *
++ * A: [BASE+0x18]...[BASE+0x0] [BASE+0x58]..[BASE+0x40]
++ * B: [BASE+0x34]...[BASE+0x1c] [BASE+0x74]..[BASE+0x5c]
++ *
++ * Each coefficient is stored in bits [31],[15:9] in two's complement
++ * format. They seem to be some kind of weights used in a low-pass
++ * filter. Both A and B coefficients are applied to the 14 nearest
++ * samples on each side (Listed from nearest to furthermost. They
++ * roughly cover 2 framebuffer pixels on each side). They are
++ * probably multiplied with some more hardwired weights before being
++ * used: B-coefficients are applied the same on both sides,
++ * A-coefficients are inverted before being applied to the opposite
++ * side.
++ *
++ * After all the hassle, I got the following formula by empirical
++ * means...
++ */
++
++#define calc_overscan(o) interpolate(0x100, 0xe1, 0xc1, o)
++
++#define id1 (1LL << 8)
++#define id2 (1LL << 16)
++#define id3 (1LL << 24)
++#define id4 (1LL << 32)
++#define id5 (1LL << 48)
++
++static struct filter_params{
++ int64_t k1;
++ int64_t ki;
++ int64_t ki2;
++ int64_t ki3;
++ int64_t kr;
++ int64_t kir;
++ int64_t ki2r;
++ int64_t ki3r;
++ int64_t kf;
++ int64_t kif;
++ int64_t ki2f;
++ int64_t ki3f;
++ int64_t krf;
++ int64_t kirf;
++ int64_t ki2rf;
++ int64_t ki3rf;
++} fparams[2][4] = {
++ /* Horizontal filter parameters */
++ {
++ {64.311690 * id5, -39.516924 * id5, 6.586143 * id5, 0.000002 * id5,
++ 0.051285 * id4, 26.168746 * id4, -4.361449 * id4, -0.000001 * id4,
++ 9.308169 * id3, 78.180965 * id3, -13.030158 * id3, -0.000001 * id3,
++ -8.801540 * id1, -46.572890 * id1, 7.762145 * id1, -0.000000 * id1},
++ {-44.565569 * id5, -68.081246 * id5, 39.812074 * id5, -4.009316 * id5,
++ 29.832207 * id4, 50.047322 * id4, -25.380017 * id4, 2.546422 * id4,
++ 104.605622 * id3, 141.908641 * id3, -74.322319 * id3, 7.484316 * id3,
++ -37.081621 * id1, -90.397510 * id1, 42.784229 * id1, -4.289952 * id1},
++ {-56.793244 * id5, 31.153584 * id5, -5.192247 * id5, -0.000003 * id5,
++ 33.541131 * id4, -34.149302 * id4, 5.691537 * id4, 0.000002 * id4,
++ 87.196610 * id3, -88.995169 * id3, 14.832456 * id3, 0.000012 * id3,
++ 17.288138 * id1, 71.864786 * id1, -11.977408 * id1, -0.000009 * id1},
++ {51.787796 * id5, 21.211771 * id5, -18.993730 * id5, 1.853310 * id5,
++ -41.470726 * id4, -17.775823 * id4, 13.057821 * id4, -1.15823 * id4,
++ -154.235673 * id3, -44.878641 * id3, 40.656077 * id3, -3.695595 * id3,
++ 112.201065 * id1, 39.992155 * id1, -25.155714 * id1, 2.113984 * id1},
++ },
++
++ /* Vertical filter parameters */
++ {
++ {67.601979 * id5, 0.428319 * id5, -0.071318 * id5, -0.000012 * id5,
++ -3.402339 * id4, 0.000209 * id4, -0.000092 * id4, 0.000010 * id4,
++ -9.180996 * id3, 6.111270 * id3, -1.024457 * id3, 0.001043 * id3,
++ 6.060315 * id1, -0.017425 * id1, 0.007830 * id1, -0.000869 * id1},
++ {6.755647 * id5, 5.841348 * id5, 1.469734 * id5, -0.149656 * id5,
++ 8.293120 * id4, -1.192888 * id4, -0.947652 * id4, 0.094507 * id4,
++ 37.526655 * id3, 10.257875 * id3, -10.823275 * id3, 1.081497 * id3,
++ -2.361928 * id1, -2.059432 * id1, 1.840671 * id1, -0.168100 * id1},
++ {-14.780391 * id5, -16.042148 * id5, 2.673692 * id5, -0.000000 * id5,
++ 39.541978 * id4, 5.680053 * id4, -0.946676 * id4, 0.000000 * id4,
++ 152.994486 * id3, 12.625439 * id3, -2.119579 * id3, 0.002708 * id3,
++ -38.125089 * id1, -0.855880 * id1, 0.155359 * id1, -0.002245 * id1},
++ {-27.476193 * id5, -1.454976 * id5, 1.286557 * id5, 0.025346 * id5,
++ 20.687300 * id4, 3.014003 * id4, -0.557786 * id4, -0.01311 * id4,
++ 60.008737 * id3, -0.738273 * id3, 5.408217 * id3, -0.796798 * id3,
++ -17.296835 * id1, 4.438577 * id1, -2.809420 * id1, 0.385491 * id1},
++ }
++};
++
++static void tv_setup_filter(struct drm_encoder *encoder)
++{
++ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
++ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
++ struct drm_display_mode *mode = &encoder->crtc->mode;
++ uint32_t (*filters[])[4][7] = {&tv_enc->state.hfilter,
++ &tv_enc->state.vfilter};
++ int i, j, k;
++ int32_t overscan = calc_overscan(tv_enc->overscan);
++ int64_t flicker = (tv_enc->flicker - 50) * (id3 / 100);
++ uint64_t rs[] = {mode->hdisplay * id3,
++ mode->vdisplay * id3};
++
++ do_div(rs[0], overscan * tv_norm->tv_enc_mode.hdisplay);
++ do_div(rs[1], overscan * tv_norm->tv_enc_mode.vdisplay);
++
++ for (k = 0; k < 2; k++) {
++ rs[k] = max((int64_t)rs[k], id2);
++
++ for (j = 0; j < 4; j++) {
++ struct filter_params *p = &fparams[k][j];
++
++ for (i = 0; i < 7; i++) {
++ int64_t c = (p->k1 + p->ki*i + p->ki2*i*i + p->ki3*i*i*i)
++ + (p->kr + p->kir*i + p->ki2r*i*i + p->ki3r*i*i*i)*rs[k]
++ + (p->kf + p->kif*i + p->ki2f*i*i + p->ki3f*i*i*i)*flicker
++ + (p->krf + p->kirf*i + p->ki2rf*i*i + p->ki3rf*i*i*i)*flicker*rs[k];
++
++ (*filters[k])[j][i] = (c + id5/2) >> 39 & (0x1 << 31 | 0x7f << 9);
++ }
++ }
++ }
++}
++
++/* Hardware state saving/restoring */
++
++static void tv_save_filter(struct drm_device *dev, uint32_t base, uint32_t regs[4][7])
++{
++ int i, j;
++ uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c };
++
++ for (i = 0; i < 4; i++) {
++ for (j = 0; j < 7; j++)
++ regs[i][j] = nv_read_ptv(dev, offsets[i]+4*j);
++ }
++}
++
++static void tv_load_filter(struct drm_device *dev, uint32_t base, uint32_t regs[4][7])
++{
++ int i, j;
++ uint32_t offsets[] = { base, base + 0x1c, base + 0x40, base + 0x5c };
++
++ for (i = 0; i < 4; i++) {
++ for (j = 0; j < 7; j++)
++ nv_write_ptv(dev, offsets[i]+4*j, regs[i][j]);
++ }
++}
++
++void nv17_tv_state_save(struct drm_device *dev, struct nv17_tv_state *state)
++{
++ int i;
++
++ for (i = 0; i < 0x40; i++)
++ state->tv_enc[i] = nv_read_tv_enc(dev, i);
++
++ tv_save_filter(dev, NV_PTV_HFILTER, state->hfilter);
++ tv_save_filter(dev, NV_PTV_HFILTER2, state->hfilter2);
++ tv_save_filter(dev, NV_PTV_VFILTER, state->vfilter);
++
++ nv_save_ptv(dev, state, 200);
++ nv_save_ptv(dev, state, 204);
++ nv_save_ptv(dev, state, 208);
++ nv_save_ptv(dev, state, 20c);
++ nv_save_ptv(dev, state, 304);
++ nv_save_ptv(dev, state, 500);
++ nv_save_ptv(dev, state, 504);
++ nv_save_ptv(dev, state, 508);
++ nv_save_ptv(dev, state, 600);
++ nv_save_ptv(dev, state, 604);
++ nv_save_ptv(dev, state, 608);
++ nv_save_ptv(dev, state, 60c);
++ nv_save_ptv(dev, state, 610);
++ nv_save_ptv(dev, state, 614);
++}
++
++void nv17_tv_state_load(struct drm_device *dev, struct nv17_tv_state *state)
++{
++ int i;
++
++ for (i = 0; i < 0x40; i++)
++ nv_write_tv_enc(dev, i, state->tv_enc[i]);
++
++ tv_load_filter(dev, NV_PTV_HFILTER, state->hfilter);
++ tv_load_filter(dev, NV_PTV_HFILTER2, state->hfilter2);
++ tv_load_filter(dev, NV_PTV_VFILTER, state->vfilter);
++
++ nv_load_ptv(dev, state, 200);
++ nv_load_ptv(dev, state, 204);
++ nv_load_ptv(dev, state, 208);
++ nv_load_ptv(dev, state, 20c);
++ nv_load_ptv(dev, state, 304);
++ nv_load_ptv(dev, state, 500);
++ nv_load_ptv(dev, state, 504);
++ nv_load_ptv(dev, state, 508);
++ nv_load_ptv(dev, state, 600);
++ nv_load_ptv(dev, state, 604);
++ nv_load_ptv(dev, state, 608);
++ nv_load_ptv(dev, state, 60c);
++ nv_load_ptv(dev, state, 610);
++ nv_load_ptv(dev, state, 614);
++
++ /* This is required for some settings to kick in. */
++ nv_write_tv_enc(dev, 0x3e, 1);
++ nv_write_tv_enc(dev, 0x3e, 0);
++}
++
++/* Timings similar to the ones the blob sets */
++
++struct drm_display_mode nv17_tv_modes[] = {
++ { DRM_MODE("320x200", DRM_MODE_TYPE_DRIVER, 0,
++ 320, 344, 392, 560, 0, 200, 200, 202, 220, 0,
++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC
++ | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
++ { DRM_MODE("320x240", DRM_MODE_TYPE_DRIVER, 0,
++ 320, 344, 392, 560, 0, 240, 240, 246, 263, 0,
++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC
++ | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
++ { DRM_MODE("400x300", DRM_MODE_TYPE_DRIVER, 0,
++ 400, 432, 496, 640, 0, 300, 300, 303, 314, 0,
++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC
++ | DRM_MODE_FLAG_DBLSCAN | DRM_MODE_FLAG_CLKDIV2) },
++ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 0,
++ 640, 672, 768, 880, 0, 480, 480, 492, 525, 0,
++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
++ { DRM_MODE("720x480", DRM_MODE_TYPE_DRIVER, 0,
++ 720, 752, 872, 960, 0, 480, 480, 493, 525, 0,
++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
++ { DRM_MODE("720x576", DRM_MODE_TYPE_DRIVER, 0,
++ 720, 776, 856, 960, 0, 576, 576, 588, 597, 0,
++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
++ { DRM_MODE("800x600", DRM_MODE_TYPE_DRIVER, 0,
++ 800, 840, 920, 1040, 0, 600, 600, 604, 618, 0,
++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 0,
++ 1024, 1064, 1200, 1344, 0, 768, 768, 777, 806, 0,
++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
++ {}
++};
++
++void nv17_tv_update_properties(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
++ struct nv17_tv_state *regs = &tv_enc->state;
++ struct nv17_tv_norm_params *tv_norm = get_tv_norm(encoder);
++ int subconnector = tv_enc->select_subconnector ?
++ tv_enc->select_subconnector :
++ tv_enc->subconnector;
++
++ switch (subconnector) {
++ case DRM_MODE_SUBCONNECTOR_Composite:
++ {
++ regs->ptv_204 = 0x2;
++
++ /* The composite connector may be found on either pin. */
++ if (tv_enc->pin_mask & 0x4)
++ regs->ptv_204 |= 0x010000;
++ else if (tv_enc->pin_mask & 0x2)
++ regs->ptv_204 |= 0x100000;
++ else
++ regs->ptv_204 |= 0x110000;
++
++ regs->tv_enc[0x7] = 0x10;
++ break;
++ }
++ case DRM_MODE_SUBCONNECTOR_SVIDEO:
++ regs->ptv_204 = 0x11012;
++ regs->tv_enc[0x7] = 0x18;
++ break;
++
++ case DRM_MODE_SUBCONNECTOR_Component:
++ regs->ptv_204 = 0x111333;
++ regs->tv_enc[0x7] = 0x14;
++ break;
++
++ case DRM_MODE_SUBCONNECTOR_SCART:
++ regs->ptv_204 = 0x111012;
++ regs->tv_enc[0x7] = 0x18;
++ break;
++ }
++
++ regs->tv_enc[0x20] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x20], 255,
++ tv_enc->saturation);
++ regs->tv_enc[0x22] = interpolate(0, tv_norm->tv_enc_mode.tv_enc[0x22], 255,
++ tv_enc->saturation);
++ regs->tv_enc[0x25] = tv_enc->hue * 255 / 100;
++
++ nv_load_ptv(dev, regs, 204);
++ nv_load_tv_enc(dev, regs, 7);
++ nv_load_tv_enc(dev, regs, 20);
++ nv_load_tv_enc(dev, regs, 22);
++ nv_load_tv_enc(dev, regs, 25);
++}
++
++void nv17_tv_update_rescaler(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
++ struct nv17_tv_state *regs = &tv_enc->state;
++
++ regs->ptv_208 = 0x40 | (calc_overscan(tv_enc->overscan) << 8);
++
++ tv_setup_filter(encoder);
++
++ nv_load_ptv(dev, regs, 208);
++ tv_load_filter(dev, NV_PTV_HFILTER, regs->hfilter);
++ tv_load_filter(dev, NV_PTV_HFILTER2, regs->hfilter2);
++ tv_load_filter(dev, NV_PTV_VFILTER, regs->vfilter);
++}
++
++void nv17_ctv_update_rescaler(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv17_tv_encoder *tv_enc = to_tv_enc(encoder);
++ int head = nouveau_crtc(encoder->crtc)->index;
++ struct nv04_crtc_reg *regs = &dev_priv->mode_reg.crtc_reg[head];
++ struct drm_display_mode *crtc_mode = &encoder->crtc->mode;
++ struct drm_display_mode *output_mode = &get_tv_norm(encoder)->ctv_enc_mode.mode;
++ int overscan, hmargin, vmargin, hratio, vratio;
++
++ /* The rescaler doesn't do the right thing for interlaced modes. */
++ if (output_mode->flags & DRM_MODE_FLAG_INTERLACE)
++ overscan = 100;
++ else
++ overscan = tv_enc->overscan;
++
++ hmargin = (output_mode->hdisplay - crtc_mode->hdisplay) / 2;
++ vmargin = (output_mode->vdisplay - crtc_mode->vdisplay) / 2;
++
++ hmargin = interpolate(0, min(hmargin, output_mode->hdisplay/20), hmargin,
++ overscan);
++ vmargin = interpolate(0, min(vmargin, output_mode->vdisplay/20), vmargin,
++ overscan);
++
++ hratio = crtc_mode->hdisplay * 0x800 / (output_mode->hdisplay - 2*hmargin);
++ vratio = crtc_mode->vdisplay * 0x800 / (output_mode->vdisplay - 2*vmargin) & ~3;
++
++ regs->fp_horiz_regs[FP_VALID_START] = hmargin;
++ regs->fp_horiz_regs[FP_VALID_END] = output_mode->hdisplay - hmargin - 1;
++ regs->fp_vert_regs[FP_VALID_START] = vmargin;
++ regs->fp_vert_regs[FP_VALID_END] = output_mode->vdisplay - vmargin - 1;
++
++ regs->fp_debug_1 = NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE |
++ XLATE(vratio, 0, NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE) |
++ NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE |
++ XLATE(hratio, 0, NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE);
++
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_START,
++ regs->fp_horiz_regs[FP_VALID_START]);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_HVALID_END,
++ regs->fp_horiz_regs[FP_VALID_END]);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_START,
++ regs->fp_vert_regs[FP_VALID_START]);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_VVALID_END,
++ regs->fp_vert_regs[FP_VALID_END]);
++ NVWriteRAMDAC(dev, head, NV_PRAMDAC_FP_DEBUG_1, regs->fp_debug_1);
++}
+diff --git a/drivers/gpu/drm/nouveau/nv20_graph.c b/drivers/gpu/drm/nouveau/nv20_graph.c
+new file mode 100644
+index 0000000..d6fc0a8
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv20_graph.c
+@@ -0,0 +1,775 @@
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++#include "nouveau_drm.h"
++
++/*
++ * NV20
++ * -----
++ * There are 3 families :
++ * NV20 is 0x10de:0x020*
++ * NV25/28 is 0x10de:0x025* / 0x10de:0x028*
++ * NV2A is 0x10de:0x02A0
++ *
++ * NV30
++ * -----
++ * There are 3 families :
++ * NV30/31 is 0x10de:0x030* / 0x10de:0x031*
++ * NV34 is 0x10de:0x032*
++ * NV35/36 is 0x10de:0x033* / 0x10de:0x034*
++ *
++ * Not seen in the wild, no dumps (probably NV35) :
++ * NV37 is 0x10de:0x00fc, 0x10de:0x00fd
++ * NV38 is 0x10de:0x0333, 0x10de:0x00fe
++ *
++ */
++
++#define NV20_GRCTX_SIZE (3580*4)
++#define NV25_GRCTX_SIZE (3529*4)
++#define NV2A_GRCTX_SIZE (3500*4)
++
++#define NV30_31_GRCTX_SIZE (24392)
++#define NV34_GRCTX_SIZE (18140)
++#define NV35_36_GRCTX_SIZE (22396)
++
++static void
++nv20_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
++{
++ int i;
++
++ nv_wo32(dev, ctx, 0x033c/4, 0xffff0000);
++ nv_wo32(dev, ctx, 0x03a0/4, 0x0fff0000);
++ nv_wo32(dev, ctx, 0x03a4/4, 0x0fff0000);
++ nv_wo32(dev, ctx, 0x047c/4, 0x00000101);
++ nv_wo32(dev, ctx, 0x0490/4, 0x00000111);
++ nv_wo32(dev, ctx, 0x04a8/4, 0x44400000);
++ for (i = 0x04d4; i <= 0x04e0; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00030303);
++ for (i = 0x04f4; i <= 0x0500; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00080000);
++ for (i = 0x050c; i <= 0x0518; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x01012000);
++ for (i = 0x051c; i <= 0x0528; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x000105b8);
++ for (i = 0x052c; i <= 0x0538; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00080008);
++ for (i = 0x055c; i <= 0x0598; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x07ff0000);
++ nv_wo32(dev, ctx, 0x05a4/4, 0x4b7fffff);
++ nv_wo32(dev, ctx, 0x05fc/4, 0x00000001);
++ nv_wo32(dev, ctx, 0x0604/4, 0x00004000);
++ nv_wo32(dev, ctx, 0x0610/4, 0x00000001);
++ nv_wo32(dev, ctx, 0x0618/4, 0x00040000);
++ nv_wo32(dev, ctx, 0x061c/4, 0x00010000);
++ for (i = 0x1c1c; i <= 0x248c; i += 16) {
++ nv_wo32(dev, ctx, (i + 0)/4, 0x10700ff9);
++ nv_wo32(dev, ctx, (i + 4)/4, 0x0436086c);
++ nv_wo32(dev, ctx, (i + 8)/4, 0x000c001b);
++ }
++ nv_wo32(dev, ctx, 0x281c/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x2830/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x285c/4, 0x40000000);
++ nv_wo32(dev, ctx, 0x2860/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x2864/4, 0x3f000000);
++ nv_wo32(dev, ctx, 0x286c/4, 0x40000000);
++ nv_wo32(dev, ctx, 0x2870/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x2878/4, 0xbf800000);
++ nv_wo32(dev, ctx, 0x2880/4, 0xbf800000);
++ nv_wo32(dev, ctx, 0x34a4/4, 0x000fe000);
++ nv_wo32(dev, ctx, 0x3530/4, 0x000003f8);
++ nv_wo32(dev, ctx, 0x3540/4, 0x002fe000);
++ for (i = 0x355c; i <= 0x3578; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x001c527c);
++}
++
++static void
++nv25_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
++{
++ int i;
++
++ nv_wo32(dev, ctx, 0x035c/4, 0xffff0000);
++ nv_wo32(dev, ctx, 0x03c0/4, 0x0fff0000);
++ nv_wo32(dev, ctx, 0x03c4/4, 0x0fff0000);
++ nv_wo32(dev, ctx, 0x049c/4, 0x00000101);
++ nv_wo32(dev, ctx, 0x04b0/4, 0x00000111);
++ nv_wo32(dev, ctx, 0x04c8/4, 0x00000080);
++ nv_wo32(dev, ctx, 0x04cc/4, 0xffff0000);
++ nv_wo32(dev, ctx, 0x04d0/4, 0x00000001);
++ nv_wo32(dev, ctx, 0x04e4/4, 0x44400000);
++ nv_wo32(dev, ctx, 0x04fc/4, 0x4b800000);
++ for (i = 0x0510; i <= 0x051c; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00030303);
++ for (i = 0x0530; i <= 0x053c; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00080000);
++ for (i = 0x0548; i <= 0x0554; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x01012000);
++ for (i = 0x0558; i <= 0x0564; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x000105b8);
++ for (i = 0x0568; i <= 0x0574; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00080008);
++ for (i = 0x0598; i <= 0x05d4; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x07ff0000);
++ nv_wo32(dev, ctx, 0x05e0/4, 0x4b7fffff);
++ nv_wo32(dev, ctx, 0x0620/4, 0x00000080);
++ nv_wo32(dev, ctx, 0x0624/4, 0x30201000);
++ nv_wo32(dev, ctx, 0x0628/4, 0x70605040);
++ nv_wo32(dev, ctx, 0x062c/4, 0xb0a09080);
++ nv_wo32(dev, ctx, 0x0630/4, 0xf0e0d0c0);
++ nv_wo32(dev, ctx, 0x0664/4, 0x00000001);
++ nv_wo32(dev, ctx, 0x066c/4, 0x00004000);
++ nv_wo32(dev, ctx, 0x0678/4, 0x00000001);
++ nv_wo32(dev, ctx, 0x0680/4, 0x00040000);
++ nv_wo32(dev, ctx, 0x0684/4, 0x00010000);
++ for (i = 0x1b04; i <= 0x2374; i += 16) {
++ nv_wo32(dev, ctx, (i + 0)/4, 0x10700ff9);
++ nv_wo32(dev, ctx, (i + 4)/4, 0x0436086c);
++ nv_wo32(dev, ctx, (i + 8)/4, 0x000c001b);
++ }
++ nv_wo32(dev, ctx, 0x2704/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x2718/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x2744/4, 0x40000000);
++ nv_wo32(dev, ctx, 0x2748/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x274c/4, 0x3f000000);
++ nv_wo32(dev, ctx, 0x2754/4, 0x40000000);
++ nv_wo32(dev, ctx, 0x2758/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x2760/4, 0xbf800000);
++ nv_wo32(dev, ctx, 0x2768/4, 0xbf800000);
++ nv_wo32(dev, ctx, 0x308c/4, 0x000fe000);
++ nv_wo32(dev, ctx, 0x3108/4, 0x000003f8);
++ nv_wo32(dev, ctx, 0x3468/4, 0x002fe000);
++ for (i = 0x3484; i <= 0x34a0; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x001c527c);
++}
++
++static void
++nv2a_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
++{
++ int i;
++
++ nv_wo32(dev, ctx, 0x033c/4, 0xffff0000);
++ nv_wo32(dev, ctx, 0x03a0/4, 0x0fff0000);
++ nv_wo32(dev, ctx, 0x03a4/4, 0x0fff0000);
++ nv_wo32(dev, ctx, 0x047c/4, 0x00000101);
++ nv_wo32(dev, ctx, 0x0490/4, 0x00000111);
++ nv_wo32(dev, ctx, 0x04a8/4, 0x44400000);
++ for (i = 0x04d4; i <= 0x04e0; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00030303);
++ for (i = 0x04f4; i <= 0x0500; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00080000);
++ for (i = 0x050c; i <= 0x0518; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x01012000);
++ for (i = 0x051c; i <= 0x0528; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x000105b8);
++ for (i = 0x052c; i <= 0x0538; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00080008);
++ for (i = 0x055c; i <= 0x0598; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x07ff0000);
++ nv_wo32(dev, ctx, 0x05a4/4, 0x4b7fffff);
++ nv_wo32(dev, ctx, 0x05fc/4, 0x00000001);
++ nv_wo32(dev, ctx, 0x0604/4, 0x00004000);
++ nv_wo32(dev, ctx, 0x0610/4, 0x00000001);
++ nv_wo32(dev, ctx, 0x0618/4, 0x00040000);
++ nv_wo32(dev, ctx, 0x061c/4, 0x00010000);
++ for (i = 0x1a9c; i <= 0x22fc; i += 16) { /*XXX: check!! */
++ nv_wo32(dev, ctx, (i + 0)/4, 0x10700ff9);
++ nv_wo32(dev, ctx, (i + 4)/4, 0x0436086c);
++ nv_wo32(dev, ctx, (i + 8)/4, 0x000c001b);
++ }
++ nv_wo32(dev, ctx, 0x269c/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x26b0/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x26dc/4, 0x40000000);
++ nv_wo32(dev, ctx, 0x26e0/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x26e4/4, 0x3f000000);
++ nv_wo32(dev, ctx, 0x26ec/4, 0x40000000);
++ nv_wo32(dev, ctx, 0x26f0/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x26f8/4, 0xbf800000);
++ nv_wo32(dev, ctx, 0x2700/4, 0xbf800000);
++ nv_wo32(dev, ctx, 0x3024/4, 0x000fe000);
++ nv_wo32(dev, ctx, 0x30a0/4, 0x000003f8);
++ nv_wo32(dev, ctx, 0x33fc/4, 0x002fe000);
++ for (i = 0x341c; i <= 0x3438; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x001c527c);
++}
++
++static void
++nv30_31_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
++{
++ int i;
++
++ nv_wo32(dev, ctx, 0x0410/4, 0x00000101);
++ nv_wo32(dev, ctx, 0x0424/4, 0x00000111);
++ nv_wo32(dev, ctx, 0x0428/4, 0x00000060);
++ nv_wo32(dev, ctx, 0x0444/4, 0x00000080);
++ nv_wo32(dev, ctx, 0x0448/4, 0xffff0000);
++ nv_wo32(dev, ctx, 0x044c/4, 0x00000001);
++ nv_wo32(dev, ctx, 0x0460/4, 0x44400000);
++ nv_wo32(dev, ctx, 0x048c/4, 0xffff0000);
++ for (i = 0x04e0; i < 0x04e8; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x0fff0000);
++ nv_wo32(dev, ctx, 0x04ec/4, 0x00011100);
++ for (i = 0x0508; i < 0x0548; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x07ff0000);
++ nv_wo32(dev, ctx, 0x0550/4, 0x4b7fffff);
++ nv_wo32(dev, ctx, 0x058c/4, 0x00000080);
++ nv_wo32(dev, ctx, 0x0590/4, 0x30201000);
++ nv_wo32(dev, ctx, 0x0594/4, 0x70605040);
++ nv_wo32(dev, ctx, 0x0598/4, 0xb8a89888);
++ nv_wo32(dev, ctx, 0x059c/4, 0xf8e8d8c8);
++ nv_wo32(dev, ctx, 0x05b0/4, 0xb0000000);
++ for (i = 0x0600; i < 0x0640; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00010588);
++ for (i = 0x0640; i < 0x0680; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00030303);
++ for (i = 0x06c0; i < 0x0700; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x0008aae4);
++ for (i = 0x0700; i < 0x0740; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x01012000);
++ for (i = 0x0740; i < 0x0780; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00080008);
++ nv_wo32(dev, ctx, 0x085c/4, 0x00040000);
++ nv_wo32(dev, ctx, 0x0860/4, 0x00010000);
++ for (i = 0x0864; i < 0x0874; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00040004);
++ for (i = 0x1f18; i <= 0x3088 ; i += 16) {
++ nv_wo32(dev, ctx, i/4 + 0, 0x10700ff9);
++ nv_wo32(dev, ctx, i/4 + 1, 0x0436086c);
++ nv_wo32(dev, ctx, i/4 + 2, 0x000c001b);
++ }
++ for (i = 0x30b8; i < 0x30c8; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x0000ffff);
++ nv_wo32(dev, ctx, 0x344c/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x3808/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x381c/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x3848/4, 0x40000000);
++ nv_wo32(dev, ctx, 0x384c/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x3850/4, 0x3f000000);
++ nv_wo32(dev, ctx, 0x3858/4, 0x40000000);
++ nv_wo32(dev, ctx, 0x385c/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x3864/4, 0xbf800000);
++ nv_wo32(dev, ctx, 0x386c/4, 0xbf800000);
++}
++
++static void
++nv34_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
++{
++ int i;
++
++ nv_wo32(dev, ctx, 0x040c/4, 0x01000101);
++ nv_wo32(dev, ctx, 0x0420/4, 0x00000111);
++ nv_wo32(dev, ctx, 0x0424/4, 0x00000060);
++ nv_wo32(dev, ctx, 0x0440/4, 0x00000080);
++ nv_wo32(dev, ctx, 0x0444/4, 0xffff0000);
++ nv_wo32(dev, ctx, 0x0448/4, 0x00000001);
++ nv_wo32(dev, ctx, 0x045c/4, 0x44400000);
++ nv_wo32(dev, ctx, 0x0480/4, 0xffff0000);
++ for (i = 0x04d4; i < 0x04dc; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x0fff0000);
++ nv_wo32(dev, ctx, 0x04e0/4, 0x00011100);
++ for (i = 0x04fc; i < 0x053c; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x07ff0000);
++ nv_wo32(dev, ctx, 0x0544/4, 0x4b7fffff);
++ nv_wo32(dev, ctx, 0x057c/4, 0x00000080);
++ nv_wo32(dev, ctx, 0x0580/4, 0x30201000);
++ nv_wo32(dev, ctx, 0x0584/4, 0x70605040);
++ nv_wo32(dev, ctx, 0x0588/4, 0xb8a89888);
++ nv_wo32(dev, ctx, 0x058c/4, 0xf8e8d8c8);
++ nv_wo32(dev, ctx, 0x05a0/4, 0xb0000000);
++ for (i = 0x05f0; i < 0x0630; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00010588);
++ for (i = 0x0630; i < 0x0670; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00030303);
++ for (i = 0x06b0; i < 0x06f0; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x0008aae4);
++ for (i = 0x06f0; i < 0x0730; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x01012000);
++ for (i = 0x0730; i < 0x0770; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00080008);
++ nv_wo32(dev, ctx, 0x0850/4, 0x00040000);
++ nv_wo32(dev, ctx, 0x0854/4, 0x00010000);
++ for (i = 0x0858; i < 0x0868; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00040004);
++ for (i = 0x15ac; i <= 0x271c ; i += 16) {
++ nv_wo32(dev, ctx, i/4 + 0, 0x10700ff9);
++ nv_wo32(dev, ctx, i/4 + 1, 0x0436086c);
++ nv_wo32(dev, ctx, i/4 + 2, 0x000c001b);
++ }
++ for (i = 0x274c; i < 0x275c; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x0000ffff);
++ nv_wo32(dev, ctx, 0x2ae0/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x2e9c/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x2eb0/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x2edc/4, 0x40000000);
++ nv_wo32(dev, ctx, 0x2ee0/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x2ee4/4, 0x3f000000);
++ nv_wo32(dev, ctx, 0x2eec/4, 0x40000000);
++ nv_wo32(dev, ctx, 0x2ef0/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x2ef8/4, 0xbf800000);
++ nv_wo32(dev, ctx, 0x2f00/4, 0xbf800000);
++}
++
++static void
++nv35_36_graph_context_init(struct drm_device *dev, struct nouveau_gpuobj *ctx)
++{
++ int i;
++
++ nv_wo32(dev, ctx, 0x040c/4, 0x00000101);
++ nv_wo32(dev, ctx, 0x0420/4, 0x00000111);
++ nv_wo32(dev, ctx, 0x0424/4, 0x00000060);
++ nv_wo32(dev, ctx, 0x0440/4, 0x00000080);
++ nv_wo32(dev, ctx, 0x0444/4, 0xffff0000);
++ nv_wo32(dev, ctx, 0x0448/4, 0x00000001);
++ nv_wo32(dev, ctx, 0x045c/4, 0x44400000);
++ nv_wo32(dev, ctx, 0x0488/4, 0xffff0000);
++ for (i = 0x04dc; i < 0x04e4; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x0fff0000);
++ nv_wo32(dev, ctx, 0x04e8/4, 0x00011100);
++ for (i = 0x0504; i < 0x0544; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x07ff0000);
++ nv_wo32(dev, ctx, 0x054c/4, 0x4b7fffff);
++ nv_wo32(dev, ctx, 0x0588/4, 0x00000080);
++ nv_wo32(dev, ctx, 0x058c/4, 0x30201000);
++ nv_wo32(dev, ctx, 0x0590/4, 0x70605040);
++ nv_wo32(dev, ctx, 0x0594/4, 0xb8a89888);
++ nv_wo32(dev, ctx, 0x0598/4, 0xf8e8d8c8);
++ nv_wo32(dev, ctx, 0x05ac/4, 0xb0000000);
++ for (i = 0x0604; i < 0x0644; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00010588);
++ for (i = 0x0644; i < 0x0684; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00030303);
++ for (i = 0x06c4; i < 0x0704; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x0008aae4);
++ for (i = 0x0704; i < 0x0744; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x01012000);
++ for (i = 0x0744; i < 0x0784; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00080008);
++ nv_wo32(dev, ctx, 0x0860/4, 0x00040000);
++ nv_wo32(dev, ctx, 0x0864/4, 0x00010000);
++ for (i = 0x0868; i < 0x0878; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x00040004);
++ for (i = 0x1f1c; i <= 0x308c ; i += 16) {
++ nv_wo32(dev, ctx, i/4 + 0, 0x10700ff9);
++ nv_wo32(dev, ctx, i/4 + 1, 0x0436086c);
++ nv_wo32(dev, ctx, i/4 + 2, 0x000c001b);
++ }
++ for (i = 0x30bc; i < 0x30cc; i += 4)
++ nv_wo32(dev, ctx, i/4, 0x0000ffff);
++ nv_wo32(dev, ctx, 0x3450/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x380c/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x3820/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x384c/4, 0x40000000);
++ nv_wo32(dev, ctx, 0x3850/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x3854/4, 0x3f000000);
++ nv_wo32(dev, ctx, 0x385c/4, 0x40000000);
++ nv_wo32(dev, ctx, 0x3860/4, 0x3f800000);
++ nv_wo32(dev, ctx, 0x3868/4, 0xbf800000);
++ nv_wo32(dev, ctx, 0x3870/4, 0xbf800000);
++}
++
++int
++nv20_graph_create_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ void (*ctx_init)(struct drm_device *, struct nouveau_gpuobj *);
++ unsigned int ctx_size;
++ unsigned int idoffs = 0x28/4;
++ int ret;
++
++ switch (dev_priv->chipset) {
++ case 0x20:
++ ctx_size = NV20_GRCTX_SIZE;
++ ctx_init = nv20_graph_context_init;
++ idoffs = 0;
++ break;
++ case 0x25:
++ case 0x28:
++ ctx_size = NV25_GRCTX_SIZE;
++ ctx_init = nv25_graph_context_init;
++ break;
++ case 0x2a:
++ ctx_size = NV2A_GRCTX_SIZE;
++ ctx_init = nv2a_graph_context_init;
++ idoffs = 0;
++ break;
++ case 0x30:
++ case 0x31:
++ ctx_size = NV30_31_GRCTX_SIZE;
++ ctx_init = nv30_31_graph_context_init;
++ break;
++ case 0x34:
++ ctx_size = NV34_GRCTX_SIZE;
++ ctx_init = nv34_graph_context_init;
++ break;
++ case 0x35:
++ case 0x36:
++ ctx_size = NV35_36_GRCTX_SIZE;
++ ctx_init = nv35_36_graph_context_init;
++ break;
++ default:
++ ctx_size = 0;
++ ctx_init = nv35_36_graph_context_init;
++ NV_ERROR(dev, "Please contact the devs if you want your NV%x"
++ " card to work\n", dev_priv->chipset);
++ return -ENOSYS;
++ break;
++ }
++
++ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, ctx_size, 16,
++ NVOBJ_FLAG_ZERO_ALLOC,
++ &chan->ramin_grctx);
++ if (ret)
++ return ret;
++
++ /* Initialise default context values */
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ ctx_init(dev, chan->ramin_grctx->gpuobj);
++
++ /* nv20: nv_wo32(dev, chan->ramin_grctx->gpuobj, 10, chan->id<<24); */
++ nv_wo32(dev, chan->ramin_grctx->gpuobj, idoffs,
++ (chan->id << 24) | 0x1); /* CTX_USER */
++
++ nv_wo32(dev, dev_priv->ctx_table->gpuobj, chan->id,
++ chan->ramin_grctx->instance >> 4);
++
++ dev_priv->engine.instmem.finish_access(dev);
++ return 0;
++}
++
++void
++nv20_graph_destroy_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ if (chan->ramin_grctx)
++ nouveau_gpuobj_ref_del(dev, &chan->ramin_grctx);
++
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ nv_wo32(dev, dev_priv->ctx_table->gpuobj, chan->id, 0);
++ dev_priv->engine.instmem.finish_access(dev);
++}
++
++int
++nv20_graph_load_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ uint32_t inst;
++
++ if (!chan->ramin_grctx)
++ return -EINVAL;
++ inst = chan->ramin_grctx->instance >> 4;
++
++ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
++ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER,
++ NV20_PGRAPH_CHANNEL_CTX_XFER_LOAD);
++ nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100);
++
++ nouveau_wait_for_idle(dev);
++ return 0;
++}
++
++int
++nv20_graph_unload_context(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
++ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
++ struct nouveau_channel *chan;
++ uint32_t inst, tmp;
++
++ chan = pgraph->channel(dev);
++ if (!chan)
++ return 0;
++ inst = chan->ramin_grctx->instance >> 4;
++
++ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
++ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_XFER,
++ NV20_PGRAPH_CHANNEL_CTX_XFER_SAVE);
++
++ nouveau_wait_for_idle(dev);
++
++ nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000000);
++ tmp = nv_rd32(dev, NV10_PGRAPH_CTX_USER) & 0x00ffffff;
++ tmp |= (pfifo->channels - 1) << 24;
++ nv_wr32(dev, NV10_PGRAPH_CTX_USER, tmp);
++ return 0;
++}
++
++static void
++nv20_graph_rdi(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int i, writecount = 32;
++ uint32_t rdi_index = 0x2c80000;
++
++ if (dev_priv->chipset == 0x20) {
++ rdi_index = 0x3d0000;
++ writecount = 15;
++ }
++
++ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, rdi_index);
++ for (i = 0; i < writecount; i++)
++ nv_wr32(dev, NV10_PGRAPH_RDI_DATA, 0);
++
++ nouveau_wait_for_idle(dev);
++}
++
++void
++nv20_graph_set_region_tiling(struct drm_device *dev, int i, uint32_t addr,
++ uint32_t size, uint32_t pitch)
++{
++ uint32_t limit = max(1u, addr + size) - 1;
++
++ if (pitch)
++ addr |= 1;
++
++ nv_wr32(dev, NV20_PGRAPH_TLIMIT(i), limit);
++ nv_wr32(dev, NV20_PGRAPH_TSIZE(i), pitch);
++ nv_wr32(dev, NV20_PGRAPH_TILE(i), addr);
++
++ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0030 + 4 * i);
++ nv_wr32(dev, NV10_PGRAPH_RDI_DATA, limit);
++ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0050 + 4 * i);
++ nv_wr32(dev, NV10_PGRAPH_RDI_DATA, pitch);
++ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0010 + 4 * i);
++ nv_wr32(dev, NV10_PGRAPH_RDI_DATA, addr);
++}
++
++int
++nv20_graph_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv =
++ (struct drm_nouveau_private *)dev->dev_private;
++ uint32_t tmp, vramsz;
++ int ret, i;
++
++ nv_wr32(dev, NV03_PMC_ENABLE,
++ nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH);
++ nv_wr32(dev, NV03_PMC_ENABLE,
++ nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH);
++
++ if (!dev_priv->ctx_table) {
++ /* Create Context Pointer Table */
++ dev_priv->ctx_table_size = 32 * 4;
++ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0,
++ dev_priv->ctx_table_size, 16,
++ NVOBJ_FLAG_ZERO_ALLOC,
++ &dev_priv->ctx_table);
++ if (ret)
++ return ret;
++ }
++
++ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE,
++ dev_priv->ctx_table->instance >> 4);
++
++ nv20_graph_rdi(dev);
++
++ nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF);
++ nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
++
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF);
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000);
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x00118700);
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xF3CE0475); /* 0x4 = auto ctx switch */
++ nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00000000);
++ nv_wr32(dev, 0x40009C , 0x00000040);
++
++ if (dev_priv->chipset >= 0x25) {
++ nv_wr32(dev, 0x400890, 0x00080000);
++ nv_wr32(dev, 0x400610, 0x304B1FB6);
++ nv_wr32(dev, 0x400B80, 0x18B82880);
++ nv_wr32(dev, 0x400B84, 0x44000000);
++ nv_wr32(dev, 0x400098, 0x40000080);
++ nv_wr32(dev, 0x400B88, 0x000000ff);
++ } else {
++ nv_wr32(dev, 0x400880, 0x00080000); /* 0x0008c7df */
++ nv_wr32(dev, 0x400094, 0x00000005);
++ nv_wr32(dev, 0x400B80, 0x45CAA208); /* 0x45eae20e */
++ nv_wr32(dev, 0x400B84, 0x24000000);
++ nv_wr32(dev, 0x400098, 0x00000040);
++ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00E00038);
++ nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000030);
++ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00E10038);
++ nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000030);
++ }
++
++ /* Turn all the tiling regions off. */
++ for (i = 0; i < NV10_PFB_TILE__SIZE; i++)
++ nv20_graph_set_region_tiling(dev, i, 0, 0, 0);
++
++ for (i = 0; i < 8; i++) {
++ nv_wr32(dev, 0x400980 + i * 4, nv_rd32(dev, 0x100300 + i * 4));
++ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0090 + i * 4);
++ nv_wr32(dev, NV10_PGRAPH_RDI_DATA,
++ nv_rd32(dev, 0x100300 + i * 4));
++ }
++ nv_wr32(dev, 0x4009a0, nv_rd32(dev, 0x100324));
++ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA000C);
++ nv_wr32(dev, NV10_PGRAPH_RDI_DATA, nv_rd32(dev, 0x100324));
++
++ nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000100);
++ nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF);
++
++ tmp = nv_rd32(dev, NV10_PGRAPH_SURFACE) & 0x0007ff00;
++ nv_wr32(dev, NV10_PGRAPH_SURFACE, tmp);
++ tmp = nv_rd32(dev, NV10_PGRAPH_SURFACE) | 0x00020100;
++ nv_wr32(dev, NV10_PGRAPH_SURFACE, tmp);
++
++ /* begin RAM config */
++ vramsz = drm_get_resource_len(dev, 0) - 1;
++ nv_wr32(dev, 0x4009A4, nv_rd32(dev, NV04_PFB_CFG0));
++ nv_wr32(dev, 0x4009A8, nv_rd32(dev, NV04_PFB_CFG1));
++ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0000);
++ nv_wr32(dev, NV10_PGRAPH_RDI_DATA , nv_rd32(dev, NV04_PFB_CFG0));
++ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0004);
++ nv_wr32(dev, NV10_PGRAPH_RDI_DATA , nv_rd32(dev, NV04_PFB_CFG1));
++ nv_wr32(dev, 0x400820, 0);
++ nv_wr32(dev, 0x400824, 0);
++ nv_wr32(dev, 0x400864, vramsz - 1);
++ nv_wr32(dev, 0x400868, vramsz - 1);
++
++ /* interesting.. the below overwrites some of the tile setup above.. */
++ nv_wr32(dev, 0x400B20, 0x00000000);
++ nv_wr32(dev, 0x400B04, 0xFFFFFFFF);
++
++ nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_XMIN, 0);
++ nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_YMIN, 0);
++ nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_XMAX, 0x7fff);
++ nv_wr32(dev, NV03_PGRAPH_ABS_UCLIP_YMAX, 0x7fff);
++
++ return 0;
++}
++
++void
++nv20_graph_takedown(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ nouveau_gpuobj_ref_del(dev, &dev_priv->ctx_table);
++}
++
++int
++nv30_graph_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int ret, i;
++
++ nv_wr32(dev, NV03_PMC_ENABLE,
++ nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PGRAPH);
++ nv_wr32(dev, NV03_PMC_ENABLE,
++ nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PGRAPH);
++
++ if (!dev_priv->ctx_table) {
++ /* Create Context Pointer Table */
++ dev_priv->ctx_table_size = 32 * 4;
++ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0,
++ dev_priv->ctx_table_size, 16,
++ NVOBJ_FLAG_ZERO_ALLOC,
++ &dev_priv->ctx_table);
++ if (ret)
++ return ret;
++ }
++
++ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_TABLE,
++ dev_priv->ctx_table->instance >> 4);
++
++ nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF);
++ nv_wr32(dev, NV03_PGRAPH_INTR_EN, 0xFFFFFFFF);
++
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF);
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000);
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x401287c0);
++ nv_wr32(dev, 0x400890, 0x01b463ff);
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xf2de0475);
++ nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00008000);
++ nv_wr32(dev, NV04_PGRAPH_LIMIT_VIOL_PIX, 0xf04bdff6);
++ nv_wr32(dev, 0x400B80, 0x1003d888);
++ nv_wr32(dev, 0x400B84, 0x0c000000);
++ nv_wr32(dev, 0x400098, 0x00000000);
++ nv_wr32(dev, 0x40009C, 0x0005ad00);
++ nv_wr32(dev, 0x400B88, 0x62ff00ff); /* suspiciously like PGRAPH_DEBUG_2 */
++ nv_wr32(dev, 0x4000a0, 0x00000000);
++ nv_wr32(dev, 0x4000a4, 0x00000008);
++ nv_wr32(dev, 0x4008a8, 0xb784a400);
++ nv_wr32(dev, 0x400ba0, 0x002f8685);
++ nv_wr32(dev, 0x400ba4, 0x00231f3f);
++ nv_wr32(dev, 0x4008a4, 0x40000020);
++
++ if (dev_priv->chipset == 0x34) {
++ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0004);
++ nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00200201);
++ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0008);
++ nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000008);
++ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00EA0000);
++ nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000032);
++ nv_wr32(dev, NV10_PGRAPH_RDI_INDEX, 0x00E00004);
++ nv_wr32(dev, NV10_PGRAPH_RDI_DATA , 0x00000002);
++ }
++
++ nv_wr32(dev, 0x4000c0, 0x00000016);
++
++ /* Turn all the tiling regions off. */
++ for (i = 0; i < NV10_PFB_TILE__SIZE; i++)
++ nv20_graph_set_region_tiling(dev, i, 0, 0, 0);
++
++ nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10000100);
++ nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF);
++ nv_wr32(dev, 0x0040075c , 0x00000001);
++
++ /* begin RAM config */
++ /* vramsz = drm_get_resource_len(dev, 0) - 1; */
++ nv_wr32(dev, 0x4009A4, nv_rd32(dev, NV04_PFB_CFG0));
++ nv_wr32(dev, 0x4009A8, nv_rd32(dev, NV04_PFB_CFG1));
++ if (dev_priv->chipset != 0x34) {
++ nv_wr32(dev, 0x400750, 0x00EA0000);
++ nv_wr32(dev, 0x400754, nv_rd32(dev, NV04_PFB_CFG0));
++ nv_wr32(dev, 0x400750, 0x00EA0004);
++ nv_wr32(dev, 0x400754, nv_rd32(dev, NV04_PFB_CFG1));
++ }
++
++ return 0;
++}
++
++struct nouveau_pgraph_object_class nv20_graph_grclass[] = {
++ { 0x0030, false, NULL }, /* null */
++ { 0x0039, false, NULL }, /* m2mf */
++ { 0x004a, false, NULL }, /* gdirect */
++ { 0x009f, false, NULL }, /* imageblit (nv12) */
++ { 0x008a, false, NULL }, /* ifc */
++ { 0x0089, false, NULL }, /* sifm */
++ { 0x0062, false, NULL }, /* surf2d */
++ { 0x0043, false, NULL }, /* rop */
++ { 0x0012, false, NULL }, /* beta1 */
++ { 0x0072, false, NULL }, /* beta4 */
++ { 0x0019, false, NULL }, /* cliprect */
++ { 0x0044, false, NULL }, /* pattern */
++ { 0x009e, false, NULL }, /* swzsurf */
++ { 0x0096, false, NULL }, /* celcius */
++ { 0x0097, false, NULL }, /* kelvin (nv20) */
++ { 0x0597, false, NULL }, /* kelvin (nv25) */
++ {}
++};
++
++struct nouveau_pgraph_object_class nv30_graph_grclass[] = {
++ { 0x0030, false, NULL }, /* null */
++ { 0x0039, false, NULL }, /* m2mf */
++ { 0x004a, false, NULL }, /* gdirect */
++ { 0x009f, false, NULL }, /* imageblit (nv12) */
++ { 0x008a, false, NULL }, /* ifc */
++ { 0x038a, false, NULL }, /* ifc (nv30) */
++ { 0x0089, false, NULL }, /* sifm */
++ { 0x0389, false, NULL }, /* sifm (nv30) */
++ { 0x0062, false, NULL }, /* surf2d */
++ { 0x0362, false, NULL }, /* surf2d (nv30) */
++ { 0x0043, false, NULL }, /* rop */
++ { 0x0012, false, NULL }, /* beta1 */
++ { 0x0072, false, NULL }, /* beta4 */
++ { 0x0019, false, NULL }, /* cliprect */
++ { 0x0044, false, NULL }, /* pattern */
++ { 0x039e, false, NULL }, /* swzsurf */
++ { 0x0397, false, NULL }, /* rankine (nv30) */
++ { 0x0497, false, NULL }, /* rankine (nv35) */
++ { 0x0697, false, NULL }, /* rankine (nv34) */
++ {}
++};
++
+diff --git a/drivers/gpu/drm/nouveau/nv40_fb.c b/drivers/gpu/drm/nouveau/nv40_fb.c
+new file mode 100644
+index 0000000..3cd07d8
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv40_fb.c
+@@ -0,0 +1,75 @@
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++#include "nouveau_drm.h"
++
++void
++nv40_fb_set_region_tiling(struct drm_device *dev, int i, uint32_t addr,
++ uint32_t size, uint32_t pitch)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t limit = max(1u, addr + size) - 1;
++
++ if (pitch)
++ addr |= 1;
++
++ switch (dev_priv->chipset) {
++ case 0x40:
++ nv_wr32(dev, NV10_PFB_TLIMIT(i), limit);
++ nv_wr32(dev, NV10_PFB_TSIZE(i), pitch);
++ nv_wr32(dev, NV10_PFB_TILE(i), addr);
++ break;
++
++ default:
++ nv_wr32(dev, NV40_PFB_TLIMIT(i), limit);
++ nv_wr32(dev, NV40_PFB_TSIZE(i), pitch);
++ nv_wr32(dev, NV40_PFB_TILE(i), addr);
++ break;
++ }
++}
++
++int
++nv40_fb_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
++ uint32_t tmp;
++ int i;
++
++ /* This is strictly a NV4x register (don't know about NV5x). */
++ /* The blob sets these to all kinds of values, and they mess up our setup. */
++ /* I got value 0x52802 instead. For some cards the blob even sets it back to 0x1. */
++ /* Note: the blob doesn't read this value, so i'm pretty sure this is safe for all cards. */
++ /* Any idea what this is? */
++ nv_wr32(dev, NV40_PFB_UNK_800, 0x1);
++
++ switch (dev_priv->chipset) {
++ case 0x40:
++ case 0x45:
++ tmp = nv_rd32(dev, NV10_PFB_CLOSE_PAGE2);
++ nv_wr32(dev, NV10_PFB_CLOSE_PAGE2, tmp & ~(1 << 15));
++ pfb->num_tiles = NV10_PFB_TILE__SIZE;
++ break;
++ case 0x46: /* G72 */
++ case 0x47: /* G70 */
++ case 0x49: /* G71 */
++ case 0x4b: /* G73 */
++ case 0x4c: /* C51 (G7X version) */
++ pfb->num_tiles = NV40_PFB_TILE__SIZE_1;
++ break;
++ default:
++ pfb->num_tiles = NV40_PFB_TILE__SIZE_0;
++ break;
++ }
++
++ /* Turn all the tiling regions off. */
++ for (i = 0; i < pfb->num_tiles; i++)
++ pfb->set_region_tiling(dev, i, 0, 0, 0);
++
++ return 0;
++}
++
++void
++nv40_fb_takedown(struct drm_device *dev)
++{
++}
+diff --git a/drivers/gpu/drm/nouveau/nv40_fifo.c b/drivers/gpu/drm/nouveau/nv40_fifo.c
+new file mode 100644
+index 0000000..b4f19cc
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv40_fifo.c
+@@ -0,0 +1,314 @@
++/*
++ * Copyright (C) 2007 Ben Skeggs.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "nouveau_drv.h"
++#include "nouveau_drm.h"
++
++#define NV40_RAMFC(c) (dev_priv->ramfc_offset + ((c) * NV40_RAMFC__SIZE))
++#define NV40_RAMFC__SIZE 128
++
++int
++nv40_fifo_create_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t fc = NV40_RAMFC(chan->id);
++ int ret;
++
++ ret = nouveau_gpuobj_new_fake(dev, NV40_RAMFC(chan->id), ~0,
++ NV40_RAMFC__SIZE, NVOBJ_FLAG_ZERO_ALLOC |
++ NVOBJ_FLAG_ZERO_FREE, NULL, &chan->ramfc);
++ if (ret)
++ return ret;
++
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ nv_wi32(dev, fc + 0, chan->pushbuf_base);
++ nv_wi32(dev, fc + 4, chan->pushbuf_base);
++ nv_wi32(dev, fc + 12, chan->pushbuf->instance >> 4);
++ nv_wi32(dev, fc + 24, NV_PFIFO_CACHE1_DMA_FETCH_TRIG_128_BYTES |
++ NV_PFIFO_CACHE1_DMA_FETCH_SIZE_128_BYTES |
++ NV_PFIFO_CACHE1_DMA_FETCH_MAX_REQS_8 |
++#ifdef __BIG_ENDIAN
++ NV_PFIFO_CACHE1_BIG_ENDIAN |
++#endif
++ 0x30000000 /* no idea.. */);
++ nv_wi32(dev, fc + 56, chan->ramin_grctx->instance >> 4);
++ nv_wi32(dev, fc + 60, 0x0001FFFF);
++ dev_priv->engine.instmem.finish_access(dev);
++
++ /* enable the fifo dma operation */
++ nv_wr32(dev, NV04_PFIFO_MODE,
++ nv_rd32(dev, NV04_PFIFO_MODE) | (1 << chan->id));
++ return 0;
++}
++
++void
++nv40_fifo_destroy_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++
++ nv_wr32(dev, NV04_PFIFO_MODE,
++ nv_rd32(dev, NV04_PFIFO_MODE) & ~(1 << chan->id));
++
++ if (chan->ramfc)
++ nouveau_gpuobj_ref_del(dev, &chan->ramfc);
++}
++
++static void
++nv40_fifo_do_load_context(struct drm_device *dev, int chid)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t fc = NV40_RAMFC(chid), tmp, tmp2;
++
++ dev_priv->engine.instmem.prepare_access(dev, false);
++
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUT, nv_ri32(dev, fc + 0));
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_GET, nv_ri32(dev, fc + 4));
++ nv_wr32(dev, NV10_PFIFO_CACHE1_REF_CNT, nv_ri32(dev, fc + 8));
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE, nv_ri32(dev, fc + 12));
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT, nv_ri32(dev, fc + 16));
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_STATE, nv_ri32(dev, fc + 20));
++
++ /* No idea what 0x2058 is.. */
++ tmp = nv_ri32(dev, fc + 24);
++ tmp2 = nv_rd32(dev, 0x2058) & 0xFFF;
++ tmp2 |= (tmp & 0x30000000);
++ nv_wr32(dev, 0x2058, tmp2);
++ tmp &= ~0x30000000;
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_FETCH, tmp);
++
++ nv_wr32(dev, NV04_PFIFO_CACHE1_ENGINE, nv_ri32(dev, fc + 28));
++ nv_wr32(dev, NV04_PFIFO_CACHE1_PULL1, nv_ri32(dev, fc + 32));
++ nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE, nv_ri32(dev, fc + 36));
++ tmp = nv_ri32(dev, fc + 40);
++ nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP, tmp);
++ nv_wr32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT, nv_ri32(dev, fc + 44));
++ nv_wr32(dev, NV10_PFIFO_CACHE1_SEMAPHORE, nv_ri32(dev, fc + 48));
++ nv_wr32(dev, NV10_PFIFO_CACHE1_DMA_SUBROUTINE, nv_ri32(dev, fc + 52));
++ nv_wr32(dev, NV40_PFIFO_GRCTX_INSTANCE, nv_ri32(dev, fc + 56));
++
++ /* Don't clobber the TIMEOUT_ENABLED flag when restoring from RAMFC */
++ tmp = nv_rd32(dev, NV04_PFIFO_DMA_TIMESLICE) & ~0x1FFFF;
++ tmp |= nv_ri32(dev, fc + 60) & 0x1FFFF;
++ nv_wr32(dev, NV04_PFIFO_DMA_TIMESLICE, tmp);
++
++ nv_wr32(dev, 0x32e4, nv_ri32(dev, fc + 64));
++ /* NVIDIA does this next line twice... */
++ nv_wr32(dev, 0x32e8, nv_ri32(dev, fc + 68));
++ nv_wr32(dev, 0x2088, nv_ri32(dev, fc + 76));
++ nv_wr32(dev, 0x3300, nv_ri32(dev, fc + 80));
++
++ dev_priv->engine.instmem.finish_access(dev);
++
++ nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
++ nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, 0);
++}
++
++int
++nv40_fifo_load_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ uint32_t tmp;
++
++ nv40_fifo_do_load_context(dev, chan->id);
++
++ /* Set channel active, and in DMA mode */
++ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1,
++ NV40_PFIFO_CACHE1_PUSH1_DMA | chan->id);
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_PUSH, 1);
++
++ /* Reset DMA_CTL_AT_INFO to INVALID */
++ tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_CTL) & ~(1 << 31);
++ nv_wr32(dev, NV04_PFIFO_CACHE1_DMA_CTL, tmp);
++
++ return 0;
++}
++
++int
++nv40_fifo_unload_context(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
++ uint32_t fc, tmp;
++ int chid;
++
++ chid = pfifo->channel_id(dev);
++ if (chid < 0 || chid >= dev_priv->engine.fifo.channels)
++ return 0;
++ fc = NV40_RAMFC(chid);
++
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ nv_wi32(dev, fc + 0, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_PUT));
++ nv_wi32(dev, fc + 4, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
++ nv_wi32(dev, fc + 8, nv_rd32(dev, NV10_PFIFO_CACHE1_REF_CNT));
++ nv_wi32(dev, fc + 12, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_INSTANCE));
++ nv_wi32(dev, fc + 16, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_DCOUNT));
++ nv_wi32(dev, fc + 20, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_STATE));
++ tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_FETCH);
++ tmp |= nv_rd32(dev, 0x2058) & 0x30000000;
++ nv_wi32(dev, fc + 24, tmp);
++ nv_wi32(dev, fc + 28, nv_rd32(dev, NV04_PFIFO_CACHE1_ENGINE));
++ nv_wi32(dev, fc + 32, nv_rd32(dev, NV04_PFIFO_CACHE1_PULL1));
++ nv_wi32(dev, fc + 36, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_VALUE));
++ tmp = nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMESTAMP);
++ nv_wi32(dev, fc + 40, tmp);
++ nv_wi32(dev, fc + 44, nv_rd32(dev, NV10_PFIFO_CACHE1_ACQUIRE_TIMEOUT));
++ nv_wi32(dev, fc + 48, nv_rd32(dev, NV10_PFIFO_CACHE1_SEMAPHORE));
++ /* NVIDIA read 0x3228 first, then write DMA_GET here.. maybe something
++ * more involved depending on the value of 0x3228?
++ */
++ nv_wi32(dev, fc + 52, nv_rd32(dev, NV04_PFIFO_CACHE1_DMA_GET));
++ nv_wi32(dev, fc + 56, nv_rd32(dev, NV40_PFIFO_GRCTX_INSTANCE));
++ nv_wi32(dev, fc + 60, nv_rd32(dev, NV04_PFIFO_DMA_TIMESLICE) & 0x1ffff);
++ /* No idea what the below is for exactly, ripped from a mmio-trace */
++ nv_wi32(dev, fc + 64, nv_rd32(dev, NV40_PFIFO_UNK32E4));
++ /* NVIDIA do this next line twice.. bug? */
++ nv_wi32(dev, fc + 68, nv_rd32(dev, 0x32e8));
++ nv_wi32(dev, fc + 76, nv_rd32(dev, 0x2088));
++ nv_wi32(dev, fc + 80, nv_rd32(dev, 0x3300));
++#if 0 /* no real idea which is PUT/GET in UNK_48.. */
++ tmp = nv_rd32(dev, NV04_PFIFO_CACHE1_GET);
++ tmp |= (nv_rd32(dev, NV04_PFIFO_CACHE1_PUT) << 16);
++ nv_wi32(dev, fc + 72, tmp);
++#endif
++ dev_priv->engine.instmem.finish_access(dev);
++
++ nv40_fifo_do_load_context(dev, pfifo->channels - 1);
++ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1,
++ NV40_PFIFO_CACHE1_PUSH1_DMA | (pfifo->channels - 1));
++ return 0;
++}
++
++static void
++nv40_fifo_init_reset(struct drm_device *dev)
++{
++ int i;
++
++ nv_wr32(dev, NV03_PMC_ENABLE,
++ nv_rd32(dev, NV03_PMC_ENABLE) & ~NV_PMC_ENABLE_PFIFO);
++ nv_wr32(dev, NV03_PMC_ENABLE,
++ nv_rd32(dev, NV03_PMC_ENABLE) | NV_PMC_ENABLE_PFIFO);
++
++ nv_wr32(dev, 0x003224, 0x000f0078);
++ nv_wr32(dev, 0x003210, 0x00000000);
++ nv_wr32(dev, 0x003270, 0x00000000);
++ nv_wr32(dev, 0x003240, 0x00000000);
++ nv_wr32(dev, 0x003244, 0x00000000);
++ nv_wr32(dev, 0x003258, 0x00000000);
++ nv_wr32(dev, 0x002504, 0x00000000);
++ for (i = 0; i < 16; i++)
++ nv_wr32(dev, 0x002510 + (i * 4), 0x00000000);
++ nv_wr32(dev, 0x00250c, 0x0000ffff);
++ nv_wr32(dev, 0x002048, 0x00000000);
++ nv_wr32(dev, 0x003228, 0x00000000);
++ nv_wr32(dev, 0x0032e8, 0x00000000);
++ nv_wr32(dev, 0x002410, 0x00000000);
++ nv_wr32(dev, 0x002420, 0x00000000);
++ nv_wr32(dev, 0x002058, 0x00000001);
++ nv_wr32(dev, 0x00221c, 0x00000000);
++ /* something with 0x2084, read/modify/write, no change */
++ nv_wr32(dev, 0x002040, 0x000000ff);
++ nv_wr32(dev, 0x002500, 0x00000000);
++ nv_wr32(dev, 0x003200, 0x00000000);
++
++ nv_wr32(dev, NV04_PFIFO_DMA_TIMESLICE, 0x2101ffff);
++}
++
++static void
++nv40_fifo_init_ramxx(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ nv_wr32(dev, NV03_PFIFO_RAMHT, (0x03 << 24) /* search 128 */ |
++ ((dev_priv->ramht_bits - 9) << 16) |
++ (dev_priv->ramht_offset >> 8));
++ nv_wr32(dev, NV03_PFIFO_RAMRO, dev_priv->ramro_offset>>8);
++
++ switch (dev_priv->chipset) {
++ case 0x47:
++ case 0x49:
++ case 0x4b:
++ nv_wr32(dev, 0x2230, 1);
++ break;
++ default:
++ break;
++ }
++
++ switch (dev_priv->chipset) {
++ case 0x40:
++ case 0x41:
++ case 0x42:
++ case 0x43:
++ case 0x45:
++ case 0x47:
++ case 0x48:
++ case 0x49:
++ case 0x4b:
++ nv_wr32(dev, NV40_PFIFO_RAMFC, 0x30002);
++ break;
++ default:
++ nv_wr32(dev, 0x2230, 0);
++ nv_wr32(dev, NV40_PFIFO_RAMFC,
++ ((nouveau_mem_fb_amount(dev) - 512 * 1024 +
++ dev_priv->ramfc_offset) >> 16) | (3 << 16));
++ break;
++ }
++}
++
++static void
++nv40_fifo_init_intr(struct drm_device *dev)
++{
++ nv_wr32(dev, 0x002100, 0xffffffff);
++ nv_wr32(dev, 0x002140, 0xffffffff);
++}
++
++int
++nv40_fifo_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
++ int i;
++
++ nv40_fifo_init_reset(dev);
++ nv40_fifo_init_ramxx(dev);
++
++ nv40_fifo_do_load_context(dev, pfifo->channels - 1);
++ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, pfifo->channels - 1);
++
++ nv40_fifo_init_intr(dev);
++ pfifo->enable(dev);
++ pfifo->reassign(dev, true);
++
++ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
++ if (dev_priv->fifos[i]) {
++ uint32_t mode = nv_rd32(dev, NV04_PFIFO_MODE);
++ nv_wr32(dev, NV04_PFIFO_MODE, mode | (1 << i));
++ }
++ }
++
++ return 0;
++}
+diff --git a/drivers/gpu/drm/nouveau/nv40_graph.c b/drivers/gpu/drm/nouveau/nv40_graph.c
+new file mode 100644
+index 0000000..53e8afe
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv40_graph.c
+@@ -0,0 +1,406 @@
++/*
++ * Copyright (C) 2007 Ben Skeggs.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++#include "nouveau_grctx.h"
++
++struct nouveau_channel *
++nv40_graph_channel(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t inst;
++ int i;
++
++ inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR);
++ if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED))
++ return NULL;
++ inst = (inst & NV40_PGRAPH_CTXCTL_CUR_INSTANCE) << 4;
++
++ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
++ struct nouveau_channel *chan = dev_priv->fifos[i];
++
++ if (chan && chan->ramin_grctx &&
++ chan->ramin_grctx->instance == inst)
++ return chan;
++ }
++
++ return NULL;
++}
++
++int
++nv40_graph_create_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_pgraph_engine *pgraph = &dev_priv->engine.graph;
++ int ret;
++
++ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pgraph->grctx_size,
++ 16, NVOBJ_FLAG_ZERO_ALLOC,
++ &chan->ramin_grctx);
++ if (ret)
++ return ret;
++
++ /* Initialise default context values */
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ if (!pgraph->ctxprog) {
++ struct nouveau_grctx ctx = {};
++
++ ctx.dev = chan->dev;
++ ctx.mode = NOUVEAU_GRCTX_VALS;
++ ctx.data = chan->ramin_grctx->gpuobj;
++ nv40_grctx_init(&ctx);
++ } else {
++ nouveau_grctx_vals_load(dev, chan->ramin_grctx->gpuobj);
++ }
++ nv_wo32(dev, chan->ramin_grctx->gpuobj, 0,
++ chan->ramin_grctx->gpuobj->im_pramin->start);
++ dev_priv->engine.instmem.finish_access(dev);
++ return 0;
++}
++
++void
++nv40_graph_destroy_context(struct nouveau_channel *chan)
++{
++ nouveau_gpuobj_ref_del(chan->dev, &chan->ramin_grctx);
++}
++
++static int
++nv40_graph_transfer_context(struct drm_device *dev, uint32_t inst, int save)
++{
++ uint32_t old_cp, tv = 1000, tmp;
++ int i;
++
++ old_cp = nv_rd32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER);
++ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
++
++ tmp = nv_rd32(dev, NV40_PGRAPH_CTXCTL_0310);
++ tmp |= save ? NV40_PGRAPH_CTXCTL_0310_XFER_SAVE :
++ NV40_PGRAPH_CTXCTL_0310_XFER_LOAD;
++ nv_wr32(dev, NV40_PGRAPH_CTXCTL_0310, tmp);
++
++ tmp = nv_rd32(dev, NV40_PGRAPH_CTXCTL_0304);
++ tmp |= NV40_PGRAPH_CTXCTL_0304_XFER_CTX;
++ nv_wr32(dev, NV40_PGRAPH_CTXCTL_0304, tmp);
++
++ nouveau_wait_for_idle(dev);
++
++ for (i = 0; i < tv; i++) {
++ if (nv_rd32(dev, NV40_PGRAPH_CTXCTL_030C) == 0)
++ break;
++ }
++
++ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, old_cp);
++
++ if (i == tv) {
++ uint32_t ucstat = nv_rd32(dev, NV40_PGRAPH_CTXCTL_UCODE_STAT);
++ NV_ERROR(dev, "Failed: Instance=0x%08x Save=%d\n", inst, save);
++ NV_ERROR(dev, "IP: 0x%02x, Opcode: 0x%08x\n",
++ ucstat >> NV40_PGRAPH_CTXCTL_UCODE_STAT_IP_SHIFT,
++ ucstat & NV40_PGRAPH_CTXCTL_UCODE_STAT_OP_MASK);
++ NV_ERROR(dev, "0x40030C = 0x%08x\n",
++ nv_rd32(dev, NV40_PGRAPH_CTXCTL_030C));
++ return -EBUSY;
++ }
++
++ return 0;
++}
++
++/* Restore the context for a specific channel into PGRAPH */
++int
++nv40_graph_load_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ uint32_t inst;
++ int ret;
++
++ if (!chan->ramin_grctx)
++ return -EINVAL;
++ inst = chan->ramin_grctx->instance >> 4;
++
++ ret = nv40_graph_transfer_context(dev, inst, 0);
++ if (ret)
++ return ret;
++
++ /* 0x40032C, no idea of it's exact function. Could simply be a
++ * record of the currently active PGRAPH context. It's currently
++ * unknown as to what bit 24 does. The nv ddx has it set, so we will
++ * set it here too.
++ */
++ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, inst);
++ nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR,
++ (inst & NV40_PGRAPH_CTXCTL_CUR_INSTANCE) |
++ NV40_PGRAPH_CTXCTL_CUR_LOADED);
++ /* 0x32E0 records the instance address of the active FIFO's PGRAPH
++ * context. If at any time this doesn't match 0x40032C, you will
++ * recieve PGRAPH_INTR_CONTEXT_SWITCH
++ */
++ nv_wr32(dev, NV40_PFIFO_GRCTX_INSTANCE, inst);
++ return 0;
++}
++
++int
++nv40_graph_unload_context(struct drm_device *dev)
++{
++ uint32_t inst;
++ int ret;
++
++ inst = nv_rd32(dev, NV40_PGRAPH_CTXCTL_CUR);
++ if (!(inst & NV40_PGRAPH_CTXCTL_CUR_LOADED))
++ return 0;
++ inst &= NV40_PGRAPH_CTXCTL_CUR_INSTANCE;
++
++ ret = nv40_graph_transfer_context(dev, inst, 1);
++
++ nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, inst);
++ return ret;
++}
++
++void
++nv40_graph_set_region_tiling(struct drm_device *dev, int i, uint32_t addr,
++ uint32_t size, uint32_t pitch)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t limit = max(1u, addr + size) - 1;
++
++ if (pitch)
++ addr |= 1;
++
++ switch (dev_priv->chipset) {
++ case 0x44:
++ case 0x4a:
++ case 0x4e:
++ nv_wr32(dev, NV20_PGRAPH_TSIZE(i), pitch);
++ nv_wr32(dev, NV20_PGRAPH_TLIMIT(i), limit);
++ nv_wr32(dev, NV20_PGRAPH_TILE(i), addr);
++ break;
++
++ case 0x46:
++ case 0x47:
++ case 0x49:
++ case 0x4b:
++ nv_wr32(dev, NV47_PGRAPH_TSIZE(i), pitch);
++ nv_wr32(dev, NV47_PGRAPH_TLIMIT(i), limit);
++ nv_wr32(dev, NV47_PGRAPH_TILE(i), addr);
++ nv_wr32(dev, NV40_PGRAPH_TSIZE1(i), pitch);
++ nv_wr32(dev, NV40_PGRAPH_TLIMIT1(i), limit);
++ nv_wr32(dev, NV40_PGRAPH_TILE1(i), addr);
++ break;
++
++ default:
++ nv_wr32(dev, NV20_PGRAPH_TSIZE(i), pitch);
++ nv_wr32(dev, NV20_PGRAPH_TLIMIT(i), limit);
++ nv_wr32(dev, NV20_PGRAPH_TILE(i), addr);
++ nv_wr32(dev, NV40_PGRAPH_TSIZE1(i), pitch);
++ nv_wr32(dev, NV40_PGRAPH_TLIMIT1(i), limit);
++ nv_wr32(dev, NV40_PGRAPH_TILE1(i), addr);
++ break;
++ }
++}
++
++/*
++ * G70 0x47
++ * G71 0x49
++ * NV45 0x48
++ * G72[M] 0x46
++ * G73 0x4b
++ * C51_G7X 0x4c
++ * C51 0x4e
++ */
++int
++nv40_graph_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv =
++ (struct drm_nouveau_private *)dev->dev_private;
++ struct nouveau_fb_engine *pfb = &dev_priv->engine.fb;
++ uint32_t vramsz;
++ int i, j;
++
++ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) &
++ ~NV_PMC_ENABLE_PGRAPH);
++ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) |
++ NV_PMC_ENABLE_PGRAPH);
++
++ if (nouveau_ctxfw) {
++ nouveau_grctx_prog_load(dev);
++ dev_priv->engine.graph.grctx_size = 175 * 1024;
++ }
++
++ if (!dev_priv->engine.graph.ctxprog) {
++ struct nouveau_grctx ctx = {};
++ uint32_t cp[256];
++
++ ctx.dev = dev;
++ ctx.mode = NOUVEAU_GRCTX_PROG;
++ ctx.data = cp;
++ ctx.ctxprog_max = 256;
++ nv40_grctx_init(&ctx);
++ dev_priv->engine.graph.grctx_size = ctx.ctxvals_pos * 4;
++
++ nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_INDEX, 0);
++ for (i = 0; i < ctx.ctxprog_len; i++)
++ nv_wr32(dev, NV40_PGRAPH_CTXCTL_UCODE_DATA, cp[i]);
++ }
++
++ /* No context present currently */
++ nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0x00000000);
++
++ nv_wr32(dev, NV03_PGRAPH_INTR , 0xFFFFFFFF);
++ nv_wr32(dev, NV40_PGRAPH_INTR_EN, 0xFFFFFFFF);
++
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0xFFFFFFFF);
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_0, 0x00000000);
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_1, 0x401287c0);
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_3, 0xe0de8055);
++ nv_wr32(dev, NV10_PGRAPH_DEBUG_4, 0x00008000);
++ nv_wr32(dev, NV04_PGRAPH_LIMIT_VIOL_PIX, 0x00be3c5f);
++
++ nv_wr32(dev, NV10_PGRAPH_CTX_CONTROL, 0x10010100);
++ nv_wr32(dev, NV10_PGRAPH_STATE , 0xFFFFFFFF);
++
++ j = nv_rd32(dev, 0x1540) & 0xff;
++ if (j) {
++ for (i = 0; !(j & 1); j >>= 1, i++)
++ ;
++ nv_wr32(dev, 0x405000, i);
++ }
++
++ if (dev_priv->chipset == 0x40) {
++ nv_wr32(dev, 0x4009b0, 0x83280fff);
++ nv_wr32(dev, 0x4009b4, 0x000000a0);
++ } else {
++ nv_wr32(dev, 0x400820, 0x83280eff);
++ nv_wr32(dev, 0x400824, 0x000000a0);
++ }
++
++ switch (dev_priv->chipset) {
++ case 0x40:
++ case 0x45:
++ nv_wr32(dev, 0x4009b8, 0x0078e366);
++ nv_wr32(dev, 0x4009bc, 0x0000014c);
++ break;
++ case 0x41:
++ case 0x42: /* pciid also 0x00Cx */
++ /* case 0x0120: XXX (pciid) */
++ nv_wr32(dev, 0x400828, 0x007596ff);
++ nv_wr32(dev, 0x40082c, 0x00000108);
++ break;
++ case 0x43:
++ nv_wr32(dev, 0x400828, 0x0072cb77);
++ nv_wr32(dev, 0x40082c, 0x00000108);
++ break;
++ case 0x44:
++ case 0x46: /* G72 */
++ case 0x4a:
++ case 0x4c: /* G7x-based C51 */
++ case 0x4e:
++ nv_wr32(dev, 0x400860, 0);
++ nv_wr32(dev, 0x400864, 0);
++ break;
++ case 0x47: /* G70 */
++ case 0x49: /* G71 */
++ case 0x4b: /* G73 */
++ nv_wr32(dev, 0x400828, 0x07830610);
++ nv_wr32(dev, 0x40082c, 0x0000016A);
++ break;
++ default:
++ break;
++ }
++
++ nv_wr32(dev, 0x400b38, 0x2ffff800);
++ nv_wr32(dev, 0x400b3c, 0x00006000);
++
++ /* Turn all the tiling regions off. */
++ for (i = 0; i < pfb->num_tiles; i++)
++ nv40_graph_set_region_tiling(dev, i, 0, 0, 0);
++
++ /* begin RAM config */
++ vramsz = drm_get_resource_len(dev, 0) - 1;
++ switch (dev_priv->chipset) {
++ case 0x40:
++ nv_wr32(dev, 0x4009A4, nv_rd32(dev, NV04_PFB_CFG0));
++ nv_wr32(dev, 0x4009A8, nv_rd32(dev, NV04_PFB_CFG1));
++ nv_wr32(dev, 0x4069A4, nv_rd32(dev, NV04_PFB_CFG0));
++ nv_wr32(dev, 0x4069A8, nv_rd32(dev, NV04_PFB_CFG1));
++ nv_wr32(dev, 0x400820, 0);
++ nv_wr32(dev, 0x400824, 0);
++ nv_wr32(dev, 0x400864, vramsz);
++ nv_wr32(dev, 0x400868, vramsz);
++ break;
++ default:
++ switch (dev_priv->chipset) {
++ case 0x46:
++ case 0x47:
++ case 0x49:
++ case 0x4b:
++ nv_wr32(dev, 0x400DF0, nv_rd32(dev, NV04_PFB_CFG0));
++ nv_wr32(dev, 0x400DF4, nv_rd32(dev, NV04_PFB_CFG1));
++ break;
++ default:
++ nv_wr32(dev, 0x4009F0, nv_rd32(dev, NV04_PFB_CFG0));
++ nv_wr32(dev, 0x4009F4, nv_rd32(dev, NV04_PFB_CFG1));
++ break;
++ }
++ nv_wr32(dev, 0x4069F0, nv_rd32(dev, NV04_PFB_CFG0));
++ nv_wr32(dev, 0x4069F4, nv_rd32(dev, NV04_PFB_CFG1));
++ nv_wr32(dev, 0x400840, 0);
++ nv_wr32(dev, 0x400844, 0);
++ nv_wr32(dev, 0x4008A0, vramsz);
++ nv_wr32(dev, 0x4008A4, vramsz);
++ break;
++ }
++
++ return 0;
++}
++
++void nv40_graph_takedown(struct drm_device *dev)
++{
++ nouveau_grctx_fini(dev);
++}
++
++struct nouveau_pgraph_object_class nv40_graph_grclass[] = {
++ { 0x0030, false, NULL }, /* null */
++ { 0x0039, false, NULL }, /* m2mf */
++ { 0x004a, false, NULL }, /* gdirect */
++ { 0x009f, false, NULL }, /* imageblit (nv12) */
++ { 0x008a, false, NULL }, /* ifc */
++ { 0x0089, false, NULL }, /* sifm */
++ { 0x3089, false, NULL }, /* sifm (nv40) */
++ { 0x0062, false, NULL }, /* surf2d */
++ { 0x3062, false, NULL }, /* surf2d (nv40) */
++ { 0x0043, false, NULL }, /* rop */
++ { 0x0012, false, NULL }, /* beta1 */
++ { 0x0072, false, NULL }, /* beta4 */
++ { 0x0019, false, NULL }, /* cliprect */
++ { 0x0044, false, NULL }, /* pattern */
++ { 0x309e, false, NULL }, /* swzsurf */
++ { 0x4097, false, NULL }, /* curie (nv40) */
++ { 0x4497, false, NULL }, /* curie (nv44) */
++ {}
++};
++
+diff --git a/drivers/gpu/drm/nouveau/nv40_grctx.c b/drivers/gpu/drm/nouveau/nv40_grctx.c
+new file mode 100644
+index 0000000..11b11c3
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv40_grctx.c
+@@ -0,0 +1,678 @@
++/*
++ * Copyright 2009 Red Hat Inc.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ *
++ * Authors: Ben Skeggs
++ */
++
++/* NVIDIA context programs handle a number of other conditions which are
++ * not implemented in our versions. It's not clear why NVIDIA context
++ * programs have this code, nor whether it's strictly necessary for
++ * correct operation. We'll implement additional handling if/when we
++ * discover it's necessary.
++ *
++ * - On context save, NVIDIA set 0x400314 bit 0 to 1 if the "3D state"
++ * flag is set, this gets saved into the context.
++ * - On context save, the context program for all cards load nsource
++ * into a flag register and check for ILLEGAL_MTHD. If it's set,
++ * opcode 0x60000d is called before resuming normal operation.
++ * - Some context programs check more conditions than the above. NV44
++ * checks: ((nsource & 0x0857) || (0x400718 & 0x0100) || (intr & 0x0001))
++ * and calls 0x60000d before resuming normal operation.
++ * - At the very beginning of NVIDIA's context programs, flag 9 is checked
++ * and if true 0x800001 is called with count=0, pos=0, the flag is cleared
++ * and then the ctxprog is aborted. It looks like a complicated NOP,
++ * its purpose is unknown.
++ * - In the section of code that loads the per-vs state, NVIDIA check
++ * flag 10. If it's set, they only transfer the small 0x300 byte block
++ * of state + the state for a single vs as opposed to the state for
++ * all vs units. It doesn't seem likely that it'll occur in normal
++ * operation, especially seeing as it appears NVIDIA may have screwed
++ * up the ctxprogs for some cards and have an invalid instruction
++ * rather than a cp_lsr(ctx, dwords_for_1_vs_unit) instruction.
++ * - There's a number of places where context offset 0 (where we place
++ * the PRAMIN offset of the context) is loaded into either 0x408000,
++ * 0x408004 or 0x408008. Not sure what's up there either.
++ * - The ctxprogs for some cards save 0x400a00 again during the cleanup
++ * path for auto-loadctx.
++ */
++
++#define CP_FLAG_CLEAR 0
++#define CP_FLAG_SET 1
++#define CP_FLAG_SWAP_DIRECTION ((0 * 32) + 0)
++#define CP_FLAG_SWAP_DIRECTION_LOAD 0
++#define CP_FLAG_SWAP_DIRECTION_SAVE 1
++#define CP_FLAG_USER_SAVE ((0 * 32) + 5)
++#define CP_FLAG_USER_SAVE_NOT_PENDING 0
++#define CP_FLAG_USER_SAVE_PENDING 1
++#define CP_FLAG_USER_LOAD ((0 * 32) + 6)
++#define CP_FLAG_USER_LOAD_NOT_PENDING 0
++#define CP_FLAG_USER_LOAD_PENDING 1
++#define CP_FLAG_STATUS ((3 * 32) + 0)
++#define CP_FLAG_STATUS_IDLE 0
++#define CP_FLAG_STATUS_BUSY 1
++#define CP_FLAG_AUTO_SAVE ((3 * 32) + 4)
++#define CP_FLAG_AUTO_SAVE_NOT_PENDING 0
++#define CP_FLAG_AUTO_SAVE_PENDING 1
++#define CP_FLAG_AUTO_LOAD ((3 * 32) + 5)
++#define CP_FLAG_AUTO_LOAD_NOT_PENDING 0
++#define CP_FLAG_AUTO_LOAD_PENDING 1
++#define CP_FLAG_UNK54 ((3 * 32) + 6)
++#define CP_FLAG_UNK54_CLEAR 0
++#define CP_FLAG_UNK54_SET 1
++#define CP_FLAG_ALWAYS ((3 * 32) + 8)
++#define CP_FLAG_ALWAYS_FALSE 0
++#define CP_FLAG_ALWAYS_TRUE 1
++#define CP_FLAG_UNK57 ((3 * 32) + 9)
++#define CP_FLAG_UNK57_CLEAR 0
++#define CP_FLAG_UNK57_SET 1
++
++#define CP_CTX 0x00100000
++#define CP_CTX_COUNT 0x000fc000
++#define CP_CTX_COUNT_SHIFT 14
++#define CP_CTX_REG 0x00003fff
++#define CP_LOAD_SR 0x00200000
++#define CP_LOAD_SR_VALUE 0x000fffff
++#define CP_BRA 0x00400000
++#define CP_BRA_IP 0x0000ff00
++#define CP_BRA_IP_SHIFT 8
++#define CP_BRA_IF_CLEAR 0x00000080
++#define CP_BRA_FLAG 0x0000007f
++#define CP_WAIT 0x00500000
++#define CP_WAIT_SET 0x00000080
++#define CP_WAIT_FLAG 0x0000007f
++#define CP_SET 0x00700000
++#define CP_SET_1 0x00000080
++#define CP_SET_FLAG 0x0000007f
++#define CP_NEXT_TO_SWAP 0x00600007
++#define CP_NEXT_TO_CURRENT 0x00600009
++#define CP_SET_CONTEXT_POINTER 0x0060000a
++#define CP_END 0x0060000e
++#define CP_LOAD_MAGIC_UNK01 0x00800001 /* unknown */
++#define CP_LOAD_MAGIC_NV44TCL 0x00800029 /* per-vs state (0x4497) */
++#define CP_LOAD_MAGIC_NV40TCL 0x00800041 /* per-vs state (0x4097) */
++
++#include "drmP.h"
++#include "nouveau_drv.h"
++#include "nouveau_grctx.h"
++
++/* TODO:
++ * - get vs count from 0x1540
++ * - document unimplemented bits compared to nvidia
++ * - nsource handling
++ * - R0 & 0x0200 handling
++ * - single-vs handling
++ * - 400314 bit 0
++ */
++
++static int
++nv40_graph_4097(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ if ((dev_priv->chipset & 0xf0) == 0x60)
++ return 0;
++
++ return !!(0x0baf & (1 << dev_priv->chipset));
++}
++
++static int
++nv40_graph_vs_count(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ switch (dev_priv->chipset) {
++ case 0x47:
++ case 0x49:
++ case 0x4b:
++ return 8;
++ case 0x40:
++ return 6;
++ case 0x41:
++ case 0x42:
++ return 5;
++ case 0x43:
++ case 0x44:
++ case 0x46:
++ case 0x4a:
++ return 3;
++ case 0x4c:
++ case 0x4e:
++ case 0x67:
++ default:
++ return 1;
++ }
++}
++
++
++enum cp_label {
++ cp_check_load = 1,
++ cp_setup_auto_load,
++ cp_setup_load,
++ cp_setup_save,
++ cp_swap_state,
++ cp_swap_state3d_3_is_save,
++ cp_prepare_exit,
++ cp_exit,
++};
++
++static void
++nv40_graph_construct_general(struct nouveau_grctx *ctx)
++{
++ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
++ int i;
++
++ cp_ctx(ctx, 0x4000a4, 1);
++ gr_def(ctx, 0x4000a4, 0x00000008);
++ cp_ctx(ctx, 0x400144, 58);
++ gr_def(ctx, 0x400144, 0x00000001);
++ cp_ctx(ctx, 0x400314, 1);
++ gr_def(ctx, 0x400314, 0x00000000);
++ cp_ctx(ctx, 0x400400, 10);
++ cp_ctx(ctx, 0x400480, 10);
++ cp_ctx(ctx, 0x400500, 19);
++ gr_def(ctx, 0x400514, 0x00040000);
++ gr_def(ctx, 0x400524, 0x55555555);
++ gr_def(ctx, 0x400528, 0x55555555);
++ gr_def(ctx, 0x40052c, 0x55555555);
++ gr_def(ctx, 0x400530, 0x55555555);
++ cp_ctx(ctx, 0x400560, 6);
++ gr_def(ctx, 0x400568, 0x0000ffff);
++ gr_def(ctx, 0x40056c, 0x0000ffff);
++ cp_ctx(ctx, 0x40057c, 5);
++ cp_ctx(ctx, 0x400710, 3);
++ gr_def(ctx, 0x400710, 0x20010001);
++ gr_def(ctx, 0x400714, 0x0f73ef00);
++ cp_ctx(ctx, 0x400724, 1);
++ gr_def(ctx, 0x400724, 0x02008821);
++ cp_ctx(ctx, 0x400770, 3);
++ if (dev_priv->chipset == 0x40) {
++ cp_ctx(ctx, 0x400814, 4);
++ cp_ctx(ctx, 0x400828, 5);
++ cp_ctx(ctx, 0x400840, 5);
++ gr_def(ctx, 0x400850, 0x00000040);
++ cp_ctx(ctx, 0x400858, 4);
++ gr_def(ctx, 0x400858, 0x00000040);
++ gr_def(ctx, 0x40085c, 0x00000040);
++ gr_def(ctx, 0x400864, 0x80000000);
++ cp_ctx(ctx, 0x40086c, 9);
++ gr_def(ctx, 0x40086c, 0x80000000);
++ gr_def(ctx, 0x400870, 0x80000000);
++ gr_def(ctx, 0x400874, 0x80000000);
++ gr_def(ctx, 0x400878, 0x80000000);
++ gr_def(ctx, 0x400888, 0x00000040);
++ gr_def(ctx, 0x40088c, 0x80000000);
++ cp_ctx(ctx, 0x4009c0, 8);
++ gr_def(ctx, 0x4009cc, 0x80000000);
++ gr_def(ctx, 0x4009dc, 0x80000000);
++ } else {
++ cp_ctx(ctx, 0x400840, 20);
++ if (!nv40_graph_4097(ctx->dev)) {
++ for (i = 0; i < 8; i++)
++ gr_def(ctx, 0x400860 + (i * 4), 0x00000001);
++ }
++ gr_def(ctx, 0x400880, 0x00000040);
++ gr_def(ctx, 0x400884, 0x00000040);
++ gr_def(ctx, 0x400888, 0x00000040);
++ cp_ctx(ctx, 0x400894, 11);
++ gr_def(ctx, 0x400894, 0x00000040);
++ if (nv40_graph_4097(ctx->dev)) {
++ for (i = 0; i < 8; i++)
++ gr_def(ctx, 0x4008a0 + (i * 4), 0x80000000);
++ }
++ cp_ctx(ctx, 0x4008e0, 2);
++ cp_ctx(ctx, 0x4008f8, 2);
++ if (dev_priv->chipset == 0x4c ||
++ (dev_priv->chipset & 0xf0) == 0x60)
++ cp_ctx(ctx, 0x4009f8, 1);
++ }
++ cp_ctx(ctx, 0x400a00, 73);
++ gr_def(ctx, 0x400b0c, 0x0b0b0b0c);
++ cp_ctx(ctx, 0x401000, 4);
++ cp_ctx(ctx, 0x405004, 1);
++ switch (dev_priv->chipset) {
++ case 0x47:
++ case 0x49:
++ case 0x4b:
++ cp_ctx(ctx, 0x403448, 1);
++ gr_def(ctx, 0x403448, 0x00001010);
++ break;
++ default:
++ cp_ctx(ctx, 0x403440, 1);
++ switch (dev_priv->chipset) {
++ case 0x40:
++ gr_def(ctx, 0x403440, 0x00000010);
++ break;
++ case 0x44:
++ case 0x46:
++ case 0x4a:
++ gr_def(ctx, 0x403440, 0x00003010);
++ break;
++ case 0x41:
++ case 0x42:
++ case 0x43:
++ case 0x4c:
++ case 0x4e:
++ case 0x67:
++ default:
++ gr_def(ctx, 0x403440, 0x00001010);
++ break;
++ }
++ break;
++ }
++}
++
++static void
++nv40_graph_construct_state3d(struct nouveau_grctx *ctx)
++{
++ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
++ int i;
++
++ if (dev_priv->chipset == 0x40) {
++ cp_ctx(ctx, 0x401880, 51);
++ gr_def(ctx, 0x401940, 0x00000100);
++ } else
++ if (dev_priv->chipset == 0x46 || dev_priv->chipset == 0x47 ||
++ dev_priv->chipset == 0x49 || dev_priv->chipset == 0x4b) {
++ cp_ctx(ctx, 0x401880, 32);
++ for (i = 0; i < 16; i++)
++ gr_def(ctx, 0x401880 + (i * 4), 0x00000111);
++ if (dev_priv->chipset == 0x46)
++ cp_ctx(ctx, 0x401900, 16);
++ cp_ctx(ctx, 0x401940, 3);
++ }
++ cp_ctx(ctx, 0x40194c, 18);
++ gr_def(ctx, 0x401954, 0x00000111);
++ gr_def(ctx, 0x401958, 0x00080060);
++ gr_def(ctx, 0x401974, 0x00000080);
++ gr_def(ctx, 0x401978, 0xffff0000);
++ gr_def(ctx, 0x40197c, 0x00000001);
++ gr_def(ctx, 0x401990, 0x46400000);
++ if (dev_priv->chipset == 0x40) {
++ cp_ctx(ctx, 0x4019a0, 2);
++ cp_ctx(ctx, 0x4019ac, 5);
++ } else {
++ cp_ctx(ctx, 0x4019a0, 1);
++ cp_ctx(ctx, 0x4019b4, 3);
++ }
++ gr_def(ctx, 0x4019bc, 0xffff0000);
++ switch (dev_priv->chipset) {
++ case 0x46:
++ case 0x47:
++ case 0x49:
++ case 0x4b:
++ cp_ctx(ctx, 0x4019c0, 18);
++ for (i = 0; i < 16; i++)
++ gr_def(ctx, 0x4019c0 + (i * 4), 0x88888888);
++ break;
++ }
++ cp_ctx(ctx, 0x401a08, 8);
++ gr_def(ctx, 0x401a10, 0x0fff0000);
++ gr_def(ctx, 0x401a14, 0x0fff0000);
++ gr_def(ctx, 0x401a1c, 0x00011100);
++ cp_ctx(ctx, 0x401a2c, 4);
++ cp_ctx(ctx, 0x401a44, 26);
++ for (i = 0; i < 16; i++)
++ gr_def(ctx, 0x401a44 + (i * 4), 0x07ff0000);
++ gr_def(ctx, 0x401a8c, 0x4b7fffff);
++ if (dev_priv->chipset == 0x40) {
++ cp_ctx(ctx, 0x401ab8, 3);
++ } else {
++ cp_ctx(ctx, 0x401ab8, 1);
++ cp_ctx(ctx, 0x401ac0, 1);
++ }
++ cp_ctx(ctx, 0x401ad0, 8);
++ gr_def(ctx, 0x401ad0, 0x30201000);
++ gr_def(ctx, 0x401ad4, 0x70605040);
++ gr_def(ctx, 0x401ad8, 0xb8a89888);
++ gr_def(ctx, 0x401adc, 0xf8e8d8c8);
++ cp_ctx(ctx, 0x401b10, dev_priv->chipset == 0x40 ? 2 : 1);
++ gr_def(ctx, 0x401b10, 0x40100000);
++ cp_ctx(ctx, 0x401b18, dev_priv->chipset == 0x40 ? 6 : 5);
++ gr_def(ctx, 0x401b28, dev_priv->chipset == 0x40 ?
++ 0x00000004 : 0x00000000);
++ cp_ctx(ctx, 0x401b30, 25);
++ gr_def(ctx, 0x401b34, 0x0000ffff);
++ gr_def(ctx, 0x401b68, 0x435185d6);
++ gr_def(ctx, 0x401b6c, 0x2155b699);
++ gr_def(ctx, 0x401b70, 0xfedcba98);
++ gr_def(ctx, 0x401b74, 0x00000098);
++ gr_def(ctx, 0x401b84, 0xffffffff);
++ gr_def(ctx, 0x401b88, 0x00ff7000);
++ gr_def(ctx, 0x401b8c, 0x0000ffff);
++ if (dev_priv->chipset != 0x44 && dev_priv->chipset != 0x4a &&
++ dev_priv->chipset != 0x4e)
++ cp_ctx(ctx, 0x401b94, 1);
++ cp_ctx(ctx, 0x401b98, 8);
++ gr_def(ctx, 0x401b9c, 0x00ff0000);
++ cp_ctx(ctx, 0x401bc0, 9);
++ gr_def(ctx, 0x401be0, 0x00ffff00);
++ cp_ctx(ctx, 0x401c00, 192);
++ for (i = 0; i < 16; i++) { /* fragment texture units */
++ gr_def(ctx, 0x401c40 + (i * 4), 0x00018488);
++ gr_def(ctx, 0x401c80 + (i * 4), 0x00028202);
++ gr_def(ctx, 0x401d00 + (i * 4), 0x0000aae4);
++ gr_def(ctx, 0x401d40 + (i * 4), 0x01012000);
++ gr_def(ctx, 0x401d80 + (i * 4), 0x00080008);
++ gr_def(ctx, 0x401e00 + (i * 4), 0x00100008);
++ }
++ for (i = 0; i < 4; i++) { /* vertex texture units */
++ gr_def(ctx, 0x401e90 + (i * 4), 0x0001bc80);
++ gr_def(ctx, 0x401ea0 + (i * 4), 0x00000202);
++ gr_def(ctx, 0x401ec0 + (i * 4), 0x00000008);
++ gr_def(ctx, 0x401ee0 + (i * 4), 0x00080008);
++ }
++ cp_ctx(ctx, 0x400f5c, 3);
++ gr_def(ctx, 0x400f5c, 0x00000002);
++ cp_ctx(ctx, 0x400f84, 1);
++}
++
++static void
++nv40_graph_construct_state3d_2(struct nouveau_grctx *ctx)
++{
++ struct drm_nouveau_private *dev_priv = ctx->dev->dev_private;
++ int i;
++
++ cp_ctx(ctx, 0x402000, 1);
++ cp_ctx(ctx, 0x402404, dev_priv->chipset == 0x40 ? 1 : 2);
++ switch (dev_priv->chipset) {
++ case 0x40:
++ gr_def(ctx, 0x402404, 0x00000001);
++ break;
++ case 0x4c:
++ case 0x4e:
++ case 0x67:
++ gr_def(ctx, 0x402404, 0x00000020);
++ break;
++ case 0x46:
++ case 0x49:
++ case 0x4b:
++ gr_def(ctx, 0x402404, 0x00000421);
++ break;
++ default:
++ gr_def(ctx, 0x402404, 0x00000021);
++ }
++ if (dev_priv->chipset != 0x40)
++ gr_def(ctx, 0x402408, 0x030c30c3);
++ switch (dev_priv->chipset) {
++ case 0x44:
++ case 0x46:
++ case 0x4a:
++ case 0x4c:
++ case 0x4e:
++ case 0x67:
++ cp_ctx(ctx, 0x402440, 1);
++ gr_def(ctx, 0x402440, 0x00011001);
++ break;
++ default:
++ break;
++ }
++ cp_ctx(ctx, 0x402480, dev_priv->chipset == 0x40 ? 8 : 9);
++ gr_def(ctx, 0x402488, 0x3e020200);
++ gr_def(ctx, 0x40248c, 0x00ffffff);
++ switch (dev_priv->chipset) {
++ case 0x40:
++ gr_def(ctx, 0x402490, 0x60103f00);
++ break;
++ case 0x47:
++ gr_def(ctx, 0x402490, 0x40103f00);
++ break;
++ case 0x41:
++ case 0x42:
++ case 0x49:
++ case 0x4b:
++ gr_def(ctx, 0x402490, 0x20103f00);
++ break;
++ default:
++ gr_def(ctx, 0x402490, 0x0c103f00);
++ break;
++ }
++ gr_def(ctx, 0x40249c, dev_priv->chipset <= 0x43 ?
++ 0x00020000 : 0x00040000);
++ cp_ctx(ctx, 0x402500, 31);
++ gr_def(ctx, 0x402530, 0x00008100);
++ if (dev_priv->chipset == 0x40)
++ cp_ctx(ctx, 0x40257c, 6);
++ cp_ctx(ctx, 0x402594, 16);
++ cp_ctx(ctx, 0x402800, 17);
++ gr_def(ctx, 0x402800, 0x00000001);
++ switch (dev_priv->chipset) {
++ case 0x47:
++ case 0x49:
++ case 0x4b:
++ cp_ctx(ctx, 0x402864, 1);
++ gr_def(ctx, 0x402864, 0x00001001);
++ cp_ctx(ctx, 0x402870, 3);
++ gr_def(ctx, 0x402878, 0x00000003);
++ if (dev_priv->chipset != 0x47) { /* belong at end!! */
++ cp_ctx(ctx, 0x402900, 1);
++ cp_ctx(ctx, 0x402940, 1);
++ cp_ctx(ctx, 0x402980, 1);
++ cp_ctx(ctx, 0x4029c0, 1);
++ cp_ctx(ctx, 0x402a00, 1);
++ cp_ctx(ctx, 0x402a40, 1);
++ cp_ctx(ctx, 0x402a80, 1);
++ cp_ctx(ctx, 0x402ac0, 1);
++ }
++ break;
++ case 0x40:
++ cp_ctx(ctx, 0x402844, 1);
++ gr_def(ctx, 0x402844, 0x00000001);
++ cp_ctx(ctx, 0x402850, 1);
++ break;
++ default:
++ cp_ctx(ctx, 0x402844, 1);
++ gr_def(ctx, 0x402844, 0x00001001);
++ cp_ctx(ctx, 0x402850, 2);
++ gr_def(ctx, 0x402854, 0x00000003);
++ break;
++ }
++
++ cp_ctx(ctx, 0x402c00, 4);
++ gr_def(ctx, 0x402c00, dev_priv->chipset == 0x40 ?
++ 0x80800001 : 0x00888001);
++ switch (dev_priv->chipset) {
++ case 0x47:
++ case 0x49:
++ case 0x4b:
++ cp_ctx(ctx, 0x402c20, 40);
++ for (i = 0; i < 32; i++)
++ gr_def(ctx, 0x402c40 + (i * 4), 0xffffffff);
++ cp_ctx(ctx, 0x4030b8, 13);
++ gr_def(ctx, 0x4030dc, 0x00000005);
++ gr_def(ctx, 0x4030e8, 0x0000ffff);
++ break;
++ default:
++ cp_ctx(ctx, 0x402c10, 4);
++ if (dev_priv->chipset == 0x40)
++ cp_ctx(ctx, 0x402c20, 36);
++ else
++ if (dev_priv->chipset <= 0x42)
++ cp_ctx(ctx, 0x402c20, 24);
++ else
++ if (dev_priv->chipset <= 0x4a)
++ cp_ctx(ctx, 0x402c20, 16);
++ else
++ cp_ctx(ctx, 0x402c20, 8);
++ cp_ctx(ctx, 0x402cb0, dev_priv->chipset == 0x40 ? 12 : 13);
++ gr_def(ctx, 0x402cd4, 0x00000005);
++ if (dev_priv->chipset != 0x40)
++ gr_def(ctx, 0x402ce0, 0x0000ffff);
++ break;
++ }
++
++ cp_ctx(ctx, 0x403400, dev_priv->chipset == 0x40 ? 4 : 3);
++ cp_ctx(ctx, 0x403410, dev_priv->chipset == 0x40 ? 4 : 3);
++ cp_ctx(ctx, 0x403420, nv40_graph_vs_count(ctx->dev));
++ for (i = 0; i < nv40_graph_vs_count(ctx->dev); i++)
++ gr_def(ctx, 0x403420 + (i * 4), 0x00005555);
++
++ if (dev_priv->chipset != 0x40) {
++ cp_ctx(ctx, 0x403600, 1);
++ gr_def(ctx, 0x403600, 0x00000001);
++ }
++ cp_ctx(ctx, 0x403800, 1);
++
++ cp_ctx(ctx, 0x403c18, 1);
++ gr_def(ctx, 0x403c18, 0x00000001);
++ switch (dev_priv->chipset) {
++ case 0x46:
++ case 0x47:
++ case 0x49:
++ case 0x4b:
++ cp_ctx(ctx, 0x405018, 1);
++ gr_def(ctx, 0x405018, 0x08e00001);
++ cp_ctx(ctx, 0x405c24, 1);
++ gr_def(ctx, 0x405c24, 0x000e3000);
++ break;
++ }
++ if (dev_priv->chipset != 0x4e)
++ cp_ctx(ctx, 0x405800, 11);
++ cp_ctx(ctx, 0x407000, 1);
++}
++
++static void
++nv40_graph_construct_state3d_3(struct nouveau_grctx *ctx)
++{
++ int len = nv40_graph_4097(ctx->dev) ? 0x0684 : 0x0084;
++
++ cp_out (ctx, 0x300000);
++ cp_lsr (ctx, len - 4);
++ cp_bra (ctx, SWAP_DIRECTION, SAVE, cp_swap_state3d_3_is_save);
++ cp_lsr (ctx, len);
++ cp_name(ctx, cp_swap_state3d_3_is_save);
++ cp_out (ctx, 0x800001);
++
++ ctx->ctxvals_pos += len;
++}
++
++static void
++nv40_graph_construct_shader(struct nouveau_grctx *ctx)
++{
++ struct drm_device *dev = ctx->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *obj = ctx->data;
++ int vs, vs_nr, vs_len, vs_nr_b0, vs_nr_b1, b0_offset, b1_offset;
++ int offset, i;
++
++ vs_nr = nv40_graph_vs_count(ctx->dev);
++ vs_nr_b0 = 363;
++ vs_nr_b1 = dev_priv->chipset == 0x40 ? 128 : 64;
++ if (dev_priv->chipset == 0x40) {
++ b0_offset = 0x2200/4; /* 33a0 */
++ b1_offset = 0x55a0/4; /* 1500 */
++ vs_len = 0x6aa0/4;
++ } else
++ if (dev_priv->chipset == 0x41 || dev_priv->chipset == 0x42) {
++ b0_offset = 0x2200/4; /* 2200 */
++ b1_offset = 0x4400/4; /* 0b00 */
++ vs_len = 0x4f00/4;
++ } else {
++ b0_offset = 0x1d40/4; /* 2200 */
++ b1_offset = 0x3f40/4; /* 0b00 : 0a40 */
++ vs_len = nv40_graph_4097(dev) ? 0x4a40/4 : 0x4980/4;
++ }
++
++ cp_lsr(ctx, vs_len * vs_nr + 0x300/4);
++ cp_out(ctx, nv40_graph_4097(dev) ? 0x800041 : 0x800029);
++
++ offset = ctx->ctxvals_pos;
++ ctx->ctxvals_pos += (0x0300/4 + (vs_nr * vs_len));
++
++ if (ctx->mode != NOUVEAU_GRCTX_VALS)
++ return;
++
++ offset += 0x0280/4;
++ for (i = 0; i < 16; i++, offset += 2)
++ nv_wo32(dev, obj, offset, 0x3f800000);
++
++ for (vs = 0; vs < vs_nr; vs++, offset += vs_len) {
++ for (i = 0; i < vs_nr_b0 * 6; i += 6)
++ nv_wo32(dev, obj, offset + b0_offset + i, 0x00000001);
++ for (i = 0; i < vs_nr_b1 * 4; i += 4)
++ nv_wo32(dev, obj, offset + b1_offset + i, 0x3f800000);
++ }
++}
++
++void
++nv40_grctx_init(struct nouveau_grctx *ctx)
++{
++ /* decide whether we're loading/unloading the context */
++ cp_bra (ctx, AUTO_SAVE, PENDING, cp_setup_save);
++ cp_bra (ctx, USER_SAVE, PENDING, cp_setup_save);
++
++ cp_name(ctx, cp_check_load);
++ cp_bra (ctx, AUTO_LOAD, PENDING, cp_setup_auto_load);
++ cp_bra (ctx, USER_LOAD, PENDING, cp_setup_load);
++ cp_bra (ctx, ALWAYS, TRUE, cp_exit);
++
++ /* setup for context load */
++ cp_name(ctx, cp_setup_auto_load);
++ cp_wait(ctx, STATUS, IDLE);
++ cp_out (ctx, CP_NEXT_TO_SWAP);
++ cp_name(ctx, cp_setup_load);
++ cp_wait(ctx, STATUS, IDLE);
++ cp_set (ctx, SWAP_DIRECTION, LOAD);
++ cp_out (ctx, 0x00910880); /* ?? */
++ cp_out (ctx, 0x00901ffe); /* ?? */
++ cp_out (ctx, 0x01940000); /* ?? */
++ cp_lsr (ctx, 0x20);
++ cp_out (ctx, 0x0060000b); /* ?? */
++ cp_wait(ctx, UNK57, CLEAR);
++ cp_out (ctx, 0x0060000c); /* ?? */
++ cp_bra (ctx, ALWAYS, TRUE, cp_swap_state);
++
++ /* setup for context save */
++ cp_name(ctx, cp_setup_save);
++ cp_set (ctx, SWAP_DIRECTION, SAVE);
++
++ /* general PGRAPH state */
++ cp_name(ctx, cp_swap_state);
++ cp_pos (ctx, 0x00020/4);
++ nv40_graph_construct_general(ctx);
++ cp_wait(ctx, STATUS, IDLE);
++
++ /* 3D state, block 1 */
++ cp_bra (ctx, UNK54, CLEAR, cp_prepare_exit);
++ nv40_graph_construct_state3d(ctx);
++ cp_wait(ctx, STATUS, IDLE);
++
++ /* 3D state, block 2 */
++ nv40_graph_construct_state3d_2(ctx);
++
++ /* Some other block of "random" state */
++ nv40_graph_construct_state3d_3(ctx);
++
++ /* Per-vertex shader state */
++ cp_pos (ctx, ctx->ctxvals_pos);
++ nv40_graph_construct_shader(ctx);
++
++ /* pre-exit state updates */
++ cp_name(ctx, cp_prepare_exit);
++ cp_bra (ctx, SWAP_DIRECTION, SAVE, cp_check_load);
++ cp_bra (ctx, USER_SAVE, PENDING, cp_exit);
++ cp_out (ctx, CP_NEXT_TO_CURRENT);
++
++ cp_name(ctx, cp_exit);
++ cp_set (ctx, USER_SAVE, NOT_PENDING);
++ cp_set (ctx, USER_LOAD, NOT_PENDING);
++ cp_out (ctx, CP_END);
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nv40_mc.c b/drivers/gpu/drm/nouveau/nv40_mc.c
+new file mode 100644
+index 0000000..2a3495e
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv40_mc.c
+@@ -0,0 +1,38 @@
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++#include "nouveau_drm.h"
++
++int
++nv40_mc_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t tmp;
++
++ /* Power up everything, resetting each individual unit will
++ * be done later if needed.
++ */
++ nv_wr32(dev, NV03_PMC_ENABLE, 0xFFFFFFFF);
++
++ switch (dev_priv->chipset) {
++ case 0x44:
++ case 0x46: /* G72 */
++ case 0x4e:
++ case 0x4c: /* C51_G7X */
++ tmp = nv_rd32(dev, NV40_PFB_020C);
++ nv_wr32(dev, NV40_PMC_1700, tmp);
++ nv_wr32(dev, NV40_PMC_1704, 0);
++ nv_wr32(dev, NV40_PMC_1708, 0);
++ nv_wr32(dev, NV40_PMC_170C, tmp);
++ break;
++ default:
++ break;
++ }
++
++ return 0;
++}
++
++void
++nv40_mc_takedown(struct drm_device *dev)
++{
++}
+diff --git a/drivers/gpu/drm/nouveau/nv50_crtc.c b/drivers/gpu/drm/nouveau/nv50_crtc.c
+new file mode 100644
+index 0000000..d1a651e
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv50_crtc.c
+@@ -0,0 +1,792 @@
++/*
++ * Copyright (C) 2008 Maarten Maathuis.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "drm_mode.h"
++#include "drm_crtc_helper.h"
++
++#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
++#include "nouveau_reg.h"
++#include "nouveau_drv.h"
++#include "nouveau_hw.h"
++#include "nouveau_encoder.h"
++#include "nouveau_crtc.h"
++#include "nouveau_fb.h"
++#include "nouveau_connector.h"
++#include "nv50_display.h"
++
++static void
++nv50_crtc_lut_load(struct drm_crtc *crtc)
++{
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ void __iomem *lut = nvbo_kmap_obj_iovirtual(nv_crtc->lut.nvbo);
++ int i;
++
++ NV_DEBUG_KMS(crtc->dev, "\n");
++
++ for (i = 0; i < 256; i++) {
++ writew(nv_crtc->lut.r[i] >> 2, lut + 8*i + 0);
++ writew(nv_crtc->lut.g[i] >> 2, lut + 8*i + 2);
++ writew(nv_crtc->lut.b[i] >> 2, lut + 8*i + 4);
++ }
++
++ if (nv_crtc->lut.depth == 30) {
++ writew(nv_crtc->lut.r[i - 1] >> 2, lut + 8*i + 0);
++ writew(nv_crtc->lut.g[i - 1] >> 2, lut + 8*i + 2);
++ writew(nv_crtc->lut.b[i - 1] >> 2, lut + 8*i + 4);
++ }
++}
++
++int
++nv50_crtc_blank(struct nouveau_crtc *nv_crtc, bool blanked)
++{
++ struct drm_device *dev = nv_crtc->base.dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *evo = dev_priv->evo;
++ int index = nv_crtc->index, ret;
++
++ NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
++ NV_DEBUG_KMS(dev, "%s\n", blanked ? "blanked" : "unblanked");
++
++ if (blanked) {
++ nv_crtc->cursor.hide(nv_crtc, false);
++
++ ret = RING_SPACE(evo, dev_priv->chipset != 0x50 ? 7 : 5);
++ if (ret) {
++ NV_ERROR(dev, "no space while blanking crtc\n");
++ return ret;
++ }
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, CLUT_MODE), 2);
++ OUT_RING(evo, NV50_EVO_CRTC_CLUT_MODE_BLANK);
++ OUT_RING(evo, 0);
++ if (dev_priv->chipset != 0x50) {
++ BEGIN_RING(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1);
++ OUT_RING(evo, NV84_EVO_CRTC_CLUT_DMA_HANDLE_NONE);
++ }
++
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, FB_DMA), 1);
++ OUT_RING(evo, NV50_EVO_CRTC_FB_DMA_HANDLE_NONE);
++ } else {
++ if (nv_crtc->cursor.visible)
++ nv_crtc->cursor.show(nv_crtc, false);
++ else
++ nv_crtc->cursor.hide(nv_crtc, false);
++
++ ret = RING_SPACE(evo, dev_priv->chipset != 0x50 ? 10 : 8);
++ if (ret) {
++ NV_ERROR(dev, "no space while unblanking crtc\n");
++ return ret;
++ }
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, CLUT_MODE), 2);
++ OUT_RING(evo, nv_crtc->lut.depth == 8 ?
++ NV50_EVO_CRTC_CLUT_MODE_OFF :
++ NV50_EVO_CRTC_CLUT_MODE_ON);
++ OUT_RING(evo, (nv_crtc->lut.nvbo->bo.mem.mm_node->start <<
++ PAGE_SHIFT) >> 8);
++ if (dev_priv->chipset != 0x50) {
++ BEGIN_RING(evo, 0, NV84_EVO_CRTC(index, CLUT_DMA), 1);
++ OUT_RING(evo, NvEvoVRAM);
++ }
++
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, FB_OFFSET), 2);
++ OUT_RING(evo, nv_crtc->fb.offset >> 8);
++ OUT_RING(evo, 0);
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(index, FB_DMA), 1);
++ if (dev_priv->chipset != 0x50)
++ if (nv_crtc->fb.tile_flags == 0x7a00)
++ OUT_RING(evo, NvEvoFB32);
++ else
++ if (nv_crtc->fb.tile_flags == 0x7000)
++ OUT_RING(evo, NvEvoFB16);
++ else
++ OUT_RING(evo, NvEvoVRAM);
++ else
++ OUT_RING(evo, NvEvoVRAM);
++ }
++
++ nv_crtc->fb.blanked = blanked;
++ return 0;
++}
++
++static int
++nv50_crtc_set_dither(struct nouveau_crtc *nv_crtc, bool on, bool update)
++{
++ struct drm_device *dev = nv_crtc->base.dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *evo = dev_priv->evo;
++ int ret;
++
++ NV_DEBUG_KMS(dev, "\n");
++
++ ret = RING_SPACE(evo, 2 + (update ? 2 : 0));
++ if (ret) {
++ NV_ERROR(dev, "no space while setting dither\n");
++ return ret;
++ }
++
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DITHER_CTRL), 1);
++ if (on)
++ OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_ON);
++ else
++ OUT_RING(evo, NV50_EVO_CRTC_DITHER_CTRL_OFF);
++
++ if (update) {
++ BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
++ OUT_RING(evo, 0);
++ FIRE_RING(evo);
++ }
++
++ return 0;
++}
++
++struct nouveau_connector *
++nouveau_crtc_connector_get(struct nouveau_crtc *nv_crtc)
++{
++ struct drm_device *dev = nv_crtc->base.dev;
++ struct drm_connector *connector;
++ struct drm_crtc *crtc = to_drm_crtc(nv_crtc);
++
++ /* The safest approach is to find an encoder with the right crtc, that
++ * is also linked to a connector. */
++ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
++ if (connector->encoder)
++ if (connector->encoder->crtc == crtc)
++ return nouveau_connector(connector);
++ }
++
++ return NULL;
++}
++
++static int
++nv50_crtc_set_scale(struct nouveau_crtc *nv_crtc, int scaling_mode, bool update)
++{
++ struct nouveau_connector *nv_connector =
++ nouveau_crtc_connector_get(nv_crtc);
++ struct drm_device *dev = nv_crtc->base.dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *evo = dev_priv->evo;
++ struct drm_display_mode *native_mode = NULL;
++ struct drm_display_mode *mode = &nv_crtc->base.mode;
++ uint32_t outX, outY, horiz, vert;
++ int ret;
++
++ NV_DEBUG_KMS(dev, "\n");
++
++ switch (scaling_mode) {
++ case DRM_MODE_SCALE_NONE:
++ break;
++ default:
++ if (!nv_connector || !nv_connector->native_mode) {
++ NV_ERROR(dev, "No native mode, forcing panel scaling\n");
++ scaling_mode = DRM_MODE_SCALE_NONE;
++ } else {
++ native_mode = nv_connector->native_mode;
++ }
++ break;
++ }
++
++ switch (scaling_mode) {
++ case DRM_MODE_SCALE_ASPECT:
++ horiz = (native_mode->hdisplay << 19) / mode->hdisplay;
++ vert = (native_mode->vdisplay << 19) / mode->vdisplay;
++
++ if (vert > horiz) {
++ outX = (mode->hdisplay * horiz) >> 19;
++ outY = (mode->vdisplay * horiz) >> 19;
++ } else {
++ outX = (mode->hdisplay * vert) >> 19;
++ outY = (mode->vdisplay * vert) >> 19;
++ }
++ break;
++ case DRM_MODE_SCALE_FULLSCREEN:
++ outX = native_mode->hdisplay;
++ outY = native_mode->vdisplay;
++ break;
++ case DRM_MODE_SCALE_CENTER:
++ case DRM_MODE_SCALE_NONE:
++ default:
++ outX = mode->hdisplay;
++ outY = mode->vdisplay;
++ break;
++ }
++
++ ret = RING_SPACE(evo, update ? 7 : 5);
++ if (ret)
++ return ret;
++
++ /* Got a better name for SCALER_ACTIVE? */
++ /* One day i've got to really figure out why this is needed. */
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CTRL), 1);
++ if ((mode->flags & DRM_MODE_FLAG_DBLSCAN) ||
++ (mode->flags & DRM_MODE_FLAG_INTERLACE) ||
++ mode->hdisplay != outX || mode->vdisplay != outY) {
++ OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_ACTIVE);
++ } else {
++ OUT_RING(evo, NV50_EVO_CRTC_SCALE_CTRL_INACTIVE);
++ }
++
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_RES1), 2);
++ OUT_RING(evo, outY << 16 | outX);
++ OUT_RING(evo, outY << 16 | outX);
++
++ if (update) {
++ BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
++ OUT_RING(evo, 0);
++ FIRE_RING(evo);
++ }
++
++ return 0;
++}
++
++int
++nv50_crtc_set_clock(struct drm_device *dev, int head, int pclk)
++{
++ uint32_t pll_reg = NV50_PDISPLAY_CRTC_CLK_CTRL1(head);
++ struct nouveau_pll_vals pll;
++ struct pll_lims limits;
++ uint32_t reg1, reg2;
++ int ret;
++
++ ret = get_pll_limits(dev, pll_reg, &limits);
++ if (ret)
++ return ret;
++
++ ret = nouveau_calc_pll_mnp(dev, &limits, pclk, &pll);
++ if (ret <= 0)
++ return ret;
++
++ if (limits.vco2.maxfreq) {
++ reg1 = nv_rd32(dev, pll_reg + 4) & 0xff00ff00;
++ reg2 = nv_rd32(dev, pll_reg + 8) & 0x8000ff00;
++ nv_wr32(dev, pll_reg, 0x10000611);
++ nv_wr32(dev, pll_reg + 4, reg1 | (pll.M1 << 16) | pll.N1);
++ nv_wr32(dev, pll_reg + 8,
++ reg2 | (pll.log2P << 28) | (pll.M2 << 16) | pll.N2);
++ } else {
++ reg1 = nv_rd32(dev, pll_reg + 4) & 0xffc00000;
++ nv_wr32(dev, pll_reg, 0x50000610);
++ nv_wr32(dev, pll_reg + 4, reg1 |
++ (pll.log2P << 16) | (pll.M1 << 8) | pll.N1);
++ }
++
++ return 0;
++}
++
++static void
++nv50_crtc_destroy(struct drm_crtc *crtc)
++{
++ struct drm_device *dev;
++ struct nouveau_crtc *nv_crtc;
++
++ if (!crtc)
++ return;
++
++ dev = crtc->dev;
++ nv_crtc = nouveau_crtc(crtc);
++
++ NV_DEBUG_KMS(dev, "\n");
++
++ drm_crtc_cleanup(&nv_crtc->base);
++
++ nv50_cursor_fini(nv_crtc);
++
++ nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo);
++ nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
++ kfree(nv_crtc->mode);
++ kfree(nv_crtc);
++}
++
++int
++nv50_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
++ uint32_t buffer_handle, uint32_t width, uint32_t height)
++{
++ struct drm_device *dev = crtc->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ struct nouveau_bo *cursor = NULL;
++ struct drm_gem_object *gem;
++ int ret = 0, i;
++
++ if (width != 64 || height != 64)
++ return -EINVAL;
++
++ if (!buffer_handle) {
++ nv_crtc->cursor.hide(nv_crtc, true);
++ return 0;
++ }
++
++ gem = drm_gem_object_lookup(dev, file_priv, buffer_handle);
++ if (!gem)
++ return -EINVAL;
++ cursor = nouveau_gem_object(gem);
++
++ ret = nouveau_bo_map(cursor);
++ if (ret)
++ goto out;
++
++ /* The simple will do for now. */
++ for (i = 0; i < 64 * 64; i++)
++ nouveau_bo_wr32(nv_crtc->cursor.nvbo, i, nouveau_bo_rd32(cursor, i));
++
++ nouveau_bo_unmap(cursor);
++
++ nv_crtc->cursor.set_offset(nv_crtc, nv_crtc->cursor.nvbo->bo.offset -
++ dev_priv->vm_vram_base);
++ nv_crtc->cursor.show(nv_crtc, true);
++
++out:
++ mutex_lock(&dev->struct_mutex);
++ drm_gem_object_unreference(gem);
++ mutex_unlock(&dev->struct_mutex);
++ return ret;
++}
++
++int
++nv50_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
++{
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++
++ nv_crtc->cursor.set_pos(nv_crtc, x, y);
++ return 0;
++}
++
++static void
++nv50_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b,
++ uint32_t size)
++{
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ int i;
++
++ if (size != 256)
++ return;
++
++ for (i = 0; i < 256; i++) {
++ nv_crtc->lut.r[i] = r[i];
++ nv_crtc->lut.g[i] = g[i];
++ nv_crtc->lut.b[i] = b[i];
++ }
++
++ /* We need to know the depth before we upload, but it's possible to
++ * get called before a framebuffer is bound. If this is the case,
++ * mark the lut values as dirty by setting depth==0, and it'll be
++ * uploaded on the first mode_set_base()
++ */
++ if (!nv_crtc->base.fb) {
++ nv_crtc->lut.depth = 0;
++ return;
++ }
++
++ nv50_crtc_lut_load(crtc);
++}
++
++static void
++nv50_crtc_save(struct drm_crtc *crtc)
++{
++ NV_ERROR(crtc->dev, "!!\n");
++}
++
++static void
++nv50_crtc_restore(struct drm_crtc *crtc)
++{
++ NV_ERROR(crtc->dev, "!!\n");
++}
++
++static const struct drm_crtc_funcs nv50_crtc_funcs = {
++ .save = nv50_crtc_save,
++ .restore = nv50_crtc_restore,
++ .cursor_set = nv50_crtc_cursor_set,
++ .cursor_move = nv50_crtc_cursor_move,
++ .gamma_set = nv50_crtc_gamma_set,
++ .set_config = drm_crtc_helper_set_config,
++ .destroy = nv50_crtc_destroy,
++};
++
++static void
++nv50_crtc_dpms(struct drm_crtc *crtc, int mode)
++{
++}
++
++static void
++nv50_crtc_prepare(struct drm_crtc *crtc)
++{
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ struct drm_device *dev = crtc->dev;
++ struct drm_encoder *encoder;
++ uint32_t dac = 0, sor = 0;
++
++ NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
++
++ /* Disconnect all unused encoders. */
++ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++
++ if (!drm_helper_encoder_in_use(encoder))
++ continue;
++
++ if (nv_encoder->dcb->type == OUTPUT_ANALOG ||
++ nv_encoder->dcb->type == OUTPUT_TV)
++ dac |= (1 << nv_encoder->or);
++ else
++ sor |= (1 << nv_encoder->or);
++ }
++
++ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++
++ if (nv_encoder->dcb->type == OUTPUT_ANALOG ||
++ nv_encoder->dcb->type == OUTPUT_TV) {
++ if (dac & (1 << nv_encoder->or))
++ continue;
++ } else {
++ if (sor & (1 << nv_encoder->or))
++ continue;
++ }
++
++ nv_encoder->disconnect(nv_encoder);
++ }
++
++ nv50_crtc_blank(nv_crtc, true);
++}
++
++static void
++nv50_crtc_commit(struct drm_crtc *crtc)
++{
++ struct drm_crtc *crtc2;
++ struct drm_device *dev = crtc->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *evo = dev_priv->evo;
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ int ret;
++
++ NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
++
++ nv50_crtc_blank(nv_crtc, false);
++
++ /* Explicitly blank all unused crtc's. */
++ list_for_each_entry(crtc2, &dev->mode_config.crtc_list, head) {
++ if (!drm_helper_crtc_in_use(crtc2))
++ nv50_crtc_blank(nouveau_crtc(crtc2), true);
++ }
++
++ ret = RING_SPACE(evo, 2);
++ if (ret) {
++ NV_ERROR(dev, "no space while committing crtc\n");
++ return;
++ }
++ BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
++ OUT_RING(evo, 0);
++ FIRE_RING(evo);
++}
++
++static bool
++nv50_crtc_mode_fixup(struct drm_crtc *crtc, struct drm_display_mode *mode,
++ struct drm_display_mode *adjusted_mode)
++{
++ return true;
++}
++
++static int
++nv50_crtc_do_mode_set_base(struct drm_crtc *crtc, int x, int y,
++ struct drm_framebuffer *old_fb, bool update)
++{
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ struct drm_device *dev = nv_crtc->base.dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *evo = dev_priv->evo;
++ struct drm_framebuffer *drm_fb = nv_crtc->base.fb;
++ struct nouveau_framebuffer *fb = nouveau_framebuffer(drm_fb);
++ int ret, format;
++
++ NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
++
++ switch (drm_fb->depth) {
++ case 8:
++ format = NV50_EVO_CRTC_FB_DEPTH_8;
++ break;
++ case 15:
++ format = NV50_EVO_CRTC_FB_DEPTH_15;
++ break;
++ case 16:
++ format = NV50_EVO_CRTC_FB_DEPTH_16;
++ break;
++ case 24:
++ case 32:
++ format = NV50_EVO_CRTC_FB_DEPTH_24;
++ break;
++ case 30:
++ format = NV50_EVO_CRTC_FB_DEPTH_30;
++ break;
++ default:
++ NV_ERROR(dev, "unknown depth %d\n", drm_fb->depth);
++ return -EINVAL;
++ }
++
++ ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM);
++ if (ret)
++ return ret;
++
++ if (old_fb) {
++ struct nouveau_framebuffer *ofb = nouveau_framebuffer(old_fb);
++ nouveau_bo_unpin(ofb->nvbo);
++ }
++
++ nv_crtc->fb.offset = fb->nvbo->bo.offset - dev_priv->vm_vram_base;
++ nv_crtc->fb.tile_flags = fb->nvbo->tile_flags;
++ nv_crtc->fb.cpp = drm_fb->bits_per_pixel / 8;
++ if (!nv_crtc->fb.blanked && dev_priv->chipset != 0x50) {
++ ret = RING_SPACE(evo, 2);
++ if (ret)
++ return ret;
++
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_DMA), 1);
++ if (nv_crtc->fb.tile_flags == 0x7a00)
++ OUT_RING(evo, NvEvoFB32);
++ else
++ if (nv_crtc->fb.tile_flags == 0x7000)
++ OUT_RING(evo, NvEvoFB16);
++ else
++ OUT_RING(evo, NvEvoVRAM);
++ }
++
++ ret = RING_SPACE(evo, 12);
++ if (ret)
++ return ret;
++
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_OFFSET), 5);
++ OUT_RING(evo, nv_crtc->fb.offset >> 8);
++ OUT_RING(evo, 0);
++ OUT_RING(evo, (drm_fb->height << 16) | drm_fb->width);
++ if (!nv_crtc->fb.tile_flags) {
++ OUT_RING(evo, drm_fb->pitch | (1 << 20));
++ } else {
++ OUT_RING(evo, ((drm_fb->pitch / 4) << 4) |
++ fb->nvbo->tile_mode);
++ }
++ if (dev_priv->chipset == 0x50)
++ OUT_RING(evo, (fb->nvbo->tile_flags << 8) | format);
++ else
++ OUT_RING(evo, format);
++
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CLUT_MODE), 1);
++ OUT_RING(evo, fb->base.depth == 8 ?
++ NV50_EVO_CRTC_CLUT_MODE_OFF : NV50_EVO_CRTC_CLUT_MODE_ON);
++
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, COLOR_CTRL), 1);
++ OUT_RING(evo, NV50_EVO_CRTC_COLOR_CTRL_COLOR);
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, FB_POS), 1);
++ OUT_RING(evo, (y << 16) | x);
++
++ if (nv_crtc->lut.depth != fb->base.depth) {
++ nv_crtc->lut.depth = fb->base.depth;
++ nv50_crtc_lut_load(crtc);
++ }
++
++ if (update) {
++ ret = RING_SPACE(evo, 2);
++ if (ret)
++ return ret;
++ BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
++ OUT_RING(evo, 0);
++ FIRE_RING(evo);
++ }
++
++ return 0;
++}
++
++static int
++nv50_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
++ struct drm_display_mode *adjusted_mode, int x, int y,
++ struct drm_framebuffer *old_fb)
++{
++ struct drm_device *dev = crtc->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *evo = dev_priv->evo;
++ struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
++ struct nouveau_connector *nv_connector = NULL;
++ uint32_t hsync_dur, vsync_dur, hsync_start_to_end, vsync_start_to_end;
++ uint32_t hunk1, vunk1, vunk2a, vunk2b;
++ int ret;
++
++ /* Find the connector attached to this CRTC */
++ nv_connector = nouveau_crtc_connector_get(nv_crtc);
++
++ *nv_crtc->mode = *adjusted_mode;
++
++ NV_DEBUG_KMS(dev, "index %d\n", nv_crtc->index);
++
++ hsync_dur = adjusted_mode->hsync_end - adjusted_mode->hsync_start;
++ vsync_dur = adjusted_mode->vsync_end - adjusted_mode->vsync_start;
++ hsync_start_to_end = adjusted_mode->htotal - adjusted_mode->hsync_start;
++ vsync_start_to_end = adjusted_mode->vtotal - adjusted_mode->vsync_start;
++ /* I can't give this a proper name, anyone else can? */
++ hunk1 = adjusted_mode->htotal -
++ adjusted_mode->hsync_start + adjusted_mode->hdisplay;
++ vunk1 = adjusted_mode->vtotal -
++ adjusted_mode->vsync_start + adjusted_mode->vdisplay;
++ /* Another strange value, this time only for interlaced adjusted_modes. */
++ vunk2a = 2 * adjusted_mode->vtotal -
++ adjusted_mode->vsync_start + adjusted_mode->vdisplay;
++ vunk2b = adjusted_mode->vtotal -
++ adjusted_mode->vsync_start + adjusted_mode->vtotal;
++
++ if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
++ vsync_dur /= 2;
++ vsync_start_to_end /= 2;
++ vunk1 /= 2;
++ vunk2a /= 2;
++ vunk2b /= 2;
++ /* magic */
++ if (adjusted_mode->flags & DRM_MODE_FLAG_DBLSCAN) {
++ vsync_start_to_end -= 1;
++ vunk1 -= 1;
++ vunk2a -= 1;
++ vunk2b -= 1;
++ }
++ }
++
++ ret = RING_SPACE(evo, 17);
++ if (ret)
++ return ret;
++
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CLOCK), 2);
++ OUT_RING(evo, adjusted_mode->clock | 0x800000);
++ OUT_RING(evo, (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) ? 2 : 0);
++
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, DISPLAY_START), 5);
++ OUT_RING(evo, 0);
++ OUT_RING(evo, (adjusted_mode->vtotal << 16) | adjusted_mode->htotal);
++ OUT_RING(evo, (vsync_dur - 1) << 16 | (hsync_dur - 1));
++ OUT_RING(evo, (vsync_start_to_end - 1) << 16 |
++ (hsync_start_to_end - 1));
++ OUT_RING(evo, (vunk1 - 1) << 16 | (hunk1 - 1));
++
++ if (adjusted_mode->flags & DRM_MODE_FLAG_INTERLACE) {
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK0824), 1);
++ OUT_RING(evo, (vunk2b - 1) << 16 | (vunk2a - 1));
++ } else {
++ OUT_RING(evo, 0);
++ OUT_RING(evo, 0);
++ }
++
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, UNK082C), 1);
++ OUT_RING(evo, 0);
++
++ /* This is the actual resolution of the mode. */
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, REAL_RES), 1);
++ OUT_RING(evo, (mode->vdisplay << 16) | mode->hdisplay);
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, SCALE_CENTER_OFFSET), 1);
++ OUT_RING(evo, NV50_EVO_CRTC_SCALE_CENTER_OFFSET_VAL(0, 0));
++
++ nv_crtc->set_dither(nv_crtc, nv_connector->use_dithering, false);
++ nv_crtc->set_scale(nv_crtc, nv_connector->scaling_mode, false);
++
++ return nv50_crtc_do_mode_set_base(crtc, x, y, old_fb, false);
++}
++
++static int
++nv50_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
++ struct drm_framebuffer *old_fb)
++{
++ return nv50_crtc_do_mode_set_base(crtc, x, y, old_fb, true);
++}
++
++static const struct drm_crtc_helper_funcs nv50_crtc_helper_funcs = {
++ .dpms = nv50_crtc_dpms,
++ .prepare = nv50_crtc_prepare,
++ .commit = nv50_crtc_commit,
++ .mode_fixup = nv50_crtc_mode_fixup,
++ .mode_set = nv50_crtc_mode_set,
++ .mode_set_base = nv50_crtc_mode_set_base,
++ .load_lut = nv50_crtc_lut_load,
++};
++
++int
++nv50_crtc_create(struct drm_device *dev, int index)
++{
++ struct nouveau_crtc *nv_crtc = NULL;
++ int ret, i;
++
++ NV_DEBUG_KMS(dev, "\n");
++
++ nv_crtc = kzalloc(sizeof(*nv_crtc), GFP_KERNEL);
++ if (!nv_crtc)
++ return -ENOMEM;
++
++ nv_crtc->mode = kzalloc(sizeof(*nv_crtc->mode), GFP_KERNEL);
++ if (!nv_crtc->mode) {
++ kfree(nv_crtc);
++ return -ENOMEM;
++ }
++
++ /* Default CLUT parameters, will be activated on the hw upon
++ * first mode set.
++ */
++ for (i = 0; i < 256; i++) {
++ nv_crtc->lut.r[i] = i << 8;
++ nv_crtc->lut.g[i] = i << 8;
++ nv_crtc->lut.b[i] = i << 8;
++ }
++ nv_crtc->lut.depth = 0;
++
++ ret = nouveau_bo_new(dev, NULL, 4096, 0x100, TTM_PL_FLAG_VRAM,
++ 0, 0x0000, false, true, &nv_crtc->lut.nvbo);
++ if (!ret) {
++ ret = nouveau_bo_pin(nv_crtc->lut.nvbo, TTM_PL_FLAG_VRAM);
++ if (!ret)
++ ret = nouveau_bo_map(nv_crtc->lut.nvbo);
++ if (ret)
++ nouveau_bo_ref(NULL, &nv_crtc->lut.nvbo);
++ }
++
++ if (ret) {
++ kfree(nv_crtc->mode);
++ kfree(nv_crtc);
++ return ret;
++ }
++
++ nv_crtc->index = index;
++
++ /* set function pointers */
++ nv_crtc->set_dither = nv50_crtc_set_dither;
++ nv_crtc->set_scale = nv50_crtc_set_scale;
++
++ drm_crtc_init(dev, &nv_crtc->base, &nv50_crtc_funcs);
++ drm_crtc_helper_add(&nv_crtc->base, &nv50_crtc_helper_funcs);
++ drm_mode_crtc_set_gamma_size(&nv_crtc->base, 256);
++
++ ret = nouveau_bo_new(dev, NULL, 64*64*4, 0x100, TTM_PL_FLAG_VRAM,
++ 0, 0x0000, false, true, &nv_crtc->cursor.nvbo);
++ if (!ret) {
++ ret = nouveau_bo_pin(nv_crtc->cursor.nvbo, TTM_PL_FLAG_VRAM);
++ if (!ret)
++ ret = nouveau_bo_map(nv_crtc->cursor.nvbo);
++ if (ret)
++ nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
++ }
++
++ nv50_cursor_init(nv_crtc);
++ return 0;
++}
+diff --git a/drivers/gpu/drm/nouveau/nv50_cursor.c b/drivers/gpu/drm/nouveau/nv50_cursor.c
+new file mode 100644
+index 0000000..753e723
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv50_cursor.c
+@@ -0,0 +1,156 @@
++/*
++ * Copyright (C) 2008 Maarten Maathuis.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "drm_mode.h"
++
++#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
++#include "nouveau_reg.h"
++#include "nouveau_drv.h"
++#include "nouveau_crtc.h"
++#include "nv50_display.h"
++
++static void
++nv50_cursor_show(struct nouveau_crtc *nv_crtc, bool update)
++{
++ struct drm_nouveau_private *dev_priv = nv_crtc->base.dev->dev_private;
++ struct nouveau_channel *evo = dev_priv->evo;
++ struct drm_device *dev = nv_crtc->base.dev;
++ int ret;
++
++ NV_DEBUG_KMS(dev, "\n");
++
++ if (update && nv_crtc->cursor.visible)
++ return;
++
++ ret = RING_SPACE(evo, (dev_priv->chipset != 0x50 ? 5 : 3) + update * 2);
++ if (ret) {
++ NV_ERROR(dev, "no space while unhiding cursor\n");
++ return;
++ }
++
++ if (dev_priv->chipset != 0x50) {
++ BEGIN_RING(evo, 0, NV84_EVO_CRTC(nv_crtc->index, CURSOR_DMA), 1);
++ OUT_RING(evo, NvEvoVRAM);
++ }
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CURSOR_CTRL), 2);
++ OUT_RING(evo, NV50_EVO_CRTC_CURSOR_CTRL_SHOW);
++ OUT_RING(evo, nv_crtc->cursor.offset >> 8);
++
++ if (update) {
++ BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
++ OUT_RING(evo, 0);
++ FIRE_RING(evo);
++ nv_crtc->cursor.visible = true;
++ }
++}
++
++static void
++nv50_cursor_hide(struct nouveau_crtc *nv_crtc, bool update)
++{
++ struct drm_nouveau_private *dev_priv = nv_crtc->base.dev->dev_private;
++ struct nouveau_channel *evo = dev_priv->evo;
++ struct drm_device *dev = nv_crtc->base.dev;
++ int ret;
++
++ NV_DEBUG_KMS(dev, "\n");
++
++ if (update && !nv_crtc->cursor.visible)
++ return;
++
++ ret = RING_SPACE(evo, (dev_priv->chipset != 0x50 ? 5 : 3) + update * 2);
++ if (ret) {
++ NV_ERROR(dev, "no space while hiding cursor\n");
++ return;
++ }
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(nv_crtc->index, CURSOR_CTRL), 2);
++ OUT_RING(evo, NV50_EVO_CRTC_CURSOR_CTRL_HIDE);
++ OUT_RING(evo, 0);
++ if (dev_priv->chipset != 0x50) {
++ BEGIN_RING(evo, 0, NV84_EVO_CRTC(nv_crtc->index, CURSOR_DMA), 1);
++ OUT_RING(evo, NV84_EVO_CRTC_CURSOR_DMA_HANDLE_NONE);
++ }
++
++ if (update) {
++ BEGIN_RING(evo, 0, NV50_EVO_UPDATE, 1);
++ OUT_RING(evo, 0);
++ FIRE_RING(evo);
++ nv_crtc->cursor.visible = false;
++ }
++}
++
++static void
++nv50_cursor_set_pos(struct nouveau_crtc *nv_crtc, int x, int y)
++{
++ struct drm_device *dev = nv_crtc->base.dev;
++
++ nv_wr32(dev, NV50_PDISPLAY_CURSOR_USER_POS(nv_crtc->index),
++ ((y & 0xFFFF) << 16) | (x & 0xFFFF));
++ /* Needed to make the cursor move. */
++ nv_wr32(dev, NV50_PDISPLAY_CURSOR_USER_POS_CTRL(nv_crtc->index), 0);
++}
++
++static void
++nv50_cursor_set_offset(struct nouveau_crtc *nv_crtc, uint32_t offset)
++{
++ NV_DEBUG_KMS(nv_crtc->base.dev, "\n");
++ if (offset == nv_crtc->cursor.offset)
++ return;
++
++ nv_crtc->cursor.offset = offset;
++ if (nv_crtc->cursor.visible) {
++ nv_crtc->cursor.visible = false;
++ nv_crtc->cursor.show(nv_crtc, true);
++ }
++}
++
++int
++nv50_cursor_init(struct nouveau_crtc *nv_crtc)
++{
++ nv_crtc->cursor.set_offset = nv50_cursor_set_offset;
++ nv_crtc->cursor.set_pos = nv50_cursor_set_pos;
++ nv_crtc->cursor.hide = nv50_cursor_hide;
++ nv_crtc->cursor.show = nv50_cursor_show;
++ return 0;
++}
++
++void
++nv50_cursor_fini(struct nouveau_crtc *nv_crtc)
++{
++ struct drm_device *dev = nv_crtc->base.dev;
++ int idx = nv_crtc->index;
++
++ NV_DEBUG_KMS(dev, "\n");
++
++ nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx), 0);
++ if (!nv_wait(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx),
++ NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) {
++ NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n");
++ NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n",
++ nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(idx)));
++ }
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nv50_dac.c b/drivers/gpu/drm/nouveau/nv50_dac.c
+new file mode 100644
+index 0000000..f08f042
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv50_dac.c
+@@ -0,0 +1,304 @@
++/*
++ * Copyright (C) 2008 Maarten Maathuis.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "drm_crtc_helper.h"
++
++#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
++#include "nouveau_reg.h"
++#include "nouveau_drv.h"
++#include "nouveau_dma.h"
++#include "nouveau_encoder.h"
++#include "nouveau_connector.h"
++#include "nouveau_crtc.h"
++#include "nv50_display.h"
++
++static void
++nv50_dac_disconnect(struct nouveau_encoder *nv_encoder)
++{
++ struct drm_device *dev = to_drm_encoder(nv_encoder)->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *evo = dev_priv->evo;
++ int ret;
++
++ NV_DEBUG_KMS(dev, "Disconnecting DAC %d\n", nv_encoder->or);
++
++ ret = RING_SPACE(evo, 2);
++ if (ret) {
++ NV_ERROR(dev, "no space while disconnecting DAC\n");
++ return;
++ }
++ BEGIN_RING(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 1);
++ OUT_RING(evo, 0);
++}
++
++static enum drm_connector_status
++nv50_dac_detect(struct drm_encoder *encoder, struct drm_connector *connector)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_device *dev = encoder->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ enum drm_connector_status status = connector_status_disconnected;
++ uint32_t dpms_state, load_pattern, load_state;
++ int or = nv_encoder->or;
++
++ nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(or), 0x00000001);
++ dpms_state = nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or));
++
++ nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or),
++ 0x00150000 | NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING);
++ if (!nv_wait(NV50_PDISPLAY_DAC_DPMS_CTRL(or),
++ NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING, 0)) {
++ NV_ERROR(dev, "timeout: DAC_DPMS_CTRL_PENDING(%d) == 0\n", or);
++ NV_ERROR(dev, "DAC_DPMS_CTRL(%d) = 0x%08x\n", or,
++ nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or)));
++ return status;
++ }
++
++ /* Use bios provided value if possible. */
++ if (dev_priv->vbios->dactestval) {
++ load_pattern = dev_priv->vbios->dactestval;
++ NV_DEBUG_KMS(dev, "Using bios provided load_pattern of %d\n",
++ load_pattern);
++ } else {
++ load_pattern = 340;
++ NV_DEBUG_KMS(dev, "Using default load_pattern of %d\n",
++ load_pattern);
++ }
++
++ nv_wr32(dev, NV50_PDISPLAY_DAC_LOAD_CTRL(or),
++ NV50_PDISPLAY_DAC_LOAD_CTRL_ACTIVE | load_pattern);
++ mdelay(45); /* give it some time to process */
++ load_state = nv_rd32(dev, NV50_PDISPLAY_DAC_LOAD_CTRL(or));
++
++ nv_wr32(dev, NV50_PDISPLAY_DAC_LOAD_CTRL(or), 0);
++ nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or), dpms_state |
++ NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING);
++
++ if ((load_state & NV50_PDISPLAY_DAC_LOAD_CTRL_PRESENT) ==
++ NV50_PDISPLAY_DAC_LOAD_CTRL_PRESENT)
++ status = connector_status_connected;
++
++ if (status == connector_status_connected)
++ NV_DEBUG_KMS(dev, "Load was detected on output with or %d\n", or);
++ else
++ NV_DEBUG_KMS(dev, "Load was not detected on output with or %d\n", or);
++
++ return status;
++}
++
++static void
++nv50_dac_dpms(struct drm_encoder *encoder, int mode)
++{
++ struct drm_device *dev = encoder->dev;
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ uint32_t val;
++ int or = nv_encoder->or;
++
++ NV_DEBUG_KMS(dev, "or %d mode %d\n", or, mode);
++
++ /* wait for it to be done */
++ if (!nv_wait(NV50_PDISPLAY_DAC_DPMS_CTRL(or),
++ NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING, 0)) {
++ NV_ERROR(dev, "timeout: DAC_DPMS_CTRL_PENDING(%d) == 0\n", or);
++ NV_ERROR(dev, "DAC_DPMS_CTRL(%d) = 0x%08x\n", or,
++ nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or)));
++ return;
++ }
++
++ val = nv_rd32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or)) & ~0x7F;
++
++ if (mode != DRM_MODE_DPMS_ON)
++ val |= NV50_PDISPLAY_DAC_DPMS_CTRL_BLANKED;
++
++ switch (mode) {
++ case DRM_MODE_DPMS_STANDBY:
++ val |= NV50_PDISPLAY_DAC_DPMS_CTRL_HSYNC_OFF;
++ break;
++ case DRM_MODE_DPMS_SUSPEND:
++ val |= NV50_PDISPLAY_DAC_DPMS_CTRL_VSYNC_OFF;
++ break;
++ case DRM_MODE_DPMS_OFF:
++ val |= NV50_PDISPLAY_DAC_DPMS_CTRL_OFF;
++ val |= NV50_PDISPLAY_DAC_DPMS_CTRL_HSYNC_OFF;
++ val |= NV50_PDISPLAY_DAC_DPMS_CTRL_VSYNC_OFF;
++ break;
++ default:
++ break;
++ }
++
++ nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(or), val |
++ NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING);
++}
++
++static void
++nv50_dac_save(struct drm_encoder *encoder)
++{
++ NV_ERROR(encoder->dev, "!!\n");
++}
++
++static void
++nv50_dac_restore(struct drm_encoder *encoder)
++{
++ NV_ERROR(encoder->dev, "!!\n");
++}
++
++static bool
++nv50_dac_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
++ struct drm_display_mode *adjusted_mode)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct nouveau_connector *connector;
++
++ NV_DEBUG_KMS(encoder->dev, "or %d\n", nv_encoder->or);
++
++ connector = nouveau_encoder_connector_get(nv_encoder);
++ if (!connector) {
++ NV_ERROR(encoder->dev, "Encoder has no connector\n");
++ return false;
++ }
++
++ if (connector->scaling_mode != DRM_MODE_SCALE_NONE &&
++ connector->native_mode) {
++ int id = adjusted_mode->base.id;
++ *adjusted_mode = *connector->native_mode;
++ adjusted_mode->base.id = id;
++ }
++
++ return true;
++}
++
++static void
++nv50_dac_prepare(struct drm_encoder *encoder)
++{
++}
++
++static void
++nv50_dac_commit(struct drm_encoder *encoder)
++{
++}
++
++static void
++nv50_dac_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
++ struct drm_display_mode *adjusted_mode)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_device *dev = encoder->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *evo = dev_priv->evo;
++ struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc);
++ uint32_t mode_ctl = 0, mode_ctl2 = 0;
++ int ret;
++
++ NV_DEBUG_KMS(dev, "or %d\n", nv_encoder->or);
++
++ nv50_dac_dpms(encoder, DRM_MODE_DPMS_ON);
++
++ if (crtc->index == 1)
++ mode_ctl |= NV50_EVO_DAC_MODE_CTRL_CRTC1;
++ else
++ mode_ctl |= NV50_EVO_DAC_MODE_CTRL_CRTC0;
++
++ /* Lacking a working tv-out, this is not a 100% sure. */
++ if (nv_encoder->dcb->type == OUTPUT_ANALOG)
++ mode_ctl |= 0x40;
++ else
++ if (nv_encoder->dcb->type == OUTPUT_TV)
++ mode_ctl |= 0x100;
++
++ if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
++ mode_ctl2 |= NV50_EVO_DAC_MODE_CTRL2_NHSYNC;
++
++ if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
++ mode_ctl2 |= NV50_EVO_DAC_MODE_CTRL2_NVSYNC;
++
++ ret = RING_SPACE(evo, 3);
++ if (ret) {
++ NV_ERROR(dev, "no space while connecting DAC\n");
++ return;
++ }
++ BEGIN_RING(evo, 0, NV50_EVO_DAC(nv_encoder->or, MODE_CTRL), 2);
++ OUT_RING(evo, mode_ctl);
++ OUT_RING(evo, mode_ctl2);
++}
++
++static const struct drm_encoder_helper_funcs nv50_dac_helper_funcs = {
++ .dpms = nv50_dac_dpms,
++ .save = nv50_dac_save,
++ .restore = nv50_dac_restore,
++ .mode_fixup = nv50_dac_mode_fixup,
++ .prepare = nv50_dac_prepare,
++ .commit = nv50_dac_commit,
++ .mode_set = nv50_dac_mode_set,
++ .detect = nv50_dac_detect
++};
++
++static void
++nv50_dac_destroy(struct drm_encoder *encoder)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++
++ if (!encoder)
++ return;
++
++ NV_DEBUG_KMS(encoder->dev, "\n");
++
++ drm_encoder_cleanup(encoder);
++ kfree(nv_encoder);
++}
++
++static const struct drm_encoder_funcs nv50_dac_encoder_funcs = {
++ .destroy = nv50_dac_destroy,
++};
++
++int
++nv50_dac_create(struct drm_device *dev, struct dcb_entry *entry)
++{
++ struct nouveau_encoder *nv_encoder;
++ struct drm_encoder *encoder;
++
++ NV_DEBUG_KMS(dev, "\n");
++ NV_INFO(dev, "Detected a DAC output\n");
++
++ nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
++ if (!nv_encoder)
++ return -ENOMEM;
++ encoder = to_drm_encoder(nv_encoder);
++
++ nv_encoder->dcb = entry;
++ nv_encoder->or = ffs(entry->or) - 1;
++
++ nv_encoder->disconnect = nv50_dac_disconnect;
++
++ drm_encoder_init(dev, encoder, &nv50_dac_encoder_funcs,
++ DRM_MODE_ENCODER_DAC);
++ drm_encoder_helper_add(encoder, &nv50_dac_helper_funcs);
++
++ encoder->possible_crtcs = entry->heads;
++ encoder->possible_clones = 0;
++ return 0;
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nv50_display.c b/drivers/gpu/drm/nouveau/nv50_display.c
+new file mode 100644
+index 0000000..90f0bf5
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv50_display.c
+@@ -0,0 +1,1032 @@
++/*
++ * Copyright (C) 2008 Maarten Maathuis.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "nv50_display.h"
++#include "nouveau_crtc.h"
++#include "nouveau_encoder.h"
++#include "nouveau_connector.h"
++#include "nouveau_fb.h"
++#include "drm_crtc_helper.h"
++
++static void
++nv50_evo_channel_del(struct nouveau_channel **pchan)
++{
++ struct nouveau_channel *chan = *pchan;
++
++ if (!chan)
++ return;
++ *pchan = NULL;
++
++ nouveau_gpuobj_channel_takedown(chan);
++ nouveau_bo_ref(NULL, &chan->pushbuf_bo);
++
++ if (chan->user)
++ iounmap(chan->user);
++
++ kfree(chan);
++}
++
++static int
++nv50_evo_dmaobj_new(struct nouveau_channel *evo, uint32_t class, uint32_t name,
++ uint32_t tile_flags, uint32_t magic_flags,
++ uint32_t offset, uint32_t limit)
++{
++ struct drm_nouveau_private *dev_priv = evo->dev->dev_private;
++ struct drm_device *dev = evo->dev;
++ struct nouveau_gpuobj *obj = NULL;
++ int ret;
++
++ ret = nouveau_gpuobj_new(dev, evo, 6*4, 32, 0, &obj);
++ if (ret)
++ return ret;
++ obj->engine = NVOBJ_ENGINE_DISPLAY;
++
++ ret = nouveau_gpuobj_ref_add(dev, evo, name, obj, NULL);
++ if (ret) {
++ nouveau_gpuobj_del(dev, &obj);
++ return ret;
++ }
++
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ nv_wo32(dev, obj, 0, (tile_flags << 22) | (magic_flags << 16) | class);
++ nv_wo32(dev, obj, 1, limit);
++ nv_wo32(dev, obj, 2, offset);
++ nv_wo32(dev, obj, 3, 0x00000000);
++ nv_wo32(dev, obj, 4, 0x00000000);
++ nv_wo32(dev, obj, 5, 0x00010000);
++ dev_priv->engine.instmem.finish_access(dev);
++
++ return 0;
++}
++
++static int
++nv50_evo_channel_new(struct drm_device *dev, struct nouveau_channel **pchan)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *chan;
++ int ret;
++
++ chan = kzalloc(sizeof(struct nouveau_channel), GFP_KERNEL);
++ if (!chan)
++ return -ENOMEM;
++ *pchan = chan;
++
++ chan->id = -1;
++ chan->dev = dev;
++ chan->user_get = 4;
++ chan->user_put = 0;
++
++ INIT_LIST_HEAD(&chan->ramht_refs);
++
++ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 32768, 0x1000,
++ NVOBJ_FLAG_ZERO_ALLOC, &chan->ramin);
++ if (ret) {
++ NV_ERROR(dev, "Error allocating EVO channel memory: %d\n", ret);
++ nv50_evo_channel_del(pchan);
++ return ret;
++ }
++
++ ret = nouveau_mem_init_heap(&chan->ramin_heap, chan->ramin->gpuobj->
++ im_pramin->start, 32768);
++ if (ret) {
++ NV_ERROR(dev, "Error initialising EVO PRAMIN heap: %d\n", ret);
++ nv50_evo_channel_del(pchan);
++ return ret;
++ }
++
++ ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, 4096, 16,
++ 0, &chan->ramht);
++ if (ret) {
++ NV_ERROR(dev, "Unable to allocate EVO RAMHT: %d\n", ret);
++ nv50_evo_channel_del(pchan);
++ return ret;
++ }
++
++ if (dev_priv->chipset != 0x50) {
++ ret = nv50_evo_dmaobj_new(chan, 0x3d, NvEvoFB16, 0x70, 0x19,
++ 0, 0xffffffff);
++ if (ret) {
++ nv50_evo_channel_del(pchan);
++ return ret;
++ }
++
++
++ ret = nv50_evo_dmaobj_new(chan, 0x3d, NvEvoFB32, 0x7a, 0x19,
++ 0, 0xffffffff);
++ if (ret) {
++ nv50_evo_channel_del(pchan);
++ return ret;
++ }
++ }
++
++ ret = nv50_evo_dmaobj_new(chan, 0x3d, NvEvoVRAM, 0, 0x19,
++ 0, nouveau_mem_fb_amount(dev));
++ if (ret) {
++ nv50_evo_channel_del(pchan);
++ return ret;
++ }
++
++ ret = nouveau_bo_new(dev, NULL, 4096, 0, TTM_PL_FLAG_VRAM, 0, 0,
++ false, true, &chan->pushbuf_bo);
++ if (ret == 0)
++ ret = nouveau_bo_pin(chan->pushbuf_bo, TTM_PL_FLAG_VRAM);
++ if (ret) {
++ NV_ERROR(dev, "Error creating EVO DMA push buffer: %d\n", ret);
++ nv50_evo_channel_del(pchan);
++ return ret;
++ }
++
++ ret = nouveau_bo_map(chan->pushbuf_bo);
++ if (ret) {
++ NV_ERROR(dev, "Error mapping EVO DMA push buffer: %d\n", ret);
++ nv50_evo_channel_del(pchan);
++ return ret;
++ }
++
++ chan->user = ioremap(pci_resource_start(dev->pdev, 0) +
++ NV50_PDISPLAY_USER(0), PAGE_SIZE);
++ if (!chan->user) {
++ NV_ERROR(dev, "Error mapping EVO control regs.\n");
++ nv50_evo_channel_del(pchan);
++ return -ENOMEM;
++ }
++
++ return 0;
++}
++
++int
++nv50_display_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_timer_engine *ptimer = &dev_priv->engine.timer;
++ struct nouveau_channel *evo = dev_priv->evo;
++ struct drm_connector *connector;
++ uint32_t val, ram_amount, hpd_en[2];
++ uint64_t start;
++ int ret, i;
++
++ NV_DEBUG_KMS(dev, "\n");
++
++ nv_wr32(dev, 0x00610184, nv_rd32(dev, 0x00614004));
++ /*
++ * I think the 0x006101XX range is some kind of main control area
++ * that enables things.
++ */
++ /* CRTC? */
++ for (i = 0; i < 2; i++) {
++ val = nv_rd32(dev, 0x00616100 + (i * 0x800));
++ nv_wr32(dev, 0x00610190 + (i * 0x10), val);
++ val = nv_rd32(dev, 0x00616104 + (i * 0x800));
++ nv_wr32(dev, 0x00610194 + (i * 0x10), val);
++ val = nv_rd32(dev, 0x00616108 + (i * 0x800));
++ nv_wr32(dev, 0x00610198 + (i * 0x10), val);
++ val = nv_rd32(dev, 0x0061610c + (i * 0x800));
++ nv_wr32(dev, 0x0061019c + (i * 0x10), val);
++ }
++ /* DAC */
++ for (i = 0; i < 3; i++) {
++ val = nv_rd32(dev, 0x0061a000 + (i * 0x800));
++ nv_wr32(dev, 0x006101d0 + (i * 0x04), val);
++ }
++ /* SOR */
++ for (i = 0; i < 4; i++) {
++ val = nv_rd32(dev, 0x0061c000 + (i * 0x800));
++ nv_wr32(dev, 0x006101e0 + (i * 0x04), val);
++ }
++ /* Something not yet in use, tv-out maybe. */
++ for (i = 0; i < 3; i++) {
++ val = nv_rd32(dev, 0x0061e000 + (i * 0x800));
++ nv_wr32(dev, 0x006101f0 + (i * 0x04), val);
++ }
++
++ for (i = 0; i < 3; i++) {
++ nv_wr32(dev, NV50_PDISPLAY_DAC_DPMS_CTRL(i), 0x00550000 |
++ NV50_PDISPLAY_DAC_DPMS_CTRL_PENDING);
++ nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL1(i), 0x00000001);
++ }
++
++ /* This used to be in crtc unblank, but seems out of place there. */
++ nv_wr32(dev, NV50_PDISPLAY_UNK_380, 0);
++ /* RAM is clamped to 256 MiB. */
++ ram_amount = nouveau_mem_fb_amount(dev);
++ NV_DEBUG_KMS(dev, "ram_amount %d\n", ram_amount);
++ if (ram_amount > 256*1024*1024)
++ ram_amount = 256*1024*1024;
++ nv_wr32(dev, NV50_PDISPLAY_RAM_AMOUNT, ram_amount - 1);
++ nv_wr32(dev, NV50_PDISPLAY_UNK_388, 0x150000);
++ nv_wr32(dev, NV50_PDISPLAY_UNK_38C, 0);
++
++ /* The precise purpose is unknown, i suspect it has something to do
++ * with text mode.
++ */
++ if (nv_rd32(dev, NV50_PDISPLAY_INTR_1) & 0x100) {
++ nv_wr32(dev, NV50_PDISPLAY_INTR_1, 0x100);
++ nv_wr32(dev, 0x006194e8, nv_rd32(dev, 0x006194e8) & ~1);
++ if (!nv_wait(0x006194e8, 2, 0)) {
++ NV_ERROR(dev, "timeout: (0x6194e8 & 2) != 0\n");
++ NV_ERROR(dev, "0x6194e8 = 0x%08x\n",
++ nv_rd32(dev, 0x6194e8));
++ return -EBUSY;
++ }
++ }
++
++ /* taken from nv bug #12637, attempts to un-wedge the hw if it's
++ * stuck in some unspecified state
++ */
++ start = ptimer->read(dev);
++ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0x2b00);
++ while ((val = nv_rd32(dev, NV50_PDISPLAY_CHANNEL_STAT(0))) & 0x1e0000) {
++ if ((val & 0x9f0000) == 0x20000)
++ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0),
++ val | 0x800000);
++
++ if ((val & 0x3f0000) == 0x30000)
++ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0),
++ val | 0x200000);
++
++ if (ptimer->read(dev) - start > 1000000000ULL) {
++ NV_ERROR(dev, "timeout: (0x610200 & 0x1e0000) != 0\n");
++ NV_ERROR(dev, "0x610200 = 0x%08x\n", val);
++ return -EBUSY;
++ }
++ }
++
++ nv_wr32(dev, NV50_PDISPLAY_CTRL_STATE, NV50_PDISPLAY_CTRL_STATE_ENABLE);
++ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0x1000b03);
++ if (!nv_wait(NV50_PDISPLAY_CHANNEL_STAT(0), 0x40000000, 0x40000000)) {
++ NV_ERROR(dev, "timeout: (0x610200 & 0x40000000) == 0x40000000\n");
++ NV_ERROR(dev, "0x610200 = 0x%08x\n",
++ nv_rd32(dev, NV50_PDISPLAY_CHANNEL_STAT(0)));
++ return -EBUSY;
++ }
++
++ for (i = 0; i < 2; i++) {
++ nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i), 0x2000);
++ if (!nv_wait(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
++ NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS, 0)) {
++ NV_ERROR(dev, "timeout: CURSOR_CTRL2_STATUS == 0\n");
++ NV_ERROR(dev, "CURSOR_CTRL2 = 0x%08x\n",
++ nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i)));
++ return -EBUSY;
++ }
++
++ nv_wr32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
++ NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_ON);
++ if (!nv_wait(NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i),
++ NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS,
++ NV50_PDISPLAY_CURSOR_CURSOR_CTRL2_STATUS_ACTIVE)) {
++ NV_ERROR(dev, "timeout: "
++ "CURSOR_CTRL2_STATUS_ACTIVE(%d)\n", i);
++ NV_ERROR(dev, "CURSOR_CTRL2(%d) = 0x%08x\n", i,
++ nv_rd32(dev, NV50_PDISPLAY_CURSOR_CURSOR_CTRL2(i)));
++ return -EBUSY;
++ }
++ }
++
++ nv_wr32(dev, NV50_PDISPLAY_OBJECTS, (evo->ramin->instance >> 8) | 9);
++
++ /* initialise fifo */
++ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_DMA_CB(0),
++ ((evo->pushbuf_bo->bo.mem.mm_node->start << PAGE_SHIFT) >> 8) |
++ NV50_PDISPLAY_CHANNEL_DMA_CB_LOCATION_VRAM |
++ NV50_PDISPLAY_CHANNEL_DMA_CB_VALID);
++ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_UNK2(0), 0x00010000);
++ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_UNK3(0), 0x00000002);
++ if (!nv_wait(0x610200, 0x80000000, 0x00000000)) {
++ NV_ERROR(dev, "timeout: (0x610200 & 0x80000000) == 0\n");
++ NV_ERROR(dev, "0x610200 = 0x%08x\n", nv_rd32(dev, 0x610200));
++ return -EBUSY;
++ }
++ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0),
++ (nv_rd32(dev, NV50_PDISPLAY_CHANNEL_STAT(0)) & ~0x00000003) |
++ NV50_PDISPLAY_CHANNEL_STAT_DMA_ENABLED);
++ nv_wr32(dev, NV50_PDISPLAY_USER_PUT(0), 0);
++ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0x01000003 |
++ NV50_PDISPLAY_CHANNEL_STAT_DMA_ENABLED);
++ nv_wr32(dev, 0x610300, nv_rd32(dev, 0x610300) & ~1);
++
++ evo->dma.max = (4096/4) - 2;
++ evo->dma.put = 0;
++ evo->dma.cur = evo->dma.put;
++ evo->dma.free = evo->dma.max - evo->dma.cur;
++
++ ret = RING_SPACE(evo, NOUVEAU_DMA_SKIPS);
++ if (ret)
++ return ret;
++
++ for (i = 0; i < NOUVEAU_DMA_SKIPS; i++)
++ OUT_RING(evo, 0);
++
++ ret = RING_SPACE(evo, 11);
++ if (ret)
++ return ret;
++ BEGIN_RING(evo, 0, NV50_EVO_UNK84, 2);
++ OUT_RING(evo, NV50_EVO_UNK84_NOTIFY_DISABLED);
++ OUT_RING(evo, NV50_EVO_DMA_NOTIFY_HANDLE_NONE);
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, FB_DMA), 1);
++ OUT_RING(evo, NV50_EVO_CRTC_FB_DMA_HANDLE_NONE);
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK0800), 1);
++ OUT_RING(evo, 0);
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, DISPLAY_START), 1);
++ OUT_RING(evo, 0);
++ BEGIN_RING(evo, 0, NV50_EVO_CRTC(0, UNK082C), 1);
++ OUT_RING(evo, 0);
++ FIRE_RING(evo);
++ if (!nv_wait(0x640004, 0xffffffff, evo->dma.put << 2))
++ NV_ERROR(dev, "evo pushbuf stalled\n");
++
++ /* enable clock change interrupts. */
++ nv_wr32(dev, 0x610028, 0x00010001);
++ nv_wr32(dev, NV50_PDISPLAY_INTR_EN, (NV50_PDISPLAY_INTR_EN_CLK_UNK10 |
++ NV50_PDISPLAY_INTR_EN_CLK_UNK20 |
++ NV50_PDISPLAY_INTR_EN_CLK_UNK40));
++
++ /* enable hotplug interrupts */
++ hpd_en[0] = hpd_en[1] = 0;
++ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
++ struct nouveau_connector *conn = nouveau_connector(connector);
++ struct dcb_gpio_entry *gpio;
++
++ if (connector->connector_type != DRM_MODE_CONNECTOR_DVII &&
++ connector->connector_type != DRM_MODE_CONNECTOR_DVID &&
++ connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort)
++ continue;
++
++ gpio = nouveau_bios_gpio_entry(dev, conn->dcb->gpio_tag);
++ if (!gpio)
++ continue;
++
++ hpd_en[gpio->line >> 4] |= (0x00010001 << (gpio->line & 0xf));
++ }
++
++ nv_wr32(dev, 0xe054, 0xffffffff);
++ nv_wr32(dev, 0xe050, hpd_en[0]);
++ if (dev_priv->chipset >= 0x90) {
++ nv_wr32(dev, 0xe074, 0xffffffff);
++ nv_wr32(dev, 0xe070, hpd_en[1]);
++ }
++
++ return 0;
++}
++
++static int nv50_display_disable(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct drm_crtc *drm_crtc;
++ int ret, i;
++
++ NV_DEBUG_KMS(dev, "\n");
++
++ list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) {
++ struct nouveau_crtc *crtc = nouveau_crtc(drm_crtc);
++
++ nv50_crtc_blank(crtc, true);
++ }
++
++ ret = RING_SPACE(dev_priv->evo, 2);
++ if (ret == 0) {
++ BEGIN_RING(dev_priv->evo, 0, NV50_EVO_UPDATE, 1);
++ OUT_RING(dev_priv->evo, 0);
++ }
++ FIRE_RING(dev_priv->evo);
++
++ /* Almost like ack'ing a vblank interrupt, maybe in the spirit of
++ * cleaning up?
++ */
++ list_for_each_entry(drm_crtc, &dev->mode_config.crtc_list, head) {
++ struct nouveau_crtc *crtc = nouveau_crtc(drm_crtc);
++ uint32_t mask = NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(crtc->index);
++
++ if (!crtc->base.enabled)
++ continue;
++
++ nv_wr32(dev, NV50_PDISPLAY_INTR_1, mask);
++ if (!nv_wait(NV50_PDISPLAY_INTR_1, mask, mask)) {
++ NV_ERROR(dev, "timeout: (0x610024 & 0x%08x) == "
++ "0x%08x\n", mask, mask);
++ NV_ERROR(dev, "0x610024 = 0x%08x\n",
++ nv_rd32(dev, NV50_PDISPLAY_INTR_1));
++ }
++ }
++
++ nv_wr32(dev, NV50_PDISPLAY_CHANNEL_STAT(0), 0);
++ nv_wr32(dev, NV50_PDISPLAY_CTRL_STATE, 0);
++ if (!nv_wait(NV50_PDISPLAY_CHANNEL_STAT(0), 0x1e0000, 0)) {
++ NV_ERROR(dev, "timeout: (0x610200 & 0x1e0000) == 0\n");
++ NV_ERROR(dev, "0x610200 = 0x%08x\n",
++ nv_rd32(dev, NV50_PDISPLAY_CHANNEL_STAT(0)));
++ }
++
++ for (i = 0; i < 3; i++) {
++ if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_STATE(i),
++ NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) {
++ NV_ERROR(dev, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", i);
++ NV_ERROR(dev, "SOR_DPMS_STATE(%d) = 0x%08x\n", i,
++ nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_STATE(i)));
++ }
++ }
++
++ /* disable interrupts. */
++ nv_wr32(dev, NV50_PDISPLAY_INTR_EN, 0x00000000);
++
++ /* disable hotplug interrupts */
++ nv_wr32(dev, 0xe054, 0xffffffff);
++ nv_wr32(dev, 0xe050, 0x00000000);
++ if (dev_priv->chipset >= 0x90) {
++ nv_wr32(dev, 0xe074, 0xffffffff);
++ nv_wr32(dev, 0xe070, 0x00000000);
++ }
++ return 0;
++}
++
++int nv50_display_create(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct parsed_dcb *dcb = dev_priv->vbios->dcb;
++ uint32_t connector[16] = {};
++ int ret, i;
++
++ NV_DEBUG_KMS(dev, "\n");
++
++ /* init basic kernel modesetting */
++ drm_mode_config_init(dev);
++
++ /* Initialise some optional connector properties. */
++ drm_mode_create_scaling_mode_property(dev);
++ drm_mode_create_dithering_property(dev);
++
++ dev->mode_config.min_width = 0;
++ dev->mode_config.min_height = 0;
++
++ dev->mode_config.funcs = (void *)&nouveau_mode_config_funcs;
++
++ dev->mode_config.max_width = 8192;
++ dev->mode_config.max_height = 8192;
++
++ dev->mode_config.fb_base = dev_priv->fb_phys;
++
++ /* Create EVO channel */
++ ret = nv50_evo_channel_new(dev, &dev_priv->evo);
++ if (ret) {
++ NV_ERROR(dev, "Error creating EVO channel: %d\n", ret);
++ return ret;
++ }
++
++ /* Create CRTC objects */
++ for (i = 0; i < 2; i++)
++ nv50_crtc_create(dev, i);
++
++ /* We setup the encoders from the BIOS table */
++ for (i = 0 ; i < dcb->entries; i++) {
++ struct dcb_entry *entry = &dcb->entry[i];
++
++ if (entry->location != DCB_LOC_ON_CHIP) {
++ NV_WARN(dev, "Off-chip encoder %d/%d unsupported\n",
++ entry->type, ffs(entry->or) - 1);
++ continue;
++ }
++
++ switch (entry->type) {
++ case OUTPUT_TMDS:
++ case OUTPUT_LVDS:
++ case OUTPUT_DP:
++ nv50_sor_create(dev, entry);
++ break;
++ case OUTPUT_ANALOG:
++ nv50_dac_create(dev, entry);
++ break;
++ default:
++ NV_WARN(dev, "DCB encoder %d unknown\n", entry->type);
++ continue;
++ }
++
++ connector[entry->connector] |= (1 << entry->type);
++ }
++
++ /* It appears that DCB 3.0+ VBIOS has a connector table, however,
++ * I'm not 100% certain how to decode it correctly yet so just
++ * look at what encoders are present on each connector index and
++ * attempt to derive the connector type from that.
++ */
++ for (i = 0 ; i < dcb->entries; i++) {
++ struct dcb_entry *entry = &dcb->entry[i];
++ uint16_t encoders;
++ int type;
++
++ encoders = connector[entry->connector];
++ if (!(encoders & (1 << entry->type)))
++ continue;
++ connector[entry->connector] = 0;
++
++ if (encoders & (1 << OUTPUT_DP)) {
++ type = DRM_MODE_CONNECTOR_DisplayPort;
++ } else if (encoders & (1 << OUTPUT_TMDS)) {
++ if (encoders & (1 << OUTPUT_ANALOG))
++ type = DRM_MODE_CONNECTOR_DVII;
++ else
++ type = DRM_MODE_CONNECTOR_DVID;
++ } else if (encoders & (1 << OUTPUT_ANALOG)) {
++ type = DRM_MODE_CONNECTOR_VGA;
++ } else if (encoders & (1 << OUTPUT_LVDS)) {
++ type = DRM_MODE_CONNECTOR_LVDS;
++ } else {
++ type = DRM_MODE_CONNECTOR_Unknown;
++ }
++
++ if (type == DRM_MODE_CONNECTOR_Unknown)
++ continue;
++
++ nouveau_connector_create(dev, entry->connector, type);
++ }
++
++ ret = nv50_display_init(dev);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++int nv50_display_destroy(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ NV_DEBUG_KMS(dev, "\n");
++
++ drm_mode_config_cleanup(dev);
++
++ nv50_display_disable(dev);
++ nv50_evo_channel_del(&dev_priv->evo);
++
++ return 0;
++}
++
++static inline uint32_t
++nv50_display_mode_ctrl(struct drm_device *dev, bool sor, int or)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t mc;
++
++ if (sor) {
++ if (dev_priv->chipset < 0x90 ||
++ dev_priv->chipset == 0x92 || dev_priv->chipset == 0xa0)
++ mc = nv_rd32(dev, NV50_PDISPLAY_SOR_MODE_CTRL_P(or));
++ else
++ mc = nv_rd32(dev, NV90_PDISPLAY_SOR_MODE_CTRL_P(or));
++ } else {
++ mc = nv_rd32(dev, NV50_PDISPLAY_DAC_MODE_CTRL_P(or));
++ }
++
++ return mc;
++}
++
++static int
++nv50_display_irq_head(struct drm_device *dev, int *phead,
++ struct dcb_entry **pdcbent)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t unk30 = nv_rd32(dev, NV50_PDISPLAY_UNK30_CTRL);
++ uint32_t dac = 0, sor = 0;
++ int head, i, or = 0, type = OUTPUT_ANY;
++
++ /* We're assuming that head 0 *or* head 1 will be active here,
++ * and not both. I'm not sure if the hw will even signal both
++ * ever, but it definitely shouldn't for us as we commit each
++ * CRTC separately, and submission will be blocked by the GPU
++ * until we handle each in turn.
++ */
++ NV_DEBUG_KMS(dev, "0x610030: 0x%08x\n", unk30);
++ head = ffs((unk30 >> 9) & 3) - 1;
++ if (head < 0)
++ return -EINVAL;
++
++ /* This assumes CRTCs are never bound to multiple encoders, which
++ * should be the case.
++ */
++ for (i = 0; i < 3 && type == OUTPUT_ANY; i++) {
++ uint32_t mc = nv50_display_mode_ctrl(dev, false, i);
++ if (!(mc & (1 << head)))
++ continue;
++
++ switch ((mc >> 8) & 0xf) {
++ case 0: type = OUTPUT_ANALOG; break;
++ case 1: type = OUTPUT_TV; break;
++ default:
++ NV_ERROR(dev, "unknown dac mode_ctrl: 0x%08x\n", dac);
++ return -1;
++ }
++
++ or = i;
++ }
++
++ for (i = 0; i < 4 && type == OUTPUT_ANY; i++) {
++ uint32_t mc = nv50_display_mode_ctrl(dev, true, i);
++ if (!(mc & (1 << head)))
++ continue;
++
++ switch ((mc >> 8) & 0xf) {
++ case 0: type = OUTPUT_LVDS; break;
++ case 1: type = OUTPUT_TMDS; break;
++ case 2: type = OUTPUT_TMDS; break;
++ case 5: type = OUTPUT_TMDS; break;
++ case 8: type = OUTPUT_DP; break;
++ case 9: type = OUTPUT_DP; break;
++ default:
++ NV_ERROR(dev, "unknown sor mode_ctrl: 0x%08x\n", sor);
++ return -1;
++ }
++
++ or = i;
++ }
++
++ NV_DEBUG_KMS(dev, "type %d, or %d\n", type, or);
++ if (type == OUTPUT_ANY) {
++ NV_ERROR(dev, "unknown encoder!!\n");
++ return -1;
++ }
++
++ for (i = 0; i < dev_priv->vbios->dcb->entries; i++) {
++ struct dcb_entry *dcbent = &dev_priv->vbios->dcb->entry[i];
++
++ if (dcbent->type != type)
++ continue;
++
++ if (!(dcbent->or & (1 << or)))
++ continue;
++
++ *phead = head;
++ *pdcbent = dcbent;
++ return 0;
++ }
++
++ NV_ERROR(dev, "no DCB entry for %d %d\n", dac != 0, or);
++ return 0;
++}
++
++static uint32_t
++nv50_display_script_select(struct drm_device *dev, struct dcb_entry *dcbent,
++ int pxclk)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_connector *nv_connector = NULL;
++ struct drm_encoder *encoder;
++ struct nvbios *bios = &dev_priv->VBIOS;
++ uint32_t mc, script = 0, or;
++
++ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++
++ if (nv_encoder->dcb != dcbent)
++ continue;
++
++ nv_connector = nouveau_encoder_connector_get(nv_encoder);
++ break;
++ }
++
++ or = ffs(dcbent->or) - 1;
++ mc = nv50_display_mode_ctrl(dev, dcbent->type != OUTPUT_ANALOG, or);
++ switch (dcbent->type) {
++ case OUTPUT_LVDS:
++ script = (mc >> 8) & 0xf;
++ if (bios->pub.fp_no_ddc) {
++ if (bios->fp.dual_link)
++ script |= 0x0100;
++ if (bios->fp.if_is_24bit)
++ script |= 0x0200;
++ } else {
++ if (pxclk >= bios->fp.duallink_transition_clk) {
++ script |= 0x0100;
++ if (bios->fp.strapless_is_24bit & 2)
++ script |= 0x0200;
++ } else
++ if (bios->fp.strapless_is_24bit & 1)
++ script |= 0x0200;
++
++ if (nv_connector && nv_connector->edid &&
++ (nv_connector->edid->revision >= 4) &&
++ (nv_connector->edid->input & 0x70) >= 0x20)
++ script |= 0x0200;
++ }
++
++ if (nouveau_uscript_lvds >= 0) {
++ NV_INFO(dev, "override script 0x%04x with 0x%04x "
++ "for output LVDS-%d\n", script,
++ nouveau_uscript_lvds, or);
++ script = nouveau_uscript_lvds;
++ }
++ break;
++ case OUTPUT_TMDS:
++ script = (mc >> 8) & 0xf;
++ if (pxclk >= 165000)
++ script |= 0x0100;
++
++ if (nouveau_uscript_tmds >= 0) {
++ NV_INFO(dev, "override script 0x%04x with 0x%04x "
++ "for output TMDS-%d\n", script,
++ nouveau_uscript_tmds, or);
++ script = nouveau_uscript_tmds;
++ }
++ break;
++ case OUTPUT_DP:
++ script = (mc >> 8) & 0xf;
++ break;
++ case OUTPUT_ANALOG:
++ script = 0xff;
++ break;
++ default:
++ NV_ERROR(dev, "modeset on unsupported output type!\n");
++ break;
++ }
++
++ return script;
++}
++
++static void
++nv50_display_vblank_crtc_handler(struct drm_device *dev, int crtc)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *chan;
++ struct list_head *entry, *tmp;
++
++ list_for_each_safe(entry, tmp, &dev_priv->vbl_waiting) {
++ chan = list_entry(entry, struct nouveau_channel, nvsw.vbl_wait);
++
++ nouveau_bo_wr32(chan->notifier_bo, chan->nvsw.vblsem_offset,
++ chan->nvsw.vblsem_rval);
++ list_del(&chan->nvsw.vbl_wait);
++ }
++}
++
++static void
++nv50_display_vblank_handler(struct drm_device *dev, uint32_t intr)
++{
++ intr &= NV50_PDISPLAY_INTR_1_VBLANK_CRTC;
++
++ if (intr & NV50_PDISPLAY_INTR_1_VBLANK_CRTC_0)
++ nv50_display_vblank_crtc_handler(dev, 0);
++
++ if (intr & NV50_PDISPLAY_INTR_1_VBLANK_CRTC_1)
++ nv50_display_vblank_crtc_handler(dev, 1);
++
++ nv_wr32(dev, NV50_PDISPLAY_INTR_EN, nv_rd32(dev,
++ NV50_PDISPLAY_INTR_EN) & ~intr);
++ nv_wr32(dev, NV50_PDISPLAY_INTR_1, intr);
++}
++
++static void
++nv50_display_unk10_handler(struct drm_device *dev)
++{
++ struct dcb_entry *dcbent;
++ int head, ret;
++
++ ret = nv50_display_irq_head(dev, &head, &dcbent);
++ if (ret)
++ goto ack;
++
++ nv_wr32(dev, 0x619494, nv_rd32(dev, 0x619494) & ~8);
++
++ nouveau_bios_run_display_table(dev, dcbent, 0, -1);
++
++ack:
++ nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK10);
++ nv_wr32(dev, 0x610030, 0x80000000);
++}
++
++static void
++nv50_display_unk20_handler(struct drm_device *dev)
++{
++ struct dcb_entry *dcbent;
++ uint32_t tmp, pclk, script;
++ int head, or, ret;
++
++ ret = nv50_display_irq_head(dev, &head, &dcbent);
++ if (ret)
++ goto ack;
++ or = ffs(dcbent->or) - 1;
++ pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(head, CLOCK)) & 0x3fffff;
++ script = nv50_display_script_select(dev, dcbent, pclk);
++
++ NV_DEBUG_KMS(dev, "head %d pxclk: %dKHz\n", head, pclk);
++
++ if (dcbent->type != OUTPUT_DP)
++ nouveau_bios_run_display_table(dev, dcbent, 0, -2);
++
++ nv50_crtc_set_clock(dev, head, pclk);
++
++ nouveau_bios_run_display_table(dev, dcbent, script, pclk);
++
++ tmp = nv_rd32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(head));
++ tmp &= ~0x000000f;
++ nv_wr32(dev, NV50_PDISPLAY_CRTC_CLK_CTRL2(head), tmp);
++
++ if (dcbent->type != OUTPUT_ANALOG) {
++ tmp = nv_rd32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or));
++ tmp &= ~0x00000f0f;
++ if (script & 0x0100)
++ tmp |= 0x00000101;
++ nv_wr32(dev, NV50_PDISPLAY_SOR_CLK_CTRL2(or), tmp);
++ } else {
++ nv_wr32(dev, NV50_PDISPLAY_DAC_CLK_CTRL2(or), 0);
++ }
++
++ack:
++ nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK20);
++ nv_wr32(dev, 0x610030, 0x80000000);
++}
++
++static void
++nv50_display_unk40_handler(struct drm_device *dev)
++{
++ struct dcb_entry *dcbent;
++ int head, pclk, script, ret;
++
++ ret = nv50_display_irq_head(dev, &head, &dcbent);
++ if (ret)
++ goto ack;
++ pclk = nv_rd32(dev, NV50_PDISPLAY_CRTC_P(head, CLOCK)) & 0x3fffff;
++ script = nv50_display_script_select(dev, dcbent, pclk);
++
++ nouveau_bios_run_display_table(dev, dcbent, script, -pclk);
++
++ack:
++ nv_wr32(dev, NV50_PDISPLAY_INTR_1, NV50_PDISPLAY_INTR_1_CLK_UNK40);
++ nv_wr32(dev, 0x610030, 0x80000000);
++ nv_wr32(dev, 0x619494, nv_rd32(dev, 0x619494) | 8);
++}
++
++void
++nv50_display_irq_handler_bh(struct work_struct *work)
++{
++ struct drm_nouveau_private *dev_priv =
++ container_of(work, struct drm_nouveau_private, irq_work);
++ struct drm_device *dev = dev_priv->dev;
++
++ for (;;) {
++ uint32_t intr0 = nv_rd32(dev, NV50_PDISPLAY_INTR_0);
++ uint32_t intr1 = nv_rd32(dev, NV50_PDISPLAY_INTR_1);
++
++ NV_DEBUG_KMS(dev, "PDISPLAY_INTR_BH 0x%08x 0x%08x\n", intr0, intr1);
++
++ if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK10)
++ nv50_display_unk10_handler(dev);
++ else
++ if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK20)
++ nv50_display_unk20_handler(dev);
++ else
++ if (intr1 & NV50_PDISPLAY_INTR_1_CLK_UNK40)
++ nv50_display_unk40_handler(dev);
++ else
++ break;
++ }
++
++ nv_wr32(dev, NV03_PMC_INTR_EN_0, 1);
++}
++
++static void
++nv50_display_error_handler(struct drm_device *dev)
++{
++ uint32_t addr, data;
++
++ nv_wr32(dev, NV50_PDISPLAY_INTR_0, 0x00010000);
++ addr = nv_rd32(dev, NV50_PDISPLAY_TRAPPED_ADDR);
++ data = nv_rd32(dev, NV50_PDISPLAY_TRAPPED_DATA);
++
++ NV_ERROR(dev, "EvoCh %d Mthd 0x%04x Data 0x%08x (0x%04x 0x%02x)\n",
++ 0, addr & 0xffc, data, addr >> 16, (addr >> 12) & 0xf);
++
++ nv_wr32(dev, NV50_PDISPLAY_TRAPPED_ADDR, 0x90000000);
++}
++
++static void
++nv50_display_irq_hotplug(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct drm_connector *connector;
++ const uint32_t gpio_reg[4] = { 0xe104, 0xe108, 0xe280, 0xe284 };
++ uint32_t unplug_mask, plug_mask, change_mask;
++ uint32_t hpd0, hpd1 = 0;
++
++ hpd0 = nv_rd32(dev, 0xe054) & nv_rd32(dev, 0xe050);
++ if (dev_priv->chipset >= 0x90)
++ hpd1 = nv_rd32(dev, 0xe074) & nv_rd32(dev, 0xe070);
++
++ plug_mask = (hpd0 & 0x0000ffff) | (hpd1 << 16);
++ unplug_mask = (hpd0 >> 16) | (hpd1 & 0xffff0000);
++ change_mask = plug_mask | unplug_mask;
++
++ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
++ struct drm_encoder_helper_funcs *helper;
++ struct nouveau_connector *nv_connector =
++ nouveau_connector(connector);
++ struct nouveau_encoder *nv_encoder;
++ struct dcb_gpio_entry *gpio;
++ uint32_t reg;
++ bool plugged;
++
++ if (!nv_connector->dcb)
++ continue;
++
++ gpio = nouveau_bios_gpio_entry(dev, nv_connector->dcb->gpio_tag);
++ if (!gpio || !(change_mask & (1 << gpio->line)))
++ continue;
++
++ reg = nv_rd32(dev, gpio_reg[gpio->line >> 3]);
++ plugged = !!(reg & (4 << ((gpio->line & 7) << 2)));
++ NV_INFO(dev, "%splugged %s\n", plugged ? "" : "un",
++ drm_get_connector_name(connector)) ;
++
++ if (!connector->encoder || !connector->encoder->crtc ||
++ !connector->encoder->crtc->enabled)
++ continue;
++ nv_encoder = nouveau_encoder(connector->encoder);
++ helper = connector->encoder->helper_private;
++
++ if (nv_encoder->dcb->type != OUTPUT_DP)
++ continue;
++
++ if (plugged)
++ helper->dpms(connector->encoder, DRM_MODE_DPMS_ON);
++ else
++ helper->dpms(connector->encoder, DRM_MODE_DPMS_OFF);
++ }
++
++ nv_wr32(dev, 0xe054, nv_rd32(dev, 0xe054));
++ if (dev_priv->chipset >= 0x90)
++ nv_wr32(dev, 0xe074, nv_rd32(dev, 0xe074));
++}
++
++void
++nv50_display_irq_handler(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t delayed = 0;
++
++ while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_HOTPLUG)
++ nv50_display_irq_hotplug(dev);
++
++ while (nv_rd32(dev, NV50_PMC_INTR_0) & NV50_PMC_INTR_0_DISPLAY) {
++ uint32_t intr0 = nv_rd32(dev, NV50_PDISPLAY_INTR_0);
++ uint32_t intr1 = nv_rd32(dev, NV50_PDISPLAY_INTR_1);
++ uint32_t clock;
++
++ NV_DEBUG_KMS(dev, "PDISPLAY_INTR 0x%08x 0x%08x\n", intr0, intr1);
++
++ if (!intr0 && !(intr1 & ~delayed))
++ break;
++
++ if (intr0 & 0x00010000) {
++ nv50_display_error_handler(dev);
++ intr0 &= ~0x00010000;
++ }
++
++ if (intr1 & NV50_PDISPLAY_INTR_1_VBLANK_CRTC) {
++ nv50_display_vblank_handler(dev, intr1);
++ intr1 &= ~NV50_PDISPLAY_INTR_1_VBLANK_CRTC;
++ }
++
++ clock = (intr1 & (NV50_PDISPLAY_INTR_1_CLK_UNK10 |
++ NV50_PDISPLAY_INTR_1_CLK_UNK20 |
++ NV50_PDISPLAY_INTR_1_CLK_UNK40));
++ if (clock) {
++ nv_wr32(dev, NV03_PMC_INTR_EN_0, 0);
++ if (!work_pending(&dev_priv->irq_work))
++ queue_work(dev_priv->wq, &dev_priv->irq_work);
++ delayed |= clock;
++ intr1 &= ~clock;
++ }
++
++ if (intr0) {
++ NV_ERROR(dev, "unknown PDISPLAY_INTR_0: 0x%08x\n", intr0);
++ nv_wr32(dev, NV50_PDISPLAY_INTR_0, intr0);
++ }
++
++ if (intr1) {
++ NV_ERROR(dev,
++ "unknown PDISPLAY_INTR_1: 0x%08x\n", intr1);
++ nv_wr32(dev, NV50_PDISPLAY_INTR_1, intr1);
++ }
++ }
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nv50_display.h b/drivers/gpu/drm/nouveau/nv50_display.h
+new file mode 100644
+index 0000000..3ae8d07
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv50_display.h
+@@ -0,0 +1,46 @@
++/*
++ * Copyright (C) 2008 Maarten Maathuis.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#ifndef __NV50_DISPLAY_H__
++#define __NV50_DISPLAY_H__
++
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++#include "nouveau_dma.h"
++#include "nouveau_reg.h"
++#include "nouveau_crtc.h"
++#include "nv50_evo.h"
++
++void nv50_display_irq_handler(struct drm_device *dev);
++void nv50_display_irq_handler_bh(struct work_struct *work);
++int nv50_display_init(struct drm_device *dev);
++int nv50_display_create(struct drm_device *dev);
++int nv50_display_destroy(struct drm_device *dev);
++int nv50_crtc_blank(struct nouveau_crtc *, bool blank);
++int nv50_crtc_set_clock(struct drm_device *, int head, int pclk);
++
++#endif /* __NV50_DISPLAY_H__ */
+diff --git a/drivers/gpu/drm/nouveau/nv50_evo.h b/drivers/gpu/drm/nouveau/nv50_evo.h
+new file mode 100644
+index 0000000..aae1334
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv50_evo.h
+@@ -0,0 +1,113 @@
++/*
++ * Copyright (C) 2008 Maarten Maathuis.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#define NV50_EVO_UPDATE 0x00000080
++#define NV50_EVO_UNK84 0x00000084
++#define NV50_EVO_UNK84_NOTIFY 0x40000000
++#define NV50_EVO_UNK84_NOTIFY_DISABLED 0x00000000
++#define NV50_EVO_UNK84_NOTIFY_ENABLED 0x40000000
++#define NV50_EVO_DMA_NOTIFY 0x00000088
++#define NV50_EVO_DMA_NOTIFY_HANDLE 0xffffffff
++#define NV50_EVO_DMA_NOTIFY_HANDLE_NONE 0x00000000
++#define NV50_EVO_UNK8C 0x0000008C
++
++#define NV50_EVO_DAC(n, r) ((n) * 0x80 + NV50_EVO_DAC_##r)
++#define NV50_EVO_DAC_MODE_CTRL 0x00000400
++#define NV50_EVO_DAC_MODE_CTRL_CRTC0 0x00000001
++#define NV50_EVO_DAC_MODE_CTRL_CRTC1 0x00000002
++#define NV50_EVO_DAC_MODE_CTRL2 0x00000404
++#define NV50_EVO_DAC_MODE_CTRL2_NHSYNC 0x00000001
++#define NV50_EVO_DAC_MODE_CTRL2_NVSYNC 0x00000002
++
++#define NV50_EVO_SOR(n, r) ((n) * 0x40 + NV50_EVO_SOR_##r)
++#define NV50_EVO_SOR_MODE_CTRL 0x00000600
++#define NV50_EVO_SOR_MODE_CTRL_CRTC0 0x00000001
++#define NV50_EVO_SOR_MODE_CTRL_CRTC1 0x00000002
++#define NV50_EVO_SOR_MODE_CTRL_TMDS 0x00000100
++#define NV50_EVO_SOR_MODE_CTRL_TMDS_DUAL_LINK 0x00000400
++#define NV50_EVO_SOR_MODE_CTRL_NHSYNC 0x00001000
++#define NV50_EVO_SOR_MODE_CTRL_NVSYNC 0x00002000
++
++#define NV50_EVO_CRTC(n, r) ((n) * 0x400 + NV50_EVO_CRTC_##r)
++#define NV84_EVO_CRTC(n, r) ((n) * 0x400 + NV84_EVO_CRTC_##r)
++#define NV50_EVO_CRTC_UNK0800 0x00000800
++#define NV50_EVO_CRTC_CLOCK 0x00000804
++#define NV50_EVO_CRTC_INTERLACE 0x00000808
++#define NV50_EVO_CRTC_DISPLAY_START 0x00000810
++#define NV50_EVO_CRTC_DISPLAY_TOTAL 0x00000814
++#define NV50_EVO_CRTC_SYNC_DURATION 0x00000818
++#define NV50_EVO_CRTC_SYNC_START_TO_BLANK_END 0x0000081c
++#define NV50_EVO_CRTC_UNK0820 0x00000820
++#define NV50_EVO_CRTC_UNK0824 0x00000824
++#define NV50_EVO_CRTC_UNK082C 0x0000082c
++#define NV50_EVO_CRTC_CLUT_MODE 0x00000840
++/* You can't have a palette in 8 bit mode (=OFF) */
++#define NV50_EVO_CRTC_CLUT_MODE_BLANK 0x00000000
++#define NV50_EVO_CRTC_CLUT_MODE_OFF 0x80000000
++#define NV50_EVO_CRTC_CLUT_MODE_ON 0xC0000000
++#define NV50_EVO_CRTC_CLUT_OFFSET 0x00000844
++#define NV84_EVO_CRTC_CLUT_DMA 0x0000085C
++#define NV84_EVO_CRTC_CLUT_DMA_HANDLE 0xffffffff
++#define NV84_EVO_CRTC_CLUT_DMA_HANDLE_NONE 0x00000000
++#define NV50_EVO_CRTC_FB_OFFSET 0x00000860
++#define NV50_EVO_CRTC_FB_SIZE 0x00000868
++#define NV50_EVO_CRTC_FB_CONFIG 0x0000086c
++#define NV50_EVO_CRTC_FB_CONFIG_MODE 0x00100000
++#define NV50_EVO_CRTC_FB_CONFIG_MODE_TILE 0x00000000
++#define NV50_EVO_CRTC_FB_CONFIG_MODE_PITCH 0x00100000
++#define NV50_EVO_CRTC_FB_DEPTH 0x00000870
++#define NV50_EVO_CRTC_FB_DEPTH_8 0x00001e00
++#define NV50_EVO_CRTC_FB_DEPTH_15 0x0000e900
++#define NV50_EVO_CRTC_FB_DEPTH_16 0x0000e800
++#define NV50_EVO_CRTC_FB_DEPTH_24 0x0000cf00
++#define NV50_EVO_CRTC_FB_DEPTH_30 0x0000d100
++#define NV50_EVO_CRTC_FB_DMA 0x00000874
++#define NV50_EVO_CRTC_FB_DMA_HANDLE 0xffffffff
++#define NV50_EVO_CRTC_FB_DMA_HANDLE_NONE 0x00000000
++#define NV50_EVO_CRTC_CURSOR_CTRL 0x00000880
++#define NV50_EVO_CRTC_CURSOR_CTRL_HIDE 0x05000000
++#define NV50_EVO_CRTC_CURSOR_CTRL_SHOW 0x85000000
++#define NV50_EVO_CRTC_CURSOR_OFFSET 0x00000884
++#define NV84_EVO_CRTC_CURSOR_DMA 0x0000089c
++#define NV84_EVO_CRTC_CURSOR_DMA_HANDLE 0xffffffff
++#define NV84_EVO_CRTC_CURSOR_DMA_HANDLE_NONE 0x00000000
++#define NV50_EVO_CRTC_DITHER_CTRL 0x000008a0
++#define NV50_EVO_CRTC_DITHER_CTRL_OFF 0x00000000
++#define NV50_EVO_CRTC_DITHER_CTRL_ON 0x00000011
++#define NV50_EVO_CRTC_SCALE_CTRL 0x000008a4
++#define NV50_EVO_CRTC_SCALE_CTRL_INACTIVE 0x00000000
++#define NV50_EVO_CRTC_SCALE_CTRL_ACTIVE 0x00000009
++#define NV50_EVO_CRTC_COLOR_CTRL 0x000008a8
++#define NV50_EVO_CRTC_COLOR_CTRL_COLOR 0x00040000
++#define NV50_EVO_CRTC_FB_POS 0x000008c0
++#define NV50_EVO_CRTC_REAL_RES 0x000008c8
++#define NV50_EVO_CRTC_SCALE_CENTER_OFFSET 0x000008d4
++#define NV50_EVO_CRTC_SCALE_CENTER_OFFSET_VAL(x, y) \
++ ((((unsigned)y << 16) & 0xFFFF0000) | (((unsigned)x) & 0x0000FFFF))
++/* Both of these are needed, otherwise nothing happens. */
++#define NV50_EVO_CRTC_SCALE_RES1 0x000008d8
++#define NV50_EVO_CRTC_SCALE_RES2 0x000008dc
++
+diff --git a/drivers/gpu/drm/nouveau/nv50_fbcon.c b/drivers/gpu/drm/nouveau/nv50_fbcon.c
+new file mode 100644
+index 0000000..0f57cdf
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv50_fbcon.c
+@@ -0,0 +1,267 @@
++#include "drmP.h"
++#include "nouveau_drv.h"
++#include "nouveau_dma.h"
++#include "nouveau_fbcon.h"
++
++void
++nv50_fbcon_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
++{
++ struct nouveau_fbcon_par *par = info->par;
++ struct drm_device *dev = par->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *chan = dev_priv->channel;
++
++ if (info->state != FBINFO_STATE_RUNNING)
++ return;
++
++ if (!(info->flags & FBINFO_HWACCEL_DISABLED) &&
++ RING_SPACE(chan, rect->rop == ROP_COPY ? 7 : 11)) {
++ nouveau_fbcon_gpu_lockup(info);
++ }
++
++ if (info->flags & FBINFO_HWACCEL_DISABLED) {
++ cfb_fillrect(info, rect);
++ return;
++ }
++
++ if (rect->rop != ROP_COPY) {
++ BEGIN_RING(chan, NvSub2D, 0x02ac, 1);
++ OUT_RING(chan, 1);
++ }
++ BEGIN_RING(chan, NvSub2D, 0x0588, 1);
++ if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
++ info->fix.visual == FB_VISUAL_DIRECTCOLOR)
++ OUT_RING(chan, ((uint32_t *)info->pseudo_palette)[rect->color]);
++ else
++ OUT_RING(chan, rect->color);
++ BEGIN_RING(chan, NvSub2D, 0x0600, 4);
++ OUT_RING(chan, rect->dx);
++ OUT_RING(chan, rect->dy);
++ OUT_RING(chan, rect->dx + rect->width);
++ OUT_RING(chan, rect->dy + rect->height);
++ if (rect->rop != ROP_COPY) {
++ BEGIN_RING(chan, NvSub2D, 0x02ac, 1);
++ OUT_RING(chan, 3);
++ }
++ FIRE_RING(chan);
++}
++
++void
++nv50_fbcon_copyarea(struct fb_info *info, const struct fb_copyarea *region)
++{
++ struct nouveau_fbcon_par *par = info->par;
++ struct drm_device *dev = par->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *chan = dev_priv->channel;
++
++ if (info->state != FBINFO_STATE_RUNNING)
++ return;
++
++ if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 12)) {
++ nouveau_fbcon_gpu_lockup(info);
++ }
++
++ if (info->flags & FBINFO_HWACCEL_DISABLED) {
++ cfb_copyarea(info, region);
++ return;
++ }
++
++ BEGIN_RING(chan, NvSub2D, 0x0110, 1);
++ OUT_RING(chan, 0);
++ BEGIN_RING(chan, NvSub2D, 0x08b0, 4);
++ OUT_RING(chan, region->dx);
++ OUT_RING(chan, region->dy);
++ OUT_RING(chan, region->width);
++ OUT_RING(chan, region->height);
++ BEGIN_RING(chan, NvSub2D, 0x08d0, 4);
++ OUT_RING(chan, 0);
++ OUT_RING(chan, region->sx);
++ OUT_RING(chan, 0);
++ OUT_RING(chan, region->sy);
++ FIRE_RING(chan);
++}
++
++void
++nv50_fbcon_imageblit(struct fb_info *info, const struct fb_image *image)
++{
++ struct nouveau_fbcon_par *par = info->par;
++ struct drm_device *dev = par->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *chan = dev_priv->channel;
++ uint32_t width, dwords, *data = (uint32_t *)image->data;
++ uint32_t mask = ~(~0 >> (32 - info->var.bits_per_pixel));
++ uint32_t *palette = info->pseudo_palette;
++
++ if (info->state != FBINFO_STATE_RUNNING)
++ return;
++
++ if (image->depth != 1) {
++ cfb_imageblit(info, image);
++ return;
++ }
++
++ if (!(info->flags & FBINFO_HWACCEL_DISABLED) && RING_SPACE(chan, 11)) {
++ nouveau_fbcon_gpu_lockup(info);
++ }
++
++ if (info->flags & FBINFO_HWACCEL_DISABLED) {
++ cfb_imageblit(info, image);
++ return;
++ }
++
++ width = (image->width + 31) & ~31;
++ dwords = (width * image->height) >> 5;
++
++ BEGIN_RING(chan, NvSub2D, 0x0814, 2);
++ if (info->fix.visual == FB_VISUAL_TRUECOLOR ||
++ info->fix.visual == FB_VISUAL_DIRECTCOLOR) {
++ OUT_RING(chan, palette[image->bg_color] | mask);
++ OUT_RING(chan, palette[image->fg_color] | mask);
++ } else {
++ OUT_RING(chan, image->bg_color);
++ OUT_RING(chan, image->fg_color);
++ }
++ BEGIN_RING(chan, NvSub2D, 0x0838, 2);
++ OUT_RING(chan, image->width);
++ OUT_RING(chan, image->height);
++ BEGIN_RING(chan, NvSub2D, 0x0850, 4);
++ OUT_RING(chan, 0);
++ OUT_RING(chan, image->dx);
++ OUT_RING(chan, 0);
++ OUT_RING(chan, image->dy);
++
++ while (dwords) {
++ int push = dwords > 2047 ? 2047 : dwords;
++
++ if (RING_SPACE(chan, push + 1)) {
++ nouveau_fbcon_gpu_lockup(info);
++ cfb_imageblit(info, image);
++ return;
++ }
++
++ dwords -= push;
++
++ BEGIN_RING(chan, NvSub2D, 0x40000860, push);
++ OUT_RINGp(chan, data, push);
++ data += push;
++ }
++
++ FIRE_RING(chan);
++}
++
++int
++nv50_fbcon_accel_init(struct fb_info *info)
++{
++ struct nouveau_fbcon_par *par = info->par;
++ struct drm_device *dev = par->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *chan = dev_priv->channel;
++ struct nouveau_gpuobj *eng2d = NULL;
++ int ret, format;
++
++ switch (info->var.bits_per_pixel) {
++ case 8:
++ format = 0xf3;
++ break;
++ case 15:
++ format = 0xf8;
++ break;
++ case 16:
++ format = 0xe8;
++ break;
++ case 32:
++ switch (info->var.transp.length) {
++ case 0: /* depth 24 */
++ case 8: /* depth 32, just use 24.. */
++ format = 0xe6;
++ break;
++ case 2: /* depth 30 */
++ format = 0xd1;
++ break;
++ default:
++ return -EINVAL;
++ }
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ ret = nouveau_gpuobj_gr_new(dev_priv->channel, 0x502d, &eng2d);
++ if (ret)
++ return ret;
++
++ ret = nouveau_gpuobj_ref_add(dev, dev_priv->channel, Nv2D, eng2d, NULL);
++ if (ret)
++ return ret;
++
++ ret = RING_SPACE(chan, 59);
++ if (ret) {
++ nouveau_fbcon_gpu_lockup(info);
++ return ret;
++ }
++
++ BEGIN_RING(chan, NvSub2D, 0x0000, 1);
++ OUT_RING(chan, Nv2D);
++ BEGIN_RING(chan, NvSub2D, 0x0180, 4);
++ OUT_RING(chan, NvNotify0);
++ OUT_RING(chan, chan->vram_handle);
++ OUT_RING(chan, chan->vram_handle);
++ OUT_RING(chan, chan->vram_handle);
++ BEGIN_RING(chan, NvSub2D, 0x0290, 1);
++ OUT_RING(chan, 0);
++ BEGIN_RING(chan, NvSub2D, 0x0888, 1);
++ OUT_RING(chan, 1);
++ BEGIN_RING(chan, NvSub2D, 0x02ac, 1);
++ OUT_RING(chan, 3);
++ BEGIN_RING(chan, NvSub2D, 0x02a0, 1);
++ OUT_RING(chan, 0x55);
++ BEGIN_RING(chan, NvSub2D, 0x08c0, 4);
++ OUT_RING(chan, 0);
++ OUT_RING(chan, 1);
++ OUT_RING(chan, 0);
++ OUT_RING(chan, 1);
++ BEGIN_RING(chan, NvSub2D, 0x0580, 2);
++ OUT_RING(chan, 4);
++ OUT_RING(chan, format);
++ BEGIN_RING(chan, NvSub2D, 0x02e8, 2);
++ OUT_RING(chan, 2);
++ OUT_RING(chan, 1);
++ BEGIN_RING(chan, NvSub2D, 0x0804, 1);
++ OUT_RING(chan, format);
++ BEGIN_RING(chan, NvSub2D, 0x0800, 1);
++ OUT_RING(chan, 1);
++ BEGIN_RING(chan, NvSub2D, 0x0808, 3);
++ OUT_RING(chan, 0);
++ OUT_RING(chan, 0);
++ OUT_RING(chan, 0);
++ BEGIN_RING(chan, NvSub2D, 0x081c, 1);
++ OUT_RING(chan, 1);
++ BEGIN_RING(chan, NvSub2D, 0x0840, 4);
++ OUT_RING(chan, 0);
++ OUT_RING(chan, 1);
++ OUT_RING(chan, 0);
++ OUT_RING(chan, 1);
++ BEGIN_RING(chan, NvSub2D, 0x0200, 2);
++ OUT_RING(chan, format);
++ OUT_RING(chan, 1);
++ BEGIN_RING(chan, NvSub2D, 0x0214, 5);
++ OUT_RING(chan, info->fix.line_length);
++ OUT_RING(chan, info->var.xres_virtual);
++ OUT_RING(chan, info->var.yres_virtual);
++ OUT_RING(chan, 0);
++ OUT_RING(chan, info->fix.smem_start - dev_priv->fb_phys +
++ dev_priv->vm_vram_base);
++ BEGIN_RING(chan, NvSub2D, 0x0230, 2);
++ OUT_RING(chan, format);
++ OUT_RING(chan, 1);
++ BEGIN_RING(chan, NvSub2D, 0x0244, 5);
++ OUT_RING(chan, info->fix.line_length);
++ OUT_RING(chan, info->var.xres_virtual);
++ OUT_RING(chan, info->var.yres_virtual);
++ OUT_RING(chan, 0);
++ OUT_RING(chan, info->fix.smem_start - dev_priv->fb_phys +
++ dev_priv->vm_vram_base);
++
++ return 0;
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nv50_fifo.c b/drivers/gpu/drm/nouveau/nv50_fifo.c
+new file mode 100644
+index 0000000..204a79f
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv50_fifo.c
+@@ -0,0 +1,495 @@
++/*
++ * Copyright (C) 2007 Ben Skeggs.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++
++struct nv50_fifo_priv {
++ struct nouveau_gpuobj_ref *thingo[2];
++ int cur_thingo;
++};
++
++#define IS_G80 ((dev_priv->chipset & 0xf0) == 0x50)
++
++static void
++nv50_fifo_init_thingo(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv50_fifo_priv *priv = dev_priv->engine.fifo.priv;
++ struct nouveau_gpuobj_ref *cur;
++ int i, nr;
++
++ NV_DEBUG(dev, "\n");
++
++ cur = priv->thingo[priv->cur_thingo];
++ priv->cur_thingo = !priv->cur_thingo;
++
++ /* We never schedule channel 0 or 127 */
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ for (i = 1, nr = 0; i < 127; i++) {
++ if (dev_priv->fifos[i] && dev_priv->fifos[i]->ramfc)
++ nv_wo32(dev, cur->gpuobj, nr++, i);
++ }
++ dev_priv->engine.instmem.finish_access(dev);
++
++ nv_wr32(dev, 0x32f4, cur->instance >> 12);
++ nv_wr32(dev, 0x32ec, nr);
++ nv_wr32(dev, 0x2500, 0x101);
++}
++
++static int
++nv50_fifo_channel_enable(struct drm_device *dev, int channel, bool nt)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *chan = dev_priv->fifos[channel];
++ uint32_t inst;
++
++ NV_DEBUG(dev, "ch%d\n", channel);
++
++ if (!chan->ramfc)
++ return -EINVAL;
++
++ if (IS_G80)
++ inst = chan->ramfc->instance >> 12;
++ else
++ inst = chan->ramfc->instance >> 8;
++ nv_wr32(dev, NV50_PFIFO_CTX_TABLE(channel),
++ inst | NV50_PFIFO_CTX_TABLE_CHANNEL_ENABLED);
++
++ if (!nt)
++ nv50_fifo_init_thingo(dev);
++ return 0;
++}
++
++static void
++nv50_fifo_channel_disable(struct drm_device *dev, int channel, bool nt)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t inst;
++
++ NV_DEBUG(dev, "ch%d, nt=%d\n", channel, nt);
++
++ if (IS_G80)
++ inst = NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G80;
++ else
++ inst = NV50_PFIFO_CTX_TABLE_INSTANCE_MASK_G84;
++ nv_wr32(dev, NV50_PFIFO_CTX_TABLE(channel), inst);
++
++ if (!nt)
++ nv50_fifo_init_thingo(dev);
++}
++
++static void
++nv50_fifo_init_reset(struct drm_device *dev)
++{
++ uint32_t pmc_e = NV_PMC_ENABLE_PFIFO;
++
++ NV_DEBUG(dev, "\n");
++
++ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~pmc_e);
++ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | pmc_e);
++}
++
++static void
++nv50_fifo_init_intr(struct drm_device *dev)
++{
++ NV_DEBUG(dev, "\n");
++
++ nv_wr32(dev, NV03_PFIFO_INTR_0, 0xFFFFFFFF);
++ nv_wr32(dev, NV03_PFIFO_INTR_EN_0, 0xFFFFFFFF);
++}
++
++static void
++nv50_fifo_init_context_table(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int i;
++
++ NV_DEBUG(dev, "\n");
++
++ for (i = 0; i < NV50_PFIFO_CTX_TABLE__SIZE; i++) {
++ if (dev_priv->fifos[i])
++ nv50_fifo_channel_enable(dev, i, true);
++ else
++ nv50_fifo_channel_disable(dev, i, true);
++ }
++
++ nv50_fifo_init_thingo(dev);
++}
++
++static void
++nv50_fifo_init_regs__nv(struct drm_device *dev)
++{
++ NV_DEBUG(dev, "\n");
++
++ nv_wr32(dev, 0x250c, 0x6f3cfc34);
++}
++
++static void
++nv50_fifo_init_regs(struct drm_device *dev)
++{
++ NV_DEBUG(dev, "\n");
++
++ nv_wr32(dev, 0x2500, 0);
++ nv_wr32(dev, 0x3250, 0);
++ nv_wr32(dev, 0x3220, 0);
++ nv_wr32(dev, 0x3204, 0);
++ nv_wr32(dev, 0x3210, 0);
++ nv_wr32(dev, 0x3270, 0);
++
++ /* Enable dummy channels setup by nv50_instmem.c */
++ nv50_fifo_channel_enable(dev, 0, true);
++ nv50_fifo_channel_enable(dev, 127, true);
++}
++
++int
++nv50_fifo_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv50_fifo_priv *priv;
++ int ret;
++
++ NV_DEBUG(dev, "\n");
++
++ priv = dev_priv->engine.fifo.priv;
++ if (priv) {
++ priv->cur_thingo = !priv->cur_thingo;
++ goto just_reset;
++ }
++
++ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++ dev_priv->engine.fifo.priv = priv;
++
++ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 128*4, 0x1000,
++ NVOBJ_FLAG_ZERO_ALLOC, &priv->thingo[0]);
++ if (ret) {
++ NV_ERROR(dev, "error creating thingo0: %d\n", ret);
++ return ret;
++ }
++
++ ret = nouveau_gpuobj_new_ref(dev, NULL, NULL, 0, 128*4, 0x1000,
++ NVOBJ_FLAG_ZERO_ALLOC, &priv->thingo[1]);
++ if (ret) {
++ NV_ERROR(dev, "error creating thingo1: %d\n", ret);
++ return ret;
++ }
++
++just_reset:
++ nv50_fifo_init_reset(dev);
++ nv50_fifo_init_intr(dev);
++ nv50_fifo_init_context_table(dev);
++ nv50_fifo_init_regs__nv(dev);
++ nv50_fifo_init_regs(dev);
++ dev_priv->engine.fifo.enable(dev);
++ dev_priv->engine.fifo.reassign(dev, true);
++
++ return 0;
++}
++
++void
++nv50_fifo_takedown(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv50_fifo_priv *priv = dev_priv->engine.fifo.priv;
++
++ NV_DEBUG(dev, "\n");
++
++ if (!priv)
++ return;
++
++ nouveau_gpuobj_ref_del(dev, &priv->thingo[0]);
++ nouveau_gpuobj_ref_del(dev, &priv->thingo[1]);
++
++ dev_priv->engine.fifo.priv = NULL;
++ kfree(priv);
++}
++
++int
++nv50_fifo_channel_id(struct drm_device *dev)
++{
++ return nv_rd32(dev, NV03_PFIFO_CACHE1_PUSH1) &
++ NV50_PFIFO_CACHE1_PUSH1_CHID_MASK;
++}
++
++int
++nv50_fifo_create_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *ramfc = NULL;
++ int ret;
++
++ NV_DEBUG(dev, "ch%d\n", chan->id);
++
++ if (IS_G80) {
++ uint32_t ramin_poffset = chan->ramin->gpuobj->im_pramin->start;
++ uint32_t ramin_voffset = chan->ramin->gpuobj->im_backing_start;
++
++ ret = nouveau_gpuobj_new_fake(dev, ramin_poffset, ramin_voffset,
++ 0x100, NVOBJ_FLAG_ZERO_ALLOC |
++ NVOBJ_FLAG_ZERO_FREE, &ramfc,
++ &chan->ramfc);
++ if (ret)
++ return ret;
++
++ ret = nouveau_gpuobj_new_fake(dev, ramin_poffset + 0x0400,
++ ramin_voffset + 0x0400, 4096,
++ 0, NULL, &chan->cache);
++ if (ret)
++ return ret;
++ } else {
++ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 0x100, 256,
++ NVOBJ_FLAG_ZERO_ALLOC |
++ NVOBJ_FLAG_ZERO_FREE,
++ &chan->ramfc);
++ if (ret)
++ return ret;
++ ramfc = chan->ramfc->gpuobj;
++
++ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, 4096, 1024,
++ 0, &chan->cache);
++ if (ret)
++ return ret;
++ }
++
++ dev_priv->engine.instmem.prepare_access(dev, true);
++
++ nv_wo32(dev, ramfc, 0x08/4, chan->pushbuf_base);
++ nv_wo32(dev, ramfc, 0x10/4, chan->pushbuf_base);
++ nv_wo32(dev, ramfc, 0x48/4, chan->pushbuf->instance >> 4);
++ nv_wo32(dev, ramfc, 0x80/4, (0xc << 24) | (chan->ramht->instance >> 4));
++ nv_wo32(dev, ramfc, 0x3c/4, 0x00086078);
++ nv_wo32(dev, ramfc, 0x44/4, 0x2101ffff);
++ nv_wo32(dev, ramfc, 0x60/4, 0x7fffffff);
++ nv_wo32(dev, ramfc, 0x40/4, 0x00000000);
++ nv_wo32(dev, ramfc, 0x7c/4, 0x30000001);
++ nv_wo32(dev, ramfc, 0x78/4, 0x00000000);
++ nv_wo32(dev, ramfc, 0x4c/4, 0xffffffff);
++
++ if (!IS_G80) {
++ nv_wo32(dev, chan->ramin->gpuobj, 0, chan->id);
++ nv_wo32(dev, chan->ramin->gpuobj, 1,
++ chan->ramfc->instance >> 8);
++
++ nv_wo32(dev, ramfc, 0x88/4, chan->cache->instance >> 10);
++ nv_wo32(dev, ramfc, 0x98/4, chan->ramin->instance >> 12);
++ }
++
++ dev_priv->engine.instmem.finish_access(dev);
++
++ ret = nv50_fifo_channel_enable(dev, chan->id, false);
++ if (ret) {
++ NV_ERROR(dev, "error enabling ch%d: %d\n", chan->id, ret);
++ nouveau_gpuobj_ref_del(dev, &chan->ramfc);
++ return ret;
++ }
++
++ return 0;
++}
++
++void
++nv50_fifo_destroy_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct nouveau_gpuobj_ref *ramfc = chan->ramfc;
++
++ NV_DEBUG(dev, "ch%d\n", chan->id);
++
++ /* This will ensure the channel is seen as disabled. */
++ chan->ramfc = NULL;
++ nv50_fifo_channel_disable(dev, chan->id, false);
++
++ /* Dummy channel, also used on ch 127 */
++ if (chan->id == 0)
++ nv50_fifo_channel_disable(dev, 127, false);
++
++ nouveau_gpuobj_ref_del(dev, &ramfc);
++ nouveau_gpuobj_ref_del(dev, &chan->cache);
++}
++
++int
++nv50_fifo_load_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *ramfc = chan->ramfc->gpuobj;
++ struct nouveau_gpuobj *cache = chan->cache->gpuobj;
++ int ptr, cnt;
++
++ NV_DEBUG(dev, "ch%d\n", chan->id);
++
++ dev_priv->engine.instmem.prepare_access(dev, false);
++
++ nv_wr32(dev, 0x3330, nv_ro32(dev, ramfc, 0x00/4));
++ nv_wr32(dev, 0x3334, nv_ro32(dev, ramfc, 0x04/4));
++ nv_wr32(dev, 0x3240, nv_ro32(dev, ramfc, 0x08/4));
++ nv_wr32(dev, 0x3320, nv_ro32(dev, ramfc, 0x0c/4));
++ nv_wr32(dev, 0x3244, nv_ro32(dev, ramfc, 0x10/4));
++ nv_wr32(dev, 0x3328, nv_ro32(dev, ramfc, 0x14/4));
++ nv_wr32(dev, 0x3368, nv_ro32(dev, ramfc, 0x18/4));
++ nv_wr32(dev, 0x336c, nv_ro32(dev, ramfc, 0x1c/4));
++ nv_wr32(dev, 0x3370, nv_ro32(dev, ramfc, 0x20/4));
++ nv_wr32(dev, 0x3374, nv_ro32(dev, ramfc, 0x24/4));
++ nv_wr32(dev, 0x3378, nv_ro32(dev, ramfc, 0x28/4));
++ nv_wr32(dev, 0x337c, nv_ro32(dev, ramfc, 0x2c/4));
++ nv_wr32(dev, 0x3228, nv_ro32(dev, ramfc, 0x30/4));
++ nv_wr32(dev, 0x3364, nv_ro32(dev, ramfc, 0x34/4));
++ nv_wr32(dev, 0x32a0, nv_ro32(dev, ramfc, 0x38/4));
++ nv_wr32(dev, 0x3224, nv_ro32(dev, ramfc, 0x3c/4));
++ nv_wr32(dev, 0x324c, nv_ro32(dev, ramfc, 0x40/4));
++ nv_wr32(dev, 0x2044, nv_ro32(dev, ramfc, 0x44/4));
++ nv_wr32(dev, 0x322c, nv_ro32(dev, ramfc, 0x48/4));
++ nv_wr32(dev, 0x3234, nv_ro32(dev, ramfc, 0x4c/4));
++ nv_wr32(dev, 0x3340, nv_ro32(dev, ramfc, 0x50/4));
++ nv_wr32(dev, 0x3344, nv_ro32(dev, ramfc, 0x54/4));
++ nv_wr32(dev, 0x3280, nv_ro32(dev, ramfc, 0x58/4));
++ nv_wr32(dev, 0x3254, nv_ro32(dev, ramfc, 0x5c/4));
++ nv_wr32(dev, 0x3260, nv_ro32(dev, ramfc, 0x60/4));
++ nv_wr32(dev, 0x3264, nv_ro32(dev, ramfc, 0x64/4));
++ nv_wr32(dev, 0x3268, nv_ro32(dev, ramfc, 0x68/4));
++ nv_wr32(dev, 0x326c, nv_ro32(dev, ramfc, 0x6c/4));
++ nv_wr32(dev, 0x32e4, nv_ro32(dev, ramfc, 0x70/4));
++ nv_wr32(dev, 0x3248, nv_ro32(dev, ramfc, 0x74/4));
++ nv_wr32(dev, 0x2088, nv_ro32(dev, ramfc, 0x78/4));
++ nv_wr32(dev, 0x2058, nv_ro32(dev, ramfc, 0x7c/4));
++ nv_wr32(dev, 0x2210, nv_ro32(dev, ramfc, 0x80/4));
++
++ cnt = nv_ro32(dev, ramfc, 0x84/4);
++ for (ptr = 0; ptr < cnt; ptr++) {
++ nv_wr32(dev, NV40_PFIFO_CACHE1_METHOD(ptr),
++ nv_ro32(dev, cache, (ptr * 2) + 0));
++ nv_wr32(dev, NV40_PFIFO_CACHE1_DATA(ptr),
++ nv_ro32(dev, cache, (ptr * 2) + 1));
++ }
++ nv_wr32(dev, NV03_PFIFO_CACHE1_PUT, cnt << 2);
++ nv_wr32(dev, NV03_PFIFO_CACHE1_GET, 0);
++
++ /* guessing that all the 0x34xx regs aren't on NV50 */
++ if (!IS_G80) {
++ nv_wr32(dev, 0x340c, nv_ro32(dev, ramfc, 0x88/4));
++ nv_wr32(dev, 0x3400, nv_ro32(dev, ramfc, 0x8c/4));
++ nv_wr32(dev, 0x3404, nv_ro32(dev, ramfc, 0x90/4));
++ nv_wr32(dev, 0x3408, nv_ro32(dev, ramfc, 0x94/4));
++ nv_wr32(dev, 0x3410, nv_ro32(dev, ramfc, 0x98/4));
++ }
++
++ dev_priv->engine.instmem.finish_access(dev);
++
++ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, chan->id | (1<<16));
++ return 0;
++}
++
++int
++nv50_fifo_unload_context(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_fifo_engine *pfifo = &dev_priv->engine.fifo;
++ struct nouveau_gpuobj *ramfc, *cache;
++ struct nouveau_channel *chan = NULL;
++ int chid, get, put, ptr;
++
++ NV_DEBUG(dev, "\n");
++
++ chid = pfifo->channel_id(dev);
++ if (chid < 1 || chid >= dev_priv->engine.fifo.channels - 1)
++ return 0;
++
++ chan = dev_priv->fifos[chid];
++ if (!chan) {
++ NV_ERROR(dev, "Inactive channel on PFIFO: %d\n", chid);
++ return -EINVAL;
++ }
++ NV_DEBUG(dev, "ch%d\n", chan->id);
++ ramfc = chan->ramfc->gpuobj;
++ cache = chan->cache->gpuobj;
++
++ dev_priv->engine.instmem.prepare_access(dev, true);
++
++ nv_wo32(dev, ramfc, 0x00/4, nv_rd32(dev, 0x3330));
++ nv_wo32(dev, ramfc, 0x04/4, nv_rd32(dev, 0x3334));
++ nv_wo32(dev, ramfc, 0x08/4, nv_rd32(dev, 0x3240));
++ nv_wo32(dev, ramfc, 0x0c/4, nv_rd32(dev, 0x3320));
++ nv_wo32(dev, ramfc, 0x10/4, nv_rd32(dev, 0x3244));
++ nv_wo32(dev, ramfc, 0x14/4, nv_rd32(dev, 0x3328));
++ nv_wo32(dev, ramfc, 0x18/4, nv_rd32(dev, 0x3368));
++ nv_wo32(dev, ramfc, 0x1c/4, nv_rd32(dev, 0x336c));
++ nv_wo32(dev, ramfc, 0x20/4, nv_rd32(dev, 0x3370));
++ nv_wo32(dev, ramfc, 0x24/4, nv_rd32(dev, 0x3374));
++ nv_wo32(dev, ramfc, 0x28/4, nv_rd32(dev, 0x3378));
++ nv_wo32(dev, ramfc, 0x2c/4, nv_rd32(dev, 0x337c));
++ nv_wo32(dev, ramfc, 0x30/4, nv_rd32(dev, 0x3228));
++ nv_wo32(dev, ramfc, 0x34/4, nv_rd32(dev, 0x3364));
++ nv_wo32(dev, ramfc, 0x38/4, nv_rd32(dev, 0x32a0));
++ nv_wo32(dev, ramfc, 0x3c/4, nv_rd32(dev, 0x3224));
++ nv_wo32(dev, ramfc, 0x40/4, nv_rd32(dev, 0x324c));
++ nv_wo32(dev, ramfc, 0x44/4, nv_rd32(dev, 0x2044));
++ nv_wo32(dev, ramfc, 0x48/4, nv_rd32(dev, 0x322c));
++ nv_wo32(dev, ramfc, 0x4c/4, nv_rd32(dev, 0x3234));
++ nv_wo32(dev, ramfc, 0x50/4, nv_rd32(dev, 0x3340));
++ nv_wo32(dev, ramfc, 0x54/4, nv_rd32(dev, 0x3344));
++ nv_wo32(dev, ramfc, 0x58/4, nv_rd32(dev, 0x3280));
++ nv_wo32(dev, ramfc, 0x5c/4, nv_rd32(dev, 0x3254));
++ nv_wo32(dev, ramfc, 0x60/4, nv_rd32(dev, 0x3260));
++ nv_wo32(dev, ramfc, 0x64/4, nv_rd32(dev, 0x3264));
++ nv_wo32(dev, ramfc, 0x68/4, nv_rd32(dev, 0x3268));
++ nv_wo32(dev, ramfc, 0x6c/4, nv_rd32(dev, 0x326c));
++ nv_wo32(dev, ramfc, 0x70/4, nv_rd32(dev, 0x32e4));
++ nv_wo32(dev, ramfc, 0x74/4, nv_rd32(dev, 0x3248));
++ nv_wo32(dev, ramfc, 0x78/4, nv_rd32(dev, 0x2088));
++ nv_wo32(dev, ramfc, 0x7c/4, nv_rd32(dev, 0x2058));
++ nv_wo32(dev, ramfc, 0x80/4, nv_rd32(dev, 0x2210));
++
++ put = (nv_rd32(dev, NV03_PFIFO_CACHE1_PUT) & 0x7ff) >> 2;
++ get = (nv_rd32(dev, NV03_PFIFO_CACHE1_GET) & 0x7ff) >> 2;
++ ptr = 0;
++ while (put != get) {
++ nv_wo32(dev, cache, ptr++,
++ nv_rd32(dev, NV40_PFIFO_CACHE1_METHOD(get)));
++ nv_wo32(dev, cache, ptr++,
++ nv_rd32(dev, NV40_PFIFO_CACHE1_DATA(get)));
++ get = (get + 1) & 0x1ff;
++ }
++
++ /* guessing that all the 0x34xx regs aren't on NV50 */
++ if (!IS_G80) {
++ nv_wo32(dev, ramfc, 0x84/4, ptr >> 1);
++ nv_wo32(dev, ramfc, 0x88/4, nv_rd32(dev, 0x340c));
++ nv_wo32(dev, ramfc, 0x8c/4, nv_rd32(dev, 0x3400));
++ nv_wo32(dev, ramfc, 0x90/4, nv_rd32(dev, 0x3404));
++ nv_wo32(dev, ramfc, 0x94/4, nv_rd32(dev, 0x3408));
++ nv_wo32(dev, ramfc, 0x98/4, nv_rd32(dev, 0x3410));
++ }
++
++ dev_priv->engine.instmem.finish_access(dev);
++
++ /*XXX: probably reload ch127 (NULL) state back too */
++ nv_wr32(dev, NV03_PFIFO_CACHE1_PUSH1, 127);
++ return 0;
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nv50_graph.c b/drivers/gpu/drm/nouveau/nv50_graph.c
+new file mode 100644
+index 0000000..6d50480
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv50_graph.c
+@@ -0,0 +1,394 @@
++/*
++ * Copyright (C) 2007 Ben Skeggs.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++
++MODULE_FIRMWARE("nouveau/nv50.ctxprog");
++MODULE_FIRMWARE("nouveau/nv50.ctxvals");
++MODULE_FIRMWARE("nouveau/nv84.ctxprog");
++MODULE_FIRMWARE("nouveau/nv84.ctxvals");
++MODULE_FIRMWARE("nouveau/nv86.ctxprog");
++MODULE_FIRMWARE("nouveau/nv86.ctxvals");
++MODULE_FIRMWARE("nouveau/nv92.ctxprog");
++MODULE_FIRMWARE("nouveau/nv92.ctxvals");
++MODULE_FIRMWARE("nouveau/nv94.ctxprog");
++MODULE_FIRMWARE("nouveau/nv94.ctxvals");
++MODULE_FIRMWARE("nouveau/nv96.ctxprog");
++MODULE_FIRMWARE("nouveau/nv96.ctxvals");
++MODULE_FIRMWARE("nouveau/nv98.ctxprog");
++MODULE_FIRMWARE("nouveau/nv98.ctxvals");
++MODULE_FIRMWARE("nouveau/nva0.ctxprog");
++MODULE_FIRMWARE("nouveau/nva0.ctxvals");
++MODULE_FIRMWARE("nouveau/nva5.ctxprog");
++MODULE_FIRMWARE("nouveau/nva5.ctxvals");
++MODULE_FIRMWARE("nouveau/nva8.ctxprog");
++MODULE_FIRMWARE("nouveau/nva8.ctxvals");
++MODULE_FIRMWARE("nouveau/nvaa.ctxprog");
++MODULE_FIRMWARE("nouveau/nvaa.ctxvals");
++MODULE_FIRMWARE("nouveau/nvac.ctxprog");
++MODULE_FIRMWARE("nouveau/nvac.ctxvals");
++
++#define IS_G80 ((dev_priv->chipset & 0xf0) == 0x50)
++
++static void
++nv50_graph_init_reset(struct drm_device *dev)
++{
++ uint32_t pmc_e = NV_PMC_ENABLE_PGRAPH | (1 << 21);
++
++ NV_DEBUG(dev, "\n");
++
++ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) & ~pmc_e);
++ nv_wr32(dev, NV03_PMC_ENABLE, nv_rd32(dev, NV03_PMC_ENABLE) | pmc_e);
++}
++
++static void
++nv50_graph_init_intr(struct drm_device *dev)
++{
++ NV_DEBUG(dev, "\n");
++
++ nv_wr32(dev, NV03_PGRAPH_INTR, 0xffffffff);
++ nv_wr32(dev, 0x400138, 0xffffffff);
++ nv_wr32(dev, NV40_PGRAPH_INTR_EN, 0xffffffff);
++}
++
++static void
++nv50_graph_init_regs__nv(struct drm_device *dev)
++{
++ NV_DEBUG(dev, "\n");
++
++ nv_wr32(dev, 0x400804, 0xc0000000);
++ nv_wr32(dev, 0x406800, 0xc0000000);
++ nv_wr32(dev, 0x400c04, 0xc0000000);
++ nv_wr32(dev, 0x401800, 0xc0000000);
++ nv_wr32(dev, 0x405018, 0xc0000000);
++ nv_wr32(dev, 0x402000, 0xc0000000);
++
++ nv_wr32(dev, 0x400108, 0xffffffff);
++
++ nv_wr32(dev, 0x400824, 0x00004000);
++ nv_wr32(dev, 0x400500, 0x00010001);
++}
++
++static void
++nv50_graph_init_regs(struct drm_device *dev)
++{
++ NV_DEBUG(dev, "\n");
++
++ nv_wr32(dev, NV04_PGRAPH_DEBUG_3,
++ (1 << 2) /* HW_CONTEXT_SWITCH_ENABLED */);
++ nv_wr32(dev, 0x402ca8, 0x800);
++}
++
++static int
++nv50_graph_init_ctxctl(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ NV_DEBUG(dev, "\n");
++
++ nouveau_grctx_prog_load(dev);
++ if (!dev_priv->engine.graph.ctxprog)
++ dev_priv->engine.graph.accel_blocked = true;
++
++ nv_wr32(dev, 0x400320, 4);
++ nv_wr32(dev, NV40_PGRAPH_CTXCTL_CUR, 0);
++ nv_wr32(dev, NV20_PGRAPH_CHANNEL_CTX_POINTER, 0);
++ return 0;
++}
++
++int
++nv50_graph_init(struct drm_device *dev)
++{
++ int ret;
++
++ NV_DEBUG(dev, "\n");
++
++ nv50_graph_init_reset(dev);
++ nv50_graph_init_regs__nv(dev);
++ nv50_graph_init_regs(dev);
++ nv50_graph_init_intr(dev);
++
++ ret = nv50_graph_init_ctxctl(dev);
++ if (ret)
++ return ret;
++
++ return 0;
++}
++
++void
++nv50_graph_takedown(struct drm_device *dev)
++{
++ NV_DEBUG(dev, "\n");
++ nouveau_grctx_fini(dev);
++}
++
++void
++nv50_graph_fifo_access(struct drm_device *dev, bool enabled)
++{
++ const uint32_t mask = 0x00010001;
++
++ if (enabled)
++ nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) | mask);
++ else
++ nv_wr32(dev, 0x400500, nv_rd32(dev, 0x400500) & ~mask);
++}
++
++struct nouveau_channel *
++nv50_graph_channel(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ uint32_t inst;
++ int i;
++
++ /* Be sure we're not in the middle of a context switch or bad things
++ * will happen, such as unloading the wrong pgraph context.
++ */
++ if (!nv_wait(0x400300, 0x00000001, 0x00000000))
++ NV_ERROR(dev, "Ctxprog is still running\n");
++
++ inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR);
++ if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED))
++ return NULL;
++ inst = (inst & NV50_PGRAPH_CTXCTL_CUR_INSTANCE) << 12;
++
++ for (i = 0; i < dev_priv->engine.fifo.channels; i++) {
++ struct nouveau_channel *chan = dev_priv->fifos[i];
++
++ if (chan && chan->ramin && chan->ramin->instance == inst)
++ return chan;
++ }
++
++ return NULL;
++}
++
++int
++nv50_graph_create_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_gpuobj *ramin = chan->ramin->gpuobj;
++ struct nouveau_gpuobj *ctx;
++ uint32_t grctx_size = 0x70000;
++ int hdr, ret;
++
++ NV_DEBUG(dev, "ch%d\n", chan->id);
++
++ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, grctx_size, 0x1000,
++ NVOBJ_FLAG_ZERO_ALLOC |
++ NVOBJ_FLAG_ZERO_FREE, &chan->ramin_grctx);
++ if (ret)
++ return ret;
++ ctx = chan->ramin_grctx->gpuobj;
++
++ hdr = IS_G80 ? 0x200 : 0x20;
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ nv_wo32(dev, ramin, (hdr + 0x00)/4, 0x00190002);
++ nv_wo32(dev, ramin, (hdr + 0x04)/4, chan->ramin_grctx->instance +
++ grctx_size - 1);
++ nv_wo32(dev, ramin, (hdr + 0x08)/4, chan->ramin_grctx->instance);
++ nv_wo32(dev, ramin, (hdr + 0x0c)/4, 0);
++ nv_wo32(dev, ramin, (hdr + 0x10)/4, 0);
++ nv_wo32(dev, ramin, (hdr + 0x14)/4, 0x00010000);
++ dev_priv->engine.instmem.finish_access(dev);
++
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ nouveau_grctx_vals_load(dev, ctx);
++ nv_wo32(dev, ctx, 0x00000/4, chan->ramin->instance >> 12);
++ if ((dev_priv->chipset & 0xf0) == 0xa0)
++ nv_wo32(dev, ctx, 0x00004/4, 0x00000000);
++ else
++ nv_wo32(dev, ctx, 0x0011c/4, 0x00000000);
++ dev_priv->engine.instmem.finish_access(dev);
++
++ return 0;
++}
++
++void
++nv50_graph_destroy_context(struct nouveau_channel *chan)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ int i, hdr = IS_G80 ? 0x200 : 0x20;
++
++ NV_DEBUG(dev, "ch%d\n", chan->id);
++
++ if (!chan->ramin || !chan->ramin->gpuobj)
++ return;
++
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ for (i = hdr; i < hdr + 24; i += 4)
++ nv_wo32(dev, chan->ramin->gpuobj, i/4, 0);
++ dev_priv->engine.instmem.finish_access(dev);
++
++ nouveau_gpuobj_ref_del(dev, &chan->ramin_grctx);
++}
++
++static int
++nv50_graph_do_load_context(struct drm_device *dev, uint32_t inst)
++{
++ uint32_t fifo = nv_rd32(dev, 0x400500);
++
++ nv_wr32(dev, 0x400500, fifo & ~1);
++ nv_wr32(dev, 0x400784, inst);
++ nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x40);
++ nv_wr32(dev, 0x400320, nv_rd32(dev, 0x400320) | 0x11);
++ nv_wr32(dev, 0x400040, 0xffffffff);
++ (void)nv_rd32(dev, 0x400040);
++ nv_wr32(dev, 0x400040, 0x00000000);
++ nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 1);
++
++ if (nouveau_wait_for_idle(dev))
++ nv_wr32(dev, 0x40032c, inst | (1<<31));
++ nv_wr32(dev, 0x400500, fifo);
++
++ return 0;
++}
++
++int
++nv50_graph_load_context(struct nouveau_channel *chan)
++{
++ uint32_t inst = chan->ramin->instance >> 12;
++
++ NV_DEBUG(chan->dev, "ch%d\n", chan->id);
++ return nv50_graph_do_load_context(chan->dev, inst);
++}
++
++int
++nv50_graph_unload_context(struct drm_device *dev)
++{
++ uint32_t inst;
++
++ inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_CUR);
++ if (!(inst & NV50_PGRAPH_CTXCTL_CUR_LOADED))
++ return 0;
++ inst &= NV50_PGRAPH_CTXCTL_CUR_INSTANCE;
++
++ nouveau_wait_for_idle(dev);
++ nv_wr32(dev, 0x400784, inst);
++ nv_wr32(dev, 0x400824, nv_rd32(dev, 0x400824) | 0x20);
++ nv_wr32(dev, 0x400304, nv_rd32(dev, 0x400304) | 0x01);
++ nouveau_wait_for_idle(dev);
++
++ nv_wr32(dev, NV50_PGRAPH_CTXCTL_CUR, inst);
++ return 0;
++}
++
++void
++nv50_graph_context_switch(struct drm_device *dev)
++{
++ uint32_t inst;
++
++ nv50_graph_unload_context(dev);
++
++ inst = nv_rd32(dev, NV50_PGRAPH_CTXCTL_NEXT);
++ inst &= NV50_PGRAPH_CTXCTL_NEXT_INSTANCE;
++ nv50_graph_do_load_context(dev, inst);
++
++ nv_wr32(dev, NV40_PGRAPH_INTR_EN, nv_rd32(dev,
++ NV40_PGRAPH_INTR_EN) | NV_PGRAPH_INTR_CONTEXT_SWITCH);
++}
++
++static int
++nv50_graph_nvsw_dma_vblsem(struct nouveau_channel *chan, int grclass,
++ int mthd, uint32_t data)
++{
++ struct nouveau_gpuobj_ref *ref = NULL;
++
++ if (nouveau_gpuobj_ref_find(chan, data, &ref))
++ return -ENOENT;
++
++ if (nouveau_notifier_offset(ref->gpuobj, NULL))
++ return -EINVAL;
++
++ chan->nvsw.vblsem = ref->gpuobj;
++ chan->nvsw.vblsem_offset = ~0;
++ return 0;
++}
++
++static int
++nv50_graph_nvsw_vblsem_offset(struct nouveau_channel *chan, int grclass,
++ int mthd, uint32_t data)
++{
++ if (nouveau_notifier_offset(chan->nvsw.vblsem, &data))
++ return -ERANGE;
++
++ chan->nvsw.vblsem_offset = data >> 2;
++ return 0;
++}
++
++static int
++nv50_graph_nvsw_vblsem_release_val(struct nouveau_channel *chan, int grclass,
++ int mthd, uint32_t data)
++{
++ chan->nvsw.vblsem_rval = data;
++ return 0;
++}
++
++static int
++nv50_graph_nvsw_vblsem_release(struct nouveau_channel *chan, int grclass,
++ int mthd, uint32_t data)
++{
++ struct drm_device *dev = chan->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ if (!chan->nvsw.vblsem || chan->nvsw.vblsem_offset == ~0 || data > 1)
++ return -EINVAL;
++
++ if (!(nv_rd32(dev, NV50_PDISPLAY_INTR_EN) &
++ NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_(data))) {
++ nv_wr32(dev, NV50_PDISPLAY_INTR_1,
++ NV50_PDISPLAY_INTR_1_VBLANK_CRTC_(data));
++ nv_wr32(dev, NV50_PDISPLAY_INTR_EN, nv_rd32(dev,
++ NV50_PDISPLAY_INTR_EN) |
++ NV50_PDISPLAY_INTR_EN_VBLANK_CRTC_(data));
++ }
++
++ list_add(&chan->nvsw.vbl_wait, &dev_priv->vbl_waiting);
++ return 0;
++}
++
++static struct nouveau_pgraph_object_method nv50_graph_nvsw_methods[] = {
++ { 0x018c, nv50_graph_nvsw_dma_vblsem },
++ { 0x0400, nv50_graph_nvsw_vblsem_offset },
++ { 0x0404, nv50_graph_nvsw_vblsem_release_val },
++ { 0x0408, nv50_graph_nvsw_vblsem_release },
++ {}
++};
++
++struct nouveau_pgraph_object_class nv50_graph_grclass[] = {
++ { 0x506e, true, nv50_graph_nvsw_methods }, /* nvsw */
++ { 0x0030, false, NULL }, /* null */
++ { 0x5039, false, NULL }, /* m2mf */
++ { 0x502d, false, NULL }, /* 2d */
++ { 0x50c0, false, NULL }, /* compute */
++ { 0x5097, false, NULL }, /* tesla (nv50) */
++ { 0x8297, false, NULL }, /* tesla (nv80/nv90) */
++ { 0x8397, false, NULL }, /* tesla (nva0) */
++ { 0x8597, false, NULL }, /* tesla (nva8) */
++ {}
++};
+diff --git a/drivers/gpu/drm/nouveau/nv50_instmem.c b/drivers/gpu/drm/nouveau/nv50_instmem.c
+new file mode 100644
+index 0000000..94400f7
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv50_instmem.c
+@@ -0,0 +1,509 @@
++/*
++ * Copyright (C) 2007 Ben Skeggs.
++ *
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++
++struct nv50_instmem_priv {
++ uint32_t save1700[5]; /* 0x1700->0x1710 */
++
++ struct nouveau_gpuobj_ref *pramin_pt;
++ struct nouveau_gpuobj_ref *pramin_bar;
++ struct nouveau_gpuobj_ref *fb_bar;
++
++ bool last_access_wr;
++};
++
++#define NV50_INSTMEM_PAGE_SHIFT 12
++#define NV50_INSTMEM_PAGE_SIZE (1 << NV50_INSTMEM_PAGE_SHIFT)
++#define NV50_INSTMEM_PT_SIZE(a) (((a) >> 12) << 3)
++
++/*NOTE: - Assumes 0x1700 already covers the correct MiB of PRAMIN
++ */
++#define BAR0_WI32(g, o, v) do { \
++ uint32_t offset; \
++ if ((g)->im_backing) { \
++ offset = (g)->im_backing_start; \
++ } else { \
++ offset = chan->ramin->gpuobj->im_backing_start; \
++ offset += (g)->im_pramin->start; \
++ } \
++ offset += (o); \
++ nv_wr32(dev, NV_RAMIN + (offset & 0xfffff), (v)); \
++} while (0)
++
++int
++nv50_instmem_init(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *chan;
++ uint32_t c_offset, c_size, c_ramfc, c_vmpd, c_base, pt_size;
++ struct nv50_instmem_priv *priv;
++ int ret, i;
++ uint32_t v, save_nv001700;
++
++ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
++ if (!priv)
++ return -ENOMEM;
++ dev_priv->engine.instmem.priv = priv;
++
++ /* Save state, will restore at takedown. */
++ for (i = 0x1700; i <= 0x1710; i += 4)
++ priv->save1700[(i-0x1700)/4] = nv_rd32(dev, i);
++
++ /* Reserve the last MiB of VRAM, we should probably try to avoid
++ * setting up the below tables over the top of the VBIOS image at
++ * some point.
++ */
++ dev_priv->ramin_rsvd_vram = 1 << 20;
++ c_offset = nouveau_mem_fb_amount(dev) - dev_priv->ramin_rsvd_vram;
++ c_size = 128 << 10;
++ c_vmpd = ((dev_priv->chipset & 0xf0) == 0x50) ? 0x1400 : 0x200;
++ c_ramfc = ((dev_priv->chipset & 0xf0) == 0x50) ? 0x0 : 0x20;
++ c_base = c_vmpd + 0x4000;
++ pt_size = NV50_INSTMEM_PT_SIZE(dev_priv->ramin_size);
++
++ NV_DEBUG(dev, " Rsvd VRAM base: 0x%08x\n", c_offset);
++ NV_DEBUG(dev, " VBIOS image: 0x%08x\n",
++ (nv_rd32(dev, 0x619f04) & ~0xff) << 8);
++ NV_DEBUG(dev, " Aperture size: %d MiB\n", dev_priv->ramin_size >> 20);
++ NV_DEBUG(dev, " PT size: %d KiB\n", pt_size >> 10);
++
++ /* Determine VM layout, we need to do this first to make sure
++ * we allocate enough memory for all the page tables.
++ */
++ dev_priv->vm_gart_base = roundup(NV50_VM_BLOCK, NV50_VM_BLOCK);
++ dev_priv->vm_gart_size = NV50_VM_BLOCK;
++
++ dev_priv->vm_vram_base = dev_priv->vm_gart_base + dev_priv->vm_gart_size;
++ dev_priv->vm_vram_size = nouveau_mem_fb_amount(dev);
++ if (dev_priv->vm_vram_size > NV50_VM_MAX_VRAM)
++ dev_priv->vm_vram_size = NV50_VM_MAX_VRAM;
++ dev_priv->vm_vram_size = roundup(dev_priv->vm_vram_size, NV50_VM_BLOCK);
++ dev_priv->vm_vram_pt_nr = dev_priv->vm_vram_size / NV50_VM_BLOCK;
++
++ dev_priv->vm_end = dev_priv->vm_vram_base + dev_priv->vm_vram_size;
++
++ NV_DEBUG(dev, "NV50VM: GART 0x%016llx-0x%016llx\n",
++ dev_priv->vm_gart_base,
++ dev_priv->vm_gart_base + dev_priv->vm_gart_size - 1);
++ NV_DEBUG(dev, "NV50VM: VRAM 0x%016llx-0x%016llx\n",
++ dev_priv->vm_vram_base,
++ dev_priv->vm_vram_base + dev_priv->vm_vram_size - 1);
++
++ c_size += dev_priv->vm_vram_pt_nr * (NV50_VM_BLOCK / 65536 * 8);
++
++ /* Map BAR0 PRAMIN aperture over the memory we want to use */
++ save_nv001700 = nv_rd32(dev, NV50_PUNK_BAR0_PRAMIN);
++ nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, (c_offset >> 16));
++
++ /* Create a fake channel, and use it as our "dummy" channels 0/127.
++ * The main reason for creating a channel is so we can use the gpuobj
++ * code. However, it's probably worth noting that NVIDIA also setup
++ * their channels 0/127 with the same values they configure here.
++ * So, there may be some other reason for doing this.
++ *
++ * Have to create the entire channel manually, as the real channel
++ * creation code assumes we have PRAMIN access, and we don't until
++ * we're done here.
++ */
++ chan = kzalloc(sizeof(*chan), GFP_KERNEL);
++ if (!chan)
++ return -ENOMEM;
++ chan->id = 0;
++ chan->dev = dev;
++ chan->file_priv = (struct drm_file *)-2;
++ dev_priv->fifos[0] = dev_priv->fifos[127] = chan;
++
++ /* Channel's PRAMIN object + heap */
++ ret = nouveau_gpuobj_new_fake(dev, 0, c_offset, c_size, 0,
++ NULL, &chan->ramin);
++ if (ret)
++ return ret;
++
++ if (nouveau_mem_init_heap(&chan->ramin_heap, c_base, c_size - c_base))
++ return -ENOMEM;
++
++ /* RAMFC + zero channel's PRAMIN up to start of VM pagedir */
++ ret = nouveau_gpuobj_new_fake(dev, c_ramfc, c_offset + c_ramfc,
++ 0x4000, 0, NULL, &chan->ramfc);
++ if (ret)
++ return ret;
++
++ for (i = 0; i < c_vmpd; i += 4)
++ BAR0_WI32(chan->ramin->gpuobj, i, 0);
++
++ /* VM page directory */
++ ret = nouveau_gpuobj_new_fake(dev, c_vmpd, c_offset + c_vmpd,
++ 0x4000, 0, &chan->vm_pd, NULL);
++ if (ret)
++ return ret;
++ for (i = 0; i < 0x4000; i += 8) {
++ BAR0_WI32(chan->vm_pd, i + 0x00, 0x00000000);
++ BAR0_WI32(chan->vm_pd, i + 0x04, 0x00000000);
++ }
++
++ /* PRAMIN page table, cheat and map into VM at 0x0000000000.
++ * We map the entire fake channel into the start of the PRAMIN BAR
++ */
++ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0, pt_size, 0x1000,
++ 0, &priv->pramin_pt);
++ if (ret)
++ return ret;
++
++ for (i = 0, v = c_offset; i < pt_size; i += 8, v += 0x1000) {
++ if (v < (c_offset + c_size))
++ BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, v | 1);
++ else
++ BAR0_WI32(priv->pramin_pt->gpuobj, i + 0, 0x00000009);
++ BAR0_WI32(priv->pramin_pt->gpuobj, i + 4, 0x00000000);
++ }
++
++ BAR0_WI32(chan->vm_pd, 0x00, priv->pramin_pt->instance | 0x63);
++ BAR0_WI32(chan->vm_pd, 0x04, 0x00000000);
++
++ /* VRAM page table(s), mapped into VM at +1GiB */
++ for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) {
++ ret = nouveau_gpuobj_new_ref(dev, chan, NULL, 0,
++ NV50_VM_BLOCK/65536*8, 0, 0,
++ &chan->vm_vram_pt[i]);
++ if (ret) {
++ NV_ERROR(dev, "Error creating VRAM page tables: %d\n",
++ ret);
++ dev_priv->vm_vram_pt_nr = i;
++ return ret;
++ }
++ dev_priv->vm_vram_pt[i] = chan->vm_vram_pt[i]->gpuobj;
++
++ for (v = 0; v < dev_priv->vm_vram_pt[i]->im_pramin->size;
++ v += 4)
++ BAR0_WI32(dev_priv->vm_vram_pt[i], v, 0);
++
++ BAR0_WI32(chan->vm_pd, 0x10 + (i*8),
++ chan->vm_vram_pt[i]->instance | 0x61);
++ BAR0_WI32(chan->vm_pd, 0x14 + (i*8), 0);
++ }
++
++ /* DMA object for PRAMIN BAR */
++ ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, 6*4, 16, 0,
++ &priv->pramin_bar);
++ if (ret)
++ return ret;
++ BAR0_WI32(priv->pramin_bar->gpuobj, 0x00, 0x7fc00000);
++ BAR0_WI32(priv->pramin_bar->gpuobj, 0x04, dev_priv->ramin_size - 1);
++ BAR0_WI32(priv->pramin_bar->gpuobj, 0x08, 0x00000000);
++ BAR0_WI32(priv->pramin_bar->gpuobj, 0x0c, 0x00000000);
++ BAR0_WI32(priv->pramin_bar->gpuobj, 0x10, 0x00000000);
++ BAR0_WI32(priv->pramin_bar->gpuobj, 0x14, 0x00000000);
++
++ /* DMA object for FB BAR */
++ ret = nouveau_gpuobj_new_ref(dev, chan, chan, 0, 6*4, 16, 0,
++ &priv->fb_bar);
++ if (ret)
++ return ret;
++ BAR0_WI32(priv->fb_bar->gpuobj, 0x00, 0x7fc00000);
++ BAR0_WI32(priv->fb_bar->gpuobj, 0x04, 0x40000000 +
++ drm_get_resource_len(dev, 1) - 1);
++ BAR0_WI32(priv->fb_bar->gpuobj, 0x08, 0x40000000);
++ BAR0_WI32(priv->fb_bar->gpuobj, 0x0c, 0x00000000);
++ BAR0_WI32(priv->fb_bar->gpuobj, 0x10, 0x00000000);
++ BAR0_WI32(priv->fb_bar->gpuobj, 0x14, 0x00000000);
++
++ /* Poke the relevant regs, and pray it works :) */
++ nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12));
++ nv_wr32(dev, NV50_PUNK_UNK1710, 0);
++ nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12) |
++ NV50_PUNK_BAR_CFG_BASE_VALID);
++ nv_wr32(dev, NV50_PUNK_BAR1_CTXDMA, (priv->fb_bar->instance >> 4) |
++ NV50_PUNK_BAR1_CTXDMA_VALID);
++ nv_wr32(dev, NV50_PUNK_BAR3_CTXDMA, (priv->pramin_bar->instance >> 4) |
++ NV50_PUNK_BAR3_CTXDMA_VALID);
++
++ for (i = 0; i < 8; i++)
++ nv_wr32(dev, 0x1900 + (i*4), 0);
++
++ /* Assume that praying isn't enough, check that we can re-read the
++ * entire fake channel back from the PRAMIN BAR */
++ dev_priv->engine.instmem.prepare_access(dev, false);
++ for (i = 0; i < c_size; i += 4) {
++ if (nv_rd32(dev, NV_RAMIN + i) != nv_ri32(dev, i)) {
++ NV_ERROR(dev, "Error reading back PRAMIN at 0x%08x\n",
++ i);
++ dev_priv->engine.instmem.finish_access(dev);
++ return -EINVAL;
++ }
++ }
++ dev_priv->engine.instmem.finish_access(dev);
++
++ nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, save_nv001700);
++
++ /* Global PRAMIN heap */
++ if (nouveau_mem_init_heap(&dev_priv->ramin_heap,
++ c_size, dev_priv->ramin_size - c_size)) {
++ dev_priv->ramin_heap = NULL;
++ NV_ERROR(dev, "Failed to init RAMIN heap\n");
++ }
++
++ /*XXX: incorrect, but needed to make hash func "work" */
++ dev_priv->ramht_offset = 0x10000;
++ dev_priv->ramht_bits = 9;
++ dev_priv->ramht_size = (1 << dev_priv->ramht_bits);
++ return 0;
++}
++
++void
++nv50_instmem_takedown(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
++ struct nouveau_channel *chan = dev_priv->fifos[0];
++ int i;
++
++ NV_DEBUG(dev, "\n");
++
++ if (!priv)
++ return;
++
++ /* Restore state from before init */
++ for (i = 0x1700; i <= 0x1710; i += 4)
++ nv_wr32(dev, i, priv->save1700[(i - 0x1700) / 4]);
++
++ nouveau_gpuobj_ref_del(dev, &priv->fb_bar);
++ nouveau_gpuobj_ref_del(dev, &priv->pramin_bar);
++ nouveau_gpuobj_ref_del(dev, &priv->pramin_pt);
++
++ /* Destroy dummy channel */
++ if (chan) {
++ for (i = 0; i < dev_priv->vm_vram_pt_nr; i++) {
++ nouveau_gpuobj_ref_del(dev, &chan->vm_vram_pt[i]);
++ dev_priv->vm_vram_pt[i] = NULL;
++ }
++ dev_priv->vm_vram_pt_nr = 0;
++
++ nouveau_gpuobj_del(dev, &chan->vm_pd);
++ nouveau_gpuobj_ref_del(dev, &chan->ramfc);
++ nouveau_gpuobj_ref_del(dev, &chan->ramin);
++ nouveau_mem_takedown(&chan->ramin_heap);
++
++ dev_priv->fifos[0] = dev_priv->fifos[127] = NULL;
++ kfree(chan);
++ }
++
++ dev_priv->engine.instmem.priv = NULL;
++ kfree(priv);
++}
++
++int
++nv50_instmem_suspend(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *chan = dev_priv->fifos[0];
++ struct nouveau_gpuobj *ramin = chan->ramin->gpuobj;
++ int i;
++
++ ramin->im_backing_suspend = vmalloc(ramin->im_pramin->size);
++ if (!ramin->im_backing_suspend)
++ return -ENOMEM;
++
++ for (i = 0; i < ramin->im_pramin->size; i += 4)
++ ramin->im_backing_suspend[i/4] = nv_ri32(dev, i);
++ return 0;
++}
++
++void
++nv50_instmem_resume(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
++ struct nouveau_channel *chan = dev_priv->fifos[0];
++ struct nouveau_gpuobj *ramin = chan->ramin->gpuobj;
++ int i;
++
++ nv_wr32(dev, NV50_PUNK_BAR0_PRAMIN, (ramin->im_backing_start >> 16));
++ for (i = 0; i < ramin->im_pramin->size; i += 4)
++ BAR0_WI32(ramin, i, ramin->im_backing_suspend[i/4]);
++ vfree(ramin->im_backing_suspend);
++ ramin->im_backing_suspend = NULL;
++
++ /* Poke the relevant regs, and pray it works :) */
++ nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12));
++ nv_wr32(dev, NV50_PUNK_UNK1710, 0);
++ nv_wr32(dev, NV50_PUNK_BAR_CFG_BASE, (chan->ramin->instance >> 12) |
++ NV50_PUNK_BAR_CFG_BASE_VALID);
++ nv_wr32(dev, NV50_PUNK_BAR1_CTXDMA, (priv->fb_bar->instance >> 4) |
++ NV50_PUNK_BAR1_CTXDMA_VALID);
++ nv_wr32(dev, NV50_PUNK_BAR3_CTXDMA, (priv->pramin_bar->instance >> 4) |
++ NV50_PUNK_BAR3_CTXDMA_VALID);
++
++ for (i = 0; i < 8; i++)
++ nv_wr32(dev, 0x1900 + (i*4), 0);
++}
++
++int
++nv50_instmem_populate(struct drm_device *dev, struct nouveau_gpuobj *gpuobj,
++ uint32_t *sz)
++{
++ int ret;
++
++ if (gpuobj->im_backing)
++ return -EINVAL;
++
++ *sz = (*sz + (NV50_INSTMEM_PAGE_SIZE-1)) & ~(NV50_INSTMEM_PAGE_SIZE-1);
++ if (*sz == 0)
++ return -EINVAL;
++
++ ret = nouveau_bo_new(dev, NULL, *sz, 0, TTM_PL_FLAG_VRAM, 0, 0x0000,
++ true, false, &gpuobj->im_backing);
++ if (ret) {
++ NV_ERROR(dev, "error getting PRAMIN backing pages: %d\n", ret);
++ return ret;
++ }
++
++ ret = nouveau_bo_pin(gpuobj->im_backing, TTM_PL_FLAG_VRAM);
++ if (ret) {
++ NV_ERROR(dev, "error pinning PRAMIN backing VRAM: %d\n", ret);
++ nouveau_bo_ref(NULL, &gpuobj->im_backing);
++ return ret;
++ }
++
++ gpuobj->im_backing_start = gpuobj->im_backing->bo.mem.mm_node->start;
++ gpuobj->im_backing_start <<= PAGE_SHIFT;
++
++ return 0;
++}
++
++void
++nv50_instmem_clear(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++
++ if (gpuobj && gpuobj->im_backing) {
++ if (gpuobj->im_bound)
++ dev_priv->engine.instmem.unbind(dev, gpuobj);
++ nouveau_bo_unpin(gpuobj->im_backing);
++ nouveau_bo_ref(NULL, &gpuobj->im_backing);
++ gpuobj->im_backing = NULL;
++ }
++}
++
++int
++nv50_instmem_bind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
++ uint32_t pte, pte_end, vram;
++
++ if (!gpuobj->im_backing || !gpuobj->im_pramin || gpuobj->im_bound)
++ return -EINVAL;
++
++ NV_DEBUG(dev, "st=0x%0llx sz=0x%0llx\n",
++ gpuobj->im_pramin->start, gpuobj->im_pramin->size);
++
++ pte = (gpuobj->im_pramin->start >> 12) << 3;
++ pte_end = ((gpuobj->im_pramin->size >> 12) << 3) + pte;
++ vram = gpuobj->im_backing_start;
++
++ NV_DEBUG(dev, "pramin=0x%llx, pte=%d, pte_end=%d\n",
++ gpuobj->im_pramin->start, pte, pte_end);
++ NV_DEBUG(dev, "first vram page: 0x%08x\n", gpuobj->im_backing_start);
++
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ while (pte < pte_end) {
++ nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 0)/4, vram | 1);
++ nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 4)/4, 0x00000000);
++
++ pte += 8;
++ vram += NV50_INSTMEM_PAGE_SIZE;
++ }
++ dev_priv->engine.instmem.finish_access(dev);
++
++ nv_wr32(dev, 0x100c80, 0x00040001);
++ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
++ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (1)\n");
++ NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
++ return -EBUSY;
++ }
++
++ nv_wr32(dev, 0x100c80, 0x00060001);
++ if (!nv_wait(0x100c80, 0x00000001, 0x00000000)) {
++ NV_ERROR(dev, "timeout: (0x100c80 & 1) == 0 (2)\n");
++ NV_ERROR(dev, "0x100c80 = 0x%08x\n", nv_rd32(dev, 0x100c80));
++ return -EBUSY;
++ }
++
++ gpuobj->im_bound = 1;
++ return 0;
++}
++
++int
++nv50_instmem_unbind(struct drm_device *dev, struct nouveau_gpuobj *gpuobj)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
++ uint32_t pte, pte_end;
++
++ if (gpuobj->im_bound == 0)
++ return -EINVAL;
++
++ pte = (gpuobj->im_pramin->start >> 12) << 3;
++ pte_end = ((gpuobj->im_pramin->size >> 12) << 3) + pte;
++
++ dev_priv->engine.instmem.prepare_access(dev, true);
++ while (pte < pte_end) {
++ nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 0)/4, 0x00000009);
++ nv_wo32(dev, priv->pramin_pt->gpuobj, (pte + 4)/4, 0x00000000);
++ pte += 8;
++ }
++ dev_priv->engine.instmem.finish_access(dev);
++
++ gpuobj->im_bound = 0;
++ return 0;
++}
++
++void
++nv50_instmem_prepare_access(struct drm_device *dev, bool write)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
++
++ priv->last_access_wr = write;
++}
++
++void
++nv50_instmem_finish_access(struct drm_device *dev)
++{
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nv50_instmem_priv *priv = dev_priv->engine.instmem.priv;
++
++ if (priv->last_access_wr) {
++ nv_wr32(dev, 0x070000, 0x00000001);
++ if (!nv_wait(0x070000, 0x00000001, 0x00000000))
++ NV_ERROR(dev, "PRAMIN flush timeout\n");
++ }
++}
++
+diff --git a/drivers/gpu/drm/nouveau/nv50_mc.c b/drivers/gpu/drm/nouveau/nv50_mc.c
+new file mode 100644
+index 0000000..e0a9c3f
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv50_mc.c
+@@ -0,0 +1,40 @@
++/*
++ * Copyright (C) 2007 Ben Skeggs.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "drm.h"
++#include "nouveau_drv.h"
++
++int
++nv50_mc_init(struct drm_device *dev)
++{
++ nv_wr32(dev, NV03_PMC_ENABLE, 0xFFFFFFFF);
++ return 0;
++}
++
++void nv50_mc_takedown(struct drm_device *dev)
++{
++}
+diff --git a/drivers/gpu/drm/nouveau/nv50_sor.c b/drivers/gpu/drm/nouveau/nv50_sor.c
+new file mode 100644
+index 0000000..c2fff54
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nv50_sor.c
+@@ -0,0 +1,323 @@
++/*
++ * Copyright (C) 2008 Maarten Maathuis.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#include "drmP.h"
++#include "drm_crtc_helper.h"
++
++#define NOUVEAU_DMA_DEBUG (nouveau_reg_debug & NOUVEAU_REG_DEBUG_EVO)
++#include "nouveau_reg.h"
++#include "nouveau_drv.h"
++#include "nouveau_dma.h"
++#include "nouveau_encoder.h"
++#include "nouveau_connector.h"
++#include "nouveau_crtc.h"
++#include "nv50_display.h"
++
++static void
++nv50_sor_disconnect(struct nouveau_encoder *nv_encoder)
++{
++ struct drm_device *dev = to_drm_encoder(nv_encoder)->dev;
++ struct drm_nouveau_private *dev_priv = dev->dev_private;
++ struct nouveau_channel *evo = dev_priv->evo;
++ int ret;
++
++ NV_DEBUG_KMS(dev, "Disconnecting SOR %d\n", nv_encoder->or);
++
++ ret = RING_SPACE(evo, 2);
++ if (ret) {
++ NV_ERROR(dev, "no space while disconnecting SOR\n");
++ return;
++ }
++ BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1);
++ OUT_RING(evo, 0);
++}
++
++static void
++nv50_sor_dp_link_train(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct bit_displayport_encoder_table *dpe;
++ int dpe_headerlen;
++
++ dpe = nouveau_bios_dp_table(dev, nv_encoder->dcb, &dpe_headerlen);
++ if (!dpe) {
++ NV_ERROR(dev, "SOR-%d: no DP encoder table!\n", nv_encoder->or);
++ return;
++ }
++
++ if (dpe->script0) {
++ NV_DEBUG_KMS(dev, "SOR-%d: running DP script 0\n", nv_encoder->or);
++ nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script0),
++ nv_encoder->dcb);
++ }
++
++ if (!nouveau_dp_link_train(encoder))
++ NV_ERROR(dev, "SOR-%d: link training failed\n", nv_encoder->or);
++
++ if (dpe->script1) {
++ NV_DEBUG_KMS(dev, "SOR-%d: running DP script 1\n", nv_encoder->or);
++ nouveau_bios_run_init_table(dev, le16_to_cpu(dpe->script1),
++ nv_encoder->dcb);
++ }
++}
++
++static void
++nv50_sor_dpms(struct drm_encoder *encoder, int mode)
++{
++ struct drm_device *dev = encoder->dev;
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_encoder *enc;
++ uint32_t val;
++ int or = nv_encoder->or;
++
++ NV_DEBUG_KMS(dev, "or %d mode %d\n", or, mode);
++
++ nv_encoder->last_dpms = mode;
++ list_for_each_entry(enc, &dev->mode_config.encoder_list, head) {
++ struct nouveau_encoder *nvenc = nouveau_encoder(enc);
++
++ if (nvenc == nv_encoder ||
++ nvenc->disconnect != nv50_sor_disconnect ||
++ nvenc->dcb->or != nv_encoder->dcb->or)
++ continue;
++
++ if (nvenc->last_dpms == DRM_MODE_DPMS_ON)
++ return;
++ }
++
++ /* wait for it to be done */
++ if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_CTRL(or),
++ NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING, 0)) {
++ NV_ERROR(dev, "timeout: SOR_DPMS_CTRL_PENDING(%d) == 0\n", or);
++ NV_ERROR(dev, "SOR_DPMS_CTRL(%d) = 0x%08x\n", or,
++ nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or)));
++ }
++
++ val = nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or));
++
++ if (mode == DRM_MODE_DPMS_ON)
++ val |= NV50_PDISPLAY_SOR_DPMS_CTRL_ON;
++ else
++ val &= ~NV50_PDISPLAY_SOR_DPMS_CTRL_ON;
++
++ nv_wr32(dev, NV50_PDISPLAY_SOR_DPMS_CTRL(or), val |
++ NV50_PDISPLAY_SOR_DPMS_CTRL_PENDING);
++ if (!nv_wait(NV50_PDISPLAY_SOR_DPMS_STATE(or),
++ NV50_PDISPLAY_SOR_DPMS_STATE_WAIT, 0)) {
++ NV_ERROR(dev, "timeout: SOR_DPMS_STATE_WAIT(%d) == 0\n", or);
++ NV_ERROR(dev, "SOR_DPMS_STATE(%d) = 0x%08x\n", or,
++ nv_rd32(dev, NV50_PDISPLAY_SOR_DPMS_STATE(or)));
++ }
++
++ if (nv_encoder->dcb->type == OUTPUT_DP && mode == DRM_MODE_DPMS_ON)
++ nv50_sor_dp_link_train(encoder);
++}
++
++static void
++nv50_sor_save(struct drm_encoder *encoder)
++{
++ NV_ERROR(encoder->dev, "!!\n");
++}
++
++static void
++nv50_sor_restore(struct drm_encoder *encoder)
++{
++ NV_ERROR(encoder->dev, "!!\n");
++}
++
++static bool
++nv50_sor_mode_fixup(struct drm_encoder *encoder, struct drm_display_mode *mode,
++ struct drm_display_mode *adjusted_mode)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct nouveau_connector *connector;
++
++ NV_DEBUG_KMS(encoder->dev, "or %d\n", nv_encoder->or);
++
++ connector = nouveau_encoder_connector_get(nv_encoder);
++ if (!connector) {
++ NV_ERROR(encoder->dev, "Encoder has no connector\n");
++ return false;
++ }
++
++ if (connector->scaling_mode != DRM_MODE_SCALE_NONE &&
++ connector->native_mode) {
++ int id = adjusted_mode->base.id;
++ *adjusted_mode = *connector->native_mode;
++ adjusted_mode->base.id = id;
++ }
++
++ return true;
++}
++
++static void
++nv50_sor_prepare(struct drm_encoder *encoder)
++{
++}
++
++static void
++nv50_sor_commit(struct drm_encoder *encoder)
++{
++}
++
++static void
++nv50_sor_mode_set(struct drm_encoder *encoder, struct drm_display_mode *mode,
++ struct drm_display_mode *adjusted_mode)
++{
++ struct drm_nouveau_private *dev_priv = encoder->dev->dev_private;
++ struct nouveau_channel *evo = dev_priv->evo;
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++ struct drm_device *dev = encoder->dev;
++ struct nouveau_crtc *crtc = nouveau_crtc(encoder->crtc);
++ uint32_t mode_ctl = 0;
++ int ret;
++
++ NV_DEBUG_KMS(dev, "or %d\n", nv_encoder->or);
++
++ nv50_sor_dpms(encoder, DRM_MODE_DPMS_ON);
++
++ switch (nv_encoder->dcb->type) {
++ case OUTPUT_TMDS:
++ if (nv_encoder->dcb->sorconf.link & 1) {
++ if (adjusted_mode->clock < 165000)
++ mode_ctl = 0x0100;
++ else
++ mode_ctl = 0x0500;
++ } else
++ mode_ctl = 0x0200;
++ break;
++ case OUTPUT_DP:
++ mode_ctl |= 0x00050000;
++ if (nv_encoder->dcb->sorconf.link & 1)
++ mode_ctl |= 0x00000800;
++ else
++ mode_ctl |= 0x00000900;
++ break;
++ default:
++ break;
++ }
++
++ if (crtc->index == 1)
++ mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC1;
++ else
++ mode_ctl |= NV50_EVO_SOR_MODE_CTRL_CRTC0;
++
++ if (adjusted_mode->flags & DRM_MODE_FLAG_NHSYNC)
++ mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NHSYNC;
++
++ if (adjusted_mode->flags & DRM_MODE_FLAG_NVSYNC)
++ mode_ctl |= NV50_EVO_SOR_MODE_CTRL_NVSYNC;
++
++ ret = RING_SPACE(evo, 2);
++ if (ret) {
++ NV_ERROR(dev, "no space while connecting SOR\n");
++ return;
++ }
++ BEGIN_RING(evo, 0, NV50_EVO_SOR(nv_encoder->or, MODE_CTRL), 1);
++ OUT_RING(evo, mode_ctl);
++}
++
++static const struct drm_encoder_helper_funcs nv50_sor_helper_funcs = {
++ .dpms = nv50_sor_dpms,
++ .save = nv50_sor_save,
++ .restore = nv50_sor_restore,
++ .mode_fixup = nv50_sor_mode_fixup,
++ .prepare = nv50_sor_prepare,
++ .commit = nv50_sor_commit,
++ .mode_set = nv50_sor_mode_set,
++ .detect = NULL
++};
++
++static void
++nv50_sor_destroy(struct drm_encoder *encoder)
++{
++ struct nouveau_encoder *nv_encoder = nouveau_encoder(encoder);
++
++ if (!encoder)
++ return;
++
++ NV_DEBUG_KMS(encoder->dev, "\n");
++
++ drm_encoder_cleanup(encoder);
++
++ kfree(nv_encoder);
++}
++
++static const struct drm_encoder_funcs nv50_sor_encoder_funcs = {
++ .destroy = nv50_sor_destroy,
++};
++
++int
++nv50_sor_create(struct drm_device *dev, struct dcb_entry *entry)
++{
++ struct nouveau_encoder *nv_encoder = NULL;
++ struct drm_encoder *encoder;
++ bool dum;
++ int type;
++
++ NV_DEBUG_KMS(dev, "\n");
++
++ switch (entry->type) {
++ case OUTPUT_TMDS:
++ NV_INFO(dev, "Detected a TMDS output\n");
++ type = DRM_MODE_ENCODER_TMDS;
++ break;
++ case OUTPUT_LVDS:
++ NV_INFO(dev, "Detected a LVDS output\n");
++ type = DRM_MODE_ENCODER_LVDS;
++
++ if (nouveau_bios_parse_lvds_table(dev, 0, &dum, &dum)) {
++ NV_ERROR(dev, "Failed parsing LVDS table\n");
++ return -EINVAL;
++ }
++ break;
++ case OUTPUT_DP:
++ NV_INFO(dev, "Detected a DP output\n");
++ type = DRM_MODE_ENCODER_TMDS;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ nv_encoder = kzalloc(sizeof(*nv_encoder), GFP_KERNEL);
++ if (!nv_encoder)
++ return -ENOMEM;
++ encoder = to_drm_encoder(nv_encoder);
++
++ nv_encoder->dcb = entry;
++ nv_encoder->or = ffs(entry->or) - 1;
++
++ nv_encoder->disconnect = nv50_sor_disconnect;
++
++ drm_encoder_init(dev, encoder, &nv50_sor_encoder_funcs, type);
++ drm_encoder_helper_add(encoder, &nv50_sor_helper_funcs);
++
++ encoder->possible_crtcs = entry->heads;
++ encoder->possible_clones = 0;
++
++ return 0;
++}
+diff --git a/drivers/gpu/drm/nouveau/nvreg.h b/drivers/gpu/drm/nouveau/nvreg.h
+new file mode 100644
+index 0000000..5998c35
+--- /dev/null
++++ b/drivers/gpu/drm/nouveau/nvreg.h
+@@ -0,0 +1,535 @@
++/* $XConsortium: nvreg.h /main/2 1996/10/28 05:13:41 kaleb $ */
++/*
++ * Copyright 1996-1997 David J. McKay
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * DAVID J. MCKAY BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
++ * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
++ * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ */
++
++/* $XFree86: xc/programs/Xserver/hw/xfree86/drivers/nv/nvreg.h,v 1.6 2002/01/25 21:56:06 tsi Exp $ */
++
++#ifndef __NVREG_H_
++#define __NVREG_H_
++
++#define NV_PMC_OFFSET 0x00000000
++#define NV_PMC_SIZE 0x00001000
++
++#define NV_PBUS_OFFSET 0x00001000
++#define NV_PBUS_SIZE 0x00001000
++
++#define NV_PFIFO_OFFSET 0x00002000
++#define NV_PFIFO_SIZE 0x00002000
++
++#define NV_HDIAG_OFFSET 0x00005000
++#define NV_HDIAG_SIZE 0x00001000
++
++#define NV_PRAM_OFFSET 0x00006000
++#define NV_PRAM_SIZE 0x00001000
++
++#define NV_PVIDEO_OFFSET 0x00008000
++#define NV_PVIDEO_SIZE 0x00001000
++
++#define NV_PTIMER_OFFSET 0x00009000
++#define NV_PTIMER_SIZE 0x00001000
++
++#define NV_PPM_OFFSET 0x0000A000
++#define NV_PPM_SIZE 0x00001000
++
++#define NV_PTV_OFFSET 0x0000D000
++#define NV_PTV_SIZE 0x00001000
++
++#define NV_PRMVGA_OFFSET 0x000A0000
++#define NV_PRMVGA_SIZE 0x00020000
++
++#define NV_PRMVIO0_OFFSET 0x000C0000
++#define NV_PRMVIO_SIZE 0x00002000
++#define NV_PRMVIO1_OFFSET 0x000C2000
++
++#define NV_PFB_OFFSET 0x00100000
++#define NV_PFB_SIZE 0x00001000
++
++#define NV_PEXTDEV_OFFSET 0x00101000
++#define NV_PEXTDEV_SIZE 0x00001000
++
++#define NV_PME_OFFSET 0x00200000
++#define NV_PME_SIZE 0x00001000
++
++#define NV_PROM_OFFSET 0x00300000
++#define NV_PROM_SIZE 0x00010000
++
++#define NV_PGRAPH_OFFSET 0x00400000
++#define NV_PGRAPH_SIZE 0x00010000
++
++#define NV_PCRTC0_OFFSET 0x00600000
++#define NV_PCRTC0_SIZE 0x00002000 /* empirical */
++
++#define NV_PRMCIO0_OFFSET 0x00601000
++#define NV_PRMCIO_SIZE 0x00002000
++#define NV_PRMCIO1_OFFSET 0x00603000
++
++#define NV50_DISPLAY_OFFSET 0x00610000
++#define NV50_DISPLAY_SIZE 0x0000FFFF
++
++#define NV_PRAMDAC0_OFFSET 0x00680000
++#define NV_PRAMDAC0_SIZE 0x00002000
++
++#define NV_PRMDIO0_OFFSET 0x00681000
++#define NV_PRMDIO_SIZE 0x00002000
++#define NV_PRMDIO1_OFFSET 0x00683000
++
++#define NV_PRAMIN_OFFSET 0x00700000
++#define NV_PRAMIN_SIZE 0x00100000
++
++#define NV_FIFO_OFFSET 0x00800000
++#define NV_FIFO_SIZE 0x00800000
++
++#define NV_PMC_BOOT_0 0x00000000
++#define NV_PMC_ENABLE 0x00000200
++
++#define NV_VIO_VSE2 0x000003c3
++#define NV_VIO_SRX 0x000003c4
++
++#define NV_CIO_CRX__COLOR 0x000003d4
++#define NV_CIO_CR__COLOR 0x000003d5
++
++#define NV_PBUS_DEBUG_1 0x00001084
++#define NV_PBUS_DEBUG_4 0x00001098
++#define NV_PBUS_DEBUG_DUALHEAD_CTL 0x000010f0
++#define NV_PBUS_POWERCTRL_1 0x00001584
++#define NV_PBUS_POWERCTRL_2 0x00001588
++#define NV_PBUS_POWERCTRL_4 0x00001590
++#define NV_PBUS_PCI_NV_19 0x0000184C
++#define NV_PBUS_PCI_NV_20 0x00001850
++# define NV_PBUS_PCI_NV_20_ROM_SHADOW_DISABLED (0 << 0)
++# define NV_PBUS_PCI_NV_20_ROM_SHADOW_ENABLED (1 << 0)
++
++#define NV_PFIFO_RAMHT 0x00002210
++
++#define NV_PTV_TV_INDEX 0x0000d220
++#define NV_PTV_TV_DATA 0x0000d224
++#define NV_PTV_HFILTER 0x0000d310
++#define NV_PTV_HFILTER2 0x0000d390
++#define NV_PTV_VFILTER 0x0000d510
++
++#define NV_PRMVIO_MISC__WRITE 0x000c03c2
++#define NV_PRMVIO_SRX 0x000c03c4
++#define NV_PRMVIO_SR 0x000c03c5
++# define NV_VIO_SR_RESET_INDEX 0x00
++# define NV_VIO_SR_CLOCK_INDEX 0x01
++# define NV_VIO_SR_PLANE_MASK_INDEX 0x02
++# define NV_VIO_SR_CHAR_MAP_INDEX 0x03
++# define NV_VIO_SR_MEM_MODE_INDEX 0x04
++#define NV_PRMVIO_MISC__READ 0x000c03cc
++#define NV_PRMVIO_GRX 0x000c03ce
++#define NV_PRMVIO_GX 0x000c03cf
++# define NV_VIO_GX_SR_INDEX 0x00
++# define NV_VIO_GX_SREN_INDEX 0x01
++# define NV_VIO_GX_CCOMP_INDEX 0x02
++# define NV_VIO_GX_ROP_INDEX 0x03
++# define NV_VIO_GX_READ_MAP_INDEX 0x04
++# define NV_VIO_GX_MODE_INDEX 0x05
++# define NV_VIO_GX_MISC_INDEX 0x06
++# define NV_VIO_GX_DONT_CARE_INDEX 0x07
++# define NV_VIO_GX_BIT_MASK_INDEX 0x08
++
++#define NV_PFB_BOOT_0 0x00100000
++#define NV_PFB_CFG0 0x00100200
++#define NV_PFB_CFG1 0x00100204
++#define NV_PFB_CSTATUS 0x0010020C
++#define NV_PFB_REFCTRL 0x00100210
++# define NV_PFB_REFCTRL_VALID_1 (1 << 31)
++#define NV_PFB_PAD 0x0010021C
++# define NV_PFB_PAD_CKE_NORMAL (1 << 0)
++#define NV_PFB_TILE_NV10 0x00100240
++#define NV_PFB_TILE_SIZE_NV10 0x00100244
++#define NV_PFB_REF 0x001002D0
++# define NV_PFB_REF_CMD_REFRESH (1 << 0)
++#define NV_PFB_PRE 0x001002D4
++# define NV_PFB_PRE_CMD_PRECHARGE (1 << 0)
++#define NV_PFB_CLOSE_PAGE2 0x0010033C
++#define NV_PFB_TILE_NV40 0x00100600
++#define NV_PFB_TILE_SIZE_NV40 0x00100604
++
++#define NV_PEXTDEV_BOOT_0 0x00101000
++# define NV_PEXTDEV_BOOT_0_STRAP_FP_IFACE_12BIT (8 << 12)
++#define NV_PEXTDEV_BOOT_3 0x0010100c
++
++#define NV_PCRTC_INTR_0 0x00600100
++# define NV_PCRTC_INTR_0_VBLANK (1 << 0)
++#define NV_PCRTC_INTR_EN_0 0x00600140
++#define NV_PCRTC_START 0x00600800
++#define NV_PCRTC_CONFIG 0x00600804
++# define NV_PCRTC_CONFIG_START_ADDRESS_NON_VGA (1 << 0)
++# define NV_PCRTC_CONFIG_START_ADDRESS_HSYNC (2 << 0)
++#define NV_PCRTC_CURSOR_CONFIG 0x00600810
++# define NV_PCRTC_CURSOR_CONFIG_ENABLE_ENABLE (1 << 0)
++# define NV_PCRTC_CURSOR_CONFIG_DOUBLE_SCAN_ENABLE (1 << 4)
++# define NV_PCRTC_CURSOR_CONFIG_ADDRESS_SPACE_PNVM (1 << 8)
++# define NV_PCRTC_CURSOR_CONFIG_CUR_BPP_32 (1 << 12)
++# define NV_PCRTC_CURSOR_CONFIG_CUR_PIXELS_64 (1 << 16)
++# define NV_PCRTC_CURSOR_CONFIG_CUR_LINES_32 (2 << 24)
++# define NV_PCRTC_CURSOR_CONFIG_CUR_LINES_64 (4 << 24)
++# define NV_PCRTC_CURSOR_CONFIG_CUR_BLEND_ALPHA (1 << 28)
++
++/* note: PCRTC_GPIO is not available on nv10, and in fact aliases 0x600810 */
++#define NV_PCRTC_GPIO 0x00600818
++#define NV_PCRTC_GPIO_EXT 0x0060081c
++#define NV_PCRTC_830 0x00600830
++#define NV_PCRTC_834 0x00600834
++#define NV_PCRTC_850 0x00600850
++#define NV_PCRTC_ENGINE_CTRL 0x00600860
++# define NV_CRTC_FSEL_I2C (1 << 4)
++# define NV_CRTC_FSEL_OVERLAY (1 << 12)
++
++#define NV_PRMCIO_ARX 0x006013c0
++#define NV_PRMCIO_AR__WRITE 0x006013c0
++#define NV_PRMCIO_AR__READ 0x006013c1
++# define NV_CIO_AR_MODE_INDEX 0x10
++# define NV_CIO_AR_OSCAN_INDEX 0x11
++# define NV_CIO_AR_PLANE_INDEX 0x12
++# define NV_CIO_AR_HPP_INDEX 0x13
++# define NV_CIO_AR_CSEL_INDEX 0x14
++#define NV_PRMCIO_INP0 0x006013c2
++#define NV_PRMCIO_CRX__COLOR 0x006013d4
++#define NV_PRMCIO_CR__COLOR 0x006013d5
++ /* Standard VGA CRTC registers */
++# define NV_CIO_CR_HDT_INDEX 0x00 /* horizontal display total */
++# define NV_CIO_CR_HDE_INDEX 0x01 /* horizontal display end */
++# define NV_CIO_CR_HBS_INDEX 0x02 /* horizontal blanking start */
++# define NV_CIO_CR_HBE_INDEX 0x03 /* horizontal blanking end */
++# define NV_CIO_CR_HBE_4_0 4:0
++# define NV_CIO_CR_HRS_INDEX 0x04 /* horizontal retrace start */
++# define NV_CIO_CR_HRE_INDEX 0x05 /* horizontal retrace end */
++# define NV_CIO_CR_HRE_4_0 4:0
++# define NV_CIO_CR_HRE_HBE_5 7:7
++# define NV_CIO_CR_VDT_INDEX 0x06 /* vertical display total */
++# define NV_CIO_CR_OVL_INDEX 0x07 /* overflow bits */
++# define NV_CIO_CR_OVL_VDT_8 0:0
++# define NV_CIO_CR_OVL_VDE_8 1:1
++# define NV_CIO_CR_OVL_VRS_8 2:2
++# define NV_CIO_CR_OVL_VBS_8 3:3
++# define NV_CIO_CR_OVL_VDT_9 5:5
++# define NV_CIO_CR_OVL_VDE_9 6:6
++# define NV_CIO_CR_OVL_VRS_9 7:7
++# define NV_CIO_CR_RSAL_INDEX 0x08 /* normally "preset row scan" */
++# define NV_CIO_CR_CELL_HT_INDEX 0x09 /* cell height?! normally "max scan line" */
++# define NV_CIO_CR_CELL_HT_VBS_9 5:5
++# define NV_CIO_CR_CELL_HT_SCANDBL 7:7
++# define NV_CIO_CR_CURS_ST_INDEX 0x0a /* cursor start */
++# define NV_CIO_CR_CURS_END_INDEX 0x0b /* cursor end */
++# define NV_CIO_CR_SA_HI_INDEX 0x0c /* screen start address high */
++# define NV_CIO_CR_SA_LO_INDEX 0x0d /* screen start address low */
++# define NV_CIO_CR_TCOFF_HI_INDEX 0x0e /* cursor offset high */
++# define NV_CIO_CR_TCOFF_LO_INDEX 0x0f /* cursor offset low */
++# define NV_CIO_CR_VRS_INDEX 0x10 /* vertical retrace start */
++# define NV_CIO_CR_VRE_INDEX 0x11 /* vertical retrace end */
++# define NV_CIO_CR_VRE_3_0 3:0
++# define NV_CIO_CR_VDE_INDEX 0x12 /* vertical display end */
++# define NV_CIO_CR_OFFSET_INDEX 0x13 /* sets screen pitch */
++# define NV_CIO_CR_ULINE_INDEX 0x14 /* underline location */
++# define NV_CIO_CR_VBS_INDEX 0x15 /* vertical blank start */
++# define NV_CIO_CR_VBE_INDEX 0x16 /* vertical blank end */
++# define NV_CIO_CR_MODE_INDEX 0x17 /* crtc mode control */
++# define NV_CIO_CR_LCOMP_INDEX 0x18 /* line compare */
++ /* Extended VGA CRTC registers */
++# define NV_CIO_CRE_RPC0_INDEX 0x19 /* repaint control 0 */
++# define NV_CIO_CRE_RPC0_OFFSET_10_8 7:5
++# define NV_CIO_CRE_RPC1_INDEX 0x1a /* repaint control 1 */
++# define NV_CIO_CRE_RPC1_LARGE 2:2
++# define NV_CIO_CRE_FF_INDEX 0x1b /* fifo control */
++# define NV_CIO_CRE_ENH_INDEX 0x1c /* enhanced? */
++# define NV_CIO_SR_LOCK_INDEX 0x1f /* crtc lock */
++# define NV_CIO_SR_UNLOCK_RW_VALUE 0x57
++# define NV_CIO_SR_LOCK_VALUE 0x99
++# define NV_CIO_CRE_FFLWM__INDEX 0x20 /* fifo low water mark */
++# define NV_CIO_CRE_21 0x21 /* vga shadow crtc lock */
++# define NV_CIO_CRE_LSR_INDEX 0x25 /* ? */
++# define NV_CIO_CRE_LSR_VDT_10 0:0
++# define NV_CIO_CRE_LSR_VDE_10 1:1
++# define NV_CIO_CRE_LSR_VRS_10 2:2
++# define NV_CIO_CRE_LSR_VBS_10 3:3
++# define NV_CIO_CRE_LSR_HBE_6 4:4
++# define NV_CIO_CR_ARX_INDEX 0x26 /* attribute index -- ro copy of 0x60.3c0 */
++# define NV_CIO_CRE_CHIP_ID_INDEX 0x27 /* chip revision */
++# define NV_CIO_CRE_PIXEL_INDEX 0x28
++# define NV_CIO_CRE_PIXEL_FORMAT 1:0
++# define NV_CIO_CRE_HEB__INDEX 0x2d /* horizontal extra bits? */
++# define NV_CIO_CRE_HEB_HDT_8 0:0
++# define NV_CIO_CRE_HEB_HDE_8 1:1
++# define NV_CIO_CRE_HEB_HBS_8 2:2
++# define NV_CIO_CRE_HEB_HRS_8 3:3
++# define NV_CIO_CRE_HEB_ILC_8 4:4
++# define NV_CIO_CRE_2E 0x2e /* some scratch or dummy reg to force writes to sink in */
++# define NV_CIO_CRE_HCUR_ADDR2_INDEX 0x2f /* cursor */
++# define NV_CIO_CRE_HCUR_ADDR0_INDEX 0x30 /* pixmap */
++# define NV_CIO_CRE_HCUR_ADDR0_ADR 6:0
++# define NV_CIO_CRE_HCUR_ASI 7:7
++# define NV_CIO_CRE_HCUR_ADDR1_INDEX 0x31 /* address */
++# define NV_CIO_CRE_HCUR_ADDR1_ENABLE 0:0
++# define NV_CIO_CRE_HCUR_ADDR1_CUR_DBL 1:1
++# define NV_CIO_CRE_HCUR_ADDR1_ADR 7:2
++# define NV_CIO_CRE_LCD__INDEX 0x33
++# define NV_CIO_CRE_LCD_LCD_SELECT 0:0
++# define NV_CIO_CRE_DDC0_STATUS__INDEX 0x36
++# define NV_CIO_CRE_DDC0_WR__INDEX 0x37
++# define NV_CIO_CRE_ILACE__INDEX 0x39 /* interlace */
++# define NV_CIO_CRE_SCRATCH3__INDEX 0x3b
++# define NV_CIO_CRE_SCRATCH4__INDEX 0x3c
++# define NV_CIO_CRE_DDC_STATUS__INDEX 0x3e
++# define NV_CIO_CRE_DDC_WR__INDEX 0x3f
++# define NV_CIO_CRE_EBR_INDEX 0x41 /* extra bits ? (vertical) */
++# define NV_CIO_CRE_EBR_VDT_11 0:0
++# define NV_CIO_CRE_EBR_VDE_11 2:2
++# define NV_CIO_CRE_EBR_VRS_11 4:4
++# define NV_CIO_CRE_EBR_VBS_11 6:6
++# define NV_CIO_CRE_43 0x43
++# define NV_CIO_CRE_44 0x44 /* head control */
++# define NV_CIO_CRE_CSB 0x45 /* colour saturation boost */
++# define NV_CIO_CRE_RCR 0x46
++# define NV_CIO_CRE_RCR_ENDIAN_BIG 7:7
++# define NV_CIO_CRE_47 0x47 /* extended fifo lwm, used on nv30+ */
++# define NV_CIO_CRE_49 0x49
++# define NV_CIO_CRE_4B 0x4b /* given patterns in 0x[2-3][a-c] regs, probably scratch 6 */
++# define NV_CIO_CRE_TVOUT_LATENCY 0x52
++# define NV_CIO_CRE_53 0x53 /* `fp_htiming' according to Haiku */
++# define NV_CIO_CRE_54 0x54 /* `fp_vtiming' according to Haiku */
++# define NV_CIO_CRE_57 0x57 /* index reg for cr58 */
++# define NV_CIO_CRE_58 0x58 /* data reg for cr57 */
++# define NV_CIO_CRE_59 0x59 /* related to on/off-chip-ness of digital outputs */
++# define NV_CIO_CRE_5B 0x5B /* newer colour saturation reg */
++# define NV_CIO_CRE_85 0x85
++# define NV_CIO_CRE_86 0x86
++#define NV_PRMCIO_INP0__COLOR 0x006013da
++
++#define NV_PRAMDAC_CU_START_POS 0x00680300
++# define NV_PRAMDAC_CU_START_POS_X 15:0
++# define NV_PRAMDAC_CU_START_POS_Y 31:16
++#define NV_RAMDAC_NV10_CURSYNC 0x00680404
++
++#define NV_PRAMDAC_NVPLL_COEFF 0x00680500
++#define NV_PRAMDAC_MPLL_COEFF 0x00680504
++#define NV_PRAMDAC_VPLL_COEFF 0x00680508
++# define NV30_RAMDAC_ENABLE_VCO2 (8 << 4)
++
++#define NV_PRAMDAC_PLL_COEFF_SELECT 0x0068050c
++# define NV_PRAMDAC_PLL_COEFF_SELECT_USE_VPLL2_TRUE (4 << 0)
++# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_MPLL (1 << 8)
++# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_VPLL (2 << 8)
++# define NV_PRAMDAC_PLL_COEFF_SELECT_SOURCE_PROG_NVPLL (4 << 8)
++# define NV_PRAMDAC_PLL_COEFF_SELECT_PLL_SOURCE_VPLL2 (8 << 8)
++# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK1 (1 << 16)
++# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK1 (2 << 16)
++# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_VSCLK2 (4 << 16)
++# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_PCLK2 (8 << 16)
++# define NV_PRAMDAC_PLL_COEFF_SELECT_TV_CLK_SOURCE_VIP (1 << 20)
++# define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK_RATIO_DB2 (1 << 28)
++# define NV_PRAMDAC_PLL_COEFF_SELECT_VCLK2_RATIO_DB2 (2 << 28)
++
++#define NV_PRAMDAC_PLL_SETUP_CONTROL 0x00680510
++#define NV_RAMDAC_VPLL2 0x00680520
++#define NV_PRAMDAC_SEL_CLK 0x00680524
++#define NV_RAMDAC_DITHER_NV11 0x00680528
++#define NV_PRAMDAC_DACCLK 0x0068052c
++# define NV_PRAMDAC_DACCLK_SEL_DACCLK (1 << 0)
++
++#define NV_RAMDAC_NVPLL_B 0x00680570
++#define NV_RAMDAC_MPLL_B 0x00680574
++#define NV_RAMDAC_VPLL_B 0x00680578
++#define NV_RAMDAC_VPLL2_B 0x0068057c
++# define NV31_RAMDAC_ENABLE_VCO2 (8 << 28)
++#define NV_PRAMDAC_580 0x00680580
++# define NV_RAMDAC_580_VPLL1_ACTIVE (1 << 8)
++# define NV_RAMDAC_580_VPLL2_ACTIVE (1 << 28)
++
++#define NV_PRAMDAC_GENERAL_CONTROL 0x00680600
++# define NV_PRAMDAC_GENERAL_CONTROL_PIXMIX_ON (3 << 4)
++# define NV_PRAMDAC_GENERAL_CONTROL_VGA_STATE_SEL (1 << 8)
++# define NV_PRAMDAC_GENERAL_CONTROL_ALT_MODE_SEL (1 << 12)
++# define NV_PRAMDAC_GENERAL_CONTROL_TERMINATION_75OHM (2 << 16)
++# define NV_PRAMDAC_GENERAL_CONTROL_BPC_8BITS (1 << 20)
++# define NV_PRAMDAC_GENERAL_CONTROL_PIPE_LONG (2 << 28)
++#define NV_PRAMDAC_TEST_CONTROL 0x00680608
++# define NV_PRAMDAC_TEST_CONTROL_TP_INS_EN_ASSERTED (1 << 12)
++# define NV_PRAMDAC_TEST_CONTROL_PWRDWN_DAC_OFF (1 << 16)
++# define NV_PRAMDAC_TEST_CONTROL_SENSEB_ALLHI (1 << 28)
++#define NV_PRAMDAC_TESTPOINT_DATA 0x00680610
++# define NV_PRAMDAC_TESTPOINT_DATA_NOTBLANK (8 << 28)
++#define NV_PRAMDAC_630 0x00680630
++#define NV_PRAMDAC_634 0x00680634
++
++#define NV_PRAMDAC_TV_SETUP 0x00680700
++#define NV_PRAMDAC_TV_VTOTAL 0x00680720
++#define NV_PRAMDAC_TV_VSKEW 0x00680724
++#define NV_PRAMDAC_TV_VSYNC_DELAY 0x00680728
++#define NV_PRAMDAC_TV_HTOTAL 0x0068072c
++#define NV_PRAMDAC_TV_HSKEW 0x00680730
++#define NV_PRAMDAC_TV_HSYNC_DELAY 0x00680734
++#define NV_PRAMDAC_TV_HSYNC_DELAY2 0x00680738
++
++#define NV_PRAMDAC_TV_SETUP 0x00680700
++
++#define NV_PRAMDAC_FP_VDISPLAY_END 0x00680800
++#define NV_PRAMDAC_FP_VTOTAL 0x00680804
++#define NV_PRAMDAC_FP_VCRTC 0x00680808
++#define NV_PRAMDAC_FP_VSYNC_START 0x0068080c
++#define NV_PRAMDAC_FP_VSYNC_END 0x00680810
++#define NV_PRAMDAC_FP_VVALID_START 0x00680814
++#define NV_PRAMDAC_FP_VVALID_END 0x00680818
++#define NV_PRAMDAC_FP_HDISPLAY_END 0x00680820
++#define NV_PRAMDAC_FP_HTOTAL 0x00680824
++#define NV_PRAMDAC_FP_HCRTC 0x00680828
++#define NV_PRAMDAC_FP_HSYNC_START 0x0068082c
++#define NV_PRAMDAC_FP_HSYNC_END 0x00680830
++#define NV_PRAMDAC_FP_HVALID_START 0x00680834
++#define NV_PRAMDAC_FP_HVALID_END 0x00680838
++
++#define NV_RAMDAC_FP_DITHER 0x0068083c
++#define NV_PRAMDAC_FP_TG_CONTROL 0x00680848
++# define NV_PRAMDAC_FP_TG_CONTROL_VSYNC_POS (1 << 0)
++# define NV_PRAMDAC_FP_TG_CONTROL_VSYNC_DISABLE (2 << 0)
++# define NV_PRAMDAC_FP_TG_CONTROL_HSYNC_POS (1 << 4)
++# define NV_PRAMDAC_FP_TG_CONTROL_HSYNC_DISABLE (2 << 4)
++# define NV_PRAMDAC_FP_TG_CONTROL_MODE_SCALE (0 << 8)
++# define NV_PRAMDAC_FP_TG_CONTROL_MODE_CENTER (1 << 8)
++# define NV_PRAMDAC_FP_TG_CONTROL_MODE_NATIVE (2 << 8)
++# define NV_PRAMDAC_FP_TG_CONTROL_READ_PROG (1 << 20)
++# define NV_PRAMDAC_FP_TG_CONTROL_WIDTH_12 (1 << 24)
++# define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_POS (1 << 28)
++# define NV_PRAMDAC_FP_TG_CONTROL_DISPEN_DISABLE (2 << 28)
++#define NV_PRAMDAC_FP_MARGIN_COLOR 0x0068084c
++#define NV_PRAMDAC_850 0x00680850
++#define NV_PRAMDAC_85C 0x0068085c
++#define NV_PRAMDAC_FP_DEBUG_0 0x00680880
++# define NV_PRAMDAC_FP_DEBUG_0_XSCALE_ENABLE (1 << 0)
++# define NV_PRAMDAC_FP_DEBUG_0_YSCALE_ENABLE (1 << 4)
++/* This doesn't seem to be essential for tmds, but still often set */
++# define NV_RAMDAC_FP_DEBUG_0_TMDS_ENABLED (8 << 4)
++# define NV_PRAMDAC_FP_DEBUG_0_XINTERP_BILINEAR (1 << 8)
++# define NV_PRAMDAC_FP_DEBUG_0_YINTERP_BILINEAR (1 << 12)
++# define NV_PRAMDAC_FP_DEBUG_0_XWEIGHT_ROUND (1 << 20)
++# define NV_PRAMDAC_FP_DEBUG_0_YWEIGHT_ROUND (1 << 24)
++# define NV_PRAMDAC_FP_DEBUG_0_PWRDOWN_FPCLK (1 << 28)
++#define NV_PRAMDAC_FP_DEBUG_1 0x00680884
++# define NV_PRAMDAC_FP_DEBUG_1_XSCALE_VALUE 11:0
++# define NV_PRAMDAC_FP_DEBUG_1_XSCALE_TESTMODE_ENABLE (1 << 12)
++# define NV_PRAMDAC_FP_DEBUG_1_YSCALE_VALUE 27:16
++# define NV_PRAMDAC_FP_DEBUG_1_YSCALE_TESTMODE_ENABLE (1 << 28)
++#define NV_PRAMDAC_FP_DEBUG_2 0x00680888
++#define NV_PRAMDAC_FP_DEBUG_3 0x0068088C
++
++/* see NV_PRAMDAC_INDIR_TMDS in rules.xml */
++#define NV_PRAMDAC_FP_TMDS_CONTROL 0x006808b0
++# define NV_PRAMDAC_FP_TMDS_CONTROL_WRITE_DISABLE (1 << 16)
++#define NV_PRAMDAC_FP_TMDS_DATA 0x006808b4
++
++#define NV_PRAMDAC_8C0 0x006808c0
++
++/* Some kind of switch */
++#define NV_PRAMDAC_900 0x00680900
++#define NV_PRAMDAC_A20 0x00680A20
++#define NV_PRAMDAC_A24 0x00680A24
++#define NV_PRAMDAC_A34 0x00680A34
++
++#define NV_PRAMDAC_CTV 0x00680c00
++
++/* names fabricated from NV_USER_DAC info */
++#define NV_PRMDIO_PIXEL_MASK 0x006813c6
++# define NV_PRMDIO_PIXEL_MASK_MASK 0xff
++#define NV_PRMDIO_READ_MODE_ADDRESS 0x006813c7
++#define NV_PRMDIO_WRITE_MODE_ADDRESS 0x006813c8
++#define NV_PRMDIO_PALETTE_DATA 0x006813c9
++
++#define NV_PGRAPH_DEBUG_0 0x00400080
++#define NV_PGRAPH_DEBUG_1 0x00400084
++#define NV_PGRAPH_DEBUG_2_NV04 0x00400088
++#define NV_PGRAPH_DEBUG_2 0x00400620
++#define NV_PGRAPH_DEBUG_3 0x0040008c
++#define NV_PGRAPH_DEBUG_4 0x00400090
++#define NV_PGRAPH_INTR 0x00400100
++#define NV_PGRAPH_INTR_EN 0x00400140
++#define NV_PGRAPH_CTX_CONTROL 0x00400144
++#define NV_PGRAPH_CTX_CONTROL_NV04 0x00400170
++#define NV_PGRAPH_ABS_UCLIP_XMIN 0x0040053C
++#define NV_PGRAPH_ABS_UCLIP_YMIN 0x00400540
++#define NV_PGRAPH_ABS_UCLIP_XMAX 0x00400544
++#define NV_PGRAPH_ABS_UCLIP_YMAX 0x00400548
++#define NV_PGRAPH_BETA_AND 0x00400608
++#define NV_PGRAPH_LIMIT_VIOL_PIX 0x00400610
++#define NV_PGRAPH_BOFFSET0 0x00400640
++#define NV_PGRAPH_BOFFSET1 0x00400644
++#define NV_PGRAPH_BOFFSET2 0x00400648
++#define NV_PGRAPH_BLIMIT0 0x00400684
++#define NV_PGRAPH_BLIMIT1 0x00400688
++#define NV_PGRAPH_BLIMIT2 0x0040068c
++#define NV_PGRAPH_STATUS 0x00400700
++#define NV_PGRAPH_SURFACE 0x00400710
++#define NV_PGRAPH_STATE 0x00400714
++#define NV_PGRAPH_FIFO 0x00400720
++#define NV_PGRAPH_PATTERN_SHAPE 0x00400810
++#define NV_PGRAPH_TILE 0x00400b00
++
++#define NV_PVIDEO_INTR_EN 0x00008140
++#define NV_PVIDEO_BUFFER 0x00008700
++#define NV_PVIDEO_STOP 0x00008704
++#define NV_PVIDEO_UVPLANE_BASE(buff) (0x00008800+(buff)*4)
++#define NV_PVIDEO_UVPLANE_LIMIT(buff) (0x00008808+(buff)*4)
++#define NV_PVIDEO_UVPLANE_OFFSET_BUFF(buff) (0x00008820+(buff)*4)
++#define NV_PVIDEO_BASE(buff) (0x00008900+(buff)*4)
++#define NV_PVIDEO_LIMIT(buff) (0x00008908+(buff)*4)
++#define NV_PVIDEO_LUMINANCE(buff) (0x00008910+(buff)*4)
++#define NV_PVIDEO_CHROMINANCE(buff) (0x00008918+(buff)*4)
++#define NV_PVIDEO_OFFSET_BUFF(buff) (0x00008920+(buff)*4)
++#define NV_PVIDEO_SIZE_IN(buff) (0x00008928+(buff)*4)
++#define NV_PVIDEO_POINT_IN(buff) (0x00008930+(buff)*4)
++#define NV_PVIDEO_DS_DX(buff) (0x00008938+(buff)*4)
++#define NV_PVIDEO_DT_DY(buff) (0x00008940+(buff)*4)
++#define NV_PVIDEO_POINT_OUT(buff) (0x00008948+(buff)*4)
++#define NV_PVIDEO_SIZE_OUT(buff) (0x00008950+(buff)*4)
++#define NV_PVIDEO_FORMAT(buff) (0x00008958+(buff)*4)
++# define NV_PVIDEO_FORMAT_PLANAR (1 << 0)
++# define NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8 (1 << 16)
++# define NV_PVIDEO_FORMAT_DISPLAY_COLOR_KEY (1 << 20)
++# define NV_PVIDEO_FORMAT_MATRIX_ITURBT709 (1 << 24)
++#define NV_PVIDEO_COLOR_KEY 0x00008B00
++
++/* NV04 overlay defines from VIDIX & Haiku */
++#define NV_PVIDEO_INTR_EN_0 0x00680140
++#define NV_PVIDEO_STEP_SIZE 0x00680200
++#define NV_PVIDEO_CONTROL_Y 0x00680204
++#define NV_PVIDEO_CONTROL_X 0x00680208
++#define NV_PVIDEO_BUFF0_START_ADDRESS 0x0068020c
++#define NV_PVIDEO_BUFF0_PITCH_LENGTH 0x00680214
++#define NV_PVIDEO_BUFF0_OFFSET 0x0068021c
++#define NV_PVIDEO_BUFF1_START_ADDRESS 0x00680210
++#define NV_PVIDEO_BUFF1_PITCH_LENGTH 0x00680218
++#define NV_PVIDEO_BUFF1_OFFSET 0x00680220
++#define NV_PVIDEO_OE_STATE 0x00680224
++#define NV_PVIDEO_SU_STATE 0x00680228
++#define NV_PVIDEO_RM_STATE 0x0068022c
++#define NV_PVIDEO_WINDOW_START 0x00680230
++#define NV_PVIDEO_WINDOW_SIZE 0x00680234
++#define NV_PVIDEO_FIFO_THRES_SIZE 0x00680238
++#define NV_PVIDEO_FIFO_BURST_LENGTH 0x0068023c
++#define NV_PVIDEO_KEY 0x00680240
++#define NV_PVIDEO_OVERLAY 0x00680244
++#define NV_PVIDEO_RED_CSC_OFFSET 0x00680280
++#define NV_PVIDEO_GREEN_CSC_OFFSET 0x00680284
++#define NV_PVIDEO_BLUE_CSC_OFFSET 0x00680288
++#define NV_PVIDEO_CSC_ADJUST 0x0068028c
++
++#endif
+diff --git a/drivers/gpu/drm/r128/r128_drv.c b/drivers/gpu/drm/r128/r128_drv.c
+index 601f4c0..b806fdc 100644
+--- a/drivers/gpu/drm/r128/r128_drv.c
++++ b/drivers/gpu/drm/r128/r128_drv.c
+@@ -64,7 +64,7 @@ static struct drm_driver driver = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+- .ioctl = drm_ioctl,
++ .unlocked_ioctl = drm_ioctl,
+ .mmap = drm_mmap,
+ .poll = drm_poll,
+ .fasync = drm_fasync,
+diff --git a/drivers/gpu/drm/r128/r128_ioc32.c b/drivers/gpu/drm/r128/r128_ioc32.c
+index d3cb676..51c99fc 100644
+--- a/drivers/gpu/drm/r128/r128_ioc32.c
++++ b/drivers/gpu/drm/r128/r128_ioc32.c
+@@ -95,8 +95,7 @@ static int compat_r128_init(struct file *file, unsigned int cmd,
+ &init->agp_textures_offset))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_R128_INIT, (unsigned long)init);
++ return drm_ioctl(file, DRM_IOCTL_R128_INIT, (unsigned long)init);
+ }
+
+ typedef struct drm_r128_depth32 {
+@@ -129,8 +128,7 @@ static int compat_r128_depth(struct file *file, unsigned int cmd,
+ &depth->mask))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_R128_DEPTH, (unsigned long)depth);
++ return drm_ioctl(file, DRM_IOCTL_R128_DEPTH, (unsigned long)depth);
+
+ }
+
+@@ -153,8 +151,7 @@ static int compat_r128_stipple(struct file *file, unsigned int cmd,
+ &stipple->mask))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_R128_STIPPLE, (unsigned long)stipple);
++ return drm_ioctl(file, DRM_IOCTL_R128_STIPPLE, (unsigned long)stipple);
+ }
+
+ typedef struct drm_r128_getparam32 {
+@@ -178,8 +175,7 @@ static int compat_r128_getparam(struct file *file, unsigned int cmd,
+ &getparam->value))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_R128_GETPARAM, (unsigned long)getparam);
++ return drm_ioctl(file, DRM_IOCTL_R128_GETPARAM, (unsigned long)getparam);
+ }
+
+ drm_ioctl_compat_t *r128_compat_ioctls[] = {
+@@ -210,12 +206,10 @@ long r128_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+ if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(r128_compat_ioctls))
+ fn = r128_compat_ioctls[nr - DRM_COMMAND_BASE];
+
+- lock_kernel(); /* XXX for now */
+ if (fn != NULL)
+ ret = (*fn) (filp, cmd, arg);
+ else
+- ret = drm_ioctl(filp->f_path.dentry->d_inode, filp, cmd, arg);
+- unlock_kernel();
++ ret = drm_ioctl(filp, cmd, arg);
+
+ return ret;
+ }
+diff --git a/drivers/gpu/drm/radeon/Kconfig b/drivers/gpu/drm/radeon/Kconfig
+index 5982321..1c02d23 100644
+--- a/drivers/gpu/drm/radeon/Kconfig
++++ b/drivers/gpu/drm/radeon/Kconfig
+@@ -1,10 +1,14 @@
+ config DRM_RADEON_KMS
+- bool "Enable modesetting on radeon by default"
++ bool "Enable modesetting on radeon by default - NEW DRIVER"
+ depends on DRM_RADEON
+ help
+- Choose this option if you want kernel modesetting enabled by default,
+- and you have a new enough userspace to support this. Running old
+- userspaces with this enabled will cause pain.
++ Choose this option if you want kernel modesetting enabled by default.
++
++ This is a completely new driver. It's only part of the existing drm
++ for compatibility reasons. It requires an entirely different graphics
++ stack above it and works very differently from the old drm stack.
++ i.e. don't enable this unless you know what you are doing it may
++ cause issues or bugs compared to the previous userspace driver stack.
+
+ When kernel modesetting is enabled the IOCTL of radeon/drm
+ driver are considered as invalid and an error message is printed
+diff --git a/drivers/gpu/drm/radeon/Makefile b/drivers/gpu/drm/radeon/Makefile
+index b5713ee..1cc7b93 100644
+--- a/drivers/gpu/drm/radeon/Makefile
++++ b/drivers/gpu/drm/radeon/Makefile
+@@ -24,6 +24,9 @@ $(obj)/rv515_reg_safe.h: $(src)/reg_srcs/rv515 $(obj)/mkregtable
+ $(obj)/r300_reg_safe.h: $(src)/reg_srcs/r300 $(obj)/mkregtable
+ $(call if_changed,mkregtable)
+
++$(obj)/r420_reg_safe.h: $(src)/reg_srcs/r420 $(obj)/mkregtable
++ $(call if_changed,mkregtable)
++
+ $(obj)/rs600_reg_safe.h: $(src)/reg_srcs/rs600 $(obj)/mkregtable
+ $(call if_changed,mkregtable)
+
+@@ -35,6 +38,8 @@ $(obj)/rv515.o: $(obj)/rv515_reg_safe.h
+
+ $(obj)/r300.o: $(obj)/r300_reg_safe.h
+
++$(obj)/r420.o: $(obj)/r420_reg_safe.h
++
+ $(obj)/rs600.o: $(obj)/rs600_reg_safe.h
+
+ radeon-y := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o \
+@@ -49,7 +54,7 @@ radeon-y += radeon_device.o radeon_kms.o \
+ radeon_cs.o radeon_bios.o radeon_benchmark.o r100.o r300.o r420.o \
+ rs400.o rs600.o rs690.o rv515.o r520.o r600.o rv770.o radeon_test.o \
+ r200.o radeon_legacy_tv.o r600_cs.o r600_blit.o r600_blit_shaders.o \
+- r600_blit_kms.o radeon_pm.o
++ r600_blit_kms.o radeon_pm.o atombios_dp.o r600_audio.o r600_hdmi.o
+
+ radeon-$(CONFIG_COMPAT) += radeon_ioc32.o
+
+diff --git a/drivers/gpu/drm/radeon/ObjectID.h b/drivers/gpu/drm/radeon/ObjectID.h
+index 6d0183c..c714179 100644
+--- a/drivers/gpu/drm/radeon/ObjectID.h
++++ b/drivers/gpu/drm/radeon/ObjectID.h
+@@ -1,5 +1,5 @@
+ /*
+-* Copyright 2006-2007 Advanced Micro Devices, Inc.
++* Copyright 2006-2007 Advanced Micro Devices, Inc.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+@@ -41,14 +41,14 @@
+ /****************************************************/
+ /* Encoder Object ID Definition */
+ /****************************************************/
+-#define ENCODER_OBJECT_ID_NONE 0x00
++#define ENCODER_OBJECT_ID_NONE 0x00
+
+ /* Radeon Class Display Hardware */
+ #define ENCODER_OBJECT_ID_INTERNAL_LVDS 0x01
+ #define ENCODER_OBJECT_ID_INTERNAL_TMDS1 0x02
+ #define ENCODER_OBJECT_ID_INTERNAL_TMDS2 0x03
+ #define ENCODER_OBJECT_ID_INTERNAL_DAC1 0x04
+-#define ENCODER_OBJECT_ID_INTERNAL_DAC2 0x05 /* TV/CV DAC */
++#define ENCODER_OBJECT_ID_INTERNAL_DAC2 0x05 /* TV/CV DAC */
+ #define ENCODER_OBJECT_ID_INTERNAL_SDVOA 0x06
+ #define ENCODER_OBJECT_ID_INTERNAL_SDVOB 0x07
+
+@@ -56,11 +56,11 @@
+ #define ENCODER_OBJECT_ID_SI170B 0x08
+ #define ENCODER_OBJECT_ID_CH7303 0x09
+ #define ENCODER_OBJECT_ID_CH7301 0x0A
+-#define ENCODER_OBJECT_ID_INTERNAL_DVO1 0x0B /* This belongs to Radeon Class Display Hardware */
++#define ENCODER_OBJECT_ID_INTERNAL_DVO1 0x0B /* This belongs to Radeon Class Display Hardware */
+ #define ENCODER_OBJECT_ID_EXTERNAL_SDVOA 0x0C
+ #define ENCODER_OBJECT_ID_EXTERNAL_SDVOB 0x0D
+ #define ENCODER_OBJECT_ID_TITFP513 0x0E
+-#define ENCODER_OBJECT_ID_INTERNAL_LVTM1 0x0F /* not used for Radeon */
++#define ENCODER_OBJECT_ID_INTERNAL_LVTM1 0x0F /* not used for Radeon */
+ #define ENCODER_OBJECT_ID_VT1623 0x10
+ #define ENCODER_OBJECT_ID_HDMI_SI1930 0x11
+ #define ENCODER_OBJECT_ID_HDMI_INTERNAL 0x12
+@@ -68,9 +68,9 @@
+ #define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 0x13
+ #define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1 0x14
+ #define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1 0x15
+-#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2 0x16 /* Shared with CV/TV and CRT */
+-#define ENCODER_OBJECT_ID_SI178 0X17 /* External TMDS (dual link, no HDCP.) */
+-#define ENCODER_OBJECT_ID_MVPU_FPGA 0x18 /* MVPU FPGA chip */
++#define ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2 0x16 /* Shared with CV/TV and CRT */
++#define ENCODER_OBJECT_ID_SI178 0X17 /* External TMDS (dual link, no HDCP.) */
++#define ENCODER_OBJECT_ID_MVPU_FPGA 0x18 /* MVPU FPGA chip */
+ #define ENCODER_OBJECT_ID_INTERNAL_DDI 0x19
+ #define ENCODER_OBJECT_ID_VT1625 0x1A
+ #define ENCODER_OBJECT_ID_HDMI_SI1932 0x1B
+@@ -86,7 +86,7 @@
+ /****************************************************/
+ /* Connector Object ID Definition */
+ /****************************************************/
+-#define CONNECTOR_OBJECT_ID_NONE 0x00
++#define CONNECTOR_OBJECT_ID_NONE 0x00
+ #define CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I 0x01
+ #define CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I 0x02
+ #define CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D 0x03
+@@ -96,7 +96,7 @@
+ #define CONNECTOR_OBJECT_ID_SVIDEO 0x07
+ #define CONNECTOR_OBJECT_ID_YPbPr 0x08
+ #define CONNECTOR_OBJECT_ID_D_CONNECTOR 0x09
+-#define CONNECTOR_OBJECT_ID_9PIN_DIN 0x0A /* Supports both CV & TV */
++#define CONNECTOR_OBJECT_ID_9PIN_DIN 0x0A /* Supports both CV & TV */
+ #define CONNECTOR_OBJECT_ID_SCART 0x0B
+ #define CONNECTOR_OBJECT_ID_HDMI_TYPE_A 0x0C
+ #define CONNECTOR_OBJECT_ID_HDMI_TYPE_B 0x0D
+@@ -106,6 +106,8 @@
+ #define CONNECTOR_OBJECT_ID_CROSSFIRE 0x11
+ #define CONNECTOR_OBJECT_ID_HARDCODE_DVI 0x12
+ #define CONNECTOR_OBJECT_ID_DISPLAYPORT 0x13
++#define CONNECTOR_OBJECT_ID_eDP 0x14
++#define CONNECTOR_OBJECT_ID_MXM 0x15
+
+ /* deleted */
+
+@@ -116,6 +118,14 @@
+ #define ROUTER_OBJECT_ID_I2C_EXTENDER_CNTL 0x01
+
+ /****************************************************/
++/* Generic Object ID Definition */
++/****************************************************/
++#define GENERIC_OBJECT_ID_NONE 0x00
++#define GENERIC_OBJECT_ID_GLSYNC 0x01
++#define GENERIC_OBJECT_ID_PX2_NON_DRIVABLE 0x02
++#define GENERIC_OBJECT_ID_MXM_OPM 0x03
++
++/****************************************************/
+ /* Graphics Object ENUM ID Definition */
+ /****************************************************/
+ #define GRAPH_OBJECT_ENUM_ID1 0x01
+@@ -124,6 +134,7 @@
+ #define GRAPH_OBJECT_ENUM_ID4 0x04
+ #define GRAPH_OBJECT_ENUM_ID5 0x05
+ #define GRAPH_OBJECT_ENUM_ID6 0x06
++#define GRAPH_OBJECT_ENUM_ID7 0x07
+
+ /****************************************************/
+ /* Graphics Object ID Bit definition */
+@@ -133,35 +144,35 @@
+ #define RESERVED1_ID_MASK 0x0800
+ #define OBJECT_TYPE_MASK 0x7000
+ #define RESERVED2_ID_MASK 0x8000
+-
++
+ #define OBJECT_ID_SHIFT 0x00
+ #define ENUM_ID_SHIFT 0x08
+ #define OBJECT_TYPE_SHIFT 0x0C
+
++
+ /****************************************************/
+ /* Graphics Object family definition */
+ /****************************************************/
+-#define CONSTRUCTOBJECTFAMILYID(GRAPHICS_OBJECT_TYPE, GRAPHICS_OBJECT_ID) \
+- (GRAPHICS_OBJECT_TYPE << OBJECT_TYPE_SHIFT | \
+- GRAPHICS_OBJECT_ID << OBJECT_ID_SHIFT)
++#define CONSTRUCTOBJECTFAMILYID(GRAPHICS_OBJECT_TYPE, GRAPHICS_OBJECT_ID) (GRAPHICS_OBJECT_TYPE << OBJECT_TYPE_SHIFT | \
++ GRAPHICS_OBJECT_ID << OBJECT_ID_SHIFT)
+ /****************************************************/
+ /* GPU Object ID definition - Shared with BIOS */
+ /****************************************************/
+-#define GPU_ENUM_ID1 (GRAPH_OBJECT_TYPE_GPU << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT)
++#define GPU_ENUM_ID1 ( GRAPH_OBJECT_TYPE_GPU << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT)
+
+ /****************************************************/
+ /* Encoder Object ID definition - Shared with BIOS */
+ /****************************************************/
+ /*
+-#define ENCODER_INTERNAL_LVDS_ENUM_ID1 0x2101
++#define ENCODER_INTERNAL_LVDS_ENUM_ID1 0x2101
+ #define ENCODER_INTERNAL_TMDS1_ENUM_ID1 0x2102
+ #define ENCODER_INTERNAL_TMDS2_ENUM_ID1 0x2103
+ #define ENCODER_INTERNAL_DAC1_ENUM_ID1 0x2104
+ #define ENCODER_INTERNAL_DAC2_ENUM_ID1 0x2105
+ #define ENCODER_INTERNAL_SDVOA_ENUM_ID1 0x2106
+ #define ENCODER_INTERNAL_SDVOB_ENUM_ID1 0x2107
+-#define ENCODER_SIL170B_ENUM_ID1 0x2108
++#define ENCODER_SIL170B_ENUM_ID1 0x2108
+ #define ENCODER_CH7303_ENUM_ID1 0x2109
+ #define ENCODER_CH7301_ENUM_ID1 0x210A
+ #define ENCODER_INTERNAL_DVO1_ENUM_ID1 0x210B
+@@ -175,8 +186,8 @@
+ #define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID1 0x2113
+ #define ENCODER_INTERNAL_KLDSCP_DVO1_ENUM_ID1 0x2114
+ #define ENCODER_INTERNAL_KLDSCP_DAC1_ENUM_ID1 0x2115
+-#define ENCODER_INTERNAL_KLDSCP_DAC2_ENUM_ID1 0x2116
+-#define ENCODER_SI178_ENUM_ID1 0x2117
++#define ENCODER_INTERNAL_KLDSCP_DAC2_ENUM_ID1 0x2116
++#define ENCODER_SI178_ENUM_ID1 0x2117
+ #define ENCODER_MVPU_FPGA_ENUM_ID1 0x2118
+ #define ENCODER_INTERNAL_DDI_ENUM_ID1 0x2119
+ #define ENCODER_VT1625_ENUM_ID1 0x211A
+@@ -185,205 +196,169 @@
+ #define ENCODER_DP_DP501_ENUM_ID1 0x211D
+ #define ENCODER_INTERNAL_UNIPHY_ENUM_ID1 0x211E
+ */
+-#define ENCODER_INTERNAL_LVDS_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_LVDS << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_TMDS1_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_TMDS1 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_TMDS2_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_TMDS2 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_DAC1_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_DAC1 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_DAC2_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_DAC2 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_SDVOA_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_SDVOA << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_SDVOA_ENUM_ID2 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_SDVOA << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_SDVOB_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_SDVOB << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_SIL170B_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_SI170B << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_CH7303_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_CH7303 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_CH7301_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_CH7301 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_DVO1_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_DVO1 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_EXTERNAL_SDVOA_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_EXTERNAL_SDVOA << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_EXTERNAL_SDVOA_ENUM_ID2 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_EXTERNAL_SDVOA << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_EXTERNAL_SDVOB_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_EXTERNAL_SDVOB << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_TITFP513_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_TITFP513 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_LVTM1_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_LVTM1 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_VT1623_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_VT1623 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_HDMI_SI1930_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_HDMI_SI1930 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_HDMI_INTERNAL_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_HDMI_INTERNAL << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID2 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_KLDSCP_DVO1_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_KLDSCP_DAC1_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_KLDSCP_DAC2_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2 << OBJECT_ID_SHIFT) /* Shared with CV/TV and CRT */
+-
+-#define ENCODER_SI178_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_SI178 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_MVPU_FPGA_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_MVPU_FPGA << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_DDI_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_DDI << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_VT1625_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_VT1625 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_HDMI_SI1932_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_HDMI_SI1932 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_DP_DP501_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_DP_DP501 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_DP_AN9801_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_DP_AN9801 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_UNIPHY_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_UNIPHY << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_UNIPHY_ENUM_ID2 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_UNIPHY << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_KLDSCP_LVTMA_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_UNIPHY1_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_UNIPHY1_ENUM_ID2 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_UNIPHY2_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_INTERNAL_UNIPHY2_ENUM_ID2 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 << OBJECT_ID_SHIFT)
+-
+-#define ENCODER_GENERAL_EXTERNAL_DVO_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO << OBJECT_ID_SHIFT)
++#define ENCODER_INTERNAL_LVDS_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_LVDS << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_TMDS1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_TMDS1 << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_TMDS2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_TMDS2 << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_DAC1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_DAC1 << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_DAC2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_DAC2 << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_SDVOA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_SDVOA << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_SDVOA_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_SDVOA << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_SDVOB_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_SDVOB << OBJECT_ID_SHIFT)
++
++#define ENCODER_SIL170B_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_SI170B << OBJECT_ID_SHIFT)
++
++#define ENCODER_CH7303_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_CH7303 << OBJECT_ID_SHIFT)
++
++#define ENCODER_CH7301_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_CH7301 << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_DVO1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_DVO1 << OBJECT_ID_SHIFT)
++
++#define ENCODER_EXTERNAL_SDVOA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_EXTERNAL_SDVOA << OBJECT_ID_SHIFT)
++
++#define ENCODER_EXTERNAL_SDVOA_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_EXTERNAL_SDVOA << OBJECT_ID_SHIFT)
++
++
++#define ENCODER_EXTERNAL_SDVOB_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_EXTERNAL_SDVOB << OBJECT_ID_SHIFT)
++
++
++#define ENCODER_TITFP513_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_TITFP513 << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_LVTM1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_LVTM1 << OBJECT_ID_SHIFT)
++
++#define ENCODER_VT1623_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_VT1623 << OBJECT_ID_SHIFT)
++
++#define ENCODER_HDMI_SI1930_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_HDMI_SI1930 << OBJECT_ID_SHIFT)
++
++#define ENCODER_HDMI_INTERNAL_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_HDMI_INTERNAL << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 << OBJECT_ID_SHIFT)
++
++
++#define ENCODER_INTERNAL_KLDSCP_TMDS1_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1 << OBJECT_ID_SHIFT)
++
++
++#define ENCODER_INTERNAL_KLDSCP_DVO1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1 << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_KLDSCP_DAC1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1 << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_KLDSCP_DAC2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC2 << OBJECT_ID_SHIFT) // Shared with CV/TV and CRT
++
++#define ENCODER_SI178_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_SI178 << OBJECT_ID_SHIFT)
++
++#define ENCODER_MVPU_FPGA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_MVPU_FPGA << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_DDI_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_DDI << OBJECT_ID_SHIFT)
++
++#define ENCODER_VT1625_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_VT1625 << OBJECT_ID_SHIFT)
++
++#define ENCODER_HDMI_SI1932_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_HDMI_SI1932 << OBJECT_ID_SHIFT)
++
++#define ENCODER_DP_DP501_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_DP_DP501 << OBJECT_ID_SHIFT)
++
++#define ENCODER_DP_AN9801_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_DP_AN9801 << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_UNIPHY_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_UNIPHY << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_UNIPHY_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_UNIPHY << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_KLDSCP_LVTMA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_UNIPHY1_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_UNIPHY1_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_UNIPHY1 << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_UNIPHY2_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 << OBJECT_ID_SHIFT)
++
++#define ENCODER_INTERNAL_UNIPHY2_ENUM_ID2 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_INTERNAL_UNIPHY2 << OBJECT_ID_SHIFT)
++
++#define ENCODER_GENERAL_EXTERNAL_DVO_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ENCODER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ENCODER_OBJECT_ID_GENERAL_EXTERNAL_DVO << OBJECT_ID_SHIFT)
+
+ /****************************************************/
+ /* Connector Object ID definition - Shared with BIOS */
+@@ -406,167 +381,253 @@
+ #define CONNECTOR_7PIN_DIN_ENUM_ID1 0x310F
+ #define CONNECTOR_PCIE_CONNECTOR_ENUM_ID1 0x3110
+ */
+-#define CONNECTOR_LVDS_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_LVDS << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID2 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID2 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID2 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_VGA_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_VGA << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_VGA_ENUM_ID2 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_VGA << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_COMPOSITE_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_COMPOSITE << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_SVIDEO_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_SVIDEO << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_YPbPr_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_YPbPr << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_D_CONNECTOR_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_D_CONNECTOR << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_9PIN_DIN_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_9PIN_DIN << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_SCART_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_SCART << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_HDMI_TYPE_A_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_HDMI_TYPE_B_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_HDMI_TYPE_B << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_7PIN_DIN_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_7PIN_DIN << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_PCIE_CONNECTOR << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID2 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_PCIE_CONNECTOR << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_CROSSFIRE_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_CROSSFIRE << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_CROSSFIRE_ENUM_ID2 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_CROSSFIRE << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_HARDCODE_DVI_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_HARDCODE_DVI << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_HARDCODE_DVI_ENUM_ID2 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_HARDCODE_DVI << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_DISPLAYPORT_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_DISPLAYPORT_ENUM_ID2 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_DISPLAYPORT_ENUM_ID3 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
+-
+-#define CONNECTOR_DISPLAYPORT_ENUM_ID4 \
+- (GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\
+- CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
++#define CONNECTOR_LVDS_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_LVDS << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_LVDS_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_LVDS << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_eDP_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_eDP << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_eDP_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_eDP << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_SINGLE_LINK_DVI_I_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_DUAL_LINK_DVI_I_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_SINGLE_LINK_DVI_D_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_DUAL_LINK_DVI_D_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_VGA_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_VGA << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_VGA_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_VGA << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_COMPOSITE_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_COMPOSITE << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_COMPOSITE_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_COMPOSITE << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_SVIDEO_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_SVIDEO << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_SVIDEO_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_SVIDEO << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_YPbPr_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_YPbPr << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_YPbPr_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_YPbPr << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_D_CONNECTOR_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_D_CONNECTOR << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_D_CONNECTOR_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_D_CONNECTOR << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_9PIN_DIN_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_9PIN_DIN << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_9PIN_DIN_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_9PIN_DIN << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_SCART_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_SCART << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_SCART_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_SCART << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_HDMI_TYPE_A_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_HDMI_TYPE_A_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_HDMI_TYPE_A_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_HDMI_TYPE_A << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_HDMI_TYPE_B_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_HDMI_TYPE_B << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_HDMI_TYPE_B_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_HDMI_TYPE_B << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_7PIN_DIN_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_7PIN_DIN << OBJECT_ID_SHIFT)
++#define CONNECTOR_7PIN_DIN_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_7PIN_DIN << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_PCIE_CONNECTOR << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_PCIE_CONNECTOR_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_PCIE_CONNECTOR << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_CROSSFIRE_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_CROSSFIRE << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_CROSSFIRE_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_CROSSFIRE << OBJECT_ID_SHIFT)
++
++
++#define CONNECTOR_HARDCODE_DVI_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_HARDCODE_DVI << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_HARDCODE_DVI_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_HARDCODE_DVI << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_DISPLAYPORT_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_DISPLAYPORT_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_DISPLAYPORT_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_DISPLAYPORT_ENUM_ID4 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_DISPLAYPORT_ENUM_ID5 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID5 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_DISPLAYPORT_ENUM_ID6 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID6 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_DISPLAYPORT << OBJECT_ID_SHIFT)
++
++#define CONNECTOR_MXM_ENUM_ID1 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DP_A
++
++#define CONNECTOR_MXM_ENUM_ID2 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DP_B
++
++#define CONNECTOR_MXM_ENUM_ID3 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID3 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DP_C
++
++#define CONNECTOR_MXM_ENUM_ID4 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID4 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DP_D
++
++#define CONNECTOR_MXM_ENUM_ID5 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID5 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_LVDS_TXxx
++
++#define CONNECTOR_MXM_ENUM_ID6 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID6 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_LVDS_UXxx
++
++#define CONNECTOR_MXM_ENUM_ID7 ( GRAPH_OBJECT_TYPE_CONNECTOR << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID7 << ENUM_ID_SHIFT |\
++ CONNECTOR_OBJECT_ID_MXM << OBJECT_ID_SHIFT) //Mapping to MXM_DAC
+
+ /****************************************************/
+ /* Router Object ID definition - Shared with BIOS */
+ /****************************************************/
+-#define ROUTER_I2C_EXTENDER_CNTL_ENUM_ID1 \
+- (GRAPH_OBJECT_TYPE_ROUTER << OBJECT_TYPE_SHIFT |\
+- GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
+- ROUTER_OBJECT_ID_I2C_EXTENDER_CNTL << OBJECT_ID_SHIFT)
++#define ROUTER_I2C_EXTENDER_CNTL_ENUM_ID1 ( GRAPH_OBJECT_TYPE_ROUTER << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ ROUTER_OBJECT_ID_I2C_EXTENDER_CNTL << OBJECT_ID_SHIFT)
+
+ /* deleted */
+
+ /****************************************************/
++/* Generic Object ID definition - Shared with BIOS */
++/****************************************************/
++#define GENERICOBJECT_GLSYNC_ENUM_ID1 (GRAPH_OBJECT_TYPE_GENERIC << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ GENERIC_OBJECT_ID_GLSYNC << OBJECT_ID_SHIFT)
++
++#define GENERICOBJECT_PX2_NON_DRIVABLE_ID1 (GRAPH_OBJECT_TYPE_GENERIC << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ GENERIC_OBJECT_ID_PX2_NON_DRIVABLE<< OBJECT_ID_SHIFT)
++
++#define GENERICOBJECT_PX2_NON_DRIVABLE_ID2 (GRAPH_OBJECT_TYPE_GENERIC << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID2 << ENUM_ID_SHIFT |\
++ GENERIC_OBJECT_ID_PX2_NON_DRIVABLE<< OBJECT_ID_SHIFT)
++
++#define GENERICOBJECT_MXM_OPM_ENUM_ID1 (GRAPH_OBJECT_TYPE_GENERIC << OBJECT_TYPE_SHIFT |\
++ GRAPH_OBJECT_ENUM_ID1 << ENUM_ID_SHIFT |\
++ GENERIC_OBJECT_ID_MXM_OPM << OBJECT_ID_SHIFT)
++
++/****************************************************/
+ /* Object Cap definition - Shared with BIOS */
+ /****************************************************/
+ #define GRAPHICS_OBJECT_CAP_I2C 0x00000001L
+ #define GRAPHICS_OBJECT_CAP_TABLE_ID 0x00000002L
+
++
+ #define GRAPHICS_OBJECT_I2CCOMMAND_TABLE_ID 0x01
+ #define GRAPHICS_OBJECT_HOTPLUGDETECTIONINTERUPT_TABLE_ID 0x02
+ #define GRAPHICS_OBJECT_ENCODER_OUTPUT_PROTECTION_TABLE_ID 0x03
+@@ -575,4 +636,8 @@
+ #pragma pack()
+ #endif
+
+-#endif /*GRAPHICTYPE */
++#endif /*GRAPHICTYPE */
++
++
++
++
+diff --git a/drivers/gpu/drm/radeon/atom.c b/drivers/gpu/drm/radeon/atom.c
+index d67c425..592ce91 100644
+--- a/drivers/gpu/drm/radeon/atom.c
++++ b/drivers/gpu/drm/radeon/atom.c
+@@ -24,6 +24,7 @@
+
+ #include <linux/module.h>
+ #include <linux/sched.h>
++#include <asm/unaligned.h>
+
+ #define ATOM_DEBUG
+
+@@ -58,6 +59,7 @@ typedef struct {
+ } atom_exec_context;
+
+ int atom_debug = 0;
++static void atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params);
+ void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params);
+
+ static uint32_t atom_arg_mask[8] =
+@@ -211,7 +213,9 @@ static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr,
+ case ATOM_ARG_PS:
+ idx = U8(*ptr);
+ (*ptr)++;
+- val = le32_to_cpu(ctx->ps[idx]);
++ /* get_unaligned_le32 avoids unaligned accesses from atombios
++ * tables, noticed on a DEC Alpha. */
++ val = get_unaligned_le32((u32 *)&ctx->ps[idx]);
+ if (print)
+ DEBUG("PS[0x%02X,0x%04X]", idx, val);
+ break;
+@@ -245,6 +249,9 @@ static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr,
+ case ATOM_WS_ATTRIBUTES:
+ val = gctx->io_attr;
+ break;
++ case ATOM_WS_REGPTR:
++ val = gctx->reg_block;
++ break;
+ default:
+ val = ctx->ws[idx];
+ }
+@@ -263,10 +270,10 @@ static uint32_t atom_get_src_int(atom_exec_context *ctx, uint8_t attr,
+ case ATOM_ARG_FB:
+ idx = U8(*ptr);
+ (*ptr)++;
++ val = gctx->scratch[((gctx->fb_base + idx) / 4)];
+ if (print)
+ DEBUG("FB[0x%02X]", idx);
+- printk(KERN_INFO "FB access is not implemented.\n");
+- return 0;
++ break;
+ case ATOM_ARG_IMM:
+ switch (align) {
+ case ATOM_SRC_DWORD:
+@@ -384,6 +391,32 @@ static uint32_t atom_get_src(atom_exec_context *ctx, uint8_t attr, int *ptr)
+ return atom_get_src_int(ctx, attr, ptr, NULL, 1);
+ }
+
++static uint32_t atom_get_src_direct(atom_exec_context *ctx, uint8_t align, int *ptr)
++{
++ uint32_t val = 0xCDCDCDCD;
++
++ switch (align) {
++ case ATOM_SRC_DWORD:
++ val = U32(*ptr);
++ (*ptr) += 4;
++ break;
++ case ATOM_SRC_WORD0:
++ case ATOM_SRC_WORD8:
++ case ATOM_SRC_WORD16:
++ val = U16(*ptr);
++ (*ptr) += 2;
++ break;
++ case ATOM_SRC_BYTE0:
++ case ATOM_SRC_BYTE8:
++ case ATOM_SRC_BYTE16:
++ case ATOM_SRC_BYTE24:
++ val = U8(*ptr);
++ (*ptr)++;
++ break;
++ }
++ return val;
++}
++
+ static uint32_t atom_get_dst(atom_exec_context *ctx, int arg, uint8_t attr,
+ int *ptr, uint32_t *saved, int print)
+ {
+@@ -481,6 +514,9 @@ static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr,
+ case ATOM_WS_ATTRIBUTES:
+ gctx->io_attr = val;
+ break;
++ case ATOM_WS_REGPTR:
++ gctx->reg_block = val;
++ break;
+ default:
+ ctx->ws[idx] = val;
+ }
+@@ -488,9 +524,9 @@ static void atom_put_dst(atom_exec_context *ctx, int arg, uint8_t attr,
+ case ATOM_ARG_FB:
+ idx = U8(*ptr);
+ (*ptr)++;
++ gctx->scratch[((gctx->fb_base + idx) / 4)] = val;
+ DEBUG("FB[0x%02X]", idx);
+- printk(KERN_INFO "FB access is not implemented.\n");
+- return;
++ break;
+ case ATOM_ARG_PLL:
+ idx = U8(*ptr);
+ (*ptr)++;
+@@ -573,7 +609,7 @@ static void atom_op_calltable(atom_exec_context *ctx, int *ptr, int arg)
+ else
+ SDEBUG(" table: %d\n", idx);
+ if (U16(ctx->ctx->cmd_table + 4 + 2 * idx))
+- atom_execute_table(ctx->ctx, idx, ctx->ps + ctx->ps_shift);
++ atom_execute_table_locked(ctx->ctx, idx, ctx->ps + ctx->ps_shift);
+ }
+
+ static void atom_op_clear(atom_exec_context *ctx, int *ptr, int arg)
+@@ -607,7 +643,8 @@ static void atom_op_delay(atom_exec_context *ctx, int *ptr, int arg)
+ uint8_t count = U8((*ptr)++);
+ SDEBUG(" count: %d\n", count);
+ if (arg == ATOM_UNIT_MICROSEC)
+- schedule_timeout_uninterruptible(usecs_to_jiffies(count));
++ udelay(count);
++ // schedule_timeout_uninterruptible(usecs_to_jiffies(count));
+ else
+ schedule_timeout_uninterruptible(msecs_to_jiffies(count));
+ }
+@@ -676,7 +713,7 @@ static void atom_op_mask(atom_exec_context *ctx, int *ptr, int arg)
+ SDEBUG(" dst: ");
+ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
+ SDEBUG(" src1: ");
+- src1 = atom_get_src(ctx, attr, ptr);
++ src1 = atom_get_src_direct(ctx, ((attr >> 3) & 7), ptr);
+ SDEBUG(" src2: ");
+ src2 = atom_get_src(ctx, attr, ptr);
+ dst &= src1;
+@@ -808,6 +845,38 @@ static void atom_op_setregblock(atom_exec_context *ctx, int *ptr, int arg)
+ SDEBUG(" base: 0x%04X\n", ctx->ctx->reg_block);
+ }
+
++static void atom_op_shift_left(atom_exec_context *ctx, int *ptr, int arg)
++{
++ uint8_t attr = U8((*ptr)++), shift;
++ uint32_t saved, dst;
++ int dptr = *ptr;
++ attr &= 0x38;
++ attr |= atom_def_dst[attr >> 3] << 6;
++ SDEBUG(" dst: ");
++ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
++ shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr);
++ SDEBUG(" shift: %d\n", shift);
++ dst <<= shift;
++ SDEBUG(" dst: ");
++ atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
++}
++
++static void atom_op_shift_right(atom_exec_context *ctx, int *ptr, int arg)
++{
++ uint8_t attr = U8((*ptr)++), shift;
++ uint32_t saved, dst;
++ int dptr = *ptr;
++ attr &= 0x38;
++ attr |= atom_def_dst[attr >> 3] << 6;
++ SDEBUG(" dst: ");
++ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
++ shift = atom_get_src_direct(ctx, ATOM_SRC_BYTE0, ptr);
++ SDEBUG(" shift: %d\n", shift);
++ dst >>= shift;
++ SDEBUG(" dst: ");
++ atom_put_dst(ctx, arg, attr, &dptr, dst, saved);
++}
++
+ static void atom_op_shl(atom_exec_context *ctx, int *ptr, int arg)
+ {
+ uint8_t attr = U8((*ptr)++), shift;
+@@ -817,7 +886,7 @@ static void atom_op_shl(atom_exec_context *ctx, int *ptr, int arg)
+ attr |= atom_def_dst[attr >> 3] << 6;
+ SDEBUG(" dst: ");
+ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
+- shift = U8((*ptr)++);
++ shift = atom_get_src(ctx, attr, ptr);
+ SDEBUG(" shift: %d\n", shift);
+ dst <<= shift;
+ SDEBUG(" dst: ");
+@@ -833,7 +902,7 @@ static void atom_op_shr(atom_exec_context *ctx, int *ptr, int arg)
+ attr |= atom_def_dst[attr >> 3] << 6;
+ SDEBUG(" dst: ");
+ dst = atom_get_dst(ctx, arg, attr, ptr, &saved, 1);
+- shift = U8((*ptr)++);
++ shift = atom_get_src(ctx, attr, ptr);
+ SDEBUG(" shift: %d\n", shift);
+ dst >>= shift;
+ SDEBUG(" dst: ");
+@@ -936,18 +1005,18 @@ static struct {
+ atom_op_or, ATOM_ARG_FB}, {
+ atom_op_or, ATOM_ARG_PLL}, {
+ atom_op_or, ATOM_ARG_MC}, {
+- atom_op_shl, ATOM_ARG_REG}, {
+- atom_op_shl, ATOM_ARG_PS}, {
+- atom_op_shl, ATOM_ARG_WS}, {
+- atom_op_shl, ATOM_ARG_FB}, {
+- atom_op_shl, ATOM_ARG_PLL}, {
+- atom_op_shl, ATOM_ARG_MC}, {
+- atom_op_shr, ATOM_ARG_REG}, {
+- atom_op_shr, ATOM_ARG_PS}, {
+- atom_op_shr, ATOM_ARG_WS}, {
+- atom_op_shr, ATOM_ARG_FB}, {
+- atom_op_shr, ATOM_ARG_PLL}, {
+- atom_op_shr, ATOM_ARG_MC}, {
++ atom_op_shift_left, ATOM_ARG_REG}, {
++ atom_op_shift_left, ATOM_ARG_PS}, {
++ atom_op_shift_left, ATOM_ARG_WS}, {
++ atom_op_shift_left, ATOM_ARG_FB}, {
++ atom_op_shift_left, ATOM_ARG_PLL}, {
++ atom_op_shift_left, ATOM_ARG_MC}, {
++ atom_op_shift_right, ATOM_ARG_REG}, {
++ atom_op_shift_right, ATOM_ARG_PS}, {
++ atom_op_shift_right, ATOM_ARG_WS}, {
++ atom_op_shift_right, ATOM_ARG_FB}, {
++ atom_op_shift_right, ATOM_ARG_PLL}, {
++ atom_op_shift_right, ATOM_ARG_MC}, {
+ atom_op_mul, ATOM_ARG_REG}, {
+ atom_op_mul, ATOM_ARG_PS}, {
+ atom_op_mul, ATOM_ARG_WS}, {
+@@ -1040,7 +1109,7 @@ static struct {
+ atom_op_shr, ATOM_ARG_MC}, {
+ atom_op_debug, 0},};
+
+-void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
++static void atom_execute_table_locked(struct atom_context *ctx, int index, uint32_t * params)
+ {
+ int base = CU16(ctx->cmd_table + 4 + 2 * index);
+ int len, ws, ps, ptr;
+@@ -1057,8 +1126,6 @@ void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
+
+ SDEBUG(">> execute %04X (len %d, WS %d, PS %d)\n", base, len, ws, ps);
+
+- /* reset reg block */
+- ctx->reg_block = 0;
+ ectx.ctx = ctx;
+ ectx.ps_shift = ps / 4;
+ ectx.start = base;
+@@ -1092,6 +1159,19 @@ void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
+ kfree(ectx.ws);
+ }
+
++void atom_execute_table(struct atom_context *ctx, int index, uint32_t * params)
++{
++ mutex_lock(&ctx->mutex);
++ /* reset reg block */
++ ctx->reg_block = 0;
++ /* reset fb window */
++ ctx->fb_base = 0;
++ /* reset io mode */
++ ctx->io_mode = ATOM_IO_MM;
++ atom_execute_table_locked(ctx, index, params);
++ mutex_unlock(&ctx->mutex);
++}
++
+ static int atom_iio_len[] = { 1, 2, 3, 3, 3, 3, 4, 4, 4, 3 };
+
+ static void atom_index_iio(struct atom_context *ctx, int base)
+@@ -1214,3 +1294,28 @@ void atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t * frev,
+ *crev = CU8(idx + 3);
+ return;
+ }
++
++int atom_allocate_fb_scratch(struct atom_context *ctx)
++{
++ int index = GetIndexIntoMasterTable(DATA, VRAM_UsageByFirmware);
++ uint16_t data_offset;
++ int usage_bytes;
++ struct _ATOM_VRAM_USAGE_BY_FIRMWARE *firmware_usage;
++
++ atom_parse_data_header(ctx, index, NULL, NULL, NULL, &data_offset);
++
++ firmware_usage = (struct _ATOM_VRAM_USAGE_BY_FIRMWARE *)(ctx->bios + data_offset);
++
++ DRM_DEBUG("atom firmware requested %08x %dkb\n",
++ firmware_usage->asFirmwareVramReserveInfo[0].ulStartAddrUsedByFirmware,
++ firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb);
++
++ usage_bytes = firmware_usage->asFirmwareVramReserveInfo[0].usFirmwareUseInKb * 1024;
++ if (usage_bytes == 0)
++ usage_bytes = 20 * 1024;
++ /* allocate some scratch memory */
++ ctx->scratch = kzalloc(usage_bytes, GFP_KERNEL);
++ if (!ctx->scratch)
++ return -ENOMEM;
++ return 0;
++}
+diff --git a/drivers/gpu/drm/radeon/atom.h b/drivers/gpu/drm/radeon/atom.h
+index e6eb38f..bc73781 100644
+--- a/drivers/gpu/drm/radeon/atom.h
++++ b/drivers/gpu/drm/radeon/atom.h
+@@ -91,6 +91,7 @@
+ #define ATOM_WS_AND_MASK 0x45
+ #define ATOM_WS_FB_WINDOW 0x46
+ #define ATOM_WS_ATTRIBUTES 0x47
++#define ATOM_WS_REGPTR 0x48
+
+ #define ATOM_IIO_NOP 0
+ #define ATOM_IIO_START 1
+@@ -120,6 +121,7 @@ struct card_info {
+
+ struct atom_context {
+ struct card_info *card;
++ struct mutex mutex;
+ void *bios;
+ uint32_t cmd_table, data_table;
+ uint16_t *iio;
+@@ -132,6 +134,7 @@ struct atom_context {
+ uint8_t shift;
+ int cs_equal, cs_above;
+ int io_mode;
++ uint32_t *scratch;
+ };
+
+ extern int atom_debug;
+@@ -142,6 +145,7 @@ int atom_asic_init(struct atom_context *);
+ void atom_destroy(struct atom_context *);
+ void atom_parse_data_header(struct atom_context *ctx, int index, uint16_t *size, uint8_t *frev, uint8_t *crev, uint16_t *data_start);
+ void atom_parse_cmd_header(struct atom_context *ctx, int index, uint8_t *frev, uint8_t *crev);
++int atom_allocate_fb_scratch(struct atom_context *ctx);
+ #include "atom-types.h"
+ #include "atombios.h"
+ #include "ObjectID.h"
+diff --git a/drivers/gpu/drm/radeon/atombios.h b/drivers/gpu/drm/radeon/atombios.h
+index c11dddd..91ad0d1 100644
+--- a/drivers/gpu/drm/radeon/atombios.h
++++ b/drivers/gpu/drm/radeon/atombios.h
+@@ -1141,7 +1141,7 @@ typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS {
+ /* ucTableFormatRevision=1,ucTableContentRevision=2 */
+ typedef struct _LVDS_ENCODER_CONTROL_PARAMETERS_V2 {
+ USHORT usPixelClock; /* in 10KHz; for bios convenient */
+- UCHAR ucMisc; /* see PANEL_ENCODER_MISC_xx defintions below */
++ UCHAR ucMisc; /* see PANEL_ENCODER_MISC_xx definitions below */
+ UCHAR ucAction; /* 0: turn off encoder */
+ /* 1: setup and turn on encoder */
+ UCHAR ucTruncate; /* bit0=0: Disable truncate */
+@@ -1424,7 +1424,7 @@ typedef struct _ATOM_MULTIMEDIA_CONFIG_INFO {
+ /* Structures used in FirmwareInfoTable */
+ /****************************************************************************/
+
+-/* usBIOSCapability Defintion: */
++/* usBIOSCapability Definition: */
+ /* Bit 0 = 0: Bios image is not Posted, =1:Bios image is Posted; */
+ /* Bit 1 = 0: Dual CRTC is not supported, =1: Dual CRTC is supported; */
+ /* Bit 2 = 0: Extended Desktop is not supported, =1: Extended Desktop is supported; */
+@@ -2386,7 +2386,7 @@ typedef struct _ATOM_ANALOG_TV_INFO_V1_2 {
+ } ATOM_ANALOG_TV_INFO_V1_2;
+
+ /**************************************************************************/
+-/* VRAM usage and their defintions */
++/* VRAM usage and their definitions */
+
+ /* One chunk of VRAM used by Bios are for HWICON surfaces,EDID data. */
+ /* Current Mode timing and Dail Timing and/or STD timing data EACH device. They can be broken down as below. */
+@@ -2680,7 +2680,7 @@ typedef struct _ATOM_I2C_RECORD {
+ typedef struct _ATOM_HPD_INT_RECORD {
+ ATOM_COMMON_RECORD_HEADER sheader;
+ UCHAR ucHPDIntGPIOID; /* Corresponding block in GPIO_PIN_INFO table gives the pin info */
+- UCHAR ucPluggged_PinState;
++ UCHAR ucPlugged_PinState;
+ } ATOM_HPD_INT_RECORD;
+
+ typedef struct _ATOM_OUTPUT_PROTECTION_RECORD {
+@@ -3046,7 +3046,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
+ #define ATOM_S0_SYSTEM_POWER_STATE_VALUE_DC 2
+ #define ATOM_S0_SYSTEM_POWER_STATE_VALUE_LITEAC 3
+
+-/* Byte aligned defintion for BIOS usage */
++/* Byte aligned definition for BIOS usage */
+ #define ATOM_S0_CRT1_MONOb0 0x01
+ #define ATOM_S0_CRT1_COLORb0 0x02
+ #define ATOM_S0_CRT1_MASKb0 (ATOM_S0_CRT1_MONOb0+ATOM_S0_CRT1_COLORb0)
+@@ -3131,7 +3131,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
+ #define ATOM_S2_DISPLAY_ROTATION_DEGREE_SHIFT 30
+ #define ATOM_S2_DISPLAY_ROTATION_ANGLE_MASK 0xC0000000L
+
+-/* Byte aligned defintion for BIOS usage */
++/* Byte aligned definition for BIOS usage */
+ #define ATOM_S2_TV1_STANDARD_MASKb0 0x0F
+ #define ATOM_S2_CURRENT_BL_LEVEL_MASKb1 0xFF
+ #define ATOM_S2_CRT1_DPMS_STATEb2 0x01
+@@ -3190,7 +3190,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
+ #define ATOM_S3_ALLOW_FAST_PWR_SWITCH 0x40000000L
+ #define ATOM_S3_RQST_GPU_USE_MIN_PWR 0x80000000L
+
+-/* Byte aligned defintion for BIOS usage */
++/* Byte aligned definition for BIOS usage */
+ #define ATOM_S3_CRT1_ACTIVEb0 0x01
+ #define ATOM_S3_LCD1_ACTIVEb0 0x02
+ #define ATOM_S3_TV1_ACTIVEb0 0x04
+@@ -3230,7 +3230,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
+ #define ATOM_S4_LCD1_REFRESH_MASK 0x0000FF00L
+ #define ATOM_S4_LCD1_REFRESH_SHIFT 8
+
+-/* Byte aligned defintion for BIOS usage */
++/* Byte aligned definition for BIOS usage */
+ #define ATOM_S4_LCD1_PANEL_ID_MASKb0 0x0FF
+ #define ATOM_S4_LCD1_REFRESH_MASKb1 ATOM_S4_LCD1_PANEL_ID_MASKb0
+ #define ATOM_S4_VRAM_INFO_MASKb2 ATOM_S4_LCD1_PANEL_ID_MASKb0
+@@ -3310,7 +3310,7 @@ typedef struct _ATOM_ASIC_INTERNAL_SS_INFO {
+ #define ATOM_S6_VRI_BRIGHTNESS_CHANGE 0x40000000L
+ #define ATOM_S6_CONFIG_DISPLAY_CHANGE_MASK 0x80000000L
+
+-/* Byte aligned defintion for BIOS usage */
++/* Byte aligned definition for BIOS usage */
+ #define ATOM_S6_DEVICE_CHANGEb0 0x01
+ #define ATOM_S6_SCALER_CHANGEb0 0x02
+ #define ATOM_S6_LID_CHANGEb0 0x04
+@@ -4690,6 +4690,205 @@ typedef struct _ATOM_POWERPLAY_INFO_V3 {
+ ATOM_POWERMODE_INFO_V3 asPowerPlayInfo[ATOM_MAX_NUMBEROF_POWER_BLOCK];
+ } ATOM_POWERPLAY_INFO_V3;
+
++/* New PPlib */
++/**************************************************************************/
++typedef struct _ATOM_PPLIB_THERMALCONTROLLER
++
++{
++ UCHAR ucType; // one of ATOM_PP_THERMALCONTROLLER_*
++ UCHAR ucI2cLine; // as interpreted by DAL I2C
++ UCHAR ucI2cAddress;
++ UCHAR ucFanParameters; // Fan Control Parameters.
++ UCHAR ucFanMinRPM; // Fan Minimum RPM (hundreds) -- for display purposes only.
++ UCHAR ucFanMaxRPM; // Fan Maximum RPM (hundreds) -- for display purposes only.
++ UCHAR ucReserved; // ----
++ UCHAR ucFlags; // to be defined
++} ATOM_PPLIB_THERMALCONTROLLER;
++
++#define ATOM_PP_FANPARAMETERS_TACHOMETER_PULSES_PER_REVOLUTION_MASK 0x0f
++#define ATOM_PP_FANPARAMETERS_NOFAN 0x80 // No fan is connected to this controller.
++
++#define ATOM_PP_THERMALCONTROLLER_NONE 0
++#define ATOM_PP_THERMALCONTROLLER_LM63 1 // Not used by PPLib
++#define ATOM_PP_THERMALCONTROLLER_ADM1032 2 // Not used by PPLib
++#define ATOM_PP_THERMALCONTROLLER_ADM1030 3 // Not used by PPLib
++#define ATOM_PP_THERMALCONTROLLER_MUA6649 4 // Not used by PPLib
++#define ATOM_PP_THERMALCONTROLLER_LM64 5
++#define ATOM_PP_THERMALCONTROLLER_F75375 6 // Not used by PPLib
++#define ATOM_PP_THERMALCONTROLLER_RV6xx 7
++#define ATOM_PP_THERMALCONTROLLER_RV770 8
++#define ATOM_PP_THERMALCONTROLLER_ADT7473 9
++
++typedef struct _ATOM_PPLIB_STATE
++{
++ UCHAR ucNonClockStateIndex;
++ UCHAR ucClockStateIndices[1]; // variable-sized
++} ATOM_PPLIB_STATE;
++
++//// ATOM_PPLIB_POWERPLAYTABLE::ulPlatformCaps
++#define ATOM_PP_PLATFORM_CAP_BACKBIAS 1
++#define ATOM_PP_PLATFORM_CAP_POWERPLAY 2
++#define ATOM_PP_PLATFORM_CAP_SBIOSPOWERSOURCE 4
++#define ATOM_PP_PLATFORM_CAP_ASPM_L0s 8
++#define ATOM_PP_PLATFORM_CAP_ASPM_L1 16
++#define ATOM_PP_PLATFORM_CAP_HARDWAREDC 32
++#define ATOM_PP_PLATFORM_CAP_GEMINIPRIMARY 64
++#define ATOM_PP_PLATFORM_CAP_STEPVDDC 128
++#define ATOM_PP_PLATFORM_CAP_VOLTAGECONTROL 256
++#define ATOM_PP_PLATFORM_CAP_SIDEPORTCONTROL 512
++#define ATOM_PP_PLATFORM_CAP_TURNOFFPLL_ASPML1 1024
++#define ATOM_PP_PLATFORM_CAP_HTLINKCONTROL 2048
++
++typedef struct _ATOM_PPLIB_POWERPLAYTABLE
++{
++ ATOM_COMMON_TABLE_HEADER sHeader;
++
++ UCHAR ucDataRevision;
++
++ UCHAR ucNumStates;
++ UCHAR ucStateEntrySize;
++ UCHAR ucClockInfoSize;
++ UCHAR ucNonClockSize;
++
++ // offset from start of this table to array of ucNumStates ATOM_PPLIB_STATE structures
++ USHORT usStateArrayOffset;
++
++ // offset from start of this table to array of ASIC-specific structures,
++ // currently ATOM_PPLIB_CLOCK_INFO.
++ USHORT usClockInfoArrayOffset;
++
++ // offset from start of this table to array of ATOM_PPLIB_NONCLOCK_INFO
++ USHORT usNonClockInfoArrayOffset;
++
++ USHORT usBackbiasTime; // in microseconds
++ USHORT usVoltageTime; // in microseconds
++ USHORT usTableSize; //the size of this structure, or the extended structure
++
++ ULONG ulPlatformCaps; // See ATOM_PPLIB_CAPS_*
++
++ ATOM_PPLIB_THERMALCONTROLLER sThermalController;
++
++ USHORT usBootClockInfoOffset;
++ USHORT usBootNonClockInfoOffset;
++
++} ATOM_PPLIB_POWERPLAYTABLE;
++
++//// ATOM_PPLIB_NONCLOCK_INFO::usClassification
++#define ATOM_PPLIB_CLASSIFICATION_UI_MASK 0x0007
++#define ATOM_PPLIB_CLASSIFICATION_UI_SHIFT 0
++#define ATOM_PPLIB_CLASSIFICATION_UI_NONE 0
++#define ATOM_PPLIB_CLASSIFICATION_UI_BATTERY 1
++#define ATOM_PPLIB_CLASSIFICATION_UI_BALANCED 3
++#define ATOM_PPLIB_CLASSIFICATION_UI_PERFORMANCE 5
++// 2, 4, 6, 7 are reserved
++
++#define ATOM_PPLIB_CLASSIFICATION_BOOT 0x0008
++#define ATOM_PPLIB_CLASSIFICATION_THERMAL 0x0010
++#define ATOM_PPLIB_CLASSIFICATION_LIMITEDPOWERSOURCE 0x0020
++#define ATOM_PPLIB_CLASSIFICATION_REST 0x0040
++#define ATOM_PPLIB_CLASSIFICATION_FORCED 0x0080
++#define ATOM_PPLIB_CLASSIFICATION_3DPERFORMANCE 0x0100
++#define ATOM_PPLIB_CLASSIFICATION_OVERDRIVETEMPLATE 0x0200
++#define ATOM_PPLIB_CLASSIFICATION_UVDSTATE 0x0400
++#define ATOM_PPLIB_CLASSIFICATION_3DLOW 0x0800
++#define ATOM_PPLIB_CLASSIFICATION_ACPI 0x1000
++// remaining 3 bits are reserved
++
++//// ATOM_PPLIB_NONCLOCK_INFO::ulCapsAndSettings
++#define ATOM_PPLIB_SINGLE_DISPLAY_ONLY 0x00000001
++#define ATOM_PPLIB_SUPPORTS_VIDEO_PLAYBACK 0x00000002
++
++// 0 is 2.5Gb/s, 1 is 5Gb/s
++#define ATOM_PPLIB_PCIE_LINK_SPEED_MASK 0x00000004
++#define ATOM_PPLIB_PCIE_LINK_SPEED_SHIFT 2
++
++// lanes - 1: 1, 2, 4, 8, 12, 16 permitted by PCIE spec
++#define ATOM_PPLIB_PCIE_LINK_WIDTH_MASK 0x000000F8
++#define ATOM_PPLIB_PCIE_LINK_WIDTH_SHIFT 3
++
++// lookup into reduced refresh-rate table
++#define ATOM_PPLIB_LIMITED_REFRESHRATE_VALUE_MASK 0x00000F00
++#define ATOM_PPLIB_LIMITED_REFRESHRATE_VALUE_SHIFT 8
++
++#define ATOM_PPLIB_LIMITED_REFRESHRATE_UNLIMITED 0
++#define ATOM_PPLIB_LIMITED_REFRESHRATE_50HZ 1
++// 2-15 TBD as needed.
++
++#define ATOM_PPLIB_SOFTWARE_DISABLE_LOADBALANCING 0x00001000
++#define ATOM_PPLIB_SOFTWARE_ENABLE_SLEEP_FOR_TIMESTAMPS 0x00002000
++#define ATOM_PPLIB_ENABLE_VARIBRIGHT 0x00008000
++
++#define ATOM_PPLIB_DISALLOW_ON_DC 0x00004000
++
++// Contained in an array starting at the offset
++// in ATOM_PPLIB_POWERPLAYTABLE::usNonClockInfoArrayOffset.
++// referenced from ATOM_PPLIB_STATE_INFO::ucNonClockStateIndex
++typedef struct _ATOM_PPLIB_NONCLOCK_INFO
++{
++ USHORT usClassification;
++ UCHAR ucMinTemperature;
++ UCHAR ucMaxTemperature;
++ ULONG ulCapsAndSettings;
++ UCHAR ucRequiredPower;
++ UCHAR ucUnused1[3];
++} ATOM_PPLIB_NONCLOCK_INFO;
++
++// Contained in an array starting at the offset
++// in ATOM_PPLIB_POWERPLAYTABLE::usClockInfoArrayOffset.
++// referenced from ATOM_PPLIB_STATE::ucClockStateIndices
++typedef struct _ATOM_PPLIB_R600_CLOCK_INFO
++{
++ USHORT usEngineClockLow;
++ UCHAR ucEngineClockHigh;
++
++ USHORT usMemoryClockLow;
++ UCHAR ucMemoryClockHigh;
++
++ USHORT usVDDC;
++ USHORT usUnused1;
++ USHORT usUnused2;
++
++ ULONG ulFlags; // ATOM_PPLIB_R600_FLAGS_*
++
++} ATOM_PPLIB_R600_CLOCK_INFO;
++
++// ulFlags in ATOM_PPLIB_R600_CLOCK_INFO
++#define ATOM_PPLIB_R600_FLAGS_PCIEGEN2 1
++#define ATOM_PPLIB_R600_FLAGS_UVDSAFE 2
++#define ATOM_PPLIB_R600_FLAGS_BACKBIASENABLE 4
++#define ATOM_PPLIB_R600_FLAGS_MEMORY_ODT_OFF 8
++#define ATOM_PPLIB_R600_FLAGS_MEMORY_DLL_OFF 16
++
++typedef struct _ATOM_PPLIB_RS780_CLOCK_INFO
++
++{
++ USHORT usLowEngineClockLow; // Low Engine clock in MHz (the same way as on the R600).
++ UCHAR ucLowEngineClockHigh;
++ USHORT usHighEngineClockLow; // High Engine clock in MHz.
++ UCHAR ucHighEngineClockHigh;
++ USHORT usMemoryClockLow; // For now one of the ATOM_PPLIB_RS780_SPMCLK_XXXX constants.
++ UCHAR ucMemoryClockHigh; // Currentyl unused.
++ UCHAR ucPadding; // For proper alignment and size.
++ USHORT usVDDC; // For the 780, use: None, Low, High, Variable
++ UCHAR ucMaxHTLinkWidth; // From SBIOS - {2, 4, 8, 16}
++ UCHAR ucMinHTLinkWidth; // From SBIOS - {2, 4, 8, 16}. Effective only if CDLW enabled. Minimum down stream width could be bigger as display BW requriement.
++ USHORT usHTLinkFreq; // See definition ATOM_PPLIB_RS780_HTLINKFREQ_xxx or in MHz(>=200).
++ ULONG ulFlags;
++} ATOM_PPLIB_RS780_CLOCK_INFO;
++
++#define ATOM_PPLIB_RS780_VOLTAGE_NONE 0
++#define ATOM_PPLIB_RS780_VOLTAGE_LOW 1
++#define ATOM_PPLIB_RS780_VOLTAGE_HIGH 2
++#define ATOM_PPLIB_RS780_VOLTAGE_VARIABLE 3
++
++#define ATOM_PPLIB_RS780_SPMCLK_NONE 0 // We cannot change the side port memory clock, leave it as it is.
++#define ATOM_PPLIB_RS780_SPMCLK_LOW 1
++#define ATOM_PPLIB_RS780_SPMCLK_HIGH 2
++
++#define ATOM_PPLIB_RS780_HTLINKFREQ_NONE 0
++#define ATOM_PPLIB_RS780_HTLINKFREQ_LOW 1
++#define ATOM_PPLIB_RS780_HTLINKFREQ_HIGH 2
++
+ /**************************************************************************/
+
+ /* Following definitions are for compatiblity issue in different SW components. */
+diff --git a/drivers/gpu/drm/radeon/atombios_crtc.c b/drivers/gpu/drm/radeon/atombios_crtc.c
+index 19f93f2..af464e3 100644
+--- a/drivers/gpu/drm/radeon/atombios_crtc.c
++++ b/drivers/gpu/drm/radeon/atombios_crtc.c
+@@ -249,15 +249,13 @@ void atombios_crtc_dpms(struct drm_crtc *crtc, int mode)
+ if (ASIC_IS_DCE3(rdev))
+ atombios_enable_crtc_memreq(crtc, 1);
+ atombios_blank_crtc(crtc, 0);
+- if (rdev->family < CHIP_R600)
+- drm_vblank_post_modeset(dev, radeon_crtc->crtc_id);
++ drm_vblank_post_modeset(dev, radeon_crtc->crtc_id);
+ radeon_crtc_load_lut(crtc);
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+- if (rdev->family < CHIP_R600)
+- drm_vblank_pre_modeset(dev, radeon_crtc->crtc_id);
++ drm_vblank_pre_modeset(dev, radeon_crtc->crtc_id);
+ atombios_blank_crtc(crtc, 1);
+ if (ASIC_IS_DCE3(rdev))
+ atombios_enable_crtc_memreq(crtc, 0);
+@@ -309,7 +307,6 @@ atombios_set_crtc_dtd_timing(struct drm_crtc *crtc,
+ args.susModeMiscInfo.usAccess = cpu_to_le16(misc);
+ args.ucCRTC = radeon_crtc->crtc_id;
+
+- printk("executing set crtc dtd timing\n");
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+ }
+
+@@ -349,7 +346,6 @@ static void atombios_crtc_set_timing(struct drm_crtc *crtc,
+ args.susModeMiscInfo.usAccess = cpu_to_le16(misc);
+ args.ucCRTC = radeon_crtc->crtc_id;
+
+- printk("executing set crtc timing\n");
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+ }
+
+@@ -411,60 +407,57 @@ static void atombios_set_ss(struct drm_crtc *crtc, int enable)
+ }
+ }
+
+-void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
++union adjust_pixel_clock {
++ ADJUST_DISPLAY_PLL_PS_ALLOCATION v1;
++};
++
++static u32 atombios_adjust_pll(struct drm_crtc *crtc,
++ struct drm_display_mode *mode,
++ struct radeon_pll *pll)
+ {
+- struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct drm_encoder *encoder = NULL;
+ struct radeon_encoder *radeon_encoder = NULL;
+- uint8_t frev, crev;
+- int index;
+- SET_PIXEL_CLOCK_PS_ALLOCATION args;
+- PIXEL_CLOCK_PARAMETERS *spc1_ptr;
+- PIXEL_CLOCK_PARAMETERS_V2 *spc2_ptr;
+- PIXEL_CLOCK_PARAMETERS_V3 *spc3_ptr;
+- uint32_t pll_clock = mode->clock;
+- uint32_t adjusted_clock;
+- uint32_t ref_div = 0, fb_div = 0, frac_fb_div = 0, post_div = 0;
+- struct radeon_pll *pll;
+- int pll_flags = 0;
++ u32 adjusted_clock = mode->clock;
+
+- memset(&args, 0, sizeof(args));
++ /* reset the pll flags */
++ pll->flags = 0;
+
+ if (ASIC_IS_AVIVO(rdev)) {
+ if ((rdev->family == CHIP_RS600) ||
+ (rdev->family == CHIP_RS690) ||
+ (rdev->family == CHIP_RS740))
+- pll_flags |= (RADEON_PLL_USE_FRAC_FB_DIV |
+- RADEON_PLL_PREFER_CLOSEST_LOWER);
++ pll->flags |= (RADEON_PLL_USE_FRAC_FB_DIV |
++ RADEON_PLL_PREFER_CLOSEST_LOWER);
+
+ if (ASIC_IS_DCE32(rdev) && mode->clock > 200000) /* range limits??? */
+- pll_flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
++ pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
+ else
+- pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
++ pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
+ } else {
+- pll_flags |= RADEON_PLL_LEGACY;
++ pll->flags |= RADEON_PLL_LEGACY;
+
+ if (mode->clock > 200000) /* range limits??? */
+- pll_flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
++ pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
+ else
+- pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
++ pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
+
+ }
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ if (encoder->crtc == crtc) {
+- if (!ASIC_IS_AVIVO(rdev)) {
+- if (encoder->encoder_type !=
+- DRM_MODE_ENCODER_DAC)
+- pll_flags |= RADEON_PLL_NO_ODD_POST_DIV;
+- if (!ASIC_IS_AVIVO(rdev)
+- && (encoder->encoder_type ==
+- DRM_MODE_ENCODER_LVDS))
+- pll_flags |= RADEON_PLL_USE_REF_DIV;
+- }
+ radeon_encoder = to_radeon_encoder(encoder);
++ if (ASIC_IS_AVIVO(rdev)) {
++ /* DVO wants 2x pixel clock if the DVO chip is in 12 bit mode */
++ if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1)
++ adjusted_clock = mode->clock * 2;
++ } else {
++ if (encoder->encoder_type != DRM_MODE_ENCODER_DAC)
++ pll->flags |= RADEON_PLL_NO_ODD_POST_DIV;
++ if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS)
++ pll->flags |= RADEON_PLL_USE_REF_DIV;
++ }
+ break;
+ }
+ }
+@@ -474,36 +467,101 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
+ * special hw requirements.
+ */
+ if (ASIC_IS_DCE3(rdev)) {
+- ADJUST_DISPLAY_PLL_PS_ALLOCATION adjust_pll_args;
+-
+- if (!encoder)
+- return;
++ union adjust_pixel_clock args;
++ struct radeon_encoder_atom_dig *dig;
++ u8 frev, crev;
++ int index;
+
+- memset(&adjust_pll_args, 0, sizeof(adjust_pll_args));
+- adjust_pll_args.usPixelClock = cpu_to_le16(mode->clock / 10);
+- adjust_pll_args.ucTransmitterID = radeon_encoder->encoder_id;
+- adjust_pll_args.ucEncodeMode = atombios_get_encoder_mode(encoder);
++ if (!radeon_encoder->enc_priv)
++ return adjusted_clock;
++ dig = radeon_encoder->enc_priv;
+
+ index = GetIndexIntoMasterTable(COMMAND, AdjustDisplayPll);
+- atom_execute_table(rdev->mode_info.atom_context,
+- index, (uint32_t *)&adjust_pll_args);
+- adjusted_clock = le16_to_cpu(adjust_pll_args.usPixelClock) * 10;
+- } else {
+- /* DVO wants 2x pixel clock if the DVO chip is in 12 bit mode */
+- if (ASIC_IS_AVIVO(rdev) &&
+- (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1))
+- adjusted_clock = mode->clock * 2;
+- else
+- adjusted_clock = mode->clock;
++ atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev,
++ &crev);
++
++ memset(&args, 0, sizeof(args));
++
++ switch (frev) {
++ case 1:
++ switch (crev) {
++ case 1:
++ case 2:
++ args.v1.usPixelClock = cpu_to_le16(mode->clock / 10);
++ args.v1.ucTransmitterID = radeon_encoder->encoder_id;
++ args.v1.ucEncodeMode = atombios_get_encoder_mode(encoder);
++
++ atom_execute_table(rdev->mode_info.atom_context,
++ index, (uint32_t *)&args);
++ adjusted_clock = le16_to_cpu(args.v1.usPixelClock) * 10;
++ break;
++ default:
++ DRM_ERROR("Unknown table version %d %d\n", frev, crev);
++ return adjusted_clock;
++ }
++ break;
++ default:
++ DRM_ERROR("Unknown table version %d %d\n", frev, crev);
++ return adjusted_clock;
++ }
++ }
++ return adjusted_clock;
++}
++
++union set_pixel_clock {
++ SET_PIXEL_CLOCK_PS_ALLOCATION base;
++ PIXEL_CLOCK_PARAMETERS v1;
++ PIXEL_CLOCK_PARAMETERS_V2 v2;
++ PIXEL_CLOCK_PARAMETERS_V3 v3;
++};
++
++void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
++{
++ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
++ struct drm_device *dev = crtc->dev;
++ struct radeon_device *rdev = dev->dev_private;
++ struct drm_encoder *encoder = NULL;
++ struct radeon_encoder *radeon_encoder = NULL;
++ u8 frev, crev;
++ int index;
++ union set_pixel_clock args;
++ u32 pll_clock = mode->clock;
++ u32 ref_div = 0, fb_div = 0, frac_fb_div = 0, post_div = 0;
++ struct radeon_pll *pll;
++ u32 adjusted_clock;
++
++ memset(&args, 0, sizeof(args));
++
++ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
++ if (encoder->crtc == crtc) {
++ radeon_encoder = to_radeon_encoder(encoder);
++ break;
++ }
+ }
+
++ if (!radeon_encoder)
++ return;
++
+ if (radeon_crtc->crtc_id == 0)
+ pll = &rdev->clock.p1pll;
+ else
+ pll = &rdev->clock.p2pll;
+
+- radeon_compute_pll(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div,
+- &ref_div, &post_div, pll_flags);
++ /* adjust pixel clock as needed */
++ adjusted_clock = atombios_adjust_pll(crtc, mode, pll);
++
++ if (ASIC_IS_AVIVO(rdev)) {
++ if (radeon_new_pll)
++ radeon_compute_pll_avivo(pll, adjusted_clock, &pll_clock,
++ &fb_div, &frac_fb_div,
++ &ref_div, &post_div);
++ else
++ radeon_compute_pll(pll, adjusted_clock, &pll_clock,
++ &fb_div, &frac_fb_div,
++ &ref_div, &post_div);
++ } else
++ radeon_compute_pll(pll, adjusted_clock, &pll_clock, &fb_div, &frac_fb_div,
++ &ref_div, &post_div);
+
+ index = GetIndexIntoMasterTable(COMMAND, SetPixelClock);
+ atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev,
+@@ -513,45 +571,38 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
+ case 1:
+ switch (crev) {
+ case 1:
+- spc1_ptr = (PIXEL_CLOCK_PARAMETERS *) & args.sPCLKInput;
+- spc1_ptr->usPixelClock = cpu_to_le16(mode->clock / 10);
+- spc1_ptr->usRefDiv = cpu_to_le16(ref_div);
+- spc1_ptr->usFbDiv = cpu_to_le16(fb_div);
+- spc1_ptr->ucFracFbDiv = frac_fb_div;
+- spc1_ptr->ucPostDiv = post_div;
+- spc1_ptr->ucPpll =
++ args.v1.usPixelClock = cpu_to_le16(mode->clock / 10);
++ args.v1.usRefDiv = cpu_to_le16(ref_div);
++ args.v1.usFbDiv = cpu_to_le16(fb_div);
++ args.v1.ucFracFbDiv = frac_fb_div;
++ args.v1.ucPostDiv = post_div;
++ args.v1.ucPpll =
+ radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1;
+- spc1_ptr->ucCRTC = radeon_crtc->crtc_id;
+- spc1_ptr->ucRefDivSrc = 1;
++ args.v1.ucCRTC = radeon_crtc->crtc_id;
++ args.v1.ucRefDivSrc = 1;
+ break;
+ case 2:
+- spc2_ptr =
+- (PIXEL_CLOCK_PARAMETERS_V2 *) & args.sPCLKInput;
+- spc2_ptr->usPixelClock = cpu_to_le16(mode->clock / 10);
+- spc2_ptr->usRefDiv = cpu_to_le16(ref_div);
+- spc2_ptr->usFbDiv = cpu_to_le16(fb_div);
+- spc2_ptr->ucFracFbDiv = frac_fb_div;
+- spc2_ptr->ucPostDiv = post_div;
+- spc2_ptr->ucPpll =
++ args.v2.usPixelClock = cpu_to_le16(mode->clock / 10);
++ args.v2.usRefDiv = cpu_to_le16(ref_div);
++ args.v2.usFbDiv = cpu_to_le16(fb_div);
++ args.v2.ucFracFbDiv = frac_fb_div;
++ args.v2.ucPostDiv = post_div;
++ args.v2.ucPpll =
+ radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1;
+- spc2_ptr->ucCRTC = radeon_crtc->crtc_id;
+- spc2_ptr->ucRefDivSrc = 1;
++ args.v2.ucCRTC = radeon_crtc->crtc_id;
++ args.v2.ucRefDivSrc = 1;
+ break;
+ case 3:
+- if (!encoder)
+- return;
+- spc3_ptr =
+- (PIXEL_CLOCK_PARAMETERS_V3 *) & args.sPCLKInput;
+- spc3_ptr->usPixelClock = cpu_to_le16(mode->clock / 10);
+- spc3_ptr->usRefDiv = cpu_to_le16(ref_div);
+- spc3_ptr->usFbDiv = cpu_to_le16(fb_div);
+- spc3_ptr->ucFracFbDiv = frac_fb_div;
+- spc3_ptr->ucPostDiv = post_div;
+- spc3_ptr->ucPpll =
++ args.v3.usPixelClock = cpu_to_le16(mode->clock / 10);
++ args.v3.usRefDiv = cpu_to_le16(ref_div);
++ args.v3.usFbDiv = cpu_to_le16(fb_div);
++ args.v3.ucFracFbDiv = frac_fb_div;
++ args.v3.ucPostDiv = post_div;
++ args.v3.ucPpll =
+ radeon_crtc->crtc_id ? ATOM_PPLL2 : ATOM_PPLL1;
+- spc3_ptr->ucMiscInfo = (radeon_crtc->crtc_id << 2);
+- spc3_ptr->ucTransmitterId = radeon_encoder->encoder_id;
+- spc3_ptr->ucEncoderMode =
++ args.v3.ucMiscInfo = (radeon_crtc->crtc_id << 2);
++ args.v3.ucTransmitterId = radeon_encoder->encoder_id;
++ args.v3.ucEncoderMode =
+ atombios_get_encoder_mode(encoder);
+ break;
+ default:
+@@ -564,33 +615,43 @@ void atombios_crtc_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
+ return;
+ }
+
+- printk("executing set pll\n");
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+ }
+
+-int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
+- struct drm_framebuffer *old_fb)
++static int avivo_crtc_set_base(struct drm_crtc *crtc, int x, int y,
++ struct drm_framebuffer *old_fb)
+ {
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+ struct drm_device *dev = crtc->dev;
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_framebuffer *radeon_fb;
+ struct drm_gem_object *obj;
+- struct drm_radeon_gem_object *obj_priv;
++ struct radeon_bo *rbo;
+ uint64_t fb_location;
+ uint32_t fb_format, fb_pitch_pixels, tiling_flags;
++ int r;
+
+- if (!crtc->fb)
+- return -EINVAL;
++ /* no fb bound */
++ if (!crtc->fb) {
++ DRM_DEBUG("No FB bound\n");
++ return 0;
++ }
+
+ radeon_fb = to_radeon_framebuffer(crtc->fb);
+
++ /* Pin framebuffer & get tilling informations */
+ obj = radeon_fb->obj;
+- obj_priv = obj->driver_private;
+-
+- if (radeon_gem_object_pin(obj, RADEON_GEM_DOMAIN_VRAM, &fb_location)) {
++ rbo = obj->driver_private;
++ r = radeon_bo_reserve(rbo, false);
++ if (unlikely(r != 0))
++ return r;
++ r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_location);
++ if (unlikely(r != 0)) {
++ radeon_bo_unreserve(rbo);
+ return -EINVAL;
+ }
++ radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL);
++ radeon_bo_unreserve(rbo);
+
+ switch (crtc->fb->bits_per_pixel) {
+ case 8:
+@@ -620,8 +681,6 @@ int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
+ return -EINVAL;
+ }
+
+- radeon_object_get_tiling_flags(obj->driver_private,
+- &tiling_flags, NULL);
+ if (tiling_flags & RADEON_TILING_MACRO)
+ fb_format |= AVIVO_D1GRPH_MACRO_ADDRESS_MODE;
+
+@@ -676,7 +735,12 @@ int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
+
+ if (old_fb && old_fb != crtc->fb) {
+ radeon_fb = to_radeon_framebuffer(old_fb);
+- radeon_gem_object_unpin(radeon_fb->obj);
++ rbo = radeon_fb->obj->driver_private;
++ r = radeon_bo_reserve(rbo, false);
++ if (unlikely(r != 0))
++ return r;
++ radeon_bo_unpin(rbo);
++ radeon_bo_unreserve(rbo);
+ }
+
+ /* Bytes per pixel may have changed */
+@@ -685,6 +749,42 @@ int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
+ return 0;
+ }
+
++int atombios_crtc_set_base(struct drm_crtc *crtc, int x, int y,
++ struct drm_framebuffer *old_fb)
++{
++ struct drm_device *dev = crtc->dev;
++ struct radeon_device *rdev = dev->dev_private;
++
++ if (ASIC_IS_AVIVO(rdev))
++ return avivo_crtc_set_base(crtc, x, y, old_fb);
++ else
++ return radeon_crtc_set_base(crtc, x, y, old_fb);
++}
++
++/* properly set additional regs when using atombios */
++static void radeon_legacy_atom_fixup(struct drm_crtc *crtc)
++{
++ struct drm_device *dev = crtc->dev;
++ struct radeon_device *rdev = dev->dev_private;
++ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
++ u32 disp_merge_cntl;
++
++ switch (radeon_crtc->crtc_id) {
++ case 0:
++ disp_merge_cntl = RREG32(RADEON_DISP_MERGE_CNTL);
++ disp_merge_cntl &= ~RADEON_DISP_RGB_OFFSET_EN;
++ WREG32(RADEON_DISP_MERGE_CNTL, disp_merge_cntl);
++ break;
++ case 1:
++ disp_merge_cntl = RREG32(RADEON_DISP2_MERGE_CNTL);
++ disp_merge_cntl &= ~RADEON_DISP2_RGB_OFFSET_EN;
++ WREG32(RADEON_DISP2_MERGE_CNTL, disp_merge_cntl);
++ WREG32(RADEON_FP_H2_SYNC_STRT_WID, RREG32(RADEON_CRTC2_H_SYNC_STRT_WID));
++ WREG32(RADEON_FP_V2_SYNC_STRT_WID, RREG32(RADEON_CRTC2_V_SYNC_STRT_WID));
++ break;
++ }
++}
++
+ int atombios_crtc_mode_set(struct drm_crtc *crtc,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode,
+@@ -706,8 +806,8 @@ int atombios_crtc_mode_set(struct drm_crtc *crtc,
+ else {
+ if (radeon_crtc->crtc_id == 0)
+ atombios_set_crtc_dtd_timing(crtc, adjusted_mode);
+- radeon_crtc_set_base(crtc, x, y, old_fb);
+- radeon_legacy_atom_set_surface(crtc);
++ atombios_crtc_set_base(crtc, x, y, old_fb);
++ radeon_legacy_atom_fixup(crtc);
+ }
+ atombios_overscan_setup(crtc, mode, adjusted_mode);
+ atombios_scaler_setup(crtc);
+@@ -725,8 +825,8 @@ static bool atombios_crtc_mode_fixup(struct drm_crtc *crtc,
+
+ static void atombios_crtc_prepare(struct drm_crtc *crtc)
+ {
+- atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+ atombios_lock_crtc(crtc, 1);
++ atombios_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
+ }
+
+ static void atombios_crtc_commit(struct drm_crtc *crtc)
+diff --git a/drivers/gpu/drm/radeon/atombios_dp.c b/drivers/gpu/drm/radeon/atombios_dp.c
+new file mode 100644
+index 0000000..99915a6
+--- /dev/null
++++ b/drivers/gpu/drm/radeon/atombios_dp.c
+@@ -0,0 +1,789 @@
++/*
++ * Copyright 2007-8 Advanced Micro Devices, Inc.
++ * Copyright 2008 Red Hat Inc.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ *
++ * Authors: Dave Airlie
++ * Alex Deucher
++ */
++#include "drmP.h"
++#include "radeon_drm.h"
++#include "radeon.h"
++
++#include "atom.h"
++#include "atom-bits.h"
++#include "drm_dp_helper.h"
++
++/* move these to drm_dp_helper.c/h */
++#define DP_LINK_CONFIGURATION_SIZE 9
++#define DP_LINK_STATUS_SIZE 6
++#define DP_DPCD_SIZE 8
++
++static char *voltage_names[] = {
++ "0.4V", "0.6V", "0.8V", "1.2V"
++};
++static char *pre_emph_names[] = {
++ "0dB", "3.5dB", "6dB", "9.5dB"
++};
++
++static const int dp_clocks[] = {
++ 54000, /* 1 lane, 1.62 Ghz */
++ 90000, /* 1 lane, 2.70 Ghz */
++ 108000, /* 2 lane, 1.62 Ghz */
++ 180000, /* 2 lane, 2.70 Ghz */
++ 216000, /* 4 lane, 1.62 Ghz */
++ 360000, /* 4 lane, 2.70 Ghz */
++};
++
++static const int num_dp_clocks = sizeof(dp_clocks) / sizeof(int);
++
++/* common helper functions */
++static int dp_lanes_for_mode_clock(u8 dpcd[DP_DPCD_SIZE], int mode_clock)
++{
++ int i;
++ u8 max_link_bw;
++ u8 max_lane_count;
++
++ if (!dpcd)
++ return 0;
++
++ max_link_bw = dpcd[DP_MAX_LINK_RATE];
++ max_lane_count = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
++
++ switch (max_link_bw) {
++ case DP_LINK_BW_1_62:
++ default:
++ for (i = 0; i < num_dp_clocks; i++) {
++ if (i % 2)
++ continue;
++ switch (max_lane_count) {
++ case 1:
++ if (i > 1)
++ return 0;
++ break;
++ case 2:
++ if (i > 3)
++ return 0;
++ break;
++ case 4:
++ default:
++ break;
++ }
++ if (dp_clocks[i] > mode_clock) {
++ if (i < 2)
++ return 1;
++ else if (i < 4)
++ return 2;
++ else
++ return 4;
++ }
++ }
++ break;
++ case DP_LINK_BW_2_7:
++ for (i = 0; i < num_dp_clocks; i++) {
++ switch (max_lane_count) {
++ case 1:
++ if (i > 1)
++ return 0;
++ break;
++ case 2:
++ if (i > 3)
++ return 0;
++ break;
++ case 4:
++ default:
++ break;
++ }
++ if (dp_clocks[i] > mode_clock) {
++ if (i < 2)
++ return 1;
++ else if (i < 4)
++ return 2;
++ else
++ return 4;
++ }
++ }
++ break;
++ }
++
++ return 0;
++}
++
++static int dp_link_clock_for_mode_clock(u8 dpcd[DP_DPCD_SIZE], int mode_clock)
++{
++ int i;
++ u8 max_link_bw;
++ u8 max_lane_count;
++
++ if (!dpcd)
++ return 0;
++
++ max_link_bw = dpcd[DP_MAX_LINK_RATE];
++ max_lane_count = dpcd[DP_MAX_LANE_COUNT] & DP_MAX_LANE_COUNT_MASK;
++
++ switch (max_link_bw) {
++ case DP_LINK_BW_1_62:
++ default:
++ for (i = 0; i < num_dp_clocks; i++) {
++ if (i % 2)
++ continue;
++ switch (max_lane_count) {
++ case 1:
++ if (i > 1)
++ return 0;
++ break;
++ case 2:
++ if (i > 3)
++ return 0;
++ break;
++ case 4:
++ default:
++ break;
++ }
++ if (dp_clocks[i] > mode_clock)
++ return 162000;
++ }
++ break;
++ case DP_LINK_BW_2_7:
++ for (i = 0; i < num_dp_clocks; i++) {
++ switch (max_lane_count) {
++ case 1:
++ if (i > 1)
++ return 0;
++ break;
++ case 2:
++ if (i > 3)
++ return 0;
++ break;
++ case 4:
++ default:
++ break;
++ }
++ if (dp_clocks[i] > mode_clock)
++ return (i % 2) ? 270000 : 162000;
++ }
++ }
++
++ return 0;
++}
++
++int dp_mode_valid(u8 dpcd[DP_DPCD_SIZE], int mode_clock)
++{
++ int lanes = dp_lanes_for_mode_clock(dpcd, mode_clock);
++ int bw = dp_lanes_for_mode_clock(dpcd, mode_clock);
++
++ if ((lanes == 0) || (bw == 0))
++ return MODE_CLOCK_HIGH;
++
++ return MODE_OK;
++}
++
++static u8 dp_link_status(u8 link_status[DP_LINK_STATUS_SIZE], int r)
++{
++ return link_status[r - DP_LANE0_1_STATUS];
++}
++
++static u8 dp_get_lane_status(u8 link_status[DP_LINK_STATUS_SIZE],
++ int lane)
++{
++ int i = DP_LANE0_1_STATUS + (lane >> 1);
++ int s = (lane & 1) * 4;
++ u8 l = dp_link_status(link_status, i);
++ return (l >> s) & 0xf;
++}
++
++static bool dp_clock_recovery_ok(u8 link_status[DP_LINK_STATUS_SIZE],
++ int lane_count)
++{
++ int lane;
++ u8 lane_status;
++
++ for (lane = 0; lane < lane_count; lane++) {
++ lane_status = dp_get_lane_status(link_status, lane);
++ if ((lane_status & DP_LANE_CR_DONE) == 0)
++ return false;
++ }
++ return true;
++}
++
++static bool dp_channel_eq_ok(u8 link_status[DP_LINK_STATUS_SIZE],
++ int lane_count)
++{
++ u8 lane_align;
++ u8 lane_status;
++ int lane;
++
++ lane_align = dp_link_status(link_status,
++ DP_LANE_ALIGN_STATUS_UPDATED);
++ if ((lane_align & DP_INTERLANE_ALIGN_DONE) == 0)
++ return false;
++ for (lane = 0; lane < lane_count; lane++) {
++ lane_status = dp_get_lane_status(link_status, lane);
++ if ((lane_status & DP_CHANNEL_EQ_BITS) != DP_CHANNEL_EQ_BITS)
++ return false;
++ }
++ return true;
++}
++
++static u8 dp_get_adjust_request_voltage(uint8_t link_status[DP_LINK_STATUS_SIZE],
++ int lane)
++
++{
++ int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
++ int s = ((lane & 1) ?
++ DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT :
++ DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT);
++ u8 l = dp_link_status(link_status, i);
++
++ return ((l >> s) & 0x3) << DP_TRAIN_VOLTAGE_SWING_SHIFT;
++}
++
++static u8 dp_get_adjust_request_pre_emphasis(uint8_t link_status[DP_LINK_STATUS_SIZE],
++ int lane)
++{
++ int i = DP_ADJUST_REQUEST_LANE0_1 + (lane >> 1);
++ int s = ((lane & 1) ?
++ DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT :
++ DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT);
++ u8 l = dp_link_status(link_status, i);
++
++ return ((l >> s) & 0x3) << DP_TRAIN_PRE_EMPHASIS_SHIFT;
++}
++
++/* XXX fix me -- chip specific */
++#define DP_VOLTAGE_MAX DP_TRAIN_VOLTAGE_SWING_1200
++static u8 dp_pre_emphasis_max(u8 voltage_swing)
++{
++ switch (voltage_swing & DP_TRAIN_VOLTAGE_SWING_MASK) {
++ case DP_TRAIN_VOLTAGE_SWING_400:
++ return DP_TRAIN_PRE_EMPHASIS_6;
++ case DP_TRAIN_VOLTAGE_SWING_600:
++ return DP_TRAIN_PRE_EMPHASIS_6;
++ case DP_TRAIN_VOLTAGE_SWING_800:
++ return DP_TRAIN_PRE_EMPHASIS_3_5;
++ case DP_TRAIN_VOLTAGE_SWING_1200:
++ default:
++ return DP_TRAIN_PRE_EMPHASIS_0;
++ }
++}
++
++static void dp_get_adjust_train(u8 link_status[DP_LINK_STATUS_SIZE],
++ int lane_count,
++ u8 train_set[4])
++{
++ u8 v = 0;
++ u8 p = 0;
++ int lane;
++
++ for (lane = 0; lane < lane_count; lane++) {
++ u8 this_v = dp_get_adjust_request_voltage(link_status, lane);
++ u8 this_p = dp_get_adjust_request_pre_emphasis(link_status, lane);
++
++ DRM_DEBUG("requested signal parameters: lane %d voltage %s pre_emph %s\n",
++ lane,
++ voltage_names[this_v >> DP_TRAIN_VOLTAGE_SWING_SHIFT],
++ pre_emph_names[this_p >> DP_TRAIN_PRE_EMPHASIS_SHIFT]);
++
++ if (this_v > v)
++ v = this_v;
++ if (this_p > p)
++ p = this_p;
++ }
++
++ if (v >= DP_VOLTAGE_MAX)
++ v = DP_VOLTAGE_MAX | DP_TRAIN_MAX_SWING_REACHED;
++
++ if (p >= dp_pre_emphasis_max(v))
++ p = dp_pre_emphasis_max(v) | DP_TRAIN_MAX_PRE_EMPHASIS_REACHED;
++
++ DRM_DEBUG("using signal parameters: voltage %s pre_emph %s\n",
++ voltage_names[(v & DP_TRAIN_VOLTAGE_SWING_MASK) >> DP_TRAIN_VOLTAGE_SWING_SHIFT],
++ pre_emph_names[(p & DP_TRAIN_PRE_EMPHASIS_MASK) >> DP_TRAIN_PRE_EMPHASIS_SHIFT]);
++
++ for (lane = 0; lane < 4; lane++)
++ train_set[lane] = v | p;
++}
++
++
++/* radeon aux chan functions */
++bool radeon_process_aux_ch(struct radeon_i2c_chan *chan, u8 *req_bytes,
++ int num_bytes, u8 *read_byte,
++ u8 read_buf_len, u8 delay)
++{
++ struct drm_device *dev = chan->dev;
++ struct radeon_device *rdev = dev->dev_private;
++ PROCESS_AUX_CHANNEL_TRANSACTION_PS_ALLOCATION args;
++ int index = GetIndexIntoMasterTable(COMMAND, ProcessAuxChannelTransaction);
++ unsigned char *base;
++ int retry_count = 0;
++
++ memset(&args, 0, sizeof(args));
++
++ base = (unsigned char *)rdev->mode_info.atom_context->scratch;
++
++retry:
++ memcpy(base, req_bytes, num_bytes);
++
++ args.lpAuxRequest = 0;
++ args.lpDataOut = 16;
++ args.ucDataOutLen = 0;
++ args.ucChannelID = chan->rec.i2c_id;
++ args.ucDelay = delay / 10;
++
++ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
++
++ if (args.ucReplyStatus && !args.ucDataOutLen) {
++ if (args.ucReplyStatus == 0x20 && retry_count++ < 10)
++ goto retry;
++ DRM_DEBUG("failed to get auxch %02x%02x %02x %02x 0x%02x %02x after %d retries\n",
++ req_bytes[1], req_bytes[0], req_bytes[2], req_bytes[3],
++ chan->rec.i2c_id, args.ucReplyStatus, retry_count);
++ return false;
++ }
++
++ if (args.ucDataOutLen && read_byte && read_buf_len) {
++ if (read_buf_len < args.ucDataOutLen) {
++ DRM_ERROR("Buffer to small for return answer %d %d\n",
++ read_buf_len, args.ucDataOutLen);
++ return false;
++ }
++ {
++ int len = min(read_buf_len, args.ucDataOutLen);
++ memcpy(read_byte, base + 16, len);
++ }
++ }
++ return true;
++}
++
++bool radeon_dp_aux_native_write(struct radeon_connector *radeon_connector, uint16_t address,
++ uint8_t send_bytes, uint8_t *send)
++{
++ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
++ u8 msg[20];
++ u8 msg_len, dp_msg_len;
++ bool ret;
++
++ dp_msg_len = 4;
++ msg[0] = address;
++ msg[1] = address >> 8;
++ msg[2] = AUX_NATIVE_WRITE << 4;
++ dp_msg_len += send_bytes;
++ msg[3] = (dp_msg_len << 4) | (send_bytes - 1);
++
++ if (send_bytes > 16)
++ return false;
++
++ memcpy(&msg[4], send, send_bytes);
++ msg_len = 4 + send_bytes;
++ ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, msg, msg_len, NULL, 0, 0);
++ return ret;
++}
++
++bool radeon_dp_aux_native_read(struct radeon_connector *radeon_connector, uint16_t address,
++ uint8_t delay, uint8_t expected_bytes,
++ uint8_t *read_p)
++{
++ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
++ u8 msg[20];
++ u8 msg_len, dp_msg_len;
++ bool ret = false;
++ msg_len = 4;
++ dp_msg_len = 4;
++ msg[0] = address;
++ msg[1] = address >> 8;
++ msg[2] = AUX_NATIVE_READ << 4;
++ msg[3] = (dp_msg_len) << 4;
++ msg[3] |= expected_bytes - 1;
++
++ ret = radeon_process_aux_ch(dig_connector->dp_i2c_bus, msg, msg_len, read_p, expected_bytes, delay);
++ return ret;
++}
++
++/* radeon dp functions */
++static u8 radeon_dp_encoder_service(struct radeon_device *rdev, int action, int dp_clock,
++ uint8_t ucconfig, uint8_t lane_num)
++{
++ DP_ENCODER_SERVICE_PARAMETERS args;
++ int index = GetIndexIntoMasterTable(COMMAND, DPEncoderService);
++
++ memset(&args, 0, sizeof(args));
++ args.ucLinkClock = dp_clock / 10;
++ args.ucConfig = ucconfig;
++ args.ucAction = action;
++ args.ucLaneNum = lane_num;
++ args.ucStatus = 0;
++
++ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
++ return args.ucStatus;
++}
++
++u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector)
++{
++ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
++ struct drm_device *dev = radeon_connector->base.dev;
++ struct radeon_device *rdev = dev->dev_private;
++
++ return radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_GET_SINK_TYPE, 0,
++ dig_connector->dp_i2c_bus->rec.i2c_id, 0);
++}
++
++bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector)
++{
++ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
++ u8 msg[25];
++ int ret;
++
++ ret = radeon_dp_aux_native_read(radeon_connector, DP_DPCD_REV, 0, 8, msg);
++ if (ret) {
++ memcpy(dig_connector->dpcd, msg, 8);
++ {
++ int i;
++ DRM_DEBUG("DPCD: ");
++ for (i = 0; i < 8; i++)
++ DRM_DEBUG("%02x ", msg[i]);
++ DRM_DEBUG("\n");
++ }
++ return true;
++ }
++ dig_connector->dpcd[0] = 0;
++ return false;
++}
++
++void radeon_dp_set_link_config(struct drm_connector *connector,
++ struct drm_display_mode *mode)
++{
++ struct radeon_connector *radeon_connector;
++ struct radeon_connector_atom_dig *dig_connector;
++
++ if ((connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) &&
++ (connector->connector_type != DRM_MODE_CONNECTOR_eDP))
++ return;
++
++ radeon_connector = to_radeon_connector(connector);
++ if (!radeon_connector->con_priv)
++ return;
++ dig_connector = radeon_connector->con_priv;
++
++ dig_connector->dp_clock =
++ dp_link_clock_for_mode_clock(dig_connector->dpcd, mode->clock);
++ dig_connector->dp_lane_count =
++ dp_lanes_for_mode_clock(dig_connector->dpcd, mode->clock);
++}
++
++int radeon_dp_mode_valid_helper(struct radeon_connector *radeon_connector,
++ struct drm_display_mode *mode)
++{
++ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
++
++ return dp_mode_valid(dig_connector->dpcd, mode->clock);
++}
++
++static bool atom_dp_get_link_status(struct radeon_connector *radeon_connector,
++ u8 link_status[DP_LINK_STATUS_SIZE])
++{
++ int ret;
++ ret = radeon_dp_aux_native_read(radeon_connector, DP_LANE0_1_STATUS, 100,
++ DP_LINK_STATUS_SIZE, link_status);
++ if (!ret) {
++ DRM_ERROR("displayport link status failed\n");
++ return false;
++ }
++
++ DRM_DEBUG("link status %02x %02x %02x %02x %02x %02x\n",
++ link_status[0], link_status[1], link_status[2],
++ link_status[3], link_status[4], link_status[5]);
++ return true;
++}
++
++bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector)
++{
++ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
++ u8 link_status[DP_LINK_STATUS_SIZE];
++
++ if (!atom_dp_get_link_status(radeon_connector, link_status))
++ return false;
++ if (dp_channel_eq_ok(link_status, dig_connector->dp_lane_count))
++ return false;
++ return true;
++}
++
++static void dp_set_power(struct radeon_connector *radeon_connector, u8 power_state)
++{
++ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
++
++ if (dig_connector->dpcd[0] >= 0x11) {
++ radeon_dp_aux_native_write(radeon_connector, DP_SET_POWER, 1,
++ &power_state);
++ }
++}
++
++static void dp_set_downspread(struct radeon_connector *radeon_connector, u8 downspread)
++{
++ radeon_dp_aux_native_write(radeon_connector, DP_DOWNSPREAD_CTRL, 1,
++ &downspread);
++}
++
++static void dp_set_link_bw_lanes(struct radeon_connector *radeon_connector,
++ u8 link_configuration[DP_LINK_CONFIGURATION_SIZE])
++{
++ radeon_dp_aux_native_write(radeon_connector, DP_LINK_BW_SET, 2,
++ link_configuration);
++}
++
++static void dp_update_dpvs_emph(struct radeon_connector *radeon_connector,
++ struct drm_encoder *encoder,
++ u8 train_set[4])
++{
++ struct radeon_connector_atom_dig *dig_connector = radeon_connector->con_priv;
++ int i;
++
++ for (i = 0; i < dig_connector->dp_lane_count; i++)
++ atombios_dig_transmitter_setup(encoder,
++ ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH,
++ i, train_set[i]);
++
++ radeon_dp_aux_native_write(radeon_connector, DP_TRAINING_LANE0_SET,
++ dig_connector->dp_lane_count, train_set);
++}
++
++static void dp_set_training(struct radeon_connector *radeon_connector,
++ u8 training)
++{
++ radeon_dp_aux_native_write(radeon_connector, DP_TRAINING_PATTERN_SET,
++ 1, &training);
++}
++
++void dp_link_train(struct drm_encoder *encoder,
++ struct drm_connector *connector)
++{
++ struct drm_device *dev = encoder->dev;
++ struct radeon_device *rdev = dev->dev_private;
++ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
++ struct radeon_encoder_atom_dig *dig;
++ struct radeon_connector *radeon_connector;
++ struct radeon_connector_atom_dig *dig_connector;
++ int enc_id = 0;
++ bool clock_recovery, channel_eq;
++ u8 link_status[DP_LINK_STATUS_SIZE];
++ u8 link_configuration[DP_LINK_CONFIGURATION_SIZE];
++ u8 tries, voltage;
++ u8 train_set[4];
++ int i;
++
++ if ((connector->connector_type != DRM_MODE_CONNECTOR_DisplayPort) &&
++ (connector->connector_type != DRM_MODE_CONNECTOR_eDP))
++ return;
++
++ if (!radeon_encoder->enc_priv)
++ return;
++ dig = radeon_encoder->enc_priv;
++
++ radeon_connector = to_radeon_connector(connector);
++ if (!radeon_connector->con_priv)
++ return;
++ dig_connector = radeon_connector->con_priv;
++
++ if (dig->dig_encoder)
++ enc_id |= ATOM_DP_CONFIG_DIG2_ENCODER;
++ else
++ enc_id |= ATOM_DP_CONFIG_DIG1_ENCODER;
++ if (dig_connector->linkb)
++ enc_id |= ATOM_DP_CONFIG_LINK_B;
++ else
++ enc_id |= ATOM_DP_CONFIG_LINK_A;
++
++ memset(link_configuration, 0, DP_LINK_CONFIGURATION_SIZE);
++ if (dig_connector->dp_clock == 270000)
++ link_configuration[0] = DP_LINK_BW_2_7;
++ else
++ link_configuration[0] = DP_LINK_BW_1_62;
++ link_configuration[1] = dig_connector->dp_lane_count;
++ if (dig_connector->dpcd[0] >= 0x11)
++ link_configuration[1] |= DP_LANE_COUNT_ENHANCED_FRAME_EN;
++
++ /* power up the sink */
++ dp_set_power(radeon_connector, DP_SET_POWER_D0);
++ /* disable the training pattern on the sink */
++ dp_set_training(radeon_connector, DP_TRAINING_PATTERN_DISABLE);
++ /* set link bw and lanes on the sink */
++ dp_set_link_bw_lanes(radeon_connector, link_configuration);
++ /* disable downspread on the sink */
++ dp_set_downspread(radeon_connector, 0);
++ /* start training on the source */
++ radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_START,
++ dig_connector->dp_clock, enc_id, 0);
++ /* set training pattern 1 on the source */
++ radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
++ dig_connector->dp_clock, enc_id, 0);
++
++ /* set initial vs/emph */
++ memset(train_set, 0, 4);
++ udelay(400);
++ /* set training pattern 1 on the sink */
++ dp_set_training(radeon_connector, DP_TRAINING_PATTERN_1);
++
++ dp_update_dpvs_emph(radeon_connector, encoder, train_set);
++
++ /* clock recovery loop */
++ clock_recovery = false;
++ tries = 0;
++ voltage = 0xff;
++ for (;;) {
++ udelay(100);
++ if (!atom_dp_get_link_status(radeon_connector, link_status))
++ break;
++
++ if (dp_clock_recovery_ok(link_status, dig_connector->dp_lane_count)) {
++ clock_recovery = true;
++ break;
++ }
++
++ for (i = 0; i < dig_connector->dp_lane_count; i++) {
++ if ((train_set[i] & DP_TRAIN_MAX_SWING_REACHED) == 0)
++ break;
++ }
++ if (i == dig_connector->dp_lane_count) {
++ DRM_ERROR("clock recovery reached max voltage\n");
++ break;
++ }
++
++ if ((train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK) == voltage) {
++ ++tries;
++ if (tries == 5) {
++ DRM_ERROR("clock recovery tried 5 times\n");
++ break;
++ }
++ } else
++ tries = 0;
++
++ voltage = train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK;
++
++ /* Compute new train_set as requested by sink */
++ dp_get_adjust_train(link_status, dig_connector->dp_lane_count, train_set);
++ dp_update_dpvs_emph(radeon_connector, encoder, train_set);
++ }
++ if (!clock_recovery)
++ DRM_ERROR("clock recovery failed\n");
++ else
++ DRM_DEBUG("clock recovery at voltage %d pre-emphasis %d\n",
++ train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
++ (train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK) >>
++ DP_TRAIN_PRE_EMPHASIS_SHIFT);
++
++
++ /* set training pattern 2 on the sink */
++ dp_set_training(radeon_connector, DP_TRAINING_PATTERN_2);
++ /* set training pattern 2 on the source */
++ radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_PATTERN_SEL,
++ dig_connector->dp_clock, enc_id, 1);
++
++ /* channel equalization loop */
++ tries = 0;
++ channel_eq = false;
++ for (;;) {
++ udelay(400);
++ if (!atom_dp_get_link_status(radeon_connector, link_status))
++ break;
++
++ if (dp_channel_eq_ok(link_status, dig_connector->dp_lane_count)) {
++ channel_eq = true;
++ break;
++ }
++
++ /* Try 5 times */
++ if (tries > 5) {
++ DRM_ERROR("channel eq failed: 5 tries\n");
++ break;
++ }
++
++ /* Compute new train_set as requested by sink */
++ dp_get_adjust_train(link_status, dig_connector->dp_lane_count, train_set);
++ dp_update_dpvs_emph(radeon_connector, encoder, train_set);
++
++ tries++;
++ }
++
++ if (!channel_eq)
++ DRM_ERROR("channel eq failed\n");
++ else
++ DRM_DEBUG("channel eq at voltage %d pre-emphasis %d\n",
++ train_set[0] & DP_TRAIN_VOLTAGE_SWING_MASK,
++ (train_set[0] & DP_TRAIN_PRE_EMPHASIS_MASK)
++ >> DP_TRAIN_PRE_EMPHASIS_SHIFT);
++
++ /* disable the training pattern on the sink */
++ dp_set_training(radeon_connector, DP_TRAINING_PATTERN_DISABLE);
++
++ radeon_dp_encoder_service(rdev, ATOM_DP_ACTION_TRAINING_COMPLETE,
++ dig_connector->dp_clock, enc_id, 0);
++}
++
++int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
++ uint8_t write_byte, uint8_t *read_byte)
++{
++ struct i2c_algo_dp_aux_data *algo_data = adapter->algo_data;
++ struct radeon_i2c_chan *auxch = (struct radeon_i2c_chan *)adapter;
++ int ret = 0;
++ uint16_t address = algo_data->address;
++ uint8_t msg[5];
++ uint8_t reply[2];
++ int msg_len, dp_msg_len;
++ int reply_bytes;
++
++ /* Set up the command byte */
++ if (mode & MODE_I2C_READ)
++ msg[2] = AUX_I2C_READ << 4;
++ else
++ msg[2] = AUX_I2C_WRITE << 4;
++
++ if (!(mode & MODE_I2C_STOP))
++ msg[2] |= AUX_I2C_MOT << 4;
++
++ msg[0] = address;
++ msg[1] = address >> 8;
++
++ reply_bytes = 1;
++
++ msg_len = 4;
++ dp_msg_len = 3;
++ switch (mode) {
++ case MODE_I2C_WRITE:
++ msg[4] = write_byte;
++ msg_len++;
++ dp_msg_len += 2;
++ break;
++ case MODE_I2C_READ:
++ dp_msg_len += 1;
++ break;
++ default:
++ break;
++ }
++
++ msg[3] = (dp_msg_len) << 4;
++ ret = radeon_process_aux_ch(auxch, msg, msg_len, reply, reply_bytes, 0);
++
++ if (ret) {
++ if (read_byte)
++ *read_byte = reply[0];
++ return reply_bytes;
++ }
++ return -EREMOTEIO;
++}
++
+diff --git a/drivers/gpu/drm/radeon/mkregtable.c b/drivers/gpu/drm/radeon/mkregtable.c
+index 0d79577..607241c 100644
+--- a/drivers/gpu/drm/radeon/mkregtable.c
++++ b/drivers/gpu/drm/radeon/mkregtable.c
+@@ -661,8 +661,10 @@ static int parser_auth(struct table *t, const char *filename)
+ fseek(file, 0, SEEK_SET);
+
+ /* get header */
+- if (fgets(buf, 1024, file) == NULL)
++ if (fgets(buf, 1024, file) == NULL) {
++ fclose(file);
+ return -1;
++ }
+
+ /* first line will contain the last register
+ * and gpu name */
+diff --git a/drivers/gpu/drm/radeon/r100.c b/drivers/gpu/drm/radeon/r100.c
+index c9e93ea..38209a6 100644
+--- a/drivers/gpu/drm/radeon/r100.c
++++ b/drivers/gpu/drm/radeon/r100.c
+@@ -65,6 +65,96 @@ MODULE_FIRMWARE(FIRMWARE_R520);
+ * r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280
+ */
+
++/* hpd for digital panel detect/disconnect */
++bool r100_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd)
++{
++ bool connected = false;
++
++ switch (hpd) {
++ case RADEON_HPD_1:
++ if (RREG32(RADEON_FP_GEN_CNTL) & RADEON_FP_DETECT_SENSE)
++ connected = true;
++ break;
++ case RADEON_HPD_2:
++ if (RREG32(RADEON_FP2_GEN_CNTL) & RADEON_FP2_DETECT_SENSE)
++ connected = true;
++ break;
++ default:
++ break;
++ }
++ return connected;
++}
++
++void r100_hpd_set_polarity(struct radeon_device *rdev,
++ enum radeon_hpd_id hpd)
++{
++ u32 tmp;
++ bool connected = r100_hpd_sense(rdev, hpd);
++
++ switch (hpd) {
++ case RADEON_HPD_1:
++ tmp = RREG32(RADEON_FP_GEN_CNTL);
++ if (connected)
++ tmp &= ~RADEON_FP_DETECT_INT_POL;
++ else
++ tmp |= RADEON_FP_DETECT_INT_POL;
++ WREG32(RADEON_FP_GEN_CNTL, tmp);
++ break;
++ case RADEON_HPD_2:
++ tmp = RREG32(RADEON_FP2_GEN_CNTL);
++ if (connected)
++ tmp &= ~RADEON_FP2_DETECT_INT_POL;
++ else
++ tmp |= RADEON_FP2_DETECT_INT_POL;
++ WREG32(RADEON_FP2_GEN_CNTL, tmp);
++ break;
++ default:
++ break;
++ }
++}
++
++void r100_hpd_init(struct radeon_device *rdev)
++{
++ struct drm_device *dev = rdev->ddev;
++ struct drm_connector *connector;
++
++ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
++ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
++ switch (radeon_connector->hpd.hpd) {
++ case RADEON_HPD_1:
++ rdev->irq.hpd[0] = true;
++ break;
++ case RADEON_HPD_2:
++ rdev->irq.hpd[1] = true;
++ break;
++ default:
++ break;
++ }
++ }
++ if (rdev->irq.installed)
++ r100_irq_set(rdev);
++}
++
++void r100_hpd_fini(struct radeon_device *rdev)
++{
++ struct drm_device *dev = rdev->ddev;
++ struct drm_connector *connector;
++
++ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
++ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
++ switch (radeon_connector->hpd.hpd) {
++ case RADEON_HPD_1:
++ rdev->irq.hpd[0] = false;
++ break;
++ case RADEON_HPD_2:
++ rdev->irq.hpd[1] = false;
++ break;
++ default:
++ break;
++ }
++ }
++}
++
+ /*
+ * PCI GART
+ */
+@@ -94,6 +184,15 @@ int r100_pci_gart_init(struct radeon_device *rdev)
+ return radeon_gart_table_ram_alloc(rdev);
+ }
+
++/* required on r1xx, r2xx, r300, r(v)350, r420/r481, rs400/rs480 */
++void r100_enable_bm(struct radeon_device *rdev)
++{
++ uint32_t tmp;
++ /* Enable bus mastering */
++ tmp = RREG32(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS;
++ WREG32(RADEON_BUS_CNTL, tmp);
++}
++
+ int r100_pci_gart_enable(struct radeon_device *rdev)
+ {
+ uint32_t tmp;
+@@ -105,9 +204,6 @@ int r100_pci_gart_enable(struct radeon_device *rdev)
+ WREG32(RADEON_AIC_LO_ADDR, rdev->mc.gtt_location);
+ tmp = rdev->mc.gtt_location + rdev->mc.gtt_size - 1;
+ WREG32(RADEON_AIC_HI_ADDR, tmp);
+- /* Enable bus mastering */
+- tmp = RREG32(RADEON_BUS_CNTL) & ~RADEON_BUS_MASTER_DIS;
+- WREG32(RADEON_BUS_CNTL, tmp);
+ /* set PCI GART page-table base address */
+ WREG32(RADEON_AIC_PT_BASE, rdev->gart.table_addr);
+ tmp = RREG32(RADEON_AIC_CNTL) | RADEON_PCIGART_TRANSLATE_EN;
+@@ -148,6 +244,11 @@ int r100_irq_set(struct radeon_device *rdev)
+ {
+ uint32_t tmp = 0;
+
++ if (!rdev->irq.installed) {
++ WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n");
++ WREG32(R_000040_GEN_INT_CNTL, 0);
++ return -EINVAL;
++ }
+ if (rdev->irq.sw_int) {
+ tmp |= RADEON_SW_INT_ENABLE;
+ }
+@@ -157,6 +258,12 @@ int r100_irq_set(struct radeon_device *rdev)
+ if (rdev->irq.crtc_vblank_int[1]) {
+ tmp |= RADEON_CRTC2_VBLANK_MASK;
+ }
++ if (rdev->irq.hpd[0]) {
++ tmp |= RADEON_FP_DETECT_MASK;
++ }
++ if (rdev->irq.hpd[1]) {
++ tmp |= RADEON_FP2_DETECT_MASK;
++ }
+ WREG32(RADEON_GEN_INT_CNTL, tmp);
+ return 0;
+ }
+@@ -175,8 +282,9 @@ void r100_irq_disable(struct radeon_device *rdev)
+ static inline uint32_t r100_irq_ack(struct radeon_device *rdev)
+ {
+ uint32_t irqs = RREG32(RADEON_GEN_INT_STATUS);
+- uint32_t irq_mask = RADEON_SW_INT_TEST | RADEON_CRTC_VBLANK_STAT |
+- RADEON_CRTC2_VBLANK_STAT;
++ uint32_t irq_mask = RADEON_SW_INT_TEST |
++ RADEON_CRTC_VBLANK_STAT | RADEON_CRTC2_VBLANK_STAT |
++ RADEON_FP_DETECT_STAT | RADEON_FP2_DETECT_STAT;
+
+ if (irqs) {
+ WREG32(RADEON_GEN_INT_STATUS, irqs);
+@@ -187,6 +295,7 @@ static inline uint32_t r100_irq_ack(struct radeon_device *rdev)
+ int r100_irq_process(struct radeon_device *rdev)
+ {
+ uint32_t status, msi_rearm;
++ bool queue_hotplug = false;
+
+ status = r100_irq_ack(rdev);
+ if (!status) {
+@@ -207,8 +316,18 @@ int r100_irq_process(struct radeon_device *rdev)
+ if (status & RADEON_CRTC2_VBLANK_STAT) {
+ drm_handle_vblank(rdev->ddev, 1);
+ }
++ if (status & RADEON_FP_DETECT_STAT) {
++ queue_hotplug = true;
++ DRM_DEBUG("HPD1\n");
++ }
++ if (status & RADEON_FP2_DETECT_STAT) {
++ queue_hotplug = true;
++ DRM_DEBUG("HPD2\n");
++ }
+ status = r100_irq_ack(rdev);
+ }
++ if (queue_hotplug)
++ queue_work(rdev->wq, &rdev->hotplug_work);
+ if (rdev->msi_enabled) {
+ switch (rdev->family) {
+ case CHIP_RS400:
+@@ -235,14 +354,25 @@ u32 r100_get_vblank_counter(struct radeon_device *rdev, int crtc)
+ return RREG32(RADEON_CRTC2_CRNT_FRAME);
+ }
+
++/* Who ever call radeon_fence_emit should call ring_lock and ask
++ * for enough space (today caller are ib schedule and buffer move) */
+ void r100_fence_ring_emit(struct radeon_device *rdev,
+ struct radeon_fence *fence)
+ {
+- /* Who ever call radeon_fence_emit should call ring_lock and ask
+- * for enough space (today caller are ib schedule and buffer move) */
++ /* We have to make sure that caches are flushed before
++ * CPU might read something from VRAM. */
++ radeon_ring_write(rdev, PACKET0(RADEON_RB3D_DSTCACHE_CTLSTAT, 0));
++ radeon_ring_write(rdev, RADEON_RB3D_DC_FLUSH_ALL);
++ radeon_ring_write(rdev, PACKET0(RADEON_RB3D_ZCACHE_CTLSTAT, 0));
++ radeon_ring_write(rdev, RADEON_RB3D_ZC_FLUSH_ALL);
+ /* Wait until IDLE & CLEAN */
+ radeon_ring_write(rdev, PACKET0(0x1720, 0));
+ radeon_ring_write(rdev, (1 << 16) | (1 << 17));
++ radeon_ring_write(rdev, PACKET0(RADEON_HOST_PATH_CNTL, 0));
++ radeon_ring_write(rdev, rdev->config.r100.hdp_cntl |
++ RADEON_HDP_READ_BUFFER_INVALIDATE);
++ radeon_ring_write(rdev, PACKET0(RADEON_HOST_PATH_CNTL, 0));
++ radeon_ring_write(rdev, rdev->config.r100.hdp_cntl);
+ /* Emit fence sequence & fire IRQ */
+ radeon_ring_write(rdev, PACKET0(rdev->fence_drv.scratch_reg, 0));
+ radeon_ring_write(rdev, fence->seq);
+@@ -255,24 +385,27 @@ int r100_wb_init(struct radeon_device *rdev)
+ int r;
+
+ if (rdev->wb.wb_obj == NULL) {
+- r = radeon_object_create(rdev, NULL, RADEON_GPU_PAGE_SIZE,
+- true,
+- RADEON_GEM_DOMAIN_GTT,
+- false, &rdev->wb.wb_obj);
++ r = radeon_bo_create(rdev, NULL, RADEON_GPU_PAGE_SIZE, true,
++ RADEON_GEM_DOMAIN_GTT,
++ &rdev->wb.wb_obj);
+ if (r) {
+- DRM_ERROR("radeon: failed to create WB buffer (%d).\n", r);
++ dev_err(rdev->dev, "(%d) create WB buffer failed\n", r);
+ return r;
+ }
+- r = radeon_object_pin(rdev->wb.wb_obj,
+- RADEON_GEM_DOMAIN_GTT,
+- &rdev->wb.gpu_addr);
++ r = radeon_bo_reserve(rdev->wb.wb_obj, false);
++ if (unlikely(r != 0))
++ return r;
++ r = radeon_bo_pin(rdev->wb.wb_obj, RADEON_GEM_DOMAIN_GTT,
++ &rdev->wb.gpu_addr);
+ if (r) {
+- DRM_ERROR("radeon: failed to pin WB buffer (%d).\n", r);
++ dev_err(rdev->dev, "(%d) pin WB buffer failed\n", r);
++ radeon_bo_unreserve(rdev->wb.wb_obj);
+ return r;
+ }
+- r = radeon_object_kmap(rdev->wb.wb_obj, (void **)&rdev->wb.wb);
++ r = radeon_bo_kmap(rdev->wb.wb_obj, (void **)&rdev->wb.wb);
++ radeon_bo_unreserve(rdev->wb.wb_obj);
+ if (r) {
+- DRM_ERROR("radeon: failed to map WB buffer (%d).\n", r);
++ dev_err(rdev->dev, "(%d) map WB buffer failed\n", r);
+ return r;
+ }
+ }
+@@ -290,11 +423,19 @@ void r100_wb_disable(struct radeon_device *rdev)
+
+ void r100_wb_fini(struct radeon_device *rdev)
+ {
++ int r;
++
+ r100_wb_disable(rdev);
+ if (rdev->wb.wb_obj) {
+- radeon_object_kunmap(rdev->wb.wb_obj);
+- radeon_object_unpin(rdev->wb.wb_obj);
+- radeon_object_unref(&rdev->wb.wb_obj);
++ r = radeon_bo_reserve(rdev->wb.wb_obj, false);
++ if (unlikely(r != 0)) {
++ dev_err(rdev->dev, "(%d) can't finish WB\n", r);
++ return;
++ }
++ radeon_bo_kunmap(rdev->wb.wb_obj);
++ radeon_bo_unpin(rdev->wb.wb_obj);
++ radeon_bo_unreserve(rdev->wb.wb_obj);
++ radeon_bo_unref(&rdev->wb.wb_obj);
+ rdev->wb.wb = NULL;
+ rdev->wb.wb_obj = NULL;
+ }
+@@ -1250,7 +1391,6 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
+ case RADEON_TXFORMAT_ARGB4444:
+ case RADEON_TXFORMAT_VYUY422:
+ case RADEON_TXFORMAT_YVYU422:
+- case RADEON_TXFORMAT_DXT1:
+ case RADEON_TXFORMAT_SHADOW16:
+ case RADEON_TXFORMAT_LDUDV655:
+ case RADEON_TXFORMAT_DUDV88:
+@@ -1258,12 +1398,19 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
+ break;
+ case RADEON_TXFORMAT_ARGB8888:
+ case RADEON_TXFORMAT_RGBA8888:
+- case RADEON_TXFORMAT_DXT23:
+- case RADEON_TXFORMAT_DXT45:
+ case RADEON_TXFORMAT_SHADOW32:
+ case RADEON_TXFORMAT_LDUDUV8888:
+ track->textures[i].cpp = 4;
+ break;
++ case RADEON_TXFORMAT_DXT1:
++ track->textures[i].cpp = 1;
++ track->textures[i].compress_format = R100_TRACK_COMP_DXT1;
++ break;
++ case RADEON_TXFORMAT_DXT23:
++ case RADEON_TXFORMAT_DXT45:
++ track->textures[i].cpp = 1;
++ track->textures[i].compress_format = R100_TRACK_COMP_DXT35;
++ break;
+ }
+ track->textures[i].cube_info[4].width = 1 << ((idx_value >> 16) & 0xf);
+ track->textures[i].cube_info[4].height = 1 << ((idx_value >> 20) & 0xf);
+@@ -1288,17 +1435,17 @@ static int r100_packet0_check(struct radeon_cs_parser *p,
+
+ int r100_cs_track_check_pkt3_indx_buffer(struct radeon_cs_parser *p,
+ struct radeon_cs_packet *pkt,
+- struct radeon_object *robj)
++ struct radeon_bo *robj)
+ {
+ unsigned idx;
+ u32 value;
+ idx = pkt->idx + 1;
+ value = radeon_get_ib_value(p, idx + 2);
+- if ((value + 1) > radeon_object_size(robj)) {
++ if ((value + 1) > radeon_bo_size(robj)) {
+ DRM_ERROR("[drm] Buffer too small for PACKET3 INDX_BUFFER "
+ "(need %u have %lu) !\n",
+ value + 1,
+- radeon_object_size(robj));
++ radeon_bo_size(robj));
+ return -EINVAL;
+ }
+ return 0;
+@@ -1363,6 +1510,7 @@ static int r100_packet3_check(struct radeon_cs_parser *p,
+ DRM_ERROR("PRIM_WALK must be 3 for IMMD draw\n");
+ return -EINVAL;
+ }
++ track->vtx_size = r100_get_vtx_size(radeon_get_ib_value(p, idx + 0));
+ track->vap_vf_cntl = radeon_get_ib_value(p, idx + 1);
+ track->immd_dwords = pkt->count - 1;
+ r = r100_cs_track_check(p->rdev, track);
+@@ -1650,6 +1798,82 @@ int r100_gpu_reset(struct radeon_device *rdev)
+ return 0;
+ }
+
++void r100_set_common_regs(struct radeon_device *rdev)
++{
++ struct drm_device *dev = rdev->ddev;
++ bool force_dac2 = false;
++
++ /* set these so they don't interfere with anything */
++ WREG32(RADEON_OV0_SCALE_CNTL, 0);
++ WREG32(RADEON_SUBPIC_CNTL, 0);
++ WREG32(RADEON_VIPH_CONTROL, 0);
++ WREG32(RADEON_I2C_CNTL_1, 0);
++ WREG32(RADEON_DVI_I2C_CNTL_1, 0);
++ WREG32(RADEON_CAP0_TRIG_CNTL, 0);
++ WREG32(RADEON_CAP1_TRIG_CNTL, 0);
++
++ /* always set up dac2 on rn50 and some rv100 as lots
++ * of servers seem to wire it up to a VGA port but
++ * don't report it in the bios connector
++ * table.
++ */
++ switch (dev->pdev->device) {
++ /* RN50 */
++ case 0x515e:
++ case 0x5969:
++ force_dac2 = true;
++ break;
++ /* RV100*/
++ case 0x5159:
++ case 0x515a:
++ /* DELL triple head servers */
++ if ((dev->pdev->subsystem_vendor == 0x1028 /* DELL */) &&
++ ((dev->pdev->subsystem_device == 0x016c) ||
++ (dev->pdev->subsystem_device == 0x016d) ||
++ (dev->pdev->subsystem_device == 0x016e) ||
++ (dev->pdev->subsystem_device == 0x016f) ||
++ (dev->pdev->subsystem_device == 0x0170) ||
++ (dev->pdev->subsystem_device == 0x017d) ||
++ (dev->pdev->subsystem_device == 0x017e) ||
++ (dev->pdev->subsystem_device == 0x0183) ||
++ (dev->pdev->subsystem_device == 0x018a) ||
++ (dev->pdev->subsystem_device == 0x019a)))
++ force_dac2 = true;
++ break;
++ }
++
++ if (force_dac2) {
++ u32 disp_hw_debug = RREG32(RADEON_DISP_HW_DEBUG);
++ u32 tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL);
++ u32 dac2_cntl = RREG32(RADEON_DAC_CNTL2);
++
++ /* For CRT on DAC2, don't turn it on if BIOS didn't
++ enable it, even it's detected.
++ */
++
++ /* force it to crtc0 */
++ dac2_cntl &= ~RADEON_DAC2_DAC_CLK_SEL;
++ dac2_cntl |= RADEON_DAC2_DAC2_CLK_SEL;
++ disp_hw_debug |= RADEON_CRT2_DISP1_SEL;
++
++ /* set up the TV DAC */
++ tv_dac_cntl &= ~(RADEON_TV_DAC_PEDESTAL |
++ RADEON_TV_DAC_STD_MASK |
++ RADEON_TV_DAC_RDACPD |
++ RADEON_TV_DAC_GDACPD |
++ RADEON_TV_DAC_BDACPD |
++ RADEON_TV_DAC_BGADJ_MASK |
++ RADEON_TV_DAC_DACADJ_MASK);
++ tv_dac_cntl |= (RADEON_TV_DAC_NBLANK |
++ RADEON_TV_DAC_NHOLD |
++ RADEON_TV_DAC_STD_PS2 |
++ (0x58 << 16));
++
++ WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl);
++ WREG32(RADEON_DISP_HW_DEBUG, disp_hw_debug);
++ WREG32(RADEON_DAC_CNTL2, dac2_cntl);
++ }
++}
+
+ /*
+ * VRAM info
+@@ -2588,13 +2812,14 @@ static inline void r100_cs_track_texture_print(struct r100_cs_track_texture *t)
+ DRM_ERROR("coordinate type %d\n", t->tex_coord_type);
+ DRM_ERROR("width round to power of 2 %d\n", t->roundup_w);
+ DRM_ERROR("height round to power of 2 %d\n", t->roundup_h);
++ DRM_ERROR("compress format %d\n", t->compress_format);
+ }
+
+ static int r100_cs_track_cube(struct radeon_device *rdev,
+ struct r100_cs_track *track, unsigned idx)
+ {
+ unsigned face, w, h;
+- struct radeon_object *cube_robj;
++ struct radeon_bo *cube_robj;
+ unsigned long size;
+
+ for (face = 0; face < 5; face++) {
+@@ -2607,9 +2832,9 @@ static int r100_cs_track_cube(struct radeon_device *rdev,
+
+ size += track->textures[idx].cube_info[face].offset;
+
+- if (size > radeon_object_size(cube_robj)) {
++ if (size > radeon_bo_size(cube_robj)) {
+ DRM_ERROR("Cube texture offset greater than object size %lu %lu\n",
+- size, radeon_object_size(cube_robj));
++ size, radeon_bo_size(cube_robj));
+ r100_cs_track_texture_print(&track->textures[idx]);
+ return -1;
+ }
+@@ -2617,10 +2842,40 @@ static int r100_cs_track_cube(struct radeon_device *rdev,
+ return 0;
+ }
+
++static int r100_track_compress_size(int compress_format, int w, int h)
++{
++ int block_width, block_height, block_bytes;
++ int wblocks, hblocks;
++ int min_wblocks;
++ int sz;
++
++ block_width = 4;
++ block_height = 4;
++
++ switch (compress_format) {
++ case R100_TRACK_COMP_DXT1:
++ block_bytes = 8;
++ min_wblocks = 4;
++ break;
++ default:
++ case R100_TRACK_COMP_DXT35:
++ block_bytes = 16;
++ min_wblocks = 2;
++ break;
++ }
++
++ hblocks = (h + block_height - 1) / block_height;
++ wblocks = (w + block_width - 1) / block_width;
++ if (wblocks < min_wblocks)
++ wblocks = min_wblocks;
++ sz = wblocks * hblocks * block_bytes;
++ return sz;
++}
++
+ static int r100_cs_track_texture_check(struct radeon_device *rdev,
+ struct r100_cs_track *track)
+ {
+- struct radeon_object *robj;
++ struct radeon_bo *robj;
+ unsigned long size;
+ unsigned u, i, w, h;
+ int ret;
+@@ -2654,9 +2909,15 @@ static int r100_cs_track_texture_check(struct radeon_device *rdev,
+ h = h / (1 << i);
+ if (track->textures[u].roundup_h)
+ h = roundup_pow_of_two(h);
+- size += w * h;
++ if (track->textures[u].compress_format) {
++
++ size += r100_track_compress_size(track->textures[u].compress_format, w, h);
++ /* compressed textures are block based */
++ } else
++ size += w * h;
+ }
+ size *= track->textures[u].cpp;
++
+ switch (track->textures[u].tex_coord_type) {
+ case 0:
+ break;
+@@ -2676,9 +2937,9 @@ static int r100_cs_track_texture_check(struct radeon_device *rdev,
+ "%u\n", track->textures[u].tex_coord_type, u);
+ return -EINVAL;
+ }
+- if (size > radeon_object_size(robj)) {
++ if (size > radeon_bo_size(robj)) {
+ DRM_ERROR("Texture of unit %u needs %lu bytes but is "
+- "%lu\n", u, size, radeon_object_size(robj));
++ "%lu\n", u, size, radeon_bo_size(robj));
+ r100_cs_track_texture_print(&track->textures[u]);
+ return -EINVAL;
+ }
+@@ -2695,15 +2956,19 @@ int r100_cs_track_check(struct radeon_device *rdev, struct r100_cs_track *track)
+
+ for (i = 0; i < track->num_cb; i++) {
+ if (track->cb[i].robj == NULL) {
++ if (!(track->fastfill || track->color_channel_mask ||
++ track->blend_read_enable)) {
++ continue;
++ }
+ DRM_ERROR("[drm] No buffer for color buffer %d !\n", i);
+ return -EINVAL;
+ }
+ size = track->cb[i].pitch * track->cb[i].cpp * track->maxy;
+ size += track->cb[i].offset;
+- if (size > radeon_object_size(track->cb[i].robj)) {
++ if (size > radeon_bo_size(track->cb[i].robj)) {
+ DRM_ERROR("[drm] Buffer too small for color buffer %d "
+ "(need %lu have %lu) !\n", i, size,
+- radeon_object_size(track->cb[i].robj));
++ radeon_bo_size(track->cb[i].robj));
+ DRM_ERROR("[drm] color buffer %d (%u %u %u %u)\n",
+ i, track->cb[i].pitch, track->cb[i].cpp,
+ track->cb[i].offset, track->maxy);
+@@ -2717,10 +2982,10 @@ int r100_cs_track_check(struct radeon_device *rdev, struct r100_cs_track *track)
+ }
+ size = track->zb.pitch * track->zb.cpp * track->maxy;
+ size += track->zb.offset;
+- if (size > radeon_object_size(track->zb.robj)) {
++ if (size > radeon_bo_size(track->zb.robj)) {
+ DRM_ERROR("[drm] Buffer too small for z buffer "
+ "(need %lu have %lu) !\n", size,
+- radeon_object_size(track->zb.robj));
++ radeon_bo_size(track->zb.robj));
+ DRM_ERROR("[drm] zbuffer (%u %u %u %u)\n",
+ track->zb.pitch, track->zb.cpp,
+ track->zb.offset, track->maxy);
+@@ -2738,11 +3003,12 @@ int r100_cs_track_check(struct radeon_device *rdev, struct r100_cs_track *track)
+ "bound\n", prim_walk, i);
+ return -EINVAL;
+ }
+- if (size > radeon_object_size(track->arrays[i].robj)) {
+- DRM_ERROR("(PW %u) Vertex array %u need %lu dwords "
+- "have %lu dwords\n", prim_walk, i,
+- size >> 2,
+- radeon_object_size(track->arrays[i].robj) >> 2);
++ if (size > radeon_bo_size(track->arrays[i].robj)) {
++ dev_err(rdev->dev, "(PW %u) Vertex array %u "
++ "need %lu dwords have %lu dwords\n",
++ prim_walk, i, size >> 2,
++ radeon_bo_size(track->arrays[i].robj)
++ >> 2);
+ DRM_ERROR("Max indices %u\n", track->max_indx);
+ return -EINVAL;
+ }
+@@ -2756,10 +3022,12 @@ int r100_cs_track_check(struct radeon_device *rdev, struct r100_cs_track *track)
+ "bound\n", prim_walk, i);
+ return -EINVAL;
+ }
+- if (size > radeon_object_size(track->arrays[i].robj)) {
+- DRM_ERROR("(PW %u) Vertex array %u need %lu dwords "
+- "have %lu dwords\n", prim_walk, i, size >> 2,
+- radeon_object_size(track->arrays[i].robj) >> 2);
++ if (size > radeon_bo_size(track->arrays[i].robj)) {
++ dev_err(rdev->dev, "(PW %u) Vertex array %u "
++ "need %lu dwords have %lu dwords\n",
++ prim_walk, i, size >> 2,
++ radeon_bo_size(track->arrays[i].robj)
++ >> 2);
+ return -EINVAL;
+ }
+ }
+@@ -2821,6 +3089,7 @@ void r100_cs_track_clear(struct radeon_device *rdev, struct r100_cs_track *track
+ track->arrays[i].esize = 0x7F;
+ }
+ for (i = 0; i < track->num_texture; i++) {
++ track->textures[i].compress_format = R100_TRACK_COMP_NONE;
+ track->textures[i].pitch = 16536;
+ track->textures[i].width = 16536;
+ track->textures[i].height = 16536;
+@@ -3101,6 +3370,9 @@ static int r100_startup(struct radeon_device *rdev)
+ {
+ int r;
+
++ /* set common regs */
++ r100_set_common_regs(rdev);
++ /* program mc */
+ r100_mc_program(rdev);
+ /* Resume clock */
+ r100_clock_startup(rdev);
+@@ -3108,14 +3380,15 @@ static int r100_startup(struct radeon_device *rdev)
+ r100_gpu_init(rdev);
+ /* Initialize GART (initialize after TTM so we can allocate
+ * memory through TTM but finalize after TTM) */
++ r100_enable_bm(rdev);
+ if (rdev->flags & RADEON_IS_PCI) {
+ r = r100_pci_gart_enable(rdev);
+ if (r)
+ return r;
+ }
+ /* Enable IRQ */
+- rdev->irq.sw_int = true;
+ r100_irq_set(rdev);
++ rdev->config.r100.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
+ /* 1M ring buffer */
+ r = r100_cp_init(rdev, 1024 * 1024);
+ if (r) {
+@@ -3150,6 +3423,8 @@ int r100_resume(struct radeon_device *rdev)
+ radeon_combios_asic_init(rdev->ddev);
+ /* Resume clock after posting */
+ r100_clock_startup(rdev);
++ /* Initialize surface registers */
++ radeon_surface_init(rdev);
+ return r100_startup(rdev);
+ }
+
+@@ -3165,16 +3440,16 @@ int r100_suspend(struct radeon_device *rdev)
+
+ void r100_fini(struct radeon_device *rdev)
+ {
+- r100_suspend(rdev);
+ r100_cp_fini(rdev);
+ r100_wb_fini(rdev);
+ r100_ib_fini(rdev);
+ radeon_gem_fini(rdev);
+ if (rdev->flags & RADEON_IS_PCI)
+ r100_pci_gart_fini(rdev);
++ radeon_agp_fini(rdev);
+ radeon_irq_kms_fini(rdev);
+ radeon_fence_driver_fini(rdev);
+- radeon_object_fini(rdev);
++ radeon_bo_fini(rdev);
+ radeon_atombios_fini(rdev);
+ kfree(rdev->bios);
+ rdev->bios = NULL;
+@@ -3195,9 +3470,7 @@ int r100_mc_init(struct radeon_device *rdev)
+ if (rdev->flags & RADEON_IS_AGP) {
+ r = radeon_agp_init(rdev);
+ if (r) {
+- printk(KERN_WARNING "[drm] Disabling AGP\n");
+- rdev->flags &= ~RADEON_IS_AGP;
+- rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024;
++ radeon_agp_disable(rdev);
+ } else {
+ rdev->mc.gtt_location = rdev->mc.agp_base;
+ }
+@@ -3242,14 +3515,14 @@ int r100_init(struct radeon_device *rdev)
+ RREG32(R_0007C0_CP_STAT));
+ }
+ /* check if cards are posted or not */
+- if (!radeon_card_posted(rdev) && rdev->bios) {
+- DRM_INFO("GPU not posted. posting now...\n");
+- radeon_combios_asic_init(rdev->ddev);
+- }
++ if (radeon_boot_test_post_card(rdev) == false)
++ return -EINVAL;
+ /* Set asic errata */
+ r100_errata(rdev);
+ /* Initialize clocks */
+ radeon_get_clock_info(rdev->ddev);
++ /* Initialize power management */
++ radeon_pm_init(rdev);
+ /* Get vram informations */
+ r100_vram_info(rdev);
+ /* Initialize memory controller (also test AGP) */
+@@ -3264,7 +3537,7 @@ int r100_init(struct radeon_device *rdev)
+ if (r)
+ return r;
+ /* Memory manager */
+- r = radeon_object_init(rdev);
++ r = radeon_bo_init(rdev);
+ if (r)
+ return r;
+ if (rdev->flags & RADEON_IS_PCI) {
+@@ -3278,13 +3551,12 @@ int r100_init(struct radeon_device *rdev)
+ if (r) {
+ /* Somethings want wront with the accel init stop accel */
+ dev_err(rdev->dev, "Disabling GPU acceleration\n");
+- r100_suspend(rdev);
+ r100_cp_fini(rdev);
+ r100_wb_fini(rdev);
+ r100_ib_fini(rdev);
++ radeon_irq_kms_fini(rdev);
+ if (rdev->flags & RADEON_IS_PCI)
+ r100_pci_gart_fini(rdev);
+- radeon_irq_kms_fini(rdev);
+ rdev->accel_working = false;
+ }
+ return 0;
+diff --git a/drivers/gpu/drm/radeon/r100_track.h b/drivers/gpu/drm/radeon/r100_track.h
+index 0daf0d7..b27a699 100644
+--- a/drivers/gpu/drm/radeon/r100_track.h
++++ b/drivers/gpu/drm/radeon/r100_track.h
+@@ -10,26 +10,30 @@
+ * CS functions
+ */
+ struct r100_cs_track_cb {
+- struct radeon_object *robj;
++ struct radeon_bo *robj;
+ unsigned pitch;
+ unsigned cpp;
+ unsigned offset;
+ };
+
+ struct r100_cs_track_array {
+- struct radeon_object *robj;
++ struct radeon_bo *robj;
+ unsigned esize;
+ };
+
+ struct r100_cs_cube_info {
+- struct radeon_object *robj;
+- unsigned offset;
++ struct radeon_bo *robj;
++ unsigned offset;
+ unsigned width;
+ unsigned height;
+ };
+
++#define R100_TRACK_COMP_NONE 0
++#define R100_TRACK_COMP_DXT1 1
++#define R100_TRACK_COMP_DXT35 2
++
+ struct r100_cs_track_texture {
+- struct radeon_object *robj;
++ struct radeon_bo *robj;
+ struct r100_cs_cube_info cube_info[5]; /* info for 5 non-primary faces */
+ unsigned pitch;
+ unsigned width;
+@@ -44,6 +48,7 @@ struct r100_cs_track_texture {
+ bool enabled;
+ bool roundup_w;
+ bool roundup_h;
++ unsigned compress_format;
+ };
+
+ struct r100_cs_track_limits {
+@@ -62,13 +67,15 @@ struct r100_cs_track {
+ unsigned immd_dwords;
+ unsigned num_arrays;
+ unsigned max_indx;
++ unsigned color_channel_mask;
+ struct r100_cs_track_array arrays[11];
+ struct r100_cs_track_cb cb[R300_MAX_CB];
+ struct r100_cs_track_cb zb;
+ struct r100_cs_track_texture textures[R300_TRACK_MAX_TEXTURE];
+ bool z_enabled;
+ bool separate_cube;
+-
++ bool fastfill;
++ bool blend_read_enable;
+ };
+
+ int r100_cs_track_check(struct radeon_device *rdev, struct r100_cs_track *track);
+diff --git a/drivers/gpu/drm/radeon/r200.c b/drivers/gpu/drm/radeon/r200.c
+index eb740fc..ff1e0cd 100644
+--- a/drivers/gpu/drm/radeon/r200.c
++++ b/drivers/gpu/drm/radeon/r200.c
+@@ -371,13 +371,16 @@ int r200_packet0_check(struct radeon_cs_parser *p,
+ case 5:
+ case 6:
+ case 7:
++ /* 1D/2D */
+ track->textures[i].tex_coord_type = 0;
+ break;
+ case 1:
+- track->textures[i].tex_coord_type = 1;
++ /* CUBE */
++ track->textures[i].tex_coord_type = 2;
+ break;
+ case 2:
+- track->textures[i].tex_coord_type = 2;
++ /* 3D */
++ track->textures[i].tex_coord_type = 1;
+ break;
+ }
+ break;
+@@ -401,7 +404,6 @@ int r200_packet0_check(struct radeon_cs_parser *p,
+ case R200_TXFORMAT_Y8:
+ track->textures[i].cpp = 1;
+ break;
+- case R200_TXFORMAT_DXT1:
+ case R200_TXFORMAT_AI88:
+ case R200_TXFORMAT_ARGB1555:
+ case R200_TXFORMAT_RGB565:
+@@ -418,9 +420,16 @@ int r200_packet0_check(struct radeon_cs_parser *p,
+ case R200_TXFORMAT_ABGR8888:
+ case R200_TXFORMAT_BGR111110:
+ case R200_TXFORMAT_LDVDU8888:
++ track->textures[i].cpp = 4;
++ break;
++ case R200_TXFORMAT_DXT1:
++ track->textures[i].cpp = 1;
++ track->textures[i].compress_format = R100_TRACK_COMP_DXT1;
++ break;
+ case R200_TXFORMAT_DXT23:
+ case R200_TXFORMAT_DXT45:
+- track->textures[i].cpp = 4;
++ track->textures[i].cpp = 1;
++ track->textures[i].compress_format = R100_TRACK_COMP_DXT1;
+ break;
+ }
+ track->textures[i].cube_info[4].width = 1 << ((idx_value >> 16) & 0xf);
+diff --git a/drivers/gpu/drm/radeon/r300.c b/drivers/gpu/drm/radeon/r300.c
+index 2f43ee8..43b55a0 100644
+--- a/drivers/gpu/drm/radeon/r300.c
++++ b/drivers/gpu/drm/radeon/r300.c
+@@ -36,7 +36,15 @@
+ #include "rv350d.h"
+ #include "r300_reg_safe.h"
+
+-/* This files gather functions specifics to: r300,r350,rv350,rv370,rv380 */
++/* This files gather functions specifics to: r300,r350,rv350,rv370,rv380
++ *
++ * GPU Errata:
++ * - HOST_PATH_CNTL: r300 family seems to dislike write to HOST_PATH_CNTL
++ * using MMIO to flush host path read cache, this lead to HARDLOCKUP.
++ * However, scheduling such write to the ring seems harmless, i suspect
++ * the CP read collide with the flush somehow, or maybe the MC, hard to
++ * tell. (Jerome Glisse)
++ */
+
+ /*
+ * rv370,rv380 PCIE GART
+@@ -137,14 +145,19 @@ int rv370_pcie_gart_enable(struct radeon_device *rdev)
+
+ void rv370_pcie_gart_disable(struct radeon_device *rdev)
+ {
+- uint32_t tmp;
++ u32 tmp;
++ int r;
+
+ tmp = RREG32_PCIE(RADEON_PCIE_TX_GART_CNTL);
+ tmp |= RADEON_PCIE_TX_GART_UNMAPPED_ACCESS_DISCARD;
+ WREG32_PCIE(RADEON_PCIE_TX_GART_CNTL, tmp & ~RADEON_PCIE_TX_GART_EN);
+ if (rdev->gart.table.vram.robj) {
+- radeon_object_kunmap(rdev->gart.table.vram.robj);
+- radeon_object_unpin(rdev->gart.table.vram.robj);
++ r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
++ if (likely(r == 0)) {
++ radeon_bo_kunmap(rdev->gart.table.vram.robj);
++ radeon_bo_unpin(rdev->gart.table.vram.robj);
++ radeon_bo_unreserve(rdev->gart.table.vram.robj);
++ }
+ }
+ }
+
+@@ -173,6 +186,11 @@ void r300_fence_ring_emit(struct radeon_device *rdev,
+ /* Wait until IDLE & CLEAN */
+ radeon_ring_write(rdev, PACKET0(0x1720, 0));
+ radeon_ring_write(rdev, (1 << 17) | (1 << 16) | (1 << 9));
++ radeon_ring_write(rdev, PACKET0(RADEON_HOST_PATH_CNTL, 0));
++ radeon_ring_write(rdev, rdev->config.r300.hdp_cntl |
++ RADEON_HDP_READ_BUFFER_INVALIDATE);
++ radeon_ring_write(rdev, PACKET0(RADEON_HOST_PATH_CNTL, 0));
++ radeon_ring_write(rdev, rdev->config.r300.hdp_cntl);
+ /* Emit fence sequence & fire IRQ */
+ radeon_ring_write(rdev, PACKET0(rdev->fence_drv.scratch_reg, 0));
+ radeon_ring_write(rdev, fence->seq);
+@@ -488,11 +506,14 @@ void r300_vram_info(struct radeon_device *rdev)
+
+ /* DDR for all card after R300 & IGP */
+ rdev->mc.vram_is_ddr = true;
++
+ tmp = RREG32(RADEON_MEM_CNTL);
+- if (tmp & R300_MEM_NUM_CHANNELS_MASK) {
+- rdev->mc.vram_width = 128;
+- } else {
+- rdev->mc.vram_width = 64;
++ tmp &= R300_MEM_NUM_CHANNELS_MASK;
++ switch (tmp) {
++ case 0: rdev->mc.vram_width = 64; break;
++ case 1: rdev->mc.vram_width = 128; break;
++ case 2: rdev->mc.vram_width = 256; break;
++ default: rdev->mc.vram_width = 128; break;
+ }
+
+ r100_vram_init_sizes(rdev);
+@@ -681,7 +702,15 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
+ r100_cs_dump_packet(p, pkt);
+ return r;
+ }
+- ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
++
++ if (reloc->lobj.tiling_flags & RADEON_TILING_MACRO)
++ tile_flags |= R300_TXO_MACRO_TILE;
++ if (reloc->lobj.tiling_flags & RADEON_TILING_MICRO)
++ tile_flags |= R300_TXO_MICRO_TILE;
++
++ tmp = idx_value + ((u32)reloc->lobj.gpu_offset);
++ tmp |= tile_flags;
++ ib[idx] = tmp;
+ track->textures[i].robj = reloc->robj;
+ break;
+ /* Tracked registers */
+@@ -847,7 +876,6 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
+ case R300_TX_FORMAT_Z6Y5X5:
+ case R300_TX_FORMAT_W4Z4Y4X4:
+ case R300_TX_FORMAT_W1Z5Y5X5:
+- case R300_TX_FORMAT_DXT1:
+ case R300_TX_FORMAT_D3DMFT_CxV8U8:
+ case R300_TX_FORMAT_B8G8_B8G8:
+ case R300_TX_FORMAT_G8R8_G8B8:
+@@ -861,8 +889,6 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
+ case 0x17:
+ case R300_TX_FORMAT_FL_I32:
+ case 0x1e:
+- case R300_TX_FORMAT_DXT3:
+- case R300_TX_FORMAT_DXT5:
+ track->textures[i].cpp = 4;
+ break;
+ case R300_TX_FORMAT_W16Z16Y16X16:
+@@ -873,6 +899,23 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
+ case R300_TX_FORMAT_FL_R32G32B32A32:
+ track->textures[i].cpp = 16;
+ break;
++ case R300_TX_FORMAT_DXT1:
++ track->textures[i].cpp = 1;
++ track->textures[i].compress_format = R100_TRACK_COMP_DXT1;
++ break;
++ case R300_TX_FORMAT_ATI2N:
++ if (p->rdev->family < CHIP_R420) {
++ DRM_ERROR("Invalid texture format %u\n",
++ (idx_value & 0x1F));
++ return -EINVAL;
++ }
++ /* The same rules apply as for DXT3/5. */
++ /* Pass through. */
++ case R300_TX_FORMAT_DXT3:
++ case R300_TX_FORMAT_DXT5:
++ track->textures[i].cpp = 1;
++ track->textures[i].compress_format = R100_TRACK_COMP_DXT35;
++ break;
+ default:
+ DRM_ERROR("Invalid texture format %u\n",
+ (idx_value & 0x1F));
+@@ -932,6 +975,16 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
+ track->textures[i].width_11 = tmp;
+ tmp = ((idx_value >> 16) & 1) << 11;
+ track->textures[i].height_11 = tmp;
++
++ /* ATI1N */
++ if (idx_value & (1 << 14)) {
++ /* The same rules apply as for DXT1. */
++ track->textures[i].compress_format =
++ R100_TRACK_COMP_DXT1;
++ }
++ } else if (idx_value & (1 << 14)) {
++ DRM_ERROR("Forbidden bit TXFORMAT_MSB\n");
++ return -EINVAL;
+ }
+ break;
+ case 0x4480:
+@@ -973,6 +1026,18 @@ static int r300_packet0_check(struct radeon_cs_parser *p,
+ }
+ ib[idx] = idx_value + ((u32)reloc->lobj.gpu_offset);
+ break;
++ case 0x4e0c:
++ /* RB3D_COLOR_CHANNEL_MASK */
++ track->color_channel_mask = idx_value;
++ break;
++ case 0x4d1c:
++ /* ZB_BW_CNTL */
++ track->fastfill = !!(idx_value & (1 << 2));
++ break;
++ case 0x4e04:
++ /* RB3D_BLENDCNTL */
++ track->blend_read_enable = !!(idx_value & (1 << 2));
++ break;
+ case 0x4be8:
+ /* valid register only on RV530 */
+ if (p->rdev->family == CHIP_RV530)
+@@ -1181,6 +1246,9 @@ static int r300_startup(struct radeon_device *rdev)
+ {
+ int r;
+
++ /* set common regs */
++ r100_set_common_regs(rdev);
++ /* program mc */
+ r300_mc_program(rdev);
+ /* Resume clock */
+ r300_clock_startup(rdev);
+@@ -1193,14 +1261,20 @@ static int r300_startup(struct radeon_device *rdev)
+ if (r)
+ return r;
+ }
++
++ if (rdev->family == CHIP_R300 ||
++ rdev->family == CHIP_R350 ||
++ rdev->family == CHIP_RV350)
++ r100_enable_bm(rdev);
++
+ if (rdev->flags & RADEON_IS_PCI) {
+ r = r100_pci_gart_enable(rdev);
+ if (r)
+ return r;
+ }
+ /* Enable IRQ */
+- rdev->irq.sw_int = true;
+ r100_irq_set(rdev);
++ rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
+ /* 1M ring buffer */
+ r = r100_cp_init(rdev, 1024 * 1024);
+ if (r) {
+@@ -1237,6 +1311,8 @@ int r300_resume(struct radeon_device *rdev)
+ radeon_combios_asic_init(rdev->ddev);
+ /* Resume clock after posting */
+ r300_clock_startup(rdev);
++ /* Initialize surface registers */
++ radeon_surface_init(rdev);
+ return r300_startup(rdev);
+ }
+
+@@ -1254,7 +1330,6 @@ int r300_suspend(struct radeon_device *rdev)
+
+ void r300_fini(struct radeon_device *rdev)
+ {
+- r300_suspend(rdev);
+ r100_cp_fini(rdev);
+ r100_wb_fini(rdev);
+ r100_ib_fini(rdev);
+@@ -1263,9 +1338,10 @@ void r300_fini(struct radeon_device *rdev)
+ rv370_pcie_gart_fini(rdev);
+ if (rdev->flags & RADEON_IS_PCI)
+ r100_pci_gart_fini(rdev);
++ radeon_agp_fini(rdev);
+ radeon_irq_kms_fini(rdev);
+ radeon_fence_driver_fini(rdev);
+- radeon_object_fini(rdev);
++ radeon_bo_fini(rdev);
+ radeon_atombios_fini(rdev);
+ kfree(rdev->bios);
+ rdev->bios = NULL;
+@@ -1303,14 +1379,14 @@ int r300_init(struct radeon_device *rdev)
+ RREG32(R_0007C0_CP_STAT));
+ }
+ /* check if cards are posted or not */
+- if (!radeon_card_posted(rdev) && rdev->bios) {
+- DRM_INFO("GPU not posted. posting now...\n");
+- radeon_combios_asic_init(rdev->ddev);
+- }
++ if (radeon_boot_test_post_card(rdev) == false)
++ return -EINVAL;
+ /* Set asic errata */
+ r300_errata(rdev);
+ /* Initialize clocks */
+ radeon_get_clock_info(rdev->ddev);
++ /* Initialize power management */
++ radeon_pm_init(rdev);
+ /* Get vram informations */
+ r300_vram_info(rdev);
+ /* Initialize memory controller (also test AGP) */
+@@ -1325,7 +1401,7 @@ int r300_init(struct radeon_device *rdev)
+ if (r)
+ return r;
+ /* Memory manager */
+- r = radeon_object_init(rdev);
++ r = radeon_bo_init(rdev);
+ if (r)
+ return r;
+ if (rdev->flags & RADEON_IS_PCIE) {
+@@ -1344,15 +1420,15 @@ int r300_init(struct radeon_device *rdev)
+ if (r) {
+ /* Somethings want wront with the accel init stop accel */
+ dev_err(rdev->dev, "Disabling GPU acceleration\n");
+- r300_suspend(rdev);
+ r100_cp_fini(rdev);
+ r100_wb_fini(rdev);
+ r100_ib_fini(rdev);
++ radeon_irq_kms_fini(rdev);
+ if (rdev->flags & RADEON_IS_PCIE)
+ rv370_pcie_gart_fini(rdev);
+ if (rdev->flags & RADEON_IS_PCI)
+ r100_pci_gart_fini(rdev);
+- radeon_irq_kms_fini(rdev);
++ radeon_agp_fini(rdev);
+ rdev->accel_working = false;
+ }
+ return 0;
+diff --git a/drivers/gpu/drm/radeon/r300_cmdbuf.c b/drivers/gpu/drm/radeon/r300_cmdbuf.c
+index cb2e470..34bffa0 100644
+--- a/drivers/gpu/drm/radeon/r300_cmdbuf.c
++++ b/drivers/gpu/drm/radeon/r300_cmdbuf.c
+@@ -990,7 +990,7 @@ static inline int r300_emit_r500fp(drm_radeon_private_t *dev_priv,
+ int sz;
+ int addr;
+ int type;
+- int clamp;
++ int isclamp;
+ int stride;
+ RING_LOCALS;
+
+@@ -999,10 +999,10 @@ static inline int r300_emit_r500fp(drm_radeon_private_t *dev_priv,
+ addr = ((header.r500fp.adrhi_flags & 1) << 8) | header.r500fp.adrlo;
+
+ type = !!(header.r500fp.adrhi_flags & R500FP_CONSTANT_TYPE);
+- clamp = !!(header.r500fp.adrhi_flags & R500FP_CONSTANT_CLAMP);
++ isclamp = !!(header.r500fp.adrhi_flags & R500FP_CONSTANT_CLAMP);
+
+ addr |= (type << 16);
+- addr |= (clamp << 17);
++ addr |= (isclamp << 17);
+
+ stride = type ? 4 : 6;
+
+diff --git a/drivers/gpu/drm/radeon/r300_reg.h b/drivers/gpu/drm/radeon/r300_reg.h
+index 4b7afef..1735a2b 100644
+--- a/drivers/gpu/drm/radeon/r300_reg.h
++++ b/drivers/gpu/drm/radeon/r300_reg.h
+@@ -900,6 +900,7 @@
+ # define R300_TX_FORMAT_FL_I32 0x1B
+ # define R300_TX_FORMAT_FL_I32A32 0x1C
+ # define R300_TX_FORMAT_FL_R32G32B32A32 0x1D
++# define R300_TX_FORMAT_ATI2N 0x1F
+ /* alpha modes, convenience mostly */
+ /* if you have alpha, pick constant appropriate to the
+ number of channels (1 for I8, 2 for I8A8, 4 for R8G8B8A8, etc */
+diff --git a/drivers/gpu/drm/radeon/r420.c b/drivers/gpu/drm/radeon/r420.c
+index 1cefdbc..d937324 100644
+--- a/drivers/gpu/drm/radeon/r420.c
++++ b/drivers/gpu/drm/radeon/r420.c
+@@ -30,7 +30,15 @@
+ #include "radeon_reg.h"
+ #include "radeon.h"
+ #include "atom.h"
++#include "r100d.h"
+ #include "r420d.h"
++#include "r420_reg_safe.h"
++
++static void r420_set_reg_safe(struct radeon_device *rdev)
++{
++ rdev->config.r300.reg_safe_bm = r420_reg_safe_bm;
++ rdev->config.r300.reg_safe_bm_size = ARRAY_SIZE(r420_reg_safe_bm);
++}
+
+ int r420_mc_init(struct radeon_device *rdev)
+ {
+@@ -42,9 +50,7 @@ int r420_mc_init(struct radeon_device *rdev)
+ if (rdev->flags & RADEON_IS_AGP) {
+ r = radeon_agp_init(rdev);
+ if (r) {
+- printk(KERN_WARNING "[drm] Disabling AGP\n");
+- rdev->flags &= ~RADEON_IS_AGP;
+- rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024;
++ radeon_agp_disable(rdev);
+ } else {
+ rdev->mc.gtt_location = rdev->mc.agp_base;
+ }
+@@ -165,10 +171,41 @@ static void r420_clock_resume(struct radeon_device *rdev)
+ WREG32_PLL(R_00000D_SCLK_CNTL, sclk_cntl);
+ }
+
++static void r420_cp_errata_init(struct radeon_device *rdev)
++{
++ /* RV410 and R420 can lock up if CP DMA to host memory happens
++ * while the 2D engine is busy.
++ *
++ * The proper workaround is to queue a RESYNC at the beginning
++ * of the CP init, apparently.
++ */
++ radeon_scratch_get(rdev, &rdev->config.r300.resync_scratch);
++ radeon_ring_lock(rdev, 8);
++ radeon_ring_write(rdev, PACKET0(R300_CP_RESYNC_ADDR, 1));
++ radeon_ring_write(rdev, rdev->config.r300.resync_scratch);
++ radeon_ring_write(rdev, 0xDEADBEEF);
++ radeon_ring_unlock_commit(rdev);
++}
++
++static void r420_cp_errata_fini(struct radeon_device *rdev)
++{
++ /* Catch the RESYNC we dispatched all the way back,
++ * at the very beginning of the CP init.
++ */
++ radeon_ring_lock(rdev, 8);
++ radeon_ring_write(rdev, PACKET0(R300_RB3D_DSTCACHE_CTLSTAT, 0));
++ radeon_ring_write(rdev, R300_RB3D_DC_FINISH);
++ radeon_ring_unlock_commit(rdev);
++ radeon_scratch_free(rdev, rdev->config.r300.resync_scratch);
++}
++
+ static int r420_startup(struct radeon_device *rdev)
+ {
+ int r;
+
++ /* set common regs */
++ r100_set_common_regs(rdev);
++ /* program mc */
+ r300_mc_program(rdev);
+ /* Resume clock */
+ r420_clock_resume(rdev);
+@@ -186,14 +223,15 @@ static int r420_startup(struct radeon_device *rdev)
+ }
+ r420_pipes_init(rdev);
+ /* Enable IRQ */
+- rdev->irq.sw_int = true;
+ r100_irq_set(rdev);
++ rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
+ /* 1M ring buffer */
+ r = r100_cp_init(rdev, 1024 * 1024);
+ if (r) {
+ dev_err(rdev->dev, "failled initializing CP (%d).\n", r);
+ return r;
+ }
++ r420_cp_errata_init(rdev);
+ r = r100_wb_init(rdev);
+ if (r) {
+ dev_err(rdev->dev, "failled initializing WB (%d).\n", r);
+@@ -229,12 +267,14 @@ int r420_resume(struct radeon_device *rdev)
+ }
+ /* Resume clock after posting */
+ r420_clock_resume(rdev);
+-
++ /* Initialize surface registers */
++ radeon_surface_init(rdev);
+ return r420_startup(rdev);
+ }
+
+ int r420_suspend(struct radeon_device *rdev)
+ {
++ r420_cp_errata_fini(rdev);
+ r100_cp_disable(rdev);
+ r100_wb_disable(rdev);
+ r100_irq_disable(rdev);
+@@ -258,7 +298,7 @@ void r420_fini(struct radeon_device *rdev)
+ radeon_agp_fini(rdev);
+ radeon_irq_kms_fini(rdev);
+ radeon_fence_driver_fini(rdev);
+- radeon_object_fini(rdev);
++ radeon_bo_fini(rdev);
+ if (rdev->is_atom_bios) {
+ radeon_atombios_fini(rdev);
+ } else {
+@@ -301,14 +341,9 @@ int r420_init(struct radeon_device *rdev)
+ RREG32(R_0007C0_CP_STAT));
+ }
+ /* check if cards are posted or not */
+- if (!radeon_card_posted(rdev) && rdev->bios) {
+- DRM_INFO("GPU not posted. posting now...\n");
+- if (rdev->is_atom_bios) {
+- atom_asic_init(rdev->mode_info.atom_context);
+- } else {
+- radeon_combios_asic_init(rdev->ddev);
+- }
+- }
++ if (radeon_boot_test_post_card(rdev) == false)
++ return -EINVAL;
++
+ /* Initialize clocks */
+ radeon_get_clock_info(rdev->ddev);
+ /* Initialize power management */
+@@ -331,10 +366,13 @@ int r420_init(struct radeon_device *rdev)
+ return r;
+ }
+ /* Memory manager */
+- r = radeon_object_init(rdev);
++ r = radeon_bo_init(rdev);
+ if (r) {
+ return r;
+ }
++ if (rdev->family == CHIP_R420)
++ r100_enable_bm(rdev);
++
+ if (rdev->flags & RADEON_IS_PCIE) {
+ r = rv370_pcie_gart_init(rdev);
+ if (r)
+@@ -345,22 +383,21 @@ int r420_init(struct radeon_device *rdev)
+ if (r)
+ return r;
+ }
+- r300_set_reg_safe(rdev);
++ r420_set_reg_safe(rdev);
+ rdev->accel_working = true;
+ r = r420_startup(rdev);
+ if (r) {
+ /* Somethings want wront with the accel init stop accel */
+ dev_err(rdev->dev, "Disabling GPU acceleration\n");
+- r420_suspend(rdev);
+ r100_cp_fini(rdev);
+ r100_wb_fini(rdev);
+ r100_ib_fini(rdev);
++ radeon_irq_kms_fini(rdev);
+ if (rdev->flags & RADEON_IS_PCIE)
+ rv370_pcie_gart_fini(rdev);
+ if (rdev->flags & RADEON_IS_PCI)
+ r100_pci_gart_fini(rdev);
+ radeon_agp_fini(rdev);
+- radeon_irq_kms_fini(rdev);
+ rdev->accel_working = false;
+ }
+ return 0;
+diff --git a/drivers/gpu/drm/radeon/r500_reg.h b/drivers/gpu/drm/radeon/r500_reg.h
+index 7baa739..74ad89b 100644
+--- a/drivers/gpu/drm/radeon/r500_reg.h
++++ b/drivers/gpu/drm/radeon/r500_reg.h
+@@ -716,6 +716,8 @@
+
+ #define AVIVO_DVOA_BIT_DEPTH_CONTROL 0x7988
+
++#define AVIVO_DC_GPIO_HPD_A 0x7e94
++
+ #define AVIVO_GPIO_0 0x7e30
+ #define AVIVO_GPIO_1 0x7e40
+ #define AVIVO_GPIO_2 0x7e50
+diff --git a/drivers/gpu/drm/radeon/r520.c b/drivers/gpu/drm/radeon/r520.c
+index f743518..ddf5731 100644
+--- a/drivers/gpu/drm/radeon/r520.c
++++ b/drivers/gpu/drm/radeon/r520.c
+@@ -185,8 +185,8 @@ static int r520_startup(struct radeon_device *rdev)
+ return r;
+ }
+ /* Enable IRQ */
+- rdev->irq.sw_int = true;
+ rs600_irq_set(rdev);
++ rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
+ /* 1M ring buffer */
+ r = r100_cp_init(rdev, 1024 * 1024);
+ if (r) {
+@@ -221,6 +221,8 @@ int r520_resume(struct radeon_device *rdev)
+ atom_asic_init(rdev->mode_info.atom_context);
+ /* Resume clock after posting */
+ rv515_clock_startup(rdev);
++ /* Initialize surface registers */
++ radeon_surface_init(rdev);
+ return r520_startup(rdev);
+ }
+
+@@ -254,6 +256,9 @@ int r520_init(struct radeon_device *rdev)
+ RREG32(R_0007C0_CP_STAT));
+ }
+ /* check if cards are posted or not */
++ if (radeon_boot_test_post_card(rdev) == false)
++ return -EINVAL;
++
+ if (!radeon_card_posted(rdev) && rdev->bios) {
+ DRM_INFO("GPU not posted. posting now...\n");
+ atom_asic_init(rdev->mode_info.atom_context);
+@@ -277,7 +282,7 @@ int r520_init(struct radeon_device *rdev)
+ if (r)
+ return r;
+ /* Memory manager */
+- r = radeon_object_init(rdev);
++ r = radeon_bo_init(rdev);
+ if (r)
+ return r;
+ r = rv370_pcie_gart_init(rdev);
+@@ -289,13 +294,12 @@ int r520_init(struct radeon_device *rdev)
+ if (r) {
+ /* Somethings want wront with the accel init stop accel */
+ dev_err(rdev->dev, "Disabling GPU acceleration\n");
+- rv515_suspend(rdev);
+ r100_cp_fini(rdev);
+ r100_wb_fini(rdev);
+ r100_ib_fini(rdev);
++ radeon_irq_kms_fini(rdev);
+ rv370_pcie_gart_fini(rdev);
+ radeon_agp_fini(rdev);
+- radeon_irq_kms_fini(rdev);
+ rdev->accel_working = false;
+ }
+ return 0;
+diff --git a/drivers/gpu/drm/radeon/r600.c b/drivers/gpu/drm/radeon/r600.c
+index 278f646..91f5af9 100644
+--- a/drivers/gpu/drm/radeon/r600.c
++++ b/drivers/gpu/drm/radeon/r600.c
+@@ -38,8 +38,10 @@
+
+ #define PFP_UCODE_SIZE 576
+ #define PM4_UCODE_SIZE 1792
++#define RLC_UCODE_SIZE 768
+ #define R700_PFP_UCODE_SIZE 848
+ #define R700_PM4_UCODE_SIZE 1360
++#define R700_RLC_UCODE_SIZE 1024
+
+ /* Firmware Names */
+ MODULE_FIRMWARE("radeon/R600_pfp.bin");
+@@ -62,6 +64,8 @@ MODULE_FIRMWARE("radeon/RV730_pfp.bin");
+ MODULE_FIRMWARE("radeon/RV730_me.bin");
+ MODULE_FIRMWARE("radeon/RV710_pfp.bin");
+ MODULE_FIRMWARE("radeon/RV710_me.bin");
++MODULE_FIRMWARE("radeon/R600_rlc.bin");
++MODULE_FIRMWARE("radeon/R700_rlc.bin");
+
+ int r600_debugfs_mc_info_init(struct radeon_device *rdev);
+
+@@ -70,6 +74,282 @@ int r600_mc_wait_for_idle(struct radeon_device *rdev);
+ void r600_gpu_init(struct radeon_device *rdev);
+ void r600_fini(struct radeon_device *rdev);
+
++/* hpd for digital panel detect/disconnect */
++bool r600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd)
++{
++ bool connected = false;
++
++ if (ASIC_IS_DCE3(rdev)) {
++ switch (hpd) {
++ case RADEON_HPD_1:
++ if (RREG32(DC_HPD1_INT_STATUS) & DC_HPDx_SENSE)
++ connected = true;
++ break;
++ case RADEON_HPD_2:
++ if (RREG32(DC_HPD2_INT_STATUS) & DC_HPDx_SENSE)
++ connected = true;
++ break;
++ case RADEON_HPD_3:
++ if (RREG32(DC_HPD3_INT_STATUS) & DC_HPDx_SENSE)
++ connected = true;
++ break;
++ case RADEON_HPD_4:
++ if (RREG32(DC_HPD4_INT_STATUS) & DC_HPDx_SENSE)
++ connected = true;
++ break;
++ /* DCE 3.2 */
++ case RADEON_HPD_5:
++ if (RREG32(DC_HPD5_INT_STATUS) & DC_HPDx_SENSE)
++ connected = true;
++ break;
++ case RADEON_HPD_6:
++ if (RREG32(DC_HPD6_INT_STATUS) & DC_HPDx_SENSE)
++ connected = true;
++ break;
++ default:
++ break;
++ }
++ } else {
++ switch (hpd) {
++ case RADEON_HPD_1:
++ if (RREG32(DC_HOT_PLUG_DETECT1_INT_STATUS) & DC_HOT_PLUG_DETECTx_SENSE)
++ connected = true;
++ break;
++ case RADEON_HPD_2:
++ if (RREG32(DC_HOT_PLUG_DETECT2_INT_STATUS) & DC_HOT_PLUG_DETECTx_SENSE)
++ connected = true;
++ break;
++ case RADEON_HPD_3:
++ if (RREG32(DC_HOT_PLUG_DETECT3_INT_STATUS) & DC_HOT_PLUG_DETECTx_SENSE)
++ connected = true;
++ break;
++ default:
++ break;
++ }
++ }
++ return connected;
++}
++
++void r600_hpd_set_polarity(struct radeon_device *rdev,
++ enum radeon_hpd_id hpd)
++{
++ u32 tmp;
++ bool connected = r600_hpd_sense(rdev, hpd);
++
++ if (ASIC_IS_DCE3(rdev)) {
++ switch (hpd) {
++ case RADEON_HPD_1:
++ tmp = RREG32(DC_HPD1_INT_CONTROL);
++ if (connected)
++ tmp &= ~DC_HPDx_INT_POLARITY;
++ else
++ tmp |= DC_HPDx_INT_POLARITY;
++ WREG32(DC_HPD1_INT_CONTROL, tmp);
++ break;
++ case RADEON_HPD_2:
++ tmp = RREG32(DC_HPD2_INT_CONTROL);
++ if (connected)
++ tmp &= ~DC_HPDx_INT_POLARITY;
++ else
++ tmp |= DC_HPDx_INT_POLARITY;
++ WREG32(DC_HPD2_INT_CONTROL, tmp);
++ break;
++ case RADEON_HPD_3:
++ tmp = RREG32(DC_HPD3_INT_CONTROL);
++ if (connected)
++ tmp &= ~DC_HPDx_INT_POLARITY;
++ else
++ tmp |= DC_HPDx_INT_POLARITY;
++ WREG32(DC_HPD3_INT_CONTROL, tmp);
++ break;
++ case RADEON_HPD_4:
++ tmp = RREG32(DC_HPD4_INT_CONTROL);
++ if (connected)
++ tmp &= ~DC_HPDx_INT_POLARITY;
++ else
++ tmp |= DC_HPDx_INT_POLARITY;
++ WREG32(DC_HPD4_INT_CONTROL, tmp);
++ break;
++ case RADEON_HPD_5:
++ tmp = RREG32(DC_HPD5_INT_CONTROL);
++ if (connected)
++ tmp &= ~DC_HPDx_INT_POLARITY;
++ else
++ tmp |= DC_HPDx_INT_POLARITY;
++ WREG32(DC_HPD5_INT_CONTROL, tmp);
++ break;
++ /* DCE 3.2 */
++ case RADEON_HPD_6:
++ tmp = RREG32(DC_HPD6_INT_CONTROL);
++ if (connected)
++ tmp &= ~DC_HPDx_INT_POLARITY;
++ else
++ tmp |= DC_HPDx_INT_POLARITY;
++ WREG32(DC_HPD6_INT_CONTROL, tmp);
++ break;
++ default:
++ break;
++ }
++ } else {
++ switch (hpd) {
++ case RADEON_HPD_1:
++ tmp = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL);
++ if (connected)
++ tmp &= ~DC_HOT_PLUG_DETECTx_INT_POLARITY;
++ else
++ tmp |= DC_HOT_PLUG_DETECTx_INT_POLARITY;
++ WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp);
++ break;
++ case RADEON_HPD_2:
++ tmp = RREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL);
++ if (connected)
++ tmp &= ~DC_HOT_PLUG_DETECTx_INT_POLARITY;
++ else
++ tmp |= DC_HOT_PLUG_DETECTx_INT_POLARITY;
++ WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp);
++ break;
++ case RADEON_HPD_3:
++ tmp = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL);
++ if (connected)
++ tmp &= ~DC_HOT_PLUG_DETECTx_INT_POLARITY;
++ else
++ tmp |= DC_HOT_PLUG_DETECTx_INT_POLARITY;
++ WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, tmp);
++ break;
++ default:
++ break;
++ }
++ }
++}
++
++void r600_hpd_init(struct radeon_device *rdev)
++{
++ struct drm_device *dev = rdev->ddev;
++ struct drm_connector *connector;
++
++ if (ASIC_IS_DCE3(rdev)) {
++ u32 tmp = DC_HPDx_CONNECTION_TIMER(0x9c4) | DC_HPDx_RX_INT_TIMER(0xfa);
++ if (ASIC_IS_DCE32(rdev))
++ tmp |= DC_HPDx_EN;
++
++ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
++ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
++ switch (radeon_connector->hpd.hpd) {
++ case RADEON_HPD_1:
++ WREG32(DC_HPD1_CONTROL, tmp);
++ rdev->irq.hpd[0] = true;
++ break;
++ case RADEON_HPD_2:
++ WREG32(DC_HPD2_CONTROL, tmp);
++ rdev->irq.hpd[1] = true;
++ break;
++ case RADEON_HPD_3:
++ WREG32(DC_HPD3_CONTROL, tmp);
++ rdev->irq.hpd[2] = true;
++ break;
++ case RADEON_HPD_4:
++ WREG32(DC_HPD4_CONTROL, tmp);
++ rdev->irq.hpd[3] = true;
++ break;
++ /* DCE 3.2 */
++ case RADEON_HPD_5:
++ WREG32(DC_HPD5_CONTROL, tmp);
++ rdev->irq.hpd[4] = true;
++ break;
++ case RADEON_HPD_6:
++ WREG32(DC_HPD6_CONTROL, tmp);
++ rdev->irq.hpd[5] = true;
++ break;
++ default:
++ break;
++ }
++ }
++ } else {
++ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
++ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
++ switch (radeon_connector->hpd.hpd) {
++ case RADEON_HPD_1:
++ WREG32(DC_HOT_PLUG_DETECT1_CONTROL, DC_HOT_PLUG_DETECTx_EN);
++ rdev->irq.hpd[0] = true;
++ break;
++ case RADEON_HPD_2:
++ WREG32(DC_HOT_PLUG_DETECT2_CONTROL, DC_HOT_PLUG_DETECTx_EN);
++ rdev->irq.hpd[1] = true;
++ break;
++ case RADEON_HPD_3:
++ WREG32(DC_HOT_PLUG_DETECT3_CONTROL, DC_HOT_PLUG_DETECTx_EN);
++ rdev->irq.hpd[2] = true;
++ break;
++ default:
++ break;
++ }
++ }
++ }
++ if (rdev->irq.installed)
++ r600_irq_set(rdev);
++}
++
++void r600_hpd_fini(struct radeon_device *rdev)
++{
++ struct drm_device *dev = rdev->ddev;
++ struct drm_connector *connector;
++
++ if (ASIC_IS_DCE3(rdev)) {
++ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
++ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
++ switch (radeon_connector->hpd.hpd) {
++ case RADEON_HPD_1:
++ WREG32(DC_HPD1_CONTROL, 0);
++ rdev->irq.hpd[0] = false;
++ break;
++ case RADEON_HPD_2:
++ WREG32(DC_HPD2_CONTROL, 0);
++ rdev->irq.hpd[1] = false;
++ break;
++ case RADEON_HPD_3:
++ WREG32(DC_HPD3_CONTROL, 0);
++ rdev->irq.hpd[2] = false;
++ break;
++ case RADEON_HPD_4:
++ WREG32(DC_HPD4_CONTROL, 0);
++ rdev->irq.hpd[3] = false;
++ break;
++ /* DCE 3.2 */
++ case RADEON_HPD_5:
++ WREG32(DC_HPD5_CONTROL, 0);
++ rdev->irq.hpd[4] = false;
++ break;
++ case RADEON_HPD_6:
++ WREG32(DC_HPD6_CONTROL, 0);
++ rdev->irq.hpd[5] = false;
++ break;
++ default:
++ break;
++ }
++ }
++ } else {
++ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
++ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
++ switch (radeon_connector->hpd.hpd) {
++ case RADEON_HPD_1:
++ WREG32(DC_HOT_PLUG_DETECT1_CONTROL, 0);
++ rdev->irq.hpd[0] = false;
++ break;
++ case RADEON_HPD_2:
++ WREG32(DC_HOT_PLUG_DETECT2_CONTROL, 0);
++ rdev->irq.hpd[1] = false;
++ break;
++ case RADEON_HPD_3:
++ WREG32(DC_HOT_PLUG_DETECT3_CONTROL, 0);
++ rdev->irq.hpd[2] = false;
++ break;
++ default:
++ break;
++ }
++ }
++ }
++}
++
+ /*
+ * R600 PCIE GART
+ */
+@@ -90,6 +370,9 @@ void r600_pcie_gart_tlb_flush(struct radeon_device *rdev)
+ unsigned i;
+ u32 tmp;
+
++ /* flush hdp cache so updates hit vram */
++ WREG32(R_005480_HDP_MEM_COHERENCY_FLUSH_CNTL, 0x1);
++
+ WREG32(VM_CONTEXT0_INVALIDATION_LOW_ADDR, rdev->mc.gtt_start >> 12);
+ WREG32(VM_CONTEXT0_INVALIDATION_HIGH_ADDR, (rdev->mc.gtt_end - 1) >> 12);
+ WREG32(VM_CONTEXT0_REQUEST_RESPONSE, REQUEST_TYPE(1));
+@@ -180,7 +463,7 @@ int r600_pcie_gart_enable(struct radeon_device *rdev)
+ void r600_pcie_gart_disable(struct radeon_device *rdev)
+ {
+ u32 tmp;
+- int i;
++ int i, r;
+
+ /* Disable all tables */
+ for (i = 0; i < 7; i++)
+@@ -208,8 +491,12 @@ void r600_pcie_gart_disable(struct radeon_device *rdev)
+ WREG32(MC_VM_L1_TLB_MCB_RD_HDP_CNTL, tmp);
+ WREG32(MC_VM_L1_TLB_MCB_WR_HDP_CNTL, tmp);
+ if (rdev->gart.table.vram.robj) {
+- radeon_object_kunmap(rdev->gart.table.vram.robj);
+- radeon_object_unpin(rdev->gart.table.vram.robj);
++ r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
++ if (likely(r == 0)) {
++ radeon_bo_kunmap(rdev->gart.table.vram.robj);
++ radeon_bo_unpin(rdev->gart.table.vram.robj);
++ radeon_bo_unreserve(rdev->gart.table.vram.robj);
++ }
+ }
+ }
+
+@@ -340,7 +627,6 @@ int r600_mc_init(struct radeon_device *rdev)
+ fixed20_12 a;
+ u32 tmp;
+ int chansize, numchan;
+- int r;
+
+ /* Get VRAM informations */
+ rdev->mc.vram_is_ddr = true;
+@@ -383,9 +669,6 @@ int r600_mc_init(struct radeon_device *rdev)
+ rdev->mc.real_vram_size = rdev->mc.aper_size;
+
+ if (rdev->flags & RADEON_IS_AGP) {
+- r = radeon_agp_init(rdev);
+- if (r)
+- return r;
+ /* gtt_size is setup by radeon_agp_init */
+ rdev->mc.gtt_location = rdev->mc.agp_base;
+ tmp = 0xFFFFFFFFUL - rdev->mc.agp_base - rdev->mc.gtt_size;
+@@ -394,11 +677,11 @@ int r600_mc_init(struct radeon_device *rdev)
+ * AGP so that GPU can catch out of VRAM/AGP access
+ */
+ if (rdev->mc.gtt_location > rdev->mc.mc_vram_size) {
+- /* Enought place before */
++ /* Enough place before */
+ rdev->mc.vram_location = rdev->mc.gtt_location -
+ rdev->mc.mc_vram_size;
+ } else if (tmp > rdev->mc.mc_vram_size) {
+- /* Enought place after */
++ /* Enough place after */
+ rdev->mc.vram_location = rdev->mc.gtt_location +
+ rdev->mc.gtt_size;
+ } else {
+@@ -443,6 +726,10 @@ int r600_mc_init(struct radeon_device *rdev)
+ a.full = rfixed_const(100);
+ rdev->pm.sclk.full = rfixed_const(rdev->clock.default_sclk);
+ rdev->pm.sclk.full = rfixed_div(rdev->pm.sclk, a);
++
++ if (rdev->flags & RADEON_IS_IGP)
++ rdev->mc.igp_sideport_enabled = radeon_atombios_sideport_present(rdev);
++
+ return 0;
+ }
+
+@@ -1101,7 +1388,6 @@ void r600_pciep_wreg(struct radeon_device *rdev, u32 reg, u32 v)
+ (void)RREG32(PCIE_PORT_DATA);
+ }
+
+-
+ /*
+ * CP & Ring
+ */
+@@ -1110,11 +1396,12 @@ void r600_cp_stop(struct radeon_device *rdev)
+ WREG32(R_0086D8_CP_ME_CNTL, S_0086D8_CP_ME_HALT(1));
+ }
+
+-int r600_cp_init_microcode(struct radeon_device *rdev)
++int r600_init_microcode(struct radeon_device *rdev)
+ {
+ struct platform_device *pdev;
+ const char *chip_name;
+- size_t pfp_req_size, me_req_size;
++ const char *rlc_chip_name;
++ size_t pfp_req_size, me_req_size, rlc_req_size;
+ char fw_name[30];
+ int err;
+
+@@ -1128,30 +1415,62 @@ int r600_cp_init_microcode(struct radeon_device *rdev)
+ }
+
+ switch (rdev->family) {
+- case CHIP_R600: chip_name = "R600"; break;
+- case CHIP_RV610: chip_name = "RV610"; break;
+- case CHIP_RV630: chip_name = "RV630"; break;
+- case CHIP_RV620: chip_name = "RV620"; break;
+- case CHIP_RV635: chip_name = "RV635"; break;
+- case CHIP_RV670: chip_name = "RV670"; break;
++ case CHIP_R600:
++ chip_name = "R600";
++ rlc_chip_name = "R600";
++ break;
++ case CHIP_RV610:
++ chip_name = "RV610";
++ rlc_chip_name = "R600";
++ break;
++ case CHIP_RV630:
++ chip_name = "RV630";
++ rlc_chip_name = "R600";
++ break;
++ case CHIP_RV620:
++ chip_name = "RV620";
++ rlc_chip_name = "R600";
++ break;
++ case CHIP_RV635:
++ chip_name = "RV635";
++ rlc_chip_name = "R600";
++ break;
++ case CHIP_RV670:
++ chip_name = "RV670";
++ rlc_chip_name = "R600";
++ break;
+ case CHIP_RS780:
+- case CHIP_RS880: chip_name = "RS780"; break;
+- case CHIP_RV770: chip_name = "RV770"; break;
++ case CHIP_RS880:
++ chip_name = "RS780";
++ rlc_chip_name = "R600";
++ break;
++ case CHIP_RV770:
++ chip_name = "RV770";
++ rlc_chip_name = "R700";
++ break;
+ case CHIP_RV730:
+- case CHIP_RV740: chip_name = "RV730"; break;
+- case CHIP_RV710: chip_name = "RV710"; break;
++ case CHIP_RV740:
++ chip_name = "RV730";
++ rlc_chip_name = "R700";
++ break;
++ case CHIP_RV710:
++ chip_name = "RV710";
++ rlc_chip_name = "R700";
++ break;
+ default: BUG();
+ }
+
+ if (rdev->family >= CHIP_RV770) {
+ pfp_req_size = R700_PFP_UCODE_SIZE * 4;
+ me_req_size = R700_PM4_UCODE_SIZE * 4;
++ rlc_req_size = R700_RLC_UCODE_SIZE * 4;
+ } else {
+ pfp_req_size = PFP_UCODE_SIZE * 4;
+ me_req_size = PM4_UCODE_SIZE * 12;
++ rlc_req_size = RLC_UCODE_SIZE * 4;
+ }
+
+- DRM_INFO("Loading %s CP Microcode\n", chip_name);
++ DRM_INFO("Loading %s Microcode\n", chip_name);
+
+ snprintf(fw_name, sizeof(fw_name), "radeon/%s_pfp.bin", chip_name);
+ err = request_firmware(&rdev->pfp_fw, fw_name, &pdev->dev);
+@@ -1175,6 +1494,18 @@ int r600_cp_init_microcode(struct radeon_device *rdev)
+ rdev->me_fw->size, fw_name);
+ err = -EINVAL;
+ }
++
++ snprintf(fw_name, sizeof(fw_name), "radeon/%s_rlc.bin", rlc_chip_name);
++ err = request_firmware(&rdev->rlc_fw, fw_name, &pdev->dev);
++ if (err)
++ goto out;
++ if (rdev->rlc_fw->size != rlc_req_size) {
++ printk(KERN_ERR
++ "r600_rlc: Bogus length %zu in firmware \"%s\"\n",
++ rdev->rlc_fw->size, fw_name);
++ err = -EINVAL;
++ }
++
+ out:
+ platform_device_unregister(pdev);
+
+@@ -1187,6 +1518,8 @@ out:
+ rdev->pfp_fw = NULL;
+ release_firmware(rdev->me_fw);
+ rdev->me_fw = NULL;
++ release_firmware(rdev->rlc_fw);
++ rdev->rlc_fw = NULL;
+ }
+ return err;
+ }
+@@ -1324,6 +1657,12 @@ void r600_ring_init(struct radeon_device *rdev, unsigned ring_size)
+ rdev->cp.align_mask = 16 - 1;
+ }
+
++void r600_cp_fini(struct radeon_device *rdev)
++{
++ r600_cp_stop(rdev);
++ radeon_ring_fini(rdev);
++}
++
+
+ /*
+ * GPU scratch registers helpers function.
+@@ -1381,10 +1720,16 @@ int r600_ring_test(struct radeon_device *rdev)
+
+ void r600_wb_disable(struct radeon_device *rdev)
+ {
++ int r;
++
+ WREG32(SCRATCH_UMSK, 0);
+ if (rdev->wb.wb_obj) {
+- radeon_object_kunmap(rdev->wb.wb_obj);
+- radeon_object_unpin(rdev->wb.wb_obj);
++ r = radeon_bo_reserve(rdev->wb.wb_obj, false);
++ if (unlikely(r != 0))
++ return;
++ radeon_bo_kunmap(rdev->wb.wb_obj);
++ radeon_bo_unpin(rdev->wb.wb_obj);
++ radeon_bo_unreserve(rdev->wb.wb_obj);
+ }
+ }
+
+@@ -1392,7 +1737,7 @@ void r600_wb_fini(struct radeon_device *rdev)
+ {
+ r600_wb_disable(rdev);
+ if (rdev->wb.wb_obj) {
+- radeon_object_unref(&rdev->wb.wb_obj);
++ radeon_bo_unref(&rdev->wb.wb_obj);
+ rdev->wb.wb = NULL;
+ rdev->wb.wb_obj = NULL;
+ }
+@@ -1403,22 +1748,29 @@ int r600_wb_enable(struct radeon_device *rdev)
+ int r;
+
+ if (rdev->wb.wb_obj == NULL) {
+- r = radeon_object_create(rdev, NULL, RADEON_GPU_PAGE_SIZE, true,
+- RADEON_GEM_DOMAIN_GTT, false, &rdev->wb.wb_obj);
++ r = radeon_bo_create(rdev, NULL, RADEON_GPU_PAGE_SIZE, true,
++ RADEON_GEM_DOMAIN_GTT, &rdev->wb.wb_obj);
+ if (r) {
+- dev_warn(rdev->dev, "failed to create WB buffer (%d).\n", r);
++ dev_warn(rdev->dev, "(%d) create WB bo failed\n", r);
++ return r;
++ }
++ r = radeon_bo_reserve(rdev->wb.wb_obj, false);
++ if (unlikely(r != 0)) {
++ r600_wb_fini(rdev);
+ return r;
+ }
+- r = radeon_object_pin(rdev->wb.wb_obj, RADEON_GEM_DOMAIN_GTT,
++ r = radeon_bo_pin(rdev->wb.wb_obj, RADEON_GEM_DOMAIN_GTT,
+ &rdev->wb.gpu_addr);
+ if (r) {
+- dev_warn(rdev->dev, "failed to pin WB buffer (%d).\n", r);
++ radeon_bo_unreserve(rdev->wb.wb_obj);
++ dev_warn(rdev->dev, "(%d) pin WB bo failed\n", r);
+ r600_wb_fini(rdev);
+ return r;
+ }
+- r = radeon_object_kmap(rdev->wb.wb_obj, (void **)&rdev->wb.wb);
++ r = radeon_bo_kmap(rdev->wb.wb_obj, (void **)&rdev->wb.wb);
++ radeon_bo_unreserve(rdev->wb.wb_obj);
+ if (r) {
+- dev_warn(rdev->dev, "failed to map WB buffer (%d).\n", r);
++ dev_warn(rdev->dev, "(%d) map WB bo failed\n", r);
+ r600_wb_fini(rdev);
+ return r;
+ }
+@@ -1433,41 +1785,43 @@ int r600_wb_enable(struct radeon_device *rdev)
+ void r600_fence_ring_emit(struct radeon_device *rdev,
+ struct radeon_fence *fence)
+ {
++ /* Also consider EVENT_WRITE_EOP. it handles the interrupts + timestamps + events */
++
++ radeon_ring_write(rdev, PACKET3(PACKET3_EVENT_WRITE, 0));
++ radeon_ring_write(rdev, CACHE_FLUSH_AND_INV_EVENT);
++ /* wait for 3D idle clean */
++ radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
++ radeon_ring_write(rdev, (WAIT_UNTIL - PACKET3_SET_CONFIG_REG_OFFSET) >> 2);
++ radeon_ring_write(rdev, WAIT_3D_IDLE_bit | WAIT_3D_IDLECLEAN_bit);
+ /* Emit fence sequence & fire IRQ */
+ radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
+ radeon_ring_write(rdev, ((rdev->fence_drv.scratch_reg - PACKET3_SET_CONFIG_REG_OFFSET) >> 2));
+ radeon_ring_write(rdev, fence->seq);
+-}
+-
+-int r600_copy_dma(struct radeon_device *rdev,
+- uint64_t src_offset,
+- uint64_t dst_offset,
+- unsigned num_pages,
+- struct radeon_fence *fence)
+-{
+- /* FIXME: implement */
+- return 0;
++ radeon_ring_write(rdev, PACKET0(R_005480_HDP_MEM_COHERENCY_FLUSH_CNTL, 0));
++ radeon_ring_write(rdev, 1);
++ /* CP_INTERRUPT packet 3 no longer exists, use packet 0 */
++ radeon_ring_write(rdev, PACKET0(CP_INT_STATUS, 0));
++ radeon_ring_write(rdev, RB_INT_STAT);
+ }
+
+ int r600_copy_blit(struct radeon_device *rdev,
+ uint64_t src_offset, uint64_t dst_offset,
+ unsigned num_pages, struct radeon_fence *fence)
+ {
+- r600_blit_prepare_copy(rdev, num_pages * RADEON_GPU_PAGE_SIZE);
++ int r;
++
++ mutex_lock(&rdev->r600_blit.mutex);
++ rdev->r600_blit.vb_ib = NULL;
++ r = r600_blit_prepare_copy(rdev, num_pages * RADEON_GPU_PAGE_SIZE);
++ if (r) {
++ if (rdev->r600_blit.vb_ib)
++ radeon_ib_free(rdev, &rdev->r600_blit.vb_ib);
++ mutex_unlock(&rdev->r600_blit.mutex);
++ return r;
++ }
+ r600_kms_blit_copy(rdev, src_offset, dst_offset, num_pages * RADEON_GPU_PAGE_SIZE);
+ r600_blit_done_copy(rdev, fence);
+- return 0;
+-}
+-
+-int r600_irq_process(struct radeon_device *rdev)
+-{
+- /* FIXME: implement */
+- return 0;
+-}
+-
+-int r600_irq_set(struct radeon_device *rdev)
+-{
+- /* FIXME: implement */
++ mutex_unlock(&rdev->r600_blit.mutex);
+ return 0;
+ }
+
+@@ -1506,6 +1860,14 @@ int r600_startup(struct radeon_device *rdev)
+ {
+ int r;
+
++ if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw) {
++ r = r600_init_microcode(rdev);
++ if (r) {
++ DRM_ERROR("Failed to load firmware!\n");
++ return r;
++ }
++ }
++
+ r600_mc_program(rdev);
+ if (rdev->flags & RADEON_IS_AGP) {
+ r600_agp_enable(rdev);
+@@ -1515,13 +1877,33 @@ int r600_startup(struct radeon_device *rdev)
+ return r;
+ }
+ r600_gpu_init(rdev);
+-
+- r = radeon_object_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM,
+- &rdev->r600_blit.shader_gpu_addr);
++ r = r600_blit_init(rdev);
+ if (r) {
+- DRM_ERROR("failed to pin blit object %d\n", r);
++ r600_blit_fini(rdev);
++ rdev->asic->copy = NULL;
++ dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r);
++ }
++ /* pin copy shader into vram */
++ if (rdev->r600_blit.shader_obj) {
++ r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
++ if (unlikely(r != 0))
++ return r;
++ r = radeon_bo_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM,
++ &rdev->r600_blit.shader_gpu_addr);
++ radeon_bo_unreserve(rdev->r600_blit.shader_obj);
++ if (r) {
++ dev_err(rdev->dev, "(%d) pin blit object failed\n", r);
++ return r;
++ }
++ }
++ /* Enable IRQ */
++ r = r600_irq_init(rdev);
++ if (r) {
++ DRM_ERROR("radeon: IH init failed (%d).\n", r);
++ radeon_irq_kms_fini(rdev);
+ return r;
+ }
++ r600_irq_set(rdev);
+
+ r = radeon_ring_init(rdev, rdev->cp.ring_size);
+ if (r)
+@@ -1578,18 +1960,35 @@ int r600_resume(struct radeon_device *rdev)
+ DRM_ERROR("radeon: failled testing IB (%d).\n", r);
+ return r;
+ }
++
++ r = r600_audio_init(rdev);
++ if (r) {
++ DRM_ERROR("radeon: audio resume failed\n");
++ return r;
++ }
++
+ return r;
+ }
+
+ int r600_suspend(struct radeon_device *rdev)
+ {
++ int r;
++
++ r600_audio_fini(rdev);
+ /* FIXME: we should wait for ring to be empty */
+ r600_cp_stop(rdev);
+ rdev->cp.ready = false;
++ r600_irq_suspend(rdev);
+ r600_wb_disable(rdev);
+ r600_pcie_gart_disable(rdev);
+ /* unpin shaders bo */
+- radeon_object_unpin(rdev->r600_blit.shader_obj);
++ if (rdev->r600_blit.shader_obj) {
++ r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
++ if (!r) {
++ radeon_bo_unpin(rdev->r600_blit.shader_obj);
++ radeon_bo_unreserve(rdev->r600_blit.shader_obj);
++ }
++ }
+ return 0;
+ }
+
+@@ -1627,7 +2026,11 @@ int r600_init(struct radeon_device *rdev)
+ if (r)
+ return r;
+ /* Post card if necessary */
+- if (!r600_card_posted(rdev) && rdev->bios) {
++ if (!r600_card_posted(rdev)) {
++ if (!rdev->bios) {
++ dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n");
++ return -EINVAL;
++ }
+ DRM_INFO("GPU not posted. posting now...\n");
+ atom_asic_init(rdev->mode_info.atom_context);
+ }
+@@ -1646,73 +2049,78 @@ int r600_init(struct radeon_device *rdev)
+ r = radeon_fence_driver_init(rdev);
+ if (r)
+ return r;
++ if (rdev->flags & RADEON_IS_AGP) {
++ r = radeon_agp_init(rdev);
++ if (r)
++ radeon_agp_disable(rdev);
++ }
+ r = r600_mc_init(rdev);
+ if (r)
+ return r;
+ /* Memory manager */
+- r = radeon_object_init(rdev);
++ r = radeon_bo_init(rdev);
+ if (r)
+ return r;
++
++ r = radeon_irq_kms_init(rdev);
++ if (r)
++ return r;
++
+ rdev->cp.ring_obj = NULL;
+ r600_ring_init(rdev, 1024 * 1024);
+
+- if (!rdev->me_fw || !rdev->pfp_fw) {
+- r = r600_cp_init_microcode(rdev);
+- if (r) {
+- DRM_ERROR("Failed to load firmware!\n");
+- return r;
+- }
+- }
++ rdev->ih.ring_obj = NULL;
++ r600_ih_ring_init(rdev, 64 * 1024);
+
+ r = r600_pcie_gart_init(rdev);
+ if (r)
+ return r;
+
+ rdev->accel_working = true;
+- r = r600_blit_init(rdev);
+- if (r) {
+- DRM_ERROR("radeon: failled blitter (%d).\n", r);
+- return r;
+- }
+-
+ r = r600_startup(rdev);
+ if (r) {
+- r600_suspend(rdev);
++ dev_err(rdev->dev, "disabling GPU acceleration\n");
++ r600_cp_fini(rdev);
+ r600_wb_fini(rdev);
+- radeon_ring_fini(rdev);
++ r600_irq_fini(rdev);
++ radeon_irq_kms_fini(rdev);
+ r600_pcie_gart_fini(rdev);
+ rdev->accel_working = false;
+ }
+ if (rdev->accel_working) {
+ r = radeon_ib_pool_init(rdev);
+ if (r) {
+- DRM_ERROR("radeon: failled initializing IB pool (%d).\n", r);
+- rdev->accel_working = false;
+- }
+- r = r600_ib_test(rdev);
+- if (r) {
+- DRM_ERROR("radeon: failled testing IB (%d).\n", r);
++ dev_err(rdev->dev, "IB initialization failed (%d).\n", r);
+ rdev->accel_working = false;
++ } else {
++ r = r600_ib_test(rdev);
++ if (r) {
++ dev_err(rdev->dev, "IB test failed (%d).\n", r);
++ rdev->accel_working = false;
++ }
+ }
+ }
++
++ r = r600_audio_init(rdev);
++ if (r)
++ return r; /* TODO error handling */
+ return 0;
+ }
+
+ void r600_fini(struct radeon_device *rdev)
+ {
+- /* Suspend operations */
+- r600_suspend(rdev);
+-
++ r600_audio_fini(rdev);
+ r600_blit_fini(rdev);
+- radeon_ring_fini(rdev);
++ r600_cp_fini(rdev);
+ r600_wb_fini(rdev);
++ r600_irq_fini(rdev);
++ radeon_irq_kms_fini(rdev);
+ r600_pcie_gart_fini(rdev);
++ radeon_agp_fini(rdev);
+ radeon_gem_fini(rdev);
+ radeon_fence_driver_fini(rdev);
+ radeon_clocks_fini(rdev);
+- if (rdev->flags & RADEON_IS_AGP)
+- radeon_agp_fini(rdev);
+- radeon_object_fini(rdev);
++ radeon_bo_fini(rdev);
+ radeon_atombios_fini(rdev);
+ kfree(rdev->bios);
+ rdev->bios = NULL;
+@@ -1798,8 +2206,668 @@ int r600_ib_test(struct radeon_device *rdev)
+ return r;
+ }
+
++/*
++ * Interrupts
++ *
++ * Interrupts use a ring buffer on r6xx/r7xx hardware. It works pretty
++ * the same as the CP ring buffer, but in reverse. Rather than the CPU
++ * writing to the ring and the GPU consuming, the GPU writes to the ring
++ * and host consumes. As the host irq handler processes interrupts, it
++ * increments the rptr. When the rptr catches up with the wptr, all the
++ * current interrupts have been processed.
++ */
++
++void r600_ih_ring_init(struct radeon_device *rdev, unsigned ring_size)
++{
++ u32 rb_bufsz;
++
++ /* Align ring size */
++ rb_bufsz = drm_order(ring_size / 4);
++ ring_size = (1 << rb_bufsz) * 4;
++ rdev->ih.ring_size = ring_size;
++ rdev->ih.ptr_mask = rdev->ih.ring_size - 1;
++ rdev->ih.rptr = 0;
++}
++
++static int r600_ih_ring_alloc(struct radeon_device *rdev)
++{
++ int r;
++
++ /* Allocate ring buffer */
++ if (rdev->ih.ring_obj == NULL) {
++ r = radeon_bo_create(rdev, NULL, rdev->ih.ring_size,
++ true,
++ RADEON_GEM_DOMAIN_GTT,
++ &rdev->ih.ring_obj);
++ if (r) {
++ DRM_ERROR("radeon: failed to create ih ring buffer (%d).\n", r);
++ return r;
++ }
++ r = radeon_bo_reserve(rdev->ih.ring_obj, false);
++ if (unlikely(r != 0))
++ return r;
++ r = radeon_bo_pin(rdev->ih.ring_obj,
++ RADEON_GEM_DOMAIN_GTT,
++ &rdev->ih.gpu_addr);
++ if (r) {
++ radeon_bo_unreserve(rdev->ih.ring_obj);
++ DRM_ERROR("radeon: failed to pin ih ring buffer (%d).\n", r);
++ return r;
++ }
++ r = radeon_bo_kmap(rdev->ih.ring_obj,
++ (void **)&rdev->ih.ring);
++ radeon_bo_unreserve(rdev->ih.ring_obj);
++ if (r) {
++ DRM_ERROR("radeon: failed to map ih ring buffer (%d).\n", r);
++ return r;
++ }
++ }
++ return 0;
++}
++
++static void r600_ih_ring_fini(struct radeon_device *rdev)
++{
++ int r;
++ if (rdev->ih.ring_obj) {
++ r = radeon_bo_reserve(rdev->ih.ring_obj, false);
++ if (likely(r == 0)) {
++ radeon_bo_kunmap(rdev->ih.ring_obj);
++ radeon_bo_unpin(rdev->ih.ring_obj);
++ radeon_bo_unreserve(rdev->ih.ring_obj);
++ }
++ radeon_bo_unref(&rdev->ih.ring_obj);
++ rdev->ih.ring = NULL;
++ rdev->ih.ring_obj = NULL;
++ }
++}
++
++static void r600_rlc_stop(struct radeon_device *rdev)
++{
++
++ if (rdev->family >= CHIP_RV770) {
++ /* r7xx asics need to soft reset RLC before halting */
++ WREG32(SRBM_SOFT_RESET, SOFT_RESET_RLC);
++ RREG32(SRBM_SOFT_RESET);
++ udelay(15000);
++ WREG32(SRBM_SOFT_RESET, 0);
++ RREG32(SRBM_SOFT_RESET);
++ }
++
++ WREG32(RLC_CNTL, 0);
++}
++
++static void r600_rlc_start(struct radeon_device *rdev)
++{
++ WREG32(RLC_CNTL, RLC_ENABLE);
++}
++
++static int r600_rlc_init(struct radeon_device *rdev)
++{
++ u32 i;
++ const __be32 *fw_data;
++
++ if (!rdev->rlc_fw)
++ return -EINVAL;
++
++ r600_rlc_stop(rdev);
++
++ WREG32(RLC_HB_BASE, 0);
++ WREG32(RLC_HB_CNTL, 0);
++ WREG32(RLC_HB_RPTR, 0);
++ WREG32(RLC_HB_WPTR, 0);
++ WREG32(RLC_HB_WPTR_LSB_ADDR, 0);
++ WREG32(RLC_HB_WPTR_MSB_ADDR, 0);
++ WREG32(RLC_MC_CNTL, 0);
++ WREG32(RLC_UCODE_CNTL, 0);
++
++ fw_data = (const __be32 *)rdev->rlc_fw->data;
++ if (rdev->family >= CHIP_RV770) {
++ for (i = 0; i < R700_RLC_UCODE_SIZE; i++) {
++ WREG32(RLC_UCODE_ADDR, i);
++ WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
++ }
++ } else {
++ for (i = 0; i < RLC_UCODE_SIZE; i++) {
++ WREG32(RLC_UCODE_ADDR, i);
++ WREG32(RLC_UCODE_DATA, be32_to_cpup(fw_data++));
++ }
++ }
++ WREG32(RLC_UCODE_ADDR, 0);
++
++ r600_rlc_start(rdev);
++
++ return 0;
++}
++
++static void r600_enable_interrupts(struct radeon_device *rdev)
++{
++ u32 ih_cntl = RREG32(IH_CNTL);
++ u32 ih_rb_cntl = RREG32(IH_RB_CNTL);
++
++ ih_cntl |= ENABLE_INTR;
++ ih_rb_cntl |= IH_RB_ENABLE;
++ WREG32(IH_CNTL, ih_cntl);
++ WREG32(IH_RB_CNTL, ih_rb_cntl);
++ rdev->ih.enabled = true;
++}
++
++static void r600_disable_interrupts(struct radeon_device *rdev)
++{
++ u32 ih_rb_cntl = RREG32(IH_RB_CNTL);
++ u32 ih_cntl = RREG32(IH_CNTL);
++
++ ih_rb_cntl &= ~IH_RB_ENABLE;
++ ih_cntl &= ~ENABLE_INTR;
++ WREG32(IH_RB_CNTL, ih_rb_cntl);
++ WREG32(IH_CNTL, ih_cntl);
++ /* set rptr, wptr to 0 */
++ WREG32(IH_RB_RPTR, 0);
++ WREG32(IH_RB_WPTR, 0);
++ rdev->ih.enabled = false;
++ rdev->ih.wptr = 0;
++ rdev->ih.rptr = 0;
++}
++
++static void r600_disable_interrupt_state(struct radeon_device *rdev)
++{
++ u32 tmp;
++
++ WREG32(CP_INT_CNTL, 0);
++ WREG32(GRBM_INT_CNTL, 0);
++ WREG32(DxMODE_INT_MASK, 0);
++ if (ASIC_IS_DCE3(rdev)) {
++ WREG32(DCE3_DACA_AUTODETECT_INT_CONTROL, 0);
++ WREG32(DCE3_DACB_AUTODETECT_INT_CONTROL, 0);
++ tmp = RREG32(DC_HPD1_INT_CONTROL) & DC_HPDx_INT_POLARITY;
++ WREG32(DC_HPD1_INT_CONTROL, tmp);
++ tmp = RREG32(DC_HPD2_INT_CONTROL) & DC_HPDx_INT_POLARITY;
++ WREG32(DC_HPD2_INT_CONTROL, tmp);
++ tmp = RREG32(DC_HPD3_INT_CONTROL) & DC_HPDx_INT_POLARITY;
++ WREG32(DC_HPD3_INT_CONTROL, tmp);
++ tmp = RREG32(DC_HPD4_INT_CONTROL) & DC_HPDx_INT_POLARITY;
++ WREG32(DC_HPD4_INT_CONTROL, tmp);
++ if (ASIC_IS_DCE32(rdev)) {
++ tmp = RREG32(DC_HPD5_INT_CONTROL) & DC_HPDx_INT_POLARITY;
++ WREG32(DC_HPD5_INT_CONTROL, 0);
++ tmp = RREG32(DC_HPD6_INT_CONTROL) & DC_HPDx_INT_POLARITY;
++ WREG32(DC_HPD6_INT_CONTROL, 0);
++ }
++ } else {
++ WREG32(DACA_AUTODETECT_INT_CONTROL, 0);
++ WREG32(DACB_AUTODETECT_INT_CONTROL, 0);
++ tmp = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL) & DC_HOT_PLUG_DETECTx_INT_POLARITY;
++ WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, 0);
++ tmp = RREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL) & DC_HOT_PLUG_DETECTx_INT_POLARITY;
++ WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, 0);
++ tmp = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL) & DC_HOT_PLUG_DETECTx_INT_POLARITY;
++ WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, 0);
++ }
++}
++
++int r600_irq_init(struct radeon_device *rdev)
++{
++ int ret = 0;
++ int rb_bufsz;
++ u32 interrupt_cntl, ih_cntl, ih_rb_cntl;
++
++ /* allocate ring */
++ ret = r600_ih_ring_alloc(rdev);
++ if (ret)
++ return ret;
++
++ /* disable irqs */
++ r600_disable_interrupts(rdev);
++
++ /* init rlc */
++ ret = r600_rlc_init(rdev);
++ if (ret) {
++ r600_ih_ring_fini(rdev);
++ return ret;
++ }
++
++ /* setup interrupt control */
++ /* set dummy read address to ring address */
++ WREG32(INTERRUPT_CNTL2, rdev->ih.gpu_addr >> 8);
++ interrupt_cntl = RREG32(INTERRUPT_CNTL);
++ /* IH_DUMMY_RD_OVERRIDE=0 - dummy read disabled with msi, enabled without msi
++ * IH_DUMMY_RD_OVERRIDE=1 - dummy read controlled by IH_DUMMY_RD_EN
++ */
++ interrupt_cntl &= ~IH_DUMMY_RD_OVERRIDE;
++ /* IH_REQ_NONSNOOP_EN=1 if ring is in non-cacheable memory, e.g., vram */
++ interrupt_cntl &= ~IH_REQ_NONSNOOP_EN;
++ WREG32(INTERRUPT_CNTL, interrupt_cntl);
++
++ WREG32(IH_RB_BASE, rdev->ih.gpu_addr >> 8);
++ rb_bufsz = drm_order(rdev->ih.ring_size / 4);
++
++ ih_rb_cntl = (IH_WPTR_OVERFLOW_ENABLE |
++ IH_WPTR_OVERFLOW_CLEAR |
++ (rb_bufsz << 1));
++ /* WPTR writeback, not yet */
++ /*ih_rb_cntl |= IH_WPTR_WRITEBACK_ENABLE;*/
++ WREG32(IH_RB_WPTR_ADDR_LO, 0);
++ WREG32(IH_RB_WPTR_ADDR_HI, 0);
++
++ WREG32(IH_RB_CNTL, ih_rb_cntl);
++
++ /* set rptr, wptr to 0 */
++ WREG32(IH_RB_RPTR, 0);
++ WREG32(IH_RB_WPTR, 0);
++
++ /* Default settings for IH_CNTL (disabled at first) */
++ ih_cntl = MC_WRREQ_CREDIT(0x10) | MC_WR_CLEAN_CNT(0x10);
++ /* RPTR_REARM only works if msi's are enabled */
++ if (rdev->msi_enabled)
++ ih_cntl |= RPTR_REARM;
++
++#ifdef __BIG_ENDIAN
++ ih_cntl |= IH_MC_SWAP(IH_MC_SWAP_32BIT);
++#endif
++ WREG32(IH_CNTL, ih_cntl);
++
++ /* force the active interrupt state to all disabled */
++ r600_disable_interrupt_state(rdev);
++
++ /* enable irqs */
++ r600_enable_interrupts(rdev);
++
++ return ret;
++}
++
++void r600_irq_suspend(struct radeon_device *rdev)
++{
++ r600_disable_interrupts(rdev);
++ r600_rlc_stop(rdev);
++}
++
++void r600_irq_fini(struct radeon_device *rdev)
++{
++ r600_irq_suspend(rdev);
++ r600_ih_ring_fini(rdev);
++}
++
++int r600_irq_set(struct radeon_device *rdev)
++{
++ u32 cp_int_cntl = CNTX_BUSY_INT_ENABLE | CNTX_EMPTY_INT_ENABLE;
++ u32 mode_int = 0;
++ u32 hpd1, hpd2, hpd3, hpd4 = 0, hpd5 = 0, hpd6 = 0;
++
++ if (!rdev->irq.installed) {
++ WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n");
++ return -EINVAL;
++ }
++ /* don't enable anything if the ih is disabled */
++ if (!rdev->ih.enabled) {
++ r600_disable_interrupts(rdev);
++ /* force the active interrupt state to all disabled */
++ r600_disable_interrupt_state(rdev);
++ return 0;
++ }
++
++ if (ASIC_IS_DCE3(rdev)) {
++ hpd1 = RREG32(DC_HPD1_INT_CONTROL) & ~DC_HPDx_INT_EN;
++ hpd2 = RREG32(DC_HPD2_INT_CONTROL) & ~DC_HPDx_INT_EN;
++ hpd3 = RREG32(DC_HPD3_INT_CONTROL) & ~DC_HPDx_INT_EN;
++ hpd4 = RREG32(DC_HPD4_INT_CONTROL) & ~DC_HPDx_INT_EN;
++ if (ASIC_IS_DCE32(rdev)) {
++ hpd5 = RREG32(DC_HPD5_INT_CONTROL) & ~DC_HPDx_INT_EN;
++ hpd6 = RREG32(DC_HPD6_INT_CONTROL) & ~DC_HPDx_INT_EN;
++ }
++ } else {
++ hpd1 = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL) & ~DC_HPDx_INT_EN;
++ hpd2 = RREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL) & ~DC_HPDx_INT_EN;
++ hpd3 = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL) & ~DC_HPDx_INT_EN;
++ }
++
++ if (rdev->irq.sw_int) {
++ DRM_DEBUG("r600_irq_set: sw int\n");
++ cp_int_cntl |= RB_INT_ENABLE;
++ }
++ if (rdev->irq.crtc_vblank_int[0]) {
++ DRM_DEBUG("r600_irq_set: vblank 0\n");
++ mode_int |= D1MODE_VBLANK_INT_MASK;
++ }
++ if (rdev->irq.crtc_vblank_int[1]) {
++ DRM_DEBUG("r600_irq_set: vblank 1\n");
++ mode_int |= D2MODE_VBLANK_INT_MASK;
++ }
++ if (rdev->irq.hpd[0]) {
++ DRM_DEBUG("r600_irq_set: hpd 1\n");
++ hpd1 |= DC_HPDx_INT_EN;
++ }
++ if (rdev->irq.hpd[1]) {
++ DRM_DEBUG("r600_irq_set: hpd 2\n");
++ hpd2 |= DC_HPDx_INT_EN;
++ }
++ if (rdev->irq.hpd[2]) {
++ DRM_DEBUG("r600_irq_set: hpd 3\n");
++ hpd3 |= DC_HPDx_INT_EN;
++ }
++ if (rdev->irq.hpd[3]) {
++ DRM_DEBUG("r600_irq_set: hpd 4\n");
++ hpd4 |= DC_HPDx_INT_EN;
++ }
++ if (rdev->irq.hpd[4]) {
++ DRM_DEBUG("r600_irq_set: hpd 5\n");
++ hpd5 |= DC_HPDx_INT_EN;
++ }
++ if (rdev->irq.hpd[5]) {
++ DRM_DEBUG("r600_irq_set: hpd 6\n");
++ hpd6 |= DC_HPDx_INT_EN;
++ }
++
++ WREG32(CP_INT_CNTL, cp_int_cntl);
++ WREG32(DxMODE_INT_MASK, mode_int);
++ if (ASIC_IS_DCE3(rdev)) {
++ WREG32(DC_HPD1_INT_CONTROL, hpd1);
++ WREG32(DC_HPD2_INT_CONTROL, hpd2);
++ WREG32(DC_HPD3_INT_CONTROL, hpd3);
++ WREG32(DC_HPD4_INT_CONTROL, hpd4);
++ if (ASIC_IS_DCE32(rdev)) {
++ WREG32(DC_HPD5_INT_CONTROL, hpd5);
++ WREG32(DC_HPD6_INT_CONTROL, hpd6);
++ }
++ } else {
++ WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, hpd1);
++ WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, hpd2);
++ WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, hpd3);
++ }
++
++ return 0;
++}
++
++static inline void r600_irq_ack(struct radeon_device *rdev,
++ u32 *disp_int,
++ u32 *disp_int_cont,
++ u32 *disp_int_cont2)
++{
++ u32 tmp;
++
++ if (ASIC_IS_DCE3(rdev)) {
++ *disp_int = RREG32(DCE3_DISP_INTERRUPT_STATUS);
++ *disp_int_cont = RREG32(DCE3_DISP_INTERRUPT_STATUS_CONTINUE);
++ *disp_int_cont2 = RREG32(DCE3_DISP_INTERRUPT_STATUS_CONTINUE2);
++ } else {
++ *disp_int = RREG32(DISP_INTERRUPT_STATUS);
++ *disp_int_cont = RREG32(DISP_INTERRUPT_STATUS_CONTINUE);
++ *disp_int_cont2 = 0;
++ }
++
++ if (*disp_int & LB_D1_VBLANK_INTERRUPT)
++ WREG32(D1MODE_VBLANK_STATUS, DxMODE_VBLANK_ACK);
++ if (*disp_int & LB_D1_VLINE_INTERRUPT)
++ WREG32(D1MODE_VLINE_STATUS, DxMODE_VLINE_ACK);
++ if (*disp_int & LB_D2_VBLANK_INTERRUPT)
++ WREG32(D2MODE_VBLANK_STATUS, DxMODE_VBLANK_ACK);
++ if (*disp_int & LB_D2_VLINE_INTERRUPT)
++ WREG32(D2MODE_VLINE_STATUS, DxMODE_VLINE_ACK);
++ if (*disp_int & DC_HPD1_INTERRUPT) {
++ if (ASIC_IS_DCE3(rdev)) {
++ tmp = RREG32(DC_HPD1_INT_CONTROL);
++ tmp |= DC_HPDx_INT_ACK;
++ WREG32(DC_HPD1_INT_CONTROL, tmp);
++ } else {
++ tmp = RREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL);
++ tmp |= DC_HPDx_INT_ACK;
++ WREG32(DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp);
++ }
++ }
++ if (*disp_int & DC_HPD2_INTERRUPT) {
++ if (ASIC_IS_DCE3(rdev)) {
++ tmp = RREG32(DC_HPD2_INT_CONTROL);
++ tmp |= DC_HPDx_INT_ACK;
++ WREG32(DC_HPD2_INT_CONTROL, tmp);
++ } else {
++ tmp = RREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL);
++ tmp |= DC_HPDx_INT_ACK;
++ WREG32(DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp);
++ }
++ }
++ if (*disp_int_cont & DC_HPD3_INTERRUPT) {
++ if (ASIC_IS_DCE3(rdev)) {
++ tmp = RREG32(DC_HPD3_INT_CONTROL);
++ tmp |= DC_HPDx_INT_ACK;
++ WREG32(DC_HPD3_INT_CONTROL, tmp);
++ } else {
++ tmp = RREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL);
++ tmp |= DC_HPDx_INT_ACK;
++ WREG32(DC_HOT_PLUG_DETECT3_INT_CONTROL, tmp);
++ }
++ }
++ if (*disp_int_cont & DC_HPD4_INTERRUPT) {
++ tmp = RREG32(DC_HPD4_INT_CONTROL);
++ tmp |= DC_HPDx_INT_ACK;
++ WREG32(DC_HPD4_INT_CONTROL, tmp);
++ }
++ if (ASIC_IS_DCE32(rdev)) {
++ if (*disp_int_cont2 & DC_HPD5_INTERRUPT) {
++ tmp = RREG32(DC_HPD5_INT_CONTROL);
++ tmp |= DC_HPDx_INT_ACK;
++ WREG32(DC_HPD5_INT_CONTROL, tmp);
++ }
++ if (*disp_int_cont2 & DC_HPD6_INTERRUPT) {
++ tmp = RREG32(DC_HPD5_INT_CONTROL);
++ tmp |= DC_HPDx_INT_ACK;
++ WREG32(DC_HPD6_INT_CONTROL, tmp);
++ }
++ }
++}
++
++void r600_irq_disable(struct radeon_device *rdev)
++{
++ u32 disp_int, disp_int_cont, disp_int_cont2;
++
++ r600_disable_interrupts(rdev);
++ /* Wait and acknowledge irq */
++ mdelay(1);
++ r600_irq_ack(rdev, &disp_int, &disp_int_cont, &disp_int_cont2);
++ r600_disable_interrupt_state(rdev);
++}
++
++static inline u32 r600_get_ih_wptr(struct radeon_device *rdev)
++{
++ u32 wptr, tmp;
++
++ /* XXX use writeback */
++ wptr = RREG32(IH_RB_WPTR);
++
++ if (wptr & RB_OVERFLOW) {
++ /* When a ring buffer overflow happen start parsing interrupt
++ * from the last not overwritten vector (wptr + 16). Hopefully
++ * this should allow us to catchup.
++ */
++ dev_warn(rdev->dev, "IH ring buffer overflow (0x%08X, %d, %d)\n",
++ wptr, rdev->ih.rptr, (wptr + 16) + rdev->ih.ptr_mask);
++ rdev->ih.rptr = (wptr + 16) & rdev->ih.ptr_mask;
++ tmp = RREG32(IH_RB_CNTL);
++ tmp |= IH_WPTR_OVERFLOW_CLEAR;
++ WREG32(IH_RB_CNTL, tmp);
++ }
++ return (wptr & rdev->ih.ptr_mask);
++}
++
++/* r600 IV Ring
++ * Each IV ring entry is 128 bits:
++ * [7:0] - interrupt source id
++ * [31:8] - reserved
++ * [59:32] - interrupt source data
++ * [127:60] - reserved
++ *
++ * The basic interrupt vector entries
++ * are decoded as follows:
++ * src_id src_data description
++ * 1 0 D1 Vblank
++ * 1 1 D1 Vline
++ * 5 0 D2 Vblank
++ * 5 1 D2 Vline
++ * 19 0 FP Hot plug detection A
++ * 19 1 FP Hot plug detection B
++ * 19 2 DAC A auto-detection
++ * 19 3 DAC B auto-detection
++ * 176 - CP_INT RB
++ * 177 - CP_INT IB1
++ * 178 - CP_INT IB2
++ * 181 - EOP Interrupt
++ * 233 - GUI Idle
++ *
++ * Note, these are based on r600 and may need to be
++ * adjusted or added to on newer asics
++ */
+
++int r600_irq_process(struct radeon_device *rdev)
++{
++ u32 wptr = r600_get_ih_wptr(rdev);
++ u32 rptr = rdev->ih.rptr;
++ u32 src_id, src_data;
++ u32 ring_index, disp_int, disp_int_cont, disp_int_cont2;
++ unsigned long flags;
++ bool queue_hotplug = false;
++
++ DRM_DEBUG("r600_irq_process start: rptr %d, wptr %d\n", rptr, wptr);
++ if (!rdev->ih.enabled)
++ return IRQ_NONE;
++
++ spin_lock_irqsave(&rdev->ih.lock, flags);
++
++ if (rptr == wptr) {
++ spin_unlock_irqrestore(&rdev->ih.lock, flags);
++ return IRQ_NONE;
++ }
++ if (rdev->shutdown) {
++ spin_unlock_irqrestore(&rdev->ih.lock, flags);
++ return IRQ_NONE;
++ }
++
++restart_ih:
++ /* display interrupts */
++ r600_irq_ack(rdev, &disp_int, &disp_int_cont, &disp_int_cont2);
++
++ rdev->ih.wptr = wptr;
++ while (rptr != wptr) {
++ /* wptr/rptr are in bytes! */
++ ring_index = rptr / 4;
++ src_id = rdev->ih.ring[ring_index] & 0xff;
++ src_data = rdev->ih.ring[ring_index + 1] & 0xfffffff;
++
++ switch (src_id) {
++ case 1: /* D1 vblank/vline */
++ switch (src_data) {
++ case 0: /* D1 vblank */
++ if (disp_int & LB_D1_VBLANK_INTERRUPT) {
++ drm_handle_vblank(rdev->ddev, 0);
++ disp_int &= ~LB_D1_VBLANK_INTERRUPT;
++ DRM_DEBUG("IH: D1 vblank\n");
++ }
++ break;
++ case 1: /* D1 vline */
++ if (disp_int & LB_D1_VLINE_INTERRUPT) {
++ disp_int &= ~LB_D1_VLINE_INTERRUPT;
++ DRM_DEBUG("IH: D1 vline\n");
++ }
++ break;
++ default:
++ DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
++ break;
++ }
++ break;
++ case 5: /* D2 vblank/vline */
++ switch (src_data) {
++ case 0: /* D2 vblank */
++ if (disp_int & LB_D2_VBLANK_INTERRUPT) {
++ drm_handle_vblank(rdev->ddev, 1);
++ disp_int &= ~LB_D2_VBLANK_INTERRUPT;
++ DRM_DEBUG("IH: D2 vblank\n");
++ }
++ break;
++ case 1: /* D1 vline */
++ if (disp_int & LB_D2_VLINE_INTERRUPT) {
++ disp_int &= ~LB_D2_VLINE_INTERRUPT;
++ DRM_DEBUG("IH: D2 vline\n");
++ }
++ break;
++ default:
++ DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
++ break;
++ }
++ break;
++ case 19: /* HPD/DAC hotplug */
++ switch (src_data) {
++ case 0:
++ if (disp_int & DC_HPD1_INTERRUPT) {
++ disp_int &= ~DC_HPD1_INTERRUPT;
++ queue_hotplug = true;
++ DRM_DEBUG("IH: HPD1\n");
++ }
++ break;
++ case 1:
++ if (disp_int & DC_HPD2_INTERRUPT) {
++ disp_int &= ~DC_HPD2_INTERRUPT;
++ queue_hotplug = true;
++ DRM_DEBUG("IH: HPD2\n");
++ }
++ break;
++ case 4:
++ if (disp_int_cont & DC_HPD3_INTERRUPT) {
++ disp_int_cont &= ~DC_HPD3_INTERRUPT;
++ queue_hotplug = true;
++ DRM_DEBUG("IH: HPD3\n");
++ }
++ break;
++ case 5:
++ if (disp_int_cont & DC_HPD4_INTERRUPT) {
++ disp_int_cont &= ~DC_HPD4_INTERRUPT;
++ queue_hotplug = true;
++ DRM_DEBUG("IH: HPD4\n");
++ }
++ break;
++ case 10:
++ if (disp_int_cont2 & DC_HPD5_INTERRUPT) {
++ disp_int_cont &= ~DC_HPD5_INTERRUPT;
++ queue_hotplug = true;
++ DRM_DEBUG("IH: HPD5\n");
++ }
++ break;
++ case 12:
++ if (disp_int_cont2 & DC_HPD6_INTERRUPT) {
++ disp_int_cont &= ~DC_HPD6_INTERRUPT;
++ queue_hotplug = true;
++ DRM_DEBUG("IH: HPD6\n");
++ }
++ break;
++ default:
++ DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
++ break;
++ }
++ break;
++ case 176: /* CP_INT in ring buffer */
++ case 177: /* CP_INT in IB1 */
++ case 178: /* CP_INT in IB2 */
++ DRM_DEBUG("IH: CP int: 0x%08x\n", src_data);
++ radeon_fence_process(rdev);
++ break;
++ case 181: /* CP EOP event */
++ DRM_DEBUG("IH: CP EOP\n");
++ break;
++ default:
++ DRM_DEBUG("Unhandled interrupt: %d %d\n", src_id, src_data);
++ break;
++ }
+
++ /* wptr/rptr are in bytes! */
++ rptr += 16;
++ rptr &= rdev->ih.ptr_mask;
++ }
++ /* make sure wptr hasn't changed while processing */
++ wptr = r600_get_ih_wptr(rdev);
++ if (wptr != rdev->ih.wptr)
++ goto restart_ih;
++ if (queue_hotplug)
++ queue_work(rdev->wq, &rdev->hotplug_work);
++ rdev->ih.rptr = rptr;
++ WREG32(IH_RB_RPTR, rdev->ih.rptr);
++ spin_unlock_irqrestore(&rdev->ih.lock, flags);
++ return IRQ_HANDLED;
++}
+
+ /*
+ * Debugfs info
+@@ -1811,21 +2879,21 @@ static int r600_debugfs_cp_ring_info(struct seq_file *m, void *data)
+ struct drm_info_node *node = (struct drm_info_node *) m->private;
+ struct drm_device *dev = node->minor->dev;
+ struct radeon_device *rdev = dev->dev_private;
+- uint32_t rdp, wdp;
+ unsigned count, i, j;
+
+ radeon_ring_free_size(rdev);
+- rdp = RREG32(CP_RB_RPTR);
+- wdp = RREG32(CP_RB_WPTR);
+- count = (rdp + rdev->cp.ring_size - wdp) & rdev->cp.ptr_mask;
++ count = (rdev->cp.ring_size / 4) - rdev->cp.ring_free_dw;
+ seq_printf(m, "CP_STAT 0x%08x\n", RREG32(CP_STAT));
+- seq_printf(m, "CP_RB_WPTR 0x%08x\n", wdp);
+- seq_printf(m, "CP_RB_RPTR 0x%08x\n", rdp);
++ seq_printf(m, "CP_RB_WPTR 0x%08x\n", RREG32(CP_RB_WPTR));
++ seq_printf(m, "CP_RB_RPTR 0x%08x\n", RREG32(CP_RB_RPTR));
++ seq_printf(m, "driver's copy of the CP_RB_WPTR 0x%08x\n", rdev->cp.wptr);
++ seq_printf(m, "driver's copy of the CP_RB_RPTR 0x%08x\n", rdev->cp.rptr);
+ seq_printf(m, "%u free dwords in ring\n", rdev->cp.ring_free_dw);
+ seq_printf(m, "%u dwords in ring\n", count);
++ i = rdev->cp.rptr;
+ for (j = 0; j <= count; j++) {
+- i = (rdp + j) & rdev->cp.ptr_mask;
+ seq_printf(m, "r[%04d]=0x%08x\n", i, rdev->cp.ring[i]);
++ i = (i + 1) & rdev->cp.ptr_mask;
+ }
+ return 0;
+ }
+@@ -1855,3 +2923,18 @@ int r600_debugfs_mc_info_init(struct radeon_device *rdev)
+ return 0;
+ #endif
+ }
++
++/**
++ * r600_ioctl_wait_idle - flush host path cache on wait idle ioctl
++ * rdev: radeon device structure
++ * bo: buffer object struct which userspace is waiting for idle
++ *
++ * Some R6XX/R7XX doesn't seems to take into account HDP flush performed
++ * through ring buffer, this leads to corruption in rendering, see
++ * http://bugzilla.kernel.org/show_bug.cgi?id=15186 to avoid this we
++ * directly perform HDP flush by writing register through MMIO.
++ */
++void r600_ioctl_wait_idle(struct radeon_device *rdev, struct radeon_bo *bo)
++{
++ WREG32(R_005480_HDP_MEM_COHERENCY_FLUSH_CNTL, 0x1);
++}
+diff --git a/drivers/gpu/drm/radeon/r600_audio.c b/drivers/gpu/drm/radeon/r600_audio.c
+new file mode 100644
+index 0000000..0dcb690
+--- /dev/null
++++ b/drivers/gpu/drm/radeon/r600_audio.c
+@@ -0,0 +1,266 @@
++/*
++ * Copyright 2008 Advanced Micro Devices, Inc.
++ * Copyright 2008 Red Hat Inc.
++ * Copyright 2009 Christian König.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ *
++ * Authors: Christian König
++ */
++#include "drmP.h"
++#include "radeon.h"
++#include "radeon_reg.h"
++#include "atom.h"
++
++#define AUDIO_TIMER_INTERVALL 100 /* 1/10 sekund should be enough */
++
++/*
++ * check if the chipset is supported
++ */
++static int r600_audio_chipset_supported(struct radeon_device *rdev)
++{
++ return (rdev->family >= CHIP_R600 && rdev->family < CHIP_RV710)
++ || rdev->family == CHIP_RS600
++ || rdev->family == CHIP_RS690
++ || rdev->family == CHIP_RS740;
++}
++
++/*
++ * current number of channels
++ */
++static int r600_audio_channels(struct radeon_device *rdev)
++{
++ return (RREG32(R600_AUDIO_RATE_BPS_CHANNEL) & 0x7) + 1;
++}
++
++/*
++ * current bits per sample
++ */
++static int r600_audio_bits_per_sample(struct radeon_device *rdev)
++{
++ uint32_t value = (RREG32(R600_AUDIO_RATE_BPS_CHANNEL) & 0xF0) >> 4;
++ switch (value) {
++ case 0x0: return 8;
++ case 0x1: return 16;
++ case 0x2: return 20;
++ case 0x3: return 24;
++ case 0x4: return 32;
++ }
++
++ DRM_ERROR("Unknown bits per sample 0x%x using 16 instead.\n", (int)value);
++
++ return 16;
++}
++
++/*
++ * current sampling rate in HZ
++ */
++static int r600_audio_rate(struct radeon_device *rdev)
++{
++ uint32_t value = RREG32(R600_AUDIO_RATE_BPS_CHANNEL);
++ uint32_t result;
++
++ if (value & 0x4000)
++ result = 44100;
++ else
++ result = 48000;
++
++ result *= ((value >> 11) & 0x7) + 1;
++ result /= ((value >> 8) & 0x7) + 1;
++
++ return result;
++}
++
++/*
++ * iec 60958 status bits
++ */
++static uint8_t r600_audio_status_bits(struct radeon_device *rdev)
++{
++ return RREG32(R600_AUDIO_STATUS_BITS) & 0xff;
++}
++
++/*
++ * iec 60958 category code
++ */
++static uint8_t r600_audio_category_code(struct radeon_device *rdev)
++{
++ return (RREG32(R600_AUDIO_STATUS_BITS) >> 8) & 0xff;
++}
++
++/*
++ * update all hdmi interfaces with current audio parameters
++ */
++static void r600_audio_update_hdmi(unsigned long param)
++{
++ struct radeon_device *rdev = (struct radeon_device *)param;
++ struct drm_device *dev = rdev->ddev;
++
++ int channels = r600_audio_channels(rdev);
++ int rate = r600_audio_rate(rdev);
++ int bps = r600_audio_bits_per_sample(rdev);
++ uint8_t status_bits = r600_audio_status_bits(rdev);
++ uint8_t category_code = r600_audio_category_code(rdev);
++
++ struct drm_encoder *encoder;
++ int changes = 0;
++
++ changes |= channels != rdev->audio_channels;
++ changes |= rate != rdev->audio_rate;
++ changes |= bps != rdev->audio_bits_per_sample;
++ changes |= status_bits != rdev->audio_status_bits;
++ changes |= category_code != rdev->audio_category_code;
++
++ if (changes) {
++ rdev->audio_channels = channels;
++ rdev->audio_rate = rate;
++ rdev->audio_bits_per_sample = bps;
++ rdev->audio_status_bits = status_bits;
++ rdev->audio_category_code = category_code;
++ }
++
++ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
++ if (changes || r600_hdmi_buffer_status_changed(encoder))
++ r600_hdmi_update_audio_settings(
++ encoder, channels,
++ rate, bps, status_bits,
++ category_code);
++ }
++
++ mod_timer(&rdev->audio_timer,
++ jiffies + msecs_to_jiffies(AUDIO_TIMER_INTERVALL));
++}
++
++/*
++ * initialize the audio vars and register the update timer
++ */
++int r600_audio_init(struct radeon_device *rdev)
++{
++ if (!r600_audio_chipset_supported(rdev))
++ return 0;
++
++ DRM_INFO("%s audio support", radeon_audio ? "Enabling" : "Disabling");
++ WREG32_P(R600_AUDIO_ENABLE, radeon_audio ? 0x81000000 : 0x0, ~0x81000000);
++
++ rdev->audio_channels = -1;
++ rdev->audio_rate = -1;
++ rdev->audio_bits_per_sample = -1;
++ rdev->audio_status_bits = 0;
++ rdev->audio_category_code = 0;
++
++ setup_timer(
++ &rdev->audio_timer,
++ r600_audio_update_hdmi,
++ (unsigned long)rdev);
++
++ mod_timer(&rdev->audio_timer, jiffies + 1);
++
++ return 0;
++}
++
++/*
++ * determin how the encoders and audio interface is wired together
++ */
++int r600_audio_tmds_index(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
++ struct drm_encoder *other;
++
++ switch (radeon_encoder->encoder_id) {
++ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
++ return 0;
++
++ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
++ /* special case check if an TMDS1 is present */
++ list_for_each_entry(other, &dev->mode_config.encoder_list, head) {
++ if (to_radeon_encoder(other)->encoder_id ==
++ ENCODER_OBJECT_ID_INTERNAL_TMDS1)
++ return 1;
++ }
++ return 0;
++
++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
++ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
++ return 1;
++
++ default:
++ DRM_ERROR("Unsupported encoder type 0x%02X\n",
++ radeon_encoder->encoder_id);
++ return -1;
++ }
++}
++
++/*
++ * atach the audio codec to the clock source of the encoder
++ */
++void r600_audio_set_clock(struct drm_encoder *encoder, int clock)
++{
++ struct drm_device *dev = encoder->dev;
++ struct radeon_device *rdev = dev->dev_private;
++ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
++ int base_rate = 48000;
++
++ switch (radeon_encoder->encoder_id) {
++ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
++ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
++ WREG32_P(R600_AUDIO_TIMING, 0, ~0x301);
++ break;
++
++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
++ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
++ WREG32_P(R600_AUDIO_TIMING, 0x100, ~0x301);
++ break;
++
++ default:
++ DRM_ERROR("Unsupported encoder type 0x%02X\n",
++ radeon_encoder->encoder_id);
++ return;
++ }
++
++ switch (r600_audio_tmds_index(encoder)) {
++ case 0:
++ WREG32(R600_AUDIO_PLL1_MUL, base_rate*50);
++ WREG32(R600_AUDIO_PLL1_DIV, clock*100);
++ WREG32(R600_AUDIO_CLK_SRCSEL, 0);
++ break;
++
++ case 1:
++ WREG32(R600_AUDIO_PLL2_MUL, base_rate*50);
++ WREG32(R600_AUDIO_PLL2_DIV, clock*100);
++ WREG32(R600_AUDIO_CLK_SRCSEL, 1);
++ break;
++ }
++}
++
++/*
++ * release the audio timer
++ * TODO: How to do this correctly on SMP systems?
++ */
++void r600_audio_fini(struct radeon_device *rdev)
++{
++ if (!r600_audio_chipset_supported(rdev))
++ return;
++
++ del_timer(&rdev->audio_timer);
++ WREG32_P(R600_AUDIO_ENABLE, 0x0, ~0x81000000);
++}
+diff --git a/drivers/gpu/drm/radeon/r600_blit_kms.c b/drivers/gpu/drm/radeon/r600_blit_kms.c
+index dbf716e..ec49dad 100644
+--- a/drivers/gpu/drm/radeon/r600_blit_kms.c
++++ b/drivers/gpu/drm/radeon/r600_blit_kms.c
+@@ -403,8 +403,6 @@ set_default_state(struct radeon_device *rdev)
+ radeon_ring_write(rdev, upper_32_bits(gpu_addr) & 0xFF);
+ radeon_ring_write(rdev, dwords);
+
+- radeon_ring_write(rdev, PACKET3(PACKET3_EVENT_WRITE, 0));
+- radeon_ring_write(rdev, CACHE_FLUSH_AND_INV_EVENT);
+ /* SQ config */
+ radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 6));
+ radeon_ring_write(rdev, (SQ_CONFIG - PACKET3_SET_CONFIG_REG_OFFSET) >> 2);
+@@ -449,6 +447,7 @@ int r600_blit_init(struct radeon_device *rdev)
+ u32 packet2s[16];
+ int num_packet2s = 0;
+
++ mutex_init(&rdev->r600_blit.mutex);
+ rdev->r600_blit.state_offset = 0;
+
+ if (rdev->family >= CHIP_RV770)
+@@ -473,9 +472,8 @@ int r600_blit_init(struct radeon_device *rdev)
+ obj_size += r6xx_ps_size * 4;
+ obj_size = ALIGN(obj_size, 256);
+
+- r = radeon_object_create(rdev, NULL, obj_size,
+- true, RADEON_GEM_DOMAIN_VRAM,
+- false, &rdev->r600_blit.shader_obj);
++ r = radeon_bo_create(rdev, NULL, obj_size, true, RADEON_GEM_DOMAIN_VRAM,
++ &rdev->r600_blit.shader_obj);
+ if (r) {
+ DRM_ERROR("r600 failed to allocate shader\n");
+ return r;
+@@ -485,12 +483,14 @@ int r600_blit_init(struct radeon_device *rdev)
+ obj_size,
+ rdev->r600_blit.vs_offset, rdev->r600_blit.ps_offset);
+
+- r = radeon_object_kmap(rdev->r600_blit.shader_obj, &ptr);
++ r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
++ if (unlikely(r != 0))
++ return r;
++ r = radeon_bo_kmap(rdev->r600_blit.shader_obj, &ptr);
+ if (r) {
+ DRM_ERROR("failed to map blit object %d\n", r);
+ return r;
+ }
+-
+ if (rdev->family >= CHIP_RV770)
+ memcpy_toio(ptr + rdev->r600_blit.state_offset,
+ r7xx_default_state, rdev->r600_blit.state_len * 4);
+@@ -500,19 +500,28 @@ int r600_blit_init(struct radeon_device *rdev)
+ if (num_packet2s)
+ memcpy_toio(ptr + rdev->r600_blit.state_offset + (rdev->r600_blit.state_len * 4),
+ packet2s, num_packet2s * 4);
+-
+-
+ memcpy(ptr + rdev->r600_blit.vs_offset, r6xx_vs, r6xx_vs_size * 4);
+ memcpy(ptr + rdev->r600_blit.ps_offset, r6xx_ps, r6xx_ps_size * 4);
+-
+- radeon_object_kunmap(rdev->r600_blit.shader_obj);
++ radeon_bo_kunmap(rdev->r600_blit.shader_obj);
++ radeon_bo_unreserve(rdev->r600_blit.shader_obj);
+ return 0;
+ }
+
+ void r600_blit_fini(struct radeon_device *rdev)
+ {
+- radeon_object_unpin(rdev->r600_blit.shader_obj);
+- radeon_object_unref(&rdev->r600_blit.shader_obj);
++ int r;
++
++ if (rdev->r600_blit.shader_obj == NULL)
++ return;
++ /* If we can't reserve the bo, unref should be enough to destroy
++ * it when it becomes idle.
++ */
++ r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
++ if (!r) {
++ radeon_bo_unpin(rdev->r600_blit.shader_obj);
++ radeon_bo_unreserve(rdev->r600_blit.shader_obj);
++ }
++ radeon_bo_unref(&rdev->r600_blit.shader_obj);
+ }
+
+ int r600_vb_ib_get(struct radeon_device *rdev)
+@@ -532,9 +541,6 @@ int r600_vb_ib_get(struct radeon_device *rdev)
+ void r600_vb_ib_put(struct radeon_device *rdev)
+ {
+ radeon_fence_emit(rdev, rdev->r600_blit.vb_ib->fence);
+- mutex_lock(&rdev->ib_pool.mutex);
+- list_add_tail(&rdev->r600_blit.vb_ib->list, &rdev->ib_pool.scheduled_ibs);
+- mutex_unlock(&rdev->ib_pool.mutex);
+ radeon_ib_free(rdev, &rdev->r600_blit.vb_ib);
+ }
+
+@@ -547,7 +553,8 @@ int r600_blit_prepare_copy(struct radeon_device *rdev, int size_bytes)
+ int dwords_per_loop = 76, num_loops;
+
+ r = r600_vb_ib_get(rdev);
+- WARN_ON(r);
++ if (r)
++ return r;
+
+ /* set_render_target emits 2 extra dwords on rv6xx */
+ if (rdev->family > CHIP_R600 && rdev->family < CHIP_RV770)
+@@ -569,11 +576,12 @@ int r600_blit_prepare_copy(struct radeon_device *rdev, int size_bytes)
+ ring_size = num_loops * dwords_per_loop;
+ /* set default + shaders */
+ ring_size += 40; /* shaders + def state */
+- ring_size += 3; /* fence emit for VB IB */
++ ring_size += 12; /* fence emit for VB IB */
+ ring_size += 5; /* done copy */
+- ring_size += 3; /* fence emit for done copy */
++ ring_size += 12; /* fence emit for done copy */
+ r = radeon_ring_lock(rdev, ring_size);
+- WARN_ON(r);
++ if (r)
++ return r;
+
+ set_default_state(rdev); /* 14 */
+ set_shaders(rdev); /* 26 */
+@@ -584,13 +592,6 @@ void r600_blit_done_copy(struct radeon_device *rdev, struct radeon_fence *fence)
+ {
+ int r;
+
+- radeon_ring_write(rdev, PACKET3(PACKET3_EVENT_WRITE, 0));
+- radeon_ring_write(rdev, CACHE_FLUSH_AND_INV_EVENT);
+- /* wait for 3D idle clean */
+- radeon_ring_write(rdev, PACKET3(PACKET3_SET_CONFIG_REG, 1));
+- radeon_ring_write(rdev, (WAIT_UNTIL - PACKET3_SET_CONFIG_REG_OFFSET) >> 2);
+- radeon_ring_write(rdev, WAIT_3D_IDLE_bit | WAIT_3D_IDLECLEAN_bit);
+-
+ if (rdev->r600_blit.vb_ib)
+ r600_vb_ib_put(rdev);
+
+diff --git a/drivers/gpu/drm/radeon/r600_blit_shaders.c b/drivers/gpu/drm/radeon/r600_blit_shaders.c
+index d745e81..a112c59 100644
+--- a/drivers/gpu/drm/radeon/r600_blit_shaders.c
++++ b/drivers/gpu/drm/radeon/r600_blit_shaders.c
+@@ -9,11 +9,6 @@ const u32 r6xx_default_state[] =
+ 0xc0012800,
+ 0x80000000,
+ 0x80000000,
+- 0xc0004600,
+- 0x00000016,
+- 0xc0016800,
+- 0x00000010,
+- 0x00028000,
+ 0xc0016800,
+ 0x00000010,
+ 0x00008000,
+@@ -531,11 +526,6 @@ const u32 r7xx_default_state[] =
+ 0xc0012800,
+ 0x80000000,
+ 0x80000000,
+- 0xc0004600,
+- 0x00000016,
+- 0xc0016800,
+- 0x00000010,
+- 0x00028000,
+ 0xc0016800,
+ 0x00000010,
+ 0x00008000,
+diff --git a/drivers/gpu/drm/radeon/r600_cs.c b/drivers/gpu/drm/radeon/r600_cs.c
+index 0d82076..e4c45ec 100644
+--- a/drivers/gpu/drm/radeon/r600_cs.c
++++ b/drivers/gpu/drm/radeon/r600_cs.c
+@@ -36,6 +36,10 @@ static int r600_cs_packet_next_reloc_nomm(struct radeon_cs_parser *p,
+ typedef int (*next_reloc_t)(struct radeon_cs_parser*, struct radeon_cs_reloc**);
+ static next_reloc_t r600_cs_packet_next_reloc = &r600_cs_packet_next_reloc_mm;
+
++struct r600_cs_track {
++ u32 cb_color0_base_last;
++};
++
+ /**
+ * r600_cs_packet_parse() - parse cp packet and point ib index to next packet
+ * @parser: parser structure holding parsing context.
+@@ -170,13 +174,35 @@ static int r600_cs_packet_next_reloc_nomm(struct radeon_cs_parser *p,
+ idx, relocs_chunk->length_dw);
+ return -EINVAL;
+ }
+- *cs_reloc = &p->relocs[0];
++ *cs_reloc = p->relocs;
+ (*cs_reloc)->lobj.gpu_offset = (u64)relocs_chunk->kdata[idx + 3] << 32;
+ (*cs_reloc)->lobj.gpu_offset |= relocs_chunk->kdata[idx + 0];
+ return 0;
+ }
+
+ /**
++ * r600_cs_packet_next_is_pkt3_nop() - test if next packet is packet3 nop for reloc
++ * @parser: parser structure holding parsing context.
++ *
++ * Check next packet is relocation packet3, do bo validation and compute
++ * GPU offset using the provided start.
++ **/
++static inline int r600_cs_packet_next_is_pkt3_nop(struct radeon_cs_parser *p)
++{
++ struct radeon_cs_packet p3reloc;
++ int r;
++
++ r = r600_cs_packet_parse(p, &p3reloc, p->idx);
++ if (r) {
++ return 0;
++ }
++ if (p3reloc.type != PACKET_TYPE3 || p3reloc.opcode != PACKET3_NOP) {
++ return 0;
++ }
++ return 1;
++}
++
++/**
+ * r600_cs_packet_next_vline() - parse userspace VLINE packet
+ * @parser: parser structure holding parsing context.
+ *
+@@ -337,6 +363,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
+ struct radeon_cs_packet *pkt)
+ {
+ struct radeon_cs_reloc *reloc;
++ struct r600_cs_track *track;
+ volatile u32 *ib;
+ unsigned idx;
+ unsigned i;
+@@ -344,6 +371,7 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
+ int r;
+ u32 idx_value;
+
++ track = (struct r600_cs_track *)p->track;
+ ib = p->ib->ptr;
+ idx = pkt->idx + 1;
+ idx_value = radeon_get_ib_value(p, idx);
+@@ -503,9 +531,60 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
+ for (i = 0; i < pkt->count; i++) {
+ reg = start_reg + (4 * i);
+ switch (reg) {
++ /* This register were added late, there is userspace
++ * which does provide relocation for those but set
++ * 0 offset. In order to avoid breaking old userspace
++ * we detect this and set address to point to last
++ * CB_COLOR0_BASE, note that if userspace doesn't set
++ * CB_COLOR0_BASE before this register we will report
++ * error. Old userspace always set CB_COLOR0_BASE
++ * before any of this.
++ */
++ case R_0280E0_CB_COLOR0_FRAG:
++ case R_0280E4_CB_COLOR1_FRAG:
++ case R_0280E8_CB_COLOR2_FRAG:
++ case R_0280EC_CB_COLOR3_FRAG:
++ case R_0280F0_CB_COLOR4_FRAG:
++ case R_0280F4_CB_COLOR5_FRAG:
++ case R_0280F8_CB_COLOR6_FRAG:
++ case R_0280FC_CB_COLOR7_FRAG:
++ case R_0280C0_CB_COLOR0_TILE:
++ case R_0280C4_CB_COLOR1_TILE:
++ case R_0280C8_CB_COLOR2_TILE:
++ case R_0280CC_CB_COLOR3_TILE:
++ case R_0280D0_CB_COLOR4_TILE:
++ case R_0280D4_CB_COLOR5_TILE:
++ case R_0280D8_CB_COLOR6_TILE:
++ case R_0280DC_CB_COLOR7_TILE:
++ if (!r600_cs_packet_next_is_pkt3_nop(p)) {
++ if (!track->cb_color0_base_last) {
++ dev_err(p->dev, "Broken old userspace ? no cb_color0_base supplied before trying to write 0x%08X\n", reg);
++ return -EINVAL;
++ }
++ ib[idx+1+i] = track->cb_color0_base_last;
++ printk_once(KERN_WARNING "radeon: You have old & broken userspace "
++ "please consider updating mesa & xf86-video-ati\n");
++ } else {
++ r = r600_cs_packet_next_reloc(p, &reloc);
++ if (r) {
++ dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg);
++ return -EINVAL;
++ }
++ ib[idx+1+i] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
++ }
++ break;
+ case DB_DEPTH_BASE:
+ case DB_HTILE_DATA_BASE:
+ case CB_COLOR0_BASE:
++ r = r600_cs_packet_next_reloc(p, &reloc);
++ if (r) {
++ DRM_ERROR("bad SET_CONTEXT_REG "
++ "0x%04X\n", reg);
++ return -EINVAL;
++ }
++ ib[idx+1+i] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
++ track->cb_color0_base_last = ib[idx+1+i];
++ break;
+ case CB_COLOR1_BASE:
+ case CB_COLOR2_BASE:
+ case CB_COLOR3_BASE:
+@@ -678,8 +757,11 @@ static int r600_packet3_check(struct radeon_cs_parser *p,
+ int r600_cs_parse(struct radeon_cs_parser *p)
+ {
+ struct radeon_cs_packet pkt;
++ struct r600_cs_track *track;
+ int r;
+
++ track = kzalloc(sizeof(*track), GFP_KERNEL);
++ p->track = track;
+ do {
+ r = r600_cs_packet_parse(p, &pkt, p->idx);
+ if (r) {
+@@ -717,7 +799,7 @@ static int r600_cs_parser_relocs_legacy(struct radeon_cs_parser *p)
+ if (p->chunk_relocs_idx == -1) {
+ return 0;
+ }
+- p->relocs = kcalloc(1, sizeof(struct radeon_cs_reloc), GFP_KERNEL);
++ p->relocs = kzalloc(sizeof(struct radeon_cs_reloc), GFP_KERNEL);
+ if (p->relocs == NULL) {
+ return -ENOMEM;
+ }
+@@ -757,6 +839,7 @@ int r600_cs_legacy(struct drm_device *dev, void *data, struct drm_file *filp,
+ /* initialize parser */
+ memset(&parser, 0, sizeof(struct radeon_cs_parser));
+ parser.filp = filp;
++ parser.dev = &dev->pdev->dev;
+ parser.rdev = NULL;
+ parser.family = family;
+ parser.ib = &fake_ib;
+diff --git a/drivers/gpu/drm/radeon/r600_hdmi.c b/drivers/gpu/drm/radeon/r600_hdmi.c
+new file mode 100644
+index 0000000..fcc949d
+--- /dev/null
++++ b/drivers/gpu/drm/radeon/r600_hdmi.c
+@@ -0,0 +1,506 @@
++/*
++ * Copyright 2008 Advanced Micro Devices, Inc.
++ * Copyright 2008 Red Hat Inc.
++ * Copyright 2009 Christian König.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be included in
++ * all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ *
++ * Authors: Christian König
++ */
++#include "drmP.h"
++#include "radeon_drm.h"
++#include "radeon.h"
++#include "atom.h"
++
++/*
++ * HDMI color format
++ */
++enum r600_hdmi_color_format {
++ RGB = 0,
++ YCC_422 = 1,
++ YCC_444 = 2
++};
++
++/*
++ * IEC60958 status bits
++ */
++enum r600_hdmi_iec_status_bits {
++ AUDIO_STATUS_DIG_ENABLE = 0x01,
++ AUDIO_STATUS_V = 0x02,
++ AUDIO_STATUS_VCFG = 0x04,
++ AUDIO_STATUS_EMPHASIS = 0x08,
++ AUDIO_STATUS_COPYRIGHT = 0x10,
++ AUDIO_STATUS_NONAUDIO = 0x20,
++ AUDIO_STATUS_PROFESSIONAL = 0x40,
++ AUDIO_STATUS_LEVEL = 0x80
++};
++
++struct {
++ uint32_t Clock;
++
++ int N_32kHz;
++ int CTS_32kHz;
++
++ int N_44_1kHz;
++ int CTS_44_1kHz;
++
++ int N_48kHz;
++ int CTS_48kHz;
++
++} r600_hdmi_ACR[] = {
++ /* 32kHz 44.1kHz 48kHz */
++ /* Clock N CTS N CTS N CTS */
++ { 25174, 4576, 28125, 7007, 31250, 6864, 28125 }, /* 25,20/1.001 MHz */
++ { 25200, 4096, 25200, 6272, 28000, 6144, 25200 }, /* 25.20 MHz */
++ { 27000, 4096, 27000, 6272, 30000, 6144, 27000 }, /* 27.00 MHz */
++ { 27027, 4096, 27027, 6272, 30030, 6144, 27027 }, /* 27.00*1.001 MHz */
++ { 54000, 4096, 54000, 6272, 60000, 6144, 54000 }, /* 54.00 MHz */
++ { 54054, 4096, 54054, 6272, 60060, 6144, 54054 }, /* 54.00*1.001 MHz */
++ { 74175, 11648, 210937, 17836, 234375, 11648, 140625 }, /* 74.25/1.001 MHz */
++ { 74250, 4096, 74250, 6272, 82500, 6144, 74250 }, /* 74.25 MHz */
++ { 148351, 11648, 421875, 8918, 234375, 5824, 140625 }, /* 148.50/1.001 MHz */
++ { 148500, 4096, 148500, 6272, 165000, 6144, 148500 }, /* 148.50 MHz */
++ { 0, 4096, 0, 6272, 0, 6144, 0 } /* Other */
++};
++
++/*
++ * calculate CTS value if it's not found in the table
++ */
++static void r600_hdmi_calc_CTS(uint32_t clock, int *CTS, int N, int freq)
++{
++ if (*CTS == 0)
++ *CTS = clock*N/(128*freq)*1000;
++ DRM_DEBUG("Using ACR timing N=%d CTS=%d for frequency %d\n",
++ N, *CTS, freq);
++}
++
++/*
++ * update the N and CTS parameters for a given pixel clock rate
++ */
++static void r600_hdmi_update_ACR(struct drm_encoder *encoder, uint32_t clock)
++{
++ struct drm_device *dev = encoder->dev;
++ struct radeon_device *rdev = dev->dev_private;
++ uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset;
++ int CTS;
++ int N;
++ int i;
++
++ for (i = 0; r600_hdmi_ACR[i].Clock != clock && r600_hdmi_ACR[i].Clock != 0; i++);
++
++ CTS = r600_hdmi_ACR[i].CTS_32kHz;
++ N = r600_hdmi_ACR[i].N_32kHz;
++ r600_hdmi_calc_CTS(clock, &CTS, N, 32000);
++ WREG32(offset+R600_HDMI_32kHz_CTS, CTS << 12);
++ WREG32(offset+R600_HDMI_32kHz_N, N);
++
++ CTS = r600_hdmi_ACR[i].CTS_44_1kHz;
++ N = r600_hdmi_ACR[i].N_44_1kHz;
++ r600_hdmi_calc_CTS(clock, &CTS, N, 44100);
++ WREG32(offset+R600_HDMI_44_1kHz_CTS, CTS << 12);
++ WREG32(offset+R600_HDMI_44_1kHz_N, N);
++
++ CTS = r600_hdmi_ACR[i].CTS_48kHz;
++ N = r600_hdmi_ACR[i].N_48kHz;
++ r600_hdmi_calc_CTS(clock, &CTS, N, 48000);
++ WREG32(offset+R600_HDMI_48kHz_CTS, CTS << 12);
++ WREG32(offset+R600_HDMI_48kHz_N, N);
++}
++
++/*
++ * calculate the crc for a given info frame
++ */
++static void r600_hdmi_infoframe_checksum(uint8_t packetType,
++ uint8_t versionNumber,
++ uint8_t length,
++ uint8_t *frame)
++{
++ int i;
++ frame[0] = packetType + versionNumber + length;
++ for (i = 1; i <= length; i++)
++ frame[0] += frame[i];
++ frame[0] = 0x100 - frame[0];
++}
++
++/*
++ * build a HDMI Video Info Frame
++ */
++static void r600_hdmi_videoinfoframe(
++ struct drm_encoder *encoder,
++ enum r600_hdmi_color_format color_format,
++ int active_information_present,
++ uint8_t active_format_aspect_ratio,
++ uint8_t scan_information,
++ uint8_t colorimetry,
++ uint8_t ex_colorimetry,
++ uint8_t quantization,
++ int ITC,
++ uint8_t picture_aspect_ratio,
++ uint8_t video_format_identification,
++ uint8_t pixel_repetition,
++ uint8_t non_uniform_picture_scaling,
++ uint8_t bar_info_data_valid,
++ uint16_t top_bar,
++ uint16_t bottom_bar,
++ uint16_t left_bar,
++ uint16_t right_bar
++)
++{
++ struct drm_device *dev = encoder->dev;
++ struct radeon_device *rdev = dev->dev_private;
++ uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset;
++
++ uint8_t frame[14];
++
++ frame[0x0] = 0;
++ frame[0x1] =
++ (scan_information & 0x3) |
++ ((bar_info_data_valid & 0x3) << 2) |
++ ((active_information_present & 0x1) << 4) |
++ ((color_format & 0x3) << 5);
++ frame[0x2] =
++ (active_format_aspect_ratio & 0xF) |
++ ((picture_aspect_ratio & 0x3) << 4) |
++ ((colorimetry & 0x3) << 6);
++ frame[0x3] =
++ (non_uniform_picture_scaling & 0x3) |
++ ((quantization & 0x3) << 2) |
++ ((ex_colorimetry & 0x7) << 4) |
++ ((ITC & 0x1) << 7);
++ frame[0x4] = (video_format_identification & 0x7F);
++ frame[0x5] = (pixel_repetition & 0xF);
++ frame[0x6] = (top_bar & 0xFF);
++ frame[0x7] = (top_bar >> 8);
++ frame[0x8] = (bottom_bar & 0xFF);
++ frame[0x9] = (bottom_bar >> 8);
++ frame[0xA] = (left_bar & 0xFF);
++ frame[0xB] = (left_bar >> 8);
++ frame[0xC] = (right_bar & 0xFF);
++ frame[0xD] = (right_bar >> 8);
++
++ r600_hdmi_infoframe_checksum(0x82, 0x02, 0x0D, frame);
++
++ WREG32(offset+R600_HDMI_VIDEOINFOFRAME_0,
++ frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24));
++ WREG32(offset+R600_HDMI_VIDEOINFOFRAME_1,
++ frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x7] << 24));
++ WREG32(offset+R600_HDMI_VIDEOINFOFRAME_2,
++ frame[0x8] | (frame[0x9] << 8) | (frame[0xA] << 16) | (frame[0xB] << 24));
++ WREG32(offset+R600_HDMI_VIDEOINFOFRAME_3,
++ frame[0xC] | (frame[0xD] << 8));
++}
++
++/*
++ * build a Audio Info Frame
++ */
++static void r600_hdmi_audioinfoframe(
++ struct drm_encoder *encoder,
++ uint8_t channel_count,
++ uint8_t coding_type,
++ uint8_t sample_size,
++ uint8_t sample_frequency,
++ uint8_t format,
++ uint8_t channel_allocation,
++ uint8_t level_shift,
++ int downmix_inhibit
++)
++{
++ struct drm_device *dev = encoder->dev;
++ struct radeon_device *rdev = dev->dev_private;
++ uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset;
++
++ uint8_t frame[11];
++
++ frame[0x0] = 0;
++ frame[0x1] = (channel_count & 0x7) | ((coding_type & 0xF) << 4);
++ frame[0x2] = (sample_size & 0x3) | ((sample_frequency & 0x7) << 2);
++ frame[0x3] = format;
++ frame[0x4] = channel_allocation;
++ frame[0x5] = ((level_shift & 0xF) << 3) | ((downmix_inhibit & 0x1) << 7);
++ frame[0x6] = 0;
++ frame[0x7] = 0;
++ frame[0x8] = 0;
++ frame[0x9] = 0;
++ frame[0xA] = 0;
++
++ r600_hdmi_infoframe_checksum(0x84, 0x01, 0x0A, frame);
++
++ WREG32(offset+R600_HDMI_AUDIOINFOFRAME_0,
++ frame[0x0] | (frame[0x1] << 8) | (frame[0x2] << 16) | (frame[0x3] << 24));
++ WREG32(offset+R600_HDMI_AUDIOINFOFRAME_1,
++ frame[0x4] | (frame[0x5] << 8) | (frame[0x6] << 16) | (frame[0x8] << 24));
++}
++
++/*
++ * test if audio buffer is filled enough to start playing
++ */
++static int r600_hdmi_is_audio_buffer_filled(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++ struct radeon_device *rdev = dev->dev_private;
++ uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset;
++
++ return (RREG32(offset+R600_HDMI_STATUS) & 0x10) != 0;
++}
++
++/*
++ * have buffer status changed since last call?
++ */
++int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder)
++{
++ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
++ int status, result;
++
++ if (!radeon_encoder->hdmi_offset)
++ return 0;
++
++ status = r600_hdmi_is_audio_buffer_filled(encoder);
++ result = radeon_encoder->hdmi_buffer_status != status;
++ radeon_encoder->hdmi_buffer_status = status;
++
++ return result;
++}
++
++/*
++ * write the audio workaround status to the hardware
++ */
++void r600_hdmi_audio_workaround(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++ struct radeon_device *rdev = dev->dev_private;
++ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
++ uint32_t offset = radeon_encoder->hdmi_offset;
++
++ if (!offset)
++ return;
++
++ if (r600_hdmi_is_audio_buffer_filled(encoder)) {
++ /* disable audio workaround and start delivering of audio frames */
++ WREG32_P(offset+R600_HDMI_CNTL, 0x00000001, ~0x00001001);
++
++ } else if (radeon_encoder->hdmi_audio_workaround) {
++ /* enable audio workaround and start delivering of audio frames */
++ WREG32_P(offset+R600_HDMI_CNTL, 0x00001001, ~0x00001001);
++
++ } else {
++ /* disable audio workaround and stop delivering of audio frames */
++ WREG32_P(offset+R600_HDMI_CNTL, 0x00000000, ~0x00001001);
++ }
++}
++
++
++/*
++ * update the info frames with the data from the current display mode
++ */
++void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode)
++{
++ struct drm_device *dev = encoder->dev;
++ struct radeon_device *rdev = dev->dev_private;
++ uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset;
++
++ if (!offset)
++ return;
++
++ r600_audio_set_clock(encoder, mode->clock);
++
++ WREG32(offset+R600_HDMI_UNKNOWN_0, 0x1000);
++ WREG32(offset+R600_HDMI_UNKNOWN_1, 0x0);
++ WREG32(offset+R600_HDMI_UNKNOWN_2, 0x1000);
++
++ r600_hdmi_update_ACR(encoder, mode->clock);
++
++ WREG32(offset+R600_HDMI_VIDEOCNTL, 0x13);
++
++ WREG32(offset+R600_HDMI_VERSION, 0x202);
++
++ r600_hdmi_videoinfoframe(encoder, RGB, 0, 0, 0, 0,
++ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
++
++ /* it's unknown what these bits do excatly, but it's indeed quite usefull for debugging */
++ WREG32(offset+R600_HDMI_AUDIO_DEBUG_0, 0x00FFFFFF);
++ WREG32(offset+R600_HDMI_AUDIO_DEBUG_1, 0x007FFFFF);
++ WREG32(offset+R600_HDMI_AUDIO_DEBUG_2, 0x00000001);
++ WREG32(offset+R600_HDMI_AUDIO_DEBUG_3, 0x00000001);
++
++ r600_hdmi_audio_workaround(encoder);
++
++ /* audio packets per line, does anyone know how to calc this ? */
++ WREG32_P(offset+R600_HDMI_CNTL, 0x00040000, ~0x001F0000);
++
++ /* update? reset? don't realy know */
++ WREG32_P(offset+R600_HDMI_CNTL, 0x14000000, ~0x14000000);
++}
++
++/*
++ * update settings with current parameters from audio engine
++ */
++void r600_hdmi_update_audio_settings(struct drm_encoder *encoder,
++ int channels,
++ int rate,
++ int bps,
++ uint8_t status_bits,
++ uint8_t category_code)
++{
++ struct drm_device *dev = encoder->dev;
++ struct radeon_device *rdev = dev->dev_private;
++ uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset;
++
++ uint32_t iec;
++
++ if (!offset)
++ return;
++
++ DRM_DEBUG("%s with %d channels, %d Hz sampling rate, %d bits per sample,\n",
++ r600_hdmi_is_audio_buffer_filled(encoder) ? "playing" : "stopped",
++ channels, rate, bps);
++ DRM_DEBUG("0x%02X IEC60958 status bits and 0x%02X category code\n",
++ (int)status_bits, (int)category_code);
++
++ iec = 0;
++ if (status_bits & AUDIO_STATUS_PROFESSIONAL)
++ iec |= 1 << 0;
++ if (status_bits & AUDIO_STATUS_NONAUDIO)
++ iec |= 1 << 1;
++ if (status_bits & AUDIO_STATUS_COPYRIGHT)
++ iec |= 1 << 2;
++ if (status_bits & AUDIO_STATUS_EMPHASIS)
++ iec |= 1 << 3;
++
++ iec |= category_code << 8;
++
++ switch (rate) {
++ case 32000: iec |= 0x3 << 24; break;
++ case 44100: iec |= 0x0 << 24; break;
++ case 88200: iec |= 0x8 << 24; break;
++ case 176400: iec |= 0xc << 24; break;
++ case 48000: iec |= 0x2 << 24; break;
++ case 96000: iec |= 0xa << 24; break;
++ case 192000: iec |= 0xe << 24; break;
++ }
++
++ WREG32(offset+R600_HDMI_IEC60958_1, iec);
++
++ iec = 0;
++ switch (bps) {
++ case 16: iec |= 0x2; break;
++ case 20: iec |= 0x3; break;
++ case 24: iec |= 0xb; break;
++ }
++ if (status_bits & AUDIO_STATUS_V)
++ iec |= 0x5 << 16;
++
++ WREG32_P(offset+R600_HDMI_IEC60958_2, iec, ~0x5000f);
++
++ /* 0x021 or 0x031 sets the audio frame length */
++ WREG32(offset+R600_HDMI_AUDIOCNTL, 0x31);
++ r600_hdmi_audioinfoframe(encoder, channels-1, 0, 0, 0, 0, 0, 0, 0);
++
++ r600_hdmi_audio_workaround(encoder);
++
++ /* update? reset? don't realy know */
++ WREG32_P(offset+R600_HDMI_CNTL, 0x04000000, ~0x04000000);
++}
++
++/*
++ * enable/disable the HDMI engine
++ */
++void r600_hdmi_enable(struct drm_encoder *encoder, int enable)
++{
++ struct drm_device *dev = encoder->dev;
++ struct radeon_device *rdev = dev->dev_private;
++ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
++ uint32_t offset = to_radeon_encoder(encoder)->hdmi_offset;
++
++ if (!offset)
++ return;
++
++ DRM_DEBUG("%s HDMI interface @ 0x%04X\n", enable ? "Enabling" : "Disabling", offset);
++
++ /* some version of atombios ignore the enable HDMI flag
++ * so enabling/disabling HDMI was moved here for TMDS1+2 */
++ switch (radeon_encoder->encoder_id) {
++ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
++ WREG32_P(AVIVO_TMDSA_CNTL, enable ? 0x4 : 0x0, ~0x4);
++ WREG32(offset+R600_HDMI_ENABLE, enable ? 0x101 : 0x0);
++ break;
++
++ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
++ WREG32_P(AVIVO_LVTMA_CNTL, enable ? 0x4 : 0x0, ~0x4);
++ WREG32(offset+R600_HDMI_ENABLE, enable ? 0x105 : 0x0);
++ break;
++
++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
++ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
++ /* This part is doubtfull in my opinion */
++ WREG32(offset+R600_HDMI_ENABLE, enable ? 0x110 : 0x0);
++ break;
++
++ default:
++ DRM_ERROR("unknown HDMI output type\n");
++ break;
++ }
++}
++
++/*
++ * determin at which register offset the HDMI encoder is
++ */
++void r600_hdmi_init(struct drm_encoder *encoder)
++{
++ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
++
++ switch (radeon_encoder->encoder_id) {
++ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
++ radeon_encoder->hdmi_offset = R600_HDMI_TMDS1;
++ break;
++
++ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
++ switch (r600_audio_tmds_index(encoder)) {
++ case 0:
++ radeon_encoder->hdmi_offset = R600_HDMI_TMDS1;
++ break;
++ case 1:
++ radeon_encoder->hdmi_offset = R600_HDMI_TMDS2;
++ break;
++ default:
++ radeon_encoder->hdmi_offset = 0;
++ break;
++ }
++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
++ radeon_encoder->hdmi_offset = R600_HDMI_TMDS2;
++ break;
++
++ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
++ radeon_encoder->hdmi_offset = R600_HDMI_DIG;
++ break;
++
++ default:
++ radeon_encoder->hdmi_offset = 0;
++ break;
++ }
++
++ DRM_DEBUG("using HDMI engine at offset 0x%04X for encoder 0x%x\n",
++ radeon_encoder->hdmi_offset, radeon_encoder->encoder_id);
++
++ /* TODO: make this configureable */
++ radeon_encoder->hdmi_audio_workaround = 0;
++}
+diff --git a/drivers/gpu/drm/radeon/r600_reg.h b/drivers/gpu/drm/radeon/r600_reg.h
+index e2d1f5f..d0e28ff 100644
+--- a/drivers/gpu/drm/radeon/r600_reg.h
++++ b/drivers/gpu/drm/radeon/r600_reg.h
+@@ -110,5 +110,79 @@
+ #define R600_BIOS_6_SCRATCH 0x173c
+ #define R600_BIOS_7_SCRATCH 0x1740
+
++/* Audio, these regs were reverse enginered,
++ * so the chance is high that the naming is wrong
++ * R6xx+ ??? */
++
++/* Audio clocks */
++#define R600_AUDIO_PLL1_MUL 0x0514
++#define R600_AUDIO_PLL1_DIV 0x0518
++#define R600_AUDIO_PLL2_MUL 0x0524
++#define R600_AUDIO_PLL2_DIV 0x0528
++#define R600_AUDIO_CLK_SRCSEL 0x0534
++
++/* Audio general */
++#define R600_AUDIO_ENABLE 0x7300
++#define R600_AUDIO_TIMING 0x7344
++
++/* Audio params */
++#define R600_AUDIO_VENDOR_ID 0x7380
++#define R600_AUDIO_REVISION_ID 0x7384
++#define R600_AUDIO_ROOT_NODE_COUNT 0x7388
++#define R600_AUDIO_NID1_NODE_COUNT 0x738c
++#define R600_AUDIO_NID1_TYPE 0x7390
++#define R600_AUDIO_SUPPORTED_SIZE_RATE 0x7394
++#define R600_AUDIO_SUPPORTED_CODEC 0x7398
++#define R600_AUDIO_SUPPORTED_POWER_STATES 0x739c
++#define R600_AUDIO_NID2_CAPS 0x73a0
++#define R600_AUDIO_NID3_CAPS 0x73a4
++#define R600_AUDIO_NID3_PIN_CAPS 0x73a8
++
++/* Audio conn list */
++#define R600_AUDIO_CONN_LIST_LEN 0x73ac
++#define R600_AUDIO_CONN_LIST 0x73b0
++
++/* Audio verbs */
++#define R600_AUDIO_RATE_BPS_CHANNEL 0x73c0
++#define R600_AUDIO_PLAYING 0x73c4
++#define R600_AUDIO_IMPLEMENTATION_ID 0x73c8
++#define R600_AUDIO_CONFIG_DEFAULT 0x73cc
++#define R600_AUDIO_PIN_SENSE 0x73d0
++#define R600_AUDIO_PIN_WIDGET_CNTL 0x73d4
++#define R600_AUDIO_STATUS_BITS 0x73d8
++
++/* HDMI base register addresses */
++#define R600_HDMI_TMDS1 0x7400
++#define R600_HDMI_TMDS2 0x7700
++#define R600_HDMI_DIG 0x7800
++
++/* HDMI registers */
++#define R600_HDMI_ENABLE 0x00
++#define R600_HDMI_STATUS 0x04
++#define R600_HDMI_CNTL 0x08
++#define R600_HDMI_UNKNOWN_0 0x0C
++#define R600_HDMI_AUDIOCNTL 0x10
++#define R600_HDMI_VIDEOCNTL 0x14
++#define R600_HDMI_VERSION 0x18
++#define R600_HDMI_UNKNOWN_1 0x28
++#define R600_HDMI_VIDEOINFOFRAME_0 0x54
++#define R600_HDMI_VIDEOINFOFRAME_1 0x58
++#define R600_HDMI_VIDEOINFOFRAME_2 0x5c
++#define R600_HDMI_VIDEOINFOFRAME_3 0x60
++#define R600_HDMI_32kHz_CTS 0xac
++#define R600_HDMI_32kHz_N 0xb0
++#define R600_HDMI_44_1kHz_CTS 0xb4
++#define R600_HDMI_44_1kHz_N 0xb8
++#define R600_HDMI_48kHz_CTS 0xbc
++#define R600_HDMI_48kHz_N 0xc0
++#define R600_HDMI_AUDIOINFOFRAME_0 0xcc
++#define R600_HDMI_AUDIOINFOFRAME_1 0xd0
++#define R600_HDMI_IEC60958_1 0xd4
++#define R600_HDMI_IEC60958_2 0xd8
++#define R600_HDMI_UNKNOWN_2 0xdc
++#define R600_HDMI_AUDIO_DEBUG_0 0xe0
++#define R600_HDMI_AUDIO_DEBUG_1 0xe4
++#define R600_HDMI_AUDIO_DEBUG_2 0xe8
++#define R600_HDMI_AUDIO_DEBUG_3 0xec
+
+ #endif
+diff --git a/drivers/gpu/drm/radeon/r600d.h b/drivers/gpu/drm/radeon/r600d.h
+index 27ab428..3048088 100644
+--- a/drivers/gpu/drm/radeon/r600d.h
++++ b/drivers/gpu/drm/radeon/r600d.h
+@@ -456,7 +456,215 @@
+ #define WAIT_2D_IDLECLEAN_bit (1 << 16)
+ #define WAIT_3D_IDLECLEAN_bit (1 << 17)
+
+-
++#define IH_RB_CNTL 0x3e00
++# define IH_RB_ENABLE (1 << 0)
++# define IH_IB_SIZE(x) ((x) << 1) /* log2 */
++# define IH_RB_FULL_DRAIN_ENABLE (1 << 6)
++# define IH_WPTR_WRITEBACK_ENABLE (1 << 8)
++# define IH_WPTR_WRITEBACK_TIMER(x) ((x) << 9) /* log2 */
++# define IH_WPTR_OVERFLOW_ENABLE (1 << 16)
++# define IH_WPTR_OVERFLOW_CLEAR (1 << 31)
++#define IH_RB_BASE 0x3e04
++#define IH_RB_RPTR 0x3e08
++#define IH_RB_WPTR 0x3e0c
++# define RB_OVERFLOW (1 << 0)
++# define WPTR_OFFSET_MASK 0x3fffc
++#define IH_RB_WPTR_ADDR_HI 0x3e10
++#define IH_RB_WPTR_ADDR_LO 0x3e14
++#define IH_CNTL 0x3e18
++# define ENABLE_INTR (1 << 0)
++# define IH_MC_SWAP(x) ((x) << 2)
++# define IH_MC_SWAP_NONE 0
++# define IH_MC_SWAP_16BIT 1
++# define IH_MC_SWAP_32BIT 2
++# define IH_MC_SWAP_64BIT 3
++# define RPTR_REARM (1 << 4)
++# define MC_WRREQ_CREDIT(x) ((x) << 15)
++# define MC_WR_CLEAN_CNT(x) ((x) << 20)
++
++#define RLC_CNTL 0x3f00
++# define RLC_ENABLE (1 << 0)
++#define RLC_HB_BASE 0x3f10
++#define RLC_HB_CNTL 0x3f0c
++#define RLC_HB_RPTR 0x3f20
++#define RLC_HB_WPTR 0x3f1c
++#define RLC_HB_WPTR_LSB_ADDR 0x3f14
++#define RLC_HB_WPTR_MSB_ADDR 0x3f18
++#define RLC_MC_CNTL 0x3f44
++#define RLC_UCODE_CNTL 0x3f48
++#define RLC_UCODE_ADDR 0x3f2c
++#define RLC_UCODE_DATA 0x3f30
++
++#define SRBM_SOFT_RESET 0xe60
++# define SOFT_RESET_RLC (1 << 13)
++
++#define CP_INT_CNTL 0xc124
++# define CNTX_BUSY_INT_ENABLE (1 << 19)
++# define CNTX_EMPTY_INT_ENABLE (1 << 20)
++# define SCRATCH_INT_ENABLE (1 << 25)
++# define TIME_STAMP_INT_ENABLE (1 << 26)
++# define IB2_INT_ENABLE (1 << 29)
++# define IB1_INT_ENABLE (1 << 30)
++# define RB_INT_ENABLE (1 << 31)
++#define CP_INT_STATUS 0xc128
++# define SCRATCH_INT_STAT (1 << 25)
++# define TIME_STAMP_INT_STAT (1 << 26)
++# define IB2_INT_STAT (1 << 29)
++# define IB1_INT_STAT (1 << 30)
++# define RB_INT_STAT (1 << 31)
++
++#define GRBM_INT_CNTL 0x8060
++# define RDERR_INT_ENABLE (1 << 0)
++# define WAIT_COUNT_TIMEOUT_INT_ENABLE (1 << 1)
++# define GUI_IDLE_INT_ENABLE (1 << 19)
++
++#define INTERRUPT_CNTL 0x5468
++# define IH_DUMMY_RD_OVERRIDE (1 << 0)
++# define IH_DUMMY_RD_EN (1 << 1)
++# define IH_REQ_NONSNOOP_EN (1 << 3)
++# define GEN_IH_INT_EN (1 << 8)
++#define INTERRUPT_CNTL2 0x546c
++
++#define D1MODE_VBLANK_STATUS 0x6534
++#define D2MODE_VBLANK_STATUS 0x6d34
++# define DxMODE_VBLANK_OCCURRED (1 << 0)
++# define DxMODE_VBLANK_ACK (1 << 4)
++# define DxMODE_VBLANK_STAT (1 << 12)
++# define DxMODE_VBLANK_INTERRUPT (1 << 16)
++# define DxMODE_VBLANK_INTERRUPT_TYPE (1 << 17)
++#define D1MODE_VLINE_STATUS 0x653c
++#define D2MODE_VLINE_STATUS 0x6d3c
++# define DxMODE_VLINE_OCCURRED (1 << 0)
++# define DxMODE_VLINE_ACK (1 << 4)
++# define DxMODE_VLINE_STAT (1 << 12)
++# define DxMODE_VLINE_INTERRUPT (1 << 16)
++# define DxMODE_VLINE_INTERRUPT_TYPE (1 << 17)
++#define DxMODE_INT_MASK 0x6540
++# define D1MODE_VBLANK_INT_MASK (1 << 0)
++# define D1MODE_VLINE_INT_MASK (1 << 4)
++# define D2MODE_VBLANK_INT_MASK (1 << 8)
++# define D2MODE_VLINE_INT_MASK (1 << 12)
++#define DCE3_DISP_INTERRUPT_STATUS 0x7ddc
++# define DC_HPD1_INTERRUPT (1 << 18)
++# define DC_HPD2_INTERRUPT (1 << 19)
++#define DISP_INTERRUPT_STATUS 0x7edc
++# define LB_D1_VLINE_INTERRUPT (1 << 2)
++# define LB_D2_VLINE_INTERRUPT (1 << 3)
++# define LB_D1_VBLANK_INTERRUPT (1 << 4)
++# define LB_D2_VBLANK_INTERRUPT (1 << 5)
++# define DACA_AUTODETECT_INTERRUPT (1 << 16)
++# define DACB_AUTODETECT_INTERRUPT (1 << 17)
++# define DC_HOT_PLUG_DETECT1_INTERRUPT (1 << 18)
++# define DC_HOT_PLUG_DETECT2_INTERRUPT (1 << 19)
++# define DC_I2C_SW_DONE_INTERRUPT (1 << 20)
++# define DC_I2C_HW_DONE_INTERRUPT (1 << 21)
++#define DISP_INTERRUPT_STATUS_CONTINUE 0x7ee8
++#define DCE3_DISP_INTERRUPT_STATUS_CONTINUE 0x7de8
++# define DC_HPD4_INTERRUPT (1 << 14)
++# define DC_HPD4_RX_INTERRUPT (1 << 15)
++# define DC_HPD3_INTERRUPT (1 << 28)
++# define DC_HPD1_RX_INTERRUPT (1 << 29)
++# define DC_HPD2_RX_INTERRUPT (1 << 30)
++#define DCE3_DISP_INTERRUPT_STATUS_CONTINUE2 0x7dec
++# define DC_HPD3_RX_INTERRUPT (1 << 0)
++# define DIGA_DP_VID_STREAM_DISABLE_INTERRUPT (1 << 1)
++# define DIGA_DP_STEER_FIFO_OVERFLOW_INTERRUPT (1 << 2)
++# define DIGB_DP_VID_STREAM_DISABLE_INTERRUPT (1 << 3)
++# define DIGB_DP_STEER_FIFO_OVERFLOW_INTERRUPT (1 << 4)
++# define AUX1_SW_DONE_INTERRUPT (1 << 5)
++# define AUX1_LS_DONE_INTERRUPT (1 << 6)
++# define AUX2_SW_DONE_INTERRUPT (1 << 7)
++# define AUX2_LS_DONE_INTERRUPT (1 << 8)
++# define AUX3_SW_DONE_INTERRUPT (1 << 9)
++# define AUX3_LS_DONE_INTERRUPT (1 << 10)
++# define AUX4_SW_DONE_INTERRUPT (1 << 11)
++# define AUX4_LS_DONE_INTERRUPT (1 << 12)
++# define DIGA_DP_FAST_TRAINING_COMPLETE_INTERRUPT (1 << 13)
++# define DIGB_DP_FAST_TRAINING_COMPLETE_INTERRUPT (1 << 14)
++/* DCE 3.2 */
++# define AUX5_SW_DONE_INTERRUPT (1 << 15)
++# define AUX5_LS_DONE_INTERRUPT (1 << 16)
++# define AUX6_SW_DONE_INTERRUPT (1 << 17)
++# define AUX6_LS_DONE_INTERRUPT (1 << 18)
++# define DC_HPD5_INTERRUPT (1 << 19)
++# define DC_HPD5_RX_INTERRUPT (1 << 20)
++# define DC_HPD6_INTERRUPT (1 << 21)
++# define DC_HPD6_RX_INTERRUPT (1 << 22)
++
++#define DACA_AUTO_DETECT_CONTROL 0x7828
++#define DACB_AUTO_DETECT_CONTROL 0x7a28
++#define DCE3_DACA_AUTO_DETECT_CONTROL 0x7028
++#define DCE3_DACB_AUTO_DETECT_CONTROL 0x7128
++# define DACx_AUTODETECT_MODE(x) ((x) << 0)
++# define DACx_AUTODETECT_MODE_NONE 0
++# define DACx_AUTODETECT_MODE_CONNECT 1
++# define DACx_AUTODETECT_MODE_DISCONNECT 2
++# define DACx_AUTODETECT_FRAME_TIME_COUNTER(x) ((x) << 8)
++/* bit 18 = R/C, 17 = G/Y, 16 = B/Comp */
++# define DACx_AUTODETECT_CHECK_MASK(x) ((x) << 16)
++
++#define DCE3_DACA_AUTODETECT_INT_CONTROL 0x7038
++#define DCE3_DACB_AUTODETECT_INT_CONTROL 0x7138
++#define DACA_AUTODETECT_INT_CONTROL 0x7838
++#define DACB_AUTODETECT_INT_CONTROL 0x7a38
++# define DACx_AUTODETECT_ACK (1 << 0)
++# define DACx_AUTODETECT_INT_ENABLE (1 << 16)
++
++#define DC_HOT_PLUG_DETECT1_CONTROL 0x7d00
++#define DC_HOT_PLUG_DETECT2_CONTROL 0x7d10
++#define DC_HOT_PLUG_DETECT3_CONTROL 0x7d24
++# define DC_HOT_PLUG_DETECTx_EN (1 << 0)
++
++#define DC_HOT_PLUG_DETECT1_INT_STATUS 0x7d04
++#define DC_HOT_PLUG_DETECT2_INT_STATUS 0x7d14
++#define DC_HOT_PLUG_DETECT3_INT_STATUS 0x7d28
++# define DC_HOT_PLUG_DETECTx_INT_STATUS (1 << 0)
++# define DC_HOT_PLUG_DETECTx_SENSE (1 << 1)
++
++/* DCE 3.0 */
++#define DC_HPD1_INT_STATUS 0x7d00
++#define DC_HPD2_INT_STATUS 0x7d0c
++#define DC_HPD3_INT_STATUS 0x7d18
++#define DC_HPD4_INT_STATUS 0x7d24
++/* DCE 3.2 */
++#define DC_HPD5_INT_STATUS 0x7dc0
++#define DC_HPD6_INT_STATUS 0x7df4
++# define DC_HPDx_INT_STATUS (1 << 0)
++# define DC_HPDx_SENSE (1 << 1)
++# define DC_HPDx_RX_INT_STATUS (1 << 8)
++
++#define DC_HOT_PLUG_DETECT1_INT_CONTROL 0x7d08
++#define DC_HOT_PLUG_DETECT2_INT_CONTROL 0x7d18
++#define DC_HOT_PLUG_DETECT3_INT_CONTROL 0x7d2c
++# define DC_HOT_PLUG_DETECTx_INT_ACK (1 << 0)
++# define DC_HOT_PLUG_DETECTx_INT_POLARITY (1 << 8)
++# define DC_HOT_PLUG_DETECTx_INT_EN (1 << 16)
++/* DCE 3.0 */
++#define DC_HPD1_INT_CONTROL 0x7d04
++#define DC_HPD2_INT_CONTROL 0x7d10
++#define DC_HPD3_INT_CONTROL 0x7d1c
++#define DC_HPD4_INT_CONTROL 0x7d28
++/* DCE 3.2 */
++#define DC_HPD5_INT_CONTROL 0x7dc4
++#define DC_HPD6_INT_CONTROL 0x7df8
++# define DC_HPDx_INT_ACK (1 << 0)
++# define DC_HPDx_INT_POLARITY (1 << 8)
++# define DC_HPDx_INT_EN (1 << 16)
++# define DC_HPDx_RX_INT_ACK (1 << 20)
++# define DC_HPDx_RX_INT_EN (1 << 24)
++
++/* DCE 3.0 */
++#define DC_HPD1_CONTROL 0x7d08
++#define DC_HPD2_CONTROL 0x7d14
++#define DC_HPD3_CONTROL 0x7d20
++#define DC_HPD4_CONTROL 0x7d2c
++/* DCE 3.2 */
++#define DC_HPD5_CONTROL 0x7dc8
++#define DC_HPD6_CONTROL 0x7dfc
++# define DC_HPDx_CONNECTION_TIMER(x) ((x) << 0)
++# define DC_HPDx_RX_INT_TIMER(x) ((x) << 16)
++/* DCE 3.2 */
++# define DC_HPDx_EN (1 << 28)
+
+ /*
+ * PM4
+@@ -500,7 +708,6 @@
+ #define PACKET3_WAIT_REG_MEM 0x3C
+ #define PACKET3_MEM_WRITE 0x3D
+ #define PACKET3_INDIRECT_BUFFER 0x32
+-#define PACKET3_CP_INTERRUPT 0x40
+ #define PACKET3_SURFACE_SYNC 0x43
+ # define PACKET3_CB0_DEST_BASE_ENA (1 << 6)
+ # define PACKET3_TC_ACTION_ENA (1 << 23)
+@@ -674,4 +881,30 @@
+ #define S_000E60_SOFT_RESET_TSC(x) (((x) & 1) << 16)
+ #define S_000E60_SOFT_RESET_VMC(x) (((x) & 1) << 17)
+
++#define R_005480_HDP_MEM_COHERENCY_FLUSH_CNTL 0x5480
++
++#define R_0280E0_CB_COLOR0_FRAG 0x0280E0
++#define S_0280E0_BASE_256B(x) (((x) & 0xFFFFFFFF) << 0)
++#define G_0280E0_BASE_256B(x) (((x) >> 0) & 0xFFFFFFFF)
++#define C_0280E0_BASE_256B 0x00000000
++#define R_0280E4_CB_COLOR1_FRAG 0x0280E4
++#define R_0280E8_CB_COLOR2_FRAG 0x0280E8
++#define R_0280EC_CB_COLOR3_FRAG 0x0280EC
++#define R_0280F0_CB_COLOR4_FRAG 0x0280F0
++#define R_0280F4_CB_COLOR5_FRAG 0x0280F4
++#define R_0280F8_CB_COLOR6_FRAG 0x0280F8
++#define R_0280FC_CB_COLOR7_FRAG 0x0280FC
++#define R_0280C0_CB_COLOR0_TILE 0x0280C0
++#define S_0280C0_BASE_256B(x) (((x) & 0xFFFFFFFF) << 0)
++#define G_0280C0_BASE_256B(x) (((x) >> 0) & 0xFFFFFFFF)
++#define C_0280C0_BASE_256B 0x00000000
++#define R_0280C4_CB_COLOR1_TILE 0x0280C4
++#define R_0280C8_CB_COLOR2_TILE 0x0280C8
++#define R_0280CC_CB_COLOR3_TILE 0x0280CC
++#define R_0280D0_CB_COLOR4_TILE 0x0280D0
++#define R_0280D4_CB_COLOR5_TILE 0x0280D4
++#define R_0280D8_CB_COLOR6_TILE 0x0280D8
++#define R_0280DC_CB_COLOR7_TILE 0x0280DC
++
++
+ #endif
+diff --git a/drivers/gpu/drm/radeon/radeon.h b/drivers/gpu/drm/radeon/radeon.h
+index 224506a..c0356bb 100644
+--- a/drivers/gpu/drm/radeon/radeon.h
++++ b/drivers/gpu/drm/radeon/radeon.h
+@@ -28,8 +28,6 @@
+ #ifndef __RADEON_H__
+ #define __RADEON_H__
+
+-#include "radeon_object.h"
+-
+ /* TODO: Here are things that needs to be done :
+ * - surface allocator & initializer : (bit like scratch reg) should
+ * initialize HDP_ stuff on RS600, R600, R700 hw, well anythings
+@@ -67,6 +65,11 @@
+ #include <linux/list.h>
+ #include <linux/kref.h>
+
++#include <ttm/ttm_bo_api.h>
++#include <ttm/ttm_bo_driver.h>
++#include <ttm/ttm_placement.h>
++#include <ttm/ttm_module.h>
++
+ #include "radeon_family.h"
+ #include "radeon_mode.h"
+ #include "radeon_reg.h"
+@@ -85,12 +88,15 @@ extern int radeon_benchmarking;
+ extern int radeon_testing;
+ extern int radeon_connector_table;
+ extern int radeon_tv;
++extern int radeon_new_pll;
++extern int radeon_audio;
+
+ /*
+ * Copy from radeon_drv.h so we don't have to include both and have conflicting
+ * symbol;
+ */
+ #define RADEON_MAX_USEC_TIMEOUT 100000 /* 100 ms */
++/* RADEON_IB_POOL_SIZE must be a power of 2 */
+ #define RADEON_IB_POOL_SIZE 16
+ #define RADEON_DEBUGFS_MAX_NUM_FILES 32
+ #define RADEONFB_CONN_LIMIT 4
+@@ -157,6 +163,7 @@ struct radeon_fence_driver {
+ struct list_head created;
+ struct list_head emited;
+ struct list_head signaled;
++ bool initialized;
+ };
+
+ struct radeon_fence {
+@@ -186,76 +193,63 @@ void radeon_fence_unref(struct radeon_fence **fence);
+ * Tiling registers
+ */
+ struct radeon_surface_reg {
+- struct radeon_object *robj;
++ struct radeon_bo *bo;
+ };
+
+ #define RADEON_GEM_MAX_SURFACES 8
+
+ /*
+- * Radeon buffer.
++ * TTM.
+ */
+-struct radeon_object;
++struct radeon_mman {
++ struct ttm_bo_global_ref bo_global_ref;
++ struct ttm_global_reference mem_global_ref;
++ struct ttm_bo_device bdev;
++ bool mem_global_referenced;
++ bool initialized;
++};
+
+-struct radeon_object_list {
++struct radeon_bo {
++ /* Protected by gem.mutex */
++ struct list_head list;
++ /* Protected by tbo.reserved */
++ u32 placements[3];
++ struct ttm_placement placement;
++ struct ttm_buffer_object tbo;
++ struct ttm_bo_kmap_obj kmap;
++ unsigned pin_count;
++ void *kptr;
++ u32 tiling_flags;
++ u32 pitch;
++ int surface_reg;
++ /* Constant after initialization */
++ struct radeon_device *rdev;
++ struct drm_gem_object *gobj;
++};
++
++struct radeon_bo_list {
+ struct list_head list;
+- struct radeon_object *robj;
++ struct radeon_bo *bo;
+ uint64_t gpu_offset;
+ unsigned rdomain;
+ unsigned wdomain;
+- uint32_t tiling_flags;
++ u32 tiling_flags;
+ };
+
+-int radeon_object_init(struct radeon_device *rdev);
+-void radeon_object_fini(struct radeon_device *rdev);
+-int radeon_object_create(struct radeon_device *rdev,
+- struct drm_gem_object *gobj,
+- unsigned long size,
+- bool kernel,
+- uint32_t domain,
+- bool interruptible,
+- struct radeon_object **robj_ptr);
+-int radeon_object_kmap(struct radeon_object *robj, void **ptr);
+-void radeon_object_kunmap(struct radeon_object *robj);
+-void radeon_object_unref(struct radeon_object **robj);
+-int radeon_object_pin(struct radeon_object *robj, uint32_t domain,
+- uint64_t *gpu_addr);
+-void radeon_object_unpin(struct radeon_object *robj);
+-int radeon_object_wait(struct radeon_object *robj);
+-int radeon_object_busy_domain(struct radeon_object *robj, uint32_t *cur_placement);
+-int radeon_object_evict_vram(struct radeon_device *rdev);
+-int radeon_object_mmap(struct radeon_object *robj, uint64_t *offset);
+-void radeon_object_force_delete(struct radeon_device *rdev);
+-void radeon_object_list_add_object(struct radeon_object_list *lobj,
+- struct list_head *head);
+-int radeon_object_list_validate(struct list_head *head, void *fence);
+-void radeon_object_list_unvalidate(struct list_head *head);
+-void radeon_object_list_clean(struct list_head *head);
+-int radeon_object_fbdev_mmap(struct radeon_object *robj,
+- struct vm_area_struct *vma);
+-unsigned long radeon_object_size(struct radeon_object *robj);
+-void radeon_object_clear_surface_reg(struct radeon_object *robj);
+-int radeon_object_check_tiling(struct radeon_object *robj, bool has_moved,
+- bool force_drop);
+-void radeon_object_set_tiling_flags(struct radeon_object *robj,
+- uint32_t tiling_flags, uint32_t pitch);
+-void radeon_object_get_tiling_flags(struct radeon_object *robj, uint32_t *tiling_flags, uint32_t *pitch);
+-void radeon_bo_move_notify(struct ttm_buffer_object *bo,
+- struct ttm_mem_reg *mem);
+-void radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo);
+ /*
+ * GEM objects.
+ */
+ struct radeon_gem {
++ struct mutex mutex;
+ struct list_head objects;
+ };
+
+ int radeon_gem_init(struct radeon_device *rdev);
+ void radeon_gem_fini(struct radeon_device *rdev);
+ int radeon_gem_object_create(struct radeon_device *rdev, int size,
+- int alignment, int initial_domain,
+- bool discardable, bool kernel,
+- bool interruptible,
+- struct drm_gem_object **obj);
++ int alignment, int initial_domain,
++ bool discardable, bool kernel,
++ struct drm_gem_object **obj);
+ int radeon_gem_object_pin(struct drm_gem_object *obj, uint32_t pin_domain,
+ uint64_t *gpu_addr);
+ void radeon_gem_object_unpin(struct drm_gem_object *obj);
+@@ -271,7 +265,7 @@ struct radeon_gart_table_ram {
+ };
+
+ struct radeon_gart_table_vram {
+- struct radeon_object *robj;
++ struct radeon_bo *robj;
+ volatile uint32_t *ptr;
+ };
+
+@@ -326,10 +320,12 @@ struct radeon_mc {
+ u64 real_vram_size;
+ int vram_mtrr;
+ bool vram_is_ddr;
++ bool igp_sideport_enabled;
+ };
+
+ int radeon_mc_setup(struct radeon_device *rdev);
+-
++bool radeon_combios_sideport_present(struct radeon_device *rdev);
++bool radeon_atombios_sideport_present(struct radeon_device *rdev);
+
+ /*
+ * GPU scratch registers structures, functions & helpers
+@@ -352,22 +348,28 @@ struct radeon_irq {
+ bool sw_int;
+ /* FIXME: use a define max crtc rather than hardcode it */
+ bool crtc_vblank_int[2];
++ /* FIXME: use defines for max hpd/dacs */
++ bool hpd[6];
++ spinlock_t sw_lock;
++ int sw_refcount;
+ };
+
+ int radeon_irq_kms_init(struct radeon_device *rdev);
+ void radeon_irq_kms_fini(struct radeon_device *rdev);
+-
++void radeon_irq_kms_sw_irq_get(struct radeon_device *rdev);
++void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev);
+
+ /*
+ * CP & ring.
+ */
+ struct radeon_ib {
+ struct list_head list;
+- unsigned long idx;
++ unsigned idx;
+ uint64_t gpu_addr;
+ struct radeon_fence *fence;
+- uint32_t *ptr;
++ uint32_t *ptr;
+ uint32_t length_dw;
++ bool free;
+ };
+
+ /*
+@@ -376,15 +378,14 @@ struct radeon_ib {
+ */
+ struct radeon_ib_pool {
+ struct mutex mutex;
+- struct radeon_object *robj;
+- struct list_head scheduled_ibs;
++ struct radeon_bo *robj;
+ struct radeon_ib ibs[RADEON_IB_POOL_SIZE];
+ bool ready;
+- DECLARE_BITMAP(alloc_bm, RADEON_IB_POOL_SIZE);
++ unsigned head_id;
+ };
+
+ struct radeon_cp {
+- struct radeon_object *ring_obj;
++ struct radeon_bo *ring_obj;
+ volatile uint32_t *ring;
+ unsigned rptr;
+ unsigned wptr;
+@@ -399,8 +400,25 @@ struct radeon_cp {
+ bool ready;
+ };
+
++/*
++ * R6xx+ IH ring
++ */
++struct r600_ih {
++ struct radeon_bo *ring_obj;
++ volatile uint32_t *ring;
++ unsigned rptr;
++ unsigned wptr;
++ unsigned wptr_old;
++ unsigned ring_size;
++ uint64_t gpu_addr;
++ uint32_t ptr_mask;
++ spinlock_t lock;
++ bool enabled;
++};
++
+ struct r600_blit {
+- struct radeon_object *shader_obj;
++ struct mutex mutex;
++ struct radeon_bo *shader_obj;
+ u64 shader_gpu_addr;
+ u32 vs_offset, ps_offset;
+ u32 state_offset;
+@@ -430,8 +448,8 @@ void radeon_ring_fini(struct radeon_device *rdev);
+ */
+ struct radeon_cs_reloc {
+ struct drm_gem_object *gobj;
+- struct radeon_object *robj;
+- struct radeon_object_list lobj;
++ struct radeon_bo *robj;
++ struct radeon_bo_list lobj;
+ uint32_t handle;
+ uint32_t flags;
+ };
+@@ -448,6 +466,7 @@ struct radeon_cs_chunk {
+ };
+
+ struct radeon_cs_parser {
++ struct device *dev;
+ struct radeon_device *rdev;
+ struct drm_file *filp;
+ /* chunks */
+@@ -527,7 +546,7 @@ void radeon_agp_fini(struct radeon_device *rdev);
+ * Writeback
+ */
+ struct radeon_wb {
+- struct radeon_object *wb_obj;
++ struct radeon_bo *wb_obj;
+ volatile uint32_t *wb;
+ uint64_t gpu_addr;
+ };
+@@ -639,6 +658,17 @@ struct radeon_asic {
+ uint32_t offset, uint32_t obj_size);
+ int (*clear_surface_reg)(struct radeon_device *rdev, int reg);
+ void (*bandwidth_update)(struct radeon_device *rdev);
++ void (*hpd_init)(struct radeon_device *rdev);
++ void (*hpd_fini)(struct radeon_device *rdev);
++ bool (*hpd_sense)(struct radeon_device *rdev, enum radeon_hpd_id hpd);
++ void (*hpd_set_polarity)(struct radeon_device *rdev, enum radeon_hpd_id hpd);
++ /* ioctl hw specific callback. Some hw might want to perform special
++ * operation on specific ioctl. For instance on wait idle some hw
++ * might want to perform and HDP flush through MMIO as it seems that
++ * some R6XX/R7XX hw doesn't take HDP flush into account if programmed
++ * through ring.
++ */
++ void (*ioctl_wait_idle)(struct radeon_device *rdev, struct radeon_bo *bo);
+ };
+
+ /*
+@@ -647,11 +677,14 @@ struct radeon_asic {
+ struct r100_asic {
+ const unsigned *reg_safe_bm;
+ unsigned reg_safe_bm_size;
++ u32 hdp_cntl;
+ };
+
+ struct r300_asic {
+ const unsigned *reg_safe_bm;
+ unsigned reg_safe_bm_size;
++ u32 resync_scratch;
++ u32 hdp_cntl;
+ };
+
+ struct r600_asic {
+@@ -751,9 +784,9 @@ struct radeon_device {
+ uint8_t *bios;
+ bool is_atom_bios;
+ uint16_t bios_header_start;
+- struct radeon_object *stollen_vga_memory;
++ struct radeon_bo *stollen_vga_memory;
+ struct fb_info *fbdev_info;
+- struct radeon_object *fbdev_robj;
++ struct radeon_bo *fbdev_rbo;
+ struct radeon_framebuffer *fbdev_rfb;
+ /* Register mmio */
+ resource_size_t rmmio_base;
+@@ -791,8 +824,20 @@ struct radeon_device {
+ struct radeon_surface_reg surface_regs[RADEON_GEM_MAX_SURFACES];
+ const struct firmware *me_fw; /* all family ME firmware */
+ const struct firmware *pfp_fw; /* r6/700 PFP firmware */
++ const struct firmware *rlc_fw; /* r6/700 RLC firmware */
+ struct r600_blit r600_blit;
+ int msi_enabled; /* msi enabled */
++ struct r600_ih ih; /* r6/700 interrupt ring */
++ struct workqueue_struct *wq;
++ struct work_struct hotplug_work;
++
++ /* audio stuff */
++ struct timer_list audio_timer;
++ int audio_channels;
++ int audio_rate;
++ int audio_bits_per_sample;
++ uint8_t audio_status_bits;
++ uint8_t audio_category_code;
+ };
+
+ int radeon_device_init(struct radeon_device *rdev,
+@@ -811,7 +856,7 @@ void r600_kms_blit_copy(struct radeon_device *rdev,
+
+ static inline uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg)
+ {
+- if (reg < 0x10000)
++ if (reg < rdev->rmmio_size)
+ return readl(((void __iomem *)rdev->rmmio) + reg);
+ else {
+ writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);
+@@ -821,7 +866,7 @@ static inline uint32_t r100_mm_rreg(struct radeon_device *rdev, uint32_t reg)
+
+ static inline void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v)
+ {
+- if (reg < 0x10000)
++ if (reg < rdev->rmmio_size)
+ writel(v, ((void __iomem *)rdev->rmmio) + reg);
+ else {
+ writel(reg, ((void __iomem *)rdev->rmmio) + RADEON_MM_INDEX);
+@@ -829,6 +874,10 @@ static inline void r100_mm_wreg(struct radeon_device *rdev, uint32_t reg, uint32
+ }
+ }
+
++/*
++ * Cast helper
++ */
++#define to_radeon_fence(p) ((struct radeon_fence *)(p))
+
+ /*
+ * Registers read & write functions.
+@@ -965,18 +1014,25 @@ static inline void radeon_ring_write(struct radeon_device *rdev, uint32_t v)
+ #define radeon_get_engine_clock(rdev) (rdev)->asic->get_engine_clock((rdev))
+ #define radeon_set_engine_clock(rdev, e) (rdev)->asic->set_engine_clock((rdev), (e))
+ #define radeon_get_memory_clock(rdev) (rdev)->asic->get_memory_clock((rdev))
+-#define radeon_set_memory_clock(rdev, e) (rdev)->asic->set_engine_clock((rdev), (e))
++#define radeon_set_memory_clock(rdev, e) (rdev)->asic->set_memory_clock((rdev), (e))
+ #define radeon_set_pcie_lanes(rdev, l) (rdev)->asic->set_pcie_lanes((rdev), (l))
+ #define radeon_set_clock_gating(rdev, e) (rdev)->asic->set_clock_gating((rdev), (e))
+ #define radeon_set_surface_reg(rdev, r, f, p, o, s) ((rdev)->asic->set_surface_reg((rdev), (r), (f), (p), (o), (s)))
+ #define radeon_clear_surface_reg(rdev, r) ((rdev)->asic->clear_surface_reg((rdev), (r)))
+ #define radeon_bandwidth_update(rdev) (rdev)->asic->bandwidth_update((rdev))
++#define radeon_hpd_init(rdev) (rdev)->asic->hpd_init((rdev))
++#define radeon_hpd_fini(rdev) (rdev)->asic->hpd_fini((rdev))
++#define radeon_hpd_sense(rdev, hpd) (rdev)->asic->hpd_sense((rdev), (hpd))
++#define radeon_hpd_set_polarity(rdev, hpd) (rdev)->asic->hpd_set_polarity((rdev), (hpd))
+
+ /* Common functions */
++/* AGP */
++extern void radeon_agp_disable(struct radeon_device *rdev);
+ extern int radeon_gart_table_vram_pin(struct radeon_device *rdev);
+ extern int radeon_modeset_init(struct radeon_device *rdev);
+ extern void radeon_modeset_fini(struct radeon_device *rdev);
+ extern bool radeon_card_posted(struct radeon_device *rdev);
++extern bool radeon_boot_test_post_card(struct radeon_device *rdev);
+ extern int radeon_clocks_init(struct radeon_device *rdev);
+ extern void radeon_clocks_fini(struct radeon_device *rdev);
+ extern void radeon_scratch_init(struct radeon_device *rdev);
+@@ -984,6 +1040,8 @@ extern void radeon_surface_init(struct radeon_device *rdev);
+ extern int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data);
+ extern void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable);
+ extern void radeon_atom_set_clock_gating(struct radeon_device *rdev, int enable);
++extern void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain);
++extern bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo);
+
+ /* r100,rv100,rs100,rv200,rs200,r200,rv250,rs300,rv280 */
+ struct r100_mc_save {
+@@ -1021,7 +1079,7 @@ extern int r100_cp_reset(struct radeon_device *rdev);
+ extern void r100_vga_render_disable(struct radeon_device *rdev);
+ extern int r100_cs_track_check_pkt3_indx_buffer(struct radeon_cs_parser *p,
+ struct radeon_cs_packet *pkt,
+- struct radeon_object *robj);
++ struct radeon_bo *robj);
+ extern int r100_cs_parse_packet0(struct radeon_cs_parser *p,
+ struct radeon_cs_packet *pkt,
+ const unsigned *auth, unsigned n,
+@@ -1029,6 +1087,8 @@ extern int r100_cs_parse_packet0(struct radeon_cs_parser *p,
+ extern int r100_cs_packet_parse(struct radeon_cs_parser *p,
+ struct radeon_cs_packet *pkt,
+ unsigned idx);
++extern void r100_enable_bm(struct radeon_device *rdev);
++extern void r100_set_common_regs(struct radeon_device *rdev);
+
+ /* rv200,rv250,rv280 */
+ extern void r200_set_safe_registers(struct radeon_device *rdev);
+@@ -1091,6 +1151,7 @@ extern bool r600_card_posted(struct radeon_device *rdev);
+ extern void r600_cp_stop(struct radeon_device *rdev);
+ extern void r600_ring_init(struct radeon_device *rdev, unsigned ring_size);
+ extern int r600_cp_resume(struct radeon_device *rdev);
++extern void r600_cp_fini(struct radeon_device *rdev);
+ extern int r600_count_pipe_bits(uint32_t val);
+ extern int r600_gart_clear_page(struct radeon_device *rdev, int i);
+ extern int r600_mc_wait_for_idle(struct radeon_device *rdev);
+@@ -1104,7 +1165,30 @@ extern void r600_wb_disable(struct radeon_device *rdev);
+ extern void r600_scratch_init(struct radeon_device *rdev);
+ extern int r600_blit_init(struct radeon_device *rdev);
+ extern void r600_blit_fini(struct radeon_device *rdev);
+-extern int r600_cp_init_microcode(struct radeon_device *rdev);
++extern int r600_init_microcode(struct radeon_device *rdev);
+ extern int r600_gpu_reset(struct radeon_device *rdev);
++/* r600 irq */
++extern int r600_irq_init(struct radeon_device *rdev);
++extern void r600_irq_fini(struct radeon_device *rdev);
++extern void r600_ih_ring_init(struct radeon_device *rdev, unsigned ring_size);
++extern int r600_irq_set(struct radeon_device *rdev);
++extern void r600_irq_suspend(struct radeon_device *rdev);
++/* r600 audio */
++extern int r600_audio_init(struct radeon_device *rdev);
++extern int r600_audio_tmds_index(struct drm_encoder *encoder);
++extern void r600_audio_set_clock(struct drm_encoder *encoder, int clock);
++extern void r600_audio_fini(struct radeon_device *rdev);
++extern void r600_hdmi_init(struct drm_encoder *encoder);
++extern void r600_hdmi_enable(struct drm_encoder *encoder, int enable);
++extern void r600_hdmi_setmode(struct drm_encoder *encoder, struct drm_display_mode *mode);
++extern int r600_hdmi_buffer_status_changed(struct drm_encoder *encoder);
++extern void r600_hdmi_update_audio_settings(struct drm_encoder *encoder,
++ int channels,
++ int rate,
++ int bps,
++ uint8_t status_bits,
++ uint8_t category_code);
++
++#include "radeon_object.h"
+
+ #endif
+diff --git a/drivers/gpu/drm/radeon/radeon_agp.c b/drivers/gpu/drm/radeon/radeon_agp.c
+index 54bf49a..c0681a5 100644
+--- a/drivers/gpu/drm/radeon/radeon_agp.c
++++ b/drivers/gpu/drm/radeon/radeon_agp.c
+@@ -144,9 +144,19 @@ int radeon_agp_init(struct radeon_device *rdev)
+
+ ret = drm_agp_info(rdev->ddev, &info);
+ if (ret) {
++ drm_agp_release(rdev->ddev);
+ DRM_ERROR("Unable to get AGP info: %d\n", ret);
+ return ret;
+ }
++
++ if (rdev->ddev->agp->agp_info.aper_size < 32) {
++ drm_agp_release(rdev->ddev);
++ dev_warn(rdev->dev, "AGP aperture too small (%zuM) "
++ "need at least 32M, disabling AGP\n",
++ rdev->ddev->agp->agp_info.aper_size);
++ return -EINVAL;
++ }
++
+ mode.mode = info.mode;
+ agp_status = (RREG32(RADEON_AGP_STATUS) | RADEON_AGPv3_MODE) & mode.mode;
+ is_v3 = !!(agp_status & RADEON_AGPv3_MODE);
+@@ -221,6 +231,7 @@ int radeon_agp_init(struct radeon_device *rdev)
+ ret = drm_agp_enable(rdev->ddev, mode);
+ if (ret) {
+ DRM_ERROR("Unable to enable AGP (mode = 0x%lx)\n", mode.mode);
++ drm_agp_release(rdev->ddev);
+ return ret;
+ }
+
+@@ -252,10 +263,8 @@ void radeon_agp_resume(struct radeon_device *rdev)
+ void radeon_agp_fini(struct radeon_device *rdev)
+ {
+ #if __OS_HAS_AGP
+- if (rdev->flags & RADEON_IS_AGP) {
+- if (rdev->ddev->agp && rdev->ddev->agp->acquired) {
+- drm_agp_release(rdev->ddev);
+- }
++ if (rdev->ddev->agp && rdev->ddev->agp->acquired) {
++ drm_agp_release(rdev->ddev);
+ }
+ #endif
+ }
+diff --git a/drivers/gpu/drm/radeon/radeon_asic.h b/drivers/gpu/drm/radeon/radeon_asic.h
+index c18fbee..05ee1ae 100644
+--- a/drivers/gpu/drm/radeon/radeon_asic.h
++++ b/drivers/gpu/drm/radeon/radeon_asic.h
+@@ -33,6 +33,7 @@
+ */
+ uint32_t radeon_legacy_get_engine_clock(struct radeon_device *rdev);
+ void radeon_legacy_set_engine_clock(struct radeon_device *rdev, uint32_t eng_clock);
++uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev);
+ void radeon_legacy_set_clock_gating(struct radeon_device *rdev, int enable);
+
+ uint32_t radeon_atom_get_engine_clock(struct radeon_device *rdev);
+@@ -76,6 +77,11 @@ int r100_clear_surface_reg(struct radeon_device *rdev, int reg);
+ void r100_bandwidth_update(struct radeon_device *rdev);
+ void r100_ring_ib_execute(struct radeon_device *rdev, struct radeon_ib *ib);
+ int r100_ring_test(struct radeon_device *rdev);
++void r100_hpd_init(struct radeon_device *rdev);
++void r100_hpd_fini(struct radeon_device *rdev);
++bool r100_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd);
++void r100_hpd_set_polarity(struct radeon_device *rdev,
++ enum radeon_hpd_id hpd);
+
+ static struct radeon_asic r100_asic = {
+ .init = &r100_init,
+@@ -100,13 +106,18 @@ static struct radeon_asic r100_asic = {
+ .copy = &r100_copy_blit,
+ .get_engine_clock = &radeon_legacy_get_engine_clock,
+ .set_engine_clock = &radeon_legacy_set_engine_clock,
+- .get_memory_clock = NULL,
++ .get_memory_clock = &radeon_legacy_get_memory_clock,
+ .set_memory_clock = NULL,
+ .set_pcie_lanes = NULL,
+ .set_clock_gating = &radeon_legacy_set_clock_gating,
+ .set_surface_reg = r100_set_surface_reg,
+ .clear_surface_reg = r100_clear_surface_reg,
+ .bandwidth_update = &r100_bandwidth_update,
++ .hpd_init = &r100_hpd_init,
++ .hpd_fini = &r100_hpd_fini,
++ .hpd_sense = &r100_hpd_sense,
++ .hpd_set_polarity = &r100_hpd_set_polarity,
++ .ioctl_wait_idle = NULL,
+ };
+
+
+@@ -155,13 +166,18 @@ static struct radeon_asic r300_asic = {
+ .copy = &r100_copy_blit,
+ .get_engine_clock = &radeon_legacy_get_engine_clock,
+ .set_engine_clock = &radeon_legacy_set_engine_clock,
+- .get_memory_clock = NULL,
++ .get_memory_clock = &radeon_legacy_get_memory_clock,
+ .set_memory_clock = NULL,
+ .set_pcie_lanes = &rv370_set_pcie_lanes,
+ .set_clock_gating = &radeon_legacy_set_clock_gating,
+ .set_surface_reg = r100_set_surface_reg,
+ .clear_surface_reg = r100_clear_surface_reg,
+ .bandwidth_update = &r100_bandwidth_update,
++ .hpd_init = &r100_hpd_init,
++ .hpd_fini = &r100_hpd_fini,
++ .hpd_sense = &r100_hpd_sense,
++ .hpd_set_polarity = &r100_hpd_set_polarity,
++ .ioctl_wait_idle = NULL,
+ };
+
+ /*
+@@ -201,6 +217,11 @@ static struct radeon_asic r420_asic = {
+ .set_surface_reg = r100_set_surface_reg,
+ .clear_surface_reg = r100_clear_surface_reg,
+ .bandwidth_update = &r100_bandwidth_update,
++ .hpd_init = &r100_hpd_init,
++ .hpd_fini = &r100_hpd_fini,
++ .hpd_sense = &r100_hpd_sense,
++ .hpd_set_polarity = &r100_hpd_set_polarity,
++ .ioctl_wait_idle = NULL,
+ };
+
+
+@@ -238,13 +259,18 @@ static struct radeon_asic rs400_asic = {
+ .copy = &r100_copy_blit,
+ .get_engine_clock = &radeon_legacy_get_engine_clock,
+ .set_engine_clock = &radeon_legacy_set_engine_clock,
+- .get_memory_clock = NULL,
++ .get_memory_clock = &radeon_legacy_get_memory_clock,
+ .set_memory_clock = NULL,
+ .set_pcie_lanes = NULL,
+ .set_clock_gating = &radeon_legacy_set_clock_gating,
+ .set_surface_reg = r100_set_surface_reg,
+ .clear_surface_reg = r100_clear_surface_reg,
+ .bandwidth_update = &r100_bandwidth_update,
++ .hpd_init = &r100_hpd_init,
++ .hpd_fini = &r100_hpd_fini,
++ .hpd_sense = &r100_hpd_sense,
++ .hpd_set_polarity = &r100_hpd_set_polarity,
++ .ioctl_wait_idle = NULL,
+ };
+
+
+@@ -263,6 +289,12 @@ int rs600_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr);
+ uint32_t rs600_mc_rreg(struct radeon_device *rdev, uint32_t reg);
+ void rs600_mc_wreg(struct radeon_device *rdev, uint32_t reg, uint32_t v);
+ void rs600_bandwidth_update(struct radeon_device *rdev);
++void rs600_hpd_init(struct radeon_device *rdev);
++void rs600_hpd_fini(struct radeon_device *rdev);
++bool rs600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd);
++void rs600_hpd_set_polarity(struct radeon_device *rdev,
++ enum radeon_hpd_id hpd);
++
+ static struct radeon_asic rs600_asic = {
+ .init = &rs600_init,
+ .fini = &rs600_fini,
+@@ -291,6 +323,11 @@ static struct radeon_asic rs600_asic = {
+ .set_pcie_lanes = NULL,
+ .set_clock_gating = &radeon_atom_set_clock_gating,
+ .bandwidth_update = &rs600_bandwidth_update,
++ .hpd_init = &rs600_hpd_init,
++ .hpd_fini = &rs600_hpd_fini,
++ .hpd_sense = &rs600_hpd_sense,
++ .hpd_set_polarity = &rs600_hpd_set_polarity,
++ .ioctl_wait_idle = NULL,
+ };
+
+
+@@ -334,6 +371,11 @@ static struct radeon_asic rs690_asic = {
+ .set_surface_reg = r100_set_surface_reg,
+ .clear_surface_reg = r100_clear_surface_reg,
+ .bandwidth_update = &rs690_bandwidth_update,
++ .hpd_init = &rs600_hpd_init,
++ .hpd_fini = &rs600_hpd_fini,
++ .hpd_sense = &rs600_hpd_sense,
++ .hpd_set_polarity = &rs600_hpd_set_polarity,
++ .ioctl_wait_idle = NULL,
+ };
+
+
+@@ -381,6 +423,11 @@ static struct radeon_asic rv515_asic = {
+ .set_surface_reg = r100_set_surface_reg,
+ .clear_surface_reg = r100_clear_surface_reg,
+ .bandwidth_update = &rv515_bandwidth_update,
++ .hpd_init = &rs600_hpd_init,
++ .hpd_fini = &rs600_hpd_fini,
++ .hpd_sense = &rs600_hpd_sense,
++ .hpd_set_polarity = &rs600_hpd_set_polarity,
++ .ioctl_wait_idle = NULL,
+ };
+
+
+@@ -419,6 +466,11 @@ static struct radeon_asic r520_asic = {
+ .set_surface_reg = r100_set_surface_reg,
+ .clear_surface_reg = r100_clear_surface_reg,
+ .bandwidth_update = &rv515_bandwidth_update,
++ .hpd_init = &rs600_hpd_init,
++ .hpd_fini = &rs600_hpd_fini,
++ .hpd_sense = &rs600_hpd_sense,
++ .hpd_set_polarity = &rs600_hpd_set_polarity,
++ .ioctl_wait_idle = NULL,
+ };
+
+ /*
+@@ -455,6 +507,12 @@ int r600_ring_test(struct radeon_device *rdev);
+ int r600_copy_blit(struct radeon_device *rdev,
+ uint64_t src_offset, uint64_t dst_offset,
+ unsigned num_pages, struct radeon_fence *fence);
++void r600_hpd_init(struct radeon_device *rdev);
++void r600_hpd_fini(struct radeon_device *rdev);
++bool r600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd);
++void r600_hpd_set_polarity(struct radeon_device *rdev,
++ enum radeon_hpd_id hpd);
++extern void r600_ioctl_wait_idle(struct radeon_device *rdev, struct radeon_bo *bo);
+
+ static struct radeon_asic r600_asic = {
+ .init = &r600_init,
+@@ -470,6 +528,7 @@ static struct radeon_asic r600_asic = {
+ .ring_ib_execute = &r600_ring_ib_execute,
+ .irq_set = &r600_irq_set,
+ .irq_process = &r600_irq_process,
++ .get_vblank_counter = &rs600_get_vblank_counter,
+ .fence_ring_emit = &r600_fence_ring_emit,
+ .cs_parse = &r600_cs_parse,
+ .copy_blit = &r600_copy_blit,
+@@ -484,6 +543,11 @@ static struct radeon_asic r600_asic = {
+ .set_surface_reg = r600_set_surface_reg,
+ .clear_surface_reg = r600_clear_surface_reg,
+ .bandwidth_update = &rv515_bandwidth_update,
++ .hpd_init = &r600_hpd_init,
++ .hpd_fini = &r600_hpd_fini,
++ .hpd_sense = &r600_hpd_sense,
++ .hpd_set_polarity = &r600_hpd_set_polarity,
++ .ioctl_wait_idle = r600_ioctl_wait_idle,
+ };
+
+ /*
+@@ -509,6 +573,7 @@ static struct radeon_asic rv770_asic = {
+ .ring_ib_execute = &r600_ring_ib_execute,
+ .irq_set = &r600_irq_set,
+ .irq_process = &r600_irq_process,
++ .get_vblank_counter = &rs600_get_vblank_counter,
+ .fence_ring_emit = &r600_fence_ring_emit,
+ .cs_parse = &r600_cs_parse,
+ .copy_blit = &r600_copy_blit,
+@@ -523,6 +588,11 @@ static struct radeon_asic rv770_asic = {
+ .set_surface_reg = r600_set_surface_reg,
+ .clear_surface_reg = r600_clear_surface_reg,
+ .bandwidth_update = &rv515_bandwidth_update,
++ .hpd_init = &r600_hpd_init,
++ .hpd_fini = &r600_hpd_fini,
++ .hpd_sense = &r600_hpd_sense,
++ .hpd_set_polarity = &r600_hpd_set_polarity,
++ .ioctl_wait_idle = r600_ioctl_wait_idle,
+ };
+
+ #endif
+diff --git a/drivers/gpu/drm/radeon/radeon_atombios.c b/drivers/gpu/drm/radeon/radeon_atombios.c
+index 969502a..2dcda61 100644
+--- a/drivers/gpu/drm/radeon/radeon_atombios.c
++++ b/drivers/gpu/drm/radeon/radeon_atombios.c
+@@ -47,7 +47,8 @@ radeon_add_atom_connector(struct drm_device *dev,
+ int connector_type,
+ struct radeon_i2c_bus_rec *i2c_bus,
+ bool linkb, uint32_t igp_lane_info,
+- uint16_t connector_object_id);
++ uint16_t connector_object_id,
++ struct radeon_hpd *hpd);
+
+ /* from radeon_legacy_encoder.c */
+ extern void
+@@ -60,16 +61,16 @@ union atom_supported_devices {
+ struct _ATOM_SUPPORTED_DEVICES_INFO_2d1 info_2d1;
+ };
+
+-static inline struct radeon_i2c_bus_rec radeon_lookup_gpio(struct drm_device
+- *dev, uint8_t id)
++static inline struct radeon_i2c_bus_rec radeon_lookup_i2c_gpio(struct radeon_device *rdev,
++ uint8_t id)
+ {
+- struct radeon_device *rdev = dev->dev_private;
+ struct atom_context *ctx = rdev->mode_info.atom_context;
+- ATOM_GPIO_I2C_ASSIGMENT gpio;
++ ATOM_GPIO_I2C_ASSIGMENT *gpio;
+ struct radeon_i2c_bus_rec i2c;
+ int index = GetIndexIntoMasterTable(DATA, GPIO_I2C_Info);
+ struct _ATOM_GPIO_I2C_INFO *i2c_info;
+ uint16_t data_offset;
++ int i;
+
+ memset(&i2c, 0, sizeof(struct radeon_i2c_bus_rec));
+ i2c.valid = false;
+@@ -78,34 +79,122 @@ static inline struct radeon_i2c_bus_rec radeon_lookup_gpio(struct drm_device
+
+ i2c_info = (struct _ATOM_GPIO_I2C_INFO *)(ctx->bios + data_offset);
+
+- gpio = i2c_info->asGPIO_Info[id];
+-
+- i2c.mask_clk_reg = le16_to_cpu(gpio.usClkMaskRegisterIndex) * 4;
+- i2c.mask_data_reg = le16_to_cpu(gpio.usDataMaskRegisterIndex) * 4;
+- i2c.put_clk_reg = le16_to_cpu(gpio.usClkEnRegisterIndex) * 4;
+- i2c.put_data_reg = le16_to_cpu(gpio.usDataEnRegisterIndex) * 4;
+- i2c.get_clk_reg = le16_to_cpu(gpio.usClkY_RegisterIndex) * 4;
+- i2c.get_data_reg = le16_to_cpu(gpio.usDataY_RegisterIndex) * 4;
+- i2c.a_clk_reg = le16_to_cpu(gpio.usClkA_RegisterIndex) * 4;
+- i2c.a_data_reg = le16_to_cpu(gpio.usDataA_RegisterIndex) * 4;
+- i2c.mask_clk_mask = (1 << gpio.ucClkMaskShift);
+- i2c.mask_data_mask = (1 << gpio.ucDataMaskShift);
+- i2c.put_clk_mask = (1 << gpio.ucClkEnShift);
+- i2c.put_data_mask = (1 << gpio.ucDataEnShift);
+- i2c.get_clk_mask = (1 << gpio.ucClkY_Shift);
+- i2c.get_data_mask = (1 << gpio.ucDataY_Shift);
+- i2c.a_clk_mask = (1 << gpio.ucClkA_Shift);
+- i2c.a_data_mask = (1 << gpio.ucDataA_Shift);
+- i2c.valid = true;
++
++ for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) {
++ gpio = &i2c_info->asGPIO_Info[i];
++
++ if (gpio->sucI2cId.ucAccess == id) {
++ i2c.mask_clk_reg = le16_to_cpu(gpio->usClkMaskRegisterIndex) * 4;
++ i2c.mask_data_reg = le16_to_cpu(gpio->usDataMaskRegisterIndex) * 4;
++ i2c.en_clk_reg = le16_to_cpu(gpio->usClkEnRegisterIndex) * 4;
++ i2c.en_data_reg = le16_to_cpu(gpio->usDataEnRegisterIndex) * 4;
++ i2c.y_clk_reg = le16_to_cpu(gpio->usClkY_RegisterIndex) * 4;
++ i2c.y_data_reg = le16_to_cpu(gpio->usDataY_RegisterIndex) * 4;
++ i2c.a_clk_reg = le16_to_cpu(gpio->usClkA_RegisterIndex) * 4;
++ i2c.a_data_reg = le16_to_cpu(gpio->usDataA_RegisterIndex) * 4;
++ i2c.mask_clk_mask = (1 << gpio->ucClkMaskShift);
++ i2c.mask_data_mask = (1 << gpio->ucDataMaskShift);
++ i2c.en_clk_mask = (1 << gpio->ucClkEnShift);
++ i2c.en_data_mask = (1 << gpio->ucDataEnShift);
++ i2c.y_clk_mask = (1 << gpio->ucClkY_Shift);
++ i2c.y_data_mask = (1 << gpio->ucDataY_Shift);
++ i2c.a_clk_mask = (1 << gpio->ucClkA_Shift);
++ i2c.a_data_mask = (1 << gpio->ucDataA_Shift);
++
++ if (gpio->sucI2cId.sbfAccess.bfHW_Capable)
++ i2c.hw_capable = true;
++ else
++ i2c.hw_capable = false;
++
++ if (gpio->sucI2cId.ucAccess == 0xa0)
++ i2c.mm_i2c = true;
++ else
++ i2c.mm_i2c = false;
++
++ i2c.i2c_id = gpio->sucI2cId.ucAccess;
++
++ i2c.valid = true;
++ break;
++ }
++ }
+
+ return i2c;
+ }
+
++static inline struct radeon_gpio_rec radeon_lookup_gpio(struct radeon_device *rdev,
++ u8 id)
++{
++ struct atom_context *ctx = rdev->mode_info.atom_context;
++ struct radeon_gpio_rec gpio;
++ int index = GetIndexIntoMasterTable(DATA, GPIO_Pin_LUT);
++ struct _ATOM_GPIO_PIN_LUT *gpio_info;
++ ATOM_GPIO_PIN_ASSIGNMENT *pin;
++ u16 data_offset, size;
++ int i, num_indices;
++
++ memset(&gpio, 0, sizeof(struct radeon_gpio_rec));
++ gpio.valid = false;
++
++ atom_parse_data_header(ctx, index, &size, NULL, NULL, &data_offset);
++
++ gpio_info = (struct _ATOM_GPIO_PIN_LUT *)(ctx->bios + data_offset);
++
++ num_indices = (size - sizeof(ATOM_COMMON_TABLE_HEADER)) / sizeof(ATOM_GPIO_PIN_ASSIGNMENT);
++
++ for (i = 0; i < num_indices; i++) {
++ pin = &gpio_info->asGPIO_Pin[i];
++ if (id == pin->ucGPIO_ID) {
++ gpio.id = pin->ucGPIO_ID;
++ gpio.reg = pin->usGpioPin_AIndex * 4;
++ gpio.mask = (1 << pin->ucGpioPinBitShift);
++ gpio.valid = true;
++ break;
++ }
++ }
++
++ return gpio;
++}
++
++static struct radeon_hpd radeon_atom_get_hpd_info_from_gpio(struct radeon_device *rdev,
++ struct radeon_gpio_rec *gpio)
++{
++ struct radeon_hpd hpd;
++ hpd.gpio = *gpio;
++ if (gpio->reg == AVIVO_DC_GPIO_HPD_A) {
++ switch(gpio->mask) {
++ case (1 << 0):
++ hpd.hpd = RADEON_HPD_1;
++ break;
++ case (1 << 8):
++ hpd.hpd = RADEON_HPD_2;
++ break;
++ case (1 << 16):
++ hpd.hpd = RADEON_HPD_3;
++ break;
++ case (1 << 24):
++ hpd.hpd = RADEON_HPD_4;
++ break;
++ case (1 << 26):
++ hpd.hpd = RADEON_HPD_5;
++ break;
++ case (1 << 28):
++ hpd.hpd = RADEON_HPD_6;
++ break;
++ default:
++ hpd.hpd = RADEON_HPD_NONE;
++ break;
++ }
++ } else
++ hpd.hpd = RADEON_HPD_NONE;
++ return hpd;
++}
++
+ static bool radeon_atom_apply_quirks(struct drm_device *dev,
+ uint32_t supported_device,
+ int *connector_type,
+ struct radeon_i2c_bus_rec *i2c_bus,
+- uint16_t *line_mux)
++ uint16_t *line_mux,
++ struct radeon_hpd *hpd)
+ {
+
+ /* Asus M2A-VM HDMI board lists the DVI port as HDMI */
+@@ -143,6 +232,15 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev,
+ return false;
+ }
+
++ /* Gigabyte X1300 is DVI+VGA, not DVI+DVI */
++ if ((dev->pdev->device == 0x7142) &&
++ (dev->pdev->subsystem_vendor == 0x1458) &&
++ (dev->pdev->subsystem_device == 0x2134)) {
++ if (supported_device == ATOM_DEVICE_DFP1_SUPPORT)
++ return false;
++ }
++
++
+ /* Funky macbooks */
+ if ((dev->pdev->device == 0x71C5) &&
+ (dev->pdev->subsystem_vendor == 0x106b) &&
+@@ -180,6 +278,24 @@ static bool radeon_atom_apply_quirks(struct drm_device *dev,
+ }
+ }
+
++ /* Acer laptop reports DVI-D as DVI-I */
++ if ((dev->pdev->device == 0x95c4) &&
++ (dev->pdev->subsystem_vendor == 0x1025) &&
++ (dev->pdev->subsystem_device == 0x013c)) {
++ if ((*connector_type == DRM_MODE_CONNECTOR_DVII) &&
++ (supported_device == ATOM_DEVICE_DFP1_SUPPORT))
++ *connector_type = DRM_MODE_CONNECTOR_DVID;
++ }
++
++ /* XFX Pine Group device rv730 reports no VGA DDC lines
++ * even though they are wired up to record 0x93
++ */
++ if ((dev->pdev->device == 0x9498) &&
++ (dev->pdev->subsystem_vendor == 0x1682) &&
++ (dev->pdev->subsystem_device == 0x2452)) {
++ struct radeon_device *rdev = dev->dev_private;
++ *i2c_bus = radeon_lookup_i2c_gpio(rdev, 0x93);
++ }
+ return true;
+ }
+
+@@ -239,7 +355,9 @@ const int object_connector_convert[] = {
+ DRM_MODE_CONNECTOR_Unknown,
+ DRM_MODE_CONNECTOR_Unknown,
+ DRM_MODE_CONNECTOR_Unknown,
+- DRM_MODE_CONNECTOR_DisplayPort
++ DRM_MODE_CONNECTOR_DisplayPort,
++ DRM_MODE_CONNECTOR_eDP,
++ DRM_MODE_CONNECTOR_Unknown
+ };
+
+ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
+@@ -248,16 +366,18 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
+ struct radeon_mode_info *mode_info = &rdev->mode_info;
+ struct atom_context *ctx = mode_info->atom_context;
+ int index = GetIndexIntoMasterTable(DATA, Object_Header);
+- uint16_t size, data_offset;
+- uint8_t frev, crev, line_mux = 0;
++ u16 size, data_offset;
++ u8 frev, crev;
+ ATOM_CONNECTOR_OBJECT_TABLE *con_obj;
+ ATOM_DISPLAY_OBJECT_PATH_TABLE *path_obj;
+ ATOM_OBJECT_HEADER *obj_header;
+ int i, j, path_size, device_support;
+ int connector_type;
+- uint16_t igp_lane_info, conn_id, connector_object_id;
++ u16 igp_lane_info, conn_id, connector_object_id;
+ bool linkb;
+ struct radeon_i2c_bus_rec ddc_bus;
++ struct radeon_gpio_rec gpio;
++ struct radeon_hpd hpd;
+
+ atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset);
+
+@@ -284,7 +404,6 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
+ path = (ATOM_DISPLAY_OBJECT_PATH *) addr;
+ path_size += le16_to_cpu(path->usSize);
+ linkb = false;
+-
+ if (device_support & le16_to_cpu(path->usDeviceTag)) {
+ uint8_t con_obj_id, con_obj_num, con_obj_type;
+
+@@ -385,10 +504,9 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
+ }
+ }
+
+- /* look up gpio for ddc */
++ /* look up gpio for ddc, hpd */
+ if ((le16_to_cpu(path->usDeviceTag) &
+- (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT))
+- == 0) {
++ (ATOM_DEVICE_TV_SUPPORT | ATOM_DEVICE_CV_SUPPORT)) == 0) {
+ for (j = 0; j < con_obj->ucNumberOfObjects; j++) {
+ if (le16_to_cpu(path->usConnObjectId) ==
+ le16_to_cpu(con_obj->asObjects[j].
+@@ -402,21 +520,34 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
+ asObjects[j].
+ usRecordOffset));
+ ATOM_I2C_RECORD *i2c_record;
++ ATOM_HPD_INT_RECORD *hpd_record;
++ ATOM_I2C_ID_CONFIG_ACCESS *i2c_config;
++ hpd.hpd = RADEON_HPD_NONE;
+
+ while (record->ucRecordType > 0
+ && record->
+ ucRecordType <=
+ ATOM_MAX_OBJECT_RECORD_NUMBER) {
+- switch (record->
+- ucRecordType) {
++ switch (record->ucRecordType) {
+ case ATOM_I2C_RECORD_TYPE:
+ i2c_record =
+- (ATOM_I2C_RECORD
+- *) record;
+- line_mux =
+- i2c_record->
+- sucI2cId.
+- bfI2C_LineMux;
++ (ATOM_I2C_RECORD *)
++ record;
++ i2c_config =
++ (ATOM_I2C_ID_CONFIG_ACCESS *)
++ &i2c_record->sucI2cId;
++ ddc_bus = radeon_lookup_i2c_gpio(rdev,
++ i2c_config->
++ ucAccess);
++ break;
++ case ATOM_HPD_INT_RECORD_TYPE:
++ hpd_record =
++ (ATOM_HPD_INT_RECORD *)
++ record;
++ gpio = radeon_lookup_gpio(rdev,
++ hpd_record->ucHPDIntGPIOID);
++ hpd = radeon_atom_get_hpd_info_from_gpio(rdev, &gpio);
++ hpd.plugged_state = hpd_record->ucPlugged_PinState;
+ break;
+ }
+ record =
+@@ -429,24 +560,16 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
+ break;
+ }
+ }
+- } else
+- line_mux = 0;
+-
+- if ((le16_to_cpu(path->usDeviceTag) ==
+- ATOM_DEVICE_TV1_SUPPORT)
+- || (le16_to_cpu(path->usDeviceTag) ==
+- ATOM_DEVICE_TV2_SUPPORT)
+- || (le16_to_cpu(path->usDeviceTag) ==
+- ATOM_DEVICE_CV_SUPPORT))
++ } else {
++ hpd.hpd = RADEON_HPD_NONE;
+ ddc_bus.valid = false;
+- else
+- ddc_bus = radeon_lookup_gpio(dev, line_mux);
++ }
+
+ conn_id = le16_to_cpu(path->usConnObjectId);
+
+ if (!radeon_atom_apply_quirks
+ (dev, le16_to_cpu(path->usDeviceTag), &connector_type,
+- &ddc_bus, &conn_id))
++ &ddc_bus, &conn_id, &hpd))
+ continue;
+
+ radeon_add_atom_connector(dev,
+@@ -455,7 +578,8 @@ bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev)
+ usDeviceTag),
+ connector_type, &ddc_bus,
+ linkb, igp_lane_info,
+- connector_object_id);
++ connector_object_id,
++ &hpd);
+
+ }
+ }
+@@ -510,6 +634,7 @@ struct bios_connector {
+ uint16_t devices;
+ int connector_type;
+ struct radeon_i2c_bus_rec ddc_bus;
++ struct radeon_hpd hpd;
+ };
+
+ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
+@@ -525,7 +650,7 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
+ uint16_t device_support;
+ uint8_t dac;
+ union atom_supported_devices *supported_devices;
+- int i, j;
++ int i, j, max_device;
+ struct bios_connector bios_connectors[ATOM_MAX_SUPPORTED_DEVICE];
+
+ atom_parse_data_header(ctx, index, &size, &frev, &crev, &data_offset);
+@@ -535,7 +660,12 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
+
+ device_support = le16_to_cpu(supported_devices->info.usDeviceSupport);
+
+- for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) {
++ if (frev > 1)
++ max_device = ATOM_MAX_SUPPORTED_DEVICE;
++ else
++ max_device = ATOM_MAX_SUPPORTED_DEVICE_INFO;
++
++ for (i = 0; i < max_device; i++) {
+ ATOM_CONNECTOR_INFO_I2C ci =
+ supported_devices->info.asConnInfo[i];
+
+@@ -561,22 +691,8 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
+
+ dac = ci.sucConnectorInfo.sbfAccess.bfAssociatedDAC;
+
+- if ((rdev->family == CHIP_RS690) ||
+- (rdev->family == CHIP_RS740)) {
+- if ((i == ATOM_DEVICE_DFP2_INDEX)
+- && (ci.sucI2cId.sbfAccess.bfI2C_LineMux == 2))
+- bios_connectors[i].line_mux =
+- ci.sucI2cId.sbfAccess.bfI2C_LineMux + 1;
+- else if ((i == ATOM_DEVICE_DFP3_INDEX)
+- && (ci.sucI2cId.sbfAccess.bfI2C_LineMux == 1))
+- bios_connectors[i].line_mux =
+- ci.sucI2cId.sbfAccess.bfI2C_LineMux + 1;
+- else
+- bios_connectors[i].line_mux =
+- ci.sucI2cId.sbfAccess.bfI2C_LineMux;
+- } else
+- bios_connectors[i].line_mux =
+- ci.sucI2cId.sbfAccess.bfI2C_LineMux;
++ bios_connectors[i].line_mux =
++ ci.sucI2cId.ucAccess;
+
+ /* give tv unique connector ids */
+ if (i == ATOM_DEVICE_TV1_INDEX) {
+@@ -590,8 +706,30 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
+ bios_connectors[i].line_mux = 52;
+ } else
+ bios_connectors[i].ddc_bus =
+- radeon_lookup_gpio(dev,
+- bios_connectors[i].line_mux);
++ radeon_lookup_i2c_gpio(rdev,
++ bios_connectors[i].line_mux);
++
++ if ((crev > 1) && (frev > 1)) {
++ u8 isb = supported_devices->info_2d1.asIntSrcInfo[i].ucIntSrcBitmap;
++ switch (isb) {
++ case 0x4:
++ bios_connectors[i].hpd.hpd = RADEON_HPD_1;
++ break;
++ case 0xa:
++ bios_connectors[i].hpd.hpd = RADEON_HPD_2;
++ break;
++ default:
++ bios_connectors[i].hpd.hpd = RADEON_HPD_NONE;
++ break;
++ }
++ } else {
++ if (i == ATOM_DEVICE_DFP1_INDEX)
++ bios_connectors[i].hpd.hpd = RADEON_HPD_1;
++ else if (i == ATOM_DEVICE_DFP2_INDEX)
++ bios_connectors[i].hpd.hpd = RADEON_HPD_2;
++ else
++ bios_connectors[i].hpd.hpd = RADEON_HPD_NONE;
++ }
+
+ /* Always set the connector type to VGA for CRT1/CRT2. if they are
+ * shared with a DVI port, we'll pick up the DVI connector when we
+@@ -603,7 +741,8 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
+
+ if (!radeon_atom_apply_quirks
+ (dev, (1 << i), &bios_connectors[i].connector_type,
+- &bios_connectors[i].ddc_bus, &bios_connectors[i].line_mux))
++ &bios_connectors[i].ddc_bus, &bios_connectors[i].line_mux,
++ &bios_connectors[i].hpd))
+ continue;
+
+ bios_connectors[i].valid = true;
+@@ -618,41 +757,42 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
+ else
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+- (1 <<
+- i),
++ (1 << i),
+ dac),
+ (1 << i));
+ }
+
+ /* combine shared connectors */
+- for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) {
++ for (i = 0; i < max_device; i++) {
+ if (bios_connectors[i].valid) {
+- for (j = 0; j < ATOM_MAX_SUPPORTED_DEVICE; j++) {
++ for (j = 0; j < max_device; j++) {
+ if (bios_connectors[j].valid && (i != j)) {
+ if (bios_connectors[i].line_mux ==
+ bios_connectors[j].line_mux) {
+- if (((bios_connectors[i].
+- devices &
+- (ATOM_DEVICE_DFP_SUPPORT))
+- && (bios_connectors[j].
+- devices &
+- (ATOM_DEVICE_CRT_SUPPORT)))
+- ||
+- ((bios_connectors[j].
+- devices &
+- (ATOM_DEVICE_DFP_SUPPORT))
+- && (bios_connectors[i].
+- devices &
+- (ATOM_DEVICE_CRT_SUPPORT)))) {
+- bios_connectors[i].
+- devices |=
+- bios_connectors[j].
+- devices;
+- bios_connectors[i].
+- connector_type =
+- DRM_MODE_CONNECTOR_DVII;
+- bios_connectors[j].
+- valid = false;
++ /* make sure not to combine LVDS */
++ if (bios_connectors[i].devices & (ATOM_DEVICE_LCD_SUPPORT)) {
++ bios_connectors[i].line_mux = 53;
++ bios_connectors[i].ddc_bus.valid = false;
++ continue;
++ }
++ if (bios_connectors[j].devices & (ATOM_DEVICE_LCD_SUPPORT)) {
++ bios_connectors[j].line_mux = 53;
++ bios_connectors[j].ddc_bus.valid = false;
++ continue;
++ }
++ /* combine analog and digital for DVI-I */
++ if (((bios_connectors[i].devices & (ATOM_DEVICE_DFP_SUPPORT)) &&
++ (bios_connectors[j].devices & (ATOM_DEVICE_CRT_SUPPORT))) ||
++ ((bios_connectors[j].devices & (ATOM_DEVICE_DFP_SUPPORT)) &&
++ (bios_connectors[i].devices & (ATOM_DEVICE_CRT_SUPPORT)))) {
++ bios_connectors[i].devices |=
++ bios_connectors[j].devices;
++ bios_connectors[i].connector_type =
++ DRM_MODE_CONNECTOR_DVII;
++ if (bios_connectors[j].devices & (ATOM_DEVICE_DFP_SUPPORT))
++ bios_connectors[i].hpd =
++ bios_connectors[j].hpd;
++ bios_connectors[j].valid = false;
+ }
+ }
+ }
+@@ -661,7 +801,7 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
+ }
+
+ /* add the connectors */
+- for (i = 0; i < ATOM_MAX_SUPPORTED_DEVICE; i++) {
++ for (i = 0; i < max_device; i++) {
+ if (bios_connectors[i].valid) {
+ uint16_t connector_object_id =
+ atombios_get_connector_object_id(dev,
+@@ -674,7 +814,8 @@ bool radeon_get_atom_connector_info_from_supported_devices_table(struct
+ connector_type,
+ &bios_connectors[i].ddc_bus,
+ false, 0,
+- connector_object_id);
++ connector_object_id,
++ &bios_connectors[i].hpd);
+ }
+ }
+
+@@ -739,7 +880,8 @@ bool radeon_atom_get_clock_info(struct drm_device *dev)
+ * pre-DCE 3.0 r6xx hardware. This might need to be adjusted per
+ * family.
+ */
+- p1pll->pll_out_min = 64800;
++ if (!radeon_new_pll)
++ p1pll->pll_out_min = 64800;
+ }
+
+ p1pll->pll_in_min =
+@@ -805,6 +947,43 @@ bool radeon_atom_get_clock_info(struct drm_device *dev)
+ return false;
+ }
+
++union igp_info {
++ struct _ATOM_INTEGRATED_SYSTEM_INFO info;
++ struct _ATOM_INTEGRATED_SYSTEM_INFO_V2 info_2;
++};
++
++bool radeon_atombios_sideport_present(struct radeon_device *rdev)
++{
++ struct radeon_mode_info *mode_info = &rdev->mode_info;
++ int index = GetIndexIntoMasterTable(DATA, IntegratedSystemInfo);
++ union igp_info *igp_info;
++ u8 frev, crev;
++ u16 data_offset;
++
++ atom_parse_data_header(mode_info->atom_context, index, NULL, &frev,
++ &crev, &data_offset);
++
++ igp_info = (union igp_info *)(mode_info->atom_context->bios +
++ data_offset);
++
++ if (igp_info) {
++ switch (crev) {
++ case 1:
++ if (igp_info->info.ucMemoryType & 0xf0)
++ return true;
++ break;
++ case 2:
++ if (igp_info->info_2.ucMemoryType & 0x0f)
++ return true;
++ break;
++ default:
++ DRM_ERROR("Unsupported IGP table: %d %d\n", frev, crev);
++ break;
++ }
++ }
++ return false;
++}
++
+ bool radeon_atombios_get_tmds_info(struct radeon_encoder *encoder,
+ struct radeon_encoder_int_tmds *tmds)
+ {
+@@ -869,6 +1048,7 @@ static struct radeon_atom_ss *radeon_atombios_get_ss_info(struct
+ struct _ATOM_SPREAD_SPECTRUM_INFO *ss_info;
+ uint8_t frev, crev;
+ struct radeon_atom_ss *ss = NULL;
++ int i;
+
+ if (id > ATOM_MAX_SS_ENTRY)
+ return NULL;
+@@ -886,12 +1066,18 @@ static struct radeon_atom_ss *radeon_atombios_get_ss_info(struct
+ if (!ss)
+ return NULL;
+
+- ss->percentage = le16_to_cpu(ss_info->asSS_Info[id].usSpreadSpectrumPercentage);
+- ss->type = ss_info->asSS_Info[id].ucSpreadSpectrumType;
+- ss->step = ss_info->asSS_Info[id].ucSS_Step;
+- ss->delay = ss_info->asSS_Info[id].ucSS_Delay;
+- ss->range = ss_info->asSS_Info[id].ucSS_Range;
+- ss->refdiv = ss_info->asSS_Info[id].ucRecommendedRef_Div;
++ for (i = 0; i < ATOM_MAX_SS_ENTRY; i++) {
++ if (ss_info->asSS_Info[i].ucSS_Id == id) {
++ ss->percentage =
++ le16_to_cpu(ss_info->asSS_Info[i].usSpreadSpectrumPercentage);
++ ss->type = ss_info->asSS_Info[i].ucSpreadSpectrumType;
++ ss->step = ss_info->asSS_Info[i].ucSS_Step;
++ ss->delay = ss_info->asSS_Info[i].ucSS_Delay;
++ ss->range = ss_info->asSS_Info[i].ucSS_Range;
++ ss->refdiv = ss_info->asSS_Info[i].ucRecommendedRef_Div;
++ break;
++ }
++ }
+ }
+ return ss;
+ }
+@@ -909,7 +1095,7 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_mode_info *mode_info = &rdev->mode_info;
+ int index = GetIndexIntoMasterTable(DATA, LVDS_Info);
+- uint16_t data_offset;
++ uint16_t data_offset, misc;
+ union lvds_info *lvds_info;
+ uint8_t frev, crev;
+ struct radeon_encoder_atom_dig *lvds = NULL;
+@@ -948,6 +1134,19 @@ struct radeon_encoder_atom_dig *radeon_atombios_get_lvds_info(struct
+ lvds->panel_pwr_delay =
+ le16_to_cpu(lvds_info->info.usOffDelayInMs);
+ lvds->lvds_misc = lvds_info->info.ucLVDS_Misc;
++
++ misc = le16_to_cpu(lvds_info->info.sLCDTiming.susModeMiscInfo.usAccess);
++ if (misc & ATOM_VSYNC_POLARITY)
++ lvds->native_mode.flags |= DRM_MODE_FLAG_NVSYNC;
++ if (misc & ATOM_HSYNC_POLARITY)
++ lvds->native_mode.flags |= DRM_MODE_FLAG_NHSYNC;
++ if (misc & ATOM_COMPOSITESYNC)
++ lvds->native_mode.flags |= DRM_MODE_FLAG_CSYNC;
++ if (misc & ATOM_INTERLACE)
++ lvds->native_mode.flags |= DRM_MODE_FLAG_INTERLACE;
++ if (misc & ATOM_DOUBLE_CLOCK_MODE)
++ lvds->native_mode.flags |= DRM_MODE_FLAG_DBLSCAN;
++
+ /* set crtc values */
+ drm_mode_set_crtcinfo(&lvds->native_mode, CRTC_INTERLACE_HALVE_V);
+
+@@ -1082,6 +1281,61 @@ bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index,
+ return true;
+ }
+
++enum radeon_tv_std
++radeon_atombios_get_tv_info(struct radeon_device *rdev)
++{
++ struct radeon_mode_info *mode_info = &rdev->mode_info;
++ int index = GetIndexIntoMasterTable(DATA, AnalogTV_Info);
++ uint16_t data_offset;
++ uint8_t frev, crev;
++ struct _ATOM_ANALOG_TV_INFO *tv_info;
++ enum radeon_tv_std tv_std = TV_STD_NTSC;
++
++ atom_parse_data_header(mode_info->atom_context, index, NULL, &frev, &crev, &data_offset);
++
++ tv_info = (struct _ATOM_ANALOG_TV_INFO *)(mode_info->atom_context->bios + data_offset);
++
++ switch (tv_info->ucTV_BootUpDefaultStandard) {
++ case ATOM_TV_NTSC:
++ tv_std = TV_STD_NTSC;
++ DRM_INFO("Default TV standard: NTSC\n");
++ break;
++ case ATOM_TV_NTSCJ:
++ tv_std = TV_STD_NTSC_J;
++ DRM_INFO("Default TV standard: NTSC-J\n");
++ break;
++ case ATOM_TV_PAL:
++ tv_std = TV_STD_PAL;
++ DRM_INFO("Default TV standard: PAL\n");
++ break;
++ case ATOM_TV_PALM:
++ tv_std = TV_STD_PAL_M;
++ DRM_INFO("Default TV standard: PAL-M\n");
++ break;
++ case ATOM_TV_PALN:
++ tv_std = TV_STD_PAL_N;
++ DRM_INFO("Default TV standard: PAL-N\n");
++ break;
++ case ATOM_TV_PALCN:
++ tv_std = TV_STD_PAL_CN;
++ DRM_INFO("Default TV standard: PAL-CN\n");
++ break;
++ case ATOM_TV_PAL60:
++ tv_std = TV_STD_PAL_60;
++ DRM_INFO("Default TV standard: PAL-60\n");
++ break;
++ case ATOM_TV_SECAM:
++ tv_std = TV_STD_SECAM;
++ DRM_INFO("Default TV standard: SECAM\n");
++ break;
++ default:
++ tv_std = TV_STD_NTSC;
++ DRM_INFO("Unknown TV standard; defaulting to NTSC\n");
++ break;
++ }
++ return tv_std;
++}
++
+ struct radeon_encoder_tv_dac *
+ radeon_atombios_get_tv_dac_info(struct radeon_encoder *encoder)
+ {
+@@ -1117,6 +1371,7 @@ radeon_atombios_get_tv_dac_info(struct radeon_encoder *encoder)
+ dac = dac_info->ucDAC2_NTSC_DAC_Adjustment;
+ tv_dac->ntsc_tvdac_adj = (bg << 16) | (dac << 20);
+
++ tv_dac->tv_std = radeon_atombios_get_tv_info(rdev);
+ }
+ return tv_dac;
+ }
+diff --git a/drivers/gpu/drm/radeon/radeon_benchmark.c b/drivers/gpu/drm/radeon/radeon_benchmark.c
+index 10bd50a..7932dc4 100644
+--- a/drivers/gpu/drm/radeon/radeon_benchmark.c
++++ b/drivers/gpu/drm/radeon/radeon_benchmark.c
+@@ -29,8 +29,8 @@
+ void radeon_benchmark_move(struct radeon_device *rdev, unsigned bsize,
+ unsigned sdomain, unsigned ddomain)
+ {
+- struct radeon_object *dobj = NULL;
+- struct radeon_object *sobj = NULL;
++ struct radeon_bo *dobj = NULL;
++ struct radeon_bo *sobj = NULL;
+ struct radeon_fence *fence = NULL;
+ uint64_t saddr, daddr;
+ unsigned long start_jiffies;
+@@ -41,47 +41,66 @@ void radeon_benchmark_move(struct radeon_device *rdev, unsigned bsize,
+
+ size = bsize;
+ n = 1024;
+- r = radeon_object_create(rdev, NULL, size, true, sdomain, false, &sobj);
++ r = radeon_bo_create(rdev, NULL, size, true, sdomain, &sobj);
+ if (r) {
+ goto out_cleanup;
+ }
+- r = radeon_object_pin(sobj, sdomain, &saddr);
++ r = radeon_bo_reserve(sobj, false);
++ if (unlikely(r != 0))
++ goto out_cleanup;
++ r = radeon_bo_pin(sobj, sdomain, &saddr);
++ radeon_bo_unreserve(sobj);
+ if (r) {
+ goto out_cleanup;
+ }
+- r = radeon_object_create(rdev, NULL, size, true, ddomain, false, &dobj);
++ r = radeon_bo_create(rdev, NULL, size, true, ddomain, &dobj);
+ if (r) {
+ goto out_cleanup;
+ }
+- r = radeon_object_pin(dobj, ddomain, &daddr);
++ r = radeon_bo_reserve(dobj, false);
++ if (unlikely(r != 0))
++ goto out_cleanup;
++ r = radeon_bo_pin(dobj, ddomain, &daddr);
++ radeon_bo_unreserve(dobj);
+ if (r) {
+ goto out_cleanup;
+ }
+- start_jiffies = jiffies;
+- for (i = 0; i < n; i++) {
+- r = radeon_fence_create(rdev, &fence);
+- if (r) {
+- goto out_cleanup;
++
++ /* r100 doesn't have dma engine so skip the test */
++ if (rdev->asic->copy_dma) {
++
++ start_jiffies = jiffies;
++ for (i = 0; i < n; i++) {
++ r = radeon_fence_create(rdev, &fence);
++ if (r) {
++ goto out_cleanup;
++ }
++
++ r = radeon_copy_dma(rdev, saddr, daddr,
++ size / RADEON_GPU_PAGE_SIZE, fence);
++
++ if (r) {
++ goto out_cleanup;
++ }
++ r = radeon_fence_wait(fence, false);
++ if (r) {
++ goto out_cleanup;
++ }
++ radeon_fence_unref(&fence);
+ }
+- r = radeon_copy_dma(rdev, saddr, daddr, size / RADEON_GPU_PAGE_SIZE, fence);
+- if (r) {
+- goto out_cleanup;
++ end_jiffies = jiffies;
++ time = end_jiffies - start_jiffies;
++ time = jiffies_to_msecs(time);
++ if (time > 0) {
++ i = ((n * size) >> 10) / time;
++ printk(KERN_INFO "radeon: dma %u bo moves of %ukb from"
++ " %d to %d in %lums (%ukb/ms %ukb/s %uM/s)\n",
++ n, size >> 10,
++ sdomain, ddomain, time,
++ i, i * 1000, (i * 1000) / 1024);
+ }
+- r = radeon_fence_wait(fence, false);
+- if (r) {
+- goto out_cleanup;
+- }
+- radeon_fence_unref(&fence);
+- }
+- end_jiffies = jiffies;
+- time = end_jiffies - start_jiffies;
+- time = jiffies_to_msecs(time);
+- if (time > 0) {
+- i = ((n * size) >> 10) / time;
+- printk(KERN_INFO "radeon: dma %u bo moves of %ukb from %d to %d"
+- " in %lums (%ukb/ms %ukb/s %uM/s)\n", n, size >> 10,
+- sdomain, ddomain, time, i, i * 1000, (i * 1000) / 1024);
+ }
++
+ start_jiffies = jiffies;
+ for (i = 0; i < n; i++) {
+ r = radeon_fence_create(rdev, &fence);
+@@ -109,12 +128,20 @@ void radeon_benchmark_move(struct radeon_device *rdev, unsigned bsize,
+ }
+ out_cleanup:
+ if (sobj) {
+- radeon_object_unpin(sobj);
+- radeon_object_unref(&sobj);
++ r = radeon_bo_reserve(sobj, false);
++ if (likely(r == 0)) {
++ radeon_bo_unpin(sobj);
++ radeon_bo_unreserve(sobj);
++ }
++ radeon_bo_unref(&sobj);
+ }
+ if (dobj) {
+- radeon_object_unpin(dobj);
+- radeon_object_unref(&dobj);
++ r = radeon_bo_reserve(dobj, false);
++ if (likely(r == 0)) {
++ radeon_bo_unpin(dobj);
++ radeon_bo_unreserve(dobj);
++ }
++ radeon_bo_unref(&dobj);
+ }
+ if (fence) {
+ radeon_fence_unref(&fence);
+diff --git a/drivers/gpu/drm/radeon/radeon_bios.c b/drivers/gpu/drm/radeon/radeon_bios.c
+index 9069217..26856ed 100644
+--- a/drivers/gpu/drm/radeon/radeon_bios.c
++++ b/drivers/gpu/drm/radeon/radeon_bios.c
+@@ -411,6 +411,12 @@ bool radeon_get_bios(struct radeon_device *rdev)
+ goto free_bios;
+ }
+
++ tmp = RBIOS16(0x18);
++ if (RBIOS8(tmp + 0x14) != 0x0) {
++ DRM_INFO("Not an x86 BIOS ROM, not using.\n");
++ goto free_bios;
++ }
++
+ rdev->bios_header_start = RBIOS16(0x48);
+ if (!rdev->bios_header_start) {
+ goto free_bios;
+diff --git a/drivers/gpu/drm/radeon/radeon_clocks.c b/drivers/gpu/drm/radeon/radeon_clocks.c
+index a813541..73c4405 100644
+--- a/drivers/gpu/drm/radeon/radeon_clocks.c
++++ b/drivers/gpu/drm/radeon/radeon_clocks.c
+@@ -44,6 +44,10 @@ uint32_t radeon_legacy_get_engine_clock(struct radeon_device *rdev)
+
+ ref_div =
+ RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) & RADEON_M_SPLL_REF_DIV_MASK;
++
++ if (ref_div == 0)
++ return 0;
++
+ sclk = fb_div / ref_div;
+
+ post_div = RREG32_PLL(RADEON_SCLK_CNTL) & RADEON_SCLK_SRC_SEL_MASK;
+@@ -52,13 +56,13 @@ uint32_t radeon_legacy_get_engine_clock(struct radeon_device *rdev)
+ else if (post_div == 3)
+ sclk >>= 2;
+ else if (post_div == 4)
+- sclk >>= 4;
++ sclk >>= 3;
+
+ return sclk;
+ }
+
+ /* 10 khz */
+-static uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev)
++uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev)
+ {
+ struct radeon_pll *mpll = &rdev->clock.mpll;
+ uint32_t fb_div, ref_div, post_div, mclk;
+@@ -70,6 +74,10 @@ static uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev)
+
+ ref_div =
+ RREG32_PLL(RADEON_M_SPLL_REF_FB_DIV) & RADEON_M_SPLL_REF_DIV_MASK;
++
++ if (ref_div == 0)
++ return 0;
++
+ mclk = fb_div / ref_div;
+
+ post_div = RREG32_PLL(RADEON_MCLK_CNTL) & 0x7;
+@@ -78,7 +86,7 @@ static uint32_t radeon_legacy_get_memory_clock(struct radeon_device *rdev)
+ else if (post_div == 3)
+ mclk >>= 2;
+ else if (post_div == 4)
+- mclk >>= 4;
++ mclk >>= 3;
+
+ return mclk;
+ }
+@@ -98,8 +106,19 @@ void radeon_get_clock_info(struct drm_device *dev)
+ ret = radeon_combios_get_clock_info(dev);
+
+ if (ret) {
+- if (p1pll->reference_div < 2)
+- p1pll->reference_div = 12;
++ if (p1pll->reference_div < 2) {
++ if (!ASIC_IS_AVIVO(rdev)) {
++ u32 tmp = RREG32_PLL(RADEON_PPLL_REF_DIV);
++ if (ASIC_IS_R300(rdev))
++ p1pll->reference_div =
++ (tmp & R300_PPLL_REF_DIV_ACC_MASK) >> R300_PPLL_REF_DIV_ACC_SHIFT;
++ else
++ p1pll->reference_div = tmp & RADEON_PPLL_REF_DIV_MASK;
++ if (p1pll->reference_div < 2)
++ p1pll->reference_div = 12;
++ } else
++ p1pll->reference_div = 12;
++ }
+ if (p2pll->reference_div < 2)
+ p2pll->reference_div = 12;
+ if (rdev->family < CHIP_RS600) {
+diff --git a/drivers/gpu/drm/radeon/radeon_combios.c b/drivers/gpu/drm/radeon/radeon_combios.c
+index 5253cbf..69f487d 100644
+--- a/drivers/gpu/drm/radeon/radeon_combios.c
++++ b/drivers/gpu/drm/radeon/radeon_combios.c
+@@ -50,7 +50,8 @@ radeon_add_legacy_connector(struct drm_device *dev,
+ uint32_t supported_device,
+ int connector_type,
+ struct radeon_i2c_bus_rec *i2c_bus,
+- uint16_t connector_object_id);
++ uint16_t connector_object_id,
++ struct radeon_hpd *hpd);
+
+ /* from radeon_legacy_encoder.c */
+ extern void
+@@ -442,38 +443,103 @@ static uint16_t combios_get_table_offset(struct drm_device *dev,
+
+ }
+
+-struct radeon_i2c_bus_rec combios_setup_i2c_bus(int ddc_line)
++bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev)
++{
++ int edid_info;
++ struct edid *edid;
++ edid_info = combios_get_table_offset(rdev->ddev, COMBIOS_HARDCODED_EDID_TABLE);
++ if (!edid_info)
++ return false;
++
++ edid = kmalloc(EDID_LENGTH * (DRM_MAX_EDID_EXT_NUM + 1),
++ GFP_KERNEL);
++ if (edid == NULL)
++ return false;
++
++ memcpy((unsigned char *)edid,
++ (unsigned char *)(rdev->bios + edid_info), EDID_LENGTH);
++
++ if (!drm_edid_is_valid(edid)) {
++ kfree(edid);
++ return false;
++ }
++
++ rdev->mode_info.bios_hardcoded_edid = edid;
++ return true;
++}
++
++struct edid *
++radeon_combios_get_hardcoded_edid(struct radeon_device *rdev)
++{
++ if (rdev->mode_info.bios_hardcoded_edid)
++ return rdev->mode_info.bios_hardcoded_edid;
++ return NULL;
++}
++
++static struct radeon_i2c_bus_rec combios_setup_i2c_bus(struct radeon_device *rdev,
++ int ddc_line)
+ {
+ struct radeon_i2c_bus_rec i2c;
+
+- i2c.mask_clk_mask = RADEON_GPIO_EN_1;
+- i2c.mask_data_mask = RADEON_GPIO_EN_0;
+- i2c.a_clk_mask = RADEON_GPIO_A_1;
+- i2c.a_data_mask = RADEON_GPIO_A_0;
+- i2c.put_clk_mask = RADEON_GPIO_EN_1;
+- i2c.put_data_mask = RADEON_GPIO_EN_0;
+- i2c.get_clk_mask = RADEON_GPIO_Y_1;
+- i2c.get_data_mask = RADEON_GPIO_Y_0;
+- if ((ddc_line == RADEON_LCD_GPIO_MASK) ||
+- (ddc_line == RADEON_MDGPIO_EN_REG)) {
+- i2c.mask_clk_reg = ddc_line;
+- i2c.mask_data_reg = ddc_line;
+- i2c.a_clk_reg = ddc_line;
+- i2c.a_data_reg = ddc_line;
+- i2c.put_clk_reg = ddc_line;
+- i2c.put_data_reg = ddc_line;
+- i2c.get_clk_reg = ddc_line + 4;
+- i2c.get_data_reg = ddc_line + 4;
++ if (ddc_line == RADEON_GPIOPAD_MASK) {
++ i2c.mask_clk_reg = RADEON_GPIOPAD_MASK;
++ i2c.mask_data_reg = RADEON_GPIOPAD_MASK;
++ i2c.a_clk_reg = RADEON_GPIOPAD_A;
++ i2c.a_data_reg = RADEON_GPIOPAD_A;
++ i2c.en_clk_reg = RADEON_GPIOPAD_EN;
++ i2c.en_data_reg = RADEON_GPIOPAD_EN;
++ i2c.y_clk_reg = RADEON_GPIOPAD_Y;
++ i2c.y_data_reg = RADEON_GPIOPAD_Y;
++ } else if (ddc_line == RADEON_MDGPIO_MASK) {
++ i2c.mask_clk_reg = RADEON_MDGPIO_MASK;
++ i2c.mask_data_reg = RADEON_MDGPIO_MASK;
++ i2c.a_clk_reg = RADEON_MDGPIO_A;
++ i2c.a_data_reg = RADEON_MDGPIO_A;
++ i2c.en_clk_reg = RADEON_MDGPIO_EN;
++ i2c.en_data_reg = RADEON_MDGPIO_EN;
++ i2c.y_clk_reg = RADEON_MDGPIO_Y;
++ i2c.y_data_reg = RADEON_MDGPIO_Y;
+ } else {
++ i2c.mask_clk_mask = RADEON_GPIO_EN_1;
++ i2c.mask_data_mask = RADEON_GPIO_EN_0;
++ i2c.a_clk_mask = RADEON_GPIO_A_1;
++ i2c.a_data_mask = RADEON_GPIO_A_0;
++ i2c.en_clk_mask = RADEON_GPIO_EN_1;
++ i2c.en_data_mask = RADEON_GPIO_EN_0;
++ i2c.y_clk_mask = RADEON_GPIO_Y_1;
++ i2c.y_data_mask = RADEON_GPIO_Y_0;
++
+ i2c.mask_clk_reg = ddc_line;
+ i2c.mask_data_reg = ddc_line;
+ i2c.a_clk_reg = ddc_line;
+ i2c.a_data_reg = ddc_line;
+- i2c.put_clk_reg = ddc_line;
+- i2c.put_data_reg = ddc_line;
+- i2c.get_clk_reg = ddc_line;
+- i2c.get_data_reg = ddc_line;
++ i2c.en_clk_reg = ddc_line;
++ i2c.en_data_reg = ddc_line;
++ i2c.y_clk_reg = ddc_line;
++ i2c.y_data_reg = ddc_line;
++ }
++
++ if (rdev->family < CHIP_R200)
++ i2c.hw_capable = false;
++ else {
++ switch (ddc_line) {
++ case RADEON_GPIO_VGA_DDC:
++ case RADEON_GPIO_DVI_DDC:
++ i2c.hw_capable = true;
++ break;
++ case RADEON_GPIO_MONID:
++ /* hw i2c on RADEON_GPIO_MONID doesn't seem to work
++ * reliably on some pre-r4xx hardware; not sure why.
++ */
++ i2c.hw_capable = false;
++ break;
++ default:
++ i2c.hw_capable = false;
++ break;
++ }
+ }
++ i2c.mm_i2c = false;
++ i2c.i2c_id = 0;
+
+ if (ddc_line)
+ i2c.valid = true;
+@@ -495,7 +561,7 @@ bool radeon_combios_get_clock_info(struct drm_device *dev)
+ uint16_t sclk, mclk;
+
+ if (rdev->bios == NULL)
+- return NULL;
++ return false;
+
+ pll_info = combios_get_table_offset(dev, COMBIOS_PLL_INFO_TABLE);
+ if (pll_info) {
+@@ -562,6 +628,48 @@ bool radeon_combios_get_clock_info(struct drm_device *dev)
+ return false;
+ }
+
++bool radeon_combios_sideport_present(struct radeon_device *rdev)
++{
++ struct drm_device *dev = rdev->ddev;
++ u16 igp_info;
++
++ igp_info = combios_get_table_offset(dev, COMBIOS_INTEGRATED_SYSTEM_INFO_TABLE);
++
++ if (igp_info) {
++ if (RBIOS16(igp_info + 0x4))
++ return true;
++ }
++ return false;
++}
++
++static const uint32_t default_primarydac_adj[CHIP_LAST] = {
++ 0x00000808, /* r100 */
++ 0x00000808, /* rv100 */
++ 0x00000808, /* rs100 */
++ 0x00000808, /* rv200 */
++ 0x00000808, /* rs200 */
++ 0x00000808, /* r200 */
++ 0x00000808, /* rv250 */
++ 0x00000000, /* rs300 */
++ 0x00000808, /* rv280 */
++ 0x00000808, /* r300 */
++ 0x00000808, /* r350 */
++ 0x00000808, /* rv350 */
++ 0x00000808, /* rv380 */
++ 0x00000808, /* r420 */
++ 0x00000808, /* r423 */
++ 0x00000808, /* rv410 */
++ 0x00000000, /* rs400 */
++ 0x00000000, /* rs480 */
++};
++
++static void radeon_legacy_get_primary_dac_info_from_table(struct radeon_device *rdev,
++ struct radeon_encoder_primary_dac *p_dac)
++{
++ p_dac->ps2_pdac_adj = default_primarydac_adj[rdev->family];
++ return;
++}
++
+ struct radeon_encoder_primary_dac *radeon_combios_get_primary_dac_info(struct
+ radeon_encoder
+ *encoder)
+@@ -571,20 +679,20 @@ struct radeon_encoder_primary_dac *radeon_combios_get_primary_dac_info(struct
+ uint16_t dac_info;
+ uint8_t rev, bg, dac;
+ struct radeon_encoder_primary_dac *p_dac = NULL;
++ int found = 0;
+
+- if (rdev->bios == NULL)
++ p_dac = kzalloc(sizeof(struct radeon_encoder_primary_dac),
++ GFP_KERNEL);
++
++ if (!p_dac)
+ return NULL;
+
++ if (rdev->bios == NULL)
++ goto out;
++
+ /* check CRT table */
+ dac_info = combios_get_table_offset(dev, COMBIOS_CRT_INFO_TABLE);
+ if (dac_info) {
+- p_dac =
+- kzalloc(sizeof(struct radeon_encoder_primary_dac),
+- GFP_KERNEL);
+-
+- if (!p_dac)
+- return NULL;
+-
+ rev = RBIOS8(dac_info) & 0x3;
+ if (rev < 2) {
+ bg = RBIOS8(dac_info + 0x2) & 0xf;
+@@ -595,20 +703,26 @@ struct radeon_encoder_primary_dac *radeon_combios_get_primary_dac_info(struct
+ dac = RBIOS8(dac_info + 0x3) & 0xf;
+ p_dac->ps2_pdac_adj = (bg << 8) | (dac);
+ }
+-
++ found = 1;
+ }
+
++out:
++ if (!found) /* fallback to defaults */
++ radeon_legacy_get_primary_dac_info_from_table(rdev, p_dac);
++
+ return p_dac;
+ }
+
+-static enum radeon_tv_std
+-radeon_combios_get_tv_info(struct radeon_encoder *encoder)
++enum radeon_tv_std
++radeon_combios_get_tv_info(struct radeon_device *rdev)
+ {
+- struct drm_device *dev = encoder->base.dev;
+- struct radeon_device *rdev = dev->dev_private;
++ struct drm_device *dev = rdev->ddev;
+ uint16_t tv_info;
+ enum radeon_tv_std tv_std = TV_STD_NTSC;
+
++ if (rdev->bios == NULL)
++ return tv_std;
++
+ tv_info = combios_get_table_offset(dev, COMBIOS_TV_INFO_TABLE);
+ if (tv_info) {
+ if (RBIOS8(tv_info + 6) == 'T') {
+@@ -746,7 +860,7 @@ struct radeon_encoder_tv_dac *radeon_combios_get_tv_dac_info(struct
+ tv_dac->ntsc_tvdac_adj = (bg << 16) | (dac << 20);
+ found = 1;
+ }
+- tv_dac->tv_std = radeon_combios_get_tv_info(encoder);
++ tv_dac->tv_std = radeon_combios_get_tv_info(rdev);
+ }
+ if (!found) {
+ /* then check CRT table */
+@@ -890,8 +1004,7 @@ struct radeon_encoder_lvds *radeon_combios_get_lvds_info(struct radeon_encoder
+ lvds->native_mode.vdisplay);
+
+ lvds->panel_vcc_delay = RBIOS16(lcd_info + 0x2c);
+- if (lvds->panel_vcc_delay > 2000 || lvds->panel_vcc_delay < 0)
+- lvds->panel_vcc_delay = 2000;
++ lvds->panel_vcc_delay = min_t(u16, lvds->panel_vcc_delay, 2000);
+
+ lvds->panel_pwr_delay = RBIOS8(lcd_info + 0x24);
+ lvds->panel_digon_delay = RBIOS16(lcd_info + 0x38) & 0xf;
+@@ -993,8 +1106,8 @@ static const struct radeon_tmds_pll default_tmds_pll[CHIP_LAST][4] = {
+ {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_R420 */
+ {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_R423 */
+ {{0xffffffff, 0xb01cb}, {0, 0}, {0, 0}, {0, 0}}, /* CHIP_RV410 */
+- {{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}}, /* CHIP_RS400 */
+- {{15000, 0xb0155}, {0xffffffff, 0xb01cb}, {0, 0}, {0, 0}}, /* CHIP_RS480 */
++ { {0, 0}, {0, 0}, {0, 0}, {0, 0} }, /* CHIP_RS400 */
++ { {0, 0}, {0, 0}, {0, 0}, {0, 0} }, /* CHIP_RS480 */
+ };
+
+ bool radeon_legacy_get_tmds_info_from_table(struct radeon_encoder *encoder,
+@@ -1028,7 +1141,6 @@ bool radeon_legacy_get_tmds_info_from_combios(struct radeon_encoder *encoder,
+ tmds_info = combios_get_table_offset(dev, COMBIOS_DFP_INFO_TABLE);
+
+ if (tmds_info) {
+-
+ ver = RBIOS8(tmds_info);
+ DRM_INFO("DFP table revision: %d\n", ver);
+ if (ver == 3) {
+@@ -1063,51 +1175,139 @@ bool radeon_legacy_get_tmds_info_from_combios(struct radeon_encoder *encoder,
+ tmds->tmds_pll[i].value);
+ }
+ }
+- } else
++ } else {
+ DRM_INFO("No TMDS info found in BIOS\n");
++ return false;
++ }
+ return true;
+ }
+
+-struct radeon_encoder_int_tmds *radeon_combios_get_tmds_info(struct radeon_encoder *encoder)
++bool radeon_legacy_get_ext_tmds_info_from_table(struct radeon_encoder *encoder,
++ struct radeon_encoder_ext_tmds *tmds)
+ {
+- struct radeon_encoder_int_tmds *tmds = NULL;
+- bool ret;
+-
+- tmds = kzalloc(sizeof(struct radeon_encoder_int_tmds), GFP_KERNEL);
++ struct drm_device *dev = encoder->base.dev;
++ struct radeon_device *rdev = dev->dev_private;
++ struct radeon_i2c_bus_rec i2c_bus;
+
+- if (!tmds)
+- return NULL;
++ /* default for macs */
++ i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
++ tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
+
+- ret = radeon_legacy_get_tmds_info_from_combios(encoder, tmds);
+- if (ret == false)
+- radeon_legacy_get_tmds_info_from_table(encoder, tmds);
++ /* XXX some macs have duallink chips */
++ switch (rdev->mode_info.connector_table) {
++ case CT_POWERBOOK_EXTERNAL:
++ case CT_MINI_EXTERNAL:
++ default:
++ tmds->dvo_chip = DVO_SIL164;
++ tmds->slave_addr = 0x70 >> 1; /* 7 bit addressing */
++ break;
++ }
+
+- return tmds;
++ return true;
+ }
+
+-void radeon_combios_get_ext_tmds_info(struct radeon_encoder *encoder)
++bool radeon_legacy_get_ext_tmds_info_from_combios(struct radeon_encoder *encoder,
++ struct radeon_encoder_ext_tmds *tmds)
+ {
+ struct drm_device *dev = encoder->base.dev;
+ struct radeon_device *rdev = dev->dev_private;
+- uint16_t ext_tmds_info;
+- uint8_t ver;
++ uint16_t offset;
++ uint8_t ver, id, blocks, clk, data;
++ int i;
++ enum radeon_combios_ddc gpio;
++ struct radeon_i2c_bus_rec i2c_bus;
+
+ if (rdev->bios == NULL)
+- return;
++ return false;
+
+- ext_tmds_info =
+- combios_get_table_offset(dev, COMBIOS_EXT_TMDS_INFO_TABLE);
+- if (ext_tmds_info) {
+- ver = RBIOS8(ext_tmds_info);
+- DRM_INFO("External TMDS Table revision: %d\n", ver);
+- // TODO
++ tmds->i2c_bus = NULL;
++ if (rdev->flags & RADEON_IS_IGP) {
++ offset = combios_get_table_offset(dev, COMBIOS_I2C_INFO_TABLE);
++ if (offset) {
++ ver = RBIOS8(offset);
++ DRM_INFO("GPIO Table revision: %d\n", ver);
++ blocks = RBIOS8(offset + 2);
++ for (i = 0; i < blocks; i++) {
++ id = RBIOS8(offset + 3 + (i * 5) + 0);
++ if (id == 136) {
++ clk = RBIOS8(offset + 3 + (i * 5) + 3);
++ data = RBIOS8(offset + 3 + (i * 5) + 4);
++ i2c_bus.valid = true;
++ i2c_bus.mask_clk_mask = (1 << clk);
++ i2c_bus.mask_data_mask = (1 << data);
++ i2c_bus.a_clk_mask = (1 << clk);
++ i2c_bus.a_data_mask = (1 << data);
++ i2c_bus.en_clk_mask = (1 << clk);
++ i2c_bus.en_data_mask = (1 << data);
++ i2c_bus.y_clk_mask = (1 << clk);
++ i2c_bus.y_data_mask = (1 << data);
++ i2c_bus.mask_clk_reg = RADEON_GPIOPAD_MASK;
++ i2c_bus.mask_data_reg = RADEON_GPIOPAD_MASK;
++ i2c_bus.a_clk_reg = RADEON_GPIOPAD_A;
++ i2c_bus.a_data_reg = RADEON_GPIOPAD_A;
++ i2c_bus.en_clk_reg = RADEON_GPIOPAD_EN;
++ i2c_bus.en_data_reg = RADEON_GPIOPAD_EN;
++ i2c_bus.y_clk_reg = RADEON_GPIOPAD_Y;
++ i2c_bus.y_data_reg = RADEON_GPIOPAD_Y;
++ tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
++ tmds->dvo_chip = DVO_SIL164;
++ tmds->slave_addr = 0x70 >> 1; /* 7 bit addressing */
++ break;
++ }
++ }
++ }
++ } else {
++ offset = combios_get_table_offset(dev, COMBIOS_EXT_TMDS_INFO_TABLE);
++ if (offset) {
++ ver = RBIOS8(offset);
++ DRM_INFO("External TMDS Table revision: %d\n", ver);
++ tmds->slave_addr = RBIOS8(offset + 4 + 2);
++ tmds->slave_addr >>= 1; /* 7 bit addressing */
++ gpio = RBIOS8(offset + 4 + 3);
++ switch (gpio) {
++ case DDC_MONID:
++ i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
++ tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
++ break;
++ case DDC_DVI:
++ i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
++ tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
++ break;
++ case DDC_VGA:
++ i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
++ tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
++ break;
++ case DDC_CRT2:
++ /* R3xx+ chips don't have GPIO_CRT2_DDC gpio pad */
++ if (rdev->family >= CHIP_R300)
++ i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
++ else
++ i2c_bus = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
++ tmds->i2c_bus = radeon_i2c_create(dev, &i2c_bus, "DVO");
++ break;
++ case DDC_LCD: /* MM i2c */
++ DRM_ERROR("MM i2c requires hw i2c engine\n");
++ break;
++ default:
++ DRM_ERROR("Unsupported gpio %d\n", gpio);
++ break;
++ }
++ }
++ }
++
++ if (!tmds->i2c_bus) {
++ DRM_INFO("No valid Ext TMDS info found in BIOS\n");
++ return false;
+ }
++
++ return true;
+ }
+
+ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ {
+ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_i2c_bus_rec ddc_i2c;
++ struct radeon_hpd hpd;
+
+ rdev->mode_info.connector_table = radeon_connector_table;
+ if (rdev->mode_info.connector_table == CT_NONE) {
+@@ -1168,7 +1368,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ /* these are the most common settings */
+ if (rdev->flags & RADEON_SINGLE_CRTC) {
+ /* VGA - primary dac */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_CRT1_SUPPORT,
+@@ -1178,10 +1379,12 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_CRT1_SUPPORT,
+ DRM_MODE_CONNECTOR_VGA,
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_VGA);
++ CONNECTOR_OBJECT_ID_VGA,
++ &hpd);
+ } else if (rdev->flags & RADEON_IS_MOBILITY) {
+ /* LVDS */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_LCD_GPIO_MASK);
++ ddc_i2c = combios_setup_i2c_bus(rdev, 0);
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_LCD1_SUPPORT,
+@@ -1191,10 +1394,12 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_LCD1_SUPPORT,
+ DRM_MODE_CONNECTOR_LVDS,
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_LVDS);
++ CONNECTOR_OBJECT_ID_LVDS,
++ &hpd);
+
+ /* VGA - primary dac */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_CRT1_SUPPORT,
+@@ -1204,10 +1409,12 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_CRT1_SUPPORT,
+ DRM_MODE_CONNECTOR_VGA,
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_VGA);
++ CONNECTOR_OBJECT_ID_VGA,
++ &hpd);
+ } else {
+ /* DVI-I - tv dac, int tmds */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
++ hpd.hpd = RADEON_HPD_1;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_DFP1_SUPPORT,
+@@ -1223,10 +1430,12 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_CRT2_SUPPORT,
+ DRM_MODE_CONNECTOR_DVII,
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I);
++ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I,
++ &hpd);
+
+ /* VGA - primary dac */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_CRT1_SUPPORT,
+@@ -1236,11 +1445,14 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_CRT1_SUPPORT,
+ DRM_MODE_CONNECTOR_VGA,
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_VGA);
++ CONNECTOR_OBJECT_ID_VGA,
++ &hpd);
+ }
+
+ if (rdev->family != CHIP_R100 && rdev->family != CHIP_R200) {
+ /* TV - tv dac */
++ ddc_i2c.valid = false;
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_TV1_SUPPORT,
+@@ -1250,14 +1462,16 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_TV1_SUPPORT,
+ DRM_MODE_CONNECTOR_SVIDEO,
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_SVIDEO);
++ CONNECTOR_OBJECT_ID_SVIDEO,
++ &hpd);
+ }
+ break;
+ case CT_IBOOK:
+ DRM_INFO("Connector Table: %d (ibook)\n",
+ rdev->mode_info.connector_table);
+ /* LVDS */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_LCD1_SUPPORT,
+@@ -1265,9 +1479,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_LCD1_SUPPORT);
+ radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT,
+ DRM_MODE_CONNECTOR_LVDS, &ddc_i2c,
+- CONNECTOR_OBJECT_ID_LVDS);
++ CONNECTOR_OBJECT_ID_LVDS,
++ &hpd);
+ /* VGA - TV DAC */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_CRT2_SUPPORT,
+@@ -1275,8 +1491,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_CRT2_SUPPORT);
+ radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT,
+ DRM_MODE_CONNECTOR_VGA, &ddc_i2c,
+- CONNECTOR_OBJECT_ID_VGA);
++ CONNECTOR_OBJECT_ID_VGA,
++ &hpd);
+ /* TV - TV DAC */
++ ddc_i2c.valid = false;
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_TV1_SUPPORT,
+@@ -1285,13 +1504,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT,
+ DRM_MODE_CONNECTOR_SVIDEO,
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_SVIDEO);
++ CONNECTOR_OBJECT_ID_SVIDEO,
++ &hpd);
+ break;
+ case CT_POWERBOOK_EXTERNAL:
+ DRM_INFO("Connector Table: %d (powerbook external tmds)\n",
+ rdev->mode_info.connector_table);
+ /* LVDS */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_LCD1_SUPPORT,
+@@ -1299,9 +1520,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_LCD1_SUPPORT);
+ radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT,
+ DRM_MODE_CONNECTOR_LVDS, &ddc_i2c,
+- CONNECTOR_OBJECT_ID_LVDS);
++ CONNECTOR_OBJECT_ID_LVDS,
++ &hpd);
+ /* DVI-I - primary dac, ext tmds */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
++ hpd.hpd = RADEON_HPD_2; /* ??? */
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_DFP2_SUPPORT,
+@@ -1317,8 +1540,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_DFP2_SUPPORT |
+ ATOM_DEVICE_CRT1_SUPPORT,
+ DRM_MODE_CONNECTOR_DVII, &ddc_i2c,
+- CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I);
++ CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I,
++ &hpd);
+ /* TV - TV DAC */
++ ddc_i2c.valid = false;
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_TV1_SUPPORT,
+@@ -1327,13 +1553,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT,
+ DRM_MODE_CONNECTOR_SVIDEO,
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_SVIDEO);
++ CONNECTOR_OBJECT_ID_SVIDEO,
++ &hpd);
+ break;
+ case CT_POWERBOOK_INTERNAL:
+ DRM_INFO("Connector Table: %d (powerbook internal tmds)\n",
+ rdev->mode_info.connector_table);
+ /* LVDS */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_LCD1_SUPPORT,
+@@ -1341,9 +1569,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_LCD1_SUPPORT);
+ radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT,
+ DRM_MODE_CONNECTOR_LVDS, &ddc_i2c,
+- CONNECTOR_OBJECT_ID_LVDS);
++ CONNECTOR_OBJECT_ID_LVDS,
++ &hpd);
+ /* DVI-I - primary dac, int tmds */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
++ hpd.hpd = RADEON_HPD_1; /* ??? */
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_DFP1_SUPPORT,
+@@ -1358,8 +1588,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_DFP1_SUPPORT |
+ ATOM_DEVICE_CRT1_SUPPORT,
+ DRM_MODE_CONNECTOR_DVII, &ddc_i2c,
+- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I);
++ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I,
++ &hpd);
+ /* TV - TV DAC */
++ ddc_i2c.valid = false;
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_TV1_SUPPORT,
+@@ -1368,13 +1601,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT,
+ DRM_MODE_CONNECTOR_SVIDEO,
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_SVIDEO);
++ CONNECTOR_OBJECT_ID_SVIDEO,
++ &hpd);
+ break;
+ case CT_POWERBOOK_VGA:
+ DRM_INFO("Connector Table: %d (powerbook vga)\n",
+ rdev->mode_info.connector_table);
+ /* LVDS */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_LCD1_SUPPORT,
+@@ -1382,9 +1617,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_LCD1_SUPPORT);
+ radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_LCD1_SUPPORT,
+ DRM_MODE_CONNECTOR_LVDS, &ddc_i2c,
+- CONNECTOR_OBJECT_ID_LVDS);
++ CONNECTOR_OBJECT_ID_LVDS,
++ &hpd);
+ /* VGA - primary dac */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_CRT1_SUPPORT,
+@@ -1392,8 +1629,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_CRT1_SUPPORT);
+ radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT1_SUPPORT,
+ DRM_MODE_CONNECTOR_VGA, &ddc_i2c,
+- CONNECTOR_OBJECT_ID_VGA);
++ CONNECTOR_OBJECT_ID_VGA,
++ &hpd);
+ /* TV - TV DAC */
++ ddc_i2c.valid = false;
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_TV1_SUPPORT,
+@@ -1402,13 +1642,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT,
+ DRM_MODE_CONNECTOR_SVIDEO,
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_SVIDEO);
++ CONNECTOR_OBJECT_ID_SVIDEO,
++ &hpd);
+ break;
+ case CT_MINI_EXTERNAL:
+ DRM_INFO("Connector Table: %d (mini external tmds)\n",
+ rdev->mode_info.connector_table);
+ /* DVI-I - tv dac, ext tmds */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
++ hpd.hpd = RADEON_HPD_2; /* ??? */
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_DFP2_SUPPORT,
+@@ -1424,8 +1666,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_DFP2_SUPPORT |
+ ATOM_DEVICE_CRT2_SUPPORT,
+ DRM_MODE_CONNECTOR_DVII, &ddc_i2c,
+- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I);
++ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I,
++ &hpd);
+ /* TV - TV DAC */
++ ddc_i2c.valid = false;
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_TV1_SUPPORT,
+@@ -1434,13 +1679,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_TV1_SUPPORT,
+ DRM_MODE_CONNECTOR_SVIDEO,
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_SVIDEO);
++ CONNECTOR_OBJECT_ID_SVIDEO,
++ &hpd);
+ break;
+ case CT_MINI_INTERNAL:
+ DRM_INFO("Connector Table: %d (mini internal tmds)\n",
+ rdev->mode_info.connector_table);
+ /* DVI-I - tv dac, int tmds */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
++ hpd.hpd = RADEON_HPD_1; /* ??? */
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_DFP1_SUPPORT,
+@@ -1455,8 +1702,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_DFP1_SUPPORT |
+ ATOM_DEVICE_CRT2_SUPPORT,
+ DRM_MODE_CONNECTOR_DVII, &ddc_i2c,
+- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I);
++ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I,
++ &hpd);
+ /* TV - TV DAC */
++ ddc_i2c.valid = false;
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_TV1_SUPPORT,
+@@ -1465,13 +1715,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_TV1_SUPPORT,
+ DRM_MODE_CONNECTOR_SVIDEO,
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_SVIDEO);
++ CONNECTOR_OBJECT_ID_SVIDEO,
++ &hpd);
+ break;
+ case CT_IMAC_G5_ISIGHT:
+ DRM_INFO("Connector Table: %d (imac g5 isight)\n",
+ rdev->mode_info.connector_table);
+ /* DVI-D - int tmds */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_MONID);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
++ hpd.hpd = RADEON_HPD_1; /* ??? */
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_DFP1_SUPPORT,
+@@ -1479,9 +1731,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_DFP1_SUPPORT);
+ radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_DFP1_SUPPORT,
+ DRM_MODE_CONNECTOR_DVID, &ddc_i2c,
+- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D);
++ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D,
++ &hpd);
+ /* VGA - tv dac */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_CRT2_SUPPORT,
+@@ -1489,8 +1743,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_CRT2_SUPPORT);
+ radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT,
+ DRM_MODE_CONNECTOR_VGA, &ddc_i2c,
+- CONNECTOR_OBJECT_ID_VGA);
++ CONNECTOR_OBJECT_ID_VGA,
++ &hpd);
+ /* TV - TV DAC */
++ ddc_i2c.valid = false;
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_TV1_SUPPORT,
+@@ -1499,13 +1756,15 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT,
+ DRM_MODE_CONNECTOR_SVIDEO,
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_SVIDEO);
++ CONNECTOR_OBJECT_ID_SVIDEO,
++ &hpd);
+ break;
+ case CT_EMAC:
+ DRM_INFO("Connector Table: %d (emac)\n",
+ rdev->mode_info.connector_table);
+ /* VGA - primary dac */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_CRT1_SUPPORT,
+@@ -1513,9 +1772,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_CRT1_SUPPORT);
+ radeon_add_legacy_connector(dev, 0, ATOM_DEVICE_CRT1_SUPPORT,
+ DRM_MODE_CONNECTOR_VGA, &ddc_i2c,
+- CONNECTOR_OBJECT_ID_VGA);
++ CONNECTOR_OBJECT_ID_VGA,
++ &hpd);
+ /* VGA - tv dac */
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_CRT2_SUPPORT,
+@@ -1523,8 +1784,11 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ ATOM_DEVICE_CRT2_SUPPORT);
+ radeon_add_legacy_connector(dev, 1, ATOM_DEVICE_CRT2_SUPPORT,
+ DRM_MODE_CONNECTOR_VGA, &ddc_i2c,
+- CONNECTOR_OBJECT_ID_VGA);
++ CONNECTOR_OBJECT_ID_VGA,
++ &hpd);
+ /* TV - TV DAC */
++ ddc_i2c.valid = false;
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id(dev,
+ ATOM_DEVICE_TV1_SUPPORT,
+@@ -1533,7 +1797,8 @@ bool radeon_get_legacy_connector_info_from_table(struct drm_device *dev)
+ radeon_add_legacy_connector(dev, 2, ATOM_DEVICE_TV1_SUPPORT,
+ DRM_MODE_CONNECTOR_SVIDEO,
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_SVIDEO);
++ CONNECTOR_OBJECT_ID_SVIDEO,
++ &hpd);
+ break;
+ default:
+ DRM_INFO("Connector table: %d (invalid)\n",
+@@ -1550,7 +1815,8 @@ static bool radeon_apply_legacy_quirks(struct drm_device *dev,
+ int bios_index,
+ enum radeon_combios_connector
+ *legacy_connector,
+- struct radeon_i2c_bus_rec *ddc_i2c)
++ struct radeon_i2c_bus_rec *ddc_i2c,
++ struct radeon_hpd *hpd)
+ {
+ struct radeon_device *rdev = dev->dev_private;
+
+@@ -1558,29 +1824,26 @@ static bool radeon_apply_legacy_quirks(struct drm_device *dev,
+ if ((rdev->family == CHIP_RS400 ||
+ rdev->family == CHIP_RS480) &&
+ ddc_i2c->mask_clk_reg == RADEON_GPIO_CRT2_DDC)
+- *ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_MONID);
++ *ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
+ else if ((rdev->family == CHIP_RS400 ||
+ rdev->family == CHIP_RS480) &&
+ ddc_i2c->mask_clk_reg == RADEON_GPIO_MONID) {
+- ddc_i2c->valid = true;
++ *ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIOPAD_MASK);
+ ddc_i2c->mask_clk_mask = (0x20 << 8);
+ ddc_i2c->mask_data_mask = 0x80;
+ ddc_i2c->a_clk_mask = (0x20 << 8);
+ ddc_i2c->a_data_mask = 0x80;
+- ddc_i2c->put_clk_mask = (0x20 << 8);
+- ddc_i2c->put_data_mask = 0x80;
+- ddc_i2c->get_clk_mask = (0x20 << 8);
+- ddc_i2c->get_data_mask = 0x80;
+- ddc_i2c->mask_clk_reg = RADEON_GPIOPAD_MASK;
+- ddc_i2c->mask_data_reg = RADEON_GPIOPAD_MASK;
+- ddc_i2c->a_clk_reg = RADEON_GPIOPAD_A;
+- ddc_i2c->a_data_reg = RADEON_GPIOPAD_A;
+- ddc_i2c->put_clk_reg = RADEON_GPIOPAD_EN;
+- ddc_i2c->put_data_reg = RADEON_GPIOPAD_EN;
+- ddc_i2c->get_clk_reg = RADEON_LCD_GPIO_Y_REG;
+- ddc_i2c->get_data_reg = RADEON_LCD_GPIO_Y_REG;
++ ddc_i2c->en_clk_mask = (0x20 << 8);
++ ddc_i2c->en_data_mask = 0x80;
++ ddc_i2c->y_clk_mask = (0x20 << 8);
++ ddc_i2c->y_data_mask = 0x80;
+ }
+
++ /* R3xx+ chips don't have GPIO_CRT2_DDC gpio pad */
++ if ((rdev->family >= CHIP_R300) &&
++ ddc_i2c->mask_clk_reg == RADEON_GPIO_CRT2_DDC)
++ *ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
++
+ /* Certain IBM chipset RN50s have a BIOS reporting two VGAs,
+ one with VGA DDC and one with CRT2 DDC. - kill the CRT2 DDC one */
+ if (dev->pdev->device == 0x515e &&
+@@ -1624,6 +1887,12 @@ static bool radeon_apply_legacy_tv_quirks(struct drm_device *dev)
+ dev->pdev->subsystem_device == 0x280a)
+ return false;
+
++ /* MSI S270 has non-existent TV port */
++ if (dev->pdev->device == 0x5955 &&
++ dev->pdev->subsystem_vendor == 0x1462 &&
++ dev->pdev->subsystem_device == 0x0131)
++ return false;
++
+ return true;
+ }
+
+@@ -1671,6 +1940,7 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
+ enum radeon_combios_connector connector;
+ int i = 0;
+ struct radeon_i2c_bus_rec ddc_i2c;
++ struct radeon_hpd hpd;
+
+ if (rdev->bios == NULL)
+ return false;
+@@ -1691,26 +1961,40 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
+ switch (ddc_type) {
+ case DDC_MONID:
+ ddc_i2c =
+- combios_setup_i2c_bus(RADEON_GPIO_MONID);
++ combios_setup_i2c_bus(rdev, RADEON_GPIO_MONID);
+ break;
+ case DDC_DVI:
+ ddc_i2c =
+- combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
++ combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
+ break;
+ case DDC_VGA:
+ ddc_i2c =
+- combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
++ combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
+ break;
+ case DDC_CRT2:
+ ddc_i2c =
+- combios_setup_i2c_bus(RADEON_GPIO_CRT2_DDC);
++ combios_setup_i2c_bus(rdev, RADEON_GPIO_CRT2_DDC);
++ break;
++ default:
++ break;
++ }
++
++ switch (connector) {
++ case CONNECTOR_PROPRIETARY_LEGACY:
++ case CONNECTOR_DVI_I_LEGACY:
++ case CONNECTOR_DVI_D_LEGACY:
++ if ((tmp >> 4) & 0x1)
++ hpd.hpd = RADEON_HPD_2;
++ else
++ hpd.hpd = RADEON_HPD_1;
+ break;
+ default:
++ hpd.hpd = RADEON_HPD_NONE;
+ break;
+ }
+
+ if (!radeon_apply_legacy_quirks(dev, i, &connector,
+- &ddc_i2c))
++ &ddc_i2c, &hpd))
+ continue;
+
+ switch (connector) {
+@@ -1727,7 +2011,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
+ legacy_connector_convert
+ [connector],
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D);
++ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_D,
++ &hpd);
+ break;
+ case CONNECTOR_CRT_LEGACY:
+ if (tmp & 0x1) {
+@@ -1753,7 +2038,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
+ legacy_connector_convert
+ [connector],
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_VGA);
++ CONNECTOR_OBJECT_ID_VGA,
++ &hpd);
+ break;
+ case CONNECTOR_DVI_I_LEGACY:
+ devices = 0;
+@@ -1799,7 +2085,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
+ legacy_connector_convert
+ [connector],
+ &ddc_i2c,
+- connector_object_id);
++ connector_object_id,
++ &hpd);
+ break;
+ case CONNECTOR_DVI_D_LEGACY:
+ if ((tmp >> 4) & 0x1) {
+@@ -1817,7 +2104,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
+ legacy_connector_convert
+ [connector],
+ &ddc_i2c,
+- connector_object_id);
++ connector_object_id,
++ &hpd);
+ break;
+ case CONNECTOR_CTV_LEGACY:
+ case CONNECTOR_STV_LEGACY:
+@@ -1832,7 +2120,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
+ legacy_connector_convert
+ [connector],
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_SVIDEO);
++ CONNECTOR_OBJECT_ID_SVIDEO,
++ &hpd);
+ break;
+ default:
+ DRM_ERROR("Unknown connector type: %d\n",
+@@ -1858,14 +2147,16 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
+ 0),
+ ATOM_DEVICE_DFP1_SUPPORT);
+
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_DVI_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_DVI_DDC);
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_connector(dev,
+ 0,
+ ATOM_DEVICE_CRT1_SUPPORT |
+ ATOM_DEVICE_DFP1_SUPPORT,
+ DRM_MODE_CONNECTOR_DVII,
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I);
++ CONNECTOR_OBJECT_ID_SINGLE_LINK_DVI_I,
++ &hpd);
+ } else {
+ uint16_t crt_info =
+ combios_get_table_offset(dev, COMBIOS_CRT_INFO_TABLE);
+@@ -1876,13 +2167,15 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
+ ATOM_DEVICE_CRT1_SUPPORT,
+ 1),
+ ATOM_DEVICE_CRT1_SUPPORT);
+- ddc_i2c = combios_setup_i2c_bus(RADEON_GPIO_VGA_DDC);
++ ddc_i2c = combios_setup_i2c_bus(rdev, RADEON_GPIO_VGA_DDC);
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_connector(dev,
+ 0,
+ ATOM_DEVICE_CRT1_SUPPORT,
+ DRM_MODE_CONNECTOR_VGA,
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_VGA);
++ CONNECTOR_OBJECT_ID_VGA,
++ &hpd);
+ } else {
+ DRM_DEBUG("No connector info found\n");
+ return false;
+@@ -1910,27 +2203,27 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
+ case DDC_MONID:
+ ddc_i2c =
+ combios_setup_i2c_bus
+- (RADEON_GPIO_MONID);
++ (rdev, RADEON_GPIO_MONID);
+ break;
+ case DDC_DVI:
+ ddc_i2c =
+ combios_setup_i2c_bus
+- (RADEON_GPIO_DVI_DDC);
++ (rdev, RADEON_GPIO_DVI_DDC);
+ break;
+ case DDC_VGA:
+ ddc_i2c =
+ combios_setup_i2c_bus
+- (RADEON_GPIO_VGA_DDC);
++ (rdev, RADEON_GPIO_VGA_DDC);
+ break;
+ case DDC_CRT2:
+ ddc_i2c =
+ combios_setup_i2c_bus
+- (RADEON_GPIO_CRT2_DDC);
++ (rdev, RADEON_GPIO_CRT2_DDC);
+ break;
+ case DDC_LCD:
+ ddc_i2c =
+ combios_setup_i2c_bus
+- (RADEON_LCD_GPIO_MASK);
++ (rdev, RADEON_GPIOPAD_MASK);
+ ddc_i2c.mask_clk_mask =
+ RBIOS32(lcd_ddc_info + 3);
+ ddc_i2c.mask_data_mask =
+@@ -1939,19 +2232,19 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
+ RBIOS32(lcd_ddc_info + 3);
+ ddc_i2c.a_data_mask =
+ RBIOS32(lcd_ddc_info + 7);
+- ddc_i2c.put_clk_mask =
++ ddc_i2c.en_clk_mask =
+ RBIOS32(lcd_ddc_info + 3);
+- ddc_i2c.put_data_mask =
++ ddc_i2c.en_data_mask =
+ RBIOS32(lcd_ddc_info + 7);
+- ddc_i2c.get_clk_mask =
++ ddc_i2c.y_clk_mask =
+ RBIOS32(lcd_ddc_info + 3);
+- ddc_i2c.get_data_mask =
++ ddc_i2c.y_data_mask =
+ RBIOS32(lcd_ddc_info + 7);
+ break;
+ case DDC_GPIO:
+ ddc_i2c =
+ combios_setup_i2c_bus
+- (RADEON_MDGPIO_EN_REG);
++ (rdev, RADEON_MDGPIO_MASK);
+ ddc_i2c.mask_clk_mask =
+ RBIOS32(lcd_ddc_info + 3);
+ ddc_i2c.mask_data_mask =
+@@ -1960,13 +2253,13 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
+ RBIOS32(lcd_ddc_info + 3);
+ ddc_i2c.a_data_mask =
+ RBIOS32(lcd_ddc_info + 7);
+- ddc_i2c.put_clk_mask =
++ ddc_i2c.en_clk_mask =
+ RBIOS32(lcd_ddc_info + 3);
+- ddc_i2c.put_data_mask =
++ ddc_i2c.en_data_mask =
+ RBIOS32(lcd_ddc_info + 7);
+- ddc_i2c.get_clk_mask =
++ ddc_i2c.y_clk_mask =
+ RBIOS32(lcd_ddc_info + 3);
+- ddc_i2c.get_data_mask =
++ ddc_i2c.y_data_mask =
+ RBIOS32(lcd_ddc_info + 7);
+ break;
+ default:
+@@ -1977,12 +2270,14 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
+ } else
+ ddc_i2c.valid = false;
+
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_connector(dev,
+ 5,
+ ATOM_DEVICE_LCD1_SUPPORT,
+ DRM_MODE_CONNECTOR_LVDS,
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_LVDS);
++ CONNECTOR_OBJECT_ID_LVDS,
++ &hpd);
+ }
+ }
+
+@@ -1993,6 +2288,7 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
+ if (tv_info) {
+ if (RBIOS8(tv_info + 6) == 'T') {
+ if (radeon_apply_legacy_tv_quirks(dev)) {
++ hpd.hpd = RADEON_HPD_NONE;
+ radeon_add_legacy_encoder(dev,
+ radeon_get_encoder_id
+ (dev,
+@@ -2003,7 +2299,8 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
+ ATOM_DEVICE_TV1_SUPPORT,
+ DRM_MODE_CONNECTOR_SVIDEO,
+ &ddc_i2c,
+- CONNECTOR_OBJECT_ID_SVIDEO);
++ CONNECTOR_OBJECT_ID_SVIDEO,
++ &hpd);
+ }
+ }
+ }
+@@ -2014,6 +2311,193 @@ bool radeon_get_legacy_connector_info_from_bios(struct drm_device *dev)
+ return true;
+ }
+
++void radeon_external_tmds_setup(struct drm_encoder *encoder)
++{
++ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
++ struct radeon_encoder_ext_tmds *tmds = radeon_encoder->enc_priv;
++
++ if (!tmds)
++ return;
++
++ switch (tmds->dvo_chip) {
++ case DVO_SIL164:
++ /* sil 164 */
++ radeon_i2c_do_lock(tmds->i2c_bus, 1);
++ radeon_i2c_sw_put_byte(tmds->i2c_bus,
++ tmds->slave_addr,
++ 0x08, 0x30);
++ radeon_i2c_sw_put_byte(tmds->i2c_bus,
++ tmds->slave_addr,
++ 0x09, 0x00);
++ radeon_i2c_sw_put_byte(tmds->i2c_bus,
++ tmds->slave_addr,
++ 0x0a, 0x90);
++ radeon_i2c_sw_put_byte(tmds->i2c_bus,
++ tmds->slave_addr,
++ 0x0c, 0x89);
++ radeon_i2c_sw_put_byte(tmds->i2c_bus,
++ tmds->slave_addr,
++ 0x08, 0x3b);
++ radeon_i2c_do_lock(tmds->i2c_bus, 0);
++ break;
++ case DVO_SIL1178:
++ /* sil 1178 - untested */
++ /*
++ * 0x0f, 0x44
++ * 0x0f, 0x4c
++ * 0x0e, 0x01
++ * 0x0a, 0x80
++ * 0x09, 0x30
++ * 0x0c, 0xc9
++ * 0x0d, 0x70
++ * 0x08, 0x32
++ * 0x08, 0x33
++ */
++ break;
++ default:
++ break;
++ }
++
++}
++
++bool radeon_combios_external_tmds_setup(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++ struct radeon_device *rdev = dev->dev_private;
++ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
++ uint16_t offset;
++ uint8_t blocks, slave_addr, rev;
++ uint32_t index, id;
++ uint32_t reg, val, and_mask, or_mask;
++ struct radeon_encoder_ext_tmds *tmds = radeon_encoder->enc_priv;
++
++ if (rdev->bios == NULL)
++ return false;
++
++ if (!tmds)
++ return false;
++
++ if (rdev->flags & RADEON_IS_IGP) {
++ offset = combios_get_table_offset(dev, COMBIOS_TMDS_POWER_ON_TABLE);
++ rev = RBIOS8(offset);
++ if (offset) {
++ rev = RBIOS8(offset);
++ if (rev > 1) {
++ blocks = RBIOS8(offset + 3);
++ index = offset + 4;
++ while (blocks > 0) {
++ id = RBIOS16(index);
++ index += 2;
++ switch (id >> 13) {
++ case 0:
++ reg = (id & 0x1fff) * 4;
++ val = RBIOS32(index);
++ index += 4;
++ WREG32(reg, val);
++ break;
++ case 2:
++ reg = (id & 0x1fff) * 4;
++ and_mask = RBIOS32(index);
++ index += 4;
++ or_mask = RBIOS32(index);
++ index += 4;
++ val = RREG32(reg);
++ val = (val & and_mask) | or_mask;
++ WREG32(reg, val);
++ break;
++ case 3:
++ val = RBIOS16(index);
++ index += 2;
++ udelay(val);
++ break;
++ case 4:
++ val = RBIOS16(index);
++ index += 2;
++ udelay(val * 1000);
++ break;
++ case 6:
++ slave_addr = id & 0xff;
++ slave_addr >>= 1; /* 7 bit addressing */
++ index++;
++ reg = RBIOS8(index);
++ index++;
++ val = RBIOS8(index);
++ index++;
++ radeon_i2c_do_lock(tmds->i2c_bus, 1);
++ radeon_i2c_sw_put_byte(tmds->i2c_bus,
++ slave_addr,
++ reg, val);
++ radeon_i2c_do_lock(tmds->i2c_bus, 0);
++ break;
++ default:
++ DRM_ERROR("Unknown id %d\n", id >> 13);
++ break;
++ }
++ blocks--;
++ }
++ return true;
++ }
++ }
++ } else {
++ offset = combios_get_table_offset(dev, COMBIOS_EXT_TMDS_INFO_TABLE);
++ if (offset) {
++ index = offset + 10;
++ id = RBIOS16(index);
++ while (id != 0xffff) {
++ index += 2;
++ switch (id >> 13) {
++ case 0:
++ reg = (id & 0x1fff) * 4;
++ val = RBIOS32(index);
++ WREG32(reg, val);
++ break;
++ case 2:
++ reg = (id & 0x1fff) * 4;
++ and_mask = RBIOS32(index);
++ index += 4;
++ or_mask = RBIOS32(index);
++ index += 4;
++ val = RREG32(reg);
++ val = (val & and_mask) | or_mask;
++ WREG32(reg, val);
++ break;
++ case 4:
++ val = RBIOS16(index);
++ index += 2;
++ udelay(val);
++ break;
++ case 5:
++ reg = id & 0x1fff;
++ and_mask = RBIOS32(index);
++ index += 4;
++ or_mask = RBIOS32(index);
++ index += 4;
++ val = RREG32_PLL(reg);
++ val = (val & and_mask) | or_mask;
++ WREG32_PLL(reg, val);
++ break;
++ case 6:
++ reg = id & 0x1fff;
++ val = RBIOS8(index);
++ index += 1;
++ radeon_i2c_do_lock(tmds->i2c_bus, 1);
++ radeon_i2c_sw_put_byte(tmds->i2c_bus,
++ tmds->slave_addr,
++ reg, val);
++ radeon_i2c_do_lock(tmds->i2c_bus, 0);
++ break;
++ default:
++ DRM_ERROR("Unknown id %d\n", id >> 13);
++ break;
++ }
++ id = RBIOS16(index);
++ }
++ return true;
++ }
++ }
++ return false;
++}
++
+ static void combios_parse_mmio_table(struct drm_device *dev, uint16_t offset)
+ {
+ struct radeon_device *rdev = dev->dev_private;
+diff --git a/drivers/gpu/drm/radeon/radeon_connectors.c b/drivers/gpu/drm/radeon/radeon_connectors.c
+index 29763ce..2381885 100644
+--- a/drivers/gpu/drm/radeon/radeon_connectors.c
++++ b/drivers/gpu/drm/radeon/radeon_connectors.c
+@@ -40,6 +40,28 @@ radeon_atombios_connected_scratch_regs(struct drm_connector *connector,
+ struct drm_encoder *encoder,
+ bool connected);
+
++void radeon_connector_hotplug(struct drm_connector *connector)
++{
++ struct drm_device *dev = connector->dev;
++ struct radeon_device *rdev = dev->dev_private;
++ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
++
++ if (radeon_connector->hpd.hpd != RADEON_HPD_NONE)
++ radeon_hpd_set_polarity(rdev, radeon_connector->hpd.hpd);
++
++ if ((connector->connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
++ (connector->connector_type == DRM_MODE_CONNECTOR_eDP)) {
++ if ((radeon_dp_getsinktype(radeon_connector) == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
++ (radeon_dp_getsinktype(radeon_connector) == CONNECTOR_OBJECT_ID_eDP)) {
++ if (radeon_dp_needs_link_train(radeon_connector)) {
++ if (connector->encoder)
++ dp_link_train(connector->encoder, connector);
++ }
++ }
++ }
++
++}
++
+ static void radeon_property_change_mode(struct drm_encoder *encoder)
+ {
+ struct drm_crtc *crtc = encoder->crtc;
+@@ -188,6 +210,18 @@ static struct drm_display_mode *radeon_fp_native_mode(struct drm_encoder *encode
+ drm_mode_set_name(mode);
+
+ DRM_DEBUG("Adding native panel mode %s\n", mode->name);
++ } else if (native_mode->hdisplay != 0 &&
++ native_mode->vdisplay != 0) {
++ /* mac laptops without an edid */
++ /* Note that this is not necessarily the exact panel mode,
++ * but an approximation based on the cvt formula. For these
++ * systems we should ideally read the mode info out of the
++ * registers or add a mode table, but this works and is much
++ * simpler.
++ */
++ mode = drm_cvt_mode(dev, native_mode->hdisplay, native_mode->vdisplay, 60, true, false, false);
++ mode->type = DRM_MODE_TYPE_PREFERRED | DRM_MODE_TYPE_DRIVER;
++ DRM_DEBUG("Adding cvt approximation of native panel mode %s\n", mode->name);
+ }
+ return mode;
+ }
+@@ -445,10 +479,10 @@ static enum drm_connector_status radeon_lvds_detect(struct drm_connector *connec
+ ret = connector_status_connected;
+ else {
+ if (radeon_connector->ddc_bus) {
+- radeon_i2c_do_lock(radeon_connector, 1);
++ radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
+ radeon_connector->edid = drm_get_edid(&radeon_connector->base,
+ &radeon_connector->ddc_bus->adapter);
+- radeon_i2c_do_lock(radeon_connector, 0);
++ radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
+ if (radeon_connector->edid)
+ ret = connector_status_connected;
+ }
+@@ -546,24 +580,26 @@ static enum drm_connector_status radeon_vga_detect(struct drm_connector *connect
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+ struct drm_encoder *encoder;
+ struct drm_encoder_helper_funcs *encoder_funcs;
+- bool dret;
++ bool dret = false;
+ enum drm_connector_status ret = connector_status_disconnected;
+
+ encoder = radeon_best_single_encoder(connector);
+ if (!encoder)
+ ret = connector_status_disconnected;
+
+- radeon_i2c_do_lock(radeon_connector, 1);
+- dret = radeon_ddc_probe(radeon_connector);
+- radeon_i2c_do_lock(radeon_connector, 0);
++ if (radeon_connector->ddc_bus) {
++ radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
++ dret = radeon_ddc_probe(radeon_connector);
++ radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
++ }
+ if (dret) {
+ if (radeon_connector->edid) {
+ kfree(radeon_connector->edid);
+ radeon_connector->edid = NULL;
+ }
+- radeon_i2c_do_lock(radeon_connector, 1);
++ radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
+ radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
+- radeon_i2c_do_lock(radeon_connector, 0);
++ radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
+
+ if (!radeon_connector->edid) {
+ DRM_ERROR("%s: probed a monitor but no|invalid EDID\n",
+@@ -583,7 +619,7 @@ static enum drm_connector_status radeon_vga_detect(struct drm_connector *connect
+ ret = connector_status_connected;
+ }
+ } else {
+- if (radeon_connector->dac_load_detect) {
++ if (radeon_connector->dac_load_detect && encoder) {
+ encoder_funcs = encoder->helper_private;
+ ret = encoder_funcs->detect(encoder, connector);
+ }
+@@ -706,19 +742,21 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect
+ struct drm_mode_object *obj;
+ int i;
+ enum drm_connector_status ret = connector_status_disconnected;
+- bool dret;
++ bool dret = false;
+
+- radeon_i2c_do_lock(radeon_connector, 1);
+- dret = radeon_ddc_probe(radeon_connector);
+- radeon_i2c_do_lock(radeon_connector, 0);
++ if (radeon_connector->ddc_bus) {
++ radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
++ dret = radeon_ddc_probe(radeon_connector);
++ radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
++ }
+ if (dret) {
+ if (radeon_connector->edid) {
+ kfree(radeon_connector->edid);
+ radeon_connector->edid = NULL;
+ }
+- radeon_i2c_do_lock(radeon_connector, 1);
++ radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
+ radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
+- radeon_i2c_do_lock(radeon_connector, 0);
++ radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
+
+ if (!radeon_connector->edid) {
+ DRM_ERROR("%s: probed a monitor but no|invalid EDID\n",
+@@ -735,6 +773,39 @@ static enum drm_connector_status radeon_dvi_detect(struct drm_connector *connect
+ ret = connector_status_disconnected;
+ } else
+ ret = connector_status_connected;
++
++ /* multiple connectors on the same encoder with the same ddc line
++ * This tends to be HDMI and DVI on the same encoder with the
++ * same ddc line. If the edid says HDMI, consider the HDMI port
++ * connected and the DVI port disconnected. If the edid doesn't
++ * say HDMI, vice versa.
++ */
++ if (radeon_connector->shared_ddc && connector_status_connected) {
++ struct drm_device *dev = connector->dev;
++ struct drm_connector *list_connector;
++ struct radeon_connector *list_radeon_connector;
++ list_for_each_entry(list_connector, &dev->mode_config.connector_list, head) {
++ if (connector == list_connector)
++ continue;
++ list_radeon_connector = to_radeon_connector(list_connector);
++ if (radeon_connector->devices == list_radeon_connector->devices) {
++ if (drm_detect_hdmi_monitor(radeon_connector->edid)) {
++ if (connector->connector_type == DRM_MODE_CONNECTOR_DVID) {
++ kfree(radeon_connector->edid);
++ radeon_connector->edid = NULL;
++ ret = connector_status_disconnected;
++ }
++ } else {
++ if ((connector->connector_type == DRM_MODE_CONNECTOR_HDMIA) ||
++ (connector->connector_type == DRM_MODE_CONNECTOR_HDMIB)) {
++ kfree(radeon_connector->edid);
++ radeon_connector->edid = NULL;
++ ret = connector_status_disconnected;
++ }
++ }
++ }
++ }
++ }
+ }
+ }
+
+@@ -833,10 +904,18 @@ static void radeon_dvi_force(struct drm_connector *connector)
+ static int radeon_dvi_mode_valid(struct drm_connector *connector,
+ struct drm_display_mode *mode)
+ {
++ struct drm_device *dev = connector->dev;
++ struct radeon_device *rdev = dev->dev_private;
+ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
+
+ /* XXX check mode bandwidth */
+
++ /* clocks over 135 MHz have heat issues with DVI on RV100 */
++ if (radeon_connector->use_digital &&
++ (rdev->family == CHIP_RV100) &&
++ (mode->clock > 135000))
++ return MODE_CLOCK_HIGH;
++
+ if (radeon_connector->use_digital && (mode->clock > 165000)) {
+ if ((radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_I) ||
+ (radeon_connector->connector_object_id == CONNECTOR_OBJECT_ID_DUAL_LINK_DVI_D) ||
+@@ -863,6 +942,93 @@ struct drm_connector_funcs radeon_dvi_connector_funcs = {
+ .force = radeon_dvi_force,
+ };
+
++static void radeon_dp_connector_destroy(struct drm_connector *connector)
++{
++ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
++ struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
++
++ if (radeon_connector->ddc_bus)
++ radeon_i2c_destroy(radeon_connector->ddc_bus);
++ if (radeon_connector->edid)
++ kfree(radeon_connector->edid);
++ if (radeon_dig_connector->dp_i2c_bus)
++ radeon_i2c_destroy(radeon_dig_connector->dp_i2c_bus);
++ kfree(radeon_connector->con_priv);
++ drm_sysfs_connector_remove(connector);
++ drm_connector_cleanup(connector);
++ kfree(connector);
++}
++
++static int radeon_dp_get_modes(struct drm_connector *connector)
++{
++ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
++ int ret;
++
++ ret = radeon_ddc_get_modes(radeon_connector);
++ return ret;
++}
++
++static enum drm_connector_status radeon_dp_detect(struct drm_connector *connector)
++{
++ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
++ enum drm_connector_status ret = connector_status_disconnected;
++ struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
++ u8 sink_type;
++
++ if (radeon_connector->edid) {
++ kfree(radeon_connector->edid);
++ radeon_connector->edid = NULL;
++ }
++
++ sink_type = radeon_dp_getsinktype(radeon_connector);
++ if ((sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
++ (sink_type == CONNECTOR_OBJECT_ID_eDP)) {
++ if (radeon_dp_getdpcd(radeon_connector)) {
++ radeon_dig_connector->dp_sink_type = sink_type;
++ ret = connector_status_connected;
++ }
++ } else {
++ radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
++ if (radeon_ddc_probe(radeon_connector)) {
++ radeon_dig_connector->dp_sink_type = sink_type;
++ ret = connector_status_connected;
++ }
++ radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
++ }
++
++ return ret;
++}
++
++static int radeon_dp_mode_valid(struct drm_connector *connector,
++ struct drm_display_mode *mode)
++{
++ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
++ struct radeon_connector_atom_dig *radeon_dig_connector = radeon_connector->con_priv;
++
++ /* XXX check mode bandwidth */
++
++ if ((radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
++ (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP))
++ return radeon_dp_mode_valid_helper(radeon_connector, mode);
++ else
++ return MODE_OK;
++}
++
++struct drm_connector_helper_funcs radeon_dp_connector_helper_funcs = {
++ .get_modes = radeon_dp_get_modes,
++ .mode_valid = radeon_dp_mode_valid,
++ .best_encoder = radeon_dvi_encoder,
++};
++
++struct drm_connector_funcs radeon_dp_connector_funcs = {
++ .dpms = drm_helper_connector_dpms,
++ .detect = radeon_dp_detect,
++ .fill_modes = drm_helper_probe_single_connector_modes,
++ .set_property = radeon_connector_set_property,
++ .destroy = radeon_dp_connector_destroy,
++ .force = radeon_dvi_force,
++};
++
+ void
+ radeon_add_atom_connector(struct drm_device *dev,
+ uint32_t connector_id,
+@@ -871,7 +1037,8 @@ radeon_add_atom_connector(struct drm_device *dev,
+ struct radeon_i2c_bus_rec *i2c_bus,
+ bool linkb,
+ uint32_t igp_lane_info,
+- uint16_t connector_object_id)
++ uint16_t connector_object_id,
++ struct radeon_hpd *hpd)
+ {
+ struct radeon_device *rdev = dev->dev_private;
+ struct drm_connector *connector;
+@@ -911,6 +1078,7 @@ radeon_add_atom_connector(struct drm_device *dev,
+ radeon_connector->devices = supported_device;
+ radeon_connector->shared_ddc = shared_ddc;
+ radeon_connector->connector_object_id = connector_object_id;
++ radeon_connector->hpd = *hpd;
+ switch (connector_type) {
+ case DRM_MODE_CONNECTOR_VGA:
+ drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
+@@ -963,10 +1131,12 @@ radeon_add_atom_connector(struct drm_device *dev,
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.coherent_mode_property,
+ 1);
+- radeon_connector->dac_load_detect = true;
+- drm_connector_attach_property(&radeon_connector->base,
+- rdev->mode_info.load_detect_property,
+- 1);
++ if (connector_type == DRM_MODE_CONNECTOR_DVII) {
++ radeon_connector->dac_load_detect = true;
++ drm_connector_attach_property(&radeon_connector->base,
++ rdev->mode_info.load_detect_property,
++ 1);
++ }
+ break;
+ case DRM_MODE_CONNECTOR_HDMIA:
+ case DRM_MODE_CONNECTOR_HDMIB:
+@@ -991,22 +1161,36 @@ radeon_add_atom_connector(struct drm_device *dev,
+ subpixel_order = SubPixelHorizontalRGB;
+ break;
+ case DRM_MODE_CONNECTOR_DisplayPort:
++ case DRM_MODE_CONNECTOR_eDP:
+ radeon_dig_connector = kzalloc(sizeof(struct radeon_connector_atom_dig), GFP_KERNEL);
+ if (!radeon_dig_connector)
+ goto failed;
+ radeon_dig_connector->linkb = linkb;
+ radeon_dig_connector->igp_lane_info = igp_lane_info;
+ radeon_connector->con_priv = radeon_dig_connector;
+- drm_connector_init(dev, &radeon_connector->base, &radeon_dvi_connector_funcs, connector_type);
+- ret = drm_connector_helper_add(&radeon_connector->base, &radeon_dvi_connector_helper_funcs);
++ drm_connector_init(dev, &radeon_connector->base, &radeon_dp_connector_funcs, connector_type);
++ ret = drm_connector_helper_add(&radeon_connector->base, &radeon_dp_connector_helper_funcs);
+ if (ret)
+ goto failed;
+ if (i2c_bus->valid) {
+- radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DP");
++ /* add DP i2c bus */
++ if (connector_type == DRM_MODE_CONNECTOR_eDP)
++ radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "eDP-auxch");
++ else
++ radeon_dig_connector->dp_i2c_bus = radeon_i2c_create_dp(dev, i2c_bus, "DP-auxch");
++ if (!radeon_dig_connector->dp_i2c_bus)
++ goto failed;
++ if (connector_type == DRM_MODE_CONNECTOR_eDP)
++ radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "eDP");
++ else
++ radeon_connector->ddc_bus = radeon_i2c_create(dev, i2c_bus, "DP");
+ if (!radeon_connector->ddc_bus)
+ goto failed;
+ }
+ subpixel_order = SubPixelHorizontalRGB;
++ drm_connector_attach_property(&radeon_connector->base,
++ rdev->mode_info.coherent_mode_property,
++ 1);
+ break;
+ case DRM_MODE_CONNECTOR_SVIDEO:
+ case DRM_MODE_CONNECTOR_Composite:
+@@ -1020,6 +1204,9 @@ radeon_add_atom_connector(struct drm_device *dev,
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.load_detect_property,
+ 1);
++ drm_connector_attach_property(&radeon_connector->base,
++ rdev->mode_info.tv_std_property,
++ radeon_atombios_get_tv_info(rdev));
+ }
+ break;
+ case DRM_MODE_CONNECTOR_LVDS:
+@@ -1038,7 +1225,6 @@ radeon_add_atom_connector(struct drm_device *dev,
+ if (!radeon_connector->ddc_bus)
+ goto failed;
+ }
+- drm_mode_create_scaling_mode_property(dev);
+ drm_connector_attach_property(&radeon_connector->base,
+ dev->mode_config.scaling_mode_property,
+ DRM_MODE_SCALE_FULLSCREEN);
+@@ -1063,7 +1249,8 @@ radeon_add_legacy_connector(struct drm_device *dev,
+ uint32_t supported_device,
+ int connector_type,
+ struct radeon_i2c_bus_rec *i2c_bus,
+- uint16_t connector_object_id)
++ uint16_t connector_object_id,
++ struct radeon_hpd *hpd)
+ {
+ struct radeon_device *rdev = dev->dev_private;
+ struct drm_connector *connector;
+@@ -1093,6 +1280,7 @@ radeon_add_legacy_connector(struct drm_device *dev,
+ radeon_connector->connector_id = connector_id;
+ radeon_connector->devices = supported_device;
+ radeon_connector->connector_object_id = connector_object_id;
++ radeon_connector->hpd = *hpd;
+ switch (connector_type) {
+ case DRM_MODE_CONNECTOR_VGA:
+ drm_connector_init(dev, &radeon_connector->base, &radeon_vga_connector_funcs, connector_type);
+@@ -1159,7 +1347,10 @@ radeon_add_legacy_connector(struct drm_device *dev,
+ radeon_connector->dac_load_detect = false;
+ drm_connector_attach_property(&radeon_connector->base,
+ rdev->mode_info.load_detect_property,
+- 1);
++ radeon_connector->dac_load_detect);
++ drm_connector_attach_property(&radeon_connector->base,
++ rdev->mode_info.tv_std_property,
++ radeon_combios_get_tv_info(rdev));
+ }
+ break;
+ case DRM_MODE_CONNECTOR_LVDS:
+diff --git a/drivers/gpu/drm/radeon/radeon_cp.c b/drivers/gpu/drm/radeon/radeon_cp.c
+index 4f7afc7..06123ba 100644
+--- a/drivers/gpu/drm/radeon/radeon_cp.c
++++ b/drivers/gpu/drm/radeon/radeon_cp.c
+@@ -1941,8 +1941,8 @@ struct drm_buf *radeon_freelist_get(struct drm_device * dev)
+ for (t = 0; t < dev_priv->usec_timeout; t++) {
+ u32 done_age = GET_SCRATCH(dev_priv, 1);
+ DRM_DEBUG("done_age = %d\n", done_age);
+- for (i = start; i < dma->buf_count; i++) {
+- buf = dma->buflist[i];
++ for (i = 0; i < dma->buf_count; i++) {
++ buf = dma->buflist[start];
+ buf_priv = buf->dev_private;
+ if (buf->file_priv == NULL || (buf->pending &&
+ buf_priv->age <=
+@@ -1951,7 +1951,8 @@ struct drm_buf *radeon_freelist_get(struct drm_device * dev)
+ buf->pending = 0;
+ return buf;
+ }
+- start = 0;
++ if (++start >= dma->buf_count)
++ start = 0;
+ }
+
+ if (t) {
+@@ -1960,47 +1961,9 @@ struct drm_buf *radeon_freelist_get(struct drm_device * dev)
+ }
+ }
+
+- DRM_DEBUG("returning NULL!\n");
+ return NULL;
+ }
+
+-#if 0
+-struct drm_buf *radeon_freelist_get(struct drm_device * dev)
+-{
+- struct drm_device_dma *dma = dev->dma;
+- drm_radeon_private_t *dev_priv = dev->dev_private;
+- drm_radeon_buf_priv_t *buf_priv;
+- struct drm_buf *buf;
+- int i, t;
+- int start;
+- u32 done_age;
+-
+- done_age = radeon_read_ring_rptr(dev_priv, RADEON_SCRATCHOFF(1));
+- if (++dev_priv->last_buf >= dma->buf_count)
+- dev_priv->last_buf = 0;
+-
+- start = dev_priv->last_buf;
+- dev_priv->stats.freelist_loops++;
+-
+- for (t = 0; t < 2; t++) {
+- for (i = start; i < dma->buf_count; i++) {
+- buf = dma->buflist[i];
+- buf_priv = buf->dev_private;
+- if (buf->file_priv == 0 || (buf->pending &&
+- buf_priv->age <=
+- done_age)) {
+- dev_priv->stats.requested_bufs++;
+- buf->pending = 0;
+- return buf;
+- }
+- }
+- start = 0;
+- }
+-
+- return NULL;
+-}
+-#endif
+-
+ void radeon_freelist_reset(struct drm_device * dev)
+ {
+ struct drm_device_dma *dma = dev->dma;
+@@ -2182,6 +2145,7 @@ int radeon_master_create(struct drm_device *dev, struct drm_master *master)
+ &master_priv->sarea);
+ if (ret) {
+ DRM_ERROR("SAREA setup failed\n");
++ kfree(master_priv);
+ return ret;
+ }
+ master_priv->sarea_priv = master_priv->sarea->handle + sizeof(struct drm_sarea);
+diff --git a/drivers/gpu/drm/radeon/radeon_cs.c b/drivers/gpu/drm/radeon/radeon_cs.c
+index 5ab2cf9..e9d0850 100644
+--- a/drivers/gpu/drm/radeon/radeon_cs.c
++++ b/drivers/gpu/drm/radeon/radeon_cs.c
+@@ -76,17 +76,17 @@ int radeon_cs_parser_relocs(struct radeon_cs_parser *p)
+ }
+ p->relocs_ptr[i] = &p->relocs[i];
+ p->relocs[i].robj = p->relocs[i].gobj->driver_private;
+- p->relocs[i].lobj.robj = p->relocs[i].robj;
++ p->relocs[i].lobj.bo = p->relocs[i].robj;
+ p->relocs[i].lobj.rdomain = r->read_domains;
+ p->relocs[i].lobj.wdomain = r->write_domain;
+ p->relocs[i].handle = r->handle;
+ p->relocs[i].flags = r->flags;
+ INIT_LIST_HEAD(&p->relocs[i].lobj.list);
+- radeon_object_list_add_object(&p->relocs[i].lobj,
+- &p->validated);
++ radeon_bo_list_add_object(&p->relocs[i].lobj,
++ &p->validated);
+ }
+ }
+- return radeon_object_list_validate(&p->validated, p->ib->fence);
++ return radeon_bo_list_validate(&p->validated);
+ }
+
+ int radeon_cs_parser_init(struct radeon_cs_parser *p, void *data)
+@@ -189,11 +189,10 @@ static void radeon_cs_parser_fini(struct radeon_cs_parser *parser, int error)
+ {
+ unsigned i;
+
+- if (error) {
+- radeon_object_list_unvalidate(&parser->validated);
+- } else {
+- radeon_object_list_clean(&parser->validated);
++ if (!error && parser->ib) {
++ radeon_bo_list_fence(&parser->validated, parser->ib->fence);
+ }
++ radeon_bo_list_unreserve(&parser->validated);
+ for (i = 0; i < parser->nrelocs; i++) {
+ if (parser->relocs[i].gobj) {
+ mutex_lock(&parser->rdev->ddev->struct_mutex);
+@@ -230,6 +229,7 @@ int radeon_cs_ioctl(struct drm_device *dev, void *data, struct drm_file *filp)
+ memset(&parser, 0, sizeof(struct radeon_cs_parser));
+ parser.filp = filp;
+ parser.rdev = rdev;
++ parser.dev = rdev->dev;
+ r = radeon_cs_parser_init(&parser, data);
+ if (r) {
+ DRM_ERROR("Failed to initialize parser !\n");
+diff --git a/drivers/gpu/drm/radeon/radeon_device.c b/drivers/gpu/drm/radeon/radeon_device.c
+index 41bb76f..768b150 100644
+--- a/drivers/gpu/drm/radeon/radeon_device.c
++++ b/drivers/gpu/drm/radeon/radeon_device.c
+@@ -44,10 +44,11 @@ void radeon_surface_init(struct radeon_device *rdev)
+ if (rdev->family < CHIP_R600) {
+ int i;
+
+- for (i = 0; i < 8; i++) {
+- WREG32(RADEON_SURFACE0_INFO +
+- i * (RADEON_SURFACE1_INFO - RADEON_SURFACE0_INFO),
+- 0);
++ for (i = 0; i < RADEON_GEM_MAX_SURFACES; i++) {
++ if (rdev->surface_regs[i].bo)
++ radeon_bo_get_surface_reg(rdev->surface_regs[i].bo);
++ else
++ radeon_clear_surface_reg(rdev, i);
+ }
+ /* enable surfaces */
+ WREG32(RADEON_SURFACE_CNTL, 0);
+@@ -208,6 +209,24 @@ bool radeon_card_posted(struct radeon_device *rdev)
+
+ }
+
++bool radeon_boot_test_post_card(struct radeon_device *rdev)
++{
++ if (radeon_card_posted(rdev))
++ return true;
++
++ if (rdev->bios) {
++ DRM_INFO("GPU not posted. posting now...\n");
++ if (rdev->is_atom_bios)
++ atom_asic_init(rdev->mode_info.atom_context);
++ else
++ radeon_combios_asic_init(rdev->ddev);
++ return true;
++ } else {
++ dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n");
++ return false;
++ }
++}
++
+ int radeon_dummy_page_init(struct radeon_device *rdev)
+ {
+ rdev->dummy_page.page = alloc_page(GFP_DMA32 | GFP_KERNEL | __GFP_ZERO);
+@@ -372,6 +391,12 @@ int radeon_asic_init(struct radeon_device *rdev)
+ /* FIXME: not supported yet */
+ return -EINVAL;
+ }
++
++ if (rdev->flags & RADEON_IS_IGP) {
++ rdev->asic->get_memory_clock = NULL;
++ rdev->asic->set_memory_clock = NULL;
++ }
++
+ return 0;
+ }
+
+@@ -462,13 +487,18 @@ int radeon_atombios_init(struct radeon_device *rdev)
+ atom_card_info->pll_write = cail_pll_write;
+
+ rdev->mode_info.atom_context = atom_parse(atom_card_info, rdev->bios);
++ mutex_init(&rdev->mode_info.atom_context->mutex);
+ radeon_atom_initialize_bios_scratch_regs(rdev->ddev);
++ atom_allocate_fb_scratch(rdev->mode_info.atom_context);
+ return 0;
+ }
+
+ void radeon_atombios_fini(struct radeon_device *rdev)
+ {
+- kfree(rdev->mode_info.atom_context);
++ if (rdev->mode_info.atom_context) {
++ kfree(rdev->mode_info.atom_context->scratch);
++ kfree(rdev->mode_info.atom_context);
++ }
+ kfree(rdev->mode_info.atom_card_info);
+ }
+
+@@ -514,11 +544,75 @@ void radeon_agp_disable(struct radeon_device *rdev)
+ rdev->asic->gart_tlb_flush = &r100_pci_gart_tlb_flush;
+ rdev->asic->gart_set_page = &r100_pci_gart_set_page;
+ }
++ rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024;
++}
++
++void radeon_check_arguments(struct radeon_device *rdev)
++{
++ /* vramlimit must be a power of two */
++ switch (radeon_vram_limit) {
++ case 0:
++ case 4:
++ case 8:
++ case 16:
++ case 32:
++ case 64:
++ case 128:
++ case 256:
++ case 512:
++ case 1024:
++ case 2048:
++ case 4096:
++ break;
++ default:
++ dev_warn(rdev->dev, "vram limit (%d) must be a power of 2\n",
++ radeon_vram_limit);
++ radeon_vram_limit = 0;
++ break;
++ }
++ radeon_vram_limit = radeon_vram_limit << 20;
++ /* gtt size must be power of two and greater or equal to 32M */
++ switch (radeon_gart_size) {
++ case 4:
++ case 8:
++ case 16:
++ dev_warn(rdev->dev, "gart size (%d) too small forcing to 512M\n",
++ radeon_gart_size);
++ radeon_gart_size = 512;
++ break;
++ case 32:
++ case 64:
++ case 128:
++ case 256:
++ case 512:
++ case 1024:
++ case 2048:
++ case 4096:
++ break;
++ default:
++ dev_warn(rdev->dev, "gart size (%d) must be a power of 2\n",
++ radeon_gart_size);
++ radeon_gart_size = 512;
++ break;
++ }
++ rdev->mc.gtt_size = radeon_gart_size * 1024 * 1024;
++ /* AGP mode can only be -1, 1, 2, 4, 8 */
++ switch (radeon_agpmode) {
++ case -1:
++ case 0:
++ case 1:
++ case 2:
++ case 4:
++ case 8:
++ break;
++ default:
++ dev_warn(rdev->dev, "invalid AGP mode %d (valid mode: "
++ "-1, 0, 1, 2, 4, 8)\n", radeon_agpmode);
++ radeon_agpmode = 0;
++ break;
++ }
+ }
+
+-/*
+- * Radeon device.
+- */
+ int radeon_device_init(struct radeon_device *rdev,
+ struct drm_device *ddev,
+ struct pci_dev *pdev,
+@@ -544,16 +638,24 @@ int radeon_device_init(struct radeon_device *rdev,
+ mutex_init(&rdev->cs_mutex);
+ mutex_init(&rdev->ib_pool.mutex);
+ mutex_init(&rdev->cp.mutex);
++ if (rdev->family >= CHIP_R600)
++ spin_lock_init(&rdev->ih.lock);
++ mutex_init(&rdev->gem.mutex);
+ rwlock_init(&rdev->fence_drv.lock);
+ INIT_LIST_HEAD(&rdev->gem.objects);
+
++ /* setup workqueue */
++ rdev->wq = create_workqueue("radeon");
++ if (rdev->wq == NULL)
++ return -ENOMEM;
++
+ /* Set asic functions */
+ r = radeon_asic_init(rdev);
+- if (r) {
++ if (r)
+ return r;
+- }
++ radeon_check_arguments(rdev);
+
+- if (radeon_agpmode == -1) {
++ if (rdev->flags & RADEON_IS_AGP && radeon_agpmode == -1) {
+ radeon_agp_disable(rdev);
+ }
+
+@@ -620,6 +722,7 @@ void radeon_device_fini(struct radeon_device *rdev)
+ DRM_INFO("radeon: finishing device.\n");
+ rdev->shutdown = true;
+ radeon_fini(rdev);
++ destroy_workqueue(rdev->wq);
+ vga_client_register(rdev->pdev, NULL, NULL, NULL);
+ iounmap(rdev->rmmio);
+ rdev->rmmio = NULL;
+@@ -631,38 +734,46 @@ void radeon_device_fini(struct radeon_device *rdev)
+ */
+ int radeon_suspend_kms(struct drm_device *dev, pm_message_t state)
+ {
+- struct radeon_device *rdev = dev->dev_private;
++ struct radeon_device *rdev;
+ struct drm_crtc *crtc;
++ int r;
+
+- if (dev == NULL || rdev == NULL) {
++ if (dev == NULL || dev->dev_private == NULL) {
+ return -ENODEV;
+ }
+ if (state.event == PM_EVENT_PRETHAW) {
+ return 0;
+ }
++ rdev = dev->dev_private;
++
+ /* unpin the front buffers */
+ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
+ struct radeon_framebuffer *rfb = to_radeon_framebuffer(crtc->fb);
+- struct radeon_object *robj;
++ struct radeon_bo *robj;
+
+ if (rfb == NULL || rfb->obj == NULL) {
+ continue;
+ }
+ robj = rfb->obj->driver_private;
+- if (robj != rdev->fbdev_robj) {
+- radeon_object_unpin(robj);
++ if (robj != rdev->fbdev_rbo) {
++ r = radeon_bo_reserve(robj, false);
++ if (unlikely(r == 0)) {
++ radeon_bo_unpin(robj);
++ radeon_bo_unreserve(robj);
++ }
+ }
+ }
+ /* evict vram memory */
+- radeon_object_evict_vram(rdev);
++ radeon_bo_evict_vram(rdev);
+ /* wait for gpu to finish processing current batch */
+ radeon_fence_wait_last(rdev);
+
+ radeon_save_bios_scratch_regs(rdev);
+
+ radeon_suspend(rdev);
++ radeon_hpd_fini(rdev);
+ /* evict remaining vram memory */
+- radeon_object_evict_vram(rdev);
++ radeon_bo_evict_vram(rdev);
+
+ pci_save_state(dev->pdev);
+ if (state.event == PM_EVENT_SUSPEND) {
+@@ -695,6 +806,8 @@ int radeon_resume_kms(struct drm_device *dev)
+ fb_set_suspend(rdev->fbdev_info, 0);
+ release_console_sem();
+
++ /* reset hpd state */
++ radeon_hpd_init(rdev);
+ /* blat the mode back in */
+ drm_helper_resume_force_mode(dev);
+ return 0;
+diff --git a/drivers/gpu/drm/radeon/radeon_display.c b/drivers/gpu/drm/radeon/radeon_display.c
+index c85df4a..ac31a66 100644
+--- a/drivers/gpu/drm/radeon/radeon_display.c
++++ b/drivers/gpu/drm/radeon/radeon_display.c
+@@ -234,7 +234,7 @@ static const char *encoder_names[34] = {
+ "INTERNAL_UNIPHY2",
+ };
+
+-static const char *connector_names[13] = {
++static const char *connector_names[15] = {
+ "Unknown",
+ "VGA",
+ "DVI-I",
+@@ -248,6 +248,18 @@ static const char *connector_names[13] = {
+ "DisplayPort",
+ "HDMI-A",
+ "HDMI-B",
++ "TV",
++ "eDP",
++};
++
++static const char *hpd_names[7] = {
++ "NONE",
++ "HPD1",
++ "HPD2",
++ "HPD3",
++ "HPD4",
++ "HPD5",
++ "HPD6",
+ };
+
+ static void radeon_print_display_setup(struct drm_device *dev)
+@@ -264,16 +276,27 @@ static void radeon_print_display_setup(struct drm_device *dev)
+ radeon_connector = to_radeon_connector(connector);
+ DRM_INFO("Connector %d:\n", i);
+ DRM_INFO(" %s\n", connector_names[connector->connector_type]);
+- if (radeon_connector->ddc_bus)
++ if (radeon_connector->hpd.hpd != RADEON_HPD_NONE)
++ DRM_INFO(" %s\n", hpd_names[radeon_connector->hpd.hpd]);
++ if (radeon_connector->ddc_bus) {
+ DRM_INFO(" DDC: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
+ radeon_connector->ddc_bus->rec.mask_clk_reg,
+ radeon_connector->ddc_bus->rec.mask_data_reg,
+ radeon_connector->ddc_bus->rec.a_clk_reg,
+ radeon_connector->ddc_bus->rec.a_data_reg,
+- radeon_connector->ddc_bus->rec.put_clk_reg,
+- radeon_connector->ddc_bus->rec.put_data_reg,
+- radeon_connector->ddc_bus->rec.get_clk_reg,
+- radeon_connector->ddc_bus->rec.get_data_reg);
++ radeon_connector->ddc_bus->rec.en_clk_reg,
++ radeon_connector->ddc_bus->rec.en_data_reg,
++ radeon_connector->ddc_bus->rec.y_clk_reg,
++ radeon_connector->ddc_bus->rec.y_data_reg);
++ } else {
++ if (connector->connector_type == DRM_MODE_CONNECTOR_VGA ||
++ connector->connector_type == DRM_MODE_CONNECTOR_DVII ||
++ connector->connector_type == DRM_MODE_CONNECTOR_DVID ||
++ connector->connector_type == DRM_MODE_CONNECTOR_DVIA ||
++ connector->connector_type == DRM_MODE_CONNECTOR_HDMIA ||
++ connector->connector_type == DRM_MODE_CONNECTOR_HDMIB)
++ DRM_INFO(" DDC: no ddc bus - possible BIOS bug - please report to xorg-driver-ati@lists.x.org\n");
++ }
+ DRM_INFO(" Encoders:\n");
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ radeon_encoder = to_radeon_encoder(encoder);
+@@ -317,13 +340,17 @@ static bool radeon_setup_enc_conn(struct drm_device *dev)
+ ret = radeon_get_atom_connector_info_from_object_table(dev);
+ else
+ ret = radeon_get_atom_connector_info_from_supported_devices_table(dev);
+- } else
++ } else {
+ ret = radeon_get_legacy_connector_info_from_bios(dev);
++ if (ret == false)
++ ret = radeon_get_legacy_connector_info_from_table(dev);
++ }
+ } else {
+ if (!ASIC_IS_AVIVO(rdev))
+ ret = radeon_get_legacy_connector_info_from_table(dev);
+ }
+ if (ret) {
++ radeon_setup_encoder_clones(dev);
+ radeon_print_display_setup(dev);
+ list_for_each_entry(drm_connector, &dev->mode_config.connector_list, head)
+ radeon_ddc_dump(drm_connector);
+@@ -334,16 +361,27 @@ static bool radeon_setup_enc_conn(struct drm_device *dev)
+
+ int radeon_ddc_get_modes(struct radeon_connector *radeon_connector)
+ {
++ struct drm_device *dev = radeon_connector->base.dev;
++ struct radeon_device *rdev = dev->dev_private;
+ int ret = 0;
+
++ if ((radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_DisplayPort) ||
++ (radeon_connector->base.connector_type == DRM_MODE_CONNECTOR_eDP)) {
++ struct radeon_connector_atom_dig *dig = radeon_connector->con_priv;
++ if ((dig->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT ||
++ dig->dp_sink_type == CONNECTOR_OBJECT_ID_eDP) && dig->dp_i2c_bus)
++ radeon_connector->edid = drm_get_edid(&radeon_connector->base, &dig->dp_i2c_bus->adapter);
++ }
+ if (!radeon_connector->ddc_bus)
+ return -1;
+ if (!radeon_connector->edid) {
+- radeon_i2c_do_lock(radeon_connector, 1);
++ radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
+ radeon_connector->edid = drm_get_edid(&radeon_connector->base, &radeon_connector->ddc_bus->adapter);
+- radeon_i2c_do_lock(radeon_connector, 0);
++ radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
+ }
+-
++ /* some servers provide a hardcoded edid in rom for KVMs */
++ if (!radeon_connector->edid)
++ radeon_connector->edid = radeon_combios_get_hardcoded_edid(rdev);
+ if (radeon_connector->edid) {
+ drm_mode_connector_update_edid_property(&radeon_connector->base, radeon_connector->edid);
+ ret = drm_add_edid_modes(&radeon_connector->base, radeon_connector->edid);
+@@ -361,9 +399,9 @@ static int radeon_ddc_dump(struct drm_connector *connector)
+
+ if (!radeon_connector->ddc_bus)
+ return -1;
+- radeon_i2c_do_lock(radeon_connector, 1);
++ radeon_i2c_do_lock(radeon_connector->ddc_bus, 1);
+ edid = drm_get_edid(connector, &radeon_connector->ddc_bus->adapter);
+- radeon_i2c_do_lock(radeon_connector, 0);
++ radeon_i2c_do_lock(radeon_connector->ddc_bus, 0);
+ if (edid) {
+ kfree(edid);
+ }
+@@ -386,11 +424,12 @@ void radeon_compute_pll(struct radeon_pll *pll,
+ uint32_t *fb_div_p,
+ uint32_t *frac_fb_div_p,
+ uint32_t *ref_div_p,
+- uint32_t *post_div_p,
+- int flags)
++ uint32_t *post_div_p)
+ {
+ uint32_t min_ref_div = pll->min_ref_div;
+ uint32_t max_ref_div = pll->max_ref_div;
++ uint32_t min_post_div = pll->min_post_div;
++ uint32_t max_post_div = pll->max_post_div;
+ uint32_t min_fractional_feed_div = 0;
+ uint32_t max_fractional_feed_div = 0;
+ uint32_t best_vco = pll->best_vco;
+@@ -406,7 +445,7 @@ void radeon_compute_pll(struct radeon_pll *pll,
+ DRM_DEBUG("PLL freq %llu %u %u\n", freq, pll->min_ref_div, pll->max_ref_div);
+ freq = freq * 1000;
+
+- if (flags & RADEON_PLL_USE_REF_DIV)
++ if (pll->flags & RADEON_PLL_USE_REF_DIV)
+ min_ref_div = max_ref_div = pll->reference_div;
+ else {
+ while (min_ref_div < max_ref_div-1) {
+@@ -421,19 +460,22 @@ void radeon_compute_pll(struct radeon_pll *pll,
+ }
+ }
+
+- if (flags & RADEON_PLL_USE_FRAC_FB_DIV) {
++ if (pll->flags & RADEON_PLL_USE_POST_DIV)
++ min_post_div = max_post_div = pll->post_div;
++
++ if (pll->flags & RADEON_PLL_USE_FRAC_FB_DIV) {
+ min_fractional_feed_div = pll->min_frac_feedback_div;
+ max_fractional_feed_div = pll->max_frac_feedback_div;
+ }
+
+- for (post_div = pll->min_post_div; post_div <= pll->max_post_div; ++post_div) {
++ for (post_div = min_post_div; post_div <= max_post_div; ++post_div) {
+ uint32_t ref_div;
+
+- if ((flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1))
++ if ((pll->flags & RADEON_PLL_NO_ODD_POST_DIV) && (post_div & 1))
+ continue;
+
+ /* legacy radeons only have a few post_divs */
+- if (flags & RADEON_PLL_LEGACY) {
++ if (pll->flags & RADEON_PLL_LEGACY) {
+ if ((post_div == 5) ||
+ (post_div == 7) ||
+ (post_div == 9) ||
+@@ -480,7 +522,7 @@ void radeon_compute_pll(struct radeon_pll *pll,
+ tmp += (uint64_t)pll->reference_freq * 1000 * frac_feedback_div;
+ current_freq = radeon_div(tmp, ref_div * post_div);
+
+- if (flags & RADEON_PLL_PREFER_CLOSEST_LOWER) {
++ if (pll->flags & RADEON_PLL_PREFER_CLOSEST_LOWER) {
+ error = freq - current_freq;
+ error = error < 0 ? 0xffffffff : error;
+ } else
+@@ -507,12 +549,12 @@ void radeon_compute_pll(struct radeon_pll *pll,
+ best_freq = current_freq;
+ best_error = error;
+ best_vco_diff = vco_diff;
+- } else if (((flags & RADEON_PLL_PREFER_LOW_REF_DIV) && (ref_div < best_ref_div)) ||
+- ((flags & RADEON_PLL_PREFER_HIGH_REF_DIV) && (ref_div > best_ref_div)) ||
+- ((flags & RADEON_PLL_PREFER_LOW_FB_DIV) && (feedback_div < best_feedback_div)) ||
+- ((flags & RADEON_PLL_PREFER_HIGH_FB_DIV) && (feedback_div > best_feedback_div)) ||
+- ((flags & RADEON_PLL_PREFER_LOW_POST_DIV) && (post_div < best_post_div)) ||
+- ((flags & RADEON_PLL_PREFER_HIGH_POST_DIV) && (post_div > best_post_div))) {
++ } else if (((pll->flags & RADEON_PLL_PREFER_LOW_REF_DIV) && (ref_div < best_ref_div)) ||
++ ((pll->flags & RADEON_PLL_PREFER_HIGH_REF_DIV) && (ref_div > best_ref_div)) ||
++ ((pll->flags & RADEON_PLL_PREFER_LOW_FB_DIV) && (feedback_div < best_feedback_div)) ||
++ ((pll->flags & RADEON_PLL_PREFER_HIGH_FB_DIV) && (feedback_div > best_feedback_div)) ||
++ ((pll->flags & RADEON_PLL_PREFER_LOW_POST_DIV) && (post_div < best_post_div)) ||
++ ((pll->flags & RADEON_PLL_PREFER_HIGH_POST_DIV) && (post_div > best_post_div))) {
+ best_post_div = post_div;
+ best_ref_div = ref_div;
+ best_feedback_div = feedback_div;
+@@ -542,6 +584,97 @@ void radeon_compute_pll(struct radeon_pll *pll,
+ *post_div_p = best_post_div;
+ }
+
++void radeon_compute_pll_avivo(struct radeon_pll *pll,
++ uint64_t freq,
++ uint32_t *dot_clock_p,
++ uint32_t *fb_div_p,
++ uint32_t *frac_fb_div_p,
++ uint32_t *ref_div_p,
++ uint32_t *post_div_p)
++{
++ fixed20_12 m, n, frac_n, p, f_vco, f_pclk, best_freq;
++ fixed20_12 pll_out_max, pll_out_min;
++ fixed20_12 pll_in_max, pll_in_min;
++ fixed20_12 reference_freq;
++ fixed20_12 error, ffreq, a, b;
++
++ pll_out_max.full = rfixed_const(pll->pll_out_max);
++ pll_out_min.full = rfixed_const(pll->pll_out_min);
++ pll_in_max.full = rfixed_const(pll->pll_in_max);
++ pll_in_min.full = rfixed_const(pll->pll_in_min);
++ reference_freq.full = rfixed_const(pll->reference_freq);
++ do_div(freq, 10);
++ ffreq.full = rfixed_const(freq);
++ error.full = rfixed_const(100 * 100);
++
++ /* max p */
++ p.full = rfixed_div(pll_out_max, ffreq);
++ p.full = rfixed_floor(p);
++
++ /* min m */
++ m.full = rfixed_div(reference_freq, pll_in_max);
++ m.full = rfixed_ceil(m);
++
++ while (1) {
++ n.full = rfixed_div(ffreq, reference_freq);
++ n.full = rfixed_mul(n, m);
++ n.full = rfixed_mul(n, p);
++
++ f_vco.full = rfixed_div(n, m);
++ f_vco.full = rfixed_mul(f_vco, reference_freq);
++
++ f_pclk.full = rfixed_div(f_vco, p);
++
++ if (f_pclk.full > ffreq.full)
++ error.full = f_pclk.full - ffreq.full;
++ else
++ error.full = ffreq.full - f_pclk.full;
++ error.full = rfixed_div(error, f_pclk);
++ a.full = rfixed_const(100 * 100);
++ error.full = rfixed_mul(error, a);
++
++ a.full = rfixed_mul(m, p);
++ a.full = rfixed_div(n, a);
++ best_freq.full = rfixed_mul(reference_freq, a);
++
++ if (rfixed_trunc(error) < 25)
++ break;
++
++ a.full = rfixed_const(1);
++ m.full = m.full + a.full;
++ a.full = rfixed_div(reference_freq, m);
++ if (a.full >= pll_in_min.full)
++ continue;
++
++ m.full = rfixed_div(reference_freq, pll_in_max);
++ m.full = rfixed_ceil(m);
++ a.full= rfixed_const(1);
++ p.full = p.full - a.full;
++ a.full = rfixed_mul(p, ffreq);
++ if (a.full >= pll_out_min.full)
++ continue;
++ else {
++ DRM_ERROR("Unable to find pll dividers\n");
++ break;
++ }
++ }
++
++ a.full = rfixed_const(10);
++ b.full = rfixed_mul(n, a);
++
++ frac_n.full = rfixed_floor(n);
++ frac_n.full = rfixed_mul(frac_n, a);
++ frac_n.full = b.full - frac_n.full;
++
++ *dot_clock_p = rfixed_trunc(best_freq);
++ *fb_div_p = rfixed_trunc(n);
++ *frac_fb_div_p = rfixed_trunc(frac_n);
++ *ref_div_p = rfixed_trunc(m);
++ *post_div_p = rfixed_trunc(p);
++
++ DRM_DEBUG("%u %d.%d, %d, %d\n", *dot_clock_p * 10, *fb_div_p, *frac_fb_div_p, *ref_div_p, *post_div_p);
++}
++
+ static void radeon_user_framebuffer_destroy(struct drm_framebuffer *fb)
+ {
+ struct radeon_framebuffer *radeon_fb = to_radeon_framebuffer(fb);
+@@ -551,7 +684,6 @@ static void radeon_user_framebuffer_destroy(struct drm_framebuffer *fb)
+ radeonfb_remove(dev, fb);
+
+ if (radeon_fb->obj) {
+- radeon_gem_object_unpin(radeon_fb->obj);
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(radeon_fb->obj);
+ mutex_unlock(&dev->struct_mutex);
+@@ -599,7 +731,11 @@ radeon_user_framebuffer_create(struct drm_device *dev,
+ struct drm_gem_object *obj;
+
+ obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle);
+-
++ if (obj == NULL) {
++ dev_err(&dev->pdev->dev, "No GEM object associated to handle 0x%08X, "
++ "can't create framebuffer\n", mode_cmd->handle);
++ return NULL;
++ }
+ return radeon_framebuffer_create(dev, mode_cmd, obj);
+ }
+
+@@ -629,7 +765,7 @@ static struct drm_prop_enum_list radeon_tv_std_enum_list[] =
+ { TV_STD_SECAM, "secam" },
+ };
+
+-int radeon_modeset_create_props(struct radeon_device *rdev)
++static int radeon_modeset_create_props(struct radeon_device *rdev)
+ {
+ int i, sz;
+
+@@ -642,7 +778,7 @@ int radeon_modeset_create_props(struct radeon_device *rdev)
+ return -ENOMEM;
+
+ rdev->mode_info.coherent_mode_property->values[0] = 0;
+- rdev->mode_info.coherent_mode_property->values[0] = 1;
++ rdev->mode_info.coherent_mode_property->values[1] = 1;
+ }
+
+ if (!ASIC_IS_AVIVO(rdev)) {
+@@ -666,7 +802,7 @@ int radeon_modeset_create_props(struct radeon_device *rdev)
+ if (!rdev->mode_info.load_detect_property)
+ return -ENOMEM;
+ rdev->mode_info.load_detect_property->values[0] = 0;
+- rdev->mode_info.load_detect_property->values[0] = 1;
++ rdev->mode_info.load_detect_property->values[1] = 1;
+
+ drm_mode_create_scaling_mode_property(rdev->ddev);
+
+@@ -710,6 +846,12 @@ int radeon_modeset_init(struct radeon_device *rdev)
+ return ret;
+ }
+
++ /* check combios for a valid hardcoded EDID - Sun servers */
++ if (!rdev->is_atom_bios) {
++ /* check for hardcoded EDID in BIOS */
++ radeon_combios_check_hardcoded_edid(rdev);
++ }
++
+ if (rdev->flags & RADEON_SINGLE_CRTC)
+ num_crtc = 1;
+
+@@ -723,13 +865,18 @@ int radeon_modeset_init(struct radeon_device *rdev)
+ if (!ret) {
+ return ret;
+ }
++ /* initialize hpd */
++ radeon_hpd_init(rdev);
+ drm_helper_initial_config(rdev->ddev);
+ return 0;
+ }
+
+ void radeon_modeset_fini(struct radeon_device *rdev)
+ {
++ kfree(rdev->mode_info.bios_hardcoded_edid);
++
+ if (rdev->mode_info.mode_config_initialized) {
++ radeon_hpd_fini(rdev);
+ drm_mode_config_cleanup(rdev->ddev);
+ rdev->mode_info.mode_config_initialized = false;
+ }
+@@ -750,9 +897,17 @@ bool radeon_crtc_scaling_mode_fixup(struct drm_crtc *crtc,
+ if (encoder->crtc != crtc)
+ continue;
+ if (first) {
+- radeon_crtc->rmx_type = radeon_encoder->rmx_type;
++ /* set scaling */
++ if (radeon_encoder->rmx_type == RMX_OFF)
++ radeon_crtc->rmx_type = RMX_OFF;
++ else if (mode->hdisplay < radeon_encoder->native_mode.hdisplay ||
++ mode->vdisplay < radeon_encoder->native_mode.vdisplay)
++ radeon_crtc->rmx_type = radeon_encoder->rmx_type;
++ else
++ radeon_crtc->rmx_type = RMX_OFF;
++ /* copy native mode */
+ memcpy(&radeon_crtc->native_mode,
+- &radeon_encoder->native_mode,
++ &radeon_encoder->native_mode,
+ sizeof(struct drm_display_mode));
+ first = false;
+ } else {
+diff --git a/drivers/gpu/drm/radeon/radeon_drv.c b/drivers/gpu/drm/radeon/radeon_drv.c
+index 7f50fb8..8ba3de7 100644
+--- a/drivers/gpu/drm/radeon/radeon_drv.c
++++ b/drivers/gpu/drm/radeon/radeon_drv.c
+@@ -86,6 +86,8 @@ int radeon_benchmarking = 0;
+ int radeon_testing = 0;
+ int radeon_connector_table = 0;
+ int radeon_tv = 1;
++int radeon_new_pll = 1;
++int radeon_audio = 1;
+
+ MODULE_PARM_DESC(no_wb, "Disable AGP writeback for scratch registers");
+ module_param_named(no_wb, radeon_no_wb, int, 0444);
+@@ -120,6 +122,12 @@ module_param_named(connector_table, radeon_connector_table, int, 0444);
+ MODULE_PARM_DESC(tv, "TV enable (0 = disable)");
+ module_param_named(tv, radeon_tv, int, 0444);
+
++MODULE_PARM_DESC(new_pll, "Select new PLL code for AVIVO chips");
++module_param_named(new_pll, radeon_new_pll, int, 0444);
++
++MODULE_PARM_DESC(audio, "Audio enable (0 = disable)");
++module_param_named(audio, radeon_audio, int, 0444);
++
+ static int radeon_suspend(struct drm_device *dev, pm_message_t state)
+ {
+ drm_radeon_private_t *dev_priv = dev->dev_private;
+@@ -188,7 +196,7 @@ static struct drm_driver driver_old = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+- .ioctl = drm_ioctl,
++ .unlocked_ioctl = drm_ioctl,
+ .mmap = drm_mmap,
+ .poll = drm_poll,
+ .fasync = drm_fasync,
+@@ -276,7 +284,7 @@ static struct drm_driver kms_driver = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+- .ioctl = drm_ioctl,
++ .unlocked_ioctl = drm_ioctl,
+ .mmap = radeon_mmap,
+ .poll = drm_poll,
+ .fasync = drm_fasync,
+diff --git a/drivers/gpu/drm/radeon/radeon_drv.h b/drivers/gpu/drm/radeon/radeon_drv.h
+index 350962e..e137852 100644
+--- a/drivers/gpu/drm/radeon/radeon_drv.h
++++ b/drivers/gpu/drm/radeon/radeon_drv.h
+@@ -1104,7 +1104,6 @@ extern u32 radeon_get_scratch(drm_radeon_private_t *dev_priv, int index);
+ # define R600_IT_WAIT_REG_MEM 0x00003C00
+ # define R600_IT_MEM_WRITE 0x00003D00
+ # define R600_IT_INDIRECT_BUFFER 0x00003200
+-# define R600_IT_CP_INTERRUPT 0x00004000
+ # define R600_IT_SURFACE_SYNC 0x00004300
+ # define R600_CB0_DEST_BASE_ENA (1 << 6)
+ # define R600_TC_ACTION_ENA (1 << 23)
+diff --git a/drivers/gpu/drm/radeon/radeon_encoders.c b/drivers/gpu/drm/radeon/radeon_encoders.c
+index d42bc51..3c91724 100644
+--- a/drivers/gpu/drm/radeon/radeon_encoders.c
++++ b/drivers/gpu/drm/radeon/radeon_encoders.c
+@@ -35,6 +35,51 @@ extern int atom_debug;
+ bool radeon_atom_get_tv_timings(struct radeon_device *rdev, int index,
+ struct drm_display_mode *mode);
+
++static uint32_t radeon_encoder_clones(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++ struct radeon_device *rdev = dev->dev_private;
++ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
++ struct drm_encoder *clone_encoder;
++ uint32_t index_mask = 0;
++ int count;
++
++ /* DIG routing gets problematic */
++ if (rdev->family >= CHIP_R600)
++ return index_mask;
++ /* LVDS/TV are too wacky */
++ if (radeon_encoder->devices & ATOM_DEVICE_LCD_SUPPORT)
++ return index_mask;
++ /* DVO requires 2x ppll clocks depending on tmds chip */
++ if (radeon_encoder->devices & ATOM_DEVICE_DFP2_SUPPORT)
++ return index_mask;
++
++ count = -1;
++ list_for_each_entry(clone_encoder, &dev->mode_config.encoder_list, head) {
++ struct radeon_encoder *radeon_clone = to_radeon_encoder(clone_encoder);
++ count++;
++
++ if (clone_encoder == encoder)
++ continue;
++ if (radeon_clone->devices & (ATOM_DEVICE_LCD_SUPPORT))
++ continue;
++ if (radeon_clone->devices & ATOM_DEVICE_DFP2_SUPPORT)
++ continue;
++ else
++ index_mask |= (1 << count);
++ }
++ return index_mask;
++}
++
++void radeon_setup_encoder_clones(struct drm_device *dev)
++{
++ struct drm_encoder *encoder;
++
++ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
++ encoder->possible_clones = radeon_encoder_clones(encoder);
++ }
++}
++
+ uint32_t
+ radeon_get_encoder_id(struct drm_device *dev, uint32_t supported_device, uint8_t dac)
+ {
+@@ -111,6 +156,26 @@ radeon_get_encoder_id(struct drm_device *dev, uint32_t supported_device, uint8_t
+ return ret;
+ }
+
++static inline bool radeon_encoder_is_digital(struct drm_encoder *encoder)
++{
++ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
++ switch (radeon_encoder->encoder_id) {
++ case ENCODER_OBJECT_ID_INTERNAL_LVDS:
++ case ENCODER_OBJECT_ID_INTERNAL_TMDS1:
++ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_TMDS1:
++ case ENCODER_OBJECT_ID_INTERNAL_LVTM1:
++ case ENCODER_OBJECT_ID_INTERNAL_DVO1:
++ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
++ case ENCODER_OBJECT_ID_INTERNAL_DDI:
++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
++ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
++ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
++ return true;
++ default:
++ return false;
++ }
++}
+ void
+ radeon_link_encoder_connector(struct drm_device *dev)
+ {
+@@ -157,35 +222,12 @@ radeon_get_connector_for_encoder(struct drm_encoder *encoder)
+
+ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
+ radeon_connector = to_radeon_connector(connector);
+- if (radeon_encoder->devices & radeon_connector->devices)
++ if (radeon_encoder->active_device & radeon_connector->devices)
+ return connector;
+ }
+ return NULL;
+ }
+
+-/* used for both atom and legacy */
+-void radeon_rmx_mode_fixup(struct drm_encoder *encoder,
+- struct drm_display_mode *mode,
+- struct drm_display_mode *adjusted_mode)
+-{
+- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+- struct drm_device *dev = encoder->dev;
+- struct radeon_device *rdev = dev->dev_private;
+- struct drm_display_mode *native_mode = &radeon_encoder->native_mode;
+-
+- if (mode->hdisplay < native_mode->hdisplay ||
+- mode->vdisplay < native_mode->vdisplay) {
+- int mode_id = adjusted_mode->base.id;
+- *adjusted_mode = *native_mode;
+- if (!ASIC_IS_AVIVO(rdev)) {
+- adjusted_mode->hdisplay = mode->hdisplay;
+- adjusted_mode->vdisplay = mode->vdisplay;
+- }
+- adjusted_mode->base.id = mode_id;
+- }
+-}
+-
+-
+ static bool radeon_atom_mode_fixup(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+ struct drm_display_mode *adjusted_mode)
+@@ -198,14 +240,26 @@ static bool radeon_atom_mode_fixup(struct drm_encoder *encoder,
+ radeon_encoder_set_active_device(encoder);
+ drm_mode_set_crtcinfo(adjusted_mode, 0);
+
+- if (radeon_encoder->rmx_type != RMX_OFF)
+- radeon_rmx_mode_fixup(encoder, mode, adjusted_mode);
+-
+ /* hw bug */
+ if ((mode->flags & DRM_MODE_FLAG_INTERLACE)
+ && (mode->crtc_vsync_start < (mode->crtc_vdisplay + 2)))
+ adjusted_mode->crtc_vsync_start = adjusted_mode->crtc_vdisplay + 2;
+
++ /* get the native mode for LVDS */
++ if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
++ struct drm_display_mode *native_mode = &radeon_encoder->native_mode;
++ int mode_id = adjusted_mode->base.id;
++ *adjusted_mode = *native_mode;
++ if (!ASIC_IS_AVIVO(rdev)) {
++ adjusted_mode->hdisplay = mode->hdisplay;
++ adjusted_mode->vdisplay = mode->vdisplay;
++ adjusted_mode->crtc_hdisplay = mode->hdisplay;
++ adjusted_mode->crtc_vdisplay = mode->vdisplay;
++ }
++ adjusted_mode->base.id = mode_id;
++ }
++
++ /* get the native mode for TV */
+ if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT)) {
+ struct radeon_encoder_atom_dac *tv_dac = radeon_encoder->enc_priv;
+ if (tv_dac) {
+@@ -218,6 +272,12 @@ static bool radeon_atom_mode_fixup(struct drm_encoder *encoder,
+ }
+ }
+
++ if (ASIC_IS_DCE3(rdev) &&
++ (radeon_encoder->active_device & (ATOM_DEVICE_DFP_SUPPORT))) {
++ struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
++ radeon_dp_set_link_config(connector, mode);
++ }
++
+ return true;
+ }
+
+@@ -392,7 +452,7 @@ union lvds_encoder_control {
+ LVDS_ENCODER_CONTROL_PS_ALLOCATION_V2 v2;
+ };
+
+-static void
++void
+ atombios_digital_setup(struct drm_encoder *encoder, int action)
+ {
+ struct drm_device *dev = encoder->dev;
+@@ -400,6 +460,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ union lvds_encoder_control args;
+ int index = 0;
++ int hdmi_detected = 0;
+ uint8_t frev, crev;
+ struct radeon_encoder_atom_dig *dig;
+ struct drm_connector *connector;
+@@ -420,6 +481,9 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
+ if (!radeon_connector->con_priv)
+ return;
+
++ if (drm_detect_hdmi_monitor(radeon_connector->edid))
++ hdmi_detected = 1;
++
+ dig_connector = radeon_connector->con_priv;
+
+ memset(&args, 0, sizeof(args));
+@@ -449,13 +513,13 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
+ case 1:
+ args.v1.ucMisc = 0;
+ args.v1.ucAction = action;
+- if (drm_detect_hdmi_monitor(radeon_connector->edid))
++ if (hdmi_detected)
+ args.v1.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE;
+ args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+ if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
+- if (dig->lvds_misc & (1 << 0))
++ if (dig->lvds_misc & ATOM_PANEL_MISC_DUAL)
+ args.v1.ucMisc |= PANEL_ENCODER_MISC_DUAL;
+- if (dig->lvds_misc & (1 << 1))
++ if (dig->lvds_misc & ATOM_PANEL_MISC_888RGB)
+ args.v1.ucMisc |= (1 << 1);
+ } else {
+ if (dig_connector->linkb)
+@@ -474,7 +538,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
+ if (dig->coherent_mode)
+ args.v2.ucMisc |= PANEL_ENCODER_MISC_COHERENT;
+ }
+- if (drm_detect_hdmi_monitor(radeon_connector->edid))
++ if (hdmi_detected)
+ args.v2.ucMisc |= PANEL_ENCODER_MISC_HDMI_TYPE;
+ args.v2.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+ args.v2.ucTruncate = 0;
+@@ -482,18 +546,18 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
+ args.v2.ucTemporal = 0;
+ args.v2.ucFRC = 0;
+ if (radeon_encoder->devices & (ATOM_DEVICE_LCD_SUPPORT)) {
+- if (dig->lvds_misc & (1 << 0))
++ if (dig->lvds_misc & ATOM_PANEL_MISC_DUAL)
+ args.v2.ucMisc |= PANEL_ENCODER_MISC_DUAL;
+- if (dig->lvds_misc & (1 << 5)) {
++ if (dig->lvds_misc & ATOM_PANEL_MISC_SPATIAL) {
+ args.v2.ucSpatial = PANEL_ENCODER_SPATIAL_DITHER_EN;
+- if (dig->lvds_misc & (1 << 1))
++ if (dig->lvds_misc & ATOM_PANEL_MISC_888RGB)
+ args.v2.ucSpatial |= PANEL_ENCODER_SPATIAL_DITHER_DEPTH;
+ }
+- if (dig->lvds_misc & (1 << 6)) {
++ if (dig->lvds_misc & ATOM_PANEL_MISC_TEMPORAL) {
+ args.v2.ucTemporal = PANEL_ENCODER_TEMPORAL_DITHER_EN;
+- if (dig->lvds_misc & (1 << 1))
++ if (dig->lvds_misc & ATOM_PANEL_MISC_888RGB)
+ args.v2.ucTemporal |= PANEL_ENCODER_TEMPORAL_DITHER_DEPTH;
+- if (((dig->lvds_misc >> 2) & 0x3) == 2)
++ if (((dig->lvds_misc >> ATOM_PANEL_MISC_GREY_LEVEL_SHIFT) & 0x3) == 2)
+ args.v2.ucTemporal |= PANEL_ENCODER_TEMPORAL_LEVEL_4;
+ }
+ } else {
+@@ -514,7 +578,7 @@ atombios_digital_setup(struct drm_encoder *encoder, int action)
+ }
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+-
++ r600_hdmi_enable(encoder, hdmi_detected);
+ }
+
+ int
+@@ -522,6 +586,7 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
+ {
+ struct drm_connector *connector;
+ struct radeon_connector *radeon_connector;
++ struct radeon_connector_atom_dig *radeon_dig_connector;
+
+ connector = radeon_get_connector_for_encoder(encoder);
+ if (!connector)
+@@ -551,21 +616,23 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
+ return ATOM_ENCODER_MODE_LVDS;
+ break;
+ case DRM_MODE_CONNECTOR_DisplayPort:
+- /*if (radeon_output->MonType == MT_DP)
+- return ATOM_ENCODER_MODE_DP;
+- else*/
+- if (drm_detect_hdmi_monitor(radeon_connector->edid))
++ case DRM_MODE_CONNECTOR_eDP:
++ radeon_dig_connector = radeon_connector->con_priv;
++ if ((radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_DISPLAYPORT) ||
++ (radeon_dig_connector->dp_sink_type == CONNECTOR_OBJECT_ID_eDP))
++ return ATOM_ENCODER_MODE_DP;
++ else if (drm_detect_hdmi_monitor(radeon_connector->edid))
+ return ATOM_ENCODER_MODE_HDMI;
+ else
+ return ATOM_ENCODER_MODE_DVI;
+ break;
+- case CONNECTOR_DVI_A:
+- case CONNECTOR_VGA:
++ case DRM_MODE_CONNECTOR_DVIA:
++ case DRM_MODE_CONNECTOR_VGA:
+ return ATOM_ENCODER_MODE_CRT;
+ break;
+- case CONNECTOR_STV:
+- case CONNECTOR_CTV:
+- case CONNECTOR_DIN:
++ case DRM_MODE_CONNECTOR_Composite:
++ case DRM_MODE_CONNECTOR_SVIDEO:
++ case DRM_MODE_CONNECTOR_9PinDIN:
+ /* fix me */
+ return ATOM_ENCODER_MODE_TV;
+ /*return ATOM_ENCODER_MODE_CV;*/
+@@ -573,6 +640,30 @@ atombios_get_encoder_mode(struct drm_encoder *encoder)
+ }
+ }
+
++/*
++ * DIG Encoder/Transmitter Setup
++ *
++ * DCE 3.0/3.1
++ * - 2 DIG transmitter blocks. UNIPHY (links A and B) and LVTMA.
++ * Supports up to 3 digital outputs
++ * - 2 DIG encoder blocks.
++ * DIG1 can drive UNIPHY link A or link B
++ * DIG2 can drive UNIPHY link B or LVTMA
++ *
++ * DCE 3.2
++ * - 3 DIG transmitter blocks. UNIPHY0/1/2 (links A and B).
++ * Supports up to 5 digital outputs
++ * - 2 DIG encoder blocks.
++ * DIG1/2 can drive UNIPHY0/1/2 link A or link B
++ *
++ * Routing
++ * crtc -> dig encoder -> UNIPHY/LVTMA (1 or 2 links)
++ * Examples:
++ * crtc0 -> dig2 -> LVTMA links A+B -> TMDS/HDMI
++ * crtc1 -> dig1 -> UNIPHY0 link B -> DP
++ * crtc0 -> dig1 -> UNIPHY2 link A -> LVDS
++ * crtc1 -> dig2 -> UNIPHY1 link B+A -> TMDS/HDMI
++ */
+ static void
+ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
+ {
+@@ -605,24 +696,11 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
+
+ memset(&args, 0, sizeof(args));
+
+- if (ASIC_IS_DCE32(rdev)) {
+- if (dig->dig_block)
+- index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl);
+- else
+- index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl);
+- num = dig->dig_block + 1;
+- } else {
+- switch (radeon_encoder->encoder_id) {
+- case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+- index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl);
+- num = 1;
+- break;
+- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+- index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl);
+- num = 2;
+- break;
+- }
+- }
++ if (dig->dig_encoder)
++ index = GetIndexIntoMasterTable(COMMAND, DIG2EncoderControl);
++ else
++ index = GetIndexIntoMasterTable(COMMAND, DIG1EncoderControl);
++ num = dig->dig_encoder + 1;
+
+ atom_parse_cmd_header(rdev->mode_info.atom_context, index, &frev, &crev);
+
+@@ -652,18 +730,21 @@ atombios_dig_encoder_setup(struct drm_encoder *encoder, int action)
+ }
+ }
+
+- if (radeon_encoder->pixel_clock > 165000) {
+- args.ucConfig |= ATOM_ENCODER_CONFIG_LINKA_B;
++ args.ucEncoderMode = atombios_get_encoder_mode(encoder);
++
++ if (args.ucEncoderMode == ATOM_ENCODER_MODE_DP) {
++ if (dig_connector->dp_clock == 270000)
++ args.ucConfig |= ATOM_ENCODER_CONFIG_DPLINKRATE_2_70GHZ;
++ args.ucLaneNum = dig_connector->dp_lane_count;
++ } else if (radeon_encoder->pixel_clock > 165000)
+ args.ucLaneNum = 8;
+- } else {
+- if (dig_connector->linkb)
+- args.ucConfig |= ATOM_ENCODER_CONFIG_LINKB;
+- else
+- args.ucConfig |= ATOM_ENCODER_CONFIG_LINKA;
++ else
+ args.ucLaneNum = 4;
+- }
+
+- args.ucEncoderMode = atombios_get_encoder_mode(encoder);
++ if (dig_connector->linkb)
++ args.ucConfig |= ATOM_ENCODER_CONFIG_LINKB;
++ else
++ args.ucConfig |= ATOM_ENCODER_CONFIG_LINKA;
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+
+@@ -674,8 +755,8 @@ union dig_transmitter_control {
+ DIG_TRANSMITTER_CONTROL_PARAMETERS_V2 v2;
+ };
+
+-static void
+-atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
++void
++atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action, uint8_t lane_num, uint8_t lane_set)
+ {
+ struct drm_device *dev = encoder->dev;
+ struct radeon_device *rdev = dev->dev_private;
+@@ -687,6 +768,7 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
+ struct drm_connector *connector;
+ struct radeon_connector *radeon_connector;
+ struct radeon_connector_atom_dig *dig_connector;
++ bool is_dp = false;
+
+ connector = radeon_get_connector_for_encoder(encoder);
+ if (!connector)
+@@ -704,6 +786,9 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
+
+ dig_connector = radeon_connector->con_priv;
+
++ if (atombios_get_encoder_mode(encoder) == ATOM_ENCODER_MODE_DP)
++ is_dp = true;
++
+ memset(&args, 0, sizeof(args));
+
+ if (ASIC_IS_DCE32(rdev))
+@@ -724,17 +809,23 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
+ args.v1.ucAction = action;
+ if (action == ATOM_TRANSMITTER_ACTION_INIT) {
+ args.v1.usInitInfo = radeon_connector->connector_object_id;
++ } else if (action == ATOM_TRANSMITTER_ACTION_SETUP_VSEMPH) {
++ args.v1.asMode.ucLaneSel = lane_num;
++ args.v1.asMode.ucLaneSet = lane_set;
+ } else {
+- if (radeon_encoder->pixel_clock > 165000)
++ if (is_dp)
++ args.v1.usPixelClock =
++ cpu_to_le16(dig_connector->dp_clock / 10);
++ else if (radeon_encoder->pixel_clock > 165000)
+ args.v1.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10);
+ else
+ args.v1.usPixelClock = cpu_to_le16(radeon_encoder->pixel_clock / 10);
+ }
+ if (ASIC_IS_DCE32(rdev)) {
+- if (radeon_encoder->pixel_clock > 165000)
+- args.v2.usPixelClock = cpu_to_le16((radeon_encoder->pixel_clock / 2) / 10);
+- if (dig->dig_block)
++ if (dig->dig_encoder == 1)
+ args.v2.acConfig.ucEncoderSel = 1;
++ if (dig_connector->linkb)
++ args.v2.acConfig.ucLinkSel = 1;
+
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+@@ -751,26 +842,30 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
+ break;
+ }
+
+- if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
++ if (is_dp)
++ args.v2.acConfig.fCoherentMode = 1;
++ else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
+ if (dig->coherent_mode)
+ args.v2.acConfig.fCoherentMode = 1;
+ }
+ } else {
++
+ args.v1.ucConfig = ATOM_TRANSMITTER_CONFIG_CLKSRC_PPLL;
+
++ if (dig->dig_encoder)
++ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER;
++ else
++ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER;
++
+ switch (radeon_encoder->encoder_id) {
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG1_ENCODER;
+ if (rdev->flags & RADEON_IS_IGP) {
+ if (radeon_encoder->pixel_clock > 165000) {
+- args.v1.ucConfig |= (ATOM_TRANSMITTER_CONFIG_8LANE_LINK |
+- ATOM_TRANSMITTER_CONFIG_LINKA_B);
+ if (dig_connector->igp_lane_info & 0x3)
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_7;
+ else if (dig_connector->igp_lane_info & 0xc)
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_8_15;
+ } else {
+- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA;
+ if (dig_connector->igp_lane_info & 0x1)
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_0_3;
+ else if (dig_connector->igp_lane_info & 0x2)
+@@ -780,42 +875,27 @@ atombios_dig_transmitter_setup(struct drm_encoder *encoder, int action)
+ else if (dig_connector->igp_lane_info & 0x8)
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LANE_12_15;
+ }
+- } else {
+- if (radeon_encoder->pixel_clock > 165000)
+- args.v1.ucConfig |= (ATOM_TRANSMITTER_CONFIG_8LANE_LINK |
+- ATOM_TRANSMITTER_CONFIG_LINKA_B |
+- ATOM_TRANSMITTER_CONFIG_LANE_0_7);
+- else {
+- if (dig_connector->linkb)
+- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB | ATOM_TRANSMITTER_CONFIG_LANE_0_3;
+- else
+- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA | ATOM_TRANSMITTER_CONFIG_LANE_0_3;
+- }
+- }
+- break;
+- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_DIG2_ENCODER;
+- if (radeon_encoder->pixel_clock > 165000)
+- args.v1.ucConfig |= (ATOM_TRANSMITTER_CONFIG_8LANE_LINK |
+- ATOM_TRANSMITTER_CONFIG_LINKA_B |
+- ATOM_TRANSMITTER_CONFIG_LANE_0_7);
+- else {
+- if (dig_connector->linkb)
+- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB | ATOM_TRANSMITTER_CONFIG_LANE_0_3;
+- else
+- args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA | ATOM_TRANSMITTER_CONFIG_LANE_0_3;
+ }
+ break;
+ }
+
+- if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
++ if (radeon_encoder->pixel_clock > 165000)
++ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_8LANE_LINK;
++
++ if (dig_connector->linkb)
++ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKB;
++ else
++ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_LINKA;
++
++ if (is_dp)
++ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT;
++ else if (radeon_encoder->devices & (ATOM_DEVICE_DFP_SUPPORT)) {
+ if (dig->coherent_mode)
+ args.v1.ucConfig |= ATOM_TRANSMITTER_CONFIG_COHERENT;
+ }
+ }
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+-
+ }
+
+ static void
+@@ -918,12 +998,16 @@ radeon_atom_encoder_dpms(struct drm_encoder *encoder, int mode)
+ if (is_dig) {
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE);
++ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE_OUTPUT, 0, 0);
++ {
++ struct drm_connector *connector = radeon_get_connector_for_encoder(encoder);
++ dp_link_train(encoder, connector);
++ }
+ break;
+ case DRM_MODE_DPMS_STANDBY:
+ case DRM_MODE_DPMS_SUSPEND:
+ case DRM_MODE_DPMS_OFF:
+- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE);
++ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE_OUTPUT, 0, 0);
+ break;
+ }
+ } else {
+@@ -957,6 +1041,7 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
+ union crtc_sourc_param args;
+ int index = GetIndexIntoMasterTable(COMMAND, SelectCRTC_Source);
+ uint8_t frev, crev;
++ struct radeon_encoder_atom_dig *dig;
+
+ memset(&args, 0, sizeof(args));
+
+@@ -1020,20 +1105,16 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY1:
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+- if (ASIC_IS_DCE32(rdev)) {
+- if (radeon_crtc->crtc_id)
+- args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
+- else
+- args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID;
+- } else
++ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
++ dig = radeon_encoder->enc_priv;
++ if (dig->dig_encoder)
++ args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
++ else
+ args.v2.ucEncoderID = ASIC_INT_DIG1_ENCODER_ID;
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DVO1:
+ args.v2.ucEncoderID = ASIC_INT_DVO_ENCODER_ID;
+ break;
+- case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+- args.v2.ucEncoderID = ASIC_INT_DIG2_ENCODER_ID;
+- break;
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_DAC1:
+ if (radeon_encoder->active_device & (ATOM_DEVICE_TV_SUPPORT))
+ args.v2.ucEncoderID = ASIC_INT_TV_ENCODER_ID;
+@@ -1060,7 +1141,6 @@ atombios_set_encoder_crtc_source(struct drm_encoder *encoder)
+ }
+
+ atom_execute_table(rdev->mode_info.atom_context, index, (uint32_t *)&args);
+-
+ }
+
+ static void
+@@ -1094,6 +1174,47 @@ atombios_apply_encoder_quirks(struct drm_encoder *encoder,
+ }
+ }
+
++static int radeon_atom_pick_dig_encoder(struct drm_encoder *encoder)
++{
++ struct drm_device *dev = encoder->dev;
++ struct radeon_device *rdev = dev->dev_private;
++ struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
++ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
++ struct drm_encoder *test_encoder;
++ struct radeon_encoder_atom_dig *dig;
++ uint32_t dig_enc_in_use = 0;
++ /* on DCE32 and encoder can driver any block so just crtc id */
++ if (ASIC_IS_DCE32(rdev)) {
++ return radeon_crtc->crtc_id;
++ }
++
++ /* on DCE3 - LVTMA can only be driven by DIGB */
++ list_for_each_entry(test_encoder, &dev->mode_config.encoder_list, head) {
++ struct radeon_encoder *radeon_test_encoder;
++
++ if (encoder == test_encoder)
++ continue;
++
++ if (!radeon_encoder_is_digital(test_encoder))
++ continue;
++
++ radeon_test_encoder = to_radeon_encoder(test_encoder);
++ dig = radeon_test_encoder->enc_priv;
++
++ if (dig->dig_encoder >= 0)
++ dig_enc_in_use |= (1 << dig->dig_encoder);
++ }
++
++ if (radeon_encoder->encoder_id == ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA) {
++ if (dig_enc_in_use & 0x2)
++ DRM_ERROR("LVDS required digital encoder 2 but it was in use - stealing\n");
++ return 1;
++ }
++ if (!(dig_enc_in_use & 1))
++ return 0;
++ return 1;
++}
++
+ static void
+ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
+ struct drm_display_mode *mode,
+@@ -1104,11 +1225,11 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(encoder->crtc);
+
+- if (radeon_encoder->enc_priv) {
+- struct radeon_encoder_atom_dig *dig;
+-
+- dig = radeon_encoder->enc_priv;
+- dig->dig_block = radeon_crtc->crtc_id;
++ if (radeon_encoder->active_device &
++ (ATOM_DEVICE_DFP_SUPPORT | ATOM_DEVICE_LCD_SUPPORT)) {
++ struct radeon_encoder_atom_dig *dig = radeon_encoder->enc_priv;
++ if (dig)
++ dig->dig_encoder = radeon_atom_pick_dig_encoder(encoder);
+ }
+ radeon_encoder->pixel_clock = adjusted_mode->clock;
+
+@@ -1134,14 +1255,14 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
+ case ENCODER_OBJECT_ID_INTERNAL_UNIPHY2:
+ case ENCODER_OBJECT_ID_INTERNAL_KLDSCP_LVTMA:
+ /* disable the encoder and transmitter */
+- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE);
++ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_DISABLE, 0, 0);
+ atombios_dig_encoder_setup(encoder, ATOM_DISABLE);
+
+ /* setup and enable the encoder and transmitter */
+ atombios_dig_encoder_setup(encoder, ATOM_ENABLE);
+- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT);
+- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP);
+- atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE);
++ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_INIT, 0, 0);
++ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_SETUP, 0, 0);
++ atombios_dig_transmitter_setup(encoder, ATOM_TRANSMITTER_ACTION_ENABLE, 0, 0);
+ break;
+ case ENCODER_OBJECT_ID_INTERNAL_DDI:
+ atombios_ddia_setup(encoder, ATOM_ENABLE);
+@@ -1160,6 +1281,8 @@ radeon_atom_encoder_mode_set(struct drm_encoder *encoder,
+ break;
+ }
+ atombios_apply_encoder_quirks(encoder, adjusted_mode);
++
++ r600_hdmi_setmode(encoder, adjusted_mode);
+ }
+
+ static bool
+@@ -1266,7 +1389,13 @@ static void radeon_atom_encoder_commit(struct drm_encoder *encoder)
+ static void radeon_atom_encoder_disable(struct drm_encoder *encoder)
+ {
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
++ struct radeon_encoder_atom_dig *dig;
+ radeon_atom_encoder_dpms(encoder, DRM_MODE_DPMS_OFF);
++
++ if (radeon_encoder_is_digital(encoder)) {
++ dig = radeon_encoder->enc_priv;
++ dig->dig_encoder = -1;
++ }
+ radeon_encoder->active_device = 0;
+ }
+
+@@ -1323,6 +1452,7 @@ radeon_atombios_set_dig_info(struct radeon_encoder *radeon_encoder)
+
+ /* coherent mode by default */
+ dig->coherent_mode = true;
++ dig->dig_encoder = -1;
+
+ return dig;
+ }
+@@ -1354,7 +1484,6 @@ radeon_add_atom_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t su
+ encoder->possible_crtcs = 0x1;
+ else
+ encoder->possible_crtcs = 0x3;
+- encoder->possible_clones = 0;
+
+ radeon_encoder->enc_priv = NULL;
+
+@@ -1406,4 +1535,6 @@ radeon_add_atom_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t su
+ drm_encoder_helper_add(encoder, &radeon_atom_dig_helper_funcs);
+ break;
+ }
++
++ r600_hdmi_init(encoder);
+ }
+diff --git a/drivers/gpu/drm/radeon/radeon_fb.c b/drivers/gpu/drm/radeon/radeon_fb.c
+index b38c4c8..d71e346 100644
+--- a/drivers/gpu/drm/radeon/radeon_fb.c
++++ b/drivers/gpu/drm/radeon/radeon_fb.c
+@@ -59,7 +59,7 @@ static struct fb_ops radeonfb_ops = {
+ };
+
+ /**
+- * Curretly it is assumed that the old framebuffer is reused.
++ * Currently it is assumed that the old framebuffer is reused.
+ *
+ * LOCKING
+ * caller should hold the mode config lock.
+@@ -140,7 +140,7 @@ int radeonfb_create(struct drm_device *dev,
+ struct radeon_framebuffer *rfb;
+ struct drm_mode_fb_cmd mode_cmd;
+ struct drm_gem_object *gobj = NULL;
+- struct radeon_object *robj = NULL;
++ struct radeon_bo *rbo = NULL;
+ struct device *device = &rdev->pdev->dev;
+ int size, aligned_size, ret;
+ u64 fb_gpuaddr;
+@@ -168,14 +168,14 @@ int radeonfb_create(struct drm_device *dev,
+ ret = radeon_gem_object_create(rdev, aligned_size, 0,
+ RADEON_GEM_DOMAIN_VRAM,
+ false, ttm_bo_type_kernel,
+- false, &gobj);
++ &gobj);
+ if (ret) {
+ printk(KERN_ERR "failed to allocate framebuffer (%d %d)\n",
+ surface_width, surface_height);
+ ret = -ENOMEM;
+ goto out;
+ }
+- robj = gobj->driver_private;
++ rbo = gobj->driver_private;
+
+ if (fb_tiled)
+ tiling_flags = RADEON_TILING_MACRO;
+@@ -192,8 +192,13 @@ int radeonfb_create(struct drm_device *dev,
+ }
+ #endif
+
+- if (tiling_flags)
+- radeon_object_set_tiling_flags(robj, tiling_flags | RADEON_TILING_SURFACE, mode_cmd.pitch);
++ if (tiling_flags) {
++ ret = radeon_bo_set_tiling_flags(rbo,
++ tiling_flags | RADEON_TILING_SURFACE,
++ mode_cmd.pitch);
++ if (ret)
++ dev_err(rdev->dev, "FB failed to set tiling flags\n");
++ }
+ mutex_lock(&rdev->ddev->struct_mutex);
+ fb = radeon_framebuffer_create(rdev->ddev, &mode_cmd, gobj);
+ if (fb == NULL) {
+@@ -201,10 +206,19 @@ int radeonfb_create(struct drm_device *dev,
+ ret = -ENOMEM;
+ goto out_unref;
+ }
+- ret = radeon_object_pin(robj, RADEON_GEM_DOMAIN_VRAM, &fb_gpuaddr);
++ ret = radeon_bo_reserve(rbo, false);
++ if (unlikely(ret != 0))
++ goto out_unref;
++ ret = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &fb_gpuaddr);
++ if (ret) {
++ radeon_bo_unreserve(rbo);
++ goto out_unref;
++ }
++ if (fb_tiled)
++ radeon_bo_check_tiling(rbo, 0, 0);
++ ret = radeon_bo_kmap(rbo, &fbptr);
++ radeon_bo_unreserve(rbo);
+ if (ret) {
+- printk(KERN_ERR "failed to pin framebuffer\n");
+- ret = -ENOMEM;
+ goto out_unref;
+ }
+
+@@ -213,7 +227,7 @@ int radeonfb_create(struct drm_device *dev,
+ *fb_p = fb;
+ rfb = to_radeon_framebuffer(fb);
+ rdev->fbdev_rfb = rfb;
+- rdev->fbdev_robj = robj;
++ rdev->fbdev_rbo = rbo;
+
+ info = framebuffer_alloc(sizeof(struct radeon_fb_device), device);
+ if (info == NULL) {
+@@ -234,15 +248,7 @@ int radeonfb_create(struct drm_device *dev,
+ if (ret)
+ goto out_unref;
+
+- if (fb_tiled)
+- radeon_object_check_tiling(robj, 0, 0);
+-
+- ret = radeon_object_kmap(robj, &fbptr);
+- if (ret) {
+- goto out_unref;
+- }
+-
+- memset_io(fbptr, 0, aligned_size);
++ memset_io(fbptr, 0x0, aligned_size);
+
+ strcpy(info->fix.id, "radeondrmfb");
+
+@@ -288,8 +294,12 @@ int radeonfb_create(struct drm_device *dev,
+ return 0;
+
+ out_unref:
+- if (robj) {
+- radeon_object_kunmap(robj);
++ if (rbo) {
++ ret = radeon_bo_reserve(rbo, false);
++ if (likely(ret == 0)) {
++ radeon_bo_kunmap(rbo);
++ radeon_bo_unreserve(rbo);
++ }
+ }
+ if (fb && ret) {
+ list_del(&fb->filp_head);
+@@ -321,14 +331,22 @@ int radeon_parse_options(char *options)
+
+ int radeonfb_probe(struct drm_device *dev)
+ {
+- return drm_fb_helper_single_fb_probe(dev, 32, &radeonfb_create);
++ struct radeon_device *rdev = dev->dev_private;
++ int bpp_sel = 32;
++
++ /* select 8 bpp console on RN50 or 16MB cards */
++ if (ASIC_IS_RN50(rdev) || rdev->mc.real_vram_size <= (32*1024*1024))
++ bpp_sel = 8;
++
++ return drm_fb_helper_single_fb_probe(dev, bpp_sel, &radeonfb_create);
+ }
+
+ int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
+ {
+ struct fb_info *info;
+ struct radeon_framebuffer *rfb = to_radeon_framebuffer(fb);
+- struct radeon_object *robj;
++ struct radeon_bo *rbo;
++ int r;
+
+ if (!fb) {
+ return -EINVAL;
+@@ -336,10 +354,14 @@ int radeonfb_remove(struct drm_device *dev, struct drm_framebuffer *fb)
+ info = fb->fbdev;
+ if (info) {
+ struct radeon_fb_device *rfbdev = info->par;
+- robj = rfb->obj->driver_private;
++ rbo = rfb->obj->driver_private;
+ unregister_framebuffer(info);
+- radeon_object_kunmap(robj);
+- radeon_object_unpin(robj);
++ r = radeon_bo_reserve(rbo, false);
++ if (likely(r == 0)) {
++ radeon_bo_kunmap(rbo);
++ radeon_bo_unpin(rbo);
++ radeon_bo_unreserve(rbo);
++ }
+ drm_fb_helper_free(&rfbdev->helper);
+ framebuffer_release(info);
+ }
+diff --git a/drivers/gpu/drm/radeon/radeon_fence.c b/drivers/gpu/drm/radeon/radeon_fence.c
+index 3beb26d..8495d4e 100644
+--- a/drivers/gpu/drm/radeon/radeon_fence.c
++++ b/drivers/gpu/drm/radeon/radeon_fence.c
+@@ -140,16 +140,15 @@ int radeon_fence_create(struct radeon_device *rdev, struct radeon_fence **fence)
+
+ bool radeon_fence_signaled(struct radeon_fence *fence)
+ {
+- struct radeon_device *rdev = fence->rdev;
+ unsigned long irq_flags;
+ bool signaled = false;
+
+- if (rdev->gpu_lockup) {
++ if (!fence)
+ return true;
+- }
+- if (fence == NULL) {
++
++ if (fence->rdev->gpu_lockup)
+ return true;
+- }
++
+ write_lock_irqsave(&fence->rdev->fence_drv.lock, irq_flags);
+ signaled = fence->signaled;
+ /* if we are shuting down report all fence as signaled */
+@@ -168,37 +167,6 @@ bool radeon_fence_signaled(struct radeon_fence *fence)
+ return signaled;
+ }
+
+-int r600_fence_wait(struct radeon_fence *fence, bool intr, bool lazy)
+-{
+- struct radeon_device *rdev;
+- int ret = 0;
+-
+- rdev = fence->rdev;
+-
+- __set_current_state(intr ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
+-
+- while (1) {
+- if (radeon_fence_signaled(fence))
+- break;
+-
+- if (time_after_eq(jiffies, fence->timeout)) {
+- ret = -EBUSY;
+- break;
+- }
+-
+- if (lazy)
+- schedule_timeout(1);
+-
+- if (intr && signal_pending(current)) {
+- ret = -ERESTARTSYS;
+- break;
+- }
+- }
+- __set_current_state(TASK_RUNNING);
+- return ret;
+-}
+-
+-
+ int radeon_fence_wait(struct radeon_fence *fence, bool intr)
+ {
+ struct radeon_device *rdev;
+@@ -216,13 +184,6 @@ int radeon_fence_wait(struct radeon_fence *fence, bool intr)
+ return 0;
+ }
+
+- if (rdev->family >= CHIP_R600) {
+- r = r600_fence_wait(fence, intr, 0);
+- if (r == -ERESTARTSYS)
+- return -EBUSY;
+- return r;
+- }
+-
+ retry:
+ cur_jiffies = jiffies;
+ timeout = HZ / 100;
+@@ -231,14 +192,17 @@ retry:
+ }
+
+ if (intr) {
++ radeon_irq_kms_sw_irq_get(rdev);
+ r = wait_event_interruptible_timeout(rdev->fence_drv.queue,
+ radeon_fence_signaled(fence), timeout);
+- if (unlikely(r == -ERESTARTSYS)) {
+- return -EBUSY;
+- }
++ radeon_irq_kms_sw_irq_put(rdev);
++ if (unlikely(r < 0))
++ return r;
+ } else {
++ radeon_irq_kms_sw_irq_get(rdev);
+ r = wait_event_timeout(rdev->fence_drv.queue,
+ radeon_fence_signaled(fence), timeout);
++ radeon_irq_kms_sw_irq_put(rdev);
+ }
+ if (unlikely(!radeon_fence_signaled(fence))) {
+ if (unlikely(r == 0)) {
+@@ -359,7 +323,7 @@ int radeon_fence_driver_init(struct radeon_device *rdev)
+ write_lock_irqsave(&rdev->fence_drv.lock, irq_flags);
+ r = radeon_scratch_get(rdev, &rdev->fence_drv.scratch_reg);
+ if (r) {
+- DRM_ERROR("Fence failed to get a scratch register.");
++ dev_err(rdev->dev, "fence failed to get scratch register\n");
+ write_unlock_irqrestore(&rdev->fence_drv.lock, irq_flags);
+ return r;
+ }
+@@ -370,9 +334,10 @@ int radeon_fence_driver_init(struct radeon_device *rdev)
+ INIT_LIST_HEAD(&rdev->fence_drv.signaled);
+ rdev->fence_drv.count_timeout = 0;
+ init_waitqueue_head(&rdev->fence_drv.queue);
++ rdev->fence_drv.initialized = true;
+ write_unlock_irqrestore(&rdev->fence_drv.lock, irq_flags);
+ if (radeon_debugfs_fence_init(rdev)) {
+- DRM_ERROR("Failed to register debugfs file for fence !\n");
++ dev_err(rdev->dev, "fence debugfs file creation failed\n");
+ }
+ return 0;
+ }
+@@ -381,11 +346,13 @@ void radeon_fence_driver_fini(struct radeon_device *rdev)
+ {
+ unsigned long irq_flags;
+
++ if (!rdev->fence_drv.initialized)
++ return;
+ wake_up_all(&rdev->fence_drv.queue);
+ write_lock_irqsave(&rdev->fence_drv.lock, irq_flags);
+ radeon_scratch_free(rdev, rdev->fence_drv.scratch_reg);
+ write_unlock_irqrestore(&rdev->fence_drv.lock, irq_flags);
+- DRM_INFO("radeon: fence finalized\n");
++ rdev->fence_drv.initialized = false;
+ }
+
+
+diff --git a/drivers/gpu/drm/radeon/radeon_fixed.h b/drivers/gpu/drm/radeon/radeon_fixed.h
+index 90187d1..3d4d84e 100644
+--- a/drivers/gpu/drm/radeon/radeon_fixed.h
++++ b/drivers/gpu/drm/radeon/radeon_fixed.h
+@@ -38,6 +38,23 @@ typedef union rfixed {
+ #define fixed_init_half(A) { .full = rfixed_const_half((A)) }
+ #define rfixed_trunc(A) ((A).full >> 12)
+
++static inline u32 rfixed_floor(fixed20_12 A)
++{
++ u32 non_frac = rfixed_trunc(A);
++
++ return rfixed_const(non_frac);
++}
++
++static inline u32 rfixed_ceil(fixed20_12 A)
++{
++ u32 non_frac = rfixed_trunc(A);
++
++ if (A.full > rfixed_const(non_frac))
++ return rfixed_const(non_frac + 1);
++ else
++ return rfixed_const(non_frac);
++}
++
+ static inline u32 rfixed_div(fixed20_12 A, fixed20_12 B)
+ {
+ u64 tmp = ((u64)A.full << 13);
+diff --git a/drivers/gpu/drm/radeon/radeon_gart.c b/drivers/gpu/drm/radeon/radeon_gart.c
+index a68d756..e73d56e 100644
+--- a/drivers/gpu/drm/radeon/radeon_gart.c
++++ b/drivers/gpu/drm/radeon/radeon_gart.c
+@@ -78,11 +78,9 @@ int radeon_gart_table_vram_alloc(struct radeon_device *rdev)
+ int r;
+
+ if (rdev->gart.table.vram.robj == NULL) {
+- r = radeon_object_create(rdev, NULL,
+- rdev->gart.table_size,
+- true,
+- RADEON_GEM_DOMAIN_VRAM,
+- false, &rdev->gart.table.vram.robj);
++ r = radeon_bo_create(rdev, NULL, rdev->gart.table_size,
++ true, RADEON_GEM_DOMAIN_VRAM,
++ &rdev->gart.table.vram.robj);
+ if (r) {
+ return r;
+ }
+@@ -95,32 +93,38 @@ int radeon_gart_table_vram_pin(struct radeon_device *rdev)
+ uint64_t gpu_addr;
+ int r;
+
+- r = radeon_object_pin(rdev->gart.table.vram.robj,
+- RADEON_GEM_DOMAIN_VRAM, &gpu_addr);
+- if (r) {
+- radeon_object_unref(&rdev->gart.table.vram.robj);
++ r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
++ if (unlikely(r != 0))
+ return r;
+- }
+- r = radeon_object_kmap(rdev->gart.table.vram.robj,
+- (void **)&rdev->gart.table.vram.ptr);
++ r = radeon_bo_pin(rdev->gart.table.vram.robj,
++ RADEON_GEM_DOMAIN_VRAM, &gpu_addr);
+ if (r) {
+- radeon_object_unpin(rdev->gart.table.vram.robj);
+- radeon_object_unref(&rdev->gart.table.vram.robj);
+- DRM_ERROR("radeon: failed to map gart vram table.\n");
++ radeon_bo_unreserve(rdev->gart.table.vram.robj);
+ return r;
+ }
++ r = radeon_bo_kmap(rdev->gart.table.vram.robj,
++ (void **)&rdev->gart.table.vram.ptr);
++ if (r)
++ radeon_bo_unpin(rdev->gart.table.vram.robj);
++ radeon_bo_unreserve(rdev->gart.table.vram.robj);
+ rdev->gart.table_addr = gpu_addr;
+- return 0;
++ return r;
+ }
+
+ void radeon_gart_table_vram_free(struct radeon_device *rdev)
+ {
++ int r;
++
+ if (rdev->gart.table.vram.robj == NULL) {
+ return;
+ }
+- radeon_object_kunmap(rdev->gart.table.vram.robj);
+- radeon_object_unpin(rdev->gart.table.vram.robj);
+- radeon_object_unref(&rdev->gart.table.vram.robj);
++ r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
++ if (likely(r == 0)) {
++ radeon_bo_kunmap(rdev->gart.table.vram.robj);
++ radeon_bo_unpin(rdev->gart.table.vram.robj);
++ radeon_bo_unreserve(rdev->gart.table.vram.robj);
++ }
++ radeon_bo_unref(&rdev->gart.table.vram.robj);
+ }
+
+
+diff --git a/drivers/gpu/drm/radeon/radeon_gem.c b/drivers/gpu/drm/radeon/radeon_gem.c
+index d880edf..db8e9a3 100644
+--- a/drivers/gpu/drm/radeon/radeon_gem.c
++++ b/drivers/gpu/drm/radeon/radeon_gem.c
+@@ -38,22 +38,21 @@ int radeon_gem_object_init(struct drm_gem_object *obj)
+
+ void radeon_gem_object_free(struct drm_gem_object *gobj)
+ {
+- struct radeon_object *robj = gobj->driver_private;
++ struct radeon_bo *robj = gobj->driver_private;
+
+ gobj->driver_private = NULL;
+ if (robj) {
+- radeon_object_unref(&robj);
++ radeon_bo_unref(&robj);
+ }
+ }
+
+ int radeon_gem_object_create(struct radeon_device *rdev, int size,
+- int alignment, int initial_domain,
+- bool discardable, bool kernel,
+- bool interruptible,
+- struct drm_gem_object **obj)
++ int alignment, int initial_domain,
++ bool discardable, bool kernel,
++ struct drm_gem_object **obj)
+ {
+ struct drm_gem_object *gobj;
+- struct radeon_object *robj;
++ struct radeon_bo *robj;
+ int r;
+
+ *obj = NULL;
+@@ -65,11 +64,11 @@ int radeon_gem_object_create(struct radeon_device *rdev, int size,
+ if (alignment < PAGE_SIZE) {
+ alignment = PAGE_SIZE;
+ }
+- r = radeon_object_create(rdev, gobj, size, kernel, initial_domain,
+- interruptible, &robj);
++ r = radeon_bo_create(rdev, gobj, size, kernel, initial_domain, &robj);
+ if (r) {
+- DRM_ERROR("Failed to allocate GEM object (%d, %d, %u)\n",
+- size, initial_domain, alignment);
++ if (r != -ERESTARTSYS)
++ DRM_ERROR("Failed to allocate GEM object (%d, %d, %u, %d)\n",
++ size, initial_domain, alignment, r);
+ mutex_lock(&rdev->ddev->struct_mutex);
+ drm_gem_object_unreference(gobj);
+ mutex_unlock(&rdev->ddev->struct_mutex);
+@@ -83,33 +82,33 @@ int radeon_gem_object_create(struct radeon_device *rdev, int size,
+ int radeon_gem_object_pin(struct drm_gem_object *obj, uint32_t pin_domain,
+ uint64_t *gpu_addr)
+ {
+- struct radeon_object *robj = obj->driver_private;
+- uint32_t flags;
++ struct radeon_bo *robj = obj->driver_private;
++ int r;
+
+- switch (pin_domain) {
+- case RADEON_GEM_DOMAIN_VRAM:
+- flags = TTM_PL_FLAG_VRAM;
+- break;
+- case RADEON_GEM_DOMAIN_GTT:
+- flags = TTM_PL_FLAG_TT;
+- break;
+- default:
+- flags = TTM_PL_FLAG_SYSTEM;
+- break;
+- }
+- return radeon_object_pin(robj, flags, gpu_addr);
++ r = radeon_bo_reserve(robj, false);
++ if (unlikely(r != 0))
++ return r;
++ r = radeon_bo_pin(robj, pin_domain, gpu_addr);
++ radeon_bo_unreserve(robj);
++ return r;
+ }
+
+ void radeon_gem_object_unpin(struct drm_gem_object *obj)
+ {
+- struct radeon_object *robj = obj->driver_private;
+- radeon_object_unpin(robj);
++ struct radeon_bo *robj = obj->driver_private;
++ int r;
++
++ r = radeon_bo_reserve(robj, false);
++ if (likely(r == 0)) {
++ radeon_bo_unpin(robj);
++ radeon_bo_unreserve(robj);
++ }
+ }
+
+ int radeon_gem_set_domain(struct drm_gem_object *gobj,
+ uint32_t rdomain, uint32_t wdomain)
+ {
+- struct radeon_object *robj;
++ struct radeon_bo *robj;
+ uint32_t domain;
+ int r;
+
+@@ -127,7 +126,7 @@ int radeon_gem_set_domain(struct drm_gem_object *gobj,
+ }
+ if (domain == RADEON_GEM_DOMAIN_CPU) {
+ /* Asking for cpu access wait for object idle */
+- r = radeon_object_wait(robj);
++ r = radeon_bo_wait(robj, NULL, false);
+ if (r) {
+ printk(KERN_ERR "Failed to wait for object !\n");
+ return r;
+@@ -144,7 +143,7 @@ int radeon_gem_init(struct radeon_device *rdev)
+
+ void radeon_gem_fini(struct radeon_device *rdev)
+ {
+- radeon_object_force_delete(rdev);
++ radeon_bo_force_delete(rdev);
+ }
+
+
+@@ -158,9 +157,13 @@ int radeon_gem_info_ioctl(struct drm_device *dev, void *data,
+ struct drm_radeon_gem_info *args = data;
+
+ args->vram_size = rdev->mc.real_vram_size;
+- /* FIXME: report somethings that makes sense */
+- args->vram_visible = rdev->mc.real_vram_size - (4 * 1024 * 1024);
+- args->gart_size = rdev->mc.gtt_size;
++ args->vram_visible = rdev->mc.real_vram_size;
++ if (rdev->stollen_vga_memory)
++ args->vram_visible -= radeon_bo_size(rdev->stollen_vga_memory);
++ if (rdev->fbdev_rbo)
++ args->vram_visible -= radeon_bo_size(rdev->fbdev_rbo);
++ args->gart_size = rdev->mc.gtt_size - rdev->cp.ring_size - 4096 -
++ RADEON_IB_POOL_SIZE*64*1024;
+ return 0;
+ }
+
+@@ -192,8 +195,8 @@ int radeon_gem_create_ioctl(struct drm_device *dev, void *data,
+ /* create a gem object to contain this object in */
+ args->size = roundup(args->size, PAGE_SIZE);
+ r = radeon_gem_object_create(rdev, args->size, args->alignment,
+- args->initial_domain, false,
+- false, true, &gobj);
++ args->initial_domain, false,
++ false, &gobj);
+ if (r) {
+ return r;
+ }
+@@ -218,7 +221,7 @@ int radeon_gem_set_domain_ioctl(struct drm_device *dev, void *data,
+ * just validate the BO into a certain domain */
+ struct drm_radeon_gem_set_domain *args = data;
+ struct drm_gem_object *gobj;
+- struct radeon_object *robj;
++ struct radeon_bo *robj;
+ int r;
+
+ /* for now if someone requests domain CPU -
+@@ -244,19 +247,18 @@ int radeon_gem_mmap_ioctl(struct drm_device *dev, void *data,
+ {
+ struct drm_radeon_gem_mmap *args = data;
+ struct drm_gem_object *gobj;
+- struct radeon_object *robj;
+- int r;
++ struct radeon_bo *robj;
+
+ gobj = drm_gem_object_lookup(dev, filp, args->handle);
+ if (gobj == NULL) {
+ return -EINVAL;
+ }
+ robj = gobj->driver_private;
+- r = radeon_object_mmap(robj, &args->addr_ptr);
++ args->addr_ptr = radeon_bo_mmap_offset(robj);
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(gobj);
+ mutex_unlock(&dev->struct_mutex);
+- return r;
++ return 0;
+ }
+
+ int radeon_gem_busy_ioctl(struct drm_device *dev, void *data,
+@@ -264,16 +266,16 @@ int radeon_gem_busy_ioctl(struct drm_device *dev, void *data,
+ {
+ struct drm_radeon_gem_busy *args = data;
+ struct drm_gem_object *gobj;
+- struct radeon_object *robj;
++ struct radeon_bo *robj;
+ int r;
+- uint32_t cur_placement;
++ uint32_t cur_placement = 0;
+
+ gobj = drm_gem_object_lookup(dev, filp, args->handle);
+ if (gobj == NULL) {
+ return -EINVAL;
+ }
+ robj = gobj->driver_private;
+- r = radeon_object_busy_domain(robj, &cur_placement);
++ r = radeon_bo_wait(robj, &cur_placement, true);
+ switch (cur_placement) {
+ case TTM_PL_VRAM:
+ args->domain = RADEON_GEM_DOMAIN_VRAM;
+@@ -297,7 +299,7 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data,
+ {
+ struct drm_radeon_gem_wait_idle *args = data;
+ struct drm_gem_object *gobj;
+- struct radeon_object *robj;
++ struct radeon_bo *robj;
+ int r;
+
+ gobj = drm_gem_object_lookup(dev, filp, args->handle);
+@@ -305,7 +307,10 @@ int radeon_gem_wait_idle_ioctl(struct drm_device *dev, void *data,
+ return -EINVAL;
+ }
+ robj = gobj->driver_private;
+- r = radeon_object_wait(robj);
++ r = radeon_bo_wait(robj, NULL, false);
++ /* callback hw specific functions if any */
++ if (robj->rdev->asic->ioctl_wait_idle)
++ robj->rdev->asic->ioctl_wait_idle(robj->rdev, robj);
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(gobj);
+ mutex_unlock(&dev->struct_mutex);
+@@ -317,7 +322,7 @@ int radeon_gem_set_tiling_ioctl(struct drm_device *dev, void *data,
+ {
+ struct drm_radeon_gem_set_tiling *args = data;
+ struct drm_gem_object *gobj;
+- struct radeon_object *robj;
++ struct radeon_bo *robj;
+ int r = 0;
+
+ DRM_DEBUG("%d \n", args->handle);
+@@ -325,7 +330,7 @@ int radeon_gem_set_tiling_ioctl(struct drm_device *dev, void *data,
+ if (gobj == NULL)
+ return -EINVAL;
+ robj = gobj->driver_private;
+- radeon_object_set_tiling_flags(robj, args->tiling_flags, args->pitch);
++ r = radeon_bo_set_tiling_flags(robj, args->tiling_flags, args->pitch);
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(gobj);
+ mutex_unlock(&dev->struct_mutex);
+@@ -337,16 +342,20 @@ int radeon_gem_get_tiling_ioctl(struct drm_device *dev, void *data,
+ {
+ struct drm_radeon_gem_get_tiling *args = data;
+ struct drm_gem_object *gobj;
+- struct radeon_object *robj;
++ struct radeon_bo *rbo;
+ int r = 0;
+
+ DRM_DEBUG("\n");
+ gobj = drm_gem_object_lookup(dev, filp, args->handle);
+ if (gobj == NULL)
+ return -EINVAL;
+- robj = gobj->driver_private;
+- radeon_object_get_tiling_flags(robj, &args->tiling_flags,
+- &args->pitch);
++ rbo = gobj->driver_private;
++ r = radeon_bo_reserve(rbo, false);
++ if (unlikely(r != 0))
++ goto out;
++ radeon_bo_get_tiling_flags(rbo, &args->tiling_flags, &args->pitch);
++ radeon_bo_unreserve(rbo);
++out:
+ mutex_lock(&dev->struct_mutex);
+ drm_gem_object_unreference(gobj);
+ mutex_unlock(&dev->struct_mutex);
+diff --git a/drivers/gpu/drm/radeon/radeon_i2c.c b/drivers/gpu/drm/radeon/radeon_i2c.c
+index dd438d3..da3da1e 100644
+--- a/drivers/gpu/drm/radeon/radeon_i2c.c
++++ b/drivers/gpu/drm/radeon/radeon_i2c.c
+@@ -59,35 +59,43 @@ bool radeon_ddc_probe(struct radeon_connector *radeon_connector)
+ }
+
+
+-void radeon_i2c_do_lock(struct radeon_connector *radeon_connector, int lock_state)
++void radeon_i2c_do_lock(struct radeon_i2c_chan *i2c, int lock_state)
+ {
+- struct radeon_device *rdev = radeon_connector->base.dev->dev_private;
++ struct radeon_device *rdev = i2c->dev->dev_private;
++ struct radeon_i2c_bus_rec *rec = &i2c->rec;
+ uint32_t temp;
+- struct radeon_i2c_bus_rec *rec = &radeon_connector->ddc_bus->rec;
+
+ /* RV410 appears to have a bug where the hw i2c in reset
+ * holds the i2c port in a bad state - switch hw i2c away before
+ * doing DDC - do this for all r200s/r300s/r400s for safety sake
+ */
+- if ((rdev->family >= CHIP_R200) && !ASIC_IS_AVIVO(rdev)) {
+- if (rec->a_clk_reg == RADEON_GPIO_MONID) {
+- WREG32(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST |
+- R200_DVI_I2C_PIN_SEL(R200_SEL_DDC1)));
+- } else {
+- WREG32(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST |
+- R200_DVI_I2C_PIN_SEL(R200_SEL_DDC3)));
++ if (rec->hw_capable) {
++ if ((rdev->family >= CHIP_R200) && !ASIC_IS_AVIVO(rdev)) {
++ if (rec->a_clk_reg == RADEON_GPIO_MONID) {
++ WREG32(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST |
++ R200_DVI_I2C_PIN_SEL(R200_SEL_DDC1)));
++ } else {
++ WREG32(RADEON_DVI_I2C_CNTL_0, (RADEON_I2C_SOFT_RST |
++ R200_DVI_I2C_PIN_SEL(R200_SEL_DDC3)));
++ }
+ }
+ }
+- if (lock_state) {
+- temp = RREG32(rec->a_clk_reg);
+- temp &= ~(rec->a_clk_mask);
+- WREG32(rec->a_clk_reg, temp);
+-
+- temp = RREG32(rec->a_data_reg);
+- temp &= ~(rec->a_data_mask);
+- WREG32(rec->a_data_reg, temp);
+- }
+
++ /* clear the output pin values */
++ temp = RREG32(rec->a_clk_reg) & ~rec->a_clk_mask;
++ WREG32(rec->a_clk_reg, temp);
++
++ temp = RREG32(rec->a_data_reg) & ~rec->a_data_mask;
++ WREG32(rec->a_data_reg, temp);
++
++ /* set the pins to input */
++ temp = RREG32(rec->en_clk_reg) & ~rec->en_clk_mask;
++ WREG32(rec->en_clk_reg, temp);
++
++ temp = RREG32(rec->en_data_reg) & ~rec->en_data_mask;
++ WREG32(rec->en_data_reg, temp);
++
++ /* mask the gpio pins for software use */
+ temp = RREG32(rec->mask_clk_reg);
+ if (lock_state)
+ temp |= rec->mask_clk_mask;
+@@ -112,8 +120,9 @@ static int get_clock(void *i2c_priv)
+ struct radeon_i2c_bus_rec *rec = &i2c->rec;
+ uint32_t val;
+
+- val = RREG32(rec->get_clk_reg);
+- val &= rec->get_clk_mask;
++ /* read the value off the pin */
++ val = RREG32(rec->y_clk_reg);
++ val &= rec->y_clk_mask;
+
+ return (val != 0);
+ }
+@@ -126,8 +135,10 @@ static int get_data(void *i2c_priv)
+ struct radeon_i2c_bus_rec *rec = &i2c->rec;
+ uint32_t val;
+
+- val = RREG32(rec->get_data_reg);
+- val &= rec->get_data_mask;
++ /* read the value off the pin */
++ val = RREG32(rec->y_data_reg);
++ val &= rec->y_data_mask;
++
+ return (val != 0);
+ }
+
+@@ -138,9 +149,10 @@ static void set_clock(void *i2c_priv, int clock)
+ struct radeon_i2c_bus_rec *rec = &i2c->rec;
+ uint32_t val;
+
+- val = RREG32(rec->put_clk_reg) & (uint32_t)~(rec->put_clk_mask);
+- val |= clock ? 0 : rec->put_clk_mask;
+- WREG32(rec->put_clk_reg, val);
++ /* set pin direction */
++ val = RREG32(rec->en_clk_reg) & ~rec->en_clk_mask;
++ val |= clock ? 0 : rec->en_clk_mask;
++ WREG32(rec->en_clk_reg, val);
+ }
+
+ static void set_data(void *i2c_priv, int data)
+@@ -150,14 +162,15 @@ static void set_data(void *i2c_priv, int data)
+ struct radeon_i2c_bus_rec *rec = &i2c->rec;
+ uint32_t val;
+
+- val = RREG32(rec->put_data_reg) & (uint32_t)~(rec->put_data_mask);
+- val |= data ? 0 : rec->put_data_mask;
+- WREG32(rec->put_data_reg, val);
++ /* set pin direction */
++ val = RREG32(rec->en_data_reg) & ~rec->en_data_mask;
++ val |= data ? 0 : rec->en_data_mask;
++ WREG32(rec->en_data_reg, val);
+ }
+
+ struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
+- struct radeon_i2c_bus_rec *rec,
+- const char *name)
++ struct radeon_i2c_bus_rec *rec,
++ const char *name)
+ {
+ struct radeon_i2c_chan *i2c;
+ int ret;
+@@ -167,20 +180,19 @@ struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
+ return NULL;
+
+ i2c->adapter.owner = THIS_MODULE;
+- i2c->adapter.algo_data = &i2c->algo;
+ i2c->dev = dev;
+- i2c->algo.setsda = set_data;
+- i2c->algo.setscl = set_clock;
+- i2c->algo.getsda = get_data;
+- i2c->algo.getscl = get_clock;
+- i2c->algo.udelay = 20;
++ i2c_set_adapdata(&i2c->adapter, i2c);
++ i2c->adapter.algo_data = &i2c->algo.bit;
++ i2c->algo.bit.setsda = set_data;
++ i2c->algo.bit.setscl = set_clock;
++ i2c->algo.bit.getsda = get_data;
++ i2c->algo.bit.getscl = get_clock;
++ i2c->algo.bit.udelay = 20;
+ /* vesa says 2.2 ms is enough, 1 jiffy doesn't seem to always
+ * make this, 2 jiffies is a lot more reliable */
+- i2c->algo.timeout = 2;
+- i2c->algo.data = i2c;
++ i2c->algo.bit.timeout = 2;
++ i2c->algo.bit.data = i2c;
+ i2c->rec = *rec;
+- i2c_set_adapdata(&i2c->adapter, i2c);
+-
+ ret = i2c_bit_add_bus(&i2c->adapter);
+ if (ret) {
+ DRM_INFO("Failed to register i2c %s\n", name);
+@@ -194,6 +206,38 @@ out_free:
+
+ }
+
++struct radeon_i2c_chan *radeon_i2c_create_dp(struct drm_device *dev,
++ struct radeon_i2c_bus_rec *rec,
++ const char *name)
++{
++ struct radeon_i2c_chan *i2c;
++ int ret;
++
++ i2c = kzalloc(sizeof(struct radeon_i2c_chan), GFP_KERNEL);
++ if (i2c == NULL)
++ return NULL;
++
++ i2c->rec = *rec;
++ i2c->adapter.owner = THIS_MODULE;
++ i2c->dev = dev;
++ i2c_set_adapdata(&i2c->adapter, i2c);
++ i2c->adapter.algo_data = &i2c->algo.dp;
++ i2c->algo.dp.aux_ch = radeon_dp_i2c_aux_ch;
++ i2c->algo.dp.address = 0;
++ ret = i2c_dp_aux_add_bus(&i2c->adapter);
++ if (ret) {
++ DRM_INFO("Failed to register i2c %s\n", name);
++ goto out_free;
++ }
++
++ return i2c;
++out_free:
++ kfree(i2c);
++ return NULL;
++
++}
++
++
+ void radeon_i2c_destroy(struct radeon_i2c_chan *i2c)
+ {
+ if (!i2c)
+@@ -207,3 +251,59 @@ struct drm_encoder *radeon_best_encoder(struct drm_connector *connector)
+ {
+ return NULL;
+ }
++
++void radeon_i2c_sw_get_byte(struct radeon_i2c_chan *i2c_bus,
++ u8 slave_addr,
++ u8 addr,
++ u8 *val)
++{
++ u8 out_buf[2];
++ u8 in_buf[2];
++ struct i2c_msg msgs[] = {
++ {
++ .addr = slave_addr,
++ .flags = 0,
++ .len = 1,
++ .buf = out_buf,
++ },
++ {
++ .addr = slave_addr,
++ .flags = I2C_M_RD,
++ .len = 1,
++ .buf = in_buf,
++ }
++ };
++
++ out_buf[0] = addr;
++ out_buf[1] = 0;
++
++ if (i2c_transfer(&i2c_bus->adapter, msgs, 2) == 2) {
++ *val = in_buf[0];
++ DRM_DEBUG("val = 0x%02x\n", *val);
++ } else {
++ DRM_ERROR("i2c 0x%02x 0x%02x read failed\n",
++ addr, *val);
++ }
++}
++
++void radeon_i2c_sw_put_byte(struct radeon_i2c_chan *i2c_bus,
++ u8 slave_addr,
++ u8 addr,
++ u8 val)
++{
++ uint8_t out_buf[2];
++ struct i2c_msg msg = {
++ .addr = slave_addr,
++ .flags = 0,
++ .len = 2,
++ .buf = out_buf,
++ };
++
++ out_buf[0] = addr;
++ out_buf[1] = val;
++
++ if (i2c_transfer(&i2c_bus->adapter, &msg, 1) != 1)
++ DRM_ERROR("i2c 0x%02x 0x%02x write failed\n",
++ addr, val);
++}
++
+diff --git a/drivers/gpu/drm/radeon/radeon_ioc32.c b/drivers/gpu/drm/radeon/radeon_ioc32.c
+index a1bf11d..48b7cea 100644
+--- a/drivers/gpu/drm/radeon/radeon_ioc32.c
++++ b/drivers/gpu/drm/radeon/radeon_ioc32.c
+@@ -92,8 +92,7 @@ static int compat_radeon_cp_init(struct file *file, unsigned int cmd,
+ &init->gart_textures_offset))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_RADEON_CP_INIT, (unsigned long)init);
++ return drm_ioctl(file, DRM_IOCTL_RADEON_CP_INIT, (unsigned long)init);
+ }
+
+ typedef struct drm_radeon_clear32 {
+@@ -125,8 +124,7 @@ static int compat_radeon_cp_clear(struct file *file, unsigned int cmd,
+ &clr->depth_boxes))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_RADEON_CLEAR, (unsigned long)clr);
++ return drm_ioctl(file, DRM_IOCTL_RADEON_CLEAR, (unsigned long)clr);
+ }
+
+ typedef struct drm_radeon_stipple32 {
+@@ -149,8 +147,7 @@ static int compat_radeon_cp_stipple(struct file *file, unsigned int cmd,
+ &request->mask))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_RADEON_STIPPLE, (unsigned long)request);
++ return drm_ioctl(file, DRM_IOCTL_RADEON_STIPPLE, (unsigned long)request);
+ }
+
+ typedef struct drm_radeon_tex_image32 {
+@@ -204,8 +201,7 @@ static int compat_radeon_cp_texture(struct file *file, unsigned int cmd,
+ &image->data))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_RADEON_TEXTURE, (unsigned long)request);
++ return drm_ioctl(file, DRM_IOCTL_RADEON_TEXTURE, (unsigned long)request);
+ }
+
+ typedef struct drm_radeon_vertex2_32 {
+@@ -238,8 +234,7 @@ static int compat_radeon_cp_vertex2(struct file *file, unsigned int cmd,
+ &request->prim))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_RADEON_VERTEX2, (unsigned long)request);
++ return drm_ioctl(file, DRM_IOCTL_RADEON_VERTEX2, (unsigned long)request);
+ }
+
+ typedef struct drm_radeon_cmd_buffer32 {
+@@ -268,8 +263,7 @@ static int compat_radeon_cp_cmdbuf(struct file *file, unsigned int cmd,
+ &request->boxes))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_RADEON_CMDBUF, (unsigned long)request);
++ return drm_ioctl(file, DRM_IOCTL_RADEON_CMDBUF, (unsigned long)request);
+ }
+
+ typedef struct drm_radeon_getparam32 {
+@@ -293,8 +287,7 @@ static int compat_radeon_cp_getparam(struct file *file, unsigned int cmd,
+ &request->value))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_RADEON_GETPARAM, (unsigned long)request);
++ return drm_ioctl(file, DRM_IOCTL_RADEON_GETPARAM, (unsigned long)request);
+ }
+
+ typedef struct drm_radeon_mem_alloc32 {
+@@ -322,8 +315,7 @@ static int compat_radeon_mem_alloc(struct file *file, unsigned int cmd,
+ &request->region_offset))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_RADEON_ALLOC, (unsigned long)request);
++ return drm_ioctl(file, DRM_IOCTL_RADEON_ALLOC, (unsigned long)request);
+ }
+
+ typedef struct drm_radeon_irq_emit32 {
+@@ -345,8 +337,7 @@ static int compat_radeon_irq_emit(struct file *file, unsigned int cmd,
+ &request->irq_seq))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_path.dentry->d_inode, file,
+- DRM_IOCTL_RADEON_IRQ_EMIT, (unsigned long)request);
++ return drm_ioctl(file, DRM_IOCTL_RADEON_IRQ_EMIT, (unsigned long)request);
+ }
+
+ /* The two 64-bit arches where alignof(u64)==4 in 32-bit code */
+@@ -372,8 +363,7 @@ static int compat_radeon_cp_setparam(struct file *file, unsigned int cmd,
+ &request->value))
+ return -EFAULT;
+
+- return drm_ioctl(file->f_dentry->d_inode, file,
+- DRM_IOCTL_RADEON_SETPARAM, (unsigned long) request);
++ return drm_ioctl(file, DRM_IOCTL_RADEON_SETPARAM, (unsigned long) request);
+ }
+ #else
+ #define compat_radeon_cp_setparam NULL
+@@ -413,12 +403,10 @@ long radeon_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+ if (nr < DRM_COMMAND_BASE + DRM_ARRAY_SIZE(radeon_compat_ioctls))
+ fn = radeon_compat_ioctls[nr - DRM_COMMAND_BASE];
+
+- lock_kernel(); /* XXX for now */
+ if (fn != NULL)
+ ret = (*fn) (filp, cmd, arg);
+ else
+- ret = drm_ioctl(filp->f_path.dentry->d_inode, filp, cmd, arg);
+- unlock_kernel();
++ ret = drm_ioctl(filp, cmd, arg);
+
+ return ret;
+ }
+@@ -431,9 +419,7 @@ long radeon_kms_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long
+ if (nr < DRM_COMMAND_BASE)
+ return drm_compat_ioctl(filp, cmd, arg);
+
+- lock_kernel(); /* XXX for now */
+- ret = drm_ioctl(filp->f_path.dentry->d_inode, filp, cmd, arg);
+- unlock_kernel();
++ ret = drm_ioctl(filp, cmd, arg);
+
+ return ret;
+ }
+diff --git a/drivers/gpu/drm/radeon/radeon_irq.c b/drivers/gpu/drm/radeon/radeon_irq.c
+index b79ecc4..2f349a3 100644
+--- a/drivers/gpu/drm/radeon/radeon_irq.c
++++ b/drivers/gpu/drm/radeon/radeon_irq.c
+@@ -289,16 +289,16 @@ int radeon_irq_emit(struct drm_device *dev, void *data, struct drm_file *file_pr
+ drm_radeon_irq_emit_t *emit = data;
+ int result;
+
+- if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
+- return -EINVAL;
+-
+- LOCK_TEST_WITH_RETURN(dev, file_priv);
+-
+ if (!dev_priv) {
+ DRM_ERROR("called with no initialization\n");
+ return -EINVAL;
+ }
+
++ if ((dev_priv->flags & RADEON_FAMILY_MASK) >= CHIP_R600)
++ return -EINVAL;
++
++ LOCK_TEST_WITH_RETURN(dev, file_priv);
++
+ result = radeon_emit_irq(dev);
+
+ if (DRM_COPY_TO_USER(emit->irq_seq, &result, sizeof(int))) {
+diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c
+index a0fe623..3cfd60f 100644
+--- a/drivers/gpu/drm/radeon/radeon_irq_kms.c
++++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c
+@@ -39,11 +39,32 @@ irqreturn_t radeon_driver_irq_handler_kms(DRM_IRQ_ARGS)
+ return radeon_irq_process(rdev);
+ }
+
++/*
++ * Handle hotplug events outside the interrupt handler proper.
++ */
++static void radeon_hotplug_work_func(struct work_struct *work)
++{
++ struct radeon_device *rdev = container_of(work, struct radeon_device,
++ hotplug_work);
++ struct drm_device *dev = rdev->ddev;
++ struct drm_mode_config *mode_config = &dev->mode_config;
++ struct drm_connector *connector;
++
++ if (mode_config->num_connector) {
++ list_for_each_entry(connector, &mode_config->connector_list, head)
++ radeon_connector_hotplug(connector);
++ }
++ /* Just fire off a uevent and let userspace tell us what to do */
++ drm_sysfs_hotplug_event(dev);
++}
++
+ void radeon_driver_irq_preinstall_kms(struct drm_device *dev)
+ {
+ struct radeon_device *rdev = dev->dev_private;
+ unsigned i;
+
++ INIT_WORK(&rdev->hotplug_work, radeon_hotplug_work_func);
++
+ /* Disable *all* interrupts */
+ rdev->irq.sw_int = false;
+ for (i = 0; i < 2; i++) {
+@@ -76,6 +97,7 @@ void radeon_driver_irq_uninstall_kms(struct drm_device *dev)
+ rdev->irq.sw_int = false;
+ for (i = 0; i < 2; i++) {
+ rdev->irq.crtc_vblank_int[i] = false;
++ rdev->irq.hpd[i] = false;
+ }
+ radeon_irq_set(rdev);
+ }
+@@ -87,30 +109,69 @@ int radeon_irq_kms_init(struct radeon_device *rdev)
+
+ if (rdev->flags & RADEON_SINGLE_CRTC)
+ num_crtc = 1;
+-
++ spin_lock_init(&rdev->irq.sw_lock);
+ r = drm_vblank_init(rdev->ddev, num_crtc);
+ if (r) {
+ return r;
+ }
+ /* enable msi */
+ rdev->msi_enabled = 0;
+- if (rdev->family >= CHIP_RV380) {
++ /* MSIs don't seem to work on my rs780;
++ * not sure about rs880 or other rs780s.
++ * Needs more investigation.
++ */
++ if ((rdev->family >= CHIP_RV380) &&
++ (rdev->family != CHIP_RS780) &&
++ (rdev->family != CHIP_RS880)) {
+ int ret = pci_enable_msi(rdev->pdev);
+- if (!ret)
++ if (!ret) {
+ rdev->msi_enabled = 1;
++ DRM_INFO("radeon: using MSI.\n");
++ }
+ }
+- drm_irq_install(rdev->ddev);
+ rdev->irq.installed = true;
++ r = drm_irq_install(rdev->ddev);
++ if (r) {
++ rdev->irq.installed = false;
++ return r;
++ }
+ DRM_INFO("radeon: irq initialized.\n");
+ return 0;
+ }
+
+ void radeon_irq_kms_fini(struct radeon_device *rdev)
+ {
++ drm_vblank_cleanup(rdev->ddev);
+ if (rdev->irq.installed) {
+- rdev->irq.installed = false;
+ drm_irq_uninstall(rdev->ddev);
++ rdev->irq.installed = false;
+ if (rdev->msi_enabled)
+ pci_disable_msi(rdev->pdev);
+ }
+ }
++
++void radeon_irq_kms_sw_irq_get(struct radeon_device *rdev)
++{
++ unsigned long irqflags;
++
++ spin_lock_irqsave(&rdev->irq.sw_lock, irqflags);
++ if (rdev->ddev->irq_enabled && (++rdev->irq.sw_refcount == 1)) {
++ rdev->irq.sw_int = true;
++ radeon_irq_set(rdev);
++ }
++ spin_unlock_irqrestore(&rdev->irq.sw_lock, irqflags);
++}
++
++void radeon_irq_kms_sw_irq_put(struct radeon_device *rdev)
++{
++ unsigned long irqflags;
++
++ spin_lock_irqsave(&rdev->irq.sw_lock, irqflags);
++ BUG_ON(rdev->ddev->irq_enabled && rdev->irq.sw_refcount <= 0);
++ if (rdev->ddev->irq_enabled && (--rdev->irq.sw_refcount == 0)) {
++ rdev->irq.sw_int = false;
++ radeon_irq_set(rdev);
++ }
++ spin_unlock_irqrestore(&rdev->irq.sw_lock, irqflags);
++}
++
+diff --git a/drivers/gpu/drm/radeon/radeon_kms.c b/drivers/gpu/drm/radeon/radeon_kms.c
+index ba12862..f23b056 100644
+--- a/drivers/gpu/drm/radeon/radeon_kms.c
++++ b/drivers/gpu/drm/radeon/radeon_kms.c
+@@ -30,10 +30,19 @@
+ #include "radeon.h"
+ #include "radeon_drm.h"
+
++int radeon_driver_unload_kms(struct drm_device *dev)
++{
++ struct radeon_device *rdev = dev->dev_private;
++
++ if (rdev == NULL)
++ return 0;
++ radeon_modeset_fini(rdev);
++ radeon_device_fini(rdev);
++ kfree(rdev);
++ dev->dev_private = NULL;
++ return 0;
++}
+
+-/*
+- * Driver load/unload
+- */
+ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags)
+ {
+ struct radeon_device *rdev;
+@@ -62,31 +71,20 @@ int radeon_driver_load_kms(struct drm_device *dev, unsigned long flags)
+ */
+ r = radeon_device_init(rdev, dev, dev->pdev, flags);
+ if (r) {
+- DRM_ERROR("Fatal error while trying to initialize radeon.\n");
+- return r;
++ dev_err(&dev->pdev->dev, "Fatal error during GPU init\n");
++ goto out;
+ }
+ /* Again modeset_init should fail only on fatal error
+ * otherwise it should provide enough functionalities
+ * for shadowfb to run
+ */
+ r = radeon_modeset_init(rdev);
+- if (r) {
+- return r;
+- }
+- return 0;
+-}
+-
+-int radeon_driver_unload_kms(struct drm_device *dev)
+-{
+- struct radeon_device *rdev = dev->dev_private;
+-
+- if (rdev == NULL)
+- return 0;
+- radeon_modeset_fini(rdev);
+- radeon_device_fini(rdev);
+- kfree(rdev);
+- dev->dev_private = NULL;
+- return 0;
++ if (r)
++ dev_err(&dev->pdev->dev, "Fatal error during modeset init\n");
++out:
++ if (r)
++ radeon_driver_unload_kms(dev);
++ return r;
+ }
+
+
+diff --git a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
+index 22ce4d6..d6d69bb 100644
+--- a/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
++++ b/drivers/gpu/drm/radeon/radeon_legacy_crtc.c
+@@ -30,9 +30,20 @@
+ #include "radeon.h"
+ #include "atom.h"
+
++static void radeon_overscan_setup(struct drm_crtc *crtc,
++ struct drm_display_mode *mode)
++{
++ struct drm_device *dev = crtc->dev;
++ struct radeon_device *rdev = dev->dev_private;
++ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
++
++ WREG32(RADEON_OVR_CLR + radeon_crtc->crtc_offset, 0);
++ WREG32(RADEON_OVR_WID_LEFT_RIGHT + radeon_crtc->crtc_offset, 0);
++ WREG32(RADEON_OVR_WID_TOP_BOTTOM + radeon_crtc->crtc_offset, 0);
++}
++
+ static void radeon_legacy_rmx_mode_set(struct drm_crtc *crtc,
+- struct drm_display_mode *mode,
+- struct drm_display_mode *adjusted_mode)
++ struct drm_display_mode *mode)
+ {
+ struct drm_device *dev = crtc->dev;
+ struct radeon_device *rdev = dev->dev_private;
+@@ -328,69 +339,6 @@ void radeon_crtc_dpms(struct drm_crtc *crtc, int mode)
+ }
+ }
+
+-/* properly set crtc bpp when using atombios */
+-void radeon_legacy_atom_set_surface(struct drm_crtc *crtc)
+-{
+- struct drm_device *dev = crtc->dev;
+- struct radeon_device *rdev = dev->dev_private;
+- struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+- int format;
+- uint32_t crtc_gen_cntl;
+- uint32_t disp_merge_cntl;
+- uint32_t crtc_pitch;
+-
+- switch (crtc->fb->bits_per_pixel) {
+- case 8:
+- format = 2;
+- break;
+- case 15: /* 555 */
+- format = 3;
+- break;
+- case 16: /* 565 */
+- format = 4;
+- break;
+- case 24: /* RGB */
+- format = 5;
+- break;
+- case 32: /* xRGB */
+- format = 6;
+- break;
+- default:
+- return;
+- }
+-
+- crtc_pitch = ((((crtc->fb->pitch / (crtc->fb->bits_per_pixel / 8)) * crtc->fb->bits_per_pixel) +
+- ((crtc->fb->bits_per_pixel * 8) - 1)) /
+- (crtc->fb->bits_per_pixel * 8));
+- crtc_pitch |= crtc_pitch << 16;
+-
+- WREG32(RADEON_CRTC_PITCH + radeon_crtc->crtc_offset, crtc_pitch);
+-
+- switch (radeon_crtc->crtc_id) {
+- case 0:
+- disp_merge_cntl = RREG32(RADEON_DISP_MERGE_CNTL);
+- disp_merge_cntl &= ~RADEON_DISP_RGB_OFFSET_EN;
+- WREG32(RADEON_DISP_MERGE_CNTL, disp_merge_cntl);
+-
+- crtc_gen_cntl = RREG32(RADEON_CRTC_GEN_CNTL) & 0xfffff0ff;
+- crtc_gen_cntl |= (format << 8);
+- crtc_gen_cntl |= RADEON_CRTC_EXT_DISP_EN;
+- WREG32(RADEON_CRTC_GEN_CNTL, crtc_gen_cntl);
+- break;
+- case 1:
+- disp_merge_cntl = RREG32(RADEON_DISP2_MERGE_CNTL);
+- disp_merge_cntl &= ~RADEON_DISP2_RGB_OFFSET_EN;
+- WREG32(RADEON_DISP2_MERGE_CNTL, disp_merge_cntl);
+-
+- crtc_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL) & 0xfffff0ff;
+- crtc_gen_cntl |= (format << 8);
+- WREG32(RADEON_CRTC2_GEN_CNTL, crtc_gen_cntl);
+- WREG32(RADEON_FP_H2_SYNC_STRT_WID, RREG32(RADEON_CRTC2_H_SYNC_STRT_WID));
+- WREG32(RADEON_FP_V2_SYNC_STRT_WID, RREG32(RADEON_CRTC2_V_SYNC_STRT_WID));
+- break;
+- }
+-}
+-
+ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb)
+ {
+@@ -399,14 +347,21 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
+ struct radeon_crtc *radeon_crtc = to_radeon_crtc(crtc);
+ struct radeon_framebuffer *radeon_fb;
+ struct drm_gem_object *obj;
++ struct radeon_bo *rbo;
+ uint64_t base;
+ uint32_t crtc_offset, crtc_offset_cntl, crtc_tile_x0_y0 = 0;
+ uint32_t crtc_pitch, pitch_pixels;
+ uint32_t tiling_flags;
+ int format;
+ uint32_t gen_cntl_reg, gen_cntl_val;
++ int r;
+
+ DRM_DEBUG("\n");
++ /* no fb bound */
++ if (!crtc->fb) {
++ DRM_DEBUG("No FB bound\n");
++ return 0;
++ }
+
+ radeon_fb = to_radeon_framebuffer(crtc->fb);
+
+@@ -430,10 +385,22 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
+ return false;
+ }
+
++ /* Pin framebuffer & get tilling informations */
+ obj = radeon_fb->obj;
+- if (radeon_gem_object_pin(obj, RADEON_GEM_DOMAIN_VRAM, &base)) {
++ rbo = obj->driver_private;
++ r = radeon_bo_reserve(rbo, false);
++ if (unlikely(r != 0))
++ return r;
++ r = radeon_bo_pin(rbo, RADEON_GEM_DOMAIN_VRAM, &base);
++ if (unlikely(r != 0)) {
++ radeon_bo_unreserve(rbo);
+ return -EINVAL;
+ }
++ radeon_bo_get_tiling_flags(rbo, &tiling_flags, NULL);
++ radeon_bo_unreserve(rbo);
++ if (tiling_flags & RADEON_TILING_MICRO)
++ DRM_ERROR("trying to scanout microtiled buffer\n");
++
+ /* if scanout was in GTT this really wouldn't work */
+ /* crtc offset is from display base addr not FB location */
+ radeon_crtc->legacy_display_base_addr = rdev->mc.vram_location;
+@@ -448,10 +415,6 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
+ (crtc->fb->bits_per_pixel * 8));
+ crtc_pitch |= crtc_pitch << 16;
+
+- radeon_object_get_tiling_flags(obj->driver_private,
+- &tiling_flags, NULL);
+- if (tiling_flags & RADEON_TILING_MICRO)
+- DRM_ERROR("trying to scanout microtiled buffer\n");
+
+ if (tiling_flags & RADEON_TILING_MACRO) {
+ if (ASIC_IS_R300(rdev))
+@@ -529,7 +492,12 @@ int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
+
+ if (old_fb && old_fb != crtc->fb) {
+ radeon_fb = to_radeon_framebuffer(old_fb);
+- radeon_gem_object_unpin(radeon_fb->obj);
++ rbo = radeon_fb->obj->driver_private;
++ r = radeon_bo_reserve(rbo, false);
++ if (unlikely(r != 0))
++ return r;
++ radeon_bo_unpin(rbo);
++ radeon_bo_unreserve(rbo);
+ }
+
+ /* Bytes per pixel may have changed */
+@@ -614,39 +582,12 @@ static bool radeon_set_crtc_timing(struct drm_crtc *crtc, struct drm_display_mod
+ ? RADEON_CRTC_V_SYNC_POL
+ : 0));
+
+- /* TODO -> Dell Server */
+- if (0) {
+- uint32_t disp_hw_debug = RREG32(RADEON_DISP_HW_DEBUG);
+- uint32_t tv_dac_cntl = RREG32(RADEON_TV_DAC_CNTL);
+- uint32_t dac2_cntl = RREG32(RADEON_DAC_CNTL2);
+- uint32_t crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL);
+-
+- dac2_cntl &= ~RADEON_DAC2_DAC_CLK_SEL;
+- dac2_cntl |= RADEON_DAC2_DAC2_CLK_SEL;
+-
+- /* For CRT on DAC2, don't turn it on if BIOS didn't
+- enable it, even it's detected.
+- */
+- disp_hw_debug |= RADEON_CRT2_DISP1_SEL;
+- tv_dac_cntl &= ~((1<<2) | (3<<8) | (7<<24) | (0xff<<16));
+- tv_dac_cntl |= (0x03 | (2<<8) | (0x58<<16));
+-
+- WREG32(RADEON_TV_DAC_CNTL, tv_dac_cntl);
+- WREG32(RADEON_DISP_HW_DEBUG, disp_hw_debug);
+- WREG32(RADEON_DAC_CNTL2, dac2_cntl);
+- WREG32(RADEON_CRTC2_GEN_CNTL, crtc2_gen_cntl);
+- }
+-
+ if (radeon_crtc->crtc_id) {
+ uint32_t crtc2_gen_cntl;
+ uint32_t disp2_merge_cntl;
+
+- /* check to see if TV DAC is enabled for another crtc and keep it enabled */
+- if (RREG32(RADEON_CRTC2_GEN_CNTL) & RADEON_CRTC2_CRT2_ON)
+- crtc2_gen_cntl = RADEON_CRTC2_CRT2_ON;
+- else
+- crtc2_gen_cntl = 0;
+-
++ /* if TV DAC is enabled for another crtc and keep it enabled */
++ crtc2_gen_cntl = RREG32(RADEON_CRTC2_GEN_CNTL) & 0x00718080;
+ crtc2_gen_cntl |= ((format << 8)
+ | RADEON_CRTC2_VSYNC_DIS
+ | RADEON_CRTC2_HSYNC_DIS
+@@ -675,7 +616,8 @@ static bool radeon_set_crtc_timing(struct drm_crtc *crtc, struct drm_display_mod
+ uint32_t crtc_ext_cntl;
+ uint32_t disp_merge_cntl;
+
+- crtc_gen_cntl = (RADEON_CRTC_EXT_DISP_EN
++ crtc_gen_cntl = RREG32(RADEON_CRTC_GEN_CNTL) & 0x00718000;
++ crtc_gen_cntl |= (RADEON_CRTC_EXT_DISP_EN
+ | (format << 8)
+ | RADEON_CRTC_DISP_REQ_EN_B
+ | ((mode->flags & DRM_MODE_FLAG_DBLSCAN)
+@@ -727,7 +669,6 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
+ uint32_t post_divider = 0;
+ uint32_t freq = 0;
+ uint8_t pll_gain;
+- int pll_flags = RADEON_PLL_LEGACY;
+ bool use_bios_divs = false;
+ /* PLL registers */
+ uint32_t pll_ref_div = 0;
+@@ -761,10 +702,12 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
+ else
+ pll = &rdev->clock.p1pll;
+
++ pll->flags = RADEON_PLL_LEGACY;
++
+ if (mode->clock > 200000) /* range limits??? */
+- pll_flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
++ pll->flags |= RADEON_PLL_PREFER_HIGH_FB_DIV;
+ else
+- pll_flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
++ pll->flags |= RADEON_PLL_PREFER_LOW_REF_DIV;
+
+ list_for_each_entry(encoder, &dev->mode_config.encoder_list, head) {
+ if (encoder->crtc == crtc) {
+@@ -776,20 +719,22 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
+ }
+
+ if (encoder->encoder_type != DRM_MODE_ENCODER_DAC)
+- pll_flags |= RADEON_PLL_NO_ODD_POST_DIV;
++ pll->flags |= RADEON_PLL_NO_ODD_POST_DIV;
+ if (encoder->encoder_type == DRM_MODE_ENCODER_LVDS) {
+- struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+- struct radeon_encoder_lvds *lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv;
+- if (lvds) {
+- if (lvds->use_bios_dividers) {
+- pll_ref_div = lvds->panel_ref_divider;
+- pll_fb_post_div = (lvds->panel_fb_divider |
+- (lvds->panel_post_divider << 16));
+- htotal_cntl = 0;
+- use_bios_divs = true;
++ if (!rdev->is_atom_bios) {
++ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
++ struct radeon_encoder_lvds *lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv;
++ if (lvds) {
++ if (lvds->use_bios_dividers) {
++ pll_ref_div = lvds->panel_ref_divider;
++ pll_fb_post_div = (lvds->panel_fb_divider |
++ (lvds->panel_post_divider << 16));
++ htotal_cntl = 0;
++ use_bios_divs = true;
++ }
+ }
+ }
+- pll_flags |= RADEON_PLL_USE_REF_DIV;
++ pll->flags |= RADEON_PLL_USE_REF_DIV;
+ }
+ }
+ }
+@@ -799,8 +744,7 @@ static void radeon_set_pll(struct drm_crtc *crtc, struct drm_display_mode *mode)
+ if (!use_bios_divs) {
+ radeon_compute_pll(pll, mode->clock,
+ &freq, &feedback_div, &frac_fb_div,
+- &reference_div, &post_divider,
+- pll_flags);
++ &reference_div, &post_divider);
+
+ for (post_div = &post_divs[0]; post_div->divider; ++post_div) {
+ if (post_div->divider == post_divider)
+@@ -1026,8 +970,9 @@ static int radeon_crtc_mode_set(struct drm_crtc *crtc,
+ radeon_crtc_set_base(crtc, x, y, old_fb);
+ radeon_set_crtc_timing(crtc, adjusted_mode);
+ radeon_set_pll(crtc, adjusted_mode);
++ radeon_overscan_setup(crtc, adjusted_mode);
+ if (radeon_crtc->crtc_id == 0) {
+- radeon_legacy_rmx_mode_set(crtc, mode, adjusted_mode);
++ radeon_legacy_rmx_mode_set(crtc, adjusted_mode);
+ } else {
+ if (radeon_crtc->rmx_type != RMX_OFF) {
+ /* FIXME: only first crtc has rmx what should we
+@@ -1041,12 +986,29 @@ static int radeon_crtc_mode_set(struct drm_crtc *crtc,
+
+ static void radeon_crtc_prepare(struct drm_crtc *crtc)
+ {
+- radeon_crtc_dpms(crtc, DRM_MODE_DPMS_OFF);
++ struct drm_device *dev = crtc->dev;
++ struct drm_crtc *crtci;
++
++ /*
++ * The hardware wedges sometimes if you reconfigure one CRTC
++ * whilst another is running (see fdo bug #24611).
++ */
++ list_for_each_entry(crtci, &dev->mode_config.crtc_list, head)
++ radeon_crtc_dpms(crtci, DRM_MODE_DPMS_OFF);
+ }
+
+ static void radeon_crtc_commit(struct drm_crtc *crtc)
+ {
+- radeon_crtc_dpms(crtc, DRM_MODE_DPMS_ON);
++ struct drm_device *dev = crtc->dev;
++ struct drm_crtc *crtci;
++
++ /*
++ * Reenable the CRTCs that should be running.
++ */
++ list_for_each_entry(crtci, &dev->mode_config.crtc_list, head) {
++ if (crtci->enabled)
++ radeon_crtc_dpms(crtci, DRM_MODE_DPMS_ON);
++ }
+ }
+
+ static const struct drm_crtc_helper_funcs legacy_helper_funcs = {
+diff --git a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c
+index 0038212..38e45e2 100644
+--- a/drivers/gpu/drm/radeon/radeon_legacy_encoders.c
++++ b/drivers/gpu/drm/radeon/radeon_legacy_encoders.c
+@@ -46,6 +46,7 @@ static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode)
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+ uint32_t lvds_gen_cntl, lvds_pll_cntl, pixclks_cntl, disp_pwr_man;
+ int panel_pwr_delay = 2000;
++ bool is_mac = false;
+ DRM_DEBUG("\n");
+
+ if (radeon_encoder->enc_priv) {
+@@ -58,6 +59,15 @@ static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode)
+ }
+ }
+
++ /* macs (and possibly some x86 oem systems?) wire up LVDS strangely
++ * Taken from radeonfb.
++ */
++ if ((rdev->mode_info.connector_table == CT_IBOOK) ||
++ (rdev->mode_info.connector_table == CT_POWERBOOK_EXTERNAL) ||
++ (rdev->mode_info.connector_table == CT_POWERBOOK_INTERNAL) ||
++ (rdev->mode_info.connector_table == CT_POWERBOOK_VGA))
++ is_mac = true;
++
+ switch (mode) {
+ case DRM_MODE_DPMS_ON:
+ disp_pwr_man = RREG32(RADEON_DISP_PWR_MAN);
+@@ -74,6 +84,8 @@ static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode)
+
+ lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL);
+ lvds_gen_cntl |= (RADEON_LVDS_ON | RADEON_LVDS_EN | RADEON_LVDS_DIGON | RADEON_LVDS_BLON);
++ if (is_mac)
++ lvds_gen_cntl |= RADEON_LVDS_BL_MOD_EN;
+ lvds_gen_cntl &= ~(RADEON_LVDS_DISPLAY_DIS);
+ udelay(panel_pwr_delay * 1000);
+ WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl);
+@@ -85,7 +97,14 @@ static void radeon_legacy_lvds_dpms(struct drm_encoder *encoder, int mode)
+ WREG32_PLL_P(RADEON_PIXCLKS_CNTL, 0, ~RADEON_PIXCLK_LVDS_ALWAYS_ONb);
+ lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL);
+ lvds_gen_cntl |= RADEON_LVDS_DISPLAY_DIS;
+- lvds_gen_cntl &= ~(RADEON_LVDS_ON | RADEON_LVDS_BLON | RADEON_LVDS_EN | RADEON_LVDS_DIGON);
++ if (is_mac) {
++ lvds_gen_cntl &= ~RADEON_LVDS_BL_MOD_EN;
++ WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl);
++ lvds_gen_cntl &= ~(RADEON_LVDS_ON | RADEON_LVDS_EN);
++ } else {
++ WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl);
++ lvds_gen_cntl &= ~(RADEON_LVDS_ON | RADEON_LVDS_BLON | RADEON_LVDS_EN | RADEON_LVDS_DIGON);
++ }
+ udelay(panel_pwr_delay * 1000);
+ WREG32(RADEON_LVDS_GEN_CNTL, lvds_gen_cntl);
+ WREG32_PLL(RADEON_PIXCLKS_CNTL, pixclks_cntl);
+@@ -136,7 +155,14 @@ static void radeon_legacy_lvds_mode_set(struct drm_encoder *encoder,
+ lvds_pll_cntl &= ~RADEON_LVDS_PLL_EN;
+
+ lvds_ss_gen_cntl = RREG32(RADEON_LVDS_SS_GEN_CNTL);
+- if ((!rdev->is_atom_bios)) {
++ if (rdev->is_atom_bios) {
++ /* LVDS_GEN_CNTL parameters are computed in LVDSEncoderControl
++ * need to call that on resume to set up the reg properly.
++ */
++ radeon_encoder->pixel_clock = adjusted_mode->clock;
++ atombios_digital_setup(encoder, PANEL_ENCODER_ACTION_ENABLE);
++ lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL);
++ } else {
+ struct radeon_encoder_lvds *lvds = (struct radeon_encoder_lvds *)radeon_encoder->enc_priv;
+ if (lvds) {
+ DRM_DEBUG("bios LVDS_GEN_CNTL: 0x%x\n", lvds->lvds_gen_cntl);
+@@ -147,8 +173,7 @@ static void radeon_legacy_lvds_mode_set(struct drm_encoder *encoder,
+ (lvds->panel_blon_delay << RADEON_LVDS_PWRSEQ_DELAY2_SHIFT));
+ } else
+ lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL);
+- } else
+- lvds_gen_cntl = RREG32(RADEON_LVDS_GEN_CNTL);
++ }
+ lvds_gen_cntl |= RADEON_LVDS_DISPLAY_DIS;
+ lvds_gen_cntl &= ~(RADEON_LVDS_ON |
+ RADEON_LVDS_BLON |
+@@ -184,9 +209,9 @@ static void radeon_legacy_lvds_mode_set(struct drm_encoder *encoder,
+ radeon_combios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id);
+ }
+
+-static bool radeon_legacy_lvds_mode_fixup(struct drm_encoder *encoder,
+- struct drm_display_mode *mode,
+- struct drm_display_mode *adjusted_mode)
++static bool radeon_legacy_mode_fixup(struct drm_encoder *encoder,
++ struct drm_display_mode *mode,
++ struct drm_display_mode *adjusted_mode)
+ {
+ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
+
+@@ -194,15 +219,24 @@ static bool radeon_legacy_lvds_mode_fixup(struct drm_encoder *encoder,
+ radeon_encoder_set_active_device(encoder);
+ drm_mode_set_crtcinfo(adjusted_mode, 0);
+
+- if (radeon_encoder->rmx_type != RMX_OFF)
+- radeon_rmx_mode_fixup(encoder, mode, adjusted_mode);
++ /* get the native mode for LVDS */
++ if (radeon_encoder->active_device & (ATOM_DEVICE_LCD_SUPPORT)) {
++ struct drm_display_mode *native_mode = &radeon_encoder->native_mode;
++ int mode_id = adjusted_mode->base.id;
++ *adjusted_mode = *native_mode;
++ adjusted_mode->hdisplay = mode->hdisplay;
++ adjusted_mode->vdisplay = mode->vdisplay;
++ adjusted_mode->crtc_hdisplay = mode->hdisplay;
++ adjusted_mode->crtc_vdisplay = mode->vdisplay;
++ adjusted_mode->base.id = mode_id;
++ }
+
+ return true;
+ }
+
+ static const struct drm_encoder_helper_funcs radeon_legacy_lvds_helper_funcs = {
+ .dpms = radeon_legacy_lvds_dpms,
+- .mode_fixup = radeon_legacy_lvds_mode_fixup,
++ .mode_fixup = radeon_legacy_mode_fixup,
+ .prepare = radeon_legacy_lvds_prepare,
+ .mode_set = radeon_legacy_lvds_mode_set,
+ .commit = radeon_legacy_lvds_commit,
+@@ -214,17 +248,6 @@ static const struct drm_encoder_funcs radeon_legacy_lvds_enc_funcs = {
+ .destroy = radeon_enc_destroy,
+ };
+
+-static bool radeon_legacy_primary_dac_mode_fixup(struct drm_encoder *encoder,
+- struct drm_display_mode *mode,
+- struct drm_display_mode *adjusted_mode)
+-{
+- /* set the active encoder to connector routing */
+- radeon_encoder_set_active_device(encoder);
+- drm_mode_set_crtcinfo(adjusted_mode, 0);
+-
+- return true;
+-}
+-
+ static void radeon_legacy_primary_dac_dpms(struct drm_encoder *encoder, int mode)
+ {
+ struct drm_device *dev = encoder->dev;
+@@ -410,7 +433,7 @@ static enum drm_connector_status radeon_legacy_primary_dac_detect(struct drm_enc
+
+ static const struct drm_encoder_helper_funcs radeon_legacy_primary_dac_helper_funcs = {
+ .dpms = radeon_legacy_primary_dac_dpms,
+- .mode_fixup = radeon_legacy_primary_dac_mode_fixup,
++ .mode_fixup = radeon_legacy_mode_fixup,
+ .prepare = radeon_legacy_primary_dac_prepare,
+ .mode_set = radeon_legacy_primary_dac_mode_set,
+ .commit = radeon_legacy_primary_dac_commit,
+@@ -423,16 +446,6 @@ static const struct drm_encoder_funcs radeon_legacy_primary_dac_enc_funcs = {
+ .destroy = radeon_enc_destroy,
+ };
+
+-static bool radeon_legacy_tmds_int_mode_fixup(struct drm_encoder *encoder,
+- struct drm_display_mode *mode,
+- struct drm_display_mode *adjusted_mode)
+-{
+-
+- drm_mode_set_crtcinfo(adjusted_mode, 0);
+-
+- return true;
+-}
+-
+ static void radeon_legacy_tmds_int_dpms(struct drm_encoder *encoder, int mode)
+ {
+ struct drm_device *dev = encoder->dev;
+@@ -584,7 +597,7 @@ static void radeon_legacy_tmds_int_mode_set(struct drm_encoder *encoder,
+
+ static const struct drm_encoder_helper_funcs radeon_legacy_tmds_int_helper_funcs = {
+ .dpms = radeon_legacy_tmds_int_dpms,
+- .mode_fixup = radeon_legacy_tmds_int_mode_fixup,
++ .mode_fixup = radeon_legacy_mode_fixup,
+ .prepare = radeon_legacy_tmds_int_prepare,
+ .mode_set = radeon_legacy_tmds_int_mode_set,
+ .commit = radeon_legacy_tmds_int_commit,
+@@ -596,17 +609,6 @@ static const struct drm_encoder_funcs radeon_legacy_tmds_int_enc_funcs = {
+ .destroy = radeon_enc_destroy,
+ };
+
+-static bool radeon_legacy_tmds_ext_mode_fixup(struct drm_encoder *encoder,
+- struct drm_display_mode *mode,
+- struct drm_display_mode *adjusted_mode)
+-{
+- /* set the active encoder to connector routing */
+- radeon_encoder_set_active_device(encoder);
+- drm_mode_set_crtcinfo(adjusted_mode, 0);
+-
+- return true;
+-}
+-
+ static void radeon_legacy_tmds_ext_dpms(struct drm_encoder *encoder, int mode)
+ {
+ struct drm_device *dev = encoder->dev;
+@@ -697,6 +699,8 @@ static void radeon_legacy_tmds_ext_mode_set(struct drm_encoder *encoder,
+ /*if (mode->clock > 165000)
+ fp2_gen_cntl |= R300_FP2_DVO_DUAL_CHANNEL_EN;*/
+ }
++ if (!radeon_combios_external_tmds_setup(encoder))
++ radeon_external_tmds_setup(encoder);
+ }
+
+ if (radeon_crtc->crtc_id == 0) {
+@@ -724,9 +728,22 @@ static void radeon_legacy_tmds_ext_mode_set(struct drm_encoder *encoder,
+ radeon_combios_encoder_crtc_scratch_regs(encoder, radeon_crtc->crtc_id);
+ }
+
++static void radeon_ext_tmds_enc_destroy(struct drm_encoder *encoder)
++{
++ struct radeon_encoder *radeon_encoder = to_radeon_encoder(encoder);
++ struct radeon_encoder_ext_tmds *tmds = radeon_encoder->enc_priv;
++ if (tmds) {
++ if (tmds->i2c_bus)
++ radeon_i2c_destroy(tmds->i2c_bus);
++ }
++ kfree(radeon_encoder->enc_priv);
++ drm_encoder_cleanup(encoder);
++ kfree(radeon_encoder);
++}
++
+ static const struct drm_encoder_helper_funcs radeon_legacy_tmds_ext_helper_funcs = {
+ .dpms = radeon_legacy_tmds_ext_dpms,
+- .mode_fixup = radeon_legacy_tmds_ext_mode_fixup,
++ .mode_fixup = radeon_legacy_mode_fixup,
+ .prepare = radeon_legacy_tmds_ext_prepare,
+ .mode_set = radeon_legacy_tmds_ext_mode_set,
+ .commit = radeon_legacy_tmds_ext_commit,
+@@ -735,20 +752,9 @@ static const struct drm_encoder_helper_funcs radeon_legacy_tmds_ext_helper_funcs
+
+
+ static const struct drm_encoder_funcs radeon_legacy_tmds_ext_enc_funcs = {
+- .destroy = radeon_enc_destroy,
++ .destroy = radeon_ext_tmds_enc_destroy,
+ };
+
+-static bool radeon_legacy_tv_dac_mode_fixup(struct drm_encoder *encoder,
+- struct drm_display_mode *mode,
+- struct drm_display_mode *adjusted_mode)
+-{
+- /* set the active encoder to connector routing */
+- radeon_encoder_set_active_device(encoder);
+- drm_mode_set_crtcinfo(adjusted_mode, 0);
+-
+- return true;
+-}
+-
+ static void radeon_legacy_tv_dac_dpms(struct drm_encoder *encoder, int mode)
+ {
+ struct drm_device *dev = encoder->dev;
+@@ -1265,7 +1271,7 @@ static enum drm_connector_status radeon_legacy_tv_dac_detect(struct drm_encoder
+
+ static const struct drm_encoder_helper_funcs radeon_legacy_tv_dac_helper_funcs = {
+ .dpms = radeon_legacy_tv_dac_dpms,
+- .mode_fixup = radeon_legacy_tv_dac_mode_fixup,
++ .mode_fixup = radeon_legacy_mode_fixup,
+ .prepare = radeon_legacy_tv_dac_prepare,
+ .mode_set = radeon_legacy_tv_dac_mode_set,
+ .commit = radeon_legacy_tv_dac_commit,
+@@ -1302,6 +1308,29 @@ static struct radeon_encoder_int_tmds *radeon_legacy_get_tmds_info(struct radeon
+ return tmds;
+ }
+
++static struct radeon_encoder_ext_tmds *radeon_legacy_get_ext_tmds_info(struct radeon_encoder *encoder)
++{
++ struct drm_device *dev = encoder->base.dev;
++ struct radeon_device *rdev = dev->dev_private;
++ struct radeon_encoder_ext_tmds *tmds = NULL;
++ bool ret;
++
++ if (rdev->is_atom_bios)
++ return NULL;
++
++ tmds = kzalloc(sizeof(struct radeon_encoder_ext_tmds), GFP_KERNEL);
++
++ if (!tmds)
++ return NULL;
++
++ ret = radeon_legacy_get_ext_tmds_info_from_combios(encoder, tmds);
++
++ if (ret == false)
++ radeon_legacy_get_ext_tmds_info_from_table(encoder, tmds);
++
++ return tmds;
++}
++
+ void
+ radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t supported_device)
+ {
+@@ -1329,7 +1358,6 @@ radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t
+ encoder->possible_crtcs = 0x1;
+ else
+ encoder->possible_crtcs = 0x3;
+- encoder->possible_clones = 0;
+
+ radeon_encoder->enc_priv = NULL;
+
+@@ -1373,7 +1401,7 @@ radeon_add_legacy_encoder(struct drm_device *dev, uint32_t encoder_id, uint32_t
+ drm_encoder_init(dev, encoder, &radeon_legacy_tmds_ext_enc_funcs, DRM_MODE_ENCODER_TMDS);
+ drm_encoder_helper_add(encoder, &radeon_legacy_tmds_ext_helper_funcs);
+ if (!rdev->is_atom_bios)
+- radeon_combios_get_ext_tmds_info(radeon_encoder);
++ radeon_encoder->enc_priv = radeon_legacy_get_ext_tmds_info(radeon_encoder);
+ break;
+ }
+ }
+diff --git a/drivers/gpu/drm/radeon/radeon_legacy_tv.c b/drivers/gpu/drm/radeon/radeon_legacy_tv.c
+index 3a12bb0..417684d 100644
+--- a/drivers/gpu/drm/radeon/radeon_legacy_tv.c
++++ b/drivers/gpu/drm/radeon/radeon_legacy_tv.c
+@@ -77,7 +77,7 @@ struct radeon_tv_mode_constants {
+ unsigned pix_to_tv;
+ };
+
+-static const uint16_t hor_timing_NTSC[] = {
++static const uint16_t hor_timing_NTSC[MAX_H_CODE_TIMING_LEN] = {
+ 0x0007,
+ 0x003f,
+ 0x0263,
+@@ -98,7 +98,7 @@ static const uint16_t hor_timing_NTSC[] = {
+ 0
+ };
+
+-static const uint16_t vert_timing_NTSC[] = {
++static const uint16_t vert_timing_NTSC[MAX_V_CODE_TIMING_LEN] = {
+ 0x2001,
+ 0x200d,
+ 0x1006,
+@@ -115,7 +115,7 @@ static const uint16_t vert_timing_NTSC[] = {
+ 0
+ };
+
+-static const uint16_t hor_timing_PAL[] = {
++static const uint16_t hor_timing_PAL[MAX_H_CODE_TIMING_LEN] = {
+ 0x0007,
+ 0x0058,
+ 0x027c,
+@@ -136,7 +136,7 @@ static const uint16_t hor_timing_PAL[] = {
+ 0
+ };
+
+-static const uint16_t vert_timing_PAL[] = {
++static const uint16_t vert_timing_PAL[MAX_V_CODE_TIMING_LEN] = {
+ 0x2001,
+ 0x200c,
+ 0x1005,
+@@ -623,9 +623,9 @@ void radeon_legacy_tv_mode_set(struct drm_encoder *encoder,
+ }
+ flicker_removal = (tmp + 500) / 1000;
+
+- if (flicker_removal < 3)
+- flicker_removal = 3;
+- for (i = 0; i < 6; ++i) {
++ if (flicker_removal < 2)
++ flicker_removal = 2;
++ for (i = 0; i < ARRAY_SIZE(SLOPE_limit); ++i) {
+ if (flicker_removal == SLOPE_limit[i])
+ break;
+ }
+diff --git a/drivers/gpu/drm/radeon/radeon_mode.h b/drivers/gpu/drm/radeon/radeon_mode.h
+index ace726a..96f0243 100644
+--- a/drivers/gpu/drm/radeon/radeon_mode.h
++++ b/drivers/gpu/drm/radeon/radeon_mode.h
+@@ -33,6 +33,7 @@
+ #include <drm_crtc.h>
+ #include <drm_mode.h>
+ #include <drm_edid.h>
++#include <drm_dp_helper.h>
+ #include <linux/i2c.h>
+ #include <linux/i2c-id.h>
+ #include <linux/i2c-algo-bit.h>
+@@ -45,32 +46,6 @@ struct radeon_device;
+ #define to_radeon_encoder(x) container_of(x, struct radeon_encoder, base)
+ #define to_radeon_framebuffer(x) container_of(x, struct radeon_framebuffer, base)
+
+-enum radeon_connector_type {
+- CONNECTOR_NONE,
+- CONNECTOR_VGA,
+- CONNECTOR_DVI_I,
+- CONNECTOR_DVI_D,
+- CONNECTOR_DVI_A,
+- CONNECTOR_STV,
+- CONNECTOR_CTV,
+- CONNECTOR_LVDS,
+- CONNECTOR_DIGITAL,
+- CONNECTOR_SCART,
+- CONNECTOR_HDMI_TYPE_A,
+- CONNECTOR_HDMI_TYPE_B,
+- CONNECTOR_0XC,
+- CONNECTOR_0XD,
+- CONNECTOR_DIN,
+- CONNECTOR_DISPLAY_PORT,
+- CONNECTOR_UNSUPPORTED
+-};
+-
+-enum radeon_dvi_type {
+- DVI_AUTO,
+- DVI_DIGITAL,
+- DVI_ANALOG
+-};
+-
+ enum radeon_rmx_type {
+ RMX_OFF,
+ RMX_FULL,
+@@ -87,26 +62,48 @@ enum radeon_tv_std {
+ TV_STD_SCART_PAL,
+ TV_STD_SECAM,
+ TV_STD_PAL_CN,
++ TV_STD_PAL_N,
+ };
+
++/* radeon gpio-based i2c
++ * 1. "mask" reg and bits
++ * grabs the gpio pins for software use
++ * 0=not held 1=held
++ * 2. "a" reg and bits
++ * output pin value
++ * 0=low 1=high
++ * 3. "en" reg and bits
++ * sets the pin direction
++ * 0=input 1=output
++ * 4. "y" reg and bits
++ * input pin value
++ * 0=low 1=high
++ */
+ struct radeon_i2c_bus_rec {
+ bool valid;
++ /* id used by atom */
++ uint8_t i2c_id;
++ /* can be used with hw i2c engine */
++ bool hw_capable;
++ /* uses multi-media i2c engine */
++ bool mm_i2c;
++ /* regs and bits */
+ uint32_t mask_clk_reg;
+ uint32_t mask_data_reg;
+ uint32_t a_clk_reg;
+ uint32_t a_data_reg;
+- uint32_t put_clk_reg;
+- uint32_t put_data_reg;
+- uint32_t get_clk_reg;
+- uint32_t get_data_reg;
++ uint32_t en_clk_reg;
++ uint32_t en_data_reg;
++ uint32_t y_clk_reg;
++ uint32_t y_data_reg;
+ uint32_t mask_clk_mask;
+ uint32_t mask_data_mask;
+- uint32_t put_clk_mask;
+- uint32_t put_data_mask;
+- uint32_t get_clk_mask;
+- uint32_t get_data_mask;
+ uint32_t a_clk_mask;
+ uint32_t a_data_mask;
++ uint32_t en_clk_mask;
++ uint32_t en_data_mask;
++ uint32_t y_clk_mask;
++ uint32_t y_data_mask;
+ };
+
+ struct radeon_tmds_pll {
+@@ -128,16 +125,24 @@ struct radeon_tmds_pll {
+ #define RADEON_PLL_PREFER_HIGH_POST_DIV (1 << 9)
+ #define RADEON_PLL_USE_FRAC_FB_DIV (1 << 10)
+ #define RADEON_PLL_PREFER_CLOSEST_LOWER (1 << 11)
++#define RADEON_PLL_USE_POST_DIV (1 << 12)
+
+ struct radeon_pll {
+- uint16_t reference_freq;
+- uint16_t reference_div;
++ /* reference frequency */
++ uint32_t reference_freq;
++
++ /* fixed dividers */
++ uint32_t reference_div;
++ uint32_t post_div;
++
++ /* pll in/out limits */
+ uint32_t pll_in_min;
+ uint32_t pll_in_max;
+ uint32_t pll_out_min;
+ uint32_t pll_out_max;
+- uint16_t xclk;
++ uint32_t best_vco;
+
++ /* divider limits */
+ uint32_t min_ref_div;
+ uint32_t max_ref_div;
+ uint32_t min_post_div;
+@@ -146,13 +151,21 @@ struct radeon_pll {
+ uint32_t max_feedback_div;
+ uint32_t min_frac_feedback_div;
+ uint32_t max_frac_feedback_div;
+- uint32_t best_vco;
++
++ /* flags for the current clock */
++ uint32_t flags;
++
++ /* pll id */
++ uint32_t id;
+ };
+
+ struct radeon_i2c_chan {
+- struct drm_device *dev;
+ struct i2c_adapter adapter;
+- struct i2c_algo_bit_data algo;
++ struct drm_device *dev;
++ union {
++ struct i2c_algo_dp_aux_data dp;
++ struct i2c_algo_bit_data bit;
++ } algo;
+ struct radeon_i2c_bus_rec rec;
+ };
+
+@@ -170,6 +183,11 @@ enum radeon_connector_table {
+ CT_EMAC,
+ };
+
++enum radeon_dvo_chip {
++ DVO_SIL164,
++ DVO_SIL1178,
++};
++
+ struct radeon_mode_info {
+ struct atom_context *atom_context;
+ struct card_info *atom_card_info;
+@@ -184,7 +202,8 @@ struct radeon_mode_info {
+ struct drm_property *tv_std_property;
+ /* legacy TMDS PLL detect */
+ struct drm_property *tmds_pll_property;
+-
++ /* hardcoded DFP edid from BIOS */
++ struct edid *bios_hardcoded_edid;
+ };
+
+ #define MAX_H_CODE_TIMING_LEN 32
+@@ -261,6 +280,13 @@ struct radeon_encoder_int_tmds {
+ struct radeon_tmds_pll tmds_pll[4];
+ };
+
++struct radeon_encoder_ext_tmds {
++ /* tmds over dvo */
++ struct radeon_i2c_chan *i2c_bus;
++ uint8_t slave_addr;
++ enum radeon_dvo_chip dvo_chip;
++};
++
+ /* spread spectrum */
+ struct radeon_atom_ss {
+ uint16_t percentage;
+@@ -274,7 +300,7 @@ struct radeon_atom_ss {
+ struct radeon_encoder_atom_dig {
+ /* atom dig */
+ bool coherent_mode;
+- int dig_block;
++ int dig_encoder; /* -1 disabled, 0 DIGA, 1 DIGB */
+ /* atom lvds */
+ uint32_t lvds_misc;
+ uint16_t panel_pwr_delay;
+@@ -297,11 +323,43 @@ struct radeon_encoder {
+ enum radeon_rmx_type rmx_type;
+ struct drm_display_mode native_mode;
+ void *enc_priv;
++ int hdmi_offset;
++ int hdmi_audio_workaround;
++ int hdmi_buffer_status;
+ };
+
+ struct radeon_connector_atom_dig {
+ uint32_t igp_lane_info;
+ bool linkb;
++ /* displayport */
++ struct radeon_i2c_chan *dp_i2c_bus;
++ u8 dpcd[8];
++ u8 dp_sink_type;
++ int dp_clock;
++ int dp_lane_count;
++};
++
++struct radeon_gpio_rec {
++ bool valid;
++ u8 id;
++ u32 reg;
++ u32 mask;
++};
++
++enum radeon_hpd_id {
++ RADEON_HPD_NONE = 0,
++ RADEON_HPD_1,
++ RADEON_HPD_2,
++ RADEON_HPD_3,
++ RADEON_HPD_4,
++ RADEON_HPD_5,
++ RADEON_HPD_6,
++};
++
++struct radeon_hpd {
++ enum radeon_hpd_id hpd;
++ u8 plugged_state;
++ struct radeon_gpio_rec gpio;
+ };
+
+ struct radeon_connector {
+@@ -318,6 +376,7 @@ struct radeon_connector {
+ void *con_priv;
+ bool dac_load_detect;
+ uint16_t connector_object_id;
++ struct radeon_hpd hpd;
+ };
+
+ struct radeon_framebuffer {
+@@ -325,10 +384,42 @@ struct radeon_framebuffer {
+ struct drm_gem_object *obj;
+ };
+
++extern enum radeon_tv_std
++radeon_combios_get_tv_info(struct radeon_device *rdev);
++extern enum radeon_tv_std
++radeon_atombios_get_tv_info(struct radeon_device *rdev);
++
++extern void radeon_connector_hotplug(struct drm_connector *connector);
++extern bool radeon_dp_needs_link_train(struct radeon_connector *radeon_connector);
++extern int radeon_dp_mode_valid_helper(struct radeon_connector *radeon_connector,
++ struct drm_display_mode *mode);
++extern void radeon_dp_set_link_config(struct drm_connector *connector,
++ struct drm_display_mode *mode);
++extern void dp_link_train(struct drm_encoder *encoder,
++ struct drm_connector *connector);
++extern u8 radeon_dp_getsinktype(struct radeon_connector *radeon_connector);
++extern bool radeon_dp_getdpcd(struct radeon_connector *radeon_connector);
++extern void atombios_dig_transmitter_setup(struct drm_encoder *encoder,
++ int action, uint8_t lane_num,
++ uint8_t lane_set);
++extern int radeon_dp_i2c_aux_ch(struct i2c_adapter *adapter, int mode,
++ uint8_t write_byte, uint8_t *read_byte);
++
++extern struct radeon_i2c_chan *radeon_i2c_create_dp(struct drm_device *dev,
++ struct radeon_i2c_bus_rec *rec,
++ const char *name);
+ extern struct radeon_i2c_chan *radeon_i2c_create(struct drm_device *dev,
+ struct radeon_i2c_bus_rec *rec,
+ const char *name);
+ extern void radeon_i2c_destroy(struct radeon_i2c_chan *i2c);
++extern void radeon_i2c_sw_get_byte(struct radeon_i2c_chan *i2c_bus,
++ u8 slave_addr,
++ u8 addr,
++ u8 *val);
++extern void radeon_i2c_sw_put_byte(struct radeon_i2c_chan *i2c,
++ u8 slave_addr,
++ u8 addr,
++ u8 val);
+ extern bool radeon_ddc_probe(struct radeon_connector *radeon_connector);
+ extern int radeon_ddc_get_modes(struct radeon_connector *radeon_connector);
+
+@@ -340,8 +431,17 @@ extern void radeon_compute_pll(struct radeon_pll *pll,
+ uint32_t *fb_div_p,
+ uint32_t *frac_fb_div_p,
+ uint32_t *ref_div_p,
+- uint32_t *post_div_p,
+- int flags);
++ uint32_t *post_div_p);
++
++extern void radeon_compute_pll_avivo(struct radeon_pll *pll,
++ uint64_t freq,
++ uint32_t *dot_clock_p,
++ uint32_t *fb_div_p,
++ uint32_t *frac_fb_div_p,
++ uint32_t *ref_div_p,
++ uint32_t *post_div_p);
++
++extern void radeon_setup_encoder_clones(struct drm_device *dev);
+
+ struct drm_encoder *radeon_encoder_legacy_lvds_add(struct drm_device *dev, int bios_index);
+ struct drm_encoder *radeon_encoder_legacy_primary_dac_add(struct drm_device *dev, int bios_index, int with_tv);
+@@ -349,6 +449,7 @@ struct drm_encoder *radeon_encoder_legacy_tv_dac_add(struct drm_device *dev, int
+ struct drm_encoder *radeon_encoder_legacy_tmds_int_add(struct drm_device *dev, int bios_index);
+ struct drm_encoder *radeon_encoder_legacy_tmds_ext_add(struct drm_device *dev, int bios_index);
+ extern void atombios_external_tmds_setup(struct drm_encoder *encoder, int action);
++extern void atombios_digital_setup(struct drm_encoder *encoder, int action);
+ extern int atombios_get_encoder_mode(struct drm_encoder *encoder);
+ extern void radeon_encoder_set_active_device(struct drm_encoder *encoder);
+
+@@ -364,7 +465,6 @@ extern void atombios_crtc_dpms(struct drm_crtc *crtc, int mode);
+
+ extern int radeon_crtc_set_base(struct drm_crtc *crtc, int x, int y,
+ struct drm_framebuffer *old_fb);
+-extern void radeon_legacy_atom_set_surface(struct drm_crtc *crtc);
+
+ extern int radeon_crtc_cursor_set(struct drm_crtc *crtc,
+ struct drm_file *file_priv,
+@@ -374,16 +474,23 @@ extern int radeon_crtc_cursor_set(struct drm_crtc *crtc,
+ extern int radeon_crtc_cursor_move(struct drm_crtc *crtc,
+ int x, int y);
+
++extern bool radeon_combios_check_hardcoded_edid(struct radeon_device *rdev);
++extern struct edid *
++radeon_combios_get_hardcoded_edid(struct radeon_device *rdev);
+ extern bool radeon_atom_get_clock_info(struct drm_device *dev);
+ extern bool radeon_combios_get_clock_info(struct drm_device *dev);
+ extern struct radeon_encoder_atom_dig *
+ radeon_atombios_get_lvds_info(struct radeon_encoder *encoder);
+-bool radeon_atombios_get_tmds_info(struct radeon_encoder *encoder,
+- struct radeon_encoder_int_tmds *tmds);
+-bool radeon_legacy_get_tmds_info_from_combios(struct radeon_encoder *encoder,
+- struct radeon_encoder_int_tmds *tmds);
+-bool radeon_legacy_get_tmds_info_from_table(struct radeon_encoder *encoder,
+- struct radeon_encoder_int_tmds *tmds);
++extern bool radeon_atombios_get_tmds_info(struct radeon_encoder *encoder,
++ struct radeon_encoder_int_tmds *tmds);
++extern bool radeon_legacy_get_tmds_info_from_combios(struct radeon_encoder *encoder,
++ struct radeon_encoder_int_tmds *tmds);
++extern bool radeon_legacy_get_tmds_info_from_table(struct radeon_encoder *encoder,
++ struct radeon_encoder_int_tmds *tmds);
++extern bool radeon_legacy_get_ext_tmds_info_from_combios(struct radeon_encoder *encoder,
++ struct radeon_encoder_ext_tmds *tmds);
++extern bool radeon_legacy_get_ext_tmds_info_from_table(struct radeon_encoder *encoder,
++ struct radeon_encoder_ext_tmds *tmds);
+ extern struct radeon_encoder_primary_dac *
+ radeon_atombios_get_primary_dac_info(struct radeon_encoder *encoder);
+ extern struct radeon_encoder_tv_dac *
+@@ -395,6 +502,8 @@ extern struct radeon_encoder_tv_dac *
+ radeon_combios_get_tv_dac_info(struct radeon_encoder *encoder);
+ extern struct radeon_encoder_primary_dac *
+ radeon_combios_get_primary_dac_info(struct radeon_encoder *encoder);
++extern bool radeon_combios_external_tmds_setup(struct drm_encoder *encoder);
++extern void radeon_external_tmds_setup(struct drm_encoder *encoder);
+ extern void radeon_combios_output_lock(struct drm_encoder *encoder, bool lock);
+ extern void radeon_combios_initialize_bios_scratch_regs(struct drm_device *dev);
+ extern void radeon_atom_output_lock(struct drm_encoder *encoder, bool lock);
+@@ -426,16 +535,13 @@ void radeon_atombios_init_crtc(struct drm_device *dev,
+ struct radeon_crtc *radeon_crtc);
+ void radeon_legacy_init_crtc(struct drm_device *dev,
+ struct radeon_crtc *radeon_crtc);
+-void radeon_i2c_do_lock(struct radeon_connector *radeon_connector, int lock_state);
++extern void radeon_i2c_do_lock(struct radeon_i2c_chan *i2c, int lock_state);
+
+ void radeon_get_clock_info(struct drm_device *dev);
+
+ extern bool radeon_get_atom_connector_info_from_object_table(struct drm_device *dev);
+ extern bool radeon_get_atom_connector_info_from_supported_devices_table(struct drm_device *dev);
+
+-void radeon_rmx_mode_fixup(struct drm_encoder *encoder,
+- struct drm_display_mode *mode,
+- struct drm_display_mode *adjusted_mode);
+ void radeon_enc_destroy(struct drm_encoder *encoder);
+ void radeon_copy_fb(struct drm_device *dev, struct drm_gem_object *dst_obj);
+ void radeon_combios_asic_init(struct drm_device *dev);
+diff --git a/drivers/gpu/drm/radeon/radeon_object.c b/drivers/gpu/drm/radeon/radeon_object.c
+index 1f056da..f1da370 100644
+--- a/drivers/gpu/drm/radeon/radeon_object.c
++++ b/drivers/gpu/drm/radeon/radeon_object.c
+@@ -34,100 +34,62 @@
+ #include "radeon_drm.h"
+ #include "radeon.h"
+
+-struct radeon_object {
+- struct ttm_buffer_object tobj;
+- struct list_head list;
+- struct radeon_device *rdev;
+- struct drm_gem_object *gobj;
+- struct ttm_bo_kmap_obj kmap;
+- unsigned pin_count;
+- uint64_t gpu_addr;
+- void *kptr;
+- bool is_iomem;
+- uint32_t tiling_flags;
+- uint32_t pitch;
+- int surface_reg;
+-};
+
+ int radeon_ttm_init(struct radeon_device *rdev);
+ void radeon_ttm_fini(struct radeon_device *rdev);
++static void radeon_bo_clear_surface_reg(struct radeon_bo *bo);
+
+ /*
+ * To exclude mutual BO access we rely on bo_reserve exclusion, as all
+ * function are calling it.
+ */
+
+-static int radeon_object_reserve(struct radeon_object *robj, bool interruptible)
++static void radeon_ttm_bo_destroy(struct ttm_buffer_object *tbo)
+ {
+- return ttm_bo_reserve(&robj->tobj, interruptible, false, false, 0);
+-}
++ struct radeon_bo *bo;
+
+-static void radeon_object_unreserve(struct radeon_object *robj)
+-{
+- ttm_bo_unreserve(&robj->tobj);
++ bo = container_of(tbo, struct radeon_bo, tbo);
++ mutex_lock(&bo->rdev->gem.mutex);
++ list_del_init(&bo->list);
++ mutex_unlock(&bo->rdev->gem.mutex);
++ radeon_bo_clear_surface_reg(bo);
++ kfree(bo);
+ }
+
+-static void radeon_ttm_object_object_destroy(struct ttm_buffer_object *tobj)
++bool radeon_ttm_bo_is_radeon_bo(struct ttm_buffer_object *bo)
+ {
+- struct radeon_object *robj;
+-
+- robj = container_of(tobj, struct radeon_object, tobj);
+- list_del_init(&robj->list);
+- radeon_object_clear_surface_reg(robj);
+- kfree(robj);
++ if (bo->destroy == &radeon_ttm_bo_destroy)
++ return true;
++ return false;
+ }
+
+-static inline void radeon_object_gpu_addr(struct radeon_object *robj)
++void radeon_ttm_placement_from_domain(struct radeon_bo *rbo, u32 domain)
+ {
+- /* Default gpu address */
+- robj->gpu_addr = 0xFFFFFFFFFFFFFFFFULL;
+- if (robj->tobj.mem.mm_node == NULL) {
+- return;
+- }
+- robj->gpu_addr = ((u64)robj->tobj.mem.mm_node->start) << PAGE_SHIFT;
+- switch (robj->tobj.mem.mem_type) {
+- case TTM_PL_VRAM:
+- robj->gpu_addr += (u64)robj->rdev->mc.vram_location;
+- break;
+- case TTM_PL_TT:
+- robj->gpu_addr += (u64)robj->rdev->mc.gtt_location;
+- break;
+- default:
+- DRM_ERROR("Unknown placement %d\n", robj->tobj.mem.mem_type);
+- robj->gpu_addr = 0xFFFFFFFFFFFFFFFFULL;
+- return;
+- }
+-}
++ u32 c = 0;
+
+-static inline uint32_t radeon_object_flags_from_domain(uint32_t domain)
+-{
+- uint32_t flags = 0;
+- if (domain & RADEON_GEM_DOMAIN_VRAM) {
+- flags |= TTM_PL_FLAG_VRAM | TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED;
+- }
+- if (domain & RADEON_GEM_DOMAIN_GTT) {
+- flags |= TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
+- }
+- if (domain & RADEON_GEM_DOMAIN_CPU) {
+- flags |= TTM_PL_FLAG_SYSTEM | TTM_PL_MASK_CACHING;
+- }
+- if (!flags) {
+- flags |= TTM_PL_FLAG_SYSTEM | TTM_PL_MASK_CACHING;
+- }
+- return flags;
++ rbo->placement.fpfn = 0;
++ rbo->placement.lpfn = 0;
++ rbo->placement.placement = rbo->placements;
++ rbo->placement.busy_placement = rbo->placements;
++ if (domain & RADEON_GEM_DOMAIN_VRAM)
++ rbo->placements[c++] = TTM_PL_FLAG_WC | TTM_PL_FLAG_UNCACHED |
++ TTM_PL_FLAG_VRAM;
++ if (domain & RADEON_GEM_DOMAIN_GTT)
++ rbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
++ if (domain & RADEON_GEM_DOMAIN_CPU)
++ rbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
++ if (!c)
++ rbo->placements[c++] = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
++ rbo->placement.num_placement = c;
++ rbo->placement.num_busy_placement = c;
+ }
+
+-int radeon_object_create(struct radeon_device *rdev,
+- struct drm_gem_object *gobj,
+- unsigned long size,
+- bool kernel,
+- uint32_t domain,
+- bool interruptible,
+- struct radeon_object **robj_ptr)
++int radeon_bo_create(struct radeon_device *rdev, struct drm_gem_object *gobj,
++ unsigned long size, bool kernel, u32 domain,
++ struct radeon_bo **bo_ptr)
+ {
+- struct radeon_object *robj;
++ struct radeon_bo *bo;
+ enum ttm_bo_type type;
+- uint32_t flags;
+ int r;
+
+ if (unlikely(rdev->mman.bdev.dev_mapping == NULL)) {
+@@ -138,238 +100,161 @@ int radeon_object_create(struct radeon_device *rdev,
+ } else {
+ type = ttm_bo_type_device;
+ }
+- *robj_ptr = NULL;
+- robj = kzalloc(sizeof(struct radeon_object), GFP_KERNEL);
+- if (robj == NULL) {
++ *bo_ptr = NULL;
++ bo = kzalloc(sizeof(struct radeon_bo), GFP_KERNEL);
++ if (bo == NULL)
+ return -ENOMEM;
+- }
+- robj->rdev = rdev;
+- robj->gobj = gobj;
+- robj->surface_reg = -1;
+- INIT_LIST_HEAD(&robj->list);
+-
+- flags = radeon_object_flags_from_domain(domain);
+- r = ttm_buffer_object_init(&rdev->mman.bdev, &robj->tobj, size, type, flags,
+- 0, 0, false, NULL, size,
+- &radeon_ttm_object_object_destroy);
++ bo->rdev = rdev;
++ bo->gobj = gobj;
++ bo->surface_reg = -1;
++ INIT_LIST_HEAD(&bo->list);
++
++ radeon_ttm_placement_from_domain(bo, domain);
++ /* Kernel allocation are uninterruptible */
++ r = ttm_bo_init(&rdev->mman.bdev, &bo->tbo, size, type,
++ &bo->placement, 0, 0, !kernel, NULL, size,
++ &radeon_ttm_bo_destroy);
+ if (unlikely(r != 0)) {
+- /* ttm call radeon_ttm_object_object_destroy if error happen */
+- DRM_ERROR("Failed to allocate TTM object (%ld, 0x%08X, %u)\n",
+- size, flags, 0);
++ if (r != -ERESTARTSYS)
++ dev_err(rdev->dev,
++ "object_init failed for (%lu, 0x%08X)\n",
++ size, domain);
+ return r;
+ }
+- *robj_ptr = robj;
++ *bo_ptr = bo;
+ if (gobj) {
+- list_add_tail(&robj->list, &rdev->gem.objects);
++ mutex_lock(&bo->rdev->gem.mutex);
++ list_add_tail(&bo->list, &rdev->gem.objects);
++ mutex_unlock(&bo->rdev->gem.mutex);
+ }
+ return 0;
+ }
+
+-int radeon_object_kmap(struct radeon_object *robj, void **ptr)
++int radeon_bo_kmap(struct radeon_bo *bo, void **ptr)
+ {
++ bool is_iomem;
+ int r;
+
+- spin_lock(&robj->tobj.lock);
+- if (robj->kptr) {
++ if (bo->kptr) {
+ if (ptr) {
+- *ptr = robj->kptr;
++ *ptr = bo->kptr;
+ }
+- spin_unlock(&robj->tobj.lock);
+ return 0;
+ }
+- spin_unlock(&robj->tobj.lock);
+- r = ttm_bo_kmap(&robj->tobj, 0, robj->tobj.num_pages, &robj->kmap);
++ r = ttm_bo_kmap(&bo->tbo, 0, bo->tbo.num_pages, &bo->kmap);
+ if (r) {
+ return r;
+ }
+- spin_lock(&robj->tobj.lock);
+- robj->kptr = ttm_kmap_obj_virtual(&robj->kmap, &robj->is_iomem);
+- spin_unlock(&robj->tobj.lock);
++ bo->kptr = ttm_kmap_obj_virtual(&bo->kmap, &is_iomem);
+ if (ptr) {
+- *ptr = robj->kptr;
++ *ptr = bo->kptr;
+ }
+- radeon_object_check_tiling(robj, 0, 0);
++ radeon_bo_check_tiling(bo, 0, 0);
+ return 0;
+ }
+
+-void radeon_object_kunmap(struct radeon_object *robj)
++void radeon_bo_kunmap(struct radeon_bo *bo)
+ {
+- spin_lock(&robj->tobj.lock);
+- if (robj->kptr == NULL) {
+- spin_unlock(&robj->tobj.lock);
++ if (bo->kptr == NULL)
+ return;
+- }
+- robj->kptr = NULL;
+- spin_unlock(&robj->tobj.lock);
+- radeon_object_check_tiling(robj, 0, 0);
+- ttm_bo_kunmap(&robj->kmap);
++ bo->kptr = NULL;
++ radeon_bo_check_tiling(bo, 0, 0);
++ ttm_bo_kunmap(&bo->kmap);
+ }
+
+-void radeon_object_unref(struct radeon_object **robj)
++void radeon_bo_unref(struct radeon_bo **bo)
+ {
+- struct ttm_buffer_object *tobj;
++ struct ttm_buffer_object *tbo;
+
+- if ((*robj) == NULL) {
++ if ((*bo) == NULL)
+ return;
+- }
+- tobj = &((*robj)->tobj);
+- ttm_bo_unref(&tobj);
+- if (tobj == NULL) {
+- *robj = NULL;
+- }
+-}
+-
+-int radeon_object_mmap(struct radeon_object *robj, uint64_t *offset)
+-{
+- *offset = robj->tobj.addr_space_offset;
+- return 0;
++ tbo = &((*bo)->tbo);
++ ttm_bo_unref(&tbo);
++ if (tbo == NULL)
++ *bo = NULL;
+ }
+
+-int radeon_object_pin(struct radeon_object *robj, uint32_t domain,
+- uint64_t *gpu_addr)
++int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr)
+ {
+- uint32_t flags;
+- uint32_t tmp;
+- int r;
++ int r, i;
+
+- flags = radeon_object_flags_from_domain(domain);
+- spin_lock(&robj->tobj.lock);
+- if (robj->pin_count) {
+- robj->pin_count++;
+- if (gpu_addr != NULL) {
+- *gpu_addr = robj->gpu_addr;
+- }
+- spin_unlock(&robj->tobj.lock);
++ radeon_ttm_placement_from_domain(bo, domain);
++ if (bo->pin_count) {
++ bo->pin_count++;
++ if (gpu_addr)
++ *gpu_addr = radeon_bo_gpu_offset(bo);
+ return 0;
+ }
+- spin_unlock(&robj->tobj.lock);
+- r = radeon_object_reserve(robj, false);
+- if (unlikely(r != 0)) {
+- DRM_ERROR("radeon: failed to reserve object for pinning it.\n");
+- return r;
+- }
+- tmp = robj->tobj.mem.placement;
+- ttm_flag_masked(&tmp, flags, TTM_PL_MASK_MEM);
+- robj->tobj.proposed_placement = tmp | TTM_PL_FLAG_NO_EVICT | TTM_PL_MASK_CACHING;
+- r = ttm_buffer_object_validate(&robj->tobj,
+- robj->tobj.proposed_placement,
+- false, false);
+- radeon_object_gpu_addr(robj);
+- if (gpu_addr != NULL) {
+- *gpu_addr = robj->gpu_addr;
+- }
+- robj->pin_count = 1;
+- if (unlikely(r != 0)) {
+- DRM_ERROR("radeon: failed to pin object.\n");
+- }
+- radeon_object_unreserve(robj);
++ radeon_ttm_placement_from_domain(bo, domain);
++ for (i = 0; i < bo->placement.num_placement; i++)
++ bo->placements[i] |= TTM_PL_FLAG_NO_EVICT;
++ r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
++ if (likely(r == 0)) {
++ bo->pin_count = 1;
++ if (gpu_addr != NULL)
++ *gpu_addr = radeon_bo_gpu_offset(bo);
++ }
++ if (unlikely(r != 0))
++ dev_err(bo->rdev->dev, "%p pin failed\n", bo);
+ return r;
+ }
+
+-void radeon_object_unpin(struct radeon_object *robj)
++int radeon_bo_unpin(struct radeon_bo *bo)
+ {
+- uint32_t flags;
+- int r;
++ int r, i;
+
+- spin_lock(&robj->tobj.lock);
+- if (!robj->pin_count) {
+- spin_unlock(&robj->tobj.lock);
+- printk(KERN_WARNING "Unpin not necessary for %p !\n", robj);
+- return;
+- }
+- robj->pin_count--;
+- if (robj->pin_count) {
+- spin_unlock(&robj->tobj.lock);
+- return;
+- }
+- spin_unlock(&robj->tobj.lock);
+- r = radeon_object_reserve(robj, false);
+- if (unlikely(r != 0)) {
+- DRM_ERROR("radeon: failed to reserve object for unpinning it.\n");
+- return;
+- }
+- flags = robj->tobj.mem.placement;
+- robj->tobj.proposed_placement = flags & ~TTM_PL_FLAG_NO_EVICT;
+- r = ttm_buffer_object_validate(&robj->tobj,
+- robj->tobj.proposed_placement,
+- false, false);
+- if (unlikely(r != 0)) {
+- DRM_ERROR("radeon: failed to unpin buffer.\n");
+- }
+- radeon_object_unreserve(robj);
+-}
+-
+-int radeon_object_wait(struct radeon_object *robj)
+-{
+- int r = 0;
+-
+- /* FIXME: should use block reservation instead */
+- r = radeon_object_reserve(robj, true);
+- if (unlikely(r != 0)) {
+- DRM_ERROR("radeon: failed to reserve object for waiting.\n");
+- return r;
+- }
+- spin_lock(&robj->tobj.lock);
+- if (robj->tobj.sync_obj) {
+- r = ttm_bo_wait(&robj->tobj, true, true, false);
+- }
+- spin_unlock(&robj->tobj.lock);
+- radeon_object_unreserve(robj);
+- return r;
+-}
+-
+-int radeon_object_busy_domain(struct radeon_object *robj, uint32_t *cur_placement)
+-{
+- int r = 0;
+-
+- r = radeon_object_reserve(robj, true);
+- if (unlikely(r != 0)) {
+- DRM_ERROR("radeon: failed to reserve object for waiting.\n");
+- return r;
+- }
+- spin_lock(&robj->tobj.lock);
+- *cur_placement = robj->tobj.mem.mem_type;
+- if (robj->tobj.sync_obj) {
+- r = ttm_bo_wait(&robj->tobj, true, true, true);
++ if (!bo->pin_count) {
++ dev_warn(bo->rdev->dev, "%p unpin not necessary\n", bo);
++ return 0;
+ }
+- spin_unlock(&robj->tobj.lock);
+- radeon_object_unreserve(robj);
++ bo->pin_count--;
++ if (bo->pin_count)
++ return 0;
++ for (i = 0; i < bo->placement.num_placement; i++)
++ bo->placements[i] &= ~TTM_PL_FLAG_NO_EVICT;
++ r = ttm_bo_validate(&bo->tbo, &bo->placement, false, false);
++ if (unlikely(r != 0))
++ dev_err(bo->rdev->dev, "%p validate failed for unpin\n", bo);
+ return r;
+ }
+
+-int radeon_object_evict_vram(struct radeon_device *rdev)
++int radeon_bo_evict_vram(struct radeon_device *rdev)
+ {
+- if (rdev->flags & RADEON_IS_IGP) {
+- /* Useless to evict on IGP chips */
+- return 0;
++ /* late 2.6.33 fix IGP hibernate - we need pm ops to do this correct */
++ if (0 && (rdev->flags & RADEON_IS_IGP)) {
++ if (rdev->mc.igp_sideport_enabled == false)
++ /* Useless to evict on IGP chips */
++ return 0;
+ }
+ return ttm_bo_evict_mm(&rdev->mman.bdev, TTM_PL_VRAM);
+ }
+
+-void radeon_object_force_delete(struct radeon_device *rdev)
++void radeon_bo_force_delete(struct radeon_device *rdev)
+ {
+- struct radeon_object *robj, *n;
++ struct radeon_bo *bo, *n;
+ struct drm_gem_object *gobj;
+
+ if (list_empty(&rdev->gem.objects)) {
+ return;
+ }
+- DRM_ERROR("Userspace still has active objects !\n");
+- list_for_each_entry_safe(robj, n, &rdev->gem.objects, list) {
++ dev_err(rdev->dev, "Userspace still has active objects !\n");
++ list_for_each_entry_safe(bo, n, &rdev->gem.objects, list) {
+ mutex_lock(&rdev->ddev->struct_mutex);
+- gobj = robj->gobj;
+- DRM_ERROR("Force free for (%p,%p,%lu,%lu)\n",
+- gobj, robj, (unsigned long)gobj->size,
+- *((unsigned long *)&gobj->refcount));
+- list_del_init(&robj->list);
+- radeon_object_unref(&robj);
++ gobj = bo->gobj;
++ dev_err(rdev->dev, "%p %p %lu %lu force free\n",
++ gobj, bo, (unsigned long)gobj->size,
++ *((unsigned long *)&gobj->refcount));
++ mutex_lock(&bo->rdev->gem.mutex);
++ list_del_init(&bo->list);
++ mutex_unlock(&bo->rdev->gem.mutex);
++ radeon_bo_unref(&bo);
+ gobj->driver_private = NULL;
+ drm_gem_object_unreference(gobj);
+ mutex_unlock(&rdev->ddev->struct_mutex);
+ }
+ }
+
+-int radeon_object_init(struct radeon_device *rdev)
++int radeon_bo_init(struct radeon_device *rdev)
+ {
+ /* Add an MTRR for the VRAM */
+ rdev->mc.vram_mtrr = mtrr_add(rdev->mc.aper_base, rdev->mc.aper_size,
+@@ -382,13 +267,13 @@ int radeon_object_init(struct radeon_device *rdev)
+ return radeon_ttm_init(rdev);
+ }
+
+-void radeon_object_fini(struct radeon_device *rdev)
++void radeon_bo_fini(struct radeon_device *rdev)
+ {
+ radeon_ttm_fini(rdev);
+ }
+
+-void radeon_object_list_add_object(struct radeon_object_list *lobj,
+- struct list_head *head)
++void radeon_bo_list_add_object(struct radeon_bo_list *lobj,
++ struct list_head *head)
+ {
+ if (lobj->wdomain) {
+ list_add(&lobj->list, head);
+@@ -397,125 +282,102 @@ void radeon_object_list_add_object(struct radeon_object_list *lobj,
+ }
+ }
+
+-int radeon_object_list_reserve(struct list_head *head)
++int radeon_bo_list_reserve(struct list_head *head)
+ {
+- struct radeon_object_list *lobj;
++ struct radeon_bo_list *lobj;
+ int r;
+
+ list_for_each_entry(lobj, head, list){
+- if (!lobj->robj->pin_count) {
+- r = radeon_object_reserve(lobj->robj, true);
+- if (unlikely(r != 0)) {
+- DRM_ERROR("radeon: failed to reserve object.\n");
+- return r;
+- }
+- } else {
+- }
++ r = radeon_bo_reserve(lobj->bo, false);
++ if (unlikely(r != 0))
++ return r;
+ }
+ return 0;
+ }
+
+-void radeon_object_list_unreserve(struct list_head *head)
++void radeon_bo_list_unreserve(struct list_head *head)
+ {
+- struct radeon_object_list *lobj;
++ struct radeon_bo_list *lobj;
+
+ list_for_each_entry(lobj, head, list) {
+- if (!lobj->robj->pin_count) {
+- radeon_object_unreserve(lobj->robj);
+- }
++ /* only unreserve object we successfully reserved */
++ if (radeon_bo_is_reserved(lobj->bo))
++ radeon_bo_unreserve(lobj->bo);
+ }
+ }
+
+-int radeon_object_list_validate(struct list_head *head, void *fence)
++int radeon_bo_list_validate(struct list_head *head)
+ {
+- struct radeon_object_list *lobj;
+- struct radeon_object *robj;
+- struct radeon_fence *old_fence = NULL;
++ struct radeon_bo_list *lobj;
++ struct radeon_bo *bo;
+ int r;
+
+- r = radeon_object_list_reserve(head);
++ r = radeon_bo_list_reserve(head);
+ if (unlikely(r != 0)) {
+- radeon_object_list_unreserve(head);
+ return r;
+ }
+ list_for_each_entry(lobj, head, list) {
+- robj = lobj->robj;
+- if (!robj->pin_count) {
++ bo = lobj->bo;
++ if (!bo->pin_count) {
+ if (lobj->wdomain) {
+- robj->tobj.proposed_placement =
+- radeon_object_flags_from_domain(lobj->wdomain);
++ radeon_ttm_placement_from_domain(bo,
++ lobj->wdomain);
+ } else {
+- robj->tobj.proposed_placement =
+- radeon_object_flags_from_domain(lobj->rdomain);
++ radeon_ttm_placement_from_domain(bo,
++ lobj->rdomain);
+ }
+- r = ttm_buffer_object_validate(&robj->tobj,
+- robj->tobj.proposed_placement,
+- true, false);
+- if (unlikely(r)) {
+- DRM_ERROR("radeon: failed to validate.\n");
++ r = ttm_bo_validate(&bo->tbo, &bo->placement,
++ true, false);
++ if (unlikely(r))
+ return r;
+- }
+- radeon_object_gpu_addr(robj);
+- }
+- lobj->gpu_offset = robj->gpu_addr;
+- lobj->tiling_flags = robj->tiling_flags;
+- if (fence) {
+- old_fence = (struct radeon_fence *)robj->tobj.sync_obj;
+- robj->tobj.sync_obj = radeon_fence_ref(fence);
+- robj->tobj.sync_obj_arg = NULL;
+- }
+- if (old_fence) {
+- radeon_fence_unref(&old_fence);
+ }
++ lobj->gpu_offset = radeon_bo_gpu_offset(bo);
++ lobj->tiling_flags = bo->tiling_flags;
+ }
+ return 0;
+ }
+
+-void radeon_object_list_unvalidate(struct list_head *head)
++void radeon_bo_list_fence(struct list_head *head, void *fence)
+ {
+- struct radeon_object_list *lobj;
++ struct radeon_bo_list *lobj;
++ struct radeon_bo *bo;
+ struct radeon_fence *old_fence = NULL;
+
+ list_for_each_entry(lobj, head, list) {
+- old_fence = (struct radeon_fence *)lobj->robj->tobj.sync_obj;
+- lobj->robj->tobj.sync_obj = NULL;
++ bo = lobj->bo;
++ spin_lock(&bo->tbo.lock);
++ old_fence = (struct radeon_fence *)bo->tbo.sync_obj;
++ bo->tbo.sync_obj = radeon_fence_ref(fence);
++ bo->tbo.sync_obj_arg = NULL;
++ spin_unlock(&bo->tbo.lock);
+ if (old_fence) {
+ radeon_fence_unref(&old_fence);
+ }
+ }
+- radeon_object_list_unreserve(head);
+ }
+
+-void radeon_object_list_clean(struct list_head *head)
+-{
+- radeon_object_list_unreserve(head);
+-}
+-
+-int radeon_object_fbdev_mmap(struct radeon_object *robj,
++int radeon_bo_fbdev_mmap(struct radeon_bo *bo,
+ struct vm_area_struct *vma)
+ {
+- return ttm_fbdev_mmap(vma, &robj->tobj);
+-}
+-
+-unsigned long radeon_object_size(struct radeon_object *robj)
+-{
+- return robj->tobj.num_pages << PAGE_SHIFT;
++ return ttm_fbdev_mmap(vma, &bo->tbo);
+ }
+
+-int radeon_object_get_surface_reg(struct radeon_object *robj)
++int radeon_bo_get_surface_reg(struct radeon_bo *bo)
+ {
+- struct radeon_device *rdev = robj->rdev;
++ struct radeon_device *rdev = bo->rdev;
+ struct radeon_surface_reg *reg;
+- struct radeon_object *old_object;
++ struct radeon_bo *old_object;
+ int steal;
+ int i;
+
+- if (!robj->tiling_flags)
++ BUG_ON(!atomic_read(&bo->tbo.reserved));
++
++ if (!bo->tiling_flags)
+ return 0;
+
+- if (robj->surface_reg >= 0) {
+- reg = &rdev->surface_regs[robj->surface_reg];
+- i = robj->surface_reg;
++ if (bo->surface_reg >= 0) {
++ reg = &rdev->surface_regs[bo->surface_reg];
++ i = bo->surface_reg;
+ goto out;
+ }
+
+@@ -523,10 +385,10 @@ int radeon_object_get_surface_reg(struct radeon_object *robj)
+ for (i = 0; i < RADEON_GEM_MAX_SURFACES; i++) {
+
+ reg = &rdev->surface_regs[i];
+- if (!reg->robj)
++ if (!reg->bo)
+ break;
+
+- old_object = reg->robj;
++ old_object = reg->bo;
+ if (old_object->pin_count == 0)
+ steal = i;
+ }
+@@ -537,91 +399,107 @@ int radeon_object_get_surface_reg(struct radeon_object *robj)
+ return -ENOMEM;
+ /* find someone with a surface reg and nuke their BO */
+ reg = &rdev->surface_regs[steal];
+- old_object = reg->robj;
++ old_object = reg->bo;
+ /* blow away the mapping */
+ DRM_DEBUG("stealing surface reg %d from %p\n", steal, old_object);
+- ttm_bo_unmap_virtual(&old_object->tobj);
++ ttm_bo_unmap_virtual(&old_object->tbo);
+ old_object->surface_reg = -1;
+ i = steal;
+ }
+
+- robj->surface_reg = i;
+- reg->robj = robj;
++ bo->surface_reg = i;
++ reg->bo = bo;
+
+ out:
+- radeon_set_surface_reg(rdev, i, robj->tiling_flags, robj->pitch,
+- robj->tobj.mem.mm_node->start << PAGE_SHIFT,
+- robj->tobj.num_pages << PAGE_SHIFT);
++ radeon_set_surface_reg(rdev, i, bo->tiling_flags, bo->pitch,
++ bo->tbo.mem.mm_node->start << PAGE_SHIFT,
++ bo->tbo.num_pages << PAGE_SHIFT);
+ return 0;
+ }
+
+-void radeon_object_clear_surface_reg(struct radeon_object *robj)
++static void radeon_bo_clear_surface_reg(struct radeon_bo *bo)
+ {
+- struct radeon_device *rdev = robj->rdev;
++ struct radeon_device *rdev = bo->rdev;
+ struct radeon_surface_reg *reg;
+
+- if (robj->surface_reg == -1)
++ if (bo->surface_reg == -1)
+ return;
+
+- reg = &rdev->surface_regs[robj->surface_reg];
+- radeon_clear_surface_reg(rdev, robj->surface_reg);
++ reg = &rdev->surface_regs[bo->surface_reg];
++ radeon_clear_surface_reg(rdev, bo->surface_reg);
+
+- reg->robj = NULL;
+- robj->surface_reg = -1;
++ reg->bo = NULL;
++ bo->surface_reg = -1;
+ }
+
+-void radeon_object_set_tiling_flags(struct radeon_object *robj,
+- uint32_t tiling_flags, uint32_t pitch)
++int radeon_bo_set_tiling_flags(struct radeon_bo *bo,
++ uint32_t tiling_flags, uint32_t pitch)
+ {
+- robj->tiling_flags = tiling_flags;
+- robj->pitch = pitch;
++ int r;
++
++ r = radeon_bo_reserve(bo, false);
++ if (unlikely(r != 0))
++ return r;
++ bo->tiling_flags = tiling_flags;
++ bo->pitch = pitch;
++ radeon_bo_unreserve(bo);
++ return 0;
+ }
+
+-void radeon_object_get_tiling_flags(struct radeon_object *robj,
+- uint32_t *tiling_flags,
+- uint32_t *pitch)
++void radeon_bo_get_tiling_flags(struct radeon_bo *bo,
++ uint32_t *tiling_flags,
++ uint32_t *pitch)
+ {
++ BUG_ON(!atomic_read(&bo->tbo.reserved));
+ if (tiling_flags)
+- *tiling_flags = robj->tiling_flags;
++ *tiling_flags = bo->tiling_flags;
+ if (pitch)
+- *pitch = robj->pitch;
++ *pitch = bo->pitch;
+ }
+
+-int radeon_object_check_tiling(struct radeon_object *robj, bool has_moved,
+- bool force_drop)
++int radeon_bo_check_tiling(struct radeon_bo *bo, bool has_moved,
++ bool force_drop)
+ {
+- if (!(robj->tiling_flags & RADEON_TILING_SURFACE))
++ BUG_ON(!atomic_read(&bo->tbo.reserved));
++
++ if (!(bo->tiling_flags & RADEON_TILING_SURFACE))
+ return 0;
+
+ if (force_drop) {
+- radeon_object_clear_surface_reg(robj);
++ radeon_bo_clear_surface_reg(bo);
+ return 0;
+ }
+
+- if (robj->tobj.mem.mem_type != TTM_PL_VRAM) {
++ if (bo->tbo.mem.mem_type != TTM_PL_VRAM) {
+ if (!has_moved)
+ return 0;
+
+- if (robj->surface_reg >= 0)
+- radeon_object_clear_surface_reg(robj);
++ if (bo->surface_reg >= 0)
++ radeon_bo_clear_surface_reg(bo);
+ return 0;
+ }
+
+- if ((robj->surface_reg >= 0) && !has_moved)
++ if ((bo->surface_reg >= 0) && !has_moved)
+ return 0;
+
+- return radeon_object_get_surface_reg(robj);
++ return radeon_bo_get_surface_reg(bo);
+ }
+
+ void radeon_bo_move_notify(struct ttm_buffer_object *bo,
+- struct ttm_mem_reg *mem)
++ struct ttm_mem_reg *mem)
+ {
+- struct radeon_object *robj = container_of(bo, struct radeon_object, tobj);
+- radeon_object_check_tiling(robj, 0, 1);
++ struct radeon_bo *rbo;
++ if (!radeon_ttm_bo_is_radeon_bo(bo))
++ return;
++ rbo = container_of(bo, struct radeon_bo, tbo);
++ radeon_bo_check_tiling(rbo, 0, 1);
+ }
+
+ void radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo)
+ {
+- struct radeon_object *robj = container_of(bo, struct radeon_object, tobj);
+- radeon_object_check_tiling(robj, 0, 0);
++ struct radeon_bo *rbo;
++ if (!radeon_ttm_bo_is_radeon_bo(bo))
++ return;
++ rbo = container_of(bo, struct radeon_bo, tbo);
++ radeon_bo_check_tiling(rbo, 0, 0);
+ }
+diff --git a/drivers/gpu/drm/radeon/radeon_object.h b/drivers/gpu/drm/radeon/radeon_object.h
+index 10e8af6..7ab43de 100644
+--- a/drivers/gpu/drm/radeon/radeon_object.h
++++ b/drivers/gpu/drm/radeon/radeon_object.h
+@@ -28,19 +28,146 @@
+ #ifndef __RADEON_OBJECT_H__
+ #define __RADEON_OBJECT_H__
+
+-#include <ttm/ttm_bo_api.h>
+-#include <ttm/ttm_bo_driver.h>
+-#include <ttm/ttm_placement.h>
+-#include <ttm/ttm_module.h>
++#include <drm/radeon_drm.h>
++#include "radeon.h"
+
+-/*
+- * TTM.
++/**
++ * radeon_mem_type_to_domain - return domain corresponding to mem_type
++ * @mem_type: ttm memory type
++ *
++ * Returns corresponding domain of the ttm mem_type
++ */
++static inline unsigned radeon_mem_type_to_domain(u32 mem_type)
++{
++ switch (mem_type) {
++ case TTM_PL_VRAM:
++ return RADEON_GEM_DOMAIN_VRAM;
++ case TTM_PL_TT:
++ return RADEON_GEM_DOMAIN_GTT;
++ case TTM_PL_SYSTEM:
++ return RADEON_GEM_DOMAIN_CPU;
++ default:
++ break;
++ }
++ return 0;
++}
++
++/**
++ * radeon_bo_reserve - reserve bo
++ * @bo: bo structure
++ * @no_wait: don't sleep while trying to reserve (return -EBUSY)
++ *
++ * Returns:
++ * -EBUSY: buffer is busy and @no_wait is true
++ * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by
++ * a signal. Release all buffer reservations and return to user-space.
+ */
+-struct radeon_mman {
+- struct ttm_bo_global_ref bo_global_ref;
+- struct ttm_global_reference mem_global_ref;
+- bool mem_global_referenced;
+- struct ttm_bo_device bdev;
+-};
++static inline int radeon_bo_reserve(struct radeon_bo *bo, bool no_wait)
++{
++ int r;
++
++ r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0);
++ if (unlikely(r != 0)) {
++ if (r != -ERESTARTSYS)
++ dev_err(bo->rdev->dev, "%p reserve failed\n", bo);
++ return r;
++ }
++ return 0;
++}
++
++static inline void radeon_bo_unreserve(struct radeon_bo *bo)
++{
++ ttm_bo_unreserve(&bo->tbo);
++}
++
++/**
++ * radeon_bo_gpu_offset - return GPU offset of bo
++ * @bo: radeon object for which we query the offset
++ *
++ * Returns current GPU offset of the object.
++ *
++ * Note: object should either be pinned or reserved when calling this
++ * function, it might be usefull to add check for this for debugging.
++ */
++static inline u64 radeon_bo_gpu_offset(struct radeon_bo *bo)
++{
++ return bo->tbo.offset;
++}
++
++static inline unsigned long radeon_bo_size(struct radeon_bo *bo)
++{
++ return bo->tbo.num_pages << PAGE_SHIFT;
++}
++
++static inline bool radeon_bo_is_reserved(struct radeon_bo *bo)
++{
++ return !!atomic_read(&bo->tbo.reserved);
++}
++
++/**
++ * radeon_bo_mmap_offset - return mmap offset of bo
++ * @bo: radeon object for which we query the offset
++ *
++ * Returns mmap offset of the object.
++ *
++ * Note: addr_space_offset is constant after ttm bo init thus isn't protected
++ * by any lock.
++ */
++static inline u64 radeon_bo_mmap_offset(struct radeon_bo *bo)
++{
++ return bo->tbo.addr_space_offset;
++}
++
++static inline int radeon_bo_wait(struct radeon_bo *bo, u32 *mem_type,
++ bool no_wait)
++{
++ int r;
++
++ r = ttm_bo_reserve(&bo->tbo, true, no_wait, false, 0);
++ if (unlikely(r != 0)) {
++ if (r != -ERESTARTSYS)
++ dev_err(bo->rdev->dev, "%p reserve failed for wait\n", bo);
++ return r;
++ }
++ spin_lock(&bo->tbo.lock);
++ if (mem_type)
++ *mem_type = bo->tbo.mem.mem_type;
++ if (bo->tbo.sync_obj)
++ r = ttm_bo_wait(&bo->tbo, true, true, no_wait);
++ spin_unlock(&bo->tbo.lock);
++ ttm_bo_unreserve(&bo->tbo);
++ return r;
++}
+
++extern int radeon_bo_create(struct radeon_device *rdev,
++ struct drm_gem_object *gobj, unsigned long size,
++ bool kernel, u32 domain,
++ struct radeon_bo **bo_ptr);
++extern int radeon_bo_kmap(struct radeon_bo *bo, void **ptr);
++extern void radeon_bo_kunmap(struct radeon_bo *bo);
++extern void radeon_bo_unref(struct radeon_bo **bo);
++extern int radeon_bo_pin(struct radeon_bo *bo, u32 domain, u64 *gpu_addr);
++extern int radeon_bo_unpin(struct radeon_bo *bo);
++extern int radeon_bo_evict_vram(struct radeon_device *rdev);
++extern void radeon_bo_force_delete(struct radeon_device *rdev);
++extern int radeon_bo_init(struct radeon_device *rdev);
++extern void radeon_bo_fini(struct radeon_device *rdev);
++extern void radeon_bo_list_add_object(struct radeon_bo_list *lobj,
++ struct list_head *head);
++extern int radeon_bo_list_reserve(struct list_head *head);
++extern void radeon_bo_list_unreserve(struct list_head *head);
++extern int radeon_bo_list_validate(struct list_head *head);
++extern void radeon_bo_list_fence(struct list_head *head, void *fence);
++extern int radeon_bo_fbdev_mmap(struct radeon_bo *bo,
++ struct vm_area_struct *vma);
++extern int radeon_bo_set_tiling_flags(struct radeon_bo *bo,
++ u32 tiling_flags, u32 pitch);
++extern void radeon_bo_get_tiling_flags(struct radeon_bo *bo,
++ u32 *tiling_flags, u32 *pitch);
++extern int radeon_bo_check_tiling(struct radeon_bo *bo, bool has_moved,
++ bool force_drop);
++extern void radeon_bo_move_notify(struct ttm_buffer_object *bo,
++ struct ttm_mem_reg *mem);
++extern void radeon_bo_fault_reserve_notify(struct ttm_buffer_object *bo);
++extern int radeon_bo_get_surface_reg(struct radeon_bo *bo);
+ #endif
+diff --git a/drivers/gpu/drm/radeon/radeon_pm.c b/drivers/gpu/drm/radeon/radeon_pm.c
+index 46146c6..8bce64c 100644
+--- a/drivers/gpu/drm/radeon/radeon_pm.c
++++ b/drivers/gpu/drm/radeon/radeon_pm.c
+@@ -27,7 +27,7 @@ int radeon_debugfs_pm_init(struct radeon_device *rdev);
+ int radeon_pm_init(struct radeon_device *rdev)
+ {
+ if (radeon_debugfs_pm_init(rdev)) {
+- DRM_ERROR("Failed to register debugfs file for CP !\n");
++ DRM_ERROR("Failed to register debugfs file for PM!\n");
+ }
+
+ return 0;
+@@ -44,8 +44,11 @@ static int radeon_debugfs_pm_info(struct seq_file *m, void *data)
+ struct drm_device *dev = node->minor->dev;
+ struct radeon_device *rdev = dev->dev_private;
+
+- seq_printf(m, "engine clock: %u0 Hz\n", radeon_get_engine_clock(rdev));
+- seq_printf(m, "memory clock: %u0 Hz\n", radeon_get_memory_clock(rdev));
++ seq_printf(m, "default engine clock: %u0 kHz\n", rdev->clock.default_sclk);
++ seq_printf(m, "current engine clock: %u0 kHz\n", radeon_get_engine_clock(rdev));
++ seq_printf(m, "default memory clock: %u0 kHz\n", rdev->clock.default_mclk);
++ if (rdev->asic->get_memory_clock)
++ seq_printf(m, "current memory clock: %u0 kHz\n", radeon_get_memory_clock(rdev));
+
+ return 0;
+ }
+diff --git a/drivers/gpu/drm/radeon/radeon_reg.h b/drivers/gpu/drm/radeon/radeon_reg.h
+index 29ab759..6d0a009 100644
+--- a/drivers/gpu/drm/radeon/radeon_reg.h
++++ b/drivers/gpu/drm/radeon/radeon_reg.h
+@@ -887,6 +887,7 @@
+ # define RADEON_FP_PANEL_FORMAT (1 << 3)
+ # define RADEON_FP_EN_TMDS (1 << 7)
+ # define RADEON_FP_DETECT_SENSE (1 << 8)
++# define RADEON_FP_DETECT_INT_POL (1 << 9)
+ # define R200_FP_SOURCE_SEL_MASK (3 << 10)
+ # define R200_FP_SOURCE_SEL_CRTC1 (0 << 10)
+ # define R200_FP_SOURCE_SEL_CRTC2 (1 << 10)
+@@ -894,6 +895,7 @@
+ # define R200_FP_SOURCE_SEL_TRANS (3 << 10)
+ # define RADEON_FP_SEL_CRTC1 (0 << 13)
+ # define RADEON_FP_SEL_CRTC2 (1 << 13)
++# define R300_HPD_SEL(x) ((x) << 13)
+ # define RADEON_FP_CRTC_DONT_SHADOW_HPAR (1 << 15)
+ # define RADEON_FP_CRTC_DONT_SHADOW_VPAR (1 << 16)
+ # define RADEON_FP_CRTC_DONT_SHADOW_HEND (1 << 17)
+@@ -909,6 +911,7 @@
+ # define RADEON_FP2_ON (1 << 2)
+ # define RADEON_FP2_PANEL_FORMAT (1 << 3)
+ # define RADEON_FP2_DETECT_SENSE (1 << 8)
++# define RADEON_FP2_DETECT_INT_POL (1 << 9)
+ # define R200_FP2_SOURCE_SEL_MASK (3 << 10)
+ # define R200_FP2_SOURCE_SEL_CRTC1 (0 << 10)
+ # define R200_FP2_SOURCE_SEL_CRTC2 (1 << 10)
+@@ -988,14 +991,20 @@
+
+ #define RADEON_GEN_INT_CNTL 0x0040
+ # define RADEON_CRTC_VBLANK_MASK (1 << 0)
++# define RADEON_FP_DETECT_MASK (1 << 4)
+ # define RADEON_CRTC2_VBLANK_MASK (1 << 9)
++# define RADEON_FP2_DETECT_MASK (1 << 10)
+ # define RADEON_SW_INT_ENABLE (1 << 25)
+ #define RADEON_GEN_INT_STATUS 0x0044
+ # define AVIVO_DISPLAY_INT_STATUS (1 << 0)
+ # define RADEON_CRTC_VBLANK_STAT (1 << 0)
+ # define RADEON_CRTC_VBLANK_STAT_ACK (1 << 0)
++# define RADEON_FP_DETECT_STAT (1 << 4)
++# define RADEON_FP_DETECT_STAT_ACK (1 << 4)
+ # define RADEON_CRTC2_VBLANK_STAT (1 << 9)
+ # define RADEON_CRTC2_VBLANK_STAT_ACK (1 << 9)
++# define RADEON_FP2_DETECT_STAT (1 << 10)
++# define RADEON_FP2_DETECT_STAT_ACK (1 << 10)
+ # define RADEON_SW_INT_FIRE (1 << 26)
+ # define RADEON_SW_INT_TEST (1 << 25)
+ # define RADEON_SW_INT_TEST_ACK (1 << 25)
+@@ -1051,20 +1060,25 @@
+
+ /* Multimedia I2C bus */
+ #define RADEON_I2C_CNTL_0 0x0090
+-#define RADEON_I2C_DONE (1<<0)
+-#define RADEON_I2C_NACK (1<<1)
+-#define RADEON_I2C_HALT (1<<2)
+-#define RADEON_I2C_SOFT_RST (1<<5)
+-#define RADEON_I2C_DRIVE_EN (1<<6)
+-#define RADEON_I2C_DRIVE_SEL (1<<7)
+-#define RADEON_I2C_START (1<<8)
+-#define RADEON_I2C_STOP (1<<9)
+-#define RADEON_I2C_RECEIVE (1<<10)
+-#define RADEON_I2C_ABORT (1<<11)
+-#define RADEON_I2C_GO (1<<12)
++#define RADEON_I2C_DONE (1 << 0)
++#define RADEON_I2C_NACK (1 << 1)
++#define RADEON_I2C_HALT (1 << 2)
++#define RADEON_I2C_SOFT_RST (1 << 5)
++#define RADEON_I2C_DRIVE_EN (1 << 6)
++#define RADEON_I2C_DRIVE_SEL (1 << 7)
++#define RADEON_I2C_START (1 << 8)
++#define RADEON_I2C_STOP (1 << 9)
++#define RADEON_I2C_RECEIVE (1 << 10)
++#define RADEON_I2C_ABORT (1 << 11)
++#define RADEON_I2C_GO (1 << 12)
++#define RADEON_I2C_PRESCALE_SHIFT 16
+ #define RADEON_I2C_CNTL_1 0x0094
+-#define RADEON_I2C_SEL (1<<16)
+-#define RADEON_I2C_EN (1<<17)
++#define RADEON_I2C_DATA_COUNT_SHIFT 0
++#define RADEON_I2C_ADDR_COUNT_SHIFT 4
++#define RADEON_I2C_INTRA_BYTE_DELAY_SHIFT 8
++#define RADEON_I2C_SEL (1 << 16)
++#define RADEON_I2C_EN (1 << 17)
++#define RADEON_I2C_TIME_LIMIT_SHIFT 24
+ #define RADEON_I2C_DATA 0x0098
+
+ #define RADEON_DVI_I2C_CNTL_0 0x02e0
+@@ -1072,7 +1086,7 @@
+ # define R200_SEL_DDC1 0 /* 0x60 - VGA_DDC */
+ # define R200_SEL_DDC2 1 /* 0x64 - DVI_DDC */
+ # define R200_SEL_DDC3 2 /* 0x68 - MONID_DDC */
+-#define RADEON_DVI_I2C_CNTL_1 0x02e4 /* ? */
++#define RADEON_DVI_I2C_CNTL_1 0x02e4
+ #define RADEON_DVI_I2C_DATA 0x02e8
+
+ #define RADEON_INTERRUPT_LINE 0x0f3c /* PCI */
+@@ -1143,15 +1157,16 @@
+ # define RADEON_IO_MCLK_MAX_DYN_STOP_LAT (1 << 13)
+ # define RADEON_MC_MCLK_DYN_ENABLE (1 << 14)
+ # define RADEON_IO_MCLK_DYN_ENABLE (1 << 15)
+-#define RADEON_LCD_GPIO_MASK 0x01a0
+-#define RADEON_GPIOPAD_EN 0x01a0
+-#define RADEON_LCD_GPIO_Y_REG 0x01a4
+-#define RADEON_MDGPIO_A_REG 0x01ac
+-#define RADEON_MDGPIO_EN_REG 0x01b0
+-#define RADEON_MDGPIO_MASK 0x0198
++
+ #define RADEON_GPIOPAD_MASK 0x0198
+ #define RADEON_GPIOPAD_A 0x019c
+-#define RADEON_MDGPIO_Y_REG 0x01b4
++#define RADEON_GPIOPAD_EN 0x01a0
++#define RADEON_GPIOPAD_Y 0x01a4
++#define RADEON_MDGPIO_MASK 0x01a8
++#define RADEON_MDGPIO_A 0x01ac
++#define RADEON_MDGPIO_EN 0x01b0
++#define RADEON_MDGPIO_Y 0x01b4
++
+ #define RADEON_MEM_ADDR_CONFIG 0x0148
+ #define RADEON_MEM_BASE 0x0f10 /* PCI */
+ #define RADEON_MEM_CNTL 0x0140
+@@ -1360,6 +1375,9 @@
+ #define RADEON_OVR_CLR 0x0230
+ #define RADEON_OVR_WID_LEFT_RIGHT 0x0234
+ #define RADEON_OVR_WID_TOP_BOTTOM 0x0238
++#define RADEON_OVR2_CLR 0x0330
++#define RADEON_OVR2_WID_LEFT_RIGHT 0x0334
++#define RADEON_OVR2_WID_TOP_BOTTOM 0x0338
+
+ /* first capture unit */
+
+diff --git a/drivers/gpu/drm/radeon/radeon_ring.c b/drivers/gpu/drm/radeon/radeon_ring.c
+index 747b4bf..694799f 100644
+--- a/drivers/gpu/drm/radeon/radeon_ring.c
++++ b/drivers/gpu/drm/radeon/radeon_ring.c
+@@ -41,68 +41,55 @@ int radeon_ib_get(struct radeon_device *rdev, struct radeon_ib **ib)
+ {
+ struct radeon_fence *fence;
+ struct radeon_ib *nib;
+- unsigned long i;
+- int r = 0;
++ int r = 0, i, c;
+
+ *ib = NULL;
+ r = radeon_fence_create(rdev, &fence);
+ if (r) {
+- DRM_ERROR("failed to create fence for new IB\n");
++ dev_err(rdev->dev, "failed to create fence for new IB\n");
+ return r;
+ }
+ mutex_lock(&rdev->ib_pool.mutex);
+- i = find_first_zero_bit(rdev->ib_pool.alloc_bm, RADEON_IB_POOL_SIZE);
+- if (i < RADEON_IB_POOL_SIZE) {
+- set_bit(i, rdev->ib_pool.alloc_bm);
+- rdev->ib_pool.ibs[i].length_dw = 0;
+- *ib = &rdev->ib_pool.ibs[i];
+- mutex_unlock(&rdev->ib_pool.mutex);
+- goto out;
++ for (i = rdev->ib_pool.head_id, c = 0, nib = NULL; c < RADEON_IB_POOL_SIZE; c++, i++) {
++ i &= (RADEON_IB_POOL_SIZE - 1);
++ if (rdev->ib_pool.ibs[i].free) {
++ nib = &rdev->ib_pool.ibs[i];
++ break;
++ }
+ }
+- if (list_empty(&rdev->ib_pool.scheduled_ibs)) {
+- /* we go do nothings here */
++ if (nib == NULL) {
++ /* This should never happen, it means we allocated all
++ * IB and haven't scheduled one yet, return EBUSY to
++ * userspace hoping that on ioctl recall we get better
++ * luck
++ */
++ dev_err(rdev->dev, "no free indirect buffer !\n");
+ mutex_unlock(&rdev->ib_pool.mutex);
+- DRM_ERROR("all IB allocated none scheduled.\n");
+- r = -EINVAL;
+- goto out;
++ radeon_fence_unref(&fence);
++ return -EBUSY;
+ }
+- /* get the first ib on the scheduled list */
+- nib = list_entry(rdev->ib_pool.scheduled_ibs.next,
+- struct radeon_ib, list);
+- if (nib->fence == NULL) {
+- /* we go do nothings here */
++ rdev->ib_pool.head_id = (nib->idx + 1) & (RADEON_IB_POOL_SIZE - 1);
++ nib->free = false;
++ if (nib->fence) {
+ mutex_unlock(&rdev->ib_pool.mutex);
+- DRM_ERROR("IB %lu scheduled without a fence.\n", nib->idx);
+- r = -EINVAL;
+- goto out;
+- }
+- mutex_unlock(&rdev->ib_pool.mutex);
+-
+- r = radeon_fence_wait(nib->fence, false);
+- if (r) {
+- DRM_ERROR("radeon: IB(%lu:0x%016lX:%u)\n", nib->idx,
+- (unsigned long)nib->gpu_addr, nib->length_dw);
+- DRM_ERROR("radeon: GPU lockup detected, fail to get a IB\n");
+- goto out;
++ r = radeon_fence_wait(nib->fence, false);
++ if (r) {
++ dev_err(rdev->dev, "error waiting fence of IB(%u:0x%016lX:%u)\n",
++ nib->idx, (unsigned long)nib->gpu_addr, nib->length_dw);
++ mutex_lock(&rdev->ib_pool.mutex);
++ nib->free = true;
++ mutex_unlock(&rdev->ib_pool.mutex);
++ radeon_fence_unref(&fence);
++ return r;
++ }
++ mutex_lock(&rdev->ib_pool.mutex);
+ }
+ radeon_fence_unref(&nib->fence);
+-
++ nib->fence = fence;
+ nib->length_dw = 0;
+-
+- /* scheduled list is accessed here */
+- mutex_lock(&rdev->ib_pool.mutex);
+- list_del(&nib->list);
+- INIT_LIST_HEAD(&nib->list);
+ mutex_unlock(&rdev->ib_pool.mutex);
+-
+ *ib = nib;
+-out:
+- if (r) {
+- radeon_fence_unref(&fence);
+- } else {
+- (*ib)->fence = fence;
+- }
+- return r;
++ return 0;
+ }
+
+ void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib **ib)
+@@ -114,18 +101,7 @@ void radeon_ib_free(struct radeon_device *rdev, struct radeon_ib **ib)
+ return;
+ }
+ mutex_lock(&rdev->ib_pool.mutex);
+- if (!list_empty(&tmp->list) && !radeon_fence_signaled(tmp->fence)) {
+- /* IB is scheduled & not signaled don't do anythings */
+- mutex_unlock(&rdev->ib_pool.mutex);
+- return;
+- }
+- list_del(&tmp->list);
+- INIT_LIST_HEAD(&tmp->list);
+- if (tmp->fence)
+- radeon_fence_unref(&tmp->fence);
+-
+- tmp->length_dw = 0;
+- clear_bit(tmp->idx, rdev->ib_pool.alloc_bm);
++ tmp->free = true;
+ mutex_unlock(&rdev->ib_pool.mutex);
+ }
+
+@@ -135,7 +111,7 @@ int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib)
+
+ if (!ib->length_dw || !rdev->cp.ready) {
+ /* TODO: Nothings in the ib we should report. */
+- DRM_ERROR("radeon: couldn't schedule IB(%lu).\n", ib->idx);
++ DRM_ERROR("radeon: couldn't schedule IB(%u).\n", ib->idx);
+ return -EINVAL;
+ }
+
+@@ -148,7 +124,8 @@ int radeon_ib_schedule(struct radeon_device *rdev, struct radeon_ib *ib)
+ radeon_ring_ib_execute(rdev, ib);
+ radeon_fence_emit(rdev, ib->fence);
+ mutex_lock(&rdev->ib_pool.mutex);
+- list_add_tail(&ib->list, &rdev->ib_pool.scheduled_ibs);
++ /* once scheduled IB is considered free and protected by the fence */
++ ib->free = true;
+ mutex_unlock(&rdev->ib_pool.mutex);
+ radeon_ring_unlock_commit(rdev);
+ return 0;
+@@ -164,20 +141,24 @@ int radeon_ib_pool_init(struct radeon_device *rdev)
+ if (rdev->ib_pool.robj)
+ return 0;
+ /* Allocate 1M object buffer */
+- INIT_LIST_HEAD(&rdev->ib_pool.scheduled_ibs);
+- r = radeon_object_create(rdev, NULL, RADEON_IB_POOL_SIZE*64*1024,
+- true, RADEON_GEM_DOMAIN_GTT,
+- false, &rdev->ib_pool.robj);
++ r = radeon_bo_create(rdev, NULL, RADEON_IB_POOL_SIZE*64*1024,
++ true, RADEON_GEM_DOMAIN_GTT,
++ &rdev->ib_pool.robj);
+ if (r) {
+ DRM_ERROR("radeon: failed to ib pool (%d).\n", r);
+ return r;
+ }
+- r = radeon_object_pin(rdev->ib_pool.robj, RADEON_GEM_DOMAIN_GTT, &gpu_addr);
++ r = radeon_bo_reserve(rdev->ib_pool.robj, false);
++ if (unlikely(r != 0))
++ return r;
++ r = radeon_bo_pin(rdev->ib_pool.robj, RADEON_GEM_DOMAIN_GTT, &gpu_addr);
+ if (r) {
++ radeon_bo_unreserve(rdev->ib_pool.robj);
+ DRM_ERROR("radeon: failed to pin ib pool (%d).\n", r);
+ return r;
+ }
+- r = radeon_object_kmap(rdev->ib_pool.robj, &ptr);
++ r = radeon_bo_kmap(rdev->ib_pool.robj, &ptr);
++ radeon_bo_unreserve(rdev->ib_pool.robj);
+ if (r) {
+ DRM_ERROR("radeon: failed to map ib poll (%d).\n", r);
+ return r;
+@@ -190,9 +171,9 @@ int radeon_ib_pool_init(struct radeon_device *rdev)
+ rdev->ib_pool.ibs[i].ptr = ptr + offset;
+ rdev->ib_pool.ibs[i].idx = i;
+ rdev->ib_pool.ibs[i].length_dw = 0;
+- INIT_LIST_HEAD(&rdev->ib_pool.ibs[i].list);
++ rdev->ib_pool.ibs[i].free = true;
+ }
+- bitmap_zero(rdev->ib_pool.alloc_bm, RADEON_IB_POOL_SIZE);
++ rdev->ib_pool.head_id = 0;
+ rdev->ib_pool.ready = true;
+ DRM_INFO("radeon: ib pool ready.\n");
+ if (radeon_debugfs_ib_init(rdev)) {
+@@ -203,14 +184,20 @@ int radeon_ib_pool_init(struct radeon_device *rdev)
+
+ void radeon_ib_pool_fini(struct radeon_device *rdev)
+ {
++ int r;
++
+ if (!rdev->ib_pool.ready) {
+ return;
+ }
+ mutex_lock(&rdev->ib_pool.mutex);
+- bitmap_zero(rdev->ib_pool.alloc_bm, RADEON_IB_POOL_SIZE);
+ if (rdev->ib_pool.robj) {
+- radeon_object_kunmap(rdev->ib_pool.robj);
+- radeon_object_unref(&rdev->ib_pool.robj);
++ r = radeon_bo_reserve(rdev->ib_pool.robj, false);
++ if (likely(r == 0)) {
++ radeon_bo_kunmap(rdev->ib_pool.robj);
++ radeon_bo_unpin(rdev->ib_pool.robj);
++ radeon_bo_unreserve(rdev->ib_pool.robj);
++ }
++ radeon_bo_unref(&rdev->ib_pool.robj);
+ rdev->ib_pool.robj = NULL;
+ }
+ mutex_unlock(&rdev->ib_pool.mutex);
+@@ -288,29 +275,28 @@ int radeon_ring_init(struct radeon_device *rdev, unsigned ring_size)
+ rdev->cp.ring_size = ring_size;
+ /* Allocate ring buffer */
+ if (rdev->cp.ring_obj == NULL) {
+- r = radeon_object_create(rdev, NULL, rdev->cp.ring_size,
+- true,
+- RADEON_GEM_DOMAIN_GTT,
+- false,
+- &rdev->cp.ring_obj);
++ r = radeon_bo_create(rdev, NULL, rdev->cp.ring_size, true,
++ RADEON_GEM_DOMAIN_GTT,
++ &rdev->cp.ring_obj);
+ if (r) {
+- DRM_ERROR("radeon: failed to create ring buffer (%d).\n", r);
+- mutex_unlock(&rdev->cp.mutex);
++ dev_err(rdev->dev, "(%d) ring create failed\n", r);
+ return r;
+ }
+- r = radeon_object_pin(rdev->cp.ring_obj,
+- RADEON_GEM_DOMAIN_GTT,
+- &rdev->cp.gpu_addr);
++ r = radeon_bo_reserve(rdev->cp.ring_obj, false);
++ if (unlikely(r != 0))
++ return r;
++ r = radeon_bo_pin(rdev->cp.ring_obj, RADEON_GEM_DOMAIN_GTT,
++ &rdev->cp.gpu_addr);
+ if (r) {
+- DRM_ERROR("radeon: failed to pin ring buffer (%d).\n", r);
+- mutex_unlock(&rdev->cp.mutex);
++ radeon_bo_unreserve(rdev->cp.ring_obj);
++ dev_err(rdev->dev, "(%d) ring pin failed\n", r);
+ return r;
+ }
+- r = radeon_object_kmap(rdev->cp.ring_obj,
++ r = radeon_bo_kmap(rdev->cp.ring_obj,
+ (void **)&rdev->cp.ring);
++ radeon_bo_unreserve(rdev->cp.ring_obj);
+ if (r) {
+- DRM_ERROR("radeon: failed to map ring buffer (%d).\n", r);
+- mutex_unlock(&rdev->cp.mutex);
++ dev_err(rdev->dev, "(%d) ring map failed\n", r);
+ return r;
+ }
+ }
+@@ -321,11 +307,17 @@ int radeon_ring_init(struct radeon_device *rdev, unsigned ring_size)
+
+ void radeon_ring_fini(struct radeon_device *rdev)
+ {
++ int r;
++
+ mutex_lock(&rdev->cp.mutex);
+ if (rdev->cp.ring_obj) {
+- radeon_object_kunmap(rdev->cp.ring_obj);
+- radeon_object_unpin(rdev->cp.ring_obj);
+- radeon_object_unref(&rdev->cp.ring_obj);
++ r = radeon_bo_reserve(rdev->cp.ring_obj, false);
++ if (likely(r == 0)) {
++ radeon_bo_kunmap(rdev->cp.ring_obj);
++ radeon_bo_unpin(rdev->cp.ring_obj);
++ radeon_bo_unreserve(rdev->cp.ring_obj);
++ }
++ radeon_bo_unref(&rdev->cp.ring_obj);
+ rdev->cp.ring = NULL;
+ rdev->cp.ring_obj = NULL;
+ }
+@@ -346,7 +338,7 @@ static int radeon_debugfs_ib_info(struct seq_file *m, void *data)
+ if (ib == NULL) {
+ return 0;
+ }
+- seq_printf(m, "IB %04lu\n", ib->idx);
++ seq_printf(m, "IB %04u\n", ib->idx);
+ seq_printf(m, "IB fence %p\n", ib->fence);
+ seq_printf(m, "IB size %05u dwords\n", ib->length_dw);
+ for (i = 0; i < ib->length_dw; i++) {
+diff --git a/drivers/gpu/drm/radeon/radeon_state.c b/drivers/gpu/drm/radeon/radeon_state.c
+index 38537d9..067167c 100644
+--- a/drivers/gpu/drm/radeon/radeon_state.c
++++ b/drivers/gpu/drm/radeon/radeon_state.c
+@@ -1950,7 +1950,7 @@ static void radeon_apply_surface_regs(int surf_index,
+ * Note that refcount can be at most 2, since during a free refcount=3
+ * might mean we have to allocate a new surface which might not always
+ * be available.
+- * For example : we allocate three contigous surfaces ABC. If B is
++ * For example : we allocate three contiguous surfaces ABC. If B is
+ * freed, we suddenly need two surfaces to store A and C, which might
+ * not always be available.
+ */
+diff --git a/drivers/gpu/drm/radeon/radeon_test.c b/drivers/gpu/drm/radeon/radeon_test.c
+index c8942ca..9f5e2f9 100644
+--- a/drivers/gpu/drm/radeon/radeon_test.c
++++ b/drivers/gpu/drm/radeon/radeon_test.c
+@@ -30,8 +30,8 @@
+ /* Test BO GTT->VRAM and VRAM->GTT GPU copies across the whole GTT aperture */
+ void radeon_test_moves(struct radeon_device *rdev)
+ {
+- struct radeon_object *vram_obj = NULL;
+- struct radeon_object **gtt_obj = NULL;
++ struct radeon_bo *vram_obj = NULL;
++ struct radeon_bo **gtt_obj = NULL;
+ struct radeon_fence *fence = NULL;
+ uint64_t gtt_addr, vram_addr;
+ unsigned i, n, size;
+@@ -52,38 +52,42 @@ void radeon_test_moves(struct radeon_device *rdev)
+ goto out_cleanup;
+ }
+
+- r = radeon_object_create(rdev, NULL, size, true, RADEON_GEM_DOMAIN_VRAM,
+- false, &vram_obj);
++ r = radeon_bo_create(rdev, NULL, size, true, RADEON_GEM_DOMAIN_VRAM,
++ &vram_obj);
+ if (r) {
+ DRM_ERROR("Failed to create VRAM object\n");
+ goto out_cleanup;
+ }
+-
+- r = radeon_object_pin(vram_obj, RADEON_GEM_DOMAIN_VRAM, &vram_addr);
++ r = radeon_bo_reserve(vram_obj, false);
++ if (unlikely(r != 0))
++ goto out_cleanup;
++ r = radeon_bo_pin(vram_obj, RADEON_GEM_DOMAIN_VRAM, &vram_addr);
+ if (r) {
+ DRM_ERROR("Failed to pin VRAM object\n");
+ goto out_cleanup;
+ }
+-
+ for (i = 0; i < n; i++) {
+ void *gtt_map, *vram_map;
+ void **gtt_start, **gtt_end;
+ void **vram_start, **vram_end;
+
+- r = radeon_object_create(rdev, NULL, size, true,
+- RADEON_GEM_DOMAIN_GTT, false, gtt_obj + i);
++ r = radeon_bo_create(rdev, NULL, size, true,
++ RADEON_GEM_DOMAIN_GTT, gtt_obj + i);
+ if (r) {
+ DRM_ERROR("Failed to create GTT object %d\n", i);
+ goto out_cleanup;
+ }
+
+- r = radeon_object_pin(gtt_obj[i], RADEON_GEM_DOMAIN_GTT, &gtt_addr);
++ r = radeon_bo_reserve(gtt_obj[i], false);
++ if (unlikely(r != 0))
++ goto out_cleanup;
++ r = radeon_bo_pin(gtt_obj[i], RADEON_GEM_DOMAIN_GTT, &gtt_addr);
+ if (r) {
+ DRM_ERROR("Failed to pin GTT object %d\n", i);
+ goto out_cleanup;
+ }
+
+- r = radeon_object_kmap(gtt_obj[i], &gtt_map);
++ r = radeon_bo_kmap(gtt_obj[i], &gtt_map);
+ if (r) {
+ DRM_ERROR("Failed to map GTT object %d\n", i);
+ goto out_cleanup;
+@@ -94,7 +98,7 @@ void radeon_test_moves(struct radeon_device *rdev)
+ gtt_start++)
+ *gtt_start = gtt_start;
+
+- radeon_object_kunmap(gtt_obj[i]);
++ radeon_bo_kunmap(gtt_obj[i]);
+
+ r = radeon_fence_create(rdev, &fence);
+ if (r) {
+@@ -116,7 +120,7 @@ void radeon_test_moves(struct radeon_device *rdev)
+
+ radeon_fence_unref(&fence);
+
+- r = radeon_object_kmap(vram_obj, &vram_map);
++ r = radeon_bo_kmap(vram_obj, &vram_map);
+ if (r) {
+ DRM_ERROR("Failed to map VRAM object after copy %d\n", i);
+ goto out_cleanup;
+@@ -131,13 +135,13 @@ void radeon_test_moves(struct radeon_device *rdev)
+ "expected 0x%p (GTT map 0x%p-0x%p)\n",
+ i, *vram_start, gtt_start, gtt_map,
+ gtt_end);
+- radeon_object_kunmap(vram_obj);
++ radeon_bo_kunmap(vram_obj);
+ goto out_cleanup;
+ }
+ *vram_start = vram_start;
+ }
+
+- radeon_object_kunmap(vram_obj);
++ radeon_bo_kunmap(vram_obj);
+
+ r = radeon_fence_create(rdev, &fence);
+ if (r) {
+@@ -159,7 +163,7 @@ void radeon_test_moves(struct radeon_device *rdev)
+
+ radeon_fence_unref(&fence);
+
+- r = radeon_object_kmap(gtt_obj[i], &gtt_map);
++ r = radeon_bo_kmap(gtt_obj[i], &gtt_map);
+ if (r) {
+ DRM_ERROR("Failed to map GTT object after copy %d\n", i);
+ goto out_cleanup;
+@@ -174,12 +178,12 @@ void radeon_test_moves(struct radeon_device *rdev)
+ "expected 0x%p (VRAM map 0x%p-0x%p)\n",
+ i, *gtt_start, vram_start, vram_map,
+ vram_end);
+- radeon_object_kunmap(gtt_obj[i]);
++ radeon_bo_kunmap(gtt_obj[i]);
+ goto out_cleanup;
+ }
+ }
+
+- radeon_object_kunmap(gtt_obj[i]);
++ radeon_bo_kunmap(gtt_obj[i]);
+
+ DRM_INFO("Tested GTT->VRAM and VRAM->GTT copy for GTT offset 0x%llx\n",
+ gtt_addr - rdev->mc.gtt_location);
+@@ -187,14 +191,20 @@ void radeon_test_moves(struct radeon_device *rdev)
+
+ out_cleanup:
+ if (vram_obj) {
+- radeon_object_unpin(vram_obj);
+- radeon_object_unref(&vram_obj);
++ if (radeon_bo_is_reserved(vram_obj)) {
++ radeon_bo_unpin(vram_obj);
++ radeon_bo_unreserve(vram_obj);
++ }
++ radeon_bo_unref(&vram_obj);
+ }
+ if (gtt_obj) {
+ for (i = 0; i < n; i++) {
+ if (gtt_obj[i]) {
+- radeon_object_unpin(gtt_obj[i]);
+- radeon_object_unref(&gtt_obj[i]);
++ if (radeon_bo_is_reserved(gtt_obj[i])) {
++ radeon_bo_unpin(gtt_obj[i]);
++ radeon_bo_unreserve(gtt_obj[i]);
++ }
++ radeon_bo_unref(&gtt_obj[i]);
+ }
+ }
+ kfree(gtt_obj);
+@@ -206,4 +216,3 @@ out_cleanup:
+ printk(KERN_WARNING "Error while testing BO move.\n");
+ }
+ }
+-
+diff --git a/drivers/gpu/drm/radeon/radeon_ttm.c b/drivers/gpu/drm/radeon/radeon_ttm.c
+index 1381e06..58b5adf 100644
+--- a/drivers/gpu/drm/radeon/radeon_ttm.c
++++ b/drivers/gpu/drm/radeon/radeon_ttm.c
+@@ -150,7 +150,7 @@ static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
+ man->default_caching = TTM_PL_FLAG_CACHED;
+ break;
+ case TTM_PL_TT:
+- man->gpu_offset = 0;
++ man->gpu_offset = rdev->mc.gtt_location;
+ man->available_caching = TTM_PL_MASK_CACHING;
+ man->default_caching = TTM_PL_FLAG_CACHED;
+ man->flags = TTM_MEMTYPE_FLAG_MAPPABLE | TTM_MEMTYPE_FLAG_CMA;
+@@ -180,7 +180,7 @@ static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
+ break;
+ case TTM_PL_VRAM:
+ /* "On-card" video ram */
+- man->gpu_offset = 0;
++ man->gpu_offset = rdev->mc.vram_location;
+ man->flags = TTM_MEMTYPE_FLAG_FIXED |
+ TTM_MEMTYPE_FLAG_NEEDS_IOREMAP |
+ TTM_MEMTYPE_FLAG_MAPPABLE;
+@@ -197,16 +197,34 @@ static int radeon_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
+ return 0;
+ }
+
+-static uint32_t radeon_evict_flags(struct ttm_buffer_object *bo)
++static void radeon_evict_flags(struct ttm_buffer_object *bo,
++ struct ttm_placement *placement)
+ {
+- uint32_t cur_placement = bo->mem.placement & ~TTM_PL_MASK_MEMTYPE;
++ struct radeon_bo *rbo;
++ static u32 placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_SYSTEM;
+
++ if (!radeon_ttm_bo_is_radeon_bo(bo)) {
++ placement->fpfn = 0;
++ placement->lpfn = 0;
++ placement->placement = &placements;
++ placement->busy_placement = &placements;
++ placement->num_placement = 1;
++ placement->num_busy_placement = 1;
++ return;
++ }
++ rbo = container_of(bo, struct radeon_bo, tbo);
+ switch (bo->mem.mem_type) {
++ case TTM_PL_VRAM:
++ if (rbo->rdev->cp.ready == false)
++ radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_CPU);
++ else
++ radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_GTT);
++ break;
++ case TTM_PL_TT:
+ default:
+- return (cur_placement & ~TTM_PL_MASK_CACHING) |
+- TTM_PL_FLAG_SYSTEM |
+- TTM_PL_FLAG_CACHED;
++ radeon_ttm_placement_from_domain(rbo, RADEON_GEM_DOMAIN_CPU);
+ }
++ *placement = rbo->placement;
+ }
+
+ static int radeon_verify_access(struct ttm_buffer_object *bo, struct file *filp)
+@@ -283,14 +301,21 @@ static int radeon_move_vram_ram(struct ttm_buffer_object *bo,
+ struct radeon_device *rdev;
+ struct ttm_mem_reg *old_mem = &bo->mem;
+ struct ttm_mem_reg tmp_mem;
+- uint32_t proposed_placement;
++ u32 placements;
++ struct ttm_placement placement;
+ int r;
+
+ rdev = radeon_get_rdev(bo->bdev);
+ tmp_mem = *new_mem;
+ tmp_mem.mm_node = NULL;
+- proposed_placement = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
+- r = ttm_bo_mem_space(bo, proposed_placement, &tmp_mem,
++ placement.fpfn = 0;
++ placement.lpfn = 0;
++ placement.num_placement = 1;
++ placement.placement = &placements;
++ placement.num_busy_placement = 1;
++ placement.busy_placement = &placements;
++ placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
++ r = ttm_bo_mem_space(bo, &placement, &tmp_mem,
+ interruptible, no_wait);
+ if (unlikely(r)) {
+ return r;
+@@ -329,15 +354,21 @@ static int radeon_move_ram_vram(struct ttm_buffer_object *bo,
+ struct radeon_device *rdev;
+ struct ttm_mem_reg *old_mem = &bo->mem;
+ struct ttm_mem_reg tmp_mem;
+- uint32_t proposed_flags;
++ struct ttm_placement placement;
++ u32 placements;
+ int r;
+
+ rdev = radeon_get_rdev(bo->bdev);
+ tmp_mem = *new_mem;
+ tmp_mem.mm_node = NULL;
+- proposed_flags = TTM_PL_FLAG_TT | TTM_PL_MASK_CACHING;
+- r = ttm_bo_mem_space(bo, proposed_flags, &tmp_mem,
+- interruptible, no_wait);
++ placement.fpfn = 0;
++ placement.lpfn = 0;
++ placement.num_placement = 1;
++ placement.placement = &placements;
++ placement.num_busy_placement = 1;
++ placement.busy_placement = &placements;
++ placements = TTM_PL_MASK_CACHING | TTM_PL_FLAG_TT;
++ r = ttm_bo_mem_space(bo, &placement, &tmp_mem, interruptible, no_wait);
+ if (unlikely(r)) {
+ return r;
+ }
+@@ -378,7 +409,7 @@ static int radeon_bo_move(struct ttm_buffer_object *bo,
+ new_mem->mem_type == TTM_PL_SYSTEM) ||
+ (old_mem->mem_type == TTM_PL_SYSTEM &&
+ new_mem->mem_type == TTM_PL_TT)) {
+- /* bind is enought */
++ /* bind is enough */
+ radeon_move_null(bo, new_mem);
+ return 0;
+ }
+@@ -407,18 +438,6 @@ memcpy:
+ return r;
+ }
+
+-const uint32_t radeon_mem_prios[] = {
+- TTM_PL_VRAM,
+- TTM_PL_TT,
+- TTM_PL_SYSTEM,
+-};
+-
+-const uint32_t radeon_busy_prios[] = {
+- TTM_PL_TT,
+- TTM_PL_VRAM,
+- TTM_PL_SYSTEM,
+-};
+-
+ static int radeon_sync_obj_wait(void *sync_obj, void *sync_arg,
+ bool lazy, bool interruptible)
+ {
+@@ -446,10 +465,6 @@ static bool radeon_sync_obj_signaled(void *sync_obj, void *sync_arg)
+ }
+
+ static struct ttm_bo_driver radeon_bo_driver = {
+- .mem_type_prio = radeon_mem_prios,
+- .mem_busy_prio = radeon_busy_prios,
+- .num_mem_type_prio = ARRAY_SIZE(radeon_mem_prios),
+- .num_mem_busy_prio = ARRAY_SIZE(radeon_busy_prios),
+ .create_ttm_backend_entry = &radeon_create_ttm_backend_entry,
+ .invalidate_caches = &radeon_invalidate_caches,
+ .init_mem_type = &radeon_init_mem_type,
+@@ -482,27 +497,32 @@ int radeon_ttm_init(struct radeon_device *rdev)
+ DRM_ERROR("failed initializing buffer object driver(%d).\n", r);
+ return r;
+ }
+- r = ttm_bo_init_mm(&rdev->mman.bdev, TTM_PL_VRAM, 0,
+- ((rdev->mc.real_vram_size) >> PAGE_SHIFT));
++ rdev->mman.initialized = true;
++ r = ttm_bo_init_mm(&rdev->mman.bdev, TTM_PL_VRAM,
++ rdev->mc.real_vram_size >> PAGE_SHIFT);
+ if (r) {
+ DRM_ERROR("Failed initializing VRAM heap.\n");
+ return r;
+ }
+- r = radeon_object_create(rdev, NULL, 256 * 1024, true,
+- RADEON_GEM_DOMAIN_VRAM, false,
+- &rdev->stollen_vga_memory);
++ r = radeon_bo_create(rdev, NULL, 256 * 1024, true,
++ RADEON_GEM_DOMAIN_VRAM,
++ &rdev->stollen_vga_memory);
+ if (r) {
+ return r;
+ }
+- r = radeon_object_pin(rdev->stollen_vga_memory, RADEON_GEM_DOMAIN_VRAM, NULL);
++ r = radeon_bo_reserve(rdev->stollen_vga_memory, false);
++ if (r)
++ return r;
++ r = radeon_bo_pin(rdev->stollen_vga_memory, RADEON_GEM_DOMAIN_VRAM, NULL);
++ radeon_bo_unreserve(rdev->stollen_vga_memory);
+ if (r) {
+- radeon_object_unref(&rdev->stollen_vga_memory);
++ radeon_bo_unref(&rdev->stollen_vga_memory);
+ return r;
+ }
+ DRM_INFO("radeon: %uM of VRAM memory ready\n",
+ (unsigned)rdev->mc.real_vram_size / (1024 * 1024));
+- r = ttm_bo_init_mm(&rdev->mman.bdev, TTM_PL_TT, 0,
+- ((rdev->mc.gtt_size) >> PAGE_SHIFT));
++ r = ttm_bo_init_mm(&rdev->mman.bdev, TTM_PL_TT,
++ rdev->mc.gtt_size >> PAGE_SHIFT);
+ if (r) {
+ DRM_ERROR("Failed initializing GTT heap.\n");
+ return r;
+@@ -523,15 +543,24 @@ int radeon_ttm_init(struct radeon_device *rdev)
+
+ void radeon_ttm_fini(struct radeon_device *rdev)
+ {
++ int r;
++
++ if (!rdev->mman.initialized)
++ return;
+ if (rdev->stollen_vga_memory) {
+- radeon_object_unpin(rdev->stollen_vga_memory);
+- radeon_object_unref(&rdev->stollen_vga_memory);
++ r = radeon_bo_reserve(rdev->stollen_vga_memory, false);
++ if (r == 0) {
++ radeon_bo_unpin(rdev->stollen_vga_memory);
++ radeon_bo_unreserve(rdev->stollen_vga_memory);
++ }
++ radeon_bo_unref(&rdev->stollen_vga_memory);
+ }
+ ttm_bo_clean_mm(&rdev->mman.bdev, TTM_PL_VRAM);
+ ttm_bo_clean_mm(&rdev->mman.bdev, TTM_PL_TT);
+ ttm_bo_device_release(&rdev->mman.bdev);
+ radeon_gart_fini(rdev);
+ radeon_ttm_global_fini(rdev);
++ rdev->mman.initialized = false;
+ DRM_INFO("radeon: ttm finalized\n");
+ }
+
+diff --git a/drivers/gpu/drm/radeon/reg_srcs/r200 b/drivers/gpu/drm/radeon/reg_srcs/r200
+index 6021c88..c29ac43 100644
+--- a/drivers/gpu/drm/radeon/reg_srcs/r200
++++ b/drivers/gpu/drm/radeon/reg_srcs/r200
+@@ -91,6 +91,8 @@ r200 0x3294
+ 0x22b8 SE_TCL_TEX_CYL_WRAP_CTL
+ 0x22c0 SE_TCL_UCP_VERT_BLEND_CNTL
+ 0x22c4 SE_TCL_POINT_SPRITE_CNTL
++0x22d0 SE_PVS_CNTL
++0x22d4 SE_PVS_CONST_CNTL
+ 0x2648 RE_POINTSIZE
+ 0x26c0 RE_TOP_LEFT
+ 0x26c4 RE_MISC
+diff --git a/drivers/gpu/drm/radeon/reg_srcs/r420 b/drivers/gpu/drm/radeon/reg_srcs/r420
+new file mode 100644
+index 0000000..989f7a0
+--- /dev/null
++++ b/drivers/gpu/drm/radeon/reg_srcs/r420
+@@ -0,0 +1,795 @@
++r420 0x4f60
++0x1434 SRC_Y_X
++0x1438 DST_Y_X
++0x143C DST_HEIGHT_WIDTH
++0x146C DP_GUI_MASTER_CNTL
++0x1474 BRUSH_Y_X
++0x1478 DP_BRUSH_BKGD_CLR
++0x147C DP_BRUSH_FRGD_CLR
++0x1480 BRUSH_DATA0
++0x1484 BRUSH_DATA1
++0x1598 DST_WIDTH_HEIGHT
++0x15C0 CLR_CMP_CNTL
++0x15C4 CLR_CMP_CLR_SRC
++0x15C8 CLR_CMP_CLR_DST
++0x15CC CLR_CMP_MSK
++0x15D8 DP_SRC_FRGD_CLR
++0x15DC DP_SRC_BKGD_CLR
++0x1600 DST_LINE_START
++0x1604 DST_LINE_END
++0x1608 DST_LINE_PATCOUNT
++0x16C0 DP_CNTL
++0x16CC DP_WRITE_MSK
++0x16D0 DP_CNTL_XDIR_YDIR_YMAJOR
++0x16E8 DEFAULT_SC_BOTTOM_RIGHT
++0x16EC SC_TOP_LEFT
++0x16F0 SC_BOTTOM_RIGHT
++0x16F4 SRC_SC_BOTTOM_RIGHT
++0x1714 DSTCACHE_CTLSTAT
++0x1720 WAIT_UNTIL
++0x172C RBBM_GUICNTL
++0x1D98 VAP_VPORT_XSCALE
++0x1D9C VAP_VPORT_XOFFSET
++0x1DA0 VAP_VPORT_YSCALE
++0x1DA4 VAP_VPORT_YOFFSET
++0x1DA8 VAP_VPORT_ZSCALE
++0x1DAC VAP_VPORT_ZOFFSET
++0x2080 VAP_CNTL
++0x2090 VAP_OUT_VTX_FMT_0
++0x2094 VAP_OUT_VTX_FMT_1
++0x20B0 VAP_VTE_CNTL
++0x2138 VAP_VF_MIN_VTX_INDX
++0x2140 VAP_CNTL_STATUS
++0x2150 VAP_PROG_STREAM_CNTL_0
++0x2154 VAP_PROG_STREAM_CNTL_1
++0x2158 VAP_PROG_STREAM_CNTL_2
++0x215C VAP_PROG_STREAM_CNTL_3
++0x2160 VAP_PROG_STREAM_CNTL_4
++0x2164 VAP_PROG_STREAM_CNTL_5
++0x2168 VAP_PROG_STREAM_CNTL_6
++0x216C VAP_PROG_STREAM_CNTL_7
++0x2180 VAP_VTX_STATE_CNTL
++0x2184 VAP_VSM_VTX_ASSM
++0x2188 VAP_VTX_STATE_IND_REG_0
++0x218C VAP_VTX_STATE_IND_REG_1
++0x2190 VAP_VTX_STATE_IND_REG_2
++0x2194 VAP_VTX_STATE_IND_REG_3
++0x2198 VAP_VTX_STATE_IND_REG_4
++0x219C VAP_VTX_STATE_IND_REG_5
++0x21A0 VAP_VTX_STATE_IND_REG_6
++0x21A4 VAP_VTX_STATE_IND_REG_7
++0x21A8 VAP_VTX_STATE_IND_REG_8
++0x21AC VAP_VTX_STATE_IND_REG_9
++0x21B0 VAP_VTX_STATE_IND_REG_10
++0x21B4 VAP_VTX_STATE_IND_REG_11
++0x21B8 VAP_VTX_STATE_IND_REG_12
++0x21BC VAP_VTX_STATE_IND_REG_13
++0x21C0 VAP_VTX_STATE_IND_REG_14
++0x21C4 VAP_VTX_STATE_IND_REG_15
++0x21DC VAP_PSC_SGN_NORM_CNTL
++0x21E0 VAP_PROG_STREAM_CNTL_EXT_0
++0x21E4 VAP_PROG_STREAM_CNTL_EXT_1
++0x21E8 VAP_PROG_STREAM_CNTL_EXT_2
++0x21EC VAP_PROG_STREAM_CNTL_EXT_3
++0x21F0 VAP_PROG_STREAM_CNTL_EXT_4
++0x21F4 VAP_PROG_STREAM_CNTL_EXT_5
++0x21F8 VAP_PROG_STREAM_CNTL_EXT_6
++0x21FC VAP_PROG_STREAM_CNTL_EXT_7
++0x2200 VAP_PVS_VECTOR_INDX_REG
++0x2204 VAP_PVS_VECTOR_DATA_REG
++0x2208 VAP_PVS_VECTOR_DATA_REG_128
++0x221C VAP_CLIP_CNTL
++0x2220 VAP_GB_VERT_CLIP_ADJ
++0x2224 VAP_GB_VERT_DISC_ADJ
++0x2228 VAP_GB_HORZ_CLIP_ADJ
++0x222C VAP_GB_HORZ_DISC_ADJ
++0x2230 VAP_PVS_FLOW_CNTL_ADDRS_0
++0x2234 VAP_PVS_FLOW_CNTL_ADDRS_1
++0x2238 VAP_PVS_FLOW_CNTL_ADDRS_2
++0x223C VAP_PVS_FLOW_CNTL_ADDRS_3
++0x2240 VAP_PVS_FLOW_CNTL_ADDRS_4
++0x2244 VAP_PVS_FLOW_CNTL_ADDRS_5
++0x2248 VAP_PVS_FLOW_CNTL_ADDRS_6
++0x224C VAP_PVS_FLOW_CNTL_ADDRS_7
++0x2250 VAP_PVS_FLOW_CNTL_ADDRS_8
++0x2254 VAP_PVS_FLOW_CNTL_ADDRS_9
++0x2258 VAP_PVS_FLOW_CNTL_ADDRS_10
++0x225C VAP_PVS_FLOW_CNTL_ADDRS_11
++0x2260 VAP_PVS_FLOW_CNTL_ADDRS_12
++0x2264 VAP_PVS_FLOW_CNTL_ADDRS_13
++0x2268 VAP_PVS_FLOW_CNTL_ADDRS_14
++0x226C VAP_PVS_FLOW_CNTL_ADDRS_15
++0x2284 VAP_PVS_STATE_FLUSH_REG
++0x2288 VAP_PVS_VTX_TIMEOUT_REG
++0x2290 VAP_PVS_FLOW_CNTL_LOOP_INDEX_0
++0x2294 VAP_PVS_FLOW_CNTL_LOOP_INDEX_1
++0x2298 VAP_PVS_FLOW_CNTL_LOOP_INDEX_2
++0x229C VAP_PVS_FLOW_CNTL_LOOP_INDEX_3
++0x22A0 VAP_PVS_FLOW_CNTL_LOOP_INDEX_4
++0x22A4 VAP_PVS_FLOW_CNTL_LOOP_INDEX_5
++0x22A8 VAP_PVS_FLOW_CNTL_LOOP_INDEX_6
++0x22AC VAP_PVS_FLOW_CNTL_LOOP_INDEX_7
++0x22B0 VAP_PVS_FLOW_CNTL_LOOP_INDEX_8
++0x22B4 VAP_PVS_FLOW_CNTL_LOOP_INDEX_9
++0x22B8 VAP_PVS_FLOW_CNTL_LOOP_INDEX_10
++0x22BC VAP_PVS_FLOW_CNTL_LOOP_INDEX_11
++0x22C0 VAP_PVS_FLOW_CNTL_LOOP_INDEX_12
++0x22C4 VAP_PVS_FLOW_CNTL_LOOP_INDEX_13
++0x22C8 VAP_PVS_FLOW_CNTL_LOOP_INDEX_14
++0x22CC VAP_PVS_FLOW_CNTL_LOOP_INDEX_15
++0x22D0 VAP_PVS_CODE_CNTL_0
++0x22D4 VAP_PVS_CONST_CNTL
++0x22D8 VAP_PVS_CODE_CNTL_1
++0x22DC VAP_PVS_FLOW_CNTL_OPC
++0x342C RB2D_DSTCACHE_CTLSTAT
++0x4000 GB_VAP_RASTER_VTX_FMT_0
++0x4004 GB_VAP_RASTER_VTX_FMT_1
++0x4008 GB_ENABLE
++0x401C GB_SELECT
++0x4020 GB_AA_CONFIG
++0x4024 GB_FIFO_SIZE
++0x4100 TX_INVALTAGS
++0x4200 GA_POINT_S0
++0x4204 GA_POINT_T0
++0x4208 GA_POINT_S1
++0x420C GA_POINT_T1
++0x4214 GA_TRIANGLE_STIPPLE
++0x421C GA_POINT_SIZE
++0x4230 GA_POINT_MINMAX
++0x4234 GA_LINE_CNTL
++0x4238 GA_LINE_STIPPLE_CONFIG
++0x4260 GA_LINE_STIPPLE_VALUE
++0x4264 GA_LINE_S0
++0x4268 GA_LINE_S1
++0x4278 GA_COLOR_CONTROL
++0x427C GA_SOLID_RG
++0x4280 GA_SOLID_BA
++0x4288 GA_POLY_MODE
++0x428C GA_ROUND_MODE
++0x4290 GA_OFFSET
++0x4294 GA_FOG_SCALE
++0x4298 GA_FOG_OFFSET
++0x42A0 SU_TEX_WRAP
++0x42A4 SU_POLY_OFFSET_FRONT_SCALE
++0x42A8 SU_POLY_OFFSET_FRONT_OFFSET
++0x42AC SU_POLY_OFFSET_BACK_SCALE
++0x42B0 SU_POLY_OFFSET_BACK_OFFSET
++0x42B4 SU_POLY_OFFSET_ENABLE
++0x42B8 SU_CULL_MODE
++0x42C0 SU_DEPTH_SCALE
++0x42C4 SU_DEPTH_OFFSET
++0x42C8 SU_REG_DEST
++0x4300 RS_COUNT
++0x4304 RS_INST_COUNT
++0x4310 RS_IP_0
++0x4314 RS_IP_1
++0x4318 RS_IP_2
++0x431C RS_IP_3
++0x4320 RS_IP_4
++0x4324 RS_IP_5
++0x4328 RS_IP_6
++0x432C RS_IP_7
++0x4330 RS_INST_0
++0x4334 RS_INST_1
++0x4338 RS_INST_2
++0x433C RS_INST_3
++0x4340 RS_INST_4
++0x4344 RS_INST_5
++0x4348 RS_INST_6
++0x434C RS_INST_7
++0x4350 RS_INST_8
++0x4354 RS_INST_9
++0x4358 RS_INST_10
++0x435C RS_INST_11
++0x4360 RS_INST_12
++0x4364 RS_INST_13
++0x4368 RS_INST_14
++0x436C RS_INST_15
++0x43A4 SC_HYPERZ_EN
++0x43A8 SC_EDGERULE
++0x43B0 SC_CLIP_0_A
++0x43B4 SC_CLIP_0_B
++0x43B8 SC_CLIP_1_A
++0x43BC SC_CLIP_1_B
++0x43C0 SC_CLIP_2_A
++0x43C4 SC_CLIP_2_B
++0x43C8 SC_CLIP_3_A
++0x43CC SC_CLIP_3_B
++0x43D0 SC_CLIP_RULE
++0x43E0 SC_SCISSOR0
++0x43E8 SC_SCREENDOOR
++0x4440 TX_FILTER1_0
++0x4444 TX_FILTER1_1
++0x4448 TX_FILTER1_2
++0x444C TX_FILTER1_3
++0x4450 TX_FILTER1_4
++0x4454 TX_FILTER1_5
++0x4458 TX_FILTER1_6
++0x445C TX_FILTER1_7
++0x4460 TX_FILTER1_8
++0x4464 TX_FILTER1_9
++0x4468 TX_FILTER1_10
++0x446C TX_FILTER1_11
++0x4470 TX_FILTER1_12
++0x4474 TX_FILTER1_13
++0x4478 TX_FILTER1_14
++0x447C TX_FILTER1_15
++0x4580 TX_CHROMA_KEY_0
++0x4584 TX_CHROMA_KEY_1
++0x4588 TX_CHROMA_KEY_2
++0x458C TX_CHROMA_KEY_3
++0x4590 TX_CHROMA_KEY_4
++0x4594 TX_CHROMA_KEY_5
++0x4598 TX_CHROMA_KEY_6
++0x459C TX_CHROMA_KEY_7
++0x45A0 TX_CHROMA_KEY_8
++0x45A4 TX_CHROMA_KEY_9
++0x45A8 TX_CHROMA_KEY_10
++0x45AC TX_CHROMA_KEY_11
++0x45B0 TX_CHROMA_KEY_12
++0x45B4 TX_CHROMA_KEY_13
++0x45B8 TX_CHROMA_KEY_14
++0x45BC TX_CHROMA_KEY_15
++0x45C0 TX_BORDER_COLOR_0
++0x45C4 TX_BORDER_COLOR_1
++0x45C8 TX_BORDER_COLOR_2
++0x45CC TX_BORDER_COLOR_3
++0x45D0 TX_BORDER_COLOR_4
++0x45D4 TX_BORDER_COLOR_5
++0x45D8 TX_BORDER_COLOR_6
++0x45DC TX_BORDER_COLOR_7
++0x45E0 TX_BORDER_COLOR_8
++0x45E4 TX_BORDER_COLOR_9
++0x45E8 TX_BORDER_COLOR_10
++0x45EC TX_BORDER_COLOR_11
++0x45F0 TX_BORDER_COLOR_12
++0x45F4 TX_BORDER_COLOR_13
++0x45F8 TX_BORDER_COLOR_14
++0x45FC TX_BORDER_COLOR_15
++0x4600 US_CONFIG
++0x4604 US_PIXSIZE
++0x4608 US_CODE_OFFSET
++0x460C US_RESET
++0x4610 US_CODE_ADDR_0
++0x4614 US_CODE_ADDR_1
++0x4618 US_CODE_ADDR_2
++0x461C US_CODE_ADDR_3
++0x4620 US_TEX_INST_0
++0x4624 US_TEX_INST_1
++0x4628 US_TEX_INST_2
++0x462C US_TEX_INST_3
++0x4630 US_TEX_INST_4
++0x4634 US_TEX_INST_5
++0x4638 US_TEX_INST_6
++0x463C US_TEX_INST_7
++0x4640 US_TEX_INST_8
++0x4644 US_TEX_INST_9
++0x4648 US_TEX_INST_10
++0x464C US_TEX_INST_11
++0x4650 US_TEX_INST_12
++0x4654 US_TEX_INST_13
++0x4658 US_TEX_INST_14
++0x465C US_TEX_INST_15
++0x4660 US_TEX_INST_16
++0x4664 US_TEX_INST_17
++0x4668 US_TEX_INST_18
++0x466C US_TEX_INST_19
++0x4670 US_TEX_INST_20
++0x4674 US_TEX_INST_21
++0x4678 US_TEX_INST_22
++0x467C US_TEX_INST_23
++0x4680 US_TEX_INST_24
++0x4684 US_TEX_INST_25
++0x4688 US_TEX_INST_26
++0x468C US_TEX_INST_27
++0x4690 US_TEX_INST_28
++0x4694 US_TEX_INST_29
++0x4698 US_TEX_INST_30
++0x469C US_TEX_INST_31
++0x46A4 US_OUT_FMT_0
++0x46A8 US_OUT_FMT_1
++0x46AC US_OUT_FMT_2
++0x46B0 US_OUT_FMT_3
++0x46B4 US_W_FMT
++0x46B8 US_CODE_BANK
++0x46BC US_CODE_EXT
++0x46C0 US_ALU_RGB_ADDR_0
++0x46C4 US_ALU_RGB_ADDR_1
++0x46C8 US_ALU_RGB_ADDR_2
++0x46CC US_ALU_RGB_ADDR_3
++0x46D0 US_ALU_RGB_ADDR_4
++0x46D4 US_ALU_RGB_ADDR_5
++0x46D8 US_ALU_RGB_ADDR_6
++0x46DC US_ALU_RGB_ADDR_7
++0x46E0 US_ALU_RGB_ADDR_8
++0x46E4 US_ALU_RGB_ADDR_9
++0x46E8 US_ALU_RGB_ADDR_10
++0x46EC US_ALU_RGB_ADDR_11
++0x46F0 US_ALU_RGB_ADDR_12
++0x46F4 US_ALU_RGB_ADDR_13
++0x46F8 US_ALU_RGB_ADDR_14
++0x46FC US_ALU_RGB_ADDR_15
++0x4700 US_ALU_RGB_ADDR_16
++0x4704 US_ALU_RGB_ADDR_17
++0x4708 US_ALU_RGB_ADDR_18
++0x470C US_ALU_RGB_ADDR_19
++0x4710 US_ALU_RGB_ADDR_20
++0x4714 US_ALU_RGB_ADDR_21
++0x4718 US_ALU_RGB_ADDR_22
++0x471C US_ALU_RGB_ADDR_23
++0x4720 US_ALU_RGB_ADDR_24
++0x4724 US_ALU_RGB_ADDR_25
++0x4728 US_ALU_RGB_ADDR_26
++0x472C US_ALU_RGB_ADDR_27
++0x4730 US_ALU_RGB_ADDR_28
++0x4734 US_ALU_RGB_ADDR_29
++0x4738 US_ALU_RGB_ADDR_30
++0x473C US_ALU_RGB_ADDR_31
++0x4740 US_ALU_RGB_ADDR_32
++0x4744 US_ALU_RGB_ADDR_33
++0x4748 US_ALU_RGB_ADDR_34
++0x474C US_ALU_RGB_ADDR_35
++0x4750 US_ALU_RGB_ADDR_36
++0x4754 US_ALU_RGB_ADDR_37
++0x4758 US_ALU_RGB_ADDR_38
++0x475C US_ALU_RGB_ADDR_39
++0x4760 US_ALU_RGB_ADDR_40
++0x4764 US_ALU_RGB_ADDR_41
++0x4768 US_ALU_RGB_ADDR_42
++0x476C US_ALU_RGB_ADDR_43
++0x4770 US_ALU_RGB_ADDR_44
++0x4774 US_ALU_RGB_ADDR_45
++0x4778 US_ALU_RGB_ADDR_46
++0x477C US_ALU_RGB_ADDR_47
++0x4780 US_ALU_RGB_ADDR_48
++0x4784 US_ALU_RGB_ADDR_49
++0x4788 US_ALU_RGB_ADDR_50
++0x478C US_ALU_RGB_ADDR_51
++0x4790 US_ALU_RGB_ADDR_52
++0x4794 US_ALU_RGB_ADDR_53
++0x4798 US_ALU_RGB_ADDR_54
++0x479C US_ALU_RGB_ADDR_55
++0x47A0 US_ALU_RGB_ADDR_56
++0x47A4 US_ALU_RGB_ADDR_57
++0x47A8 US_ALU_RGB_ADDR_58
++0x47AC US_ALU_RGB_ADDR_59
++0x47B0 US_ALU_RGB_ADDR_60
++0x47B4 US_ALU_RGB_ADDR_61
++0x47B8 US_ALU_RGB_ADDR_62
++0x47BC US_ALU_RGB_ADDR_63
++0x47C0 US_ALU_ALPHA_ADDR_0
++0x47C4 US_ALU_ALPHA_ADDR_1
++0x47C8 US_ALU_ALPHA_ADDR_2
++0x47CC US_ALU_ALPHA_ADDR_3
++0x47D0 US_ALU_ALPHA_ADDR_4
++0x47D4 US_ALU_ALPHA_ADDR_5
++0x47D8 US_ALU_ALPHA_ADDR_6
++0x47DC US_ALU_ALPHA_ADDR_7
++0x47E0 US_ALU_ALPHA_ADDR_8
++0x47E4 US_ALU_ALPHA_ADDR_9
++0x47E8 US_ALU_ALPHA_ADDR_10
++0x47EC US_ALU_ALPHA_ADDR_11
++0x47F0 US_ALU_ALPHA_ADDR_12
++0x47F4 US_ALU_ALPHA_ADDR_13
++0x47F8 US_ALU_ALPHA_ADDR_14
++0x47FC US_ALU_ALPHA_ADDR_15
++0x4800 US_ALU_ALPHA_ADDR_16
++0x4804 US_ALU_ALPHA_ADDR_17
++0x4808 US_ALU_ALPHA_ADDR_18
++0x480C US_ALU_ALPHA_ADDR_19
++0x4810 US_ALU_ALPHA_ADDR_20
++0x4814 US_ALU_ALPHA_ADDR_21
++0x4818 US_ALU_ALPHA_ADDR_22
++0x481C US_ALU_ALPHA_ADDR_23
++0x4820 US_ALU_ALPHA_ADDR_24
++0x4824 US_ALU_ALPHA_ADDR_25
++0x4828 US_ALU_ALPHA_ADDR_26
++0x482C US_ALU_ALPHA_ADDR_27
++0x4830 US_ALU_ALPHA_ADDR_28
++0x4834 US_ALU_ALPHA_ADDR_29
++0x4838 US_ALU_ALPHA_ADDR_30
++0x483C US_ALU_ALPHA_ADDR_31
++0x4840 US_ALU_ALPHA_ADDR_32
++0x4844 US_ALU_ALPHA_ADDR_33
++0x4848 US_ALU_ALPHA_ADDR_34
++0x484C US_ALU_ALPHA_ADDR_35
++0x4850 US_ALU_ALPHA_ADDR_36
++0x4854 US_ALU_ALPHA_ADDR_37
++0x4858 US_ALU_ALPHA_ADDR_38
++0x485C US_ALU_ALPHA_ADDR_39
++0x4860 US_ALU_ALPHA_ADDR_40
++0x4864 US_ALU_ALPHA_ADDR_41
++0x4868 US_ALU_ALPHA_ADDR_42
++0x486C US_ALU_ALPHA_ADDR_43
++0x4870 US_ALU_ALPHA_ADDR_44
++0x4874 US_ALU_ALPHA_ADDR_45
++0x4878 US_ALU_ALPHA_ADDR_46
++0x487C US_ALU_ALPHA_ADDR_47
++0x4880 US_ALU_ALPHA_ADDR_48
++0x4884 US_ALU_ALPHA_ADDR_49
++0x4888 US_ALU_ALPHA_ADDR_50
++0x488C US_ALU_ALPHA_ADDR_51
++0x4890 US_ALU_ALPHA_ADDR_52
++0x4894 US_ALU_ALPHA_ADDR_53
++0x4898 US_ALU_ALPHA_ADDR_54
++0x489C US_ALU_ALPHA_ADDR_55
++0x48A0 US_ALU_ALPHA_ADDR_56
++0x48A4 US_ALU_ALPHA_ADDR_57
++0x48A8 US_ALU_ALPHA_ADDR_58
++0x48AC US_ALU_ALPHA_ADDR_59
++0x48B0 US_ALU_ALPHA_ADDR_60
++0x48B4 US_ALU_ALPHA_ADDR_61
++0x48B8 US_ALU_ALPHA_ADDR_62
++0x48BC US_ALU_ALPHA_ADDR_63
++0x48C0 US_ALU_RGB_INST_0
++0x48C4 US_ALU_RGB_INST_1
++0x48C8 US_ALU_RGB_INST_2
++0x48CC US_ALU_RGB_INST_3
++0x48D0 US_ALU_RGB_INST_4
++0x48D4 US_ALU_RGB_INST_5
++0x48D8 US_ALU_RGB_INST_6
++0x48DC US_ALU_RGB_INST_7
++0x48E0 US_ALU_RGB_INST_8
++0x48E4 US_ALU_RGB_INST_9
++0x48E8 US_ALU_RGB_INST_10
++0x48EC US_ALU_RGB_INST_11
++0x48F0 US_ALU_RGB_INST_12
++0x48F4 US_ALU_RGB_INST_13
++0x48F8 US_ALU_RGB_INST_14
++0x48FC US_ALU_RGB_INST_15
++0x4900 US_ALU_RGB_INST_16
++0x4904 US_ALU_RGB_INST_17
++0x4908 US_ALU_RGB_INST_18
++0x490C US_ALU_RGB_INST_19
++0x4910 US_ALU_RGB_INST_20
++0x4914 US_ALU_RGB_INST_21
++0x4918 US_ALU_RGB_INST_22
++0x491C US_ALU_RGB_INST_23
++0x4920 US_ALU_RGB_INST_24
++0x4924 US_ALU_RGB_INST_25
++0x4928 US_ALU_RGB_INST_26
++0x492C US_ALU_RGB_INST_27
++0x4930 US_ALU_RGB_INST_28
++0x4934 US_ALU_RGB_INST_29
++0x4938 US_ALU_RGB_INST_30
++0x493C US_ALU_RGB_INST_31
++0x4940 US_ALU_RGB_INST_32
++0x4944 US_ALU_RGB_INST_33
++0x4948 US_ALU_RGB_INST_34
++0x494C US_ALU_RGB_INST_35
++0x4950 US_ALU_RGB_INST_36
++0x4954 US_ALU_RGB_INST_37
++0x4958 US_ALU_RGB_INST_38
++0x495C US_ALU_RGB_INST_39
++0x4960 US_ALU_RGB_INST_40
++0x4964 US_ALU_RGB_INST_41
++0x4968 US_ALU_RGB_INST_42
++0x496C US_ALU_RGB_INST_43
++0x4970 US_ALU_RGB_INST_44
++0x4974 US_ALU_RGB_INST_45
++0x4978 US_ALU_RGB_INST_46
++0x497C US_ALU_RGB_INST_47
++0x4980 US_ALU_RGB_INST_48
++0x4984 US_ALU_RGB_INST_49
++0x4988 US_ALU_RGB_INST_50
++0x498C US_ALU_RGB_INST_51
++0x4990 US_ALU_RGB_INST_52
++0x4994 US_ALU_RGB_INST_53
++0x4998 US_ALU_RGB_INST_54
++0x499C US_ALU_RGB_INST_55
++0x49A0 US_ALU_RGB_INST_56
++0x49A4 US_ALU_RGB_INST_57
++0x49A8 US_ALU_RGB_INST_58
++0x49AC US_ALU_RGB_INST_59
++0x49B0 US_ALU_RGB_INST_60
++0x49B4 US_ALU_RGB_INST_61
++0x49B8 US_ALU_RGB_INST_62
++0x49BC US_ALU_RGB_INST_63
++0x49C0 US_ALU_ALPHA_INST_0
++0x49C4 US_ALU_ALPHA_INST_1
++0x49C8 US_ALU_ALPHA_INST_2
++0x49CC US_ALU_ALPHA_INST_3
++0x49D0 US_ALU_ALPHA_INST_4
++0x49D4 US_ALU_ALPHA_INST_5
++0x49D8 US_ALU_ALPHA_INST_6
++0x49DC US_ALU_ALPHA_INST_7
++0x49E0 US_ALU_ALPHA_INST_8
++0x49E4 US_ALU_ALPHA_INST_9
++0x49E8 US_ALU_ALPHA_INST_10
++0x49EC US_ALU_ALPHA_INST_11
++0x49F0 US_ALU_ALPHA_INST_12
++0x49F4 US_ALU_ALPHA_INST_13
++0x49F8 US_ALU_ALPHA_INST_14
++0x49FC US_ALU_ALPHA_INST_15
++0x4A00 US_ALU_ALPHA_INST_16
++0x4A04 US_ALU_ALPHA_INST_17
++0x4A08 US_ALU_ALPHA_INST_18
++0x4A0C US_ALU_ALPHA_INST_19
++0x4A10 US_ALU_ALPHA_INST_20
++0x4A14 US_ALU_ALPHA_INST_21
++0x4A18 US_ALU_ALPHA_INST_22
++0x4A1C US_ALU_ALPHA_INST_23
++0x4A20 US_ALU_ALPHA_INST_24
++0x4A24 US_ALU_ALPHA_INST_25
++0x4A28 US_ALU_ALPHA_INST_26
++0x4A2C US_ALU_ALPHA_INST_27
++0x4A30 US_ALU_ALPHA_INST_28
++0x4A34 US_ALU_ALPHA_INST_29
++0x4A38 US_ALU_ALPHA_INST_30
++0x4A3C US_ALU_ALPHA_INST_31
++0x4A40 US_ALU_ALPHA_INST_32
++0x4A44 US_ALU_ALPHA_INST_33
++0x4A48 US_ALU_ALPHA_INST_34
++0x4A4C US_ALU_ALPHA_INST_35
++0x4A50 US_ALU_ALPHA_INST_36
++0x4A54 US_ALU_ALPHA_INST_37
++0x4A58 US_ALU_ALPHA_INST_38
++0x4A5C US_ALU_ALPHA_INST_39
++0x4A60 US_ALU_ALPHA_INST_40
++0x4A64 US_ALU_ALPHA_INST_41
++0x4A68 US_ALU_ALPHA_INST_42
++0x4A6C US_ALU_ALPHA_INST_43
++0x4A70 US_ALU_ALPHA_INST_44
++0x4A74 US_ALU_ALPHA_INST_45
++0x4A78 US_ALU_ALPHA_INST_46
++0x4A7C US_ALU_ALPHA_INST_47
++0x4A80 US_ALU_ALPHA_INST_48
++0x4A84 US_ALU_ALPHA_INST_49
++0x4A88 US_ALU_ALPHA_INST_50
++0x4A8C US_ALU_ALPHA_INST_51
++0x4A90 US_ALU_ALPHA_INST_52
++0x4A94 US_ALU_ALPHA_INST_53
++0x4A98 US_ALU_ALPHA_INST_54
++0x4A9C US_ALU_ALPHA_INST_55
++0x4AA0 US_ALU_ALPHA_INST_56
++0x4AA4 US_ALU_ALPHA_INST_57
++0x4AA8 US_ALU_ALPHA_INST_58
++0x4AAC US_ALU_ALPHA_INST_59
++0x4AB0 US_ALU_ALPHA_INST_60
++0x4AB4 US_ALU_ALPHA_INST_61
++0x4AB8 US_ALU_ALPHA_INST_62
++0x4ABC US_ALU_ALPHA_INST_63
++0x4AC0 US_ALU_EXT_ADDR_0
++0x4AC4 US_ALU_EXT_ADDR_1
++0x4AC8 US_ALU_EXT_ADDR_2
++0x4ACC US_ALU_EXT_ADDR_3
++0x4AD0 US_ALU_EXT_ADDR_4
++0x4AD4 US_ALU_EXT_ADDR_5
++0x4AD8 US_ALU_EXT_ADDR_6
++0x4ADC US_ALU_EXT_ADDR_7
++0x4AE0 US_ALU_EXT_ADDR_8
++0x4AE4 US_ALU_EXT_ADDR_9
++0x4AE8 US_ALU_EXT_ADDR_10
++0x4AEC US_ALU_EXT_ADDR_11
++0x4AF0 US_ALU_EXT_ADDR_12
++0x4AF4 US_ALU_EXT_ADDR_13
++0x4AF8 US_ALU_EXT_ADDR_14
++0x4AFC US_ALU_EXT_ADDR_15
++0x4B00 US_ALU_EXT_ADDR_16
++0x4B04 US_ALU_EXT_ADDR_17
++0x4B08 US_ALU_EXT_ADDR_18
++0x4B0C US_ALU_EXT_ADDR_19
++0x4B10 US_ALU_EXT_ADDR_20
++0x4B14 US_ALU_EXT_ADDR_21
++0x4B18 US_ALU_EXT_ADDR_22
++0x4B1C US_ALU_EXT_ADDR_23
++0x4B20 US_ALU_EXT_ADDR_24
++0x4B24 US_ALU_EXT_ADDR_25
++0x4B28 US_ALU_EXT_ADDR_26
++0x4B2C US_ALU_EXT_ADDR_27
++0x4B30 US_ALU_EXT_ADDR_28
++0x4B34 US_ALU_EXT_ADDR_29
++0x4B38 US_ALU_EXT_ADDR_30
++0x4B3C US_ALU_EXT_ADDR_31
++0x4B40 US_ALU_EXT_ADDR_32
++0x4B44 US_ALU_EXT_ADDR_33
++0x4B48 US_ALU_EXT_ADDR_34
++0x4B4C US_ALU_EXT_ADDR_35
++0x4B50 US_ALU_EXT_ADDR_36
++0x4B54 US_ALU_EXT_ADDR_37
++0x4B58 US_ALU_EXT_ADDR_38
++0x4B5C US_ALU_EXT_ADDR_39
++0x4B60 US_ALU_EXT_ADDR_40
++0x4B64 US_ALU_EXT_ADDR_41
++0x4B68 US_ALU_EXT_ADDR_42
++0x4B6C US_ALU_EXT_ADDR_43
++0x4B70 US_ALU_EXT_ADDR_44
++0x4B74 US_ALU_EXT_ADDR_45
++0x4B78 US_ALU_EXT_ADDR_46
++0x4B7C US_ALU_EXT_ADDR_47
++0x4B80 US_ALU_EXT_ADDR_48
++0x4B84 US_ALU_EXT_ADDR_49
++0x4B88 US_ALU_EXT_ADDR_50
++0x4B8C US_ALU_EXT_ADDR_51
++0x4B90 US_ALU_EXT_ADDR_52
++0x4B94 US_ALU_EXT_ADDR_53
++0x4B98 US_ALU_EXT_ADDR_54
++0x4B9C US_ALU_EXT_ADDR_55
++0x4BA0 US_ALU_EXT_ADDR_56
++0x4BA4 US_ALU_EXT_ADDR_57
++0x4BA8 US_ALU_EXT_ADDR_58
++0x4BAC US_ALU_EXT_ADDR_59
++0x4BB0 US_ALU_EXT_ADDR_60
++0x4BB4 US_ALU_EXT_ADDR_61
++0x4BB8 US_ALU_EXT_ADDR_62
++0x4BBC US_ALU_EXT_ADDR_63
++0x4BC0 FG_FOG_BLEND
++0x4BC4 FG_FOG_FACTOR
++0x4BC8 FG_FOG_COLOR_R
++0x4BCC FG_FOG_COLOR_G
++0x4BD0 FG_FOG_COLOR_B
++0x4BD4 FG_ALPHA_FUNC
++0x4BD8 FG_DEPTH_SRC
++0x4C00 US_ALU_CONST_R_0
++0x4C04 US_ALU_CONST_G_0
++0x4C08 US_ALU_CONST_B_0
++0x4C0C US_ALU_CONST_A_0
++0x4C10 US_ALU_CONST_R_1
++0x4C14 US_ALU_CONST_G_1
++0x4C18 US_ALU_CONST_B_1
++0x4C1C US_ALU_CONST_A_1
++0x4C20 US_ALU_CONST_R_2
++0x4C24 US_ALU_CONST_G_2
++0x4C28 US_ALU_CONST_B_2
++0x4C2C US_ALU_CONST_A_2
++0x4C30 US_ALU_CONST_R_3
++0x4C34 US_ALU_CONST_G_3
++0x4C38 US_ALU_CONST_B_3
++0x4C3C US_ALU_CONST_A_3
++0x4C40 US_ALU_CONST_R_4
++0x4C44 US_ALU_CONST_G_4
++0x4C48 US_ALU_CONST_B_4
++0x4C4C US_ALU_CONST_A_4
++0x4C50 US_ALU_CONST_R_5
++0x4C54 US_ALU_CONST_G_5
++0x4C58 US_ALU_CONST_B_5
++0x4C5C US_ALU_CONST_A_5
++0x4C60 US_ALU_CONST_R_6
++0x4C64 US_ALU_CONST_G_6
++0x4C68 US_ALU_CONST_B_6
++0x4C6C US_ALU_CONST_A_6
++0x4C70 US_ALU_CONST_R_7
++0x4C74 US_ALU_CONST_G_7
++0x4C78 US_ALU_CONST_B_7
++0x4C7C US_ALU_CONST_A_7
++0x4C80 US_ALU_CONST_R_8
++0x4C84 US_ALU_CONST_G_8
++0x4C88 US_ALU_CONST_B_8
++0x4C8C US_ALU_CONST_A_8
++0x4C90 US_ALU_CONST_R_9
++0x4C94 US_ALU_CONST_G_9
++0x4C98 US_ALU_CONST_B_9
++0x4C9C US_ALU_CONST_A_9
++0x4CA0 US_ALU_CONST_R_10
++0x4CA4 US_ALU_CONST_G_10
++0x4CA8 US_ALU_CONST_B_10
++0x4CAC US_ALU_CONST_A_10
++0x4CB0 US_ALU_CONST_R_11
++0x4CB4 US_ALU_CONST_G_11
++0x4CB8 US_ALU_CONST_B_11
++0x4CBC US_ALU_CONST_A_11
++0x4CC0 US_ALU_CONST_R_12
++0x4CC4 US_ALU_CONST_G_12
++0x4CC8 US_ALU_CONST_B_12
++0x4CCC US_ALU_CONST_A_12
++0x4CD0 US_ALU_CONST_R_13
++0x4CD4 US_ALU_CONST_G_13
++0x4CD8 US_ALU_CONST_B_13
++0x4CDC US_ALU_CONST_A_13
++0x4CE0 US_ALU_CONST_R_14
++0x4CE4 US_ALU_CONST_G_14
++0x4CE8 US_ALU_CONST_B_14
++0x4CEC US_ALU_CONST_A_14
++0x4CF0 US_ALU_CONST_R_15
++0x4CF4 US_ALU_CONST_G_15
++0x4CF8 US_ALU_CONST_B_15
++0x4CFC US_ALU_CONST_A_15
++0x4D00 US_ALU_CONST_R_16
++0x4D04 US_ALU_CONST_G_16
++0x4D08 US_ALU_CONST_B_16
++0x4D0C US_ALU_CONST_A_16
++0x4D10 US_ALU_CONST_R_17
++0x4D14 US_ALU_CONST_G_17
++0x4D18 US_ALU_CONST_B_17
++0x4D1C US_ALU_CONST_A_17
++0x4D20 US_ALU_CONST_R_18
++0x4D24 US_ALU_CONST_G_18
++0x4D28 US_ALU_CONST_B_18
++0x4D2C US_ALU_CONST_A_18
++0x4D30 US_ALU_CONST_R_19
++0x4D34 US_ALU_CONST_G_19
++0x4D38 US_ALU_CONST_B_19
++0x4D3C US_ALU_CONST_A_19
++0x4D40 US_ALU_CONST_R_20
++0x4D44 US_ALU_CONST_G_20
++0x4D48 US_ALU_CONST_B_20
++0x4D4C US_ALU_CONST_A_20
++0x4D50 US_ALU_CONST_R_21
++0x4D54 US_ALU_CONST_G_21
++0x4D58 US_ALU_CONST_B_21
++0x4D5C US_ALU_CONST_A_21
++0x4D60 US_ALU_CONST_R_22
++0x4D64 US_ALU_CONST_G_22
++0x4D68 US_ALU_CONST_B_22
++0x4D6C US_ALU_CONST_A_22
++0x4D70 US_ALU_CONST_R_23
++0x4D74 US_ALU_CONST_G_23
++0x4D78 US_ALU_CONST_B_23
++0x4D7C US_ALU_CONST_A_23
++0x4D80 US_ALU_CONST_R_24
++0x4D84 US_ALU_CONST_G_24
++0x4D88 US_ALU_CONST_B_24
++0x4D8C US_ALU_CONST_A_24
++0x4D90 US_ALU_CONST_R_25
++0x4D94 US_ALU_CONST_G_25
++0x4D98 US_ALU_CONST_B_25
++0x4D9C US_ALU_CONST_A_25
++0x4DA0 US_ALU_CONST_R_26
++0x4DA4 US_ALU_CONST_G_26
++0x4DA8 US_ALU_CONST_B_26
++0x4DAC US_ALU_CONST_A_26
++0x4DB0 US_ALU_CONST_R_27
++0x4DB4 US_ALU_CONST_G_27
++0x4DB8 US_ALU_CONST_B_27
++0x4DBC US_ALU_CONST_A_27
++0x4DC0 US_ALU_CONST_R_28
++0x4DC4 US_ALU_CONST_G_28
++0x4DC8 US_ALU_CONST_B_28
++0x4DCC US_ALU_CONST_A_28
++0x4DD0 US_ALU_CONST_R_29
++0x4DD4 US_ALU_CONST_G_29
++0x4DD8 US_ALU_CONST_B_29
++0x4DDC US_ALU_CONST_A_29
++0x4DE0 US_ALU_CONST_R_30
++0x4DE4 US_ALU_CONST_G_30
++0x4DE8 US_ALU_CONST_B_30
++0x4DEC US_ALU_CONST_A_30
++0x4DF0 US_ALU_CONST_R_31
++0x4DF4 US_ALU_CONST_G_31
++0x4DF8 US_ALU_CONST_B_31
++0x4DFC US_ALU_CONST_A_31
++0x4E04 RB3D_BLENDCNTL_R3
++0x4E08 RB3D_ABLENDCNTL_R3
++0x4E0C RB3D_COLOR_CHANNEL_MASK
++0x4E10 RB3D_CONSTANT_COLOR
++0x4E14 RB3D_COLOR_CLEAR_VALUE
++0x4E18 RB3D_ROPCNTL_R3
++0x4E1C RB3D_CLRCMP_FLIPE_R3
++0x4E20 RB3D_CLRCMP_CLR_R3
++0x4E24 RB3D_CLRCMP_MSK_R3
++0x4E48 RB3D_DEBUG_CTL
++0x4E4C RB3D_DSTCACHE_CTLSTAT_R3
++0x4E50 RB3D_DITHER_CTL
++0x4E54 RB3D_CMASK_OFFSET0
++0x4E58 RB3D_CMASK_OFFSET1
++0x4E5C RB3D_CMASK_OFFSET2
++0x4E60 RB3D_CMASK_OFFSET3
++0x4E64 RB3D_CMASK_PITCH0
++0x4E68 RB3D_CMASK_PITCH1
++0x4E6C RB3D_CMASK_PITCH2
++0x4E70 RB3D_CMASK_PITCH3
++0x4E74 RB3D_CMASK_WRINDEX
++0x4E78 RB3D_CMASK_DWORD
++0x4E7C RB3D_CMASK_RDINDEX
++0x4E80 RB3D_AARESOLVE_OFFSET
++0x4E84 RB3D_AARESOLVE_PITCH
++0x4E88 RB3D_AARESOLVE_CTL
++0x4EA0 RB3D_DISCARD_SRC_PIXEL_LTE_THRESHOLD
++0x4EA4 RB3D_DISCARD_SRC_PIXEL_GTE_THRESHOLD
++0x4F04 ZB_ZSTENCILCNTL
++0x4F08 ZB_STENCILREFMASK
++0x4F14 ZB_ZTOP
++0x4F18 ZB_ZCACHE_CTLSTAT
++0x4F1C ZB_BW_CNTL
++0x4F28 ZB_DEPTHCLEARVALUE
++0x4F30 ZB_ZMASK_OFFSET
++0x4F34 ZB_ZMASK_PITCH
++0x4F38 ZB_ZMASK_WRINDEX
++0x4F3C ZB_ZMASK_DWORD
++0x4F40 ZB_ZMASK_RDINDEX
++0x4F44 ZB_HIZ_OFFSET
++0x4F48 ZB_HIZ_WRINDEX
++0x4F4C ZB_HIZ_DWORD
++0x4F50 ZB_HIZ_RDINDEX
++0x4F54 ZB_HIZ_PITCH
++0x4F58 ZB_ZPASS_DATA
+diff --git a/drivers/gpu/drm/radeon/reg_srcs/rs600 b/drivers/gpu/drm/radeon/reg_srcs/rs600
+index 8e3c0b8..6801b86 100644
+--- a/drivers/gpu/drm/radeon/reg_srcs/rs600
++++ b/drivers/gpu/drm/radeon/reg_srcs/rs600
+@@ -153,7 +153,7 @@ rs600 0x6d40
+ 0x42A4 SU_POLY_OFFSET_FRONT_SCALE
+ 0x42A8 SU_POLY_OFFSET_FRONT_OFFSET
+ 0x42AC SU_POLY_OFFSET_BACK_SCALE
+-0x42B0 SU_POLY_OFFSET_BACK_OFFSET
++0x42B0 SU_POLY_OFFSET_BACK_OFFSET
+ 0x42B4 SU_POLY_OFFSET_ENABLE
+ 0x42B8 SU_CULL_MODE
+ 0x42C0 SU_DEPTH_SCALE
+@@ -291,6 +291,8 @@ rs600 0x6d40
+ 0x46AC US_OUT_FMT_2
+ 0x46B0 US_OUT_FMT_3
+ 0x46B4 US_W_FMT
++0x46B8 US_CODE_BANK
++0x46BC US_CODE_EXT
+ 0x46C0 US_ALU_RGB_ADDR_0
+ 0x46C4 US_ALU_RGB_ADDR_1
+ 0x46C8 US_ALU_RGB_ADDR_2
+@@ -547,6 +549,70 @@ rs600 0x6d40
+ 0x4AB4 US_ALU_ALPHA_INST_61
+ 0x4AB8 US_ALU_ALPHA_INST_62
+ 0x4ABC US_ALU_ALPHA_INST_63
++0x4AC0 US_ALU_EXT_ADDR_0
++0x4AC4 US_ALU_EXT_ADDR_1
++0x4AC8 US_ALU_EXT_ADDR_2
++0x4ACC US_ALU_EXT_ADDR_3
++0x4AD0 US_ALU_EXT_ADDR_4
++0x4AD4 US_ALU_EXT_ADDR_5
++0x4AD8 US_ALU_EXT_ADDR_6
++0x4ADC US_ALU_EXT_ADDR_7
++0x4AE0 US_ALU_EXT_ADDR_8
++0x4AE4 US_ALU_EXT_ADDR_9
++0x4AE8 US_ALU_EXT_ADDR_10
++0x4AEC US_ALU_EXT_ADDR_11
++0x4AF0 US_ALU_EXT_ADDR_12
++0x4AF4 US_ALU_EXT_ADDR_13
++0x4AF8 US_ALU_EXT_ADDR_14
++0x4AFC US_ALU_EXT_ADDR_15
++0x4B00 US_ALU_EXT_ADDR_16
++0x4B04 US_ALU_EXT_ADDR_17
++0x4B08 US_ALU_EXT_ADDR_18
++0x4B0C US_ALU_EXT_ADDR_19
++0x4B10 US_ALU_EXT_ADDR_20
++0x4B14 US_ALU_EXT_ADDR_21
++0x4B18 US_ALU_EXT_ADDR_22
++0x4B1C US_ALU_EXT_ADDR_23
++0x4B20 US_ALU_EXT_ADDR_24
++0x4B24 US_ALU_EXT_ADDR_25
++0x4B28 US_ALU_EXT_ADDR_26
++0x4B2C US_ALU_EXT_ADDR_27
++0x4B30 US_ALU_EXT_ADDR_28
++0x4B34 US_ALU_EXT_ADDR_29
++0x4B38 US_ALU_EXT_ADDR_30
++0x4B3C US_ALU_EXT_ADDR_31
++0x4B40 US_ALU_EXT_ADDR_32
++0x4B44 US_ALU_EXT_ADDR_33
++0x4B48 US_ALU_EXT_ADDR_34
++0x4B4C US_ALU_EXT_ADDR_35
++0x4B50 US_ALU_EXT_ADDR_36
++0x4B54 US_ALU_EXT_ADDR_37
++0x4B58 US_ALU_EXT_ADDR_38
++0x4B5C US_ALU_EXT_ADDR_39
++0x4B60 US_ALU_EXT_ADDR_40
++0x4B64 US_ALU_EXT_ADDR_41
++0x4B68 US_ALU_EXT_ADDR_42
++0x4B6C US_ALU_EXT_ADDR_43
++0x4B70 US_ALU_EXT_ADDR_44
++0x4B74 US_ALU_EXT_ADDR_45
++0x4B78 US_ALU_EXT_ADDR_46
++0x4B7C US_ALU_EXT_ADDR_47
++0x4B80 US_ALU_EXT_ADDR_48
++0x4B84 US_ALU_EXT_ADDR_49
++0x4B88 US_ALU_EXT_ADDR_50
++0x4B8C US_ALU_EXT_ADDR_51
++0x4B90 US_ALU_EXT_ADDR_52
++0x4B94 US_ALU_EXT_ADDR_53
++0x4B98 US_ALU_EXT_ADDR_54
++0x4B9C US_ALU_EXT_ADDR_55
++0x4BA0 US_ALU_EXT_ADDR_56
++0x4BA4 US_ALU_EXT_ADDR_57
++0x4BA8 US_ALU_EXT_ADDR_58
++0x4BAC US_ALU_EXT_ADDR_59
++0x4BB0 US_ALU_EXT_ADDR_60
++0x4BB4 US_ALU_EXT_ADDR_61
++0x4BB8 US_ALU_EXT_ADDR_62
++0x4BBC US_ALU_EXT_ADDR_63
+ 0x4BC0 FG_FOG_BLEND
+ 0x4BC4 FG_FOG_FACTOR
+ 0x4BC8 FG_FOG_COLOR_R
+diff --git a/drivers/gpu/drm/radeon/reg_srcs/rv515 b/drivers/gpu/drm/radeon/reg_srcs/rv515
+index 0102a0d..38abf63 100644
+--- a/drivers/gpu/drm/radeon/reg_srcs/rv515
++++ b/drivers/gpu/drm/radeon/reg_srcs/rv515
+@@ -161,7 +161,12 @@ rv515 0x6d40
+ 0x401C GB_SELECT
+ 0x4020 GB_AA_CONFIG
+ 0x4024 GB_FIFO_SIZE
++0x4028 GB_Z_PEQ_CONFIG
+ 0x4100 TX_INVALTAGS
++0x4114 SU_TEX_WRAP_PS3
++0x4118 PS3_ENABLE
++0x411c PS3_VTX_FMT
++0x4120 PS3_TEX_SOURCE
+ 0x4200 GA_POINT_S0
+ 0x4204 GA_POINT_T0
+ 0x4208 GA_POINT_S1
+@@ -171,6 +176,7 @@ rv515 0x6d40
+ 0x4230 GA_POINT_MINMAX
+ 0x4234 GA_LINE_CNTL
+ 0x4238 GA_LINE_STIPPLE_CONFIG
++0x4258 GA_COLOR_CONTROL_PS3
+ 0x4260 GA_LINE_STIPPLE_VALUE
+ 0x4264 GA_LINE_S0
+ 0x4268 GA_LINE_S1
+diff --git a/drivers/gpu/drm/radeon/rs400.c b/drivers/gpu/drm/radeon/rs400.c
+index ca03716..287fceb 100644
+--- a/drivers/gpu/drm/radeon/rs400.c
++++ b/drivers/gpu/drm/radeon/rs400.c
+@@ -223,15 +223,31 @@ int rs400_gart_set_page(struct radeon_device *rdev, int i, uint64_t addr)
+ return 0;
+ }
+
++int rs400_mc_wait_for_idle(struct radeon_device *rdev)
++{
++ unsigned i;
++ uint32_t tmp;
++
++ for (i = 0; i < rdev->usec_timeout; i++) {
++ /* read MC_STATUS */
++ tmp = RREG32(0x0150);
++ if (tmp & (1 << 2)) {
++ return 0;
++ }
++ DRM_UDELAY(1);
++ }
++ return -1;
++}
++
+ void rs400_gpu_init(struct radeon_device *rdev)
+ {
+ /* FIXME: HDP same place on rs400 ? */
+ r100_hdp_reset(rdev);
+ /* FIXME: is this correct ? */
+ r420_pipes_init(rdev);
+- if (r300_mc_wait_for_idle(rdev)) {
+- printk(KERN_WARNING "Failed to wait MC idle while "
+- "programming pipes. Bad things might happen.\n");
++ if (rs400_mc_wait_for_idle(rdev)) {
++ printk(KERN_WARNING "rs400: Failed to wait MC idle while "
++ "programming pipes. Bad things might happen. %08x\n", RREG32(0x150));
+ }
+ }
+
+@@ -352,10 +368,11 @@ static int rs400_mc_init(struct radeon_device *rdev)
+ u32 tmp;
+
+ /* Setup GPU memory space */
+- tmp = G_00015C_MC_FB_START(RREG32(R_00015C_NB_TOM));
++ tmp = RREG32(R_00015C_NB_TOM);
+ rdev->mc.vram_location = G_00015C_MC_FB_START(tmp) << 16;
+ rdev->mc.gtt_location = 0xFFFFFFFFUL;
+ r = radeon_mc_setup(rdev);
++ rdev->mc.igp_sideport_enabled = radeon_combios_sideport_present(rdev);
+ if (r)
+ return r;
+ return 0;
+@@ -369,8 +386,8 @@ void rs400_mc_program(struct radeon_device *rdev)
+ r100_mc_stop(rdev, &save);
+
+ /* Wait for mc idle */
+- if (r300_mc_wait_for_idle(rdev))
+- dev_warn(rdev->dev, "Wait MC idle timeout before updating MC.\n");
++ if (rs400_mc_wait_for_idle(rdev))
++ dev_warn(rdev->dev, "rs400: Wait MC idle timeout before updating MC.\n");
+ WREG32(R_000148_MC_FB_LOCATION,
+ S_000148_MC_FB_START(rdev->mc.vram_start >> 16) |
+ S_000148_MC_FB_TOP(rdev->mc.vram_end >> 16));
+@@ -387,14 +404,15 @@ static int rs400_startup(struct radeon_device *rdev)
+ r300_clock_startup(rdev);
+ /* Initialize GPU configuration (# pipes, ...) */
+ rs400_gpu_init(rdev);
++ r100_enable_bm(rdev);
+ /* Initialize GART (initialize after TTM so we can allocate
+ * memory through TTM but finalize after TTM) */
+ r = rs400_gart_enable(rdev);
+ if (r)
+ return r;
+ /* Enable IRQ */
+- rdev->irq.sw_int = true;
+ r100_irq_set(rdev);
++ rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
+ /* 1M ring buffer */
+ r = r100_cp_init(rdev, 1024 * 1024);
+ if (r) {
+@@ -430,6 +448,8 @@ int rs400_resume(struct radeon_device *rdev)
+ radeon_combios_asic_init(rdev->ddev);
+ /* Resume clock after posting */
+ r300_clock_startup(rdev);
++ /* Initialize surface registers */
++ radeon_surface_init(rdev);
+ return rs400_startup(rdev);
+ }
+
+@@ -444,7 +464,6 @@ int rs400_suspend(struct radeon_device *rdev)
+
+ void rs400_fini(struct radeon_device *rdev)
+ {
+- rs400_suspend(rdev);
+ r100_cp_fini(rdev);
+ r100_wb_fini(rdev);
+ r100_ib_fini(rdev);
+@@ -452,7 +471,7 @@ void rs400_fini(struct radeon_device *rdev)
+ rs400_gart_fini(rdev);
+ radeon_irq_kms_fini(rdev);
+ radeon_fence_driver_fini(rdev);
+- radeon_object_fini(rdev);
++ radeon_bo_fini(rdev);
+ radeon_atombios_fini(rdev);
+ kfree(rdev->bios);
+ rdev->bios = NULL;
+@@ -490,12 +509,13 @@ int rs400_init(struct radeon_device *rdev)
+ RREG32(R_0007C0_CP_STAT));
+ }
+ /* check if cards are posted or not */
+- if (!radeon_card_posted(rdev) && rdev->bios) {
+- DRM_INFO("GPU not posted. posting now...\n");
+- radeon_combios_asic_init(rdev->ddev);
+- }
++ if (radeon_boot_test_post_card(rdev) == false)
++ return -EINVAL;
++
+ /* Initialize clocks */
+ radeon_get_clock_info(rdev->ddev);
++ /* Initialize power management */
++ radeon_pm_init(rdev);
+ /* Get vram informations */
+ rs400_vram_info(rdev);
+ /* Initialize memory controller (also test AGP) */
+@@ -510,7 +530,7 @@ int rs400_init(struct radeon_device *rdev)
+ if (r)
+ return r;
+ /* Memory manager */
+- r = radeon_object_init(rdev);
++ r = radeon_bo_init(rdev);
+ if (r)
+ return r;
+ r = rs400_gart_init(rdev);
+@@ -522,7 +542,6 @@ int rs400_init(struct radeon_device *rdev)
+ if (r) {
+ /* Somethings want wront with the accel init stop accel */
+ dev_err(rdev->dev, "Disabling GPU acceleration\n");
+- rs400_suspend(rdev);
+ r100_cp_fini(rdev);
+ r100_wb_fini(rdev);
+ r100_ib_fini(rdev);
+diff --git a/drivers/gpu/drm/radeon/rs600.c b/drivers/gpu/drm/radeon/rs600.c
+index 4444f48..c381856 100644
+--- a/drivers/gpu/drm/radeon/rs600.c
++++ b/drivers/gpu/drm/radeon/rs600.c
+@@ -45,6 +45,124 @@
+ void rs600_gpu_init(struct radeon_device *rdev);
+ int rs600_mc_wait_for_idle(struct radeon_device *rdev);
+
++int rs600_mc_init(struct radeon_device *rdev)
++{
++ /* read back the MC value from the hw */
++ int r;
++ u32 tmp;
++
++ /* Setup GPU memory space */
++ tmp = RREG32_MC(R_000004_MC_FB_LOCATION);
++ rdev->mc.vram_location = G_000004_MC_FB_START(tmp) << 16;
++ rdev->mc.gtt_location = 0xffffffffUL;
++ r = radeon_mc_setup(rdev);
++ rdev->mc.igp_sideport_enabled = radeon_atombios_sideport_present(rdev);
++ if (r)
++ return r;
++ return 0;
++}
++
++/* hpd for digital panel detect/disconnect */
++bool rs600_hpd_sense(struct radeon_device *rdev, enum radeon_hpd_id hpd)
++{
++ u32 tmp;
++ bool connected = false;
++
++ switch (hpd) {
++ case RADEON_HPD_1:
++ tmp = RREG32(R_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS);
++ if (G_007D04_DC_HOT_PLUG_DETECT1_SENSE(tmp))
++ connected = true;
++ break;
++ case RADEON_HPD_2:
++ tmp = RREG32(R_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS);
++ if (G_007D14_DC_HOT_PLUG_DETECT2_SENSE(tmp))
++ connected = true;
++ break;
++ default:
++ break;
++ }
++ return connected;
++}
++
++void rs600_hpd_set_polarity(struct radeon_device *rdev,
++ enum radeon_hpd_id hpd)
++{
++ u32 tmp;
++ bool connected = rs600_hpd_sense(rdev, hpd);
++
++ switch (hpd) {
++ case RADEON_HPD_1:
++ tmp = RREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL);
++ if (connected)
++ tmp &= ~S_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY(1);
++ else
++ tmp |= S_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY(1);
++ WREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp);
++ break;
++ case RADEON_HPD_2:
++ tmp = RREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL);
++ if (connected)
++ tmp &= ~S_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY(1);
++ else
++ tmp |= S_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY(1);
++ WREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp);
++ break;
++ default:
++ break;
++ }
++}
++
++void rs600_hpd_init(struct radeon_device *rdev)
++{
++ struct drm_device *dev = rdev->ddev;
++ struct drm_connector *connector;
++
++ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
++ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
++ switch (radeon_connector->hpd.hpd) {
++ case RADEON_HPD_1:
++ WREG32(R_007D00_DC_HOT_PLUG_DETECT1_CONTROL,
++ S_007D00_DC_HOT_PLUG_DETECT1_EN(1));
++ rdev->irq.hpd[0] = true;
++ break;
++ case RADEON_HPD_2:
++ WREG32(R_007D10_DC_HOT_PLUG_DETECT2_CONTROL,
++ S_007D10_DC_HOT_PLUG_DETECT2_EN(1));
++ rdev->irq.hpd[1] = true;
++ break;
++ default:
++ break;
++ }
++ }
++ if (rdev->irq.installed)
++ rs600_irq_set(rdev);
++}
++
++void rs600_hpd_fini(struct radeon_device *rdev)
++{
++ struct drm_device *dev = rdev->ddev;
++ struct drm_connector *connector;
++
++ list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
++ struct radeon_connector *radeon_connector = to_radeon_connector(connector);
++ switch (radeon_connector->hpd.hpd) {
++ case RADEON_HPD_1:
++ WREG32(R_007D00_DC_HOT_PLUG_DETECT1_CONTROL,
++ S_007D00_DC_HOT_PLUG_DETECT1_EN(0));
++ rdev->irq.hpd[0] = false;
++ break;
++ case RADEON_HPD_2:
++ WREG32(R_007D10_DC_HOT_PLUG_DETECT2_CONTROL,
++ S_007D10_DC_HOT_PLUG_DETECT2_EN(0));
++ rdev->irq.hpd[1] = false;
++ break;
++ default:
++ break;
++ }
++ }
++}
++
+ /*
+ * GART.
+ */
+@@ -100,40 +218,40 @@ int rs600_gart_enable(struct radeon_device *rdev)
+ WREG32(R_00004C_BUS_CNTL, tmp);
+ /* FIXME: setup default page */
+ WREG32_MC(R_000100_MC_PT0_CNTL,
+- (S_000100_EFFECTIVE_L2_CACHE_SIZE(6) |
+- S_000100_EFFECTIVE_L2_QUEUE_SIZE(6)));
++ (S_000100_EFFECTIVE_L2_CACHE_SIZE(6) |
++ S_000100_EFFECTIVE_L2_QUEUE_SIZE(6)));
++
+ for (i = 0; i < 19; i++) {
+ WREG32_MC(R_00016C_MC_PT0_CLIENT0_CNTL + i,
+- S_00016C_ENABLE_TRANSLATION_MODE_OVERRIDE(1) |
+- S_00016C_SYSTEM_ACCESS_MODE_MASK(
+- V_00016C_SYSTEM_ACCESS_MODE_IN_SYS) |
+- S_00016C_SYSTEM_APERTURE_UNMAPPED_ACCESS(
+- V_00016C_SYSTEM_APERTURE_UNMAPPED_DEFAULT_PAGE) |
+- S_00016C_EFFECTIVE_L1_CACHE_SIZE(1) |
+- S_00016C_ENABLE_FRAGMENT_PROCESSING(1) |
+- S_00016C_EFFECTIVE_L1_QUEUE_SIZE(1));
++ S_00016C_ENABLE_TRANSLATION_MODE_OVERRIDE(1) |
++ S_00016C_SYSTEM_ACCESS_MODE_MASK(
++ V_00016C_SYSTEM_ACCESS_MODE_NOT_IN_SYS) |
++ S_00016C_SYSTEM_APERTURE_UNMAPPED_ACCESS(
++ V_00016C_SYSTEM_APERTURE_UNMAPPED_PASSTHROUGH) |
++ S_00016C_EFFECTIVE_L1_CACHE_SIZE(3) |
++ S_00016C_ENABLE_FRAGMENT_PROCESSING(1) |
++ S_00016C_EFFECTIVE_L1_QUEUE_SIZE(3));
+ }
+-
+- /* System context map to GART space */
+- WREG32_MC(R_000112_MC_PT0_SYSTEM_APERTURE_LOW_ADDR, rdev->mc.gtt_start);
+- WREG32_MC(R_000114_MC_PT0_SYSTEM_APERTURE_HIGH_ADDR, rdev->mc.gtt_end);
+-
+ /* enable first context */
+- WREG32_MC(R_00013C_MC_PT0_CONTEXT0_FLAT_START_ADDR, rdev->mc.gtt_start);
+- WREG32_MC(R_00014C_MC_PT0_CONTEXT0_FLAT_END_ADDR, rdev->mc.gtt_end);
+ WREG32_MC(R_000102_MC_PT0_CONTEXT0_CNTL,
+- S_000102_ENABLE_PAGE_TABLE(1) |
+- S_000102_PAGE_TABLE_DEPTH(V_000102_PAGE_TABLE_FLAT));
++ S_000102_ENABLE_PAGE_TABLE(1) |
++ S_000102_PAGE_TABLE_DEPTH(V_000102_PAGE_TABLE_FLAT));
++
+ /* disable all other contexts */
+- for (i = 1; i < 8; i++) {
++ for (i = 1; i < 8; i++)
+ WREG32_MC(R_000102_MC_PT0_CONTEXT0_CNTL + i, 0);
+- }
+
+ /* setup the page table */
+ WREG32_MC(R_00012C_MC_PT0_CONTEXT0_FLAT_BASE_ADDR,
+- rdev->gart.table_addr);
++ rdev->gart.table_addr);
++ WREG32_MC(R_00013C_MC_PT0_CONTEXT0_FLAT_START_ADDR, rdev->mc.gtt_start);
++ WREG32_MC(R_00014C_MC_PT0_CONTEXT0_FLAT_END_ADDR, rdev->mc.gtt_end);
+ WREG32_MC(R_00011C_MC_PT0_CONTEXT0_DEFAULT_READ_ADDR, 0);
+
++ /* System context maps to VRAM space */
++ WREG32_MC(R_000112_MC_PT0_SYSTEM_APERTURE_LOW_ADDR, rdev->mc.vram_start);
++ WREG32_MC(R_000114_MC_PT0_SYSTEM_APERTURE_HIGH_ADDR, rdev->mc.vram_end);
++
+ /* enable page tables */
+ tmp = RREG32_MC(R_000100_MC_PT0_CNTL);
+ WREG32_MC(R_000100_MC_PT0_CNTL, (tmp | S_000100_ENABLE_PT(1)));
+@@ -146,15 +264,20 @@ int rs600_gart_enable(struct radeon_device *rdev)
+
+ void rs600_gart_disable(struct radeon_device *rdev)
+ {
+- uint32_t tmp;
++ u32 tmp;
++ int r;
+
+ /* FIXME: disable out of gart access */
+ WREG32_MC(R_000100_MC_PT0_CNTL, 0);
+ tmp = RREG32_MC(R_000009_MC_CNTL1);
+ WREG32_MC(R_000009_MC_CNTL1, tmp & C_000009_ENABLE_PAGE_TABLES);
+ if (rdev->gart.table.vram.robj) {
+- radeon_object_kunmap(rdev->gart.table.vram.robj);
+- radeon_object_unpin(rdev->gart.table.vram.robj);
++ r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
++ if (r == 0) {
++ radeon_bo_kunmap(rdev->gart.table.vram.robj);
++ radeon_bo_unpin(rdev->gart.table.vram.robj);
++ radeon_bo_unreserve(rdev->gart.table.vram.robj);
++ }
+ }
+ }
+
+@@ -189,7 +312,16 @@ int rs600_irq_set(struct radeon_device *rdev)
+ {
+ uint32_t tmp = 0;
+ uint32_t mode_int = 0;
+-
++ u32 hpd1 = RREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL) &
++ ~S_007D08_DC_HOT_PLUG_DETECT1_INT_EN(1);
++ u32 hpd2 = RREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL) &
++ ~S_007D18_DC_HOT_PLUG_DETECT2_INT_EN(1);
++
++ if (!rdev->irq.installed) {
++ WARN(1, "Can't enable IRQ/MSI because no handler is installed.\n");
++ WREG32(R_000040_GEN_INT_CNTL, 0);
++ return -EINVAL;
++ }
+ if (rdev->irq.sw_int) {
+ tmp |= S_000040_SW_INT_EN(1);
+ }
+@@ -199,8 +331,16 @@ int rs600_irq_set(struct radeon_device *rdev)
+ if (rdev->irq.crtc_vblank_int[1]) {
+ mode_int |= S_006540_D2MODE_VBLANK_INT_MASK(1);
+ }
++ if (rdev->irq.hpd[0]) {
++ hpd1 |= S_007D08_DC_HOT_PLUG_DETECT1_INT_EN(1);
++ }
++ if (rdev->irq.hpd[1]) {
++ hpd2 |= S_007D18_DC_HOT_PLUG_DETECT2_INT_EN(1);
++ }
+ WREG32(R_000040_GEN_INT_CNTL, tmp);
+ WREG32(R_006540_DxMODE_INT_MASK, mode_int);
++ WREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL, hpd1);
++ WREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL, hpd2);
+ return 0;
+ }
+
+@@ -208,6 +348,7 @@ static inline uint32_t rs600_irq_ack(struct radeon_device *rdev, u32 *r500_disp_
+ {
+ uint32_t irqs = RREG32(R_000044_GEN_INT_STATUS);
+ uint32_t irq_mask = ~C_000044_SW_INT;
++ u32 tmp;
+
+ if (G_000044_DISPLAY_INT_STAT(irqs)) {
+ *r500_disp_int = RREG32(R_007EDC_DISP_INTERRUPT_STATUS);
+@@ -219,6 +360,16 @@ static inline uint32_t rs600_irq_ack(struct radeon_device *rdev, u32 *r500_disp_
+ WREG32(R_006D34_D2MODE_VBLANK_STATUS,
+ S_006D34_D2MODE_VBLANK_ACK(1));
+ }
++ if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(*r500_disp_int)) {
++ tmp = RREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL);
++ tmp |= S_007D08_DC_HOT_PLUG_DETECT1_INT_ACK(1);
++ WREG32(R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL, tmp);
++ }
++ if (G_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(*r500_disp_int)) {
++ tmp = RREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL);
++ tmp |= S_007D18_DC_HOT_PLUG_DETECT2_INT_ACK(1);
++ WREG32(R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL, tmp);
++ }
+ } else {
+ *r500_disp_int = 0;
+ }
+@@ -244,6 +395,7 @@ int rs600_irq_process(struct radeon_device *rdev)
+ {
+ uint32_t status, msi_rearm;
+ uint32_t r500_disp_int;
++ bool queue_hotplug = false;
+
+ status = rs600_irq_ack(rdev, &r500_disp_int);
+ if (!status && !r500_disp_int) {
+@@ -251,15 +403,25 @@ int rs600_irq_process(struct radeon_device *rdev)
+ }
+ while (status || r500_disp_int) {
+ /* SW interrupt */
+- if (G_000040_SW_INT_EN(status))
++ if (G_000044_SW_INT(status))
+ radeon_fence_process(rdev);
+ /* Vertical blank interrupts */
+ if (G_007EDC_LB_D1_VBLANK_INTERRUPT(r500_disp_int))
+ drm_handle_vblank(rdev->ddev, 0);
+ if (G_007EDC_LB_D2_VBLANK_INTERRUPT(r500_disp_int))
+ drm_handle_vblank(rdev->ddev, 1);
++ if (G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(r500_disp_int)) {
++ queue_hotplug = true;
++ DRM_DEBUG("HPD1\n");
++ }
++ if (G_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(r500_disp_int)) {
++ queue_hotplug = true;
++ DRM_DEBUG("HPD2\n");
++ }
+ status = rs600_irq_ack(rdev, &r500_disp_int);
+ }
++ if (queue_hotplug)
++ queue_work(rdev->wq, &rdev->hotplug_work);
+ if (rdev->msi_enabled) {
+ switch (rdev->family) {
+ case CHIP_RS600:
+@@ -397,8 +559,8 @@ static int rs600_startup(struct radeon_device *rdev)
+ if (r)
+ return r;
+ /* Enable IRQ */
+- rdev->irq.sw_int = true;
+ rs600_irq_set(rdev);
++ rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
+ /* 1M ring buffer */
+ r = r100_cp_init(rdev, 1024 * 1024);
+ if (r) {
+@@ -432,6 +594,8 @@ int rs600_resume(struct radeon_device *rdev)
+ atom_asic_init(rdev->mode_info.atom_context);
+ /* Resume clock after posting */
+ rv515_clock_startup(rdev);
++ /* Initialize surface registers */
++ radeon_surface_init(rdev);
+ return rs600_startup(rdev);
+ }
+
+@@ -446,7 +610,6 @@ int rs600_suspend(struct radeon_device *rdev)
+
+ void rs600_fini(struct radeon_device *rdev)
+ {
+- rs600_suspend(rdev);
+ r100_cp_fini(rdev);
+ r100_wb_fini(rdev);
+ r100_ib_fini(rdev);
+@@ -454,7 +617,7 @@ void rs600_fini(struct radeon_device *rdev)
+ rs600_gart_fini(rdev);
+ radeon_irq_kms_fini(rdev);
+ radeon_fence_driver_fini(rdev);
+- radeon_object_fini(rdev);
++ radeon_bo_fini(rdev);
+ radeon_atombios_fini(rdev);
+ kfree(rdev->bios);
+ rdev->bios = NULL;
+@@ -491,10 +654,9 @@ int rs600_init(struct radeon_device *rdev)
+ RREG32(R_0007C0_CP_STAT));
+ }
+ /* check if cards are posted or not */
+- if (!radeon_card_posted(rdev) && rdev->bios) {
+- DRM_INFO("GPU not posted. posting now...\n");
+- atom_asic_init(rdev->mode_info.atom_context);
+- }
++ if (radeon_boot_test_post_card(rdev) == false)
++ return -EINVAL;
++
+ /* Initialize clocks */
+ radeon_get_clock_info(rdev->ddev);
+ /* Initialize power management */
+@@ -502,7 +664,7 @@ int rs600_init(struct radeon_device *rdev)
+ /* Get vram informations */
+ rs600_vram_info(rdev);
+ /* Initialize memory controller (also test AGP) */
+- r = r420_mc_init(rdev);
++ r = rs600_mc_init(rdev);
+ if (r)
+ return r;
+ rs600_debugfs(rdev);
+@@ -514,7 +676,7 @@ int rs600_init(struct radeon_device *rdev)
+ if (r)
+ return r;
+ /* Memory manager */
+- r = radeon_object_init(rdev);
++ r = radeon_bo_init(rdev);
+ if (r)
+ return r;
+ r = rs600_gart_init(rdev);
+@@ -526,7 +688,6 @@ int rs600_init(struct radeon_device *rdev)
+ if (r) {
+ /* Somethings want wront with the accel init stop accel */
+ dev_err(rdev->dev, "Disabling GPU acceleration\n");
+- rs600_suspend(rdev);
+ r100_cp_fini(rdev);
+ r100_wb_fini(rdev);
+ r100_ib_fini(rdev);
+diff --git a/drivers/gpu/drm/radeon/rs600d.h b/drivers/gpu/drm/radeon/rs600d.h
+index 8130892..c1c8f58 100644
+--- a/drivers/gpu/drm/radeon/rs600d.h
++++ b/drivers/gpu/drm/radeon/rs600d.h
+@@ -30,27 +30,12 @@
+
+ /* Registers */
+ #define R_000040_GEN_INT_CNTL 0x000040
+-#define S_000040_DISPLAY_INT_STATUS(x) (((x) & 0x1) << 0)
+-#define G_000040_DISPLAY_INT_STATUS(x) (((x) >> 0) & 0x1)
+-#define C_000040_DISPLAY_INT_STATUS 0xFFFFFFFE
+-#define S_000040_DMA_VIPH0_INT_EN(x) (((x) & 0x1) << 12)
+-#define G_000040_DMA_VIPH0_INT_EN(x) (((x) >> 12) & 0x1)
+-#define C_000040_DMA_VIPH0_INT_EN 0xFFFFEFFF
+-#define S_000040_CRTC2_VSYNC(x) (((x) & 0x1) << 6)
+-#define G_000040_CRTC2_VSYNC(x) (((x) >> 6) & 0x1)
+-#define C_000040_CRTC2_VSYNC 0xFFFFFFBF
+-#define S_000040_SNAPSHOT2(x) (((x) & 0x1) << 7)
+-#define G_000040_SNAPSHOT2(x) (((x) >> 7) & 0x1)
+-#define C_000040_SNAPSHOT2 0xFFFFFF7F
+-#define S_000040_CRTC2_VBLANK(x) (((x) & 0x1) << 9)
+-#define G_000040_CRTC2_VBLANK(x) (((x) >> 9) & 0x1)
+-#define C_000040_CRTC2_VBLANK 0xFFFFFDFF
+-#define S_000040_FP2_DETECT(x) (((x) & 0x1) << 10)
+-#define G_000040_FP2_DETECT(x) (((x) >> 10) & 0x1)
+-#define C_000040_FP2_DETECT 0xFFFFFBFF
+-#define S_000040_VSYNC_DIFF_OVER_LIMIT(x) (((x) & 0x1) << 11)
+-#define G_000040_VSYNC_DIFF_OVER_LIMIT(x) (((x) >> 11) & 0x1)
+-#define C_000040_VSYNC_DIFF_OVER_LIMIT 0xFFFFF7FF
++#define S_000040_SCRATCH_INT_MASK(x) (((x) & 0x1) << 18)
++#define G_000040_SCRATCH_INT_MASK(x) (((x) >> 18) & 0x1)
++#define C_000040_SCRATCH_INT_MASK 0xFFFBFFFF
++#define S_000040_GUI_IDLE_MASK(x) (((x) & 0x1) << 19)
++#define G_000040_GUI_IDLE_MASK(x) (((x) >> 19) & 0x1)
++#define C_000040_GUI_IDLE_MASK 0xFFF7FFFF
+ #define S_000040_DMA_VIPH1_INT_EN(x) (((x) & 0x1) << 13)
+ #define G_000040_DMA_VIPH1_INT_EN(x) (((x) >> 13) & 0x1)
+ #define C_000040_DMA_VIPH1_INT_EN 0xFFFFDFFF
+@@ -370,7 +355,90 @@
+ #define S_007EDC_LB_D2_VBLANK_INTERRUPT(x) (((x) & 0x1) << 5)
+ #define G_007EDC_LB_D2_VBLANK_INTERRUPT(x) (((x) >> 5) & 0x1)
+ #define C_007EDC_LB_D2_VBLANK_INTERRUPT 0xFFFFFFDF
+-
++#define S_007EDC_DACA_AUTODETECT_INTERRUPT(x) (((x) & 0x1) << 16)
++#define G_007EDC_DACA_AUTODETECT_INTERRUPT(x) (((x) >> 16) & 0x1)
++#define C_007EDC_DACA_AUTODETECT_INTERRUPT 0xFFFEFFFF
++#define S_007EDC_DACB_AUTODETECT_INTERRUPT(x) (((x) & 0x1) << 17)
++#define G_007EDC_DACB_AUTODETECT_INTERRUPT(x) (((x) >> 17) & 0x1)
++#define C_007EDC_DACB_AUTODETECT_INTERRUPT 0xFFFDFFFF
++#define S_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(x) (((x) & 0x1) << 18)
++#define G_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT(x) (((x) >> 18) & 0x1)
++#define C_007EDC_DC_HOT_PLUG_DETECT1_INTERRUPT 0xFFFBFFFF
++#define S_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(x) (((x) & 0x1) << 19)
++#define G_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT(x) (((x) >> 19) & 0x1)
++#define C_007EDC_DC_HOT_PLUG_DETECT2_INTERRUPT 0xFFF7FFFF
++#define R_007828_DACA_AUTODETECT_CONTROL 0x007828
++#define S_007828_DACA_AUTODETECT_MODE(x) (((x) & 0x3) << 0)
++#define G_007828_DACA_AUTODETECT_MODE(x) (((x) >> 0) & 0x3)
++#define C_007828_DACA_AUTODETECT_MODE 0xFFFFFFFC
++#define S_007828_DACA_AUTODETECT_FRAME_TIME_COUNTER(x) (((x) & 0xff) << 8)
++#define G_007828_DACA_AUTODETECT_FRAME_TIME_COUNTER(x) (((x) >> 8) & 0xff)
++#define C_007828_DACA_AUTODETECT_FRAME_TIME_COUNTER 0xFFFF00FF
++#define S_007828_DACA_AUTODETECT_CHECK_MASK(x) (((x) & 0x3) << 16)
++#define G_007828_DACA_AUTODETECT_CHECK_MASK(x) (((x) >> 16) & 0x3)
++#define C_007828_DACA_AUTODETECT_CHECK_MASK 0xFFFCFFFF
++#define R_007838_DACA_AUTODETECT_INT_CONTROL 0x007838
++#define S_007838_DACA_AUTODETECT_ACK(x) (((x) & 0x1) << 0)
++#define C_007838_DACA_DACA_AUTODETECT_ACK 0xFFFFFFFE
++#define S_007838_DACA_AUTODETECT_INT_ENABLE(x) (((x) & 0x1) << 16)
++#define G_007838_DACA_AUTODETECT_INT_ENABLE(x) (((x) >> 16) & 0x1)
++#define C_007838_DACA_AUTODETECT_INT_ENABLE 0xFFFCFFFF
++#define R_007A28_DACB_AUTODETECT_CONTROL 0x007A28
++#define S_007A28_DACB_AUTODETECT_MODE(x) (((x) & 0x3) << 0)
++#define G_007A28_DACB_AUTODETECT_MODE(x) (((x) >> 0) & 0x3)
++#define C_007A28_DACB_AUTODETECT_MODE 0xFFFFFFFC
++#define S_007A28_DACB_AUTODETECT_FRAME_TIME_COUNTER(x) (((x) & 0xff) << 8)
++#define G_007A28_DACB_AUTODETECT_FRAME_TIME_COUNTER(x) (((x) >> 8) & 0xff)
++#define C_007A28_DACB_AUTODETECT_FRAME_TIME_COUNTER 0xFFFF00FF
++#define S_007A28_DACB_AUTODETECT_CHECK_MASK(x) (((x) & 0x3) << 16)
++#define G_007A28_DACB_AUTODETECT_CHECK_MASK(x) (((x) >> 16) & 0x3)
++#define C_007A28_DACB_AUTODETECT_CHECK_MASK 0xFFFCFFFF
++#define R_007A38_DACB_AUTODETECT_INT_CONTROL 0x007A38
++#define S_007A38_DACB_AUTODETECT_ACK(x) (((x) & 0x1) << 0)
++#define C_007A38_DACB_DACA_AUTODETECT_ACK 0xFFFFFFFE
++#define S_007A38_DACB_AUTODETECT_INT_ENABLE(x) (((x) & 0x1) << 16)
++#define G_007A38_DACB_AUTODETECT_INT_ENABLE(x) (((x) >> 16) & 0x1)
++#define C_007A38_DACB_AUTODETECT_INT_ENABLE 0xFFFCFFFF
++#define R_007D00_DC_HOT_PLUG_DETECT1_CONTROL 0x007D00
++#define S_007D00_DC_HOT_PLUG_DETECT1_EN(x) (((x) & 0x1) << 0)
++#define G_007D00_DC_HOT_PLUG_DETECT1_EN(x) (((x) >> 0) & 0x1)
++#define C_007D00_DC_HOT_PLUG_DETECT1_EN 0xFFFFFFFE
++#define R_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS 0x007D04
++#define S_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS(x) (((x) & 0x1) << 0)
++#define G_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS(x) (((x) >> 0) & 0x1)
++#define C_007D04_DC_HOT_PLUG_DETECT1_INT_STATUS 0xFFFFFFFE
++#define S_007D04_DC_HOT_PLUG_DETECT1_SENSE(x) (((x) & 0x1) << 1)
++#define G_007D04_DC_HOT_PLUG_DETECT1_SENSE(x) (((x) >> 1) & 0x1)
++#define C_007D04_DC_HOT_PLUG_DETECT1_SENSE 0xFFFFFFFD
++#define R_007D08_DC_HOT_PLUG_DETECT1_INT_CONTROL 0x007D08
++#define S_007D08_DC_HOT_PLUG_DETECT1_INT_ACK(x) (((x) & 0x1) << 0)
++#define C_007D08_DC_HOT_PLUG_DETECT1_INT_ACK 0xFFFFFFFE
++#define S_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY(x) (((x) & 0x1) << 8)
++#define G_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY(x) (((x) >> 8) & 0x1)
++#define C_007D08_DC_HOT_PLUG_DETECT1_INT_POLARITY 0xFFFFFEFF
++#define S_007D08_DC_HOT_PLUG_DETECT1_INT_EN(x) (((x) & 0x1) << 16)
++#define G_007D08_DC_HOT_PLUG_DETECT1_INT_EN(x) (((x) >> 16) & 0x1)
++#define C_007D08_DC_HOT_PLUG_DETECT1_INT_EN 0xFFFEFFFF
++#define R_007D10_DC_HOT_PLUG_DETECT2_CONTROL 0x007D10
++#define S_007D10_DC_HOT_PLUG_DETECT2_EN(x) (((x) & 0x1) << 0)
++#define G_007D10_DC_HOT_PLUG_DETECT2_EN(x) (((x) >> 0) & 0x1)
++#define C_007D10_DC_HOT_PLUG_DETECT2_EN 0xFFFFFFFE
++#define R_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS 0x007D14
++#define S_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS(x) (((x) & 0x1) << 0)
++#define G_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS(x) (((x) >> 0) & 0x1)
++#define C_007D14_DC_HOT_PLUG_DETECT2_INT_STATUS 0xFFFFFFFE
++#define S_007D14_DC_HOT_PLUG_DETECT2_SENSE(x) (((x) & 0x1) << 1)
++#define G_007D14_DC_HOT_PLUG_DETECT2_SENSE(x) (((x) >> 1) & 0x1)
++#define C_007D14_DC_HOT_PLUG_DETECT2_SENSE 0xFFFFFFFD
++#define R_007D18_DC_HOT_PLUG_DETECT2_INT_CONTROL 0x007D18
++#define S_007D18_DC_HOT_PLUG_DETECT2_INT_ACK(x) (((x) & 0x1) << 0)
++#define C_007D18_DC_HOT_PLUG_DETECT2_INT_ACK 0xFFFFFFFE
++#define S_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY(x) (((x) & 0x1) << 8)
++#define G_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY(x) (((x) >> 8) & 0x1)
++#define C_007D18_DC_HOT_PLUG_DETECT2_INT_POLARITY 0xFFFFFEFF
++#define S_007D18_DC_HOT_PLUG_DETECT2_INT_EN(x) (((x) & 0x1) << 16)
++#define G_007D18_DC_HOT_PLUG_DETECT2_INT_EN(x) (((x) >> 16) & 0x1)
++#define C_007D18_DC_HOT_PLUG_DETECT2_INT_EN 0xFFFEFFFF
+
+ /* MC registers */
+ #define R_000000_MC_STATUS 0x000000
+diff --git a/drivers/gpu/drm/radeon/rs690.c b/drivers/gpu/drm/radeon/rs690.c
+index b12ff76..06e2771 100644
+--- a/drivers/gpu/drm/radeon/rs690.c
++++ b/drivers/gpu/drm/radeon/rs690.c
+@@ -162,6 +162,22 @@ void rs690_vram_info(struct radeon_device *rdev)
+ rdev->pm.core_bandwidth.full = rfixed_div(rdev->pm.sclk, a);
+ }
+
++static int rs690_mc_init(struct radeon_device *rdev)
++{
++ int r;
++ u32 tmp;
++
++ /* Setup GPU memory space */
++ tmp = RREG32_MC(R_000100_MCCFG_FB_LOCATION);
++ rdev->mc.vram_location = G_000100_MC_FB_START(tmp) << 16;
++ rdev->mc.gtt_location = 0xFFFFFFFFUL;
++ r = radeon_mc_setup(rdev);
++ rdev->mc.igp_sideport_enabled = radeon_atombios_sideport_present(rdev);
++ if (r)
++ return r;
++ return 0;
++}
++
+ void rs690_line_buffer_adjust(struct radeon_device *rdev,
+ struct drm_display_mode *mode1,
+ struct drm_display_mode *mode2)
+@@ -245,8 +261,9 @@ void rs690_crtc_bandwidth_compute(struct radeon_device *rdev,
+
+ b.full = rfixed_const(mode->crtc_hdisplay);
+ c.full = rfixed_const(256);
+- a.full = rfixed_mul(wm->num_line_pair, b);
+- request_fifo_depth.full = rfixed_div(a, c);
++ a.full = rfixed_div(b, c);
++ request_fifo_depth.full = rfixed_mul(a, wm->num_line_pair);
++ request_fifo_depth.full = rfixed_ceil(request_fifo_depth);
+ if (a.full < rfixed_const(4)) {
+ wm->lb_request_fifo_depth = 4;
+ } else {
+@@ -375,6 +392,7 @@ void rs690_crtc_bandwidth_compute(struct radeon_device *rdev,
+ a.full = rfixed_const(16);
+ wm->priority_mark_max.full = rfixed_const(crtc->base.mode.crtc_hdisplay);
+ wm->priority_mark_max.full = rfixed_div(wm->priority_mark_max, a);
++ wm->priority_mark_max.full = rfixed_ceil(wm->priority_mark_max);
+
+ /* Determine estimated width */
+ estimated_width.full = tolerable_latency.full - wm->worst_case_latency.full;
+@@ -384,6 +402,7 @@ void rs690_crtc_bandwidth_compute(struct radeon_device *rdev,
+ } else {
+ a.full = rfixed_const(16);
+ wm->priority_mark.full = rfixed_div(estimated_width, a);
++ wm->priority_mark.full = rfixed_ceil(wm->priority_mark);
+ wm->priority_mark.full = wm->priority_mark_max.full - wm->priority_mark.full;
+ }
+ }
+@@ -606,8 +625,8 @@ static int rs690_startup(struct radeon_device *rdev)
+ if (r)
+ return r;
+ /* Enable IRQ */
+- rdev->irq.sw_int = true;
+ rs600_irq_set(rdev);
++ rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
+ /* 1M ring buffer */
+ r = r100_cp_init(rdev, 1024 * 1024);
+ if (r) {
+@@ -641,6 +660,8 @@ int rs690_resume(struct radeon_device *rdev)
+ atom_asic_init(rdev->mode_info.atom_context);
+ /* Resume clock after posting */
+ rv515_clock_startup(rdev);
++ /* Initialize surface registers */
++ radeon_surface_init(rdev);
+ return rs690_startup(rdev);
+ }
+
+@@ -655,7 +676,6 @@ int rs690_suspend(struct radeon_device *rdev)
+
+ void rs690_fini(struct radeon_device *rdev)
+ {
+- rs690_suspend(rdev);
+ r100_cp_fini(rdev);
+ r100_wb_fini(rdev);
+ r100_ib_fini(rdev);
+@@ -663,7 +683,7 @@ void rs690_fini(struct radeon_device *rdev)
+ rs400_gart_fini(rdev);
+ radeon_irq_kms_fini(rdev);
+ radeon_fence_driver_fini(rdev);
+- radeon_object_fini(rdev);
++ radeon_bo_fini(rdev);
+ radeon_atombios_fini(rdev);
+ kfree(rdev->bios);
+ rdev->bios = NULL;
+@@ -701,10 +721,9 @@ int rs690_init(struct radeon_device *rdev)
+ RREG32(R_0007C0_CP_STAT));
+ }
+ /* check if cards are posted or not */
+- if (!radeon_card_posted(rdev) && rdev->bios) {
+- DRM_INFO("GPU not posted. posting now...\n");
+- atom_asic_init(rdev->mode_info.atom_context);
+- }
++ if (radeon_boot_test_post_card(rdev) == false)
++ return -EINVAL;
++
+ /* Initialize clocks */
+ radeon_get_clock_info(rdev->ddev);
+ /* Initialize power management */
+@@ -712,7 +731,7 @@ int rs690_init(struct radeon_device *rdev)
+ /* Get vram informations */
+ rs690_vram_info(rdev);
+ /* Initialize memory controller (also test AGP) */
+- r = r420_mc_init(rdev);
++ r = rs690_mc_init(rdev);
+ if (r)
+ return r;
+ rv515_debugfs(rdev);
+@@ -724,7 +743,7 @@ int rs690_init(struct radeon_device *rdev)
+ if (r)
+ return r;
+ /* Memory manager */
+- r = radeon_object_init(rdev);
++ r = radeon_bo_init(rdev);
+ if (r)
+ return r;
+ r = rs400_gart_init(rdev);
+@@ -736,7 +755,6 @@ int rs690_init(struct radeon_device *rdev)
+ if (r) {
+ /* Somethings want wront with the accel init stop accel */
+ dev_err(rdev->dev, "Disabling GPU acceleration\n");
+- rs690_suspend(rdev);
+ r100_cp_fini(rdev);
+ r100_wb_fini(rdev);
+ r100_ib_fini(rdev);
+diff --git a/drivers/gpu/drm/radeon/rv515.c b/drivers/gpu/drm/radeon/rv515.c
+index ba68c9f..0e1e6b8 100644
+--- a/drivers/gpu/drm/radeon/rv515.c
++++ b/drivers/gpu/drm/radeon/rv515.c
+@@ -478,8 +478,8 @@ static int rv515_startup(struct radeon_device *rdev)
+ return r;
+ }
+ /* Enable IRQ */
+- rdev->irq.sw_int = true;
+ rs600_irq_set(rdev);
++ rdev->config.r300.hdp_cntl = RREG32(RADEON_HOST_PATH_CNTL);
+ /* 1M ring buffer */
+ r = r100_cp_init(rdev, 1024 * 1024);
+ if (r) {
+@@ -514,6 +514,8 @@ int rv515_resume(struct radeon_device *rdev)
+ atom_asic_init(rdev->mode_info.atom_context);
+ /* Resume clock after posting */
+ rv515_clock_startup(rdev);
++ /* Initialize surface registers */
++ radeon_surface_init(rdev);
+ return rv515_startup(rdev);
+ }
+
+@@ -535,16 +537,15 @@ void rv515_set_safe_registers(struct radeon_device *rdev)
+
+ void rv515_fini(struct radeon_device *rdev)
+ {
+- rv515_suspend(rdev);
+ r100_cp_fini(rdev);
+ r100_wb_fini(rdev);
+ r100_ib_fini(rdev);
+ radeon_gem_fini(rdev);
+- rv370_pcie_gart_fini(rdev);
++ rv370_pcie_gart_fini(rdev);
+ radeon_agp_fini(rdev);
+ radeon_irq_kms_fini(rdev);
+ radeon_fence_driver_fini(rdev);
+- radeon_object_fini(rdev);
++ radeon_bo_fini(rdev);
+ radeon_atombios_fini(rdev);
+ kfree(rdev->bios);
+ rdev->bios = NULL;
+@@ -580,10 +581,8 @@ int rv515_init(struct radeon_device *rdev)
+ RREG32(R_0007C0_CP_STAT));
+ }
+ /* check if cards are posted or not */
+- if (!radeon_card_posted(rdev) && rdev->bios) {
+- DRM_INFO("GPU not posted. posting now...\n");
+- atom_asic_init(rdev->mode_info.atom_context);
+- }
++ if (radeon_boot_test_post_card(rdev) == false)
++ return -EINVAL;
+ /* Initialize clocks */
+ radeon_get_clock_info(rdev->ddev);
+ /* Initialize power management */
+@@ -603,7 +602,7 @@ int rv515_init(struct radeon_device *rdev)
+ if (r)
+ return r;
+ /* Memory manager */
+- r = radeon_object_init(rdev);
++ r = radeon_bo_init(rdev);
+ if (r)
+ return r;
+ r = rv370_pcie_gart_init(rdev);
+@@ -615,13 +614,12 @@ int rv515_init(struct radeon_device *rdev)
+ if (r) {
+ /* Somethings want wront with the accel init stop accel */
+ dev_err(rdev->dev, "Disabling GPU acceleration\n");
+- rv515_suspend(rdev);
+ r100_cp_fini(rdev);
+ r100_wb_fini(rdev);
+ r100_ib_fini(rdev);
++ radeon_irq_kms_fini(rdev);
+ rv370_pcie_gart_fini(rdev);
+ radeon_agp_fini(rdev);
+- radeon_irq_kms_fini(rdev);
+ rdev->accel_working = false;
+ }
+ return 0;
+@@ -892,8 +890,9 @@ void rv515_crtc_bandwidth_compute(struct radeon_device *rdev,
+
+ b.full = rfixed_const(mode->crtc_hdisplay);
+ c.full = rfixed_const(256);
+- a.full = rfixed_mul(wm->num_line_pair, b);
+- request_fifo_depth.full = rfixed_div(a, c);
++ a.full = rfixed_div(b, c);
++ request_fifo_depth.full = rfixed_mul(a, wm->num_line_pair);
++ request_fifo_depth.full = rfixed_ceil(request_fifo_depth);
+ if (a.full < rfixed_const(4)) {
+ wm->lb_request_fifo_depth = 4;
+ } else {
+@@ -995,15 +994,17 @@ void rv515_crtc_bandwidth_compute(struct radeon_device *rdev,
+ a.full = rfixed_const(16);
+ wm->priority_mark_max.full = rfixed_const(crtc->base.mode.crtc_hdisplay);
+ wm->priority_mark_max.full = rfixed_div(wm->priority_mark_max, a);
++ wm->priority_mark_max.full = rfixed_ceil(wm->priority_mark_max);
+
+ /* Determine estimated width */
+ estimated_width.full = tolerable_latency.full - wm->worst_case_latency.full;
+ estimated_width.full = rfixed_div(estimated_width, consumption_time);
+ if (rfixed_trunc(estimated_width) > crtc->base.mode.crtc_hdisplay) {
+- wm->priority_mark.full = rfixed_const(10);
++ wm->priority_mark.full = wm->priority_mark_max.full;
+ } else {
+ a.full = rfixed_const(16);
+ wm->priority_mark.full = rfixed_div(estimated_width, a);
++ wm->priority_mark.full = rfixed_ceil(wm->priority_mark);
+ wm->priority_mark.full = wm->priority_mark_max.full - wm->priority_mark.full;
+ }
+ }
+diff --git a/drivers/gpu/drm/radeon/rv770.c b/drivers/gpu/drm/radeon/rv770.c
+index b0efd0d..5943d56 100644
+--- a/drivers/gpu/drm/radeon/rv770.c
++++ b/drivers/gpu/drm/radeon/rv770.c
+@@ -92,7 +92,7 @@ int rv770_pcie_gart_enable(struct radeon_device *rdev)
+ void rv770_pcie_gart_disable(struct radeon_device *rdev)
+ {
+ u32 tmp;
+- int i;
++ int i, r;
+
+ /* Disable all tables */
+ for (i = 0; i < 7; i++)
+@@ -113,8 +113,12 @@ void rv770_pcie_gart_disable(struct radeon_device *rdev)
+ WREG32(MC_VM_MB_L1_TLB2_CNTL, tmp);
+ WREG32(MC_VM_MB_L1_TLB3_CNTL, tmp);
+ if (rdev->gart.table.vram.robj) {
+- radeon_object_kunmap(rdev->gart.table.vram.robj);
+- radeon_object_unpin(rdev->gart.table.vram.robj);
++ r = radeon_bo_reserve(rdev->gart.table.vram.robj, false);
++ if (likely(r == 0)) {
++ radeon_bo_kunmap(rdev->gart.table.vram.robj);
++ radeon_bo_unpin(rdev->gart.table.vram.robj);
++ radeon_bo_unreserve(rdev->gart.table.vram.robj);
++ }
+ }
+ }
+
+@@ -775,7 +779,6 @@ int rv770_mc_init(struct radeon_device *rdev)
+ fixed20_12 a;
+ u32 tmp;
+ int chansize, numchan;
+- int r;
+
+ /* Get VRAM informations */
+ rdev->mc.vram_is_ddr = true;
+@@ -818,9 +821,6 @@ int rv770_mc_init(struct radeon_device *rdev)
+ rdev->mc.real_vram_size = rdev->mc.aper_size;
+
+ if (rdev->flags & RADEON_IS_AGP) {
+- r = radeon_agp_init(rdev);
+- if (r)
+- return r;
+ /* gtt_size is setup by radeon_agp_init */
+ rdev->mc.gtt_location = rdev->mc.agp_base;
+ tmp = 0xFFFFFFFFUL - rdev->mc.agp_base - rdev->mc.gtt_size;
+@@ -829,11 +829,11 @@ int rv770_mc_init(struct radeon_device *rdev)
+ * AGP so that GPU can catch out of VRAM/AGP access
+ */
+ if (rdev->mc.gtt_location > rdev->mc.mc_vram_size) {
+- /* Enought place before */
++ /* Enough place before */
+ rdev->mc.vram_location = rdev->mc.gtt_location -
+ rdev->mc.mc_vram_size;
+ } else if (tmp > rdev->mc.mc_vram_size) {
+- /* Enought place after */
++ /* Enough place after */
+ rdev->mc.vram_location = rdev->mc.gtt_location +
+ rdev->mc.gtt_size;
+ } else {
+@@ -870,6 +870,14 @@ static int rv770_startup(struct radeon_device *rdev)
+ {
+ int r;
+
++ if (!rdev->me_fw || !rdev->pfp_fw || !rdev->rlc_fw) {
++ r = r600_init_microcode(rdev);
++ if (r) {
++ DRM_ERROR("Failed to load firmware!\n");
++ return r;
++ }
++ }
++
+ rv770_mc_program(rdev);
+ if (rdev->flags & RADEON_IS_AGP) {
+ rv770_agp_enable(rdev);
+@@ -879,13 +887,33 @@ static int rv770_startup(struct radeon_device *rdev)
+ return r;
+ }
+ rv770_gpu_init(rdev);
+-
+- r = radeon_object_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM,
+- &rdev->r600_blit.shader_gpu_addr);
++ r = r600_blit_init(rdev);
++ if (r) {
++ r600_blit_fini(rdev);
++ rdev->asic->copy = NULL;
++ dev_warn(rdev->dev, "failed blitter (%d) falling back to memcpy\n", r);
++ }
++ /* pin copy shader into vram */
++ if (rdev->r600_blit.shader_obj) {
++ r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
++ if (unlikely(r != 0))
++ return r;
++ r = radeon_bo_pin(rdev->r600_blit.shader_obj, RADEON_GEM_DOMAIN_VRAM,
++ &rdev->r600_blit.shader_gpu_addr);
++ radeon_bo_unreserve(rdev->r600_blit.shader_obj);
++ if (r) {
++ DRM_ERROR("failed to pin blit object %d\n", r);
++ return r;
++ }
++ }
++ /* Enable IRQ */
++ r = r600_irq_init(rdev);
+ if (r) {
+- DRM_ERROR("failed to pin blit object %d\n", r);
++ DRM_ERROR("radeon: IH init failed (%d).\n", r);
++ radeon_irq_kms_fini(rdev);
+ return r;
+ }
++ r600_irq_set(rdev);
+
+ r = radeon_ring_init(rdev, rdev->cp.ring_size);
+ if (r)
+@@ -934,13 +962,22 @@ int rv770_resume(struct radeon_device *rdev)
+
+ int rv770_suspend(struct radeon_device *rdev)
+ {
++ int r;
++
+ /* FIXME: we should wait for ring to be empty */
+ r700_cp_stop(rdev);
+ rdev->cp.ready = false;
++ r600_irq_suspend(rdev);
+ r600_wb_disable(rdev);
+ rv770_pcie_gart_disable(rdev);
+ /* unpin shaders bo */
+- radeon_object_unpin(rdev->r600_blit.shader_obj);
++ if (rdev->r600_blit.shader_obj) {
++ r = radeon_bo_reserve(rdev->r600_blit.shader_obj, false);
++ if (likely(r == 0)) {
++ radeon_bo_unpin(rdev->r600_blit.shader_obj);
++ radeon_bo_unreserve(rdev->r600_blit.shader_obj);
++ }
++ }
+ return 0;
+ }
+
+@@ -975,7 +1012,11 @@ int rv770_init(struct radeon_device *rdev)
+ if (r)
+ return r;
+ /* Post card if necessary */
+- if (!r600_card_posted(rdev) && rdev->bios) {
++ if (!r600_card_posted(rdev)) {
++ if (!rdev->bios) {
++ dev_err(rdev->dev, "Card not posted and no BIOS - ignoring\n");
++ return -EINVAL;
++ }
+ DRM_INFO("GPU not posted. posting now...\n");
+ atom_asic_init(rdev->mode_info.atom_context);
+ }
+@@ -994,53 +1035,55 @@ int rv770_init(struct radeon_device *rdev)
+ r = radeon_fence_driver_init(rdev);
+ if (r)
+ return r;
++ if (rdev->flags & RADEON_IS_AGP) {
++ r = radeon_agp_init(rdev);
++ if (r)
++ radeon_agp_disable(rdev);
++ }
+ r = rv770_mc_init(rdev);
+ if (r)
+ return r;
+ /* Memory manager */
+- r = radeon_object_init(rdev);
++ r = radeon_bo_init(rdev);
+ if (r)
+ return r;
++
++ r = radeon_irq_kms_init(rdev);
++ if (r)
++ return r;
++
+ rdev->cp.ring_obj = NULL;
+ r600_ring_init(rdev, 1024 * 1024);
+
+- if (!rdev->me_fw || !rdev->pfp_fw) {
+- r = r600_cp_init_microcode(rdev);
+- if (r) {
+- DRM_ERROR("Failed to load firmware!\n");
+- return r;
+- }
+- }
++ rdev->ih.ring_obj = NULL;
++ r600_ih_ring_init(rdev, 64 * 1024);
+
+ r = r600_pcie_gart_init(rdev);
+ if (r)
+ return r;
+
+ rdev->accel_working = true;
+- r = r600_blit_init(rdev);
+- if (r) {
+- DRM_ERROR("radeon: failled blitter (%d).\n", r);
+- rdev->accel_working = false;
+- }
+-
+ r = rv770_startup(rdev);
+ if (r) {
+- rv770_suspend(rdev);
++ dev_err(rdev->dev, "disabling GPU acceleration\n");
++ r600_cp_fini(rdev);
+ r600_wb_fini(rdev);
+- radeon_ring_fini(rdev);
++ r600_irq_fini(rdev);
++ radeon_irq_kms_fini(rdev);
+ rv770_pcie_gart_fini(rdev);
+ rdev->accel_working = false;
+ }
+ if (rdev->accel_working) {
+ r = radeon_ib_pool_init(rdev);
+ if (r) {
+- DRM_ERROR("radeon: failled initializing IB pool (%d).\n", r);
+- rdev->accel_working = false;
+- }
+- r = r600_ib_test(rdev);
+- if (r) {
+- DRM_ERROR("radeon: failled testing IB (%d).\n", r);
++ dev_err(rdev->dev, "IB initialization failed (%d).\n", r);
+ rdev->accel_working = false;
++ } else {
++ r = r600_ib_test(rdev);
++ if (r) {
++ dev_err(rdev->dev, "IB test failed (%d).\n", r);
++ rdev->accel_working = false;
++ }
+ }
+ }
+ return 0;
+@@ -1048,18 +1091,17 @@ int rv770_init(struct radeon_device *rdev)
+
+ void rv770_fini(struct radeon_device *rdev)
+ {
+- rv770_suspend(rdev);
+-
+ r600_blit_fini(rdev);
+- radeon_ring_fini(rdev);
++ r600_cp_fini(rdev);
+ r600_wb_fini(rdev);
++ r600_irq_fini(rdev);
++ radeon_irq_kms_fini(rdev);
+ rv770_pcie_gart_fini(rdev);
+ radeon_gem_fini(rdev);
+ radeon_fence_driver_fini(rdev);
+ radeon_clocks_fini(rdev);
+- if (rdev->flags & RADEON_IS_AGP)
+- radeon_agp_fini(rdev);
+- radeon_object_fini(rdev);
++ radeon_agp_fini(rdev);
++ radeon_bo_fini(rdev);
+ radeon_atombios_fini(rdev);
+ kfree(rdev->bios);
+ rdev->bios = NULL;
+diff --git a/drivers/gpu/drm/savage/savage_drv.c b/drivers/gpu/drm/savage/savage_drv.c
+index eee52aa..021de44 100644
+--- a/drivers/gpu/drm/savage/savage_drv.c
++++ b/drivers/gpu/drm/savage/savage_drv.c
+@@ -50,7 +50,7 @@ static struct drm_driver driver = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+- .ioctl = drm_ioctl,
++ .unlocked_ioctl = drm_ioctl,
+ .mmap = drm_mmap,
+ .poll = drm_poll,
+ .fasync = drm_fasync,
+diff --git a/drivers/gpu/drm/sis/sis_drv.c b/drivers/gpu/drm/sis/sis_drv.c
+index e725cc0..4fd1f06 100644
+--- a/drivers/gpu/drm/sis/sis_drv.c
++++ b/drivers/gpu/drm/sis/sis_drv.c
+@@ -80,7 +80,7 @@ static struct drm_driver driver = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+- .ioctl = drm_ioctl,
++ .unlocked_ioctl = drm_ioctl,
+ .mmap = drm_mmap,
+ .poll = drm_poll,
+ .fasync = drm_fasync,
+diff --git a/drivers/gpu/drm/tdfx/tdfx_drv.c b/drivers/gpu/drm/tdfx/tdfx_drv.c
+index 012ff2e..ec5a43e 100644
+--- a/drivers/gpu/drm/tdfx/tdfx_drv.c
++++ b/drivers/gpu/drm/tdfx/tdfx_drv.c
+@@ -48,7 +48,7 @@ static struct drm_driver driver = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+- .ioctl = drm_ioctl,
++ .unlocked_ioctl = drm_ioctl,
+ .mmap = drm_mmap,
+ .poll = drm_poll,
+ .fasync = drm_fasync,
+diff --git a/drivers/gpu/drm/ttm/Makefile b/drivers/gpu/drm/ttm/Makefile
+index b0a9de7..1e138f5 100644
+--- a/drivers/gpu/drm/ttm/Makefile
++++ b/drivers/gpu/drm/ttm/Makefile
+@@ -3,6 +3,7 @@
+
+ ccflags-y := -Iinclude/drm
+ ttm-y := ttm_agp_backend.o ttm_memory.o ttm_tt.o ttm_bo.o \
+- ttm_bo_util.o ttm_bo_vm.o ttm_module.o ttm_global.o
++ ttm_bo_util.o ttm_bo_vm.o ttm_module.o ttm_global.o \
++ ttm_object.o ttm_lock.o ttm_execbuf_util.o
+
+ obj-$(CONFIG_DRM_TTM) += ttm.o
+diff --git a/drivers/gpu/drm/ttm/ttm_bo.c b/drivers/gpu/drm/ttm/ttm_bo.c
+index 87c0625..c7320ce 100644
+--- a/drivers/gpu/drm/ttm/ttm_bo.c
++++ b/drivers/gpu/drm/ttm/ttm_bo.c
+@@ -27,6 +27,14 @@
+ /*
+ * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
+ */
++/* Notes:
++ *
++ * We store bo pointer in drm_mm_node struct so we know which bo own a
++ * specific node. There is no protection on the pointer, thus to make
++ * sure things don't go berserk you have to access this pointer while
++ * holding the global lru lock and make sure anytime you free a node you
++ * reset the pointer to NULL.
++ */
+
+ #include "ttm/ttm_module.h"
+ #include "ttm/ttm_bo_driver.h"
+@@ -51,6 +59,59 @@ static struct attribute ttm_bo_count = {
+ .mode = S_IRUGO
+ };
+
++static inline int ttm_mem_type_from_flags(uint32_t flags, uint32_t *mem_type)
++{
++ int i;
++
++ for (i = 0; i <= TTM_PL_PRIV5; i++)
++ if (flags & (1 << i)) {
++ *mem_type = i;
++ return 0;
++ }
++ return -EINVAL;
++}
++
++static void ttm_mem_type_debug(struct ttm_bo_device *bdev, int mem_type)
++{
++ struct ttm_mem_type_manager *man = &bdev->man[mem_type];
++
++ printk(KERN_ERR TTM_PFX " has_type: %d\n", man->has_type);
++ printk(KERN_ERR TTM_PFX " use_type: %d\n", man->use_type);
++ printk(KERN_ERR TTM_PFX " flags: 0x%08X\n", man->flags);
++ printk(KERN_ERR TTM_PFX " gpu_offset: 0x%08lX\n", man->gpu_offset);
++ printk(KERN_ERR TTM_PFX " io_offset: 0x%08lX\n", man->io_offset);
++ printk(KERN_ERR TTM_PFX " io_size: %ld\n", man->io_size);
++ printk(KERN_ERR TTM_PFX " size: %llu\n", man->size);
++ printk(KERN_ERR TTM_PFX " available_caching: 0x%08X\n",
++ man->available_caching);
++ printk(KERN_ERR TTM_PFX " default_caching: 0x%08X\n",
++ man->default_caching);
++ if (mem_type != TTM_PL_SYSTEM) {
++ spin_lock(&bdev->glob->lru_lock);
++ drm_mm_debug_table(&man->manager, TTM_PFX);
++ spin_unlock(&bdev->glob->lru_lock);
++ }
++}
++
++static void ttm_bo_mem_space_debug(struct ttm_buffer_object *bo,
++ struct ttm_placement *placement)
++{
++ int i, ret, mem_type;
++
++ printk(KERN_ERR TTM_PFX "No space for %p (%lu pages, %luK, %luM)\n",
++ bo, bo->mem.num_pages, bo->mem.size >> 10,
++ bo->mem.size >> 20);
++ for (i = 0; i < placement->num_placement; i++) {
++ ret = ttm_mem_type_from_flags(placement->placement[i],
++ &mem_type);
++ if (ret)
++ return;
++ printk(KERN_ERR TTM_PFX " placement[%d]=0x%08X (%d)\n",
++ i, placement->placement[i], mem_type);
++ ttm_mem_type_debug(bo->bdev, mem_type);
++ }
++}
++
+ static ssize_t ttm_bo_global_show(struct kobject *kobj,
+ struct attribute *attr,
+ char *buffer)
+@@ -117,12 +178,13 @@ int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo, bool interruptible)
+ ret = wait_event_interruptible(bo->event_queue,
+ atomic_read(&bo->reserved) == 0);
+ if (unlikely(ret != 0))
+- return -ERESTART;
++ return ret;
+ } else {
+ wait_event(bo->event_queue, atomic_read(&bo->reserved) == 0);
+ }
+ return 0;
+ }
++EXPORT_SYMBOL(ttm_bo_wait_unreserved);
+
+ static void ttm_bo_add_to_lru(struct ttm_buffer_object *bo)
+ {
+@@ -247,7 +309,6 @@ EXPORT_SYMBOL(ttm_bo_unreserve);
+ /*
+ * Call bo->mutex locked.
+ */
+-
+ static int ttm_bo_add_ttm(struct ttm_buffer_object *bo, bool zero_alloc)
+ {
+ struct ttm_bo_device *bdev = bo->bdev;
+@@ -275,9 +336,10 @@ static int ttm_bo_add_ttm(struct ttm_buffer_object *bo, bool zero_alloc)
+ bo->ttm = ttm_tt_create(bdev, bo->num_pages << PAGE_SHIFT,
+ page_flags | TTM_PAGE_FLAG_USER,
+ glob->dummy_read_page);
+- if (unlikely(bo->ttm == NULL))
++ if (unlikely(bo->ttm == NULL)) {
+ ret = -ENOMEM;
+- break;
++ break;
++ }
+
+ ret = ttm_tt_set_user(bo->ttm, current,
+ bo->buffer_start, bo->num_pages);
+@@ -328,14 +390,8 @@ static int ttm_bo_handle_move_mem(struct ttm_buffer_object *bo,
+ }
+
+ if (bo->mem.mem_type == TTM_PL_SYSTEM) {
+-
+- struct ttm_mem_reg *old_mem = &bo->mem;
+- uint32_t save_flags = old_mem->placement;
+-
+- *old_mem = *mem;
++ bo->mem = *mem;
+ mem->mm_node = NULL;
+- ttm_flag_masked(&save_flags, mem->placement,
+- TTM_PL_MASK_MEMTYPE);
+ goto moved;
+ }
+
+@@ -370,7 +426,8 @@ moved:
+ bdev->man[bo->mem.mem_type].gpu_offset;
+ bo->cur_placement = bo->mem.placement;
+ spin_unlock(&bo->lock);
+- }
++ } else
++ bo->offset = 0;
+
+ return 0;
+
+@@ -408,6 +465,8 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, bool remove_all)
+ spin_unlock(&bo->lock);
+
+ spin_lock(&glob->lru_lock);
++ put_count = ttm_bo_del_from_lru(bo);
++
+ ret = ttm_bo_reserve_locked(bo, false, false, false, 0);
+ BUG_ON(ret);
+ if (bo->ttm)
+@@ -415,19 +474,19 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, bool remove_all)
+
+ if (!list_empty(&bo->ddestroy)) {
+ list_del_init(&bo->ddestroy);
+- kref_put(&bo->list_kref, ttm_bo_ref_bug);
++ ++put_count;
+ }
+ if (bo->mem.mm_node) {
++ bo->mem.mm_node->private = NULL;
+ drm_mm_put_block(bo->mem.mm_node);
+ bo->mem.mm_node = NULL;
+ }
+- put_count = ttm_bo_del_from_lru(bo);
+ spin_unlock(&glob->lru_lock);
+
+ atomic_set(&bo->reserved, 0);
+
+ while (put_count--)
+- kref_put(&bo->list_kref, ttm_bo_release_list);
++ kref_put(&bo->list_kref, ttm_bo_ref_bug);
+
+ return 0;
+ }
+@@ -465,52 +524,44 @@ static int ttm_bo_cleanup_refs(struct ttm_buffer_object *bo, bool remove_all)
+ static int ttm_bo_delayed_delete(struct ttm_bo_device *bdev, bool remove_all)
+ {
+ struct ttm_bo_global *glob = bdev->glob;
+- struct ttm_buffer_object *entry, *nentry;
+- struct list_head *list, *next;
+- int ret;
++ struct ttm_buffer_object *entry = NULL;
++ int ret = 0;
+
+ spin_lock(&glob->lru_lock);
+- list_for_each_safe(list, next, &bdev->ddestroy) {
+- entry = list_entry(list, struct ttm_buffer_object, ddestroy);
+- nentry = NULL;
++ if (list_empty(&bdev->ddestroy))
++ goto out_unlock;
+
+- /*
+- * Protect the next list entry from destruction while we
+- * unlock the lru_lock.
+- */
++ entry = list_first_entry(&bdev->ddestroy,
++ struct ttm_buffer_object, ddestroy);
++ kref_get(&entry->list_kref);
+
+- if (next != &bdev->ddestroy) {
+- nentry = list_entry(next, struct ttm_buffer_object,
+- ddestroy);
++ for (;;) {
++ struct ttm_buffer_object *nentry = NULL;
++
++ if (entry->ddestroy.next != &bdev->ddestroy) {
++ nentry = list_first_entry(&entry->ddestroy,
++ struct ttm_buffer_object, ddestroy);
+ kref_get(&nentry->list_kref);
+ }
+- kref_get(&entry->list_kref);
+
+ spin_unlock(&glob->lru_lock);
+ ret = ttm_bo_cleanup_refs(entry, remove_all);
+ kref_put(&entry->list_kref, ttm_bo_release_list);
++ entry = nentry;
++
++ if (ret || !entry)
++ goto out;
+
+ spin_lock(&glob->lru_lock);
+- if (nentry) {
+- bool next_onlist = !list_empty(next);
+- spin_unlock(&glob->lru_lock);
+- kref_put(&nentry->list_kref, ttm_bo_release_list);
+- spin_lock(&glob->lru_lock);
+- /*
+- * Someone might have raced us and removed the
+- * next entry from the list. We don't bother restarting
+- * list traversal.
+- */
+-
+- if (!next_onlist)
+- break;
+- }
+- if (ret)
++ if (list_empty(&entry->ddestroy))
+ break;
+ }
+- ret = !list_empty(&bdev->ddestroy);
+- spin_unlock(&glob->lru_lock);
+
++out_unlock:
++ spin_unlock(&glob->lru_lock);
++out:
++ if (entry)
++ kref_put(&entry->list_kref, ttm_bo_release_list);
+ return ret;
+ }
+
+@@ -554,24 +605,21 @@ void ttm_bo_unref(struct ttm_buffer_object **p_bo)
+ }
+ EXPORT_SYMBOL(ttm_bo_unref);
+
+-static int ttm_bo_evict(struct ttm_buffer_object *bo, unsigned mem_type,
+- bool interruptible, bool no_wait)
++static int ttm_bo_evict(struct ttm_buffer_object *bo, bool interruptible,
++ bool no_wait)
+ {
+- int ret = 0;
+ struct ttm_bo_device *bdev = bo->bdev;
+ struct ttm_bo_global *glob = bo->glob;
+ struct ttm_mem_reg evict_mem;
+- uint32_t proposed_placement;
+-
+- if (bo->mem.mem_type != mem_type)
+- goto out;
++ struct ttm_placement placement;
++ int ret = 0;
+
+ spin_lock(&bo->lock);
+ ret = ttm_bo_wait(bo, false, interruptible, no_wait);
+ spin_unlock(&bo->lock);
+
+ if (unlikely(ret != 0)) {
+- if (ret != -ERESTART) {
++ if (ret != -ERESTARTSYS) {
+ printk(KERN_ERR TTM_PFX
+ "Failed to expire sync object before "
+ "buffer eviction.\n");
+@@ -584,116 +632,165 @@ static int ttm_bo_evict(struct ttm_buffer_object *bo, unsigned mem_type,
+ evict_mem = bo->mem;
+ evict_mem.mm_node = NULL;
+
+- proposed_placement = bdev->driver->evict_flags(bo);
+-
+- ret = ttm_bo_mem_space(bo, proposed_placement,
+- &evict_mem, interruptible, no_wait);
+- if (unlikely(ret != 0 && ret != -ERESTART))
+- ret = ttm_bo_mem_space(bo, TTM_PL_FLAG_SYSTEM,
+- &evict_mem, interruptible, no_wait);
+-
++ placement.fpfn = 0;
++ placement.lpfn = 0;
++ placement.num_placement = 0;
++ placement.num_busy_placement = 0;
++ bdev->driver->evict_flags(bo, &placement);
++ ret = ttm_bo_mem_space(bo, &placement, &evict_mem, interruptible,
++ no_wait);
+ if (ret) {
+- if (ret != -ERESTART)
++ if (ret != -ERESTARTSYS) {
+ printk(KERN_ERR TTM_PFX
+ "Failed to find memory space for "
+ "buffer 0x%p eviction.\n", bo);
++ ttm_bo_mem_space_debug(bo, &placement);
++ }
+ goto out;
+ }
+
+ ret = ttm_bo_handle_move_mem(bo, &evict_mem, true, interruptible,
+ no_wait);
+ if (ret) {
+- if (ret != -ERESTART)
++ if (ret != -ERESTARTSYS)
+ printk(KERN_ERR TTM_PFX "Buffer eviction failed\n");
++ spin_lock(&glob->lru_lock);
++ if (evict_mem.mm_node) {
++ evict_mem.mm_node->private = NULL;
++ drm_mm_put_block(evict_mem.mm_node);
++ evict_mem.mm_node = NULL;
++ }
++ spin_unlock(&glob->lru_lock);
+ goto out;
+ }
+-
+- spin_lock(&glob->lru_lock);
+- if (evict_mem.mm_node) {
+- drm_mm_put_block(evict_mem.mm_node);
+- evict_mem.mm_node = NULL;
+- }
+- spin_unlock(&glob->lru_lock);
+ bo->evicted = true;
+ out:
+ return ret;
+ }
+
+-/**
+- * Repeatedly evict memory from the LRU for @mem_type until we create enough
+- * space, or we've evicted everything and there isn't enough space.
+- */
+-static int ttm_bo_mem_force_space(struct ttm_bo_device *bdev,
+- struct ttm_mem_reg *mem,
+- uint32_t mem_type,
+- bool interruptible, bool no_wait)
++static int ttm_mem_evict_first(struct ttm_bo_device *bdev,
++ uint32_t mem_type,
++ bool interruptible, bool no_wait)
+ {
+ struct ttm_bo_global *glob = bdev->glob;
+- struct drm_mm_node *node;
+- struct ttm_buffer_object *entry;
+ struct ttm_mem_type_manager *man = &bdev->man[mem_type];
+- struct list_head *lru;
+- unsigned long num_pages = mem->num_pages;
+- int put_count = 0;
+- int ret;
+-
+-retry_pre_get:
+- ret = drm_mm_pre_get(&man->manager);
+- if (unlikely(ret != 0))
+- return ret;
++ struct ttm_buffer_object *bo;
++ int ret, put_count = 0;
+
++retry:
+ spin_lock(&glob->lru_lock);
+- do {
+- node = drm_mm_search_free(&man->manager, num_pages,
+- mem->page_alignment, 1);
+- if (node)
+- break;
++ if (list_empty(&man->lru)) {
++ spin_unlock(&glob->lru_lock);
++ return -EBUSY;
++ }
+
+- lru = &man->lru;
+- if (list_empty(lru))
+- break;
++ bo = list_first_entry(&man->lru, struct ttm_buffer_object, lru);
++ kref_get(&bo->list_kref);
+
+- entry = list_first_entry(lru, struct ttm_buffer_object, lru);
+- kref_get(&entry->list_kref);
++ ret = ttm_bo_reserve_locked(bo, false, true, false, 0);
+
+- ret =
+- ttm_bo_reserve_locked(entry, interruptible, no_wait,
+- false, 0);
++ if (unlikely(ret == -EBUSY)) {
++ spin_unlock(&glob->lru_lock);
++ if (likely(!no_wait))
++ ret = ttm_bo_wait_unreserved(bo, interruptible);
+
+- if (likely(ret == 0))
+- put_count = ttm_bo_del_from_lru(entry);
++ kref_put(&bo->list_kref, ttm_bo_release_list);
+
+- spin_unlock(&glob->lru_lock);
++ /**
++ * We *need* to retry after releasing the lru lock.
++ */
+
+ if (unlikely(ret != 0))
+ return ret;
++ goto retry;
++ }
+
+- while (put_count--)
+- kref_put(&entry->list_kref, ttm_bo_ref_bug);
++ put_count = ttm_bo_del_from_lru(bo);
++ spin_unlock(&glob->lru_lock);
++
++ BUG_ON(ret != 0);
++
++ while (put_count--)
++ kref_put(&bo->list_kref, ttm_bo_ref_bug);
+
+- ret = ttm_bo_evict(entry, mem_type, interruptible, no_wait);
++ ret = ttm_bo_evict(bo, interruptible, no_wait);
++ ttm_bo_unreserve(bo);
+
+- ttm_bo_unreserve(entry);
++ kref_put(&bo->list_kref, ttm_bo_release_list);
++ return ret;
++}
+
+- kref_put(&entry->list_kref, ttm_bo_release_list);
+- if (ret)
++static int ttm_bo_man_get_node(struct ttm_buffer_object *bo,
++ struct ttm_mem_type_manager *man,
++ struct ttm_placement *placement,
++ struct ttm_mem_reg *mem,
++ struct drm_mm_node **node)
++{
++ struct ttm_bo_global *glob = bo->glob;
++ unsigned long lpfn;
++ int ret;
++
++ lpfn = placement->lpfn;
++ if (!lpfn)
++ lpfn = man->size;
++ *node = NULL;
++ do {
++ ret = drm_mm_pre_get(&man->manager);
++ if (unlikely(ret))
+ return ret;
+
+ spin_lock(&glob->lru_lock);
+- } while (1);
+-
+- if (!node) {
++ *node = drm_mm_search_free_in_range(&man->manager,
++ mem->num_pages, mem->page_alignment,
++ placement->fpfn, lpfn, 1);
++ if (unlikely(*node == NULL)) {
++ spin_unlock(&glob->lru_lock);
++ return 0;
++ }
++ *node = drm_mm_get_block_atomic_range(*node, mem->num_pages,
++ mem->page_alignment,
++ placement->fpfn,
++ lpfn);
+ spin_unlock(&glob->lru_lock);
+- return -ENOMEM;
+- }
++ } while (*node == NULL);
++ return 0;
++}
+
+- node = drm_mm_get_block_atomic(node, num_pages, mem->page_alignment);
+- if (unlikely(!node)) {
+- spin_unlock(&glob->lru_lock);
+- goto retry_pre_get;
+- }
++/**
++ * Repeatedly evict memory from the LRU for @mem_type until we create enough
++ * space, or we've evicted everything and there isn't enough space.
++ */
++static int ttm_bo_mem_force_space(struct ttm_buffer_object *bo,
++ uint32_t mem_type,
++ struct ttm_placement *placement,
++ struct ttm_mem_reg *mem,
++ bool interruptible, bool no_wait)
++{
++ struct ttm_bo_device *bdev = bo->bdev;
++ struct ttm_bo_global *glob = bdev->glob;
++ struct ttm_mem_type_manager *man = &bdev->man[mem_type];
++ struct drm_mm_node *node;
++ int ret;
+
+- spin_unlock(&glob->lru_lock);
++ do {
++ ret = ttm_bo_man_get_node(bo, man, placement, mem, &node);
++ if (unlikely(ret != 0))
++ return ret;
++ if (node)
++ break;
++ spin_lock(&glob->lru_lock);
++ if (list_empty(&man->lru)) {
++ spin_unlock(&glob->lru_lock);
++ break;
++ }
++ spin_unlock(&glob->lru_lock);
++ ret = ttm_mem_evict_first(bdev, mem_type, interruptible,
++ no_wait);
++ if (unlikely(ret != 0))
++ return ret;
++ } while (1);
++ if (node == NULL)
++ return -ENOMEM;
+ mem->mm_node = node;
+ mem->mem_type = mem_type;
+ return 0;
+@@ -724,7 +821,6 @@ static uint32_t ttm_bo_select_caching(struct ttm_mem_type_manager *man,
+ return result;
+ }
+
+-
+ static bool ttm_bo_mt_compatible(struct ttm_mem_type_manager *man,
+ bool disallow_fixed,
+ uint32_t mem_type,
+@@ -757,66 +853,55 @@ static bool ttm_bo_mt_compatible(struct ttm_mem_type_manager *man,
+ * space.
+ */
+ int ttm_bo_mem_space(struct ttm_buffer_object *bo,
+- uint32_t proposed_placement,
+- struct ttm_mem_reg *mem,
+- bool interruptible, bool no_wait)
++ struct ttm_placement *placement,
++ struct ttm_mem_reg *mem,
++ bool interruptible, bool no_wait)
+ {
+ struct ttm_bo_device *bdev = bo->bdev;
+- struct ttm_bo_global *glob = bo->glob;
+ struct ttm_mem_type_manager *man;
+-
+- uint32_t num_prios = bdev->driver->num_mem_type_prio;
+- const uint32_t *prios = bdev->driver->mem_type_prio;
+- uint32_t i;
+ uint32_t mem_type = TTM_PL_SYSTEM;
+ uint32_t cur_flags = 0;
+ bool type_found = false;
+ bool type_ok = false;
+- bool has_eagain = false;
++ bool has_erestartsys = false;
+ struct drm_mm_node *node = NULL;
+- int ret;
++ int i, ret;
+
+ mem->mm_node = NULL;
+- for (i = 0; i < num_prios; ++i) {
+- mem_type = prios[i];
++ for (i = 0; i < placement->num_placement; ++i) {
++ ret = ttm_mem_type_from_flags(placement->placement[i],
++ &mem_type);
++ if (ret)
++ return ret;
+ man = &bdev->man[mem_type];
+
+ type_ok = ttm_bo_mt_compatible(man,
+- bo->type == ttm_bo_type_user,
+- mem_type, proposed_placement,
+- &cur_flags);
++ bo->type == ttm_bo_type_user,
++ mem_type,
++ placement->placement[i],
++ &cur_flags);
+
+ if (!type_ok)
+ continue;
+
+ cur_flags = ttm_bo_select_caching(man, bo->mem.placement,
+ cur_flags);
++ /*
++ * Use the access and other non-mapping-related flag bits from
++ * the memory placement flags to the current flags
++ */
++ ttm_flag_masked(&cur_flags, placement->placement[i],
++ ~TTM_PL_MASK_MEMTYPE);
+
+ if (mem_type == TTM_PL_SYSTEM)
+ break;
+
+ if (man->has_type && man->use_type) {
+ type_found = true;
+- do {
+- ret = drm_mm_pre_get(&man->manager);
+- if (unlikely(ret))
+- return ret;
+-
+- spin_lock(&glob->lru_lock);
+- node = drm_mm_search_free(&man->manager,
+- mem->num_pages,
+- mem->page_alignment,
+- 1);
+- if (unlikely(!node)) {
+- spin_unlock(&glob->lru_lock);
+- break;
+- }
+- node = drm_mm_get_block_atomic(node,
+- mem->num_pages,
+- mem->
+- page_alignment);
+- spin_unlock(&glob->lru_lock);
+- } while (!node);
++ ret = ttm_bo_man_get_node(bo, man, placement, mem,
++ &node);
++ if (unlikely(ret))
++ return ret;
+ }
+ if (node)
+ break;
+@@ -826,67 +911,74 @@ int ttm_bo_mem_space(struct ttm_buffer_object *bo,
+ mem->mm_node = node;
+ mem->mem_type = mem_type;
+ mem->placement = cur_flags;
++ if (node)
++ node->private = bo;
+ return 0;
+ }
+
+ if (!type_found)
+ return -EINVAL;
+
+- num_prios = bdev->driver->num_mem_busy_prio;
+- prios = bdev->driver->mem_busy_prio;
+-
+- for (i = 0; i < num_prios; ++i) {
+- mem_type = prios[i];
++ for (i = 0; i < placement->num_busy_placement; ++i) {
++ ret = ttm_mem_type_from_flags(placement->busy_placement[i],
++ &mem_type);
++ if (ret)
++ return ret;
+ man = &bdev->man[mem_type];
+-
+ if (!man->has_type)
+ continue;
+-
+ if (!ttm_bo_mt_compatible(man,
+- bo->type == ttm_bo_type_user,
+- mem_type,
+- proposed_placement, &cur_flags))
++ bo->type == ttm_bo_type_user,
++ mem_type,
++ placement->busy_placement[i],
++ &cur_flags))
+ continue;
+
+ cur_flags = ttm_bo_select_caching(man, bo->mem.placement,
+ cur_flags);
++ /*
++ * Use the access and other non-mapping-related flag bits from
++ * the memory placement flags to the current flags
++ */
++ ttm_flag_masked(&cur_flags, placement->busy_placement[i],
++ ~TTM_PL_MASK_MEMTYPE);
+
+- ret = ttm_bo_mem_force_space(bdev, mem, mem_type,
+- interruptible, no_wait);
+
+- if (ret == 0 && mem->mm_node) {
++ if (mem_type == TTM_PL_SYSTEM) {
++ mem->mem_type = mem_type;
+ mem->placement = cur_flags;
++ mem->mm_node = NULL;
+ return 0;
+ }
+
+- if (ret == -ERESTART)
+- has_eagain = true;
++ ret = ttm_bo_mem_force_space(bo, mem_type, placement, mem,
++ interruptible, no_wait);
++ if (ret == 0 && mem->mm_node) {
++ mem->placement = cur_flags;
++ mem->mm_node->private = bo;
++ return 0;
++ }
++ if (ret == -ERESTARTSYS)
++ has_erestartsys = true;
+ }
+-
+- ret = (has_eagain) ? -ERESTART : -ENOMEM;
++ ret = (has_erestartsys) ? -ERESTARTSYS : -ENOMEM;
+ return ret;
+ }
+ EXPORT_SYMBOL(ttm_bo_mem_space);
+
+ int ttm_bo_wait_cpu(struct ttm_buffer_object *bo, bool no_wait)
+ {
+- int ret = 0;
+-
+ if ((atomic_read(&bo->cpu_writers) > 0) && no_wait)
+ return -EBUSY;
+
+- ret = wait_event_interruptible(bo->event_queue,
+- atomic_read(&bo->cpu_writers) == 0);
+-
+- if (ret == -ERESTARTSYS)
+- ret = -ERESTART;
+-
+- return ret;
++ return wait_event_interruptible(bo->event_queue,
++ atomic_read(&bo->cpu_writers) == 0);
+ }
++EXPORT_SYMBOL(ttm_bo_wait_cpu);
+
+ int ttm_bo_move_buffer(struct ttm_buffer_object *bo,
+- uint32_t proposed_placement,
+- bool interruptible, bool no_wait)
++ struct ttm_placement *placement,
++ bool interruptible, bool no_wait)
+ {
+ struct ttm_bo_global *glob = bo->glob;
+ int ret = 0;
+@@ -899,147 +991,138 @@ int ttm_bo_move_buffer(struct ttm_buffer_object *bo,
+ * Have the driver move function wait for idle when necessary,
+ * instead of doing it here.
+ */
+-
+ spin_lock(&bo->lock);
+ ret = ttm_bo_wait(bo, false, interruptible, no_wait);
+ spin_unlock(&bo->lock);
+-
+ if (ret)
+ return ret;
+-
+ mem.num_pages = bo->num_pages;
+ mem.size = mem.num_pages << PAGE_SHIFT;
+ mem.page_alignment = bo->mem.page_alignment;
+-
+ /*
+ * Determine where to move the buffer.
+ */
+-
+- ret = ttm_bo_mem_space(bo, proposed_placement, &mem,
+- interruptible, no_wait);
++ ret = ttm_bo_mem_space(bo, placement, &mem, interruptible, no_wait);
+ if (ret)
+ goto out_unlock;
+-
+ ret = ttm_bo_handle_move_mem(bo, &mem, false, interruptible, no_wait);
+-
+ out_unlock:
+ if (ret && mem.mm_node) {
+ spin_lock(&glob->lru_lock);
++ mem.mm_node->private = NULL;
+ drm_mm_put_block(mem.mm_node);
+ spin_unlock(&glob->lru_lock);
+ }
+ return ret;
+ }
+
+-static int ttm_bo_mem_compat(uint32_t proposed_placement,
++static int ttm_bo_mem_compat(struct ttm_placement *placement,
+ struct ttm_mem_reg *mem)
+ {
+- if ((proposed_placement & mem->placement & TTM_PL_MASK_MEM) == 0)
+- return 0;
+- if ((proposed_placement & mem->placement & TTM_PL_MASK_CACHING) == 0)
+- return 0;
+-
+- return 1;
++ int i;
++ struct drm_mm_node *node = mem->mm_node;
++
++ if (node && placement->lpfn != 0 &&
++ (node->start < placement->fpfn ||
++ node->start + node->size > placement->lpfn))
++ return -1;
++
++ for (i = 0; i < placement->num_placement; i++) {
++ if ((placement->placement[i] & mem->placement &
++ TTM_PL_MASK_CACHING) &&
++ (placement->placement[i] & mem->placement &
++ TTM_PL_MASK_MEM))
++ return i;
++ }
++ return -1;
+ }
+
+-int ttm_buffer_object_validate(struct ttm_buffer_object *bo,
+- uint32_t proposed_placement,
+- bool interruptible, bool no_wait)
++int ttm_bo_validate(struct ttm_buffer_object *bo,
++ struct ttm_placement *placement,
++ bool interruptible, bool no_wait)
+ {
+ int ret;
+
+ BUG_ON(!atomic_read(&bo->reserved));
+- bo->proposed_placement = proposed_placement;
+-
+- TTM_DEBUG("Proposed placement 0x%08lx, Old flags 0x%08lx\n",
+- (unsigned long)proposed_placement,
+- (unsigned long)bo->mem.placement);
+-
++ /* Check that range is valid */
++ if (placement->lpfn || placement->fpfn)
++ if (placement->fpfn > placement->lpfn ||
++ (placement->lpfn - placement->fpfn) < bo->num_pages)
++ return -EINVAL;
+ /*
+ * Check whether we need to move buffer.
+ */
+-
+- if (!ttm_bo_mem_compat(bo->proposed_placement, &bo->mem)) {
+- ret = ttm_bo_move_buffer(bo, bo->proposed_placement,
+- interruptible, no_wait);
+- if (ret) {
+- if (ret != -ERESTART)
+- printk(KERN_ERR TTM_PFX
+- "Failed moving buffer. "
+- "Proposed placement 0x%08x\n",
+- bo->proposed_placement);
+- if (ret == -ENOMEM)
+- printk(KERN_ERR TTM_PFX
+- "Out of aperture space or "
+- "DRM memory quota.\n");
++ ret = ttm_bo_mem_compat(placement, &bo->mem);
++ if (ret < 0) {
++ ret = ttm_bo_move_buffer(bo, placement, interruptible, no_wait);
++ if (ret)
+ return ret;
+- }
++ } else {
++ /*
++ * Use the access and other non-mapping-related flag bits from
++ * the compatible memory placement flags to the active flags
++ */
++ ttm_flag_masked(&bo->mem.placement, placement->placement[ret],
++ ~TTM_PL_MASK_MEMTYPE);
+ }
+-
+ /*
+ * We might need to add a TTM.
+ */
+-
+ if (bo->mem.mem_type == TTM_PL_SYSTEM && bo->ttm == NULL) {
+ ret = ttm_bo_add_ttm(bo, true);
+ if (ret)
+ return ret;
+ }
+- /*
+- * Validation has succeeded, move the access and other
+- * non-mapping-related flag bits from the proposed flags to
+- * the active flags
+- */
+-
+- ttm_flag_masked(&bo->mem.placement, bo->proposed_placement,
+- ~TTM_PL_MASK_MEMTYPE);
+-
+ return 0;
+ }
+-EXPORT_SYMBOL(ttm_buffer_object_validate);
++EXPORT_SYMBOL(ttm_bo_validate);
+
+-int
+-ttm_bo_check_placement(struct ttm_buffer_object *bo,
+- uint32_t set_flags, uint32_t clr_flags)
++int ttm_bo_check_placement(struct ttm_buffer_object *bo,
++ struct ttm_placement *placement)
+ {
+- uint32_t new_mask = set_flags | clr_flags;
+-
+- if ((bo->type == ttm_bo_type_user) &&
+- (clr_flags & TTM_PL_FLAG_CACHED)) {
+- printk(KERN_ERR TTM_PFX
+- "User buffers require cache-coherent memory.\n");
+- return -EINVAL;
+- }
+-
+- if (!capable(CAP_SYS_ADMIN)) {
+- if (new_mask & TTM_PL_FLAG_NO_EVICT) {
+- printk(KERN_ERR TTM_PFX "Need to be root to modify"
+- " NO_EVICT status.\n");
++ int i;
++
++ if (placement->fpfn || placement->lpfn) {
++ if (bo->mem.num_pages > (placement->lpfn - placement->fpfn)) {
++ printk(KERN_ERR TTM_PFX "Page number range to small "
++ "Need %lu pages, range is [%u, %u]\n",
++ bo->mem.num_pages, placement->fpfn,
++ placement->lpfn);
+ return -EINVAL;
+ }
+-
+- if ((clr_flags & bo->mem.placement & TTM_PL_MASK_MEMTYPE) &&
+- (bo->mem.placement & TTM_PL_FLAG_NO_EVICT)) {
+- printk(KERN_ERR TTM_PFX
+- "Incompatible memory specification"
+- " for NO_EVICT buffer.\n");
+- return -EINVAL;
++ }
++ for (i = 0; i < placement->num_placement; i++) {
++ if (!capable(CAP_SYS_ADMIN)) {
++ if (placement->placement[i] & TTM_PL_FLAG_NO_EVICT) {
++ printk(KERN_ERR TTM_PFX "Need to be root to "
++ "modify NO_EVICT status.\n");
++ return -EINVAL;
++ }
++ }
++ }
++ for (i = 0; i < placement->num_busy_placement; i++) {
++ if (!capable(CAP_SYS_ADMIN)) {
++ if (placement->busy_placement[i] & TTM_PL_FLAG_NO_EVICT) {
++ printk(KERN_ERR TTM_PFX "Need to be root to "
++ "modify NO_EVICT status.\n");
++ return -EINVAL;
++ }
+ }
+ }
+ return 0;
+ }
+
+-int ttm_buffer_object_init(struct ttm_bo_device *bdev,
+- struct ttm_buffer_object *bo,
+- unsigned long size,
+- enum ttm_bo_type type,
+- uint32_t flags,
+- uint32_t page_alignment,
+- unsigned long buffer_start,
+- bool interruptible,
+- struct file *persistant_swap_storage,
+- size_t acc_size,
+- void (*destroy) (struct ttm_buffer_object *))
++int ttm_bo_init(struct ttm_bo_device *bdev,
++ struct ttm_buffer_object *bo,
++ unsigned long size,
++ enum ttm_bo_type type,
++ struct ttm_placement *placement,
++ uint32_t page_alignment,
++ unsigned long buffer_start,
++ bool interruptible,
++ struct file *persistant_swap_storage,
++ size_t acc_size,
++ void (*destroy) (struct ttm_buffer_object *))
+ {
+ int ret = 0;
+ unsigned long num_pages;
+@@ -1065,6 +1148,7 @@ int ttm_buffer_object_init(struct ttm_bo_device *bdev,
+ bo->glob = bdev->glob;
+ bo->type = type;
+ bo->num_pages = num_pages;
++ bo->mem.size = num_pages << PAGE_SHIFT;
+ bo->mem.mem_type = TTM_PL_SYSTEM;
+ bo->mem.num_pages = bo->num_pages;
+ bo->mem.mm_node = NULL;
+@@ -1077,29 +1161,21 @@ int ttm_buffer_object_init(struct ttm_bo_device *bdev,
+ bo->acc_size = acc_size;
+ atomic_inc(&bo->glob->bo_count);
+
+- ret = ttm_bo_check_placement(bo, flags, 0ULL);
++ ret = ttm_bo_check_placement(bo, placement);
+ if (unlikely(ret != 0))
+ goto out_err;
+
+ /*
+- * If no caching attributes are set, accept any form of caching.
+- */
+-
+- if ((flags & TTM_PL_MASK_CACHING) == 0)
+- flags |= TTM_PL_MASK_CACHING;
+-
+- /*
+ * For ttm_bo_type_device buffers, allocate
+ * address space from the device.
+ */
+-
+ if (bo->type == ttm_bo_type_device) {
+ ret = ttm_bo_setup_vm(bo);
+ if (ret)
+ goto out_err;
+ }
+
+- ret = ttm_buffer_object_validate(bo, flags, interruptible, false);
++ ret = ttm_bo_validate(bo, placement, interruptible, false);
+ if (ret)
+ goto out_err;
+
+@@ -1112,7 +1188,7 @@ out_err:
+
+ return ret;
+ }
+-EXPORT_SYMBOL(ttm_buffer_object_init);
++EXPORT_SYMBOL(ttm_bo_init);
+
+ static inline size_t ttm_bo_size(struct ttm_bo_global *glob,
+ unsigned long num_pages)
+@@ -1123,19 +1199,19 @@ static inline size_t ttm_bo_size(struct ttm_bo_global *glob,
+ return glob->ttm_bo_size + 2 * page_array_size;
+ }
+
+-int ttm_buffer_object_create(struct ttm_bo_device *bdev,
+- unsigned long size,
+- enum ttm_bo_type type,
+- uint32_t flags,
+- uint32_t page_alignment,
+- unsigned long buffer_start,
+- bool interruptible,
+- struct file *persistant_swap_storage,
+- struct ttm_buffer_object **p_bo)
++int ttm_bo_create(struct ttm_bo_device *bdev,
++ unsigned long size,
++ enum ttm_bo_type type,
++ struct ttm_placement *placement,
++ uint32_t page_alignment,
++ unsigned long buffer_start,
++ bool interruptible,
++ struct file *persistant_swap_storage,
++ struct ttm_buffer_object **p_bo)
+ {
+ struct ttm_buffer_object *bo;
+- int ret;
+ struct ttm_mem_global *mem_glob = bdev->glob->mem_glob;
++ int ret;
+
+ size_t acc_size =
+ ttm_bo_size(bdev->glob, (size + PAGE_SIZE - 1) >> PAGE_SHIFT);
+@@ -1150,76 +1226,41 @@ int ttm_buffer_object_create(struct ttm_bo_device *bdev,
+ return -ENOMEM;
+ }
+
+- ret = ttm_buffer_object_init(bdev, bo, size, type, flags,
+- page_alignment, buffer_start,
+- interruptible,
+- persistant_swap_storage, acc_size, NULL);
++ ret = ttm_bo_init(bdev, bo, size, type, placement, page_alignment,
++ buffer_start, interruptible,
++ persistant_swap_storage, acc_size, NULL);
+ if (likely(ret == 0))
+ *p_bo = bo;
+
+ return ret;
+ }
+
+-static int ttm_bo_leave_list(struct ttm_buffer_object *bo,
+- uint32_t mem_type, bool allow_errors)
+-{
+- int ret;
+-
+- spin_lock(&bo->lock);
+- ret = ttm_bo_wait(bo, false, false, false);
+- spin_unlock(&bo->lock);
+-
+- if (ret && allow_errors)
+- goto out;
+-
+- if (bo->mem.mem_type == mem_type)
+- ret = ttm_bo_evict(bo, mem_type, false, false);
+-
+- if (ret) {
+- if (allow_errors) {
+- goto out;
+- } else {
+- ret = 0;
+- printk(KERN_ERR TTM_PFX "Cleanup eviction failed\n");
+- }
+- }
+-
+-out:
+- return ret;
+-}
+-
+ static int ttm_bo_force_list_clean(struct ttm_bo_device *bdev,
+- struct list_head *head,
+- unsigned mem_type, bool allow_errors)
++ unsigned mem_type, bool allow_errors)
+ {
++ struct ttm_mem_type_manager *man = &bdev->man[mem_type];
+ struct ttm_bo_global *glob = bdev->glob;
+- struct ttm_buffer_object *entry;
+ int ret;
+- int put_count;
+
+ /*
+ * Can't use standard list traversal since we're unlocking.
+ */
+
+ spin_lock(&glob->lru_lock);
+-
+- while (!list_empty(head)) {
+- entry = list_first_entry(head, struct ttm_buffer_object, lru);
+- kref_get(&entry->list_kref);
+- ret = ttm_bo_reserve_locked(entry, false, false, false, 0);
+- put_count = ttm_bo_del_from_lru(entry);
++ while (!list_empty(&man->lru)) {
+ spin_unlock(&glob->lru_lock);
+- while (put_count--)
+- kref_put(&entry->list_kref, ttm_bo_ref_bug);
+- BUG_ON(ret);
+- ret = ttm_bo_leave_list(entry, mem_type, allow_errors);
+- ttm_bo_unreserve(entry);
+- kref_put(&entry->list_kref, ttm_bo_release_list);
++ ret = ttm_mem_evict_first(bdev, mem_type, false, false);
++ if (ret) {
++ if (allow_errors) {
++ return ret;
++ } else {
++ printk(KERN_ERR TTM_PFX
++ "Cleanup eviction failed\n");
++ }
++ }
+ spin_lock(&glob->lru_lock);
+ }
+-
+ spin_unlock(&glob->lru_lock);
+-
+ return 0;
+ }
+
+@@ -1246,7 +1287,7 @@ int ttm_bo_clean_mm(struct ttm_bo_device *bdev, unsigned mem_type)
+
+ ret = 0;
+ if (mem_type > 0) {
+- ttm_bo_force_list_clean(bdev, &man->lru, mem_type, false);
++ ttm_bo_force_list_clean(bdev, mem_type, false);
+
+ spin_lock(&glob->lru_lock);
+ if (drm_mm_clean(&man->manager))
+@@ -1279,12 +1320,12 @@ int ttm_bo_evict_mm(struct ttm_bo_device *bdev, unsigned mem_type)
+ return 0;
+ }
+
+- return ttm_bo_force_list_clean(bdev, &man->lru, mem_type, true);
++ return ttm_bo_force_list_clean(bdev, mem_type, true);
+ }
+ EXPORT_SYMBOL(ttm_bo_evict_mm);
+
+ int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type,
+- unsigned long p_offset, unsigned long p_size)
++ unsigned long p_size)
+ {
+ int ret = -EINVAL;
+ struct ttm_mem_type_manager *man;
+@@ -1314,7 +1355,7 @@ int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type,
+ type);
+ return ret;
+ }
+- ret = drm_mm_init(&man->manager, p_offset, p_size);
++ ret = drm_mm_init(&man->manager, 0, p_size);
+ if (ret)
+ return ret;
+ }
+@@ -1463,7 +1504,7 @@ int ttm_bo_device_init(struct ttm_bo_device *bdev,
+ * Initialize the system memory buffer type.
+ * Other types need to be driver / IOCTL initialized.
+ */
+- ret = ttm_bo_init_mm(bdev, TTM_PL_SYSTEM, 0, 0);
++ ret = ttm_bo_init_mm(bdev, TTM_PL_SYSTEM, 0);
+ if (unlikely(ret != 0))
+ goto out_no_sys;
+
+@@ -1693,7 +1734,7 @@ int ttm_bo_block_reservation(struct ttm_buffer_object *bo, bool interruptible,
+ ret = wait_event_interruptible
+ (bo->event_queue, atomic_read(&bo->reserved) == 0);
+ if (unlikely(ret != 0))
+- return -ERESTART;
++ return ret;
+ } else {
+ wait_event(bo->event_queue,
+ atomic_read(&bo->reserved) == 0);
+@@ -1722,12 +1763,14 @@ int ttm_bo_synccpu_write_grab(struct ttm_buffer_object *bo, bool no_wait)
+ ttm_bo_unreserve(bo);
+ return ret;
+ }
++EXPORT_SYMBOL(ttm_bo_synccpu_write_grab);
+
+ void ttm_bo_synccpu_write_release(struct ttm_buffer_object *bo)
+ {
+ if (atomic_dec_and_test(&bo->cpu_writers))
+ wake_up_all(&bo->event_queue);
+ }
++EXPORT_SYMBOL(ttm_bo_synccpu_write_release);
+
+ /**
+ * A buffer object shrink method that tries to swap out the first
+@@ -1808,6 +1851,9 @@ static int ttm_bo_swapout(struct ttm_mem_shrink *shrink)
+ * anyone tries to access a ttm page.
+ */
+
++ if (bo->bdev->driver->swap_notify)
++ bo->bdev->driver->swap_notify(bo);
++
+ ret = ttm_tt_swapout(bo->ttm, bo->persistant_swap_storage);
+ out:
+
+@@ -1828,3 +1874,4 @@ void ttm_bo_swapout_all(struct ttm_bo_device *bdev)
+ while (ttm_bo_swapout(&bdev->glob->shrink) == 0)
+ ;
+ }
++EXPORT_SYMBOL(ttm_bo_swapout_all);
+diff --git a/drivers/gpu/drm/ttm/ttm_bo_util.c b/drivers/gpu/drm/ttm/ttm_bo_util.c
+index c70927e..5ca37a5 100644
+--- a/drivers/gpu/drm/ttm/ttm_bo_util.c
++++ b/drivers/gpu/drm/ttm/ttm_bo_util.c
+@@ -53,7 +53,6 @@ int ttm_bo_move_ttm(struct ttm_buffer_object *bo,
+ {
+ struct ttm_tt *ttm = bo->ttm;
+ struct ttm_mem_reg *old_mem = &bo->mem;
+- uint32_t save_flags = old_mem->placement;
+ int ret;
+
+ if (old_mem->mem_type != TTM_PL_SYSTEM) {
+@@ -62,7 +61,6 @@ int ttm_bo_move_ttm(struct ttm_buffer_object *bo,
+ ttm_flag_masked(&old_mem->placement, TTM_PL_FLAG_SYSTEM,
+ TTM_PL_MASK_MEM);
+ old_mem->mem_type = TTM_PL_SYSTEM;
+- save_flags = old_mem->placement;
+ }
+
+ ret = ttm_tt_set_placement_caching(ttm, new_mem->placement);
+@@ -77,7 +75,7 @@ int ttm_bo_move_ttm(struct ttm_buffer_object *bo,
+
+ *old_mem = *new_mem;
+ new_mem->mm_node = NULL;
+- ttm_flag_masked(&save_flags, new_mem->placement, TTM_PL_MASK_MEMTYPE);
++
+ return 0;
+ }
+ EXPORT_SYMBOL(ttm_bo_move_ttm);
+@@ -219,7 +217,6 @@ int ttm_bo_move_memcpy(struct ttm_buffer_object *bo,
+ void *old_iomap;
+ void *new_iomap;
+ int ret;
+- uint32_t save_flags = old_mem->placement;
+ unsigned long i;
+ unsigned long page;
+ unsigned long add = 0;
+@@ -270,7 +267,6 @@ out2:
+
+ *old_mem = *new_mem;
+ new_mem->mm_node = NULL;
+- ttm_flag_masked(&save_flags, new_mem->placement, TTM_PL_MASK_MEMTYPE);
+
+ if ((man->flags & TTM_MEMTYPE_FLAG_FIXED) && (ttm != NULL)) {
+ ttm_tt_unbind(ttm);
+@@ -369,6 +365,7 @@ pgprot_t ttm_io_prot(uint32_t caching_flags, pgprot_t tmp)
+ #endif
+ return tmp;
+ }
++EXPORT_SYMBOL(ttm_io_prot);
+
+ static int ttm_bo_ioremap(struct ttm_buffer_object *bo,
+ unsigned long bus_base,
+@@ -427,7 +424,7 @@ static int ttm_bo_kmap_ttm(struct ttm_buffer_object *bo,
+
+ /*
+ * We need to use vmap to get the desired page protection
+- * or to make the buffer object look contigous.
++ * or to make the buffer object look contiguous.
+ */
+ prot = (mem->placement & TTM_PL_FLAG_CACHED) ?
+ PAGE_KERNEL :
+@@ -536,7 +533,6 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo,
+ struct ttm_mem_type_manager *man = &bdev->man[new_mem->mem_type];
+ struct ttm_mem_reg *old_mem = &bo->mem;
+ int ret;
+- uint32_t save_flags = old_mem->placement;
+ struct ttm_buffer_object *ghost_obj;
+ void *tmp_obj = NULL;
+
+@@ -597,7 +593,7 @@ int ttm_bo_move_accel_cleanup(struct ttm_buffer_object *bo,
+
+ *old_mem = *new_mem;
+ new_mem->mm_node = NULL;
+- ttm_flag_masked(&save_flags, new_mem->placement, TTM_PL_MASK_MEMTYPE);
++
+ return 0;
+ }
+ EXPORT_SYMBOL(ttm_bo_move_accel_cleanup);
+diff --git a/drivers/gpu/drm/ttm/ttm_bo_vm.c b/drivers/gpu/drm/ttm/ttm_bo_vm.c
+index 1c040d0..668dbe8 100644
+--- a/drivers/gpu/drm/ttm/ttm_bo_vm.c
++++ b/drivers/gpu/drm/ttm/ttm_bo_vm.c
+@@ -114,7 +114,7 @@ static int ttm_bo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
+ ret = ttm_bo_wait(bo, false, true, false);
+ spin_unlock(&bo->lock);
+ if (unlikely(ret != 0)) {
+- retval = (ret != -ERESTART) ?
++ retval = (ret != -ERESTARTSYS) ?
+ VM_FAULT_SIGBUS : VM_FAULT_NOPAGE;
+ goto out_unlock;
+ }
+@@ -320,7 +320,7 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
+ return -EFAULT;
+
+ driver = bo->bdev->driver;
+- if (unlikely(driver->verify_access)) {
++ if (unlikely(!driver->verify_access)) {
+ ret = -EPERM;
+ goto out_unref;
+ }
+@@ -349,9 +349,6 @@ ssize_t ttm_bo_io(struct ttm_bo_device *bdev, struct file *filp,
+ switch (ret) {
+ case 0:
+ break;
+- case -ERESTART:
+- ret = -EINTR;
+- goto out_unref;
+ case -EBUSY:
+ ret = -EAGAIN;
+ goto out_unref;
+@@ -421,8 +418,6 @@ ssize_t ttm_bo_fbdev_io(struct ttm_buffer_object *bo, const char __user *wbuf,
+ switch (ret) {
+ case 0:
+ break;
+- case -ERESTART:
+- return -EINTR;
+ case -EBUSY:
+ return -EAGAIN;
+ default:
+diff --git a/drivers/gpu/drm/ttm/ttm_execbuf_util.c b/drivers/gpu/drm/ttm/ttm_execbuf_util.c
+new file mode 100644
+index 0000000..c285c29
+--- /dev/null
++++ b/drivers/gpu/drm/ttm/ttm_execbuf_util.c
+@@ -0,0 +1,117 @@
++/**************************************************************************
++ *
++ * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++#include "ttm/ttm_execbuf_util.h"
++#include "ttm/ttm_bo_driver.h"
++#include "ttm/ttm_placement.h"
++#include <linux/wait.h>
++#include <linux/sched.h>
++#include <linux/module.h>
++
++void ttm_eu_backoff_reservation(struct list_head *list)
++{
++ struct ttm_validate_buffer *entry;
++
++ list_for_each_entry(entry, list, head) {
++ struct ttm_buffer_object *bo = entry->bo;
++ if (!entry->reserved)
++ continue;
++
++ entry->reserved = false;
++ ttm_bo_unreserve(bo);
++ }
++}
++EXPORT_SYMBOL(ttm_eu_backoff_reservation);
++
++/*
++ * Reserve buffers for validation.
++ *
++ * If a buffer in the list is marked for CPU access, we back off and
++ * wait for that buffer to become free for GPU access.
++ *
++ * If a buffer is reserved for another validation, the validator with
++ * the highest validation sequence backs off and waits for that buffer
++ * to become unreserved. This prevents deadlocks when validating multiple
++ * buffers in different orders.
++ */
++
++int ttm_eu_reserve_buffers(struct list_head *list, uint32_t val_seq)
++{
++ struct ttm_validate_buffer *entry;
++ int ret;
++
++retry:
++ list_for_each_entry(entry, list, head) {
++ struct ttm_buffer_object *bo = entry->bo;
++
++ entry->reserved = false;
++ ret = ttm_bo_reserve(bo, true, false, true, val_seq);
++ if (ret != 0) {
++ ttm_eu_backoff_reservation(list);
++ if (ret == -EAGAIN) {
++ ret = ttm_bo_wait_unreserved(bo, true);
++ if (unlikely(ret != 0))
++ return ret;
++ goto retry;
++ } else
++ return ret;
++ }
++
++ entry->reserved = true;
++ if (unlikely(atomic_read(&bo->cpu_writers) > 0)) {
++ ttm_eu_backoff_reservation(list);
++ ret = ttm_bo_wait_cpu(bo, false);
++ if (ret)
++ return ret;
++ goto retry;
++ }
++ }
++ return 0;
++}
++EXPORT_SYMBOL(ttm_eu_reserve_buffers);
++
++void ttm_eu_fence_buffer_objects(struct list_head *list, void *sync_obj)
++{
++ struct ttm_validate_buffer *entry;
++
++ list_for_each_entry(entry, list, head) {
++ struct ttm_buffer_object *bo = entry->bo;
++ struct ttm_bo_driver *driver = bo->bdev->driver;
++ void *old_sync_obj;
++
++ spin_lock(&bo->lock);
++ old_sync_obj = bo->sync_obj;
++ bo->sync_obj = driver->sync_obj_ref(sync_obj);
++ bo->sync_obj_arg = entry->new_sync_obj_arg;
++ spin_unlock(&bo->lock);
++ ttm_bo_unreserve(bo);
++ entry->reserved = false;
++ if (old_sync_obj)
++ driver->sync_obj_unref(&old_sync_obj);
++ }
++}
++EXPORT_SYMBOL(ttm_eu_fence_buffer_objects);
+diff --git a/drivers/gpu/drm/ttm/ttm_lock.c b/drivers/gpu/drm/ttm/ttm_lock.c
+new file mode 100644
+index 0000000..3d172ef
+--- /dev/null
++++ b/drivers/gpu/drm/ttm/ttm_lock.c
+@@ -0,0 +1,313 @@
++/**************************************************************************
++ *
++ * Copyright (c) 2007-2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++/*
++ * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
++ */
++
++#include "ttm/ttm_lock.h"
++#include "ttm/ttm_module.h"
++#include <asm/atomic.h>
++#include <linux/errno.h>
++#include <linux/wait.h>
++#include <linux/sched.h>
++#include <linux/module.h>
++
++#define TTM_WRITE_LOCK_PENDING (1 << 0)
++#define TTM_VT_LOCK_PENDING (1 << 1)
++#define TTM_SUSPEND_LOCK_PENDING (1 << 2)
++#define TTM_VT_LOCK (1 << 3)
++#define TTM_SUSPEND_LOCK (1 << 4)
++
++void ttm_lock_init(struct ttm_lock *lock)
++{
++ spin_lock_init(&lock->lock);
++ init_waitqueue_head(&lock->queue);
++ lock->rw = 0;
++ lock->flags = 0;
++ lock->kill_takers = false;
++ lock->signal = SIGKILL;
++}
++EXPORT_SYMBOL(ttm_lock_init);
++
++void ttm_read_unlock(struct ttm_lock *lock)
++{
++ spin_lock(&lock->lock);
++ if (--lock->rw == 0)
++ wake_up_all(&lock->queue);
++ spin_unlock(&lock->lock);
++}
++EXPORT_SYMBOL(ttm_read_unlock);
++
++static bool __ttm_read_lock(struct ttm_lock *lock)
++{
++ bool locked = false;
++
++ spin_lock(&lock->lock);
++ if (unlikely(lock->kill_takers)) {
++ send_sig(lock->signal, current, 0);
++ spin_unlock(&lock->lock);
++ return false;
++ }
++ if (lock->rw >= 0 && lock->flags == 0) {
++ ++lock->rw;
++ locked = true;
++ }
++ spin_unlock(&lock->lock);
++ return locked;
++}
++
++int ttm_read_lock(struct ttm_lock *lock, bool interruptible)
++{
++ int ret = 0;
++
++ if (interruptible)
++ ret = wait_event_interruptible(lock->queue,
++ __ttm_read_lock(lock));
++ else
++ wait_event(lock->queue, __ttm_read_lock(lock));
++ return ret;
++}
++EXPORT_SYMBOL(ttm_read_lock);
++
++static bool __ttm_read_trylock(struct ttm_lock *lock, bool *locked)
++{
++ bool block = true;
++
++ *locked = false;
++
++ spin_lock(&lock->lock);
++ if (unlikely(lock->kill_takers)) {
++ send_sig(lock->signal, current, 0);
++ spin_unlock(&lock->lock);
++ return false;
++ }
++ if (lock->rw >= 0 && lock->flags == 0) {
++ ++lock->rw;
++ block = false;
++ *locked = true;
++ } else if (lock->flags == 0) {
++ block = false;
++ }
++ spin_unlock(&lock->lock);
++
++ return !block;
++}
++
++int ttm_read_trylock(struct ttm_lock *lock, bool interruptible)
++{
++ int ret = 0;
++ bool locked;
++
++ if (interruptible)
++ ret = wait_event_interruptible
++ (lock->queue, __ttm_read_trylock(lock, &locked));
++ else
++ wait_event(lock->queue, __ttm_read_trylock(lock, &locked));
++
++ if (unlikely(ret != 0)) {
++ BUG_ON(locked);
++ return ret;
++ }
++
++ return (locked) ? 0 : -EBUSY;
++}
++
++void ttm_write_unlock(struct ttm_lock *lock)
++{
++ spin_lock(&lock->lock);
++ lock->rw = 0;
++ wake_up_all(&lock->queue);
++ spin_unlock(&lock->lock);
++}
++EXPORT_SYMBOL(ttm_write_unlock);
++
++static bool __ttm_write_lock(struct ttm_lock *lock)
++{
++ bool locked = false;
++
++ spin_lock(&lock->lock);
++ if (unlikely(lock->kill_takers)) {
++ send_sig(lock->signal, current, 0);
++ spin_unlock(&lock->lock);
++ return false;
++ }
++ if (lock->rw == 0 && ((lock->flags & ~TTM_WRITE_LOCK_PENDING) == 0)) {
++ lock->rw = -1;
++ lock->flags &= ~TTM_WRITE_LOCK_PENDING;
++ locked = true;
++ } else {
++ lock->flags |= TTM_WRITE_LOCK_PENDING;
++ }
++ spin_unlock(&lock->lock);
++ return locked;
++}
++
++int ttm_write_lock(struct ttm_lock *lock, bool interruptible)
++{
++ int ret = 0;
++
++ if (interruptible) {
++ ret = wait_event_interruptible(lock->queue,
++ __ttm_write_lock(lock));
++ if (unlikely(ret != 0)) {
++ spin_lock(&lock->lock);
++ lock->flags &= ~TTM_WRITE_LOCK_PENDING;
++ wake_up_all(&lock->queue);
++ spin_unlock(&lock->lock);
++ }
++ } else
++ wait_event(lock->queue, __ttm_read_lock(lock));
++
++ return ret;
++}
++EXPORT_SYMBOL(ttm_write_lock);
++
++void ttm_write_lock_downgrade(struct ttm_lock *lock)
++{
++ spin_lock(&lock->lock);
++ lock->rw = 1;
++ wake_up_all(&lock->queue);
++ spin_unlock(&lock->lock);
++}
++
++static int __ttm_vt_unlock(struct ttm_lock *lock)
++{
++ int ret = 0;
++
++ spin_lock(&lock->lock);
++ if (unlikely(!(lock->flags & TTM_VT_LOCK)))
++ ret = -EINVAL;
++ lock->flags &= ~TTM_VT_LOCK;
++ wake_up_all(&lock->queue);
++ spin_unlock(&lock->lock);
++ printk(KERN_INFO TTM_PFX "vt unlock.\n");
++
++ return ret;
++}
++
++static void ttm_vt_lock_remove(struct ttm_base_object **p_base)
++{
++ struct ttm_base_object *base = *p_base;
++ struct ttm_lock *lock = container_of(base, struct ttm_lock, base);
++ int ret;
++
++ *p_base = NULL;
++ ret = __ttm_vt_unlock(lock);
++ BUG_ON(ret != 0);
++}
++
++static bool __ttm_vt_lock(struct ttm_lock *lock)
++{
++ bool locked = false;
++
++ spin_lock(&lock->lock);
++ if (lock->rw == 0) {
++ lock->flags &= ~TTM_VT_LOCK_PENDING;
++ lock->flags |= TTM_VT_LOCK;
++ locked = true;
++ } else {
++ lock->flags |= TTM_VT_LOCK_PENDING;
++ }
++ spin_unlock(&lock->lock);
++ return locked;
++}
++
++int ttm_vt_lock(struct ttm_lock *lock,
++ bool interruptible,
++ struct ttm_object_file *tfile)
++{
++ int ret = 0;
++
++ if (interruptible) {
++ ret = wait_event_interruptible(lock->queue,
++ __ttm_vt_lock(lock));
++ if (unlikely(ret != 0)) {
++ spin_lock(&lock->lock);
++ lock->flags &= ~TTM_VT_LOCK_PENDING;
++ wake_up_all(&lock->queue);
++ spin_unlock(&lock->lock);
++ return ret;
++ }
++ } else
++ wait_event(lock->queue, __ttm_vt_lock(lock));
++
++ /*
++ * Add a base-object, the destructor of which will
++ * make sure the lock is released if the client dies
++ * while holding it.
++ */
++
++ ret = ttm_base_object_init(tfile, &lock->base, false,
++ ttm_lock_type, &ttm_vt_lock_remove, NULL);
++ if (ret)
++ (void)__ttm_vt_unlock(lock);
++ else {
++ lock->vt_holder = tfile;
++ printk(KERN_INFO TTM_PFX "vt lock.\n");
++ }
++
++ return ret;
++}
++EXPORT_SYMBOL(ttm_vt_lock);
++
++int ttm_vt_unlock(struct ttm_lock *lock)
++{
++ return ttm_ref_object_base_unref(lock->vt_holder,
++ lock->base.hash.key, TTM_REF_USAGE);
++}
++EXPORT_SYMBOL(ttm_vt_unlock);
++
++void ttm_suspend_unlock(struct ttm_lock *lock)
++{
++ spin_lock(&lock->lock);
++ lock->flags &= ~TTM_SUSPEND_LOCK;
++ wake_up_all(&lock->queue);
++ spin_unlock(&lock->lock);
++}
++EXPORT_SYMBOL(ttm_suspend_unlock);
++
++static bool __ttm_suspend_lock(struct ttm_lock *lock)
++{
++ bool locked = false;
++
++ spin_lock(&lock->lock);
++ if (lock->rw == 0) {
++ lock->flags &= ~TTM_SUSPEND_LOCK_PENDING;
++ lock->flags |= TTM_SUSPEND_LOCK;
++ locked = true;
++ } else {
++ lock->flags |= TTM_SUSPEND_LOCK_PENDING;
++ }
++ spin_unlock(&lock->lock);
++ return locked;
++}
++
++void ttm_suspend_lock(struct ttm_lock *lock)
++{
++ wait_event(lock->queue, __ttm_suspend_lock(lock));
++}
++EXPORT_SYMBOL(ttm_suspend_lock);
+diff --git a/drivers/gpu/drm/ttm/ttm_memory.c b/drivers/gpu/drm/ttm/ttm_memory.c
+index 072c281..f5245c0 100644
+--- a/drivers/gpu/drm/ttm/ttm_memory.c
++++ b/drivers/gpu/drm/ttm/ttm_memory.c
+@@ -274,16 +274,17 @@ static int ttm_mem_init_kernel_zone(struct ttm_mem_global *glob,
+ static int ttm_mem_init_highmem_zone(struct ttm_mem_global *glob,
+ const struct sysinfo *si)
+ {
+- struct ttm_mem_zone *zone = kzalloc(sizeof(*zone), GFP_KERNEL);
++ struct ttm_mem_zone *zone;
+ uint64_t mem;
+ int ret;
+
+- if (unlikely(!zone))
+- return -ENOMEM;
+-
+ if (si->totalhigh == 0)
+ return 0;
+
++ zone = kzalloc(sizeof(*zone), GFP_KERNEL);
++ if (unlikely(!zone))
++ return -ENOMEM;
++
+ mem = si->totalram;
+ mem *= si->mem_unit;
+
+@@ -322,8 +323,10 @@ static int ttm_mem_init_dma32_zone(struct ttm_mem_global *glob,
+ * No special dma32 zone needed.
+ */
+
+- if (mem <= ((uint64_t) 1ULL << 32))
++ if (mem <= ((uint64_t) 1ULL << 32)) {
++ kfree(zone);
+ return 0;
++ }
+
+ /*
+ * Limit max dma32 memory to 4GB for now
+@@ -460,6 +463,7 @@ void ttm_mem_global_free(struct ttm_mem_global *glob,
+ {
+ return ttm_mem_global_free_zone(glob, NULL, amount);
+ }
++EXPORT_SYMBOL(ttm_mem_global_free);
+
+ static int ttm_mem_global_reserve(struct ttm_mem_global *glob,
+ struct ttm_mem_zone *single_zone,
+@@ -533,6 +537,7 @@ int ttm_mem_global_alloc(struct ttm_mem_global *glob, uint64_t memory,
+ return ttm_mem_global_alloc_zone(glob, NULL, memory, no_wait,
+ interruptible);
+ }
++EXPORT_SYMBOL(ttm_mem_global_alloc);
+
+ int ttm_mem_global_alloc_page(struct ttm_mem_global *glob,
+ struct page *page,
+@@ -588,3 +593,4 @@ size_t ttm_round_pot(size_t size)
+ }
+ return 0;
+ }
++EXPORT_SYMBOL(ttm_round_pot);
+diff --git a/drivers/gpu/drm/ttm/ttm_object.c b/drivers/gpu/drm/ttm/ttm_object.c
+new file mode 100644
+index 0000000..75e9d6f
+--- /dev/null
++++ b/drivers/gpu/drm/ttm/ttm_object.c
+@@ -0,0 +1,452 @@
++/**************************************************************************
++ *
++ * Copyright (c) 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++/*
++ * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
++ */
++/** @file ttm_ref_object.c
++ *
++ * Base- and reference object implementation for the various
++ * ttm objects. Implements reference counting, minimal security checks
++ * and release on file close.
++ */
++
++/**
++ * struct ttm_object_file
++ *
++ * @tdev: Pointer to the ttm_object_device.
++ *
++ * @lock: Lock that protects the ref_list list and the
++ * ref_hash hash tables.
++ *
++ * @ref_list: List of ttm_ref_objects to be destroyed at
++ * file release.
++ *
++ * @ref_hash: Hash tables of ref objects, one per ttm_ref_type,
++ * for fast lookup of ref objects given a base object.
++ */
++
++#include "ttm/ttm_object.h"
++#include "ttm/ttm_module.h"
++#include <linux/list.h>
++#include <linux/spinlock.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <asm/atomic.h>
++
++struct ttm_object_file {
++ struct ttm_object_device *tdev;
++ rwlock_t lock;
++ struct list_head ref_list;
++ struct drm_open_hash ref_hash[TTM_REF_NUM];
++ struct kref refcount;
++};
++
++/**
++ * struct ttm_object_device
++ *
++ * @object_lock: lock that protects the object_hash hash table.
++ *
++ * @object_hash: hash table for fast lookup of object global names.
++ *
++ * @object_count: Per device object count.
++ *
++ * This is the per-device data structure needed for ttm object management.
++ */
++
++struct ttm_object_device {
++ rwlock_t object_lock;
++ struct drm_open_hash object_hash;
++ atomic_t object_count;
++ struct ttm_mem_global *mem_glob;
++};
++
++/**
++ * struct ttm_ref_object
++ *
++ * @hash: Hash entry for the per-file object reference hash.
++ *
++ * @head: List entry for the per-file list of ref-objects.
++ *
++ * @kref: Ref count.
++ *
++ * @obj: Base object this ref object is referencing.
++ *
++ * @ref_type: Type of ref object.
++ *
++ * This is similar to an idr object, but it also has a hash table entry
++ * that allows lookup with a pointer to the referenced object as a key. In
++ * that way, one can easily detect whether a base object is referenced by
++ * a particular ttm_object_file. It also carries a ref count to avoid creating
++ * multiple ref objects if a ttm_object_file references the same base
++ * object more than once.
++ */
++
++struct ttm_ref_object {
++ struct drm_hash_item hash;
++ struct list_head head;
++ struct kref kref;
++ enum ttm_ref_type ref_type;
++ struct ttm_base_object *obj;
++ struct ttm_object_file *tfile;
++};
++
++static inline struct ttm_object_file *
++ttm_object_file_ref(struct ttm_object_file *tfile)
++{
++ kref_get(&tfile->refcount);
++ return tfile;
++}
++
++static void ttm_object_file_destroy(struct kref *kref)
++{
++ struct ttm_object_file *tfile =
++ container_of(kref, struct ttm_object_file, refcount);
++
++ kfree(tfile);
++}
++
++
++static inline void ttm_object_file_unref(struct ttm_object_file **p_tfile)
++{
++ struct ttm_object_file *tfile = *p_tfile;
++
++ *p_tfile = NULL;
++ kref_put(&tfile->refcount, ttm_object_file_destroy);
++}
++
++
++int ttm_base_object_init(struct ttm_object_file *tfile,
++ struct ttm_base_object *base,
++ bool shareable,
++ enum ttm_object_type object_type,
++ void (*refcount_release) (struct ttm_base_object **),
++ void (*ref_obj_release) (struct ttm_base_object *,
++ enum ttm_ref_type ref_type))
++{
++ struct ttm_object_device *tdev = tfile->tdev;
++ int ret;
++
++ base->shareable = shareable;
++ base->tfile = ttm_object_file_ref(tfile);
++ base->refcount_release = refcount_release;
++ base->ref_obj_release = ref_obj_release;
++ base->object_type = object_type;
++ write_lock(&tdev->object_lock);
++ kref_init(&base->refcount);
++ ret = drm_ht_just_insert_please(&tdev->object_hash,
++ &base->hash,
++ (unsigned long)base, 31, 0, 0);
++ write_unlock(&tdev->object_lock);
++ if (unlikely(ret != 0))
++ goto out_err0;
++
++ ret = ttm_ref_object_add(tfile, base, TTM_REF_USAGE, NULL);
++ if (unlikely(ret != 0))
++ goto out_err1;
++
++ ttm_base_object_unref(&base);
++
++ return 0;
++out_err1:
++ (void)drm_ht_remove_item(&tdev->object_hash, &base->hash);
++out_err0:
++ return ret;
++}
++EXPORT_SYMBOL(ttm_base_object_init);
++
++static void ttm_release_base(struct kref *kref)
++{
++ struct ttm_base_object *base =
++ container_of(kref, struct ttm_base_object, refcount);
++ struct ttm_object_device *tdev = base->tfile->tdev;
++
++ (void)drm_ht_remove_item(&tdev->object_hash, &base->hash);
++ write_unlock(&tdev->object_lock);
++ if (base->refcount_release) {
++ ttm_object_file_unref(&base->tfile);
++ base->refcount_release(&base);
++ }
++ write_lock(&tdev->object_lock);
++}
++
++void ttm_base_object_unref(struct ttm_base_object **p_base)
++{
++ struct ttm_base_object *base = *p_base;
++ struct ttm_object_device *tdev = base->tfile->tdev;
++
++ *p_base = NULL;
++
++ /*
++ * Need to take the lock here to avoid racing with
++ * users trying to look up the object.
++ */
++
++ write_lock(&tdev->object_lock);
++ (void)kref_put(&base->refcount, &ttm_release_base);
++ write_unlock(&tdev->object_lock);
++}
++EXPORT_SYMBOL(ttm_base_object_unref);
++
++struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file *tfile,
++ uint32_t key)
++{
++ struct ttm_object_device *tdev = tfile->tdev;
++ struct ttm_base_object *base;
++ struct drm_hash_item *hash;
++ int ret;
++
++ read_lock(&tdev->object_lock);
++ ret = drm_ht_find_item(&tdev->object_hash, key, &hash);
++
++ if (likely(ret == 0)) {
++ base = drm_hash_entry(hash, struct ttm_base_object, hash);
++ kref_get(&base->refcount);
++ }
++ read_unlock(&tdev->object_lock);
++
++ if (unlikely(ret != 0))
++ return NULL;
++
++ if (tfile != base->tfile && !base->shareable) {
++ printk(KERN_ERR TTM_PFX
++ "Attempted access of non-shareable object.\n");
++ ttm_base_object_unref(&base);
++ return NULL;
++ }
++
++ return base;
++}
++EXPORT_SYMBOL(ttm_base_object_lookup);
++
++int ttm_ref_object_add(struct ttm_object_file *tfile,
++ struct ttm_base_object *base,
++ enum ttm_ref_type ref_type, bool *existed)
++{
++ struct drm_open_hash *ht = &tfile->ref_hash[ref_type];
++ struct ttm_ref_object *ref;
++ struct drm_hash_item *hash;
++ struct ttm_mem_global *mem_glob = tfile->tdev->mem_glob;
++ int ret = -EINVAL;
++
++ if (existed != NULL)
++ *existed = true;
++
++ while (ret == -EINVAL) {
++ read_lock(&tfile->lock);
++ ret = drm_ht_find_item(ht, base->hash.key, &hash);
++
++ if (ret == 0) {
++ ref = drm_hash_entry(hash, struct ttm_ref_object, hash);
++ kref_get(&ref->kref);
++ read_unlock(&tfile->lock);
++ break;
++ }
++
++ read_unlock(&tfile->lock);
++ ret = ttm_mem_global_alloc(mem_glob, sizeof(*ref),
++ false, false);
++ if (unlikely(ret != 0))
++ return ret;
++ ref = kmalloc(sizeof(*ref), GFP_KERNEL);
++ if (unlikely(ref == NULL)) {
++ ttm_mem_global_free(mem_glob, sizeof(*ref));
++ return -ENOMEM;
++ }
++
++ ref->hash.key = base->hash.key;
++ ref->obj = base;
++ ref->tfile = tfile;
++ ref->ref_type = ref_type;
++ kref_init(&ref->kref);
++
++ write_lock(&tfile->lock);
++ ret = drm_ht_insert_item(ht, &ref->hash);
++
++ if (likely(ret == 0)) {
++ list_add_tail(&ref->head, &tfile->ref_list);
++ kref_get(&base->refcount);
++ write_unlock(&tfile->lock);
++ if (existed != NULL)
++ *existed = false;
++ break;
++ }
++
++ write_unlock(&tfile->lock);
++ BUG_ON(ret != -EINVAL);
++
++ ttm_mem_global_free(mem_glob, sizeof(*ref));
++ kfree(ref);
++ }
++
++ return ret;
++}
++EXPORT_SYMBOL(ttm_ref_object_add);
++
++static void ttm_ref_object_release(struct kref *kref)
++{
++ struct ttm_ref_object *ref =
++ container_of(kref, struct ttm_ref_object, kref);
++ struct ttm_base_object *base = ref->obj;
++ struct ttm_object_file *tfile = ref->tfile;
++ struct drm_open_hash *ht;
++ struct ttm_mem_global *mem_glob = tfile->tdev->mem_glob;
++
++ ht = &tfile->ref_hash[ref->ref_type];
++ (void)drm_ht_remove_item(ht, &ref->hash);
++ list_del(&ref->head);
++ write_unlock(&tfile->lock);
++
++ if (ref->ref_type != TTM_REF_USAGE && base->ref_obj_release)
++ base->ref_obj_release(base, ref->ref_type);
++
++ ttm_base_object_unref(&ref->obj);
++ ttm_mem_global_free(mem_glob, sizeof(*ref));
++ kfree(ref);
++ write_lock(&tfile->lock);
++}
++
++int ttm_ref_object_base_unref(struct ttm_object_file *tfile,
++ unsigned long key, enum ttm_ref_type ref_type)
++{
++ struct drm_open_hash *ht = &tfile->ref_hash[ref_type];
++ struct ttm_ref_object *ref;
++ struct drm_hash_item *hash;
++ int ret;
++
++ write_lock(&tfile->lock);
++ ret = drm_ht_find_item(ht, key, &hash);
++ if (unlikely(ret != 0)) {
++ write_unlock(&tfile->lock);
++ return -EINVAL;
++ }
++ ref = drm_hash_entry(hash, struct ttm_ref_object, hash);
++ kref_put(&ref->kref, ttm_ref_object_release);
++ write_unlock(&tfile->lock);
++ return 0;
++}
++EXPORT_SYMBOL(ttm_ref_object_base_unref);
++
++void ttm_object_file_release(struct ttm_object_file **p_tfile)
++{
++ struct ttm_ref_object *ref;
++ struct list_head *list;
++ unsigned int i;
++ struct ttm_object_file *tfile = *p_tfile;
++
++ *p_tfile = NULL;
++ write_lock(&tfile->lock);
++
++ /*
++ * Since we release the lock within the loop, we have to
++ * restart it from the beginning each time.
++ */
++
++ while (!list_empty(&tfile->ref_list)) {
++ list = tfile->ref_list.next;
++ ref = list_entry(list, struct ttm_ref_object, head);
++ ttm_ref_object_release(&ref->kref);
++ }
++
++ for (i = 0; i < TTM_REF_NUM; ++i)
++ drm_ht_remove(&tfile->ref_hash[i]);
++
++ write_unlock(&tfile->lock);
++ ttm_object_file_unref(&tfile);
++}
++EXPORT_SYMBOL(ttm_object_file_release);
++
++struct ttm_object_file *ttm_object_file_init(struct ttm_object_device *tdev,
++ unsigned int hash_order)
++{
++ struct ttm_object_file *tfile = kmalloc(sizeof(*tfile), GFP_KERNEL);
++ unsigned int i;
++ unsigned int j = 0;
++ int ret;
++
++ if (unlikely(tfile == NULL))
++ return NULL;
++
++ rwlock_init(&tfile->lock);
++ tfile->tdev = tdev;
++ kref_init(&tfile->refcount);
++ INIT_LIST_HEAD(&tfile->ref_list);
++
++ for (i = 0; i < TTM_REF_NUM; ++i) {
++ ret = drm_ht_create(&tfile->ref_hash[i], hash_order);
++ if (ret) {
++ j = i;
++ goto out_err;
++ }
++ }
++
++ return tfile;
++out_err:
++ for (i = 0; i < j; ++i)
++ drm_ht_remove(&tfile->ref_hash[i]);
++
++ kfree(tfile);
++
++ return NULL;
++}
++EXPORT_SYMBOL(ttm_object_file_init);
++
++struct ttm_object_device *ttm_object_device_init(struct ttm_mem_global
++ *mem_glob,
++ unsigned int hash_order)
++{
++ struct ttm_object_device *tdev = kmalloc(sizeof(*tdev), GFP_KERNEL);
++ int ret;
++
++ if (unlikely(tdev == NULL))
++ return NULL;
++
++ tdev->mem_glob = mem_glob;
++ rwlock_init(&tdev->object_lock);
++ atomic_set(&tdev->object_count, 0);
++ ret = drm_ht_create(&tdev->object_hash, hash_order);
++
++ if (likely(ret == 0))
++ return tdev;
++
++ kfree(tdev);
++ return NULL;
++}
++EXPORT_SYMBOL(ttm_object_device_init);
++
++void ttm_object_device_release(struct ttm_object_device **p_tdev)
++{
++ struct ttm_object_device *tdev = *p_tdev;
++
++ *p_tdev = NULL;
++
++ write_lock(&tdev->object_lock);
++ drm_ht_remove(&tdev->object_hash);
++ write_unlock(&tdev->object_lock);
++
++ kfree(tdev);
++}
++EXPORT_SYMBOL(ttm_object_device_release);
+diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c
+index 7bcb89f..e2123af 100644
+--- a/drivers/gpu/drm/ttm/ttm_tt.c
++++ b/drivers/gpu/drm/ttm/ttm_tt.c
+@@ -192,22 +192,32 @@ int ttm_tt_populate(struct ttm_tt *ttm)
+ ttm->state = tt_unbound;
+ return 0;
+ }
++EXPORT_SYMBOL(ttm_tt_populate);
+
+ #ifdef CONFIG_X86
+ static inline int ttm_tt_set_page_caching(struct page *p,
+ enum ttm_caching_state c_state)
+ {
++ int ret = 0;
++
+ if (PageHighMem(p))
+ return 0;
+
+- switch (c_state) {
+- case tt_cached:
+- return set_pages_wb(p, 1);
+- case tt_wc:
+- return set_memory_wc((unsigned long) page_address(p), 1);
+- default:
+- return set_pages_uc(p, 1);
++ if (get_page_memtype(p) != -1) {
++ /* p isn't in the default caching state, set it to
++ * writeback first to free its current memtype. */
++
++ ret = set_pages_wb(p, 1);
++ if (ret)
++ return ret;
+ }
++
++ if (c_state == tt_wc)
++ ret = set_memory_wc((unsigned long) page_address(p), 1);
++ else if (c_state == tt_uncached)
++ ret = set_pages_uc(p, 1);
++
++ return ret;
+ }
+ #else /* CONFIG_X86 */
+ static inline int ttm_tt_set_page_caching(struct page *p,
+diff --git a/drivers/gpu/drm/via/via_drv.c b/drivers/gpu/drm/via/via_drv.c
+index bc2f518..7a1b210 100644
+--- a/drivers/gpu/drm/via/via_drv.c
++++ b/drivers/gpu/drm/via/via_drv.c
+@@ -58,7 +58,7 @@ static struct drm_driver driver = {
+ .owner = THIS_MODULE,
+ .open = drm_open,
+ .release = drm_release,
+- .ioctl = drm_ioctl,
++ .unlocked_ioctl = drm_ioctl,
+ .mmap = drm_mmap,
+ .poll = drm_poll,
+ .fasync = drm_fasync,
+diff --git a/drivers/gpu/drm/vmwgfx/Kconfig b/drivers/gpu/drm/vmwgfx/Kconfig
+new file mode 100644
+index 0000000..f20b8bc
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/Kconfig
+@@ -0,0 +1,13 @@
++config DRM_VMWGFX
++ tristate "DRM driver for VMware Virtual GPU"
++ depends on DRM && PCI
++ select FB_DEFERRED_IO
++ select FB_CFB_FILLRECT
++ select FB_CFB_COPYAREA
++ select FB_CFB_IMAGEBLIT
++ select DRM_TTM
++ help
++ KMS enabled DRM driver for SVGA2 virtual hardware.
++
++ If unsure say n. The compiled module will be
++ called vmwgfx.ko
+diff --git a/drivers/gpu/drm/vmwgfx/Makefile b/drivers/gpu/drm/vmwgfx/Makefile
+new file mode 100644
+index 0000000..1a3cb68
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/Makefile
+@@ -0,0 +1,9 @@
++
++ccflags-y := -Iinclude/drm
++
++vmwgfx-y := vmwgfx_execbuf.o vmwgfx_gmr.o vmwgfx_kms.o vmwgfx_drv.o \
++ vmwgfx_fb.o vmwgfx_ioctl.o vmwgfx_resource.o vmwgfx_buffer.o \
++ vmwgfx_fifo.o vmwgfx_irq.o vmwgfx_ldu.o vmwgfx_ttm_glue.o \
++ vmwgfx_overlay.o
++
++obj-$(CONFIG_DRM_VMWGFX) := vmwgfx.o
+diff --git a/drivers/gpu/drm/vmwgfx/svga3d_reg.h b/drivers/gpu/drm/vmwgfx/svga3d_reg.h
+new file mode 100644
+index 0000000..77cb453
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/svga3d_reg.h
+@@ -0,0 +1,1793 @@
++/**********************************************************
++ * Copyright 1998-2009 VMware, Inc. All rights reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person
++ * obtaining a copy of this software and associated documentation
++ * files (the "Software"), to deal in the Software without
++ * restriction, including without limitation the rights to use, copy,
++ * modify, merge, publish, distribute, sublicense, and/or sell copies
++ * of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ *
++ **********************************************************/
++
++/*
++ * svga3d_reg.h --
++ *
++ * SVGA 3D hardware definitions
++ */
++
++#ifndef _SVGA3D_REG_H_
++#define _SVGA3D_REG_H_
++
++#include "svga_reg.h"
++
++
++/*
++ * 3D Hardware Version
++ *
++ * The hardware version is stored in the SVGA_FIFO_3D_HWVERSION fifo
++ * register. Is set by the host and read by the guest. This lets
++ * us make new guest drivers which are backwards-compatible with old
++ * SVGA hardware revisions. It does not let us support old guest
++ * drivers. Good enough for now.
++ *
++ */
++
++#define SVGA3D_MAKE_HWVERSION(major, minor) (((major) << 16) | ((minor) & 0xFF))
++#define SVGA3D_MAJOR_HWVERSION(version) ((version) >> 16)
++#define SVGA3D_MINOR_HWVERSION(version) ((version) & 0xFF)
++
++typedef enum {
++ SVGA3D_HWVERSION_WS5_RC1 = SVGA3D_MAKE_HWVERSION(0, 1),
++ SVGA3D_HWVERSION_WS5_RC2 = SVGA3D_MAKE_HWVERSION(0, 2),
++ SVGA3D_HWVERSION_WS51_RC1 = SVGA3D_MAKE_HWVERSION(0, 3),
++ SVGA3D_HWVERSION_WS6_B1 = SVGA3D_MAKE_HWVERSION(1, 1),
++ SVGA3D_HWVERSION_FUSION_11 = SVGA3D_MAKE_HWVERSION(1, 4),
++ SVGA3D_HWVERSION_WS65_B1 = SVGA3D_MAKE_HWVERSION(2, 0),
++ SVGA3D_HWVERSION_CURRENT = SVGA3D_HWVERSION_WS65_B1,
++} SVGA3dHardwareVersion;
++
++/*
++ * Generic Types
++ */
++
++typedef uint32 SVGA3dBool; /* 32-bit Bool definition */
++#define SVGA3D_NUM_CLIPPLANES 6
++#define SVGA3D_MAX_SIMULTANEOUS_RENDER_TARGETS 8
++
++
++/*
++ * Surface formats.
++ *
++ * If you modify this list, be sure to keep GLUtil.c in sync. It
++ * includes the internal format definition of each surface in
++ * GLUtil_ConvertSurfaceFormat, and it contains a table of
++ * human-readable names in GLUtil_GetFormatName.
++ */
++
++typedef enum SVGA3dSurfaceFormat {
++ SVGA3D_FORMAT_INVALID = 0,
++
++ SVGA3D_X8R8G8B8 = 1,
++ SVGA3D_A8R8G8B8 = 2,
++
++ SVGA3D_R5G6B5 = 3,
++ SVGA3D_X1R5G5B5 = 4,
++ SVGA3D_A1R5G5B5 = 5,
++ SVGA3D_A4R4G4B4 = 6,
++
++ SVGA3D_Z_D32 = 7,
++ SVGA3D_Z_D16 = 8,
++ SVGA3D_Z_D24S8 = 9,
++ SVGA3D_Z_D15S1 = 10,
++
++ SVGA3D_LUMINANCE8 = 11,
++ SVGA3D_LUMINANCE4_ALPHA4 = 12,
++ SVGA3D_LUMINANCE16 = 13,
++ SVGA3D_LUMINANCE8_ALPHA8 = 14,
++
++ SVGA3D_DXT1 = 15,
++ SVGA3D_DXT2 = 16,
++ SVGA3D_DXT3 = 17,
++ SVGA3D_DXT4 = 18,
++ SVGA3D_DXT5 = 19,
++
++ SVGA3D_BUMPU8V8 = 20,
++ SVGA3D_BUMPL6V5U5 = 21,
++ SVGA3D_BUMPX8L8V8U8 = 22,
++ SVGA3D_BUMPL8V8U8 = 23,
++
++ SVGA3D_ARGB_S10E5 = 24, /* 16-bit floating-point ARGB */
++ SVGA3D_ARGB_S23E8 = 25, /* 32-bit floating-point ARGB */
++
++ SVGA3D_A2R10G10B10 = 26,
++
++ /* signed formats */
++ SVGA3D_V8U8 = 27,
++ SVGA3D_Q8W8V8U8 = 28,
++ SVGA3D_CxV8U8 = 29,
++
++ /* mixed formats */
++ SVGA3D_X8L8V8U8 = 30,
++ SVGA3D_A2W10V10U10 = 31,
++
++ SVGA3D_ALPHA8 = 32,
++
++ /* Single- and dual-component floating point formats */
++ SVGA3D_R_S10E5 = 33,
++ SVGA3D_R_S23E8 = 34,
++ SVGA3D_RG_S10E5 = 35,
++ SVGA3D_RG_S23E8 = 36,
++
++ /*
++ * Any surface can be used as a buffer object, but SVGA3D_BUFFER is
++ * the most efficient format to use when creating new surfaces
++ * expressly for index or vertex data.
++ */
++ SVGA3D_BUFFER = 37,
++
++ SVGA3D_Z_D24X8 = 38,
++
++ SVGA3D_V16U16 = 39,
++
++ SVGA3D_G16R16 = 40,
++ SVGA3D_A16B16G16R16 = 41,
++
++ /* Packed Video formats */
++ SVGA3D_UYVY = 42,
++ SVGA3D_YUY2 = 43,
++
++ SVGA3D_FORMAT_MAX
++} SVGA3dSurfaceFormat;
++
++typedef uint32 SVGA3dColor; /* a, r, g, b */
++
++/*
++ * These match the D3DFORMAT_OP definitions used by Direct3D. We need
++ * them so that we can query the host for what the supported surface
++ * operations are (when we're using the D3D backend, in particular),
++ * and so we can send those operations to the guest.
++ */
++typedef enum {
++ SVGA3DFORMAT_OP_TEXTURE = 0x00000001,
++ SVGA3DFORMAT_OP_VOLUMETEXTURE = 0x00000002,
++ SVGA3DFORMAT_OP_CUBETEXTURE = 0x00000004,
++ SVGA3DFORMAT_OP_OFFSCREEN_RENDERTARGET = 0x00000008,
++ SVGA3DFORMAT_OP_SAME_FORMAT_RENDERTARGET = 0x00000010,
++ SVGA3DFORMAT_OP_ZSTENCIL = 0x00000040,
++ SVGA3DFORMAT_OP_ZSTENCIL_WITH_ARBITRARY_COLOR_DEPTH = 0x00000080,
++
++/*
++ * This format can be used as a render target if the current display mode
++ * is the same depth if the alpha channel is ignored. e.g. if the device
++ * can render to A8R8G8B8 when the display mode is X8R8G8B8, then the
++ * format op list entry for A8R8G8B8 should have this cap.
++ */
++ SVGA3DFORMAT_OP_SAME_FORMAT_UP_TO_ALPHA_RENDERTARGET = 0x00000100,
++
++/*
++ * This format contains DirectDraw support (including Flip). This flag
++ * should not to be set on alpha formats.
++ */
++ SVGA3DFORMAT_OP_DISPLAYMODE = 0x00000400,
++
++/*
++ * The rasterizer can support some level of Direct3D support in this format
++ * and implies that the driver can create a Context in this mode (for some
++ * render target format). When this flag is set, the SVGA3DFORMAT_OP_DISPLAYMODE
++ * flag must also be set.
++ */
++ SVGA3DFORMAT_OP_3DACCELERATION = 0x00000800,
++
++/*
++ * This is set for a private format when the driver has put the bpp in
++ * the structure.
++ */
++ SVGA3DFORMAT_OP_PIXELSIZE = 0x00001000,
++
++/*
++ * Indicates that this format can be converted to any RGB format for which
++ * SVGA3DFORMAT_OP_MEMBEROFGROUP_ARGB is specified
++ */
++ SVGA3DFORMAT_OP_CONVERT_TO_ARGB = 0x00002000,
++
++/*
++ * Indicates that this format can be used to create offscreen plain surfaces.
++ */
++ SVGA3DFORMAT_OP_OFFSCREENPLAIN = 0x00004000,
++
++/*
++ * Indicated that this format can be read as an SRGB texture (meaning that the
++ * sampler will linearize the looked up data)
++ */
++ SVGA3DFORMAT_OP_SRGBREAD = 0x00008000,
++
++/*
++ * Indicates that this format can be used in the bumpmap instructions
++ */
++ SVGA3DFORMAT_OP_BUMPMAP = 0x00010000,
++
++/*
++ * Indicates that this format can be sampled by the displacement map sampler
++ */
++ SVGA3DFORMAT_OP_DMAP = 0x00020000,
++
++/*
++ * Indicates that this format cannot be used with texture filtering
++ */
++ SVGA3DFORMAT_OP_NOFILTER = 0x00040000,
++
++/*
++ * Indicates that format conversions are supported to this RGB format if
++ * SVGA3DFORMAT_OP_CONVERT_TO_ARGB is specified in the source format.
++ */
++ SVGA3DFORMAT_OP_MEMBEROFGROUP_ARGB = 0x00080000,
++
++/*
++ * Indicated that this format can be written as an SRGB target (meaning that the
++ * pixel pipe will DE-linearize data on output to format)
++ */
++ SVGA3DFORMAT_OP_SRGBWRITE = 0x00100000,
++
++/*
++ * Indicates that this format cannot be used with alpha blending
++ */
++ SVGA3DFORMAT_OP_NOALPHABLEND = 0x00200000,
++
++/*
++ * Indicates that the device can auto-generated sublevels for resources
++ * of this format
++ */
++ SVGA3DFORMAT_OP_AUTOGENMIPMAP = 0x00400000,
++
++/*
++ * Indicates that this format can be used by vertex texture sampler
++ */
++ SVGA3DFORMAT_OP_VERTEXTEXTURE = 0x00800000,
++
++/*
++ * Indicates that this format supports neither texture coordinate wrap
++ * modes, nor mipmapping
++ */
++ SVGA3DFORMAT_OP_NOTEXCOORDWRAPNORMIP = 0x01000000
++} SVGA3dFormatOp;
++
++/*
++ * This structure is a conversion of SVGA3DFORMAT_OP_*.
++ * Entries must be located at the same position.
++ */
++typedef union {
++ uint32 value;
++ struct {
++ uint32 texture : 1;
++ uint32 volumeTexture : 1;
++ uint32 cubeTexture : 1;
++ uint32 offscreenRenderTarget : 1;
++ uint32 sameFormatRenderTarget : 1;
++ uint32 unknown1 : 1;
++ uint32 zStencil : 1;
++ uint32 zStencilArbitraryDepth : 1;
++ uint32 sameFormatUpToAlpha : 1;
++ uint32 unknown2 : 1;
++ uint32 displayMode : 1;
++ uint32 acceleration3d : 1;
++ uint32 pixelSize : 1;
++ uint32 convertToARGB : 1;
++ uint32 offscreenPlain : 1;
++ uint32 sRGBRead : 1;
++ uint32 bumpMap : 1;
++ uint32 dmap : 1;
++ uint32 noFilter : 1;
++ uint32 memberOfGroupARGB : 1;
++ uint32 sRGBWrite : 1;
++ uint32 noAlphaBlend : 1;
++ uint32 autoGenMipMap : 1;
++ uint32 vertexTexture : 1;
++ uint32 noTexCoordWrapNorMip : 1;
++ };
++} SVGA3dSurfaceFormatCaps;
++
++/*
++ * SVGA_3D_CMD_SETRENDERSTATE Types. All value types
++ * must fit in a uint32.
++ */
++
++typedef enum {
++ SVGA3D_RS_INVALID = 0,
++ SVGA3D_RS_ZENABLE = 1, /* SVGA3dBool */
++ SVGA3D_RS_ZWRITEENABLE = 2, /* SVGA3dBool */
++ SVGA3D_RS_ALPHATESTENABLE = 3, /* SVGA3dBool */
++ SVGA3D_RS_DITHERENABLE = 4, /* SVGA3dBool */
++ SVGA3D_RS_BLENDENABLE = 5, /* SVGA3dBool */
++ SVGA3D_RS_FOGENABLE = 6, /* SVGA3dBool */
++ SVGA3D_RS_SPECULARENABLE = 7, /* SVGA3dBool */
++ SVGA3D_RS_STENCILENABLE = 8, /* SVGA3dBool */
++ SVGA3D_RS_LIGHTINGENABLE = 9, /* SVGA3dBool */
++ SVGA3D_RS_NORMALIZENORMALS = 10, /* SVGA3dBool */
++ SVGA3D_RS_POINTSPRITEENABLE = 11, /* SVGA3dBool */
++ SVGA3D_RS_POINTSCALEENABLE = 12, /* SVGA3dBool */
++ SVGA3D_RS_STENCILREF = 13, /* uint32 */
++ SVGA3D_RS_STENCILMASK = 14, /* uint32 */
++ SVGA3D_RS_STENCILWRITEMASK = 15, /* uint32 */
++ SVGA3D_RS_FOGSTART = 16, /* float */
++ SVGA3D_RS_FOGEND = 17, /* float */
++ SVGA3D_RS_FOGDENSITY = 18, /* float */
++ SVGA3D_RS_POINTSIZE = 19, /* float */
++ SVGA3D_RS_POINTSIZEMIN = 20, /* float */
++ SVGA3D_RS_POINTSIZEMAX = 21, /* float */
++ SVGA3D_RS_POINTSCALE_A = 22, /* float */
++ SVGA3D_RS_POINTSCALE_B = 23, /* float */
++ SVGA3D_RS_POINTSCALE_C = 24, /* float */
++ SVGA3D_RS_FOGCOLOR = 25, /* SVGA3dColor */
++ SVGA3D_RS_AMBIENT = 26, /* SVGA3dColor */
++ SVGA3D_RS_CLIPPLANEENABLE = 27, /* SVGA3dClipPlanes */
++ SVGA3D_RS_FOGMODE = 28, /* SVGA3dFogMode */
++ SVGA3D_RS_FILLMODE = 29, /* SVGA3dFillMode */
++ SVGA3D_RS_SHADEMODE = 30, /* SVGA3dShadeMode */
++ SVGA3D_RS_LINEPATTERN = 31, /* SVGA3dLinePattern */
++ SVGA3D_RS_SRCBLEND = 32, /* SVGA3dBlendOp */
++ SVGA3D_RS_DSTBLEND = 33, /* SVGA3dBlendOp */
++ SVGA3D_RS_BLENDEQUATION = 34, /* SVGA3dBlendEquation */
++ SVGA3D_RS_CULLMODE = 35, /* SVGA3dFace */
++ SVGA3D_RS_ZFUNC = 36, /* SVGA3dCmpFunc */
++ SVGA3D_RS_ALPHAFUNC = 37, /* SVGA3dCmpFunc */
++ SVGA3D_RS_STENCILFUNC = 38, /* SVGA3dCmpFunc */
++ SVGA3D_RS_STENCILFAIL = 39, /* SVGA3dStencilOp */
++ SVGA3D_RS_STENCILZFAIL = 40, /* SVGA3dStencilOp */
++ SVGA3D_RS_STENCILPASS = 41, /* SVGA3dStencilOp */
++ SVGA3D_RS_ALPHAREF = 42, /* float (0.0 .. 1.0) */
++ SVGA3D_RS_FRONTWINDING = 43, /* SVGA3dFrontWinding */
++ SVGA3D_RS_COORDINATETYPE = 44, /* SVGA3dCoordinateType */
++ SVGA3D_RS_ZBIAS = 45, /* float */
++ SVGA3D_RS_RANGEFOGENABLE = 46, /* SVGA3dBool */
++ SVGA3D_RS_COLORWRITEENABLE = 47, /* SVGA3dColorMask */
++ SVGA3D_RS_VERTEXMATERIALENABLE = 48, /* SVGA3dBool */
++ SVGA3D_RS_DIFFUSEMATERIALSOURCE = 49, /* SVGA3dVertexMaterial */
++ SVGA3D_RS_SPECULARMATERIALSOURCE = 50, /* SVGA3dVertexMaterial */
++ SVGA3D_RS_AMBIENTMATERIALSOURCE = 51, /* SVGA3dVertexMaterial */
++ SVGA3D_RS_EMISSIVEMATERIALSOURCE = 52, /* SVGA3dVertexMaterial */
++ SVGA3D_RS_TEXTUREFACTOR = 53, /* SVGA3dColor */
++ SVGA3D_RS_LOCALVIEWER = 54, /* SVGA3dBool */
++ SVGA3D_RS_SCISSORTESTENABLE = 55, /* SVGA3dBool */
++ SVGA3D_RS_BLENDCOLOR = 56, /* SVGA3dColor */
++ SVGA3D_RS_STENCILENABLE2SIDED = 57, /* SVGA3dBool */
++ SVGA3D_RS_CCWSTENCILFUNC = 58, /* SVGA3dCmpFunc */
++ SVGA3D_RS_CCWSTENCILFAIL = 59, /* SVGA3dStencilOp */
++ SVGA3D_RS_CCWSTENCILZFAIL = 60, /* SVGA3dStencilOp */
++ SVGA3D_RS_CCWSTENCILPASS = 61, /* SVGA3dStencilOp */
++ SVGA3D_RS_VERTEXBLEND = 62, /* SVGA3dVertexBlendFlags */
++ SVGA3D_RS_SLOPESCALEDEPTHBIAS = 63, /* float */
++ SVGA3D_RS_DEPTHBIAS = 64, /* float */
++
++
++ /*
++ * Output Gamma Level
++ *
++ * Output gamma effects the gamma curve of colors that are output from the
++ * rendering pipeline. A value of 1.0 specifies a linear color space. If the
++ * value is <= 0.0, gamma correction is ignored and linear color space is
++ * used.
++ */
++
++ SVGA3D_RS_OUTPUTGAMMA = 65, /* float */
++ SVGA3D_RS_ZVISIBLE = 66, /* SVGA3dBool */
++ SVGA3D_RS_LASTPIXEL = 67, /* SVGA3dBool */
++ SVGA3D_RS_CLIPPING = 68, /* SVGA3dBool */
++ SVGA3D_RS_WRAP0 = 69, /* SVGA3dWrapFlags */
++ SVGA3D_RS_WRAP1 = 70, /* SVGA3dWrapFlags */
++ SVGA3D_RS_WRAP2 = 71, /* SVGA3dWrapFlags */
++ SVGA3D_RS_WRAP3 = 72, /* SVGA3dWrapFlags */
++ SVGA3D_RS_WRAP4 = 73, /* SVGA3dWrapFlags */
++ SVGA3D_RS_WRAP5 = 74, /* SVGA3dWrapFlags */
++ SVGA3D_RS_WRAP6 = 75, /* SVGA3dWrapFlags */
++ SVGA3D_RS_WRAP7 = 76, /* SVGA3dWrapFlags */
++ SVGA3D_RS_WRAP8 = 77, /* SVGA3dWrapFlags */
++ SVGA3D_RS_WRAP9 = 78, /* SVGA3dWrapFlags */
++ SVGA3D_RS_WRAP10 = 79, /* SVGA3dWrapFlags */
++ SVGA3D_RS_WRAP11 = 80, /* SVGA3dWrapFlags */
++ SVGA3D_RS_WRAP12 = 81, /* SVGA3dWrapFlags */
++ SVGA3D_RS_WRAP13 = 82, /* SVGA3dWrapFlags */
++ SVGA3D_RS_WRAP14 = 83, /* SVGA3dWrapFlags */
++ SVGA3D_RS_WRAP15 = 84, /* SVGA3dWrapFlags */
++ SVGA3D_RS_MULTISAMPLEANTIALIAS = 85, /* SVGA3dBool */
++ SVGA3D_RS_MULTISAMPLEMASK = 86, /* uint32 */
++ SVGA3D_RS_INDEXEDVERTEXBLENDENABLE = 87, /* SVGA3dBool */
++ SVGA3D_RS_TWEENFACTOR = 88, /* float */
++ SVGA3D_RS_ANTIALIASEDLINEENABLE = 89, /* SVGA3dBool */
++ SVGA3D_RS_COLORWRITEENABLE1 = 90, /* SVGA3dColorMask */
++ SVGA3D_RS_COLORWRITEENABLE2 = 91, /* SVGA3dColorMask */
++ SVGA3D_RS_COLORWRITEENABLE3 = 92, /* SVGA3dColorMask */
++ SVGA3D_RS_SEPARATEALPHABLENDENABLE = 93, /* SVGA3dBool */
++ SVGA3D_RS_SRCBLENDALPHA = 94, /* SVGA3dBlendOp */
++ SVGA3D_RS_DSTBLENDALPHA = 95, /* SVGA3dBlendOp */
++ SVGA3D_RS_BLENDEQUATIONALPHA = 96, /* SVGA3dBlendEquation */
++ SVGA3D_RS_MAX
++} SVGA3dRenderStateName;
++
++typedef enum {
++ SVGA3D_VERTEXMATERIAL_NONE = 0, /* Use the value in the current material */
++ SVGA3D_VERTEXMATERIAL_DIFFUSE = 1, /* Use the value in the diffuse component */
++ SVGA3D_VERTEXMATERIAL_SPECULAR = 2, /* Use the value in the specular component */
++} SVGA3dVertexMaterial;
++
++typedef enum {
++ SVGA3D_FILLMODE_INVALID = 0,
++ SVGA3D_FILLMODE_POINT = 1,
++ SVGA3D_FILLMODE_LINE = 2,
++ SVGA3D_FILLMODE_FILL = 3,
++ SVGA3D_FILLMODE_MAX
++} SVGA3dFillModeType;
++
++
++typedef
++union {
++ struct {
++ uint16 mode; /* SVGA3dFillModeType */
++ uint16 face; /* SVGA3dFace */
++ };
++ uint32 uintValue;
++} SVGA3dFillMode;
++
++typedef enum {
++ SVGA3D_SHADEMODE_INVALID = 0,
++ SVGA3D_SHADEMODE_FLAT = 1,
++ SVGA3D_SHADEMODE_SMOOTH = 2,
++ SVGA3D_SHADEMODE_PHONG = 3, /* Not supported */
++ SVGA3D_SHADEMODE_MAX
++} SVGA3dShadeMode;
++
++typedef
++union {
++ struct {
++ uint16 repeat;
++ uint16 pattern;
++ };
++ uint32 uintValue;
++} SVGA3dLinePattern;
++
++typedef enum {
++ SVGA3D_BLENDOP_INVALID = 0,
++ SVGA3D_BLENDOP_ZERO = 1,
++ SVGA3D_BLENDOP_ONE = 2,
++ SVGA3D_BLENDOP_SRCCOLOR = 3,
++ SVGA3D_BLENDOP_INVSRCCOLOR = 4,
++ SVGA3D_BLENDOP_SRCALPHA = 5,
++ SVGA3D_BLENDOP_INVSRCALPHA = 6,
++ SVGA3D_BLENDOP_DESTALPHA = 7,
++ SVGA3D_BLENDOP_INVDESTALPHA = 8,
++ SVGA3D_BLENDOP_DESTCOLOR = 9,
++ SVGA3D_BLENDOP_INVDESTCOLOR = 10,
++ SVGA3D_BLENDOP_SRCALPHASAT = 11,
++ SVGA3D_BLENDOP_BLENDFACTOR = 12,
++ SVGA3D_BLENDOP_INVBLENDFACTOR = 13,
++ SVGA3D_BLENDOP_MAX
++} SVGA3dBlendOp;
++
++typedef enum {
++ SVGA3D_BLENDEQ_INVALID = 0,
++ SVGA3D_BLENDEQ_ADD = 1,
++ SVGA3D_BLENDEQ_SUBTRACT = 2,
++ SVGA3D_BLENDEQ_REVSUBTRACT = 3,
++ SVGA3D_BLENDEQ_MINIMUM = 4,
++ SVGA3D_BLENDEQ_MAXIMUM = 5,
++ SVGA3D_BLENDEQ_MAX
++} SVGA3dBlendEquation;
++
++typedef enum {
++ SVGA3D_FRONTWINDING_INVALID = 0,
++ SVGA3D_FRONTWINDING_CW = 1,
++ SVGA3D_FRONTWINDING_CCW = 2,
++ SVGA3D_FRONTWINDING_MAX
++} SVGA3dFrontWinding;
++
++typedef enum {
++ SVGA3D_FACE_INVALID = 0,
++ SVGA3D_FACE_NONE = 1,
++ SVGA3D_FACE_FRONT = 2,
++ SVGA3D_FACE_BACK = 3,
++ SVGA3D_FACE_FRONT_BACK = 4,
++ SVGA3D_FACE_MAX
++} SVGA3dFace;
++
++/*
++ * The order and the values should not be changed
++ */
++
++typedef enum {
++ SVGA3D_CMP_INVALID = 0,
++ SVGA3D_CMP_NEVER = 1,
++ SVGA3D_CMP_LESS = 2,
++ SVGA3D_CMP_EQUAL = 3,
++ SVGA3D_CMP_LESSEQUAL = 4,
++ SVGA3D_CMP_GREATER = 5,
++ SVGA3D_CMP_NOTEQUAL = 6,
++ SVGA3D_CMP_GREATEREQUAL = 7,
++ SVGA3D_CMP_ALWAYS = 8,
++ SVGA3D_CMP_MAX
++} SVGA3dCmpFunc;
++
++/*
++ * SVGA3D_FOGFUNC_* specifies the fog equation, or PER_VERTEX which allows
++ * the fog factor to be specified in the alpha component of the specular
++ * (a.k.a. secondary) vertex color.
++ */
++typedef enum {
++ SVGA3D_FOGFUNC_INVALID = 0,
++ SVGA3D_FOGFUNC_EXP = 1,
++ SVGA3D_FOGFUNC_EXP2 = 2,
++ SVGA3D_FOGFUNC_LINEAR = 3,
++ SVGA3D_FOGFUNC_PER_VERTEX = 4
++} SVGA3dFogFunction;
++
++/*
++ * SVGA3D_FOGTYPE_* specifies if fog factors are computed on a per-vertex
++ * or per-pixel basis.
++ */
++typedef enum {
++ SVGA3D_FOGTYPE_INVALID = 0,
++ SVGA3D_FOGTYPE_VERTEX = 1,
++ SVGA3D_FOGTYPE_PIXEL = 2,
++ SVGA3D_FOGTYPE_MAX = 3
++} SVGA3dFogType;
++
++/*
++ * SVGA3D_FOGBASE_* selects depth or range-based fog. Depth-based fog is
++ * computed using the eye Z value of each pixel (or vertex), whereas range-
++ * based fog is computed using the actual distance (range) to the eye.
++ */
++typedef enum {
++ SVGA3D_FOGBASE_INVALID = 0,
++ SVGA3D_FOGBASE_DEPTHBASED = 1,
++ SVGA3D_FOGBASE_RANGEBASED = 2,
++ SVGA3D_FOGBASE_MAX = 3
++} SVGA3dFogBase;
++
++typedef enum {
++ SVGA3D_STENCILOP_INVALID = 0,
++ SVGA3D_STENCILOP_KEEP = 1,
++ SVGA3D_STENCILOP_ZERO = 2,
++ SVGA3D_STENCILOP_REPLACE = 3,
++ SVGA3D_STENCILOP_INCRSAT = 4,
++ SVGA3D_STENCILOP_DECRSAT = 5,
++ SVGA3D_STENCILOP_INVERT = 6,
++ SVGA3D_STENCILOP_INCR = 7,
++ SVGA3D_STENCILOP_DECR = 8,
++ SVGA3D_STENCILOP_MAX
++} SVGA3dStencilOp;
++
++typedef enum {
++ SVGA3D_CLIPPLANE_0 = (1 << 0),
++ SVGA3D_CLIPPLANE_1 = (1 << 1),
++ SVGA3D_CLIPPLANE_2 = (1 << 2),
++ SVGA3D_CLIPPLANE_3 = (1 << 3),
++ SVGA3D_CLIPPLANE_4 = (1 << 4),
++ SVGA3D_CLIPPLANE_5 = (1 << 5),
++} SVGA3dClipPlanes;
++
++typedef enum {
++ SVGA3D_CLEAR_COLOR = 0x1,
++ SVGA3D_CLEAR_DEPTH = 0x2,
++ SVGA3D_CLEAR_STENCIL = 0x4
++} SVGA3dClearFlag;
++
++typedef enum {
++ SVGA3D_RT_DEPTH = 0,
++ SVGA3D_RT_STENCIL = 1,
++ SVGA3D_RT_COLOR0 = 2,
++ SVGA3D_RT_COLOR1 = 3,
++ SVGA3D_RT_COLOR2 = 4,
++ SVGA3D_RT_COLOR3 = 5,
++ SVGA3D_RT_COLOR4 = 6,
++ SVGA3D_RT_COLOR5 = 7,
++ SVGA3D_RT_COLOR6 = 8,
++ SVGA3D_RT_COLOR7 = 9,
++ SVGA3D_RT_MAX,
++ SVGA3D_RT_INVALID = ((uint32)-1),
++} SVGA3dRenderTargetType;
++
++#define SVGA3D_MAX_RT_COLOR (SVGA3D_RT_COLOR7 - SVGA3D_RT_COLOR0 + 1)
++
++typedef
++union {
++ struct {
++ uint32 red : 1;
++ uint32 green : 1;
++ uint32 blue : 1;
++ uint32 alpha : 1;
++ };
++ uint32 uintValue;
++} SVGA3dColorMask;
++
++typedef enum {
++ SVGA3D_VBLEND_DISABLE = 0,
++ SVGA3D_VBLEND_1WEIGHT = 1,
++ SVGA3D_VBLEND_2WEIGHT = 2,
++ SVGA3D_VBLEND_3WEIGHT = 3,
++} SVGA3dVertexBlendFlags;
++
++typedef enum {
++ SVGA3D_WRAPCOORD_0 = 1 << 0,
++ SVGA3D_WRAPCOORD_1 = 1 << 1,
++ SVGA3D_WRAPCOORD_2 = 1 << 2,
++ SVGA3D_WRAPCOORD_3 = 1 << 3,
++ SVGA3D_WRAPCOORD_ALL = 0xF,
++} SVGA3dWrapFlags;
++
++/*
++ * SVGA_3D_CMD_TEXTURESTATE Types. All value types
++ * must fit in a uint32.
++ */
++
++typedef enum {
++ SVGA3D_TS_INVALID = 0,
++ SVGA3D_TS_BIND_TEXTURE = 1, /* SVGA3dSurfaceId */
++ SVGA3D_TS_COLOROP = 2, /* SVGA3dTextureCombiner */
++ SVGA3D_TS_COLORARG1 = 3, /* SVGA3dTextureArgData */
++ SVGA3D_TS_COLORARG2 = 4, /* SVGA3dTextureArgData */
++ SVGA3D_TS_ALPHAOP = 5, /* SVGA3dTextureCombiner */
++ SVGA3D_TS_ALPHAARG1 = 6, /* SVGA3dTextureArgData */
++ SVGA3D_TS_ALPHAARG2 = 7, /* SVGA3dTextureArgData */
++ SVGA3D_TS_ADDRESSU = 8, /* SVGA3dTextureAddress */
++ SVGA3D_TS_ADDRESSV = 9, /* SVGA3dTextureAddress */
++ SVGA3D_TS_MIPFILTER = 10, /* SVGA3dTextureFilter */
++ SVGA3D_TS_MAGFILTER = 11, /* SVGA3dTextureFilter */
++ SVGA3D_TS_MINFILTER = 12, /* SVGA3dTextureFilter */
++ SVGA3D_TS_BORDERCOLOR = 13, /* SVGA3dColor */
++ SVGA3D_TS_TEXCOORDINDEX = 14, /* uint32 */
++ SVGA3D_TS_TEXTURETRANSFORMFLAGS = 15, /* SVGA3dTexTransformFlags */
++ SVGA3D_TS_TEXCOORDGEN = 16, /* SVGA3dTextureCoordGen */
++ SVGA3D_TS_BUMPENVMAT00 = 17, /* float */
++ SVGA3D_TS_BUMPENVMAT01 = 18, /* float */
++ SVGA3D_TS_BUMPENVMAT10 = 19, /* float */
++ SVGA3D_TS_BUMPENVMAT11 = 20, /* float */
++ SVGA3D_TS_TEXTURE_MIPMAP_LEVEL = 21, /* uint32 */
++ SVGA3D_TS_TEXTURE_LOD_BIAS = 22, /* float */
++ SVGA3D_TS_TEXTURE_ANISOTROPIC_LEVEL = 23, /* uint32 */
++ SVGA3D_TS_ADDRESSW = 24, /* SVGA3dTextureAddress */
++
++
++ /*
++ * Sampler Gamma Level
++ *
++ * Sampler gamma effects the color of samples taken from the sampler. A
++ * value of 1.0 will produce linear samples. If the value is <= 0.0 the
++ * gamma value is ignored and a linear space is used.
++ */
++
++ SVGA3D_TS_GAMMA = 25, /* float */
++ SVGA3D_TS_BUMPENVLSCALE = 26, /* float */
++ SVGA3D_TS_BUMPENVLOFFSET = 27, /* float */
++ SVGA3D_TS_COLORARG0 = 28, /* SVGA3dTextureArgData */
++ SVGA3D_TS_ALPHAARG0 = 29, /* SVGA3dTextureArgData */
++ SVGA3D_TS_MAX
++} SVGA3dTextureStateName;
++
++typedef enum {
++ SVGA3D_TC_INVALID = 0,
++ SVGA3D_TC_DISABLE = 1,
++ SVGA3D_TC_SELECTARG1 = 2,
++ SVGA3D_TC_SELECTARG2 = 3,
++ SVGA3D_TC_MODULATE = 4,
++ SVGA3D_TC_ADD = 5,
++ SVGA3D_TC_ADDSIGNED = 6,
++ SVGA3D_TC_SUBTRACT = 7,
++ SVGA3D_TC_BLENDTEXTUREALPHA = 8,
++ SVGA3D_TC_BLENDDIFFUSEALPHA = 9,
++ SVGA3D_TC_BLENDCURRENTALPHA = 10,
++ SVGA3D_TC_BLENDFACTORALPHA = 11,
++ SVGA3D_TC_MODULATE2X = 12,
++ SVGA3D_TC_MODULATE4X = 13,
++ SVGA3D_TC_DSDT = 14,
++ SVGA3D_TC_DOTPRODUCT3 = 15,
++ SVGA3D_TC_BLENDTEXTUREALPHAPM = 16,
++ SVGA3D_TC_ADDSIGNED2X = 17,
++ SVGA3D_TC_ADDSMOOTH = 18,
++ SVGA3D_TC_PREMODULATE = 19,
++ SVGA3D_TC_MODULATEALPHA_ADDCOLOR = 20,
++ SVGA3D_TC_MODULATECOLOR_ADDALPHA = 21,
++ SVGA3D_TC_MODULATEINVALPHA_ADDCOLOR = 22,
++ SVGA3D_TC_MODULATEINVCOLOR_ADDALPHA = 23,
++ SVGA3D_TC_BUMPENVMAPLUMINANCE = 24,
++ SVGA3D_TC_MULTIPLYADD = 25,
++ SVGA3D_TC_LERP = 26,
++ SVGA3D_TC_MAX
++} SVGA3dTextureCombiner;
++
++#define SVGA3D_TC_CAP_BIT(svga3d_tc_op) (svga3d_tc_op ? (1 << (svga3d_tc_op - 1)) : 0)
++
++typedef enum {
++ SVGA3D_TEX_ADDRESS_INVALID = 0,
++ SVGA3D_TEX_ADDRESS_WRAP = 1,
++ SVGA3D_TEX_ADDRESS_MIRROR = 2,
++ SVGA3D_TEX_ADDRESS_CLAMP = 3,
++ SVGA3D_TEX_ADDRESS_BORDER = 4,
++ SVGA3D_TEX_ADDRESS_MIRRORONCE = 5,
++ SVGA3D_TEX_ADDRESS_EDGE = 6,
++ SVGA3D_TEX_ADDRESS_MAX
++} SVGA3dTextureAddress;
++
++/*
++ * SVGA3D_TEX_FILTER_NONE as the minification filter means mipmapping is
++ * disabled, and the rasterizer should use the magnification filter instead.
++ */
++typedef enum {
++ SVGA3D_TEX_FILTER_NONE = 0,
++ SVGA3D_TEX_FILTER_NEAREST = 1,
++ SVGA3D_TEX_FILTER_LINEAR = 2,
++ SVGA3D_TEX_FILTER_ANISOTROPIC = 3,
++ SVGA3D_TEX_FILTER_FLATCUBIC = 4, // Deprecated, not implemented
++ SVGA3D_TEX_FILTER_GAUSSIANCUBIC = 5, // Deprecated, not implemented
++ SVGA3D_TEX_FILTER_PYRAMIDALQUAD = 6, // Not currently implemented
++ SVGA3D_TEX_FILTER_GAUSSIANQUAD = 7, // Not currently implemented
++ SVGA3D_TEX_FILTER_MAX
++} SVGA3dTextureFilter;
++
++typedef enum {
++ SVGA3D_TEX_TRANSFORM_OFF = 0,
++ SVGA3D_TEX_TRANSFORM_S = (1 << 0),
++ SVGA3D_TEX_TRANSFORM_T = (1 << 1),
++ SVGA3D_TEX_TRANSFORM_R = (1 << 2),
++ SVGA3D_TEX_TRANSFORM_Q = (1 << 3),
++ SVGA3D_TEX_PROJECTED = (1 << 15),
++} SVGA3dTexTransformFlags;
++
++typedef enum {
++ SVGA3D_TEXCOORD_GEN_OFF = 0,
++ SVGA3D_TEXCOORD_GEN_EYE_POSITION = 1,
++ SVGA3D_TEXCOORD_GEN_EYE_NORMAL = 2,
++ SVGA3D_TEXCOORD_GEN_REFLECTIONVECTOR = 3,
++ SVGA3D_TEXCOORD_GEN_SPHERE = 4,
++ SVGA3D_TEXCOORD_GEN_MAX
++} SVGA3dTextureCoordGen;
++
++/*
++ * Texture argument constants for texture combiner
++ */
++typedef enum {
++ SVGA3D_TA_INVALID = 0,
++ SVGA3D_TA_CONSTANT = 1,
++ SVGA3D_TA_PREVIOUS = 2,
++ SVGA3D_TA_DIFFUSE = 3,
++ SVGA3D_TA_TEXTURE = 4,
++ SVGA3D_TA_SPECULAR = 5,
++ SVGA3D_TA_MAX
++} SVGA3dTextureArgData;
++
++#define SVGA3D_TM_MASK_LEN 4
++
++/* Modifiers for texture argument constants defined above. */
++typedef enum {
++ SVGA3D_TM_NONE = 0,
++ SVGA3D_TM_ALPHA = (1 << SVGA3D_TM_MASK_LEN),
++ SVGA3D_TM_ONE_MINUS = (2 << SVGA3D_TM_MASK_LEN),
++} SVGA3dTextureArgModifier;
++
++#define SVGA3D_INVALID_ID ((uint32)-1)
++#define SVGA3D_MAX_CLIP_PLANES 6
++
++/*
++ * This is the limit to the number of fixed-function texture
++ * transforms and texture coordinates we can support. It does *not*
++ * correspond to the number of texture image units (samplers) we
++ * support!
++ */
++#define SVGA3D_MAX_TEXTURE_COORDS 8
++
++/*
++ * Vertex declarations
++ *
++ * Notes:
++ *
++ * SVGA3D_DECLUSAGE_POSITIONT is for pre-transformed vertices. If you
++ * draw with any POSITIONT vertex arrays, the programmable vertex
++ * pipeline will be implicitly disabled. Drawing will take place as if
++ * no vertex shader was bound.
++ */
++
++typedef enum {
++ SVGA3D_DECLUSAGE_POSITION = 0,
++ SVGA3D_DECLUSAGE_BLENDWEIGHT, // 1
++ SVGA3D_DECLUSAGE_BLENDINDICES, // 2
++ SVGA3D_DECLUSAGE_NORMAL, // 3
++ SVGA3D_DECLUSAGE_PSIZE, // 4
++ SVGA3D_DECLUSAGE_TEXCOORD, // 5
++ SVGA3D_DECLUSAGE_TANGENT, // 6
++ SVGA3D_DECLUSAGE_BINORMAL, // 7
++ SVGA3D_DECLUSAGE_TESSFACTOR, // 8
++ SVGA3D_DECLUSAGE_POSITIONT, // 9
++ SVGA3D_DECLUSAGE_COLOR, // 10
++ SVGA3D_DECLUSAGE_FOG, // 11
++ SVGA3D_DECLUSAGE_DEPTH, // 12
++ SVGA3D_DECLUSAGE_SAMPLE, // 13
++ SVGA3D_DECLUSAGE_MAX
++} SVGA3dDeclUsage;
++
++typedef enum {
++ SVGA3D_DECLMETHOD_DEFAULT = 0,
++ SVGA3D_DECLMETHOD_PARTIALU,
++ SVGA3D_DECLMETHOD_PARTIALV,
++ SVGA3D_DECLMETHOD_CROSSUV, // Normal
++ SVGA3D_DECLMETHOD_UV,
++ SVGA3D_DECLMETHOD_LOOKUP, // Lookup a displacement map
++ SVGA3D_DECLMETHOD_LOOKUPPRESAMPLED, // Lookup a pre-sampled displacement map
++} SVGA3dDeclMethod;
++
++typedef enum {
++ SVGA3D_DECLTYPE_FLOAT1 = 0,
++ SVGA3D_DECLTYPE_FLOAT2 = 1,
++ SVGA3D_DECLTYPE_FLOAT3 = 2,
++ SVGA3D_DECLTYPE_FLOAT4 = 3,
++ SVGA3D_DECLTYPE_D3DCOLOR = 4,
++ SVGA3D_DECLTYPE_UBYTE4 = 5,
++ SVGA3D_DECLTYPE_SHORT2 = 6,
++ SVGA3D_DECLTYPE_SHORT4 = 7,
++ SVGA3D_DECLTYPE_UBYTE4N = 8,
++ SVGA3D_DECLTYPE_SHORT2N = 9,
++ SVGA3D_DECLTYPE_SHORT4N = 10,
++ SVGA3D_DECLTYPE_USHORT2N = 11,
++ SVGA3D_DECLTYPE_USHORT4N = 12,
++ SVGA3D_DECLTYPE_UDEC3 = 13,
++ SVGA3D_DECLTYPE_DEC3N = 14,
++ SVGA3D_DECLTYPE_FLOAT16_2 = 15,
++ SVGA3D_DECLTYPE_FLOAT16_4 = 16,
++ SVGA3D_DECLTYPE_MAX,
++} SVGA3dDeclType;
++
++/*
++ * This structure is used for the divisor for geometry instancing;
++ * it's a direct translation of the Direct3D equivalent.
++ */
++typedef union {
++ struct {
++ /*
++ * For index data, this number represents the number of instances to draw.
++ * For instance data, this number represents the number of
++ * instances/vertex in this stream
++ */
++ uint32 count : 30;
++
++ /*
++ * This is 1 if this is supposed to be the data that is repeated for
++ * every instance.
++ */
++ uint32 indexedData : 1;
++
++ /*
++ * This is 1 if this is supposed to be the per-instance data.
++ */
++ uint32 instanceData : 1;
++ };
++
++ uint32 value;
++} SVGA3dVertexDivisor;
++
++typedef enum {
++ SVGA3D_PRIMITIVE_INVALID = 0,
++ SVGA3D_PRIMITIVE_TRIANGLELIST = 1,
++ SVGA3D_PRIMITIVE_POINTLIST = 2,
++ SVGA3D_PRIMITIVE_LINELIST = 3,
++ SVGA3D_PRIMITIVE_LINESTRIP = 4,
++ SVGA3D_PRIMITIVE_TRIANGLESTRIP = 5,
++ SVGA3D_PRIMITIVE_TRIANGLEFAN = 6,
++ SVGA3D_PRIMITIVE_MAX
++} SVGA3dPrimitiveType;
++
++typedef enum {
++ SVGA3D_COORDINATE_INVALID = 0,
++ SVGA3D_COORDINATE_LEFTHANDED = 1,
++ SVGA3D_COORDINATE_RIGHTHANDED = 2,
++ SVGA3D_COORDINATE_MAX
++} SVGA3dCoordinateType;
++
++typedef enum {
++ SVGA3D_TRANSFORM_INVALID = 0,
++ SVGA3D_TRANSFORM_WORLD = 1,
++ SVGA3D_TRANSFORM_VIEW = 2,
++ SVGA3D_TRANSFORM_PROJECTION = 3,
++ SVGA3D_TRANSFORM_TEXTURE0 = 4,
++ SVGA3D_TRANSFORM_TEXTURE1 = 5,
++ SVGA3D_TRANSFORM_TEXTURE2 = 6,
++ SVGA3D_TRANSFORM_TEXTURE3 = 7,
++ SVGA3D_TRANSFORM_TEXTURE4 = 8,
++ SVGA3D_TRANSFORM_TEXTURE5 = 9,
++ SVGA3D_TRANSFORM_TEXTURE6 = 10,
++ SVGA3D_TRANSFORM_TEXTURE7 = 11,
++ SVGA3D_TRANSFORM_WORLD1 = 12,
++ SVGA3D_TRANSFORM_WORLD2 = 13,
++ SVGA3D_TRANSFORM_WORLD3 = 14,
++ SVGA3D_TRANSFORM_MAX
++} SVGA3dTransformType;
++
++typedef enum {
++ SVGA3D_LIGHTTYPE_INVALID = 0,
++ SVGA3D_LIGHTTYPE_POINT = 1,
++ SVGA3D_LIGHTTYPE_SPOT1 = 2, /* 1-cone, in degrees */
++ SVGA3D_LIGHTTYPE_SPOT2 = 3, /* 2-cone, in radians */
++ SVGA3D_LIGHTTYPE_DIRECTIONAL = 4,
++ SVGA3D_LIGHTTYPE_MAX
++} SVGA3dLightType;
++
++typedef enum {
++ SVGA3D_CUBEFACE_POSX = 0,
++ SVGA3D_CUBEFACE_NEGX = 1,
++ SVGA3D_CUBEFACE_POSY = 2,
++ SVGA3D_CUBEFACE_NEGY = 3,
++ SVGA3D_CUBEFACE_POSZ = 4,
++ SVGA3D_CUBEFACE_NEGZ = 5,
++} SVGA3dCubeFace;
++
++typedef enum {
++ SVGA3D_SHADERTYPE_COMPILED_DX8 = 0,
++ SVGA3D_SHADERTYPE_VS = 1,
++ SVGA3D_SHADERTYPE_PS = 2,
++ SVGA3D_SHADERTYPE_MAX
++} SVGA3dShaderType;
++
++typedef enum {
++ SVGA3D_CONST_TYPE_FLOAT = 0,
++ SVGA3D_CONST_TYPE_INT = 1,
++ SVGA3D_CONST_TYPE_BOOL = 2,
++} SVGA3dShaderConstType;
++
++#define SVGA3D_MAX_SURFACE_FACES 6
++
++typedef enum {
++ SVGA3D_STRETCH_BLT_POINT = 0,
++ SVGA3D_STRETCH_BLT_LINEAR = 1,
++ SVGA3D_STRETCH_BLT_MAX
++} SVGA3dStretchBltMode;
++
++typedef enum {
++ SVGA3D_QUERYTYPE_OCCLUSION = 0,
++ SVGA3D_QUERYTYPE_MAX
++} SVGA3dQueryType;
++
++typedef enum {
++ SVGA3D_QUERYSTATE_PENDING = 0, /* Waiting on the host (set by guest) */
++ SVGA3D_QUERYSTATE_SUCCEEDED = 1, /* Completed successfully (set by host) */
++ SVGA3D_QUERYSTATE_FAILED = 2, /* Completed unsuccessfully (set by host) */
++ SVGA3D_QUERYSTATE_NEW = 3, /* Never submitted (For guest use only) */
++} SVGA3dQueryState;
++
++typedef enum {
++ SVGA3D_WRITE_HOST_VRAM = 1,
++ SVGA3D_READ_HOST_VRAM = 2,
++} SVGA3dTransferType;
++
++/*
++ * The maximum number vertex arrays we're guaranteed to support in
++ * SVGA_3D_CMD_DRAWPRIMITIVES.
++ */
++#define SVGA3D_MAX_VERTEX_ARRAYS 32
++
++/*
++ * Identifiers for commands in the command FIFO.
++ *
++ * IDs between 1000 and 1039 (inclusive) were used by obsolete versions of
++ * the SVGA3D protocol and remain reserved; they should not be used in the
++ * future.
++ *
++ * IDs between 1040 and 1999 (inclusive) are available for use by the
++ * current SVGA3D protocol.
++ *
++ * FIFO clients other than SVGA3D should stay below 1000, or at 2000
++ * and up.
++ */
++
++#define SVGA_3D_CMD_LEGACY_BASE 1000
++#define SVGA_3D_CMD_BASE 1040
++
++#define SVGA_3D_CMD_SURFACE_DEFINE SVGA_3D_CMD_BASE + 0
++#define SVGA_3D_CMD_SURFACE_DESTROY SVGA_3D_CMD_BASE + 1
++#define SVGA_3D_CMD_SURFACE_COPY SVGA_3D_CMD_BASE + 2
++#define SVGA_3D_CMD_SURFACE_STRETCHBLT SVGA_3D_CMD_BASE + 3
++#define SVGA_3D_CMD_SURFACE_DMA SVGA_3D_CMD_BASE + 4
++#define SVGA_3D_CMD_CONTEXT_DEFINE SVGA_3D_CMD_BASE + 5
++#define SVGA_3D_CMD_CONTEXT_DESTROY SVGA_3D_CMD_BASE + 6
++#define SVGA_3D_CMD_SETTRANSFORM SVGA_3D_CMD_BASE + 7
++#define SVGA_3D_CMD_SETZRANGE SVGA_3D_CMD_BASE + 8
++#define SVGA_3D_CMD_SETRENDERSTATE SVGA_3D_CMD_BASE + 9
++#define SVGA_3D_CMD_SETRENDERTARGET SVGA_3D_CMD_BASE + 10
++#define SVGA_3D_CMD_SETTEXTURESTATE SVGA_3D_CMD_BASE + 11
++#define SVGA_3D_CMD_SETMATERIAL SVGA_3D_CMD_BASE + 12
++#define SVGA_3D_CMD_SETLIGHTDATA SVGA_3D_CMD_BASE + 13
++#define SVGA_3D_CMD_SETLIGHTENABLED SVGA_3D_CMD_BASE + 14
++#define SVGA_3D_CMD_SETVIEWPORT SVGA_3D_CMD_BASE + 15
++#define SVGA_3D_CMD_SETCLIPPLANE SVGA_3D_CMD_BASE + 16
++#define SVGA_3D_CMD_CLEAR SVGA_3D_CMD_BASE + 17
++#define SVGA_3D_CMD_PRESENT SVGA_3D_CMD_BASE + 18 // Deprecated
++#define SVGA_3D_CMD_SHADER_DEFINE SVGA_3D_CMD_BASE + 19
++#define SVGA_3D_CMD_SHADER_DESTROY SVGA_3D_CMD_BASE + 20
++#define SVGA_3D_CMD_SET_SHADER SVGA_3D_CMD_BASE + 21
++#define SVGA_3D_CMD_SET_SHADER_CONST SVGA_3D_CMD_BASE + 22
++#define SVGA_3D_CMD_DRAW_PRIMITIVES SVGA_3D_CMD_BASE + 23
++#define SVGA_3D_CMD_SETSCISSORRECT SVGA_3D_CMD_BASE + 24
++#define SVGA_3D_CMD_BEGIN_QUERY SVGA_3D_CMD_BASE + 25
++#define SVGA_3D_CMD_END_QUERY SVGA_3D_CMD_BASE + 26
++#define SVGA_3D_CMD_WAIT_FOR_QUERY SVGA_3D_CMD_BASE + 27
++#define SVGA_3D_CMD_PRESENT_READBACK SVGA_3D_CMD_BASE + 28 // Deprecated
++#define SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN SVGA_3D_CMD_BASE + 29
++#define SVGA_3D_CMD_MAX SVGA_3D_CMD_BASE + 30
++
++#define SVGA_3D_CMD_FUTURE_MAX 2000
++
++/*
++ * Common substructures used in multiple FIFO commands:
++ */
++
++typedef struct {
++ union {
++ struct {
++ uint16 function; // SVGA3dFogFunction
++ uint8 type; // SVGA3dFogType
++ uint8 base; // SVGA3dFogBase
++ };
++ uint32 uintValue;
++ };
++} SVGA3dFogMode;
++
++/*
++ * Uniquely identify one image (a 1D/2D/3D array) from a surface. This
++ * is a surface ID as well as face/mipmap indices.
++ */
++
++typedef
++struct SVGA3dSurfaceImageId {
++ uint32 sid;
++ uint32 face;
++ uint32 mipmap;
++} SVGA3dSurfaceImageId;
++
++typedef
++struct SVGA3dGuestImage {
++ SVGAGuestPtr ptr;
++
++ /*
++ * A note on interpretation of pitch: This value of pitch is the
++ * number of bytes between vertically adjacent image
++ * blocks. Normally this is the number of bytes between the first
++ * pixel of two adjacent scanlines. With compressed textures,
++ * however, this may represent the number of bytes between
++ * compression blocks rather than between rows of pixels.
++ *
++ * XXX: Compressed textures currently must be tightly packed in guest memory.
++ *
++ * If the image is 1-dimensional, pitch is ignored.
++ *
++ * If 'pitch' is zero, the SVGA3D device calculates a pitch value
++ * assuming each row of blocks is tightly packed.
++ */
++ uint32 pitch;
++} SVGA3dGuestImage;
++
++
++/*
++ * FIFO command format definitions:
++ */
++
++/*
++ * The data size header following cmdNum for every 3d command
++ */
++typedef
++struct {
++ uint32 id;
++ uint32 size;
++} SVGA3dCmdHeader;
++
++/*
++ * A surface is a hierarchy of host VRAM surfaces: 1D, 2D, or 3D, with
++ * optional mipmaps and cube faces.
++ */
++
++typedef
++struct {
++ uint32 width;
++ uint32 height;
++ uint32 depth;
++} SVGA3dSize;
++
++typedef enum {
++ SVGA3D_SURFACE_CUBEMAP = (1 << 0),
++ SVGA3D_SURFACE_HINT_STATIC = (1 << 1),
++ SVGA3D_SURFACE_HINT_DYNAMIC = (1 << 2),
++ SVGA3D_SURFACE_HINT_INDEXBUFFER = (1 << 3),
++ SVGA3D_SURFACE_HINT_VERTEXBUFFER = (1 << 4),
++ SVGA3D_SURFACE_HINT_TEXTURE = (1 << 5),
++ SVGA3D_SURFACE_HINT_RENDERTARGET = (1 << 6),
++ SVGA3D_SURFACE_HINT_DEPTHSTENCIL = (1 << 7),
++ SVGA3D_SURFACE_HINT_WRITEONLY = (1 << 8),
++} SVGA3dSurfaceFlags;
++
++typedef
++struct {
++ uint32 numMipLevels;
++} SVGA3dSurfaceFace;
++
++typedef
++struct {
++ uint32 sid;
++ SVGA3dSurfaceFlags surfaceFlags;
++ SVGA3dSurfaceFormat format;
++ SVGA3dSurfaceFace face[SVGA3D_MAX_SURFACE_FACES];
++ /*
++ * Followed by an SVGA3dSize structure for each mip level in each face.
++ *
++ * A note on surface sizes: Sizes are always specified in pixels,
++ * even if the true surface size is not a multiple of the minimum
++ * block size of the surface's format. For example, a 3x3x1 DXT1
++ * compressed texture would actually be stored as a 4x4x1 image in
++ * memory.
++ */
++} SVGA3dCmdDefineSurface; /* SVGA_3D_CMD_SURFACE_DEFINE */
++
++typedef
++struct {
++ uint32 sid;
++} SVGA3dCmdDestroySurface; /* SVGA_3D_CMD_SURFACE_DESTROY */
++
++typedef
++struct {
++ uint32 cid;
++} SVGA3dCmdDefineContext; /* SVGA_3D_CMD_CONTEXT_DEFINE */
++
++typedef
++struct {
++ uint32 cid;
++} SVGA3dCmdDestroyContext; /* SVGA_3D_CMD_CONTEXT_DESTROY */
++
++typedef
++struct {
++ uint32 cid;
++ SVGA3dClearFlag clearFlag;
++ uint32 color;
++ float depth;
++ uint32 stencil;
++ /* Followed by variable number of SVGA3dRect structures */
++} SVGA3dCmdClear; /* SVGA_3D_CMD_CLEAR */
++
++typedef
++struct SVGA3dCopyRect {
++ uint32 x;
++ uint32 y;
++ uint32 w;
++ uint32 h;
++ uint32 srcx;
++ uint32 srcy;
++} SVGA3dCopyRect;
++
++typedef
++struct SVGA3dCopyBox {
++ uint32 x;
++ uint32 y;
++ uint32 z;
++ uint32 w;
++ uint32 h;
++ uint32 d;
++ uint32 srcx;
++ uint32 srcy;
++ uint32 srcz;
++} SVGA3dCopyBox;
++
++typedef
++struct {
++ uint32 x;
++ uint32 y;
++ uint32 w;
++ uint32 h;
++} SVGA3dRect;
++
++typedef
++struct {
++ uint32 x;
++ uint32 y;
++ uint32 z;
++ uint32 w;
++ uint32 h;
++ uint32 d;
++} SVGA3dBox;
++
++typedef
++struct {
++ uint32 x;
++ uint32 y;
++ uint32 z;
++} SVGA3dPoint;
++
++typedef
++struct {
++ SVGA3dLightType type;
++ SVGA3dBool inWorldSpace;
++ float diffuse[4];
++ float specular[4];
++ float ambient[4];
++ float position[4];
++ float direction[4];
++ float range;
++ float falloff;
++ float attenuation0;
++ float attenuation1;
++ float attenuation2;
++ float theta;
++ float phi;
++} SVGA3dLightData;
++
++typedef
++struct {
++ uint32 sid;
++ /* Followed by variable number of SVGA3dCopyRect structures */
++} SVGA3dCmdPresent; /* SVGA_3D_CMD_PRESENT */
++
++typedef
++struct {
++ SVGA3dRenderStateName state;
++ union {
++ uint32 uintValue;
++ float floatValue;
++ };
++} SVGA3dRenderState;
++
++typedef
++struct {
++ uint32 cid;
++ /* Followed by variable number of SVGA3dRenderState structures */
++} SVGA3dCmdSetRenderState; /* SVGA_3D_CMD_SETRENDERSTATE */
++
++typedef
++struct {
++ uint32 cid;
++ SVGA3dRenderTargetType type;
++ SVGA3dSurfaceImageId target;
++} SVGA3dCmdSetRenderTarget; /* SVGA_3D_CMD_SETRENDERTARGET */
++
++typedef
++struct {
++ SVGA3dSurfaceImageId src;
++ SVGA3dSurfaceImageId dest;
++ /* Followed by variable number of SVGA3dCopyBox structures */
++} SVGA3dCmdSurfaceCopy; /* SVGA_3D_CMD_SURFACE_COPY */
++
++typedef
++struct {
++ SVGA3dSurfaceImageId src;
++ SVGA3dSurfaceImageId dest;
++ SVGA3dBox boxSrc;
++ SVGA3dBox boxDest;
++ SVGA3dStretchBltMode mode;
++} SVGA3dCmdSurfaceStretchBlt; /* SVGA_3D_CMD_SURFACE_STRETCHBLT */
++
++typedef
++struct {
++ /*
++ * If the discard flag is present in a surface DMA operation, the host may
++ * discard the contents of the current mipmap level and face of the target
++ * surface before applying the surface DMA contents.
++ */
++ uint32 discard : 1;
++
++ /*
++ * If the unsynchronized flag is present, the host may perform this upload
++ * without syncing to pending reads on this surface.
++ */
++ uint32 unsynchronized : 1;
++
++ /*
++ * Guests *MUST* set the reserved bits to 0 before submitting the command
++ * suffix as future flags may occupy these bits.
++ */
++ uint32 reserved : 30;
++} SVGA3dSurfaceDMAFlags;
++
++typedef
++struct {
++ SVGA3dGuestImage guest;
++ SVGA3dSurfaceImageId host;
++ SVGA3dTransferType transfer;
++ /*
++ * Followed by variable number of SVGA3dCopyBox structures. For consistency
++ * in all clipping logic and coordinate translation, we define the
++ * "source" in each copyBox as the guest image and the
++ * "destination" as the host image, regardless of transfer
++ * direction.
++ *
++ * For efficiency, the SVGA3D device is free to copy more data than
++ * specified. For example, it may round copy boxes outwards such
++ * that they lie on particular alignment boundaries.
++ */
++} SVGA3dCmdSurfaceDMA; /* SVGA_3D_CMD_SURFACE_DMA */
++
++/*
++ * SVGA3dCmdSurfaceDMASuffix --
++ *
++ * This is a command suffix that will appear after a SurfaceDMA command in
++ * the FIFO. It contains some extra information that hosts may use to
++ * optimize performance or protect the guest. This suffix exists to preserve
++ * backwards compatibility while also allowing for new functionality to be
++ * implemented.
++ */
++
++typedef
++struct {
++ uint32 suffixSize;
++
++ /*
++ * The maximum offset is used to determine the maximum offset from the
++ * guestPtr base address that will be accessed or written to during this
++ * surfaceDMA. If the suffix is supported, the host will respect this
++ * boundary while performing surface DMAs.
++ *
++ * Defaults to MAX_UINT32
++ */
++ uint32 maximumOffset;
++
++ /*
++ * A set of flags that describes optimizations that the host may perform
++ * while performing this surface DMA operation. The guest should never rely
++ * on behaviour that is different when these flags are set for correctness.
++ *
++ * Defaults to 0
++ */
++ SVGA3dSurfaceDMAFlags flags;
++} SVGA3dCmdSurfaceDMASuffix;
++
++/*
++ * SVGA_3D_CMD_DRAW_PRIMITIVES --
++ *
++ * This command is the SVGA3D device's generic drawing entry point.
++ * It can draw multiple ranges of primitives, optionally using an
++ * index buffer, using an arbitrary collection of vertex buffers.
++ *
++ * Each SVGA3dVertexDecl defines a distinct vertex array to bind
++ * during this draw call. The declarations specify which surface
++ * the vertex data lives in, what that vertex data is used for,
++ * and how to interpret it.
++ *
++ * Each SVGA3dPrimitiveRange defines a collection of primitives
++ * to render using the same vertex arrays. An index buffer is
++ * optional.
++ */
++
++typedef
++struct {
++ /*
++ * A range hint is an optional specification for the range of indices
++ * in an SVGA3dArray that will be used. If 'last' is zero, it is assumed
++ * that the entire array will be used.
++ *
++ * These are only hints. The SVGA3D device may use them for
++ * performance optimization if possible, but it's also allowed to
++ * ignore these values.
++ */
++ uint32 first;
++ uint32 last;
++} SVGA3dArrayRangeHint;
++
++typedef
++struct {
++ /*
++ * Define the origin and shape of a vertex or index array. Both
++ * 'offset' and 'stride' are in bytes. The provided surface will be
++ * reinterpreted as a flat array of bytes in the same format used
++ * by surface DMA operations. To avoid unnecessary conversions, the
++ * surface should be created with the SVGA3D_BUFFER format.
++ *
++ * Index 0 in the array starts 'offset' bytes into the surface.
++ * Index 1 begins at byte 'offset + stride', etc. Array indices may
++ * not be negative.
++ */
++ uint32 surfaceId;
++ uint32 offset;
++ uint32 stride;
++} SVGA3dArray;
++
++typedef
++struct {
++ /*
++ * Describe a vertex array's data type, and define how it is to be
++ * used by the fixed function pipeline or the vertex shader. It
++ * isn't useful to have two VertexDecls with the same
++ * VertexArrayIdentity in one draw call.
++ */
++ SVGA3dDeclType type;
++ SVGA3dDeclMethod method;
++ SVGA3dDeclUsage usage;
++ uint32 usageIndex;
++} SVGA3dVertexArrayIdentity;
++
++typedef
++struct {
++ SVGA3dVertexArrayIdentity identity;
++ SVGA3dArray array;
++ SVGA3dArrayRangeHint rangeHint;
++} SVGA3dVertexDecl;
++
++typedef
++struct {
++ /*
++ * Define a group of primitives to render, from sequential indices.
++ *
++ * The value of 'primitiveType' and 'primitiveCount' imply the
++ * total number of vertices that will be rendered.
++ */
++ SVGA3dPrimitiveType primType;
++ uint32 primitiveCount;
++
++ /*
++ * Optional index buffer. If indexArray.surfaceId is
++ * SVGA3D_INVALID_ID, we render without an index buffer. Rendering
++ * without an index buffer is identical to rendering with an index
++ * buffer containing the sequence [0, 1, 2, 3, ...].
++ *
++ * If an index buffer is in use, indexWidth specifies the width in
++ * bytes of each index value. It must be less than or equal to
++ * indexArray.stride.
++ *
++ * (Currently, the SVGA3D device requires index buffers to be tightly
++ * packed. In other words, indexWidth == indexArray.stride)
++ */
++ SVGA3dArray indexArray;
++ uint32 indexWidth;
++
++ /*
++ * Optional index bias. This number is added to all indices from
++ * indexArray before they are used as vertex array indices. This
++ * can be used in multiple ways:
++ *
++ * - When not using an indexArray, this bias can be used to
++ * specify where in the vertex arrays to begin rendering.
++ *
++ * - A positive number here is equivalent to increasing the
++ * offset in each vertex array.
++ *
++ * - A negative number can be used to render using a small
++ * vertex array and an index buffer that contains large
++ * values. This may be used by some applications that
++ * crop a vertex buffer without modifying their index
++ * buffer.
++ *
++ * Note that rendering with a negative bias value may be slower and
++ * use more memory than rendering with a positive or zero bias.
++ */
++ int32 indexBias;
++} SVGA3dPrimitiveRange;
++
++typedef
++struct {
++ uint32 cid;
++ uint32 numVertexDecls;
++ uint32 numRanges;
++
++ /*
++ * There are two variable size arrays after the
++ * SVGA3dCmdDrawPrimitives structure. In order,
++ * they are:
++ *
++ * 1. SVGA3dVertexDecl, quantity 'numVertexDecls'
++ * 2. SVGA3dPrimitiveRange, quantity 'numRanges'
++ * 3. Optionally, SVGA3dVertexDivisor, quantity 'numVertexDecls' (contains
++ * the frequency divisor for this the corresponding vertex decl)
++ */
++} SVGA3dCmdDrawPrimitives; /* SVGA_3D_CMD_DRAWPRIMITIVES */
++
++typedef
++struct {
++ uint32 stage;
++ SVGA3dTextureStateName name;
++ union {
++ uint32 value;
++ float floatValue;
++ };
++} SVGA3dTextureState;
++
++typedef
++struct {
++ uint32 cid;
++ /* Followed by variable number of SVGA3dTextureState structures */
++} SVGA3dCmdSetTextureState; /* SVGA_3D_CMD_SETTEXTURESTATE */
++
++typedef
++struct {
++ uint32 cid;
++ SVGA3dTransformType type;
++ float matrix[16];
++} SVGA3dCmdSetTransform; /* SVGA_3D_CMD_SETTRANSFORM */
++
++typedef
++struct {
++ float min;
++ float max;
++} SVGA3dZRange;
++
++typedef
++struct {
++ uint32 cid;
++ SVGA3dZRange zRange;
++} SVGA3dCmdSetZRange; /* SVGA_3D_CMD_SETZRANGE */
++
++typedef
++struct {
++ float diffuse[4];
++ float ambient[4];
++ float specular[4];
++ float emissive[4];
++ float shininess;
++} SVGA3dMaterial;
++
++typedef
++struct {
++ uint32 cid;
++ SVGA3dFace face;
++ SVGA3dMaterial material;
++} SVGA3dCmdSetMaterial; /* SVGA_3D_CMD_SETMATERIAL */
++
++typedef
++struct {
++ uint32 cid;
++ uint32 index;
++ SVGA3dLightData data;
++} SVGA3dCmdSetLightData; /* SVGA_3D_CMD_SETLIGHTDATA */
++
++typedef
++struct {
++ uint32 cid;
++ uint32 index;
++ uint32 enabled;
++} SVGA3dCmdSetLightEnabled; /* SVGA_3D_CMD_SETLIGHTENABLED */
++
++typedef
++struct {
++ uint32 cid;
++ SVGA3dRect rect;
++} SVGA3dCmdSetViewport; /* SVGA_3D_CMD_SETVIEWPORT */
++
++typedef
++struct {
++ uint32 cid;
++ SVGA3dRect rect;
++} SVGA3dCmdSetScissorRect; /* SVGA_3D_CMD_SETSCISSORRECT */
++
++typedef
++struct {
++ uint32 cid;
++ uint32 index;
++ float plane[4];
++} SVGA3dCmdSetClipPlane; /* SVGA_3D_CMD_SETCLIPPLANE */
++
++typedef
++struct {
++ uint32 cid;
++ uint32 shid;
++ SVGA3dShaderType type;
++ /* Followed by variable number of DWORDs for shader bycode */
++} SVGA3dCmdDefineShader; /* SVGA_3D_CMD_SHADER_DEFINE */
++
++typedef
++struct {
++ uint32 cid;
++ uint32 shid;
++ SVGA3dShaderType type;
++} SVGA3dCmdDestroyShader; /* SVGA_3D_CMD_SHADER_DESTROY */
++
++typedef
++struct {
++ uint32 cid;
++ uint32 reg; /* register number */
++ SVGA3dShaderType type;
++ SVGA3dShaderConstType ctype;
++ uint32 values[4];
++} SVGA3dCmdSetShaderConst; /* SVGA_3D_CMD_SET_SHADER_CONST */
++
++typedef
++struct {
++ uint32 cid;
++ SVGA3dShaderType type;
++ uint32 shid;
++} SVGA3dCmdSetShader; /* SVGA_3D_CMD_SET_SHADER */
++
++typedef
++struct {
++ uint32 cid;
++ SVGA3dQueryType type;
++} SVGA3dCmdBeginQuery; /* SVGA_3D_CMD_BEGIN_QUERY */
++
++typedef
++struct {
++ uint32 cid;
++ SVGA3dQueryType type;
++ SVGAGuestPtr guestResult; /* Points to an SVGA3dQueryResult structure */
++} SVGA3dCmdEndQuery; /* SVGA_3D_CMD_END_QUERY */
++
++typedef
++struct {
++ uint32 cid; /* Same parameters passed to END_QUERY */
++ SVGA3dQueryType type;
++ SVGAGuestPtr guestResult;
++} SVGA3dCmdWaitForQuery; /* SVGA_3D_CMD_WAIT_FOR_QUERY */
++
++typedef
++struct {
++ uint32 totalSize; /* Set by guest before query is ended. */
++ SVGA3dQueryState state; /* Set by host or guest. See SVGA3dQueryState. */
++ union { /* Set by host on exit from PENDING state */
++ uint32 result32;
++ };
++} SVGA3dQueryResult;
++
++/*
++ * SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN --
++ *
++ * This is a blit from an SVGA3D surface to a Screen Object. Just
++ * like GMR-to-screen blits, this blit may be directed at a
++ * specific screen or to the virtual coordinate space.
++ *
++ * The blit copies from a rectangular region of an SVGA3D surface
++ * image to a rectangular region of a screen or screens.
++ *
++ * This command takes an optional variable-length list of clipping
++ * rectangles after the body of the command. If no rectangles are
++ * specified, there is no clipping region. The entire destRect is
++ * drawn to. If one or more rectangles are included, they describe
++ * a clipping region. The clip rectangle coordinates are measured
++ * relative to the top-left corner of destRect.
++ *
++ * This clipping region serves multiple purposes:
++ *
++ * - It can be used to perform an irregularly shaped blit more
++ * efficiently than by issuing many separate blit commands.
++ *
++ * - It is equivalent to allowing blits with non-integer
++ * source coordinates. You could blit just one half-pixel
++ * of a source, for example, by specifying a larger
++ * destination rectangle than you need, then removing
++ * part of it using a clip rectangle.
++ *
++ * Availability:
++ * SVGA_FIFO_CAP_SCREEN_OBJECT
++ *
++ * Limitations:
++ *
++ * - Currently, no backend supports blits from a mipmap or face
++ * other than the first one.
++ */
++
++typedef
++struct {
++ SVGA3dSurfaceImageId srcImage;
++ SVGASignedRect srcRect;
++ uint32 destScreenId; /* Screen ID or SVGA_ID_INVALID for virt. coords */
++ SVGASignedRect destRect; /* Supports scaling if src/rest different size */
++ /* Clipping: zero or more SVGASignedRects follow */
++} SVGA3dCmdBlitSurfaceToScreen; /* SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN */
++
++
++/*
++ * Capability query index.
++ *
++ * Notes:
++ *
++ * 1. SVGA3D_DEVCAP_MAX_TEXTURES reflects the maximum number of
++ * fixed-function texture units available. Each of these units
++ * work in both FFP and Shader modes, and they support texture
++ * transforms and texture coordinates. The host may have additional
++ * texture image units that are only usable with shaders.
++ *
++ * 2. The BUFFER_FORMAT capabilities are deprecated, and they always
++ * return TRUE. Even on physical hardware that does not support
++ * these formats natively, the SVGA3D device will provide an emulation
++ * which should be invisible to the guest OS.
++ *
++ * In general, the SVGA3D device should support any operation on
++ * any surface format, it just may perform some of these
++ * operations in software depending on the capabilities of the
++ * available physical hardware.
++ *
++ * XXX: In the future, we will add capabilities that describe in
++ * detail what formats are supported in hardware for what kinds
++ * of operations.
++ */
++
++typedef enum {
++ SVGA3D_DEVCAP_3D = 0,
++ SVGA3D_DEVCAP_MAX_LIGHTS = 1,
++ SVGA3D_DEVCAP_MAX_TEXTURES = 2, /* See note (1) */
++ SVGA3D_DEVCAP_MAX_CLIP_PLANES = 3,
++ SVGA3D_DEVCAP_VERTEX_SHADER_VERSION = 4,
++ SVGA3D_DEVCAP_VERTEX_SHADER = 5,
++ SVGA3D_DEVCAP_FRAGMENT_SHADER_VERSION = 6,
++ SVGA3D_DEVCAP_FRAGMENT_SHADER = 7,
++ SVGA3D_DEVCAP_MAX_RENDER_TARGETS = 8,
++ SVGA3D_DEVCAP_S23E8_TEXTURES = 9,
++ SVGA3D_DEVCAP_S10E5_TEXTURES = 10,
++ SVGA3D_DEVCAP_MAX_FIXED_VERTEXBLEND = 11,
++ SVGA3D_DEVCAP_D16_BUFFER_FORMAT = 12, /* See note (2) */
++ SVGA3D_DEVCAP_D24S8_BUFFER_FORMAT = 13, /* See note (2) */
++ SVGA3D_DEVCAP_D24X8_BUFFER_FORMAT = 14, /* See note (2) */
++ SVGA3D_DEVCAP_QUERY_TYPES = 15,
++ SVGA3D_DEVCAP_TEXTURE_GRADIENT_SAMPLING = 16,
++ SVGA3D_DEVCAP_MAX_POINT_SIZE = 17,
++ SVGA3D_DEVCAP_MAX_SHADER_TEXTURES = 18,
++ SVGA3D_DEVCAP_MAX_TEXTURE_WIDTH = 19,
++ SVGA3D_DEVCAP_MAX_TEXTURE_HEIGHT = 20,
++ SVGA3D_DEVCAP_MAX_VOLUME_EXTENT = 21,
++ SVGA3D_DEVCAP_MAX_TEXTURE_REPEAT = 22,
++ SVGA3D_DEVCAP_MAX_TEXTURE_ASPECT_RATIO = 23,
++ SVGA3D_DEVCAP_MAX_TEXTURE_ANISOTROPY = 24,
++ SVGA3D_DEVCAP_MAX_PRIMITIVE_COUNT = 25,
++ SVGA3D_DEVCAP_MAX_VERTEX_INDEX = 26,
++ SVGA3D_DEVCAP_MAX_VERTEX_SHADER_INSTRUCTIONS = 27,
++ SVGA3D_DEVCAP_MAX_FRAGMENT_SHADER_INSTRUCTIONS = 28,
++ SVGA3D_DEVCAP_MAX_VERTEX_SHADER_TEMPS = 29,
++ SVGA3D_DEVCAP_MAX_FRAGMENT_SHADER_TEMPS = 30,
++ SVGA3D_DEVCAP_TEXTURE_OPS = 31,
++ SVGA3D_DEVCAP_SURFACEFMT_X8R8G8B8 = 32,
++ SVGA3D_DEVCAP_SURFACEFMT_A8R8G8B8 = 33,
++ SVGA3D_DEVCAP_SURFACEFMT_A2R10G10B10 = 34,
++ SVGA3D_DEVCAP_SURFACEFMT_X1R5G5B5 = 35,
++ SVGA3D_DEVCAP_SURFACEFMT_A1R5G5B5 = 36,
++ SVGA3D_DEVCAP_SURFACEFMT_A4R4G4B4 = 37,
++ SVGA3D_DEVCAP_SURFACEFMT_R5G6B5 = 38,
++ SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE16 = 39,
++ SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE8_ALPHA8 = 40,
++ SVGA3D_DEVCAP_SURFACEFMT_ALPHA8 = 41,
++ SVGA3D_DEVCAP_SURFACEFMT_LUMINANCE8 = 42,
++ SVGA3D_DEVCAP_SURFACEFMT_Z_D16 = 43,
++ SVGA3D_DEVCAP_SURFACEFMT_Z_D24S8 = 44,
++ SVGA3D_DEVCAP_SURFACEFMT_Z_D24X8 = 45,
++ SVGA3D_DEVCAP_SURFACEFMT_DXT1 = 46,
++ SVGA3D_DEVCAP_SURFACEFMT_DXT2 = 47,
++ SVGA3D_DEVCAP_SURFACEFMT_DXT3 = 48,
++ SVGA3D_DEVCAP_SURFACEFMT_DXT4 = 49,
++ SVGA3D_DEVCAP_SURFACEFMT_DXT5 = 50,
++ SVGA3D_DEVCAP_SURFACEFMT_BUMPX8L8V8U8 = 51,
++ SVGA3D_DEVCAP_SURFACEFMT_A2W10V10U10 = 52,
++ SVGA3D_DEVCAP_SURFACEFMT_BUMPU8V8 = 53,
++ SVGA3D_DEVCAP_SURFACEFMT_Q8W8V8U8 = 54,
++ SVGA3D_DEVCAP_SURFACEFMT_CxV8U8 = 55,
++ SVGA3D_DEVCAP_SURFACEFMT_R_S10E5 = 56,
++ SVGA3D_DEVCAP_SURFACEFMT_R_S23E8 = 57,
++ SVGA3D_DEVCAP_SURFACEFMT_RG_S10E5 = 58,
++ SVGA3D_DEVCAP_SURFACEFMT_RG_S23E8 = 59,
++ SVGA3D_DEVCAP_SURFACEFMT_ARGB_S10E5 = 60,
++ SVGA3D_DEVCAP_SURFACEFMT_ARGB_S23E8 = 61,
++ SVGA3D_DEVCAP_MAX_VERTEX_SHADER_TEXTURES = 63,
++
++ /*
++ * Note that MAX_SIMULTANEOUS_RENDER_TARGETS is a maximum count of color
++ * render targets. This does no include the depth or stencil targets.
++ */
++ SVGA3D_DEVCAP_MAX_SIMULTANEOUS_RENDER_TARGETS = 64,
++
++ SVGA3D_DEVCAP_SURFACEFMT_V16U16 = 65,
++ SVGA3D_DEVCAP_SURFACEFMT_G16R16 = 66,
++ SVGA3D_DEVCAP_SURFACEFMT_A16B16G16R16 = 67,
++ SVGA3D_DEVCAP_SURFACEFMT_UYVY = 68,
++ SVGA3D_DEVCAP_SURFACEFMT_YUY2 = 69,
++
++ /*
++ * Don't add new caps into the previous section; the values in this
++ * enumeration must not change. You can put new values right before
++ * SVGA3D_DEVCAP_MAX.
++ */
++ SVGA3D_DEVCAP_MAX /* This must be the last index. */
++} SVGA3dDevCapIndex;
++
++typedef union {
++ Bool b;
++ uint32 u;
++ int32 i;
++ float f;
++} SVGA3dDevCapResult;
++
++#endif /* _SVGA3D_REG_H_ */
+diff --git a/drivers/gpu/drm/vmwgfx/svga_escape.h b/drivers/gpu/drm/vmwgfx/svga_escape.h
+new file mode 100644
+index 0000000..7b85e9b
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/svga_escape.h
+@@ -0,0 +1,89 @@
++/**********************************************************
++ * Copyright 2007-2009 VMware, Inc. All rights reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person
++ * obtaining a copy of this software and associated documentation
++ * files (the "Software"), to deal in the Software without
++ * restriction, including without limitation the rights to use, copy,
++ * modify, merge, publish, distribute, sublicense, and/or sell copies
++ * of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ *
++ **********************************************************/
++
++/*
++ * svga_escape.h --
++ *
++ * Definitions for our own (vendor-specific) SVGA Escape commands.
++ */
++
++#ifndef _SVGA_ESCAPE_H_
++#define _SVGA_ESCAPE_H_
++
++
++/*
++ * Namespace IDs for the escape command
++ */
++
++#define SVGA_ESCAPE_NSID_VMWARE 0x00000000
++#define SVGA_ESCAPE_NSID_DEVEL 0xFFFFFFFF
++
++
++/*
++ * Within SVGA_ESCAPE_NSID_VMWARE, we multiplex commands according to
++ * the first DWORD of escape data (after the nsID and size). As a
++ * guideline we're using the high word and low word as a major and
++ * minor command number, respectively.
++ *
++ * Major command number allocation:
++ *
++ * 0000: Reserved
++ * 0001: SVGA_ESCAPE_VMWARE_LOG (svga_binary_logger.h)
++ * 0002: SVGA_ESCAPE_VMWARE_VIDEO (svga_overlay.h)
++ * 0003: SVGA_ESCAPE_VMWARE_HINT (svga_escape.h)
++ */
++
++#define SVGA_ESCAPE_VMWARE_MAJOR_MASK 0xFFFF0000
++
++
++/*
++ * SVGA Hint commands.
++ *
++ * These escapes let the SVGA driver provide optional information to
++ * he host about the state of the guest or guest applications. The
++ * host can use these hints to make user interface or performance
++ * decisions.
++ *
++ * Notes:
++ *
++ * - SVGA_ESCAPE_VMWARE_HINT_FULLSCREEN is deprecated for guests
++ * that use the SVGA Screen Object extension. Instead of sending
++ * this escape, use the SVGA_SCREEN_FULLSCREEN_HINT flag on your
++ * Screen Object.
++ */
++
++#define SVGA_ESCAPE_VMWARE_HINT 0x00030000
++#define SVGA_ESCAPE_VMWARE_HINT_FULLSCREEN 0x00030001 // Deprecated
++
++typedef
++struct {
++ uint32 command;
++ uint32 fullscreen;
++ struct {
++ int32 x, y;
++ } monitorPosition;
++} SVGAEscapeHintFullscreen;
++
++#endif /* _SVGA_ESCAPE_H_ */
+diff --git a/drivers/gpu/drm/vmwgfx/svga_overlay.h b/drivers/gpu/drm/vmwgfx/svga_overlay.h
+new file mode 100644
+index 0000000..f753d73
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/svga_overlay.h
+@@ -0,0 +1,201 @@
++/**********************************************************
++ * Copyright 2007-2009 VMware, Inc. All rights reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person
++ * obtaining a copy of this software and associated documentation
++ * files (the "Software"), to deal in the Software without
++ * restriction, including without limitation the rights to use, copy,
++ * modify, merge, publish, distribute, sublicense, and/or sell copies
++ * of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ *
++ **********************************************************/
++
++/*
++ * svga_overlay.h --
++ *
++ * Definitions for video-overlay support.
++ */
++
++#ifndef _SVGA_OVERLAY_H_
++#define _SVGA_OVERLAY_H_
++
++#include "svga_reg.h"
++
++/*
++ * Video formats we support
++ */
++
++#define VMWARE_FOURCC_YV12 0x32315659 // 'Y' 'V' '1' '2'
++#define VMWARE_FOURCC_YUY2 0x32595559 // 'Y' 'U' 'Y' '2'
++#define VMWARE_FOURCC_UYVY 0x59565955 // 'U' 'Y' 'V' 'Y'
++
++typedef enum {
++ SVGA_OVERLAY_FORMAT_INVALID = 0,
++ SVGA_OVERLAY_FORMAT_YV12 = VMWARE_FOURCC_YV12,
++ SVGA_OVERLAY_FORMAT_YUY2 = VMWARE_FOURCC_YUY2,
++ SVGA_OVERLAY_FORMAT_UYVY = VMWARE_FOURCC_UYVY,
++} SVGAOverlayFormat;
++
++#define SVGA_VIDEO_COLORKEY_MASK 0x00ffffff
++
++#define SVGA_ESCAPE_VMWARE_VIDEO 0x00020000
++
++#define SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS 0x00020001
++ /* FIFO escape layout:
++ * Type, Stream Id, (Register Id, Value) pairs */
++
++#define SVGA_ESCAPE_VMWARE_VIDEO_FLUSH 0x00020002
++ /* FIFO escape layout:
++ * Type, Stream Id */
++
++typedef
++struct SVGAEscapeVideoSetRegs {
++ struct {
++ uint32 cmdType;
++ uint32 streamId;
++ } header;
++
++ // May include zero or more items.
++ struct {
++ uint32 registerId;
++ uint32 value;
++ } items[1];
++} SVGAEscapeVideoSetRegs;
++
++typedef
++struct SVGAEscapeVideoFlush {
++ uint32 cmdType;
++ uint32 streamId;
++} SVGAEscapeVideoFlush;
++
++
++/*
++ * Struct definitions for the video overlay commands built on
++ * SVGAFifoCmdEscape.
++ */
++typedef
++struct {
++ uint32 command;
++ uint32 overlay;
++} SVGAFifoEscapeCmdVideoBase;
++
++typedef
++struct {
++ SVGAFifoEscapeCmdVideoBase videoCmd;
++} SVGAFifoEscapeCmdVideoFlush;
++
++typedef
++struct {
++ SVGAFifoEscapeCmdVideoBase videoCmd;
++ struct {
++ uint32 regId;
++ uint32 value;
++ } items[1];
++} SVGAFifoEscapeCmdVideoSetRegs;
++
++typedef
++struct {
++ SVGAFifoEscapeCmdVideoBase videoCmd;
++ struct {
++ uint32 regId;
++ uint32 value;
++ } items[SVGA_VIDEO_NUM_REGS];
++} SVGAFifoEscapeCmdVideoSetAllRegs;
++
++
++/*
++ *----------------------------------------------------------------------
++ *
++ * VMwareVideoGetAttributes --
++ *
++ * Computes the size, pitches and offsets for YUV frames.
++ *
++ * Results:
++ * TRUE on success; otherwise FALSE on failure.
++ *
++ * Side effects:
++ * Pitches and offsets for the given YUV frame are put in 'pitches'
++ * and 'offsets' respectively. They are both optional though.
++ *
++ *----------------------------------------------------------------------
++ */
++
++static inline bool
++VMwareVideoGetAttributes(const SVGAOverlayFormat format, // IN
++ uint32 *width, // IN / OUT
++ uint32 *height, // IN / OUT
++ uint32 *size, // OUT
++ uint32 *pitches, // OUT (optional)
++ uint32 *offsets) // OUT (optional)
++{
++ int tmp;
++
++ *width = (*width + 1) & ~1;
++
++ if (offsets) {
++ offsets[0] = 0;
++ }
++
++ switch (format) {
++ case VMWARE_FOURCC_YV12:
++ *height = (*height + 1) & ~1;
++ *size = (*width + 3) & ~3;
++
++ if (pitches) {
++ pitches[0] = *size;
++ }
++
++ *size *= *height;
++
++ if (offsets) {
++ offsets[1] = *size;
++ }
++
++ tmp = ((*width >> 1) + 3) & ~3;
++
++ if (pitches) {
++ pitches[1] = pitches[2] = tmp;
++ }
++
++ tmp *= (*height >> 1);
++ *size += tmp;
++
++ if (offsets) {
++ offsets[2] = *size;
++ }
++
++ *size += tmp;
++ break;
++
++ case VMWARE_FOURCC_YUY2:
++ case VMWARE_FOURCC_UYVY:
++ *size = *width * 2;
++
++ if (pitches) {
++ pitches[0] = *size;
++ }
++
++ *size *= *height;
++ break;
++
++ default:
++ return false;
++ }
++
++ return true;
++}
++
++#endif // _SVGA_OVERLAY_H_
+diff --git a/drivers/gpu/drm/vmwgfx/svga_reg.h b/drivers/gpu/drm/vmwgfx/svga_reg.h
+new file mode 100644
+index 0000000..1b96c2e
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/svga_reg.h
+@@ -0,0 +1,1346 @@
++/**********************************************************
++ * Copyright 1998-2009 VMware, Inc. All rights reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person
++ * obtaining a copy of this software and associated documentation
++ * files (the "Software"), to deal in the Software without
++ * restriction, including without limitation the rights to use, copy,
++ * modify, merge, publish, distribute, sublicense, and/or sell copies
++ * of the Software, and to permit persons to whom the Software is
++ * furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice shall be
++ * included in all copies or substantial portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
++ * SOFTWARE.
++ *
++ **********************************************************/
++
++/*
++ * svga_reg.h --
++ *
++ * Virtual hardware definitions for the VMware SVGA II device.
++ */
++
++#ifndef _SVGA_REG_H_
++#define _SVGA_REG_H_
++
++/*
++ * PCI device IDs.
++ */
++#define PCI_VENDOR_ID_VMWARE 0x15AD
++#define PCI_DEVICE_ID_VMWARE_SVGA2 0x0405
++
++/*
++ * Legal values for the SVGA_REG_CURSOR_ON register in old-fashioned
++ * cursor bypass mode. This is still supported, but no new guest
++ * drivers should use it.
++ */
++#define SVGA_CURSOR_ON_HIDE 0x0 /* Must be 0 to maintain backward compatibility */
++#define SVGA_CURSOR_ON_SHOW 0x1 /* Must be 1 to maintain backward compatibility */
++#define SVGA_CURSOR_ON_REMOVE_FROM_FB 0x2 /* Remove the cursor from the framebuffer because we need to see what's under it */
++#define SVGA_CURSOR_ON_RESTORE_TO_FB 0x3 /* Put the cursor back in the framebuffer so the user can see it */
++
++/*
++ * The maximum framebuffer size that can traced for e.g. guests in VESA mode.
++ * The changeMap in the monitor is proportional to this number. Therefore, we'd
++ * like to keep it as small as possible to reduce monitor overhead (using
++ * SVGA_VRAM_MAX_SIZE for this increases the size of the shared area by over
++ * 4k!).
++ *
++ * NB: For compatibility reasons, this value must be greater than 0xff0000.
++ * See bug 335072.
++ */
++#define SVGA_FB_MAX_TRACEABLE_SIZE 0x1000000
++
++#define SVGA_MAX_PSEUDOCOLOR_DEPTH 8
++#define SVGA_MAX_PSEUDOCOLORS (1 << SVGA_MAX_PSEUDOCOLOR_DEPTH)
++#define SVGA_NUM_PALETTE_REGS (3 * SVGA_MAX_PSEUDOCOLORS)
++
++#define SVGA_MAGIC 0x900000UL
++#define SVGA_MAKE_ID(ver) (SVGA_MAGIC << 8 | (ver))
++
++/* Version 2 let the address of the frame buffer be unsigned on Win32 */
++#define SVGA_VERSION_2 2
++#define SVGA_ID_2 SVGA_MAKE_ID(SVGA_VERSION_2)
++
++/* Version 1 has new registers starting with SVGA_REG_CAPABILITIES so
++ PALETTE_BASE has moved */
++#define SVGA_VERSION_1 1
++#define SVGA_ID_1 SVGA_MAKE_ID(SVGA_VERSION_1)
++
++/* Version 0 is the initial version */
++#define SVGA_VERSION_0 0
++#define SVGA_ID_0 SVGA_MAKE_ID(SVGA_VERSION_0)
++
++/* "Invalid" value for all SVGA IDs. (Version ID, screen object ID, surface ID...) */
++#define SVGA_ID_INVALID 0xFFFFFFFF
++
++/* Port offsets, relative to BAR0 */
++#define SVGA_INDEX_PORT 0x0
++#define SVGA_VALUE_PORT 0x1
++#define SVGA_BIOS_PORT 0x2
++#define SVGA_IRQSTATUS_PORT 0x8
++
++/*
++ * Interrupt source flags for IRQSTATUS_PORT and IRQMASK.
++ *
++ * Interrupts are only supported when the
++ * SVGA_CAP_IRQMASK capability is present.
++ */
++#define SVGA_IRQFLAG_ANY_FENCE 0x1 /* Any fence was passed */
++#define SVGA_IRQFLAG_FIFO_PROGRESS 0x2 /* Made forward progress in the FIFO */
++#define SVGA_IRQFLAG_FENCE_GOAL 0x4 /* SVGA_FIFO_FENCE_GOAL reached */
++
++/*
++ * Registers
++ */
++
++enum {
++ SVGA_REG_ID = 0,
++ SVGA_REG_ENABLE = 1,
++ SVGA_REG_WIDTH = 2,
++ SVGA_REG_HEIGHT = 3,
++ SVGA_REG_MAX_WIDTH = 4,
++ SVGA_REG_MAX_HEIGHT = 5,
++ SVGA_REG_DEPTH = 6,
++ SVGA_REG_BITS_PER_PIXEL = 7, /* Current bpp in the guest */
++ SVGA_REG_PSEUDOCOLOR = 8,
++ SVGA_REG_RED_MASK = 9,
++ SVGA_REG_GREEN_MASK = 10,
++ SVGA_REG_BLUE_MASK = 11,
++ SVGA_REG_BYTES_PER_LINE = 12,
++ SVGA_REG_FB_START = 13, /* (Deprecated) */
++ SVGA_REG_FB_OFFSET = 14,
++ SVGA_REG_VRAM_SIZE = 15,
++ SVGA_REG_FB_SIZE = 16,
++
++ /* ID 0 implementation only had the above registers, then the palette */
++
++ SVGA_REG_CAPABILITIES = 17,
++ SVGA_REG_MEM_START = 18, /* (Deprecated) */
++ SVGA_REG_MEM_SIZE = 19,
++ SVGA_REG_CONFIG_DONE = 20, /* Set when memory area configured */
++ SVGA_REG_SYNC = 21, /* See "FIFO Synchronization Registers" */
++ SVGA_REG_BUSY = 22, /* See "FIFO Synchronization Registers" */
++ SVGA_REG_GUEST_ID = 23, /* Set guest OS identifier */
++ SVGA_REG_CURSOR_ID = 24, /* (Deprecated) */
++ SVGA_REG_CURSOR_X = 25, /* (Deprecated) */
++ SVGA_REG_CURSOR_Y = 26, /* (Deprecated) */
++ SVGA_REG_CURSOR_ON = 27, /* (Deprecated) */
++ SVGA_REG_HOST_BITS_PER_PIXEL = 28, /* (Deprecated) */
++ SVGA_REG_SCRATCH_SIZE = 29, /* Number of scratch registers */
++ SVGA_REG_MEM_REGS = 30, /* Number of FIFO registers */
++ SVGA_REG_NUM_DISPLAYS = 31, /* (Deprecated) */
++ SVGA_REG_PITCHLOCK = 32, /* Fixed pitch for all modes */
++ SVGA_REG_IRQMASK = 33, /* Interrupt mask */
++
++ /* Legacy multi-monitor support */
++ SVGA_REG_NUM_GUEST_DISPLAYS = 34,/* Number of guest displays in X/Y direction */
++ SVGA_REG_DISPLAY_ID = 35, /* Display ID for the following display attributes */
++ SVGA_REG_DISPLAY_IS_PRIMARY = 36,/* Whether this is a primary display */
++ SVGA_REG_DISPLAY_POSITION_X = 37,/* The display position x */
++ SVGA_REG_DISPLAY_POSITION_Y = 38,/* The display position y */
++ SVGA_REG_DISPLAY_WIDTH = 39, /* The display's width */
++ SVGA_REG_DISPLAY_HEIGHT = 40, /* The display's height */
++
++ /* See "Guest memory regions" below. */
++ SVGA_REG_GMR_ID = 41,
++ SVGA_REG_GMR_DESCRIPTOR = 42,
++ SVGA_REG_GMR_MAX_IDS = 43,
++ SVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH = 44,
++
++ SVGA_REG_TRACES = 45, /* Enable trace-based updates even when FIFO is on */
++ SVGA_REG_TOP = 46, /* Must be 1 more than the last register */
++
++ SVGA_PALETTE_BASE = 1024, /* Base of SVGA color map */
++ /* Next 768 (== 256*3) registers exist for colormap */
++
++ SVGA_SCRATCH_BASE = SVGA_PALETTE_BASE + SVGA_NUM_PALETTE_REGS
++ /* Base of scratch registers */
++ /* Next reg[SVGA_REG_SCRATCH_SIZE] registers exist for scratch usage:
++ First 4 are reserved for VESA BIOS Extension; any remaining are for
++ the use of the current SVGA driver. */
++};
++
++
++/*
++ * Guest memory regions (GMRs):
++ *
++ * This is a new memory mapping feature available in SVGA devices
++ * which have the SVGA_CAP_GMR bit set. Previously, there were two
++ * fixed memory regions available with which to share data between the
++ * device and the driver: the FIFO ('MEM') and the framebuffer. GMRs
++ * are our name for an extensible way of providing arbitrary DMA
++ * buffers for use between the driver and the SVGA device. They are a
++ * new alternative to framebuffer memory, usable for both 2D and 3D
++ * graphics operations.
++ *
++ * Since GMR mapping must be done synchronously with guest CPU
++ * execution, we use a new pair of SVGA registers:
++ *
++ * SVGA_REG_GMR_ID --
++ *
++ * Read/write.
++ * This register holds the 32-bit ID (a small positive integer)
++ * of a GMR to create, delete, or redefine. Writing this register
++ * has no side-effects.
++ *
++ * SVGA_REG_GMR_DESCRIPTOR --
++ *
++ * Write-only.
++ * Writing this register will create, delete, or redefine the GMR
++ * specified by the above ID register. If this register is zero,
++ * the GMR is deleted. Any pointers into this GMR (including those
++ * currently being processed by FIFO commands) will be
++ * synchronously invalidated.
++ *
++ * If this register is nonzero, it must be the physical page
++ * number (PPN) of a data structure which describes the physical
++ * layout of the memory region this GMR should describe. The
++ * descriptor structure will be read synchronously by the SVGA
++ * device when this register is written. The descriptor need not
++ * remain allocated for the lifetime of the GMR.
++ *
++ * The guest driver should write SVGA_REG_GMR_ID first, then
++ * SVGA_REG_GMR_DESCRIPTOR.
++ *
++ * SVGA_REG_GMR_MAX_IDS --
++ *
++ * Read-only.
++ * The SVGA device may choose to support a maximum number of
++ * user-defined GMR IDs. This register holds the number of supported
++ * IDs. (The maximum supported ID plus 1)
++ *
++ * SVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH --
++ *
++ * Read-only.
++ * The SVGA device may choose to put a limit on the total number
++ * of SVGAGuestMemDescriptor structures it will read when defining
++ * a single GMR.
++ *
++ * The descriptor structure is an array of SVGAGuestMemDescriptor
++ * structures. Each structure may do one of three things:
++ *
++ * - Terminate the GMR descriptor list.
++ * (ppn==0, numPages==0)
++ *
++ * - Add a PPN or range of PPNs to the GMR's virtual address space.
++ * (ppn != 0, numPages != 0)
++ *
++ * - Provide the PPN of the next SVGAGuestMemDescriptor, in order to
++ * support multi-page GMR descriptor tables without forcing the
++ * driver to allocate physically contiguous memory.
++ * (ppn != 0, numPages == 0)
++ *
++ * Note that each physical page of SVGAGuestMemDescriptor structures
++ * can describe at least 2MB of guest memory. If the driver needs to
++ * use more than one page of descriptor structures, it must use one of
++ * its SVGAGuestMemDescriptors to point to an additional page. The
++ * device will never automatically cross a page boundary.
++ *
++ * Once the driver has described a GMR, it is immediately available
++ * for use via any FIFO command that uses an SVGAGuestPtr structure.
++ * These pointers include a GMR identifier plus an offset into that
++ * GMR.
++ *
++ * The driver must check the SVGA_CAP_GMR bit before using the GMR
++ * registers.
++ */
++
++/*
++ * Special GMR IDs, allowing SVGAGuestPtrs to point to framebuffer
++ * memory as well. In the future, these IDs could even be used to
++ * allow legacy memory regions to be redefined by the guest as GMRs.
++ *
++ * Using the guest framebuffer (GFB) at BAR1 for general purpose DMA
++ * is being phased out. Please try to use user-defined GMRs whenever
++ * possible.
++ */
++#define SVGA_GMR_NULL ((uint32) -1)
++#define SVGA_GMR_FRAMEBUFFER ((uint32) -2) // Guest Framebuffer (GFB)
++
++typedef
++struct SVGAGuestMemDescriptor {
++ uint32 ppn;
++ uint32 numPages;
++} SVGAGuestMemDescriptor;
++
++typedef
++struct SVGAGuestPtr {
++ uint32 gmrId;
++ uint32 offset;
++} SVGAGuestPtr;
++
++
++/*
++ * SVGAGMRImageFormat --
++ *
++ * This is a packed representation of the source 2D image format
++ * for a GMR-to-screen blit. Currently it is defined as an encoding
++ * of the screen's color depth and bits-per-pixel, however, 16 bits
++ * are reserved for future use to identify other encodings (such as
++ * RGBA or higher-precision images).
++ *
++ * Currently supported formats:
++ *
++ * bpp depth Format Name
++ * --- ----- -----------
++ * 32 24 32-bit BGRX
++ * 24 24 24-bit BGR
++ * 16 16 RGB 5-6-5
++ * 16 15 RGB 5-5-5
++ *
++ */
++
++typedef
++struct SVGAGMRImageFormat {
++ union {
++ struct {
++ uint32 bitsPerPixel : 8;
++ uint32 colorDepth : 8;
++ uint32 reserved : 16; // Must be zero
++ };
++
++ uint32 value;
++ };
++} SVGAGMRImageFormat;
++
++/*
++ * SVGAColorBGRX --
++ *
++ * A 24-bit color format (BGRX), which does not depend on the
++ * format of the legacy guest framebuffer (GFB) or the current
++ * GMRFB state.
++ */
++
++typedef
++struct SVGAColorBGRX {
++ union {
++ struct {
++ uint32 b : 8;
++ uint32 g : 8;
++ uint32 r : 8;
++ uint32 x : 8; // Unused
++ };
++
++ uint32 value;
++ };
++} SVGAColorBGRX;
++
++
++/*
++ * SVGASignedRect --
++ * SVGASignedPoint --
++ *
++ * Signed rectangle and point primitives. These are used by the new
++ * 2D primitives for drawing to Screen Objects, which can occupy a
++ * signed virtual coordinate space.
++ *
++ * SVGASignedRect specifies a half-open interval: the (left, top)
++ * pixel is part of the rectangle, but the (right, bottom) pixel is
++ * not.
++ */
++
++typedef
++struct SVGASignedRect {
++ int32 left;
++ int32 top;
++ int32 right;
++ int32 bottom;
++} SVGASignedRect;
++
++typedef
++struct SVGASignedPoint {
++ int32 x;
++ int32 y;
++} SVGASignedPoint;
++
++
++/*
++ * Capabilities
++ *
++ * Note the holes in the bitfield. Missing bits have been deprecated,
++ * and must not be reused. Those capabilities will never be reported
++ * by new versions of the SVGA device.
++ */
++
++#define SVGA_CAP_NONE 0x00000000
++#define SVGA_CAP_RECT_COPY 0x00000002
++#define SVGA_CAP_CURSOR 0x00000020
++#define SVGA_CAP_CURSOR_BYPASS 0x00000040 // Legacy (Use Cursor Bypass 3 instead)
++#define SVGA_CAP_CURSOR_BYPASS_2 0x00000080 // Legacy (Use Cursor Bypass 3 instead)
++#define SVGA_CAP_8BIT_EMULATION 0x00000100
++#define SVGA_CAP_ALPHA_CURSOR 0x00000200
++#define SVGA_CAP_3D 0x00004000
++#define SVGA_CAP_EXTENDED_FIFO 0x00008000
++#define SVGA_CAP_MULTIMON 0x00010000 // Legacy multi-monitor support
++#define SVGA_CAP_PITCHLOCK 0x00020000
++#define SVGA_CAP_IRQMASK 0x00040000
++#define SVGA_CAP_DISPLAY_TOPOLOGY 0x00080000 // Legacy multi-monitor support
++#define SVGA_CAP_GMR 0x00100000
++#define SVGA_CAP_TRACES 0x00200000
++
++
++/*
++ * FIFO register indices.
++ *
++ * The FIFO is a chunk of device memory mapped into guest physmem. It
++ * is always treated as 32-bit words.
++ *
++ * The guest driver gets to decide how to partition it between
++ * - FIFO registers (there are always at least 4, specifying where the
++ * following data area is and how much data it contains; there may be
++ * more registers following these, depending on the FIFO protocol
++ * version in use)
++ * - FIFO data, written by the guest and slurped out by the VMX.
++ * These indices are 32-bit word offsets into the FIFO.
++ */
++
++enum {
++ /*
++ * Block 1 (basic registers): The originally defined FIFO registers.
++ * These exist and are valid for all versions of the FIFO protocol.
++ */
++
++ SVGA_FIFO_MIN = 0,
++ SVGA_FIFO_MAX, /* The distance from MIN to MAX must be at least 10K */
++ SVGA_FIFO_NEXT_CMD,
++ SVGA_FIFO_STOP,
++
++ /*
++ * Block 2 (extended registers): Mandatory registers for the extended
++ * FIFO. These exist if the SVGA caps register includes
++ * SVGA_CAP_EXTENDED_FIFO; some of them are valid only if their
++ * associated capability bit is enabled.
++ *
++ * Note that when originally defined, SVGA_CAP_EXTENDED_FIFO implied
++ * support only for (FIFO registers) CAPABILITIES, FLAGS, and FENCE.
++ * This means that the guest has to test individually (in most cases
++ * using FIFO caps) for the presence of registers after this; the VMX
++ * can define "extended FIFO" to mean whatever it wants, and currently
++ * won't enable it unless there's room for that set and much more.
++ */
++
++ SVGA_FIFO_CAPABILITIES = 4,
++ SVGA_FIFO_FLAGS,
++ // Valid with SVGA_FIFO_CAP_FENCE:
++ SVGA_FIFO_FENCE,
++
++ /*
++ * Block 3a (optional extended registers): Additional registers for the
++ * extended FIFO, whose presence isn't actually implied by
++ * SVGA_CAP_EXTENDED_FIFO; these exist if SVGA_FIFO_MIN is high enough to
++ * leave room for them.
++ *
++ * These in block 3a, the VMX currently considers mandatory for the
++ * extended FIFO.
++ */
++
++ // Valid if exists (i.e. if extended FIFO enabled):
++ SVGA_FIFO_3D_HWVERSION, /* See SVGA3dHardwareVersion in svga3d_reg.h */
++ // Valid with SVGA_FIFO_CAP_PITCHLOCK:
++ SVGA_FIFO_PITCHLOCK,
++
++ // Valid with SVGA_FIFO_CAP_CURSOR_BYPASS_3:
++ SVGA_FIFO_CURSOR_ON, /* Cursor bypass 3 show/hide register */
++ SVGA_FIFO_CURSOR_X, /* Cursor bypass 3 x register */
++ SVGA_FIFO_CURSOR_Y, /* Cursor bypass 3 y register */
++ SVGA_FIFO_CURSOR_COUNT, /* Incremented when any of the other 3 change */
++ SVGA_FIFO_CURSOR_LAST_UPDATED,/* Last time the host updated the cursor */
++
++ // Valid with SVGA_FIFO_CAP_RESERVE:
++ SVGA_FIFO_RESERVED, /* Bytes past NEXT_CMD with real contents */
++
++ /*
++ * Valid with SVGA_FIFO_CAP_SCREEN_OBJECT:
++ *
++ * By default this is SVGA_ID_INVALID, to indicate that the cursor
++ * coordinates are specified relative to the virtual root. If this
++ * is set to a specific screen ID, cursor position is reinterpreted
++ * as a signed offset relative to that screen's origin. This is the
++ * only way to place the cursor on a non-rooted screen.
++ */
++ SVGA_FIFO_CURSOR_SCREEN_ID,
++
++ /*
++ * XXX: The gap here, up until SVGA_FIFO_3D_CAPS, can be used for new
++ * registers, but this must be done carefully and with judicious use of
++ * capability bits, since comparisons based on SVGA_FIFO_MIN aren't
++ * enough to tell you whether the register exists: we've shipped drivers
++ * and products that used SVGA_FIFO_3D_CAPS but didn't know about some of
++ * the earlier ones. The actual order of introduction was:
++ * - PITCHLOCK
++ * - 3D_CAPS
++ * - CURSOR_* (cursor bypass 3)
++ * - RESERVED
++ * So, code that wants to know whether it can use any of the
++ * aforementioned registers, or anything else added after PITCHLOCK and
++ * before 3D_CAPS, needs to reason about something other than
++ * SVGA_FIFO_MIN.
++ */
++
++ /*
++ * 3D caps block space; valid with 3D hardware version >=
++ * SVGA3D_HWVERSION_WS6_B1.
++ */
++ SVGA_FIFO_3D_CAPS = 32,
++ SVGA_FIFO_3D_CAPS_LAST = 32 + 255,
++
++ /*
++ * End of VMX's current definition of "extended-FIFO registers".
++ * Registers before here are always enabled/disabled as a block; either
++ * the extended FIFO is enabled and includes all preceding registers, or
++ * it's disabled entirely.
++ *
++ * Block 3b (truly optional extended registers): Additional registers for
++ * the extended FIFO, which the VMX already knows how to enable and
++ * disable with correct granularity.
++ *
++ * Registers after here exist if and only if the guest SVGA driver
++ * sets SVGA_FIFO_MIN high enough to leave room for them.
++ */
++
++ // Valid if register exists:
++ SVGA_FIFO_GUEST_3D_HWVERSION, /* Guest driver's 3D version */
++ SVGA_FIFO_FENCE_GOAL, /* Matching target for SVGA_IRQFLAG_FENCE_GOAL */
++ SVGA_FIFO_BUSY, /* See "FIFO Synchronization Registers" */
++
++ /*
++ * Always keep this last. This defines the maximum number of
++ * registers we know about. At power-on, this value is placed in
++ * the SVGA_REG_MEM_REGS register, and we expect the guest driver
++ * to allocate this much space in FIFO memory for registers.
++ */
++ SVGA_FIFO_NUM_REGS
++};
++
++
++/*
++ * Definition of registers included in extended FIFO support.
++ *
++ * The guest SVGA driver gets to allocate the FIFO between registers
++ * and data. It must always allocate at least 4 registers, but old
++ * drivers stopped there.
++ *
++ * The VMX will enable extended FIFO support if and only if the guest
++ * left enough room for all registers defined as part of the mandatory
++ * set for the extended FIFO.
++ *
++ * Note that the guest drivers typically allocate the FIFO only at
++ * initialization time, not at mode switches, so it's likely that the
++ * number of FIFO registers won't change without a reboot.
++ *
++ * All registers less than this value are guaranteed to be present if
++ * svgaUser->fifo.extended is set. Any later registers must be tested
++ * individually for compatibility at each use (in the VMX).
++ *
++ * This value is used only by the VMX, so it can change without
++ * affecting driver compatibility; keep it that way?
++ */
++#define SVGA_FIFO_EXTENDED_MANDATORY_REGS (SVGA_FIFO_3D_CAPS_LAST + 1)
++
++
++/*
++ * FIFO Synchronization Registers
++ *
++ * This explains the relationship between the various FIFO
++ * sync-related registers in IOSpace and in FIFO space.
++ *
++ * SVGA_REG_SYNC --
++ *
++ * The SYNC register can be used in two different ways by the guest:
++ *
++ * 1. If the guest wishes to fully sync (drain) the FIFO,
++ * it will write once to SYNC then poll on the BUSY
++ * register. The FIFO is sync'ed once BUSY is zero.
++ *
++ * 2. If the guest wants to asynchronously wake up the host,
++ * it will write once to SYNC without polling on BUSY.
++ * Ideally it will do this after some new commands have
++ * been placed in the FIFO, and after reading a zero
++ * from SVGA_FIFO_BUSY.
++ *
++ * (1) is the original behaviour that SYNC was designed to
++ * support. Originally, a write to SYNC would implicitly
++ * trigger a read from BUSY. This causes us to synchronously
++ * process the FIFO.
++ *
++ * This behaviour has since been changed so that writing SYNC
++ * will *not* implicitly cause a read from BUSY. Instead, it
++ * makes a channel call which asynchronously wakes up the MKS
++ * thread.
++ *
++ * New guests can use this new behaviour to implement (2)
++ * efficiently. This lets guests get the host's attention
++ * without waiting for the MKS to poll, which gives us much
++ * better CPU utilization on SMP hosts and on UP hosts while
++ * we're blocked on the host GPU.
++ *
++ * Old guests shouldn't notice the behaviour change. SYNC was
++ * never guaranteed to process the entire FIFO, since it was
++ * bounded to a particular number of CPU cycles. Old guests will
++ * still loop on the BUSY register until the FIFO is empty.
++ *
++ * Writing to SYNC currently has the following side-effects:
++ *
++ * - Sets SVGA_REG_BUSY to TRUE (in the monitor)
++ * - Asynchronously wakes up the MKS thread for FIFO processing
++ * - The value written to SYNC is recorded as a "reason", for
++ * stats purposes.
++ *
++ * If SVGA_FIFO_BUSY is available, drivers are advised to only
++ * write to SYNC if SVGA_FIFO_BUSY is FALSE. Drivers should set
++ * SVGA_FIFO_BUSY to TRUE after writing to SYNC. The MKS will
++ * eventually set SVGA_FIFO_BUSY on its own, but this approach
++ * lets the driver avoid sending multiple asynchronous wakeup
++ * messages to the MKS thread.
++ *
++ * SVGA_REG_BUSY --
++ *
++ * This register is set to TRUE when SVGA_REG_SYNC is written,
++ * and it reads as FALSE when the FIFO has been completely
++ * drained.
++ *
++ * Every read from this register causes us to synchronously
++ * process FIFO commands. There is no guarantee as to how many
++ * commands each read will process.
++ *
++ * CPU time spent processing FIFO commands will be billed to
++ * the guest.
++ *
++ * New drivers should avoid using this register unless they
++ * need to guarantee that the FIFO is completely drained. It
++ * is overkill for performing a sync-to-fence. Older drivers
++ * will use this register for any type of synchronization.
++ *
++ * SVGA_FIFO_BUSY --
++ *
++ * This register is a fast way for the guest driver to check
++ * whether the FIFO is already being processed. It reads and
++ * writes at normal RAM speeds, with no monitor intervention.
++ *
++ * If this register reads as TRUE, the host is guaranteeing that
++ * any new commands written into the FIFO will be noticed before
++ * the MKS goes back to sleep.
++ *
++ * If this register reads as FALSE, no such guarantee can be
++ * made.
++ *
++ * The guest should use this register to quickly determine
++ * whether or not it needs to wake up the host. If the guest
++ * just wrote a command or group of commands that it would like
++ * the host to begin processing, it should:
++ *
++ * 1. Read SVGA_FIFO_BUSY. If it reads as TRUE, no further
++ * action is necessary.
++ *
++ * 2. Write TRUE to SVGA_FIFO_BUSY. This informs future guest
++ * code that we've already sent a SYNC to the host and we
++ * don't need to send a duplicate.
++ *
++ * 3. Write a reason to SVGA_REG_SYNC. This will send an
++ * asynchronous wakeup to the MKS thread.
++ */
++
++
++/*
++ * FIFO Capabilities
++ *
++ * Fence -- Fence register and command are supported
++ * Accel Front -- Front buffer only commands are supported
++ * Pitch Lock -- Pitch lock register is supported
++ * Video -- SVGA Video overlay units are supported
++ * Escape -- Escape command is supported
++ *
++ * XXX: Add longer descriptions for each capability, including a list
++ * of the new features that each capability provides.
++ *
++ * SVGA_FIFO_CAP_SCREEN_OBJECT --
++ *
++ * Provides dynamic multi-screen rendering, for improved Unity and
++ * multi-monitor modes. With Screen Object, the guest can
++ * dynamically create and destroy 'screens', which can represent
++ * Unity windows or virtual monitors. Screen Object also provides
++ * strong guarantees that DMA operations happen only when
++ * guest-initiated. Screen Object deprecates the BAR1 guest
++ * framebuffer (GFB) and all commands that work only with the GFB.
++ *
++ * New registers:
++ * FIFO_CURSOR_SCREEN_ID, VIDEO_DATA_GMRID, VIDEO_DST_SCREEN_ID
++ *
++ * New 2D commands:
++ * DEFINE_SCREEN, DESTROY_SCREEN, DEFINE_GMRFB, BLIT_GMRFB_TO_SCREEN,
++ * BLIT_SCREEN_TO_GMRFB, ANNOTATION_FILL, ANNOTATION_COPY
++ *
++ * New 3D commands:
++ * BLIT_SURFACE_TO_SCREEN
++ *
++ * New guarantees:
++ *
++ * - The host will not read or write guest memory, including the GFB,
++ * except when explicitly initiated by a DMA command.
++ *
++ * - All DMA, including legacy DMA like UPDATE and PRESENT_READBACK,
++ * is guaranteed to complete before any subsequent FENCEs.
++ *
++ * - All legacy commands which affect a Screen (UPDATE, PRESENT,
++ * PRESENT_READBACK) as well as new Screen blit commands will
++ * all behave consistently as blits, and memory will be read
++ * or written in FIFO order.
++ *
++ * For example, if you PRESENT from one SVGA3D surface to multiple
++ * places on the screen, the data copied will always be from the
++ * SVGA3D surface at the time the PRESENT was issued in the FIFO.
++ * This was not necessarily true on devices without Screen Object.
++ *
++ * This means that on devices that support Screen Object, the
++ * PRESENT_READBACK command should not be necessary unless you
++ * actually want to read back the results of 3D rendering into
++ * system memory. (And for that, the BLIT_SCREEN_TO_GMRFB
++ * command provides a strict superset of functionality.)
++ *
++ * - When a screen is resized, either using Screen Object commands or
++ * legacy multimon registers, its contents are preserved.
++ */
++
++#define SVGA_FIFO_CAP_NONE 0
++#define SVGA_FIFO_CAP_FENCE (1<<0)
++#define SVGA_FIFO_CAP_ACCELFRONT (1<<1)
++#define SVGA_FIFO_CAP_PITCHLOCK (1<<2)
++#define SVGA_FIFO_CAP_VIDEO (1<<3)
++#define SVGA_FIFO_CAP_CURSOR_BYPASS_3 (1<<4)
++#define SVGA_FIFO_CAP_ESCAPE (1<<5)
++#define SVGA_FIFO_CAP_RESERVE (1<<6)
++#define SVGA_FIFO_CAP_SCREEN_OBJECT (1<<7)
++
++
++/*
++ * FIFO Flags
++ *
++ * Accel Front -- Driver should use front buffer only commands
++ */
++
++#define SVGA_FIFO_FLAG_NONE 0
++#define SVGA_FIFO_FLAG_ACCELFRONT (1<<0)
++#define SVGA_FIFO_FLAG_RESERVED (1<<31) // Internal use only
++
++/*
++ * FIFO reservation sentinel value
++ */
++
++#define SVGA_FIFO_RESERVED_UNKNOWN 0xffffffff
++
++
++/*
++ * Video overlay support
++ */
++
++#define SVGA_NUM_OVERLAY_UNITS 32
++
++
++/*
++ * Video capabilities that the guest is currently using
++ */
++
++#define SVGA_VIDEO_FLAG_COLORKEY 0x0001
++
++
++/*
++ * Offsets for the video overlay registers
++ */
++
++enum {
++ SVGA_VIDEO_ENABLED = 0,
++ SVGA_VIDEO_FLAGS,
++ SVGA_VIDEO_DATA_OFFSET,
++ SVGA_VIDEO_FORMAT,
++ SVGA_VIDEO_COLORKEY,
++ SVGA_VIDEO_SIZE, // Deprecated
++ SVGA_VIDEO_WIDTH,
++ SVGA_VIDEO_HEIGHT,
++ SVGA_VIDEO_SRC_X,
++ SVGA_VIDEO_SRC_Y,
++ SVGA_VIDEO_SRC_WIDTH,
++ SVGA_VIDEO_SRC_HEIGHT,
++ SVGA_VIDEO_DST_X, // Signed int32
++ SVGA_VIDEO_DST_Y, // Signed int32
++ SVGA_VIDEO_DST_WIDTH,
++ SVGA_VIDEO_DST_HEIGHT,
++ SVGA_VIDEO_PITCH_1,
++ SVGA_VIDEO_PITCH_2,
++ SVGA_VIDEO_PITCH_3,
++ SVGA_VIDEO_DATA_GMRID, // Optional, defaults to SVGA_GMR_FRAMEBUFFER
++ SVGA_VIDEO_DST_SCREEN_ID, // Optional, defaults to virtual coords (SVGA_ID_INVALID)
++ SVGA_VIDEO_NUM_REGS
++};
++
++
++/*
++ * SVGA Overlay Units
++ *
++ * width and height relate to the entire source video frame.
++ * srcX, srcY, srcWidth and srcHeight represent subset of the source
++ * video frame to be displayed.
++ */
++
++typedef struct SVGAOverlayUnit {
++ uint32 enabled;
++ uint32 flags;
++ uint32 dataOffset;
++ uint32 format;
++ uint32 colorKey;
++ uint32 size;
++ uint32 width;
++ uint32 height;
++ uint32 srcX;
++ uint32 srcY;
++ uint32 srcWidth;
++ uint32 srcHeight;
++ int32 dstX;
++ int32 dstY;
++ uint32 dstWidth;
++ uint32 dstHeight;
++ uint32 pitches[3];
++ uint32 dataGMRId;
++ uint32 dstScreenId;
++} SVGAOverlayUnit;
++
++
++/*
++ * SVGAScreenObject --
++ *
++ * This is a new way to represent a guest's multi-monitor screen or
++ * Unity window. Screen objects are only supported if the
++ * SVGA_FIFO_CAP_SCREEN_OBJECT capability bit is set.
++ *
++ * If Screen Objects are supported, they can be used to fully
++ * replace the functionality provided by the framebuffer registers
++ * (SVGA_REG_WIDTH, HEIGHT, etc.) and by SVGA_CAP_DISPLAY_TOPOLOGY.
++ *
++ * The screen object is a struct with guaranteed binary
++ * compatibility. New flags can be added, and the struct may grow,
++ * but existing fields must retain their meaning.
++ *
++ */
++
++#define SVGA_SCREEN_HAS_ROOT (1 << 0) // Screen is present in the virtual coord space
++#define SVGA_SCREEN_IS_PRIMARY (1 << 1) // Guest considers this screen to be 'primary'
++#define SVGA_SCREEN_FULLSCREEN_HINT (1 << 2) // Guest is running a fullscreen app here
++
++typedef
++struct SVGAScreenObject {
++ uint32 structSize; // sizeof(SVGAScreenObject)
++ uint32 id;
++ uint32 flags;
++ struct {
++ uint32 width;
++ uint32 height;
++ } size;
++ struct {
++ int32 x;
++ int32 y;
++ } root; // Only used if SVGA_SCREEN_HAS_ROOT is set.
++} SVGAScreenObject;
++
++
++/*
++ * Commands in the command FIFO:
++ *
++ * Command IDs defined below are used for the traditional 2D FIFO
++ * communication (not all commands are available for all versions of the
++ * SVGA FIFO protocol).
++ *
++ * Note the holes in the command ID numbers: These commands have been
++ * deprecated, and the old IDs must not be reused.
++ *
++ * Command IDs from 1000 to 1999 are reserved for use by the SVGA3D
++ * protocol.
++ *
++ * Each command's parameters are described by the comments and
++ * structs below.
++ */
++
++typedef enum {
++ SVGA_CMD_INVALID_CMD = 0,
++ SVGA_CMD_UPDATE = 1,
++ SVGA_CMD_RECT_COPY = 3,
++ SVGA_CMD_DEFINE_CURSOR = 19,
++ SVGA_CMD_DEFINE_ALPHA_CURSOR = 22,
++ SVGA_CMD_UPDATE_VERBOSE = 25,
++ SVGA_CMD_FRONT_ROP_FILL = 29,
++ SVGA_CMD_FENCE = 30,
++ SVGA_CMD_ESCAPE = 33,
++ SVGA_CMD_DEFINE_SCREEN = 34,
++ SVGA_CMD_DESTROY_SCREEN = 35,
++ SVGA_CMD_DEFINE_GMRFB = 36,
++ SVGA_CMD_BLIT_GMRFB_TO_SCREEN = 37,
++ SVGA_CMD_BLIT_SCREEN_TO_GMRFB = 38,
++ SVGA_CMD_ANNOTATION_FILL = 39,
++ SVGA_CMD_ANNOTATION_COPY = 40,
++ SVGA_CMD_MAX
++} SVGAFifoCmdId;
++
++#define SVGA_CMD_MAX_ARGS 64
++
++
++/*
++ * SVGA_CMD_UPDATE --
++ *
++ * This is a DMA transfer which copies from the Guest Framebuffer
++ * (GFB) at BAR1 + SVGA_REG_FB_OFFSET to any screens which
++ * intersect with the provided virtual rectangle.
++ *
++ * This command does not support using arbitrary guest memory as a
++ * data source- it only works with the pre-defined GFB memory.
++ * This command also does not support signed virtual coordinates.
++ * If you have defined screens (using SVGA_CMD_DEFINE_SCREEN) with
++ * negative root x/y coordinates, the negative portion of those
++ * screens will not be reachable by this command.
++ *
++ * This command is not necessary when using framebuffer
++ * traces. Traces are automatically enabled if the SVGA FIFO is
++ * disabled, and you may explicitly enable/disable traces using
++ * SVGA_REG_TRACES. With traces enabled, any write to the GFB will
++ * automatically act as if a subsequent SVGA_CMD_UPDATE was issued.
++ *
++ * Traces and SVGA_CMD_UPDATE are the only supported ways to render
++ * pseudocolor screen updates. The newer Screen Object commands
++ * only support true color formats.
++ *
++ * Availability:
++ * Always available.
++ */
++
++typedef
++struct {
++ uint32 x;
++ uint32 y;
++ uint32 width;
++ uint32 height;
++} SVGAFifoCmdUpdate;
++
++
++/*
++ * SVGA_CMD_RECT_COPY --
++ *
++ * Perform a rectangular DMA transfer from one area of the GFB to
++ * another, and copy the result to any screens which intersect it.
++ *
++ * Availability:
++ * SVGA_CAP_RECT_COPY
++ */
++
++typedef
++struct {
++ uint32 srcX;
++ uint32 srcY;
++ uint32 destX;
++ uint32 destY;
++ uint32 width;
++ uint32 height;
++} SVGAFifoCmdRectCopy;
++
++
++/*
++ * SVGA_CMD_DEFINE_CURSOR --
++ *
++ * Provide a new cursor image, as an AND/XOR mask.
++ *
++ * The recommended way to position the cursor overlay is by using
++ * the SVGA_FIFO_CURSOR_* registers, supported by the
++ * SVGA_FIFO_CAP_CURSOR_BYPASS_3 capability.
++ *
++ * Availability:
++ * SVGA_CAP_CURSOR
++ */
++
++typedef
++struct {
++ uint32 id; // Reserved, must be zero.
++ uint32 hotspotX;
++ uint32 hotspotY;
++ uint32 width;
++ uint32 height;
++ uint32 andMaskDepth; // Value must be 1 or equal to BITS_PER_PIXEL
++ uint32 xorMaskDepth; // Value must be 1 or equal to BITS_PER_PIXEL
++ /*
++ * Followed by scanline data for AND mask, then XOR mask.
++ * Each scanline is padded to a 32-bit boundary.
++ */
++} SVGAFifoCmdDefineCursor;
++
++
++/*
++ * SVGA_CMD_DEFINE_ALPHA_CURSOR --
++ *
++ * Provide a new cursor image, in 32-bit BGRA format.
++ *
++ * The recommended way to position the cursor overlay is by using
++ * the SVGA_FIFO_CURSOR_* registers, supported by the
++ * SVGA_FIFO_CAP_CURSOR_BYPASS_3 capability.
++ *
++ * Availability:
++ * SVGA_CAP_ALPHA_CURSOR
++ */
++
++typedef
++struct {
++ uint32 id; // Reserved, must be zero.
++ uint32 hotspotX;
++ uint32 hotspotY;
++ uint32 width;
++ uint32 height;
++ /* Followed by scanline data */
++} SVGAFifoCmdDefineAlphaCursor;
++
++
++/*
++ * SVGA_CMD_UPDATE_VERBOSE --
++ *
++ * Just like SVGA_CMD_UPDATE, but also provide a per-rectangle
++ * 'reason' value, an opaque cookie which is used by internal
++ * debugging tools. Third party drivers should not use this
++ * command.
++ *
++ * Availability:
++ * SVGA_CAP_EXTENDED_FIFO
++ */
++
++typedef
++struct {
++ uint32 x;
++ uint32 y;
++ uint32 width;
++ uint32 height;
++ uint32 reason;
++} SVGAFifoCmdUpdateVerbose;
++
++
++/*
++ * SVGA_CMD_FRONT_ROP_FILL --
++ *
++ * This is a hint which tells the SVGA device that the driver has
++ * just filled a rectangular region of the GFB with a solid
++ * color. Instead of reading these pixels from the GFB, the device
++ * can assume that they all equal 'color'. This is primarily used
++ * for remote desktop protocols.
++ *
++ * Availability:
++ * SVGA_FIFO_CAP_ACCELFRONT
++ */
++
++#define SVGA_ROP_COPY 0x03
++
++typedef
++struct {
++ uint32 color; // In the same format as the GFB
++ uint32 x;
++ uint32 y;
++ uint32 width;
++ uint32 height;
++ uint32 rop; // Must be SVGA_ROP_COPY
++} SVGAFifoCmdFrontRopFill;
++
++
++/*
++ * SVGA_CMD_FENCE --
++ *
++ * Insert a synchronization fence. When the SVGA device reaches
++ * this command, it will copy the 'fence' value into the
++ * SVGA_FIFO_FENCE register. It will also compare the fence against
++ * SVGA_FIFO_FENCE_GOAL. If the fence matches the goal and the
++ * SVGA_IRQFLAG_FENCE_GOAL interrupt is enabled, the device will
++ * raise this interrupt.
++ *
++ * Availability:
++ * SVGA_FIFO_FENCE for this command,
++ * SVGA_CAP_IRQMASK for SVGA_FIFO_FENCE_GOAL.
++ */
++
++typedef
++struct {
++ uint32 fence;
++} SVGAFifoCmdFence;
++
++
++/*
++ * SVGA_CMD_ESCAPE --
++ *
++ * Send an extended or vendor-specific variable length command.
++ * This is used for video overlay, third party plugins, and
++ * internal debugging tools. See svga_escape.h
++ *
++ * Availability:
++ * SVGA_FIFO_CAP_ESCAPE
++ */
++
++typedef
++struct {
++ uint32 nsid;
++ uint32 size;
++ /* followed by 'size' bytes of data */
++} SVGAFifoCmdEscape;
++
++
++/*
++ * SVGA_CMD_DEFINE_SCREEN --
++ *
++ * Define or redefine an SVGAScreenObject. See the description of
++ * SVGAScreenObject above. The video driver is responsible for
++ * generating new screen IDs. They should be small positive
++ * integers. The virtual device will have an implementation
++ * specific upper limit on the number of screen IDs
++ * supported. Drivers are responsible for recycling IDs. The first
++ * valid ID is zero.
++ *
++ * - Interaction with other registers:
++ *
++ * For backwards compatibility, when the GFB mode registers (WIDTH,
++ * HEIGHT, PITCHLOCK, BITS_PER_PIXEL) are modified, the SVGA device
++ * deletes all screens other than screen #0, and redefines screen
++ * #0 according to the specified mode. Drivers that use
++ * SVGA_CMD_DEFINE_SCREEN should destroy or redefine screen #0.
++ *
++ * If you use screen objects, do not use the legacy multi-mon
++ * registers (SVGA_REG_NUM_GUEST_DISPLAYS, SVGA_REG_DISPLAY_*).
++ *
++ * Availability:
++ * SVGA_FIFO_CAP_SCREEN_OBJECT
++ */
++
++typedef
++struct {
++ SVGAScreenObject screen; // Variable-length according to version
++} SVGAFifoCmdDefineScreen;
++
++
++/*
++ * SVGA_CMD_DESTROY_SCREEN --
++ *
++ * Destroy an SVGAScreenObject. Its ID is immediately available for
++ * re-use.
++ *
++ * Availability:
++ * SVGA_FIFO_CAP_SCREEN_OBJECT
++ */
++
++typedef
++struct {
++ uint32 screenId;
++} SVGAFifoCmdDestroyScreen;
++
++
++/*
++ * SVGA_CMD_DEFINE_GMRFB --
++ *
++ * This command sets a piece of SVGA device state called the
++ * Guest Memory Region Framebuffer, or GMRFB. The GMRFB is a
++ * piece of light-weight state which identifies the location and
++ * format of an image in guest memory or in BAR1. The GMRFB has
++ * an arbitrary size, and it doesn't need to match the geometry
++ * of the GFB or any screen object.
++ *
++ * The GMRFB can be redefined as often as you like. You could
++ * always use the same GMRFB, you could redefine it before
++ * rendering from a different guest screen, or you could even
++ * redefine it before every blit.
++ *
++ * There are multiple ways to use this command. The simplest way is
++ * to use it to move the framebuffer either to elsewhere in the GFB
++ * (BAR1) memory region, or to a user-defined GMR. This lets a
++ * driver use a framebuffer allocated entirely out of normal system
++ * memory, which we encourage.
++ *
++ * Another way to use this command is to set up a ring buffer of
++ * updates in GFB memory. If a driver wants to ensure that no
++ * frames are skipped by the SVGA device, it is important that the
++ * driver not modify the source data for a blit until the device is
++ * done processing the command. One efficient way to accomplish
++ * this is to use a ring of small DMA buffers. Each buffer is used
++ * for one blit, then we move on to the next buffer in the
++ * ring. The FENCE mechanism is used to protect each buffer from
++ * re-use until the device is finished with that buffer's
++ * corresponding blit.
++ *
++ * This command does not affect the meaning of SVGA_CMD_UPDATE.
++ * UPDATEs always occur from the legacy GFB memory area. This
++ * command has no support for pseudocolor GMRFBs. Currently only
++ * true-color 15, 16, and 24-bit depths are supported. Future
++ * devices may expose capabilities for additional framebuffer
++ * formats.
++ *
++ * The default GMRFB value is undefined. Drivers must always send
++ * this command at least once before performing any blit from the
++ * GMRFB.
++ *
++ * Availability:
++ * SVGA_FIFO_CAP_SCREEN_OBJECT
++ */
++
++typedef
++struct {
++ SVGAGuestPtr ptr;
++ uint32 bytesPerLine;
++ SVGAGMRImageFormat format;
++} SVGAFifoCmdDefineGMRFB;
++
++
++/*
++ * SVGA_CMD_BLIT_GMRFB_TO_SCREEN --
++ *
++ * This is a guest-to-host blit. It performs a DMA operation to
++ * copy a rectangular region of pixels from the current GMRFB to
++ * one or more Screen Objects.
++ *
++ * The destination coordinate may be specified relative to a
++ * screen's origin (if a screen ID is specified) or relative to the
++ * virtual coordinate system's origin (if the screen ID is
++ * SVGA_ID_INVALID). The actual destination may span zero or more
++ * screens, in the case of a virtual destination rect or a rect
++ * which extends off the edge of the specified screen.
++ *
++ * This command writes to the screen's "base layer": the underlying
++ * framebuffer which exists below any cursor or video overlays. No
++ * action is necessary to explicitly hide or update any overlays
++ * which exist on top of the updated region.
++ *
++ * The SVGA device is guaranteed to finish reading from the GMRFB
++ * by the time any subsequent FENCE commands are reached.
++ *
++ * This command consumes an annotation. See the
++ * SVGA_CMD_ANNOTATION_* commands for details.
++ *
++ * Availability:
++ * SVGA_FIFO_CAP_SCREEN_OBJECT
++ */
++
++typedef
++struct {
++ SVGASignedPoint srcOrigin;
++ SVGASignedRect destRect;
++ uint32 destScreenId;
++} SVGAFifoCmdBlitGMRFBToScreen;
++
++
++/*
++ * SVGA_CMD_BLIT_SCREEN_TO_GMRFB --
++ *
++ * This is a host-to-guest blit. It performs a DMA operation to
++ * copy a rectangular region of pixels from a single Screen Object
++ * back to the current GMRFB.
++ *
++ * Usage note: This command should be used rarely. It will
++ * typically be inefficient, but it is necessary for some types of
++ * synchronization between 3D (GPU) and 2D (CPU) rendering into
++ * overlapping areas of a screen.
++ *
++ * The source coordinate is specified relative to a screen's
++ * origin. The provided screen ID must be valid. If any parameters
++ * are invalid, the resulting pixel values are undefined.
++ *
++ * This command reads the screen's "base layer". Overlays like
++ * video and cursor are not included, but any data which was sent
++ * using a blit-to-screen primitive will be available, no matter
++ * whether the data's original source was the GMRFB or the 3D
++ * acceleration hardware.
++ *
++ * Note that our guest-to-host blits and host-to-guest blits aren't
++ * symmetric in their current implementation. While the parameters
++ * are identical, host-to-guest blits are a lot less featureful.
++ * They do not support clipping: If the source parameters don't
++ * fully fit within a screen, the blit fails. They must originate
++ * from exactly one screen. Virtual coordinates are not directly
++ * supported.
++ *
++ * Host-to-guest blits do support the same set of GMRFB formats
++ * offered by guest-to-host blits.
++ *
++ * The SVGA device is guaranteed to finish writing to the GMRFB by
++ * the time any subsequent FENCE commands are reached.
++ *
++ * Availability:
++ * SVGA_FIFO_CAP_SCREEN_OBJECT
++ */
++
++typedef
++struct {
++ SVGASignedPoint destOrigin;
++ SVGASignedRect srcRect;
++ uint32 srcScreenId;
++} SVGAFifoCmdBlitScreenToGMRFB;
++
++
++/*
++ * SVGA_CMD_ANNOTATION_FILL --
++ *
++ * This is a blit annotation. This command stores a small piece of
++ * device state which is consumed by the next blit-to-screen
++ * command. The state is only cleared by commands which are
++ * specifically documented as consuming an annotation. Other
++ * commands (such as ESCAPEs for debugging) may intervene between
++ * the annotation and its associated blit.
++ *
++ * This annotation is a promise about the contents of the next
++ * blit: The video driver is guaranteeing that all pixels in that
++ * blit will have the same value, specified here as a color in
++ * SVGAColorBGRX format.
++ *
++ * The SVGA device can still render the blit correctly even if it
++ * ignores this annotation, but the annotation may allow it to
++ * perform the blit more efficiently, for example by ignoring the
++ * source data and performing a fill in hardware.
++ *
++ * This annotation is most important for performance when the
++ * user's display is being remoted over a network connection.
++ *
++ * Availability:
++ * SVGA_FIFO_CAP_SCREEN_OBJECT
++ */
++
++typedef
++struct {
++ SVGAColorBGRX color;
++} SVGAFifoCmdAnnotationFill;
++
++
++/*
++ * SVGA_CMD_ANNOTATION_COPY --
++ *
++ * This is a blit annotation. See SVGA_CMD_ANNOTATION_FILL for more
++ * information about annotations.
++ *
++ * This annotation is a promise about the contents of the next
++ * blit: The video driver is guaranteeing that all pixels in that
++ * blit will have the same value as those which already exist at an
++ * identically-sized region on the same or a different screen.
++ *
++ * Note that the source pixels for the COPY in this annotation are
++ * sampled before applying the anqnotation's associated blit. They
++ * are allowed to overlap with the blit's destination pixels.
++ *
++ * The copy source rectangle is specified the same way as the blit
++ * destination: it can be a rectangle which spans zero or more
++ * screens, specified relative to either a screen or to the virtual
++ * coordinate system's origin. If the source rectangle includes
++ * pixels which are not from exactly one screen, the results are
++ * undefined.
++ *
++ * Availability:
++ * SVGA_FIFO_CAP_SCREEN_OBJECT
++ */
++
++typedef
++struct {
++ SVGASignedPoint srcOrigin;
++ uint32 srcScreenId;
++} SVGAFifoCmdAnnotationCopy;
++
++#endif
+diff --git a/drivers/gpu/drm/vmwgfx/svga_types.h b/drivers/gpu/drm/vmwgfx/svga_types.h
+new file mode 100644
+index 0000000..55836de
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/svga_types.h
+@@ -0,0 +1,45 @@
++/**************************************************************************
++ *
++ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++/**
++ * Silly typedefs for the svga headers. Currently the headers are shared
++ * between all components that talk to svga. And as such the headers are
++ * are in a completely different style and use weird defines.
++ *
++ * This file lets all the ugly be prefixed with svga*.
++ */
++
++#ifndef _SVGA_TYPES_H_
++#define _SVGA_TYPES_H_
++
++typedef uint16_t uint16;
++typedef uint32_t uint32;
++typedef uint8_t uint8;
++typedef int32_t int32;
++typedef bool Bool;
++
++#endif
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c
+new file mode 100644
+index 0000000..825ebe3
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_buffer.c
+@@ -0,0 +1,252 @@
++/**************************************************************************
++ *
++ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++#include "vmwgfx_drv.h"
++#include "ttm/ttm_bo_driver.h"
++#include "ttm/ttm_placement.h"
++
++static uint32_t vram_placement_flags = TTM_PL_FLAG_VRAM |
++ TTM_PL_FLAG_CACHED;
++
++static uint32_t vram_ne_placement_flags = TTM_PL_FLAG_VRAM |
++ TTM_PL_FLAG_CACHED |
++ TTM_PL_FLAG_NO_EVICT;
++
++static uint32_t sys_placement_flags = TTM_PL_FLAG_SYSTEM |
++ TTM_PL_FLAG_CACHED;
++
++struct ttm_placement vmw_vram_placement = {
++ .fpfn = 0,
++ .lpfn = 0,
++ .num_placement = 1,
++ .placement = &vram_placement_flags,
++ .num_busy_placement = 1,
++ .busy_placement = &vram_placement_flags
++};
++
++struct ttm_placement vmw_vram_sys_placement = {
++ .fpfn = 0,
++ .lpfn = 0,
++ .num_placement = 1,
++ .placement = &vram_placement_flags,
++ .num_busy_placement = 1,
++ .busy_placement = &sys_placement_flags
++};
++
++struct ttm_placement vmw_vram_ne_placement = {
++ .fpfn = 0,
++ .lpfn = 0,
++ .num_placement = 1,
++ .placement = &vram_ne_placement_flags,
++ .num_busy_placement = 1,
++ .busy_placement = &vram_ne_placement_flags
++};
++
++struct ttm_placement vmw_sys_placement = {
++ .fpfn = 0,
++ .lpfn = 0,
++ .num_placement = 1,
++ .placement = &sys_placement_flags,
++ .num_busy_placement = 1,
++ .busy_placement = &sys_placement_flags
++};
++
++struct vmw_ttm_backend {
++ struct ttm_backend backend;
++};
++
++static int vmw_ttm_populate(struct ttm_backend *backend,
++ unsigned long num_pages, struct page **pages,
++ struct page *dummy_read_page)
++{
++ return 0;
++}
++
++static int vmw_ttm_bind(struct ttm_backend *backend, struct ttm_mem_reg *bo_mem)
++{
++ return 0;
++}
++
++static int vmw_ttm_unbind(struct ttm_backend *backend)
++{
++ return 0;
++}
++
++static void vmw_ttm_clear(struct ttm_backend *backend)
++{
++}
++
++static void vmw_ttm_destroy(struct ttm_backend *backend)
++{
++ struct vmw_ttm_backend *vmw_be =
++ container_of(backend, struct vmw_ttm_backend, backend);
++
++ kfree(vmw_be);
++}
++
++static struct ttm_backend_func vmw_ttm_func = {
++ .populate = vmw_ttm_populate,
++ .clear = vmw_ttm_clear,
++ .bind = vmw_ttm_bind,
++ .unbind = vmw_ttm_unbind,
++ .destroy = vmw_ttm_destroy,
++};
++
++struct ttm_backend *vmw_ttm_backend_init(struct ttm_bo_device *bdev)
++{
++ struct vmw_ttm_backend *vmw_be;
++
++ vmw_be = kmalloc(sizeof(*vmw_be), GFP_KERNEL);
++ if (!vmw_be)
++ return NULL;
++
++ vmw_be->backend.func = &vmw_ttm_func;
++
++ return &vmw_be->backend;
++}
++
++int vmw_invalidate_caches(struct ttm_bo_device *bdev, uint32_t flags)
++{
++ return 0;
++}
++
++int vmw_init_mem_type(struct ttm_bo_device *bdev, uint32_t type,
++ struct ttm_mem_type_manager *man)
++{
++ struct vmw_private *dev_priv =
++ container_of(bdev, struct vmw_private, bdev);
++
++ switch (type) {
++ case TTM_PL_SYSTEM:
++ /* System memory */
++
++ man->flags = TTM_MEMTYPE_FLAG_MAPPABLE;
++ man->available_caching = TTM_PL_MASK_CACHING;
++ man->default_caching = TTM_PL_FLAG_CACHED;
++ break;
++ case TTM_PL_VRAM:
++ /* "On-card" video ram */
++ man->gpu_offset = 0;
++ man->io_offset = dev_priv->vram_start;
++ man->io_size = dev_priv->vram_size;
++ man->flags = TTM_MEMTYPE_FLAG_FIXED |
++ TTM_MEMTYPE_FLAG_NEEDS_IOREMAP | TTM_MEMTYPE_FLAG_MAPPABLE;
++ man->io_addr = NULL;
++ man->available_caching = TTM_PL_MASK_CACHING;
++ man->default_caching = TTM_PL_FLAG_WC;
++ break;
++ default:
++ DRM_ERROR("Unsupported memory type %u\n", (unsigned)type);
++ return -EINVAL;
++ }
++ return 0;
++}
++
++void vmw_evict_flags(struct ttm_buffer_object *bo,
++ struct ttm_placement *placement)
++{
++ *placement = vmw_sys_placement;
++}
++
++/**
++ * FIXME: Proper access checks on buffers.
++ */
++
++static int vmw_verify_access(struct ttm_buffer_object *bo, struct file *filp)
++{
++ return 0;
++}
++
++static void vmw_move_notify(struct ttm_buffer_object *bo,
++ struct ttm_mem_reg *new_mem)
++{
++ if (new_mem->mem_type != TTM_PL_SYSTEM)
++ vmw_dmabuf_gmr_unbind(bo);
++}
++
++static void vmw_swap_notify(struct ttm_buffer_object *bo)
++{
++ vmw_dmabuf_gmr_unbind(bo);
++}
++
++/**
++ * FIXME: We're using the old vmware polling method to sync.
++ * Do this with fences instead.
++ */
++
++static void *vmw_sync_obj_ref(void *sync_obj)
++{
++ return sync_obj;
++}
++
++static void vmw_sync_obj_unref(void **sync_obj)
++{
++ *sync_obj = NULL;
++}
++
++static int vmw_sync_obj_flush(void *sync_obj, void *sync_arg)
++{
++ struct vmw_private *dev_priv = (struct vmw_private *)sync_arg;
++
++ mutex_lock(&dev_priv->hw_mutex);
++ vmw_write(dev_priv, SVGA_REG_SYNC, SVGA_SYNC_GENERIC);
++ mutex_unlock(&dev_priv->hw_mutex);
++ return 0;
++}
++
++static bool vmw_sync_obj_signaled(void *sync_obj, void *sync_arg)
++{
++ struct vmw_private *dev_priv = (struct vmw_private *)sync_arg;
++ uint32_t sequence = (unsigned long) sync_obj;
++
++ return vmw_fence_signaled(dev_priv, sequence);
++}
++
++static int vmw_sync_obj_wait(void *sync_obj, void *sync_arg,
++ bool lazy, bool interruptible)
++{
++ struct vmw_private *dev_priv = (struct vmw_private *)sync_arg;
++ uint32_t sequence = (unsigned long) sync_obj;
++
++ return vmw_wait_fence(dev_priv, false, sequence, false, 3*HZ);
++}
++
++struct ttm_bo_driver vmw_bo_driver = {
++ .create_ttm_backend_entry = vmw_ttm_backend_init,
++ .invalidate_caches = vmw_invalidate_caches,
++ .init_mem_type = vmw_init_mem_type,
++ .evict_flags = vmw_evict_flags,
++ .move = NULL,
++ .verify_access = vmw_verify_access,
++ .sync_obj_signaled = vmw_sync_obj_signaled,
++ .sync_obj_wait = vmw_sync_obj_wait,
++ .sync_obj_flush = vmw_sync_obj_flush,
++ .sync_obj_unref = vmw_sync_obj_unref,
++ .sync_obj_ref = vmw_sync_obj_ref,
++ .move_notify = vmw_move_notify,
++ .swap_notify = vmw_swap_notify
++};
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+new file mode 100644
+index 0000000..a6e8f68
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.c
+@@ -0,0 +1,800 @@
++/**************************************************************************
++ *
++ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++#include "drmP.h"
++#include "vmwgfx_drv.h"
++#include "ttm/ttm_placement.h"
++#include "ttm/ttm_bo_driver.h"
++#include "ttm/ttm_object.h"
++#include "ttm/ttm_module.h"
++
++#define VMWGFX_DRIVER_NAME "vmwgfx"
++#define VMWGFX_DRIVER_DESC "Linux drm driver for VMware graphics devices"
++#define VMWGFX_CHIP_SVGAII 0
++#define VMW_FB_RESERVATION 0
++
++/**
++ * Fully encoded drm commands. Might move to vmw_drm.h
++ */
++
++#define DRM_IOCTL_VMW_GET_PARAM \
++ DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_GET_PARAM, \
++ struct drm_vmw_getparam_arg)
++#define DRM_IOCTL_VMW_ALLOC_DMABUF \
++ DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_ALLOC_DMABUF, \
++ union drm_vmw_alloc_dmabuf_arg)
++#define DRM_IOCTL_VMW_UNREF_DMABUF \
++ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_DMABUF, \
++ struct drm_vmw_unref_dmabuf_arg)
++#define DRM_IOCTL_VMW_CURSOR_BYPASS \
++ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_CURSOR_BYPASS, \
++ struct drm_vmw_cursor_bypass_arg)
++
++#define DRM_IOCTL_VMW_CONTROL_STREAM \
++ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_CONTROL_STREAM, \
++ struct drm_vmw_control_stream_arg)
++#define DRM_IOCTL_VMW_CLAIM_STREAM \
++ DRM_IOR(DRM_COMMAND_BASE + DRM_VMW_CLAIM_STREAM, \
++ struct drm_vmw_stream_arg)
++#define DRM_IOCTL_VMW_UNREF_STREAM \
++ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_STREAM, \
++ struct drm_vmw_stream_arg)
++
++#define DRM_IOCTL_VMW_CREATE_CONTEXT \
++ DRM_IOR(DRM_COMMAND_BASE + DRM_VMW_CREATE_CONTEXT, \
++ struct drm_vmw_context_arg)
++#define DRM_IOCTL_VMW_UNREF_CONTEXT \
++ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_CONTEXT, \
++ struct drm_vmw_context_arg)
++#define DRM_IOCTL_VMW_CREATE_SURFACE \
++ DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_CREATE_SURFACE, \
++ union drm_vmw_surface_create_arg)
++#define DRM_IOCTL_VMW_UNREF_SURFACE \
++ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_UNREF_SURFACE, \
++ struct drm_vmw_surface_arg)
++#define DRM_IOCTL_VMW_REF_SURFACE \
++ DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_REF_SURFACE, \
++ union drm_vmw_surface_reference_arg)
++#define DRM_IOCTL_VMW_EXECBUF \
++ DRM_IOW(DRM_COMMAND_BASE + DRM_VMW_EXECBUF, \
++ struct drm_vmw_execbuf_arg)
++#define DRM_IOCTL_VMW_FIFO_DEBUG \
++ DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_FIFO_DEBUG, \
++ struct drm_vmw_fifo_debug_arg)
++#define DRM_IOCTL_VMW_FENCE_WAIT \
++ DRM_IOWR(DRM_COMMAND_BASE + DRM_VMW_FENCE_WAIT, \
++ struct drm_vmw_fence_wait_arg)
++
++
++/**
++ * The core DRM version of this macro doesn't account for
++ * DRM_COMMAND_BASE.
++ */
++
++#define VMW_IOCTL_DEF(ioctl, func, flags) \
++ [DRM_IOCTL_NR(ioctl) - DRM_COMMAND_BASE] = {ioctl, flags, func}
++
++/**
++ * Ioctl definitions.
++ */
++
++static struct drm_ioctl_desc vmw_ioctls[] = {
++ VMW_IOCTL_DEF(DRM_IOCTL_VMW_GET_PARAM, vmw_getparam_ioctl,
++ DRM_AUTH | DRM_UNLOCKED),
++ VMW_IOCTL_DEF(DRM_IOCTL_VMW_ALLOC_DMABUF, vmw_dmabuf_alloc_ioctl,
++ DRM_AUTH | DRM_UNLOCKED),
++ VMW_IOCTL_DEF(DRM_IOCTL_VMW_UNREF_DMABUF, vmw_dmabuf_unref_ioctl,
++ DRM_AUTH | DRM_UNLOCKED),
++ VMW_IOCTL_DEF(DRM_IOCTL_VMW_CURSOR_BYPASS,
++ vmw_kms_cursor_bypass_ioctl,
++ DRM_MASTER | DRM_CONTROL_ALLOW | DRM_UNLOCKED),
++
++ VMW_IOCTL_DEF(DRM_IOCTL_VMW_CONTROL_STREAM, vmw_overlay_ioctl,
++ DRM_MASTER | DRM_CONTROL_ALLOW | DRM_UNLOCKED),
++ VMW_IOCTL_DEF(DRM_IOCTL_VMW_CLAIM_STREAM, vmw_stream_claim_ioctl,
++ DRM_MASTER | DRM_CONTROL_ALLOW | DRM_UNLOCKED),
++ VMW_IOCTL_DEF(DRM_IOCTL_VMW_UNREF_STREAM, vmw_stream_unref_ioctl,
++ DRM_MASTER | DRM_CONTROL_ALLOW | DRM_UNLOCKED),
++
++ VMW_IOCTL_DEF(DRM_IOCTL_VMW_CREATE_CONTEXT, vmw_context_define_ioctl,
++ DRM_AUTH | DRM_UNLOCKED),
++ VMW_IOCTL_DEF(DRM_IOCTL_VMW_UNREF_CONTEXT, vmw_context_destroy_ioctl,
++ DRM_AUTH | DRM_UNLOCKED),
++ VMW_IOCTL_DEF(DRM_IOCTL_VMW_CREATE_SURFACE, vmw_surface_define_ioctl,
++ DRM_AUTH | DRM_UNLOCKED),
++ VMW_IOCTL_DEF(DRM_IOCTL_VMW_UNREF_SURFACE, vmw_surface_destroy_ioctl,
++ DRM_AUTH | DRM_UNLOCKED),
++ VMW_IOCTL_DEF(DRM_IOCTL_VMW_REF_SURFACE, vmw_surface_reference_ioctl,
++ DRM_AUTH | DRM_UNLOCKED),
++ VMW_IOCTL_DEF(DRM_IOCTL_VMW_EXECBUF, vmw_execbuf_ioctl,
++ DRM_AUTH | DRM_UNLOCKED),
++ VMW_IOCTL_DEF(DRM_IOCTL_VMW_FIFO_DEBUG, vmw_fifo_debug_ioctl,
++ DRM_AUTH | DRM_ROOT_ONLY | DRM_MASTER | DRM_UNLOCKED),
++ VMW_IOCTL_DEF(DRM_IOCTL_VMW_FENCE_WAIT, vmw_fence_wait_ioctl,
++ DRM_AUTH | DRM_UNLOCKED)
++};
++
++static struct pci_device_id vmw_pci_id_list[] = {
++ {0x15ad, 0x0405, PCI_ANY_ID, PCI_ANY_ID, 0, 0, VMWGFX_CHIP_SVGAII},
++ {0, 0, 0}
++};
++
++static char *vmw_devname = "vmwgfx";
++
++static int vmw_probe(struct pci_dev *, const struct pci_device_id *);
++static void vmw_master_init(struct vmw_master *);
++static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,
++ void *ptr);
++
++static void vmw_print_capabilities(uint32_t capabilities)
++{
++ DRM_INFO("Capabilities:\n");
++ if (capabilities & SVGA_CAP_RECT_COPY)
++ DRM_INFO(" Rect copy.\n");
++ if (capabilities & SVGA_CAP_CURSOR)
++ DRM_INFO(" Cursor.\n");
++ if (capabilities & SVGA_CAP_CURSOR_BYPASS)
++ DRM_INFO(" Cursor bypass.\n");
++ if (capabilities & SVGA_CAP_CURSOR_BYPASS_2)
++ DRM_INFO(" Cursor bypass 2.\n");
++ if (capabilities & SVGA_CAP_8BIT_EMULATION)
++ DRM_INFO(" 8bit emulation.\n");
++ if (capabilities & SVGA_CAP_ALPHA_CURSOR)
++ DRM_INFO(" Alpha cursor.\n");
++ if (capabilities & SVGA_CAP_3D)
++ DRM_INFO(" 3D.\n");
++ if (capabilities & SVGA_CAP_EXTENDED_FIFO)
++ DRM_INFO(" Extended Fifo.\n");
++ if (capabilities & SVGA_CAP_MULTIMON)
++ DRM_INFO(" Multimon.\n");
++ if (capabilities & SVGA_CAP_PITCHLOCK)
++ DRM_INFO(" Pitchlock.\n");
++ if (capabilities & SVGA_CAP_IRQMASK)
++ DRM_INFO(" Irq mask.\n");
++ if (capabilities & SVGA_CAP_DISPLAY_TOPOLOGY)
++ DRM_INFO(" Display Topology.\n");
++ if (capabilities & SVGA_CAP_GMR)
++ DRM_INFO(" GMR.\n");
++ if (capabilities & SVGA_CAP_TRACES)
++ DRM_INFO(" Traces.\n");
++}
++
++static int vmw_request_device(struct vmw_private *dev_priv)
++{
++ int ret;
++
++ vmw_kms_save_vga(dev_priv);
++
++ ret = vmw_fifo_init(dev_priv, &dev_priv->fifo);
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("Unable to initialize FIFO.\n");
++ return ret;
++ }
++
++ return 0;
++}
++
++static void vmw_release_device(struct vmw_private *dev_priv)
++{
++ vmw_fifo_release(dev_priv, &dev_priv->fifo);
++ vmw_kms_restore_vga(dev_priv);
++}
++
++
++static int vmw_driver_load(struct drm_device *dev, unsigned long chipset)
++{
++ struct vmw_private *dev_priv;
++ int ret;
++ uint32_t svga_id;
++
++ dev_priv = kzalloc(sizeof(*dev_priv), GFP_KERNEL);
++ if (unlikely(dev_priv == NULL)) {
++ DRM_ERROR("Failed allocating a device private struct.\n");
++ return -ENOMEM;
++ }
++ memset(dev_priv, 0, sizeof(*dev_priv));
++
++ dev_priv->dev = dev;
++ dev_priv->vmw_chipset = chipset;
++ dev_priv->last_read_sequence = (uint32_t) -100;
++ mutex_init(&dev_priv->hw_mutex);
++ mutex_init(&dev_priv->cmdbuf_mutex);
++ rwlock_init(&dev_priv->resource_lock);
++ idr_init(&dev_priv->context_idr);
++ idr_init(&dev_priv->surface_idr);
++ idr_init(&dev_priv->stream_idr);
++ ida_init(&dev_priv->gmr_ida);
++ mutex_init(&dev_priv->init_mutex);
++ init_waitqueue_head(&dev_priv->fence_queue);
++ init_waitqueue_head(&dev_priv->fifo_queue);
++ atomic_set(&dev_priv->fence_queue_waiters, 0);
++ atomic_set(&dev_priv->fifo_queue_waiters, 0);
++ INIT_LIST_HEAD(&dev_priv->gmr_lru);
++
++ dev_priv->io_start = pci_resource_start(dev->pdev, 0);
++ dev_priv->vram_start = pci_resource_start(dev->pdev, 1);
++ dev_priv->mmio_start = pci_resource_start(dev->pdev, 2);
++
++ mutex_lock(&dev_priv->hw_mutex);
++
++ vmw_write(dev_priv, SVGA_REG_ID, SVGA_ID_2);
++ svga_id = vmw_read(dev_priv, SVGA_REG_ID);
++ if (svga_id != SVGA_ID_2) {
++ ret = -ENOSYS;
++ DRM_ERROR("Unsuported SVGA ID 0x%x\n", svga_id);
++ mutex_unlock(&dev_priv->hw_mutex);
++ goto out_err0;
++ }
++
++ dev_priv->capabilities = vmw_read(dev_priv, SVGA_REG_CAPABILITIES);
++
++ if (dev_priv->capabilities & SVGA_CAP_GMR) {
++ dev_priv->max_gmr_descriptors =
++ vmw_read(dev_priv,
++ SVGA_REG_GMR_MAX_DESCRIPTOR_LENGTH);
++ dev_priv->max_gmr_ids =
++ vmw_read(dev_priv, SVGA_REG_GMR_MAX_IDS);
++ }
++
++ dev_priv->vram_size = vmw_read(dev_priv, SVGA_REG_VRAM_SIZE);
++ dev_priv->mmio_size = vmw_read(dev_priv, SVGA_REG_MEM_SIZE);
++ dev_priv->fb_max_width = vmw_read(dev_priv, SVGA_REG_MAX_WIDTH);
++ dev_priv->fb_max_height = vmw_read(dev_priv, SVGA_REG_MAX_HEIGHT);
++
++ mutex_unlock(&dev_priv->hw_mutex);
++
++ vmw_print_capabilities(dev_priv->capabilities);
++
++ if (dev_priv->capabilities & SVGA_CAP_GMR) {
++ DRM_INFO("Max GMR ids is %u\n",
++ (unsigned)dev_priv->max_gmr_ids);
++ DRM_INFO("Max GMR descriptors is %u\n",
++ (unsigned)dev_priv->max_gmr_descriptors);
++ }
++ DRM_INFO("VRAM at 0x%08x size is %u kiB\n",
++ dev_priv->vram_start, dev_priv->vram_size / 1024);
++ DRM_INFO("MMIO at 0x%08x size is %u kiB\n",
++ dev_priv->mmio_start, dev_priv->mmio_size / 1024);
++
++ ret = vmw_ttm_global_init(dev_priv);
++ if (unlikely(ret != 0))
++ goto out_err0;
++
++
++ vmw_master_init(&dev_priv->fbdev_master);
++ ttm_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM);
++ dev_priv->active_master = &dev_priv->fbdev_master;
++
++
++ ret = ttm_bo_device_init(&dev_priv->bdev,
++ dev_priv->bo_global_ref.ref.object,
++ &vmw_bo_driver, VMWGFX_FILE_PAGE_OFFSET,
++ false);
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("Failed initializing TTM buffer object driver.\n");
++ goto out_err1;
++ }
++
++ ret = ttm_bo_init_mm(&dev_priv->bdev, TTM_PL_VRAM,
++ (dev_priv->vram_size >> PAGE_SHIFT));
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("Failed initializing memory manager for VRAM.\n");
++ goto out_err2;
++ }
++
++ dev_priv->mmio_mtrr = drm_mtrr_add(dev_priv->mmio_start,
++ dev_priv->mmio_size, DRM_MTRR_WC);
++
++ dev_priv->mmio_virt = ioremap_wc(dev_priv->mmio_start,
++ dev_priv->mmio_size);
++
++ if (unlikely(dev_priv->mmio_virt == NULL)) {
++ ret = -ENOMEM;
++ DRM_ERROR("Failed mapping MMIO.\n");
++ goto out_err3;
++ }
++
++ dev_priv->tdev = ttm_object_device_init
++ (dev_priv->mem_global_ref.object, 12);
++
++ if (unlikely(dev_priv->tdev == NULL)) {
++ DRM_ERROR("Unable to initialize TTM object management.\n");
++ ret = -ENOMEM;
++ goto out_err4;
++ }
++
++ dev->dev_private = dev_priv;
++
++ if (!dev->devname)
++ dev->devname = vmw_devname;
++
++ if (dev_priv->capabilities & SVGA_CAP_IRQMASK) {
++ ret = drm_irq_install(dev);
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("Failed installing irq: %d\n", ret);
++ goto out_no_irq;
++ }
++ }
++
++ ret = pci_request_regions(dev->pdev, "vmwgfx probe");
++ dev_priv->stealth = (ret != 0);
++ if (dev_priv->stealth) {
++ /**
++ * Request at least the mmio PCI resource.
++ */
++
++ DRM_INFO("It appears like vesafb is loaded. "
++ "Ignore above error if any. Entering stealth mode.\n");
++ ret = pci_request_region(dev->pdev, 2, "vmwgfx stealth probe");
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("Failed reserving the SVGA MMIO resource.\n");
++ goto out_no_device;
++ }
++ vmw_kms_init(dev_priv);
++ vmw_overlay_init(dev_priv);
++ } else {
++ ret = vmw_request_device(dev_priv);
++ if (unlikely(ret != 0))
++ goto out_no_device;
++ vmw_kms_init(dev_priv);
++ vmw_overlay_init(dev_priv);
++ vmw_fb_init(dev_priv);
++ }
++
++ dev_priv->pm_nb.notifier_call = vmwgfx_pm_notifier;
++ register_pm_notifier(&dev_priv->pm_nb);
++
++ DRM_INFO("%s", vmw_fifo_have_3d(dev_priv) ? "Have 3D\n" : "No 3D\n");
++
++ return 0;
++
++out_no_device:
++ if (dev_priv->capabilities & SVGA_CAP_IRQMASK)
++ drm_irq_uninstall(dev_priv->dev);
++ if (dev->devname == vmw_devname)
++ dev->devname = NULL;
++out_no_irq:
++ ttm_object_device_release(&dev_priv->tdev);
++out_err4:
++ iounmap(dev_priv->mmio_virt);
++out_err3:
++ drm_mtrr_del(dev_priv->mmio_mtrr, dev_priv->mmio_start,
++ dev_priv->mmio_size, DRM_MTRR_WC);
++ (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM);
++out_err2:
++ (void)ttm_bo_device_release(&dev_priv->bdev);
++out_err1:
++ vmw_ttm_global_release(dev_priv);
++out_err0:
++ ida_destroy(&dev_priv->gmr_ida);
++ idr_destroy(&dev_priv->surface_idr);
++ idr_destroy(&dev_priv->context_idr);
++ idr_destroy(&dev_priv->stream_idr);
++ kfree(dev_priv);
++ return ret;
++}
++
++static int vmw_driver_unload(struct drm_device *dev)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++
++ DRM_INFO(VMWGFX_DRIVER_NAME " unload.\n");
++
++ unregister_pm_notifier(&dev_priv->pm_nb);
++
++ if (!dev_priv->stealth) {
++ vmw_fb_close(dev_priv);
++ vmw_kms_close(dev_priv);
++ vmw_overlay_close(dev_priv);
++ vmw_release_device(dev_priv);
++ pci_release_regions(dev->pdev);
++ } else {
++ vmw_kms_close(dev_priv);
++ vmw_overlay_close(dev_priv);
++ pci_release_region(dev->pdev, 2);
++ }
++ if (dev_priv->capabilities & SVGA_CAP_IRQMASK)
++ drm_irq_uninstall(dev_priv->dev);
++ if (dev->devname == vmw_devname)
++ dev->devname = NULL;
++ ttm_object_device_release(&dev_priv->tdev);
++ iounmap(dev_priv->mmio_virt);
++ drm_mtrr_del(dev_priv->mmio_mtrr, dev_priv->mmio_start,
++ dev_priv->mmio_size, DRM_MTRR_WC);
++ (void)ttm_bo_clean_mm(&dev_priv->bdev, TTM_PL_VRAM);
++ (void)ttm_bo_device_release(&dev_priv->bdev);
++ vmw_ttm_global_release(dev_priv);
++ ida_destroy(&dev_priv->gmr_ida);
++ idr_destroy(&dev_priv->surface_idr);
++ idr_destroy(&dev_priv->context_idr);
++ idr_destroy(&dev_priv->stream_idr);
++
++ kfree(dev_priv);
++
++ return 0;
++}
++
++static void vmw_postclose(struct drm_device *dev,
++ struct drm_file *file_priv)
++{
++ struct vmw_fpriv *vmw_fp;
++
++ vmw_fp = vmw_fpriv(file_priv);
++ ttm_object_file_release(&vmw_fp->tfile);
++ if (vmw_fp->locked_master)
++ drm_master_put(&vmw_fp->locked_master);
++ kfree(vmw_fp);
++}
++
++static int vmw_driver_open(struct drm_device *dev, struct drm_file *file_priv)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ struct vmw_fpriv *vmw_fp;
++ int ret = -ENOMEM;
++
++ vmw_fp = kzalloc(sizeof(*vmw_fp), GFP_KERNEL);
++ if (unlikely(vmw_fp == NULL))
++ return ret;
++
++ vmw_fp->tfile = ttm_object_file_init(dev_priv->tdev, 10);
++ if (unlikely(vmw_fp->tfile == NULL))
++ goto out_no_tfile;
++
++ file_priv->driver_priv = vmw_fp;
++
++ if (unlikely(dev_priv->bdev.dev_mapping == NULL))
++ dev_priv->bdev.dev_mapping =
++ file_priv->filp->f_path.dentry->d_inode->i_mapping;
++
++ return 0;
++
++out_no_tfile:
++ kfree(vmw_fp);
++ return ret;
++}
++
++static long vmw_unlocked_ioctl(struct file *filp, unsigned int cmd,
++ unsigned long arg)
++{
++ struct drm_file *file_priv = filp->private_data;
++ struct drm_device *dev = file_priv->minor->dev;
++ unsigned int nr = DRM_IOCTL_NR(cmd);
++
++ /*
++ * Do extra checking on driver private ioctls.
++ */
++
++ if ((nr >= DRM_COMMAND_BASE) && (nr < DRM_COMMAND_END)
++ && (nr < DRM_COMMAND_BASE + dev->driver->num_ioctls)) {
++ struct drm_ioctl_desc *ioctl =
++ &vmw_ioctls[nr - DRM_COMMAND_BASE];
++
++ if (unlikely(ioctl->cmd != cmd)) {
++ DRM_ERROR("Invalid command format, ioctl %d\n",
++ nr - DRM_COMMAND_BASE);
++ return -EINVAL;
++ }
++ }
++
++ return drm_ioctl(filp, cmd, arg);
++}
++
++static int vmw_firstopen(struct drm_device *dev)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ dev_priv->is_opened = true;
++
++ return 0;
++}
++
++static void vmw_lastclose(struct drm_device *dev)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ struct drm_crtc *crtc;
++ struct drm_mode_set set;
++ int ret;
++
++ /**
++ * Do nothing on the lastclose call from drm_unload.
++ */
++
++ if (!dev_priv->is_opened)
++ return;
++
++ dev_priv->is_opened = false;
++ set.x = 0;
++ set.y = 0;
++ set.fb = NULL;
++ set.mode = NULL;
++ set.connectors = NULL;
++ set.num_connectors = 0;
++
++ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
++ set.crtc = crtc;
++ ret = crtc->funcs->set_config(&set);
++ WARN_ON(ret != 0);
++ }
++
++}
++
++static void vmw_master_init(struct vmw_master *vmaster)
++{
++ ttm_lock_init(&vmaster->lock);
++}
++
++static int vmw_master_create(struct drm_device *dev,
++ struct drm_master *master)
++{
++ struct vmw_master *vmaster;
++
++ DRM_INFO("Master create.\n");
++ vmaster = kzalloc(sizeof(*vmaster), GFP_KERNEL);
++ if (unlikely(vmaster == NULL))
++ return -ENOMEM;
++
++ ttm_lock_init(&vmaster->lock);
++ ttm_lock_set_kill(&vmaster->lock, true, SIGTERM);
++ master->driver_priv = vmaster;
++
++ return 0;
++}
++
++static void vmw_master_destroy(struct drm_device *dev,
++ struct drm_master *master)
++{
++ struct vmw_master *vmaster = vmw_master(master);
++
++ DRM_INFO("Master destroy.\n");
++ master->driver_priv = NULL;
++ kfree(vmaster);
++}
++
++
++static int vmw_master_set(struct drm_device *dev,
++ struct drm_file *file_priv,
++ bool from_open)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
++ struct vmw_master *active = dev_priv->active_master;
++ struct vmw_master *vmaster = vmw_master(file_priv->master);
++ int ret = 0;
++
++ DRM_INFO("Master set.\n");
++ if (dev_priv->stealth) {
++ ret = vmw_request_device(dev_priv);
++ if (unlikely(ret != 0))
++ return ret;
++ }
++
++ if (active) {
++ BUG_ON(active != &dev_priv->fbdev_master);
++ ret = ttm_vt_lock(&active->lock, false, vmw_fp->tfile);
++ if (unlikely(ret != 0))
++ goto out_no_active_lock;
++
++ ttm_lock_set_kill(&active->lock, true, SIGTERM);
++ ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM);
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("Unable to clean VRAM on "
++ "master drop.\n");
++ }
++
++ dev_priv->active_master = NULL;
++ }
++
++ ttm_lock_set_kill(&vmaster->lock, false, SIGTERM);
++ if (!from_open) {
++ ttm_vt_unlock(&vmaster->lock);
++ BUG_ON(vmw_fp->locked_master != file_priv->master);
++ drm_master_put(&vmw_fp->locked_master);
++ }
++
++ dev_priv->active_master = vmaster;
++
++ return 0;
++
++out_no_active_lock:
++ vmw_release_device(dev_priv);
++ return ret;
++}
++
++static void vmw_master_drop(struct drm_device *dev,
++ struct drm_file *file_priv,
++ bool from_release)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ struct vmw_fpriv *vmw_fp = vmw_fpriv(file_priv);
++ struct vmw_master *vmaster = vmw_master(file_priv->master);
++ int ret;
++
++ DRM_INFO("Master drop.\n");
++
++ /**
++ * Make sure the master doesn't disappear while we have
++ * it locked.
++ */
++
++ vmw_fp->locked_master = drm_master_get(file_priv->master);
++ ret = ttm_vt_lock(&vmaster->lock, false, vmw_fp->tfile);
++
++ if (unlikely((ret != 0))) {
++ DRM_ERROR("Unable to lock TTM at VT switch.\n");
++ drm_master_put(&vmw_fp->locked_master);
++ }
++
++ ttm_lock_set_kill(&vmaster->lock, true, SIGTERM);
++
++ if (dev_priv->stealth) {
++ ret = ttm_bo_evict_mm(&dev_priv->bdev, TTM_PL_VRAM);
++ if (unlikely(ret != 0))
++ DRM_ERROR("Unable to clean VRAM on master drop.\n");
++ vmw_release_device(dev_priv);
++ }
++ dev_priv->active_master = &dev_priv->fbdev_master;
++ ttm_lock_set_kill(&dev_priv->fbdev_master.lock, false, SIGTERM);
++ ttm_vt_unlock(&dev_priv->fbdev_master.lock);
++
++ if (!dev_priv->stealth)
++ vmw_fb_on(dev_priv);
++}
++
++
++static void vmw_remove(struct pci_dev *pdev)
++{
++ struct drm_device *dev = pci_get_drvdata(pdev);
++
++ drm_put_dev(dev);
++}
++
++static int vmwgfx_pm_notifier(struct notifier_block *nb, unsigned long val,
++ void *ptr)
++{
++ struct vmw_private *dev_priv =
++ container_of(nb, struct vmw_private, pm_nb);
++ struct vmw_master *vmaster = dev_priv->active_master;
++
++ switch (val) {
++ case PM_HIBERNATION_PREPARE:
++ case PM_SUSPEND_PREPARE:
++ ttm_suspend_lock(&vmaster->lock);
++
++ /**
++ * This empties VRAM and unbinds all GMR bindings.
++ * Buffer contents is moved to swappable memory.
++ */
++ ttm_bo_swapout_all(&dev_priv->bdev);
++ break;
++ case PM_POST_HIBERNATION:
++ case PM_POST_SUSPEND:
++ ttm_suspend_unlock(&vmaster->lock);
++ break;
++ case PM_RESTORE_PREPARE:
++ break;
++ case PM_POST_RESTORE:
++ break;
++ default:
++ break;
++ }
++ return 0;
++}
++
++/**
++ * These might not be needed with the virtual SVGA device.
++ */
++
++int vmw_pci_suspend(struct pci_dev *pdev, pm_message_t state)
++{
++ pci_save_state(pdev);
++ pci_disable_device(pdev);
++ pci_set_power_state(pdev, PCI_D3hot);
++ return 0;
++}
++
++int vmw_pci_resume(struct pci_dev *pdev)
++{
++ pci_set_power_state(pdev, PCI_D0);
++ pci_restore_state(pdev);
++ return pci_enable_device(pdev);
++}
++
++static struct drm_driver driver = {
++ .driver_features = DRIVER_HAVE_IRQ | DRIVER_IRQ_SHARED |
++ DRIVER_MODESET,
++ .load = vmw_driver_load,
++ .unload = vmw_driver_unload,
++ .firstopen = vmw_firstopen,
++ .lastclose = vmw_lastclose,
++ .irq_preinstall = vmw_irq_preinstall,
++ .irq_postinstall = vmw_irq_postinstall,
++ .irq_uninstall = vmw_irq_uninstall,
++ .irq_handler = vmw_irq_handler,
++ .reclaim_buffers_locked = NULL,
++ .get_map_ofs = drm_core_get_map_ofs,
++ .get_reg_ofs = drm_core_get_reg_ofs,
++ .ioctls = vmw_ioctls,
++ .num_ioctls = DRM_ARRAY_SIZE(vmw_ioctls),
++ .dma_quiescent = NULL, /*vmw_dma_quiescent, */
++ .master_create = vmw_master_create,
++ .master_destroy = vmw_master_destroy,
++ .master_set = vmw_master_set,
++ .master_drop = vmw_master_drop,
++ .open = vmw_driver_open,
++ .postclose = vmw_postclose,
++ .fops = {
++ .owner = THIS_MODULE,
++ .open = drm_open,
++ .release = drm_release,
++ .unlocked_ioctl = vmw_unlocked_ioctl,
++ .mmap = vmw_mmap,
++ .poll = drm_poll,
++ .fasync = drm_fasync,
++#if defined(CONFIG_COMPAT)
++ .compat_ioctl = drm_compat_ioctl,
++#endif
++ },
++ .pci_driver = {
++ .name = VMWGFX_DRIVER_NAME,
++ .id_table = vmw_pci_id_list,
++ .probe = vmw_probe,
++ .remove = vmw_remove,
++ .suspend = vmw_pci_suspend,
++ .resume = vmw_pci_resume
++ },
++ .name = VMWGFX_DRIVER_NAME,
++ .desc = VMWGFX_DRIVER_DESC,
++ .date = VMWGFX_DRIVER_DATE,
++ .major = VMWGFX_DRIVER_MAJOR,
++ .minor = VMWGFX_DRIVER_MINOR,
++ .patchlevel = VMWGFX_DRIVER_PATCHLEVEL
++};
++
++static int vmw_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
++{
++ return drm_get_dev(pdev, ent, &driver);
++}
++
++static int __init vmwgfx_init(void)
++{
++ int ret;
++ ret = drm_init(&driver);
++ if (ret)
++ DRM_ERROR("Failed initializing DRM.\n");
++ return ret;
++}
++
++static void __exit vmwgfx_exit(void)
++{
++ drm_exit(&driver);
++}
++
++module_init(vmwgfx_init);
++module_exit(vmwgfx_exit);
++
++MODULE_AUTHOR("VMware Inc. and others");
++MODULE_DESCRIPTION("Standalone drm driver for the VMware SVGA device");
++MODULE_LICENSE("GPL and additional rights");
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
+new file mode 100644
+index 0000000..356dc93
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_drv.h
+@@ -0,0 +1,521 @@
++/**************************************************************************
++ *
++ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++#ifndef _VMWGFX_DRV_H_
++#define _VMWGFX_DRV_H_
++
++#include "vmwgfx_reg.h"
++#include "drmP.h"
++#include "vmwgfx_drm.h"
++#include "drm_hashtab.h"
++#include "linux/suspend.h"
++#include "ttm/ttm_bo_driver.h"
++#include "ttm/ttm_object.h"
++#include "ttm/ttm_lock.h"
++#include "ttm/ttm_execbuf_util.h"
++#include "ttm/ttm_module.h"
++
++#define VMWGFX_DRIVER_DATE "20100209"
++#define VMWGFX_DRIVER_MAJOR 1
++#define VMWGFX_DRIVER_MINOR 0
++#define VMWGFX_DRIVER_PATCHLEVEL 0
++#define VMWGFX_FILE_PAGE_OFFSET 0x00100000
++#define VMWGFX_FIFO_STATIC_SIZE (1024*1024)
++#define VMWGFX_MAX_RELOCATIONS 2048
++#define VMWGFX_MAX_GMRS 2048
++
++struct vmw_fpriv {
++ struct drm_master *locked_master;
++ struct ttm_object_file *tfile;
++};
++
++struct vmw_dma_buffer {
++ struct ttm_buffer_object base;
++ struct list_head validate_list;
++ struct list_head gmr_lru;
++ uint32_t gmr_id;
++ bool gmr_bound;
++ uint32_t cur_validate_node;
++ bool on_validate_list;
++};
++
++struct vmw_resource {
++ struct kref kref;
++ struct vmw_private *dev_priv;
++ struct idr *idr;
++ int id;
++ enum ttm_object_type res_type;
++ bool avail;
++ void (*hw_destroy) (struct vmw_resource *res);
++ void (*res_free) (struct vmw_resource *res);
++
++ /* TODO is a generic snooper needed? */
++#if 0
++ void (*snoop)(struct vmw_resource *res,
++ struct ttm_object_file *tfile,
++ SVGA3dCmdHeader *header);
++ void *snoop_priv;
++#endif
++};
++
++struct vmw_cursor_snooper {
++ struct drm_crtc *crtc;
++ size_t age;
++ uint32_t *image;
++};
++
++struct vmw_surface {
++ struct vmw_resource res;
++ uint32_t flags;
++ uint32_t format;
++ uint32_t mip_levels[DRM_VMW_MAX_SURFACE_FACES];
++ struct drm_vmw_size *sizes;
++ uint32_t num_sizes;
++
++ bool scanout;
++
++ /* TODO so far just a extra pointer */
++ struct vmw_cursor_snooper snooper;
++};
++
++struct vmw_fifo_state {
++ unsigned long reserved_size;
++ __le32 *dynamic_buffer;
++ __le32 *static_buffer;
++ __le32 *last_buffer;
++ uint32_t last_data_size;
++ uint32_t last_buffer_size;
++ bool last_buffer_add;
++ unsigned long static_buffer_size;
++ bool using_bounce_buffer;
++ uint32_t capabilities;
++ struct mutex fifo_mutex;
++ struct rw_semaphore rwsem;
++};
++
++struct vmw_relocation {
++ SVGAGuestPtr *location;
++ uint32_t index;
++};
++
++struct vmw_sw_context{
++ struct ida bo_list;
++ uint32_t last_cid;
++ bool cid_valid;
++ uint32_t last_sid;
++ uint32_t sid_translation;
++ bool sid_valid;
++ struct ttm_object_file *tfile;
++ struct list_head validate_nodes;
++ struct vmw_relocation relocs[VMWGFX_MAX_RELOCATIONS];
++ uint32_t cur_reloc;
++ struct ttm_validate_buffer val_bufs[VMWGFX_MAX_GMRS];
++ uint32_t cur_val_buf;
++};
++
++struct vmw_legacy_display;
++struct vmw_overlay;
++
++struct vmw_master {
++ struct ttm_lock lock;
++};
++
++struct vmw_private {
++ struct ttm_bo_device bdev;
++ struct ttm_bo_global_ref bo_global_ref;
++ struct ttm_global_reference mem_global_ref;
++
++ struct vmw_fifo_state fifo;
++
++ struct drm_device *dev;
++ unsigned long vmw_chipset;
++ unsigned int io_start;
++ uint32_t vram_start;
++ uint32_t vram_size;
++ uint32_t mmio_start;
++ uint32_t mmio_size;
++ uint32_t fb_max_width;
++ uint32_t fb_max_height;
++ __le32 __iomem *mmio_virt;
++ int mmio_mtrr;
++ uint32_t capabilities;
++ uint32_t max_gmr_descriptors;
++ uint32_t max_gmr_ids;
++ struct mutex hw_mutex;
++
++ /*
++ * VGA registers.
++ */
++
++ uint32_t vga_width;
++ uint32_t vga_height;
++ uint32_t vga_depth;
++ uint32_t vga_bpp;
++ uint32_t vga_pseudo;
++ uint32_t vga_red_mask;
++ uint32_t vga_blue_mask;
++ uint32_t vga_green_mask;
++
++ /*
++ * Framebuffer info.
++ */
++
++ void *fb_info;
++ struct vmw_legacy_display *ldu_priv;
++ struct vmw_overlay *overlay_priv;
++
++ /*
++ * Context and surface management.
++ */
++
++ rwlock_t resource_lock;
++ struct idr context_idr;
++ struct idr surface_idr;
++ struct idr stream_idr;
++
++ /*
++ * Block lastclose from racing with firstopen.
++ */
++
++ struct mutex init_mutex;
++
++ /*
++ * A resource manager for kernel-only surfaces and
++ * contexts.
++ */
++
++ struct ttm_object_device *tdev;
++
++ /*
++ * Fencing and IRQs.
++ */
++
++ atomic_t fence_seq;
++ wait_queue_head_t fence_queue;
++ wait_queue_head_t fifo_queue;
++ atomic_t fence_queue_waiters;
++ atomic_t fifo_queue_waiters;
++ uint32_t last_read_sequence;
++ spinlock_t irq_lock;
++
++ /*
++ * Device state
++ */
++
++ uint32_t traces_state;
++ uint32_t enable_state;
++ uint32_t config_done_state;
++
++ /**
++ * Execbuf
++ */
++ /**
++ * Protected by the cmdbuf mutex.
++ */
++
++ struct vmw_sw_context ctx;
++ uint32_t val_seq;
++ struct mutex cmdbuf_mutex;
++
++ /**
++ * GMR management. Protected by the lru spinlock.
++ */
++
++ struct ida gmr_ida;
++ struct list_head gmr_lru;
++
++
++ /**
++ * Operating mode.
++ */
++
++ bool stealth;
++ bool is_opened;
++
++ /**
++ * Master management.
++ */
++
++ struct vmw_master *active_master;
++ struct vmw_master fbdev_master;
++ struct notifier_block pm_nb;
++};
++
++static inline struct vmw_private *vmw_priv(struct drm_device *dev)
++{
++ return (struct vmw_private *)dev->dev_private;
++}
++
++static inline struct vmw_fpriv *vmw_fpriv(struct drm_file *file_priv)
++{
++ return (struct vmw_fpriv *)file_priv->driver_priv;
++}
++
++static inline struct vmw_master *vmw_master(struct drm_master *master)
++{
++ return (struct vmw_master *) master->driver_priv;
++}
++
++static inline void vmw_write(struct vmw_private *dev_priv,
++ unsigned int offset, uint32_t value)
++{
++ outl(offset, dev_priv->io_start + VMWGFX_INDEX_PORT);
++ outl(value, dev_priv->io_start + VMWGFX_VALUE_PORT);
++}
++
++static inline uint32_t vmw_read(struct vmw_private *dev_priv,
++ unsigned int offset)
++{
++ uint32_t val;
++
++ outl(offset, dev_priv->io_start + VMWGFX_INDEX_PORT);
++ val = inl(dev_priv->io_start + VMWGFX_VALUE_PORT);
++ return val;
++}
++
++/**
++ * GMR utilities - vmwgfx_gmr.c
++ */
++
++extern int vmw_gmr_bind(struct vmw_private *dev_priv,
++ struct ttm_buffer_object *bo);
++extern void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id);
++
++/**
++ * Resource utilities - vmwgfx_resource.c
++ */
++
++extern struct vmw_resource *vmw_context_alloc(struct vmw_private *dev_priv);
++extern void vmw_resource_unreference(struct vmw_resource **p_res);
++extern struct vmw_resource *vmw_resource_reference(struct vmw_resource *res);
++extern int vmw_context_destroy_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv);
++extern int vmw_context_define_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv);
++extern int vmw_context_check(struct vmw_private *dev_priv,
++ struct ttm_object_file *tfile,
++ int id);
++extern void vmw_surface_res_free(struct vmw_resource *res);
++extern int vmw_surface_init(struct vmw_private *dev_priv,
++ struct vmw_surface *srf,
++ void (*res_free) (struct vmw_resource *res));
++extern int vmw_user_surface_lookup_handle(struct vmw_private *dev_priv,
++ struct ttm_object_file *tfile,
++ uint32_t handle,
++ struct vmw_surface **out);
++extern int vmw_surface_destroy_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv);
++extern int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv);
++extern int vmw_surface_reference_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv);
++extern int vmw_surface_check(struct vmw_private *dev_priv,
++ struct ttm_object_file *tfile,
++ uint32_t handle, int *id);
++extern void vmw_dmabuf_bo_free(struct ttm_buffer_object *bo);
++extern int vmw_dmabuf_init(struct vmw_private *dev_priv,
++ struct vmw_dma_buffer *vmw_bo,
++ size_t size, struct ttm_placement *placement,
++ bool interuptable,
++ void (*bo_free) (struct ttm_buffer_object *bo));
++extern int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv);
++extern int vmw_dmabuf_unref_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv);
++extern uint32_t vmw_dmabuf_validate_node(struct ttm_buffer_object *bo,
++ uint32_t cur_validate_node);
++extern void vmw_dmabuf_validate_clear(struct ttm_buffer_object *bo);
++extern int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile,
++ uint32_t id, struct vmw_dma_buffer **out);
++extern uint32_t vmw_dmabuf_gmr(struct ttm_buffer_object *bo);
++extern void vmw_dmabuf_set_gmr(struct ttm_buffer_object *bo, uint32_t id);
++extern int vmw_gmr_id_alloc(struct vmw_private *dev_priv, uint32_t *p_id);
++extern int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv,
++ struct vmw_dma_buffer *bo);
++extern int vmw_dmabuf_from_vram(struct vmw_private *vmw_priv,
++ struct vmw_dma_buffer *bo);
++extern void vmw_dmabuf_gmr_unbind(struct ttm_buffer_object *bo);
++extern int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv);
++extern int vmw_stream_unref_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv);
++extern int vmw_user_stream_lookup(struct vmw_private *dev_priv,
++ struct ttm_object_file *tfile,
++ uint32_t *inout_id,
++ struct vmw_resource **out);
++
++
++/**
++ * Misc Ioctl functionality - vmwgfx_ioctl.c
++ */
++
++extern int vmw_getparam_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv);
++extern int vmw_fifo_debug_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv);
++
++/**
++ * Fifo utilities - vmwgfx_fifo.c
++ */
++
++extern int vmw_fifo_init(struct vmw_private *dev_priv,
++ struct vmw_fifo_state *fifo);
++extern void vmw_fifo_release(struct vmw_private *dev_priv,
++ struct vmw_fifo_state *fifo);
++extern void *vmw_fifo_reserve(struct vmw_private *dev_priv, uint32_t bytes);
++extern void vmw_fifo_commit(struct vmw_private *dev_priv, uint32_t bytes);
++extern int vmw_fifo_send_fence(struct vmw_private *dev_priv,
++ uint32_t *sequence);
++extern void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason);
++extern int vmw_fifo_mmap(struct file *filp, struct vm_area_struct *vma);
++extern bool vmw_fifo_have_3d(struct vmw_private *dev_priv);
++
++/**
++ * TTM glue - vmwgfx_ttm_glue.c
++ */
++
++extern int vmw_ttm_global_init(struct vmw_private *dev_priv);
++extern void vmw_ttm_global_release(struct vmw_private *dev_priv);
++extern int vmw_mmap(struct file *filp, struct vm_area_struct *vma);
++
++/**
++ * TTM buffer object driver - vmwgfx_buffer.c
++ */
++
++extern struct ttm_placement vmw_vram_placement;
++extern struct ttm_placement vmw_vram_ne_placement;
++extern struct ttm_placement vmw_vram_sys_placement;
++extern struct ttm_placement vmw_sys_placement;
++extern struct ttm_bo_driver vmw_bo_driver;
++extern int vmw_dma_quiescent(struct drm_device *dev);
++
++/**
++ * Command submission - vmwgfx_execbuf.c
++ */
++
++extern int vmw_execbuf_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv);
++
++/**
++ * IRQs and wating - vmwgfx_irq.c
++ */
++
++extern irqreturn_t vmw_irq_handler(DRM_IRQ_ARGS);
++extern int vmw_wait_fence(struct vmw_private *dev_priv, bool lazy,
++ uint32_t sequence, bool interruptible,
++ unsigned long timeout);
++extern void vmw_irq_preinstall(struct drm_device *dev);
++extern int vmw_irq_postinstall(struct drm_device *dev);
++extern void vmw_irq_uninstall(struct drm_device *dev);
++extern bool vmw_fence_signaled(struct vmw_private *dev_priv,
++ uint32_t sequence);
++extern int vmw_fence_wait_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv);
++extern int vmw_fallback_wait(struct vmw_private *dev_priv,
++ bool lazy,
++ bool fifo_idle,
++ uint32_t sequence,
++ bool interruptible,
++ unsigned long timeout);
++
++/**
++ * Kernel framebuffer - vmwgfx_fb.c
++ */
++
++int vmw_fb_init(struct vmw_private *vmw_priv);
++int vmw_fb_close(struct vmw_private *dev_priv);
++int vmw_fb_off(struct vmw_private *vmw_priv);
++int vmw_fb_on(struct vmw_private *vmw_priv);
++
++/**
++ * Kernel modesetting - vmwgfx_kms.c
++ */
++
++int vmw_kms_init(struct vmw_private *dev_priv);
++int vmw_kms_close(struct vmw_private *dev_priv);
++int vmw_kms_save_vga(struct vmw_private *vmw_priv);
++int vmw_kms_restore_vga(struct vmw_private *vmw_priv);
++int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv);
++void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv);
++void vmw_kms_cursor_snoop(struct vmw_surface *srf,
++ struct ttm_object_file *tfile,
++ struct ttm_buffer_object *bo,
++ SVGA3dCmdHeader *header);
++
++/**
++ * Overlay control - vmwgfx_overlay.c
++ */
++
++int vmw_overlay_init(struct vmw_private *dev_priv);
++int vmw_overlay_close(struct vmw_private *dev_priv);
++int vmw_overlay_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv);
++int vmw_overlay_stop_all(struct vmw_private *dev_priv);
++int vmw_overlay_resume_all(struct vmw_private *dev_priv);
++int vmw_overlay_pause_all(struct vmw_private *dev_priv);
++int vmw_overlay_claim(struct vmw_private *dev_priv, uint32_t *out);
++int vmw_overlay_unref(struct vmw_private *dev_priv, uint32_t stream_id);
++int vmw_overlay_num_overlays(struct vmw_private *dev_priv);
++int vmw_overlay_num_free_overlays(struct vmw_private *dev_priv);
++
++/**
++ * Inline helper functions
++ */
++
++static inline void vmw_surface_unreference(struct vmw_surface **srf)
++{
++ struct vmw_surface *tmp_srf = *srf;
++ struct vmw_resource *res = &tmp_srf->res;
++ *srf = NULL;
++
++ vmw_resource_unreference(&res);
++}
++
++static inline struct vmw_surface *vmw_surface_reference(struct vmw_surface *srf)
++{
++ (void) vmw_resource_reference(&srf->res);
++ return srf;
++}
++
++static inline void vmw_dmabuf_unreference(struct vmw_dma_buffer **buf)
++{
++ struct vmw_dma_buffer *tmp_buf = *buf;
++ struct ttm_buffer_object *bo = &tmp_buf->base;
++ *buf = NULL;
++
++ ttm_bo_unref(&bo);
++}
++
++static inline struct vmw_dma_buffer *vmw_dmabuf_reference(struct vmw_dma_buffer *buf)
++{
++ if (ttm_bo_reference(&buf->base))
++ return buf;
++ return NULL;
++}
++
++#endif
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+new file mode 100644
+index 0000000..d69caf9
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c
+@@ -0,0 +1,640 @@
++/**************************************************************************
++ *
++ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++#include "vmwgfx_drv.h"
++#include "vmwgfx_reg.h"
++#include "ttm/ttm_bo_api.h"
++#include "ttm/ttm_placement.h"
++
++static int vmw_cmd_invalid(struct vmw_private *dev_priv,
++ struct vmw_sw_context *sw_context,
++ SVGA3dCmdHeader *header)
++{
++ return capable(CAP_SYS_ADMIN) ? : -EINVAL;
++}
++
++static int vmw_cmd_ok(struct vmw_private *dev_priv,
++ struct vmw_sw_context *sw_context,
++ SVGA3dCmdHeader *header)
++{
++ return 0;
++}
++
++static int vmw_cmd_cid_check(struct vmw_private *dev_priv,
++ struct vmw_sw_context *sw_context,
++ SVGA3dCmdHeader *header)
++{
++ struct vmw_cid_cmd {
++ SVGA3dCmdHeader header;
++ __le32 cid;
++ } *cmd;
++ int ret;
++
++ cmd = container_of(header, struct vmw_cid_cmd, header);
++ if (likely(sw_context->cid_valid && cmd->cid == sw_context->last_cid))
++ return 0;
++
++ ret = vmw_context_check(dev_priv, sw_context->tfile, cmd->cid);
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("Could not find or use context %u\n",
++ (unsigned) cmd->cid);
++ return ret;
++ }
++
++ sw_context->last_cid = cmd->cid;
++ sw_context->cid_valid = true;
++
++ return 0;
++}
++
++static int vmw_cmd_sid_check(struct vmw_private *dev_priv,
++ struct vmw_sw_context *sw_context,
++ uint32_t *sid)
++{
++ if (*sid == SVGA3D_INVALID_ID)
++ return 0;
++
++ if (unlikely((!sw_context->sid_valid ||
++ *sid != sw_context->last_sid))) {
++ int real_id;
++ int ret = vmw_surface_check(dev_priv, sw_context->tfile,
++ *sid, &real_id);
++
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("Could ot find or use surface 0x%08x "
++ "address 0x%08lx\n",
++ (unsigned int) *sid,
++ (unsigned long) sid);
++ return ret;
++ }
++
++ sw_context->last_sid = *sid;
++ sw_context->sid_valid = true;
++ *sid = real_id;
++ sw_context->sid_translation = real_id;
++ } else
++ *sid = sw_context->sid_translation;
++
++ return 0;
++}
++
++
++static int vmw_cmd_set_render_target_check(struct vmw_private *dev_priv,
++ struct vmw_sw_context *sw_context,
++ SVGA3dCmdHeader *header)
++{
++ struct vmw_sid_cmd {
++ SVGA3dCmdHeader header;
++ SVGA3dCmdSetRenderTarget body;
++ } *cmd;
++ int ret;
++
++ ret = vmw_cmd_cid_check(dev_priv, sw_context, header);
++ if (unlikely(ret != 0))
++ return ret;
++
++ cmd = container_of(header, struct vmw_sid_cmd, header);
++ ret = vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.target.sid);
++ return ret;
++}
++
++static int vmw_cmd_surface_copy_check(struct vmw_private *dev_priv,
++ struct vmw_sw_context *sw_context,
++ SVGA3dCmdHeader *header)
++{
++ struct vmw_sid_cmd {
++ SVGA3dCmdHeader header;
++ SVGA3dCmdSurfaceCopy body;
++ } *cmd;
++ int ret;
++
++ cmd = container_of(header, struct vmw_sid_cmd, header);
++ ret = vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.src.sid);
++ if (unlikely(ret != 0))
++ return ret;
++ return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.dest.sid);
++}
++
++static int vmw_cmd_stretch_blt_check(struct vmw_private *dev_priv,
++ struct vmw_sw_context *sw_context,
++ SVGA3dCmdHeader *header)
++{
++ struct vmw_sid_cmd {
++ SVGA3dCmdHeader header;
++ SVGA3dCmdSurfaceStretchBlt body;
++ } *cmd;
++ int ret;
++
++ cmd = container_of(header, struct vmw_sid_cmd, header);
++ ret = vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.src.sid);
++ if (unlikely(ret != 0))
++ return ret;
++ return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.dest.sid);
++}
++
++static int vmw_cmd_blt_surf_screen_check(struct vmw_private *dev_priv,
++ struct vmw_sw_context *sw_context,
++ SVGA3dCmdHeader *header)
++{
++ struct vmw_sid_cmd {
++ SVGA3dCmdHeader header;
++ SVGA3dCmdBlitSurfaceToScreen body;
++ } *cmd;
++
++ cmd = container_of(header, struct vmw_sid_cmd, header);
++ return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.srcImage.sid);
++}
++
++static int vmw_cmd_present_check(struct vmw_private *dev_priv,
++ struct vmw_sw_context *sw_context,
++ SVGA3dCmdHeader *header)
++{
++ struct vmw_sid_cmd {
++ SVGA3dCmdHeader header;
++ SVGA3dCmdPresent body;
++ } *cmd;
++
++ cmd = container_of(header, struct vmw_sid_cmd, header);
++ return vmw_cmd_sid_check(dev_priv, sw_context, &cmd->body.sid);
++}
++
++static int vmw_cmd_dma(struct vmw_private *dev_priv,
++ struct vmw_sw_context *sw_context,
++ SVGA3dCmdHeader *header)
++{
++ uint32_t handle;
++ struct vmw_dma_buffer *vmw_bo = NULL;
++ struct ttm_buffer_object *bo;
++ struct vmw_surface *srf = NULL;
++ struct vmw_dma_cmd {
++ SVGA3dCmdHeader header;
++ SVGA3dCmdSurfaceDMA dma;
++ } *cmd;
++ struct vmw_relocation *reloc;
++ int ret;
++ uint32_t cur_validate_node;
++ struct ttm_validate_buffer *val_buf;
++
++ cmd = container_of(header, struct vmw_dma_cmd, header);
++ handle = cmd->dma.guest.ptr.gmrId;
++ ret = vmw_user_dmabuf_lookup(sw_context->tfile, handle, &vmw_bo);
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("Could not find or use GMR region.\n");
++ return -EINVAL;
++ }
++ bo = &vmw_bo->base;
++
++ if (unlikely(sw_context->cur_reloc >= VMWGFX_MAX_RELOCATIONS)) {
++ DRM_ERROR("Max number of DMA commands per submission"
++ " exceeded\n");
++ ret = -EINVAL;
++ goto out_no_reloc;
++ }
++
++ reloc = &sw_context->relocs[sw_context->cur_reloc++];
++ reloc->location = &cmd->dma.guest.ptr;
++
++ cur_validate_node = vmw_dmabuf_validate_node(bo, sw_context->cur_val_buf);
++ if (unlikely(cur_validate_node >= VMWGFX_MAX_GMRS)) {
++ DRM_ERROR("Max number of DMA buffers per submission"
++ " exceeded.\n");
++ ret = -EINVAL;
++ goto out_no_reloc;
++ }
++
++ reloc->index = cur_validate_node;
++ if (unlikely(cur_validate_node == sw_context->cur_val_buf)) {
++ val_buf = &sw_context->val_bufs[cur_validate_node];
++ val_buf->bo = ttm_bo_reference(bo);
++ val_buf->new_sync_obj_arg = (void *) dev_priv;
++ list_add_tail(&val_buf->head, &sw_context->validate_nodes);
++ ++sw_context->cur_val_buf;
++ }
++
++ ret = vmw_user_surface_lookup_handle(dev_priv, sw_context->tfile,
++ cmd->dma.host.sid, &srf);
++ if (ret) {
++ DRM_ERROR("could not find surface\n");
++ goto out_no_reloc;
++ }
++
++ /**
++ * Patch command stream with device SID.
++ */
++
++ cmd->dma.host.sid = srf->res.id;
++ vmw_kms_cursor_snoop(srf, sw_context->tfile, bo, header);
++ /**
++ * FIXME: May deadlock here when called from the
++ * command parsing code.
++ */
++ vmw_surface_unreference(&srf);
++
++out_no_reloc:
++ vmw_dmabuf_unreference(&vmw_bo);
++ return ret;
++}
++
++static int vmw_cmd_draw(struct vmw_private *dev_priv,
++ struct vmw_sw_context *sw_context,
++ SVGA3dCmdHeader *header)
++{
++ struct vmw_draw_cmd {
++ SVGA3dCmdHeader header;
++ SVGA3dCmdDrawPrimitives body;
++ } *cmd;
++ SVGA3dVertexDecl *decl = (SVGA3dVertexDecl *)(
++ (unsigned long)header + sizeof(*cmd));
++ SVGA3dPrimitiveRange *range;
++ uint32_t i;
++ uint32_t maxnum;
++ int ret;
++
++ ret = vmw_cmd_cid_check(dev_priv, sw_context, header);
++ if (unlikely(ret != 0))
++ return ret;
++
++ cmd = container_of(header, struct vmw_draw_cmd, header);
++ maxnum = (header->size - sizeof(cmd->body)) / sizeof(*decl);
++
++ if (unlikely(cmd->body.numVertexDecls > maxnum)) {
++ DRM_ERROR("Illegal number of vertex declarations.\n");
++ return -EINVAL;
++ }
++
++ for (i = 0; i < cmd->body.numVertexDecls; ++i, ++decl) {
++ ret = vmw_cmd_sid_check(dev_priv, sw_context,
++ &decl->array.surfaceId);
++ if (unlikely(ret != 0))
++ return ret;
++ }
++
++ maxnum = (header->size - sizeof(cmd->body) -
++ cmd->body.numVertexDecls * sizeof(*decl)) / sizeof(*range);
++ if (unlikely(cmd->body.numRanges > maxnum)) {
++ DRM_ERROR("Illegal number of index ranges.\n");
++ return -EINVAL;
++ }
++
++ range = (SVGA3dPrimitiveRange *) decl;
++ for (i = 0; i < cmd->body.numRanges; ++i, ++range) {
++ ret = vmw_cmd_sid_check(dev_priv, sw_context,
++ &range->indexArray.surfaceId);
++ if (unlikely(ret != 0))
++ return ret;
++ }
++ return 0;
++}
++
++
++static int vmw_cmd_tex_state(struct vmw_private *dev_priv,
++ struct vmw_sw_context *sw_context,
++ SVGA3dCmdHeader *header)
++{
++ struct vmw_tex_state_cmd {
++ SVGA3dCmdHeader header;
++ SVGA3dCmdSetTextureState state;
++ };
++
++ SVGA3dTextureState *last_state = (SVGA3dTextureState *)
++ ((unsigned long) header + header->size + sizeof(header));
++ SVGA3dTextureState *cur_state = (SVGA3dTextureState *)
++ ((unsigned long) header + sizeof(struct vmw_tex_state_cmd));
++ int ret;
++
++ ret = vmw_cmd_cid_check(dev_priv, sw_context, header);
++ if (unlikely(ret != 0))
++ return ret;
++
++ for (; cur_state < last_state; ++cur_state) {
++ if (likely(cur_state->name != SVGA3D_TS_BIND_TEXTURE))
++ continue;
++
++ ret = vmw_cmd_sid_check(dev_priv, sw_context,
++ &cur_state->value);
++ if (unlikely(ret != 0))
++ return ret;
++ }
++
++ return 0;
++}
++
++
++typedef int (*vmw_cmd_func) (struct vmw_private *,
++ struct vmw_sw_context *,
++ SVGA3dCmdHeader *);
++
++#define VMW_CMD_DEF(cmd, func) \
++ [cmd - SVGA_3D_CMD_BASE] = func
++
++static vmw_cmd_func vmw_cmd_funcs[SVGA_3D_CMD_MAX] = {
++ VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_DEFINE, &vmw_cmd_invalid),
++ VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_DESTROY, &vmw_cmd_invalid),
++ VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_COPY, &vmw_cmd_surface_copy_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_STRETCHBLT, &vmw_cmd_stretch_blt_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_SURFACE_DMA, &vmw_cmd_dma),
++ VMW_CMD_DEF(SVGA_3D_CMD_CONTEXT_DEFINE, &vmw_cmd_invalid),
++ VMW_CMD_DEF(SVGA_3D_CMD_CONTEXT_DESTROY, &vmw_cmd_invalid),
++ VMW_CMD_DEF(SVGA_3D_CMD_SETTRANSFORM, &vmw_cmd_cid_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_SETZRANGE, &vmw_cmd_cid_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_SETRENDERSTATE, &vmw_cmd_cid_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_SETRENDERTARGET,
++ &vmw_cmd_set_render_target_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_SETTEXTURESTATE, &vmw_cmd_tex_state),
++ VMW_CMD_DEF(SVGA_3D_CMD_SETMATERIAL, &vmw_cmd_cid_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_SETLIGHTDATA, &vmw_cmd_cid_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_SETLIGHTENABLED, &vmw_cmd_cid_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_SETVIEWPORT, &vmw_cmd_cid_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_SETCLIPPLANE, &vmw_cmd_cid_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_CLEAR, &vmw_cmd_cid_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_PRESENT, &vmw_cmd_present_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_SHADER_DEFINE, &vmw_cmd_cid_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_SHADER_DESTROY, &vmw_cmd_cid_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_SET_SHADER, &vmw_cmd_cid_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_SET_SHADER_CONST, &vmw_cmd_cid_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_DRAW_PRIMITIVES, &vmw_cmd_draw),
++ VMW_CMD_DEF(SVGA_3D_CMD_SETSCISSORRECT, &vmw_cmd_cid_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_BEGIN_QUERY, &vmw_cmd_cid_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_END_QUERY, &vmw_cmd_cid_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_WAIT_FOR_QUERY, &vmw_cmd_cid_check),
++ VMW_CMD_DEF(SVGA_3D_CMD_PRESENT_READBACK, &vmw_cmd_ok),
++ VMW_CMD_DEF(SVGA_3D_CMD_BLIT_SURFACE_TO_SCREEN,
++ &vmw_cmd_blt_surf_screen_check)
++};
++
++static int vmw_cmd_check(struct vmw_private *dev_priv,
++ struct vmw_sw_context *sw_context,
++ void *buf, uint32_t *size)
++{
++ uint32_t cmd_id;
++ uint32_t size_remaining = *size;
++ SVGA3dCmdHeader *header = (SVGA3dCmdHeader *) buf;
++ int ret;
++
++ cmd_id = ((uint32_t *)buf)[0];
++ if (cmd_id == SVGA_CMD_UPDATE) {
++ *size = 5 << 2;
++ return 0;
++ }
++
++ cmd_id = le32_to_cpu(header->id);
++ *size = le32_to_cpu(header->size) + sizeof(SVGA3dCmdHeader);
++
++ cmd_id -= SVGA_3D_CMD_BASE;
++ if (unlikely(*size > size_remaining))
++ goto out_err;
++
++ if (unlikely(cmd_id >= SVGA_3D_CMD_MAX - SVGA_3D_CMD_BASE))
++ goto out_err;
++
++ ret = vmw_cmd_funcs[cmd_id](dev_priv, sw_context, header);
++ if (unlikely(ret != 0))
++ goto out_err;
++
++ return 0;
++out_err:
++ DRM_ERROR("Illegal / Invalid SVGA3D command: %d\n",
++ cmd_id + SVGA_3D_CMD_BASE);
++ return -EINVAL;
++}
++
++static int vmw_cmd_check_all(struct vmw_private *dev_priv,
++ struct vmw_sw_context *sw_context,
++ void *buf, uint32_t size)
++{
++ int32_t cur_size = size;
++ int ret;
++
++ while (cur_size > 0) {
++ size = cur_size;
++ ret = vmw_cmd_check(dev_priv, sw_context, buf, &size);
++ if (unlikely(ret != 0))
++ return ret;
++ buf = (void *)((unsigned long) buf + size);
++ cur_size -= size;
++ }
++
++ if (unlikely(cur_size != 0)) {
++ DRM_ERROR("Command verifier out of sync.\n");
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static void vmw_free_relocations(struct vmw_sw_context *sw_context)
++{
++ sw_context->cur_reloc = 0;
++}
++
++static void vmw_apply_relocations(struct vmw_sw_context *sw_context)
++{
++ uint32_t i;
++ struct vmw_relocation *reloc;
++ struct ttm_validate_buffer *validate;
++ struct ttm_buffer_object *bo;
++
++ for (i = 0; i < sw_context->cur_reloc; ++i) {
++ reloc = &sw_context->relocs[i];
++ validate = &sw_context->val_bufs[reloc->index];
++ bo = validate->bo;
++ reloc->location->offset += bo->offset;
++ reloc->location->gmrId = vmw_dmabuf_gmr(bo);
++ }
++ vmw_free_relocations(sw_context);
++}
++
++static void vmw_clear_validations(struct vmw_sw_context *sw_context)
++{
++ struct ttm_validate_buffer *entry, *next;
++
++ list_for_each_entry_safe(entry, next, &sw_context->validate_nodes,
++ head) {
++ list_del(&entry->head);
++ vmw_dmabuf_validate_clear(entry->bo);
++ ttm_bo_unref(&entry->bo);
++ sw_context->cur_val_buf--;
++ }
++ BUG_ON(sw_context->cur_val_buf != 0);
++}
++
++static int vmw_validate_single_buffer(struct vmw_private *dev_priv,
++ struct ttm_buffer_object *bo)
++{
++ int ret;
++
++ if (vmw_dmabuf_gmr(bo) != SVGA_GMR_NULL)
++ return 0;
++
++ /**
++ * Put BO in VRAM, only if there is space.
++ */
++
++ ret = ttm_bo_validate(bo, &vmw_vram_sys_placement, true, false);
++ if (unlikely(ret == -ERESTARTSYS))
++ return ret;
++
++ /**
++ * Otherwise, set it up as GMR.
++ */
++
++ if (vmw_dmabuf_gmr(bo) != SVGA_GMR_NULL)
++ return 0;
++
++ ret = vmw_gmr_bind(dev_priv, bo);
++ if (likely(ret == 0 || ret == -ERESTARTSYS))
++ return ret;
++
++ /**
++ * If that failed, try VRAM again, this time evicting
++ * previous contents.
++ */
++
++ ret = ttm_bo_validate(bo, &vmw_vram_placement, true, false);
++ return ret;
++}
++
++
++static int vmw_validate_buffers(struct vmw_private *dev_priv,
++ struct vmw_sw_context *sw_context)
++{
++ struct ttm_validate_buffer *entry;
++ int ret;
++
++ list_for_each_entry(entry, &sw_context->validate_nodes, head) {
++ ret = vmw_validate_single_buffer(dev_priv, entry->bo);
++ if (unlikely(ret != 0))
++ return ret;
++ }
++ return 0;
++}
++
++int vmw_execbuf_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ struct drm_vmw_execbuf_arg *arg = (struct drm_vmw_execbuf_arg *)data;
++ struct drm_vmw_fence_rep fence_rep;
++ struct drm_vmw_fence_rep __user *user_fence_rep;
++ int ret;
++ void *user_cmd;
++ void *cmd;
++ uint32_t sequence;
++ struct vmw_sw_context *sw_context = &dev_priv->ctx;
++ struct vmw_master *vmaster = vmw_master(file_priv->master);
++
++ ret = ttm_read_lock(&vmaster->lock, true);
++ if (unlikely(ret != 0))
++ return ret;
++
++ ret = mutex_lock_interruptible(&dev_priv->cmdbuf_mutex);
++ if (unlikely(ret != 0)) {
++ ret = -ERESTARTSYS;
++ goto out_no_cmd_mutex;
++ }
++
++ cmd = vmw_fifo_reserve(dev_priv, arg->command_size);
++ if (unlikely(cmd == NULL)) {
++ DRM_ERROR("Failed reserving fifo space for commands.\n");
++ ret = -ENOMEM;
++ goto out_unlock;
++ }
++
++ user_cmd = (void __user *)(unsigned long)arg->commands;
++ ret = copy_from_user(cmd, user_cmd, arg->command_size);
++
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("Failed copying commands.\n");
++ goto out_commit;
++ }
++
++ sw_context->tfile = vmw_fpriv(file_priv)->tfile;
++ sw_context->cid_valid = false;
++ sw_context->sid_valid = false;
++ sw_context->cur_reloc = 0;
++ sw_context->cur_val_buf = 0;
++
++ INIT_LIST_HEAD(&sw_context->validate_nodes);
++
++ ret = vmw_cmd_check_all(dev_priv, sw_context, cmd, arg->command_size);
++ if (unlikely(ret != 0))
++ goto out_err;
++ ret = ttm_eu_reserve_buffers(&sw_context->validate_nodes,
++ dev_priv->val_seq++);
++ if (unlikely(ret != 0))
++ goto out_err;
++
++ ret = vmw_validate_buffers(dev_priv, sw_context);
++ if (unlikely(ret != 0))
++ goto out_err;
++
++ vmw_apply_relocations(sw_context);
++ vmw_fifo_commit(dev_priv, arg->command_size);
++
++ ret = vmw_fifo_send_fence(dev_priv, &sequence);
++
++ ttm_eu_fence_buffer_objects(&sw_context->validate_nodes,
++ (void *)(unsigned long) sequence);
++ vmw_clear_validations(sw_context);
++ mutex_unlock(&dev_priv->cmdbuf_mutex);
++
++ /*
++ * This error is harmless, because if fence submission fails,
++ * vmw_fifo_send_fence will sync.
++ */
++
++ if (ret != 0)
++ DRM_ERROR("Fence submission error. Syncing.\n");
++
++ fence_rep.error = ret;
++ fence_rep.fence_seq = (uint64_t) sequence;
++
++ user_fence_rep = (struct drm_vmw_fence_rep __user *)
++ (unsigned long)arg->fence_rep;
++
++ /*
++ * copy_to_user errors will be detected by user space not
++ * seeing fence_rep::error filled in.
++ */
++
++ ret = copy_to_user(user_fence_rep, &fence_rep, sizeof(fence_rep));
++
++ vmw_kms_cursor_post_execbuf(dev_priv);
++ ttm_read_unlock(&vmaster->lock);
++ return 0;
++out_err:
++ vmw_free_relocations(sw_context);
++ ttm_eu_backoff_reservation(&sw_context->validate_nodes);
++ vmw_clear_validations(sw_context);
++out_commit:
++ vmw_fifo_commit(dev_priv, 0);
++out_unlock:
++ mutex_unlock(&dev_priv->cmdbuf_mutex);
++out_no_cmd_mutex:
++ ttm_read_unlock(&vmaster->lock);
++ return ret;
++}
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+new file mode 100644
+index 0000000..4f4f643
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fb.c
+@@ -0,0 +1,734 @@
++/**************************************************************************
++ *
++ * Copyright © 2007 David Airlie
++ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++#include "drmP.h"
++#include "vmwgfx_drv.h"
++
++#include "ttm/ttm_placement.h"
++
++#define VMW_DIRTY_DELAY (HZ / 30)
++
++struct vmw_fb_par {
++ struct vmw_private *vmw_priv;
++
++ void *vmalloc;
++
++ struct vmw_dma_buffer *vmw_bo;
++ struct ttm_bo_kmap_obj map;
++
++ u32 pseudo_palette[17];
++
++ unsigned depth;
++ unsigned bpp;
++
++ unsigned max_width;
++ unsigned max_height;
++
++ void *bo_ptr;
++ unsigned bo_size;
++ bool bo_iowrite;
++
++ struct {
++ spinlock_t lock;
++ bool active;
++ unsigned x1;
++ unsigned y1;
++ unsigned x2;
++ unsigned y2;
++ } dirty;
++};
++
++static int vmw_fb_setcolreg(unsigned regno, unsigned red, unsigned green,
++ unsigned blue, unsigned transp,
++ struct fb_info *info)
++{
++ struct vmw_fb_par *par = info->par;
++ u32 *pal = par->pseudo_palette;
++
++ if (regno > 15) {
++ DRM_ERROR("Bad regno %u.\n", regno);
++ return 1;
++ }
++
++ switch (par->depth) {
++ case 24:
++ case 32:
++ pal[regno] = ((red & 0xff00) << 8) |
++ (green & 0xff00) |
++ ((blue & 0xff00) >> 8);
++ break;
++ default:
++ DRM_ERROR("Bad depth %u, bpp %u.\n", par->depth, par->bpp);
++ return 1;
++ }
++
++ return 0;
++}
++
++static int vmw_fb_check_var(struct fb_var_screeninfo *var,
++ struct fb_info *info)
++{
++ int depth = var->bits_per_pixel;
++ struct vmw_fb_par *par = info->par;
++ struct vmw_private *vmw_priv = par->vmw_priv;
++
++ switch (var->bits_per_pixel) {
++ case 32:
++ depth = (var->transp.length > 0) ? 32 : 24;
++ break;
++ default:
++ DRM_ERROR("Bad bpp %u.\n", var->bits_per_pixel);
++ return -EINVAL;
++ }
++
++ switch (depth) {
++ case 24:
++ var->red.offset = 16;
++ var->green.offset = 8;
++ var->blue.offset = 0;
++ var->red.length = 8;
++ var->green.length = 8;
++ var->blue.length = 8;
++ var->transp.length = 0;
++ var->transp.offset = 0;
++ break;
++ case 32:
++ var->red.offset = 16;
++ var->green.offset = 8;
++ var->blue.offset = 0;
++ var->red.length = 8;
++ var->green.length = 8;
++ var->blue.length = 8;
++ var->transp.length = 8;
++ var->transp.offset = 24;
++ break;
++ default:
++ DRM_ERROR("Bad depth %u.\n", depth);
++ return -EINVAL;
++ }
++
++ /* without multimon its hard to resize */
++ if (!(vmw_priv->capabilities & SVGA_CAP_MULTIMON) &&
++ (var->xres != par->max_width ||
++ var->yres != par->max_height)) {
++ DRM_ERROR("Tried to resize, but we don't have multimon\n");
++ return -EINVAL;
++ }
++
++ if (var->xres > par->max_width ||
++ var->yres > par->max_height) {
++ DRM_ERROR("Requested geom can not fit in framebuffer\n");
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int vmw_fb_set_par(struct fb_info *info)
++{
++ struct vmw_fb_par *par = info->par;
++ struct vmw_private *vmw_priv = par->vmw_priv;
++
++ if (vmw_priv->capabilities & SVGA_CAP_MULTIMON) {
++ vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, 0);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_IS_PRIMARY, true);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_X, 0);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_Y, 0);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_WIDTH, 0);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_HEIGHT, 0);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
++
++ vmw_write(vmw_priv, SVGA_REG_ENABLE, 1);
++ vmw_write(vmw_priv, SVGA_REG_WIDTH, par->max_width);
++ vmw_write(vmw_priv, SVGA_REG_HEIGHT, par->max_height);
++ vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, par->bpp);
++ vmw_write(vmw_priv, SVGA_REG_DEPTH, par->depth);
++ vmw_write(vmw_priv, SVGA_REG_RED_MASK, 0x00ff0000);
++ vmw_write(vmw_priv, SVGA_REG_GREEN_MASK, 0x0000ff00);
++ vmw_write(vmw_priv, SVGA_REG_BLUE_MASK, 0x000000ff);
++
++ /* TODO check if pitch and offset changes */
++
++ vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, 0);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_IS_PRIMARY, true);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_X, info->var.xoffset);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_Y, info->var.yoffset);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_WIDTH, info->var.xres);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_HEIGHT, info->var.yres);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
++ } else {
++ vmw_write(vmw_priv, SVGA_REG_WIDTH, info->var.xres);
++ vmw_write(vmw_priv, SVGA_REG_HEIGHT, info->var.yres);
++
++ /* TODO check if pitch and offset changes */
++ }
++
++ return 0;
++}
++
++static int vmw_fb_pan_display(struct fb_var_screeninfo *var,
++ struct fb_info *info)
++{
++ return 0;
++}
++
++static int vmw_fb_blank(int blank, struct fb_info *info)
++{
++ return 0;
++}
++
++/*
++ * Dirty code
++ */
++
++static void vmw_fb_dirty_flush(struct vmw_fb_par *par)
++{
++ struct vmw_private *vmw_priv = par->vmw_priv;
++ struct fb_info *info = vmw_priv->fb_info;
++ int stride = (info->fix.line_length / 4);
++ int *src = (int *)info->screen_base;
++ __le32 __iomem *vram_mem = par->bo_ptr;
++ unsigned long flags;
++ unsigned x, y, w, h;
++ int i, k;
++ struct {
++ uint32_t header;
++ SVGAFifoCmdUpdate body;
++ } *cmd;
++
++ spin_lock_irqsave(&par->dirty.lock, flags);
++ if (!par->dirty.active) {
++ spin_unlock_irqrestore(&par->dirty.lock, flags);
++ return;
++ }
++ x = par->dirty.x1;
++ y = par->dirty.y1;
++ w = min(par->dirty.x2, info->var.xres) - x;
++ h = min(par->dirty.y2, info->var.yres) - y;
++ par->dirty.x1 = par->dirty.x2 = 0;
++ par->dirty.y1 = par->dirty.y2 = 0;
++ spin_unlock_irqrestore(&par->dirty.lock, flags);
++
++ for (i = y * stride; i < info->fix.smem_len / 4; i += stride) {
++ for (k = i+x; k < i+x+w && k < info->fix.smem_len / 4; k++)
++ iowrite32(src[k], vram_mem + k);
++ }
++
++#if 0
++ DRM_INFO("%s, (%u, %u) (%ux%u)\n", __func__, x, y, w, h);
++#endif
++
++ cmd = vmw_fifo_reserve(vmw_priv, sizeof(*cmd));
++ if (unlikely(cmd == NULL)) {
++ DRM_ERROR("Fifo reserve failed.\n");
++ return;
++ }
++
++ cmd->header = cpu_to_le32(SVGA_CMD_UPDATE);
++ cmd->body.x = cpu_to_le32(x);
++ cmd->body.y = cpu_to_le32(y);
++ cmd->body.width = cpu_to_le32(w);
++ cmd->body.height = cpu_to_le32(h);
++ vmw_fifo_commit(vmw_priv, sizeof(*cmd));
++}
++
++static void vmw_fb_dirty_mark(struct vmw_fb_par *par,
++ unsigned x1, unsigned y1,
++ unsigned width, unsigned height)
++{
++ struct fb_info *info = par->vmw_priv->fb_info;
++ unsigned long flags;
++ unsigned x2 = x1 + width;
++ unsigned y2 = y1 + height;
++
++ spin_lock_irqsave(&par->dirty.lock, flags);
++ if (par->dirty.x1 == par->dirty.x2) {
++ par->dirty.x1 = x1;
++ par->dirty.y1 = y1;
++ par->dirty.x2 = x2;
++ par->dirty.y2 = y2;
++ /* if we are active start the dirty work
++ * we share the work with the defio system */
++ if (par->dirty.active)
++ schedule_delayed_work(&info->deferred_work, VMW_DIRTY_DELAY);
++ } else {
++ if (x1 < par->dirty.x1)
++ par->dirty.x1 = x1;
++ if (y1 < par->dirty.y1)
++ par->dirty.y1 = y1;
++ if (x2 > par->dirty.x2)
++ par->dirty.x2 = x2;
++ if (y2 > par->dirty.y2)
++ par->dirty.y2 = y2;
++ }
++ spin_unlock_irqrestore(&par->dirty.lock, flags);
++}
++
++static void vmw_deferred_io(struct fb_info *info,
++ struct list_head *pagelist)
++{
++ struct vmw_fb_par *par = info->par;
++ unsigned long start, end, min, max;
++ unsigned long flags;
++ struct page *page;
++ int y1, y2;
++
++ min = ULONG_MAX;
++ max = 0;
++ list_for_each_entry(page, pagelist, lru) {
++ start = page->index << PAGE_SHIFT;
++ end = start + PAGE_SIZE - 1;
++ min = min(min, start);
++ max = max(max, end);
++ }
++
++ if (min < max) {
++ y1 = min / info->fix.line_length;
++ y2 = (max / info->fix.line_length) + 1;
++
++ spin_lock_irqsave(&par->dirty.lock, flags);
++ par->dirty.x1 = 0;
++ par->dirty.y1 = y1;
++ par->dirty.x2 = info->var.xres;
++ par->dirty.y2 = y2;
++ spin_unlock_irqrestore(&par->dirty.lock, flags);
++ }
++
++ vmw_fb_dirty_flush(par);
++};
++
++struct fb_deferred_io vmw_defio = {
++ .delay = VMW_DIRTY_DELAY,
++ .deferred_io = vmw_deferred_io,
++};
++
++/*
++ * Draw code
++ */
++
++static void vmw_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
++{
++ cfb_fillrect(info, rect);
++ vmw_fb_dirty_mark(info->par, rect->dx, rect->dy,
++ rect->width, rect->height);
++}
++
++static void vmw_fb_copyarea(struct fb_info *info, const struct fb_copyarea *region)
++{
++ cfb_copyarea(info, region);
++ vmw_fb_dirty_mark(info->par, region->dx, region->dy,
++ region->width, region->height);
++}
++
++static void vmw_fb_imageblit(struct fb_info *info, const struct fb_image *image)
++{
++ cfb_imageblit(info, image);
++ vmw_fb_dirty_mark(info->par, image->dx, image->dy,
++ image->width, image->height);
++}
++
++/*
++ * Bring up code
++ */
++
++static struct fb_ops vmw_fb_ops = {
++ .owner = THIS_MODULE,
++ .fb_check_var = vmw_fb_check_var,
++ .fb_set_par = vmw_fb_set_par,
++ .fb_setcolreg = vmw_fb_setcolreg,
++ .fb_fillrect = vmw_fb_fillrect,
++ .fb_copyarea = vmw_fb_copyarea,
++ .fb_imageblit = vmw_fb_imageblit,
++ .fb_pan_display = vmw_fb_pan_display,
++ .fb_blank = vmw_fb_blank,
++};
++
++static int vmw_fb_create_bo(struct vmw_private *vmw_priv,
++ size_t size, struct vmw_dma_buffer **out)
++{
++ struct vmw_dma_buffer *vmw_bo;
++ struct ttm_placement ne_placement = vmw_vram_ne_placement;
++ int ret;
++
++ ne_placement.lpfn = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
++
++ /* interuptable? */
++ ret = ttm_write_lock(&vmw_priv->fbdev_master.lock, false);
++ if (unlikely(ret != 0))
++ return ret;
++
++ vmw_bo = kmalloc(sizeof(*vmw_bo), GFP_KERNEL);
++ if (!vmw_bo)
++ goto err_unlock;
++
++ ret = vmw_dmabuf_init(vmw_priv, vmw_bo, size,
++ &ne_placement,
++ false,
++ &vmw_dmabuf_bo_free);
++ if (unlikely(ret != 0))
++ goto err_unlock; /* init frees the buffer on failure */
++
++ *out = vmw_bo;
++
++ ttm_write_unlock(&vmw_priv->fbdev_master.lock);
++
++ return 0;
++
++err_unlock:
++ ttm_write_unlock(&vmw_priv->fbdev_master.lock);
++ return ret;
++}
++
++int vmw_fb_init(struct vmw_private *vmw_priv)
++{
++ struct device *device = &vmw_priv->dev->pdev->dev;
++ struct vmw_fb_par *par;
++ struct fb_info *info;
++ unsigned initial_width, initial_height;
++ unsigned fb_width, fb_height;
++ unsigned fb_bbp, fb_depth, fb_offset, fb_pitch, fb_size;
++ int ret;
++
++ initial_width = 800;
++ initial_height = 600;
++
++ fb_bbp = 32;
++ fb_depth = 24;
++
++ if (vmw_priv->capabilities & SVGA_CAP_MULTIMON) {
++ fb_width = min(vmw_priv->fb_max_width, (unsigned)2048);
++ fb_height = min(vmw_priv->fb_max_height, (unsigned)2048);
++ } else {
++ fb_width = min(vmw_priv->fb_max_width, initial_width);
++ fb_height = min(vmw_priv->fb_max_height, initial_height);
++ }
++
++ initial_width = min(fb_width, initial_width);
++ initial_height = min(fb_height, initial_height);
++
++ vmw_write(vmw_priv, SVGA_REG_WIDTH, fb_width);
++ vmw_write(vmw_priv, SVGA_REG_HEIGHT, fb_height);
++ vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, fb_bbp);
++ vmw_write(vmw_priv, SVGA_REG_DEPTH, fb_depth);
++ vmw_write(vmw_priv, SVGA_REG_RED_MASK, 0x00ff0000);
++ vmw_write(vmw_priv, SVGA_REG_GREEN_MASK, 0x0000ff00);
++ vmw_write(vmw_priv, SVGA_REG_BLUE_MASK, 0x000000ff);
++
++ fb_size = vmw_read(vmw_priv, SVGA_REG_FB_SIZE);
++ fb_offset = vmw_read(vmw_priv, SVGA_REG_FB_OFFSET);
++ fb_pitch = vmw_read(vmw_priv, SVGA_REG_BYTES_PER_LINE);
++
++ DRM_DEBUG("width %u\n", vmw_read(vmw_priv, SVGA_REG_MAX_WIDTH));
++ DRM_DEBUG("height %u\n", vmw_read(vmw_priv, SVGA_REG_MAX_HEIGHT));
++ DRM_DEBUG("width %u\n", vmw_read(vmw_priv, SVGA_REG_WIDTH));
++ DRM_DEBUG("height %u\n", vmw_read(vmw_priv, SVGA_REG_HEIGHT));
++ DRM_DEBUG("bpp %u\n", vmw_read(vmw_priv, SVGA_REG_BITS_PER_PIXEL));
++ DRM_DEBUG("depth %u\n", vmw_read(vmw_priv, SVGA_REG_DEPTH));
++ DRM_DEBUG("bpl %u\n", vmw_read(vmw_priv, SVGA_REG_BYTES_PER_LINE));
++ DRM_DEBUG("r mask %08x\n", vmw_read(vmw_priv, SVGA_REG_RED_MASK));
++ DRM_DEBUG("g mask %08x\n", vmw_read(vmw_priv, SVGA_REG_GREEN_MASK));
++ DRM_DEBUG("b mask %08x\n", vmw_read(vmw_priv, SVGA_REG_BLUE_MASK));
++ DRM_DEBUG("fb_offset 0x%08x\n", fb_offset);
++ DRM_DEBUG("fb_pitch %u\n", fb_pitch);
++ DRM_DEBUG("fb_size %u kiB\n", fb_size / 1024);
++
++ info = framebuffer_alloc(sizeof(*par), device);
++ if (!info)
++ return -ENOMEM;
++
++ /*
++ * Par
++ */
++ vmw_priv->fb_info = info;
++ par = info->par;
++ par->vmw_priv = vmw_priv;
++ par->depth = fb_depth;
++ par->bpp = fb_bbp;
++ par->vmalloc = NULL;
++ par->max_width = fb_width;
++ par->max_height = fb_height;
++
++ /*
++ * Create buffers and alloc memory
++ */
++ par->vmalloc = vmalloc(fb_size);
++ if (unlikely(par->vmalloc == NULL)) {
++ ret = -ENOMEM;
++ goto err_free;
++ }
++
++ ret = vmw_fb_create_bo(vmw_priv, fb_size, &par->vmw_bo);
++ if (unlikely(ret != 0))
++ goto err_free;
++
++ ret = ttm_bo_kmap(&par->vmw_bo->base,
++ 0,
++ par->vmw_bo->base.num_pages,
++ &par->map);
++ if (unlikely(ret != 0))
++ goto err_unref;
++ par->bo_ptr = ttm_kmap_obj_virtual(&par->map, &par->bo_iowrite);
++ par->bo_size = fb_size;
++
++ /*
++ * Fixed and var
++ */
++ strcpy(info->fix.id, "svgadrmfb");
++ info->fix.type = FB_TYPE_PACKED_PIXELS;
++ info->fix.visual = FB_VISUAL_TRUECOLOR;
++ info->fix.type_aux = 0;
++ info->fix.xpanstep = 1; /* doing it in hw */
++ info->fix.ypanstep = 1; /* doing it in hw */
++ info->fix.ywrapstep = 0;
++ info->fix.accel = FB_ACCEL_NONE;
++ info->fix.line_length = fb_pitch;
++
++ info->fix.smem_start = 0;
++ info->fix.smem_len = fb_size;
++
++ info->fix.mmio_start = 0;
++ info->fix.mmio_len = 0;
++
++ info->pseudo_palette = par->pseudo_palette;
++ info->screen_base = par->vmalloc;
++ info->screen_size = fb_size;
++
++ info->flags = FBINFO_DEFAULT;
++ info->fbops = &vmw_fb_ops;
++
++ /* 24 depth per default */
++ info->var.red.offset = 16;
++ info->var.green.offset = 8;
++ info->var.blue.offset = 0;
++ info->var.red.length = 8;
++ info->var.green.length = 8;
++ info->var.blue.length = 8;
++ info->var.transp.offset = 0;
++ info->var.transp.length = 0;
++
++ info->var.xres_virtual = fb_width;
++ info->var.yres_virtual = fb_height;
++ info->var.bits_per_pixel = par->bpp;
++ info->var.xoffset = 0;
++ info->var.yoffset = 0;
++ info->var.activate = FB_ACTIVATE_NOW;
++ info->var.height = -1;
++ info->var.width = -1;
++
++ info->var.xres = initial_width;
++ info->var.yres = initial_height;
++
++#if 0
++ info->pixmap.size = 64*1024;
++ info->pixmap.buf_align = 8;
++ info->pixmap.access_align = 32;
++ info->pixmap.flags = FB_PIXMAP_SYSTEM;
++ info->pixmap.scan_align = 1;
++#else
++ info->pixmap.size = 0;
++ info->pixmap.buf_align = 8;
++ info->pixmap.access_align = 32;
++ info->pixmap.flags = FB_PIXMAP_SYSTEM;
++ info->pixmap.scan_align = 1;
++#endif
++
++ /*
++ * Dirty & Deferred IO
++ */
++ par->dirty.x1 = par->dirty.x2 = 0;
++ par->dirty.y1 = par->dirty.y1 = 0;
++ par->dirty.active = true;
++ spin_lock_init(&par->dirty.lock);
++ info->fbdefio = &vmw_defio;
++ fb_deferred_io_init(info);
++
++ ret = register_framebuffer(info);
++ if (unlikely(ret != 0))
++ goto err_defio;
++
++ return 0;
++
++err_defio:
++ fb_deferred_io_cleanup(info);
++ ttm_bo_kunmap(&par->map);
++err_unref:
++ ttm_bo_unref((struct ttm_buffer_object **)&par->vmw_bo);
++err_free:
++ vfree(par->vmalloc);
++ framebuffer_release(info);
++ vmw_priv->fb_info = NULL;
++
++ return ret;
++}
++
++int vmw_fb_close(struct vmw_private *vmw_priv)
++{
++ struct fb_info *info;
++ struct vmw_fb_par *par;
++ struct ttm_buffer_object *bo;
++
++ if (!vmw_priv->fb_info)
++ return 0;
++
++ info = vmw_priv->fb_info;
++ par = info->par;
++ bo = &par->vmw_bo->base;
++ par->vmw_bo = NULL;
++
++ /* ??? order */
++ fb_deferred_io_cleanup(info);
++ unregister_framebuffer(info);
++
++ ttm_bo_kunmap(&par->map);
++ ttm_bo_unref(&bo);
++
++ vfree(par->vmalloc);
++ framebuffer_release(info);
++
++ return 0;
++}
++
++int vmw_dmabuf_from_vram(struct vmw_private *vmw_priv,
++ struct vmw_dma_buffer *vmw_bo)
++{
++ struct ttm_buffer_object *bo = &vmw_bo->base;
++ int ret = 0;
++
++ ret = ttm_bo_reserve(bo, false, false, false, 0);
++ if (unlikely(ret != 0))
++ return ret;
++
++ ret = ttm_bo_validate(bo, &vmw_sys_placement, false, false);
++ ttm_bo_unreserve(bo);
++
++ return ret;
++}
++
++int vmw_dmabuf_to_start_of_vram(struct vmw_private *vmw_priv,
++ struct vmw_dma_buffer *vmw_bo)
++{
++ struct ttm_buffer_object *bo = &vmw_bo->base;
++ struct ttm_placement ne_placement = vmw_vram_ne_placement;
++ int ret = 0;
++
++ ne_placement.lpfn = bo->num_pages;
++
++ /* interuptable? */
++ ret = ttm_write_lock(&vmw_priv->active_master->lock, false);
++ if (unlikely(ret != 0))
++ return ret;
++
++ ret = ttm_bo_reserve(bo, false, false, false, 0);
++ if (unlikely(ret != 0))
++ goto err_unlock;
++
++ ret = ttm_bo_validate(bo, &ne_placement, false, false);
++ ttm_bo_unreserve(bo);
++err_unlock:
++ ttm_write_unlock(&vmw_priv->active_master->lock);
++
++ return ret;
++}
++
++int vmw_fb_off(struct vmw_private *vmw_priv)
++{
++ struct fb_info *info;
++ struct vmw_fb_par *par;
++ unsigned long flags;
++
++ if (!vmw_priv->fb_info)
++ return -EINVAL;
++
++ info = vmw_priv->fb_info;
++ par = info->par;
++
++ spin_lock_irqsave(&par->dirty.lock, flags);
++ par->dirty.active = false;
++ spin_unlock_irqrestore(&par->dirty.lock, flags);
++
++ flush_scheduled_work();
++
++ par->bo_ptr = NULL;
++ ttm_bo_kunmap(&par->map);
++
++ vmw_dmabuf_from_vram(vmw_priv, par->vmw_bo);
++
++ return 0;
++}
++
++int vmw_fb_on(struct vmw_private *vmw_priv)
++{
++ struct fb_info *info;
++ struct vmw_fb_par *par;
++ unsigned long flags;
++ bool dummy;
++ int ret;
++
++ if (!vmw_priv->fb_info)
++ return -EINVAL;
++
++ info = vmw_priv->fb_info;
++ par = info->par;
++
++ /* we are already active */
++ if (par->bo_ptr != NULL)
++ return 0;
++
++ /* Make sure that all overlays are stoped when we take over */
++ vmw_overlay_stop_all(vmw_priv);
++
++ ret = vmw_dmabuf_to_start_of_vram(vmw_priv, par->vmw_bo);
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("could not move buffer to start of VRAM\n");
++ goto err_no_buffer;
++ }
++
++ ret = ttm_bo_kmap(&par->vmw_bo->base,
++ 0,
++ par->vmw_bo->base.num_pages,
++ &par->map);
++ BUG_ON(ret != 0);
++ par->bo_ptr = ttm_kmap_obj_virtual(&par->map, &dummy);
++
++ spin_lock_irqsave(&par->dirty.lock, flags);
++ par->dirty.active = true;
++ spin_unlock_irqrestore(&par->dirty.lock, flags);
++
++err_no_buffer:
++ vmw_fb_set_par(info);
++
++ vmw_fb_dirty_mark(par, 0, 0, info->var.xres, info->var.yres);
++
++ /* If there already was stuff dirty we wont
++ * schedule a new work, so lets do it now */
++ schedule_delayed_work(&info->deferred_work, 0);
++
++ return 0;
++}
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
+new file mode 100644
+index 0000000..39d43a0
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_fifo.c
+@@ -0,0 +1,538 @@
++/**************************************************************************
++ *
++ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++#include "vmwgfx_drv.h"
++#include "drmP.h"
++#include "ttm/ttm_placement.h"
++
++bool vmw_fifo_have_3d(struct vmw_private *dev_priv)
++{
++ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
++ uint32_t fifo_min, hwversion;
++
++ fifo_min = ioread32(fifo_mem + SVGA_FIFO_MIN);
++ if (fifo_min <= SVGA_FIFO_3D_HWVERSION * sizeof(unsigned int))
++ return false;
++
++ hwversion = ioread32(fifo_mem + SVGA_FIFO_3D_HWVERSION);
++ if (hwversion == 0)
++ return false;
++
++ if (hwversion < SVGA3D_HWVERSION_WS65_B1)
++ return false;
++
++ return true;
++}
++
++int vmw_fifo_init(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)
++{
++ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
++ uint32_t max;
++ uint32_t min;
++ uint32_t dummy;
++ int ret;
++
++ fifo->static_buffer_size = VMWGFX_FIFO_STATIC_SIZE;
++ fifo->static_buffer = vmalloc(fifo->static_buffer_size);
++ if (unlikely(fifo->static_buffer == NULL))
++ return -ENOMEM;
++
++ fifo->last_buffer_size = VMWGFX_FIFO_STATIC_SIZE;
++ fifo->last_data_size = 0;
++ fifo->last_buffer_add = false;
++ fifo->last_buffer = vmalloc(fifo->last_buffer_size);
++ if (unlikely(fifo->last_buffer == NULL)) {
++ ret = -ENOMEM;
++ goto out_err;
++ }
++
++ fifo->dynamic_buffer = NULL;
++ fifo->reserved_size = 0;
++ fifo->using_bounce_buffer = false;
++
++ mutex_init(&fifo->fifo_mutex);
++ init_rwsem(&fifo->rwsem);
++
++ /*
++ * Allow mapping the first page read-only to user-space.
++ */
++
++ DRM_INFO("width %d\n", vmw_read(dev_priv, SVGA_REG_WIDTH));
++ DRM_INFO("height %d\n", vmw_read(dev_priv, SVGA_REG_HEIGHT));
++ DRM_INFO("bpp %d\n", vmw_read(dev_priv, SVGA_REG_BITS_PER_PIXEL));
++
++ mutex_lock(&dev_priv->hw_mutex);
++ dev_priv->enable_state = vmw_read(dev_priv, SVGA_REG_ENABLE);
++ dev_priv->config_done_state = vmw_read(dev_priv, SVGA_REG_CONFIG_DONE);
++ vmw_write(dev_priv, SVGA_REG_ENABLE, 1);
++
++ min = 4;
++ if (dev_priv->capabilities & SVGA_CAP_EXTENDED_FIFO)
++ min = vmw_read(dev_priv, SVGA_REG_MEM_REGS);
++ min <<= 2;
++
++ if (min < PAGE_SIZE)
++ min = PAGE_SIZE;
++
++ iowrite32(min, fifo_mem + SVGA_FIFO_MIN);
++ iowrite32(dev_priv->mmio_size, fifo_mem + SVGA_FIFO_MAX);
++ wmb();
++ iowrite32(min, fifo_mem + SVGA_FIFO_NEXT_CMD);
++ iowrite32(min, fifo_mem + SVGA_FIFO_STOP);
++ iowrite32(0, fifo_mem + SVGA_FIFO_BUSY);
++ mb();
++
++ vmw_write(dev_priv, SVGA_REG_CONFIG_DONE, 1);
++ mutex_unlock(&dev_priv->hw_mutex);
++
++ max = ioread32(fifo_mem + SVGA_FIFO_MAX);
++ min = ioread32(fifo_mem + SVGA_FIFO_MIN);
++ fifo->capabilities = ioread32(fifo_mem + SVGA_FIFO_CAPABILITIES);
++
++ DRM_INFO("Fifo max 0x%08x min 0x%08x cap 0x%08x\n",
++ (unsigned int) max,
++ (unsigned int) min,
++ (unsigned int) fifo->capabilities);
++
++ atomic_set(&dev_priv->fence_seq, dev_priv->last_read_sequence);
++ iowrite32(dev_priv->last_read_sequence, fifo_mem + SVGA_FIFO_FENCE);
++
++ return vmw_fifo_send_fence(dev_priv, &dummy);
++out_err:
++ vfree(fifo->static_buffer);
++ fifo->static_buffer = NULL;
++ return ret;
++}
++
++void vmw_fifo_ping_host(struct vmw_private *dev_priv, uint32_t reason)
++{
++ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
++
++ mutex_lock(&dev_priv->hw_mutex);
++
++ if (unlikely(ioread32(fifo_mem + SVGA_FIFO_BUSY) == 0)) {
++ iowrite32(1, fifo_mem + SVGA_FIFO_BUSY);
++ vmw_write(dev_priv, SVGA_REG_SYNC, reason);
++ }
++
++ mutex_unlock(&dev_priv->hw_mutex);
++}
++
++void vmw_fifo_release(struct vmw_private *dev_priv, struct vmw_fifo_state *fifo)
++{
++ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
++
++ mutex_lock(&dev_priv->hw_mutex);
++
++ while (vmw_read(dev_priv, SVGA_REG_BUSY) != 0)
++ vmw_write(dev_priv, SVGA_REG_SYNC, SVGA_SYNC_GENERIC);
++
++ dev_priv->last_read_sequence = ioread32(fifo_mem + SVGA_FIFO_FENCE);
++
++ vmw_write(dev_priv, SVGA_REG_CONFIG_DONE,
++ dev_priv->config_done_state);
++ vmw_write(dev_priv, SVGA_REG_ENABLE,
++ dev_priv->enable_state);
++
++ mutex_unlock(&dev_priv->hw_mutex);
++
++ if (likely(fifo->last_buffer != NULL)) {
++ vfree(fifo->last_buffer);
++ fifo->last_buffer = NULL;
++ }
++
++ if (likely(fifo->static_buffer != NULL)) {
++ vfree(fifo->static_buffer);
++ fifo->static_buffer = NULL;
++ }
++
++ if (likely(fifo->dynamic_buffer != NULL)) {
++ vfree(fifo->dynamic_buffer);
++ fifo->dynamic_buffer = NULL;
++ }
++}
++
++static bool vmw_fifo_is_full(struct vmw_private *dev_priv, uint32_t bytes)
++{
++ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
++ uint32_t max = ioread32(fifo_mem + SVGA_FIFO_MAX);
++ uint32_t next_cmd = ioread32(fifo_mem + SVGA_FIFO_NEXT_CMD);
++ uint32_t min = ioread32(fifo_mem + SVGA_FIFO_MIN);
++ uint32_t stop = ioread32(fifo_mem + SVGA_FIFO_STOP);
++
++ return ((max - next_cmd) + (stop - min) <= bytes);
++}
++
++static int vmw_fifo_wait_noirq(struct vmw_private *dev_priv,
++ uint32_t bytes, bool interruptible,
++ unsigned long timeout)
++{
++ int ret = 0;
++ unsigned long end_jiffies = jiffies + timeout;
++ DEFINE_WAIT(__wait);
++
++ DRM_INFO("Fifo wait noirq.\n");
++
++ for (;;) {
++ prepare_to_wait(&dev_priv->fifo_queue, &__wait,
++ (interruptible) ?
++ TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
++ if (!vmw_fifo_is_full(dev_priv, bytes))
++ break;
++ if (time_after_eq(jiffies, end_jiffies)) {
++ ret = -EBUSY;
++ DRM_ERROR("SVGA device lockup.\n");
++ break;
++ }
++ schedule_timeout(1);
++ if (interruptible && signal_pending(current)) {
++ ret = -ERESTARTSYS;
++ break;
++ }
++ }
++ finish_wait(&dev_priv->fifo_queue, &__wait);
++ wake_up_all(&dev_priv->fifo_queue);
++ DRM_INFO("Fifo noirq exit.\n");
++ return ret;
++}
++
++static int vmw_fifo_wait(struct vmw_private *dev_priv,
++ uint32_t bytes, bool interruptible,
++ unsigned long timeout)
++{
++ long ret = 1L;
++ unsigned long irq_flags;
++
++ if (likely(!vmw_fifo_is_full(dev_priv, bytes)))
++ return 0;
++
++ vmw_fifo_ping_host(dev_priv, SVGA_SYNC_FIFOFULL);
++ if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
++ return vmw_fifo_wait_noirq(dev_priv, bytes,
++ interruptible, timeout);
++
++ mutex_lock(&dev_priv->hw_mutex);
++ if (atomic_add_return(1, &dev_priv->fifo_queue_waiters) > 0) {
++ spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
++ outl(SVGA_IRQFLAG_FIFO_PROGRESS,
++ dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
++ vmw_write(dev_priv, SVGA_REG_IRQMASK,
++ vmw_read(dev_priv, SVGA_REG_IRQMASK) |
++ SVGA_IRQFLAG_FIFO_PROGRESS);
++ spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
++ }
++ mutex_unlock(&dev_priv->hw_mutex);
++
++ if (interruptible)
++ ret = wait_event_interruptible_timeout
++ (dev_priv->fifo_queue,
++ !vmw_fifo_is_full(dev_priv, bytes), timeout);
++ else
++ ret = wait_event_timeout
++ (dev_priv->fifo_queue,
++ !vmw_fifo_is_full(dev_priv, bytes), timeout);
++
++ if (unlikely(ret == 0))
++ ret = -EBUSY;
++ else if (likely(ret > 0))
++ ret = 0;
++
++ mutex_lock(&dev_priv->hw_mutex);
++ if (atomic_dec_and_test(&dev_priv->fifo_queue_waiters)) {
++ spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
++ vmw_write(dev_priv, SVGA_REG_IRQMASK,
++ vmw_read(dev_priv, SVGA_REG_IRQMASK) &
++ ~SVGA_IRQFLAG_FIFO_PROGRESS);
++ spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
++ }
++ mutex_unlock(&dev_priv->hw_mutex);
++
++ return ret;
++}
++
++void *vmw_fifo_reserve(struct vmw_private *dev_priv, uint32_t bytes)
++{
++ struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
++ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
++ uint32_t max;
++ uint32_t min;
++ uint32_t next_cmd;
++ uint32_t reserveable = fifo_state->capabilities & SVGA_FIFO_CAP_RESERVE;
++ int ret;
++
++ mutex_lock(&fifo_state->fifo_mutex);
++ max = ioread32(fifo_mem + SVGA_FIFO_MAX);
++ min = ioread32(fifo_mem + SVGA_FIFO_MIN);
++ next_cmd = ioread32(fifo_mem + SVGA_FIFO_NEXT_CMD);
++
++ if (unlikely(bytes >= (max - min)))
++ goto out_err;
++
++ BUG_ON(fifo_state->reserved_size != 0);
++ BUG_ON(fifo_state->dynamic_buffer != NULL);
++
++ fifo_state->reserved_size = bytes;
++
++ while (1) {
++ uint32_t stop = ioread32(fifo_mem + SVGA_FIFO_STOP);
++ bool need_bounce = false;
++ bool reserve_in_place = false;
++
++ if (next_cmd >= stop) {
++ if (likely((next_cmd + bytes < max ||
++ (next_cmd + bytes == max && stop > min))))
++ reserve_in_place = true;
++
++ else if (vmw_fifo_is_full(dev_priv, bytes)) {
++ ret = vmw_fifo_wait(dev_priv, bytes,
++ false, 3 * HZ);
++ if (unlikely(ret != 0))
++ goto out_err;
++ } else
++ need_bounce = true;
++
++ } else {
++
++ if (likely((next_cmd + bytes < stop)))
++ reserve_in_place = true;
++ else {
++ ret = vmw_fifo_wait(dev_priv, bytes,
++ false, 3 * HZ);
++ if (unlikely(ret != 0))
++ goto out_err;
++ }
++ }
++
++ if (reserve_in_place) {
++ if (reserveable || bytes <= sizeof(uint32_t)) {
++ fifo_state->using_bounce_buffer = false;
++
++ if (reserveable)
++ iowrite32(bytes, fifo_mem +
++ SVGA_FIFO_RESERVED);
++ return fifo_mem + (next_cmd >> 2);
++ } else {
++ need_bounce = true;
++ }
++ }
++
++ if (need_bounce) {
++ fifo_state->using_bounce_buffer = true;
++ if (bytes < fifo_state->static_buffer_size)
++ return fifo_state->static_buffer;
++ else {
++ fifo_state->dynamic_buffer = vmalloc(bytes);
++ return fifo_state->dynamic_buffer;
++ }
++ }
++ }
++out_err:
++ fifo_state->reserved_size = 0;
++ mutex_unlock(&fifo_state->fifo_mutex);
++ return NULL;
++}
++
++static void vmw_fifo_res_copy(struct vmw_fifo_state *fifo_state,
++ __le32 __iomem *fifo_mem,
++ uint32_t next_cmd,
++ uint32_t max, uint32_t min, uint32_t bytes)
++{
++ uint32_t chunk_size = max - next_cmd;
++ uint32_t rest;
++ uint32_t *buffer = (fifo_state->dynamic_buffer != NULL) ?
++ fifo_state->dynamic_buffer : fifo_state->static_buffer;
++
++ if (bytes < chunk_size)
++ chunk_size = bytes;
++
++ iowrite32(bytes, fifo_mem + SVGA_FIFO_RESERVED);
++ mb();
++ memcpy_toio(fifo_mem + (next_cmd >> 2), buffer, chunk_size);
++ rest = bytes - chunk_size;
++ if (rest)
++ memcpy_toio(fifo_mem + (min >> 2), buffer + (chunk_size >> 2),
++ rest);
++}
++
++static void vmw_fifo_slow_copy(struct vmw_fifo_state *fifo_state,
++ __le32 __iomem *fifo_mem,
++ uint32_t next_cmd,
++ uint32_t max, uint32_t min, uint32_t bytes)
++{
++ uint32_t *buffer = (fifo_state->dynamic_buffer != NULL) ?
++ fifo_state->dynamic_buffer : fifo_state->static_buffer;
++
++ while (bytes > 0) {
++ iowrite32(*buffer++, fifo_mem + (next_cmd >> 2));
++ next_cmd += sizeof(uint32_t);
++ if (unlikely(next_cmd == max))
++ next_cmd = min;
++ mb();
++ iowrite32(next_cmd, fifo_mem + SVGA_FIFO_NEXT_CMD);
++ mb();
++ bytes -= sizeof(uint32_t);
++ }
++}
++
++void vmw_fifo_commit(struct vmw_private *dev_priv, uint32_t bytes)
++{
++ struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
++ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
++ uint32_t next_cmd = ioread32(fifo_mem + SVGA_FIFO_NEXT_CMD);
++ uint32_t max = ioread32(fifo_mem + SVGA_FIFO_MAX);
++ uint32_t min = ioread32(fifo_mem + SVGA_FIFO_MIN);
++ bool reserveable = fifo_state->capabilities & SVGA_FIFO_CAP_RESERVE;
++
++ BUG_ON((bytes & 3) != 0);
++ BUG_ON(bytes > fifo_state->reserved_size);
++
++ fifo_state->reserved_size = 0;
++
++ if (fifo_state->using_bounce_buffer) {
++ if (reserveable)
++ vmw_fifo_res_copy(fifo_state, fifo_mem,
++ next_cmd, max, min, bytes);
++ else
++ vmw_fifo_slow_copy(fifo_state, fifo_mem,
++ next_cmd, max, min, bytes);
++
++ if (fifo_state->dynamic_buffer) {
++ vfree(fifo_state->dynamic_buffer);
++ fifo_state->dynamic_buffer = NULL;
++ }
++
++ }
++
++ down_write(&fifo_state->rwsem);
++ if (fifo_state->using_bounce_buffer || reserveable) {
++ next_cmd += bytes;
++ if (next_cmd >= max)
++ next_cmd -= max - min;
++ mb();
++ iowrite32(next_cmd, fifo_mem + SVGA_FIFO_NEXT_CMD);
++ }
++
++ if (reserveable)
++ iowrite32(0, fifo_mem + SVGA_FIFO_RESERVED);
++ mb();
++ up_write(&fifo_state->rwsem);
++ vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
++ mutex_unlock(&fifo_state->fifo_mutex);
++}
++
++int vmw_fifo_send_fence(struct vmw_private *dev_priv, uint32_t *sequence)
++{
++ struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
++ struct svga_fifo_cmd_fence *cmd_fence;
++ void *fm;
++ int ret = 0;
++ uint32_t bytes = sizeof(__le32) + sizeof(*cmd_fence);
++
++ fm = vmw_fifo_reserve(dev_priv, bytes);
++ if (unlikely(fm == NULL)) {
++ *sequence = atomic_read(&dev_priv->fence_seq);
++ ret = -ENOMEM;
++ (void)vmw_fallback_wait(dev_priv, false, true, *sequence,
++ false, 3*HZ);
++ goto out_err;
++ }
++
++ do {
++ *sequence = atomic_add_return(1, &dev_priv->fence_seq);
++ } while (*sequence == 0);
++
++ if (!(fifo_state->capabilities & SVGA_FIFO_CAP_FENCE)) {
++
++ /*
++ * Don't request hardware to send a fence. The
++ * waiting code in vmwgfx_irq.c will emulate this.
++ */
++
++ vmw_fifo_commit(dev_priv, 0);
++ return 0;
++ }
++
++ *(__le32 *) fm = cpu_to_le32(SVGA_CMD_FENCE);
++ cmd_fence = (struct svga_fifo_cmd_fence *)
++ ((unsigned long)fm + sizeof(__le32));
++
++ iowrite32(*sequence, &cmd_fence->fence);
++ fifo_state->last_buffer_add = true;
++ vmw_fifo_commit(dev_priv, bytes);
++ fifo_state->last_buffer_add = false;
++
++out_err:
++ return ret;
++}
++
++/**
++ * Map the first page of the FIFO read-only to user-space.
++ */
++
++static int vmw_fifo_vm_fault(struct vm_area_struct *vma, struct vm_fault *vmf)
++{
++ int ret;
++ unsigned long address = (unsigned long)vmf->virtual_address;
++
++ if (address != vma->vm_start)
++ return VM_FAULT_SIGBUS;
++
++ ret = vm_insert_pfn(vma, address, vma->vm_pgoff);
++ if (likely(ret == -EBUSY || ret == 0))
++ return VM_FAULT_NOPAGE;
++ else if (ret == -ENOMEM)
++ return VM_FAULT_OOM;
++
++ return VM_FAULT_SIGBUS;
++}
++
++static struct vm_operations_struct vmw_fifo_vm_ops = {
++ .fault = vmw_fifo_vm_fault,
++ .open = NULL,
++ .close = NULL
++};
++
++int vmw_fifo_mmap(struct file *filp, struct vm_area_struct *vma)
++{
++ struct drm_file *file_priv;
++ struct vmw_private *dev_priv;
++
++ file_priv = (struct drm_file *)filp->private_data;
++ dev_priv = vmw_priv(file_priv->minor->dev);
++
++ if (vma->vm_pgoff != (dev_priv->mmio_start >> PAGE_SHIFT) ||
++ (vma->vm_end - vma->vm_start) != PAGE_SIZE)
++ return -EINVAL;
++
++ vma->vm_flags &= ~(VM_WRITE | VM_MAYWRITE);
++ vma->vm_flags |= VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_SHARED;
++ vma->vm_page_prot = vm_get_page_prot(vma->vm_flags);
++ vma->vm_page_prot = ttm_io_prot(TTM_PL_FLAG_UNCACHED,
++ vma->vm_page_prot);
++ vma->vm_ops = &vmw_fifo_vm_ops;
++ return 0;
++}
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c
+new file mode 100644
+index 0000000..5f8908a
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_gmr.c
+@@ -0,0 +1,213 @@
++/**************************************************************************
++ *
++ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++#include "vmwgfx_drv.h"
++#include "drmP.h"
++#include "ttm/ttm_bo_driver.h"
++
++/**
++ * FIXME: Adjust to the ttm lowmem / highmem storage to minimize
++ * the number of used descriptors.
++ */
++
++static int vmw_gmr_build_descriptors(struct list_head *desc_pages,
++ struct page *pages[],
++ unsigned long num_pages)
++{
++ struct page *page, *next;
++ struct svga_guest_mem_descriptor *page_virtual = NULL;
++ struct svga_guest_mem_descriptor *desc_virtual = NULL;
++ unsigned int desc_per_page;
++ unsigned long prev_pfn;
++ unsigned long pfn;
++ int ret;
++
++ desc_per_page = PAGE_SIZE /
++ sizeof(struct svga_guest_mem_descriptor) - 1;
++
++ while (likely(num_pages != 0)) {
++ page = alloc_page(__GFP_HIGHMEM);
++ if (unlikely(page == NULL)) {
++ ret = -ENOMEM;
++ goto out_err;
++ }
++
++ list_add_tail(&page->lru, desc_pages);
++
++ /*
++ * Point previous page terminating descriptor to this
++ * page before unmapping it.
++ */
++
++ if (likely(page_virtual != NULL)) {
++ desc_virtual->ppn = page_to_pfn(page);
++ kunmap_atomic(page_virtual, KM_USER0);
++ }
++
++ page_virtual = kmap_atomic(page, KM_USER0);
++ desc_virtual = page_virtual - 1;
++ prev_pfn = ~(0UL);
++
++ while (likely(num_pages != 0)) {
++ pfn = page_to_pfn(*pages);
++
++ if (pfn != prev_pfn + 1) {
++
++ if (desc_virtual - page_virtual ==
++ desc_per_page - 1)
++ break;
++
++ (++desc_virtual)->ppn = cpu_to_le32(pfn);
++ desc_virtual->num_pages = cpu_to_le32(1);
++ } else {
++ uint32_t tmp =
++ le32_to_cpu(desc_virtual->num_pages);
++ desc_virtual->num_pages = cpu_to_le32(tmp + 1);
++ }
++ prev_pfn = pfn;
++ --num_pages;
++ ++pages;
++ }
++
++ (++desc_virtual)->ppn = cpu_to_le32(0);
++ desc_virtual->num_pages = cpu_to_le32(0);
++ }
++
++ if (likely(page_virtual != NULL))
++ kunmap_atomic(page_virtual, KM_USER0);
++
++ return 0;
++out_err:
++ list_for_each_entry_safe(page, next, desc_pages, lru) {
++ list_del_init(&page->lru);
++ __free_page(page);
++ }
++ return ret;
++}
++
++static inline void vmw_gmr_free_descriptors(struct list_head *desc_pages)
++{
++ struct page *page, *next;
++
++ list_for_each_entry_safe(page, next, desc_pages, lru) {
++ list_del_init(&page->lru);
++ __free_page(page);
++ }
++}
++
++static void vmw_gmr_fire_descriptors(struct vmw_private *dev_priv,
++ int gmr_id, struct list_head *desc_pages)
++{
++ struct page *page;
++
++ if (unlikely(list_empty(desc_pages)))
++ return;
++
++ page = list_entry(desc_pages->next, struct page, lru);
++
++ mutex_lock(&dev_priv->hw_mutex);
++
++ vmw_write(dev_priv, SVGA_REG_GMR_ID, gmr_id);
++ wmb();
++ vmw_write(dev_priv, SVGA_REG_GMR_DESCRIPTOR, page_to_pfn(page));
++ mb();
++
++ mutex_unlock(&dev_priv->hw_mutex);
++
++}
++
++/**
++ * FIXME: Adjust to the ttm lowmem / highmem storage to minimize
++ * the number of used descriptors.
++ */
++
++static unsigned long vmw_gmr_count_descriptors(struct page *pages[],
++ unsigned long num_pages)
++{
++ unsigned long prev_pfn = ~(0UL);
++ unsigned long pfn;
++ unsigned long descriptors = 0;
++
++ while (num_pages--) {
++ pfn = page_to_pfn(*pages++);
++ if (prev_pfn + 1 != pfn)
++ ++descriptors;
++ prev_pfn = pfn;
++ }
++
++ return descriptors;
++}
++
++int vmw_gmr_bind(struct vmw_private *dev_priv,
++ struct ttm_buffer_object *bo)
++{
++ struct ttm_tt *ttm = bo->ttm;
++ unsigned long descriptors;
++ int ret;
++ uint32_t id;
++ struct list_head desc_pages;
++
++ if (!(dev_priv->capabilities & SVGA_CAP_GMR))
++ return -EINVAL;
++
++ ret = ttm_tt_populate(ttm);
++ if (unlikely(ret != 0))
++ return ret;
++
++ descriptors = vmw_gmr_count_descriptors(ttm->pages, ttm->num_pages);
++ if (unlikely(descriptors > dev_priv->max_gmr_descriptors))
++ return -EINVAL;
++
++ INIT_LIST_HEAD(&desc_pages);
++ ret = vmw_gmr_build_descriptors(&desc_pages, ttm->pages,
++ ttm->num_pages);
++ if (unlikely(ret != 0))
++ return ret;
++
++ ret = vmw_gmr_id_alloc(dev_priv, &id);
++ if (unlikely(ret != 0))
++ goto out_no_id;
++
++ vmw_gmr_fire_descriptors(dev_priv, id, &desc_pages);
++ vmw_gmr_free_descriptors(&desc_pages);
++ vmw_dmabuf_set_gmr(bo, id);
++ return 0;
++
++out_no_id:
++ vmw_gmr_free_descriptors(&desc_pages);
++ return ret;
++}
++
++void vmw_gmr_unbind(struct vmw_private *dev_priv, int gmr_id)
++{
++ mutex_lock(&dev_priv->hw_mutex);
++ vmw_write(dev_priv, SVGA_REG_GMR_ID, gmr_id);
++ wmb();
++ vmw_write(dev_priv, SVGA_REG_GMR_DESCRIPTOR, 0);
++ mb();
++ mutex_unlock(&dev_priv->hw_mutex);
++}
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
+new file mode 100644
+index 0000000..1c7a316
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ioctl.c
+@@ -0,0 +1,87 @@
++/**************************************************************************
++ *
++ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++#include "vmwgfx_drv.h"
++#include "vmwgfx_drm.h"
++
++int vmw_getparam_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ struct drm_vmw_getparam_arg *param =
++ (struct drm_vmw_getparam_arg *)data;
++
++ switch (param->param) {
++ case DRM_VMW_PARAM_NUM_STREAMS:
++ param->value = vmw_overlay_num_overlays(dev_priv);
++ break;
++ case DRM_VMW_PARAM_NUM_FREE_STREAMS:
++ param->value = vmw_overlay_num_free_overlays(dev_priv);
++ break;
++ case DRM_VMW_PARAM_3D:
++ param->value = vmw_fifo_have_3d(dev_priv) ? 1 : 0;
++ break;
++ case DRM_VMW_PARAM_FIFO_OFFSET:
++ param->value = dev_priv->mmio_start;
++ break;
++ case DRM_VMW_PARAM_HW_CAPS:
++ param->value = dev_priv->capabilities;
++ break;
++ case DRM_VMW_PARAM_FIFO_CAPS:
++ param->value = dev_priv->fifo.capabilities;
++ break;
++ default:
++ DRM_ERROR("Illegal vmwgfx get param request: %d\n",
++ param->param);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++int vmw_fifo_debug_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
++ struct drm_vmw_fifo_debug_arg *arg =
++ (struct drm_vmw_fifo_debug_arg *)data;
++ __le32 __user *buffer = (__le32 __user *)
++ (unsigned long)arg->debug_buffer;
++
++ if (unlikely(fifo_state->last_buffer == NULL))
++ return -EINVAL;
++
++ if (arg->debug_buffer_size < fifo_state->last_data_size) {
++ arg->used_size = arg->debug_buffer_size;
++ arg->did_not_fit = 1;
++ } else {
++ arg->used_size = fifo_state->last_data_size;
++ arg->did_not_fit = 0;
++ }
++ return copy_to_user(buffer, fifo_state->last_buffer, arg->used_size);
++}
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c
+new file mode 100644
+index 0000000..4d7cb53
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_irq.c
+@@ -0,0 +1,286 @@
++/**************************************************************************
++ *
++ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++#include "drmP.h"
++#include "vmwgfx_drv.h"
++
++#define VMW_FENCE_WRAP (1 << 24)
++
++irqreturn_t vmw_irq_handler(DRM_IRQ_ARGS)
++{
++ struct drm_device *dev = (struct drm_device *)arg;
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ uint32_t status;
++
++ spin_lock(&dev_priv->irq_lock);
++ status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
++ spin_unlock(&dev_priv->irq_lock);
++
++ if (status & SVGA_IRQFLAG_ANY_FENCE)
++ wake_up_all(&dev_priv->fence_queue);
++ if (status & SVGA_IRQFLAG_FIFO_PROGRESS)
++ wake_up_all(&dev_priv->fifo_queue);
++
++ if (likely(status)) {
++ outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
++ return IRQ_HANDLED;
++ }
++
++ return IRQ_NONE;
++}
++
++static bool vmw_fifo_idle(struct vmw_private *dev_priv, uint32_t sequence)
++{
++ uint32_t busy;
++
++ mutex_lock(&dev_priv->hw_mutex);
++ busy = vmw_read(dev_priv, SVGA_REG_BUSY);
++ mutex_unlock(&dev_priv->hw_mutex);
++
++ return (busy == 0);
++}
++
++
++bool vmw_fence_signaled(struct vmw_private *dev_priv,
++ uint32_t sequence)
++{
++ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
++ struct vmw_fifo_state *fifo_state;
++ bool ret;
++
++ if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP))
++ return true;
++
++ dev_priv->last_read_sequence = ioread32(fifo_mem + SVGA_FIFO_FENCE);
++ if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP))
++ return true;
++
++ fifo_state = &dev_priv->fifo;
++ if (!(fifo_state->capabilities & SVGA_FIFO_CAP_FENCE) &&
++ vmw_fifo_idle(dev_priv, sequence))
++ return true;
++
++ /**
++ * Then check if the sequence is higher than what we've actually
++ * emitted. Then the fence is stale and signaled.
++ */
++
++ ret = ((atomic_read(&dev_priv->fence_seq) - sequence)
++ > VMW_FENCE_WRAP);
++
++ return ret;
++}
++
++int vmw_fallback_wait(struct vmw_private *dev_priv,
++ bool lazy,
++ bool fifo_idle,
++ uint32_t sequence,
++ bool interruptible,
++ unsigned long timeout)
++{
++ struct vmw_fifo_state *fifo_state = &dev_priv->fifo;
++
++ uint32_t count = 0;
++ uint32_t signal_seq;
++ int ret;
++ unsigned long end_jiffies = jiffies + timeout;
++ bool (*wait_condition)(struct vmw_private *, uint32_t);
++ DEFINE_WAIT(__wait);
++
++ wait_condition = (fifo_idle) ? &vmw_fifo_idle :
++ &vmw_fence_signaled;
++
++ /**
++ * Block command submission while waiting for idle.
++ */
++
++ if (fifo_idle)
++ down_read(&fifo_state->rwsem);
++ signal_seq = atomic_read(&dev_priv->fence_seq);
++ ret = 0;
++
++ for (;;) {
++ prepare_to_wait(&dev_priv->fence_queue, &__wait,
++ (interruptible) ?
++ TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE);
++ if (wait_condition(dev_priv, sequence))
++ break;
++ if (time_after_eq(jiffies, end_jiffies)) {
++ DRM_ERROR("SVGA device lockup.\n");
++ break;
++ }
++ if (lazy)
++ schedule_timeout(1);
++ else if ((++count & 0x0F) == 0) {
++ /**
++ * FIXME: Use schedule_hr_timeout here for
++ * newer kernels and lower CPU utilization.
++ */
++
++ __set_current_state(TASK_RUNNING);
++ schedule();
++ __set_current_state((interruptible) ?
++ TASK_INTERRUPTIBLE :
++ TASK_UNINTERRUPTIBLE);
++ }
++ if (interruptible && signal_pending(current)) {
++ ret = -ERESTARTSYS;
++ break;
++ }
++ }
++ finish_wait(&dev_priv->fence_queue, &__wait);
++ if (ret == 0 && fifo_idle) {
++ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
++ iowrite32(signal_seq, fifo_mem + SVGA_FIFO_FENCE);
++ }
++ wake_up_all(&dev_priv->fence_queue);
++ if (fifo_idle)
++ up_read(&fifo_state->rwsem);
++
++ return ret;
++}
++
++int vmw_wait_fence(struct vmw_private *dev_priv,
++ bool lazy, uint32_t sequence,
++ bool interruptible, unsigned long timeout)
++{
++ long ret;
++ unsigned long irq_flags;
++ struct vmw_fifo_state *fifo = &dev_priv->fifo;
++
++ if (likely(dev_priv->last_read_sequence - sequence < VMW_FENCE_WRAP))
++ return 0;
++
++ if (likely(vmw_fence_signaled(dev_priv, sequence)))
++ return 0;
++
++ vmw_fifo_ping_host(dev_priv, SVGA_SYNC_GENERIC);
++
++ if (!(fifo->capabilities & SVGA_FIFO_CAP_FENCE))
++ return vmw_fallback_wait(dev_priv, lazy, true, sequence,
++ interruptible, timeout);
++
++ if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
++ return vmw_fallback_wait(dev_priv, lazy, false, sequence,
++ interruptible, timeout);
++
++ mutex_lock(&dev_priv->hw_mutex);
++ if (atomic_add_return(1, &dev_priv->fence_queue_waiters) > 0) {
++ spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
++ outl(SVGA_IRQFLAG_ANY_FENCE,
++ dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
++ vmw_write(dev_priv, SVGA_REG_IRQMASK,
++ vmw_read(dev_priv, SVGA_REG_IRQMASK) |
++ SVGA_IRQFLAG_ANY_FENCE);
++ spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
++ }
++ mutex_unlock(&dev_priv->hw_mutex);
++
++ if (interruptible)
++ ret = wait_event_interruptible_timeout
++ (dev_priv->fence_queue,
++ vmw_fence_signaled(dev_priv, sequence),
++ timeout);
++ else
++ ret = wait_event_timeout
++ (dev_priv->fence_queue,
++ vmw_fence_signaled(dev_priv, sequence),
++ timeout);
++
++ if (unlikely(ret == 0))
++ ret = -EBUSY;
++ else if (likely(ret > 0))
++ ret = 0;
++
++ mutex_lock(&dev_priv->hw_mutex);
++ if (atomic_dec_and_test(&dev_priv->fence_queue_waiters)) {
++ spin_lock_irqsave(&dev_priv->irq_lock, irq_flags);
++ vmw_write(dev_priv, SVGA_REG_IRQMASK,
++ vmw_read(dev_priv, SVGA_REG_IRQMASK) &
++ ~SVGA_IRQFLAG_ANY_FENCE);
++ spin_unlock_irqrestore(&dev_priv->irq_lock, irq_flags);
++ }
++ mutex_unlock(&dev_priv->hw_mutex);
++
++ return ret;
++}
++
++void vmw_irq_preinstall(struct drm_device *dev)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ uint32_t status;
++
++ if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
++ return;
++
++ spin_lock_init(&dev_priv->irq_lock);
++ status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
++ outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
++}
++
++int vmw_irq_postinstall(struct drm_device *dev)
++{
++ return 0;
++}
++
++void vmw_irq_uninstall(struct drm_device *dev)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ uint32_t status;
++
++ if (!(dev_priv->capabilities & SVGA_CAP_IRQMASK))
++ return;
++
++ mutex_lock(&dev_priv->hw_mutex);
++ vmw_write(dev_priv, SVGA_REG_IRQMASK, 0);
++ mutex_unlock(&dev_priv->hw_mutex);
++
++ status = inl(dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
++ outl(status, dev_priv->io_start + VMWGFX_IRQSTATUS_PORT);
++}
++
++#define VMW_FENCE_WAIT_TIMEOUT 3*HZ;
++
++int vmw_fence_wait_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_vmw_fence_wait_arg *arg =
++ (struct drm_vmw_fence_wait_arg *)data;
++ unsigned long timeout;
++
++ if (!arg->cookie_valid) {
++ arg->cookie_valid = 1;
++ arg->kernel_cookie = jiffies + VMW_FENCE_WAIT_TIMEOUT;
++ }
++
++ timeout = jiffies;
++ if (time_after_eq(timeout, (unsigned long)arg->kernel_cookie))
++ return -EBUSY;
++
++ timeout = (unsigned long)arg->kernel_cookie - timeout;
++ return vmw_wait_fence(vmw_priv(dev), true, arg->sequence, true, timeout);
++}
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+new file mode 100644
+index 0000000..31f9afe
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.c
+@@ -0,0 +1,880 @@
++/**************************************************************************
++ *
++ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++#include "vmwgfx_kms.h"
++
++/* Might need a hrtimer here? */
++#define VMWGFX_PRESENT_RATE ((HZ / 60 > 0) ? HZ / 60 : 1)
++
++
++void vmw_display_unit_cleanup(struct vmw_display_unit *du)
++{
++ if (du->cursor_surface)
++ vmw_surface_unreference(&du->cursor_surface);
++ if (du->cursor_dmabuf)
++ vmw_dmabuf_unreference(&du->cursor_dmabuf);
++ drm_crtc_cleanup(&du->crtc);
++ drm_encoder_cleanup(&du->encoder);
++ drm_connector_cleanup(&du->connector);
++}
++
++/*
++ * Display Unit Cursor functions
++ */
++
++int vmw_cursor_update_image(struct vmw_private *dev_priv,
++ u32 *image, u32 width, u32 height,
++ u32 hotspotX, u32 hotspotY)
++{
++ struct {
++ u32 cmd;
++ SVGAFifoCmdDefineAlphaCursor cursor;
++ } *cmd;
++ u32 image_size = width * height * 4;
++ u32 cmd_size = sizeof(*cmd) + image_size;
++
++ if (!image)
++ return -EINVAL;
++
++ cmd = vmw_fifo_reserve(dev_priv, cmd_size);
++ if (unlikely(cmd == NULL)) {
++ DRM_ERROR("Fifo reserve failed.\n");
++ return -ENOMEM;
++ }
++
++ memset(cmd, 0, sizeof(*cmd));
++
++ memcpy(&cmd[1], image, image_size);
++
++ cmd->cmd = cpu_to_le32(SVGA_CMD_DEFINE_ALPHA_CURSOR);
++ cmd->cursor.id = cpu_to_le32(0);
++ cmd->cursor.width = cpu_to_le32(width);
++ cmd->cursor.height = cpu_to_le32(height);
++ cmd->cursor.hotspotX = cpu_to_le32(hotspotX);
++ cmd->cursor.hotspotY = cpu_to_le32(hotspotY);
++
++ vmw_fifo_commit(dev_priv, cmd_size);
++
++ return 0;
++}
++
++void vmw_cursor_update_position(struct vmw_private *dev_priv,
++ bool show, int x, int y)
++{
++ __le32 __iomem *fifo_mem = dev_priv->mmio_virt;
++ uint32_t count;
++
++ iowrite32(show ? 1 : 0, fifo_mem + SVGA_FIFO_CURSOR_ON);
++ iowrite32(x, fifo_mem + SVGA_FIFO_CURSOR_X);
++ iowrite32(y, fifo_mem + SVGA_FIFO_CURSOR_Y);
++ count = ioread32(fifo_mem + SVGA_FIFO_CURSOR_COUNT);
++ iowrite32(++count, fifo_mem + SVGA_FIFO_CURSOR_COUNT);
++}
++
++int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
++ uint32_t handle, uint32_t width, uint32_t height)
++{
++ struct vmw_private *dev_priv = vmw_priv(crtc->dev);
++ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
++ struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
++ struct vmw_surface *surface = NULL;
++ struct vmw_dma_buffer *dmabuf = NULL;
++ int ret;
++
++ if (handle) {
++ ret = vmw_user_surface_lookup_handle(dev_priv, tfile,
++ handle, &surface);
++ if (!ret) {
++ if (!surface->snooper.image) {
++ DRM_ERROR("surface not suitable for cursor\n");
++ return -EINVAL;
++ }
++ } else {
++ ret = vmw_user_dmabuf_lookup(tfile,
++ handle, &dmabuf);
++ if (ret) {
++ DRM_ERROR("failed to find surface or dmabuf: %i\n", ret);
++ return -EINVAL;
++ }
++ }
++ }
++
++ /* takedown old cursor */
++ if (du->cursor_surface) {
++ du->cursor_surface->snooper.crtc = NULL;
++ vmw_surface_unreference(&du->cursor_surface);
++ }
++ if (du->cursor_dmabuf)
++ vmw_dmabuf_unreference(&du->cursor_dmabuf);
++
++ /* setup new image */
++ if (surface) {
++ /* vmw_user_surface_lookup takes one reference */
++ du->cursor_surface = surface;
++
++ du->cursor_surface->snooper.crtc = crtc;
++ du->cursor_age = du->cursor_surface->snooper.age;
++ vmw_cursor_update_image(dev_priv, surface->snooper.image,
++ 64, 64, du->hotspot_x, du->hotspot_y);
++ } else if (dmabuf) {
++ struct ttm_bo_kmap_obj map;
++ unsigned long kmap_offset;
++ unsigned long kmap_num;
++ void *virtual;
++ bool dummy;
++
++ /* vmw_user_surface_lookup takes one reference */
++ du->cursor_dmabuf = dmabuf;
++
++ kmap_offset = 0;
++ kmap_num = (64*64*4) >> PAGE_SHIFT;
++
++ ret = ttm_bo_reserve(&dmabuf->base, true, false, false, 0);
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("reserve failed\n");
++ return -EINVAL;
++ }
++
++ ret = ttm_bo_kmap(&dmabuf->base, kmap_offset, kmap_num, &map);
++ if (unlikely(ret != 0))
++ goto err_unreserve;
++
++ virtual = ttm_kmap_obj_virtual(&map, &dummy);
++ vmw_cursor_update_image(dev_priv, virtual, 64, 64,
++ du->hotspot_x, du->hotspot_y);
++
++ ttm_bo_kunmap(&map);
++err_unreserve:
++ ttm_bo_unreserve(&dmabuf->base);
++
++ } else {
++ vmw_cursor_update_position(dev_priv, false, 0, 0);
++ return 0;
++ }
++
++ vmw_cursor_update_position(dev_priv, true, du->cursor_x, du->cursor_y);
++
++ return 0;
++}
++
++int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y)
++{
++ struct vmw_private *dev_priv = vmw_priv(crtc->dev);
++ struct vmw_display_unit *du = vmw_crtc_to_du(crtc);
++ bool shown = du->cursor_surface || du->cursor_dmabuf ? true : false;
++
++ du->cursor_x = x + crtc->x;
++ du->cursor_y = y + crtc->y;
++
++ vmw_cursor_update_position(dev_priv, shown,
++ du->cursor_x, du->cursor_y);
++
++ return 0;
++}
++
++void vmw_kms_cursor_snoop(struct vmw_surface *srf,
++ struct ttm_object_file *tfile,
++ struct ttm_buffer_object *bo,
++ SVGA3dCmdHeader *header)
++{
++ struct ttm_bo_kmap_obj map;
++ unsigned long kmap_offset;
++ unsigned long kmap_num;
++ SVGA3dCopyBox *box;
++ unsigned box_count;
++ void *virtual;
++ bool dummy;
++ struct vmw_dma_cmd {
++ SVGA3dCmdHeader header;
++ SVGA3dCmdSurfaceDMA dma;
++ } *cmd;
++ int ret;
++
++ cmd = container_of(header, struct vmw_dma_cmd, header);
++
++ /* No snooper installed */
++ if (!srf->snooper.image)
++ return;
++
++ if (cmd->dma.host.face != 0 || cmd->dma.host.mipmap != 0) {
++ DRM_ERROR("face and mipmap for cursors should never != 0\n");
++ return;
++ }
++
++ if (cmd->header.size < 64) {
++ DRM_ERROR("at least one full copy box must be given\n");
++ return;
++ }
++
++ box = (SVGA3dCopyBox *)&cmd[1];
++ box_count = (cmd->header.size - sizeof(SVGA3dCmdSurfaceDMA)) /
++ sizeof(SVGA3dCopyBox);
++
++ if (cmd->dma.guest.pitch != (64 * 4) ||
++ cmd->dma.guest.ptr.offset % PAGE_SIZE ||
++ box->x != 0 || box->y != 0 || box->z != 0 ||
++ box->srcx != 0 || box->srcy != 0 || box->srcz != 0 ||
++ box->w != 64 || box->h != 64 || box->d != 1 ||
++ box_count != 1) {
++ /* TODO handle none page aligned offsets */
++ /* TODO handle partial uploads and pitch != 256 */
++ /* TODO handle more then one copy (size != 64) */
++ DRM_ERROR("lazy programer, cant handle wierd stuff\n");
++ return;
++ }
++
++ kmap_offset = cmd->dma.guest.ptr.offset >> PAGE_SHIFT;
++ kmap_num = (64*64*4) >> PAGE_SHIFT;
++
++ ret = ttm_bo_reserve(bo, true, false, false, 0);
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("reserve failed\n");
++ return;
++ }
++
++ ret = ttm_bo_kmap(bo, kmap_offset, kmap_num, &map);
++ if (unlikely(ret != 0))
++ goto err_unreserve;
++
++ virtual = ttm_kmap_obj_virtual(&map, &dummy);
++
++ memcpy(srf->snooper.image, virtual, 64*64*4);
++ srf->snooper.age++;
++
++ /* we can't call this function from this function since execbuf has
++ * reserved fifo space.
++ *
++ * if (srf->snooper.crtc)
++ * vmw_ldu_crtc_cursor_update_image(dev_priv,
++ * srf->snooper.image, 64, 64,
++ * du->hotspot_x, du->hotspot_y);
++ */
++
++ ttm_bo_kunmap(&map);
++err_unreserve:
++ ttm_bo_unreserve(bo);
++}
++
++void vmw_kms_cursor_post_execbuf(struct vmw_private *dev_priv)
++{
++ struct drm_device *dev = dev_priv->dev;
++ struct vmw_display_unit *du;
++ struct drm_crtc *crtc;
++
++ mutex_lock(&dev->mode_config.mutex);
++
++ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
++ du = vmw_crtc_to_du(crtc);
++ if (!du->cursor_surface ||
++ du->cursor_age == du->cursor_surface->snooper.age)
++ continue;
++
++ du->cursor_age = du->cursor_surface->snooper.age;
++ vmw_cursor_update_image(dev_priv,
++ du->cursor_surface->snooper.image,
++ 64, 64, du->hotspot_x, du->hotspot_y);
++ }
++
++ mutex_unlock(&dev->mode_config.mutex);
++}
++
++/*
++ * Generic framebuffer code
++ */
++
++int vmw_framebuffer_create_handle(struct drm_framebuffer *fb,
++ struct drm_file *file_priv,
++ unsigned int *handle)
++{
++ if (handle)
++ handle = 0;
++
++ return 0;
++}
++
++/*
++ * Surface framebuffer code
++ */
++
++#define vmw_framebuffer_to_vfbs(x) \
++ container_of(x, struct vmw_framebuffer_surface, base.base)
++
++struct vmw_framebuffer_surface {
++ struct vmw_framebuffer base;
++ struct vmw_surface *surface;
++ struct delayed_work d_work;
++ struct mutex work_lock;
++ bool present_fs;
++};
++
++void vmw_framebuffer_surface_destroy(struct drm_framebuffer *framebuffer)
++{
++ struct vmw_framebuffer_surface *vfb =
++ vmw_framebuffer_to_vfbs(framebuffer);
++
++ cancel_delayed_work_sync(&vfb->d_work);
++ drm_framebuffer_cleanup(framebuffer);
++ vmw_surface_unreference(&vfb->surface);
++
++ kfree(framebuffer);
++}
++
++static void vmw_framebuffer_present_fs_callback(struct work_struct *work)
++{
++ struct delayed_work *d_work =
++ container_of(work, struct delayed_work, work);
++ struct vmw_framebuffer_surface *vfbs =
++ container_of(d_work, struct vmw_framebuffer_surface, d_work);
++ struct vmw_surface *surf = vfbs->surface;
++ struct drm_framebuffer *framebuffer = &vfbs->base.base;
++ struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
++
++ struct {
++ SVGA3dCmdHeader header;
++ SVGA3dCmdPresent body;
++ SVGA3dCopyRect cr;
++ } *cmd;
++
++ mutex_lock(&vfbs->work_lock);
++ if (!vfbs->present_fs)
++ goto out_unlock;
++
++ cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
++ if (unlikely(cmd == NULL))
++ goto out_resched;
++
++ cmd->header.id = cpu_to_le32(SVGA_3D_CMD_PRESENT);
++ cmd->header.size = cpu_to_le32(sizeof(cmd->body) + sizeof(cmd->cr));
++ cmd->body.sid = cpu_to_le32(surf->res.id);
++ cmd->cr.x = cpu_to_le32(0);
++ cmd->cr.y = cpu_to_le32(0);
++ cmd->cr.srcx = cmd->cr.x;
++ cmd->cr.srcy = cmd->cr.y;
++ cmd->cr.w = cpu_to_le32(framebuffer->width);
++ cmd->cr.h = cpu_to_le32(framebuffer->height);
++ vfbs->present_fs = false;
++ vmw_fifo_commit(dev_priv, sizeof(*cmd));
++out_resched:
++ /**
++ * Will not re-add if already pending.
++ */
++ schedule_delayed_work(&vfbs->d_work, VMWGFX_PRESENT_RATE);
++out_unlock:
++ mutex_unlock(&vfbs->work_lock);
++}
++
++
++int vmw_framebuffer_surface_dirty(struct drm_framebuffer *framebuffer,
++ unsigned flags, unsigned color,
++ struct drm_clip_rect *clips,
++ unsigned num_clips)
++{
++ struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
++ struct vmw_framebuffer_surface *vfbs =
++ vmw_framebuffer_to_vfbs(framebuffer);
++ struct vmw_surface *surf = vfbs->surface;
++ struct drm_clip_rect norect;
++ SVGA3dCopyRect *cr;
++ int i, inc = 1;
++
++ struct {
++ SVGA3dCmdHeader header;
++ SVGA3dCmdPresent body;
++ SVGA3dCopyRect cr;
++ } *cmd;
++
++ if (!num_clips ||
++ !(dev_priv->fifo.capabilities &
++ SVGA_FIFO_CAP_SCREEN_OBJECT)) {
++ int ret;
++
++ mutex_lock(&vfbs->work_lock);
++ vfbs->present_fs = true;
++ ret = schedule_delayed_work(&vfbs->d_work, VMWGFX_PRESENT_RATE);
++ mutex_unlock(&vfbs->work_lock);
++ if (ret) {
++ /**
++ * No work pending, Force immediate present.
++ */
++ vmw_framebuffer_present_fs_callback(&vfbs->d_work.work);
++ }
++ return 0;
++ }
++
++ if (!num_clips) {
++ num_clips = 1;
++ clips = &norect;
++ norect.x1 = norect.y1 = 0;
++ norect.x2 = framebuffer->width;
++ norect.y2 = framebuffer->height;
++ } else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {
++ num_clips /= 2;
++ inc = 2; /* skip source rects */
++ }
++
++ cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr));
++ if (unlikely(cmd == NULL)) {
++ DRM_ERROR("Fifo reserve failed.\n");
++ return -ENOMEM;
++ }
++
++ memset(cmd, 0, sizeof(*cmd));
++
++ cmd->header.id = cpu_to_le32(SVGA_3D_CMD_PRESENT);
++ cmd->header.size = cpu_to_le32(sizeof(cmd->body) + num_clips * sizeof(cmd->cr));
++ cmd->body.sid = cpu_to_le32(surf->res.id);
++
++ for (i = 0, cr = &cmd->cr; i < num_clips; i++, cr++, clips += inc) {
++ cr->x = cpu_to_le16(clips->x1);
++ cr->y = cpu_to_le16(clips->y1);
++ cr->srcx = cr->x;
++ cr->srcy = cr->y;
++ cr->w = cpu_to_le16(clips->x2 - clips->x1);
++ cr->h = cpu_to_le16(clips->y2 - clips->y1);
++ }
++
++ vmw_fifo_commit(dev_priv, sizeof(*cmd) + (num_clips - 1) * sizeof(cmd->cr));
++
++ return 0;
++}
++
++static struct drm_framebuffer_funcs vmw_framebuffer_surface_funcs = {
++ .destroy = vmw_framebuffer_surface_destroy,
++ .dirty = vmw_framebuffer_surface_dirty,
++ .create_handle = vmw_framebuffer_create_handle,
++};
++
++int vmw_kms_new_framebuffer_surface(struct vmw_private *dev_priv,
++ struct vmw_surface *surface,
++ struct vmw_framebuffer **out,
++ unsigned width, unsigned height)
++
++{
++ struct drm_device *dev = dev_priv->dev;
++ struct vmw_framebuffer_surface *vfbs;
++ int ret;
++
++ vfbs = kzalloc(sizeof(*vfbs), GFP_KERNEL);
++ if (!vfbs) {
++ ret = -ENOMEM;
++ goto out_err1;
++ }
++
++ ret = drm_framebuffer_init(dev, &vfbs->base.base,
++ &vmw_framebuffer_surface_funcs);
++ if (ret)
++ goto out_err2;
++
++ if (!vmw_surface_reference(surface)) {
++ DRM_ERROR("failed to reference surface %p\n", surface);
++ goto out_err3;
++ }
++
++ /* XXX get the first 3 from the surface info */
++ vfbs->base.base.bits_per_pixel = 32;
++ vfbs->base.base.pitch = width * 32 / 4;
++ vfbs->base.base.depth = 24;
++ vfbs->base.base.width = width;
++ vfbs->base.base.height = height;
++ vfbs->base.pin = NULL;
++ vfbs->base.unpin = NULL;
++ vfbs->surface = surface;
++ mutex_init(&vfbs->work_lock);
++ INIT_DELAYED_WORK(&vfbs->d_work, &vmw_framebuffer_present_fs_callback);
++ *out = &vfbs->base;
++
++ return 0;
++
++out_err3:
++ drm_framebuffer_cleanup(&vfbs->base.base);
++out_err2:
++ kfree(vfbs);
++out_err1:
++ return ret;
++}
++
++/*
++ * Dmabuf framebuffer code
++ */
++
++#define vmw_framebuffer_to_vfbd(x) \
++ container_of(x, struct vmw_framebuffer_dmabuf, base.base)
++
++struct vmw_framebuffer_dmabuf {
++ struct vmw_framebuffer base;
++ struct vmw_dma_buffer *buffer;
++};
++
++void vmw_framebuffer_dmabuf_destroy(struct drm_framebuffer *framebuffer)
++{
++ struct vmw_framebuffer_dmabuf *vfbd =
++ vmw_framebuffer_to_vfbd(framebuffer);
++
++ drm_framebuffer_cleanup(framebuffer);
++ vmw_dmabuf_unreference(&vfbd->buffer);
++
++ kfree(vfbd);
++}
++
++int vmw_framebuffer_dmabuf_dirty(struct drm_framebuffer *framebuffer,
++ unsigned flags, unsigned color,
++ struct drm_clip_rect *clips,
++ unsigned num_clips)
++{
++ struct vmw_private *dev_priv = vmw_priv(framebuffer->dev);
++ struct drm_clip_rect norect;
++ struct {
++ uint32_t header;
++ SVGAFifoCmdUpdate body;
++ } *cmd;
++ int i, increment = 1;
++
++ if (!num_clips) {
++ num_clips = 1;
++ clips = &norect;
++ norect.x1 = norect.y1 = 0;
++ norect.x2 = framebuffer->width;
++ norect.y2 = framebuffer->height;
++ } else if (flags & DRM_MODE_FB_DIRTY_ANNOTATE_COPY) {
++ num_clips /= 2;
++ increment = 2;
++ }
++
++ cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd) * num_clips);
++ if (unlikely(cmd == NULL)) {
++ DRM_ERROR("Fifo reserve failed.\n");
++ return -ENOMEM;
++ }
++
++ for (i = 0; i < num_clips; i++, clips += increment) {
++ cmd[i].header = cpu_to_le32(SVGA_CMD_UPDATE);
++ cmd[i].body.x = cpu_to_le32(clips->x1);
++ cmd[i].body.y = cpu_to_le32(clips->y1);
++ cmd[i].body.width = cpu_to_le32(clips->x2 - clips->x1);
++ cmd[i].body.height = cpu_to_le32(clips->y2 - clips->y1);
++ }
++
++ vmw_fifo_commit(dev_priv, sizeof(*cmd) * num_clips);
++
++ return 0;
++}
++
++static struct drm_framebuffer_funcs vmw_framebuffer_dmabuf_funcs = {
++ .destroy = vmw_framebuffer_dmabuf_destroy,
++ .dirty = vmw_framebuffer_dmabuf_dirty,
++ .create_handle = vmw_framebuffer_create_handle,
++};
++
++static int vmw_framebuffer_dmabuf_pin(struct vmw_framebuffer *vfb)
++{
++ struct vmw_private *dev_priv = vmw_priv(vfb->base.dev);
++ struct vmw_framebuffer_dmabuf *vfbd =
++ vmw_framebuffer_to_vfbd(&vfb->base);
++ int ret;
++
++ vmw_overlay_pause_all(dev_priv);
++
++ ret = vmw_dmabuf_to_start_of_vram(dev_priv, vfbd->buffer);
++
++ if (dev_priv->capabilities & SVGA_CAP_MULTIMON) {
++ vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, 0);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_IS_PRIMARY, true);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_X, 0);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_Y, 0);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_WIDTH, 0);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_HEIGHT, 0);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
++
++ vmw_write(dev_priv, SVGA_REG_ENABLE, 1);
++ vmw_write(dev_priv, SVGA_REG_WIDTH, vfb->base.width);
++ vmw_write(dev_priv, SVGA_REG_HEIGHT, vfb->base.height);
++ vmw_write(dev_priv, SVGA_REG_BITS_PER_PIXEL, vfb->base.bits_per_pixel);
++ vmw_write(dev_priv, SVGA_REG_DEPTH, vfb->base.depth);
++ vmw_write(dev_priv, SVGA_REG_RED_MASK, 0x00ff0000);
++ vmw_write(dev_priv, SVGA_REG_GREEN_MASK, 0x0000ff00);
++ vmw_write(dev_priv, SVGA_REG_BLUE_MASK, 0x000000ff);
++ } else
++ WARN_ON(true);
++
++ vmw_overlay_resume_all(dev_priv);
++
++ return 0;
++}
++
++static int vmw_framebuffer_dmabuf_unpin(struct vmw_framebuffer *vfb)
++{
++ struct vmw_private *dev_priv = vmw_priv(vfb->base.dev);
++ struct vmw_framebuffer_dmabuf *vfbd =
++ vmw_framebuffer_to_vfbd(&vfb->base);
++
++ if (!vfbd->buffer) {
++ WARN_ON(!vfbd->buffer);
++ return 0;
++ }
++
++ return vmw_dmabuf_from_vram(dev_priv, vfbd->buffer);
++}
++
++int vmw_kms_new_framebuffer_dmabuf(struct vmw_private *dev_priv,
++ struct vmw_dma_buffer *dmabuf,
++ struct vmw_framebuffer **out,
++ unsigned width, unsigned height)
++
++{
++ struct drm_device *dev = dev_priv->dev;
++ struct vmw_framebuffer_dmabuf *vfbd;
++ int ret;
++
++ vfbd = kzalloc(sizeof(*vfbd), GFP_KERNEL);
++ if (!vfbd) {
++ ret = -ENOMEM;
++ goto out_err1;
++ }
++
++ ret = drm_framebuffer_init(dev, &vfbd->base.base,
++ &vmw_framebuffer_dmabuf_funcs);
++ if (ret)
++ goto out_err2;
++
++ if (!vmw_dmabuf_reference(dmabuf)) {
++ DRM_ERROR("failed to reference dmabuf %p\n", dmabuf);
++ goto out_err3;
++ }
++
++ /* XXX get the first 3 from the surface info */
++ vfbd->base.base.bits_per_pixel = 32;
++ vfbd->base.base.pitch = width * 32 / 4;
++ vfbd->base.base.depth = 24;
++ vfbd->base.base.width = width;
++ vfbd->base.base.height = height;
++ vfbd->base.pin = vmw_framebuffer_dmabuf_pin;
++ vfbd->base.unpin = vmw_framebuffer_dmabuf_unpin;
++ vfbd->buffer = dmabuf;
++ *out = &vfbd->base;
++
++ return 0;
++
++out_err3:
++ drm_framebuffer_cleanup(&vfbd->base.base);
++out_err2:
++ kfree(vfbd);
++out_err1:
++ return ret;
++}
++
++/*
++ * Generic Kernel modesetting functions
++ */
++
++static struct drm_framebuffer *vmw_kms_fb_create(struct drm_device *dev,
++ struct drm_file *file_priv,
++ struct drm_mode_fb_cmd *mode_cmd)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
++ struct vmw_framebuffer *vfb = NULL;
++ struct vmw_surface *surface = NULL;
++ struct vmw_dma_buffer *bo = NULL;
++ int ret;
++
++ ret = vmw_user_surface_lookup_handle(dev_priv, tfile,
++ mode_cmd->handle, &surface);
++ if (ret)
++ goto try_dmabuf;
++
++ if (!surface->scanout)
++ goto err_not_scanout;
++
++ ret = vmw_kms_new_framebuffer_surface(dev_priv, surface, &vfb,
++ mode_cmd->width, mode_cmd->height);
++
++ /* vmw_user_surface_lookup takes one ref so does new_fb */
++ vmw_surface_unreference(&surface);
++
++ if (ret) {
++ DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret);
++ return NULL;
++ }
++ return &vfb->base;
++
++try_dmabuf:
++ DRM_INFO("%s: trying buffer\n", __func__);
++
++ ret = vmw_user_dmabuf_lookup(tfile, mode_cmd->handle, &bo);
++ if (ret) {
++ DRM_ERROR("failed to find buffer: %i\n", ret);
++ return NULL;
++ }
++
++ ret = vmw_kms_new_framebuffer_dmabuf(dev_priv, bo, &vfb,
++ mode_cmd->width, mode_cmd->height);
++
++ /* vmw_user_dmabuf_lookup takes one ref so does new_fb */
++ vmw_dmabuf_unreference(&bo);
++
++ if (ret) {
++ DRM_ERROR("failed to create vmw_framebuffer: %i\n", ret);
++ return NULL;
++ }
++
++ return &vfb->base;
++
++err_not_scanout:
++ DRM_ERROR("surface not marked as scanout\n");
++ /* vmw_user_surface_lookup takes one ref */
++ vmw_surface_unreference(&surface);
++
++ return NULL;
++}
++
++static int vmw_kms_fb_changed(struct drm_device *dev)
++{
++ return 0;
++}
++
++static struct drm_mode_config_funcs vmw_kms_funcs = {
++ .fb_create = vmw_kms_fb_create,
++ .fb_changed = vmw_kms_fb_changed,
++};
++
++int vmw_kms_init(struct vmw_private *dev_priv)
++{
++ struct drm_device *dev = dev_priv->dev;
++ int ret;
++
++ drm_mode_config_init(dev);
++ dev->mode_config.funcs = &vmw_kms_funcs;
++ dev->mode_config.min_width = 1;
++ dev->mode_config.min_height = 1;
++ dev->mode_config.max_width = dev_priv->fb_max_width;
++ dev->mode_config.max_height = dev_priv->fb_max_height;
++
++ ret = vmw_kms_init_legacy_display_system(dev_priv);
++
++ return 0;
++}
++
++int vmw_kms_close(struct vmw_private *dev_priv)
++{
++ /*
++ * Docs says we should take the lock before calling this function
++ * but since it destroys encoders and our destructor calls
++ * drm_encoder_cleanup which takes the lock we deadlock.
++ */
++ drm_mode_config_cleanup(dev_priv->dev);
++ vmw_kms_close_legacy_display_system(dev_priv);
++ return 0;
++}
++
++int vmw_kms_cursor_bypass_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_vmw_cursor_bypass_arg *arg = data;
++ struct vmw_display_unit *du;
++ struct drm_mode_object *obj;
++ struct drm_crtc *crtc;
++ int ret = 0;
++
++
++ mutex_lock(&dev->mode_config.mutex);
++ if (arg->flags & DRM_VMW_CURSOR_BYPASS_ALL) {
++
++ list_for_each_entry(crtc, &dev->mode_config.crtc_list, head) {
++ du = vmw_crtc_to_du(crtc);
++ du->hotspot_x = arg->xhot;
++ du->hotspot_y = arg->yhot;
++ }
++
++ mutex_unlock(&dev->mode_config.mutex);
++ return 0;
++ }
++
++ obj = drm_mode_object_find(dev, arg->crtc_id, DRM_MODE_OBJECT_CRTC);
++ if (!obj) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ crtc = obj_to_crtc(obj);
++ du = vmw_crtc_to_du(crtc);
++
++ du->hotspot_x = arg->xhot;
++ du->hotspot_y = arg->yhot;
++
++out:
++ mutex_unlock(&dev->mode_config.mutex);
++
++ return ret;
++}
++
++int vmw_kms_save_vga(struct vmw_private *vmw_priv)
++{
++ /*
++ * setup a single multimon monitor with the size
++ * of 0x0, this stops the UI from resizing when we
++ * change the framebuffer size
++ */
++ if (vmw_priv->capabilities & SVGA_CAP_MULTIMON) {
++ vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 1);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, 0);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_IS_PRIMARY, true);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_X, 0);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_POSITION_Y, 0);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_WIDTH, 0);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_HEIGHT, 0);
++ vmw_write(vmw_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
++ }
++
++ vmw_priv->vga_width = vmw_read(vmw_priv, SVGA_REG_WIDTH);
++ vmw_priv->vga_height = vmw_read(vmw_priv, SVGA_REG_HEIGHT);
++ vmw_priv->vga_bpp = vmw_read(vmw_priv, SVGA_REG_BITS_PER_PIXEL);
++ vmw_priv->vga_depth = vmw_read(vmw_priv, SVGA_REG_DEPTH);
++ vmw_priv->vga_pseudo = vmw_read(vmw_priv, SVGA_REG_PSEUDOCOLOR);
++ vmw_priv->vga_red_mask = vmw_read(vmw_priv, SVGA_REG_RED_MASK);
++ vmw_priv->vga_green_mask = vmw_read(vmw_priv, SVGA_REG_GREEN_MASK);
++ vmw_priv->vga_blue_mask = vmw_read(vmw_priv, SVGA_REG_BLUE_MASK);
++
++ return 0;
++}
++
++int vmw_kms_restore_vga(struct vmw_private *vmw_priv)
++{
++ vmw_write(vmw_priv, SVGA_REG_WIDTH, vmw_priv->vga_width);
++ vmw_write(vmw_priv, SVGA_REG_HEIGHT, vmw_priv->vga_height);
++ vmw_write(vmw_priv, SVGA_REG_BITS_PER_PIXEL, vmw_priv->vga_bpp);
++ vmw_write(vmw_priv, SVGA_REG_DEPTH, vmw_priv->vga_depth);
++ vmw_write(vmw_priv, SVGA_REG_PSEUDOCOLOR, vmw_priv->vga_pseudo);
++ vmw_write(vmw_priv, SVGA_REG_RED_MASK, vmw_priv->vga_red_mask);
++ vmw_write(vmw_priv, SVGA_REG_GREEN_MASK, vmw_priv->vga_green_mask);
++ vmw_write(vmw_priv, SVGA_REG_BLUE_MASK, vmw_priv->vga_blue_mask);
++
++ /* TODO check for multimon */
++ vmw_write(vmw_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 0);
++
++ return 0;
++}
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
+new file mode 100644
+index 0000000..8b95249
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_kms.h
+@@ -0,0 +1,102 @@
++/**************************************************************************
++ *
++ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++#ifndef VMWGFX_KMS_H_
++#define VMWGFX_KMS_H_
++
++#include "drmP.h"
++#include "vmwgfx_drv.h"
++
++
++#define vmw_framebuffer_to_vfb(x) \
++ container_of(x, struct vmw_framebuffer, base)
++
++/**
++ * Base class for framebuffers
++ *
++ * @pin is called the when ever a crtc uses this framebuffer
++ * @unpin is called
++ */
++struct vmw_framebuffer {
++ struct drm_framebuffer base;
++ int (*pin)(struct vmw_framebuffer *fb);
++ int (*unpin)(struct vmw_framebuffer *fb);
++};
++
++
++#define vmw_crtc_to_du(x) \
++ container_of(x, struct vmw_display_unit, crtc)
++
++/*
++ * Basic cursor manipulation
++ */
++int vmw_cursor_update_image(struct vmw_private *dev_priv,
++ u32 *image, u32 width, u32 height,
++ u32 hotspotX, u32 hotspotY);
++void vmw_cursor_update_position(struct vmw_private *dev_priv,
++ bool show, int x, int y);
++
++/**
++ * Base class display unit.
++ *
++ * Since the SVGA hw doesn't have a concept of a crtc, encoder or connector
++ * so the display unit is all of them at the same time. This is true for both
++ * legacy multimon and screen objects.
++ */
++struct vmw_display_unit {
++ struct drm_crtc crtc;
++ struct drm_encoder encoder;
++ struct drm_connector connector;
++
++ struct vmw_surface *cursor_surface;
++ struct vmw_dma_buffer *cursor_dmabuf;
++ size_t cursor_age;
++
++ int cursor_x;
++ int cursor_y;
++
++ int hotspot_x;
++ int hotspot_y;
++
++ unsigned unit;
++};
++
++/*
++ * Shared display unit functions - vmwgfx_kms.c
++ */
++void vmw_display_unit_cleanup(struct vmw_display_unit *du);
++int vmw_du_crtc_cursor_set(struct drm_crtc *crtc, struct drm_file *file_priv,
++ uint32_t handle, uint32_t width, uint32_t height);
++int vmw_du_crtc_cursor_move(struct drm_crtc *crtc, int x, int y);
++
++/*
++ * Legacy display unit functions - vmwgfx_ldu.h
++ */
++int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv);
++int vmw_kms_close_legacy_display_system(struct vmw_private *dev_priv);
++
++#endif
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+new file mode 100644
+index 0000000..9089159
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ldu.c
+@@ -0,0 +1,516 @@
++/**************************************************************************
++ *
++ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++#include "vmwgfx_kms.h"
++
++#define vmw_crtc_to_ldu(x) \
++ container_of(x, struct vmw_legacy_display_unit, base.crtc)
++#define vmw_encoder_to_ldu(x) \
++ container_of(x, struct vmw_legacy_display_unit, base.encoder)
++#define vmw_connector_to_ldu(x) \
++ container_of(x, struct vmw_legacy_display_unit, base.connector)
++
++struct vmw_legacy_display {
++ struct list_head active;
++
++ unsigned num_active;
++
++ struct vmw_framebuffer *fb;
++};
++
++/**
++ * Display unit using the legacy register interface.
++ */
++struct vmw_legacy_display_unit {
++ struct vmw_display_unit base;
++
++ struct list_head active;
++
++ unsigned unit;
++};
++
++static void vmw_ldu_destroy(struct vmw_legacy_display_unit *ldu)
++{
++ list_del_init(&ldu->active);
++ vmw_display_unit_cleanup(&ldu->base);
++ kfree(ldu);
++}
++
++
++/*
++ * Legacy Display Unit CRTC functions
++ */
++
++static void vmw_ldu_crtc_save(struct drm_crtc *crtc)
++{
++}
++
++static void vmw_ldu_crtc_restore(struct drm_crtc *crtc)
++{
++}
++
++static void vmw_ldu_crtc_gamma_set(struct drm_crtc *crtc,
++ u16 *r, u16 *g, u16 *b,
++ uint32_t size)
++{
++}
++
++static void vmw_ldu_crtc_destroy(struct drm_crtc *crtc)
++{
++ vmw_ldu_destroy(vmw_crtc_to_ldu(crtc));
++}
++
++static int vmw_ldu_commit_list(struct vmw_private *dev_priv)
++{
++ struct vmw_legacy_display *lds = dev_priv->ldu_priv;
++ struct vmw_legacy_display_unit *entry;
++ struct drm_crtc *crtc;
++ int i = 0;
++
++ /* to stop the screen from changing size on resize */
++ vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, 0);
++ for (i = 0; i < lds->num_active; i++) {
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, i);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_IS_PRIMARY, !i);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_X, 0);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_Y, 0);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_WIDTH, 0);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_HEIGHT, 0);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
++ }
++
++ /* Now set the mode */
++ vmw_write(dev_priv, SVGA_REG_NUM_GUEST_DISPLAYS, lds->num_active);
++ i = 0;
++ list_for_each_entry(entry, &lds->active, active) {
++ crtc = &entry->base.crtc;
++
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, i);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_IS_PRIMARY, !i);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_X, crtc->x);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_POSITION_Y, crtc->y);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_WIDTH, crtc->mode.hdisplay);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_HEIGHT, crtc->mode.vdisplay);
++ vmw_write(dev_priv, SVGA_REG_DISPLAY_ID, SVGA_ID_INVALID);
++
++ i++;
++ }
++
++ return 0;
++}
++
++static int vmw_ldu_del_active(struct vmw_private *vmw_priv,
++ struct vmw_legacy_display_unit *ldu)
++{
++ struct vmw_legacy_display *ld = vmw_priv->ldu_priv;
++ if (list_empty(&ldu->active))
++ return 0;
++
++ list_del_init(&ldu->active);
++ if (--(ld->num_active) == 0) {
++ BUG_ON(!ld->fb);
++ if (ld->fb->unpin)
++ ld->fb->unpin(ld->fb);
++ ld->fb = NULL;
++ }
++
++ return 0;
++}
++
++static int vmw_ldu_add_active(struct vmw_private *vmw_priv,
++ struct vmw_legacy_display_unit *ldu,
++ struct vmw_framebuffer *vfb)
++{
++ struct vmw_legacy_display *ld = vmw_priv->ldu_priv;
++ struct vmw_legacy_display_unit *entry;
++ struct list_head *at;
++
++ if (!list_empty(&ldu->active))
++ return 0;
++
++ at = &ld->active;
++ list_for_each_entry(entry, &ld->active, active) {
++ if (entry->unit > ldu->unit)
++ break;
++
++ at = &entry->active;
++ }
++
++ list_add(&ldu->active, at);
++ if (ld->num_active++ == 0) {
++ BUG_ON(ld->fb);
++ if (vfb->pin)
++ vfb->pin(vfb);
++ ld->fb = vfb;
++ }
++
++ return 0;
++}
++
++static int vmw_ldu_crtc_set_config(struct drm_mode_set *set)
++{
++ struct vmw_private *dev_priv;
++ struct vmw_legacy_display_unit *ldu;
++ struct drm_connector *connector;
++ struct drm_display_mode *mode;
++ struct drm_encoder *encoder;
++ struct vmw_framebuffer *vfb;
++ struct drm_framebuffer *fb;
++ struct drm_crtc *crtc;
++
++ if (!set)
++ return -EINVAL;
++
++ if (!set->crtc)
++ return -EINVAL;
++
++ /* get the ldu */
++ crtc = set->crtc;
++ ldu = vmw_crtc_to_ldu(crtc);
++ vfb = set->fb ? vmw_framebuffer_to_vfb(set->fb) : NULL;
++ dev_priv = vmw_priv(crtc->dev);
++
++ if (set->num_connectors > 1) {
++ DRM_ERROR("to many connectors\n");
++ return -EINVAL;
++ }
++
++ if (set->num_connectors == 1 &&
++ set->connectors[0] != &ldu->base.connector) {
++ DRM_ERROR("connector doesn't match %p %p\n",
++ set->connectors[0], &ldu->base.connector);
++ return -EINVAL;
++ }
++
++ /* ldu only supports one fb active at the time */
++ if (dev_priv->ldu_priv->fb && vfb &&
++ dev_priv->ldu_priv->fb != vfb) {
++ DRM_ERROR("Multiple framebuffers not supported\n");
++ return -EINVAL;
++ }
++
++ /* since they always map one to one these are safe */
++ connector = &ldu->base.connector;
++ encoder = &ldu->base.encoder;
++
++ /* should we turn the crtc off? */
++ if (set->num_connectors == 0 || !set->mode || !set->fb) {
++
++ connector->encoder = NULL;
++ encoder->crtc = NULL;
++ crtc->fb = NULL;
++
++ vmw_ldu_del_active(dev_priv, ldu);
++
++ vmw_ldu_commit_list(dev_priv);
++
++ return 0;
++ }
++
++
++ /* we now know we want to set a mode */
++ mode = set->mode;
++ fb = set->fb;
++
++ if (set->x + mode->hdisplay > fb->width ||
++ set->y + mode->vdisplay > fb->height) {
++ DRM_ERROR("set outside of framebuffer\n");
++ return -EINVAL;
++ }
++
++ vmw_fb_off(dev_priv);
++
++ crtc->fb = fb;
++ encoder->crtc = crtc;
++ connector->encoder = encoder;
++ crtc->x = set->x;
++ crtc->y = set->y;
++ crtc->mode = *mode;
++
++ vmw_ldu_add_active(dev_priv, ldu, vfb);
++
++ vmw_ldu_commit_list(dev_priv);
++
++ return 0;
++}
++
++static struct drm_crtc_funcs vmw_legacy_crtc_funcs = {
++ .save = vmw_ldu_crtc_save,
++ .restore = vmw_ldu_crtc_restore,
++ .cursor_set = vmw_du_crtc_cursor_set,
++ .cursor_move = vmw_du_crtc_cursor_move,
++ .gamma_set = vmw_ldu_crtc_gamma_set,
++ .destroy = vmw_ldu_crtc_destroy,
++ .set_config = vmw_ldu_crtc_set_config,
++};
++
++/*
++ * Legacy Display Unit encoder functions
++ */
++
++static void vmw_ldu_encoder_destroy(struct drm_encoder *encoder)
++{
++ vmw_ldu_destroy(vmw_encoder_to_ldu(encoder));
++}
++
++static struct drm_encoder_funcs vmw_legacy_encoder_funcs = {
++ .destroy = vmw_ldu_encoder_destroy,
++};
++
++/*
++ * Legacy Display Unit connector functions
++ */
++
++static void vmw_ldu_connector_dpms(struct drm_connector *connector, int mode)
++{
++}
++
++static void vmw_ldu_connector_save(struct drm_connector *connector)
++{
++}
++
++static void vmw_ldu_connector_restore(struct drm_connector *connector)
++{
++}
++
++static enum drm_connector_status
++ vmw_ldu_connector_detect(struct drm_connector *connector)
++{
++ /* XXX vmwctrl should control connection status */
++ if (vmw_connector_to_ldu(connector)->base.unit == 0)
++ return connector_status_connected;
++ return connector_status_disconnected;
++}
++
++static struct drm_display_mode vmw_ldu_connector_builtin[] = {
++ /* 640x480@60Hz */
++ { DRM_MODE("640x480", DRM_MODE_TYPE_DRIVER, 25175, 640, 656,
++ 752, 800, 0, 480, 489, 492, 525, 0,
++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
++ /* 800x600@60Hz */
++ { DRM_MODE("800x600",
++ DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED,
++ 40000, 800, 840, 968, 1056, 0, 600, 601, 605, 628,
++ 0, DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ /* 1024x768@60Hz */
++ { DRM_MODE("1024x768", DRM_MODE_TYPE_DRIVER, 65000, 1024, 1048,
++ 1184, 1344, 0, 768, 771, 777, 806, 0,
++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_NVSYNC) },
++ /* 1152x864@75Hz */
++ { DRM_MODE("1152x864", DRM_MODE_TYPE_DRIVER, 108000, 1152, 1216,
++ 1344, 1600, 0, 864, 865, 868, 900, 0,
++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ /* 1280x768@60Hz */
++ { DRM_MODE("1280x768", DRM_MODE_TYPE_DRIVER, 79500, 1280, 1344,
++ 1472, 1664, 0, 768, 771, 778, 798, 0,
++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ /* 1280x800@60Hz */
++ { DRM_MODE("1280x800", DRM_MODE_TYPE_DRIVER, 83500, 1280, 1352,
++ 1480, 1680, 0, 800, 803, 809, 831, 0,
++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_NVSYNC) },
++ /* 1280x960@60Hz */
++ { DRM_MODE("1280x960", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1376,
++ 1488, 1800, 0, 960, 961, 964, 1000, 0,
++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ /* 1280x1024@60Hz */
++ { DRM_MODE("1280x1024", DRM_MODE_TYPE_DRIVER, 108000, 1280, 1328,
++ 1440, 1688, 0, 1024, 1025, 1028, 1066, 0,
++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ /* 1360x768@60Hz */
++ { DRM_MODE("1360x768", DRM_MODE_TYPE_DRIVER, 85500, 1360, 1424,
++ 1536, 1792, 0, 768, 771, 777, 795, 0,
++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ /* 1440x1050@60Hz */
++ { DRM_MODE("1400x1050", DRM_MODE_TYPE_DRIVER, 121750, 1400, 1488,
++ 1632, 1864, 0, 1050, 1053, 1057, 1089, 0,
++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ /* 1440x900@60Hz */
++ { DRM_MODE("1440x900", DRM_MODE_TYPE_DRIVER, 106500, 1440, 1520,
++ 1672, 1904, 0, 900, 903, 909, 934, 0,
++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ /* 1600x1200@60Hz */
++ { DRM_MODE("1600x1200", DRM_MODE_TYPE_DRIVER, 162000, 1600, 1664,
++ 1856, 2160, 0, 1200, 1201, 1204, 1250, 0,
++ DRM_MODE_FLAG_PHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ /* 1680x1050@60Hz */
++ { DRM_MODE("1680x1050", DRM_MODE_TYPE_DRIVER, 146250, 1680, 1784,
++ 1960, 2240, 0, 1050, 1053, 1059, 1089, 0,
++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ /* 1792x1344@60Hz */
++ { DRM_MODE("1792x1344", DRM_MODE_TYPE_DRIVER, 204750, 1792, 1920,
++ 2120, 2448, 0, 1344, 1345, 1348, 1394, 0,
++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ /* 1853x1392@60Hz */
++ { DRM_MODE("1856x1392", DRM_MODE_TYPE_DRIVER, 218250, 1856, 1952,
++ 2176, 2528, 0, 1392, 1393, 1396, 1439, 0,
++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ /* 1920x1200@60Hz */
++ { DRM_MODE("1920x1200", DRM_MODE_TYPE_DRIVER, 193250, 1920, 2056,
++ 2256, 2592, 0, 1200, 1203, 1209, 1245, 0,
++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ /* 1920x1440@60Hz */
++ { DRM_MODE("1920x1440", DRM_MODE_TYPE_DRIVER, 234000, 1920, 2048,
++ 2256, 2600, 0, 1440, 1441, 1444, 1500, 0,
++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ /* 2560x1600@60Hz */
++ { DRM_MODE("2560x1600", DRM_MODE_TYPE_DRIVER, 348500, 2560, 2752,
++ 3032, 3504, 0, 1600, 1603, 1609, 1658, 0,
++ DRM_MODE_FLAG_NHSYNC | DRM_MODE_FLAG_PVSYNC) },
++ /* Terminate */
++ { DRM_MODE("", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) },
++};
++
++static int vmw_ldu_connector_fill_modes(struct drm_connector *connector,
++ uint32_t max_width, uint32_t max_height)
++{
++ struct drm_device *dev = connector->dev;
++ struct drm_display_mode *mode = NULL;
++ int i;
++
++ for (i = 0; vmw_ldu_connector_builtin[i].type != 0; i++) {
++ if (vmw_ldu_connector_builtin[i].hdisplay > max_width ||
++ vmw_ldu_connector_builtin[i].vdisplay > max_height)
++ continue;
++
++ mode = drm_mode_duplicate(dev, &vmw_ldu_connector_builtin[i]);
++ if (!mode)
++ return 0;
++ mode->vrefresh = drm_mode_vrefresh(mode);
++
++ drm_mode_probed_add(connector, mode);
++ }
++
++ drm_mode_connector_list_update(connector);
++
++ return 1;
++}
++
++static int vmw_ldu_connector_set_property(struct drm_connector *connector,
++ struct drm_property *property,
++ uint64_t val)
++{
++ return 0;
++}
++
++static void vmw_ldu_connector_destroy(struct drm_connector *connector)
++{
++ vmw_ldu_destroy(vmw_connector_to_ldu(connector));
++}
++
++static struct drm_connector_funcs vmw_legacy_connector_funcs = {
++ .dpms = vmw_ldu_connector_dpms,
++ .save = vmw_ldu_connector_save,
++ .restore = vmw_ldu_connector_restore,
++ .detect = vmw_ldu_connector_detect,
++ .fill_modes = vmw_ldu_connector_fill_modes,
++ .set_property = vmw_ldu_connector_set_property,
++ .destroy = vmw_ldu_connector_destroy,
++};
++
++static int vmw_ldu_init(struct vmw_private *dev_priv, unsigned unit)
++{
++ struct vmw_legacy_display_unit *ldu;
++ struct drm_device *dev = dev_priv->dev;
++ struct drm_connector *connector;
++ struct drm_encoder *encoder;
++ struct drm_crtc *crtc;
++
++ ldu = kzalloc(sizeof(*ldu), GFP_KERNEL);
++ if (!ldu)
++ return -ENOMEM;
++
++ ldu->unit = unit;
++ crtc = &ldu->base.crtc;
++ encoder = &ldu->base.encoder;
++ connector = &ldu->base.connector;
++
++ drm_connector_init(dev, connector, &vmw_legacy_connector_funcs,
++ DRM_MODE_CONNECTOR_LVDS);
++ /* Initial status */
++ if (unit == 0)
++ connector->status = connector_status_connected;
++ else
++ connector->status = connector_status_disconnected;
++
++ drm_encoder_init(dev, encoder, &vmw_legacy_encoder_funcs,
++ DRM_MODE_ENCODER_LVDS);
++ drm_mode_connector_attach_encoder(connector, encoder);
++ encoder->possible_crtcs = (1 << unit);
++ encoder->possible_clones = 0;
++
++ INIT_LIST_HEAD(&ldu->active);
++
++ drm_crtc_init(dev, crtc, &vmw_legacy_crtc_funcs);
++
++ drm_connector_attach_property(connector,
++ dev->mode_config.dirty_info_property,
++ 1);
++
++ return 0;
++}
++
++int vmw_kms_init_legacy_display_system(struct vmw_private *dev_priv)
++{
++ if (dev_priv->ldu_priv) {
++ DRM_INFO("ldu system already on\n");
++ return -EINVAL;
++ }
++
++ dev_priv->ldu_priv = kmalloc(GFP_KERNEL, sizeof(*dev_priv->ldu_priv));
++
++ if (!dev_priv->ldu_priv)
++ return -ENOMEM;
++
++ INIT_LIST_HEAD(&dev_priv->ldu_priv->active);
++ dev_priv->ldu_priv->num_active = 0;
++ dev_priv->ldu_priv->fb = NULL;
++
++ drm_mode_create_dirty_info_property(dev_priv->dev);
++
++ vmw_ldu_init(dev_priv, 0);
++ vmw_ldu_init(dev_priv, 1);
++ vmw_ldu_init(dev_priv, 2);
++ vmw_ldu_init(dev_priv, 3);
++ vmw_ldu_init(dev_priv, 4);
++ vmw_ldu_init(dev_priv, 5);
++ vmw_ldu_init(dev_priv, 6);
++ vmw_ldu_init(dev_priv, 7);
++
++ return 0;
++}
++
++int vmw_kms_close_legacy_display_system(struct vmw_private *dev_priv)
++{
++ if (!dev_priv->ldu_priv)
++ return -ENOSYS;
++
++ BUG_ON(!list_empty(&dev_priv->ldu_priv->active));
++
++ kfree(dev_priv->ldu_priv);
++
++ return 0;
++}
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c
+new file mode 100644
+index 0000000..5b6eabe
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_overlay.c
+@@ -0,0 +1,625 @@
++/**************************************************************************
++ *
++ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++
++#include "drmP.h"
++#include "vmwgfx_drv.h"
++
++#include "ttm/ttm_placement.h"
++
++#include "svga_overlay.h"
++#include "svga_escape.h"
++
++#define VMW_MAX_NUM_STREAMS 1
++
++struct vmw_stream {
++ struct vmw_dma_buffer *buf;
++ bool claimed;
++ bool paused;
++ struct drm_vmw_control_stream_arg saved;
++};
++
++/**
++ * Overlay control
++ */
++struct vmw_overlay {
++ /*
++ * Each stream is a single overlay. In Xv these are called ports.
++ */
++ struct mutex mutex;
++ struct vmw_stream stream[VMW_MAX_NUM_STREAMS];
++};
++
++static inline struct vmw_overlay *vmw_overlay(struct drm_device *dev)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ return dev_priv ? dev_priv->overlay_priv : NULL;
++}
++
++struct vmw_escape_header {
++ uint32_t cmd;
++ SVGAFifoCmdEscape body;
++};
++
++struct vmw_escape_video_flush {
++ struct vmw_escape_header escape;
++ SVGAEscapeVideoFlush flush;
++};
++
++static inline void fill_escape(struct vmw_escape_header *header,
++ uint32_t size)
++{
++ header->cmd = SVGA_CMD_ESCAPE;
++ header->body.nsid = SVGA_ESCAPE_NSID_VMWARE;
++ header->body.size = size;
++}
++
++static inline void fill_flush(struct vmw_escape_video_flush *cmd,
++ uint32_t stream_id)
++{
++ fill_escape(&cmd->escape, sizeof(cmd->flush));
++ cmd->flush.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_FLUSH;
++ cmd->flush.streamId = stream_id;
++}
++
++/**
++ * Pin or unpin a buffer in vram.
++ *
++ * @dev_priv: Driver private.
++ * @buf: DMA buffer to pin or unpin.
++ * @pin: Pin buffer in vram if true.
++ * @interruptible: Use interruptible wait.
++ *
++ * Takes the current masters ttm lock in read.
++ *
++ * Returns
++ * -ERESTARTSYS if interrupted by a signal.
++ */
++static int vmw_dmabuf_pin_in_vram(struct vmw_private *dev_priv,
++ struct vmw_dma_buffer *buf,
++ bool pin, bool interruptible)
++{
++ struct ttm_buffer_object *bo = &buf->base;
++ struct ttm_placement *overlay_placement = &vmw_vram_placement;
++ int ret;
++
++ ret = ttm_read_lock(&dev_priv->active_master->lock, interruptible);
++ if (unlikely(ret != 0))
++ return ret;
++
++ ret = ttm_bo_reserve(bo, interruptible, false, false, 0);
++ if (unlikely(ret != 0))
++ goto err;
++
++ if (pin)
++ overlay_placement = &vmw_vram_ne_placement;
++
++ ret = ttm_bo_validate(bo, overlay_placement, interruptible, false);
++
++ ttm_bo_unreserve(bo);
++
++err:
++ ttm_read_unlock(&dev_priv->active_master->lock);
++
++ return ret;
++}
++
++/**
++ * Send put command to hw.
++ *
++ * Returns
++ * -ERESTARTSYS if interrupted by a signal.
++ */
++static int vmw_overlay_send_put(struct vmw_private *dev_priv,
++ struct vmw_dma_buffer *buf,
++ struct drm_vmw_control_stream_arg *arg,
++ bool interruptible)
++{
++ struct {
++ struct vmw_escape_header escape;
++ struct {
++ struct {
++ uint32_t cmdType;
++ uint32_t streamId;
++ } header;
++ struct {
++ uint32_t registerId;
++ uint32_t value;
++ } items[SVGA_VIDEO_PITCH_3 + 1];
++ } body;
++ struct vmw_escape_video_flush flush;
++ } *cmds;
++ uint32_t offset;
++ int i, ret;
++
++ for (;;) {
++ cmds = vmw_fifo_reserve(dev_priv, sizeof(*cmds));
++ if (cmds)
++ break;
++
++ ret = vmw_fallback_wait(dev_priv, false, true, 0,
++ interruptible, 3*HZ);
++ if (interruptible && ret == -ERESTARTSYS)
++ return ret;
++ else
++ BUG_ON(ret != 0);
++ }
++
++ fill_escape(&cmds->escape, sizeof(cmds->body));
++ cmds->body.header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS;
++ cmds->body.header.streamId = arg->stream_id;
++
++ for (i = 0; i <= SVGA_VIDEO_PITCH_3; i++)
++ cmds->body.items[i].registerId = i;
++
++ offset = buf->base.offset + arg->offset;
++
++ cmds->body.items[SVGA_VIDEO_ENABLED].value = true;
++ cmds->body.items[SVGA_VIDEO_FLAGS].value = arg->flags;
++ cmds->body.items[SVGA_VIDEO_DATA_OFFSET].value = offset;
++ cmds->body.items[SVGA_VIDEO_FORMAT].value = arg->format;
++ cmds->body.items[SVGA_VIDEO_COLORKEY].value = arg->color_key;
++ cmds->body.items[SVGA_VIDEO_SIZE].value = arg->size;
++ cmds->body.items[SVGA_VIDEO_WIDTH].value = arg->width;
++ cmds->body.items[SVGA_VIDEO_HEIGHT].value = arg->height;
++ cmds->body.items[SVGA_VIDEO_SRC_X].value = arg->src.x;
++ cmds->body.items[SVGA_VIDEO_SRC_Y].value = arg->src.y;
++ cmds->body.items[SVGA_VIDEO_SRC_WIDTH].value = arg->src.w;
++ cmds->body.items[SVGA_VIDEO_SRC_HEIGHT].value = arg->src.h;
++ cmds->body.items[SVGA_VIDEO_DST_X].value = arg->dst.x;
++ cmds->body.items[SVGA_VIDEO_DST_Y].value = arg->dst.y;
++ cmds->body.items[SVGA_VIDEO_DST_WIDTH].value = arg->dst.w;
++ cmds->body.items[SVGA_VIDEO_DST_HEIGHT].value = arg->dst.h;
++ cmds->body.items[SVGA_VIDEO_PITCH_1].value = arg->pitch[0];
++ cmds->body.items[SVGA_VIDEO_PITCH_2].value = arg->pitch[1];
++ cmds->body.items[SVGA_VIDEO_PITCH_3].value = arg->pitch[2];
++
++ fill_flush(&cmds->flush, arg->stream_id);
++
++ vmw_fifo_commit(dev_priv, sizeof(*cmds));
++
++ return 0;
++}
++
++/**
++ * Send stop command to hw.
++ *
++ * Returns
++ * -ERESTARTSYS if interrupted by a signal.
++ */
++static int vmw_overlay_send_stop(struct vmw_private *dev_priv,
++ uint32_t stream_id,
++ bool interruptible)
++{
++ struct {
++ struct vmw_escape_header escape;
++ SVGAEscapeVideoSetRegs body;
++ struct vmw_escape_video_flush flush;
++ } *cmds;
++ int ret;
++
++ for (;;) {
++ cmds = vmw_fifo_reserve(dev_priv, sizeof(*cmds));
++ if (cmds)
++ break;
++
++ ret = vmw_fallback_wait(dev_priv, false, true, 0,
++ interruptible, 3*HZ);
++ if (interruptible && ret == -ERESTARTSYS)
++ return ret;
++ else
++ BUG_ON(ret != 0);
++ }
++
++ fill_escape(&cmds->escape, sizeof(cmds->body));
++ cmds->body.header.cmdType = SVGA_ESCAPE_VMWARE_VIDEO_SET_REGS;
++ cmds->body.header.streamId = stream_id;
++ cmds->body.items[0].registerId = SVGA_VIDEO_ENABLED;
++ cmds->body.items[0].value = false;
++ fill_flush(&cmds->flush, stream_id);
++
++ vmw_fifo_commit(dev_priv, sizeof(*cmds));
++
++ return 0;
++}
++
++/**
++ * Stop or pause a stream.
++ *
++ * If the stream is paused the no evict flag is removed from the buffer
++ * but left in vram. This allows for instance mode_set to evict it
++ * should it need to.
++ *
++ * The caller must hold the overlay lock.
++ *
++ * @stream_id which stream to stop/pause.
++ * @pause true to pause, false to stop completely.
++ */
++static int vmw_overlay_stop(struct vmw_private *dev_priv,
++ uint32_t stream_id, bool pause,
++ bool interruptible)
++{
++ struct vmw_overlay *overlay = dev_priv->overlay_priv;
++ struct vmw_stream *stream = &overlay->stream[stream_id];
++ int ret;
++
++ /* no buffer attached the stream is completely stopped */
++ if (!stream->buf)
++ return 0;
++
++ /* If the stream is paused this is already done */
++ if (!stream->paused) {
++ ret = vmw_overlay_send_stop(dev_priv, stream_id,
++ interruptible);
++ if (ret)
++ return ret;
++
++ /* We just remove the NO_EVICT flag so no -ENOMEM */
++ ret = vmw_dmabuf_pin_in_vram(dev_priv, stream->buf, false,
++ interruptible);
++ if (interruptible && ret == -ERESTARTSYS)
++ return ret;
++ else
++ BUG_ON(ret != 0);
++ }
++
++ if (!pause) {
++ vmw_dmabuf_unreference(&stream->buf);
++ stream->paused = false;
++ } else {
++ stream->paused = true;
++ }
++
++ return 0;
++}
++
++/**
++ * Update a stream and send any put or stop fifo commands needed.
++ *
++ * The caller must hold the overlay lock.
++ *
++ * Returns
++ * -ENOMEM if buffer doesn't fit in vram.
++ * -ERESTARTSYS if interrupted.
++ */
++static int vmw_overlay_update_stream(struct vmw_private *dev_priv,
++ struct vmw_dma_buffer *buf,
++ struct drm_vmw_control_stream_arg *arg,
++ bool interruptible)
++{
++ struct vmw_overlay *overlay = dev_priv->overlay_priv;
++ struct vmw_stream *stream = &overlay->stream[arg->stream_id];
++ int ret = 0;
++
++ if (!buf)
++ return -EINVAL;
++
++ DRM_DEBUG(" %s: old %p, new %p, %spaused\n", __func__,
++ stream->buf, buf, stream->paused ? "" : "not ");
++
++ if (stream->buf != buf) {
++ ret = vmw_overlay_stop(dev_priv, arg->stream_id,
++ false, interruptible);
++ if (ret)
++ return ret;
++ } else if (!stream->paused) {
++ /* If the buffers match and not paused then just send
++ * the put command, no need to do anything else.
++ */
++ ret = vmw_overlay_send_put(dev_priv, buf, arg, interruptible);
++ if (ret == 0)
++ stream->saved = *arg;
++ else
++ BUG_ON(!interruptible);
++
++ return ret;
++ }
++
++ /* We don't start the old stream if we are interrupted.
++ * Might return -ENOMEM if it can't fit the buffer in vram.
++ */
++ ret = vmw_dmabuf_pin_in_vram(dev_priv, buf, true, interruptible);
++ if (ret)
++ return ret;
++
++ ret = vmw_overlay_send_put(dev_priv, buf, arg, interruptible);
++ if (ret) {
++ /* This one needs to happen no matter what. We only remove
++ * the NO_EVICT flag so this is safe from -ENOMEM.
++ */
++ BUG_ON(vmw_dmabuf_pin_in_vram(dev_priv, buf, false, false) != 0);
++ return ret;
++ }
++
++ if (stream->buf != buf)
++ stream->buf = vmw_dmabuf_reference(buf);
++ stream->saved = *arg;
++
++ return 0;
++}
++
++/**
++ * Stop all streams.
++ *
++ * Used by the fb code when starting.
++ *
++ * Takes the overlay lock.
++ */
++int vmw_overlay_stop_all(struct vmw_private *dev_priv)
++{
++ struct vmw_overlay *overlay = dev_priv->overlay_priv;
++ int i, ret;
++
++ if (!overlay)
++ return 0;
++
++ mutex_lock(&overlay->mutex);
++
++ for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
++ struct vmw_stream *stream = &overlay->stream[i];
++ if (!stream->buf)
++ continue;
++
++ ret = vmw_overlay_stop(dev_priv, i, false, false);
++ WARN_ON(ret != 0);
++ }
++
++ mutex_unlock(&overlay->mutex);
++
++ return 0;
++}
++
++/**
++ * Try to resume all paused streams.
++ *
++ * Used by the kms code after moving a new scanout buffer to vram.
++ *
++ * Takes the overlay lock.
++ */
++int vmw_overlay_resume_all(struct vmw_private *dev_priv)
++{
++ struct vmw_overlay *overlay = dev_priv->overlay_priv;
++ int i, ret;
++
++ if (!overlay)
++ return 0;
++
++ mutex_lock(&overlay->mutex);
++
++ for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
++ struct vmw_stream *stream = &overlay->stream[i];
++ if (!stream->paused)
++ continue;
++
++ ret = vmw_overlay_update_stream(dev_priv, stream->buf,
++ &stream->saved, false);
++ if (ret != 0)
++ DRM_INFO("%s: *warning* failed to resume stream %i\n",
++ __func__, i);
++ }
++
++ mutex_unlock(&overlay->mutex);
++
++ return 0;
++}
++
++/**
++ * Pauses all active streams.
++ *
++ * Used by the kms code when moving a new scanout buffer to vram.
++ *
++ * Takes the overlay lock.
++ */
++int vmw_overlay_pause_all(struct vmw_private *dev_priv)
++{
++ struct vmw_overlay *overlay = dev_priv->overlay_priv;
++ int i, ret;
++
++ if (!overlay)
++ return 0;
++
++ mutex_lock(&overlay->mutex);
++
++ for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
++ if (overlay->stream[i].paused)
++ DRM_INFO("%s: *warning* stream %i already paused\n",
++ __func__, i);
++ ret = vmw_overlay_stop(dev_priv, i, true, false);
++ WARN_ON(ret != 0);
++ }
++
++ mutex_unlock(&overlay->mutex);
++
++ return 0;
++}
++
++int vmw_overlay_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ struct vmw_overlay *overlay = dev_priv->overlay_priv;
++ struct drm_vmw_control_stream_arg *arg =
++ (struct drm_vmw_control_stream_arg *)data;
++ struct vmw_dma_buffer *buf;
++ struct vmw_resource *res;
++ int ret;
++
++ if (!overlay)
++ return -ENOSYS;
++
++ ret = vmw_user_stream_lookup(dev_priv, tfile, &arg->stream_id, &res);
++ if (ret)
++ return ret;
++
++ mutex_lock(&overlay->mutex);
++
++ if (!arg->enabled) {
++ ret = vmw_overlay_stop(dev_priv, arg->stream_id, false, true);
++ goto out_unlock;
++ }
++
++ ret = vmw_user_dmabuf_lookup(tfile, arg->handle, &buf);
++ if (ret)
++ goto out_unlock;
++
++ ret = vmw_overlay_update_stream(dev_priv, buf, arg, true);
++
++ vmw_dmabuf_unreference(&buf);
++
++out_unlock:
++ mutex_unlock(&overlay->mutex);
++ vmw_resource_unreference(&res);
++
++ return ret;
++}
++
++int vmw_overlay_num_overlays(struct vmw_private *dev_priv)
++{
++ if (!dev_priv->overlay_priv)
++ return 0;
++
++ return VMW_MAX_NUM_STREAMS;
++}
++
++int vmw_overlay_num_free_overlays(struct vmw_private *dev_priv)
++{
++ struct vmw_overlay *overlay = dev_priv->overlay_priv;
++ int i, k;
++
++ if (!overlay)
++ return 0;
++
++ mutex_lock(&overlay->mutex);
++
++ for (i = 0, k = 0; i < VMW_MAX_NUM_STREAMS; i++)
++ if (!overlay->stream[i].claimed)
++ k++;
++
++ mutex_unlock(&overlay->mutex);
++
++ return k;
++}
++
++int vmw_overlay_claim(struct vmw_private *dev_priv, uint32_t *out)
++{
++ struct vmw_overlay *overlay = dev_priv->overlay_priv;
++ int i;
++
++ if (!overlay)
++ return -ENOSYS;
++
++ mutex_lock(&overlay->mutex);
++
++ for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
++
++ if (overlay->stream[i].claimed)
++ continue;
++
++ overlay->stream[i].claimed = true;
++ *out = i;
++ mutex_unlock(&overlay->mutex);
++ return 0;
++ }
++
++ mutex_unlock(&overlay->mutex);
++ return -ESRCH;
++}
++
++int vmw_overlay_unref(struct vmw_private *dev_priv, uint32_t stream_id)
++{
++ struct vmw_overlay *overlay = dev_priv->overlay_priv;
++
++ BUG_ON(stream_id >= VMW_MAX_NUM_STREAMS);
++
++ if (!overlay)
++ return -ENOSYS;
++
++ mutex_lock(&overlay->mutex);
++
++ WARN_ON(!overlay->stream[stream_id].claimed);
++ vmw_overlay_stop(dev_priv, stream_id, false, false);
++ overlay->stream[stream_id].claimed = false;
++
++ mutex_unlock(&overlay->mutex);
++ return 0;
++}
++
++int vmw_overlay_init(struct vmw_private *dev_priv)
++{
++ struct vmw_overlay *overlay;
++ int i;
++
++ if (dev_priv->overlay_priv)
++ return -EINVAL;
++
++ if (!(dev_priv->fifo.capabilities & SVGA_FIFO_CAP_VIDEO) &&
++ (dev_priv->fifo.capabilities & SVGA_FIFO_CAP_ESCAPE)) {
++ DRM_INFO("hardware doesn't support overlays\n");
++ return -ENOSYS;
++ }
++
++ overlay = kmalloc(GFP_KERNEL, sizeof(*overlay));
++ if (!overlay)
++ return -ENOMEM;
++
++ memset(overlay, 0, sizeof(*overlay));
++ mutex_init(&overlay->mutex);
++ for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
++ overlay->stream[i].buf = NULL;
++ overlay->stream[i].paused = false;
++ overlay->stream[i].claimed = false;
++ }
++
++ dev_priv->overlay_priv = overlay;
++
++ return 0;
++}
++
++int vmw_overlay_close(struct vmw_private *dev_priv)
++{
++ struct vmw_overlay *overlay = dev_priv->overlay_priv;
++ bool forgotten_buffer = false;
++ int i;
++
++ if (!overlay)
++ return -ENOSYS;
++
++ for (i = 0; i < VMW_MAX_NUM_STREAMS; i++) {
++ if (overlay->stream[i].buf) {
++ forgotten_buffer = true;
++ vmw_overlay_stop(dev_priv, i, false, false);
++ }
++ }
++
++ WARN_ON(forgotten_buffer);
++
++ dev_priv->overlay_priv = NULL;
++ kfree(overlay);
++
++ return 0;
++}
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_reg.h b/drivers/gpu/drm/vmwgfx/vmwgfx_reg.h
+new file mode 100644
+index 0000000..9d0dd3a
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_reg.h
+@@ -0,0 +1,57 @@
++/**************************************************************************
++ *
++ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++/**
++ * This file contains virtual hardware defines for kernel space.
++ */
++
++#ifndef _VMWGFX_REG_H_
++#define _VMWGFX_REG_H_
++
++#include <linux/types.h>
++
++#define VMWGFX_INDEX_PORT 0x0
++#define VMWGFX_VALUE_PORT 0x1
++#define VMWGFX_IRQSTATUS_PORT 0x8
++
++struct svga_guest_mem_descriptor {
++ __le32 ppn;
++ __le32 num_pages;
++};
++
++struct svga_fifo_cmd_fence {
++ __le32 fence;
++};
++
++#define SVGA_SYNC_GENERIC 1
++#define SVGA_SYNC_FIFOFULL 2
++
++#include "svga_types.h"
++
++#include "svga3d_reg.h"
++
++#endif
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+new file mode 100644
+index 0000000..f8fbbc6
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_resource.c
+@@ -0,0 +1,1187 @@
++/**************************************************************************
++ *
++ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++#include "vmwgfx_drv.h"
++#include "vmwgfx_drm.h"
++#include "ttm/ttm_object.h"
++#include "ttm/ttm_placement.h"
++#include "drmP.h"
++
++#define VMW_RES_CONTEXT ttm_driver_type0
++#define VMW_RES_SURFACE ttm_driver_type1
++#define VMW_RES_STREAM ttm_driver_type2
++
++struct vmw_user_context {
++ struct ttm_base_object base;
++ struct vmw_resource res;
++};
++
++struct vmw_user_surface {
++ struct ttm_base_object base;
++ struct vmw_surface srf;
++};
++
++struct vmw_user_dma_buffer {
++ struct ttm_base_object base;
++ struct vmw_dma_buffer dma;
++};
++
++struct vmw_bo_user_rep {
++ uint32_t handle;
++ uint64_t map_handle;
++};
++
++struct vmw_stream {
++ struct vmw_resource res;
++ uint32_t stream_id;
++};
++
++struct vmw_user_stream {
++ struct ttm_base_object base;
++ struct vmw_stream stream;
++};
++
++static inline struct vmw_dma_buffer *
++vmw_dma_buffer(struct ttm_buffer_object *bo)
++{
++ return container_of(bo, struct vmw_dma_buffer, base);
++}
++
++static inline struct vmw_user_dma_buffer *
++vmw_user_dma_buffer(struct ttm_buffer_object *bo)
++{
++ struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
++ return container_of(vmw_bo, struct vmw_user_dma_buffer, dma);
++}
++
++struct vmw_resource *vmw_resource_reference(struct vmw_resource *res)
++{
++ kref_get(&res->kref);
++ return res;
++}
++
++static void vmw_resource_release(struct kref *kref)
++{
++ struct vmw_resource *res =
++ container_of(kref, struct vmw_resource, kref);
++ struct vmw_private *dev_priv = res->dev_priv;
++
++ idr_remove(res->idr, res->id);
++ write_unlock(&dev_priv->resource_lock);
++
++ if (likely(res->hw_destroy != NULL))
++ res->hw_destroy(res);
++
++ if (res->res_free != NULL)
++ res->res_free(res);
++ else
++ kfree(res);
++
++ write_lock(&dev_priv->resource_lock);
++}
++
++void vmw_resource_unreference(struct vmw_resource **p_res)
++{
++ struct vmw_resource *res = *p_res;
++ struct vmw_private *dev_priv = res->dev_priv;
++
++ *p_res = NULL;
++ write_lock(&dev_priv->resource_lock);
++ kref_put(&res->kref, vmw_resource_release);
++ write_unlock(&dev_priv->resource_lock);
++}
++
++static int vmw_resource_init(struct vmw_private *dev_priv,
++ struct vmw_resource *res,
++ struct idr *idr,
++ enum ttm_object_type obj_type,
++ void (*res_free) (struct vmw_resource *res))
++{
++ int ret;
++
++ kref_init(&res->kref);
++ res->hw_destroy = NULL;
++ res->res_free = res_free;
++ res->res_type = obj_type;
++ res->idr = idr;
++ res->avail = false;
++ res->dev_priv = dev_priv;
++
++ do {
++ if (unlikely(idr_pre_get(idr, GFP_KERNEL) == 0))
++ return -ENOMEM;
++
++ write_lock(&dev_priv->resource_lock);
++ ret = idr_get_new_above(idr, res, 1, &res->id);
++ write_unlock(&dev_priv->resource_lock);
++
++ } while (ret == -EAGAIN);
++
++ return ret;
++}
++
++/**
++ * vmw_resource_activate
++ *
++ * @res: Pointer to the newly created resource
++ * @hw_destroy: Destroy function. NULL if none.
++ *
++ * Activate a resource after the hardware has been made aware of it.
++ * Set tye destroy function to @destroy. Typically this frees the
++ * resource and destroys the hardware resources associated with it.
++ * Activate basically means that the function vmw_resource_lookup will
++ * find it.
++ */
++
++static void vmw_resource_activate(struct vmw_resource *res,
++ void (*hw_destroy) (struct vmw_resource *))
++{
++ struct vmw_private *dev_priv = res->dev_priv;
++
++ write_lock(&dev_priv->resource_lock);
++ res->avail = true;
++ res->hw_destroy = hw_destroy;
++ write_unlock(&dev_priv->resource_lock);
++}
++
++struct vmw_resource *vmw_resource_lookup(struct vmw_private *dev_priv,
++ struct idr *idr, int id)
++{
++ struct vmw_resource *res;
++
++ read_lock(&dev_priv->resource_lock);
++ res = idr_find(idr, id);
++ if (res && res->avail)
++ kref_get(&res->kref);
++ else
++ res = NULL;
++ read_unlock(&dev_priv->resource_lock);
++
++ if (unlikely(res == NULL))
++ return NULL;
++
++ return res;
++}
++
++/**
++ * Context management:
++ */
++
++static void vmw_hw_context_destroy(struct vmw_resource *res)
++{
++
++ struct vmw_private *dev_priv = res->dev_priv;
++ struct {
++ SVGA3dCmdHeader header;
++ SVGA3dCmdDestroyContext body;
++ } *cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
++
++ if (unlikely(cmd == NULL)) {
++ DRM_ERROR("Failed reserving FIFO space for surface "
++ "destruction.\n");
++ return;
++ }
++
++ cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DESTROY);
++ cmd->header.size = cpu_to_le32(sizeof(cmd->body));
++ cmd->body.cid = cpu_to_le32(res->id);
++
++ vmw_fifo_commit(dev_priv, sizeof(*cmd));
++}
++
++static int vmw_context_init(struct vmw_private *dev_priv,
++ struct vmw_resource *res,
++ void (*res_free) (struct vmw_resource *res))
++{
++ int ret;
++
++ struct {
++ SVGA3dCmdHeader header;
++ SVGA3dCmdDefineContext body;
++ } *cmd;
++
++ ret = vmw_resource_init(dev_priv, res, &dev_priv->context_idr,
++ VMW_RES_CONTEXT, res_free);
++
++ if (unlikely(ret != 0)) {
++ if (res_free == NULL)
++ kfree(res);
++ else
++ res_free(res);
++ return ret;
++ }
++
++ cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
++ if (unlikely(cmd == NULL)) {
++ DRM_ERROR("Fifo reserve failed.\n");
++ vmw_resource_unreference(&res);
++ return -ENOMEM;
++ }
++
++ cmd->header.id = cpu_to_le32(SVGA_3D_CMD_CONTEXT_DEFINE);
++ cmd->header.size = cpu_to_le32(sizeof(cmd->body));
++ cmd->body.cid = cpu_to_le32(res->id);
++
++ vmw_fifo_commit(dev_priv, sizeof(*cmd));
++ vmw_resource_activate(res, vmw_hw_context_destroy);
++ return 0;
++}
++
++struct vmw_resource *vmw_context_alloc(struct vmw_private *dev_priv)
++{
++ struct vmw_resource *res = kmalloc(sizeof(*res), GFP_KERNEL);
++ int ret;
++
++ if (unlikely(res == NULL))
++ return NULL;
++
++ ret = vmw_context_init(dev_priv, res, NULL);
++ return (ret == 0) ? res : NULL;
++}
++
++/**
++ * User-space context management:
++ */
++
++static void vmw_user_context_free(struct vmw_resource *res)
++{
++ struct vmw_user_context *ctx =
++ container_of(res, struct vmw_user_context, res);
++
++ kfree(ctx);
++}
++
++/**
++ * This function is called when user space has no more references on the
++ * base object. It releases the base-object's reference on the resource object.
++ */
++
++static void vmw_user_context_base_release(struct ttm_base_object **p_base)
++{
++ struct ttm_base_object *base = *p_base;
++ struct vmw_user_context *ctx =
++ container_of(base, struct vmw_user_context, base);
++ struct vmw_resource *res = &ctx->res;
++
++ *p_base = NULL;
++ vmw_resource_unreference(&res);
++}
++
++int vmw_context_destroy_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ struct vmw_resource *res;
++ struct vmw_user_context *ctx;
++ struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data;
++ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
++ int ret = 0;
++
++ res = vmw_resource_lookup(dev_priv, &dev_priv->context_idr, arg->cid);
++ if (unlikely(res == NULL))
++ return -EINVAL;
++
++ if (res->res_free != &vmw_user_context_free) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ ctx = container_of(res, struct vmw_user_context, res);
++ if (ctx->base.tfile != tfile && !ctx->base.shareable) {
++ ret = -EPERM;
++ goto out;
++ }
++
++ ttm_ref_object_base_unref(tfile, ctx->base.hash.key, TTM_REF_USAGE);
++out:
++ vmw_resource_unreference(&res);
++ return ret;
++}
++
++int vmw_context_define_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ struct vmw_user_context *ctx = kmalloc(sizeof(*ctx), GFP_KERNEL);
++ struct vmw_resource *res;
++ struct vmw_resource *tmp;
++ struct drm_vmw_context_arg *arg = (struct drm_vmw_context_arg *)data;
++ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
++ int ret;
++
++ if (unlikely(ctx == NULL))
++ return -ENOMEM;
++
++ res = &ctx->res;
++ ctx->base.shareable = false;
++ ctx->base.tfile = NULL;
++
++ ret = vmw_context_init(dev_priv, res, vmw_user_context_free);
++ if (unlikely(ret != 0))
++ return ret;
++
++ tmp = vmw_resource_reference(&ctx->res);
++ ret = ttm_base_object_init(tfile, &ctx->base, false, VMW_RES_CONTEXT,
++ &vmw_user_context_base_release, NULL);
++
++ if (unlikely(ret != 0)) {
++ vmw_resource_unreference(&tmp);
++ goto out_err;
++ }
++
++ arg->cid = res->id;
++out_err:
++ vmw_resource_unreference(&res);
++ return ret;
++
++}
++
++int vmw_context_check(struct vmw_private *dev_priv,
++ struct ttm_object_file *tfile,
++ int id)
++{
++ struct vmw_resource *res;
++ int ret = 0;
++
++ read_lock(&dev_priv->resource_lock);
++ res = idr_find(&dev_priv->context_idr, id);
++ if (res && res->avail) {
++ struct vmw_user_context *ctx =
++ container_of(res, struct vmw_user_context, res);
++ if (ctx->base.tfile != tfile && !ctx->base.shareable)
++ ret = -EPERM;
++ } else
++ ret = -EINVAL;
++ read_unlock(&dev_priv->resource_lock);
++
++ return ret;
++}
++
++
++/**
++ * Surface management.
++ */
++
++static void vmw_hw_surface_destroy(struct vmw_resource *res)
++{
++
++ struct vmw_private *dev_priv = res->dev_priv;
++ struct {
++ SVGA3dCmdHeader header;
++ SVGA3dCmdDestroySurface body;
++ } *cmd = vmw_fifo_reserve(dev_priv, sizeof(*cmd));
++
++ if (unlikely(cmd == NULL)) {
++ DRM_ERROR("Failed reserving FIFO space for surface "
++ "destruction.\n");
++ return;
++ }
++
++ cmd->header.id = cpu_to_le32(SVGA_3D_CMD_SURFACE_DESTROY);
++ cmd->header.size = cpu_to_le32(sizeof(cmd->body));
++ cmd->body.sid = cpu_to_le32(res->id);
++
++ vmw_fifo_commit(dev_priv, sizeof(*cmd));
++}
++
++void vmw_surface_res_free(struct vmw_resource *res)
++{
++ struct vmw_surface *srf = container_of(res, struct vmw_surface, res);
++
++ kfree(srf->sizes);
++ kfree(srf->snooper.image);
++ kfree(srf);
++}
++
++int vmw_surface_init(struct vmw_private *dev_priv,
++ struct vmw_surface *srf,
++ void (*res_free) (struct vmw_resource *res))
++{
++ int ret;
++ struct {
++ SVGA3dCmdHeader header;
++ SVGA3dCmdDefineSurface body;
++ } *cmd;
++ SVGA3dSize *cmd_size;
++ struct vmw_resource *res = &srf->res;
++ struct drm_vmw_size *src_size;
++ size_t submit_size;
++ uint32_t cmd_len;
++ int i;
++
++ BUG_ON(res_free == NULL);
++ ret = vmw_resource_init(dev_priv, res, &dev_priv->surface_idr,
++ VMW_RES_SURFACE, res_free);
++
++ if (unlikely(ret != 0)) {
++ res_free(res);
++ return ret;
++ }
++
++ submit_size = sizeof(*cmd) + srf->num_sizes * sizeof(SVGA3dSize);
++ cmd_len = sizeof(cmd->body) + srf->num_sizes * sizeof(SVGA3dSize);
++
++ cmd = vmw_fifo_reserve(dev_priv, submit_size);
++ if (unlikely(cmd == NULL)) {
++ DRM_ERROR("Fifo reserve failed for create surface.\n");
++ vmw_resource_unreference(&res);
++ return -ENOMEM;
++ }
++
++ cmd->header.id = cpu_to_le32(SVGA_3D_CMD_SURFACE_DEFINE);
++ cmd->header.size = cpu_to_le32(cmd_len);
++ cmd->body.sid = cpu_to_le32(res->id);
++ cmd->body.surfaceFlags = cpu_to_le32(srf->flags);
++ cmd->body.format = cpu_to_le32(srf->format);
++ for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i) {
++ cmd->body.face[i].numMipLevels =
++ cpu_to_le32(srf->mip_levels[i]);
++ }
++
++ cmd += 1;
++ cmd_size = (SVGA3dSize *) cmd;
++ src_size = srf->sizes;
++
++ for (i = 0; i < srf->num_sizes; ++i, cmd_size++, src_size++) {
++ cmd_size->width = cpu_to_le32(src_size->width);
++ cmd_size->height = cpu_to_le32(src_size->height);
++ cmd_size->depth = cpu_to_le32(src_size->depth);
++ }
++
++ vmw_fifo_commit(dev_priv, submit_size);
++ vmw_resource_activate(res, vmw_hw_surface_destroy);
++ return 0;
++}
++
++static void vmw_user_surface_free(struct vmw_resource *res)
++{
++ struct vmw_surface *srf = container_of(res, struct vmw_surface, res);
++ struct vmw_user_surface *user_srf =
++ container_of(srf, struct vmw_user_surface, srf);
++
++ kfree(srf->sizes);
++ kfree(srf->snooper.image);
++ kfree(user_srf);
++}
++
++int vmw_user_surface_lookup_handle(struct vmw_private *dev_priv,
++ struct ttm_object_file *tfile,
++ uint32_t handle, struct vmw_surface **out)
++{
++ struct vmw_resource *res;
++ struct vmw_surface *srf;
++ struct vmw_user_surface *user_srf;
++ struct ttm_base_object *base;
++ int ret = -EINVAL;
++
++ base = ttm_base_object_lookup(tfile, handle);
++ if (unlikely(base == NULL))
++ return -EINVAL;
++
++ if (unlikely(base->object_type != VMW_RES_SURFACE))
++ goto out_bad_resource;
++
++ user_srf = container_of(base, struct vmw_user_surface, base);
++ srf = &user_srf->srf;
++ res = &srf->res;
++
++ read_lock(&dev_priv->resource_lock);
++
++ if (!res->avail || res->res_free != &vmw_user_surface_free) {
++ read_unlock(&dev_priv->resource_lock);
++ goto out_bad_resource;
++ }
++
++ kref_get(&res->kref);
++ read_unlock(&dev_priv->resource_lock);
++
++ *out = srf;
++ ret = 0;
++
++out_bad_resource:
++ ttm_base_object_unref(&base);
++
++ return ret;
++}
++
++static void vmw_user_surface_base_release(struct ttm_base_object **p_base)
++{
++ struct ttm_base_object *base = *p_base;
++ struct vmw_user_surface *user_srf =
++ container_of(base, struct vmw_user_surface, base);
++ struct vmw_resource *res = &user_srf->srf.res;
++
++ *p_base = NULL;
++ vmw_resource_unreference(&res);
++}
++
++int vmw_surface_destroy_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_vmw_surface_arg *arg = (struct drm_vmw_surface_arg *)data;
++ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
++
++ return ttm_ref_object_base_unref(tfile, arg->sid, TTM_REF_USAGE);
++}
++
++int vmw_surface_define_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ struct vmw_user_surface *user_srf =
++ kmalloc(sizeof(*user_srf), GFP_KERNEL);
++ struct vmw_surface *srf;
++ struct vmw_resource *res;
++ struct vmw_resource *tmp;
++ union drm_vmw_surface_create_arg *arg =
++ (union drm_vmw_surface_create_arg *)data;
++ struct drm_vmw_surface_create_req *req = &arg->req;
++ struct drm_vmw_surface_arg *rep = &arg->rep;
++ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
++ struct drm_vmw_size __user *user_sizes;
++ int ret;
++ int i;
++
++ if (unlikely(user_srf == NULL))
++ return -ENOMEM;
++
++ srf = &user_srf->srf;
++ res = &srf->res;
++
++ srf->flags = req->flags;
++ srf->format = req->format;
++ srf->scanout = req->scanout;
++ memcpy(srf->mip_levels, req->mip_levels, sizeof(srf->mip_levels));
++ srf->num_sizes = 0;
++ for (i = 0; i < DRM_VMW_MAX_SURFACE_FACES; ++i)
++ srf->num_sizes += srf->mip_levels[i];
++
++ if (srf->num_sizes > DRM_VMW_MAX_SURFACE_FACES *
++ DRM_VMW_MAX_MIP_LEVELS) {
++ ret = -EINVAL;
++ goto out_err0;
++ }
++
++ srf->sizes = kmalloc(srf->num_sizes * sizeof(*srf->sizes), GFP_KERNEL);
++ if (unlikely(srf->sizes == NULL)) {
++ ret = -ENOMEM;
++ goto out_err0;
++ }
++
++ user_sizes = (struct drm_vmw_size __user *)(unsigned long)
++ req->size_addr;
++
++ ret = copy_from_user(srf->sizes, user_sizes,
++ srf->num_sizes * sizeof(*srf->sizes));
++ if (unlikely(ret != 0))
++ goto out_err1;
++
++ if (srf->scanout &&
++ srf->num_sizes == 1 &&
++ srf->sizes[0].width == 64 &&
++ srf->sizes[0].height == 64 &&
++ srf->format == SVGA3D_A8R8G8B8) {
++
++ srf->snooper.image = kmalloc(64 * 64 * 4, GFP_KERNEL);
++ /* clear the image */
++ if (srf->snooper.image) {
++ memset(srf->snooper.image, 0x00, 64 * 64 * 4);
++ } else {
++ DRM_ERROR("Failed to allocate cursor_image\n");
++ ret = -ENOMEM;
++ goto out_err1;
++ }
++ } else {
++ srf->snooper.image = NULL;
++ }
++ srf->snooper.crtc = NULL;
++
++ user_srf->base.shareable = false;
++ user_srf->base.tfile = NULL;
++
++ /**
++ * From this point, the generic resource management functions
++ * destroy the object on failure.
++ */
++
++ ret = vmw_surface_init(dev_priv, srf, vmw_user_surface_free);
++ if (unlikely(ret != 0))
++ return ret;
++
++ tmp = vmw_resource_reference(&srf->res);
++ ret = ttm_base_object_init(tfile, &user_srf->base,
++ req->shareable, VMW_RES_SURFACE,
++ &vmw_user_surface_base_release, NULL);
++
++ if (unlikely(ret != 0)) {
++ vmw_resource_unreference(&tmp);
++ vmw_resource_unreference(&res);
++ return ret;
++ }
++
++ rep->sid = user_srf->base.hash.key;
++ if (rep->sid == SVGA3D_INVALID_ID)
++ DRM_ERROR("Created bad Surface ID.\n");
++
++ vmw_resource_unreference(&res);
++ return 0;
++out_err1:
++ kfree(srf->sizes);
++out_err0:
++ kfree(user_srf);
++ return ret;
++}
++
++int vmw_surface_reference_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ union drm_vmw_surface_reference_arg *arg =
++ (union drm_vmw_surface_reference_arg *)data;
++ struct drm_vmw_surface_arg *req = &arg->req;
++ struct drm_vmw_surface_create_req *rep = &arg->rep;
++ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
++ struct vmw_surface *srf;
++ struct vmw_user_surface *user_srf;
++ struct drm_vmw_size __user *user_sizes;
++ struct ttm_base_object *base;
++ int ret = -EINVAL;
++
++ base = ttm_base_object_lookup(tfile, req->sid);
++ if (unlikely(base == NULL)) {
++ DRM_ERROR("Could not find surface to reference.\n");
++ return -EINVAL;
++ }
++
++ if (unlikely(base->object_type != VMW_RES_SURFACE))
++ goto out_bad_resource;
++
++ user_srf = container_of(base, struct vmw_user_surface, base);
++ srf = &user_srf->srf;
++
++ ret = ttm_ref_object_add(tfile, &user_srf->base, TTM_REF_USAGE, NULL);
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("Could not add a reference to a surface.\n");
++ goto out_no_reference;
++ }
++
++ rep->flags = srf->flags;
++ rep->format = srf->format;
++ memcpy(rep->mip_levels, srf->mip_levels, sizeof(srf->mip_levels));
++ user_sizes = (struct drm_vmw_size __user *)(unsigned long)
++ rep->size_addr;
++
++ if (user_sizes)
++ ret = copy_to_user(user_sizes, srf->sizes,
++ srf->num_sizes * sizeof(*srf->sizes));
++ if (unlikely(ret != 0))
++ DRM_ERROR("copy_to_user failed %p %u\n",
++ user_sizes, srf->num_sizes);
++out_bad_resource:
++out_no_reference:
++ ttm_base_object_unref(&base);
++
++ return ret;
++}
++
++int vmw_surface_check(struct vmw_private *dev_priv,
++ struct ttm_object_file *tfile,
++ uint32_t handle, int *id)
++{
++ struct ttm_base_object *base;
++ struct vmw_user_surface *user_srf;
++
++ int ret = -EPERM;
++
++ base = ttm_base_object_lookup(tfile, handle);
++ if (unlikely(base == NULL))
++ return -EINVAL;
++
++ if (unlikely(base->object_type != VMW_RES_SURFACE))
++ goto out_bad_surface;
++
++ user_srf = container_of(base, struct vmw_user_surface, base);
++ *id = user_srf->srf.res.id;
++ ret = 0;
++
++out_bad_surface:
++ /**
++ * FIXME: May deadlock here when called from the
++ * command parsing code.
++ */
++
++ ttm_base_object_unref(&base);
++ return ret;
++}
++
++/**
++ * Buffer management.
++ */
++
++static size_t vmw_dmabuf_acc_size(struct ttm_bo_global *glob,
++ unsigned long num_pages)
++{
++ static size_t bo_user_size = ~0;
++
++ size_t page_array_size =
++ (num_pages * sizeof(void *) + PAGE_SIZE - 1) & PAGE_MASK;
++
++ if (unlikely(bo_user_size == ~0)) {
++ bo_user_size = glob->ttm_bo_extra_size +
++ ttm_round_pot(sizeof(struct vmw_dma_buffer));
++ }
++
++ return bo_user_size + page_array_size;
++}
++
++void vmw_dmabuf_gmr_unbind(struct ttm_buffer_object *bo)
++{
++ struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
++ struct ttm_bo_global *glob = bo->glob;
++ struct vmw_private *dev_priv =
++ container_of(bo->bdev, struct vmw_private, bdev);
++
++ if (vmw_bo->gmr_bound) {
++ vmw_gmr_unbind(dev_priv, vmw_bo->gmr_id);
++ spin_lock(&glob->lru_lock);
++ ida_remove(&dev_priv->gmr_ida, vmw_bo->gmr_id);
++ spin_unlock(&glob->lru_lock);
++ vmw_bo->gmr_bound = false;
++ }
++}
++
++void vmw_dmabuf_bo_free(struct ttm_buffer_object *bo)
++{
++ struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
++ struct ttm_bo_global *glob = bo->glob;
++
++ vmw_dmabuf_gmr_unbind(bo);
++ ttm_mem_global_free(glob->mem_glob, bo->acc_size);
++ kfree(vmw_bo);
++}
++
++int vmw_dmabuf_init(struct vmw_private *dev_priv,
++ struct vmw_dma_buffer *vmw_bo,
++ size_t size, struct ttm_placement *placement,
++ bool interruptible,
++ void (*bo_free) (struct ttm_buffer_object *bo))
++{
++ struct ttm_bo_device *bdev = &dev_priv->bdev;
++ struct ttm_mem_global *mem_glob = bdev->glob->mem_glob;
++ size_t acc_size;
++ int ret;
++
++ BUG_ON(!bo_free);
++
++ acc_size =
++ vmw_dmabuf_acc_size(bdev->glob,
++ (size + PAGE_SIZE - 1) >> PAGE_SHIFT);
++
++ ret = ttm_mem_global_alloc(mem_glob, acc_size, false, false);
++ if (unlikely(ret != 0)) {
++ /* we must free the bo here as
++ * ttm_buffer_object_init does so as well */
++ bo_free(&vmw_bo->base);
++ return ret;
++ }
++
++ memset(vmw_bo, 0, sizeof(*vmw_bo));
++
++ INIT_LIST_HEAD(&vmw_bo->gmr_lru);
++ INIT_LIST_HEAD(&vmw_bo->validate_list);
++ vmw_bo->gmr_id = 0;
++ vmw_bo->gmr_bound = false;
++
++ ret = ttm_bo_init(bdev, &vmw_bo->base, size,
++ ttm_bo_type_device, placement,
++ 0, 0, interruptible,
++ NULL, acc_size, bo_free);
++ return ret;
++}
++
++static void vmw_user_dmabuf_destroy(struct ttm_buffer_object *bo)
++{
++ struct vmw_user_dma_buffer *vmw_user_bo = vmw_user_dma_buffer(bo);
++ struct ttm_bo_global *glob = bo->glob;
++
++ vmw_dmabuf_gmr_unbind(bo);
++ ttm_mem_global_free(glob->mem_glob, bo->acc_size);
++ kfree(vmw_user_bo);
++}
++
++static void vmw_user_dmabuf_release(struct ttm_base_object **p_base)
++{
++ struct vmw_user_dma_buffer *vmw_user_bo;
++ struct ttm_base_object *base = *p_base;
++ struct ttm_buffer_object *bo;
++
++ *p_base = NULL;
++
++ if (unlikely(base == NULL))
++ return;
++
++ vmw_user_bo = container_of(base, struct vmw_user_dma_buffer, base);
++ bo = &vmw_user_bo->dma.base;
++ ttm_bo_unref(&bo);
++}
++
++int vmw_dmabuf_alloc_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ union drm_vmw_alloc_dmabuf_arg *arg =
++ (union drm_vmw_alloc_dmabuf_arg *)data;
++ struct drm_vmw_alloc_dmabuf_req *req = &arg->req;
++ struct drm_vmw_dmabuf_rep *rep = &arg->rep;
++ struct vmw_user_dma_buffer *vmw_user_bo;
++ struct ttm_buffer_object *tmp;
++ struct vmw_master *vmaster = vmw_master(file_priv->master);
++ int ret;
++
++ vmw_user_bo = kzalloc(sizeof(*vmw_user_bo), GFP_KERNEL);
++ if (unlikely(vmw_user_bo == NULL))
++ return -ENOMEM;
++
++ ret = ttm_read_lock(&vmaster->lock, true);
++ if (unlikely(ret != 0)) {
++ kfree(vmw_user_bo);
++ return ret;
++ }
++
++ ret = vmw_dmabuf_init(dev_priv, &vmw_user_bo->dma, req->size,
++ &vmw_vram_sys_placement, true,
++ &vmw_user_dmabuf_destroy);
++ if (unlikely(ret != 0))
++ return ret;
++
++ tmp = ttm_bo_reference(&vmw_user_bo->dma.base);
++ ret = ttm_base_object_init(vmw_fpriv(file_priv)->tfile,
++ &vmw_user_bo->base,
++ false,
++ ttm_buffer_type,
++ &vmw_user_dmabuf_release, NULL);
++ if (unlikely(ret != 0)) {
++ ttm_bo_unref(&tmp);
++ } else {
++ rep->handle = vmw_user_bo->base.hash.key;
++ rep->map_handle = vmw_user_bo->dma.base.addr_space_offset;
++ rep->cur_gmr_id = vmw_user_bo->base.hash.key;
++ rep->cur_gmr_offset = 0;
++ }
++ ttm_bo_unref(&tmp);
++
++ ttm_read_unlock(&vmaster->lock);
++
++ return 0;
++}
++
++int vmw_dmabuf_unref_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct drm_vmw_unref_dmabuf_arg *arg =
++ (struct drm_vmw_unref_dmabuf_arg *)data;
++
++ return ttm_ref_object_base_unref(vmw_fpriv(file_priv)->tfile,
++ arg->handle,
++ TTM_REF_USAGE);
++}
++
++uint32_t vmw_dmabuf_validate_node(struct ttm_buffer_object *bo,
++ uint32_t cur_validate_node)
++{
++ struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
++
++ if (likely(vmw_bo->on_validate_list))
++ return vmw_bo->cur_validate_node;
++
++ vmw_bo->cur_validate_node = cur_validate_node;
++ vmw_bo->on_validate_list = true;
++
++ return cur_validate_node;
++}
++
++void vmw_dmabuf_validate_clear(struct ttm_buffer_object *bo)
++{
++ struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
++
++ vmw_bo->on_validate_list = false;
++}
++
++uint32_t vmw_dmabuf_gmr(struct ttm_buffer_object *bo)
++{
++ struct vmw_dma_buffer *vmw_bo;
++
++ if (bo->mem.mem_type == TTM_PL_VRAM)
++ return SVGA_GMR_FRAMEBUFFER;
++
++ vmw_bo = vmw_dma_buffer(bo);
++
++ return (vmw_bo->gmr_bound) ? vmw_bo->gmr_id : SVGA_GMR_NULL;
++}
++
++void vmw_dmabuf_set_gmr(struct ttm_buffer_object *bo, uint32_t id)
++{
++ struct vmw_dma_buffer *vmw_bo = vmw_dma_buffer(bo);
++ vmw_bo->gmr_bound = true;
++ vmw_bo->gmr_id = id;
++}
++
++int vmw_user_dmabuf_lookup(struct ttm_object_file *tfile,
++ uint32_t handle, struct vmw_dma_buffer **out)
++{
++ struct vmw_user_dma_buffer *vmw_user_bo;
++ struct ttm_base_object *base;
++
++ base = ttm_base_object_lookup(tfile, handle);
++ if (unlikely(base == NULL)) {
++ printk(KERN_ERR "Invalid buffer object handle 0x%08lx.\n",
++ (unsigned long)handle);
++ return -ESRCH;
++ }
++
++ if (unlikely(base->object_type != ttm_buffer_type)) {
++ ttm_base_object_unref(&base);
++ printk(KERN_ERR "Invalid buffer object handle 0x%08lx.\n",
++ (unsigned long)handle);
++ return -EINVAL;
++ }
++
++ vmw_user_bo = container_of(base, struct vmw_user_dma_buffer, base);
++ (void)ttm_bo_reference(&vmw_user_bo->dma.base);
++ ttm_base_object_unref(&base);
++ *out = &vmw_user_bo->dma;
++
++ return 0;
++}
++
++/**
++ * TODO: Implement a gmr id eviction mechanism. Currently we just fail
++ * when we're out of ids, causing GMR space to be allocated
++ * out of VRAM.
++ */
++
++int vmw_gmr_id_alloc(struct vmw_private *dev_priv, uint32_t *p_id)
++{
++ struct ttm_bo_global *glob = dev_priv->bdev.glob;
++ int id;
++ int ret;
++
++ do {
++ if (unlikely(ida_pre_get(&dev_priv->gmr_ida, GFP_KERNEL) == 0))
++ return -ENOMEM;
++
++ spin_lock(&glob->lru_lock);
++ ret = ida_get_new(&dev_priv->gmr_ida, &id);
++ spin_unlock(&glob->lru_lock);
++ } while (ret == -EAGAIN);
++
++ if (unlikely(ret != 0))
++ return ret;
++
++ if (unlikely(id >= dev_priv->max_gmr_ids)) {
++ spin_lock(&glob->lru_lock);
++ ida_remove(&dev_priv->gmr_ida, id);
++ spin_unlock(&glob->lru_lock);
++ return -EBUSY;
++ }
++
++ *p_id = (uint32_t) id;
++ return 0;
++}
++
++/*
++ * Stream managment
++ */
++
++static void vmw_stream_destroy(struct vmw_resource *res)
++{
++ struct vmw_private *dev_priv = res->dev_priv;
++ struct vmw_stream *stream;
++ int ret;
++
++ DRM_INFO("%s: unref\n", __func__);
++ stream = container_of(res, struct vmw_stream, res);
++
++ ret = vmw_overlay_unref(dev_priv, stream->stream_id);
++ WARN_ON(ret != 0);
++}
++
++static int vmw_stream_init(struct vmw_private *dev_priv,
++ struct vmw_stream *stream,
++ void (*res_free) (struct vmw_resource *res))
++{
++ struct vmw_resource *res = &stream->res;
++ int ret;
++
++ ret = vmw_resource_init(dev_priv, res, &dev_priv->stream_idr,
++ VMW_RES_STREAM, res_free);
++
++ if (unlikely(ret != 0)) {
++ if (res_free == NULL)
++ kfree(stream);
++ else
++ res_free(&stream->res);
++ return ret;
++ }
++
++ ret = vmw_overlay_claim(dev_priv, &stream->stream_id);
++ if (ret) {
++ vmw_resource_unreference(&res);
++ return ret;
++ }
++
++ DRM_INFO("%s: claimed\n", __func__);
++
++ vmw_resource_activate(&stream->res, vmw_stream_destroy);
++ return 0;
++}
++
++/**
++ * User-space context management:
++ */
++
++static void vmw_user_stream_free(struct vmw_resource *res)
++{
++ struct vmw_user_stream *stream =
++ container_of(res, struct vmw_user_stream, stream.res);
++
++ kfree(stream);
++}
++
++/**
++ * This function is called when user space has no more references on the
++ * base object. It releases the base-object's reference on the resource object.
++ */
++
++static void vmw_user_stream_base_release(struct ttm_base_object **p_base)
++{
++ struct ttm_base_object *base = *p_base;
++ struct vmw_user_stream *stream =
++ container_of(base, struct vmw_user_stream, base);
++ struct vmw_resource *res = &stream->stream.res;
++
++ *p_base = NULL;
++ vmw_resource_unreference(&res);
++}
++
++int vmw_stream_unref_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ struct vmw_resource *res;
++ struct vmw_user_stream *stream;
++ struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data;
++ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
++ int ret = 0;
++
++ res = vmw_resource_lookup(dev_priv, &dev_priv->stream_idr, arg->stream_id);
++ if (unlikely(res == NULL))
++ return -EINVAL;
++
++ if (res->res_free != &vmw_user_stream_free) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ stream = container_of(res, struct vmw_user_stream, stream.res);
++ if (stream->base.tfile != tfile) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ ttm_ref_object_base_unref(tfile, stream->base.hash.key, TTM_REF_USAGE);
++out:
++ vmw_resource_unreference(&res);
++ return ret;
++}
++
++int vmw_stream_claim_ioctl(struct drm_device *dev, void *data,
++ struct drm_file *file_priv)
++{
++ struct vmw_private *dev_priv = vmw_priv(dev);
++ struct vmw_user_stream *stream = kmalloc(sizeof(*stream), GFP_KERNEL);
++ struct vmw_resource *res;
++ struct vmw_resource *tmp;
++ struct drm_vmw_stream_arg *arg = (struct drm_vmw_stream_arg *)data;
++ struct ttm_object_file *tfile = vmw_fpriv(file_priv)->tfile;
++ int ret;
++
++ if (unlikely(stream == NULL))
++ return -ENOMEM;
++
++ res = &stream->stream.res;
++ stream->base.shareable = false;
++ stream->base.tfile = NULL;
++
++ ret = vmw_stream_init(dev_priv, &stream->stream, vmw_user_stream_free);
++ if (unlikely(ret != 0))
++ return ret;
++
++ tmp = vmw_resource_reference(res);
++ ret = ttm_base_object_init(tfile, &stream->base, false, VMW_RES_STREAM,
++ &vmw_user_stream_base_release, NULL);
++
++ if (unlikely(ret != 0)) {
++ vmw_resource_unreference(&tmp);
++ goto out_err;
++ }
++
++ arg->stream_id = res->id;
++out_err:
++ vmw_resource_unreference(&res);
++ return ret;
++}
++
++int vmw_user_stream_lookup(struct vmw_private *dev_priv,
++ struct ttm_object_file *tfile,
++ uint32_t *inout_id, struct vmw_resource **out)
++{
++ struct vmw_user_stream *stream;
++ struct vmw_resource *res;
++ int ret;
++
++ res = vmw_resource_lookup(dev_priv, &dev_priv->stream_idr, *inout_id);
++ if (unlikely(res == NULL))
++ return -EINVAL;
++
++ if (res->res_free != &vmw_user_stream_free) {
++ ret = -EINVAL;
++ goto err_ref;
++ }
++
++ stream = container_of(res, struct vmw_user_stream, stream.res);
++ if (stream->base.tfile != tfile) {
++ ret = -EPERM;
++ goto err_ref;
++ }
++
++ *inout_id = stream->stream.stream_id;
++ *out = res;
++ return 0;
++err_ref:
++ vmw_resource_unreference(&res);
++ return ret;
++}
+diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c
+new file mode 100644
+index 0000000..e3df4ad
+--- /dev/null
++++ b/drivers/gpu/drm/vmwgfx/vmwgfx_ttm_glue.c
+@@ -0,0 +1,99 @@
++/**************************************************************************
++ *
++ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++#include "drmP.h"
++#include "vmwgfx_drv.h"
++
++int vmw_mmap(struct file *filp, struct vm_area_struct *vma)
++{
++ struct drm_file *file_priv;
++ struct vmw_private *dev_priv;
++
++ if (unlikely(vma->vm_pgoff < VMWGFX_FILE_PAGE_OFFSET)) {
++ if (vmw_fifo_mmap(filp, vma) == 0)
++ return 0;
++ return drm_mmap(filp, vma);
++ }
++
++ file_priv = (struct drm_file *)filp->private_data;
++ dev_priv = vmw_priv(file_priv->minor->dev);
++ return ttm_bo_mmap(filp, vma, &dev_priv->bdev);
++}
++
++static int vmw_ttm_mem_global_init(struct ttm_global_reference *ref)
++{
++ DRM_INFO("global init.\n");
++ return ttm_mem_global_init(ref->object);
++}
++
++static void vmw_ttm_mem_global_release(struct ttm_global_reference *ref)
++{
++ ttm_mem_global_release(ref->object);
++}
++
++int vmw_ttm_global_init(struct vmw_private *dev_priv)
++{
++ struct ttm_global_reference *global_ref;
++ int ret;
++
++ global_ref = &dev_priv->mem_global_ref;
++ global_ref->global_type = TTM_GLOBAL_TTM_MEM;
++ global_ref->size = sizeof(struct ttm_mem_global);
++ global_ref->init = &vmw_ttm_mem_global_init;
++ global_ref->release = &vmw_ttm_mem_global_release;
++
++ ret = ttm_global_item_ref(global_ref);
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("Failed setting up TTM memory accounting.\n");
++ return ret;
++ }
++
++ dev_priv->bo_global_ref.mem_glob =
++ dev_priv->mem_global_ref.object;
++ global_ref = &dev_priv->bo_global_ref.ref;
++ global_ref->global_type = TTM_GLOBAL_TTM_BO;
++ global_ref->size = sizeof(struct ttm_bo_global);
++ global_ref->init = &ttm_bo_global_init;
++ global_ref->release = &ttm_bo_global_release;
++ ret = ttm_global_item_ref(global_ref);
++
++ if (unlikely(ret != 0)) {
++ DRM_ERROR("Failed setting up TTM buffer objects.\n");
++ goto out_no_bo;
++ }
++
++ return 0;
++out_no_bo:
++ ttm_global_item_unref(&dev_priv->mem_global_ref);
++ return ret;
++}
++
++void vmw_ttm_global_release(struct vmw_private *dev_priv)
++{
++ ttm_global_item_unref(&dev_priv->bo_global_ref.ref);
++ ttm_global_item_unref(&dev_priv->mem_global_ref);
++}
+diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig
+index d21b346..260a581 100644
+--- a/drivers/staging/Kconfig
++++ b/drivers/staging/Kconfig
+@@ -101,8 +101,6 @@ source "drivers/staging/p9auth/Kconfig"
+
+ source "drivers/staging/line6/Kconfig"
+
+-source "drivers/gpu/drm/radeon/Kconfig"
+-
+ source "drivers/staging/octeon/Kconfig"
+
+ source "drivers/staging/serqt_usb2/Kconfig"
+diff --git a/fs/ubifs/gc.c b/fs/ubifs/gc.c
+index 618c270..e5a3d8e 100644
+--- a/fs/ubifs/gc.c
++++ b/fs/ubifs/gc.c
+@@ -54,6 +54,7 @@
+ */
+
+ #include <linux/pagemap.h>
++#include <linux/list_sort.h>
+ #include "ubifs.h"
+
+ /*
+@@ -108,101 +109,6 @@ static int switch_gc_head(struct ubifs_info *c)
+ }
+
+ /**
+- * list_sort - sort a list.
+- * @priv: private data, passed to @cmp
+- * @head: the list to sort
+- * @cmp: the elements comparison function
+- *
+- * This function has been implemented by Mark J Roberts <mjr@znex.org>. It
+- * implements "merge sort" which has O(nlog(n)) complexity. The list is sorted
+- * in ascending order.
+- *
+- * The comparison function @cmp is supposed to return a negative value if @a is
+- * than @b, and a positive value if @a is greater than @b. If @a and @b are
+- * equivalent, then it does not matter what this function returns.
+- */
+-static void list_sort(void *priv, struct list_head *head,
+- int (*cmp)(void *priv, struct list_head *a,
+- struct list_head *b))
+-{
+- struct list_head *p, *q, *e, *list, *tail, *oldhead;
+- int insize, nmerges, psize, qsize, i;
+-
+- if (list_empty(head))
+- return;
+-
+- list = head->next;
+- list_del(head);
+- insize = 1;
+- for (;;) {
+- p = oldhead = list;
+- list = tail = NULL;
+- nmerges = 0;
+-
+- while (p) {
+- nmerges++;
+- q = p;
+- psize = 0;
+- for (i = 0; i < insize; i++) {
+- psize++;
+- q = q->next == oldhead ? NULL : q->next;
+- if (!q)
+- break;
+- }
+-
+- qsize = insize;
+- while (psize > 0 || (qsize > 0 && q)) {
+- if (!psize) {
+- e = q;
+- q = q->next;
+- qsize--;
+- if (q == oldhead)
+- q = NULL;
+- } else if (!qsize || !q) {
+- e = p;
+- p = p->next;
+- psize--;
+- if (p == oldhead)
+- p = NULL;
+- } else if (cmp(priv, p, q) <= 0) {
+- e = p;
+- p = p->next;
+- psize--;
+- if (p == oldhead)
+- p = NULL;
+- } else {
+- e = q;
+- q = q->next;
+- qsize--;
+- if (q == oldhead)
+- q = NULL;
+- }
+- if (tail)
+- tail->next = e;
+- else
+- list = e;
+- e->prev = tail;
+- tail = e;
+- }
+- p = q;
+- }
+-
+- tail->next = list;
+- list->prev = tail;
+-
+- if (nmerges <= 1)
+- break;
+-
+- insize *= 2;
+- }
+-
+- head->next = list;
+- head->prev = list->prev;
+- list->prev->next = head;
+- list->prev = head;
+-}
+-
+-/**
+ * data_nodes_cmp - compare 2 data nodes.
+ * @priv: UBIFS file-system description object
+ * @a: first data node
+diff --git a/include/drm/Kbuild b/include/drm/Kbuild
+index b940fdf..bd3a1c2 100644
+--- a/include/drm/Kbuild
++++ b/include/drm/Kbuild
+@@ -7,4 +7,6 @@ unifdef-y += r128_drm.h
+ unifdef-y += radeon_drm.h
+ unifdef-y += sis_drm.h
+ unifdef-y += savage_drm.h
++unifdef-y += vmwgfx_drm.h
+ unifdef-y += via_drm.h
++unifdef-y += nouveau_drm.h
+diff --git a/include/drm/drm.h b/include/drm/drm.h
+index 7cb50bd..e3f46e0 100644
+--- a/include/drm/drm.h
++++ b/include/drm/drm.h
+@@ -36,17 +36,27 @@
+ #ifndef _DRM_H_
+ #define _DRM_H_
+
++#if defined(__linux__)
++
+ #include <linux/types.h>
+-#include <asm/ioctl.h> /* For _IO* macros */
+-#define DRM_IOCTL_NR(n) _IOC_NR(n)
+-#define DRM_IOC_VOID _IOC_NONE
+-#define DRM_IOC_READ _IOC_READ
+-#define DRM_IOC_WRITE _IOC_WRITE
+-#define DRM_IOC_READWRITE _IOC_READ|_IOC_WRITE
+-#define DRM_IOC(dir, group, nr, size) _IOC(dir, group, nr, size)
++#include <asm/ioctl.h>
++typedef unsigned int drm_handle_t;
+
+-#define DRM_MAJOR 226
+-#define DRM_MAX_MINOR 15
++#else /* One of the BSDs */
++
++#include <sys/ioccom.h>
++#include <sys/types.h>
++typedef int8_t __s8;
++typedef uint8_t __u8;
++typedef int16_t __s16;
++typedef uint16_t __u16;
++typedef int32_t __s32;
++typedef uint32_t __u32;
++typedef int64_t __s64;
++typedef uint64_t __u64;
++typedef unsigned long drm_handle_t;
++
++#endif
+
+ #define DRM_NAME "drm" /**< Name in kernel, /dev, and /proc */
+ #define DRM_MIN_ORDER 5 /**< At least 2^5 bytes = 32 bytes */
+@@ -59,7 +69,6 @@
+ #define _DRM_LOCK_IS_CONT(lock) ((lock) & _DRM_LOCK_CONT)
+ #define _DRM_LOCKING_CONTEXT(lock) ((lock) & ~(_DRM_LOCK_HELD|_DRM_LOCK_CONT))
+
+-typedef unsigned int drm_handle_t;
+ typedef unsigned int drm_context_t;
+ typedef unsigned int drm_drawable_t;
+ typedef unsigned int drm_magic_t;
+@@ -454,6 +463,7 @@ struct drm_irq_busid {
+ enum drm_vblank_seq_type {
+ _DRM_VBLANK_ABSOLUTE = 0x0, /**< Wait for specific vblank sequence number */
+ _DRM_VBLANK_RELATIVE = 0x1, /**< Wait for given number of vblanks */
++ _DRM_VBLANK_EVENT = 0x4000000, /**< Send event instead of blocking */
+ _DRM_VBLANK_FLIP = 0x8000000, /**< Scheduled buffer swap should flip */
+ _DRM_VBLANK_NEXTONMISS = 0x10000000, /**< If missed, wait for next vblank */
+ _DRM_VBLANK_SECONDARY = 0x20000000, /**< Secondary display controller */
+@@ -461,8 +471,8 @@ enum drm_vblank_seq_type {
+ };
+
+ #define _DRM_VBLANK_TYPES_MASK (_DRM_VBLANK_ABSOLUTE | _DRM_VBLANK_RELATIVE)
+-#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_SIGNAL | _DRM_VBLANK_SECONDARY | \
+- _DRM_VBLANK_NEXTONMISS)
++#define _DRM_VBLANK_FLAGS_MASK (_DRM_VBLANK_EVENT | _DRM_VBLANK_SIGNAL | \
++ _DRM_VBLANK_SECONDARY | _DRM_VBLANK_NEXTONMISS)
+
+ struct drm_wait_vblank_request {
+ enum drm_vblank_seq_type type;
+@@ -686,6 +696,8 @@ struct drm_gem_open {
+ #define DRM_IOCTL_MODE_GETFB DRM_IOWR(0xAD, struct drm_mode_fb_cmd)
+ #define DRM_IOCTL_MODE_ADDFB DRM_IOWR(0xAE, struct drm_mode_fb_cmd)
+ #define DRM_IOCTL_MODE_RMFB DRM_IOWR(0xAF, unsigned int)
++#define DRM_IOCTL_MODE_PAGE_FLIP DRM_IOWR(0xB0, struct drm_mode_crtc_page_flip)
++#define DRM_IOCTL_MODE_DIRTYFB DRM_IOWR(0xB1, struct drm_mode_fb_dirty_cmd)
+
+ /**
+ * Device specific ioctls should only be in their respective headers
+@@ -698,6 +710,35 @@ struct drm_gem_open {
+ #define DRM_COMMAND_BASE 0x40
+ #define DRM_COMMAND_END 0xA0
+
++/**
++ * Header for events written back to userspace on the drm fd. The
++ * type defines the type of event, the length specifies the total
++ * length of the event (including the header), and user_data is
++ * typically a 64 bit value passed with the ioctl that triggered the
++ * event. A read on the drm fd will always only return complete
++ * events, that is, if for example the read buffer is 100 bytes, and
++ * there are two 64 byte events pending, only one will be returned.
++ *
++ * Event types 0 - 0x7fffffff are generic drm events, 0x80000000 and
++ * up are chipset specific.
++ */
++struct drm_event {
++ __u32 type;
++ __u32 length;
++};
++
++#define DRM_EVENT_VBLANK 0x01
++#define DRM_EVENT_FLIP_COMPLETE 0x02
++
++struct drm_event_vblank {
++ struct drm_event base;
++ __u64 user_data;
++ __u32 tv_sec;
++ __u32 tv_usec;
++ __u32 sequence;
++ __u32 reserved;
++};
++
+ /* typedef area */
+ #ifndef __KERNEL__
+ typedef struct drm_clip_rect drm_clip_rect_t;
+diff --git a/include/drm/drmP.h b/include/drm/drmP.h
+index 7ad3faa..ffac157 100644
+--- a/include/drm/drmP.h
++++ b/include/drm/drmP.h
+@@ -245,16 +245,6 @@ extern void drm_ut_debug_printk(unsigned int request_level,
+
+ #endif
+
+-#define DRM_PROC_LIMIT (PAGE_SIZE-80)
+-
+-#define DRM_PROC_PRINT(fmt, arg...) \
+- len += sprintf(&buf[len], fmt , ##arg); \
+- if (len > DRM_PROC_LIMIT) { *eof = 1; return len - offset; }
+-
+-#define DRM_PROC_PRINT_RET(ret, fmt, arg...) \
+- len += sprintf(&buf[len], fmt , ##arg); \
+- if (len > DRM_PROC_LIMIT) { ret; *eof = 1; return len - offset; }
+-
+ /*@}*/
+
+ /***********************************************************************/
+@@ -265,19 +255,8 @@ extern void drm_ut_debug_printk(unsigned int request_level,
+
+ #define DRM_LEFTCOUNT(x) (((x)->rp + (x)->count - (x)->wp) % ((x)->count + 1))
+ #define DRM_BUFCOUNT(x) ((x)->count - DRM_LEFTCOUNT(x))
+-#define DRM_WAITCOUNT(dev,idx) DRM_BUFCOUNT(&dev->queuelist[idx]->waitlist)
+
+ #define DRM_IF_VERSION(maj, min) (maj << 16 | min)
+-/**
+- * Get the private SAREA mapping.
+- *
+- * \param _dev DRM device.
+- * \param _ctx context number.
+- * \param _map output mapping.
+- */
+-#define DRM_GET_PRIV_SAREA(_dev, _ctx, _map) do { \
+- (_map) = (_dev)->context_sareas[_ctx]; \
+-} while(0)
+
+ /**
+ * Test that the hardware lock is held by the caller, returning otherwise.
+@@ -297,18 +276,6 @@ do { \
+ } while (0)
+
+ /**
+- * Copy and IOCTL return string to user space
+- */
+-#define DRM_COPY( name, value ) \
+- len = strlen( value ); \
+- if ( len > name##_len ) len = name##_len; \
+- name##_len = strlen( value ); \
+- if ( len && name ) { \
+- if ( copy_to_user( name, value, len ) ) \
+- return -EFAULT; \
+- }
+-
+-/**
+ * Ioctl function type.
+ *
+ * \param inode device inode.
+@@ -322,10 +289,14 @@ typedef int drm_ioctl_t(struct drm_device *dev, void *data,
+ typedef int drm_ioctl_compat_t(struct file *filp, unsigned int cmd,
+ unsigned long arg);
+
++#define DRM_IOCTL_NR(n) _IOC_NR(n)
++#define DRM_MAJOR 226
++
+ #define DRM_AUTH 0x1
+ #define DRM_MASTER 0x2
+ #define DRM_ROOT_ONLY 0x4
+ #define DRM_CONTROL_ALLOW 0x8
++#define DRM_UNLOCKED 0x10
+
+ struct drm_ioctl_desc {
+ unsigned int cmd;
+@@ -426,6 +397,14 @@ struct drm_buf_entry {
+ struct drm_freelist freelist;
+ };
+
++/* Event queued up for userspace to read */
++struct drm_pending_event {
++ struct drm_event *event;
++ struct list_head link;
++ struct drm_file *file_priv;
++ void (*destroy)(struct drm_pending_event *event);
++};
++
+ /** File private data */
+ struct drm_file {
+ int authenticated;
+@@ -449,6 +428,10 @@ struct drm_file {
+ struct drm_master *master; /* master this node is currently associated with
+ N.B. not always minor->master */
+ struct list_head fbs;
++
++ wait_queue_head_t event_wait;
++ struct list_head event_list;
++ int event_space;
+ };
+
+ /** Wait queue */
+@@ -795,6 +778,15 @@ struct drm_driver {
+ /* Master routines */
+ int (*master_create)(struct drm_device *dev, struct drm_master *master);
+ void (*master_destroy)(struct drm_device *dev, struct drm_master *master);
++ /**
++ * master_set is called whenever the minor master is set.
++ * master_drop is called whenever the minor master is dropped.
++ */
++
++ int (*master_set)(struct drm_device *dev, struct drm_file *file_priv,
++ bool from_open);
++ void (*master_drop)(struct drm_device *dev, struct drm_file *file_priv,
++ bool from_release);
+
+ int (*proc_init)(struct drm_minor *minor);
+ void (*proc_cleanup)(struct drm_minor *minor);
+@@ -900,6 +892,12 @@ struct drm_minor {
+ struct drm_mode_group mode_group;
+ };
+
++struct drm_pending_vblank_event {
++ struct drm_pending_event base;
++ int pipe;
++ struct drm_event_vblank event;
++};
++
+ /**
+ * DRM device structure. This structure represent a complete card that
+ * may contain multiple heads.
+@@ -999,6 +997,12 @@ struct drm_device {
+
+ u32 max_vblank_count; /**< size of vblank counter register */
+
++ /**
++ * List of events
++ */
++ struct list_head vblank_event_list;
++ spinlock_t event_lock;
++
+ /*@} */
+ cycles_t ctx_start;
+ cycles_t lck_start;
+@@ -1125,8 +1129,8 @@ static inline int drm_mtrr_del(int handle, unsigned long offset,
+ /* Driver support (drm_drv.h) */
+ extern int drm_init(struct drm_driver *driver);
+ extern void drm_exit(struct drm_driver *driver);
+-extern int drm_ioctl(struct inode *inode, struct file *filp,
+- unsigned int cmd, unsigned long arg);
++extern long drm_ioctl(struct file *filp,
++ unsigned int cmd, unsigned long arg);
+ extern long drm_compat_ioctl(struct file *filp,
+ unsigned int cmd, unsigned long arg);
+ extern int drm_lastclose(struct drm_device *dev);
+@@ -1135,6 +1139,8 @@ extern int drm_lastclose(struct drm_device *dev);
+ extern int drm_open(struct inode *inode, struct file *filp);
+ extern int drm_stub_open(struct inode *inode, struct file *filp);
+ extern int drm_fasync(int fd, struct file *filp, int on);
++extern ssize_t drm_read(struct file *filp, char __user *buffer,
++ size_t count, loff_t *offset);
+ extern int drm_release(struct inode *inode, struct file *filp);
+
+ /* Mapping support (drm_vm.h) */
+@@ -1520,14 +1526,27 @@ static __inline__ void drm_core_dropmap(struct drm_local_map *map)
+
+ static __inline__ void *drm_calloc_large(size_t nmemb, size_t size)
+ {
++ if (size != 0 && nmemb > ULONG_MAX / size)
++ return NULL;
++
+ if (size * nmemb <= PAGE_SIZE)
+ return kcalloc(nmemb, size, GFP_KERNEL);
+
++ return __vmalloc(size * nmemb,
++ GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, PAGE_KERNEL);
++}
++
++/* Modeled after cairo's malloc_ab, it's like calloc but without the zeroing. */
++static __inline__ void *drm_malloc_ab(size_t nmemb, size_t size)
++{
+ if (size != 0 && nmemb > ULONG_MAX / size)
+ return NULL;
+
++ if (size * nmemb <= PAGE_SIZE)
++ return kmalloc(nmemb * size, GFP_KERNEL);
++
+ return __vmalloc(size * nmemb,
+- GFP_KERNEL | __GFP_HIGHMEM | __GFP_ZERO, PAGE_KERNEL);
++ GFP_KERNEL | __GFP_HIGHMEM, PAGE_KERNEL);
+ }
+
+ static __inline void drm_free_large(void *ptr)
+diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h
+index b69347b..1347524 100644
+--- a/include/drm/drm_crtc.h
++++ b/include/drm/drm_crtc.h
+@@ -123,7 +123,7 @@ struct drm_display_mode {
+ int type;
+
+ /* Proposed mode values */
+- int clock;
++ int clock; /* in kHz */
+ int hdisplay;
+ int hsync_start;
+ int hsync_end;
+@@ -164,8 +164,8 @@ struct drm_display_mode {
+ int *private;
+ int private_flags;
+
+- int vrefresh;
+- float hsync;
++ int vrefresh; /* in Hz */
++ int hsync; /* in kHz */
+ };
+
+ enum drm_connector_status {
+@@ -242,6 +242,21 @@ struct drm_framebuffer_funcs {
+ int (*create_handle)(struct drm_framebuffer *fb,
+ struct drm_file *file_priv,
+ unsigned int *handle);
++ /**
++ * Optinal callback for the dirty fb ioctl.
++ *
++ * Userspace can notify the driver via this callback
++ * that a area of the framebuffer has changed and should
++ * be flushed to the display hardware.
++ *
++ * See documentation in drm_mode.h for the struct
++ * drm_mode_fb_dirty_cmd for more information as all
++ * the semantics and arguments have a one to one mapping
++ * on this function.
++ */
++ int (*dirty)(struct drm_framebuffer *framebuffer, unsigned flags,
++ unsigned color, struct drm_clip_rect *clips,
++ unsigned num_clips);
+ };
+
+ struct drm_framebuffer {
+@@ -256,7 +271,7 @@ struct drm_framebuffer {
+ unsigned int depth;
+ int bits_per_pixel;
+ int flags;
+- void *fbdev;
++ struct fb_info *fbdev;
+ u32 pseudo_palette[17];
+ struct list_head filp_head;
+ /* if you are using the helper */
+@@ -290,6 +305,7 @@ struct drm_property {
+ struct drm_crtc;
+ struct drm_connector;
+ struct drm_encoder;
++struct drm_pending_vblank_event;
+
+ /**
+ * drm_crtc_funcs - control CRTCs for a given device
+@@ -333,6 +349,19 @@ struct drm_crtc_funcs {
+ void (*destroy)(struct drm_crtc *crtc);
+
+ int (*set_config)(struct drm_mode_set *set);
++
++ /*
++ * Flip to the given framebuffer. This implements the page
++ * flip ioctl descibed in drm_mode.h, specifically, the
++ * implementation must return immediately and block all
++ * rendering to the current fb until the flip has completed.
++ * If userspace set the event flag in the ioctl, the event
++ * argument will point to an event to send back when the flip
++ * completes, otherwise it will be NULL.
++ */
++ int (*page_flip)(struct drm_crtc *crtc,
++ struct drm_framebuffer *fb,
++ struct drm_pending_vblank_event *event);
+ };
+
+ /**
+@@ -596,6 +625,7 @@ struct drm_mode_config {
+ /* Optional properties */
+ struct drm_property *scaling_mode_property;
+ struct drm_property *dithering_mode_property;
++ struct drm_property *dirty_info_property;
+ };
+
+ #define obj_to_crtc(x) container_of(x, struct drm_crtc, base)
+@@ -667,6 +697,7 @@ extern void drm_mode_validate_size(struct drm_device *dev,
+ extern void drm_mode_prune_invalid(struct drm_device *dev,
+ struct list_head *mode_list, bool verbose);
+ extern void drm_mode_sort(struct list_head *mode_list);
++extern int drm_mode_hsync(struct drm_display_mode *mode);
+ extern int drm_mode_vrefresh(struct drm_display_mode *mode);
+ extern void drm_mode_set_crtcinfo(struct drm_display_mode *p,
+ int adjust_flags);
+@@ -703,6 +734,7 @@ extern int drm_mode_create_tv_properties(struct drm_device *dev, int num_formats
+ char *formats[]);
+ extern int drm_mode_create_scaling_mode_property(struct drm_device *dev);
+ extern int drm_mode_create_dithering_property(struct drm_device *dev);
++extern int drm_mode_create_dirty_info_property(struct drm_device *dev);
+ extern char *drm_get_encoder_name(struct drm_encoder *encoder);
+
+ extern int drm_mode_connector_attach_encoder(struct drm_connector *connector,
+@@ -711,7 +743,8 @@ extern void drm_mode_connector_detach_encoder(struct drm_connector *connector,
+ struct drm_encoder *encoder);
+ extern bool drm_mode_crtc_set_gamma_size(struct drm_crtc *crtc,
+ int gamma_size);
+-extern void *drm_mode_object_find(struct drm_device *dev, uint32_t id, uint32_t type);
++extern struct drm_mode_object *drm_mode_object_find(struct drm_device *dev,
++ uint32_t id, uint32_t type);
+ /* IOCTLs */
+ extern int drm_mode_getresources(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+@@ -730,6 +763,8 @@ extern int drm_mode_rmfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+ extern int drm_mode_getfb(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
++extern int drm_mode_dirtyfb_ioctl(struct drm_device *dev,
++ void *data, struct drm_file *file_priv);
+ extern int drm_mode_addmode_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+ extern int drm_mode_rmmode_ioctl(struct drm_device *dev,
+@@ -756,6 +791,8 @@ extern int drm_mode_gamma_get_ioctl(struct drm_device *dev,
+ extern int drm_mode_gamma_set_ioctl(struct drm_device *dev,
+ void *data, struct drm_file *file_priv);
+ extern bool drm_detect_hdmi_monitor(struct edid *edid);
++extern int drm_mode_page_flip_ioctl(struct drm_device *dev,
++ void *data, struct drm_file *file_priv);
+ extern struct drm_display_mode *drm_cvt_mode(struct drm_device *dev,
+ int hdisplay, int vdisplay, int vrefresh,
+ bool reduced, bool interlaced, bool margins);
+@@ -764,4 +801,6 @@ extern struct drm_display_mode *drm_gtf_mode(struct drm_device *dev,
+ bool interlaced, int margins);
+ extern int drm_add_modes_noedid(struct drm_connector *connector,
+ int hdisplay, int vdisplay);
++
++extern bool drm_edid_is_valid(struct edid *edid);
+ #endif /* __DRM_CRTC_H__ */
+diff --git a/include/drm/drm_dp_helper.h b/include/drm/drm_dp_helper.h
+new file mode 100644
+index 0000000..a49e791
+--- /dev/null
++++ b/include/drm/drm_dp_helper.h
+@@ -0,0 +1,180 @@
++/*
++ * Copyright © 2008 Keith Packard
++ *
++ * Permission to use, copy, modify, distribute, and sell this software and its
++ * documentation for any purpose is hereby granted without fee, provided that
++ * the above copyright notice appear in all copies and that both that copyright
++ * notice and this permission notice appear in supporting documentation, and
++ * that the name of the copyright holders not be used in advertising or
++ * publicity pertaining to distribution of the software without specific,
++ * written prior permission. The copyright holders make no representations
++ * about the suitability of this software for any purpose. It is provided "as
++ * is" without express or implied warranty.
++ *
++ * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
++ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
++ * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
++ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
++ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
++ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
++ * OF THIS SOFTWARE.
++ */
++
++#ifndef _DRM_DP_HELPER_H_
++#define _DRM_DP_HELPER_H_
++
++/* From the VESA DisplayPort spec */
++
++#define AUX_NATIVE_WRITE 0x8
++#define AUX_NATIVE_READ 0x9
++#define AUX_I2C_WRITE 0x0
++#define AUX_I2C_READ 0x1
++#define AUX_I2C_STATUS 0x2
++#define AUX_I2C_MOT 0x4
++
++#define AUX_NATIVE_REPLY_ACK (0x0 << 4)
++#define AUX_NATIVE_REPLY_NACK (0x1 << 4)
++#define AUX_NATIVE_REPLY_DEFER (0x2 << 4)
++#define AUX_NATIVE_REPLY_MASK (0x3 << 4)
++
++#define AUX_I2C_REPLY_ACK (0x0 << 6)
++#define AUX_I2C_REPLY_NACK (0x1 << 6)
++#define AUX_I2C_REPLY_DEFER (0x2 << 6)
++#define AUX_I2C_REPLY_MASK (0x3 << 6)
++
++/* AUX CH addresses */
++/* DPCD */
++#define DP_DPCD_REV 0x000
++
++#define DP_MAX_LINK_RATE 0x001
++
++#define DP_MAX_LANE_COUNT 0x002
++# define DP_MAX_LANE_COUNT_MASK 0x1f
++# define DP_ENHANCED_FRAME_CAP (1 << 7)
++
++#define DP_MAX_DOWNSPREAD 0x003
++# define DP_NO_AUX_HANDSHAKE_LINK_TRAINING (1 << 6)
++
++#define DP_NORP 0x004
++
++#define DP_DOWNSTREAMPORT_PRESENT 0x005
++# define DP_DWN_STRM_PORT_PRESENT (1 << 0)
++# define DP_DWN_STRM_PORT_TYPE_MASK 0x06
++/* 00b = DisplayPort */
++/* 01b = Analog */
++/* 10b = TMDS or HDMI */
++/* 11b = Other */
++# define DP_FORMAT_CONVERSION (1 << 3)
++
++#define DP_MAIN_LINK_CHANNEL_CODING 0x006
++
++/* link configuration */
++#define DP_LINK_BW_SET 0x100
++# define DP_LINK_BW_1_62 0x06
++# define DP_LINK_BW_2_7 0x0a
++
++#define DP_LANE_COUNT_SET 0x101
++# define DP_LANE_COUNT_MASK 0x0f
++# define DP_LANE_COUNT_ENHANCED_FRAME_EN (1 << 7)
++
++#define DP_TRAINING_PATTERN_SET 0x102
++# define DP_TRAINING_PATTERN_DISABLE 0
++# define DP_TRAINING_PATTERN_1 1
++# define DP_TRAINING_PATTERN_2 2
++# define DP_TRAINING_PATTERN_MASK 0x3
++
++# define DP_LINK_QUAL_PATTERN_DISABLE (0 << 2)
++# define DP_LINK_QUAL_PATTERN_D10_2 (1 << 2)
++# define DP_LINK_QUAL_PATTERN_ERROR_RATE (2 << 2)
++# define DP_LINK_QUAL_PATTERN_PRBS7 (3 << 2)
++# define DP_LINK_QUAL_PATTERN_MASK (3 << 2)
++
++# define DP_RECOVERED_CLOCK_OUT_EN (1 << 4)
++# define DP_LINK_SCRAMBLING_DISABLE (1 << 5)
++
++# define DP_SYMBOL_ERROR_COUNT_BOTH (0 << 6)
++# define DP_SYMBOL_ERROR_COUNT_DISPARITY (1 << 6)
++# define DP_SYMBOL_ERROR_COUNT_SYMBOL (2 << 6)
++# define DP_SYMBOL_ERROR_COUNT_MASK (3 << 6)
++
++#define DP_TRAINING_LANE0_SET 0x103
++#define DP_TRAINING_LANE1_SET 0x104
++#define DP_TRAINING_LANE2_SET 0x105
++#define DP_TRAINING_LANE3_SET 0x106
++
++# define DP_TRAIN_VOLTAGE_SWING_MASK 0x3
++# define DP_TRAIN_VOLTAGE_SWING_SHIFT 0
++# define DP_TRAIN_MAX_SWING_REACHED (1 << 2)
++# define DP_TRAIN_VOLTAGE_SWING_400 (0 << 0)
++# define DP_TRAIN_VOLTAGE_SWING_600 (1 << 0)
++# define DP_TRAIN_VOLTAGE_SWING_800 (2 << 0)
++# define DP_TRAIN_VOLTAGE_SWING_1200 (3 << 0)
++
++# define DP_TRAIN_PRE_EMPHASIS_MASK (3 << 3)
++# define DP_TRAIN_PRE_EMPHASIS_0 (0 << 3)
++# define DP_TRAIN_PRE_EMPHASIS_3_5 (1 << 3)
++# define DP_TRAIN_PRE_EMPHASIS_6 (2 << 3)
++# define DP_TRAIN_PRE_EMPHASIS_9_5 (3 << 3)
++
++# define DP_TRAIN_PRE_EMPHASIS_SHIFT 3
++# define DP_TRAIN_MAX_PRE_EMPHASIS_REACHED (1 << 5)
++
++#define DP_DOWNSPREAD_CTRL 0x107
++# define DP_SPREAD_AMP_0_5 (1 << 4)
++
++#define DP_MAIN_LINK_CHANNEL_CODING_SET 0x108
++# define DP_SET_ANSI_8B10B (1 << 0)
++
++#define DP_LANE0_1_STATUS 0x202
++#define DP_LANE2_3_STATUS 0x203
++# define DP_LANE_CR_DONE (1 << 0)
++# define DP_LANE_CHANNEL_EQ_DONE (1 << 1)
++# define DP_LANE_SYMBOL_LOCKED (1 << 2)
++
++#define DP_CHANNEL_EQ_BITS (DP_LANE_CR_DONE | \
++ DP_LANE_CHANNEL_EQ_DONE | \
++ DP_LANE_SYMBOL_LOCKED)
++
++#define DP_LANE_ALIGN_STATUS_UPDATED 0x204
++
++#define DP_INTERLANE_ALIGN_DONE (1 << 0)
++#define DP_DOWNSTREAM_PORT_STATUS_CHANGED (1 << 6)
++#define DP_LINK_STATUS_UPDATED (1 << 7)
++
++#define DP_SINK_STATUS 0x205
++
++#define DP_RECEIVE_PORT_0_STATUS (1 << 0)
++#define DP_RECEIVE_PORT_1_STATUS (1 << 1)
++
++#define DP_ADJUST_REQUEST_LANE0_1 0x206
++#define DP_ADJUST_REQUEST_LANE2_3 0x207
++# define DP_ADJUST_VOLTAGE_SWING_LANE0_MASK 0x03
++# define DP_ADJUST_VOLTAGE_SWING_LANE0_SHIFT 0
++# define DP_ADJUST_PRE_EMPHASIS_LANE0_MASK 0x0c
++# define DP_ADJUST_PRE_EMPHASIS_LANE0_SHIFT 2
++# define DP_ADJUST_VOLTAGE_SWING_LANE1_MASK 0x30
++# define DP_ADJUST_VOLTAGE_SWING_LANE1_SHIFT 4
++# define DP_ADJUST_PRE_EMPHASIS_LANE1_MASK 0xc0
++# define DP_ADJUST_PRE_EMPHASIS_LANE1_SHIFT 6
++
++#define DP_SET_POWER 0x600
++# define DP_SET_POWER_D0 0x1
++# define DP_SET_POWER_D3 0x2
++
++#define MODE_I2C_START 1
++#define MODE_I2C_WRITE 2
++#define MODE_I2C_READ 4
++#define MODE_I2C_STOP 8
++
++struct i2c_algo_dp_aux_data {
++ bool running;
++ u16 address;
++ int (*aux_ch) (struct i2c_adapter *adapter,
++ int mode, uint8_t write_byte,
++ uint8_t *read_byte);
++};
++
++int
++i2c_dp_aux_add_bus(struct i2c_adapter *adapter);
++
++#endif /* _DRM_DP_HELPER_H_ */
+diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h
+index 7d6c9a2..b420989 100644
+--- a/include/drm/drm_edid.h
++++ b/include/drm/drm_edid.h
+@@ -106,6 +106,10 @@ struct detailed_data_color_point {
+ u8 wpindex2[3];
+ } __attribute__((packed));
+
++struct cvt_timing {
++ u8 code[3];
++} __attribute__((packed));
++
+ struct detailed_non_pixel {
+ u8 pad1;
+ u8 type; /* ff=serial, fe=string, fd=monitor range, fc=monitor name
+@@ -117,9 +121,13 @@ struct detailed_non_pixel {
+ struct detailed_data_monitor_range range;
+ struct detailed_data_wpindex color;
+ struct std_timing timings[5];
++ struct cvt_timing cvt[4];
+ } data;
+ } __attribute__((packed));
+
++#define EDID_DETAIL_EST_TIMINGS 0xf7
++#define EDID_DETAIL_CVT_3BYTE 0xf8
++#define EDID_DETAIL_COLOR_MGMT_DATA 0xf9
+ #define EDID_DETAIL_STD_MODES 0xfa
+ #define EDID_DETAIL_MONITOR_CPDATA 0xfb
+ #define EDID_DETAIL_MONITOR_NAME 0xfc
+@@ -193,4 +201,7 @@ struct edid {
+
+ #define EDID_PRODUCT_ID(e) ((e)->prod_code[0] | ((e)->prod_code[1] << 8))
+
++/* define the number of Extension EDID block */
++#define DRM_MAX_EDID_EXT_NUM 4
++
+ #endif /* __DRM_EDID_H__ */
+diff --git a/include/drm/drm_mm.h b/include/drm/drm_mm.h
+index 62329f9..4c10be3 100644
+--- a/include/drm/drm_mm.h
++++ b/include/drm/drm_mm.h
+@@ -66,6 +66,13 @@ extern struct drm_mm_node *drm_mm_get_block_generic(struct drm_mm_node *node,
+ unsigned long size,
+ unsigned alignment,
+ int atomic);
++extern struct drm_mm_node *drm_mm_get_block_range_generic(
++ struct drm_mm_node *node,
++ unsigned long size,
++ unsigned alignment,
++ unsigned long start,
++ unsigned long end,
++ int atomic);
+ static inline struct drm_mm_node *drm_mm_get_block(struct drm_mm_node *parent,
+ unsigned long size,
+ unsigned alignment)
+@@ -78,11 +85,38 @@ static inline struct drm_mm_node *drm_mm_get_block_atomic(struct drm_mm_node *pa
+ {
+ return drm_mm_get_block_generic(parent, size, alignment, 1);
+ }
++static inline struct drm_mm_node *drm_mm_get_block_range(
++ struct drm_mm_node *parent,
++ unsigned long size,
++ unsigned alignment,
++ unsigned long start,
++ unsigned long end)
++{
++ return drm_mm_get_block_range_generic(parent, size, alignment,
++ start, end, 0);
++}
++static inline struct drm_mm_node *drm_mm_get_block_atomic_range(
++ struct drm_mm_node *parent,
++ unsigned long size,
++ unsigned alignment,
++ unsigned long start,
++ unsigned long end)
++{
++ return drm_mm_get_block_range_generic(parent, size, alignment,
++ start, end, 1);
++}
+ extern void drm_mm_put_block(struct drm_mm_node *cur);
+ extern struct drm_mm_node *drm_mm_search_free(const struct drm_mm *mm,
+ unsigned long size,
+ unsigned alignment,
+ int best_match);
++extern struct drm_mm_node *drm_mm_search_free_in_range(
++ const struct drm_mm *mm,
++ unsigned long size,
++ unsigned alignment,
++ unsigned long start,
++ unsigned long end,
++ int best_match);
+ extern int drm_mm_init(struct drm_mm *mm, unsigned long start,
+ unsigned long size);
+ extern void drm_mm_takedown(struct drm_mm *mm);
+@@ -99,6 +133,7 @@ static inline struct drm_mm *drm_get_mm(struct drm_mm_node *block)
+ return block->mm;
+ }
+
++extern void drm_mm_debug_table(struct drm_mm *mm, const char *prefix);
+ #ifdef CONFIG_DEBUG_FS
+ int drm_mm_dump_table(struct seq_file *m, struct drm_mm *mm);
+ #endif
+diff --git a/include/drm/drm_mode.h b/include/drm/drm_mode.h
+index 1f90841..c5ba163 100644
+--- a/include/drm/drm_mode.h
++++ b/include/drm/drm_mode.h
+@@ -27,9 +27,6 @@
+ #ifndef _DRM_MODE_H
+ #define _DRM_MODE_H
+
+-#include <linux/kernel.h>
+-#include <linux/types.h>
+-
+ #define DRM_DISPLAY_INFO_LEN 32
+ #define DRM_CONNECTOR_NAME_LEN 32
+ #define DRM_DISPLAY_MODE_LEN 32
+@@ -78,12 +75,17 @@
+ #define DRM_MODE_DITHERING_OFF 0
+ #define DRM_MODE_DITHERING_ON 1
+
++/* Dirty info options */
++#define DRM_MODE_DIRTY_OFF 0
++#define DRM_MODE_DIRTY_ON 1
++#define DRM_MODE_DIRTY_ANNOTATE 2
++
+ struct drm_mode_modeinfo {
+ __u32 clock;
+ __u16 hdisplay, hsync_start, hsync_end, htotal, hskew;
+ __u16 vdisplay, vsync_start, vsync_end, vtotal, vscan;
+
+- __u32 vrefresh; /* vertical refresh * 1000 */
++ __u32 vrefresh;
+
+ __u32 flags;
+ __u32 type;
+@@ -158,6 +160,7 @@ struct drm_mode_get_encoder {
+ #define DRM_MODE_CONNECTOR_HDMIA 11
+ #define DRM_MODE_CONNECTOR_HDMIB 12
+ #define DRM_MODE_CONNECTOR_TV 13
++#define DRM_MODE_CONNECTOR_eDP 14
+
+ struct drm_mode_get_connector {
+
+@@ -225,6 +228,45 @@ struct drm_mode_fb_cmd {
+ __u32 handle;
+ };
+
++#define DRM_MODE_FB_DIRTY_ANNOTATE_COPY 0x01
++#define DRM_MODE_FB_DIRTY_ANNOTATE_FILL 0x02
++#define DRM_MODE_FB_DIRTY_FLAGS 0x03
++
++/*
++ * Mark a region of a framebuffer as dirty.
++ *
++ * Some hardware does not automatically update display contents
++ * as a hardware or software draw to a framebuffer. This ioctl
++ * allows userspace to tell the kernel and the hardware what
++ * regions of the framebuffer have changed.
++ *
++ * The kernel or hardware is free to update more then just the
++ * region specified by the clip rects. The kernel or hardware
++ * may also delay and/or coalesce several calls to dirty into a
++ * single update.
++ *
++ * Userspace may annotate the updates, the annotates are a
++ * promise made by the caller that the change is either a copy
++ * of pixels or a fill of a single color in the region specified.
++ *
++ * If the DRM_MODE_FB_DIRTY_ANNOTATE_COPY flag is given then
++ * the number of updated regions are half of num_clips given,
++ * where the clip rects are paired in src and dst. The width and
++ * height of each one of the pairs must match.
++ *
++ * If the DRM_MODE_FB_DIRTY_ANNOTATE_FILL flag is given the caller
++ * promises that the region specified of the clip rects is filled
++ * completely with a single color as given in the color argument.
++ */
++
++struct drm_mode_fb_dirty_cmd {
++ __u32 fb_id;
++ __u32 flags;
++ __u32 color;
++ __u32 num_clips;
++ __u64 clips_ptr;
++};
++
+ struct drm_mode_mode_cmd {
+ __u32 connector_id;
+ struct drm_mode_modeinfo mode;
+@@ -268,4 +310,37 @@ struct drm_mode_crtc_lut {
+ __u64 blue;
+ };
+
++#define DRM_MODE_PAGE_FLIP_EVENT 0x01
++#define DRM_MODE_PAGE_FLIP_FLAGS DRM_MODE_PAGE_FLIP_EVENT
++
++/*
++ * Request a page flip on the specified crtc.
++ *
++ * This ioctl will ask KMS to schedule a page flip for the specified
++ * crtc. Once any pending rendering targeting the specified fb (as of
++ * ioctl time) has completed, the crtc will be reprogrammed to display
++ * that fb after the next vertical refresh. The ioctl returns
++ * immediately, but subsequent rendering to the current fb will block
++ * in the execbuffer ioctl until the page flip happens. If a page
++ * flip is already pending as the ioctl is called, EBUSY will be
++ * returned.
++ *
++ * The ioctl supports one flag, DRM_MODE_PAGE_FLIP_EVENT, which will
++ * request that drm sends back a vblank event (see drm.h: struct
++ * drm_event_vblank) when the page flip is done. The user_data field
++ * passed in with this ioctl will be returned as the user_data field
++ * in the vblank event struct.
++ *
++ * The reserved field must be zero until we figure out something
++ * clever to use it for.
++ */
++
++struct drm_mode_crtc_page_flip {
++ __u32 crtc_id;
++ __u32 fb_id;
++ __u32 flags;
++ __u32 reserved;
++ __u64 user_data;
++};
++
+ #endif
+diff --git a/include/drm/drm_os_linux.h b/include/drm/drm_os_linux.h
+index 26641e9..3933691 100644
+--- a/include/drm/drm_os_linux.h
++++ b/include/drm/drm_os_linux.h
+@@ -123,5 +123,5 @@ do { \
+ remove_wait_queue(&(queue), &entry); \
+ } while (0)
+
+-#define DRM_WAKEUP( queue ) wake_up_interruptible( queue )
++#define DRM_WAKEUP( queue ) wake_up( queue )
+ #define DRM_INIT_WAITQUEUE( queue ) init_waitqueue_head( queue )
+diff --git a/include/drm/i2c/ch7006.h b/include/drm/i2c/ch7006.h
+new file mode 100644
+index 0000000..8390b43
+--- /dev/null
++++ b/include/drm/i2c/ch7006.h
+@@ -0,0 +1,86 @@
++/*
++ * Copyright (C) 2009 Francisco Jerez.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining
++ * a copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sublicense, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial
++ * portions of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
++ * IN NO EVENT SHALL THE COPYRIGHT OWNER(S) AND/OR ITS SUPPLIERS BE
++ * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
++ * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ */
++
++#ifndef __DRM_I2C_CH7006_H__
++#define __DRM_I2C_CH7006_H__
++
++/**
++ * struct ch7006_encoder_params
++ *
++ * Describes how the ch7006 is wired up with the GPU. It should be
++ * used as the @params parameter of its @set_config method.
++ *
++ * See "http://www.chrontel.com/pdf/7006.pdf" for their precise
++ * meaning.
++ */
++struct ch7006_encoder_params {
++ enum {
++ CH7006_FORMAT_RGB16 = 0,
++ CH7006_FORMAT_YCrCb24m16,
++ CH7006_FORMAT_RGB24m16,
++ CH7006_FORMAT_RGB15,
++ CH7006_FORMAT_RGB24m12C,
++ CH7006_FORMAT_RGB24m12I,
++ CH7006_FORMAT_RGB24m8,
++ CH7006_FORMAT_RGB16m8,
++ CH7006_FORMAT_RGB15m8,
++ CH7006_FORMAT_YCrCb24m8,
++ } input_format;
++
++ enum {
++ CH7006_CLOCK_SLAVE = 0,
++ CH7006_CLOCK_MASTER,
++ } clock_mode;
++
++ enum {
++ CH7006_CLOCK_EDGE_NEG = 0,
++ CH7006_CLOCK_EDGE_POS,
++ } clock_edge;
++
++ int xcm, pcm;
++
++ enum {
++ CH7006_SYNC_SLAVE = 0,
++ CH7006_SYNC_MASTER,
++ } sync_direction;
++
++ enum {
++ CH7006_SYNC_SEPARATED = 0,
++ CH7006_SYNC_EMBEDDED,
++ } sync_encoding;
++
++ enum {
++ CH7006_POUT_1_8V = 0,
++ CH7006_POUT_3_3V,
++ } pout_level;
++
++ enum {
++ CH7006_ACTIVE_HSYNC = 0,
++ CH7006_ACTIVE_DSTART,
++ } active_detect;
++};
++
++#endif
+diff --git a/include/drm/i915_drm.h b/include/drm/i915_drm.h
+index 7e0cb1d..b64a8d7 100644
+--- a/include/drm/i915_drm.h
++++ b/include/drm/i915_drm.h
+@@ -27,11 +27,11 @@
+ #ifndef _I915_DRM_H_
+ #define _I915_DRM_H_
+
++#include "drm.h"
++
+ /* Please note that modifications to all structs defined here are
+ * subject to backwards-compatibility constraints.
+ */
+-#include <linux/types.h>
+-#include "drm.h"
+
+ /* Each region is a minimum of 16k, and there are at most 255 of them.
+ */
+@@ -186,6 +186,9 @@ typedef struct _drm_i915_sarea {
+ #define DRM_I915_GEM_MMAP_GTT 0x24
+ #define DRM_I915_GET_PIPE_FROM_CRTC_ID 0x25
+ #define DRM_I915_GEM_MADVISE 0x26
++#define DRM_I915_OVERLAY_PUT_IMAGE 0x27
++#define DRM_I915_OVERLAY_ATTRS 0x28
++#define DRM_I915_GEM_EXECBUFFER2 0x29
+
+ #define DRM_IOCTL_I915_INIT DRM_IOW( DRM_COMMAND_BASE + DRM_I915_INIT, drm_i915_init_t)
+ #define DRM_IOCTL_I915_FLUSH DRM_IO ( DRM_COMMAND_BASE + DRM_I915_FLUSH)
+@@ -205,6 +208,7 @@ typedef struct _drm_i915_sarea {
+ #define DRM_IOCTL_I915_VBLANK_SWAP DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_VBLANK_SWAP, drm_i915_vblank_swap_t)
+ #define DRM_IOCTL_I915_GEM_INIT DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_INIT, struct drm_i915_gem_init)
+ #define DRM_IOCTL_I915_GEM_EXECBUFFER DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER, struct drm_i915_gem_execbuffer)
++#define DRM_IOCTL_I915_GEM_EXECBUFFER2 DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_EXECBUFFER2, struct drm_i915_gem_execbuffer2)
+ #define DRM_IOCTL_I915_GEM_PIN DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_PIN, struct drm_i915_gem_pin)
+ #define DRM_IOCTL_I915_GEM_UNPIN DRM_IOW(DRM_COMMAND_BASE + DRM_I915_GEM_UNPIN, struct drm_i915_gem_unpin)
+ #define DRM_IOCTL_I915_GEM_BUSY DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_BUSY, struct drm_i915_gem_busy)
+@@ -221,8 +225,10 @@ typedef struct _drm_i915_sarea {
+ #define DRM_IOCTL_I915_GEM_SET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_SET_TILING, struct drm_i915_gem_set_tiling)
+ #define DRM_IOCTL_I915_GEM_GET_TILING DRM_IOWR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_TILING, struct drm_i915_gem_get_tiling)
+ #define DRM_IOCTL_I915_GEM_GET_APERTURE DRM_IOR (DRM_COMMAND_BASE + DRM_I915_GEM_GET_APERTURE, struct drm_i915_gem_get_aperture)
+-#define DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_PIPE_FROM_CRTC_ID, struct drm_intel_get_pipe_from_crtc_id)
++#define DRM_IOCTL_I915_GET_PIPE_FROM_CRTC_ID DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GET_PIPE_FROM_CRTC_ID, struct drm_i915_get_pipe_from_crtc_id)
+ #define DRM_IOCTL_I915_GEM_MADVISE DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_GEM_MADVISE, struct drm_i915_gem_madvise)
++#define DRM_IOCTL_I915_OVERLAY_PUT_IMAGE DRM_IOW(DRM_COMMAND_BASE + DRM_IOCTL_I915_OVERLAY_ATTRS, struct drm_intel_overlay_put_image)
++#define DRM_IOCTL_I915_OVERLAY_ATTRS DRM_IOWR(DRM_COMMAND_BASE + DRM_I915_OVERLAY_ATTRS, struct drm_intel_overlay_attrs)
+
+ /* Allow drivers to submit batchbuffers directly to hardware, relying
+ * on the security mechanisms provided by hardware.
+@@ -266,6 +272,9 @@ typedef struct drm_i915_irq_wait {
+ #define I915_PARAM_CHIPSET_ID 4
+ #define I915_PARAM_HAS_GEM 5
+ #define I915_PARAM_NUM_FENCES_AVAIL 6
++#define I915_PARAM_HAS_OVERLAY 7
++#define I915_PARAM_HAS_PAGEFLIPPING 8
++#define I915_PARAM_HAS_EXECBUF2 9
+
+ typedef struct drm_i915_getparam {
+ int param;
+@@ -561,6 +570,57 @@ struct drm_i915_gem_execbuffer {
+ __u64 cliprects_ptr;
+ };
+
++struct drm_i915_gem_exec_object2 {
++ /**
++ * User's handle for a buffer to be bound into the GTT for this
++ * operation.
++ */
++ __u32 handle;
++
++ /** Number of relocations to be performed on this buffer */
++ __u32 relocation_count;
++ /**
++ * Pointer to array of struct drm_i915_gem_relocation_entry containing
++ * the relocations to be performed in this buffer.
++ */
++ __u64 relocs_ptr;
++
++ /** Required alignment in graphics aperture */
++ __u64 alignment;
++
++ /**
++ * Returned value of the updated offset of the object, for future
++ * presumed_offset writes.
++ */
++ __u64 offset;
++
++#define EXEC_OBJECT_NEEDS_FENCE (1<<0)
++ __u64 flags;
++ __u64 rsvd1;
++ __u64 rsvd2;
++};
++
++struct drm_i915_gem_execbuffer2 {
++ /**
++ * List of gem_exec_object2 structs
++ */
++ __u64 buffers_ptr;
++ __u32 buffer_count;
++
++ /** Offset in the batchbuffer to start execution from. */
++ __u32 batch_start_offset;
++ /** Bytes used in batchbuffer from batch_start_offset */
++ __u32 batch_len;
++ __u32 DR1;
++ __u32 DR4;
++ __u32 num_cliprects;
++ /** This is a struct drm_clip_rect *cliprects */
++ __u64 cliprects_ptr;
++ __u64 flags; /* currently unused */
++ __u64 rsvd1;
++ __u64 rsvd2;
++};
++
+ struct drm_i915_gem_pin {
+ /** Handle of the buffer to be pinned. */
+ __u32 handle;
+@@ -686,4 +746,70 @@ struct drm_i915_gem_madvise {
+ __u32 retained;
+ };
+
++/* flags */
++#define I915_OVERLAY_TYPE_MASK 0xff
++#define I915_OVERLAY_YUV_PLANAR 0x01
++#define I915_OVERLAY_YUV_PACKED 0x02
++#define I915_OVERLAY_RGB 0x03
++
++#define I915_OVERLAY_DEPTH_MASK 0xff00
++#define I915_OVERLAY_RGB24 0x1000
++#define I915_OVERLAY_RGB16 0x2000
++#define I915_OVERLAY_RGB15 0x3000
++#define I915_OVERLAY_YUV422 0x0100
++#define I915_OVERLAY_YUV411 0x0200
++#define I915_OVERLAY_YUV420 0x0300
++#define I915_OVERLAY_YUV410 0x0400
++
++#define I915_OVERLAY_SWAP_MASK 0xff0000
++#define I915_OVERLAY_NO_SWAP 0x000000
++#define I915_OVERLAY_UV_SWAP 0x010000
++#define I915_OVERLAY_Y_SWAP 0x020000
++#define I915_OVERLAY_Y_AND_UV_SWAP 0x030000
++
++#define I915_OVERLAY_FLAGS_MASK 0xff000000
++#define I915_OVERLAY_ENABLE 0x01000000
++
++struct drm_intel_overlay_put_image {
++ /* various flags and src format description */
++ __u32 flags;
++ /* source picture description */
++ __u32 bo_handle;
++ /* stride values and offsets are in bytes, buffer relative */
++ __u16 stride_Y; /* stride for packed formats */
++ __u16 stride_UV;
++ __u32 offset_Y; /* offset for packet formats */
++ __u32 offset_U;
++ __u32 offset_V;
++ /* in pixels */
++ __u16 src_width;
++ __u16 src_height;
++ /* to compensate the scaling factors for partially covered surfaces */
++ __u16 src_scan_width;
++ __u16 src_scan_height;
++ /* output crtc description */
++ __u32 crtc_id;
++ __u16 dst_x;
++ __u16 dst_y;
++ __u16 dst_width;
++ __u16 dst_height;
++};
++
++/* flags */
++#define I915_OVERLAY_UPDATE_ATTRS (1<<0)
++#define I915_OVERLAY_UPDATE_GAMMA (1<<1)
++struct drm_intel_overlay_attrs {
++ __u32 flags;
++ __u32 color_key;
++ __s32 brightness;
++ __u32 contrast;
++ __u32 saturation;
++ __u32 gamma0;
++ __u32 gamma1;
++ __u32 gamma2;
++ __u32 gamma3;
++ __u32 gamma4;
++ __u32 gamma5;
++};
++
+ #endif /* _I915_DRM_H_ */
+diff --git a/include/drm/mga_drm.h b/include/drm/mga_drm.h
+index 325fd6f..3ffbc47 100644
+--- a/include/drm/mga_drm.h
++++ b/include/drm/mga_drm.h
+@@ -35,7 +35,7 @@
+ #ifndef __MGA_DRM_H__
+ #define __MGA_DRM_H__
+
+-#include <linux/types.h>
++#include "drm.h"
+
+ /* WARNING: If you change any of these defines, make sure to change the
+ * defines in the Xserver file (mga_sarea.h)
+diff --git a/include/drm/nouveau_drm.h b/include/drm/nouveau_drm.h
+new file mode 100644
+index 0000000..f745948
+--- /dev/null
++++ b/include/drm/nouveau_drm.h
+@@ -0,0 +1,221 @@
++/*
++ * Copyright 2005 Stephane Marchesin.
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the "Software"),
++ * to deal in the Software without restriction, including without limitation
++ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
++ * and/or sell copies of the Software, and to permit persons to whom the
++ * Software is furnished to do so, subject to the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the next
++ * paragraph) shall be included in all copies or substantial portions of the
++ * Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
++ * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
++ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
++ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
++ * OTHER DEALINGS IN THE SOFTWARE.
++ */
++
++#ifndef __NOUVEAU_DRM_H__
++#define __NOUVEAU_DRM_H__
++
++#define NOUVEAU_DRM_HEADER_PATCHLEVEL 15
++
++struct drm_nouveau_channel_alloc {
++ uint32_t fb_ctxdma_handle;
++ uint32_t tt_ctxdma_handle;
++
++ int channel;
++
++ /* Notifier memory */
++ uint32_t notifier_handle;
++
++ /* DRM-enforced subchannel assignments */
++ struct {
++ uint32_t handle;
++ uint32_t grclass;
++ } subchan[8];
++ uint32_t nr_subchan;
++};
++
++struct drm_nouveau_channel_free {
++ int channel;
++};
++
++struct drm_nouveau_grobj_alloc {
++ int channel;
++ uint32_t handle;
++ int class;
++};
++
++struct drm_nouveau_notifierobj_alloc {
++ uint32_t channel;
++ uint32_t handle;
++ uint32_t size;
++ uint32_t offset;
++};
++
++struct drm_nouveau_gpuobj_free {
++ int channel;
++ uint32_t handle;
++};
++
++/* FIXME : maybe unify {GET,SET}PARAMs */
++#define NOUVEAU_GETPARAM_PCI_VENDOR 3
++#define NOUVEAU_GETPARAM_PCI_DEVICE 4
++#define NOUVEAU_GETPARAM_BUS_TYPE 5
++#define NOUVEAU_GETPARAM_FB_PHYSICAL 6
++#define NOUVEAU_GETPARAM_AGP_PHYSICAL 7
++#define NOUVEAU_GETPARAM_FB_SIZE 8
++#define NOUVEAU_GETPARAM_AGP_SIZE 9
++#define NOUVEAU_GETPARAM_PCI_PHYSICAL 10
++#define NOUVEAU_GETPARAM_CHIPSET_ID 11
++#define NOUVEAU_GETPARAM_VM_VRAM_BASE 12
++#define NOUVEAU_GETPARAM_GRAPH_UNITS 13
++struct drm_nouveau_getparam {
++ uint64_t param;
++ uint64_t value;
++};
++
++struct drm_nouveau_setparam {
++ uint64_t param;
++ uint64_t value;
++};
++
++#define NOUVEAU_GEM_DOMAIN_CPU (1 << 0)
++#define NOUVEAU_GEM_DOMAIN_VRAM (1 << 1)
++#define NOUVEAU_GEM_DOMAIN_GART (1 << 2)
++#define NOUVEAU_GEM_DOMAIN_MAPPABLE (1 << 3)
++
++struct drm_nouveau_gem_info {
++ uint32_t handle;
++ uint32_t domain;
++ uint64_t size;
++ uint64_t offset;
++ uint64_t map_handle;
++ uint32_t tile_mode;
++ uint32_t tile_flags;
++};
++
++struct drm_nouveau_gem_new {
++ struct drm_nouveau_gem_info info;
++ uint32_t channel_hint;
++ uint32_t align;
++};
++
++struct drm_nouveau_gem_pushbuf_bo {
++ uint64_t user_priv;
++ uint32_t handle;
++ uint32_t read_domains;
++ uint32_t write_domains;
++ uint32_t valid_domains;
++ uint32_t presumed_ok;
++ uint32_t presumed_domain;
++ uint64_t presumed_offset;
++};
++
++#define NOUVEAU_GEM_RELOC_LOW (1 << 0)
++#define NOUVEAU_GEM_RELOC_HIGH (1 << 1)
++#define NOUVEAU_GEM_RELOC_OR (1 << 2)
++struct drm_nouveau_gem_pushbuf_reloc {
++ uint32_t bo_index;
++ uint32_t reloc_index;
++ uint32_t flags;
++ uint32_t data;
++ uint32_t vor;
++ uint32_t tor;
++};
++
++#define NOUVEAU_GEM_MAX_BUFFERS 1024
++#define NOUVEAU_GEM_MAX_RELOCS 1024
++
++struct drm_nouveau_gem_pushbuf {
++ uint32_t channel;
++ uint32_t nr_dwords;
++ uint32_t nr_buffers;
++ uint32_t nr_relocs;
++ uint64_t dwords;
++ uint64_t buffers;
++ uint64_t relocs;
++};
++
++struct drm_nouveau_gem_pushbuf_call {
++ uint32_t channel;
++ uint32_t handle;
++ uint32_t offset;
++ uint32_t nr_buffers;
++ uint32_t nr_relocs;
++ uint32_t nr_dwords;
++ uint64_t buffers;
++ uint64_t relocs;
++ uint32_t suffix0;
++ uint32_t suffix1;
++ /* below only accessed for CALL2 */
++ uint64_t vram_available;
++ uint64_t gart_available;
++};
++
++struct drm_nouveau_gem_pin {
++ uint32_t handle;
++ uint32_t domain;
++ uint64_t offset;
++};
++
++struct drm_nouveau_gem_unpin {
++ uint32_t handle;
++};
++
++#define NOUVEAU_GEM_CPU_PREP_NOWAIT 0x00000001
++#define NOUVEAU_GEM_CPU_PREP_NOBLOCK 0x00000002
++#define NOUVEAU_GEM_CPU_PREP_WRITE 0x00000004
++struct drm_nouveau_gem_cpu_prep {
++ uint32_t handle;
++ uint32_t flags;
++};
++
++struct drm_nouveau_gem_cpu_fini {
++ uint32_t handle;
++};
++
++struct drm_nouveau_gem_tile {
++ uint32_t handle;
++ uint32_t offset;
++ uint32_t size;
++ uint32_t tile_mode;
++ uint32_t tile_flags;
++};
++
++enum nouveau_bus_type {
++ NV_AGP = 0,
++ NV_PCI = 1,
++ NV_PCIE = 2,
++};
++
++struct drm_nouveau_sarea {
++};
++
++#define DRM_NOUVEAU_CARD_INIT 0x00
++#define DRM_NOUVEAU_GETPARAM 0x01
++#define DRM_NOUVEAU_SETPARAM 0x02
++#define DRM_NOUVEAU_CHANNEL_ALLOC 0x03
++#define DRM_NOUVEAU_CHANNEL_FREE 0x04
++#define DRM_NOUVEAU_GROBJ_ALLOC 0x05
++#define DRM_NOUVEAU_NOTIFIEROBJ_ALLOC 0x06
++#define DRM_NOUVEAU_GPUOBJ_FREE 0x07
++#define DRM_NOUVEAU_GEM_NEW 0x40
++#define DRM_NOUVEAU_GEM_PUSHBUF 0x41
++#define DRM_NOUVEAU_GEM_PUSHBUF_CALL 0x42
++#define DRM_NOUVEAU_GEM_PIN 0x43 /* !KMS only */
++#define DRM_NOUVEAU_GEM_UNPIN 0x44 /* !KMS only */
++#define DRM_NOUVEAU_GEM_CPU_PREP 0x45
++#define DRM_NOUVEAU_GEM_CPU_FINI 0x46
++#define DRM_NOUVEAU_GEM_INFO 0x47
++#define DRM_NOUVEAU_GEM_PUSHBUF_CALL2 0x48
++
++#endif /* __NOUVEAU_DRM_H__ */
+diff --git a/include/drm/radeon_drm.h b/include/drm/radeon_drm.h
+index 3b9932a..39537f3 100644
+--- a/include/drm/radeon_drm.h
++++ b/include/drm/radeon_drm.h
+@@ -33,7 +33,7 @@
+ #ifndef __RADEON_DRM_H__
+ #define __RADEON_DRM_H__
+
+-#include <linux/types.h>
++#include "drm.h"
+
+ /* WARNING: If you change any of these defines, make sure to change the
+ * defines in the X server file (radeon_sarea.h)
+diff --git a/include/drm/ttm/ttm_bo_api.h b/include/drm/ttm/ttm_bo_api.h
+index 4911461..81eb9f4 100644
+--- a/include/drm/ttm/ttm_bo_api.h
++++ b/include/drm/ttm/ttm_bo_api.h
+@@ -44,6 +44,29 @@ struct ttm_bo_device;
+
+ struct drm_mm_node;
+
++
++/**
++ * struct ttm_placement
++ *
++ * @fpfn: first valid page frame number to put the object
++ * @lpfn: last valid page frame number to put the object
++ * @num_placement: number of prefered placements
++ * @placement: prefered placements
++ * @num_busy_placement: number of prefered placements when need to evict buffer
++ * @busy_placement: prefered placements when need to evict buffer
++ *
++ * Structure indicating the placement you request for an object.
++ */
++struct ttm_placement {
++ unsigned fpfn;
++ unsigned lpfn;
++ unsigned num_placement;
++ const uint32_t *placement;
++ unsigned num_busy_placement;
++ const uint32_t *busy_placement;
++};
++
++
+ /**
+ * struct ttm_mem_reg
+ *
+@@ -109,10 +132,6 @@ struct ttm_tt;
+ * the object is destroyed.
+ * @event_queue: Queue for processes waiting on buffer object status change.
+ * @lock: spinlock protecting mostly synchronization members.
+- * @proposed_placement: Proposed placement for the buffer. Changed only by the
+- * creator prior to validation as opposed to bo->mem.proposed_flags which is
+- * changed by the implementation prior to a buffer move if it wants to outsmart
+- * the buffer creator / user. This latter happens, for example, at eviction.
+ * @mem: structure describing current placement.
+ * @persistant_swap_storage: Usually the swap storage is deleted for buffers
+ * pinned in physical memory. If this behaviour is not desired, this member
+@@ -177,7 +196,6 @@ struct ttm_buffer_object {
+ * Members protected by the bo::reserved lock.
+ */
+
+- uint32_t proposed_placement;
+ struct ttm_mem_reg mem;
+ struct file *persistant_swap_storage;
+ struct ttm_tt *ttm;
+@@ -285,29 +303,30 @@ ttm_bo_reference(struct ttm_buffer_object *bo)
+ * Note: It might be necessary to block validations before the
+ * wait by reserving the buffer.
+ * Returns -EBUSY if no_wait is true and the buffer is busy.
+- * Returns -ERESTART if interrupted by a signal.
++ * Returns -ERESTARTSYS if interrupted by a signal.
+ */
+ extern int ttm_bo_wait(struct ttm_buffer_object *bo, bool lazy,
+ bool interruptible, bool no_wait);
+ /**
+- * ttm_buffer_object_validate
++ * ttm_bo_validate
+ *
+ * @bo: The buffer object.
+- * @proposed_placement: Proposed_placement for the buffer object.
++ * @placement: Proposed placement for the buffer object.
+ * @interruptible: Sleep interruptible if sleeping.
+ * @no_wait: Return immediately if the buffer is busy.
+ *
+ * Changes placement and caching policy of the buffer object
+- * according to bo::proposed_flags.
++ * according proposed placement.
+ * Returns
+- * -EINVAL on invalid proposed_flags.
++ * -EINVAL on invalid proposed placement.
+ * -ENOMEM on out-of-memory condition.
+ * -EBUSY if no_wait is true and buffer busy.
+- * -ERESTART if interrupted by a signal.
++ * -ERESTARTSYS if interrupted by a signal.
+ */
+-extern int ttm_buffer_object_validate(struct ttm_buffer_object *bo,
+- uint32_t proposed_placement,
+- bool interruptible, bool no_wait);
++extern int ttm_bo_validate(struct ttm_buffer_object *bo,
++ struct ttm_placement *placement,
++ bool interruptible, bool no_wait);
++
+ /**
+ * ttm_bo_unref
+ *
+@@ -328,7 +347,7 @@ extern void ttm_bo_unref(struct ttm_buffer_object **bo);
+ * waiting for buffer idle. This lock is recursive.
+ * Returns
+ * -EBUSY if the buffer is busy and no_wait is true.
+- * -ERESTART if interrupted by a signal.
++ * -ERESTARTSYS if interrupted by a signal.
+ */
+
+ extern int
+@@ -343,7 +362,7 @@ ttm_bo_synccpu_write_grab(struct ttm_buffer_object *bo, bool no_wait);
+ extern void ttm_bo_synccpu_write_release(struct ttm_buffer_object *bo);
+
+ /**
+- * ttm_buffer_object_init
++ * ttm_bo_init
+ *
+ * @bdev: Pointer to a ttm_bo_device struct.
+ * @bo: Pointer to a ttm_buffer_object to be initialized.
+@@ -371,20 +390,20 @@ extern void ttm_bo_synccpu_write_release(struct ttm_buffer_object *bo);
+ * Returns
+ * -ENOMEM: Out of memory.
+ * -EINVAL: Invalid placement flags.
+- * -ERESTART: Interrupted by signal while sleeping waiting for resources.
++ * -ERESTARTSYS: Interrupted by signal while sleeping waiting for resources.
+ */
+
+-extern int ttm_buffer_object_init(struct ttm_bo_device *bdev,
+- struct ttm_buffer_object *bo,
+- unsigned long size,
+- enum ttm_bo_type type,
+- uint32_t flags,
+- uint32_t page_alignment,
+- unsigned long buffer_start,
+- bool interrubtible,
+- struct file *persistant_swap_storage,
+- size_t acc_size,
+- void (*destroy) (struct ttm_buffer_object *));
++extern int ttm_bo_init(struct ttm_bo_device *bdev,
++ struct ttm_buffer_object *bo,
++ unsigned long size,
++ enum ttm_bo_type type,
++ struct ttm_placement *placement,
++ uint32_t page_alignment,
++ unsigned long buffer_start,
++ bool interrubtible,
++ struct file *persistant_swap_storage,
++ size_t acc_size,
++ void (*destroy) (struct ttm_buffer_object *));
+ /**
+ * ttm_bo_synccpu_object_init
+ *
+@@ -405,47 +424,43 @@ extern int ttm_buffer_object_init(struct ttm_bo_device *bdev,
+ * GEM user interface.
+ * @p_bo: On successful completion *p_bo points to the created object.
+ *
+- * This function allocates a ttm_buffer_object, and then calls
+- * ttm_buffer_object_init on that object.
+- * The destroy function is set to kfree().
++ * This function allocates a ttm_buffer_object, and then calls ttm_bo_init
++ * on that object. The destroy function is set to kfree().
+ * Returns
+ * -ENOMEM: Out of memory.
+ * -EINVAL: Invalid placement flags.
+- * -ERESTART: Interrupted by signal while waiting for resources.
++ * -ERESTARTSYS: Interrupted by signal while waiting for resources.
+ */
+
+-extern int ttm_buffer_object_create(struct ttm_bo_device *bdev,
+- unsigned long size,
+- enum ttm_bo_type type,
+- uint32_t flags,
+- uint32_t page_alignment,
+- unsigned long buffer_start,
+- bool interruptible,
+- struct file *persistant_swap_storage,
+- struct ttm_buffer_object **p_bo);
++extern int ttm_bo_create(struct ttm_bo_device *bdev,
++ unsigned long size,
++ enum ttm_bo_type type,
++ struct ttm_placement *placement,
++ uint32_t page_alignment,
++ unsigned long buffer_start,
++ bool interruptible,
++ struct file *persistant_swap_storage,
++ struct ttm_buffer_object **p_bo);
+
+ /**
+ * ttm_bo_check_placement
+ *
+- * @bo: the buffer object.
+- * @set_flags: placement flags to set.
+- * @clr_flags: placement flags to clear.
++ * @bo: the buffer object.
++ * @placement: placements
+ *
+ * Performs minimal validity checking on an intended change of
+ * placement flags.
+ * Returns
+ * -EINVAL: Intended change is invalid or not allowed.
+ */
+-
+ extern int ttm_bo_check_placement(struct ttm_buffer_object *bo,
+- uint32_t set_flags, uint32_t clr_flags);
++ struct ttm_placement *placement);
+
+ /**
+ * ttm_bo_init_mm
+ *
+ * @bdev: Pointer to a ttm_bo_device struct.
+ * @mem_type: The memory type.
+- * @p_offset: offset for managed area in pages.
+ * @p_size: size managed area in pages.
+ *
+ * Initialize a manager for a given memory type.
+@@ -458,7 +473,7 @@ extern int ttm_bo_check_placement(struct ttm_buffer_object *bo,
+ */
+
+ extern int ttm_bo_init_mm(struct ttm_bo_device *bdev, unsigned type,
+- unsigned long p_offset, unsigned long p_size);
++ unsigned long p_size);
+ /**
+ * ttm_bo_clean_mm
+ *
+@@ -503,7 +518,7 @@ extern int ttm_bo_clean_mm(struct ttm_bo_device *bdev, unsigned mem_type);
+ *
+ * Returns:
+ * -EINVAL: Invalid or uninitialized memory type.
+- * -ERESTART: The call was interrupted by a signal while waiting to
++ * -ERESTARTSYS: The call was interrupted by a signal while waiting to
+ * evict a buffer.
+ */
+
+@@ -606,7 +621,7 @@ extern int ttm_bo_mmap(struct file *filp, struct vm_area_struct *vma,
+ * be called from the fops::read and fops::write method.
+ * Returns:
+ * See man (2) write, man(2) read. In particular,
+- * the function may return -EINTR if
++ * the function may return -ERESTARTSYS if
+ * interrupted by a signal.
+ */
+
+diff --git a/include/drm/ttm/ttm_bo_driver.h b/include/drm/ttm/ttm_bo_driver.h
+index e8cd6d2..4c4e0f8 100644
+--- a/include/drm/ttm/ttm_bo_driver.h
++++ b/include/drm/ttm/ttm_bo_driver.h
+@@ -242,12 +242,6 @@ struct ttm_mem_type_manager {
+ /**
+ * struct ttm_bo_driver
+ *
+- * @mem_type_prio: Priority array of memory types to place a buffer object in
+- * if it fits without evicting buffers from any of these memory types.
+- * @mem_busy_prio: Priority array of memory types to place a buffer object in
+- * if it needs to evict buffers to make room.
+- * @num_mem_type_prio: Number of elements in the @mem_type_prio array.
+- * @num_mem_busy_prio: Number of elements in the @num_mem_busy_prio array.
+ * @create_ttm_backend_entry: Callback to create a struct ttm_backend.
+ * @invalidate_caches: Callback to invalidate read caches when a buffer object
+ * has been evicted.
+@@ -265,11 +259,6 @@ struct ttm_mem_type_manager {
+ */
+
+ struct ttm_bo_driver {
+- const uint32_t *mem_type_prio;
+- const uint32_t *mem_busy_prio;
+- uint32_t num_mem_type_prio;
+- uint32_t num_mem_busy_prio;
+-
+ /**
+ * struct ttm_bo_driver member create_ttm_backend_entry
+ *
+@@ -306,7 +295,8 @@ struct ttm_bo_driver {
+ * finished, they'll end up in bo->mem.flags
+ */
+
+- uint32_t(*evict_flags) (struct ttm_buffer_object *bo);
++ void(*evict_flags) (struct ttm_buffer_object *bo,
++ struct ttm_placement *placement);
+ /**
+ * struct ttm_bo_driver member move:
+ *
+@@ -363,6 +353,11 @@ struct ttm_bo_driver {
+ /* notify the driver we are taking a fault on this BO
+ * and have reserved it */
+ void (*fault_reserve_notify)(struct ttm_buffer_object *bo);
++
++ /**
++ * notify the driver that we're about to swap out this bo
++ */
++ void (*swap_notify) (struct ttm_buffer_object *bo);
+ };
+
+ /**
+@@ -545,6 +540,15 @@ extern int ttm_tt_set_user(struct ttm_tt *ttm,
+ extern int ttm_tt_bind(struct ttm_tt *ttm, struct ttm_mem_reg *bo_mem);
+
+ /**
++ * ttm_tt_populate:
++ *
++ * @ttm: The struct ttm_tt to contain the backing pages.
++ *
++ * Add backing pages to all of @ttm
++ */
++extern int ttm_tt_populate(struct ttm_tt *ttm);
++
++/**
+ * ttm_ttm_destroy:
+ *
+ * @ttm: The struct ttm_tt.
+@@ -639,12 +643,12 @@ extern bool ttm_mem_reg_is_pci(struct ttm_bo_device *bdev,
+ * -EBUSY: No space available (only if no_wait == 1).
+ * -ENOMEM: Could not allocate memory for the buffer object, either due to
+ * fragmentation or concurrent allocators.
+- * -ERESTART: An interruptible sleep was interrupted by a signal.
++ * -ERESTARTSYS: An interruptible sleep was interrupted by a signal.
+ */
+ extern int ttm_bo_mem_space(struct ttm_buffer_object *bo,
+- uint32_t proposed_placement,
+- struct ttm_mem_reg *mem,
+- bool interruptible, bool no_wait);
++ struct ttm_placement *placement,
++ struct ttm_mem_reg *mem,
++ bool interruptible, bool no_wait);
+ /**
+ * ttm_bo_wait_for_cpu
+ *
+@@ -654,7 +658,7 @@ extern int ttm_bo_mem_space(struct ttm_buffer_object *bo,
+ * Wait until a buffer object is no longer sync'ed for CPU access.
+ * Returns:
+ * -EBUSY: Buffer object was sync'ed for CPU access. (only if no_wait == 1).
+- * -ERESTART: An interruptible sleep was interrupted by a signal.
++ * -ERESTARTSYS: An interruptible sleep was interrupted by a signal.
+ */
+
+ extern int ttm_bo_wait_cpu(struct ttm_buffer_object *bo, bool no_wait);
+@@ -758,7 +762,7 @@ extern void ttm_bo_unmap_virtual(struct ttm_buffer_object *bo);
+ * -EAGAIN: The reservation may cause a deadlock.
+ * Release all buffer reservations, wait for @bo to become unreserved and
+ * try again. (only if use_sequence == 1).
+- * -ERESTART: A wait for the buffer to become unreserved was interrupted by
++ * -ERESTARTSYS: A wait for the buffer to become unreserved was interrupted by
+ * a signal. Release all buffer reservations and return to user-space.
+ */
+ extern int ttm_bo_reserve(struct ttm_buffer_object *bo,
+@@ -799,7 +803,7 @@ extern int ttm_bo_wait_unreserved(struct ttm_buffer_object *bo,
+ *
+ * Returns:
+ * -EBUSY: If no_wait == 1 and the buffer is already reserved.
+- * -ERESTART: If interruptible == 1 and the process received a signal
++ * -ERESTARTSYS: If interruptible == 1 and the process received a signal
+ * while sleeping.
+ */
+ extern int ttm_bo_block_reservation(struct ttm_buffer_object *bo,
+diff --git a/include/drm/ttm/ttm_execbuf_util.h b/include/drm/ttm/ttm_execbuf_util.h
+new file mode 100644
+index 0000000..cd2c475
+--- /dev/null
++++ b/include/drm/ttm/ttm_execbuf_util.h
+@@ -0,0 +1,107 @@
++/**************************************************************************
++ *
++ * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++/*
++ * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
++ */
++
++#ifndef _TTM_EXECBUF_UTIL_H_
++#define _TTM_EXECBUF_UTIL_H_
++
++#include "ttm/ttm_bo_api.h"
++#include <linux/list.h>
++
++/**
++ * struct ttm_validate_buffer
++ *
++ * @head: list head for thread-private list.
++ * @bo: refcounted buffer object pointer.
++ * @new_sync_obj_arg: New sync_obj_arg for @bo, to be used once
++ * adding a new sync object.
++ * @reservied: Indicates whether @bo has been reserved for validation.
++ */
++
++struct ttm_validate_buffer {
++ struct list_head head;
++ struct ttm_buffer_object *bo;
++ void *new_sync_obj_arg;
++ bool reserved;
++};
++
++/**
++ * function ttm_eu_backoff_reservation
++ *
++ * @list: thread private list of ttm_validate_buffer structs.
++ *
++ * Undoes all buffer validation reservations for bos pointed to by
++ * the list entries.
++ */
++
++extern void ttm_eu_backoff_reservation(struct list_head *list);
++
++/**
++ * function ttm_eu_reserve_buffers
++ *
++ * @list: thread private list of ttm_validate_buffer structs.
++ * @val_seq: A unique sequence number.
++ *
++ * Tries to reserve bos pointed to by the list entries for validation.
++ * If the function returns 0, all buffers are marked as "unfenced",
++ * taken off the lru lists and are not synced for write CPU usage.
++ *
++ * If the function detects a deadlock due to multiple threads trying to
++ * reserve the same buffers in reverse order, all threads except one will
++ * back off and retry. This function may sleep while waiting for
++ * CPU write reservations to be cleared, and for other threads to
++ * unreserve their buffers.
++ *
++ * This function may return -ERESTART or -EAGAIN if the calling process
++ * receives a signal while waiting. In that case, no buffers on the list
++ * will be reserved upon return.
++ *
++ * Buffers reserved by this function should be unreserved by
++ * a call to either ttm_eu_backoff_reservation() or
++ * ttm_eu_fence_buffer_objects() when command submission is complete or
++ * has failed.
++ */
++
++extern int ttm_eu_reserve_buffers(struct list_head *list, uint32_t val_seq);
++
++/**
++ * function ttm_eu_fence_buffer_objects.
++ *
++ * @list: thread private list of ttm_validate_buffer structs.
++ * @sync_obj: The new sync object for the buffers.
++ *
++ * This function should be called when command submission is complete, and
++ * it will add a new sync object to bos pointed to by entries on @list.
++ * It also unreserves all buffers, putting them on lru lists.
++ *
++ */
++
++extern void ttm_eu_fence_buffer_objects(struct list_head *list, void *sync_obj);
++
++#endif
+diff --git a/include/drm/ttm/ttm_lock.h b/include/drm/ttm/ttm_lock.h
+new file mode 100644
+index 0000000..81ba0b0
+--- /dev/null
++++ b/include/drm/ttm/ttm_lock.h
+@@ -0,0 +1,247 @@
++/**************************************************************************
++ *
++ * Copyright (c) 2007-2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++/*
++ * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
++ */
++
++/** @file ttm_lock.h
++ * This file implements a simple replacement for the buffer manager use
++ * of the DRM heavyweight hardware lock.
++ * The lock is a read-write lock. Taking it in read mode and write mode
++ * is relatively fast, and intended for in-kernel use only.
++ *
++ * The vt mode is used only when there is a need to block all
++ * user-space processes from validating buffers.
++ * It's allowed to leave kernel space with the vt lock held.
++ * If a user-space process dies while having the vt-lock,
++ * it will be released during the file descriptor release. The vt lock
++ * excludes write lock and read lock.
++ *
++ * The suspend mode is used to lock out all TTM users when preparing for
++ * and executing suspend operations.
++ *
++ */
++
++#ifndef _TTM_LOCK_H_
++#define _TTM_LOCK_H_
++
++#include "ttm/ttm_object.h"
++#include <linux/wait.h>
++#include <asm/atomic.h>
++
++/**
++ * struct ttm_lock
++ *
++ * @base: ttm base object used solely to release the lock if the client
++ * holding the lock dies.
++ * @queue: Queue for processes waiting for lock change-of-status.
++ * @lock: Spinlock protecting some lock members.
++ * @rw: Read-write lock counter. Protected by @lock.
++ * @flags: Lock state. Protected by @lock.
++ * @kill_takers: Boolean whether to kill takers of the lock.
++ * @signal: Signal to send when kill_takers is true.
++ */
++
++struct ttm_lock {
++ struct ttm_base_object base;
++ wait_queue_head_t queue;
++ spinlock_t lock;
++ int32_t rw;
++ uint32_t flags;
++ bool kill_takers;
++ int signal;
++ struct ttm_object_file *vt_holder;
++};
++
++
++/**
++ * ttm_lock_init
++ *
++ * @lock: Pointer to a struct ttm_lock
++ * Initializes the lock.
++ */
++extern void ttm_lock_init(struct ttm_lock *lock);
++
++/**
++ * ttm_read_unlock
++ *
++ * @lock: Pointer to a struct ttm_lock
++ *
++ * Releases a read lock.
++ */
++extern void ttm_read_unlock(struct ttm_lock *lock);
++
++/**
++ * ttm_read_lock
++ *
++ * @lock: Pointer to a struct ttm_lock
++ * @interruptible: Interruptible sleeping while waiting for a lock.
++ *
++ * Takes the lock in read mode.
++ * Returns:
++ * -ERESTARTSYS If interrupted by a signal and interruptible is true.
++ */
++extern int ttm_read_lock(struct ttm_lock *lock, bool interruptible);
++
++/**
++ * ttm_read_trylock
++ *
++ * @lock: Pointer to a struct ttm_lock
++ * @interruptible: Interruptible sleeping while waiting for a lock.
++ *
++ * Tries to take the lock in read mode. If the lock is already held
++ * in write mode, the function will return -EBUSY. If the lock is held
++ * in vt or suspend mode, the function will sleep until these modes
++ * are unlocked.
++ *
++ * Returns:
++ * -EBUSY The lock was already held in write mode.
++ * -ERESTARTSYS If interrupted by a signal and interruptible is true.
++ */
++extern int ttm_read_trylock(struct ttm_lock *lock, bool interruptible);
++
++/**
++ * ttm_write_unlock
++ *
++ * @lock: Pointer to a struct ttm_lock
++ *
++ * Releases a write lock.
++ */
++extern void ttm_write_unlock(struct ttm_lock *lock);
++
++/**
++ * ttm_write_lock
++ *
++ * @lock: Pointer to a struct ttm_lock
++ * @interruptible: Interruptible sleeping while waiting for a lock.
++ *
++ * Takes the lock in write mode.
++ * Returns:
++ * -ERESTARTSYS If interrupted by a signal and interruptible is true.
++ */
++extern int ttm_write_lock(struct ttm_lock *lock, bool interruptible);
++
++/**
++ * ttm_lock_downgrade
++ *
++ * @lock: Pointer to a struct ttm_lock
++ *
++ * Downgrades a write lock to a read lock.
++ */
++extern void ttm_lock_downgrade(struct ttm_lock *lock);
++
++/**
++ * ttm_suspend_lock
++ *
++ * @lock: Pointer to a struct ttm_lock
++ *
++ * Takes the lock in suspend mode. Excludes read and write mode.
++ */
++extern void ttm_suspend_lock(struct ttm_lock *lock);
++
++/**
++ * ttm_suspend_unlock
++ *
++ * @lock: Pointer to a struct ttm_lock
++ *
++ * Releases a suspend lock
++ */
++extern void ttm_suspend_unlock(struct ttm_lock *lock);
++
++/**
++ * ttm_vt_lock
++ *
++ * @lock: Pointer to a struct ttm_lock
++ * @interruptible: Interruptible sleeping while waiting for a lock.
++ * @tfile: Pointer to a struct ttm_object_file to register the lock with.
++ *
++ * Takes the lock in vt mode.
++ * Returns:
++ * -ERESTARTSYS If interrupted by a signal and interruptible is true.
++ * -ENOMEM: Out of memory when locking.
++ */
++extern int ttm_vt_lock(struct ttm_lock *lock, bool interruptible,
++ struct ttm_object_file *tfile);
++
++/**
++ * ttm_vt_unlock
++ *
++ * @lock: Pointer to a struct ttm_lock
++ *
++ * Releases a vt lock.
++ * Returns:
++ * -EINVAL If the lock was not held.
++ */
++extern int ttm_vt_unlock(struct ttm_lock *lock);
++
++/**
++ * ttm_write_unlock
++ *
++ * @lock: Pointer to a struct ttm_lock
++ *
++ * Releases a write lock.
++ */
++extern void ttm_write_unlock(struct ttm_lock *lock);
++
++/**
++ * ttm_write_lock
++ *
++ * @lock: Pointer to a struct ttm_lock
++ * @interruptible: Interruptible sleeping while waiting for a lock.
++ *
++ * Takes the lock in write mode.
++ * Returns:
++ * -ERESTARTSYS If interrupted by a signal and interruptible is true.
++ */
++extern int ttm_write_lock(struct ttm_lock *lock, bool interruptible);
++
++/**
++ * ttm_lock_set_kill
++ *
++ * @lock: Pointer to a struct ttm_lock
++ * @val: Boolean whether to kill processes taking the lock.
++ * @signal: Signal to send to the process taking the lock.
++ *
++ * The kill-when-taking-lock functionality is used to kill processes that keep
++ * on using the TTM functionality when its resources has been taken down, for
++ * example when the X server exits. A typical sequence would look like this:
++ * - X server takes lock in write mode.
++ * - ttm_lock_set_kill() is called with @val set to true.
++ * - As part of X server exit, TTM resources are taken down.
++ * - X server releases the lock on file release.
++ * - Another dri client wants to render, takes the lock and is killed.
++ *
++ */
++static inline void ttm_lock_set_kill(struct ttm_lock *lock, bool val,
++ int signal)
++{
++ lock->kill_takers = val;
++ if (val)
++ lock->signal = signal;
++}
++
++#endif
+diff --git a/include/drm/ttm/ttm_object.h b/include/drm/ttm/ttm_object.h
+new file mode 100644
+index 0000000..0d9db09
+--- /dev/null
++++ b/include/drm/ttm/ttm_object.h
+@@ -0,0 +1,271 @@
++/**************************************************************************
++ *
++ * Copyright (c) 2006-2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++/*
++ * Authors: Thomas Hellstrom <thellstrom-at-vmware-dot-com>
++ */
++/** @file ttm_object.h
++ *
++ * Base- and reference object implementation for the various
++ * ttm objects. Implements reference counting, minimal security checks
++ * and release on file close.
++ */
++
++#ifndef _TTM_OBJECT_H_
++#define _TTM_OBJECT_H_
++
++#include <linux/list.h>
++#include "drm_hashtab.h"
++#include <linux/kref.h>
++#include <ttm/ttm_memory.h>
++
++/**
++ * enum ttm_ref_type
++ *
++ * Describes what type of reference a ref object holds.
++ *
++ * TTM_REF_USAGE is a simple refcount on a base object.
++ *
++ * TTM_REF_SYNCCPU_READ is a SYNCCPU_READ reference on a
++ * buffer object.
++ *
++ * TTM_REF_SYNCCPU_WRITE is a SYNCCPU_WRITE reference on a
++ * buffer object.
++ *
++ */
++
++enum ttm_ref_type {
++ TTM_REF_USAGE,
++ TTM_REF_SYNCCPU_READ,
++ TTM_REF_SYNCCPU_WRITE,
++ TTM_REF_NUM
++};
++
++/**
++ * enum ttm_object_type
++ *
++ * One entry per ttm object type.
++ * Device-specific types should use the
++ * ttm_driver_typex types.
++ */
++
++enum ttm_object_type {
++ ttm_fence_type,
++ ttm_buffer_type,
++ ttm_lock_type,
++ ttm_driver_type0 = 256,
++ ttm_driver_type1,
++ ttm_driver_type2,
++ ttm_driver_type3,
++ ttm_driver_type4,
++ ttm_driver_type5
++};
++
++struct ttm_object_file;
++struct ttm_object_device;
++
++/**
++ * struct ttm_base_object
++ *
++ * @hash: hash entry for the per-device object hash.
++ * @type: derived type this object is base class for.
++ * @shareable: Other ttm_object_files can access this object.
++ *
++ * @tfile: Pointer to ttm_object_file of the creator.
++ * NULL if the object was not created by a user request.
++ * (kernel object).
++ *
++ * @refcount: Number of references to this object, not
++ * including the hash entry. A reference to a base object can
++ * only be held by a ref object.
++ *
++ * @refcount_release: A function to be called when there are
++ * no more references to this object. This function should
++ * destroy the object (or make sure destruction eventually happens),
++ * and when it is called, the object has
++ * already been taken out of the per-device hash. The parameter
++ * "base" should be set to NULL by the function.
++ *
++ * @ref_obj_release: A function to be called when a reference object
++ * with another ttm_ref_type than TTM_REF_USAGE is deleted.
++ * this function may, for example, release a lock held by a user-space
++ * process.
++ *
++ * This struct is intended to be used as a base struct for objects that
++ * are visible to user-space. It provides a global name, race-safe
++ * access and refcounting, minimal access contol and hooks for unref actions.
++ */
++
++struct ttm_base_object {
++ struct drm_hash_item hash;
++ enum ttm_object_type object_type;
++ bool shareable;
++ struct ttm_object_file *tfile;
++ struct kref refcount;
++ void (*refcount_release) (struct ttm_base_object **base);
++ void (*ref_obj_release) (struct ttm_base_object *base,
++ enum ttm_ref_type ref_type);
++};
++
++/**
++ * ttm_base_object_init
++ *
++ * @tfile: Pointer to a struct ttm_object_file.
++ * @base: The struct ttm_base_object to initialize.
++ * @shareable: This object is shareable with other applcations.
++ * (different @tfile pointers.)
++ * @type: The object type.
++ * @refcount_release: See the struct ttm_base_object description.
++ * @ref_obj_release: See the struct ttm_base_object description.
++ *
++ * Initializes a struct ttm_base_object.
++ */
++
++extern int ttm_base_object_init(struct ttm_object_file *tfile,
++ struct ttm_base_object *base,
++ bool shareable,
++ enum ttm_object_type type,
++ void (*refcount_release) (struct ttm_base_object
++ **),
++ void (*ref_obj_release) (struct ttm_base_object
++ *,
++ enum ttm_ref_type
++ ref_type));
++
++/**
++ * ttm_base_object_lookup
++ *
++ * @tfile: Pointer to a struct ttm_object_file.
++ * @key: Hash key
++ *
++ * Looks up a struct ttm_base_object with the key @key.
++ * Also verifies that the object is visible to the application, by
++ * comparing the @tfile argument and checking the object shareable flag.
++ */
++
++extern struct ttm_base_object *ttm_base_object_lookup(struct ttm_object_file
++ *tfile, uint32_t key);
++
++/**
++ * ttm_base_object_unref
++ *
++ * @p_base: Pointer to a pointer referncing a struct ttm_base_object.
++ *
++ * Decrements the base object refcount and clears the pointer pointed to by
++ * p_base.
++ */
++
++extern void ttm_base_object_unref(struct ttm_base_object **p_base);
++
++/**
++ * ttm_ref_object_add.
++ *
++ * @tfile: A struct ttm_object_file representing the application owning the
++ * ref_object.
++ * @base: The base object to reference.
++ * @ref_type: The type of reference.
++ * @existed: Upon completion, indicates that an identical reference object
++ * already existed, and the refcount was upped on that object instead.
++ *
++ * Adding a ref object to a base object is basically like referencing the
++ * base object, but a user-space application holds the reference. When the
++ * file corresponding to @tfile is closed, all its reference objects are
++ * deleted. A reference object can have different types depending on what
++ * it's intended for. It can be refcounting to prevent object destruction,
++ * When user-space takes a lock, it can add a ref object to that lock to
++ * make sure the lock is released if the application dies. A ref object
++ * will hold a single reference on a base object.
++ */
++extern int ttm_ref_object_add(struct ttm_object_file *tfile,
++ struct ttm_base_object *base,
++ enum ttm_ref_type ref_type, bool *existed);
++/**
++ * ttm_ref_object_base_unref
++ *
++ * @key: Key representing the base object.
++ * @ref_type: Ref type of the ref object to be dereferenced.
++ *
++ * Unreference a ref object with type @ref_type
++ * on the base object identified by @key. If there are no duplicate
++ * references, the ref object will be destroyed and the base object
++ * will be unreferenced.
++ */
++extern int ttm_ref_object_base_unref(struct ttm_object_file *tfile,
++ unsigned long key,
++ enum ttm_ref_type ref_type);
++
++/**
++ * ttm_object_file_init - initialize a struct ttm_object file
++ *
++ * @tdev: A struct ttm_object device this file is initialized on.
++ * @hash_order: Order of the hash table used to hold the reference objects.
++ *
++ * This is typically called by the file_ops::open function.
++ */
++
++extern struct ttm_object_file *ttm_object_file_init(struct ttm_object_device
++ *tdev,
++ unsigned int hash_order);
++
++/**
++ * ttm_object_file_release - release data held by a ttm_object_file
++ *
++ * @p_tfile: Pointer to pointer to the ttm_object_file object to release.
++ * *p_tfile will be set to NULL by this function.
++ *
++ * Releases all data associated by a ttm_object_file.
++ * Typically called from file_ops::release. The caller must
++ * ensure that there are no concurrent users of tfile.
++ */
++
++extern void ttm_object_file_release(struct ttm_object_file **p_tfile);
++
++/**
++ * ttm_object device init - initialize a struct ttm_object_device
++ *
++ * @hash_order: Order of hash table used to hash the base objects.
++ *
++ * This function is typically called on device initialization to prepare
++ * data structures needed for ttm base and ref objects.
++ */
++
++extern struct ttm_object_device *ttm_object_device_init
++ (struct ttm_mem_global *mem_glob, unsigned int hash_order);
++
++/**
++ * ttm_object_device_release - release data held by a ttm_object_device
++ *
++ * @p_tdev: Pointer to pointer to the ttm_object_device object to release.
++ * *p_tdev will be set to NULL by this function.
++ *
++ * Releases all data associated by a ttm_object_device.
++ * Typically called from driver::unload before the destruction of the
++ * device private data structure.
++ */
++
++extern void ttm_object_device_release(struct ttm_object_device **p_tdev);
++
++#endif
+diff --git a/include/drm/via_drm.h b/include/drm/via_drm.h
+index 170786e..fd11a5b 100644
+--- a/include/drm/via_drm.h
++++ b/include/drm/via_drm.h
+@@ -24,7 +24,7 @@
+ #ifndef _VIA_DRM_H_
+ #define _VIA_DRM_H_
+
+-#include <linux/types.h>
++#include "drm.h"
+
+ /* WARNING: These defines must be the same as what the Xserver uses.
+ * if you change them, you must change the defines in the Xserver.
+diff --git a/include/drm/vmwgfx_drm.h b/include/drm/vmwgfx_drm.h
+new file mode 100644
+index 0000000..c7645f4
+--- /dev/null
++++ b/include/drm/vmwgfx_drm.h
+@@ -0,0 +1,588 @@
++/**************************************************************************
++ *
++ * Copyright © 2009 VMware, Inc., Palo Alto, CA., USA
++ * All Rights Reserved.
++ *
++ * Permission is hereby granted, free of charge, to any person obtaining a
++ * copy of this software and associated documentation files (the
++ * "Software"), to deal in the Software without restriction, including
++ * without limitation the rights to use, copy, modify, merge, publish,
++ * distribute, sub license, and/or sell copies of the Software, and to
++ * permit persons to whom the Software is furnished to do so, subject to
++ * the following conditions:
++ *
++ * The above copyright notice and this permission notice (including the
++ * next paragraph) shall be included in all copies or substantial portions
++ * of the Software.
++ *
++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
++ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
++ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
++ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
++ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
++ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
++ * USE OR OTHER DEALINGS IN THE SOFTWARE.
++ *
++ **************************************************************************/
++
++#ifndef __VMWGFX_DRM_H__
++#define __VMWGFX_DRM_H__
++
++#define DRM_VMW_MAX_SURFACE_FACES 6
++#define DRM_VMW_MAX_MIP_LEVELS 24
++
++#define DRM_VMW_EXT_NAME_LEN 128
++
++#define DRM_VMW_GET_PARAM 0
++#define DRM_VMW_ALLOC_DMABUF 1
++#define DRM_VMW_UNREF_DMABUF 2
++#define DRM_VMW_CURSOR_BYPASS 3
++/* guarded by DRM_VMW_PARAM_NUM_STREAMS != 0*/
++#define DRM_VMW_CONTROL_STREAM 4
++#define DRM_VMW_CLAIM_STREAM 5
++#define DRM_VMW_UNREF_STREAM 6
++/* guarded by DRM_VMW_PARAM_3D == 1 */
++#define DRM_VMW_CREATE_CONTEXT 7
++#define DRM_VMW_UNREF_CONTEXT 8
++#define DRM_VMW_CREATE_SURFACE 9
++#define DRM_VMW_UNREF_SURFACE 10
++#define DRM_VMW_REF_SURFACE 11
++#define DRM_VMW_EXECBUF 12
++#define DRM_VMW_FIFO_DEBUG 13
++#define DRM_VMW_FENCE_WAIT 14
++
++
++/*************************************************************************/
++/**
++ * DRM_VMW_GET_PARAM - get device information.
++ *
++ * DRM_VMW_PARAM_FIFO_OFFSET:
++ * Offset to use to map the first page of the FIFO read-only.
++ * The fifo is mapped using the mmap() system call on the drm device.
++ *
++ * DRM_VMW_PARAM_OVERLAY_IOCTL:
++ * Does the driver support the overlay ioctl.
++ */
++
++#define DRM_VMW_PARAM_NUM_STREAMS 0
++#define DRM_VMW_PARAM_NUM_FREE_STREAMS 1
++#define DRM_VMW_PARAM_3D 2
++#define DRM_VMW_PARAM_FIFO_OFFSET 3
++#define DRM_VMW_PARAM_HW_CAPS 4
++#define DRM_VMW_PARAM_FIFO_CAPS 5
++
++/**
++ * struct drm_vmw_getparam_arg
++ *
++ * @value: Returned value. //Out
++ * @param: Parameter to query. //In.
++ *
++ * Argument to the DRM_VMW_GET_PARAM Ioctl.
++ */
++
++struct drm_vmw_getparam_arg {
++ uint64_t value;
++ uint32_t param;
++ uint32_t pad64;
++};
++
++/*************************************************************************/
++/**
++ * DRM_VMW_EXTENSION - Query device extensions.
++ */
++
++/**
++ * struct drm_vmw_extension_rep
++ *
++ * @exists: The queried extension exists.
++ * @driver_ioctl_offset: Ioctl number of the first ioctl in the extension.
++ * @driver_sarea_offset: Offset to any space in the DRI SAREA
++ * used by the extension.
++ * @major: Major version number of the extension.
++ * @minor: Minor version number of the extension.
++ * @pl: Patch level version number of the extension.
++ *
++ * Output argument to the DRM_VMW_EXTENSION Ioctl.
++ */
++
++struct drm_vmw_extension_rep {
++ int32_t exists;
++ uint32_t driver_ioctl_offset;
++ uint32_t driver_sarea_offset;
++ uint32_t major;
++ uint32_t minor;
++ uint32_t pl;
++ uint32_t pad64;
++};
++
++/**
++ * union drm_vmw_extension_arg
++ *
++ * @extension - Ascii name of the extension to be queried. //In
++ * @rep - Reply as defined above. //Out
++ *
++ * Argument to the DRM_VMW_EXTENSION Ioctl.
++ */
++
++union drm_vmw_extension_arg {
++ char extension[DRM_VMW_EXT_NAME_LEN];
++ struct drm_vmw_extension_rep rep;
++};
++
++/*************************************************************************/
++/**
++ * DRM_VMW_CREATE_CONTEXT - Create a host context.
++ *
++ * Allocates a device unique context id, and queues a create context command
++ * for the host. Does not wait for host completion.
++ */
++
++/**
++ * struct drm_vmw_context_arg
++ *
++ * @cid: Device unique context ID.
++ *
++ * Output argument to the DRM_VMW_CREATE_CONTEXT Ioctl.
++ * Input argument to the DRM_VMW_UNREF_CONTEXT Ioctl.
++ */
++
++struct drm_vmw_context_arg {
++ int32_t cid;
++ uint32_t pad64;
++};
++
++/*************************************************************************/
++/**
++ * DRM_VMW_UNREF_CONTEXT - Create a host context.
++ *
++ * Frees a global context id, and queues a destroy host command for the host.
++ * Does not wait for host completion. The context ID can be used directly
++ * in the command stream and shows up as the same context ID on the host.
++ */
++
++/*************************************************************************/
++/**
++ * DRM_VMW_CREATE_SURFACE - Create a host suface.
++ *
++ * Allocates a device unique surface id, and queues a create surface command
++ * for the host. Does not wait for host completion. The surface ID can be
++ * used directly in the command stream and shows up as the same surface
++ * ID on the host.
++ */
++
++/**
++ * struct drm_wmv_surface_create_req
++ *
++ * @flags: Surface flags as understood by the host.
++ * @format: Surface format as understood by the host.
++ * @mip_levels: Number of mip levels for each face.
++ * An unused face should have 0 encoded.
++ * @size_addr: Address of a user-space array of sruct drm_vmw_size
++ * cast to an uint64_t for 32-64 bit compatibility.
++ * The size of the array should equal the total number of mipmap levels.
++ * @shareable: Boolean whether other clients (as identified by file descriptors)
++ * may reference this surface.
++ * @scanout: Boolean whether the surface is intended to be used as a
++ * scanout.
++ *
++ * Input data to the DRM_VMW_CREATE_SURFACE Ioctl.
++ * Output data from the DRM_VMW_REF_SURFACE Ioctl.
++ */
++
++struct drm_vmw_surface_create_req {
++ uint32_t flags;
++ uint32_t format;
++ uint32_t mip_levels[DRM_VMW_MAX_SURFACE_FACES];
++ uint64_t size_addr;
++ int32_t shareable;
++ int32_t scanout;
++};
++
++/**
++ * struct drm_wmv_surface_arg
++ *
++ * @sid: Surface id of created surface or surface to destroy or reference.
++ *
++ * Output data from the DRM_VMW_CREATE_SURFACE Ioctl.
++ * Input argument to the DRM_VMW_UNREF_SURFACE Ioctl.
++ * Input argument to the DRM_VMW_REF_SURFACE Ioctl.
++ */
++
++struct drm_vmw_surface_arg {
++ int32_t sid;
++ uint32_t pad64;
++};
++
++/**
++ * struct drm_vmw_size ioctl.
++ *
++ * @width - mip level width
++ * @height - mip level height
++ * @depth - mip level depth
++ *
++ * Description of a mip level.
++ * Input data to the DRM_WMW_CREATE_SURFACE Ioctl.
++ */
++
++struct drm_vmw_size {
++ uint32_t width;
++ uint32_t height;
++ uint32_t depth;
++ uint32_t pad64;
++};
++
++/**
++ * union drm_vmw_surface_create_arg
++ *
++ * @rep: Output data as described above.
++ * @req: Input data as described above.
++ *
++ * Argument to the DRM_VMW_CREATE_SURFACE Ioctl.
++ */
++
++union drm_vmw_surface_create_arg {
++ struct drm_vmw_surface_arg rep;
++ struct drm_vmw_surface_create_req req;
++};
++
++/*************************************************************************/
++/**
++ * DRM_VMW_REF_SURFACE - Reference a host surface.
++ *
++ * Puts a reference on a host surface with a give sid, as previously
++ * returned by the DRM_VMW_CREATE_SURFACE ioctl.
++ * A reference will make sure the surface isn't destroyed while we hold
++ * it and will allow the calling client to use the surface ID in the command
++ * stream.
++ *
++ * On successful return, the Ioctl returns the surface information given
++ * in the DRM_VMW_CREATE_SURFACE ioctl.
++ */
++
++/**
++ * union drm_vmw_surface_reference_arg
++ *
++ * @rep: Output data as described above.
++ * @req: Input data as described above.
++ *
++ * Argument to the DRM_VMW_REF_SURFACE Ioctl.
++ */
++
++union drm_vmw_surface_reference_arg {
++ struct drm_vmw_surface_create_req rep;
++ struct drm_vmw_surface_arg req;
++};
++
++/*************************************************************************/
++/**
++ * DRM_VMW_UNREF_SURFACE - Unreference a host surface.
++ *
++ * Clear a reference previously put on a host surface.
++ * When all references are gone, including the one implicitly placed
++ * on creation,
++ * a destroy surface command will be queued for the host.
++ * Does not wait for completion.
++ */
++
++/*************************************************************************/
++/**
++ * DRM_VMW_EXECBUF
++ *
++ * Submit a command buffer for execution on the host, and return a
++ * fence sequence that when signaled, indicates that the command buffer has
++ * executed.
++ */
++
++/**
++ * struct drm_vmw_execbuf_arg
++ *
++ * @commands: User-space address of a command buffer cast to an uint64_t.
++ * @command-size: Size in bytes of the command buffer.
++ * @throttle-us: Sleep until software is less than @throttle_us
++ * microseconds ahead of hardware. The driver may round this value
++ * to the nearest kernel tick.
++ * @fence_rep: User-space address of a struct drm_vmw_fence_rep cast to an
++ * uint64_t.
++ * @version: Allows expanding the execbuf ioctl parameters without breaking
++ * backwards compatibility, since user-space will always tell the kernel
++ * which version it uses.
++ * @flags: Execbuf flags. None currently.
++ *
++ * Argument to the DRM_VMW_EXECBUF Ioctl.
++ */
++
++#define DRM_VMW_EXECBUF_VERSION 0
++
++struct drm_vmw_execbuf_arg {
++ uint64_t commands;
++ uint32_t command_size;
++ uint32_t throttle_us;
++ uint64_t fence_rep;
++ uint32_t version;
++ uint32_t flags;
++};
++
++/**
++ * struct drm_vmw_fence_rep
++ *
++ * @fence_seq: Fence sequence associated with a command submission.
++ * @error: This member should've been set to -EFAULT on submission.
++ * The following actions should be take on completion:
++ * error == -EFAULT: Fence communication failed. The host is synchronized.
++ * Use the last fence id read from the FIFO fence register.
++ * error != 0 && error != -EFAULT:
++ * Fence submission failed. The host is synchronized. Use the fence_seq member.
++ * error == 0: All is OK, The host may not be synchronized.
++ * Use the fence_seq member.
++ *
++ * Input / Output data to the DRM_VMW_EXECBUF Ioctl.
++ */
++
++struct drm_vmw_fence_rep {
++ uint64_t fence_seq;
++ int32_t error;
++ uint32_t pad64;
++};
++
++/*************************************************************************/
++/**
++ * DRM_VMW_ALLOC_DMABUF
++ *
++ * Allocate a DMA buffer that is visible also to the host.
++ * NOTE: The buffer is
++ * identified by a handle and an offset, which are private to the guest, but
++ * useable in the command stream. The guest kernel may translate these
++ * and patch up the command stream accordingly. In the future, the offset may
++ * be zero at all times, or it may disappear from the interface before it is
++ * fixed.
++ *
++ * The DMA buffer may stay user-space mapped in the guest at all times,
++ * and is thus suitable for sub-allocation.
++ *
++ * DMA buffers are mapped using the mmap() syscall on the drm device.
++ */
++
++/**
++ * struct drm_vmw_alloc_dmabuf_req
++ *
++ * @size: Required minimum size of the buffer.
++ *
++ * Input data to the DRM_VMW_ALLOC_DMABUF Ioctl.
++ */
++
++struct drm_vmw_alloc_dmabuf_req {
++ uint32_t size;
++ uint32_t pad64;
++};
++
++/**
++ * struct drm_vmw_dmabuf_rep
++ *
++ * @map_handle: Offset to use in the mmap() call used to map the buffer.
++ * @handle: Handle unique to this buffer. Used for unreferencing.
++ * @cur_gmr_id: GMR id to use in the command stream when this buffer is
++ * referenced. See not above.
++ * @cur_gmr_offset: Offset to use in the command stream when this buffer is
++ * referenced. See note above.
++ *
++ * Output data from the DRM_VMW_ALLOC_DMABUF Ioctl.
++ */
++
++struct drm_vmw_dmabuf_rep {
++ uint64_t map_handle;
++ uint32_t handle;
++ uint32_t cur_gmr_id;
++ uint32_t cur_gmr_offset;
++ uint32_t pad64;
++};
++
++/**
++ * union drm_vmw_dmabuf_arg
++ *
++ * @req: Input data as described above.
++ * @rep: Output data as described above.
++ *
++ * Argument to the DRM_VMW_ALLOC_DMABUF Ioctl.
++ */
++
++union drm_vmw_alloc_dmabuf_arg {
++ struct drm_vmw_alloc_dmabuf_req req;
++ struct drm_vmw_dmabuf_rep rep;
++};
++
++/*************************************************************************/
++/**
++ * DRM_VMW_UNREF_DMABUF - Free a DMA buffer.
++ *
++ */
++
++/**
++ * struct drm_vmw_unref_dmabuf_arg
++ *
++ * @handle: Handle indicating what buffer to free. Obtained from the
++ * DRM_VMW_ALLOC_DMABUF Ioctl.
++ *
++ * Argument to the DRM_VMW_UNREF_DMABUF Ioctl.
++ */
++
++struct drm_vmw_unref_dmabuf_arg {
++ uint32_t handle;
++ uint32_t pad64;
++};
++
++/*************************************************************************/
++/**
++ * DRM_VMW_FIFO_DEBUG - Get last FIFO submission.
++ *
++ * This IOCTL copies the last FIFO submission directly out of the FIFO buffer.
++ */
++
++/**
++ * struct drm_vmw_fifo_debug_arg
++ *
++ * @debug_buffer: User space address of a debug_buffer cast to an uint64_t //In
++ * @debug_buffer_size: Size in bytes of debug buffer //In
++ * @used_size: Number of bytes copied to the buffer // Out
++ * @did_not_fit: Boolean indicating that the fifo contents did not fit. //Out
++ *
++ * Argument to the DRM_VMW_FIFO_DEBUG Ioctl.
++ */
++
++struct drm_vmw_fifo_debug_arg {
++ uint64_t debug_buffer;
++ uint32_t debug_buffer_size;
++ uint32_t used_size;
++ int32_t did_not_fit;
++ uint32_t pad64;
++};
++
++struct drm_vmw_fence_wait_arg {
++ uint64_t sequence;
++ uint64_t kernel_cookie;
++ int32_t cookie_valid;
++ int32_t pad64;
++};
++
++/*************************************************************************/
++/**
++ * DRM_VMW_CONTROL_STREAM - Control overlays, aka streams.
++ *
++ * This IOCTL controls the overlay units of the svga device.
++ * The SVGA overlay units does not work like regular hardware units in
++ * that they do not automaticaly read back the contents of the given dma
++ * buffer. But instead only read back for each call to this ioctl, and
++ * at any point between this call being made and a following call that
++ * either changes the buffer or disables the stream.
++ */
++
++/**
++ * struct drm_vmw_rect
++ *
++ * Defines a rectangle. Used in the overlay ioctl to define
++ * source and destination rectangle.
++ */
++
++struct drm_vmw_rect {
++ int32_t x;
++ int32_t y;
++ uint32_t w;
++ uint32_t h;
++};
++
++/**
++ * struct drm_vmw_control_stream_arg
++ *
++ * @stream_id: Stearm to control
++ * @enabled: If false all following arguments are ignored.
++ * @handle: Handle to buffer for getting data from.
++ * @format: Format of the overlay as understood by the host.
++ * @width: Width of the overlay.
++ * @height: Height of the overlay.
++ * @size: Size of the overlay in bytes.
++ * @pitch: Array of pitches, the two last are only used for YUV12 formats.
++ * @offset: Offset from start of dma buffer to overlay.
++ * @src: Source rect, must be within the defined area above.
++ * @dst: Destination rect, x and y may be negative.
++ *
++ * Argument to the DRM_VMW_CONTROL_STREAM Ioctl.
++ */
++
++struct drm_vmw_control_stream_arg {
++ uint32_t stream_id;
++ uint32_t enabled;
++
++ uint32_t flags;
++ uint32_t color_key;
++
++ uint32_t handle;
++ uint32_t offset;
++ int32_t format;
++ uint32_t size;
++ uint32_t width;
++ uint32_t height;
++ uint32_t pitch[3];
++
++ uint32_t pad64;
++ struct drm_vmw_rect src;
++ struct drm_vmw_rect dst;
++};
++
++/*************************************************************************/
++/**
++ * DRM_VMW_CURSOR_BYPASS - Give extra information about cursor bypass.
++ *
++ */
++
++#define DRM_VMW_CURSOR_BYPASS_ALL (1 << 0)
++#define DRM_VMW_CURSOR_BYPASS_FLAGS (1)
++
++/**
++ * struct drm_vmw_cursor_bypass_arg
++ *
++ * @flags: Flags.
++ * @crtc_id: Crtc id, only used if DMR_CURSOR_BYPASS_ALL isn't passed.
++ * @xpos: X position of cursor.
++ * @ypos: Y position of cursor.
++ * @xhot: X hotspot.
++ * @yhot: Y hotspot.
++ *
++ * Argument to the DRM_VMW_CURSOR_BYPASS Ioctl.
++ */
++
++struct drm_vmw_cursor_bypass_arg {
++ uint32_t flags;
++ uint32_t crtc_id;
++ int32_t xpos;
++ int32_t ypos;
++ int32_t xhot;
++ int32_t yhot;
++};
++
++/*************************************************************************/
++/**
++ * DRM_VMW_CLAIM_STREAM - Claim a single stream.
++ */
++
++/**
++ * struct drm_vmw_context_arg
++ *
++ * @stream_id: Device unique context ID.
++ *
++ * Output argument to the DRM_VMW_CREATE_CONTEXT Ioctl.
++ * Input argument to the DRM_VMW_UNREF_CONTEXT Ioctl.
++ */
++
++struct drm_vmw_stream_arg {
++ uint32_t stream_id;
++ uint32_t pad64;
++};
++
++/*************************************************************************/
++/**
++ * DRM_VMW_UNREF_STREAM - Unclaim a stream.
++ *
++ * Return a single stream that was claimed by this process. Also makes
++ * sure that the stream has been stopped.
++ */
++
++#endif
+diff --git a/include/linux/list_sort.h b/include/linux/list_sort.h
+new file mode 100644
+index 0000000..1a2df2e
+--- /dev/null
++++ b/include/linux/list_sort.h
+@@ -0,0 +1,11 @@
++#ifndef _LINUX_LIST_SORT_H
++#define _LINUX_LIST_SORT_H
++
++#include <linux/types.h>
++
++struct list_head;
++
++void list_sort(void *priv, struct list_head *head,
++ int (*cmp)(void *priv, struct list_head *a,
++ struct list_head *b));
++#endif
+diff --git a/lib/Makefile b/lib/Makefile
+index 2e78277..bae1a76 100644
+--- a/lib/Makefile
++++ b/lib/Makefile
+@@ -21,7 +21,7 @@ lib-y += kobject.o kref.o klist.o
+
+ obj-y += bcd.o div64.o sort.o parser.o halfmd4.o debug_locks.o random32.o \
+ bust_spinlocks.o hexdump.o kasprintf.o bitmap.o scatterlist.o \
+- string_helpers.o gcd.o lcm.o
++ string_helpers.o gcd.o lcm.o list_sort.o
+
+ ifeq ($(CONFIG_DEBUG_KOBJECT),y)
+ CFLAGS_kobject.o += -DDEBUG
+diff --git a/lib/list_sort.c b/lib/list_sort.c
+new file mode 100644
+index 0000000..19d11e0
+--- /dev/null
++++ b/lib/list_sort.c
+@@ -0,0 +1,102 @@
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/list_sort.h>
++#include <linux/slab.h>
++#include <linux/list.h>
++
++/**
++ * list_sort - sort a list.
++ * @priv: private data, passed to @cmp
++ * @head: the list to sort
++ * @cmp: the elements comparison function
++ *
++ * This function has been implemented by Mark J Roberts <mjr@znex.org>. It
++ * implements "merge sort" which has O(nlog(n)) complexity. The list is sorted
++ * in ascending order.
++ *
++ * The comparison function @cmp is supposed to return a negative value if @a is
++ * less than @b, and a positive value if @a is greater than @b. If @a and @b
++ * are equivalent, then it does not matter what this function returns.
++ */
++void list_sort(void *priv, struct list_head *head,
++ int (*cmp)(void *priv, struct list_head *a,
++ struct list_head *b))
++{
++ struct list_head *p, *q, *e, *list, *tail, *oldhead;
++ int insize, nmerges, psize, qsize, i;
++
++ if (list_empty(head))
++ return;
++
++ list = head->next;
++ list_del(head);
++ insize = 1;
++ for (;;) {
++ p = oldhead = list;
++ list = tail = NULL;
++ nmerges = 0;
++
++ while (p) {
++ nmerges++;
++ q = p;
++ psize = 0;
++ for (i = 0; i < insize; i++) {
++ psize++;
++ q = q->next == oldhead ? NULL : q->next;
++ if (!q)
++ break;
++ }
++
++ qsize = insize;
++ while (psize > 0 || (qsize > 0 && q)) {
++ if (!psize) {
++ e = q;
++ q = q->next;
++ qsize--;
++ if (q == oldhead)
++ q = NULL;
++ } else if (!qsize || !q) {
++ e = p;
++ p = p->next;
++ psize--;
++ if (p == oldhead)
++ p = NULL;
++ } else if (cmp(priv, p, q) <= 0) {
++ e = p;
++ p = p->next;
++ psize--;
++ if (p == oldhead)
++ p = NULL;
++ } else {
++ e = q;
++ q = q->next;
++ qsize--;
++ if (q == oldhead)
++ q = NULL;
++ }
++ if (tail)
++ tail->next = e;
++ else
++ list = e;
++ e->prev = tail;
++ tail = e;
++ }
++ p = q;
++ }
++
++ tail->next = list;
++ list->prev = tail;
++
++ if (nmerges <= 1)
++ break;
++
++ insize *= 2;
++ }
++
++ head->next = list;
++ head->prev = list->prev;
++ list->prev->next = head;
++ list->prev = head;
++}
++
++EXPORT_SYMBOL(list_sort);
diff --git a/ethtool-fix-buffer-overflow.patch b/ethtool-fix-buffer-overflow.patch
new file mode 100644
index 0000000..01b1a41
--- /dev/null
+++ b/ethtool-fix-buffer-overflow.patch
@@ -0,0 +1,33 @@
+From: Ben Hutchings <bhutchings@solarflare.com>
+Date: Mon, 28 Jun 2010 08:44:07 +0000 (+0000)
+Subject: ethtool: Fix potential kernel buffer overflow in ETHTOOL_GRXCLSRLALL
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Fdavem%2Fnet-2.6.git;a=commitdiff_plain;h=db048b69037e7fa6a7d9e95a1271a50dc08ae233
+
+ethtool: Fix potential kernel buffer overflow in ETHTOOL_GRXCLSRLALL
+
+On a 32-bit machine, info.rule_cnt >= 0x40000000 leads to integer
+overflow and the buffer may be smaller than needed. Since
+ETHTOOL_GRXCLSRLALL is unprivileged, this can presumably be used for at
+least denial of service.
+
+Signed-off-by: Ben Hutchings <bhutchings@solarflare.com>
+Cc: stable@kernel.org
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+diff --git a/net/core/ethtool.c b/net/core/ethtool.c
+index a0f4964..a3a7e9a 100644
+--- a/net/core/ethtool.c
++++ b/net/core/ethtool.c
+@@ -347,8 +347,9 @@ static noinline_for_stack int ethtool_get_rxnfc(struct net_device *dev,
+
+ if (info.cmd == ETHTOOL_GRXCLSRLALL) {
+ if (info.rule_cnt > 0) {
+- rule_buf = kmalloc(info.rule_cnt * sizeof(u32),
+- GFP_USER);
++ if (info.rule_cnt <= KMALLOC_MAX_SIZE / sizeof(u32))
++ rule_buf = kmalloc(info.rule_cnt * sizeof(u32),
++ GFP_USER);
+ if (!rule_buf)
+ return -ENOMEM;
+ }
diff --git a/ext4-issue-discard-operation-before-releasing-blocks.patch b/ext4-issue-discard-operation-before-releasing-blocks.patch
new file mode 100644
index 0000000..aa103bd
--- /dev/null
+++ b/ext4-issue-discard-operation-before-releasing-blocks.patch
@@ -0,0 +1,62 @@
+From: Theodore Ts'o <tytso@mit.edu>
+Date: Tue, 20 Apr 2010 20:51:59 +0000 (-0400)
+Subject: ext4: Issue the discard operation *before* releasing the blocks to be reused
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=b90f687018e6d6c77d981b09203780f7001407e5
+
+ext4: Issue the discard operation *before* releasing the blocks to be reused
+
+[ backported to 2.6.3[23] ]
+
+Otherwise, we can end up having data corruption because the blocks
+could get reused and then discarded!
+
+https://bugzilla.kernel.org/show_bug.cgi?id=15579
+
+Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
+---
+
+diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c
+index 54df209..e5ab41b 100644
+--- a/fs/ext4/mballoc.c
++++ b/fs/ext4/mballoc.c
+@@ -2534,6 +2534,20 @@ static void release_blocks_on_commit(journal_t *journal, transaction_t *txn)
+ mb_debug(1, "gonna free %u blocks in group %u (0x%p):",
+ entry->count, entry->group, entry);
+
++ if (test_opt(sb, DISCARD)) {
++ ext4_fsblk_t discard_block;
++ struct ext4_super_block *es = EXT4_SB(sb)->s_es;
++
++ discard_block = (ext4_fsblk_t)entry->group *
++ EXT4_BLOCKS_PER_GROUP(sb)
++ + entry->start_blk
++ + le32_to_cpu(es->s_first_data_block);
++ trace_ext4_discard_blocks(sb,
++ (unsigned long long)discard_block,
++ entry->count);
++ sb_issue_discard(sb, discard_block, entry->count);
++ }
++
+ err = ext4_mb_load_buddy(sb, entry->group, &e4b);
+ /* we expect to find existing buddy because it's pinned */
+ BUG_ON(err != 0);
+@@ -2555,19 +2566,6 @@ static void release_blocks_on_commit(journal_t *journal, transaction_t *txn)
+ page_cache_release(e4b.bd_bitmap_page);
+ }
+ ext4_unlock_group(sb, entry->group);
+- if (test_opt(sb, DISCARD)) {
+- ext4_fsblk_t discard_block;
+- struct ext4_super_block *es = EXT4_SB(sb)->s_es;
+-
+- discard_block = (ext4_fsblk_t)entry->group *
+- EXT4_BLOCKS_PER_GROUP(sb)
+- + entry->start_blk
+- + le32_to_cpu(es->s_first_data_block);
+- trace_ext4_discard_blocks(sb,
+- (unsigned long long)discard_block,
+- entry->count);
+- sb_issue_discard(sb, discard_block, entry->count);
+- }
+ kmem_cache_free(ext4_free_ext_cachep, entry);
+ ext4_mb_release_desc(&e4b);
+ }
diff --git a/ext4-make-sure-the-move_ext-ioctl-can-t-overwrite-append-only-files.patch b/ext4-make-sure-the-move_ext-ioctl-can-t-overwrite-append-only-files.patch
new file mode 100644
index 0000000..14407a5
--- /dev/null
+++ b/ext4-make-sure-the-move_ext-ioctl-can-t-overwrite-append-only-files.patch
@@ -0,0 +1,34 @@
+From 1f5a81e41f8b1a782c68d3843e9ec1bfaadf7d72 Mon Sep 17 00:00:00 2001
+From: Theodore Ts'o <tytso@mit.edu>
+Date: Wed, 2 Jun 2010 22:04:39 -0400
+Subject: ext4: Make sure the MOVE_EXT ioctl can't overwrite append-only files
+
+From: Theodore Ts'o <tytso@mit.edu>
+
+commit 1f5a81e41f8b1a782c68d3843e9ec1bfaadf7d72 upstream.
+
+Dan Roseberg has reported a problem with the MOVE_EXT ioctl. If the
+donor file is an append-only file, we should not allow the operation
+to proceed, lest we end up overwriting the contents of an append-only
+file.
+
+Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
+Cc: Dan Rosenberg <dan.j.rosenberg@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ fs/ext4/move_extent.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/fs/ext4/move_extent.c
++++ b/fs/ext4/move_extent.c
+@@ -959,6 +959,9 @@ mext_check_arguments(struct inode *orig_
+ return -EINVAL;
+ }
+
++ if (IS_IMMUTABLE(donor_inode) || IS_APPEND(donor_inode))
++ return -EPERM;
++
+ /* Ext4 move extent does not support swapfile */
+ if (IS_SWAPFILE(orig_inode) || IS_SWAPFILE(donor_inode)) {
+ ext4_debug("ext4 move extent: The argument files should "
diff --git a/find-provides b/find-provides
new file mode 100755
index 0000000..b28d102
--- /dev/null
+++ b/find-provides
@@ -0,0 +1,44 @@
+#!/usr/bin/python
+#
+# find-provides: munge the provides dependencies from the kabideps file
+#
+# This software may be freely redistributed under the terms of the GNU
+# General Public License (GPL).
+#
+# Takes a directory prefix, then outputs the kabideps file contents.
+
+__author__ = "Jon Masters <jcm@redhat.com>"
+__version__ = "1.0"
+__date__ = "Tue 25 Jul 2006 04:00 GMT"
+__copyright__ = "Copyright (C) 2006 Red Hat, Inc"
+__license__ = "GPL"
+
+import os
+import re
+import string
+import sys
+
+false = 0
+true = 1
+
+kabideps=""
+
+p = re.compile('^(.*)/symvers-(.*).gz$')
+while true:
+ foo = sys.stdin.readline()
+ if foo == "":
+ break
+ string.split(foo)
+ m = p.match(foo)
+ if m:
+ kabideps=sys.argv[1] + "/kernel-" + m.group(2) + "-kabideps"
+
+if kabideps == "":
+ sys.exit(0)
+
+if not (os.path.isfile(kabideps)):
+ sys.stderr.write(sys.argv[0] + ": cannot locate kabideps file: " + kabideps + "\n")
+ sys.exit(1)
+
+sys.stderr.write(sys.argv[0] + ": processing kABI: " + kabideps)
+os.system("cat " + kabideps)
diff --git a/fix-abrtd.patch b/fix-abrtd.patch
new file mode 100644
index 0000000..4a8db58
--- /dev/null
+++ b/fix-abrtd.patch
@@ -0,0 +1,774 @@
+diff --git a/fs/binfmt_aout.c b/fs/binfmt_aout.c
+index b639dcf..346b694 100644
+--- a/fs/binfmt_aout.c
++++ b/fs/binfmt_aout.c
+@@ -32,7 +32,7 @@
+
+ static int load_aout_binary(struct linux_binprm *, struct pt_regs * regs);
+ static int load_aout_library(struct file*);
+-static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
++static int aout_core_dump(struct coredump_params *cprm);
+
+ static struct linux_binfmt aout_format = {
+ .module = THIS_MODULE,
+@@ -89,8 +89,9 @@ if (file->f_op->llseek) { \
+ * dumping of the process results in another error..
+ */
+
+-static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit)
++static int aout_core_dump(struct coredump_params *cprm)
+ {
++ struct file *file = cprm->file;
+ mm_segment_t fs;
+ int has_dumped = 0;
+ unsigned long dump_start, dump_size;
+@@ -108,16 +109,16 @@ static int aout_core_dump(long signr, struct pt_regs *regs, struct file *file, u
+ current->flags |= PF_DUMPCORE;
+ strncpy(dump.u_comm, current->comm, sizeof(dump.u_comm));
+ dump.u_ar0 = offsetof(struct user, regs);
+- dump.signal = signr;
+- aout_dump_thread(regs, &dump);
++ dump.signal = cprm->signr;
++ aout_dump_thread(cprm->regs, &dump);
+
+ /* If the size of the dump file exceeds the rlimit, then see what would happen
+ if we wrote the stack, but not the data area. */
+- if ((dump.u_dsize + dump.u_ssize+1) * PAGE_SIZE > limit)
++ if ((dump.u_dsize + dump.u_ssize+1) * PAGE_SIZE > cprm->limit)
+ dump.u_dsize = 0;
+
+ /* Make sure we have enough room to write the stack and data areas. */
+- if ((dump.u_ssize + 1) * PAGE_SIZE > limit)
++ if ((dump.u_ssize + 1) * PAGE_SIZE > cprm->limit)
+ dump.u_ssize = 0;
+
+ /* make sure we actually have a data and stack area to dump */
+diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
+index b9b3bb5..4ee5bb2 100644
+--- a/fs/binfmt_elf.c
++++ b/fs/binfmt_elf.c
+@@ -45,7 +45,7 @@ static unsigned long elf_map(struct file *, unsigned long, struct elf_phdr *,
+ * don't even try.
+ */
+ #if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE)
+-static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
++static int elf_core_dump(struct coredump_params *cprm);
+ #else
+ #define elf_core_dump NULL
+ #endif
+@@ -1277,8 +1277,9 @@ static int writenote(struct memelfnote *men, struct file *file,
+ }
+ #undef DUMP_WRITE
+
+-#define DUMP_WRITE(addr, nr) \
+- if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \
++#define DUMP_WRITE(addr, nr) \
++ if ((size += (nr)) > cprm->limit || \
++ !dump_write(cprm->file, (addr), (nr))) \
+ goto end_coredump;
+
+ static void fill_elf_header(struct elfhdr *elf, int segs,
+@@ -1906,7 +1907,7 @@ static struct vm_area_struct *next_vma(struct vm_area_struct *this_vma,
+ * and then they are actually written out. If we run out of core limit
+ * we just truncate.
+ */
+-static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit)
++static int elf_core_dump(struct coredump_params *cprm)
+ {
+ int has_dumped = 0;
+ mm_segment_t fs;
+@@ -1952,7 +1953,7 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
+ * notes. This also sets up the file header.
+ */
+ if (!fill_note_info(elf, segs + 1, /* including notes section */
+- &info, signr, regs))
++ &info, cprm->signr, cprm->regs))
+ goto cleanup;
+
+ has_dumped = 1;
+@@ -2014,14 +2015,14 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
+ #endif
+
+ /* write out the notes section */
+- if (!write_note_info(&info, file, &foffset))
++ if (!write_note_info(&info, cprm->file, &foffset))
+ goto end_coredump;
+
+- if (elf_coredump_extra_notes_write(file, &foffset))
++ if (elf_coredump_extra_notes_write(cprm->file, &foffset))
+ goto end_coredump;
+
+ /* Align to page */
+- if (!dump_seek(file, dataoff - foffset))
++ if (!dump_seek(cprm->file, dataoff - foffset))
+ goto end_coredump;
+
+ for (vma = first_vma(current, gate_vma); vma != NULL;
+@@ -2038,12 +2039,13 @@ static int elf_core_dump(long signr, struct pt_regs *regs, struct file *file, un
+ page = get_dump_page(addr);
+ if (page) {
+ void *kaddr = kmap(page);
+- stop = ((size += PAGE_SIZE) > limit) ||
+- !dump_write(file, kaddr, PAGE_SIZE);
++ stop = ((size += PAGE_SIZE) > cprm->limit) ||
++ !dump_write(cprm->file, kaddr,
++ PAGE_SIZE);
+ kunmap(page);
+ page_cache_release(page);
+ } else
+- stop = !dump_seek(file, PAGE_SIZE);
++ stop = !dump_seek(cprm->file, PAGE_SIZE);
+ if (stop)
+ goto end_coredump;
+ }
+diff --git a/fs/binfmt_elf_fdpic.c b/fs/binfmt_elf_fdpic.c
+index 38502c6..917e1b4 100644
+--- a/fs/binfmt_elf_fdpic.c
++++ b/fs/binfmt_elf_fdpic.c
+@@ -76,7 +76,7 @@ static int elf_fdpic_map_file_by_direct_mmap(struct elf_fdpic_params *,
+ struct file *, struct mm_struct *);
+
+ #if defined(USE_ELF_CORE_DUMP) && defined(CONFIG_ELF_CORE)
+-static int elf_fdpic_core_dump(long, struct pt_regs *, struct file *, unsigned long limit);
++static int elf_fdpic_core_dump(struct coredump_params *cprm);
+ #endif
+
+ static struct linux_binfmt elf_fdpic_format = {
+@@ -1325,8 +1325,9 @@ static int writenote(struct memelfnote *men, struct file *file)
+ #undef DUMP_WRITE
+ #undef DUMP_SEEK
+
+-#define DUMP_WRITE(addr, nr) \
+- if ((size += (nr)) > limit || !dump_write(file, (addr), (nr))) \
++#define DUMP_WRITE(addr, nr) \
++ if ((size += (nr)) > cprm->limit || \
++ !dump_write(cprm->file, (addr), (nr))) \
+ goto end_coredump;
+
+ static inline void fill_elf_fdpic_header(struct elfhdr *elf, int segs)
+@@ -1581,8 +1582,7 @@ static int elf_fdpic_dump_segments(struct file *file, size_t *size,
+ * and then they are actually written out. If we run out of core limit
+ * we just truncate.
+ */
+-static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
+- struct file *file, unsigned long limit)
++static int elf_fdpic_core_dump(struct coredump_params *cprm)
+ {
+ #define NUM_NOTES 6
+ int has_dumped = 0;
+@@ -1641,7 +1641,7 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
+ goto cleanup;
+ #endif
+
+- if (signr) {
++ if (cprm->signr) {
+ struct core_thread *ct;
+ struct elf_thread_status *tmp;
+
+@@ -1660,14 +1660,14 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
+ int sz;
+
+ tmp = list_entry(t, struct elf_thread_status, list);
+- sz = elf_dump_thread_status(signr, tmp);
++ sz = elf_dump_thread_status(cprm->signr, tmp);
+ thread_status_size += sz;
+ }
+ }
+
+ /* now collect the dump for the current */
+- fill_prstatus(prstatus, current, signr);
+- elf_core_copy_regs(&prstatus->pr_reg, regs);
++ fill_prstatus(prstatus, current, cprm->signr);
++ elf_core_copy_regs(&prstatus->pr_reg, cprm->regs);
+
+ segs = current->mm->map_count;
+ #ifdef ELF_CORE_EXTRA_PHDRS
+@@ -1702,7 +1702,7 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
+
+ /* Try to dump the FPU. */
+ if ((prstatus->pr_fpvalid =
+- elf_core_copy_task_fpregs(current, regs, fpu)))
++ elf_core_copy_task_fpregs(current, cprm->regs, fpu)))
+ fill_note(notes + numnote++,
+ "CORE", NT_PRFPREG, sizeof(*fpu), fpu);
+ #ifdef ELF_CORE_COPY_XFPREGS
+@@ -1773,7 +1773,7 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
+
+ /* write out the notes section */
+ for (i = 0; i < numnote; i++)
+- if (!writenote(notes + i, file))
++ if (!writenote(notes + i, cprm->file))
+ goto end_coredump;
+
+ /* write out the thread status notes section */
+@@ -1782,14 +1782,15 @@ static int elf_fdpic_core_dump(long signr, struct pt_regs *regs,
+ list_entry(t, struct elf_thread_status, list);
+
+ for (i = 0; i < tmp->num_notes; i++)
+- if (!writenote(&tmp->notes[i], file))
++ if (!writenote(&tmp->notes[i], cprm->file))
+ goto end_coredump;
+ }
+
+- if (!dump_seek(file, dataoff))
++ if (!dump_seek(cprm->file, dataoff))
+ goto end_coredump;
+
+- if (elf_fdpic_dump_segments(file, &size, &limit, mm_flags) < 0)
++ if (elf_fdpic_dump_segments(cprm->file, &size, &cprm->limit,
++ mm_flags) < 0)
+ goto end_coredump;
+
+ #ifdef ELF_CORE_WRITE_EXTRA_DATA
+diff --git a/fs/binfmt_flat.c b/fs/binfmt_flat.c
+index a279665..d4a00ea 100644
+--- a/fs/binfmt_flat.c
++++ b/fs/binfmt_flat.c
+@@ -87,7 +87,7 @@ static int load_flat_shared_library(int id, struct lib_info *p);
+ #endif
+
+ static int load_flat_binary(struct linux_binprm *, struct pt_regs * regs);
+-static int flat_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
++static int flat_core_dump(struct coredump_params *cprm);
+
+ static struct linux_binfmt flat_format = {
+ .module = THIS_MODULE,
+@@ -102,10 +102,10 @@ static struct linux_binfmt flat_format = {
+ * Currently only a stub-function.
+ */
+
+-static int flat_core_dump(long signr, struct pt_regs *regs, struct file *file, unsigned long limit)
++static int flat_core_dump(struct coredump_params *cprm)
+ {
+ printk("Process %s:%d received signr %d and should have core dumped\n",
+- current->comm, current->pid, (int) signr);
++ current->comm, current->pid, (int) cprm->signr);
+ return(1);
+ }
+
+diff --git a/fs/binfmt_som.c b/fs/binfmt_som.c
+index eff74b9..2a9b533 100644
+--- a/fs/binfmt_som.c
++++ b/fs/binfmt_som.c
+@@ -43,7 +43,7 @@ static int load_som_library(struct file *);
+ * don't even try.
+ */
+ #if 0
+-static int som_core_dump(long signr, struct pt_regs *regs, unsigned long limit);
++static int som_core_dump(struct coredump_params *cprm);
+ #else
+ #define som_core_dump NULL
+ #endif
+diff --git a/fs/exec.c b/fs/exec.c
+index ba112bd..08ec506 100644
+--- a/fs/exec.c
++++ b/fs/exec.c
+@@ -1749,6 +1749,50 @@ static void wait_for_dump_helpers(struct file *file)
+ }
+
+
++/*
++ * uhm_pipe_setup
++ * helper function to customize the process used
++ * to collect the core in userspace. Specifically
++ * it sets up a pipe and installs it as fd 0 (stdin)
++ * for the process. Returns 0 on success, or
++ * PTR_ERR on failure.
++ * Note that it also sets the core limit to 1. This
++ * is a special value that we use to trap recursive
++ * core dumps
++ */
++static int umh_pipe_setup(struct subprocess_info *info)
++{
++ struct file *rp, *wp;
++ struct fdtable *fdt;
++ struct coredump_params *cp = (struct coredump_params *)info->data;
++ struct files_struct *cf = current->files;
++
++ wp = create_write_pipe(0);
++ if (IS_ERR(wp))
++ return PTR_ERR(wp);
++
++ rp = create_read_pipe(wp, 0);
++ if (IS_ERR(rp)) {
++ free_write_pipe(wp);
++ return PTR_ERR(rp);
++ }
++
++ cp->file = wp;
++
++ sys_close(0);
++ fd_install(0, rp);
++ spin_lock(&cf->file_lock);
++ fdt = files_fdtable(cf);
++ FD_SET(0, fdt->open_fds);
++ FD_CLR(0, fdt->close_on_exec);
++ spin_unlock(&cf->file_lock);
++
++ /* and disallow core files too */
++ current->signal->rlim[RLIMIT_CORE] = (struct rlimit){1, 1};
++
++ return 0;
++}
++
+ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
+ {
+ struct core_state core_state;
+@@ -1756,17 +1800,20 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
+ struct mm_struct *mm = current->mm;
+ struct linux_binfmt * binfmt;
+ struct inode * inode;
+- struct file * file;
+ const struct cred *old_cred;
+ struct cred *cred;
+ int retval = 0;
+ int flag = 0;
+ int ispipe = 0;
+- unsigned long core_limit = current->signal->rlim[RLIMIT_CORE].rlim_cur;
+ char **helper_argv = NULL;
+ int helper_argc = 0;
+ int dump_count = 0;
+ static atomic_t core_dump_count = ATOMIC_INIT(0);
++ struct coredump_params cprm = {
++ .signr = signr,
++ .regs = regs,
++ .limit = current->signal->rlim[RLIMIT_CORE].rlim_cur,
++ };
+
+ audit_core_dumps(signr);
+
+@@ -1822,19 +1869,19 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
+ ispipe = format_corename(corename, signr);
+ unlock_kernel();
+
+- if ((!ispipe) && (core_limit < binfmt->min_coredump))
++ if ((!ispipe) && (cprm.limit < binfmt->min_coredump))
+ goto fail_unlock;
+
+ if (ispipe) {
+- if (core_limit == 0) {
++ if (cprm.limit == 1) {
+ /*
+ * Normally core limits are irrelevant to pipes, since
+ * we're not writing to the file system, but we use
+- * core_limit of 0 here as a speacial value. Any
+- * non-zero limit gets set to RLIM_INFINITY below, but
++ * cprm.limit of 1 here as a speacial value. Any
++ * non-1 limit gets set to RLIM_INFINITY below, but
+ * a limit of 0 skips the dump. This is a consistent
+ * way to catch recursive crashes. We can still crash
+- * if the core_pattern binary sets RLIM_CORE = !0
++ * if the core_pattern binary sets RLIM_CORE = !1
+ * but it runs as root, and can do lots of stupid things
+ * Note that we use task_tgid_vnr here to grab the pid
+ * of the process group leader. That way we get the
+@@ -1842,7 +1889,7 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
+ * core_pattern process dies.
+ */
+ printk(KERN_WARNING
+- "Process %d(%s) has RLIMIT_CORE set to 0\n",
++ "Process %d(%s) has RLIMIT_CORE set to 1\n",
+ task_tgid_vnr(current), current->comm);
+ printk(KERN_WARNING "Aborting core\n");
+ goto fail_unlock;
+@@ -1863,25 +1910,30 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
+ goto fail_dropcount;
+ }
+
+- core_limit = RLIM_INFINITY;
++ cprm.limit = RLIM_INFINITY;
+
+ /* SIGPIPE can happen, but it's just never processed */
+- if (call_usermodehelper_pipe(helper_argv[0], helper_argv, NULL,
+- &file)) {
++ cprm.file = NULL;
++ if (call_usermodehelper_fns(helper_argv[0], helper_argv, NULL,
++ UMH_WAIT_EXEC, umh_pipe_setup,
++ NULL, &cprm)) {
++ if (cprm.file)
++ filp_close(cprm.file, NULL);
++
+ printk(KERN_INFO "Core dump to %s pipe failed\n",
+ corename);
+ goto fail_dropcount;
+ }
+ } else
+- file = filp_open(corename,
++ cprm.file = filp_open(corename,
+ O_CREAT | 2 | O_NOFOLLOW | O_LARGEFILE | flag,
+ 0600);
+- if (IS_ERR(file))
++ if (IS_ERR(cprm.file))
+ goto fail_dropcount;
+- inode = file->f_path.dentry->d_inode;
++ inode = cprm.file->f_path.dentry->d_inode;
+ if (inode->i_nlink > 1)
+ goto close_fail; /* multiple links - don't dump */
+- if (!ispipe && d_unhashed(file->f_path.dentry))
++ if (!ispipe && d_unhashed(cprm.file->f_path.dentry))
+ goto close_fail;
+
+ /* AK: actually i see no reason to not allow this for named pipes etc.,
+@@ -1894,21 +1946,22 @@ void do_coredump(long signr, int exit_code, struct pt_regs *regs)
+ */
+ if (!ispipe && (inode->i_uid != current_fsuid()))
+ goto close_fail;
+- if (!file->f_op)
++ if (!cprm.file->f_op)
+ goto close_fail;
+- if (!file->f_op->write)
++ if (!cprm.file->f_op->write)
+ goto close_fail;
+- if (!ispipe && do_truncate(file->f_path.dentry, 0, 0, file) != 0)
++ if (!ispipe &&
++ do_truncate(cprm.file->f_path.dentry, 0, 0, cprm.file) != 0)
+ goto close_fail;
+
+- retval = binfmt->core_dump(signr, regs, file, core_limit);
++ retval = binfmt->core_dump(&cprm);
+
+ if (retval)
+ current->signal->group_exit_code |= 0x80;
+ close_fail:
+ if (ispipe && core_pipe_limit)
+- wait_for_dump_helpers(file);
+- filp_close(file, NULL);
++ wait_for_dump_helpers(cprm.file);
++ filp_close(cprm.file, NULL);
+ fail_dropcount:
+ if (dump_count)
+ atomic_dec(&core_dump_count);
+diff --git a/include/linux/binfmts.h b/include/linux/binfmts.h
+index aece486..cd4349b 100644
+--- a/include/linux/binfmts.h
++++ b/include/linux/binfmts.h
+@@ -68,6 +68,14 @@ struct linux_binprm{
+
+ #define BINPRM_MAX_RECURSION 4
+
++/* Function parameter for binfmt->coredump */
++struct coredump_params {
++ long signr;
++ struct pt_regs *regs;
++ struct file *file;
++ unsigned long limit;
++};
++
+ /*
+ * This structure defines the functions that are used to load the binary formats that
+ * linux accepts.
+@@ -77,7 +85,7 @@ struct linux_binfmt {
+ struct module *module;
+ int (*load_binary)(struct linux_binprm *, struct pt_regs * regs);
+ int (*load_shlib)(struct file *);
+- int (*core_dump)(long signr, struct pt_regs *regs, struct file *file, unsigned long limit);
++ int (*core_dump)(struct coredump_params *cprm);
+ unsigned long min_coredump; /* minimal dump size */
+ int hasvdso;
+ };
+diff --git a/include/linux/kmod.h b/include/linux/kmod.h
+index 384ca8b..ec69956 100644
+--- a/include/linux/kmod.h
++++ b/include/linux/kmod.h
+@@ -23,6 +23,7 @@
+ #include <linux/stddef.h>
+ #include <linux/errno.h>
+ #include <linux/compiler.h>
++#include <linux/workqueue.h>
+
+ #define KMOD_PATH_LEN 256
+
+@@ -44,7 +45,26 @@ static inline int request_module_nowait(const char *name, ...) { return -ENOSYS;
+
+ struct key;
+ struct file;
+-struct subprocess_info;
++
++enum umh_wait {
++ UMH_NO_WAIT = -1, /* don't wait at all */
++ UMH_WAIT_EXEC = 0, /* wait for the exec, but not the process */
++ UMH_WAIT_PROC = 1, /* wait for the process to complete */
++};
++
++struct subprocess_info {
++ struct work_struct work;
++ struct completion *complete;
++ struct cred *cred;
++ char *path;
++ char **argv;
++ char **envp;
++ enum umh_wait wait;
++ int retval;
++ int (*init)(struct subprocess_info *info);
++ void (*cleanup)(struct subprocess_info *info);
++ void *data;
++};
+
+ /* Allocate a subprocess_info structure */
+ struct subprocess_info *call_usermodehelper_setup(char *path, char **argv,
+@@ -55,14 +75,10 @@ void call_usermodehelper_setkeys(struct subprocess_info *info,
+ struct key *session_keyring);
+ int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info,
+ struct file **filp);
+-void call_usermodehelper_setcleanup(struct subprocess_info *info,
+- void (*cleanup)(char **argv, char **envp));
+-
+-enum umh_wait {
+- UMH_NO_WAIT = -1, /* don't wait at all */
+- UMH_WAIT_EXEC = 0, /* wait for the exec, but not the process */
+- UMH_WAIT_PROC = 1, /* wait for the process to complete */
+-};
++void call_usermodehelper_setfns(struct subprocess_info *info,
++ int (*init)(struct subprocess_info *info),
++ void (*cleanup)(struct subprocess_info *info),
++ void *data);
+
+ /* Actually execute the sub-process */
+ int call_usermodehelper_exec(struct subprocess_info *info, enum umh_wait wait);
+@@ -72,7 +88,10 @@ int call_usermodehelper_exec(struct subprocess_info *info, enum umh_wait wait);
+ void call_usermodehelper_freeinfo(struct subprocess_info *info);
+
+ static inline int
+-call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait)
++call_usermodehelper_fns(char *path, char **argv, char **envp,
++ enum umh_wait wait,
++ int (*init)(struct subprocess_info *info),
++ void (*cleanup)(struct subprocess_info *), void *data)
+ {
+ struct subprocess_info *info;
+ gfp_t gfp_mask = (wait == UMH_NO_WAIT) ? GFP_ATOMIC : GFP_KERNEL;
+@@ -80,10 +99,18 @@ call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait)
+ info = call_usermodehelper_setup(path, argv, envp, gfp_mask);
+ if (info == NULL)
+ return -ENOMEM;
++ call_usermodehelper_setfns(info, init, cleanup, data);
+ return call_usermodehelper_exec(info, wait);
+ }
+
+ static inline int
++call_usermodehelper(char *path, char **argv, char **envp, enum umh_wait wait)
++{
++ return call_usermodehelper_fns(path, argv, envp,
++ wait, NULL, NULL, NULL);
++}
++
++static inline int
+ call_usermodehelper_keys(char *path, char **argv, char **envp,
+ struct key *session_keyring, enum umh_wait wait)
+ {
+@@ -100,10 +127,6 @@ call_usermodehelper_keys(char *path, char **argv, char **envp,
+
+ extern void usermodehelper_init(void);
+
+-struct file;
+-extern int call_usermodehelper_pipe(char *path, char *argv[], char *envp[],
+- struct file **filp);
+-
+ extern int usermodehelper_disable(void);
+ extern void usermodehelper_enable(void);
+
+diff --git a/kernel/kmod.c b/kernel/kmod.c
+index 9fcb53a..7281229 100644
+--- a/kernel/kmod.c
++++ b/kernel/kmod.c
+@@ -124,19 +124,6 @@ int __request_module(bool wait, const char *fmt, ...)
+ EXPORT_SYMBOL(__request_module);
+ #endif /* CONFIG_MODULES */
+
+-struct subprocess_info {
+- struct work_struct work;
+- struct completion *complete;
+- struct cred *cred;
+- char *path;
+- char **argv;
+- char **envp;
+- enum umh_wait wait;
+- int retval;
+- struct file *stdin;
+- void (*cleanup)(char **argv, char **envp);
+-};
+-
+ /*
+ * This is the task which runs the usermode application
+ */
+@@ -158,26 +145,15 @@ static int ____call_usermodehelper(void *data)
+ commit_creds(sub_info->cred);
+ sub_info->cred = NULL;
+
+- /* Install input pipe when needed */
+- if (sub_info->stdin) {
+- struct files_struct *f = current->files;
+- struct fdtable *fdt;
+- /* no races because files should be private here */
+- sys_close(0);
+- fd_install(0, sub_info->stdin);
+- spin_lock(&f->file_lock);
+- fdt = files_fdtable(f);
+- FD_SET(0, fdt->open_fds);
+- FD_CLR(0, fdt->close_on_exec);
+- spin_unlock(&f->file_lock);
+-
+- /* and disallow core files too */
+- current->signal->rlim[RLIMIT_CORE] = (struct rlimit){0, 0};
+- }
+-
+ /* We can run anywhere, unlike our parent keventd(). */
+ set_cpus_allowed_ptr(current, cpu_all_mask);
+
++ if (sub_info->init) {
++ retval = sub_info->init(sub_info);
++ if (retval)
++ goto fail;
++ }
++
+ /*
+ * Our parent is keventd, which runs with elevated scheduling priority.
+ * Avoid propagating that into the userspace child.
+@@ -187,6 +163,7 @@ static int ____call_usermodehelper(void *data)
+ retval = kernel_execve(sub_info->path, sub_info->argv, sub_info->envp);
+
+ /* Exec failed? */
++fail:
+ sub_info->retval = retval;
+ do_exit(0);
+ }
+@@ -194,7 +171,7 @@ static int ____call_usermodehelper(void *data)
+ void call_usermodehelper_freeinfo(struct subprocess_info *info)
+ {
+ if (info->cleanup)
+- (*info->cleanup)(info->argv, info->envp);
++ (*info->cleanup)(info);
+ if (info->cred)
+ put_cred(info->cred);
+ kfree(info);
+@@ -406,50 +383,31 @@ void call_usermodehelper_setkeys(struct subprocess_info *info,
+ EXPORT_SYMBOL(call_usermodehelper_setkeys);
+
+ /**
+- * call_usermodehelper_setcleanup - set a cleanup function
++ * call_usermodehelper_setfns - set a cleanup/init function
+ * @info: a subprocess_info returned by call_usermodehelper_setup
+ * @cleanup: a cleanup function
++ * @init: an init function
++ * @data: arbitrary context sensitive data
++ *
++ * The init function is used to customize the helper process prior to
++ * exec. A non-zero return code causes the process to error out, exit,
++ * and return the failure to the calling process
+ *
+- * The cleanup function is just befor ethe subprocess_info is about to
++ * The cleanup function is just before ethe subprocess_info is about to
+ * be freed. This can be used for freeing the argv and envp. The
+ * Function must be runnable in either a process context or the
+ * context in which call_usermodehelper_exec is called.
+ */
+-void call_usermodehelper_setcleanup(struct subprocess_info *info,
+- void (*cleanup)(char **argv, char **envp))
++void call_usermodehelper_setfns(struct subprocess_info *info,
++ int (*init)(struct subprocess_info *info),
++ void (*cleanup)(struct subprocess_info *info),
++ void *data)
+ {
+ info->cleanup = cleanup;
++ info->init = init;
++ info->data = data;
+ }
+-EXPORT_SYMBOL(call_usermodehelper_setcleanup);
+-
+-/**
+- * call_usermodehelper_stdinpipe - set up a pipe to be used for stdin
+- * @sub_info: a subprocess_info returned by call_usermodehelper_setup
+- * @filp: set to the write-end of a pipe
+- *
+- * This constructs a pipe, and sets the read end to be the stdin of the
+- * subprocess, and returns the write-end in *@filp.
+- */
+-int call_usermodehelper_stdinpipe(struct subprocess_info *sub_info,
+- struct file **filp)
+-{
+- struct file *f;
+-
+- f = create_write_pipe(0);
+- if (IS_ERR(f))
+- return PTR_ERR(f);
+- *filp = f;
+-
+- f = create_read_pipe(f, 0);
+- if (IS_ERR(f)) {
+- free_write_pipe(*filp);
+- return PTR_ERR(f);
+- }
+- sub_info->stdin = f;
+-
+- return 0;
+-}
+-EXPORT_SYMBOL(call_usermodehelper_stdinpipe);
++EXPORT_SYMBOL(call_usermodehelper_setfns);
+
+ /**
+ * call_usermodehelper_exec - start a usermode application
+@@ -498,39 +456,6 @@ unlock:
+ }
+ EXPORT_SYMBOL(call_usermodehelper_exec);
+
+-/**
+- * call_usermodehelper_pipe - call a usermode helper process with a pipe stdin
+- * @path: path to usermode executable
+- * @argv: arg vector for process
+- * @envp: environment for process
+- * @filp: set to the write-end of a pipe
+- *
+- * This is a simple wrapper which executes a usermode-helper function
+- * with a pipe as stdin. It is implemented entirely in terms of
+- * lower-level call_usermodehelper_* functions.
+- */
+-int call_usermodehelper_pipe(char *path, char **argv, char **envp,
+- struct file **filp)
+-{
+- struct subprocess_info *sub_info;
+- int ret;
+-
+- sub_info = call_usermodehelper_setup(path, argv, envp, GFP_KERNEL);
+- if (sub_info == NULL)
+- return -ENOMEM;
+-
+- ret = call_usermodehelper_stdinpipe(sub_info, filp);
+- if (ret < 0)
+- goto out;
+-
+- return call_usermodehelper_exec(sub_info, UMH_WAIT_EXEC);
+-
+- out:
+- call_usermodehelper_freeinfo(sub_info);
+- return ret;
+-}
+-EXPORT_SYMBOL(call_usermodehelper_pipe);
+-
+ void __init usermodehelper_init(void)
+ {
+ khelper_wq = create_singlethread_workqueue("khelper");
+diff --git a/kernel/sys.c b/kernel/sys.c
+index ce17760..0b8a55e 100644
+--- a/kernel/sys.c
++++ b/kernel/sys.c
+@@ -1600,9 +1600,9 @@ SYSCALL_DEFINE3(getcpu, unsigned __user *, cpup, unsigned __user *, nodep,
+
+ char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff";
+
+-static void argv_cleanup(char **argv, char **envp)
++static void argv_cleanup(struct subprocess_info *info)
+ {
+- argv_free(argv);
++ argv_free(info->argv);
+ }
+
+ /**
+@@ -1636,7 +1636,7 @@ int orderly_poweroff(bool force)
+ goto out;
+ }
+
+- call_usermodehelper_setcleanup(info, argv_cleanup);
++ call_usermodehelper_setfns(info, NULL, argv_cleanup, NULL);
+
+ ret = call_usermodehelper_exec(info, UMH_NO_WAIT);
+
diff --git a/fix-ima-null-ptr-deref.patch b/fix-ima-null-ptr-deref.patch
new file mode 100644
index 0000000..01515f7
--- /dev/null
+++ b/fix-ima-null-ptr-deref.patch
@@ -0,0 +1,54 @@
+From: J. R. Okajima <hooanon05@yahoo.co.jp>
+Date: Sun, 7 Feb 2010 06:48:55 +0000 (+1100)
+Subject: ima: fix null pointer deref
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Fjmorris%2Fsecurity-testing-2.6.git;a=commitdiff_plain;h=8bb6795424b09db0eca1cccf7a17b93fc28ac7f7
+
+ima: fix null pointer deref
+
+The commit 6c21a7f "LSM: imbed ima calls in the security hooks"
+which moves the ima_file_free() call within security_file_free()
+brought a problem into pipe.c.
+In the error path of pipe(2), the allocated resources are freed by
+path_put() and put_filp() (in this order). Since security_file_free()
+refers f_dentry and ima_file_free() refers f_dentry->d_inode, path_put()
+should be called after put_filp().
+
+Signed-off-by: J. R. Okajima <hooanon05@yahoo.co.jp>
+Signed-off-by: James Morris <jmorris@namei.org>
+---
+
+diff --git a/fs/pipe.c b/fs/pipe.c
+index 37ba29f..90b543d 100644
+--- a/fs/pipe.c
++++ b/fs/pipe.c
+@@ -1004,9 +1004,10 @@ struct file *create_write_pipe(int flags)
+
+ void free_write_pipe(struct file *f)
+ {
++ struct path path = f->f_path;
+ free_pipe_info(f->f_dentry->d_inode);
+- path_put(&f->f_path);
+ put_filp(f);
++ path_put(&path);
+ }
+
+ struct file *create_read_pipe(struct file *wrf, int flags)
+@@ -1028,6 +1029,7 @@ int do_pipe_flags(int *fd, int flags)
+ struct file *fw, *fr;
+ int error;
+ int fdw, fdr;
++ struct path path;
+
+ if (flags & ~(O_CLOEXEC | O_NONBLOCK))
+ return -EINVAL;
+@@ -1061,8 +1063,9 @@ int do_pipe_flags(int *fd, int flags)
+ err_fdr:
+ put_unused_fd(fdr);
+ err_read_pipe:
+- path_put(&fr->f_path);
++ path = fr->f_path;
+ put_filp(fr);
++ path_put(&path);
+ err_write_pipe:
+ free_write_pipe(fw);
+ return error;
diff --git a/genkey b/genkey
new file mode 100644
index 0000000..49c6ce8
--- /dev/null
+++ b/genkey
@@ -0,0 +1,7 @@
+%pubring kernel.pub
+%secring kernel.sec
+Key-Type: DSA
+Key-Length: 512
+Name-Real: Red Hat, Inc.
+Name-Comment: Kernel Module GPG key
+%commit
diff --git a/git-bluetooth.patch b/git-bluetooth.patch
new file mode 100644
index 0000000..8ecc995
--- /dev/null
+++ b/git-bluetooth.patch
@@ -0,0 +1,3344 @@
+commit b1fb06830dc870d862f7f80e276130c0ab84d59f
+Author: Wei Yongjun <yjwei@cn.fujitsu.com>
+Date: Wed Feb 25 18:09:33 2009 +0800
+
+ Bluetooth: Remove some pointless conditionals before kfree_skb()
+
+ Remove some pointless conditionals before kfree_skb().
+
+ Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com>
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 7585b97a48180f754ebdade1be94092e36bef365
+Author: Wei Yongjun <yjwei@cn.fujitsu.com>
+Date: Wed Feb 25 18:29:52 2009 +0800
+
+ Bluetooth: Remove some pointless conditionals before kfree_skb()
+
+ Remove some pointless conditionals before kfree_skb().
+
+ Signed-off-by: Wei Yongjun <yjwei@cn.fujitsu.com>
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 2ae9a6be5f476f3512839a4d11a8f432bfd2914c
+Author: Dave Young <hidave.darkstar@gmail.com>
+Date: Sat Feb 21 16:13:34 2009 +0800
+
+ Bluetooth: Move hci_conn_del_sysfs() back to avoid device destruct too early
+
+ The following commit introduce a regression:
+
+ commit 7d0db0a373195385a2e0b19d1f5e4b186fdcffac
+ Author: Marcel Holtmann <marcel@holtmann.org>
+ Date: Mon Jul 14 20:13:51 2008 +0200
+
+ [Bluetooth] Use a more unique bus name for connections
+
+ I get panic as following (by netconsole):
+
+ [ 2709.344034] usb 5-1: new full speed USB device using uhci_hcd and address 4
+ [ 2709.505776] usb 5-1: configuration #1 chosen from 1 choice
+ [ 2709.569207] Bluetooth: Generic Bluetooth USB driver ver 0.4
+ [ 2709.570169] usbcore: registered new interface driver btusb
+ [ 2845.742781] BUG: unable to handle kernel paging request at 6b6b6c2f
+ [ 2845.742958] IP: [<c015515c>] __lock_acquire+0x6c/0xa80
+ [ 2845.743087] *pde = 00000000
+ [ 2845.743206] Oops: 0002 [#1] SMP
+ [ 2845.743377] last sysfs file: /sys/class/bluetooth/hci0/hci0:6/type
+ [ 2845.743742] Modules linked in: btusb netconsole snd_seq_dummy snd_seq_oss snd_seq_midi_event snd_seq snd_seq_device snd_pcm_oss snd_mixer_oss rfcomm l2cap bluetooth vfat fuse snd_hda_codec_idt snd_hda_intel snd_hda_codec snd_hwdep snd_pcm pl2303 snd_timer psmouse usbserial snd 3c59x e100 serio_raw soundcore i2c_i801 intel_agp mii agpgart snd_page_alloc rtc_cmos rtc_core thermal processor rtc_lib button thermal_sys sg evdev
+ [ 2845.743742]
+ [ 2845.743742] Pid: 0, comm: swapper Not tainted (2.6.29-rc5-smp #54) Dell DM051
+ [ 2845.743742] EIP: 0060:[<c015515c>] EFLAGS: 00010002 CPU: 0
+ [ 2845.743742] EIP is at __lock_acquire+0x6c/0xa80
+ [ 2845.743742] EAX: 00000046 EBX: 00000046 ECX: 6b6b6b6b EDX: 00000002
+ [ 2845.743742] ESI: 6b6b6b6b EDI: 00000000 EBP: c064fd14 ESP: c064fcc8
+ [ 2845.743742] DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068
+ [ 2845.743742] Process swapper (pid: 0, ti=c064e000 task=c05d1400 task.ti=c064e000)
+ [ 2845.743742] Stack:
+ [ 2845.743742] c05d1400 00000002 c05d1400 00000001 00000002 00000000 f65388dc c05d1400
+ [ 2845.743742] 6b6b6b6b 00000292 c064fd0c c0153732 00000000 00000000 00000001 f700fa50
+ [ 2845.743742] 00000046 00000000 00000000 c064fd40 c0155be6 00000000 00000002 00000001
+ [ 2845.743742] Call Trace:
+ [ 2845.743742] [<c0153732>] ? trace_hardirqs_on_caller+0x72/0x1c0
+ [ 2845.743742] [<c0155be6>] ? lock_acquire+0x76/0xa0
+ [ 2845.743742] [<c03e1aad>] ? skb_dequeue+0x1d/0x70
+ [ 2845.743742] [<c046c885>] ? _spin_lock_irqsave+0x45/0x80
+ [ 2845.743742] [<c03e1aad>] ? skb_dequeue+0x1d/0x70
+ [ 2845.743742] [<c03e1aad>] ? skb_dequeue+0x1d/0x70
+ [ 2845.743742] [<c03e1f94>] ? skb_queue_purge+0x14/0x20
+ [ 2845.743742] [<f8171f5a>] ? hci_conn_del+0x10a/0x1c0 [bluetooth]
+ [ 2845.743742] [<f81399c9>] ? l2cap_disconn_ind+0x59/0xb0 [l2cap]
+ [ 2845.743742] [<f81795ce>] ? hci_conn_del_sysfs+0x8e/0xd0 [bluetooth]
+ [ 2845.743742] [<f8175758>] ? hci_event_packet+0x5f8/0x31c0 [bluetooth]
+ [ 2845.743742] [<c03dfe19>] ? sock_def_readable+0x59/0x80
+ [ 2845.743742] [<c046c14d>] ? _read_unlock+0x1d/0x20
+ [ 2845.743742] [<f8178aa9>] ? hci_send_to_sock+0xe9/0x1d0 [bluetooth]
+ [ 2845.743742] [<c015388b>] ? trace_hardirqs_on+0xb/0x10
+ [ 2845.743742] [<f816fa6a>] ? hci_rx_task+0x2ba/0x490 [bluetooth]
+ [ 2845.743742] [<c0133661>] ? tasklet_action+0x31/0xc0
+ [ 2845.743742] [<c013367c>] ? tasklet_action+0x4c/0xc0
+ [ 2845.743742] [<c0132eb7>] ? __do_softirq+0xa7/0x170
+ [ 2845.743742] [<c0116dec>] ? ack_apic_level+0x5c/0x1c0
+ [ 2845.743742] [<c0132fd7>] ? do_softirq+0x57/0x60
+ [ 2845.743742] [<c01333dc>] ? irq_exit+0x7c/0x90
+ [ 2845.743742] [<c01055bb>] ? do_IRQ+0x4b/0x90
+ [ 2845.743742] [<c01333d5>] ? irq_exit+0x75/0x90
+ [ 2845.743742] [<c010392c>] ? common_interrupt+0x2c/0x34
+ [ 2845.743742] [<c010a14f>] ? mwait_idle+0x4f/0x70
+ [ 2845.743742] [<c0101c05>] ? cpu_idle+0x65/0xb0
+ [ 2845.743742] [<c045731e>] ? rest_init+0x4e/0x60
+ [ 2845.743742] Code: 0f 84 69 02 00 00 83 ff 07 0f 87 1e 06 00 00 85 ff 0f 85 08 05 00 00 8b 4d cc 8b 49 04 85 c9 89 4d d4 0f 84 f7 04 00 00 8b 75 d4 <f0> ff 86 c4 00 00 00 89 f0 e8 56 a9 ff ff 85 c0 0f 85 6e 03 00
+ [ 2845.743742] EIP: [<c015515c>] __lock_acquire+0x6c/0xa80 SS:ESP 0068:c064fcc8
+ [ 2845.743742] ---[ end trace 4c985b38f022279f ]---
+ [ 2845.743742] Kernel panic - not syncing: Fatal exception in interrupt
+ [ 2845.743742] ------------[ cut here ]------------
+ [ 2845.743742] WARNING: at kernel/smp.c:329 smp_call_function_many+0x151/0x200()
+ [ 2845.743742] Hardware name: Dell DM051
+ [ 2845.743742] Modules linked in: btusb netconsole snd_seq_dummy snd_seq_oss snd_seq_midi_event snd_seq snd_seq_device snd_pcm_oss snd_mixer_oss rfcomm l2cap bluetooth vfat fuse snd_hda_codec_idt snd_hda_intel snd_hda_codec snd_hwdep snd_pcm pl2303 snd_timer psmouse usbserial snd 3c59x e100 serio_raw soundcore i2c_i801 intel_agp mii agpgart snd_page_alloc rtc_cmos rtc_core thermal processor rtc_lib button thermal_sys sg evdev
+ [ 2845.743742] Pid: 0, comm: swapper Tainted: G D 2.6.29-rc5-smp #54
+ [ 2845.743742] Call Trace:
+ [ 2845.743742] [<c012e076>] warn_slowpath+0x86/0xa0
+ [ 2845.743742] [<c015041b>] ? trace_hardirqs_off+0xb/0x10
+ [ 2845.743742] [<c0146384>] ? up+0x14/0x40
+ [ 2845.743742] [<c012e661>] ? release_console_sem+0x31/0x1e0
+ [ 2845.743742] [<c046c8ab>] ? _spin_lock_irqsave+0x6b/0x80
+ [ 2845.743742] [<c015041b>] ? trace_hardirqs_off+0xb/0x10
+ [ 2845.743742] [<c046c900>] ? _read_lock_irqsave+0x40/0x80
+ [ 2845.743742] [<c012e7f2>] ? release_console_sem+0x1c2/0x1e0
+ [ 2845.743742] [<c0146384>] ? up+0x14/0x40
+ [ 2845.743742] [<c015041b>] ? trace_hardirqs_off+0xb/0x10
+ [ 2845.743742] [<c046a3d7>] ? __mutex_unlock_slowpath+0x97/0x160
+ [ 2845.743742] [<c046a563>] ? mutex_trylock+0xb3/0x180
+ [ 2845.743742] [<c046a4a8>] ? mutex_unlock+0x8/0x10
+ [ 2845.743742] [<c015b991>] smp_call_function_many+0x151/0x200
+ [ 2845.743742] [<c010a1a0>] ? stop_this_cpu+0x0/0x40
+ [ 2845.743742] [<c015ba61>] smp_call_function+0x21/0x30
+ [ 2845.743742] [<c01137ae>] native_smp_send_stop+0x1e/0x50
+ [ 2845.743742] [<c012e0f5>] panic+0x55/0x110
+ [ 2845.743742] [<c01065a8>] oops_end+0xb8/0xc0
+ [ 2845.743742] [<c010668f>] die+0x4f/0x70
+ [ 2845.743742] [<c011a8c9>] do_page_fault+0x269/0x610
+ [ 2845.743742] [<c011a660>] ? do_page_fault+0x0/0x610
+ [ 2845.743742] [<c046cbaf>] error_code+0x77/0x7c
+ [ 2845.743742] [<c015515c>] ? __lock_acquire+0x6c/0xa80
+ [ 2845.743742] [<c0153732>] ? trace_hardirqs_on_caller+0x72/0x1c0
+ [ 2845.743742] [<c0155be6>] lock_acquire+0x76/0xa0
+ [ 2845.743742] [<c03e1aad>] ? skb_dequeue+0x1d/0x70
+ [ 2845.743742] [<c046c885>] _spin_lock_irqsave+0x45/0x80
+ [ 2845.743742] [<c03e1aad>] ? skb_dequeue+0x1d/0x70
+ [ 2845.743742] [<c03e1aad>] skb_dequeue+0x1d/0x70
+ [ 2845.743742] [<c03e1f94>] skb_queue_purge+0x14/0x20
+ [ 2845.743742] [<f8171f5a>] hci_conn_del+0x10a/0x1c0 [bluetooth]
+ [ 2845.743742] [<f81399c9>] ? l2cap_disconn_ind+0x59/0xb0 [l2cap]
+ [ 2845.743742] [<f81795ce>] ? hci_conn_del_sysfs+0x8e/0xd0 [bluetooth]
+ [ 2845.743742] [<f8175758>] hci_event_packet+0x5f8/0x31c0 [bluetooth]
+ [ 2845.743742] [<c03dfe19>] ? sock_def_readable+0x59/0x80
+ [ 2845.743742] [<c046c14d>] ? _read_unlock+0x1d/0x20
+ [ 2845.743742] [<f8178aa9>] ? hci_send_to_sock+0xe9/0x1d0 [bluetooth]
+ [ 2845.743742] [<c015388b>] ? trace_hardirqs_on+0xb/0x10
+ [ 2845.743742] [<f816fa6a>] hci_rx_task+0x2ba/0x490 [bluetooth]
+ [ 2845.743742] [<c0133661>] ? tasklet_action+0x31/0xc0
+ [ 2845.743742] [<c013367c>] tasklet_action+0x4c/0xc0
+ [ 2845.743742] [<c0132eb7>] __do_softirq+0xa7/0x170
+ [ 2845.743742] [<c0116dec>] ? ack_apic_level+0x5c/0x1c0
+ [ 2845.743742] [<c0132fd7>] do_softirq+0x57/0x60
+ [ 2845.743742] [<c01333dc>] irq_exit+0x7c/0x90
+ [ 2845.743742] [<c01055bb>] do_IRQ+0x4b/0x90
+ [ 2845.743742] [<c01333d5>] ? irq_exit+0x75/0x90
+ [ 2845.743742] [<c010392c>] common_interrupt+0x2c/0x34
+ [ 2845.743742] [<c010a14f>] ? mwait_idle+0x4f/0x70
+ [ 2845.743742] [<c0101c05>] cpu_idle+0x65/0xb0
+ [ 2845.743742] [<c045731e>] rest_init+0x4e/0x60
+ [ 2845.743742] ---[ end trace 4c985b38f02227a0 ]---
+ [ 2845.743742] ------------[ cut here ]------------
+ [ 2845.743742] WARNING: at kernel/smp.c:226 smp_call_function_single+0x8e/0x110()
+ [ 2845.743742] Hardware name: Dell DM051
+ [ 2845.743742] Modules linked in: btusb netconsole snd_seq_dummy snd_seq_oss snd_seq_midi_event snd_seq snd_seq_device snd_pcm_oss snd_mixer_oss rfcomm l2cap bluetooth vfat fuse snd_hda_codec_idt snd_hda_intel snd_hda_codec snd_hwdep snd_pcm pl2303 snd_timer psmouse usbserial snd 3c59x e100 serio_raw soundcore i2c_i801 intel_agp mii agpgart snd_page_alloc rtc_cmos rtc_core thermal processor rtc_lib button thermal_sys sg evdev
+ [ 2845.743742] Pid: 0, comm: swapper Tainted: G D W 2.6.29-rc5-smp #54
+ [ 2845.743742] Call Trace:
+ [ 2845.743742] [<c012e076>] warn_slowpath+0x86/0xa0
+ [ 2845.743742] [<c012e000>] ? warn_slowpath+0x10/0xa0
+ [ 2845.743742] [<c015041b>] ? trace_hardirqs_off+0xb/0x10
+ [ 2845.743742] [<c0146384>] ? up+0x14/0x40
+ [ 2845.743742] [<c012e661>] ? release_console_sem+0x31/0x1e0
+ [ 2845.743742] [<c046c8ab>] ? _spin_lock_irqsave+0x6b/0x80
+ [ 2845.743742] [<c015041b>] ? trace_hardirqs_off+0xb/0x10
+ [ 2845.743742] [<c046c900>] ? _read_lock_irqsave+0x40/0x80
+ [ 2845.743742] [<c012e7f2>] ? release_console_sem+0x1c2/0x1e0
+ [ 2845.743742] [<c0146384>] ? up+0x14/0x40
+ [ 2845.743742] [<c015b7be>] smp_call_function_single+0x8e/0x110
+ [ 2845.743742] [<c010a1a0>] ? stop_this_cpu+0x0/0x40
+ [ 2845.743742] [<c026d23f>] ? cpumask_next_and+0x1f/0x40
+ [ 2845.743742] [<c015b95a>] smp_call_function_many+0x11a/0x200
+ [ 2845.743742] [<c010a1a0>] ? stop_this_cpu+0x0/0x40
+ [ 2845.743742] [<c015ba61>] smp_call_function+0x21/0x30
+ [ 2845.743742] [<c01137ae>] native_smp_send_stop+0x1e/0x50
+ [ 2845.743742] [<c012e0f5>] panic+0x55/0x110
+ [ 2845.743742] [<c01065a8>] oops_end+0xb8/0xc0
+ [ 2845.743742] [<c010668f>] die+0x4f/0x70
+ [ 2845.743742] [<c011a8c9>] do_page_fault+0x269/0x610
+ [ 2845.743742] [<c011a660>] ? do_page_fault+0x0/0x610
+ [ 2845.743742] [<c046cbaf>] error_code+0x77/0x7c
+ [ 2845.743742] [<c015515c>] ? __lock_acquire+0x6c/0xa80
+ [ 2845.743742] [<c0153732>] ? trace_hardirqs_on_caller+0x72/0x1c0
+ [ 2845.743742] [<c0155be6>] lock_acquire+0x76/0xa0
+ [ 2845.743742] [<c03e1aad>] ? skb_dequeue+0x1d/0x70
+ [ 2845.743742] [<c046c885>] _spin_lock_irqsave+0x45/0x80
+ [ 2845.743742] [<c03e1aad>] ? skb_dequeue+0x1d/0x70
+ [ 2845.743742] [<c03e1aad>] skb_dequeue+0x1d/0x70
+ [ 2845.743742] [<c03e1f94>] skb_queue_purge+0x14/0x20
+ [ 2845.743742] [<f8171f5a>] hci_conn_del+0x10a/0x1c0 [bluetooth]
+ [ 2845.743742] [<f81399c9>] ? l2cap_disconn_ind+0x59/0xb0 [l2cap]
+ [ 2845.743742] [<f81795ce>] ? hci_conn_del_sysfs+0x8e/0xd0 [bluetooth]
+ [ 2845.743742] [<f8175758>] hci_event_packet+0x5f8/0x31c0 [bluetooth]
+ [ 2845.743742] [<c03dfe19>] ? sock_def_readable+0x59/0x80
+ [ 2845.743742] [<c046c14d>] ? _read_unlock+0x1d/0x20
+ [ 2845.743742] [<f8178aa9>] ? hci_send_to_sock+0xe9/0x1d0 [bluetooth]
+ [ 2845.743742] [<c015388b>] ? trace_hardirqs_on+0xb/0x10
+ [ 2845.743742] [<f816fa6a>] hci_rx_task+0x2ba/0x490 [bluetooth]
+ [ 2845.743742] [<c0133661>] ? tasklet_action+0x31/0xc0
+ [ 2845.743742] [<c013367c>] tasklet_action+0x4c/0xc0
+ [ 2845.743742] [<c0132eb7>] __do_softirq+0xa7/0x170
+ [ 2845.743742] [<c0116dec>] ? ack_apic_level+0x5c/0x1c0
+ [ 2845.743742] [<c0132fd7>] do_softirq+0x57/0x60
+ [ 2845.743742] [<c01333dc>] irq_exit+0x7c/0x90
+ [ 2845.743742] [<c01055bb>] do_IRQ+0x4b/0x90
+ [ 2845.743742] [<c01333d5>] ? irq_exit+0x75/0x90
+ [ 2845.743742] [<c010392c>] common_interrupt+0x2c/0x34
+ [ 2845.743742] [<c010a14f>] ? mwait_idle+0x4f/0x70
+ [ 2845.743742] [<c0101c05>] cpu_idle+0x65/0xb0
+ [ 2845.743742] [<c045731e>] rest_init+0x4e/0x60
+ [ 2845.743742] ---[ end trace 4c985b38f02227a1 ]---
+ [ 2845.743742] Rebooting in 3 seconds..
+
+ My logitec bluetooth mouse trying connect to pc, but
+ pc side reject the connection again and again. then panic happens.
+
+ The reason is due to hci_conn_del_sysfs now called in hci_event_packet,
+ the del work is done in a workqueue, so it's possible done before
+ skb_queue_purge called.
+
+ I move the hci_conn_del_sysfs after skb_queue_purge just as that before
+ marcel's commit.
+
+ Remove the hci_conn_del_sysfs in hci_conn_hash_flush as well due to
+ hci_conn_del will deal with the work.
+
+ Signed-off-by: Dave Young <hidave.darkstar@gmail.com>
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 2526d3d8b2f671a7d36cc486af984052cd5a690f
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Fri Feb 20 20:54:06 2009 +0100
+
+ Bluetooth: Permit BT_SECURITY also for L2CAP raw sockets
+
+ Userspace pairing code can be simplified if it doesn't have to fall
+ back to using L2CAP_LM in the case of L2CAP raw sockets. This patch
+ allows the BT_SECURITY socket option to be used for these sockets.
+
+ Signed-off-by: Johan Hedberg <johan.hedberg@nokia.com>
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 37e62f5516cfb210e64fe53457932df4341b0ad1
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Tue Feb 17 21:49:33 2009 +0100
+
+ Bluetooth: Fix RFCOMM usage of in-kernel L2CAP sockets
+
+ The CID value of L2CAP sockets need to be set to zero. All userspace
+ applications do this via memset() on the sockaddr_l2 structure. The
+ RFCOMM implementation uses in-kernel L2CAP sockets and so it has to
+ make sure that l2_cid is set to zero.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 2a517ca687232adc8f14893730644da712010ffc
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Mon Feb 16 03:20:31 2009 +0100
+
+ Bluetooth: Disallow usage of L2CAP CID setting for now
+
+ In the future the L2CAP layer will have full support for fixed channels
+ and right now it already can export the channel assignment, but for the
+ functions bind() and connect() the usage of only CID 0 is allowed. This
+ allows an easy detection if the kernel supports fixed channels or not,
+ because otherwise it would impossible for application to tell.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 8bf4794174659b06d43cc5e290cd384757374613
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Mon Feb 16 02:59:49 2009 +0100
+
+ Bluetooth: Change RFCOMM to use BT_CONNECT2 for BT_DEFER_SETUP
+
+ When BT_DEFER_SETUP is enabled on a RFCOMM socket, then switch its
+ current state from BT_OPEN to BT_CONNECT2. This gives the Bluetooth
+ core a unified way to handle L2CAP and RFCOMM sockets. The BT_CONNECT2
+ state is designated for incoming connections.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit d5f2d2be68876f65dd051b978a7b66265fde9ffd
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Mon Feb 16 02:57:30 2009 +0100
+
+ Bluetooth: Fix poll() misbehavior when using BT_DEFER_SETUP
+
+ When BT_DEFER_SETUP has been enabled on a Bluetooth socket it keeps
+ signaling POLLIN all the time. This is a wrong behavior. The POLLIN
+ should only be signaled if the client socket is in BT_CONNECT2 state
+ and the parent has been BT_DEFER_SETUP enabled.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 96a3183322cba1a2846771b067c99b9d6f481263
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Thu Feb 12 16:23:03 2009 +0100
+
+ Bluetooth: Set authentication requirement before requesting it
+
+ The authentication requirement got only updated when the security level
+ increased. This is a wrong behavior. The authentication requirement is
+ read by the Bluetooth daemon to make proper decisions when handling the
+ IO capabilities exchange. So set the value that is currently expected by
+ the higher layers like L2CAP and RFCOMM.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 00ae4af91d8c5b6814e2bb3bfaaf743845f989eb
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Thu Feb 12 16:19:45 2009 +0100
+
+ Bluetooth: Fix authentication requirements for L2CAP security check
+
+ The L2CAP layer can trigger the authentication via an ACL connection or
+ later on to increase the security level. When increasing the security
+ level it didn't use the same authentication requirements when triggering
+ a new ACL connection. Make sure that exactly the same authentication
+ requirements are used. The only exception here are the L2CAP raw sockets
+ which are only used for dedicated bonding.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 2950f21acb0f6b8fcd964485c2ebf1e06545ac20
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Thu Feb 12 14:02:50 2009 +0100
+
+ Bluetooth: Ask upper layers for HCI disconnect reason
+
+ Some of the qualification tests demand that in case of failures in L2CAP
+ the HCI disconnect should indicate a reason why L2CAP fails. This is a
+ bluntly layer violation since multiple L2CAP connections could be using
+ the same ACL and thus forcing a disconnect reason is not a good idea.
+
+ To comply with the Bluetooth test specification, the disconnect reason
+ is now stored in the L2CAP connection structure and every time a new
+ L2CAP channel is added it will set back to its default. So only in the
+ case where the L2CAP channel with the disconnect reason is really the
+ last one, it will propagated to the HCI layer.
+
+ The HCI layer has been extended with a disconnect indication that allows
+ it to ask upper layers for a disconnect reason. The upper layer must not
+ support this callback and in that case it will nicely default to the
+ existing behavior. If an upper layer like L2CAP can provide a disconnect
+ reason that one will be used to disconnect the ACL or SCO link.
+
+ No modification to the ACL disconnect timeout have been made. So in case
+ of Linux to Linux connection the initiator will disconnect the ACL link
+ before the acceptor side can signal the specific disconnect reason. That
+ is perfectly fine since Linux doesn't make use of this value anyway. The
+ L2CAP layer has a perfect valid error code for rejecting connection due
+ to a security violation. It is unclear why the Bluetooth specification
+ insists on having specific HCI disconnect reason.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit f29972de8e7476706ab3c01304a505e7c95d9040
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Thu Feb 12 05:07:45 2009 +0100
+
+ Bluetooth: Add CID field to L2CAP socket address structure
+
+ In preparation for L2CAP fixed channel support, the CID value of a
+ L2CAP connection needs to be accessible via the socket interface. The
+ CID is the connection identifier and exists as source and destination
+ value. So extend the L2CAP socket address structure with this field and
+ change getsockname() and getpeername() to fill it in.
+
+ The bind() and connect() functions have been modified to handle L2CAP
+ socket address structures of variable sizes. This makes them future
+ proof if additional fields need to be added.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit e1027a7c69700301d14db03d2e049ee60c4f92df
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Mon Feb 9 09:18:02 2009 +0100
+
+ Bluetooth: Request L2CAP fixed channel list if available
+
+ If the extended features mask indicates support for fixed channels,
+ request the list of available fixed channels. This also enables the
+ fixed channel features bit so remote implementations can request
+ information about it. Currently only the signal channel will be
+ listed.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 435fef20acfc48f46476abad55b0cd3aa47b8365
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Mon Feb 9 03:55:28 2009 +0100
+
+ Bluetooth: Don't enforce authentication for L2CAP PSM 1 and 3
+
+ The recommendation for the L2CAP PSM 1 (SDP) is to not use any kind
+ of authentication or encryption. So don't trigger authentication
+ for incoming and outgoing SDP connections.
+
+ For L2CAP PSM 3 (RFCOMM) there is no clear requirement, but with
+ Bluetooth 2.1 the initiator is required to enable authentication
+ and encryption first and this gets enforced. So there is no need
+ to trigger an additional authentication step. The RFCOMM service
+ security will make sure that a secure enough link key is present.
+
+ When the encryption gets enabled after the SDP connection setup,
+ then switch the security level from SDP to low security.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 6a8d3010b313d99adbb28f1826fac0234395bb26
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Fri Feb 6 23:56:36 2009 +0100
+
+ Bluetooth: Fix double L2CAP connection request
+
+ If the remote L2CAP server uses authentication pending stage and
+ encryption is enabled it can happen that a L2CAP connection request is
+ sent twice due to a race condition in the connection state machine.
+
+ When the remote side indicates any kind of connection pending, then
+ track this state and skip sending of L2CAP commands for this period.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 984947dc64f82bc6cafa4d84ba1a139718f634a8
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Fri Feb 6 23:35:19 2009 +0100
+
+ Bluetooth: Fix race condition with L2CAP information request
+
+ When two L2CAP connections are requested quickly after the ACL link has
+ been established there exists a window for a race condition where a
+ connection request is sent before the information response has been
+ received. Any connection request should only be sent after an exchange
+ of the extended features mask has been finished.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 657e17b03c80bec817975984d221bef716f83558
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Fri Feb 6 19:45:36 2009 +0100
+
+ Bluetooth: Set authentication requirements if not available
+
+ When no authentication requirements are selected, but an outgoing or
+ incoming connection has requested any kind of security enforcement,
+ then set these authentication requirements.
+
+ This ensures that the userspace always gets informed about the
+ authentication requirements (if available). Only when no security
+ enforcement has happened, the kernel will signal invalid requirements.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 0684e5f9fb9e3f7e168ab831dfca693bcb44805b
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Mon Feb 9 02:48:38 2009 +0100
+
+ Bluetooth: Use general bonding whenever possible
+
+ When receiving incoming connection to specific services, always use
+ general bonding. This ensures that the link key gets stored and can be
+ used for further authentications.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit efc7688b557dd1be10eead7399b315efcb1dbc74
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Fri Feb 6 09:13:37 2009 +0100
+
+ Bluetooth: Add SCO fallback for eSCO connection attempts
+
+ When attempting to setup eSCO connections it can happen that some link
+ manager implementations fail to properly negotiate the eSCO parameters
+ and thus fail the eSCO setup. Normally the link manager is responsible
+ for the negotiation of the parameters and actually fallback to SCO if
+ no agreement can be reached. In cases where the link manager is just too
+ stupid, then at least try to establish a SCO link if eSCO fails.
+
+ For the Bluetooth devices with EDR support this includes handling packet
+ types of EDR basebands. This is particular tricky since for the EDR the
+ logic of enabling/disabling one specific packet type is turned around.
+ This fix contains an extra bitmask to disable eSCO EDR packet when
+ trying to fallback to a SCO connection.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 255c76014af74165428e7aa16414b857e2bdccf2
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Wed Feb 4 21:07:19 2009 +0100
+
+ Bluetooth: Don't check encryption for L2CAP raw sockets
+
+ For L2CAP sockets with medium and high security requirement a missing
+ encryption will enforce the closing of the link. For the L2CAP raw
+ sockets this is not needed, so skip that check.
+
+ This fixes a crash when pairing Bluetooth 2.0 (and earlier) devices
+ since the L2CAP state machine got confused and then locked up the whole
+ system.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 43c2e57f94c15744495fee564610aa24602b3824
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Wed Feb 4 17:41:38 2009 +0100
+
+ Bluetooth: Submit bulk URBs along with interrupt URBs
+
+ Submitting the bulk URBs for ACL data transfers only on demand has no
+ real benefit compared to just submit them when a Bluetooth device gets
+ opened. So when submitting the interrupt URBs for HCI events, just
+ submit the bulk URBs, too.
+
+ This solves a problem with some Bluetooth USB dongles that has been
+ reported over the last few month. These devices require that the bulk
+ URBs are actually present. These devices are really broken, but there
+ is nothing we can do about it.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 6e1031a40029492c10509e8c3dcac9b611438ccb
+Author: Jaikumar Ganesh <jaikumar@google.com>
+Date: Mon Feb 2 18:03:57 2009 -0800
+
+ Bluetooth: When encryption is dropped, do not send RFCOMM packets
+
+ During a role change with pre-Bluetooth 2.1 devices, the remote side drops
+ the encryption of the RFCOMM connection. We allow a grace period for the
+ encryption to be re-established, before dropping the connection. During
+ this grace period, the RFCOMM_SEC_PENDING flag is set. Check this flag
+ before sending RFCOMM packets.
+
+ Signed-off-by: Jaikumar Ganesh <jaikumar@google.com>
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 34a55eda483e8177c9044f93fd2c9107f02bf1c7
+Author: Andre Haupt <andre@bitwigglers.org>
+Date: Mon Feb 2 14:45:11 2009 -0800
+
+ Bluetooth: Eliminate a sparse warning in bt3c driver
+
+ This eliminates a sparse warning that symbol 'stat' shadows an earlier one.
+
+ Signed-off-by: Andre Haupt <andre@bitwigglers.org>
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit dd2efd03b49d56ae795c71335bc7358022514c32
+Author: Dave Young <hidave.darkstar@gmail.com>
+Date: Sat Jan 31 13:51:15 2009 +0800
+
+ Bluetooth: Remove CONFIG_DEBUG_LOCK_ALLOC ifdefs
+
+ Due to lockdep changes, the CONFIG_DEBUG_LOCK_ALLOC ifdef is not needed
+ now. So just remove it here.
+
+ The following commit fixed the !lockdep build warnings:
+
+ commit e8f6fbf62de37cbc2e179176ac7010d5f4396b67
+ Author: Ingo Molnar <mingo@elte.hu>
+ Date: Wed Nov 12 01:38:36 2008 +0000
+
+ lockdep: include/linux/lockdep.h - fix warning in net/bluetooth/af_bluetooth.c
+
+ Signed-off-by: Dave Young <hidave.darkstar@gmail.com>
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 5f9018af004fa8635bbbe3ab2dc61e8a686edfaa
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Fri Jan 16 10:09:50 2009 +0100
+
+ Bluetooth: Update version numbers
+
+ With the support for the enhanced security model and the support for
+ deferring connection setup, it is a good idea to increase various
+ version numbers.
+
+ This is purely cosmetic and has no effect on the behavior, but can
+ be really helpful when debugging problems in different kernel versions.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 0588d94fd7e414367a7ae517569d2222441c255f
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Fri Jan 16 10:06:13 2009 +0100
+
+ Bluetooth: Restrict application of socket options
+
+ The new socket options should only be evaluated for SOL_BLUETOOTH level
+ and not for every other level. Previously this causes some minor issues
+ when detecting if a kernel with certain features is available.
+
+ Also restrict BT_SECURITY to SOCK_SEQPACKET for L2CAP and SOCK_STREAM for
+ the RFCOMM protocol.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit f62e4323ab43c59e7cd7f72c1eb392d7c767ce5a
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Thu Jan 15 21:58:44 2009 +0100
+
+ Bluetooth: Disconnect L2CAP connections without encryption
+
+ For L2CAP connections with high security setting, the link will be
+ immediately dropped when the encryption gets disabled. For L2CAP
+ connections with medium security there will be grace period where
+ the remote device has the chance to re-enable encryption. If it
+ doesn't happen then the link will also be disconnected.
+
+ The requirement for the grace period with medium security comes from
+ Bluetooth 2.0 and earlier devices that require role switching.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 8c84b83076b5062f59b6167cdda90d9e5124aa71
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Fri Jan 16 08:17:51 2009 +0100
+
+ Bluetooth: Pause RFCOMM TX when encryption drops
+
+ A role switch with devices following the Bluetooth pre-2.1 standards
+ or without Encryption Pause and Resume support is not possible if
+ encryption is enabled. Most newer headsets require the role switch,
+ but also require that the connection is encrypted.
+
+ For connections with a high security mode setting, the link will be
+ immediately dropped. When the connection uses medium security mode
+ setting, then a grace period is introduced where the TX is halted and
+ the remote device gets a change to re-enable encryption after the
+ role switch. If not re-enabled the link will be dropped.
+
+ Based on initial work by Ville Tervo <ville.tervo@nokia.com>
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 9f2c8a03fbb3048cf38b158f87aa0c3c09bca084
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Thu Jan 15 21:58:40 2009 +0100
+
+ Bluetooth: Replace RFCOMM link mode with security level
+
+ Change the RFCOMM internals to use the new security levels and remove
+ the link mode details.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 2af6b9d518ddfbc4d6990d5f9c9b1a05341c1cef
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Thu Jan 15 21:58:38 2009 +0100
+
+ Bluetooth: Replace L2CAP link mode with security level
+
+ Change the L2CAP internals to use the new security levels and remove
+ the link mode details.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 8c1b235594fbab9a13240a1dac12ea9fd99b6440
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Thu Jan 15 21:58:04 2009 +0100
+
+ Bluetooth: Add enhanced security model for Simple Pairing
+
+ The current security model is based around the flags AUTH, ENCRYPT and
+ SECURE. Starting with support for the Bluetooth 2.1 specification this is
+ no longer sufficient. The different security levels are now defined as
+ SDP, LOW, MEDIUM and SECURE.
+
+ Previously it was possible to set each security independently, but this
+ actually doesn't make a lot of sense. For Bluetooth the encryption depends
+ on a previous successful authentication. Also you can only update your
+ existing link key if you successfully created at least one before. And of
+ course the update of link keys without having proper encryption in place
+ is a security issue.
+
+ The new security levels from the Bluetooth 2.1 specification are now
+ used internally. All old settings are mapped to the new values and this
+ way it ensures that old applications still work. The only limitation
+ is that it is no longer possible to set authentication without also
+ enabling encryption. No application should have done this anyway since
+ this is actually a security issue. Without encryption the integrity of
+ the authentication can't be guaranteed.
+
+ As default for a new L2CAP or RFCOMM connection, the LOW security level
+ is used. The only exception here are the service discovery sessions on
+ PSM 1 where SDP level is used. To have similar security strength as with
+ a Bluetooth 2.0 and before combination key, the MEDIUM level should be
+ used. This is according to the Bluetooth specification. The MEDIUM level
+ will not require any kind of man-in-the-middle (MITM) protection. Only
+ the HIGH security level will require this.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit c89b6e6bda4c8021195778f47567d0cc9dbfe7ec
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Thu Jan 15 21:57:03 2009 +0100
+
+ Bluetooth: Fix SCO state handling for incoming connections
+
+ When the remote device supports only SCO connections, on receipt of
+ the HCI_EV_CONN_COMPLETE event packet, the connect state is changed to
+ BT_CONNECTED, but the socket state is not updated. Hence, the connect()
+ call times out even though the SCO connection has been successfully
+ established.
+
+ Based on a report by Jaikumar Ganesh <jaikumar@google.com>
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 71aeeaa1fd88fe7446391e0553336f0e0c2cfe6a
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Thu Jan 15 21:57:02 2009 +0100
+
+ Bluetooth: Reject incoming SCO connections without listeners
+
+ All SCO and eSCO connection are auto-accepted no matter if there is a
+ corresponding listening socket for them. This patch changes this and
+ connection requests for SCO and eSCO without any socket are rejected.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit f66dc81f44d918ee1aa1a9d821bb2f25c7592bc0
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Thu Jan 15 21:57:00 2009 +0100
+
+ Bluetooth: Add support for deferring L2CAP connection setup
+
+ In order to decide if listening L2CAP sockets should be accept()ed
+ the BD_ADDR of the remote device needs to be known. This patch adds
+ a socket option which defines a timeout for deferring the actual
+ connection setup.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit bb23c0ab824653be4aa7dfca15b07b3059717004
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Thu Jan 15 21:56:48 2009 +0100
+
+ Bluetooth: Add support for deferring RFCOMM connection setup
+
+ In order to decide if listening RFCOMM sockets should be accept()ed
+ the BD_ADDR of the remote device needs to be known. This patch adds
+ a socket option which defines a timeout for deferring the actual
+ connection setup.
+
+ The connection setup is done after reading from the socket for the
+ first time. Until then writing to the socket returns ENOTCONN.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit c4f912e155504e94dd4f3d63c378dab0ff03dbda
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Thu Jan 15 21:52:16 2009 +0100
+
+ Bluetooth: Add global deferred socket parameter
+
+ The L2CAP and RFCOMM applications require support for authorization
+ and the ability of rejecting incoming connection requests. The socket
+ interface is not really able to support this.
+
+ This patch does the ground work for a socket option to defer connection
+ setup. Setting this option allows calling of accept() and then the
+ first read() will trigger the final connection setup. Calling close()
+ would reject the connection.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit d58daf42d29a3a4a4d4be46cf47ceee096789680
+Author: Marcel Holtmann <marcel@holtmann.org>
+Date: Thu Jan 15 21:52:14 2009 +0100
+
+ Bluetooth: Preparation for usage of SOL_BLUETOOTH
+
+ The socket option levels SOL_L2CAP, SOL_RFOMM and SOL_SCO are currently
+ in use by various Bluetooth applications. Going forward the common
+ option level SOL_BLUETOOTH should be used. This patch prepares the clean
+ split of the old and new option levels while keeping everything backward
+ compatibility.
+
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+commit 91aa35a5aa3540223066bf6b51c935418c63a35d
+Author: Victor Shcherbatyuk <victor.shcherbatyuk@tomtom.com>
+Date: Thu Jan 15 21:52:12 2009 +0100
+
+ Bluetooth: Fix issue with return value of rfcomm_sock_sendmsg()
+
+ In case of connection failures the rfcomm_sock_sendmsg() should return
+ an error and not a 0 value.
+
+ Signed-off-by: Victor Shcherbatyuk <victor.shcherbatyuk@tomtom.com>
+ Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
+
+diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
+index d3f14be..2a00707 100644
+--- a/drivers/bluetooth/bfusb.c
++++ b/drivers/bluetooth/bfusb.c
+@@ -257,8 +257,7 @@ static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned ch
+
+ if (hdr & 0x10) {
+ BT_ERR("%s error in block", data->hdev->name);
+- if (data->reassembly)
+- kfree_skb(data->reassembly);
++ kfree_skb(data->reassembly);
+ data->reassembly = NULL;
+ return -EIO;
+ }
+diff --git a/drivers/bluetooth/bt3c_cs.c b/drivers/bluetooth/bt3c_cs.c
+index ff195c2..d58e22b 100644
+--- a/drivers/bluetooth/bt3c_cs.c
++++ b/drivers/bluetooth/bt3c_cs.c
+@@ -359,9 +359,9 @@ static irqreturn_t bt3c_interrupt(int irq, void *dev_inst)
+ BT_ERR("Very strange (stat=0x%04x)", stat);
+ } else if ((stat & 0xff) != 0xff) {
+ if (stat & 0x0020) {
+- int stat = bt3c_read(iobase, 0x7002) & 0x10;
++ int status = bt3c_read(iobase, 0x7002) & 0x10;
+ BT_INFO("%s: Antenna %s", info->hdev->name,
+- stat ? "out" : "in");
++ status ? "out" : "in");
+ }
+ if (stat & 0x0001)
+ bt3c_receive(info);
+diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
+index b5fbda6..e70c57e 100644
+--- a/drivers/bluetooth/btusb.c
++++ b/drivers/bluetooth/btusb.c
+@@ -35,7 +35,7 @@
+ #include <net/bluetooth/bluetooth.h>
+ #include <net/bluetooth/hci_core.h>
+
+-#define VERSION "0.4"
++#define VERSION "0.5"
+
+ static int ignore_dga;
+ static int ignore_csr;
+@@ -171,6 +171,7 @@ struct btusb_data {
+
+ __u8 cmdreq_type;
+
++ unsigned int sco_num;
+ int isoc_altsetting;
+ int suspend_count;
+ };
+@@ -496,11 +497,23 @@ static int btusb_open(struct hci_dev *hdev)
+ return 0;
+
+ err = btusb_submit_intr_urb(hdev, GFP_KERNEL);
++ if (err < 0)
++ goto failed;
++
++ err = btusb_submit_bulk_urb(hdev, GFP_KERNEL);
+ if (err < 0) {
+- clear_bit(BTUSB_INTR_RUNNING, &data->flags);
+- clear_bit(HCI_RUNNING, &hdev->flags);
++ usb_kill_anchored_urbs(&data->intr_anchor);
++ goto failed;
+ }
+
++ set_bit(BTUSB_BULK_RUNNING, &data->flags);
++ btusb_submit_bulk_urb(hdev, GFP_KERNEL);
++
++ return 0;
++
++failed:
++ clear_bit(BTUSB_INTR_RUNNING, &data->flags);
++ clear_bit(HCI_RUNNING, &hdev->flags);
+ return err;
+ }
+
+@@ -655,19 +668,10 @@ static void btusb_notify(struct hci_dev *hdev, unsigned int evt)
+
+ BT_DBG("%s evt %d", hdev->name, evt);
+
+- if (hdev->conn_hash.acl_num > 0) {
+- if (!test_and_set_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+- if (btusb_submit_bulk_urb(hdev, GFP_ATOMIC) < 0)
+- clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+- else
+- btusb_submit_bulk_urb(hdev, GFP_ATOMIC);
+- }
+- } else {
+- clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+- usb_unlink_anchored_urbs(&data->bulk_anchor);
++ if (hdev->conn_hash.sco_num != data->sco_num) {
++ data->sco_num = hdev->conn_hash.sco_num;
++ schedule_work(&data->work);
+ }
+-
+- schedule_work(&data->work);
+ }
+
+ static int inline __set_isoc_interface(struct hci_dev *hdev, int altsetting)
+@@ -982,9 +986,11 @@ static int btusb_resume(struct usb_interface *intf)
+ }
+
+ if (test_bit(BTUSB_BULK_RUNNING, &data->flags)) {
+- if (btusb_submit_bulk_urb(hdev, GFP_NOIO) < 0)
++ err = btusb_submit_bulk_urb(hdev, GFP_NOIO);
++ if (err < 0) {
+ clear_bit(BTUSB_BULK_RUNNING, &data->flags);
+- else
++ return err;
++ } else
+ btusb_submit_bulk_urb(hdev, GFP_NOIO);
+ }
+
+diff --git a/drivers/bluetooth/hci_h4.c b/drivers/bluetooth/hci_h4.c
+index b0fafb0..c0ce813 100644
+--- a/drivers/bluetooth/hci_h4.c
++++ b/drivers/bluetooth/hci_h4.c
+@@ -102,8 +102,7 @@ static int h4_close(struct hci_uart *hu)
+
+ skb_queue_purge(&h4->txq);
+
+- if (h4->rx_skb)
+- kfree_skb(h4->rx_skb);
++ kfree_skb(h4->rx_skb);
+
+ hu->priv = NULL;
+ kfree(h4);
+diff --git a/drivers/bluetooth/hci_ll.c b/drivers/bluetooth/hci_ll.c
+index b91d45a..5c65014 100644
+--- a/drivers/bluetooth/hci_ll.c
++++ b/drivers/bluetooth/hci_ll.c
+@@ -163,8 +163,7 @@ static int ll_close(struct hci_uart *hu)
+ skb_queue_purge(&ll->tx_wait_q);
+ skb_queue_purge(&ll->txq);
+
+- if (ll->rx_skb)
+- kfree_skb(ll->rx_skb);
++ kfree_skb(ll->rx_skb);
+
+ hu->priv = NULL;
+
+diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h
+index a04f846..3ad5390 100644
+--- a/include/net/bluetooth/bluetooth.h
++++ b/include/net/bluetooth/bluetooth.h
+@@ -53,6 +53,17 @@
+ #define SOL_SCO 17
+ #define SOL_RFCOMM 18
+
++#define BT_SECURITY 4
++struct bt_security {
++ __u8 level;
++};
++#define BT_SECURITY_SDP 0
++#define BT_SECURITY_LOW 1
++#define BT_SECURITY_MEDIUM 2
++#define BT_SECURITY_HIGH 3
++
++#define BT_DEFER_SETUP 7
++
+ #define BT_INFO(fmt, arg...) printk(KERN_INFO "Bluetooth: " fmt "\n" , ## arg)
+ #define BT_ERR(fmt, arg...) printk(KERN_ERR "%s: " fmt "\n" , __func__ , ## arg)
+ #define BT_DBG(fmt, arg...) pr_debug("%s: " fmt "\n" , __func__ , ## arg)
+@@ -108,6 +119,7 @@ struct bt_sock {
+ bdaddr_t dst;
+ struct list_head accept_q;
+ struct sock *parent;
++ u32 defer_setup;
+ };
+
+ struct bt_sock_list {
+diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
+index 3645139..f69f015 100644
+--- a/include/net/bluetooth/hci.h
++++ b/include/net/bluetooth/hci.h
+@@ -133,8 +133,13 @@ enum {
+ #define ESCO_EV3 0x0008
+ #define ESCO_EV4 0x0010
+ #define ESCO_EV5 0x0020
++#define ESCO_2EV3 0x0040
++#define ESCO_3EV3 0x0080
++#define ESCO_2EV5 0x0100
++#define ESCO_3EV5 0x0200
+
+ #define SCO_ESCO_MASK (ESCO_HV1 | ESCO_HV2 | ESCO_HV3)
++#define EDR_ESCO_MASK (ESCO_2EV3 | ESCO_3EV3 | ESCO_2EV5 | ESCO_3EV5)
+
+ /* ACL flags */
+ #define ACL_CONT 0x01
+@@ -176,6 +181,9 @@ enum {
+ #define LMP_EV5 0x02
+
+ #define LMP_SNIFF_SUBR 0x02
++#define LMP_EDR_ESCO_2M 0x20
++#define LMP_EDR_ESCO_3M 0x40
++#define LMP_EDR_3S_ESCO 0x80
+
+ #define LMP_SIMPLE_PAIR 0x08
+
+diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
+index 46a43b7..01f9316 100644
+--- a/include/net/bluetooth/hci_core.h
++++ b/include/net/bluetooth/hci_core.h
+@@ -169,6 +169,7 @@ struct hci_conn {
+ __u16 link_policy;
+ __u32 link_mode;
+ __u8 auth_type;
++ __u8 sec_level;
+ __u8 power_save;
+ unsigned long pend;
+
+@@ -325,12 +326,11 @@ int hci_conn_del(struct hci_conn *conn);
+ void hci_conn_hash_flush(struct hci_dev *hdev);
+ void hci_conn_check_pending(struct hci_dev *hdev);
+
+-struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 auth_type);
++struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type);
+ int hci_conn_check_link_mode(struct hci_conn *conn);
+-int hci_conn_auth(struct hci_conn *conn);
+-int hci_conn_encrypt(struct hci_conn *conn);
++int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type);
+ int hci_conn_change_link_key(struct hci_conn *conn);
+-int hci_conn_switch_role(struct hci_conn *conn, uint8_t role);
++int hci_conn_switch_role(struct hci_conn *conn, __u8 role);
+
+ void hci_conn_enter_active_mode(struct hci_conn *conn);
+ void hci_conn_enter_sniff_mode(struct hci_conn *conn);
+@@ -470,26 +470,26 @@ void hci_conn_del_sysfs(struct hci_conn *conn);
+
+ /* ----- HCI protocols ----- */
+ struct hci_proto {
+- char *name;
++ char *name;
+ unsigned int id;
+ unsigned long flags;
+
+ void *priv;
+
+- int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type);
++ int (*connect_ind) (struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type);
+ int (*connect_cfm) (struct hci_conn *conn, __u8 status);
+- int (*disconn_ind) (struct hci_conn *conn, __u8 reason);
++ int (*disconn_ind) (struct hci_conn *conn);
++ int (*disconn_cfm) (struct hci_conn *conn, __u8 reason);
+ int (*recv_acldata) (struct hci_conn *conn, struct sk_buff *skb, __u16 flags);
+ int (*recv_scodata) (struct hci_conn *conn, struct sk_buff *skb);
+- int (*auth_cfm) (struct hci_conn *conn, __u8 status);
+- int (*encrypt_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt);
++ int (*security_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt);
+ };
+
+ static inline int hci_proto_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
+ {
+ register struct hci_proto *hp;
+ int mask = 0;
+-
++
+ hp = hci_proto[HCI_PROTO_L2CAP];
+ if (hp && hp->connect_ind)
+ mask |= hp->connect_ind(hdev, bdaddr, type);
+@@ -514,30 +514,52 @@ static inline void hci_proto_connect_cfm(struct hci_conn *conn, __u8 status)
+ hp->connect_cfm(conn, status);
+ }
+
+-static inline void hci_proto_disconn_ind(struct hci_conn *conn, __u8 reason)
++static inline int hci_proto_disconn_ind(struct hci_conn *conn)
+ {
+ register struct hci_proto *hp;
++ int reason = 0x13;
+
+ hp = hci_proto[HCI_PROTO_L2CAP];
+ if (hp && hp->disconn_ind)
+- hp->disconn_ind(conn, reason);
++ reason = hp->disconn_ind(conn);
+
+ hp = hci_proto[HCI_PROTO_SCO];
+ if (hp && hp->disconn_ind)
+- hp->disconn_ind(conn, reason);
++ reason = hp->disconn_ind(conn);
++
++ return reason;
++}
++
++static inline void hci_proto_disconn_cfm(struct hci_conn *conn, __u8 reason)
++{
++ register struct hci_proto *hp;
++
++ hp = hci_proto[HCI_PROTO_L2CAP];
++ if (hp && hp->disconn_cfm)
++ hp->disconn_cfm(conn, reason);
++
++ hp = hci_proto[HCI_PROTO_SCO];
++ if (hp && hp->disconn_cfm)
++ hp->disconn_cfm(conn, reason);
+ }
+
+ static inline void hci_proto_auth_cfm(struct hci_conn *conn, __u8 status)
+ {
+ register struct hci_proto *hp;
++ __u8 encrypt;
++
++ if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
++ return;
++
++ encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00;
+
+ hp = hci_proto[HCI_PROTO_L2CAP];
+- if (hp && hp->auth_cfm)
+- hp->auth_cfm(conn, status);
++ if (hp && hp->security_cfm)
++ hp->security_cfm(conn, status, encrypt);
+
+ hp = hci_proto[HCI_PROTO_SCO];
+- if (hp && hp->auth_cfm)
+- hp->auth_cfm(conn, status);
++ if (hp && hp->security_cfm)
++ hp->security_cfm(conn, status, encrypt);
+ }
+
+ static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encrypt)
+@@ -545,12 +567,12 @@ static inline void hci_proto_encrypt_cfm(struct hci_conn *conn, __u8 status, __u
+ register struct hci_proto *hp;
+
+ hp = hci_proto[HCI_PROTO_L2CAP];
+- if (hp && hp->encrypt_cfm)
+- hp->encrypt_cfm(conn, status, encrypt);
++ if (hp && hp->security_cfm)
++ hp->security_cfm(conn, status, encrypt);
+
+ hp = hci_proto[HCI_PROTO_SCO];
+- if (hp && hp->encrypt_cfm)
+- hp->encrypt_cfm(conn, status, encrypt);
++ if (hp && hp->security_cfm)
++ hp->security_cfm(conn, status, encrypt);
+ }
+
+ int hci_register_proto(struct hci_proto *hproto);
+@@ -562,8 +584,7 @@ struct hci_cb {
+
+ char *name;
+
+- void (*auth_cfm) (struct hci_conn *conn, __u8 status);
+- void (*encrypt_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt);
++ void (*security_cfm) (struct hci_conn *conn, __u8 status, __u8 encrypt);
+ void (*key_change_cfm) (struct hci_conn *conn, __u8 status);
+ void (*role_switch_cfm) (struct hci_conn *conn, __u8 status, __u8 role);
+ };
+@@ -571,14 +592,20 @@ struct hci_cb {
+ static inline void hci_auth_cfm(struct hci_conn *conn, __u8 status)
+ {
+ struct list_head *p;
++ __u8 encrypt;
+
+ hci_proto_auth_cfm(conn, status);
+
++ if (test_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
++ return;
++
++ encrypt = (conn->link_mode & HCI_LM_ENCRYPT) ? 0x01 : 0x00;
++
+ read_lock_bh(&hci_cb_list_lock);
+ list_for_each(p, &hci_cb_list) {
+ struct hci_cb *cb = list_entry(p, struct hci_cb, list);
+- if (cb->auth_cfm)
+- cb->auth_cfm(conn, status);
++ if (cb->security_cfm)
++ cb->security_cfm(conn, status, encrypt);
+ }
+ read_unlock_bh(&hci_cb_list_lock);
+ }
+@@ -587,13 +614,16 @@ static inline void hci_encrypt_cfm(struct hci_conn *conn, __u8 status, __u8 encr
+ {
+ struct list_head *p;
+
++ if (conn->sec_level == BT_SECURITY_SDP)
++ conn->sec_level = BT_SECURITY_LOW;
++
+ hci_proto_encrypt_cfm(conn, status, encrypt);
+
+ read_lock_bh(&hci_cb_list_lock);
+ list_for_each(p, &hci_cb_list) {
+ struct hci_cb *cb = list_entry(p, struct hci_cb, list);
+- if (cb->encrypt_cfm)
+- cb->encrypt_cfm(conn, status, encrypt);
++ if (cb->security_cfm)
++ cb->security_cfm(conn, status, encrypt);
+ }
+ read_unlock_bh(&hci_cb_list_lock);
+ }
+diff --git a/include/net/bluetooth/l2cap.h b/include/net/bluetooth/l2cap.h
+index 73e115b..f566aa1 100644
+--- a/include/net/bluetooth/l2cap.h
++++ b/include/net/bluetooth/l2cap.h
+@@ -37,6 +37,7 @@ struct sockaddr_l2 {
+ sa_family_t l2_family;
+ __le16 l2_psm;
+ bdaddr_t l2_bdaddr;
++ __le16 l2_cid;
+ };
+
+ /* L2CAP socket options */
+@@ -185,6 +186,7 @@ struct l2cap_info_rsp {
+ /* info type */
+ #define L2CAP_IT_CL_MTU 0x0001
+ #define L2CAP_IT_FEAT_MASK 0x0002
++#define L2CAP_IT_FIXED_CHAN 0x0003
+
+ /* info result */
+ #define L2CAP_IR_SUCCESS 0x0000
+@@ -219,11 +221,14 @@ struct l2cap_conn {
+ __u8 rx_ident;
+ __u8 tx_ident;
+
++ __u8 disc_reason;
++
+ struct l2cap_chan_list chan_list;
+ };
+
+ #define L2CAP_INFO_CL_MTU_REQ_SENT 0x01
+-#define L2CAP_INFO_FEAT_MASK_REQ_SENT 0x02
++#define L2CAP_INFO_FEAT_MASK_REQ_SENT 0x04
++#define L2CAP_INFO_FEAT_MASK_REQ_DONE 0x08
+
+ /* ----- L2CAP channel and socket info ----- */
+ #define l2cap_pi(sk) ((struct l2cap_pinfo *) sk)
+@@ -237,8 +242,9 @@ struct l2cap_pinfo {
+ __u16 imtu;
+ __u16 omtu;
+ __u16 flush_to;
+-
+- __u32 link_mode;
++ __u8 sec_level;
++ __u8 role_switch;
++ __u8 force_reliable;
+
+ __u8 conf_req[64];
+ __u8 conf_len;
+@@ -257,6 +263,7 @@ struct l2cap_pinfo {
+ #define L2CAP_CONF_REQ_SENT 0x01
+ #define L2CAP_CONF_INPUT_DONE 0x02
+ #define L2CAP_CONF_OUTPUT_DONE 0x04
++#define L2CAP_CONF_CONNECT_PEND 0x80
+
+ #define L2CAP_CONF_MAX_RETRIES 2
+
+diff --git a/include/net/bluetooth/rfcomm.h b/include/net/bluetooth/rfcomm.h
+index 4dc8d92..8007261 100644
+--- a/include/net/bluetooth/rfcomm.h
++++ b/include/net/bluetooth/rfcomm.h
+@@ -183,8 +183,9 @@ struct rfcomm_dlc {
+ u8 remote_v24_sig;
+ u8 mscex;
+ u8 out;
+-
+- u32 link_mode;
++ u8 sec_level;
++ u8 role_switch;
++ u32 defer_setup;
+
+ uint mtu;
+ uint cfc;
+@@ -202,10 +203,12 @@ struct rfcomm_dlc {
+ #define RFCOMM_RX_THROTTLED 0
+ #define RFCOMM_TX_THROTTLED 1
+ #define RFCOMM_TIMED_OUT 2
+-#define RFCOMM_MSC_PENDING 3
+-#define RFCOMM_AUTH_PENDING 4
+-#define RFCOMM_AUTH_ACCEPT 5
+-#define RFCOMM_AUTH_REJECT 6
++#define RFCOMM_MSC_PENDING 3
++#define RFCOMM_SEC_PENDING 4
++#define RFCOMM_AUTH_PENDING 5
++#define RFCOMM_AUTH_ACCEPT 6
++#define RFCOMM_AUTH_REJECT 7
++#define RFCOMM_DEFER_SETUP 8
+
+ /* Scheduling flags and events */
+ #define RFCOMM_SCHED_STATE 0
+@@ -239,6 +242,7 @@ int rfcomm_dlc_close(struct rfcomm_dlc *d, int reason);
+ int rfcomm_dlc_send(struct rfcomm_dlc *d, struct sk_buff *skb);
+ int rfcomm_dlc_set_modem_status(struct rfcomm_dlc *d, u8 v24_sig);
+ int rfcomm_dlc_get_modem_status(struct rfcomm_dlc *d, u8 *v24_sig);
++void rfcomm_dlc_accept(struct rfcomm_dlc *d);
+
+ #define rfcomm_dlc_lock(d) spin_lock(&d->lock)
+ #define rfcomm_dlc_unlock(d) spin_unlock(&d->lock)
+@@ -304,7 +308,8 @@ struct rfcomm_pinfo {
+ struct bt_sock bt;
+ struct rfcomm_dlc *dlc;
+ u8 channel;
+- u32 link_mode;
++ u8 sec_level;
++ u8 role_switch;
+ };
+
+ int rfcomm_init_sockets(void);
+@@ -333,7 +338,6 @@ struct rfcomm_dev_req {
+ bdaddr_t src;
+ bdaddr_t dst;
+ u8 channel;
+-
+ };
+
+ struct rfcomm_dev_info {
+diff --git a/net/bluetooth/af_bluetooth.c b/net/bluetooth/af_bluetooth.c
+index 744ed3f..02b9baa 100644
+--- a/net/bluetooth/af_bluetooth.c
++++ b/net/bluetooth/af_bluetooth.c
+@@ -41,14 +41,13 @@
+
+ #include <net/bluetooth/bluetooth.h>
+
+-#define VERSION "2.14"
++#define VERSION "2.15"
+
+ /* Bluetooth sockets */
+ #define BT_MAX_PROTO 8
+ static struct net_proto_family *bt_proto[BT_MAX_PROTO];
+ static DEFINE_RWLOCK(bt_proto_lock);
+
+-#ifdef CONFIG_DEBUG_LOCK_ALLOC
+ static struct lock_class_key bt_lock_key[BT_MAX_PROTO];
+ static const char *bt_key_strings[BT_MAX_PROTO] = {
+ "sk_lock-AF_BLUETOOTH-BTPROTO_L2CAP",
+@@ -86,11 +85,6 @@ static inline void bt_sock_reclassify_lock(struct socket *sock, int proto)
+ bt_slock_key_strings[proto], &bt_slock_key[proto],
+ bt_key_strings[proto], &bt_lock_key[proto]);
+ }
+-#else
+-static inline void bt_sock_reclassify_lock(struct socket *sock, int proto)
+-{
+-}
+-#endif
+
+ int bt_sock_register(int proto, struct net_proto_family *ops)
+ {
+@@ -217,7 +211,8 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
+ continue;
+ }
+
+- if (sk->sk_state == BT_CONNECTED || !newsock) {
++ if (sk->sk_state == BT_CONNECTED || !newsock ||
++ bt_sk(parent)->defer_setup) {
+ bt_accept_unlink(sk);
+ if (newsock)
+ sock_graft(sk, newsock);
+@@ -232,7 +227,7 @@ struct sock *bt_accept_dequeue(struct sock *parent, struct socket *newsock)
+ EXPORT_SYMBOL(bt_accept_dequeue);
+
+ int bt_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
+- struct msghdr *msg, size_t len, int flags)
++ struct msghdr *msg, size_t len, int flags)
+ {
+ int noblock = flags & MSG_DONTWAIT;
+ struct sock *sk = sock->sk;
+@@ -277,7 +272,9 @@ static inline unsigned int bt_accept_poll(struct sock *parent)
+
+ list_for_each_safe(p, n, &bt_sk(parent)->accept_q) {
+ sk = (struct sock *) list_entry(p, struct bt_sock, accept_q);
+- if (sk->sk_state == BT_CONNECTED)
++ if (sk->sk_state == BT_CONNECTED ||
++ (bt_sk(parent)->defer_setup &&
++ sk->sk_state == BT_CONNECT2))
+ return POLLIN | POLLRDNORM;
+ }
+
+diff --git a/net/bluetooth/cmtp/core.c b/net/bluetooth/cmtp/core.c
+index c9cac77..0073ec8 100644
+--- a/net/bluetooth/cmtp/core.c
++++ b/net/bluetooth/cmtp/core.c
+@@ -126,8 +126,7 @@ static inline void cmtp_add_msgpart(struct cmtp_session *session, int id, const
+
+ session->reassembly[id] = nskb;
+
+- if (skb)
+- kfree_skb(skb);
++ kfree_skb(skb);
+ }
+
+ static inline int cmtp_recv_frame(struct cmtp_session *session, struct sk_buff *skb)
+diff --git a/net/bluetooth/hci_conn.c b/net/bluetooth/hci_conn.c
+index a4a789f..1181db0 100644
+--- a/net/bluetooth/hci_conn.c
++++ b/net/bluetooth/hci_conn.c
+@@ -123,6 +123,8 @@ void hci_add_sco(struct hci_conn *conn, __u16 handle)
+ conn->state = BT_CONNECT;
+ conn->out = 1;
+
++ conn->attempt++;
++
+ cp.handle = cpu_to_le16(handle);
+ cp.pkt_type = cpu_to_le16(conn->pkt_type);
+
+@@ -139,6 +141,8 @@ void hci_setup_sync(struct hci_conn *conn, __u16 handle)
+ conn->state = BT_CONNECT;
+ conn->out = 1;
+
++ conn->attempt++;
++
+ cp.handle = cpu_to_le16(handle);
+ cp.pkt_type = cpu_to_le16(conn->pkt_type);
+
+@@ -155,6 +159,7 @@ static void hci_conn_timeout(unsigned long arg)
+ {
+ struct hci_conn *conn = (void *) arg;
+ struct hci_dev *hdev = conn->hdev;
++ __u8 reason;
+
+ BT_DBG("conn %p state %d", conn, conn->state);
+
+@@ -173,7 +178,8 @@ static void hci_conn_timeout(unsigned long arg)
+ break;
+ case BT_CONFIG:
+ case BT_CONNECTED:
+- hci_acl_disconn(conn, 0x13);
++ reason = hci_proto_disconn_ind(conn);
++ hci_acl_disconn(conn, reason);
+ break;
+ default:
+ conn->state = BT_CLOSED;
+@@ -216,12 +222,13 @@ struct hci_conn *hci_conn_add(struct hci_dev *hdev, int type, bdaddr_t *dst)
+ break;
+ case SCO_LINK:
+ if (lmp_esco_capable(hdev))
+- conn->pkt_type = hdev->esco_type & SCO_ESCO_MASK;
++ conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
++ (hdev->esco_type & EDR_ESCO_MASK);
+ else
+ conn->pkt_type = hdev->pkt_type & SCO_PTYPE_MASK;
+ break;
+ case ESCO_LINK:
+- conn->pkt_type = hdev->esco_type;
++ conn->pkt_type = hdev->esco_type & ~EDR_ESCO_MASK;
+ break;
+ }
+
+@@ -280,6 +287,8 @@ int hci_conn_del(struct hci_conn *conn)
+
+ skb_queue_purge(&conn->data_q);
+
++ hci_conn_del_sysfs(conn);
++
+ return 0;
+ }
+
+@@ -325,7 +334,7 @@ EXPORT_SYMBOL(hci_get_route);
+
+ /* Create SCO or ACL connection.
+ * Device _must_ be locked */
+-struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 auth_type)
++struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8 sec_level, __u8 auth_type)
+ {
+ struct hci_conn *acl;
+ struct hci_conn *sco;
+@@ -340,6 +349,7 @@ struct hci_conn *hci_connect(struct hci_dev *hdev, int type, bdaddr_t *dst, __u8
+ hci_conn_hold(acl);
+
+ if (acl->state == BT_OPEN || acl->state == BT_CLOSED) {
++ acl->sec_level = sec_level;
+ acl->auth_type = auth_type;
+ hci_acl_connect(acl);
+ }
+@@ -385,51 +395,59 @@ int hci_conn_check_link_mode(struct hci_conn *conn)
+ EXPORT_SYMBOL(hci_conn_check_link_mode);
+
+ /* Authenticate remote device */
+-int hci_conn_auth(struct hci_conn *conn)
++static int hci_conn_auth(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
+ {
+ BT_DBG("conn %p", conn);
+
+- if (conn->ssp_mode > 0 && conn->hdev->ssp_mode > 0) {
+- if (!(conn->auth_type & 0x01)) {
+- conn->auth_type |= 0x01;
+- conn->link_mode &= ~HCI_LM_AUTH;
+- }
+- }
+-
+- if (conn->link_mode & HCI_LM_AUTH)
++ if (sec_level > conn->sec_level)
++ conn->sec_level = sec_level;
++ else if (conn->link_mode & HCI_LM_AUTH)
+ return 1;
+
++ conn->auth_type = auth_type;
++
+ if (!test_and_set_bit(HCI_CONN_AUTH_PEND, &conn->pend)) {
+ struct hci_cp_auth_requested cp;
+ cp.handle = cpu_to_le16(conn->handle);
+ hci_send_cmd(conn->hdev, HCI_OP_AUTH_REQUESTED,
+ sizeof(cp), &cp);
+ }
++
+ return 0;
+ }
+-EXPORT_SYMBOL(hci_conn_auth);
+
+-/* Enable encryption */
+-int hci_conn_encrypt(struct hci_conn *conn)
++/* Enable security */
++int hci_conn_security(struct hci_conn *conn, __u8 sec_level, __u8 auth_type)
+ {
+ BT_DBG("conn %p", conn);
+
++ if (sec_level == BT_SECURITY_SDP)
++ return 1;
++
++ if (sec_level == BT_SECURITY_LOW) {
++ if (conn->ssp_mode > 0 && conn->hdev->ssp_mode > 0)
++ return hci_conn_auth(conn, sec_level, auth_type);
++ else
++ return 1;
++ }
++
+ if (conn->link_mode & HCI_LM_ENCRYPT)
+- return hci_conn_auth(conn);
++ return hci_conn_auth(conn, sec_level, auth_type);
+
+ if (test_and_set_bit(HCI_CONN_ENCRYPT_PEND, &conn->pend))
+ return 0;
+
+- if (hci_conn_auth(conn)) {
++ if (hci_conn_auth(conn, sec_level, auth_type)) {
+ struct hci_cp_set_conn_encrypt cp;
+ cp.handle = cpu_to_le16(conn->handle);
+ cp.encrypt = 1;
+ hci_send_cmd(conn->hdev, HCI_OP_SET_CONN_ENCRYPT,
+ sizeof(cp), &cp);
+ }
++
+ return 0;
+ }
+-EXPORT_SYMBOL(hci_conn_encrypt);
++EXPORT_SYMBOL(hci_conn_security);
+
+ /* Change link key */
+ int hci_conn_change_link_key(struct hci_conn *conn)
+@@ -442,12 +460,13 @@ int hci_conn_change_link_key(struct hci_conn *conn)
+ hci_send_cmd(conn->hdev, HCI_OP_CHANGE_CONN_LINK_KEY,
+ sizeof(cp), &cp);
+ }
++
+ return 0;
+ }
+ EXPORT_SYMBOL(hci_conn_change_link_key);
+
+ /* Switch role */
+-int hci_conn_switch_role(struct hci_conn *conn, uint8_t role)
++int hci_conn_switch_role(struct hci_conn *conn, __u8 role)
+ {
+ BT_DBG("conn %p", conn);
+
+@@ -460,6 +479,7 @@ int hci_conn_switch_role(struct hci_conn *conn, uint8_t role)
+ cp.role = role;
+ hci_send_cmd(conn->hdev, HCI_OP_SWITCH_ROLE, sizeof(cp), &cp);
+ }
++
+ return 0;
+ }
+ EXPORT_SYMBOL(hci_conn_switch_role);
+@@ -542,9 +562,7 @@ void hci_conn_hash_flush(struct hci_dev *hdev)
+
+ c->state = BT_CLOSED;
+
+- hci_conn_del_sysfs(c);
+-
+- hci_proto_disconn_ind(c, 0x16);
++ hci_proto_disconn_cfm(c, 0x16);
+ hci_conn_del(c);
+ }
+ }
+diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
+index ba78cc1..cd06151 100644
+--- a/net/bluetooth/hci_core.c
++++ b/net/bluetooth/hci_core.c
+@@ -1565,8 +1565,7 @@ static void hci_cmd_task(unsigned long arg)
+
+ /* Send queued commands */
+ if (atomic_read(&hdev->cmd_cnt) && (skb = skb_dequeue(&hdev->cmd_q))) {
+- if (hdev->sent_cmd)
+- kfree_skb(hdev->sent_cmd);
++ kfree_skb(hdev->sent_cmd);
+
+ if ((hdev->sent_cmd = skb_clone(skb, GFP_ATOMIC))) {
+ atomic_dec(&hdev->cmd_cnt);
+diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
+index f91ba69..5553424 100644
+--- a/net/bluetooth/hci_event.c
++++ b/net/bluetooth/hci_event.c
+@@ -484,6 +484,15 @@ static void hci_cc_read_local_features(struct hci_dev *hdev, struct sk_buff *skb
+ if (hdev->features[4] & LMP_EV5)
+ hdev->esco_type |= (ESCO_EV5);
+
++ if (hdev->features[5] & LMP_EDR_ESCO_2M)
++ hdev->esco_type |= (ESCO_2EV3);
++
++ if (hdev->features[5] & LMP_EDR_ESCO_3M)
++ hdev->esco_type |= (ESCO_3EV3);
++
++ if (hdev->features[5] & LMP_EDR_3S_ESCO)
++ hdev->esco_type |= (ESCO_2EV5 | ESCO_3EV5);
++
+ BT_DBG("%s features 0x%.2x%.2x%.2x%.2x%.2x%.2x%.2x%.2x", hdev->name,
+ hdev->features[0], hdev->features[1],
+ hdev->features[2], hdev->features[3],
+@@ -914,7 +923,8 @@ static inline void hci_conn_complete_evt(struct hci_dev *hdev, struct sk_buff *s
+ if (ev->status) {
+ hci_proto_connect_cfm(conn, ev->status);
+ hci_conn_del(conn);
+- }
++ } else if (ev->link_type != ACL_LINK)
++ hci_proto_connect_cfm(conn, ev->status);
+
+ unlock:
+ hci_dev_unlock(hdev);
+@@ -1009,9 +1019,7 @@ static inline void hci_disconn_complete_evt(struct hci_dev *hdev, struct sk_buff
+ if (conn) {
+ conn->state = BT_CLOSED;
+
+- hci_conn_del_sysfs(conn);
+-
+- hci_proto_disconn_ind(conn, ev->reason);
++ hci_proto_disconn_cfm(conn, ev->reason);
+ hci_conn_del(conn);
+ }
+
+@@ -1600,7 +1608,8 @@ static inline void hci_remote_ext_features_evt(struct hci_dev *hdev, struct sk_b
+
+ if (conn->state == BT_CONFIG) {
+ if (!ev->status && hdev->ssp_mode > 0 &&
+- conn->ssp_mode > 0 && conn->out) {
++ conn->ssp_mode > 0 && conn->out &&
++ conn->sec_level != BT_SECURITY_SDP) {
+ struct hci_cp_auth_requested cp;
+ cp.handle = ev->handle;
+ hci_send_cmd(hdev, HCI_OP_AUTH_REQUESTED,
+@@ -1637,6 +1646,13 @@ static inline void hci_sync_conn_complete_evt(struct hci_dev *hdev, struct sk_bu
+ conn->type = SCO_LINK;
+ }
+
++ if (conn->out && ev->status == 0x1c && conn->attempt < 2) {
++ conn->pkt_type = (hdev->esco_type & SCO_ESCO_MASK) |
++ (hdev->esco_type & EDR_ESCO_MASK);
++ hci_setup_sync(conn, conn->link->handle);
++ goto unlock;
++ }
++
+ if (!ev->status) {
+ conn->handle = __le16_to_cpu(ev->handle);
+ conn->state = BT_CONNECTED;
+diff --git a/net/bluetooth/l2cap.c b/net/bluetooth/l2cap.c
+index b93748e..ca4d3b4 100644
+--- a/net/bluetooth/l2cap.c
++++ b/net/bluetooth/l2cap.c
+@@ -50,9 +50,10 @@
+ #include <net/bluetooth/hci_core.h>
+ #include <net/bluetooth/l2cap.h>
+
+-#define VERSION "2.11"
++#define VERSION "2.13"
+
+-static u32 l2cap_feat_mask = 0x0000;
++static u32 l2cap_feat_mask = 0x0080;
++static u8 l2cap_fixed_chan[8] = { 0x02, };
+
+ static const struct proto_ops l2cap_sock_ops;
+
+@@ -77,9 +78,10 @@ static void l2cap_sock_timeout(unsigned long arg)
+
+ bh_lock_sock(sk);
+
+- if (sk->sk_state == BT_CONNECT &&
+- (l2cap_pi(sk)->link_mode & (L2CAP_LM_AUTH |
+- L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)))
++ if (sk->sk_state == BT_CONNECTED || sk->sk_state == BT_CONFIG)
++ reason = ECONNREFUSED;
++ else if (sk->sk_state == BT_CONNECT &&
++ l2cap_pi(sk)->sec_level != BT_SECURITY_SDP)
+ reason = ECONNREFUSED;
+ else
+ reason = ETIMEDOUT;
+@@ -204,6 +206,8 @@ static void __l2cap_chan_add(struct l2cap_conn *conn, struct sock *sk, struct so
+
+ BT_DBG("conn %p, psm 0x%2.2x, dcid 0x%4.4x", conn, l2cap_pi(sk)->psm, l2cap_pi(sk)->dcid);
+
++ conn->disc_reason = 0x13;
++
+ l2cap_pi(sk)->conn = conn;
+
+ if (sk->sk_type == SOCK_SEQPACKET) {
+@@ -259,18 +263,35 @@ static void l2cap_chan_del(struct sock *sk, int err)
+ }
+
+ /* Service level security */
+-static inline int l2cap_check_link_mode(struct sock *sk)
++static inline int l2cap_check_security(struct sock *sk)
+ {
+ struct l2cap_conn *conn = l2cap_pi(sk)->conn;
++ __u8 auth_type;
+
+- if ((l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT) ||
+- (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE))
+- return hci_conn_encrypt(conn->hcon);
++ if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) {
++ if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH)
++ auth_type = HCI_AT_NO_BONDING_MITM;
++ else
++ auth_type = HCI_AT_NO_BONDING;
+
+- if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH)
+- return hci_conn_auth(conn->hcon);
++ if (l2cap_pi(sk)->sec_level == BT_SECURITY_LOW)
++ l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
++ } else {
++ switch (l2cap_pi(sk)->sec_level) {
++ case BT_SECURITY_HIGH:
++ auth_type = HCI_AT_GENERAL_BONDING_MITM;
++ break;
++ case BT_SECURITY_MEDIUM:
++ auth_type = HCI_AT_GENERAL_BONDING;
++ break;
++ default:
++ auth_type = HCI_AT_NO_BONDING;
++ break;
++ }
++ }
+
+- return 1;
++ return hci_conn_security(conn->hcon, l2cap_pi(sk)->sec_level,
++ auth_type);
+ }
+
+ static inline u8 l2cap_get_ident(struct l2cap_conn *conn)
+@@ -312,7 +333,10 @@ static void l2cap_do_start(struct sock *sk)
+ struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+
+ if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
+- if (l2cap_check_link_mode(sk)) {
++ if (!(conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE))
++ return;
++
++ if (l2cap_check_security(sk)) {
+ struct l2cap_conn_req req;
+ req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
+ req.psm = l2cap_pi(sk)->psm;
+@@ -356,7 +380,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
+ }
+
+ if (sk->sk_state == BT_CONNECT) {
+- if (l2cap_check_link_mode(sk)) {
++ if (l2cap_check_security(sk)) {
+ struct l2cap_conn_req req;
+ req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
+ req.psm = l2cap_pi(sk)->psm;
+@@ -371,10 +395,18 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
+ rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
+ rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
+
+- if (l2cap_check_link_mode(sk)) {
+- sk->sk_state = BT_CONFIG;
+- rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
+- rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
++ if (l2cap_check_security(sk)) {
++ if (bt_sk(sk)->defer_setup) {
++ struct sock *parent = bt_sk(sk)->parent;
++ rsp.result = cpu_to_le16(L2CAP_CR_PEND);
++ rsp.status = cpu_to_le16(L2CAP_CS_AUTHOR_PEND);
++ parent->sk_data_ready(parent, 0);
++
++ } else {
++ sk->sk_state = BT_CONFIG;
++ rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
++ rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
++ }
+ } else {
+ rsp.result = cpu_to_le16(L2CAP_CR_PEND);
+ rsp.status = cpu_to_le16(L2CAP_CS_AUTHEN_PEND);
+@@ -426,7 +458,7 @@ static void l2cap_conn_unreliable(struct l2cap_conn *conn, int err)
+ read_lock(&l->lock);
+
+ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+- if (l2cap_pi(sk)->link_mode & L2CAP_LM_RELIABLE)
++ if (l2cap_pi(sk)->force_reliable)
+ sk->sk_err = err;
+ }
+
+@@ -437,6 +469,7 @@ static void l2cap_info_timeout(unsigned long arg)
+ {
+ struct l2cap_conn *conn = (void *) arg;
+
++ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
+ conn->info_ident = 0;
+
+ l2cap_conn_start(conn);
+@@ -470,6 +503,8 @@ static struct l2cap_conn *l2cap_conn_add(struct hci_conn *hcon, u8 status)
+ spin_lock_init(&conn->lock);
+ rwlock_init(&conn->chan_list.lock);
+
++ conn->disc_reason = 0x13;
++
+ return conn;
+ }
+
+@@ -483,8 +518,7 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
+
+ BT_DBG("hcon %p conn %p, err %d", hcon, conn, err);
+
+- if (conn->rx_skb)
+- kfree_skb(conn->rx_skb);
++ kfree_skb(conn->rx_skb);
+
+ /* Kill channels */
+ while ((sk = conn->chan_list.head)) {
+@@ -608,7 +642,6 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
+
+ case BT_CONNECTED:
+ case BT_CONFIG:
+- case BT_CONNECT2:
+ if (sk->sk_type == SOCK_SEQPACKET) {
+ struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+ struct l2cap_disconn_req req;
+@@ -624,6 +657,27 @@ static void __l2cap_sock_close(struct sock *sk, int reason)
+ l2cap_chan_del(sk, reason);
+ break;
+
++ case BT_CONNECT2:
++ if (sk->sk_type == SOCK_SEQPACKET) {
++ struct l2cap_conn *conn = l2cap_pi(sk)->conn;
++ struct l2cap_conn_rsp rsp;
++ __u16 result;
++
++ if (bt_sk(sk)->defer_setup)
++ result = L2CAP_CR_SEC_BLOCK;
++ else
++ result = L2CAP_CR_BAD_PSM;
++
++ rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
++ rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
++ rsp.result = cpu_to_le16(result);
++ rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
++ l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
++ L2CAP_CONN_RSP, sizeof(rsp), &rsp);
++ } else
++ l2cap_chan_del(sk, reason);
++ break;
++
+ case BT_CONNECT:
+ case BT_DISCONN:
+ l2cap_chan_del(sk, reason);
+@@ -653,13 +707,19 @@ static void l2cap_sock_init(struct sock *sk, struct sock *parent)
+
+ if (parent) {
+ sk->sk_type = parent->sk_type;
++ bt_sk(sk)->defer_setup = bt_sk(parent)->defer_setup;
++
+ pi->imtu = l2cap_pi(parent)->imtu;
+ pi->omtu = l2cap_pi(parent)->omtu;
+- pi->link_mode = l2cap_pi(parent)->link_mode;
++ pi->sec_level = l2cap_pi(parent)->sec_level;
++ pi->role_switch = l2cap_pi(parent)->role_switch;
++ pi->force_reliable = l2cap_pi(parent)->force_reliable;
+ } else {
+ pi->imtu = L2CAP_DEFAULT_MTU;
+ pi->omtu = 0;
+- pi->link_mode = 0;
++ pi->sec_level = BT_SECURITY_LOW;
++ pi->role_switch = 0;
++ pi->force_reliable = 0;
+ }
+
+ /* Default config options */
+@@ -723,17 +783,24 @@ static int l2cap_sock_create(struct net *net, struct socket *sock, int protocol)
+ return 0;
+ }
+
+-static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len)
++static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
+ {
+- struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
+ struct sock *sk = sock->sk;
+- int err = 0;
++ struct sockaddr_l2 la;
++ int len, err = 0;
+
+- BT_DBG("sk %p, %s %d", sk, batostr(&la->l2_bdaddr), la->l2_psm);
++ BT_DBG("sk %p", sk);
+
+ if (!addr || addr->sa_family != AF_BLUETOOTH)
+ return -EINVAL;
+
++ memset(&la, 0, sizeof(la));
++ len = min_t(unsigned int, sizeof(la), alen);
++ memcpy(&la, addr, len);
++
++ if (la.l2_cid)
++ return -EINVAL;
++
+ lock_sock(sk);
+
+ if (sk->sk_state != BT_OPEN) {
+@@ -741,7 +808,7 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_
+ goto done;
+ }
+
+- if (la->l2_psm && btohs(la->l2_psm) < 0x1001 &&
++ if (la.l2_psm && btohs(la.l2_psm) < 0x1001 &&
+ !capable(CAP_NET_BIND_SERVICE)) {
+ err = -EACCES;
+ goto done;
+@@ -749,14 +816,17 @@ static int l2cap_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_
+
+ write_lock_bh(&l2cap_sk_list.lock);
+
+- if (la->l2_psm && __l2cap_get_sock_by_addr(la->l2_psm, &la->l2_bdaddr)) {
++ if (la.l2_psm && __l2cap_get_sock_by_addr(la.l2_psm, &la.l2_bdaddr)) {
+ err = -EADDRINUSE;
+ } else {
+ /* Save source address */
+- bacpy(&bt_sk(sk)->src, &la->l2_bdaddr);
+- l2cap_pi(sk)->psm = la->l2_psm;
+- l2cap_pi(sk)->sport = la->l2_psm;
++ bacpy(&bt_sk(sk)->src, &la.l2_bdaddr);
++ l2cap_pi(sk)->psm = la.l2_psm;
++ l2cap_pi(sk)->sport = la.l2_psm;
+ sk->sk_state = BT_BOUND;
++
++ if (btohs(la.l2_psm) == 0x0001 || btohs(la.l2_psm) == 0x0003)
++ l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
+ }
+
+ write_unlock_bh(&l2cap_sk_list.lock);
+@@ -776,7 +846,8 @@ static int l2cap_do_connect(struct sock *sk)
+ __u8 auth_type;
+ int err = 0;
+
+- BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst), l2cap_pi(sk)->psm);
++ BT_DBG("%s -> %s psm 0x%2.2x", batostr(src), batostr(dst),
++ l2cap_pi(sk)->psm);
+
+ if (!(hdev = hci_get_route(dst, src)))
+ return -EHOSTUNREACH;
+@@ -785,21 +856,42 @@ static int l2cap_do_connect(struct sock *sk)
+
+ err = -ENOMEM;
+
+- if (l2cap_pi(sk)->link_mode & L2CAP_LM_AUTH ||
+- l2cap_pi(sk)->link_mode & L2CAP_LM_ENCRYPT ||
+- l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) {
+- if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001))
++ if (sk->sk_type == SOCK_RAW) {
++ switch (l2cap_pi(sk)->sec_level) {
++ case BT_SECURITY_HIGH:
++ auth_type = HCI_AT_DEDICATED_BONDING_MITM;
++ break;
++ case BT_SECURITY_MEDIUM:
++ auth_type = HCI_AT_DEDICATED_BONDING;
++ break;
++ default:
++ auth_type = HCI_AT_NO_BONDING;
++ break;
++ }
++ } else if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001)) {
++ if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH)
+ auth_type = HCI_AT_NO_BONDING_MITM;
+ else
+- auth_type = HCI_AT_GENERAL_BONDING_MITM;
+- } else {
+- if (l2cap_pi(sk)->psm == cpu_to_le16(0x0001))
+ auth_type = HCI_AT_NO_BONDING;
+- else
++
++ if (l2cap_pi(sk)->sec_level == BT_SECURITY_LOW)
++ l2cap_pi(sk)->sec_level = BT_SECURITY_SDP;
++ } else {
++ switch (l2cap_pi(sk)->sec_level) {
++ case BT_SECURITY_HIGH:
++ auth_type = HCI_AT_GENERAL_BONDING_MITM;
++ break;
++ case BT_SECURITY_MEDIUM:
+ auth_type = HCI_AT_GENERAL_BONDING;
++ break;
++ default:
++ auth_type = HCI_AT_NO_BONDING;
++ break;
++ }
+ }
+
+- hcon = hci_connect(hdev, ACL_LINK, dst, auth_type);
++ hcon = hci_connect(hdev, ACL_LINK, dst,
++ l2cap_pi(sk)->sec_level, auth_type);
+ if (!hcon)
+ goto done;
+
+@@ -835,20 +927,25 @@ done:
+
+ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int alen, int flags)
+ {
+- struct sockaddr_l2 *la = (struct sockaddr_l2 *) addr;
+ struct sock *sk = sock->sk;
+- int err = 0;
+-
+- lock_sock(sk);
++ struct sockaddr_l2 la;
++ int len, err = 0;
+
+ BT_DBG("sk %p", sk);
+
+- if (addr->sa_family != AF_BLUETOOTH || alen < sizeof(struct sockaddr_l2)) {
+- err = -EINVAL;
+- goto done;
+- }
++ if (!addr || addr->sa_family != AF_BLUETOOTH)
++ return -EINVAL;
++
++ memset(&la, 0, sizeof(la));
++ len = min_t(unsigned int, sizeof(la), alen);
++ memcpy(&la, addr, len);
++
++ if (la.l2_cid)
++ return -EINVAL;
++
++ lock_sock(sk);
+
+- if (sk->sk_type == SOCK_SEQPACKET && !la->l2_psm) {
++ if (sk->sk_type == SOCK_SEQPACKET && !la.l2_psm) {
+ err = -EINVAL;
+ goto done;
+ }
+@@ -875,8 +972,8 @@ static int l2cap_sock_connect(struct socket *sock, struct sockaddr *addr, int al
+ }
+
+ /* Set destination address and psm */
+- bacpy(&bt_sk(sk)->dst, &la->l2_bdaddr);
+- l2cap_pi(sk)->psm = la->l2_psm;
++ bacpy(&bt_sk(sk)->dst, &la.l2_bdaddr);
++ l2cap_pi(sk)->psm = la.l2_psm;
+
+ if ((err = l2cap_do_connect(sk)))
+ goto done;
+@@ -1000,12 +1097,16 @@ static int l2cap_sock_getname(struct socket *sock, struct sockaddr *addr, int *l
+ addr->sa_family = AF_BLUETOOTH;
+ *len = sizeof(struct sockaddr_l2);
+
+- if (peer)
++ if (peer) {
++ la->l2_psm = l2cap_pi(sk)->psm;
+ bacpy(&la->l2_bdaddr, &bt_sk(sk)->dst);
+- else
++ la->l2_cid = htobs(l2cap_pi(sk)->dcid);
++ } else {
++ la->l2_psm = l2cap_pi(sk)->sport;
+ bacpy(&la->l2_bdaddr, &bt_sk(sk)->src);
++ la->l2_cid = htobs(l2cap_pi(sk)->scid);
++ }
+
+- la->l2_psm = l2cap_pi(sk)->psm;
+ return 0;
+ }
+
+@@ -1106,11 +1207,38 @@ static int l2cap_sock_sendmsg(struct kiocb *iocb, struct socket *sock, struct ms
+ return err;
+ }
+
+-static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
++static int l2cap_sock_recvmsg(struct kiocb *iocb, struct socket *sock, struct msghdr *msg, size_t len, int flags)
++{
++ struct sock *sk = sock->sk;
++
++ lock_sock(sk);
++
++ if (sk->sk_state == BT_CONNECT2 && bt_sk(sk)->defer_setup) {
++ struct l2cap_conn_rsp rsp;
++
++ sk->sk_state = BT_CONFIG;
++
++ rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
++ rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
++ rsp.result = cpu_to_le16(L2CAP_CR_SUCCESS);
++ rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
++ l2cap_send_cmd(l2cap_pi(sk)->conn, l2cap_pi(sk)->ident,
++ L2CAP_CONN_RSP, sizeof(rsp), &rsp);
++
++ release_sock(sk);
++ return 0;
++ }
++
++ release_sock(sk);
++
++ return bt_sock_recvmsg(iocb, sock, msg, len, flags);
++}
++
++static int l2cap_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, int optlen)
+ {
+ struct sock *sk = sock->sk;
+ struct l2cap_options opts;
+- int err = 0, len;
++ int len, err = 0;
+ u32 opt;
+
+ BT_DBG("sk %p", sk);
+@@ -1140,7 +1268,15 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
+ break;
+ }
+
+- l2cap_pi(sk)->link_mode = opt;
++ if (opt & L2CAP_LM_AUTH)
++ l2cap_pi(sk)->sec_level = BT_SECURITY_LOW;
++ if (opt & L2CAP_LM_ENCRYPT)
++ l2cap_pi(sk)->sec_level = BT_SECURITY_MEDIUM;
++ if (opt & L2CAP_LM_SECURE)
++ l2cap_pi(sk)->sec_level = BT_SECURITY_HIGH;
++
++ l2cap_pi(sk)->role_switch = (opt & L2CAP_LM_MASTER);
++ l2cap_pi(sk)->force_reliable = (opt & L2CAP_LM_RELIABLE);
+ break;
+
+ default:
+@@ -1152,12 +1288,77 @@ static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, ch
+ return err;
+ }
+
+-static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
++static int l2cap_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
++{
++ struct sock *sk = sock->sk;
++ struct bt_security sec;
++ int len, err = 0;
++ u32 opt;
++
++ BT_DBG("sk %p", sk);
++
++ if (level == SOL_L2CAP)
++ return l2cap_sock_setsockopt_old(sock, optname, optval, optlen);
++
++ if (level != SOL_BLUETOOTH)
++ return -ENOPROTOOPT;
++
++ lock_sock(sk);
++
++ switch (optname) {
++ case BT_SECURITY:
++ if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_RAW) {
++ err = -EINVAL;
++ break;
++ }
++
++ sec.level = BT_SECURITY_LOW;
++
++ len = min_t(unsigned int, sizeof(sec), optlen);
++ if (copy_from_user((char *) &sec, optval, len)) {
++ err = -EFAULT;
++ break;
++ }
++
++ if (sec.level < BT_SECURITY_LOW ||
++ sec.level > BT_SECURITY_HIGH) {
++ err = -EINVAL;
++ break;
++ }
++
++ l2cap_pi(sk)->sec_level = sec.level;
++ break;
++
++ case BT_DEFER_SETUP:
++ if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
++ err = -EINVAL;
++ break;
++ }
++
++ if (get_user(opt, (u32 __user *) optval)) {
++ err = -EFAULT;
++ break;
++ }
++
++ bt_sk(sk)->defer_setup = opt;
++ break;
++
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ }
++
++ release_sock(sk);
++ return err;
++}
++
++static int l2cap_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
+ {
+ struct sock *sk = sock->sk;
+ struct l2cap_options opts;
+ struct l2cap_conninfo cinfo;
+ int len, err = 0;
++ u32 opt;
+
+ BT_DBG("sk %p", sk);
+
+@@ -1180,12 +1381,36 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
+ break;
+
+ case L2CAP_LM:
+- if (put_user(l2cap_pi(sk)->link_mode, (u32 __user *) optval))
++ switch (l2cap_pi(sk)->sec_level) {
++ case BT_SECURITY_LOW:
++ opt = L2CAP_LM_AUTH;
++ break;
++ case BT_SECURITY_MEDIUM:
++ opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT;
++ break;
++ case BT_SECURITY_HIGH:
++ opt = L2CAP_LM_AUTH | L2CAP_LM_ENCRYPT |
++ L2CAP_LM_SECURE;
++ break;
++ default:
++ opt = 0;
++ break;
++ }
++
++ if (l2cap_pi(sk)->role_switch)
++ opt |= L2CAP_LM_MASTER;
++
++ if (l2cap_pi(sk)->force_reliable)
++ opt |= L2CAP_LM_RELIABLE;
++
++ if (put_user(opt, (u32 __user *) optval))
+ err = -EFAULT;
+ break;
+
+ case L2CAP_CONNINFO:
+- if (sk->sk_state != BT_CONNECTED) {
++ if (sk->sk_state != BT_CONNECTED &&
++ !(sk->sk_state == BT_CONNECT2 &&
++ bt_sk(sk)->defer_setup)) {
+ err = -ENOTCONN;
+ break;
+ }
+@@ -1208,6 +1433,60 @@ static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, ch
+ return err;
+ }
+
++static int l2cap_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
++{
++ struct sock *sk = sock->sk;
++ struct bt_security sec;
++ int len, err = 0;
++
++ BT_DBG("sk %p", sk);
++
++ if (level == SOL_L2CAP)
++ return l2cap_sock_getsockopt_old(sock, optname, optval, optlen);
++
++ if (level != SOL_BLUETOOTH)
++ return -ENOPROTOOPT;
++
++ if (get_user(len, optlen))
++ return -EFAULT;
++
++ lock_sock(sk);
++
++ switch (optname) {
++ case BT_SECURITY:
++ if (sk->sk_type != SOCK_SEQPACKET && sk->sk_type != SOCK_RAW) {
++ err = -EINVAL;
++ break;
++ }
++
++ sec.level = l2cap_pi(sk)->sec_level;
++
++ len = min_t(unsigned int, len, sizeof(sec));
++ if (copy_to_user(optval, (char *) &sec, len))
++ err = -EFAULT;
++
++ break;
++
++ case BT_DEFER_SETUP:
++ if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
++ err = -EINVAL;
++ break;
++ }
++
++ if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval))
++ err = -EFAULT;
++
++ break;
++
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ }
++
++ release_sock(sk);
++ return err;
++}
++
+ static int l2cap_sock_shutdown(struct socket *sock, int how)
+ {
+ struct sock *sk = sock->sk;
+@@ -1270,11 +1549,6 @@ static void l2cap_chan_ready(struct sock *sk)
+ */
+ parent->sk_data_ready(parent, 0);
+ }
+-
+- if (l2cap_pi(sk)->link_mode & L2CAP_LM_SECURE) {
+- struct l2cap_conn *conn = l2cap_pi(sk)->conn;
+- hci_conn_change_link_key(conn->hcon);
+- }
+ }
+
+ /* Copy frame to all raw sockets on that connection */
+@@ -1549,8 +1823,11 @@ static inline int l2cap_command_rej(struct l2cap_conn *conn, struct l2cap_cmd_hd
+
+ if ((conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) &&
+ cmd->ident == conn->info_ident) {
+- conn->info_ident = 0;
+ del_timer(&conn->info_timer);
++
++ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
++ conn->info_ident = 0;
++
+ l2cap_conn_start(conn);
+ }
+
+@@ -1580,6 +1857,7 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
+ /* Check if the ACL is secure enough (if not SDP) */
+ if (psm != cpu_to_le16(0x0001) &&
+ !hci_conn_check_link_mode(conn->hcon)) {
++ conn->disc_reason = 0x05;
+ result = L2CAP_CR_SEC_BLOCK;
+ goto response;
+ }
+@@ -1621,11 +1899,18 @@ static inline int l2cap_connect_req(struct l2cap_conn *conn, struct l2cap_cmd_hd
+
+ l2cap_pi(sk)->ident = cmd->ident;
+
+- if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_SENT) {
+- if (l2cap_check_link_mode(sk)) {
+- sk->sk_state = BT_CONFIG;
+- result = L2CAP_CR_SUCCESS;
+- status = L2CAP_CS_NO_INFO;
++ if (conn->info_state & L2CAP_INFO_FEAT_MASK_REQ_DONE) {
++ if (l2cap_check_security(sk)) {
++ if (bt_sk(sk)->defer_setup) {
++ sk->sk_state = BT_CONNECT2;
++ result = L2CAP_CR_PEND;
++ status = L2CAP_CS_AUTHOR_PEND;
++ parent->sk_data_ready(parent, 0);
++ } else {
++ sk->sk_state = BT_CONFIG;
++ result = L2CAP_CR_SUCCESS;
++ status = L2CAP_CS_NO_INFO;
++ }
+ } else {
+ sk->sk_state = BT_CONNECT2;
+ result = L2CAP_CR_PEND;
+@@ -1695,11 +1980,14 @@ static inline int l2cap_connect_rsp(struct l2cap_conn *conn, struct l2cap_cmd_hd
+ l2cap_pi(sk)->dcid = dcid;
+ l2cap_pi(sk)->conf_state |= L2CAP_CONF_REQ_SENT;
+
++ l2cap_pi(sk)->conf_state &= ~L2CAP_CONF_CONNECT_PEND;
++
+ l2cap_send_cmd(conn, l2cap_get_ident(conn), L2CAP_CONF_REQ,
+ l2cap_build_conf_req(sk, req), req);
+ break;
+
+ case L2CAP_CR_PEND:
++ l2cap_pi(sk)->conf_state |= L2CAP_CONF_CONNECT_PEND;
+ break;
+
+ default:
+@@ -1908,6 +2196,14 @@ static inline int l2cap_information_req(struct l2cap_conn *conn, struct l2cap_cm
+ put_unaligned(cpu_to_le32(l2cap_feat_mask), (__le32 *) rsp->data);
+ l2cap_send_cmd(conn, cmd->ident,
+ L2CAP_INFO_RSP, sizeof(buf), buf);
++ } else if (type == L2CAP_IT_FIXED_CHAN) {
++ u8 buf[12];
++ struct l2cap_info_rsp *rsp = (struct l2cap_info_rsp *) buf;
++ rsp->type = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
++ rsp->result = cpu_to_le16(L2CAP_IR_SUCCESS);
++ memcpy(buf + 4, l2cap_fixed_chan, 8);
++ l2cap_send_cmd(conn, cmd->ident,
++ L2CAP_INFO_RSP, sizeof(buf), buf);
+ } else {
+ struct l2cap_info_rsp rsp;
+ rsp.type = cpu_to_le16(type);
+@@ -1929,14 +2225,31 @@ static inline int l2cap_information_rsp(struct l2cap_conn *conn, struct l2cap_cm
+
+ BT_DBG("type 0x%4.4x result 0x%2.2x", type, result);
+
+- conn->info_ident = 0;
+-
+ del_timer(&conn->info_timer);
+
+- if (type == L2CAP_IT_FEAT_MASK)
++ if (type == L2CAP_IT_FEAT_MASK) {
+ conn->feat_mask = get_unaligned_le32(rsp->data);
+
+- l2cap_conn_start(conn);
++ if (conn->feat_mask & 0x0080) {
++ struct l2cap_info_req req;
++ req.type = cpu_to_le16(L2CAP_IT_FIXED_CHAN);
++
++ conn->info_ident = l2cap_get_ident(conn);
++
++ l2cap_send_cmd(conn, conn->info_ident,
++ L2CAP_INFO_REQ, sizeof(req), &req);
++ } else {
++ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
++ conn->info_ident = 0;
++
++ l2cap_conn_start(conn);
++ }
++ } else if (type == L2CAP_IT_FIXED_CHAN) {
++ conn->info_state |= L2CAP_INFO_FEAT_MASK_REQ_DONE;
++ conn->info_ident = 0;
++
++ l2cap_conn_start(conn);
++ }
+
+ return 0;
+ }
+@@ -2143,10 +2456,15 @@ static int l2cap_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 type)
+ continue;
+
+ if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr)) {
+- lm1 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode);
++ lm1 |= HCI_LM_ACCEPT;
++ if (l2cap_pi(sk)->role_switch)
++ lm1 |= HCI_LM_MASTER;
+ exact++;
+- } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY))
+- lm2 |= (HCI_LM_ACCEPT | l2cap_pi(sk)->link_mode);
++ } else if (!bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
++ lm2 |= HCI_LM_ACCEPT;
++ if (l2cap_pi(sk)->role_switch)
++ lm2 |= HCI_LM_MASTER;
++ }
+ }
+ read_unlock(&l2cap_sk_list.lock);
+
+@@ -2172,89 +2490,48 @@ static int l2cap_connect_cfm(struct hci_conn *hcon, u8 status)
+ return 0;
+ }
+
+-static int l2cap_disconn_ind(struct hci_conn *hcon, u8 reason)
++static int l2cap_disconn_ind(struct hci_conn *hcon)
+ {
+- BT_DBG("hcon %p reason %d", hcon, reason);
++ struct l2cap_conn *conn = hcon->l2cap_data;
+
+- if (hcon->type != ACL_LINK)
+- return 0;
++ BT_DBG("hcon %p", hcon);
+
+- l2cap_conn_del(hcon, bt_err(reason));
++ if (hcon->type != ACL_LINK || !conn)
++ return 0x13;
+
+- return 0;
++ return conn->disc_reason;
+ }
+
+-static int l2cap_auth_cfm(struct hci_conn *hcon, u8 status)
++static int l2cap_disconn_cfm(struct hci_conn *hcon, u8 reason)
+ {
+- struct l2cap_chan_list *l;
+- struct l2cap_conn *conn = hcon->l2cap_data;
+- struct sock *sk;
++ BT_DBG("hcon %p reason %d", hcon, reason);
+
+- if (!conn)
++ if (hcon->type != ACL_LINK)
+ return 0;
+
+- l = &conn->chan_list;
+-
+- BT_DBG("conn %p", conn);
+-
+- read_lock(&l->lock);
+-
+- for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+- struct l2cap_pinfo *pi = l2cap_pi(sk);
+-
+- bh_lock_sock(sk);
+-
+- if ((pi->link_mode & (L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)) &&
+- !(hcon->link_mode & HCI_LM_ENCRYPT) &&
+- !status) {
+- bh_unlock_sock(sk);
+- continue;
+- }
+-
+- if (sk->sk_state == BT_CONNECT) {
+- if (!status) {
+- struct l2cap_conn_req req;
+- req.scid = cpu_to_le16(l2cap_pi(sk)->scid);
+- req.psm = l2cap_pi(sk)->psm;
+-
+- l2cap_pi(sk)->ident = l2cap_get_ident(conn);
+-
+- l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
+- L2CAP_CONN_REQ, sizeof(req), &req);
+- } else {
+- l2cap_sock_clear_timer(sk);
+- l2cap_sock_set_timer(sk, HZ / 10);
+- }
+- } else if (sk->sk_state == BT_CONNECT2) {
+- struct l2cap_conn_rsp rsp;
+- __u16 result;
++ l2cap_conn_del(hcon, bt_err(reason));
+
+- if (!status) {
+- sk->sk_state = BT_CONFIG;
+- result = L2CAP_CR_SUCCESS;
+- } else {
+- sk->sk_state = BT_DISCONN;
+- l2cap_sock_set_timer(sk, HZ / 10);
+- result = L2CAP_CR_SEC_BLOCK;
+- }
++ return 0;
++}
+
+- rsp.scid = cpu_to_le16(l2cap_pi(sk)->dcid);
+- rsp.dcid = cpu_to_le16(l2cap_pi(sk)->scid);
+- rsp.result = cpu_to_le16(result);
+- rsp.status = cpu_to_le16(L2CAP_CS_NO_INFO);
+- l2cap_send_cmd(conn, l2cap_pi(sk)->ident,
+- L2CAP_CONN_RSP, sizeof(rsp), &rsp);
+- }
++static inline void l2cap_check_encryption(struct sock *sk, u8 encrypt)
++{
++ if (sk->sk_type != SOCK_SEQPACKET)
++ return;
+
+- bh_unlock_sock(sk);
++ if (encrypt == 0x00) {
++ if (l2cap_pi(sk)->sec_level == BT_SECURITY_MEDIUM) {
++ l2cap_sock_clear_timer(sk);
++ l2cap_sock_set_timer(sk, HZ * 5);
++ } else if (l2cap_pi(sk)->sec_level == BT_SECURITY_HIGH)
++ __l2cap_sock_close(sk, ECONNREFUSED);
++ } else {
++ if (l2cap_pi(sk)->sec_level == BT_SECURITY_MEDIUM)
++ l2cap_sock_clear_timer(sk);
+ }
+-
+- read_unlock(&l->lock);
+-
+- return 0;
+ }
+
+-static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
++static int l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
+ {
+ struct l2cap_chan_list *l;
+ struct l2cap_conn *conn = hcon->l2cap_data;
+@@ -2270,15 +2547,16 @@ static int l2cap_encrypt_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
+ read_lock(&l->lock);
+
+ for (sk = l->head; sk; sk = l2cap_pi(sk)->next_c) {
+- struct l2cap_pinfo *pi = l2cap_pi(sk);
+-
+ bh_lock_sock(sk);
+
+- if ((pi->link_mode & (L2CAP_LM_ENCRYPT | L2CAP_LM_SECURE)) &&
+- (sk->sk_state == BT_CONNECTED ||
+- sk->sk_state == BT_CONFIG) &&
+- !status && encrypt == 0x00) {
+- __l2cap_sock_close(sk, ECONNREFUSED);
++ if (l2cap_pi(sk)->conf_state & L2CAP_CONF_CONNECT_PEND) {
++ bh_unlock_sock(sk);
++ continue;
++ }
++
++ if (!status && (sk->sk_state == BT_CONNECTED ||
++ sk->sk_state == BT_CONFIG)) {
++ l2cap_check_encryption(sk, encrypt);
+ bh_unlock_sock(sk);
+ continue;
+ }
+@@ -2376,7 +2654,7 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl
+ goto drop;
+
+ skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
+- skb->len);
++ skb->len);
+ conn->rx_len = len - skb->len;
+ } else {
+ BT_DBG("Cont: frag len %d (expecting %d)", skb->len, conn->rx_len);
+@@ -2398,7 +2676,7 @@ static int l2cap_recv_acldata(struct hci_conn *hcon, struct sk_buff *skb, u16 fl
+ }
+
+ skb_copy_from_linear_data(skb, skb_put(conn->rx_skb, skb->len),
+- skb->len);
++ skb->len);
+ conn->rx_len -= skb->len;
+
+ if (!conn->rx_len) {
+@@ -2424,10 +2702,10 @@ static ssize_t l2cap_sysfs_show(struct class *dev, char *buf)
+ sk_for_each(sk, node, &l2cap_sk_list.head) {
+ struct l2cap_pinfo *pi = l2cap_pi(sk);
+
+- str += sprintf(str, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d 0x%x\n",
++ str += sprintf(str, "%s %s %d %d 0x%4.4x 0x%4.4x %d %d %d\n",
+ batostr(&bt_sk(sk)->src), batostr(&bt_sk(sk)->dst),
+ sk->sk_state, btohs(pi->psm), pi->scid, pi->dcid,
+- pi->imtu, pi->omtu, pi->link_mode);
++ pi->imtu, pi->omtu, pi->sec_level);
+ }
+
+ read_unlock_bh(&l2cap_sk_list.lock);
+@@ -2447,7 +2725,7 @@ static const struct proto_ops l2cap_sock_ops = {
+ .accept = l2cap_sock_accept,
+ .getname = l2cap_sock_getname,
+ .sendmsg = l2cap_sock_sendmsg,
+- .recvmsg = bt_sock_recvmsg,
++ .recvmsg = l2cap_sock_recvmsg,
+ .poll = bt_sock_poll,
+ .ioctl = bt_sock_ioctl,
+ .mmap = sock_no_mmap,
+@@ -2469,8 +2747,8 @@ static struct hci_proto l2cap_hci_proto = {
+ .connect_ind = l2cap_connect_ind,
+ .connect_cfm = l2cap_connect_cfm,
+ .disconn_ind = l2cap_disconn_ind,
+- .auth_cfm = l2cap_auth_cfm,
+- .encrypt_cfm = l2cap_encrypt_cfm,
++ .disconn_cfm = l2cap_disconn_cfm,
++ .security_cfm = l2cap_security_cfm,
+ .recv_acldata = l2cap_recv_acldata
+ };
+
+diff --git a/net/bluetooth/rfcomm/core.c b/net/bluetooth/rfcomm/core.c
+index acd84fd..1d0fb0f 100644
+--- a/net/bluetooth/rfcomm/core.c
++++ b/net/bluetooth/rfcomm/core.c
+@@ -46,7 +46,7 @@
+ #include <net/bluetooth/l2cap.h>
+ #include <net/bluetooth/rfcomm.h>
+
+-#define VERSION "1.10"
++#define VERSION "1.11"
+
+ static int disable_cfc = 0;
+ static int channel_mtu = -1;
+@@ -223,19 +223,25 @@ static int rfcomm_l2sock_create(struct socket **sock)
+ return err;
+ }
+
+-static inline int rfcomm_check_link_mode(struct rfcomm_dlc *d)
++static inline int rfcomm_check_security(struct rfcomm_dlc *d)
+ {
+ struct sock *sk = d->session->sock->sk;
++ __u8 auth_type;
+
+- if (d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) {
+- if (!hci_conn_encrypt(l2cap_pi(sk)->conn->hcon))
+- return 1;
+- } else if (d->link_mode & RFCOMM_LM_AUTH) {
+- if (!hci_conn_auth(l2cap_pi(sk)->conn->hcon))
+- return 1;
++ switch (d->sec_level) {
++ case BT_SECURITY_HIGH:
++ auth_type = HCI_AT_GENERAL_BONDING_MITM;
++ break;
++ case BT_SECURITY_MEDIUM:
++ auth_type = HCI_AT_GENERAL_BONDING;
++ break;
++ default:
++ auth_type = HCI_AT_NO_BONDING;
++ break;
+ }
+
+- return 0;
++ return hci_conn_security(l2cap_pi(sk)->conn->hcon, d->sec_level,
++ auth_type);
+ }
+
+ /* ---- RFCOMM DLCs ---- */
+@@ -388,10 +394,10 @@ static int __rfcomm_dlc_open(struct rfcomm_dlc *d, bdaddr_t *src, bdaddr_t *dst,
+ d->cfc = (s->cfc == RFCOMM_CFC_UNKNOWN) ? 0 : s->cfc;
+
+ if (s->state == BT_CONNECTED) {
+- if (rfcomm_check_link_mode(d))
+- set_bit(RFCOMM_AUTH_PENDING, &d->flags);
+- else
++ if (rfcomm_check_security(d))
+ rfcomm_send_pn(s, 1, d);
++ else
++ set_bit(RFCOMM_AUTH_PENDING, &d->flags);
+ }
+
+ rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT);
+@@ -421,9 +427,16 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
+ d, d->state, d->dlci, err, s);
+
+ switch (d->state) {
+- case BT_CONNECTED:
+- case BT_CONFIG:
+ case BT_CONNECT:
++ case BT_CONFIG:
++ if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
++ set_bit(RFCOMM_AUTH_REJECT, &d->flags);
++ rfcomm_schedule(RFCOMM_SCHED_AUTH);
++ break;
++ }
++ /* Fall through */
++
++ case BT_CONNECTED:
+ d->state = BT_DISCONN;
+ if (skb_queue_empty(&d->tx_queue)) {
+ rfcomm_send_disc(s, d->dlci);
+@@ -434,6 +447,15 @@ static int __rfcomm_dlc_close(struct rfcomm_dlc *d, int err)
+ }
+ break;
+
++ case BT_OPEN:
++ case BT_CONNECT2:
++ if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
++ set_bit(RFCOMM_AUTH_REJECT, &d->flags);
++ rfcomm_schedule(RFCOMM_SCHED_AUTH);
++ break;
++ }
++ /* Fall through */
++
+ default:
+ rfcomm_dlc_clear_timer(d);
+
+@@ -636,6 +658,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst
+ bacpy(&addr.l2_bdaddr, src);
+ addr.l2_family = AF_BLUETOOTH;
+ addr.l2_psm = 0;
++ addr.l2_cid = 0;
+ *err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
+ if (*err < 0)
+ goto failed;
+@@ -657,6 +680,7 @@ static struct rfcomm_session *rfcomm_session_create(bdaddr_t *src, bdaddr_t *dst
+ bacpy(&addr.l2_bdaddr, dst);
+ addr.l2_family = AF_BLUETOOTH;
+ addr.l2_psm = htobs(RFCOMM_PSM);
++ addr.l2_cid = 0;
+ *err = kernel_connect(sock, (struct sockaddr *) &addr, sizeof(addr), O_NONBLOCK);
+ if (*err == 0 || *err == -EINPROGRESS)
+ return s;
+@@ -1162,7 +1186,7 @@ static int rfcomm_recv_disc(struct rfcomm_session *s, u8 dlci)
+ return 0;
+ }
+
+-static void rfcomm_dlc_accept(struct rfcomm_dlc *d)
++void rfcomm_dlc_accept(struct rfcomm_dlc *d)
+ {
+ struct sock *sk = d->session->sock->sk;
+
+@@ -1175,12 +1199,31 @@ static void rfcomm_dlc_accept(struct rfcomm_dlc *d)
+ d->state_change(d, 0);
+ rfcomm_dlc_unlock(d);
+
+- if (d->link_mode & RFCOMM_LM_MASTER)
++ if (d->role_switch)
+ hci_conn_switch_role(l2cap_pi(sk)->conn->hcon, 0x00);
+
+ rfcomm_send_msc(d->session, 1, d->dlci, d->v24_sig);
+ }
+
++static void rfcomm_check_accept(struct rfcomm_dlc *d)
++{
++ if (rfcomm_check_security(d)) {
++ if (d->defer_setup) {
++ set_bit(RFCOMM_DEFER_SETUP, &d->flags);
++ rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
++
++ rfcomm_dlc_lock(d);
++ d->state = BT_CONNECT2;
++ d->state_change(d, 0);
++ rfcomm_dlc_unlock(d);
++ } else
++ rfcomm_dlc_accept(d);
++ } else {
++ set_bit(RFCOMM_AUTH_PENDING, &d->flags);
++ rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
++ }
++}
++
+ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
+ {
+ struct rfcomm_dlc *d;
+@@ -1203,11 +1246,7 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
+ if (d) {
+ if (d->state == BT_OPEN) {
+ /* DLC was previously opened by PN request */
+- if (rfcomm_check_link_mode(d)) {
+- set_bit(RFCOMM_AUTH_PENDING, &d->flags);
+- rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
+- } else
+- rfcomm_dlc_accept(d);
++ rfcomm_check_accept(d);
+ }
+ return 0;
+ }
+@@ -1219,11 +1258,7 @@ static int rfcomm_recv_sabm(struct rfcomm_session *s, u8 dlci)
+ d->addr = __addr(s->initiator, dlci);
+ rfcomm_dlc_link(s, d);
+
+- if (rfcomm_check_link_mode(d)) {
+- set_bit(RFCOMM_AUTH_PENDING, &d->flags);
+- rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
+- } else
+- rfcomm_dlc_accept(d);
++ rfcomm_check_accept(d);
+ } else {
+ rfcomm_send_dm(s, dlci);
+ }
+@@ -1637,11 +1672,12 @@ static void rfcomm_process_connect(struct rfcomm_session *s)
+ d = list_entry(p, struct rfcomm_dlc, list);
+ if (d->state == BT_CONFIG) {
+ d->mtu = s->mtu;
+- if (rfcomm_check_link_mode(d)) {
++ if (rfcomm_check_security(d)) {
++ rfcomm_send_pn(s, 1, d);
++ } else {
+ set_bit(RFCOMM_AUTH_PENDING, &d->flags);
+ rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
+- } else
+- rfcomm_send_pn(s, 1, d);
++ }
+ }
+ }
+ }
+@@ -1717,11 +1753,17 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
+ if (d->out) {
+ rfcomm_send_pn(s, 1, d);
+ rfcomm_dlc_set_timer(d, RFCOMM_CONN_TIMEOUT);
+- } else
+- rfcomm_dlc_accept(d);
+- if (d->link_mode & RFCOMM_LM_SECURE) {
+- struct sock *sk = s->sock->sk;
+- hci_conn_change_link_key(l2cap_pi(sk)->conn->hcon);
++ } else {
++ if (d->defer_setup) {
++ set_bit(RFCOMM_DEFER_SETUP, &d->flags);
++ rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
++
++ rfcomm_dlc_lock(d);
++ d->state = BT_CONNECT2;
++ d->state_change(d, 0);
++ rfcomm_dlc_unlock(d);
++ } else
++ rfcomm_dlc_accept(d);
+ }
+ continue;
+ } else if (test_and_clear_bit(RFCOMM_AUTH_REJECT, &d->flags)) {
+@@ -1734,6 +1776,9 @@ static inline void rfcomm_process_dlcs(struct rfcomm_session *s)
+ continue;
+ }
+
++ if (test_bit(RFCOMM_SEC_PENDING, &d->flags))
++ continue;
++
+ if (test_bit(RFCOMM_TX_THROTTLED, &s->flags))
+ continue;
+
+@@ -1876,6 +1921,7 @@ static int rfcomm_add_listener(bdaddr_t *ba)
+ bacpy(&addr.l2_bdaddr, ba);
+ addr.l2_family = AF_BLUETOOTH;
+ addr.l2_psm = htobs(RFCOMM_PSM);
++ addr.l2_cid = 0;
+ err = kernel_bind(sock, (struct sockaddr *) &addr, sizeof(addr));
+ if (err < 0) {
+ BT_ERR("Bind failed %d", err);
+@@ -1947,42 +1993,7 @@ static int rfcomm_run(void *unused)
+ return 0;
+ }
+
+-static void rfcomm_auth_cfm(struct hci_conn *conn, u8 status)
+-{
+- struct rfcomm_session *s;
+- struct rfcomm_dlc *d;
+- struct list_head *p, *n;
+-
+- BT_DBG("conn %p status 0x%02x", conn, status);
+-
+- s = rfcomm_session_get(&conn->hdev->bdaddr, &conn->dst);
+- if (!s)
+- return;
+-
+- rfcomm_session_hold(s);
+-
+- list_for_each_safe(p, n, &s->dlcs) {
+- d = list_entry(p, struct rfcomm_dlc, list);
+-
+- if ((d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) &&
+- !(conn->link_mode & HCI_LM_ENCRYPT) && !status)
+- continue;
+-
+- if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
+- continue;
+-
+- if (!status)
+- set_bit(RFCOMM_AUTH_ACCEPT, &d->flags);
+- else
+- set_bit(RFCOMM_AUTH_REJECT, &d->flags);
+- }
+-
+- rfcomm_session_put(s);
+-
+- rfcomm_schedule(RFCOMM_SCHED_AUTH);
+-}
+-
+-static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
++static void rfcomm_security_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
+ {
+ struct rfcomm_session *s;
+ struct rfcomm_dlc *d;
+@@ -1999,18 +2010,29 @@ static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
+ list_for_each_safe(p, n, &s->dlcs) {
+ d = list_entry(p, struct rfcomm_dlc, list);
+
+- if ((d->link_mode & (RFCOMM_LM_ENCRYPT | RFCOMM_LM_SECURE)) &&
+- (d->state == BT_CONNECTED ||
+- d->state == BT_CONFIG) &&
+- !status && encrypt == 0x00) {
+- __rfcomm_dlc_close(d, ECONNREFUSED);
+- continue;
++ if (test_and_clear_bit(RFCOMM_SEC_PENDING, &d->flags)) {
++ rfcomm_dlc_clear_timer(d);
++ if (status || encrypt == 0x00) {
++ __rfcomm_dlc_close(d, ECONNREFUSED);
++ continue;
++ }
++ }
++
++ if (d->state == BT_CONNECTED && !status && encrypt == 0x00) {
++ if (d->sec_level == BT_SECURITY_MEDIUM) {
++ set_bit(RFCOMM_SEC_PENDING, &d->flags);
++ rfcomm_dlc_set_timer(d, RFCOMM_AUTH_TIMEOUT);
++ continue;
++ } else if (d->sec_level == BT_SECURITY_HIGH) {
++ __rfcomm_dlc_close(d, ECONNREFUSED);
++ continue;
++ }
+ }
+
+ if (!test_and_clear_bit(RFCOMM_AUTH_PENDING, &d->flags))
+ continue;
+
+- if (!status && encrypt)
++ if (!status)
+ set_bit(RFCOMM_AUTH_ACCEPT, &d->flags);
+ else
+ set_bit(RFCOMM_AUTH_REJECT, &d->flags);
+@@ -2023,8 +2045,7 @@ static void rfcomm_encrypt_cfm(struct hci_conn *conn, u8 status, u8 encrypt)
+
+ static struct hci_cb rfcomm_cb = {
+ .name = "RFCOMM",
+- .auth_cfm = rfcomm_auth_cfm,
+- .encrypt_cfm = rfcomm_encrypt_cfm
++ .security_cfm = rfcomm_security_cfm
+ };
+
+ static ssize_t rfcomm_dlc_sysfs_show(struct class *dev, char *buf)
+diff --git a/net/bluetooth/rfcomm/sock.c b/net/bluetooth/rfcomm/sock.c
+index d3fc6fc..7f48278 100644
+--- a/net/bluetooth/rfcomm/sock.c
++++ b/net/bluetooth/rfcomm/sock.c
+@@ -261,12 +261,19 @@ static void rfcomm_sock_init(struct sock *sk, struct sock *parent)
+
+ if (parent) {
+ sk->sk_type = parent->sk_type;
+- pi->link_mode = rfcomm_pi(parent)->link_mode;
++ pi->dlc->defer_setup = bt_sk(parent)->defer_setup;
++
++ pi->sec_level = rfcomm_pi(parent)->sec_level;
++ pi->role_switch = rfcomm_pi(parent)->role_switch;
+ } else {
+- pi->link_mode = 0;
++ pi->dlc->defer_setup = 0;
++
++ pi->sec_level = BT_SECURITY_LOW;
++ pi->role_switch = 0;
+ }
+
+- pi->dlc->link_mode = pi->link_mode;
++ pi->dlc->sec_level = pi->sec_level;
++ pi->dlc->role_switch = pi->role_switch;
+ }
+
+ static struct proto rfcomm_proto = {
+@@ -406,7 +413,8 @@ static int rfcomm_sock_connect(struct socket *sock, struct sockaddr *addr, int a
+ bacpy(&bt_sk(sk)->dst, &sa->rc_bdaddr);
+ rfcomm_pi(sk)->channel = sa->rc_channel;
+
+- d->link_mode = rfcomm_pi(sk)->link_mode;
++ d->sec_level = rfcomm_pi(sk)->sec_level;
++ d->role_switch = rfcomm_pi(sk)->role_switch;
+
+ err = rfcomm_dlc_open(d, &bt_sk(sk)->src, &sa->rc_bdaddr, sa->rc_channel);
+ if (!err)
+@@ -554,6 +562,9 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
+ struct sk_buff *skb;
+ int sent = 0;
+
++ if (test_bit(RFCOMM_DEFER_SETUP, &d->flags))
++ return -ENOTCONN;
++
+ if (msg->msg_flags & MSG_OOB)
+ return -EOPNOTSUPP;
+
+@@ -570,8 +581,11 @@ static int rfcomm_sock_sendmsg(struct kiocb *iocb, struct socket *sock,
+
+ skb = sock_alloc_send_skb(sk, size + RFCOMM_SKB_RESERVE,
+ msg->msg_flags & MSG_DONTWAIT, &err);
+- if (!skb)
++ if (!skb) {
++ if (sent == 0)
++ sent = err;
+ break;
++ }
+ skb_reserve(skb, RFCOMM_SKB_HEAD_RESERVE);
+
+ err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
+@@ -630,10 +644,16 @@ static int rfcomm_sock_recvmsg(struct kiocb *iocb, struct socket *sock,
+ struct msghdr *msg, size_t size, int flags)
+ {
+ struct sock *sk = sock->sk;
++ struct rfcomm_dlc *d = rfcomm_pi(sk)->dlc;
+ int err = 0;
+ size_t target, copied = 0;
+ long timeo;
+
++ if (test_and_clear_bit(RFCOMM_DEFER_SETUP, &d->flags)) {
++ rfcomm_dlc_accept(d);
++ return 0;
++ }
++
+ if (flags & MSG_OOB)
+ return -EOPNOTSUPP;
+
+@@ -710,7 +730,7 @@ out:
+ return copied ? : err;
+ }
+
+-static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
++static int rfcomm_sock_setsockopt_old(struct socket *sock, int optname, char __user *optval, int optlen)
+ {
+ struct sock *sk = sock->sk;
+ int err = 0;
+@@ -727,7 +747,14 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c
+ break;
+ }
+
+- rfcomm_pi(sk)->link_mode = opt;
++ if (opt & RFCOMM_LM_AUTH)
++ rfcomm_pi(sk)->sec_level = BT_SECURITY_LOW;
++ if (opt & RFCOMM_LM_ENCRYPT)
++ rfcomm_pi(sk)->sec_level = BT_SECURITY_MEDIUM;
++ if (opt & RFCOMM_LM_SECURE)
++ rfcomm_pi(sk)->sec_level = BT_SECURITY_HIGH;
++
++ rfcomm_pi(sk)->role_switch = (opt & RFCOMM_LM_MASTER);
+ break;
+
+ default:
+@@ -739,12 +766,76 @@ static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, c
+ return err;
+ }
+
+-static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
++static int rfcomm_sock_setsockopt(struct socket *sock, int level, int optname, char __user *optval, int optlen)
++{
++ struct sock *sk = sock->sk;
++ struct bt_security sec;
++ int len, err = 0;
++ u32 opt;
++
++ BT_DBG("sk %p", sk);
++
++ if (level == SOL_RFCOMM)
++ return rfcomm_sock_setsockopt_old(sock, optname, optval, optlen);
++
++ if (level != SOL_BLUETOOTH)
++ return -ENOPROTOOPT;
++
++ lock_sock(sk);
++
++ switch (optname) {
++ case BT_SECURITY:
++ if (sk->sk_type != SOCK_STREAM) {
++ err = -EINVAL;
++ break;
++ }
++
++ sec.level = BT_SECURITY_LOW;
++
++ len = min_t(unsigned int, sizeof(sec), optlen);
++ if (copy_from_user((char *) &sec, optval, len)) {
++ err = -EFAULT;
++ break;
++ }
++
++ if (sec.level > BT_SECURITY_HIGH) {
++ err = -EINVAL;
++ break;
++ }
++
++ rfcomm_pi(sk)->sec_level = sec.level;
++ break;
++
++ case BT_DEFER_SETUP:
++ if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
++ err = -EINVAL;
++ break;
++ }
++
++ if (get_user(opt, (u32 __user *) optval)) {
++ err = -EFAULT;
++ break;
++ }
++
++ bt_sk(sk)->defer_setup = opt;
++ break;
++
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ }
++
++ release_sock(sk);
++ return err;
++}
++
++static int rfcomm_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
+ {
+ struct sock *sk = sock->sk;
+ struct sock *l2cap_sk;
+ struct rfcomm_conninfo cinfo;
+ int len, err = 0;
++ u32 opt;
+
+ BT_DBG("sk %p", sk);
+
+@@ -755,12 +846,32 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c
+
+ switch (optname) {
+ case RFCOMM_LM:
+- if (put_user(rfcomm_pi(sk)->link_mode, (u32 __user *) optval))
++ switch (rfcomm_pi(sk)->sec_level) {
++ case BT_SECURITY_LOW:
++ opt = RFCOMM_LM_AUTH;
++ break;
++ case BT_SECURITY_MEDIUM:
++ opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT;
++ break;
++ case BT_SECURITY_HIGH:
++ opt = RFCOMM_LM_AUTH | RFCOMM_LM_ENCRYPT |
++ RFCOMM_LM_SECURE;
++ break;
++ default:
++ opt = 0;
++ break;
++ }
++
++ if (rfcomm_pi(sk)->role_switch)
++ opt |= RFCOMM_LM_MASTER;
++
++ if (put_user(opt, (u32 __user *) optval))
+ err = -EFAULT;
+ break;
+
+ case RFCOMM_CONNINFO:
+- if (sk->sk_state != BT_CONNECTED) {
++ if (sk->sk_state != BT_CONNECTED &&
++ !rfcomm_pi(sk)->dlc->defer_setup) {
+ err = -ENOTCONN;
+ break;
+ }
+@@ -785,6 +896,60 @@ static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, c
+ return err;
+ }
+
++static int rfcomm_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
++{
++ struct sock *sk = sock->sk;
++ struct bt_security sec;
++ int len, err = 0;
++
++ BT_DBG("sk %p", sk);
++
++ if (level == SOL_RFCOMM)
++ return rfcomm_sock_getsockopt_old(sock, optname, optval, optlen);
++
++ if (level != SOL_BLUETOOTH)
++ return -ENOPROTOOPT;
++
++ if (get_user(len, optlen))
++ return -EFAULT;
++
++ lock_sock(sk);
++
++ switch (optname) {
++ case BT_SECURITY:
++ if (sk->sk_type != SOCK_STREAM) {
++ err = -EINVAL;
++ break;
++ }
++
++ sec.level = rfcomm_pi(sk)->sec_level;
++
++ len = min_t(unsigned int, len, sizeof(sec));
++ if (copy_to_user(optval, (char *) &sec, len))
++ err = -EFAULT;
++
++ break;
++
++ case BT_DEFER_SETUP:
++ if (sk->sk_state != BT_BOUND && sk->sk_state != BT_LISTEN) {
++ err = -EINVAL;
++ break;
++ }
++
++ if (put_user(bt_sk(sk)->defer_setup, (u32 __user *) optval))
++ err = -EFAULT;
++
++ break;
++
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ }
++
++ release_sock(sk);
++ return err;
++}
++
+ static int rfcomm_sock_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg)
+ {
+ struct sock *sk __maybe_unused = sock->sk;
+@@ -888,6 +1053,10 @@ int rfcomm_connect_ind(struct rfcomm_session *s, u8 channel, struct rfcomm_dlc *
+
+ done:
+ bh_unlock_sock(parent);
++
++ if (bt_sk(parent)->defer_setup)
++ parent->sk_state_change(parent);
++
+ return result;
+ }
+
+diff --git a/net/bluetooth/sco.c b/net/bluetooth/sco.c
+index 46fd8bf..51ae0c3 100644
+--- a/net/bluetooth/sco.c
++++ b/net/bluetooth/sco.c
+@@ -195,7 +195,7 @@ static int sco_connect(struct sock *sk)
+ else
+ type = SCO_LINK;
+
+- hcon = hci_connect(hdev, type, dst, HCI_AT_NO_BONDING);
++ hcon = hci_connect(hdev, type, dst, BT_SECURITY_LOW, HCI_AT_NO_BONDING);
+ if (!hcon)
+ goto done;
+
+@@ -668,7 +668,7 @@ static int sco_sock_setsockopt(struct socket *sock, int level, int optname, char
+ return err;
+ }
+
+-static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
++static int sco_sock_getsockopt_old(struct socket *sock, int optname, char __user *optval, int __user *optlen)
+ {
+ struct sock *sk = sock->sk;
+ struct sco_options opts;
+@@ -723,6 +723,31 @@ static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char
+ return err;
+ }
+
++static int sco_sock_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *optlen)
++{
++ struct sock *sk = sock->sk;
++ int len, err = 0;
++
++ BT_DBG("sk %p", sk);
++
++ if (level == SOL_SCO)
++ return sco_sock_getsockopt_old(sock, optname, optval, optlen);
++
++ if (get_user(len, optlen))
++ return -EFAULT;
++
++ lock_sock(sk);
++
++ switch (optname) {
++ default:
++ err = -ENOPROTOOPT;
++ break;
++ }
++
++ release_sock(sk);
++ return err;
++}
++
+ static int sco_sock_release(struct socket *sock)
+ {
+ struct sock *sk = sock->sk;
+@@ -832,10 +857,30 @@ done:
+ /* ----- SCO interface with lower layer (HCI) ----- */
+ static int sco_connect_ind(struct hci_dev *hdev, bdaddr_t *bdaddr, __u8 type)
+ {
++ register struct sock *sk;
++ struct hlist_node *node;
++ int lm = 0;
++
++ if (type != SCO_LINK && type != ESCO_LINK)
++ return 0;
++
+ BT_DBG("hdev %s, bdaddr %s", hdev->name, batostr(bdaddr));
+
+- /* Always accept connection */
+- return HCI_LM_ACCEPT;
++ /* Find listening sockets */
++ read_lock(&sco_sk_list.lock);
++ sk_for_each(sk, node, &sco_sk_list.head) {
++ if (sk->sk_state != BT_LISTEN)
++ continue;
++
++ if (!bacmp(&bt_sk(sk)->src, &hdev->bdaddr) ||
++ !bacmp(&bt_sk(sk)->src, BDADDR_ANY)) {
++ lm |= HCI_LM_ACCEPT;
++ break;
++ }
++ }
++ read_unlock(&sco_sk_list.lock);
++
++ return lm;
+ }
+
+ static int sco_connect_cfm(struct hci_conn *hcon, __u8 status)
+@@ -857,7 +902,7 @@ static int sco_connect_cfm(struct hci_conn *hcon, __u8 status)
+ return 0;
+ }
+
+-static int sco_disconn_ind(struct hci_conn *hcon, __u8 reason)
++static int sco_disconn_cfm(struct hci_conn *hcon, __u8 reason)
+ {
+ BT_DBG("hcon %p reason %d", hcon, reason);
+
+@@ -940,7 +985,7 @@ static struct hci_proto sco_hci_proto = {
+ .id = HCI_PROTO_SCO,
+ .connect_ind = sco_connect_ind,
+ .connect_cfm = sco_connect_cfm,
+- .disconn_ind = sco_disconn_ind,
++ .disconn_cfm = sco_disconn_cfm,
+ .recv_scodata = sco_recv_scodata
+ };
+
diff --git a/git-cpufreq.patch b/git-cpufreq.patch
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/git-cpufreq.patch
diff --git a/git-linus.diff b/git-linus.diff
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/git-linus.diff
diff --git a/hda_intel-prealloc-4mb-dmabuffer.patch b/hda_intel-prealloc-4mb-dmabuffer.patch
new file mode 100644
index 0000000..c80f11d
--- /dev/null
+++ b/hda_intel-prealloc-4mb-dmabuffer.patch
@@ -0,0 +1,35 @@
+diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
+index c8d9178..7d3bb15 100644
+--- a/sound/pci/hda/hda_intel.c
++++ b/sound/pci/hda/hda_intel.c
+@@ -1774,6 +1774,7 @@ azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
+ struct azx_pcm *apcm;
+ int pcm_dev = cpcm->device;
+ int s, err;
++ size_t prealloc_min = 64*1024; /* 64KB */
+
+ if (pcm_dev >= AZX_MAX_PCMS) {
+ snd_printk(KERN_ERR SFX "Invalid PCM device number %d\n",
+@@ -1807,10 +1808,21 @@ azx_attach_pcm_stream(struct hda_bus *bus, struct hda_codec *codec,
+ if (cpcm->stream[s].substreams)
+ snd_pcm_set_ops(pcm, s, &azx_pcm_ops);
+ }
++
+ /* buffer pre-allocation */
++
++ /* subtle, don't allocate a big buffer for modems...
++ * also, don't just test 32BIT_MASK, since azx supports
++ * 64-bit DMA in some cases.
++ */
++ /* lennart wants a 2.2MB buffer for 2sec of 48khz */
++ if (pcm->dev_class == SNDRV_PCM_CLASS_GENERIC &&
++ chip->pci->dma_mask >= DMA_32BIT_MASK)
++ prealloc_min = 4 * 1024 * 1024; /* 4MB */
++
+ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG,
+ snd_dma_pci_data(chip->pci),
+- 1024 * 64, 32 * 1024 * 1024);
++ prealloc_min, 32 * 1024 * 1024);
+ return 0;
+ }
+
diff --git a/hdpvr-ir-enable.patch b/hdpvr-ir-enable.patch
new file mode 100644
index 0000000..a5c7e92
--- /dev/null
+++ b/hdpvr-ir-enable.patch
@@ -0,0 +1,213 @@
+diff -Naurp a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c
+--- a/drivers/media/video/hdpvr/hdpvr-core.c 2010-07-06 17:36:44.000000000 -0400
++++ b/drivers/media/video/hdpvr/hdpvr-core.c 2010-07-06 17:38:13.000000000 -0400
+@@ -363,9 +363,8 @@ static int hdpvr_probe(struct usb_interf
+ goto error;
+ }
+
+-#ifdef CONFIG_I2C
+- /* until i2c is working properly */
+- retval = 0; /* hdpvr_register_i2c_adapter(dev); */
++#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
++ retval = hdpvr_register_i2c_adapter(dev);
+ if (retval < 0) {
+ v4l2_err(&dev->v4l2_dev, "registering i2c adapter failed\n");
+ goto error;
+@@ -411,12 +410,9 @@ static void hdpvr_disconnect(struct usb_
+ mutex_unlock(&dev->io_mutex);
+
+ /* deregister I2C adapter */
+-#ifdef CONFIG_I2C
++#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
+ mutex_lock(&dev->i2c_mutex);
+- if (dev->i2c_adapter)
+- i2c_del_adapter(dev->i2c_adapter);
+- kfree(dev->i2c_adapter);
+- dev->i2c_adapter = NULL;
++ i2c_del_adapter(&dev->i2c_adapter);
+ mutex_unlock(&dev->i2c_mutex);
+ #endif /* CONFIG_I2C */
+
+diff -Naurp a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/video/hdpvr/hdpvr.h
+--- a/drivers/media/video/hdpvr/hdpvr.h 2010-02-24 13:52:17.000000000 -0500
++++ b/drivers/media/video/hdpvr/hdpvr.h 2010-07-06 17:42:20.000000000 -0400
+@@ -101,7 +101,7 @@ struct hdpvr_device {
+ struct work_struct worker;
+
+ /* I2C adapter */
+- struct i2c_adapter *i2c_adapter;
++ struct i2c_adapter i2c_adapter;
+ /* I2C lock */
+ struct mutex i2c_mutex;
+
+diff -Naurp a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/video/hdpvr/hdpvr-i2c.c
+--- a/drivers/media/video/hdpvr/hdpvr-i2c.c 2010-07-06 17:36:51.000000000 -0400
++++ b/drivers/media/video/hdpvr/hdpvr-i2c.c 2010-07-06 17:45:50.000000000 -0400
+@@ -10,6 +10,8 @@
+ *
+ */
+
++#if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
++
+ #include <linux/i2c.h>
+
+ #include "hdpvr.h"
+@@ -19,10 +21,13 @@
+
+ #define REQTYPE_I2C_READ 0xb1
+ #define REQTYPE_I2C_WRITE 0xb0
+-#define REQTYPE_I2C_WRITE_STATT 0xd0
++#define REQTYPE_I2C_WRITE_STAT 0xd0
++
++#define HDPVR_HW_Z8F0811_IR_TX_I2C_ADDR 0x70
++#define HDPVR_HW_Z8F0811_IR_RX_I2C_ADDR 0x71
+
+ static int hdpvr_i2c_read(struct hdpvr_device *dev, unsigned char addr,
+- char *data, int len)
++ char *data, int len, int bus)
+ {
+ int ret;
+ char *buf = kmalloc(len, GFP_KERNEL);
+@@ -32,7 +37,7 @@ static int hdpvr_i2c_read(struct hdpvr_d
+ ret = usb_control_msg(dev->udev,
+ usb_rcvctrlpipe(dev->udev, 0),
+ REQTYPE_I2C_READ, CTRL_READ_REQUEST,
+- 0x100|addr, 0, buf, len, 1000);
++ bus<<8 | addr, 0, buf, len, 1000);
+
+ if (ret == len) {
+ memcpy(data, buf, len);
+@@ -46,7 +51,7 @@ static int hdpvr_i2c_read(struct hdpvr_d
+ }
+
+ static int hdpvr_i2c_write(struct hdpvr_device *dev, unsigned char addr,
+- char *data, int len)
++ char *data, int len, int bus)
+ {
+ int ret;
+ char *buf = kmalloc(len, GFP_KERNEL);
+@@ -57,17 +62,17 @@ static int hdpvr_i2c_write(struct hdpvr_
+ ret = usb_control_msg(dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST,
+- 0x100|addr, 0, buf, len, 1000);
++ bus<<8 | addr, 0, buf, len, 1000);
+
+ if (ret < 0)
+ goto error;
+
+ ret = usb_control_msg(dev->udev,
+ usb_rcvctrlpipe(dev->udev, 0),
+- REQTYPE_I2C_WRITE_STATT, CTRL_READ_REQUEST,
++ REQTYPE_I2C_WRITE_STAT, CTRL_READ_REQUEST,
+ 0, 0, buf, 2, 1000);
+
+- if (ret == 2)
++ if (ret == 2 && buf[1] == (len - 1))
+ ret = 0;
+ else if (ret >= 0)
+ ret = -EIO;
+@@ -93,10 +98,10 @@ static int hdpvr_transfer(struct i2c_ada
+
+ if (msgs[i].flags & I2C_M_RD)
+ retval = hdpvr_i2c_read(dev, addr, msgs[i].buf,
+- msgs[i].len);
++ msgs[i].len, 1);
+ else
+ retval = hdpvr_i2c_write(dev, addr, msgs[i].buf,
+- msgs[i].len);
++ msgs[i].len, 1);
+ }
+
+ mutex_unlock(&dev->i2c_mutex);
+@@ -114,31 +119,61 @@ static struct i2c_algorithm hdpvr_algo =
+ .functionality = hdpvr_functionality,
+ };
+
++static struct i2c_adapter hdpvr_i2c_adap_template = {
++ .name = "Hauppauge HD PVR I2C",
++ .owner = THIS_MODULE,
++ .id = I2C_HW_B_HDPVR,
++ .algo = &hdpvr_algo,
++ .algo_data = NULL,
++ .class = I2C_CLASS_TV_ANALOG,
++};
++
++static struct i2c_board_info hdpvr_i2c_board_info = {
++ I2C_BOARD_INFO("ir_tx_z8f0811_haup", HDPVR_HW_Z8F0811_IR_TX_I2C_ADDR),
++ I2C_BOARD_INFO("ir_rx_z8f0811_haup", HDPVR_HW_Z8F0811_IR_RX_I2C_ADDR),
++};
++
++static int hdpvr_activate_ir(struct hdpvr_device *dev)
++{
++ char buffer[8];
++
++ mutex_lock(&dev->i2c_mutex);
++
++ hdpvr_i2c_read(dev, 0x54, buffer, 1, 0);
++
++ buffer[0] = 0;
++ buffer[1] = 0x8;
++ hdpvr_i2c_write(dev, 0x54, buffer, 2, 1);
++
++ buffer[1] = 0x18;
++ hdpvr_i2c_write(dev, 0x54, buffer, 2, 1);
++
++ mutex_unlock(&dev->i2c_mutex);
++ return 0;
++}
++
++
+ int hdpvr_register_i2c_adapter(struct hdpvr_device *dev)
+ {
+- struct i2c_adapter *i2c_adap;
+ int retval = -ENOMEM;
+
+- i2c_adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
+- if (i2c_adap == NULL)
++ hdpvr_activate_ir(dev);
++
++ memcpy(&dev->i2c_adapter, &hdpvr_i2c_adap_template,
++ sizeof(struct i2c_adapter));
++ dev->i2c_adapter.dev.parent = &dev->udev->dev;
++
++ i2c_set_adapdata(&dev->i2c_adapter, dev);
++
++ retval = i2c_add_adapter(&dev->i2c_adapter);
++
++ if (retval)
+ goto error;
+
+- strlcpy(i2c_adap->name, "Hauppauge HD PVR I2C",
+- sizeof(i2c_adap->name));
+- i2c_adap->algo = &hdpvr_algo;
+- i2c_adap->class = I2C_CLASS_TV_ANALOG;
+- i2c_adap->owner = THIS_MODULE;
+- i2c_adap->dev.parent = &dev->udev->dev;
+-
+- i2c_set_adapdata(i2c_adap, dev);
+-
+- retval = i2c_add_adapter(i2c_adap);
+-
+- if (!retval)
+- dev->i2c_adapter = i2c_adap;
+- else
+- kfree(i2c_adap);
++ i2c_new_device(&dev->i2c_adapter, &hdpvr_i2c_board_info);
+
+ error:
+ return retval;
+ }
++
++#endif /* CONFIG_I2C */
+diff -Naurp a/drivers/media/video/hdpvr/Makefile b/drivers/media/video/hdpvr/Makefile
+--- a/drivers/media/video/hdpvr/Makefile 2010-07-06 17:36:38.000000000 -0400
++++ b/drivers/media/video/hdpvr/Makefile 2010-07-06 17:35:17.000000000 -0400
+@@ -1,6 +1,4 @@
+-hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o
+-
+-hdpvr-$(CONFIG_I2C) += hdpvr-i2c.o
++hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-i2c.o hdpvr-video.o
+
+ obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o
+
diff --git a/ice1712-fix-revo71-mixer-names.patch b/ice1712-fix-revo71-mixer-names.patch
new file mode 100644
index 0000000..3296510
--- /dev/null
+++ b/ice1712-fix-revo71-mixer-names.patch
@@ -0,0 +1,42 @@
+diff --git a/sound/pci/ice1712/revo.c b/sound/pci/ice1712/revo.c
+index b508bb3..dc13e2d 100644
+--- a/sound/pci/ice1712/revo.c
++++ b/sound/pci/ice1712/revo.c
+@@ -197,26 +197,26 @@ static int revo51_i2c_init(struct snd_ice1712 *ice,
+
+ static const struct snd_akm4xxx_dac_channel revo71_front[] = {
+ {
+- .name = "PCM Playback Volume",
++ .name = "Front Playback Volume",
+ .num_channels = 2,
+ /* front channels DAC supports muting */
+- .switch_name = "PCM Playback Switch",
++ .switch_name = "Front Playback Switch",
+ },
+ };
+
+ static const struct snd_akm4xxx_dac_channel revo71_surround[] = {
+- AK_DAC("PCM Center Playback Volume", 1),
+- AK_DAC("PCM LFE Playback Volume", 1),
+- AK_DAC("PCM Side Playback Volume", 2),
+- AK_DAC("PCM Rear Playback Volume", 2),
++ AK_DAC("Center Playback Volume", 1),
++ AK_DAC("LFE Playback Volume", 1),
++ AK_DAC("Side Playback Volume", 2),
++ AK_DAC("Rear Playback Volume", 2),
+ };
+
+ static const struct snd_akm4xxx_dac_channel revo51_dac[] = {
+- AK_DAC("PCM Playback Volume", 2),
+- AK_DAC("PCM Center Playback Volume", 1),
+- AK_DAC("PCM LFE Playback Volume", 1),
+- AK_DAC("PCM Rear Playback Volume", 2),
+- AK_DAC("PCM Headphone Volume", 2),
++ AK_DAC("Front Playback Volume", 2),
++ AK_DAC("Center Playback Volume", 1),
++ AK_DAC("LFE Playback Volume", 1),
++ AK_DAC("Rear Playback Volume", 2),
++ AK_DAC("Headphone Volume", 2),
+ };
+
+ static const char *revo51_adc_input_names[] = {
diff --git a/inotify-fix-inotify-oneshot-support.patch b/inotify-fix-inotify-oneshot-support.patch
new file mode 100644
index 0000000..ba63e10
--- /dev/null
+++ b/inotify-fix-inotify-oneshot-support.patch
@@ -0,0 +1,25 @@
+#607327
+
+During the large inotify rewrite to fsnotify I completely dropped support
+for IN_ONESHOT. Reimplement that support.
+
+Signed-off-by: Eric Paris <eparis@redhat.com>
+---
+
+ fs/notify/inotify/inotify_fsnotify.c | 3 +++
+ 1 files changed, 3 insertions(+), 0 deletions(-)
+
+diff --git a/fs/notify/inotify/inotify_fsnotify.c b/fs/notify/inotify/inotify_fsnotify.c
+index daa666a..388a150 100644
+--- a/fs/notify/inotify/inotify_fsnotify.c
++++ b/fs/notify/inotify/inotify_fsnotify.c
+@@ -126,6 +126,9 @@ static int inotify_handle_event(struct fsnotify_group *group, struct fsnotify_ev
+ ret = 0;
+ }
+
++ if (entry->mask & IN_ONESHOT)
++ fsnotify_destroy_mark_by_entry(entry);
++
+ /*
+ * If we hold the entry until after the event is on the queue
+ * IN_IGNORED won't be able to pass this event in the queue
diff --git a/inotify-send-IN_UNMOUNT-events.patch b/inotify-send-IN_UNMOUNT-events.patch
new file mode 100644
index 0000000..cf1d4c4
--- /dev/null
+++ b/inotify-send-IN_UNMOUNT-events.patch
@@ -0,0 +1,29 @@
+#607327 ?
+
+Since the .31 or so notify rewrite inotify has not sent events about
+inodes which are unmounted. This patch restores those events.
+
+Signed-off-by: Eric Paris <eparis@redhat.com>
+---
+
+ fs/notify/inotify/inotify_user.c | 7 +++++--
+ 1 files changed, 5 insertions(+), 2 deletions(-)
+
+diff --git a/fs/notify/inotify/inotify_user.c b/fs/notify/inotify/inotify_user.c
+index 44aeb0f..f381daf 100644
+--- a/fs/notify/inotify/inotify_user.c
++++ b/fs/notify/inotify/inotify_user.c
+@@ -90,8 +90,11 @@ static inline __u32 inotify_arg_to_mask(u32 arg)
+ {
+ __u32 mask;
+
+- /* everything should accept their own ignored and cares about children */
+- mask = (FS_IN_IGNORED | FS_EVENT_ON_CHILD);
++ /*
++ * everything should accept their own ignored, cares about children,
++ * and should receive events when the inode is unmounted
++ */
++ mask = (FS_IN_IGNORED | FS_EVENT_ON_CHILD | FS_UNMOUNT);
+
+ /* mask off the flags used to open the fd */
+ mask |= (arg & (IN_ALL_EVENTS | IN_ONESHOT));
diff --git a/iwlwifi-cancel-scan-watchdog-in-iwl_bg_abort_scan.patch b/iwlwifi-cancel-scan-watchdog-in-iwl_bg_abort_scan.patch
new file mode 100644
index 0000000..465d2ac
--- /dev/null
+++ b/iwlwifi-cancel-scan-watchdog-in-iwl_bg_abort_scan.patch
@@ -0,0 +1,58 @@
+commit a69b03e941abae00380fc6bc1877fb797a1b31e6
+Author: John W. Linville <linville@tuxdriver.com>
+Date: Mon Jun 14 14:30:25 2010 -0400
+
+ iwlwifi: cancel scan watchdog in iwl_bg_abort_scan
+
+ Avoids this:
+
+ WARNING: at net/mac80211/scan.c:312 ieee80211_scan_completed+0x5f/0x1f1
+ [mac80211]()
+ Hardware name: Latitude E5400
+ Modules linked in: aes_x86_64 aes_generic fuse ipt_MASQUERADE iptable_nat
+ nf_nat rfcomm sco bridge stp llc bnep l2cap sunrpc cpufreq_ondemand
+ acpi_cpufreq freq_table xt_physdev ip6t_REJECT nf_conntrack_ipv6
+ ip6table_filter ip6_tables ipv6 kvm_intel kvm uinput arc4 ecb
+ snd_hda_codec_intelhdmi snd_hda_codec_idt snd_hda_intel iwlagn snd_hda_codec
+ snd_hwdep snd_seq snd_seq_device iwlcore snd_pcm dell_wmi sdhci_pci sdhci
+ iTCO_wdt tg3 dell_laptop mmc_core i2c_i801 wmi mac80211 snd_timer
+ iTCO_vendor_support btusb joydev dcdbas cfg80211 bluetooth snd soundcore
+ microcode rfkill snd_page_alloc firewire_ohci firewire_core crc_itu_t
+ yenta_socket rsrc_nonstatic i915 drm_kms_helper drm i2c_algo_bit i2c_core video
+ output [last unloaded: scsi_wait_scan]
+ Pid: 979, comm: iwlagn Tainted: G W 2.6.33.3-85.fc13.x86_64 #1
+ Call Trace:
+ [<ffffffff8104b558>] warn_slowpath_common+0x77/0x8f
+ [<ffffffff8104b57f>] warn_slowpath_null+0xf/0x11
+ [<ffffffffa01bb7d9>] ieee80211_scan_completed+0x5f/0x1f1 [mac80211]
+ [<ffffffffa02a23f0>] iwl_bg_scan_completed+0xbb/0x17a [iwlcore]
+ [<ffffffff81060d3d>] worker_thread+0x1a4/0x232
+ [<ffffffffa02a2335>] ? iwl_bg_scan_completed+0x0/0x17a [iwlcore]
+ [<ffffffff81064817>] ? autoremove_wake_function+0x0/0x34
+ [<ffffffff81060b99>] ? worker_thread+0x0/0x232
+ [<ffffffff810643c7>] kthread+0x7a/0x82
+ [<ffffffff8100a924>] kernel_thread_helper+0x4/0x10
+ [<ffffffff8106434d>] ? kthread+0x0/0x82
+ [<ffffffff8100a920>] ? kernel_thread_helper+0x0/0x10
+
+ Reported here:
+
+ https://bugzilla.redhat.com/show_bug.cgi?id=590436
+
+ Signed-off-by: John W. Linville <linville@tuxdriver.com>
+ Reported-by: Mihai Harpau <mishu@piatafinanciara.ro>
+ Cc: stable@kernel.org
+ Acked-by: Reinette Chatre <reinette.chatre@intel.com>
+
+diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
+index 5d3f51f..386c5f9 100644
+--- a/drivers/net/wireless/iwlwifi/iwl-scan.c
++++ b/drivers/net/wireless/iwlwifi/iwl-scan.c
+@@ -491,6 +491,7 @@ void iwl_bg_abort_scan(struct work_struct *work)
+
+ mutex_lock(&priv->mutex);
+
++ cancel_delayed_work_sync(&priv->scan_check);
+ set_bit(STATUS_SCAN_ABORTING, &priv->status);
+ iwl_send_scan_abort(priv);
+
diff --git a/iwlwifi-fix-internal-scan-race.patch b/iwlwifi-fix-internal-scan-race.patch
new file mode 100644
index 0000000..18b3156
--- /dev/null
+++ b/iwlwifi-fix-internal-scan-race.patch
@@ -0,0 +1,123 @@
+From reinette.chatre@intel.com Thu May 13 17:49:59 2010
+Return-path: <reinette.chatre@intel.com>
+Envelope-to: linville@tuxdriver.com
+Delivery-date: Thu, 13 May 2010 17:49:59 -0400
+Received: from mga09.intel.com ([134.134.136.24])
+ by smtp.tuxdriver.com with esmtp (Exim 4.63)
+ (envelope-from <reinette.chatre@intel.com>)
+ id 1OCgI1-0007H3-Eg
+ for linville@tuxdriver.com; Thu, 13 May 2010 17:49:59 -0400
+Received: from orsmga002.jf.intel.com ([10.7.209.21])
+ by orsmga102.jf.intel.com with ESMTP; 13 May 2010 14:48:04 -0700
+X-ExtLoop1: 1
+X-IronPort-AV: E=Sophos;i="4.53,224,1272870000";
+ d="scan'208";a="517743256"
+Received: from rchatre-desk.amr.corp.intel.com.jf.intel.com (HELO localhost.localdomain) ([134.134.15.94])
+ by orsmga002.jf.intel.com with ESMTP; 13 May 2010 14:49:12 -0700
+From: Reinette Chatre <reinette.chatre@intel.com>
+To: linville@tuxdriver.com
+Cc: linux-wireless@vger.kernel.org, ipw3945-devel@lists.sourceforge.net, Reinette Chatre <reinette.chatre@intel.com>
+Subject: [PATCH 1/2] iwlwifi: fix internal scan race
+Date: Thu, 13 May 2010 14:49:44 -0700
+Message-Id: <1273787385-9248-2-git-send-email-reinette.chatre@intel.com>
+X-Mailer: git-send-email 1.6.3.3
+In-Reply-To: <1273787385-9248-1-git-send-email-reinette.chatre@intel.com>
+References: <1273787385-9248-1-git-send-email-reinette.chatre@intel.com>
+X-Spam-Score: -4.2 (----)
+X-Spam-Status: No
+Status: RO
+Content-Length: 3370
+Lines: 91
+
+From: Reinette Chatre <reinette.chatre@intel.com>
+
+It is possible for internal scan to race against itself if the device is
+not returning the scan results from first requests. What happens in this
+case is the cleanup done during the abort of the first internal scan also
+cleans up part of the new scan, causing it to access memory it shouldn't.
+
+Here are details:
+* First internal scan is triggered and scan command sent to device.
+* After seven seconds there is no scan results so the watchdog timer
+ triggers a scan abort.
+* The scan abort succeeds and a SCAN_COMPLETE_NOTIFICATION is received for
+ failed scan.
+* During processing of SCAN_COMPLETE_NOTIFICATION we clear STATUS_SCANNING
+ and queue the "scan_completed" work.
+** At this time, since the problem that caused the internal scan in first
+ place is still present, a new internal scan is triggered.
+The behavior at this point is a bit different between 2.6.34 and 2.6.35
+since 2.6.35 has a lot of this synchronized. The rest of the race
+description will thus be generalized.
+** As part of preparing for the scan "is_internal_short_scan" is set to
+true.
+* At this point the completion work for fist scan is run. As part of this
+ there is some locking missing around the "is_internal_short_scan"
+ variable and it is set to "false".
+** Now the second scan runs and it considers itself a real (not internal0
+ scan and thus causes problems with wrong memory being accessed.
+
+The fix is twofold.
+* Since "is_internal_short_scan" should be protected by mutex, fix this in
+ scan completion work so that changes to it can be serialized.
+* Do not queue a new internal scan if one is in progress.
+
+This fixes https://bugzilla.kernel.org/show_bug.cgi?id=15824
+
+Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
+---
+ drivers/net/wireless/iwlwifi/iwl-scan.c | 21 ++++++++++++++++++---
+ 1 files changed, 18 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
+index 2367286..a2c4855 100644
+--- a/drivers/net/wireless/iwlwifi/iwl-scan.c
++++ b/drivers/net/wireless/iwlwifi/iwl-scan.c
+@@ -560,6 +560,11 @@ static void iwl_bg_start_internal_scan(struct work_struct *work)
+
+ mutex_lock(&priv->mutex);
+
++ if (priv->is_internal_short_scan == true) {
++ IWL_DEBUG_SCAN(priv, "Internal scan already in progress\n");
++ goto unlock;
++ }
++
+ if (!iwl_is_ready_rf(priv)) {
+ IWL_DEBUG_SCAN(priv, "not ready or exit pending\n");
+ goto unlock;
+@@ -957,17 +962,27 @@ void iwl_bg_scan_completed(struct work_struct *work)
+ {
+ struct iwl_priv *priv =
+ container_of(work, struct iwl_priv, scan_completed);
++ bool internal = false;
+
+ IWL_DEBUG_SCAN(priv, "SCAN complete scan\n");
+
+ cancel_delayed_work(&priv->scan_check);
+
+- if (!priv->is_internal_short_scan)
+- ieee80211_scan_completed(priv->hw, false);
+- else {
++ mutex_lock(&priv->mutex);
++ if (priv->is_internal_short_scan) {
+ priv->is_internal_short_scan = false;
+ IWL_DEBUG_SCAN(priv, "internal short scan completed\n");
++ internal = true;
+ }
++ mutex_unlock(&priv->mutex);
++
++ /*
++ * Do not hold mutex here since this will cause mac80211 to call
++ * into driver again into functions that will attempt to take
++ * mutex.
++ */
++ if (!internal)
++ ieee80211_scan_completed(priv->hw, false);
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+--
+1.6.3.3
+
+
+
diff --git a/iwlwifi-fix-scan-races.patch b/iwlwifi-fix-scan-races.patch
new file mode 100644
index 0000000..2e00f00
--- /dev/null
+++ b/iwlwifi-fix-scan-races.patch
@@ -0,0 +1,139 @@
+commit 88be026490ed89c2ffead81a52531fbac5507e01
+Author: Johannes Berg <johannes.berg@intel.com>
+Date: Wed Apr 7 00:21:36 2010 -0700
+
+ iwlwifi: fix scan races
+
+ When an internal scan is started, nothing protects the
+ is_internal_short_scan variable which can cause crashes,
+ cf. https://bugzilla.kernel.org/show_bug.cgi?id=15667.
+ Fix this by making the short scan request use the mutex
+ for locking, which requires making the request go to a
+ work struct so that it can sleep.
+
+ Reported-by: Peter Zijlstra <peterz@infradead.org>
+ Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+ Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
+
+diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
+index e4c2e1e..ba0fdba 100644
+--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
++++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
+@@ -3330,6 +3330,7 @@ static void iwl_cancel_deferred_work(struct iwl_priv *priv)
+
+ cancel_delayed_work_sync(&priv->init_alive_start);
+ cancel_delayed_work(&priv->scan_check);
++ cancel_work_sync(&priv->start_internal_scan);
+ cancel_delayed_work(&priv->alive_start);
+ cancel_work_sync(&priv->beacon_update);
+ del_timer_sync(&priv->statistics_periodic);
+diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
+index 894bcb8..1459cdb 100644
+--- a/drivers/net/wireless/iwlwifi/iwl-core.c
++++ b/drivers/net/wireless/iwlwifi/iwl-core.c
+@@ -3357,7 +3357,6 @@ static void iwl_force_rf_reset(struct iwl_priv *priv)
+ */
+ IWL_DEBUG_INFO(priv, "perform radio reset.\n");
+ iwl_internal_short_hw_scan(priv);
+- return;
+ }
+
+
+diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
+index 732590f..36940a9 100644
+--- a/drivers/net/wireless/iwlwifi/iwl-core.h
++++ b/drivers/net/wireless/iwlwifi/iwl-core.h
+@@ -506,7 +506,7 @@ void iwl_init_scan_params(struct iwl_priv *priv);
+ int iwl_scan_cancel(struct iwl_priv *priv);
+ int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
+ int iwl_mac_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req);
+-int iwl_internal_short_hw_scan(struct iwl_priv *priv);
++void iwl_internal_short_hw_scan(struct iwl_priv *priv);
+ int iwl_force_reset(struct iwl_priv *priv, int mode);
+ u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame,
+ const u8 *ie, int ie_len, int left);
+diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
+index 6054c5f..ef1720a 100644
+--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
++++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
+@@ -1296,6 +1296,7 @@ struct iwl_priv {
+ struct work_struct tt_work;
+ struct work_struct ct_enter;
+ struct work_struct ct_exit;
++ struct work_struct start_internal_scan;
+
+ struct tasklet_struct irq_tasklet;
+
+diff --git a/drivers/net/wireless/iwlwifi/iwl-scan.c b/drivers/net/wireless/iwlwifi/iwl-scan.c
+index bd2f7c4..5062f4e 100644
+--- a/drivers/net/wireless/iwlwifi/iwl-scan.c
++++ b/drivers/net/wireless/iwlwifi/iwl-scan.c
+@@ -469,6 +469,8 @@ EXPORT_SYMBOL(iwl_init_scan_params);
+
+ static int iwl_scan_initiate(struct iwl_priv *priv)
+ {
++ WARN_ON(!mutex_is_locked(&priv->mutex));
++
+ IWL_DEBUG_INFO(priv, "Starting scan...\n");
+ set_bit(STATUS_SCANNING, &priv->status);
+ priv->is_internal_short_scan = false;
+@@ -546,24 +548,31 @@ EXPORT_SYMBOL(iwl_mac_hw_scan);
+ * internal short scan, this function should only been called while associated.
+ * It will reset and tune the radio to prevent possible RF related problem
+ */
+-int iwl_internal_short_hw_scan(struct iwl_priv *priv)
++void iwl_internal_short_hw_scan(struct iwl_priv *priv)
+ {
+- int ret = 0;
++ queue_work(priv->workqueue, &priv->start_internal_scan);
++}
++
++static void iwl_bg_start_internal_scan(struct work_struct *work)
++{
++ struct iwl_priv *priv =
++ container_of(work, struct iwl_priv, start_internal_scan);
++
++ mutex_lock(&priv->mutex);
+
+ if (!iwl_is_ready_rf(priv)) {
+- ret = -EIO;
+ IWL_DEBUG_SCAN(priv, "not ready or exit pending\n");
+- goto out;
++ goto unlock;
+ }
++
+ if (test_bit(STATUS_SCANNING, &priv->status)) {
+ IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
+- ret = -EAGAIN;
+- goto out;
++ goto unlock;
+ }
++
+ if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
+ IWL_DEBUG_SCAN(priv, "Scan request while abort pending\n");
+- ret = -EAGAIN;
+- goto out;
++ goto unlock;
+ }
+
+ priv->scan_bands = 0;
+@@ -576,9 +585,8 @@ int iwl_internal_short_hw_scan(struct iwl_priv *priv)
+ set_bit(STATUS_SCANNING, &priv->status);
+ priv->is_internal_short_scan = true;
+ queue_work(priv->workqueue, &priv->request_scan);
+-
+-out:
+- return ret;
++ unlock:
++ mutex_unlock(&priv->mutex);
+ }
+ EXPORT_SYMBOL(iwl_internal_short_hw_scan);
+
+@@ -964,6 +972,7 @@ void iwl_setup_scan_deferred_work(struct iwl_priv *priv)
+ INIT_WORK(&priv->scan_completed, iwl_bg_scan_completed);
+ INIT_WORK(&priv->request_scan, iwl_bg_request_scan);
+ INIT_WORK(&priv->abort_scan, iwl_bg_abort_scan);
++ INIT_WORK(&priv->start_internal_scan, iwl_bg_start_internal_scan);
+ INIT_DELAYED_WORK(&priv->scan_check, iwl_bg_scan_check);
+ }
+ EXPORT_SYMBOL(iwl_setup_scan_deferred_work);
diff --git a/iwlwifi-manage-QoS-by-mac-stack.patch b/iwlwifi-manage-QoS-by-mac-stack.patch
new file mode 100644
index 0000000..9ac9971
--- /dev/null
+++ b/iwlwifi-manage-QoS-by-mac-stack.patch
@@ -0,0 +1,100 @@
+From: Stanislaw Gruszka <sgruszka@redhat.com>
+To: kernel@lists.fedoraproject.org, "John W. Linville" <linville@redhat.com>
+Subject: [PATCH 1/4 2.6.32.y] mac80211: explicitly disable/enable QoS
+Date: Fri, 11 Jun 2010 17:03:13 +0200
+
+Add interface to disable/enable QoS (aka WMM or WME). Currently drivers
+enable it explicitly when ->conf_tx method is called, and newer disable.
+Disabling is needed for some APs, which do not support QoS, such
+we should send QoS frames to them.
+
+Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
+---
+ include/net/mac80211.h | 5 +++++
+ net/mac80211/mlme.c | 9 ++++++++-
+ net/mac80211/util.c | 5 +++++
+ 3 files changed, 18 insertions(+), 1 deletions(-)
+
+diff --git a/include/net/mac80211.h b/include/net/mac80211.h
+index c39ed07..de904fc 100644
+--- a/include/net/mac80211.h
++++ b/include/net/mac80211.h
+@@ -572,11 +572,15 @@ struct ieee80211_rx_status {
+ * may turn the device off as much as possible. Typically, this flag will
+ * be set when an interface is set UP but not associated or scanning, but
+ * it can also be unset in that case when monitor interfaces are active.
++ * @IEEE80211_CONF_QOS: Enable 802.11e QoS also know as WMM (Wireless
++ * Multimedia). On some drivers (iwlwifi is one of know) we have
++ * to enable/disable QoS explicitly.
+ */
+ enum ieee80211_conf_flags {
+ IEEE80211_CONF_RADIOTAP = (1<<0),
+ IEEE80211_CONF_PS = (1<<1),
+ IEEE80211_CONF_IDLE = (1<<2),
++ IEEE80211_CONF_QOS = (1<<3),
+ };
+
+
+@@ -599,6 +603,7 @@ enum ieee80211_conf_changed {
+ IEEE80211_CONF_CHANGE_CHANNEL = BIT(6),
+ IEEE80211_CONF_CHANGE_RETRY_LIMITS = BIT(7),
+ IEEE80211_CONF_CHANGE_IDLE = BIT(8),
++ IEEE80211_CONF_CHANGE_QOS = BIT(9),
+ };
+
+ /**
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index 4a15df1..d3950b7 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -786,6 +786,9 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
+ int count;
+ u8 *pos;
+
++ if (!local->ops->conf_tx)
++ return;
++
+ if (!(ifmgd->flags & IEEE80211_STA_WMM_ENABLED))
+ return;
+
+@@ -844,11 +847,15 @@ static void ieee80211_sta_wmm_params(struct ieee80211_local *local,
+ wiphy_name(local->hw.wiphy), queue, aci, acm,
+ params.aifs, params.cw_min, params.cw_max, params.txop);
+ #endif
+- if (drv_conf_tx(local, queue, &params) && local->ops->conf_tx)
++ if (drv_conf_tx(local, queue, &params))
+ printk(KERN_DEBUG "%s: failed to set TX queue "
+ "parameters for queue %d\n",
+ wiphy_name(local->hw.wiphy), queue);
+ }
++
++ /* enable WMM or activate new settings */
++ local->hw.conf.flags |= IEEE80211_CONF_QOS;
++ drv_config(local, IEEE80211_CONF_CHANGE_QOS);
+ }
+
+ static u32 ieee80211_handle_bss_capability(struct ieee80211_sub_if_data *sdata,
+diff --git a/net/mac80211/util.c b/net/mac80211/util.c
+index 31b1085..21f11cc 100644
+--- a/net/mac80211/util.c
++++ b/net/mac80211/util.c
+@@ -791,6 +791,11 @@ void ieee80211_set_wmm_default(struct ieee80211_sub_if_data *sdata)
+
+ drv_conf_tx(local, queue, &qparam);
+ }
++
++ /* after reinitialize QoS TX queues setting to default,
++ * disable QoS at all */
++ local->hw.conf.flags &= ~IEEE80211_CONF_QOS;
++ drv_config(local, IEEE80211_CONF_CHANGE_QOS);
+ }
+
+ void ieee80211_sta_def_wmm_params(struct ieee80211_sub_if_data *sdata,
+--
+1.6.2.5
+
+_______________________________________________
+kernel mailing list
+kernel@lists.fedoraproject.org
+https://admin.fedoraproject.org/mailman/listinfo/kernel
+
diff --git a/iwlwifi-recover_from_tx_stall.patch b/iwlwifi-recover_from_tx_stall.patch
new file mode 100644
index 0000000..0b69e44
--- /dev/null
+++ b/iwlwifi-recover_from_tx_stall.patch
@@ -0,0 +1,13 @@
+https://bugzilla.redhat.com/show_bug.cgi?id=589777#c5
+
+diff -up linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c.orig linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c
+--- linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c.orig 2010-05-19 16:07:15.000000000 -0400
++++ linux-2.6.33.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c 2010-05-19 16:09:42.000000000 -0400
+@@ -2794,6 +2794,7 @@ static struct iwl_lib_ops iwl3945_lib =
+ .post_associate = iwl3945_post_associate,
+ .isr = iwl_isr_legacy,
+ .config_ap = iwl3945_config_ap,
++ .recover_from_tx_stall = iwl_bg_monitor_recover,
+ };
+
+ static struct iwl_hcmd_utils_ops iwl3945_hcmd_utils = {
diff --git a/iwlwifi-reset-card-during-probe.patch b/iwlwifi-reset-card-during-probe.patch
new file mode 100644
index 0000000..74345dc
--- /dev/null
+++ b/iwlwifi-reset-card-during-probe.patch
@@ -0,0 +1,167 @@
+From linville@redhat.com Mon Mar 29 14:49:37 2010
+Return-path: <linville@redhat.com>
+Envelope-to: linville@tuxdriver.com
+Delivery-date: Mon, 29 Mar 2010 14:49:37 -0400
+Received: from mx1.redhat.com ([209.132.183.28])
+ by smtp.tuxdriver.com with esmtp (Exim 4.63)
+ (envelope-from <linville@redhat.com>)
+ id 1NwK1n-0004Zz-SW
+ for linville@tuxdriver.com; Mon, 29 Mar 2010 14:49:37 -0400
+Received: from int-mx04.intmail.prod.int.phx2.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.17])
+ by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o2TInYO7028996
+ (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK)
+ for <linville@tuxdriver.com>; Mon, 29 Mar 2010 14:49:35 -0400
+Received: from savage.usersys.redhat.com (savage.devel.redhat.com [10.11.231.4])
+ by int-mx04.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o2TInX27023483
+ (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO)
+ for <linville@tuxdriver.com>; Mon, 29 Mar 2010 14:49:33 -0400
+Received: from savage.usersys.redhat.com (localhost.localdomain [127.0.0.1])
+ by savage.usersys.redhat.com (8.13.1/8.13.1) with ESMTP id o2TInXPn000652
+ for <linville@tuxdriver.com>; Mon, 29 Mar 2010 14:49:33 -0400
+Received: (from linville@localhost)
+ by savage.usersys.redhat.com (8.13.1/8.13.1/Submit) id o2TInWt7000651
+ for linville@tuxdriver.com; Mon, 29 Mar 2010 14:49:32 -0400
+Resent-Message-Id: <201003291849.o2TInWt7000651@savage.usersys.redhat.com>
+Received: from zmta03.collab.prod.int.phx2.redhat.com (LHLO
+ zmta03.collab.prod.int.phx2.redhat.com) (10.5.5.33) by
+ mail03.corp.redhat.com with LMTP; Fri, 26 Mar 2010 06:05:51 -0400 (EDT)
+Received: from localhost (localhost.localdomain [127.0.0.1])
+ by zmta03.collab.prod.int.phx2.redhat.com (Postfix) with ESMTP id 038004CBE9;
+ Fri, 26 Mar 2010 06:05:51 -0400 (EDT)
+Received: from zmta03.collab.prod.int.phx2.redhat.com ([127.0.0.1])
+ by localhost (zmta03.collab.prod.int.phx2.redhat.com [127.0.0.1]) (amavisd-new, port 10024)
+ with ESMTP id IVjBQyibLBw2; Fri, 26 Mar 2010 06:05:50 -0400 (EDT)
+Received: from int-mx04.intmail.prod.int.phx2.redhat.com (int-mx04.intmail.prod.int.phx2.redhat.com [10.5.11.17])
+ by zmta03.collab.prod.int.phx2.redhat.com (Postfix) with ESMTP id BF0144CBE7;
+ Fri, 26 Mar 2010 06:05:50 -0400 (EDT)
+Received: from mx1.redhat.com (ext-mx08.extmail.prod.ext.phx2.redhat.com [10.5.110.12])
+ by int-mx04.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id o2QA5m7L004056;
+ Fri, 26 Mar 2010 06:05:49 -0400
+Received: from bastion.fedoraproject.org (bastion.phx2.fedoraproject.org [10.5.126.11])
+ by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o2QA5bS2028477;
+ Fri, 26 Mar 2010 06:05:37 -0400
+Received: from lists.fedoraproject.org (collab1.vpn.fedoraproject.org [192.168.1.21])
+ by bastion02.phx2.fedoraproject.org (Postfix) with ESMTP id 16EF710F96C;
+ Fri, 26 Mar 2010 10:05:37 +0000 (UTC)
+Received: from collab1.fedoraproject.org (localhost.localdomain [127.0.0.1])
+ by lists.fedoraproject.org (Postfix) with ESMTP id 1C8C93267AC;
+ Fri, 26 Mar 2010 10:05:19 +0000 (UTC)
+X-Original-To: kernel@lists.fedoraproject.org
+Delivered-To: kernel@lists.fedoraproject.org
+Received: from smtp-mm1.fedoraproject.org (smtp-mm1.fedoraproject.org
+ [80.239.156.217])
+ by lists.fedoraproject.org (Postfix) with ESMTP id 5FD26326780
+ for <kernel@lists.fedoraproject.org>;
+ Fri, 26 Mar 2010 10:05:14 +0000 (UTC)
+Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28])
+ by smtp-mm1.fedoraproject.org (Postfix) with ESMTP id 9BB6A87E5F
+ for <kernel@lists.fedoraproject.org>;
+ Fri, 26 Mar 2010 10:05:13 +0000 (UTC)
+Received: from int-mx08.intmail.prod.int.phx2.redhat.com
+ (int-mx08.intmail.prod.int.phx2.redhat.com [10.5.11.21])
+ by mx1.redhat.com (8.13.8/8.13.8) with ESMTP id o2QA5CbS005173
+ (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK);
+ Fri, 26 Mar 2010 06:05:12 -0400
+Received: from localhost (dhcp-0-189.brq.redhat.com [10.34.0.189])
+ by int-mx08.intmail.prod.int.phx2.redhat.com (8.13.8/8.13.8) with ESMTP
+ id o2QA5BKo028563; Fri, 26 Mar 2010 06:05:11 -0400
+From: Stanislaw Gruszka <sgruszka@redhat.com>
+To: kernel@lists.fedoraproject.org
+Subject: [PATCH 2/3] iwlwifi: reset card during probe
+Date: Fri, 26 Mar 2010 11:03:26 +0100
+Message-Id: <1269597807-2925-2-git-send-email-sgruszka@redhat.com>
+In-Reply-To: <1269597807-2925-1-git-send-email-sgruszka@redhat.com>
+References: <1269597807-2925-1-git-send-email-sgruszka@redhat.com>
+X-Scanned-By: MIMEDefang 2.67 on 10.5.11.17
+X-Scanned-By: MIMEDefang 2.67 on 10.5.11.17
+X-Scanned-By: MIMEDefang 2.67 on 10.5.110.12
+X-Scanned-By: MIMEDefang 2.67 on 10.5.11.21
+Cc: Stanislaw Gruszka <sgruszka@redhat.com>,
+ "John W. Linville" <linville@tuxdriver.com>
+X-BeenThere: kernel@lists.fedoraproject.org
+X-Mailman-Version: 2.1.9
+Precedence: list
+List-Id: "Fedora kernel development." <kernel.lists.fedoraproject.org>
+List-Unsubscribe: <https://admin.fedoraproject.org/mailman/listinfo/kernel>,
+ <mailto:kernel-request@lists.fedoraproject.org?subject=unsubscribe>
+List-Archive: <http://lists.fedoraproject.org/pipermail/kernel>
+List-Post: <mailto:kernel@lists.fedoraproject.org>
+List-Help: <mailto:kernel-request@lists.fedoraproject.org?subject=help>
+List-Subscribe: <https://admin.fedoraproject.org/mailman/listinfo/kernel>,
+ <mailto:kernel-request@lists.fedoraproject.org?subject=subscribe>
+MIME-Version: 1.0
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+Sender: kernel-bounces@lists.fedoraproject.org
+Errors-To: kernel-bounces@lists.fedoraproject.org
+X-RedHat-Spam-Score: -0.01 (T_RP_MATCHES_RCVD)
+Resent-From: linville@redhat.com
+Resent-Date: Mon, 29 Mar 2010 14:49:32 -0400
+Resent-To: linville@tuxdriver.com
+X-Spam-Score: -8.8 (--------)
+X-Spam-Status: No
+Content-Length: 2455
+Lines: 61
+
+RHBZ#557084
+
+To ensure that card is in a sane state during probe we add a reset call.
+This change was prompted by users of kdump who was not able to bring up the
+wireless driver in the kdump kernel. The problem here was that the primary
+kernel, which is not running at the time, left the wireless card up and
+running. When the kdump kernel starts it is thus possible to immediately
+receive interrupts from firmware after registering interrupt, but without
+being ready to deal with interrupts from firmware yet.
+
+Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
+---
+ drivers/net/wireless/iwlwifi/iwl-agn.c | 8 ++++++++
+ drivers/net/wireless/iwlwifi/iwl3945-base.c | 7 +++++++
+ 2 files changed, 15 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
+index 921dc4a..1661f3c 100644
+--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
++++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
+@@ -2976,6 +2976,14 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+ * we should init now
+ */
+ spin_lock_init(&priv->reg_lock);
++
++ /*
++ * stop and reset the on-board processor just in case it is in a
++ * strange state ... like being left stranded by a primary kernel
++ * and this is now the kdump kernel trying to start up
++ */
++ iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
++
+ iwl_hw_detect(priv);
+ IWL_INFO(priv, "Detected Intel Wireless WiFi Link %s REV=0x%X\n",
+ priv->cfg->name, priv->hw_rev);
+diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
+index 5f26c93..3726b01 100644
+--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
++++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
+@@ -4032,6 +4032,13 @@ static int iwl3945_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
+ IWL_INFO(priv, "Detected Intel Wireless WiFi Link %s\n",
+ priv->cfg->name);
+
++ /*
++ * stop and reset the on-board processor just in case it is in a
++ * strange state ... like being left stranded by a primary kernel
++ * and this is now the kdump kernel trying to start up
++ */
++ iwl_write32(priv, CSR_RESET, CSR_RESET_REG_FLAG_NEVO_RESET);
++
+ /***********************
+ * 7. Setup Services
+ * ********************/
+--
+1.6.2.5
+
+_______________________________________________
+kernel mailing list
+kernel@lists.fedoraproject.org
+https://admin.fedoraproject.org/mailman/listinfo/kernel
+
+
diff --git a/iwlwifi_-Adjusting-PLCP-error-threshold-for-1000-NIC.patch b/iwlwifi_-Adjusting-PLCP-error-threshold-for-1000-NIC.patch
new file mode 100644
index 0000000..f1adf0f
--- /dev/null
+++ b/iwlwifi_-Adjusting-PLCP-error-threshold-for-1000-NIC.patch
@@ -0,0 +1,38 @@
+Back-port of the following upstream commit...
+
+commit 6c3872e1d52290dcd506473028867cacc6b7393d
+Author: Trieu 'Andrew' Nguyen <trieux.t.nguyen@intel.com>
+Date: Mon Feb 8 13:53:05 2010 -0800
+
+ iwlwifi: Adjusting PLCP error threshold for 1000 NIC
+
+ While testing the station with the NIC 1000 family, it is found that
+ the plcp error can easily exceed 50 value in 100mSecs. This creates
+ unneccessary radio reset/tuning. This patch raises the PLCP error
+ threshold of the NIC 1000 from 50 to 200 error count.
+
+ Signed-off-by: Trieu 'Andrew' Nguyen <trieux.t.nguyen@intel.com>
+ Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
+
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c.orig 2010-03-22 14:23:01.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c 2010-03-22 15:33:38.000000000 -0400
+@@ -162,6 +162,6 @@ struct iwl_cfg iwl1000_bgn_cfg = {
+ .shadow_ram_support = false,
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
+- .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
++ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
+ };
+
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig 2010-03-22 15:24:28.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h 2010-03-22 15:33:00.000000000 -0400
+@@ -970,6 +970,7 @@ struct traffic_stats {
+ #define IWL_MAX_PLCP_ERR_THRESHOLD_MIN (0)
+ #define IWL_MAX_PLCP_ERR_THRESHOLD_DEF (50)
+ #define IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF (100)
++#define IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF (200)
+ #define IWL_MAX_PLCP_ERR_THRESHOLD_MAX (255)
+
+ enum iwl_reset {
diff --git a/iwlwifi_-Logic-to-control-how-frequent-radio-should-be-reset-if-needed.patch b/iwlwifi_-Logic-to-control-how-frequent-radio-should-be-reset-if-needed.patch
new file mode 100644
index 0000000..e9f1623
--- /dev/null
+++ b/iwlwifi_-Logic-to-control-how-frequent-radio-should-be-reset-if-needed.patch
@@ -0,0 +1,82 @@
+Back-port of the following upstream commit...
+
+commit d4d59e88cb746165c6fe33eacb6f582d525c6ef1
+Author: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+Date: Fri Jan 22 14:22:45 2010 -0800
+
+ iwlwifi: Logic to control how frequent radio should be reset if needed
+
+ Add additional logic for internal scan routine to control how
+ frequent this function should be performed.
+
+ The intent of this function is to reset/re-tune the radio and bring the
+ RF/PHY back to normal state, it does not make sense calling it too
+ frequent,
+ if reset the radio can not bring it back to normal state, it indicate
+ there are other reason to cause the radio not operate correctly.
+
+ Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+ Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
+ Signed-off-by: John W. Linville <linville@tuxdriver.com>
+
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig 2010-03-22 11:26:18.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h 2010-03-22 14:12:32.000000000 -0400
+@@ -1013,6 +1013,7 @@ struct iwl_priv {
+ unsigned long scan_start;
+ unsigned long scan_pass_start;
+ unsigned long scan_start_tsf;
++ unsigned long last_internal_scan_jiffies;
+ void *scan;
+ int scan_bands;
+ struct cfg80211_scan_request *scan_request;
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c.orig 2010-03-22 11:26:18.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c 2010-03-22 14:15:28.000000000 -0400
+@@ -206,7 +206,8 @@ static void iwl_rx_scan_results_notif(st
+ #endif
+
+ priv->last_scan_jiffies = jiffies;
+- priv->next_scan_jiffies = 0;
++ if (!priv->is_internal_short_scan)
++ priv->next_scan_jiffies = 0;
+ }
+
+ /* Service SCAN_COMPLETE_NOTIFICATION (0x84) */
+@@ -252,8 +253,11 @@ static void iwl_rx_scan_complete_notif(s
+ goto reschedule;
+ }
+
+- priv->last_scan_jiffies = jiffies;
+- priv->next_scan_jiffies = 0;
++ if (!priv->is_internal_short_scan)
++ priv->next_scan_jiffies = 0;
++ else
++ priv->last_internal_scan_jiffies = jiffies;
++
+ IWL_DEBUG_INFO(priv, "Setting scan to off\n");
+
+ clear_bit(STATUS_SCANNING, &priv->status);
+@@ -560,6 +564,8 @@ EXPORT_SYMBOL(iwl_mac_hw_scan);
+ * internal short scan, this function should only been called while associated.
+ * It will reset and tune the radio to prevent possible RF related problem
+ */
++#define IWL_DELAY_NEXT_INTERNAL_SCAN (HZ*1)
++
+ int iwl_internal_short_hw_scan(struct iwl_priv *priv)
+ {
+ int ret = 0;
+@@ -579,6 +585,13 @@ int iwl_internal_short_hw_scan(struct iw
+ ret = -EAGAIN;
+ goto out;
+ }
++ if (priv->last_internal_scan_jiffies &&
++ time_after(priv->last_internal_scan_jiffies +
++ IWL_DELAY_NEXT_INTERNAL_SCAN, jiffies)) {
++ IWL_DEBUG_SCAN(priv, "internal scan rejected\n");
++ goto out;
++ }
++
+ priv->scan_bands = 0;
+ if (priv->band == IEEE80211_BAND_5GHZ)
+ priv->scan_bands |= BIT(IEEE80211_BAND_5GHZ);
diff --git a/iwlwifi_-Recover-TX-flow-failure.patch b/iwlwifi_-Recover-TX-flow-failure.patch
new file mode 100644
index 0000000..52bdd9f
--- /dev/null
+++ b/iwlwifi_-Recover-TX-flow-failure.patch
@@ -0,0 +1,137 @@
+This patch is not upstream yet...
+
+From 34c75818bfcd65e54fed9fe852fc41aba8cf233d Mon Sep 17 00:00:00 2001
+From: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+Date: Thu, 4 Mar 2010 13:38:59 -0800
+Subject: [PATCH 15/17] iwlwifi: Recover TX flow failure
+
+Monitors the tx statistics to detect the drop in throughput.
+When the throughput drops, the ratio of the actual_ack_count and the
+expected_ack_count also drops. At the same time, the aggregated
+ba_timeout (the number of ba timeout retries) also rises. If the
+actual_ack_count/expected_ack_count ratio is 0 and the number of ba
+timeout retries rises to BA_TIMEOUT_MAX, no tx packets can be delivered.
+Reloading the uCode and bring the system back to normal operational
+state.
+
+Signed-off-by: Trieu 'Andrew' Nguyen <trieux.t.nguyen@intel.com>
+Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
+
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c.orig 2010-03-22 15:48:54.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c 2010-03-22 16:06:01.000000000 -0400
+@@ -2559,10 +2559,21 @@ static int iwl_mac_ampdu_action(struct i
+ return ret;
+ case IEEE80211_AMPDU_TX_START:
+ IWL_DEBUG_HT(priv, "start Tx\n");
+- return iwl_tx_agg_start(priv, sta->addr, tid, ssn);
++ ret = iwl_tx_agg_start(priv, sta->addr, tid, ssn);
++ if (ret == 0) {
++ priv->_agn.agg_tids_count++;
++ IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n",
++ priv->_agn.agg_tids_count);
++ }
++ return ret;
+ case IEEE80211_AMPDU_TX_STOP:
+ IWL_DEBUG_HT(priv, "stop Tx\n");
+ ret = iwl_tx_agg_stop(priv, sta->addr, tid);
++ if ((ret == 0) && (priv->_agn.agg_tids_count > 0)) {
++ priv->_agn.agg_tids_count--;
++ IWL_DEBUG_HT(priv, "priv->_agn.agg_tids_count = %u\n",
++ priv->_agn.agg_tids_count);
++ }
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return 0;
+ else
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig 2010-03-22 16:08:56.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c 2010-03-22 16:08:53.000000000 -0400
+@@ -1494,6 +1494,7 @@ int iwl_init_drv(struct iwl_priv *priv)
+ priv->band = IEEE80211_BAND_2GHZ;
+
+ priv->iw_mode = NL80211_IFTYPE_STATION;
++ priv->_agn.agg_tids_count = 0;
+
+ priv->current_ht_config.sm_ps = WLAN_HT_CAP_SM_PS_DISABLED;
+
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig 2010-03-22 15:48:54.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h 2010-03-22 16:06:01.000000000 -0400
+@@ -1206,6 +1206,16 @@ struct iwl_priv {
+ u16 beacon_int;
+ struct ieee80211_vif *vif;
+
++ union {
++ struct {
++ /*
++ * reporting the number of tids has AGG on. 0 means
++ * no AGGREGATION
++ */
++ u8 agg_tids_count;
++ } _agn;
++ };
++
+ /*Added for 3945 */
+ void *shared_virt;
+ dma_addr_t shared_phys;
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c.orig 2010-03-22 16:02:35.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c 2010-03-22 16:06:01.000000000 -0400
+@@ -550,9 +550,18 @@ static void iwl_rx_calc_noise(struct iwl
+
+ #define REG_RECALIB_PERIOD (60)
+
++/* the threshold ratio of actual_ack_cnt to expected_ack_cnt in percent */
++#define ACK_CNT_RATIO (50)
++#define BA_TIMEOUT_CNT (5)
++#define BA_TIMEOUT_MAX (16)
++
+ #define PLCP_MSG "plcp_err exceeded %u, %u, %u, %u, %u, %d, %u mSecs\n"
+ /*
+- * This function checks for plcp error.
++ * This function checks for plcp error, ACK count ratios, aggregated BA
++ * timeout retries.
++ * - When the ACK count ratio is 0 and aggregated BA timeout retries is
++ * exceeding the BA_TIMEOUT_MAX, it will recover the failure by resetting
++ * the firmware.
+ * - When the plcp error is exceeding the thresholds, it will reset the radio
+ * to improve the throughput.
+ */
+@@ -562,6 +571,36 @@ void iwl_recover_from_statistics(struct
+ int combined_plcp_delta;
+ unsigned int plcp_msec;
+ unsigned long plcp_received_jiffies;
++ int actual_ack_cnt_delta;
++ int expected_ack_cnt_delta;
++ int ba_timeout_delta;
++
++ actual_ack_cnt_delta =
++ le32_to_cpu(pkt->u.stats.tx.actual_ack_cnt) -
++ le32_to_cpu(priv->statistics.tx.actual_ack_cnt);
++ expected_ack_cnt_delta =
++ le32_to_cpu(pkt->u.stats.tx.expected_ack_cnt) -
++ le32_to_cpu(priv->statistics.tx.expected_ack_cnt);
++ ba_timeout_delta =
++ le32_to_cpu(pkt->u.stats.tx.agg.ba_timeout) -
++ le32_to_cpu(priv->statistics.tx.agg.ba_timeout);
++ if ((priv->_agn.agg_tids_count > 0) &&
++ (expected_ack_cnt_delta > 0) &&
++ (((actual_ack_cnt_delta * 100) / expected_ack_cnt_delta)
++ < ACK_CNT_RATIO) &&
++ (ba_timeout_delta > BA_TIMEOUT_CNT)) {
++ IWL_DEBUG_RADIO(priv, "actual_ack_cnt delta = %d,"
++ " expected_ack_cnt = %d\n",
++ actual_ack_cnt_delta, expected_ack_cnt_delta);
++ IWL_DEBUG_RADIO(priv, "agg ba_timeout delta = %d\n",
++ ba_timeout_delta);
++ if ((actual_ack_cnt_delta == 0) &&
++ (ba_timeout_delta >= BA_TIMEOUT_MAX)) {
++ IWL_DEBUG_RADIO(priv,
++ "call iwl_force_reset(IWL_FW_RESET)\n");
++ iwl_force_reset(priv, IWL_FW_RESET);
++ }
++ }
+
+ /*
+ * check for plcp_err and trigger radio reset if it exceeds
diff --git a/iwlwifi_-Recover-TX-flow-stall-due-to-stuck-queue.patch b/iwlwifi_-Recover-TX-flow-stall-due-to-stuck-queue.patch
new file mode 100644
index 0000000..e3b4b5c
--- /dev/null
+++ b/iwlwifi_-Recover-TX-flow-stall-due-to-stuck-queue.patch
@@ -0,0 +1,446 @@
+This patch is not yet upstream...
+
+From a5e660b4e294556822913627544f661e59b39716 Mon Sep 17 00:00:00 2001
+From: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+Date: Mon, 1 Mar 2010 17:23:50 -0800
+Subject: [PATCH 13/17] iwlwifi: Recover TX flow stall due to stuck queue
+
+Monitors the internal TX queues periodically. When a queue is stuck
+for some unknown conditions causing the throughput to drop and the
+transfer is stop, the driver will force firmware reload and bring the
+system back to normal operational state.
+
+The iwlwifi devices behave differently in this regard so this feature is
+made part of the ops infrastructure so we can have more control on how to
+monitor and recover from tx queue stall case per device.
+
+Signed-off-by: Trieu 'Andrew' Nguyen <trieux.t.nguyen@intel.com>
+Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
+
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c.orig 2010-03-22 15:33:38.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c 2010-03-22 15:48:54.000000000 -0400
+@@ -135,6 +135,7 @@ static struct iwl_lib_ops iwl1000_lib =
+ .temperature = iwl5000_temperature,
+ .set_ct_kill = iwl1000_set_ct_threshold,
+ },
++ .recover_from_tx_stall = iwl_bg_monitor_recover,
+ };
+
+ static struct iwl_ops iwl1000_ops = {
+@@ -163,5 +164,6 @@ struct iwl_cfg iwl1000_bgn_cfg = {
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF,
++ .monitor_recover_period = IWL_MONITORING_PERIOD,
+ };
+
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl3945-base.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl3945-base.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl3945-base.c.orig 2010-03-22 15:44:04.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl3945-base.c 2010-03-22 15:48:54.000000000 -0400
+@@ -2453,6 +2453,13 @@ static void iwl3945_alive_start(struct i
+ /* After the ALIVE response, we can send commands to 3945 uCode */
+ set_bit(STATUS_ALIVE, &priv->status);
+
++ if (priv->cfg->ops->lib->recover_from_tx_stall) {
++ /* Enable timer to monitor the driver queues */
++ mod_timer(&priv->monitor_recover,
++ jiffies +
++ msecs_to_jiffies(priv->cfg->monitor_recover_period));
++ }
++
+ if (iwl_is_rfkill(priv))
+ return;
+
+@@ -3730,6 +3737,13 @@ static void iwl3945_setup_deferred_work(
+
+ iwl3945_hw_setup_deferred_work(priv);
+
++ if (priv->cfg->ops->lib->recover_from_tx_stall) {
++ init_timer(&priv->monitor_recover);
++ priv->monitor_recover.data = (unsigned long)priv;
++ priv->monitor_recover.function =
++ priv->cfg->ops->lib->recover_from_tx_stall;
++ }
++
+ tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
+ iwl3945_irq_tasklet, (unsigned long)priv);
+ }
+@@ -3742,6 +3756,8 @@ static void iwl3945_cancel_deferred_work
+ cancel_delayed_work(&priv->scan_check);
+ cancel_delayed_work(&priv->alive_start);
+ cancel_work_sync(&priv->beacon_update);
++ if (priv->cfg->ops->lib->recover_from_tx_stall)
++ del_timer_sync(&priv->monitor_recover);
+ }
+
+ static struct attribute *iwl3945_sysfs_entries[] = {
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c.orig 2010-03-22 14:20:28.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c 2010-03-22 15:48:54.000000000 -0400
+@@ -2897,6 +2897,7 @@ static struct iwl_cfg iwl3945_bg_cfg = {
+ .ht_greenfield_support = false,
+ .broken_powersave = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
++ .monitor_recover_period = IWL_MONITORING_PERIOD,
+ };
+
+ static struct iwl_cfg iwl3945_abg_cfg = {
+@@ -2913,6 +2914,7 @@ static struct iwl_cfg iwl3945_abg_cfg =
+ .ht_greenfield_support = false,
+ .broken_powersave = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
++ .monitor_recover_period = IWL_MONITORING_PERIOD,
+ };
+
+ struct pci_device_id iwl3945_hw_card_ids[] = {
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c.orig 2010-03-22 14:24:14.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c 2010-03-22 15:48:54.000000000 -0400
+@@ -2364,6 +2364,7 @@ struct iwl_cfg iwl4965_agn_cfg = {
+ .ht_greenfield_support = false,
+ .broken_powersave = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
++ .monitor_recover_period = IWL_MONITORING_PERIOD,
+ };
+
+ /* Module firmware */
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c.orig 2010-03-22 14:27:05.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c 2010-03-22 15:48:54.000000000 -0400
+@@ -1579,6 +1579,7 @@ struct iwl_lib_ops iwl5000_lib = {
+ .temperature = iwl5000_temperature,
+ .set_ct_kill = iwl5000_set_ct_threshold,
+ },
++ .recover_from_tx_stall = iwl_bg_monitor_recover,
+ };
+
+ static struct iwl_lib_ops iwl5150_lib = {
+@@ -1631,6 +1632,7 @@ static struct iwl_lib_ops iwl5150_lib =
+ .temperature = iwl5150_temperature,
+ .set_ct_kill = iwl5150_set_ct_threshold,
+ },
++ .recover_from_tx_stall = iwl_bg_monitor_recover,
+ };
+
+ struct iwl_ops iwl5000_ops = {
+@@ -1673,6 +1675,7 @@ struct iwl_cfg iwl5300_agn_cfg = {
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
++ .monitor_recover_period = IWL_MONITORING_PERIOD,
+ };
+
+ struct iwl_cfg iwl5100_bg_cfg = {
+@@ -1691,6 +1694,7 @@ struct iwl_cfg iwl5100_bg_cfg = {
+ .need_pll_cfg = true,
+ .ht_greenfield_support = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
++ .monitor_recover_period = IWL_MONITORING_PERIOD,
+ };
+
+ struct iwl_cfg iwl5100_abg_cfg = {
+@@ -1709,6 +1713,7 @@ struct iwl_cfg iwl5100_abg_cfg = {
+ .valid_rx_ant = ANT_AB,
+ .need_pll_cfg = true,
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
++ .monitor_recover_period = IWL_MONITORING_PERIOD,
+ };
+
+ struct iwl_cfg iwl5100_agn_cfg = {
+@@ -1728,6 +1733,7 @@ struct iwl_cfg iwl5100_agn_cfg = {
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
++ .monitor_recover_period = IWL_MONITORING_PERIOD,
+ };
+
+ struct iwl_cfg iwl5350_agn_cfg = {
+@@ -1747,6 +1753,7 @@ struct iwl_cfg iwl5350_agn_cfg = {
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
++ .monitor_recover_period = IWL_MONITORING_PERIOD,
+ };
+
+ struct iwl_cfg iwl5150_agn_cfg = {
+@@ -1766,6 +1773,7 @@ struct iwl_cfg iwl5150_agn_cfg = {
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
++ .monitor_recover_period = IWL_MONITORING_PERIOD,
+ };
+
+ MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX));
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c.orig 2010-03-22 14:28:04.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c 2010-03-22 15:51:12.000000000 -0400
+@@ -137,6 +137,7 @@ static struct iwl_lib_ops iwl6000_lib =
+ .temperature = iwl5000_temperature,
+ .set_ct_kill = iwl6000_set_ct_threshold,
+ },
++ .recover_from_tx_stall = iwl_bg_monitor_recover,
+ };
+
+ static struct iwl_hcmd_utils_ops iwl6000_hcmd_utils = {
+@@ -177,6 +178,7 @@ struct iwl_cfg iwl6000h_2agn_cfg = {
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
++ .monitor_recover_period = IWL_MONITORING_PERIOD,
+ };
+
+ /*
+@@ -202,6 +204,7 @@ struct iwl_cfg iwl6000i_2agn_cfg = {
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
++ .monitor_recover_period = IWL_MONITORING_PERIOD,
+ };
+
+ struct iwl_cfg iwl6050_2agn_cfg = {
+@@ -224,6 +227,7 @@ struct iwl_cfg iwl6050_2agn_cfg = {
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
++ .monitor_recover_period = IWL_MONITORING_PERIOD,
+ };
+
+ struct iwl_cfg iwl6000_3agn_cfg = {
+@@ -246,6 +250,7 @@ struct iwl_cfg iwl6000_3agn_cfg = {
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
++ .monitor_recover_period = IWL_MONITORING_PERIOD,
+ };
+
+ struct iwl_cfg iwl6050_3agn_cfg = {
+@@ -268,6 +273,7 @@ struct iwl_cfg iwl6050_3agn_cfg = {
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
+ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
++ .monitor_recover_period = IWL_MONITORING_PERIOD,
+ };
+
+ MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX));
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c.orig 2009-12-02 22:51:21.000000000 -0500
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c 2010-03-22 15:48:54.000000000 -0400
+@@ -1755,6 +1755,13 @@ static void iwl_alive_start(struct iwl_p
+ /* After the ALIVE response, we can send host commands to the uCode */
+ set_bit(STATUS_ALIVE, &priv->status);
+
++ if (priv->cfg->ops->lib->recover_from_tx_stall) {
++ /* Enable timer to monitor the driver queues */
++ mod_timer(&priv->monitor_recover,
++ jiffies +
++ msecs_to_jiffies(priv->cfg->monitor_recover_period));
++ }
++
+ if (iwl_is_rfkill(priv))
+ return;
+
+@@ -2829,6 +2836,13 @@ static void iwl_setup_deferred_work(stru
+ priv->statistics_periodic.data = (unsigned long)priv;
+ priv->statistics_periodic.function = iwl_bg_statistics_periodic;
+
++ if (priv->cfg->ops->lib->recover_from_tx_stall) {
++ init_timer(&priv->monitor_recover);
++ priv->monitor_recover.data = (unsigned long)priv;
++ priv->monitor_recover.function =
++ priv->cfg->ops->lib->recover_from_tx_stall;
++ }
++
+ if (!priv->cfg->use_isr_legacy)
+ tasklet_init(&priv->irq_tasklet, (void (*)(unsigned long))
+ iwl_irq_tasklet, (unsigned long)priv);
+@@ -2847,6 +2861,8 @@ static void iwl_cancel_deferred_work(str
+ cancel_delayed_work(&priv->alive_start);
+ cancel_work_sync(&priv->beacon_update);
+ del_timer_sync(&priv->statistics_periodic);
++ if (priv->cfg->ops->lib->recover_from_tx_stall)
++ del_timer_sync(&priv->monitor_recover);
+ }
+
+ static struct attribute *iwl_sysfs_entries[] = {
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig 2010-03-22 15:40:48.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c 2010-03-22 15:48:54.000000000 -0400
+@@ -3107,6 +3107,99 @@ int iwl_force_reset(struct iwl_priv *pri
+ }
+ return 0;
+ }
++EXPORT_SYMBOL(iwl_force_reset);
++
++/**
++ * iwl_bg_monitor_recover - Timer callback to check for stuck queue and recover
++ *
++ * During normal condition (no queue is stuck), the timer is continually set to
++ * execute every monitor_recover_period milliseconds after the last timer
++ * expired. When the queue read_ptr is at the same place, the timer is
++ * shorten to 100mSecs. This is
++ * 1) to reduce the chance that the read_ptr may wrap around (not stuck)
++ * 2) to detect the stuck queues quicker before the station and AP can
++ * disassociate each other.
++ *
++ * This function monitors all the tx queues and recover from it if any
++ * of the queues are stuck.
++ * 1. It first check the cmd queue for stuck conditions. If it is stuck,
++ * it will recover by resetting the firmware and return.
++ * 2. Then, it checks for station association. If it associates it will check
++ * other queues. If any queue is stuck, it will recover by resetting
++ * the firmware.
++ * Note: It the number of times the queue read_ptr to be at the same place to
++ * be MAX_REPEAT+1 in order to consider to be stuck.
++ */
++/*
++ * The maximum number of times the read pointer of the tx queue at the
++ * same place without considering to be stuck.
++ */
++#define MAX_REPEAT (2)
++static int iwl_check_stuck_queue(struct iwl_priv *priv, int cnt)
++{
++ struct iwl_tx_queue *txq;
++ struct iwl_queue *q;
++
++ txq = &priv->txq[cnt];
++ q = &txq->q;
++ /* queue is empty, skip */
++ if (q->read_ptr != q->write_ptr) {
++ if (q->read_ptr == q->last_read_ptr) {
++ /* a queue has not been read from last time */
++ if (q->repeat_same_read_ptr > MAX_REPEAT) {
++ IWL_ERR(priv,
++ "queue %d stuck %d time. Fw reload.\n",
++ q->id, q->repeat_same_read_ptr);
++ q->repeat_same_read_ptr = 0;
++ iwl_force_reset(priv, IWL_FW_RESET);
++ } else {
++ q->repeat_same_read_ptr++;
++ IWL_DEBUG_RADIO(priv,
++ "queue %d, not read %d time\n",
++ q->id,
++ q->repeat_same_read_ptr);
++ mod_timer(&priv->monitor_recover, jiffies +
++ msecs_to_jiffies(IWL_ONE_HUNDRED_MSECS));
++ }
++ return 1;
++ } else {
++ q->last_read_ptr = q->read_ptr;
++ q->repeat_same_read_ptr = 0;
++ }
++ }
++ return 0;
++}
++
++void iwl_bg_monitor_recover(unsigned long data)
++{
++ struct iwl_priv *priv = (struct iwl_priv *)data;
++ int cnt;
++
++ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
++ return;
++
++ /* monitor and check for stuck cmd queue */
++ if (iwl_check_stuck_queue(priv, IWL_CMD_QUEUE_NUM))
++ return;
++
++ /* monitor and check for other stuck queues */
++ if (iwl_is_associated(priv)) {
++ for (cnt = 0; cnt < priv->hw_params.max_txq_num; cnt++) {
++ /* skip as we already checked the command queue */
++ if (cnt == IWL_CMD_QUEUE_NUM)
++ continue;
++ if (iwl_check_stuck_queue(priv, cnt))
++ return;
++ }
++ }
++ /*
++ * Reschedule the timer to occur in
++ * priv->cfg->monitor_recover_period
++ */
++ mod_timer(&priv->monitor_recover,
++ jiffies + msecs_to_jiffies(priv->cfg->monitor_recover_period));
++}
++EXPORT_SYMBOL(iwl_bg_monitor_recover);
+
+ #ifdef CONFIG_PM
+
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig 2010-03-22 15:24:28.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h 2010-03-22 15:48:54.000000000 -0400
+@@ -183,6 +183,8 @@ struct iwl_lib_ops {
+
+ /* temperature */
+ struct iwl_temp_ops temp_ops;
++ /* recover from tx queue stall */
++ void (*recover_from_tx_stall)(unsigned long data);
+ };
+
+ struct iwl_ops {
+@@ -260,6 +262,8 @@ struct iwl_cfg {
+ const bool broken_powersave;
+ bool use_rts_for_ht;
+ u8 plcp_delta_threshold;
++ /* timer period for monitor the driver queues */
++ u32 monitor_recover_period;
+ };
+
+ /***************************
+@@ -543,6 +547,9 @@ static inline u16 iwl_pcie_link_ctl(stru
+ pci_read_config_word(priv->pci_dev, pos + PCI_EXP_LNKCTL, &pci_lnk_ctl);
+ return pci_lnk_ctl;
+ }
++
++void iwl_bg_monitor_recover(unsigned long data);
++
+ #ifdef CONFIG_PM
+ int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state);
+ int iwl_pci_resume(struct pci_dev *pdev);
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig 2010-03-22 15:37:04.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h 2010-03-22 15:48:54.000000000 -0400
+@@ -184,6 +184,10 @@ struct iwl_queue {
+ int n_bd; /* number of BDs in this queue */
+ int write_ptr; /* 1-st empty entry (index) host_w*/
+ int read_ptr; /* last used entry (index) host_r*/
++ /* use for monitoring and recovering the stuck queue */
++ int last_read_ptr; /* storing the last read_ptr */
++ /* number of time read_ptr and last_read_ptr are the same */
++ u8 repeat_same_read_ptr;
+ dma_addr_t dma_addr; /* physical addr for BD's */
+ int n_window; /* safe queue window */
+ u32 id;
+@@ -976,6 +980,11 @@ struct traffic_stats {
+ #define IWL_DELAY_NEXT_FORCE_RF_RESET (HZ*3)
+ #define IWL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5)
+
++/* timer constants use to monitor and recover stuck tx queues in mSecs */
++#define IWL_MONITORING_PERIOD (1000)
++#define IWL_ONE_HUNDRED_MSECS (100)
++#define IWL_SIXTY_SECS (60000)
++
+ enum iwl_reset {
+ IWL_RF_RESET = 0,
+ IWL_FW_RESET,
+@@ -1275,6 +1284,7 @@ struct iwl_priv {
+ u32 disable_tx_power_cal;
+ struct work_struct run_time_calib_work;
+ struct timer_list statistics_periodic;
++ struct timer_list monitor_recover;
+ bool hw_ready;
+ /*For 3945*/
+ #define IWL_DEFAULT_TX_POWER 0x0F
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-tx.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-tx.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-tx.c.orig 2010-03-22 11:07:02.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-tx.c 2010-03-22 15:48:54.000000000 -0400
+@@ -291,6 +291,8 @@ static int iwl_queue_init(struct iwl_pri
+ q->high_mark = 2;
+
+ q->write_ptr = q->read_ptr = 0;
++ q->last_read_ptr = 0;
++ q->repeat_same_read_ptr = 0;
+
+ return 0;
+ }
diff --git a/iwlwifi_-Tune-radio-to-prevent-unexpected-behavior.patch b/iwlwifi_-Tune-radio-to-prevent-unexpected-behavior.patch
new file mode 100644
index 0000000..1e0189e
--- /dev/null
+++ b/iwlwifi_-Tune-radio-to-prevent-unexpected-behavior.patch
@@ -0,0 +1,369 @@
+Back-port of the following upstream commit...
+
+commit 3e4fb5faefb57824f2e42305b3d5907845af978c
+Author: Trieu 'Andrew' Nguyen <trieux.t.nguyen@intel.com>
+Date: Fri Jan 22 14:22:46 2010 -0800
+
+ iwlwifi: Tune radio to prevent unexpected behavior
+
+ We have seen the throughput dropped due to external noisy environment
+ and the radio is out of tune. There are lot of plcp errors indicating
+ this condition. Eventually the station can get de-authenticated by the
+ Access Point. By resetting and tuning the radio, the plcp errors are
+ reduced or eliminated and the throughput starts to rise.
+
+ To prevent unexpected behavior such as drop in throughput or deauthentication,
+ - The change provides the driver feature to monitor and tune the radio base on
+ the statistics notification from the uCode.
+ - It also allows the setting of the plcp error rate threshold via
+ the plcp_delta under debugfs interface.
+
+ Signed-off-by: Trieu 'Andrew' Nguyen <trieux.t.nguyen@intel.com>
+ Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
+ Signed-off-by: John W. Linville <linville@tuxdriver.com>
+
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c.orig 2009-12-02 22:51:21.000000000 -0500
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c 2010-03-22 14:23:01.000000000 -0400
+@@ -162,5 +162,6 @@ struct iwl_cfg iwl1000_bgn_cfg = {
+ .shadow_ram_support = false,
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
++ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ };
+
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c.orig 2010-03-22 10:23:59.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-3945.c 2010-03-22 14:20:28.000000000 -0400
+@@ -2896,6 +2896,7 @@ static struct iwl_cfg iwl3945_bg_cfg = {
+ .use_isr_legacy = true,
+ .ht_greenfield_support = false,
+ .broken_powersave = true,
++ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ };
+
+ static struct iwl_cfg iwl3945_abg_cfg = {
+@@ -2911,6 +2912,7 @@ static struct iwl_cfg iwl3945_abg_cfg =
+ .use_isr_legacy = true,
+ .ht_greenfield_support = false,
+ .broken_powersave = true,
++ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ };
+
+ struct pci_device_id iwl3945_hw_card_ids[] = {
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c.orig 2010-03-22 11:22:14.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c 2010-03-22 14:24:14.000000000 -0400
+@@ -2363,6 +2363,7 @@ struct iwl_cfg iwl4965_agn_cfg = {
+ .use_isr_legacy = true,
+ .ht_greenfield_support = false,
+ .broken_powersave = true,
++ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ };
+
+ /* Module firmware */
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c.orig 2010-03-22 11:22:14.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c 2010-03-22 14:27:05.000000000 -0400
+@@ -1672,6 +1672,7 @@ struct iwl_cfg iwl5300_agn_cfg = {
+ .need_pll_cfg = true,
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
++ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+ };
+
+ struct iwl_cfg iwl5100_bg_cfg = {
+@@ -1689,6 +1690,7 @@ struct iwl_cfg iwl5100_bg_cfg = {
+ .valid_rx_ant = ANT_AB,
+ .need_pll_cfg = true,
+ .ht_greenfield_support = true,
++ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+ };
+
+ struct iwl_cfg iwl5100_abg_cfg = {
+@@ -1706,6 +1708,7 @@ struct iwl_cfg iwl5100_abg_cfg = {
+ .valid_tx_ant = ANT_B,
+ .valid_rx_ant = ANT_AB,
+ .need_pll_cfg = true,
++ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+ };
+
+ struct iwl_cfg iwl5100_agn_cfg = {
+@@ -1724,6 +1727,7 @@ struct iwl_cfg iwl5100_agn_cfg = {
+ .need_pll_cfg = true,
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
++ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+ };
+
+ struct iwl_cfg iwl5350_agn_cfg = {
+@@ -1742,6 +1746,7 @@ struct iwl_cfg iwl5350_agn_cfg = {
+ .need_pll_cfg = true,
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
++ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+ };
+
+ struct iwl_cfg iwl5150_agn_cfg = {
+@@ -1760,6 +1765,7 @@ struct iwl_cfg iwl5150_agn_cfg = {
+ .need_pll_cfg = true,
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
++ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF,
+ };
+
+ MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX));
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c.orig 2009-12-02 22:51:21.000000000 -0500
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c 2010-03-22 14:28:04.000000000 -0400
+@@ -176,6 +176,7 @@ struct iwl_cfg iwl6000h_2agn_cfg = {
+ .shadow_ram_support = true,
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
++ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ };
+
+ /*
+@@ -200,6 +201,7 @@ struct iwl_cfg iwl6000i_2agn_cfg = {
+ .shadow_ram_support = true,
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
++ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ };
+
+ struct iwl_cfg iwl6050_2agn_cfg = {
+@@ -221,6 +223,7 @@ struct iwl_cfg iwl6050_2agn_cfg = {
+ .shadow_ram_support = true,
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
++ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ };
+
+ struct iwl_cfg iwl6000_3agn_cfg = {
+@@ -242,6 +245,7 @@ struct iwl_cfg iwl6000_3agn_cfg = {
+ .shadow_ram_support = true,
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
++ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ };
+
+ struct iwl_cfg iwl6050_3agn_cfg = {
+@@ -263,6 +267,7 @@ struct iwl_cfg iwl6050_3agn_cfg = {
+ .shadow_ram_support = true,
+ .ht_greenfield_support = true,
+ .use_rts_for_ht = true, /* use rts/cts protection */
++ .plcp_delta_threshold = IWL_MAX_PLCP_ERR_THRESHOLD_DEF,
+ };
+
+ MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX));
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig 2010-03-22 11:26:18.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h 2010-03-22 14:29:11.000000000 -0400
+@@ -214,6 +214,8 @@ struct iwl_mod_params {
+ * @max_ll_items: max number of OTP blocks
+ * @shadow_ram_support: shadow support for OTP memory
+ * @use_rts_for_ht: use rts/cts protection for HT traffic
++ * @plcp_delta_threshold: plcp error rate threshold used to trigger
++ * radio tuning when there is a high receiving plcp error rate
+ *
+ * We enable the driver to be backward compatible wrt API version. The
+ * driver specifies which APIs it supports (with @ucode_api_max being the
+@@ -257,6 +259,7 @@ struct iwl_cfg {
+ const bool ht_greenfield_support;
+ const bool broken_powersave;
+ bool use_rts_for_ht;
++ u8 plcp_delta_threshold;
+ };
+
+ /***************************
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debugfs.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debugfs.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debugfs.c.orig 2010-03-22 11:33:02.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debugfs.c 2010-03-22 14:31:01.000000000 -0400
+@@ -853,6 +853,47 @@ static ssize_t iwl_dbgfs_current_sleep_c
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ }
+
++static ssize_t iwl_dbgfs_plcp_delta_read(struct file *file,
++ char __user *user_buf,
++ size_t count, loff_t *ppos) {
++
++ struct iwl_priv *priv = (struct iwl_priv *)file->private_data;
++ int pos = 0;
++ char buf[12];
++ const size_t bufsz = sizeof(buf);
++ ssize_t ret;
++
++ pos += scnprintf(buf + pos, bufsz - pos, "%u\n",
++ priv->cfg->plcp_delta_threshold);
++
++ ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos);
++ return ret;
++}
++
++static ssize_t iwl_dbgfs_plcp_delta_write(struct file *file,
++ const char __user *user_buf,
++ size_t count, loff_t *ppos) {
++
++ struct iwl_priv *priv = file->private_data;
++ char buf[8];
++ int buf_size;
++ int plcp;
++
++ memset(buf, 0, sizeof(buf));
++ buf_size = min(count, sizeof(buf) - 1);
++ if (copy_from_user(buf, user_buf, buf_size))
++ return -EFAULT;
++ if (sscanf(buf, "%d", &plcp) != 1)
++ return -EINVAL;
++ if ((plcp <= IWL_MAX_PLCP_ERR_THRESHOLD_MIN) ||
++ (plcp > IWL_MAX_PLCP_ERR_THRESHOLD_MAX))
++ priv->cfg->plcp_delta_threshold =
++ IWL_MAX_PLCP_ERR_THRESHOLD_DEF;
++ else
++ priv->cfg->plcp_delta_threshold = plcp;
++ return count;
++}
++
+ DEBUGFS_READ_WRITE_FILE_OPS(sram);
+ DEBUGFS_WRITE_FILE_OPS(log_event);
+ DEBUGFS_READ_FILE_OPS(nvm);
+@@ -1647,6 +1688,7 @@ DEBUGFS_READ_FILE_OPS(sensitivity);
+ DEBUGFS_READ_FILE_OPS(chain_noise);
+ DEBUGFS_READ_FILE_OPS(tx_power);
+ DEBUGFS_WRITE_FILE_OPS(internal_scan);
++DEBUGFS_READ_WRITE_FILE_OPS(plcp_delta);
+
+ /*
+ * Create the debugfs files and directories
+@@ -1697,6 +1739,7 @@ int iwl_dbgfs_register(struct iwl_priv *
+ DEBUGFS_ADD_FILE(tx_queue, debug);
+ DEBUGFS_ADD_FILE(tx_power, debug);
+ DEBUGFS_ADD_FILE(internal_scan, debug);
++ DEBUGFS_ADD_FILE(plcp_delta, debug);
+ if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) {
+ DEBUGFS_ADD_FILE(ucode_rx_stats, debug);
+ DEBUGFS_ADD_FILE(ucode_tx_stats, debug);
+@@ -1752,6 +1795,7 @@ void iwl_dbgfs_unregister(struct iwl_pri
+ DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_queue);
+ DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_power);
+ DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_internal_scan);
++ DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_plcp_delta);
+ if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) {
+ DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.
+ file_ucode_rx_stats);
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debug.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debug.h
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debug.h.orig 2010-03-22 11:27:31.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debug.h 2010-03-22 14:20:28.000000000 -0400
+@@ -109,6 +109,7 @@ struct iwl_debugfs {
+ struct dentry *file_chain_noise;
+ struct dentry *file_tx_power;
+ struct dentry *file_internal_scan;
++ struct dentry *file_plcp_delta;
+ } dbgfs_debug_files;
+ u32 sram_offset;
+ u32 sram_len;
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig 2010-03-22 14:12:32.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h 2010-03-22 14:20:28.000000000 -0400
+@@ -963,6 +963,15 @@ struct traffic_stats {
+
+ #define IWL_MAX_NUM_QUEUES 20 /* FIXME: do dynamic allocation */
+
++/*
++ * This is the threshold value of plcp error rate per 100mSecs. It is
++ * used to set and check for the validity of plcp_delta.
++ */
++#define IWL_MAX_PLCP_ERR_THRESHOLD_MIN (0)
++#define IWL_MAX_PLCP_ERR_THRESHOLD_DEF (50)
++#define IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF (100)
++#define IWL_MAX_PLCP_ERR_THRESHOLD_MAX (255)
++
+ struct iwl_priv {
+
+ /* ieee device used by generic ieee processing code */
+@@ -991,6 +1000,9 @@ struct iwl_priv {
+ /* ucode beacon time */
+ u32 ucode_beacon_time;
+
++ /* storing the jiffies when the plcp error rate is received */
++ unsigned long plcp_jiffies;
++
+ /* we allocate array of iwl4965_channel_info for NIC's valid channels.
+ * Access via channel # using indirect index array */
+ struct iwl_channel_info *channel_info; /* channel info array */
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c.orig 2009-12-02 22:51:21.000000000 -0500
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c 2010-03-22 15:18:59.000000000 -0400
+@@ -550,11 +550,15 @@ static void iwl_rx_calc_noise(struct iwl
+
+ #define REG_RECALIB_PERIOD (60)
+
++#define PLCP_MSG "plcp_err exceeded %u, %u, %u, %u, %u, %d, %u mSecs\n"
+ void iwl_rx_statistics(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb)
+ {
+ int change;
+ struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
++ int combined_plcp_delta;
++ unsigned int plcp_msec;
++ unsigned long plcp_received_jiffies;
+
+ IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n",
+ (int)sizeof(priv->statistics),
+@@ -566,6 +570,56 @@ void iwl_rx_statistics(struct iwl_priv *
+ STATISTICS_REPLY_FLG_HT40_MODE_MSK) !=
+ (pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK)));
+
++ /*
++ * check for plcp_err and trigger radio reset if it exceeds
++ * the plcp error threshold plcp_delta.
++ */
++ plcp_received_jiffies = jiffies;
++ plcp_msec = jiffies_to_msecs((long) plcp_received_jiffies -
++ (long) priv->plcp_jiffies);
++ priv->plcp_jiffies = plcp_received_jiffies;
++ /*
++ * check to make sure plcp_msec is not 0 to prevent division
++ * by zero.
++ */
++ if (plcp_msec) {
++ combined_plcp_delta =
++ (le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err) -
++ le32_to_cpu(priv->statistics.rx.ofdm.plcp_err)) +
++ (le32_to_cpu(pkt->u.stats.rx.ofdm_ht.plcp_err) -
++ le32_to_cpu(priv->statistics.rx.ofdm_ht.plcp_err));
++
++ if ((combined_plcp_delta > 0) &&
++ ((combined_plcp_delta * 100) / plcp_msec) >
++ priv->cfg->plcp_delta_threshold) {
++ /*
++ * if plcp_err exceed the threshold, the following
++ * data is printed in csv format:
++ * Text: plcp_err exceeded %d,
++ * Received ofdm.plcp_err,
++ * Current ofdm.plcp_err,
++ * Received ofdm_ht.plcp_err,
++ * Current ofdm_ht.plcp_err,
++ * combined_plcp_delta,
++ * plcp_msec
++ */
++ IWL_DEBUG_RADIO(priv, PLCP_MSG,
++ priv->cfg->plcp_delta_threshold,
++ le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err),
++ le32_to_cpu(priv->statistics.rx.ofdm.plcp_err),
++ le32_to_cpu(pkt->u.stats.rx.ofdm_ht.plcp_err),
++ le32_to_cpu(
++ priv->statistics.rx.ofdm_ht.plcp_err),
++ combined_plcp_delta, plcp_msec);
++
++ /*
++ * Reset the RF radio due to the high plcp
++ * error rate
++ */
++ iwl_force_rf_reset(priv);
++ }
++ }
++
+ memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics));
+
+ set_bit(STATUS_STATISTICS, &priv->status);
diff --git a/iwlwifi_-add-function-to-reset_tune-radio-if-needed.patch b/iwlwifi_-add-function-to-reset_tune-radio-if-needed.patch
new file mode 100644
index 0000000..f83e12a
--- /dev/null
+++ b/iwlwifi_-add-function-to-reset_tune-radio-if-needed.patch
@@ -0,0 +1,374 @@
+Back-port of the following upstream commit...
+
+commit afbdd69af0e6a0c40676d4d4b94a0a4414708eaa
+Author: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+Date: Fri Jan 22 14:22:43 2010 -0800
+
+ iwlwifi: add function to reset/tune radio if needed
+
+ Adding "radio reset" function to help reset and stabilize the radio.
+
+ During normal operation, sometime for unknown reason, radio encounter
+ problem and can not recover by itself; the best way to
+ recover from it is to reset and re-tune the radio. Currently, there is
+ no RF reset command available, but since radio will get reset when
+ switching channel, use internal hw scan request to force radio
+ reset and get back to normal operation state.
+
+ The internal hw scan will only perform passive scan on the first
+ available channel (not the channel being used) in associated state. The
+ request should be ignored if already performing scan operation or STA is
+ not in associated state.
+
+ Also include an "internal_scan" debugfs file to help trigger the
+ internal scan from user mode.
+
+ Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+ Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
+ Signed-off-by: John W. Linville <linville@tuxdriver.com>
+
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig 2010-03-22 10:23:59.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c 2010-03-22 11:26:18.000000000 -0400
+@@ -3035,6 +3035,30 @@ void iwl_update_stats(struct iwl_priv *p
+ EXPORT_SYMBOL(iwl_update_stats);
+ #endif
+
++void iwl_force_rf_reset(struct iwl_priv *priv)
++{
++ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
++ return;
++
++ if (!iwl_is_associated(priv)) {
++ IWL_DEBUG_SCAN(priv, "force reset rejected: not associated\n");
++ return;
++ }
++ /*
++ * There is no easy and better way to force reset the radio,
++ * the only known method is switching channel which will force to
++ * reset and tune the radio.
++ * Use internal short scan (single channel) operation to should
++ * achieve this objective.
++ * Driver should reset the radio when number of consecutive missed
++ * beacon, or any other uCode error condition detected.
++ */
++ IWL_DEBUG_INFO(priv, "perform radio reset.\n");
++ iwl_internal_short_hw_scan(priv);
++ return;
++}
++EXPORT_SYMBOL(iwl_force_rf_reset);
++
+ #ifdef CONFIG_PM
+
+ int iwl_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig 2010-03-22 10:23:59.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h 2010-03-22 11:26:18.000000000 -0400
+@@ -461,6 +461,8 @@ void iwl_init_scan_params(struct iwl_pri
+ int iwl_scan_cancel(struct iwl_priv *priv);
+ int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
+ int iwl_mac_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req);
++int iwl_internal_short_hw_scan(struct iwl_priv *priv);
++void iwl_force_rf_reset(struct iwl_priv *priv);
+ u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame,
+ const u8 *ie, int ie_len, int left);
+ void iwl_setup_rx_scan_handlers(struct iwl_priv *priv);
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debugfs.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debugfs.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debugfs.c.orig 2009-12-02 22:51:21.000000000 -0500
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debugfs.c 2010-03-22 11:33:02.000000000 -0400
+@@ -1614,6 +1614,27 @@ static ssize_t iwl_dbgfs_tx_power_read(s
+ return simple_read_from_buffer(user_buf, count, ppos, buf, pos);
+ }
+
++static ssize_t iwl_dbgfs_internal_scan_write(struct file *file,
++ const char __user *user_buf,
++ size_t count, loff_t *ppos)
++{
++ struct iwl_priv *priv = file->private_data;
++ char buf[8];
++ int buf_size;
++ int scan;
++
++ memset(buf, 0, sizeof(buf));
++ buf_size = min(count, sizeof(buf) - 1);
++ if (copy_from_user(buf, user_buf, buf_size))
++ return -EFAULT;
++ if (sscanf(buf, "%d", &scan) != 1)
++ return -EINVAL;
++
++ iwl_internal_short_hw_scan(priv);
++
++ return count;
++}
++
+ DEBUGFS_READ_WRITE_FILE_OPS(rx_statistics);
+ DEBUGFS_READ_WRITE_FILE_OPS(tx_statistics);
+ DEBUGFS_READ_WRITE_FILE_OPS(traffic_log);
+@@ -1625,6 +1646,7 @@ DEBUGFS_READ_FILE_OPS(ucode_general_stat
+ DEBUGFS_READ_FILE_OPS(sensitivity);
+ DEBUGFS_READ_FILE_OPS(chain_noise);
+ DEBUGFS_READ_FILE_OPS(tx_power);
++DEBUGFS_WRITE_FILE_OPS(internal_scan);
+
+ /*
+ * Create the debugfs files and directories
+@@ -1674,6 +1696,7 @@ int iwl_dbgfs_register(struct iwl_priv *
+ DEBUGFS_ADD_FILE(rx_queue, debug);
+ DEBUGFS_ADD_FILE(tx_queue, debug);
+ DEBUGFS_ADD_FILE(tx_power, debug);
++ DEBUGFS_ADD_FILE(internal_scan, debug);
+ if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) {
+ DEBUGFS_ADD_FILE(ucode_rx_stats, debug);
+ DEBUGFS_ADD_FILE(ucode_tx_stats, debug);
+@@ -1728,6 +1751,7 @@ void iwl_dbgfs_unregister(struct iwl_pri
+ DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_rx_queue);
+ DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_queue);
+ DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_tx_power);
++ DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.file_internal_scan);
+ if ((priv->hw_rev & CSR_HW_REV_TYPE_MSK) != CSR_HW_REV_TYPE_3945) {
+ DEBUGFS_REMOVE(priv->dbgfs->dbgfs_debug_files.
+ file_ucode_rx_stats);
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debug.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debug.h
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debug.h.orig 2009-12-02 22:51:21.000000000 -0500
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-debug.h 2010-03-22 11:27:31.000000000 -0400
+@@ -108,6 +108,7 @@ struct iwl_debugfs {
+ struct dentry *file_sensitivity;
+ struct dentry *file_chain_noise;
+ struct dentry *file_tx_power;
++ struct dentry *file_internal_scan;
+ } dbgfs_debug_files;
+ u32 sram_offset;
+ u32 sram_len;
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig 2010-03-22 10:23:59.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h 2010-03-22 11:26:18.000000000 -0400
+@@ -1016,6 +1016,7 @@ struct iwl_priv {
+ void *scan;
+ int scan_bands;
+ struct cfg80211_scan_request *scan_request;
++ bool is_internal_short_scan;
+ u8 scan_tx_ant[IEEE80211_NUM_BANDS];
+ u8 mgmt_tx_ant;
+
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c.orig 2009-12-02 22:51:21.000000000 -0500
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c 2010-03-22 11:26:18.000000000 -0400
+@@ -316,6 +316,72 @@ u16 iwl_get_passive_dwell_time(struct iw
+ }
+ EXPORT_SYMBOL(iwl_get_passive_dwell_time);
+
++static int iwl_get_single_channel_for_scan(struct iwl_priv *priv,
++ enum ieee80211_band band,
++ struct iwl_scan_channel *scan_ch)
++{
++ const struct ieee80211_supported_band *sband;
++ const struct iwl_channel_info *ch_info;
++ u16 passive_dwell = 0;
++ u16 active_dwell = 0;
++ int i, added = 0;
++ u16 channel = 0;
++
++ sband = iwl_get_hw_mode(priv, band);
++ if (!sband) {
++ IWL_ERR(priv, "invalid band\n");
++ return added;
++ }
++
++ active_dwell = iwl_get_active_dwell_time(priv, band, 0);
++ passive_dwell = iwl_get_passive_dwell_time(priv, band);
++
++ if (passive_dwell <= active_dwell)
++ passive_dwell = active_dwell + 1;
++
++ /* only scan single channel, good enough to reset the RF */
++ /* pick the first valid not in-use channel */
++ if (band == IEEE80211_BAND_5GHZ) {
++ for (i = 14; i < priv->channel_count; i++) {
++ if (priv->channel_info[i].channel !=
++ le16_to_cpu(priv->staging_rxon.channel)) {
++ channel = priv->channel_info[i].channel;
++ ch_info = iwl_get_channel_info(priv,
++ band, channel);
++ if (is_channel_valid(ch_info))
++ break;
++ }
++ }
++ } else {
++ for (i = 0; i < 14; i++) {
++ if (priv->channel_info[i].channel !=
++ le16_to_cpu(priv->staging_rxon.channel)) {
++ channel =
++ priv->channel_info[i].channel;
++ ch_info = iwl_get_channel_info(priv,
++ band, channel);
++ if (is_channel_valid(ch_info))
++ break;
++ }
++ }
++ }
++ if (channel) {
++ scan_ch->channel = cpu_to_le16(channel);
++ scan_ch->type = SCAN_CHANNEL_TYPE_PASSIVE;
++ scan_ch->active_dwell = cpu_to_le16(active_dwell);
++ scan_ch->passive_dwell = cpu_to_le16(passive_dwell);
++ /* Set txpower levels to defaults */
++ scan_ch->dsp_atten = 110;
++ if (band == IEEE80211_BAND_5GHZ)
++ scan_ch->tx_gain = ((1 << 5) | (3 << 3)) | 3;
++ else
++ scan_ch->tx_gain = ((1 << 5) | (5 << 3));
++ added++;
++ } else
++ IWL_ERR(priv, "no valid channel found\n");
++ return added;
++}
++
+ static int iwl_get_channels_for_scan(struct iwl_priv *priv,
+ enum ieee80211_band band,
+ u8 is_active, u8 n_probes,
+@@ -422,6 +488,7 @@ static int iwl_scan_initiate(struct iwl_
+
+ IWL_DEBUG_INFO(priv, "Starting scan...\n");
+ set_bit(STATUS_SCANNING, &priv->status);
++ priv->is_internal_short_scan = false;
+ priv->scan_start = jiffies;
+ priv->scan_pass_start = priv->scan_start;
+
+@@ -489,6 +556,45 @@ out_unlock:
+ }
+ EXPORT_SYMBOL(iwl_mac_hw_scan);
+
++/*
++ * internal short scan, this function should only been called while associated.
++ * It will reset and tune the radio to prevent possible RF related problem
++ */
++int iwl_internal_short_hw_scan(struct iwl_priv *priv)
++{
++ int ret = 0;
++
++ if (!iwl_is_ready_rf(priv)) {
++ ret = -EIO;
++ IWL_DEBUG_SCAN(priv, "not ready or exit pending\n");
++ goto out;
++ }
++ if (test_bit(STATUS_SCANNING, &priv->status)) {
++ IWL_DEBUG_SCAN(priv, "Scan already in progress.\n");
++ ret = -EAGAIN;
++ goto out;
++ }
++ if (test_bit(STATUS_SCAN_ABORTING, &priv->status)) {
++ IWL_DEBUG_SCAN(priv, "Scan request while abort pending\n");
++ ret = -EAGAIN;
++ goto out;
++ }
++ priv->scan_bands = 0;
++ if (priv->band == IEEE80211_BAND_5GHZ)
++ priv->scan_bands |= BIT(IEEE80211_BAND_5GHZ);
++ else
++ priv->scan_bands |= BIT(IEEE80211_BAND_2GHZ);
++
++ IWL_DEBUG_SCAN(priv, "Start internal short scan...\n");
++ set_bit(STATUS_SCANNING, &priv->status);
++ priv->is_internal_short_scan = true;
++ queue_work(priv->workqueue, &priv->request_scan);
++
++out:
++ return ret;
++}
++EXPORT_SYMBOL(iwl_internal_short_hw_scan);
++
+ #define IWL_SCAN_CHECK_WATCHDOG (7 * HZ)
+
+ void iwl_bg_scan_check(struct work_struct *data)
+@@ -552,7 +658,8 @@ u16 iwl_fill_probe_req(struct iwl_priv *
+ if (WARN_ON(left < ie_len))
+ return len;
+
+- memcpy(pos, ies, ie_len);
++ if (ies)
++ memcpy(pos, ies, ie_len);
+ len += ie_len;
+ left -= ie_len;
+
+@@ -654,7 +761,6 @@ static void iwl_bg_request_scan(struct w
+ unsigned long flags;
+
+ IWL_DEBUG_INFO(priv, "Scanning while associated...\n");
+-
+ spin_lock_irqsave(&priv->lock, flags);
+ interval = priv->beacon_int;
+ spin_unlock_irqrestore(&priv->lock, flags);
+@@ -672,7 +778,9 @@ static void iwl_bg_request_scan(struct w
+ scan_suspend_time, interval);
+ }
+
+- if (priv->scan_request->n_ssids) {
++ if (priv->is_internal_short_scan) {
++ IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n");
++ } else if (priv->scan_request->n_ssids) {
+ int i, p = 0;
+ IWL_DEBUG_SCAN(priv, "Kicking off active scan\n");
+ for (i = 0; i < priv->scan_request->n_ssids; i++) {
+@@ -740,24 +848,38 @@ static void iwl_bg_request_scan(struct w
+ rx_chain |= rx_ant << RXON_RX_CHAIN_FORCE_SEL_POS;
+ rx_chain |= 0x1 << RXON_RX_CHAIN_DRIVER_FORCE_POS;
+ scan->rx_chain = cpu_to_le16(rx_chain);
+- cmd_len = iwl_fill_probe_req(priv,
+- (struct ieee80211_mgmt *)scan->data,
+- priv->scan_request->ie,
+- priv->scan_request->ie_len,
+- IWL_MAX_SCAN_SIZE - sizeof(*scan));
++ if (!priv->is_internal_short_scan) {
++ cmd_len = iwl_fill_probe_req(priv,
++ (struct ieee80211_mgmt *)scan->data,
++ priv->scan_request->ie,
++ priv->scan_request->ie_len,
++ IWL_MAX_SCAN_SIZE - sizeof(*scan));
++ } else {
++ cmd_len = iwl_fill_probe_req(priv,
++ (struct ieee80211_mgmt *)scan->data,
++ NULL, 0,
++ IWL_MAX_SCAN_SIZE - sizeof(*scan));
+
++ }
+ scan->tx_cmd.len = cpu_to_le16(cmd_len);
+-
+ if (iwl_is_monitor_mode(priv))
+ scan->filter_flags = RXON_FILTER_PROMISC_MSK;
+
+ scan->filter_flags |= (RXON_FILTER_ACCEPT_GRP_MSK |
+ RXON_FILTER_BCON_AWARE_MSK);
+
+- scan->channel_count =
+- iwl_get_channels_for_scan(priv, band, is_active, n_probes,
+- (void *)&scan->data[le16_to_cpu(scan->tx_cmd.len)]);
+-
++ if (priv->is_internal_short_scan) {
++ scan->channel_count =
++ iwl_get_single_channel_for_scan(priv, band,
++ (void *)&scan->data[le16_to_cpu(
++ scan->tx_cmd.len)]);
++ } else {
++ scan->channel_count =
++ iwl_get_channels_for_scan(priv, band,
++ is_active, n_probes,
++ (void *)&scan->data[le16_to_cpu(
++ scan->tx_cmd.len)]);
++ }
+ if (scan->channel_count == 0) {
+ IWL_DEBUG_SCAN(priv, "channel count %d\n", scan->channel_count);
+ goto done;
+@@ -818,7 +940,12 @@ void iwl_bg_scan_completed(struct work_s
+
+ cancel_delayed_work(&priv->scan_check);
+
+- ieee80211_scan_completed(priv->hw, false);
++ if (!priv->is_internal_short_scan)
++ ieee80211_scan_completed(priv->hw, false);
++ else {
++ priv->is_internal_short_scan = false;
++ IWL_DEBUG_SCAN(priv, "internal short scan completed\n");
++ }
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
diff --git a/iwlwifi_-add-internal-short-scan-support-for-3945.patch b/iwlwifi_-add-internal-short-scan-support-for-3945.patch
new file mode 100644
index 0000000..db132ee
--- /dev/null
+++ b/iwlwifi_-add-internal-short-scan-support-for-3945.patch
@@ -0,0 +1,85 @@
+commit 4f4d4088b05155d4904e29d5c00316395ce32f27
+Author: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+Date: Wed Feb 24 08:28:30 2010 -0800
+
+ iwlwifi: add internal short scan support for 3945
+
+ Add internal short scan support for 3945 NIC, This allows 3945 NIC
+ to support radio reset request like the other series of NICs.
+
+ Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+ Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
+
+diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
+index dd33251..252df12 100644
+--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
++++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
+@@ -2799,7 +2799,6 @@ static void iwl3945_bg_request_scan(struct work_struct *data)
+ .len = sizeof(struct iwl3945_scan_cmd),
+ .flags = CMD_SIZE_HUGE,
+ };
+- int rc = 0;
+ struct iwl3945_scan_cmd *scan;
+ struct ieee80211_conf *conf = NULL;
+ u8 n_probes = 0;
+@@ -2827,7 +2826,6 @@ static void iwl3945_bg_request_scan(struct work_struct *data)
+ if (test_bit(STATUS_SCAN_HW, &priv->status)) {
+ IWL_DEBUG_INFO(priv, "Multiple concurrent scan requests "
+ "Ignoring second request.\n");
+- rc = -EIO;
+ goto done;
+ }
+
+@@ -2862,7 +2860,7 @@ static void iwl3945_bg_request_scan(struct work_struct *data)
+ priv->scan = kmalloc(sizeof(struct iwl3945_scan_cmd) +
+ IWL_MAX_SCAN_SIZE, GFP_KERNEL);
+ if (!priv->scan) {
+- rc = -ENOMEM;
++ IWL_DEBUG_SCAN(priv, "Fail to allocate scan memory\n");
+ goto done;
+ }
+ }
+@@ -2905,7 +2903,9 @@ static void iwl3945_bg_request_scan(struct work_struct *data)
+ scan_suspend_time, interval);
+ }
+
+- if (priv->scan_request->n_ssids) {
++ if (priv->is_internal_short_scan) {
++ IWL_DEBUG_SCAN(priv, "Start internal passive scan.\n");
++ } else if (priv->scan_request->n_ssids) {
+ int i, p = 0;
+ IWL_DEBUG_SCAN(priv, "Kicking off active scan\n");
+ for (i = 0; i < priv->scan_request->n_ssids; i++) {
+@@ -2952,13 +2952,20 @@ static void iwl3945_bg_request_scan(struct work_struct *data)
+ goto done;
+ }
+
+- scan->tx_cmd.len = cpu_to_le16(
++ if (!priv->is_internal_short_scan) {
++ scan->tx_cmd.len = cpu_to_le16(
+ iwl_fill_probe_req(priv,
+ (struct ieee80211_mgmt *)scan->data,
+ priv->scan_request->ie,
+ priv->scan_request->ie_len,
+ IWL_MAX_SCAN_SIZE - sizeof(*scan)));
+-
++ } else {
++ scan->tx_cmd.len = cpu_to_le16(
++ iwl_fill_probe_req(priv,
++ (struct ieee80211_mgmt *)scan->data,
++ NULL, 0,
++ IWL_MAX_SCAN_SIZE - sizeof(*scan)));
++ }
+ /* select Rx antennas */
+ scan->flags |= iwl3945_get_antenna_flags(priv);
+
+@@ -2980,8 +2987,7 @@ static void iwl3945_bg_request_scan(struct work_struct *data)
+ scan->len = cpu_to_le16(cmd.len);
+
+ set_bit(STATUS_SCAN_HW, &priv->status);
+- rc = iwl_send_cmd_sync(priv, &cmd);
+- if (rc)
++ if (iwl_send_cmd_sync(priv, &cmd))
+ goto done;
+
+ queue_delayed_work(priv->workqueue, &priv->scan_check,
diff --git a/iwlwifi_-code-cleanup-for-connectivity-recovery.patch b/iwlwifi_-code-cleanup-for-connectivity-recovery.patch
new file mode 100644
index 0000000..587c5ff
--- /dev/null
+++ b/iwlwifi_-code-cleanup-for-connectivity-recovery.patch
@@ -0,0 +1,230 @@
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c.orig 2010-03-22 16:42:34.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c 2010-03-22 16:42:57.000000000 -0400
+@@ -136,7 +136,8 @@ static struct iwl_lib_ops iwl1000_lib =
+ .set_ct_kill = iwl1000_set_ct_threshold,
+ },
+ .recover_from_tx_stall = iwl_bg_monitor_recover,
+- .recover_from_statistics = iwl_recover_from_statistics,
++ .check_plcp_health = iwl_good_plcp_health,
++ .check_ack_health = iwl_good_ack_health,
+ };
+
+ static struct iwl_ops iwl1000_ops = {
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c.orig 2010-03-22 16:42:34.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c 2010-03-22 16:42:57.000000000 -0400
+@@ -2340,7 +2340,7 @@ static struct iwl_lib_ops iwl4965_lib =
+ .temperature = iwl4965_temperature_calib,
+ .set_ct_kill = iwl4965_set_ct_threshold,
+ },
+- .recover_from_statistics = iwl_recover_from_statistics,
++ .check_plcp_health = iwl_good_plcp_health,
+ };
+
+ static struct iwl_ops iwl4965_ops = {
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c.orig 2010-03-22 16:42:34.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c 2010-03-22 16:42:57.000000000 -0400
+@@ -1580,7 +1580,8 @@ struct iwl_lib_ops iwl5000_lib = {
+ .set_ct_kill = iwl5000_set_ct_threshold,
+ },
+ .recover_from_tx_stall = iwl_bg_monitor_recover,
+- .recover_from_statistics = iwl_recover_from_statistics,
++ .check_plcp_health = iwl_good_plcp_health,
++ .check_ack_health = iwl_good_ack_health,
+ };
+
+ static struct iwl_lib_ops iwl5150_lib = {
+@@ -1634,7 +1635,8 @@ static struct iwl_lib_ops iwl5150_lib =
+ .set_ct_kill = iwl5150_set_ct_threshold,
+ },
+ .recover_from_tx_stall = iwl_bg_monitor_recover,
+- .recover_from_statistics = iwl_recover_from_statistics,
++ .check_plcp_health = iwl_good_plcp_health,
++ .check_ack_health = iwl_good_ack_health,
+ };
+
+ struct iwl_ops iwl5000_ops = {
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c.orig 2010-03-22 16:42:34.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c 2010-03-22 16:42:57.000000000 -0400
+@@ -138,7 +138,8 @@ static struct iwl_lib_ops iwl6000_lib =
+ .set_ct_kill = iwl6000_set_ct_threshold,
+ },
+ .recover_from_tx_stall = iwl_bg_monitor_recover,
+- .recover_from_statistics = iwl_recover_from_statistics,
++ .check_plcp_health = iwl_good_plcp_health,
++ .check_ack_health = iwl_good_ack_health,
+ };
+
+ static struct iwl_hcmd_utils_ops iwl6000_hcmd_utils = {
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig 2010-03-22 16:42:34.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h 2010-03-22 16:42:57.000000000 -0400
+@@ -185,8 +185,11 @@ struct iwl_lib_ops {
+ struct iwl_temp_ops temp_ops;
+ /* recover from tx queue stall */
+ void (*recover_from_tx_stall)(unsigned long data);
+- /* recover from errors showed in statistics */
+- void (*recover_from_statistics)(struct iwl_priv *priv,
++ /* check for plcp health */
++ bool (*check_plcp_health)(struct iwl_priv *priv,
++ struct iwl_rx_packet *pkt);
++ /* check for ack health */
++ bool (*check_ack_health)(struct iwl_priv *priv,
+ struct iwl_rx_packet *pkt);
+ };
+
+@@ -401,7 +404,9 @@ int iwl_tx_queue_reclaim(struct iwl_priv
+ /* Handlers */
+ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb);
+-void iwl_recover_from_statistics(struct iwl_priv *priv,
++bool iwl_good_plcp_health(struct iwl_priv *priv,
++ struct iwl_rx_packet *pkt);
++bool iwl_good_ack_health(struct iwl_priv *priv,
+ struct iwl_rx_packet *pkt);
+ void iwl_rx_statistics(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb);
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c.orig 2010-03-22 16:42:34.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c 2010-03-22 16:45:03.000000000 -0400
+@@ -555,24 +555,18 @@ static void iwl_rx_calc_noise(struct iwl
+ #define BA_TIMEOUT_CNT (5)
+ #define BA_TIMEOUT_MAX (16)
+
+-#define PLCP_MSG "plcp_err exceeded %u, %u, %u, %u, %u, %d, %u mSecs\n"
+-/*
+- * This function checks for plcp error, ACK count ratios, aggregated BA
+- * timeout retries.
+- * - When the ACK count ratio is 0 and aggregated BA timeout retries is
+- * exceeding the BA_TIMEOUT_MAX, it will recover the failure by resetting
+- * the firmware.
+- * - When the plcp error is exceeding the thresholds, it will reset the radio
+- * to improve the throughput.
++/**
++ * iwl_good_ack_health - checks for ACK count ratios, BA timeout retries.
++ *
++ * When the ACK count ratio is 0 and aggregated BA timeout retries exceeding
++ * the BA_TIMEOUT_MAX, reload firmware and bring system back to normal
++ * operation state.
+ */
+-void iwl_recover_from_statistics(struct iwl_priv *priv,
+- struct iwl_rx_packet *pkt)
++bool iwl_good_ack_health(struct iwl_priv *priv,
++ struct iwl_rx_packet *pkt)
+ {
+- int combined_plcp_delta;
+- unsigned int plcp_msec;
+- unsigned long plcp_received_jiffies;
+- int actual_ack_cnt_delta;
+- int expected_ack_cnt_delta;
++ bool rc = true;
++ int actual_ack_cnt_delta, expected_ack_cnt_delta;
+ int ba_timeout_delta;
+
+ actual_ack_cnt_delta =
+@@ -594,13 +588,27 @@ void iwl_recover_from_statistics(struct
+ actual_ack_cnt_delta, expected_ack_cnt_delta);
+ IWL_DEBUG_RADIO(priv, "agg ba_timeout delta = %d\n",
+ ba_timeout_delta);
+- if ((actual_ack_cnt_delta == 0) &&
+- (ba_timeout_delta >= BA_TIMEOUT_MAX)) {
+- IWL_DEBUG_RADIO(priv,
+- "call iwl_force_reset(IWL_FW_RESET)\n");
+- iwl_force_reset(priv, IWL_FW_RESET);
+- }
++ if (!actual_ack_cnt_delta &&
++ (ba_timeout_delta >= BA_TIMEOUT_MAX))
++ rc = false;
+ }
++ return rc;
++}
++EXPORT_SYMBOL(iwl_good_ack_health);
++
++/**
++ * iwl_good_plcp_health - checks for plcp error.
++ *
++ * When the plcp error is exceeding the thresholds, reset the radio
++ * to improve the throughput.
++ */
++bool iwl_good_plcp_health(struct iwl_priv *priv,
++ struct iwl_rx_packet *pkt)
++{
++ bool rc = true;
++ int combined_plcp_delta;
++ unsigned int plcp_msec;
++ unsigned long plcp_received_jiffies;
+
+ /*
+ * check for plcp_err and trigger radio reset if it exceeds
+@@ -635,7 +643,8 @@ void iwl_recover_from_statistics(struct
+ * combined_plcp_delta,
+ * plcp_msec
+ */
+- IWL_DEBUG_RADIO(priv, PLCP_MSG,
++ IWL_DEBUG_RADIO(priv, "plcp_err exceeded %u, "
++ "%u, %u, %u, %u, %d, %u mSecs\n",
+ priv->cfg->plcp_delta_threshold,
+ le32_to_cpu(pkt->u.stats.rx.ofdm.plcp_err),
+ le32_to_cpu(priv->statistics.rx.ofdm.plcp_err),
+@@ -643,15 +652,42 @@ void iwl_recover_from_statistics(struct
+ le32_to_cpu(
+ priv->statistics.rx.ofdm_ht.plcp_err),
+ combined_plcp_delta, plcp_msec);
+- /*
+- * Reset the RF radio due to the high plcp
+- * error rate
+- */
+- iwl_force_reset(priv, IWL_RF_RESET);
++ rc = false;
++ }
++ }
++ return rc;
++}
++EXPORT_SYMBOL(iwl_good_plcp_health);
++
++static void iwl_recover_from_statistics(struct iwl_priv *priv,
++ struct iwl_rx_packet *pkt)
++{
++ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
++ return;
++ if (iwl_is_associated(priv)) {
++ if (priv->cfg->ops->lib->check_ack_health) {
++ if (!priv->cfg->ops->lib->check_ack_health(
++ priv, pkt)) {
++ /*
++ * low ack count detected
++ * restart Firmware
++ */
++ IWL_ERR(priv, "low ack count detected, "
++ "restart firmware\n");
++ iwl_force_reset(priv, IWL_FW_RESET);
++ }
++ } else if (priv->cfg->ops->lib->check_plcp_health) {
++ if (!priv->cfg->ops->lib->check_plcp_health(
++ priv, pkt)) {
++ /*
++ * high plcp error detected
++ * reset Radio
++ */
++ iwl_force_reset(priv, IWL_RF_RESET);
++ }
+ }
+ }
+ }
+-EXPORT_SYMBOL(iwl_recover_from_statistics);
+
+ void iwl_rx_statistics(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb)
+@@ -670,8 +706,7 @@ void iwl_rx_statistics(struct iwl_priv *
+ STATISTICS_REPLY_FLG_HT40_MODE_MSK) !=
+ (pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK)));
+
+- if (priv->cfg->ops->lib->recover_from_statistics)
+- priv->cfg->ops->lib->recover_from_statistics(priv, pkt);
++ iwl_recover_from_statistics(priv, pkt);
+
+ memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics));
+
diff --git a/iwlwifi_-iwl_good_ack_health-only-apply-to-AGN-device.patch b/iwlwifi_-iwl_good_ack_health-only-apply-to-AGN-device.patch
new file mode 100644
index 0000000..60037ac
--- /dev/null
+++ b/iwlwifi_-iwl_good_ack_health-only-apply-to-AGN-device.patch
@@ -0,0 +1,146 @@
+From 2386b8d18106262e27c9ca1a674a1018af29bdde Mon Sep 17 00:00:00 2001
+From: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+Date: Tue, 16 Mar 2010 10:46:31 -0700
+Subject: [PATCH 17/17] iwlwifi: iwl_good_ack_health() only apply to AGN device
+
+iwl_good_ack_health() check for expected and actual ack count which only
+apply to aggregation mode. Move the function to iwlagn module.
+
+Reported-by: Chantry Xavier <chantry.xavier@gmail.com>
+Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
+---
+ drivers/net/wireless/iwlwifi/iwl-agn.c | 45 ++++++++++++++++++++++++++++++
+ drivers/net/wireless/iwlwifi/iwl-core.h | 2 +
+ drivers/net/wireless/iwlwifi/iwl-rx.c | 46 -------------------------------
+ 3 files changed, 47 insertions(+), 46 deletions(-)
+
+diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
+index c22db6c..d57f215 100644
+--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
++++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
+@@ -1316,6 +1316,51 @@ static void iwl_irq_tasklet(struct iwl_priv *priv)
+ iwl_enable_interrupts(priv);
+ }
+
++/* the threshold ratio of actual_ack_cnt to expected_ack_cnt in percent */
++#define ACK_CNT_RATIO (50)
++#define BA_TIMEOUT_CNT (5)
++#define BA_TIMEOUT_MAX (16)
++
++/**
++ * iwl_good_ack_health - checks for ACK count ratios, BA timeout retries.
++ *
++ * When the ACK count ratio is 0 and aggregated BA timeout retries exceeding
++ * the BA_TIMEOUT_MAX, reload firmware and bring system back to normal
++ * operation state.
++ */
++bool iwl_good_ack_health(struct iwl_priv *priv,
++ struct iwl_rx_packet *pkt)
++{
++ bool rc = true;
++ int actual_ack_cnt_delta, expected_ack_cnt_delta;
++ int ba_timeout_delta;
++
++ actual_ack_cnt_delta =
++ le32_to_cpu(pkt->u.stats.tx.actual_ack_cnt) -
++ le32_to_cpu(priv->statistics.tx.actual_ack_cnt);
++ expected_ack_cnt_delta =
++ le32_to_cpu(pkt->u.stats.tx.expected_ack_cnt) -
++ le32_to_cpu(priv->statistics.tx.expected_ack_cnt);
++ ba_timeout_delta =
++ le32_to_cpu(pkt->u.stats.tx.agg.ba_timeout) -
++ le32_to_cpu(priv->statistics.tx.agg.ba_timeout);
++ if ((priv->_agn.agg_tids_count > 0) &&
++ (expected_ack_cnt_delta > 0) &&
++ (((actual_ack_cnt_delta * 100) / expected_ack_cnt_delta)
++ < ACK_CNT_RATIO) &&
++ (ba_timeout_delta > BA_TIMEOUT_CNT)) {
++ IWL_DEBUG_RADIO(priv, "actual_ack_cnt delta = %d,"
++ " expected_ack_cnt = %d\n",
++ actual_ack_cnt_delta, expected_ack_cnt_delta);
++ IWL_DEBUG_RADIO(priv, "agg ba_timeout delta = %d\n",
++ ba_timeout_delta);
++ if (!actual_ack_cnt_delta &&
++ (ba_timeout_delta >= BA_TIMEOUT_MAX))
++ rc = false;
++ }
++ return rc;
++}
++
+
+ /******************************************************************************
+ *
+diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
+index 8bf0c39..ca4a516 100644
+--- a/drivers/net/wireless/iwlwifi/iwl-core.h
++++ b/drivers/net/wireless/iwlwifi/iwl-core.h
+@@ -584,6 +584,8 @@ void iwl_disable_ict(struct iwl_priv *priv);
+ int iwl_alloc_isr_ict(struct iwl_priv *priv);
+ void iwl_free_isr_ict(struct iwl_priv *priv);
+ irqreturn_t iwl_isr_ict(int irq, void *data);
++bool iwl_good_ack_health(struct iwl_priv *priv,
++ struct iwl_rx_packet *pkt);
+
+ static inline u16 iwl_pcie_link_ctl(struct iwl_priv *priv)
+ {
+diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c
+index 1b2a3fc..054d169 100644
+--- a/drivers/net/wireless/iwlwifi/iwl-rx.c
++++ b/drivers/net/wireless/iwlwifi/iwl-rx.c
+@@ -592,52 +592,6 @@ static void iwl_accumulative_statistics(struct iwl_priv *priv,
+
+ #define REG_RECALIB_PERIOD (60)
+
+-/* the threshold ratio of actual_ack_cnt to expected_ack_cnt in percent */
+-#define ACK_CNT_RATIO (50)
+-#define BA_TIMEOUT_CNT (5)
+-#define BA_TIMEOUT_MAX (16)
+-
+-/**
+- * iwl_good_ack_health - checks for ACK count ratios, BA timeout retries.
+- *
+- * When the ACK count ratio is 0 and aggregated BA timeout retries exceeding
+- * the BA_TIMEOUT_MAX, reload firmware and bring system back to normal
+- * operation state.
+- */
+-bool iwl_good_ack_health(struct iwl_priv *priv,
+- struct iwl_rx_packet *pkt)
+-{
+- bool rc = true;
+- int actual_ack_cnt_delta, expected_ack_cnt_delta;
+- int ba_timeout_delta;
+-
+- actual_ack_cnt_delta =
+- le32_to_cpu(pkt->u.stats.tx.actual_ack_cnt) -
+- le32_to_cpu(priv->statistics.tx.actual_ack_cnt);
+- expected_ack_cnt_delta =
+- le32_to_cpu(pkt->u.stats.tx.expected_ack_cnt) -
+- le32_to_cpu(priv->statistics.tx.expected_ack_cnt);
+- ba_timeout_delta =
+- le32_to_cpu(pkt->u.stats.tx.agg.ba_timeout) -
+- le32_to_cpu(priv->statistics.tx.agg.ba_timeout);
+- if ((priv->_agn.agg_tids_count > 0) &&
+- (expected_ack_cnt_delta > 0) &&
+- (((actual_ack_cnt_delta * 100) / expected_ack_cnt_delta)
+- < ACK_CNT_RATIO) &&
+- (ba_timeout_delta > BA_TIMEOUT_CNT)) {
+- IWL_DEBUG_RADIO(priv, "actual_ack_cnt delta = %d,"
+- " expected_ack_cnt = %d\n",
+- actual_ack_cnt_delta, expected_ack_cnt_delta);
+- IWL_DEBUG_RADIO(priv, "agg ba_timeout delta = %d\n",
+- ba_timeout_delta);
+- if (!actual_ack_cnt_delta &&
+- (ba_timeout_delta >= BA_TIMEOUT_MAX))
+- rc = false;
+- }
+- return rc;
+-}
+-EXPORT_SYMBOL(iwl_good_ack_health);
+-
+ /**
+ * iwl_good_plcp_health - checks for plcp error.
+ *
+--
+1.6.3.3
+
diff --git a/iwlwifi_-move-plcp-check-to-separated-function.patch b/iwlwifi_-move-plcp-check-to-separated-function.patch
new file mode 100644
index 0000000..75d8a4d
--- /dev/null
+++ b/iwlwifi_-move-plcp-check-to-separated-function.patch
@@ -0,0 +1,179 @@
+This patch is not upstream yet...
+
+From 171e0b730fd471b8df0d138daf382b8f6835fb18 Mon Sep 17 00:00:00 2001
+From: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+Date: Thu, 4 Mar 2010 13:38:58 -0800
+Subject: [PATCH 14/17] iwlwifi: move plcp check to separated function
+
+Move the plcp error checking into stand alone function and pointed by ops
+to accommodate devices not needing this recovery.
+
+Signed-off-by: Trieu 'Andrew' Nguyen <trieux.t.nguyen@intel.com>
+Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
+
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c.orig 2010-03-22 15:48:54.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-1000.c 2010-03-22 15:56:08.000000000 -0400
+@@ -136,6 +136,7 @@ static struct iwl_lib_ops iwl1000_lib =
+ .set_ct_kill = iwl1000_set_ct_threshold,
+ },
+ .recover_from_tx_stall = iwl_bg_monitor_recover,
++ .recover_from_statistics = iwl_recover_from_statistics,
+ };
+
+ static struct iwl_ops iwl1000_ops = {
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c.orig 2010-03-22 15:48:54.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-4965.c 2010-03-22 15:56:08.000000000 -0400
+@@ -2340,6 +2340,7 @@ static struct iwl_lib_ops iwl4965_lib =
+ .temperature = iwl4965_temperature_calib,
+ .set_ct_kill = iwl4965_set_ct_threshold,
+ },
++ .recover_from_statistics = iwl_recover_from_statistics,
+ };
+
+ static struct iwl_ops iwl4965_ops = {
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c.orig 2010-03-22 15:48:54.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-5000.c 2010-03-22 15:56:08.000000000 -0400
+@@ -1580,6 +1580,7 @@ struct iwl_lib_ops iwl5000_lib = {
+ .set_ct_kill = iwl5000_set_ct_threshold,
+ },
+ .recover_from_tx_stall = iwl_bg_monitor_recover,
++ .recover_from_statistics = iwl_recover_from_statistics,
+ };
+
+ static struct iwl_lib_ops iwl5150_lib = {
+@@ -1633,6 +1634,7 @@ static struct iwl_lib_ops iwl5150_lib =
+ .set_ct_kill = iwl5150_set_ct_threshold,
+ },
+ .recover_from_tx_stall = iwl_bg_monitor_recover,
++ .recover_from_statistics = iwl_recover_from_statistics,
+ };
+
+ struct iwl_ops iwl5000_ops = {
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c.orig 2010-03-22 15:51:12.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-6000.c 2010-03-22 15:56:08.000000000 -0400
+@@ -138,6 +138,7 @@ static struct iwl_lib_ops iwl6000_lib =
+ .set_ct_kill = iwl6000_set_ct_threshold,
+ },
+ .recover_from_tx_stall = iwl_bg_monitor_recover,
++ .recover_from_statistics = iwl_recover_from_statistics,
+ };
+
+ static struct iwl_hcmd_utils_ops iwl6000_hcmd_utils = {
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig 2010-03-22 15:48:54.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h 2010-03-22 15:56:08.000000000 -0400
+@@ -185,6 +185,9 @@ struct iwl_lib_ops {
+ struct iwl_temp_ops temp_ops;
+ /* recover from tx queue stall */
+ void (*recover_from_tx_stall)(unsigned long data);
++ /* recover from errors showed in statistics */
++ void (*recover_from_statistics)(struct iwl_priv *priv,
++ struct iwl_rx_packet *pkt);
+ };
+
+ struct iwl_ops {
+@@ -398,6 +401,8 @@ int iwl_tx_queue_reclaim(struct iwl_priv
+ /* Handlers */
+ void iwl_rx_missed_beacon_notif(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb);
++void iwl_recover_from_statistics(struct iwl_priv *priv,
++ struct iwl_rx_packet *pkt);
+ void iwl_rx_statistics(struct iwl_priv *priv,
+ struct iwl_rx_mem_buffer *rxb);
+ void iwl_rx_csa(struct iwl_priv *priv, struct iwl_rx_mem_buffer *rxb);
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c.orig 2010-03-22 15:24:28.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c 2010-03-22 16:02:35.000000000 -0400
+@@ -551,25 +551,18 @@ static void iwl_rx_calc_noise(struct iwl
+ #define REG_RECALIB_PERIOD (60)
+
+ #define PLCP_MSG "plcp_err exceeded %u, %u, %u, %u, %u, %d, %u mSecs\n"
+-void iwl_rx_statistics(struct iwl_priv *priv,
+- struct iwl_rx_mem_buffer *rxb)
++/*
++ * This function checks for plcp error.
++ * - When the plcp error is exceeding the thresholds, it will reset the radio
++ * to improve the throughput.
++ */
++void iwl_recover_from_statistics(struct iwl_priv *priv,
++ struct iwl_rx_packet *pkt)
+ {
+- int change;
+- struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
+ int combined_plcp_delta;
+ unsigned int plcp_msec;
+ unsigned long plcp_received_jiffies;
+
+- IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n",
+- (int)sizeof(priv->statistics),
+- le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK);
+-
+- change = ((priv->statistics.general.temperature !=
+- pkt->u.stats.general.temperature) ||
+- ((priv->statistics.flag &
+- STATISTICS_REPLY_FLG_HT40_MODE_MSK) !=
+- (pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK)));
+-
+ /*
+ * check for plcp_err and trigger radio reset if it exceeds
+ * the plcp error threshold plcp_delta.
+@@ -590,11 +583,11 @@ void iwl_rx_statistics(struct iwl_priv *
+ le32_to_cpu(priv->statistics.rx.ofdm_ht.plcp_err));
+
+ if ((combined_plcp_delta > 0) &&
+- ((combined_plcp_delta * 100) / plcp_msec) >
++ ((combined_plcp_delta * 100) / plcp_msec) >
+ priv->cfg->plcp_delta_threshold) {
+ /*
+- * if plcp_err exceed the threshold, the following
+- * data is printed in csv format:
++ * if plcp_err exceed the threshold,
++ * the following data is printed in csv format:
+ * Text: plcp_err exceeded %d,
+ * Received ofdm.plcp_err,
+ * Current ofdm.plcp_err,
+@@ -609,9 +602,8 @@ void iwl_rx_statistics(struct iwl_priv *
+ le32_to_cpu(priv->statistics.rx.ofdm.plcp_err),
+ le32_to_cpu(pkt->u.stats.rx.ofdm_ht.plcp_err),
+ le32_to_cpu(
+- priv->statistics.rx.ofdm_ht.plcp_err),
++ priv->statistics.rx.ofdm_ht.plcp_err),
+ combined_plcp_delta, plcp_msec);
+-
+ /*
+ * Reset the RF radio due to the high plcp
+ * error rate
+@@ -619,6 +611,28 @@ void iwl_rx_statistics(struct iwl_priv *
+ iwl_force_reset(priv, IWL_RF_RESET);
+ }
+ }
++}
++EXPORT_SYMBOL(iwl_recover_from_statistics);
++
++void iwl_rx_statistics(struct iwl_priv *priv,
++ struct iwl_rx_mem_buffer *rxb)
++{
++ int change;
++ struct iwl_rx_packet *pkt = (struct iwl_rx_packet *)rxb->skb->data;
++
++
++ IWL_DEBUG_RX(priv, "Statistics notification received (%d vs %d).\n",
++ (int)sizeof(priv->statistics),
++ le32_to_cpu(pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK);
++
++ change = ((priv->statistics.general.temperature !=
++ pkt->u.stats.general.temperature) ||
++ ((priv->statistics.flag &
++ STATISTICS_REPLY_FLG_HT40_MODE_MSK) !=
++ (pkt->u.stats.flag & STATISTICS_REPLY_FLG_HT40_MODE_MSK)));
++
++ if (priv->cfg->ops->lib->recover_from_statistics)
++ priv->cfg->ops->lib->recover_from_statistics(priv, pkt);
+
+ memcpy(&priv->statistics, &pkt->u.stats, sizeof(priv->statistics));
+
diff --git a/iwlwifi_-multiple-force-reset-mode.patch b/iwlwifi_-multiple-force-reset-mode.patch
new file mode 100644
index 0000000..adc3b24
--- /dev/null
+++ b/iwlwifi_-multiple-force-reset-mode.patch
@@ -0,0 +1,152 @@
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig 2010-03-22 16:37:23.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c 2010-03-22 16:39:46.000000000 -0400
+@@ -3035,7 +3035,7 @@ void iwl_update_stats(struct iwl_priv *p
+ EXPORT_SYMBOL(iwl_update_stats);
+ #endif
+
+-void iwl_force_rf_reset(struct iwl_priv *priv)
++static void iwl_force_rf_reset(struct iwl_priv *priv)
+ {
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+@@ -3057,7 +3057,47 @@ void iwl_force_rf_reset(struct iwl_priv
+ iwl_internal_short_hw_scan(priv);
+ return;
+ }
+-EXPORT_SYMBOL(iwl_force_rf_reset);
++
++#define IWL_DELAY_NEXT_FORCE_RESET (HZ*3)
++
++int iwl_force_reset(struct iwl_priv *priv, int mode)
++{
++ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
++ return -EINVAL;
++
++ if (priv->last_force_reset_jiffies &&
++ time_after(priv->last_force_reset_jiffies +
++ IWL_DELAY_NEXT_FORCE_RESET, jiffies)) {
++ IWL_DEBUG_INFO(priv, "force reset rejected\n");
++ return -EAGAIN;
++ }
++
++ IWL_DEBUG_INFO(priv, "perform force reset (%d)\n", mode);
++
++ switch (mode) {
++ case IWL_RF_RESET:
++ iwl_force_rf_reset(priv);
++ break;
++ case IWL_FW_RESET:
++ IWL_ERR(priv, "On demand firmware reload\n");
++ /* Set the FW error flag -- cleared on iwl_down */
++ set_bit(STATUS_FW_ERROR, &priv->status);
++ wake_up_interruptible(&priv->wait_command_queue);
++ /*
++ * Keep the restart process from trying to send host
++ * commands by clearing the INIT status bit
++ */
++ clear_bit(STATUS_READY, &priv->status);
++ queue_work(priv->workqueue, &priv->restart);
++ break;
++ default:
++ IWL_DEBUG_INFO(priv, "invalid reset request.\n");
++ return -EINVAL;
++ }
++ priv->last_force_reset_jiffies = jiffies;
++
++ return 0;
++}
+
+ #ifdef CONFIG_PM
+
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h.orig 2010-03-22 16:37:23.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.h 2010-03-22 16:39:46.000000000 -0400
+@@ -465,7 +465,7 @@ int iwl_scan_cancel(struct iwl_priv *pri
+ int iwl_scan_cancel_timeout(struct iwl_priv *priv, unsigned long ms);
+ int iwl_mac_hw_scan(struct ieee80211_hw *hw, struct cfg80211_scan_request *req);
+ int iwl_internal_short_hw_scan(struct iwl_priv *priv);
+-void iwl_force_rf_reset(struct iwl_priv *priv);
++int iwl_force_reset(struct iwl_priv *priv, int mode);
+ u16 iwl_fill_probe_req(struct iwl_priv *priv, struct ieee80211_mgmt *frame,
+ const u8 *ie, int ie_len, int left);
+ void iwl_setup_rx_scan_handlers(struct iwl_priv *priv);
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig 2010-03-22 16:37:23.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h 2010-03-22 16:39:46.000000000 -0400
+@@ -972,6 +972,11 @@ struct traffic_stats {
+ #define IWL_MAX_PLCP_ERR_LONG_THRESHOLD_DEF (100)
+ #define IWL_MAX_PLCP_ERR_THRESHOLD_MAX (255)
+
++enum iwl_reset {
++ IWL_RF_RESET = 0,
++ IWL_FW_RESET,
++};
++
+ struct iwl_priv {
+
+ /* ieee device used by generic ieee processing code */
+@@ -1003,6 +1008,9 @@ struct iwl_priv {
+ /* storing the jiffies when the plcp error rate is received */
+ unsigned long plcp_jiffies;
+
++ /* force reset */
++ unsigned long last_force_reset_jiffies;
++
+ /* we allocate array of iwl4965_channel_info for NIC's valid channels.
+ * Access via channel # using indirect index array */
+ struct iwl_channel_info *channel_info; /* channel info array */
+@@ -1025,7 +1033,6 @@ struct iwl_priv {
+ unsigned long scan_start;
+ unsigned long scan_pass_start;
+ unsigned long scan_start_tsf;
+- unsigned long last_internal_scan_jiffies;
+ void *scan;
+ int scan_bands;
+ struct cfg80211_scan_request *scan_request;
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c.orig 2010-03-22 16:37:23.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-rx.c 2010-03-22 16:39:46.000000000 -0400
+@@ -616,7 +616,7 @@ void iwl_rx_statistics(struct iwl_priv *
+ * Reset the RF radio due to the high plcp
+ * error rate
+ */
+- iwl_force_rf_reset(priv);
++ iwl_force_reset(priv, IWL_RF_RESET);
+ }
+ }
+
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c.orig 2010-03-22 16:37:23.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-scan.c 2010-03-22 16:39:46.000000000 -0400
+@@ -255,8 +255,6 @@ static void iwl_rx_scan_complete_notif(s
+
+ if (!priv->is_internal_short_scan)
+ priv->next_scan_jiffies = 0;
+- else
+- priv->last_internal_scan_jiffies = jiffies;
+
+ IWL_DEBUG_INFO(priv, "Setting scan to off\n");
+
+@@ -564,8 +562,6 @@ EXPORT_SYMBOL(iwl_mac_hw_scan);
+ * internal short scan, this function should only been called while associated.
+ * It will reset and tune the radio to prevent possible RF related problem
+ */
+-#define IWL_DELAY_NEXT_INTERNAL_SCAN (HZ*1)
+-
+ int iwl_internal_short_hw_scan(struct iwl_priv *priv)
+ {
+ int ret = 0;
+@@ -585,12 +581,6 @@ int iwl_internal_short_hw_scan(struct iw
+ ret = -EAGAIN;
+ goto out;
+ }
+- if (priv->last_internal_scan_jiffies &&
+- time_after(priv->last_internal_scan_jiffies +
+- IWL_DELAY_NEXT_INTERNAL_SCAN, jiffies)) {
+- IWL_DEBUG_SCAN(priv, "internal scan rejected\n");
+- goto out;
+- }
+
+ priv->scan_bands = 0;
+ if (priv->band == IEEE80211_BAND_5GHZ)
diff --git a/iwlwifi_-separated-time-check-for-different-type-of-force-reset.patch b/iwlwifi_-separated-time-check-for-different-type-of-force-reset.patch
new file mode 100644
index 0000000..0faa505
--- /dev/null
+++ b/iwlwifi_-separated-time-check-for-different-type-of-force-reset.patch
@@ -0,0 +1,120 @@
+Back-port of the following upstream commit...
+
+commit 8a472da431998b7357e6dc562e79a3061ed56cad
+Author: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+Date: Thu Feb 18 22:03:06 2010 -0800
+
+ iwlwifi: separated time check for different type of force reset
+
+ Use different timing duration check for different type of force reset,
+ force reset request can come from different source and based on
+ different reason; one type of reset request should not block other type of
+ reset request.
+
+ Adding structure to keep track of different force reset request.
+
+ Signed-off-by: Wey-Yi Guy <wey-yi.w.guy@intel.com>
+ Signed-off-by: Reinette Chatre <reinette.chatre@intel.com>
+ Signed-off-by: John W. Linville <linville@tuxdriver.com>
+
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-agn.c
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c.orig 2010-03-22 15:24:28.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-core.c 2010-03-22 15:40:48.000000000 -0400
+@@ -1497,6 +1497,12 @@ int iwl_init_drv(struct iwl_priv *priv)
+
+ priv->current_ht_config.sm_ps = WLAN_HT_CAP_SM_PS_DISABLED;
+
++ /* initialize force reset */
++ priv->force_reset[IWL_RF_RESET].reset_duration =
++ IWL_DELAY_NEXT_FORCE_RF_RESET;
++ priv->force_reset[IWL_FW_RESET].reset_duration =
++ IWL_DELAY_NEXT_FORCE_FW_RELOAD;
++
+ /* Choose which receivers/antennas to use */
+ if (priv->cfg->ops->hcmd->set_rxon_chain)
+ priv->cfg->ops->hcmd->set_rxon_chain(priv);
+@@ -3058,22 +3064,30 @@ static void iwl_force_rf_reset(struct iw
+ return;
+ }
+
+-#define IWL_DELAY_NEXT_FORCE_RESET (HZ*3)
+
+ int iwl_force_reset(struct iwl_priv *priv, int mode)
+ {
++ struct iwl_force_reset *force_reset;
++
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return -EINVAL;
+
+- if (priv->last_force_reset_jiffies &&
+- time_after(priv->last_force_reset_jiffies +
+- IWL_DELAY_NEXT_FORCE_RESET, jiffies)) {
++ if (mode >= IWL_MAX_FORCE_RESET) {
++ IWL_DEBUG_INFO(priv, "invalid reset request.\n");
++ return -EINVAL;
++ }
++ force_reset = &priv->force_reset[mode];
++ force_reset->reset_request_count++;
++ if (force_reset->last_force_reset_jiffies &&
++ time_after(force_reset->last_force_reset_jiffies +
++ force_reset->reset_duration, jiffies)) {
+ IWL_DEBUG_INFO(priv, "force reset rejected\n");
++ force_reset->reset_reject_count++;
+ return -EAGAIN;
+ }
+-
++ force_reset->reset_success_count++;
++ force_reset->last_force_reset_jiffies = jiffies;
+ IWL_DEBUG_INFO(priv, "perform force reset (%d)\n", mode);
+-
+ switch (mode) {
+ case IWL_RF_RESET:
+ iwl_force_rf_reset(priv);
+@@ -3090,12 +3104,7 @@ int iwl_force_reset(struct iwl_priv *pri
+ clear_bit(STATUS_READY, &priv->status);
+ queue_work(priv->workqueue, &priv->restart);
+ break;
+- default:
+- IWL_DEBUG_INFO(priv, "invalid reset request.\n");
+- return -EINVAL;
+ }
+- priv->last_force_reset_jiffies = jiffies;
+-
+ return 0;
+ }
+
+diff -up linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h
+--- linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h.orig 2010-03-22 15:33:00.000000000 -0400
++++ linux-2.6.32.noarch/drivers/net/wireless/iwlwifi/iwl-dev.h 2010-03-22 15:37:04.000000000 -0400
+@@ -973,9 +973,21 @@ struct traffic_stats {
+ #define IWL_MAX_PLCP_ERR_EXT_LONG_THRESHOLD_DEF (200)
+ #define IWL_MAX_PLCP_ERR_THRESHOLD_MAX (255)
+
++#define IWL_DELAY_NEXT_FORCE_RF_RESET (HZ*3)
++#define IWL_DELAY_NEXT_FORCE_FW_RELOAD (HZ*5)
++
+ enum iwl_reset {
+ IWL_RF_RESET = 0,
+ IWL_FW_RESET,
++ IWL_MAX_FORCE_RESET,
++};
++
++struct iwl_force_reset {
++ int reset_request_count;
++ int reset_success_count;
++ int reset_reject_count;
++ unsigned long reset_duration;
++ unsigned long last_force_reset_jiffies;
+ };
+
+ struct iwl_priv {
+@@ -1010,7 +1022,7 @@ struct iwl_priv {
+ unsigned long plcp_jiffies;
+
+ /* force reset */
+- unsigned long last_force_reset_jiffies;
++ struct iwl_force_reset force_reset[IWL_MAX_FORCE_RESET];
+
+ /* we allocate array of iwl4965_channel_info for NIC's valid channels.
+ * Access via channel # using indirect index array */
diff --git a/kernel.spec b/kernel.spec
new file mode 100644
index 0000000..113b346
--- /dev/null
+++ b/kernel.spec
@@ -0,0 +1,4423 @@
+# We have to override the new %%install behavior because, well... the kernel is special.
+%global __spec_install_pre %{___build_pre}
+
+Summary: The Linux kernel
+
+# For a stable, released kernel, released_kernel should be 1. For rawhide
+# and/or a kernel built from an rc or git snapshot, released_kernel should
+# be 0.
+%global released_kernel 1
+
+# Save original buildid for later if it's defined
+%if 0%{?buildid:1}
+%global orig_buildid %{buildid}
+%undefine buildid
+%endif
+
+###################################################################
+# Polite request for people who spin their own kernel rpms:
+# please modify the "buildid" define in a way that identifies
+# that the kernel isn't the stock distribution kernel, for example,
+# by setting the define to ".local" or ".bz123456". This will be
+# appended to the full kernel version.
+#
+# (Uncomment the '#' and the first two spaces below to set buildid.)
+# % define buildid .local
+###################################################################
+
+# The buildid can also be specified on the rpmbuild command line
+# by adding --define="buildid .whatever". If both the specfile and
+# the environment define a buildid they will be concatenated together.
+%if 0%{?orig_buildid:1}
+%if 0%{?buildid:1}
+%global srpm_buildid %{buildid}
+%define buildid %{srpm_buildid}%{orig_buildid}
+%else
+%define buildid %{orig_buildid}
+%endif
+%endif
+
+# fedora_build defines which build revision of this kernel version we're
+# building. Rather than incrementing forever, as with the prior versioning
+# setup, we set fedora_cvs_origin to the current cvs revision s/1.// of the
+# kernel spec when the kernel is rebased, so fedora_build automatically
+# works out to the offset from the rebase, so it doesn't get too ginormous.
+#
+# If you're building on a branch, the RCS revision will be something like
+# 1.1205.1.1. In this case we drop the initial 1, subtract fedora_cvs_origin
+# from the second number, and then append the rest of the RCS string as is.
+# Don't stare at the awk too long, you'll go blind.
+%define fedora_cvs_origin 1962
+%define fedora_cvs_revision() %2
+%global fedora_build %(echo %{fedora_cvs_origin}.%{fedora_cvs_revision $Revision: 1.2116 $} | awk -F . '{ OFS = "."; ORS = ""; print $3 - $1 ; i = 4 ; OFS = ""; while (i <= NF) { print ".", $i ; i++} }')
+
+# base_sublevel is the kernel version we're starting with and patching
+# on top of -- for example, 2.6.22-rc7-git1 starts with a 2.6.21 base,
+# which yields a base_sublevel of 21.
+%define base_sublevel 32
+
+## If this is a released kernel ##
+%if 0%{?released_kernel}
+
+# Do we have a -stable update to apply?
+%define stable_update 16
+# Is it a -stable RC?
+%define stable_rc 0
+# Set rpm version accordingly
+%if 0%{?stable_update}
+%define stablerev .%{stable_update}
+%define stable_base %{stable_update}
+%if 0%{?stable_rc}
+# stable RCs are incremental patches, so we need the previous stable patch
+%define stable_base %(echo $((%{stable_update} - 1)))
+%endif
+%endif
+%define rpmversion 2.6.%{base_sublevel}%{?stablerev}
+
+## The not-released-kernel case ##
+%else
+# The next upstream release sublevel (base_sublevel+1)
+%define upstream_sublevel %(echo $((%{base_sublevel} + 1)))
+# The rc snapshot level
+%define rcrev 0
+# The git snapshot level
+%define gitrev 0
+# Set rpm version accordingly
+%define rpmversion 2.6.%{upstream_sublevel}
+%endif
+# Nb: The above rcrev and gitrev values automagically define Patch00 and Patch01 below.
+
+# What parts do we want to build? We must build at least one kernel.
+# These are the kernels that are built IF the architecture allows it.
+# All should default to 1 (enabled) and be flipped to 0 (disabled)
+# by later arch-specific checks.
+
+# The following build options are enabled by default.
+# Use either --without <opt> in your rpmbuild command or force values
+# to 0 in here to disable them.
+#
+# standard kernel
+%define with_up %{?_without_up: 0} %{?!_without_up: 1}
+# kernel-smp (only valid for ppc 32-bit)
+%define with_smp %{?_without_smp: 0} %{?!_without_smp: 1}
+# kernel-kdump
+%define with_kdump %{?_without_kdump: 0} %{?!_without_kdump: 1}
+# kernel-debug
+%define with_debug %{?_without_debug: 0} %{?!_without_debug: 1}
+# kernel-doc
+%define with_doc %{?_without_doc: 0} %{?!_without_doc: 1}
+# kernel-headers
+%define with_headers %{?_without_headers: 0} %{?!_without_headers: 1}
+# kernel-firmware
+%define with_firmware %{?_with_firmware: 1} %{?!_with_firmware: 0}
+# tools/perf
+%define with_perftool %{?_without_perftool: 0} %{?!_without_perftool: 1}
+# perf noarch subpkg
+%define with_perf %{?_without_perf: 0} %{?!_without_perf: 1}
+# kernel-debuginfo
+%define with_debuginfo %{?_without_debuginfo: 0} %{?!_without_debuginfo: 1}
+# kernel-bootwrapper (for creating zImages from kernel + initrd)
+%define with_bootwrapper %{?_without_bootwrapper: 0} %{?!_without_bootwrapper: 1}
+# Want to build a the vsdo directories installed
+%define with_vdso_install %{?_without_vdso_install: 0} %{?!_without_vdso_install: 1}
+# Use dracut instead of mkinitrd for initrd image generation
+%define with_dracut %{?_without_dracut: 0} %{?!_without_dracut: 1}
+
+# Build the kernel-doc package, but don't fail the build if it botches.
+# Here "true" means "continue" and "false" means "fail the build".
+%if 0%{?released_kernel}
+%define doc_build_fail false
+%else
+%define doc_build_fail true
+%endif
+
+%define rawhide_skip_docs 0
+%if 0%{?rawhide_skip_docs}
+%define with_doc 0
+%endif
+
+# Additional options for user-friendly one-off kernel building:
+#
+# Only build the base kernel (--with baseonly):
+%define with_baseonly %{?_with_baseonly: 1} %{?!_with_baseonly: 0}
+# Only build the smp kernel (--with smponly):
+%define with_smponly %{?_with_smponly: 1} %{?!_with_smponly: 0}
+# Only build the debug kernel (--with dbgonly):
+%define with_dbgonly %{?_with_dbgonly: 1} %{?!_with_dbgonly: 0}
+
+# should we do C=1 builds with sparse
+%define with_sparse %{?_with_sparse: 1} %{?!_with_sparse: 0}
+
+# Set debugbuildsenabled to 1 for production (build separate debug kernels)
+# and 0 for rawhide (all kernels are debug kernels).
+# See also 'make debug' and 'make release'.
+%define debugbuildsenabled 1
+
+# Want to build a vanilla kernel build without any non-upstream patches?
+# (well, almost none, we need nonintconfig for build purposes). Default to 0 (off).
+%define with_vanilla %{?_with_vanilla: 1} %{?!_with_vanilla: 0}
+
+# pkg_release is what we'll fill in for the rpm Release: field
+%if 0%{?released_kernel}
+
+%if 0%{?stable_rc}
+%define stable_rctag .rc%{stable_rc}
+%endif
+%define pkg_release %{fedora_build}%{?stable_rctag}%{?buildid}%{?dist}
+
+%else
+
+# non-released_kernel
+%if 0%{?rcrev}
+%define rctag .rc%rcrev
+%else
+%define rctag .rc0
+%endif
+%if 0%{?gitrev}
+%define gittag .git%gitrev
+%else
+%define gittag .git0
+%endif
+%define pkg_release 0.%{fedora_build}%{?rctag}%{?gittag}%{?buildid}%{?dist}
+
+%endif
+
+# The kernel tarball/base version
+%define kversion 2.6.%{base_sublevel}
+
+%define make_target bzImage
+
+%define KVERREL %{version}-%{release}.%{_target_cpu}
+%define hdrarch %_target_cpu
+%define asmarch %_target_cpu
+
+%if 0%{!?nopatches:1}
+%define nopatches 0
+%endif
+
+%if %{with_vanilla}
+%define nopatches 1
+%endif
+
+%if %{nopatches}
+%define with_bootwrapper 0
+%define variant -vanilla
+%else
+%define variant_fedora -fedora
+%endif
+
+%define using_upstream_branch 0
+%if 0%{?upstream_branch:1}
+%define stable_update 0
+%define using_upstream_branch 1
+%define variant -%{upstream_branch}%{?variant_fedora}
+%define pkg_release 0.%{fedora_build}%{upstream_branch_tag}%{?buildid}%{?dist}
+%endif
+
+%if !%{debugbuildsenabled}
+%define with_debug 0
+%endif
+
+%if !%{with_debuginfo}
+%define _enable_debug_packages 0
+%endif
+%define debuginfodir /usr/lib/debug
+
+# kernel-PAE is only built on i686.
+%ifarch i686
+%define with_pae 1
+%else
+%define with_pae 0
+%endif
+
+# if requested, only build base kernel
+%if %{with_baseonly}
+%define with_smp 0
+%define with_kdump 0
+%define with_debug 0
+%endif
+
+# if requested, only build smp kernel
+%if %{with_smponly}
+%define with_up 0
+%define with_kdump 0
+%define with_debug 0
+%endif
+
+# if requested, only build debug kernel
+%if %{with_dbgonly}
+%if %{debugbuildsenabled}
+%define with_up 0
+%endif
+%define with_smp 0
+%define with_pae 0
+%define with_xen 0
+%define with_kdump 0
+%define with_perftool 0
+%endif
+
+%define all_x86 i386 i686
+
+%if %{with_vdso_install}
+# These arches install vdso/ directories.
+%define vdso_arches %{all_x86} x86_64 ppc ppc64
+%endif
+
+# Overrides for generic default options
+
+# only ppc and alphav56 need separate smp kernels
+%ifnarch ppc alphaev56
+%define with_smp 0
+%endif
+
+# only build kernel-kdump on ppc64
+# (no relocatable kernel support upstream yet)
+#FIXME: Temporarily disabled to speed up builds.
+#ifnarch ppc64
+%define with_kdump 0
+#endif
+
+# don't do debug builds on anything but i686 and x86_64
+%ifnarch i686 x86_64
+%define with_debug 0
+%endif
+
+# only package docs noarch
+%ifnarch noarch
+%define with_doc 0
+%define with_perf 0
+%endif
+
+# don't build noarch kernels or headers (duh)
+%ifarch noarch
+%define with_up 0
+%define with_headers 0
+%define all_arch_configs kernel-%{version}-*.config
+%define with_firmware %{?_without_firmware: 0} %{?!_without_firmware: 1}
+%endif
+
+# bootwrapper is only on ppc
+%ifnarch ppc ppc64
+%define with_bootwrapper 0
+%endif
+
+# sparse blows up on ppc64 alpha and sparc64
+%ifarch ppc64 ppc alpha sparc64
+%define with_sparse 0
+%endif
+
+# Per-arch tweaks
+
+%ifarch %{all_x86}
+%define asmarch x86
+%define hdrarch i386
+%define all_arch_configs kernel-%{version}-i?86*.config
+%define image_install_path boot
+%define kernel_image arch/x86/boot/bzImage
+%endif
+
+%ifarch x86_64
+%define asmarch x86
+%define all_arch_configs kernel-%{version}-x86_64*.config
+%define image_install_path boot
+%define kernel_image arch/x86/boot/bzImage
+%endif
+
+%ifarch ppc64
+%define asmarch powerpc
+%define hdrarch powerpc
+%define all_arch_configs kernel-%{version}-ppc64*.config
+%define image_install_path boot
+%define make_target vmlinux
+%define kernel_image vmlinux
+%define kernel_image_elf 1
+%endif
+
+%ifarch s390x
+%define asmarch s390
+%define hdrarch s390
+%define all_arch_configs kernel-%{version}-s390x.config
+%define image_install_path boot
+%define make_target image
+%define kernel_image arch/s390/boot/image
+%endif
+
+%ifarch sparc
+# We only build sparc headers since we dont support sparc32 hardware
+%endif
+
+%ifarch sparc64
+%define asmarch sparc
+%define all_arch_configs kernel-%{version}-sparc64*.config
+%define make_target image
+%define kernel_image arch/sparc/boot/image
+%define image_install_path boot
+%define with_perftool 0
+%endif
+
+%ifarch ppc
+%define asmarch powerpc
+%define hdrarch powerpc
+%define all_arch_configs kernel-%{version}-ppc{-,.}*config
+%define image_install_path boot
+%define make_target vmlinux
+%define kernel_image vmlinux
+%define kernel_image_elf 1
+%endif
+
+%ifarch ia64
+%define all_arch_configs kernel-%{version}-ia64*.config
+%define image_install_path boot/efi/EFI/redhat
+%define make_target compressed
+%define kernel_image vmlinux.gz
+%endif
+
+%ifarch alpha alphaev56
+%define all_arch_configs kernel-%{version}-alpha*.config
+%define image_install_path boot
+%define make_target vmlinux
+%define kernel_image vmlinux
+%endif
+
+%ifarch %{arm}
+%define all_arch_configs kernel-%{version}-arm*.config
+%define image_install_path boot
+%define hdrarch arm
+%define make_target vmlinux
+%define kernel_image vmlinux
+%endif
+
+%if %{nopatches}
+# XXX temporary until last vdso patches are upstream
+%define vdso_arches ppc ppc64
+%endif
+
+%if %{nopatches}%{using_upstream_branch}
+# Ignore unknown options in our config-* files.
+# Some options go with patches we're not applying.
+%define oldconfig_target loose_nonint_oldconfig
+%else
+%define oldconfig_target nonint_oldconfig
+%endif
+
+# To temporarily exclude an architecture from being built, add it to
+# %nobuildarches. Do _NOT_ use the ExclusiveArch: line, because if we
+# don't build kernel-headers then the new build system will no longer let
+# us use the previous build of that package -- it'll just be completely AWOL.
+# Which is a BadThing(tm).
+
+# We don't build a kernel on i386; we only do kernel-headers there,
+# and we no longer build for 31bit S390. Same for 32bit sparc and arm.
+%define nobuildarches i386 s390 sparc %{arm}
+
+%ifarch %nobuildarches
+%define with_up 0
+%define with_smp 0
+%define with_pae 0
+%define with_kdump 0
+%define with_debuginfo 0
+%define with_perftool 0
+%define _enable_debug_packages 0
+%endif
+
+%define with_pae_debug 0
+%if %{with_pae}
+%define with_pae_debug %{with_debug}
+%endif
+
+#
+# Three sets of minimum package version requirements in the form of Conflicts:
+# to versions below the minimum
+#
+
+#
+# First the general kernel 2.6 required versions as per
+# Documentation/Changes
+#
+%define kernel_dot_org_conflicts ppp < 2.4.3-3, isdn4k-utils < 3.2-32, nfs-utils < 1.0.7-12, e2fsprogs < 1.37-4, util-linux < 2.12, jfsutils < 1.1.7-2, reiserfs-utils < 3.6.19-2, xfsprogs < 2.6.13-4, procps < 3.2.5-6.3, oprofile < 0.9.1-2
+
+#
+# Then a series of requirements that are distribution specific, either
+# because we add patches for something, or the older versions have
+# problems with the newer kernel or lack certain things that make
+# integration in the distro harder than needed.
+#
+%define package_conflicts initscripts < 7.23, udev < 063-6, iptables < 1.3.2-1, ipw2200-firmware < 2.4, iwl4965-firmware < 228.57.2, selinux-policy-targeted < 1.25.3-14, squashfs-tools < 4.0, wireless-tools < 29-3
+
+#
+# The ld.so.conf.d file we install uses syntax older ldconfig's don't grok.
+#
+%define kernel_xen_conflicts glibc < 2.3.5-1, xen < 3.0.1
+
+%define kernel_PAE_obsoletes kernel-smp < 2.6.17, kernel-xen <= 2.6.27-0.2.rc0.git6.fc10
+%define kernel_PAE_provides kernel-xen = %{rpmversion}-%{pkg_release}
+
+%ifarch x86_64
+%define kernel_obsoletes kernel-xen <= 2.6.27-0.2.rc0.git6.fc10
+%define kernel_provides kernel-xen = %{rpmversion}-%{pkg_release}
+%endif
+
+# We moved the drm include files into kernel-headers, make sure there's
+# a recent enough libdrm-devel on the system that doesn't have those.
+%define kernel_headers_conflicts libdrm-devel < 2.4.0-0.15
+
+#
+# Packages that need to be installed before the kernel is, because the %post
+# scripts use them.
+#
+%define kernel_prereq fileutils, module-init-tools, initscripts >= 8.11.1-1, kernel-firmware >= %{rpmversion}-%{pkg_release}, grubby >= 7.0.4-1
+%if %{with_dracut}
+%define initrd_prereq dracut >= 002 xorg-x11-drv-ati-firmware
+%else
+%define initrd_prereq mkinitrd >= 6.0.61-1
+%endif
+
+#
+# This macro does requires, provides, conflicts, obsoletes for a kernel package.
+# %%kernel_reqprovconf <subpackage>
+# It uses any kernel_<subpackage>_conflicts and kernel_<subpackage>_obsoletes
+# macros defined above.
+#
+%define kernel_reqprovconf \
+Provides: kernel = %{rpmversion}-%{pkg_release}\
+Provides: kernel-%{_target_cpu} = %{rpmversion}-%{pkg_release}%{?1:.%{1}}\
+Provides: kernel-drm = 4.3.0\
+Provides: kernel-drm-nouveau = 15\
+Provides: kernel-modeset = 1\
+Provides: kernel-uname-r = %{KVERREL}%{?1:.%{1}}\
+Requires(pre): %{kernel_prereq}\
+Requires(pre): %{initrd_prereq}\
+Requires(post): /sbin/new-kernel-pkg\
+Requires(preun): /sbin/new-kernel-pkg\
+Conflicts: %{kernel_dot_org_conflicts}\
+Conflicts: %{package_conflicts}\
+%{expand:%%{?kernel%{?1:_%{1}}_conflicts:Conflicts: %%{kernel%{?1:_%{1}}_conflicts}}}\
+%{expand:%%{?kernel%{?1:_%{1}}_obsoletes:Obsoletes: %%{kernel%{?1:_%{1}}_obsoletes}}}\
+%{expand:%%{?kernel%{?1:_%{1}}_provides:Provides: %%{kernel%{?1:_%{1}}_provides}}}\
+# We can't let RPM do the dependencies automatic because it'll then pick up\
+# a correct but undesirable perl dependency from the module headers which\
+# isn't required for the kernel proper to function\
+AutoReq: no\
+AutoProv: yes\
+%{nil}
+
+Name: kernel%{?variant}
+Group: System Environment/Kernel
+License: GPLv2
+URL: http://www.kernel.org/
+Version: %{rpmversion}
+Release: %{pkg_release}
+# DO NOT CHANGE THE 'ExclusiveArch' LINE TO TEMPORARILY EXCLUDE AN ARCHITECTURE BUILD.
+# SET %%nobuildarches (ABOVE) INSTEAD
+ExclusiveArch: noarch %{all_x86} x86_64 ppc ppc64 ia64 sparc sparc64 s390x alpha alphaev56 %{arm}
+ExclusiveOS: Linux
+
+%kernel_reqprovconf
+%ifarch x86_64 sparc64
+Obsoletes: kernel-smp
+%endif
+
+
+#
+# List the packages used during the kernel build
+#
+BuildRequires: module-init-tools, patch >= 2.5.4, bash >= 2.03, sh-utils, tar
+BuildRequires: bzip2, findutils, gzip, m4, perl, make >= 3.78, diffutils, gawk
+BuildRequires: gcc >= 3.4.2, binutils >= 2.12, redhat-rpm-config
+BuildRequires: net-tools
+BuildRequires: xmlto, asciidoc
+%if %{with_sparse}
+BuildRequires: sparse >= 0.4.1
+%endif
+%if %{with_perftool}
+BuildRequires: elfutils-libelf-devel zlib-devel binutils-devel
+%endif
+BuildConflicts: rhbuildsys(DiskFree) < 500Mb
+
+%define fancy_debuginfo 0
+%if %{with_debuginfo}
+%if 0%{?fedora} >= 8 || 0%{?rhel} >= 6
+%define fancy_debuginfo 1
+%endif
+%endif
+
+%if %{fancy_debuginfo}
+# Fancy new debuginfo generation introduced in Fedora 8.
+BuildRequires: rpm-build >= 4.4.2.1-4
+%define debuginfo_args --strict-build-id
+%endif
+
+Source0: ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-%{kversion}.tar.bz2
+
+Source11: genkey
+Source14: find-provides
+Source15: merge.pl
+
+Source20: Makefile.config
+Source21: config-debug
+Source22: config-nodebug
+Source23: config-generic
+Source24: config-rhel-generic
+
+Source30: config-x86-generic
+Source31: config-i686-PAE
+
+Source40: config-x86_64-generic
+
+Source50: config-powerpc-generic
+Source51: config-powerpc32-generic
+Source52: config-powerpc32-smp
+Source53: config-powerpc64
+
+Source60: config-ia64-generic
+
+Source70: config-s390x
+
+Source90: config-sparc64-generic
+
+Source100: config-arm
+
+Source200: perf
+
+# Here should be only the patches up to the upstream canonical Linus tree.
+
+# For a stable release kernel
+%if 0%{?stable_update}
+%if 0%{?stable_base}
+%define stable_patch_00 patch-2.6.%{base_sublevel}.%{stable_base}.bz2
+Patch00: %{stable_patch_00}
+%endif
+%if 0%{?stable_rc}
+%define stable_patch_01 patch-2.6.%{base_sublevel}.%{stable_update}-rc%{stable_rc}.bz2
+Patch01: %{stable_patch_01}
+%endif
+
+# non-released_kernel case
+# These are automagically defined by the rcrev and gitrev values set up
+# near the top of this spec file.
+%else
+%if 0%{?rcrev}
+Patch00: patch-2.6.%{upstream_sublevel}-rc%{rcrev}.bz2
+%if 0%{?gitrev}
+Patch01: patch-2.6.%{upstream_sublevel}-rc%{rcrev}-git%{gitrev}.bz2
+%endif
+%else
+# pre-{base_sublevel+1}-rc1 case
+%if 0%{?gitrev}
+Patch00: patch-2.6.%{base_sublevel}-git%{gitrev}.bz2
+%endif
+%endif
+%endif
+
+%if %{using_upstream_branch}
+### BRANCH PATCH ###
+%endif
+
+Patch02: git-linus.diff
+
+# we always need nonintconfig, even for -vanilla kernels
+Patch03: linux-2.6-build-nonintconfig.patch
+
+# we also need compile fixes for -vanilla
+Patch04: linux-2.6-compile-fixes.patch
+
+# build tweak for build ID magic, even for -vanilla
+Patch05: linux-2.6-makefile-after_link.patch
+
+%if !%{nopatches}
+
+# revert upstream patches we get via other methods
+Patch09: linux-2.6-upstream-reverts.patch
+# Git trees.
+Patch10: git-cpufreq.patch
+Patch11: git-bluetooth.patch
+
+# Standalone patches
+Patch20: linux-2.6-hotfixes.patch
+
+Patch21: linux-2.6-tracehook.patch
+Patch22: linux-2.6-utrace.patch
+Patch23: linux-2.6-utrace-ptrace.patch
+
+Patch141: linux-2.6-ps3-storage-alias.patch
+Patch143: linux-2.6-g5-therm-shutdown.patch
+Patch144: linux-2.6-vio-modalias.patch
+Patch147: linux-2.6-imac-transparent-bridge.patch
+
+Patch150: linux-2.6.29-sparc-IOC_TYPECHECK.patch
+
+Patch160: linux-2.6-execshield.patch
+
+Patch250: linux-2.6-debug-sizeof-structs.patch
+Patch260: linux-2.6-debug-nmi-timeout.patch
+Patch270: linux-2.6-debug-taint-vm.patch
+Patch300: linux-2.6-driver-level-usb-autosuspend.diff
+Patch303: linux-2.6-enable-btusb-autosuspend.patch
+Patch304: linux-2.6-usb-uvc-autosuspend.diff
+Patch305: linux-2.6-usb-wwan-update.patch
+
+Patch310: linux-2.6-autoload-wmi.patch
+# wmi autoload fixes
+Patch311: wmi-check-find_guid-return-value-to-prevent-oops.patch
+Patch312: wmi-survive-bios-with-duplicate-guids.patch
+
+Patch340: linux-2.6-debug-vm-would-have-oomkilled.patch
+Patch360: linux-2.6-debug-always-inline-kzalloc.patch
+Patch380: linux-2.6-defaults-pci_no_msi.patch
+Patch381: linux-2.6-pciehp-update.patch
+Patch382: linux-2.6-defaults-pciehp.patch
+Patch383: linux-2.6-defaults-aspm.patch
+Patch384: pci-acpi-disable-aspm-if-no-osc.patch
+Patch385: pci-aspm-dont-enable-too-early.patch
+
+Patch390: linux-2.6-defaults-acpi-video.patch
+Patch391: linux-2.6-acpi-video-dos.patch
+Patch450: linux-2.6-input-kill-stupid-messages.patch
+Patch451: linux-2.6-input-fix-toshiba-hotkeys.patch
+Patch452: linux-2.6.30-no-pcspkr-modalias.patch
+Patch454: linux-2.6-input-hid-quirk-egalax.patch
+Patch455: thinkpad-acpi-add-x100e.patch
+Patch456: thinkpad-acpi-fix-backlight.patch
+
+Patch460: linux-2.6-serial-460800.patch
+
+Patch470: die-floppy-die.patch
+
+Patch510: linux-2.6-silence-noise.patch
+Patch520: linux-2.6.30-hush-rom-warning.patch
+Patch530: linux-2.6-silence-fbcon-logo.patch
+Patch531: viafb-neuter-device-table.patch
+Patch570: linux-2.6-selinux-mprotect-checks.patch
+Patch580: linux-2.6-sparc-selinux-mprotect-checks.patch
+
+Patch600: linux-2.6-defaults-alsa-hda-beep-off.patch
+Patch610: hda_intel-prealloc-4mb-dmabuffer.patch
+
+Patch670: linux-2.6-ata-quirk.patch
+
+Patch701: sky2-optima-add-register-definitions.patch
+Patch702: sky2-optima-support.patch
+Patch703: sky2-optima-fix-tcp-offload.patch
+Patch704: sky2-optima-print-chip-name.patch
+Patch705: sky2-optima-add-missing-write-bits.patch
+
+Patch800: linux-2.6-crash-driver.patch
+
+Patch900: linux-2.6-pci-cacheline-sizing.patch
+
+Patch1515: lirc-2.6.32.patch
+Patch1517: hdpvr-ir-enable.patch
+Patch1520: crystalhd-2.6.34-staging.patch
+
+# virt + ksm patches
+Patch1551: linux-2.6-ksm-kvm.patch
+
+# fbdev multi-card fix
+Patch1700: linux-2.6-x86-64-fbdev-primary.patch
+
+# nouveau + drm fixes
+Patch1810: drm-upgrayedd.patch
+Patch1813: drm-radeon-pm.patch
+#Patch1814: drm-nouveau.patch
+Patch1818: drm-i915-resume-force-mode.patch
+Patch1819: drm-intel-big-hammer.patch
+Patch1820: drm-intel-no-tv-hotplug.patch
+Patch1821: drm-i915-fix-hibernate-memory-corruption.patch
+Patch1822: drm-i915-add-reclaimable-to-page-allocations.patch
+Patch1823: drm-intel-945gm-stability-fixes.patch
+# intel drm is all merged upstream
+Patch1824: drm-intel-next.patch
+Patch1825: drm-intel-acpi-populate-didl.patch
+Patch1826: drm-intel-make-lvds-work.patch
+#Patch1827: linux-2.6-intel-agp-clear-gtt.patch
+Patch1828: drm-nouveau-g80-ctxprog.patch
+Patch1831: drm-nouveau-tvout-disable.patch
+Patch1832: drm-nouveau-safetile-getparam.patch
+Patch1844: drm-nouveau-kconfig.patch
+Patch1845: drm-nouveau-mutex.patch
+Patch1846: drm-nouveau-update.patch
+Patch1847: drm-nouveau-d620.patch
+
+# kludge to make ich9 e1000 work
+Patch2000: linux-2.6-e1000-ich9.patch
+
+Patch2100: linux-2.6-phylib-autoload.patch
+
+# linux1394 git patches
+Patch2200: linux-2.6-firewire-git-update.patch
+Patch2201: linux-2.6-firewire-git-pending.patch
+
+# Quiet boot fixes
+# silence the ACPI blacklist code
+Patch2802: linux-2.6-silence-acpi-blacklist.patch
+
+Patch2899: linux-2.6-v4l-dvb-fixes.patch
+Patch2900: linux-2.6-v4l-dvb-update.patch
+Patch2901: linux-2.6-v4l-dvb-experimental.patch
+Patch2903: linux-2.6-revert-dvb-net-kabi-change.patch
+Patch2904: linux-2.6-v4l-dvb-rebase-gspca-to-latest.patch
+Patch2905: linux-2.6-v4l-dvb-add-lgdt3304-support.patch
+Patch2096: linux-2.6-v4l-dvb-add-kworld-a340-support.patch
+
+# fs fixes
+
+# ext4/quota
+Patch3000: linux-2.6-ext4-quota-metadata-reservation.patch
+
+# NFSv4
+Patch3050: linux-2.6-nfsd4-proots.patch
+Patch3051: linux-2.6-nfs4-callback-hidden.patch
+
+# btrfs
+Patch3100: linux-2.6-btrfs-fix-acl.patch
+Patch3101: btrfs-prohibit-a-operation-of-changing-acls-mask-when-noacl-mount-option-is-used.patch
+
+# XFS
+
+# VIA Nano / VX8xx updates
+Patch11010: via-hwmon-temp-sensor.patch
+
+# patches headed upstream
+Patch12010: linux-2.6-dell-laptop-rfkill-fix.patch
+Patch12011: linux-2.6-block-silently-error-unsupported-empty-barriers-too.patch
+Patch12013: linux-2.6-rfkill-all.patch
+Patch12020: linux-2.6-cantiga-iommu-gfx.patch
+
+Patch12200: add-appleir-usb-driver.patch
+
+# Patches for -stable
+
+# ACPI EC bugfixes (not all upstream)
+Patch12250: acpi-ec-add-delay-before-write.patch
+
+Patch12311: fix-ima-null-ptr-deref.patch
+
+Patch12315: fix-abrtd.patch
+
+# rhbz#/566565
+Patch12340: ice1712-fix-revo71-mixer-names.patch
+
+# rhbz#572653
+Patch12370: linux-2.6-b43_-Rewrite-DMA-Tx-status-handling-sanity-checks.patch
+
+# rhbz#533746
+Patch12380: ssb_check_for_sprom.patch
+
+# backport iwlwifi fixes (thanks, sgruszka!) -- drop when stable catches-up
+Patch12391: iwlwifi-reset-card-during-probe.patch
+
+# patches from Intel to address intermittent firmware failures with iwlagn
+Patch12404: iwlwifi_-add-function-to-reset_tune-radio-if-needed.patch
+Patch12405: iwlwifi_-Logic-to-control-how-frequent-radio-should-be-reset-if-needed.patch
+Patch12406: iwlwifi_-Tune-radio-to-prevent-unexpected-behavior.patch
+Patch12407: iwlwifi_-multiple-force-reset-mode.patch
+Patch12409: iwlwifi_-Adjusting-PLCP-error-threshold-for-1000-NIC.patch
+Patch12410: iwlwifi_-separated-time-check-for-different-type-of-force-reset.patch
+Patch12411: iwlwifi_-add-internal-short-scan-support-for-3945.patch
+Patch12412: iwlwifi_-Recover-TX-flow-stall-due-to-stuck-queue.patch
+Patch12413: iwlwifi_-move-plcp-check-to-separated-function.patch
+Patch12414: iwlwifi_-Recover-TX-flow-failure.patch
+Patch12415: iwlwifi_-code-cleanup-for-connectivity-recovery.patch
+Patch12416: iwlwifi_-iwl_good_ack_health-only-apply-to-AGN-device.patch
+
+# fix possible corruption with ssd
+Patch12700: ext4-issue-discard-operation-before-releasing-blocks.patch
+
+# iwlwifi: fix scan races
+Patch12910: iwlwifi-fix-scan-races.patch
+# iwlwifi: fix internal scan race
+Patch12911: iwlwifi-fix-internal-scan-race.patch
+# iwlwifi: recover_from_tx_stall
+Patch12912: iwlwifi-recover_from_tx_stall.patch
+
+Patch12921: iwlwifi-manage-QoS-by-mac-stack.patch
+Patch12922: mac80211-do-not-wipe-out-old-supported-rates.patch
+Patch12923: mac80211-explicitly-disable-enable-QoS.patch
+Patch12924: mac80211-fix-supported-rates-IE-if-AP-doesnt-give-us-its-rates.patch
+
+# iwlwifi: cancel scan watchdog in iwl_bg_abort_scan
+Patch13020: iwlwifi-cancel-scan-watchdog-in-iwl_bg_abort_scan.patch
+
+# l2tp: fix oops in pppol2tp_xmit (#607054)
+Patch13030: l2tp-fix-oops-in-pppol2tp_xmit.patch
+
+Patch14000: sched-fix-over-scheduling-bug.patch
+Patch14010: ethtool-fix-buffer-overflow.patch
+
+Patch14020: inotify-fix-inotify-oneshot-support.patch
+Patch14030: inotify-send-IN_UNMOUNT-events.patch
+
+Patch14040: crypto-testmgr-add-null-test-for-aesni.patch
+Patch14050: crypto-add-async-hash-testing.patch
+
+Patch14100: cifs-fix-malicious-redirect-problem-in-the-dns-lookup-code.patch
+Patch14110: ext4-make-sure-the-move_ext-ioctl-can-t-overwrite-append-only-files.patch
+Patch14115: xfs-prevent-swapext-from-operating-on-write-only-files.patch
+
+Patch14120: usb-obey-the-sysfs-power-wakeup-setting.patch
+
+# Red Hat Bugzilla #610911
+Patch14130: kvm-mmu-fix-conflict-access-permissions-in-direct-sp.patch
+
+# ==============================================================================
+%endif
+
+BuildRoot: %{_tmppath}/kernel-%{KVERREL}-root
+
+%description
+The kernel package contains the Linux kernel (vmlinuz), the core of any
+Linux operating system. The kernel handles the basic functions
+of the operating system: memory allocation, process allocation, device
+input and output, etc.
+
+
+%package doc
+Summary: Various documentation bits found in the kernel source
+Group: Documentation
+%description doc
+This package contains documentation files from the kernel
+source. Various bits of information about the Linux kernel and the
+device drivers shipped with it are documented in these files.
+
+You'll want to install this package if you need a reference to the
+options that can be passed to Linux kernel modules at load time.
+
+
+%package headers
+Summary: Header files for the Linux kernel for use by glibc
+Group: Development/System
+Obsoletes: glibc-kernheaders
+Provides: glibc-kernheaders = 3.0-46
+%description headers
+Kernel-headers includes the C header files that specify the interface
+between the Linux kernel and userspace libraries and programs. The
+header files define structures and constants that are needed for
+building most standard programs and are also needed for rebuilding the
+glibc package.
+
+%package firmware
+Summary: Firmware files used by the Linux kernel
+Group: Development/System
+# This is... complicated.
+# Look at the WHENCE file.
+License: GPL+ and GPLv2+ and MIT and Redistributable, no modification permitted
+%if "x%{?variant}" != "x"
+Provides: kernel-firmware = %{rpmversion}-%{pkg_release}
+%endif
+%description firmware
+Kernel-firmware includes firmware files required for some devices to
+operate.
+
+%package bootwrapper
+Summary: Boot wrapper files for generating combined kernel + initrd images
+Group: Development/System
+Requires: gzip
+%description bootwrapper
+Kernel-bootwrapper contains the wrapper code which makes bootable "zImage"
+files combining both kernel and initial ramdisk.
+
+%package debuginfo-common-%{_target_cpu}
+Summary: Kernel source files used by %{name}-debuginfo packages
+Group: Development/Debug
+%description debuginfo-common-%{_target_cpu}
+This package is required by %{name}-debuginfo subpackages.
+It provides the kernel source files common to all builds.
+
+%package -n perf
+Summary: Performance monitoring for the Linux kernel
+Group: Development/System
+License: GPLv2
+Requires: libdwarf
+%description -n perf
+This package provides the perf shell script, supporting documentation and
+required libraries for the perf tool shipped in each kernel image subpackage.
+
+#
+# This macro creates a kernel-<subpackage>-debuginfo package.
+# %%kernel_debuginfo_package <subpackage>
+#
+%define kernel_debuginfo_package() \
+%package %{?1:%{1}-}debuginfo\
+Summary: Debug information for package %{name}%{?1:-%{1}}\
+Group: Development/Debug\
+Requires: %{name}-debuginfo-common-%{_target_cpu} = %{version}-%{release}\
+Provides: %{name}%{?1:-%{1}}-debuginfo-%{_target_cpu} = %{version}-%{release}\
+AutoReqProv: no\
+%description -n %{name}%{?1:-%{1}}-debuginfo\
+This package provides debug information for package %{name}%{?1:-%{1}}.\
+This is required to use SystemTap with %{name}%{?1:-%{1}}-%{KVERREL}.\
+%{expand:%%global debuginfo_args %{?debuginfo_args} -p '/.*/%%{KVERREL}%{?1:\.%{1}}/.*|/.*%%{KVERREL}%{?1:\.%{1}}(\.debug)?' -o debuginfo%{?1}.list}\
+%{nil}
+
+#
+# This macro creates a kernel-<subpackage>-devel package.
+# %%kernel_devel_package <subpackage> <pretty-name>
+#
+%define kernel_devel_package() \
+%package %{?1:%{1}-}devel\
+Summary: Development package for building kernel modules to match the %{?2:%{2} }kernel\
+Group: System Environment/Kernel\
+Provides: kernel%{?1:-%{1}}-devel-%{_target_cpu} = %{version}-%{release}\
+Provides: kernel-devel-%{_target_cpu} = %{version}-%{release}%{?1:.%{1}}\
+Provides: kernel-devel = %{version}-%{release}%{?1:.%{1}}\
+Provides: kernel-devel-uname-r = %{KVERREL}%{?1:.%{1}}\
+AutoReqProv: no\
+Requires(pre): /usr/bin/find\
+%description -n kernel%{?variant}%{?1:-%{1}}-devel\
+This package provides kernel headers and makefiles sufficient to build modules\
+against the %{?2:%{2} }kernel package.\
+%{nil}
+
+#
+# This macro creates a kernel-<subpackage> and its -devel and -debuginfo too.
+# %%define variant_summary The Linux kernel compiled for <configuration>
+# %%kernel_variant_package [-n <pretty-name>] <subpackage>
+#
+%define kernel_variant_package(n:) \
+%package %1\
+Summary: %{variant_summary}\
+Group: System Environment/Kernel\
+%kernel_reqprovconf\
+%{expand:%%kernel_devel_package %1 %{!?-n:%1}%{?-n:%{-n*}}}\
+%{expand:%%kernel_debuginfo_package %1}\
+%{nil}
+
+
+# First the auxiliary packages of the main kernel package.
+%kernel_devel_package
+%kernel_debuginfo_package
+
+
+# Now, each variant package.
+
+%define variant_summary The Linux kernel compiled for SMP machines
+%kernel_variant_package -n SMP smp
+%description smp
+This package includes a SMP version of the Linux kernel. It is
+required only on machines with two or more CPUs as well as machines with
+hyperthreading technology.
+
+Install the kernel-smp package if your machine uses two or more CPUs.
+
+
+%define variant_summary The Linux kernel compiled for PAE capable machines
+%kernel_variant_package PAE
+%description PAE
+This package includes a version of the Linux kernel with support for up to
+64GB of high memory. It requires a CPU with Physical Address Extensions (PAE).
+The non-PAE kernel can only address up to 4GB of memory.
+Install the kernel-PAE package if your machine has more than 4GB of memory.
+
+
+%define variant_summary The Linux kernel compiled with extra debugging enabled for PAE capable machines
+%kernel_variant_package PAEdebug
+Obsoletes: kernel-PAE-debug
+%description PAEdebug
+This package includes a version of the Linux kernel with support for up to
+64GB of high memory. It requires a CPU with Physical Address Extensions (PAE).
+The non-PAE kernel can only address up to 4GB of memory.
+Install the kernel-PAE package if your machine has more than 4GB of memory.
+
+This variant of the kernel has numerous debugging options enabled.
+It should only be installed when trying to gather additional information
+on kernel bugs, as some of these options impact performance noticably.
+
+
+%define variant_summary The Linux kernel compiled with extra debugging enabled
+%kernel_variant_package debug
+%description debug
+The kernel package contains the Linux kernel (vmlinuz), the core of any
+Linux operating system. The kernel handles the basic functions
+of the operating system: memory allocation, process allocation, device
+input and output, etc.
+
+This variant of the kernel has numerous debugging options enabled.
+It should only be installed when trying to gather additional information
+on kernel bugs, as some of these options impact performance noticably.
+
+
+%define variant_summary A minimal Linux kernel compiled for crash dumps
+%kernel_variant_package kdump
+%description kdump
+This package includes a kdump version of the Linux kernel. It is
+required only on machines which will use the kexec-based kernel crash dump
+mechanism.
+
+
+%prep
+# do a few sanity-checks for --with *only builds
+%if %{with_baseonly}
+%if !%{with_up}%{with_pae}
+echo "Cannot build --with baseonly, up build is disabled"
+exit 1
+%endif
+%endif
+
+%if %{with_smponly}
+%if !%{with_smp}
+echo "Cannot build --with smponly, smp build is disabled"
+exit 1
+%endif
+%endif
+
+# more sanity checking; do it quietly
+if [ "%{patches}" != "%%{patches}" ] ; then
+ for patch in %{patches} ; do
+ if [ ! -f $patch ] ; then
+ echo "ERROR: Patch ${patch##/*/} listed in specfile but is missing"
+ exit 1
+ fi
+ done
+fi 2>/dev/null
+
+patch_command='patch -p1 -F1 -s'
+ApplyPatch()
+{
+ local patch=$1
+ shift
+ if [ ! -f $RPM_SOURCE_DIR/$patch ]; then
+ exit 1
+ fi
+%if !%{using_upstream_branch}
+ if ! egrep "^Patch[0-9]+: $patch\$" %{_specdir}/${RPM_PACKAGE_NAME%%%%%{?variant}}.spec ; then
+ if [ "${patch:0:10}" != "patch-2.6." ] ; then
+ echo "ERROR: Patch $patch not listed as a source patch in specfile"
+ exit 1
+ fi
+ fi 2>/dev/null
+%endif
+ case "$patch" in
+ *.bz2) bunzip2 < "$RPM_SOURCE_DIR/$patch" | $patch_command ${1+"$@"} ;;
+ *.gz) gunzip < "$RPM_SOURCE_DIR/$patch" | $patch_command ${1+"$@"} ;;
+ *) $patch_command ${1+"$@"} < "$RPM_SOURCE_DIR/$patch" ;;
+ esac
+}
+
+# don't apply patch if it's empty
+ApplyOptionalPatch()
+{
+ local patch=$1
+ shift
+ if [ ! -f $RPM_SOURCE_DIR/$patch ]; then
+ exit 1
+ fi
+ local C=$(wc -l $RPM_SOURCE_DIR/$patch | awk '{print $1}')
+ if [ "$C" -gt 9 ]; then
+ ApplyPatch $patch ${1+"$@"}
+ fi
+}
+
+# we don't want a .config file when building firmware: it just confuses the build system
+%define build_firmware \
+ mv .config .config.firmware_save \
+ make INSTALL_FW_PATH=$RPM_BUILD_ROOT/lib/firmware firmware_install \
+ mv .config.firmware_save .config
+
+# First we unpack the kernel tarball.
+# If this isn't the first make prep, we use links to the existing clean tarball
+# which speeds things up quite a bit.
+
+# Update to latest upstream.
+%if 0%{?released_kernel}
+%define vanillaversion 2.6.%{base_sublevel}
+# non-released_kernel case
+%else
+%if 0%{?rcrev}
+%define vanillaversion 2.6.%{upstream_sublevel}-rc%{rcrev}
+%if 0%{?gitrev}
+%define vanillaversion 2.6.%{upstream_sublevel}-rc%{rcrev}-git%{gitrev}
+%endif
+%else
+# pre-{base_sublevel+1}-rc1 case
+%if 0%{?gitrev}
+%define vanillaversion 2.6.%{base_sublevel}-git%{gitrev}
+%else
+%define vanillaversion 2.6.%{base_sublevel}
+%endif
+%endif
+%endif
+
+# We can share hardlinked source trees by putting a list of
+# directory names of the CVS checkouts that we want to share
+# with in .shared-srctree. (Full pathnames are required.)
+[ -f .shared-srctree ] && sharedirs=$(cat .shared-srctree)
+
+if [ ! -d kernel-%{kversion}/vanilla-%{vanillaversion} ]; then
+
+ if [ -d kernel-%{kversion}/vanilla-%{kversion} ]; then
+
+ cd kernel-%{kversion}
+
+ # Any vanilla-* directories other than the base one are stale.
+ for dir in vanilla-*; do
+ [ "$dir" = vanilla-%{kversion} ] || rm -rf $dir &
+ done
+
+ else
+
+ # Ok, first time we do a make prep.
+ rm -f pax_global_header
+ for sharedir in $sharedirs ; do
+ if [[ ! -z $sharedir && -d $sharedir/kernel-%{kversion}/vanilla-%{kversion} ]] ; then
+ break
+ fi
+ done
+ if [[ ! -z $sharedir && -d $sharedir/kernel-%{kversion}/vanilla-%{kversion} ]] ; then
+%setup -q -n kernel-%{kversion} -c -T
+ cp -rl $sharedir/kernel-%{kversion}/vanilla-%{kversion} .
+ else
+%setup -q -n kernel-%{kversion} -c
+ mv linux-%{kversion} vanilla-%{kversion}
+ fi
+
+ fi
+
+%if "%{kversion}" != "%{vanillaversion}"
+
+ for sharedir in $sharedirs ; do
+ if [[ ! -z $sharedir && -d $sharedir/kernel-%{kversion}/vanilla-%{vanillaversion} ]] ; then
+ break
+ fi
+ done
+ if [[ ! -z $sharedir && -d $sharedir/kernel-%{kversion}/vanilla-%{vanillaversion} ]] ; then
+
+ cp -rl $sharedir/kernel-%{kversion}/vanilla-%{vanillaversion} .
+
+ else
+
+ cp -rl vanilla-%{kversion} vanilla-%{vanillaversion}
+ cd vanilla-%{vanillaversion}
+
+# Update vanilla to the latest upstream.
+# (non-released_kernel case only)
+%if 0%{?rcrev}
+ ApplyPatch patch-2.6.%{upstream_sublevel}-rc%{rcrev}.bz2
+%if 0%{?gitrev}
+ ApplyPatch patch-2.6.%{upstream_sublevel}-rc%{rcrev}-git%{gitrev}.bz2
+%endif
+%else
+# pre-{base_sublevel+1}-rc1 case
+%if 0%{?gitrev}
+ ApplyPatch patch-2.6.%{base_sublevel}-git%{gitrev}.bz2
+%endif
+%endif
+
+ cd ..
+
+ fi
+
+%endif
+
+else
+ # We already have a vanilla dir.
+ cd kernel-%{kversion}
+fi
+
+if [ -d linux-%{kversion}.%{_target_cpu} ]; then
+ # Just in case we ctrl-c'd a prep already
+ rm -rf deleteme.%{_target_cpu}
+ # Move away the stale away, and delete in background.
+ mv linux-%{kversion}.%{_target_cpu} deleteme.%{_target_cpu}
+ rm -rf deleteme.%{_target_cpu} &
+fi
+
+cp -rl vanilla-%{vanillaversion} linux-%{kversion}.%{_target_cpu}
+
+cd linux-%{kversion}.%{_target_cpu}
+
+# released_kernel with possible stable updates
+%if 0%{?stable_base}
+ApplyPatch %{stable_patch_00}
+%endif
+%if 0%{?stable_rc}
+ApplyPatch %{stable_patch_01}
+%endif
+
+%if %{using_upstream_branch}
+### BRANCH APPLY ###
+%endif
+
+# Drop some necessary files from the source dir into the buildroot
+cp $RPM_SOURCE_DIR/config-* .
+cp %{SOURCE15} .
+
+# Dynamically generate kernel .config files from config-* files
+make -f %{SOURCE20} VERSION=%{version} configs
+
+#if a rhel kernel, apply the rhel config options
+%if 0%{?rhel}
+ for i in %{all_arch_configs}
+ do
+ mv $i $i.tmp
+ ./merge.pl config-rhel-generic $i.tmp > $i
+ rm $i.tmp
+ done
+%endif
+
+ApplyOptionalPatch git-linus.diff
+
+# This patch adds a "make nonint_oldconfig" which is non-interactive and
+# also gives a list of missing options at the end. Useful for automated
+# builds (as used in the buildsystem).
+ApplyPatch linux-2.6-build-nonintconfig.patch
+
+ApplyPatch linux-2.6-makefile-after_link.patch
+
+#
+# misc small stuff to make things compile
+#
+ApplyOptionalPatch linux-2.6-compile-fixes.patch
+
+%if !%{nopatches}
+
+# revert patches from upstream that conflict or that we get via other means
+ApplyOptionalPatch linux-2.6-upstream-reverts.patch -R
+
+#ApplyPatch git-cpufreq.patch
+#ApplyPatch git-bluetooth.patch
+
+ApplyOptionalPatch linux-2.6-hotfixes.patch
+
+# Roland's utrace ptrace replacement.
+ApplyPatch linux-2.6-tracehook.patch
+ApplyPatch linux-2.6-utrace.patch
+ApplyPatch linux-2.6-utrace-ptrace.patch
+
+# Architecture patches
+# x86(-64)
+ApplyPatch via-hwmon-temp-sensor.patch
+ApplyPatch linux-2.6-dell-laptop-rfkill-fix.patch
+
+#
+# Intel IOMMU
+#
+ApplyPatch linux-2.6-cantiga-iommu-gfx.patch
+
+#
+# PowerPC
+#
+### NOT (YET) UPSTREAM:
+# The storage alias patch is Fedora-local, and allows the old 'ps3_storage'
+# module name to work on upgrades. Otherwise, I believe mkinitrd will fail
+# to pull the module in,
+ApplyPatch linux-2.6-ps3-storage-alias.patch
+# Alleviate G5 thermal shutdown problems
+ApplyPatch linux-2.6-g5-therm-shutdown.patch
+# Provide modalias in sysfs for vio devices
+ApplyPatch linux-2.6-vio-modalias.patch
+# Work around PCIe bridge setup on iSight
+ApplyPatch linux-2.6-imac-transparent-bridge.patch
+
+#
+# SPARC64
+#
+ApplyPatch linux-2.6.29-sparc-IOC_TYPECHECK.patch
+
+#
+# Exec shield
+#
+ApplyPatch linux-2.6-execshield.patch
+
+#
+# bugfixes to drivers and filesystems
+#
+
+# ext4
+ApplyPatch linux-2.6-ext4-quota-metadata-reservation.patch
+
+# xfs
+
+# btrfs
+ApplyPatch linux-2.6-btrfs-fix-acl.patch
+ApplyPatch btrfs-prohibit-a-operation-of-changing-acls-mask-when-noacl-mount-option-is-used.patch
+
+# eCryptfs
+
+# NFSv4
+ApplyPatch linux-2.6-nfsd4-proots.patch
+ApplyPatch linux-2.6-nfs4-callback-hidden.patch
+
+# USB
+ApplyPatch linux-2.6-driver-level-usb-autosuspend.diff
+ApplyPatch linux-2.6-enable-btusb-autosuspend.patch
+ApplyPatch linux-2.6-usb-uvc-autosuspend.diff
+ApplyPatch linux-2.6-usb-wwan-update.patch
+
+# WMI
+ApplyPatch linux-2.6-autoload-wmi.patch
+# autoload fixes
+ApplyPatch wmi-check-find_guid-return-value-to-prevent-oops.patch
+ApplyPatch wmi-survive-bios-with-duplicate-guids.patch
+
+# ACPI
+ApplyPatch linux-2.6-defaults-acpi-video.patch
+ApplyPatch linux-2.6-acpi-video-dos.patch
+
+ApplyPatch acpi-ec-add-delay-before-write.patch
+
+# Various low-impact patches to aid debugging.
+ApplyPatch linux-2.6-debug-sizeof-structs.patch
+ApplyPatch linux-2.6-debug-nmi-timeout.patch
+ApplyPatch linux-2.6-debug-taint-vm.patch
+ApplyPatch linux-2.6-debug-vm-would-have-oomkilled.patch
+ApplyPatch linux-2.6-debug-always-inline-kzalloc.patch
+
+#
+# PCI
+#
+# disable message signaled interrupts
+ApplyPatch linux-2.6-defaults-pci_no_msi.patch
+# update the pciehp driver
+#ApplyPatch linux-2.6-pciehp-update.patch
+# default to enabling passively listening for hotplug events
+#ApplyPatch linux-2.6-defaults-pciehp.patch
+# enable ASPM by default on hardware we expect to work
+ApplyPatch linux-2.6-defaults-aspm.patch
+# disable aspm if acpi doesn't provide an _OSC method
+ApplyPatch pci-acpi-disable-aspm-if-no-osc.patch
+# allow drivers to disable aspm at load time
+ApplyPatch pci-aspm-dont-enable-too-early.patch
+
+#
+# SCSI Bits.
+#
+
+# ALSA
+# squelch hda_beep by default
+ApplyPatch linux-2.6-defaults-alsa-hda-beep-off.patch
+ApplyPatch hda_intel-prealloc-4mb-dmabuffer.patch
+
+# Networking
+ApplyPatch sky2-optima-add-register-definitions.patch
+ApplyPatch sky2-optima-support.patch
+ApplyPatch sky2-optima-fix-tcp-offload.patch
+ApplyPatch sky2-optima-print-chip-name.patch
+ApplyPatch sky2-optima-add-missing-write-bits.patch
+
+# Misc fixes
+# The input layer spews crap no-one cares about.
+ApplyPatch linux-2.6-input-kill-stupid-messages.patch
+
+# stop floppy.ko from autoloading during udev...
+ApplyPatch die-floppy-die.patch
+
+# Get away from having to poll Toshibas
+#ApplyPatch linux-2.6-input-fix-toshiba-hotkeys.patch
+
+ApplyPatch linux-2.6.30-no-pcspkr-modalias.patch
+
+ApplyPatch linux-2.6-input-hid-quirk-egalax.patch
+ApplyPatch thinkpad-acpi-add-x100e.patch
+ApplyPatch thinkpad-acpi-fix-backlight.patch
+
+# Allow to use 480600 baud on 16C950 UARTs
+ApplyPatch linux-2.6-serial-460800.patch
+
+# Silence some useless messages that still get printed with 'quiet'
+ApplyPatch linux-2.6-silence-noise.patch
+ApplyPatch linux-2.6.30-hush-rom-warning.patch
+
+# Make fbcon not show the penguins with 'quiet'
+ApplyPatch linux-2.6-silence-fbcon-logo.patch
+
+# don't autoload viafb
+ApplyPatch viafb-neuter-device-table.patch
+
+# Fix the SELinux mprotect checks on executable mappings
+# ApplyPatch linux-2.6-selinux-mprotect-checks.patch
+# Fix SELinux for sparc
+ApplyPatch linux-2.6-sparc-selinux-mprotect-checks.patch
+
+# Changes to upstream defaults.
+
+
+# ia64 ata quirk
+ApplyPatch linux-2.6-ata-quirk.patch
+
+# /dev/crash driver.
+ApplyPatch linux-2.6-crash-driver.patch
+
+# Determine cacheline sizes in a generic manner.
+ApplyPatch linux-2.6-pci-cacheline-sizing.patch
+
+# http://www.lirc.org/
+ApplyPatch lirc-2.6.32.patch
+# enable IR transceiver on Hauppauge HD PVR (v4l-dvb merge pending)
+ApplyPatch hdpvr-ir-enable.patch
+# Broadcom Crystal HD driver from 2.6.34 staging
+ApplyPatch crystalhd-2.6.34-staging.patch
+
+# Add kernel KSM support
+# Optimize KVM for KSM support
+#ApplyPatch linux-2.6-ksm-kvm.patch
+
+# Assorted Virt Fixes
+
+# Fix block I/O errors in KVM
+#ApplyPatch linux-2.6-block-silently-error-unsupported-empty-barriers-too.patch
+
+ApplyPatch linux-2.6-e1000-ich9.patch
+
+ApplyPatch linux-2.6-phylib-autoload.patch
+
+ApplyPatch linux-2.6-x86-64-fbdev-primary.patch
+# Nouveau DRM + drm fixes
+ApplyPatch drm-upgrayedd.patch
+#ApplyPatch drm-intel-big-hammer.patch
+#ApplyPatch drm-intel-no-tv-hotplug.patch
+ApplyOptionalPatch drm-intel-next.patch
+ApplyPatch drm-intel-acpi-populate-didl.patch
+ApplyPatch drm-intel-make-lvds-work.patch
+# gm45 stability fixes
+ApplyPatch drm-intel-945gm-stability-fixes.patch
+# hibernation memory corruption fixes
+ApplyPatch drm-i915-fix-hibernate-memory-corruption.patch
+ApplyPatch drm-i915-add-reclaimable-to-page-allocations.patch
+
+ApplyPatch drm-nouveau-g80-ctxprog.patch
+ApplyPatch drm-nouveau-tvout-disable.patch
+ApplyPatch drm-nouveau-safetile-getparam.patch
+ApplyPatch drm-nouveau-kconfig.patch
+ApplyPatch drm-nouveau-update.patch
+ApplyPatch drm-nouveau-d620.patch
+
+# linux1394 git patches
+#ApplyPatch linux-2.6-firewire-git-update.patch
+#ApplyOptionalPatch linux-2.6-firewire-git-pending.patch
+
+# silence the ACPI blacklist code
+ApplyPatch linux-2.6-silence-acpi-blacklist.patch
+
+# V4L/DVB updates/fixes/experimental drivers
+#ApplyPatch linux-2.6-v4l-dvb-fixes.patch
+#ApplyPatch linux-2.6-v4l-dvb-update.patch
+#ApplyPatch linux-2.6-v4l-dvb-experimental.patch
+#ApplyPatch linux-2.6-revert-dvb-net-kabi-change.patch
+ApplyPatch linux-2.6-v4l-dvb-rebase-gspca-to-latest.patch
+ApplyPatch linux-2.6-v4l-dvb-add-lgdt3304-support.patch
+ApplyPatch linux-2.6-v4l-dvb-add-kworld-a340-support.patch
+
+# Patches headed upstream
+ApplyPatch linux-2.6-rfkill-all.patch
+
+ApplyPatch add-appleir-usb-driver.patch
+
+# Patches for -stable
+
+ApplyPatch fix-ima-null-ptr-deref.patch
+
+ApplyPatch fix-abrtd.patch
+
+# rhbz#566565
+ApplyPatch ice1712-fix-revo71-mixer-names.patch
+
+# rhbz#572653
+ApplyPatch linux-2.6-b43_-Rewrite-DMA-Tx-status-handling-sanity-checks.patch
+
+# rhbz#533746
+ApplyPatch ssb_check_for_sprom.patch
+
+# backport iwlwifi fixes (thanks, sgruszka!) -- drop when stable catches-up
+ApplyPatch iwlwifi-reset-card-during-probe.patch
+
+# patches from Intel to address intermittent firmware failures with iwlagn
+ApplyPatch iwlwifi_-add-function-to-reset_tune-radio-if-needed.patch
+ApplyPatch iwlwifi_-Logic-to-control-how-frequent-radio-should-be-reset-if-needed.patch
+ApplyPatch iwlwifi_-Tune-radio-to-prevent-unexpected-behavior.patch
+ApplyPatch iwlwifi_-multiple-force-reset-mode.patch
+ApplyPatch iwlwifi_-Adjusting-PLCP-error-threshold-for-1000-NIC.patch
+ApplyPatch iwlwifi_-separated-time-check-for-different-type-of-force-reset.patch
+ApplyPatch iwlwifi_-add-internal-short-scan-support-for-3945.patch
+ApplyPatch iwlwifi_-Recover-TX-flow-stall-due-to-stuck-queue.patch
+ApplyPatch iwlwifi_-move-plcp-check-to-separated-function.patch
+ApplyPatch iwlwifi_-Recover-TX-flow-failure.patch
+ApplyPatch iwlwifi_-code-cleanup-for-connectivity-recovery.patch
+ApplyPatch iwlwifi_-iwl_good_ack_health-only-apply-to-AGN-device.patch
+
+# fix possible corruption with ssd
+ApplyPatch ext4-issue-discard-operation-before-releasing-blocks.patch
+
+# iwlwifi: fix scan races
+ApplyPatch iwlwifi-fix-scan-races.patch
+# iwlwifi: fix internal scan race
+ApplyPatch iwlwifi-fix-internal-scan-race.patch
+# iwlwifi: recover_from_tx_stall
+ApplyPatch iwlwifi-recover_from_tx_stall.patch
+
+# mac80211/iwlwifi fix connections to some APs (rhbz#558002)
+ApplyPatch mac80211-explicitly-disable-enable-QoS.patch
+ApplyPatch iwlwifi-manage-QoS-by-mac-stack.patch
+ApplyPatch mac80211-do-not-wipe-out-old-supported-rates.patch
+ApplyPatch mac80211-fix-supported-rates-IE-if-AP-doesnt-give-us-its-rates.patch
+
+# iwlwifi: cancel scan watchdog in iwl_bg_abort_scan
+ApplyPatch iwlwifi-cancel-scan-watchdog-in-iwl_bg_abort_scan.patch
+
+# l2tp: fix oops in pppol2tp_xmit (#607054)
+ApplyPatch l2tp-fix-oops-in-pppol2tp_xmit.patch
+
+# fix performance problem with CGROUPS
+ApplyPatch sched-fix-over-scheduling-bug.patch
+
+# CVE-2010-2478
+ApplyPatch ethtool-fix-buffer-overflow.patch
+
+# fix broken oneshot support and missing umount events (F13#607327)
+ApplyPatch inotify-fix-inotify-oneshot-support.patch
+ApplyPatch inotify-send-IN_UNMOUNT-events.patch
+
+# add tests for aesni module (#571577)
+ApplyPatch crypto-testmgr-add-null-test-for-aesni.patch
+# add tests for crypto async hashing (#571577)
+ApplyPatch crypto-add-async-hash-testing.patch
+
+# CVE-2010-2524
+ApplyPatch cifs-fix-malicious-redirect-problem-in-the-dns-lookup-code.patch
+# CVE-2010-2066
+ApplyPatch ext4-make-sure-the-move_ext-ioctl-can-t-overwrite-append-only-files.patch
+# CVE-2010-2266
+ApplyPatch xfs-prevent-swapext-from-operating-on-write-only-files.patch
+
+# fix broken USB device wakeups (#617559)
+ApplyPatch usb-obey-the-sysfs-power-wakeup-setting.patch
+
+ApplyPatch kvm-mmu-fix-conflict-access-permissions-in-direct-sp.patch
+
+# END OF PATCH APPLICATIONS ====================================================
+%endif
+
+# Any further pre-build tree manipulations happen here.
+
+chmod +x scripts/checkpatch.pl
+
+# only deal with configs if we are going to build for the arch
+%ifnarch %nobuildarches
+
+mkdir configs
+
+# Remove configs not for the buildarch
+for cfg in kernel-%{version}-*.config; do
+ if [ `echo %{all_arch_configs} | grep -c $cfg` -eq 0 ]; then
+ rm -f $cfg
+ fi
+done
+
+%if !%{debugbuildsenabled}
+rm -f kernel-%{version}-*debug.config
+%endif
+
+# now run oldconfig over all the config files
+for i in *.config
+do
+ mv $i .config
+ Arch=`head -1 .config | cut -b 3-`
+ make ARCH=$Arch %{oldconfig_target} > /dev/null
+ echo "# $Arch" > configs/$i
+ cat .config >> configs/$i
+done
+# end of kernel config
+%endif
+
+# get rid of unwanted files resulting from patch fuzz
+find . \( -name "*.orig" -o -name "*~" \) -exec rm -f {} \; >/dev/null
+
+cd ..
+
+###
+### build
+###
+%build
+
+%if %{with_sparse}
+%define sparse_mflags C=1
+%endif
+
+%if %{fancy_debuginfo}
+# This override tweaks the kernel makefiles so that we run debugedit on an
+# object before embedding it. When we later run find-debuginfo.sh, it will
+# run debugedit again. The edits it does change the build ID bits embedded
+# in the stripped object, but repeating debugedit is a no-op. We do it
+# beforehand to get the proper final build ID bits into the embedded image.
+# This affects the vDSO images in vmlinux, and the vmlinux image in bzImage.
+export AFTER_LINK=\
+'sh -xc "/usr/lib/rpm/debugedit -b $$RPM_BUILD_DIR -d /usr/src/debug \
+ -i $@ > $@.id"'
+%endif
+
+cp_vmlinux()
+{
+ eu-strip --remove-comment -o "$2" "$1"
+}
+
+BuildKernel() {
+ MakeTarget=$1
+ KernelImage=$2
+ Flavour=$3
+ InstallName=${4:-vmlinuz}
+
+ # Pick the right config file for the kernel we're building
+ Config=kernel-%{version}-%{_target_cpu}${Flavour:+-${Flavour}}.config
+ DevelDir=/usr/src/kernels/%{KVERREL}${Flavour:+.${Flavour}}
+
+ # When the bootable image is just the ELF kernel, strip it.
+ # We already copy the unstripped file into the debuginfo package.
+ if [ "$KernelImage" = vmlinux ]; then
+ CopyKernel=cp_vmlinux
+ else
+ CopyKernel=cp
+ fi
+
+ KernelVer=%{version}-%{release}.%{_target_cpu}${Flavour:+.${Flavour}}
+ echo BUILDING A KERNEL FOR ${Flavour} %{_target_cpu}...
+
+ # make sure EXTRAVERSION says what we want it to say
+ perl -p -i -e "s/^EXTRAVERSION.*/EXTRAVERSION = %{?stablerev}-%{release}.%{_target_cpu}${Flavour:+.${Flavour}}/" Makefile
+
+ # if pre-rc1 devel kernel, must fix up SUBLEVEL for our versioning scheme
+ %if !0%{?rcrev}
+ %if 0%{?gitrev}
+ perl -p -i -e 's/^SUBLEVEL.*/SUBLEVEL = %{upstream_sublevel}/' Makefile
+ %endif
+ %endif
+
+ # and now to start the build process
+
+ make -s mrproper
+ cp configs/$Config .config
+
+ Arch=`head -1 .config | cut -b 3-`
+ echo USING ARCH=$Arch
+
+ make -s ARCH=$Arch %{oldconfig_target} > /dev/null
+ make -s ARCH=$Arch V=1 %{?_smp_mflags} $MakeTarget %{?sparse_mflags}
+ make -s ARCH=$Arch V=1 %{?_smp_mflags} modules %{?sparse_mflags} || exit 1
+
+%if %{with_perftool}
+ pushd tools/perf
+# make sure the scripts are executable... won't be in tarball until 2.6.31 :/
+ chmod +x util/generate-cmdlist.sh util/PERF-VERSION-GEN
+ make -s V=1 %{?_smp_mflags} perf
+ mkdir -p $RPM_BUILD_ROOT/usr/libexec/
+ install -m 755 perf $RPM_BUILD_ROOT/usr/libexec/perf.$KernelVer
+ popd
+%endif
+
+ # Start installing the results
+%if %{with_debuginfo}
+ mkdir -p $RPM_BUILD_ROOT%{debuginfodir}/boot
+ mkdir -p $RPM_BUILD_ROOT%{debuginfodir}/%{image_install_path}
+%endif
+ mkdir -p $RPM_BUILD_ROOT/%{image_install_path}
+ install -m 644 .config $RPM_BUILD_ROOT/boot/config-$KernelVer
+ install -m 644 System.map $RPM_BUILD_ROOT/boot/System.map-$KernelVer
+%if %{with_dracut}
+ # We estimate the size of the initramfs because rpm needs to take this size
+ # into consideration when performing disk space calculations. (See bz #530778)
+ dd if=/dev/zero of=$RPM_BUILD_ROOT/boot/initramfs-$KernelVer.img bs=1M count=20
+%else
+ dd if=/dev/zero of=$RPM_BUILD_ROOT/boot/initrd-$KernelVer.img bs=1M count=5
+%endif
+ if [ -f arch/$Arch/boot/zImage.stub ]; then
+ cp arch/$Arch/boot/zImage.stub $RPM_BUILD_ROOT/%{image_install_path}/zImage.stub-$KernelVer || :
+ fi
+ $CopyKernel $KernelImage \
+ $RPM_BUILD_ROOT/%{image_install_path}/$InstallName-$KernelVer
+ chmod 755 $RPM_BUILD_ROOT/%{image_install_path}/$InstallName-$KernelVer
+
+ mkdir -p $RPM_BUILD_ROOT/lib/modules/$KernelVer
+ # Override $(mod-fw) because we don't want it to install any firmware
+ # We'll do that ourselves with 'make firmware_install'
+ make -s ARCH=$Arch INSTALL_MOD_PATH=$RPM_BUILD_ROOT modules_install KERNELRELEASE=$KernelVer mod-fw=
+%ifarch %{vdso_arches}
+ make -s ARCH=$Arch INSTALL_MOD_PATH=$RPM_BUILD_ROOT vdso_install KERNELRELEASE=$KernelVer
+ if grep '^CONFIG_XEN=y$' .config >/dev/null; then
+ echo > ldconfig-kernel.conf "\
+# This directive teaches ldconfig to search in nosegneg subdirectories
+# and cache the DSOs there with extra bit 0 set in their hwcap match
+# fields. In Xen guest kernels, the vDSO tells the dynamic linker to
+# search in nosegneg subdirectories and to match this extra hwcap bit
+# in the ld.so.cache file.
+hwcap 0 nosegneg"
+ fi
+ if [ ! -s ldconfig-kernel.conf ]; then
+ echo > ldconfig-kernel.conf "\
+# Placeholder file, no vDSO hwcap entries used in this kernel."
+ fi
+ %{__install} -D -m 444 ldconfig-kernel.conf \
+ $RPM_BUILD_ROOT/etc/ld.so.conf.d/kernel-$KernelVer.conf
+%endif
+
+ # And save the headers/makefiles etc for building modules against
+ #
+ # This all looks scary, but the end result is supposed to be:
+ # * all arch relevant include/ files
+ # * all Makefile/Kconfig files
+ # * all script/ files
+
+ rm -f $RPM_BUILD_ROOT/lib/modules/$KernelVer/build
+ rm -f $RPM_BUILD_ROOT/lib/modules/$KernelVer/source
+ mkdir -p $RPM_BUILD_ROOT/lib/modules/$KernelVer/build
+ (cd $RPM_BUILD_ROOT/lib/modules/$KernelVer ; ln -s build source)
+ # dirs for additional modules per module-init-tools, kbuild/modules.txt
+ mkdir -p $RPM_BUILD_ROOT/lib/modules/$KernelVer/extra
+ mkdir -p $RPM_BUILD_ROOT/lib/modules/$KernelVer/updates
+ mkdir -p $RPM_BUILD_ROOT/lib/modules/$KernelVer/weak-updates
+ # first copy everything
+ cp --parents `find -type f -name "Makefile*" -o -name "Kconfig*"` $RPM_BUILD_ROOT/lib/modules/$KernelVer/build
+ cp Module.symvers $RPM_BUILD_ROOT/lib/modules/$KernelVer/build
+ cp System.map $RPM_BUILD_ROOT/lib/modules/$KernelVer/build
+ if [ -s Module.markers ]; then
+ cp Module.markers $RPM_BUILD_ROOT/lib/modules/$KernelVer/build
+ fi
+ # then drop all but the needed Makefiles/Kconfig files
+ rm -rf $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/Documentation
+ rm -rf $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/scripts
+ rm -rf $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/include
+ cp .config $RPM_BUILD_ROOT/lib/modules/$KernelVer/build
+ cp -a scripts $RPM_BUILD_ROOT/lib/modules/$KernelVer/build
+ if [ -d arch/$Arch/scripts ]; then
+ cp -a arch/$Arch/scripts $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/arch/%{_arch} || :
+ fi
+ if [ -f arch/$Arch/*lds ]; then
+ cp -a arch/$Arch/*lds $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/arch/%{_arch}/ || :
+ fi
+ rm -f $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/scripts/*.o
+ rm -f $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/scripts/*/*.o
+%ifarch ppc
+ cp -a --parents arch/powerpc/lib/crtsavres.[So] $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/
+%endif
+ if [ -d arch/%{asmarch}/include ]; then
+ cp -a --parents arch/%{asmarch}/include $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/
+ fi
+ mkdir -p $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/include
+ cd include
+ cp -a acpi config crypto keys linux math-emu media mtd net pcmcia rdma rxrpc scsi sound trace video drm asm-generic $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/include
+ asmdir=$(readlink asm)
+ cp -a $asmdir $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/include/
+ pushd $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/include
+ ln -s $asmdir asm
+ popd
+ # Make sure the Makefile and version.h have a matching timestamp so that
+ # external modules can be built
+ touch -r $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/Makefile $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/include/linux/version.h
+ touch -r $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/.config $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/include/linux/autoconf.h
+ # Copy .config to include/config/auto.conf so "make prepare" is unnecessary.
+ cp $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/.config $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/include/config/auto.conf
+ cd ..
+
+ if test -s vmlinux.id; then
+ cp vmlinux.id $RPM_BUILD_ROOT/lib/modules/$KernelVer/build/vmlinux.id
+ else
+ echo >&2 "*** WARNING *** no vmlinux build ID! ***"
+ fi
+
+ #
+ # save the vmlinux file for kernel debugging into the kernel-debuginfo rpm
+ #
+%if %{with_debuginfo}
+ mkdir -p $RPM_BUILD_ROOT%{debuginfodir}/lib/modules/$KernelVer
+ cp vmlinux $RPM_BUILD_ROOT%{debuginfodir}/lib/modules/$KernelVer
+%endif
+
+ find $RPM_BUILD_ROOT/lib/modules/$KernelVer -name "*.ko" -type f >modnames
+
+ # mark modules executable so that strip-to-file can strip them
+ xargs --no-run-if-empty chmod u+x < modnames
+
+ # Generate a list of modules for block and networking.
+
+ fgrep /drivers/ modnames | xargs --no-run-if-empty nm -upA |
+ sed -n 's,^.*/\([^/]*\.ko\): *U \(.*\)$,\1 \2,p' > drivers.undef
+
+ collect_modules_list()
+ {
+ sed -r -n -e "s/^([^ ]+) \\.?($2)\$/\\1/p" drivers.undef |
+ LC_ALL=C sort -u > $RPM_BUILD_ROOT/lib/modules/$KernelVer/modules.$1
+ }
+
+ collect_modules_list networking \
+ 'register_netdev|ieee80211_register_hw|usbnet_probe'
+ collect_modules_list block \
+ 'ata_scsi_ioctl|scsi_add_host|scsi_add_host_with_dma|blk_init_queue|register_mtd_blktrans|scsi_esp_register|scsi_register_device_handler'
+ collect_modules_list drm \
+ 'drm_open|drm_init'
+ collect_modules_list modesetting \
+ 'drm_crtc_init'
+
+ # detect missing or incorrect license tags
+ rm -f modinfo
+ while read i
+ do
+ echo -n "${i#$RPM_BUILD_ROOT/lib/modules/$KernelVer/} " >> modinfo
+ /sbin/modinfo -l $i >> modinfo
+ done < modnames
+
+ egrep -v \
+ 'GPL( v2)?$|Dual BSD/GPL$|Dual MPL/GPL$|GPL and additional rights$' \
+ modinfo && exit 1
+
+ rm -f modinfo modnames
+
+ # remove files that will be auto generated by depmod at rpm -i time
+ for i in alias alias.bin ccwmap dep dep.bin ieee1394map inputmap isapnpmap ofmap pcimap seriomap symbols symbols.bin usbmap
+ do
+ rm -f $RPM_BUILD_ROOT/lib/modules/$KernelVer/modules.$i
+ done
+
+ # Move the devel headers out of the root file system
+ mkdir -p $RPM_BUILD_ROOT/usr/src/kernels
+ mv $RPM_BUILD_ROOT/lib/modules/$KernelVer/build $RPM_BUILD_ROOT/$DevelDir
+ ln -sf ../../..$DevelDir $RPM_BUILD_ROOT/lib/modules/$KernelVer/build
+}
+
+###
+# DO it...
+###
+
+# prepare directories
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/boot
+
+cd linux-%{kversion}.%{_target_cpu}
+
+%if %{with_debug}
+BuildKernel %make_target %kernel_image debug
+%endif
+
+%if %{with_pae_debug}
+BuildKernel %make_target %kernel_image PAEdebug
+%endif
+
+%if %{with_pae}
+BuildKernel %make_target %kernel_image PAE
+%endif
+
+%if %{with_up}
+BuildKernel %make_target %kernel_image
+%endif
+
+%if %{with_smp}
+BuildKernel %make_target %kernel_image smp
+%endif
+
+%if %{with_kdump}
+BuildKernel vmlinux vmlinux kdump vmlinux
+%endif
+
+%if %{with_doc}
+# Make the HTML and man pages.
+#make %{?_smp_mflags} htmldocs mandocs || %{doc_build_fail}
+make -j1 htmldocs mandocs || %{doc_build_fail}
+
+# sometimes non-world-readable files sneak into the kernel source tree
+chmod -R a=rX Documentation
+find Documentation -type d | xargs chmod u+w
+%endif
+
+%if %{with_perf}
+pushd tools/perf
+make %{?_smp_mflags} man || %{doc_build_fail}
+popd
+%endif
+
+###
+### Special hacks for debuginfo subpackages.
+###
+
+# This macro is used by %%install, so we must redefine it before that.
+%define debug_package %{nil}
+
+%if %{fancy_debuginfo}
+%define __debug_install_post \
+ /usr/lib/rpm/find-debuginfo.sh %{debuginfo_args} %{_builddir}/%{?buildsubdir}\
+%{nil}
+%endif
+
+%if %{with_debuginfo}
+%ifnarch noarch
+%global __debug_package 1
+%files -f debugfiles.list debuginfo-common-%{_target_cpu}
+%defattr(-,root,root)
+%endif
+%endif
+
+###
+### install
+###
+
+%install
+
+cd linux-%{kversion}.%{_target_cpu}
+
+%if %{with_doc}
+docdir=$RPM_BUILD_ROOT%{_datadir}/doc/kernel-doc-%{rpmversion}
+man9dir=$RPM_BUILD_ROOT%{_datadir}/man/man9
+
+# copy the source over
+mkdir -p $docdir
+tar -f - --exclude=man --exclude='.*' -c Documentation | tar xf - -C $docdir
+
+# Install man pages for the kernel API.
+mkdir -p $man9dir
+find Documentation/DocBook/man -name '*.9.gz' -print0 |
+xargs -0 --no-run-if-empty %{__install} -m 444 -t $man9dir $m
+ls $man9dir | grep -q '' || > $man9dir/BROKEN
+%endif # with_doc
+
+%if %{with_perf}
+# perf docs
+mandir=$RPM_BUILD_ROOT%{_datadir}/man
+man1dir=$mandir/man1
+pushd tools/perf/Documentation
+make install-man mandir=$mandir
+popd
+
+pushd $man1dir
+for d in *.1; do
+ gzip $d;
+done
+popd
+
+# perf shell wrapper and examples
+mkdir -p $RPM_BUILD_ROOT/usr/sbin/
+cp $RPM_SOURCE_DIR/perf $RPM_BUILD_ROOT/usr/sbin/perf
+chmod 0755 $RPM_BUILD_ROOT/usr/sbin/perf
+mkdir -p $RPM_BUILD_ROOT%{_datadir}/doc/perf
+cp tools/perf/Documentation/examples.txt $RPM_BUILD_ROOT%{_datadir}/doc/perf
+%endif # with_perf
+
+%if %{with_headers}
+# Install kernel headers
+make ARCH=%{hdrarch} INSTALL_HDR_PATH=$RPM_BUILD_ROOT/usr headers_install
+
+# Do headers_check but don't die if it fails.
+make ARCH=%{hdrarch} INSTALL_HDR_PATH=$RPM_BUILD_ROOT/usr headers_check \
+ > hdrwarnings.txt || :
+if grep -q exist hdrwarnings.txt; then
+ sed s:^$RPM_BUILD_ROOT/usr/include/:: hdrwarnings.txt
+ # Temporarily cause a build failure if header inconsistencies.
+ # exit 1
+fi
+
+find $RPM_BUILD_ROOT/usr/include \
+ \( -name .install -o -name .check -o \
+ -name ..install.cmd -o -name ..check.cmd \) | xargs rm -f
+
+# glibc provides scsi headers for itself, for now
+rm -rf $RPM_BUILD_ROOT/usr/include/scsi
+rm -f $RPM_BUILD_ROOT/usr/include/asm*/atomic.h
+rm -f $RPM_BUILD_ROOT/usr/include/asm*/io.h
+rm -f $RPM_BUILD_ROOT/usr/include/asm*/irq.h
+%endif
+
+%if %{with_firmware}
+%{build_firmware}
+%endif
+
+%if %{with_bootwrapper}
+make DESTDIR=$RPM_BUILD_ROOT bootwrapper_install WRAPPER_OBJDIR=%{_libdir}/kernel-wrapper WRAPPER_DTSDIR=%{_libdir}/kernel-wrapper/dts
+%endif
+
+
+###
+### clean
+###
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+###
+### scripts
+###
+
+#
+# This macro defines a %%post script for a kernel*-devel package.
+# %%kernel_devel_post [<subpackage>]
+#
+%define kernel_devel_post() \
+%{expand:%%post %{?1:%{1}-}devel}\
+if [ -f /etc/sysconfig/kernel ]\
+then\
+ . /etc/sysconfig/kernel || exit $?\
+fi\
+if [ "$HARDLINK" != "no" -a -x /usr/sbin/hardlink ]\
+then\
+ (cd /usr/src/kernels/%{KVERREL}%{?1:.%{1}} &&\
+ /usr/bin/find . -type f | while read f; do\
+ hardlink -c /usr/src/kernels/*.fc*.*/$f $f\
+ done)\
+fi\
+%{nil}
+
+# This macro defines a %%posttrans script for a kernel package.
+# %%kernel_variant_posttrans [<subpackage>]
+# More text can follow to go at the end of this variant's %%post.
+#
+%define kernel_variant_posttrans() \
+%{expand:%%posttrans %{?1}}\
+/sbin/new-kernel-pkg --package kernel%{?1:-%{1}} --rpmposttrans %{KVERREL}%{?1:.%{1}} || exit $?\
+%{nil}
+
+#
+# This macro defines a %%post script for a kernel package and its devel package.
+# %%kernel_variant_post [-v <subpackage>] [-r <replace>]
+# More text can follow to go at the end of this variant's %%post.
+#
+%define kernel_variant_post(v:r:) \
+%{expand:%%kernel_devel_post %{?-v*}}\
+%{expand:%%kernel_variant_posttrans %{?-v*}}\
+%{expand:%%post %{?-v*}}\
+%{-r:\
+if [ `uname -i` == "x86_64" -o `uname -i` == "i386" ] &&\
+ [ -f /etc/sysconfig/kernel ]; then\
+ /bin/sed -r -i -e 's/^DEFAULTKERNEL=%{-r*}$/DEFAULTKERNEL=kernel%{?-v:-%{-v*}}/' /etc/sysconfig/kernel || exit $?\
+fi}\
+%{expand:\
+%if %{with_dracut}\
+/sbin/new-kernel-pkg --package kernel%{?-v:-%{-v*}} --mkinitrd --dracut --depmod --install %{KVERREL}%{?-v:.%{-v*}} || exit $?\
+%else\
+/sbin/new-kernel-pkg --package kernel%{?-v:-%{-v*}} --mkinitrd --depmod --install %{KVERREL}%{?-v:.%{-v*}} || exit $?\
+%endif}\
+#if [ -x /sbin/weak-modules ]\
+#then\
+# /sbin/weak-modules --add-kernel %{KVERREL}%{?-v*} || exit $?\
+#fi\
+%{nil}
+
+#
+# This macro defines a %%preun script for a kernel package.
+# %%kernel_variant_preun <subpackage>
+#
+%define kernel_variant_preun() \
+%{expand:%%preun %{?1}}\
+/sbin/new-kernel-pkg --rminitrd --rmmoddep --remove %{KVERREL}%{?1:.%{1}} || exit $?\
+#if [ -x /sbin/weak-modules ]\
+#then\
+# /sbin/weak-modules --remove-kernel %{KVERREL}%{?1} || exit $?\
+#fi\
+%{nil}
+
+%kernel_variant_preun
+%ifarch x86_64
+%kernel_variant_post -r (kernel-smp|kernel-xen)
+%else
+%kernel_variant_post -r kernel-smp
+%endif
+
+%kernel_variant_preun smp
+%kernel_variant_post -v smp
+
+%kernel_variant_preun PAE
+%kernel_variant_post -v PAE -r (kernel|kernel-smp|kernel-xen)
+
+%kernel_variant_preun debug
+%kernel_variant_post -v debug
+
+%kernel_variant_post -v PAEdebug -r (kernel|kernel-smp|kernel-xen)
+%kernel_variant_preun PAEdebug
+
+if [ -x /sbin/ldconfig ]
+then
+ /sbin/ldconfig -X || exit $?
+fi
+
+###
+### file lists
+###
+
+%if %{with_headers}
+%files headers
+%defattr(-,root,root)
+/usr/include/*
+%endif
+
+%if %{with_firmware}
+%files firmware
+%defattr(-,root,root)
+/lib/firmware/*
+%doc linux-%{kversion}.%{_target_cpu}/firmware/WHENCE
+%endif
+
+%if %{with_bootwrapper}
+%files bootwrapper
+%defattr(-,root,root)
+/usr/sbin/*
+%{_libdir}/kernel-wrapper
+%endif
+
+# only some architecture builds need kernel-doc
+%if %{with_doc}
+%files doc
+%defattr(-,root,root)
+%{_datadir}/doc/kernel-doc-%{rpmversion}/Documentation/*
+%dir %{_datadir}/doc/kernel-doc-%{rpmversion}/Documentation
+%dir %{_datadir}/doc/kernel-doc-%{rpmversion}
+%{_datadir}/man/man9/*
+%endif
+
+%if %{with_perf}
+%files -n perf
+%defattr(-,root,root)
+%{_datadir}/doc/perf
+/usr/sbin/perf
+%{_datadir}/man/man1/*
+%endif
+
+# This is %{image_install_path} on an arch where that includes ELF files,
+# or empty otherwise.
+%define elf_image_install_path %{?kernel_image_elf:%{image_install_path}}
+
+#
+# This macro defines the %%files sections for a kernel package
+# and its devel and debuginfo packages.
+# %%kernel_variant_files [-k vmlinux] <condition> <subpackage>
+#
+%define kernel_variant_files(k:) \
+%if %{1}\
+%{expand:%%files %{?2}}\
+%defattr(-,root,root)\
+/%{image_install_path}/%{?-k:%{-k*}}%{!?-k:vmlinuz}-%{KVERREL}%{?2:.%{2}}\
+/boot/System.map-%{KVERREL}%{?2:.%{2}}\
+%if %{with_perftool}\
+/usr/libexec/perf.%{KVERREL}%{?2:.%{2}}\
+%endif\
+#/boot/symvers-%{KVERREL}%{?2:.%{2}}.gz\
+/boot/config-%{KVERREL}%{?2:.%{2}}\
+%dir /lib/modules/%{KVERREL}%{?2:.%{2}}\
+/lib/modules/%{KVERREL}%{?2:.%{2}}/kernel\
+/lib/modules/%{KVERREL}%{?2:.%{2}}/build\
+/lib/modules/%{KVERREL}%{?2:.%{2}}/source\
+/lib/modules/%{KVERREL}%{?2:.%{2}}/extra\
+/lib/modules/%{KVERREL}%{?2:.%{2}}/updates\
+/lib/modules/%{KVERREL}%{?2:.%{2}}/weak-updates\
+%ifarch %{vdso_arches}\
+/lib/modules/%{KVERREL}%{?2:.%{2}}/vdso\
+/etc/ld.so.conf.d/kernel-%{KVERREL}%{?2:.%{2}}.conf\
+%endif\
+/lib/modules/%{KVERREL}%{?2:.%{2}}/modules.*\
+%if %{with_dracut}\
+%ghost /boot/initramfs-%{KVERREL}%{?2:.%{2}}.img\
+%else\
+%ghost /boot/initrd-%{KVERREL}%{?2:.%{2}}.img\
+%endif\
+%{expand:%%files %{?2:%{2}-}devel}\
+%defattr(-,root,root)\
+%verify(not mtime) /usr/src/kernels/%{KVERREL}%{?2:.%{2}}\
+/usr/src/kernels/%{KVERREL}%{?2:.%{2}}\
+%if %{with_debuginfo}\
+%ifnarch noarch\
+%if %{fancy_debuginfo}\
+%{expand:%%files -f debuginfo%{?2}.list %{?2:%{2}-}debuginfo}\
+%else\
+%{expand:%%files %{?2:%{2}-}debuginfo}\
+%endif\
+%defattr(-,root,root)\
+%if !%{fancy_debuginfo}\
+%if "%{elf_image_install_path}" != ""\
+%{debuginfodir}/%{elf_image_install_path}/*-%{KVERREL}%{?2:.%{2}}.debug\
+%endif\
+%{debuginfodir}/lib/modules/%{KVERREL}%{?2:.%{2}}\
+%{debuginfodir}/usr/src/kernels/%{KVERREL}%{?2:.%{2}}\
+%endif\
+%endif\
+%endif\
+%endif\
+%{nil}
+
+
+%kernel_variant_files %{with_up}
+%kernel_variant_files %{with_smp} smp
+%kernel_variant_files %{with_debug} debug
+%kernel_variant_files %{with_pae} PAE
+%kernel_variant_files %{with_pae_debug} PAEdebug
+%kernel_variant_files -k vmlinux %{with_kdump} kdump
+
+%changelog
+* Tue Jul 27 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.16-154
+- xfs-prevent-swapext-from-operating-on-write-only-files.patch:
+ CVE-2010-2266
+
+* Tue Jul 27 2010 Jarod Wilson <jarod@redhat.com> 2.6.32.16-153
+- kvm-mmu-fix-conflict-access-permissions-in-direct-sp.patch:
+ Fix crash in guest Python programs (#610911)
+
+* Mon Jul 26 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.16-152
+- usb-obey-the-sysfs-power-wakeup-setting.patch:
+ Restore ability of USB devices to wake the machine (F13#617559)
+
+* Mon Jul 26 2010 Eric Sandeen <sandeen@redhat.com> 2.6.32.16-151
+- Fix ext4 metadata vs. quota reservation bug (#608770)
+
+* Fri Jul 23 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.16-150
+- drm-intel-945gm-stability-fixes.patch
+- Make doc build single-threaded to prevent build failures.
+
+* Fri Jul 23 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.16-149
+- drm-i915-fix-hibernate-memory-corruption.patch,
+ drm-i915-add-reclaimable-to-page-allocations.patch:
+ Fixes for hibernation memory corruption bugs introduced in 2.6.32.8
+
+* Fri Jul 23 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.16-148
+- ext4-make-sure-the-move_ext-ioctl-can-t-overwrite-append-only-files.patch
+ (CVE-2010-2066)
+
+* Thu Jul 22 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.16-147
+- cifs-fix-malicious-redirect-problem-in-the-dns-lookup-code.patch:
+ Fix a malicious redirect problem in the DNS lookup code (CVE-2010-2524)
+
+* Wed Jul 21 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.16-146
+- pci-acpi-disable-aspm-if-no-osc.patch, pci-aspm-dont-enable-too-early.patch
+ PCI layer fixes for problems with hardware that doesn't support ASPM.
+
+* Wed Jul 21 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.16-145
+- crypto-add-async-hash-testing.patch: fix the rest of the errors
+ reported during crypto testing (#571577)
+
+* Wed Jul 21 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.16-144
+- Fix inotify-oneshot-support patch so it builds.
+- crypto-testmgr-add-null-test-for-aesni.patch:
+ Add tests for aesni crypto module (#571577)
+
+* Fri Jul 16 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.16-143
+- inotify-fix-inotify-oneshot-support.patch,
+ inotify-send-IN_UNMOUNT-events.patch:
+ Fix broken oneshot support and missing umount events. (F13#607327)
+
+* Wed Jul 14 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.16-142
+- Drop Intel Moorestown support.
+
+* Wed Jul 07 2010 Jarod Wilson <jarod@redhat.com> 2.6.32.16-141
+- Really make hdpvr i2c IR part register this time, so something can
+ actually be bound to it (like, say, lirc_zilog)
+
+* Tue Jul 06 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.16-140
+- ethtool-fix-buffer-overflow.patch: ethtool buffer overflow (CVE-2010-2478)
+- sched-fix-over-scheduling-bug.patch: fix scheduler bug with CGROUPS
+
+* Tue Jul 06 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.16-139
+- Linux 2.6.32.16
+
+* Fri Jul 02 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.16-138.rc1
+- Linux 2.6.32.16-rc1
+- Drop patches merged upstream:
+ btrfs-should-add-permission-check-for-setfacl.patch
+ linux-2.6-wireless_-report-reasonable-bitrate-for-MCS-rates-through-wext.patch
+ drm-upgrayed-fixes.patch
+ iwlwifi_-check-for-aggregation-frame-and-queue.patch
+ iwlwifi-recalculate-average-tpt-if-not-current.patch
+ iwlwifi-update-supported-PCI_ID-list-for-5xx0-series.patch
+ keys-find-keyring-by-name-can-gain-access-to-the-freed-keyring.patch
+- Fix up wireless patches and usb-wwan-update.patch to apply on 2.6.32.16
+
+* Thu Jul 01 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.15-137
+- Linux 2.6.32.15
+
+* Mon Jun 28 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.14-136
+- Drop unused ro-nx patches.
+- Add support for sky2 Optima chip.
+
+* Wed Jun 23 2010 Kyle McMartin <kyle@redhat.com> 2.6.32.14-135
+- l2tp: fix oops in pppol2tp_xmit (rhbz#607054)
+
+* Tue Jun 15 2010 Kyle McMartin <kyle@redhat.com> 2.6.32.14-134
+- Fix btrfs ACL fixes... commit 431547b3c4533b8c7fd150ab36980b9a3147797b
+ changed them to take a struct dentry instead of struct inode after
+ 2.6.32.
+
+* Tue Jun 15 2010 John W. Linville <linville@redhat.com> 2.6.32.14-133
+- iwlwifi: cancel scan watchdog in iwl_bg_abort_scan (#604264)
+
+* Mon Jun 14 2010 Kyle McMartin <kyle@redhat.com> 2.6.32.14-132
+- Add in ACL fixes to btrfs from CVE-2010-2071.
+
+* Sun Jun 13 2010 Kyle McMartin <kyle@redhat.com> 2.6.32.14-131
+- mac80211/iwlwifi fix connections to some APs (rhbz#558002)
+ patches from sgruszka@.
+
+* Wed Jun 2 2010 John W. Linville <linville@redhat.com>
+- iwlwifi: update supported PCI_ID list for 5xx0 series (#599153)
+
+* Thu May 27 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.14-127
+- CVE-2010-1437: keyrings: find_keyring_by_name() can gain the freed keyring
+
+* Wed May 26 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.14-126
+- Linux 2.6.32.14
+- Drop merged patches:
+ btrfs-check-for-read-permission-on-src-file-in-clone-ioctl.patch
+ iwlwifi_-clear-all-the-stop_queue-flag-after-load-firmware.patch
+ revert-ath9k_-fix-lockdep-warning-when-unloading-module.patch
+
+* Mon May 24 2010 John W. Linville <linville@redhat.com>
+- iwlwifi: recalculate average tpt if not current (#588021)
+
+* Mon May 24 2010 John W. Linville <linville@redhat.com>
+- iwlwifi: recover_from_tx_stall (#589777)
+- iwlwifi: fix scan races (#592011)
+- iwlwifi: fix internal scan race (#592011)
+
+* Tue May 18 2010 Kyle McMartin <kyle@redhat.com>
+- btrfs: check for read permission on src file in the clone ioctl
+ (rhbz#593226)
+
+* Mon May 17 2010 Matthew Garrett <mjg@redhat.com>
+- thinkpad-acpi-fix-backlight.patch: Fix backlight control on some recent
+ Thinkpads
+
+* Fri May 14 2010 Jarod Wilson <jarod@redhat.com>
+- Update previous kworld patch w/the one that actually works
+ correctly w/o constant rf tracking filter recalibration
+
+* Thu May 13 2010 Jarod Wilson <jarod@redhat.com> 2.6.32.13-120
+- Restore patch to enable hauppauge hdpvr ir part
+- Enable support for kworld ub435-q and 340u usb atsc tuners
+
+* Wed May 12 2010 Chuck Ebbert <cebbert@redhat.com>
+- Completely drop patches merged in 2.6.32.13
+
+* Wed May 12 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.13-118
+- Linux 2.6.32.13
+
+* Wed May 12 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.13-117.rc1
+- Linux 2.6.32.13-rc1
+- Drop patches:
+ libata-fix-accesses-at-LBA28-boundary.patch
+ hugetlb-fix-infinite-loop-in-get-futex-key.patch
+ reiserfs-fix-permissions-on-reiserfs-priv.patch
+ libiscsi-regression-fix-header-digest-errors.patch
+
+* Wed May 12 2010 Roland McGrath <roland@redhat.com> 2.6.32.12-116
+- utrace update (#590954)
+
+* Fri Apr 30 2010 John W. Linville <linville@redhat.com> 2.6.32.12-115
+- Revert "ath9k: fix lockdep warning when unloading module"
+
+* Tue Apr 27 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.12-114
+- libiscsi-regression-fix-header-digest-errors.patch:
+ fix iscsi header authentication broken in .32 (#583581)
+
+* Tue Apr 27 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.12-113
+- Fix possible data corruption with ext4 mounted with -o discard
+
+* Tue Apr 27 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.12-112
+- hugetlb-fix-infinite-loop-in-get-futex-key.patch (#552257)
+- reiserfs-fix-permissions-on-reiserfs-priv.patch (CVE-2010-1146)
+
+* Mon Apr 26 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.12-111
+- Linux 2.6.32.12
+
+* Fri Apr 23 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.12-110.rc2
+- Linux 2.6.32.12-rc2
+- Drop -rc1 workarounds
+
+* Thu Apr 22 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.12-109.rc1
+- Linux 2.6.32.12-rc1
+- Drop patches merged upstream:
+ xfs_swap_extents-needs-to-handle-dynamic-fork-offsets.patch
+ acpi-ec-allow-multibyte-access-to-ec.patch
+ acpi-ec-limit-burst-to-64-bit.patch
+ vgaarb-fix-userspace-ptr-deref.patch
+ linux-2.6-net-r8169-improved-rx-length-check-errors.patch
+ tcp-fix-icmp-rto-war.patch
+ iwlwifi-fix-nfreed--.patch
+ mac80211_-tear-down-all-agg-queues-when-restart_reconfig-hw.patch
+ iwlwifi_-clear-all-tx-queues-when-firmware-ready.patch
+ iwlwifi_-fix-scan-race.patch
+ b43_-Optimize-PIO-scratchbuffer-usage.patch
+ b43_-Remove-reset-after-fatal-DMA-error.patch
+ b43_-Allow-PIO-mode-to-be-selected-at-module-load.patch
+ b43_-fall-back-gracefully-to-PIO-mode-after-fatal-DMA-errors.patch
+ mac80211_-fix-deferred-hardware-scan-requests.patch
+- Revert -rc1 patch: md-raid5-allow-for-more-than-2-31-chunks.patch
+ ( ERROR: "__umoddi3" [drivers/md/raid456.ko] undefined! )
+- Hotfix -rc1 lockdep patch with fix from LKML
+
+* Wed Apr 21 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.11-108
+- ACPI embedded controller fixes from Fedora 13 (#532161)
+
+* Wed Apr 21 2010 Matthew Garrett <mjg@redhat.com>
+- thinkpad-acpi-add-x100e.patch: Add EC path for Thinkpad X100
+
+* Wed Apr 21 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.11-106
+- libata-fix-accesses-at-LBA28-boundary.patch
+
+* Tue Apr 20 2010 John W. Linville <linville@redhat.com> 2.6.32.11-105
+- mac80211: fix deferred hardware scan requests
+
+* Fri Apr 16 2010 John W. Linville <linville@redhat.com> 2.6.32.11-104
+- b43: Optimize PIO scratchbuffer usage
+- b43: Remove reset after fatal DMA error
+- b43: Allow PIO mode to be selected at module load
+- b43: fall back gracefully to PIO mode after fatal DMA errors
+
+* Wed Apr 14 2010 David Woodhouse <David.Woodhouse@intel.com>
+- Fix autoloading of phy modules (#525966)
+
+* Mon Apr 12 2010 John W. Linville <linville@redhat.com> 2.6.32.11-102
+- patches from Intel to address intermittent firmware failures with iwlagn
+
+* Tue Apr 07 2010 Chuck Ebbert <cebbert@redhat.com>
+- Disable async RAID4/5/6 processing (#575402)
+
+* Tue Apr 06 2010 Chuck Ebbert <cebbert@redhat.com>
+- Build eeepc-laptop driver for x86_64 (F13#565582)
+- Build all of the DVB frontend drivers (F13#578755)
+
+* Mon Apr 05 2010 Jarod Wilson <jarod@redhat.com> 2.6.32.11-99
+- Fix oops in lirc_it87 driver (#579270)
+- Support more imon 0xffdc key combinations
+
+* Sat Apr 03 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.11-98
+- Linux 2.6.32.11
+- Reverted: we already have in our DRM:
+ drm-edid-unify-detailed-block-parsing-between-base-and-extension-blocks.patch
+ drm-i915-fix-gpio-register-detection-logic-for-bios-without-vbt.patch
+- Reverted: already in linux-2.6-usb-wwan-update.patch:
+ usb-qcserial-add-new-device-ids.patch
+- Dropped patches merged upstream:
+ linux-2.6-delalloc-quota-fixes.patch
+ linux-2.6-efi-handover.fix
+ coredump-uid-pipe-check.patch
+ tg3-05-assign-flags-to-fixes-in-start_xmit_dma_bug.patch
+ tg3-06-fix-5906-transmit-hangs.patch
+ linux-2.6-tg3-netpoll.patch
+ iwlwifi-silence-tfds-in-queue-message.patch
+ iwlwifi-use-dma_alloc_coherent.patch
+- Fixups:
+ drm-upgrayedd.patch: lib/lcm.o was added
+ fix-abrtd.patch: coredump-uid-pipe-check went upstream
+
+* Thu Apr 01 2010 Eric Sandeen <sandeen@redhat.com>
+- Fix quota WARN_ON for ext4 (#521914)
+
+* Thu Apr 01 2010 Matthew Garrett <mjg@redhat.com>
+- drm-intel-acpi-populate-didl.patch: Fix backlight hotkeys on some hardware
+- drm-intel-make-lvds-work.patch: Fix screen not turning back on on lid open
+- linux-2.6-usb-wwan-update.patch: Update wwan code and fix qcserial
+
+* Tue Mar 30 2010 John W. Linville <linville@redhat.com> 2.6.32.10-94
+- Avoid null pointer dereference introduced by 'ssb: check for sprom' (#577463)
+
+* Mon Mar 29 2010 John W. Linville <linville@redhat.com> 2.6.32.10-93
+- iwlwifi: fix nfreed--
+- iwlwifi: reset card during probe (#557084)
+- iwlwifi: use dma_alloc_coherent (#574146)
+
+* Mon Mar 29 2010 Ben Skeggs <bskeggs@redhat.com> 2.6.32.10-91
+- nouveau: fix display issues on Dell D620 laptops
+
+* Mon Mar 22 2010 Jarod Wilson <jarod@redhat.com> 2.6.32.10-90
+- A few more imon driver button additions
+- Fix minor init issue w/topseed 0x0008 mceusb transceivers
+
+* Mon Mar 22 2010 Neil Horman <nhorman@redhat.com>
+- Fix tg3 poll controller to not oops (bz 574969)
+
+* Fri Mar 19 2010 John W. Linville <linville@redhat.com> 2.6.32.10-88
+- Revise "ssb: check for sprom" (#533746)
+
+* Fri Mar 19 2010 Jarod Wilson <jarod@redhat.com> 2.6.32.10-86
+- Improve mouse button and pad handling on 0xffdc imon devices
+- Add xmit support to topseed 0x0008 lirc_mceusb transceiver
+
+* Fri Mar 19 2010 John W. Linville <linville@redhat.com> 2.6.32.10-85
+- ssb: check for sprom (#533746)
+
+* Thu Mar 18 2010 Neil Horman <nhorman@redhat.com>
+- Remove TIPC from config (bz 574800)
+
+* Wed Mar 17 2010 John W. Linville <linville@redhat.com> 2.6.32.10-83
+- b43: Rewrite DMA Tx status handling sanity checks
+
+* Tue Mar 16 2010 Jarod Wilson <jarod@redhat.com>
+- Catch imon panel/knob events from older 0xffdc devices
+- Add two more variants of imon mce mode star and pound keys
+
+* Tue Mar 16 2010 Chuck Ebbert <cebbert@redhat.com>
+- iwlwifi-silence-tfds-in-queue-message.patch:
+ don't spam the log when the problem keeps happening
+
+* Tue Mar 16 2010 Jarod Wilson <jarod@redhat.com> 2.6.32.10-78
+- Fix null ptr deref in lirc_imon (#545599)
+- Fix lirc_zilog not loading on cx2341x devices
+
+* Mon Mar 15 2010 Chuck Ebbert <cebbert@redhat.com>
+- Linux 2.6.32.10
+
+* Mon Mar 15 2010 Jarod Wilson <jarod@redhat.com> 2.6.32.10-76.rc1
+- Rebase lirc drivers to latest git tree
+- Copious amounts of imon driver update
+
+* Mon Mar 15 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.10-75.rc1
+- Make the perf package require libdwarf; fix up description (#568309)
+
+* Sun Mar 14 2010 Chuck Ebbert <cebbert@redhat.com>
+- Fix regression in tg3 driver (#571638)
+
+* Sat Mar 13 2010 Chuck Ebbert <cebbert@redhat.com>
+- Linux 2.6.32.10-rc1
+- Manually fix up drm-upgrayedd.patch to apply after this stable patch:
+ drm-i915-use-a-dmi-quirk-to-skip-a-broken-sdvo-tv-output.patch
+- Added stable patches to upstream-reverts; we have these in our backports:
+ drm-ttm-handle-oom-in-ttm_tt_swapout.patch
+ v4l-dvb-13991-gspca_mr973010a-fix-cif-type-1-cameras-not-streaming-on-uhci-controllers.patch
+ drm-i915-disable-tv-hotplug-status-check.patch
+ drm-i915-fix-get_core_clock_speed-for-g33-class-desktop-chips.patch
+ drm-radeon-r6xx-r7xx-possible-security-issue-system-ram-access.patch
+ drm-radeon-kms-r600-r700-don-t-test-ib-if-ib-initialization-fails.patch
+ drm-radeon-kms-forbid-creation-of-framebuffer-with-no-valid-gem-object.patch
+ acpi-i915-blacklist-clevo-m5x0n-bad_lid-state.patch
+- Dropped merged upstream patches:
+ fix-LOOKUP_FOLLOW-on-automount-symlinks.patch
+ fs-exec.c-fix-initial-stack-reservation.patch
+ kms-offb-handoff.patch
+ linux-2.6-input-hid-quirk-hp-touchsmart.patch
+ sparc-align-clone-and-signal-stacks-to-16-bytes.patch
+ sparc-additional-pci-id-xvr-500.patch
+
+* Thu Mar 11 2010 Dennis Gilmore <dennis@ausil.us>
+- add add aditional pci-id for xvr-500
+- sparc stack alignment fix
+
+* Wed Mar 10 2010 Tom "spot" Callaway <tcallawa@redhat.com>
+- cleanup and re-enable sparc selinux mprotect fix
+
+* Wed Mar 03 2010 Dave Airlie <airlied@redhat.com> 2.6.32.9-70
+- drm-upgrayed-fixes: backport a bunch of fixes from upstream
+- should fix AGP slowdowns + rv740 hw.
+- linux-2.6-efi-handover.patch - fix efifb handover from upstream
+
+* Tue Mar 02 2010 Chuck Ebbert <cebbert@redhat.com>
+- Include examples.txt in the perf package (#569506)
+
+* Mon Mar 01 2010 Dave Jones <davej@redhat.com>
+- Don't own /usr/src/kernels any more, it's now owned by filesystem. (#569438)
+
+* Sat Feb 27 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.9-67
+- Fix lookup of automount symlinks (#567813)
+- Fix stack expansion rlimit check broken by a patch in 2.6.32.9
+
+* Thu Feb 25 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.9-66
+- TCP: fix broken RTO calculation causing high CPU load (#567530)
+
+* Thu Feb 25 2010 Ben Skeggs <bskeggs@redhat.com> 2.6.32.9-65
+- nouveau: DP fix for cards with version 0x20 DP tables
+
+* Tue Feb 23 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.9-64
+- Linux 2.6.32.9
+
+* Tue Feb 23 2010 Ben Skeggs <bskeggs@redhat.com> 2.6.32.9-63.rc1
+- nouveau: fix pre-nv17 output detection regression, support for GF8 IGPs
+
+* Mon Feb 22 2010 Kyle McMartin <kyle@redhat.com>
+- coredump-uid-pipe-check.patch: ditto from F-13/
+
+* Mon Feb 22 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.9-61.rc1
+- Drop the PCI device table in the viafb driver -- it was added in
+ 2.6.32 and we don't want the driver to autoload.
+
+* Mon Feb 22 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.9-60.rc1
+- Linux 2.6.32.9-rc1
+- Revert the 14 DRM patches from 2.6.32.9: we already have them
+- Drop other patches merged upstream:
+ wmi-check-wmi-get-event-data-return-value.patch
+ wmi-free-the-allocated-acpi-objects.patch
+ fix-conntrack-bug-with-namespaces.patch
+ prevent-runtime-conntrack-changes.patch
+ fix-crash-with-sys_move_pages.patch
+ futex-handle-futex-value-corruption-gracefully.patch
+ futex-handle-user-space-corruption-gracefully.patch
+ futex_lock_pi-key-refcnt-fix.patch
+ fix-race-in-tty_fasync-properly.patch
+
+* Thu Feb 18 2010 Kyle McMartin <kyle@redhat.com> 2.6.32.8-59
+- ice1712-fix-revo71-mixer-names.patch: fix mixer names for
+ monty. (rhbz#566565)
+
+* Wed Feb 17 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.8-58
+- fix-race-in-tty_fasync-properly.patch: fix for deadlock caused
+ by original patch in 2.6.32.6
+
+* Wed Feb 17 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.8-57
+- CVE-2010-0623 kernel: local DoS via futex_lock_pi
+
+* Wed Feb 17 2010 Chuck Ebbert <cebbert@redhat.com>
+- CVE-2009-4537 kernel: r8169 issue reported at 26c3
+ (fix taken from Red Hat/CentOS 5.4)
+
+* Wed Feb 17 2010 Ben Skeggs <bskeggs@redhat.com> 2.6.32.8.55
+- drm-nouveau-mutex.patch: add fix that didn't quite make it to linus yet
+
+* Wed Feb 17 2010 Ben Skeggs <bskeggs@redhat.com>
+- nouveau: rebase fedora patchset
+- drm-nouveau-shared-fb.patch: drop
+- drm-nouveau-update.patch: drop, included in previous drm update
+
+* Wed Feb 17 2010 Dave Airlie <airlied@redhat.com>
+- drm-upgrayedd.patch - update - needs nouveau patch rebase
+- drop reverted upstream commits since rc8 - also drop other merged patches
+
+* Tue Feb 16 2010 Kyle McMartin <kyle@redhat.com> 2.6.32.8-51
+- vgaarb-fix-userspace-ptr-deref.patch: fix a userspace ptr deref.
+ (rhbz#564246) [and correct upstream diff]
+
+* Tue Feb 16 2010 Kyle McMartin <kyle@redhat.com> 2.6.32.8-50
+- fix-abrtd.patch: backport of nhorman's call_usermode_helper changes
+ from devel/ & linux-next.
+
+* Tue Feb 09 2010 Kyle McMartin <kyle@redhat.com> 2.6.32.8-49
+- Linux 2.6.32.8
+- futex-handle-user-space-corruption-gracefully.patch: Fix oops in
+ the PI futex code. (rhbz#563091)
+
+* Sun Feb 07 2010 Kyle McMartin <kyle@redhat.com> 2.6.32.8-48.rc2
+- xfs: xfs_swap_extents needs to handle dynamic fork offsets (rhbz#510823)
+ from sandeen.
+
+* Sun Feb 07 2010 Kyle McMartin <kyle@redhat.com> 2.6.32.8-47.rc2
+- fix-ima-null-ptr-deref.patch: fix null ptr deref in IMA introduced
+ in 2.6.32-rc5.
+
+* Sat Feb 06 2010 Kyle McMartin <kyle@redhat.com> 2.6.32.8-46.rc2
+- fix-crash-with-sys_move_pages.patch: sys_move_pages doesn't bounds
+ check the node properly.
+
+* Fri Feb 05 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.8-45.rc2
+- Linux 2.6.32.8-rc2
+- Drop fix-net-restore-ip-source-validation.patch
+
+* Fri Feb 05 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.8-44.rc1
+- Fix networking bug in 2.6.32.8-rc1
+
+* Thu Feb 04 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.8-43.rc1
+- Linux 2.6.32.8-rc1
+- Revert three DRM patches from 2.6.32.8:
+ drm-i915-only-enable-hotplug-for-detected-outputs.patch : already in
+ drm-i915-reload-hangcheck-timer-too-for-ironlake.patch : conflicts
+ drm-i915-selectively-enable-self-reclaim.patch : already in
+- Drop patches merged in -stable:
+ linux-2.6-userspace_kvmclock_offset.patch
+ block-fix-bugs-in-bio-integrity-mempool-usage.patch
+
+* Wed Feb 03 2010 Kyle McMartin <kyle@redhat.com>
+- Fix another conntrack issue pointed out by jcm.
+- Fix utrace header. (rhbz#561536)
+
+* Wed Feb 03 2010 Kyle McMartin <kyle@redhat.com>
+- fix-conntrack-bug-with-namespaces.patch: Fix for issue identified by jcm,
+ http://lkml.org/lkml/2010/2/3/112
+
+* Tue Feb 02 2010 David Woodhouse <David.Woodhouse@intel.com> 2.6.32.7-40
+- Disable graphics DMAR unit on Cantiga (#538163)
+
+* Mon Feb 01 2010 Dave Airlie <airlied@redhat.com> 2.6.32.7-39
+- Add two input quirks for HP and eGalax touchscreens.
+
+* Sat Jan 30 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.7-38
+- Fix possible oops in bio-integrity code.
+
+* Thu Jan 28 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.7-37
+- Linux 2.6.32.7
+
+* Mon Jan 25 2010 Kyle McMartin <kyle@redhat.com> 2.6.32.6-36
+- fix gspca mismerge.
+
+* Mon Jan 25 2010 Kyle McMartin <kyle@redhat.com> 2.6.32.6-35
+- stable update 2.6.32.6
+- rebase gspca crud.
+
+* Mon Jan 25 2010 Dave Jones <davej@redhat.com> 2.6.32.5-34
+- Disable CONFIG_X86_CPU_DEBUG
+
+* Sun Jan 24 2010 Hans de Goede <hdegoede@redhat.com> 2.6.32.5-33
+- Rebase gspca usb webcam driver + sub drivers to latest upstream, this
+ adds support for the following webcam bridge chipsets:
+ benq, cpia1, ovfx2, sn9c2028, w996xCF, stv0680;
+ and also includes many many bugfixes (hopefully also fixes 531234)
+
+* Sat Jan 23 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.5-32
+- Linux 2.6.32.5
+- Revert commit c7c85101afd0cb8ce497456d12ee1cad4aad152f from
+ upstream .32.5 because it conflicts with our DRM update.
+
+* Sat Jan 23 2010 Dave Airlie <airlied@redhat.com> 2.6.32.4-31
+- fix dracut depends
+
+* Thu Jan 21 2010 John W. Linville <linville@redhat.com> 2.6.32.4-30
+- Report meaningful values for MCS rates through wireless extensions
+
+* Wed Jan 20 2010 Kyle McMartin <kyle@redhat.com> 2.6.32.4-29
+- add appleir usb driver
+
+* Mon Jan 18 2010 Kyle McMartin <kyle@redhat.com> 2.6.32.4-28
+- Linux stable 2.6.32.4
+- drm-upgrayedd: rebase for related changes in intel_display.c, i915_drv.h
+
+* Mon Jan 18 2010 Ben Skeggs <bskeggs@redhat.com> 2.6.32.3-27
+- various nouveau fixes from upstream
+- dropped drm-nouveau-bios-paranoia.patch, it's upstream now
+
+* Thu Jan 14 2010 Adam Jackson <ajax@redhat.com> 2.6.32.3-26
+- Don't generate i915 TV hotplug interrupts ever ever ever.
+
+* Thu Jan 14 2010 Josef Bacik <josef@toxicpanda.com> 2.6.32.3-25
+- fix null pointer dereference in btrfs acl code
+
+* Wed Jan 13 2010 Dave Airlie <airlied@redhat.com> 2.6.32.3-24
+- drm fixes from upstream - mostly printk stupids + integrate radeon s/r fix
+
+* Wed Jan 13 2010 Dave Airlie <airlied@redhat.com> 2.6.32.3-23
+- crystalhd fix build on powerpc
+
+* Wed Jan 13 2010 Dave Airlie <airlied@redhat.com> 2.6.32.3-22
+- bring back offb handoff patch - fixes G5 + nouveau
+
+* Wed Jan 13 2010 Dave Airlie <airlied@redhat.com> 2.6.32.3-21
+- fix regression in radeon s/r - hangs on suspend
+
+* Wed Jan 13 2010 Dave Airlie <airlied@redhat.com> 2.6.32.3-20
+- force depend ati firmware, better safe than sorry. I don't
+ think dracut and dracut kernel are what I wanted to do.
+
+* Wed Jan 13 2010 Dave Airlie <airlied@redhat.com> 2.6.32.3-19
+- update dracut requires to make sure we get -ati firmware
+- add fbdev multi-card console fix patch
+
+* Tue Jan 12 2010 Jarod Wilson <jarod@redhat.com> 2.6.32.3-18
+- Add Broadcom Crystal HD driver from staging
+
+* Tue Jan 12 2010 Ben Skeggs <bskeggs@redhat.com> 2.6.32.3-17
+- add nouveau to staging Kconfig
+
+* Tue Jan 12 2010 Ben Skeggs <bskeggs@redhat.com> 2.6.32.3-16
+- nouveau: fix nvac noaccel patch, not sure what happened there!
+
+* Tue Jan 12 2010 Ben Skeggs <bskeggs@redhat.com> 2.6.32.3-15
+- nouveau: patches and firmware from F12 that aren't in upstream kernel
+
+* Mon Jan 11 2010 Dave Airlie <airlied@redhat.com> 2.6.32.3-14
+- drm-upgrayedd.patch: rebase to present 2.6.33 (drm-linus)
+- nouveau TODO - comment out no-tv-hp for now, leave patch
+- we can readd if all the upstream goodness didn't fix it
+
+* Mon Jan 11 2010 Kyle McMartin <kyle@redhat.com> 2.6.32.3-13
+- drm-intel-no-tv-hotplug.patch: re-add lost patch from F-12
+ 2.6.31 (#522611, #544671)
+
+* Mon Jan 11 2010 Kyle McMartin <kyle@redhat.com> 2.6.32.3-12
+- Re-enable ATM_HE (#545289)
+
+* Fri Jan 08 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.3-11
+- Add another symbol to look for when generating modules.block
+
+* Thu Jan 07 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.3-10
+- Turn off default powersave mode for AC97 and HDA audio devices
+ due to continuing bug reports (can still be enabled in sysfs.)
+- Remove some patches that are upstream:
+ v4l-dvb-fix-cx25840-firmware-loading.patch
+ fix-9p-fscache.patch
+ ext4-fix-insufficient-checks-in-EXT4_IOC_MOVE_EXT.patch
+
+* Thu Jan 07 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.3-9
+- Change configs to build the acerhdf driver again, requires
+ setting CONFIG_HWMON=y
+
+* Wed Jan 06 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.3-8
+- Linux 2.6.32.3
+
+* Wed Jan 06 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.3-7.rc2
+- Remove obsolete config options (generated .config files are
+ unchanged.)
+
+* Wed Jan 06 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.3-6.rc2
+- Linux 2.6.32.3-rc2
+
+* Tue Jan 05 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.3-5.rc1
+- Linux 2.6.32.3-rc1
+- Drop merged patches:
+ linux-2.6-fix-btusb-autosuspend.patch
+ drm-radeon-fix-crtc-vbl-update-for-r600.patch
+
+* Tue Jan 05 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.2-4
+- Fix WMI driver oopses and a memory leak.
+
+* Tue Jan 05 2010 Chuck Ebbert <cebbert@redhat.com> 2.6.32.2-3
+- Run 'make release', fix up tracing options to match what shipped
+ in F-12, fix CPU count on x86-64.
+
+* Mon Jan 04 2010 Dave Jones <davej@redhat.com>
+- Drop some of the spinlock/vm taint patches. dump_stack() already does same.
+
+* Fri Jan 1 2010 Kyle McMartin <kyle@redhat.com> 2.6.32.2-1
+- Rebased F-12 to 2.6.32, still needs forward porting of patches.
+
+* Thu Dec 24 2009 Kyle McMartin <kyle@redhat.com> 2.6.32.2-15
+- Add patch from dri-devel to fix vblanks on r600.
+ [http://marc.info/?l=dri-devel&m=126137027403059&w=2]
+
+* Fri Dec 18 2009 Kyle McMartin <kyle@redhat.com> 2.6.32.2-14
+- Linux 2.6.32.2
+- dropped upstream patches.
+
+* Fri Dec 18 2009 Roland McGrath <roland@redhat.com> - 2.6.32.1-13
+- minor utrace update
+
+* Thu Dec 17 2009 Matthew Garrett <mjg@redhat.com> 2.6.32.1-12
+- linux-2.6-driver-level-usb-autosuspend.diff: fix so it works properly...
+- linux-2.6-fix-btusb-autosuspend.patch: avoid bluetooth connection drops
+- linux-2.6-enable-btusb-autosuspend.patch: and default it to on
+- linux-2.6-autoload-wmi.patch: autoload WMI drivers
+
+* Thu Dec 17 2009 Jarod Wilson <jarod@redhat.com> 2.6.32.1-11
+- Split off onboard decode imon devices into pure input driver,
+ leaving lirc_imon for the ancient imon devices only
+- Fix NULL ptr deref in lirc_serial (#543886)
+- Assorted lirc_mceusb fixups suggested by Mauro
+- Dropped compat ioctls from lirc_dev, main ioctls should now be
+ compatible between 32-bit and 64-bit (also at Mauro's suggestion)
+
+* Wed Dec 16 2009 Roland McGrath <roland@redhat.com> 2.6.32.1-10
+- utrace update, now testing the utrace-based ptrace!
+
+* Mon Dec 14 2009 Kyle McMartin <kyle@redhat.com> 2.6.32.1-9
+- 2.6.32.1
+- ext4 patches and more...
+
+* Wed Dec 09 2009 Kyle McMartin <kyle@redhat.com> 2.6.32-8
+- Add a patch off lkml from krh to fix perf when DEBUG_PERF_USE_VMALLOC
+ (rhbz#542791)
+- Re-enable CONFIG_DEBUG_PERF_USE_VMALLOC on debug kernels.
+
+* Wed Dec 09 2009 Kyle McMartin <kyle@redhat.com> 2.6.32-7
+- ext4-fix-insufficient-checks-in-EXT4_IOC_MOVE_EXT.patch: CVE-2009-4131
+ fix insufficient permission checking which could result in arbitrary
+ data corruption by a local unprivileged user.
+
+* Tue Dec 08 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.32-6
+- Copy fix for #540580 from F-12.
+
+* Tue Dec 08 2009 Kyle McMartin <kyle@redhat.com> 2.6.32-5
+- new rpm changes:
+ - %{PACKAGE_VERSION} -> %{version}
+ - %{PACKAGE_RELEASE} -> %{release}
+
+* Tue Dec 08 2009 Kyle McMartin <kyle@redhat.com> 2.6.32-4
+- Disable CONFIG_DEBUG_PERF_USE_VMALLOC for now, causes issues
+ on x86_64. (rhbz#542791)
+
+* Mon Dec 7 2009 Justin M. Forbes <jforbes@redhat.com> 2.6.32-3
+- Allow userspace to adjust kvmclock offset (#530389)
+
+* Mon Dec 7 2009 Steve Dickson <steved@redhat.com> 2.6.32-2
+- Updated the NFS4 pseudo root code to the latest release.
+
+* Thu Dec 03 2009 Kyle McMartin <kyle@redhat.com> 2.6.32-1
+- Linux 2.6.32
+
+* Wed Dec 02 2009 Kyle McMartin <kyle@redhat.com> 2.6.32-0.65.rc8.git5
+- 2.6.32-rc8-git5
+- nuke 9p cachefiles fix, upstream.
+- SLOW_WORK_PROC was renamed to SLOW_WORK_DEBUG, debugfs instead of procfs.
+
+* Wed Dec 02 2009 John W. Linville <linville@redhat.com> 2.6.32-0.64.rc8.git2
+- ath9k: add fixes suggested by upstream maintainer
+
+* Wed Dec 02 2009 David Woodhouse <David.Woodhouse@intel.com> 2.6.32-0.63.rc8.git2
+- forward port IOMMU fixes from F-12 for HP BIOS brokenness
+- Fix oops with intel_iommu=igfx_off
+- agp/intel: Clear full GTT at startup
+
+* Wed Dec 02 2009 Dave Airlie <airlied@redhat.com> 2.6.32-0.62.rc8.git2
+- forward port radeon fixes from F-12 + add radeon display port support
+
+* Mon Nov 30 2009 Kyle McMartin <kyle@redhat.com> 2.6.32-0.61.rc8.git2
+- fix-9p-fscache.patch: fix build.
+
+* Mon Nov 30 2009 Kyle McMartin <kyle@redhat.com> 2.6.32-0.60.rc8.git2
+- 2.6.32-rc8-git2 daily snapshot
+- nuke include/generated nuke-age since the patch was reverted upstream
+- config changes:
+ - generic:
+ +CONFIG_FSCACHE_OBJECT_LIST=y
+ +CONFIG_SLOW_WORK_PROC=y
+
+* Mon Nov 30 2009 Kyle McMartin <kyle@redhat.com>
+- drm-i915-fix-sync-to-vbl-when-vga-is-off.patch: add, (rhbz#541670)
+
+* Sun Nov 29 2009 Kyle McMartin <kyle@redhat.com>
+- linux-2.6-sysrq-c.patch: drop, was made consistent upstream.
+
+* Sat Nov 28 2009 Jarod Wilson <jarod@redhat.com> 2.6.32-0.55.rc8.git1
+- add device name to lirc_zilog, fixes issues w/multiple target devices
+- add lirc_imon pure input mode support for onboard decode devices
+
+* Fri Nov 27 2009 Dave Airlie <airlied@redhat.com> 2.6.32-0.54.rc8.git1
+- attempt to put nouveau back - same patch as F-12 should work
+
+* Mon Nov 23 2009 Roland McGrath <roland@redhat.com>
+- Install vmlinux.id file in kernel-devel rpm.
+
+* Fri Nov 20 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.32-0.52.rc8.git1
+- 2.6.32-rc8-git1
+- Enable CONFIG_MEMORY_HOTPLUG (and HOTREMOVE) on x86_64
+
+* Thu Nov 19 2009 Kyle McMartin <kyle@redhat.com> 2.6.32-0.51.rc7.git2
+- Oops, re-enable debug builds for rawhide... didn't mean to commit that.
+
+* Thu Nov 19 2009 Kyle McMartin <kyle@redhat.com> 2.6.32-0.50.rc7.git2
+- Disable FUNCTION_TRACER and DYNAMIC_FTRACE in non-debug builds for
+ Fedora 13. Some pondering required to see if it's actually worth doing
+ though. Anecdotal evidence worth half as much as benchmarks.
+ STACK_TRACER selects FUNCTION_TRACER, so it has to go off too, sadly,
+ since it hooks every mcount to log the stack depth for the task.
+
+* Thu Nov 19 2009 Kyle McMartin <kyle@redhat.com> 2.6.32-0.49.rc7.git2
+- 2.6.32-rc7-git2
+
+* Mon Nov 16 2009 Dave Airlie <airlied@redhat.com> 2.6.32-0.48.rc7.git1
+- backout gpg change now that koji is fixed
+
+* Sun Nov 15 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.32-0.47.rc7.git1
+- Buildrequire gpg
+
+* Sun Nov 15 2009 Chuck Ebbert <cebbert@redhat.com>
+- Fix oops in VIA Padlock driver.
+
+* Sun Nov 15 2009 Chuck Ebbert <cebbert@redhat.com>
+- Linux 2.6.32-rc7-git1
+
+* Fri Nov 13 2009 Chuck Ebbert <cebbert@redhat.com>
+- Linux 2.6.32-rc7
+
+* Thu Nov 05 2009 Jarod Wilson <jarod@redhat.com>
+- Add --with dbgonly rpmbuild option to build only debug kernels
+
+* Wed Nov 04 2009 Kyle McMartin <kyle@redhat.com>
+- Make JBD2_DEBUG a toggleable config option.
+
+* Wed Nov 04 2009 Kyle McMartin <kyle@redhat.com> 2.6.32-0.39.rc6.git0
+- 2.6.32-rc6, fix for NULL ptr deref in cfg80211.
+
+* Mon Nov 02 2009 Kyle McMartin <kyle@redhat.com> 2.6.32-0.39.rc5.git6
+- 2.6.32-rc5-git6 (with sandeen's reversion of "ext4: Remove journal_checksum
+ mount option and enable it by default")
+
+* Mon Nov 02 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.32-rc5-git5
+
+* Tue Oct 27 2009 John W. Linville <linville@redhat.com>
+- Disable build of prism54 module
+
+* Tue Oct 27 2009 Dave Airlie <airlied@redhat.com>
+- Get dd command line args correct.
+
+* Mon Oct 26 2009 Dave Jones <davej@redhat.com>
+- Make a 20MB initramfs file so rpm gets its diskspace calculations right. (#530778)
+
+* Sat Oct 23 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.32-rc5-git3
+- Drop merged patch:
+ linux-2.6-virtio_blk-revert-QUEUE_FLAG_VIRT-addition.patch
+
+* Sat Oct 17 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.32-0.33.rc5.git1
+- 2.6.32-rc5-git1
+
+* Fri Oct 16 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.32-rc5
+- New config option: CONFIG_VMXNET3=m
+
+* Wed Oct 14 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.32-rc4-git4
+
+* Wed Oct 14 2009 Steve Dickson <steved@redhat.com>
+- Updated the NFS v4 pseudo root patch so it will apply
+- Fixed hang during NFS installs (bz 528537)
+
+* Wed Oct 14 2009 Peter Jones <pjones@redhat.com>
+- Add scsi_register_device_handler to modules.block's symbol list so
+ we'll have scsi device handlers in installer images.
+
+* Tue Oct 13 2009 Kyle McMartin <kyle@redhat.com>
+- Always build perf docs, regardless of whether we build kernel-doc.
+ Seems rather unfair to not ship the manpages half the time.
+ Also, drop BuildRequires %if when not with_doc, the rules about %if
+ there are f*!&^ing complicated.
+
+* Tue Oct 13 2009 Kyle McMartin <kyle@redhat.com>
+- Build perf manpages properly.
+
+* Tue Oct 13 2009 Dave Airlie <airlied@redhat.com>
+- cleanup some of drm vga arb bits that are upstream
+
+* Mon Oct 12 2009 Jarod Wilson <jarod@redhat.com>
+- Merge lirc compile fixes into lirc patch
+- Refresh lirc patch with additional irq handling fixage
+- Fix IR transmit on port 1 of 1st-gen mceusb transceiver
+- Support another mouse button variant on imon devices
+
+* Mon Oct 12 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.32-0.24.rc4.git0
+- Last-minute USB fix from upstream.
+
+* Sun Oct 11 2009 Chuck Ebbert <cebbert@redhat.com>
+- Fix lirc build after header changes.
+- Fix bug in lirc interrupt processing.
+
+* Sun Oct 11 2009 Chuck Ebbert <cebbert@redhat.com>
+- Fix up multiple definition of debug options
+ (EXT4_DEBUG, DEBUG_FORCE_WEAK_PER_CPU)
+
+* Sun Oct 11 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.32-rc4
+- New config options:
+ CONFIG_BE2ISCSI=m
+ CONFIG_SCSI_BFA_FC=m
+ CONFIG_USB_MUSB_HDRC is not set
+
+* Sun Oct 11 2009 Kyle McMartin <kyle@redhat.com>
+- 2.6.32-rc3-git3
+
+* Thu Oct 08 2009 Ben Skeggs <bskeggs@redhat.com>
+- ppc: compile nvidiafb as a module only, nvidiafb+nouveau = bang! (rh#491308)
+
+* Wed Oct 07 2009 Dave Jones <davej@redhat.com>
+- Enable FUNCTION_GRAPH_TRACER on x86-64.
+
+* Wed Oct 07 2009 Dave Jones <davej@redhat.com>
+- Disable CONFIG_IRQSOFF_TRACER on srostedt's recommendation.
+ (Adds unwanted overhead when not in use).
+
+* Sun Oct 04 2009 Kyle McMartin <kyle@redhat.com> 2.6.32-0.17.rc3.git0
+- 2.6.32-rc3 (bah, rebase script didn't catch it.)
+
+* Sun Oct 04 2009 Kyle McMartin <kyle@redhat.com>
+- 2.6.32-rc1-git7
+- [x86,x86_64] ACPI_PROCESSOR_AGGREGATOR=m
+
+* Mon Sep 28 2009 Kyle McMartin <kyle@redhat.com>
+- 2.6.32-rc1
+- rebased crash-driver patchset, ia64_ksyms.c conflicts. move x86 crash.h
+ file to the right place.
+- full changelog forthcoming & to fedora-kernel-list.
+
+* Mon Sep 28 2009 Kyle McMartin <kyle@redhat.com>
+- sick of rejects.
+
+* Mon Sep 28 2009 Chuck Ebbert <cebbert@redhat.com>
+- Fix up some items missing in make debug vs. make release,
+ rearrange so the options are in the same order.
+- Add new debug options:
+ CONFIG_EXT4_DEBUG
+ CONFIG_DEBUG_FORCE_WEAK_PER_CPU
+
+* Sun Sep 27 2009 Kyle McMartin <kyle@redhat.com>
+- Must now make mrproper after each config pass, due to Kbuild
+ stashing away the $ARCH variable.
+
+* Sun Sep 27 2009 Kyle McMartin <kyle@redhat.com>
+- 2.6.31-git18
+- rebased:
+ - hdpvr-ir-enable.patch
+ - linux-2.6-build-nonintconfig.patch
+ - linux-2.6-debug-sizeof-structs.patch
+ - linux-2.6-debug-vm-would-have-oomkilled.patch
+ - linux-2.6-execshield.patch
+ - linux-2.6-makefile-after_link.patch
+ - linux-2.6-serial-460800.patch
+ - linux-2.6-utrace.patch
+ - via-hwmon-temp-sensor.patch
+- merged:
+ - linux-2.6-tracehook.patch
+ - linux-2.6-die-closed-source-bios-muppets-die.patch
+ - linux-2.6-intel-iommu-updates.patch
+ - linux-2.6-ksm.patch
+ - linux-2.6-ksm-updates.patch
+ - linux-2.6-ksm-fix-munlock.patch
+ - linux-2.6-vga-arb.patch
+ - v4l-dvb-fix-cx25840-firmware-loading.patch
+ - linux-2.6-rtc-show-hctosys.patch
+
+* Fri Sep 18 2009 Dave Jones <davej@redhat.com>
+- %ghost the dracut initramfs file.
+
+* Thu Sep 17 2009 Hans de Goede <hdegoede@redhat.com>
+- Now that we have %%post generation of dracut images we do not need to
+ Require dracut-kernel anymore
+
+* Thu Sep 17 2009 Chuck Ebbert <cebbert@redhat.com>
+- Disable drm-nouveau too -- it won't build without other
+ drm updates.
+
+* Wed Sep 16 2009 Roland McGrath <roland@redhat.com>
+- Remove workaround for gcc bug #521991, now fixed.
+
+* Tue Sep 15 2009 Kyle McMartin <kyle@redhat.com>
+- 2.6.31-git4
+- rebased:
+ - linux-2.6-execshield.patch: split paravirt_types.h
+ - linux-2.6-buildnonintconfig.patch
+- disabled:
+ - ksm, drm.
+- merged:
+ - linux-2.6-kvm-pvmmu-do-not-batch-pte-updates-from-interrupt-context.patch
+ - linux-2.6-kvm-vmx-check-cpl-before-emulating-debug-register-access.patch
+ - linux-2.6-use-__pa_symbol-to-calculate-address-of-C-symbol.patch
+ - linux-2.6-xen-stack-protector-fix.patch
+ - linux-2.6-bluetooth-autosuspend.diff
+ - hid-ignore-all-recent-imon-devices.patch
+- config changes:
+ - arm:
+ - CONFIG_HIGHPTE off, seems safer this way.
+ - generic:
+ - RDS_RDMA/RDS_TCP=m
+ - SCSI_PMCRAID=m
+ - WLAN=y, CFG80211_DEFAULT_PS=y, NL80211_TESTMODE off.
+ - WL12XX=m
+ - B43_PHY_LP=y
+ - BT_MRVL=m
+ - new MISDN stuff modular.
+ - sparc:
+ - enable PERF_COUNTERS & EVENT_PROFILE
+ - ppc:
+ - XILINX_EMACSLITE=m
+
+* Mon Sep 14 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.31-git2
+- Drop merged patches:
+ sched-introduce-SCHED_RESET_ON_FORK-scheduling-policy-flag.patch
+ linux-2.6-nfs4-ver4opt.patch
+ linux-2.6-alsa-improve-hda-powerdown.patch
+ alsa-tell-user-that-stream-to-be-rewound-is-suspended.patch
+ linux-2.6-ahci-export-capabilities.patch
+- New s390 config option:
+ CONFIG_SCLP_ASYNC=m
+- New generic config options:
+ CONFIG_ATA_VERBOSE_ERROR=y
+ CONFIG_PATA_RDC=m
+ CONFIG_SOUND_OSS_CORE_PRECLAIM=y
+ CONFIG_SND_HDA_PATCH_LOADER=y
+ CONFIG_SND_HDA_CODEC_CIRRUS=y
+ CONFIG_OPROFILE_EVENT_MULTIPLEX=y
+ CONFIG_CRYPTO_VMAC=m
+ CONFIG_CRYPTO_GHASH=m
+- New debug option:
+ CONFIG_DEBUG_CREDENTIALS=y in debug kernels
+
+* Mon Sep 14 2009 Steve Dickson <steved@redhat.com>
+- Added support for -o v4 mount parsing
+
+* Fri Sep 11 2009 Dave Jones <davej@redhat.com>
+- Apply NX/RO to modules
+
+* Fri Sep 11 2009 Dave Jones <davej@redhat.com>
+- Mark kernel data section as NX
+
+* Fri Sep 11 2009 Ben Skeggs <bskeggs@redhat.com>
+- nouveau: bring in Matthew Garret's initial switchable graphics support
+
+* Fri Sep 11 2009 Ben Skeggs <bskeggs@redhat.com>
+- nouveau: fixed use of strap-based panel mode when required (rh#522649)
+- nouveau: temporarily block accel on NVAC chipsets (rh#522361, rh#522575)
+
+* Thu Sep 10 2009 Matthew Garrett <mjg@redhat.com>
+- linux-2.6-ahci-export-capabilities.patch: Backport from upstream
+- linux-2.6-rtc-show-hctosys.patch: Export the hctosys state of an rtc
+- linux-2.6-rfkill-all.patch: Support for keys that toggle all rfkill state
+
+* Thu Sep 10 2009 Ben Skeggs <bskeggs@redhat.com>
+- drm-nouveau.patch: add some scaler-only modes for LVDS, GEM/TTM fixes
+
+* Wed Sep 09 2009 Dennis Gilmore <dennis@ausil.us> 2.6.31-2
+- touch the dracut initrd file when using %%{with_dracut}
+
+* Wed Sep 09 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.31-1
+- Linux 2.6.31
+
+* Wed Sep 09 2009 Chuck Ebbert <cebbert@redhat.com>
+- Enable VXpocket and PDaudioCF PCMCIA sound drivers.
+
+* Wed Sep 09 2009 Hans de Goede <hdegoede@redhat.com>
+- Move to %%post generation of dracut initrd, because of GPL issues surrounding
+ shipping a prebuild initrd
+- Require grubby >= 7.0.4-1, for %%post generation
+
+* Wed Sep 9 2009 Steve Dickson <steved@redhat.com>
+- Updated the NFS4 pseudo root code to the latest release.
+
+* Wed Sep 09 2009 Justin M. Forbes <jforbes@redhat.com>
+- Revert virtio_blk to rotational mode. (#509383)
+
+* Wed Sep 09 2009 Dave Airlie <airlied@redhat.com> 2.6.31-0.219.rc9.git
+- uggh lost nouveau bits in page flip
+
+* Wed Sep 09 2009 Dave Airlie <airlied@redhat.com> 2.6.31-0.218.rc9.git2
+- fix r600 oops with page flip patch (#520766)
+
+* Wed Sep 09 2009 Ben Skeggs <bskeggs@redhat.com>
+- drm-nouveau.patch: fix display resume on pre-G8x chips
+
+* Wed Sep 09 2009 Ben Skeggs <bskeggs@redhat.com>
+- drm-nouveau.patch: add getparam to know using tile_flags is ok for scanout
+
+* Wed Sep 09 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.31-rc9-git2
+
+* Wed Sep 9 2009 Roland McGrath <roland@redhat.com> 2.6.31-0.214.rc9.git1
+- compile with -fno-var-tracking-assignments, work around gcc bug #521991
+
+* Wed Sep 09 2009 Dave Airlie <airlied@redhat.com> 2.6.31-0.213.rc9.git1
+- fix two bugs in r600 kms, fencing + mobile lvds
+
+* Tue Sep 08 2009 Ben Skeggs <bskeggs@redhat.com> 2.6.31-0.212.rc9.git1
+- drm-nouveau.patch: fix ppc build
+
+* Tue Sep 08 2009 Ben Skeggs <bskeggs@redhat.com> 2.6.31-0.211.rc9.git1
+- drm-nouveau.patch: more misc fixes
+
+* Tue Sep 08 2009 Dave Airlie <airlied@redhat.com> 2.6.31-0.210.rc9.git1
+- drm-page-flip.patch: rebase again
+
+* Tue Sep 08 2009 Dave Airlie <airlied@redhat.com> 2.6.31-0.209.rc9.git1
+- drm-next.patch: fix r600 signal interruption return value
+
+* Tue Sep 08 2009 Ben Skeggs <bskeggs@redhat.com> 2.6.31-0.208.rc9.git1
+- drm-nouveau.patch: latest upstream + rebase onto drm-next
+
+* Tue Sep 08 2009 Dave Airlie <airlied@redhat.com> 2.6.31-0.207.rc9.git1
+- drm-vga-arb.patch: update to avoid lockdep + add r600 support
+
+* Tue Sep 08 2009 Dave Airlie <airlied@redhat.com> 2.6.31-0.206.rc9.git1
+- drm: rebase to drm-next - r600 accel + kms should start working now
+
+* Mon Sep 07 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.31-0.205.rc9.git1
+- 2.6.31-rc9-git1
+- Temporarily hack the drm-next patch so it still applies; the result
+ should still be safe to build.
+
+* Sat Sep 05 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.31-0.204.rc9
+- 2.6.31-rc9
+
+* Fri Sep 04 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.31-0.203.rc8.git2
+- Fix kernel build errors when building firmware by removing the
+ .config file before that step and restoring it afterward.
+
+* Thu Sep 03 2009 Adam Jackson <ajax@redhat.com>
+- drm-ddc-caching-bug.patch: Empty the connector's mode list when it's
+ disconnected.
+
+* Thu Sep 03 2009 Jarod Wilson <jarod@redhat.com>
+- Update hdpvr and lirc_zilog drivers for 2.6.31 i2c
+
+* Thu Sep 03 2009 Justin M.Forbes <jforbes@redhat.com>
+- Fix xen guest with stack protector. (#508120)
+- Small kvm fixes.
+
+* Wed Sep 02 2009 Adam Jackson <ajax@redhat.com> 2.6.31-0.199.rc8.git2
+- drm-intel-pm.patch: Disable by default, too flickery on too many machines.
+ Enable with i915.powersave=1.
+
+* Wed Sep 02 2009 Dave Jones <davej@redhat.com>
+- Add missing scriptlet dependancy. (#520788)
+
+* Tue Sep 01 2009 Adam Jackson <ajax@redhat.com>
+- Make DRM less chatty about EDID failures. No one cares.
+
+* Tue Sep 01 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.31-rc8-git2
+- Blank out drm-intel-next: entire contents are now upstream.
+
+* Tue Sep 01 2009 Dave Jones <davej@redhat.com>
+- Make firmware buildarch noarch. (Suggested by drago01 on irc)
+
+* Tue Sep 01 2009 Jarod Wilson <jarod@redhat.com>
+- Fix up lirc_zilog to enable functional IR transmit and receive
+ on the Hauppauge HD PVR
+- Fix audio on PVR-500 when used in same system as HVR-1800 (#480728)
+
+* Sun Aug 30 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.31-rc8-git1
+- Drop linux-2.6-inotify-accounting.patch, merged upstream.
+
+* Sun Aug 30 2009 Jarod Wilson <jarod@redhat.com>
+- fix lirc_imon oops on older devices w/o tx ctrl ep (#520008)
+
+* Fri Aug 28 2009 Eric Paris <eparis@redhat.com> 2.6.31-0.190.rc8
+- fix inotify length accounting and send inotify events
+
+* Fri Aug 28 2009 David Woodhouse <David.Woodhouse@intel.com>
+- Enable Solos DSL driver
+
+* Fri Aug 28 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.31-rc8
+
+* Thu Aug 27 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.31-0.185.rc7.git6
+- 2.6.31-rc7-git6
+- Drop patch merged upstream:
+ xen-fb-probe-fix.patch
+
+* Thu Aug 27 2009 Adam Jackson <ajax@redhat.com>
+- drm-rv710-ucode-fix.patch: Treat successful microcode load on RV710 as,
+ you know, success. (#519718)
+
+* Thu Aug 27 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.31-rc7-git5
+- Drop patch linux-2.6-ima-leak.patch, now merged upstream.
+
+* Wed Aug 26 2009 Jarod Wilson <jarod@redhat.com>
+- Fix up hdpvr ir enable patch for use w/modular i2c (David Engel)
+
+* Wed Aug 26 2009 Eric Paris <eparis@redhat.com>
+- fix iint_cache leak in IMA code
+ drop the ima=0 patch
+
+* Wed Aug 26 2009 Justin M. Forbes <jforbes@redhat.com>
+- Fix munlock with KSM (#516909)
+- Re-enable KSM
+
+* Wed Aug 26 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.31-rc7-git4
+- Drop patches merged upstream:
+ xen-x86-fix-stackprotect.patch
+ xen-x86-no-stackprotect.patch
+
+* Wed Aug 26 2009 Adam Jackson <ajax@redhat.com>
+- drm-intel-next.patch: Update, various output setup fixes.
+
+* Wed Aug 26 2009 David Woodhouse <David.Woodhouse@intel.com>
+- Make WiMAX modular (#512070)
+
+* Tue Aug 25 2009 Kyle McMartin <kyle@redhat.com>
+- allow-disabling-ima.diff: debugging patch... adds ima=0 kernel
+ param to disable initialization of IMA.
+
+* Tue Aug 25 2009 Ben Skeggs <bskeggs@redhat.com> 2.6.31-0.174.rc7.git2
+- drm-nouveau.patch: upstream update, pre-nv50 tv-out + misc fixes
+
+* Tue Aug 25 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.31-0.173.rc7.git2
+- Fix Xen boot (#508120)
+
+* Tue Aug 25 2009 Dave Airlie <airlied@redhat.com>
+- pull in drm-next tree + rebase around it
+
+* Mon Aug 24 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.31-rc7-git2
+
+* Mon Aug 24 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.31-rc7-git1
+
+* Sat Aug 22 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.31-rc7
+
+* Thu Aug 20 2009 Mark McLoughlin <markmc@redhat.com>
+- Disable LZMA for xen (#515831)
+
+* Thu Aug 20 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.31-rc6-git5
+- Fix up drm-r600-kms.patch
+- Drop fix-perf-make-man-failure.patch
+
+* Wed Aug 19 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.31-rc6-git5
+- Revert linux-2.6-debug-vm-would-have-oomkilled.patch to v1.2
+ because upstream changes to oom-kill.c were all reverted.
+
+* Tue Aug 18 2009 Kyle McMartin <kyle@redhat.com>
+- Fix up perf so that it builds docs now that they are fixed.
+- with_docs disables perf docs too. be warned. (logic is that the
+ build deps are (mostly) the same, so if you don't want one, odds are...)
+
+* Tue Aug 18 2009 Dave Jones <davej@redhat.com>
+- 2.6.31-rc6-git3
+
+* Mon Aug 17 2009 Dave Jones <davej@redhat.com> 2.6.31-0.161.rc6.git2
+- 2.6.31-rc6-git2
+
+* Mon Aug 17 2009 Chuck Ebbert <cebbert@redhat.com>
+- Stop generating the (unused) ppc64-kdump.config file.
+
+* Mon Aug 17 2009 Jarod Wilson <jarod@redhat.com>
+- Add new lirc driver for built-in ENE0100 device on some laptops
+
+* Sun Aug 16 2009 Kyle McMartin <kyle@redhat.com> 2.6.31-0.158.rc6
+- Improve the perf script so it prints something helpful if the
+ perf binary doesn't exist.
+
+* Sat Aug 15 2009 Dave Jones <davej@redhat.com> 2.6.31-0.157.rc6
+- Disable KSM patches on a hunch. Chasing the "encrypted VGs don't work" bug.
+
+* Fri Aug 14 2009 Dave Jones <davej@redhat.com> 2.6.31-0.155.rc6
+- 2.6.31-rc6
+
+* Wed Aug 12 2009 Kyle McMartin <kyle@redhat.com>
+- fix perf.
+- move perf to perf.$ver instead of perf-$ver...
+
+* Wed Aug 12 2009 Dennis Gilmore <dennis@ausil.us>
+- Obsolete kernel-smp on sparc64
+- Require grubby >= 7.0.2-1 since thats what introduces the dracut options we use
+
+* Wed Aug 12 2009 Kristian Høgsberg <krh@redhat.com>
+- Fix drm-page-flip.patch to not break radeon kms and to not reset
+ crtc offset into fb on flip.
+
+* Wed Aug 12 2009 Adam Jackson <ajax@redhat.com>
+- Update drm-intel-next patch
+
+* Tue Aug 11 2009 Dennis Gilmore <dennis@ausil.us> - 2.6.31-0.149.rc5.git3
+- disable building the -smp kernel on sparc64
+- disable building kernel-perf on sparc64 syscalls not supported
+
+* Tue Aug 11 2009 Eric Paris <eparis@redhat.com>
+- Enable config IMA
+
+* Tue Aug 11 2009 Ben Skeggs <bskeggs@redhat.com>
+- nouveau: various cleanups and fixes + more sanity checking in dma paths
+
+* Mon Aug 10 2009 Jarod Wilson <jarod@redhat.com>
+- Add new device ID to lirc_mceusb (#512483)
+- Fix some lockdep false positives
+- Add support for setting and enabling iMON clock via sysfs
+- Add tunable pad threshold support to lirc_imon
+- Add new pseudo-IR protocl to lirc_imon for universals w/o a pad
+- Fix mouse device support on older iMON devices
+
+* Mon Aug 10 2009 David Woodhouse <David.Woodhouse@intel.com> 2.6.31-0.145.rc5.git3
+- Merge latest Intel IOMMU fixes and BIOS workarounds, re-enable by default.
+
+* Sun Aug 09 2009 Kyle McMartin <kyle@redhat.com>
+- btusb autosuspend: fix build on !CONFIG_PM by stubbing out
+ suspend/resume methods.
+
+* Sat Aug 08 2009 Dennis Gilmore <dennis@ausil.us> 2.6.31-0.141.rc5.git3
+- disable kgdb on sparc64 uni-processor kernel
+- set max cpus to 256 on sparc64
+- enable AT keyboard on sparc64
+
+* Fri Aug 07 2009 Justin M. Forbes <jforbes@redhat.com>
+- Apply KSM updates from upstream
+
+* Fri Aug 07 2009 Hans de Goede <hdegoede@redhat.com>
+- When building a dracut generic initrd tell new-kernel-pkg to use that
+ instead of running mkinitrd
+
+* Fri Aug 07 2009 Dave Airlie <airlied@redhat.com> 2.6.31-0.139.rc5.git3
+- drm-r600-kms.patch - update r600 KMS
+- drm-radeon-fixes.patch - patches for queue to Linus
+
+* Thu Aug 06 2009 Justin M. Forbes <jforbes@redhat.com> 2.6.31-0.138.rc5.git3
+- Fix kvm virtio_blk errors (#514901)
+
+* Thu Aug 06 2009 Adam Jackson <ajax@redhat.com>
+- Hush DRM vblank warnings, they're constant (and harmless) under DRI2.
+
+* Thu Aug 06 2009 Dave Airlie <airlied@redhat.com> 2.6.31.0.134.rc5.git3
+- fixup vga arb warning at startup and handover between gpus
+
+* Thu Aug 06 2009 Kyle McMartin <kyle@redhat.com> 2.6.31.0.133.rc5.git3
+- die-floppy-die.patch: it's the 21st century, let's not rely on
+ steam powered technology.
+
+* Wed Aug 05 2009 Dave Airlie <airlied@redhat.com> 2.6.31.0.132.rc5.git3
+- revert-ftrace-powerpc-snafu.patch - fix ppc build
+
+* Wed Aug 05 2009 Ben Skeggs <bskeggs@redhat.com>
+- nouveau: respect nomodeset
+
+* Wed Aug 05 2009 Chuck Ebbert <cebbert@redhat.com>
+- Fix /usr/sbin/perf script. (#515494)
+
+* Wed Aug 05 2009 Dave Jones <davej@redhat.com>
+- Fix shift in pci cacheline size printk.
+
+* Wed Aug 05 2009 Dave Airlie <airlied@redhat.com> 2.6.31.0.128.rc5.git3
+- 2.6.31-rc5-git3
+- drop cpufreq + set memory fixes
+
+* Wed Aug 05 2009 Dave Airlie <airlied@redhat.com>
+- Add Jeromes initial r600 kms work.
+- rebase arb patch
+
+* Tue Aug 04 2009 Kyle McMartin <kyle@redhat.com>
+- alsa-tell-user-that-stream-to-be-rewound-is-suspended.patch: apply patch
+ destined for 2.6.32, requested by Lennart.
+
+* Tue Aug 04 2009 Ben Skeggs <bskeggs@redhat.com>
+- nouveau: more code share between nv50/<nv50 kms, bug fixes
+
+* Tue Aug 04 2009 Dave Airlie <airlied@redhat.com>
+- update VGA arb patches again
+
+* Mon Aug 03 2009 Adam Jackson <ajax@redhat.com>
+- Update intel drm from anholt's tree
+- Rebase drm-intel-pm.patch to match
+- Drop gen3 fb hack, merged
+- Drop previous watermark setup change
+
+* Mon Aug 03 2009 Dave Jones <davej@redhat.com> 2.6.31-0.122.rc5.git2
+- 2.6.31-rc5-git2
+
+* Mon Aug 03 2009 Adam Jackson <ajax@redhat.com>
+- (Attempt to) fix watermark setup on Intel 9xx parts.
+
+* Mon Aug 03 2009 Jarod Wilson <jarod@redhat.com>
+- make usbhid driver ignore all recent SoundGraph iMON devices, so the
+ lirc_imon driver can grab them instead
+
+* Mon Aug 03 2009 Dave Airlie <airlied@redhat.com>
+- update VGA arb patches
+
+* Sat Aug 01 2009 David Woodhouse <David.Woodhouse@intel.com> 2.6.31-0.118.rc5
+- Fix boot failures on ppc32 (#514010, #505071)
+
+* Fri Jul 31 2009 Kyle McMartin <kyle@redhat.com> 2.6.31-0.117.rc5
+- Linux 2.6.31-rc5
+
+* Fri Jul 31 2009 Matthew Garrett <mjg@redhat.com>
+- linux-2.6-dell-laptop-rfkill-fix.patch: Fix up Dell rfkill
+
+* Fri Jul 31 2009 Ben Skeggs <bskeggs@redhat.com>
+- nouveau: build against 2.6.31-rc4-git6, fix script parsing on some G8x chips
+
+* Thu Jul 30 2009 Chuck Ebbert <cebbert@redhat.com>
+- Linux 2.6.31-rc4-git6
+ New config item: CONFIG_BATTERY_DS2782 is not set
+- Add last-minute set_memory_wc() fix from LKML.
+
+* Thu Jul 30 2009 Matthew Garrett <mjg@redhat.com>
+- drm-intel-pm.patch: Don't reclock external outputs. Increase the reduced
+ clock slightly to avoid upsetting some hardware. Disable renderclock
+ adjustment for the moment - it's breaking on some hardware.
+
+* Thu Jul 30 2009 Ben Skeggs <bskeggs@redhat.com>
+- nouveau: another DCB 1.5 entry, G80 corruption fixes, small <G80 KMS fix
+
+* Thu Jul 30 2009 Dave Airlie <airlied@redhat.com>
+- fix VGA ARB + kms
+
+* Wed Jul 29 2009 Dave Jones <davej@redhat.com>
+- Add support for dracut. (Harald Hoyer)
+
+* Wed Jul 29 2009 Ben Skeggs <bskeggs@redhat.com>
+- drm-nouveau.patch: nv50/nva0 tiled scanout fixes, nv40 kms fixes
+
+* Wed Jul 29 2009 Chuck Ebbert <cebbert@redhat.com>
+- Linux 2.6.31-rc4-git3
+- Drop linux-2.6-ecryptfs-overflow-fixes.patch, merged upstream now.
+
+* Wed Jul 29 2009 Dave Airlie <airlied@redhat.com>
+- update VGA arb patches
+
+* Tue Jul 28 2009 Adam Jackson <ajax@redhat.com>
+- Remove the pcspkr modalias. If you're still living in 1994, load it
+ by hand.
+
+* Tue Jul 28 2009 Eric Sandeen <sandeen@redhat.com> 2.6.31-0.102.rc4.git2
+- Fix eCryptfs overflow issues (CVE-2009-2406, CVE-2009-2407)
+
+* Tue Jul 28 2009 Kyle McMartin <kyle@redhat.com> 2.6.31-0.101.rc4.git2
+- 2.6.31-rc4-git2
+- rebase linux-2.6-fix-usb-serial-autosuspend.diff
+- config changes:
+ - USB_GSPCA_SN9C20X=m (_EVDEV=y)
+
+* Tue Jul 28 2009 Ben Skeggs <bskeggs@redhat.com>
+- drm-nouveau.patch: cleanup userspace API, various bugfixes.
+ Looks worse than it is, register macros got cleaned up, which
+ touches pretty much everywhere..
+
+* Mon Jul 27 2009 Adam Jackson <ajax@redhat.com>
+- Warn quieter about not finding PCI bus parents for ROM BARs, they're
+ not usually needed and there's nothing you can do about it anyway.
+
+* Mon Jul 27 2009 Matthew Garrett <mjg@redhat.com>
+- linux-2.6-alsa-improve-hda-powerdown.patch - attempt to reduce audio glitches
+ caused by HDA powerdown
+- disable CONFIG_DEBUG_KOBJECT again for now, since it produces huge dmesg spew
+
+* Mon Jul 27 2009 Dave Airlie <airlied@redhat.com>
+- update vga arb code
+
+* Mon Jul 27 2009 Matthew Garrett <mjg@redhat.com>
+- drm-intel-pm.patch - Add runtime PM for Intel graphics
+
+* Fri Jul 24 2009 Kristian Høgsberg <krh@redhat.com>
+- Add drm-page-flip.patch to support vsynced page flipping on intel
+ chipsets.
+- Really add patch.
+- Fix patch to not break nouveau.
+
+* Fri Jul 24 2009 Chuck Ebbert <cebbert@redhat.com>
+- Enable CONFIG_DEBUG_KOBJECT in debug kernels. (#513606)
+
+* Thu Jul 23 2009 Kyle McMartin <kyle@redhat.com>
+- perf BuildRequires binutils-devel now.
+
+* Thu Jul 23 2009 Justin M. Forbes <jforbes@redhat.com>
+- Add KSM support
+
+* Thu Jul 23 2009 Kyle McMartin <kyle@redhat.com> 2.6.31-0.87.rc4
+- Linux 2.6.31-rc4
+- config changes:
+ - USB_CDC_PHONET=m [all]
+ - EVENT_PROFILE=y [i386, x86_64, powerpc, s390]
+
+* Wed Jul 22 2009 Tom "spot" Callaway <tcallawa@redhat.com>
+- We have to override the new %%install behavior because, well... the kernel is special.
+
+* Wed Jul 22 2009 Dave Jones <davej@redhat.com>
+- 2.6.31-rc3-git5
+
+* Wed Jul 22 2009 Ben Skeggs <bskeggs@redhat.com> 2.6.31-0.82.rc3.git4
+- Enable KMS for nouveau
+
+* Wed Jul 22 2009 Ben Skeggs <bskeggs@redhat.com>
+- Update nouveau from upstream (initial suspend/resume + misc bugfixes)
+
+* Mon Jul 20 2009 Adam Jackson <ajax@redhat.com>
+- Disable VGA arbiter patches for a moment
+
+* Mon Jul 20 2009 Adam Jackson <ajax@redhat.com>
+- Revive 4k framebuffers for intel gen3
+
+* Mon Jul 20 2009 Dave Jones <davej@redhat.com> 2.6.31-0.78.rc3.git4
+- Enable CONFIG_RTC_HCTOSYS (#489494)
+
+* Mon Jul 20 2009 Dave Jones <davej@redhat.com> 2.6.31-0.77.rc3.git4
+- Don't build 586 kernels any more.
+
+* Sun Jul 19 2009 Dave Jones <davej@redhat.com> 2.6.31-0.75.rc3.git4
+- build a 'full' package on i686 (Bill Nottingham)
+
+* Sun Jul 19 2009 Dave Jones <davej@redhat.com> 2.6.31-0.74.rc3.git4
+- 2.6.31-rc3-git4
+
+* Sat Jul 18 2009 Matthew Garrett <mjg@redhat.com>
+- linux-2.6-driver-level-usb-autosuspend.diff - allow drivers to enable autopm
+- linux-2.6-fix-usb-serial-autosuspend.diff - fix generic usb-serial autopm
+- linux-2.6-qcserial-autosuspend.diff - enable autopm by default on qcserial
+- linux-2.6-bluetooth-autosuspend.diff - enable autopm by default on btusb
+- linux-2.6-usb-uvc-autosuspend.diff - enable autopm by default on uvc
+
+* Thu Jul 16 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.31-rc3-git3
+
+* Thu Jul 16 2009 Matthew Garrett <mjg@redhat.com>
+- linux-2.6-defaults-aspm.patch - default ASPM to on for PCIe >= 1.1 hardware
+
+* Thu Jul 16 2009 Dave Airlie <airlied@redhat.com> 2.6.31-0.69.rc3
+- linux-2.6-vga-arb.patch - add VGA arbiter.
+- drm-vga-arb.patch - add VGA arbiter support to drm
+
+* Tue Jul 14 2009 Kyle McMartin <kyle@redhat.com> 2.6.31-0.68-rc3
+- 2.6.31-rc3
+- config changes:
+ - RTL8192SU is not set, (staging)
+
+* Mon Jul 13 2009 Kyle McMartin <kyle@redhat.com> 2.6.31-0.67.rc2.git9
+- 2.6.31-rc2-git9
+- config changes:
+ - BLK_DEV_OSD=m
+
+* Mon Jul 13 2009 Ben Skeggs <bskeggs@redhat.com>
+- drm-nouveau.patch: update from upstream
+
+* Fri Jul 10 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.31-rc2-git6
+- Drop dmadebug-spinlock patch -- merged upstream.
+
+* Fri Jul 10 2009 Dave Jones <davej@redhat.com> 2.6.31-0.64.rc2.git5
+- Don't jump through hoops that ppc powerbooks have to on sensible systems
+ in cpufreq_suspend.
+
+* Fri Jul 10 2009 Dave Jones <davej@redhat.com>
+- 2.6.31-rc2-git5
+
+* Thu Jul 09 2009 Dave Jones <davej@redhat.com> 2.6.31-0.62.rc2.git4
+- Use correct spinlock initialization in dma-debug
+
+* Thu Jul 09 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.31-0.61.rc2.git4
+- 2.6.31-rc2-git4
+
+* Thu Jul 09 2009 Jarod Wilson <jarod@redhat.com>
+- Enable IR receiver on the Hauppauge HD PVR
+- Trim the changelog, axing everything before 2.6.29 (see cvs
+ if you still really want to see that far back)
+
+* Wed Jul 08 2009 Dave Jones <davej@redhat.com>
+- Enable a bunch of debugging options that were missed somehow.
+
+* Wed Jul 08 2009 Kyle McMartin <kyle@redhat.com>
+- Bump NR_CPUS on x86_64 to 512.
+
+* Wed Jul 08 2009 Adam Jackson <ajax@redhat.com>
+- drm-no-gem-on-i8xx.patch: Drop, intel 2D driver requires GEM now. This
+ should be entertaining.
+
+* Wed Jul 08 2009 Kyle McMartin <kyle@redhat.com>
+- First cut of /usr/sbin/perf wrapper script and 'perf'
+ subpackage.
+
+* Wed Jul 08 2009 Kyle McMartin <kyle@redhat.com> 2.6.31-0.54.rc2.git2
+- Rebase and re-apply all the Fedora-specific linux-2.6-debug-*
+ patches.
+- Cull a bunch of upstreamed patches from the spec.
+
+* Wed Jul 08 2009 Steve Dickson <steved@redhat.com>
+- Added NFSD v4 dynamic pseudo root patch which allows
+ NFS v3 exports to be mounted by v4 clients.
+
+* Tue Jul 07 2009 Jarod Wilson <jarod@redhat.com>
+- See if we can't make lirc_streamzap behave better... (#508952)
+
+* Tue Jul 07 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.31-0.47.rc2.git2
+- 2.6.31-rc2-git2
+
+* Tue Jul 07 2009 Jarod Wilson <jarod@redhat.com>
+- Make lirc_i2c actually work with 2.6.31 i2c
+
+* Mon Jul 06 2009 Chuck Ebbert <cebbert@redhat.com>
+- Use LZMA for kernel compression on X86.
+
+* Mon Jul 06 2009 Jarod Wilson <jarod@redhat.com>
+- Hack up lirc_i2c and lirc_zilog to compile with 2.6.31 i2c
+ changes. The drivers might not actually be functional now, but
+ at least they compile again. Will fix later, if need be...
+
+* Sat Jul 04 2009 Dave Jones <davej@redhat.com> 2.6.31-0.42.rc2
+- 2.6.31-rc2
+
+* Sat Jul 04 2009 Chuck Ebbert <cebbert@redhat.com>
+- 2.6.31-rc1-git11
+
+* Fri Jul 03 2009 Hans de Goede <hdegoede@redhat.com>
+- Disable v4l1 ov511 and quickcam_messenger drivers (obsoleted by
+ v4l2 gspca subdrivers)
+
+* Thu Jul 02 2009 Kyle McMartin <kyle@redhat.com> 2.6.31-0.39.rc1.git9
+- 2.6.31-rc1-git9
+- linux-2.6-dm-fix-exstore-search.patch: similar patch merged upstream.
+
+* Tue Jun 30 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.31-0.38.rc1.git7
+- 2.6.31-rc1-git7
+
+* Tue Jun 30 2009 Dave Jones <davej@redhat.com> 2.6.31-0.37.rc1.git5
+- Disable kmemleak. Way too noisy, and not finding any real bugs.
+
+* Tue Jun 30 2009 Ben Skeggs <bskeggs@redhat.com>
+- drm-nouveau.patch: match upstream
+
+* Mon Jun 29 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.31-0.35.rc1.git5
+- 2.6.31-rc1-git5
+- CONFIG_LEDS_LP3944=m
+
+* Mon Jun 29 2009 Chuck Ebbert <cebbert@redhat.com>
+- Try to fix the dm overlay bug for real (#505121)
+
+* Sat Jun 27 2009 Ben Skeggs <bskeggs@redhat.com> 2.6.31-0.33.rc1.git2
+- drm-nouveau.patch: fix conflicts from 2.6.31-rc1-git2
+
+* Fri Jun 26 2009 Dave Jones <davej@redhat.com> 2.6.31-0.31.rc1.git2
+- Further improvements to kmemleak
+
+* Fri Jun 26 2009 Dave Jones <davej@redhat.com> 2.6.31-0.30.rc1.git2
+- 2.6.31-rc1-git2
+
+* Fri Jun 26 2009 Ben Skeggs <bskeggs@redhat.com>
+- drm-nouveau.patch: latest upstream + reenable
+
+* Thu Jun 25 2009 Dave Jones <davej@redhat.com> 2.6.31-0.29.rc1
+- Make kmemleak scan process stacks by default.
+ Should reduce false positives (which does also increase false negatives,
+ but that's at least less noisy)
+
+* Wed Jun 24 2009 Kyle McMartin <kyle@redhat.com> 2.6.31-0.28.rc1
+- 2.6.31-rc1
+- linux-2.6-utrace.patch: rebase on kernel/Makefile changes
+- config changes:
+ - generic:
+ - CONFIG_DM_LOG_USERSPACE=m
+ - CONFIG_DM_MULTIPATH_QL=m
+ - CONFIG_DM_MULTIPATH_ST=m
+ - CONFIG_BATTERY_MAX17040=m
+ - CONFIG_I2C_DESIGNWARE is off (depends on clk.h)
+
+* Wed Jun 24 2009 Kyle McMartin <kyle@redhat.com>
+- Move perf to /usr/libexec/perf-$KernelVer.
+
+* Wed Jun 24 2009 Kyle McMartin <kyle@redhat.com>
+- config changes:
+ - generic:
+ - CONFIG_SCSI_DEBUG=m (was off, requested by davidz)
+
+* Wed Jun 24 2009 Dave Jones <davej@redhat.com> 2.6.31-0.22.rc0.git22
+- 2.6.30-git22
+
+* Tue Jun 23 2009 Dave Jones <davej@redhat.com> 2.6.31-0.22.rc0.git20
+- 2.6.30-git20
+
+* Mon Jun 22 2009 Kyle McMartin <kyle@redhat.com> 2.6.31-0.24.rc0.git18
+- Enable tools/perf, installed as /bin/perf-$KernelVer. Docs and a /bin/perf
+ wrapper come next if this builds ok.
+
+* Mon Jun 22 2009 Kyle McMartin <kyle@redhat.com>
+- sched-introduce-SCHED_RESET_ON_FORK-scheduling-policy-flag.patch: pull in
+ two fixes from Mike Galbraith from tip.git
+
+* Sun Jun 21 2009 Dave Jones <davej@redhat.com> 2.6.31-0.21.rc0.git18
+- Add patch to possibly fix the pktlen problem on via-velocity.
+
+* Sun Jun 21 2009 Dave Jones <davej@redhat.com> 2.6.31-0.20.rc0.git18
+- 2.6.30-git18
+ VIA crypto & mmc patches now upstream.
+
+* Sun Jun 21 2009 Dave Jones <davej@redhat.com>
+- Determine cacheline sizes in a generic manner.
+
+* Sun Jun 21 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.31-0.18.rc0.git17
+- 2.6.30-git17
+- Config changes:
+ - powerpc32-generic
+ CONFIG_PERF_COUNTERS=y
+ - generic
+ CONFIG_KEYBOARD_LM8323 is not set
+ CONFIG_MOUSE_SYNAPTICS_I2C=m
+ CONFIG_TOUCHSCREEN_EETI=m
+ CONFIG_TOUCHSCREEN_W90X900=m
+- Dropped agp-set_memory_ucwb.patch, all fixed upstream now.
+
+* Sat Jun 20 2009 Kyle McMartin <kyle@redhat.com> 2.6.31.0.17.rc0.git15
+- config changes:
+ - ppc generic:
+ - CONFIG_PPC_DISABLE_WERROR=y (switched... chrp fails otherwise, stack
+ frame size.)
+
+* Sat Jun 20 2009 Kyle McMartin <kyle@redhat.com> 2.6.31.0.16.rc0.git15
+- 2.6.30-git15
+- config changes:
+ - generic:
+ - CONFIG_LBDAF=y
+ - staging:
+ - CONFIG_USB_SERIAL_QUATECH2 is not set
+ - CONFIG_VT6655 is not set
+ - CONFIG_USB_CPC is not set
+ - CONFIG_RDC_17F3101X is not set
+ - CONFIG_FB_UDL is not set
+ - ppc32:
+ - CONFIG_KMETER1=y
+ - ppc generic:
+ - CONFIG_PPC_DISABLE_WERROR is not set
+- lirc disabled due to i2c detach_client removal.
+
+* Sat Jun 20 2009 Kyle McMartin <kyle@redhat.com>
+- sched-introduce-SCHED_RESET_ON_FORK-scheduling-policy-flag.patch: add,
+ queued in tip/sched/core (ca94c442535a44d508c99a77e54f21a59f4fc462)
+
+* Fri Jun 19 2009 Kyle McMartin <kyle@redhat.com> 2.6.31.0.15.rc0.git14
+- Fix up ptrace, hopefully. Builds on x86_64 at least.
+
+* Fri Jun 19 2009 Chuck Ebbert <cebbert@redhat.com>
+- linux-2.6-tip.git-203abd67b75f7714ce98ab0cdbd6cfd7ad79dec4.patch
+ Fixes oops on boot with qemu (#507007)
+
+* Fri Jun 19 2009 Kyle McMartin <kyle@redhat.com> 2.6.31-0.13.rc0.git14
+- 2.6.30-git14
+
+* Fri Jun 19 2009 Chuck Ebbert <cebbert@redhat.com>
+- Fix up the via-sdmmc and via-hwmon-temp-sensor patches.
+- Drop VIA Padlock patches merged upstream:
+ via-rng-enable-64bit.patch
+ via-padlock-10-enable-64bit.patch
+ via-padlock-20-add-x86-dependency.patch
+
+* Thu Jun 18 2009 Kyle McMartin <kyle@redhat.com> 2.6.31-0.11.rc0.git13
+- 2.6.30-git13
+- config changes:
+ - arm:
+ - CONFIG_UACCESS_WITH_MEMCPY is not set
+ - i686-PAE:
+ - CONFIG_XEN_DEV_EVTCHN=m
+ - CONFIG_XEN_SYS_HYPERVISOR=y
+ - ia64:
+ - CONFIG_RCU_FANOUT=64
+ - nodebug:
+ - CONFIG_DEBUG_KMEMLEAK is not set
+ - CONFIG_DEBUG_KMEMLEAK_TEST=m
+ - powerpc:
+ - CONFIG_CAN_SJA1000_OF_PLATFORM=m
+ - CONFIG_PPC_EMULATED_STATS=y
+ - CONFIG_SWIOTLB=y
+ - CONFIG_RDS is not set (broken on ppc32)
+ - powerpc32:
+ - CONFIG_RCU_FANOUT=32
+ - powerpc64:
+ - CONFIG_RCU_FANOUT=64
+ - CONFIG_PERF_COUNTERS=y
+ - s390x:
+ - CONFIG_RCU_FANOUT=64
+ - CONFIG_SECCOMP=y
+ - CONFIG_PM=y
+ - CONFIG_HIBERNATION=y
+ - CONFIG_PM_STD_PARTITION="/dev/jokes"
+ - sparc64:
+ - CONFIG_RCU_FANOUT=64
+ - x86:
+ - CONFIG_RCU_FANOUT=32
+ - CONFIG_IOMMU_STRESS is not set
+ - CONFIG_PERF_COUNTERS=y
+ - CONFIG_X86_OLD_MCE is not set
+ - CONFIG_X86_MCE_INTEL=y
+ - CONFIG_X86_MCE_AMD=y
+ - CONFIG_X86_ANCIENT_MCE is not set
+ - CONFIG_X86_MCE_INJECT is not set
+ - x86_64:
+ - CONFIG_EDAC_AMD64=m
+ - CONFIG_EDAC_AMD64_ERROR_INJECTION is not set
+ - CONFIG_XEN_DEV_EVTCHN=m
+ - CONFIG_XEN_SYS_HYPERVISOR=y
+ - CONFIG_RCU_FANOUT=64
+ - CONFIG_IOMMU_STRESS is not set
+ - CONFIG_PERF_COUNTERS=y
+ - CONFIG_X86_MCE_INJECT is not set
+ - generic:
+ - CONFIG_RCU_FANOUT=32
+ - CONFIG_MMC_SDHCI_PLTFM=m
+ - CONFIG_MMC_CB710=m
+ - CONFIG_CB710_CORE=m
+ - CONFIG_CB710_DEBUG is not set
+ - CONFIG_SCSI_MVSAS_DEBUG is not set
+ - CONFIG_SCSI_BNX2_ISCSI=m
+ - CONFIG_NETFILTER_XT_MATCH_OSF=m
+ - CONFIG_RFKILL_INPUT=y (used to be =m, which was invalid)
+ - CONFIG_DE2104X_DSL=0
+ - CONFIG_KS8842 is not set
+ - CONFIG_CFG80211_DEBUGFS=y
+ - CONFIG_MAC80211_DEFAULT_PS=y
+ - CONFIG_IWM=m
+ - CONFIG_IWM_DEBUG is not set
+ - CONFIG_RT2800USB=m
+ - CONFIG_CAN_DEV=m
+ - CONFIG_CAN_CALC_BITTIMING=y
+ - CONFIG_CAN_SJA1000=m
+ - CONFIG_CAN_SJA1000_PLATFORM=m
+ - CONFIG_CAN_EMS_PCI=m
+ - CONFIG_CAN_KVASER_PCI=m
+ - CONFIG_EEPROM_MAX6875=m
+ - CONFIG_SENSORS_TMP401=m
+ - CONFIG_MEDIA_SUPPORT=m
+ - CONFIG_SND_CTXFI=m
+ - CONFIG_SND_LX6464ES=m
+ - CONFIG_SND_HDA_CODEC_CA0110=y
+ - CONFIG_USB_XHCI_HCD=m
+ - CONFIG_USB_XHCI_HCD_DEBUGGING is not set
+ - CONFIG_DRAGONRISE_FF=y (used to be =m)
+ - CONFIG_GREENASIA_FF=y (used to be =m)
+ - CONFIG_SMARTJOYPLUS_FF=y (used to be =m)
+ - CONFIG_USB_NET_INT51X1=m
+ - CONFIG_CUSE=m
+ - CONFIG_FUNCTION_PROFILER=y
+ - CONFIG_RING_BUFFER_BENCHMARK=m
+ - CONFIG_REGULATOR_USERSPACE_CONSUMER=m
+ - CONFIG_REGULATOR_MAX1586=m
+ - CONFIG_REGULATOR_LP3971=m
+ - CONFIG_RCU_FANOUT_EXACT is not set
+ - CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
+ - CONFIG_FSNOTIFY=y
+ - CONFIG_IEEE802154=m
+ - CONFIG_IEEE802154_DRIVERS=m
+ - CONFIG_IEEE802154_FAKEHARD=m
+ - CONFIG_CNIC=m
+
+* Wed Jun 17 2009 Jarod Wilson <jarod@redhat.com>
+- New lirc_imon hotness, update 2:
+ * support dual-interface devices with a single lirc device
+ * directional pad functions as an input device mouse
+ * touchscreen devices finally properly supported
+ * support for using MCE/RC-6 protocol remotes
+ * fix oops in RF remote association code (F10 bug #475496)
+ * fix re-enabling case/panel buttons and/or knobs
+- Add some misc additional lirc_mceusb2 transceiver IDs
+- Add missing unregister_chrdev_region() call to lirc_dev exit
+- Add it8720 support to lirc_it87
+
+* Tue Jun 16 2009 Chuck Ebbert <cebbert@redhat.com>
+- Update via-sdmmc driver
+
+* Mon Jun 15 2009 Jarod Wilson <jarod@redhat.com>
+- Update lirc patches w/new imon hotness
+
+* Fri Jun 12 2009 Chuck Ebbert <cebbert@redhat.com>
+- Update VIA temp sensor and mmc drivers.
+
+* Fri Jun 12 2009 John W. Linville <linville@redhat.com> 2.6.30-6
+- neigh: fix state transition INCOMPLETE->FAILED via Netlink request
+- enable CONFIG_ARPD (used by OpenNHRP)
+
+* Wed Jun 10 2009 Chuck Ebbert <cebbert@redhat.com>
+- VIA Nano updates:
+ Enable Padlock AES encryption and random number generator on x86-64
+ Add via-sdmmc and via-cputemp drivers
+
+* Wed Jun 10 2009 Kyle McMartin <kyle@redhat.com> 2.6.30-1
+- Linux 2.6.30 rebase.
+
+* Tue Jun 09 2009 John W. Linville <linville@tuxdriver.com>
+- Clean-up some wireless bits in config-generic
+
+* Tue Jun 09 2009 Chuck Ebbert <cebbert@redhat.com>
+- Add support for ACPI P-states on VIA processors.
+- Disable the e_powersaver driver.
+
+* Tue Jun 09 2009 Chuck Ebbert <cebbert@redhat.com>
+- Linux 2.6.30-rc8-git6
+
+* Fri Jun 05 2009 Chuck Ebbert <cebbert@redhat.com>
+- Linux 2.6.30-rc8-git1
+
+* Wed Jun 03 2009 Kyle McMartin <kyle@redhat.com>
+- Linux 2.6.30-rc8
+
+* Tue Jun 2 2009 Roland McGrath <roland@redhat.com>
+- utrace update (fixes stap PR10185)
+
+* Tue Jun 02 2009 Dave Jones <davej@redhat.com>
+- For reasons unknown, RT2X00 driver was being built-in.
+ Make it modular.
+
+* Tue Jun 02 2009 Dave Jones <davej@redhat.com>
+- 2.6.30-rc7-git5
+
+* Sat May 30 2009 Dave Jones <davej@redhat.com>
+- 2.6.30-rc7-git4
+
+* Thu May 28 2009 Dave Jones <davej@redhat.com
+- 2.6.30-rc7-git3
+
+* Wed May 27 2009 Dave Jones <davej@redhat.com>
+- 2.6.30-rc7-git2
+
+* Tue May 26 2009 Dave Jones <davej@redhat.com>
+- Various cpufreq patches from git.
+
+* Tue May 26 2009 Dave Jones <davej@redhat.com
+- 2.6.30-rc7-git1
+
+* Tue May 26 2009 Dave Jones <davej@redhat.com>
+- 2.6.30-rc7-git1
+
+* Mon May 25 2009 Kyle McMartin <kyle@redhat.com>
+- rds-only-on-64-bit-or-x86.patch: drop patch, issue is fixed upstream.
+
+* Sat May 23 2009 Dave Jones <davej@redhat.com>
+- 2.6.30-rc7
+
+* Thu May 21 2009 Dave Jones <davej@redhat.com>
+- 2.6.30-rc6-git6
+
+* Wed May 20 2009 Chuck Ebbert <cebbert@redhat.com>
+- Enable Divas (formerly Eicon) ISDN drivers on x86_64. (#480837)
+
+* Wed May 20 2009 Dave Jones <davej@redhat.com>
+- 2.6.30-rc6-git5
+
+* Mon May 18 2009 Dave Jones <davej@redhat.com>
+- 2.6.30-rc6-git3
+
+* Sun May 17 2009 Dave Jones <davej@redhat.com>
+- 2.6.30-rc6-git2
+
+* Sat May 16 2009 Dave Jones <davej@redhat.com>
+- 2.6.30-rc6
+
+* Mon May 11 2009 Kyle McMartin <kyle@redhat.com>
+- Linux 2.6.30-rc5-git1
+
+* Fri May 08 2009 Kyle McMartin <kyle@redhat.com>
+- Linux 2.6.30-rc5
+
+* Fri May 08 2009 Kyle McMartin <kyle@redhat.com>
+- Linux 2.6.30-rc4-git4
+
+* Wed May 06 2009 Kyle McMartin <kyle@redhat.com>
+- Linux 2.6.30-rc4-git3
+- linux-2.6-cdrom-door-status.patch: merged upstream.
+- linux-2.6-iwl3945-remove-useless-exports.patch: merged upstream.
+- linux-2.6-utrace.patch: rebase against changes to fs/proc/array.c
+- USB_NET_CDC_EEM=m
+
+* Fri May 01 2009 Eric Sandeen <sandeen@redhat.com>
+- Fix ext4 corruption on partial write into prealloc block
+
+* Thu Apr 30 2009 Kyle McMartin <kyle@redhat.com>
+- 2.6.30-rc4
+
+* Wed Apr 29 2009 Dave Jones <davej@redhat.com>
+- 2.6.30-rc3-git6
+
+* Tue Apr 28 2009 Dave Jones <davej@redhat.com>
+- 2.6.30-rc3-git4
+
+* Tue Apr 28 2009 Chuck Ebbert <cebbert@redhat.com>
+- Make the kernel-vanilla package buildable again.
+- Allow building with older versions of RPM.
+
+* Tue Apr 28 2009 Neil Horman <nhorman@redhat.com>
+- Backport missing snmp stats (bz 492391)
+
+* Tue Apr 28 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.30-0.72.rc3.git3
+- Drop unused exports from the iwl3945 driver.
+
+* Tue Apr 28 2009 Chuck Ebbert <cebbert@redhat.com>
+- Linux 2.6.30-rc3-git3
+
+* Mon Apr 27 2009 Dave Jones <davej@redhat.com>
+- 2.6.30-rc3-git2
+
+* Sun Apr 26 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.30-0.68.rc3.git1
+- Linux 2.6.30-rc3-git1
+
+* Wed Apr 22 2009 Dave Jones <davej@redhat.com> 2.6.30-0.67.rc3
+- Disable SYSFS_DEPRECATED on ia64
+
+* Wed Apr 22 2009 Kyle McMartin <kyle@redhat.com>
+- Linux 2.6.30-rc3
+- PROC_VMCORE=y: Exports the dump image of crashed
+ kernel in ELF format
+
+* Wed Apr 22 2009 Neil Horman <nhorman@redhat.com>
+- Enable RELOCATABLE and CRASH_DUMP for powerpc64
+- With this we can remove the -kdump build variant
+- for the ppc64 arch
+
+* Tue Apr 21 2009 Chuck Ebbert <cebbert@redhat.com>
+- Don't include the modules.*.bin files in the RPM package.
+
+* Tue Apr 21 2009 Dave Jones <davej@redhat.com>
+- 2.6.30-rc2-git7
+
+* Mon Apr 20 2009 Dave Jones <davej@redhat.com>
+- Various s390x config tweaks. (#496596, #496601, #496605, #496607)
+
+* Mon Apr 20 2009 Dave Jones <davej@redhat.com>
+- 2.6.30-rc2-git6
+
+* Sat Apr 18 2009 Chuck Ebbert <cebbert@redhat.com>
+- Set CONFIG_UEVENT_HELPER_PATH to the empty string (#496296)
+
+* Fri Apr 17 2009 Dave Jones <davej@redhat.com>
+- 2.6.30-rc2-git3
+
+* Thu Apr 16 2009 Kyle McMartin <kyle@redhat.com> 2.6.30-0.58.rc2.git1
+- 2.6.30-rc2-git1
+
+* Wed Apr 15 2009 Kyle McMartin <kyle@redhat.com> 2.6.30-0.57.rc2
+- 2.6.30-rc2
+
+* Tue Apr 14 2009 Kyle McMartin <kyle@redhat.com>
+- 2.6.30-rc1-git7
+- CONFIG_TOUCHSCREEN_AD7879_I2C=m
+- CONFIG_STRIP_ASM_SYMS=y, off for -debug
+
+* Mon Apr 13 2009 Kyle McMartin <kyle@redhat.com>
+- ppc-fix-parport_pc.patch: add from linuxppc-dev@
+
+* Mon Apr 13 2009 Kyle McMartin <kyle@redhat.com>
+- execshield: fix build (load_user_cs_desc is 32-bit only in tlb.c)
+
+* Sun Apr 12 2009 Kyle McMartin <kyle@redhat.com>
+- 2.6.30-rc1-git5
+- revert-fix-modules_install-via-nfs.patch: reverted upstream
+
+* Thu Apr 09 2009 Kyle McMartin <kyle@redhat.com>
+- actually drop utrace-ftrace from srpm.
+
+* Thu Apr 09 2009 Kyle McMartin <kyle@redhat.com>
+- 2.6.30-rc1-git2
+- CONFIG_IGBVF=m
+- CONFIG_NETFILTER_XT_TARGET_LED=m
+
+* Thu Apr 09 2009 Dave Jones <davej@redhat.com>
+- Bring back the /dev/crash driver. (#492803)
+
+* Wed Apr 08 2009 Dave Jones <davej@redhat.com>
+- disable MMIOTRACE in non-debug builds (#494584)
+
+* Wed Apr 08 2009 Kyle McMartin <kyle@redhat.com> 2.6.30-0.44.rc1
+- 2.6.30-rc1
+- linux-2.6-hwmon-atk0110.patch: drop
+- CONFIG_DETECT_HUNG_TASK=y
+- # CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
+
+* Tue Apr 7 2009 Roland McGrath <roland@redhat.com>
+- utrace update, drop unfinished utrace-ftrace
+
+* Tue Apr 07 2009 Kyle McMartin <kyle@redhat.com>
+- Linux 2.6.29-git15
+- EXT3_DEFAULTS_TO_ORDERED on for now.
+- X86_X2APIC enabled.
+- LEDS_LP5521, LEDS_BD2802 off... look not generally relevant.
+- LIBFCOE on.
+
+* Tue Apr 07 2009 Dave Jones <davej@redhat.com>
+- Enable CONFIG_CIFS_STATS (#494545)
+
+* Mon Apr 06 2009 Kyle McMartin <kyle@redhat.com>
+- linux-2.6-execshield.patch: rebase for 2.6.30
+
+* Mon Apr 06 2009 Kyle McMartin <kyle@redhat.com>
+- Linux 2.6.29-git13
+- drop patches merged upstream:
+ - fix-ppc-debug_kmap_atomic.patch
+ - fix-staging-at76.patch
+ - linux-2.6-acpi-video-didl-intel-outputs.patch
+ - linux-2.6-acpi-strict-resources.patch
+ - linux-2.6-sony-laptop-rfkill.patch
+ - linux-2.6-btrfs-fix-umount-hang.patch
+ - linux-2.6-fiemap-header-install.patch
+ - linux-2.6-debug-dma-api.patch
+ - dma-api-debug-fixes.patch
+ - linux-2.6-ext4-flush-on-close.patch
+ - linux-2.6-relatime-by-default.patch
+ - linux-2.6-pci-sysfs-remove-id.patch
+ - linux-2.6-scsi-cpqarray-set-master.patch
+ - alsa-rewrite-hw_ptr-updaters.patch
+ - alsa-pcm-always-reset-invalid-position.patch
+ - alsa-pcm-fix-delta-calc-at-overlap.patch
+ - alsa-pcm-safer-boundary-checks.patch
+ - linux-2.6-input-hid-extra-gamepad.patch
+ - linux-2.6-ipw2x00-age-scan-results-on-resume.patch
+ - linux-2.6-dropwatch-protocol.patch
+ - linux-2.6-net-fix-gro-bug.patch
+ - linux-2.6-net-fix-another-gro-bug.patch
+ - linux-2.6-net-xfrm-fix-spin-unlock.patch
+ - linux-2.6.29-pat-change-is_linear_pfn_mapping-to-not-use-vm_pgoff.patch
+ - linux-2.6.29-pat-pci-change-prot-for-inherit.patch
+
+* Thu Apr 02 2009 Josef Bacik <josef@toxicpanda.com>
+- linux-2.6-btrfs-fix-umount-hang.patch: fix umount hang on btrfs
+
+* Thu Apr 02 2009 Kyle McMartin <kyle@redhat.com>
+- fix-ppc-debug_kmap_atomic.patch: fix build failures on ppc.
+
+* Thu Apr 02 2009 Kyle McMartin <kyle@redhat.com>
+- Linux 2.6.29-git9
+
+* Tue Mar 31 2009 Kyle McMartin <kyle@redhat.com>
+- rds-only-on-64-bit-or-x86.patch: add
+- at76-netdev_ops.patch: add
+
+* Tue Mar 31 2009 Kyle McMartin <kyle@redhat.com>
+- Linux 2.6.29-git8
+- linux-2.6-net-fix-another-gro-bug.patch: upstream.
+
+* Tue Mar 31 2009 Eric Sandeen <sandeen@redhat.com>
+- add fiemap.h to kernel-headers
+- build ext4 (and jbd2 and crc16) into the kernel
+
+* Tue Mar 31 2009 Kyle McMartin <kyle@redhat.com>
+- Linux 2.6.29-git7
+- fix-staging-at76.patch: pull patch from linux-wireless to fix...
+
+* Mon Mar 30 2009 Kyle McMartin <kyle@redhat.com> 2.6.30-0.28.rc0.git6
+- Linux 2.6.29-git6
+- Bunch of stuff disabled, most merged, some needs rebasing.
+
+* Mon Mar 30 2009 Chuck Ebbert <cebbert@redhat.com>
+- Make the .shared-srctree file a list so more than two checkouts
+ can share source files.
+
+* Mon Mar 30 2009 Chuck Ebbert <cebbert@redhat.com>
+- Separate PAT fixes that are headed for -stable from our out-of-tree ones.
+
+* Mon Mar 30 2009 Dave Jones <davej@redhat.com>
+- Make io schedulers selectable at boot time again. (#492817)
+
+* Mon Mar 30 2009 Dave Jones <davej@redhat.com>
+- Add a strict-devmem=0 boot argument (#492803)
+
+* Mon Mar 30 2009 Adam Jackson <ajax@redhat.com>
+- linux-2.6.29-pat-fixes.patch: Fix PAT/GTT interaction
+
+* Mon Mar 30 2009 Mauro Carvalho Chehab <mchehab@redhat.com>
+- some fixes of troubles caused by v4l2 subdev conversion
+
+* Mon Mar 30 2009 Mark McLoughlin <markmc@redhat.com> 2.6.29-21
+- Fix guest->remote network stall with virtio/GSO (#490266)
+
+* Mon Mar 30 2009 Ben Skeggs <bskeggs@redhat.com>
+- drm-nouveau.patch
+ - rewrite nouveau PCI(E) GART functions, should fix rh#492492
+ - kms: kernel option to allow dual-link dvi
+ - modinfo descriptions for module parameters
+
+* Sun Mar 29 2009 Mauro Carvalho Chehab <mchehab@redhat.com>
+- more v4l/dvb updates: v4l subdev conversion and some driver improvements
+
+* Sun Mar 29 2009 Chuck Ebbert <cebbert@redhat.com>
+- More fixes for ALSA hardware pointer updating.
+
+* Sat Mar 28 2009 Mauro Carvalho Chehab <mchehab@redhat.com>
+- linux-2.6-revert-dvb-net-kabi-change.patch: attempt to fix dvb net breakage
+- update v4l fixes patch to reflect what's ready for 2.6.30
+- update v4l devel patch to reflect what will be kept on linux-next for a while
+
+* Fri Mar 27 2009 Chuck Ebbert <cebbert@redhat.com> 2.6.29-16
+- Fix 2.6.29 networking lockups.
+- Fix locking in net/xfrm/xfrm_state.c (#489764)
+
+* Fri Mar 27 2009 Ben Skeggs <bskeggs@redhat.com>
+- drm-nouveau.patch: do nothing for dac_{prepare,commit}, it's useless
+ and breaks some things in strange ways.
+
+* Fri Mar 27 2009 Ben Skeggs <bskeggs@redhat.com>
+- nv50: clear 0x1900/8 on init, possible fix for rh#492240
+- forcibly disable GEM also if KMS requested where not supported
+- inform the user if we disable KMS because of it not being supported
+
+* Thu Mar 26 2009 Matthew Garrett <mjg@redhat.com>
+- linux-2.6-relatime-by-default.patch: Backport relatime code from 2.6.30
+
+* Thu Mar 26 2009 Dave Jones <davej@redhat.com>
+- Check for modesetting enabled before forcing mode on 915. (#490336)
+
+* Thu Mar 26 2009 Dave Jones <davej@redhat.com>
+- Set kernel-PAE as default in grub. (#487578)
+
+* Thu Mar 26 2009 Dave Jones <davej@redhat.com>
+- Enable CONFIG_MOUSE_PS2_ELANTECH (#492163)
+
+* Thu Mar 26 2009 Kyle McMartin <kyle@redhat.com>
+- linux-2.6-v4l-pvrusb2-fixes.patch: fix build for uncle steve.
+
+* Thu Mar 26 2009 Mauro Carvalho Chehab <mchehab@redhat.com>
+- Move all 2.6.30 stuff into linux-2.6-v4l-dvb-fixes.patch, in
+ preparation for upstream pull;
+- Added two new drivers: gspca sq905c and DVB Intel ce6230
+- Updated to the latest v4l-dvb drivers.
+
+* Wed Mar 25 2009 Mauro Carvalho Chehab <mchehab@redhat.com>
+- remove duplicated Cinergy T2 entry at config-generic
+
+* Wed Mar 25 2009 Neil Horman <nhorman@redhat.com>
+- Add dropmonitor/dropwatch protocol from 2.6.30
+
+* Wed Mar 25 2009 Kyle McMartin <kyle@redhat.com>
+- alsa-rewrite-hw_ptr-updaters.patch: snd_pcm_update_hw_ptr() tries to
+ detect the unexpected hwptr jumps more strictly to avoid the position
+ mess-up, which often results in the bad quality I/O with pulseaudio.
+
+* Wed Mar 25 2009 Ben Skeggs <bskeggs@redhat.com>
+- drm-nouveau.patch: idle channels better before destroying them
+
+* Tue Mar 24 2009 Kyle McMartin <kyle@redhat.com>
+- Disable DMAR by default until suspend & resume is fixed.
+
+* Tue Mar 24 2009 Josef Bacik <josef@toxicpanda.com>
+- fsync replay fixes for btrfs
+
+* Mon Mar 23 2009 Dave Jones <davej@redhat.com>
+- 2.6.29
+
+###
+# The following Emacs magic makes C-c C-e use UTC dates.
+# Local Variables:
+# rpm-change-log-uses-utc: t
+# End:
+###
diff --git a/kvm-mmu-fix-conflict-access-permissions-in-direct-sp.patch b/kvm-mmu-fix-conflict-access-permissions-in-direct-sp.patch
new file mode 100644
index 0000000..eefdda5
--- /dev/null
+++ b/kvm-mmu-fix-conflict-access-permissions-in-direct-sp.patch
@@ -0,0 +1,49 @@
+From: Xiao Guangrong <xiaoguangrong@cn.fujitsu.com>
+Date: Wed, 30 Jun 2010 08:02:45 +0000 (+0800)
+Subject: KVM: MMU: fix conflict access permissions in direct sp
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=6aa0b9dec5d6dde26ea17b0b5be8fccfe19df3c9
+
+KVM: MMU: fix conflict access permissions in direct sp
+
+In no-direct mapping, we mark sp is 'direct' when we mapping the
+guest's larger page, but its access is encoded form upper page-struct
+entire not include the last mapping, it will cause access conflict.
+
+For example, have this mapping:
+ [W]
+ / PDE1 -> |---|
+ P[W] | | LPA
+ \ PDE2 -> |---|
+ [R]
+
+P have two children, PDE1 and PDE2, both PDE1 and PDE2 mapping the
+same lage page(LPA). The P's access is WR, PDE1's access is WR,
+PDE2's access is RO(just consider read-write permissions here)
+
+When guest access PDE1, we will create a direct sp for LPA, the sp's
+access is from P, is W, then we will mark the ptes is W in this sp.
+
+Then, guest access PDE2, we will find LPA's shadow page, is the same as
+PDE's, and mark the ptes is RO.
+
+So, if guest access PDE1, the incorrect #PF is occured.
+
+Fixed by encode the last mapping access into direct shadow page
+
+Signed-off-by: Xiao Guangrong <xiaoguangrong@cn.fujitsu.com>
+Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
+Signed-off-by: Avi Kivity <avi@redhat.com>
+---
+
+diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h
+index 89d66ca..2331bdc 100644
+--- a/arch/x86/kvm/paging_tmpl.h
++++ b/arch/x86/kvm/paging_tmpl.h
+@@ -342,6 +342,7 @@ static u64 *FNAME(fetch)(struct kvm_vcpu *vcpu, gva_t addr,
+ /* advance table_gfn when emulating 1gb pages with 4k */
+ if (delta == 0)
+ table_gfn += PT_INDEX(addr, level);
++ access &= gw->pte_access;
+ } else {
+ direct = 0;
+ table_gfn = gw->table_gfn[level - 2];
diff --git a/l2tp-fix-oops-in-pppol2tp_xmit.patch b/l2tp-fix-oops-in-pppol2tp_xmit.patch
new file mode 100644
index 0000000..bf07648
--- /dev/null
+++ b/l2tp-fix-oops-in-pppol2tp_xmit.patch
@@ -0,0 +1,78 @@
+From: James Chapman <jchapman@katalix.com>
+Date: Tue, 16 Mar 2010 06:46:31 +0000 (+0000)
+Subject: l2tp: Fix oops in pppol2tp_xmit
+X-Git-Tag: v2.6.34-rc2~28^2~10
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=3feec909
+
+l2tp: Fix oops in pppol2tp_xmit
+
+When transmitting L2TP frames, we derive the outgoing interface's UDP
+checksum hardware assist capabilities from the tunnel dst dev. This
+can sometimes be NULL, especially when routing protocols are used and
+routing changes occur. This patch just checks for NULL dst or dev
+pointers when checking for netdev hardware assist features.
+
+BUG: unable to handle kernel NULL pointer dereference at 0000000c
+IP: [<f89d074c>] pppol2tp_xmit+0x341/0x4da [pppol2tp]
+*pde = 00000000
+Oops: 0000 [#1] SMP
+last sysfs file: /sys/class/net/lo/operstate
+Modules linked in: pppol2tp pppox ppp_generic slhc ipv6 dummy loop snd_hda_codec_atihdmi snd_hda_intel snd_hda_codec snd_pcm snd_timer snd soundcore snd_page_alloc evdev psmouse serio_raw processor button i2c_piix4 i2c_core ati_agp agpgart pcspkr ext3 jbd mbcache sd_mod ide_pci_generic atiixp ide_core ahci ata_generic floppy ehci_hcd ohci_hcd libata e1000e scsi_mod usbcore nls_base thermal fan thermal_sys [last unloaded: scsi_wait_scan]
+
+Pid: 0, comm: swapper Not tainted (2.6.32.8 #1)
+EIP: 0060:[<f89d074c>] EFLAGS: 00010297 CPU: 3
+EIP is at pppol2tp_xmit+0x341/0x4da [pppol2tp]
+EAX: 00000000 EBX: f64d1680 ECX: 000005b9 EDX: 00000000
+ESI: f6b91850 EDI: f64d16ac EBP: f6a0c4c0 ESP: f70a9cac
+ DS: 007b ES: 007b FS: 00d8 GS: 0000 SS: 0068
+Process swapper (pid: 0, ti=f70a8000 task=f70a31c0 task.ti=f70a8000)
+Stack:
+ 000005a9 000005b9 f734c400 f66652c0 f7352e00 f67dc800 00000000 f6b91800
+<0> 000005a3 f70ef6c4 f67dcda9 000005a3 f89b192e 00000246 000005a3 f64d1680
+<0> f63633e0 f6363320 f64d1680 f65a7320 f65a7364 f65856c0 f64d1680 f679f02f
+Call Trace:
+ [<f89b192e>] ? ppp_push+0x459/0x50e [ppp_generic]
+ [<f89b217f>] ? ppp_xmit_process+0x3b6/0x430 [ppp_generic]
+ [<f89b2306>] ? ppp_start_xmit+0x10d/0x120 [ppp_generic]
+ [<c11c15cb>] ? dev_hard_start_xmit+0x21f/0x2b2
+ [<c11d0947>] ? sch_direct_xmit+0x48/0x10e
+ [<c11c19a0>] ? dev_queue_xmit+0x263/0x3a6
+ [<c11e2a9f>] ? ip_finish_output+0x1f7/0x221
+ [<c11df682>] ? ip_forward_finish+0x2e/0x30
+ [<c11de645>] ? ip_rcv_finish+0x295/0x2a9
+ [<c11c0b19>] ? netif_receive_skb+0x3e9/0x404
+ [<f814b791>] ? e1000_clean_rx_irq+0x253/0x2fc [e1000e]
+ [<f814cb7a>] ? e1000_clean+0x63/0x1fc [e1000e]
+ [<c1047eff>] ? sched_clock_local+0x15/0x11b
+ [<c11c1095>] ? net_rx_action+0x96/0x195
+ [<c1035750>] ? __do_softirq+0xaa/0x151
+ [<c1035828>] ? do_softirq+0x31/0x3c
+ [<c10358fe>] ? irq_exit+0x26/0x58
+ [<c1004b21>] ? do_IRQ+0x78/0x89
+ [<c1003729>] ? common_interrupt+0x29/0x30
+ [<c101ac28>] ? native_safe_halt+0x2/0x3
+ [<c1008c54>] ? default_idle+0x55/0x75
+ [<c1009045>] ? c1e_idle+0xd2/0xd5
+ [<c100233c>] ? cpu_idle+0x46/0x62
+Code: 8d 45 08 f0 ff 45 08 89 6b 08 c7 43 68 7e fb 9c f8 8a 45 24 83 e0 0c 3c 04 75 09 80 63 64 f3 e9 b4 00 00 00 8b 43 18 8b 4c 24 04 <8b> 40 0c 8d 79 11 f6 40 44 0e 8a 43 64 75 51 6a 00 8b 4c 24 08
+EIP: [<f89d074c>] pppol2tp_xmit+0x341/0x4da [pppol2tp] SS:ESP 0068:f70a9cac
+CR2: 000000000000000c
+
+Signed-off-by: James Chapman <jchapman@katalix.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+diff --git a/drivers/net/pppol2tp.c b/drivers/net/pppol2tp.c
+index 9fbb2eb..5861ee9 100644
+--- a/drivers/net/pppol2tp.c
++++ b/drivers/net/pppol2tp.c
+@@ -1180,7 +1180,8 @@ static int pppol2tp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
+ /* Calculate UDP checksum if configured to do so */
+ if (sk_tun->sk_no_check == UDP_CSUM_NOXMIT)
+ skb->ip_summed = CHECKSUM_NONE;
+- else if (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM)) {
++ else if ((skb_dst(skb) && skb_dst(skb)->dev) &&
++ (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM))) {
+ skb->ip_summed = CHECKSUM_COMPLETE;
+ csum = skb_checksum(skb, 0, udp_len, 0);
+ uh->check = csum_tcpudp_magic(inet->inet_saddr,
diff --git a/linux-2.6-acpi-video-dos.patch b/linux-2.6-acpi-video-dos.patch
new file mode 100644
index 0000000..3e20851
--- /dev/null
+++ b/linux-2.6-acpi-video-dos.patch
@@ -0,0 +1,17 @@
+Disable firmware video brightness change on AC/Battery switch by default
+
+-- mjg59
+
+diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
+index bac2901..93b1a9e 100644
+--- a/drivers/acpi/video.c
++++ b/drivers/acpi/video.c
+@@ -1818,7 +1818,7 @@ static int acpi_video_bus_put_devices(struct acpi_video_bus *video)
+
+ static int acpi_video_bus_start_devices(struct acpi_video_bus *video)
+ {
+- return acpi_video_bus_DOS(video, 0, 0);
++ return acpi_video_bus_DOS(video, 0, 1);
+ }
+
+ static int acpi_video_bus_stop_devices(struct acpi_video_bus *video)
diff --git a/linux-2.6-ata-quirk.patch b/linux-2.6-ata-quirk.patch
new file mode 100644
index 0000000..32096d4
--- /dev/null
+++ b/linux-2.6-ata-quirk.patch
@@ -0,0 +1,58 @@
+--- linux-2.6.20/arch/ia64/kernel/quirks.c 1969-12-31 19:00:00.000000000 -0500
++++ linux-2.6.20_fix/arch/ia64/kernel/quirks.c 2007-02-13 13:56:34.000000000 -0500
+@@ -0,0 +1,45 @@
++/*
++ * This file contains work-arounds for ia64 platform bugs.
++ */
++#include <linux/pci.h>
++
++/*
++ * quirk_intel_ide_controller: If an ide/ata controller is
++ * at legacy mode, BIOS might initiates BAR(bar 0~3 and 5)
++ * with incorrect value. This quirk will reset the incorrect
++ * value to 0.
++ */
++static void __devinit quirk_intel_ide_controller(struct pci_dev *dev)
++{
++ unsigned int pos;
++ struct resource *res;
++ int fixed = 0;
++ u8 tmp8;
++
++ if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE)
++ return;
++
++ /* TODO: What if one channel is in native mode ... */
++ pci_read_config_byte(dev, PCI_CLASS_PROG, &tmp8);
++ if ((tmp8 & 5) == 5)
++ return;
++
++ for( pos = 0; pos < 6; pos ++ ) {
++ res = &dev->resource[pos];
++ if (!(res->flags & (IORESOURCE_IO | IORESOURCE_MEM)))
++ continue;
++
++ if (!res->start && res->end) {
++ res->start = res->end = 0;
++ res->flags = 0;
++ fixed = 1;
++ }
++ }
++ if (fixed)
++ printk(KERN_WARNING
++ "PCI device %s: BIOS resource configuration fixed.\n",
++ pci_name(dev));
++}
++
++DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_11, quirk_intel_ide_controller);
++
+--- linux-2.6.21.noarch/arch/ia64/kernel/Makefile~ 2007-05-27 23:23:36.000000000 -0400
++++ linux-2.6.21.noarch/arch/ia64/kernel/Makefile 2007-05-27 23:23:48.000000000 -0400
+@@ -33,6 +33,7 @@ obj-$(CONFIG_CRASH_DUMP) += crash_dump.o
+ obj-$(CONFIG_IA64_UNCACHED_ALLOCATOR) += uncached.o
+ obj-$(CONFIG_AUDIT) += audit.o
+ obj-$(CONFIG_PCI_MSI) += msi_ia64.o
++obj-$(CONFIG_PCI) += quirks.o
+ mca_recovery-y += mca_drv.o mca_drv_asm.o
+ obj-$(CONFIG_IA64_MC_ERR_INJECT)+= err_inject.o
+
diff --git a/linux-2.6-autoload-wmi.patch b/linux-2.6-autoload-wmi.patch
new file mode 100644
index 0000000..f093a40
--- /dev/null
+++ b/linux-2.6-autoload-wmi.patch
@@ -0,0 +1,244 @@
+From: Matthew Garrett <mjg@redhat.com>
+Date: Wed, 4 Nov 2009 19:17:53 +0000 (-0500)
+Subject: wmi: Add support for module autoloading
+X-Git-Tag: v2.6.33-rc1~47^2~5^2
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=1caab3c1a90be3aa4ec3599409d8fe044b077478
+
+wmi: Add support for module autoloading
+
+WMI provides interface-specific GUIDs that are exported from modules as
+modalises, but the core currently generates no events to trigger module
+loading. This patch adds support for registering devices for each WMI GUID
+and generating the appropriate uevent.
+
+Based heavily on a patch by Carlos Corbacho (<carlos@strangeworlds.co.uk>).
+
+Signed-off-by: Matthew Garrett <mjg@redhat.com>
+Tested-by: Carlos Corbacho <carlos@strangeworlds.co.uk>
+Acked-by: Carlos Corbacho <carlos@strangeworlds.co.uk>
+Signed-off-by: Len Brown <len.brown@intel.com>
+---
+
+diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
+index 177f8d7..e425a86 100644
+--- a/drivers/platform/x86/wmi.c
++++ b/drivers/platform/x86/wmi.c
+@@ -30,6 +30,7 @@
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/types.h>
++#include <linux/device.h>
+ #include <linux/list.h>
+ #include <linux/acpi.h>
+ #include <acpi/acpi_bus.h>
+@@ -65,6 +66,7 @@ struct wmi_block {
+ acpi_handle handle;
+ wmi_notify_handler handler;
+ void *handler_data;
++ struct device *dev;
+ };
+
+ static struct wmi_block wmi_blocks;
+@@ -195,6 +197,34 @@ static bool wmi_parse_guid(const u8 *src, u8 *dest)
+ return true;
+ }
+
++/*
++ * Convert a raw GUID to the ACII string representation
++ */
++static int wmi_gtoa(const char *in, char *out)
++{
++ int i;
++
++ for (i = 3; i >= 0; i--)
++ out += sprintf(out, "%02X", in[i] & 0xFF);
++
++ out += sprintf(out, "-");
++ out += sprintf(out, "%02X", in[5] & 0xFF);
++ out += sprintf(out, "%02X", in[4] & 0xFF);
++ out += sprintf(out, "-");
++ out += sprintf(out, "%02X", in[7] & 0xFF);
++ out += sprintf(out, "%02X", in[6] & 0xFF);
++ out += sprintf(out, "-");
++ out += sprintf(out, "%02X", in[8] & 0xFF);
++ out += sprintf(out, "%02X", in[9] & 0xFF);
++ out += sprintf(out, "-");
++
++ for (i = 10; i <= 15; i++)
++ out += sprintf(out, "%02X", in[i] & 0xFF);
++
++ out = '\0';
++ return 0;
++}
++
+ static bool find_guid(const char *guid_string, struct wmi_block **out)
+ {
+ char tmp[16], guid_input[16];
+@@ -555,6 +585,138 @@ bool wmi_has_guid(const char *guid_string)
+ EXPORT_SYMBOL_GPL(wmi_has_guid);
+
+ /*
++ * sysfs interface
++ */
++static ssize_t show_modalias(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ char guid_string[37];
++ struct wmi_block *wblock;
++
++ wblock = dev_get_drvdata(dev);
++ if (!wblock)
++ return -ENOMEM;
++
++ wmi_gtoa(wblock->gblock.guid, guid_string);
++
++ return sprintf(buf, "wmi:%s\n", guid_string);
++}
++static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);
++
++static int wmi_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
++{
++ char guid_string[37];
++
++ struct wmi_block *wblock;
++
++ if (add_uevent_var(env, "MODALIAS="))
++ return -ENOMEM;
++
++ wblock = dev_get_drvdata(dev);
++ if (!wblock)
++ return -ENOMEM;
++
++ wmi_gtoa(wblock->gblock.guid, guid_string);
++
++ strcpy(&env->buf[env->buflen - 1], "wmi:");
++ memcpy(&env->buf[env->buflen - 1 + 4], guid_string, 36);
++ env->buflen += 40;
++
++ return 0;
++}
++
++static void wmi_dev_free(struct device *dev)
++{
++ kfree(dev);
++}
++
++static struct class wmi_class = {
++ .name = "wmi",
++ .dev_release = wmi_dev_free,
++ .dev_uevent = wmi_dev_uevent,
++};
++
++static int wmi_create_devs(void)
++{
++ int result;
++ char guid_string[37];
++ struct guid_block *gblock;
++ struct wmi_block *wblock;
++ struct list_head *p;
++ struct device *guid_dev;
++
++ /* Create devices for all the GUIDs */
++ list_for_each(p, &wmi_blocks.list) {
++ wblock = list_entry(p, struct wmi_block, list);
++
++ guid_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
++ if (!guid_dev)
++ return -ENOMEM;
++
++ wblock->dev = guid_dev;
++
++ guid_dev->class = &wmi_class;
++ dev_set_drvdata(guid_dev, wblock);
++
++ gblock = &wblock->gblock;
++
++ wmi_gtoa(gblock->guid, guid_string);
++ dev_set_name(guid_dev, guid_string);
++
++ result = device_register(guid_dev);
++ if (result)
++ return result;
++
++ result = device_create_file(guid_dev, &dev_attr_modalias);
++ if (result)
++ return result;
++ }
++
++ return 0;
++}
++
++static void wmi_remove_devs(void)
++{
++ struct guid_block *gblock;
++ struct wmi_block *wblock;
++ struct list_head *p;
++ struct device *guid_dev;
++
++ /* Delete devices for all the GUIDs */
++ list_for_each(p, &wmi_blocks.list) {
++ wblock = list_entry(p, struct wmi_block, list);
++
++ guid_dev = wblock->dev;
++ gblock = &wblock->gblock;
++
++ device_remove_file(guid_dev, &dev_attr_modalias);
++
++ device_unregister(guid_dev);
++ }
++}
++
++static void wmi_class_exit(void)
++{
++ wmi_remove_devs();
++ class_unregister(&wmi_class);
++}
++
++static int wmi_class_init(void)
++{
++ int ret;
++
++ ret = class_register(&wmi_class);
++ if (ret)
++ return ret;
++
++ ret = wmi_create_devs();
++ if (ret)
++ wmi_class_exit();
++
++ return ret;
++}
++
++/*
+ * Parse the _WDG method for the GUID data blocks
+ */
+ static __init acpi_status parse_wdg(acpi_handle handle)
+@@ -709,10 +871,17 @@ static int __init acpi_wmi_init(void)
+
+ if (result < 0) {
+ printk(KERN_INFO PREFIX "Error loading mapper\n");
+- } else {
+- printk(KERN_INFO PREFIX "Mapper loaded\n");
++ return -ENODEV;
++ }
++
++ result = wmi_class_init();
++ if (result) {
++ acpi_bus_unregister_driver(&acpi_wmi_driver);
++ return result;
+ }
+
++ printk(KERN_INFO PREFIX "Mapper loaded\n");
++
+ return result;
+ }
+
+@@ -721,6 +890,8 @@ static void __exit acpi_wmi_exit(void)
+ struct list_head *p, *tmp;
+ struct wmi_block *wblock;
+
++ wmi_class_exit();
++
+ acpi_bus_unregister_driver(&acpi_wmi_driver);
+
+ list_for_each_safe(p, tmp, &wmi_blocks.list) {
diff --git a/linux-2.6-b43_-Rewrite-DMA-Tx-status-handling-sanity-checks.patch b/linux-2.6-b43_-Rewrite-DMA-Tx-status-handling-sanity-checks.patch
new file mode 100644
index 0000000..441213c
--- /dev/null
+++ b/linux-2.6-b43_-Rewrite-DMA-Tx-status-handling-sanity-checks.patch
@@ -0,0 +1,182 @@
+Back-port of the following upstream commit...
+
+commit 07681e211d736ba2394ab7f29f77e93adecd22c5
+Author: Michael Buesch <mb@bu3sch.de>
+Date: Thu Nov 19 22:24:29 2009 +0100
+
+ b43: Rewrite DMA Tx status handling sanity checks
+
+ This rewrites the error handling policies in the TX status handler.
+ It tries to be error-tolerant as in "try hard to not crash the machine".
+ It won't recover from errors (that are bugs in the firmware or driver),
+ because that's impossible. However, it will return a more or less useful
+ error message and bail out. It also tries hard to use rate-limited messages
+ to not flood the syslog in case of a failure.
+
+ Signed-off-by: Michael Buesch <mb@bu3sch.de>
+ Signed-off-by: John W. Linville <linville@tuxdriver.com>
+
+diff -up linux-2.6.32.noarch/drivers/net/wireless/b43/dma.c.orig linux-2.6.32.noarch/drivers/net/wireless/b43/dma.c
+--- linux-2.6.32.noarch/drivers/net/wireless/b43/dma.c.orig 2009-12-02 22:51:21.000000000 -0500
++++ linux-2.6.32.noarch/drivers/net/wireless/b43/dma.c 2010-03-17 14:02:28.000000000 -0400
+@@ -770,7 +770,7 @@ static void free_all_descbuffers(struct
+ for (i = 0; i < ring->nr_slots; i++) {
+ desc = ring->ops->idx2desc(ring, i, &meta);
+
+- if (!meta->skb) {
++ if (!meta->skb || b43_dma_ptr_is_poisoned(meta->skb)) {
+ B43_WARN_ON(!ring->tx);
+ continue;
+ }
+@@ -822,7 +822,7 @@ struct b43_dmaring *b43_setup_dmaring(st
+ enum b43_dmatype type)
+ {
+ struct b43_dmaring *ring;
+- int err;
++ int i, err;
+ dma_addr_t dma_test;
+
+ ring = kzalloc(sizeof(*ring), GFP_KERNEL);
+@@ -837,6 +837,8 @@ struct b43_dmaring *b43_setup_dmaring(st
+ GFP_KERNEL);
+ if (!ring->meta)
+ goto err_kfree_ring;
++ for (i = 0; i < ring->nr_slots; i++)
++ ring->meta->skb = B43_DMA_PTR_POISON;
+
+ ring->type = type;
+ ring->dev = dev;
+@@ -1147,11 +1149,13 @@ struct b43_dmaring *parse_cookie(struct
+ case 0x5000:
+ ring = dma->tx_ring_mcast;
+ break;
+- default:
+- B43_WARN_ON(1);
+ }
+ *slot = (cookie & 0x0FFF);
+- B43_WARN_ON(!(ring && *slot >= 0 && *slot < ring->nr_slots));
++ if (unlikely(!ring || *slot < 0 || *slot >= ring->nr_slots)) {
++ b43dbg(dev->wl, "TX-status contains "
++ "invalid cookie: 0x%04X\n", cookie);
++ return NULL;
++ }
+
+ return ring;
+ }
+@@ -1400,19 +1404,40 @@ void b43_dma_handle_txstatus(struct b43_
+ struct b43_dmaring *ring;
+ struct b43_dmadesc_generic *desc;
+ struct b43_dmadesc_meta *meta;
+- int slot;
++ int slot, firstused;
+ bool frame_succeed;
+
+ ring = parse_cookie(dev, status->cookie, &slot);
+ if (unlikely(!ring))
+ return;
+-
+ B43_WARN_ON(!ring->tx);
++
++ /* Sanity check: TX packets are processed in-order on one ring.
++ * Check if the slot deduced from the cookie really is the first
++ * used slot. */
++ firstused = ring->current_slot - ring->used_slots + 1;
++ if (firstused < 0)
++ firstused = ring->nr_slots + firstused;
++ if (unlikely(slot != firstused)) {
++ /* This possibly is a firmware bug and will result in
++ * malfunction, memory leaks and/or stall of DMA functionality. */
++ b43dbg(dev->wl, "Out of order TX status report on DMA ring %d. "
++ "Expected %d, but got %d\n",
++ ring->index, firstused, slot);
++ return;
++ }
++
+ ops = ring->ops;
+ while (1) {
+- B43_WARN_ON(!(slot >= 0 && slot < ring->nr_slots));
++ B43_WARN_ON(slot < 0 || slot >= ring->nr_slots);
+ desc = ops->idx2desc(ring, slot, &meta);
+
++ if (b43_dma_ptr_is_poisoned(meta->skb)) {
++ b43dbg(dev->wl, "Poisoned TX slot %d (first=%d) "
++ "on ring %d\n",
++ slot, firstused, ring->index);
++ break;
++ }
+ if (meta->skb)
+ unmap_descbuffer(ring, meta->dmaaddr, meta->skb->len,
+ 1);
+@@ -1423,7 +1448,14 @@ void b43_dma_handle_txstatus(struct b43_
+ if (meta->is_last_fragment) {
+ struct ieee80211_tx_info *info;
+
+- BUG_ON(!meta->skb);
++ if (unlikely(!meta->skb)) {
++ /* This is a scatter-gather fragment of a frame, so
++ * the skb pointer must not be NULL. */
++ b43dbg(dev->wl, "TX status unexpected NULL skb "
++ "at slot %d (first=%d) on ring %d\n",
++ slot, firstused, ring->index);
++ break;
++ }
+
+ info = IEEE80211_SKB_CB(meta->skb);
+
+@@ -1441,20 +1473,29 @@ void b43_dma_handle_txstatus(struct b43_
+ #endif /* DEBUG */
+ ieee80211_tx_status(dev->wl->hw, meta->skb);
+
+- /* skb is freed by ieee80211_tx_status() */
+- meta->skb = NULL;
++ /* skb will be freed by ieee80211_tx_status().
++ * Poison our pointer. */
++ meta->skb = B43_DMA_PTR_POISON;
+ } else {
+ /* No need to call free_descriptor_buffer here, as
+ * this is only the txhdr, which is not allocated.
+ */
+- B43_WARN_ON(meta->skb);
++ if (unlikely(meta->skb)) {
++ b43dbg(dev->wl, "TX status unexpected non-NULL skb "
++ "at slot %d (first=%d) on ring %d\n",
++ slot, firstused, ring->index);
++ break;
++ }
+ }
+
+ /* Everything unmapped and free'd. So it's not used anymore. */
+ ring->used_slots--;
+
+- if (meta->is_last_fragment)
++ if (meta->is_last_fragment) {
++ /* This is the last scatter-gather
++ * fragment of the frame. We are done. */
+ break;
++ }
+ slot = next_slot(ring, slot);
+ }
+ if (ring->stopped) {
+diff -up linux-2.6.32.noarch/drivers/net/wireless/b43/dma.h.orig linux-2.6.32.noarch/drivers/net/wireless/b43/dma.h
+--- linux-2.6.32.noarch/drivers/net/wireless/b43/dma.h.orig 2009-12-02 22:51:21.000000000 -0500
++++ linux-2.6.32.noarch/drivers/net/wireless/b43/dma.h 2010-03-17 13:57:57.000000000 -0400
+@@ -1,7 +1,7 @@
+ #ifndef B43_DMA_H_
+ #define B43_DMA_H_
+
+-#include <linux/ieee80211.h>
++#include <linux/err.h>
+
+ #include "b43.h"
+
+@@ -165,6 +165,10 @@ struct b43_dmadesc_generic {
+ #define B43_RXRING_SLOTS 64
+ #define B43_DMA0_RX_BUFFERSIZE IEEE80211_MAX_FRAME_LEN
+
++/* Pointer poison */
++#define B43_DMA_PTR_POISON ((void *)ERR_PTR(-ENOMEM))
++#define b43_dma_ptr_is_poisoned(ptr) (unlikely((ptr) == B43_DMA_PTR_POISON))
++
+
+ struct sk_buff;
+ struct b43_private;
diff --git a/linux-2.6-block-silently-error-unsupported-empty-barriers-too.patch b/linux-2.6-block-silently-error-unsupported-empty-barriers-too.patch
new file mode 100644
index 0000000..e6ab893
--- /dev/null
+++ b/linux-2.6-block-silently-error-unsupported-empty-barriers-too.patch
@@ -0,0 +1,48 @@
+From: Mark McLoughlin <markmc@redhat.com>
+Subject: [PATCH] block: silently error unsupported empty barriers too
+
+With 2.6.31-rc5 in a KVM guest using dm and virtio_blk, we see the
+following errors:
+
+ end_request: I/O error, dev vda, sector 0
+ end_request: I/O error, dev vda, sector 0
+
+The errors go away if dm stops submitting empty barriers, by reverting:
+
+ commit 52b1fd5a27c625c78373e024bf570af3c9d44a79
+ Author: Mikulas Patocka <mpatocka@redhat.com>
+ dm: send empty barriers to targets in dm_flush
+
+We should error all barriers, even empty barriers, on devices like
+virtio_blk which don't support them.
+
+See also:
+
+ https://bugzilla.redhat.com/514901
+
+Signed-off-by: Mark McLoughlin <markmc@redhat.com>
+Cc: Rusty Russell <rusty@rustcorp.com.au>
+Cc: Mikulas Patocka <mpatocka@redhat.com>
+Cc: Alasdair G Kergon <agk@redhat.com>
+Cc: Neil Brown <neilb@suse.de>
+---
+ block/blk-core.c | 3 +--
+ 1 files changed, 1 insertions(+), 2 deletions(-)
+
+diff --git a/block/blk-core.c b/block/blk-core.c
+index e3299a7..35ad2bb 100644
+--- a/block/blk-core.c
++++ b/block/blk-core.c
+@@ -1163,8 +1163,7 @@ static int __make_request(struct request_queue *q, struct bio *bio)
+ const int unplug = bio_unplug(bio);
+ int rw_flags;
+
+- if (bio_barrier(bio) && bio_has_data(bio) &&
+- (q->next_ordered == QUEUE_ORDERED_NONE)) {
++ if (bio_barrier(bio) && (q->next_ordered == QUEUE_ORDERED_NONE)) {
+ bio_endio(bio, -EOPNOTSUPP);
+ return 0;
+ }
+--
+1.6.4
+
diff --git a/linux-2.6-btrfs-fix-acl.patch b/linux-2.6-btrfs-fix-acl.patch
new file mode 100644
index 0000000..3e015da
--- /dev/null
+++ b/linux-2.6-btrfs-fix-acl.patch
@@ -0,0 +1,25 @@
+diff -up linux-2.6.32.noarch/fs/btrfs/acl.c.orig linux-2.6.32.noarch/fs/btrfs/acl.c
+--- linux-2.6.32.noarch/fs/btrfs/acl.c.orig 2009-12-02 22:51:21.000000000 -0500
++++ linux-2.6.32.noarch/fs/btrfs/acl.c 2010-01-14 15:36:25.926371944 -0500
+@@ -110,13 +110,15 @@ static int btrfs_set_acl(struct inode *i
+
+ switch (type) {
+ case ACL_TYPE_ACCESS:
+- mode = inode->i_mode;
+- ret = posix_acl_equiv_mode(acl, &mode);
+- if (ret < 0)
+- return ret;
+- ret = 0;
+- inode->i_mode = mode;
+ name = POSIX_ACL_XATTR_ACCESS;
++ if (acl) {
++ mode = inode->i_mode;
++ ret = posix_acl_equiv_mode(acl, &mode);
++ if (ret < 0)
++ return ret;
++ ret = 0;
++ inode->i_mode = mode;
++ }
+ break;
+ case ACL_TYPE_DEFAULT:
+ if (!S_ISDIR(inode->i_mode))
diff --git a/linux-2.6-build-nonintconfig.patch b/linux-2.6-build-nonintconfig.patch
new file mode 100644
index 0000000..e88e0ea
--- /dev/null
+++ b/linux-2.6-build-nonintconfig.patch
@@ -0,0 +1,128 @@
+diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
+index 6d69c7c..ff84d12 100644
+--- a/scripts/kconfig/Makefile
++++ b/scripts/kconfig/Makefile
+@@ -58,6 +58,11 @@ localyesconfig: $(obj)/streamline_config.pl $(obj)/conf
+ fi
+ $(Q)rm -f .tmp.config
+
++nonint_oldconfig: $(obj)/conf
++ $< -b $(Kconfig)
++loose_nonint_oldconfig: $(obj)/conf
++ $< -B $(Kconfig)
++
+ # Create new linux.pot file
+ # Adjust charset to UTF-8 in .po file to accept UTF-8 in Kconfig files
+ # The symlink is used to repair a deficiency in arch/um
+diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
+index 9960d1c..ac8d455 100644
+--- a/scripts/kconfig/conf.c
++++ b/scripts/kconfig/conf.c
+@@ -23,6 +23,8 @@ enum {
+ ask_all,
+ ask_new,
+ ask_silent,
++ dont_ask,
++ dont_ask_dont_tell,
+ set_default,
+ set_yes,
+ set_mod,
+@@ -360,7 +362,10 @@ static void conf(struct menu *menu)
+
+ switch (prop->type) {
+ case P_MENU:
+- if (input_mode == ask_silent && rootEntry != menu) {
++ if ((input_mode == ask_silent ||
++ input_mode == dont_ask ||
++ input_mode == dont_ask_dont_tell) &&
++ rootEntry != menu) {
+ check_conf(menu);
+ return;
+ }
+@@ -406,6 +411,8 @@ conf_childs:
+ indent -= 2;
+ }
+
++static int return_value;
++
+ static void check_conf(struct menu *menu)
+ {
+ struct symbol *sym;
+@@ -418,12 +425,21 @@ static void check_conf(struct menu *menu)
+ if (sym && !sym_has_value(sym)) {
+ if (sym_is_changable(sym) ||
+ (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes)) {
++ if (input_mode == dont_ask ||
++ input_mode == dont_ask_dont_tell) {
++ if (input_mode == dont_ask &&
++ sym->name && !sym_is_choice_value(sym)) {
++ fprintf(stderr,"CONFIG_%s\n",sym->name);
++ ++return_value;
++ }
++ } else {
+ if (!conf_cnt++)
+ printf(_("*\n* Restart config...\n*\n"));
+ rootEntry = menu_get_parent_menu(menu);
+ conf(rootEntry);
+ }
+ }
++ }
+
+ for (child = menu->list; child; child = child->next)
+ check_conf(child);
+@@ -439,7 +455,7 @@ int main(int ac, char **av)
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+
+- while ((opt = getopt(ac, av, "osdD:nmyrh")) != -1) {
++ while ((opt = getopt(ac, av, "osbBdD:nmyrh")) != -1) {
+ switch (opt) {
+ case 'o':
+ input_mode = ask_silent;
+@@ -448,6 +464,12 @@ int main(int ac, char **av)
+ input_mode = ask_silent;
+ sync_kconfig = 1;
+ break;
++ case 'b':
++ input_mode = dont_ask;
++ break;
++ case 'B':
++ input_mode = dont_ask_dont_tell;
++ break;
+ case 'd':
+ input_mode = set_default;
+ break;
+@@ -525,6 +547,8 @@ int main(int ac, char **av)
+ case ask_silent:
+ case ask_all:
+ case ask_new:
++ case dont_ask:
++ case dont_ask_dont_tell:
+ conf_read(NULL);
+ break;
+ case set_no:
+@@ -586,12 +610,16 @@ int main(int ac, char **av)
+ conf(&rootmenu);
+ input_mode = ask_silent;
+ /* fall through */
++ case dont_ask:
++ case dont_ask_dont_tell:
+ case ask_silent:
+ /* Update until a loop caused no more changes */
+ do {
+ conf_cnt = 0;
+ check_conf(&rootmenu);
+- } while (conf_cnt);
++ } while (conf_cnt &&
++ (input_mode != dont_ask &&
++ input_mode != dont_ask_dont_tell));
+ break;
+ }
+
+@@ -613,5 +641,5 @@ int main(int ac, char **av)
+ exit(1);
+ }
+ }
+- return 0;
++ return return_value;
+ }
diff --git a/linux-2.6-cantiga-iommu-gfx.patch b/linux-2.6-cantiga-iommu-gfx.patch
new file mode 100644
index 0000000..a18e38b
--- /dev/null
+++ b/linux-2.6-cantiga-iommu-gfx.patch
@@ -0,0 +1,26 @@
+diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c
+index 4173125..baa32a0 100644
+--- a/drivers/pci/intel-iommu.c
++++ b/drivers/pci/intel-iommu.c
+@@ -340,7 +340,7 @@ int dmar_disabled = 0;
+ int dmar_disabled = 1;
+ #endif /*CONFIG_DMAR_DEFAULT_ON*/
+
+-static int __initdata dmar_map_gfx = 1;
++static int dmar_map_gfx = 1;
+ static int dmar_forcedac;
+ static int intel_iommu_strict;
+
+@@ -3728,6 +3728,12 @@ static void __devinit quirk_iommu_rwbf(struct pci_dev *dev)
+ */
+ printk(KERN_INFO "DMAR: Forcing write-buffer flush capability\n");
+ rwbf_quirk = 1;
++
++ /* https://bugzilla.redhat.com/show_bug.cgi?id=538163 */
++ if (dev->revision == 0x07) {
++ printk(KERN_INFO "DMAR: Disabling IOMMU for graphics on this chipset\n");
++ dmar_map_gfx = 0;
++ }
+ }
+
+ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_rwbf);
diff --git a/linux-2.6-compile-fixes.patch b/linux-2.6-compile-fixes.patch
new file mode 100644
index 0000000..34c08ce
--- /dev/null
+++ b/linux-2.6-compile-fixes.patch
@@ -0,0 +1,6 @@
+#
+# Small compile fixes (For more involved fixes, please use a separate patch).
+#
+# Please add the errors from gcc before the diffs to save others having
+# to do a compile to figure out what your diff is fixing. Thanks.
+#
diff --git a/linux-2.6-crash-driver.patch b/linux-2.6-crash-driver.patch
new file mode 100644
index 0000000..5669f7a
--- /dev/null
+++ b/linux-2.6-crash-driver.patch
@@ -0,0 +1,363 @@
+diff --git a/arch/ia64/include/asm/crash.h b/arch/ia64/include/asm/crash.h
+new file mode 100644
+index 0000000..541af84
+--- /dev/null
++++ b/arch/ia64/include/asm/crash.h
+@@ -0,0 +1,90 @@
++#ifndef _ASM_IA64_CRASH_H
++#define _ASM_IA64_CRASH_H
++
++/*
++ * linux/include/asm-ia64/crash.h
++ *
++ * Copyright (c) 2004 Red Hat, Inc. All rights reserved.
++ *
++ * 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, 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.
++ *
++ */
++
++#ifdef __KERNEL__
++
++#include <linux/efi.h>
++#include <linux/mm.h>
++#include <asm/mmzone.h>
++
++static inline void *
++map_virtual(u64 offset, struct page **pp)
++{
++ struct page *page;
++ unsigned long pfn;
++ u32 type;
++
++ if (REGION_NUMBER(offset) == 5) {
++ char byte;
++
++ if (__get_user(byte, (char *)offset) == 0)
++ return (void *)offset;
++ else
++ return NULL;
++ }
++
++ switch (type = efi_mem_type(offset))
++ {
++ case EFI_LOADER_CODE:
++ case EFI_LOADER_DATA:
++ case EFI_BOOT_SERVICES_CODE:
++ case EFI_BOOT_SERVICES_DATA:
++ case EFI_CONVENTIONAL_MEMORY:
++ break;
++
++ default:
++ printk(KERN_INFO
++ "crash memory driver: invalid memory type for %lx: %d\n",
++ offset, type);
++ return NULL;
++ }
++
++ pfn = offset >> PAGE_SHIFT;
++
++ if (!pfn_valid(pfn)) {
++ printk(KERN_INFO
++ "crash memory driver: invalid pfn: %lx )\n", pfn);
++ return NULL;
++ }
++
++ page = pfn_to_page(pfn);
++
++ if (!page->virtual) {
++ printk(KERN_INFO
++ "crash memory driver: offset: %lx page: %lx page->virtual: NULL\n",
++ offset, (unsigned long)page);
++ return NULL;
++ }
++
++ return (page->virtual + (offset & (PAGE_SIZE-1)));
++}
++
++static inline void unmap_virtual(struct page *page)
++{
++ return;
++}
++
++#endif /* __KERNEL__ */
++
++#endif /* _ASM_IA64_CRASH_H */
+diff --git a/arch/ia64/kernel/ia64_ksyms.c b/arch/ia64/kernel/ia64_ksyms.c
+index 14d39e3..cf3d040 100644
+--- a/arch/ia64/kernel/ia64_ksyms.c
++++ b/arch/ia64/kernel/ia64_ksyms.c
+@@ -84,6 +84,9 @@ EXPORT_SYMBOL(ia64_save_scratch_fpregs);
+ #include <asm/unwind.h>
+ EXPORT_SYMBOL(unw_init_running);
+
++#include <linux/efi.h>
++EXPORT_SYMBOL_GPL(efi_mem_type);
++
+ #if defined(CONFIG_IA64_ESI) || defined(CONFIG_IA64_ESI_MODULE)
+ extern void esi_call_phys (void);
+ EXPORT_SYMBOL_GPL(esi_call_phys);
+diff --git a/arch/x86/include/asm/crash.h b/arch/x86/include/asm/crash.h
+new file mode 100644
+index 0000000..dfcc006
+--- /dev/null
++++ b/arch/x86/include/asm/crash.h
+@@ -0,0 +1,75 @@
++#ifndef _ASM_I386_CRASH_H
++#define _ASM_I386_CRASH_H
++
++/*
++ * linux/include/asm-i386/crash.h
++ *
++ * Copyright (c) 2004 Red Hat, Inc. All rights reserved.
++ *
++ * 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, 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.
++ *
++ */
++
++#ifdef __KERNEL__
++
++#include <linux/mm.h>
++#include <linux/highmem.h>
++#include <asm/mmzone.h>
++
++extern int page_is_ram(unsigned long);
++
++static inline void *
++map_virtual(u64 offset, struct page **pp)
++{
++ struct page *page;
++ unsigned long pfn;
++ void *vaddr;
++
++ pfn = (unsigned long)(offset >> PAGE_SHIFT);
++
++ if (!page_is_ram(pfn)) {
++ printk(KERN_INFO
++ "crash memory driver: !page_is_ram(pfn: %lx)\n", pfn);
++ return NULL;
++ }
++
++ if (!pfn_valid(pfn)) {
++ printk(KERN_INFO
++ "crash memory driver: invalid pfn: %lx )\n", pfn);
++ return NULL;
++ }
++
++ page = pfn_to_page(pfn);
++
++ vaddr = kmap(page);
++ if (!vaddr) {
++ printk(KERN_INFO
++ "crash memory driver: pfn: %lx kmap(page: %lx) failed\n",
++ pfn, (unsigned long)page);
++ return NULL;
++ }
++
++ *pp = page;
++ return (vaddr + (offset & (PAGE_SIZE-1)));
++}
++
++static inline void unmap_virtual(struct page *page)
++{
++ kunmap(page);
++}
++
++#endif /* __KERNEL__ */
++
++#endif /* _ASM_I386_CRASH_H */
+diff --git a/arch/x86/mm/ioremap.c b/arch/x86/mm/ioremap.c
+index 334e63c..8e1ccbc 100644
+--- a/arch/x86/mm/ioremap.c
++++ b/arch/x86/mm/ioremap.c
+@@ -60,6 +60,7 @@ int page_is_ram(unsigned long pagenr)
+ }
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(page_is_ram);
+
+ /*
+ * Fix up the linear direct mapping of the kernel to avoid cache attribute
+diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
+index 08a6f50..8bc5e9a 100644
+--- a/drivers/char/Kconfig
++++ b/drivers/char/Kconfig
+@@ -484,6 +484,8 @@ config LEGACY_PTYS
+ security. This option enables these legacy devices; on most
+ systems, it is safe to say N.
+
++config CRASH
++ tristate "Crash Utility memory driver"
+
+ config LEGACY_PTY_COUNT
+ int "Maximum number of legacy PTY in use"
+diff --git a/drivers/char/Makefile b/drivers/char/Makefile
+index 19a79dd..0bee860 100644
+--- a/drivers/char/Makefile
++++ b/drivers/char/Makefile
+@@ -112,6 +112,8 @@ obj-$(CONFIG_PS3_FLASH) += ps3flash.o
+ obj-$(CONFIG_JS_RTC) += js-rtc.o
+ js-rtc-y = rtc.o
+
++obj-$(CONFIG_CRASH) += crash.o
++
+ # Files generated that shall be removed upon make clean
+ clean-files := consolemap_deftbl.c defkeymap.c
+
+diff --git a/drivers/char/crash.c b/drivers/char/crash.c
+new file mode 100644
+index 0000000..e5437de
+--- /dev/null
++++ b/drivers/char/crash.c
+@@ -0,0 +1,128 @@
++/*
++ * linux/drivers/char/crash.c
++ *
++ * Copyright (C) 2004 Dave Anderson <anderson@redhat.com>
++ * Copyright (C) 2004 Red Hat, Inc.
++ */
++
++/******************************************************************************
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2, 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 <linux/module.h>
++#include <linux/types.h>
++#include <linux/miscdevice.h>
++#include <linux/init.h>
++#include <asm/io.h>
++#include <asm/uaccess.h>
++#include <asm/types.h>
++#include <asm/crash.h>
++
++#define CRASH_VERSION "1.0"
++
++/*
++ * These are the file operation functions that allow crash utility
++ * access to physical memory.
++ */
++
++static loff_t
++crash_llseek(struct file * file, loff_t offset, int orig)
++{
++ switch (orig) {
++ case 0:
++ file->f_pos = offset;
++ return file->f_pos;
++ case 1:
++ file->f_pos += offset;
++ return file->f_pos;
++ default:
++ return -EINVAL;
++ }
++}
++
++/*
++ * Determine the page address for an address offset value,
++ * get a virtual address for it, and copy it out.
++ * Accesses must fit within a page.
++ */
++static ssize_t
++crash_read(struct file *file, char *buf, size_t count, loff_t *poff)
++{
++ void *vaddr;
++ struct page *page;
++ u64 offset;
++ ssize_t read;
++
++ offset = *poff;
++ if (offset >> PAGE_SHIFT != (offset+count-1) >> PAGE_SHIFT)
++ return -EINVAL;
++
++ vaddr = map_virtual(offset, &page);
++ if (!vaddr)
++ return -EFAULT;
++
++ if (copy_to_user(buf, vaddr, count)) {
++ unmap_virtual(page);
++ return -EFAULT;
++ }
++ unmap_virtual(page);
++
++ read = count;
++ *poff += read;
++ return read;
++}
++
++static struct file_operations crash_fops = {
++ .owner = THIS_MODULE,
++ .llseek = crash_llseek,
++ .read = crash_read,
++};
++
++static struct miscdevice crash_dev = {
++ MISC_DYNAMIC_MINOR,
++ "crash",
++ &crash_fops
++};
++
++static int __init
++crash_init(void)
++{
++ int ret;
++
++ ret = misc_register(&crash_dev);
++ if (ret) {
++ printk(KERN_ERR
++ "crash memory driver: cannot misc_register (MISC_DYNAMIC_MINOR)\n");
++ goto out;
++ }
++
++ ret = 0;
++ printk(KERN_INFO "crash memory driver: version %s\n", CRASH_VERSION);
++out:
++ return ret;
++}
++
++static void __exit
++crash_cleanup_module(void)
++{
++ misc_deregister(&crash_dev);
++}
++
++module_init(crash_init);
++module_exit(crash_cleanup_module);
++
++MODULE_LICENSE("GPL");
diff --git a/linux-2.6-debug-always-inline-kzalloc.patch b/linux-2.6-debug-always-inline-kzalloc.patch
new file mode 100644
index 0000000..24f665c
--- /dev/null
+++ b/linux-2.6-debug-always-inline-kzalloc.patch
@@ -0,0 +1,25 @@
+From 76ec0e2e6d6edf81abc0331d5e7873ef7b2f6019 Mon Sep 17 00:00:00 2001
+From: Kyle McMartin <kyle@phobos.i.jkkm.org>
+Date: Wed, 8 Jul 2009 13:06:01 -0400
+Subject: [PATCH 6/6] fedora: linux-2.6-debug-always-inline-kzalloc.patch
+
+---
+ include/linux/slab.h | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/include/linux/slab.h b/include/linux/slab.h
+index 2da8372..d4ef74f 100644
+--- a/include/linux/slab.h
++++ b/include/linux/slab.h
+@@ -310,7 +310,7 @@ static inline void *kmem_cache_zalloc(struct kmem_cache *k, gfp_t flags)
+ * @size: how many bytes of memory are required.
+ * @flags: the type of memory to allocate (see kmalloc).
+ */
+-static inline void *kzalloc(size_t size, gfp_t flags)
++static __always_inline void *kzalloc(size_t size, gfp_t flags)
+ {
+ return kmalloc(size, flags | __GFP_ZERO);
+ }
+--
+1.6.2.5
+
diff --git a/linux-2.6-debug-nmi-timeout.patch b/linux-2.6-debug-nmi-timeout.patch
new file mode 100644
index 0000000..447419c
--- /dev/null
+++ b/linux-2.6-debug-nmi-timeout.patch
@@ -0,0 +1,45 @@
+From 899dd25ae272c73407c1477ec223982d0b57a668 Mon Sep 17 00:00:00 2001
+From: Kyle McMartin <kyle@phobos.i.jkkm.org>
+Date: Wed, 8 Jul 2009 13:03:06 -0400
+Subject: [PATCH 2/6] fedora: linux-2.6-debug-nmi-timeout.patch
+
+---
+ arch/x86/kernel/apic/nmi.c | 2 +-
+ lib/Kconfig.debug | 8 ++++++++
+ 2 files changed, 9 insertions(+), 1 deletions(-)
+
+diff --git a/arch/x86/kernel/apic/nmi.c b/arch/x86/kernel/apic/nmi.c
+index b3025b4..e82a450 100644
+--- a/arch/x86/kernel/apic/nmi.c
++++ b/arch/x86/kernel/apic/nmi.c
+@@ -436,7 +436,7 @@ nmi_watchdog_tick(struct pt_regs *regs, unsigned reason)
+ * wait a few IRQs (5 seconds) before doing the oops ...
+ */
+ local_inc(&__get_cpu_var(alert_counter));
+- if (local_read(&__get_cpu_var(alert_counter)) == 5 * nmi_hz)
++ if (local_read(&__get_cpu_var(alert_counter)) == CONFIG_DEBUG_NMI_TIMEOUT * nmi_hz)
+ /*
+ * die_nmi will return ONLY if NOTIFY_STOP happens..
+ */
+diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
+index 12327b2..2790b4f 100644
+--- a/lib/Kconfig.debug
++++ b/lib/Kconfig.debug
+@@ -245,6 +245,14 @@ config SCHEDSTATS
+ application, you can say N to avoid the very slight overhead
+ this adds.
+
++config DEBUG_NMI_TIMEOUT
++ int "Number of seconds before NMI timeout"
++ depends on X86
++ default 5
++ help
++ This value is the number of seconds the NMI watchdog will tick
++ before it decides the machine has hung.
++
+ config TIMER_STATS
+ bool "Collect kernel timers statistics"
+ depends on DEBUG_KERNEL && PROC_FS
+--
+1.6.2.5
+
diff --git a/linux-2.6-debug-sizeof-structs.patch b/linux-2.6-debug-sizeof-structs.patch
new file mode 100644
index 0000000..cc7747d
--- /dev/null
+++ b/linux-2.6-debug-sizeof-structs.patch
@@ -0,0 +1,31 @@
+diff --git a/init/main.c b/init/main.c
+index 7449819..98cfaae 100644
+--- a/init/main.c
++++ b/init/main.c
+@@ -369,6 +369,10 @@ static void __init setup_nr_cpu_ids(void)
+ nr_cpu_ids = find_last_bit(cpumask_bits(cpu_possible_mask),NR_CPUS) + 1;
+ }
+
++#include <linux/ext3_fs_i.h>
++#include <linux/skbuff.h>
++#include <linux/sched.h>
++
+ /* Called by boot processor to activate the rest. */
+ static void __init smp_init(void)
+ {
+@@ -391,6 +395,15 @@ static void __init smp_init(void)
+ /* Any cleanup work */
+ printk(KERN_INFO "Brought up %ld CPUs\n", (long)num_online_cpus());
+ smp_cpus_done(setup_max_cpus);
++
++ printk(KERN_DEBUG "sizeof(vma)=%u bytes\n", (unsigned int) sizeof(struct vm_area_struct));
++ printk(KERN_DEBUG "sizeof(page)=%u bytes\n", (unsigned int) sizeof(struct page));
++ printk(KERN_DEBUG "sizeof(inode)=%u bytes\n", (unsigned int) sizeof(struct inode));
++ printk(KERN_DEBUG "sizeof(dentry)=%u bytes\n", (unsigned int) sizeof(struct dentry));
++ printk(KERN_DEBUG "sizeof(ext3inode)=%u bytes\n", (unsigned int) sizeof(struct ext3_inode_info));
++ printk(KERN_DEBUG "sizeof(buffer_head)=%u bytes\n", (unsigned int) sizeof(struct buffer_head));
++ printk(KERN_DEBUG "sizeof(skbuff)=%u bytes\n", (unsigned int) sizeof(struct sk_buff));
++ printk(KERN_DEBUG "sizeof(task_struct)=%u bytes\n", (unsigned int) sizeof(struct task_struct));
+ }
+
+ #endif
diff --git a/linux-2.6-debug-taint-vm.patch b/linux-2.6-debug-taint-vm.patch
new file mode 100644
index 0000000..ee367d4
--- /dev/null
+++ b/linux-2.6-debug-taint-vm.patch
@@ -0,0 +1,65 @@
+From b04c57d9dc889462951312be2ac81ff6c702e954 Mon Sep 17 00:00:00 2001
+From: Kyle McMartin <kyle@phobos.i.jkkm.org>
+Date: Wed, 8 Jul 2009 13:05:09 -0400
+Subject: [PATCH 3/6] fedora: linux-2.6-debug-taint-vm.patch
+
+---
+ kernel/panic.c | 4 +++-
+ mm/slab.c | 8 ++++----
+ mm/slub.c | 2 +-
+ 4 files changed, 11 insertions(+), 8 deletions(-)
+
+diff --git a/kernel/panic.c b/kernel/panic.c
+index 984b3ec..6d1c3be 100644
+--- a/kernel/panic.c
++++ b/kernel/panic.c
+@@ -199,6 +199,7 @@ const char *print_tainted(void)
+
+ return buf;
+ }
++EXPORT_SYMBOL(print_tainted);
+
+ int test_taint(unsigned flag)
+ {
+diff --git a/mm/slab.c b/mm/slab.c
+index e74a16e..7bc287e 100644
+--- a/mm/slab.c
++++ b/mm/slab.c
+@@ -1803,8 +1803,8 @@ static void check_poison_obj(struct kmem_cache *cachep, void *objp)
+ /* Print header */
+ if (lines == 0) {
+ printk(KERN_ERR
+- "Slab corruption: %s start=%p, len=%d\n",
+- cachep->name, realobj, size);
++ "Slab corruption (%s): %s start=%p, len=%d\n",
++ print_tainted(), cachep->name, realobj, size);
+ print_objinfo(cachep, objp, 0);
+ }
+ /* Hexdump the affected line */
+@@ -2902,8 +2902,8 @@ static void check_slabp(struct kmem_cache *cachep, struct slab *slabp)
+ if (entries != cachep->num - slabp->inuse) {
+ bad:
+ printk(KERN_ERR "slab: Internal list corruption detected in "
+- "cache '%s'(%d), slabp %p(%d). Hexdump:\n",
+- cachep->name, cachep->num, slabp, slabp->inuse);
++ "cache '%s'(%d), slabp %p(%d). Tainted(%s). Hexdump:\n",
++ cachep->name, cachep->num, slabp, slabp->inuse, print_tainted());
+ for (i = 0;
+ i < sizeof(*slabp) + cachep->num * sizeof(kmem_bufctl_t);
+ i++) {
+diff --git a/mm/slub.c b/mm/slub.c
+index 819f056..8eff0f4 100644
+--- a/mm/slub.c
++++ b/mm/slub.c
+@@ -433,7 +433,7 @@ static void slab_bug(struct kmem_cache *s, char *fmt, ...)
+ va_end(args);
+ printk(KERN_ERR "========================================"
+ "=====================================\n");
+- printk(KERN_ERR "BUG %s: %s\n", s->name, buf);
++ printk(KERN_ERR "BUG %s (%s): %s\n", s->name, print_tainted(), buf);
+ printk(KERN_ERR "----------------------------------------"
+ "-------------------------------------\n\n");
+ }
+--
+1.6.2.5
+
diff --git a/linux-2.6-debug-vm-would-have-oomkilled.patch b/linux-2.6-debug-vm-would-have-oomkilled.patch
new file mode 100644
index 0000000..2fcd9b9
--- /dev/null
+++ b/linux-2.6-debug-vm-would-have-oomkilled.patch
@@ -0,0 +1,52 @@
+diff --git a/kernel/sysctl.c b/kernel/sysctl.c
+index 0d949c5..8fb2bd9 100644
+--- a/kernel/sysctl.c
++++ b/kernel/sysctl.c
+@@ -72,6 +72,7 @@ extern int sysctl_overcommit_ratio;
+ extern int sysctl_panic_on_oom;
+ extern int sysctl_oom_kill_allocating_task;
+ extern int sysctl_oom_dump_tasks;
++extern int sysctl_would_have_oomkilled;
+ extern int max_threads;
+ extern int core_uses_pid;
+ extern int suid_dumpable;
+@@ -1073,6 +1074,14 @@ static struct ctl_table vm_table[] = {
+ .proc_handler = &proc_dointvec,
+ },
+ {
++ .ctl_name = CTL_UNNUMBERED,
++ .procname = "would_have_oomkilled",
++ .data = &sysctl_would_have_oomkilled,
++ .maxlen = sizeof(sysctl_would_have_oomkilled),
++ .mode = 0644,
++ .proc_handler = &proc_dointvec,
++ },
++ {
+ .ctl_name = VM_OVERCOMMIT_RATIO,
+ .procname = "overcommit_ratio",
+ .data = &sysctl_overcommit_ratio,
+diff --git a/mm/oom_kill.c b/mm/oom_kill.c
+index ea2147d..788fe84 100644
+--- a/mm/oom_kill.c
++++ b/mm/oom_kill.c
+@@ -31,6 +31,7 @@
+ int sysctl_panic_on_oom;
+ int sysctl_oom_kill_allocating_task;
+ int sysctl_oom_dump_tasks;
++int sysctl_would_have_oomkilled;
+ static DEFINE_SPINLOCK(zone_scan_lock);
+ /* #define DEBUG */
+
+@@ -356,6 +357,12 @@ static void __oom_kill_task(struct task_struct *p, int verbose)
+ return;
+ }
+
++ if (sysctl_would_have_oomkilled == 1) {
++ printk(KERN_ERR "Would have killed process %d (%s). But continuing instead.\n",
++ task_pid_nr(p), p->comm);
++ return;
++ }
++
+ if (verbose)
+ printk(KERN_ERR "Killed process %d (%s)\n",
+ task_pid_nr(p), p->comm);
diff --git a/linux-2.6-defaults-acpi-video.patch b/linux-2.6-defaults-acpi-video.patch
new file mode 100644
index 0000000..af883b0
--- /dev/null
+++ b/linux-2.6-defaults-acpi-video.patch
@@ -0,0 +1,13 @@
+diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c
+index d8d7596..a1b7117 100644
+--- a/drivers/acpi/video.c
++++ b/drivers/acpi/video.c
+@@ -71,7 +71,7 @@ MODULE_AUTHOR("Bruno Ducrot");
+ MODULE_DESCRIPTION("ACPI Video Driver");
+ MODULE_LICENSE("GPL");
+
+-static int brightness_switch_enabled = 1;
++static int brightness_switch_enabled = 0;
+ module_param(brightness_switch_enabled, bool, 0644);
+
+ static int acpi_video_bus_add(struct acpi_device *device);
diff --git a/linux-2.6-defaults-alsa-hda-beep-off.patch b/linux-2.6-defaults-alsa-hda-beep-off.patch
new file mode 100644
index 0000000..15c1af0
--- /dev/null
+++ b/linux-2.6-defaults-alsa-hda-beep-off.patch
@@ -0,0 +1,13 @@
+diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c
+index 3ecd7e7..2762b40 100644
+--- a/sound/pci/hda/hda_beep.c
++++ b/sound/pci/hda/hda_beep.c
+@@ -122,7 +122,7 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid)
+ beep->nid = nid;
+ beep->dev = input_dev;
+ beep->codec = codec;
+- beep->enabled = 1;
++ beep->enabled = 0;
+ codec->beep = beep;
+
+ INIT_WORK(&beep->beep_work, &snd_hda_generate_beep);
diff --git a/linux-2.6-defaults-aspm.patch b/linux-2.6-defaults-aspm.patch
new file mode 100644
index 0000000..49b832d
--- /dev/null
+++ b/linux-2.6-defaults-aspm.patch
@@ -0,0 +1,12 @@
+diff -up linux-2.6.30.noarch/drivers/pci/pcie/aspm.c.mjg linux-2.6.30.noarch/drivers/pci/pcie/aspm.c
+--- linux-2.6.30.noarch/drivers/pci/pcie/aspm.c.mjg 2009-07-16 22:01:11.000000000 +0100
++++ linux-2.6.30.noarch/drivers/pci/pcie/aspm.c 2009-07-16 22:01:30.000000000 +0100
+@@ -65,7 +65,7 @@ static LIST_HEAD(link_list);
+ #define POLICY_DEFAULT 0 /* BIOS default setting */
+ #define POLICY_PERFORMANCE 1 /* high performance */
+ #define POLICY_POWERSAVE 2 /* high power saving */
+-static int aspm_policy;
++static int aspm_policy = POLICY_POWERSAVE;
+ static const char *policy_str[] = {
+ [POLICY_DEFAULT] = "default",
+ [POLICY_PERFORMANCE] = "performance",
diff --git a/linux-2.6-defaults-pci_no_msi.patch b/linux-2.6-defaults-pci_no_msi.patch
new file mode 100644
index 0000000..fad6a53
--- /dev/null
+++ b/linux-2.6-defaults-pci_no_msi.patch
@@ -0,0 +1,92 @@
+--- linux-2.6.30.noarch/Documentation/kernel-parameters.txt~ 2009-06-24 14:25:04.000000000 -0400
++++ linux-2.6.30.noarch/Documentation/kernel-parameters.txt 2009-06-24 14:25:32.000000000 -0400
+@@ -1811,6 +1811,9 @@ and is between 256 and 4096 characters.
+ check_enable_amd_mmconf [X86] check for and enable
+ properly configured MMIO access to PCI
+ config space on AMD family 10h CPU
++ msi [MSI] If the PCI_MSI kernel config parameter is
++ enabled, this kernel boot option can be used to
++ enable the use of MSI interrupts system-wide.
+ nomsi [MSI] If the PCI_MSI kernel config parameter is
+ enabled, this kernel boot option can be used to
+ disable the use of MSI interrupts system-wide.
+diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
+index 2a4501d..209758c 100644
+--- a/drivers/pci/Kconfig
++++ b/drivers/pci/Kconfig
+@@ -21,6 +21,18 @@ config PCI_MSI
+
+ If you don't know what to do here, say N.
+
++config PCI_MSI_DEFAULT_ON
++ def_bool y
++ prompt "Use Message Signaled Interrupts by default"
++ depends on PCI_MSI
++ help
++ Selecting this option will enable use of PCI MSI where applicable
++ by default. Support for MSI can be disabled through the use of the
++ pci=nomsi boot flag. Conversely, if this option is not selected,
++ support for PCI MSI can be enabled by passing the pci=msi flag.
++
++ If you don't know what to do here, say N.
++
+ config PCI_LEGACY
+ bool "Enable deprecated pci_find_* API"
+ depends on PCI
+diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
+index 896a15d..53df583 100644
+--- a/drivers/pci/msi.c
++++ b/drivers/pci/msi.c
+@@ -23,7 +23,11 @@
+ #include "pci.h"
+ #include "msi.h"
+
++#ifdef CONFIG_PCI_MSI_DEFAULT_ON
+ static int pci_msi_enable = 1;
++#else
++static int pci_msi_enable = 0;
++#endif /*CONFIG_PCI_MSI_DEFAULT_ON*/
+
+ /* Arch hooks */
+
+@@ -786,6 +790,11 @@ int pci_msi_enabled(void)
+ }
+ EXPORT_SYMBOL(pci_msi_enabled);
+
++void pci_yes_msi(void)
++{
++ pci_msi_enable = 1;
++}
++
+ void pci_msi_init_pci_dev(struct pci_dev *dev)
+ {
+ INIT_LIST_HEAD(&dev->msi_list);
+diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
+index 17bd932..e9bc9fe 100644
+--- a/drivers/pci/pci.c
++++ b/drivers/pci/pci.c
+@@ -2393,6 +2393,8 @@ static int __init pci_setup(char *str)
+ if (*str && (str = pcibios_setup(str)) && *str) {
+ if (!strcmp(str, "nomsi")) {
+ pci_no_msi();
++ } else if (!strcmp(str, "msi")) {
++ pci_yes_msi();
+ } else if (!strcmp(str, "noaer")) {
+ pci_no_aer();
+ } else if (!strcmp(str, "nodomains")) {
+diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
+index 26ddf78..85efe81 100644
+--- a/drivers/pci/pci.h
++++ b/drivers/pci/pci.h
+@@ -111,9 +111,11 @@ extern unsigned int pci_pm_d3_delay;
+
+ #ifdef CONFIG_PCI_MSI
+ void pci_no_msi(void);
++void pci_yes_msi(void);
+ extern void pci_msi_init_pci_dev(struct pci_dev *dev);
+ #else
+ static inline void pci_no_msi(void) { }
++static inline void pci_yes_msi(void) { }
+ static inline void pci_msi_init_pci_dev(struct pci_dev *dev) { }
+ #endif
+
diff --git a/linux-2.6-defaults-pciehp.patch b/linux-2.6-defaults-pciehp.patch
new file mode 100644
index 0000000..07f2670
--- /dev/null
+++ b/linux-2.6-defaults-pciehp.patch
@@ -0,0 +1,13 @@
+diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
+index e7f3c9e..4f2b037 100644
+--- a/drivers/pci/hotplug/pciehp_core.c
++++ b/drivers/pci/hotplug/pciehp_core.c
+@@ -41,7 +41,7 @@ int pciehp_debug;
+ int pciehp_poll_mode;
+ int pciehp_poll_time;
+ int pciehp_force;
+-int pciehp_passive;
++int pciehp_passive=1;
+ struct workqueue_struct *pciehp_wq;
+
+ #define DRIVER_VERSION "0.4"
diff --git a/linux-2.6-dell-laptop-rfkill-fix.patch b/linux-2.6-dell-laptop-rfkill-fix.patch
new file mode 100644
index 0000000..336788c
--- /dev/null
+++ b/linux-2.6-dell-laptop-rfkill-fix.patch
@@ -0,0 +1,323 @@
+diff --git a/drivers/input/input.c b/drivers/input/input.c
+index 7c237e6..80f1e48 100644
+--- a/drivers/input/input.c
++++ b/drivers/input/input.c
+@@ -88,19 +88,26 @@ static int input_defuzz_abs_event(int value, int old_val, int fuzz)
+ */
+ static void input_pass_event(struct input_dev *dev,
+ unsigned int type, unsigned int code, int value)
+-{
+- struct input_handle *handle;
++
++{ struct input_handle *handle;
+
+ rcu_read_lock();
+
+ handle = rcu_dereference(dev->grab);
+- if (handle)
++ if (handle) {
+ handle->handler->event(handle, type, code, value);
+- else
+- list_for_each_entry_rcu(handle, &dev->h_list, d_node)
+- if (handle->open)
+- handle->handler->event(handle,
+- type, code, value);
++ goto out;
++ }
++
++ handle = rcu_dereference(dev->filter);
++ if (handle && handle->handler->filter(handle, type, code, value))
++ goto out;
++
++ list_for_each_entry_rcu(handle, &dev->h_list, d_node)
++ if (handle->open)
++ handle->handler->event(handle,
++ type, code, value);
++out:
+ rcu_read_unlock();
+ }
+
+@@ -375,12 +382,15 @@ int input_grab_device(struct input_handle *handle)
+ }
+ EXPORT_SYMBOL(input_grab_device);
+
+-static void __input_release_device(struct input_handle *handle)
++static void __input_release_device(struct input_handle *handle, bool filter)
+ {
+ struct input_dev *dev = handle->dev;
+
+- if (dev->grab == handle) {
+- rcu_assign_pointer(dev->grab, NULL);
++ if (handle == (filter ? dev->filter : dev->grab)) {
++ if (filter)
++ rcu_assign_pointer(dev->filter, NULL);
++ else
++ rcu_assign_pointer(dev->grab, NULL);
+ /* Make sure input_pass_event() notices that grab is gone */
+ synchronize_rcu();
+
+@@ -404,12 +414,65 @@ void input_release_device(struct input_handle *handle)
+ struct input_dev *dev = handle->dev;
+
+ mutex_lock(&dev->mutex);
+- __input_release_device(handle);
++ __input_release_device(handle, false);
+ mutex_unlock(&dev->mutex);
+ }
+ EXPORT_SYMBOL(input_release_device);
+
+ /**
++ * input_filter_device - allow input events to be filtered from higher layers
++ * @handle: input handle that wants to filter the device
++ *
++ * When a device is filtered by an input handle all events generated by
++ * the device are to this handle. If the filter function returns true then
++ * the event is discarded rather than being passed to any other input handles,
++ * otherwise it is passed to them as normal. Grabs will be handled before
++ * filters, so a grabbed device will not deliver events to a filter function.
++ */
++int input_filter_device(struct input_handle *handle)
++{
++ struct input_dev *dev = handle->dev;
++ int retval;
++
++ retval = mutex_lock_interruptible(&dev->mutex);
++ if (retval)
++ return retval;
++
++ if (dev->filter) {
++ retval = -EBUSY;
++ goto out;
++ }
++
++ rcu_assign_pointer(dev->filter, handle);
++ synchronize_rcu();
++
++ out:
++ mutex_unlock(&dev->mutex);
++ return retval;
++}
++EXPORT_SYMBOL(input_filter_device);
++
++/**
++ * input_unfilter_device - removes a filter from a device
++ * @handle: input handle that owns the device
++ *
++ * Removes the filter from a device so that other input handles can
++ * start receiving unfiltered input events. Upon release all handlers
++ * attached to the device have their start() method called so they
++ * have a change to synchronize device state with the rest of the
++ * system.
++ */
++void input_unfilter_device(struct input_handle *handle)
++{
++ struct input_dev *dev = handle->dev;
++
++ mutex_lock(&dev->mutex);
++ __input_release_device(handle, true);
++ mutex_unlock(&dev->mutex);
++}
++EXPORT_SYMBOL(input_unfilter_device);
++
++/**
+ * input_open_device - open input device
+ * @handle: handle through which device is being accessed
+ *
+@@ -482,7 +545,9 @@ void input_close_device(struct input_handle *handle)
+
+ mutex_lock(&dev->mutex);
+
+- __input_release_device(handle);
++ /* Release both grabs and filters */
++ __input_release_device(handle, false);
++ __input_release_device(handle, true);
+
+ if (!--dev->users && dev->close)
+ dev->close(dev);
+diff --git a/include/linux/input.h b/include/linux/input.h
+index 8b3bc3e..e28f116 100644
+--- a/include/linux/input.h
++++ b/include/linux/input.h
+@@ -1118,6 +1118,7 @@ struct input_dev {
+ int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value);
+
+ struct input_handle *grab;
++ struct input_handle *filter;
+
+ spinlock_t event_lock;
+ struct mutex mutex;
+@@ -1218,6 +1219,7 @@ struct input_handler {
+ void *private;
+
+ void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
++ bool (*filter)(struct input_handle *handle, unsigned int type, unsigned int code, int value);
+ int (*connect)(struct input_handler *handler, struct input_dev *dev, const struct input_device_id *id);
+ void (*disconnect)(struct input_handle *handle);
+ void (*start)(struct input_handle *handle);
+@@ -1295,6 +1297,9 @@ void input_unregister_handle(struct input_handle *);
+ int input_grab_device(struct input_handle *);
+ void input_release_device(struct input_handle *);
+
++int input_filter_device(struct input_handle *);
++void input_unfilter_device(struct input_handle *);
++
+ int input_open_device(struct input_handle *);
+ void input_close_device(struct input_handle *);
+
+diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
+index 74909c4..71a4149 100644
+--- a/drivers/platform/x86/dell-laptop.c
++++ b/drivers/platform/x86/dell-laptop.c
+@@ -22,6 +22,7 @@
+ #include <linux/rfkill.h>
+ #include <linux/power_supply.h>
+ #include <linux/acpi.h>
++#include <linux/input.h>
+ #include "../../firmware/dcdbas.h"
+
+ #define BRIGHTNESS_TOKEN 0x7d
+@@ -206,6 +207,16 @@ static const struct rfkill_ops dell_rfkill_ops = {
+ .query = dell_rfkill_query,
+ };
+
++static void dell_rfkill_update(void)
++{
++ if (wifi_rfkill)
++ dell_rfkill_query(wifi_rfkill, (void *)1);
++ if (bluetooth_rfkill)
++ dell_rfkill_query(bluetooth_rfkill, (void *)2);
++ if (wwan_rfkill)
++ dell_rfkill_query(wwan_rfkill, (void *)3);
++}
++
+ static int dell_setup_rfkill(void)
+ {
+ struct calling_interface_buffer buffer;
+@@ -310,6 +321,90 @@ static struct backlight_ops dell_ops = {
+ .update_status = dell_send_intensity,
+ };
+
++static const struct input_device_id dell_input_ids[] = {
++ {
++ .bustype = 0x11,
++ .vendor = 0x01,
++ .product = 0x01,
++ .version = 0xab41,
++ .flags = INPUT_DEVICE_ID_MATCH_BUS |
++ INPUT_DEVICE_ID_MATCH_VENDOR |
++ INPUT_DEVICE_ID_MATCH_PRODUCT |
++ INPUT_DEVICE_ID_MATCH_VERSION
++ },
++ { },
++};
++
++static bool dell_input_filter(struct input_handle *handle, unsigned int type,
++ unsigned int code, int value)
++{
++ if (type == EV_KEY && code == KEY_WLAN && value == 1) {
++ dell_rfkill_update();
++ return 1;
++ }
++
++ return 0;
++}
++
++static void dell_input_event(struct input_handle *handle, unsigned int type,
++ unsigned int code, int value)
++{
++}
++
++static int dell_input_connect(struct input_handler *handler,
++ struct input_dev *dev,
++ const struct input_device_id *id)
++{
++ struct input_handle *handle;
++ int error;
++
++ handle = kzalloc(sizeof(struct input_handle), GFP_KERNEL);
++ if (!handle)
++ return -ENOMEM;
++
++ handle->dev = dev;
++ handle->handler = handler;
++ handle->name = "dell-laptop";
++
++ error = input_register_handle(handle);
++ if (error)
++ goto err_free_handle;
++
++ error = input_open_device(handle);
++ if (error)
++ goto err_unregister_handle;
++
++ error = input_filter_device(handle);
++ if (error)
++ goto err_close_handle;
++
++ return 0;
++
++err_close_handle:
++ input_close_device(handle);
++err_unregister_handle:
++ input_unregister_handle(handle);
++err_free_handle:
++ kfree(handle);
++ return error;
++}
++
++static void dell_input_disconnect(struct input_handle *handle)
++{
++ input_close_device(handle);
++ input_unregister_handle(handle);
++ kfree(handle);
++}
++
++static struct input_handler dell_input_handler = {
++ .name = "dell-laptop",
++ .filter = dell_input_filter,
++ .event = dell_input_event,
++ .connect = dell_input_connect,
++ .disconnect = dell_input_disconnect,
++ .id_table = dell_input_ids,
++};
++
+ static int __init dell_init(void)
+ {
+ struct calling_interface_buffer buffer;
+@@ -333,6 +428,10 @@ static int __init dell_init(void)
+ goto out;
+ }
+
++ if (input_register_handler(&dell_input_handler))
++ printk(KERN_INFO
++ "dell-laptop: Could not register input filter\n");
++
+ #ifdef CONFIG_ACPI
+ /* In the event of an ACPI backlight being available, don't
+ * register the platform controller.
+@@ -388,6 +487,7 @@ static void __exit dell_exit(void)
+ rfkill_unregister(bluetooth_rfkill);
+ if (wwan_rfkill)
+ rfkill_unregister(wwan_rfkill);
++ input_unregister_handler(&dell_input_handler);
+ }
+
+ module_init(dell_init);
+diff --git a/drivers/platform/x86/dell-laptop.c b/drivers/platform/x86/dell-laptop.c
+index 71a4149..e559fa1 100644
+--- a/drivers/platform/x86/dell-laptop.c
++++ b/drivers/platform/x86/dell-laptop.c
+@@ -198,8 +198,8 @@ static void dell_rfkill_query(struct rfkill *rfkill, void *data)
+ dell_send_request(&buffer, 17, 11);
+ status = buffer.output[1];
+
+- if (status & BIT(bit))
+- rfkill_set_hw_state(rfkill, !!(status & BIT(16)));
++ rfkill_set_sw_state(rfkill, !!(status & BIT(bit)));
++ rfkill_set_hw_state(rfkill, !(status & BIT(16)));
+ }
+
+ static const struct rfkill_ops dell_rfkill_ops = {
+--
+1.6.3.3
+
diff --git a/linux-2.6-driver-level-usb-autosuspend.diff b/linux-2.6-driver-level-usb-autosuspend.diff
new file mode 100644
index 0000000..2632af4
--- /dev/null
+++ b/linux-2.6-driver-level-usb-autosuspend.diff
@@ -0,0 +1,62 @@
+commit 7d0d20a25c6f477fb198b85510c78156d7d7c5af
+Author: Matthew Garrett <mjg@redhat.com>
+Date: Tue Jun 9 20:11:47 2009 +0100
+
+ usb: Allow drivers to enable USB autosuspend on a per-device basis
+
+ USB autosuspend is currently only enabled by default for hubs. On other
+ hardware the decision is made by userspace. This is unnecessary in cases
+ where we know that the hardware supports autosuspend, so this patch adds
+ a function to allow drivers to enable it at probe time.
+
+ Signed-off-by: Matthew Garrett <mjg@redhat.com>
+
+diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
+index 4f86447..f7caf00 100644
+--- a/drivers/usb/core/driver.c
++++ b/drivers/usb/core/driver.c
+@@ -1575,6 +1575,22 @@ void usb_autopm_put_interface_async(struct usb_interface *intf)
+ EXPORT_SYMBOL_GPL(usb_autopm_put_interface_async);
+
+ /**
++ * usb_device_autosuspend_enable - enable autosuspend on a device
++ * @udev: the usb_device to be autosuspended
++ *
++ * This routine should be called by an interface driver when it knows that
++ * the device in question supports USB autosuspend.
++ *
++ */
++void usb_device_autosuspend_enable(struct usb_device *udev)
++{
++ udev->autosuspend_disabled = 0;
++ udev->autoresume_disabled = 0;
++ usb_external_suspend_device(udev, PMSG_USER_SUSPEND);
++}
++EXPORT_SYMBOL_GPL(usb_device_autosuspend_enable);
++
++/**
+ * usb_autopm_get_interface - increment a USB interface's PM-usage counter
+ * @intf: the usb_interface whose counter should be incremented
+ *
+diff --git a/include/linux/usb.h b/include/linux/usb.h
+index a34fa89..0c22c64 100644
+--- a/include/linux/usb.h
++++ b/include/linux/usb.h
+@@ -543,6 +543,7 @@ extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id);
+
+ /* USB autosuspend and autoresume */
+ #ifdef CONFIG_USB_SUSPEND
++extern void usb_device_autosuspend_enable(struct usb_device *udev);
+ extern int usb_autopm_set_interface(struct usb_interface *intf);
+ extern int usb_autopm_get_interface(struct usb_interface *intf);
+ extern void usb_autopm_put_interface(struct usb_interface *intf);
+@@ -568,6 +569,9 @@ static inline void usb_mark_last_busy(struct usb_device *udev)
+
+ #else
+
++static inline void usb_device_autosuspend_enable(struct usb_device *udev)
++{ }
++
+ static inline int usb_autopm_set_interface(struct usb_interface *intf)
+ { return 0; }
+
diff --git a/linux-2.6-e1000-ich9.patch b/linux-2.6-e1000-ich9.patch
new file mode 100644
index 0000000..5a3391c
--- /dev/null
+++ b/linux-2.6-e1000-ich9.patch
@@ -0,0 +1,27 @@
+diff --git a/drivers/net/e1000e/ich8lan.c b/drivers/net/e1000e/ich8lan.c
+index 8f8139d..22c49ec 100644
+--- a/drivers/net/e1000e/ich8lan.c
++++ b/drivers/net/e1000e/ich8lan.c
+@@ -209,6 +209,12 @@ static s32 e1000_init_phy_params_ich8lan(struct e1000_hw *hw)
+
+ /* Verify phy id */
+ switch (phy->id) {
++ case 0x0:
++ if (hw->adapter->pdev->device == 0x10be)
++ hw_dbg(hw, "got 0 phy id, trying anyway");
++ /* Fall through to IGP03E1000 case below */
++ else
++ return -E1000_ERR_PHY;
+ case IGP03E1000_E_PHY_ID:
+ phy->type = e1000_phy_igp_3;
+ phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT;
+--- linux-2.6.25.noarch/drivers/net/e1000e/netdev.c~ 2008-05-16 10:31:41.000000000 -0400
++++ linux-2.6.25.noarch/drivers/net/e1000e/netdev.c 2008-05-16 10:32:43.000000000 -0400
+@@ -4599,6 +4599,7 @@ static struct pci_device_id e1000_pci_tb
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IGP_M), board_ich9lan },
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IGP_M_AMT), board_ich9lan },
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH9_IGP_M_V), board_ich9lan },
++ { PCI_VDEVICE(INTEL, 0x10be), board_ich9lan },
+
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH10_R_BM_LM), board_ich9lan },
+ { PCI_VDEVICE(INTEL, E1000_DEV_ID_ICH10_R_BM_LF), board_ich9lan },
diff --git a/linux-2.6-enable-btusb-autosuspend.patch b/linux-2.6-enable-btusb-autosuspend.patch
new file mode 100644
index 0000000..7e75341
--- /dev/null
+++ b/linux-2.6-enable-btusb-autosuspend.patch
@@ -0,0 +1,18 @@
+commit 8e962bd41a2cbf7f0e55191a757b87f793a725a8
+Author: Matthew Garrett <mjg@redhat.com>
+Date: Tue Jun 9 20:47:51 2009 +0100
+
+ btusb: Enable autosuspend by default
+
+diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
+index 44bc8bb..4c33417 100644
+--- a/drivers/bluetooth/btusb.c
++++ b/drivers/bluetooth/btusb.c
+@@ -1020,6 +1020,7 @@ static int btusb_probe(struct usb_interface *intf,
+ }
+
+ usb_set_intfdata(intf, data);
++ usb_device_autosuspend_enable(data->udev);
+
+ return 0;
+ }
diff --git a/linux-2.6-execshield.patch b/linux-2.6-execshield.patch
new file mode 100644
index 0000000..61e444f
--- /dev/null
+++ b/linux-2.6-execshield.patch
@@ -0,0 +1,987 @@
+diff --git a/arch/x86/include/asm/desc.h b/arch/x86/include/asm/desc.h
+index e8de2f6..538c2b6 100644
+--- a/arch/x86/include/asm/desc.h
++++ b/arch/x86/include/asm/desc.h
+@@ -5,6 +5,7 @@
+ #include <asm/ldt.h>
+ #include <asm/mmu.h>
+ #include <linux/smp.h>
++#include <linux/mm_types.h>
+
+ static inline void fill_ldt(struct desc_struct *desc,
+ const struct user_desc *info)
+@@ -93,6 +94,9 @@ static inline int desc_empty(const void *ptr)
+
+ #define load_TLS(t, cpu) native_load_tls(t, cpu)
+ #define set_ldt native_set_ldt
++#ifdef CONFIG_X86_32
++#define load_user_cs_desc native_load_user_cs_desc
++#endif /*CONFIG_X86_32*/
+
+ #define write_ldt_entry(dt, entry, desc) \
+ native_write_ldt_entry(dt, entry, desc)
+@@ -392,4 +396,25 @@ static inline void set_system_intr_gate_ist(int n, void *addr, unsigned ist)
+ _set_gate(n, GATE_INTERRUPT, addr, 0x3, ist, __KERNEL_CS);
+ }
+
++#ifdef CONFIG_X86_32
++static inline void set_user_cs(struct desc_struct *desc, unsigned long limit)
++{
++ limit = (limit - 1) / PAGE_SIZE;
++ desc->a = limit & 0xffff;
++ desc->b = (limit & 0xf0000) | 0x00c0fb00;
++}
++
++static inline void native_load_user_cs_desc(int cpu, struct mm_struct *mm)
++{
++ get_cpu_gdt_table(cpu)[GDT_ENTRY_DEFAULT_USER_CS] = (mm)->context.user_cs;
++}
++
++#define arch_add_exec_range arch_add_exec_range
++#define arch_remove_exec_range arch_remove_exec_range
++#define arch_flush_exec_range arch_flush_exec_range
++extern void arch_add_exec_range(struct mm_struct *mm, unsigned long limit);
++extern void arch_remove_exec_range(struct mm_struct *mm, unsigned long limit);
++extern void arch_flush_exec_range(struct mm_struct *mm);
++#endif /* CONFIG_X86_32 */
++
+ #endif /* _ASM_X86_DESC_H */
+diff --git a/arch/x86/include/asm/mmu.h b/arch/x86/include/asm/mmu.h
+index 80a1dee..8314c66 100644
+--- a/arch/x86/include/asm/mmu.h
++++ b/arch/x86/include/asm/mmu.h
+@@ -7,12 +7,19 @@
+ /*
+ * The x86 doesn't have a mmu context, but
+ * we put the segment information here.
++ *
++ * exec_limit is used to track the range PROT_EXEC
++ * mappings span.
+ */
+ typedef struct {
+ void *ldt;
+ int size;
+ struct mutex lock;
+ void *vdso;
++#ifdef CONFIG_X86_32
++ struct desc_struct user_cs;
++ unsigned long exec_limit;
++#endif
+ } mm_context_t;
+
+ #ifdef CONFIG_SMP
+diff --git a/arch/x86/include/asm/paravirt.h b/arch/x86/include/asm/paravirt.h
+index 8aebcc4..cbbd2b0 100644
+--- a/arch/x86/include/asm/paravirt.h
++++ b/arch/x86/include/asm/paravirt.h
+@@ -289,6 +289,12 @@ static inline void set_ldt(const void *addr, unsigned entries)
+ {
+ PVOP_VCALL2(pv_cpu_ops.set_ldt, addr, entries);
+ }
++#ifdef CONFIG_X86_32
++static inline void load_user_cs_desc(unsigned int cpu, struct mm_struct *mm)
++{
++ PVOP_VCALL2(pv_cpu_ops.load_user_cs_desc, cpu, mm);
++}
++#endif /*CONFIG_X86_32*/
+ static inline void store_gdt(struct desc_ptr *dtr)
+ {
+ PVOP_VCALL1(pv_cpu_ops.store_gdt, dtr);
+diff --git a/arch/x86/include/asm/paravirt_types.h b/arch/x86/include/asm/paravirt_types.h
+index dd0f5b3..c2727ef 100644
+--- a/arch/x86/include/asm/paravirt_types.h
++++ b/arch/x86/include/asm/paravirt_types.h
+@@ -118,6 +118,9 @@ struct pv_cpu_ops {
+ void (*store_gdt)(struct desc_ptr *);
+ void (*store_idt)(struct desc_ptr *);
+ void (*set_ldt)(const void *desc, unsigned entries);
++#ifdef CONFIG_X86_32
++ void (*load_user_cs_desc)(int cpu, struct mm_struct *mm);
++#endif
+ unsigned long (*store_tr)(void);
+ void (*load_tls)(struct thread_struct *t, unsigned int cpu);
+ #ifdef CONFIG_X86_64
+diff --git a/arch/x86/include/asm/processor.h b/arch/x86/include/asm/processor.h
+index c3429e8..62cc460 100644
+--- a/arch/x86/include/asm/processor.h
++++ b/arch/x86/include/asm/processor.h
+@@ -161,6 +161,9 @@ static inline int hlt_works(int cpu)
+
+ #define cache_line_size() (boot_cpu_data.x86_cache_alignment)
+
++#define __HAVE_ARCH_ALIGN_STACK
++extern unsigned long arch_align_stack(unsigned long sp);
++
+ extern void cpu_detect(struct cpuinfo_x86 *c);
+
+ extern struct pt_regs *idle_regs(struct pt_regs *);
+diff --git a/arch/x86/kernel/cpu/common.c b/arch/x86/kernel/cpu/common.c
+index cc25c2b..6ce4863 100644
+--- a/arch/x86/kernel/cpu/common.c
++++ b/arch/x86/kernel/cpu/common.c
+@@ -798,6 +798,20 @@ static void __cpuinit identify_cpu(struct cpuinfo_x86 *c)
+ /* Filter out anything that depends on CPUID levels we don't have */
+ filter_cpuid_features(c, true);
+
++ /*
++ * emulation of NX with segment limits unfortunately means
++ * we have to disable the fast system calls, due to the way that
++ * sysexit clears the segment limits on return.
++ * If we have either disabled exec-shield on the boot command line,
++ * or we have NX, then we don't need to do this.
++ */
++ if (exec_shield != 0) {
++#ifdef CONFIG_X86_PAE
++ if (!test_cpu_cap(c, X86_FEATURE_NX))
++#endif
++ clear_cpu_cap(c, X86_FEATURE_SEP);
++ }
++
+ /* If the model name is still unset, do table lookup. */
+ if (!c->x86_model_id[0]) {
+ const char *p;
+diff --git a/arch/x86/kernel/paravirt.c b/arch/x86/kernel/paravirt.c
+index 1b1739d..c2dda16 100644
+--- a/arch/x86/kernel/paravirt.c
++++ b/arch/x86/kernel/paravirt.c
+@@ -345,6 +345,9 @@ struct pv_cpu_ops pv_cpu_ops = {
+ .read_tscp = native_read_tscp,
+ .load_tr_desc = native_load_tr_desc,
+ .set_ldt = native_set_ldt,
++#ifdef CONFIG_X86_32
++ .load_user_cs_desc = native_load_user_cs_desc,
++#endif /*CONFIG_X86_32*/
+ .load_gdt = native_load_gdt,
+ .load_idt = native_load_idt,
+ .store_gdt = native_store_gdt,
+diff --git a/arch/x86/kernel/process_32.c b/arch/x86/kernel/process_32.c
+index 4cf7956..b2407dc 100644
+--- a/arch/x86/kernel/process_32.c
++++ b/arch/x86/kernel/process_32.c
+@@ -296,7 +296,10 @@ int copy_thread(unsigned long clone_flags, unsigned long sp,
+ void
+ start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
+ {
++ int cpu;
++
+ set_user_gs(regs, 0);
++
+ regs->fs = 0;
+ set_fs(USER_DS);
+ regs->ds = __USER_DS;
+@@ -305,6 +308,11 @@ start_thread(struct pt_regs *regs, unsigned long new_ip, unsigned long new_sp)
+ regs->cs = __USER_CS;
+ regs->ip = new_ip;
+ regs->sp = new_sp;
++
++ cpu = get_cpu();
++ load_user_cs_desc(cpu, current->mm);
++ put_cpu();
++
+ /*
+ * Free the old FP and other extended state
+ */
+@@ -364,6 +372,9 @@ __switch_to(struct task_struct *prev_p, struct task_struct *next_p)
+ if (preload_fpu)
+ prefetch(next->xstate);
+
++ if (next_p->mm)
++ load_user_cs_desc(cpu, next_p->mm);
++
+ /*
+ * Reload esp0.
+ */
+@@ -497,3 +508,40 @@ unsigned long get_wchan(struct task_struct *p)
+ return 0;
+ }
+
++static void modify_cs(struct mm_struct *mm, unsigned long limit)
++{
++ mm->context.exec_limit = limit;
++ set_user_cs(&mm->context.user_cs, limit);
++ if (mm == current->mm) {
++ int cpu;
++
++ cpu = get_cpu();
++ load_user_cs_desc(cpu, mm);
++ put_cpu();
++ }
++}
++
++void arch_add_exec_range(struct mm_struct *mm, unsigned long limit)
++{
++ if (limit > mm->context.exec_limit)
++ modify_cs(mm, limit);
++}
++
++void arch_remove_exec_range(struct mm_struct *mm, unsigned long old_end)
++{
++ struct vm_area_struct *vma;
++ unsigned long limit = PAGE_SIZE;
++
++ if (old_end == mm->context.exec_limit) {
++ for (vma = mm->mmap; vma; vma = vma->vm_next)
++ if ((vma->vm_flags & VM_EXEC) && (vma->vm_end > limit))
++ limit = vma->vm_end;
++ modify_cs(mm, limit);
++ }
++}
++
++void arch_flush_exec_range(struct mm_struct *mm)
++{
++ mm->context.exec_limit = 0;
++ set_user_cs(&mm->context.user_cs, 0);
++}
+diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c
+index 7e37dce..92ae538 100644
+--- a/arch/x86/kernel/traps.c
++++ b/arch/x86/kernel/traps.c
+@@ -115,6 +115,76 @@ die_if_kernel(const char *str, struct pt_regs *regs, long err)
+ if (!user_mode_vm(regs))
+ die(str, regs, err);
+ }
++
++static inline int
++__compare_user_cs_desc(const struct desc_struct *desc1,
++ const struct desc_struct *desc2)
++{
++ return ((desc1->limit0 != desc2->limit0) ||
++ (desc1->limit != desc2->limit) ||
++ (desc1->base0 != desc2->base0) ||
++ (desc1->base1 != desc2->base1) ||
++ (desc1->base2 != desc2->base2));
++}
++
++/*
++ * lazy-check for CS validity on exec-shield binaries:
++ *
++ * the original non-exec stack patch was written by
++ * Solar Designer <solar at openwall.com>. Thanks!
++ */
++static int
++check_lazy_exec_limit(int cpu, struct pt_regs *regs, long error_code)
++{
++ struct desc_struct *desc1, *desc2;
++ struct vm_area_struct *vma;
++ unsigned long limit;
++
++ if (current->mm == NULL)
++ return 0;
++
++ limit = -1UL;
++ if (current->mm->context.exec_limit != -1UL) {
++ limit = PAGE_SIZE;
++ spin_lock(&current->mm->page_table_lock);
++ for (vma = current->mm->mmap; vma; vma = vma->vm_next)
++ if ((vma->vm_flags & VM_EXEC) && (vma->vm_end > limit))
++ limit = vma->vm_end;
++ vma = get_gate_vma(current);
++ if (vma && (vma->vm_flags & VM_EXEC) && (vma->vm_end > limit))
++ limit = vma->vm_end;
++ spin_unlock(&current->mm->page_table_lock);
++ if (limit >= TASK_SIZE)
++ limit = -1UL;
++ current->mm->context.exec_limit = limit;
++ }
++ set_user_cs(&current->mm->context.user_cs, limit);
++
++ desc1 = &current->mm->context.user_cs;
++ desc2 = get_cpu_gdt_table(cpu) + GDT_ENTRY_DEFAULT_USER_CS;
++
++ if (__compare_user_cs_desc(desc1, desc2)) {
++ /*
++ * The CS was not in sync - reload it and retry the
++ * instruction. If the instruction still faults then
++ * we won't hit this branch next time around.
++ */
++ if (print_fatal_signals >= 2) {
++ printk(KERN_ERR "#GPF fixup (%ld[seg:%lx]) at %08lx, CPU#%d.\n",
++ error_code, error_code/8, regs->ip,
++ smp_processor_id());
++ printk(KERN_ERR "exec_limit: %08lx, user_cs: %08x/%08x, CPU_cs: %08x/%08x.\n",
++ current->mm->context.exec_limit,
++ desc1->a, desc1->b, desc2->a, desc2->b);
++ }
++
++ load_user_cs_desc(cpu, current->mm);
++
++ return 1;
++ }
++
++ return 0;
++}
+ #endif
+
+ static void __kprobes
+@@ -273,6 +343,29 @@ do_general_protection(struct pt_regs *regs, long error_code)
+ if (!user_mode(regs))
+ goto gp_in_kernel;
+
++#ifdef CONFIG_X86_32
++{
++ int cpu;
++ int ok;
++
++ cpu = get_cpu();
++ ok = check_lazy_exec_limit(cpu, regs, error_code);
++ put_cpu();
++
++ if (ok)
++ return;
++
++ if (print_fatal_signals) {
++ printk(KERN_ERR "#GPF(%ld[seg:%lx]) at %08lx, CPU#%d.\n",
++ error_code, error_code/8, regs->ip, smp_processor_id());
++ printk(KERN_ERR "exec_limit: %08lx, user_cs: %08x/%08x.\n",
++ current->mm->context.exec_limit,
++ current->mm->context.user_cs.a,
++ current->mm->context.user_cs.b);
++ }
++}
++#endif /*CONFIG_X86_32*/
++
+ tsk->thread.error_code = error_code;
+ tsk->thread.trap_no = 13;
+
+@@ -881,19 +974,37 @@ do_device_not_available(struct pt_regs *regs, long error_code)
+ }
+
+ #ifdef CONFIG_X86_32
++/*
++ * The fixup code for errors in iret jumps to here (iret_exc). It loses
++ * the original trap number and erorr code. The bogus trap 32 and error
++ * code 0 are what the vanilla kernel delivers via:
++ * DO_ERROR_INFO(32, SIGSEGV, "iret exception", iret_error, ILL_BADSTK, 0, 1)
++ *
++ * NOTE: Because of the final "1" in the macro we need to enable interrupts.
++ *
++ * In case of a general protection fault in the iret instruction, we
++ * need to check for a lazy CS update for exec-shield.
++ */
+ dotraplinkage void do_iret_error(struct pt_regs *regs, long error_code)
+ {
+- siginfo_t info;
++ int ok;
++ int cpu;
++
+ local_irq_enable();
+
+- info.si_signo = SIGILL;
+- info.si_errno = 0;
+- info.si_code = ILL_BADSTK;
+- info.si_addr = NULL;
+- if (notify_die(DIE_TRAP, "iret exception",
+- regs, error_code, 32, SIGILL) == NOTIFY_STOP)
+- return;
+- do_trap(32, SIGILL, "iret exception", regs, error_code, &info);
++ cpu = get_cpu();
++ ok = check_lazy_exec_limit(cpu, regs, error_code);
++ put_cpu();
++
++ if (!ok && notify_die(DIE_TRAP, "iret exception", regs,
++ error_code, 32, SIGSEGV) != NOTIFY_STOP) {
++ siginfo_t info;
++ info.si_signo = SIGSEGV;
++ info.si_errno = 0;
++ info.si_code = ILL_BADSTK;
++ info.si_addr = 0;
++ do_trap(32, SIGSEGV, "iret exception", regs, error_code, &info);
++ }
+ }
+ #endif
+
+diff --git a/arch/x86/mm/init.c b/arch/x86/mm/init.c
+index 73ffd55..0cf2a7b 100644
+--- a/arch/x86/mm/init.c
++++ b/arch/x86/mm/init.c
+@@ -149,6 +149,12 @@ unsigned long __init_refok init_memory_mapping(unsigned long start,
+ set_nx();
+ if (nx_enabled)
+ printk(KERN_INFO "NX (Execute Disable) protection: active\n");
++#ifdef CONFIG_X86_32
++ else
++ if (exec_shield)
++ printk(KERN_INFO "Using x86 segment limits to approximate "
++ "NX protection\n");
++#endif
+
+ /* Enable PSE if available */
+ if (cpu_has_pse)
+diff --git a/arch/x86/mm/mmap.c b/arch/x86/mm/mmap.c
+index c8191de..7d84d01 100644
+--- a/arch/x86/mm/mmap.c
++++ b/arch/x86/mm/mmap.c
+@@ -124,13 +124,16 @@ static unsigned long mmap_legacy_base(void)
+ */
+ void arch_pick_mmap_layout(struct mm_struct *mm)
+ {
+- if (mmap_is_legacy()) {
++ if (!(2 & exec_shield) && mmap_is_legacy()) {
+ mm->mmap_base = mmap_legacy_base();
+ mm->get_unmapped_area = arch_get_unmapped_area;
+ mm->unmap_area = arch_unmap_area;
+ } else {
+ mm->mmap_base = mmap_base();
+ mm->get_unmapped_area = arch_get_unmapped_area_topdown;
++ if (!(current->personality & READ_IMPLIES_EXEC)
++ && mmap_is_ia32())
++ mm->get_unmapped_exec_area = arch_get_unmapped_exec_area;
+ mm->unmap_area = arch_unmap_area_topdown;
+ }
+ }
+diff --git a/arch/x86/mm/setup_nx.c b/arch/x86/mm/setup_nx.c
+index 513d8ed..c614a90 100644
+--- a/arch/x86/mm/setup_nx.c
++++ b/arch/x86/mm/setup_nx.c
+@@ -1,3 +1,4 @@
++#include <linux/sched.h>
+ #include <linux/spinlock.h>
+ #include <linux/errno.h>
+ #include <linux/init.h>
+@@ -27,6 +28,9 @@ static int __init noexec_setup(char *str)
+ } else if (!strncmp(str, "off", 3)) {
+ disable_nx = 1;
+ __supported_pte_mask &= ~_PAGE_NX;
++#ifdef CONFIG_X86_32
++ exec_shield = 0;
++#endif
+ }
+ return 0;
+ }
+diff --git a/arch/x86/mm/tlb.c b/arch/x86/mm/tlb.c
+index 36fe08e..3806a45 100644
+--- a/arch/x86/mm/tlb.c
++++ b/arch/x86/mm/tlb.c
+@@ -6,6 +6,7 @@
+ #include <linux/interrupt.h>
+ #include <linux/module.h>
+
++#include <asm/desc.h>
+ #include <asm/tlbflush.h>
+ #include <asm/mmu_context.h>
+ #include <asm/apic.h>
+@@ -130,6 +131,12 @@ void smp_invalidate_interrupt(struct pt_regs *regs)
+ union smp_flush_state *f;
+
+ cpu = smp_processor_id();
++
++#ifdef CONFIG_X86_32
++ if (current->active_mm)
++ load_user_cs_desc(cpu, current->active_mm);
++#endif
++
+ /*
+ * orig_rax contains the negated interrupt vector.
+ * Use that to determine where the sender put the data.
+diff --git a/arch/x86/vdso/vdso32-setup.c b/arch/x86/vdso/vdso32-setup.c
+index 58bc00f..1fdafb5 100644
+--- a/arch/x86/vdso/vdso32-setup.c
++++ b/arch/x86/vdso/vdso32-setup.c
+@@ -331,7 +331,7 @@ int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
+ if (compat)
+ addr = VDSO_HIGH_BASE;
+ else {
+- addr = get_unmapped_area(NULL, 0, PAGE_SIZE, 0, 0);
++ addr = get_unmapped_area_prot(NULL, 0, PAGE_SIZE, 0, 0, 1);
+ if (IS_ERR_VALUE(addr)) {
+ ret = addr;
+ goto up_fail;
+diff --git a/arch/x86/xen/enlighten.c b/arch/x86/xen/enlighten.c
+index 3439616..31e5c6f 100644
+--- a/arch/x86/xen/enlighten.c
++++ b/arch/x86/xen/enlighten.c
+@@ -323,6 +323,24 @@ static void xen_set_ldt(const void *addr, unsigned entries)
+ xen_mc_issue(PARAVIRT_LAZY_CPU);
+ }
+
++#ifdef CONFIG_X86_32
++static void xen_load_user_cs_desc(int cpu, struct mm_struct *mm)
++{
++ void *gdt;
++ xmaddr_t mgdt;
++ u64 descriptor;
++ struct desc_struct user_cs;
++
++ gdt = &get_cpu_gdt_table(cpu)[GDT_ENTRY_DEFAULT_USER_CS];
++ mgdt = virt_to_machine(gdt);
++
++ user_cs = mm->context.user_cs;
++ descriptor = (u64) user_cs.a | ((u64) user_cs.b) << 32;
++
++ HYPERVISOR_update_descriptor(mgdt.maddr, descriptor);
++}
++#endif /*CONFIG_X86_32*/
++
+ static void xen_load_gdt(const struct desc_ptr *dtr)
+ {
+ unsigned long va = dtr->address;
+@@ -949,6 +967,9 @@ static const struct pv_cpu_ops xen_cpu_ops __initdata = {
+
+ .load_tr_desc = paravirt_nop,
+ .set_ldt = xen_set_ldt,
++#ifdef CONFIG_X86_32
++ .load_user_cs_desc = xen_load_user_cs_desc,
++#endif /*CONFIG_X86_32*/
+ .load_gdt = xen_load_gdt,
+ .load_idt = xen_load_idt,
+ .load_tls = xen_load_tls,
+diff --git a/fs/binfmt_elf.c b/fs/binfmt_elf.c
+index b9b3bb5..1e55926 100644
+--- a/fs/binfmt_elf.c
++++ b/fs/binfmt_elf.c
+@@ -73,7 +73,7 @@ static struct linux_binfmt elf_format = {
+ .hasvdso = 1
+ };
+
+-#define BAD_ADDR(x) ((unsigned long)(x) >= TASK_SIZE)
++#define BAD_ADDR(x) IS_ERR_VALUE(x)
+
+ static int set_brk(unsigned long start, unsigned long end)
+ {
+@@ -721,6 +721,11 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
+ break;
+ }
+
++ if (current->personality == PER_LINUX && (exec_shield & 2)) {
++ executable_stack = EXSTACK_DISABLE_X;
++ current->flags |= PF_RANDOMIZE;
++ }
++
+ /* Some simple consistency checks for the interpreter */
+ if (elf_interpreter) {
+ retval = -ELIBBAD;
+@@ -740,6 +745,15 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
+ if (retval)
+ goto out_free_dentry;
+
++#ifdef CONFIG_X86_32
++ /*
++ * Turn off the CS limit completely if exec-shield disabled or
++ * NX active:
++ */
++ if (!exec_shield || executable_stack != EXSTACK_DISABLE_X || nx_enabled)
++ arch_add_exec_range(current->mm, -1);
++#endif
++
+ /* OK, This is the point of no return */
+ current->flags &= ~PF_FORKNOEXEC;
+ current->mm->def_flags = def_flags;
+@@ -747,7 +761,8 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
+ /* Do this immediately, since STACK_TOP as used in setup_arg_pages
+ may depend on the personality. */
+ SET_PERSONALITY(loc->elf_ex);
+- if (elf_read_implies_exec(loc->elf_ex, executable_stack))
++ if (!(exec_shield & 2) &&
++ elf_read_implies_exec(loc->elf_ex, executable_stack))
+ current->personality |= READ_IMPLIES_EXEC;
+
+ if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
+@@ -912,7 +927,7 @@ static int load_elf_binary(struct linux_binprm *bprm, struct pt_regs *regs)
+ interpreter,
+ &interp_map_addr,
+ load_bias);
+- if (!IS_ERR((void *)elf_entry)) {
++ if (!BAD_ADDR(elf_entry)) {
+ /*
+ * load_elf_interp() returns relocation
+ * adjustment
+diff --git a/include/linux/mm.h b/include/linux/mm.h
+index 24c3956..88f944d 100644
+--- a/include/linux/mm.h
++++ b/include/linux/mm.h
+@@ -1129,7 +1129,13 @@ extern int install_special_mapping(struct mm_struct *mm,
+ unsigned long addr, unsigned long len,
+ unsigned long flags, struct page **pages);
+
+-extern unsigned long get_unmapped_area(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
++extern unsigned long get_unmapped_area_prot(struct file *, unsigned long, unsigned long, unsigned long, unsigned long, int);
++
++static inline unsigned long get_unmapped_area(struct file *file, unsigned long addr,
++ unsigned long len, unsigned long pgoff, unsigned long flags)
++{
++ return get_unmapped_area_prot(file, addr, len, pgoff, flags, 0);
++}
+
+ extern unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
+ unsigned long len, unsigned long prot,
+diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h
+index 84a524a..a81e0db 100644
+--- a/include/linux/mm_types.h
++++ b/include/linux/mm_types.h
+@@ -206,6 +206,9 @@ struct mm_struct {
+ unsigned long (*get_unmapped_area) (struct file *filp,
+ unsigned long addr, unsigned long len,
+ unsigned long pgoff, unsigned long flags);
++ unsigned long (*get_unmapped_exec_area) (struct file *filp,
++ unsigned long addr, unsigned long len,
++ unsigned long pgoff, unsigned long flags);
+ void (*unmap_area) (struct mm_struct *mm, unsigned long addr);
+ unsigned long mmap_base; /* base of mmap area */
+ unsigned long task_size; /* size of task vm space */
+diff --git a/include/linux/resource.h b/include/linux/resource.h
+index 40fc7e6..68c2549 100644
+--- a/include/linux/resource.h
++++ b/include/linux/resource.h
+@@ -55,8 +55,11 @@ struct rlimit {
+ /*
+ * Limit the stack by to some sane default: root can always
+ * increase this limit if needed.. 8MB seems reasonable.
++ *
++ * (2MB more to cover randomization effects.)
+ */
+-#define _STK_LIM (8*1024*1024)
++#define _STK_LIM (10*1024*1024)
++#define EXEC_STACK_BIAS (2*1024*1024)
+
+ /*
+ * GPG2 wants 64kB of mlocked memory, to make sure pass phrases
+diff --git a/include/linux/sched.h b/include/linux/sched.h
+index 75e6e60..0bce489 100644
+--- a/include/linux/sched.h
++++ b/include/linux/sched.h
+@@ -102,6 +102,9 @@ struct fs_struct;
+ struct bts_context;
+ struct perf_event_context;
+
++extern int exec_shield;
++extern int print_fatal_signals;
++
+ /*
+ * List of flags we want to share for kernel threads,
+ * if only because they are not used by them anyway.
+@@ -378,6 +381,10 @@ extern int sysctl_max_map_count;
+ extern unsigned long
+ arch_get_unmapped_area(struct file *, unsigned long, unsigned long,
+ unsigned long, unsigned long);
++
++extern unsigned long
++arch_get_unmapped_exec_area(struct file *, unsigned long, unsigned long,
++ unsigned long, unsigned long);
+ extern unsigned long
+ arch_get_unmapped_area_topdown(struct file *filp, unsigned long addr,
+ unsigned long len, unsigned long pgoff,
+diff --git a/kernel/sysctl.c b/kernel/sysctl.c
+index 0d949c5..12ca319 100644
+--- a/kernel/sysctl.c
++++ b/kernel/sysctl.c
+@@ -88,6 +88,26 @@ extern int sysctl_nr_open_min, sysctl_nr_open_max;
+ #ifndef CONFIG_MMU
+ extern int sysctl_nr_trim_pages;
+ #endif
++
++int exec_shield = (1<<0);
++/* exec_shield is a bitmask:
++ * 0: off; vdso at STACK_TOP, 1 page below TASK_SIZE
++ * (1<<0) 1: on [also on if !=0]
++ * (1<<1) 2: force noexecstack regardless of PT_GNU_STACK
++ * The old settings
++ * (1<<2) 4: vdso just below .text of main (unless too low)
++ * (1<<3) 8: vdso just below .text of PT_INTERP (unless too low)
++ * are ignored because the vdso is placed completely randomly
++ */
++
++static int __init setup_exec_shield(char *str)
++{
++ get_option(&str, &exec_shield);
++
++ return 1;
++}
++__setup("exec-shield=", setup_exec_shield);
++
+ #ifdef CONFIG_RCU_TORTURE_TEST
+ extern int rcutorture_runnable;
+ #endif /* #ifdef CONFIG_RCU_TORTURE_TEST */
+@@ -408,6 +428,14 @@ static struct ctl_table kern_table[] = {
+ .proc_handler = &proc_dointvec,
+ },
+ {
++ .ctl_name = CTL_UNNUMBERED,
++ .procname = "exec-shield",
++ .data = &exec_shield,
++ .maxlen = sizeof(int),
++ .mode = 0644,
++ .proc_handler = &proc_dointvec,
++ },
++ {
+ .ctl_name = KERN_CORE_USES_PID,
+ .procname = "core_uses_pid",
+ .data = &core_uses_pid,
+diff --git a/mm/mmap.c b/mm/mmap.c
+index 73f5e4b..814b95f 100644
+--- a/mm/mmap.c
++++ b/mm/mmap.c
+@@ -29,6 +29,7 @@
+ #include <linux/rmap.h>
+ #include <linux/mmu_notifier.h>
+ #include <linux/perf_event.h>
++#include <linux/random.h>
+
+ #include <asm/uaccess.h>
+ #include <asm/cacheflush.h>
+@@ -45,6 +46,18 @@
+ #define arch_rebalance_pgtables(addr, len) (addr)
+ #endif
+
++/* No sane architecture will #define these to anything else */
++#ifndef arch_add_exec_range
++#define arch_add_exec_range(mm, limit) do { ; } while (0)
++#endif
++#ifndef arch_flush_exec_range
++#define arch_flush_exec_range(mm) do { ; } while (0)
++#endif
++#ifndef arch_remove_exec_range
++#define arch_remove_exec_range(mm, limit) do { ; } while (0)
++#endif
++
++
+ static void unmap_region(struct mm_struct *mm,
+ struct vm_area_struct *vma, struct vm_area_struct *prev,
+ unsigned long start, unsigned long end);
+@@ -389,6 +402,8 @@ static inline void
+ __vma_link_list(struct mm_struct *mm, struct vm_area_struct *vma,
+ struct vm_area_struct *prev, struct rb_node *rb_parent)
+ {
++ if (vma->vm_flags & VM_EXEC)
++ arch_add_exec_range(mm, vma->vm_end);
+ if (prev) {
+ vma->vm_next = prev->vm_next;
+ prev->vm_next = vma;
+@@ -491,6 +506,8 @@ __vma_unlink(struct mm_struct *mm, struct vm_area_struct *vma,
+ rb_erase(&vma->vm_rb, &mm->mm_rb);
+ if (mm->mmap_cache == vma)
+ mm->mmap_cache = prev;
++ if (vma->vm_flags & VM_EXEC)
++ arch_remove_exec_range(mm, vma->vm_end);
+ }
+
+ /*
+@@ -798,6 +815,8 @@ struct vm_area_struct *vma_merge(struct mm_struct *mm,
+ } else /* cases 2, 5, 7 */
+ vma_adjust(prev, prev->vm_start,
+ end, prev->vm_pgoff, NULL);
++ if (prev->vm_flags & VM_EXEC)
++ arch_add_exec_range(mm, prev->vm_end);
+ return prev;
+ }
+
+@@ -970,7 +989,8 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
+ /* Obtain the address to map to. we verify (or select) it and ensure
+ * that it represents a valid section of the address space.
+ */
+- addr = get_unmapped_area(file, addr, len, pgoff, flags);
++ addr = get_unmapped_area_prot(file, addr, len, pgoff, flags,
++ prot & PROT_EXEC);
+ if (addr & ~PAGE_MASK)
+ return addr;
+
+@@ -1453,21 +1473,25 @@ void arch_unmap_area_topdown(struct mm_struct *mm, unsigned long addr)
+ }
+
+ unsigned long
+-get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
+- unsigned long pgoff, unsigned long flags)
++get_unmapped_area_prot(struct file *file, unsigned long addr, unsigned long len,
++ unsigned long pgoff, unsigned long flags, int exec)
+ {
+ unsigned long (*get_area)(struct file *, unsigned long,
+ unsigned long, unsigned long, unsigned long);
+
+ unsigned long error = arch_mmap_check(addr, len, flags);
+ if (error)
+ return error;
+
+ /* Careful about overflows.. */
+ if (len > TASK_SIZE)
+ return -ENOMEM;
+
+- get_area = current->mm->get_unmapped_area;
++ if (exec && current->mm->get_unmapped_exec_area)
++ get_area = current->mm->get_unmapped_exec_area;
++ else
++ get_area = current->mm->get_unmapped_area;
++
+ if (file && file->f_op && file->f_op->get_unmapped_area)
+ get_area = file->f_op->get_unmapped_area;
+ addr = get_area(file, addr, len, pgoff, flags);
+@@ -1473,8 +1497,76 @@ get_unmapped_area(struct file *file, unsigned long addr, unsigned long len,
+
+ return arch_rebalance_pgtables(addr, len);
+ }
++EXPORT_SYMBOL(get_unmapped_area_prot);
++
++#define SHLIB_BASE 0x00110000
++
++unsigned long
++arch_get_unmapped_exec_area(struct file *filp, unsigned long addr0,
++ unsigned long len0, unsigned long pgoff, unsigned long flags)
++{
++ unsigned long addr = addr0, len = len0;
++ struct mm_struct *mm = current->mm;
++ struct vm_area_struct *vma;
++ unsigned long tmp;
++
++ if (len > TASK_SIZE)
++ return -ENOMEM;
++
++ if (flags & MAP_FIXED)
++ return addr;
++
++ if (!addr)
++ addr = randomize_range(SHLIB_BASE, 0x01000000, len);
++
++ if (addr) {
++ addr = PAGE_ALIGN(addr);
++ vma = find_vma(mm, addr);
++ if (TASK_SIZE - len >= addr &&
++ (!vma || addr + len <= vma->vm_start))
++ return addr;
++ }
++
++ addr = SHLIB_BASE;
++ for (vma = find_vma(mm, addr); ; vma = vma->vm_next) {
++ /* At this point: (!vma || addr < vma->vm_end). */
++ if (TASK_SIZE - len < addr)
++ return -ENOMEM;
++
++ if (!vma || addr + len <= vma->vm_start) {
++ /*
++ * Must not let a PROT_EXEC mapping get into the
++ * brk area:
++ */
++ if (addr + len > mm->brk)
++ goto failed;
++
++ /*
++ * Up until the brk area we randomize addresses
++ * as much as possible:
++ */
++ if (addr >= 0x01000000) {
++ tmp = randomize_range(0x01000000,
++ PAGE_ALIGN(max(mm->start_brk,
++ (unsigned long)0x08000000)), len);
++ vma = find_vma(mm, tmp);
++ if (TASK_SIZE - len >= tmp &&
++ (!vma || tmp + len <= vma->vm_start))
++ return tmp;
++ }
++ /*
++ * Ok, randomization didnt work out - return
++ * the result of the linear search:
++ */
++ return addr;
++ }
++ addr = vma->vm_end;
++ }
++
++failed:
++ return current->mm->get_unmapped_area(filp, addr0, len0, pgoff, flags);
++}
+
+-EXPORT_SYMBOL(get_unmapped_area);
+
+ /* Look up the first VMA which satisfies addr < vm_end, NULL if none. */
+ struct vm_area_struct *find_vma(struct mm_struct *mm, unsigned long addr)
+@@ -1549,6 +1641,14 @@ out:
+ return prev ? prev->vm_next : vma;
+ }
+
++static int over_stack_limit(unsigned long sz)
++{
++ if (sz < EXEC_STACK_BIAS)
++ return 0;
++ return (sz - EXEC_STACK_BIAS) >
++ current->signal->rlim[RLIMIT_STACK].rlim_cur;
++}
++
+ /*
+ * Verify that the stack growth is acceptable and
+ * update accounting. This is shared with both the
+@@ -1565,7 +1665,7 @@ static int acct_stack_growth(struct vm_area_struct *vma, unsigned long size, uns
+ return -ENOMEM;
+
+ /* Stack limit test */
+- if (size > rlim[RLIMIT_STACK].rlim_cur)
++ if (over_stack_limit(size))
+ return -ENOMEM;
+
+ /* mlock limit tests */
+@@ -1875,10 +1975,14 @@ int split_vma(struct mm_struct * mm, struct vm_area_struct * vma,
+ if (new->vm_ops && new->vm_ops->open)
+ new->vm_ops->open(new);
+
+- if (new_below)
++ if (new_below) {
++ unsigned long old_end = vma->vm_end;
++
+ vma_adjust(vma, addr, vma->vm_end, vma->vm_pgoff +
+ ((addr - new->vm_start) >> PAGE_SHIFT), new);
+- else
++ if (vma->vm_flags & VM_EXEC)
++ arch_remove_exec_range(mm, old_end);
++ } else
+ vma_adjust(vma, vma->vm_start, addr, vma->vm_pgoff, new);
+
+ return 0;
+@@ -2128,6 +2232,7 @@ void exit_mmap(struct mm_struct *mm)
+
+ free_pgtables(tlb, vma, FIRST_USER_ADDRESS, 0);
+ tlb_finish_mmu(tlb, 0, end);
++ arch_flush_exec_range(mm);
+
+ /*
+ * Walk the list again, actually closing and freeing it,
+diff --git a/mm/mprotect.c b/mm/mprotect.c
+index 8bc969d..3c9b4fc 100644
+--- a/mm/mprotect.c
++++ b/mm/mprotect.c
+@@ -26,9 +26,14 @@
+ #include <linux/perf_event.h>
+ #include <asm/uaccess.h>
+ #include <asm/pgtable.h>
++#include <asm/pgalloc.h>
+ #include <asm/cacheflush.h>
+ #include <asm/tlbflush.h>
+
++#ifndef arch_remove_exec_range
++#define arch_remove_exec_range(mm, limit) do { ; } while (0)
++#endif
++
+ #ifndef pgprot_modify
+ static inline pgprot_t pgprot_modify(pgprot_t oldprot, pgprot_t newprot)
+ {
+@@ -139,7 +144,7 @@ mprotect_fixup(struct vm_area_struct *vma, struct vm_area_struct **pprev,
+ struct mm_struct *mm = vma->vm_mm;
+ unsigned long oldflags = vma->vm_flags;
+ long nrpages = (end - start) >> PAGE_SHIFT;
+- unsigned long charged = 0;
++ unsigned long charged = 0, old_end = vma->vm_end;
+ pgoff_t pgoff;
+ int error;
+ int dirty_accountable = 0;
+@@ -204,6 +209,9 @@ success:
+ dirty_accountable = 1;
+ }
+
++ if (oldflags & VM_EXEC)
++ arch_remove_exec_range(current->mm, old_end);
++
+ mmu_notifier_invalidate_range_start(mm, start, end);
+ if (is_vm_hugetlb_page(vma))
+ hugetlb_change_protection(vma, start, end, vma->vm_page_prot);
+diff --git a/mm/mremap.c b/mm/mremap.c
+index 97bff25..17a9fd7 100644
+--- a/mm/mremap.c
++++ b/mm/mremap.c
+@@ -414,10 +414,10 @@ unsigned long do_mremap(unsigned long addr,
+ if (vma->vm_flags & VM_MAYSHARE)
+ map_flags |= MAP_SHARED;
+
+- new_addr = get_unmapped_area(vma->vm_file, 0, new_len,
++ new_addr = get_unmapped_area_prot(vma->vm_file, 0, new_len,
+ vma->vm_pgoff +
+ ((addr - vma->vm_start) >> PAGE_SHIFT),
+- map_flags);
++ map_flags, vma->vm_flags & VM_EXEC);
+ if (new_addr & ~PAGE_MASK) {
+ ret = new_addr;
+ goto out;
diff --git a/linux-2.6-ext4-quota-metadata-reservation.patch b/linux-2.6-ext4-quota-metadata-reservation.patch
new file mode 100644
index 0000000..de000cb
--- /dev/null
+++ b/linux-2.6-ext4-quota-metadata-reservation.patch
@@ -0,0 +1,723 @@
+For bug #608770
+
+Do not speculatively allocate metadata for quota; it's too complex, and we
+usually get it wrong.
+
+Backport of these upstream patches, some of which just play a supporting role.
+
+ -Eric
+
+515f41c33a9d44a964264c9511ad2c869af1fac3 ext4: Ensure zeroout blocks have no dirty metadata
+d21cd8f163ac44b15c465aab7306db931c606908 ext4: Fix potential quota deadlock
+0637c6f4135f592f094207c7c21e7c0fc5557834 ext4: Patch up how we claim metadata blocks for quota purposes
+9d0be50230b333005635967f7ecd4897dbfd181b ext4: Calculate metadata requirements more accurately
+1db913823c0f8360fccbd24ca67eb073966a5ffd ext4: Handle -EDQUOT error on write
+5f634d064c709ea02c3cdaa850a08323a4a4bf28 ext4: Fix quota accounting error with fallocate
+56246f9ae4cfa95b460f9dfbcfb1b772d85db046 quota: use flags interface for dquot alloc/free space
+0e05842bc117ea70ceb979cca798fd026879951b quota: add the option to not fail with EDQUOT in block
+72b8ab9dde211ea518ff27e631b2046ef90c29a2 ext4: don't use quota reservation for speculative metadata
+
+
+Index: linux-2.6.32.noarch/fs/ext4/extents.c
+===================================================================
+--- linux-2.6.32.noarch.orig/fs/ext4/extents.c
++++ linux-2.6.32.noarch/fs/ext4/extents.c
+@@ -296,29 +296,44 @@ static inline int ext4_ext_space_root_id
+ * to allocate @blocks
+ * Worse case is one block per extent
+ */
+-int ext4_ext_calc_metadata_amount(struct inode *inode, int blocks)
++int ext4_ext_calc_metadata_amount(struct inode *inode, sector_t lblock)
+ {
+- int lcap, icap, rcap, leafs, idxs, num;
+- int newextents = blocks;
++ struct ext4_inode_info *ei = EXT4_I(inode);
++ int idxs, num = 0;
+
+- rcap = ext4_ext_space_root_idx(inode, 0);
+- lcap = ext4_ext_space_block(inode, 0);
+- icap = ext4_ext_space_block_idx(inode, 0);
++ idxs = ((inode->i_sb->s_blocksize - sizeof(struct ext4_extent_header))
++ / sizeof(struct ext4_extent_idx));
+
+- /* number of new leaf blocks needed */
+- num = leafs = (newextents + lcap - 1) / lcap;
++ /*
++ * If the new delayed allocation block is contiguous with the
++ * previous da block, it can share index blocks with the
++ * previous block, so we only need to allocate a new index
++ * block every idxs leaf blocks. At ldxs**2 blocks, we need
++ * an additional index block, and at ldxs**3 blocks, yet
++ * another index blocks.
++ */
++ if (ei->i_da_metadata_calc_len &&
++ ei->i_da_metadata_calc_last_lblock+1 == lblock) {
++ if ((ei->i_da_metadata_calc_len % idxs) == 0)
++ num++;
++ if ((ei->i_da_metadata_calc_len % (idxs*idxs)) == 0)
++ num++;
++ if ((ei->i_da_metadata_calc_len % (idxs*idxs*idxs)) == 0) {
++ num++;
++ ei->i_da_metadata_calc_len = 0;
++ } else
++ ei->i_da_metadata_calc_len++;
++ ei->i_da_metadata_calc_last_lblock++;
++ return num;
++ }
+
+ /*
+- * Worse case, we need separate index block(s)
+- * to link all new leaf blocks
++ * In the worst case we need a new set of index blocks at
++ * every level of the inode's extent tree.
+ */
+- idxs = (leafs + icap - 1) / icap;
+- do {
+- num += idxs;
+- idxs = (idxs + icap - 1) / icap;
+- } while (idxs > rcap);
+-
+- return num;
++ ei->i_da_metadata_calc_len = 1;
++ ei->i_da_metadata_calc_last_lblock = lblock;
++ return ext_depth(inode) + 1;
+ }
+
+ static int
+@@ -3029,6 +3044,14 @@ out:
+ return err;
+ }
+
++static void unmap_underlying_metadata_blocks(struct block_device *bdev,
++ sector_t block, int count)
++{
++ int i;
++ for (i = 0; i < count; i++)
++ unmap_underlying_metadata(bdev, block + i);
++}
++
+ static int
+ ext4_ext_handle_uninitialized_extents(handle_t *handle, struct inode *inode,
+ ext4_lblk_t iblock, unsigned int max_blocks,
+@@ -3104,6 +3127,30 @@ out:
+ } else
+ allocated = ret;
+ set_buffer_new(bh_result);
++ /*
++ * if we allocated more blocks than requested
++ * we need to make sure we unmap the extra block
++ * allocated. The actual needed block will get
++ * unmapped later when we find the buffer_head marked
++ * new.
++ */
++ if (allocated > max_blocks) {
++ unmap_underlying_metadata_blocks(inode->i_sb->s_bdev,
++ newblock + max_blocks,
++ allocated - max_blocks);
++ allocated = max_blocks;
++ }
++
++ /*
++ * If we have done fallocate with the offset that is already
++ * delayed allocated, we would have block reservation
++ * and quota reservation done in the delayed write path.
++ * But fallocate would have already updated quota and block
++ * count for this offset. So cancel these reservation
++ */
++ if (flags & EXT4_GET_BLOCKS_UPDATE_RESERVE_SPACE)
++ ext4_da_update_reserve_space(inode, allocated, 0);
++
+ map_out:
+ set_buffer_mapped(bh_result);
+ out1:
+@@ -3333,9 +3380,18 @@ int ext4_ext_get_blocks(handle_t *handle
+ /* previous routine could use block we allocated */
+ newblock = ext_pblock(&newex);
+ allocated = ext4_ext_get_actual_len(&newex);
++ if (allocated > max_blocks)
++ allocated = max_blocks;
+ set_buffer_new(bh_result);
+
+ /*
++ * Update reserved blocks/metadata blocks after successful
++ * block allocation which had been deferred till now.
++ */
++ if (flags & EXT4_GET_BLOCKS_UPDATE_RESERVE_SPACE)
++ ext4_da_update_reserve_space(inode, allocated, 1);
++
++ /*
+ * Cache the extent and update transaction to commit on fdatasync only
+ * when it is _not_ an uninitialized extent.
+ */
+Index: linux-2.6.32.noarch/fs/ext4/inode.c
+===================================================================
+--- linux-2.6.32.noarch.orig/fs/ext4/inode.c
++++ linux-2.6.32.noarch/fs/ext4/inode.c
+@@ -1051,81 +1051,105 @@ qsize_t *ext4_get_reserved_space(struct
+ return &EXT4_I(inode)->i_reserved_quota;
+ }
+ #endif
++
+ /*
+ * Calculate the number of metadata blocks need to reserve
+- * to allocate @blocks for non extent file based file
++ * to allocate a new block at @lblocks for non extent file based file
+ */
+-static int ext4_indirect_calc_metadata_amount(struct inode *inode, int blocks)
++static int ext4_indirect_calc_metadata_amount(struct inode *inode,
++ sector_t lblock)
+ {
+- int icap = EXT4_ADDR_PER_BLOCK(inode->i_sb);
+- int ind_blks, dind_blks, tind_blks;
+-
+- /* number of new indirect blocks needed */
+- ind_blks = (blocks + icap - 1) / icap;
++ struct ext4_inode_info *ei = EXT4_I(inode);
++ int dind_mask = EXT4_ADDR_PER_BLOCK(inode->i_sb) - 1;
++ int blk_bits;
+
+- dind_blks = (ind_blks + icap - 1) / icap;
++ if (lblock < EXT4_NDIR_BLOCKS)
++ return 0;
+
+- tind_blks = 1;
++ lblock -= EXT4_NDIR_BLOCKS;
+
+- return ind_blks + dind_blks + tind_blks;
++ if (ei->i_da_metadata_calc_len &&
++ (lblock & dind_mask) == ei->i_da_metadata_calc_last_lblock) {
++ ei->i_da_metadata_calc_len++;
++ return 0;
++ }
++ ei->i_da_metadata_calc_last_lblock = lblock & dind_mask;
++ ei->i_da_metadata_calc_len = 1;
++ blk_bits = roundup_pow_of_two(lblock + 1);
++ return (blk_bits / EXT4_ADDR_PER_BLOCK_BITS(inode->i_sb)) + 1;
+ }
+
+ /*
+ * Calculate the number of metadata blocks need to reserve
+- * to allocate given number of blocks
++ * to allocate a block located at @lblock
+ */
+-static int ext4_calc_metadata_amount(struct inode *inode, int blocks)
++static int ext4_calc_metadata_amount(struct inode *inode, sector_t lblock)
+ {
+- if (!blocks)
+- return 0;
+-
+ if (EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)
+- return ext4_ext_calc_metadata_amount(inode, blocks);
++ return ext4_ext_calc_metadata_amount(inode, lblock);
+
+- return ext4_indirect_calc_metadata_amount(inode, blocks);
++ return ext4_indirect_calc_metadata_amount(inode, lblock);
+ }
+
+-static void ext4_da_update_reserve_space(struct inode *inode, int used)
++/*
++ * Called with i_data_sem down, which is important since we can call
++ * ext4_discard_preallocations() from here.
++ */
++void ext4_da_update_reserve_space(struct inode *inode,
++ int used, int quota_claim)
+ {
+ struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
+- int total, mdb, mdb_free;
++ struct ext4_inode_info *ei = EXT4_I(inode);
+
+- spin_lock(&EXT4_I(inode)->i_block_reservation_lock);
+- /* recalculate the number of metablocks still need to be reserved */
+- total = EXT4_I(inode)->i_reserved_data_blocks - used;
+- mdb = ext4_calc_metadata_amount(inode, total);
+-
+- /* figure out how many metablocks to release */
+- BUG_ON(mdb > EXT4_I(inode)->i_reserved_meta_blocks);
+- mdb_free = EXT4_I(inode)->i_reserved_meta_blocks - mdb;
+-
+- if (mdb_free) {
+- /* Account for allocated meta_blocks */
+- mdb_free -= EXT4_I(inode)->i_allocated_meta_blocks;
+-
+- /* update fs dirty blocks counter */
+- percpu_counter_sub(&sbi->s_dirtyblocks_counter, mdb_free);
+- EXT4_I(inode)->i_allocated_meta_blocks = 0;
+- EXT4_I(inode)->i_reserved_meta_blocks = mdb;
+- }
+-
+- /* update per-inode reservations */
+- BUG_ON(used > EXT4_I(inode)->i_reserved_data_blocks);
+- EXT4_I(inode)->i_reserved_data_blocks -= used;
++ spin_lock(&ei->i_block_reservation_lock);
++ if (unlikely(used > ei->i_reserved_data_blocks)) {
++ ext4_msg(inode->i_sb, KERN_NOTICE, "%s: ino %lu, used %d "
++ "with only %d reserved data blocks\n",
++ __func__, inode->i_ino, used,
++ ei->i_reserved_data_blocks);
++ WARN_ON(1);
++ used = ei->i_reserved_data_blocks;
++ }
++
++ /* Update per-inode reservations */
++ ei->i_reserved_data_blocks -= used;
++ ei->i_reserved_meta_blocks -= ei->i_allocated_meta_blocks;
++ percpu_counter_sub(&sbi->s_dirtyblocks_counter,
++ used + ei->i_allocated_meta_blocks);
++ ei->i_allocated_meta_blocks = 0;
++
++ if (ei->i_reserved_data_blocks == 0) {
++ /*
++ * We can release all of the reserved metadata blocks
++ * only when we have written all of the delayed
++ * allocation blocks.
++ */
++ percpu_counter_sub(&sbi->s_dirtyblocks_counter,
++ ei->i_reserved_meta_blocks);
++ ei->i_reserved_meta_blocks = 0;
++ ei->i_da_metadata_calc_len = 0;
++ }
+ spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
+
+- /*
+- * free those over-booking quota for metadata blocks
+- */
+- if (mdb_free)
+- vfs_dq_release_reservation_block(inode, mdb_free);
++ /* Update quota subsystem for data blocks */
++ if (quota_claim) {
++ vfs_dq_claim_block(inode, used);
++ } else {
++ /*
++ * We did fallocate with an offset that is already delayed
++ * allocated. So on delayed allocated writeback we should
++ * not re-claim the quota for fallocated blocks.
++ */
++ vfs_dq_release_reservation_block(inode, used);
++ }
+
+ /*
+ * If we have done all the pending block allocations and if
+ * there aren't any writers on the inode, we can discard the
+ * inode's preallocations.
+ */
+- if (!total && (atomic_read(&inode->i_writecount) == 0))
++ if ((ei->i_reserved_data_blocks == 0) &&
++ (atomic_read(&inode->i_writecount) == 0))
+ ext4_discard_preallocations(inode);
+ }
+
+@@ -1317,18 +1341,20 @@ int ext4_get_blocks(handle_t *handle, st
+ */
+ EXT4_I(inode)->i_state &= ~EXT4_STATE_EXT_MIGRATE;
+ }
+- }
+
++ /*
++ * Update reserved blocks/metadata blocks after successful
++ * block allocation which had been deferred till now. We don't
++ * support fallocate for non extent files. So we can update
++ * reserve space here.
++ */
++ if ((retval > 0) &&
++ (flags & EXT4_GET_BLOCKS_UPDATE_RESERVE_SPACE))
++ ext4_da_update_reserve_space(inode, retval, 1);
++ }
+ if (flags & EXT4_GET_BLOCKS_DELALLOC_RESERVE)
+ EXT4_I(inode)->i_delalloc_reserved_flag = 0;
+
+- /*
+- * Update reserved blocks/metadata blocks after successful
+- * block allocation which had been deferred till now.
+- */
+- if ((retval > 0) && (flags & EXT4_GET_BLOCKS_UPDATE_RESERVE_SPACE))
+- ext4_da_update_reserve_space(inode, retval);
+-
+ up_write((&EXT4_I(inode)->i_data_sem));
+ if (retval > 0 && buffer_mapped(bh)) {
+ int ret = check_block_validity(inode, "file system "
+@@ -1834,11 +1860,15 @@ static int ext4_journalled_write_end(str
+ return ret ? ret : copied;
+ }
+
+-static int ext4_da_reserve_space(struct inode *inode, int nrblocks)
++/*
++ * Reserve a single block located at lblock
++ */
++static int ext4_da_reserve_space(struct inode *inode, sector_t lblock)
+ {
+ int retries = 0;
+ struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
+- unsigned long md_needed, mdblocks, total = 0;
++ struct ext4_inode_info *ei = EXT4_I(inode);
++ unsigned long md_needed;
+
+ /*
+ * recalculate the amount of metadata blocks to reserve
+@@ -1846,35 +1876,34 @@ static int ext4_da_reserve_space(struct
+ * worse case is one extent per block
+ */
+ repeat:
+- spin_lock(&EXT4_I(inode)->i_block_reservation_lock);
+- total = EXT4_I(inode)->i_reserved_data_blocks + nrblocks;
+- mdblocks = ext4_calc_metadata_amount(inode, total);
+- BUG_ON(mdblocks < EXT4_I(inode)->i_reserved_meta_blocks);
+-
+- md_needed = mdblocks - EXT4_I(inode)->i_reserved_meta_blocks;
+- total = md_needed + nrblocks;
+- spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
++ spin_lock(&ei->i_block_reservation_lock);
++ md_needed = ext4_calc_metadata_amount(inode, lblock);
++ spin_unlock(&ei->i_block_reservation_lock);
+
+ /*
+- * Make quota reservation here to prevent quota overflow
+- * later. Real quota accounting is done at pages writeout
+- * time.
++ * We will charge metadata quota at writeout time; this saves
++ * us from metadata over-estimation, though we may go over by
++ * a small amount in the end. Here we just reserve for data.
+ */
+- if (vfs_dq_reserve_block(inode, total))
++ if (vfs_dq_reserve_block(inode, 1))
+ return -EDQUOT;
+
+- if (ext4_claim_free_blocks(sbi, total)) {
+- vfs_dq_release_reservation_block(inode, total);
++ /*
++ * We do still charge estimated metadata to the sb though;
++ * we cannot afford to run out of free blocks.
++ */
++ if (ext4_claim_free_blocks(sbi, md_needed + 1)) {
++ vfs_dq_release_reservation_block(inode, 1);
+ if (ext4_should_retry_alloc(inode->i_sb, &retries)) {
+ yield();
+ goto repeat;
+ }
+ return -ENOSPC;
+ }
+- spin_lock(&EXT4_I(inode)->i_block_reservation_lock);
+- EXT4_I(inode)->i_reserved_data_blocks += nrblocks;
+- EXT4_I(inode)->i_reserved_meta_blocks += md_needed;
+- spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
++ spin_lock(&ei->i_block_reservation_lock);
++ ei->i_reserved_data_blocks++;
++ ei->i_reserved_meta_blocks += md_needed;
++ spin_unlock(&ei->i_block_reservation_lock);
+
+ return 0; /* success */
+ }
+@@ -1882,49 +1911,47 @@ repeat:
+ static void ext4_da_release_space(struct inode *inode, int to_free)
+ {
+ struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
+- int total, mdb, mdb_free, release;
++ struct ext4_inode_info *ei = EXT4_I(inode);
+
+ if (!to_free)
+ return; /* Nothing to release, exit */
+
+ spin_lock(&EXT4_I(inode)->i_block_reservation_lock);
+
+- if (!EXT4_I(inode)->i_reserved_data_blocks) {
++ if (unlikely(to_free > ei->i_reserved_data_blocks)) {
+ /*
+- * if there is no reserved blocks, but we try to free some
+- * then the counter is messed up somewhere.
+- * but since this function is called from invalidate
+- * page, it's harmless to return without any action
+- */
+- printk(KERN_INFO "ext4 delalloc try to release %d reserved "
+- "blocks for inode %lu, but there is no reserved "
+- "data blocks\n", to_free, inode->i_ino);
+- spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
+- return;
++ * if there aren't enough reserved blocks, then the
++ * counter is messed up somewhere. Since this
++ * function is called from invalidate page, it's
++ * harmless to return without any action.
++ */
++ ext4_msg(inode->i_sb, KERN_NOTICE, "ext4_da_release_space: "
++ "ino %lu, to_free %d with only %d reserved "
++ "data blocks\n", inode->i_ino, to_free,
++ ei->i_reserved_data_blocks);
++ WARN_ON(1);
++ to_free = ei->i_reserved_data_blocks;
+ }
++ ei->i_reserved_data_blocks -= to_free;
+
+- /* recalculate the number of metablocks still need to be reserved */
+- total = EXT4_I(inode)->i_reserved_data_blocks - to_free;
+- mdb = ext4_calc_metadata_amount(inode, total);
+-
+- /* figure out how many metablocks to release */
+- BUG_ON(mdb > EXT4_I(inode)->i_reserved_meta_blocks);
+- mdb_free = EXT4_I(inode)->i_reserved_meta_blocks - mdb;
+-
+- release = to_free + mdb_free;
+-
+- /* update fs dirty blocks counter for truncate case */
+- percpu_counter_sub(&sbi->s_dirtyblocks_counter, release);
++ if (ei->i_reserved_data_blocks == 0) {
++ /*
++ * We can release all of the reserved metadata blocks
++ * only when we have written all of the delayed
++ * allocation blocks.
++ */
++ percpu_counter_sub(&sbi->s_dirtyblocks_counter,
++ ei->i_reserved_meta_blocks);
++ ei->i_reserved_meta_blocks = 0;
++ ei->i_da_metadata_calc_len = 0;
++ }
+
+- /* update per-inode reservations */
+- BUG_ON(to_free > EXT4_I(inode)->i_reserved_data_blocks);
+- EXT4_I(inode)->i_reserved_data_blocks -= to_free;
++ /* update fs dirty data blocks counter */
++ percpu_counter_sub(&sbi->s_dirtyblocks_counter, to_free);
+
+- BUG_ON(mdb > EXT4_I(inode)->i_reserved_meta_blocks);
+- EXT4_I(inode)->i_reserved_meta_blocks = mdb;
+ spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
+
+- vfs_dq_release_reservation_block(inode, release);
++ vfs_dq_release_reservation_block(inode, to_free);
+ }
+
+ static void ext4_da_page_release_reservation(struct page *page,
+@@ -2530,7 +2557,7 @@ static int ext4_da_get_block_prep(struct
+ * XXX: __block_prepare_write() unmaps passed block,
+ * is it OK?
+ */
+- ret = ext4_da_reserve_space(inode, 1);
++ ret = ext4_da_reserve_space(inode, iblock);
+ if (ret)
+ /* not enough space to reserve */
+ return ret;
+Index: linux-2.6.32.noarch/fs/ext4/mballoc.c
+===================================================================
+--- linux-2.6.32.noarch.orig/fs/ext4/mballoc.c
++++ linux-2.6.32.noarch/fs/ext4/mballoc.c
+@@ -2756,12 +2756,6 @@ ext4_mb_mark_diskspace_used(struct ext4_
+ if (!(ac->ac_flags & EXT4_MB_DELALLOC_RESERVED))
+ /* release all the reserved blocks if non delalloc */
+ percpu_counter_sub(&sbi->s_dirtyblocks_counter, reserv_blks);
+- else {
+- percpu_counter_sub(&sbi->s_dirtyblocks_counter,
+- ac->ac_b_ex.fe_len);
+- /* convert reserved quota blocks to real quota blocks */
+- vfs_dq_claim_block(ac->ac_inode, ac->ac_b_ex.fe_len);
+- }
+
+ if (sbi->s_log_groups_per_flex) {
+ ext4_group_t flex_group = ext4_flex_group(sbi,
+Index: linux-2.6.32.noarch/fs/ext4/ext4.h
+===================================================================
+--- linux-2.6.32.noarch.orig/fs/ext4/ext4.h
++++ linux-2.6.32.noarch/fs/ext4/ext4.h
+@@ -693,6 +693,8 @@ struct ext4_inode_info {
+ unsigned int i_reserved_meta_blocks;
+ unsigned int i_allocated_meta_blocks;
+ unsigned short i_delalloc_reserved_flag;
++ sector_t i_da_metadata_calc_last_lblock;
++ int i_da_metadata_calc_len;
+
+ /* on-disk additional length */
+ __u16 i_extra_isize;
+@@ -1438,6 +1440,8 @@ extern int ext4_block_truncate_page(hand
+ extern int ext4_page_mkwrite(struct vm_area_struct *vma, struct vm_fault *vmf);
+ extern qsize_t *ext4_get_reserved_space(struct inode *inode);
+ extern int flush_aio_dio_completed_IO(struct inode *inode);
++extern void ext4_da_update_reserve_space(struct inode *inode,
++ int used, int quota_claim);
+ /* ioctl.c */
+ extern long ext4_ioctl(struct file *, unsigned int, unsigned long);
+ extern long ext4_compat_ioctl(struct file *, unsigned int, unsigned long);
+Index: linux-2.6.32.noarch/fs/ext4/ext4_extents.h
+===================================================================
+--- linux-2.6.32.noarch.orig/fs/ext4/ext4_extents.h
++++ linux-2.6.32.noarch/fs/ext4/ext4_extents.h
+@@ -225,7 +225,8 @@ static inline void ext4_ext_mark_initial
+ ext->ee_len = cpu_to_le16(ext4_ext_get_actual_len(ext));
+ }
+
+-extern int ext4_ext_calc_metadata_amount(struct inode *inode, int blocks);
++extern int ext4_ext_calc_metadata_amount(struct inode *inode,
++ sector_t lblocks);
+ extern ext4_fsblk_t ext_pblock(struct ext4_extent *ex);
+ extern ext4_fsblk_t idx_pblock(struct ext4_extent_idx *);
+ extern void ext4_ext_store_pblock(struct ext4_extent *, ext4_fsblk_t);
+Index: linux-2.6.32.noarch/fs/ext4/super.c
+===================================================================
+--- linux-2.6.32.noarch.orig/fs/ext4/super.c
++++ linux-2.6.32.noarch/fs/ext4/super.c
+@@ -702,6 +702,7 @@ static struct inode *ext4_alloc_inode(st
+ ei->i_reserved_data_blocks = 0;
+ ei->i_reserved_meta_blocks = 0;
+ ei->i_allocated_meta_blocks = 0;
++ ei->i_da_metadata_calc_len = 0;
+ ei->i_delalloc_reserved_flag = 0;
+ spin_lock_init(&(ei->i_block_reservation_lock));
+ #ifdef CONFIG_QUOTA
+Index: linux-2.6.32.noarch/fs/quota/dquot.c
+===================================================================
+--- linux-2.6.32.noarch.orig/fs/quota/dquot.c
++++ linux-2.6.32.noarch/fs/quota/dquot.c
+@@ -1492,11 +1492,13 @@ static void inode_decr_space(struct inod
+ /*
+ * This operation can block, but only after everything is updated
+ */
+-int __dquot_alloc_space(struct inode *inode, qsize_t number,
+- int warn, int reserve)
++int __dquot_alloc_space(struct inode *inode, qsize_t number, int flags)
+ {
+ int cnt, ret = QUOTA_OK;
+ char warntype[MAXQUOTAS];
++ int warn = flags & DQUOT_SPACE_WARN;
++ int reserve = flags & DQUOT_SPACE_RESERVE;
++ int nofail = flags & DQUOT_SPACE_NOFAIL;
+
+ /*
+ * First test before acquiring mutex - solves deadlocks when we
+@@ -1521,7 +1523,7 @@ int __dquot_alloc_space(struct inode *in
+ if (!inode->i_dquot[cnt])
+ continue;
+ if (check_bdq(inode->i_dquot[cnt], number, warn, warntype+cnt)
+- == NO_QUOTA) {
++ == NO_QUOTA && !nofail) {
+ ret = NO_QUOTA;
+ spin_unlock(&dq_data_lock);
+ goto out_flush_warn;
+@@ -1552,15 +1554,19 @@ out:
+ return ret;
+ }
+
+-int dquot_alloc_space(struct inode *inode, qsize_t number, int warn)
++int dquot_alloc_space(struct inode *inode, qsize_t number, int flags)
+ {
+- return __dquot_alloc_space(inode, number, warn, 0);
++ return __dquot_alloc_space(inode, number, flags);
+ }
+ EXPORT_SYMBOL(dquot_alloc_space);
+
+ int dquot_reserve_space(struct inode *inode, qsize_t number, int warn)
+ {
+- return __dquot_alloc_space(inode, number, warn, 1);
++ int flags = DQUOT_SPACE_RESERVE;
++
++ if (warn)
++ flags |= DQUOT_SPACE_WARN;
++ return __dquot_alloc_space(inode, number, flags);
+ }
+ EXPORT_SYMBOL(dquot_reserve_space);
+
+@@ -1651,10 +1657,11 @@ EXPORT_SYMBOL(dquot_claim_space);
+ /*
+ * This operation can block, but only after everything is updated
+ */
+-int __dquot_free_space(struct inode *inode, qsize_t number, int reserve)
++int __dquot_free_space(struct inode *inode, qsize_t number, int flags)
+ {
+ unsigned int cnt;
+ char warntype[MAXQUOTAS];
++ int reserve = flags & DQUOT_SPACE_RESERVE;
+
+ /* First test before acquiring mutex - solves deadlocks when we
+ * re-enter the quota code and are already holding the mutex */
+@@ -1706,7 +1713,7 @@ EXPORT_SYMBOL(dquot_free_space);
+ */
+ void dquot_release_reserved_space(struct inode *inode, qsize_t number)
+ {
+- __dquot_free_space(inode, number, 1);
++ __dquot_free_space(inode, number, DQUOT_SPACE_RESERVE);
+
+ }
+ EXPORT_SYMBOL(dquot_release_reserved_space);
+Index: linux-2.6.32.noarch/include/linux/quotaops.h
+===================================================================
+--- linux-2.6.32.noarch.orig/include/linux/quotaops.h
++++ linux-2.6.32.noarch/include/linux/quotaops.h
+@@ -14,6 +14,10 @@ static inline struct quota_info *sb_dqop
+ return &sb->s_dquot;
+ }
+
++#define DQUOT_SPACE_WARN 0x1
++#define DQUOT_SPACE_RESERVE 0x2
++#define DQUOT_SPACE_NOFAIL 0x4
++
+ #if defined(CONFIG_QUOTA)
+
+ /*
+@@ -159,7 +163,7 @@ static inline int vfs_dq_prealloc_space_
+ {
+ if (sb_any_quota_active(inode->i_sb)) {
+ /* Used space is updated in alloc_space() */
+- if (inode->i_sb->dq_op->alloc_space(inode, nr, 1) == NO_QUOTA)
++ if (inode->i_sb->dq_op->alloc_space(inode, nr, DQUOT_SPACE_WARN) == NO_QUOTA)
+ return 1;
+ }
+ else
+@@ -187,6 +191,16 @@ static inline int vfs_dq_alloc_space_nod
+ return 0;
+ }
+
++static inline void vfs_dq_alloc_space_nofail(struct inode *inode, qsize_t nr)
++{
++ if (sb_any_quota_active(inode->i_sb)) {
++ /* Used space is updated in alloc_space() */
++ inode->i_sb->dq_op->alloc_space(inode, nr, DQUOT_SPACE_NOFAIL);
++ } else
++ inode_add_bytes(inode, nr);
++ mark_inode_dirty(inode);
++}
++
+ static inline int vfs_dq_alloc_space(struct inode *inode, qsize_t nr)
+ {
+ int ret;
+@@ -382,6 +396,12 @@ static inline int vfs_dq_alloc_space_nod
+ return 0;
+ }
+
++static inline void vfs_dq_alloc_space_nofail(struct inode *inode, qsize_t nr)
++{
++ inode_add_bytes(inode, nr);
++ mark_inode_dirty(inode);
++}
++
+ static inline int vfs_dq_alloc_space(struct inode *inode, qsize_t nr)
+ {
+ vfs_dq_alloc_space_nodirty(inode, nr);
+@@ -433,6 +453,11 @@ static inline int vfs_dq_alloc_block_nod
+ return vfs_dq_alloc_space_nodirty(inode, nr << inode->i_blkbits);
+ }
+
++static inline void vfs_dq_alloc_block_nofail(struct inode *inode, qsize_t nr)
++{
++ vfs_dq_alloc_space_nofail(inode, nr << inode->i_blkbits);
++}
++
+ static inline int vfs_dq_alloc_block(struct inode *inode, qsize_t nr)
+ {
+ return vfs_dq_alloc_space(inode, nr << inode->i_blkbits);
+Index: linux-2.6.32.noarch/fs/ext4/balloc.c
+===================================================================
+--- linux-2.6.32.noarch.orig/fs/ext4/balloc.c
++++ linux-2.6.32.noarch/fs/ext4/balloc.c
+@@ -642,14 +642,15 @@ ext4_fsblk_t ext4_new_meta_blocks(handle
+ ret = ext4_mb_new_blocks(handle, &ar, errp);
+ if (count)
+ *count = ar.len;
+-
+ /*
+- * Account for the allocated meta blocks
++ * Account for the allocated meta blocks. We will never
++ * fail EDQUOT for metdata, but we do account for it.
+ */
+ if (!(*errp) && EXT4_I(inode)->i_delalloc_reserved_flag) {
+ spin_lock(&EXT4_I(inode)->i_block_reservation_lock);
+ EXT4_I(inode)->i_allocated_meta_blocks += ar.len;
+ spin_unlock(&EXT4_I(inode)->i_block_reservation_lock);
++ vfs_dq_alloc_block_nofail(inode, ar.len);
+ }
+ return ret;
+ }
+
diff --git a/linux-2.6-firewire-git-pending.patch b/linux-2.6-firewire-git-pending.patch
new file mode 100644
index 0000000..e05471f
--- /dev/null
+++ b/linux-2.6-firewire-git-pending.patch
@@ -0,0 +1,4 @@
+#
+# Patches under review and/or pending inclusion in the linux1394-git
+# tree (and/or in by the time your read this), which we want...
+#
diff --git a/linux-2.6-firewire-git-update.patch b/linux-2.6-firewire-git-update.patch
new file mode 100644
index 0000000..6858081
--- /dev/null
+++ b/linux-2.6-firewire-git-update.patch
@@ -0,0 +1,3682 @@
+linux1394-2.6.git tree vs. linus v2.6.29-rc3-git1 on 20090130 by jarod
+
+---
+ firewire-git/drivers/firewire/fw-card.c | 68 -
+ firewire-git/drivers/firewire/fw-cdev.c | 1014 +++++++++++++++++--------
+ firewire-git/drivers/firewire/fw-device.c | 43 -
+ firewire-git/drivers/firewire/fw-device.h | 7
+ firewire-git/drivers/firewire/fw-iso.c | 225 ++++-
+ firewire-git/drivers/firewire/fw-ohci.c | 236 ++---
+ firewire-git/drivers/firewire/fw-sbp2.c | 57 -
+ firewire-git/drivers/firewire/fw-topology.c | 28
+ firewire-git/drivers/firewire/fw-topology.h | 19
+ firewire-git/drivers/firewire/fw-transaction.c | 151 +--
+ firewire-git/drivers/firewire/fw-transaction.h | 125 ---
+ include/linux/firewire-cdev.h | 170 +++-
+ 12 files changed, 1359 insertions(+), 784 deletions(-)
+
+diff -Naurp linux-2.6-git/drivers/firewire/fw-card.c firewire-git/drivers/firewire/fw-card.c
+--- linux-2.6-git/drivers/firewire/fw-card.c 2009-01-30 13:39:02.989651512 -0500
++++ firewire-git/drivers/firewire/fw-card.c 2009-01-30 13:35:51.859771884 -0500
+@@ -63,8 +63,7 @@ static int descriptor_count;
+ #define BIB_CMC ((1) << 30)
+ #define BIB_IMC ((1) << 31)
+
+-static u32 *
+-generate_config_rom(struct fw_card *card, size_t *config_rom_length)
++static u32 *generate_config_rom(struct fw_card *card, size_t *config_rom_length)
+ {
+ struct fw_descriptor *desc;
+ static u32 config_rom[256];
+@@ -128,8 +127,7 @@ generate_config_rom(struct fw_card *card
+ return config_rom;
+ }
+
+-static void
+-update_config_roms(void)
++static void update_config_roms(void)
+ {
+ struct fw_card *card;
+ u32 *config_rom;
+@@ -141,8 +139,7 @@ update_config_roms(void)
+ }
+ }
+
+-int
+-fw_core_add_descriptor(struct fw_descriptor *desc)
++int fw_core_add_descriptor(struct fw_descriptor *desc)
+ {
+ size_t i;
+
+@@ -171,8 +168,7 @@ fw_core_add_descriptor(struct fw_descrip
+ return 0;
+ }
+
+-void
+-fw_core_remove_descriptor(struct fw_descriptor *desc)
++void fw_core_remove_descriptor(struct fw_descriptor *desc)
+ {
+ mutex_lock(&card_mutex);
+
+@@ -189,8 +185,7 @@ static const char gap_count_table[] = {
+ 63, 5, 7, 8, 10, 13, 16, 18, 21, 24, 26, 29, 32, 35, 37, 40
+ };
+
+-void
+-fw_schedule_bm_work(struct fw_card *card, unsigned long delay)
++void fw_schedule_bm_work(struct fw_card *card, unsigned long delay)
+ {
+ int scheduled;
+
+@@ -200,8 +195,7 @@ fw_schedule_bm_work(struct fw_card *card
+ fw_card_put(card);
+ }
+
+-static void
+-fw_card_bm_work(struct work_struct *work)
++static void fw_card_bm_work(struct work_struct *work)
+ {
+ struct fw_card *card = container_of(work, struct fw_card, work.work);
+ struct fw_device *root_device;
+@@ -371,17 +365,16 @@ fw_card_bm_work(struct work_struct *work
+ fw_card_put(card);
+ }
+
+-static void
+-flush_timer_callback(unsigned long data)
++static void flush_timer_callback(unsigned long data)
+ {
+ struct fw_card *card = (struct fw_card *)data;
+
+ fw_flush_transactions(card);
+ }
+
+-void
+-fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver,
+- struct device *device)
++void fw_card_initialize(struct fw_card *card,
++ const struct fw_card_driver *driver,
++ struct device *device)
+ {
+ static atomic_t index = ATOMIC_INIT(-1);
+
+@@ -406,9 +399,8 @@ fw_card_initialize(struct fw_card *card,
+ }
+ EXPORT_SYMBOL(fw_card_initialize);
+
+-int
+-fw_card_add(struct fw_card *card,
+- u32 max_receive, u32 link_speed, u64 guid)
++int fw_card_add(struct fw_card *card,
++ u32 max_receive, u32 link_speed, u64 guid)
+ {
+ u32 *config_rom;
+ size_t length;
+@@ -435,23 +427,20 @@ EXPORT_SYMBOL(fw_card_add);
+ * dummy driver just fails all IO.
+ */
+
+-static int
+-dummy_enable(struct fw_card *card, u32 *config_rom, size_t length)
++static int dummy_enable(struct fw_card *card, u32 *config_rom, size_t length)
+ {
+ BUG();
+ return -1;
+ }
+
+-static int
+-dummy_update_phy_reg(struct fw_card *card, int address,
+- int clear_bits, int set_bits)
++static int dummy_update_phy_reg(struct fw_card *card, int address,
++ int clear_bits, int set_bits)
+ {
+ return -ENODEV;
+ }
+
+-static int
+-dummy_set_config_rom(struct fw_card *card,
+- u32 *config_rom, size_t length)
++static int dummy_set_config_rom(struct fw_card *card,
++ u32 *config_rom, size_t length)
+ {
+ /*
+ * We take the card out of card_list before setting the dummy
+@@ -461,27 +450,23 @@ dummy_set_config_rom(struct fw_card *car
+ return -1;
+ }
+
+-static void
+-dummy_send_request(struct fw_card *card, struct fw_packet *packet)
++static void dummy_send_request(struct fw_card *card, struct fw_packet *packet)
+ {
+ packet->callback(packet, card, -ENODEV);
+ }
+
+-static void
+-dummy_send_response(struct fw_card *card, struct fw_packet *packet)
++static void dummy_send_response(struct fw_card *card, struct fw_packet *packet)
+ {
+ packet->callback(packet, card, -ENODEV);
+ }
+
+-static int
+-dummy_cancel_packet(struct fw_card *card, struct fw_packet *packet)
++static int dummy_cancel_packet(struct fw_card *card, struct fw_packet *packet)
+ {
+ return -ENOENT;
+ }
+
+-static int
+-dummy_enable_phys_dma(struct fw_card *card,
+- int node_id, int generation)
++static int dummy_enable_phys_dma(struct fw_card *card,
++ int node_id, int generation)
+ {
+ return -ENODEV;
+ }
+@@ -496,16 +481,14 @@ static struct fw_card_driver dummy_drive
+ .enable_phys_dma = dummy_enable_phys_dma,
+ };
+
+-void
+-fw_card_release(struct kref *kref)
++void fw_card_release(struct kref *kref)
+ {
+ struct fw_card *card = container_of(kref, struct fw_card, kref);
+
+ complete(&card->done);
+ }
+
+-void
+-fw_core_remove_card(struct fw_card *card)
++void fw_core_remove_card(struct fw_card *card)
+ {
+ card->driver->update_phy_reg(card, 4,
+ PHY_LINK_ACTIVE | PHY_CONTENDER, 0);
+@@ -529,8 +512,7 @@ fw_core_remove_card(struct fw_card *card
+ }
+ EXPORT_SYMBOL(fw_core_remove_card);
+
+-int
+-fw_core_initiate_bus_reset(struct fw_card *card, int short_reset)
++int fw_core_initiate_bus_reset(struct fw_card *card, int short_reset)
+ {
+ int reg = short_reset ? 5 : 1;
+ int bit = short_reset ? PHY_BUS_SHORT_RESET : PHY_BUS_RESET;
+diff -Naurp linux-2.6-git/drivers/firewire/fw-cdev.c firewire-git/drivers/firewire/fw-cdev.c
+--- linux-2.6-git/drivers/firewire/fw-cdev.c 2008-11-04 11:19:19.000000000 -0500
++++ firewire-git/drivers/firewire/fw-cdev.c 2009-01-30 13:35:51.860646788 -0500
+@@ -18,87 +18,162 @@
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+-#include <linux/module.h>
+-#include <linux/kernel.h>
+-#include <linux/wait.h>
+-#include <linux/errno.h>
++#include <linux/compat.h>
++#include <linux/delay.h>
+ #include <linux/device.h>
+-#include <linux/vmalloc.h>
++#include <linux/errno.h>
++#include <linux/firewire-cdev.h>
++#include <linux/idr.h>
++#include <linux/jiffies.h>
++#include <linux/kernel.h>
++#include <linux/kref.h>
++#include <linux/mm.h>
++#include <linux/module.h>
++#include <linux/mutex.h>
+ #include <linux/poll.h>
+ #include <linux/preempt.h>
++#include <linux/spinlock.h>
+ #include <linux/time.h>
+-#include <linux/delay.h>
+-#include <linux/mm.h>
+-#include <linux/idr.h>
+-#include <linux/compat.h>
+-#include <linux/firewire-cdev.h>
++#include <linux/vmalloc.h>
++#include <linux/wait.h>
++#include <linux/workqueue.h>
++
+ #include <asm/system.h>
+ #include <asm/uaccess.h>
+-#include "fw-transaction.h"
+-#include "fw-topology.h"
++
+ #include "fw-device.h"
++#include "fw-topology.h"
++#include "fw-transaction.h"
++
++struct client {
++ u32 version;
++ struct fw_device *device;
++
++ spinlock_t lock;
++ bool in_shutdown;
++ struct idr resource_idr;
++ struct list_head event_list;
++ wait_queue_head_t wait;
++ u64 bus_reset_closure;
++
++ struct fw_iso_context *iso_context;
++ u64 iso_closure;
++ struct fw_iso_buffer buffer;
++ unsigned long vm_start;
+
+-struct client;
+-struct client_resource {
+ struct list_head link;
+- void (*release)(struct client *client, struct client_resource *r);
+- u32 handle;
++ struct kref kref;
++};
++
++static inline void client_get(struct client *client)
++{
++ kref_get(&client->kref);
++}
++
++static void client_release(struct kref *kref)
++{
++ struct client *client = container_of(kref, struct client, kref);
++
++ fw_device_put(client->device);
++ kfree(client);
++}
++
++static void client_put(struct client *client)
++{
++ kref_put(&client->kref, client_release);
++}
++
++struct client_resource;
++typedef void (*client_resource_release_fn_t)(struct client *,
++ struct client_resource *);
++struct client_resource {
++ client_resource_release_fn_t release;
++ int handle;
++};
++
++struct address_handler_resource {
++ struct client_resource resource;
++ struct fw_address_handler handler;
++ __u64 closure;
++ struct client *client;
++};
++
++struct outbound_transaction_resource {
++ struct client_resource resource;
++ struct fw_transaction transaction;
++};
++
++struct inbound_transaction_resource {
++ struct client_resource resource;
++ struct fw_request *request;
++ void *data;
++ size_t length;
+ };
+
++struct descriptor_resource {
++ struct client_resource resource;
++ struct fw_descriptor descriptor;
++ u32 data[0];
++};
++
++struct iso_resource {
++ struct client_resource resource;
++ struct client *client;
++ /* Schedule work and access todo only with client->lock held. */
++ struct delayed_work work;
++ enum {ISO_RES_ALLOC, ISO_RES_REALLOC, ISO_RES_DEALLOC,
++ ISO_RES_ALLOC_ONCE, ISO_RES_DEALLOC_ONCE,} todo;
++ int generation;
++ u64 channels;
++ s32 bandwidth;
++ struct iso_resource_event *e_alloc, *e_dealloc;
++};
++
++static void schedule_iso_resource(struct iso_resource *);
++static void release_iso_resource(struct client *, struct client_resource *);
++
+ /*
+ * dequeue_event() just kfree()'s the event, so the event has to be
+- * the first field in the struct.
++ * the first field in a struct XYZ_event.
+ */
+-
+ struct event {
+ struct { void *data; size_t size; } v[2];
+ struct list_head link;
+ };
+
+-struct bus_reset {
++struct bus_reset_event {
+ struct event event;
+ struct fw_cdev_event_bus_reset reset;
+ };
+
+-struct response {
++struct outbound_transaction_event {
+ struct event event;
+- struct fw_transaction transaction;
+ struct client *client;
+- struct client_resource resource;
++ struct outbound_transaction_resource r;
+ struct fw_cdev_event_response response;
+ };
+
+-struct iso_interrupt {
++struct inbound_transaction_event {
+ struct event event;
+- struct fw_cdev_event_iso_interrupt interrupt;
++ struct fw_cdev_event_request request;
+ };
+
+-struct client {
+- u32 version;
+- struct fw_device *device;
+- spinlock_t lock;
+- u32 resource_handle;
+- struct list_head resource_list;
+- struct list_head event_list;
+- wait_queue_head_t wait;
+- u64 bus_reset_closure;
+-
+- struct fw_iso_context *iso_context;
+- u64 iso_closure;
+- struct fw_iso_buffer buffer;
+- unsigned long vm_start;
++struct iso_interrupt_event {
++ struct event event;
++ struct fw_cdev_event_iso_interrupt interrupt;
++};
+
+- struct list_head link;
++struct iso_resource_event {
++ struct event event;
++ struct fw_cdev_event_iso_resource resource;
+ };
+
+-static inline void __user *
+-u64_to_uptr(__u64 value)
++static inline void __user *u64_to_uptr(__u64 value)
+ {
+ return (void __user *)(unsigned long)value;
+ }
+
+-static inline __u64
+-uptr_to_u64(void __user *ptr)
++static inline __u64 uptr_to_u64(void __user *ptr)
+ {
+ return (__u64)(unsigned long)ptr;
+ }
+@@ -107,7 +182,6 @@ static int fw_device_op_open(struct inod
+ {
+ struct fw_device *device;
+ struct client *client;
+- unsigned long flags;
+
+ device = fw_device_get_by_devt(inode->i_rdev);
+ if (device == NULL)
+@@ -125,16 +199,17 @@ static int fw_device_op_open(struct inod
+ }
+
+ client->device = device;
+- INIT_LIST_HEAD(&client->event_list);
+- INIT_LIST_HEAD(&client->resource_list);
+ spin_lock_init(&client->lock);
++ idr_init(&client->resource_idr);
++ INIT_LIST_HEAD(&client->event_list);
+ init_waitqueue_head(&client->wait);
++ kref_init(&client->kref);
+
+ file->private_data = client;
+
+- spin_lock_irqsave(&device->card->lock, flags);
++ mutex_lock(&device->client_list_mutex);
+ list_add_tail(&client->link, &device->client_list);
+- spin_unlock_irqrestore(&device->card->lock, flags);
++ mutex_unlock(&device->client_list_mutex);
+
+ return 0;
+ }
+@@ -150,68 +225,69 @@ static void queue_event(struct client *c
+ event->v[1].size = size1;
+
+ spin_lock_irqsave(&client->lock, flags);
+- list_add_tail(&event->link, &client->event_list);
++ if (client->in_shutdown)
++ kfree(event);
++ else
++ list_add_tail(&event->link, &client->event_list);
+ spin_unlock_irqrestore(&client->lock, flags);
+
+ wake_up_interruptible(&client->wait);
+ }
+
+-static int
+-dequeue_event(struct client *client, char __user *buffer, size_t count)
++static int dequeue_event(struct client *client,
++ char __user *buffer, size_t count)
+ {
+- unsigned long flags;
+ struct event *event;
+ size_t size, total;
+- int i, retval;
++ int i, ret;
+
+- retval = wait_event_interruptible(client->wait,
+- !list_empty(&client->event_list) ||
+- fw_device_is_shutdown(client->device));
+- if (retval < 0)
+- return retval;
++ ret = wait_event_interruptible(client->wait,
++ !list_empty(&client->event_list) ||
++ fw_device_is_shutdown(client->device));
++ if (ret < 0)
++ return ret;
+
+ if (list_empty(&client->event_list) &&
+ fw_device_is_shutdown(client->device))
+ return -ENODEV;
+
+- spin_lock_irqsave(&client->lock, flags);
+- event = container_of(client->event_list.next, struct event, link);
++ spin_lock_irq(&client->lock);
++ event = list_first_entry(&client->event_list, struct event, link);
+ list_del(&event->link);
+- spin_unlock_irqrestore(&client->lock, flags);
++ spin_unlock_irq(&client->lock);
+
+ total = 0;
+ for (i = 0; i < ARRAY_SIZE(event->v) && total < count; i++) {
+ size = min(event->v[i].size, count - total);
+ if (copy_to_user(buffer + total, event->v[i].data, size)) {
+- retval = -EFAULT;
++ ret = -EFAULT;
+ goto out;
+ }
+ total += size;
+ }
+- retval = total;
++ ret = total;
+
+ out:
+ kfree(event);
+
+- return retval;
++ return ret;
+ }
+
+-static ssize_t
+-fw_device_op_read(struct file *file,
+- char __user *buffer, size_t count, loff_t *offset)
++static ssize_t fw_device_op_read(struct file *file, char __user *buffer,
++ size_t count, loff_t *offset)
+ {
+ struct client *client = file->private_data;
+
+ return dequeue_event(client, buffer, count);
+ }
+
+-/* caller must hold card->lock so that node pointers can be dereferenced here */
+-static void
+-fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
+- struct client *client)
++static void fill_bus_reset_event(struct fw_cdev_event_bus_reset *event,
++ struct client *client)
+ {
+ struct fw_card *card = client->device->card;
+
++ spin_lock_irq(&card->lock);
++
+ event->closure = client->bus_reset_closure;
+ event->type = FW_CDEV_EVENT_BUS_RESET;
+ event->generation = client->device->generation;
+@@ -220,39 +296,49 @@ fill_bus_reset_event(struct fw_cdev_even
+ event->bm_node_id = 0; /* FIXME: We don't track the BM. */
+ event->irm_node_id = card->irm_node->node_id;
+ event->root_node_id = card->root_node->node_id;
++
++ spin_unlock_irq(&card->lock);
+ }
+
+-static void
+-for_each_client(struct fw_device *device,
+- void (*callback)(struct client *client))
++static void for_each_client(struct fw_device *device,
++ void (*callback)(struct client *client))
+ {
+- struct fw_card *card = device->card;
+ struct client *c;
+- unsigned long flags;
+-
+- spin_lock_irqsave(&card->lock, flags);
+
++ mutex_lock(&device->client_list_mutex);
+ list_for_each_entry(c, &device->client_list, link)
+ callback(c);
++ mutex_unlock(&device->client_list_mutex);
++}
++
++static int schedule_reallocations(int id, void *p, void *data)
++{
++ struct client_resource *r = p;
+
+- spin_unlock_irqrestore(&card->lock, flags);
++ if (r->release == release_iso_resource)
++ schedule_iso_resource(container_of(r,
++ struct iso_resource, resource));
++ return 0;
+ }
+
+-static void
+-queue_bus_reset_event(struct client *client)
++static void queue_bus_reset_event(struct client *client)
+ {
+- struct bus_reset *bus_reset;
++ struct bus_reset_event *e;
+
+- bus_reset = kzalloc(sizeof(*bus_reset), GFP_ATOMIC);
+- if (bus_reset == NULL) {
++ e = kzalloc(sizeof(*e), GFP_KERNEL);
++ if (e == NULL) {
+ fw_notify("Out of memory when allocating bus reset event\n");
+ return;
+ }
+
+- fill_bus_reset_event(&bus_reset->reset, client);
++ fill_bus_reset_event(&e->reset, client);
++
++ queue_event(client, &e->event,
++ &e->reset, sizeof(e->reset), NULL, 0);
+
+- queue_event(client, &bus_reset->event,
+- &bus_reset->reset, sizeof(bus_reset->reset), NULL, 0);
++ spin_lock_irq(&client->lock);
++ idr_for_each(&client->resource_idr, schedule_reallocations, client);
++ spin_unlock_irq(&client->lock);
+ }
+
+ void fw_device_cdev_update(struct fw_device *device)
+@@ -274,11 +360,11 @@ static int ioctl_get_info(struct client
+ {
+ struct fw_cdev_get_info *get_info = buffer;
+ struct fw_cdev_event_bus_reset bus_reset;
+- struct fw_card *card = client->device->card;
+ unsigned long ret = 0;
+
+ client->version = get_info->version;
+ get_info->version = FW_CDEV_VERSION;
++ get_info->card = client->device->card->index;
+
+ down_read(&fw_device_rwsem);
+
+@@ -300,49 +386,61 @@ static int ioctl_get_info(struct client
+ client->bus_reset_closure = get_info->bus_reset_closure;
+ if (get_info->bus_reset != 0) {
+ void __user *uptr = u64_to_uptr(get_info->bus_reset);
+- unsigned long flags;
+
+- spin_lock_irqsave(&card->lock, flags);
+ fill_bus_reset_event(&bus_reset, client);
+- spin_unlock_irqrestore(&card->lock, flags);
+-
+ if (copy_to_user(uptr, &bus_reset, sizeof(bus_reset)))
+ return -EFAULT;
+ }
+
+- get_info->card = card->index;
+-
+ return 0;
+ }
+
+-static void
+-add_client_resource(struct client *client, struct client_resource *resource)
++static int add_client_resource(struct client *client,
++ struct client_resource *resource, gfp_t gfp_mask)
+ {
+ unsigned long flags;
++ int ret;
++
++ retry:
++ if (idr_pre_get(&client->resource_idr, gfp_mask | __GFP_ZERO) == 0)
++ return -ENOMEM;
+
+ spin_lock_irqsave(&client->lock, flags);
+- list_add_tail(&resource->link, &client->resource_list);
+- resource->handle = client->resource_handle++;
++ if (client->in_shutdown)
++ ret = -ECANCELED;
++ else
++ ret = idr_get_new(&client->resource_idr, resource,
++ &resource->handle);
++ if (ret >= 0) {
++ client_get(client);
++ if (resource->release == release_iso_resource)
++ schedule_iso_resource(container_of(resource,
++ struct iso_resource, resource));
++ }
+ spin_unlock_irqrestore(&client->lock, flags);
++
++ if (ret == -EAGAIN)
++ goto retry;
++
++ return ret < 0 ? ret : 0;
+ }
+
+-static int
+-release_client_resource(struct client *client, u32 handle,
+- struct client_resource **resource)
++static int release_client_resource(struct client *client, u32 handle,
++ client_resource_release_fn_t release,
++ struct client_resource **resource)
+ {
+ struct client_resource *r;
+- unsigned long flags;
+
+- spin_lock_irqsave(&client->lock, flags);
+- list_for_each_entry(r, &client->resource_list, link) {
+- if (r->handle == handle) {
+- list_del(&r->link);
+- break;
+- }
+- }
+- spin_unlock_irqrestore(&client->lock, flags);
++ spin_lock_irq(&client->lock);
++ if (client->in_shutdown)
++ r = NULL;
++ else
++ r = idr_find(&client->resource_idr, handle);
++ if (r && r->release == release)
++ idr_remove(&client->resource_idr, handle);
++ spin_unlock_irq(&client->lock);
+
+- if (&r->link == &client->resource_list)
++ if (!(r && r->release == release))
+ return -EINVAL;
+
+ if (resource)
+@@ -350,203 +448,242 @@ release_client_resource(struct client *c
+ else
+ r->release(client, r);
+
++ client_put(client);
++
+ return 0;
+ }
+
+-static void
+-release_transaction(struct client *client, struct client_resource *resource)
++static void release_transaction(struct client *client,
++ struct client_resource *resource)
+ {
+- struct response *response =
+- container_of(resource, struct response, resource);
++ struct outbound_transaction_resource *r = container_of(resource,
++ struct outbound_transaction_resource, resource);
+
+- fw_cancel_transaction(client->device->card, &response->transaction);
++ fw_cancel_transaction(client->device->card, &r->transaction);
+ }
+
+-static void
+-complete_transaction(struct fw_card *card, int rcode,
+- void *payload, size_t length, void *data)
++static void complete_transaction(struct fw_card *card, int rcode,
++ void *payload, size_t length, void *data)
+ {
+- struct response *response = data;
+- struct client *client = response->client;
++ struct outbound_transaction_event *e = data;
++ struct fw_cdev_event_response *rsp = &e->response;
++ struct client *client = e->client;
+ unsigned long flags;
+- struct fw_cdev_event_response *r = &response->response;
+
+- if (length < r->length)
+- r->length = length;
++ if (length < rsp->length)
++ rsp->length = length;
+ if (rcode == RCODE_COMPLETE)
+- memcpy(r->data, payload, r->length);
++ memcpy(rsp->data, payload, rsp->length);
+
+ spin_lock_irqsave(&client->lock, flags);
+- list_del(&response->resource.link);
++ /*
++ * 1. If called while in shutdown, the idr tree must be left untouched.
++ * The idr handle will be removed and the client reference will be
++ * dropped later.
++ * 2. If the call chain was release_client_resource ->
++ * release_transaction -> complete_transaction (instead of a normal
++ * conclusion of the transaction), i.e. if this resource was already
++ * unregistered from the idr, the client reference will be dropped
++ * by release_client_resource and we must not drop it here.
++ */
++ if (!client->in_shutdown &&
++ idr_find(&client->resource_idr, e->r.resource.handle)) {
++ idr_remove(&client->resource_idr, e->r.resource.handle);
++ /* Drop the idr's reference */
++ client_put(client);
++ }
+ spin_unlock_irqrestore(&client->lock, flags);
+
+- r->type = FW_CDEV_EVENT_RESPONSE;
+- r->rcode = rcode;
++ rsp->type = FW_CDEV_EVENT_RESPONSE;
++ rsp->rcode = rcode;
+
+ /*
+- * In the case that sizeof(*r) doesn't align with the position of the
++ * In the case that sizeof(*rsp) doesn't align with the position of the
+ * data, and the read is short, preserve an extra copy of the data
+ * to stay compatible with a pre-2.6.27 bug. Since the bug is harmless
+ * for short reads and some apps depended on it, this is both safe
+ * and prudent for compatibility.
+ */
+- if (r->length <= sizeof(*r) - offsetof(typeof(*r), data))
+- queue_event(client, &response->event, r, sizeof(*r),
+- r->data, r->length);
++ if (rsp->length <= sizeof(*rsp) - offsetof(typeof(*rsp), data))
++ queue_event(client, &e->event, rsp, sizeof(*rsp),
++ rsp->data, rsp->length);
+ else
+- queue_event(client, &response->event, r, sizeof(*r) + r->length,
++ queue_event(client, &e->event, rsp, sizeof(*rsp) + rsp->length,
+ NULL, 0);
++
++ /* Drop the transaction callback's reference */
++ client_put(client);
+ }
+
+-static int ioctl_send_request(struct client *client, void *buffer)
++static int init_request(struct client *client,
++ struct fw_cdev_send_request *request,
++ int destination_id, int speed)
+ {
+- struct fw_device *device = client->device;
+- struct fw_cdev_send_request *request = buffer;
+- struct response *response;
++ struct outbound_transaction_event *e;
++ int ret;
+
+- /* What is the biggest size we'll accept, really? */
+- if (request->length > 4096)
+- return -EINVAL;
++ if (request->length > 4096 || request->length > 512 << speed)
++ return -EIO;
+
+- response = kmalloc(sizeof(*response) + request->length, GFP_KERNEL);
+- if (response == NULL)
++ e = kmalloc(sizeof(*e) + request->length, GFP_KERNEL);
++ if (e == NULL)
+ return -ENOMEM;
+
+- response->client = client;
+- response->response.length = request->length;
+- response->response.closure = request->closure;
++ e->client = client;
++ e->response.length = request->length;
++ e->response.closure = request->closure;
+
+ if (request->data &&
+- copy_from_user(response->response.data,
++ copy_from_user(e->response.data,
+ u64_to_uptr(request->data), request->length)) {
+- kfree(response);
+- return -EFAULT;
++ ret = -EFAULT;
++ goto failed;
+ }
+
+- response->resource.release = release_transaction;
+- add_client_resource(client, &response->resource);
+-
+- fw_send_request(device->card, &response->transaction,
+- request->tcode & 0x1f,
+- device->node->node_id,
+- request->generation,
+- device->max_speed,
+- request->offset,
+- response->response.data, request->length,
+- complete_transaction, response);
++ e->r.resource.release = release_transaction;
++ ret = add_client_resource(client, &e->r.resource, GFP_KERNEL);
++ if (ret < 0)
++ goto failed;
++
++ /* Get a reference for the transaction callback */
++ client_get(client);
++
++ fw_send_request(client->device->card, &e->r.transaction,
++ request->tcode & 0x1f, destination_id,
++ request->generation, speed, request->offset,
++ e->response.data, request->length,
++ complete_transaction, e);
+
+ if (request->data)
+ return sizeof(request) + request->length;
+ else
+ return sizeof(request);
++ failed:
++ kfree(e);
++
++ return ret;
+ }
+
+-struct address_handler {
+- struct fw_address_handler handler;
+- __u64 closure;
+- struct client *client;
+- struct client_resource resource;
+-};
++static int ioctl_send_request(struct client *client, void *buffer)
++{
++ struct fw_cdev_send_request *request = buffer;
+
+-struct request {
+- struct fw_request *request;
+- void *data;
+- size_t length;
+- struct client_resource resource;
+-};
++ switch (request->tcode) {
++ case TCODE_WRITE_QUADLET_REQUEST:
++ case TCODE_WRITE_BLOCK_REQUEST:
++ case TCODE_READ_QUADLET_REQUEST:
++ case TCODE_READ_BLOCK_REQUEST:
++ case TCODE_LOCK_MASK_SWAP:
++ case TCODE_LOCK_COMPARE_SWAP:
++ case TCODE_LOCK_FETCH_ADD:
++ case TCODE_LOCK_LITTLE_ADD:
++ case TCODE_LOCK_BOUNDED_ADD:
++ case TCODE_LOCK_WRAP_ADD:
++ case TCODE_LOCK_VENDOR_DEPENDENT:
++ break;
++ default:
++ return -EINVAL;
++ }
+
+-struct request_event {
+- struct event event;
+- struct fw_cdev_event_request request;
+-};
++ return init_request(client, request, client->device->node->node_id,
++ client->device->max_speed);
++}
+
+-static void
+-release_request(struct client *client, struct client_resource *resource)
++static void release_request(struct client *client,
++ struct client_resource *resource)
+ {
+- struct request *request =
+- container_of(resource, struct request, resource);
++ struct inbound_transaction_resource *r = container_of(resource,
++ struct inbound_transaction_resource, resource);
+
+- fw_send_response(client->device->card, request->request,
++ fw_send_response(client->device->card, r->request,
+ RCODE_CONFLICT_ERROR);
+- kfree(request);
++ kfree(r);
+ }
+
+-static void
+-handle_request(struct fw_card *card, struct fw_request *r,
+- int tcode, int destination, int source,
+- int generation, int speed,
+- unsigned long long offset,
+- void *payload, size_t length, void *callback_data)
+-{
+- struct address_handler *handler = callback_data;
+- struct request *request;
+- struct request_event *e;
+- struct client *client = handler->client;
++static void handle_request(struct fw_card *card, struct fw_request *request,
++ int tcode, int destination, int source,
++ int generation, int speed,
++ unsigned long long offset,
++ void *payload, size_t length, void *callback_data)
++{
++ struct address_handler_resource *handler = callback_data;
++ struct inbound_transaction_resource *r;
++ struct inbound_transaction_event *e;
++ int ret;
+
+- request = kmalloc(sizeof(*request), GFP_ATOMIC);
++ r = kmalloc(sizeof(*r), GFP_ATOMIC);
+ e = kmalloc(sizeof(*e), GFP_ATOMIC);
+- if (request == NULL || e == NULL) {
+- kfree(request);
+- kfree(e);
+- fw_send_response(card, r, RCODE_CONFLICT_ERROR);
+- return;
+- }
+-
+- request->request = r;
+- request->data = payload;
+- request->length = length;
++ if (r == NULL || e == NULL)
++ goto failed;
+
+- request->resource.release = release_request;
+- add_client_resource(client, &request->resource);
++ r->request = request;
++ r->data = payload;
++ r->length = length;
++
++ r->resource.release = release_request;
++ ret = add_client_resource(handler->client, &r->resource, GFP_ATOMIC);
++ if (ret < 0)
++ goto failed;
+
+ e->request.type = FW_CDEV_EVENT_REQUEST;
+ e->request.tcode = tcode;
+ e->request.offset = offset;
+ e->request.length = length;
+- e->request.handle = request->resource.handle;
++ e->request.handle = r->resource.handle;
+ e->request.closure = handler->closure;
+
+- queue_event(client, &e->event,
++ queue_event(handler->client, &e->event,
+ &e->request, sizeof(e->request), payload, length);
++ return;
++
++ failed:
++ kfree(r);
++ kfree(e);
++ fw_send_response(card, request, RCODE_CONFLICT_ERROR);
+ }
+
+-static void
+-release_address_handler(struct client *client,
+- struct client_resource *resource)
++static void release_address_handler(struct client *client,
++ struct client_resource *resource)
+ {
+- struct address_handler *handler =
+- container_of(resource, struct address_handler, resource);
++ struct address_handler_resource *r =
++ container_of(resource, struct address_handler_resource, resource);
+
+- fw_core_remove_address_handler(&handler->handler);
+- kfree(handler);
++ fw_core_remove_address_handler(&r->handler);
++ kfree(r);
+ }
+
+ static int ioctl_allocate(struct client *client, void *buffer)
+ {
+ struct fw_cdev_allocate *request = buffer;
+- struct address_handler *handler;
++ struct address_handler_resource *r;
+ struct fw_address_region region;
++ int ret;
+
+- handler = kmalloc(sizeof(*handler), GFP_KERNEL);
+- if (handler == NULL)
++ r = kmalloc(sizeof(*r), GFP_KERNEL);
++ if (r == NULL)
+ return -ENOMEM;
+
+ region.start = request->offset;
+ region.end = request->offset + request->length;
+- handler->handler.length = request->length;
+- handler->handler.address_callback = handle_request;
+- handler->handler.callback_data = handler;
+- handler->closure = request->closure;
+- handler->client = client;
+-
+- if (fw_core_add_address_handler(&handler->handler, &region) < 0) {
+- kfree(handler);
+- return -EBUSY;
++ r->handler.length = request->length;
++ r->handler.address_callback = handle_request;
++ r->handler.callback_data = r;
++ r->closure = request->closure;
++ r->client = client;
++
++ ret = fw_core_add_address_handler(&r->handler, &region);
++ if (ret < 0) {
++ kfree(r);
++ return ret;
+ }
+
+- handler->resource.release = release_address_handler;
+- add_client_resource(client, &handler->resource);
+- request->handle = handler->resource.handle;
++ r->resource.release = release_address_handler;
++ ret = add_client_resource(client, &r->resource, GFP_KERNEL);
++ if (ret < 0) {
++ release_address_handler(client, &r->resource);
++ return ret;
++ }
++ request->handle = r->resource.handle;
+
+ return 0;
+ }
+@@ -555,18 +692,22 @@ static int ioctl_deallocate(struct clien
+ {
+ struct fw_cdev_deallocate *request = buffer;
+
+- return release_client_resource(client, request->handle, NULL);
++ return release_client_resource(client, request->handle,
++ release_address_handler, NULL);
+ }
+
+ static int ioctl_send_response(struct client *client, void *buffer)
+ {
+ struct fw_cdev_send_response *request = buffer;
+ struct client_resource *resource;
+- struct request *r;
++ struct inbound_transaction_resource *r;
+
+- if (release_client_resource(client, request->handle, &resource) < 0)
++ if (release_client_resource(client, request->handle,
++ release_request, &resource) < 0)
+ return -EINVAL;
+- r = container_of(resource, struct request, resource);
++
++ r = container_of(resource, struct inbound_transaction_resource,
++ resource);
+ if (request->length < r->length)
+ r->length = request->length;
+ if (copy_from_user(r->data, u64_to_uptr(request->data), r->length))
+@@ -588,85 +729,84 @@ static int ioctl_initiate_bus_reset(stru
+ return fw_core_initiate_bus_reset(client->device->card, short_reset);
+ }
+
+-struct descriptor {
+- struct fw_descriptor d;
+- struct client_resource resource;
+- u32 data[0];
+-};
+-
+ static void release_descriptor(struct client *client,
+ struct client_resource *resource)
+ {
+- struct descriptor *descriptor =
+- container_of(resource, struct descriptor, resource);
++ struct descriptor_resource *r =
++ container_of(resource, struct descriptor_resource, resource);
+
+- fw_core_remove_descriptor(&descriptor->d);
+- kfree(descriptor);
++ fw_core_remove_descriptor(&r->descriptor);
++ kfree(r);
+ }
+
+ static int ioctl_add_descriptor(struct client *client, void *buffer)
+ {
+ struct fw_cdev_add_descriptor *request = buffer;
+- struct descriptor *descriptor;
+- int retval;
++ struct descriptor_resource *r;
++ int ret;
+
+ if (request->length > 256)
+ return -EINVAL;
+
+- descriptor =
+- kmalloc(sizeof(*descriptor) + request->length * 4, GFP_KERNEL);
+- if (descriptor == NULL)
++ r = kmalloc(sizeof(*r) + request->length * 4, GFP_KERNEL);
++ if (r == NULL)
+ return -ENOMEM;
+
+- if (copy_from_user(descriptor->data,
++ if (copy_from_user(r->data,
+ u64_to_uptr(request->data), request->length * 4)) {
+- kfree(descriptor);
+- return -EFAULT;
++ ret = -EFAULT;
++ goto failed;
+ }
+
+- descriptor->d.length = request->length;
+- descriptor->d.immediate = request->immediate;
+- descriptor->d.key = request->key;
+- descriptor->d.data = descriptor->data;
+-
+- retval = fw_core_add_descriptor(&descriptor->d);
+- if (retval < 0) {
+- kfree(descriptor);
+- return retval;
++ r->descriptor.length = request->length;
++ r->descriptor.immediate = request->immediate;
++ r->descriptor.key = request->key;
++ r->descriptor.data = r->data;
++
++ ret = fw_core_add_descriptor(&r->descriptor);
++ if (ret < 0)
++ goto failed;
++
++ r->resource.release = release_descriptor;
++ ret = add_client_resource(client, &r->resource, GFP_KERNEL);
++ if (ret < 0) {
++ fw_core_remove_descriptor(&r->descriptor);
++ goto failed;
+ }
+-
+- descriptor->resource.release = release_descriptor;
+- add_client_resource(client, &descriptor->resource);
+- request->handle = descriptor->resource.handle;
++ request->handle = r->resource.handle;
+
+ return 0;
++ failed:
++ kfree(r);
++
++ return ret;
+ }
+
+ static int ioctl_remove_descriptor(struct client *client, void *buffer)
+ {
+ struct fw_cdev_remove_descriptor *request = buffer;
+
+- return release_client_resource(client, request->handle, NULL);
++ return release_client_resource(client, request->handle,
++ release_descriptor, NULL);
+ }
+
+-static void
+-iso_callback(struct fw_iso_context *context, u32 cycle,
+- size_t header_length, void *header, void *data)
++static void iso_callback(struct fw_iso_context *context, u32 cycle,
++ size_t header_length, void *header, void *data)
+ {
+ struct client *client = data;
+- struct iso_interrupt *irq;
++ struct iso_interrupt_event *e;
+
+- irq = kzalloc(sizeof(*irq) + header_length, GFP_ATOMIC);
+- if (irq == NULL)
++ e = kzalloc(sizeof(*e) + header_length, GFP_ATOMIC);
++ if (e == NULL)
+ return;
+
+- irq->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT;
+- irq->interrupt.closure = client->iso_closure;
+- irq->interrupt.cycle = cycle;
+- irq->interrupt.header_length = header_length;
+- memcpy(irq->interrupt.header, header, header_length);
+- queue_event(client, &irq->event, &irq->interrupt,
+- sizeof(irq->interrupt) + header_length, NULL, 0);
++ e->interrupt.type = FW_CDEV_EVENT_ISO_INTERRUPT;
++ e->interrupt.closure = client->iso_closure;
++ e->interrupt.cycle = cycle;
++ e->interrupt.header_length = header_length;
++ memcpy(e->interrupt.header, header, header_length);
++ queue_event(client, &e->event, &e->interrupt,
++ sizeof(e->interrupt) + header_length, NULL, 0);
+ }
+
+ static int ioctl_create_iso_context(struct client *client, void *buffer)
+@@ -871,6 +1011,237 @@ static int ioctl_get_cycle_timer(struct
+ return 0;
+ }
+
++static void iso_resource_work(struct work_struct *work)
++{
++ struct iso_resource_event *e;
++ struct iso_resource *r =
++ container_of(work, struct iso_resource, work.work);
++ struct client *client = r->client;
++ int generation, channel, bandwidth, todo;
++ bool skip, free, success;
++
++ spin_lock_irq(&client->lock);
++ generation = client->device->generation;
++ todo = r->todo;
++ /* Allow 1000ms grace period for other reallocations. */
++ if (todo == ISO_RES_ALLOC &&
++ time_is_after_jiffies(client->device->card->reset_jiffies + HZ)) {
++ if (schedule_delayed_work(&r->work, DIV_ROUND_UP(HZ, 3)))
++ client_get(client);
++ skip = true;
++ } else {
++ /* We could be called twice within the same generation. */
++ skip = todo == ISO_RES_REALLOC &&
++ r->generation == generation;
++ }
++ free = todo == ISO_RES_DEALLOC ||
++ todo == ISO_RES_ALLOC_ONCE ||
++ todo == ISO_RES_DEALLOC_ONCE;
++ r->generation = generation;
++ spin_unlock_irq(&client->lock);
++
++ if (skip)
++ goto out;
++
++ bandwidth = r->bandwidth;
++
++ fw_iso_resource_manage(client->device->card, generation,
++ r->channels, &channel, &bandwidth,
++ todo == ISO_RES_ALLOC ||
++ todo == ISO_RES_REALLOC ||
++ todo == ISO_RES_ALLOC_ONCE);
++ /*
++ * Is this generation outdated already? As long as this resource sticks
++ * in the idr, it will be scheduled again for a newer generation or at
++ * shutdown.
++ */
++ if (channel == -EAGAIN &&
++ (todo == ISO_RES_ALLOC || todo == ISO_RES_REALLOC))
++ goto out;
++
++ success = channel >= 0 || bandwidth > 0;
++
++ spin_lock_irq(&client->lock);
++ /*
++ * Transit from allocation to reallocation, except if the client
++ * requested deallocation in the meantime.
++ */
++ if (r->todo == ISO_RES_ALLOC)
++ r->todo = ISO_RES_REALLOC;
++ /*
++ * Allocation or reallocation failure? Pull this resource out of the
++ * idr and prepare for deletion, unless the client is shutting down.
++ */
++ if (r->todo == ISO_RES_REALLOC && !success &&
++ !client->in_shutdown &&
++ idr_find(&client->resource_idr, r->resource.handle)) {
++ idr_remove(&client->resource_idr, r->resource.handle);
++ client_put(client);
++ free = true;
++ }
++ spin_unlock_irq(&client->lock);
++
++ if (todo == ISO_RES_ALLOC && channel >= 0)
++ r->channels = 1ULL << channel;
++
++ if (todo == ISO_RES_REALLOC && success)
++ goto out;
++
++ if (todo == ISO_RES_ALLOC || todo == ISO_RES_ALLOC_ONCE) {
++ e = r->e_alloc;
++ r->e_alloc = NULL;
++ } else {
++ e = r->e_dealloc;
++ r->e_dealloc = NULL;
++ }
++ e->resource.handle = r->resource.handle;
++ e->resource.channel = channel;
++ e->resource.bandwidth = bandwidth;
++
++ queue_event(client, &e->event,
++ &e->resource, sizeof(e->resource), NULL, 0);
++
++ if (free) {
++ cancel_delayed_work(&r->work);
++ kfree(r->e_alloc);
++ kfree(r->e_dealloc);
++ kfree(r);
++ }
++ out:
++ client_put(client);
++}
++
++static void schedule_iso_resource(struct iso_resource *r)
++{
++ client_get(r->client);
++ if (!schedule_delayed_work(&r->work, 0))
++ client_put(r->client);
++}
++
++static void release_iso_resource(struct client *client,
++ struct client_resource *resource)
++{
++ struct iso_resource *r =
++ container_of(resource, struct iso_resource, resource);
++
++ spin_lock_irq(&client->lock);
++ r->todo = ISO_RES_DEALLOC;
++ schedule_iso_resource(r);
++ spin_unlock_irq(&client->lock);
++}
++
++static int init_iso_resource(struct client *client,
++ struct fw_cdev_allocate_iso_resource *request, int todo)
++{
++ struct iso_resource_event *e1, *e2;
++ struct iso_resource *r;
++ int ret;
++
++ if ((request->channels == 0 && request->bandwidth == 0) ||
++ request->bandwidth > BANDWIDTH_AVAILABLE_INITIAL ||
++ request->bandwidth < 0)
++ return -EINVAL;
++
++ r = kmalloc(sizeof(*r), GFP_KERNEL);
++ e1 = kmalloc(sizeof(*e1), GFP_KERNEL);
++ e2 = kmalloc(sizeof(*e2), GFP_KERNEL);
++ if (r == NULL || e1 == NULL || e2 == NULL) {
++ ret = -ENOMEM;
++ goto fail;
++ }
++
++ INIT_DELAYED_WORK(&r->work, iso_resource_work);
++ r->client = client;
++ r->todo = todo;
++ r->generation = -1;
++ r->channels = request->channels;
++ r->bandwidth = request->bandwidth;
++ r->e_alloc = e1;
++ r->e_dealloc = e2;
++
++ e1->resource.closure = request->closure;
++ e1->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED;
++ e2->resource.closure = request->closure;
++ e2->resource.type = FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED;
++
++ if (todo == ISO_RES_ALLOC) {
++ r->resource.release = release_iso_resource;
++ ret = add_client_resource(client, &r->resource, GFP_KERNEL);
++ if (ret < 0)
++ goto fail;
++ } else {
++ r->resource.release = NULL;
++ r->resource.handle = -1;
++ schedule_iso_resource(r);
++ }
++ request->handle = r->resource.handle;
++
++ return 0;
++ fail:
++ kfree(r);
++ kfree(e1);
++ kfree(e2);
++
++ return ret;
++}
++
++static int ioctl_allocate_iso_resource(struct client *client, void *buffer)
++{
++ struct fw_cdev_allocate_iso_resource *request = buffer;
++
++ return init_iso_resource(client, request, ISO_RES_ALLOC);
++}
++
++static int ioctl_deallocate_iso_resource(struct client *client, void *buffer)
++{
++ struct fw_cdev_deallocate *request = buffer;
++
++ return release_client_resource(client, request->handle,
++ release_iso_resource, NULL);
++}
++
++static int ioctl_allocate_iso_resource_once(struct client *client, void *buffer)
++{
++ struct fw_cdev_allocate_iso_resource *request = buffer;
++
++ return init_iso_resource(client, request, ISO_RES_ALLOC_ONCE);
++}
++
++static int ioctl_deallocate_iso_resource_once(struct client *client, void *buffer)
++{
++ struct fw_cdev_allocate_iso_resource *request = buffer;
++
++ return init_iso_resource(client, request, ISO_RES_DEALLOC_ONCE);
++}
++
++static int ioctl_get_speed(struct client *client, void *buffer)
++{
++ struct fw_cdev_get_speed *request = buffer;
++
++ request->max_speed = client->device->max_speed;
++
++ return 0;
++}
++
++static int ioctl_send_broadcast_request(struct client *client, void *buffer)
++{
++ struct fw_cdev_send_request *request = buffer;
++
++ switch (request->tcode) {
++ case TCODE_WRITE_QUADLET_REQUEST:
++ case TCODE_WRITE_BLOCK_REQUEST:
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* Security policy: Only allow accesses to Units Space. */
++ if (request->offset < CSR_REGISTER_BASE + CSR_CONFIG_ROM_END)
++ return -EACCES;
++
++ return init_request(client, request, LOCAL_BUS | 0x3f, SCODE_100);
++}
++
+ static int (* const ioctl_handlers[])(struct client *client, void *buffer) = {
+ ioctl_get_info,
+ ioctl_send_request,
+@@ -885,13 +1256,19 @@ static int (* const ioctl_handlers[])(st
+ ioctl_start_iso,
+ ioctl_stop_iso,
+ ioctl_get_cycle_timer,
++ ioctl_allocate_iso_resource,
++ ioctl_deallocate_iso_resource,
++ ioctl_allocate_iso_resource_once,
++ ioctl_deallocate_iso_resource_once,
++ ioctl_get_speed,
++ ioctl_send_broadcast_request,
+ };
+
+-static int
+-dispatch_ioctl(struct client *client, unsigned int cmd, void __user *arg)
++static int dispatch_ioctl(struct client *client,
++ unsigned int cmd, void __user *arg)
+ {
+ char buffer[256];
+- int retval;
++ int ret;
+
+ if (_IOC_TYPE(cmd) != '#' ||
+ _IOC_NR(cmd) >= ARRAY_SIZE(ioctl_handlers))
+@@ -903,9 +1280,9 @@ dispatch_ioctl(struct client *client, un
+ return -EFAULT;
+ }
+
+- retval = ioctl_handlers[_IOC_NR(cmd)](client, buffer);
+- if (retval < 0)
+- return retval;
++ ret = ioctl_handlers[_IOC_NR(cmd)](client, buffer);
++ if (ret < 0)
++ return ret;
+
+ if (_IOC_DIR(cmd) & _IOC_READ) {
+ if (_IOC_SIZE(cmd) > sizeof(buffer) ||
+@@ -913,12 +1290,11 @@ dispatch_ioctl(struct client *client, un
+ return -EFAULT;
+ }
+
+- return retval;
++ return ret;
+ }
+
+-static long
+-fw_device_op_ioctl(struct file *file,
+- unsigned int cmd, unsigned long arg)
++static long fw_device_op_ioctl(struct file *file,
++ unsigned int cmd, unsigned long arg)
+ {
+ struct client *client = file->private_data;
+
+@@ -929,9 +1305,8 @@ fw_device_op_ioctl(struct file *file,
+ }
+
+ #ifdef CONFIG_COMPAT
+-static long
+-fw_device_op_compat_ioctl(struct file *file,
+- unsigned int cmd, unsigned long arg)
++static long fw_device_op_compat_ioctl(struct file *file,
++ unsigned int cmd, unsigned long arg)
+ {
+ struct client *client = file->private_data;
+
+@@ -947,7 +1322,7 @@ static int fw_device_op_mmap(struct file
+ struct client *client = file->private_data;
+ enum dma_data_direction direction;
+ unsigned long size;
+- int page_count, retval;
++ int page_count, ret;
+
+ if (fw_device_is_shutdown(client->device))
+ return -ENODEV;
+@@ -973,48 +1348,57 @@ static int fw_device_op_mmap(struct file
+ else
+ direction = DMA_FROM_DEVICE;
+
+- retval = fw_iso_buffer_init(&client->buffer, client->device->card,
+- page_count, direction);
+- if (retval < 0)
+- return retval;
++ ret = fw_iso_buffer_init(&client->buffer, client->device->card,
++ page_count, direction);
++ if (ret < 0)
++ return ret;
+
+- retval = fw_iso_buffer_map(&client->buffer, vma);
+- if (retval < 0)
++ ret = fw_iso_buffer_map(&client->buffer, vma);
++ if (ret < 0)
+ fw_iso_buffer_destroy(&client->buffer, client->device->card);
+
+- return retval;
++ return ret;
++}
++
++static int shutdown_resource(int id, void *p, void *data)
++{
++ struct client_resource *r = p;
++ struct client *client = data;
++
++ r->release(client, r);
++ client_put(client);
++
++ return 0;
+ }
+
+ static int fw_device_op_release(struct inode *inode, struct file *file)
+ {
+ struct client *client = file->private_data;
+ struct event *e, *next_e;
+- struct client_resource *r, *next_r;
+- unsigned long flags;
+
+- if (client->buffer.pages)
+- fw_iso_buffer_destroy(&client->buffer, client->device->card);
++ mutex_lock(&client->device->client_list_mutex);
++ list_del(&client->link);
++ mutex_unlock(&client->device->client_list_mutex);
+
+ if (client->iso_context)
+ fw_iso_context_destroy(client->iso_context);
+
+- list_for_each_entry_safe(r, next_r, &client->resource_list, link)
+- r->release(client, r);
++ if (client->buffer.pages)
++ fw_iso_buffer_destroy(&client->buffer, client->device->card);
+
+- /*
+- * FIXME: We should wait for the async tasklets to stop
+- * running before freeing the memory.
+- */
++ /* Freeze client->resource_idr and client->event_list */
++ spin_lock_irq(&client->lock);
++ client->in_shutdown = true;
++ spin_unlock_irq(&client->lock);
++
++ idr_for_each(&client->resource_idr, shutdown_resource, client);
++ idr_remove_all(&client->resource_idr);
++ idr_destroy(&client->resource_idr);
+
+ list_for_each_entry_safe(e, next_e, &client->event_list, link)
+ kfree(e);
+
+- spin_lock_irqsave(&client->device->card->lock, flags);
+- list_del(&client->link);
+- spin_unlock_irqrestore(&client->device->card->lock, flags);
+-
+- fw_device_put(client->device);
+- kfree(client);
++ client_put(client);
+
+ return 0;
+ }
+diff -Naurp linux-2.6-git/drivers/firewire/fw-device.c firewire-git/drivers/firewire/fw-device.c
+--- linux-2.6-git/drivers/firewire/fw-device.c 2009-01-30 13:39:02.989651512 -0500
++++ firewire-git/drivers/firewire/fw-device.c 2009-01-30 13:35:51.860646788 -0500
+@@ -27,8 +27,10 @@
+ #include <linux/idr.h>
+ #include <linux/jiffies.h>
+ #include <linux/string.h>
++#include <linux/mutex.h>
+ #include <linux/rwsem.h>
+ #include <linux/semaphore.h>
++#include <linux/spinlock.h>
+ #include <asm/system.h>
+ #include <linux/ctype.h>
+ #include "fw-transaction.h"
+@@ -132,8 +134,7 @@ static int get_modalias(struct fw_unit *
+ vendor, model, specifier_id, version);
+ }
+
+-static int
+-fw_unit_uevent(struct device *dev, struct kobj_uevent_env *env)
++static int fw_unit_uevent(struct device *dev, struct kobj_uevent_env *env)
+ {
+ struct fw_unit *unit = fw_unit(dev);
+ char modalias[64];
+@@ -191,8 +192,8 @@ struct config_rom_attribute {
+ u32 key;
+ };
+
+-static ssize_t
+-show_immediate(struct device *dev, struct device_attribute *dattr, char *buf)
++static ssize_t show_immediate(struct device *dev,
++ struct device_attribute *dattr, char *buf)
+ {
+ struct config_rom_attribute *attr =
+ container_of(dattr, struct config_rom_attribute, attr);
+@@ -223,8 +224,8 @@ show_immediate(struct device *dev, struc
+ #define IMMEDIATE_ATTR(name, key) \
+ { __ATTR(name, S_IRUGO, show_immediate, NULL), key }
+
+-static ssize_t
+-show_text_leaf(struct device *dev, struct device_attribute *dattr, char *buf)
++static ssize_t show_text_leaf(struct device *dev,
++ struct device_attribute *dattr, char *buf)
+ {
+ struct config_rom_attribute *attr =
+ container_of(dattr, struct config_rom_attribute, attr);
+@@ -293,10 +294,9 @@ static struct config_rom_attribute confi
+ TEXT_LEAF_ATTR(hardware_version_name, CSR_HARDWARE_VERSION),
+ };
+
+-static void
+-init_fw_attribute_group(struct device *dev,
+- struct device_attribute *attrs,
+- struct fw_attribute_group *group)
++static void init_fw_attribute_group(struct device *dev,
++ struct device_attribute *attrs,
++ struct fw_attribute_group *group)
+ {
+ struct device_attribute *attr;
+ int i, j;
+@@ -319,9 +319,8 @@ init_fw_attribute_group(struct device *d
+ dev->groups = group->groups;
+ }
+
+-static ssize_t
+-modalias_show(struct device *dev,
+- struct device_attribute *attr, char *buf)
++static ssize_t modalias_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
+ {
+ struct fw_unit *unit = fw_unit(dev);
+ int length;
+@@ -332,9 +331,8 @@ modalias_show(struct device *dev,
+ return length + 1;
+ }
+
+-static ssize_t
+-rom_index_show(struct device *dev,
+- struct device_attribute *attr, char *buf)
++static ssize_t rom_index_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
+ {
+ struct fw_device *device = fw_device(dev->parent);
+ struct fw_unit *unit = fw_unit(dev);
+@@ -349,8 +347,8 @@ static struct device_attribute fw_unit_a
+ __ATTR_NULL,
+ };
+
+-static ssize_t
+-config_rom_show(struct device *dev, struct device_attribute *attr, char *buf)
++static ssize_t config_rom_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
+ {
+ struct fw_device *device = fw_device(dev);
+ size_t length;
+@@ -363,8 +361,8 @@ config_rom_show(struct device *dev, stru
+ return length;
+ }
+
+-static ssize_t
+-guid_show(struct device *dev, struct device_attribute *attr, char *buf)
++static ssize_t guid_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
+ {
+ struct fw_device *device = fw_device(dev);
+ int ret;
+@@ -383,8 +381,8 @@ static struct device_attribute fw_device
+ __ATTR_NULL,
+ };
+
+-static int
+-read_rom(struct fw_device *device, int generation, int index, u32 *data)
++static int read_rom(struct fw_device *device,
++ int generation, int index, u32 *data)
+ {
+ int rcode;
+
+@@ -1004,6 +1002,7 @@ void fw_node_event(struct fw_card *card,
+ device->node = fw_node_get(node);
+ device->node_id = node->node_id;
+ device->generation = card->generation;
++ mutex_init(&device->client_list_mutex);
+ INIT_LIST_HEAD(&device->client_list);
+
+ /*
+diff -Naurp linux-2.6-git/drivers/firewire/fw-device.h firewire-git/drivers/firewire/fw-device.h
+--- linux-2.6-git/drivers/firewire/fw-device.h 2009-01-30 13:39:02.989651512 -0500
++++ firewire-git/drivers/firewire/fw-device.h 2009-01-30 13:35:51.860646788 -0500
+@@ -23,6 +23,7 @@
+ #include <linux/cdev.h>
+ #include <linux/idr.h>
+ #include <linux/rwsem.h>
++#include <linux/mutex.h>
+ #include <asm/atomic.h>
+
+ enum fw_device_state {
+@@ -64,7 +65,10 @@ struct fw_device {
+ bool cmc;
+ struct fw_card *card;
+ struct device device;
++
++ struct mutex client_list_mutex;
+ struct list_head client_list;
++
+ u32 *config_rom;
+ size_t config_rom_length;
+ int config_rom_retries;
+@@ -176,8 +180,7 @@ struct fw_driver {
+ const struct fw_device_id *id_table;
+ };
+
+-static inline struct fw_driver *
+-fw_driver(struct device_driver *drv)
++static inline struct fw_driver *fw_driver(struct device_driver *drv)
+ {
+ return container_of(drv, struct fw_driver, driver);
+ }
+diff -Naurp linux-2.6-git/drivers/firewire/fw-iso.c firewire-git/drivers/firewire/fw-iso.c
+--- linux-2.6-git/drivers/firewire/fw-iso.c 2008-11-04 11:18:33.000000000 -0500
++++ firewire-git/drivers/firewire/fw-iso.c 2009-01-30 13:35:51.860646788 -0500
+@@ -1,5 +1,7 @@
+ /*
+- * Isochronous IO functionality
++ * Isochronous I/O functionality:
++ * - Isochronous DMA context management
++ * - Isochronous bus resource management (channels, bandwidth), client side
+ *
+ * Copyright (C) 2006 Kristian Hoegsberg <krh@bitplanet.net>
+ *
+@@ -18,21 +20,25 @@
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+-#include <linux/kernel.h>
+-#include <linux/module.h>
+ #include <linux/dma-mapping.h>
+-#include <linux/vmalloc.h>
++#include <linux/errno.h>
++#include <linux/firewire-constants.h>
++#include <linux/kernel.h>
+ #include <linux/mm.h>
++#include <linux/spinlock.h>
++#include <linux/vmalloc.h>
+
+-#include "fw-transaction.h"
+ #include "fw-topology.h"
+-#include "fw-device.h"
++#include "fw-transaction.h"
+
+-int
+-fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,
+- int page_count, enum dma_data_direction direction)
++/*
++ * Isochronous DMA context management
++ */
++
++int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,
++ int page_count, enum dma_data_direction direction)
+ {
+- int i, j, retval = -ENOMEM;
++ int i, j;
+ dma_addr_t address;
+
+ buffer->page_count = page_count;
+@@ -69,19 +75,19 @@ fw_iso_buffer_init(struct fw_iso_buffer
+ kfree(buffer->pages);
+ out:
+ buffer->pages = NULL;
+- return retval;
++ return -ENOMEM;
+ }
+
+ int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma)
+ {
+ unsigned long uaddr;
+- int i, retval;
++ int i, ret;
+
+ uaddr = vma->vm_start;
+ for (i = 0; i < buffer->page_count; i++) {
+- retval = vm_insert_page(vma, uaddr, buffer->pages[i]);
+- if (retval)
+- return retval;
++ ret = vm_insert_page(vma, uaddr, buffer->pages[i]);
++ if (ret)
++ return ret;
+ uaddr += PAGE_SIZE;
+ }
+
+@@ -105,14 +111,14 @@ void fw_iso_buffer_destroy(struct fw_iso
+ buffer->pages = NULL;
+ }
+
+-struct fw_iso_context *
+-fw_iso_context_create(struct fw_card *card, int type,
+- int channel, int speed, size_t header_size,
+- fw_iso_callback_t callback, void *callback_data)
++struct fw_iso_context *fw_iso_context_create(struct fw_card *card,
++ int type, int channel, int speed, size_t header_size,
++ fw_iso_callback_t callback, void *callback_data)
+ {
+ struct fw_iso_context *ctx;
+
+- ctx = card->driver->allocate_iso_context(card, type, header_size);
++ ctx = card->driver->allocate_iso_context(card,
++ type, channel, header_size);
+ if (IS_ERR(ctx))
+ return ctx;
+
+@@ -134,25 +140,186 @@ void fw_iso_context_destroy(struct fw_is
+ card->driver->free_iso_context(ctx);
+ }
+
+-int
+-fw_iso_context_start(struct fw_iso_context *ctx, int cycle, int sync, int tags)
++int fw_iso_context_start(struct fw_iso_context *ctx,
++ int cycle, int sync, int tags)
+ {
+ return ctx->card->driver->start_iso(ctx, cycle, sync, tags);
+ }
+
+-int
+-fw_iso_context_queue(struct fw_iso_context *ctx,
+- struct fw_iso_packet *packet,
+- struct fw_iso_buffer *buffer,
+- unsigned long payload)
++int fw_iso_context_queue(struct fw_iso_context *ctx,
++ struct fw_iso_packet *packet,
++ struct fw_iso_buffer *buffer,
++ unsigned long payload)
+ {
+ struct fw_card *card = ctx->card;
+
+ return card->driver->queue_iso(ctx, packet, buffer, payload);
+ }
+
+-int
+-fw_iso_context_stop(struct fw_iso_context *ctx)
++int fw_iso_context_stop(struct fw_iso_context *ctx)
+ {
+ return ctx->card->driver->stop_iso(ctx);
+ }
++
++/*
++ * Isochronous bus resource management (channels, bandwidth), client side
++ */
++
++static int manage_bandwidth(struct fw_card *card, int irm_id, int generation,
++ int bandwidth, bool allocate)
++{
++ __be32 data[2];
++ int try, new, old = allocate ? BANDWIDTH_AVAILABLE_INITIAL : 0;
++
++ /*
++ * On a 1394a IRM with low contention, try < 1 is enough.
++ * On a 1394-1995 IRM, we need at least try < 2.
++ * Let's just do try < 5.
++ */
++ for (try = 0; try < 5; try++) {
++ new = allocate ? old - bandwidth : old + bandwidth;
++ if (new < 0 || new > BANDWIDTH_AVAILABLE_INITIAL)
++ break;
++
++ data[0] = cpu_to_be32(old);
++ data[1] = cpu_to_be32(new);
++ switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP,
++ irm_id, generation, SCODE_100,
++ CSR_REGISTER_BASE + CSR_BANDWIDTH_AVAILABLE,
++ data, sizeof(data))) {
++ case RCODE_GENERATION:
++ /* A generation change frees all bandwidth. */
++ return allocate ? -EAGAIN : bandwidth;
++
++ case RCODE_COMPLETE:
++ if (be32_to_cpup(data) == old)
++ return bandwidth;
++
++ old = be32_to_cpup(data);
++ /* Fall through. */
++ }
++ }
++
++ return -EIO;
++}
++
++static int manage_channel(struct fw_card *card, int irm_id, int generation,
++ u32 channels_mask, u64 offset, bool allocate)
++{
++ __be32 data[2], c, all, old;
++ int i, retry = 5;
++
++ old = all = allocate ? cpu_to_be32(~0) : 0;
++
++ for (i = 0; i < 32; i++) {
++ if (!(channels_mask & 1 << i))
++ continue;
++
++ c = cpu_to_be32(1 << (31 - i));
++ if ((old & c) != (all & c))
++ continue;
++
++ data[0] = old;
++ data[1] = old ^ c;
++ switch (fw_run_transaction(card, TCODE_LOCK_COMPARE_SWAP,
++ irm_id, generation, SCODE_100,
++ offset, data, sizeof(data))) {
++ case RCODE_GENERATION:
++ /* A generation change frees all channels. */
++ return allocate ? -EAGAIN : i;
++
++ case RCODE_COMPLETE:
++ if (data[0] == old)
++ return i;
++
++ old = data[0];
++
++ /* Is the IRM 1394a-2000 compliant? */
++ if ((data[0] & c) == (data[1] & c))
++ continue;
++
++ /* 1394-1995 IRM, fall through to retry. */
++ default:
++ if (retry--)
++ i--;
++ }
++ }
++
++ return -EIO;
++}
++
++static void deallocate_channel(struct fw_card *card, int irm_id,
++ int generation, int channel)
++{
++ u32 mask;
++ u64 offset;
++
++ mask = channel < 32 ? 1 << channel : 1 << (channel - 32);
++ offset = channel < 32 ? CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI :
++ CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO;
++
++ manage_channel(card, irm_id, generation, mask, offset, false);
++}
++
++/**
++ * fw_iso_resource_manage - Allocate or deallocate a channel and/or bandwidth
++ *
++ * In parameters: card, generation, channels_mask, bandwidth, allocate
++ * Out parameters: channel, bandwidth
++ * This function blocks (sleeps) during communication with the IRM.
++ *
++ * Allocates or deallocates at most one channel out of channels_mask.
++ * channels_mask is a bitfield with MSB for channel 63 and LSB for channel 0.
++ * (Note, the IRM's CHANNELS_AVAILABLE is a big-endian bitfield with MSB for
++ * channel 0 and LSB for channel 63.)
++ * Allocates or deallocates as many bandwidth allocation units as specified.
++ *
++ * Returns channel < 0 if no channel was allocated or deallocated.
++ * Returns bandwidth = 0 if no bandwidth was allocated or deallocated.
++ *
++ * If generation is stale, deallocations succeed but allocations fail with
++ * channel = -EAGAIN.
++ *
++ * If channel allocation fails, no bandwidth will be allocated either.
++ * If bandwidth allocation fails, no channel will be allocated either.
++ * But deallocations of channel and bandwidth are tried independently
++ * of each other's success.
++ */
++void fw_iso_resource_manage(struct fw_card *card, int generation,
++ u64 channels_mask, int *channel, int *bandwidth,
++ bool allocate)
++{
++ u32 channels_hi = channels_mask; /* channels 31...0 */
++ u32 channels_lo = channels_mask >> 32; /* channels 63...32 */
++ int irm_id, ret, c = -EINVAL;
++
++ spin_lock_irq(&card->lock);
++ irm_id = card->irm_node->node_id;
++ spin_unlock_irq(&card->lock);
++
++ if (channels_hi)
++ c = manage_channel(card, irm_id, generation, channels_hi,
++ CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_HI, allocate);
++ if (channels_lo && c < 0) {
++ c = manage_channel(card, irm_id, generation, channels_lo,
++ CSR_REGISTER_BASE + CSR_CHANNELS_AVAILABLE_LO, allocate);
++ if (c >= 0)
++ c += 32;
++ }
++ *channel = c;
++
++ if (allocate && channels_mask != 0 && c < 0)
++ *bandwidth = 0;
++
++ if (*bandwidth == 0)
++ return;
++
++ ret = manage_bandwidth(card, irm_id, generation, *bandwidth, allocate);
++ if (ret < 0)
++ *bandwidth = 0;
++
++ if (allocate && ret < 0 && c >= 0) {
++ deallocate_channel(card, irm_id, generation, c);
++ *channel = ret;
++ }
++}
+diff -Naurp linux-2.6-git/drivers/firewire/fw-ohci.c firewire-git/drivers/firewire/fw-ohci.c
+--- linux-2.6-git/drivers/firewire/fw-ohci.c 2009-01-30 13:39:02.990772025 -0500
++++ firewire-git/drivers/firewire/fw-ohci.c 2009-01-30 13:35:51.861646907 -0500
+@@ -205,6 +205,7 @@ struct fw_ohci {
+
+ u32 it_context_mask;
+ struct iso_context *it_context_list;
++ u64 ir_context_channels;
+ u32 ir_context_mask;
+ struct iso_context *ir_context_list;
+ };
+@@ -441,9 +442,8 @@ static inline void flush_writes(const st
+ reg_read(ohci, OHCI1394_Version);
+ }
+
+-static int
+-ohci_update_phy_reg(struct fw_card *card, int addr,
+- int clear_bits, int set_bits)
++static int ohci_update_phy_reg(struct fw_card *card, int addr,
++ int clear_bits, int set_bits)
+ {
+ struct fw_ohci *ohci = fw_ohci(card);
+ u32 val, old;
+@@ -658,8 +658,8 @@ static void ar_context_tasklet(unsigned
+ }
+ }
+
+-static int
+-ar_context_init(struct ar_context *ctx, struct fw_ohci *ohci, u32 regs)
++static int ar_context_init(struct ar_context *ctx,
++ struct fw_ohci *ohci, u32 regs)
+ {
+ struct ar_buffer ab;
+
+@@ -690,8 +690,7 @@ static void ar_context_run(struct ar_con
+ flush_writes(ctx->ohci);
+ }
+
+-static struct descriptor *
+-find_branch_descriptor(struct descriptor *d, int z)
++static struct descriptor *find_branch_descriptor(struct descriptor *d, int z)
+ {
+ int b, key;
+
+@@ -751,8 +750,7 @@ static void context_tasklet(unsigned lon
+ * Allocate a new buffer and add it to the list of free buffers for this
+ * context. Must be called with ohci->lock held.
+ */
+-static int
+-context_add_buffer(struct context *ctx)
++static int context_add_buffer(struct context *ctx)
+ {
+ struct descriptor_buffer *desc;
+ dma_addr_t uninitialized_var(bus_addr);
+@@ -781,9 +779,8 @@ context_add_buffer(struct context *ctx)
+ return 0;
+ }
+
+-static int
+-context_init(struct context *ctx, struct fw_ohci *ohci,
+- u32 regs, descriptor_callback_t callback)
++static int context_init(struct context *ctx, struct fw_ohci *ohci,
++ u32 regs, descriptor_callback_t callback)
+ {
+ ctx->ohci = ohci;
+ ctx->regs = regs;
+@@ -814,8 +811,7 @@ context_init(struct context *ctx, struct
+ return 0;
+ }
+
+-static void
+-context_release(struct context *ctx)
++static void context_release(struct context *ctx)
+ {
+ struct fw_card *card = &ctx->ohci->card;
+ struct descriptor_buffer *desc, *tmp;
+@@ -827,8 +823,8 @@ context_release(struct context *ctx)
+ }
+
+ /* Must be called with ohci->lock held */
+-static struct descriptor *
+-context_get_descriptors(struct context *ctx, int z, dma_addr_t *d_bus)
++static struct descriptor *context_get_descriptors(struct context *ctx,
++ int z, dma_addr_t *d_bus)
+ {
+ struct descriptor *d = NULL;
+ struct descriptor_buffer *desc = ctx->buffer_tail;
+@@ -912,8 +908,8 @@ struct driver_data {
+ * Must always be called with the ochi->lock held to ensure proper
+ * generation handling and locking around packet queue manipulation.
+ */
+-static int
+-at_context_queue_packet(struct context *ctx, struct fw_packet *packet)
++static int at_context_queue_packet(struct context *ctx,
++ struct fw_packet *packet)
+ {
+ struct fw_ohci *ohci = ctx->ohci;
+ dma_addr_t d_bus, uninitialized_var(payload_bus);
+@@ -1095,8 +1091,8 @@ static int handle_at_packet(struct conte
+ #define HEADER_GET_DATA_LENGTH(q) (((q) >> 16) & 0xffff)
+ #define HEADER_GET_EXTENDED_TCODE(q) (((q) >> 0) & 0xffff)
+
+-static void
+-handle_local_rom(struct fw_ohci *ohci, struct fw_packet *packet, u32 csr)
++static void handle_local_rom(struct fw_ohci *ohci,
++ struct fw_packet *packet, u32 csr)
+ {
+ struct fw_packet response;
+ int tcode, length, i;
+@@ -1122,8 +1118,8 @@ handle_local_rom(struct fw_ohci *ohci, s
+ fw_core_handle_response(&ohci->card, &response);
+ }
+
+-static void
+-handle_local_lock(struct fw_ohci *ohci, struct fw_packet *packet, u32 csr)
++static void handle_local_lock(struct fw_ohci *ohci,
++ struct fw_packet *packet, u32 csr)
+ {
+ struct fw_packet response;
+ int tcode, length, ext_tcode, sel;
+@@ -1164,8 +1160,7 @@ handle_local_lock(struct fw_ohci *ohci,
+ fw_core_handle_response(&ohci->card, &response);
+ }
+
+-static void
+-handle_local_request(struct context *ctx, struct fw_packet *packet)
++static void handle_local_request(struct context *ctx, struct fw_packet *packet)
+ {
+ u64 offset;
+ u32 csr;
+@@ -1205,11 +1200,10 @@ handle_local_request(struct context *ctx
+ }
+ }
+
+-static void
+-at_context_transmit(struct context *ctx, struct fw_packet *packet)
++static void at_context_transmit(struct context *ctx, struct fw_packet *packet)
+ {
+ unsigned long flags;
+- int retval;
++ int ret;
+
+ spin_lock_irqsave(&ctx->ohci->lock, flags);
+
+@@ -1220,10 +1214,10 @@ at_context_transmit(struct context *ctx,
+ return;
+ }
+
+- retval = at_context_queue_packet(ctx, packet);
++ ret = at_context_queue_packet(ctx, packet);
+ spin_unlock_irqrestore(&ctx->ohci->lock, flags);
+
+- if (retval < 0)
++ if (ret < 0)
+ packet->callback(packet, &ctx->ohci->card, packet->ack);
+
+ }
+@@ -1590,12 +1584,12 @@ static int ohci_enable(struct fw_card *c
+ return 0;
+ }
+
+-static int
+-ohci_set_config_rom(struct fw_card *card, u32 *config_rom, size_t length)
++static int ohci_set_config_rom(struct fw_card *card,
++ u32 *config_rom, size_t length)
+ {
+ struct fw_ohci *ohci;
+ unsigned long flags;
+- int retval = -EBUSY;
++ int ret = -EBUSY;
+ __be32 *next_config_rom;
+ dma_addr_t uninitialized_var(next_config_rom_bus);
+
+@@ -1649,7 +1643,7 @@ ohci_set_config_rom(struct fw_card *card
+
+ reg_write(ohci, OHCI1394_ConfigROMmap,
+ ohci->next_config_rom_bus);
+- retval = 0;
++ ret = 0;
+ }
+
+ spin_unlock_irqrestore(&ohci->lock, flags);
+@@ -1661,13 +1655,13 @@ ohci_set_config_rom(struct fw_card *card
+ * controller could need to access it before the bus reset
+ * takes effect.
+ */
+- if (retval == 0)
++ if (ret == 0)
+ fw_core_initiate_bus_reset(&ohci->card, 1);
+ else
+ dma_free_coherent(ohci->card.device, CONFIG_ROM_SIZE,
+ next_config_rom, next_config_rom_bus);
+
+- return retval;
++ return ret;
+ }
+
+ static void ohci_send_request(struct fw_card *card, struct fw_packet *packet)
+@@ -1689,7 +1683,7 @@ static int ohci_cancel_packet(struct fw_
+ struct fw_ohci *ohci = fw_ohci(card);
+ struct context *ctx = &ohci->at_request_ctx;
+ struct driver_data *driver_data = packet->driver_data;
+- int retval = -ENOENT;
++ int ret = -ENOENT;
+
+ tasklet_disable(&ctx->tasklet);
+
+@@ -1704,23 +1698,22 @@ static int ohci_cancel_packet(struct fw_
+ driver_data->packet = NULL;
+ packet->ack = RCODE_CANCELLED;
+ packet->callback(packet, &ohci->card, packet->ack);
+- retval = 0;
+-
++ ret = 0;
+ out:
+ tasklet_enable(&ctx->tasklet);
+
+- return retval;
++ return ret;
+ }
+
+-static int
+-ohci_enable_phys_dma(struct fw_card *card, int node_id, int generation)
++static int ohci_enable_phys_dma(struct fw_card *card,
++ int node_id, int generation)
+ {
+ #ifdef CONFIG_FIREWIRE_OHCI_REMOTE_DMA
+ return 0;
+ #else
+ struct fw_ohci *ohci = fw_ohci(card);
+ unsigned long flags;
+- int n, retval = 0;
++ int n, ret = 0;
+
+ /*
+ * FIXME: Make sure this bitmask is cleared when we clear the busReset
+@@ -1730,7 +1723,7 @@ ohci_enable_phys_dma(struct fw_card *car
+ spin_lock_irqsave(&ohci->lock, flags);
+
+ if (ohci->generation != generation) {
+- retval = -ESTALE;
++ ret = -ESTALE;
+ goto out;
+ }
+
+@@ -1748,12 +1741,12 @@ ohci_enable_phys_dma(struct fw_card *car
+ flush_writes(ohci);
+ out:
+ spin_unlock_irqrestore(&ohci->lock, flags);
+- return retval;
++
++ return ret;
+ #endif /* CONFIG_FIREWIRE_OHCI_REMOTE_DMA */
+ }
+
+-static u64
+-ohci_get_bus_time(struct fw_card *card)
++static u64 ohci_get_bus_time(struct fw_card *card)
+ {
+ struct fw_ohci *ohci = fw_ohci(card);
+ u32 cycle_time;
+@@ -1765,6 +1758,28 @@ ohci_get_bus_time(struct fw_card *card)
+ return bus_time;
+ }
+
++static void copy_iso_headers(struct iso_context *ctx, void *p)
++{
++ int i = ctx->header_length;
++
++ if (i + ctx->base.header_size > PAGE_SIZE)
++ return;
++
++ /*
++ * The iso header is byteswapped to little endian by
++ * the controller, but the remaining header quadlets
++ * are big endian. We want to present all the headers
++ * as big endian, so we have to swap the first quadlet.
++ */
++ if (ctx->base.header_size > 0)
++ *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4));
++ if (ctx->base.header_size > 4)
++ *(u32 *) (ctx->header + i + 4) = __swab32(*(u32 *) p);
++ if (ctx->base.header_size > 8)
++ memcpy(ctx->header + i + 8, p + 8, ctx->base.header_size - 8);
++ ctx->header_length += ctx->base.header_size;
++}
++
+ static int handle_ir_dualbuffer_packet(struct context *context,
+ struct descriptor *d,
+ struct descriptor *last)
+@@ -1775,7 +1790,6 @@ static int handle_ir_dualbuffer_packet(s
+ __le32 *ir_header;
+ size_t header_length;
+ void *p, *end;
+- int i;
+
+ if (db->first_res_count != 0 && db->second_res_count != 0) {
+ if (ctx->excess_bytes <= le16_to_cpu(db->second_req_count)) {
+@@ -1788,25 +1802,14 @@ static int handle_ir_dualbuffer_packet(s
+ header_length = le16_to_cpu(db->first_req_count) -
+ le16_to_cpu(db->first_res_count);
+
+- i = ctx->header_length;
+ p = db + 1;
+ end = p + header_length;
+- while (p < end && i + ctx->base.header_size <= PAGE_SIZE) {
+- /*
+- * The iso header is byteswapped to little endian by
+- * the controller, but the remaining header quadlets
+- * are big endian. We want to present all the headers
+- * as big endian, so we have to swap the first
+- * quadlet.
+- */
+- *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4));
+- memcpy(ctx->header + i + 4, p + 8, ctx->base.header_size - 4);
+- i += ctx->base.header_size;
++ while (p < end) {
++ copy_iso_headers(ctx, p);
+ ctx->excess_bytes +=
+ (le32_to_cpu(*(__le32 *)(p + 4)) >> 16) & 0xffff;
+- p += ctx->base.header_size + 4;
++ p += max(ctx->base.header_size, (size_t)8);
+ }
+- ctx->header_length = i;
+
+ ctx->excess_bytes -= le16_to_cpu(db->second_req_count) -
+ le16_to_cpu(db->second_res_count);
+@@ -1832,7 +1835,6 @@ static int handle_ir_packet_per_buffer(s
+ struct descriptor *pd;
+ __le32 *ir_header;
+ void *p;
+- int i;
+
+ for (pd = d; pd <= last; pd++) {
+ if (pd->transfer_status)
+@@ -1842,21 +1844,8 @@ static int handle_ir_packet_per_buffer(s
+ /* Descriptor(s) not done yet, stop iteration */
+ return 0;
+
+- i = ctx->header_length;
+- p = last + 1;
+-
+- if (ctx->base.header_size > 0 &&
+- i + ctx->base.header_size <= PAGE_SIZE) {
+- /*
+- * The iso header is byteswapped to little endian by
+- * the controller, but the remaining header quadlets
+- * are big endian. We want to present all the headers
+- * as big endian, so we have to swap the first quadlet.
+- */
+- *(u32 *) (ctx->header + i) = __swab32(*(u32 *) (p + 4));
+- memcpy(ctx->header + i + 4, p + 8, ctx->base.header_size - 4);
+- ctx->header_length += ctx->base.header_size;
+- }
++ p = last + 1;
++ copy_iso_headers(ctx, p);
+
+ if (le16_to_cpu(last->control) & DESCRIPTOR_IRQ_ALWAYS) {
+ ir_header = (__le32 *) p;
+@@ -1888,21 +1877,24 @@ static int handle_it_packet(struct conte
+ return 1;
+ }
+
+-static struct fw_iso_context *
+-ohci_allocate_iso_context(struct fw_card *card, int type, size_t header_size)
++static struct fw_iso_context *ohci_allocate_iso_context(struct fw_card *card,
++ int type, int channel, size_t header_size)
+ {
+ struct fw_ohci *ohci = fw_ohci(card);
+ struct iso_context *ctx, *list;
+ descriptor_callback_t callback;
++ u64 *channels, dont_care = ~0ULL;
+ u32 *mask, regs;
+ unsigned long flags;
+- int index, retval = -ENOMEM;
++ int index, ret = -ENOMEM;
+
+ if (type == FW_ISO_CONTEXT_TRANSMIT) {
++ channels = &dont_care;
+ mask = &ohci->it_context_mask;
+ list = ohci->it_context_list;
+ callback = handle_it_packet;
+ } else {
++ channels = &ohci->ir_context_channels;
+ mask = &ohci->ir_context_mask;
+ list = ohci->ir_context_list;
+ if (ohci->use_dualbuffer)
+@@ -1912,9 +1904,11 @@ ohci_allocate_iso_context(struct fw_card
+ }
+
+ spin_lock_irqsave(&ohci->lock, flags);
+- index = ffs(*mask) - 1;
+- if (index >= 0)
++ index = *channels & 1ULL << channel ? ffs(*mask) - 1 : -1;
++ if (index >= 0) {
++ *channels &= ~(1ULL << channel);
+ *mask &= ~(1 << index);
++ }
+ spin_unlock_irqrestore(&ohci->lock, flags);
+
+ if (index < 0)
+@@ -1932,8 +1926,8 @@ ohci_allocate_iso_context(struct fw_card
+ if (ctx->header == NULL)
+ goto out;
+
+- retval = context_init(&ctx->context, ohci, regs, callback);
+- if (retval < 0)
++ ret = context_init(&ctx->context, ohci, regs, callback);
++ if (ret < 0)
+ goto out_with_header;
+
+ return &ctx->base;
+@@ -1945,7 +1939,7 @@ ohci_allocate_iso_context(struct fw_card
+ *mask |= 1 << index;
+ spin_unlock_irqrestore(&ohci->lock, flags);
+
+- return ERR_PTR(retval);
++ return ERR_PTR(ret);
+ }
+
+ static int ohci_start_iso(struct fw_iso_context *base,
+@@ -2024,16 +2018,16 @@ static void ohci_free_iso_context(struct
+ } else {
+ index = ctx - ohci->ir_context_list;
+ ohci->ir_context_mask |= 1 << index;
++ ohci->ir_context_channels |= 1ULL << base->channel;
+ }
+
+ spin_unlock_irqrestore(&ohci->lock, flags);
+ }
+
+-static int
+-ohci_queue_iso_transmit(struct fw_iso_context *base,
+- struct fw_iso_packet *packet,
+- struct fw_iso_buffer *buffer,
+- unsigned long payload)
++static int ohci_queue_iso_transmit(struct fw_iso_context *base,
++ struct fw_iso_packet *packet,
++ struct fw_iso_buffer *buffer,
++ unsigned long payload)
+ {
+ struct iso_context *ctx = container_of(base, struct iso_context, base);
+ struct descriptor *d, *last, *pd;
+@@ -2128,11 +2122,10 @@ ohci_queue_iso_transmit(struct fw_iso_co
+ return 0;
+ }
+
+-static int
+-ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
+- struct fw_iso_packet *packet,
+- struct fw_iso_buffer *buffer,
+- unsigned long payload)
++static int ohci_queue_iso_receive_dualbuffer(struct fw_iso_context *base,
++ struct fw_iso_packet *packet,
++ struct fw_iso_buffer *buffer,
++ unsigned long payload)
+ {
+ struct iso_context *ctx = container_of(base, struct iso_context, base);
+ struct db_descriptor *db = NULL;
+@@ -2151,11 +2144,11 @@ ohci_queue_iso_receive_dualbuffer(struct
+ z = 2;
+
+ /*
+- * The OHCI controller puts the status word in the header
+- * buffer too, so we need 4 extra bytes per packet.
++ * The OHCI controller puts the isochronous header and trailer in the
++ * buffer, so we need at least 8 bytes.
+ */
+ packet_count = p->header_length / ctx->base.header_size;
+- header_size = packet_count * (ctx->base.header_size + 4);
++ header_size = packet_count * max(ctx->base.header_size, (size_t)8);
+
+ /* Get header size in number of descriptors. */
+ header_z = DIV_ROUND_UP(header_size, sizeof(*d));
+@@ -2173,7 +2166,8 @@ ohci_queue_iso_receive_dualbuffer(struct
+ db = (struct db_descriptor *) d;
+ db->control = cpu_to_le16(DESCRIPTOR_STATUS |
+ DESCRIPTOR_BRANCH_ALWAYS);
+- db->first_size = cpu_to_le16(ctx->base.header_size + 4);
++ db->first_size =
++ cpu_to_le16(max(ctx->base.header_size, (size_t)8));
+ if (p->skip && rest == p->payload_length) {
+ db->control |= cpu_to_le16(DESCRIPTOR_WAIT);
+ db->first_req_count = db->first_size;
+@@ -2208,11 +2202,10 @@ ohci_queue_iso_receive_dualbuffer(struct
+ return 0;
+ }
+
+-static int
+-ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base,
+- struct fw_iso_packet *packet,
+- struct fw_iso_buffer *buffer,
+- unsigned long payload)
++static int ohci_queue_iso_receive_packet_per_buffer(struct fw_iso_context *base,
++ struct fw_iso_packet *packet,
++ struct fw_iso_buffer *buffer,
++ unsigned long payload)
+ {
+ struct iso_context *ctx = container_of(base, struct iso_context, base);
+ struct descriptor *d = NULL, *pd = NULL;
+@@ -2223,11 +2216,11 @@ ohci_queue_iso_receive_packet_per_buffer
+ int page, offset, packet_count, header_size, payload_per_buffer;
+
+ /*
+- * The OHCI controller puts the status word in the
+- * buffer too, so we need 4 extra bytes per packet.
++ * The OHCI controller puts the isochronous header and trailer in the
++ * buffer, so we need at least 8 bytes.
+ */
+ packet_count = p->header_length / ctx->base.header_size;
+- header_size = ctx->base.header_size + 4;
++ header_size = max(ctx->base.header_size, (size_t)8);
+
+ /* Get header size in number of descriptors. */
+ header_z = DIV_ROUND_UP(header_size, sizeof(*d));
+@@ -2286,29 +2279,27 @@ ohci_queue_iso_receive_packet_per_buffer
+ return 0;
+ }
+
+-static int
+-ohci_queue_iso(struct fw_iso_context *base,
+- struct fw_iso_packet *packet,
+- struct fw_iso_buffer *buffer,
+- unsigned long payload)
++static int ohci_queue_iso(struct fw_iso_context *base,
++ struct fw_iso_packet *packet,
++ struct fw_iso_buffer *buffer,
++ unsigned long payload)
+ {
+ struct iso_context *ctx = container_of(base, struct iso_context, base);
+ unsigned long flags;
+- int retval;
++ int ret;
+
+ spin_lock_irqsave(&ctx->context.ohci->lock, flags);
+ if (base->type == FW_ISO_CONTEXT_TRANSMIT)
+- retval = ohci_queue_iso_transmit(base, packet, buffer, payload);
++ ret = ohci_queue_iso_transmit(base, packet, buffer, payload);
+ else if (ctx->context.ohci->use_dualbuffer)
+- retval = ohci_queue_iso_receive_dualbuffer(base, packet,
+- buffer, payload);
++ ret = ohci_queue_iso_receive_dualbuffer(base, packet,
++ buffer, payload);
+ else
+- retval = ohci_queue_iso_receive_packet_per_buffer(base, packet,
+- buffer,
+- payload);
++ ret = ohci_queue_iso_receive_packet_per_buffer(base, packet,
++ buffer, payload);
+ spin_unlock_irqrestore(&ctx->context.ohci->lock, flags);
+
+- return retval;
++ return ret;
+ }
+
+ static const struct fw_card_driver ohci_driver = {
+@@ -2357,8 +2348,8 @@ static void ohci_pmac_off(struct pci_dev
+ #define ohci_pmac_off(dev)
+ #endif /* CONFIG_PPC_PMAC */
+
+-static int __devinit
+-pci_probe(struct pci_dev *dev, const struct pci_device_id *ent)
++static int __devinit pci_probe(struct pci_dev *dev,
++ const struct pci_device_id *ent)
+ {
+ struct fw_ohci *ohci;
+ u32 bus_options, max_receive, link_speed, version;
+@@ -2440,6 +2431,7 @@ pci_probe(struct pci_dev *dev, const str
+ ohci->it_context_list = kzalloc(size, GFP_KERNEL);
+
+ reg_write(ohci, OHCI1394_IsoXmitIntMaskSet, ~0);
++ ohci->ir_context_channels = ~0ULL;
+ ohci->ir_context_mask = reg_read(ohci, OHCI1394_IsoXmitIntMaskSet);
+ reg_write(ohci, OHCI1394_IsoXmitIntMaskClear, ~0);
+ size = sizeof(struct iso_context) * hweight32(ohci->ir_context_mask);
+diff -Naurp linux-2.6-git/drivers/firewire/fw-sbp2.c firewire-git/drivers/firewire/fw-sbp2.c
+--- linux-2.6-git/drivers/firewire/fw-sbp2.c 2009-01-30 13:39:02.991771976 -0500
++++ firewire-git/drivers/firewire/fw-sbp2.c 2009-01-30 13:35:51.861646907 -0500
+@@ -392,20 +392,18 @@ static const struct {
+ }
+ };
+
+-static void
+-free_orb(struct kref *kref)
++static void free_orb(struct kref *kref)
+ {
+ struct sbp2_orb *orb = container_of(kref, struct sbp2_orb, kref);
+
+ kfree(orb);
+ }
+
+-static void
+-sbp2_status_write(struct fw_card *card, struct fw_request *request,
+- int tcode, int destination, int source,
+- int generation, int speed,
+- unsigned long long offset,
+- void *payload, size_t length, void *callback_data)
++static void sbp2_status_write(struct fw_card *card, struct fw_request *request,
++ int tcode, int destination, int source,
++ int generation, int speed,
++ unsigned long long offset,
++ void *payload, size_t length, void *callback_data)
+ {
+ struct sbp2_logical_unit *lu = callback_data;
+ struct sbp2_orb *orb;
+@@ -451,9 +449,8 @@ sbp2_status_write(struct fw_card *card,
+ fw_send_response(card, request, RCODE_COMPLETE);
+ }
+
+-static void
+-complete_transaction(struct fw_card *card, int rcode,
+- void *payload, size_t length, void *data)
++static void complete_transaction(struct fw_card *card, int rcode,
++ void *payload, size_t length, void *data)
+ {
+ struct sbp2_orb *orb = data;
+ unsigned long flags;
+@@ -482,9 +479,8 @@ complete_transaction(struct fw_card *car
+ kref_put(&orb->kref, free_orb);
+ }
+
+-static void
+-sbp2_send_orb(struct sbp2_orb *orb, struct sbp2_logical_unit *lu,
+- int node_id, int generation, u64 offset)
++static void sbp2_send_orb(struct sbp2_orb *orb, struct sbp2_logical_unit *lu,
++ int node_id, int generation, u64 offset)
+ {
+ struct fw_device *device = fw_device(lu->tgt->unit->device.parent);
+ unsigned long flags;
+@@ -531,8 +527,8 @@ static int sbp2_cancel_orbs(struct sbp2_
+ return retval;
+ }
+
+-static void
+-complete_management_orb(struct sbp2_orb *base_orb, struct sbp2_status *status)
++static void complete_management_orb(struct sbp2_orb *base_orb,
++ struct sbp2_status *status)
+ {
+ struct sbp2_management_orb *orb =
+ container_of(base_orb, struct sbp2_management_orb, base);
+@@ -542,10 +538,9 @@ complete_management_orb(struct sbp2_orb
+ complete(&orb->done);
+ }
+
+-static int
+-sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
+- int generation, int function, int lun_or_login_id,
+- void *response)
++static int sbp2_send_management_orb(struct sbp2_logical_unit *lu, int node_id,
++ int generation, int function,
++ int lun_or_login_id, void *response)
+ {
+ struct fw_device *device = fw_device(lu->tgt->unit->device.parent);
+ struct sbp2_management_orb *orb;
+@@ -652,9 +647,8 @@ static void sbp2_agent_reset(struct sbp2
+ &d, sizeof(d));
+ }
+
+-static void
+-complete_agent_reset_write_no_wait(struct fw_card *card, int rcode,
+- void *payload, size_t length, void *data)
++static void complete_agent_reset_write_no_wait(struct fw_card *card,
++ int rcode, void *payload, size_t length, void *data)
+ {
+ kfree(data);
+ }
+@@ -1299,8 +1293,7 @@ static void sbp2_unmap_scatterlist(struc
+ sizeof(orb->page_table), DMA_TO_DEVICE);
+ }
+
+-static unsigned int
+-sbp2_status_to_sense_data(u8 *sbp2_status, u8 *sense_data)
++static unsigned int sbp2_status_to_sense_data(u8 *sbp2_status, u8 *sense_data)
+ {
+ int sam_status;
+
+@@ -1337,8 +1330,8 @@ sbp2_status_to_sense_data(u8 *sbp2_statu
+ }
+ }
+
+-static void
+-complete_command_orb(struct sbp2_orb *base_orb, struct sbp2_status *status)
++static void complete_command_orb(struct sbp2_orb *base_orb,
++ struct sbp2_status *status)
+ {
+ struct sbp2_command_orb *orb =
+ container_of(base_orb, struct sbp2_command_orb, base);
+@@ -1384,9 +1377,8 @@ complete_command_orb(struct sbp2_orb *ba
+ orb->done(orb->cmd);
+ }
+
+-static int
+-sbp2_map_scatterlist(struct sbp2_command_orb *orb, struct fw_device *device,
+- struct sbp2_logical_unit *lu)
++static int sbp2_map_scatterlist(struct sbp2_command_orb *orb,
++ struct fw_device *device, struct sbp2_logical_unit *lu)
+ {
+ struct scatterlist *sg = scsi_sglist(orb->cmd);
+ int i, n;
+@@ -1584,9 +1576,8 @@ static int sbp2_scsi_abort(struct scsi_c
+ * This is the concatenation of target port identifier and logical unit
+ * identifier as per SAM-2...SAM-4 annex A.
+ */
+-static ssize_t
+-sbp2_sysfs_ieee1394_id_show(struct device *dev, struct device_attribute *attr,
+- char *buf)
++static ssize_t sbp2_sysfs_ieee1394_id_show(struct device *dev,
++ struct device_attribute *attr, char *buf)
+ {
+ struct scsi_device *sdev = to_scsi_device(dev);
+ struct sbp2_logical_unit *lu;
+diff -Naurp linux-2.6-git/drivers/firewire/fw-topology.c firewire-git/drivers/firewire/fw-topology.c
+--- linux-2.6-git/drivers/firewire/fw-topology.c 2009-01-30 13:39:02.991771976 -0500
++++ firewire-git/drivers/firewire/fw-topology.c 2009-01-30 13:35:51.862647087 -0500
+@@ -314,9 +314,8 @@ typedef void (*fw_node_callback_t)(struc
+ struct fw_node * node,
+ struct fw_node * parent);
+
+-static void
+-for_each_fw_node(struct fw_card *card, struct fw_node *root,
+- fw_node_callback_t callback)
++static void for_each_fw_node(struct fw_card *card, struct fw_node *root,
++ fw_node_callback_t callback)
+ {
+ struct list_head list;
+ struct fw_node *node, *next, *child, *parent;
+@@ -349,9 +348,8 @@ for_each_fw_node(struct fw_card *card, s
+ fw_node_put(node);
+ }
+
+-static void
+-report_lost_node(struct fw_card *card,
+- struct fw_node *node, struct fw_node *parent)
++static void report_lost_node(struct fw_card *card,
++ struct fw_node *node, struct fw_node *parent)
+ {
+ fw_node_event(card, node, FW_NODE_DESTROYED);
+ fw_node_put(node);
+@@ -360,9 +358,8 @@ report_lost_node(struct fw_card *card,
+ card->bm_retries = 0;
+ }
+
+-static void
+-report_found_node(struct fw_card *card,
+- struct fw_node *node, struct fw_node *parent)
++static void report_found_node(struct fw_card *card,
++ struct fw_node *node, struct fw_node *parent)
+ {
+ int b_path = (node->phy_speed == SCODE_BETA);
+
+@@ -415,8 +412,7 @@ static void move_tree(struct fw_node *no
+ * found, lost or updated. Update the nodes in the card topology tree
+ * as we go.
+ */
+-static void
+-update_tree(struct fw_card *card, struct fw_node *root)
++static void update_tree(struct fw_card *card, struct fw_node *root)
+ {
+ struct list_head list0, list1;
+ struct fw_node *node0, *node1, *next1;
+@@ -497,8 +493,8 @@ update_tree(struct fw_card *card, struct
+ }
+ }
+
+-static void
+-update_topology_map(struct fw_card *card, u32 *self_ids, int self_id_count)
++static void update_topology_map(struct fw_card *card,
++ u32 *self_ids, int self_id_count)
+ {
+ int node_count;
+
+@@ -510,10 +506,8 @@ update_topology_map(struct fw_card *card
+ fw_compute_block_crc(card->topology_map);
+ }
+
+-void
+-fw_core_handle_bus_reset(struct fw_card *card,
+- int node_id, int generation,
+- int self_id_count, u32 * self_ids)
++void fw_core_handle_bus_reset(struct fw_card *card, int node_id, int generation,
++ int self_id_count, u32 *self_ids)
+ {
+ struct fw_node *local_node;
+ unsigned long flags;
+diff -Naurp linux-2.6-git/drivers/firewire/fw-topology.h firewire-git/drivers/firewire/fw-topology.h
+--- linux-2.6-git/drivers/firewire/fw-topology.h 2008-11-04 11:18:33.000000000 -0500
++++ firewire-git/drivers/firewire/fw-topology.h 2009-01-30 13:35:51.862647087 -0500
+@@ -19,6 +19,11 @@
+ #ifndef __fw_topology_h
+ #define __fw_topology_h
+
++#include <linux/list.h>
++#include <linux/slab.h>
++
++#include <asm/atomic.h>
++
+ enum {
+ FW_NODE_CREATED,
+ FW_NODE_UPDATED,
+@@ -51,26 +56,22 @@ struct fw_node {
+ struct fw_node *ports[0];
+ };
+
+-static inline struct fw_node *
+-fw_node_get(struct fw_node *node)
++static inline struct fw_node *fw_node_get(struct fw_node *node)
+ {
+ atomic_inc(&node->ref_count);
+
+ return node;
+ }
+
+-static inline void
+-fw_node_put(struct fw_node *node)
++static inline void fw_node_put(struct fw_node *node)
+ {
+ if (atomic_dec_and_test(&node->ref_count))
+ kfree(node);
+ }
+
+-void
+-fw_destroy_nodes(struct fw_card *card);
+-
+-int
+-fw_compute_block_crc(u32 *block);
++struct fw_card;
++void fw_destroy_nodes(struct fw_card *card);
+
++int fw_compute_block_crc(u32 *block);
+
+ #endif /* __fw_topology_h */
+diff -Naurp linux-2.6-git/drivers/firewire/fw-transaction.c firewire-git/drivers/firewire/fw-transaction.c
+--- linux-2.6-git/drivers/firewire/fw-transaction.c 2009-01-30 13:39:02.991771976 -0500
++++ firewire-git/drivers/firewire/fw-transaction.c 2009-01-30 13:35:51.862647087 -0500
+@@ -64,10 +64,9 @@
+ #define PHY_CONFIG_ROOT_ID(node_id) ((((node_id) & 0x3f) << 24) | (1 << 23))
+ #define PHY_IDENTIFIER(id) ((id) << 30)
+
+-static int
+-close_transaction(struct fw_transaction *transaction,
+- struct fw_card *card, int rcode,
+- u32 *payload, size_t length)
++static int close_transaction(struct fw_transaction *transaction,
++ struct fw_card *card, int rcode,
++ u32 *payload, size_t length)
+ {
+ struct fw_transaction *t;
+ unsigned long flags;
+@@ -94,9 +93,8 @@ close_transaction(struct fw_transaction
+ * Only valid for transactions that are potentially pending (ie have
+ * been sent).
+ */
+-int
+-fw_cancel_transaction(struct fw_card *card,
+- struct fw_transaction *transaction)
++int fw_cancel_transaction(struct fw_card *card,
++ struct fw_transaction *transaction)
+ {
+ /*
+ * Cancel the packet transmission if it's still queued. That
+@@ -116,9 +114,8 @@ fw_cancel_transaction(struct fw_card *ca
+ }
+ EXPORT_SYMBOL(fw_cancel_transaction);
+
+-static void
+-transmit_complete_callback(struct fw_packet *packet,
+- struct fw_card *card, int status)
++static void transmit_complete_callback(struct fw_packet *packet,
++ struct fw_card *card, int status)
+ {
+ struct fw_transaction *t =
+ container_of(packet, struct fw_transaction, packet);
+@@ -151,8 +148,7 @@ transmit_complete_callback(struct fw_pac
+ }
+ }
+
+-static void
+-fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
++static void fw_fill_request(struct fw_packet *packet, int tcode, int tlabel,
+ int destination_id, int source_id, int generation, int speed,
+ unsigned long long offset, void *payload, size_t length)
+ {
+@@ -247,12 +243,10 @@ fw_fill_request(struct fw_packet *packet
+ * @param callback_data pointer to arbitrary data, which will be
+ * passed to the callback
+ */
+-void
+-fw_send_request(struct fw_card *card, struct fw_transaction *t,
+- int tcode, int destination_id, int generation, int speed,
+- unsigned long long offset,
+- void *payload, size_t length,
+- fw_transaction_callback_t callback, void *callback_data)
++void fw_send_request(struct fw_card *card, struct fw_transaction *t, int tcode,
++ int destination_id, int generation, int speed,
++ unsigned long long offset, void *payload, size_t length,
++ fw_transaction_callback_t callback, void *callback_data)
+ {
+ unsigned long flags;
+ int tlabel;
+@@ -322,8 +316,8 @@ static void transaction_callback(struct
+ * Returns the RCODE.
+ */
+ int fw_run_transaction(struct fw_card *card, int tcode, int destination_id,
+- int generation, int speed, unsigned long long offset,
+- void *data, size_t length)
++ int generation, int speed, unsigned long long offset,
++ void *data, size_t length)
+ {
+ struct transaction_callback_data d;
+ struct fw_transaction t;
+@@ -399,9 +393,8 @@ void fw_flush_transactions(struct fw_car
+ }
+ }
+
+-static struct fw_address_handler *
+-lookup_overlapping_address_handler(struct list_head *list,
+- unsigned long long offset, size_t length)
++static struct fw_address_handler *lookup_overlapping_address_handler(
++ struct list_head *list, unsigned long long offset, size_t length)
+ {
+ struct fw_address_handler *handler;
+
+@@ -414,9 +407,8 @@ lookup_overlapping_address_handler(struc
+ return NULL;
+ }
+
+-static struct fw_address_handler *
+-lookup_enclosing_address_handler(struct list_head *list,
+- unsigned long long offset, size_t length)
++static struct fw_address_handler *lookup_enclosing_address_handler(
++ struct list_head *list, unsigned long long offset, size_t length)
+ {
+ struct fw_address_handler *handler;
+
+@@ -449,36 +441,44 @@ const struct fw_address_region fw_unit_s
+ #endif /* 0 */
+
+ /**
+- * Allocate a range of addresses in the node space of the OHCI
+- * controller. When a request is received that falls within the
+- * specified address range, the specified callback is invoked. The
+- * parameters passed to the callback give the details of the
+- * particular request.
++ * fw_core_add_address_handler - register for incoming requests
++ * @handler: callback
++ * @region: region in the IEEE 1212 node space address range
++ *
++ * region->start, ->end, and handler->length have to be quadlet-aligned.
++ *
++ * When a request is received that falls within the specified address range,
++ * the specified callback is invoked. The parameters passed to the callback
++ * give the details of the particular request.
+ *
+ * Return value: 0 on success, non-zero otherwise.
+ * The start offset of the handler's address region is determined by
+ * fw_core_add_address_handler() and is returned in handler->offset.
+- * The offset is quadlet-aligned.
+ */
+-int
+-fw_core_add_address_handler(struct fw_address_handler *handler,
+- const struct fw_address_region *region)
++int fw_core_add_address_handler(struct fw_address_handler *handler,
++ const struct fw_address_region *region)
+ {
+ struct fw_address_handler *other;
+ unsigned long flags;
+ int ret = -EBUSY;
+
++ if (region->start & 0xffff000000000003ULL ||
++ region->end & 0xffff000000000003ULL ||
++ region->start >= region->end ||
++ handler->length & 3 ||
++ handler->length == 0)
++ return -EINVAL;
++
+ spin_lock_irqsave(&address_handler_lock, flags);
+
+- handler->offset = roundup(region->start, 4);
++ handler->offset = region->start;
+ while (handler->offset + handler->length <= region->end) {
+ other =
+ lookup_overlapping_address_handler(&address_handler_list,
+ handler->offset,
+ handler->length);
+ if (other != NULL) {
+- handler->offset =
+- roundup(other->offset + other->length, 4);
++ handler->offset += other->length;
+ } else {
+ list_add_tail(&handler->link, &address_handler_list);
+ ret = 0;
+@@ -493,12 +493,7 @@ fw_core_add_address_handler(struct fw_ad
+ EXPORT_SYMBOL(fw_core_add_address_handler);
+
+ /**
+- * Deallocate a range of addresses allocated with fw_allocate. This
+- * will call the associated callback one last time with a the special
+- * tcode TCODE_DEALLOCATE, to let the client destroy the registered
+- * callback data. For convenience, the callback parameters offset and
+- * length are set to the start and the length respectively for the
+- * deallocated region, payload is set to NULL.
++ * fw_core_remove_address_handler - unregister an address handler
+ */
+ void fw_core_remove_address_handler(struct fw_address_handler *handler)
+ {
+@@ -518,9 +513,8 @@ struct fw_request {
+ u32 data[0];
+ };
+
+-static void
+-free_response_callback(struct fw_packet *packet,
+- struct fw_card *card, int status)
++static void free_response_callback(struct fw_packet *packet,
++ struct fw_card *card, int status)
+ {
+ struct fw_request *request;
+
+@@ -528,9 +522,8 @@ free_response_callback(struct fw_packet
+ kfree(request);
+ }
+
+-void
+-fw_fill_response(struct fw_packet *response, u32 *request_header,
+- int rcode, void *payload, size_t length)
++void fw_fill_response(struct fw_packet *response, u32 *request_header,
++ int rcode, void *payload, size_t length)
+ {
+ int tcode, tlabel, extended_tcode, source, destination;
+
+@@ -588,8 +581,7 @@ fw_fill_response(struct fw_packet *respo
+ }
+ EXPORT_SYMBOL(fw_fill_response);
+
+-static struct fw_request *
+-allocate_request(struct fw_packet *p)
++static struct fw_request *allocate_request(struct fw_packet *p)
+ {
+ struct fw_request *request;
+ u32 *data, length;
+@@ -649,8 +641,8 @@ allocate_request(struct fw_packet *p)
+ return request;
+ }
+
+-void
+-fw_send_response(struct fw_card *card, struct fw_request *request, int rcode)
++void fw_send_response(struct fw_card *card,
++ struct fw_request *request, int rcode)
+ {
+ /* unified transaction or broadcast transaction: don't respond */
+ if (request->ack != ACK_PENDING ||
+@@ -670,8 +662,7 @@ fw_send_response(struct fw_card *card, s
+ }
+ EXPORT_SYMBOL(fw_send_response);
+
+-void
+-fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
++void fw_core_handle_request(struct fw_card *card, struct fw_packet *p)
+ {
+ struct fw_address_handler *handler;
+ struct fw_request *request;
+@@ -719,8 +710,7 @@ fw_core_handle_request(struct fw_card *c
+ }
+ EXPORT_SYMBOL(fw_core_handle_request);
+
+-void
+-fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
++void fw_core_handle_response(struct fw_card *card, struct fw_packet *p)
+ {
+ struct fw_transaction *t;
+ unsigned long flags;
+@@ -793,12 +783,10 @@ static const struct fw_address_region to
+ { .start = CSR_REGISTER_BASE | CSR_TOPOLOGY_MAP,
+ .end = CSR_REGISTER_BASE | CSR_TOPOLOGY_MAP_END, };
+
+-static void
+-handle_topology_map(struct fw_card *card, struct fw_request *request,
+- int tcode, int destination, int source,
+- int generation, int speed,
+- unsigned long long offset,
+- void *payload, size_t length, void *callback_data)
++static void handle_topology_map(struct fw_card *card, struct fw_request *request,
++ int tcode, int destination, int source, int generation,
++ int speed, unsigned long long offset,
++ void *payload, size_t length, void *callback_data)
+ {
+ int i, start, end;
+ __be32 *map;
+@@ -832,12 +820,10 @@ static const struct fw_address_region re
+ { .start = CSR_REGISTER_BASE,
+ .end = CSR_REGISTER_BASE | CSR_CONFIG_ROM, };
+
+-static void
+-handle_registers(struct fw_card *card, struct fw_request *request,
+- int tcode, int destination, int source,
+- int generation, int speed,
+- unsigned long long offset,
+- void *payload, size_t length, void *callback_data)
++static void handle_registers(struct fw_card *card, struct fw_request *request,
++ int tcode, int destination, int source, int generation,
++ int speed, unsigned long long offset,
++ void *payload, size_t length, void *callback_data)
+ {
+ int reg = offset & ~CSR_REGISTER_BASE;
+ unsigned long long bus_time;
+@@ -939,11 +925,11 @@ static struct fw_descriptor model_id_des
+
+ static int __init fw_core_init(void)
+ {
+- int retval;
++ int ret;
+
+- retval = bus_register(&fw_bus_type);
+- if (retval < 0)
+- return retval;
++ ret = bus_register(&fw_bus_type);
++ if (ret < 0)
++ return ret;
+
+ fw_cdev_major = register_chrdev(0, "firewire", &fw_device_ops);
+ if (fw_cdev_major < 0) {
+@@ -951,19 +937,10 @@ static int __init fw_core_init(void)
+ return fw_cdev_major;
+ }
+
+- retval = fw_core_add_address_handler(&topology_map,
+- &topology_map_region);
+- BUG_ON(retval < 0);
+-
+- retval = fw_core_add_address_handler(&registers,
+- &registers_region);
+- BUG_ON(retval < 0);
+-
+- /* Add the vendor textual descriptor. */
+- retval = fw_core_add_descriptor(&vendor_id_descriptor);
+- BUG_ON(retval < 0);
+- retval = fw_core_add_descriptor(&model_id_descriptor);
+- BUG_ON(retval < 0);
++ fw_core_add_address_handler(&topology_map, &topology_map_region);
++ fw_core_add_address_handler(&registers, &registers_region);
++ fw_core_add_descriptor(&vendor_id_descriptor);
++ fw_core_add_descriptor(&model_id_descriptor);
+
+ return 0;
+ }
+diff -Naurp linux-2.6-git/drivers/firewire/fw-transaction.h firewire-git/drivers/firewire/fw-transaction.h
+--- linux-2.6-git/drivers/firewire/fw-transaction.h 2009-01-30 13:39:02.992772636 -0500
++++ firewire-git/drivers/firewire/fw-transaction.h 2009-01-30 13:35:51.862647087 -0500
+@@ -82,14 +82,14 @@
+ #define CSR_SPEED_MAP 0x2000
+ #define CSR_SPEED_MAP_END 0x3000
+
++#define BANDWIDTH_AVAILABLE_INITIAL 4915
+ #define BROADCAST_CHANNEL_INITIAL (1 << 31 | 31)
+ #define BROADCAST_CHANNEL_VALID (1 << 30)
+
+ #define fw_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, ## args)
+ #define fw_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args)
+
+-static inline void
+-fw_memcpy_from_be32(void *_dst, void *_src, size_t size)
++static inline void fw_memcpy_from_be32(void *_dst, void *_src, size_t size)
+ {
+ u32 *dst = _dst;
+ __be32 *src = _src;
+@@ -99,8 +99,7 @@ fw_memcpy_from_be32(void *_dst, void *_s
+ dst[i] = be32_to_cpu(src[i]);
+ }
+
+-static inline void
+-fw_memcpy_to_be32(void *_dst, void *_src, size_t size)
++static inline void fw_memcpy_to_be32(void *_dst, void *_src, size_t size)
+ {
+ fw_memcpy_from_be32(_dst, _src, size);
+ }
+@@ -125,8 +124,7 @@ typedef void (*fw_packet_callback_t)(str
+ struct fw_card *card, int status);
+
+ typedef void (*fw_transaction_callback_t)(struct fw_card *card, int rcode,
+- void *data,
+- size_t length,
++ void *data, size_t length,
+ void *callback_data);
+
+ /*
+@@ -141,12 +139,6 @@ typedef void (*fw_address_callback_t)(st
+ void *data, size_t length,
+ void *callback_data);
+
+-typedef void (*fw_bus_reset_callback_t)(struct fw_card *handle,
+- int node_id, int generation,
+- u32 *self_ids,
+- int self_id_count,
+- void *callback_data);
+-
+ struct fw_packet {
+ int speed;
+ int generation;
+@@ -187,12 +179,6 @@ struct fw_transaction {
+ void *callback_data;
+ };
+
+-static inline struct fw_packet *
+-fw_packet(struct list_head *l)
+-{
+- return list_entry(l, struct fw_packet, link);
+-}
+-
+ struct fw_address_handler {
+ u64 offset;
+ size_t length;
+@@ -201,7 +187,6 @@ struct fw_address_handler {
+ struct list_head link;
+ };
+
+-
+ struct fw_address_region {
+ u64 start;
+ u64 end;
+@@ -315,10 +300,8 @@ struct fw_iso_packet {
+ struct fw_iso_context;
+
+ typedef void (*fw_iso_callback_t)(struct fw_iso_context *context,
+- u32 cycle,
+- size_t header_length,
+- void *header,
+- void *data);
++ u32 cycle, size_t header_length,
++ void *header, void *data);
+
+ /*
+ * An iso buffer is just a set of pages mapped for DMA in the
+@@ -344,36 +327,25 @@ struct fw_iso_context {
+ void *callback_data;
+ };
+
+-int
+-fw_iso_buffer_init(struct fw_iso_buffer *buffer,
+- struct fw_card *card,
+- int page_count,
+- enum dma_data_direction direction);
+-int
+-fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma);
+-void
+-fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, struct fw_card *card);
+-
+-struct fw_iso_context *
+-fw_iso_context_create(struct fw_card *card, int type,
+- int channel, int speed, size_t header_size,
+- fw_iso_callback_t callback, void *callback_data);
+-
+-void
+-fw_iso_context_destroy(struct fw_iso_context *ctx);
+-
+-int
+-fw_iso_context_queue(struct fw_iso_context *ctx,
+- struct fw_iso_packet *packet,
+- struct fw_iso_buffer *buffer,
+- unsigned long payload);
+-
+-int
+-fw_iso_context_start(struct fw_iso_context *ctx,
+- int cycle, int sync, int tags);
++int fw_iso_buffer_init(struct fw_iso_buffer *buffer, struct fw_card *card,
++ int page_count, enum dma_data_direction direction);
++int fw_iso_buffer_map(struct fw_iso_buffer *buffer, struct vm_area_struct *vma);
++void fw_iso_buffer_destroy(struct fw_iso_buffer *buffer, struct fw_card *card);
++
++struct fw_iso_context *fw_iso_context_create(struct fw_card *card,
++ int type, int channel, int speed, size_t header_size,
++ fw_iso_callback_t callback, void *callback_data);
++int fw_iso_context_queue(struct fw_iso_context *ctx,
++ struct fw_iso_packet *packet,
++ struct fw_iso_buffer *buffer,
++ unsigned long payload);
++int fw_iso_context_start(struct fw_iso_context *ctx,
++ int cycle, int sync, int tags);
++int fw_iso_context_stop(struct fw_iso_context *ctx);
++void fw_iso_context_destroy(struct fw_iso_context *ctx);
+
+-int
+-fw_iso_context_stop(struct fw_iso_context *ctx);
++void fw_iso_resource_manage(struct fw_card *card, int generation,
++ u64 channels_mask, int *channel, int *bandwidth, bool allocate);
+
+ struct fw_card_driver {
+ /*
+@@ -415,7 +387,7 @@ struct fw_card_driver {
+
+ struct fw_iso_context *
+ (*allocate_iso_context)(struct fw_card *card,
+- int type, size_t header_size);
++ int type, int channel, size_t header_size);
+ void (*free_iso_context)(struct fw_iso_context *ctx);
+
+ int (*start_iso)(struct fw_iso_context *ctx,
+@@ -429,24 +401,18 @@ struct fw_card_driver {
+ int (*stop_iso)(struct fw_iso_context *ctx);
+ };
+
+-int
+-fw_core_initiate_bus_reset(struct fw_card *card, int short_reset);
++int fw_core_initiate_bus_reset(struct fw_card *card, int short_reset);
+
+-void
+-fw_send_request(struct fw_card *card, struct fw_transaction *t,
++void fw_send_request(struct fw_card *card, struct fw_transaction *t,
+ int tcode, int destination_id, int generation, int speed,
+ unsigned long long offset, void *data, size_t length,
+ fw_transaction_callback_t callback, void *callback_data);
+-
+-int fw_run_transaction(struct fw_card *card, int tcode, int destination_id,
+- int generation, int speed, unsigned long long offset,
+- void *data, size_t length);
+-
+ int fw_cancel_transaction(struct fw_card *card,
+ struct fw_transaction *transaction);
+-
+ void fw_flush_transactions(struct fw_card *card);
+-
++int fw_run_transaction(struct fw_card *card, int tcode, int destination_id,
++ int generation, int speed, unsigned long long offset,
++ void *data, size_t length);
+ void fw_send_phy_config(struct fw_card *card,
+ int node_id, int generation, int gap_count);
+
+@@ -454,29 +420,18 @@ void fw_send_phy_config(struct fw_card *
+ * Called by the topology code to inform the device code of node
+ * activity; found, lost, or updated nodes.
+ */
+-void
+-fw_node_event(struct fw_card *card, struct fw_node *node, int event);
++void fw_node_event(struct fw_card *card, struct fw_node *node, int event);
+
+ /* API used by card level drivers */
+
+-void
+-fw_card_initialize(struct fw_card *card, const struct fw_card_driver *driver,
+- struct device *device);
+-int
+-fw_card_add(struct fw_card *card,
+- u32 max_receive, u32 link_speed, u64 guid);
+-
+-void
+-fw_core_remove_card(struct fw_card *card);
+-
+-void
+-fw_core_handle_bus_reset(struct fw_card *card,
+- int node_id, int generation,
+- int self_id_count, u32 *self_ids);
+-void
+-fw_core_handle_request(struct fw_card *card, struct fw_packet *request);
+-
+-void
+-fw_core_handle_response(struct fw_card *card, struct fw_packet *packet);
++void fw_card_initialize(struct fw_card *card,
++ const struct fw_card_driver *driver, struct device *device);
++int fw_card_add(struct fw_card *card,
++ u32 max_receive, u32 link_speed, u64 guid);
++void fw_core_remove_card(struct fw_card *card);
++void fw_core_handle_bus_reset(struct fw_card *card, int node_id,
++ int generation, int self_id_count, u32 *self_ids);
++void fw_core_handle_request(struct fw_card *card, struct fw_packet *request);
++void fw_core_handle_response(struct fw_card *card, struct fw_packet *packet);
+
+ #endif /* __fw_transaction_h */
+--- linux-2.6-git/include/linux/firewire-cdev.h 2008-11-04 11:19:21.000000000 -0500
++++ firewire-git/include/linux/firewire-cdev.h 2009-01-30 13:35:54.327647015 -0500
+@@ -25,10 +25,12 @@
+ #include <linux/types.h>
+ #include <linux/firewire-constants.h>
+
+-#define FW_CDEV_EVENT_BUS_RESET 0x00
+-#define FW_CDEV_EVENT_RESPONSE 0x01
+-#define FW_CDEV_EVENT_REQUEST 0x02
+-#define FW_CDEV_EVENT_ISO_INTERRUPT 0x03
++#define FW_CDEV_EVENT_BUS_RESET 0x00
++#define FW_CDEV_EVENT_RESPONSE 0x01
++#define FW_CDEV_EVENT_REQUEST 0x02
++#define FW_CDEV_EVENT_ISO_INTERRUPT 0x03
++#define FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED 0x04
++#define FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED 0x05
+
+ /**
+ * struct fw_cdev_event_common - Common part of all fw_cdev_event_ types
+@@ -136,7 +138,24 @@ struct fw_cdev_event_request {
+ * This event is sent when the controller has completed an &fw_cdev_iso_packet
+ * with the %FW_CDEV_ISO_INTERRUPT bit set. In the receive case, the headers
+ * stripped of all packets up until and including the interrupt packet are
+- * returned in the @header field.
++ * returned in the @header field. The amount of header data per packet is as
++ * specified at iso context creation by &fw_cdev_create_iso_context.header_size.
++ *
++ * In version 1 of this ABI, header data consisted of the 1394 isochronous
++ * packet header, followed by quadlets from the packet payload if
++ * &fw_cdev_create_iso_context.header_size > 4.
++ *
++ * In version 2 of this ABI, header data consist of the 1394 isochronous
++ * packet header, followed by a timestamp quadlet if
++ * &fw_cdev_create_iso_context.header_size > 4, followed by quadlets from the
++ * packet payload if &fw_cdev_create_iso_context.header_size > 8.
++ *
++ * Behaviour of ver. 1 of this ABI is no longer available since ABI ver. 2.
++ *
++ * Format of 1394 iso packet header: 16 bits len, 2 bits tag, 6 bits channel,
++ * 4 bits tcode, 4 bits sy, in big endian byte order. Format of timestamp:
++ * 16 bits invalid, 3 bits cycleSeconds, 13 bits cycleCount, in big endian byte
++ * order.
+ */
+ struct fw_cdev_event_iso_interrupt {
+ __u64 closure;
+@@ -147,12 +166,44 @@ struct fw_cdev_event_iso_interrupt {
+ };
+
+ /**
++ * struct fw_cdev_event_iso_resource - Iso resources were allocated or freed
++ * @closure: See &fw_cdev_event_common;
++ * set by %FW_CDEV_IOC_(DE)ALLOCATE_ISO_RESOURCE(_ONCE) ioctl
++ * @type: %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or
++ * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED
++ * @handle: Reference by which an allocated resource can be deallocated
++ * @channel: Isochronous channel which was (de)allocated, if any
++ * @bandwidth: Bandwidth allocation units which were (de)allocated, if any
++ *
++ * An %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED event is sent after an isochronous
++ * resource was allocated at the IRM. The client has to check @channel and
++ * @bandwidth for whether the allocation actually succeeded.
++ *
++ * An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event is sent after an isochronous
++ * resource was deallocated at the IRM. It is also sent when automatic
++ * reallocation after a bus reset failed.
++ *
++ * @channel is <0 if no channel was (de)allocated or if reallocation failed.
++ * @bandwidth is 0 if no bandwidth was (de)allocated or if reallocation failed.
++ */
++struct fw_cdev_event_iso_resource {
++ __u64 closure;
++ __u32 type;
++ __u32 handle;
++ __s32 channel;
++ __s32 bandwidth;
++};
++
++/**
+ * union fw_cdev_event - Convenience union of fw_cdev_event_ types
+ * @common: Valid for all types
+ * @bus_reset: Valid if @common.type == %FW_CDEV_EVENT_BUS_RESET
+ * @response: Valid if @common.type == %FW_CDEV_EVENT_RESPONSE
+ * @request: Valid if @common.type == %FW_CDEV_EVENT_REQUEST
+ * @iso_interrupt: Valid if @common.type == %FW_CDEV_EVENT_ISO_INTERRUPT
++ * @iso_resource: Valid if @common.type ==
++ * %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED or
++ * %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED
+ *
+ * Convenience union for userspace use. Events could be read(2) into an
+ * appropriately aligned char buffer and then cast to this union for further
+@@ -163,13 +214,15 @@ struct fw_cdev_event_iso_interrupt {
+ * not fit will be discarded so that the next read(2) will return a new event.
+ */
+ union fw_cdev_event {
+- struct fw_cdev_event_common common;
+- struct fw_cdev_event_bus_reset bus_reset;
+- struct fw_cdev_event_response response;
+- struct fw_cdev_event_request request;
+- struct fw_cdev_event_iso_interrupt iso_interrupt;
++ struct fw_cdev_event_common common;
++ struct fw_cdev_event_bus_reset bus_reset;
++ struct fw_cdev_event_response response;
++ struct fw_cdev_event_request request;
++ struct fw_cdev_event_iso_interrupt iso_interrupt;
++ struct fw_cdev_event_iso_resource iso_resource;
+ };
+
++/* available since kernel version 2.6.22 */
+ #define FW_CDEV_IOC_GET_INFO _IOWR('#', 0x00, struct fw_cdev_get_info)
+ #define FW_CDEV_IOC_SEND_REQUEST _IOW('#', 0x01, struct fw_cdev_send_request)
+ #define FW_CDEV_IOC_ALLOCATE _IOWR('#', 0x02, struct fw_cdev_allocate)
+@@ -178,18 +231,29 @@ union fw_cdev_event {
+ #define FW_CDEV_IOC_INITIATE_BUS_RESET _IOW('#', 0x05, struct fw_cdev_initiate_bus_reset)
+ #define FW_CDEV_IOC_ADD_DESCRIPTOR _IOWR('#', 0x06, struct fw_cdev_add_descriptor)
+ #define FW_CDEV_IOC_REMOVE_DESCRIPTOR _IOW('#', 0x07, struct fw_cdev_remove_descriptor)
+-
+ #define FW_CDEV_IOC_CREATE_ISO_CONTEXT _IOWR('#', 0x08, struct fw_cdev_create_iso_context)
+ #define FW_CDEV_IOC_QUEUE_ISO _IOWR('#', 0x09, struct fw_cdev_queue_iso)
+ #define FW_CDEV_IOC_START_ISO _IOW('#', 0x0a, struct fw_cdev_start_iso)
+ #define FW_CDEV_IOC_STOP_ISO _IOW('#', 0x0b, struct fw_cdev_stop_iso)
++
++/* available since kernel version 2.6.24 */
+ #define FW_CDEV_IOC_GET_CYCLE_TIMER _IOR('#', 0x0c, struct fw_cdev_get_cycle_timer)
+
+-/* FW_CDEV_VERSION History
+- *
+- * 1 Feb 18, 2007: Initial version.
++/* available since kernel version 2.6.30 */
++#define FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE _IOWR('#', 0x0d, struct fw_cdev_allocate_iso_resource)
++#define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE _IOW('#', 0x0e, struct fw_cdev_deallocate)
++#define FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE _IOW('#', 0x0f, struct fw_cdev_allocate_iso_resource)
++#define FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE_ONCE _IOW('#', 0x10, struct fw_cdev_allocate_iso_resource)
++#define FW_CDEV_IOC_GET_SPEED _IOR('#', 0x11, struct fw_cdev_get_speed)
++#define FW_CDEV_IOC_SEND_BROADCAST_REQUEST _IOW('#', 0x12, struct fw_cdev_send_request)
++
++/*
++ * FW_CDEV_VERSION History
++ * 1 (2.6.22) - initial version
++ * 2 (2.6.30) - changed &fw_cdev_event_iso_interrupt.header if
++ * &fw_cdev_create_iso_context.header_size is 8 or more
+ */
+-#define FW_CDEV_VERSION 1
++#define FW_CDEV_VERSION 2
+
+ /**
+ * struct fw_cdev_get_info - General purpose information ioctl
+@@ -201,7 +265,7 @@ union fw_cdev_event {
+ * case, @rom_length is updated with the actual length of the
+ * configuration ROM.
+ * @rom: If non-zero, address of a buffer to be filled by a copy of the
+- * local node's configuration ROM
++ * device's configuration ROM
+ * @bus_reset: If non-zero, address of a buffer to be filled by a
+ * &struct fw_cdev_event_bus_reset with the current state
+ * of the bus. This does not cause a bus reset to happen.
+@@ -229,7 +293,7 @@ struct fw_cdev_get_info {
+ * Send a request to the device. This ioctl implements all outgoing requests.
+ * Both quadlet and block request specify the payload as a pointer to the data
+ * in the @data field. Once the transaction completes, the kernel writes an
+- * &fw_cdev_event_request event back. The @closure field is passed back to
++ * &fw_cdev_event_response event back. The @closure field is passed back to
+ * user space in the response event.
+ */
+ struct fw_cdev_send_request {
+@@ -284,9 +348,9 @@ struct fw_cdev_allocate {
+ };
+
+ /**
+- * struct fw_cdev_deallocate - Free an address range allocation
+- * @handle: Handle to the address range, as returned by the kernel when the
+- * range was allocated
++ * struct fw_cdev_deallocate - Free a CSR address range or isochronous resource
++ * @handle: Handle to the address range or iso resource, as returned by the
++ * kernel when the range or resource was allocated
+ */
+ struct fw_cdev_deallocate {
+ __u32 handle;
+@@ -370,6 +434,9 @@ struct fw_cdev_remove_descriptor {
+ *
+ * If a context was successfully created, the kernel writes back a handle to the
+ * context, which must be passed in for subsequent operations on that context.
++ *
++ * Note that the effect of a @header_size > 4 depends on
++ * &fw_cdev_get_info.version, as documented at &fw_cdev_event_iso_interrupt.
+ */
+ struct fw_cdev_create_iso_context {
+ __u32 type;
+@@ -473,10 +540,73 @@ struct fw_cdev_stop_iso {
+ * The %FW_CDEV_IOC_GET_CYCLE_TIMER ioctl reads the isochronous cycle timer
+ * and also the system clock. This allows to express the receive time of an
+ * isochronous packet as a system time with microsecond accuracy.
++ *
++ * @cycle_timer consists of 7 bits cycleSeconds, 13 bits cycleCount, and
++ * 12 bits cycleOffset, in host byte order.
+ */
+ struct fw_cdev_get_cycle_timer {
+ __u64 local_time;
+ __u32 cycle_timer;
+ };
+
++/**
++ * struct fw_cdev_allocate_iso_resource - (De)allocate a channel or bandwidth
++ * @closure: Passed back to userspace in correponding iso resource events
++ * @channels: Isochronous channels of which one is to be (de)allocated
++ * @bandwidth: Isochronous bandwidth units to be (de)allocated
++ * @handle: Handle to the allocation, written by the kernel (only valid in
++ * case of %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctls)
++ *
++ * The %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE ioctl initiates allocation of an
++ * isochronous channel and/or of isochronous bandwidth at the isochronous
++ * resource manager (IRM). Only one of the channels specified in @channels is
++ * allocated. An %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED is sent after
++ * communication with the IRM, indicating success or failure in the event data.
++ * The kernel will automatically reallocate the resources after bus resets.
++ * Should a reallocation fail, an %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event
++ * will be sent. The kernel will also automatically deallocate the resources
++ * when the file descriptor is closed.
++ *
++ * The %FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE ioctl can be used to initiate
++ * deallocation of resources which were allocated as described above.
++ * An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event concludes this operation.
++ *
++ * The %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE ioctl is a variant of allocation
++ * without automatic re- or deallocation.
++ * An %FW_CDEV_EVENT_ISO_RESOURCE_ALLOCATED event concludes this operation,
++ * indicating success or failure in its data.
++ *
++ * The %FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE_ONCE ioctl works like
++ * %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE except that resources are freed
++ * instead of allocated.
++ * An %FW_CDEV_EVENT_ISO_RESOURCE_DEALLOCATED event concludes this operation.
++ *
++ * To summarize, %FW_CDEV_IOC_DEALLOCATE_ISO_RESOURCE allocates iso resources
++ * for the lifetime of the fd or handle.
++ * In contrast, %FW_CDEV_IOC_ALLOCATE_ISO_RESOURCE_ONCE allocates iso resources
++ * for the duration of a bus generation.
++ *
++ * @channels is a host-endian bitfield with the least significant bit
++ * representing channel 0 and the most significant bit representing channel 63:
++ * 1ULL << c for each channel c that is a candidate for (de)allocation.
++ *
++ * @bandwidth is expressed in bandwidth allocation units, i.e. the time to send
++ * one quadlet of data (payload or header data) at speed S1600.
++ */
++struct fw_cdev_allocate_iso_resource {
++ __u64 closure;
++ __u64 channels;
++ __u32 bandwidth;
++ __u32 handle;
++};
++
++/**
++ * struct fw_cdev_get_speed - Query maximum speed to or from this device
++ * @max_speed: Speed code; minimum of the device's link speed, the local node's
++ * link speed, and all PHY port speeds between the two links
++ */
++struct fw_cdev_get_speed {
++ __u32 max_speed;
++};
++
+ #endif /* _LINUX_FIREWIRE_CDEV_H */
diff --git a/linux-2.6-g5-therm-shutdown.patch b/linux-2.6-g5-therm-shutdown.patch
new file mode 100644
index 0000000..1471ef1
--- /dev/null
+++ b/linux-2.6-g5-therm-shutdown.patch
@@ -0,0 +1,70 @@
+--- linux-2.6.15/drivers/macintosh/therm_pm72.c.orig 2006-04-02 21:34:48.000000000 +0100
++++ linux-2.6.15/drivers/macintosh/therm_pm72.c 2006-04-02 22:33:27.000000000 +0100
+@@ -924,10 +925,16 @@ static void do_monitor_cpu_combined(void
+ printk(KERN_WARNING "Warning ! Temperature way above maximum (%d) !\n",
+ temp_combi >> 16);
+ state0->overtemp += CPU_MAX_OVERTEMP / 4;
+- } else if (temp_combi > (state0->mpu.tmax << 16))
++ } else if (temp_combi > (state0->mpu.tmax << 16)) {
+ state0->overtemp++;
+- else
++ printk(KERN_WARNING "Temperature %d above max %d. overtemp %d\n",
++ temp_combi >> 16, state0->mpu.tmax, state0->overtemp);
++ } else {
++ if (state0->overtemp)
++ printk(KERN_WARNING "Temperature back down to %d\n",
++ temp_combi >> 16);
+ state0->overtemp = 0;
++ }
+ if (state0->overtemp >= CPU_MAX_OVERTEMP)
+ critical_state = 1;
+ if (state0->overtemp > 0) {
+@@ -999,10 +1015,16 @@ static void do_monitor_cpu_split(struct
+ " (%d) !\n",
+ state->index, temp >> 16);
+ state->overtemp += CPU_MAX_OVERTEMP / 4;
+- } else if (temp > (state->mpu.tmax << 16))
++ } else if (temp > (state->mpu.tmax << 16)) {
+ state->overtemp++;
+- else
++ printk(KERN_WARNING "CPU %d temperature %d above max %d. overtemp %d\n",
++ state->index, temp >> 16, state->mpu.tmax, state->overtemp);
++ } else {
++ if (state->overtemp)
++ printk(KERN_WARNING "CPU %d temperature back down to %d\n",
++ state->index, temp >> 16);
+ state->overtemp = 0;
++ }
+ if (state->overtemp >= CPU_MAX_OVERTEMP)
+ critical_state = 1;
+ if (state->overtemp > 0) {
+@@ -1061,10 +1097,16 @@ static void do_monitor_cpu_rack(struct c
+ " (%d) !\n",
+ state->index, temp >> 16);
+ state->overtemp = CPU_MAX_OVERTEMP / 4;
+- } else if (temp > (state->mpu.tmax << 16))
++ } else if (temp > (state->mpu.tmax << 16)) {
+ state->overtemp++;
+- else
++ printk(KERN_WARNING "CPU %d temperature %d above max %d. overtemp %d\n",
++ state->index, temp >> 16, state->mpu.tmax, state->overtemp);
++ } else {
++ if (state->overtemp)
++ printk(KERN_WARNING "CPU %d temperature back down to %d\n",
++ state->index, temp >> 16);
+ state->overtemp = 0;
++ }
+ if (state->overtemp >= CPU_MAX_OVERTEMP)
+ critical_state = 1;
+ if (state->overtemp > 0) {
+--- linux-2.6.15/drivers/macintosh/therm_pm72.h~ 2006-01-03 03:21:10.000000000 +0000
++++ linux-2.6.15/drivers/macintosh/therm_pm72.h 2006-04-02 22:25:58.000000000 +0100
+@@ -243,7 +243,7 @@ struct dimm_pid_state
+ #define CPU_TEMP_HISTORY_SIZE 2
+ #define CPU_POWER_HISTORY_SIZE 10
+ #define CPU_PID_INTERVAL 1
+-#define CPU_MAX_OVERTEMP 30
++#define CPU_MAX_OVERTEMP 90
+
+ #define CPUA_PUMP_RPM_INDEX 7
+ #define CPUB_PUMP_RPM_INDEX 8
diff --git a/linux-2.6-hotfixes.patch b/linux-2.6-hotfixes.patch
new file mode 100644
index 0000000..257cc56
--- /dev/null
+++ b/linux-2.6-hotfixes.patch
@@ -0,0 +1 @@
+foo
diff --git a/linux-2.6-imac-transparent-bridge.patch b/linux-2.6-imac-transparent-bridge.patch
new file mode 100755
index 0000000..7fd5360
--- /dev/null
+++ b/linux-2.6-imac-transparent-bridge.patch
@@ -0,0 +1,15 @@
+--- linux/arch/powerpc/platforms/powermac/pci.c~ 2008-03-22 19:08:07.000000000 +0000
++++ linux/arch/powerpc/platforms/powermac/pci.c 2008-03-23 09:10:46.000000000 +0000
+@@ -1271,6 +1271,12 @@ void pmac_pci_fixup_pciata(struct pci_de
+ }
+ }
+ DECLARE_PCI_FIXUP_EARLY(PCI_ANY_ID, PCI_ANY_ID, pmac_pci_fixup_pciata);
++#else /* CONFIG_PPC64 */
++static void __devinit imac_transparent_bridge(struct pci_dev *dev)
++{
++ dev->transparent = 1;
++}
++DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_APPLE, 0x005b, imac_transparent_bridge);
+ #endif /* CONFIG_PPC32 */
+
+ /*
diff --git a/linux-2.6-input-fix-toshiba-hotkeys.patch b/linux-2.6-input-fix-toshiba-hotkeys.patch
new file mode 100644
index 0000000..74558e6
--- /dev/null
+++ b/linux-2.6-input-fix-toshiba-hotkeys.patch
@@ -0,0 +1,278 @@
+commit 61a2aa30877a6e2be1d3fb3a71385e1f741819d7
+Author: Matthew Garrett <mjg@redhat.com>
+Date: Fri Mar 6 00:25:45 2009 +0000
+
+ toshiba-acpi: Add support for hotkey notifications
+
+ Calling the ENAB method on Toshiba laptops results in notifications being
+ sent when laptop hotkeys are pressed. This patch simply calls that method
+ and sets up an input device if it's successful.
+
+ Signed-off-by: Matthew Garrett <mjg@redhat.com>
+
+diff --git a/drivers/platform/x86/toshiba_acpi.c b/drivers/platform/x86/toshiba_acpi.c
+index 40e60fc..604f9fa 100644
+--- a/drivers/platform/x86/toshiba_acpi.c
++++ b/drivers/platform/x86/toshiba_acpi.c
+@@ -46,6 +46,7 @@
+ #include <linux/platform_device.h>
+ #include <linux/rfkill.h>
+ #include <linux/input-polldev.h>
++#include <linux/input.h>
+
+ #include <asm/uaccess.h>
+
+@@ -62,9 +63,10 @@ MODULE_LICENSE("GPL");
+
+ /* Toshiba ACPI method paths */
+ #define METHOD_LCD_BRIGHTNESS "\\_SB_.PCI0.VGA_.LCD_._BCM"
+-#define METHOD_HCI_1 "\\_SB_.VALD.GHCI"
+-#define METHOD_HCI_2 "\\_SB_.VALZ.GHCI"
++#define TOSH_INTERFACE_1 "\\_SB_.VALD"
++#define TOSH_INTERFACE_2 "\\_SB_.VALZ"
+ #define METHOD_VIDEO_OUT "\\_SB_.VALX.DSSX"
++#define GHCI_METHOD ".GHCI"
+
+ /* Toshiba HCI interface definitions
+ *
+@@ -116,6 +118,36 @@ static const struct acpi_device_id toshiba_device_ids[] = {
+ };
+ MODULE_DEVICE_TABLE(acpi, toshiba_device_ids);
+
++struct key_entry {
++ char type;
++ u16 code;
++ u16 keycode;
++};
++
++enum {KE_KEY, KE_END};
++
++static struct key_entry toshiba_acpi_keymap[] = {
++ {KE_KEY, 0x101, KEY_MUTE},
++ {KE_KEY, 0x13b, KEY_COFFEE},
++ {KE_KEY, 0x13c, KEY_BATTERY},
++ {KE_KEY, 0x13d, KEY_SLEEP},
++ {KE_KEY, 0x13e, KEY_SUSPEND},
++ {KE_KEY, 0x13f, KEY_SWITCHVIDEOMODE},
++ {KE_KEY, 0x140, KEY_BRIGHTNESSDOWN},
++ {KE_KEY, 0x141, KEY_BRIGHTNESSUP},
++ {KE_KEY, 0x142, KEY_WLAN},
++ {KE_KEY, 0x143, KEY_PROG1},
++ {KE_KEY, 0xb05, KEY_PROG2},
++ {KE_KEY, 0xb06, KEY_WWW},
++ {KE_KEY, 0xb07, KEY_MAIL},
++ {KE_KEY, 0xb30, KEY_STOP},
++ {KE_KEY, 0xb31, KEY_PREVIOUSSONG},
++ {KE_KEY, 0xb32, KEY_NEXTSONG},
++ {KE_KEY, 0xb33, KEY_PLAYPAUSE},
++ {KE_KEY, 0xb5a, KEY_MEDIA},
++ {KE_END, 0, 0},
++};
++
+ /* utility
+ */
+
+@@ -252,6 +284,8 @@ struct toshiba_acpi_dev {
+ struct platform_device *p_dev;
+ struct rfkill *rfk_dev;
+ struct input_polled_dev *poll_dev;
++ struct input_dev *hotkey_dev;
++ acpi_handle handle;
+
+ const char *bt_name;
+ const char *rfk_name;
+@@ -702,6 +736,154 @@ static struct backlight_ops toshiba_backlight_data = {
+ .update_status = set_lcd_status,
+ };
+
++static struct key_entry *toshiba_acpi_get_entry_by_scancode(int code)
++{
++ struct key_entry *key;
++
++ for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
++ if (code == key->code)
++ return key;
++
++ return NULL;
++}
++
++static struct key_entry *toshiba_acpi_get_entry_by_keycode(int code)
++{
++ struct key_entry *key;
++
++ for (key = toshiba_acpi_keymap; key->type != KE_END; key++)
++ if (code == key->keycode && key->type == KE_KEY)
++ return key;
++
++ return NULL;
++}
++
++static int toshiba_acpi_getkeycode(struct input_dev *dev, int scancode,
++ int *keycode)
++{
++ struct key_entry *key = toshiba_acpi_get_entry_by_scancode(scancode);
++
++ if (key && key->type == KE_KEY) {
++ *keycode = key->keycode;
++ return 0;
++ }
++
++ return -EINVAL;
++}
++
++static int toshiba_acpi_setkeycode(struct input_dev *dev, int scancode,
++ int keycode)
++{
++ struct key_entry *key;
++ int old_keycode;
++
++ if (keycode < 0 || keycode > KEY_MAX)
++ return -EINVAL;
++
++ key = toshiba_acpi_get_entry_by_scancode(scancode);
++ if (key && key->type == KE_KEY) {
++ old_keycode = key->keycode;
++ key->keycode = keycode;
++ set_bit(keycode, dev->keybit);
++ if (!toshiba_acpi_get_entry_by_keycode(old_keycode))
++ clear_bit(old_keycode, dev->keybit);
++ return 0;
++ }
++
++ return -EINVAL;
++}
++
++static void toshiba_acpi_notify(acpi_handle handle, u32 event, void *data)
++{
++ u32 hci_result, value;
++ struct key_entry *key;
++
++ if (event != 0x80)
++ return;
++ do {
++ hci_read1(HCI_SYSTEM_EVENT, &value, &hci_result);
++ if (hci_result == HCI_SUCCESS) {
++ if (value == 0x100)
++ continue;
++ else if (value & 0x80) {
++ key = toshiba_acpi_get_entry_by_scancode
++ (value & ~0x80);
++ if (!key) {
++ printk(MY_INFO "Unknown key %x\n",
++ value & ~0x80);
++ continue;
++ }
++ input_report_key(toshiba_acpi.hotkey_dev,
++ key->keycode, 1);
++ input_sync(toshiba_acpi.hotkey_dev);
++ input_report_key(toshiba_acpi.hotkey_dev,
++ key->keycode, 0);
++ input_sync(toshiba_acpi.hotkey_dev);
++ }
++ } else if (hci_result == HCI_NOT_SUPPORTED) {
++ /* This is a workaround for an unresolved issue on
++ * some machines where system events sporadically
++ * become disabled. */
++ hci_write1(HCI_SYSTEM_EVENT, 1, &hci_result);
++ printk(MY_NOTICE "Re-enabled hotkeys\n");
++ }
++ } while (hci_result != HCI_EMPTY);
++}
++
++static int toshiba_acpi_setup_keyboard(char *device)
++{
++ acpi_status status;
++ acpi_handle handle;
++ int result;
++ const struct key_entry *key;
++
++ status = acpi_get_handle(NULL, device, &handle);
++ if (ACPI_FAILURE(status)) {
++ printk(MY_INFO "Unable to get notification device\n");
++ return -ENODEV;
++ }
++
++ toshiba_acpi.handle = handle;
++
++ status = acpi_evaluate_object(handle, "ENAB", NULL, NULL);
++ if (ACPI_FAILURE(status)) {
++ printk(MY_INFO "Unable to enable hotkeys\n");
++ return -ENODEV;
++ }
++
++ status = acpi_install_notify_handler (handle, ACPI_DEVICE_NOTIFY,
++ toshiba_acpi_notify, NULL);
++ if (ACPI_FAILURE(status)) {
++ printk(MY_INFO "Unable to install hotkey notification\n");
++ return -ENODEV;
++ }
++
++ toshiba_acpi.hotkey_dev = input_allocate_device();
++ if (!toshiba_acpi.hotkey_dev) {
++ printk(MY_INFO "Unable to register input device\n");
++ return -ENOMEM;
++ }
++
++ toshiba_acpi.hotkey_dev->name = "Toshiba input device";
++ toshiba_acpi.hotkey_dev->phys = device;
++ toshiba_acpi.hotkey_dev->id.bustype = BUS_HOST;
++ toshiba_acpi.hotkey_dev->getkeycode = toshiba_acpi_getkeycode;
++ toshiba_acpi.hotkey_dev->setkeycode = toshiba_acpi_setkeycode;
++
++ for (key = toshiba_acpi_keymap; key->type != KE_END; key++) {
++ set_bit(EV_KEY, toshiba_acpi.hotkey_dev->evbit);
++ set_bit(key->keycode, toshiba_acpi.hotkey_dev->keybit);
++ }
++
++ result = input_register_device(toshiba_acpi.hotkey_dev);
++ if (result) {
++ printk(MY_INFO "Unable to register input device\n");
++ return result;
++ }
++
++ return 0;
++}
++
+ static void toshiba_acpi_exit(void)
+ {
+ if (toshiba_acpi.poll_dev) {
+@@ -709,12 +891,18 @@ static void toshiba_acpi_exit(void)
+ input_free_polled_device(toshiba_acpi.poll_dev);
+ }
+
++ if (toshiba_acpi.hotkey_dev)
++ input_unregister_device(toshiba_acpi.hotkey_dev);
++
+ if (toshiba_acpi.rfk_dev)
+ rfkill_unregister(toshiba_acpi.rfk_dev);
+
+ if (toshiba_backlight_device)
+ backlight_device_unregister(toshiba_backlight_device);
+
++ acpi_remove_notify_handler(toshiba_acpi.handle, ACPI_DEVICE_NOTIFY,
++ toshiba_acpi_notify);
++
+ remove_device();
+
+ if (toshiba_proc_dir)
+@@ -738,11 +926,15 @@ static int __init toshiba_acpi_init(void)
+ return -ENODEV;
+
+ /* simple device detection: look for HCI method */
+- if (is_valid_acpi_path(METHOD_HCI_1))
+- method_hci = METHOD_HCI_1;
+- else if (is_valid_acpi_path(METHOD_HCI_2))
+- method_hci = METHOD_HCI_2;
+- else
++ if (is_valid_acpi_path(TOSH_INTERFACE_1 GHCI_METHOD)) {
++ method_hci = TOSH_INTERFACE_1 GHCI_METHOD;
++ if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_1))
++ printk(MY_INFO "Unable to activate hotkeys\n");
++ } else if (is_valid_acpi_path(TOSH_INTERFACE_2 GHCI_METHOD)) {
++ method_hci = TOSH_INTERFACE_2 GHCI_METHOD;
++ if (toshiba_acpi_setup_keyboard(TOSH_INTERFACE_2))
++ printk(MY_INFO "Unable to activate hotkeys\n");
++ } else
+ return -ENODEV;
+
+ printk(MY_INFO "Toshiba Laptop ACPI Extras version %s\n",
diff --git a/linux-2.6-input-hid-quirk-egalax.patch b/linux-2.6-input-hid-quirk-egalax.patch
new file mode 100644
index 0000000..db38685
--- /dev/null
+++ b/linux-2.6-input-hid-quirk-egalax.patch
@@ -0,0 +1,41 @@
+Date: Mon, 1 Feb 2010 12:53:47 +1300
+From: Peter Hutterer <peter.hutterer@redhat.com>
+To: Dave Airlie <airlied@redhat.com>
+Subject: [PATCH] HID: add multi-input quirk for eGalax Touchcontroller
+
+Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
+Tested-by: Alfred Broda <guaranga@wp.pl>
+---
+ drivers/hid/hid-ids.h | 3 +++
+ drivers/hid/usbhid/hid-quirks.c | 1 +
+ 2 files changed, 4 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h
+index f5144b8..2e698a2 100644
+--- a/drivers/hid/hid-ids.h
++++ b/drivers/hid/hid-ids.h
+@@ -158,6 +158,9 @@
+
+ #define USB_VENDOR_ID_DRAGONRISE 0x0079
+
++#define USB_VENDOR_ID_EGALAX 0x0EEF
++#define USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER 0x0001
++
+ #define USB_VENDOR_ID_ELO 0x04E7
+ #define USB_DEVICE_ID_ELO_TS2700 0x0020
+
+diff --git a/drivers/hid/usbhid/hid-quirks.c b/drivers/hid/usbhid/hid-quirks.c
+index e987562..dc27d74 100644
+--- a/drivers/hid/usbhid/hid-quirks.c
++++ b/drivers/hid/usbhid/hid-quirks.c
+@@ -32,6 +32,7 @@ static const struct hid_blacklist {
+ { USB_VENDOR_ID_AASHIMA, USB_DEVICE_ID_AASHIMA_PREDATOR, HID_QUIRK_BADPAD },
+ { USB_VENDOR_ID_ALPS, USB_DEVICE_ID_IBM_GAMEPAD, HID_QUIRK_BADPAD },
+ { USB_VENDOR_ID_CHIC, USB_DEVICE_ID_CHIC_GAMEPAD, HID_QUIRK_BADPAD },
++ { USB_VENDOR_ID_EGALAX, USB_DEVICE_ID_EGALAX_TOUCHCONTROLLER, HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_DRIVING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FLYING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
+ { USB_VENDOR_ID_HAPP, USB_DEVICE_ID_UGCI_FIGHTING, HID_QUIRK_BADPAD | HID_QUIRK_MULTI_INPUT },
+--
+1.6.5.2
+
diff --git a/linux-2.6-input-kill-stupid-messages.patch b/linux-2.6-input-kill-stupid-messages.patch
new file mode 100644
index 0000000..2e63146
--- /dev/null
+++ b/linux-2.6-input-kill-stupid-messages.patch
@@ -0,0 +1,17 @@
+--- linux-2.6.21.noarch/drivers/input/keyboard/atkbd.c~ 2007-07-06 10:51:04.000000000 -0400
++++ linux-2.6.21.noarch/drivers/input/keyboard/atkbd.c 2007-07-06 10:51:33.000000000 -0400
+@@ -409,10 +409,14 @@ static irqreturn_t atkbd_interrupt(struc
+ goto out;
+ case ATKBD_RET_ACK:
+ case ATKBD_RET_NAK:
++#if 0
++ /* Quite a few key switchers and other tools trigger this and it confuses
++ people who can do nothing about it */
+ if (printk_ratelimit())
+ printk(KERN_WARNING "atkbd.c: Spurious %s on %s. "
+ "Some program might be trying access hardware directly.\n",
+ data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);
++#endif
+ goto out;
+ case ATKBD_RET_ERR:
+ atkbd->err_count++;
diff --git a/linux-2.6-ksm-kvm.patch b/linux-2.6-ksm-kvm.patch
new file mode 100644
index 0000000..9fac2a8
--- /dev/null
+++ b/linux-2.6-ksm-kvm.patch
@@ -0,0 +1,314 @@
+When using mmu notifiers, we are allowed to remove the page count
+reference tooken by get_user_pages to a specific page that is mapped
+inside the shadow page tables.
+
+This is needed so we can balance the pagecount against mapcount
+checking.
+
+(Right now kvm increase the pagecount and does not increase the
+mapcount when mapping page into shadow page table entry,
+so when comparing pagecount against mapcount, you have no
+reliable result.)
+
+add SPTE_HOST_WRITEABLE flag notify that the host physical page we are
+pointing to from the spte is write protected, and therefore we cant
+change its access to be write unless we run get_user_pages(write = 1).
+
+(this is needed for change_pte support in kvm)
+
+support for change_pte mmu notifiers is needed for kvm if it want ksm to
+directly map pages into its shadow page tables.
+
+Signed-off-by: Izik Eidus <ieidus@redhat.com>
+Signed-off-by: Justin M. Forbes <jforbes@redhat.com>
+---
+--- linux-2.6.30.x86_64/arch/x86/include/asm/kvm_host.h 2009-08-20 10:37:37.784886414 -0500
++++ linux-2.6.30.x86_64.kvm/arch/x86/include/asm/kvm_host.h 2009-08-20 10:39:33.742641558 -0500
+@@ -796,5 +796,6 @@ asmlinkage void kvm_handle_fault_on_rebo
+ int kvm_unmap_hva(struct kvm *kvm, unsigned long hva);
+ int kvm_age_hva(struct kvm *kvm, unsigned long hva);
+ int cpuid_maxphyaddr(struct kvm_vcpu *vcpu);
++void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte);
+
+ #endif /* _ASM_X86_KVM_HOST_H */
+--- linux-2.6.30.x86_64/arch/x86/kvm/mmu.c 2009-08-20 10:37:37.964887039 -0500
++++ linux-2.6.30.x86_64.kvm/arch/x86/kvm/mmu.c 2009-08-20 10:41:15.231638028 -0500
+@@ -139,6 +139,8 @@ module_param(oos_shadow, bool, 0644);
+ #define ACC_USER_MASK PT_USER_MASK
+ #define ACC_ALL (ACC_EXEC_MASK | ACC_WRITE_MASK | ACC_USER_MASK)
+
++#define SPTE_HOST_WRITEABLE (1ULL << PT_FIRST_AVAIL_BITS_SHIFT)
++
+ #define SHADOW_PT_INDEX(addr, level) PT64_INDEX(addr, level)
+
+ struct kvm_rmap_desc {
+@@ -254,6 +256,11 @@ static pfn_t spte_to_pfn(u64 pte)
+ return (pte & PT64_BASE_ADDR_MASK) >> PAGE_SHIFT;
+ }
+
++static pte_t ptep_val(pte_t *ptep)
++{
++ return *ptep;
++}
++
+ static gfn_t pse36_gfn_delta(u32 gpte)
+ {
+ int shift = 32 - PT32_DIR_PSE36_SHIFT - PAGE_SHIFT;
+@@ -573,9 +580,7 @@ static void rmap_remove(struct kvm *kvm,
+ if (*spte & shadow_accessed_mask)
+ kvm_set_pfn_accessed(pfn);
+ if (is_writeble_pte(*spte))
+- kvm_release_pfn_dirty(pfn);
+- else
+- kvm_release_pfn_clean(pfn);
++ kvm_set_pfn_dirty(pfn);
+ rmapp = gfn_to_rmap(kvm, sp->gfns[spte - sp->spt], is_large_pte(*spte));
+ if (!*rmapp) {
+ printk(KERN_ERR "rmap_remove: %p %llx 0->BUG\n", spte, *spte);
+@@ -684,7 +689,8 @@ static int rmap_write_protect(struct kvm
+ return write_protected;
+ }
+
+-static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp)
++static int kvm_unmap_rmapp(struct kvm *kvm, unsigned long *rmapp,
++ unsigned long data)
+ {
+ u64 *spte;
+ int need_tlb_flush = 0;
+@@ -699,8 +705,48 @@ static int kvm_unmap_rmapp(struct kvm *k
+ return need_tlb_flush;
+ }
+
++static int kvm_set_pte_rmapp(struct kvm *kvm, unsigned long *rmapp,
++ unsigned long data)
++{
++ int need_flush = 0;
++ u64 *spte, new_spte;
++ pte_t *ptep = (pte_t *)data;
++ pfn_t new_pfn;
++
++ new_pfn = pte_pfn(ptep_val(ptep));
++ spte = rmap_next(kvm, rmapp, NULL);
++ while (spte) {
++ BUG_ON(!is_shadow_present_pte(*spte));
++ rmap_printk("kvm_set_pte_rmapp: spte %p %llx\n", spte, *spte);
++ need_flush = 1;
++ if (pte_write(ptep_val(ptep))) {
++ rmap_remove(kvm, spte);
++ set_shadow_pte(spte, shadow_trap_nonpresent_pte);
++ spte = rmap_next(kvm, rmapp, NULL);
++ } else {
++ new_spte = *spte &~ (PT64_BASE_ADDR_MASK);
++ new_spte |= new_pfn << PAGE_SHIFT;
++
++ if (!pte_write(ptep_val(ptep))) {
++ new_spte &= ~PT_WRITABLE_MASK;
++ new_spte &= ~SPTE_HOST_WRITEABLE;
++ if (is_writeble_pte(*spte))
++ kvm_set_pfn_dirty(spte_to_pfn(*spte));
++ }
++ set_shadow_pte(spte, new_spte);
++ spte = rmap_next(kvm, rmapp, spte);
++ }
++ }
++ if (need_flush)
++ kvm_flush_remote_tlbs(kvm);
++
++ return 0;
++}
++
+ static int kvm_handle_hva(struct kvm *kvm, unsigned long hva,
+- int (*handler)(struct kvm *kvm, unsigned long *rmapp))
++ unsigned long data,
++ int (*handler)(struct kvm *kvm, unsigned long *rmapp,
++ unsigned long data))
+ {
+ int i;
+ int retval = 0;
+@@ -721,11 +767,13 @@ static int kvm_handle_hva(struct kvm *kv
+ end = start + (memslot->npages << PAGE_SHIFT);
+ if (hva >= start && hva < end) {
+ gfn_t gfn_offset = (hva - start) >> PAGE_SHIFT;
+- retval |= handler(kvm, &memslot->rmap[gfn_offset]);
++ retval |= handler(kvm, &memslot->rmap[gfn_offset],
++ data);
+ retval |= handler(kvm,
+ &memslot->lpage_info[
+ gfn_offset /
+- KVM_PAGES_PER_HPAGE].rmap_pde);
++ KVM_PAGES_PER_HPAGE].rmap_pde,
++ data);
+ }
+ }
+
+@@ -734,10 +782,16 @@ static int kvm_handle_hva(struct kvm *kv
+
+ int kvm_unmap_hva(struct kvm *kvm, unsigned long hva)
+ {
+- return kvm_handle_hva(kvm, hva, kvm_unmap_rmapp);
++ return kvm_handle_hva(kvm, hva, 0, kvm_unmap_rmapp);
++}
++
++void kvm_set_spte_hva(struct kvm *kvm, unsigned long hva, pte_t pte)
++{
++ kvm_handle_hva(kvm, hva, (unsigned long)&pte, kvm_set_pte_rmapp);
+ }
+
+-static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp)
++static int kvm_age_rmapp(struct kvm *kvm, unsigned long *rmapp,
++ unsigned long data)
+ {
+ u64 *spte;
+ int young = 0;
+@@ -770,13 +824,13 @@ static void rmap_recycle(struct kvm_vcpu
+ gfn = unalias_gfn(vcpu->kvm, gfn);
+ rmapp = gfn_to_rmap(vcpu->kvm, gfn, lpage);
+
+- kvm_unmap_rmapp(vcpu->kvm, rmapp);
++ kvm_unmap_rmapp(vcpu->kvm, rmapp, 0);
+ kvm_flush_remote_tlbs(vcpu->kvm);
+ }
+
+ int kvm_age_hva(struct kvm *kvm, unsigned long hva)
+ {
+- return kvm_handle_hva(kvm, hva, kvm_age_rmapp);
++ return kvm_handle_hva(kvm, hva, 0, kvm_age_rmapp);
+ }
+
+ #ifdef MMU_DEBUG
+@@ -1686,7 +1740,7 @@ static int set_spte(struct kvm_vcpu *vcp
+ unsigned pte_access, int user_fault,
+ int write_fault, int dirty, int largepage,
+ gfn_t gfn, pfn_t pfn, bool speculative,
+- bool can_unsync)
++ bool can_unsync, bool reset_host_protection)
+ {
+ u64 spte;
+ int ret = 0;
+@@ -1744,6 +1798,8 @@ static int set_spte(struct kvm_vcpu *vcp
+ spte &= ~PT_WRITABLE_MASK;
+ }
+ }
++ if (reset_host_protection)
++ spte |= SPTE_HOST_WRITEABLE;
+
+ if (pte_access & ACC_WRITE_MASK)
+ mark_page_dirty(vcpu->kvm, gfn);
+@@ -1757,7 +1813,8 @@ static void mmu_set_spte(struct kvm_vcpu
+ unsigned pt_access, unsigned pte_access,
+ int user_fault, int write_fault, int dirty,
+ int *ptwrite, int largepage, gfn_t gfn,
+- pfn_t pfn, bool speculative)
++ pfn_t pfn, bool speculative,
++ bool reset_host_protection)
+ {
+ int was_rmapped = 0;
+ int was_writeble = is_writeble_pte(*shadow_pte);
+@@ -1787,7 +1844,8 @@ static void mmu_set_spte(struct kvm_vcpu
+ was_rmapped = 1;
+ }
+ if (set_spte(vcpu, shadow_pte, pte_access, user_fault, write_fault,
+- dirty, largepage, gfn, pfn, speculative, true)) {
++ dirty, largepage, gfn, pfn, speculative, true,
++ reset_host_protection)) {
+ if (write_fault)
+ *ptwrite = 1;
+ kvm_x86_ops->tlb_flush(vcpu);
+@@ -1804,8 +1862,7 @@ static void mmu_set_spte(struct kvm_vcpu
+ page_header_update_slot(vcpu->kvm, shadow_pte, gfn);
+ if (!was_rmapped) {
+ rmap_count = rmap_add(vcpu, shadow_pte, gfn, largepage);
+- if (!is_rmap_pte(*shadow_pte))
+- kvm_release_pfn_clean(pfn);
++ kvm_release_pfn_clean(pfn);
+ if (rmap_count > RMAP_RECYCLE_THRESHOLD)
+ rmap_recycle(vcpu, gfn, largepage);
+ } else {
+@@ -1837,7 +1894,7 @@ static int __direct_map(struct kvm_vcpu
+ || (largepage && iterator.level == PT_DIRECTORY_LEVEL)) {
+ mmu_set_spte(vcpu, iterator.sptep, ACC_ALL, ACC_ALL,
+ 0, write, 1, &pt_write,
+- largepage, gfn, pfn, false);
++ largepage, gfn, pfn, false, true);
+ ++vcpu->stat.pf_fixed;
+ break;
+ }
+--- linux-2.6.30.x86_64/arch/x86/kvm/paging_tmpl.h 2009-08-20 10:37:37.966889166 -0500
++++ linux-2.6.30.x86_64.kvm/arch/x86/kvm/paging_tmpl.h 2009-08-20 10:39:33.747636180 -0500
+@@ -266,9 +266,13 @@ static void FNAME(update_pte)(struct kvm
+ if (mmu_notifier_retry(vcpu, vcpu->arch.update_pte.mmu_seq))
+ return;
+ kvm_get_pfn(pfn);
++ /*
++ * we call mmu_set_spte() with reset_host_protection = true beacuse that
++ * vcpu->arch.update_pte.pfn was fetched from get_user_pages(write = 1).
++ */
+ mmu_set_spte(vcpu, spte, page->role.access, pte_access, 0, 0,
+ gpte & PT_DIRTY_MASK, NULL, largepage,
+- gpte_to_gfn(gpte), pfn, true);
++ gpte_to_gfn(gpte), pfn, true, true);
+ }
+
+ /*
+@@ -302,7 +306,7 @@ static u64 *FNAME(fetch)(struct kvm_vcpu
+ user_fault, write_fault,
+ gw->ptes[gw->level-1] & PT_DIRTY_MASK,
+ ptwrite, largepage,
+- gw->gfn, pfn, false);
++ gw->gfn, pfn, false, true);
+ break;
+ }
+
+@@ -552,6 +556,7 @@ static void FNAME(prefetch_page)(struct
+ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp)
+ {
+ int i, offset, nr_present;
++ bool reset_host_protection = 1;
+
+ offset = nr_present = 0;
+
+@@ -589,9 +594,13 @@ static int FNAME(sync_page)(struct kvm_v
+
+ nr_present++;
+ pte_access = sp->role.access & FNAME(gpte_access)(vcpu, gpte);
++ if (!(sp->spt[i] & SPTE_HOST_WRITEABLE)) {
++ pte_access &= ~PT_WRITABLE_MASK;
++ reset_host_protection = 0;
++ } else { reset_host_protection = 1; }
+ set_spte(vcpu, &sp->spt[i], pte_access, 0, 0,
+ is_dirty_pte(gpte), 0, gfn,
+- spte_to_pfn(sp->spt[i]), true, false);
++ spte_to_pfn(sp->spt[i]), true, false, reset_host_protection);
+ }
+
+ return !nr_present;
+--- linux-2.6.30.x86_64/virt/kvm/kvm_main.c 2009-08-20 10:37:45.448886340 -0500
++++ linux-2.6.30.x86_64.kvm/virt/kvm/kvm_main.c 2009-08-20 10:39:33.749636212 -0500
+@@ -859,6 +859,19 @@ static void kvm_mmu_notifier_invalidate_
+
+ }
+
++static void kvm_mmu_notifier_change_pte(struct mmu_notifier *mn,
++ struct mm_struct *mm,
++ unsigned long address,
++ pte_t pte)
++{
++ struct kvm *kvm = mmu_notifier_to_kvm(mn);
++
++ spin_lock(&kvm->mmu_lock);
++ kvm->mmu_notifier_seq++;
++ kvm_set_spte_hva(kvm, address, pte);
++ spin_unlock(&kvm->mmu_lock);
++}
++
+ static void kvm_mmu_notifier_invalidate_range_start(struct mmu_notifier *mn,
+ struct mm_struct *mm,
+ unsigned long start,
+@@ -938,6 +951,7 @@ static const struct mmu_notifier_ops kvm
+ .invalidate_range_start = kvm_mmu_notifier_invalidate_range_start,
+ .invalidate_range_end = kvm_mmu_notifier_invalidate_range_end,
+ .clear_flush_young = kvm_mmu_notifier_clear_flush_young,
++ .change_pte = kvm_mmu_notifier_change_pte,
+ .release = kvm_mmu_notifier_release,
+ };
+ #endif /* CONFIG_MMU_NOTIFIER && KVM_ARCH_WANT_MMU_NOTIFIER */
diff --git a/linux-2.6-makefile-after_link.patch b/linux-2.6-makefile-after_link.patch
new file mode 100644
index 0000000..94b71f9
--- /dev/null
+++ b/linux-2.6-makefile-after_link.patch
@@ -0,0 +1,57 @@
+diff --git a/Makefile b/Makefile
+index f908acc..960ff6f 100644
+--- a/Makefile
++++ b/Makefile
+@@ -746,6 +746,10 @@ quiet_cmd_vmlinux__ ?= LD $@
+ --start-group $(vmlinux-main) --end-group \
+ $(filter-out $(vmlinux-lds) $(vmlinux-init) $(vmlinux-main) vmlinux.o FORCE ,$^)
+
++ifdef AFTER_LINK
++cmd_vmlinux__ += ; $(AFTER_LINK)
++endif
++
+ # Generate new vmlinux version
+ quiet_cmd_vmlinux_version = GEN .version
+ cmd_vmlinux_version = set -e; \
+diff --git a/arch/powerpc/kernel/vdso32/Makefile b/arch/powerpc/kernel/vdso32/Makefile
+index 51ead52..ad21273 100644
+--- a/arch/powerpc/kernel/vdso32/Makefile
++++ b/arch/powerpc/kernel/vdso32/Makefile
+@@ -41,7 +41,8 @@ $(obj-vdso32): %.o: %.S
+
+ # actual build commands
+ quiet_cmd_vdso32ld = VDSO32L $@
+- cmd_vdso32ld = $(CROSS32CC) $(c_flags) -Wl,-T $^ -o $@
++ cmd_vdso32ld = $(CROSS32CC) $(c_flags) -Wl,-T $^ -o $@ \
++ $(if $(AFTER_LINK),; $(AFTER_LINK))
+ quiet_cmd_vdso32as = VDSO32A $@
+ cmd_vdso32as = $(CROSS32CC) $(a_flags) -c -o $@ $<
+
+diff --git a/arch/powerpc/kernel/vdso64/Makefile b/arch/powerpc/kernel/vdso64/Makefile
+index 79da65d..f11c21b 100644
+--- a/arch/powerpc/kernel/vdso64/Makefile
++++ b/arch/powerpc/kernel/vdso64/Makefile
+@@ -36,7 +36,8 @@ $(obj-vdso64): %.o: %.S
+
+ # actual build commands
+ quiet_cmd_vdso64ld = VDSO64L $@
+- cmd_vdso64ld = $(CC) $(c_flags) -Wl,-T $^ -o $@
++ cmd_vdso64ld = $(CC) $(c_flags) -Wl,-T $^ -o $@ \
++ $(if $(AFTER_LINK),; $(AFTER_LINK))
+ quiet_cmd_vdso64as = VDSO64A $@
+ cmd_vdso64as = $(CC) $(a_flags) -c -o $@ $<
+
+diff --git a/arch/x86/vdso/Makefile b/arch/x86/vdso/Makefile
+index 6b4ffed..cbc3d05 100644
+--- a/arch/x86/vdso/Makefile
++++ b/arch/x86/vdso/Makefile
+@@ -120,7 +120,8 @@ $(obj)/vdso32-syms.lds: $(vdso32.so-y:%=$(obj)/vdso32-%-syms.lds) FORCE
+ quiet_cmd_vdso = VDSO $@
+ cmd_vdso = $(CC) -nostdlib -o $@ \
+ $(VDSO_LDFLAGS) $(VDSO_LDFLAGS_$(filter %.lds,$(^F))) \
+- -Wl,-T,$(filter %.lds,$^) $(filter %.o,$^)
++ -Wl,-T,$(filter %.lds,$^) $(filter %.o,$^) \
++ $(if $(AFTER_LINK),; $(AFTER_LINK))
+
+ VDSO_LDFLAGS = -fPIC -shared $(call cc-ldoption, -Wl$(comma)--hash-style=sysv)
+ GCOV_PROFILE := n
diff --git a/linux-2.6-nfs4-callback-hidden.patch b/linux-2.6-nfs4-callback-hidden.patch
new file mode 100644
index 0000000..8fc2368
--- /dev/null
+++ b/linux-2.6-nfs4-callback-hidden.patch
@@ -0,0 +1,20 @@
+Author: Steve Dickson <steved@redhat.com>
+Date: Tue Oct 13 15:59:57 EDT 2009
+
+To avoid hangs in the svc_unregister(), on version 4 mounts
+(and unmounts), when rpcbind is not running, make the nfs4 callback
+program an 'hidden' service by setting the 'vs_hidden' flag in the
+nfs4_callback_version structure.
+
+Signed-off-by: Steve Dickson <steved@redhat.com>
+
+diff -up linux-2.6.31.x86_64/fs/nfs/callback_xdr.c.orig linux-2.6.31.x86_64/fs/nfs/callback_xdr.c
+--- linux-2.6.31.x86_64/fs/nfs/callback_xdr.c.orig 2009-09-09 18:13:59.000000000 -0400
++++ linux-2.6.31.x86_64/fs/nfs/callback_xdr.c 2009-10-13 15:40:19.000000000 -0400
+@@ -716,5 +716,6 @@ struct svc_version nfs4_callback_version
+ .vs_proc = nfs4_callback_procedures1,
+ .vs_xdrsize = NFS4_CALLBACK_XDRSIZE,
+ .vs_dispatch = NULL,
++ .vs_hidden = 1,
+ };
+
diff --git a/linux-2.6-nfsd4-proots.patch b/linux-2.6-nfsd4-proots.patch
new file mode 100644
index 0000000..84d5894
--- /dev/null
+++ b/linux-2.6-nfsd4-proots.patch
@@ -0,0 +1,226 @@
+diff -up linux-2.6.32.i686/fs/nfsd/export.c.save linux-2.6.32.i686/fs/nfsd/export.c
+--- linux-2.6.32.i686/fs/nfsd/export.c.save 2009-12-04 10:24:17.000000000 -0500
++++ linux-2.6.32.i686/fs/nfsd/export.c 2009-12-04 10:40:52.000000000 -0500
+@@ -372,10 +372,12 @@ static struct svc_export *svc_export_loo
+ static int check_export(struct inode *inode, int flags, unsigned char *uuid)
+ {
+
+- /* We currently export only dirs and regular files.
+- * This is what umountd does.
++ /*
++ * We currently export only dirs, regular files, and (for v4
++ * pseudoroot) symlinks.
+ */
+ if (!S_ISDIR(inode->i_mode) &&
++ !S_ISLNK(inode->i_mode) &&
+ !S_ISREG(inode->i_mode))
+ return -ENOTDIR;
+
+@@ -1425,6 +1427,7 @@ static struct flags {
+ { NFSEXP_CROSSMOUNT, {"crossmnt", ""}},
+ { NFSEXP_NOSUBTREECHECK, {"no_subtree_check", ""}},
+ { NFSEXP_NOAUTHNLM, {"insecure_locks", ""}},
++ { NFSEXP_V4ROOT, {"v4root", ""}},
+ #ifdef MSNFS
+ { NFSEXP_MSNFS, {"msnfs", ""}},
+ #endif
+@@ -1505,7 +1508,7 @@ static int e_show(struct seq_file *m, vo
+ struct svc_export *exp = container_of(cp, struct svc_export, h);
+
+ if (p == SEQ_START_TOKEN) {
+- seq_puts(m, "# Version 1.1\n");
++ seq_puts(m, "# Version 1.2\n");
+ seq_puts(m, "# Path Client(Flags) # IPs\n");
+ return 0;
+ }
+diff -up linux-2.6.32.i686/fs/nfsd/nfs4xdr.c.save linux-2.6.32.i686/fs/nfsd/nfs4xdr.c
+--- linux-2.6.32.i686/fs/nfsd/nfs4xdr.c.save 2009-12-04 10:24:17.000000000 -0500
++++ linux-2.6.32.i686/fs/nfsd/nfs4xdr.c 2009-12-04 10:26:49.000000000 -0500
+@@ -2204,11 +2204,14 @@ nfsd4_encode_dirent_fattr(struct nfsd4_r
+ * we will not follow the cross mount and will fill the attribtutes
+ * directly from the mountpoint dentry.
+ */
+- if (d_mountpoint(dentry) && !attributes_need_mount(cd->rd_bmval))
+- ignore_crossmnt = 1;
+- else if (d_mountpoint(dentry)) {
++ if (nfsd_mountpoint(dentry, exp)) {
+ int err;
+
++ if (!(exp->ex_flags & NFSEXP_V4ROOT)
++ && !attributes_need_mount(cd->rd_bmval)) {
++ ignore_crossmnt = 1;
++ goto out_encode;
++ }
+ /*
+ * Why the heck aren't we just using nfsd_lookup??
+ * Different "."/".." handling? Something else?
+@@ -2224,6 +2227,7 @@ nfsd4_encode_dirent_fattr(struct nfsd4_r
+ goto out_put;
+
+ }
++out_encode:
+ nfserr = nfsd4_encode_fattr(NULL, exp, dentry, p, buflen, cd->rd_bmval,
+ cd->rd_rqstp, ignore_crossmnt);
+ out_put:
+diff -up linux-2.6.32.i686/fs/nfsd/nfsfh.c.save linux-2.6.32.i686/fs/nfsd/nfsfh.c
+--- linux-2.6.32.i686/fs/nfsd/nfsfh.c.save 2009-12-04 10:24:17.000000000 -0500
++++ linux-2.6.32.i686/fs/nfsd/nfsfh.c 2009-12-04 10:38:26.000000000 -0500
+@@ -109,6 +109,36 @@ static __be32 nfsd_setuser_and_check_por
+ return nfserrno(nfsd_setuser(rqstp, exp));
+ }
+
++static inline __be32 check_pseudo_root(struct svc_rqst *rqstp,
++ struct dentry *dentry, struct svc_export *exp)
++{
++ if (!(exp->ex_flags & NFSEXP_V4ROOT))
++ return nfs_ok;
++ /*
++ * v2/v3 clients have no need for the V4ROOT export--they use
++ * the mount protocl instead; also, further V4ROOT checks may be
++ * in v4-specific code, in which case v2/v3 clients could bypass
++ * them.
++ */
++ if (!nfsd_v4client(rqstp))
++ return nfserr_stale;
++ /*
++ * We're exposing only the directories and symlinks that have to be
++ * traversed on the way to real exports:
++ */
++ if (unlikely(!S_ISDIR(dentry->d_inode->i_mode) &&
++ !S_ISLNK(dentry->d_inode->i_mode)))
++ return nfserr_stale;
++ /*
++ * A pseudoroot export gives permission to access only one
++ * single directory; the kernel has to make another upcall
++ * before granting access to anything else under it:
++ */
++ if (unlikely(dentry != exp->ex_path.dentry))
++ return nfserr_stale;
++ return nfs_ok;
++}
++
+ /*
+ * Use the given filehandle to look up the corresponding export and
+ * dentry. On success, the results are used to set fh_export and
+@@ -317,6 +347,13 @@ fh_verify(struct svc_rqst *rqstp, struct
+ goto out;
+ }
+
++ /*
++ * Do some spoof checking if we are on the pseudo root
++ */
++ error = check_pseudo_root(rqstp, dentry, exp);
++ if (error)
++ goto out;
++
+ error = nfsd_mode_check(rqstp, dentry->d_inode->i_mode, type);
+ if (error)
+ goto out;
+diff -up linux-2.6.32.i686/fs/nfsd/vfs.c.save linux-2.6.32.i686/fs/nfsd/vfs.c
+--- linux-2.6.32.i686/fs/nfsd/vfs.c.save 2009-12-04 10:24:18.000000000 -0500
++++ linux-2.6.32.i686/fs/nfsd/vfs.c 2009-12-04 10:35:04.000000000 -0500
+@@ -89,12 +89,6 @@ struct raparm_hbucket {
+ #define RAPARM_HASH_MASK (RAPARM_HASH_SIZE-1)
+ static struct raparm_hbucket raparm_hash[RAPARM_HASH_SIZE];
+
+-static inline int
+-nfsd_v4client(struct svc_rqst *rq)
+-{
+- return rq->rq_prog == NFS_PROGRAM && rq->rq_vers == 4;
+-}
+-
+ /*
+ * Called from nfsd_lookup and encode_dirent. Check if we have crossed
+ * a mount point.
+@@ -116,8 +110,16 @@ nfsd_cross_mnt(struct svc_rqst *rqstp, s
+
+ exp2 = rqst_exp_get_by_name(rqstp, &path);
+ if (IS_ERR(exp2)) {
+- if (PTR_ERR(exp2) != -ENOENT)
+- err = PTR_ERR(exp2);
++ err = PTR_ERR(exp2);
++ /*
++ * We normally allow NFS clients to continue
++ * "underneath" a mountpoint that is not exported.
++ * The exception is V4ROOT, where no traversal is ever
++ * allowed without an explicit export of the new
++ * directory.
++ */
++ if (err == -ENOENT && !(exp->ex_flags & NFSEXP_V4ROOT))
++ err = 0;
+ path_put(&path);
+ goto out;
+ }
+@@ -141,6 +143,19 @@ out:
+ return err;
+ }
+
++/*
++ * For nfsd purposes, we treat V4ROOT exports as though there was an
++ * export at *every* directory.
++ */
++int nfsd_mountpoint(struct dentry *dentry, struct svc_export *exp)
++{
++ if (d_mountpoint(dentry))
++ return 1;
++ if (!(exp->ex_flags & NFSEXP_V4ROOT))
++ return 0;
++ return dentry->d_inode != NULL;
++}
++
+ __be32
+ nfsd_lookup_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp,
+ const char *name, unsigned int len,
+@@ -208,7 +223,7 @@ nfsd_lookup_dentry(struct svc_rqst *rqst
+ /*
+ * check if we have crossed a mount point ...
+ */
+- if (d_mountpoint(dentry)) {
++ if (nfsd_mountpoint(dentry, exp)) {
+ if ((host_err = nfsd_cross_mnt(rqstp, &dentry, &exp))) {
+ dput(dentry);
+ goto out_nfserr;
+diff -up linux-2.6.32.i686/include/linux/nfsd/export.h.save linux-2.6.32.i686/include/linux/nfsd/export.h
+--- linux-2.6.32.i686/include/linux/nfsd/export.h.save 2009-12-04 10:24:18.000000000 -0500
++++ linux-2.6.32.i686/include/linux/nfsd/export.h 2009-12-04 10:25:08.000000000 -0500
+@@ -39,7 +39,17 @@
+ #define NFSEXP_FSID 0x2000
+ #define NFSEXP_CROSSMOUNT 0x4000
+ #define NFSEXP_NOACL 0x8000 /* reserved for possible ACL related use */
+-#define NFSEXP_ALLFLAGS 0xFE3F
++/*
++ * The NFSEXP_V4ROOT flag causes the kernel to give access only to NFSv4
++ * clients, and only to the single directory that is the root of the
++ * export; further lookup and readdir operations are treated as if every
++ * subdirectory was a mountpoint, and ignored if they are not themselves
++ * exported. This is used by nfsd and mountd to construct the NFSv4
++ * pseudofilesystem, which provides access only to paths leading to each
++ * exported filesystem.
++ */
++#define NFSEXP_V4ROOT 0x10000
++#define NFSEXP_ALLFLAGS 0x1FE3F
+
+ /* The flags that may vary depending on security flavor: */
+ #define NFSEXP_SECINFO_FLAGS (NFSEXP_READONLY | NFSEXP_ROOTSQUASH \
+diff -up linux-2.6.32.i686/include/linux/nfsd/nfsd.h.save linux-2.6.32.i686/include/linux/nfsd/nfsd.h
+--- linux-2.6.32.i686/include/linux/nfsd/nfsd.h.save 2009-12-04 10:24:18.000000000 -0500
++++ linux-2.6.32.i686/include/linux/nfsd/nfsd.h 2009-12-04 10:39:18.000000000 -0500
+@@ -86,6 +86,7 @@ __be32 nfsd_lookup_dentry(struct svc_r
+ struct svc_export **, struct dentry **);
+ __be32 nfsd_setattr(struct svc_rqst *, struct svc_fh *,
+ struct iattr *, int, time_t);
++int nfsd_mountpoint(struct dentry *, struct svc_export *);
+ #ifdef CONFIG_NFSD_V4
+ __be32 nfsd4_set_nfs4_acl(struct svc_rqst *, struct svc_fh *,
+ struct nfs4_acl *);
+@@ -394,6 +395,10 @@ static inline u32 nfsd_suppattrs2(u32 mi
+ return minorversion ? NFSD4_1_SUPPORTED_ATTRS_WORD2
+ : NFSD4_SUPPORTED_ATTRS_WORD2;
+ }
++static inline int nfsd_v4client(struct svc_rqst *rq)
++{
++ return rq->rq_prog == NFS_PROGRAM && rq->rq_vers == 4;
++}
+
+ /* These will return ERR_INVAL if specified in GETATTR or READDIR. */
+ #define NFSD_WRITEONLY_ATTRS_WORD1 \
diff --git a/linux-2.6-pci-cacheline-sizing.patch b/linux-2.6-pci-cacheline-sizing.patch
new file mode 100644
index 0000000..8acaee4
--- /dev/null
+++ b/linux-2.6-pci-cacheline-sizing.patch
@@ -0,0 +1,41 @@
+PCI: Use generic cacheline sizing instead of per-vendor tests.
+
+Instead of the pci code needing to have code to determine the
+cacheline size of each processor, use the data the cpu identification
+code should have already determined during early boot.
+
+I chose not to delete the existing code for the time being.
+Instead I added some additional debug statements to be sure that it's
+doing the right thing, and compares it against what the old code would
+have done. After this has been proven to be right in a release,
+we can delete the paranoid checks, and all the old vendor checking code.
+
+Signed-off-by: Dave Jones <davej@redhat.com>
+
+diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c
+index 2202b62..f371fe8 100644
+--- a/arch/x86/pci/common.c
++++ b/arch/x86/pci/common.c
+@@ -432,6 +432,22 @@ int __init pcibios_init(void)
+ else if (c->x86 > 6 && c->x86_vendor == X86_VENDOR_INTEL)
+ pci_cache_line_size = 128 >> 2; /* P4 */
+
++ if (c->x86_clflush_size != (pci_cache_line_size <<2))
++ printk(KERN_DEBUG "PCI: old code would have set cacheline "
++ "size to %d bytes, but clflush_size = %d\n",
++ pci_cache_line_size << 2,
++ c->x86_clflush_size);
++
++ /* Once we know this logic works, all the above code can be deleted. */
++ if (c->x86_clflush_size > 0) {
++ pci_cache_line_size = c->x86_clflush_size >> 2;
++ printk(KERN_DEBUG "PCI: pci_cache_line_size set to %d bytes\n",
++ pci_cache_line_size << 2);
++ } else {
++ pci_cache_line_size = 32 >> 2;
++ printk(KERN_DEBUG "PCI: Unknown cacheline size. Setting to 32 bytes\n");
++ }
++
+ pcibios_resource_survey();
+
+ if (pci_bf_sort >= pci_force_bf)
diff --git a/linux-2.6-pciehp-update.patch b/linux-2.6-pciehp-update.patch
new file mode 100644
index 0000000..38ec797
--- /dev/null
+++ b/linux-2.6-pciehp-update.patch
@@ -0,0 +1,147 @@
+diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h
+index b2801a7..c9f18f9 100644
+--- a/drivers/pci/hotplug/pciehp.h
++++ b/drivers/pci/hotplug/pciehp.h
+@@ -224,6 +224,10 @@ static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev)
+ {
+ u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL |
+ OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
++ if (pciehp_force) {
++ dev_info(&dev->dev, "Bypassing BIOS check for pciehp\n");
++ return 0;
++ }
+ return acpi_get_hp_hw_control_from_firmware(dev, flags);
+ }
+
+diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c
+index 39cf248..ab6b016 100644
+--- a/drivers/pci/hotplug/pciehp_core.c
++++ b/drivers/pci/hotplug/pciehp_core.c
+@@ -41,6 +41,7 @@ int pciehp_debug;
+ int pciehp_poll_mode;
+ int pciehp_poll_time;
+ int pciehp_force;
++int pciehp_passive;
+ struct workqueue_struct *pciehp_wq;
+
+ #define DRIVER_VERSION "0.4"
+@@ -50,15 +51,18 @@ struct workqueue_struct *pciehp_wq;
+ MODULE_AUTHOR(DRIVER_AUTHOR);
+ MODULE_DESCRIPTION(DRIVER_DESC);
+ MODULE_LICENSE("GPL");
++MODULE_ALIAS("acpi*:PNP0A08:*");
+
+ module_param(pciehp_debug, bool, 0644);
+ module_param(pciehp_poll_mode, bool, 0644);
+ module_param(pciehp_poll_time, int, 0644);
+ module_param(pciehp_force, bool, 0644);
++module_param(pciehp_passive, bool, 0644);
+ MODULE_PARM_DESC(pciehp_debug, "Debugging mode enabled or not");
+ MODULE_PARM_DESC(pciehp_poll_mode, "Using polling mechanism for hot-plug events or not");
+ MODULE_PARM_DESC(pciehp_poll_time, "Polling mechanism frequency, in seconds");
+ MODULE_PARM_DESC(pciehp_force, "Force pciehp, even if _OSC and OSHP are missing");
++MODULE_PARM_DESC(pciehp_passive, "Listen for pciehp events, even if _OSC and OSHP are missing");
+
+ #define PCIE_MODULE_NAME "pciehp"
+
+@@ -85,6 +89,13 @@ static struct hotplug_slot_ops pciehp_hotplug_slot_ops = {
+ .get_cur_bus_speed = get_cur_bus_speed,
+ };
+
++static struct hotplug_slot_ops pciehp_passive_hotplug_slot_ops = {
++ .owner = THIS_MODULE,
++ .get_adapter_status = get_adapter_status,
++ .get_max_bus_speed = get_max_bus_speed,
++ .get_cur_bus_speed = get_cur_bus_speed,
++};
++
+ /*
+ * Check the status of the Electro Mechanical Interlock (EMI)
+ */
+@@ -212,7 +223,11 @@ static int init_slots(struct controller *ctrl)
+ hotplug_slot->info = info;
+ hotplug_slot->private = slot;
+ hotplug_slot->release = &release_slot;
+- hotplug_slot->ops = &pciehp_hotplug_slot_ops;
++ if (pciehp_passive &&
++ pciehp_get_hp_hw_control_from_firmware(ctrl->pci_dev))
++ hotplug_slot->ops = &pciehp_passive_hotplug_slot_ops;
++ else
++ hotplug_slot->ops = &pciehp_hotplug_slot_ops;
+ slot->hotplug_slot = hotplug_slot;
+ snprintf(name, SLOT_NAME_SIZE, "%u", slot->number);
+
+@@ -407,11 +422,7 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
+ u8 value;
+ struct pci_dev *pdev = dev->port;
+
+- if (pciehp_force)
+- dev_info(&dev->device,
+- "Bypassing BIOS check for pciehp use on %s\n",
+- pci_name(pdev));
+- else if (pciehp_get_hp_hw_control_from_firmware(pdev))
++ if (!pciehp_passive && pciehp_get_hp_hw_control_from_firmware(pdev))
+ goto err_out_none;
+
+ ctrl = pcie_init(dev);
+@@ -436,7 +447,7 @@ static int pciehp_probe(struct pcie_device *dev, const struct pcie_port_service_
+ t_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset);
+ t_slot->hpc_ops->get_adapter_status(t_slot, &value);
+ if (value) {
+- if (pciehp_force)
++ if (pciehp_force || pciehp_passive)
+ pciehp_enable_slot(t_slot);
+ } else {
+ /* Power off slot if not occupied */
+@@ -474,8 +485,11 @@ static int pciehp_suspend (struct pcie_device *dev, pm_message_t state)
+
+ static int pciehp_resume (struct pcie_device *dev)
+ {
++ struct pci_dev *pdev = dev->port;
+ dev_info(&dev->device, "%s ENTRY\n", __func__);
+- if (pciehp_force) {
++
++ if (pciehp_force || (pciehp_passive &&
++ pciehp_get_hp_hw_control_from_firmware(pdev))) {
+ struct controller *ctrl = get_service_data(dev);
+ struct slot *t_slot;
+ u8 status;
+diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
+index fead63c..12640bd 100644
+--- a/drivers/pci/hotplug/pciehp_ctrl.c
++++ b/drivers/pci/hotplug/pciehp_ctrl.c
+@@ -185,7 +185,8 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot)
+ * before taking any action that relies on power having been
+ * removed from the slot/adapter.
+ */
+- msleep(1000);
++ if (PWR_LED(ctrl) || ATTN_LED(ctrl))
++ msleep(1000);
+
+ if (PWR_LED(ctrl))
+ pslot->hpc_ops->green_led_off(pslot);
+@@ -288,16 +289,16 @@ static int remove_board(struct slot *p_slot)
+ }
+ }
+
+- /*
+- * After turning power off, we must wait for at least 1 second
+- * before taking any action that relies on power having been
+- * removed from the slot/adapter.
+- */
+- msleep(1000);
+-
+- if (PWR_LED(ctrl))
++ if (PWR_LED(ctrl)) {
++ /*
++ * After turning power off, we must wait for at least 1 second
++ * before taking any action that relies on power having been
++ * removed from the slot/adapter.
++ */
++ msleep(1000);
+ /* turn off Green LED */
+ p_slot->hpc_ops->green_led_off(p_slot);
++ }
+
+ return 0;
+ }
diff --git a/linux-2.6-phylib-autoload.patch b/linux-2.6-phylib-autoload.patch
new file mode 100644
index 0000000..2c423a9
--- /dev/null
+++ b/linux-2.6-phylib-autoload.patch
@@ -0,0 +1,406 @@
+From c413dfa59bf979475a9647cc165f547021efeb27 Mon Sep 17 00:00:00 2001
+From: David Woodhouse <dwmw2@infradead.org>
+Date: Wed, 31 Mar 2010 02:10:20 +0100
+Subject: [PATCH 1/2] phylib: Support phy module autoloading
+
+We don't use the normal hotplug mechanism because it doesn't work. It will
+load the module some time after the device appears, but that's not good
+enough for us -- we need the driver loaded _immediately_ because otherwise
+the NIC driver may just abort and then the phy 'device' goes away.
+
+[bwh: s/phy/mdio/ in module alias, kerneldoc for struct mdio_device_id]
+
+Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+---
+ drivers/net/phy/phy_device.c | 12 ++++++++++++
+ include/linux/mod_devicetable.h | 26 ++++++++++++++++++++++++++
+ include/linux/phy.h | 1 +
+ scripts/mod/file2alias.c | 26 ++++++++++++++++++++++++++
+ 4 files changed, 65 insertions(+), 0 deletions(-)
+
+From 9ddd9886cc89827a4713e9a96614148272fdaa8e Mon Sep 17 00:00:00 2001
+From: David Woodhouse <dwmw2@infradead.org>
+Date: Wed, 31 Mar 2010 02:12:06 +0100
+Subject: [PATCH 2/2] phylib: Add module table to all existing phy drivers
+
+Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
+---
+ drivers/net/phy/bcm63xx.c | 8 ++++++++
+ drivers/net/phy/broadcom.c | 16 ++++++++++++++++
+ drivers/net/phy/cicada.c | 8 ++++++++
+ drivers/net/phy/davicom.c | 9 +++++++++
+ drivers/net/phy/et1011c.c | 7 +++++++
+ drivers/net/phy/icplus.c | 7 +++++++
+ drivers/net/phy/lxt.c | 8 ++++++++
+ drivers/net/phy/marvell.c | 13 +++++++++++++
+ drivers/net/phy/national.c | 7 +++++++
+ drivers/net/phy/qsemi.c | 7 +++++++
+ drivers/net/phy/realtek.c | 7 +++++++
+ drivers/net/phy/smsc.c | 11 +++++++++++
+ drivers/net/phy/ste10Xp.c | 8 ++++++++
+ drivers/net/phy/vitesse.c | 8 ++++++++
+ 14 files changed, 124 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
+index b10fedd..0db6781 100644
+--- a/drivers/net/phy/phy_device.c
++++ b/drivers/net/phy/phy_device.c
+@@ -149,6 +149,7 @@ EXPORT_SYMBOL(phy_scan_fixups);
+ struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id)
+ {
+ struct phy_device *dev;
++
+ /* We allocate the device, and initialize the
+ * default values */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+@@ -178,6 +179,17 @@ struct phy_device* phy_device_create(struct mii_bus *bus, int addr, int phy_id)
+
+ mutex_init(&dev->lock);
+
++ /* Request the appropriate module unconditionally; don't
++ bother trying to do so only if it isn't already loaded,
++ because that gets complicated. A hotplug event would have
++ done an unconditional modprobe anyway.
++ We don't do normal hotplug because it won't work for MDIO
++ -- because it relies on the device staying around for long
++ enough for the driver to get loaded. With MDIO, the NIC
++ driver will get bored and give up as soon as it finds that
++ there's no driver _already_ loaded. */
++ request_module(MDIO_MODULE_PREFIX MDIO_ID_FMT, MDIO_ID_ARGS(phy_id));
++
+ return dev;
+ }
+ EXPORT_SYMBOL(phy_device_create);
+diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
+index f58e9d8..55f1f9c 100644
+--- a/include/linux/mod_devicetable.h
++++ b/include/linux/mod_devicetable.h
+@@ -474,4 +474,30 @@ struct platform_device_id {
+ __attribute__((aligned(sizeof(kernel_ulong_t))));
+ };
+
++#define MDIO_MODULE_PREFIX "mdio:"
++
++#define MDIO_ID_FMT "%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d"
++#define MDIO_ID_ARGS(_id) \
++ (_id)>>31, ((_id)>>30) & 1, ((_id)>>29) & 1, ((_id)>>28) & 1, \
++ ((_id)>>27) & 1, ((_id)>>26) & 1, ((_id)>>25) & 1, ((_id)>>24) & 1, \
++ ((_id)>>23) & 1, ((_id)>>22) & 1, ((_id)>>21) & 1, ((_id)>>20) & 1, \
++ ((_id)>>19) & 1, ((_id)>>18) & 1, ((_id)>>17) & 1, ((_id)>>16) & 1, \
++ ((_id)>>15) & 1, ((_id)>>14) & 1, ((_id)>>13) & 1, ((_id)>>12) & 1, \
++ ((_id)>>11) & 1, ((_id)>>10) & 1, ((_id)>>9) & 1, ((_id)>>8) & 1, \
++ ((_id)>>7) & 1, ((_id)>>6) & 1, ((_id)>>5) & 1, ((_id)>>4) & 1, \
++ ((_id)>>3) & 1, ((_id)>>2) & 1, ((_id)>>1) & 1, (_id) & 1
++
++/**
++ * struct mdio_device_id - identifies PHY devices on an MDIO/MII bus
++ * @phy_id: The result of
++ * (mdio_read(&MII_PHYSID1) << 16 | mdio_read(&PHYSID2)) & @phy_id_mask
++ * for this PHY type
++ * @phy_id_mask: Defines the significant bits of @phy_id. A value of 0
++ * is used to terminate an array of struct mdio_device_id.
++ */
++struct mdio_device_id {
++ __u32 phy_id;
++ __u32 phy_id_mask;
++};
++
+ #endif /* LINUX_MOD_DEVICETABLE_H */
+diff --git a/include/linux/phy.h b/include/linux/phy.h
+index b1368b8..b85de0d 100644
+--- a/include/linux/phy.h
++++ b/include/linux/phy.h
+@@ -24,6 +24,7 @@
+ #include <linux/mii.h>
+ #include <linux/timer.h>
+ #include <linux/workqueue.h>
++#include <linux/mod_devicetable.h>
+
+ #include <asm/atomic.h>
+
+diff --git a/scripts/mod/file2alias.c b/scripts/mod/file2alias.c
+index 62a9025..05f0c06 100644
+--- a/scripts/mod/file2alias.c
++++ b/scripts/mod/file2alias.c
+@@ -727,6 +727,28 @@ static int do_platform_entry(const char *filename,
+ return 1;
+ }
+
++static int do_mdio_entry(const char *filename,
++ struct mdio_device_id *id, char *alias)
++{
++ int i;
++
++ alias += sprintf(alias, MDIO_MODULE_PREFIX);
++
++ for (i = 0; i < 32; i++) {
++ if (!((id->phy_id_mask >> (31-i)) & 1))
++ *(alias++) = '?';
++ else if ((id->phy_id >> (31-i)) & 1)
++ *(alias++) = '1';
++ else
++ *(alias++) = '0';
++ }
++
++ /* Terminate the string */
++ *alias = 0;
++
++ return 1;
++}
++
+ /* Ignore any prefix, eg. some architectures prepend _ */
+ static inline int sym_is(const char *symbol, const char *name)
+ {
+@@ -874,6 +896,10 @@ void handle_moddevtable(struct module *mod, struct elf_info *info,
+ do_table(symval, sym->st_size,
+ sizeof(struct platform_device_id), "platform",
+ do_platform_entry, mod);
++ else if (sym_is(symname, "__mod_mdio_device_table"))
++ do_table(symval, sym->st_size,
++ sizeof(struct mdio_device_id), "mdio",
++ do_mdio_entry, mod);
+ free(zeros);
+ }
+
+diff --git a/drivers/net/phy/bcm63xx.c b/drivers/net/phy/bcm63xx.c
+index 4fed95e..ac5e498 100644
+--- a/drivers/net/phy/bcm63xx.c
++++ b/drivers/net/phy/bcm63xx.c
+@@ -130,3 +130,11 @@ static void __exit bcm63xx_phy_exit(void)
+
+ module_init(bcm63xx_phy_init);
+ module_exit(bcm63xx_phy_exit);
++
++static struct mdio_device_id bcm63xx_tbl[] = {
++ { 0x00406000, 0xfffffc00 },
++ { 0x002bdc00, 0xfffffc00 },
++ { }
++};
++
++MODULE_DEVICE_TABLE(mdio, bcm64xx_tbl);
+diff --git a/drivers/net/phy/broadcom.c b/drivers/net/phy/broadcom.c
+index f81e532..f46815d 100644
+--- a/drivers/net/phy/broadcom.c
++++ b/drivers/net/phy/broadcom.c
+@@ -830,3 +830,19 @@ static void __exit broadcom_exit(void)
+
+ module_init(broadcom_init);
+ module_exit(broadcom_exit);
++
++static struct mdio_device_id broadcom_tbl[] = {
++ { 0x00206070, 0xfffffff0 },
++ { 0x002060e0, 0xfffffff0 },
++ { 0x002060c0, 0xfffffff0 },
++ { 0x002060b0, 0xfffffff0 },
++ { 0x0143bca0, 0xfffffff0 },
++ { 0x0143bcb0, 0xfffffff0 },
++ { PHY_ID_BCM50610, 0xfffffff0 },
++ { PHY_ID_BCM50610M, 0xfffffff0 },
++ { PHY_ID_BCM57780, 0xfffffff0 },
++ { 0x0143bc70, 0xfffffff0 },
++ { }
++};
++
++MODULE_DEVICE_TABLE(mdio, broadcom_tbl);
+diff --git a/drivers/net/phy/cicada.c b/drivers/net/phy/cicada.c
+index a1bd599..efc608f 100644
+--- a/drivers/net/phy/cicada.c
++++ b/drivers/net/phy/cicada.c
+@@ -159,3 +159,11 @@ static void __exit cicada_exit(void)
+
+ module_init(cicada_init);
+ module_exit(cicada_exit);
++
++static struct mdio_device_id cicada_tbl[] = {
++ { 0x000fc410, 0x000ffff0 },
++ { 0x000fc440, 0x000fffc0 },
++ { }
++};
++
++MODULE_DEVICE_TABLE(mdio, cicada_tbl);
+diff --git a/drivers/net/phy/davicom.c b/drivers/net/phy/davicom.c
+index d926168..e02b18c 100644
+--- a/drivers/net/phy/davicom.c
++++ b/drivers/net/phy/davicom.c
+@@ -219,3 +219,12 @@ static void __exit davicom_exit(void)
+
+ module_init(davicom_init);
+ module_exit(davicom_exit);
++
++static struct mdio_device_id davicom_tbl[] = {
++ { 0x0181b880, 0x0ffffff0 },
++ { 0x0181b8a0, 0x0ffffff0 },
++ { 0x00181b80, 0x0ffffff0 },
++ { }
++};
++
++MODULE_DEVICE_TABLE(mdio, davicom_tbl);
+diff --git a/drivers/net/phy/et1011c.c b/drivers/net/phy/et1011c.c
+index b031fa2..500f0fd 100644
+--- a/drivers/net/phy/et1011c.c
++++ b/drivers/net/phy/et1011c.c
+@@ -111,3 +111,10 @@ static void __exit et1011c_exit(void)
+
+ module_init(et1011c_init);
+ module_exit(et1011c_exit);
++
++static struct mdio_device_id et1011c_tbl[] = {
++ { 0x0282f014, 0xfffffff0 },
++ { }
++};
++
++MODULE_DEVICE_TABLE(mdio, et1011c_tbl);
+diff --git a/drivers/net/phy/icplus.c b/drivers/net/phy/icplus.c
+index af3f1f2..e661e90 100644
+--- a/drivers/net/phy/icplus.c
++++ b/drivers/net/phy/icplus.c
+@@ -132,3 +132,10 @@ static void __exit ip175c_exit(void)
+
+ module_init(ip175c_init);
+ module_exit(ip175c_exit);
++
++static struct mdio_device_id icplus_tbl[] = {
++ { 0x02430d80, 0x0ffffff0 },
++ { }
++};
++
++MODULE_DEVICE_TABLE(mdio, icplus_tbl);
+diff --git a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c
+index 4cf3324..1d94f1d 100644
+--- a/drivers/net/phy/lxt.c
++++ b/drivers/net/phy/lxt.c
+@@ -174,3 +174,11 @@ static void __exit lxt_exit(void)
+
+ module_init(lxt_init);
+ module_exit(lxt_exit);
++
++static struct mdio_device_id lxt_tbl[] = {
++ { 0x78100000, 0xfffffff0 },
++ { 0x001378e0, 0xfffffff0 },
++ { }
++};
++
++MODULE_DEVICE_TABLE(mdio, lxt_tbl);
+diff --git a/drivers/net/phy/marvell.c b/drivers/net/phy/marvell.c
+index 6f69b9b..4e58b2c 100644
+--- a/drivers/net/phy/marvell.c
++++ b/drivers/net/phy/marvell.c
+@@ -611,3 +611,16 @@ static void __exit marvell_exit(void)
+
+ module_init(marvell_init);
+ module_exit(marvell_exit);
++
++static struct mdio_device_id marvell_tbl[] = {
++ { 0x01410c60, 0xfffffff0 },
++ { 0x01410c90, 0xfffffff0 },
++ { 0x01410cc0, 0xfffffff0 },
++ { 0x01410e10, 0xfffffff0 },
++ { 0x01410cb0, 0xfffffff0 },
++ { 0x01410cd0, 0xfffffff0 },
++ { 0x01410e30, 0xfffffff0 },
++ { }
++};
++
++MODULE_DEVICE_TABLE(mdio, marvell_tbl);
+diff --git a/drivers/net/phy/national.c b/drivers/net/phy/national.c
+index 6c636eb..729ab29 100644
+--- a/drivers/net/phy/national.c
++++ b/drivers/net/phy/national.c
+@@ -153,3 +153,10 @@ MODULE_LICENSE("GPL");
+
+ module_init(ns_init);
+ module_exit(ns_exit);
++
++static struct mdio_device_id ns_tbl[] = {
++ { DP83865_PHY_ID, 0xfffffff0 },
++ { }
++};
++
++MODULE_DEVICE_TABLE(mdio, ns_tbl);
+diff --git a/drivers/net/phy/qsemi.c b/drivers/net/phy/qsemi.c
+index 23062d0..3ec9610 100644
+--- a/drivers/net/phy/qsemi.c
++++ b/drivers/net/phy/qsemi.c
+@@ -138,3 +138,10 @@ static void __exit qs6612_exit(void)
+
+ module_init(qs6612_init);
+ module_exit(qs6612_exit);
++
++static struct mdio_device_id qs6612_tbl[] = {
++ { 0x00181440, 0xfffffff0 },
++ { }
++};
++
++MODULE_DEVICE_TABLE(mdio, qs6612_tbl);
+diff --git a/drivers/net/phy/realtek.c b/drivers/net/phy/realtek.c
+index a052a67..f567c0e 100644
+--- a/drivers/net/phy/realtek.c
++++ b/drivers/net/phy/realtek.c
+@@ -78,3 +78,10 @@ static void __exit realtek_exit(void)
+
+ module_init(realtek_init);
+ module_exit(realtek_exit);
++
++static struct mdio_device_id realtek_tbl[] = {
++ { 0x001cc912, 0x001fffff },
++ { }
++};
++
++MODULE_DEVICE_TABLE(mdio, realtek_tbl);
+diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c
+index 5123bb9..9fb14b4 100644
+--- a/drivers/net/phy/smsc.c
++++ b/drivers/net/phy/smsc.c
+@@ -236,3 +236,14 @@ MODULE_LICENSE("GPL");
+
+ module_init(smsc_init);
+ module_exit(smsc_exit);
++
++static struct mdio_device_id smsc_tbl[] = {
++ { 0x0007c0a0, 0xfffffff0 },
++ { 0x0007c0b0, 0xfffffff0 },
++ { 0x0007c0c0, 0xfffffff0 },
++ { 0x0007c0d0, 0xfffffff0 },
++ { 0x0007c0f0, 0xfffffff0 },
++ { }
++};
++
++MODULE_DEVICE_TABLE(mdio, smsc_tbl);
+diff --git a/drivers/net/phy/ste10Xp.c b/drivers/net/phy/ste10Xp.c
+index 6bdb0d5..7229009 100644
+--- a/drivers/net/phy/ste10Xp.c
++++ b/drivers/net/phy/ste10Xp.c
+@@ -132,6 +132,14 @@ static void __exit ste10Xp_exit(void)
+ module_init(ste10Xp_init);
+ module_exit(ste10Xp_exit);
+
++static struct mdio_device_id ste10Xp_tbl[] = {
++ { STE101P_PHY_ID, 0xfffffff0 },
++ { STE100P_PHY_ID, 0xffffffff },
++ { }
++};
++
++MODULE_DEVICE_TABLE(mdio, ste10Xp_tbl);
++
+ MODULE_DESCRIPTION("STMicroelectronics STe10Xp PHY driver");
+ MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
+ MODULE_LICENSE("GPL");
+diff --git a/drivers/net/phy/vitesse.c b/drivers/net/phy/vitesse.c
+index dd3b244..45cce50 100644
+--- a/drivers/net/phy/vitesse.c
++++ b/drivers/net/phy/vitesse.c
+@@ -191,3 +191,11 @@ static void __exit vsc82xx_exit(void)
+
+ module_init(vsc82xx_init);
+ module_exit(vsc82xx_exit);
++
++static struct mdio_device_id vitesse_tbl[] = {
++ { PHY_ID_VSC8244, 0x000fffc0 },
++ { PHY_ID_VSC8221, 0x000ffff0 },
++ { }
++};
++
++MODULE_DEVICE_TABLE(mdio, vitesse_tbl);
+--
+1.6.6.1
+
diff --git a/linux-2.6-ps3-storage-alias.patch b/linux-2.6-ps3-storage-alias.patch
new file mode 100644
index 0000000..ceb6519
--- /dev/null
+++ b/linux-2.6-ps3-storage-alias.patch
@@ -0,0 +1,7 @@
+--- linux-2.6.22.ppc64/drivers/block/ps3disk.c~ 2007-07-25 16:06:16.000000000 +0100
++++ linux-2.6.22.ppc64/drivers/block/ps3disk.c 2007-07-26 08:49:44.000000000 +0100
+@@ -628,3 +628,4 @@ MODULE_LICENSE("GPL");
+ MODULE_DESCRIPTION("PS3 Disk Storage Driver");
+ MODULE_AUTHOR("Sony Corporation");
+ MODULE_ALIAS(PS3_MODULE_ALIAS_STOR_DISK);
++MODULE_ALIAS("ps3_storage");
diff --git a/linux-2.6-revert-dvb-net-kabi-change.patch b/linux-2.6-revert-dvb-net-kabi-change.patch
new file mode 100644
index 0000000..28d55e0
--- /dev/null
+++ b/linux-2.6-revert-dvb-net-kabi-change.patch
@@ -0,0 +1,149 @@
+diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c
+index f6ba846..03fd9dd 100644
+--- a/drivers/media/dvb/dvb-core/dvb_net.c
++++ b/drivers/media/dvb/dvb-core/dvb_net.c
+@@ -125,6 +125,7 @@ static void hexdump( const unsigned char *buf, unsigned short len )
+
+ struct dvb_net_priv {
+ int in_use;
++ struct net_device_stats stats;
+ u16 pid;
+ struct net_device *net;
+ struct dvb_net *host;
+@@ -383,8 +384,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
+ if (priv->ule_skb) {
+ dev_kfree_skb( priv->ule_skb );
+ /* Prepare for next SNDU. */
+- dev->stats.rx_errors++;
+- dev->stats.rx_frame_errors++;
++ priv->stats.rx_errors++;
++ priv->stats.rx_frame_errors++;
+ }
+ reset_ule(priv);
+ priv->need_pusi = 1;
+@@ -437,8 +438,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
+ dev_kfree_skb( priv->ule_skb );
+ /* Prepare for next SNDU. */
+ // reset_ule(priv); moved to below.
+- dev->stats.rx_errors++;
+- dev->stats.rx_frame_errors++;
++ priv->stats.rx_errors++;
++ priv->stats.rx_frame_errors++;
+ }
+ reset_ule(priv);
+ /* skip to next PUSI. */
+@@ -459,8 +460,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
+ /* Drop partly decoded SNDU, reset state, resync on PUSI. */
+ if (priv->ule_skb) {
+ dev_kfree_skb( priv->ule_skb );
+- dev->stats.rx_errors++;
+- dev->stats.rx_frame_errors++;
++ priv->stats.rx_errors++;
++ priv->stats.rx_frame_errors++;
+ }
+ reset_ule(priv);
+ priv->need_pusi = 1;
+@@ -476,8 +477,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
+ if (priv->ule_sndu_remain > 183) {
+ /* Current SNDU lacks more data than there could be available in the
+ * current TS cell. */
+- dev->stats.rx_errors++;
+- dev->stats.rx_length_errors++;
++ priv->stats.rx_errors++;
++ priv->stats.rx_length_errors++;
+ printk(KERN_WARNING "%lu: Expected %d more SNDU bytes, but "
+ "got PUSI (pf %d, ts_remain %d). Flushing incomplete payload.\n",
+ priv->ts_count, priv->ule_sndu_remain, ts[4], ts_remain);
+@@ -519,8 +520,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
+ if (priv->ule_sndu_len < 5) {
+ printk(KERN_WARNING "%lu: Invalid ULE SNDU length %u. "
+ "Resyncing.\n", priv->ts_count, priv->ule_sndu_len);
+- dev->stats.rx_errors++;
+- dev->stats.rx_length_errors++;
++ priv->stats.rx_errors++;
++ priv->stats.rx_length_errors++;
+ priv->ule_sndu_len = 0;
+ priv->need_pusi = 1;
+ new_ts = 1;
+@@ -572,7 +573,7 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
+ if (priv->ule_skb == NULL) {
+ printk(KERN_NOTICE "%s: Memory squeeze, dropping packet.\n",
+ dev->name);
+- dev->stats.rx_dropped++;
++ priv->stats.rx_dropped++;
+ return;
+ }
+
+@@ -636,8 +637,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
+ ule_dump = 1;
+ #endif
+
+- dev->stats.rx_errors++;
+- dev->stats.rx_crc_errors++;
++ priv->stats.rx_errors++;
++ priv->stats.rx_crc_errors++;
+ dev_kfree_skb(priv->ule_skb);
+ } else {
+ /* CRC32 verified OK. */
+@@ -743,8 +744,8 @@ static void dvb_net_ule( struct net_device *dev, const u8 *buf, size_t buf_len )
+ * receive the packet anyhow. */
+ /* if (priv->ule_dbit && skb->pkt_type == PACKET_OTHERHOST)
+ priv->ule_skb->pkt_type = PACKET_HOST; */
+- dev->stats.rx_packets++;
+- dev->stats.rx_bytes += priv->ule_skb->len;
++ priv->stats.rx_packets++;
++ priv->stats.rx_bytes += priv->ule_skb->len;
+ netif_rx(priv->ule_skb);
+ }
+ sndu_done:
+@@ -799,7 +800,8 @@ static void dvb_net_sec(struct net_device *dev,
+ {
+ u8 *eth;
+ struct sk_buff *skb;
+- struct net_device_stats *stats = &dev->stats;
++ struct net_device_stats *stats =
++ &((struct dvb_net_priv *) netdev_priv(dev))->stats;
+ int snap = 0;
+
+ /* note: pkt_len includes a 32bit checksum */
+@@ -1214,29 +1216,28 @@ static int dvb_net_stop(struct net_device *dev)
+ return dvb_net_feed_stop(dev);
+ }
+
++static struct net_device_stats * dvb_net_get_stats(struct net_device *dev)
++{
++ return &((struct dvb_net_priv *) netdev_priv(dev))->stats;
++}
++
+ static const struct header_ops dvb_header_ops = {
+ .create = eth_header,
+ .parse = eth_header_parse,
+ .rebuild = eth_rebuild_header,
+ };
+
+-
+-static const struct net_device_ops dvb_netdev_ops = {
+- .ndo_open = dvb_net_open,
+- .ndo_stop = dvb_net_stop,
+- .ndo_start_xmit = dvb_net_tx,
+- .ndo_set_multicast_list = dvb_net_set_multicast_list,
+- .ndo_set_mac_address = dvb_net_set_mac,
+- .ndo_change_mtu = eth_change_mtu,
+- .ndo_validate_addr = eth_validate_addr,
+-};
+-
+ static void dvb_net_setup(struct net_device *dev)
+ {
+ ether_setup(dev);
+
+ dev->header_ops = &dvb_header_ops;
+- dev->netdev_ops = &dvb_netdev_ops;
++ dev->open = dvb_net_open;
++ dev->stop = dvb_net_stop;
++ dev->hard_start_xmit = dvb_net_tx;
++ dev->get_stats = dvb_net_get_stats;
++ dev->set_multicast_list = dvb_net_set_multicast_list;
++ dev->set_mac_address = dvb_net_set_mac;
+ dev->mtu = 4096;
+ dev->mc_count = 0;
+
diff --git a/linux-2.6-rfkill-all.patch b/linux-2.6-rfkill-all.patch
new file mode 100644
index 0000000..cd1db3e
--- /dev/null
+++ b/linux-2.6-rfkill-all.patch
@@ -0,0 +1,52 @@
+diff --git a/include/linux/input.h b/include/linux/input.h
+index 8b3bc3e..20a622e 100644
+--- a/include/linux/input.h
++++ b/include/linux/input.h
+@@ -595,6 +595,8 @@ struct input_absinfo {
+ #define KEY_NUMERIC_STAR 0x20a
+ #define KEY_NUMERIC_POUND 0x20b
+
++#define KEY_RFKILL 0x20c /* Key that controls all radios */
++
+ /* We avoid low common keys in module aliases so they don't get huge. */
+ #define KEY_MIN_INTERESTING KEY_MUTE
+ #define KEY_MAX 0x2ff
+diff --git a/include/linux/rfkill.h b/include/linux/rfkill.h
+index 278777f..4c39f7e 100644
+--- a/include/linux/rfkill.h
++++ b/include/linux/rfkill.h
+@@ -32,7 +32,7 @@
+ /**
+ * enum rfkill_type - type of rfkill switch.
+ *
+- * @RFKILL_TYPE_ALL: toggles all switches (userspace only)
++ * @RFKILL_TYPE_ALL: toggles all switches (requests only - not a switch type)
+ * @RFKILL_TYPE_WLAN: switch is on a 802.11 wireless network device.
+ * @RFKILL_TYPE_BLUETOOTH: switch is on a bluetooth device.
+ * @RFKILL_TYPE_UWB: switch is on a ultra wideband device.
+diff --git a/net/rfkill/input.c b/net/rfkill/input.c
+index a7295ad..3713d7e 100644
+--- a/net/rfkill/input.c
++++ b/net/rfkill/input.c
+@@ -212,6 +212,9 @@ static void rfkill_event(struct input_handle *handle, unsigned int type,
+ case KEY_WIMAX:
+ rfkill_schedule_toggle(RFKILL_TYPE_WIMAX);
+ break;
++ case KEY_RFKILL:
++ rfkill_schedule_toggle(RFKILL_TYPE_ALL);
++ break;
+ }
+ } else if (type == EV_SW && code == SW_RFKILL_ALL)
+ rfkill_schedule_evsw_rfkillall(data);
+@@ -295,6 +298,11 @@ static const struct input_device_id rfkill_ids[] = {
+ .keybit = { [BIT_WORD(KEY_WIMAX)] = BIT_MASK(KEY_WIMAX) },
+ },
+ {
++ .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
++ .evbit = { BIT_MASK(EV_KEY) },
++ .keybit = { [BIT_WORD(KEY_RFKILL)] = BIT_MASK(KEY_RFKILL) },
++ },
++ {
+ .flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_SWBIT,
+ .evbit = { BIT(EV_SW) },
+ .swbit = { [BIT_WORD(SW_RFKILL_ALL)] = BIT_MASK(SW_RFKILL_ALL) },
diff --git a/linux-2.6-selinux-mprotect-checks.patch b/linux-2.6-selinux-mprotect-checks.patch
new file mode 100644
index 0000000..1752525
--- /dev/null
+++ b/linux-2.6-selinux-mprotect-checks.patch
@@ -0,0 +1,41 @@
+This needs a fixed toolchain, and a userspace rebuild to work.
+For these reasons, it's had difficulty getting upstream.
+
+ie, Fedora has a new enough toolchain, and has been rebuilt, so we don't need
+the ifdefs. Other distros don't/haven't, and this patch would break them
+if pushed upstream.
+
+--- linux-2.6.26.noarch/security/selinux/hooks.c~ 2008-09-25 14:11:17.000000000 -0400
++++ linux-2.6.26.noarch/security/selinux/hooks.c 2008-09-25 14:12:17.000000000 -0400
+@@ -3018,7 +3018,6 @@ static int file_map_prot_check(struct fi
+ const struct cred *cred = current_cred();
+ int rc = 0;
+
+-#ifndef CONFIG_PPC32
+ if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) {
+ /*
+ * We are making executable an anonymous mapping or a
+@@ -3029,7 +3028,6 @@ static int file_map_prot_check(struct fi
+ if (rc)
+ goto error;
+ }
+-#endif
+
+ if (file) {
+ /* read access is always possible with a mapping */
+@@ -3024,7 +3022,6 @@ static int selinux_file_mprotect(struct
+ if (selinux_checkreqprot)
+ prot = reqprot;
+
+-#ifndef CONFIG_PPC32
+ if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
+ rc = 0;
+ if (vma->vm_start >= vma->vm_mm->start_brk &&
+@@ -3049,7 +3046,6 @@ static int selinux_file_mprotect(struct
+ if (rc)
+ return rc;
+ }
+-#endif
+
+ return file_map_prot_check(vma->vm_file, prot, vma->vm_flags&VM_SHARED);
+ }
diff --git a/linux-2.6-serial-460800.patch b/linux-2.6-serial-460800.patch
new file mode 100644
index 0000000..17d67ef
--- /dev/null
+++ b/linux-2.6-serial-460800.patch
@@ -0,0 +1,70 @@
+diff --git a/drivers/serial/8250.c b/drivers/serial/8250.c
+index 2209620..659c1bb 100644
+--- a/drivers/serial/8250.c
++++ b/drivers/serial/8250.c
+@@ -7,6 +7,9 @@
+ *
+ * Copyright (C) 2001 Russell King.
+ *
++ * 2005/09/16: Enabled higher baud rates for 16C95x.
++ * (Mathias Adam <a2@adamis.de>)
++ *
+ * 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
+@@ -2227,6 +2230,14 @@ static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int
+ else if ((port->flags & UPF_MAGIC_MULTIPLIER) &&
+ baud == (port->uartclk/8))
+ quot = 0x8002;
++ /*
++ * For 16C950s UART_TCR is used in combination with divisor==1
++ * to achieve baud rates up to baud_base*4.
++ */
++ else if ((port->type == PORT_16C950) &&
++ baud > (port->uartclk/16))
++ quot = 1;
++
+ else
+ quot = uart_get_divisor(port, baud);
+
+@@ -2240,7 +2251,7 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
+ struct uart_8250_port *up = (struct uart_8250_port *)port;
+ unsigned char cval, fcr = 0;
+ unsigned long flags;
+- unsigned int baud, quot;
++ unsigned int baud, quot, max_baud;
+
+ switch (termios->c_cflag & CSIZE) {
+ case CS5:
+@@ -2272,9 +2283,10 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
+ /*
+ * Ask the core to calculate the divisor for us.
+ */
++ max_baud = (up->port.type == PORT_16C950 ? port->uartclk/4 : port->uartclk/16);
+ baud = uart_get_baud_rate(port, termios, old,
+ port->uartclk / 16 / 0xffff,
+- port->uartclk / 16);
++ max_baud);
+ quot = serial8250_get_divisor(port, baud);
+
+ /*
+@@ -2311,6 +2323,19 @@ serial8250_set_termios(struct uart_port *port, struct ktermios *termios,
+ spin_lock_irqsave(&up->port.lock, flags);
+
+ /*
++ * 16C950 supports additional prescaler ratios between 1:16 and 1:4
++ * thus increasing max baud rate to uartclk/4.
++ */
++ if (up->port.type == PORT_16C950) {
++ if (baud == port->uartclk/4)
++ serial_icr_write(up, UART_TCR, 0x4);
++ else if (baud == port->uartclk/8)
++ serial_icr_write(up, UART_TCR, 0x8);
++ else
++ serial_icr_write(up, UART_TCR, 0);
++ }
++
++ /*
+ * Update the per-port timeout.
+ */
+ uart_update_timeout(port, termios->c_cflag, baud);
diff --git a/linux-2.6-silence-acpi-blacklist.patch b/linux-2.6-silence-acpi-blacklist.patch
new file mode 100644
index 0000000..c5997bb
--- /dev/null
+++ b/linux-2.6-silence-acpi-blacklist.patch
@@ -0,0 +1,25 @@
+diff -up linux-2.6.26.noarch/drivers/acpi/blacklist.c.jx linux-2.6.26.noarch/drivers/acpi/blacklist.c
+--- linux-2.6.26.noarch/drivers/acpi/blacklist.c.jx 2008-07-13 17:51:29.000000000 -0400
++++ linux-2.6.26.noarch/drivers/acpi/blacklist.c 2008-08-12 14:21:39.000000000 -0400
+@@ -81,18 +81,18 @@ static int __init blacklist_by_year(void
+
+ /* Doesn't exist? Likely an old system */
+ if (!dmi_get_date(DMI_BIOS_DATE, &year, NULL, NULL)) {
+- printk(KERN_ERR PREFIX "no DMI BIOS year, "
++ printk(KERN_INFO PREFIX "no DMI BIOS year, "
+ "acpi=force is required to enable ACPI\n" );
+ return 1;
+ }
+ /* 0? Likely a buggy new BIOS */
+ if (year == 0) {
+- printk(KERN_ERR PREFIX "DMI BIOS year==0, "
++ printk(KERN_INFO PREFIX "DMI BIOS year==0, "
+ "assuming ACPI-capable machine\n" );
+ return 0;
+ }
+ if (year < CONFIG_ACPI_BLACKLIST_YEAR) {
+- printk(KERN_ERR PREFIX "BIOS age (%d) fails cutoff (%d), "
++ printk(KERN_INFO PREFIX "BIOS age (%d) fails cutoff (%d), "
+ "acpi=force is required to enable ACPI\n",
+ year, CONFIG_ACPI_BLACKLIST_YEAR);
+ return 1;
diff --git a/linux-2.6-silence-fbcon-logo.patch b/linux-2.6-silence-fbcon-logo.patch
new file mode 100644
index 0000000..45ab733
--- /dev/null
+++ b/linux-2.6-silence-fbcon-logo.patch
@@ -0,0 +1,42 @@
+diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
+index 1657b96..4c5c2be 100644
+--- a/drivers/video/console/fbcon.c
++++ b/drivers/video/console/fbcon.c
+@@ -631,13 +631,15 @@ static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info,
+ kfree(save);
+ }
+
+- if (logo_lines > vc->vc_bottom) {
+- logo_shown = FBCON_LOGO_CANSHOW;
+- printk(KERN_INFO
+- "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
+- } else if (logo_shown != FBCON_LOGO_DONTSHOW) {
+- logo_shown = FBCON_LOGO_DRAW;
+- vc->vc_top = logo_lines;
++ if (logo_shown != FBCON_LOGO_DONTSHOW) {
++ if (logo_lines > vc->vc_bottom) {
++ logo_shown = FBCON_LOGO_CANSHOW;
++ printk(KERN_INFO
++ "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n");
++ } else {
++ logo_shown = FBCON_LOGO_DRAW;
++ vc->vc_top = logo_lines;
++ }
+ }
+ }
+ #endif /* MODULE */
+@@ -3489,6 +3491,14 @@ static int __init fb_console_init(void)
+ return 0;
+ }
+
++static int __init quiet_logo(char *str)
++{
++ logo_shown = FBCON_LOGO_DONTSHOW;
++ return 0;
++}
++
++early_param("quiet", quiet_logo);
++
+ module_init(fb_console_init);
+
+ #ifdef MODULE
diff --git a/linux-2.6-silence-noise.patch b/linux-2.6-silence-noise.patch
new file mode 100644
index 0000000..119a977
--- /dev/null
+++ b/linux-2.6-silence-noise.patch
@@ -0,0 +1,66 @@
+--- linux-2.6.26.noarch/drivers/base/power/main.c~ 2008-08-22 20:57:57.000000000 -0400
++++ linux-2.6.26.noarch/drivers/base/power/main.c 2008-08-22 20:58:05.000000000 -0400
+@@ -69,9 +69,6 @@ void device_pm_unlock(void)
+ */
+ void device_pm_add(struct device *dev)
+ {
+- pr_debug("PM: Adding info for %s:%s\n",
+- dev->bus ? dev->bus->name : "No Bus",
+- kobject_name(&dev->kobj));
+ mutex_lock(&dpm_list_mtx);
+ if (dev->parent) {
+ if (dev->parent->power.status >= DPM_SUSPENDING)
+From b4e96f34c17e5a79cd28774cc722bb33e7e02c6e Mon Sep 17 00:00:00 2001
+From: Peter Jones <pjones@redhat.com>
+Date: Thu, 25 Sep 2008 16:23:33 -0400
+Subject: [PATCH] Don't print an error message just because there's no i8042 chip.
+
+Some systems, such as EFI-based Apple systems, won't necessarily have an
+i8042 to initialize. We shouldn't be printing an error message in this
+case, since not detecting the chip is the correct behavior.
+---
+ drivers/input/serio/i8042.c | 4 +---
+ 1 files changed, 1 insertions(+), 3 deletions(-)
+
+diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
+index 170f71e..4f3e632 100644
+--- a/drivers/input/serio/i8042.c
++++ b/drivers/input/serio/i8042.c
+@@ -701,10 +701,8 @@ static int __devinit i8042_check_aux(void)
+
+ static int i8042_controller_check(void)
+ {
+- if (i8042_flush() == I8042_BUFFER_SIZE) {
+- printk(KERN_ERR "i8042.c: No controller found.\n");
++ if (i8042_flush() == I8042_BUFFER_SIZE)
+ return -ENODEV;
+- }
+
+ return 0;
+ }
+--
+1.6.0.1
+
+Socket fuzzers like sfuzz will trigger this printk a lot, even though it's
+ratelimited. It isn't particularly useful, so just remove it.
+
+Signed-off-by: Dave Jones <davej@redhat.com>
+
+--- linux-2.6.27.noarch/net/can/af_can.c~ 2008-12-11 16:53:48.000000000 -0500
++++ linux-2.6.27.noarch/net/can/af_can.c 2008-12-11 16:54:42.000000000 -0500
+@@ -134,13 +134,9 @@ static int can_create(struct net *net, s
+ err = request_module("can-proto-%d", protocol);
+
+ /*
+- * In case of error we only print a message but don't
+- * return the error code immediately. Below we will
+- * return -EPROTONOSUPPORT
++ * In case of error we don't return the error code immediately.
++ * Below we will return -EPROTONOSUPPORT
+ */
+- if (err && printk_ratelimit())
+- printk(KERN_ERR "can: request_module "
+- "(can-proto-%d) failed.\n", protocol);
+ }
+ #endif
+
diff --git a/linux-2.6-sparc-selinux-mprotect-checks.patch b/linux-2.6-sparc-selinux-mprotect-checks.patch
new file mode 100644
index 0000000..30d3689
--- /dev/null
+++ b/linux-2.6-sparc-selinux-mprotect-checks.patch
@@ -0,0 +1,21 @@
+diff -up linux-2.6.32.noarch/security/selinux/hooks.c.mprotect-sparc linux-2.6.32.noarch/security/selinux/hooks.c
+--- linux-2.6.32.noarch/security/selinux/hooks.c.mprotect-sparc 2010-03-10 08:28:20.957571926 -0500
++++ linux-2.6.32.noarch/security/selinux/hooks.c 2010-03-10 08:29:15.732698763 -0500
+@@ -3010,7 +3010,7 @@ static int file_map_prot_check(struct fi
+ const struct cred *cred = current_cred();
+ int rc = 0;
+
+-#ifndef CONFIG_PPC32
++#if !defined(CONFIG_PPC32) && !defined(CONFIG_SPARC)
+ if ((prot & PROT_EXEC) && (!file || (!shared && (prot & PROT_WRITE)))) {
+ /*
+ * We are making executable an anonymous mapping or a
+@@ -3082,7 +3082,7 @@ static int selinux_file_mprotect(struct
+ if (selinux_checkreqprot)
+ prot = reqprot;
+
+-#ifndef CONFIG_PPC32
++#if !defined(CONFIG_PPC32) && !defined(CONFIG_SPARC)
+ if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
+ int rc = 0;
+ if (vma->vm_start >= vma->vm_mm->start_brk &&
diff --git a/linux-2.6-tracehook.patch b/linux-2.6-tracehook.patch
new file mode 100644
index 0000000..7f6c05a
--- /dev/null
+++ b/linux-2.6-tracehook.patch
@@ -0,0 +1,368 @@
+From: Oleg Nesterov <oleg@redhat.com>
+
+[PATCH] signals: check ->group_stop_count after tracehook_get_signal()
+
+Move the call to do_signal_stop() down, after tracehook call.
+This makes ->group_stop_count condition visible to tracers before
+do_signal_stop() will participate in this group-stop.
+
+Currently the patch has no effect, tracehook_get_signal() always
+returns 0.
+
+Signed-off-by: Oleg Nesterov <oleg@redhat.com>
+Signed-off-by: Roland McGrath <roland@redhat.com>
+---
+ arch/powerpc/include/asm/ptrace.h | 2 +
+ arch/powerpc/kernel/traps.c | 9 ++++++
+ arch/s390/kernel/traps.c | 6 ++--
+ arch/x86/include/asm/ptrace.h | 2 +
+ arch/x86/kernel/ptrace.c | 51 ++++++++++++++++++++----------------
+ include/linux/ptrace.h | 24 +++++++++++------
+ include/linux/sched.h | 1 +
+ include/linux/tracehook.h | 15 ++++++++---
+ kernel/ptrace.c | 2 +-
+ kernel/signal.c | 13 ++++-----
+ 10 files changed, 79 insertions(+), 46 deletions(-)
+
+diff --git a/arch/powerpc/include/asm/ptrace.h b/arch/powerpc/include/asm/ptrace.h
+index 8c34149..cbd759e 100644
+--- a/arch/powerpc/include/asm/ptrace.h
++++ b/arch/powerpc/include/asm/ptrace.h
+@@ -140,6 +140,8 @@ extern void user_enable_single_step(stru
+ extern void user_enable_block_step(struct task_struct *);
+ extern void user_disable_single_step(struct task_struct *);
+
++#define ARCH_HAS_USER_SINGLE_STEP_INFO
++
+ #endif /* __ASSEMBLY__ */
+
+ #endif /* __KERNEL__ */
+diff --git a/arch/powerpc/kernel/traps.c b/arch/powerpc/kernel/traps.c
+index 6f0ae1a..83b57ac 100644
+--- a/arch/powerpc/kernel/traps.c
++++ b/arch/powerpc/kernel/traps.c
+@@ -174,6 +174,15 @@ int die(const char *str, struct pt_regs
+ return 0;
+ }
+
++void user_single_step_siginfo(struct task_struct *tsk,
++ struct pt_regs *regs, siginfo_t *info)
++{
++ memset(info, 0, sizeof(*info));
++ info->si_signo = SIGTRAP;
++ info->si_code = TRAP_TRACE;
++ info->si_addr = (void __user *)regs->nip;
++}
++
+ void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
+ {
+ siginfo_t info;
+diff --git a/arch/s390/kernel/traps.c b/arch/s390/kernel/traps.c
+index c2e42cc..6e7ad63 100644
+--- a/arch/s390/kernel/traps.c
++++ b/arch/s390/kernel/traps.c
+@@ -18,7 +18,7 @@
+ #include <linux/kernel.h>
+ #include <linux/string.h>
+ #include <linux/errno.h>
+-#include <linux/ptrace.h>
++#include <linux/tracehook.h>
+ #include <linux/timer.h>
+ #include <linux/mm.h>
+ #include <linux/smp.h>
+@@ -382,7 +382,7 @@ void __kprobes do_single_step(struct pt_
+ SIGTRAP) == NOTIFY_STOP){
+ return;
+ }
+- if ((current->ptrace & PT_PTRACED) != 0)
++ if (tracehook_consider_fatal_signal(current, SIGTRAP))
+ force_sig(SIGTRAP, current);
+ }
+
+@@ -483,7 +483,7 @@ static void illegal_op(struct pt_regs *
+ if (get_user(*((__u16 *) opcode), (__u16 __user *) location))
+ return;
+ if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) {
+- if (current->ptrace & PT_PTRACED)
++ if (tracehook_consider_fatal_signal(current, SIGTRAP))
+ force_sig(SIGTRAP, current);
+ else
+ signal = SIGILL;
+diff --git a/arch/x86/include/asm/ptrace.h b/arch/x86/include/asm/ptrace.h
+index 0f0d908..7a88a82 100644
+--- a/arch/x86/include/asm/ptrace.h
++++ b/arch/x86/include/asm/ptrace.h
+@@ -230,6 +230,8 @@ extern void user_enable_block_step(struc
+ #define arch_has_block_step() (boot_cpu_data.x86 >= 6)
+ #endif
+
++#define ARCH_HAS_USER_SINGLE_STEP_INFO
++
+ struct user_desc;
+ extern int do_get_thread_area(struct task_struct *p, int idx,
+ struct user_desc __user *info);
+diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
+index 7b058a2..ea35dee 100644
+--- a/arch/x86/kernel/ptrace.c
++++ b/arch/x86/kernel/ptrace.c
+@@ -1437,21 +1437,33 @@ const struct user_regset_view *task_user
+ #endif
+ }
+
+-void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs,
+- int error_code, int si_code)
++static void fill_sigtrap_info(struct task_struct *tsk,
++ struct pt_regs *regs,
++ int error_code, int si_code,
++ struct siginfo *info)
+ {
+- struct siginfo info;
+-
+ tsk->thread.trap_no = 1;
+ tsk->thread.error_code = error_code;
+
+- memset(&info, 0, sizeof(info));
+- info.si_signo = SIGTRAP;
+- info.si_code = si_code;
++ memset(info, 0, sizeof(*info));
++ info->si_signo = SIGTRAP;
++ info->si_code = si_code;
++ info->si_addr = user_mode_vm(regs) ? (void __user *)regs->ip : NULL;
++}
+
+- /* User-mode ip? */
+- info.si_addr = user_mode_vm(regs) ? (void __user *) regs->ip : NULL;
++void user_single_step_siginfo(struct task_struct *tsk,
++ struct pt_regs *regs,
++ struct siginfo *info)
++{
++ fill_sigtrap_info(tsk, regs, 0, TRAP_BRKPT, info);
++}
++
++void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs,
++ int error_code, int si_code)
++{
++ struct siginfo info;
+
++ fill_sigtrap_info(tsk, regs, error_code, si_code, &info);
+ /* Send us the fake SIGTRAP */
+ force_sig_info(SIGTRAP, &info, tsk);
+ }
+@@ -1516,29 +1528,22 @@ asmregparm long syscall_trace_enter(stru
+
+ asmregparm void syscall_trace_leave(struct pt_regs *regs)
+ {
++ bool step;
++
+ if (unlikely(current->audit_context))
+ audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax);
+
+ if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
+ trace_sys_exit(regs, regs->ax);
+
+- if (test_thread_flag(TIF_SYSCALL_TRACE))
+- tracehook_report_syscall_exit(regs, 0);
+-
+ /*
+ * If TIF_SYSCALL_EMU is set, we only get here because of
+ * TIF_SINGLESTEP (i.e. this is PTRACE_SYSEMU_SINGLESTEP).
+ * We already reported this syscall instruction in
+- * syscall_trace_enter(), so don't do any more now.
+- */
+- if (unlikely(test_thread_flag(TIF_SYSCALL_EMU)))
+- return;
+-
+- /*
+- * If we are single-stepping, synthesize a trap to follow the
+- * system call instruction.
++ * syscall_trace_enter().
+ */
+- if (test_thread_flag(TIF_SINGLESTEP) &&
+- tracehook_consider_fatal_signal(current, SIGTRAP))
+- send_sigtrap(current, regs, 0, TRAP_BRKPT);
++ step = unlikely(test_thread_flag(TIF_SINGLESTEP)) &&
++ !test_thread_flag(TIF_SYSCALL_EMU);
++ if (step || test_thread_flag(TIF_SYSCALL_TRACE))
++ tracehook_report_syscall_exit(regs, step);
+ }
+diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
+index 7456d7d..4802e2a 100644
+--- a/include/linux/ptrace.h
++++ b/include/linux/ptrace.h
+@@ -85,6 +85,7 @@ extern int ptrace_traceme(void);
+ extern int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len);
+ extern int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len);
+ extern int ptrace_attach(struct task_struct *tsk);
++extern bool __ptrace_detach(struct task_struct *tracer, struct task_struct *tracee);
+ extern int ptrace_detach(struct task_struct *, unsigned int);
+ extern void ptrace_disable(struct task_struct *);
+ extern int ptrace_check_attach(struct task_struct *task, int kill);
+@@ -105,12 +106,7 @@ static inline int ptrace_reparented(stru
+ {
+ return child->real_parent != child->parent;
+ }
+-static inline void ptrace_link(struct task_struct *child,
+- struct task_struct *new_parent)
+-{
+- if (unlikely(child->ptrace))
+- __ptrace_link(child, new_parent);
+-}
++
+ static inline void ptrace_unlink(struct task_struct *child)
+ {
+ if (unlikely(child->ptrace))
+@@ -169,9 +165,9 @@ static inline void ptrace_init_task(stru
+ INIT_LIST_HEAD(&child->ptraced);
+ child->parent = child->real_parent;
+ child->ptrace = 0;
+- if (unlikely(ptrace)) {
++ if (unlikely(ptrace) && (current->ptrace & PT_PTRACED)) {
+ child->ptrace = current->ptrace;
+- ptrace_link(child, current->parent);
++ __ptrace_link(child, current->parent);
+ }
+ }
+
+@@ -278,6 +274,18 @@ static inline void user_enable_block_ste
+ }
+ #endif /* arch_has_block_step */
+
++#ifdef ARCH_HAS_USER_SINGLE_STEP_INFO
++extern void user_single_step_siginfo(struct task_struct *tsk,
++ struct pt_regs *regs, siginfo_t *info);
++#else
++static inline void user_single_step_siginfo(struct task_struct *tsk,
++ struct pt_regs *regs, siginfo_t *info)
++{
++ memset(info, 0, sizeof(*info));
++ info->si_signo = SIGTRAP;
++}
++#endif
++
+ #ifndef arch_ptrace_stop_needed
+ /**
+ * arch_ptrace_stop_needed - Decide whether arch_ptrace_stop() should be called
+diff --git a/include/linux/sched.h b/include/linux/sched.h
+index 75e6e60..6c8928b 100644
+--- a/include/linux/sched.h
++++ b/include/linux/sched.h
+@@ -2060,6 +2060,7 @@ extern int kill_pgrp(struct pid *pid, in
+ extern int kill_pid(struct pid *pid, int sig, int priv);
+ extern int kill_proc_info(int, struct siginfo *, pid_t);
+ extern int do_notify_parent(struct task_struct *, int);
++extern void do_notify_parent_cldstop(struct task_struct *, int);
+ extern void __wake_up_parent(struct task_struct *p, struct task_struct *parent);
+ extern void force_sig(int, struct task_struct *);
+ extern void force_sig_specific(int, struct task_struct *);
+diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h
+index 1eb44a9..c78b2f4 100644
+--- a/include/linux/tracehook.h
++++ b/include/linux/tracehook.h
+@@ -134,6 +134,13 @@ static inline __must_check int tracehook
+ */
+ static inline void tracehook_report_syscall_exit(struct pt_regs *regs, int step)
+ {
++ if (step && (task_ptrace(current) & PT_PTRACED)) {
++ siginfo_t info;
++ user_single_step_siginfo(current, regs, &info);
++ force_sig_info(SIGTRAP, &info, current);
++ return;
++ }
++
+ ptrace_report_syscall(regs);
+ }
+
+@@ -149,7 +156,7 @@ static inline int tracehook_unsafe_exec(
+ {
+ int unsafe = 0;
+ int ptrace = task_ptrace(task);
+- if (ptrace & PT_PTRACED) {
++ if (ptrace) {
+ if (ptrace & PT_PTRACE_CAP)
+ unsafe |= LSM_UNSAFE_PTRACE_CAP;
+ else
+@@ -171,7 +178,7 @@ static inline int tracehook_unsafe_exec(
+ */
+ static inline struct task_struct *tracehook_tracer_task(struct task_struct *tsk)
+ {
+- if (task_ptrace(tsk) & PT_PTRACED)
++ if (task_ptrace(tsk))
+ return rcu_dereference(tsk->parent);
+ return NULL;
+ }
+@@ -379,7 +386,7 @@ static inline void tracehook_signal_hand
+ const struct k_sigaction *ka,
+ struct pt_regs *regs, int stepping)
+ {
+- if (stepping)
++ if (stepping && (task_ptrace(current) & PT_PTRACED))
+ ptrace_notify(SIGTRAP);
+ }
+
+@@ -485,7 +492,7 @@ static inline int tracehook_get_signal(s
+ */
+ static inline int tracehook_notify_jctl(int notify, int why)
+ {
+- return notify ?: (current->ptrace & PT_PTRACED) ? why : 0;
++ return notify ?: task_ptrace(current) ? why : 0;
+ }
+
+ /**
+diff --git a/kernel/ptrace.c b/kernel/ptrace.c
+index 23bd09c..b7c1d32 100644
+--- a/kernel/ptrace.c
++++ b/kernel/ptrace.c
+@@ -271,7 +271,7 @@ static int ignoring_children(struct sigh
+ * reap it now, in that case we must also wake up sub-threads sleeping in
+ * do_wait().
+ */
+-static bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
++bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
+ {
+ __ptrace_unlink(p);
+
+diff --git a/kernel/signal.c b/kernel/signal.c
+index 6705320..9908335 100644
+--- a/kernel/signal.c
++++ b/kernel/signal.c
+@@ -1461,7 +1461,7 @@ int do_notify_parent(struct task_struct
+ return ret;
+ }
+
+-static void do_notify_parent_cldstop(struct task_struct *tsk, int why)
++void do_notify_parent_cldstop(struct task_struct *tsk, int why)
+ {
+ struct siginfo info;
+ unsigned long flags;
+@@ -1731,7 +1731,7 @@ static int do_signal_stop(int signr)
+ static int ptrace_signal(int signr, siginfo_t *info,
+ struct pt_regs *regs, void *cookie)
+ {
+- if (!task_ptrace(current))
++ if (!(task_ptrace(current) & PT_PTRACED))
+ return signr;
+
+ ptrace_signal_deliver(regs, cookie);
+@@ -1807,11 +1807,6 @@ relock:
+
+ for (;;) {
+ struct k_sigaction *ka;
+-
+- if (unlikely(signal->group_stop_count > 0) &&
+- do_signal_stop(0))
+- goto relock;
+-
+ /*
+ * Tracing can induce an artifical signal and choose sigaction.
+ * The return value in @signr determines the default action,
+@@ -1823,6 +1818,10 @@ relock:
+ if (unlikely(signr != 0))
+ ka = return_ka;
+ else {
++ if (unlikely(signal->group_stop_count > 0) &&
++ do_signal_stop(0))
++ goto relock;
++
+ signr = dequeue_signal(current, &current->blocked,
+ info);
+
diff --git a/linux-2.6-upstream-reverts.patch b/linux-2.6-upstream-reverts.patch
new file mode 100644
index 0000000..1984356
--- /dev/null
+++ b/linux-2.6-upstream-reverts.patch
@@ -0,0 +1,1936 @@
+From 0725e95ea56698774e893edb7e7276b1d6890954 Mon Sep 17 00:00:00 2001
+From: Bernhard Rosenkraenzer <br@blankpage.ch>
+Date: Wed, 10 Mar 2010 12:36:43 +0100
+Subject: USB: qcserial: add new device ids
+
+From: Bernhard Rosenkraenzer <br@blankpage.ch>
+
+commit 0725e95ea56698774e893edb7e7276b1d6890954 upstream.
+
+This patch adds various USB device IDs for Gobi 2000 devices, as found in the
+drivers available at https://www.codeaurora.org/wiki/GOBI_Releases
+
+Signed-off-by: Bernhard Rosenkraenzer <bero@arklinux.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/qcserial.c | 29 +++++++++++++++++++++++++++++
+ 1 file changed, 29 insertions(+)
+
+--- a/drivers/usb/serial/qcserial.c
++++ b/drivers/usb/serial/qcserial.c
+@@ -47,6 +47,35 @@ static struct usb_device_id id_table[] =
+ {USB_DEVICE(0x05c6, 0x9221)}, /* Generic Gobi QDL device */
+ {USB_DEVICE(0x05c6, 0x9231)}, /* Generic Gobi QDL device */
+ {USB_DEVICE(0x1f45, 0x0001)}, /* Unknown Gobi QDL device */
++ {USB_DEVICE(0x413c, 0x8185)}, /* Dell Gobi 2000 QDL device (N0218, VU936) */
++ {USB_DEVICE(0x413c, 0x8186)}, /* Dell Gobi 2000 Modem device (N0218, VU936) */
++ {USB_DEVICE(0x05c6, 0x9224)}, /* Sony Gobi 2000 QDL device (N0279, VU730) */
++ {USB_DEVICE(0x05c6, 0x9225)}, /* Sony Gobi 2000 Modem device (N0279, VU730) */
++ {USB_DEVICE(0x05c6, 0x9244)}, /* Samsung Gobi 2000 QDL device (VL176) */
++ {USB_DEVICE(0x05c6, 0x9245)}, /* Samsung Gobi 2000 Modem device (VL176) */
++ {USB_DEVICE(0x03f0, 0x241d)}, /* HP Gobi 2000 QDL device (VP412) */
++ {USB_DEVICE(0x03f0, 0x251d)}, /* HP Gobi 2000 Modem device (VP412) */
++ {USB_DEVICE(0x05c6, 0x9214)}, /* Acer Gobi 2000 QDL device (VP413) */
++ {USB_DEVICE(0x05c6, 0x9215)}, /* Acer Gobi 2000 Modem device (VP413) */
++ {USB_DEVICE(0x05c6, 0x9264)}, /* Asus Gobi 2000 QDL device (VR305) */
++ {USB_DEVICE(0x05c6, 0x9265)}, /* Asus Gobi 2000 Modem device (VR305) */
++ {USB_DEVICE(0x05c6, 0x9234)}, /* Top Global Gobi 2000 QDL device (VR306) */
++ {USB_DEVICE(0x05c6, 0x9235)}, /* Top Global Gobi 2000 Modem device (VR306) */
++ {USB_DEVICE(0x05c6, 0x9274)}, /* iRex Technologies Gobi 2000 QDL device (VR307) */
++ {USB_DEVICE(0x05c6, 0x9275)}, /* iRex Technologies Gobi 2000 Modem device (VR307) */
++ {USB_DEVICE(0x1199, 0x9000)}, /* Sierra Wireless Gobi 2000 QDL device (VT773) */
++ {USB_DEVICE(0x1199, 0x9001)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x1199, 0x9002)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x1199, 0x9003)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x1199, 0x9004)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x1199, 0x9005)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x1199, 0x9006)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x1199, 0x9007)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x1199, 0x9008)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x1199, 0x9009)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x1199, 0x900a)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x16d8, 0x8001)}, /* CMDTech Gobi 2000 QDL device (VU922) */
++ {USB_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */
+ { } /* Terminating entry */
+ };
+ MODULE_DEVICE_TABLE(usb, id_table);
+From 9cf00977da092096c7a983276dad8b3002d23a99 Mon Sep 17 00:00:00 2001
+From: Adam Jackson <ajax@redhat.com>
+Date: Thu, 3 Dec 2009 17:44:36 -0500
+Subject: drm/edid: Unify detailed block parsing between base and extension blocks
+
+From: Adam Jackson <ajax@redhat.com>
+
+commit 9cf00977da092096c7a983276dad8b3002d23a99 upstream.
+
+Also fix an embarassing bug in standard timing subblock parsing that
+would result in an infinite loop.
+
+Signed-off-by: Adam Jackson <ajax@redhat.com>
+Signed-off-by: Dave Airlie <airlied@redhat.com>
+Cc: maximilian attems <max@stro.at>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/drm_edid.c | 163 ++++++++++++++++-----------------------------
+ 1 file changed, 61 insertions(+), 102 deletions(-)
+
+--- a/drivers/gpu/drm/drm_edid.c
++++ b/drivers/gpu/drm/drm_edid.c
+@@ -834,8 +834,57 @@ static int add_standard_modes(struct drm
+ return modes;
+ }
+
++static int add_detailed_modes(struct drm_connector *connector,
++ struct detailed_timing *timing,
++ struct edid *edid, u32 quirks, int preferred)
++{
++ int i, modes = 0;
++ struct detailed_non_pixel *data = &timing->data.other_data;
++ int timing_level = standard_timing_level(edid);
++ struct drm_display_mode *newmode;
++ struct drm_device *dev = connector->dev;
++
++ if (timing->pixel_clock) {
++ newmode = drm_mode_detailed(dev, edid, timing, quirks);
++ if (!newmode)
++ return 0;
++
++ if (preferred)
++ newmode->type |= DRM_MODE_TYPE_PREFERRED;
++
++ drm_mode_probed_add(connector, newmode);
++ return 1;
++ }
++
++ /* other timing types */
++ switch (data->type) {
++ case EDID_DETAIL_MONITOR_RANGE:
++ /* Get monitor range data */
++ break;
++ case EDID_DETAIL_STD_MODES:
++ /* Six modes per detailed section */
++ for (i = 0; i < 6; i++) {
++ struct std_timing *std;
++ struct drm_display_mode *newmode;
++
++ std = &data->data.timings[i];
++ newmode = drm_mode_std(dev, std, edid->revision,
++ timing_level);
++ if (newmode) {
++ drm_mode_probed_add(connector, newmode);
++ modes++;
++ }
++ }
++ break;
++ default:
++ break;
++ }
++
++ return modes;
++}
++
+ /**
+- * add_detailed_modes - get detailed mode info from EDID data
++ * add_detailed_info - get detailed mode info from EDID data
+ * @connector: attached connector
+ * @edid: EDID block to scan
+ * @quirks: quirks to apply
+@@ -846,67 +895,24 @@ static int add_standard_modes(struct drm
+ static int add_detailed_info(struct drm_connector *connector,
+ struct edid *edid, u32 quirks)
+ {
+- struct drm_device *dev = connector->dev;
+- int i, j, modes = 0;
+- int timing_level;
+-
+- timing_level = standard_timing_level(edid);
++ int i, modes = 0;
+
+ for (i = 0; i < EDID_DETAILED_TIMINGS; i++) {
+ struct detailed_timing *timing = &edid->detailed_timings[i];
+- struct detailed_non_pixel *data = &timing->data.other_data;
+- struct drm_display_mode *newmode;
++ int preferred = (i == 0) && (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING);
+
+- /* X server check is version 1.1 or higher */
+- if (edid->version == 1 && edid->revision >= 1 &&
+- !timing->pixel_clock) {
+- /* Other timing or info */
+- switch (data->type) {
+- case EDID_DETAIL_MONITOR_SERIAL:
+- break;
+- case EDID_DETAIL_MONITOR_STRING:
+- break;
+- case EDID_DETAIL_MONITOR_RANGE:
+- /* Get monitor range data */
+- break;
+- case EDID_DETAIL_MONITOR_NAME:
+- break;
+- case EDID_DETAIL_MONITOR_CPDATA:
+- break;
+- case EDID_DETAIL_STD_MODES:
+- for (j = 0; j < 6; i++) {
+- struct std_timing *std;
+- struct drm_display_mode *newmode;
+-
+- std = &data->data.timings[j];
+- newmode = drm_mode_std(dev, std,
+- edid->revision,
+- timing_level);
+- if (newmode) {
+- drm_mode_probed_add(connector, newmode);
+- modes++;
+- }
+- }
+- break;
+- default:
+- break;
+- }
+- } else {
+- newmode = drm_mode_detailed(dev, edid, timing, quirks);
+- if (!newmode)
+- continue;
+-
+- /* First detailed mode is preferred */
+- if (i == 0 && (edid->features & DRM_EDID_FEATURE_PREFERRED_TIMING))
+- newmode->type |= DRM_MODE_TYPE_PREFERRED;
+- drm_mode_probed_add(connector, newmode);
++ /* In 1.0, only timings are allowed */
++ if (!timing->pixel_clock && edid->version == 1 &&
++ edid->revision == 0)
++ continue;
+
+- modes++;
+- }
++ modes += add_detailed_modes(connector, timing, edid, quirks,
++ preferred);
+ }
+
+ return modes;
+ }
++
+ /**
+ * add_detailed_mode_eedid - get detailed mode info from addtional timing
+ * EDID block
+@@ -920,12 +926,9 @@ static int add_detailed_info(struct drm_
+ static int add_detailed_info_eedid(struct drm_connector *connector,
+ struct edid *edid, u32 quirks)
+ {
+- struct drm_device *dev = connector->dev;
+- int i, j, modes = 0;
++ int i, modes = 0;
+ char *edid_ext = NULL;
+ struct detailed_timing *timing;
+- struct detailed_non_pixel *data;
+- struct drm_display_mode *newmode;
+ int edid_ext_num;
+ int start_offset, end_offset;
+ int timing_level;
+@@ -976,51 +979,7 @@ static int add_detailed_info_eedid(struc
+ for (i = start_offset; i < end_offset;
+ i += sizeof(struct detailed_timing)) {
+ timing = (struct detailed_timing *)(edid_ext + i);
+- data = &timing->data.other_data;
+- /* Detailed mode timing */
+- if (timing->pixel_clock) {
+- newmode = drm_mode_detailed(dev, edid, timing, quirks);
+- if (!newmode)
+- continue;
+-
+- drm_mode_probed_add(connector, newmode);
+-
+- modes++;
+- continue;
+- }
+-
+- /* Other timing or info */
+- switch (data->type) {
+- case EDID_DETAIL_MONITOR_SERIAL:
+- break;
+- case EDID_DETAIL_MONITOR_STRING:
+- break;
+- case EDID_DETAIL_MONITOR_RANGE:
+- /* Get monitor range data */
+- break;
+- case EDID_DETAIL_MONITOR_NAME:
+- break;
+- case EDID_DETAIL_MONITOR_CPDATA:
+- break;
+- case EDID_DETAIL_STD_MODES:
+- /* Five modes per detailed section */
+- for (j = 0; j < 5; i++) {
+- struct std_timing *std;
+- struct drm_display_mode *newmode;
+-
+- std = &data->data.timings[j];
+- newmode = drm_mode_std(dev, std,
+- edid->revision,
+- timing_level);
+- if (newmode) {
+- drm_mode_probed_add(connector, newmode);
+- modes++;
+- }
+- }
+- break;
+- default:
+- break;
+- }
++ modes += add_detailed_modes(connector, timing, edid, quirks, 0);
+ }
+
+ return modes;
+From 29874f44fbcbc24b231b42c9956f8f9de9407231 Mon Sep 17 00:00:00 2001
+From: Shaohua Li <shaohua.li@intel.com>
+Date: Wed, 18 Nov 2009 15:15:02 +0800
+Subject: drm/i915: fix gpio register detection logic for BIOS without VBT
+
+From: Shaohua Li <shaohua.li@intel.com>
+
+commit 29874f44fbcbc24b231b42c9956f8f9de9407231 upstream.
+
+if no VBT is present, crt_ddc_bus will be left at 0, and cause us
+to use that for the GPIO register offset. That's never a valid register
+offset, so let the "undefined" value be 0 instead of -1.
+
+Signed-off-by: Shaohua Li <shaohua.li@intel.com>
+Signed-off-by: Zhao Yakui <yakui.zhao@intel.com>
+Signed-off-by: Eric Anholt <eric@anholt.net>
+[anholt: clarified the commit message a bit]
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/i915/i915_drv.h | 2 +-
+ drivers/gpu/drm/i915/intel_bios.c | 4 ----
+ drivers/gpu/drm/i915/intel_crt.c | 2 +-
+ 3 files changed, 2 insertions(+), 6 deletions(-)
+
+--- a/drivers/gpu/drm/i915/i915_drv.h
++++ b/drivers/gpu/drm/i915/i915_drv.h
+@@ -258,7 +258,7 @@ typedef struct drm_i915_private {
+
+ struct notifier_block lid_notifier;
+
+- int crt_ddc_bus; /* -1 = unknown, else GPIO to use for CRT DDC */
++ int crt_ddc_bus; /* 0 = unknown, else GPIO to use for CRT DDC */
+ struct drm_i915_fence_reg fence_regs[16]; /* assume 965 */
+ int fence_reg_start; /* 4 if userland hasn't ioctl'd us yet */
+ int num_fence_regs; /* 8 on pre-965, 16 otherwise */
+--- a/drivers/gpu/drm/i915/intel_bios.c
++++ b/drivers/gpu/drm/i915/intel_bios.c
+@@ -241,10 +241,6 @@ parse_general_definitions(struct drm_i91
+ GPIOF,
+ };
+
+- /* Set sensible defaults in case we can't find the general block
+- or it is the wrong chipset */
+- dev_priv->crt_ddc_bus = -1;
+-
+ general = find_section(bdb, BDB_GENERAL_DEFINITIONS);
+ if (general) {
+ u16 block_size = get_blocksize(general);
+--- a/drivers/gpu/drm/i915/intel_crt.c
++++ b/drivers/gpu/drm/i915/intel_crt.c
+@@ -557,7 +557,7 @@ void intel_crt_init(struct drm_device *d
+ else {
+ i2c_reg = GPIOA;
+ /* Use VBT information for CRT DDC if available */
+- if (dev_priv->crt_ddc_bus != -1)
++ if (dev_priv->crt_ddc_bus != 0)
+ i2c_reg = dev_priv->crt_ddc_bus;
+ }
+ intel_output->ddc_bus = intel_i2c_create(dev, i2c_reg, "CRTDDC_A");
+From 290e55056ec3d25c72088628245d8cae037b30db Mon Sep 17 00:00:00 2001
+From: Maarten Maathuis <madman2003@gmail.com>
+Date: Sat, 20 Feb 2010 03:22:21 +0100
+Subject: drm/ttm: handle OOM in ttm_tt_swapout
+
+From: Maarten Maathuis <madman2003@gmail.com>
+
+commit 290e55056ec3d25c72088628245d8cae037b30db upstream.
+
+- Without this change I get a general protection fault.
+- Also use PTR_ERR where applicable.
+
+Signed-off-by: Maarten Maathuis <madman2003@gmail.com>
+Reviewed-by: Dave Airlie <airlied@redhat.com>
+Acked-by: Thomas Hellstrom <thellstrom@vmware.com>
+Signed-off-by: Dave Airlie <airlied@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/ttm/ttm_tt.c | 18 +++++++++++-------
+ 1 file changed, 11 insertions(+), 7 deletions(-)
+
+--- a/drivers/gpu/drm/ttm/ttm_tt.c
++++ b/drivers/gpu/drm/ttm/ttm_tt.c
+@@ -466,7 +466,7 @@ static int ttm_tt_swapin(struct ttm_tt *
+ void *from_virtual;
+ void *to_virtual;
+ int i;
+- int ret;
++ int ret = -ENOMEM;
+
+ if (ttm->page_flags & TTM_PAGE_FLAG_USER) {
+ ret = ttm_tt_set_user(ttm, ttm->tsk, ttm->start,
+@@ -485,8 +485,10 @@ static int ttm_tt_swapin(struct ttm_tt *
+
+ for (i = 0; i < ttm->num_pages; ++i) {
+ from_page = read_mapping_page(swap_space, i, NULL);
+- if (IS_ERR(from_page))
++ if (IS_ERR(from_page)) {
++ ret = PTR_ERR(from_page);
+ goto out_err;
++ }
+ to_page = __ttm_tt_get_page(ttm, i);
+ if (unlikely(to_page == NULL))
+ goto out_err;
+@@ -509,7 +511,7 @@ static int ttm_tt_swapin(struct ttm_tt *
+ return 0;
+ out_err:
+ ttm_tt_free_alloced_pages(ttm);
+- return -ENOMEM;
++ return ret;
+ }
+
+ int ttm_tt_swapout(struct ttm_tt *ttm, struct file *persistant_swap_storage)
+@@ -521,6 +523,7 @@ int ttm_tt_swapout(struct ttm_tt *ttm, s
+ void *from_virtual;
+ void *to_virtual;
+ int i;
++ int ret = -ENOMEM;
+
+ BUG_ON(ttm->state != tt_unbound && ttm->state != tt_unpopulated);
+ BUG_ON(ttm->caching_state != tt_cached);
+@@ -543,7 +546,7 @@ int ttm_tt_swapout(struct ttm_tt *ttm, s
+ 0);
+ if (unlikely(IS_ERR(swap_storage))) {
+ printk(KERN_ERR "Failed allocating swap storage.\n");
+- return -ENOMEM;
++ return PTR_ERR(swap_storage);
+ }
+ } else
+ swap_storage = persistant_swap_storage;
+@@ -555,9 +558,10 @@ int ttm_tt_swapout(struct ttm_tt *ttm, s
+ if (unlikely(from_page == NULL))
+ continue;
+ to_page = read_mapping_page(swap_space, i, NULL);
+- if (unlikely(to_page == NULL))
++ if (unlikely(IS_ERR(to_page))) {
++ ret = PTR_ERR(to_page);
+ goto out_err;
+-
++ }
+ preempt_disable();
+ from_virtual = kmap_atomic(from_page, KM_USER0);
+ to_virtual = kmap_atomic(to_page, KM_USER1);
+@@ -581,5 +585,5 @@ out_err:
+ if (!persistant_swap_storage)
+ fput(swap_storage);
+
+- return -ENOMEM;
++ return ret;
+ }
+From 70136081fc67ea77d849f86fa323e5773c8e40ea Mon Sep 17 00:00:00 2001
+From: Theodore Kilgore <kilgota@auburn.edu>
+Date: Fri, 25 Dec 2009 05:15:10 -0300
+Subject: V4L/DVB (13991): gspca_mr973010a: Fix cif type 1 cameras not streaming on UHCI controllers
+
+From: Theodore Kilgore <kilgota@auburn.edu>
+
+commit 70136081fc67ea77d849f86fa323e5773c8e40ea upstream.
+
+If you read the mail to Oliver Neukum on the linux-usb list, then you know
+that I found a cure for the mysterious problem that the MR97310a CIF "type
+1" cameras have been freezing up and refusing to stream if hooked up to a
+machine with a UHCI controller.
+
+Namely, the cure is that if the camera is an mr97310a CIF type 1 camera, you
+have to send it 0xa0, 0x00. Somehow, this is a timing reset command, or
+such. It un-blocks whatever was previously stopping the CIF type 1 cameras
+from working on the UHCI-based machines.
+
+Signed-off-by: Theodore Kilgore <kilgota@auburn.edu>
+Signed-off-by: Hans de Goede <hdegoede@redhat.com>
+Signed-off-by: Mauro Carvalho Chehab <mchehab@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/media/video/gspca/mr97310a.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/media/video/gspca/mr97310a.c
++++ b/drivers/media/video/gspca/mr97310a.c
+@@ -530,6 +530,12 @@ static int start_cif_cam(struct gspca_de
+ {0x13, 0x00, {0x01}, 1},
+ {0, 0, {0}, 0}
+ };
++ /* Without this command the cam won't work with USB-UHCI */
++ gspca_dev->usb_buf[0] = 0x0a;
++ gspca_dev->usb_buf[1] = 0x00;
++ err_code = mr_write(gspca_dev, 2);
++ if (err_code < 0)
++ return err_code;
+ err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data,
+ ARRAY_SIZE(cif_sensor1_init_data));
+ }
+From 8fcc501831aa5b37a4a5a8cd9dc965be3cacc599 Mon Sep 17 00:00:00 2001
+From: Zhenyu Wang <zhenyuw@linux.intel.com>
+Date: Mon, 28 Dec 2009 13:15:20 +0800
+Subject: drm/i915: disable TV hotplug status check
+
+From: Zhenyu Wang <zhenyuw@linux.intel.com>
+
+commit 8fcc501831aa5b37a4a5a8cd9dc965be3cacc599 upstream.
+
+As we removed TV hotplug, don't check its status ever.
+
+Reviewed-by: Adam Jackson <ajax@redhat.com>
+Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com>
+Signed-off-by: Eric Anholt <eric@anholt.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/i915/intel_tv.c | 2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/drivers/gpu/drm/i915/intel_tv.c
++++ b/drivers/gpu/drm/i915/intel_tv.c
+@@ -1801,8 +1801,6 @@ intel_tv_init(struct drm_device *dev)
+ drm_connector_attach_property(connector,
+ dev->mode_config.tv_bottom_margin_property,
+ tv_priv->margin[TV_MARGIN_BOTTOM]);
+-
+- dev_priv->hotplug_supported_mask |= TV_HOTPLUG_INT_STATUS;
+ out:
+ drm_sysfs_connector_add(connector);
+ }
+From 43bcd61fae05fc6062b4f117c5adb1a72c9f8c57 Mon Sep 17 00:00:00 2001
+From: Daniel Vetter <daniel.vetter@ffwll.ch>
+Date: Tue, 3 Nov 2009 09:03:34 +0000
+Subject: drm/i915: fix get_core_clock_speed for G33 class desktop chips
+
+From: Daniel Vetter <daniel.vetter@ffwll.ch>
+
+commit 43bcd61fae05fc6062b4f117c5adb1a72c9f8c57 upstream.
+
+Somehow the case for G33 got dropped while porting from ums code.
+This made a 400MHz chip into a 133MHz one which resulted in the
+unnecessary enabling of double wide pipe mode which in turn
+screwed up the overlay code.
+
+Nothing else (than the overlay code) seems to be affected.
+
+This fixes fdo.org bug #24835
+
+Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
+Signed-off-by: Eric Anholt <eric@anholt.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/i915/intel_display.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/i915/intel_display.c
++++ b/drivers/gpu/drm/i915/intel_display.c
+@@ -4322,7 +4322,7 @@ static void intel_init_display(struct dr
+ }
+
+ /* Returns the core display clock speed */
+- if (IS_I945G(dev))
++ if (IS_I945G(dev) || (IS_G33(dev) && ! IS_IGDGM(dev)))
+ dev_priv->display.get_display_clock_speed =
+ i945_get_display_clock_speed;
+ else if (IS_I915G(dev))
+From ceb0297d3da7ecf44bccec2c4520c8710612c238 Mon Sep 17 00:00:00 2001
+From: Jerome Glisse <jglisse@redhat.com>
+Date: Sun, 14 Feb 2010 19:33:18 +0000
+Subject: drm/radeon: r6xx/r7xx possible security issue, system ram access
+
+From: Jerome Glisse <jglisse@redhat.com>
+
+commit c8c15ff1e90bfc4a2db1ba77a01b3b2783e723fc upstream
+
+This patch workaround a possible security issue which can allow
+user to abuse drm on r6xx/r7xx hw to access any system ram memory.
+This patch doesn't break userspace, it detect "valid" old use of
+CB_COLOR[0-7]_FRAG & CB_COLOR[0-7]_TILE registers and overwritte
+the address these registers are pointing to with the one of the
+last color buffer. This workaround will work for old mesa &
+xf86-video-ati and any old user which did use similar register
+programming pattern as those (we expect that there is no others
+user of those ioctl except possibly a malicious one). This patch
+add a warning if it detects such usage, warning encourage people
+to update their mesa & xf86-video-ati. New userspace will submit
+proper relocation.
+
+Fix for xf86-video-ati / mesa (this kernel patch is enough to
+prevent abuse, fix for userspace are to set proper cs stream and
+avoid kernel warning) :
+http://cgit.freedesktop.org/xorg/driver/xf86-video-ati/commit/?id=95d63e408cc88b6934bec84a0b1ef94dfe8bee7b
+http://cgit.freedesktop.org/mesa/mesa/commit/?id=46dc6fd3ed5ef96cda53641a97bc68c3bc104a9f
+
+Abusing this register to perform system ram memory is not easy,
+here is outline on how it could be achieve. First attacker must
+have access to the drm device and be able to submit command stream
+throught cs ioctl. Then attacker must build a proper command stream
+for r6xx/r7xx hw which will abuse the FRAG or TILE buffer to
+overwrite the GPU GART which is in VRAM. To achieve so attacker
+as to setup CB_COLOR[0-7]_FRAG or CB_COLOR[0-7]_TILE to point
+to the GPU GART, then it has to find a way to write predictable
+value into those buffer (with little cleverness i believe this
+can be done but this is an hard task). Once attacker have such
+program it can overwritte GPU GART to program GPU gart to point
+anywhere in system memory. It then can reusse same method as he
+used to reprogram GART to overwritte the system ram through the
+GART mapping. In the process the attacker has to be carefull to
+not overwritte any sensitive area of the GART table, like ring
+or IB gart entry as it will more then likely lead to GPU lockup.
+Bottom line is that i think it's very hard to use this flaw
+to get system ram access but in theory one can achieve so.
+
+Side note: I am not aware of anyone ever using the GPU as an
+attack vector, nevertheless we take great care in the opensource
+driver to try to detect and forbid malicious use of GPU. I don't
+think the closed source driver are as cautious as we are.
+
+[bwh: Adjusted context for 2.6.32]
+Signed-off-by: Jerome Glisse <jglisse@redhat.com>
+Signed-off-by: Dave Airlie <airlied@linux.ie>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/radeon/r600_cs.c | 83 +++++++++++++++++++++++++++++++++++++
+ drivers/gpu/drm/radeon/r600d.h | 26 +++++++++++
+ drivers/gpu/drm/radeon/radeon.h | 1
+ drivers/gpu/drm/radeon/radeon_cs.c | 1
+ 4 files changed, 111 insertions(+)
+
+--- a/drivers/gpu/drm/radeon/r600_cs.c
++++ b/drivers/gpu/drm/radeon/r600_cs.c
+@@ -36,6 +36,10 @@ static int r600_cs_packet_next_reloc_nom
+ typedef int (*next_reloc_t)(struct radeon_cs_parser*, struct radeon_cs_reloc**);
+ static next_reloc_t r600_cs_packet_next_reloc = &r600_cs_packet_next_reloc_mm;
+
++struct r600_cs_track {
++ u32 cb_color0_base_last;
++};
++
+ /**
+ * r600_cs_packet_parse() - parse cp packet and point ib index to next packet
+ * @parser: parser structure holding parsing context.
+@@ -177,6 +181,28 @@ static int r600_cs_packet_next_reloc_nom
+ }
+
+ /**
++ * r600_cs_packet_next_is_pkt3_nop() - test if next packet is packet3 nop for reloc
++ * @parser: parser structure holding parsing context.
++ *
++ * Check next packet is relocation packet3, do bo validation and compute
++ * GPU offset using the provided start.
++ **/
++static inline int r600_cs_packet_next_is_pkt3_nop(struct radeon_cs_parser *p)
++{
++ struct radeon_cs_packet p3reloc;
++ int r;
++
++ r = r600_cs_packet_parse(p, &p3reloc, p->idx);
++ if (r) {
++ return 0;
++ }
++ if (p3reloc.type != PACKET_TYPE3 || p3reloc.opcode != PACKET3_NOP) {
++ return 0;
++ }
++ return 1;
++}
++
++/**
+ * r600_cs_packet_next_vline() - parse userspace VLINE packet
+ * @parser: parser structure holding parsing context.
+ *
+@@ -337,6 +363,7 @@ static int r600_packet3_check(struct rad
+ struct radeon_cs_packet *pkt)
+ {
+ struct radeon_cs_reloc *reloc;
++ struct r600_cs_track *track;
+ volatile u32 *ib;
+ unsigned idx;
+ unsigned i;
+@@ -344,6 +371,7 @@ static int r600_packet3_check(struct rad
+ int r;
+ u32 idx_value;
+
++ track = (struct r600_cs_track *)p->track;
+ ib = p->ib->ptr;
+ idx = pkt->idx + 1;
+ idx_value = radeon_get_ib_value(p, idx);
+@@ -503,9 +531,60 @@ static int r600_packet3_check(struct rad
+ for (i = 0; i < pkt->count; i++) {
+ reg = start_reg + (4 * i);
+ switch (reg) {
++ /* This register were added late, there is userspace
++ * which does provide relocation for those but set
++ * 0 offset. In order to avoid breaking old userspace
++ * we detect this and set address to point to last
++ * CB_COLOR0_BASE, note that if userspace doesn't set
++ * CB_COLOR0_BASE before this register we will report
++ * error. Old userspace always set CB_COLOR0_BASE
++ * before any of this.
++ */
++ case R_0280E0_CB_COLOR0_FRAG:
++ case R_0280E4_CB_COLOR1_FRAG:
++ case R_0280E8_CB_COLOR2_FRAG:
++ case R_0280EC_CB_COLOR3_FRAG:
++ case R_0280F0_CB_COLOR4_FRAG:
++ case R_0280F4_CB_COLOR5_FRAG:
++ case R_0280F8_CB_COLOR6_FRAG:
++ case R_0280FC_CB_COLOR7_FRAG:
++ case R_0280C0_CB_COLOR0_TILE:
++ case R_0280C4_CB_COLOR1_TILE:
++ case R_0280C8_CB_COLOR2_TILE:
++ case R_0280CC_CB_COLOR3_TILE:
++ case R_0280D0_CB_COLOR4_TILE:
++ case R_0280D4_CB_COLOR5_TILE:
++ case R_0280D8_CB_COLOR6_TILE:
++ case R_0280DC_CB_COLOR7_TILE:
++ if (!r600_cs_packet_next_is_pkt3_nop(p)) {
++ if (!track->cb_color0_base_last) {
++ dev_err(p->dev, "Broken old userspace ? no cb_color0_base supplied before trying to write 0x%08X\n", reg);
++ return -EINVAL;
++ }
++ ib[idx+1+i] = track->cb_color0_base_last;
++ printk_once(KERN_WARNING "You have old & broken userspace "
++ "please consider updating mesa & xf86-video-ati\n");
++ } else {
++ r = r600_cs_packet_next_reloc(p, &reloc);
++ if (r) {
++ dev_err(p->dev, "bad SET_CONTEXT_REG 0x%04X\n", reg);
++ return -EINVAL;
++ }
++ ib[idx+1+i] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
++ }
++ break;
+ case DB_DEPTH_BASE:
+ case DB_HTILE_DATA_BASE:
+ case CB_COLOR0_BASE:
++ r = r600_cs_packet_next_reloc(p, &reloc);
++ if (r) {
++ DRM_ERROR("bad SET_CONTEXT_REG "
++ "0x%04X\n", reg);
++ return -EINVAL;
++ }
++ ib[idx+1+i] += (u32)((reloc->lobj.gpu_offset >> 8) & 0xffffffff);
++ track->cb_color0_base_last = ib[idx+1+i];
++ break;
+ case CB_COLOR1_BASE:
+ case CB_COLOR2_BASE:
+ case CB_COLOR3_BASE:
+@@ -678,8 +757,11 @@ static int r600_packet3_check(struct rad
+ int r600_cs_parse(struct radeon_cs_parser *p)
+ {
+ struct radeon_cs_packet pkt;
++ struct r600_cs_track *track;
+ int r;
+
++ track = kzalloc(sizeof(*track), GFP_KERNEL);
++ p->track = track;
+ do {
+ r = r600_cs_packet_parse(p, &pkt, p->idx);
+ if (r) {
+@@ -757,6 +839,7 @@ int r600_cs_legacy(struct drm_device *de
+ /* initialize parser */
+ memset(&parser, 0, sizeof(struct radeon_cs_parser));
+ parser.filp = filp;
++ parser.dev = &dev->pdev->dev;
+ parser.rdev = NULL;
+ parser.family = family;
+ parser.ib = &fake_ib;
+--- a/drivers/gpu/drm/radeon/r600d.h
++++ b/drivers/gpu/drm/radeon/r600d.h
+@@ -674,4 +674,30 @@
+ #define S_000E60_SOFT_RESET_TSC(x) (((x) & 1) << 16)
+ #define S_000E60_SOFT_RESET_VMC(x) (((x) & 1) << 17)
+
++#define R_005480_HDP_MEM_COHERENCY_FLUSH_CNTL 0x5480
++
++#define R_0280E0_CB_COLOR0_FRAG 0x0280E0
++#define S_0280E0_BASE_256B(x) (((x) & 0xFFFFFFFF) << 0)
++#define G_0280E0_BASE_256B(x) (((x) >> 0) & 0xFFFFFFFF)
++#define C_0280E0_BASE_256B 0x00000000
++#define R_0280E4_CB_COLOR1_FRAG 0x0280E4
++#define R_0280E8_CB_COLOR2_FRAG 0x0280E8
++#define R_0280EC_CB_COLOR3_FRAG 0x0280EC
++#define R_0280F0_CB_COLOR4_FRAG 0x0280F0
++#define R_0280F4_CB_COLOR5_FRAG 0x0280F4
++#define R_0280F8_CB_COLOR6_FRAG 0x0280F8
++#define R_0280FC_CB_COLOR7_FRAG 0x0280FC
++#define R_0280C0_CB_COLOR0_TILE 0x0280C0
++#define S_0280C0_BASE_256B(x) (((x) & 0xFFFFFFFF) << 0)
++#define G_0280C0_BASE_256B(x) (((x) >> 0) & 0xFFFFFFFF)
++#define C_0280C0_BASE_256B 0x00000000
++#define R_0280C4_CB_COLOR1_TILE 0x0280C4
++#define R_0280C8_CB_COLOR2_TILE 0x0280C8
++#define R_0280CC_CB_COLOR3_TILE 0x0280CC
++#define R_0280D0_CB_COLOR4_TILE 0x0280D0
++#define R_0280D4_CB_COLOR5_TILE 0x0280D4
++#define R_0280D8_CB_COLOR6_TILE 0x0280D8
++#define R_0280DC_CB_COLOR7_TILE 0x0280DC
++
++
+ #endif
+--- a/drivers/gpu/drm/radeon/radeon.h
++++ b/drivers/gpu/drm/radeon/radeon.h
+@@ -448,6 +448,7 @@ struct radeon_cs_chunk {
+ };
+
+ struct radeon_cs_parser {
++ struct device *dev;
+ struct radeon_device *rdev;
+ struct drm_file *filp;
+ /* chunks */
+--- a/drivers/gpu/drm/radeon/radeon_cs.c
++++ b/drivers/gpu/drm/radeon/radeon_cs.c
+@@ -230,6 +230,7 @@ int radeon_cs_ioctl(struct drm_device *d
+ memset(&parser, 0, sizeof(struct radeon_cs_parser));
+ parser.filp = filp;
+ parser.rdev = rdev;
++ parser.dev = rdev->dev;
+ r = radeon_cs_parser_init(&parser, data);
+ if (r) {
+ DRM_ERROR("Failed to initialize parser !\n");
+From e3dae5087754984ed7e6daf4fbb742ff026aadd5 Mon Sep 17 00:00:00 2001
+From: Jerome Glisse <jglisse@redhat.com>
+Date: Sun, 14 Feb 2010 19:31:58 +0000
+Subject: drm/radeon/kms: r600/r700 don't test ib if ib initialization fails
+
+From: Jerome Glisse <jglisse@redhat.com>
+
+commit db96380ea26fcc31ab37189aedeabd12894b1431 upstream
+
+If ib initialization failed don't try to test ib as it will result
+in an oops (accessing NULL ib buffer ptr).
+
+[bwh: Adjusted context for 2.6.32]
+Signed-off-by: Jerome Glisse <jglisse@redhat.com>
+Signed-off-by: Dave Airlie <airlied@linux.ie>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/radeon/r600.c | 13 +++++++------
+ drivers/gpu/drm/radeon/rv770.c | 13 +++++++------
+ 2 files changed, 14 insertions(+), 12 deletions(-)
+
+--- a/drivers/gpu/drm/radeon/r600.c
++++ b/drivers/gpu/drm/radeon/r600.c
+@@ -1686,13 +1686,14 @@ int r600_init(struct radeon_device *rdev
+ if (rdev->accel_working) {
+ r = radeon_ib_pool_init(rdev);
+ if (r) {
+- DRM_ERROR("radeon: failled initializing IB pool (%d).\n", r);
+- rdev->accel_working = false;
+- }
+- r = r600_ib_test(rdev);
+- if (r) {
+- DRM_ERROR("radeon: failled testing IB (%d).\n", r);
++ dev_err(rdev->dev, "IB initialization failed (%d).\n", r);
+ rdev->accel_working = false;
++ } else {
++ r = r600_ib_test(rdev);
++ if (r) {
++ dev_err(rdev->dev, "IB test failed (%d).\n", r);
++ rdev->accel_working = false;
++ }
+ }
+ }
+ return 0;
+--- a/drivers/gpu/drm/radeon/rv770.c
++++ b/drivers/gpu/drm/radeon/rv770.c
+@@ -1034,13 +1034,14 @@ int rv770_init(struct radeon_device *rde
+ if (rdev->accel_working) {
+ r = radeon_ib_pool_init(rdev);
+ if (r) {
+- DRM_ERROR("radeon: failled initializing IB pool (%d).\n", r);
+- rdev->accel_working = false;
+- }
+- r = r600_ib_test(rdev);
+- if (r) {
+- DRM_ERROR("radeon: failled testing IB (%d).\n", r);
++ dev_err(rdev->dev, "IB initialization failed (%d).\n", r);
+ rdev->accel_working = false;
++ } else {
++ r = r600_ib_test(rdev);
++ if (r) {
++ dev_err(rdev->dev, "IB test failed (%d).\n", r);
++ rdev->accel_working = false;
++ }
+ }
+ }
+ return 0;
+From 7e71c9e2e7704ebf044d4a964e02fbd2098a173f Mon Sep 17 00:00:00 2001
+From: Jerome Glisse <jglisse@redhat.com>
+Date: Sun, 17 Jan 2010 21:21:41 +0100
+Subject: drm/radeon/kms: Forbid creation of framebuffer with no valid GEM object
+
+From: Jerome Glisse <jglisse@redhat.com>
+
+commit 7e71c9e2e7704ebf044d4a964e02fbd2098a173f upstream.
+
+This will avoid oops if at later point the fb is use. Trying to create
+a framebuffer with no valid GEM object is bogus and should be forbidden
+as this patch does.
+
+Signed-off-by: Jerome Glisse <jglisse@redhat.com>
+Signed-off-by: Dave Airlie <airlied@linux.ie>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/radeon/radeon_display.c | 6 +++++-
+ 1 file changed, 5 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/radeon/radeon_display.c
++++ b/drivers/gpu/drm/radeon/radeon_display.c
+@@ -599,7 +599,11 @@ radeon_user_framebuffer_create(struct dr
+ struct drm_gem_object *obj;
+
+ obj = drm_gem_object_lookup(dev, file_priv, mode_cmd->handle);
+-
++ if (obj == NULL) {
++ dev_err(&dev->pdev->dev, "No GEM object associated to handle 0x%08X, "
++ "can't create framebuffer\n", mode_cmd->handle);
++ return NULL;
++ }
+ return radeon_framebuffer_create(dev, mode_cmd, obj);
+ }
+
+From 1379d2fef0ec07c7027a5e89036025ce761470c8 Mon Sep 17 00:00:00 2001
+From: Zhang Rui <rui.zhang@intel.com>
+Date: Tue, 16 Feb 2010 04:16:55 -0500
+Subject: ACPI, i915: blacklist Clevo M5x0N bad_lid state
+
+From: Zhang Rui <rui.zhang@intel.com>
+
+commit 1379d2fef0ec07c7027a5e89036025ce761470c8 upstream.
+
+Wrong Lid state reported.
+Need to blacklist this machine for LVDS detection.
+
+Signed-off-by: Zhang Rui <rui.zhang@intel.com>
+Signed-off-by: Len Brown <len.brown@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/i915/intel_lvds.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/gpu/drm/i915/intel_lvds.c
++++ b/drivers/gpu/drm/i915/intel_lvds.c
+@@ -629,6 +629,13 @@ static const struct dmi_system_id bad_li
+ DMI_MATCH(DMI_PRODUCT_NAME, "PC-81005"),
+ },
+ },
++ {
++ .ident = "Clevo M5x0N",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "CLEVO Co."),
++ DMI_MATCH(DMI_BOARD_NAME, "M5x0N"),
++ },
++ },
+ { }
+ };
+
+From 01d4503968f471f876fb44335800d2cf8dc5a2ce Mon Sep 17 00:00:00 2001
+From: Dave Airlie <airlied@redhat.com>
+Date: Sun, 31 Jan 2010 07:07:14 +1000
+Subject: drm/radeon/kms: use udelay for short delays
+
+From: Dave Airlie <airlied@redhat.com>
+
+commit 01d4503968f471f876fb44335800d2cf8dc5a2ce upstream.
+
+For usec delays use udelay instead of scheduling, this should
+allow reclocking to happen faster. This also was the cause
+of reported 33s delays at bootup on certain systems.
+
+fixes: freedesktop.org bug 25506
+
+Signed-off-by: Dave Airlie <airlied@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/radeon/atom.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/radeon/atom.c
++++ b/drivers/gpu/drm/radeon/atom.c
+@@ -607,7 +607,7 @@ static void atom_op_delay(atom_exec_cont
+ uint8_t count = U8((*ptr)++);
+ SDEBUG(" count: %d\n", count);
+ if (arg == ATOM_UNIT_MICROSEC)
+- schedule_timeout_uninterruptible(usecs_to_jiffies(count));
++ udelay(count);
+ else
+ schedule_timeout_uninterruptible(msecs_to_jiffies(count));
+ }
+From b9241ea31fae4887104e5d1b3b18f4009c25a0c4 Mon Sep 17 00:00:00 2001
+From: Zhenyu Wang <zhenyuw@linux.intel.com>
+Date: Wed, 25 Nov 2009 13:09:39 +0800
+Subject: drm/i915: Don't wait interruptible for possible plane buffer flush
+
+From: Zhenyu Wang <zhenyuw@linux.intel.com>
+
+commit b9241ea31fae4887104e5d1b3b18f4009c25a0c4 upstream.
+
+When we setup buffer for display plane, we'll check any pending
+required GPU flush and possible make interruptible wait for flush
+complete. But that wait would be most possibly to fail in case of
+signals received for X process, which will then fail modeset process
+and put display engine in unconsistent state. The result could be
+blank screen or CPU hang, and DDX driver would always turn on outputs
+DPMS after whatever modeset fails or not.
+
+So this one creates new helper for setup display plane buffer, and
+when needing flush using uninterruptible wait for that.
+
+This one should fix bug like https://bugs.freedesktop.org/show_bug.cgi?id=24009.
+Also fixing mode switch stress test on Ironlake.
+
+Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com>
+Signed-off-by: Eric Anholt <eric@anholt.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/i915/i915_drv.h | 1
+ drivers/gpu/drm/i915/i915_gem.c | 51 +++++++++++++++++++++++++++++++++++
+ drivers/gpu/drm/i915/intel_display.c | 2 -
+ 3 files changed, 53 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/i915/i915_drv.h
++++ b/drivers/gpu/drm/i915/i915_drv.h
+@@ -829,6 +829,7 @@ int i915_lp_ring_sync(struct drm_device
+ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
+ int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj,
+ int write);
++int i915_gem_object_set_to_display_plane(struct drm_gem_object *obj);
+ int i915_gem_attach_phys_object(struct drm_device *dev,
+ struct drm_gem_object *obj, int id);
+ void i915_gem_detach_phys_object(struct drm_device *dev,
+--- a/drivers/gpu/drm/i915/i915_gem.c
++++ b/drivers/gpu/drm/i915/i915_gem.c
+@@ -2825,6 +2825,57 @@ i915_gem_object_set_to_gtt_domain(struct
+ return 0;
+ }
+
++/*
++ * Prepare buffer for display plane. Use uninterruptible for possible flush
++ * wait, as in modesetting process we're not supposed to be interrupted.
++ */
++int
++i915_gem_object_set_to_display_plane(struct drm_gem_object *obj)
++{
++ struct drm_device *dev = obj->dev;
++ struct drm_i915_gem_object *obj_priv = obj->driver_private;
++ uint32_t old_write_domain, old_read_domains;
++ int ret;
++
++ /* Not valid to be called on unbound objects. */
++ if (obj_priv->gtt_space == NULL)
++ return -EINVAL;
++
++ i915_gem_object_flush_gpu_write_domain(obj);
++
++ /* Wait on any GPU rendering and flushing to occur. */
++ if (obj_priv->active) {
++#if WATCH_BUF
++ DRM_INFO("%s: object %p wait for seqno %08x\n",
++ __func__, obj, obj_priv->last_rendering_seqno);
++#endif
++ ret = i915_do_wait_request(dev, obj_priv->last_rendering_seqno, 0);
++ if (ret != 0)
++ return ret;
++ }
++
++ old_write_domain = obj->write_domain;
++ old_read_domains = obj->read_domains;
++
++ obj->read_domains &= I915_GEM_DOMAIN_GTT;
++
++ i915_gem_object_flush_cpu_write_domain(obj);
++
++ /* It should now be out of any other write domains, and we can update
++ * the domain values for our changes.
++ */
++ BUG_ON((obj->write_domain & ~I915_GEM_DOMAIN_GTT) != 0);
++ obj->read_domains |= I915_GEM_DOMAIN_GTT;
++ obj->write_domain = I915_GEM_DOMAIN_GTT;
++ obj_priv->dirty = 1;
++
++ trace_i915_gem_object_change_domain(obj,
++ old_read_domains,
++ old_write_domain);
++
++ return 0;
++}
++
+ /**
+ * Moves a single object to the CPU read, and possibly write domain.
+ *
+--- a/drivers/gpu/drm/i915/intel_display.c
++++ b/drivers/gpu/drm/i915/intel_display.c
+@@ -1253,7 +1253,7 @@ intel_pipe_set_base(struct drm_crtc *crt
+ return ret;
+ }
+
+- ret = i915_gem_object_set_to_gtt_domain(obj, 1);
++ ret = i915_gem_object_set_to_display_plane(obj);
+ if (ret != 0) {
+ i915_gem_object_unpin(obj);
+ mutex_unlock(&dev->struct_mutex);
+From 48764bf43f746113fc77877d7e80f2df23ca4cbb Mon Sep 17 00:00:00 2001
+From: Daniel Vetter <daniel.vetter@ffwll.ch>
+Date: Tue, 15 Sep 2009 22:57:32 +0200
+Subject: drm/i915: add i915_lp_ring_sync helper
+
+From: Daniel Vetter <daniel.vetter@ffwll.ch>
+
+commit 48764bf43f746113fc77877d7e80f2df23ca4cbb upstream.
+
+This just waits until the hw passed the current ring position with
+cmd execution. This slightly changes the existing i915_wait_request
+function to make uninterruptible waiting possible - no point in
+returning to userspace while mucking around with the overlay, that
+piece of hw is just too fragile.
+
+Also replace a magic 0 with the symbolic constant (and kill the then
+superflous comment) while I was looking at the code.
+
+Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
+Signed-off-by: Eric Anholt <eric@anholt.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/i915/i915_drv.h | 1
+ drivers/gpu/drm/i915/i915_gem.c | 49 +++++++++++++++++++++++++++++++---------
+ include/drm/drm_os_linux.h | 2 -
+ 3 files changed, 41 insertions(+), 11 deletions(-)
+
+--- a/drivers/gpu/drm/i915/i915_drv.h
++++ b/drivers/gpu/drm/i915/i915_drv.h
+@@ -825,6 +825,7 @@ void i915_gem_cleanup_ringbuffer(struct
+ int i915_gem_do_init(struct drm_device *dev, unsigned long start,
+ unsigned long end);
+ int i915_gem_idle(struct drm_device *dev);
++int i915_lp_ring_sync(struct drm_device *dev);
+ int i915_gem_fault(struct vm_area_struct *vma, struct vm_fault *vmf);
+ int i915_gem_object_set_to_gtt_domain(struct drm_gem_object *obj,
+ int write);
+--- a/drivers/gpu/drm/i915/i915_gem.c
++++ b/drivers/gpu/drm/i915/i915_gem.c
+@@ -1809,12 +1809,8 @@ i915_gem_retire_work_handler(struct work
+ mutex_unlock(&dev->struct_mutex);
+ }
+
+-/**
+- * Waits for a sequence number to be signaled, and cleans up the
+- * request and object lists appropriately for that event.
+- */
+ static int
+-i915_wait_request(struct drm_device *dev, uint32_t seqno)
++i915_do_wait_request(struct drm_device *dev, uint32_t seqno, int interruptible)
+ {
+ drm_i915_private_t *dev_priv = dev->dev_private;
+ u32 ier;
+@@ -1841,10 +1837,15 @@ i915_wait_request(struct drm_device *dev
+
+ dev_priv->mm.waiting_gem_seqno = seqno;
+ i915_user_irq_get(dev);
+- ret = wait_event_interruptible(dev_priv->irq_queue,
+- i915_seqno_passed(i915_get_gem_seqno(dev),
+- seqno) ||
+- atomic_read(&dev_priv->mm.wedged));
++ if (interruptible)
++ ret = wait_event_interruptible(dev_priv->irq_queue,
++ i915_seqno_passed(i915_get_gem_seqno(dev), seqno) ||
++ atomic_read(&dev_priv->mm.wedged));
++ else
++ wait_event(dev_priv->irq_queue,
++ i915_seqno_passed(i915_get_gem_seqno(dev), seqno) ||
++ atomic_read(&dev_priv->mm.wedged));
++
+ i915_user_irq_put(dev);
+ dev_priv->mm.waiting_gem_seqno = 0;
+
+@@ -1868,6 +1869,34 @@ i915_wait_request(struct drm_device *dev
+ return ret;
+ }
+
++/**
++ * Waits for a sequence number to be signaled, and cleans up the
++ * request and object lists appropriately for that event.
++ */
++static int
++i915_wait_request(struct drm_device *dev, uint32_t seqno)
++{
++ return i915_do_wait_request(dev, seqno, 1);
++}
++
++/**
++ * Waits for the ring to finish up to the latest request. Usefull for waiting
++ * for flip events, e.g for the overlay support. */
++int i915_lp_ring_sync(struct drm_device *dev)
++{
++ uint32_t seqno;
++ int ret;
++
++ seqno = i915_add_request(dev, NULL, 0);
++
++ if (seqno == 0)
++ return -ENOMEM;
++
++ ret = i915_do_wait_request(dev, seqno, 0);
++ BUG_ON(ret == -ERESTARTSYS);
++ return ret;
++}
++
+ static void
+ i915_gem_flush(struct drm_device *dev,
+ uint32_t invalidate_domains,
+@@ -1936,7 +1965,7 @@ i915_gem_flush(struct drm_device *dev,
+ #endif
+ BEGIN_LP_RING(2);
+ OUT_RING(cmd);
+- OUT_RING(0); /* noop */
++ OUT_RING(MI_NOOP);
+ ADVANCE_LP_RING();
+ }
+ }
+--- a/include/drm/drm_os_linux.h
++++ b/include/drm/drm_os_linux.h
+@@ -123,5 +123,5 @@ do { \
+ remove_wait_queue(&(queue), &entry); \
+ } while (0)
+
+-#define DRM_WAKEUP( queue ) wake_up_interruptible( queue )
++#define DRM_WAKEUP( queue ) wake_up( queue )
+ #define DRM_INIT_WAITQUEUE( queue ) init_waitqueue_head( queue )
+From 823f68fd646da6a39a9c0d3eb4c60d69dab5aa13 Mon Sep 17 00:00:00 2001
+From: Zhenyu Wang <zhenyuw@linux.intel.com>
+Date: Mon, 28 Dec 2009 13:23:36 +0800
+Subject: drm/i915: remove full registers dump debug
+
+From: Zhenyu Wang <zhenyuw@linux.intel.com>
+
+commit 823f68fd646da6a39a9c0d3eb4c60d69dab5aa13 upstream.
+
+This one reverts 9e3a6d155ed0a7636b926a798dd7221ea107b274.
+As reported by http://bugzilla.kernel.org/show_bug.cgi?id=14485,
+this dump will cause hang problem on some machine. If something
+really needs this kind of full registers dump, that could be done
+within intel-gpu-tools.
+
+Cc: Ben Gamari <bgamari.foss@gmail.com>
+Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com>
+Signed-off-by: Eric Anholt <eric@anholt.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ drivers/gpu/drm/i915/i915_debugfs.c | 30 ------------------------------
+ 1 file changed, 30 deletions(-)
+
+--- a/drivers/gpu/drm/i915/i915_debugfs.c
++++ b/drivers/gpu/drm/i915/i915_debugfs.c
+@@ -384,37 +384,7 @@ out:
+ return 0;
+ }
+
+-static int i915_registers_info(struct seq_file *m, void *data) {
+- struct drm_info_node *node = (struct drm_info_node *) m->private;
+- struct drm_device *dev = node->minor->dev;
+- drm_i915_private_t *dev_priv = dev->dev_private;
+- uint32_t reg;
+-
+-#define DUMP_RANGE(start, end) \
+- for (reg=start; reg < end; reg += 4) \
+- seq_printf(m, "%08x\t%08x\n", reg, I915_READ(reg));
+-
+- DUMP_RANGE(0x00000, 0x00fff); /* VGA registers */
+- DUMP_RANGE(0x02000, 0x02fff); /* instruction, memory, interrupt control registers */
+- DUMP_RANGE(0x03000, 0x031ff); /* FENCE and PPGTT control registers */
+- DUMP_RANGE(0x03200, 0x03fff); /* frame buffer compression registers */
+- DUMP_RANGE(0x05000, 0x05fff); /* I/O control registers */
+- DUMP_RANGE(0x06000, 0x06fff); /* clock control registers */
+- DUMP_RANGE(0x07000, 0x07fff); /* 3D internal debug registers */
+- DUMP_RANGE(0x07400, 0x088ff); /* GPE debug registers */
+- DUMP_RANGE(0x0a000, 0x0afff); /* display palette registers */
+- DUMP_RANGE(0x10000, 0x13fff); /* MMIO MCHBAR */
+- DUMP_RANGE(0x30000, 0x3ffff); /* overlay registers */
+- DUMP_RANGE(0x60000, 0x6ffff); /* display engine pipeline registers */
+- DUMP_RANGE(0x70000, 0x72fff); /* display and cursor registers */
+- DUMP_RANGE(0x73000, 0x73fff); /* performance counters */
+-
+- return 0;
+-}
+-
+-
+ static struct drm_info_list i915_debugfs_list[] = {
+- {"i915_regs", i915_registers_info, 0},
+ {"i915_gem_active", i915_gem_object_list_info, 0, (void *) ACTIVE_LIST},
+ {"i915_gem_flushing", i915_gem_object_list_info, 0, (void *) FLUSHING_LIST},
+ {"i915_gem_inactive", i915_gem_object_list_info, 0, (void *) INACTIVE_LIST},
+From 99fcb766a3a50466fe31d743260a3400c1aee855 Mon Sep 17 00:00:00 2001
+From: Daniel Vetter <daniel.vetter@ffwll.ch>
+Date: Sun, 7 Feb 2010 16:20:18 +0100
+Subject: drm/i915: Update write_domains on active list after flush.
+
+From: Daniel Vetter <daniel.vetter@ffwll.ch>
+
+commit 99fcb766a3a50466fe31d743260a3400c1aee855 upstream.
+
+Before changing the status of a buffer with a pending write we will await
+upon a new flush for that buffer. So we can take advantage of any flushes
+posted whilst the buffer is active and pending processing by the GPU, by
+clearing its write_domain and updating its last_rendering_seqno -- thus
+saving a potential flush in deep queues and improves flushing behaviour
+upon eviction for both GTT space and fences.
+
+In order to reduce the time spent searching the active list for matching
+write_domains, we move those to a separate list whose elements are
+the buffers belong to the active/flushing list with pending writes.
+
+Orignal patch by Chris Wilson <chris@chris-wilson.co.uk>, forward-ported
+by me.
+
+In addition to better performance, this also fixes a real bug. Before
+this changes, i915_gem_evict_everything didn't work as advertised. When
+the gpu was actually busy and processing request, the flush and subsequent
+wait would not move active and dirty buffers to the inactive list, but
+just to the flushing list. Which triggered the BUG_ON at the end of this
+function. With the more tight dirty buffer tracking, all currently busy and
+dirty buffers get moved to the inactive list by one i915_gem_flush operation.
+
+I've left the BUG_ON I've used to prove this in there.
+
+References:
+ Bug 25911 - 2.10.0 causes kernel oops and system hangs
+ http://bugs.freedesktop.org/show_bug.cgi?id=25911
+
+ Bug 26101 - [i915] xf86-video-intel 2.10.0 (and git) triggers kernel oops
+ within seconds after login
+ http://bugs.freedesktop.org/show_bug.cgi?id=26101
+
+Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
+Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
+Tested-by: Adam Lantos <hege@playma.org>
+Signed-off-by: Eric Anholt <eric@anholt.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/i915/i915_drv.h | 11 +++++++++++
+ drivers/gpu/drm/i915/i915_gem.c | 23 +++++++++++++++++++----
+ 2 files changed, 30 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/i915/i915_drv.h
++++ b/drivers/gpu/drm/i915/i915_drv.h
+@@ -467,6 +467,15 @@ typedef struct drm_i915_private {
+ struct list_head flushing_list;
+
+ /**
++ * List of objects currently pending a GPU write flush.
++ *
++ * All elements on this list will belong to either the
++ * active_list or flushing_list, last_rendering_seqno can
++ * be used to differentiate between the two elements.
++ */
++ struct list_head gpu_write_list;
++
++ /**
+ * LRU list of objects which are not in the ringbuffer and
+ * are ready to unbind, but are still in the GTT.
+ *
+@@ -558,6 +567,8 @@ struct drm_i915_gem_object {
+
+ /** This object's place on the active/flushing/inactive lists */
+ struct list_head list;
++ /** This object's place on GPU write list */
++ struct list_head gpu_write_list;
+
+ /** This object's place on the fenced object LRU */
+ struct list_head fence_list;
+--- a/drivers/gpu/drm/i915/i915_gem.c
++++ b/drivers/gpu/drm/i915/i915_gem.c
+@@ -1552,6 +1552,8 @@ i915_gem_object_move_to_inactive(struct
+ else
+ list_move_tail(&obj_priv->list, &dev_priv->mm.inactive_list);
+
++ BUG_ON(!list_empty(&obj_priv->gpu_write_list));
++
+ obj_priv->last_rendering_seqno = 0;
+ if (obj_priv->active) {
+ obj_priv->active = 0;
+@@ -1622,7 +1624,8 @@ i915_add_request(struct drm_device *dev,
+ struct drm_i915_gem_object *obj_priv, *next;
+
+ list_for_each_entry_safe(obj_priv, next,
+- &dev_priv->mm.flushing_list, list) {
++ &dev_priv->mm.gpu_write_list,
++ gpu_write_list) {
+ struct drm_gem_object *obj = obj_priv->obj;
+
+ if ((obj->write_domain & flush_domains) ==
+@@ -1630,6 +1633,7 @@ i915_add_request(struct drm_device *dev,
+ uint32_t old_write_domain = obj->write_domain;
+
+ obj->write_domain = 0;
++ list_del_init(&obj_priv->gpu_write_list);
+ i915_gem_object_move_to_active(obj, seqno);
+
+ trace_i915_gem_object_change_domain(obj,
+@@ -2073,8 +2077,8 @@ static int
+ i915_gem_evict_everything(struct drm_device *dev)
+ {
+ drm_i915_private_t *dev_priv = dev->dev_private;
+- uint32_t seqno;
+ int ret;
++ uint32_t seqno;
+ bool lists_empty;
+
+ spin_lock(&dev_priv->mm.active_list_lock);
+@@ -2096,6 +2100,8 @@ i915_gem_evict_everything(struct drm_dev
+ if (ret)
+ return ret;
+
++ BUG_ON(!list_empty(&dev_priv->mm.flushing_list));
++
+ ret = i915_gem_evict_from_inactive_list(dev);
+ if (ret)
+ return ret;
+@@ -2690,7 +2696,7 @@ i915_gem_object_flush_gpu_write_domain(s
+ old_write_domain = obj->write_domain;
+ i915_gem_flush(dev, 0, obj->write_domain);
+ seqno = i915_add_request(dev, NULL, obj->write_domain);
+- obj->write_domain = 0;
++ BUG_ON(obj->write_domain);
+ i915_gem_object_move_to_active(obj, seqno);
+
+ trace_i915_gem_object_change_domain(obj,
+@@ -3710,16 +3716,23 @@ i915_gem_execbuffer(struct drm_device *d
+ i915_gem_flush(dev,
+ dev->invalidate_domains,
+ dev->flush_domains);
+- if (dev->flush_domains)
++ if (dev->flush_domains & I915_GEM_GPU_DOMAINS)
+ (void)i915_add_request(dev, file_priv,
+ dev->flush_domains);
+ }
+
+ for (i = 0; i < args->buffer_count; i++) {
+ struct drm_gem_object *obj = object_list[i];
++ struct drm_i915_gem_object *obj_priv = obj->driver_private;
+ uint32_t old_write_domain = obj->write_domain;
+
+ obj->write_domain = obj->pending_write_domain;
++ if (obj->write_domain)
++ list_move_tail(&obj_priv->gpu_write_list,
++ &dev_priv->mm.gpu_write_list);
++ else
++ list_del_init(&obj_priv->gpu_write_list);
++
+ trace_i915_gem_object_change_domain(obj,
+ obj->read_domains,
+ old_write_domain);
+@@ -4112,6 +4125,7 @@ int i915_gem_init_object(struct drm_gem_
+ obj_priv->obj = obj;
+ obj_priv->fence_reg = I915_FENCE_REG_NONE;
+ INIT_LIST_HEAD(&obj_priv->list);
++ INIT_LIST_HEAD(&obj_priv->gpu_write_list);
+ INIT_LIST_HEAD(&obj_priv->fence_list);
+ obj_priv->madv = I915_MADV_WILLNEED;
+
+@@ -4563,6 +4577,7 @@ i915_gem_load(struct drm_device *dev)
+ spin_lock_init(&dev_priv->mm.active_list_lock);
+ INIT_LIST_HEAD(&dev_priv->mm.active_list);
+ INIT_LIST_HEAD(&dev_priv->mm.flushing_list);
++ INIT_LIST_HEAD(&dev_priv->mm.gpu_write_list);
+ INIT_LIST_HEAD(&dev_priv->mm.inactive_list);
+ INIT_LIST_HEAD(&dev_priv->mm.request_list);
+ INIT_LIST_HEAD(&dev_priv->mm.fence_list);
+From fd2e8ea597222b8f38ae8948776a61ea7958232e Mon Sep 17 00:00:00 2001
+From: Chris Wilson <chris@chris-wilson.co.uk>
+Date: Tue, 9 Feb 2010 14:14:36 +0000
+Subject: drm/i915: Increase fb alignment to 64k
+
+From: Chris Wilson <chris@chris-wilson.co.uk>
+
+commit fd2e8ea597222b8f38ae8948776a61ea7958232e upstream.
+
+An untiled framebuffer must be aligned to 64k. This is normally handled
+by intel_pin_and_fence_fb_obj(), but the intelfb_create() likes to be
+different and do the pinning itself. However, it aligns the buffer
+object incorrectly for pre-i965 chipsets causing a PGTBL_ERR when it is
+installed onto the output.
+
+Fixes:
+ KMS error message while initializing modesetting -
+ render error detected: EIR: 0x10 [i915]
+ http://bugs.freedesktop.org/show_bug.cgi?id=22936
+
+Signed-off-by: Chris Wilson <chris@chris-wilson.co.uk>
+Signed-off-by: Eric Anholt <eric@anholt.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/i915/intel_fb.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/i915/intel_fb.c
++++ b/drivers/gpu/drm/i915/intel_fb.c
+@@ -148,7 +148,7 @@ static int intelfb_create(struct drm_dev
+
+ mutex_lock(&dev->struct_mutex);
+
+- ret = i915_gem_object_pin(fbo, PAGE_SIZE);
++ ret = i915_gem_object_pin(fbo, 64*1024);
+ if (ret) {
+ DRM_ERROR("failed to pin fb: %d\n", ret);
+ goto out_unref;
+From ee25df2bc379728c45d81e04cf87984db1425edf Mon Sep 17 00:00:00 2001
+From: Jesse Barnes <jbarnes@virtuousgeek.org>
+Date: Sat, 6 Feb 2010 10:41:53 -0800
+Subject: drm/i915: handle FBC and self-refresh better
+
+From: Jesse Barnes <jbarnes@virtuousgeek.org>
+
+commit ee25df2bc379728c45d81e04cf87984db1425edf upstream.
+
+On 945, we need to avoid entering self-refresh if the compressor is
+busy, or we may cause display FIFO underruns leading to ugly flicker.
+
+Fixes fdo bug #24314, kernel bug #15043.
+
+Tested-by: Alexander Lam <lambchop468@gmail.com>
+Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
+Tested-by: Julien Cristau <jcristau@debian.org> (fd.o #25371)
+Signed-off-by: Eric Anholt <eric@anholt.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/i915/i915_reg.h | 1 +
+ drivers/gpu/drm/i915/intel_display.c | 2 ++
+ 2 files changed, 3 insertions(+)
+
+--- a/drivers/gpu/drm/i915/i915_reg.h
++++ b/drivers/gpu/drm/i915/i915_reg.h
+@@ -329,6 +329,7 @@
+ #define FBC_CTL_PERIODIC (1<<30)
+ #define FBC_CTL_INTERVAL_SHIFT (16)
+ #define FBC_CTL_UNCOMPRESSIBLE (1<<14)
++#define FBC_C3_IDLE (1<<13)
+ #define FBC_CTL_STRIDE_SHIFT (5)
+ #define FBC_CTL_FENCENO (1<<0)
+ #define FBC_COMMAND 0x0320c
+--- a/drivers/gpu/drm/i915/intel_display.c
++++ b/drivers/gpu/drm/i915/intel_display.c
+@@ -988,6 +988,8 @@ static void i8xx_enable_fbc(struct drm_c
+
+ /* enable it... */
+ fbc_ctl = FBC_CTL_EN | FBC_CTL_PERIODIC;
++ if (IS_I945GM(dev))
++ fbc_ctl |= FBC_C3_IDLE; /* 945 needs special SR handling */
+ fbc_ctl |= (dev_priv->cfb_pitch & 0xff) << FBC_CTL_STRIDE_SHIFT;
+ fbc_ctl |= (interval & 0x2fff) << FBC_CTL_INTERVAL_SHIFT;
+ if (obj_priv->tiling_mode != I915_TILING_NONE)
+From a3cb5195f6db58dbebd8a31b877ddce082c9b63d Mon Sep 17 00:00:00 2001
+From: Zhao Yakui <yakui.zhao@intel.com>
+Date: Fri, 11 Dec 2009 09:26:10 +0800
+Subject: drm/i915: Add MALATA PC-81005 to ACPI LID quirk list
+
+From: Zhao Yakui <yakui.zhao@intel.com>
+
+commit a3cb5195f6db58dbebd8a31b877ddce082c9b63d upstream.
+
+The MALATA PC-81005 laptop always reports that the LID status is closed and we
+can't use it reliabily for LVDS detection. So add this box into the quirk list.
+
+https://bugs.freedesktop.org/show_bug.cgi?id=25523
+
+Signed-off-by: Zhao Yakui <yakui.zhao@intel.com>
+Review-by: Jesse Barnes <jbarnes@virtuousgeek.org>
+Tested-by: Hector <hector1987@gmail.com>
+Signed-off-by: Eric Anholt <eric@anholt.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/i915/intel_lvds.c | 7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/gpu/drm/i915/intel_lvds.c
++++ b/drivers/gpu/drm/i915/intel_lvds.c
+@@ -622,6 +622,13 @@ static const struct dmi_system_id bad_li
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire one"),
+ },
+ },
++ {
++ .ident = "PC-81005",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "MALATA"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "PC-81005"),
++ },
++ },
+ { }
+ };
+
+From f034b12dbb5749b11e9390e15e93ffa87ece8038 Mon Sep 17 00:00:00 2001
+From: Zhao Yakui <yakui.zhao@intel.com>
+Date: Thu, 21 Jan 2010 15:20:18 +0800
+Subject: drm/i915: Fix the incorrect DMI string for Samsung SX20S laptop
+
+From: Zhao Yakui <yakui.zhao@intel.com>
+
+commit f034b12dbb5749b11e9390e15e93ffa87ece8038 upstream.
+
+Signed-off-by: Zhao Yakui <yakui.zhao@intel.com>
+Reported-by: Philipp Kohlbecher <xt28@gmx.de>
+Signed-off-by: Eric Anholt <eric@anholt.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/i915/intel_lvds.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/i915/intel_lvds.c
++++ b/drivers/gpu/drm/i915/intel_lvds.c
+@@ -611,7 +611,7 @@ static const struct dmi_system_id bad_li
+ {
+ .ident = "Samsung SX20S",
+ .matches = {
+- DMI_MATCH(DMI_SYS_VENDOR, "Phoenix Technologies LTD"),
++ DMI_MATCH(DMI_SYS_VENDOR, "Samsung Electronics"),
+ DMI_MATCH(DMI_BOARD_NAME, "SX20S"),
+ },
+ },
+From 40f33a92100f4d9b6e85ad642100cfe42d7ff57d Mon Sep 17 00:00:00 2001
+From: Zhao Yakui <yakui.zhao@intel.com>
+Date: Wed, 6 Jan 2010 13:30:36 +0800
+Subject: drm/i915: Add HP nx9020/SamsungSX20S to ACPI LID quirk list
+
+From: Zhao Yakui <yakui.zhao@intel.com>
+
+commit 40f33a92100f4d9b6e85ad642100cfe42d7ff57d upstream.
+
+The HP comaq nx9020/Samsung SX20S laptop always report that the LID status is
+closed and we can't use it reliabily for LVDS detection. So add the two boxes
+into the quirk list.
+
+http://bugzilla.kernel.org/show_bug.cgi?id=14957
+http://bugzilla.kernel.org/show_bug.cgi?id=14554
+
+Signed-off-by: Zhao Yakui <yakui.zhao@intel.com>
+Signed-off-by: Eric Anholt <eric@anholt.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/i915/intel_lvds.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/drivers/gpu/drm/i915/intel_lvds.c
++++ b/drivers/gpu/drm/i915/intel_lvds.c
+@@ -602,6 +602,20 @@ static void intel_lvds_mode_set(struct d
+ /* Some lid devices report incorrect lid status, assume they're connected */
+ static const struct dmi_system_id bad_lid_status[] = {
+ {
++ .ident = "Compaq nx9020",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
++ DMI_MATCH(DMI_BOARD_NAME, "3084"),
++ },
++ },
++ {
++ .ident = "Samsung SX20S",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "Phoenix Technologies LTD"),
++ DMI_MATCH(DMI_BOARD_NAME, "SX20S"),
++ },
++ },
++ {
+ .ident = "Aspire One",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+From f0217c42c9ab3d772e543f635ce628b9478f70b6 Mon Sep 17 00:00:00 2001
+From: Eric Anholt <eric@anholt.net>
+Date: Tue, 1 Dec 2009 11:56:30 -0800
+Subject: drm/i915: Fix DDC on some systems by clearing BIOS GMBUS setup.
+
+From: Eric Anholt <eric@anholt.net>
+
+commit f0217c42c9ab3d772e543f635ce628b9478f70b6 upstream.
+
+This is a sync of a fix I made in the old UMS code. If the BIOS uses
+the GMBUS and doesn't clear that setup, then our bit-banging I2C can
+fail, leading to monitors not being detected.
+
+Signed-off-by: Eric Anholt <eric@anholt.net>
+Cc: maximilian attems <max@stro.at>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/i915/i915_reg.h | 14 ++++++++++++++
+ drivers/gpu/drm/i915/i915_suspend.c | 5 ++++-
+ drivers/gpu/drm/i915/intel_drv.h | 2 ++
+ drivers/gpu/drm/i915/intel_i2c.c | 19 +++++++++++++++++++
+ 4 files changed, 39 insertions(+), 1 deletion(-)
+
+--- a/drivers/gpu/drm/i915/i915_reg.h
++++ b/drivers/gpu/drm/i915/i915_reg.h
+@@ -405,6 +405,13 @@
+ # define GPIO_DATA_VAL_IN (1 << 12)
+ # define GPIO_DATA_PULLUP_DISABLE (1 << 13)
+
++#define GMBUS0 0x5100
++#define GMBUS1 0x5104
++#define GMBUS2 0x5108
++#define GMBUS3 0x510c
++#define GMBUS4 0x5110
++#define GMBUS5 0x5120
++
+ /*
+ * Clock control & power management
+ */
+@@ -2153,6 +2160,13 @@
+ #define PCH_GPIOE 0xc5020
+ #define PCH_GPIOF 0xc5024
+
++#define PCH_GMBUS0 0xc5100
++#define PCH_GMBUS1 0xc5104
++#define PCH_GMBUS2 0xc5108
++#define PCH_GMBUS3 0xc510c
++#define PCH_GMBUS4 0xc5110
++#define PCH_GMBUS5 0xc5120
++
+ #define PCH_DPLL_A 0xc6014
+ #define PCH_DPLL_B 0xc6018
+
+--- a/drivers/gpu/drm/i915/i915_suspend.c
++++ b/drivers/gpu/drm/i915/i915_suspend.c
+@@ -27,7 +27,7 @@
+ #include "drmP.h"
+ #include "drm.h"
+ #include "i915_drm.h"
+-#include "i915_drv.h"
++#include "intel_drv.h"
+
+ static bool i915_pipe_enabled(struct drm_device *dev, enum pipe pipe)
+ {
+@@ -846,6 +846,9 @@ int i915_restore_state(struct drm_device
+ for (i = 0; i < 3; i++)
+ I915_WRITE(SWF30 + (i << 2), dev_priv->saveSWF2[i]);
+
++ /* I2C state */
++ intel_i2c_reset_gmbus(dev);
++
+ return 0;
+ }
+
+--- a/drivers/gpu/drm/i915/intel_drv.h
++++ b/drivers/gpu/drm/i915/intel_drv.h
+@@ -134,6 +134,8 @@ void intel_i2c_destroy(struct i2c_adapte
+ int intel_ddc_get_modes(struct intel_output *intel_output);
+ extern bool intel_ddc_probe(struct intel_output *intel_output);
+ void intel_i2c_quirk_set(struct drm_device *dev, bool enable);
++void intel_i2c_reset_gmbus(struct drm_device *dev);
++
+ extern void intel_crt_init(struct drm_device *dev);
+ extern void intel_hdmi_init(struct drm_device *dev, int sdvox_reg);
+ extern bool intel_sdvo_init(struct drm_device *dev, int output_device);
+--- a/drivers/gpu/drm/i915/intel_i2c.c
++++ b/drivers/gpu/drm/i915/intel_i2c.c
+@@ -118,6 +118,23 @@ static void set_data(void *data, int sta
+ udelay(I2C_RISEFALL_TIME); /* wait for the line to change state */
+ }
+
++/* Clears the GMBUS setup. Our driver doesn't make use of the GMBUS I2C
++ * engine, but if the BIOS leaves it enabled, then that can break our use
++ * of the bit-banging I2C interfaces. This is notably the case with the
++ * Mac Mini in EFI mode.
++ */
++void
++intel_i2c_reset_gmbus(struct drm_device *dev)
++{
++ struct drm_i915_private *dev_priv = dev->dev_private;
++
++ if (IS_IGDNG(dev)) {
++ I915_WRITE(PCH_GMBUS0, 0);
++ } else {
++ I915_WRITE(GMBUS0, 0);
++ }
++}
++
+ /**
+ * intel_i2c_create - instantiate an Intel i2c bus using the specified GPIO reg
+ * @dev: DRM device
+@@ -168,6 +185,8 @@ struct i2c_adapter *intel_i2c_create(str
+ if(i2c_bit_add_bus(&chan->adapter))
+ goto out_free;
+
++ intel_i2c_reset_gmbus(dev);
++
+ /* JJJ: raise SCL and SDA? */
+ intel_i2c_quirk_set(dev, true);
+ set_data(chan, 1);
+From 33c5fd121eabbccc9103daf6cda36941eb3c349f Mon Sep 17 00:00:00 2001
+From: David John <davidjon@xenontk.org>
+Date: Wed, 27 Jan 2010 15:19:08 +0530
+Subject: drm/i915: Disable SR when more than one pipe is enabled
+
+From: David John <davidjon@xenontk.org>
+
+commit 33c5fd121eabbccc9103daf6cda36941eb3c349f upstream.
+
+Self Refresh should be disabled on dual plane configs. Otherwise, as
+the SR watermark is not calculated for such configs, switching to non
+VGA mode causes FIFO underrun and display flicker.
+
+This fixes Korg Bug #14897.
+
+Signed-off-by: David John <davidjon@xenontk.org>
+Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
+Signed-off-by: Eric Anholt <eric@anholt.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/i915/intel_display.c | 12 ++++++++++++
+ 1 file changed, 12 insertions(+)
+
+--- a/drivers/gpu/drm/i915/intel_display.c
++++ b/drivers/gpu/drm/i915/intel_display.c
+@@ -2538,6 +2538,10 @@ static void g4x_update_wm(struct drm_dev
+ sr_entries = roundup(sr_entries / cacheline_size, 1);
+ DRM_DEBUG("self-refresh entries: %d\n", sr_entries);
+ I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN);
++ } else {
++ /* Turn off self refresh if both pipes are enabled */
++ I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
++ & ~FW_BLC_SELF_EN);
+ }
+
+ DRM_DEBUG("Setting FIFO watermarks - A: %d, B: %d, SR %d\n",
+@@ -2581,6 +2585,10 @@ static void i965_update_wm(struct drm_de
+ srwm = 1;
+ srwm &= 0x3f;
+ I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN);
++ } else {
++ /* Turn off self refresh if both pipes are enabled */
++ I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
++ & ~FW_BLC_SELF_EN);
+ }
+
+ DRM_DEBUG_KMS("Setting FIFO watermarks - A: 8, B: 8, C: 8, SR %d\n",
+@@ -2649,6 +2657,10 @@ static void i9xx_update_wm(struct drm_de
+ if (srwm < 0)
+ srwm = 1;
+ I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN | (srwm & 0x3f));
++ } else {
++ /* Turn off self refresh if both pipes are enabled */
++ I915_WRITE(FW_BLC_SELF, I915_READ(FW_BLC_SELF)
++ & ~FW_BLC_SELF_EN);
+ }
+
+ DRM_DEBUG("Setting FIFO watermarks - A: %d, B: %d, C: %d, SR %d\n",
+From 1dc7546d1a73664e5d117715b214bea9cae5951c Mon Sep 17 00:00:00 2001
+From: Jesse Barnes <jbarnes@jbarnes-x200.(none)>
+Date: Mon, 19 Oct 2009 10:08:17 +0900
+Subject: drm/i915: enable self-refresh on 965
+
+From: Jesse Barnes <jbarnes@jbarnes-x200.(none)>
+
+commit 1dc7546d1a73664e5d117715b214bea9cae5951c upstream.
+
+Need to calculate the SR watermark and enable it.
+
+Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
+Signed-off-by: Eric Anholt <eric@anholt.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/i915/intel_display.c | 32 ++++++++++++++++++++++++++++----
+ 1 file changed, 28 insertions(+), 4 deletions(-)
+
+--- a/drivers/gpu/drm/i915/intel_display.c
++++ b/drivers/gpu/drm/i915/intel_display.c
+@@ -2556,15 +2556,39 @@ static void g4x_update_wm(struct drm_dev
+ (cursor_sr << DSPFW_CURSOR_SR_SHIFT));
+ }
+
+-static void i965_update_wm(struct drm_device *dev, int unused, int unused2,
+- int unused3, int unused4)
++static void i965_update_wm(struct drm_device *dev, int planea_clock,
++ int planeb_clock, int sr_hdisplay, int pixel_size)
+ {
+ struct drm_i915_private *dev_priv = dev->dev_private;
++ unsigned long line_time_us;
++ int sr_clock, sr_entries, srwm = 1;
+
+- DRM_DEBUG("Setting FIFO watermarks - A: 8, B: 8, C: 8, SR 8\n");
++ /* Calc sr entries for one plane configs */
++ if (sr_hdisplay && (!planea_clock || !planeb_clock)) {
++ /* self-refresh has much higher latency */
++ const static int sr_latency_ns = 12000;
++
++ sr_clock = planea_clock ? planea_clock : planeb_clock;
++ line_time_us = ((sr_hdisplay * 1000) / sr_clock);
++
++ /* Use ns/us then divide to preserve precision */
++ sr_entries = (((sr_latency_ns / line_time_us) + 1) *
++ pixel_size * sr_hdisplay) / 1000;
++ sr_entries = roundup(sr_entries / I915_FIFO_LINE_SIZE, 1);
++ DRM_DEBUG("self-refresh entries: %d\n", sr_entries);
++ srwm = I945_FIFO_SIZE - sr_entries;
++ if (srwm < 0)
++ srwm = 1;
++ srwm &= 0x3f;
++ I915_WRITE(FW_BLC_SELF, FW_BLC_SELF_EN);
++ }
++
++ DRM_DEBUG_KMS("Setting FIFO watermarks - A: 8, B: 8, C: 8, SR %d\n",
++ srwm);
+
+ /* 965 has limitations... */
+- I915_WRITE(DSPFW1, (8 << 16) | (8 << 8) | (8 << 0));
++ I915_WRITE(DSPFW1, (srwm << DSPFW_SR_SHIFT) | (8 << 16) | (8 << 8) |
++ (8 << 0));
+ I915_WRITE(DSPFW2, (8 << 8) | (8 << 0));
+ }
+
+From eceb784cec4dc0fcc2993d9ee4a7c0d111ada80a Mon Sep 17 00:00:00 2001
+From: Zhenyu Wang <zhenyuw@linux.intel.com>
+Date: Mon, 25 Jan 2010 10:35:16 +0800
+Subject: drm/i915: disable hotplug detect before Ironlake CRT detect
+
+From: Zhenyu Wang <zhenyuw@linux.intel.com>
+
+commit eceb784cec4dc0fcc2993d9ee4a7c0d111ada80a upstream.
+
+This tries to fix CRT detect loop hang seen on some Ironlake form
+factor, to clear up hotplug detect state before taking CRT detect
+to make sure next hotplug detect cycle is consistent.
+
+Signed-off-by: Zhenyu Wang <zhenyuw@linux.intel.com>
+Signed-off-by: Eric Anholt <eric@anholt.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/gpu/drm/i915/intel_crt.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/gpu/drm/i915/intel_crt.c
++++ b/drivers/gpu/drm/i915/intel_crt.c
+@@ -185,6 +185,9 @@ static bool intel_igdng_crt_detect_hotpl
+ adpa = I915_READ(PCH_ADPA);
+
+ adpa &= ~ADPA_CRT_HOTPLUG_MASK;
++ /* disable HPD first */
++ I915_WRITE(PCH_ADPA, adpa);
++ (void)I915_READ(PCH_ADPA);
+
+ adpa |= (ADPA_CRT_HOTPLUG_PERIOD_128 |
+ ADPA_CRT_HOTPLUG_WARMUP_10MS |
diff --git a/linux-2.6-usb-uvc-autosuspend.diff b/linux-2.6-usb-uvc-autosuspend.diff
new file mode 100644
index 0000000..b7c7f6e
--- /dev/null
+++ b/linux-2.6-usb-uvc-autosuspend.diff
@@ -0,0 +1,19 @@
+commit 9d4c919bcfa794c054cc33155c7e3c53ac2c5684
+Author: Matthew Garrett <mjg@redhat.com>
+Date: Sun Jul 19 02:24:49 2009 +0100
+
+ Enable autosuspend on UVC by default
+
+diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
+index 89927b7..8de516b 100644
+--- a/drivers/media/video/uvc/uvc_driver.c
++++ b/drivers/media/video/uvc/uvc_driver.c
+@@ -1647,6 +1647,8 @@ static int uvc_probe(struct usb_interface *intf,
+ "supported.\n", ret);
+ }
+
++ usb_device_autosuspend_enable(udev);
++
+ uvc_trace(UVC_TRACE_PROBE, "UVC device initialized.\n");
+ return 0;
+
diff --git a/linux-2.6-usb-wwan-update.patch b/linux-2.6-usb-wwan-update.patch
new file mode 100644
index 0000000..d6ab3d3
--- /dev/null
+++ b/linux-2.6-usb-wwan-update.patch
@@ -0,0 +1,1634 @@
+diff -up linux-2.6.32.noarch/drivers/usb/serial/Kconfig.orig linux-2.6.32.noarch/drivers/usb/serial/Kconfig
+--- linux-2.6.32.noarch/drivers/usb/serial/Kconfig.orig 2009-12-02 22:51:21.000000000 -0500
++++ linux-2.6.32.noarch/drivers/usb/serial/Kconfig 2010-04-01 12:52:26.989997164 -0400
+@@ -565,8 +565,12 @@ config USB_SERIAL_XIRCOM
+ To compile this driver as a module, choose M here: the
+ module will be called keyspan_pda.
+
++config USB_SERIAL_WWAN
++ tristate
++
+ config USB_SERIAL_OPTION
+ tristate "USB driver for GSM and CDMA modems"
++ select USB_SERIAL_WWAN
+ help
+ Say Y here if you have a GSM or CDMA modem that's connected to USB.
+
+diff -up linux-2.6.32.noarch/drivers/usb/serial/Makefile.orig linux-2.6.32.noarch/drivers/usb/serial/Makefile
+--- linux-2.6.32.noarch/drivers/usb/serial/Makefile.orig 2009-12-02 22:51:21.000000000 -0500
++++ linux-2.6.32.noarch/drivers/usb/serial/Makefile 2010-04-01 12:52:26.992996185 -0400
+@@ -51,6 +51,7 @@ obj-$(CONFIG_USB_SERIAL_SIEMENS_MPI) +=
+ obj-$(CONFIG_USB_SERIAL_SIERRAWIRELESS) += sierra.o
+ obj-$(CONFIG_USB_SERIAL_SPCP8X5) += spcp8x5.o
+ obj-$(CONFIG_USB_SERIAL_SYMBOL) += symbolserial.o
++obj-$(CONFIG_USB_SERIAL_WWAN) += usb_wwan.o
+ obj-$(CONFIG_USB_SERIAL_TI) += ti_usb_3410_5052.o
+ obj-$(CONFIG_USB_SERIAL_VISOR) += visor.o
+ obj-$(CONFIG_USB_SERIAL_WHITEHEAT) += whiteheat.o
+diff -up linux-2.6.32.noarch/drivers/usb/serial/option.c.orig linux-2.6.32.noarch/drivers/usb/serial/option.c
+--- linux-2.6.32.noarch/drivers/usb/serial/option.c.orig 2010-04-01 12:51:42.346995579 -0400
++++ linux-2.6.32.noarch/drivers/usb/serial/option.c 2010-04-01 12:53:47.537995720 -0400
+@@ -41,35 +41,14 @@
+ #include <linux/bitops.h>
+ #include <linux/usb.h>
+ #include <linux/usb/serial.h>
++#include "usb-wwan.h"
+
+ /* Function prototypes */
+ static int option_probe(struct usb_serial *serial,
+ const struct usb_device_id *id);
+-static int option_open(struct tty_struct *tty, struct usb_serial_port *port);
+-static void option_close(struct usb_serial_port *port);
+-static void option_dtr_rts(struct usb_serial_port *port, int on);
+-
+-static int option_startup(struct usb_serial *serial);
+-static void option_disconnect(struct usb_serial *serial);
+-static void option_release(struct usb_serial *serial);
+-static int option_write_room(struct tty_struct *tty);
+-
++static int option_send_setup(struct usb_serial_port *port);
+ static void option_instat_callback(struct urb *urb);
+
+-static int option_write(struct tty_struct *tty, struct usb_serial_port *port,
+- const unsigned char *buf, int count);
+-static int option_chars_in_buffer(struct tty_struct *tty);
+-static void option_set_termios(struct tty_struct *tty,
+- struct usb_serial_port *port, struct ktermios *old);
+-static int option_tiocmget(struct tty_struct *tty, struct file *file);
+-static int option_tiocmset(struct tty_struct *tty, struct file *file,
+- unsigned int set, unsigned int clear);
+-static int option_send_setup(struct usb_serial_port *port);
+-#ifdef CONFIG_PM
+-static int option_suspend(struct usb_serial *serial, pm_message_t message);
+-static int option_resume(struct usb_serial *serial);
+-#endif
+-
+ /* Vendor and product IDs */
+ #define OPTION_VENDOR_ID 0x0AF0
+ #define OPTION_PRODUCT_COLT 0x5000
+@@ -677,22 +656,22 @@ static struct usb_serial_driver option_1
+ .id_table = option_ids,
+ .num_ports = 1,
+ .probe = option_probe,
+- .open = option_open,
+- .close = option_close,
+- .dtr_rts = option_dtr_rts,
+- .write = option_write,
+- .write_room = option_write_room,
+- .chars_in_buffer = option_chars_in_buffer,
+- .set_termios = option_set_termios,
+- .tiocmget = option_tiocmget,
+- .tiocmset = option_tiocmset,
+- .attach = option_startup,
+- .disconnect = option_disconnect,
+- .release = option_release,
++ .open = usb_wwan_open,
++ .close = usb_wwan_close,
++ .dtr_rts = usb_wwan_dtr_rts,
++ .write = usb_wwan_write,
++ .write_room = usb_wwan_write_room,
++ .chars_in_buffer = usb_wwan_chars_in_buffer,
++ .set_termios = usb_wwan_set_termios,
++ .tiocmget = usb_wwan_tiocmget,
++ .tiocmset = usb_wwan_tiocmset,
++ .attach = usb_wwan_startup,
++ .disconnect = usb_wwan_disconnect,
++ .release = usb_wwan_release,
+ .read_int_callback = option_instat_callback,
+ #ifdef CONFIG_PM
+- .suspend = option_suspend,
+- .resume = option_resume,
++ .suspend = usb_wwan_suspend,
++ .resume = usb_wwan_resume,
+ #endif
+ };
+
+@@ -705,12 +684,6 @@ static int debug;
+ #define IN_BUFLEN 4096
+ #define OUT_BUFLEN 4096
+
+-struct option_intf_private {
+- spinlock_t susp_lock;
+- unsigned int suspended:1;
+- int in_flight;
+-};
+-
+ struct option_port_private {
+ /* Input endpoints and buffer for this port */
+ struct urb *in_urbs[N_IN_URB];
+@@ -767,216 +740,28 @@ module_exit(option_exit);
+ static int option_probe(struct usb_serial *serial,
+ const struct usb_device_id *id)
+ {
+- struct option_intf_private *data;
++ struct usb_wwan_intf_private *data;
+
+ /* D-Link DWM 652 still exposes CD-Rom emulation interface in modem mode */
+ if (serial->dev->descriptor.idVendor == DLINK_VENDOR_ID &&
+ serial->dev->descriptor.idProduct == DLINK_PRODUCT_DWM_652 &&
+ serial->interface->cur_altsetting->desc.bInterfaceClass == 0x8)
+ return -ENODEV;
+
+ /* Bandrich modem and AT command interface is 0xff */
+ if ((serial->dev->descriptor.idVendor == BANDRICH_VENDOR_ID ||
+ serial->dev->descriptor.idVendor == PIRELLI_VENDOR_ID) &&
+ serial->interface->cur_altsetting->desc.bInterfaceClass != 0xff)
+ return -ENODEV;
+
+- data = serial->private = kzalloc(sizeof(struct option_intf_private), GFP_KERNEL);
++ data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
++ data->send_setup = option_send_setup;
+ spin_lock_init(&data->susp_lock);
+ return 0;
+ }
+
+-static void option_set_termios(struct tty_struct *tty,
+- struct usb_serial_port *port, struct ktermios *old_termios)
+-{
+- dbg("%s", __func__);
+- /* Doesn't support option setting */
+- tty_termios_copy_hw(tty->termios, old_termios);
+- option_send_setup(port);
+-}
+-
+-static int option_tiocmget(struct tty_struct *tty, struct file *file)
+-{
+- struct usb_serial_port *port = tty->driver_data;
+- unsigned int value;
+- struct option_port_private *portdata;
+-
+- portdata = usb_get_serial_port_data(port);
+-
+- value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
+- ((portdata->dtr_state) ? TIOCM_DTR : 0) |
+- ((portdata->cts_state) ? TIOCM_CTS : 0) |
+- ((portdata->dsr_state) ? TIOCM_DSR : 0) |
+- ((portdata->dcd_state) ? TIOCM_CAR : 0) |
+- ((portdata->ri_state) ? TIOCM_RNG : 0);
+-
+- return value;
+-}
+-
+-static int option_tiocmset(struct tty_struct *tty, struct file *file,
+- unsigned int set, unsigned int clear)
+-{
+- struct usb_serial_port *port = tty->driver_data;
+- struct option_port_private *portdata;
+-
+- portdata = usb_get_serial_port_data(port);
+-
+- /* FIXME: what locks portdata fields ? */
+- if (set & TIOCM_RTS)
+- portdata->rts_state = 1;
+- if (set & TIOCM_DTR)
+- portdata->dtr_state = 1;
+-
+- if (clear & TIOCM_RTS)
+- portdata->rts_state = 0;
+- if (clear & TIOCM_DTR)
+- portdata->dtr_state = 0;
+- return option_send_setup(port);
+-}
+-
+-/* Write */
+-static int option_write(struct tty_struct *tty, struct usb_serial_port *port,
+- const unsigned char *buf, int count)
+-{
+- struct option_port_private *portdata;
+- struct option_intf_private *intfdata;
+- int i;
+- int left, todo;
+- struct urb *this_urb = NULL; /* spurious */
+- int err;
+- unsigned long flags;
+-
+- portdata = usb_get_serial_port_data(port);
+- intfdata = port->serial->private;
+-
+- dbg("%s: write (%d chars)", __func__, count);
+-
+- i = 0;
+- left = count;
+- for (i = 0; left > 0 && i < N_OUT_URB; i++) {
+- todo = left;
+- if (todo > OUT_BUFLEN)
+- todo = OUT_BUFLEN;
+-
+- this_urb = portdata->out_urbs[i];
+- if (test_and_set_bit(i, &portdata->out_busy)) {
+- if (time_before(jiffies,
+- portdata->tx_start_time[i] + 10 * HZ))
+- continue;
+- usb_unlink_urb(this_urb);
+- continue;
+- }
+- dbg("%s: endpoint %d buf %d", __func__,
+- usb_pipeendpoint(this_urb->pipe), i);
+-
+- err = usb_autopm_get_interface_async(port->serial->interface);
+- if (err < 0)
+- break;
+-
+- /* send the data */
+- memcpy(this_urb->transfer_buffer, buf, todo);
+- this_urb->transfer_buffer_length = todo;
+-
+- spin_lock_irqsave(&intfdata->susp_lock, flags);
+- if (intfdata->suspended) {
+- usb_anchor_urb(this_urb, &portdata->delayed);
+- spin_unlock_irqrestore(&intfdata->susp_lock, flags);
+- } else {
+- intfdata->in_flight++;
+- spin_unlock_irqrestore(&intfdata->susp_lock, flags);
+- err = usb_submit_urb(this_urb, GFP_ATOMIC);
+- if (err) {
+- dbg("usb_submit_urb %p (write bulk) failed "
+- "(%d)", this_urb, err);
+- clear_bit(i, &portdata->out_busy);
+- spin_lock_irqsave(&intfdata->susp_lock, flags);
+- intfdata->in_flight--;
+- spin_unlock_irqrestore(&intfdata->susp_lock, flags);
+- continue;
+- }
+- }
+-
+- portdata->tx_start_time[i] = jiffies;
+- buf += todo;
+- left -= todo;
+- }
+-
+- count -= left;
+- dbg("%s: wrote (did %d)", __func__, count);
+- return count;
+-}
+-
+-static void option_indat_callback(struct urb *urb)
+-{
+- int err;
+- int endpoint;
+- struct usb_serial_port *port;
+- struct tty_struct *tty;
+- unsigned char *data = urb->transfer_buffer;
+- int status = urb->status;
+-
+- dbg("%s: %p", __func__, urb);
+-
+- endpoint = usb_pipeendpoint(urb->pipe);
+- port = urb->context;
+-
+- if (status) {
+- dbg("%s: nonzero status: %d on endpoint %02x.",
+- __func__, status, endpoint);
+- } else {
+- tty = tty_port_tty_get(&port->port);
+- if (urb->actual_length) {
+- tty_buffer_request_room(tty, urb->actual_length);
+- tty_insert_flip_string(tty, data, urb->actual_length);
+- tty_flip_buffer_push(tty);
+- } else
+- dbg("%s: empty read urb received", __func__);
+- tty_kref_put(tty);
+-
+- /* Resubmit urb so we continue receiving */
+- if (port->port.count && status != -ESHUTDOWN) {
+- err = usb_submit_urb(urb, GFP_ATOMIC);
+- if (err)
+- printk(KERN_ERR "%s: resubmit read urb failed. "
+- "(%d)", __func__, err);
+- else
+- usb_mark_last_busy(port->serial->dev);
+- }
+-
+- }
+- return;
+-}
+-
+-static void option_outdat_callback(struct urb *urb)
+-{
+- struct usb_serial_port *port;
+- struct option_port_private *portdata;
+- struct option_intf_private *intfdata;
+- int i;
+-
+- dbg("%s", __func__);
+-
+- port = urb->context;
+- intfdata = port->serial->private;
+-
+- usb_serial_port_softint(port);
+- usb_autopm_put_interface_async(port->serial->interface);
+- portdata = usb_get_serial_port_data(port);
+- spin_lock(&intfdata->susp_lock);
+- intfdata->in_flight--;
+- spin_unlock(&intfdata->susp_lock);
+-
+- for (i = 0; i < N_OUT_URB; ++i) {
+- if (portdata->out_urbs[i] == urb) {
+- smp_mb__before_clear_bit();
+- clear_bit(i, &portdata->out_busy);
+- break;
+- }
+- }
+-}
+-
+ static void option_instat_callback(struct urb *urb)
+ {
+ int err;
+@@ -1026,183 +811,6 @@ static void option_instat_callback(struc
+ }
+ }
+
+-static int option_write_room(struct tty_struct *tty)
+-{
+- struct usb_serial_port *port = tty->driver_data;
+- struct option_port_private *portdata;
+- int i;
+- int data_len = 0;
+- struct urb *this_urb;
+-
+- portdata = usb_get_serial_port_data(port);
+-
+- for (i = 0; i < N_OUT_URB; i++) {
+- this_urb = portdata->out_urbs[i];
+- if (this_urb && !test_bit(i, &portdata->out_busy))
+- data_len += OUT_BUFLEN;
+- }
+-
+- dbg("%s: %d", __func__, data_len);
+- return data_len;
+-}
+-
+-static int option_chars_in_buffer(struct tty_struct *tty)
+-{
+- struct usb_serial_port *port = tty->driver_data;
+- struct option_port_private *portdata;
+- int i;
+- int data_len = 0;
+- struct urb *this_urb;
+-
+- portdata = usb_get_serial_port_data(port);
+-
+- for (i = 0; i < N_OUT_URB; i++) {
+- this_urb = portdata->out_urbs[i];
+- /* FIXME: This locking is insufficient as this_urb may
+- go unused during the test */
+- if (this_urb && test_bit(i, &portdata->out_busy))
+- data_len += this_urb->transfer_buffer_length;
+- }
+- dbg("%s: %d", __func__, data_len);
+- return data_len;
+-}
+-
+-static int option_open(struct tty_struct *tty, struct usb_serial_port *port)
+-{
+- struct option_port_private *portdata;
+- struct option_intf_private *intfdata;
+- struct usb_serial *serial = port->serial;
+- int i, err;
+- struct urb *urb;
+-
+- portdata = usb_get_serial_port_data(port);
+- intfdata = serial->private;
+-
+- dbg("%s", __func__);
+-
+- /* Start reading from the IN endpoint */
+- for (i = 0; i < N_IN_URB; i++) {
+- urb = portdata->in_urbs[i];
+- if (!urb)
+- continue;
+- err = usb_submit_urb(urb, GFP_KERNEL);
+- if (err) {
+- dbg("%s: submit urb %d failed (%d) %d",
+- __func__, i, err,
+- urb->transfer_buffer_length);
+- }
+- }
+-
+- option_send_setup(port);
+-
+- serial->interface->needs_remote_wakeup = 1;
+- spin_lock_irq(&intfdata->susp_lock);
+- portdata->opened = 1;
+- spin_unlock_irq(&intfdata->susp_lock);
+- usb_autopm_put_interface(serial->interface);
+-
+- return 0;
+-}
+-
+-static void option_dtr_rts(struct usb_serial_port *port, int on)
+-{
+- struct usb_serial *serial = port->serial;
+- struct option_port_private *portdata;
+-
+- dbg("%s", __func__);
+- portdata = usb_get_serial_port_data(port);
+- mutex_lock(&serial->disc_mutex);
+- portdata->rts_state = on;
+- portdata->dtr_state = on;
+- if (serial->dev)
+- option_send_setup(port);
+- mutex_unlock(&serial->disc_mutex);
+-}
+-
+-
+-static void option_close(struct usb_serial_port *port)
+-{
+- int i;
+- struct usb_serial *serial = port->serial;
+- struct option_port_private *portdata;
+- struct option_intf_private *intfdata = port->serial->private;
+-
+- dbg("%s", __func__);
+- portdata = usb_get_serial_port_data(port);
+-
+- if (serial->dev) {
+- /* Stop reading/writing urbs */
+- spin_lock_irq(&intfdata->susp_lock);
+- portdata->opened = 0;
+- spin_unlock_irq(&intfdata->susp_lock);
+-
+- for (i = 0; i < N_IN_URB; i++)
+- usb_kill_urb(portdata->in_urbs[i]);
+- for (i = 0; i < N_OUT_URB; i++)
+- usb_kill_urb(portdata->out_urbs[i]);
+- usb_autopm_get_interface(serial->interface);
+- serial->interface->needs_remote_wakeup = 0;
+- }
+-}
+-
+-/* Helper functions used by option_setup_urbs */
+-static struct urb *option_setup_urb(struct usb_serial *serial, int endpoint,
+- int dir, void *ctx, char *buf, int len,
+- void (*callback)(struct urb *))
+-{
+- struct urb *urb;
+-
+- if (endpoint == -1)
+- return NULL; /* endpoint not needed */
+-
+- urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */
+- if (urb == NULL) {
+- dbg("%s: alloc for endpoint %d failed.", __func__, endpoint);
+- return NULL;
+- }
+-
+- /* Fill URB using supplied data. */
+- usb_fill_bulk_urb(urb, serial->dev,
+- usb_sndbulkpipe(serial->dev, endpoint) | dir,
+- buf, len, callback, ctx);
+-
+- return urb;
+-}
+-
+-/* Setup urbs */
+-static void option_setup_urbs(struct usb_serial *serial)
+-{
+- int i, j;
+- struct usb_serial_port *port;
+- struct option_port_private *portdata;
+-
+- dbg("%s", __func__);
+-
+- for (i = 0; i < serial->num_ports; i++) {
+- port = serial->port[i];
+- portdata = usb_get_serial_port_data(port);
+-
+- /* Do indat endpoints first */
+- for (j = 0; j < N_IN_URB; ++j) {
+- portdata->in_urbs[j] = option_setup_urb(serial,
+- port->bulk_in_endpointAddress,
+- USB_DIR_IN, port,
+- portdata->in_buffer[j],
+- IN_BUFLEN, option_indat_callback);
+- }
+-
+- /* outdat endpoints */
+- for (j = 0; j < N_OUT_URB; ++j) {
+- portdata->out_urbs[j] = option_setup_urb(serial,
+- port->bulk_out_endpointAddress,
+- USB_DIR_OUT, port,
+- portdata->out_buffer[j],
+- OUT_BUFLEN, option_outdat_callback);
+- }
+- }
+-}
+-
+-
+ /** send RTS/DTR state to the port.
+ *
+ * This is exactly the same as SET_CONTROL_LINE_STATE from the PSTN
+@@ -1228,224 +836,6 @@ static int option_send_setup(struct usb_
+ 0x22, 0x21, val, ifNum, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ }
+
+-static int option_startup(struct usb_serial *serial)
+-{
+- int i, j, err;
+- struct usb_serial_port *port;
+- struct option_port_private *portdata;
+- u8 *buffer;
+-
+- dbg("%s", __func__);
+-
+- /* Now setup per port private data */
+- for (i = 0; i < serial->num_ports; i++) {
+- port = serial->port[i];
+- portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
+- if (!portdata) {
+- dbg("%s: kmalloc for option_port_private (%d) failed!.",
+- __func__, i);
+- return 1;
+- }
+- init_usb_anchor(&portdata->delayed);
+-
+- for (j = 0; j < N_IN_URB; j++) {
+- buffer = (u8 *)__get_free_page(GFP_KERNEL);
+- if (!buffer)
+- goto bail_out_error;
+- portdata->in_buffer[j] = buffer;
+- }
+-
+- for (j = 0; j < N_OUT_URB; j++) {
+- buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL);
+- if (!buffer)
+- goto bail_out_error2;
+- portdata->out_buffer[j] = buffer;
+- }
+-
+- usb_set_serial_port_data(port, portdata);
+-
+- if (!port->interrupt_in_urb)
+- continue;
+- err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
+- if (err)
+- dbg("%s: submit irq_in urb failed %d",
+- __func__, err);
+- }
+- option_setup_urbs(serial);
+- return 0;
+-
+-bail_out_error2:
+- for (j = 0; j < N_OUT_URB; j++)
+- kfree(portdata->out_buffer[j]);
+-bail_out_error:
+- for (j = 0; j < N_IN_URB; j++)
+- if (portdata->in_buffer[j])
+- free_page((unsigned long)portdata->in_buffer[j]);
+- kfree(portdata);
+- return 1;
+-}
+-
+-static void stop_read_write_urbs(struct usb_serial *serial)
+-{
+- int i, j;
+- struct usb_serial_port *port;
+- struct option_port_private *portdata;
+-
+- /* Stop reading/writing urbs */
+- for (i = 0; i < serial->num_ports; ++i) {
+- port = serial->port[i];
+- portdata = usb_get_serial_port_data(port);
+- for (j = 0; j < N_IN_URB; j++)
+- usb_kill_urb(portdata->in_urbs[j]);
+- for (j = 0; j < N_OUT_URB; j++)
+- usb_kill_urb(portdata->out_urbs[j]);
+- }
+-}
+-
+-static void option_disconnect(struct usb_serial *serial)
+-{
+- dbg("%s", __func__);
+-
+- stop_read_write_urbs(serial);
+-}
+-
+-static void option_release(struct usb_serial *serial)
+-{
+- int i, j;
+- struct usb_serial_port *port;
+- struct option_port_private *portdata;
+-
+- dbg("%s", __func__);
+-
+- /* Now free them */
+- for (i = 0; i < serial->num_ports; ++i) {
+- port = serial->port[i];
+- portdata = usb_get_serial_port_data(port);
+-
+- for (j = 0; j < N_IN_URB; j++) {
+- if (portdata->in_urbs[j]) {
+- usb_free_urb(portdata->in_urbs[j]);
+- free_page((unsigned long)
+- portdata->in_buffer[j]);
+- portdata->in_urbs[j] = NULL;
+- }
+- }
+- for (j = 0; j < N_OUT_URB; j++) {
+- if (portdata->out_urbs[j]) {
+- usb_free_urb(portdata->out_urbs[j]);
+- kfree(portdata->out_buffer[j]);
+- portdata->out_urbs[j] = NULL;
+- }
+- }
+- }
+-
+- /* Now free per port private data */
+- for (i = 0; i < serial->num_ports; i++) {
+- port = serial->port[i];
+- kfree(usb_get_serial_port_data(port));
+- }
+-}
+-
+-#ifdef CONFIG_PM
+-static int option_suspend(struct usb_serial *serial, pm_message_t message)
+-{
+- struct option_intf_private *intfdata = serial->private;
+- int b;
+-
+- dbg("%s entered", __func__);
+-
+- if (serial->dev->auto_pm) {
+- spin_lock_irq(&intfdata->susp_lock);
+- b = intfdata->in_flight;
+- spin_unlock_irq(&intfdata->susp_lock);
+-
+- if (b)
+- return -EBUSY;
+- }
+-
+- spin_lock_irq(&intfdata->susp_lock);
+- intfdata->suspended = 1;
+- spin_unlock_irq(&intfdata->susp_lock);
+- stop_read_write_urbs(serial);
+-
+- return 0;
+-}
+-
+-static void play_delayed(struct usb_serial_port *port)
+-{
+- struct option_intf_private *data;
+- struct option_port_private *portdata;
+- struct urb *urb;
+- int err;
+-
+- portdata = usb_get_serial_port_data(port);
+- data = port->serial->private;
+- while ((urb = usb_get_from_anchor(&portdata->delayed))) {
+- err = usb_submit_urb(urb, GFP_ATOMIC);
+- if (!err)
+- data->in_flight++;
+- }
+-}
+-
+-static int option_resume(struct usb_serial *serial)
+-{
+- int i, j;
+- struct usb_serial_port *port;
+- struct option_intf_private *intfdata = serial->private;
+- struct option_port_private *portdata;
+- struct urb *urb;
+- int err = 0;
+-
+- dbg("%s entered", __func__);
+- /* get the interrupt URBs resubmitted unconditionally */
+- for (i = 0; i < serial->num_ports; i++) {
+- port = serial->port[i];
+- if (!port->interrupt_in_urb) {
+- dbg("%s: No interrupt URB for port %d\n", __func__, i);
+- continue;
+- }
+- err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
+- dbg("Submitted interrupt URB for port %d (result %d)", i, err);
+- if (err < 0) {
+- err("%s: Error %d for interrupt URB of port%d",
+- __func__, err, i);
+- goto err_out;
+- }
+- }
+-
+- for (i = 0; i < serial->num_ports; i++) {
+- /* walk all ports */
+- port = serial->port[i];
+- portdata = usb_get_serial_port_data(port);
+-
+- /* skip closed ports */
+- spin_lock_irq(&intfdata->susp_lock);
+- if (!portdata->opened) {
+- spin_unlock_irq(&intfdata->susp_lock);
+- continue;
+- }
+-
+- for (j = 0; j < N_IN_URB; j++) {
+- urb = portdata->in_urbs[j];
+- err = usb_submit_urb(urb, GFP_ATOMIC);
+- if (err < 0) {
+- err("%s: Error %d for bulk URB %d",
+- __func__, err, i);
+- spin_unlock_irq(&intfdata->susp_lock);
+- goto err_out;
+- }
+- }
+- play_delayed(port);
+- spin_unlock_irq(&intfdata->susp_lock);
+- }
+- spin_lock_irq(&intfdata->susp_lock);
+- intfdata->suspended = 0;
+- spin_unlock_irq(&intfdata->susp_lock);
+-err_out:
+- return err;
+-}
+-#endif
+-
+ MODULE_AUTHOR(DRIVER_AUTHOR);
+ MODULE_DESCRIPTION(DRIVER_DESC);
+ MODULE_VERSION(DRIVER_VERSION);
+diff -up linux-2.6.32.noarch/drivers/usb/serial/qcserial.c.orig linux-2.6.32.noarch/drivers/usb/serial/qcserial.c
+--- linux-2.6.32.noarch/drivers/usb/serial/qcserial.c.orig 2009-12-02 22:51:21.000000000 -0500
++++ linux-2.6.32.noarch/drivers/usb/serial/qcserial.c 2010-04-01 12:52:26.997995807 -0400
+@@ -15,13 +15,14 @@
+ #include <linux/tty_flip.h>
+ #include <linux/usb.h>
+ #include <linux/usb/serial.h>
++#include "usb-wwan.h"
+
+ #define DRIVER_AUTHOR "Qualcomm Inc"
+ #define DRIVER_DESC "Qualcomm USB Serial driver"
+
+ static int debug;
+
+-static struct usb_device_id id_table[] = {
++static const struct usb_device_id id_table[] = {
+ {USB_DEVICE(0x05c6, 0x9211)}, /* Acer Gobi QDL device */
+ {USB_DEVICE(0x05c6, 0x9212)}, /* Acer Gobi Modem Device */
+ {USB_DEVICE(0x03f0, 0x1f1d)}, /* HP un2400 Gobi Modem Device */
+@@ -47,6 +48,37 @@ static struct usb_device_id id_table[] =
+ {USB_DEVICE(0x05c6, 0x9221)}, /* Generic Gobi QDL device */
+ {USB_DEVICE(0x05c6, 0x9231)}, /* Generic Gobi QDL device */
+ {USB_DEVICE(0x1f45, 0x0001)}, /* Unknown Gobi QDL device */
++ {USB_DEVICE(0x413c, 0x8185)}, /* Dell Gobi 2000 QDL device (N0218, VU936) */
++ {USB_DEVICE(0x413c, 0x8186)}, /* Dell Gobi 2000 Modem device (N0218, VU936) */
++ {USB_DEVICE(0x05c6, 0x9224)}, /* Sony Gobi 2000 QDL device (N0279, VU730) */
++ {USB_DEVICE(0x05c6, 0x9225)}, /* Sony Gobi 2000 Modem device (N0279, VU730) */
++ {USB_DEVICE(0x05c6, 0x9244)}, /* Samsung Gobi 2000 QDL device (VL176) */
++ {USB_DEVICE(0x05c6, 0x9245)}, /* Samsung Gobi 2000 Modem device (VL176) */
++ {USB_DEVICE(0x03f0, 0x241d)}, /* HP Gobi 2000 QDL device (VP412) */
++ {USB_DEVICE(0x03f0, 0x251d)}, /* HP Gobi 2000 Modem device (VP412) */
++ {USB_DEVICE(0x05c6, 0x9214)}, /* Acer Gobi 2000 QDL device (VP413) */
++ {USB_DEVICE(0x05c6, 0x9215)}, /* Acer Gobi 2000 Modem device (VP413) */
++ {USB_DEVICE(0x05c6, 0x9264)}, /* Asus Gobi 2000 QDL device (VR305) */
++ {USB_DEVICE(0x05c6, 0x9265)}, /* Asus Gobi 2000 Modem device (VR305) */
++ {USB_DEVICE(0x05c6, 0x9234)}, /* Top Global Gobi 2000 QDL device (VR306) */
++ {USB_DEVICE(0x05c6, 0x9235)}, /* Top Global Gobi 2000 Modem device (VR306) */
++ {USB_DEVICE(0x05c6, 0x9274)}, /* iRex Technologies Gobi 2000 QDL device (VR307) */
++ {USB_DEVICE(0x05c6, 0x9275)}, /* iRex Technologies Gobi 2000 Modem device (VR307) */
++ {USB_DEVICE(0x1199, 0x9000)}, /* Sierra Wireless Gobi 2000 QDL device (VT773) */
++ {USB_DEVICE(0x1199, 0x9001)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x1199, 0x9002)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x1199, 0x9003)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x1199, 0x9004)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x1199, 0x9005)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x1199, 0x9006)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x1199, 0x9007)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x1199, 0x9008)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x1199, 0x9009)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x1199, 0x900a)}, /* Sierra Wireless Gobi 2000 Modem device (VT773) */
++ {USB_DEVICE(0x16d8, 0x8001)}, /* CMDTech Gobi 2000 QDL device (VU922) */
++ {USB_DEVICE(0x16d8, 0x8002)}, /* CMDTech Gobi 2000 Modem device (VU922) */
++ {USB_DEVICE(0x05c6, 0x9204)}, /* Gobi 2000 QDL device */
++ {USB_DEVICE(0x05c6, 0x9205)}, /* Gobi 2000 Modem device */
+ { } /* Terminating entry */
+ };
+ MODULE_DEVICE_TABLE(usb, id_table);
+@@ -63,6 +95,8 @@ static struct usb_driver qcdriver = {
+
+ static int qcprobe(struct usb_serial *serial, const struct usb_device_id *id)
+ {
++ struct usb_wwan_intf_private *data;
++ struct usb_host_interface *intf = serial->interface->cur_altsetting;
+ int retval = -ENODEV;
+ __u8 nintf;
+ __u8 ifnum;
+@@ -71,33 +105,45 @@ static int qcprobe(struct usb_serial *se
+
+ nintf = serial->dev->actconfig->desc.bNumInterfaces;
+ dbg("Num Interfaces = %d", nintf);
+- ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber;
++ ifnum = intf->desc.bInterfaceNumber;
+ dbg("This Interface = %d", ifnum);
+
++ data = serial->private = kzalloc(sizeof(struct usb_wwan_intf_private),
++ GFP_KERNEL);
++ if (!data)
++ return -ENOMEM;
++
++ spin_lock_init(&data->susp_lock);
++
+ switch (nintf) {
+ case 1:
+ /* QDL mode */
+- if (serial->interface->num_altsetting == 2) {
+- struct usb_host_interface *intf;
+-
++ /* Gobi 2000 has a single altsetting, older ones have two */
++ if (serial->interface->num_altsetting == 2)
+ intf = &serial->interface->altsetting[1];
+- if (intf->desc.bNumEndpoints == 2) {
+- if (usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) &&
+- usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) {
+- dbg("QDL port found");
+- retval = usb_set_interface(serial->dev, ifnum, 1);
+- if (retval < 0) {
+- dev_err(&serial->dev->dev,
+- "Could not set interface, error %d\n",
+- retval);
+- retval = -ENODEV;
+- }
+- return retval;
+- }
++ else if (serial->interface->num_altsetting > 2)
++ break;
++
++ if (intf->desc.bNumEndpoints == 2 &&
++ usb_endpoint_is_bulk_in(&intf->endpoint[0].desc) &&
++ usb_endpoint_is_bulk_out(&intf->endpoint[1].desc)) {
++ dbg("QDL port found");
++
++ if (serial->interface->num_altsetting == 1)
++ return 0;
++
++ retval = usb_set_interface(serial->dev, ifnum, 1);
++ if (retval < 0) {
++ dev_err(&serial->dev->dev,
++ "Could not set interface, error %d\n",
++ retval);
++ retval = -ENODEV;
+ }
++ return retval;
+ }
+ break;
+
++ case 3:
+ case 4:
+ /* Composite mode */
+ if (ifnum == 2) {
+@@ -132,6 +178,18 @@ static struct usb_serial_driver qcdevice
+ .usb_driver = &qcdriver,
+ .num_ports = 1,
+ .probe = qcprobe,
++ .open = usb_wwan_open,
++ .close = usb_wwan_close,
++ .write = usb_wwan_write,
++ .write_room = usb_wwan_write_room,
++ .chars_in_buffer = usb_wwan_chars_in_buffer,
++ .attach = usb_wwan_startup,
++ .disconnect = usb_wwan_disconnect,
++ .release = usb_wwan_release,
++#ifdef CONFIG_PM
++ .suspend = usb_wwan_suspend,
++ .resume = usb_wwan_resume,
++#endif
+ };
+
+ static int __init qcinit(void)
+diff -up linux-2.6.32.noarch/drivers/usb/serial/usb_wwan.c.orig linux-2.6.32.noarch/drivers/usb/serial/usb_wwan.c
+--- linux-2.6.32.noarch/drivers/usb/serial/usb_wwan.c.orig 2010-04-01 12:52:26.999995712 -0400
++++ linux-2.6.32.noarch/drivers/usb/serial/usb_wwan.c 2010-04-01 12:52:26.998995550 -0400
+@@ -0,0 +1,665 @@
++/*
++ USB Driver layer for GSM modems
++
++ Copyright (C) 2005 Matthias Urlichs <smurf@smurf.noris.de>
++
++ This driver is free software; you can redistribute it and/or modify
++ it under the terms of Version 2 of the GNU General Public License as
++ published by the Free Software Foundation.
++
++ Portions copied from the Keyspan driver by Hugh Blemings <hugh@blemings.org>
++
++ History: see the git log.
++
++ Work sponsored by: Sigos GmbH, Germany <info@sigos.de>
++
++ This driver exists because the "normal" serial driver doesn't work too well
++ with GSM modems. Issues:
++ - data loss -- one single Receive URB is not nearly enough
++ - controlling the baud rate doesn't make sense
++*/
++
++#define DRIVER_VERSION "v0.7.2"
++#define DRIVER_AUTHOR "Matthias Urlichs <smurf@smurf.noris.de>"
++#define DRIVER_DESC "USB Driver for GSM modems"
++
++#include <linux/kernel.h>
++#include <linux/jiffies.h>
++#include <linux/errno.h>
++#include <linux/tty.h>
++#include <linux/tty_flip.h>
++#include <linux/module.h>
++#include <linux/bitops.h>
++#include <linux/usb.h>
++#include <linux/usb/serial.h>
++#include "usb-wwan.h"
++
++static int debug;
++
++void usb_wwan_dtr_rts(struct usb_serial_port *port, int on)
++{
++ struct usb_serial *serial = port->serial;
++ struct usb_wwan_port_private *portdata;
++
++ struct usb_wwan_intf_private *intfdata;
++
++ dbg("%s", __func__);
++
++ intfdata = port->serial->private;
++
++ if (!intfdata->send_setup)
++ return;
++
++ portdata = usb_get_serial_port_data(port);
++ mutex_lock(&serial->disc_mutex);
++ portdata->rts_state = on;
++ portdata->dtr_state = on;
++ if (serial->dev)
++ intfdata->send_setup(port);
++ mutex_unlock(&serial->disc_mutex);
++}
++EXPORT_SYMBOL(usb_wwan_dtr_rts);
++
++void usb_wwan_set_termios(struct tty_struct *tty,
++ struct usb_serial_port *port,
++ struct ktermios *old_termios)
++{
++ struct usb_wwan_intf_private *intfdata = port->serial->private;
++
++ dbg("%s", __func__);
++
++ /* Doesn't support option setting */
++ tty_termios_copy_hw(tty->termios, old_termios);
++
++ if (intfdata->send_setup)
++ intfdata->send_setup(port);
++}
++EXPORT_SYMBOL(usb_wwan_set_termios);
++
++int usb_wwan_tiocmget(struct tty_struct *tty, struct file *file)
++{
++ struct usb_serial_port *port = tty->driver_data;
++ unsigned int value;
++ struct usb_wwan_port_private *portdata;
++
++ portdata = usb_get_serial_port_data(port);
++
++ value = ((portdata->rts_state) ? TIOCM_RTS : 0) |
++ ((portdata->dtr_state) ? TIOCM_DTR : 0) |
++ ((portdata->cts_state) ? TIOCM_CTS : 0) |
++ ((portdata->dsr_state) ? TIOCM_DSR : 0) |
++ ((portdata->dcd_state) ? TIOCM_CAR : 0) |
++ ((portdata->ri_state) ? TIOCM_RNG : 0);
++
++ return value;
++}
++EXPORT_SYMBOL(usb_wwan_tiocmget);
++
++int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file,
++ unsigned int set, unsigned int clear)
++{
++ struct usb_serial_port *port = tty->driver_data;
++ struct usb_wwan_port_private *portdata;
++ struct usb_wwan_intf_private *intfdata;
++
++ portdata = usb_get_serial_port_data(port);
++ intfdata = port->serial->private;
++
++ if (!intfdata->send_setup)
++ return -EINVAL;
++
++ /* FIXME: what locks portdata fields ? */
++ if (set & TIOCM_RTS)
++ portdata->rts_state = 1;
++ if (set & TIOCM_DTR)
++ portdata->dtr_state = 1;
++
++ if (clear & TIOCM_RTS)
++ portdata->rts_state = 0;
++ if (clear & TIOCM_DTR)
++ portdata->dtr_state = 0;
++ return intfdata->send_setup(port);
++}
++EXPORT_SYMBOL(usb_wwan_tiocmset);
++
++/* Write */
++int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
++ const unsigned char *buf, int count)
++{
++ struct usb_wwan_port_private *portdata;
++ struct usb_wwan_intf_private *intfdata;
++ int i;
++ int left, todo;
++ struct urb *this_urb = NULL; /* spurious */
++ int err;
++ unsigned long flags;
++
++ portdata = usb_get_serial_port_data(port);
++ intfdata = port->serial->private;
++
++ dbg("%s: write (%d chars)", __func__, count);
++
++ i = 0;
++ left = count;
++ for (i = 0; left > 0 && i < N_OUT_URB; i++) {
++ todo = left;
++ if (todo > OUT_BUFLEN)
++ todo = OUT_BUFLEN;
++
++ this_urb = portdata->out_urbs[i];
++ if (test_and_set_bit(i, &portdata->out_busy)) {
++ if (time_before(jiffies,
++ portdata->tx_start_time[i] + 10 * HZ))
++ continue;
++ usb_unlink_urb(this_urb);
++ continue;
++ }
++ dbg("%s: endpoint %d buf %d", __func__,
++ usb_pipeendpoint(this_urb->pipe), i);
++
++ err = usb_autopm_get_interface_async(port->serial->interface);
++ if (err < 0)
++ break;
++
++ /* send the data */
++ memcpy(this_urb->transfer_buffer, buf, todo);
++ this_urb->transfer_buffer_length = todo;
++
++ spin_lock_irqsave(&intfdata->susp_lock, flags);
++ if (intfdata->suspended) {
++ usb_anchor_urb(this_urb, &portdata->delayed);
++ spin_unlock_irqrestore(&intfdata->susp_lock, flags);
++ } else {
++ intfdata->in_flight++;
++ spin_unlock_irqrestore(&intfdata->susp_lock, flags);
++ err = usb_submit_urb(this_urb, GFP_ATOMIC);
++ if (err) {
++ dbg("usb_submit_urb %p (write bulk) failed "
++ "(%d)", this_urb, err);
++ clear_bit(i, &portdata->out_busy);
++ spin_lock_irqsave(&intfdata->susp_lock, flags);
++ intfdata->in_flight--;
++ spin_unlock_irqrestore(&intfdata->susp_lock,
++ flags);
++ continue;
++ }
++ }
++
++ portdata->tx_start_time[i] = jiffies;
++ buf += todo;
++ left -= todo;
++ }
++
++ count -= left;
++ dbg("%s: wrote (did %d)", __func__, count);
++ return count;
++}
++EXPORT_SYMBOL(usb_wwan_write);
++
++static void usb_wwan_indat_callback(struct urb *urb)
++{
++ int err;
++ int endpoint;
++ struct usb_serial_port *port;
++ struct tty_struct *tty;
++ unsigned char *data = urb->transfer_buffer;
++ int status = urb->status;
++
++ dbg("%s: %p", __func__, urb);
++
++ endpoint = usb_pipeendpoint(urb->pipe);
++ port = urb->context;
++
++ if (status) {
++ dbg("%s: nonzero status: %d on endpoint %02x.",
++ __func__, status, endpoint);
++ } else {
++ tty = tty_port_tty_get(&port->port);
++ if (urb->actual_length) {
++ tty_buffer_request_room(tty, urb->actual_length);
++ tty_insert_flip_string(tty, data, urb->actual_length);
++ tty_flip_buffer_push(tty);
++ } else
++ dbg("%s: empty read urb received", __func__);
++ tty_kref_put(tty);
++
++ /* Resubmit urb so we continue receiving */
++ if (port->port.count && status != -ESHUTDOWN) {
++ err = usb_submit_urb(urb, GFP_ATOMIC);
++ if (err)
++ printk(KERN_ERR "%s: resubmit read urb failed. "
++ "(%d)", __func__, err);
++ else
++ usb_mark_last_busy(port->serial->dev);
++ }
++
++ }
++ return;
++}
++
++static void usb_wwan_outdat_callback(struct urb *urb)
++{
++ struct usb_serial_port *port;
++ struct usb_wwan_port_private *portdata;
++ struct usb_wwan_intf_private *intfdata;
++ int i;
++
++ dbg("%s", __func__);
++
++ port = urb->context;
++ intfdata = port->serial->private;
++
++ usb_serial_port_softint(port);
++ usb_autopm_put_interface_async(port->serial->interface);
++ portdata = usb_get_serial_port_data(port);
++ spin_lock(&intfdata->susp_lock);
++ intfdata->in_flight--;
++ spin_unlock(&intfdata->susp_lock);
++
++ for (i = 0; i < N_OUT_URB; ++i) {
++ if (portdata->out_urbs[i] == urb) {
++ smp_mb__before_clear_bit();
++ clear_bit(i, &portdata->out_busy);
++ break;
++ }
++ }
++}
++
++int usb_wwan_write_room(struct tty_struct *tty)
++{
++ struct usb_serial_port *port = tty->driver_data;
++ struct usb_wwan_port_private *portdata;
++ int i;
++ int data_len = 0;
++ struct urb *this_urb;
++
++ portdata = usb_get_serial_port_data(port);
++
++ for (i = 0; i < N_OUT_URB; i++) {
++ this_urb = portdata->out_urbs[i];
++ if (this_urb && !test_bit(i, &portdata->out_busy))
++ data_len += OUT_BUFLEN;
++ }
++
++ dbg("%s: %d", __func__, data_len);
++ return data_len;
++}
++EXPORT_SYMBOL(usb_wwan_write_room);
++
++int usb_wwan_chars_in_buffer(struct tty_struct *tty)
++{
++ struct usb_serial_port *port = tty->driver_data;
++ struct usb_wwan_port_private *portdata;
++ int i;
++ int data_len = 0;
++ struct urb *this_urb;
++
++ portdata = usb_get_serial_port_data(port);
++
++ for (i = 0; i < N_OUT_URB; i++) {
++ this_urb = portdata->out_urbs[i];
++ /* FIXME: This locking is insufficient as this_urb may
++ go unused during the test */
++ if (this_urb && test_bit(i, &portdata->out_busy))
++ data_len += this_urb->transfer_buffer_length;
++ }
++ dbg("%s: %d", __func__, data_len);
++ return data_len;
++}
++EXPORT_SYMBOL(usb_wwan_chars_in_buffer);
++
++int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port)
++{
++ struct usb_wwan_port_private *portdata;
++ struct usb_wwan_intf_private *intfdata;
++ struct usb_serial *serial = port->serial;
++ int i, err;
++ struct urb *urb;
++
++ portdata = usb_get_serial_port_data(port);
++ intfdata = serial->private;
++
++ dbg("%s", __func__);
++
++ /* Start reading from the IN endpoint */
++ for (i = 0; i < N_IN_URB; i++) {
++ urb = portdata->in_urbs[i];
++ if (!urb)
++ continue;
++ err = usb_submit_urb(urb, GFP_KERNEL);
++ if (err) {
++ dbg("%s: submit urb %d failed (%d) %d",
++ __func__, i, err, urb->transfer_buffer_length);
++ }
++ }
++
++ if (intfdata->send_setup)
++ intfdata->send_setup(port);
++
++ serial->interface->needs_remote_wakeup = 1;
++ spin_lock_irq(&intfdata->susp_lock);
++ portdata->opened = 1;
++ spin_unlock_irq(&intfdata->susp_lock);
++ usb_autopm_put_interface(serial->interface);
++
++ return 0;
++}
++EXPORT_SYMBOL(usb_wwan_open);
++
++void usb_wwan_close(struct usb_serial_port *port)
++{
++ int i;
++ struct usb_serial *serial = port->serial;
++ struct usb_wwan_port_private *portdata;
++ struct usb_wwan_intf_private *intfdata = port->serial->private;
++
++ dbg("%s", __func__);
++ portdata = usb_get_serial_port_data(port);
++
++ if (serial->dev) {
++ /* Stop reading/writing urbs */
++ spin_lock_irq(&intfdata->susp_lock);
++ portdata->opened = 0;
++ spin_unlock_irq(&intfdata->susp_lock);
++
++ for (i = 0; i < N_IN_URB; i++)
++ usb_kill_urb(portdata->in_urbs[i]);
++ for (i = 0; i < N_OUT_URB; i++)
++ usb_kill_urb(portdata->out_urbs[i]);
++ usb_autopm_get_interface(serial->interface);
++ serial->interface->needs_remote_wakeup = 0;
++ }
++}
++EXPORT_SYMBOL(usb_wwan_close);
++
++/* Helper functions used by usb_wwan_setup_urbs */
++static struct urb *usb_wwan_setup_urb(struct usb_serial *serial, int endpoint,
++ int dir, void *ctx, char *buf, int len,
++ void (*callback) (struct urb *))
++{
++ struct urb *urb;
++
++ if (endpoint == -1)
++ return NULL; /* endpoint not needed */
++
++ urb = usb_alloc_urb(0, GFP_KERNEL); /* No ISO */
++ if (urb == NULL) {
++ dbg("%s: alloc for endpoint %d failed.", __func__, endpoint);
++ return NULL;
++ }
++
++ /* Fill URB using supplied data. */
++ usb_fill_bulk_urb(urb, serial->dev,
++ usb_sndbulkpipe(serial->dev, endpoint) | dir,
++ buf, len, callback, ctx);
++
++ return urb;
++}
++
++/* Setup urbs */
++static void usb_wwan_setup_urbs(struct usb_serial *serial)
++{
++ int i, j;
++ struct usb_serial_port *port;
++ struct usb_wwan_port_private *portdata;
++
++ dbg("%s", __func__);
++
++ for (i = 0; i < serial->num_ports; i++) {
++ port = serial->port[i];
++ portdata = usb_get_serial_port_data(port);
++
++ /* Do indat endpoints first */
++ for (j = 0; j < N_IN_URB; ++j) {
++ portdata->in_urbs[j] = usb_wwan_setup_urb(serial,
++ port->
++ bulk_in_endpointAddress,
++ USB_DIR_IN,
++ port,
++ portdata->
++ in_buffer[j],
++ IN_BUFLEN,
++ usb_wwan_indat_callback);
++ }
++
++ /* outdat endpoints */
++ for (j = 0; j < N_OUT_URB; ++j) {
++ portdata->out_urbs[j] = usb_wwan_setup_urb(serial,
++ port->
++ bulk_out_endpointAddress,
++ USB_DIR_OUT,
++ port,
++ portdata->
++ out_buffer
++ [j],
++ OUT_BUFLEN,
++ usb_wwan_outdat_callback);
++ }
++ }
++}
++
++int usb_wwan_startup(struct usb_serial *serial)
++{
++ int i, j, err;
++ struct usb_serial_port *port;
++ struct usb_wwan_port_private *portdata;
++ u8 *buffer;
++
++ dbg("%s", __func__);
++
++ /* Now setup per port private data */
++ for (i = 0; i < serial->num_ports; i++) {
++ port = serial->port[i];
++ portdata = kzalloc(sizeof(*portdata), GFP_KERNEL);
++ if (!portdata) {
++ dbg("%s: kmalloc for usb_wwan_port_private (%d) failed!.",
++ __func__, i);
++ return 1;
++ }
++ init_usb_anchor(&portdata->delayed);
++
++ for (j = 0; j < N_IN_URB; j++) {
++ buffer = (u8 *) __get_free_page(GFP_KERNEL);
++ if (!buffer)
++ goto bail_out_error;
++ portdata->in_buffer[j] = buffer;
++ }
++
++ for (j = 0; j < N_OUT_URB; j++) {
++ buffer = kmalloc(OUT_BUFLEN, GFP_KERNEL);
++ if (!buffer)
++ goto bail_out_error2;
++ portdata->out_buffer[j] = buffer;
++ }
++
++ usb_set_serial_port_data(port, portdata);
++
++ if (!port->interrupt_in_urb)
++ continue;
++ err = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
++ if (err)
++ dbg("%s: submit irq_in urb failed %d", __func__, err);
++ }
++ usb_wwan_setup_urbs(serial);
++ return 0;
++
++bail_out_error2:
++ for (j = 0; j < N_OUT_URB; j++)
++ kfree(portdata->out_buffer[j]);
++bail_out_error:
++ for (j = 0; j < N_IN_URB; j++)
++ if (portdata->in_buffer[j])
++ free_page((unsigned long)portdata->in_buffer[j]);
++ kfree(portdata);
++ return 1;
++}
++EXPORT_SYMBOL(usb_wwan_startup);
++
++static void stop_read_write_urbs(struct usb_serial *serial)
++{
++ int i, j;
++ struct usb_serial_port *port;
++ struct usb_wwan_port_private *portdata;
++
++ /* Stop reading/writing urbs */
++ for (i = 0; i < serial->num_ports; ++i) {
++ port = serial->port[i];
++ portdata = usb_get_serial_port_data(port);
++ for (j = 0; j < N_IN_URB; j++)
++ usb_kill_urb(portdata->in_urbs[j]);
++ for (j = 0; j < N_OUT_URB; j++)
++ usb_kill_urb(portdata->out_urbs[j]);
++ }
++}
++
++void usb_wwan_disconnect(struct usb_serial *serial)
++{
++ dbg("%s", __func__);
++
++ stop_read_write_urbs(serial);
++}
++EXPORT_SYMBOL(usb_wwan_disconnect);
++
++void usb_wwan_release(struct usb_serial *serial)
++{
++ int i, j;
++ struct usb_serial_port *port;
++ struct usb_wwan_port_private *portdata;
++
++ dbg("%s", __func__);
++
++ /* Now free them */
++ for (i = 0; i < serial->num_ports; ++i) {
++ port = serial->port[i];
++ portdata = usb_get_serial_port_data(port);
++
++ for (j = 0; j < N_IN_URB; j++) {
++ usb_free_urb(portdata->in_urbs[j]);
++ free_page((unsigned long)
++ portdata->in_buffer[j]);
++ portdata->in_urbs[j] = NULL;
++ }
++ for (j = 0; j < N_OUT_URB; j++) {
++ usb_free_urb(portdata->out_urbs[j]);
++ kfree(portdata->out_buffer[j]);
++ portdata->out_urbs[j] = NULL;
++ }
++ }
++
++ /* Now free per port private data */
++ for (i = 0; i < serial->num_ports; i++) {
++ port = serial->port[i];
++ kfree(usb_get_serial_port_data(port));
++ }
++}
++EXPORT_SYMBOL(usb_wwan_release);
++
++#ifdef CONFIG_PM
++int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message)
++{
++ struct usb_wwan_intf_private *intfdata = serial->private;
++ int b;
++
++ dbg("%s entered", __func__);
++
++ if (message.event & PM_EVENT_AUTO) {
++ spin_lock_irq(&intfdata->susp_lock);
++ b = intfdata->in_flight;
++ spin_unlock_irq(&intfdata->susp_lock);
++
++ if (b)
++ return -EBUSY;
++ }
++
++ spin_lock_irq(&intfdata->susp_lock);
++ intfdata->suspended = 1;
++ spin_unlock_irq(&intfdata->susp_lock);
++ stop_read_write_urbs(serial);
++
++ return 0;
++}
++EXPORT_SYMBOL(usb_wwan_suspend);
++
++static void play_delayed(struct usb_serial_port *port)
++{
++ struct usb_wwan_intf_private *data;
++ struct usb_wwan_port_private *portdata;
++ struct urb *urb;
++ int err;
++
++ portdata = usb_get_serial_port_data(port);
++ data = port->serial->private;
++ while ((urb = usb_get_from_anchor(&portdata->delayed))) {
++ err = usb_submit_urb(urb, GFP_ATOMIC);
++ if (!err)
++ data->in_flight++;
++ }
++}
++
++int usb_wwan_resume(struct usb_serial *serial)
++{
++ int i, j;
++ struct usb_serial_port *port;
++ struct usb_wwan_intf_private *intfdata = serial->private;
++ struct usb_wwan_port_private *portdata;
++ struct urb *urb;
++ int err = 0;
++
++ dbg("%s entered", __func__);
++ /* get the interrupt URBs resubmitted unconditionally */
++ for (i = 0; i < serial->num_ports; i++) {
++ port = serial->port[i];
++ if (!port->interrupt_in_urb) {
++ dbg("%s: No interrupt URB for port %d\n", __func__, i);
++ continue;
++ }
++ err = usb_submit_urb(port->interrupt_in_urb, GFP_NOIO);
++ dbg("Submitted interrupt URB for port %d (result %d)", i, err);
++ if (err < 0) {
++ err("%s: Error %d for interrupt URB of port%d",
++ __func__, err, i);
++ goto err_out;
++ }
++ }
++
++ for (i = 0; i < serial->num_ports; i++) {
++ /* walk all ports */
++ port = serial->port[i];
++ portdata = usb_get_serial_port_data(port);
++
++ /* skip closed ports */
++ spin_lock_irq(&intfdata->susp_lock);
++ if (!portdata->opened) {
++ spin_unlock_irq(&intfdata->susp_lock);
++ continue;
++ }
++
++ for (j = 0; j < N_IN_URB; j++) {
++ urb = portdata->in_urbs[j];
++ err = usb_submit_urb(urb, GFP_ATOMIC);
++ if (err < 0) {
++ err("%s: Error %d for bulk URB %d",
++ __func__, err, i);
++ spin_unlock_irq(&intfdata->susp_lock);
++ goto err_out;
++ }
++ }
++ play_delayed(port);
++ spin_unlock_irq(&intfdata->susp_lock);
++ }
++ spin_lock_irq(&intfdata->susp_lock);
++ intfdata->suspended = 0;
++ spin_unlock_irq(&intfdata->susp_lock);
++err_out:
++ return err;
++}
++EXPORT_SYMBOL(usb_wwan_resume);
++#endif
++
++MODULE_AUTHOR(DRIVER_AUTHOR);
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_VERSION(DRIVER_VERSION);
++MODULE_LICENSE("GPL");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Debug messages");
+diff -up linux-2.6.32.noarch/drivers/usb/serial/usb-wwan.h.orig linux-2.6.32.noarch/drivers/usb/serial/usb-wwan.h
+--- linux-2.6.32.noarch/drivers/usb/serial/usb-wwan.h.orig 2010-04-01 12:52:26.999995712 -0400
++++ linux-2.6.32.noarch/drivers/usb/serial/usb-wwan.h 2010-04-01 12:52:26.999995712 -0400
+@@ -0,0 +1,66 @@
++/*
++ * Definitions for USB serial mobile broadband cards
++ */
++
++#ifndef __LINUX_USB_USB_WWAN
++#define __LINUX_USB_USB_WWAN
++
++extern void usb_wwan_dtr_rts(struct usb_serial_port *port, int on);
++extern int usb_wwan_open(struct tty_struct *tty, struct usb_serial_port *port);
++extern void usb_wwan_close(struct usb_serial_port *port);
++extern int usb_wwan_startup(struct usb_serial *serial);
++extern void usb_wwan_disconnect(struct usb_serial *serial);
++extern void usb_wwan_release(struct usb_serial *serial);
++extern int usb_wwan_write_room(struct tty_struct *tty);
++extern void usb_wwan_set_termios(struct tty_struct *tty,
++ struct usb_serial_port *port,
++ struct ktermios *old);
++extern int usb_wwan_tiocmget(struct tty_struct *tty, struct file *file);
++extern int usb_wwan_tiocmset(struct tty_struct *tty, struct file *file,
++ unsigned int set, unsigned int clear);
++extern int usb_wwan_send_setup(struct usb_serial_port *port);
++extern int usb_wwan_write(struct tty_struct *tty, struct usb_serial_port *port,
++ const unsigned char *buf, int count);
++extern int usb_wwan_chars_in_buffer(struct tty_struct *tty);
++#ifdef CONFIG_PM
++extern int usb_wwan_suspend(struct usb_serial *serial, pm_message_t message);
++extern int usb_wwan_resume(struct usb_serial *serial);
++#endif
++
++/* per port private data */
++
++#define N_IN_URB 4
++#define N_OUT_URB 4
++#define IN_BUFLEN 4096
++#define OUT_BUFLEN 4096
++
++struct usb_wwan_intf_private {
++ spinlock_t susp_lock;
++ unsigned int suspended:1;
++ int in_flight;
++ int (*send_setup) (struct usb_serial_port *port);
++};
++
++struct usb_wwan_port_private {
++ /* Input endpoints and buffer for this port */
++ struct urb *in_urbs[N_IN_URB];
++ u8 *in_buffer[N_IN_URB];
++ /* Output endpoints and buffer for this port */
++ struct urb *out_urbs[N_OUT_URB];
++ u8 *out_buffer[N_OUT_URB];
++ unsigned long out_busy; /* Bit vector of URBs in use */
++ int opened;
++ struct usb_anchor delayed;
++
++ /* Settings for the port */
++ int rts_state; /* Handshaking pins (outputs) */
++ int dtr_state;
++ int cts_state; /* Handshaking pins (inputs) */
++ int dsr_state;
++ int dcd_state;
++ int ri_state;
++
++ unsigned long tx_start_time[N_OUT_URB];
++};
++
++#endif /* __LINUX_USB_USB_WWAN */
diff --git a/linux-2.6-utrace-ptrace.patch b/linux-2.6-utrace-ptrace.patch
new file mode 100644
index 0000000..e95ca6a
--- /dev/null
+++ b/linux-2.6-utrace-ptrace.patch
@@ -0,0 +1,1825 @@
+implement utrace-ptrace
+
+The patch adds the new file, kernel/ptrace-utrace.c, which contains
+the new implementation of ptrace over utrace.
+
+This file is not compiled until we have CONFIG_UTRACE option, will be
+added by the next "utrace core" patch.
+
+It's supposed to be an invisible implementation change, nothing should
+change to userland when CONFIG_UTRACE is enabled.
+
+Signed-off-by: Roland McGrath <roland@redhat.com>
+Signed-off-by: Oleg Nesterov <oleg@redhat.com>
+---
+ include/linux/ptrace.h | 2 +-
+ kernel/Makefile | 1 +
+ kernel/ptrace-utrace.c | 1080 ++++++++++++++++++++++++++++++++++++++++++++++++
+ kernel/ptrace.c | 572 +++++++++++++-------------
+ kernel/utrace.c | 16 +
+ 5 files changed, 1378 insertions(+), 293 deletions(-)
+
+diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
+index 4802e2a..03f8fc7 100644
+--- a/include/linux/ptrace.h
++++ b/include/linux/ptrace.h
+@@ -79,7 +79,7 @@
+ #include <linux/compiler.h> /* For unlikely. */
+ #include <linux/sched.h> /* For struct task_struct. */
+
+-
++extern void ptrace_notify_stop(struct task_struct *tracee);
+ extern long arch_ptrace(struct task_struct *child, long request, long addr, long data);
+ extern int ptrace_traceme(void);
+ extern int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len);
+diff --git a/kernel/Makefile b/kernel/Makefile
+index 263bb19..42cb1ec 100644
+--- a/kernel/Makefile
++++ b/kernel/Makefile
+@@ -69,6 +69,7 @@ obj-$(CONFIG_RESOURCE_COUNTERS) += res_c
+ obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
+ obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
+ obj-$(CONFIG_UTRACE) += utrace.o
++obj-$(CONFIG_UTRACE) += ptrace-utrace.o
+ obj-$(CONFIG_AUDIT) += audit.o auditfilter.o audit_watch.o
+ obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
+ obj-$(CONFIG_GCOV_KERNEL) += gcov/
+diff --git a/kernel/ptrace-utrace.c b/kernel/ptrace-utrace.c
+new file mode 100644
+index ...ea419ee 100644
+--- /dev/null
++++ b/kernel/ptrace-utrace.c
+@@ -0,0 +1,1080 @@
++/*
++ * linux/kernel/ptrace.c
++ *
++ * (C) Copyright 1999 Linus Torvalds
++ *
++ * Common interfaces for "ptrace()" which we do not want
++ * to continually duplicate across every architecture.
++ */
++
++#include <linux/capability.h>
++#include <linux/module.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/mm.h>
++#include <linux/highmem.h>
++#include <linux/pagemap.h>
++#include <linux/smp_lock.h>
++#include <linux/ptrace.h>
++#include <linux/utrace.h>
++#include <linux/security.h>
++#include <linux/signal.h>
++#include <linux/audit.h>
++#include <linux/pid_namespace.h>
++#include <linux/syscalls.h>
++#include <linux/uaccess.h>
++
++/*
++ * ptrace a task: make the debugger its new parent and
++ * move it to the ptrace list.
++ *
++ * Must be called with the tasklist lock write-held.
++ */
++void __ptrace_link(struct task_struct *child, struct task_struct *new_parent)
++{
++ BUG_ON(!list_empty(&child->ptrace_entry));
++ list_add(&child->ptrace_entry, &new_parent->ptraced);
++ child->parent = new_parent;
++}
++
++/*
++ * unptrace a task: move it back to its original parent and
++ * remove it from the ptrace list.
++ *
++ * Must be called with the tasklist lock write-held.
++ */
++void __ptrace_unlink(struct task_struct *child)
++{
++ BUG_ON(!child->ptrace);
++
++ child->ptrace = 0;
++ child->parent = child->real_parent;
++ list_del_init(&child->ptrace_entry);
++
++ arch_ptrace_untrace(child);
++}
++
++struct ptrace_context {
++ int options;
++
++ int signr;
++ siginfo_t *siginfo;
++
++ int stop_code;
++ unsigned long eventmsg;
++
++ enum utrace_resume_action resume;
++};
++
++#define PT_UTRACED 0x00001000
++
++#define PTRACE_O_SYSEMU 0x100
++
++#define PTRACE_EVENT_SYSCALL (1 << 16)
++#define PTRACE_EVENT_SIGTRAP (2 << 16)
++#define PTRACE_EVENT_SIGNAL (3 << 16)
++/* events visible to user-space */
++#define PTRACE_EVENT_MASK 0xFFFF
++
++static inline bool ptrace_event_pending(struct ptrace_context *ctx)
++{
++ return ctx->stop_code != 0;
++}
++
++static inline int get_stop_event(struct ptrace_context *ctx)
++{
++ return ctx->stop_code >> 8;
++}
++
++static inline void set_stop_code(struct ptrace_context *ctx, int event)
++{
++ ctx->stop_code = (event << 8) | SIGTRAP;
++}
++
++static inline struct ptrace_context *
++ptrace_context(struct utrace_engine *engine)
++{
++ return engine->data;
++}
++
++static const struct utrace_engine_ops ptrace_utrace_ops; /* forward decl */
++
++static struct utrace_engine *ptrace_lookup_engine(struct task_struct *tracee)
++{
++ return utrace_attach_task(tracee, UTRACE_ATTACH_MATCH_OPS,
++ &ptrace_utrace_ops, NULL);
++}
++
++static struct utrace_engine *
++ptrace_reuse_engine(struct task_struct *tracee)
++{
++ struct utrace_engine *engine;
++ struct ptrace_context *ctx;
++ int err = -EPERM;
++
++ engine = ptrace_lookup_engine(tracee);
++ if (IS_ERR(engine))
++ return engine;
++
++ ctx = ptrace_context(engine);
++ if (unlikely(ctx->resume == UTRACE_DETACH)) {
++ /*
++ * Try to reuse this self-detaching engine.
++ * The only caller which can hit this case is ptrace_attach(),
++ * it holds ->cred_guard_mutex.
++ */
++ ctx->options = 0;
++ ctx->eventmsg = 0;
++
++ /* make sure we don't get unwanted reports */
++ err = utrace_set_events(tracee, engine, UTRACE_EVENT(QUIESCE));
++ if (!err || err == -EINPROGRESS) {
++ ctx->resume = UTRACE_RESUME;
++ /* synchronize with ptrace_report_signal() */
++ err = utrace_barrier(tracee, engine);
++ }
++ WARN_ON(!err != (engine->ops == &ptrace_utrace_ops));
++
++ if (!err)
++ return engine;
++ }
++
++ utrace_engine_put(engine);
++ return ERR_PTR(err);
++}
++
++static struct utrace_engine *
++ptrace_attach_engine(struct task_struct *tracee)
++{
++ struct utrace_engine *engine;
++ struct ptrace_context *ctx;
++
++ if (unlikely(task_utrace_flags(tracee))) {
++ engine = ptrace_reuse_engine(tracee);
++ if (!IS_ERR(engine) || IS_ERR(engine) == -EPERM)
++ return engine;
++ }
++
++ ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
++ if (unlikely(!ctx))
++ return ERR_PTR(-ENOMEM);
++
++ ctx->resume = UTRACE_RESUME;
++
++ engine = utrace_attach_task(tracee, UTRACE_ATTACH_CREATE |
++ UTRACE_ATTACH_EXCLUSIVE |
++ UTRACE_ATTACH_MATCH_OPS,
++ &ptrace_utrace_ops, ctx);
++ if (unlikely(IS_ERR(engine))) {
++ if (engine != ERR_PTR(-ESRCH) &&
++ engine != ERR_PTR(-ERESTARTNOINTR))
++ engine = ERR_PTR(-EPERM);
++ kfree(ctx);
++ }
++
++ return engine;
++}
++
++static inline int ptrace_set_events(struct task_struct *target,
++ struct utrace_engine *engine,
++ unsigned long options)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++ /*
++ * We need QUIESCE for resume handling, CLONE to check
++ * for CLONE_PTRACE, other events are always reported.
++ */
++ unsigned long events = UTRACE_EVENT(QUIESCE) | UTRACE_EVENT(CLONE) |
++ UTRACE_EVENT(EXEC) | UTRACE_EVENT_SIGNAL_ALL;
++
++ ctx->options = options;
++ if (options & PTRACE_O_TRACEEXIT)
++ events |= UTRACE_EVENT(EXIT);
++
++ return utrace_set_events(target, engine, events);
++}
++
++/*
++ * Attach a utrace engine for ptrace and set up its event mask.
++ * Returns error code or 0 on success.
++ */
++static int ptrace_attach_task(struct task_struct *tracee, int options)
++{
++ struct utrace_engine *engine;
++ int err;
++
++ engine = ptrace_attach_engine(tracee);
++ if (IS_ERR(engine))
++ return PTR_ERR(engine);
++ /*
++ * It can fail only if the tracee is dead, the caller
++ * must notice this before setting PT_UTRACED.
++ */
++ err = ptrace_set_events(tracee, engine, options);
++ WARN_ON(err && !tracee->exit_state);
++ utrace_engine_put(engine);
++ return 0;
++}
++
++static int ptrace_wake_up(struct task_struct *tracee,
++ struct utrace_engine *engine,
++ enum utrace_resume_action action,
++ bool force_wakeup)
++{
++ if (force_wakeup) {
++ unsigned long flags;
++ /*
++ * Preserve the compatibility bug. Historically ptrace
++ * wakes up the tracee even if it should not. Clear
++ * SIGNAL_STOP_STOPPED for utrace_wakeup().
++ */
++ if (lock_task_sighand(tracee, &flags)) {
++ tracee->signal->flags &= ~SIGNAL_STOP_STOPPED;
++ unlock_task_sighand(tracee, &flags);
++ }
++ }
++
++ if (action != UTRACE_REPORT)
++ ptrace_context(engine)->stop_code = 0;
++
++ return utrace_control(tracee, engine, action);
++}
++
++static void ptrace_detach_task(struct task_struct *tracee, int sig)
++{
++ /*
++ * If true, the caller is PTRACE_DETACH, otherwise
++ * the tracer detaches implicitly during exit.
++ */
++ bool voluntary = (sig >= 0);
++ struct utrace_engine *engine = ptrace_lookup_engine(tracee);
++ enum utrace_resume_action action = UTRACE_DETACH;
++
++ if (unlikely(IS_ERR(engine)))
++ return;
++
++ if (sig) {
++ struct ptrace_context *ctx = ptrace_context(engine);
++
++ switch (get_stop_event(ctx)) {
++ case PTRACE_EVENT_SYSCALL:
++ if (voluntary)
++ send_sig_info(sig, SEND_SIG_PRIV, tracee);
++ break;
++
++ case PTRACE_EVENT_SIGNAL:
++ if (voluntary)
++ ctx->signr = sig;
++ ctx->resume = UTRACE_DETACH;
++ action = UTRACE_RESUME;
++ break;
++ }
++ }
++
++ ptrace_wake_up(tracee, engine, action, voluntary);
++ utrace_engine_put(engine);
++}
++
++static void ptrace_abort_attach(struct task_struct *tracee)
++{
++ ptrace_detach_task(tracee, 0);
++}
++
++static u32 ptrace_report_exit(u32 action, struct utrace_engine *engine,
++ long orig_code, long *code)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++
++ WARN_ON(ptrace_event_pending(ctx) &&
++ !signal_group_exit(current->signal));
++
++ set_stop_code(ctx, PTRACE_EVENT_EXIT);
++ ctx->eventmsg = *code;
++
++ return UTRACE_STOP;
++}
++
++static void ptrace_clone_attach(struct task_struct *child,
++ int options)
++{
++ struct task_struct *parent = current;
++ struct task_struct *tracer;
++ bool abort = true;
++
++ if (unlikely(ptrace_attach_task(child, options))) {
++ WARN_ON(1);
++ return;
++ }
++
++ write_lock_irq(&tasklist_lock);
++ tracer = parent->parent;
++ if (!(tracer->flags & PF_EXITING) && parent->ptrace) {
++ child->ptrace = parent->ptrace;
++ __ptrace_link(child, tracer);
++ abort = false;
++ }
++ write_unlock_irq(&tasklist_lock);
++ if (unlikely(abort)) {
++ ptrace_abort_attach(child);
++ return;
++ }
++
++ sigaddset(&child->pending.signal, SIGSTOP);
++ set_tsk_thread_flag(child, TIF_SIGPENDING);
++}
++
++static u32 ptrace_report_clone(u32 action, struct utrace_engine *engine,
++ unsigned long clone_flags,
++ struct task_struct *child)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++ int event = 0;
++
++ WARN_ON(ptrace_event_pending(ctx));
++
++ if (clone_flags & CLONE_UNTRACED) {
++ /* no events reported */
++ } else if (clone_flags & CLONE_VFORK) {
++ if (ctx->options & PTRACE_O_TRACEVFORK)
++ event = PTRACE_EVENT_VFORK;
++ else if (ctx->options & PTRACE_O_TRACEVFORKDONE)
++ event = PTRACE_EVENT_VFORK_DONE;
++ } else if ((clone_flags & CSIGNAL) != SIGCHLD) {
++ if (ctx->options & PTRACE_O_TRACECLONE)
++ event = PTRACE_EVENT_CLONE;
++ } else if (ctx->options & PTRACE_O_TRACEFORK) {
++ event = PTRACE_EVENT_FORK;
++ }
++ /*
++ * Any of these reports implies auto-attaching the new child.
++ * So does CLONE_PTRACE, even with no event to report.
++ */
++ if ((event && event != PTRACE_EVENT_VFORK_DONE) ||
++ (clone_flags & CLONE_PTRACE))
++ ptrace_clone_attach(child, ctx->options);
++
++ if (!event)
++ return UTRACE_RESUME;
++
++ set_stop_code(ctx, event);
++ ctx->eventmsg = child->pid;
++ /*
++ * We shouldn't stop now, inside the do_fork() path.
++ * We will stop later, before return to user-mode.
++ */
++ if (event == PTRACE_EVENT_VFORK_DONE)
++ return UTRACE_REPORT;
++ else
++ return UTRACE_STOP;
++}
++
++static inline void set_syscall_code(struct ptrace_context *ctx)
++{
++ set_stop_code(ctx, PTRACE_EVENT_SYSCALL);
++ if (ctx->options & PTRACE_O_TRACESYSGOOD)
++ ctx->stop_code |= 0x80;
++}
++
++static u32 ptrace_report_syscall_entry(u32 action, struct utrace_engine *engine,
++ struct pt_regs *regs)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++
++ if (action & UTRACE_SYSCALL_RESUMED) {
++ /*
++ * We already reported the first time.
++ * Nothing more to do now.
++ */
++ if (unlikely(ctx->options & PTRACE_O_SYSEMU))
++ return UTRACE_SYSCALL_ABORT | UTRACE_REPORT;
++ return utrace_syscall_action(action) | UTRACE_RESUME;
++ }
++
++ WARN_ON(ptrace_event_pending(ctx));
++
++ set_syscall_code(ctx);
++
++ if (unlikely(ctx->options & PTRACE_O_SYSEMU))
++ return UTRACE_SYSCALL_ABORT | UTRACE_REPORT;
++ /*
++ * Stop now to report. We will get another callback after
++ * we resume, with the UTRACE_SYSCALL_RESUMED flag set.
++ */
++ return UTRACE_SYSCALL_RUN | UTRACE_STOP;
++}
++
++static u32 ptrace_report_syscall_exit(u32 action, struct utrace_engine *engine,
++ struct pt_regs *regs)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++
++ if (ptrace_event_pending(ctx))
++ return UTRACE_STOP;
++
++ if (ctx->resume != UTRACE_RESUME) {
++ WARN_ON(ctx->resume != UTRACE_BLOCKSTEP &&
++ ctx->resume != UTRACE_SINGLESTEP);
++ ctx->resume = UTRACE_RESUME;
++
++ ctx->signr = SIGTRAP;
++ return UTRACE_INTERRUPT;
++ }
++
++ set_syscall_code(ctx);
++ return UTRACE_STOP;
++}
++
++static u32 ptrace_report_exec(u32 action, struct utrace_engine *engine,
++ const struct linux_binfmt *fmt,
++ const struct linux_binprm *bprm,
++ struct pt_regs *regs)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++
++ WARN_ON(ptrace_event_pending(ctx));
++
++ if (!(ctx->options & PTRACE_O_TRACEEXEC)) {
++ /*
++ * Old-fashioned ptrace'd exec just posts a plain signal.
++ */
++ send_sig(SIGTRAP, current, 0);
++ return UTRACE_RESUME;
++ }
++
++ set_stop_code(ctx, PTRACE_EVENT_EXEC);
++ return UTRACE_STOP;
++}
++
++static enum utrace_signal_action resume_signal(struct ptrace_context *ctx,
++ struct k_sigaction *return_ka)
++{
++ siginfo_t *info = ctx->siginfo;
++ int signr = ctx->signr;
++
++ ctx->siginfo = NULL;
++ ctx->signr = 0;
++
++ /* Did the debugger cancel the sig? */
++ if (!signr)
++ return UTRACE_SIGNAL_IGN;
++ /*
++ * Update the siginfo structure if the signal has changed.
++ * If the debugger wanted something specific in the siginfo
++ * then it should have updated *info via PTRACE_SETSIGINFO.
++ */
++ if (info->si_signo != signr) {
++ info->si_signo = signr;
++ info->si_errno = 0;
++ info->si_code = SI_USER;
++ info->si_pid = task_pid_vnr(current->parent);
++ info->si_uid = task_uid(current->parent);
++ }
++
++ /* If the (new) signal is now blocked, requeue it. */
++ if (sigismember(&current->blocked, signr)) {
++ send_sig_info(signr, info, current);
++ return UTRACE_SIGNAL_IGN;
++ }
++
++ spin_lock_irq(&current->sighand->siglock);
++ *return_ka = current->sighand->action[signr - 1];
++ spin_unlock_irq(&current->sighand->siglock);
++
++ return UTRACE_SIGNAL_DELIVER;
++}
++
++static u32 ptrace_report_signal(u32 action, struct utrace_engine *engine,
++ struct pt_regs *regs,
++ siginfo_t *info,
++ const struct k_sigaction *orig_ka,
++ struct k_sigaction *return_ka)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++ enum utrace_resume_action resume = ctx->resume;
++
++ if (ptrace_event_pending(ctx)) {
++ action = utrace_signal_action(action);
++ WARN_ON(action != UTRACE_SIGNAL_REPORT);
++ return action | UTRACE_STOP;
++ }
++
++ switch (utrace_signal_action(action)) {
++ case UTRACE_SIGNAL_HANDLER:
++ if (WARN_ON(ctx->siginfo))
++ ctx->siginfo = NULL;
++
++ if (resume != UTRACE_RESUME) {
++ WARN_ON(resume != UTRACE_BLOCKSTEP &&
++ resume != UTRACE_SINGLESTEP);
++
++ set_stop_code(ctx, PTRACE_EVENT_SIGTRAP);
++ return UTRACE_STOP | UTRACE_SIGNAL_IGN;
++ }
++
++ case UTRACE_SIGNAL_REPORT:
++ if (!ctx->siginfo) {
++ if (ctx->signr) {
++ /* set by ptrace_resume(SYSCALL_EXIT) */
++ WARN_ON(ctx->signr != SIGTRAP);
++ user_single_step_siginfo(current, regs, info);
++ force_sig_info(SIGTRAP, info, current);
++ }
++
++ return resume | UTRACE_SIGNAL_IGN;
++ }
++
++ if (WARN_ON(ctx->siginfo != info))
++ return resume | UTRACE_SIGNAL_IGN;
++
++ return resume | resume_signal(ctx, return_ka);
++
++ default:
++ break;
++ }
++
++ WARN_ON(ctx->siginfo);
++ ctx->siginfo = info;
++ /*
++ * ctx->siginfo points to the caller's stack.
++ * Make sure the subsequent UTRACE_SIGNAL_REPORT clears
++ * ->siginfo before return from get_signal_to_deliver().
++ */
++ if (utrace_control(current, engine, UTRACE_INTERRUPT))
++ WARN_ON(1);
++
++ ctx->signr = info->si_signo;
++ ctx->stop_code = (PTRACE_EVENT_SIGNAL << 8) | ctx->signr;
++
++ return UTRACE_STOP | UTRACE_SIGNAL_IGN;
++}
++
++static u32 ptrace_report_quiesce(u32 action, struct utrace_engine *engine,
++ unsigned long event)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++
++ if (ptrace_event_pending(ctx))
++ return UTRACE_STOP;
++
++ return event ? UTRACE_RESUME : ctx->resume;
++}
++
++static void ptrace_release(void *data)
++{
++ kfree(data);
++}
++
++static const struct utrace_engine_ops ptrace_utrace_ops = {
++ .report_signal = ptrace_report_signal,
++ .report_quiesce = ptrace_report_quiesce,
++ .report_exec = ptrace_report_exec,
++ .report_exit = ptrace_report_exit,
++ .report_clone = ptrace_report_clone,
++ .report_syscall_entry = ptrace_report_syscall_entry,
++ .report_syscall_exit = ptrace_report_syscall_exit,
++ .release = ptrace_release,
++};
++
++int ptrace_check_attach(struct task_struct *child, int kill)
++{
++ struct utrace_engine *engine;
++ struct utrace_examiner exam;
++ int ret = -ESRCH;
++
++ engine = ptrace_lookup_engine(child);
++ if (IS_ERR(engine))
++ return ret;
++
++ if (child->parent != current)
++ goto out;
++
++ if (unlikely(kill))
++ ret = 0;
++
++ if (!task_is_stopped_or_traced(child))
++ goto out;
++ /*
++ * Make sure our engine has already stopped the child.
++ * Then wait for it to be off the CPU.
++ */
++ if (!utrace_control(child, engine, UTRACE_STOP) &&
++ !utrace_prepare_examine(child, engine, &exam))
++ ret = 0;
++out:
++ utrace_engine_put(engine);
++ return ret;
++}
++
++int ptrace_attach(struct task_struct *task)
++{
++ int retval;
++
++ audit_ptrace(task);
++
++ retval = -EPERM;
++ if (unlikely(task->flags & PF_KTHREAD))
++ goto out;
++ if (same_thread_group(task, current))
++ goto out;
++
++ /*
++ * Protect exec's credential calculations against our interference;
++ * interference; SUID, SGID and LSM creds get determined differently
++ * under ptrace.
++ */
++ retval = -ERESTARTNOINTR;
++ if (mutex_lock_interruptible(&task->cred_guard_mutex))
++ goto out;
++
++ task_lock(task);
++ retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
++ task_unlock(task);
++ if (retval)
++ goto unlock_creds;
++
++ retval = ptrace_attach_task(task, 0);
++ if (unlikely(retval))
++ goto unlock_creds;
++
++ write_lock_irq(&tasklist_lock);
++ retval = -EPERM;
++ if (unlikely(task->exit_state))
++ goto unlock_tasklist;
++
++ BUG_ON(task->ptrace);
++ task->ptrace = PT_UTRACED;
++ if (capable(CAP_SYS_PTRACE))
++ task->ptrace |= PT_PTRACE_CAP;
++
++ __ptrace_link(task, current);
++ send_sig_info(SIGSTOP, SEND_SIG_FORCED, task);
++
++ retval = 0;
++unlock_tasklist:
++ write_unlock_irq(&tasklist_lock);
++unlock_creds:
++ mutex_unlock(&task->cred_guard_mutex);
++out:
++ return retval;
++}
++
++/*
++ * Performs checks and sets PT_UTRACED.
++ * Should be used by all ptrace implementations for PTRACE_TRACEME.
++ */
++int ptrace_traceme(void)
++{
++ bool detach = true;
++ int ret = ptrace_attach_task(current, 0);
++
++ if (unlikely(ret))
++ return ret;
++
++ ret = -EPERM;
++ write_lock_irq(&tasklist_lock);
++ BUG_ON(current->ptrace);
++ ret = security_ptrace_traceme(current->parent);
++ /*
++ * Check PF_EXITING to ensure ->real_parent has not passed
++ * exit_ptrace(). Otherwise we don't report the error but
++ * pretend ->real_parent untraces us right after return.
++ */
++ if (!ret && !(current->real_parent->flags & PF_EXITING)) {
++ current->ptrace = PT_UTRACED;
++ __ptrace_link(current, current->real_parent);
++ detach = false;
++ }
++ write_unlock_irq(&tasklist_lock);
++
++ if (detach)
++ ptrace_abort_attach(current);
++ return ret;
++}
++
++static void ptrace_do_detach(struct task_struct *tracee, unsigned int data)
++{
++ bool detach, release;
++
++ write_lock_irq(&tasklist_lock);
++ /*
++ * This tracee can be already killed. Make sure de_thread() or
++ * our sub-thread doing do_wait() didn't do release_task() yet.
++ */
++ detach = tracee->ptrace != 0;
++ release = false;
++ if (likely(detach))
++ release = __ptrace_detach(current, tracee);
++ write_unlock_irq(&tasklist_lock);
++
++ if (unlikely(release))
++ release_task(tracee);
++ else if (likely(detach))
++ ptrace_detach_task(tracee, data);
++}
++
++int ptrace_detach(struct task_struct *child, unsigned int data)
++{
++ if (!valid_signal(data))
++ return -EIO;
++
++ ptrace_do_detach(child, data);
++
++ return 0;
++}
++
++/*
++ * Detach all tasks we were using ptrace on.
++ */
++void exit_ptrace(struct task_struct *tracer)
++{
++ for (;;) {
++ struct task_struct *tracee = NULL;
++
++ read_lock(&tasklist_lock);
++ if (!list_empty(&tracer->ptraced)) {
++ tracee = list_first_entry(&tracer->ptraced,
++ struct task_struct, ptrace_entry);
++ get_task_struct(tracee);
++ }
++ read_unlock(&tasklist_lock);
++ if (!tracee)
++ break;
++
++ ptrace_do_detach(tracee, -1);
++ put_task_struct(tracee);
++ }
++}
++
++static int ptrace_set_options(struct task_struct *tracee,
++ struct utrace_engine *engine, long data)
++{
++ BUILD_BUG_ON(PTRACE_O_MASK & PTRACE_O_SYSEMU);
++
++ ptrace_set_events(tracee, engine, data & PTRACE_O_MASK);
++ return (data & ~PTRACE_O_MASK) ? -EINVAL : 0;
++}
++
++static int ptrace_rw_siginfo(struct task_struct *tracee,
++ struct ptrace_context *ctx,
++ siginfo_t *info, bool write)
++{
++ unsigned long flags;
++ int err;
++
++ switch (get_stop_event(ctx)) {
++ case 0: /* jctl stop */
++ return -EINVAL;
++
++ case PTRACE_EVENT_SIGNAL:
++ err = -ESRCH;
++ if (lock_task_sighand(tracee, &flags)) {
++ if (likely(task_is_traced(tracee))) {
++ if (write)
++ *ctx->siginfo = *info;
++ else
++ *info = *ctx->siginfo;
++ err = 0;
++ }
++ unlock_task_sighand(tracee, &flags);
++ }
++
++ return err;
++
++ default:
++ if (!write) {
++ memset(info, 0, sizeof(*info));
++ info->si_signo = SIGTRAP;
++ info->si_code = ctx->stop_code & PTRACE_EVENT_MASK;
++ info->si_pid = task_pid_vnr(tracee);
++ info->si_uid = task_uid(tracee);
++ }
++
++ return 0;
++ }
++}
++
++static void do_ptrace_notify_stop(struct ptrace_context *ctx,
++ struct task_struct *tracee)
++{
++ /*
++ * This can race with SIGKILL, but we borrow this race from
++ * the old ptrace implementation. ->exit_code is only needed
++ * for wait_task_stopped()->task_stopped_code(), we should
++ * change it to use ptrace_context.
++ */
++ tracee->exit_code = ctx->stop_code & PTRACE_EVENT_MASK;
++ WARN_ON(!tracee->exit_code);
++
++ read_lock(&tasklist_lock);
++ /*
++ * Don't want to allow preemption here, because
++ * sys_ptrace() needs this task to be inactive.
++ */
++ preempt_disable();
++ /*
++ * It can be killed and then released by our subthread,
++ * or ptrace_attach() has not completed yet.
++ */
++ if (task_ptrace(tracee))
++ do_notify_parent_cldstop(tracee, CLD_TRAPPED);
++ read_unlock(&tasklist_lock);
++ preempt_enable_no_resched();
++}
++
++void ptrace_notify_stop(struct task_struct *tracee)
++{
++ struct utrace_engine *engine = ptrace_lookup_engine(tracee);
++
++ if (IS_ERR(engine))
++ return;
++
++ do_ptrace_notify_stop(ptrace_context(engine), tracee);
++ utrace_engine_put(engine);
++}
++
++static int ptrace_resume_action(struct task_struct *tracee,
++ struct utrace_engine *engine, long request)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++ unsigned long events;
++ int action;
++
++ ctx->options &= ~PTRACE_O_SYSEMU;
++ events = engine->flags & ~UTRACE_EVENT_SYSCALL;
++ action = UTRACE_RESUME;
++
++ switch (request) {
++#ifdef PTRACE_SINGLEBLOCK
++ case PTRACE_SINGLEBLOCK:
++ if (unlikely(!arch_has_block_step()))
++ return -EIO;
++ action = UTRACE_BLOCKSTEP;
++ events |= UTRACE_EVENT(SYSCALL_EXIT);
++ break;
++#endif
++
++#ifdef PTRACE_SINGLESTEP
++ case PTRACE_SINGLESTEP:
++ if (unlikely(!arch_has_single_step()))
++ return -EIO;
++ action = UTRACE_SINGLESTEP;
++ events |= UTRACE_EVENT(SYSCALL_EXIT);
++ break;
++#endif
++
++#ifdef PTRACE_SYSEMU
++ case PTRACE_SYSEMU_SINGLESTEP:
++ if (unlikely(!arch_has_single_step()))
++ return -EIO;
++ action = UTRACE_SINGLESTEP;
++ case PTRACE_SYSEMU:
++ ctx->options |= PTRACE_O_SYSEMU;
++ events |= UTRACE_EVENT(SYSCALL_ENTRY);
++ break;
++#endif
++
++ case PTRACE_SYSCALL:
++ events |= UTRACE_EVENT_SYSCALL;
++ break;
++
++ case PTRACE_CONT:
++ break;
++ default:
++ return -EIO;
++ }
++
++ if (events != engine->flags &&
++ utrace_set_events(tracee, engine, events))
++ return -ESRCH;
++
++ return action;
++}
++
++static int ptrace_resume(struct task_struct *tracee,
++ struct utrace_engine *engine,
++ long request, long data)
++{
++ struct ptrace_context *ctx = ptrace_context(engine);
++ int action;
++
++ if (!valid_signal(data))
++ return -EIO;
++
++ action = ptrace_resume_action(tracee, engine, request);
++ if (action < 0)
++ return action;
++
++ switch (get_stop_event(ctx)) {
++ case PTRACE_EVENT_VFORK:
++ if (ctx->options & PTRACE_O_TRACEVFORKDONE) {
++ set_stop_code(ctx, PTRACE_EVENT_VFORK_DONE);
++ action = UTRACE_REPORT;
++ }
++ break;
++
++ case PTRACE_EVENT_EXEC:
++ case PTRACE_EVENT_FORK:
++ case PTRACE_EVENT_CLONE:
++ case PTRACE_EVENT_VFORK_DONE:
++ if (request == PTRACE_SYSCALL) {
++ set_syscall_code(ctx);
++ do_ptrace_notify_stop(ctx, tracee);
++ return 0;
++ }
++
++ if (action != UTRACE_RESUME) {
++ /*
++ * single-stepping. UTRACE_SIGNAL_REPORT will
++ * synthesize a trap to follow the syscall insn.
++ */
++ ctx->signr = SIGTRAP;
++ action = UTRACE_INTERRUPT;
++ }
++ break;
++
++ case PTRACE_EVENT_SYSCALL:
++ if (data)
++ send_sig_info(data, SEND_SIG_PRIV, tracee);
++ break;
++
++ case PTRACE_EVENT_SIGNAL:
++ ctx->signr = data;
++ break;
++ }
++
++ ctx->resume = action;
++ ptrace_wake_up(tracee, engine, action, true);
++ return 0;
++}
++
++int ptrace_request(struct task_struct *child, long request,
++ long addr, long data)
++{
++ struct utrace_engine *engine = ptrace_lookup_engine(child);
++ siginfo_t siginfo;
++ int ret;
++
++ if (unlikely(IS_ERR(engine)))
++ return -ESRCH;
++
++ switch (request) {
++ case PTRACE_PEEKTEXT:
++ case PTRACE_PEEKDATA:
++ ret = generic_ptrace_peekdata(child, addr, data);
++ break;
++ case PTRACE_POKETEXT:
++ case PTRACE_POKEDATA:
++ ret = generic_ptrace_pokedata(child, addr, data);
++ break;
++
++#ifdef PTRACE_OLDSETOPTIONS
++ case PTRACE_OLDSETOPTIONS:
++#endif
++ case PTRACE_SETOPTIONS:
++ ret = ptrace_set_options(child, engine, data);
++ break;
++ case PTRACE_GETEVENTMSG:
++ ret = put_user(ptrace_context(engine)->eventmsg,
++ (unsigned long __user *) data);
++ break;
++
++ case PTRACE_GETSIGINFO:
++ ret = ptrace_rw_siginfo(child, ptrace_context(engine),
++ &siginfo, false);
++ if (!ret)
++ ret = copy_siginfo_to_user((siginfo_t __user *) data,
++ &siginfo);
++ break;
++
++ case PTRACE_SETSIGINFO:
++ if (copy_from_user(&siginfo, (siginfo_t __user *) data,
++ sizeof siginfo))
++ ret = -EFAULT;
++ else
++ ret = ptrace_rw_siginfo(child, ptrace_context(engine),
++ &siginfo, true);
++ break;
++
++ case PTRACE_DETACH: /* detach a process that was attached. */
++ ret = ptrace_detach(child, data);
++ break;
++
++ case PTRACE_KILL:
++ /* Ugly historical behaviour. */
++ if (task_is_traced(child))
++ ptrace_resume(child, engine, PTRACE_CONT, SIGKILL);
++ ret = 0;
++ break;
++
++ default:
++ ret = ptrace_resume(child, engine, request, data);
++ break;
++ }
++
++ utrace_engine_put(engine);
++ return ret;
++}
++
++#if defined CONFIG_COMPAT
++#include <linux/compat.h>
++
++int compat_ptrace_request(struct task_struct *child, compat_long_t request,
++ compat_ulong_t addr, compat_ulong_t data)
++{
++ struct utrace_engine *engine = ptrace_lookup_engine(child);
++ compat_ulong_t __user *datap = compat_ptr(data);
++ compat_ulong_t word;
++ siginfo_t siginfo;
++ int ret;
++
++ if (unlikely(IS_ERR(engine)))
++ return -ESRCH;
++
++ switch (request) {
++ case PTRACE_PEEKTEXT:
++ case PTRACE_PEEKDATA:
++ ret = access_process_vm(child, addr, &word, sizeof(word), 0);
++ if (ret != sizeof(word))
++ ret = -EIO;
++ else
++ ret = put_user(word, datap);
++ break;
++
++ case PTRACE_POKETEXT:
++ case PTRACE_POKEDATA:
++ ret = access_process_vm(child, addr, &data, sizeof(data), 1);
++ ret = (ret != sizeof(data) ? -EIO : 0);
++ break;
++
++ case PTRACE_GETEVENTMSG:
++ ret = put_user((compat_ulong_t)ptrace_context(engine)->eventmsg,
++ datap);
++ break;
++
++ case PTRACE_GETSIGINFO:
++ ret = ptrace_rw_siginfo(child, ptrace_context(engine),
++ &siginfo, false);
++ if (!ret)
++ ret = copy_siginfo_to_user32(
++ (struct compat_siginfo __user *) datap,
++ &siginfo);
++ break;
++
++ case PTRACE_SETSIGINFO:
++ memset(&siginfo, 0, sizeof siginfo);
++ if (copy_siginfo_from_user32(
++ &siginfo, (struct compat_siginfo __user *) datap))
++ ret = -EFAULT;
++ else
++ ret = ptrace_rw_siginfo(child, ptrace_context(engine),
++ &siginfo, true);
++ break;
++
++ default:
++ ret = ptrace_request(child, request, addr, data);
++ }
++
++ utrace_engine_put(engine);
++ return ret;
++}
++#endif /* CONFIG_COMPAT */
+diff --git a/kernel/ptrace.c b/kernel/ptrace.c
+index a408bf7..4e87441 100644
+--- a/kernel/ptrace.c
++++ b/kernel/ptrace.c
+@@ -16,7 +16,6 @@
+ #include <linux/pagemap.h>
+ #include <linux/smp_lock.h>
+ #include <linux/ptrace.h>
+-#include <linux/utrace.h>
+ #include <linux/security.h>
+ #include <linux/signal.h>
+ #include <linux/audit.h>
+@@ -24,7 +23,286 @@
+ #include <linux/syscalls.h>
+ #include <linux/uaccess.h>
+
++int __ptrace_may_access(struct task_struct *task, unsigned int mode)
++{
++ const struct cred *cred = current_cred(), *tcred;
++
++ /* May we inspect the given task?
++ * This check is used both for attaching with ptrace
++ * and for allowing access to sensitive information in /proc.
++ *
++ * ptrace_attach denies several cases that /proc allows
++ * because setting up the necessary parent/child relationship
++ * or halting the specified task is impossible.
++ */
++ int dumpable = 0;
++ /* Don't let security modules deny introspection */
++ if (task == current)
++ return 0;
++ rcu_read_lock();
++ tcred = __task_cred(task);
++ if ((cred->uid != tcred->euid ||
++ cred->uid != tcred->suid ||
++ cred->uid != tcred->uid ||
++ cred->gid != tcred->egid ||
++ cred->gid != tcred->sgid ||
++ cred->gid != tcred->gid) &&
++ !capable(CAP_SYS_PTRACE)) {
++ rcu_read_unlock();
++ return -EPERM;
++ }
++ rcu_read_unlock();
++ smp_rmb();
++ if (task->mm)
++ dumpable = get_dumpable(task->mm);
++ if (!dumpable && !capable(CAP_SYS_PTRACE))
++ return -EPERM;
++
++ return security_ptrace_access_check(task, mode);
++}
++
++bool ptrace_may_access(struct task_struct *task, unsigned int mode)
++{
++ int err;
++ task_lock(task);
++ err = __ptrace_may_access(task, mode);
++ task_unlock(task);
++ return !err;
++}
++
++/*
++ * Called with irqs disabled, returns true if childs should reap themselves.
++ */
++static int ignoring_children(struct sighand_struct *sigh)
++{
++ int ret;
++ spin_lock(&sigh->siglock);
++ ret = (sigh->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) ||
++ (sigh->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT);
++ spin_unlock(&sigh->siglock);
++ return ret;
++}
++
++/*
++ * Called with tasklist_lock held for writing.
++ * Unlink a traced task, and clean it up if it was a traced zombie.
++ * Return true if it needs to be reaped with release_task().
++ * (We can't call release_task() here because we already hold tasklist_lock.)
++ *
++ * If it's a zombie, our attachedness prevented normal parent notification
++ * or self-reaping. Do notification now if it would have happened earlier.
++ * If it should reap itself, return true.
++ *
++ * If it's our own child, there is no notification to do. But if our normal
++ * children self-reap, then this child was prevented by ptrace and we must
++ * reap it now, in that case we must also wake up sub-threads sleeping in
++ * do_wait().
++ */
++bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
++{
++ __ptrace_unlink(p);
++
++ if (p->exit_state == EXIT_ZOMBIE) {
++ if (!task_detached(p) && thread_group_empty(p)) {
++ if (!same_thread_group(p->real_parent, tracer))
++ do_notify_parent(p, p->exit_signal);
++ else if (ignoring_children(tracer->sighand)) {
++ __wake_up_parent(p, tracer);
++ p->exit_signal = -1;
++ }
++ }
++ if (task_detached(p)) {
++ /* Mark it as in the process of being reaped. */
++ p->exit_state = EXIT_DEAD;
++ return true;
++ }
++ }
++
++ return false;
++}
++
++int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
++{
++ int copied = 0;
++
++ while (len > 0) {
++ char buf[128];
++ int this_len, retval;
++
++ this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
++ retval = access_process_vm(tsk, src, buf, this_len, 0);
++ if (!retval) {
++ if (copied)
++ break;
++ return -EIO;
++ }
++ if (copy_to_user(dst, buf, retval))
++ return -EFAULT;
++ copied += retval;
++ src += retval;
++ dst += retval;
++ len -= retval;
++ }
++ return copied;
++}
++
++int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len)
++{
++ int copied = 0;
++
++ while (len > 0) {
++ char buf[128];
++ int this_len, retval;
++
++ this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
++ if (copy_from_user(buf, src, this_len))
++ return -EFAULT;
++ retval = access_process_vm(tsk, dst, buf, this_len, 1);
++ if (!retval) {
++ if (copied)
++ break;
++ return -EIO;
++ }
++ copied += retval;
++ src += retval;
++ dst += retval;
++ len -= retval;
++ }
++ return copied;
++}
++
++static struct task_struct *ptrace_get_task_struct(pid_t pid)
++{
++ struct task_struct *child;
++
++ rcu_read_lock();
++ child = find_task_by_vpid(pid);
++ if (child)
++ get_task_struct(child);
++ rcu_read_unlock();
++
++ if (!child)
++ return ERR_PTR(-ESRCH);
++ return child;
++}
++
++#ifndef arch_ptrace_attach
++#define arch_ptrace_attach(child) do { } while (0)
++#endif
+
++SYSCALL_DEFINE4(ptrace, long, request, long, pid, long, addr, long, data)
++{
++ struct task_struct *child;
++ long ret;
++
++ /*
++ * This lock_kernel fixes a subtle race with suid exec
++ */
++ lock_kernel();
++ if (request == PTRACE_TRACEME) {
++ ret = ptrace_traceme();
++ if (!ret)
++ arch_ptrace_attach(current);
++ goto out;
++ }
++
++ child = ptrace_get_task_struct(pid);
++ if (IS_ERR(child)) {
++ ret = PTR_ERR(child);
++ goto out;
++ }
++
++ if (request == PTRACE_ATTACH) {
++ ret = ptrace_attach(child);
++ /*
++ * Some architectures need to do book-keeping after
++ * a ptrace attach.
++ */
++ if (!ret)
++ arch_ptrace_attach(child);
++ goto out_put_task_struct;
++ }
++
++ ret = ptrace_check_attach(child, request == PTRACE_KILL);
++ if (ret < 0)
++ goto out_put_task_struct;
++
++ ret = arch_ptrace(child, request, addr, data);
++
++ out_put_task_struct:
++ put_task_struct(child);
++ out:
++ unlock_kernel();
++ return ret;
++}
++
++int generic_ptrace_peekdata(struct task_struct *tsk, long addr, long data)
++{
++ unsigned long tmp;
++ int copied;
++
++ copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
++ if (copied != sizeof(tmp))
++ return -EIO;
++ return put_user(tmp, (unsigned long __user *)data);
++}
++
++int generic_ptrace_pokedata(struct task_struct *tsk, long addr, long data)
++{
++ int copied;
++
++ copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
++ return (copied == sizeof(data)) ? 0 : -EIO;
++}
++
++#if defined CONFIG_COMPAT
++#include <linux/compat.h>
++
++asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
++ compat_long_t addr, compat_long_t data)
++{
++ struct task_struct *child;
++ long ret;
++
++ /*
++ * This lock_kernel fixes a subtle race with suid exec
++ */
++ lock_kernel();
++ if (request == PTRACE_TRACEME) {
++ ret = ptrace_traceme();
++ goto out;
++ }
++
++ child = ptrace_get_task_struct(pid);
++ if (IS_ERR(child)) {
++ ret = PTR_ERR(child);
++ goto out;
++ }
++
++ if (request == PTRACE_ATTACH) {
++ ret = ptrace_attach(child);
++ /*
++ * Some architectures need to do book-keeping after
++ * a ptrace attach.
++ */
++ if (!ret)
++ arch_ptrace_attach(child);
++ goto out_put_task_struct;
++ }
++
++ ret = ptrace_check_attach(child, request == PTRACE_KILL);
++ if (!ret)
++ ret = compat_arch_ptrace(child, request, addr, data);
++
++ out_put_task_struct:
++ put_task_struct(child);
++ out:
++ unlock_kernel();
++ return ret;
++}
++#endif /* CONFIG_COMPAT */
++
++#ifndef CONFIG_UTRACE
+ /*
+ * ptrace a task: make the debugger its new parent and
+ * move it to the ptrace list.
+@@ -101,76 +379,21 @@ int ptrace_check_attach(struct task_stru
+ /*
+ * child->sighand can't be NULL, release_task()
+ * does ptrace_unlink() before __exit_signal().
+- */
+- spin_lock_irq(&child->sighand->siglock);
+- if (task_is_stopped(child))
+- child->state = TASK_TRACED;
+- else if (!task_is_traced(child) && !kill)
+- ret = -ESRCH;
+- spin_unlock_irq(&child->sighand->siglock);
+- }
+- read_unlock(&tasklist_lock);
+-
+- if (!ret && !kill)
+- ret = wait_task_inactive(child, TASK_TRACED) ? 0 : -ESRCH;
+-
+- /* All systems go.. */
+- return ret;
+-}
+-
+-int __ptrace_may_access(struct task_struct *task, unsigned int mode)
+-{
+- const struct cred *cred = current_cred(), *tcred;
+-
+- /* May we inspect the given task?
+- * This check is used both for attaching with ptrace
+- * and for allowing access to sensitive information in /proc.
+- *
+- * ptrace_attach denies several cases that /proc allows
+- * because setting up the necessary parent/child relationship
+- * or halting the specified task is impossible.
+- */
+- int dumpable = 0;
+- /* Don't let security modules deny introspection */
+- if (task == current)
+- return 0;
+- rcu_read_lock();
+- tcred = __task_cred(task);
+- if ((cred->uid != tcred->euid ||
+- cred->uid != tcred->suid ||
+- cred->uid != tcred->uid ||
+- cred->gid != tcred->egid ||
+- cred->gid != tcred->sgid ||
+- cred->gid != tcred->gid) &&
+- !capable(CAP_SYS_PTRACE)) {
+- rcu_read_unlock();
+- return -EPERM;
+- }
+- rcu_read_unlock();
+- smp_rmb();
+- if (task->mm)
+- dumpable = get_dumpable(task->mm);
+- if (!dumpable && !capable(CAP_SYS_PTRACE))
+- return -EPERM;
+-
+- return security_ptrace_access_check(task, mode);
+-}
++ */
++ spin_lock_irq(&child->sighand->siglock);
++ if (task_is_stopped(child))
++ child->state = TASK_TRACED;
++ else if (!task_is_traced(child) && !kill)
++ ret = -ESRCH;
++ spin_unlock_irq(&child->sighand->siglock);
++ }
++ read_unlock(&tasklist_lock);
+
+-bool ptrace_may_access(struct task_struct *task, unsigned int mode)
+-{
+- int err;
+- task_lock(task);
+- err = __ptrace_may_access(task, mode);
+- task_unlock(task);
+- return !err;
+-}
++ if (!ret && !kill)
++ ret = wait_task_inactive(child, TASK_TRACED) ? 0 : -ESRCH;
+
+-/*
+- * For experimental use of utrace, exclude ptrace on the same task.
+- */
+-static inline bool exclude_ptrace(struct task_struct *task)
+-{
+- return unlikely(!!task_utrace_flags(task));
++ /* All systems go.. */
++ return ret;
+ }
+
+ int ptrace_attach(struct task_struct *task)
+@@ -196,8 +419,6 @@ int ptrace_attach(struct task_struct *ta
+
+ task_lock(task);
+ retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
+- if (!retval && exclude_ptrace(task))
+- retval = -EBUSY;
+ task_unlock(task);
+ if (retval)
+ goto unlock_creds;
+@@ -235,9 +456,6 @@ int ptrace_traceme(void)
+ {
+ int ret = -EPERM;
+
+- if (exclude_ptrace(current)) /* XXX locking */
+- return -EBUSY;
+-
+ write_lock_irq(&tasklist_lock);
+ /* Are we already being traced? */
+ if (!current->ptrace) {
+@@ -257,57 +475,6 @@ int ptrace_traceme(void)
+ return ret;
+ }
+
+-/*
+- * Called with irqs disabled, returns true if childs should reap themselves.
+- */
+-static int ignoring_children(struct sighand_struct *sigh)
+-{
+- int ret;
+- spin_lock(&sigh->siglock);
+- ret = (sigh->action[SIGCHLD-1].sa.sa_handler == SIG_IGN) ||
+- (sigh->action[SIGCHLD-1].sa.sa_flags & SA_NOCLDWAIT);
+- spin_unlock(&sigh->siglock);
+- return ret;
+-}
+-
+-/*
+- * Called with tasklist_lock held for writing.
+- * Unlink a traced task, and clean it up if it was a traced zombie.
+- * Return true if it needs to be reaped with release_task().
+- * (We can't call release_task() here because we already hold tasklist_lock.)
+- *
+- * If it's a zombie, our attachedness prevented normal parent notification
+- * or self-reaping. Do notification now if it would have happened earlier.
+- * If it should reap itself, return true.
+- *
+- * If it's our own child, there is no notification to do. But if our normal
+- * children self-reap, then this child was prevented by ptrace and we must
+- * reap it now, in that case we must also wake up sub-threads sleeping in
+- * do_wait().
+- */
+-bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
+-{
+- __ptrace_unlink(p);
+-
+- if (p->exit_state == EXIT_ZOMBIE) {
+- if (!task_detached(p) && thread_group_empty(p)) {
+- if (!same_thread_group(p->real_parent, tracer))
+- do_notify_parent(p, p->exit_signal);
+- else if (ignoring_children(tracer->sighand)) {
+- __wake_up_parent(p, tracer);
+- p->exit_signal = -1;
+- }
+- }
+- if (task_detached(p)) {
+- /* Mark it as in the process of being reaped. */
+- p->exit_state = EXIT_DEAD;
+- return true;
+- }
+- }
+-
+- return false;
+-}
+-
+ int ptrace_detach(struct task_struct *child, unsigned int data)
+ {
+ bool dead = false;
+@@ -361,56 +528,6 @@ void exit_ptrace(struct task_struct *tra
+ }
+ }
+
+-int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len)
+-{
+- int copied = 0;
+-
+- while (len > 0) {
+- char buf[128];
+- int this_len, retval;
+-
+- this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
+- retval = access_process_vm(tsk, src, buf, this_len, 0);
+- if (!retval) {
+- if (copied)
+- break;
+- return -EIO;
+- }
+- if (copy_to_user(dst, buf, retval))
+- return -EFAULT;
+- copied += retval;
+- src += retval;
+- dst += retval;
+- len -= retval;
+- }
+- return copied;
+-}
+-
+-int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len)
+-{
+- int copied = 0;
+-
+- while (len > 0) {
+- char buf[128];
+- int this_len, retval;
+-
+- this_len = (len > sizeof(buf)) ? sizeof(buf) : len;
+- if (copy_from_user(buf, src, this_len))
+- return -EFAULT;
+- retval = access_process_vm(tsk, dst, buf, this_len, 1);
+- if (!retval) {
+- if (copied)
+- break;
+- return -EIO;
+- }
+- copied += retval;
+- src += retval;
+- dst += retval;
+- len -= retval;
+- }
+- return copied;
+-}
+-
+ static int ptrace_setoptions(struct task_struct *child, long data)
+ {
+ child->ptrace &= ~PT_TRACE_MASK;
+@@ -594,93 +710,7 @@ int ptrace_request(struct task_struct *c
+ return ret;
+ }
+
+-static struct task_struct *ptrace_get_task_struct(pid_t pid)
+-{
+- struct task_struct *child;
+-
+- rcu_read_lock();
+- child = find_task_by_vpid(pid);
+- if (child)
+- get_task_struct(child);
+- rcu_read_unlock();
+-
+- if (!child)
+- return ERR_PTR(-ESRCH);
+- return child;
+-}
+-
+-#ifndef arch_ptrace_attach
+-#define arch_ptrace_attach(child) do { } while (0)
+-#endif
+-
+-SYSCALL_DEFINE4(ptrace, long, request, long, pid, long, addr, long, data)
+-{
+- struct task_struct *child;
+- long ret;
+-
+- /*
+- * This lock_kernel fixes a subtle race with suid exec
+- */
+- lock_kernel();
+- if (request == PTRACE_TRACEME) {
+- ret = ptrace_traceme();
+- if (!ret)
+- arch_ptrace_attach(current);
+- goto out;
+- }
+-
+- child = ptrace_get_task_struct(pid);
+- if (IS_ERR(child)) {
+- ret = PTR_ERR(child);
+- goto out;
+- }
+-
+- if (request == PTRACE_ATTACH) {
+- ret = ptrace_attach(child);
+- /*
+- * Some architectures need to do book-keeping after
+- * a ptrace attach.
+- */
+- if (!ret)
+- arch_ptrace_attach(child);
+- goto out_put_task_struct;
+- }
+-
+- ret = ptrace_check_attach(child, request == PTRACE_KILL);
+- if (ret < 0)
+- goto out_put_task_struct;
+-
+- ret = arch_ptrace(child, request, addr, data);
+-
+- out_put_task_struct:
+- put_task_struct(child);
+- out:
+- unlock_kernel();
+- return ret;
+-}
+-
+-int generic_ptrace_peekdata(struct task_struct *tsk, long addr, long data)
+-{
+- unsigned long tmp;
+- int copied;
+-
+- copied = access_process_vm(tsk, addr, &tmp, sizeof(tmp), 0);
+- if (copied != sizeof(tmp))
+- return -EIO;
+- return put_user(tmp, (unsigned long __user *)data);
+-}
+-
+-int generic_ptrace_pokedata(struct task_struct *tsk, long addr, long data)
+-{
+- int copied;
+-
+- copied = access_process_vm(tsk, addr, &data, sizeof(data), 1);
+- return (copied == sizeof(data)) ? 0 : -EIO;
+-}
+-
+ #if defined CONFIG_COMPAT
+-#include <linux/compat.h>
+-
+ int compat_ptrace_request(struct task_struct *child, compat_long_t request,
+ compat_ulong_t addr, compat_ulong_t data)
+ {
+@@ -732,47 +762,5 @@ int compat_ptrace_request(struct task_st
+
+ return ret;
+ }
+-
+-asmlinkage long compat_sys_ptrace(compat_long_t request, compat_long_t pid,
+- compat_long_t addr, compat_long_t data)
+-{
+- struct task_struct *child;
+- long ret;
+-
+- /*
+- * This lock_kernel fixes a subtle race with suid exec
+- */
+- lock_kernel();
+- if (request == PTRACE_TRACEME) {
+- ret = ptrace_traceme();
+- goto out;
+- }
+-
+- child = ptrace_get_task_struct(pid);
+- if (IS_ERR(child)) {
+- ret = PTR_ERR(child);
+- goto out;
+- }
+-
+- if (request == PTRACE_ATTACH) {
+- ret = ptrace_attach(child);
+- /*
+- * Some architectures need to do book-keeping after
+- * a ptrace attach.
+- */
+- if (!ret)
+- arch_ptrace_attach(child);
+- goto out_put_task_struct;
+- }
+-
+- ret = ptrace_check_attach(child, request == PTRACE_KILL);
+- if (!ret)
+- ret = compat_arch_ptrace(child, request, addr, data);
+-
+- out_put_task_struct:
+- put_task_struct(child);
+- out:
+- unlock_kernel();
+- return ret;
+-}
+ #endif /* CONFIG_COMPAT */
++#endif /* CONFIG_UTRACE */
+diff --git a/kernel/utrace.c b/kernel/utrace.c
+index 84d965d..ead1f13 100644
+--- a/kernel/utrace.c
++++ b/kernel/utrace.c
+@@ -811,6 +811,22 @@ relock:
+ spin_unlock_irq(&task->sighand->siglock);
+ spin_unlock(&utrace->lock);
+
++ /*
++ * If ptrace is among the reasons for this stop, do its
++ * notification now. This could not just be done in
++ * ptrace's own event report callbacks because it has to
++ * be done after we are in TASK_TRACED. This makes the
++ * synchronization with ptrace_do_wait() work right.
++ *
++ * It's only because of the bad old overloading of the do_wait()
++ * logic for handling ptrace stops that we need this special case
++ * here. One day we will clean up ptrace so it does not need to
++ * work this way. New things that are designed sensibly don't need
++ * a wakeup that synchronizes with tasklist_lock and ->state, so
++ * the proper utrace API does not try to support this weirdness.
++ */
++ ptrace_notify_stop(task);
++
+ schedule();
+
+ utrace_finish_stop();
diff --git a/linux-2.6-utrace.patch b/linux-2.6-utrace.patch
new file mode 100644
index 0000000..8d1adf8
--- /dev/null
+++ b/linux-2.6-utrace.patch
@@ -0,0 +1,4163 @@
+utrace core
+
+This adds the utrace facility, a new modular interface in the kernel for
+implementing user thread tracing and debugging. This fits on top of the
+tracehook_* layer, so the new code is well-isolated.
+
+The new interface is in <linux/utrace.h> and the DocBook utrace book
+describes it. It allows for multiple separate tracing engines to work in
+parallel without interfering with each other. Higher-level tracing
+facilities can be implemented as loadable kernel modules using this layer.
+
+The new facility is made optional under CONFIG_UTRACE.
+When this is not enabled, no new code is added.
+It can only be enabled on machines that have all the
+prerequisites and select CONFIG_HAVE_ARCH_TRACEHOOK.
+
+In this initial version, utrace and ptrace do not play together at all.
+If ptrace is attached to a thread, the attach calls in the utrace kernel
+API return -EBUSY. If utrace is attached to a thread, the PTRACE_ATTACH
+or PTRACE_TRACEME request will return EBUSY to userland. The old ptrace
+code is otherwise unchanged and nothing using ptrace should be affected
+by this patch as long as utrace is not used at the same time. In the
+future we can clean up the ptrace implementation and rework it to use
+the utrace API.
+
+Signed-off-by: Roland McGrath <roland@redhat.com>
+---
+ Documentation/DocBook/Makefile | 2 +-
+ Documentation/DocBook/utrace.tmpl | 590 +++++++++
+ fs/proc/array.c | 3 +
+ include/linux/sched.h | 5 +
+ include/linux/tracehook.h | 87 ++-
+ include/linux/utrace.h | 692 +++++++++++
+ init/Kconfig | 9 +
+ kernel/Makefile | 1 +
+ kernel/fork.c | 3 +
+ kernel/ptrace.c | 14 +
+ kernel/utrace.c | 2436 +++++++++++++++++++++++++++++++++++++
+ 11 files changed, 3840 insertions(+), 2 deletions(-)
+
+diff --git a/Documentation/DocBook/Makefile b/Documentation/DocBook/Makefile
+index ab8300f..95f59e4 100644
+--- a/Documentation/DocBook/Makefile
++++ b/Documentation/DocBook/Makefile
+@@ -9,7 +9,7 @@
+ DOCBOOKS := z8530book.xml mcabook.xml device-drivers.xml \
+ kernel-hacking.xml kernel-locking.xml deviceiobook.xml \
+ procfs-guide.xml writing_usb_driver.xml networking.xml \
+- kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml \
++ kernel-api.xml filesystems.xml lsm.xml usb.xml kgdb.xml utrace.xml \
+ gadget.xml libata.xml mtdnand.xml librs.xml rapidio.xml \
+ genericirq.xml s390-drivers.xml uio-howto.xml scsi.xml \
+ mac80211.xml debugobjects.xml sh.xml regulator.xml \
+diff --git a/Documentation/DocBook/utrace.tmpl b/Documentation/DocBook/utrace.tmpl
+new file mode 100644
+index ...e149f49 100644
+--- /dev/null
++++ b/Documentation/DocBook/utrace.tmpl
+@@ -0,0 +1,590 @@
++<?xml version="1.0" encoding="UTF-8"?>
++<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
++"http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" []>
++
++<book id="utrace">
++ <bookinfo>
++ <title>The utrace User Debugging Infrastructure</title>
++ </bookinfo>
++
++ <toc></toc>
++
++ <chapter id="concepts"><title>utrace concepts</title>
++
++ <sect1 id="intro"><title>Introduction</title>
++
++ <para>
++ <application>utrace</application> is infrastructure code for tracing
++ and controlling user threads. This is the foundation for writing
++ tracing engines, which can be loadable kernel modules.
++ </para>
++
++ <para>
++ The basic actors in <application>utrace</application> are the thread
++ and the tracing engine. A tracing engine is some body of code that
++ calls into the <filename>&lt;linux/utrace.h&gt;</filename>
++ interfaces, represented by a <structname>struct
++ utrace_engine_ops</structname>. (Usually it's a kernel module,
++ though the legacy <function>ptrace</function> support is a tracing
++ engine that is not in a kernel module.) The interface operates on
++ individual threads (<structname>struct task_struct</structname>).
++ If an engine wants to treat several threads as a group, that is up
++ to its higher-level code.
++ </para>
++
++ <para>
++ Tracing begins by attaching an engine to a thread, using
++ <function>utrace_attach_task</function> or
++ <function>utrace_attach_pid</function>. If successful, it returns a
++ pointer that is the handle used in all other calls.
++ </para>
++
++ </sect1>
++
++ <sect1 id="callbacks"><title>Events and Callbacks</title>
++
++ <para>
++ An attached engine does nothing by default. An engine makes something
++ happen by requesting callbacks via <function>utrace_set_events</function>
++ and poking the thread with <function>utrace_control</function>.
++ The synchronization issues related to these two calls
++ are discussed further below in <xref linkend="teardown"/>.
++ </para>
++
++ <para>
++ Events are specified using the macro
++ <constant>UTRACE_EVENT(<replaceable>type</replaceable>)</constant>.
++ Each event type is associated with a callback in <structname>struct
++ utrace_engine_ops</structname>. A tracing engine can leave unused
++ callbacks <constant>NULL</constant>. The only callbacks required
++ are those used by the event flags it sets.
++ </para>
++
++ <para>
++ Many engines can be attached to each thread. When a thread has an
++ event, each engine gets a callback if it has set the event flag for
++ that event type. For most events, engines are called in the order they
++ attached. Engines that attach after the event has occurred do not get
++ callbacks for that event. This includes any new engines just attached
++ by an existing engine's callback function. Once the sequence of
++ callbacks for that one event has completed, such new engines are then
++ eligible in the next sequence that starts when there is another event.
++ </para>
++
++ <para>
++ Event reporting callbacks have details particular to the event type,
++ but are all called in similar environments and have the same
++ constraints. Callbacks are made from safe points, where no locks
++ are held, no special resources are pinned (usually), and the
++ user-mode state of the thread is accessible. So, callback code has
++ a pretty free hand. But to be a good citizen, callback code should
++ never block for long periods. It is fine to block in
++ <function>kmalloc</function> and the like, but never wait for i/o or
++ for user mode to do something. If you need the thread to wait, use
++ <constant>UTRACE_STOP</constant> and return from the callback
++ quickly. When your i/o finishes or whatever, you can use
++ <function>utrace_control</function> to resume the thread.
++ </para>
++
++ <para>
++ The <constant>UTRACE_EVENT(SYSCALL_ENTRY)</constant> event is a special
++ case. While other events happen in the kernel when it will return to
++ user mode soon, this event happens when entering the kernel before it
++ will proceed with the work requested from user mode. Because of this
++ difference, the <function>report_syscall_entry</function> callback is
++ special in two ways. For this event, engines are called in reverse of
++ the normal order (this includes the <function>report_quiesce</function>
++ call that precedes a <function>report_syscall_entry</function> call).
++ This preserves the semantics that the last engine to attach is called
++ "closest to user mode"--the engine that is first to see a thread's user
++ state when it enters the kernel is also the last to see that state when
++ the thread returns to user mode. For the same reason, if these
++ callbacks use <constant>UTRACE_STOP</constant> (see the next section),
++ the thread stops immediately after callbacks rather than only when it's
++ ready to return to user mode; when allowed to resume, it will actually
++ attempt the system call indicated by the register values at that time.
++ </para>
++
++ </sect1>
++
++ <sect1 id="safely"><title>Stopping Safely</title>
++
++ <sect2 id="well-behaved"><title>Writing well-behaved callbacks</title>
++
++ <para>
++ Well-behaved callbacks are important to maintain two essential
++ properties of the interface. The first of these is that unrelated
++ tracing engines should not interfere with each other. If your engine's
++ event callback does not return quickly, then another engine won't get
++ the event notification in a timely manner. The second important
++ property is that tracing should be as noninvasive as possible to the
++ normal operation of the system overall and of the traced thread in
++ particular. That is, attached tracing engines should not perturb a
++ thread's behavior, except to the extent that changing its user-visible
++ state is explicitly what you want to do. (Obviously some perturbation
++ is unavoidable, primarily timing changes, ranging from small delays due
++ to the overhead of tracing, to arbitrary pauses in user code execution
++ when a user stops a thread with a debugger for examination.) Even when
++ you explicitly want the perturbation of making the traced thread block,
++ just blocking directly in your callback has more unwanted effects. For
++ example, the <constant>CLONE</constant> event callbacks are called when
++ the new child thread has been created but not yet started running; the
++ child can never be scheduled until the <constant>CLONE</constant>
++ tracing callbacks return. (This allows engines tracing the parent to
++ attach to the child.) If a <constant>CLONE</constant> event callback
++ blocks the parent thread, it also prevents the child thread from
++ running (even to process a <constant>SIGKILL</constant>). If what you
++ want is to make both the parent and child block, then use
++ <function>utrace_attach_task</function> on the child and then use
++ <constant>UTRACE_STOP</constant> on both threads. A more crucial
++ problem with blocking in callbacks is that it can prevent
++ <constant>SIGKILL</constant> from working. A thread that is blocking
++ due to <constant>UTRACE_STOP</constant> will still wake up and die
++ immediately when sent a <constant>SIGKILL</constant>, as all threads
++ should. Relying on the <application>utrace</application>
++ infrastructure rather than on private synchronization calls in event
++ callbacks is an important way to help keep tracing robustly
++ noninvasive.
++ </para>
++
++ </sect2>
++
++ <sect2 id="UTRACE_STOP"><title>Using <constant>UTRACE_STOP</constant></title>
++
++ <para>
++ To control another thread and access its state, it must be stopped
++ with <constant>UTRACE_STOP</constant>. This means that it is
++ stopped and won't start running again while we access it. When a
++ thread is not already stopped, <function>utrace_control</function>
++ returns <constant>-EINPROGRESS</constant> and an engine must wait
++ for an event callback when the thread is ready to stop. The thread
++ may be running on another CPU or may be blocked. When it is ready
++ to be examined, it will make callbacks to engines that set the
++ <constant>UTRACE_EVENT(QUIESCE)</constant> event bit. To wake up an
++ interruptible wait, use <constant>UTRACE_INTERRUPT</constant>.
++ </para>
++
++ <para>
++ As long as some engine has used <constant>UTRACE_STOP</constant> and
++ not called <function>utrace_control</function> to resume the thread,
++ then the thread will remain stopped. <constant>SIGKILL</constant>
++ will wake it up, but it will not run user code. When the stop is
++ cleared with <function>utrace_control</function> or a callback
++ return value, the thread starts running again.
++ (See also <xref linkend="teardown"/>.)
++ </para>
++
++ </sect2>
++
++ </sect1>
++
++ <sect1 id="teardown"><title>Tear-down Races</title>
++
++ <sect2 id="SIGKILL"><title>Primacy of <constant>SIGKILL</constant></title>
++ <para>
++ Ordinarily synchronization issues for tracing engines are kept fairly
++ straightforward by using <constant>UTRACE_STOP</constant>. You ask a
++ thread to stop, and then once it makes the
++ <function>report_quiesce</function> callback it cannot do anything else
++ that would result in another callback, until you let it with a
++ <function>utrace_control</function> call. This simple arrangement
++ avoids complex and error-prone code in each one of a tracing engine's
++ event callbacks to keep them serialized with the engine's other
++ operations done on that thread from another thread of control.
++ However, giving tracing engines complete power to keep a traced thread
++ stuck in place runs afoul of a more important kind of simplicity that
++ the kernel overall guarantees: nothing can prevent or delay
++ <constant>SIGKILL</constant> from making a thread die and release its
++ resources. To preserve this important property of
++ <constant>SIGKILL</constant>, it as a special case can break
++ <constant>UTRACE_STOP</constant> like nothing else normally can. This
++ includes both explicit <constant>SIGKILL</constant> signals and the
++ implicit <constant>SIGKILL</constant> sent to each other thread in the
++ same thread group by a thread doing an exec, or processing a fatal
++ signal, or making an <function>exit_group</function> system call. A
++ tracing engine can prevent a thread from beginning the exit or exec or
++ dying by signal (other than <constant>SIGKILL</constant>) if it is
++ attached to that thread, but once the operation begins, no tracing
++ engine can prevent or delay all other threads in the same thread group
++ dying.
++ </para>
++ </sect2>
++
++ <sect2 id="reap"><title>Final callbacks</title>
++ <para>
++ The <function>report_reap</function> callback is always the final event
++ in the life cycle of a traced thread. Tracing engines can use this as
++ the trigger to clean up their own data structures. The
++ <function>report_death</function> callback is always the penultimate
++ event a tracing engine might see; it's seen unless the thread was
++ already in the midst of dying when the engine attached. Many tracing
++ engines will have no interest in when a parent reaps a dead process,
++ and nothing they want to do with a zombie thread once it dies; for
++ them, the <function>report_death</function> callback is the natural
++ place to clean up data structures and detach. To facilitate writing
++ such engines robustly, given the asynchrony of
++ <constant>SIGKILL</constant>, and without error-prone manual
++ implementation of synchronization schemes, the
++ <application>utrace</application> infrastructure provides some special
++ guarantees about the <function>report_death</function> and
++ <function>report_reap</function> callbacks. It still takes some care
++ to be sure your tracing engine is robust to tear-down races, but these
++ rules make it reasonably straightforward and concise to handle a lot of
++ corner cases correctly.
++ </para>
++ </sect2>
++
++ <sect2 id="refcount"><title>Engine and task pointers</title>
++ <para>
++ The first sort of guarantee concerns the core data structures
++ themselves. <structname>struct utrace_engine</structname> is
++ a reference-counted data structure. While you hold a reference, an
++ engine pointer will always stay valid so that you can safely pass it to
++ any <application>utrace</application> call. Each call to
++ <function>utrace_attach_task</function> or
++ <function>utrace_attach_pid</function> returns an engine pointer with a
++ reference belonging to the caller. You own that reference until you
++ drop it using <function>utrace_engine_put</function>. There is an
++ implicit reference on the engine while it is attached. So if you drop
++ your only reference, and then use
++ <function>utrace_attach_task</function> without
++ <constant>UTRACE_ATTACH_CREATE</constant> to look up that same engine,
++ you will get the same pointer with a new reference to replace the one
++ you dropped, just like calling <function>utrace_engine_get</function>.
++ When an engine has been detached, either explicitly with
++ <constant>UTRACE_DETACH</constant> or implicitly after
++ <function>report_reap</function>, then any references you hold are all
++ that keep the old engine pointer alive.
++ </para>
++
++ <para>
++ There is nothing a kernel module can do to keep a <structname>struct
++ task_struct</structname> alive outside of
++ <function>rcu_read_lock</function>. When the task dies and is reaped
++ by its parent (or itself), that structure can be freed so that any
++ dangling pointers you have stored become invalid.
++ <application>utrace</application> will not prevent this, but it can
++ help you detect it safely. By definition, a task that has been reaped
++ has had all its engines detached. All
++ <application>utrace</application> calls can be safely called on a
++ detached engine if the caller holds a reference on that engine pointer,
++ even if the task pointer passed in the call is invalid. All calls
++ return <constant>-ESRCH</constant> for a detached engine, which tells
++ you that the task pointer you passed could be invalid now. Since
++ <function>utrace_control</function> and
++ <function>utrace_set_events</function> do not block, you can call those
++ inside a <function>rcu_read_lock</function> section and be sure after
++ they don't return <constant>-ESRCH</constant> that the task pointer is
++ still valid until <function>rcu_read_unlock</function>. The
++ infrastructure never holds task references of its own. Though neither
++ <function>rcu_read_lock</function> nor any other lock is held while
++ making a callback, it's always guaranteed that the <structname>struct
++ task_struct</structname> and the <structname>struct
++ utrace_engine</structname> passed as arguments remain valid
++ until the callback function returns.
++ </para>
++
++ <para>
++ The common means for safely holding task pointers that is available to
++ kernel modules is to use <structname>struct pid</structname>, which
++ permits <function>put_pid</function> from kernel modules. When using
++ that, the calls <function>utrace_attach_pid</function>,
++ <function>utrace_control_pid</function>,
++ <function>utrace_set_events_pid</function>, and
++ <function>utrace_barrier_pid</function> are available.
++ </para>
++ </sect2>
++
++ <sect2 id="reap-after-death">
++ <title>
++ Serialization of <constant>DEATH</constant> and <constant>REAP</constant>
++ </title>
++ <para>
++ The second guarantee is the serialization of
++ <constant>DEATH</constant> and <constant>REAP</constant> event
++ callbacks for a given thread. The actual reaping by the parent
++ (<function>release_task</function> call) can occur simultaneously
++ while the thread is still doing the final steps of dying, including
++ the <function>report_death</function> callback. If a tracing engine
++ has requested both <constant>DEATH</constant> and
++ <constant>REAP</constant> event reports, it's guaranteed that the
++ <function>report_reap</function> callback will not be made until
++ after the <function>report_death</function> callback has returned.
++ If the <function>report_death</function> callback itself detaches
++ from the thread, then the <function>report_reap</function> callback
++ will never be made. Thus it is safe for a
++ <function>report_death</function> callback to clean up data
++ structures and detach.
++ </para>
++ </sect2>
++
++ <sect2 id="interlock"><title>Interlock with final callbacks</title>
++ <para>
++ The final sort of guarantee is that a tracing engine will know for sure
++ whether or not the <function>report_death</function> and/or
++ <function>report_reap</function> callbacks will be made for a certain
++ thread. These tear-down races are disambiguated by the error return
++ values of <function>utrace_set_events</function> and
++ <function>utrace_control</function>. Normally
++ <function>utrace_control</function> called with
++ <constant>UTRACE_DETACH</constant> returns zero, and this means that no
++ more callbacks will be made. If the thread is in the midst of dying,
++ it returns <constant>-EALREADY</constant> to indicate that the
++ <constant>report_death</constant> callback may already be in progress;
++ when you get this error, you know that any cleanup your
++ <function>report_death</function> callback does is about to happen or
++ has just happened--note that if the <function>report_death</function>
++ callback does not detach, the engine remains attached until the thread
++ gets reaped. If the thread is in the midst of being reaped,
++ <function>utrace_control</function> returns <constant>-ESRCH</constant>
++ to indicate that the <function>report_reap</function> callback may
++ already be in progress; this means the engine is implicitly detached
++ when the callback completes. This makes it possible for a tracing
++ engine that has decided asynchronously to detach from a thread to
++ safely clean up its data structures, knowing that no
++ <function>report_death</function> or <function>report_reap</function>
++ callback will try to do the same. <constant>utrace_detach</constant>
++ returns <constant>-ESRCH</constant> when the <structname>struct
++ utrace_engine</structname> has already been detached, but is
++ still a valid pointer because of its reference count. A tracing engine
++ can use this to safely synchronize its own independent multiple threads
++ of control with each other and with its event callbacks that detach.
++ </para>
++
++ <para>
++ In the same vein, <function>utrace_set_events</function> normally
++ returns zero; if the target thread was stopped before the call, then
++ after a successful call, no event callbacks not requested in the new
++ flags will be made. It fails with <constant>-EALREADY</constant> if
++ you try to clear <constant>UTRACE_EVENT(DEATH)</constant> when the
++ <function>report_death</function> callback may already have begun, if
++ you try to clear <constant>UTRACE_EVENT(REAP)</constant> when the
++ <function>report_reap</function> callback may already have begun, or if
++ you try to newly set <constant>UTRACE_EVENT(DEATH)</constant> or
++ <constant>UTRACE_EVENT(QUIESCE)</constant> when the target is already
++ dead or dying. Like <function>utrace_control</function>, it returns
++ <constant>-ESRCH</constant> when the thread has already been detached
++ (including forcible detach on reaping). This lets the tracing engine
++ know for sure which event callbacks it will or won't see after
++ <function>utrace_set_events</function> has returned. By checking for
++ errors, it can know whether to clean up its data structures immediately
++ or to let its callbacks do the work.
++ </para>
++ </sect2>
++
++ <sect2 id="barrier"><title>Using <function>utrace_barrier</function></title>
++ <para>
++ When a thread is safely stopped, calling
++ <function>utrace_control</function> with <constant>UTRACE_DETACH</constant>
++ or calling <function>utrace_set_events</function> to disable some events
++ ensures synchronously that your engine won't get any more of the callbacks
++ that have been disabled (none at all when detaching). But these can also
++ be used while the thread is not stopped, when it might be simultaneously
++ making a callback to your engine. For this situation, these calls return
++ <constant>-EINPROGRESS</constant> when it's possible a callback is in
++ progress. If you are not prepared to have your old callbacks still run,
++ then you can synchronize to be sure all the old callbacks are finished,
++ using <function>utrace_barrier</function>. This is necessary if the
++ kernel module containing your callback code is going to be unloaded.
++ </para>
++ <para>
++ After using <constant>UTRACE_DETACH</constant> once, further calls to
++ <function>utrace_control</function> with the same engine pointer will
++ return <constant>-ESRCH</constant>. In contrast, after getting
++ <constant>-EINPROGRESS</constant> from
++ <function>utrace_set_events</function>, you can call
++ <function>utrace_set_events</function> again later and if it returns zero
++ then know the old callbacks have finished.
++ </para>
++ <para>
++ Unlike all other calls, <function>utrace_barrier</function> (and
++ <function>utrace_barrier_pid</function>) will accept any engine pointer you
++ hold a reference on, even if <constant>UTRACE_DETACH</constant> has already
++ been used. After any <function>utrace_control</function> or
++ <function>utrace_set_events</function> call (these do not block), you can
++ call <function>utrace_barrier</function> to block until callbacks have
++ finished. This returns <constant>-ESRCH</constant> only if the engine is
++ completely detached (finished all callbacks). Otherwise it waits
++ until the thread is definitely not in the midst of a callback to this
++ engine and then returns zero, but can return
++ <constant>-ERESTARTSYS</constant> if its wait is interrupted.
++ </para>
++ </sect2>
++
++</sect1>
++
++</chapter>
++
++<chapter id="core"><title>utrace core API</title>
++
++<para>
++ The utrace API is declared in <filename>&lt;linux/utrace.h&gt;</filename>.
++</para>
++
++!Iinclude/linux/utrace.h
++!Ekernel/utrace.c
++
++</chapter>
++
++<chapter id="machine"><title>Machine State</title>
++
++<para>
++ The <function>task_current_syscall</function> function can be used on any
++ valid <structname>struct task_struct</structname> at any time, and does
++ not even require that <function>utrace_attach_task</function> was used at all.
++</para>
++
++<para>
++ The other ways to access the registers and other machine-dependent state of
++ a task can only be used on a task that is at a known safe point. The safe
++ points are all the places where <function>utrace_set_events</function> can
++ request callbacks (except for the <constant>DEATH</constant> and
++ <constant>REAP</constant> events). So at any event callback, it is safe to
++ examine <varname>current</varname>.
++</para>
++
++<para>
++ One task can examine another only after a callback in the target task that
++ returns <constant>UTRACE_STOP</constant> so that task will not return to user
++ mode after the safe point. This guarantees that the task will not resume
++ until the same engine uses <function>utrace_control</function>, unless the
++ task dies suddenly. To examine safely, one must use a pair of calls to
++ <function>utrace_prepare_examine</function> and
++ <function>utrace_finish_examine</function> surrounding the calls to
++ <structname>struct user_regset</structname> functions or direct examination
++ of task data structures. <function>utrace_prepare_examine</function> returns
++ an error if the task is not properly stopped, or is dead. After a
++ successful examination, the paired <function>utrace_finish_examine</function>
++ call returns an error if the task ever woke up during the examination. If
++ so, any data gathered may be scrambled and should be discarded. This means
++ there was a spurious wake-up (which should not happen), or a sudden death.
++</para>
++
++<sect1 id="regset"><title><structname>struct user_regset</structname></title>
++
++<para>
++ The <structname>struct user_regset</structname> API
++ is declared in <filename>&lt;linux/regset.h&gt;</filename>.
++</para>
++
++!Finclude/linux/regset.h
++
++</sect1>
++
++<sect1 id="task_current_syscall">
++ <title><filename>System Call Information</filename></title>
++
++<para>
++ This function is declared in <filename>&lt;linux/ptrace.h&gt;</filename>.
++</para>
++
++!Elib/syscall.c
++
++</sect1>
++
++<sect1 id="syscall"><title><filename>System Call Tracing</filename></title>
++
++<para>
++ The arch API for system call information is declared in
++ <filename>&lt;asm/syscall.h&gt;</filename>.
++ Each of these calls can be used only at system call entry tracing,
++ or can be used only at system call exit and the subsequent safe points
++ before returning to user mode.
++ At system call entry tracing means either during a
++ <structfield>report_syscall_entry</structfield> callback,
++ or any time after that callback has returned <constant>UTRACE_STOP</constant>.
++</para>
++
++!Finclude/asm-generic/syscall.h
++
++</sect1>
++
++</chapter>
++
++<chapter id="internals"><title>Kernel Internals</title>
++
++<para>
++ This chapter covers the interface to the tracing infrastructure
++ from the core of the kernel and the architecture-specific code.
++ This is for maintainers of the kernel and arch code, and not relevant
++ to using the tracing facilities described in preceding chapters.
++</para>
++
++<sect1 id="tracehook"><title>Core Calls In</title>
++
++<para>
++ These calls are declared in <filename>&lt;linux/tracehook.h&gt;</filename>.
++ The core kernel calls these functions at various important places.
++</para>
++
++!Finclude/linux/tracehook.h
++
++</sect1>
++
++<sect1 id="arch"><title>Architecture Calls Out</title>
++
++<para>
++ An arch that has done all these things sets
++ <constant>CONFIG_HAVE_ARCH_TRACEHOOK</constant>.
++ This is required to enable the <application>utrace</application> code.
++</para>
++
++<sect2 id="arch-ptrace"><title><filename>&lt;asm/ptrace.h&gt;</filename></title>
++
++<para>
++ An arch defines these in <filename>&lt;asm/ptrace.h&gt;</filename>
++ if it supports hardware single-step or block-step features.
++</para>
++
++!Finclude/linux/ptrace.h arch_has_single_step arch_has_block_step
++!Finclude/linux/ptrace.h user_enable_single_step user_enable_block_step
++!Finclude/linux/ptrace.h user_disable_single_step
++
++</sect2>
++
++<sect2 id="arch-syscall">
++ <title><filename>&lt;asm/syscall.h&gt;</filename></title>
++
++ <para>
++ An arch provides <filename>&lt;asm/syscall.h&gt;</filename> that
++ defines these as inlines, or declares them as exported functions.
++ These interfaces are described in <xref linkend="syscall"/>.
++ </para>
++
++</sect2>
++
++<sect2 id="arch-tracehook">
++ <title><filename>&lt;linux/tracehook.h&gt;</filename></title>
++
++ <para>
++ An arch must define <constant>TIF_NOTIFY_RESUME</constant>
++ and <constant>TIF_SYSCALL_TRACE</constant>
++ in its <filename>&lt;asm/thread_info.h&gt;</filename>.
++ The arch code must call the following functions, all declared
++ in <filename>&lt;linux/tracehook.h&gt;</filename> and
++ described in <xref linkend="tracehook"/>:
++
++ <itemizedlist>
++ <listitem>
++ <para><function>tracehook_notify_resume</function></para>
++ </listitem>
++ <listitem>
++ <para><function>tracehook_report_syscall_entry</function></para>
++ </listitem>
++ <listitem>
++ <para><function>tracehook_report_syscall_exit</function></para>
++ </listitem>
++ <listitem>
++ <para><function>tracehook_signal_handler</function></para>
++ </listitem>
++ </itemizedlist>
++
++ </para>
++
++</sect2>
++
++</sect1>
++
++</chapter>
++
++</book>
+diff --git a/fs/proc/array.c b/fs/proc/array.c
+index 822c2d5..9069c91 100644
+--- a/fs/proc/array.c
++++ b/fs/proc/array.c
+@@ -82,6 +82,7 @@
+ #include <linux/pid_namespace.h>
+ #include <linux/ptrace.h>
+ #include <linux/tracehook.h>
++#include <linux/utrace.h>
+
+ #include <asm/pgtable.h>
+ #include <asm/processor.h>
+@@ -189,6 +190,8 @@ static inline void task_state(struct seq
+ cred->uid, cred->euid, cred->suid, cred->fsuid,
+ cred->gid, cred->egid, cred->sgid, cred->fsgid);
+
++ task_utrace_proc_status(m, p);
++
+ task_lock(p);
+ if (p->files)
+ fdt = files_fdtable(p->files);
+diff --git a/include/linux/sched.h b/include/linux/sched.h
+index 6c8928b..139d300 100644
+--- a/include/linux/sched.h
++++ b/include/linux/sched.h
+@@ -1393,6 +1393,11 @@ struct task_struct {
+ #endif
+ seccomp_t seccomp;
+
++#ifdef CONFIG_UTRACE
++ struct utrace *utrace;
++ unsigned long utrace_flags;
++#endif
++
+ /* Thread group tracking */
+ u32 parent_exec_id;
+ u32 self_exec_id;
+diff --git a/include/linux/tracehook.h b/include/linux/tracehook.h
+index c78b2f4..71fa250 100644
+--- a/include/linux/tracehook.h
++++ b/include/linux/tracehook.h
+@@ -49,6 +49,7 @@
+ #include <linux/sched.h>
+ #include <linux/ptrace.h>
+ #include <linux/security.h>
++#include <linux/utrace.h>
+ struct linux_binprm;
+
+ /**
+@@ -63,6 +64,8 @@ struct linux_binprm;
+ */
+ static inline int tracehook_expect_breakpoints(struct task_struct *task)
+ {
++ if (unlikely(task_utrace_flags(task) & UTRACE_EVENT(SIGNAL_CORE)))
++ return 1;
+ return (task_ptrace(task) & PT_PTRACED) != 0;
+ }
+
+@@ -111,6 +114,9 @@ static inline void ptrace_report_syscall
+ static inline __must_check int tracehook_report_syscall_entry(
+ struct pt_regs *regs)
+ {
++ if ((task_utrace_flags(current) & UTRACE_EVENT(SYSCALL_ENTRY)) &&
++ utrace_report_syscall_entry(regs))
++ return 1;
+ ptrace_report_syscall(regs);
+ return 0;
+ }
+@@ -134,6 +140,9 @@ static inline __must_check int tracehook
+ */
+ static inline void tracehook_report_syscall_exit(struct pt_regs *regs, int step)
+ {
++ if (task_utrace_flags(current) & UTRACE_EVENT(SYSCALL_EXIT))
++ utrace_report_syscall_exit(regs);
++
+ if (step && (task_ptrace(current) & PT_PTRACED)) {
+ siginfo_t info;
+ user_single_step_siginfo(current, regs, &info);
+@@ -201,6 +210,8 @@ static inline void tracehook_report_exec
+ struct linux_binprm *bprm,
+ struct pt_regs *regs)
+ {
++ if (unlikely(task_utrace_flags(current) & UTRACE_EVENT(EXEC)))
++ utrace_report_exec(fmt, bprm, regs);
+ if (!ptrace_event(PT_TRACE_EXEC, PTRACE_EVENT_EXEC, 0) &&
+ unlikely(task_ptrace(current) & PT_PTRACED))
+ send_sig(SIGTRAP, current, 0);
+@@ -218,10 +229,37 @@ static inline void tracehook_report_exec
+ */
+ static inline void tracehook_report_exit(long *exit_code)
+ {
++ if (unlikely(task_utrace_flags(current) & UTRACE_EVENT(EXIT)))
++ utrace_report_exit(exit_code);
+ ptrace_event(PT_TRACE_EXIT, PTRACE_EVENT_EXIT, *exit_code);
+ }
+
+ /**
++ * tracehook_init_task - task_struct has just been copied
++ * @task: new &struct task_struct just copied from parent
++ *
++ * Called from do_fork() when @task has just been duplicated.
++ * After this, @task will be passed to tracehook_free_task()
++ * even if the rest of its setup fails before it is fully created.
++ */
++static inline void tracehook_init_task(struct task_struct *task)
++{
++ utrace_init_task(task);
++}
++
++/**
++ * tracehook_free_task - task_struct is being freed
++ * @task: dead &struct task_struct being freed
++ *
++ * Called from free_task() when @task is no longer in use.
++ */
++static inline void tracehook_free_task(struct task_struct *task)
++{
++ if (task_utrace_struct(task))
++ utrace_free_task(task);
++}
++
++/**
+ * tracehook_prepare_clone - prepare for new child to be cloned
+ * @clone_flags: %CLONE_* flags from clone/fork/vfork system call
+ *
+@@ -285,6 +323,8 @@ static inline void tracehook_report_clon
+ unsigned long clone_flags,
+ pid_t pid, struct task_struct *child)
+ {
++ if (unlikely(task_utrace_flags(current) & UTRACE_EVENT(CLONE)))
++ utrace_report_clone(clone_flags, child);
+ if (unlikely(task_ptrace(child))) {
+ /*
+ * It doesn't matter who attached/attaching to this
+@@ -317,6 +357,9 @@ static inline void tracehook_report_clon
+ pid_t pid,
+ struct task_struct *child)
+ {
++ if (unlikely(task_utrace_flags(current) & UTRACE_EVENT(CLONE)) &&
++ (clone_flags & CLONE_VFORK))
++ utrace_finish_vfork(current);
+ if (unlikely(trace))
+ ptrace_event(0, trace, pid);
+ }
+@@ -351,6 +394,10 @@ static inline void tracehook_report_vfor
+ */
+ static inline void tracehook_prepare_release_task(struct task_struct *task)
+ {
++ /* see utrace_add_engine() about this barrier */
++ smp_mb();
++ if (task_utrace_flags(task))
++ utrace_maybe_reap(task, task_utrace_struct(task), true);
+ }
+
+ /**
+@@ -365,6 +412,7 @@ static inline void tracehook_prepare_rel
+ static inline void tracehook_finish_release_task(struct task_struct *task)
+ {
+ ptrace_release_task(task);
++ BUG_ON(task->exit_state != EXIT_DEAD);
+ }
+
+ /**
+@@ -386,6 +434,8 @@ static inline void tracehook_signal_hand
+ const struct k_sigaction *ka,
+ struct pt_regs *regs, int stepping)
+ {
++ if (task_utrace_flags(current))
++ utrace_signal_handler(current, stepping);
+ if (stepping && (task_ptrace(current) & PT_PTRACED))
+ ptrace_notify(SIGTRAP);
+ }
+@@ -403,6 +453,8 @@ static inline void tracehook_signal_hand
+ static inline int tracehook_consider_ignored_signal(struct task_struct *task,
+ int sig)
+ {
++ if (unlikely(task_utrace_flags(task) & UTRACE_EVENT(SIGNAL_IGN)))
++ return 1;
+ return (task_ptrace(task) & PT_PTRACED) != 0;
+ }
+
+@@ -422,6 +474,9 @@ static inline int tracehook_consider_ign
+ static inline int tracehook_consider_fatal_signal(struct task_struct *task,
+ int sig)
+ {
++ if (unlikely(task_utrace_flags(task) & (UTRACE_EVENT(SIGNAL_TERM) |
++ UTRACE_EVENT(SIGNAL_CORE))))
++ return 1;
+ return (task_ptrace(task) & PT_PTRACED) != 0;
+ }
+
+@@ -436,6 +491,8 @@ static inline int tracehook_consider_fat
+ */
+ static inline int tracehook_force_sigpending(void)
+ {
++ if (unlikely(task_utrace_flags(current)))
++ return utrace_interrupt_pending();
+ return 0;
+ }
+
+@@ -465,6 +522,8 @@ static inline int tracehook_get_signal(s
+ siginfo_t *info,
+ struct k_sigaction *return_ka)
+ {
++ if (unlikely(task_utrace_flags(task)))
++ return utrace_get_signal(task, regs, info, return_ka);
+ return 0;
+ }
+
+@@ -492,6 +551,8 @@ static inline int tracehook_get_signal(s
+ */
+ static inline int tracehook_notify_jctl(int notify, int why)
+ {
++ if (task_utrace_flags(current) & UTRACE_EVENT(JCTL))
++ utrace_report_jctl(notify, why);
+ return notify ?: task_ptrace(current) ? why : 0;
+ }
+
+@@ -502,6 +563,8 @@ static inline int tracehook_notify_jctl(
+ */
+ static inline void tracehook_finish_jctl(void)
+ {
++ if (task_utrace_flags(current))
++ utrace_finish_stop();
+ }
+
+ #define DEATH_REAP -1
+@@ -524,6 +587,8 @@ static inline void tracehook_finish_jctl
+ static inline int tracehook_notify_death(struct task_struct *task,
+ void **death_cookie, int group_dead)
+ {
++ *death_cookie = task_utrace_struct(task);
++
+ if (task_detached(task))
+ return task->ptrace ? SIGCHLD : DEATH_REAP;
+
+@@ -560,6 +625,15 @@ static inline void tracehook_report_deat
+ int signal, void *death_cookie,
+ int group_dead)
+ {
++ /*
++ * If utrace_set_events() was just called to enable
++ * UTRACE_EVENT(DEATH), then we are obliged to call
++ * utrace_report_death() and not miss it. utrace_set_events()
++ * checks @task->exit_state under tasklist_lock to synchronize
++ * with exit_notify(), the caller.
++ */
++ if (task_utrace_flags(task) & _UTRACE_DEATH_EVENTS)
++ utrace_report_death(task, death_cookie, group_dead, signal);
+ }
+
+ #ifdef TIF_NOTIFY_RESUME
+@@ -589,10 +663,21 @@ static inline void set_notify_resume(str
+ * asynchronously, this will be called again before we return to
+ * user mode.
+ *
+- * Called without locks.
++ * Called without locks. However, on some machines this may be
++ * called with interrupts disabled.
+ */
+ static inline void tracehook_notify_resume(struct pt_regs *regs)
+ {
++ struct task_struct *task = current;
++ /*
++ * Prevent the following store/load from getting ahead of the
++ * caller which clears TIF_NOTIFY_RESUME. This pairs with the
++ * implicit mb() before setting TIF_NOTIFY_RESUME in
++ * set_notify_resume().
++ */
++ smp_mb();
++ if (task_utrace_flags(task))
++ utrace_resume(task, regs);
+ }
+ #endif /* TIF_NOTIFY_RESUME */
+
+diff --git a/include/linux/utrace.h b/include/linux/utrace.h
+new file mode 100644
+index ...f251efe 100644
+--- /dev/null
++++ b/include/linux/utrace.h
+@@ -0,0 +1,692 @@
++/*
++ * utrace infrastructure interface for debugging user processes
++ *
++ * Copyright (C) 2006-2009 Red Hat, Inc. All rights reserved.
++ *
++ * This copyrighted material is made available to anyone wishing to use,
++ * modify, copy, or redistribute it subject to the terms and conditions
++ * of the GNU General Public License v.2.
++ *
++ * Red Hat Author: Roland McGrath.
++ *
++ * This interface allows for notification of interesting events in a
++ * thread. It also mediates access to thread state such as registers.
++ * Multiple unrelated users can be associated with a single thread.
++ * We call each of these a tracing engine.
++ *
++ * A tracing engine starts by calling utrace_attach_task() or
++ * utrace_attach_pid() on the chosen thread, passing in a set of hooks
++ * (&struct utrace_engine_ops), and some associated data. This produces a
++ * &struct utrace_engine, which is the handle used for all other
++ * operations. An attached engine has its ops vector, its data, and an
++ * event mask controlled by utrace_set_events().
++ *
++ * For each event bit that is set, that engine will get the
++ * appropriate ops->report_*() callback when the event occurs. The
++ * &struct utrace_engine_ops need not provide callbacks for an event
++ * unless the engine sets one of the associated event bits.
++ */
++
++#ifndef _LINUX_UTRACE_H
++#define _LINUX_UTRACE_H 1
++
++#include <linux/list.h>
++#include <linux/kref.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++
++struct linux_binprm;
++struct pt_regs;
++struct utrace;
++struct user_regset;
++struct user_regset_view;
++
++/*
++ * Event bits passed to utrace_set_events().
++ * These appear in &struct task_struct.@utrace_flags
++ * and &struct utrace_engine.@flags.
++ */
++enum utrace_events {
++ _UTRACE_EVENT_QUIESCE, /* Thread is available for examination. */
++ _UTRACE_EVENT_REAP, /* Zombie reaped, no more tracing possible. */
++ _UTRACE_EVENT_CLONE, /* Successful clone/fork/vfork just done. */
++ _UTRACE_EVENT_EXEC, /* Successful execve just completed. */
++ _UTRACE_EVENT_EXIT, /* Thread exit in progress. */
++ _UTRACE_EVENT_DEATH, /* Thread has died. */
++ _UTRACE_EVENT_SYSCALL_ENTRY, /* User entered kernel for system call. */
++ _UTRACE_EVENT_SYSCALL_EXIT, /* Returning to user after system call. */
++ _UTRACE_EVENT_SIGNAL, /* Signal delivery will run a user handler. */
++ _UTRACE_EVENT_SIGNAL_IGN, /* No-op signal to be delivered. */
++ _UTRACE_EVENT_SIGNAL_STOP, /* Signal delivery will suspend. */
++ _UTRACE_EVENT_SIGNAL_TERM, /* Signal delivery will terminate. */
++ _UTRACE_EVENT_SIGNAL_CORE, /* Signal delivery will dump core. */
++ _UTRACE_EVENT_JCTL, /* Job control stop or continue completed. */
++ _UTRACE_NEVENTS
++};
++#define UTRACE_EVENT(type) (1UL << _UTRACE_EVENT_##type)
++
++/*
++ * All the kinds of signal events.
++ * These all use the @report_signal() callback.
++ */
++#define UTRACE_EVENT_SIGNAL_ALL (UTRACE_EVENT(SIGNAL) \
++ | UTRACE_EVENT(SIGNAL_IGN) \
++ | UTRACE_EVENT(SIGNAL_STOP) \
++ | UTRACE_EVENT(SIGNAL_TERM) \
++ | UTRACE_EVENT(SIGNAL_CORE))
++/*
++ * Both kinds of syscall events; these call the @report_syscall_entry()
++ * and @report_syscall_exit() callbacks, respectively.
++ */
++#define UTRACE_EVENT_SYSCALL \
++ (UTRACE_EVENT(SYSCALL_ENTRY) | UTRACE_EVENT(SYSCALL_EXIT))
++
++/*
++ * The event reports triggered synchronously by task death.
++ */
++#define _UTRACE_DEATH_EVENTS (UTRACE_EVENT(DEATH) | UTRACE_EVENT(QUIESCE))
++
++/*
++ * Hooks in <linux/tracehook.h> call these entry points to the utrace dispatch.
++ */
++void utrace_free_task(struct task_struct *);
++bool utrace_interrupt_pending(void);
++void utrace_resume(struct task_struct *, struct pt_regs *);
++void utrace_finish_stop(void);
++void utrace_maybe_reap(struct task_struct *, struct utrace *, bool);
++int utrace_get_signal(struct task_struct *, struct pt_regs *,
++ siginfo_t *, struct k_sigaction *);
++void utrace_report_clone(unsigned long, struct task_struct *);
++void utrace_finish_vfork(struct task_struct *);
++void utrace_report_exit(long *exit_code);
++void utrace_report_death(struct task_struct *, struct utrace *, bool, int);
++void utrace_report_jctl(int notify, int type);
++void utrace_report_exec(struct linux_binfmt *, struct linux_binprm *,
++ struct pt_regs *regs);
++bool utrace_report_syscall_entry(struct pt_regs *);
++void utrace_report_syscall_exit(struct pt_regs *);
++void utrace_signal_handler(struct task_struct *, int);
++
++#ifndef CONFIG_UTRACE
++
++/*
++ * <linux/tracehook.h> uses these accessors to avoid #ifdef CONFIG_UTRACE.
++ */
++static inline unsigned long task_utrace_flags(struct task_struct *task)
++{
++ return 0;
++}
++static inline struct utrace *task_utrace_struct(struct task_struct *task)
++{
++ return NULL;
++}
++static inline void utrace_init_task(struct task_struct *child)
++{
++}
++
++static inline void task_utrace_proc_status(struct seq_file *m,
++ struct task_struct *p)
++{
++}
++
++#else /* CONFIG_UTRACE */
++
++static inline unsigned long task_utrace_flags(struct task_struct *task)
++{
++ return task->utrace_flags;
++}
++
++static inline struct utrace *task_utrace_struct(struct task_struct *task)
++{
++ struct utrace *utrace;
++
++ /*
++ * This barrier ensures that any prior load of task->utrace_flags
++ * is ordered before this load of task->utrace. We use those
++ * utrace_flags checks in the hot path to decide to call into
++ * the utrace code. The first attach installs task->utrace before
++ * setting task->utrace_flags nonzero with implicit barrier in
++ * between, see utrace_add_engine().
++ */
++ smp_rmb();
++ utrace = task->utrace;
++
++ smp_read_barrier_depends(); /* See utrace_task_alloc(). */
++ return utrace;
++}
++
++static inline void utrace_init_task(struct task_struct *task)
++{
++ task->utrace_flags = 0;
++ task->utrace = NULL;
++}
++
++void task_utrace_proc_status(struct seq_file *m, struct task_struct *p);
++
++
++/*
++ * Version number of the API defined in this file. This will change
++ * whenever a tracing engine's code would need some updates to keep
++ * working. We maintain this here for the benefit of tracing engine code
++ * that is developed concurrently with utrace API improvements before they
++ * are merged into the kernel, making LINUX_VERSION_CODE checks unwieldy.
++ */
++#define UTRACE_API_VERSION 20091216
++
++/**
++ * enum utrace_resume_action - engine's choice of action for a traced task
++ * @UTRACE_STOP: Stay quiescent after callbacks.
++ * @UTRACE_INTERRUPT: Make @report_signal() callback soon.
++ * @UTRACE_REPORT: Make some callback soon.
++ * @UTRACE_SINGLESTEP: Resume in user mode for one instruction.
++ * @UTRACE_BLOCKSTEP: Resume in user mode until next branch.
++ * @UTRACE_RESUME: Resume normally in user mode.
++ * @UTRACE_DETACH: Detach my engine (implies %UTRACE_RESUME).
++ *
++ * See utrace_control() for detailed descriptions of each action. This is
++ * encoded in the @action argument and the return value for every callback
++ * with a &u32 return value.
++ *
++ * The order of these is important. When there is more than one engine,
++ * each supplies its choice and the smallest value prevails.
++ */
++enum utrace_resume_action {
++ UTRACE_STOP,
++ UTRACE_INTERRUPT,
++ UTRACE_REPORT,
++ UTRACE_SINGLESTEP,
++ UTRACE_BLOCKSTEP,
++ UTRACE_RESUME,
++ UTRACE_DETACH,
++ UTRACE_RESUME_MAX
++};
++#define UTRACE_RESUME_BITS (ilog2(UTRACE_RESUME_MAX) + 1)
++#define UTRACE_RESUME_MASK ((1 << UTRACE_RESUME_BITS) - 1)
++
++/**
++ * utrace_resume_action - &enum utrace_resume_action from callback action
++ * @action: &u32 callback @action argument or return value
++ *
++ * This extracts the &enum utrace_resume_action from @action,
++ * which is the @action argument to a &struct utrace_engine_ops
++ * callback or the return value from one.
++ */
++static inline enum utrace_resume_action utrace_resume_action(u32 action)
++{
++ return action & UTRACE_RESUME_MASK;
++}
++
++/**
++ * enum utrace_signal_action - disposition of signal
++ * @UTRACE_SIGNAL_DELIVER: Deliver according to sigaction.
++ * @UTRACE_SIGNAL_IGN: Ignore the signal.
++ * @UTRACE_SIGNAL_TERM: Terminate the process.
++ * @UTRACE_SIGNAL_CORE: Terminate with core dump.
++ * @UTRACE_SIGNAL_STOP: Deliver as absolute stop.
++ * @UTRACE_SIGNAL_TSTP: Deliver as job control stop.
++ * @UTRACE_SIGNAL_REPORT: Reporting before pending signals.
++ * @UTRACE_SIGNAL_HANDLER: Reporting after signal handler setup.
++ *
++ * This is encoded in the @action argument and the return value for
++ * a @report_signal() callback. It says what will happen to the
++ * signal described by the &siginfo_t parameter to the callback.
++ *
++ * The %UTRACE_SIGNAL_REPORT value is used in an @action argument when
++ * a tracing report is being made before dequeuing any pending signal.
++ * If this is immediately after a signal handler has been set up, then
++ * %UTRACE_SIGNAL_HANDLER is used instead. A @report_signal callback
++ * that uses %UTRACE_SIGNAL_DELIVER|%UTRACE_SINGLESTEP will ensure
++ * it sees a %UTRACE_SIGNAL_HANDLER report.
++ */
++enum utrace_signal_action {
++ UTRACE_SIGNAL_DELIVER = 0x00,
++ UTRACE_SIGNAL_IGN = 0x10,
++ UTRACE_SIGNAL_TERM = 0x20,
++ UTRACE_SIGNAL_CORE = 0x30,
++ UTRACE_SIGNAL_STOP = 0x40,
++ UTRACE_SIGNAL_TSTP = 0x50,
++ UTRACE_SIGNAL_REPORT = 0x60,
++ UTRACE_SIGNAL_HANDLER = 0x70
++};
++#define UTRACE_SIGNAL_MASK 0xf0
++#define UTRACE_SIGNAL_HOLD 0x100 /* Flag, push signal back on queue. */
++
++/**
++ * utrace_signal_action - &enum utrace_signal_action from callback action
++ * @action: @report_signal callback @action argument or return value
++ *
++ * This extracts the &enum utrace_signal_action from @action, which
++ * is the @action argument to a @report_signal callback or the
++ * return value from one.
++ */
++static inline enum utrace_signal_action utrace_signal_action(u32 action)
++{
++ return action & UTRACE_SIGNAL_MASK;
++}
++
++/**
++ * enum utrace_syscall_action - disposition of system call attempt
++ * @UTRACE_SYSCALL_RUN: Run the system call.
++ * @UTRACE_SYSCALL_ABORT: Don't run the system call.
++ *
++ * This is encoded in the @action argument and the return value for
++ * a @report_syscall_entry callback.
++ */
++enum utrace_syscall_action {
++ UTRACE_SYSCALL_RUN = 0x00,
++ UTRACE_SYSCALL_ABORT = 0x10
++};
++#define UTRACE_SYSCALL_MASK 0xf0
++#define UTRACE_SYSCALL_RESUMED 0x100 /* Flag, report_syscall_entry() repeats */
++
++/**
++ * utrace_syscall_action - &enum utrace_syscall_action from callback action
++ * @action: @report_syscall_entry callback @action or return value
++ *
++ * This extracts the &enum utrace_syscall_action from @action, which
++ * is the @action argument to a @report_syscall_entry callback or the
++ * return value from one.
++ */
++static inline enum utrace_syscall_action utrace_syscall_action(u32 action)
++{
++ return action & UTRACE_SYSCALL_MASK;
++}
++
++/*
++ * Flags for utrace_attach_task() and utrace_attach_pid().
++ */
++#define UTRACE_ATTACH_MATCH_OPS 0x0001 /* Match engines on ops. */
++#define UTRACE_ATTACH_MATCH_DATA 0x0002 /* Match engines on data. */
++#define UTRACE_ATTACH_MATCH_MASK 0x000f
++#define UTRACE_ATTACH_CREATE 0x0010 /* Attach a new engine. */
++#define UTRACE_ATTACH_EXCLUSIVE 0x0020 /* Refuse if existing match. */
++
++/**
++ * struct utrace_engine - per-engine structure
++ * @ops: &struct utrace_engine_ops pointer passed to utrace_attach_task()
++ * @data: engine-private &void * passed to utrace_attach_task()
++ * @flags: event mask set by utrace_set_events() plus internal flag bits
++ *
++ * The task itself never has to worry about engines detaching while
++ * it's doing event callbacks. These structures are removed from the
++ * task's active list only when it's stopped, or by the task itself.
++ *
++ * utrace_engine_get() and utrace_engine_put() maintain a reference count.
++ * When it drops to zero, the structure is freed. One reference is held
++ * implicitly while the engine is attached to its task.
++ */
++struct utrace_engine {
++/* private: */
++ struct kref kref;
++ void (*release)(void *);
++ struct list_head entry;
++
++/* public: */
++ const struct utrace_engine_ops *ops;
++ void *data;
++
++ unsigned long flags;
++};
++
++/**
++ * utrace_engine_get - acquire a reference on a &struct utrace_engine
++ * @engine: &struct utrace_engine pointer
++ *
++ * You must hold a reference on @engine, and you get another.
++ */
++static inline void utrace_engine_get(struct utrace_engine *engine)
++{
++ kref_get(&engine->kref);
++}
++
++void __utrace_engine_release(struct kref *);
++
++/**
++ * utrace_engine_put - release a reference on a &struct utrace_engine
++ * @engine: &struct utrace_engine pointer
++ *
++ * You must hold a reference on @engine, and you lose that reference.
++ * If it was the last one, @engine becomes an invalid pointer.
++ */
++static inline void utrace_engine_put(struct utrace_engine *engine)
++{
++ kref_put(&engine->kref, __utrace_engine_release);
++}
++
++/**
++ * struct utrace_engine_ops - tracing engine callbacks
++ *
++ * Each @report_*() callback corresponds to an %UTRACE_EVENT(*) bit.
++ * utrace_set_events() calls on @engine choose which callbacks will
++ * be made to @engine from @task.
++ *
++ * Most callbacks take an @action argument, giving the resume action
++ * chosen by other tracing engines. All callbacks take an @engine
++ * argument. The @report_reap callback takes a @task argument that
++ * might or might not be @current. All other @report_* callbacks
++ * report an event in the @current task.
++ *
++ * For some calls, @action also includes bits specific to that event
++ * and utrace_resume_action() is used to extract the resume action.
++ * This shows what would happen if @engine wasn't there, or will if
++ * the callback's return value uses %UTRACE_RESUME. This always
++ * starts as %UTRACE_RESUME when no other tracing is being done on
++ * this task.
++ *
++ * All return values contain &enum utrace_resume_action bits. For
++ * some calls, other bits specific to that kind of event are added to
++ * the resume action bits with OR. These are the same bits used in
++ * the @action argument. The resume action returned by a callback
++ * does not override previous engines' choices, it only says what
++ * @engine wants done. What @current actually does is the action that's
++ * most constrained among the choices made by all attached engines.
++ * See utrace_control() for more information on the actions.
++ *
++ * When %UTRACE_STOP is used in @report_syscall_entry, then @current
++ * stops before attempting the system call. In this case, another
++ * @report_syscall_entry callback will follow after @current resumes if
++ * %UTRACE_REPORT or %UTRACE_INTERRUPT was returned by some callback
++ * or passed to utrace_control(). In a second or later callback,
++ * %UTRACE_SYSCALL_RESUMED is set in the @action argument to indicate
++ * a repeat callback still waiting to attempt the same system call
++ * invocation. This repeat callback gives each engine an opportunity
++ * to reexamine registers another engine might have changed while
++ * @current was held in %UTRACE_STOP.
++ *
++ * In other cases, the resume action does not take effect until @current
++ * is ready to check for signals and return to user mode. If there
++ * are more callbacks to be made, the last round of calls determines
++ * the final action. A @report_quiesce callback with @event zero, or
++ * a @report_signal callback, will always be the last one made before
++ * @current resumes. Only %UTRACE_STOP is "sticky"--if @engine returned
++ * %UTRACE_STOP then @current stays stopped unless @engine returns
++ * different from a following callback.
++ *
++ * The report_death() and report_reap() callbacks do not take @action
++ * arguments, and only %UTRACE_DETACH is meaningful in the return value
++ * from a report_death() callback. None of the resume actions applies
++ * to a dead thread.
++ *
++ * All @report_*() hooks are called with no locks held, in a generally
++ * safe environment when we will be returning to user mode soon (or just
++ * entered the kernel). It is fine to block for memory allocation and
++ * the like, but all hooks are asynchronous and must not block on
++ * external events! If you want the thread to block, use %UTRACE_STOP
++ * in your hook's return value; then later wake it up with utrace_control().
++ *
++ * @report_quiesce:
++ * Requested by %UTRACE_EVENT(%QUIESCE).
++ * This does not indicate any event, but just that @current is in a
++ * safe place for examination. This call is made before each specific
++ * event callback, except for @report_reap. The @event argument gives
++ * the %UTRACE_EVENT(@which) value for the event occurring. This
++ * callback might be made for events @engine has not requested, if
++ * some other engine is tracing the event; calling utrace_set_events()
++ * call here can request the immediate callback for this occurrence of
++ * @event. @event is zero when there is no other event, @current is
++ * now ready to check for signals and return to user mode, and some
++ * engine has used %UTRACE_REPORT or %UTRACE_INTERRUPT to request this
++ * callback. For this case, if @report_signal is not %NULL, the
++ * @report_quiesce callback may be replaced with a @report_signal
++ * callback passing %UTRACE_SIGNAL_REPORT in its @action argument,
++ * whenever @current is entering the signal-check path anyway.
++ *
++ * @report_signal:
++ * Requested by %UTRACE_EVENT(%SIGNAL_*) or %UTRACE_EVENT(%QUIESCE).
++ * Use utrace_signal_action() and utrace_resume_action() on @action.
++ * The signal action is %UTRACE_SIGNAL_REPORT when some engine has
++ * used %UTRACE_REPORT or %UTRACE_INTERRUPT; the callback can choose
++ * to stop or to deliver an artificial signal, before pending signals.
++ * It's %UTRACE_SIGNAL_HANDLER instead when signal handler setup just
++ * finished (after a previous %UTRACE_SIGNAL_DELIVER return); this
++ * serves in lieu of any %UTRACE_SIGNAL_REPORT callback requested by
++ * %UTRACE_REPORT or %UTRACE_INTERRUPT, and is also implicitly
++ * requested by %UTRACE_SINGLESTEP or %UTRACE_BLOCKSTEP into the
++ * signal delivery. The other signal actions indicate a signal about
++ * to be delivered; the previous engine's return value sets the signal
++ * action seen by the the following engine's callback. The @info data
++ * can be changed at will, including @info->si_signo. The settings in
++ * @return_ka determines what %UTRACE_SIGNAL_DELIVER does. @orig_ka
++ * is what was in force before other tracing engines intervened, and
++ * it's %NULL when this report began as %UTRACE_SIGNAL_REPORT or
++ * %UTRACE_SIGNAL_HANDLER. For a report without a new signal, @info
++ * is left uninitialized and must be set completely by an engine that
++ * chooses to deliver a signal; if there was a previous @report_signal
++ * callback ending in %UTRACE_STOP and it was just resumed using
++ * %UTRACE_REPORT or %UTRACE_INTERRUPT, then @info is left unchanged
++ * from the previous callback. In this way, the original signal can
++ * be left in @info while returning %UTRACE_STOP|%UTRACE_SIGNAL_IGN
++ * and then found again when resuming with %UTRACE_INTERRUPT.
++ * The %UTRACE_SIGNAL_HOLD flag bit can be OR'd into the return value,
++ * and might be in @action if the previous engine returned it. This
++ * flag asks that the signal in @info be pushed back on @current's queue
++ * so that it will be seen again after whatever action is taken now.
++ *
++ * @report_clone:
++ * Requested by %UTRACE_EVENT(%CLONE).
++ * Event reported for parent, before the new task @child might run.
++ * @clone_flags gives the flags used in the clone system call, or
++ * equivalent flags for a fork() or vfork() system call. This
++ * function can use utrace_attach_task() on @child. Then passing
++ * %UTRACE_STOP to utrace_control() on @child here keeps the child
++ * stopped before it ever runs in user mode, %UTRACE_REPORT or
++ * %UTRACE_INTERRUPT ensures a callback from @child before it
++ * starts in user mode.
++ *
++ * @report_jctl:
++ * Requested by %UTRACE_EVENT(%JCTL).
++ * Job control event; @type is %CLD_STOPPED or %CLD_CONTINUED,
++ * indicating whether we are stopping or resuming now. If @notify
++ * is nonzero, @current is the last thread to stop and so will send
++ * %SIGCHLD to its parent after this callback; @notify reflects
++ * what the parent's %SIGCHLD has in @si_code, which can sometimes
++ * be %CLD_STOPPED even when @type is %CLD_CONTINUED.
++ *
++ * @report_exec:
++ * Requested by %UTRACE_EVENT(%EXEC).
++ * An execve system call has succeeded and the new program is about to
++ * start running. The initial user register state is handy to be tweaked
++ * directly in @regs. @fmt and @bprm gives the details of this exec.
++ *
++ * @report_syscall_entry:
++ * Requested by %UTRACE_EVENT(%SYSCALL_ENTRY).
++ * Thread has entered the kernel to request a system call.
++ * The user register state is handy to be tweaked directly in @regs.
++ * The @action argument contains an &enum utrace_syscall_action,
++ * use utrace_syscall_action() to extract it. The return value
++ * overrides the last engine's action for the system call.
++ * If the final action is %UTRACE_SYSCALL_ABORT, no system call
++ * is made. The details of the system call being attempted can
++ * be fetched here with syscall_get_nr() and syscall_get_arguments().
++ * The parameter registers can be changed with syscall_set_arguments().
++ * See above about the %UTRACE_SYSCALL_RESUMED flag in @action.
++ * Use %UTRACE_REPORT in the return value to guarantee you get
++ * another callback (with %UTRACE_SYSCALL_RESUMED flag) in case
++ * @current stops with %UTRACE_STOP before attempting the system call.
++ *
++ * @report_syscall_exit:
++ * Requested by %UTRACE_EVENT(%SYSCALL_EXIT).
++ * Thread is about to leave the kernel after a system call request.
++ * The user register state is handy to be tweaked directly in @regs.
++ * The results of the system call attempt can be examined here using
++ * syscall_get_error() and syscall_get_return_value(). It is safe
++ * here to call syscall_set_return_value() or syscall_rollback().
++ *
++ * @report_exit:
++ * Requested by %UTRACE_EVENT(%EXIT).
++ * Thread is exiting and cannot be prevented from doing so,
++ * but all its state is still live. The @code value will be
++ * the wait result seen by the parent, and can be changed by
++ * this engine or others. The @orig_code value is the real
++ * status, not changed by any tracing engine. Returning %UTRACE_STOP
++ * here keeps @current stopped before it cleans up its state and dies,
++ * so it can be examined by other processes. When @current is allowed
++ * to run, it will die and get to the @report_death callback.
++ *
++ * @report_death:
++ * Requested by %UTRACE_EVENT(%DEATH).
++ * Thread is really dead now. It might be reaped by its parent at
++ * any time, or self-reap immediately. Though the actual reaping
++ * may happen in parallel, a report_reap() callback will always be
++ * ordered after a report_death() callback.
++ *
++ * @report_reap:
++ * Requested by %UTRACE_EVENT(%REAP).
++ * Called when someone reaps the dead task (parent, init, or self).
++ * This means the parent called wait, or else this was a detached
++ * thread or a process whose parent ignores SIGCHLD.
++ * No more callbacks are made after this one.
++ * The engine is always detached.
++ * There is nothing more a tracing engine can do about this thread.
++ * After this callback, the @engine pointer will become invalid.
++ * The @task pointer may become invalid if get_task_struct() hasn't
++ * been used to keep it alive.
++ * An engine should always request this callback if it stores the
++ * @engine pointer or stores any pointer in @engine->data, so it
++ * can clean up its data structures.
++ * Unlike other callbacks, this can be called from the parent's context
++ * rather than from the traced thread itself--it must not delay the
++ * parent by blocking.
++ *
++ * @release:
++ * If not %NULL, this is called after the last utrace_engine_put()
++ * call for a &struct utrace_engine, which could be implicit after
++ * a %UTRACE_DETACH return from another callback. Its argument is
++ * the engine's @data member.
++ */
++struct utrace_engine_ops {
++ u32 (*report_quiesce)(u32 action, struct utrace_engine *engine,
++ unsigned long event);
++ u32 (*report_signal)(u32 action, struct utrace_engine *engine,
++ struct pt_regs *regs,
++ siginfo_t *info,
++ const struct k_sigaction *orig_ka,
++ struct k_sigaction *return_ka);
++ u32 (*report_clone)(u32 action, struct utrace_engine *engine,
++ unsigned long clone_flags,
++ struct task_struct *child);
++ u32 (*report_jctl)(u32 action, struct utrace_engine *engine,
++ int type, int notify);
++ u32 (*report_exec)(u32 action, struct utrace_engine *engine,
++ const struct linux_binfmt *fmt,
++ const struct linux_binprm *bprm,
++ struct pt_regs *regs);
++ u32 (*report_syscall_entry)(u32 action, struct utrace_engine *engine,
++ struct pt_regs *regs);
++ u32 (*report_syscall_exit)(u32 action, struct utrace_engine *engine,
++ struct pt_regs *regs);
++ u32 (*report_exit)(u32 action, struct utrace_engine *engine,
++ long orig_code, long *code);
++ u32 (*report_death)(struct utrace_engine *engine,
++ bool group_dead, int signal);
++ void (*report_reap)(struct utrace_engine *engine,
++ struct task_struct *task);
++ void (*release)(void *data);
++};
++
++/**
++ * struct utrace_examiner - private state for using utrace_prepare_examine()
++ *
++ * The members of &struct utrace_examiner are private to the implementation.
++ * This data type holds the state from a call to utrace_prepare_examine()
++ * to be used by a call to utrace_finish_examine().
++ */
++struct utrace_examiner {
++/* private: */
++ long state;
++ unsigned long ncsw;
++};
++
++/*
++ * These are the exported entry points for tracing engines to use.
++ * See kernel/utrace.c for their kerneldoc comments with interface details.
++ */
++struct utrace_engine *utrace_attach_task(struct task_struct *, int,
++ const struct utrace_engine_ops *,
++ void *);
++struct utrace_engine *utrace_attach_pid(struct pid *, int,
++ const struct utrace_engine_ops *,
++ void *);
++int __must_check utrace_control(struct task_struct *,
++ struct utrace_engine *,
++ enum utrace_resume_action);
++int __must_check utrace_set_events(struct task_struct *,
++ struct utrace_engine *,
++ unsigned long eventmask);
++int __must_check utrace_barrier(struct task_struct *,
++ struct utrace_engine *);
++int __must_check utrace_prepare_examine(struct task_struct *,
++ struct utrace_engine *,
++ struct utrace_examiner *);
++int __must_check utrace_finish_examine(struct task_struct *,
++ struct utrace_engine *,
++ struct utrace_examiner *);
++
++/**
++ * utrace_control_pid - control a thread being traced by a tracing engine
++ * @pid: thread to affect
++ * @engine: attached engine to affect
++ * @action: &enum utrace_resume_action for thread to do
++ *
++ * This is the same as utrace_control(), but takes a &struct pid
++ * pointer rather than a &struct task_struct pointer. The caller must
++ * hold a ref on @pid, but does not need to worry about the task
++ * staying valid. If it's been reaped so that @pid points nowhere,
++ * then this call returns -%ESRCH.
++ */
++static inline __must_check int utrace_control_pid(
++ struct pid *pid, struct utrace_engine *engine,
++ enum utrace_resume_action action)
++{
++ /*
++ * We don't bother with rcu_read_lock() here to protect the
++ * task_struct pointer, because utrace_control will return
++ * -ESRCH without looking at that pointer if the engine is
++ * already detached. A task_struct pointer can't die before
++ * all the engines are detached in release_task() first.
++ */
++ struct task_struct *task = pid_task(pid, PIDTYPE_PID);
++ return unlikely(!task) ? -ESRCH : utrace_control(task, engine, action);
++}
++
++/**
++ * utrace_set_events_pid - choose which event reports a tracing engine gets
++ * @pid: thread to affect
++ * @engine: attached engine to affect
++ * @eventmask: new event mask
++ *
++ * This is the same as utrace_set_events(), but takes a &struct pid
++ * pointer rather than a &struct task_struct pointer. The caller must
++ * hold a ref on @pid, but does not need to worry about the task
++ * staying valid. If it's been reaped so that @pid points nowhere,
++ * then this call returns -%ESRCH.
++ */
++static inline __must_check int utrace_set_events_pid(
++ struct pid *pid, struct utrace_engine *engine, unsigned long eventmask)
++{
++ struct task_struct *task = pid_task(pid, PIDTYPE_PID);
++ return unlikely(!task) ? -ESRCH :
++ utrace_set_events(task, engine, eventmask);
++}
++
++/**
++ * utrace_barrier_pid - synchronize with simultaneous tracing callbacks
++ * @pid: thread to affect
++ * @engine: engine to affect (can be detached)
++ *
++ * This is the same as utrace_barrier(), but takes a &struct pid
++ * pointer rather than a &struct task_struct pointer. The caller must
++ * hold a ref on @pid, but does not need to worry about the task
++ * staying valid. If it's been reaped so that @pid points nowhere,
++ * then this call returns -%ESRCH.
++ */
++static inline __must_check int utrace_barrier_pid(struct pid *pid,
++ struct utrace_engine *engine)
++{
++ struct task_struct *task = pid_task(pid, PIDTYPE_PID);
++ return unlikely(!task) ? -ESRCH : utrace_barrier(task, engine);
++}
++
++#endif /* CONFIG_UTRACE */
++
++#endif /* linux/utrace.h */
+diff --git a/init/Kconfig b/init/Kconfig
+index eb4b337..140e636 100644
+--- a/init/Kconfig
++++ b/init/Kconfig
+@@ -310,6 +310,15 @@ config AUDIT_TREE
+ depends on AUDITSYSCALL
+ select INOTIFY
+
++config UTRACE
++ bool "Infrastructure for tracing and debugging user processes"
++ depends on EXPERIMENTAL
++ depends on HAVE_ARCH_TRACEHOOK
++ help
++ Enable the utrace process tracing interface. This is an internal
++ kernel interface exported to kernel modules, to track events in
++ user threads, extract and change user thread state.
++
+ menu "RCU Subsystem"
+
+ choice
+diff --git a/kernel/Makefile b/kernel/Makefile
+index d7c13d2..263bb19 100644
+--- a/kernel/Makefile
++++ b/kernel/Makefile
+@@ -68,6 +68,7 @@ obj-$(CONFIG_IKCONFIG) += configs.o
+ obj-$(CONFIG_RESOURCE_COUNTERS) += res_counter.o
+ obj-$(CONFIG_STOP_MACHINE) += stop_machine.o
+ obj-$(CONFIG_KPROBES_SANITY_TEST) += test_kprobes.o
++obj-$(CONFIG_UTRACE) += utrace.o
+ obj-$(CONFIG_AUDIT) += audit.o auditfilter.o audit_watch.o
+ obj-$(CONFIG_AUDITSYSCALL) += auditsc.o
+ obj-$(CONFIG_GCOV_KERNEL) += gcov/
+diff --git a/kernel/fork.c b/kernel/fork.c
+index 166b8c4..3ac952e 100644
+--- a/kernel/fork.c
++++ b/kernel/fork.c
+@@ -152,6 +152,7 @@ void free_task(struct task_struct *tsk)
+ free_thread_info(tsk->stack);
+ rt_mutex_debug_task_free(tsk);
+ ftrace_graph_exit_task(tsk);
++ tracehook_free_task(tsk);
+ free_task_struct(tsk);
+ }
+ EXPORT_SYMBOL(free_task);
+@@ -1018,6 +1019,8 @@ static struct task_struct *copy_process(
+ if (!p)
+ goto fork_out;
+
++ tracehook_init_task(p);
++
+ ftrace_graph_init_task(p);
+
+ rt_mutex_init_task(p);
+diff --git a/kernel/ptrace.c b/kernel/ptrace.c
+index b7c1d32..a408bf7 100644
+--- a/kernel/ptrace.c
++++ b/kernel/ptrace.c
+@@ -16,6 +16,7 @@
+ #include <linux/pagemap.h>
+ #include <linux/smp_lock.h>
+ #include <linux/ptrace.h>
++#include <linux/utrace.h>
+ #include <linux/security.h>
+ #include <linux/signal.h>
+ #include <linux/audit.h>
+@@ -164,6 +165,14 @@ bool ptrace_may_access(struct task_struc
+ return !err;
+ }
+
++/*
++ * For experimental use of utrace, exclude ptrace on the same task.
++ */
++static inline bool exclude_ptrace(struct task_struct *task)
++{
++ return unlikely(!!task_utrace_flags(task));
++}
++
+ int ptrace_attach(struct task_struct *task)
+ {
+ int retval;
+@@ -187,6 +196,8 @@ int ptrace_attach(struct task_struct *ta
+
+ task_lock(task);
+ retval = __ptrace_may_access(task, PTRACE_MODE_ATTACH);
++ if (!retval && exclude_ptrace(task))
++ retval = -EBUSY;
+ task_unlock(task);
+ if (retval)
+ goto unlock_creds;
+@@ -224,6 +235,9 @@ int ptrace_traceme(void)
+ {
+ int ret = -EPERM;
+
++ if (exclude_ptrace(current)) /* XXX locking */
++ return -EBUSY;
++
+ write_lock_irq(&tasklist_lock);
+ /* Are we already being traced? */
+ if (!current->ptrace) {
+diff --git a/kernel/utrace.c b/kernel/utrace.c
+new file mode 100644
+index ...f003e34 100644
+--- /dev/null
++++ b/kernel/utrace.c
+@@ -0,0 +1,2436 @@
++/*
++ * utrace infrastructure interface for debugging user processes
++ *
++ * Copyright (C) 2006-2010 Red Hat, Inc. All rights reserved.
++ *
++ * This copyrighted material is made available to anyone wishing to use,
++ * modify, copy, or redistribute it subject to the terms and conditions
++ * of the GNU General Public License v.2.
++ *
++ * Red Hat Author: Roland McGrath.
++ */
++
++#include <linux/utrace.h>
++#include <linux/tracehook.h>
++#include <linux/regset.h>
++#include <asm/syscall.h>
++#include <linux/ptrace.h>
++#include <linux/err.h>
++#include <linux/sched.h>
++#include <linux/freezer.h>
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/seq_file.h>
++
++
++/*
++ * Per-thread structure private to utrace implementation.
++ * If task_struct.utrace_flags is nonzero, task_struct.utrace
++ * has always been allocated first. Once allocated, it is
++ * never freed until free_task().
++ *
++ * The common event reporting loops are done by the task making the
++ * report without ever taking any locks. To facilitate this, the two
++ * lists @attached and @attaching work together for smooth asynchronous
++ * attaching with low overhead. Modifying either list requires @lock.
++ * The @attaching list can be modified any time while holding @lock.
++ * New engines being attached always go on this list.
++ *
++ * The @attached list is what the task itself uses for its reporting
++ * loops. When the task itself is not quiescent, it can use the
++ * @attached list without taking any lock. Nobody may modify the list
++ * when the task is not quiescent. When it is quiescent, that means
++ * that it won't run again without taking @lock itself before using
++ * the list.
++ *
++ * At each place where we know the task is quiescent (or it's current),
++ * while holding @lock, we call splice_attaching(), below. This moves
++ * the @attaching list members on to the end of the @attached list.
++ * Since this happens at the start of any reporting pass, any new
++ * engines attached asynchronously go on the stable @attached list
++ * in time to have their callbacks seen.
++ */
++struct utrace {
++ spinlock_t lock;
++ struct list_head attached, attaching;
++
++ struct task_struct *cloning;
++
++ struct utrace_engine *reporting;
++
++ enum utrace_resume_action resume:UTRACE_RESUME_BITS;
++ unsigned int signal_handler:1;
++ unsigned int vfork_stop:1; /* need utrace_stop() before vfork wait */
++ unsigned int death:1; /* in utrace_report_death() now */
++ unsigned int reap:1; /* release_task() has run */
++ unsigned int pending_attach:1; /* need splice_attaching() */
++};
++
++static struct kmem_cache *utrace_cachep;
++static struct kmem_cache *utrace_engine_cachep;
++static const struct utrace_engine_ops utrace_detached_ops; /* forward decl */
++
++static int __init utrace_init(void)
++{
++ utrace_cachep = KMEM_CACHE(utrace, SLAB_PANIC);
++ utrace_engine_cachep = KMEM_CACHE(utrace_engine, SLAB_PANIC);
++ return 0;
++}
++module_init(utrace_init);
++
++/*
++ * Set up @task.utrace for the first time. We can have races
++ * between two utrace_attach_task() calls here. The task_lock()
++ * governs installing the new pointer. If another one got in first,
++ * we just punt the new one we allocated.
++ *
++ * This returns false only in case of a memory allocation failure.
++ */
++static bool utrace_task_alloc(struct task_struct *task)
++{
++ struct utrace *utrace = kmem_cache_zalloc(utrace_cachep, GFP_KERNEL);
++ if (unlikely(!utrace))
++ return false;
++ spin_lock_init(&utrace->lock);
++ INIT_LIST_HEAD(&utrace->attached);
++ INIT_LIST_HEAD(&utrace->attaching);
++ utrace->resume = UTRACE_RESUME;
++ task_lock(task);
++ if (likely(!task->utrace)) {
++ /*
++ * This barrier makes sure the initialization of the struct
++ * precedes the installation of the pointer. This pairs
++ * with smp_read_barrier_depends() in task_utrace_struct().
++ */
++ smp_wmb();
++ task->utrace = utrace;
++ }
++ task_unlock(task);
++
++ if (unlikely(task->utrace != utrace))
++ kmem_cache_free(utrace_cachep, utrace);
++ return true;
++}
++
++/*
++ * This is called via tracehook_free_task() from free_task()
++ * when @task is being deallocated.
++ */
++void utrace_free_task(struct task_struct *task)
++{
++ kmem_cache_free(utrace_cachep, task->utrace);
++}
++
++/*
++ * This is calledwhen the task is safely quiescent, i.e. it won't consult
++ * utrace->attached without the lock. Move any engines attached
++ * asynchronously from @utrace->attaching onto the @utrace->attached list.
++ */
++static void splice_attaching(struct utrace *utrace)
++{
++ lockdep_assert_held(&utrace->lock);
++ list_splice_tail_init(&utrace->attaching, &utrace->attached);
++ utrace->pending_attach = 0;
++}
++
++/*
++ * This is the exported function used by the utrace_engine_put() inline.
++ */
++void __utrace_engine_release(struct kref *kref)
++{
++ struct utrace_engine *engine = container_of(kref, struct utrace_engine,
++ kref);
++ BUG_ON(!list_empty(&engine->entry));
++ if (engine->release)
++ (*engine->release)(engine->data);
++ kmem_cache_free(utrace_engine_cachep, engine);
++}
++EXPORT_SYMBOL_GPL(__utrace_engine_release);
++
++static bool engine_matches(struct utrace_engine *engine, int flags,
++ const struct utrace_engine_ops *ops, void *data)
++{
++ if ((flags & UTRACE_ATTACH_MATCH_OPS) && engine->ops != ops)
++ return false;
++ if ((flags & UTRACE_ATTACH_MATCH_DATA) && engine->data != data)
++ return false;
++ return engine->ops && engine->ops != &utrace_detached_ops;
++}
++
++static struct utrace_engine *find_matching_engine(
++ struct utrace *utrace, int flags,
++ const struct utrace_engine_ops *ops, void *data)
++{
++ struct utrace_engine *engine;
++ list_for_each_entry(engine, &utrace->attached, entry)
++ if (engine_matches(engine, flags, ops, data))
++ return engine;
++ list_for_each_entry(engine, &utrace->attaching, entry)
++ if (engine_matches(engine, flags, ops, data))
++ return engine;
++ return NULL;
++}
++
++/*
++ * Enqueue @engine, or maybe don't if UTRACE_ATTACH_EXCLUSIVE.
++ */
++static int utrace_add_engine(struct task_struct *target,
++ struct utrace *utrace,
++ struct utrace_engine *engine,
++ int flags,
++ const struct utrace_engine_ops *ops,
++ void *data)
++{
++ int ret;
++
++ spin_lock(&utrace->lock);
++
++ ret = -EEXIST;
++ if ((flags & UTRACE_ATTACH_EXCLUSIVE) &&
++ unlikely(find_matching_engine(utrace, flags, ops, data)))
++ goto unlock;
++
++ /*
++ * In case we had no engines before, make sure that
++ * utrace_flags is not zero. Since we did unlock+lock
++ * at least once after utrace_task_alloc() installed
++ * ->utrace, we have the necessary barrier which pairs
++ * with rmb() in task_utrace_struct().
++ */
++ ret = -ESRCH;
++ if (!target->utrace_flags) {
++ target->utrace_flags = UTRACE_EVENT(REAP);
++ /*
++ * If we race with tracehook_prepare_release_task()
++ * make sure that either it sees utrace_flags != 0
++ * or we see exit_state == EXIT_DEAD.
++ */
++ smp_mb();
++ if (unlikely(target->exit_state == EXIT_DEAD)) {
++ target->utrace_flags = 0;
++ goto unlock;
++ }
++ }
++
++ /*
++ * Put the new engine on the pending ->attaching list.
++ * Make sure it gets onto the ->attached list by the next
++ * time it's examined. Setting ->pending_attach ensures
++ * that start_report() takes the lock and splices the lists
++ * before the next new reporting pass.
++ *
++ * When target == current, it would be safe just to call
++ * splice_attaching() right here. But if we're inside a
++ * callback, that would mean the new engine also gets
++ * notified about the event that precipitated its own
++ * creation. This is not what the user wants.
++ */
++ list_add_tail(&engine->entry, &utrace->attaching);
++ utrace->pending_attach = 1;
++ ret = 0;
++unlock:
++ spin_unlock(&utrace->lock);
++
++ return ret;
++}
++
++/**
++ * utrace_attach_task - attach new engine, or look up an attached engine
++ * @target: thread to attach to
++ * @flags: flag bits combined with OR, see below
++ * @ops: callback table for new engine
++ * @data: engine private data pointer
++ *
++ * The caller must ensure that the @target thread does not get freed,
++ * i.e. hold a ref or be its parent. It is always safe to call this
++ * on @current, or on the @child pointer in a @report_clone callback.
++ * For most other cases, it's easier to use utrace_attach_pid() instead.
++ *
++ * UTRACE_ATTACH_CREATE:
++ * Create a new engine. If %UTRACE_ATTACH_CREATE is not specified, you
++ * only look up an existing engine already attached to the thread.
++ *
++ * UTRACE_ATTACH_EXCLUSIVE:
++ * Attempting to attach a second (matching) engine fails with -%EEXIST.
++ *
++ * UTRACE_ATTACH_MATCH_OPS: Only consider engines matching @ops.
++ * UTRACE_ATTACH_MATCH_DATA: Only consider engines matching @data.
++ *
++ * Calls with neither %UTRACE_ATTACH_MATCH_OPS nor %UTRACE_ATTACH_MATCH_DATA
++ * match the first among any engines attached to @target. That means that
++ * %UTRACE_ATTACH_EXCLUSIVE in such a call fails with -%EEXIST if there
++ * are any engines on @target at all.
++ */
++struct utrace_engine *utrace_attach_task(
++ struct task_struct *target, int flags,
++ const struct utrace_engine_ops *ops, void *data)
++{
++ struct utrace *utrace = task_utrace_struct(target);
++ struct utrace_engine *engine;
++ int ret;
++
++ if (!(flags & UTRACE_ATTACH_CREATE)) {
++ if (unlikely(!utrace))
++ return ERR_PTR(-ENOENT);
++ spin_lock(&utrace->lock);
++ engine = find_matching_engine(utrace, flags, ops, data);
++ if (engine)
++ utrace_engine_get(engine);
++ spin_unlock(&utrace->lock);
++ return engine ?: ERR_PTR(-ENOENT);
++ }
++
++ if (unlikely(!ops) || unlikely(ops == &utrace_detached_ops))
++ return ERR_PTR(-EINVAL);
++
++ if (unlikely(target->flags & PF_KTHREAD))
++ /*
++ * Silly kernel, utrace is for users!
++ */
++ return ERR_PTR(-EPERM);
++
++ if (!utrace) {
++ if (unlikely(!utrace_task_alloc(target)))
++ return ERR_PTR(-ENOMEM);
++ utrace = task_utrace_struct(target);
++ }
++
++ engine = kmem_cache_alloc(utrace_engine_cachep, GFP_KERNEL);
++ if (unlikely(!engine))
++ return ERR_PTR(-ENOMEM);
++
++ /*
++ * Initialize the new engine structure. It starts out with two
++ * refs: one ref to return, and one ref for being attached.
++ */
++ kref_set(&engine->kref, 2);
++ engine->flags = 0;
++ engine->ops = ops;
++ engine->data = data;
++ engine->release = ops->release;
++
++ ret = utrace_add_engine(target, utrace, engine, flags, ops, data);
++
++ if (unlikely(ret)) {
++ kmem_cache_free(utrace_engine_cachep, engine);
++ engine = ERR_PTR(ret);
++ }
++
++ return engine;
++}
++EXPORT_SYMBOL_GPL(utrace_attach_task);
++
++/**
++ * utrace_attach_pid - attach new engine, or look up an attached engine
++ * @pid: &struct pid pointer representing thread to attach to
++ * @flags: flag bits combined with OR, see utrace_attach_task()
++ * @ops: callback table for new engine
++ * @data: engine private data pointer
++ *
++ * This is the same as utrace_attach_task(), but takes a &struct pid
++ * pointer rather than a &struct task_struct pointer. The caller must
++ * hold a ref on @pid, but does not need to worry about the task
++ * staying valid. If it's been reaped so that @pid points nowhere,
++ * then this call returns -%ESRCH.
++ */
++struct utrace_engine *utrace_attach_pid(
++ struct pid *pid, int flags,
++ const struct utrace_engine_ops *ops, void *data)
++{
++ struct utrace_engine *engine = ERR_PTR(-ESRCH);
++ struct task_struct *task = get_pid_task(pid, PIDTYPE_PID);
++ if (task) {
++ engine = utrace_attach_task(task, flags, ops, data);
++ put_task_struct(task);
++ }
++ return engine;
++}
++EXPORT_SYMBOL_GPL(utrace_attach_pid);
++
++/*
++ * When an engine is detached, the target thread may still see it and
++ * make callbacks until it quiesces. We install a special ops vector
++ * with these two callbacks. When the target thread quiesces, it can
++ * safely free the engine itself. For any event we will always get
++ * the report_quiesce() callback first, so we only need this one
++ * pointer to be set. The only exception is report_reap(), so we
++ * supply that callback too.
++ */
++static u32 utrace_detached_quiesce(u32 action, struct utrace_engine *engine,
++ unsigned long event)
++{
++ return UTRACE_DETACH;
++}
++
++static void utrace_detached_reap(struct utrace_engine *engine,
++ struct task_struct *task)
++{
++}
++
++static const struct utrace_engine_ops utrace_detached_ops = {
++ .report_quiesce = &utrace_detached_quiesce,
++ .report_reap = &utrace_detached_reap
++};
++
++/*
++ * The caller has to hold a ref on the engine. If the attached flag is
++ * true (all but utrace_barrier() calls), the engine is supposed to be
++ * attached. If the attached flag is false (utrace_barrier() only),
++ * then return -ERESTARTSYS for an engine marked for detach but not yet
++ * fully detached. The task pointer can be invalid if the engine is
++ * detached.
++ *
++ * Get the utrace lock for the target task.
++ * Returns the struct if locked, or ERR_PTR(-errno).
++ *
++ * This has to be robust against races with:
++ * utrace_control(target, UTRACE_DETACH) calls
++ * UTRACE_DETACH after reports
++ * utrace_report_death
++ * utrace_release_task
++ */
++static struct utrace *get_utrace_lock(struct task_struct *target,
++ struct utrace_engine *engine,
++ bool attached)
++ __acquires(utrace->lock)
++{
++ struct utrace *utrace;
++
++ rcu_read_lock();
++
++ /*
++ * If this engine was already detached, bail out before we look at
++ * the task_struct pointer at all. If it's detached after this
++ * check, then RCU is still keeping this task_struct pointer valid.
++ *
++ * The ops pointer is NULL when the engine is fully detached.
++ * It's &utrace_detached_ops when it's marked detached but still
++ * on the list. In the latter case, utrace_barrier() still works,
++ * since the target might be in the middle of an old callback.
++ */
++ if (unlikely(!engine->ops)) {
++ rcu_read_unlock();
++ return ERR_PTR(-ESRCH);
++ }
++
++ if (unlikely(engine->ops == &utrace_detached_ops)) {
++ rcu_read_unlock();
++ return attached ? ERR_PTR(-ESRCH) : ERR_PTR(-ERESTARTSYS);
++ }
++
++ utrace = task_utrace_struct(target);
++ spin_lock(&utrace->lock);
++ if (unlikely(!engine->ops) ||
++ unlikely(engine->ops == &utrace_detached_ops)) {
++ /*
++ * By the time we got the utrace lock,
++ * it had been reaped or detached already.
++ */
++ spin_unlock(&utrace->lock);
++ utrace = ERR_PTR(-ESRCH);
++ if (!attached && engine->ops == &utrace_detached_ops)
++ utrace = ERR_PTR(-ERESTARTSYS);
++ }
++ rcu_read_unlock();
++
++ return utrace;
++}
++
++/*
++ * Now that we don't hold any locks, run through any
++ * detached engines and free their references. Each
++ * engine had one implicit ref while it was attached.
++ */
++static void put_detached_list(struct list_head *list)
++{
++ struct utrace_engine *engine, *next;
++ list_for_each_entry_safe(engine, next, list, entry) {
++ list_del_init(&engine->entry);
++ utrace_engine_put(engine);
++ }
++}
++
++/*
++ * We use an extra bit in utrace_engine.flags past the event bits,
++ * to record whether the engine is keeping the target thread stopped.
++ *
++ * This bit is set in task_struct.utrace_flags whenever it is set in any
++ * engine's flags. Only utrace_reset() resets it in utrace_flags.
++ */
++#define ENGINE_STOP (1UL << _UTRACE_NEVENTS)
++
++static void mark_engine_wants_stop(struct task_struct *task,
++ struct utrace_engine *engine)
++{
++ engine->flags |= ENGINE_STOP;
++ task->utrace_flags |= ENGINE_STOP;
++}
++
++static void clear_engine_wants_stop(struct utrace_engine *engine)
++{
++ engine->flags &= ~ENGINE_STOP;
++}
++
++static bool engine_wants_stop(struct utrace_engine *engine)
++{
++ return (engine->flags & ENGINE_STOP) != 0;
++}
++
++/**
++ * utrace_set_events - choose which event reports a tracing engine gets
++ * @target: thread to affect
++ * @engine: attached engine to affect
++ * @events: new event mask
++ *
++ * This changes the set of events for which @engine wants callbacks made.
++ *
++ * This fails with -%EALREADY and does nothing if you try to clear
++ * %UTRACE_EVENT(%DEATH) when the @report_death callback may already have
++ * begun, if you try to clear %UTRACE_EVENT(%REAP) when the @report_reap
++ * callback may already have begun, or if you try to newly set
++ * %UTRACE_EVENT(%DEATH) or %UTRACE_EVENT(%QUIESCE) when @target is
++ * already dead or dying.
++ *
++ * This can fail with -%ESRCH when @target has already been detached,
++ * including forcible detach on reaping.
++ *
++ * If @target was stopped before the call, then after a successful call,
++ * no event callbacks not requested in @events will be made; if
++ * %UTRACE_EVENT(%QUIESCE) is included in @events, then a
++ * @report_quiesce callback will be made when @target resumes.
++ *
++ * If @target was not stopped and @events excludes some bits that were
++ * set before, this can return -%EINPROGRESS to indicate that @target
++ * may have been making some callback to @engine. When this returns
++ * zero, you can be sure that no event callbacks you've disabled in
++ * @events can be made. If @events only sets new bits that were not set
++ * before on @engine, then -%EINPROGRESS will never be returned.
++ *
++ * To synchronize after an -%EINPROGRESS return, see utrace_barrier().
++ *
++ * When @target is @current, -%EINPROGRESS is not returned. But note
++ * that a newly-created engine will not receive any callbacks related to
++ * an event notification already in progress. This call enables @events
++ * callbacks to be made as soon as @engine becomes eligible for any
++ * callbacks, see utrace_attach_task().
++ *
++ * These rules provide for coherent synchronization based on %UTRACE_STOP,
++ * even when %SIGKILL is breaking its normal simple rules.
++ */
++int utrace_set_events(struct task_struct *target,
++ struct utrace_engine *engine,
++ unsigned long events)
++{
++ struct utrace *utrace;
++ unsigned long old_flags, old_utrace_flags;
++ int ret;
++
++ /*
++ * We just ignore the internal bit, so callers can use
++ * engine->flags to seed bitwise ops for our argument.
++ */
++ events &= ~ENGINE_STOP;
++
++ utrace = get_utrace_lock(target, engine, true);
++ if (unlikely(IS_ERR(utrace)))
++ return PTR_ERR(utrace);
++
++ old_utrace_flags = target->utrace_flags;
++ old_flags = engine->flags & ~ENGINE_STOP;
++
++ if (target->exit_state &&
++ (((events & ~old_flags) & _UTRACE_DEATH_EVENTS) ||
++ (utrace->death &&
++ ((old_flags & ~events) & _UTRACE_DEATH_EVENTS)) ||
++ (utrace->reap && ((old_flags & ~events) & UTRACE_EVENT(REAP))))) {
++ spin_unlock(&utrace->lock);
++ return -EALREADY;
++ }
++
++ /*
++ * When setting these flags, it's essential that we really
++ * synchronize with exit_notify(). They cannot be set after
++ * exit_notify() takes the tasklist_lock. By holding the read
++ * lock here while setting the flags, we ensure that the calls
++ * to tracehook_notify_death() and tracehook_report_death() will
++ * see the new flags. This ensures that utrace_release_task()
++ * knows positively that utrace_report_death() will be called or
++ * that it won't.
++ */
++ if ((events & ~old_utrace_flags) & _UTRACE_DEATH_EVENTS) {
++ read_lock(&tasklist_lock);
++ if (unlikely(target->exit_state)) {
++ read_unlock(&tasklist_lock);
++ spin_unlock(&utrace->lock);
++ return -EALREADY;
++ }
++ target->utrace_flags |= events;
++ read_unlock(&tasklist_lock);
++ }
++
++ engine->flags = events | (engine->flags & ENGINE_STOP);
++ target->utrace_flags |= events;
++
++ if ((events & UTRACE_EVENT_SYSCALL) &&
++ !(old_utrace_flags & UTRACE_EVENT_SYSCALL))
++ set_tsk_thread_flag(target, TIF_SYSCALL_TRACE);
++
++ ret = 0;
++ if ((old_flags & ~events) && target != current &&
++ !task_is_stopped_or_traced(target) && !target->exit_state) {
++ /*
++ * This barrier ensures that our engine->flags changes
++ * have hit before we examine utrace->reporting,
++ * pairing with the barrier in start_callback(). If
++ * @target has not yet hit finish_callback() to clear
++ * utrace->reporting, we might be in the middle of a
++ * callback to @engine.
++ */
++ smp_mb();
++ if (utrace->reporting == engine)
++ ret = -EINPROGRESS;
++ }
++
++ spin_unlock(&utrace->lock);
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(utrace_set_events);
++
++/*
++ * Asynchronously mark an engine as being detached.
++ *
++ * This must work while the target thread races with us doing
++ * start_callback(), defined below. It uses smp_rmb() between checking
++ * @engine->flags and using @engine->ops. Here we change @engine->ops
++ * first, then use smp_wmb() before changing @engine->flags. This ensures
++ * it can check the old flags before using the old ops, or check the old
++ * flags before using the new ops, or check the new flags before using the
++ * new ops, but can never check the new flags before using the old ops.
++ * Hence, utrace_detached_ops might be used with any old flags in place.
++ * It has report_quiesce() and report_reap() callbacks to handle all cases.
++ */
++static void mark_engine_detached(struct utrace_engine *engine)
++{
++ engine->ops = &utrace_detached_ops;
++ smp_wmb();
++ engine->flags = UTRACE_EVENT(QUIESCE);
++}
++
++/*
++ * Get @target to stop and return true if it is already stopped now.
++ * If we return false, it will make some event callback soonish.
++ * Called with @utrace locked.
++ */
++static bool utrace_do_stop(struct task_struct *target, struct utrace *utrace)
++{
++ if (task_is_stopped(target)) {
++ /*
++ * Stopped is considered quiescent; when it wakes up, it will
++ * go through utrace_finish_stop() before doing anything else.
++ */
++ spin_lock_irq(&target->sighand->siglock);
++ if (likely(task_is_stopped(target)))
++ __set_task_state(target, TASK_TRACED);
++ spin_unlock_irq(&target->sighand->siglock);
++ } else if (utrace->resume > UTRACE_REPORT) {
++ utrace->resume = UTRACE_REPORT;
++ set_notify_resume(target);
++ }
++
++ return task_is_traced(target);
++}
++
++/*
++ * If the target is not dead it should not be in tracing
++ * stop any more. Wake it unless it's in job control stop.
++ */
++static void utrace_wakeup(struct task_struct *target, struct utrace *utrace)
++{
++ lockdep_assert_held(&utrace->lock);
++ spin_lock_irq(&target->sighand->siglock);
++ if (target->signal->flags & SIGNAL_STOP_STOPPED ||
++ target->signal->group_stop_count)
++ target->state = TASK_STOPPED;
++ else
++ wake_up_state(target, __TASK_TRACED);
++ spin_unlock_irq(&target->sighand->siglock);
++}
++
++/*
++ * This is called when there might be some detached engines on the list or
++ * some stale bits in @task->utrace_flags. Clean them up and recompute the
++ * flags. Returns true if we're now fully detached.
++ *
++ * Called with @utrace->lock held, returns with it released.
++ * After this returns, @utrace might be freed if everything detached.
++ */
++static bool utrace_reset(struct task_struct *task, struct utrace *utrace)
++ __releases(utrace->lock)
++{
++ struct utrace_engine *engine, *next;
++ unsigned long flags = 0;
++ LIST_HEAD(detached);
++
++ splice_attaching(utrace);
++
++ /*
++ * Update the set of events of interest from the union
++ * of the interests of the remaining tracing engines.
++ * For any engine marked detached, remove it from the list.
++ * We'll collect them on the detached list.
++ */
++ list_for_each_entry_safe(engine, next, &utrace->attached, entry) {
++ if (engine->ops == &utrace_detached_ops) {
++ engine->ops = NULL;
++ list_move(&engine->entry, &detached);
++ } else {
++ flags |= engine->flags | UTRACE_EVENT(REAP);
++ }
++ }
++
++ if (task->exit_state) {
++ /*
++ * Once it's already dead, we never install any flags
++ * except REAP. When ->exit_state is set and events
++ * like DEATH are not set, then they never can be set.
++ * This ensures that utrace_release_task() knows
++ * positively that utrace_report_death() can never run.
++ */
++ BUG_ON(utrace->death);
++ flags &= UTRACE_EVENT(REAP);
++ } else if (!(flags & UTRACE_EVENT_SYSCALL) &&
++ test_tsk_thread_flag(task, TIF_SYSCALL_TRACE)) {
++ clear_tsk_thread_flag(task, TIF_SYSCALL_TRACE);
++ }
++
++ if (!flags) {
++ /*
++ * No more engines, cleared out the utrace.
++ */
++ utrace->resume = UTRACE_RESUME;
++ utrace->signal_handler = 0;
++ }
++
++ /*
++ * If no more engines want it stopped, wake it up.
++ */
++ if (task_is_traced(task) && !(flags & ENGINE_STOP))
++ utrace_wakeup(task, utrace);
++
++ /*
++ * In theory spin_lock() doesn't imply rcu_read_lock().
++ * Once we clear ->utrace_flags this task_struct can go away
++ * because tracehook_prepare_release_task() path does not take
++ * utrace->lock when ->utrace_flags == 0.
++ */
++ rcu_read_lock();
++ task->utrace_flags = flags;
++ spin_unlock(&utrace->lock);
++ rcu_read_unlock();
++
++ put_detached_list(&detached);
++
++ return !flags;
++}
++
++void utrace_finish_stop(void)
++{
++ /*
++ * If we were task_is_traced() and then SIGKILL'ed, make
++ * sure we do nothing until the tracer drops utrace->lock.
++ */
++ if (unlikely(__fatal_signal_pending(current))) {
++ struct utrace *utrace = task_utrace_struct(current);
++ spin_unlock_wait(&utrace->lock);
++ }
++}
++
++/*
++ * Perform %UTRACE_STOP, i.e. block in TASK_TRACED until woken up.
++ * @task == current, @utrace == current->utrace, which is not locked.
++ * Return true if we were woken up by SIGKILL even though some utrace
++ * engine may still want us to stay stopped.
++ */
++static void utrace_stop(struct task_struct *task, struct utrace *utrace,
++ enum utrace_resume_action action)
++{
++relock:
++ spin_lock(&utrace->lock);
++
++ if (action < utrace->resume) {
++ /*
++ * Ensure a reporting pass when we're resumed.
++ */
++ utrace->resume = action;
++ if (action == UTRACE_INTERRUPT)
++ set_thread_flag(TIF_SIGPENDING);
++ else
++ set_thread_flag(TIF_NOTIFY_RESUME);
++ }
++
++ /*
++ * If the ENGINE_STOP bit is clear in utrace_flags, that means
++ * utrace_reset() ran after we processed some UTRACE_STOP return
++ * values from callbacks to get here. If all engines have detached
++ * or resumed us, we don't stop. This check doesn't require
++ * siglock, but it should follow the interrupt/report bookkeeping
++ * steps (this can matter for UTRACE_RESUME but not UTRACE_DETACH).
++ */
++ if (unlikely(!(task->utrace_flags & ENGINE_STOP))) {
++ utrace_reset(task, utrace);
++ if (task->utrace_flags & ENGINE_STOP)
++ goto relock;
++ return;
++ }
++
++ /*
++ * The siglock protects us against signals. As well as SIGKILL
++ * waking us up, we must synchronize with the signal bookkeeping
++ * for stop signals and SIGCONT.
++ */
++ spin_lock_irq(&task->sighand->siglock);
++
++ if (unlikely(__fatal_signal_pending(task))) {
++ spin_unlock_irq(&task->sighand->siglock);
++ spin_unlock(&utrace->lock);
++ return;
++ }
++
++ __set_current_state(TASK_TRACED);
++
++ /*
++ * If there is a group stop in progress,
++ * we must participate in the bookkeeping.
++ */
++ if (unlikely(task->signal->group_stop_count) &&
++ !--task->signal->group_stop_count)
++ task->signal->flags = SIGNAL_STOP_STOPPED;
++
++ spin_unlock_irq(&task->sighand->siglock);
++ spin_unlock(&utrace->lock);
++
++ schedule();
++
++ utrace_finish_stop();
++
++ /*
++ * While in TASK_TRACED, we were considered "frozen enough".
++ * Now that we woke up, it's crucial if we're supposed to be
++ * frozen that we freeze now before running anything substantial.
++ */
++ try_to_freeze();
++
++ /*
++ * While we were in TASK_TRACED, complete_signal() considered
++ * us "uninterested" in signal wakeups. Now make sure our
++ * TIF_SIGPENDING state is correct for normal running.
++ */
++ spin_lock_irq(&task->sighand->siglock);
++ recalc_sigpending();
++ spin_unlock_irq(&task->sighand->siglock);
++}
++
++/*
++ * Called by release_task() with @reap set to true.
++ * Called by utrace_report_death() with @reap set to false.
++ * On reap, make report_reap callbacks and clean out @utrace
++ * unless still making callbacks. On death, update bookkeeping
++ * and handle the reap work if release_task() came in first.
++ */
++void utrace_maybe_reap(struct task_struct *target, struct utrace *utrace,
++ bool reap)
++{
++ struct utrace_engine *engine, *next;
++ struct list_head attached;
++
++ spin_lock(&utrace->lock);
++
++ if (reap) {
++ /*
++ * If the target will do some final callbacks but hasn't
++ * finished them yet, we know because it clears these event
++ * bits after it's done. Instead of cleaning up here and
++ * requiring utrace_report_death() to cope with it, we
++ * delay the REAP report and the teardown until after the
++ * target finishes its death reports.
++ */
++ utrace->reap = 1;
++
++ if (target->utrace_flags & _UTRACE_DEATH_EVENTS) {
++ spin_unlock(&utrace->lock);
++ return;
++ }
++ } else {
++ /*
++ * After we unlock with this flag clear, any competing
++ * utrace_control/utrace_set_events calls know that we've
++ * finished our callbacks and any detach bookkeeping.
++ */
++ utrace->death = 0;
++
++ if (!utrace->reap) {
++ /*
++ * We're just dead, not reaped yet. This will
++ * reset @target->utrace_flags so the later call
++ * with @reap set won't hit the check above.
++ */
++ utrace_reset(target, utrace);
++ return;
++ }
++ }
++
++ /*
++ * utrace_add_engine() checks ->utrace_flags != 0. Since
++ * @utrace->reap is set, nobody can set or clear UTRACE_EVENT(REAP)
++ * in @engine->flags or change @engine->ops and nobody can change
++ * @utrace->attached after we drop the lock.
++ */
++ target->utrace_flags = 0;
++
++ /*
++ * We clear out @utrace->attached before we drop the lock so
++ * that find_matching_engine() can't come across any old engine
++ * while we are busy tearing it down.
++ */
++ list_replace_init(&utrace->attached, &attached);
++ list_splice_tail_init(&utrace->attaching, &attached);
++
++ spin_unlock(&utrace->lock);
++
++ list_for_each_entry_safe(engine, next, &attached, entry) {
++ if (engine->flags & UTRACE_EVENT(REAP))
++ engine->ops->report_reap(engine, target);
++
++ engine->ops = NULL;
++ engine->flags = 0;
++ list_del_init(&engine->entry);
++
++ utrace_engine_put(engine);
++ }
++}
++
++/*
++ * You can't do anything to a dead task but detach it.
++ * If release_task() has been called, you can't do that.
++ *
++ * On the exit path, DEATH and QUIESCE event bits are set only
++ * before utrace_report_death() has taken the lock. At that point,
++ * the death report will come soon, so disallow detach until it's
++ * done. This prevents us from racing with it detaching itself.
++ *
++ * Called only when @target->exit_state is nonzero.
++ */
++static inline int utrace_control_dead(struct task_struct *target,
++ struct utrace *utrace,
++ enum utrace_resume_action action)
++{
++ lockdep_assert_held(&utrace->lock);
++
++ if (action != UTRACE_DETACH || unlikely(utrace->reap))
++ return -ESRCH;
++
++ if (unlikely(utrace->death))
++ /*
++ * We have already started the death report. We can't
++ * prevent the report_death and report_reap callbacks,
++ * so tell the caller they will happen.
++ */
++ return -EALREADY;
++
++ return 0;
++}
++
++/**
++ * utrace_control - control a thread being traced by a tracing engine
++ * @target: thread to affect
++ * @engine: attached engine to affect
++ * @action: &enum utrace_resume_action for thread to do
++ *
++ * This is how a tracing engine asks a traced thread to do something.
++ * This call is controlled by the @action argument, which has the
++ * same meaning as the &enum utrace_resume_action value returned by
++ * event reporting callbacks.
++ *
++ * If @target is already dead (@target->exit_state nonzero),
++ * all actions except %UTRACE_DETACH fail with -%ESRCH.
++ *
++ * The following sections describe each option for the @action argument.
++ *
++ * UTRACE_DETACH:
++ *
++ * After this, the @engine data structure is no longer accessible,
++ * and the thread might be reaped. The thread will start running
++ * again if it was stopped and no longer has any attached engines
++ * that want it stopped.
++ *
++ * If the @report_reap callback may already have begun, this fails
++ * with -%ESRCH. If the @report_death callback may already have
++ * begun, this fails with -%EALREADY.
++ *
++ * If @target is not already stopped, then a callback to this engine
++ * might be in progress or about to start on another CPU. If so,
++ * then this returns -%EINPROGRESS; the detach happens as soon as
++ * the pending callback is finished. To synchronize after an
++ * -%EINPROGRESS return, see utrace_barrier().
++ *
++ * If @target is properly stopped before utrace_control() is called,
++ * then after successful return it's guaranteed that no more callbacks
++ * to the @engine->ops vector will be made.
++ *
++ * The only exception is %SIGKILL (and exec or group-exit by another
++ * thread in the group), which can cause asynchronous @report_death
++ * and/or @report_reap callbacks even when %UTRACE_STOP was used.
++ * (In that event, this fails with -%ESRCH or -%EALREADY, see above.)
++ *
++ * UTRACE_STOP:
++ *
++ * This asks that @target stop running. This returns 0 only if
++ * @target is already stopped, either for tracing or for job
++ * control. Then @target will remain stopped until another
++ * utrace_control() call is made on @engine; @target can be woken
++ * only by %SIGKILL (or equivalent, such as exec or termination by
++ * another thread in the same thread group).
++ *
++ * This returns -%EINPROGRESS if @target is not already stopped.
++ * Then the effect is like %UTRACE_REPORT. A @report_quiesce or
++ * @report_signal callback will be made soon. Your callback can
++ * then return %UTRACE_STOP to keep @target stopped.
++ *
++ * This does not interrupt system calls in progress, including ones
++ * that sleep for a long time. For that, use %UTRACE_INTERRUPT.
++ * To interrupt system calls and then keep @target stopped, your
++ * @report_signal callback can return %UTRACE_STOP.
++ *
++ * UTRACE_RESUME:
++ *
++ * Just let @target continue running normally, reversing the effect
++ * of a previous %UTRACE_STOP. If another engine is keeping @target
++ * stopped, then it remains stopped until all engines let it resume.
++ * If @target was not stopped, this has no effect.
++ *
++ * UTRACE_REPORT:
++ *
++ * This is like %UTRACE_RESUME, but also ensures that there will be
++ * a @report_quiesce or @report_signal callback made soon. If
++ * @target had been stopped, then there will be a callback before it
++ * resumes running normally. If another engine is keeping @target
++ * stopped, then there might be no callbacks until all engines let
++ * it resume.
++ *
++ * Since this is meaningless unless @report_quiesce callbacks will
++ * be made, it returns -%EINVAL if @engine lacks %UTRACE_EVENT(%QUIESCE).
++ *
++ * UTRACE_INTERRUPT:
++ *
++ * This is like %UTRACE_REPORT, but ensures that @target will make a
++ * @report_signal callback before it resumes or delivers signals.
++ * If @target was in a system call or about to enter one, work in
++ * progress will be interrupted as if by %SIGSTOP. If another
++ * engine is keeping @target stopped, then there might be no
++ * callbacks until all engines let it resume.
++ *
++ * This gives @engine an opportunity to introduce a forced signal
++ * disposition via its @report_signal callback.
++ *
++ * UTRACE_SINGLESTEP:
++ *
++ * It's invalid to use this unless arch_has_single_step() returned true.
++ * This is like %UTRACE_RESUME, but resumes for one user instruction only.
++ *
++ * Note that passing %UTRACE_SINGLESTEP or %UTRACE_BLOCKSTEP to
++ * utrace_control() or returning it from an event callback alone does
++ * not necessarily ensure that stepping will be enabled. If there are
++ * more callbacks made to any engine before returning to user mode,
++ * then the resume action is chosen only by the last set of callbacks.
++ * To be sure, enable %UTRACE_EVENT(%QUIESCE) and look for the
++ * @report_quiesce callback with a zero event mask, or the
++ * @report_signal callback with %UTRACE_SIGNAL_REPORT.
++ *
++ * Since this is not robust unless @report_quiesce callbacks will
++ * be made, it returns -%EINVAL if @engine lacks %UTRACE_EVENT(%QUIESCE).
++ *
++ * UTRACE_BLOCKSTEP:
++ *
++ * It's invalid to use this unless arch_has_block_step() returned true.
++ * This is like %UTRACE_SINGLESTEP, but resumes for one whole basic
++ * block of user instructions.
++ *
++ * Since this is not robust unless @report_quiesce callbacks will
++ * be made, it returns -%EINVAL if @engine lacks %UTRACE_EVENT(%QUIESCE).
++ *
++ * %UTRACE_BLOCKSTEP devolves to %UTRACE_SINGLESTEP when another
++ * tracing engine is using %UTRACE_SINGLESTEP at the same time.
++ */
++int utrace_control(struct task_struct *target,
++ struct utrace_engine *engine,
++ enum utrace_resume_action action)
++{
++ struct utrace *utrace;
++ bool reset;
++ int ret;
++
++ if (unlikely(action >= UTRACE_RESUME_MAX)) {
++ WARN(1, "invalid action argument to utrace_control()!");
++ return -EINVAL;
++ }
++
++ /*
++ * This is a sanity check for a programming error in the caller.
++ * Their request can only work properly in all cases by relying on
++ * a follow-up callback, but they didn't set one up! This check
++ * doesn't do locking, but it shouldn't matter. The caller has to
++ * be synchronously sure the callback is set up to be operating the
++ * interface properly.
++ */
++ if (action >= UTRACE_REPORT && action < UTRACE_RESUME &&
++ unlikely(!(engine->flags & UTRACE_EVENT(QUIESCE)))) {
++ WARN(1, "utrace_control() with no QUIESCE callback in place!");
++ return -EINVAL;
++ }
++
++ utrace = get_utrace_lock(target, engine, true);
++ if (unlikely(IS_ERR(utrace)))
++ return PTR_ERR(utrace);
++
++ reset = task_is_traced(target);
++ ret = 0;
++
++ /*
++ * ->exit_state can change under us, this doesn't matter.
++ * We do not care about ->exit_state in fact, but we do
++ * care about ->reap and ->death. If either flag is set,
++ * we must also see ->exit_state != 0.
++ */
++ if (unlikely(target->exit_state)) {
++ ret = utrace_control_dead(target, utrace, action);
++ if (ret) {
++ spin_unlock(&utrace->lock);
++ return ret;
++ }
++ reset = true;
++ }
++
++ switch (action) {
++ case UTRACE_STOP:
++ mark_engine_wants_stop(target, engine);
++ if (!reset && !utrace_do_stop(target, utrace))
++ ret = -EINPROGRESS;
++ reset = false;
++ break;
++
++ case UTRACE_DETACH:
++ if (engine_wants_stop(engine))
++ target->utrace_flags &= ~ENGINE_STOP;
++ mark_engine_detached(engine);
++ reset = reset || utrace_do_stop(target, utrace);
++ if (!reset) {
++ /*
++ * As in utrace_set_events(), this barrier ensures
++ * that our engine->flags changes have hit before we
++ * examine utrace->reporting, pairing with the barrier
++ * in start_callback(). If @target has not yet hit
++ * finish_callback() to clear utrace->reporting, we
++ * might be in the middle of a callback to @engine.
++ */
++ smp_mb();
++ if (utrace->reporting == engine)
++ ret = -EINPROGRESS;
++ }
++ break;
++
++ case UTRACE_RESUME:
++ /*
++ * This and all other cases imply resuming if stopped.
++ * There might not be another report before it just
++ * resumes, so make sure single-step is not left set.
++ */
++ clear_engine_wants_stop(engine);
++ if (likely(reset))
++ user_disable_single_step(target);
++ break;
++
++ case UTRACE_BLOCKSTEP:
++ /*
++ * Resume from stopped, step one block.
++ * We fall through to treat it like UTRACE_SINGLESTEP.
++ */
++ if (unlikely(!arch_has_block_step())) {
++ WARN(1, "UTRACE_BLOCKSTEP when !arch_has_block_step()");
++ action = UTRACE_SINGLESTEP;
++ }
++
++ case UTRACE_SINGLESTEP:
++ /*
++ * Resume from stopped, step one instruction.
++ * We fall through to the UTRACE_REPORT case.
++ */
++ if (unlikely(!arch_has_single_step())) {
++ WARN(1,
++ "UTRACE_SINGLESTEP when !arch_has_single_step()");
++ reset = false;
++ ret = -EOPNOTSUPP;
++ break;
++ }
++
++ case UTRACE_REPORT:
++ /*
++ * Make the thread call tracehook_notify_resume() soon.
++ * But don't bother if it's already been interrupted.
++ * In that case, utrace_get_signal() will be reporting soon.
++ */
++ clear_engine_wants_stop(engine);
++ if (action < utrace->resume) {
++ utrace->resume = action;
++ set_notify_resume(target);
++ }
++ break;
++
++ case UTRACE_INTERRUPT:
++ /*
++ * Make the thread call tracehook_get_signal() soon.
++ */
++ clear_engine_wants_stop(engine);
++ if (utrace->resume == UTRACE_INTERRUPT)
++ break;
++ utrace->resume = UTRACE_INTERRUPT;
++
++ /*
++ * If it's not already stopped, interrupt it now. We need
++ * the siglock here in case it calls recalc_sigpending()
++ * and clears its own TIF_SIGPENDING. By taking the lock,
++ * we've serialized any later recalc_sigpending() after our
++ * setting of utrace->resume to force it on.
++ */
++ if (reset) {
++ /*
++ * This is really just to keep the invariant that
++ * TIF_SIGPENDING is set with UTRACE_INTERRUPT.
++ * When it's stopped, we know it's always going
++ * through utrace_get_signal() and will recalculate.
++ */
++ set_tsk_thread_flag(target, TIF_SIGPENDING);
++ } else {
++ struct sighand_struct *sighand;
++ unsigned long irqflags;
++ sighand = lock_task_sighand(target, &irqflags);
++ if (likely(sighand)) {
++ signal_wake_up(target, 0);
++ unlock_task_sighand(target, &irqflags);
++ }
++ }
++ break;
++
++ default:
++ BUG(); /* We checked it on entry. */
++ }
++
++ /*
++ * Let the thread resume running. If it's not stopped now,
++ * there is nothing more we need to do.
++ */
++ if (reset)
++ utrace_reset(target, utrace);
++ else
++ spin_unlock(&utrace->lock);
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(utrace_control);
++
++/**
++ * utrace_barrier - synchronize with simultaneous tracing callbacks
++ * @target: thread to affect
++ * @engine: engine to affect (can be detached)
++ *
++ * This blocks while @target might be in the midst of making a callback to
++ * @engine. It can be interrupted by signals and will return -%ERESTARTSYS.
++ * A return value of zero means no callback from @target to @engine was
++ * in progress. Any effect of its return value (such as %UTRACE_STOP) has
++ * already been applied to @engine.
++ *
++ * It's not necessary to keep the @target pointer alive for this call.
++ * It's only necessary to hold a ref on @engine. This will return
++ * safely even if @target has been reaped and has no task refs.
++ *
++ * A successful return from utrace_barrier() guarantees its ordering
++ * with respect to utrace_set_events() and utrace_control() calls. If
++ * @target was not properly stopped, event callbacks just disabled might
++ * still be in progress; utrace_barrier() waits until there is no chance
++ * an unwanted callback can be in progress.
++ */
++int utrace_barrier(struct task_struct *target, struct utrace_engine *engine)
++{
++ struct utrace *utrace;
++ int ret = -ERESTARTSYS;
++
++ if (unlikely(target == current))
++ return 0;
++
++ do {
++ utrace = get_utrace_lock(target, engine, false);
++ if (unlikely(IS_ERR(utrace))) {
++ ret = PTR_ERR(utrace);
++ if (ret != -ERESTARTSYS)
++ break;
++ } else {
++ /*
++ * All engine state changes are done while
++ * holding the lock, i.e. before we get here.
++ * Since we have the lock, we only need to
++ * worry about @target making a callback.
++ * When it has entered start_callback() but
++ * not yet gotten to finish_callback(), we
++ * will see utrace->reporting == @engine.
++ * When @target doesn't take the lock, it uses
++ * barriers to order setting utrace->reporting
++ * before it examines the engine state.
++ */
++ if (utrace->reporting != engine)
++ ret = 0;
++ spin_unlock(&utrace->lock);
++ if (!ret)
++ break;
++ }
++ schedule_timeout_interruptible(1);
++ } while (!signal_pending(current));
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(utrace_barrier);
++
++/*
++ * This is local state used for reporting loops, perhaps optimized away.
++ */
++struct utrace_report {
++ u32 result;
++ enum utrace_resume_action action;
++ enum utrace_resume_action resume_action;
++ bool detaches;
++ bool spurious;
++};
++
++#define INIT_REPORT(var) \
++ struct utrace_report var = { \
++ .action = UTRACE_RESUME, \
++ .resume_action = UTRACE_RESUME, \
++ .spurious = true \
++ }
++
++/*
++ * We are now making the report, so clear the flag saying we need one.
++ * When there is a new attach, ->pending_attach is set just so we will
++ * know to do splice_attaching() here before the callback loop.
++ */
++static enum utrace_resume_action start_report(struct utrace *utrace)
++{
++ enum utrace_resume_action resume = utrace->resume;
++ if (utrace->pending_attach ||
++ (resume > UTRACE_INTERRUPT && resume < UTRACE_RESUME)) {
++ spin_lock(&utrace->lock);
++ splice_attaching(utrace);
++ resume = utrace->resume;
++ if (resume > UTRACE_INTERRUPT)
++ utrace->resume = UTRACE_RESUME;
++ spin_unlock(&utrace->lock);
++ }
++ return resume;
++}
++
++static inline void finish_report_reset(struct task_struct *task,
++ struct utrace *utrace,
++ struct utrace_report *report)
++{
++ if (unlikely(report->spurious || report->detaches)) {
++ spin_lock(&utrace->lock);
++ if (utrace_reset(task, utrace))
++ report->action = UTRACE_RESUME;
++ }
++}
++
++/*
++ * Complete a normal reporting pass, pairing with a start_report() call.
++ * This handles any UTRACE_DETACH or UTRACE_REPORT or UTRACE_INTERRUPT
++ * returns from engine callbacks. If @will_not_stop is true and any
++ * engine's last callback used UTRACE_STOP, we do UTRACE_REPORT here to
++ * ensure we stop before user mode. If there were no callbacks made, it
++ * will recompute @task->utrace_flags to avoid another false-positive.
++ */
++static void finish_report(struct task_struct *task, struct utrace *utrace,
++ struct utrace_report *report, bool will_not_stop)
++{
++ enum utrace_resume_action resume = report->action;
++
++ if (resume == UTRACE_STOP)
++ resume = will_not_stop ? UTRACE_REPORT : UTRACE_RESUME;
++
++ if (resume < utrace->resume) {
++ spin_lock(&utrace->lock);
++ utrace->resume = resume;
++ if (resume == UTRACE_INTERRUPT)
++ set_tsk_thread_flag(task, TIF_SIGPENDING);
++ else
++ set_tsk_thread_flag(task, TIF_NOTIFY_RESUME);
++ spin_unlock(&utrace->lock);
++ }
++
++ finish_report_reset(task, utrace, report);
++}
++
++static void finish_callback_report(struct task_struct *task,
++ struct utrace *utrace,
++ struct utrace_report *report,
++ struct utrace_engine *engine,
++ enum utrace_resume_action action)
++{
++ if (action == UTRACE_DETACH) {
++ /*
++ * By holding the lock here, we make sure that
++ * utrace_barrier() (really get_utrace_lock()) sees the
++ * effect of this detach. Otherwise utrace_barrier() could
++ * return 0 after this callback had returned UTRACE_DETACH.
++ * This way, a 0 return is an unambiguous indicator that any
++ * callback returning UTRACE_DETACH has indeed caused detach.
++ */
++ spin_lock(&utrace->lock);
++ engine->ops = &utrace_detached_ops;
++ spin_unlock(&utrace->lock);
++ }
++
++ /*
++ * If utrace_control() was used, treat that like UTRACE_DETACH here.
++ */
++ if (engine->ops == &utrace_detached_ops) {
++ report->detaches = true;
++ return;
++ }
++
++ if (action < report->action)
++ report->action = action;
++
++ if (action != UTRACE_STOP) {
++ if (action < report->resume_action)
++ report->resume_action = action;
++
++ if (engine_wants_stop(engine)) {
++ spin_lock(&utrace->lock);
++ clear_engine_wants_stop(engine);
++ spin_unlock(&utrace->lock);
++ }
++
++ return;
++ }
++
++ if (!engine_wants_stop(engine)) {
++ spin_lock(&utrace->lock);
++ /*
++ * If utrace_control() came in and detached us
++ * before we got the lock, we must not stop now.
++ */
++ if (unlikely(engine->ops == &utrace_detached_ops))
++ report->detaches = true;
++ else
++ mark_engine_wants_stop(task, engine);
++ spin_unlock(&utrace->lock);
++ }
++}
++
++/*
++ * Apply the return value of one engine callback to @report.
++ * Returns true if @engine detached and should not get any more callbacks.
++ */
++static bool finish_callback(struct task_struct *task, struct utrace *utrace,
++ struct utrace_report *report,
++ struct utrace_engine *engine,
++ u32 ret)
++{
++ report->result = ret & ~UTRACE_RESUME_MASK;
++ finish_callback_report(task, utrace, report, engine,
++ utrace_resume_action(ret));
++
++ /*
++ * Now that we have applied the effect of the return value,
++ * clear this so that utrace_barrier() can stop waiting.
++ * A subsequent utrace_control() can stop or resume @engine
++ * and know this was ordered after its callback's action.
++ *
++ * We don't need any barriers here because utrace_barrier()
++ * takes utrace->lock. If we touched engine->flags above,
++ * the lock guaranteed this change was before utrace_barrier()
++ * examined utrace->reporting.
++ */
++ utrace->reporting = NULL;
++
++ /*
++ * We've just done an engine callback. These are allowed to sleep,
++ * though all well-behaved ones restrict that to blocking kalloc()
++ * or quickly-acquired mutex_lock() and the like. This is a good
++ * place to make sure tracing engines don't introduce too much
++ * latency under voluntary preemption.
++ */
++ might_sleep();
++
++ return engine->ops == &utrace_detached_ops;
++}
++
++/*
++ * Start the callbacks for @engine to consider @event (a bit mask).
++ * This makes the report_quiesce() callback first. If @engine wants
++ * a specific callback for @event, we return the ops vector to use.
++ * If not, we return NULL. The return value from the ops->callback
++ * function called should be passed to finish_callback().
++ */
++static const struct utrace_engine_ops *start_callback(
++ struct utrace *utrace, struct utrace_report *report,
++ struct utrace_engine *engine, struct task_struct *task,
++ unsigned long event)
++{
++ const struct utrace_engine_ops *ops;
++ unsigned long want;
++
++ /*
++ * This barrier ensures that we've set utrace->reporting before
++ * we examine engine->flags or engine->ops. utrace_barrier()
++ * relies on this ordering to indicate that the effect of any
++ * utrace_control() and utrace_set_events() calls is in place
++ * by the time utrace->reporting can be seen to be NULL.
++ */
++ utrace->reporting = engine;
++ smp_mb();
++
++ /*
++ * This pairs with the barrier in mark_engine_detached().
++ * It makes sure that we never see the old ops vector with
++ * the new flags, in case the original vector had no report_quiesce.
++ */
++ want = engine->flags;
++ smp_rmb();
++ ops = engine->ops;
++
++ if (want & UTRACE_EVENT(QUIESCE)) {
++ if (finish_callback(task, utrace, report, engine,
++ (*ops->report_quiesce)(report->action,
++ engine, event)))
++ return NULL;
++
++ /*
++ * finish_callback() reset utrace->reporting after the
++ * quiesce callback. Now we set it again (as above)
++ * before re-examining engine->flags, which could have
++ * been changed synchronously by ->report_quiesce or
++ * asynchronously by utrace_control() or utrace_set_events().
++ */
++ utrace->reporting = engine;
++ smp_mb();
++ want = engine->flags;
++ }
++
++ if (want & ENGINE_STOP)
++ report->action = UTRACE_STOP;
++
++ if (want & event) {
++ report->spurious = false;
++ return ops;
++ }
++
++ utrace->reporting = NULL;
++ return NULL;
++}
++
++/*
++ * Do a normal reporting pass for engines interested in @event.
++ * @callback is the name of the member in the ops vector, and remaining
++ * args are the extras it takes after the standard three args.
++ */
++#define REPORT_CALLBACKS(rev, task, utrace, report, event, callback, ...) \
++ do { \
++ struct utrace_engine *engine; \
++ const struct utrace_engine_ops *ops; \
++ list_for_each_entry##rev(engine, &utrace->attached, entry) { \
++ ops = start_callback(utrace, report, engine, task, \
++ event); \
++ if (!ops) \
++ continue; \
++ finish_callback(task, utrace, report, engine, \
++ (*ops->callback)(__VA_ARGS__)); \
++ } \
++ } while (0)
++#define REPORT(task, utrace, report, event, callback, ...) \
++ do { \
++ start_report(utrace); \
++ REPORT_CALLBACKS(, task, utrace, report, event, callback, \
++ (report)->action, engine, ## __VA_ARGS__); \
++ finish_report(task, utrace, report, true); \
++ } while (0)
++
++/*
++ * Called iff UTRACE_EVENT(EXEC) flag is set.
++ */
++void utrace_report_exec(struct linux_binfmt *fmt, struct linux_binprm *bprm,
++ struct pt_regs *regs)
++{
++ struct task_struct *task = current;
++ struct utrace *utrace = task_utrace_struct(task);
++ INIT_REPORT(report);
++
++ REPORT(task, utrace, &report, UTRACE_EVENT(EXEC),
++ report_exec, fmt, bprm, regs);
++}
++
++static u32 do_report_syscall_entry(struct pt_regs *regs,
++ struct task_struct *task,
++ struct utrace *utrace,
++ struct utrace_report *report,
++ u32 resume_report)
++{
++ start_report(utrace);
++ REPORT_CALLBACKS(_reverse, task, utrace, report,
++ UTRACE_EVENT(SYSCALL_ENTRY), report_syscall_entry,
++ resume_report | report->result | report->action,
++ engine, regs);
++ finish_report(task, utrace, report, false);
++
++ if (report->action != UTRACE_STOP)
++ return 0;
++
++ utrace_stop(task, utrace, report->resume_action);
++
++ if (fatal_signal_pending(task)) {
++ /*
++ * We are continuing despite UTRACE_STOP because of a
++ * SIGKILL. Don't let the system call actually proceed.
++ */
++ report->result = UTRACE_SYSCALL_ABORT;
++ } else if (utrace->resume <= UTRACE_REPORT) {
++ /*
++ * If we've been asked for another report after our stop,
++ * go back to report (and maybe stop) again before we run
++ * the system call. The second (and later) reports are
++ * marked with the UTRACE_SYSCALL_RESUMED flag so that
++ * engines know this is a second report at the same
++ * entry. This gives them the chance to examine the
++ * registers anew after they might have been changed
++ * while we were stopped.
++ */
++ report->detaches = false;
++ report->spurious = true;
++ report->action = report->resume_action = UTRACE_RESUME;
++ return UTRACE_SYSCALL_RESUMED;
++ }
++
++ return 0;
++}
++
++/*
++ * Called iff UTRACE_EVENT(SYSCALL_ENTRY) flag is set.
++ * Return true to prevent the system call.
++ */
++bool utrace_report_syscall_entry(struct pt_regs *regs)
++{
++ struct task_struct *task = current;
++ struct utrace *utrace = task_utrace_struct(task);
++ INIT_REPORT(report);
++ u32 resume_report = 0;
++
++ do {
++ resume_report = do_report_syscall_entry(regs, task, utrace,
++ &report, resume_report);
++ } while (resume_report);
++
++ return utrace_syscall_action(report.result) == UTRACE_SYSCALL_ABORT;
++}
++
++/*
++ * Called iff UTRACE_EVENT(SYSCALL_EXIT) flag is set.
++ */
++void utrace_report_syscall_exit(struct pt_regs *regs)
++{
++ struct task_struct *task = current;
++ struct utrace *utrace = task_utrace_struct(task);
++ INIT_REPORT(report);
++
++ REPORT(task, utrace, &report, UTRACE_EVENT(SYSCALL_EXIT),
++ report_syscall_exit, regs);
++}
++
++/*
++ * Called iff UTRACE_EVENT(CLONE) flag is set.
++ * This notification call blocks the wake_up_new_task call on the child.
++ * So we must not quiesce here. tracehook_report_clone_complete will do
++ * a quiescence check momentarily.
++ */
++void utrace_report_clone(unsigned long clone_flags, struct task_struct *child)
++{
++ struct task_struct *task = current;
++ struct utrace *utrace = task_utrace_struct(task);
++ INIT_REPORT(report);
++
++ /*
++ * We don't use the REPORT() macro here, because we need
++ * to clear utrace->cloning before finish_report().
++ * After finish_report(), utrace can be a stale pointer
++ * in cases when report.action is still UTRACE_RESUME.
++ */
++ start_report(utrace);
++ utrace->cloning = child;
++
++ REPORT_CALLBACKS(, task, utrace, &report,
++ UTRACE_EVENT(CLONE), report_clone,
++ report.action, engine, clone_flags, child);
++
++ utrace->cloning = NULL;
++ finish_report(task, utrace, &report, !(clone_flags & CLONE_VFORK));
++
++ /*
++ * For a vfork, we will go into an uninterruptible block waiting
++ * for the child. We need UTRACE_STOP to happen before this, not
++ * after. For CLONE_VFORK, utrace_finish_vfork() will be called.
++ */
++ if (report.action == UTRACE_STOP && (clone_flags & CLONE_VFORK)) {
++ spin_lock(&utrace->lock);
++ utrace->vfork_stop = 1;
++ spin_unlock(&utrace->lock);
++ }
++}
++
++/*
++ * We're called after utrace_report_clone() for a CLONE_VFORK.
++ * If UTRACE_STOP was left from the clone report, we stop here.
++ * After this, we'll enter the uninterruptible wait_for_completion()
++ * waiting for the child.
++ */
++void utrace_finish_vfork(struct task_struct *task)
++{
++ struct utrace *utrace = task_utrace_struct(task);
++
++ if (utrace->vfork_stop) {
++ spin_lock(&utrace->lock);
++ utrace->vfork_stop = 0;
++ spin_unlock(&utrace->lock);
++ utrace_stop(task, utrace, UTRACE_RESUME); /* XXX */
++ }
++}
++
++/*
++ * Called iff UTRACE_EVENT(JCTL) flag is set.
++ *
++ * Called with siglock held.
++ */
++void utrace_report_jctl(int notify, int what)
++{
++ struct task_struct *task = current;
++ struct utrace *utrace = task_utrace_struct(task);
++ INIT_REPORT(report);
++
++ spin_unlock_irq(&task->sighand->siglock);
++
++ REPORT(task, utrace, &report, UTRACE_EVENT(JCTL),
++ report_jctl, what, notify);
++
++ spin_lock_irq(&task->sighand->siglock);
++}
++
++/*
++ * Called iff UTRACE_EVENT(EXIT) flag is set.
++ */
++void utrace_report_exit(long *exit_code)
++{
++ struct task_struct *task = current;
++ struct utrace *utrace = task_utrace_struct(task);
++ INIT_REPORT(report);
++ long orig_code = *exit_code;
++
++ REPORT(task, utrace, &report, UTRACE_EVENT(EXIT),
++ report_exit, orig_code, exit_code);
++
++ if (report.action == UTRACE_STOP)
++ utrace_stop(task, utrace, report.resume_action);
++}
++
++/*
++ * Called iff UTRACE_EVENT(DEATH) or UTRACE_EVENT(QUIESCE) flag is set.
++ *
++ * It is always possible that we are racing with utrace_release_task here.
++ * For this reason, utrace_release_task checks for the event bits that get
++ * us here, and delays its cleanup for us to do.
++ */
++void utrace_report_death(struct task_struct *task, struct utrace *utrace,
++ bool group_dead, int signal)
++{
++ INIT_REPORT(report);
++
++ BUG_ON(!task->exit_state);
++
++ /*
++ * We are presently considered "quiescent"--which is accurate
++ * inasmuch as we won't run any more user instructions ever again.
++ * But for utrace_control and utrace_set_events to be robust, they
++ * must be sure whether or not we will run any more callbacks. If
++ * a call comes in before we do, taking the lock here synchronizes
++ * us so we don't run any callbacks just disabled. Calls that come
++ * in while we're running the callbacks will see the exit.death
++ * flag and know that we are not yet fully quiescent for purposes
++ * of detach bookkeeping.
++ */
++ spin_lock(&utrace->lock);
++ BUG_ON(utrace->death);
++ utrace->death = 1;
++ utrace->resume = UTRACE_RESUME;
++ splice_attaching(utrace);
++ spin_unlock(&utrace->lock);
++
++ REPORT_CALLBACKS(, task, utrace, &report, UTRACE_EVENT(DEATH),
++ report_death, engine, group_dead, signal);
++
++ utrace_maybe_reap(task, utrace, false);
++}
++
++/*
++ * Finish the last reporting pass before returning to user mode.
++ */
++static void finish_resume_report(struct task_struct *task,
++ struct utrace *utrace,
++ struct utrace_report *report)
++{
++ finish_report_reset(task, utrace, report);
++
++ switch (report->action) {
++ case UTRACE_STOP:
++ utrace_stop(task, utrace, report->resume_action);
++ break;
++
++ case UTRACE_INTERRUPT:
++ if (!signal_pending(task))
++ set_tsk_thread_flag(task, TIF_SIGPENDING);
++ break;
++
++ case UTRACE_BLOCKSTEP:
++ if (likely(arch_has_block_step())) {
++ user_enable_block_step(task);
++ break;
++ }
++
++ /*
++ * This means some callback is to blame for failing
++ * to check arch_has_block_step() itself. Warn and
++ * then fall through to treat it as SINGLESTEP.
++ */
++ WARN(1, "UTRACE_BLOCKSTEP when !arch_has_block_step()");
++
++ case UTRACE_SINGLESTEP:
++ if (likely(arch_has_single_step())) {
++ user_enable_single_step(task);
++ } else {
++ /*
++ * This means some callback is to blame for failing
++ * to check arch_has_single_step() itself. Spew
++ * about it so the loser will fix his module.
++ */
++ WARN(1,
++ "UTRACE_SINGLESTEP when !arch_has_single_step()");
++ }
++ break;
++
++ case UTRACE_REPORT:
++ case UTRACE_RESUME:
++ default:
++ user_disable_single_step(task);
++ break;
++ }
++}
++
++/*
++ * This is called when TIF_NOTIFY_RESUME had been set (and is now clear).
++ * We are close to user mode, and this is the place to report or stop.
++ * When we return, we're going to user mode or into the signals code.
++ */
++void utrace_resume(struct task_struct *task, struct pt_regs *regs)
++{
++ struct utrace *utrace = task_utrace_struct(task);
++ INIT_REPORT(report);
++ struct utrace_engine *engine;
++
++ /*
++ * Some machines get here with interrupts disabled. The same arch
++ * code path leads to calling into get_signal_to_deliver(), which
++ * implicitly reenables them by virtue of spin_unlock_irq.
++ */
++ local_irq_enable();
++
++ /*
++ * If this flag is still set it's because there was a signal
++ * handler setup done but no report_signal following it. Clear
++ * the flag before we get to user so it doesn't confuse us later.
++ */
++ if (unlikely(utrace->signal_handler)) {
++ spin_lock(&utrace->lock);
++ utrace->signal_handler = 0;
++ spin_unlock(&utrace->lock);
++ }
++
++ /*
++ * Update our bookkeeping even if there are no callbacks made here.
++ */
++ report.action = start_report(utrace);
++
++ switch (report.action) {
++ case UTRACE_RESUME:
++ /*
++ * Anything we might have done was already handled by
++ * utrace_get_signal(), or this is an entirely spurious
++ * call. (The arch might use TIF_NOTIFY_RESUME for other
++ * purposes as well as calling us.)
++ */
++ return;
++ case UTRACE_REPORT:
++ if (unlikely(!(task->utrace_flags & UTRACE_EVENT(QUIESCE))))
++ break;
++ /*
++ * Do a simple reporting pass, with no specific
++ * callback after report_quiesce.
++ */
++ report.action = UTRACE_RESUME;
++ list_for_each_entry(engine, &utrace->attached, entry)
++ start_callback(utrace, &report, engine, task, 0);
++ break;
++ default:
++ /*
++ * Even if this report was truly spurious, there is no need
++ * for utrace_reset() now. TIF_NOTIFY_RESUME was already
++ * cleared--it doesn't stay spuriously set.
++ */
++ report.spurious = false;
++ break;
++ }
++
++ /*
++ * Finish the report and either stop or get ready to resume.
++ * If utrace->resume was not UTRACE_REPORT, this applies its
++ * effect now (i.e. step or interrupt).
++ */
++ finish_resume_report(task, utrace, &report);
++}
++
++/*
++ * Return true if current has forced signal_pending().
++ *
++ * This is called only when current->utrace_flags is nonzero, so we know
++ * that current->utrace must be set. It's not inlined in tracehook.h
++ * just so that struct utrace can stay opaque outside this file.
++ */
++bool utrace_interrupt_pending(void)
++{
++ return task_utrace_struct(current)->resume == UTRACE_INTERRUPT;
++}
++
++/*
++ * Take the siglock and push @info back on our queue.
++ * Returns with @task->sighand->siglock held.
++ */
++static void push_back_signal(struct task_struct *task, siginfo_t *info)
++ __acquires(task->sighand->siglock)
++{
++ struct sigqueue *q;
++
++ if (unlikely(!info->si_signo)) { /* Oh, a wise guy! */
++ spin_lock_irq(&task->sighand->siglock);
++ return;
++ }
++
++ q = sigqueue_alloc();
++ if (likely(q)) {
++ q->flags = 0;
++ copy_siginfo(&q->info, info);
++ }
++
++ spin_lock_irq(&task->sighand->siglock);
++
++ sigaddset(&task->pending.signal, info->si_signo);
++ if (likely(q))
++ list_add(&q->list, &task->pending.list);
++
++ set_tsk_thread_flag(task, TIF_SIGPENDING);
++}
++
++/*
++ * This is the hook from the signals code, called with the siglock held.
++ * Here is the ideal place to stop. We also dequeue and intercept signals.
++ */
++int utrace_get_signal(struct task_struct *task, struct pt_regs *regs,
++ siginfo_t *info, struct k_sigaction *return_ka)
++ __releases(task->sighand->siglock)
++ __acquires(task->sighand->siglock)
++{
++ struct utrace *utrace;
++ struct k_sigaction *ka;
++ INIT_REPORT(report);
++ struct utrace_engine *engine;
++ const struct utrace_engine_ops *ops;
++ unsigned long event, want;
++ u32 ret;
++ int signr;
++
++ utrace = task_utrace_struct(task);
++ if (utrace->resume < UTRACE_RESUME ||
++ utrace->pending_attach || utrace->signal_handler) {
++ enum utrace_resume_action resume;
++
++ /*
++ * We've been asked for an explicit report before we
++ * even check for pending signals.
++ */
++
++ spin_unlock_irq(&task->sighand->siglock);
++
++ spin_lock(&utrace->lock);
++
++ splice_attaching(utrace);
++
++ report.result = utrace->signal_handler ?
++ UTRACE_SIGNAL_HANDLER : UTRACE_SIGNAL_REPORT;
++ utrace->signal_handler = 0;
++
++ resume = utrace->resume;
++ utrace->resume = UTRACE_RESUME;
++
++ spin_unlock(&utrace->lock);
++
++ /*
++ * Make sure signal_pending() only returns true
++ * if there are real signals pending.
++ */
++ if (signal_pending(task)) {
++ spin_lock_irq(&task->sighand->siglock);
++ recalc_sigpending();
++ spin_unlock_irq(&task->sighand->siglock);
++ }
++
++ if (resume > UTRACE_REPORT) {
++ /*
++ * We only got here to process utrace->resume.
++ * Despite no callbacks, this report is not spurious.
++ */
++ report.action = resume;
++ report.spurious = false;
++ finish_resume_report(task, utrace, &report);
++ return -1;
++ } else if (!(task->utrace_flags & UTRACE_EVENT(QUIESCE))) {
++ /*
++ * We only got here to clear utrace->signal_handler.
++ */
++ return -1;
++ }
++
++ /*
++ * Do a reporting pass for no signal, just for EVENT(QUIESCE).
++ * The engine callbacks can fill in *info and *return_ka.
++ * We'll pass NULL for the @orig_ka argument to indicate
++ * that there was no original signal.
++ */
++ event = 0;
++ ka = NULL;
++ memset(return_ka, 0, sizeof *return_ka);
++ } else if (!(task->utrace_flags & UTRACE_EVENT_SIGNAL_ALL) ||
++ unlikely(task->signal->group_stop_count)) {
++ /*
++ * If no engine is interested in intercepting signals or
++ * we must stop, let the caller just dequeue them normally
++ * or participate in group-stop.
++ */
++ return 0;
++ } else {
++ /*
++ * Steal the next signal so we can let tracing engines
++ * examine it. From the signal number and sigaction,
++ * determine what normal delivery would do. If no
++ * engine perturbs it, we'll do that by returning the
++ * signal number after setting *return_ka.
++ */
++ signr = dequeue_signal(task, &task->blocked, info);
++ if (signr == 0)
++ return signr;
++ BUG_ON(signr != info->si_signo);
++
++ ka = &task->sighand->action[signr - 1];
++ *return_ka = *ka;
++
++ /*
++ * We are never allowed to interfere with SIGKILL.
++ * Just punt after filling in *return_ka for our caller.
++ */
++ if (signr == SIGKILL)
++ return signr;
++
++ if (ka->sa.sa_handler == SIG_IGN) {
++ event = UTRACE_EVENT(SIGNAL_IGN);
++ report.result = UTRACE_SIGNAL_IGN;
++ } else if (ka->sa.sa_handler != SIG_DFL) {
++ event = UTRACE_EVENT(SIGNAL);
++ report.result = UTRACE_SIGNAL_DELIVER;
++ } else if (sig_kernel_coredump(signr)) {
++ event = UTRACE_EVENT(SIGNAL_CORE);
++ report.result = UTRACE_SIGNAL_CORE;
++ } else if (sig_kernel_ignore(signr)) {
++ event = UTRACE_EVENT(SIGNAL_IGN);
++ report.result = UTRACE_SIGNAL_IGN;
++ } else if (signr == SIGSTOP) {
++ event = UTRACE_EVENT(SIGNAL_STOP);
++ report.result = UTRACE_SIGNAL_STOP;
++ } else if (sig_kernel_stop(signr)) {
++ event = UTRACE_EVENT(SIGNAL_STOP);
++ report.result = UTRACE_SIGNAL_TSTP;
++ } else {
++ event = UTRACE_EVENT(SIGNAL_TERM);
++ report.result = UTRACE_SIGNAL_TERM;
++ }
++
++ /*
++ * Now that we know what event type this signal is, we
++ * can short-circuit if no engines care about those.
++ */
++ if ((task->utrace_flags & (event | UTRACE_EVENT(QUIESCE))) == 0)
++ return signr;
++
++ /*
++ * We have some interested engines, so tell them about
++ * the signal and let them change its disposition.
++ */
++ spin_unlock_irq(&task->sighand->siglock);
++ }
++
++ /*
++ * This reporting pass chooses what signal disposition we'll act on.
++ */
++ list_for_each_entry(engine, &utrace->attached, entry) {
++ /*
++ * See start_callback() comment about this barrier.
++ */
++ utrace->reporting = engine;
++ smp_mb();
++
++ /*
++ * This pairs with the barrier in mark_engine_detached(),
++ * see start_callback() comments.
++ */
++ want = engine->flags;
++ smp_rmb();
++ ops = engine->ops;
++
++ if ((want & (event | UTRACE_EVENT(QUIESCE))) == 0) {
++ utrace->reporting = NULL;
++ continue;
++ }
++
++ if (ops->report_signal)
++ ret = (*ops->report_signal)(
++ report.result | report.action, engine,
++ regs, info, ka, return_ka);
++ else
++ ret = (report.result | (*ops->report_quiesce)(
++ report.action, engine, event));
++
++ /*
++ * Avoid a tight loop reporting again and again if some
++ * engine is too stupid.
++ */
++ switch (utrace_resume_action(ret)) {
++ default:
++ break;
++ case UTRACE_INTERRUPT:
++ case UTRACE_REPORT:
++ ret = (ret & ~UTRACE_RESUME_MASK) | UTRACE_RESUME;
++ break;
++ }
++
++ finish_callback(task, utrace, &report, engine, ret);
++ }
++
++ /*
++ * We express the chosen action to the signals code in terms
++ * of a representative signal whose default action does it.
++ * Our caller uses our return value (signr) to decide what to
++ * do, but uses info->si_signo as the signal number to report.
++ */
++ switch (utrace_signal_action(report.result)) {
++ case UTRACE_SIGNAL_TERM:
++ signr = SIGTERM;
++ break;
++
++ case UTRACE_SIGNAL_CORE:
++ signr = SIGQUIT;
++ break;
++
++ case UTRACE_SIGNAL_STOP:
++ signr = SIGSTOP;
++ break;
++
++ case UTRACE_SIGNAL_TSTP:
++ signr = SIGTSTP;
++ break;
++
++ case UTRACE_SIGNAL_DELIVER:
++ signr = info->si_signo;
++
++ if (return_ka->sa.sa_handler == SIG_DFL) {
++ /*
++ * We'll do signr's normal default action.
++ * For ignore, we'll fall through below.
++ * For stop/death, break locks and returns it.
++ */
++ if (likely(signr) && !sig_kernel_ignore(signr))
++ break;
++ } else if (return_ka->sa.sa_handler != SIG_IGN &&
++ likely(signr)) {
++ /*
++ * Complete the bookkeeping after the report.
++ * The handler will run. If an engine wanted to
++ * stop or step, then make sure we do another
++ * report after signal handler setup.
++ */
++ if (report.action != UTRACE_RESUME)
++ report.action = UTRACE_INTERRUPT;
++ finish_report(task, utrace, &report, true);
++
++ if (unlikely(report.result & UTRACE_SIGNAL_HOLD))
++ push_back_signal(task, info);
++ else
++ spin_lock_irq(&task->sighand->siglock);
++
++ /*
++ * We do the SA_ONESHOT work here since the
++ * normal path will only touch *return_ka now.
++ */
++ if (unlikely(return_ka->sa.sa_flags & SA_ONESHOT)) {
++ return_ka->sa.sa_flags &= ~SA_ONESHOT;
++ if (likely(valid_signal(signr))) {
++ ka = &task->sighand->action[signr - 1];
++ ka->sa.sa_handler = SIG_DFL;
++ }
++ }
++
++ return signr;
++ }
++
++ /* Fall through for an ignored signal. */
++
++ case UTRACE_SIGNAL_IGN:
++ case UTRACE_SIGNAL_REPORT:
++ default:
++ /*
++ * If the signal is being ignored, then we are on the way
++ * directly back to user mode. We can stop here, or step,
++ * as in utrace_resume(), above. After we've dealt with that,
++ * our caller will relock and come back through here.
++ */
++ finish_resume_report(task, utrace, &report);
++
++ if (unlikely(fatal_signal_pending(task))) {
++ /*
++ * The only reason we woke up now was because of a
++ * SIGKILL. Don't do normal dequeuing in case it
++ * might get a signal other than SIGKILL. That would
++ * perturb the death state so it might differ from
++ * what the debugger would have allowed to happen.
++ * Instead, pluck out just the SIGKILL to be sure
++ * we'll die immediately with nothing else different
++ * from the quiescent state the debugger wanted us in.
++ */
++ sigset_t sigkill_only;
++ siginitsetinv(&sigkill_only, sigmask(SIGKILL));
++ spin_lock_irq(&task->sighand->siglock);
++ signr = dequeue_signal(task, &sigkill_only, info);
++ BUG_ON(signr != SIGKILL);
++ *return_ka = task->sighand->action[SIGKILL - 1];
++ return signr;
++ }
++
++ if (unlikely(report.result & UTRACE_SIGNAL_HOLD)) {
++ push_back_signal(task, info);
++ spin_unlock_irq(&task->sighand->siglock);
++ }
++
++ return -1;
++ }
++
++ /*
++ * Complete the bookkeeping after the report.
++ * This sets utrace->resume if UTRACE_STOP was used.
++ */
++ finish_report(task, utrace, &report, true);
++
++ return_ka->sa.sa_handler = SIG_DFL;
++
++ /*
++ * If this signal is fatal, si_signo gets through as exit_code.
++ * We can't allow a completely bogus value there or else core
++ * kernel code can freak out. (If an engine wants to control
++ * the exit_code value exactly, it can do so in report_exit.)
++ * We'll produce a big complaint in dmesg, but won't crash.
++ * That's nicer for debugging your utrace engine.
++ */
++ if (unlikely(info->si_signo & 0x80)) {
++ WARN(1, "utrace engine left bogus si_signo value!");
++ info->si_signo = SIGTRAP;
++ }
++
++ if (unlikely(report.result & UTRACE_SIGNAL_HOLD))
++ push_back_signal(task, info);
++ else
++ spin_lock_irq(&task->sighand->siglock);
++
++ if (sig_kernel_stop(signr))
++ task->signal->flags |= SIGNAL_STOP_DEQUEUED;
++
++ return signr;
++}
++
++/*
++ * This gets called after a signal handler has been set up.
++ * We set a flag so the next report knows it happened.
++ * If we're already stepping, make sure we do a report_signal.
++ * If not, make sure we get into utrace_resume() where we can
++ * clear the signal_handler flag before resuming.
++ */
++void utrace_signal_handler(struct task_struct *task, int stepping)
++{
++ struct utrace *utrace = task_utrace_struct(task);
++
++ spin_lock(&utrace->lock);
++
++ utrace->signal_handler = 1;
++ if (utrace->resume > UTRACE_INTERRUPT) {
++ if (stepping) {
++ utrace->resume = UTRACE_INTERRUPT;
++ set_tsk_thread_flag(task, TIF_SIGPENDING);
++ } else if (utrace->resume == UTRACE_RESUME) {
++ set_tsk_thread_flag(task, TIF_NOTIFY_RESUME);
++ }
++ }
++
++ spin_unlock(&utrace->lock);
++}
++
++/**
++ * utrace_prepare_examine - prepare to examine thread state
++ * @target: thread of interest, a &struct task_struct pointer
++ * @engine: engine pointer returned by utrace_attach_task()
++ * @exam: temporary state, a &struct utrace_examiner pointer
++ *
++ * This call prepares to safely examine the thread @target using
++ * &struct user_regset calls, or direct access to thread-synchronous fields.
++ *
++ * When @target is current, this call is superfluous. When @target is
++ * another thread, it must be held stopped via %UTRACE_STOP by @engine.
++ *
++ * This call may block the caller until @target stays stopped, so it must
++ * be called only after the caller is sure @target is about to unschedule.
++ * This means a zero return from a utrace_control() call on @engine giving
++ * %UTRACE_STOP, or a report_quiesce() or report_signal() callback to
++ * @engine that used %UTRACE_STOP in its return value.
++ *
++ * Returns -%ESRCH if @target is dead or -%EINVAL if %UTRACE_STOP was
++ * not used. If @target has started running again despite %UTRACE_STOP
++ * (for %SIGKILL or a spurious wakeup), this call returns -%EAGAIN.
++ *
++ * When this call returns zero, it's safe to use &struct user_regset
++ * calls and task_user_regset_view() on @target and to examine some of
++ * its fields directly. When the examination is complete, a
++ * utrace_finish_examine() call must follow to check whether it was
++ * completed safely.
++ */
++int utrace_prepare_examine(struct task_struct *target,
++ struct utrace_engine *engine,
++ struct utrace_examiner *exam)
++{
++ int ret = 0;
++
++ if (unlikely(target == current))
++ return 0;
++
++ rcu_read_lock();
++ if (unlikely(!engine_wants_stop(engine)))
++ ret = -EINVAL;
++ else if (unlikely(target->exit_state))
++ ret = -ESRCH;
++ else {
++ exam->state = target->state;
++ if (unlikely(exam->state == TASK_RUNNING))
++ ret = -EAGAIN;
++ else
++ get_task_struct(target);
++ }
++ rcu_read_unlock();
++
++ if (likely(!ret)) {
++ exam->ncsw = wait_task_inactive(target, exam->state);
++ put_task_struct(target);
++ if (unlikely(!exam->ncsw))
++ ret = -EAGAIN;
++ }
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(utrace_prepare_examine);
++
++/**
++ * utrace_finish_examine - complete an examination of thread state
++ * @target: thread of interest, a &struct task_struct pointer
++ * @engine: engine pointer returned by utrace_attach_task()
++ * @exam: pointer passed to utrace_prepare_examine() call
++ *
++ * This call completes an examination on the thread @target begun by a
++ * paired utrace_prepare_examine() call with the same arguments that
++ * returned success (zero).
++ *
++ * When @target is current, this call is superfluous. When @target is
++ * another thread, this returns zero if @target has remained unscheduled
++ * since the paired utrace_prepare_examine() call returned zero.
++ *
++ * When this returns an error, any examination done since the paired
++ * utrace_prepare_examine() call is unreliable and the data extracted
++ * should be discarded. The error is -%EINVAL if @engine is not
++ * keeping @target stopped, or -%EAGAIN if @target woke up unexpectedly.
++ */
++int utrace_finish_examine(struct task_struct *target,
++ struct utrace_engine *engine,
++ struct utrace_examiner *exam)
++{
++ int ret = 0;
++
++ if (unlikely(target == current))
++ return 0;
++
++ rcu_read_lock();
++ if (unlikely(!engine_wants_stop(engine)))
++ ret = -EINVAL;
++ else if (unlikely(target->state != exam->state))
++ ret = -EAGAIN;
++ else
++ get_task_struct(target);
++ rcu_read_unlock();
++
++ if (likely(!ret)) {
++ unsigned long ncsw = wait_task_inactive(target, exam->state);
++ if (unlikely(ncsw != exam->ncsw))
++ ret = -EAGAIN;
++ put_task_struct(target);
++ }
++
++ return ret;
++}
++EXPORT_SYMBOL_GPL(utrace_finish_examine);
++
++/*
++ * This is declared in linux/regset.h and defined in machine-dependent
++ * code. We put the export here to ensure no machine forgets it.
++ */
++EXPORT_SYMBOL_GPL(task_user_regset_view);
++
++/*
++ * Called with rcu_read_lock() held.
++ */
++void task_utrace_proc_status(struct seq_file *m, struct task_struct *p)
++{
++ seq_printf(m, "Utrace:\t%lx\n", p->utrace_flags);
++}
diff --git a/linux-2.6-v4l-dvb-add-kworld-a340-support.patch b/linux-2.6-v4l-dvb-add-kworld-a340-support.patch
new file mode 100644
index 0000000..196e4da
--- /dev/null
+++ b/linux-2.6-v4l-dvb-add-kworld-a340-support.patch
@@ -0,0 +1,151 @@
+[PATCH] dvb: add support for kworld 340u and ub435-q to em28xx-dvb
+
+This adds support for the KWorld PlusTV 340U and KWorld UB345-Q ATSC
+sticks, which are really the same device. The sticks have an eMPIA
+em2870 usb bridge chipset, an LG Electronics LGDT3304 ATSC/QAM
+demodulator and an NXP TDA18271HD tuner -- early versions of the 340U
+have a a TDA18271HD/C1, later models and the UB435-Q have a C2.
+
+The stick has been tested succesfully with both VSB_8 and QAM_256 signals.
+Its using lgdt3304 support added to the lgdt3305 driver by a prior patch,
+rather than the current lgdt3304 driver, as its severely lacking in
+functionality by comparison (see said patch for details).
+
+Signed-off-by: Jarod Wilson <jarod@redhat.com>
+
+---
+Index: linux-2.6.32.noarch/drivers/media/video/em28xx/em28xx-cards.c
+===================================================================
+--- linux-2.6.32.noarch.orig/drivers/media/video/em28xx/em28xx-cards.c
++++ linux-2.6.32.noarch/drivers/media/video/em28xx/em28xx-cards.c
+@@ -157,6 +157,22 @@ static struct em28xx_reg_seq evga_indtub
+ { -1, -1, -1, -1},
+ };
+
++/*
++ * KWorld PlusTV 340U and UB435-Q (ATSC) GPIOs map:
++ * EM_GPIO_0 - currently unknown
++ * EM_GPIO_1 - LED disable/enable (1 = off, 0 = on)
++ * EM_GPIO_2 - currently unknown
++ * EM_GPIO_3 - currently unknown
++ * EM_GPIO_4 - TDA18271HD/C1 tuner (1 = active, 0 = in reset)
++ * EM_GPIO_5 - LGDT3304 ATSC/QAM demod (1 = active, 0 = in reset)
++ * EM_GPIO_6 - currently unknown
++ * EM_GPIO_7 - currently unknown
++ */
++static struct em28xx_reg_seq kworld_a340_digital[] = {
++ {EM28XX_R08_GPIO, 0x6d, ~EM_GPIO_4, 10},
++ { -1, -1, -1, -1},
++};
++
+ /* Pinnacle Hybrid Pro eb1a:2881 */
+ static struct em28xx_reg_seq pinnacle_hybrid_pro_analog[] = {
+ {EM28XX_R08_GPIO, 0xfd, ~EM_GPIO_4, 10},
+@@ -1587,6 +1603,16 @@ struct em28xx_board em28xx_boards[] = {
+ .tuner_gpio = reddo_dvb_c_usb_box,
+ .has_dvb = 1,
+ },
++ /* 1b80:a340 - Empia EM2870, NXP TDA18271HD and LG DT3304, sold
++ * initially as the KWorld PlusTV 340U, then as the UB435-Q.
++ * Early variants have a TDA18271HD/C1, later ones a TDA18271HD/C2 */
++ [EM2870_BOARD_KWORLD_A340] = {
++ .name = "KWorld PlusTV 340U or UB435-Q (ATSC)",
++ .tuner_type = TUNER_ABSENT, /* Digital-only TDA18271HD */
++ .has_dvb = 1,
++ .dvb_gpio = kworld_a340_digital,
++ .tuner_gpio = default_tuner_gpio,
++ },
+ };
+ const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
+
+@@ -1704,6 +1730,8 @@ struct usb_device_id em28xx_id_table[] =
+ .driver_info = EM2820_BOARD_IODATA_GVMVP_SZ },
+ { USB_DEVICE(0xeb1a, 0x50a6),
+ .driver_info = EM2860_BOARD_GADMEI_UTV330 },
++ { USB_DEVICE(0x1b80, 0xa340),
++ .driver_info = EM2870_BOARD_KWORLD_A340 },
+ { },
+ };
+ MODULE_DEVICE_TABLE(usb, em28xx_id_table);
+Index: linux-2.6.32.noarch/drivers/media/video/em28xx/em28xx-dvb.c
+===================================================================
+--- linux-2.6.32.noarch.orig/drivers/media/video/em28xx/em28xx-dvb.c
++++ linux-2.6.32.noarch/drivers/media/video/em28xx/em28xx-dvb.c
+@@ -29,11 +29,13 @@
+ #include "tuner-simple.h"
+
+ #include "lgdt330x.h"
++#include "lgdt3305.h"
+ #include "zl10353.h"
+ #include "s5h1409.h"
+ #include "mt352.h"
+ #include "mt352_priv.h" /* FIXME */
+ #include "tda1002x.h"
++#include "tda18271.h"
+
+ MODULE_DESCRIPTION("driver for em28xx based DVB cards");
+ MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+@@ -230,6 +232,18 @@ static struct lgdt330x_config em2880_lgd
+ .demod_chip = LGDT3303,
+ };
+
++static struct lgdt3305_config em2870_lgdt3304_dev = {
++ .i2c_addr = 0x0e,
++ .demod_chip = LGDT3304,
++ .spectral_inversion = 1,
++ .deny_i2c_rptr = 1,
++ .mpeg_mode = LGDT3305_MPEG_PARALLEL,
++ .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE,
++ .tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
++ .vsb_if_khz = 3250,
++ .qam_if_khz = 4000,
++};
++
+ static struct zl10353_config em28xx_zl10353_with_xc3028 = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+@@ -246,6 +260,17 @@ static struct s5h1409_config em28xx_s5h1
+ .mpeg_timing = S5H1409_MPEGTIMING_CONTINOUS_NONINVERTING_CLOCK
+ };
+
++static struct tda18271_std_map kworld_a340_std_map = {
++ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 0,
++ .if_lvl = 1, .rfagc_top = 0x37, },
++ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 1,
++ .if_lvl = 1, .rfagc_top = 0x37, },
++};
++
++static struct tda18271_config kworld_a340_config = {
++ .std_map = &kworld_a340_std_map,
++};
++
+ static struct zl10353_config em28xx_zl10353_xc3028_no_i2c_gate = {
+ .demod_address = (0x1e >> 1),
+ .no_tuner = 1,
+@@ -568,6 +593,14 @@ static int dvb_init(struct em28xx *dev)
+ }
+ }
+ break;
++ case EM2870_BOARD_KWORLD_A340:
++ dvb->frontend = dvb_attach(lgdt3305_attach,
++ &em2870_lgdt3304_dev,
++ &dev->i2c_adap);
++ if (dvb->frontend != NULL)
++ dvb_attach(tda18271_attach, dvb->frontend, 0x60,
++ &dev->i2c_adap, &kworld_a340_config);
++ break;
+ default:
+ printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card"
+ " isn't supported yet\n",
+Index: linux-2.6.32.noarch/drivers/media/video/em28xx/em28xx.h
+===================================================================
+--- linux-2.6.32.noarch.orig/drivers/media/video/em28xx/em28xx.h
++++ linux-2.6.32.noarch/drivers/media/video/em28xx/em28xx.h
+@@ -110,6 +110,7 @@
+ #define EM2820_BOARD_SILVERCREST_WEBCAM 71
+ #define EM2861_BOARD_GADMEI_UTV330PLUS 72
+ #define EM2870_BOARD_REDDO_DVB_C_USB_BOX 73
++#define EM2870_BOARD_KWORLD_A340 76
+
+ /* Limits minimum and default number of buffers */
+ #define EM28XX_MIN_BUF 4
diff --git a/linux-2.6-v4l-dvb-add-lgdt3304-support.patch b/linux-2.6-v4l-dvb-add-lgdt3304-support.patch
new file mode 100644
index 0000000..30c5043
--- /dev/null
+++ b/linux-2.6-v4l-dvb-add-lgdt3304-support.patch
@@ -0,0 +1,350 @@
+From b71e18093e2e7f240797875c50c49552722f8825 Mon Sep 17 00:00:00 2001
+From: Jarod Wilson <jarod@redhat.com>
+Date: Mon, 15 Feb 2010 17:13:25 -0500
+Subject: [PATCH 1/2] dvb: add lgdt3304 support to lgdt3305 driver
+
+There's a currently-unused lgdt3304 demod driver, which leaves a lot to
+be desired as far as functionality. The 3304 is unsurprisingly quite
+similar to the 3305, and empirical testing yeilds far better results
+and more complete functionality by merging 3304 support into the 3305
+driver. (For example, the current lgdt3304 driver lacks support for
+signal strength, snr, ucblocks, etc., which we get w/the lgdt3305).
+
+For the moment, not dropping the lgdt3304 driver, and its still up to
+a given device's config setup to choose which demod driver to use, but
+I'd suggest dropping the 3304 driver entirely.
+
+As a follow-up to this patch, I've got another patch that adds support
+for the KWorld PlusTV 340U (ATSC) em2870-based tuner stick, driving
+its lgdt3304 demod via this lgdt3305 driver, which is what I used to
+successfully test this patch with both VSB_8 and QAM_256 signals.
+
+A few pieces are still a touch crude, but I think its a solid start,
+as well as much cleaner and more feature-complete than the existing
+lgdt3304 driver.
+
+Signed-off-by: Jarod Wilson <jarod@redhat.com>
+---
+ drivers/media/dvb/frontends/lgdt3305.c | 206 ++++++++++++++++++++++++++++++--
+ drivers/media/dvb/frontends/lgdt3305.h | 6 +
+ 2 files changed, 203 insertions(+), 9 deletions(-)
+
+diff --git a/drivers/media/dvb/frontends/lgdt3305.c b/drivers/media/dvb/frontends/lgdt3305.c
+index fde8c59..40695e6 100644
+--- a/drivers/media/dvb/frontends/lgdt3305.c
++++ b/drivers/media/dvb/frontends/lgdt3305.c
+@@ -1,5 +1,5 @@
+ /*
+- * Support for LGDT3305 - VSB/QAM
++ * Support for LG Electronics LGDT3304 and LGDT3305 - VSB/QAM
+ *
+ * Copyright (C) 2008, 2009 Michael Krufky <mkrufky@linuxtv.org>
+ *
+@@ -357,7 +357,10 @@ static int lgdt3305_rfagc_loop(struct lgdt3305_state *state,
+ case QAM_256:
+ agcdelay = 0x046b;
+ rfbw = 0x8889;
+- ifbw = 0x8888;
++ if (state->cfg->demod_chip == LGDT3305)
++ ifbw = 0x8888;
++ else
++ ifbw = 0x6666;
+ break;
+ default:
+ return -EINVAL;
+@@ -409,8 +412,18 @@ static int lgdt3305_agc_setup(struct lgdt3305_state *state,
+ lg_dbg("lockdten = %d, acqen = %d\n", lockdten, acqen);
+
+ /* control agc function */
+- lgdt3305_write_reg(state, LGDT3305_AGC_CTRL_4, 0xe1 | lockdten << 1);
+- lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 2, acqen);
++ switch (state->cfg->demod_chip) {
++ case LGDT3304:
++ lgdt3305_write_reg(state, 0x0314, 0xe1 | lockdten << 1);
++ lgdt3305_set_reg_bit(state, 0x030e, 2, acqen);
++ break;
++ case LGDT3305:
++ lgdt3305_write_reg(state, LGDT3305_AGC_CTRL_4, 0xe1 | lockdten << 1);
++ lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 2, acqen);
++ break;
++ default:
++ return -EINVAL;
++ }
+
+ return lgdt3305_rfagc_loop(state, param);
+ }
+@@ -543,6 +556,11 @@ static int lgdt3305_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+ enable ? 0 : 1);
+ }
+
++static int lgdt3304_sleep(struct dvb_frontend *fe)
++{
++ return 0;
++}
++
+ static int lgdt3305_sleep(struct dvb_frontend *fe)
+ {
+ struct lgdt3305_state *state = fe->demodulator_priv;
+@@ -571,6 +589,55 @@ static int lgdt3305_sleep(struct dvb_frontend *fe)
+ return 0;
+ }
+
++static int lgdt3304_init(struct dvb_frontend *fe)
++{
++ struct lgdt3305_state *state = fe->demodulator_priv;
++ int ret;
++
++ static struct lgdt3305_reg lgdt3304_init_data[] = {
++ { .reg = LGDT3305_GEN_CTRL_1, .val = 0x03, },
++ { .reg = 0x000d, .val = 0x02, },
++ { .reg = 0x000e, .val = 0x02, },
++ { .reg = LGDT3305_DGTL_AGC_REF_1, .val = 0x32, },
++ { .reg = LGDT3305_DGTL_AGC_REF_2, .val = 0xc4, },
++ { .reg = LGDT3305_CR_CTR_FREQ_1, .val = 0x00, },
++ { .reg = LGDT3305_CR_CTR_FREQ_2, .val = 0x00, },
++ { .reg = LGDT3305_CR_CTR_FREQ_3, .val = 0x00, },
++ { .reg = LGDT3305_CR_CTR_FREQ_4, .val = 0x00, },
++ { .reg = LGDT3305_CR_CTRL_7, .val = 0xf9, },
++ { .reg = 0x0112, .val = 0x17, },
++ { .reg = 0x0113, .val = 0x15, },
++ { .reg = 0x0114, .val = 0x18, },
++ { .reg = 0x0115, .val = 0xff, },
++ { .reg = 0x0116, .val = 0x3c, },
++ { .reg = 0x0214, .val = 0x67, },
++ { .reg = 0x0424, .val = 0x8d, },
++ { .reg = 0x0427, .val = 0x12, },
++ { .reg = 0x0428, .val = 0x4f, },
++ { .reg = LGDT3305_IFBW_1, .val = 0x80, },
++ { .reg = LGDT3305_IFBW_2, .val = 0x00, },
++ { .reg = 0x030a, .val = 0x08, },
++ { .reg = 0x030b, .val = 0x9b, },
++ { .reg = 0x030d, .val = 0x00, },
++ { .reg = 0x030e, .val = 0x1c, },
++ { .reg = 0x0314, .val = 0xe1, },
++ { .reg = 0x000d, .val = 0x82, },
++ { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, },
++ { .reg = LGDT3305_TP_CTRL_1, .val = 0x5b, },
++ };
++
++ lg_dbg("\n");
++
++ ret = lgdt3305_write_regs(state, lgdt3304_init_data,
++ ARRAY_SIZE(lgdt3304_init_data));
++ if (lg_fail(ret))
++ goto fail;
++
++ ret = lgdt3305_soft_reset(state);
++fail:
++ return ret;
++}
++
+ static int lgdt3305_init(struct dvb_frontend *fe)
+ {
+ struct lgdt3305_state *state = fe->demodulator_priv;
+@@ -639,6 +706,88 @@ fail:
+ return ret;
+ }
+
++static int lgdt3304_set_parameters(struct dvb_frontend *fe,
++ struct dvb_frontend_parameters *param)
++{
++ struct lgdt3305_state *state = fe->demodulator_priv;
++ int ret;
++
++ lg_dbg("(%d, %d)\n", param->frequency, param->u.vsb.modulation);
++
++ if (fe->ops.tuner_ops.set_params) {
++ ret = fe->ops.tuner_ops.set_params(fe, param);
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0);
++ if (lg_fail(ret))
++ goto fail;
++ state->current_frequency = param->frequency;
++ }
++
++ ret = lgdt3305_set_modulation(state, param);
++ if (lg_fail(ret))
++ goto fail;
++
++ ret = lgdt3305_passband_digital_agc(state, param);
++ if (lg_fail(ret))
++ goto fail;
++
++ ret = lgdt3305_agc_setup(state, param);
++ if (lg_fail(ret))
++ goto fail;
++
++ /* reg 0x030d is 3304-only... seen in vsb and qam usbsnoops... */
++ switch (param->u.vsb.modulation) {
++ case VSB_8:
++ lgdt3305_write_reg(state, 0x030d, 0x00);
++#if 1
++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_1, 0x4f);
++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_2, 0x0c);
++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_3, 0xac);
++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_4, 0xba);
++#endif
++ break;
++ case QAM_64:
++ case QAM_256:
++ lgdt3305_write_reg(state, 0x030d, 0x14);
++#if 1
++ ret = lgdt3305_set_if(state, param);
++ if (lg_fail(ret))
++ goto fail;
++#endif
++ break;
++ default:
++ return -EINVAL;
++ }
++
++#if 0
++ /* the set_if vsb formula doesn't work for the 3304, we end up sending
++ * 0x40851e07 instead of 0x4f0cacba (which works back to 94050, rather
++ * than 3250, in the case of the kworld 340u) */
++ ret = lgdt3305_set_if(state, param);
++ if (lg_fail(ret))
++ goto fail;
++#endif
++
++ ret = lgdt3305_spectral_inversion(state, param,
++ state->cfg->spectral_inversion
++ ? 1 : 0);
++ if (lg_fail(ret))
++ goto fail;
++
++ state->current_modulation = param->u.vsb.modulation;
++
++ ret = lgdt3305_mpeg_mode(state, state->cfg->mpeg_mode);
++ if (lg_fail(ret))
++ goto fail;
++
++ /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */
++ ret = lgdt3305_mpeg_mode_polarity(state,
++ state->cfg->tpclk_edge,
++ state->cfg->tpvalid_polarity);
++fail:
++ return ret;
++}
++
+ static int lgdt3305_set_parameters(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *param)
+ {
+@@ -847,6 +996,10 @@ static int lgdt3305_read_status(struct dvb_frontend *fe, fe_status_t *status)
+ switch (state->current_modulation) {
+ case QAM_256:
+ case QAM_64:
++#if 0 /* needed w/3304 to set FE_HAS_SIGNAL */
++ if (cr_lock)
++ *status |= FE_HAS_SIGNAL;
++#endif
+ ret = lgdt3305_read_fec_lock_status(state, &fec_lock);
+ if (lg_fail(ret))
+ goto fail;
+@@ -992,6 +1145,7 @@ static void lgdt3305_release(struct dvb_frontend *fe)
+ kfree(state);
+ }
+
++static struct dvb_frontend_ops lgdt3304_ops;
+ static struct dvb_frontend_ops lgdt3305_ops;
+
+ struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config,
+@@ -1012,11 +1166,21 @@ struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config,
+ state->cfg = config;
+ state->i2c_adap = i2c_adap;
+
+- memcpy(&state->frontend.ops, &lgdt3305_ops,
+- sizeof(struct dvb_frontend_ops));
++ switch (config->demod_chip) {
++ case LGDT3304:
++ memcpy(&state->frontend.ops, &lgdt3304_ops,
++ sizeof(struct dvb_frontend_ops));
++ break;
++ case LGDT3305:
++ memcpy(&state->frontend.ops, &lgdt3305_ops,
++ sizeof(struct dvb_frontend_ops));
++ break;
++ default:
++ goto fail;
++ }
+ state->frontend.demodulator_priv = state;
+
+- /* verify that we're talking to a lg dt3305 */
++ /* verify that we're talking to a lg dt3304/5 */
+ ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_2, &val);
+ if ((lg_fail(ret)) | (val == 0))
+ goto fail;
+@@ -1035,12 +1199,36 @@ struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config,
+
+ return &state->frontend;
+ fail:
+- lg_warn("unable to detect LGDT3305 hardware\n");
++ lg_warn("unable to detect %s hardware\n",
++ config->demod_chip ? "LGDT3304" : "LGDT3305");
+ kfree(state);
+ return NULL;
+ }
+ EXPORT_SYMBOL(lgdt3305_attach);
+
++static struct dvb_frontend_ops lgdt3304_ops = {
++ .info = {
++ .name = "LG Electronics LGDT3304 VSB/QAM Frontend",
++ .type = FE_ATSC,
++ .frequency_min = 54000000,
++ .frequency_max = 858000000,
++ .frequency_stepsize = 62500,
++ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
++ },
++ .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl,
++ .init = lgdt3304_init,
++ .sleep = lgdt3304_sleep,
++ .set_frontend = lgdt3304_set_parameters,
++ .get_frontend = lgdt3305_get_frontend,
++ .get_tune_settings = lgdt3305_get_tune_settings,
++ .read_status = lgdt3305_read_status,
++ .read_ber = lgdt3305_read_ber,
++ .read_signal_strength = lgdt3305_read_signal_strength,
++ .read_snr = lgdt3305_read_snr,
++ .read_ucblocks = lgdt3305_read_ucblocks,
++ .release = lgdt3305_release,
++};
++
+ static struct dvb_frontend_ops lgdt3305_ops = {
+ .info = {
+ .name = "LG Electronics LGDT3305 VSB/QAM Frontend",
+@@ -1064,7 +1252,7 @@ static struct dvb_frontend_ops lgdt3305_ops = {
+ .release = lgdt3305_release,
+ };
+
+-MODULE_DESCRIPTION("LG Electronics LGDT3305 ATSC/QAM-B Demodulator Driver");
++MODULE_DESCRIPTION("LG Electronics LGDT3304/5 ATSC/QAM-B Demodulator Driver");
+ MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
+ MODULE_LICENSE("GPL");
+ MODULE_VERSION("0.1");
+diff --git a/drivers/media/dvb/frontends/lgdt3305.h b/drivers/media/dvb/frontends/lgdt3305.h
+index 9cb11c9..a7f30c2 100644
+--- a/drivers/media/dvb/frontends/lgdt3305.h
++++ b/drivers/media/dvb/frontends/lgdt3305.h
+@@ -41,6 +41,11 @@ enum lgdt3305_tp_valid_polarity {
+ LGDT3305_TP_VALID_HIGH = 1,
+ };
+
++enum lgdt_demod_chip_type {
++ LGDT3305 = 0,
++ LGDT3304 = 1,
++};
++
+ struct lgdt3305_config {
+ u8 i2c_addr;
+
+@@ -65,6 +70,7 @@ struct lgdt3305_config {
+ enum lgdt3305_mpeg_mode mpeg_mode;
+ enum lgdt3305_tp_clock_edge tpclk_edge;
+ enum lgdt3305_tp_valid_polarity tpvalid_polarity;
++ enum lgdt_demod_chip_type demod_chip;
+ };
+
+ #if defined(CONFIG_DVB_LGDT3305) || (defined(CONFIG_DVB_LGDT3305_MODULE) && \
+--
+1.6.6
+
diff --git a/linux-2.6-v4l-dvb-experimental.patch b/linux-2.6-v4l-dvb-experimental.patch
new file mode 100644
index 0000000..1a26e7e
--- /dev/null
+++ b/linux-2.6-v4l-dvb-experimental.patch
@@ -0,0 +1,14750 @@
+Hans Verkuil (2):
+ V4L/DVB (10982): cx231xx: fix compile warning
+ V4L/DVB (10989): cx25840: cx23885 detection was broken
+
+Mauro Carvalho Chehab (12):
+ V4L/DVB (10953): cx25840: Fix CodingStyle errors introduced by the last patch
+ V4L/DVB (10955): cx231xx: CodingStyle automatic fixes with Lindent
+ V4L/DVB (10956): cx231xx: First series of manual CodingStyle fixes
+ V4L/DVB (10957a): cx231xx: Fix compilation breakage
+ V4L/DVB (11130): cx231xx: fix an inverted logic at vidioc_streamoff
+ V4L/DVB (11131): cx231xx: avoid trying to access unfilled dev struct
+ V4L/DVB (11132): cx231xx: usb probe cleanups
+ V4L/DVB (11133): cx231xx: don't print pcb config debug messages by default
+ V4L/DVB (11134): cx231xx: dmesg cleanup
+ V4L/DVB (11135): cx231xx: use usb_make_path() for bus_info
+ V4L/DVB (11250): cx231xx: Fix Kconfig help items
+ Merge branch 'next' of ../pending into Fedora
+
+Sri Deevi (7):
+ V4L/DVB (10952): cx25840: prepare it to be used by cx231xx module
+ V4L/DVB (10954): Add cx231xx USB driver
+ V4L/DVB (10957): cx231xx: Fix CodingStyle
+ V4L/DVB (10958): cx231xx: some additional CodingStyle and minor fixes
+ V4L/DVB (11038): Fix the issue with audio module & correction of Names
+ V4L/DVB (11128): cx231xx: convert the calls to subdev format
+ V4L/DVB (11129): cx231xx: Use generic names for each device block
+
+Uri Shkolnik (2):
+ V4L/DVB (11239): sdio: add cards ids for sms (Siano Mobile Silicon) MDTV receivers
+ V4L/DVB (11240): siano: add high level SDIO interface driver for SMS based cards
+
+diff --git a/drivers/media/dvb/siano/smssdio.c b/drivers/media/dvb/siano/smssdio.c
+new file mode 100644
+index 0000000..31ba8c5
+--- /dev/null
++++ b/drivers/media/dvb/siano/smssdio.c
+@@ -0,0 +1,356 @@
++/*
++ * smssdio.c - Siano 1xxx SDIO interface driver
++ *
++ * Copyright 2008 Pierre Ossman
++ *
++ * Based on code by Siano Mobile Silicon, Inc.,
++ * Copyright (C) 2006-2008, Uri Shkolnik
++ *
++ * 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 hardware is a bit odd in that all transfers should be done
++ * to/from the SMSSDIO_DATA register, yet the "increase address" bit
++ * always needs to be set.
++ *
++ * Also, buffers from the card are always aligned to 128 byte
++ * boundaries.
++ */
++
++/*
++ * General cleanup notes:
++ *
++ * - only typedefs should be name *_t
++ *
++ * - use ERR_PTR and friends for smscore_register_device()
++ *
++ * - smscore_getbuffer should zero fields
++ *
++ * Fix stop command
++ */
++
++#include <linux/moduleparam.h>
++#include <linux/firmware.h>
++#include <linux/delay.h>
++#include <linux/mmc/card.h>
++#include <linux/mmc/sdio_func.h>
++#include <linux/mmc/sdio_ids.h>
++
++#include "smscoreapi.h"
++#include "sms-cards.h"
++
++/* Registers */
++
++#define SMSSDIO_DATA 0x00
++#define SMSSDIO_INT 0x04
++
++static const struct sdio_device_id smssdio_ids[] = {
++ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR),
++ .driver_data = SMS1XXX_BOARD_SIANO_STELLAR},
++ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_A0),
++ .driver_data = SMS1XXX_BOARD_SIANO_NOVA_A},
++ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_B0),
++ .driver_data = SMS1XXX_BOARD_SIANO_NOVA_B},
++ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VEGA_A0),
++ .driver_data = SMS1XXX_BOARD_SIANO_VEGA},
++ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE),
++ .driver_data = SMS1XXX_BOARD_SIANO_VEGA},
++ { /* end: all zeroes */ },
++};
++
++MODULE_DEVICE_TABLE(sdio, smssdio_ids);
++
++struct smssdio_device {
++ struct sdio_func *func;
++
++ struct smscore_device_t *coredev;
++
++ struct smscore_buffer_t *split_cb;
++};
++
++/*******************************************************************/
++/* Siano core callbacks */
++/*******************************************************************/
++
++static int smssdio_sendrequest(void *context, void *buffer, size_t size)
++{
++ int ret;
++ struct smssdio_device *smsdev;
++
++ smsdev = context;
++
++ sdio_claim_host(smsdev->func);
++
++ while (size >= smsdev->func->cur_blksize) {
++ ret = sdio_write_blocks(smsdev->func, SMSSDIO_DATA, buffer, 1);
++ if (ret)
++ goto out;
++
++ buffer += smsdev->func->cur_blksize;
++ size -= smsdev->func->cur_blksize;
++ }
++
++ if (size) {
++ ret = sdio_write_bytes(smsdev->func, SMSSDIO_DATA,
++ buffer, size);
++ if (ret)
++ goto out;
++ }
++
++out:
++ sdio_release_host(smsdev->func);
++
++ return ret;
++}
++
++/*******************************************************************/
++/* SDIO callbacks */
++/*******************************************************************/
++
++static void smssdio_interrupt(struct sdio_func *func)
++{
++ int ret, isr;
++
++ struct smssdio_device *smsdev;
++ struct smscore_buffer_t *cb;
++ struct SmsMsgHdr_ST *hdr;
++ size_t size;
++
++ smsdev = sdio_get_drvdata(func);
++
++ /*
++ * The interrupt register has no defined meaning. It is just
++ * a way of turning of the level triggered interrupt.
++ */
++ isr = sdio_readb(func, SMSSDIO_INT, &ret);
++ if (ret) {
++ dev_err(&smsdev->func->dev,
++ "Unable to read interrupt register!\n");
++ return;
++ }
++
++ if (smsdev->split_cb == NULL) {
++ cb = smscore_getbuffer(smsdev->coredev);
++ if (!cb) {
++ dev_err(&smsdev->func->dev,
++ "Unable to allocate data buffer!\n");
++ return;
++ }
++
++ ret = sdio_read_blocks(smsdev->func, cb->p, SMSSDIO_DATA, 1);
++ if (ret) {
++ dev_err(&smsdev->func->dev,
++ "Error %d reading initial block!\n", ret);
++ return;
++ }
++
++ hdr = cb->p;
++
++ if (hdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG) {
++ smsdev->split_cb = cb;
++ return;
++ }
++
++ size = hdr->msgLength - smsdev->func->cur_blksize;
++ } else {
++ cb = smsdev->split_cb;
++ hdr = cb->p;
++
++ size = hdr->msgLength - sizeof(struct SmsMsgHdr_ST);
++
++ smsdev->split_cb = NULL;
++ }
++
++ if (hdr->msgLength > smsdev->func->cur_blksize) {
++ void *buffer;
++
++ size = ALIGN(size, 128);
++ buffer = cb->p + hdr->msgLength;
++
++ BUG_ON(smsdev->func->cur_blksize != 128);
++
++ /*
++ * First attempt to transfer all of it in one go...
++ */
++ ret = sdio_read_blocks(smsdev->func, buffer,
++ SMSSDIO_DATA, size / 128);
++ if (ret && ret != -EINVAL) {
++ smscore_putbuffer(smsdev->coredev, cb);
++ dev_err(&smsdev->func->dev,
++ "Error %d reading data from card!\n", ret);
++ return;
++ }
++
++ /*
++ * ..then fall back to one block at a time if that is
++ * not possible...
++ *
++ * (we have to do this manually because of the
++ * problem with the "increase address" bit)
++ */
++ if (ret == -EINVAL) {
++ while (size) {
++ ret = sdio_read_blocks(smsdev->func,
++ buffer, SMSSDIO_DATA, 1);
++ if (ret) {
++ smscore_putbuffer(smsdev->coredev, cb);
++ dev_err(&smsdev->func->dev,
++ "Error %d reading "
++ "data from card!\n", ret);
++ return;
++ }
++
++ buffer += smsdev->func->cur_blksize;
++ if (size > smsdev->func->cur_blksize)
++ size -= smsdev->func->cur_blksize;
++ else
++ size = 0;
++ }
++ }
++ }
++
++ cb->size = hdr->msgLength;
++ cb->offset = 0;
++
++ smscore_onresponse(smsdev->coredev, cb);
++}
++
++static int smssdio_probe(struct sdio_func *func,
++ const struct sdio_device_id *id)
++{
++ int ret;
++
++ int board_id;
++ struct smssdio_device *smsdev;
++ struct smsdevice_params_t params;
++
++ board_id = id->driver_data;
++
++ smsdev = kzalloc(sizeof(struct smssdio_device), GFP_KERNEL);
++ if (!smsdev)
++ return -ENOMEM;
++
++ smsdev->func = func;
++
++ memset(&params, 0, sizeof(struct smsdevice_params_t));
++
++ params.device = &func->dev;
++ params.buffer_size = 0x5000; /* ?? */
++ params.num_buffers = 22; /* ?? */
++ params.context = smsdev;
++
++ snprintf(params.devpath, sizeof(params.devpath),
++ "sdio\\%s", sdio_func_id(func));
++
++ params.sendrequest_handler = smssdio_sendrequest;
++
++ params.device_type = sms_get_board(board_id)->type;
++
++ if (params.device_type != SMS_STELLAR)
++ params.flags |= SMS_DEVICE_FAMILY2;
++ else {
++ /*
++ * FIXME: Stellar needs special handling...
++ */
++ ret = -ENODEV;
++ goto free;
++ }
++
++ ret = smscore_register_device(&params, &smsdev->coredev);
++ if (ret < 0)
++ goto free;
++
++ smscore_set_board_id(smsdev->coredev, board_id);
++
++ sdio_claim_host(func);
++
++ ret = sdio_enable_func(func);
++ if (ret)
++ goto release;
++
++ ret = sdio_set_block_size(func, 128);
++ if (ret)
++ goto disable;
++
++ ret = sdio_claim_irq(func, smssdio_interrupt);
++ if (ret)
++ goto disable;
++
++ sdio_set_drvdata(func, smsdev);
++
++ sdio_release_host(func);
++
++ ret = smscore_start_device(smsdev->coredev);
++ if (ret < 0)
++ goto reclaim;
++
++ return 0;
++
++reclaim:
++ sdio_claim_host(func);
++ sdio_release_irq(func);
++disable:
++ sdio_disable_func(func);
++release:
++ sdio_release_host(func);
++ smscore_unregister_device(smsdev->coredev);
++free:
++ kfree(smsdev);
++
++ return ret;
++}
++
++static void smssdio_remove(struct sdio_func *func)
++{
++ struct smssdio_device *smsdev;
++
++ smsdev = sdio_get_drvdata(func);
++
++ /* FIXME: racy! */
++ if (smsdev->split_cb)
++ smscore_putbuffer(smsdev->coredev, smsdev->split_cb);
++
++ smscore_unregister_device(smsdev->coredev);
++
++ sdio_claim_host(func);
++ sdio_release_irq(func);
++ sdio_disable_func(func);
++ sdio_release_host(func);
++
++ kfree(smsdev);
++}
++
++static struct sdio_driver smssdio_driver = {
++ .name = "smssdio",
++ .id_table = smssdio_ids,
++ .probe = smssdio_probe,
++ .remove = smssdio_remove,
++};
++
++/*******************************************************************/
++/* Module functions */
++/*******************************************************************/
++
++int smssdio_register(void)
++{
++ int ret = 0;
++
++ printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n");
++ printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n");
++
++ ret = sdio_register_driver(&smssdio_driver);
++
++ return ret;
++}
++
++void smssdio_unregister(void)
++{
++ sdio_unregister_driver(&smssdio_driver);
++}
++
++MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver");
++MODULE_AUTHOR("Pierre Ossman");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
+index 76bad58..58abbe3 100644
+--- a/drivers/media/video/Kconfig
++++ b/drivers/media/video/Kconfig
+@@ -795,6 +795,8 @@ source "drivers/media/video/hdpvr/Kconfig"
+
+ source "drivers/media/video/em28xx/Kconfig"
+
++source "drivers/media/video/cx231xx/Kconfig"
++
+ source "drivers/media/video/usbvision/Kconfig"
+
+ source "drivers/media/video/usbvideo/Kconfig"
+diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
+index b904674..08765d8 100644
+--- a/drivers/media/video/Makefile
++++ b/drivers/media/video/Makefile
+@@ -67,6 +67,7 @@ obj-$(CONFIG_VIDEO_MEYE) += meye.o
+ obj-$(CONFIG_VIDEO_SAA7134) += saa7134/
+ obj-$(CONFIG_VIDEO_CX88) += cx88/
+ obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
++obj-$(CONFIG_VIDEO_CX231XX) += cx231xx/
+ obj-$(CONFIG_VIDEO_USBVISION) += usbvision/
+ obj-$(CONFIG_VIDEO_TVP5150) += tvp5150.o
+ obj-$(CONFIG_VIDEO_TVP514X) += tvp514x.o
+diff --git a/drivers/media/video/cx231xx/Kconfig b/drivers/media/video/cx231xx/Kconfig
+new file mode 100644
+index 0000000..9115654
+--- /dev/null
++++ b/drivers/media/video/cx231xx/Kconfig
+@@ -0,0 +1,35 @@
++config VIDEO_CX231XX
++ tristate "Conexant cx231xx USB video capture support"
++ depends on VIDEO_DEV && I2C && INPUT
++ select VIDEO_TUNER
++ select VIDEO_TVEEPROM
++ select VIDEO_IR
++ select VIDEOBUF_VMALLOC
++ select VIDEO_CX25840
++ select VIDEO_CX231XX_ALSA
++
++ ---help---
++ This is a video4linux driver for Conexant 231xx USB based TV cards.
++
++ To compile this driver as a module, choose M here: the
++ module will be called cx231xx
++
++config VIDEO_CX231XX_ALSA
++ tristate "Conexant Cx231xx ALSA audio module"
++ depends on VIDEO_CX231XX && SND
++ select SND_PCM
++
++ ---help---
++ This is an ALSA driver for Cx231xx USB based TV cards.
++
++ To compile this driver as a module, choose M here: the
++ module will be called cx231xx-alsa
++
++config VIDEO_CX231XX_DVB
++ tristate "DVB/ATSC Support for Cx231xx based TV cards"
++ depends on VIDEO_CX231XX && DVB_CORE
++ select VIDEOBUF_DVB
++ select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMISE
++ ---help---
++ This adds support for DVB cards based on the
++ Conexant cx231xx chips.
+diff --git a/drivers/media/video/cx231xx/Makefile b/drivers/media/video/cx231xx/Makefile
+new file mode 100644
+index 0000000..755dd0c
+--- /dev/null
++++ b/drivers/media/video/cx231xx/Makefile
+@@ -0,0 +1,14 @@
++cx231xx-objs := cx231xx-video.o cx231xx-i2c.o cx231xx-cards.o cx231xx-core.o \
++ cx231xx-avcore.o cx231xx-pcb-cfg.o cx231xx-vbi.o
++
++cx231xx-alsa-objs := cx231xx-audio.o
++
++obj-$(CONFIG_VIDEO_CX231XX) += cx231xx.o
++obj-$(CONFIG_VIDEO_CX231XX_ALSA) += cx231xx-alsa.o
++obj-$(CONFIG_VIDEO_CX231XX_DVB) += cx231xx-dvb.o
++
++EXTRA_CFLAGS += -Idrivers/media/video
++EXTRA_CFLAGS += -Idrivers/media/common/tuners
++EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
++EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
++
+diff --git a/drivers/media/video/cx231xx/cx231xx-audio.c b/drivers/media/video/cx231xx/cx231xx-audio.c
+new file mode 100644
+index 0000000..9ab0628
+--- /dev/null
++++ b/drivers/media/video/cx231xx/cx231xx-audio.c
+@@ -0,0 +1,585 @@
++/*
++ * Conexant Cx231xx audio extension
++ *
++ * Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
++ * Based on em28xx driver
++ *
++ *
++ * 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 <linux/kernel.h>
++#include <linux/usb.h>
++#include <linux/init.h>
++#include <linux/sound.h>
++#include <linux/spinlock.h>
++#include <linux/soundcard.h>
++#include <linux/slab.h>
++#include <linux/vmalloc.h>
++#include <linux/proc_fs.h>
++#include <linux/module.h>
++#include <sound/core.h>
++#include <sound/pcm.h>
++#include <sound/pcm_params.h>
++#include <sound/info.h>
++#include <sound/initval.h>
++#include <sound/control.h>
++#include <media/v4l2-common.h>
++#include "cx231xx.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 "cx231xx-audio %s: " fmt, \
++ __func__, ##arg); \
++ } while (0)
++
++static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
++
++static int cx231xx_isoc_audio_deinit(struct cx231xx *dev)
++{
++ int i;
++
++ dprintk("Stopping isoc\n");
++
++ for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
++ if (dev->adev.urb[i]) {
++ if (!irqs_disabled())
++ usb_kill_urb(dev->adev.urb[i]);
++ else
++ usb_unlink_urb(dev->adev.urb[i]);
++
++ usb_free_urb(dev->adev.urb[i]);
++ dev->adev.urb[i] = NULL;
++
++ kfree(dev->adev.transfer_buffer[i]);
++ dev->adev.transfer_buffer[i] = NULL;
++ }
++ }
++
++ return 0;
++}
++
++static void cx231xx_audio_isocirq(struct urb *urb)
++{
++ struct cx231xx *dev = urb->context;
++ int i;
++ unsigned int oldptr;
++ int period_elapsed = 0;
++ int status;
++ unsigned char *cp;
++ unsigned int stride;
++ struct snd_pcm_substream *substream;
++ struct snd_pcm_runtime *runtime;
++
++ switch (urb->status) {
++ case 0: /* success */
++ case -ETIMEDOUT: /* NAK */
++ break;
++ case -ECONNRESET: /* kill */
++ case -ENOENT:
++ case -ESHUTDOWN:
++ return;
++ default: /* error */
++ dprintk("urb completition error %d.\n", urb->status);
++ break;
++ }
++
++ 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;
++
++ oldptr = dev->adev.hwptr_done_capture;
++ if (oldptr + length >= runtime->buffer_size) {
++ unsigned int cnt;
++
++ cnt = runtime->buffer_size - oldptr;
++ memcpy(runtime->dma_area + oldptr * stride, cp,
++ cnt * stride);
++ memcpy(runtime->dma_area, cp + cnt * stride,
++ length * stride - cnt * stride);
++ } else {
++ memcpy(runtime->dma_area + oldptr * stride, cp,
++ length * stride);
++ }
++
++ snd_pcm_stream_lock(substream);
++
++ 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;
++ }
++ snd_pcm_stream_unlock(substream);
++ }
++ if (period_elapsed)
++ snd_pcm_period_elapsed(substream);
++ }
++ urb->status = 0;
++
++ status = usb_submit_urb(urb, GFP_ATOMIC);
++ if (status < 0) {
++ cx231xx_errdev("resubmit of audio urb failed (error=%i)\n",
++ status);
++ }
++ return;
++}
++
++static int cx231xx_init_audio_isoc(struct cx231xx *dev)
++{
++ int i, errCode;
++ int sb_size;
++
++ cx231xx_info("%s: Starting AUDIO transfers\n", __func__);
++
++ sb_size = CX231XX_NUM_AUDIO_PACKETS * dev->adev.max_pkt_size;
++
++ for (i = 0; i < CX231XX_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(CX231XX_NUM_AUDIO_PACKETS, GFP_ATOMIC);
++ if (!urb) {
++ cx231xx_errdev("usb_alloc_urb failed!\n");
++ for (j = 0; j < i; j++) {
++ usb_free_urb(dev->adev.urb[j]);
++ kfree(dev->adev.transfer_buffer[j]);
++ }
++ return -ENOMEM;
++ }
++
++ urb->dev = dev->udev;
++ urb->context = dev;
++ urb->pipe = usb_rcvisocpipe(dev->udev,
++ dev->adev.end_point_addr);
++ urb->transfer_flags = URB_ISO_ASAP;
++ urb->transfer_buffer = dev->adev.transfer_buffer[i];
++ urb->interval = 1;
++ urb->complete = cx231xx_audio_isocirq;
++ urb->number_of_packets = CX231XX_NUM_AUDIO_PACKETS;
++ urb->transfer_buffer_length = sb_size;
++
++ for (j = k = 0; j < CX231XX_NUM_AUDIO_PACKETS;
++ j++, k += dev->adev.max_pkt_size) {
++ urb->iso_frame_desc[j].offset = k;
++ urb->iso_frame_desc[j].length = dev->adev.max_pkt_size;
++ }
++ dev->adev.urb[i] = urb;
++ }
++
++ for (i = 0; i < CX231XX_AUDIO_BUFS; i++) {
++ errCode = usb_submit_urb(dev->adev.urb[i], GFP_ATOMIC);
++ if (errCode < 0) {
++ cx231xx_isoc_audio_deinit(dev);
++ return errCode;
++ }
++ }
++
++ return errCode;
++}
++
++static int cx231xx_cmd(struct cx231xx *dev, int cmd, int arg)
++{
++ dprintk("%s transfer\n", (dev->adev.capture_stream == STREAM_ON) ?
++ "stop" : "start");
++
++ switch (cmd) {
++ case CX231XX_CAPTURE_STREAM_EN:
++ if (dev->adev.capture_stream == STREAM_OFF && arg == 1) {
++ dev->adev.capture_stream = STREAM_ON;
++ cx231xx_init_audio_isoc(dev);
++ } else if (dev->adev.capture_stream == STREAM_ON && arg == 0) {
++ dev->adev.capture_stream = STREAM_OFF;
++ cx231xx_isoc_audio_deinit(dev);
++ } else {
++ cx231xx_errdev("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("Allocating 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_cx231xx_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_cx231xx_capture_open(struct snd_pcm_substream *substream)
++{
++ struct cx231xx *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");
++
++ if (!dev) {
++ cx231xx_errdev("BUG: cx231xx can't find device struct."
++ " Can't proceed with open\n");
++ return -ENODEV;
++ }
++
++ /* Sets volume, mute, etc */
++ dev->mute = 0;
++
++ /* set alternate setting for audio interface */
++ /* 1 - 48000 samples per sec */
++ ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 1);
++ if (ret < 0) {
++ cx231xx_errdev("failed to set alternate setting !\n");
++
++ return ret;
++ }
++
++ /* inform hardware to start streaming */
++ ret = cx231xx_capture_start(dev, 1, Audio);
++
++ runtime->hw = snd_cx231xx_hw_capture;
++
++ mutex_lock(&dev->lock);
++ dev->adev.users++;
++ mutex_unlock(&dev->lock);
++
++ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
++ dev->adev.capture_pcm_substream = substream;
++ runtime->private_data = dev;
++
++ return 0;
++}
++
++static int snd_cx231xx_pcm_close(struct snd_pcm_substream *substream)
++{
++ int ret;
++ struct cx231xx *dev = snd_pcm_substream_chip(substream);
++
++ dprintk("closing device\n");
++
++ /* set alternate setting for audio interface */
++ /* 1 - 48000 samples per sec */
++ ret = cx231xx_set_alt_setting(dev, INDEX_AUDIO, 0);
++ if (ret < 0) {
++ cx231xx_errdev("failed to set alternate setting !\n");
++
++ return ret;
++ }
++
++ /* inform hardware to start streaming */
++ ret = cx231xx_capture_start(dev, 0, Audio);
++
++ dev->mute = 1;
++ mutex_lock(&dev->lock);
++ dev->adev.users--;
++ mutex_unlock(&dev->lock);
++
++ 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");
++ cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, 0);
++ }
++ return 0;
++}
++
++static int snd_cx231xx_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 cx231xx 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_cx231xx_hw_capture_free(struct snd_pcm_substream *substream)
++{
++ struct cx231xx *dev = snd_pcm_substream_chip(substream);
++
++ dprintk("Stop capture, if needed\n");
++
++ if (dev->adev.capture_stream == STREAM_ON)
++ cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, CX231XX_STOP_AUDIO);
++
++ return 0;
++}
++
++static int snd_cx231xx_prepare(struct snd_pcm_substream *substream)
++{
++ return 0;
++}
++
++static int snd_cx231xx_capture_trigger(struct snd_pcm_substream *substream,
++ int cmd)
++{
++ struct cx231xx *dev = snd_pcm_substream_chip(substream);
++ int retval;
++
++ dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START) ?
++ "start" : "stop");
++
++ spin_lock(&dev->adev.slock);
++ switch (cmd) {
++ case SNDRV_PCM_TRIGGER_START:
++ cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN,
++ CX231XX_START_AUDIO);
++ retval = 0;
++ break;
++ case SNDRV_PCM_TRIGGER_STOP:
++ cx231xx_cmd(dev, CX231XX_CAPTURE_STREAM_EN, CX231XX_STOP_AUDIO);
++ retval = 0;
++ break;
++ default:
++ retval = -EINVAL;
++ }
++
++ spin_unlock(&dev->adev.slock);
++ return retval;
++}
++
++static snd_pcm_uframes_t snd_cx231xx_capture_pointer(struct snd_pcm_substream
++ *substream)
++{
++ struct cx231xx *dev;
++ unsigned long flags;
++ snd_pcm_uframes_t hwptr_done;
++
++ dev = snd_pcm_substream_chip(substream);
++
++ spin_lock_irqsave(&dev->adev.slock, flags);
++ hwptr_done = dev->adev.hwptr_done_capture;
++ spin_unlock_irqrestore(&dev->adev.slock, flags);
++
++ 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_cx231xx_pcm_capture = {
++ .open = snd_cx231xx_capture_open,
++ .close = snd_cx231xx_pcm_close,
++ .ioctl = snd_pcm_lib_ioctl,
++ .hw_params = snd_cx231xx_hw_capture_params,
++ .hw_free = snd_cx231xx_hw_capture_free,
++ .prepare = snd_cx231xx_prepare,
++ .trigger = snd_cx231xx_capture_trigger,
++ .pointer = snd_cx231xx_capture_pointer,
++ .page = snd_pcm_get_vmalloc_page,
++};
++
++static int cx231xx_audio_init(struct cx231xx *dev)
++{
++ struct cx231xx_audio *adev = &dev->adev;
++ struct snd_pcm *pcm;
++ struct snd_card *card;
++ static int devnr;
++ int err;
++ struct usb_interface *uif;
++ int i, isoc_pipe = 0;
++
++ if (dev->has_alsa_audio != 1) {
++ /* This device does not support the extension (in this case
++ the device is expecting the snd-usb-audio module or
++ doesn't have analog audio support at all) */
++ return 0;
++ }
++
++ cx231xx_info("cx231xx-audio.c: probing for cx231xx "
++ "non standard usbaudio\n");
++
++ card = snd_card_new(index[devnr], "Cx231xx Audio", THIS_MODULE, 0);
++ if (card == NULL)
++ return -ENOMEM;
++
++ spin_lock_init(&adev->slock);
++ err = snd_pcm_new(card, "Cx231xx Audio", 0, 0, 1, &pcm);
++ if (err < 0) {
++ snd_card_free(card);
++ return err;
++ }
++
++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
++ &snd_cx231xx_pcm_capture);
++ pcm->info_flags = 0;
++ pcm->private_data = dev;
++ strcpy(pcm->name, "Conexant cx231xx Capture");
++ strcpy(card->driver, "Conexant cx231xx Audio");
++ strcpy(card->shortname, "Cx231xx Audio");
++ strcpy(card->longname, "Conexant cx231xx Audio");
++
++ err = snd_card_register(card);
++ if (err < 0) {
++ snd_card_free(card);
++ return err;
++ }
++ adev->sndcard = card;
++ adev->udev = dev->udev;
++
++ /* compute alternate max packet sizes for Audio */
++ uif =
++ dev->udev->actconfig->interface[dev->current_pcb_config.
++ hs_config_info[0].interface_info.
++ audio_index + 1];
++
++ adev->end_point_addr =
++ le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc.
++ bEndpointAddress);
++
++ adev->num_alt = uif->num_altsetting;
++ cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
++ adev->end_point_addr, adev->num_alt);
++ adev->alt_max_pkt_size = kmalloc(32 * adev->num_alt, GFP_KERNEL);
++
++ if (adev->alt_max_pkt_size == NULL) {
++ cx231xx_errdev("out of memory!\n");
++ return -ENOMEM;
++ }
++
++ for (i = 0; i < adev->num_alt; i++) {
++ u16 tmp =
++ le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].desc.
++ wMaxPacketSize);
++ adev->alt_max_pkt_size[i] =
++ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
++ cx231xx_info("Alternate setting %i, max size= %i\n", i,
++ adev->alt_max_pkt_size[i]);
++ }
++
++ return 0;
++}
++
++static int cx231xx_audio_fini(struct cx231xx *dev)
++{
++ if (dev == NULL)
++ return 0;
++
++ if (dev->has_alsa_audio != 1) {
++ /* This device does not support the extension (in this case
++ the device is expecting the snd-usb-audio module or
++ doesn't have analog audio support at all) */
++ return 0;
++ }
++
++ if (dev->adev.sndcard) {
++ snd_card_free(dev->adev.sndcard);
++ kfree(dev->adev.alt_max_pkt_size);
++ dev->adev.sndcard = NULL;
++ }
++
++ return 0;
++}
++
++static struct cx231xx_ops audio_ops = {
++ .id = CX231XX_AUDIO,
++ .name = "Cx231xx Audio Extension",
++ .init = cx231xx_audio_init,
++ .fini = cx231xx_audio_fini,
++};
++
++static int __init cx231xx_alsa_register(void)
++{
++ return cx231xx_register_extension(&audio_ops);
++}
++
++static void __exit cx231xx_alsa_unregister(void)
++{
++ cx231xx_unregister_extension(&audio_ops);
++}
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
++MODULE_DESCRIPTION("Cx231xx Audio driver");
++
++module_init(cx231xx_alsa_register);
++module_exit(cx231xx_alsa_unregister);
+diff --git a/drivers/media/video/cx231xx/cx231xx-avcore.c b/drivers/media/video/cx231xx/cx231xx-avcore.c
+new file mode 100644
+index 0000000..1be3881
+--- /dev/null
++++ b/drivers/media/video/cx231xx/cx231xx-avcore.c
+@@ -0,0 +1,2581 @@
++/*
++ cx231xx_avcore.c - driver for Conexant Cx23100/101/102
++ USB video capture devices
++
++ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
++
++ This program contains the specific code to control the avdecoder chip and
++ other related usb control functions for cx231xx based chipset.
++
++ 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 <linux/init.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/bitmap.h>
++#include <linux/usb.h>
++#include <linux/i2c.h>
++#include <linux/version.h>
++#include <linux/mm.h>
++#include <linux/mutex.h>
++
++#include <media/v4l2-common.h>
++#include <media/v4l2-ioctl.h>
++#include <media/v4l2-chip-ident.h>
++
++#include "cx231xx.h"
++
++/******************************************************************************
++ -: BLOCK ARRANGEMENT :-
++ I2S block ----------------------|
++ [I2S audio] |
++ |
++ Analog Front End --> Direct IF -|-> Cx25840 --> Audio
++ [video & audio] | [Audio]
++ |
++ |-> Cx25840 --> Video
++ [Video]
++
++*******************************************************************************/
++
++/******************************************************************************
++ * A F E - B L O C K C O N T R O L functions *
++ * [ANALOG FRONT END] *
++ ******************************************************************************/
++static int afe_write_byte(struct cx231xx *dev, u16 saddr, u8 data)
++{
++ return cx231xx_write_i2c_data(dev, AFE_DEVICE_ADDRESS,
++ saddr, 2, data, 1);
++}
++
++static int afe_read_byte(struct cx231xx *dev, u16 saddr, u8 *data)
++{
++ int status;
++ u32 temp = 0;
++
++ status = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
++ saddr, 2, &temp, 1);
++ *data = (u8) temp;
++ return status;
++}
++
++int cx231xx_afe_init_super_block(struct cx231xx *dev, u32 ref_count)
++{
++ int status = 0;
++ u8 temp = 0;
++ u8 afe_power_status = 0;
++ int i = 0;
++
++ /* super block initialize */
++ temp = (u8) (ref_count & 0xff);
++ status = afe_write_byte(dev, SUP_BLK_TUNE2, temp);
++ if (status < 0)
++ return status;
++
++ status = afe_read_byte(dev, SUP_BLK_TUNE2, &afe_power_status);
++ if (status < 0)
++ return status;
++
++ temp = (u8) ((ref_count & 0x300) >> 8);
++ temp |= 0x40;
++ status = afe_write_byte(dev, SUP_BLK_TUNE1, temp);
++ if (status < 0)
++ return status;
++
++ status = afe_write_byte(dev, SUP_BLK_PLL2, 0x0f);
++ if (status < 0)
++ return status;
++
++ /* enable pll */
++ while (afe_power_status != 0x18) {
++ status = afe_write_byte(dev, SUP_BLK_PWRDN, 0x18);
++ if (status < 0) {
++ cx231xx_info(
++ ": Init Super Block failed in send cmd\n");
++ break;
++ }
++
++ status = afe_read_byte(dev, SUP_BLK_PWRDN, &afe_power_status);
++ afe_power_status &= 0xff;
++ if (status < 0) {
++ cx231xx_info(
++ ": Init Super Block failed in receive cmd\n");
++ break;
++ }
++ i++;
++ if (i == 10) {
++ cx231xx_info(
++ ": Init Super Block force break in loop !!!!\n");
++ status = -1;
++ break;
++ }
++ }
++
++ if (status < 0)
++ return status;
++
++ /* start tuning filter */
++ status = afe_write_byte(dev, SUP_BLK_TUNE3, 0x40);
++ if (status < 0)
++ return status;
++
++ msleep(5);
++
++ /* exit tuning */
++ status = afe_write_byte(dev, SUP_BLK_TUNE3, 0x00);
++
++ return status;
++}
++
++int cx231xx_afe_init_channels(struct cx231xx *dev)
++{
++ int status = 0;
++
++ /* power up all 3 channels, clear pd_buffer */
++ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1, 0x00);
++ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, 0x00);
++ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3, 0x00);
++
++ /* Enable quantizer calibration */
++ status = afe_write_byte(dev, ADC_COM_QUANT, 0x02);
++
++ /* channel initialize, force modulator (fb) reset */
++ status = afe_write_byte(dev, ADC_FB_FRCRST_CH1, 0x17);
++ status = afe_write_byte(dev, ADC_FB_FRCRST_CH2, 0x17);
++ status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, 0x17);
++
++ /* start quantilizer calibration */
++ status = afe_write_byte(dev, ADC_CAL_ATEST_CH1, 0x10);
++ status = afe_write_byte(dev, ADC_CAL_ATEST_CH2, 0x10);
++ status = afe_write_byte(dev, ADC_CAL_ATEST_CH3, 0x10);
++ msleep(5);
++
++ /* exit modulator (fb) reset */
++ status = afe_write_byte(dev, ADC_FB_FRCRST_CH1, 0x07);
++ status = afe_write_byte(dev, ADC_FB_FRCRST_CH2, 0x07);
++ status = afe_write_byte(dev, ADC_FB_FRCRST_CH3, 0x07);
++
++ /* enable the pre_clamp in each channel for single-ended input */
++ status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH1, 0xf0);
++ status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH2, 0xf0);
++ status = afe_write_byte(dev, ADC_NTF_PRECLMP_EN_CH3, 0xf0);
++
++ /* use diode instead of resistor, so set term_en to 0, res_en to 0 */
++ status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8,
++ ADC_QGAIN_RES_TRM_CH1, 3, 7, 0x00);
++ status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8,
++ ADC_QGAIN_RES_TRM_CH2, 3, 7, 0x00);
++ status = cx231xx_reg_mask_write(dev, AFE_DEVICE_ADDRESS, 8,
++ ADC_QGAIN_RES_TRM_CH3, 3, 7, 0x00);
++
++ /* dynamic element matching off */
++ status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH1, 0x03);
++ status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH2, 0x03);
++ status = afe_write_byte(dev, ADC_DCSERVO_DEM_CH3, 0x03);
++
++ return status;
++}
++
++int cx231xx_afe_setup_AFE_for_baseband(struct cx231xx *dev)
++{
++ u8 c_value = 0;
++ int status = 0;
++
++ status = afe_read_byte(dev, ADC_PWRDN_CLAMP_CH2, &c_value);
++ c_value &= (~(0x50));
++ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2, c_value);
++
++ return status;
++}
++
++/*
++ The Analog Front End in Cx231xx has 3 channels. These
++ channels are used to share between different inputs
++ like tuner, s-video and composite inputs.
++
++ channel 1 ----- pin 1 to pin4(in reg is 1-4)
++ channel 2 ----- pin 5 to pin8(in reg is 5-8)
++ channel 3 ----- pin 9 to pin 12(in reg is 9-11)
++*/
++int cx231xx_afe_set_input_mux(struct cx231xx *dev, u32 input_mux)
++{
++ u8 ch1_setting = (u8) input_mux;
++ u8 ch2_setting = (u8) (input_mux >> 8);
++ u8 ch3_setting = (u8) (input_mux >> 16);
++ int status = 0;
++ u8 value = 0;
++
++ if (ch1_setting != 0) {
++ status = afe_read_byte(dev, ADC_INPUT_CH1, &value);
++ value &= (!INPUT_SEL_MASK);
++ value |= (ch1_setting - 1) << 4;
++ value &= 0xff;
++ status = afe_write_byte(dev, ADC_INPUT_CH1, value);
++ }
++
++ if (ch2_setting != 0) {
++ status = afe_read_byte(dev, ADC_INPUT_CH2, &value);
++ value &= (!INPUT_SEL_MASK);
++ value |= (ch2_setting - 1) << 4;
++ value &= 0xff;
++ status = afe_write_byte(dev, ADC_INPUT_CH2, value);
++ }
++
++ /* For ch3_setting, the value to put in the register is
++ 7 less than the input number */
++ if (ch3_setting != 0) {
++ status = afe_read_byte(dev, ADC_INPUT_CH3, &value);
++ value &= (!INPUT_SEL_MASK);
++ value |= (ch3_setting - 1) << 4;
++ value &= 0xff;
++ status = afe_write_byte(dev, ADC_INPUT_CH3, value);
++ }
++
++ return status;
++}
++
++int cx231xx_afe_set_mode(struct cx231xx *dev, enum AFE_MODE mode)
++{
++ int status = 0;
++
++ /*
++ * FIXME: We need to implement the AFE code for LOW IF and for HI IF.
++ * Currently, only baseband works.
++ */
++
++ switch (mode) {
++ case AFE_MODE_LOW_IF:
++ /* SetupAFEforLowIF(); */
++ break;
++ case AFE_MODE_BASEBAND:
++ status = cx231xx_afe_setup_AFE_for_baseband(dev);
++ break;
++ case AFE_MODE_EU_HI_IF:
++ /* SetupAFEforEuHiIF(); */
++ break;
++ case AFE_MODE_US_HI_IF:
++ /* SetupAFEforUsHiIF(); */
++ break;
++ case AFE_MODE_JAPAN_HI_IF:
++ /* SetupAFEforJapanHiIF(); */
++ break;
++ }
++
++ if ((mode != dev->afe_mode) &&
++ (dev->video_input == CX231XX_VMUX_TELEVISION))
++ status = cx231xx_afe_adjust_ref_count(dev,
++ CX231XX_VMUX_TELEVISION);
++
++ dev->afe_mode = mode;
++
++ return status;
++}
++
++int cx231xx_afe_update_power_control(struct cx231xx *dev,
++ enum AV_MODE avmode)
++{
++ u8 afe_power_status = 0;
++ int status = 0;
++
++ switch (dev->model) {
++ case CX231XX_BOARD_CNXT_RDE_250:
++ case CX231XX_BOARD_CNXT_RDU_250:
++ if (avmode == POLARIS_AVMODE_ANALOGT_TV) {
++ while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
++ FLD_PWRDN_ENABLE_PLL)) {
++ status = afe_write_byte(dev, SUP_BLK_PWRDN,
++ FLD_PWRDN_TUNING_BIAS |
++ FLD_PWRDN_ENABLE_PLL);
++ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
++ &afe_power_status);
++ if (status < 0)
++ break;
++ }
++
++ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
++ 0x00);
++ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
++ 0x00);
++ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
++ 0x00);
++ } else if (avmode == POLARIS_AVMODE_DIGITAL) {
++ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
++ 0x70);
++ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
++ 0x70);
++ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
++ 0x70);
++
++ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
++ &afe_power_status);
++ afe_power_status |= FLD_PWRDN_PD_BANDGAP |
++ FLD_PWRDN_PD_BIAS |
++ FLD_PWRDN_PD_TUNECK;
++ status |= afe_write_byte(dev, SUP_BLK_PWRDN,
++ afe_power_status);
++ } else if (avmode == POLARIS_AVMODE_ENXTERNAL_AV) {
++ while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
++ FLD_PWRDN_ENABLE_PLL)) {
++ status = afe_write_byte(dev, SUP_BLK_PWRDN,
++ FLD_PWRDN_TUNING_BIAS |
++ FLD_PWRDN_ENABLE_PLL);
++ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
++ &afe_power_status);
++ if (status < 0)
++ break;
++ }
++
++ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
++ 0x00);
++ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
++ 0x00);
++ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
++ 0x00);
++ } else {
++ cx231xx_info("Invalid AV mode input\n");
++ status = -1;
++ }
++ break;
++ default:
++ if (avmode == POLARIS_AVMODE_ANALOGT_TV) {
++ while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
++ FLD_PWRDN_ENABLE_PLL)) {
++ status = afe_write_byte(dev, SUP_BLK_PWRDN,
++ FLD_PWRDN_TUNING_BIAS |
++ FLD_PWRDN_ENABLE_PLL);
++ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
++ &afe_power_status);
++ if (status < 0)
++ break;
++ }
++
++ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
++ 0x40);
++ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
++ 0x40);
++ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
++ 0x00);
++ } else if (avmode == POLARIS_AVMODE_DIGITAL) {
++ status = afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
++ 0x70);
++ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
++ 0x70);
++ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
++ 0x70);
++
++ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
++ &afe_power_status);
++ afe_power_status |= FLD_PWRDN_PD_BANDGAP |
++ FLD_PWRDN_PD_BIAS |
++ FLD_PWRDN_PD_TUNECK;
++ status |= afe_write_byte(dev, SUP_BLK_PWRDN,
++ afe_power_status);
++ } else if (avmode == POLARIS_AVMODE_ENXTERNAL_AV) {
++ while (afe_power_status != (FLD_PWRDN_TUNING_BIAS |
++ FLD_PWRDN_ENABLE_PLL)) {
++ status = afe_write_byte(dev, SUP_BLK_PWRDN,
++ FLD_PWRDN_TUNING_BIAS |
++ FLD_PWRDN_ENABLE_PLL);
++ status |= afe_read_byte(dev, SUP_BLK_PWRDN,
++ &afe_power_status);
++ if (status < 0)
++ break;
++ }
++
++ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH1,
++ 0x00);
++ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH2,
++ 0x00);
++ status |= afe_write_byte(dev, ADC_PWRDN_CLAMP_CH3,
++ 0x40);
++ } else {
++ cx231xx_info("Invalid AV mode input\n");
++ status = -1;
++ }
++ } /* switch */
++
++ return status;
++}
++
++int cx231xx_afe_adjust_ref_count(struct cx231xx *dev, u32 video_input)
++{
++ u8 input_mode = 0;
++ u8 ntf_mode = 0;
++ int status = 0;
++
++ dev->video_input = video_input;
++
++ if (video_input == CX231XX_VMUX_TELEVISION) {
++ status = afe_read_byte(dev, ADC_INPUT_CH3, &input_mode);
++ status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH3,
++ &ntf_mode);
++ } else {
++ status = afe_read_byte(dev, ADC_INPUT_CH1, &input_mode);
++ status = afe_read_byte(dev, ADC_NTF_PRECLMP_EN_CH1,
++ &ntf_mode);
++ }
++
++ input_mode = (ntf_mode & 0x3) | ((input_mode & 0x6) << 1);
++
++ switch (input_mode) {
++ case SINGLE_ENDED:
++ dev->afe_ref_count = 0x23C;
++ break;
++ case LOW_IF:
++ dev->afe_ref_count = 0x24C;
++ break;
++ case EU_IF:
++ dev->afe_ref_count = 0x258;
++ break;
++ case US_IF:
++ dev->afe_ref_count = 0x260;
++ break;
++ default:
++ break;
++ }
++
++ status = cx231xx_afe_init_super_block(dev, dev->afe_ref_count);
++
++ return status;
++}
++
++/******************************************************************************
++ * V I D E O / A U D I O D E C O D E R C O N T R O L functions *
++ ******************************************************************************/
++static int vid_blk_write_byte(struct cx231xx *dev, u16 saddr, u8 data)
++{
++ return cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS,
++ saddr, 2, data, 1);
++}
++
++static int vid_blk_read_byte(struct cx231xx *dev, u16 saddr, u8 *data)
++{
++ int status;
++ u32 temp = 0;
++
++ status = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
++ saddr, 2, &temp, 1);
++ *data = (u8) temp;
++ return status;
++}
++
++static int vid_blk_write_word(struct cx231xx *dev, u16 saddr, u32 data)
++{
++ return cx231xx_write_i2c_data(dev, VID_BLK_I2C_ADDRESS,
++ saddr, 2, data, 4);
++}
++
++static int vid_blk_read_word(struct cx231xx *dev, u16 saddr, u32 *data)
++{
++ return cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
++ saddr, 2, data, 4);
++}
++
++int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input)
++{
++ int status = 0;
++
++ switch (INPUT(input)->type) {
++ case CX231XX_VMUX_COMPOSITE1:
++ case CX231XX_VMUX_SVIDEO:
++ if ((dev->current_pcb_config.type == USB_BUS_POWER) &&
++ (dev->power_mode != POLARIS_AVMODE_ENXTERNAL_AV)) {
++ /* External AV */
++ status = cx231xx_set_power_mode(dev,
++ POLARIS_AVMODE_ENXTERNAL_AV);
++ if (status < 0) {
++ cx231xx_errdev("%s: set_power_mode : Failed to"
++ " set Power - errCode [%d]!\n",
++ __func__, status);
++ return status;
++ }
++ }
++ status = cx231xx_set_decoder_video_input(dev,
++ INPUT(input)->type,
++ INPUT(input)->vmux);
++ break;
++ case CX231XX_VMUX_TELEVISION:
++ case CX231XX_VMUX_CABLE:
++ if ((dev->current_pcb_config.type == USB_BUS_POWER) &&
++ (dev->power_mode != POLARIS_AVMODE_ANALOGT_TV)) {
++ /* Tuner */
++ status = cx231xx_set_power_mode(dev,
++ POLARIS_AVMODE_ANALOGT_TV);
++ if (status < 0) {
++ cx231xx_errdev("%s: set_power_mode:Failed"
++ " to set Power - errCode [%d]!\n",
++ __func__, status);
++ return status;
++ }
++ }
++ status = cx231xx_set_decoder_video_input(dev,
++ CX231XX_VMUX_COMPOSITE1,
++ INPUT(input)->vmux);
++ break;
++ default:
++ cx231xx_errdev("%s: set_power_mode : Unknown Input %d !\n",
++ __func__, INPUT(input)->type);
++ break;
++ }
++
++ /* save the selection */
++ dev->video_input = input;
++
++ return status;
++}
++
++int cx231xx_set_decoder_video_input(struct cx231xx *dev,
++ u8 pin_type, u8 input)
++{
++ int status = 0;
++ u32 value = 0;
++
++ if (pin_type != dev->video_input) {
++ status = cx231xx_afe_adjust_ref_count(dev, pin_type);
++ if (status < 0) {
++ cx231xx_errdev("%s: adjust_ref_count :Failed to set"
++ "AFE input mux - errCode [%d]!\n",
++ __func__, status);
++ return status;
++ }
++ }
++
++ /* call afe block to set video inputs */
++ status = cx231xx_afe_set_input_mux(dev, input);
++ if (status < 0) {
++ cx231xx_errdev("%s: set_input_mux :Failed to set"
++ " AFE input mux - errCode [%d]!\n",
++ __func__, status);
++ return status;
++ }
++
++ switch (pin_type) {
++ case CX231XX_VMUX_COMPOSITE1:
++ status = vid_blk_read_word(dev, AFE_CTRL, &value);
++ value |= (0 << 13) | (1 << 4);
++ value &= ~(1 << 5);
++
++ /* set [24:23] [22:15] to 0 */
++ value &= (~(0x1ff8000));
++ /* set FUNC_MODE[24:23] = 2 IF_MOD[22:15] = 0 */
++ value |= 0x1000000;
++ status = vid_blk_write_word(dev, AFE_CTRL, value);
++
++ status = vid_blk_read_word(dev, OUT_CTRL1, &value);
++ value |= (1 << 7);
++ status = vid_blk_write_word(dev, OUT_CTRL1, value);
++
++ /* Set vip 1.1 output mode */
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ OUT_CTRL1,
++ FLD_OUT_MODE,
++ OUT_MODE_VIP11);
++
++ /* Tell DIF object to go to baseband mode */
++ status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND);
++ if (status < 0) {
++ cx231xx_errdev("%s: cx231xx_dif set to By pass"
++ " mode- errCode [%d]!\n",
++ __func__, status);
++ return status;
++ }
++
++ /* Read the DFE_CTRL1 register */
++ status = vid_blk_read_word(dev, DFE_CTRL1, &value);
++
++ /* enable the VBI_GATE_EN */
++ value |= FLD_VBI_GATE_EN;
++
++ /* Enable the auto-VGA enable */
++ value |= FLD_VGA_AUTO_EN;
++
++ /* Write it back */
++ status = vid_blk_write_word(dev, DFE_CTRL1, value);
++
++ /* Disable auto config of registers */
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ MODE_CTRL, FLD_ACFG_DIS,
++ cx231xx_set_field(FLD_ACFG_DIS, 1));
++
++ /* Set CVBS input mode */
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ MODE_CTRL, FLD_INPUT_MODE,
++ cx231xx_set_field(FLD_INPUT_MODE, INPUT_MODE_CVBS_0));
++ break;
++ case CX231XX_VMUX_SVIDEO:
++ /* Disable the use of DIF */
++
++ status = vid_blk_read_word(dev, AFE_CTRL, &value);
++
++ /* set [24:23] [22:15] to 0 */
++ value &= (~(0x1ff8000));
++ /* set FUNC_MODE[24:23] = 2
++ IF_MOD[22:15] = 0 DCR_BYP_CH2[4:4] = 1; */
++ value |= 0x1000010;
++ status = vid_blk_write_word(dev, AFE_CTRL, value);
++
++ /* Tell DIF object to go to baseband mode */
++ status = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND);
++ if (status < 0) {
++ cx231xx_errdev("%s: cx231xx_dif set to By pass"
++ " mode- errCode [%d]!\n",
++ __func__, status);
++ return status;
++ }
++
++ /* Read the DFE_CTRL1 register */
++ status = vid_blk_read_word(dev, DFE_CTRL1, &value);
++
++ /* enable the VBI_GATE_EN */
++ value |= FLD_VBI_GATE_EN;
++
++ /* Enable the auto-VGA enable */
++ value |= FLD_VGA_AUTO_EN;
++
++ /* Write it back */
++ status = vid_blk_write_word(dev, DFE_CTRL1, value);
++
++ /* Disable auto config of registers */
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ MODE_CTRL, FLD_ACFG_DIS,
++ cx231xx_set_field(FLD_ACFG_DIS, 1));
++
++ /* Set YC input mode */
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ MODE_CTRL,
++ FLD_INPUT_MODE,
++ cx231xx_set_field(FLD_INPUT_MODE, INPUT_MODE_YC_1));
++
++ /* Chroma to ADC2 */
++ status = vid_blk_read_word(dev, AFE_CTRL, &value);
++ value |= FLD_CHROMA_IN_SEL; /* set the chroma in select */
++
++ /* Clear VGA_SEL_CH2 and VGA_SEL_CH3 (bits 7 and 8)
++ This sets them to use video
++ rather than audio. Only one of the two will be in use. */
++ value &= ~(FLD_VGA_SEL_CH2 | FLD_VGA_SEL_CH3);
++
++ status = vid_blk_write_word(dev, AFE_CTRL, value);
++
++ status = cx231xx_afe_set_mode(dev, AFE_MODE_BASEBAND);
++ break;
++ case CX231XX_VMUX_TELEVISION:
++ case CX231XX_VMUX_CABLE:
++ default:
++ switch (dev->model) {
++ case CX231XX_BOARD_CNXT_RDE_250:
++ case CX231XX_BOARD_CNXT_RDU_250:
++ /* Disable the use of DIF */
++
++ status = vid_blk_read_word(dev, AFE_CTRL, &value);
++ value |= (0 << 13) | (1 << 4);
++ value &= ~(1 << 5);
++
++ /* set [24:23] [22:15] to 0 */
++ value &= (~(0x1FF8000));
++ /* set FUNC_MODE[24:23] = 2 IF_MOD[22:15] = 0 */
++ value |= 0x1000000;
++ status = vid_blk_write_word(dev, AFE_CTRL, value);
++
++ status = vid_blk_read_word(dev, OUT_CTRL1, &value);
++ value |= (1 << 7);
++ status = vid_blk_write_word(dev, OUT_CTRL1, value);
++
++ /* Set vip 1.1 output mode */
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ OUT_CTRL1, FLD_OUT_MODE,
++ OUT_MODE_VIP11);
++
++ /* Tell DIF object to go to baseband mode */
++ status = cx231xx_dif_set_standard(dev,
++ DIF_USE_BASEBAND);
++ if (status < 0) {
++ cx231xx_errdev("%s: cx231xx_dif set to By pass"
++ " mode- errCode [%d]!\n",
++ __func__, status);
++ return status;
++ }
++
++ /* Read the DFE_CTRL1 register */
++ status = vid_blk_read_word(dev, DFE_CTRL1, &value);
++
++ /* enable the VBI_GATE_EN */
++ value |= FLD_VBI_GATE_EN;
++
++ /* Enable the auto-VGA enable */
++ value |= FLD_VGA_AUTO_EN;
++
++ /* Write it back */
++ status = vid_blk_write_word(dev, DFE_CTRL1, value);
++
++ /* Disable auto config of registers */
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ MODE_CTRL, FLD_ACFG_DIS,
++ cx231xx_set_field(FLD_ACFG_DIS, 1));
++
++ /* Set CVBS input mode */
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ MODE_CTRL, FLD_INPUT_MODE,
++ cx231xx_set_field(FLD_INPUT_MODE,
++ INPUT_MODE_CVBS_0));
++ break;
++ default:
++ /* Enable the DIF for the tuner */
++
++ /* Reinitialize the DIF */
++ status = cx231xx_dif_set_standard(dev, dev->norm);
++ if (status < 0) {
++ cx231xx_errdev("%s: cx231xx_dif set to By pass"
++ " mode- errCode [%d]!\n",
++ __func__, status);
++ return status;
++ }
++
++ /* Make sure bypass is cleared */
++ status = vid_blk_read_word(dev, DIF_MISC_CTRL, &value);
++
++ /* Clear the bypass bit */
++ value &= ~FLD_DIF_DIF_BYPASS;
++
++ /* Enable the use of the DIF block */
++ status = vid_blk_write_word(dev, DIF_MISC_CTRL, value);
++
++ /* Read the DFE_CTRL1 register */
++ status = vid_blk_read_word(dev, DFE_CTRL1, &value);
++
++ /* Disable the VBI_GATE_EN */
++ value &= ~FLD_VBI_GATE_EN;
++
++ /* Enable the auto-VGA enable, AGC, and
++ set the skip count to 2 */
++ value |= FLD_VGA_AUTO_EN | FLD_AGC_AUTO_EN | 0x00200000;
++
++ /* Write it back */
++ status = vid_blk_write_word(dev, DFE_CTRL1, value);
++
++ /* Wait until AGC locks up */
++ msleep(1);
++
++ /* Disable the auto-VGA enable AGC */
++ value &= ~(FLD_VGA_AUTO_EN);
++
++ /* Write it back */
++ status = vid_blk_write_word(dev, DFE_CTRL1, value);
++
++ /* Enable Polaris B0 AGC output */
++ status = vid_blk_read_word(dev, PIN_CTRL, &value);
++ value |= (FLD_OEF_AGC_RF) |
++ (FLD_OEF_AGC_IFVGA) |
++ (FLD_OEF_AGC_IF);
++ status = vid_blk_write_word(dev, PIN_CTRL, value);
++
++ /* Set vip 1.1 output mode */
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ OUT_CTRL1, FLD_OUT_MODE,
++ OUT_MODE_VIP11);
++
++ /* Disable auto config of registers */
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ MODE_CTRL, FLD_ACFG_DIS,
++ cx231xx_set_field(FLD_ACFG_DIS, 1));
++
++ /* Set CVBS input mode */
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ MODE_CTRL, FLD_INPUT_MODE,
++ cx231xx_set_field(FLD_INPUT_MODE,
++ INPUT_MODE_CVBS_0));
++
++ /* Set some bits in AFE_CTRL so that channel 2 or 3
++ * is ready to receive audio */
++ /* Clear clamp for channels 2 and 3 (bit 16-17) */
++ /* Clear droop comp (bit 19-20) */
++ /* Set VGA_SEL (for audio control) (bit 7-8) */
++ status = vid_blk_read_word(dev, AFE_CTRL, &value);
++
++ value |= FLD_VGA_SEL_CH3 | FLD_VGA_SEL_CH2;
++
++ status = vid_blk_write_word(dev, AFE_CTRL, value);
++ break;
++
++ }
++ break;
++ }
++
++ /* Set raw VBI mode */
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ OUT_CTRL1, FLD_VBIHACTRAW_EN,
++ cx231xx_set_field(FLD_VBIHACTRAW_EN, 1));
++
++ status = vid_blk_read_word(dev, OUT_CTRL1, &value);
++ if (value & 0x02) {
++ value |= (1 << 19);
++ status = vid_blk_write_word(dev, OUT_CTRL1, value);
++ }
++
++ return status;
++}
++
++/*
++ * Handle any video-mode specific overrides that are different
++ * on a per video standards basis after touching the MODE_CTRL
++ * register which resets many values for autodetect
++ */
++int cx231xx_do_mode_ctrl_overrides(struct cx231xx *dev)
++{
++ int status = 0;
++
++ cx231xx_info("do_mode_ctrl_overrides : 0x%x\n",
++ (unsigned int)dev->norm);
++
++ /* Change the DFE_CTRL3 bp_percent to fix flagging */
++ status = vid_blk_write_word(dev, DFE_CTRL3, 0xCD3F0280);
++
++ if (dev->norm & (V4L2_STD_NTSC | V4L2_STD_PAL_M)) {
++ cx231xx_info("do_mode_ctrl_overrides NTSC\n");
++
++ /* Move the close caption lines out of active video,
++ adjust the active video start point */
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ VERT_TIM_CTRL,
++ FLD_VBLANK_CNT, 0x18);
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ VERT_TIM_CTRL,
++ FLD_VACTIVE_CNT,
++ 0x1E6000);
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ VERT_TIM_CTRL,
++ FLD_V656BLANK_CNT,
++ 0x1E000000);
++
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ HORIZ_TIM_CTRL,
++ FLD_HBLANK_CNT,
++ cx231xx_set_field
++ (FLD_HBLANK_CNT, 0x79));
++ } else if (dev->norm & V4L2_STD_SECAM) {
++ cx231xx_info("do_mode_ctrl_overrides SECAM\n");
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ VERT_TIM_CTRL,
++ FLD_VBLANK_CNT, 0x24);
++ /* Adjust the active video horizontal start point */
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ HORIZ_TIM_CTRL,
++ FLD_HBLANK_CNT,
++ cx231xx_set_field
++ (FLD_HBLANK_CNT, 0x85));
++ } else {
++ cx231xx_info("do_mode_ctrl_overrides PAL\n");
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ VERT_TIM_CTRL,
++ FLD_VBLANK_CNT, 0x24);
++ /* Adjust the active video horizontal start point */
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ HORIZ_TIM_CTRL,
++ FLD_HBLANK_CNT,
++ cx231xx_set_field
++ (FLD_HBLANK_CNT, 0x85));
++ }
++
++ return status;
++}
++
++int cx231xx_set_audio_input(struct cx231xx *dev, u8 input)
++{
++ int status = 0;
++ enum AUDIO_INPUT ainput = AUDIO_INPUT_LINE;
++
++ switch (INPUT(input)->amux) {
++ case CX231XX_AMUX_VIDEO:
++ ainput = AUDIO_INPUT_TUNER_TV;
++ break;
++ case CX231XX_AMUX_LINE_IN:
++ status = cx231xx_i2s_blk_set_audio_input(dev, input);
++ ainput = AUDIO_INPUT_LINE;
++ break;
++ default:
++ break;
++ }
++
++ status = cx231xx_set_audio_decoder_input(dev, ainput);
++
++ return status;
++}
++
++int cx231xx_set_audio_decoder_input(struct cx231xx *dev,
++ enum AUDIO_INPUT audio_input)
++{
++ u32 dwval;
++ int status;
++ u8 gen_ctrl;
++ u32 value = 0;
++
++ /* Put it in soft reset */
++ status = vid_blk_read_byte(dev, GENERAL_CTL, &gen_ctrl);
++ gen_ctrl |= 1;
++ status = vid_blk_write_byte(dev, GENERAL_CTL, gen_ctrl);
++
++ switch (audio_input) {
++ case AUDIO_INPUT_LINE:
++ /* setup AUD_IO control from Merlin paralle output */
++ value = cx231xx_set_field(FLD_AUD_CHAN1_SRC,
++ AUD_CHAN_SRC_PARALLEL);
++ status = vid_blk_write_word(dev, AUD_IO_CTRL, value);
++
++ /* setup input to Merlin, SRC2 connect to AC97
++ bypass upsample-by-2, slave mode, sony mode, left justify
++ adr 091c, dat 01000000 */
++ status = vid_blk_read_word(dev, AC97_CTL, &dwval);
++
++ status = vid_blk_write_word(dev, AC97_CTL,
++ (dwval | FLD_AC97_UP2X_BYPASS));
++
++ /* select the parallel1 and SRC3 */
++ status = vid_blk_write_word(dev, BAND_OUT_SEL,
++ cx231xx_set_field(FLD_SRC3_IN_SEL, 0x0) |
++ cx231xx_set_field(FLD_SRC3_CLK_SEL, 0x0) |
++ cx231xx_set_field(FLD_PARALLEL1_SRC_SEL, 0x0));
++
++ /* unmute all, AC97 in, independence mode
++ adr 08d0, data 0x00063073 */
++ status = vid_blk_write_word(dev, PATH1_CTL1, 0x00063073);
++
++ /* set AVC maximum threshold, adr 08d4, dat ffff0024 */
++ status = vid_blk_read_word(dev, PATH1_VOL_CTL, &dwval);
++ status = vid_blk_write_word(dev, PATH1_VOL_CTL,
++ (dwval | FLD_PATH1_AVC_THRESHOLD));
++
++ /* set SC maximum threshold, adr 08ec, dat ffffb3a3 */
++ status = vid_blk_read_word(dev, PATH1_SC_CTL, &dwval);
++ status = vid_blk_write_word(dev, PATH1_SC_CTL,
++ (dwval | FLD_PATH1_SC_THRESHOLD));
++ break;
++
++ case AUDIO_INPUT_TUNER_TV:
++ default:
++
++ /* Setup SRC sources and clocks */
++ status = vid_blk_write_word(dev, BAND_OUT_SEL,
++ cx231xx_set_field(FLD_SRC6_IN_SEL, 0x00) |
++ cx231xx_set_field(FLD_SRC6_CLK_SEL, 0x01) |
++ cx231xx_set_field(FLD_SRC5_IN_SEL, 0x00) |
++ cx231xx_set_field(FLD_SRC5_CLK_SEL, 0x02) |
++ cx231xx_set_field(FLD_SRC4_IN_SEL, 0x02) |
++ cx231xx_set_field(FLD_SRC4_CLK_SEL, 0x03) |
++ cx231xx_set_field(FLD_SRC3_IN_SEL, 0x00) |
++ cx231xx_set_field(FLD_SRC3_CLK_SEL, 0x00) |
++ cx231xx_set_field(FLD_BASEBAND_BYPASS_CTL, 0x00) |
++ cx231xx_set_field(FLD_AC97_SRC_SEL, 0x03) |
++ cx231xx_set_field(FLD_I2S_SRC_SEL, 0x00) |
++ cx231xx_set_field(FLD_PARALLEL2_SRC_SEL, 0x02) |
++ cx231xx_set_field(FLD_PARALLEL1_SRC_SEL, 0x01));
++
++ /* Setup the AUD_IO control */
++ status = vid_blk_write_word(dev, AUD_IO_CTRL,
++ cx231xx_set_field(FLD_I2S_PORT_DIR, 0x00) |
++ cx231xx_set_field(FLD_I2S_OUT_SRC, 0x00) |
++ cx231xx_set_field(FLD_AUD_CHAN3_SRC, 0x00) |
++ cx231xx_set_field(FLD_AUD_CHAN2_SRC, 0x00) |
++ cx231xx_set_field(FLD_AUD_CHAN1_SRC, 0x03));
++
++ status = vid_blk_write_word(dev, PATH1_CTL1, 0x1F063870);
++
++ /* setAudioStandard(_audio_standard); */
++
++ status = vid_blk_write_word(dev, PATH1_CTL1, 0x00063870);
++ switch (dev->model) {
++ case CX231XX_BOARD_CNXT_RDE_250:
++ case CX231XX_BOARD_CNXT_RDU_250:
++ status = cx231xx_read_modify_write_i2c_dword(dev,
++ VID_BLK_I2C_ADDRESS,
++ CHIP_CTRL,
++ FLD_SIF_EN,
++ cx231xx_set_field(FLD_SIF_EN, 1));
++ break;
++ default:
++ break;
++ }
++ break;
++
++ case AUDIO_INPUT_TUNER_FM:
++ /* use SIF for FM radio
++ setupFM();
++ setAudioStandard(_audio_standard);
++ */
++ break;
++
++ case AUDIO_INPUT_MUTE:
++ status = vid_blk_write_word(dev, PATH1_CTL1, 0x1F011012);
++ break;
++ }
++
++ /* Take it out of soft reset */
++ status = vid_blk_read_byte(dev, GENERAL_CTL, &gen_ctrl);
++ gen_ctrl &= ~1;
++ status = vid_blk_write_byte(dev, GENERAL_CTL, gen_ctrl);
++
++ return status;
++}
++
++/* Set resolution of the video */
++int cx231xx_resolution_set(struct cx231xx *dev)
++{
++ int width, height;
++ u32 hscale, vscale;
++ int status = 0;
++
++ width = dev->width;
++ height = dev->height;
++
++ get_scale(dev, width, height, &hscale, &vscale);
++
++ /* set horzontal scale */
++ status = vid_blk_write_word(dev, HSCALE_CTRL, hscale);
++
++ /* set vertical scale */
++ status = vid_blk_write_word(dev, VSCALE_CTRL, vscale);
++
++ return status;
++}
++
++/******************************************************************************
++ * C H I P Specific C O N T R O L functions *
++ ******************************************************************************/
++int cx231xx_init_ctrl_pin_status(struct cx231xx *dev)
++{
++ u32 value;
++ int status = 0;
++
++ status = vid_blk_read_word(dev, PIN_CTRL, &value);
++ value |= (~dev->board.ctl_pin_status_mask);
++ status = vid_blk_write_word(dev, PIN_CTRL, value);
++
++ return status;
++}
++
++int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev,
++ u8 analog_or_digital)
++{
++ int status = 0;
++
++ /* first set the direction to output */
++ status = cx231xx_set_gpio_direction(dev,
++ dev->board.
++ agc_analog_digital_select_gpio, 1);
++
++ /* 0 - demod ; 1 - Analog mode */
++ status = cx231xx_set_gpio_value(dev,
++ dev->board.agc_analog_digital_select_gpio,
++ analog_or_digital);
++
++ return status;
++}
++
++int cx231xx_enable_i2c_for_tuner(struct cx231xx *dev, u8 I2CIndex)
++{
++ u8 value[4] = { 0, 0, 0, 0 };
++ int status = 0;
++
++ cx231xx_info("Changing the i2c port for tuner to %d\n", I2CIndex);
++
++ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
++ PWR_CTL_EN, value, 4);
++ if (status < 0)
++ return status;
++
++ if (I2CIndex == I2C_1) {
++ if (value[0] & I2C_DEMOD_EN) {
++ value[0] &= ~I2C_DEMOD_EN;
++ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
++ PWR_CTL_EN, value, 4);
++ }
++ } else {
++ if (!(value[0] & I2C_DEMOD_EN)) {
++ value[0] |= I2C_DEMOD_EN;
++ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
++ PWR_CTL_EN, value, 4);
++ }
++ }
++
++ return status;
++
++}
++
++/******************************************************************************
++ * D I F - B L O C K C O N T R O L functions *
++ ******************************************************************************/
++int cx231xx_dif_configure_C2HH_for_low_IF(struct cx231xx *dev, u32 mode,
++ u32 function_mode, u32 standard)
++{
++ int status = 0;
++
++ if (mode == V4L2_TUNER_RADIO) {
++ /* C2HH */
++ /* lo if big signal */
++ status = cx231xx_reg_mask_write(dev,
++ VID_BLK_I2C_ADDRESS, 32,
++ AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1);
++ /* FUNC_MODE = DIF */
++ status = cx231xx_reg_mask_write(dev,
++ VID_BLK_I2C_ADDRESS, 32,
++ AFE_CTRL_C2HH_SRC_CTRL, 23, 24, function_mode);
++ /* IF_MODE */
++ status = cx231xx_reg_mask_write(dev,
++ VID_BLK_I2C_ADDRESS, 32,
++ AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xFF);
++ /* no inv */
++ status = cx231xx_reg_mask_write(dev,
++ VID_BLK_I2C_ADDRESS, 32,
++ AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1);
++ } else if (standard != DIF_USE_BASEBAND) {
++ if (standard & V4L2_STD_MN) {
++ /* lo if big signal */
++ status = cx231xx_reg_mask_write(dev,
++ VID_BLK_I2C_ADDRESS, 32,
++ AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1);
++ /* FUNC_MODE = DIF */
++ status = cx231xx_reg_mask_write(dev,
++ VID_BLK_I2C_ADDRESS, 32,
++ AFE_CTRL_C2HH_SRC_CTRL, 23, 24,
++ function_mode);
++ /* IF_MODE */
++ status = cx231xx_reg_mask_write(dev,
++ VID_BLK_I2C_ADDRESS, 32,
++ AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xb);
++ /* no inv */
++ status = cx231xx_reg_mask_write(dev,
++ VID_BLK_I2C_ADDRESS, 32,
++ AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1);
++ /* 0x124, AUD_CHAN1_SRC = 0x3 */
++ status = cx231xx_reg_mask_write(dev,
++ VID_BLK_I2C_ADDRESS, 32,
++ AUD_IO_CTRL, 0, 31, 0x00000003);
++ } else if ((standard == V4L2_STD_PAL_I) |
++ (standard & V4L2_STD_SECAM)) {
++ /* C2HH setup */
++ /* lo if big signal */
++ status = cx231xx_reg_mask_write(dev,
++ VID_BLK_I2C_ADDRESS, 32,
++ AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1);
++ /* FUNC_MODE = DIF */
++ status = cx231xx_reg_mask_write(dev,
++ VID_BLK_I2C_ADDRESS, 32,
++ AFE_CTRL_C2HH_SRC_CTRL, 23, 24,
++ function_mode);
++ /* IF_MODE */
++ status = cx231xx_reg_mask_write(dev,
++ VID_BLK_I2C_ADDRESS, 32,
++ AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xF);
++ /* no inv */
++ status = cx231xx_reg_mask_write(dev,
++ VID_BLK_I2C_ADDRESS, 32,
++ AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1);
++ } else {
++ /* default PAL BG */
++ /* C2HH setup */
++ /* lo if big signal */
++ status = cx231xx_reg_mask_write(dev,
++ VID_BLK_I2C_ADDRESS, 32,
++ AFE_CTRL_C2HH_SRC_CTRL, 30, 31, 0x1);
++ /* FUNC_MODE = DIF */
++ status = cx231xx_reg_mask_write(dev,
++ VID_BLK_I2C_ADDRESS, 32,
++ AFE_CTRL_C2HH_SRC_CTRL, 23, 24,
++ function_mode);
++ /* IF_MODE */
++ status = cx231xx_reg_mask_write(dev,
++ VID_BLK_I2C_ADDRESS, 32,
++ AFE_CTRL_C2HH_SRC_CTRL, 15, 22, 0xE);
++ /* no inv */
++ status = cx231xx_reg_mask_write(dev,
++ VID_BLK_I2C_ADDRESS, 32,
++ AFE_CTRL_C2HH_SRC_CTRL, 9, 9, 0x1);
++ }
++ }
++
++ return status;
++}
++
++int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard)
++{
++ int status = 0;
++ u32 dif_misc_ctrl_value = 0;
++ u32 func_mode = 0;
++
++ cx231xx_info("%s: setStandard to %x\n", __func__, standard);
++
++ status = vid_blk_read_word(dev, DIF_MISC_CTRL, &dif_misc_ctrl_value);
++ if (standard != DIF_USE_BASEBAND)
++ dev->norm = standard;
++
++ switch (dev->model) {
++ case CX231XX_BOARD_CNXT_RDE_250:
++ case CX231XX_BOARD_CNXT_RDU_250:
++ func_mode = 0x03;
++ break;
++ default:
++ func_mode = 0x01;
++ }
++
++ status = cx231xx_dif_configure_C2HH_for_low_IF(dev, dev->active_mode,
++ func_mode, standard);
++
++ if (standard == DIF_USE_BASEBAND) { /* base band */
++ /* There is a different SRC_PHASE_INC value
++ for baseband vs. DIF */
++ status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC, 0xDF7DF83);
++ status = vid_blk_read_word(dev, DIF_MISC_CTRL,
++ &dif_misc_ctrl_value);
++ dif_misc_ctrl_value |= FLD_DIF_DIF_BYPASS;
++ status = vid_blk_write_word(dev, DIF_MISC_CTRL,
++ dif_misc_ctrl_value);
++ } else if (standard & V4L2_STD_PAL_D) {
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL, 0, 31, 0x6503bc0c);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL1, 0, 31, 0xbd038c85);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL2, 0, 31, 0x1db4640a);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL3, 0, 31, 0x00008800);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_IF_REF, 0, 31, 0x444C1380);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_CTRL_IF, 0, 31, 0xDA302600);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_CTRL_INT, 0, 31, 0xDA261700);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_CTRL_RF, 0, 31, 0xDA262600);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_IF_INT_CURRENT, 0, 31,
++ 0x26001700);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_RF_CURRENT, 0, 31,
++ 0x00002660);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_VIDEO_AGC_CTRL, 0, 31,
++ 0x72500800);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_VID_AUD_OVERRIDE, 0, 31,
++ 0x27000100);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AV_SEP_CTRL, 0, 31, 0x3F3934EA);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_COMP_FLT_CTRL, 0, 31,
++ 0x00000000);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_SRC_PHASE_INC, 0, 31,
++ 0x1befbf06);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_SRC_GAIN_CONTROL, 0, 31,
++ 0x000035e8);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_RPT_VARIANCE, 0, 31, 0x00000000);
++ /* Save the Spec Inversion value */
++ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
++ dif_misc_ctrl_value |= 0x3a023F11;
++ } else if (standard & V4L2_STD_PAL_I) {
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL, 0, 31, 0x6503bc0c);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL1, 0, 31, 0xbd038c85);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL2, 0, 31, 0x1db4640a);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL3, 0, 31, 0x00008800);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_IF_REF, 0, 31, 0x444C1380);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_CTRL_IF, 0, 31, 0xDA302600);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_CTRL_INT, 0, 31, 0xDA261700);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_CTRL_RF, 0, 31, 0xDA262600);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_IF_INT_CURRENT, 0, 31,
++ 0x26001700);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_RF_CURRENT, 0, 31,
++ 0x00002660);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_VIDEO_AGC_CTRL, 0, 31,
++ 0x72500800);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_VID_AUD_OVERRIDE, 0, 31,
++ 0x27000100);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AV_SEP_CTRL, 0, 31, 0x5F39A934);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_COMP_FLT_CTRL, 0, 31,
++ 0x00000000);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_SRC_PHASE_INC, 0, 31,
++ 0x1befbf06);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_SRC_GAIN_CONTROL, 0, 31,
++ 0x000035e8);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_RPT_VARIANCE, 0, 31, 0x00000000);
++ /* Save the Spec Inversion value */
++ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
++ dif_misc_ctrl_value |= 0x3a033F11;
++ } else if (standard & V4L2_STD_PAL_M) {
++ /* improved Low Frequency Phase Noise */
++ status = vid_blk_write_word(dev, DIF_PLL_CTRL, 0xFF01FF0C);
++ status = vid_blk_write_word(dev, DIF_PLL_CTRL1, 0xbd038c85);
++ status = vid_blk_write_word(dev, DIF_PLL_CTRL2, 0x1db4640a);
++ status = vid_blk_write_word(dev, DIF_PLL_CTRL3, 0x00008800);
++ status = vid_blk_write_word(dev, DIF_AGC_IF_REF, 0x444C1380);
++ status = vid_blk_write_word(dev, DIF_AGC_IF_INT_CURRENT,
++ 0x26001700);
++ status = vid_blk_write_word(dev, DIF_AGC_RF_CURRENT,
++ 0x00002660);
++ status = vid_blk_write_word(dev, DIF_VIDEO_AGC_CTRL,
++ 0x72500800);
++ status = vid_blk_write_word(dev, DIF_VID_AUD_OVERRIDE,
++ 0x27000100);
++ status = vid_blk_write_word(dev, DIF_AV_SEP_CTRL, 0x012c405d);
++ status = vid_blk_write_word(dev, DIF_COMP_FLT_CTRL,
++ 0x009f50c1);
++ status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC,
++ 0x1befbf06);
++ status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL,
++ 0x000035e8);
++ status = vid_blk_write_word(dev, DIF_SOFT_RST_CTRL_REVB,
++ 0x00000000);
++ /* Save the Spec Inversion value */
++ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
++ dif_misc_ctrl_value |= 0x3A0A3F10;
++ } else if (standard & (V4L2_STD_PAL_N | V4L2_STD_PAL_Nc)) {
++ /* improved Low Frequency Phase Noise */
++ status = vid_blk_write_word(dev, DIF_PLL_CTRL, 0xFF01FF0C);
++ status = vid_blk_write_word(dev, DIF_PLL_CTRL1, 0xbd038c85);
++ status = vid_blk_write_word(dev, DIF_PLL_CTRL2, 0x1db4640a);
++ status = vid_blk_write_word(dev, DIF_PLL_CTRL3, 0x00008800);
++ status = vid_blk_write_word(dev, DIF_AGC_IF_REF, 0x444C1380);
++ status = vid_blk_write_word(dev, DIF_AGC_IF_INT_CURRENT,
++ 0x26001700);
++ status = vid_blk_write_word(dev, DIF_AGC_RF_CURRENT,
++ 0x00002660);
++ status = vid_blk_write_word(dev, DIF_VIDEO_AGC_CTRL,
++ 0x72500800);
++ status = vid_blk_write_word(dev, DIF_VID_AUD_OVERRIDE,
++ 0x27000100);
++ status = vid_blk_write_word(dev, DIF_AV_SEP_CTRL,
++ 0x012c405d);
++ status = vid_blk_write_word(dev, DIF_COMP_FLT_CTRL,
++ 0x009f50c1);
++ status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC,
++ 0x1befbf06);
++ status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL,
++ 0x000035e8);
++ status = vid_blk_write_word(dev, DIF_SOFT_RST_CTRL_REVB,
++ 0x00000000);
++ /* Save the Spec Inversion value */
++ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
++ dif_misc_ctrl_value = 0x3A093F10;
++ } else if (standard &
++ (V4L2_STD_SECAM_B | V4L2_STD_SECAM_D | V4L2_STD_SECAM_G |
++ V4L2_STD_SECAM_K | V4L2_STD_SECAM_K1)) {
++
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL, 0, 31, 0x6503bc0c);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL1, 0, 31, 0xbd038c85);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL2, 0, 31, 0x1db4640a);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL3, 0, 31, 0x00008800);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_IF_REF, 0, 31, 0x888C0380);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_CTRL_IF, 0, 31, 0xe0262600);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_CTRL_INT, 0, 31, 0xc2171700);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_CTRL_RF, 0, 31, 0xc2262600);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_IF_INT_CURRENT, 0, 31,
++ 0x26001700);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_RF_CURRENT, 0, 31,
++ 0x00002660);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_VID_AUD_OVERRIDE, 0, 31,
++ 0x27000100);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AV_SEP_CTRL, 0, 31, 0x3F3530ec);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_COMP_FLT_CTRL, 0, 31,
++ 0x00000000);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_SRC_PHASE_INC, 0, 31,
++ 0x1befbf06);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_SRC_GAIN_CONTROL, 0, 31,
++ 0x000035e8);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_RPT_VARIANCE, 0, 31, 0x00000000);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_VIDEO_AGC_CTRL, 0, 31,
++ 0xf4000000);
++
++ /* Save the Spec Inversion value */
++ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
++ dif_misc_ctrl_value |= 0x3a023F11;
++ } else if (standard & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_LC)) {
++ /* Is it SECAM_L1? */
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL, 0, 31, 0x6503bc0c);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL1, 0, 31, 0xbd038c85);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL2, 0, 31, 0x1db4640a);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL3, 0, 31, 0x00008800);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_IF_REF, 0, 31, 0x888C0380);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_CTRL_IF, 0, 31, 0xe0262600);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_CTRL_INT, 0, 31, 0xc2171700);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_CTRL_RF, 0, 31, 0xc2262600);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_IF_INT_CURRENT, 0, 31,
++ 0x26001700);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_RF_CURRENT, 0, 31,
++ 0x00002660);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_VID_AUD_OVERRIDE, 0, 31,
++ 0x27000100);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AV_SEP_CTRL, 0, 31, 0x3F3530ec);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_COMP_FLT_CTRL, 0, 31,
++ 0x00000000);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_SRC_PHASE_INC, 0, 31,
++ 0x1befbf06);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_SRC_GAIN_CONTROL, 0, 31,
++ 0x000035e8);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_RPT_VARIANCE, 0, 31, 0x00000000);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_VIDEO_AGC_CTRL, 0, 31,
++ 0xf2560000);
++
++ /* Save the Spec Inversion value */
++ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
++ dif_misc_ctrl_value |= 0x3a023F11;
++
++ } else if (standard & V4L2_STD_NTSC_M) {
++ /* V4L2_STD_NTSC_M (75 IRE Setup) Or
++ V4L2_STD_NTSC_M_JP (Japan, 0 IRE Setup) */
++
++ /* For NTSC the centre frequency of video coming out of
++ sidewinder is around 7.1MHz or 3.6MHz depending on the
++ spectral inversion. so for a non spectrally inverted channel
++ the pll freq word is 0x03420c49
++ */
++
++ status = vid_blk_write_word(dev, DIF_PLL_CTRL, 0x6503BC0C);
++ status = vid_blk_write_word(dev, DIF_PLL_CTRL1, 0xBD038C85);
++ status = vid_blk_write_word(dev, DIF_PLL_CTRL2, 0x1DB4640A);
++ status = vid_blk_write_word(dev, DIF_PLL_CTRL3, 0x00008800);
++ status = vid_blk_write_word(dev, DIF_AGC_IF_REF, 0x444C0380);
++ status = vid_blk_write_word(dev, DIF_AGC_IF_INT_CURRENT,
++ 0x26001700);
++ status = vid_blk_write_word(dev, DIF_AGC_RF_CURRENT,
++ 0x00002660);
++ status = vid_blk_write_word(dev, DIF_VIDEO_AGC_CTRL,
++ 0x04000800);
++ status = vid_blk_write_word(dev, DIF_VID_AUD_OVERRIDE,
++ 0x27000100);
++ status = vid_blk_write_word(dev, DIF_AV_SEP_CTRL, 0x01296e1f);
++
++ status = vid_blk_write_word(dev, DIF_COMP_FLT_CTRL,
++ 0x009f50c1);
++ status = vid_blk_write_word(dev, DIF_SRC_PHASE_INC,
++ 0x1befbf06);
++ status = vid_blk_write_word(dev, DIF_SRC_GAIN_CONTROL,
++ 0x000035e8);
++
++ status = vid_blk_write_word(dev, DIF_AGC_CTRL_IF, 0xC2262600);
++ status = vid_blk_write_word(dev, DIF_AGC_CTRL_INT,
++ 0xC2262600);
++ status = vid_blk_write_word(dev, DIF_AGC_CTRL_RF, 0xC2262600);
++
++ /* Save the Spec Inversion value */
++ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
++ dif_misc_ctrl_value |= 0x3a003F10;
++ } else {
++ /* default PAL BG */
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL, 0, 31, 0x6503bc0c);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL1, 0, 31, 0xbd038c85);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL2, 0, 31, 0x1db4640a);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_PLL_CTRL3, 0, 31, 0x00008800);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_IF_REF, 0, 31, 0x444C1380);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_CTRL_IF, 0, 31, 0xDA302600);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_CTRL_INT, 0, 31, 0xDA261700);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_CTRL_RF, 0, 31, 0xDA262600);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_IF_INT_CURRENT, 0, 31,
++ 0x26001700);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AGC_RF_CURRENT, 0, 31,
++ 0x00002660);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_VIDEO_AGC_CTRL, 0, 31,
++ 0x72500800);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_VID_AUD_OVERRIDE, 0, 31,
++ 0x27000100);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_AV_SEP_CTRL, 0, 31, 0x3F3530EC);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_COMP_FLT_CTRL, 0, 31,
++ 0x00A653A8);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_SRC_PHASE_INC, 0, 31,
++ 0x1befbf06);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_SRC_GAIN_CONTROL, 0, 31,
++ 0x000035e8);
++ status = cx231xx_reg_mask_write(dev, VID_BLK_I2C_ADDRESS, 32,
++ DIF_RPT_VARIANCE, 0, 31, 0x00000000);
++ /* Save the Spec Inversion value */
++ dif_misc_ctrl_value &= FLD_DIF_SPEC_INV;
++ dif_misc_ctrl_value |= 0x3a013F11;
++ }
++
++ /* The AGC values should be the same for all standards,
++ AUD_SRC_SEL[19] should always be disabled */
++ dif_misc_ctrl_value &= ~FLD_DIF_AUD_SRC_SEL;
++
++ /* It is still possible to get Set Standard calls even when we
++ are in FM mode.
++ This is done to override the value for FM. */
++ if (dev->active_mode == V4L2_TUNER_RADIO)
++ dif_misc_ctrl_value = 0x7a080000;
++
++ /* Write the calculated value for misc ontrol register */
++ status = vid_blk_write_word(dev, DIF_MISC_CTRL, dif_misc_ctrl_value);
++
++ return status;
++}
++
++int cx231xx_tuner_pre_channel_change(struct cx231xx *dev)
++{
++ int status = 0;
++ u32 dwval;
++
++ /* Set the RF and IF k_agc values to 3 */
++ status = vid_blk_read_word(dev, DIF_AGC_IF_REF, &dwval);
++ dwval &= ~(FLD_DIF_K_AGC_RF | FLD_DIF_K_AGC_IF);
++ dwval |= 0x33000000;
++
++ status = vid_blk_write_word(dev, DIF_AGC_IF_REF, dwval);
++
++ return status;
++}
++
++int cx231xx_tuner_post_channel_change(struct cx231xx *dev)
++{
++ int status = 0;
++ u32 dwval;
++
++ /* Set the RF and IF k_agc values to 4 for PAL/NTSC and 8 for
++ * SECAM L/B/D standards */
++ status = vid_blk_read_word(dev, DIF_AGC_IF_REF, &dwval);
++ dwval &= ~(FLD_DIF_K_AGC_RF | FLD_DIF_K_AGC_IF);
++
++ if (dev->norm & (V4L2_STD_SECAM_L | V4L2_STD_SECAM_B |
++ V4L2_STD_SECAM_D))
++ dwval |= 0x88000000;
++ else
++ dwval |= 0x44000000;
++
++ status = vid_blk_write_word(dev, DIF_AGC_IF_REF, dwval);
++
++ return status;
++}
++
++/******************************************************************************
++ * I 2 S - B L O C K C O N T R O L functions *
++ ******************************************************************************/
++int cx231xx_i2s_blk_initialize(struct cx231xx *dev)
++{
++ int status = 0;
++ u32 value;
++
++ status = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
++ CH_PWR_CTRL1, 1, &value, 1);
++ /* enables clock to delta-sigma and decimation filter */
++ value |= 0x80;
++ status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
++ CH_PWR_CTRL1, 1, value, 1);
++ /* power up all channel */
++ status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
++ CH_PWR_CTRL2, 1, 0x00, 1);
++
++ return status;
++}
++
++int cx231xx_i2s_blk_update_power_control(struct cx231xx *dev,
++ enum AV_MODE avmode)
++{
++ int status = 0;
++ u32 value = 0;
++
++ if (avmode != POLARIS_AVMODE_ENXTERNAL_AV) {
++ status = cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
++ CH_PWR_CTRL2, 1, &value, 1);
++ value |= 0xfe;
++ status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
++ CH_PWR_CTRL2, 1, value, 1);
++ } else {
++ status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
++ CH_PWR_CTRL2, 1, 0x00, 1);
++ }
++
++ return status;
++}
++
++/* set i2s_blk for audio input types */
++int cx231xx_i2s_blk_set_audio_input(struct cx231xx *dev, u8 audio_input)
++{
++ int status = 0;
++
++ switch (audio_input) {
++ case CX231XX_AMUX_LINE_IN:
++ status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
++ CH_PWR_CTRL2, 1, 0x00, 1);
++ status = cx231xx_write_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
++ CH_PWR_CTRL1, 1, 0x80, 1);
++ break;
++ case CX231XX_AMUX_VIDEO:
++ default:
++ break;
++ }
++
++ dev->ctl_ainput = audio_input;
++
++ return status;
++}
++
++/******************************************************************************
++ * P O W E R C O N T R O L functions *
++ ******************************************************************************/
++int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode)
++{
++ u8 value[4] = { 0, 0, 0, 0 };
++ u32 tmp = 0;
++ int status = 0;
++
++ if (dev->power_mode != mode)
++ dev->power_mode = mode;
++ else {
++ cx231xx_info(" setPowerMode::mode = %d, No Change req.\n",
++ mode);
++ return 0;
++ }
++
++ cx231xx_info(" setPowerMode::mode = %d\n", mode);
++
++ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value,
++ 4);
++ if (status < 0)
++ return status;
++
++ tmp = *((u32 *) value);
++
++ switch (mode) {
++ case POLARIS_AVMODE_ENXTERNAL_AV:
++
++ tmp &= (~PWR_MODE_MASK);
++
++ tmp |= PWR_AV_EN;
++ value[0] = (u8) tmp;
++ value[1] = (u8) (tmp >> 8);
++ value[2] = (u8) (tmp >> 16);
++ value[3] = (u8) (tmp >> 24);
++ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
++ PWR_CTL_EN, value, 4);
++ msleep(PWR_SLEEP_INTERVAL);
++
++ tmp |= PWR_ISO_EN;
++ value[0] = (u8) tmp;
++ value[1] = (u8) (tmp >> 8);
++ value[2] = (u8) (tmp >> 16);
++ value[3] = (u8) (tmp >> 24);
++ status =
++ cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, PWR_CTL_EN,
++ value, 4);
++ msleep(PWR_SLEEP_INTERVAL);
++
++ tmp |= POLARIS_AVMODE_ENXTERNAL_AV;
++ value[0] = (u8) tmp;
++ value[1] = (u8) (tmp >> 8);
++ value[2] = (u8) (tmp >> 16);
++ value[3] = (u8) (tmp >> 24);
++ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
++ PWR_CTL_EN, value, 4);
++
++ /* reset state of xceive tuner */
++ dev->xc_fw_load_done = 0;
++ break;
++
++ case POLARIS_AVMODE_ANALOGT_TV:
++
++ tmp &= (~PWR_DEMOD_EN);
++ tmp |= (I2C_DEMOD_EN);
++ value[0] = (u8) tmp;
++ value[1] = (u8) (tmp >> 8);
++ value[2] = (u8) (tmp >> 16);
++ value[3] = (u8) (tmp >> 24);
++ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
++ PWR_CTL_EN, value, 4);
++ msleep(PWR_SLEEP_INTERVAL);
++
++ if (!(tmp & PWR_TUNER_EN)) {
++ tmp |= (PWR_TUNER_EN);
++ value[0] = (u8) tmp;
++ value[1] = (u8) (tmp >> 8);
++ value[2] = (u8) (tmp >> 16);
++ value[3] = (u8) (tmp >> 24);
++ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
++ PWR_CTL_EN, value, 4);
++ msleep(PWR_SLEEP_INTERVAL);
++ }
++
++ if (!(tmp & PWR_AV_EN)) {
++ tmp |= PWR_AV_EN;
++ value[0] = (u8) tmp;
++ value[1] = (u8) (tmp >> 8);
++ value[2] = (u8) (tmp >> 16);
++ value[3] = (u8) (tmp >> 24);
++ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
++ PWR_CTL_EN, value, 4);
++ msleep(PWR_SLEEP_INTERVAL);
++ }
++ if (!(tmp & PWR_ISO_EN)) {
++ tmp |= PWR_ISO_EN;
++ value[0] = (u8) tmp;
++ value[1] = (u8) (tmp >> 8);
++ value[2] = (u8) (tmp >> 16);
++ value[3] = (u8) (tmp >> 24);
++ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
++ PWR_CTL_EN, value, 4);
++ msleep(PWR_SLEEP_INTERVAL);
++ }
++
++ if (!(tmp & POLARIS_AVMODE_ANALOGT_TV)) {
++ tmp |= POLARIS_AVMODE_ANALOGT_TV;
++ value[0] = (u8) tmp;
++ value[1] = (u8) (tmp >> 8);
++ value[2] = (u8) (tmp >> 16);
++ value[3] = (u8) (tmp >> 24);
++ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
++ PWR_CTL_EN, value, 4);
++ msleep(PWR_SLEEP_INTERVAL);
++ }
++
++ if ((dev->model == CX231XX_BOARD_CNXT_RDE_250) ||
++ (dev->model == CX231XX_BOARD_CNXT_RDU_250)) {
++ /* tuner path to channel 1 from port 3 */
++ cx231xx_enable_i2c_for_tuner(dev, I2C_3);
++
++ if (dev->cx231xx_reset_analog_tuner)
++ dev->cx231xx_reset_analog_tuner(dev);
++ }
++ break;
++
++ case POLARIS_AVMODE_DIGITAL:
++ if (!(tmp & PWR_TUNER_EN)) {
++ tmp |= (PWR_TUNER_EN);
++ value[0] = (u8) tmp;
++ value[1] = (u8) (tmp >> 8);
++ value[2] = (u8) (tmp >> 16);
++ value[3] = (u8) (tmp >> 24);
++ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
++ PWR_CTL_EN, value, 4);
++ msleep(PWR_SLEEP_INTERVAL);
++ }
++ if (!(tmp & PWR_AV_EN)) {
++ tmp |= PWR_AV_EN;
++ value[0] = (u8) tmp;
++ value[1] = (u8) (tmp >> 8);
++ value[2] = (u8) (tmp >> 16);
++ value[3] = (u8) (tmp >> 24);
++ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
++ PWR_CTL_EN, value, 4);
++ msleep(PWR_SLEEP_INTERVAL);
++ }
++ if (!(tmp & PWR_ISO_EN)) {
++ tmp |= PWR_ISO_EN;
++ value[0] = (u8) tmp;
++ value[1] = (u8) (tmp >> 8);
++ value[2] = (u8) (tmp >> 16);
++ value[3] = (u8) (tmp >> 24);
++ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
++ PWR_CTL_EN, value, 4);
++ msleep(PWR_SLEEP_INTERVAL);
++ }
++
++ tmp |= POLARIS_AVMODE_DIGITAL | I2C_DEMOD_EN;
++ value[0] = (u8) tmp;
++ value[1] = (u8) (tmp >> 8);
++ value[2] = (u8) (tmp >> 16);
++ value[3] = (u8) (tmp >> 24);
++ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
++ PWR_CTL_EN, value, 4);
++ msleep(PWR_SLEEP_INTERVAL);
++
++ if (!(tmp & PWR_DEMOD_EN)) {
++ tmp |= PWR_DEMOD_EN;
++ value[0] = (u8) tmp;
++ value[1] = (u8) (tmp >> 8);
++ value[2] = (u8) (tmp >> 16);
++ value[3] = (u8) (tmp >> 24);
++ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
++ PWR_CTL_EN, value, 4);
++ msleep(PWR_SLEEP_INTERVAL);
++ }
++
++ if ((dev->model == CX231XX_BOARD_CNXT_RDE_250) ||
++ (dev->model == CX231XX_BOARD_CNXT_RDU_250)) {
++ /* tuner path to channel 1 from port 3 */
++ cx231xx_enable_i2c_for_tuner(dev, I2C_3);
++
++ if (dev->cx231xx_reset_analog_tuner)
++ dev->cx231xx_reset_analog_tuner(dev);
++ }
++ break;
++
++ default:
++ break;
++ }
++
++ msleep(PWR_SLEEP_INTERVAL);
++
++ /* For power saving, only enable Pwr_resetout_n
++ when digital TV is selected. */
++ if (mode == POLARIS_AVMODE_DIGITAL) {
++ tmp |= PWR_RESETOUT_EN;
++ value[0] = (u8) tmp;
++ value[1] = (u8) (tmp >> 8);
++ value[2] = (u8) (tmp >> 16);
++ value[3] = (u8) (tmp >> 24);
++ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER,
++ PWR_CTL_EN, value, 4);
++ msleep(PWR_SLEEP_INTERVAL);
++ }
++
++ /* update power control for afe */
++ status = cx231xx_afe_update_power_control(dev, mode);
++
++ /* update power control for i2s_blk */
++ status = cx231xx_i2s_blk_update_power_control(dev, mode);
++
++ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN, value,
++ 4);
++ cx231xx_info(" The data of PWR_CTL_EN register 0x74"
++ "=0x%0x,0x%0x,0x%0x,0x%0x\n",
++ value[0], value[1], value[2], value[3]);
++
++ return status;
++}
++
++int cx231xx_power_suspend(struct cx231xx *dev)
++{
++ u8 value[4] = { 0, 0, 0, 0 };
++ u32 tmp = 0;
++ int status = 0;
++
++ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, PWR_CTL_EN,
++ value, 4);
++ if (status > 0)
++ return status;
++
++ tmp = *((u32 *) value);
++ tmp &= (~PWR_MODE_MASK);
++
++ value[0] = (u8) tmp;
++ value[1] = (u8) (tmp >> 8);
++ value[2] = (u8) (tmp >> 16);
++ value[3] = (u8) (tmp >> 24);
++ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, PWR_CTL_EN,
++ value, 4);
++
++ return status;
++}
++
++/******************************************************************************
++ * S T R E A M C O N T R O L functions *
++ ******************************************************************************/
++int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask)
++{
++ u8 value[4] = { 0x0, 0x0, 0x0, 0x0 };
++ u32 tmp = 0;
++ int status = 0;
++
++ cx231xx_info("cx231xx_start_stream():: ep_mask = %x\n", ep_mask);
++ status = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET,
++ value, 4);
++ if (status < 0)
++ return status;
++
++ tmp = *((u32 *) value);
++ tmp |= ep_mask;
++ value[0] = (u8) tmp;
++ value[1] = (u8) (tmp >> 8);
++ value[2] = (u8) (tmp >> 16);
++ value[3] = (u8) (tmp >> 24);
++
++ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, EP_MODE_SET,
++ value, 4);
++
++ return status;
++}
++
++int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask)
++{
++ u8 value[4] = { 0x0, 0x0, 0x0, 0x0 };
++ u32 tmp = 0;
++ int status = 0;
++
++ cx231xx_info("cx231xx_stop_stream():: ep_mask = %x\n", ep_mask);
++ status =
++ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, EP_MODE_SET, value, 4);
++ if (status < 0)
++ return status;
++
++ tmp = *((u32 *) value);
++ tmp &= (~ep_mask);
++ value[0] = (u8) tmp;
++ value[1] = (u8) (tmp >> 8);
++ value[2] = (u8) (tmp >> 16);
++ value[3] = (u8) (tmp >> 24);
++
++ status = cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, EP_MODE_SET,
++ value, 4);
++
++ return status;
++}
++
++int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type)
++{
++ int status = 0;
++
++ if (dev->udev->speed == USB_SPEED_HIGH) {
++ switch (media_type) {
++ case 81: /* audio */
++ cx231xx_info("%s: Audio enter HANC\n", __func__);
++ status =
++ cx231xx_mode_register(dev, TS_MODE_REG, 0x9300);
++ break;
++
++ case 2: /* vbi */
++ cx231xx_info("%s: set vanc registers\n", __func__);
++ status = cx231xx_mode_register(dev, TS_MODE_REG, 0x300);
++ break;
++
++ case 3: /* sliced cc */
++ cx231xx_info("%s: set hanc registers\n", __func__);
++ status =
++ cx231xx_mode_register(dev, TS_MODE_REG, 0x1300);
++ break;
++
++ case 0: /* video */
++ cx231xx_info("%s: set video registers\n", __func__);
++ status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100);
++ break;
++
++ case 4: /* ts1 */
++ cx231xx_info("%s: set ts1 registers\n", __func__);
++ status = cx231xx_mode_register(dev, TS_MODE_REG, 0x101);
++ status = cx231xx_mode_register(dev, TS1_CFG_REG, 0x400);
++ break;
++ case 6: /* ts1 parallel mode */
++ cx231xx_info("%s: set ts1 parrallel mode registers\n",
++ __func__);
++ status = cx231xx_mode_register(dev, TS_MODE_REG, 0x100);
++ status = cx231xx_mode_register(dev, TS1_CFG_REG, 0x400);
++ break;
++ }
++ } else {
++ status = cx231xx_mode_register(dev, TS_MODE_REG, 0x101);
++ }
++
++ return status;
++}
++
++int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type)
++{
++ int rc;
++ u32 ep_mask = -1;
++ struct pcb_config *pcb_config;
++
++ /* get EP for media type */
++ pcb_config = (struct pcb_config *)&dev->current_pcb_config;
++
++ if (pcb_config->config_num == 1) {
++ switch (media_type) {
++ case 0: /* Video */
++ ep_mask = ENABLE_EP4; /* ep4 [00:1000] */
++ break;
++ case 1: /* Audio */
++ ep_mask = ENABLE_EP3; /* ep3 [00:0100] */
++ break;
++ case 2: /* Vbi */
++ ep_mask = ENABLE_EP5; /* ep5 [01:0000] */
++ break;
++ case 3: /* Sliced_cc */
++ ep_mask = ENABLE_EP6; /* ep6 [10:0000] */
++ break;
++ case 4: /* ts1 */
++ case 6: /* ts1 parallel mode */
++ ep_mask = ENABLE_EP1; /* ep1 [00:0001] */
++ break;
++ case 5: /* ts2 */
++ ep_mask = ENABLE_EP2; /* ep2 [00:0010] */
++ break;
++ }
++
++ } else if (pcb_config->config_num > 1) {
++ switch (media_type) {
++ case 0: /* Video */
++ ep_mask = ENABLE_EP4; /* ep4 [00:1000] */
++ break;
++ case 1: /* Audio */
++ ep_mask = ENABLE_EP3; /* ep3 [00:0100] */
++ break;
++ case 2: /* Vbi */
++ ep_mask = ENABLE_EP5; /* ep5 [01:0000] */
++ break;
++ case 3: /* Sliced_cc */
++ ep_mask = ENABLE_EP6; /* ep6 [10:0000] */
++ break;
++ case 4: /* ts1 */
++ case 6: /* ts1 parallel mode */
++ ep_mask = ENABLE_EP1; /* ep1 [00:0001] */
++ break;
++ case 5: /* ts2 */
++ ep_mask = ENABLE_EP2; /* ep2 [00:0010] */
++ break;
++ }
++
++ }
++
++ if (start) {
++ rc = cx231xx_initialize_stream_xfer(dev, media_type);
++
++ if (rc < 0)
++ return rc;
++
++ /* enable video capture */
++ if (ep_mask > 0)
++ rc = cx231xx_start_stream(dev, ep_mask);
++ } else {
++ /* disable video capture */
++ if (ep_mask > 0)
++ rc = cx231xx_stop_stream(dev, ep_mask);
++ }
++
++ if (dev->mode == CX231XX_ANALOG_MODE)
++ ;/* do any in Analog mode */
++ else
++ ;/* do any in digital mode */
++
++ return rc;
++}
++EXPORT_SYMBOL_GPL(cx231xx_capture_start);
++
++/*****************************************************************************
++* G P I O B I T control functions *
++******************************************************************************/
++int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 * gpio_val)
++{
++ int status = 0;
++
++ status = cx231xx_send_gpio_cmd(dev, gpio_bit, gpio_val, 4, 0, 0);
++
++ return status;
++}
++
++int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 * gpio_val)
++{
++ int status = 0;
++
++ status = cx231xx_send_gpio_cmd(dev, gpio_bit, gpio_val, 4, 0, 1);
++
++ return status;
++}
++
++/*
++* cx231xx_set_gpio_direction
++* Sets the direction of the GPIO pin to input or output
++*
++* Parameters :
++* pin_number : The GPIO Pin number to program the direction for
++* from 0 to 31
++* pin_value : The Direction of the GPIO Pin under reference.
++* 0 = Input direction
++* 1 = Output direction
++*/
++int cx231xx_set_gpio_direction(struct cx231xx *dev,
++ int pin_number, int pin_value)
++{
++ int status = 0;
++ u32 value = 0;
++
++ /* Check for valid pin_number - if 32 , bail out */
++ if (pin_number >= 32)
++ return -EINVAL;
++
++ /* input */
++ if (pin_value == 0)
++ value = dev->gpio_dir & (~(1 << pin_number)); /* clear */
++ else
++ value = dev->gpio_dir | (1 << pin_number);
++
++ status = cx231xx_set_gpio_bit(dev, value, (u8 *) &dev->gpio_val);
++
++ /* cache the value for future */
++ dev->gpio_dir = value;
++
++ return status;
++}
++
++/*
++* cx231xx_set_gpio_value
++* Sets the value of the GPIO pin to Logic high or low. The Pin under
++* reference should ALREADY BE SET IN OUTPUT MODE !!!!!!!!!
++*
++* Parameters :
++* pin_number : The GPIO Pin number to program the direction for
++* pin_value : The value of the GPIO Pin under reference.
++* 0 = set it to 0
++* 1 = set it to 1
++*/
++int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value)
++{
++ int status = 0;
++ u32 value = 0;
++
++ /* Check for valid pin_number - if 0xFF , bail out */
++ if (pin_number >= 32)
++ return -EINVAL;
++
++ /* first do a sanity check - if the Pin is not output, make it output */
++ if ((dev->gpio_dir & (1 << pin_number)) == 0x00) {
++ /* It was in input mode */
++ value = dev->gpio_dir | (1 << pin_number);
++ dev->gpio_dir = value;
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
++ (u8 *) &dev->gpio_val);
++ value = 0;
++ }
++
++ if (pin_value == 0)
++ value = dev->gpio_val & (~(1 << pin_number));
++ else
++ value = dev->gpio_val | (1 << pin_number);
++
++ /* store the value */
++ dev->gpio_val = value;
++
++ /* toggle bit0 of GP_IO */
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++
++ return status;
++}
++
++/*****************************************************************************
++* G P I O I2C related functions *
++******************************************************************************/
++int cx231xx_gpio_i2c_start(struct cx231xx *dev)
++{
++ int status = 0;
++
++ /* set SCL to output 1 ; set SDA to output 1 */
++ dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio;
++ dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio;
++ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
++ dev->gpio_val |= 1 << dev->board.tuner_sda_gpio;
++
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++ if (status < 0)
++ return -EINVAL;
++
++ /* set SCL to output 1; set SDA to output 0 */
++ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
++ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
++
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++ if (status < 0)
++ return -EINVAL;
++
++ /* set SCL to output 0; set SDA to output 0 */
++ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
++ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
++
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++ if (status < 0)
++ return -EINVAL;
++
++ return status;
++}
++
++int cx231xx_gpio_i2c_end(struct cx231xx *dev)
++{
++ int status = 0;
++
++ /* set SCL to output 0; set SDA to output 0 */
++ dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio;
++ dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio;
++
++ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
++ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
++
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++ if (status < 0)
++ return -EINVAL;
++
++ /* set SCL to output 1; set SDA to output 0 */
++ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
++ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
++
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++ if (status < 0)
++ return -EINVAL;
++
++ /* set SCL to input ,release SCL cable control
++ set SDA to input ,release SDA cable control */
++ dev->gpio_dir &= ~(1 << dev->board.tuner_scl_gpio);
++ dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio);
++
++ status =
++ cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++ if (status < 0)
++ return -EINVAL;
++
++ return status;
++}
++
++int cx231xx_gpio_i2c_write_byte(struct cx231xx *dev, u8 data)
++{
++ int status = 0;
++ u8 i;
++
++ /* set SCL to output ; set SDA to output */
++ dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio;
++ dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio;
++
++ for (i = 0; i < 8; i++) {
++ if (((data << i) & 0x80) == 0) {
++ /* set SCL to output 0; set SDA to output 0 */
++ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
++ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
++ (u8 *)&dev->gpio_val);
++
++ /* set SCL to output 1; set SDA to output 0 */
++ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
++ (u8 *)&dev->gpio_val);
++
++ /* set SCL to output 0; set SDA to output 0 */
++ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
++ (u8 *)&dev->gpio_val);
++ } else {
++ /* set SCL to output 0; set SDA to output 1 */
++ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
++ dev->gpio_val |= 1 << dev->board.tuner_sda_gpio;
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
++ (u8 *)&dev->gpio_val);
++
++ /* set SCL to output 1; set SDA to output 1 */
++ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
++ (u8 *)&dev->gpio_val);
++
++ /* set SCL to output 0; set SDA to output 1 */
++ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
++ (u8 *)&dev->gpio_val);
++ }
++ }
++ return status;
++}
++
++int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 * buf)
++{
++ u8 value = 0;
++ int status = 0;
++ u32 gpio_logic_value = 0;
++ u8 i;
++
++ /* read byte */
++ for (i = 0; i < 8; i++) { /* send write I2c addr */
++
++ /* set SCL to output 0; set SDA to input */
++ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
++ (u8 *)&dev->gpio_val);
++
++ /* set SCL to output 1; set SDA to input */
++ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir,
++ (u8 *)&dev->gpio_val);
++
++ /* get SDA data bit */
++ gpio_logic_value = dev->gpio_val;
++ status = cx231xx_get_gpio_bit(dev, dev->gpio_dir,
++ (u8 *)&dev->gpio_val);
++ if ((dev->gpio_val & (1 << dev->board.tuner_sda_gpio)) != 0)
++ value |= (1 << (8 - i - 1));
++
++ dev->gpio_val = gpio_logic_value;
++ }
++
++ /* set SCL to output 0,finish the read latest SCL signal.
++ !!!set SDA to input, never to modify SDA direction at
++ the same times */
++ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++
++ /* store the value */
++ *buf = value & 0xff;
++
++ return status;
++}
++
++int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev)
++{
++ int status = 0;
++ u32 gpio_logic_value = 0;
++ int nCnt = 10;
++ int nInit = nCnt;
++
++ /* clock stretch; set SCL to input; set SDA to input;
++ get SCL value till SCL = 1 */
++ dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio);
++ dev->gpio_dir &= ~(1 << dev->board.tuner_scl_gpio);
++
++ gpio_logic_value = dev->gpio_val;
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++
++ do {
++ msleep(2);
++ status = cx231xx_get_gpio_bit(dev, dev->gpio_dir,
++ (u8 *)&dev->gpio_val);
++ nCnt--;
++ } while (((dev->gpio_val &
++ (1 << dev->board.tuner_scl_gpio)) == 0) &&
++ (nCnt > 0));
++
++ if (nCnt == 0)
++ cx231xx_info("No ACK after %d msec -GPIO I2C failed!",
++ nInit * 10);
++
++ /* readAck
++ throuth clock stretch ,slave has given a SCL signal,
++ so the SDA data can be directly read. */
++ status = cx231xx_get_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++
++ if ((dev->gpio_val & 1 << dev->board.tuner_sda_gpio) == 0) {
++ dev->gpio_val = gpio_logic_value;
++ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
++ status = 0;
++ } else {
++ dev->gpio_val = gpio_logic_value;
++ dev->gpio_val |= (1 << dev->board.tuner_sda_gpio);
++ }
++
++ /* read SDA end, set the SCL to output 0, after this operation,
++ SDA direction can be changed. */
++ dev->gpio_val = gpio_logic_value;
++ dev->gpio_dir |= (1 << dev->board.tuner_scl_gpio);
++ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++
++ return status;
++}
++
++int cx231xx_gpio_i2c_write_ack(struct cx231xx *dev)
++{
++ int status = 0;
++
++ /* set SDA to ouput */
++ dev->gpio_dir |= 1 << dev->board.tuner_sda_gpio;
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++
++ /* set SCL = 0 (output); set SDA = 0 (output) */
++ dev->gpio_val &= ~(1 << dev->board.tuner_sda_gpio);
++ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++
++ /* set SCL = 1 (output); set SDA = 0 (output) */
++ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++
++ /* set SCL = 0 (output); set SDA = 0 (output) */
++ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++
++ /* set SDA to input,and then the slave will read data from SDA. */
++ dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio);
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++
++ return status;
++}
++
++int cx231xx_gpio_i2c_write_nak(struct cx231xx *dev)
++{
++ int status = 0;
++
++ /* set scl to output ; set sda to input */
++ dev->gpio_dir |= 1 << dev->board.tuner_scl_gpio;
++ dev->gpio_dir &= ~(1 << dev->board.tuner_sda_gpio);
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++
++ /* set scl to output 0; set sda to input */
++ dev->gpio_val &= ~(1 << dev->board.tuner_scl_gpio);
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++
++ /* set scl to output 1; set sda to input */
++ dev->gpio_val |= 1 << dev->board.tuner_scl_gpio;
++ status = cx231xx_set_gpio_bit(dev, dev->gpio_dir, (u8 *)&dev->gpio_val);
++
++ return status;
++}
++
++/*****************************************************************************
++* G P I O I2C related functions *
++******************************************************************************/
++/* cx231xx_gpio_i2c_read
++ * Function to read data from gpio based I2C interface
++ */
++int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 * buf, u8 len)
++{
++ int status = 0;
++ int i = 0;
++
++ /* get the lock */
++ mutex_lock(&dev->gpio_i2c_lock);
++
++ /* start */
++ status = cx231xx_gpio_i2c_start(dev);
++
++ /* write dev_addr */
++ status = cx231xx_gpio_i2c_write_byte(dev, (dev_addr << 1) + 1);
++
++ /* readAck */
++ status = cx231xx_gpio_i2c_read_ack(dev);
++
++ /* read data */
++ for (i = 0; i < len; i++) {
++ /* read data */
++ buf[i] = 0;
++ status = cx231xx_gpio_i2c_read_byte(dev, &buf[i]);
++
++ if ((i + 1) != len) {
++ /* only do write ack if we more length */
++ status = cx231xx_gpio_i2c_write_ack(dev);
++ }
++ }
++
++ /* write NAK - inform reads are complete */
++ status = cx231xx_gpio_i2c_write_nak(dev);
++
++ /* write end */
++ status = cx231xx_gpio_i2c_end(dev);
++
++ /* release the lock */
++ mutex_unlock(&dev->gpio_i2c_lock);
++
++ return status;
++}
++
++/* cx231xx_gpio_i2c_write
++ * Function to write data to gpio based I2C interface
++ */
++int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 * buf, u8 len)
++{
++ int status = 0;
++ int i = 0;
++
++ /* get the lock */
++ mutex_lock(&dev->gpio_i2c_lock);
++
++ /* start */
++ status = cx231xx_gpio_i2c_start(dev);
++
++ /* write dev_addr */
++ status = cx231xx_gpio_i2c_write_byte(dev, dev_addr << 1);
++
++ /* read Ack */
++ status = cx231xx_gpio_i2c_read_ack(dev);
++
++ for (i = 0; i < len; i++) {
++ /* Write data */
++ status = cx231xx_gpio_i2c_write_byte(dev, buf[i]);
++
++ /* read Ack */
++ status = cx231xx_gpio_i2c_read_ack(dev);
++ }
++
++ /* write End */
++ status = cx231xx_gpio_i2c_end(dev);
++
++ /* release the lock */
++ mutex_unlock(&dev->gpio_i2c_lock);
++
++ return 0;
++}
+diff --git a/drivers/media/video/cx231xx/cx231xx-cards.c b/drivers/media/video/cx231xx/cx231xx-cards.c
+new file mode 100644
+index 0000000..d2f2091
+--- /dev/null
++++ b/drivers/media/video/cx231xx/cx231xx-cards.c
+@@ -0,0 +1,919 @@
++/*
++ cx231xx-cards.c - driver for Conexant Cx23100/101/102
++ USB video capture devices
++
++ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
++ Based on em28xx driver
++
++ 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 <linux/init.h>
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/i2c.h>
++#include <linux/usb.h>
++#include <media/tuner.h>
++#include <media/tveeprom.h>
++#include <media/v4l2-common.h>
++#include <media/v4l2-chip-ident.h>
++
++#include <media/cx25840.h>
++#include "xc5000.h"
++
++#include "cx231xx.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");
++
++/* Bitmask marking allocated devices from 0 to CX231XX_MAXBOARDS */
++static unsigned long cx231xx_devused;
++
++/*
++ * Reset sequences for analog/digital modes
++ */
++
++static struct cx231xx_reg_seq RDE250_XCV_TUNER[] = {
++ {0x03, 0x01, 10},
++ {0x03, 0x00, 30},
++ {0x03, 0x01, 10},
++ {-1, -1, -1},
++};
++
++/*
++ * Board definitions
++ */
++struct cx231xx_board cx231xx_boards[] = {
++ [CX231XX_BOARD_UNKNOWN] = {
++ .name = "Unknown CX231xx video grabber",
++ .tuner_type = TUNER_ABSENT,
++ .input = {{
++ .type = CX231XX_VMUX_TELEVISION,
++ .vmux = CX231XX_VIN_3_1,
++ .amux = CX231XX_AMUX_VIDEO,
++ .gpio = 0,
++ }, {
++ .type = CX231XX_VMUX_COMPOSITE1,
++ .vmux = CX231XX_VIN_2_1,
++ .amux = CX231XX_AMUX_LINE_IN,
++ .gpio = 0,
++ }, {
++ .type = CX231XX_VMUX_SVIDEO,
++ .vmux = CX231XX_VIN_1_1 |
++ (CX231XX_VIN_1_2 << 8) |
++ CX25840_SVIDEO_ON,
++ .amux = CX231XX_AMUX_LINE_IN,
++ .gpio = 0,
++ }
++ },
++ },
++ [CX231XX_BOARD_CNXT_RDE_250] = {
++ .name = "Conexant Hybrid TV - RDE250",
++ .tuner_type = TUNER_XC5000,
++ .tuner_addr = 0x61,
++ .tuner_gpio = RDE250_XCV_TUNER,
++ .tuner_sif_gpio = 0x05,
++ .tuner_scl_gpio = 0x1a,
++ .tuner_sda_gpio = 0x1b,
++ .decoder = CX231XX_AVDECODER,
++ .demod_xfer_mode = 0,
++ .ctl_pin_status_mask = 0xFFFFFFC4,
++ .agc_analog_digital_select_gpio = 0x0c,
++ .gpio_pin_status_mask = 0x4001000,
++ .tuner_i2c_master = 1,
++ .demod_i2c_master = 2,
++ .has_dvb = 1,
++ .demod_addr = 0x02,
++ .norm = V4L2_STD_PAL,
++
++ .input = {{
++ .type = CX231XX_VMUX_TELEVISION,
++ .vmux = CX231XX_VIN_3_1,
++ .amux = CX231XX_AMUX_VIDEO,
++ .gpio = 0,
++ }, {
++ .type = CX231XX_VMUX_COMPOSITE1,
++ .vmux = CX231XX_VIN_2_1,
++ .amux = CX231XX_AMUX_LINE_IN,
++ .gpio = 0,
++ }, {
++ .type = CX231XX_VMUX_SVIDEO,
++ .vmux = CX231XX_VIN_1_1 |
++ (CX231XX_VIN_1_2 << 8) |
++ CX25840_SVIDEO_ON,
++ .amux = CX231XX_AMUX_LINE_IN,
++ .gpio = 0,
++ }
++ },
++ },
++
++ [CX231XX_BOARD_CNXT_RDU_250] = {
++ .name = "Conexant Hybrid TV - RDU250",
++ .tuner_type = TUNER_XC5000,
++ .tuner_addr = 0x61,
++ .tuner_gpio = RDE250_XCV_TUNER,
++ .tuner_sif_gpio = 0x05,
++ .tuner_scl_gpio = 0x1a,
++ .tuner_sda_gpio = 0x1b,
++ .decoder = CX231XX_AVDECODER,
++ .demod_xfer_mode = 0,
++ .ctl_pin_status_mask = 0xFFFFFFC4,
++ .agc_analog_digital_select_gpio = 0x0c,
++ .gpio_pin_status_mask = 0x4001000,
++ .tuner_i2c_master = 1,
++ .demod_i2c_master = 2,
++ .has_dvb = 1,
++ .demod_addr = 0x32,
++ .norm = V4L2_STD_NTSC,
++
++ .input = {{
++ .type = CX231XX_VMUX_TELEVISION,
++ .vmux = CX231XX_VIN_3_1,
++ .amux = CX231XX_AMUX_VIDEO,
++ .gpio = 0,
++ }, {
++ .type = CX231XX_VMUX_COMPOSITE1,
++ .vmux = CX231XX_VIN_2_1,
++ .amux = CX231XX_AMUX_LINE_IN,
++ .gpio = 0,
++ }, {
++ .type = CX231XX_VMUX_SVIDEO,
++ .vmux = CX231XX_VIN_1_1 |
++ (CX231XX_VIN_1_2 << 8) |
++ CX25840_SVIDEO_ON,
++ .amux = CX231XX_AMUX_LINE_IN,
++ .gpio = 0,
++ }
++ },
++ },
++};
++const unsigned int cx231xx_bcount = ARRAY_SIZE(cx231xx_boards);
++
++/* table of devices that work with this driver */
++struct usb_device_id cx231xx_id_table[] = {
++ {USB_DEVICE(0x0572, 0x5A3C),
++ .driver_info = CX231XX_BOARD_UNKNOWN},
++ {USB_DEVICE(0x0572, 0x58A2),
++ .driver_info = CX231XX_BOARD_CNXT_RDE_250},
++ {USB_DEVICE(0x0572, 0x58A1),
++ .driver_info = CX231XX_BOARD_CNXT_RDU_250},
++ {},
++};
++
++MODULE_DEVICE_TABLE(usb, cx231xx_id_table);
++
++/* cx231xx_tuner_callback
++ * will be used to reset XC5000 tuner using GPIO pin
++ */
++
++int cx231xx_tuner_callback(void *ptr, int component, int command, int arg)
++{
++ int rc = 0;
++ struct cx231xx *dev = ptr;
++
++ if (dev->tuner_type == TUNER_XC5000) {
++ if (command == XC5000_TUNER_RESET) {
++ cx231xx_info
++ ("Tuner CB: RESET: cmd %d : tuner type %d \n",
++ command, dev->tuner_type);
++ cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
++ 1);
++ msleep(10);
++ cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
++ 0);
++ msleep(330);
++ cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit,
++ 1);
++ msleep(10);
++ }
++ }
++ return rc;
++}
++EXPORT_SYMBOL_GPL(cx231xx_tuner_callback);
++
++static inline void cx231xx_set_model(struct cx231xx *dev)
++{
++ memcpy(&dev->board, &cx231xx_boards[dev->model], sizeof(dev->board));
++}
++
++/* Since cx231xx_pre_card_setup() requires a proper dev->model,
++ * this won't work for boards with generic PCI IDs
++ */
++void cx231xx_pre_card_setup(struct cx231xx *dev)
++{
++
++ cx231xx_set_model(dev);
++
++ cx231xx_info("Identified as %s (card=%d)\n",
++ dev->board.name, dev->model);
++
++ /* set the direction for GPIO pins */
++ cx231xx_set_gpio_direction(dev, dev->board.tuner_gpio->bit, 1);
++ cx231xx_set_gpio_value(dev, dev->board.tuner_gpio->bit, 1);
++ cx231xx_set_gpio_direction(dev, dev->board.tuner_sif_gpio, 1);
++
++ /* request some modules if any required */
++
++ /* reset the Tuner */
++ cx231xx_gpio_set(dev, dev->board.tuner_gpio);
++
++ /* set the mode to Analog mode initially */
++ cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
++
++ /* Unlock device */
++ /* cx231xx_set_mode(dev, CX231XX_SUSPEND); */
++
++}
++
++static void cx231xx_config_tuner(struct cx231xx *dev)
++{
++ 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 = cx231xx_tuner_callback;
++
++ tuner_call(dev, tuner, s_type_addr, &tun_setup);
++
++#if 0
++ if (tun_setup.type == TUNER_XC5000) {
++ static struct xc2028_ctrl ctrl = {
++ .fname = XC5000_DEFAULT_FIRMWARE,
++ .max_len = 64,
++ .demod = 0;
++ };
++ struct v4l2_priv_tun_config cfg = {
++ .tuner = dev->tuner_type,
++ .priv = &ctrl,
++ };
++ tuner_call(dev, tuner, s_config, &cfg);
++ }
++#endif
++ /* configure tuner */
++ f.tuner = 0;
++ f.type = V4L2_TUNER_ANALOG_TV;
++ f.frequency = 9076; /* just a magic number */
++ dev->ctl_freq = f.frequency;
++ call_all(dev, tuner, s_frequency, &f);
++
++}
++
++/* ----------------------------------------------------------------------- */
++void cx231xx_set_ir(struct cx231xx *dev, struct IR_i2c *ir)
++{
++ if (disable_ir) {
++ ir->get_key = NULL;
++ return;
++ }
++
++ /* detect & configure */
++ switch (dev->model) {
++
++ case CX231XX_BOARD_CNXT_RDE_250:
++ break;
++ case CX231XX_BOARD_CNXT_RDU_250:
++ break;
++ default:
++ break;
++ }
++}
++
++void cx231xx_card_setup(struct cx231xx *dev)
++{
++
++ cx231xx_set_model(dev);
++
++ dev->tuner_type = cx231xx_boards[dev->model].tuner_type;
++ if (cx231xx_boards[dev->model].tuner_addr)
++ dev->tuner_addr = cx231xx_boards[dev->model].tuner_addr;
++
++ /* request some modules */
++ if (dev->board.decoder == CX231XX_AVDECODER) {
++ dev->sd_cx25840 =
++ v4l2_i2c_new_subdev(&dev->i2c_bus[0].i2c_adap,
++ "cx25840", "cx25840", 0x88 >> 1);
++ if (dev->sd_cx25840 == NULL)
++ cx231xx_info("cx25840 subdev registration failure\n");
++ cx25840_call(dev, core, init, 0);
++
++ }
++
++ if (dev->board.tuner_type != TUNER_ABSENT) {
++ dev->sd_tuner =
++ v4l2_i2c_new_subdev(&dev->i2c_bus[1].i2c_adap,
++ "tuner", "tuner", 0xc2 >> 1);
++ if (dev->sd_tuner == NULL)
++ cx231xx_info("tuner subdev registration failure\n");
++
++ cx231xx_config_tuner(dev);
++ }
++
++ cx231xx_config_tuner(dev);
++
++#if 0
++ /* TBD IR will be added later */
++ cx231xx_ir_init(dev);
++#endif
++}
++
++/*
++ * cx231xx_config()
++ * inits registers with sane defaults
++ */
++int cx231xx_config(struct cx231xx *dev)
++{
++ /* TBD need to add cx231xx specific code */
++ dev->mute = 1; /* maybe not the right place... */
++ dev->volume = 0x1f;
++
++ return 0;
++}
++
++/*
++ * cx231xx_config_i2c()
++ * configure i2c attached devices
++ */
++void cx231xx_config_i2c(struct cx231xx *dev)
++{
++ struct v4l2_routing route;
++
++ route.input = INPUT(dev->video_input)->vmux;
++ route.output = 0;
++
++ call_all(dev, video, s_stream, 1);
++}
++
++/*
++ * cx231xx_realease_resources()
++ * unregisters the v4l2,i2c and usb devices
++ * called when the device gets disconected or at module unload
++*/
++void cx231xx_release_resources(struct cx231xx *dev)
++{
++
++#if 0 /* TBD IR related */
++ if (dev->ir)
++ cx231xx_ir_fini(dev);
++#endif
++
++ cx231xx_release_analog_resources(dev);
++
++ cx231xx_remove_from_devlist(dev);
++
++ cx231xx_dev_uninit(dev);
++
++ usb_put_dev(dev->udev);
++
++ /* Mark device as unused */
++ cx231xx_devused &= ~(1 << dev->devno);
++}
++
++/*
++ * cx231xx_init_dev()
++ * allocates and inits the device structs, registers i2c bus and v4l device
++ */
++static int cx231xx_init_dev(struct cx231xx **devhandle, struct usb_device *udev,
++ int minor)
++{
++ struct cx231xx *dev = *devhandle;
++ int retval = -ENOMEM;
++ int errCode;
++ unsigned int maxh, maxw;
++
++ dev->udev = udev;
++ mutex_init(&dev->lock);
++ mutex_init(&dev->ctrl_urb_lock);
++ mutex_init(&dev->gpio_i2c_lock);
++
++ spin_lock_init(&dev->video_mode.slock);
++ spin_lock_init(&dev->vbi_mode.slock);
++ spin_lock_init(&dev->sliced_cc_mode.slock);
++
++ init_waitqueue_head(&dev->open);
++ init_waitqueue_head(&dev->wait_frame);
++ init_waitqueue_head(&dev->wait_stream);
++
++ dev->cx231xx_read_ctrl_reg = cx231xx_read_ctrl_reg;
++ dev->cx231xx_write_ctrl_reg = cx231xx_write_ctrl_reg;
++ dev->cx231xx_send_usb_command = cx231xx_send_usb_command;
++ dev->cx231xx_gpio_i2c_read = cx231xx_gpio_i2c_read;
++ dev->cx231xx_gpio_i2c_write = cx231xx_gpio_i2c_write;
++
++ /* Query cx231xx to find what pcb config it is related to */
++ initialize_cx231xx(dev);
++
++ /* Cx231xx pre card setup */
++ cx231xx_pre_card_setup(dev);
++
++ errCode = cx231xx_config(dev);
++ if (errCode) {
++ cx231xx_errdev("error configuring device\n");
++ return -ENOMEM;
++ }
++
++ /* set default norm */
++ dev->norm = dev->board.norm;
++
++ /* register i2c bus */
++ errCode = cx231xx_dev_init(dev);
++ if (errCode < 0) {
++ cx231xx_errdev("%s: cx231xx_i2c_register - errCode [%d]!\n",
++ __func__, errCode);
++ return errCode;
++ }
++
++ /* Do board specific init */
++ cx231xx_card_setup(dev);
++
++ /* configure the device */
++ cx231xx_config_i2c(dev);
++
++ maxw = norm_maxw(dev);
++ maxh = norm_maxh(dev);
++
++ /* set default image size */
++ dev->width = maxw;
++ dev->height = maxh;
++ dev->interlaced = 0;
++ dev->hscale = 0;
++ dev->vscale = 0;
++ dev->video_input = 0;
++
++ errCode = cx231xx_config(dev);
++ if (errCode < 0) {
++ cx231xx_errdev("%s: cx231xx_config - errCode [%d]!\n",
++ __func__, errCode);
++ return errCode;
++ }
++
++ /* init video dma queues */
++ INIT_LIST_HEAD(&dev->video_mode.vidq.active);
++ INIT_LIST_HEAD(&dev->video_mode.vidq.queued);
++
++ /* init vbi dma queues */
++ INIT_LIST_HEAD(&dev->vbi_mode.vidq.active);
++ INIT_LIST_HEAD(&dev->vbi_mode.vidq.queued);
++
++ /* Reset other chips required if they are tied up with GPIO pins */
++
++ cx231xx_add_into_devlist(dev);
++
++ retval = cx231xx_register_analog_devices(dev);
++ if (retval < 0) {
++ cx231xx_release_resources(dev);
++ goto fail_reg_devices;
++ }
++
++ cx231xx_init_extension(dev);
++
++ return 0;
++
++fail_reg_devices:
++ mutex_unlock(&dev->lock);
++ return retval;
++}
++
++#if defined(CONFIG_MODULES) && defined(MODULE)
++static void request_module_async(struct work_struct *work)
++{
++ struct cx231xx *dev = container_of(work,
++ struct cx231xx, request_module_wk);
++
++ if (dev->has_alsa_audio)
++ request_module("cx231xx-alsa");
++
++ if (dev->board.has_dvb)
++ request_module("cx231xx-dvb");
++
++}
++
++static void request_modules(struct cx231xx *dev)
++{
++ INIT_WORK(&dev->request_module_wk, request_module_async);
++ schedule_work(&dev->request_module_wk);
++}
++#else
++#define request_modules(dev)
++#endif /* CONFIG_MODULES */
++
++/*
++ * cx231xx_usb_probe()
++ * checks for supported devices
++ */
++static int cx231xx_usb_probe(struct usb_interface *interface,
++ const struct usb_device_id *id)
++{
++ struct usb_device *udev;
++ struct usb_interface *uif;
++ struct cx231xx *dev = NULL;
++ int retval = -ENODEV;
++ int nr = 0, ifnum;
++ int i, isoc_pipe = 0;
++ char *speed;
++ char descr[255] = "";
++ struct usb_interface *lif = NULL;
++ int skip_interface = 0;
++ struct usb_interface_assoc_descriptor *assoc_desc;
++
++ udev = usb_get_dev(interface_to_usbdev(interface));
++ ifnum = interface->altsetting[0].desc.bInterfaceNumber;
++
++ if (!ifnum) {
++ /*
++ * Interface number 0 - IR interface
++ */
++ /* Check to see next free device and mark as used */
++ nr = find_first_zero_bit(&cx231xx_devused, CX231XX_MAXBOARDS);
++ cx231xx_devused |= 1 << nr;
++
++ if (nr >= CX231XX_MAXBOARDS) {
++ cx231xx_err(DRIVER_NAME ": Supports only %i cx231xx boards.\n",
++ CX231XX_MAXBOARDS);
++ cx231xx_devused &= ~(1 << nr);
++ return -ENOMEM;
++ }
++
++ /* allocate memory for our device state and initialize it */
++ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
++ if (dev == NULL) {
++ cx231xx_err(DRIVER_NAME ": out of memory!\n");
++ cx231xx_devused &= ~(1 << nr);
++ return -ENOMEM;
++ }
++
++ snprintf(dev->name, 29, "cx231xx #%d", nr);
++ dev->devno = nr;
++ dev->model = id->driver_info;
++ dev->video_mode.alt = -1;
++ dev->interface_count++;
++
++ /* reset gpio dir and value */
++ dev->gpio_dir = 0;
++ dev->gpio_val = 0;
++ dev->xc_fw_load_done = 0;
++ dev->has_alsa_audio = 1;
++ dev->power_mode = -1;
++
++ /* 0 - vbi ; 1 -sliced cc mode */
++ dev->vbi_or_sliced_cc_mode = 0;
++
++ /* get maximum no.of IAD interfaces */
++ assoc_desc = udev->actconfig->intf_assoc[0];
++ dev->max_iad_interface_count = assoc_desc->bInterfaceCount;
++
++ /* init CIR module TBD */
++
++ /* store the current interface */
++ lif = interface;
++
++ switch (udev->speed) {
++ case USB_SPEED_LOW:
++ speed = "1.5";
++ break;
++ case USB_SPEED_UNKNOWN:
++ case USB_SPEED_FULL:
++ speed = "12";
++ break;
++ case USB_SPEED_HIGH:
++ speed = "480";
++ break;
++ default:
++ speed = "unknown";
++ }
++
++ if (udev->manufacturer)
++ strlcpy(descr, udev->manufacturer, sizeof(descr));
++
++ if (udev->product) {
++ if (*descr)
++ strlcat(descr, " ", sizeof(descr));
++ strlcat(descr, udev->product, sizeof(descr));
++ }
++ if (*descr)
++ strlcat(descr, " ", sizeof(descr));
++
++ cx231xx_info("New device %s@ %s Mbps "
++ "(%04x:%04x) with %d interfaces\n",
++ descr,
++ speed,
++ le16_to_cpu(udev->descriptor.idVendor),
++ le16_to_cpu(udev->descriptor.idProduct),
++ dev->max_iad_interface_count);
++ } else {
++ /* Get dev structure first */
++ dev = usb_get_intfdata(udev->actconfig->interface[0]);
++ if (dev == NULL) {
++ cx231xx_err(DRIVER_NAME ": out of first interface!\n");
++ return -ENODEV;
++ }
++
++ /* store the interface 0 back */
++ lif = udev->actconfig->interface[0];
++
++ /* increment interface count */
++ dev->interface_count++;
++
++ /* get device number */
++ nr = dev->devno;
++
++ /*
++ * set skip interface, for all interfaces but
++ * interface 1 and the last one
++ */
++ if ((ifnum != 1) && ((dev->interface_count - 1)
++ != dev->max_iad_interface_count))
++ skip_interface = 1;
++
++ if (ifnum == 1) {
++ assoc_desc = udev->actconfig->intf_assoc[0];
++ if (assoc_desc->bFirstInterface != ifnum) {
++ cx231xx_err(DRIVER_NAME ": Not found "
++ "matching IAD interface\n");
++ return -ENODEV;
++ }
++ }
++ }
++
++ if (skip_interface)
++ return -ENODEV;
++
++ cx231xx_info("registering interface %d\n", ifnum);
++
++ /* save our data pointer in this interface device */
++ usb_set_intfdata(lif, dev);
++
++ if ((dev->interface_count - 1) != dev->max_iad_interface_count)
++ return 0;
++
++ /*
++ * AV device initialization - only done at the last interface
++ */
++
++ /* Create v4l2 device */
++ snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
++ "%s-%03d", "cx231xx", nr);
++ retval = v4l2_device_register(&udev->dev, &dev->v4l2_dev);
++ if (retval) {
++ cx231xx_errdev("v4l2_device_register failed\n");
++ cx231xx_devused &= ~(1 << nr);
++ kfree(dev);
++ return -EIO;
++ }
++
++ /* allocate device struct */
++ retval = cx231xx_init_dev(&dev, udev, nr);
++ if (retval) {
++ cx231xx_devused &= ~(1 << dev->devno);
++ v4l2_device_unregister(&dev->v4l2_dev);
++ kfree(dev);
++ return retval;
++ }
++
++ /* compute alternate max packet sizes for video */
++ uif = udev->actconfig->interface[dev->current_pcb_config.
++ hs_config_info[0].interface_info.video_index + 1];
++
++ dev->video_mode.end_point_addr = le16_to_cpu(uif->altsetting[0].
++ endpoint[isoc_pipe].desc.bEndpointAddress);
++
++ dev->video_mode.num_alt = uif->num_altsetting;
++ cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
++ dev->video_mode.end_point_addr,
++ dev->video_mode.num_alt);
++ dev->video_mode.alt_max_pkt_size =
++ kmalloc(32 * dev->video_mode.num_alt, GFP_KERNEL);
++
++ if (dev->video_mode.alt_max_pkt_size == NULL) {
++ cx231xx_errdev("out of memory!\n");
++ cx231xx_devused &= ~(1 << nr);
++ v4l2_device_unregister(&dev->v4l2_dev);
++ kfree(dev);
++ return -ENOMEM;
++ }
++
++ for (i = 0; i < dev->video_mode.num_alt; i++) {
++ u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
++ desc.wMaxPacketSize);
++ dev->video_mode.alt_max_pkt_size[i] =
++ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
++ cx231xx_info("Alternate setting %i, max size= %i\n", i,
++ dev->video_mode.alt_max_pkt_size[i]);
++ }
++
++ /* compute alternate max packet sizes for vbi */
++ uif = udev->actconfig->interface[dev->current_pcb_config.
++ hs_config_info[0].interface_info.
++ vanc_index + 1];
++
++ dev->vbi_mode.end_point_addr =
++ le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc.
++ bEndpointAddress);
++
++ dev->vbi_mode.num_alt = uif->num_altsetting;
++ cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
++ dev->vbi_mode.end_point_addr,
++ dev->vbi_mode.num_alt);
++ dev->vbi_mode.alt_max_pkt_size =
++ kmalloc(32 * dev->vbi_mode.num_alt, GFP_KERNEL);
++
++ if (dev->vbi_mode.alt_max_pkt_size == NULL) {
++ cx231xx_errdev("out of memory!\n");
++ cx231xx_devused &= ~(1 << nr);
++ v4l2_device_unregister(&dev->v4l2_dev);
++ kfree(dev);
++ return -ENOMEM;
++ }
++
++ for (i = 0; i < dev->vbi_mode.num_alt; i++) {
++ u16 tmp =
++ le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
++ desc.wMaxPacketSize);
++ dev->vbi_mode.alt_max_pkt_size[i] =
++ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
++ cx231xx_info("Alternate setting %i, max size= %i\n", i,
++ dev->vbi_mode.alt_max_pkt_size[i]);
++ }
++
++ /* compute alternate max packet sizes for sliced CC */
++ uif = udev->actconfig->interface[dev->current_pcb_config.
++ hs_config_info[0].interface_info.
++ hanc_index + 1];
++
++ dev->sliced_cc_mode.end_point_addr =
++ le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].desc.
++ bEndpointAddress);
++
++ dev->sliced_cc_mode.num_alt = uif->num_altsetting;
++ cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
++ dev->sliced_cc_mode.end_point_addr,
++ dev->sliced_cc_mode.num_alt);
++ dev->sliced_cc_mode.alt_max_pkt_size =
++ kmalloc(32 * dev->sliced_cc_mode.num_alt, GFP_KERNEL);
++
++ if (dev->sliced_cc_mode.alt_max_pkt_size == NULL) {
++ cx231xx_errdev("out of memory!\n");
++ cx231xx_devused &= ~(1 << nr);
++ v4l2_device_unregister(&dev->v4l2_dev);
++ kfree(dev);
++ return -ENOMEM;
++ }
++
++ for (i = 0; i < dev->sliced_cc_mode.num_alt; i++) {
++ u16 tmp = le16_to_cpu(uif->altsetting[i].endpoint[isoc_pipe].
++ desc.wMaxPacketSize);
++ dev->sliced_cc_mode.alt_max_pkt_size[i] =
++ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
++ cx231xx_info("Alternate setting %i, max size= %i\n", i,
++ dev->sliced_cc_mode.alt_max_pkt_size[i]);
++ }
++
++ if (dev->current_pcb_config.ts1_source != 0xff) {
++ /* compute alternate max packet sizes for TS1 */
++ uif = udev->actconfig->interface[dev->current_pcb_config.
++ hs_config_info[0].
++ interface_info.
++ ts1_index + 1];
++
++ dev->ts1_mode.end_point_addr =
++ le16_to_cpu(uif->altsetting[0].endpoint[isoc_pipe].
++ desc.bEndpointAddress);
++
++ dev->ts1_mode.num_alt = uif->num_altsetting;
++ cx231xx_info("EndPoint Addr 0x%x, Alternate settings: %i\n",
++ dev->ts1_mode.end_point_addr,
++ dev->ts1_mode.num_alt);
++ dev->ts1_mode.alt_max_pkt_size =
++ kmalloc(32 * dev->ts1_mode.num_alt, GFP_KERNEL);
++
++ if (dev->ts1_mode.alt_max_pkt_size == NULL) {
++ cx231xx_errdev("out of memory!\n");
++ cx231xx_devused &= ~(1 << nr);
++ v4l2_device_unregister(&dev->v4l2_dev);
++ kfree(dev);
++ return -ENOMEM;
++ }
++
++ for (i = 0; i < dev->ts1_mode.num_alt; i++) {
++ u16 tmp = le16_to_cpu(uif->altsetting[i].
++ endpoint[isoc_pipe].desc.
++ wMaxPacketSize);
++ dev->ts1_mode.alt_max_pkt_size[i] =
++ (tmp & 0x07ff) * (((tmp & 0x1800) >> 11) + 1);
++ cx231xx_info("Alternate setting %i, max size= %i\n", i,
++ dev->ts1_mode.alt_max_pkt_size[i]);
++ }
++ }
++
++ /* load other modules required */
++ request_modules(dev);
++
++ return 0;
++}
++
++/*
++ * cx231xx_usb_disconnect()
++ * called when the device gets diconencted
++ * video device will be unregistered on v4l2_close in case it is still open
++ */
++static void cx231xx_usb_disconnect(struct usb_interface *interface)
++{
++ struct cx231xx *dev;
++
++ dev = usb_get_intfdata(interface);
++ usb_set_intfdata(interface, NULL);
++
++ if (!dev)
++ return;
++
++ if (!dev->udev)
++ return;
++
++ /* delete v4l2 device */
++ v4l2_device_unregister(&dev->v4l2_dev);
++
++ /* wait until all current v4l2 io is finished then deallocate
++ resources */
++ mutex_lock(&dev->lock);
++
++ wake_up_interruptible_all(&dev->open);
++
++ if (dev->users) {
++ cx231xx_warn
++ ("device /dev/video%d is open! Deregistration and memory "
++ "deallocation are deferred on close.\n", dev->vdev->num);
++
++ dev->state |= DEV_MISCONFIGURED;
++ cx231xx_uninit_isoc(dev);
++ dev->state |= DEV_DISCONNECTED;
++ wake_up_interruptible(&dev->wait_frame);
++ wake_up_interruptible(&dev->wait_stream);
++ } else {
++ dev->state |= DEV_DISCONNECTED;
++ cx231xx_release_resources(dev);
++ }
++
++ cx231xx_close_extension(dev);
++
++ mutex_unlock(&dev->lock);
++
++ if (!dev->users) {
++ kfree(dev->video_mode.alt_max_pkt_size);
++ kfree(dev->vbi_mode.alt_max_pkt_size);
++ kfree(dev->sliced_cc_mode.alt_max_pkt_size);
++ kfree(dev->ts1_mode.alt_max_pkt_size);
++ kfree(dev);
++ }
++}
++
++static struct usb_driver cx231xx_usb_driver = {
++ .name = "cx231xx",
++ .probe = cx231xx_usb_probe,
++ .disconnect = cx231xx_usb_disconnect,
++ .id_table = cx231xx_id_table,
++};
++
++static int __init cx231xx_module_init(void)
++{
++ int result;
++
++ printk(KERN_INFO DRIVER_NAME " v4l2 driver loaded.\n");
++
++ /* register this driver with the USB subsystem */
++ result = usb_register(&cx231xx_usb_driver);
++ if (result)
++ cx231xx_err(DRIVER_NAME
++ " usb_register failed. Error number %d.\n", result);
++
++ return result;
++}
++
++static void __exit cx231xx_module_exit(void)
++{
++ /* deregister this driver with the USB subsystem */
++ usb_deregister(&cx231xx_usb_driver);
++}
++
++module_init(cx231xx_module_init);
++module_exit(cx231xx_module_exit);
+diff --git a/drivers/media/video/cx231xx/cx231xx-conf-reg.h b/drivers/media/video/cx231xx/cx231xx-conf-reg.h
+new file mode 100644
+index 0000000..a6f398a
+--- /dev/null
++++ b/drivers/media/video/cx231xx/cx231xx-conf-reg.h
+@@ -0,0 +1,494 @@
++/*
++ cx231xx_conf-reg.h - driver for Conexant Cx23100/101/102 USB
++ video capture devices
++
++ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#ifndef _POLARIS_REG_H_
++#define _POLARIS_REG_H_
++
++#define BOARD_CFG_STAT 0x0
++#define TS_MODE_REG 0x4
++#define TS1_CFG_REG 0x8
++#define TS1_LENGTH_REG 0xc
++#define TS2_CFG_REG 0x10
++#define TS2_LENGTH_REG 0x14
++#define EP_MODE_SET 0x18
++#define CIR_PWR_PTN1 0x1c
++#define CIR_PWR_PTN2 0x20
++#define CIR_PWR_PTN3 0x24
++#define CIR_PWR_MASK0 0x28
++#define CIR_PWR_MASK1 0x2c
++#define CIR_PWR_MASK2 0x30
++#define CIR_GAIN 0x34
++#define CIR_CAR_REG 0x38
++#define CIR_OT_CFG1 0x40
++#define CIR_OT_CFG2 0x44
++#define PWR_CTL_EN 0x74
++
++/* Polaris Endpoints capture mask for register EP_MODE_SET */
++#define ENABLE_EP1 0x01 /* Bit[0]=1 */
++#define ENABLE_EP2 0x02 /* Bit[1]=1 */
++#define ENABLE_EP3 0x04 /* Bit[2]=1 */
++#define ENABLE_EP4 0x08 /* Bit[3]=1 */
++#define ENABLE_EP5 0x10 /* Bit[4]=1 */
++#define ENABLE_EP6 0x20 /* Bit[5]=1 */
++
++/* Bit definition for register PWR_CTL_EN */
++#define PWR_MODE_MASK 0x17f
++#define PWR_AV_EN 0x08 /* bit3 */
++#define PWR_ISO_EN 0x40 /* bit6 */
++#define PWR_AV_MODE 0x30 /* bit4,5 */
++#define PWR_TUNER_EN 0x04 /* bit2 */
++#define PWR_DEMOD_EN 0x02 /* bit1 */
++#define I2C_DEMOD_EN 0x01 /* bit0 */
++#define PWR_RESETOUT_EN 0x100 /* bit8 */
++
++enum AV_MODE{
++ POLARIS_AVMODE_DEFAULT = 0,
++ POLARIS_AVMODE_DIGITAL = 0x10,
++ POLARIS_AVMODE_ANALOGT_TV = 0x20,
++ POLARIS_AVMODE_ENXTERNAL_AV = 0x30,
++
++};
++
++/* Colibri Registers */
++
++#define SINGLE_ENDED 0x0
++#define LOW_IF 0x4
++#define EU_IF 0x9
++#define US_IF 0xa
++
++#define SUP_BLK_TUNE1 0x00
++#define SUP_BLK_TUNE2 0x01
++#define SUP_BLK_TUNE3 0x02
++#define SUP_BLK_XTAL 0x03
++#define SUP_BLK_PLL1 0x04
++#define SUP_BLK_PLL2 0x05
++#define SUP_BLK_PLL3 0x06
++#define SUP_BLK_REF 0x07
++#define SUP_BLK_PWRDN 0x08
++#define SUP_BLK_TESTPAD 0x09
++#define ADC_COM_INT5_STAB_REF 0x0a
++#define ADC_COM_QUANT 0x0b
++#define ADC_COM_BIAS1 0x0c
++#define ADC_COM_BIAS2 0x0d
++#define ADC_COM_BIAS3 0x0e
++#define TESTBUS_CTRL 0x12
++
++#define FLD_PWRDN_TUNING_BIAS 0x10
++#define FLD_PWRDN_ENABLE_PLL 0x08
++#define FLD_PWRDN_PD_BANDGAP 0x04
++#define FLD_PWRDN_PD_BIAS 0x02
++#define FLD_PWRDN_PD_TUNECK 0x01
++
++
++#define ADC_STATUS_CH1 0x20
++#define ADC_STATUS_CH2 0x40
++#define ADC_STATUS_CH3 0x60
++
++#define ADC_STATUS2_CH1 0x21
++#define ADC_STATUS2_CH2 0x41
++#define ADC_STATUS2_CH3 0x61
++
++#define ADC_CAL_ATEST_CH1 0x22
++#define ADC_CAL_ATEST_CH2 0x42
++#define ADC_CAL_ATEST_CH3 0x62
++
++#define ADC_PWRDN_CLAMP_CH1 0x23
++#define ADC_PWRDN_CLAMP_CH2 0x43
++#define ADC_PWRDN_CLAMP_CH3 0x63
++
++#define ADC_CTRL_DAC23_CH1 0x24
++#define ADC_CTRL_DAC23_CH2 0x44
++#define ADC_CTRL_DAC23_CH3 0x64
++
++#define ADC_CTRL_DAC1_CH1 0x25
++#define ADC_CTRL_DAC1_CH2 0x45
++#define ADC_CTRL_DAC1_CH3 0x65
++
++#define ADC_DCSERVO_DEM_CH1 0x26
++#define ADC_DCSERVO_DEM_CH2 0x46
++#define ADC_DCSERVO_DEM_CH3 0x66
++
++#define ADC_FB_FRCRST_CH1 0x27
++#define ADC_FB_FRCRST_CH2 0x47
++#define ADC_FB_FRCRST_CH3 0x67
++
++#define ADC_INPUT_CH1 0x28
++#define ADC_INPUT_CH2 0x48
++#define ADC_INPUT_CH3 0x68
++#define INPUT_SEL_MASK 0x30 /* [5:4] in_sel */
++
++#define ADC_NTF_PRECLMP_EN_CH1 0x29
++#define ADC_NTF_PRECLMP_EN_CH2 0x49
++#define ADC_NTF_PRECLMP_EN_CH3 0x69
++
++#define ADC_QGAIN_RES_TRM_CH1 0x2a
++#define ADC_QGAIN_RES_TRM_CH2 0x4a
++#define ADC_QGAIN_RES_TRM_CH3 0x6a
++
++#define ADC_SOC_PRECLMP_TERM_CH1 0x2b
++#define ADC_SOC_PRECLMP_TERM_CH2 0x4b
++#define ADC_SOC_PRECLMP_TERM_CH3 0x6b
++
++#define TESTBUS_CTRL_CH1 0x32
++#define TESTBUS_CTRL_CH2 0x52
++#define TESTBUS_CTRL_CH3 0x72
++
++/******************************************************************************
++ * DIF registers *
++ ******************************************************************************/
++#define DIRECT_IF_REVB_BASE 0x00300
++
++/*****************************************************************************/
++#define DIF_PLL_FREQ_WORD (DIRECT_IF_REVB_BASE + 0x00000000)
++/*****************************************************************************/
++#define FLD_DIF_PLL_LOCK 0x80000000
++/* Reserved [30:29] */
++#define FLD_DIF_PLL_FREE_RUN 0x10000000
++#define FLD_DIF_PLL_FREQ 0x0fffffff
++
++/*****************************************************************************/
++#define DIF_PLL_CTRL (DIRECT_IF_REVB_BASE + 0x00000004)
++/*****************************************************************************/
++#define FLD_DIF_KD_PD 0xff000000
++/* Reserved [23:20] */
++#define FLD_DIF_KDS_PD 0x000f0000
++#define FLD_DIF_KI_PD 0x0000ff00
++/* Reserved [7:4] */
++#define FLD_DIF_KIS_PD 0x0000000f
++
++/*****************************************************************************/
++#define DIF_PLL_CTRL1 (DIRECT_IF_REVB_BASE + 0x00000008)
++/*****************************************************************************/
++#define FLD_DIF_KD_FD 0xff000000
++/* Reserved [23:20] */
++#define FLD_DIF_KDS_FD 0x000f0000
++#define FLD_DIF_KI_FD 0x0000ff00
++#define FLD_DIF_SIG_PROP_SZ 0x000000f0
++#define FLD_DIF_KIS_FD 0x0000000f
++
++/*****************************************************************************/
++#define DIF_PLL_CTRL2 (DIRECT_IF_REVB_BASE + 0x0000000c)
++/*****************************************************************************/
++#define FLD_DIF_PLL_AGC_REF 0xfff00000
++#define FLD_DIF_PLL_AGC_KI 0x000f0000
++/* Reserved [15] */
++#define FLD_DIF_FREQ_LIMIT 0x00007000
++#define FLD_DIF_K_FD 0x00000f00
++#define FLD_DIF_DOWNSMPL_FD 0x000000ff
++
++/*****************************************************************************/
++#define DIF_PLL_CTRL3 (DIRECT_IF_REVB_BASE + 0x00000010)
++/*****************************************************************************/
++/* Reserved [31:16] */
++#define FLD_DIF_PLL_AGC_EN 0x00008000
++/* Reserved [14:12] */
++#define FLD_DIF_PLL_MAN_GAIN 0x00000fff
++
++/*****************************************************************************/
++#define DIF_AGC_IF_REF (DIRECT_IF_REVB_BASE + 0x00000014)
++/*****************************************************************************/
++#define FLD_DIF_K_AGC_RF 0xf0000000
++#define FLD_DIF_K_AGC_IF 0x0f000000
++#define FLD_DIF_K_AGC_INT 0x00f00000
++/* Reserved [19:12] */
++#define FLD_DIF_IF_REF 0x00000fff
++
++/*****************************************************************************/
++#define DIF_AGC_CTRL_IF (DIRECT_IF_REVB_BASE + 0x00000018)
++/*****************************************************************************/
++#define FLD_DIF_IF_MAX 0xff000000
++#define FLD_DIF_IF_MIN 0x00ff0000
++#define FLD_DIF_IF_AGC 0x0000ffff
++
++/*****************************************************************************/
++#define DIF_AGC_CTRL_INT (DIRECT_IF_REVB_BASE + 0x0000001c)
++/*****************************************************************************/
++#define FLD_DIF_INT_MAX 0xff000000
++#define FLD_DIF_INT_MIN 0x00ff0000
++#define FLD_DIF_INT_AGC 0x0000ffff
++
++/*****************************************************************************/
++#define DIF_AGC_CTRL_RF (DIRECT_IF_REVB_BASE + 0x00000020)
++/*****************************************************************************/
++#define FLD_DIF_RF_MAX 0xff000000
++#define FLD_DIF_RF_MIN 0x00ff0000
++#define FLD_DIF_RF_AGC 0x0000ffff
++
++/*****************************************************************************/
++#define DIF_AGC_IF_INT_CURRENT (DIRECT_IF_REVB_BASE + 0x00000024)
++/*****************************************************************************/
++#define FLD_DIF_IF_AGC_IN 0xffff0000
++#define FLD_DIF_INT_AGC_IN 0x0000ffff
++
++/*****************************************************************************/
++#define DIF_AGC_RF_CURRENT (DIRECT_IF_REVB_BASE + 0x00000028)
++/*****************************************************************************/
++/* Reserved [31:16] */
++#define FLD_DIF_RF_AGC_IN 0x0000ffff
++
++/*****************************************************************************/
++#define DIF_VIDEO_AGC_CTRL (DIRECT_IF_REVB_BASE + 0x0000002c)
++/*****************************************************************************/
++#define FLD_DIF_AFD 0xc0000000
++#define FLD_DIF_K_VID_AGC 0x30000000
++#define FLD_DIF_LINE_LENGTH 0x0fff0000
++#define FLD_DIF_AGC_GAIN 0x0000ffff
++
++/*****************************************************************************/
++#define DIF_VID_AUD_OVERRIDE (DIRECT_IF_REVB_BASE + 0x00000030)
++/*****************************************************************************/
++#define FLD_DIF_AUDIO_AGC_OVERRIDE 0x80000000
++/* Reserved [30:30] */
++#define FLD_DIF_AUDIO_MAN_GAIN 0x3f000000
++/* Reserved [23:17] */
++#define FLD_DIF_VID_AGC_OVERRIDE 0x00010000
++#define FLD_DIF_VID_MAN_GAIN 0x0000ffff
++
++/*****************************************************************************/
++#define DIF_AV_SEP_CTRL (DIRECT_IF_REVB_BASE + 0x00000034)
++/*****************************************************************************/
++#define FLD_DIF_LPF_FREQ 0xc0000000
++#define FLD_DIF_AV_PHASE_INC 0x3f000000
++#define FLD_DIF_AUDIO_FREQ 0x00ffffff
++
++/*****************************************************************************/
++#define DIF_COMP_FLT_CTRL (DIRECT_IF_REVB_BASE + 0x00000038)
++/*****************************************************************************/
++/* Reserved [31:24] */
++#define FLD_DIF_IIR23_R2 0x00ff0000
++#define FLD_DIF_IIR23_R1 0x0000ff00
++#define FLD_DIF_IIR1_R1 0x000000ff
++
++/*****************************************************************************/
++#define DIF_MISC_CTRL (DIRECT_IF_REVB_BASE + 0x0000003c)
++/*****************************************************************************/
++#define FLD_DIF_DIF_BYPASS 0x80000000
++#define FLD_DIF_FM_NYQ_GAIN 0x40000000
++#define FLD_DIF_RF_AGC_ENA 0x20000000
++#define FLD_DIF_INT_AGC_ENA 0x10000000
++#define FLD_DIF_IF_AGC_ENA 0x08000000
++#define FLD_DIF_FORCE_RF_IF_LOCK 0x04000000
++#define FLD_DIF_VIDEO_AGC_ENA 0x02000000
++#define FLD_DIF_RF_AGC_INV 0x01000000
++#define FLD_DIF_INT_AGC_INV 0x00800000
++#define FLD_DIF_IF_AGC_INV 0x00400000
++#define FLD_DIF_SPEC_INV 0x00200000
++#define FLD_DIF_AUD_FULL_BW 0x00100000
++#define FLD_DIF_AUD_SRC_SEL 0x00080000
++/* Reserved [18] */
++#define FLD_DIF_IF_FREQ 0x00030000
++/* Reserved [15:14] */
++#define FLD_DIF_TIP_OFFSET 0x00003f00
++/* Reserved [7:5] */
++#define FLD_DIF_DITHER_ENA 0x00000010
++/* Reserved [3:1] */
++#define FLD_DIF_RF_IF_LOCK 0x00000001
++
++/*****************************************************************************/
++#define DIF_SRC_PHASE_INC (DIRECT_IF_REVB_BASE + 0x00000040)
++/*****************************************************************************/
++/* Reserved [31:29] */
++#define FLD_DIF_PHASE_INC 0x1fffffff
++
++/*****************************************************************************/
++#define DIF_SRC_GAIN_CONTROL (DIRECT_IF_REVB_BASE + 0x00000044)
++/*****************************************************************************/
++/* Reserved [31:16] */
++#define FLD_DIF_SRC_KI 0x0000ff00
++#define FLD_DIF_SRC_KD 0x000000ff
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF01 (DIRECT_IF_REVB_BASE + 0x00000048)
++/*****************************************************************************/
++/* Reserved [31:19] */
++#define FLD_DIF_BPF_COEFF_0 0x00070000
++/* Reserved [15:4] */
++#define FLD_DIF_BPF_COEFF_1 0x0000000f
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF23 (DIRECT_IF_REVB_BASE + 0x0000004c)
++/*****************************************************************************/
++/* Reserved [31:22] */
++#define FLD_DIF_BPF_COEFF_2 0x003f0000
++/* Reserved [15:7] */
++#define FLD_DIF_BPF_COEFF_3 0x0000007f
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF45 (DIRECT_IF_REVB_BASE + 0x00000050)
++/*****************************************************************************/
++/* Reserved [31:24] */
++#define FLD_DIF_BPF_COEFF_4 0x00ff0000
++/* Reserved [15:8] */
++#define FLD_DIF_BPF_COEFF_5 0x000000ff
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF67 (DIRECT_IF_REVB_BASE + 0x00000054)
++/*****************************************************************************/
++/* Reserved [31:25] */
++#define FLD_DIF_BPF_COEFF_6 0x01ff0000
++/* Reserved [15:9] */
++#define FLD_DIF_BPF_COEFF_7 0x000001ff
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF89 (DIRECT_IF_REVB_BASE + 0x00000058)
++/*****************************************************************************/
++/* Reserved [31:26] */
++#define FLD_DIF_BPF_COEFF_8 0x03ff0000
++/* Reserved [15:10] */
++#define FLD_DIF_BPF_COEFF_9 0x000003ff
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF1011 (DIRECT_IF_REVB_BASE + 0x0000005c)
++/*****************************************************************************/
++/* Reserved [31:27] */
++#define FLD_DIF_BPF_COEFF_10 0x07ff0000
++/* Reserved [15:11] */
++#define FLD_DIF_BPF_COEFF_11 0x000007ff
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF1213 (DIRECT_IF_REVB_BASE + 0x00000060)
++/*****************************************************************************/
++/* Reserved [31:27] */
++#define FLD_DIF_BPF_COEFF_12 0x07ff0000
++/* Reserved [15:12] */
++#define FLD_DIF_BPF_COEFF_13 0x00000fff
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF1415 (DIRECT_IF_REVB_BASE + 0x00000064)
++/*****************************************************************************/
++/* Reserved [31:28] */
++#define FLD_DIF_BPF_COEFF_14 0x0fff0000
++/* Reserved [15:12] */
++#define FLD_DIF_BPF_COEFF_15 0x00000fff
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF1617 (DIRECT_IF_REVB_BASE + 0x00000068)
++/*****************************************************************************/
++/* Reserved [31:29] */
++#define FLD_DIF_BPF_COEFF_16 0x1fff0000
++/* Reserved [15:13] */
++#define FLD_DIF_BPF_COEFF_17 0x00001fff
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF1819 (DIRECT_IF_REVB_BASE + 0x0000006c)
++/*****************************************************************************/
++/* Reserved [31:29] */
++#define FLD_DIF_BPF_COEFF_18 0x1fff0000
++/* Reserved [15:13] */
++#define FLD_DIF_BPF_COEFF_19 0x00001fff
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF2021 (DIRECT_IF_REVB_BASE + 0x00000070)
++/*****************************************************************************/
++/* Reserved [31:29] */
++#define FLD_DIF_BPF_COEFF_20 0x1fff0000
++/* Reserved [15:14] */
++#define FLD_DIF_BPF_COEFF_21 0x00003fff
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF2223 (DIRECT_IF_REVB_BASE + 0x00000074)
++/*****************************************************************************/
++/* Reserved [31:30] */
++#define FLD_DIF_BPF_COEFF_22 0x3fff0000
++/* Reserved [15:14] */
++#define FLD_DIF_BPF_COEFF_23 0x00003fff
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF2425 (DIRECT_IF_REVB_BASE + 0x00000078)
++/*****************************************************************************/
++/* Reserved [31:30] */
++#define FLD_DIF_BPF_COEFF_24 0x3fff0000
++/* Reserved [15:14] */
++#define FLD_DIF_BPF_COEFF_25 0x00003fff
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF2627 (DIRECT_IF_REVB_BASE + 0x0000007c)
++/*****************************************************************************/
++/* Reserved [31:30] */
++#define FLD_DIF_BPF_COEFF_26 0x3fff0000
++/* Reserved [15:14] */
++#define FLD_DIF_BPF_COEFF_27 0x00003fff
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF2829 (DIRECT_IF_REVB_BASE + 0x00000080)
++/*****************************************************************************/
++/* Reserved [31:30] */
++#define FLD_DIF_BPF_COEFF_28 0x3fff0000
++/* Reserved [15:14] */
++#define FLD_DIF_BPF_COEFF_29 0x00003fff
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF3031 (DIRECT_IF_REVB_BASE + 0x00000084)
++/*****************************************************************************/
++/* Reserved [31:30] */
++#define FLD_DIF_BPF_COEFF_30 0x3fff0000
++/* Reserved [15:14] */
++#define FLD_DIF_BPF_COEFF_31 0x00003fff
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF3233 (DIRECT_IF_REVB_BASE + 0x00000088)
++/*****************************************************************************/
++/* Reserved [31:30] */
++#define FLD_DIF_BPF_COEFF_32 0x3fff0000
++/* Reserved [15:14] */
++#define FLD_DIF_BPF_COEFF_33 0x00003fff
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF3435 (DIRECT_IF_REVB_BASE + 0x0000008c)
++/*****************************************************************************/
++/* Reserved [31:30] */
++#define FLD_DIF_BPF_COEFF_34 0x3fff0000
++/* Reserved [15:14] */
++#define FLD_DIF_BPF_COEFF_35 0x00003fff
++
++/*****************************************************************************/
++#define DIF_BPF_COEFF36 (DIRECT_IF_REVB_BASE + 0x00000090)
++/*****************************************************************************/
++/* Reserved [31:30] */
++#define FLD_DIF_BPF_COEFF_36 0x3fff0000
++/* Reserved [15:0] */
++
++/*****************************************************************************/
++#define DIF_RPT_VARIANCE (DIRECT_IF_REVB_BASE + 0x00000094)
++/*****************************************************************************/
++/* Reserved [31:20] */
++#define FLD_DIF_RPT_VARIANCE 0x000fffff
++
++/*****************************************************************************/
++#define DIF_SOFT_RST_CTRL_REVB (DIRECT_IF_REVB_BASE + 0x00000098)
++/*****************************************************************************/
++/* Reserved [31:8] */
++#define FLD_DIF_DIF_SOFT_RST 0x00000080
++#define FLD_DIF_DIF_REG_RST_MSK 0x00000040
++#define FLD_DIF_AGC_RST_MSK 0x00000020
++#define FLD_DIF_CMP_RST_MSK 0x00000010
++#define FLD_DIF_AVS_RST_MSK 0x00000008
++#define FLD_DIF_NYQ_RST_MSK 0x00000004
++#define FLD_DIF_DIF_SRC_RST_MSK 0x00000002
++#define FLD_DIF_PLL_RST_MSK 0x00000001
++
++/*****************************************************************************/
++#define DIF_PLL_FREQ_ERR (DIRECT_IF_REVB_BASE + 0x0000009c)
++/*****************************************************************************/
++/* Reserved [31:25] */
++#define FLD_DIF_CTL_IP 0x01ffffff
++
++#endif
+diff --git a/drivers/media/video/cx231xx/cx231xx-core.c b/drivers/media/video/cx231xx/cx231xx-core.c
+new file mode 100644
+index 0000000..0d333e6
+--- /dev/null
++++ b/drivers/media/video/cx231xx/cx231xx-core.c
+@@ -0,0 +1,1200 @@
++/*
++ cx231xx-core.c - driver for Conexant Cx23100/101/102
++ USB video capture devices
++
++ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
++ Based on em28xx driver
++
++ 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 <linux/init.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/usb.h>
++#include <linux/vmalloc.h>
++#include <media/v4l2-common.h>
++
++#include "cx231xx.h"
++#include "cx231xx-reg.h"
++
++/* #define ENABLE_DEBUG_ISOC_FRAMES */
++
++static unsigned int core_debug;
++module_param(core_debug, int, 0644);
++MODULE_PARM_DESC(core_debug, "enable debug messages [core]");
++
++#define cx231xx_coredbg(fmt, arg...) do {\
++ if (core_debug) \
++ printk(KERN_INFO "%s %s :"fmt, \
++ dev->name, __func__ , ##arg); } while (0)
++
++static unsigned int reg_debug;
++module_param(reg_debug, int, 0644);
++MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]");
++
++#define cx231xx_regdbg(fmt, arg...) do {\
++ if (reg_debug) \
++ printk(KERN_INFO "%s %s :"fmt, \
++ dev->name, __func__ , ##arg); } while (0)
++
++static int alt = CX231XX_PINOUT;
++module_param(alt, int, 0644);
++MODULE_PARM_DESC(alt, "alternate setting to use for video endpoint");
++
++#define cx231xx_isocdbg(fmt, arg...) do {\
++ if (core_debug) \
++ printk(KERN_INFO "%s %s :"fmt, \
++ dev->name, __func__ , ##arg); } while (0)
++
++/*****************************************************************
++* Device control list functions *
++******************************************************************/
++
++static LIST_HEAD(cx231xx_devlist);
++static DEFINE_MUTEX(cx231xx_devlist_mutex);
++
++struct cx231xx *cx231xx_get_device(int minor,
++ enum v4l2_buf_type *fh_type, int *has_radio)
++{
++ struct cx231xx *h, *dev = NULL;
++
++ *fh_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ *has_radio = 0;
++
++ mutex_lock(&cx231xx_devlist_mutex);
++ list_for_each_entry(h, &cx231xx_devlist, devlist) {
++ if (h->vdev->minor == minor)
++ dev = h;
++ if (h->vbi_dev->minor == minor) {
++ dev = h;
++ *fh_type = V4L2_BUF_TYPE_VBI_CAPTURE;
++ }
++ if (h->radio_dev && h->radio_dev->minor == minor) {
++ dev = h;
++ *has_radio = 1;
++ }
++ }
++ mutex_unlock(&cx231xx_devlist_mutex);
++
++ return dev;
++}
++
++/*
++ * cx231xx_realease_resources()
++ * unregisters the v4l2,i2c and usb devices
++ * called when the device gets disconected or at module unload
++*/
++void cx231xx_remove_from_devlist(struct cx231xx *dev)
++{
++ mutex_lock(&cx231xx_devlist_mutex);
++ list_del(&dev->devlist);
++ mutex_unlock(&cx231xx_devlist_mutex);
++};
++
++void cx231xx_add_into_devlist(struct cx231xx *dev)
++{
++ mutex_lock(&cx231xx_devlist_mutex);
++ list_add_tail(&dev->devlist, &cx231xx_devlist);
++ mutex_unlock(&cx231xx_devlist_mutex);
++};
++
++static LIST_HEAD(cx231xx_extension_devlist);
++static DEFINE_MUTEX(cx231xx_extension_devlist_lock);
++
++int cx231xx_register_extension(struct cx231xx_ops *ops)
++{
++ struct cx231xx *dev = NULL;
++
++ mutex_lock(&cx231xx_devlist_mutex);
++ mutex_lock(&cx231xx_extension_devlist_lock);
++ list_add_tail(&ops->next, &cx231xx_extension_devlist);
++ list_for_each_entry(dev, &cx231xx_devlist, devlist) {
++ if (dev)
++ ops->init(dev);
++ }
++ printk(KERN_INFO DRIVER_NAME ": %s initialized\n", ops->name);
++ mutex_unlock(&cx231xx_extension_devlist_lock);
++ mutex_unlock(&cx231xx_devlist_mutex);
++ return 0;
++}
++EXPORT_SYMBOL(cx231xx_register_extension);
++
++void cx231xx_unregister_extension(struct cx231xx_ops *ops)
++{
++ struct cx231xx *dev = NULL;
++
++ mutex_lock(&cx231xx_devlist_mutex);
++ list_for_each_entry(dev, &cx231xx_devlist, devlist) {
++ if (dev)
++ ops->fini(dev);
++ }
++
++ mutex_lock(&cx231xx_extension_devlist_lock);
++ printk(KERN_INFO DRIVER_NAME ": %s removed\n", ops->name);
++ list_del(&ops->next);
++ mutex_unlock(&cx231xx_extension_devlist_lock);
++ mutex_unlock(&cx231xx_devlist_mutex);
++}
++EXPORT_SYMBOL(cx231xx_unregister_extension);
++
++void cx231xx_init_extension(struct cx231xx *dev)
++{
++ struct cx231xx_ops *ops = NULL;
++
++ mutex_lock(&cx231xx_extension_devlist_lock);
++ if (!list_empty(&cx231xx_extension_devlist)) {
++ list_for_each_entry(ops, &cx231xx_extension_devlist, next) {
++ if (ops->init)
++ ops->init(dev);
++ }
++ }
++ mutex_unlock(&cx231xx_extension_devlist_lock);
++}
++
++void cx231xx_close_extension(struct cx231xx *dev)
++{
++ struct cx231xx_ops *ops = NULL;
++
++ mutex_lock(&cx231xx_extension_devlist_lock);
++ if (!list_empty(&cx231xx_extension_devlist)) {
++ list_for_each_entry(ops, &cx231xx_extension_devlist, next) {
++ if (ops->fini)
++ ops->fini(dev);
++ }
++ }
++ mutex_unlock(&cx231xx_extension_devlist_lock);
++}
++
++/****************************************************************
++* U S B related functions *
++*****************************************************************/
++int cx231xx_send_usb_command(struct cx231xx_i2c *i2c_bus,
++ struct cx231xx_i2c_xfer_data *req_data)
++{
++ int status = 0;
++ struct cx231xx *dev = i2c_bus->dev;
++ struct VENDOR_REQUEST_IN ven_req;
++
++ u8 saddr_len = 0;
++ u8 _i2c_period = 0;
++ u8 _i2c_nostop = 0;
++ u8 _i2c_reserve = 0;
++
++ /* Get the I2C period, nostop and reserve parameters */
++ _i2c_period = i2c_bus->i2c_period;
++ _i2c_nostop = i2c_bus->i2c_nostop;
++ _i2c_reserve = i2c_bus->i2c_reserve;
++
++ saddr_len = req_data->saddr_len;
++
++ /* Set wValue */
++ if (saddr_len == 1) /* need check saddr_len == 0 */
++ ven_req.wValue =
++ req_data->
++ dev_addr << 9 | _i2c_period << 4 | saddr_len << 2 |
++ _i2c_nostop << 1 | I2C_SYNC | _i2c_reserve << 6;
++ else
++ ven_req.wValue =
++ req_data->
++ dev_addr << 9 | _i2c_period << 4 | saddr_len << 2 |
++ _i2c_nostop << 1 | I2C_SYNC | _i2c_reserve << 6;
++
++ /* set channel number */
++ if (req_data->direction & I2C_M_RD) {
++ /* channel number, for read,spec required channel_num +4 */
++ ven_req.bRequest = i2c_bus->nr + 4;
++ } else
++ ven_req.bRequest = i2c_bus->nr; /* channel number, */
++
++ /* set index value */
++ switch (saddr_len) {
++ case 0:
++ ven_req.wIndex = 0; /* need check */
++ break;
++ case 1:
++ ven_req.wIndex = (req_data->saddr_dat & 0xff);
++ break;
++ case 2:
++ ven_req.wIndex = req_data->saddr_dat;
++ break;
++ }
++
++ /* set wLength value */
++ ven_req.wLength = req_data->buf_size;
++
++ /* set bData value */
++ ven_req.bData = 0;
++
++ /* set the direction */
++ if (req_data->direction) {
++ ven_req.direction = USB_DIR_IN;
++ memset(req_data->p_buffer, 0x00, ven_req.wLength);
++ } else
++ ven_req.direction = USB_DIR_OUT;
++
++ /* set the buffer for read / write */
++ ven_req.pBuff = req_data->p_buffer;
++
++
++ /* call common vendor command request */
++ status = cx231xx_send_vendor_cmd(dev, &ven_req);
++ if (status < 0) {
++ cx231xx_info
++ ("UsbInterface::sendCommand, failed with status -%d\n",
++ status);
++ }
++
++ return status;
++}
++EXPORT_SYMBOL_GPL(cx231xx_send_usb_command);
++
++/*
++ * cx231xx_read_ctrl_reg()
++ * reads data from the usb device specifying bRequest and wValue
++ */
++int cx231xx_read_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg,
++ char *buf, int len)
++{
++ u8 val = 0;
++ int ret;
++ int pipe = usb_rcvctrlpipe(dev->udev, 0);
++
++ if (dev->state & DEV_DISCONNECTED)
++ return -ENODEV;
++
++ if (len > URB_MAX_CTRL_SIZE)
++ return -EINVAL;
++
++ switch (len) {
++ case 1:
++ val = ENABLE_ONE_BYTE;
++ break;
++ case 2:
++ val = ENABLE_TWE_BYTE;
++ break;
++ case 3:
++ val = ENABLE_THREE_BYTE;
++ break;
++ case 4:
++ val = ENABLE_FOUR_BYTE;
++ break;
++ default:
++ val = 0xFF; /* invalid option */
++ }
++
++ if (val == 0xFF)
++ return -EINVAL;
++
++ if (reg_debug) {
++ cx231xx_isocdbg("(pipe 0x%08x): "
++ "IN: %02x %02x %02x %02x %02x %02x %02x %02x ",
++ pipe,
++ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ req, 0, val,
++ reg & 0xff, reg >> 8, len & 0xff, len >> 8);
++ }
++
++ mutex_lock(&dev->ctrl_urb_lock);
++ ret = usb_control_msg(dev->udev, pipe, req,
++ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ val, reg, dev->urb_buf, len, HZ);
++ if (ret < 0) {
++ cx231xx_isocdbg(" failed!\n");
++ /* mutex_unlock(&dev->ctrl_urb_lock); */
++ return ret;
++ }
++
++ if (len)
++ memcpy(buf, dev->urb_buf, len);
++
++ mutex_unlock(&dev->ctrl_urb_lock);
++
++ if (reg_debug) {
++ int byte;
++
++ cx231xx_isocdbg("<<<");
++ for (byte = 0; byte < len; byte++)
++ cx231xx_isocdbg(" %02x", (unsigned char)buf[byte]);
++ cx231xx_isocdbg("\n");
++ }
++
++ return ret;
++}
++
++int cx231xx_send_vendor_cmd(struct cx231xx *dev,
++ struct VENDOR_REQUEST_IN *ven_req)
++{
++ int ret;
++ int pipe = 0;
++
++ if (dev->state & DEV_DISCONNECTED)
++ return -ENODEV;
++
++ if ((ven_req->wLength > URB_MAX_CTRL_SIZE))
++ return -EINVAL;
++
++ if (ven_req->direction)
++ pipe = usb_rcvctrlpipe(dev->udev, 0);
++ else
++ pipe = usb_sndctrlpipe(dev->udev, 0);
++
++ if (reg_debug) {
++ int byte;
++
++ cx231xx_isocdbg("(pipe 0x%08x): "
++ "OUT: %02x %02x %02x %04x %04x %04x >>>",
++ pipe,
++ ven_req->
++ direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ ven_req->bRequest, 0, ven_req->wValue,
++ ven_req->wIndex, ven_req->wLength);
++
++ for (byte = 0; byte < ven_req->wLength; byte++)
++ cx231xx_isocdbg(" %02x",
++ (unsigned char)ven_req->pBuff[byte]);
++ cx231xx_isocdbg("\n");
++ }
++
++ mutex_lock(&dev->ctrl_urb_lock);
++ ret = usb_control_msg(dev->udev, pipe, ven_req->bRequest,
++ ven_req->
++ direction | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ ven_req->wValue, ven_req->wIndex, ven_req->pBuff,
++ ven_req->wLength, HZ);
++ mutex_unlock(&dev->ctrl_urb_lock);
++
++ return ret;
++}
++
++/*
++ * cx231xx_write_ctrl_reg()
++ * sends data to the usb device, specifying bRequest
++ */
++int cx231xx_write_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg, char *buf,
++ int len)
++{
++ u8 val = 0;
++ int ret;
++ int pipe = usb_sndctrlpipe(dev->udev, 0);
++
++ if (dev->state & DEV_DISCONNECTED)
++ return -ENODEV;
++
++ if ((len < 1) || (len > URB_MAX_CTRL_SIZE))
++ return -EINVAL;
++
++ switch (len) {
++ case 1:
++ val = ENABLE_ONE_BYTE;
++ break;
++ case 2:
++ val = ENABLE_TWE_BYTE;
++ break;
++ case 3:
++ val = ENABLE_THREE_BYTE;
++ break;
++ case 4:
++ val = ENABLE_FOUR_BYTE;
++ break;
++ default:
++ val = 0xFF; /* invalid option */
++ }
++
++ if (val == 0xFF)
++ return -EINVAL;
++
++ if (reg_debug) {
++ int byte;
++
++ cx231xx_isocdbg("(pipe 0x%08x): "
++ "OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>",
++ pipe,
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ req, 0, val, reg & 0xff,
++ reg >> 8, len & 0xff, len >> 8);
++
++ for (byte = 0; byte < len; byte++)
++ cx231xx_isocdbg(" %02x", (unsigned char)buf[byte]);
++ cx231xx_isocdbg("\n");
++ }
++
++ mutex_lock(&dev->ctrl_urb_lock);
++ memcpy(dev->urb_buf, buf, len);
++ ret = usb_control_msg(dev->udev, pipe, req,
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ val, reg, dev->urb_buf, len, HZ);
++ mutex_unlock(&dev->ctrl_urb_lock);
++
++ return ret;
++}
++
++/****************************************************************
++* USB Alternate Setting functions *
++*****************************************************************/
++
++int cx231xx_set_video_alternate(struct cx231xx *dev)
++{
++ int errCode, prev_alt = dev->video_mode.alt;
++ unsigned int min_pkt_size = dev->width * 2 + 4;
++ u32 usb_interface_index = 0;
++
++ /* When image size is bigger than a certain value,
++ the frame size should be increased, otherwise, only
++ green screen will be received.
++ */
++ if (dev->width * 2 * dev->height > 720 * 240 * 2)
++ min_pkt_size *= 2;
++
++ if (dev->width > 360) {
++ /* resolutions: 720,704,640 */
++ dev->video_mode.alt = 3;
++ } else if (dev->width > 180) {
++ /* resolutions: 360,352,320,240 */
++ dev->video_mode.alt = 2;
++ } else if (dev->width > 0) {
++ /* resolutions: 180,176,160,128,88 */
++ dev->video_mode.alt = 1;
++ } else {
++ /* Change to alt0 BULK to release USB bandwidth */
++ dev->video_mode.alt = 0;
++ }
++
++ /* Get the correct video interface Index */
++ usb_interface_index =
++ dev->current_pcb_config.hs_config_info[0].interface_info.
++ video_index + 1;
++
++ if (dev->video_mode.alt != prev_alt) {
++ cx231xx_coredbg("minimum isoc packet size: %u (alt=%d)\n",
++ min_pkt_size, dev->video_mode.alt);
++ dev->video_mode.max_pkt_size =
++ dev->video_mode.alt_max_pkt_size[dev->video_mode.alt];
++ cx231xx_coredbg("setting alternate %d with wMaxPacketSize=%u\n",
++ dev->video_mode.alt,
++ dev->video_mode.max_pkt_size);
++ cx231xx_info
++ (" setting alt %d with wMaxPktSize=%u , Interface = %d\n",
++ dev->video_mode.alt, dev->video_mode.max_pkt_size,
++ usb_interface_index);
++ errCode =
++ usb_set_interface(dev->udev, usb_interface_index,
++ dev->video_mode.alt);
++ if (errCode < 0) {
++ cx231xx_errdev
++ ("cannot change alt number to %d (error=%i)\n",
++ dev->video_mode.alt, errCode);
++ return errCode;
++ }
++ }
++ return 0;
++}
++
++int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt)
++{
++ int status = 0;
++ u32 usb_interface_index = 0;
++ u32 max_pkt_size = 0;
++
++ switch (index) {
++ case INDEX_TS1:
++ usb_interface_index =
++ dev->current_pcb_config.hs_config_info[0].interface_info.
++ ts1_index + 1;
++ dev->video_mode.alt = alt;
++ if (dev->ts1_mode.alt_max_pkt_size != NULL)
++ max_pkt_size = dev->ts1_mode.max_pkt_size =
++ dev->ts1_mode.alt_max_pkt_size[dev->ts1_mode.alt];
++ break;
++ case INDEX_TS2:
++ usb_interface_index =
++ dev->current_pcb_config.hs_config_info[0].interface_info.
++ ts2_index + 1;
++ break;
++ case INDEX_AUDIO:
++ usb_interface_index =
++ dev->current_pcb_config.hs_config_info[0].interface_info.
++ audio_index + 1;
++ dev->adev.alt = alt;
++ if (dev->adev.alt_max_pkt_size != NULL)
++ max_pkt_size = dev->adev.max_pkt_size =
++ dev->adev.alt_max_pkt_size[dev->adev.alt];
++ break;
++ case INDEX_VIDEO:
++ usb_interface_index =
++ dev->current_pcb_config.hs_config_info[0].interface_info.
++ video_index + 1;
++ dev->video_mode.alt = alt;
++ if (dev->video_mode.alt_max_pkt_size != NULL)
++ max_pkt_size = dev->video_mode.max_pkt_size =
++ dev->video_mode.alt_max_pkt_size[dev->video_mode.
++ alt];
++ break;
++ case INDEX_VANC:
++ usb_interface_index =
++ dev->current_pcb_config.hs_config_info[0].interface_info.
++ vanc_index + 1;
++ dev->vbi_mode.alt = alt;
++ if (dev->vbi_mode.alt_max_pkt_size != NULL)
++ max_pkt_size = dev->vbi_mode.max_pkt_size =
++ dev->vbi_mode.alt_max_pkt_size[dev->vbi_mode.alt];
++ break;
++ case INDEX_HANC:
++ usb_interface_index =
++ dev->current_pcb_config.hs_config_info[0].interface_info.
++ hanc_index + 1;
++ dev->sliced_cc_mode.alt = alt;
++ if (dev->sliced_cc_mode.alt_max_pkt_size != NULL)
++ max_pkt_size = dev->sliced_cc_mode.max_pkt_size =
++ dev->sliced_cc_mode.alt_max_pkt_size[dev->
++ sliced_cc_mode.
++ alt];
++ break;
++ default:
++ break;
++ }
++
++ if (alt > 0 && max_pkt_size == 0) {
++ cx231xx_errdev
++ ("can't change interface %d alt no. to %d: Max. Pkt size = 0\n",
++ usb_interface_index, alt);
++ return -1;
++ }
++
++ cx231xx_info
++ (" setting alternate %d with wMaxPacketSize=%u , Interface = %d\n",
++ alt, max_pkt_size, usb_interface_index);
++
++ if (usb_interface_index > 0) {
++ status = usb_set_interface(dev->udev, usb_interface_index, alt);
++ if (status < 0) {
++ cx231xx_errdev
++ ("can't change interface %d alt no. to %d (err=%i)\n",
++ usb_interface_index, alt, status);
++ return status;
++ }
++ }
++
++ return status;
++}
++EXPORT_SYMBOL_GPL(cx231xx_set_alt_setting);
++
++int cx231xx_gpio_set(struct cx231xx *dev, struct cx231xx_reg_seq *gpio)
++{
++ int rc = 0;
++
++ if (!gpio)
++ return rc;
++
++ /* Send GPIO reset sequences specified at board entry */
++ while (gpio->sleep >= 0) {
++ rc = cx231xx_set_gpio_value(dev, gpio->bit, gpio->val);
++ if (rc < 0)
++ return rc;
++
++ if (gpio->sleep > 0)
++ msleep(gpio->sleep);
++
++ gpio++;
++ }
++ return rc;
++}
++
++int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode)
++{
++ if (dev->mode == set_mode)
++ return 0;
++
++ if (set_mode == CX231XX_SUSPEND) {
++ /* Set the chip in power saving mode */
++ dev->mode = set_mode;
++ }
++
++ /* Resource is locked */
++ if (dev->mode != CX231XX_SUSPEND)
++ return -EINVAL;
++
++ dev->mode = set_mode;
++
++ if (dev->mode == CX231XX_DIGITAL_MODE)
++ ;/* Set Digital power mode */
++ else
++ ;/* Set Analog Power mode */
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(cx231xx_set_mode);
++
++/*****************************************************************
++* URB Streaming functions *
++******************************************************************/
++
++/*
++ * IRQ callback, called by URB callback
++ */
++static void cx231xx_irq_callback(struct urb *urb)
++{
++ struct cx231xx_dmaqueue *dma_q = urb->context;
++ struct cx231xx_video_mode *vmode =
++ container_of(dma_q, struct cx231xx_video_mode, vidq);
++ struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode);
++ int rc, i;
++
++ switch (urb->status) {
++ case 0: /* success */
++ case -ETIMEDOUT: /* NAK */
++ break;
++ case -ECONNRESET: /* kill */
++ case -ENOENT:
++ case -ESHUTDOWN:
++ return;
++ default: /* error */
++ cx231xx_isocdbg("urb completition error %d.\n", urb->status);
++ break;
++ }
++
++ /* Copy data from URB */
++ spin_lock(&dev->video_mode.slock);
++ rc = dev->video_mode.isoc_ctl.isoc_copy(dev, urb);
++ spin_unlock(&dev->video_mode.slock);
++
++ /* Reset urb buffers */
++ for (i = 0; i < urb->number_of_packets; i++) {
++ urb->iso_frame_desc[i].status = 0;
++ urb->iso_frame_desc[i].actual_length = 0;
++ }
++ urb->status = 0;
++
++ urb->status = usb_submit_urb(urb, GFP_ATOMIC);
++ if (urb->status) {
++ cx231xx_isocdbg("urb resubmit failed (error=%i)\n",
++ urb->status);
++ }
++}
++
++/*
++ * Stop and Deallocate URBs
++ */
++void cx231xx_uninit_isoc(struct cx231xx *dev)
++{
++ struct urb *urb;
++ int i;
++
++ cx231xx_isocdbg("cx231xx: called cx231xx_uninit_isoc\n");
++
++ dev->video_mode.isoc_ctl.nfields = -1;
++ for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) {
++ urb = dev->video_mode.isoc_ctl.urb[i];
++ if (urb) {
++ if (!irqs_disabled())
++ usb_kill_urb(urb);
++ else
++ usb_unlink_urb(urb);
++
++ if (dev->video_mode.isoc_ctl.transfer_buffer[i]) {
++ usb_buffer_free(dev->udev,
++ urb->transfer_buffer_length,
++ dev->video_mode.isoc_ctl.
++ transfer_buffer[i],
++ urb->transfer_dma);
++ }
++ usb_free_urb(urb);
++ dev->video_mode.isoc_ctl.urb[i] = NULL;
++ }
++ dev->video_mode.isoc_ctl.transfer_buffer[i] = NULL;
++ }
++
++ kfree(dev->video_mode.isoc_ctl.urb);
++ kfree(dev->video_mode.isoc_ctl.transfer_buffer);
++
++ dev->video_mode.isoc_ctl.urb = NULL;
++ dev->video_mode.isoc_ctl.transfer_buffer = NULL;
++ dev->video_mode.isoc_ctl.num_bufs = 0;
++
++ cx231xx_capture_start(dev, 0, Raw_Video);
++}
++EXPORT_SYMBOL_GPL(cx231xx_uninit_isoc);
++
++/*
++ * Allocate URBs and start IRQ
++ */
++int cx231xx_init_isoc(struct cx231xx *dev, int max_packets,
++ int num_bufs, int max_pkt_size,
++ int (*isoc_copy) (struct cx231xx *dev, struct urb *urb))
++{
++ struct cx231xx_dmaqueue *dma_q = &dev->video_mode.vidq;
++ int i;
++ int sb_size, pipe;
++ struct urb *urb;
++ int j, k;
++ int rc;
++
++ cx231xx_isocdbg("cx231xx: called cx231xx_prepare_isoc\n");
++
++ dev->video_input = dev->video_input > 2 ? 2 : dev->video_input;
++
++ cx231xx_info("Setting Video mux to %d\n", dev->video_input);
++ video_mux(dev, dev->video_input);
++
++ /* De-allocates all pending stuff */
++ cx231xx_uninit_isoc(dev);
++
++ dev->video_mode.isoc_ctl.isoc_copy = isoc_copy;
++ dev->video_mode.isoc_ctl.num_bufs = num_bufs;
++ dma_q->pos = 0;
++ dma_q->is_partial_line = 0;
++ dma_q->last_sav = 0;
++ dma_q->current_field = -1;
++ dma_q->field1_done = 0;
++ dma_q->lines_per_field = dev->height / 2;
++ dma_q->bytes_left_in_line = dev->width << 1;
++ dma_q->lines_completed = 0;
++ for (i = 0; i < 8; i++)
++ dma_q->partial_buf[i] = 0;
++
++ dev->video_mode.isoc_ctl.urb =
++ kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
++ if (!dev->video_mode.isoc_ctl.urb) {
++ cx231xx_errdev("cannot alloc memory for usb buffers\n");
++ return -ENOMEM;
++ }
++
++ dev->video_mode.isoc_ctl.transfer_buffer =
++ kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
++ if (!dev->video_mode.isoc_ctl.transfer_buffer) {
++ cx231xx_errdev("cannot allocate memory for usbtransfer\n");
++ kfree(dev->video_mode.isoc_ctl.urb);
++ return -ENOMEM;
++ }
++
++ dev->video_mode.isoc_ctl.max_pkt_size = max_pkt_size;
++ dev->video_mode.isoc_ctl.buf = NULL;
++
++ sb_size = max_packets * dev->video_mode.isoc_ctl.max_pkt_size;
++
++ /* allocate urbs and transfer buffers */
++ for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) {
++ urb = usb_alloc_urb(max_packets, GFP_KERNEL);
++ if (!urb) {
++ cx231xx_err("cannot alloc isoc_ctl.urb %i\n", i);
++ cx231xx_uninit_isoc(dev);
++ return -ENOMEM;
++ }
++ dev->video_mode.isoc_ctl.urb[i] = urb;
++
++ dev->video_mode.isoc_ctl.transfer_buffer[i] =
++ usb_buffer_alloc(dev->udev, sb_size, GFP_KERNEL,
++ &urb->transfer_dma);
++ if (!dev->video_mode.isoc_ctl.transfer_buffer[i]) {
++ cx231xx_err("unable to allocate %i bytes for transfer"
++ " buffer %i%s\n",
++ sb_size, i,
++ in_interrupt() ? " while in int" : "");
++ cx231xx_uninit_isoc(dev);
++ return -ENOMEM;
++ }
++ memset(dev->video_mode.isoc_ctl.transfer_buffer[i], 0, sb_size);
++
++ pipe =
++ usb_rcvisocpipe(dev->udev, dev->video_mode.end_point_addr);
++
++ usb_fill_int_urb(urb, dev->udev, pipe,
++ dev->video_mode.isoc_ctl.transfer_buffer[i],
++ sb_size, cx231xx_irq_callback, dma_q, 1);
++
++ urb->number_of_packets = max_packets;
++ urb->transfer_flags = URB_ISO_ASAP;
++
++ k = 0;
++ for (j = 0; j < max_packets; j++) {
++ urb->iso_frame_desc[j].offset = k;
++ urb->iso_frame_desc[j].length =
++ dev->video_mode.isoc_ctl.max_pkt_size;
++ k += dev->video_mode.isoc_ctl.max_pkt_size;
++ }
++ }
++
++ init_waitqueue_head(&dma_q->wq);
++
++ /* submit urbs and enables IRQ */
++ for (i = 0; i < dev->video_mode.isoc_ctl.num_bufs; i++) {
++ rc = usb_submit_urb(dev->video_mode.isoc_ctl.urb[i],
++ GFP_ATOMIC);
++ if (rc) {
++ cx231xx_err("submit of urb %i failed (error=%i)\n", i,
++ rc);
++ cx231xx_uninit_isoc(dev);
++ return rc;
++ }
++ }
++
++ cx231xx_capture_start(dev, 1, Raw_Video);
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(cx231xx_init_isoc);
++
++/*****************************************************************
++* Device Init/UnInit functions *
++******************************************************************/
++int cx231xx_dev_init(struct cx231xx *dev)
++{
++ int errCode = 0;
++
++ /* Initialize I2C bus */
++
++ /* External Master 1 Bus */
++ dev->i2c_bus[0].nr = 0;
++ dev->i2c_bus[0].dev = dev;
++ dev->i2c_bus[0].i2c_period = I2C_SPEED_1M; /* 1MHz */
++ dev->i2c_bus[0].i2c_nostop = 0;
++ dev->i2c_bus[0].i2c_reserve = 0;
++
++ /* External Master 2 Bus */
++ dev->i2c_bus[1].nr = 1;
++ dev->i2c_bus[1].dev = dev;
++ dev->i2c_bus[1].i2c_period = I2C_SPEED_1M; /* 1MHz */
++ dev->i2c_bus[1].i2c_nostop = 0;
++ dev->i2c_bus[1].i2c_reserve = 0;
++
++ /* Internal Master 3 Bus */
++ dev->i2c_bus[2].nr = 2;
++ dev->i2c_bus[2].dev = dev;
++ dev->i2c_bus[2].i2c_period = I2C_SPEED_400K; /* 400kHz */
++ dev->i2c_bus[2].i2c_nostop = 0;
++ dev->i2c_bus[2].i2c_reserve = 0;
++
++ /* register I2C buses */
++ cx231xx_i2c_register(&dev->i2c_bus[0]);
++ cx231xx_i2c_register(&dev->i2c_bus[1]);
++ cx231xx_i2c_register(&dev->i2c_bus[2]);
++
++ /* init hardware */
++ /* Note : with out calling set power mode function,
++ afe can not be set up correctly */
++ errCode = cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV);
++ if (errCode < 0) {
++ cx231xx_errdev
++ ("%s: Failed to set Power - errCode [%d]!\n",
++ __func__, errCode);
++ return errCode;
++ }
++
++ /* initialize Colibri block */
++ errCode = cx231xx_afe_init_super_block(dev, 0x23c);
++ if (errCode < 0) {
++ cx231xx_errdev
++ ("%s: cx231xx_afe init super block - errCode [%d]!\n",
++ __func__, errCode);
++ return errCode;
++ }
++ errCode = cx231xx_afe_init_channels(dev);
++ if (errCode < 0) {
++ cx231xx_errdev
++ ("%s: cx231xx_afe init channels - errCode [%d]!\n",
++ __func__, errCode);
++ return errCode;
++ }
++
++ /* Set DIF in By pass mode */
++ errCode = cx231xx_dif_set_standard(dev, DIF_USE_BASEBAND);
++ if (errCode < 0) {
++ cx231xx_errdev
++ ("%s: cx231xx_dif set to By pass mode - errCode [%d]!\n",
++ __func__, errCode);
++ return errCode;
++ }
++
++ /* I2S block related functions */
++ errCode = cx231xx_i2s_blk_initialize(dev);
++ if (errCode < 0) {
++ cx231xx_errdev
++ ("%s: cx231xx_i2s block initialize - errCode [%d]!\n",
++ __func__, errCode);
++ return errCode;
++ }
++
++ /* init control pins */
++ errCode = cx231xx_init_ctrl_pin_status(dev);
++ if (errCode < 0) {
++ cx231xx_errdev("%s: cx231xx_init ctrl pins - errCode [%d]!\n",
++ __func__, errCode);
++ return errCode;
++ }
++
++ /* set AGC mode to Analog */
++ errCode = cx231xx_set_agc_analog_digital_mux_select(dev, 1);
++ if (errCode < 0) {
++ cx231xx_errdev
++ ("%s: cx231xx_AGC mode to Analog - errCode [%d]!\n",
++ __func__, errCode);
++ return errCode;
++ }
++
++ /* set all alternate settings to zero initially */
++ cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0);
++ cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
++ cx231xx_set_alt_setting(dev, INDEX_HANC, 0);
++ if (dev->board.has_dvb)
++ cx231xx_set_alt_setting(dev, INDEX_TS1, 0);
++
++ /* set the I2C master port to 3 on channel 1 */
++ errCode = cx231xx_enable_i2c_for_tuner(dev, I2C_3);
++
++ return errCode;
++}
++EXPORT_SYMBOL_GPL(cx231xx_dev_init);
++
++void cx231xx_dev_uninit(struct cx231xx *dev)
++{
++ /* Un Initialize I2C bus */
++ cx231xx_i2c_unregister(&dev->i2c_bus[2]);
++ cx231xx_i2c_unregister(&dev->i2c_bus[1]);
++ cx231xx_i2c_unregister(&dev->i2c_bus[0]);
++}
++EXPORT_SYMBOL_GPL(cx231xx_dev_uninit);
++
++/*****************************************************************
++* G P I O related functions *
++******************************************************************/
++int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 * gpio_val,
++ u8 len, u8 request, u8 direction)
++{
++ int status = 0;
++ struct VENDOR_REQUEST_IN ven_req;
++
++ /* Set wValue */
++ ven_req.wValue = (u16) (gpio_bit >> 16 & 0xffff);
++
++ /* set request */
++ if (!request) {
++ if (direction)
++ ven_req.bRequest = VRT_GET_GPIO; /* 0x8 gpio */
++ else
++ ven_req.bRequest = VRT_SET_GPIO; /* 0x9 gpio */
++ } else {
++ if (direction)
++ ven_req.bRequest = VRT_GET_GPIE; /* 0xa gpie */
++ else
++ ven_req.bRequest = VRT_SET_GPIE; /* 0xb gpie */
++ }
++
++ /* set index value */
++ ven_req.wIndex = (u16) (gpio_bit & 0xffff);
++
++ /* set wLength value */
++ ven_req.wLength = len;
++
++ /* set bData value */
++ ven_req.bData = 0;
++
++ /* set the buffer for read / write */
++ ven_req.pBuff = gpio_val;
++
++ /* set the direction */
++ if (direction) {
++ ven_req.direction = USB_DIR_IN;
++ memset(ven_req.pBuff, 0x00, ven_req.wLength);
++ } else
++ ven_req.direction = USB_DIR_OUT;
++
++
++ /* call common vendor command request */
++ status = cx231xx_send_vendor_cmd(dev, &ven_req);
++ if (status < 0) {
++ cx231xx_info
++ ("UsbInterface::sendCommand, failed with status -%d\n",
++ status);
++ }
++
++ return status;
++}
++EXPORT_SYMBOL_GPL(cx231xx_send_gpio_cmd);
++
++/*****************************************************************
++ * C O N T R O L - Register R E A D / W R I T E functions *
++ *****************************************************************/
++int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode)
++{
++ u8 value[4] = { 0x0, 0x0, 0x0, 0x0 };
++ u32 tmp = 0;
++ int status = 0;
++
++ status =
++ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, address, value, 4);
++ if (status < 0)
++ return status;
++
++ tmp = *((u32 *) value);
++ tmp |= mode;
++
++ value[0] = (u8) tmp;
++ value[1] = (u8) (tmp >> 8);
++ value[2] = (u8) (tmp >> 16);
++ value[3] = (u8) (tmp >> 24);
++
++ status =
++ cx231xx_write_ctrl_reg(dev, VRT_SET_REGISTER, address, value, 4);
++
++ return status;
++}
++
++/*****************************************************************
++ * I 2 C Internal C O N T R O L functions *
++ *****************************************************************/
++int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr, u16 saddr,
++ u8 saddr_len, u32 *data, u8 data_len)
++{
++ int status = 0;
++ struct cx231xx_i2c_xfer_data req_data;
++ u8 value[4] = { 0, 0, 0, 0 };
++
++ if (saddr_len == 0)
++ saddr = 0;
++ else if (saddr_len == 0)
++ saddr &= 0xff;
++
++ /* prepare xfer_data struct */
++ req_data.dev_addr = dev_addr >> 1;
++ req_data.direction = I2C_M_RD;
++ req_data.saddr_len = saddr_len;
++ req_data.saddr_dat = saddr;
++ req_data.buf_size = data_len;
++ req_data.p_buffer = (u8 *) value;
++
++ /* usb send command */
++ status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], &req_data);
++
++ if (status >= 0) {
++ /* Copy the data read back to main buffer */
++ if (data_len == 1)
++ *data = value[0];
++ else
++ *data =
++ value[0] | value[1] << 8 | value[2] << 16 | value[3]
++ << 24;
++ }
++
++ return status;
++}
++
++int cx231xx_write_i2c_data(struct cx231xx *dev, u8 dev_addr, u16 saddr,
++ u8 saddr_len, u32 data, u8 data_len)
++{
++ int status = 0;
++ u8 value[4] = { 0, 0, 0, 0 };
++ struct cx231xx_i2c_xfer_data req_data;
++
++ value[0] = (u8) data;
++ value[1] = (u8) (data >> 8);
++ value[2] = (u8) (data >> 16);
++ value[3] = (u8) (data >> 24);
++
++ if (saddr_len == 0)
++ saddr = 0;
++ else if (saddr_len == 0)
++ saddr &= 0xff;
++
++ /* prepare xfer_data struct */
++ req_data.dev_addr = dev_addr >> 1;
++ req_data.direction = 0;
++ req_data.saddr_len = saddr_len;
++ req_data.saddr_dat = saddr;
++ req_data.buf_size = data_len;
++ req_data.p_buffer = value;
++
++ /* usb send command */
++ status = dev->cx231xx_send_usb_command(&dev->i2c_bus[0], &req_data);
++
++ return status;
++}
++
++int cx231xx_reg_mask_write(struct cx231xx *dev, u8 dev_addr, u8 size,
++ u16 register_address, u8 bit_start, u8 bit_end,
++ u32 value)
++{
++ int status = 0;
++ u32 tmp;
++ u32 mask = 0;
++ int i;
++
++ if (bit_start > (size - 1) || bit_end > (size - 1))
++ return -1;
++
++ if (size == 8) {
++ status =
++ cx231xx_read_i2c_data(dev, dev_addr, register_address, 2,
++ &tmp, 1);
++ } else {
++ status =
++ cx231xx_read_i2c_data(dev, dev_addr, register_address, 2,
++ &tmp, 4);
++ }
++
++ if (status < 0)
++ return status;
++
++ mask = 1 << bit_end;
++ for (i = bit_end; i > bit_start && i > 0; i--)
++ mask = mask + (1 << (i - 1));
++
++ value <<= bit_start;
++
++ if (size == 8) {
++ tmp &= ~mask;
++ tmp |= value;
++ tmp &= 0xff;
++ status =
++ cx231xx_write_i2c_data(dev, dev_addr, register_address, 2,
++ tmp, 1);
++ } else {
++ tmp &= ~mask;
++ tmp |= value;
++ status =
++ cx231xx_write_i2c_data(dev, dev_addr, register_address, 2,
++ tmp, 4);
++ }
++
++ return status;
++}
++
++int cx231xx_read_modify_write_i2c_dword(struct cx231xx *dev, u8 dev_addr,
++ u16 saddr, u32 mask, u32 value)
++{
++ u32 temp;
++ int status = 0;
++
++ status = cx231xx_read_i2c_data(dev, dev_addr, saddr, 2, &temp, 4);
++
++ if (status < 0)
++ return status;
++
++ temp &= ~mask;
++ temp |= value;
++
++ status = cx231xx_write_i2c_data(dev, dev_addr, saddr, 2, temp, 4);
++
++ return status;
++}
++
++u32 cx231xx_set_field(u32 field_mask, u32 data)
++{
++ u32 temp;
++
++ for (temp = field_mask; (temp & 1) == 0; temp >>= 1)
++ data <<= 1;
++
++ return data;
++}
+diff --git a/drivers/media/video/cx231xx/cx231xx-dvb.c b/drivers/media/video/cx231xx/cx231xx-dvb.c
+new file mode 100644
+index 0000000..c5082a4
+--- /dev/null
++++ b/drivers/media/video/cx231xx/cx231xx-dvb.c
+@@ -0,0 +1,559 @@
++/*
++ DVB device driver for cx231xx
++
++ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
++ Based on em28xx driver
++
++ 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 <linux/kernel.h>
++#include <linux/usb.h>
++
++#include "cx231xx.h"
++#include <media/v4l2-common.h>
++#include <media/videobuf-vmalloc.h>
++
++#include "xc5000.h"
++#include "dvb_dummy_fe.h"
++
++MODULE_DESCRIPTION("driver for cx231xx based DVB cards");
++MODULE_AUTHOR("Srinivasa Deevi <srinivasa.deevi@conexant.com>");
++MODULE_LICENSE("GPL");
++
++static unsigned int debug;
++module_param(debug, int, 0644);
++MODULE_PARM_DESC(debug, "enable debug messages [dvb]");
++
++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
++
++#define dprintk(level, fmt, arg...) do { \
++if (debug >= level) \
++ printk(KERN_DEBUG "%s/2-dvb: " fmt, dev->name, ## arg); \
++} while (0)
++
++#define CX231XX_DVB_NUM_BUFS 5
++#define CX231XX_DVB_MAX_PACKETSIZE 564
++#define CX231XX_DVB_MAX_PACKETS 64
++
++struct cx231xx_dvb {
++ struct dvb_frontend *frontend;
++
++ /* feed count management */
++ struct mutex lock;
++ int nfeeds;
++
++ /* general boilerplate stuff */
++ struct dvb_adapter adapter;
++ struct dvb_demux demux;
++ struct dmxdev dmxdev;
++ struct dmx_frontend fe_hw;
++ struct dmx_frontend fe_mem;
++ struct dvb_net net;
++};
++
++static inline void print_err_status(struct cx231xx *dev, int packet, int status)
++{
++ char *errmsg = "Unknown";
++
++ switch (status) {
++ case -ENOENT:
++ errmsg = "unlinked synchronuously";
++ break;
++ case -ECONNRESET:
++ errmsg = "unlinked asynchronuously";
++ break;
++ case -ENOSR:
++ errmsg = "Buffer error (overrun)";
++ break;
++ case -EPIPE:
++ errmsg = "Stalled (device not responding)";
++ break;
++ case -EOVERFLOW:
++ errmsg = "Babble (bad cable?)";
++ break;
++ case -EPROTO:
++ errmsg = "Bit-stuff error (bad cable?)";
++ break;
++ case -EILSEQ:
++ errmsg = "CRC/Timeout (could be anything)";
++ break;
++ case -ETIME:
++ errmsg = "Device does not respond";
++ break;
++ }
++ if (packet < 0) {
++ dprintk(1, "URB status %d [%s].\n", status, errmsg);
++ } else {
++ dprintk(1, "URB packet %d, status %d [%s].\n",
++ packet, status, errmsg);
++ }
++}
++
++static inline int dvb_isoc_copy(struct cx231xx *dev, struct urb *urb)
++{
++ int i;
++
++ if (!dev)
++ return 0;
++
++ if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
++ return 0;
++
++ if (urb->status < 0) {
++ print_err_status(dev, -1, urb->status);
++ if (urb->status == -ENOENT)
++ return 0;
++ }
++
++ for (i = 0; i < urb->number_of_packets; i++) {
++ int status = urb->iso_frame_desc[i].status;
++
++ if (status < 0) {
++ print_err_status(dev, i, status);
++ if (urb->iso_frame_desc[i].status != -EPROTO)
++ continue;
++ }
++
++ dvb_dmx_swfilter(&dev->dvb->demux, urb->transfer_buffer +
++ urb->iso_frame_desc[i].offset,
++ urb->iso_frame_desc[i].actual_length);
++ }
++
++ return 0;
++}
++
++static int start_streaming(struct cx231xx_dvb *dvb)
++{
++ int rc;
++ struct cx231xx *dev = dvb->adapter.priv;
++
++ usb_set_interface(dev->udev, 0, 1);
++ rc = cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
++ if (rc < 0)
++ return rc;
++
++ return cx231xx_init_isoc(dev, CX231XX_DVB_MAX_PACKETS,
++ CX231XX_DVB_NUM_BUFS,
++ CX231XX_DVB_MAX_PACKETSIZE, dvb_isoc_copy);
++}
++
++static int stop_streaming(struct cx231xx_dvb *dvb)
++{
++ struct cx231xx *dev = dvb->adapter.priv;
++
++ cx231xx_uninit_isoc(dev);
++
++ cx231xx_set_mode(dev, CX231XX_SUSPEND);
++
++ return 0;
++}
++
++static int start_feed(struct dvb_demux_feed *feed)
++{
++ struct dvb_demux *demux = feed->demux;
++ struct cx231xx_dvb *dvb = demux->priv;
++ int rc, ret;
++
++ if (!demux->dmx.frontend)
++ return -EINVAL;
++
++ mutex_lock(&dvb->lock);
++ dvb->nfeeds++;
++ rc = dvb->nfeeds;
++
++ if (dvb->nfeeds == 1) {
++ ret = start_streaming(dvb);
++ if (ret < 0)
++ rc = ret;
++ }
++
++ mutex_unlock(&dvb->lock);
++ return rc;
++}
++
++static int stop_feed(struct dvb_demux_feed *feed)
++{
++ struct dvb_demux *demux = feed->demux;
++ struct cx231xx_dvb *dvb = demux->priv;
++ int err = 0;
++
++ mutex_lock(&dvb->lock);
++ dvb->nfeeds--;
++
++ if (0 == dvb->nfeeds)
++ err = stop_streaming(dvb);
++
++ mutex_unlock(&dvb->lock);
++ return err;
++}
++
++/* ------------------------------------------------------------------ */
++static int cx231xx_dvb_bus_ctrl(struct dvb_frontend *fe, int acquire)
++{
++ struct cx231xx *dev = fe->dvb->priv;
++
++ if (acquire)
++ return cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
++ else
++ return cx231xx_set_mode(dev, CX231XX_SUSPEND);
++}
++
++/* ------------------------------------------------------------------ */
++
++static struct xc5000_config cnxt_rde250_tunerconfig = {
++ .i2c_address = 0x61,
++ .if_khz = 5380,
++};
++
++/* ------------------------------------------------------------------ */
++#if 0
++static int attach_xc5000(u8 addr, struct cx231xx *dev)
++{
++
++ struct dvb_frontend *fe;
++ struct xc5000_config cfg;
++
++ memset(&cfg, 0, sizeof(cfg));
++ cfg.i2c_adap = &dev->i2c_bus[1].i2c_adap;
++ cfg.i2c_addr = addr;
++
++ if (!dev->dvb->frontend) {
++ printk(KERN_ERR "%s/2: dvb frontend not attached. "
++ "Can't attach xc5000\n", dev->name);
++ return -EINVAL;
++ }
++
++ fe = dvb_attach(xc5000_attach, dev->dvb->frontend, &cfg);
++ if (!fe) {
++ printk(KERN_ERR "%s/2: xc5000 attach failed\n", dev->name);
++ dvb_frontend_detach(dev->dvb->frontend);
++ dev->dvb->frontend = NULL;
++ return -EINVAL;
++ }
++
++ printk(KERN_INFO "%s/2: xc5000 attached\n", dev->name);
++
++ return 0;
++}
++#endif
++
++int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq)
++{
++ int status = 0;
++
++ if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) {
++
++ struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops;
++
++ if (dops->set_analog_params != NULL) {
++ struct analog_parameters params;
++
++ params.frequency = freq;
++ params.std = dev->norm;
++ params.mode = 0; /* 0- Air; 1 - cable */
++ /*params.audmode = ; */
++
++ /* Set the analog parameters to set the frequency */
++ cx231xx_info("Setting Frequency for XC5000\n");
++ dops->set_analog_params(dev->dvb->frontend, &params);
++ }
++
++ }
++
++ return status;
++}
++
++int cx231xx_reset_analog_tuner(struct cx231xx *dev)
++{
++ int status = 0;
++
++ if ((dev->dvb != NULL) && (dev->dvb->frontend != NULL)) {
++
++ struct dvb_tuner_ops *dops = &dev->dvb->frontend->ops.tuner_ops;
++
++ if (dops->init != NULL && !dev->xc_fw_load_done) {
++
++ cx231xx_info("Reloading firmware for XC5000\n");
++ status = dops->init(dev->dvb->frontend);
++ if (status == 0) {
++ dev->xc_fw_load_done = 1;
++ cx231xx_info
++ ("XC5000 firmware download completed\n");
++ } else {
++ dev->xc_fw_load_done = 0;
++ cx231xx_info
++ ("XC5000 firmware download failed !!!\n");
++ }
++ }
++
++ }
++
++ return status;
++}
++
++/* ------------------------------------------------------------------ */
++
++static int register_dvb(struct cx231xx_dvb *dvb,
++ struct module *module,
++ struct cx231xx *dev, struct device *device)
++{
++ int result;
++
++ mutex_init(&dvb->lock);
++
++ /* register adapter */
++ result = dvb_register_adapter(&dvb->adapter, dev->name, module, device,
++ adapter_nr);
++ if (result < 0) {
++ printk(KERN_WARNING
++ "%s: dvb_register_adapter failed (errno = %d)\n",
++ dev->name, result);
++ goto fail_adapter;
++ }
++
++ /* Ensure all frontends negotiate bus access */
++ dvb->frontend->ops.ts_bus_ctrl = cx231xx_dvb_bus_ctrl;
++
++ dvb->adapter.priv = dev;
++
++ /* register frontend */
++ result = dvb_register_frontend(&dvb->adapter, dvb->frontend);
++ if (result < 0) {
++ printk(KERN_WARNING
++ "%s: dvb_register_frontend failed (errno = %d)\n",
++ dev->name, result);
++ goto fail_frontend;
++ }
++
++ /* register demux stuff */
++ dvb->demux.dmx.capabilities =
++ DMX_TS_FILTERING | DMX_SECTION_FILTERING |
++ DMX_MEMORY_BASED_FILTERING;
++ dvb->demux.priv = dvb;
++ dvb->demux.filternum = 256;
++ dvb->demux.feednum = 256;
++ dvb->demux.start_feed = start_feed;
++ dvb->demux.stop_feed = stop_feed;
++
++ result = dvb_dmx_init(&dvb->demux);
++ if (result < 0) {
++ printk(KERN_WARNING "%s: dvb_dmx_init failed (errno = %d)\n",
++ dev->name, result);
++ goto fail_dmx;
++ }
++
++ dvb->dmxdev.filternum = 256;
++ dvb->dmxdev.demux = &dvb->demux.dmx;
++ dvb->dmxdev.capabilities = 0;
++ result = dvb_dmxdev_init(&dvb->dmxdev, &dvb->adapter);
++ if (result < 0) {
++ printk(KERN_WARNING "%s: dvb_dmxdev_init failed (errno = %d)\n",
++ dev->name, result);
++ goto fail_dmxdev;
++ }
++
++ dvb->fe_hw.source = DMX_FRONTEND_0;
++ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_hw);
++ if (result < 0) {
++ printk(KERN_WARNING
++ "%s: add_frontend failed (DMX_FRONTEND_0, errno = %d)\n",
++ dev->name, result);
++ goto fail_fe_hw;
++ }
++
++ dvb->fe_mem.source = DMX_MEMORY_FE;
++ result = dvb->demux.dmx.add_frontend(&dvb->demux.dmx, &dvb->fe_mem);
++ if (result < 0) {
++ printk(KERN_WARNING
++ "%s: add_frontend failed (DMX_MEMORY_FE, errno = %d)\n",
++ dev->name, result);
++ goto fail_fe_mem;
++ }
++
++ result = dvb->demux.dmx.connect_frontend(&dvb->demux.dmx, &dvb->fe_hw);
++ if (result < 0) {
++ printk(KERN_WARNING
++ "%s: connect_frontend failed (errno = %d)\n", dev->name,
++ result);
++ goto fail_fe_conn;
++ }
++
++ /* register network adapter */
++ dvb_net_init(&dvb->adapter, &dvb->net, &dvb->demux.dmx);
++ return 0;
++
++fail_fe_conn:
++ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
++fail_fe_mem:
++ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
++fail_fe_hw:
++ dvb_dmxdev_release(&dvb->dmxdev);
++fail_dmxdev:
++ dvb_dmx_release(&dvb->demux);
++fail_dmx:
++ dvb_unregister_frontend(dvb->frontend);
++fail_frontend:
++ dvb_frontend_detach(dvb->frontend);
++ dvb_unregister_adapter(&dvb->adapter);
++fail_adapter:
++ return result;
++}
++
++static void unregister_dvb(struct cx231xx_dvb *dvb)
++{
++ dvb_net_release(&dvb->net);
++ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_mem);
++ dvb->demux.dmx.remove_frontend(&dvb->demux.dmx, &dvb->fe_hw);
++ dvb_dmxdev_release(&dvb->dmxdev);
++ dvb_dmx_release(&dvb->demux);
++ dvb_unregister_frontend(dvb->frontend);
++ dvb_frontend_detach(dvb->frontend);
++ dvb_unregister_adapter(&dvb->adapter);
++}
++
++static int dvb_init(struct cx231xx *dev)
++{
++ int result = 0;
++ struct cx231xx_dvb *dvb;
++
++ if (!dev->board.has_dvb) {
++ /* This device does not support the extension */
++ return 0;
++ }
++
++ dvb = kzalloc(sizeof(struct cx231xx_dvb), GFP_KERNEL);
++
++ if (dvb == NULL) {
++ printk(KERN_INFO "cx231xx_dvb: memory allocation failed\n");
++ return -ENOMEM;
++ }
++ dev->dvb = dvb;
++ dev->cx231xx_set_analog_freq = cx231xx_set_analog_freq;
++ dev->cx231xx_reset_analog_tuner = cx231xx_reset_analog_tuner;
++
++ cx231xx_set_mode(dev, CX231XX_DIGITAL_MODE);
++ /* init frontend */
++ switch (dev->model) {
++ case CX231XX_BOARD_CNXT_RDE_250:
++
++ /* dev->dvb->frontend = dvb_attach(s5h1411_attach,
++ &dvico_s5h1411_config,
++ &dev->i2c_bus[1].i2c_adap); */
++ dev->dvb->frontend = dvb_attach(dvb_dummy_fe_ofdm_attach);
++
++ if (dev->dvb->frontend == NULL) {
++ printk(DRIVER_NAME
++ ": Failed to attach dummy front end\n");
++ result = -EINVAL;
++ goto out_free;
++ }
++
++ /* define general-purpose callback pointer */
++ dvb->frontend->callback = cx231xx_tuner_callback;
++
++ if (dvb_attach(xc5000_attach, dev->dvb->frontend,
++ &dev->i2c_bus[1].i2c_adap,
++ &cnxt_rde250_tunerconfig) < 0) {
++ result = -EINVAL;
++ goto out_free;
++ }
++
++ break;
++ case CX231XX_BOARD_CNXT_RDU_250:
++
++ dev->dvb->frontend = dvb_attach(dvb_dummy_fe_ofdm_attach);
++
++ if (dev->dvb->frontend == NULL) {
++ printk(DRIVER_NAME
++ ": Failed to attach dummy front end\n");
++ result = -EINVAL;
++ goto out_free;
++ }
++
++ /* define general-purpose callback pointer */
++ dvb->frontend->callback = cx231xx_tuner_callback;
++
++ if (dvb_attach(xc5000_attach, dev->dvb->frontend,
++ &dev->i2c_bus[1].i2c_adap,
++ &cnxt_rde250_tunerconfig) < 0) {
++ result = -EINVAL;
++ goto out_free;
++ }
++ break;
++
++ default:
++ printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card"
++ " isn't supported yet\n", dev->name);
++ break;
++ }
++ if (NULL == dvb->frontend) {
++ printk(KERN_ERR
++ "%s/2: frontend initialization failed\n", dev->name);
++ result = -EINVAL;
++ goto out_free;
++ }
++
++ /* register everything */
++ result = register_dvb(dvb, THIS_MODULE, dev, &dev->udev->dev);
++
++ if (result < 0)
++ goto out_free;
++
++ cx231xx_set_mode(dev, CX231XX_SUSPEND);
++ printk(KERN_INFO "Successfully loaded cx231xx-dvb\n");
++ return 0;
++
++out_free:
++ cx231xx_set_mode(dev, CX231XX_SUSPEND);
++ kfree(dvb);
++ dev->dvb = NULL;
++ return result;
++}
++
++static int dvb_fini(struct cx231xx *dev)
++{
++ if (!dev->board.has_dvb) {
++ /* This device does not support the extension */
++ return 0;
++ }
++
++ if (dev->dvb) {
++ unregister_dvb(dev->dvb);
++ dev->dvb = NULL;
++ }
++
++ return 0;
++}
++
++static struct cx231xx_ops dvb_ops = {
++ .id = CX231XX_DVB,
++ .name = "Cx231xx dvb Extension",
++ .init = dvb_init,
++ .fini = dvb_fini,
++};
++
++static int __init cx231xx_dvb_register(void)
++{
++ return cx231xx_register_extension(&dvb_ops);
++}
++
++static void __exit cx231xx_dvb_unregister(void)
++{
++ cx231xx_unregister_extension(&dvb_ops);
++}
++
++module_init(cx231xx_dvb_register);
++module_exit(cx231xx_dvb_unregister);
+diff --git a/drivers/media/video/cx231xx/cx231xx-i2c.c b/drivers/media/video/cx231xx/cx231xx-i2c.c
+new file mode 100644
+index 0000000..b4a03d8
+--- /dev/null
++++ b/drivers/media/video/cx231xx/cx231xx-i2c.c
+@@ -0,0 +1,555 @@
++/*
++ cx231xx-i2c.c - driver for Conexant Cx23100/101/102 USB video capture devices
++
++ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
++ Based on em28xx driver
++ Based on Cx23885 driver
++
++ 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 <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/usb.h>
++#include <linux/i2c.h>
++#include <media/v4l2-common.h>
++#include <media/tuner.h>
++
++#include "cx231xx.h"
++
++/* ----------------------------------------------------------- */
++
++static unsigned int i2c_scan;
++module_param(i2c_scan, int, 0444);
++MODULE_PARM_DESC(i2c_scan, "scan i2c bus at insmod time");
++
++static unsigned int i2c_debug;
++module_param(i2c_debug, int, 0644);
++MODULE_PARM_DESC(i2c_debug, "enable debug messages [i2c]");
++
++#define dprintk1(lvl, fmt, args...) \
++do { \
++ if (i2c_debug >= lvl) { \
++ printk(fmt, ##args); \
++ } \
++} while (0)
++
++#define dprintk2(lvl, fmt, args...) \
++do { \
++ if (i2c_debug >= lvl) { \
++ printk(KERN_DEBUG "%s at %s: " fmt, \
++ dev->name, __func__ , ##args); \
++ } \
++} while (0)
++
++/*
++ * cx231xx_i2c_send_bytes()
++ */
++int cx231xx_i2c_send_bytes(struct i2c_adapter *i2c_adap,
++ const struct i2c_msg *msg)
++{
++ struct cx231xx_i2c *bus = i2c_adap->algo_data;
++ struct cx231xx *dev = bus->dev;
++ struct cx231xx_i2c_xfer_data req_data;
++ int status = 0;
++ u16 size = 0;
++ u8 loop = 0;
++ u8 saddr_len = 1;
++ u8 *buf_ptr = NULL;
++ u16 saddr = 0;
++ u8 need_gpio = 0;
++
++ if ((bus->nr == 1) && (msg->addr == 0x61)
++ && (dev->tuner_type == TUNER_XC5000)) {
++
++ size = msg->len;
++
++ if (size == 2) { /* register write sub addr */
++ /* Just writing sub address will cause problem
++ * to XC5000. So ignore the request */
++ return 0;
++ } else if (size == 4) { /* register write with sub addr */
++ if (msg->len >= 2)
++ saddr = msg->buf[0] << 8 | msg->buf[1];
++ else if (msg->len == 1)
++ saddr = msg->buf[0];
++
++ switch (saddr) {
++ case 0x0000: /* start tuner calibration mode */
++ need_gpio = 1;
++ /* FW Loading is done */
++ dev->xc_fw_load_done = 1;
++ break;
++ case 0x000D: /* Set signal source */
++ case 0x0001: /* Set TV standard - Video */
++ case 0x0002: /* Set TV standard - Audio */
++ case 0x0003: /* Set RF Frequency */
++ need_gpio = 1;
++ break;
++ default:
++ if (dev->xc_fw_load_done)
++ need_gpio = 1;
++ break;
++ }
++
++ if (need_gpio) {
++ dprintk1(1,
++ "GPIO WRITE: addr 0x%x, len %d, saddr 0x%x\n",
++ msg->addr, msg->len, saddr);
++
++ return dev->cx231xx_gpio_i2c_write(dev,
++ msg->addr,
++ msg->buf,
++ msg->len);
++ }
++ }
++
++ /* special case for Xc5000 tuner case */
++ saddr_len = 1;
++
++ /* adjust the length to correct length */
++ size -= saddr_len;
++ buf_ptr = (u8 *) (msg->buf + 1);
++
++ do {
++ /* prepare xfer_data struct */
++ req_data.dev_addr = msg->addr;
++ req_data.direction = msg->flags;
++ req_data.saddr_len = saddr_len;
++ req_data.saddr_dat = msg->buf[0];
++ req_data.buf_size = size > 16 ? 16 : size;
++ req_data.p_buffer = (u8 *) (buf_ptr + loop * 16);
++
++ bus->i2c_nostop = (size > 16) ? 1 : 0;
++ bus->i2c_reserve = (loop == 0) ? 0 : 1;
++
++ /* usb send command */
++ status = dev->cx231xx_send_usb_command(bus, &req_data);
++ loop++;
++
++ if (size >= 16)
++ size -= 16;
++ else
++ size = 0;
++
++ } while (size > 0);
++
++ bus->i2c_nostop = 0;
++ bus->i2c_reserve = 0;
++
++ } else { /* regular case */
++
++ /* prepare xfer_data struct */
++ req_data.dev_addr = msg->addr;
++ req_data.direction = msg->flags;
++ req_data.saddr_len = 0;
++ req_data.saddr_dat = 0;
++ req_data.buf_size = msg->len;
++ req_data.p_buffer = msg->buf;
++
++ /* usb send command */
++ status = dev->cx231xx_send_usb_command(bus, &req_data);
++ }
++
++ return status < 0 ? status : 0;
++}
++
++/*
++ * cx231xx_i2c_recv_bytes()
++ * read a byte from the i2c device
++ */
++static int cx231xx_i2c_recv_bytes(struct i2c_adapter *i2c_adap,
++ const struct i2c_msg *msg)
++{
++ struct cx231xx_i2c *bus = i2c_adap->algo_data;
++ struct cx231xx *dev = bus->dev;
++ struct cx231xx_i2c_xfer_data req_data;
++ int status = 0;
++ u16 saddr = 0;
++ u8 need_gpio = 0;
++
++ if ((bus->nr == 1) && (msg->addr == 0x61)
++ && dev->tuner_type == TUNER_XC5000) {
++
++ if (msg->len == 2)
++ saddr = msg->buf[0] << 8 | msg->buf[1];
++ else if (msg->len == 1)
++ saddr = msg->buf[0];
++
++ if (dev->xc_fw_load_done) {
++
++ switch (saddr) {
++ case 0x0009: /* BUSY check */
++ dprintk1(1,
++ "GPIO R E A D: Special case BUSY check \n");
++ /*Try read BUSY register, just set it to zero*/
++ msg->buf[0] = 0;
++ if (msg->len == 2)
++ msg->buf[1] = 0;
++ return 0;
++ case 0x0004: /* read Lock status */
++ need_gpio = 1;
++ break;
++
++ }
++
++ if (need_gpio) {
++ /* this is a special case to handle Xceive tuner
++ clock stretch issue with gpio based I2C */
++
++ dprintk1(1,
++ "GPIO R E A D: addr 0x%x, len %d, saddr 0x%x\n",
++ msg->addr, msg->len,
++ msg->buf[0] << 8 | msg->buf[1]);
++
++ status =
++ dev->cx231xx_gpio_i2c_write(dev, msg->addr,
++ msg->buf,
++ msg->len);
++ status =
++ dev->cx231xx_gpio_i2c_read(dev, msg->addr,
++ msg->buf,
++ msg->len);
++ return status;
++ }
++ }
++
++ /* prepare xfer_data struct */
++ req_data.dev_addr = msg->addr;
++ req_data.direction = msg->flags;
++ req_data.saddr_len = msg->len;
++ req_data.saddr_dat = msg->buf[0] << 8 | msg->buf[1];
++ req_data.buf_size = msg->len;
++ req_data.p_buffer = msg->buf;
++
++ /* usb send command */
++ status = dev->cx231xx_send_usb_command(bus, &req_data);
++
++ } else {
++
++ /* prepare xfer_data struct */
++ req_data.dev_addr = msg->addr;
++ req_data.direction = msg->flags;
++ req_data.saddr_len = 0;
++ req_data.saddr_dat = 0;
++ req_data.buf_size = msg->len;
++ req_data.p_buffer = msg->buf;
++
++ /* usb send command */
++ status = dev->cx231xx_send_usb_command(bus, &req_data);
++ }
++
++ return status < 0 ? status : 0;
++}
++
++/*
++ * cx231xx_i2c_recv_bytes_with_saddr()
++ * read a byte from the i2c device
++ */
++static int cx231xx_i2c_recv_bytes_with_saddr(struct i2c_adapter *i2c_adap,
++ const struct i2c_msg *msg1,
++ const struct i2c_msg *msg2)
++{
++ struct cx231xx_i2c *bus = i2c_adap->algo_data;
++ struct cx231xx *dev = bus->dev;
++ struct cx231xx_i2c_xfer_data req_data;
++ int status = 0;
++ u16 saddr = 0;
++ u8 need_gpio = 0;
++
++ if (msg1->len == 2)
++ saddr = msg1->buf[0] << 8 | msg1->buf[1];
++ else if (msg1->len == 1)
++ saddr = msg1->buf[0];
++
++ if ((bus->nr == 1) && (msg2->addr == 0x61)
++ && dev->tuner_type == TUNER_XC5000) {
++
++ if ((msg2->len < 16)) {
++
++ dprintk1(1,
++ "i2c_read: addr 0x%x, len %d, saddr 0x%x, len %d\n",
++ msg2->addr, msg2->len, saddr, msg1->len);
++
++ switch (saddr) {
++ case 0x0008: /* read FW load status */
++ need_gpio = 1;
++ break;
++ case 0x0004: /* read Lock status */
++ need_gpio = 1;
++ break;
++ }
++
++ if (need_gpio) {
++ status =
++ dev->cx231xx_gpio_i2c_write(dev, msg1->addr,
++ msg1->buf,
++ msg1->len);
++ status =
++ dev->cx231xx_gpio_i2c_read(dev, msg2->addr,
++ msg2->buf,
++ msg2->len);
++ return status;
++ }
++ }
++ }
++
++ /* prepare xfer_data struct */
++ req_data.dev_addr = msg2->addr;
++ req_data.direction = msg2->flags;
++ req_data.saddr_len = msg1->len;
++ req_data.saddr_dat = saddr;
++ req_data.buf_size = msg2->len;
++ req_data.p_buffer = msg2->buf;
++
++ /* usb send command */
++ status = dev->cx231xx_send_usb_command(bus, &req_data);
++
++ return status < 0 ? status : 0;
++}
++
++/*
++ * cx231xx_i2c_check_for_device()
++ * check if there is a i2c_device at the supplied address
++ */
++static int cx231xx_i2c_check_for_device(struct i2c_adapter *i2c_adap,
++ const struct i2c_msg *msg)
++{
++ struct cx231xx_i2c *bus = i2c_adap->algo_data;
++ struct cx231xx *dev = bus->dev;
++ struct cx231xx_i2c_xfer_data req_data;
++ int status = 0;
++
++ /* prepare xfer_data struct */
++ req_data.dev_addr = msg->addr;
++ req_data.direction = msg->flags;
++ req_data.saddr_len = 0;
++ req_data.saddr_dat = 0;
++ req_data.buf_size = 0;
++ req_data.p_buffer = NULL;
++
++ /* usb send command */
++ status = dev->cx231xx_send_usb_command(bus, &req_data);
++
++ return status < 0 ? status : 0;
++}
++
++/*
++ * cx231xx_i2c_xfer()
++ * the main i2c transfer function
++ */
++static int cx231xx_i2c_xfer(struct i2c_adapter *i2c_adap,
++ struct i2c_msg msgs[], int num)
++{
++ struct cx231xx_i2c *bus = i2c_adap->algo_data;
++ struct cx231xx *dev = bus->dev;
++ int addr, rc, i, byte;
++
++ if (num <= 0)
++ return 0;
++
++ for (i = 0; i < num; i++) {
++
++ addr = msgs[i].addr >> 1;
++
++ dprintk2(2, "%s %s addr=%x len=%d:",
++ (msgs[i].flags & I2C_M_RD) ? "read" : "write",
++ i == num - 1 ? "stop" : "nonstop", addr, msgs[i].len);
++ if (!msgs[i].len) {
++ /* no len: check only for device presence */
++ rc = cx231xx_i2c_check_for_device(i2c_adap, &msgs[i]);
++ if (rc < 0) {
++ dprintk2(2, " no device\n");
++ return rc;
++ }
++
++ } else if (msgs[i].flags & I2C_M_RD) {
++ /* read bytes */
++ rc = cx231xx_i2c_recv_bytes(i2c_adap, &msgs[i]);
++ if (i2c_debug >= 2) {
++ for (byte = 0; byte < msgs[i].len; byte++)
++ printk(" %02x", msgs[i].buf[byte]);
++ }
++ } else if (i + 1 < num && (msgs[i + 1].flags & I2C_M_RD) &&
++ msgs[i].addr == msgs[i + 1].addr
++ && (msgs[i].len <= 2) && (bus->nr < 2)) {
++ /* read bytes */
++ rc = cx231xx_i2c_recv_bytes_with_saddr(i2c_adap,
++ &msgs[i],
++ &msgs[i + 1]);
++ if (i2c_debug >= 2) {
++ for (byte = 0; byte < msgs[i].len; byte++)
++ printk(" %02x", msgs[i].buf[byte]);
++ }
++ i++;
++ } else {
++ /* write bytes */
++ if (i2c_debug >= 2) {
++ for (byte = 0; byte < msgs[i].len; byte++)
++ printk(" %02x", msgs[i].buf[byte]);
++ }
++ rc = cx231xx_i2c_send_bytes(i2c_adap, &msgs[i]);
++ }
++ if (rc < 0)
++ goto err;
++ if (i2c_debug >= 2)
++ printk("\n");
++ }
++
++ return num;
++err:
++ dprintk2(2, " ERROR: %i\n", rc);
++ return rc;
++}
++
++/* ----------------------------------------------------------- */
++
++/*
++ * functionality()
++ */
++static u32 functionality(struct i2c_adapter *adap)
++{
++ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
++}
++
++/*
++ * attach_inform()
++ * gets called when a device attaches to the i2c bus
++ * does some basic configuration
++ */
++static int attach_inform(struct i2c_client *client)
++{
++ struct cx231xx_i2c *bus = i2c_get_adapdata(client->adapter);
++ struct cx231xx *dev = bus->dev;
++
++ switch (client->addr << 1) {
++ case 0x8e:
++ {
++ struct IR_i2c *ir = i2c_get_clientdata(client);
++ dprintk1(1, "attach_inform: IR detected (%s).\n",
++ ir->phys);
++ cx231xx_set_ir(dev, ir);
++ break;
++ }
++ break;
++
++ default:
++ break;
++ }
++
++ return 0;
++}
++
++static struct i2c_algorithm cx231xx_algo = {
++ .master_xfer = cx231xx_i2c_xfer,
++ .functionality = functionality,
++};
++
++static struct i2c_adapter cx231xx_adap_template = {
++ .owner = THIS_MODULE,
++ .name = "cx231xx",
++ .id = I2C_HW_B_CX231XX,
++ .algo = &cx231xx_algo,
++ .client_register = attach_inform,
++};
++
++static struct i2c_client cx231xx_client_template = {
++ .name = "cx231xx internal",
++};
++
++/* ----------------------------------------------------------- */
++
++/*
++ * i2c_devs
++ * incomplete list of known devices
++ */
++static char *i2c_devs[128] = {
++ [0x60 >> 1] = "colibri",
++ [0x88 >> 1] = "hammerhead",
++ [0x8e >> 1] = "CIR",
++ [0x32 >> 1] = "GeminiIII",
++ [0x02 >> 1] = "Aquarius",
++ [0xa0 >> 1] = "eeprom",
++ [0xc0 >> 1] = "tuner/XC3028",
++ [0xc2 >> 1] = "tuner/XC5000",
++};
++
++/*
++ * cx231xx_do_i2c_scan()
++ * check i2c address range for devices
++ */
++void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c)
++{
++ unsigned char buf;
++ int i, rc;
++
++ cx231xx_info(": Checking for I2C devices ..\n");
++ for (i = 0; i < 128; i++) {
++ c->addr = i;
++ rc = i2c_master_recv(c, &buf, 0);
++ if (rc < 0)
++ continue;
++ cx231xx_info("%s: i2c scan: found device @ 0x%x [%s]\n",
++ dev->name, i << 1,
++ i2c_devs[i] ? i2c_devs[i] : "???");
++ }
++ cx231xx_info(": Completed Checking for I2C devices.\n");
++}
++
++/*
++ * cx231xx_i2c_register()
++ * register i2c bus
++ */
++int cx231xx_i2c_register(struct cx231xx_i2c *bus)
++{
++ struct cx231xx *dev = bus->dev;
++
++ BUG_ON(!dev->cx231xx_send_usb_command);
++
++ memcpy(&bus->i2c_adap, &cx231xx_adap_template, sizeof(bus->i2c_adap));
++ memcpy(&bus->i2c_algo, &cx231xx_algo, sizeof(bus->i2c_algo));
++ memcpy(&bus->i2c_client, &cx231xx_client_template,
++ sizeof(bus->i2c_client));
++
++ bus->i2c_adap.dev.parent = &dev->udev->dev;
++
++ strlcpy(bus->i2c_adap.name, bus->dev->name, sizeof(bus->i2c_adap.name));
++
++ bus->i2c_algo.data = bus;
++ bus->i2c_adap.algo_data = bus;
++ i2c_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
++ i2c_add_adapter(&bus->i2c_adap);
++
++ bus->i2c_client.adapter = &bus->i2c_adap;
++
++ if (0 == bus->i2c_rc) {
++ if (i2c_scan)
++ cx231xx_do_i2c_scan(dev, &bus->i2c_client);
++ } else
++ cx231xx_warn("%s: i2c bus %d register FAILED\n",
++ dev->name, bus->nr);
++
++ return bus->i2c_rc;
++}
++
++/*
++ * cx231xx_i2c_unregister()
++ * unregister i2c_bus
++ */
++int cx231xx_i2c_unregister(struct cx231xx_i2c *bus)
++{
++ i2c_del_adapter(&bus->i2c_adap);
++ return 0;
++}
+diff --git a/drivers/media/video/cx231xx/cx231xx-input.c b/drivers/media/video/cx231xx/cx231xx-input.c
+new file mode 100644
+index 0000000..97e304c
+--- /dev/null
++++ b/drivers/media/video/cx231xx/cx231xx-input.c
+@@ -0,0 +1,246 @@
++/*
++ handle cx231xx IR remotes via linux kernel input layer.
++
++ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
++ Based on em28xx driver
++
++ < This is a place holder for IR now.>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/delay.h>
++#include <linux/interrupt.h>
++#include <linux/input.h>
++#include <linux/usb.h>
++
++#include "cx231xx.h"
++
++static unsigned int ir_debug;
++module_param(ir_debug, int, 0644);
++MODULE_PARM_DESC(ir_debug, "enable debug messages [IR]");
++
++#define i2cdprintk(fmt, arg...) \
++ if (ir_debug) { \
++ printk(KERN_DEBUG "%s/ir: " fmt, ir->c.name , ## arg); \
++ }
++
++#define dprintk(fmt, arg...) \
++ if (ir_debug) { \
++ printk(KERN_DEBUG "%s/ir: " fmt, ir->name , ## arg); \
++ }
++
++/**********************************************************
++ Polling structure used by cx231xx IR's
++ **********************************************************/
++
++struct cx231xx_ir_poll_result {
++ unsigned int toggle_bit:1;
++ unsigned int read_count:7;
++ u8 rc_address;
++ u8 rc_data[4];
++};
++
++struct cx231xx_IR {
++ struct cx231xx *dev;
++ struct input_dev *input;
++ struct ir_input_state ir;
++ char name[32];
++ char phys[32];
++
++ /* poll external decoder */
++ int polling;
++ struct work_struct work;
++ struct timer_list timer;
++ unsigned int last_toggle:1;
++ unsigned int last_readcount;
++ unsigned int repeat_interval;
++
++ int (*get_key) (struct cx231xx_IR *, struct cx231xx_ir_poll_result *);
++};
++
++/**********************************************************
++ Polling code for cx231xx
++ **********************************************************/
++
++static void cx231xx_ir_handle_key(struct cx231xx_IR *ir)
++{
++ int result;
++ int do_sendkey = 0;
++ struct cx231xx_ir_poll_result poll_result;
++
++ /* read the registers containing the IR status */
++ result = ir->get_key(ir, &poll_result);
++ if (result < 0) {
++ dprintk("ir->get_key() failed %d\n", result);
++ return;
++ }
++
++ dprintk("ir->get_key result tb=%02x rc=%02x lr=%02x data=%02x\n",
++ poll_result.toggle_bit, poll_result.read_count,
++ ir->last_readcount, poll_result.rc_data[0]);
++
++ if (ir->dev->chip_id == CHIP_ID_EM2874) {
++ /* The em2874 clears the readcount field every time the
++ register is read. The em2860/2880 datasheet says that it
++ is supposed to clear the readcount, but it doesn't. So with
++ the em2874, we are looking for a non-zero read count as
++ opposed to a readcount that is incrementing */
++ ir->last_readcount = 0;
++ }
++
++ if (poll_result.read_count == 0) {
++ /* The button has not been pressed since the last read */
++ } else if (ir->last_toggle != poll_result.toggle_bit) {
++ /* A button has been pressed */
++ dprintk("button has been pressed\n");
++ ir->last_toggle = poll_result.toggle_bit;
++ ir->repeat_interval = 0;
++ do_sendkey = 1;
++ } else if (poll_result.toggle_bit == ir->last_toggle &&
++ poll_result.read_count > 0 &&
++ poll_result.read_count != ir->last_readcount) {
++ /* The button is still being held down */
++ dprintk("button being held down\n");
++
++ /* Debouncer for first keypress */
++ if (ir->repeat_interval++ > 9) {
++ /* Start repeating after 1 second */
++ do_sendkey = 1;
++ }
++ }
++
++ if (do_sendkey) {
++ dprintk("sending keypress\n");
++ ir_input_keydown(ir->input, &ir->ir, poll_result.rc_data[0],
++ poll_result.rc_data[0]);
++ ir_input_nokey(ir->input, &ir->ir);
++ }
++
++ ir->last_readcount = poll_result.read_count;
++ return;
++}
++
++static void ir_timer(unsigned long data)
++{
++ struct cx231xx_IR *ir = (struct cx231xx_IR *)data;
++
++ schedule_work(&ir->work);
++}
++
++static void cx231xx_ir_work(struct work_struct *work)
++{
++ struct cx231xx_IR *ir = container_of(work, struct cx231xx_IR, work);
++
++ cx231xx_ir_handle_key(ir);
++ mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
++}
++
++void cx231xx_ir_start(struct cx231xx_IR *ir)
++{
++ setup_timer(&ir->timer, ir_timer, (unsigned long)ir);
++ INIT_WORK(&ir->work, cx231xx_ir_work);
++ schedule_work(&ir->work);
++}
++
++static void cx231xx_ir_stop(struct cx231xx_IR *ir)
++{
++ del_timer_sync(&ir->timer);
++ flush_scheduled_work();
++}
++
++int cx231xx_ir_init(struct cx231xx *dev)
++{
++ struct cx231xx_IR *ir;
++ struct input_dev *input_dev;
++ u8 ir_config;
++ int err = -ENOMEM;
++
++ if (dev->board.ir_codes == NULL) {
++ /* No remote control support */
++ return 0;
++ }
++
++ ir = kzalloc(sizeof(*ir), GFP_KERNEL);
++ input_dev = input_allocate_device();
++ if (!ir || !input_dev)
++ goto err_out_free;
++
++ ir->input = input_dev;
++
++ /* Setup the proper handler based on the chip */
++ switch (dev->chip_id) {
++ default:
++ printk("Unrecognized cx231xx chip id: IR not supported\n");
++ goto err_out_free;
++ }
++
++ /* This is how often we ask the chip for IR information */
++ ir->polling = 100; /* ms */
++
++ /* init input device */
++ snprintf(ir->name, sizeof(ir->name), "cx231xx IR (%s)", dev->name);
++
++ usb_make_path(dev->udev, ir->phys, sizeof(ir->phys));
++ strlcat(ir->phys, "/input0", sizeof(ir->phys));
++
++ ir_input_init(input_dev, &ir->ir, IR_TYPE_OTHER, dev->board.ir_codes);
++ input_dev->name = ir->name;
++ input_dev->phys = ir->phys;
++ input_dev->id.bustype = BUS_USB;
++ input_dev->id.version = 1;
++ input_dev->id.vendor = le16_to_cpu(dev->udev->descriptor.idVendor);
++ input_dev->id.product = le16_to_cpu(dev->udev->descriptor.idProduct);
++
++ input_dev->dev.parent = &dev->udev->dev;
++ /* record handles to ourself */
++ ir->dev = dev;
++ dev->ir = ir;
++
++ cx231xx_ir_start(ir);
++
++ /* all done */
++ err = input_register_device(ir->input);
++ if (err)
++ goto err_out_stop;
++
++ return 0;
++err_out_stop:
++ cx231xx_ir_stop(ir);
++ dev->ir = NULL;
++err_out_free:
++ input_free_device(input_dev);
++ kfree(ir);
++ return err;
++}
++
++int cx231xx_ir_fini(struct cx231xx *dev)
++{
++ struct cx231xx_IR *ir = dev->ir;
++
++ /* skip detach on non attached boards */
++ if (!ir)
++ return 0;
++
++ cx231xx_ir_stop(ir);
++ input_unregister_device(ir->input);
++ kfree(ir);
++
++ /* done */
++ dev->ir = NULL;
++ return 0;
++}
+diff --git a/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c b/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c
+new file mode 100644
+index 0000000..7473c33
+--- /dev/null
++++ b/drivers/media/video/cx231xx/cx231xx-pcb-cfg.c
+@@ -0,0 +1,795 @@
++/*
++ cx231xx-pcb-config.c - driver for Conexant
++ Cx23100/101/102 USB video capture devices
++
++ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#include "cx231xx.h"
++#include "cx231xx-conf-reg.h"
++
++static unsigned int pcb_debug;
++module_param(pcb_debug, int, 0644);
++MODULE_PARM_DESC(pcb_debug, "enable pcb config debug messages [video]");
++
++/******************************************************************************/
++
++struct pcb_config cx231xx_Scenario[] = {
++ {
++ INDEX_SELFPOWER_DIGITAL_ONLY, /* index */
++ USB_SELF_POWER, /* power_type */
++ 0, /* speed , not decide yet */
++ MOD_DIGITAL, /* mode */
++ SOURCE_TS_BDA, /* ts1_source, digital tv only */
++ NOT_SUPPORTED, /* ts2_source */
++ NOT_SUPPORTED, /* analog source */
++
++ 0, /* digital_index */
++ 0, /* analog index */
++ 0, /* dif_index */
++ 0, /* external_index */
++
++ 1, /* only one configuration */
++ {
++ {
++ 0, /* config index */
++ {
++ 0, /* interrupt ep index */
++ 1, /* ts1 index */
++ NOT_SUPPORTED, /* TS2 index */
++ NOT_SUPPORTED, /* AUDIO */
++ NOT_SUPPORTED, /* VIDEO */
++ NOT_SUPPORTED, /* VANC */
++ NOT_SUPPORTED, /* HANC */
++ NOT_SUPPORTED /* ir_index */
++ }
++ ,
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ }
++ ,
++ /* full-speed config */
++ {
++ {
++ 0, /* config index */
++ {
++ 0, /* interrupt ep index */
++ 1, /* ts1 index */
++ NOT_SUPPORTED, /* TS2 index */
++ NOT_SUPPORTED, /* AUDIO */
++ NOT_SUPPORTED, /* VIDEO */
++ NOT_SUPPORTED, /* VANC */
++ NOT_SUPPORTED, /* HANC */
++ NOT_SUPPORTED /* ir_index */
++ }
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ }
++ }
++ ,
++
++ {
++ INDEX_SELFPOWER_DUAL_DIGITAL, /* index */
++ USB_SELF_POWER, /* power_type */
++ 0, /* speed , not decide yet */
++ MOD_DIGITAL, /* mode */
++ SOURCE_TS_BDA, /* ts1_source, digital tv only */
++ 0, /* ts2_source,need update from register */
++ NOT_SUPPORTED, /* analog source */
++ 0, /* digital_index */
++ 0, /* analog index */
++ 0, /* dif_index */
++ 0, /* external_index */
++
++ 1, /* only one configuration */
++ {
++ {
++ 0, /* config index */
++ {
++ 0, /* interrupt ep index */
++ 1, /* ts1 index */
++ 2, /* TS2 index */
++ NOT_SUPPORTED, /* AUDIO */
++ NOT_SUPPORTED, /* VIDEO */
++ NOT_SUPPORTED, /* VANC */
++ NOT_SUPPORTED, /* HANC */
++ NOT_SUPPORTED /* ir_index */
++ }
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ }
++ ,
++ /* full-speed */
++ {
++ {
++ 0, /* config index */
++ {
++ 0, /* interrupt ep index */
++ 1, /* ts1 index */
++ 2, /* TS2 index */
++ NOT_SUPPORTED, /* AUDIO */
++ NOT_SUPPORTED, /* VIDEO */
++ NOT_SUPPORTED, /* VANC */
++ NOT_SUPPORTED, /* HANC */
++ NOT_SUPPORTED /* ir_index */
++ }
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ }
++ }
++ ,
++
++ {
++ INDEX_SELFPOWER_ANALOG_ONLY, /* index */
++ USB_SELF_POWER, /* power_type */
++ 0, /* speed , not decide yet */
++ MOD_ANALOG | MOD_DIF | MOD_EXTERNAL, /* mode ,analog tv only */
++ NOT_SUPPORTED, /* ts1_source, NOT SUPPORT */
++ NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */
++ 0, /* analog source, need update */
++
++ 0, /* digital_index */
++ 0, /* analog index */
++ 0, /* dif_index */
++ 0, /* external_index */
++
++ 1, /* only one configuration */
++ {
++ {
++ 0, /* config index */
++ {
++ 0, /* interrupt ep index */
++ NOT_SUPPORTED, /* ts1 index */
++ NOT_SUPPORTED, /* TS2 index */
++ 1, /* AUDIO */
++ 2, /* VIDEO */
++ 3, /* VANC */
++ 4, /* HANC */
++ NOT_SUPPORTED /* ir_index */
++ }
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ }
++ ,
++ /* full-speed */
++ {
++ {
++ 0, /* config index */
++ {
++ 0, /* interrupt ep index */
++ NOT_SUPPORTED, /* ts1 index */
++ NOT_SUPPORTED, /* TS2 index */
++ 1, /* AUDIO */
++ 2, /* VIDEO */
++ NOT_SUPPORTED, /* VANC */
++ NOT_SUPPORTED, /* HANC */
++ NOT_SUPPORTED /* ir_index */
++ }
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ }
++ }
++ ,
++
++ {
++ INDEX_SELFPOWER_DUAL, /* index */
++ USB_SELF_POWER, /* power_type */
++ 0, /* speed , not decide yet */
++ /* mode ,analog tv and digital path */
++ MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL,
++ 0, /* ts1_source,will update in register */
++ NOT_SUPPORTED, /* ts2_source,NOT SUPPORT */
++ 0, /* analog source need update */
++ 0, /* digital_index */
++ 0, /* analog index */
++ 0, /* dif_index */
++ 0, /* external_index */
++ 1, /* only one configuration */
++ {
++ {
++ 0, /* config index */
++ {
++ 0, /* interrupt ep index */
++ 1, /* ts1 index */
++ NOT_SUPPORTED, /* TS2 index */
++ 2, /* AUDIO */
++ 3, /* VIDEO */
++ 4, /* VANC */
++ 5, /* HANC */
++ NOT_SUPPORTED /* ir_index */
++ }
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ }
++ ,
++ /* full-speed */
++ {
++ {
++ 0, /* config index */
++ {
++ 0, /* interrupt ep index */
++ 1, /* ts1 index */
++ NOT_SUPPORTED, /* TS2 index */
++ 2, /* AUDIO */
++ 3, /* VIDEO */
++ NOT_SUPPORTED, /* VANC */
++ NOT_SUPPORTED, /* HANC */
++ NOT_SUPPORTED /* ir_index */
++ }
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ }
++ }
++ ,
++
++ {
++ INDEX_SELFPOWER_TRIPLE, /* index */
++ USB_SELF_POWER, /* power_type */
++ 0, /* speed , not decide yet */
++ /* mode ,analog tv and digital path */
++ MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL,
++ 0, /* ts1_source, update in register */
++ 0, /* ts2_source,update in register */
++ 0, /* analog source, need update */
++
++ 0, /* digital_index */
++ 0, /* analog index */
++ 0, /* dif_index */
++ 0, /* external_index */
++ 1, /* only one configuration */
++ {
++ {
++ 0, /* config index */
++ {
++ 0, /* interrupt ep index */
++ 1, /* ts1 index */
++ 2, /* TS2 index */
++ 3, /* AUDIO */
++ 4, /* VIDEO */
++ 5, /* VANC */
++ 6, /* HANC */
++ NOT_SUPPORTED /* ir_index */
++ }
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ }
++ ,
++ /* full-speed */
++ {
++ {
++ 0, /* config index */
++ {
++ 0, /* interrupt ep index */
++ 1, /* ts1 index */
++ 2, /* TS2 index */
++ 3, /* AUDIO */
++ 4, /* VIDEO */
++ NOT_SUPPORTED, /* VANC */
++ NOT_SUPPORTED, /* HANC */
++ NOT_SUPPORTED /* ir_index */
++ }
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ }
++ }
++ ,
++
++ {
++ INDEX_SELFPOWER_COMPRESSOR, /* index */
++ USB_SELF_POWER, /* power_type */
++ 0, /* speed , not decide yet */
++ /* mode ,analog tv AND DIGITAL path */
++ MOD_ANALOG | MOD_DIF | MOD_DIGITAL | MOD_EXTERNAL,
++ NOT_SUPPORTED, /* ts1_source, disable */
++ SOURCE_TS_BDA, /* ts2_source */
++ 0, /* analog source,need update */
++ 0, /* digital_index */
++ 0, /* analog index */
++ 0, /* dif_index */
++ 0, /* external_index */
++ 1, /* only one configuration */
++ {
++ {
++ 0, /* config index */
++ {
++ 0, /* interrupt ep index */
++ NOT_SUPPORTED, /* ts1 index */
++ 1, /* TS2 index */
++ 2, /* AUDIO */
++ 3, /* VIDEO */
++ 4, /* VANC */
++ 5, /* HANC */
++ NOT_SUPPORTED /* ir_index */
++ }
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ }
++ ,
++ /* full-speed */
++ {
++ {
++ 0, /* config index */
++ {
++ 0, /* interrupt ep index */
++ NOT_SUPPORTED, /* ts1 index */
++ 1, /* TS2 index */
++ 2, /* AUDIO */
++ 3, /* VIDEO */
++ NOT_SUPPORTED, /* VANC */
++ NOT_SUPPORTED, /* HANC */
++ NOT_SUPPORTED /* ir_index */
++ }
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ }
++ }
++ ,
++
++ {
++ INDEX_BUSPOWER_DIGITAL_ONLY, /* index */
++ USB_BUS_POWER, /* power_type */
++ 0, /* speed , not decide yet */
++ MOD_DIGITAL, /* mode ,analog tv AND DIGITAL path */
++ SOURCE_TS_BDA, /* ts1_source, disable */
++ NOT_SUPPORTED, /* ts2_source */
++ NOT_SUPPORTED, /* analog source */
++
++ 0, /* digital_index */
++ 0, /* analog index */
++ 0, /* dif_index */
++ 0, /* external_index */
++
++ 1, /* only one configuration */
++ {
++ {
++ 0, /* config index */
++ {
++ 0, /* interrupt ep index = 2 */
++ 1, /* ts1 index */
++ NOT_SUPPORTED, /* TS2 index */
++ NOT_SUPPORTED, /* AUDIO */
++ NOT_SUPPORTED, /* VIDEO */
++ NOT_SUPPORTED, /* VANC */
++ NOT_SUPPORTED, /* HANC */
++ NOT_SUPPORTED /* ir_index */
++ }
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ }
++ ,
++ /* full-speed */
++ {
++ {
++ 0, /* config index */
++ {
++ 0, /* interrupt ep index = 2 */
++ 1, /* ts1 index */
++ NOT_SUPPORTED, /* TS2 index */
++ NOT_SUPPORTED, /* AUDIO */
++ NOT_SUPPORTED, /* VIDEO */
++ NOT_SUPPORTED, /* VANC */
++ NOT_SUPPORTED, /* HANC */
++ NOT_SUPPORTED /* ir_index */
++ }
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ }
++ }
++ ,
++ {
++ INDEX_BUSPOWER_ANALOG_ONLY, /* index */
++ USB_BUS_POWER, /* power_type */
++ 0, /* speed , not decide yet */
++ MOD_ANALOG, /* mode ,analog tv AND DIGITAL path */
++ NOT_SUPPORTED, /* ts1_source, disable */
++ NOT_SUPPORTED, /* ts2_source */
++ SOURCE_ANALOG, /* analog source--analog */
++ 0, /* digital_index */
++ 0, /* analog index */
++ 0, /* dif_index */
++ 0, /* external_index */
++ 1, /* only one configuration */
++ {
++ {
++ 0, /* config index */
++ {
++ 0, /* interrupt ep index */
++ NOT_SUPPORTED, /* ts1 index */
++ NOT_SUPPORTED, /* TS2 index */
++ 1, /* AUDIO */
++ 2, /* VIDEO */
++ 3, /* VANC */
++ 4, /* HANC */
++ NOT_SUPPORTED /* ir_index */
++ }
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ }
++ ,
++ { /* full-speed */
++ {
++ 0, /* config index */
++ {
++ 0, /* interrupt ep index */
++ NOT_SUPPORTED, /* ts1 index */
++ NOT_SUPPORTED, /* TS2 index */
++ 1, /* AUDIO */
++ 2, /* VIDEO */
++ NOT_SUPPORTED, /* VANC */
++ NOT_SUPPORTED, /* HANC */
++ NOT_SUPPORTED /* ir_index */
++ }
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ }
++ }
++ ,
++ {
++ INDEX_BUSPOWER_DIF_ONLY, /* index */
++ USB_BUS_POWER, /* power_type */
++ 0, /* speed , not decide yet */
++ /* mode ,analog tv AND DIGITAL path */
++ MOD_DIF | MOD_ANALOG | MOD_DIGITAL | MOD_EXTERNAL,
++ SOURCE_TS_BDA, /* ts1_source, disable */
++ NOT_SUPPORTED, /* ts2_source */
++ SOURCE_DIF | SOURCE_ANALOG | SOURCE_EXTERNAL, /* analog source, dif */
++ 0, /* digital_index */
++ 0, /* analog index */
++ 0, /* dif_index */
++ 0, /* external_index */
++ 1, /* only one configuration */
++ {
++ {
++ 0, /* config index */
++ {
++ 0, /* interrupt ep index */
++ 1, /* ts1 index */
++ NOT_SUPPORTED, /* TS2 index */
++ 2, /* AUDIO */
++ 3, /* VIDEO */
++ 4, /* VANC */
++ 5, /* HANC */
++ NOT_SUPPORTED /* ir_index */
++ }
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ }
++ ,
++ { /* full speed */
++ {
++ 0, /* config index */
++ {
++ 0, /* interrupt ep index */
++ 1, /* ts1 index */
++ NOT_SUPPORTED, /* TS2 index */
++ 2, /* AUDIO */
++ 3, /* VIDEO */
++ NOT_SUPPORTED, /* VANC */
++ NOT_SUPPORTED, /* HANC */
++ NOT_SUPPORTED /* ir_index */
++ }
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ ,
++ {NOT_SUPPORTED, {NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED, NOT_SUPPORTED, NOT_SUPPORTED,
++ NOT_SUPPORTED}
++ }
++ }
++ }
++ ,
++
++};
++
++/*****************************************************************/
++
++u32 initialize_cx231xx(struct cx231xx *dev)
++{
++ u32 config_info = 0;
++ struct pcb_config *p_pcb_info;
++ u8 usb_speed = 1; /* from register,1--HS, 0--FS */
++ u8 data[4] = { 0, 0, 0, 0 };
++ u32 ts1_source = 0;
++ u32 ts2_source = 0;
++ u32 analog_source = 0;
++ u8 _current_scenario_idx = 0xff;
++
++ ts1_source = SOURCE_TS_BDA;
++ ts2_source = SOURCE_TS_BDA;
++
++ /* read board config register to find out which
++ pcb config it is related to */
++ cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER, BOARD_CFG_STAT, data, 4);
++
++ config_info = *((u32 *) data);
++ usb_speed = (u8) (config_info & 0x1);
++
++ /* Verify this device belongs to Bus power or Self power device */
++ if (config_info & BUS_POWER) { /* bus-power */
++ switch (config_info & BUSPOWER_MASK) {
++ case TS1_PORT | BUS_POWER:
++ cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY].speed =
++ usb_speed;
++ p_pcb_info =
++ &cx231xx_Scenario[INDEX_BUSPOWER_DIGITAL_ONLY];
++ _current_scenario_idx = INDEX_BUSPOWER_DIGITAL_ONLY;
++ break;
++ case AVDEC_ENABLE | BUS_POWER:
++ cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY].speed =
++ usb_speed;
++ p_pcb_info =
++ &cx231xx_Scenario[INDEX_BUSPOWER_ANALOG_ONLY];
++ _current_scenario_idx = INDEX_BUSPOWER_ANALOG_ONLY;
++ break;
++ case AVDEC_ENABLE | BUS_POWER | TS1_PORT:
++ cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY].speed =
++ usb_speed;
++ p_pcb_info = &cx231xx_Scenario[INDEX_BUSPOWER_DIF_ONLY];
++ _current_scenario_idx = INDEX_BUSPOWER_DIF_ONLY;
++ break;
++ default:
++ cx231xx_info("bad config in buspower!!!!\n");
++ cx231xx_info("config_info=%x\n",
++ (config_info & BUSPOWER_MASK));
++ return 1;
++ }
++ } else { /* self-power */
++
++ switch (config_info & SELFPOWER_MASK) {
++ case TS1_PORT | SELF_POWER:
++ cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY].speed =
++ usb_speed;
++ p_pcb_info =
++ &cx231xx_Scenario[INDEX_SELFPOWER_DIGITAL_ONLY];
++ _current_scenario_idx = INDEX_SELFPOWER_DIGITAL_ONLY;
++ break;
++ case TS1_TS2_PORT | SELF_POWER:
++ cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL].speed =
++ usb_speed;
++ cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL].
++ ts2_source = ts2_source;
++ p_pcb_info =
++ &cx231xx_Scenario[INDEX_SELFPOWER_DUAL_DIGITAL];
++ _current_scenario_idx = INDEX_SELFPOWER_DUAL_DIGITAL;
++ break;
++ case AVDEC_ENABLE | SELF_POWER:
++ cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY].speed =
++ usb_speed;
++ cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY].
++ analog_source = analog_source;
++ p_pcb_info =
++ &cx231xx_Scenario[INDEX_SELFPOWER_ANALOG_ONLY];
++ _current_scenario_idx = INDEX_SELFPOWER_ANALOG_ONLY;
++ break;
++ case AVDEC_ENABLE | TS1_PORT | SELF_POWER:
++ cx231xx_Scenario[INDEX_SELFPOWER_DUAL].speed =
++ usb_speed;
++ cx231xx_Scenario[INDEX_SELFPOWER_DUAL].ts1_source =
++ ts1_source;
++ cx231xx_Scenario[INDEX_SELFPOWER_DUAL].analog_source =
++ analog_source;
++ p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_DUAL];
++ _current_scenario_idx = INDEX_SELFPOWER_DUAL;
++ break;
++ case AVDEC_ENABLE | TS1_TS2_PORT | SELF_POWER:
++ cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].speed =
++ usb_speed;
++ cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts1_source =
++ ts1_source;
++ cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].ts2_source =
++ ts2_source;
++ cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE].analog_source =
++ analog_source;
++ p_pcb_info = &cx231xx_Scenario[INDEX_SELFPOWER_TRIPLE];
++ _current_scenario_idx = INDEX_SELFPOWER_TRIPLE;
++ break;
++ case AVDEC_ENABLE | TS1VIP_TS2_PORT | SELF_POWER:
++ cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR].speed =
++ usb_speed;
++ cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR].
++ analog_source = analog_source;
++ p_pcb_info =
++ &cx231xx_Scenario[INDEX_SELFPOWER_COMPRESSOR];
++ _current_scenario_idx = INDEX_SELFPOWER_COMPRESSOR;
++ break;
++ default:
++ cx231xx_info("bad senario!!!!!\n");
++ cx231xx_info("config_info=%x\n",
++ (config_info & SELFPOWER_MASK));
++ return 1;
++ }
++ }
++
++ dev->current_scenario_idx = _current_scenario_idx;
++
++ memcpy(&dev->current_pcb_config, p_pcb_info,
++ sizeof(struct pcb_config));
++
++ if (pcb_debug) {
++ cx231xx_info("SC(0x00) register = 0x%x\n", config_info);
++ cx231xx_info("scenario %d\n",
++ (dev->current_pcb_config.index) + 1);
++ cx231xx_info("type=%x\n", dev->current_pcb_config.type);
++ cx231xx_info("mode=%x\n", dev->current_pcb_config.mode);
++ cx231xx_info("speed=%x\n", dev->current_pcb_config.speed);
++ cx231xx_info("ts1_source=%x\n",
++ dev->current_pcb_config.ts1_source);
++ cx231xx_info("ts2_source=%x\n",
++ dev->current_pcb_config.ts2_source);
++ cx231xx_info("analog_source=%x\n",
++ dev->current_pcb_config.analog_source);
++ }
++
++ return 0;
++}
+diff --git a/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h b/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h
+new file mode 100644
+index 0000000..f5e46e8
+--- /dev/null
++++ b/drivers/media/video/cx231xx/cx231xx-pcb-cfg.h
+@@ -0,0 +1,231 @@
++/*
++ cx231xx-pcb-cfg.h - driver for Conexant
++ Cx23100/101/102 USB video capture devices
++
++ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#ifndef _PCB_CONFIG_H_
++#define _PCB_CONFIG_H_
++
++#include <linux/init.h>
++#include <linux/module.h>
++
++/***************************************************************************
++ * Class Information *
++***************************************************************************/
++#define CLASS_DEFAULT 0xFF
++
++enum VENDOR_REQUEST_TYPE {
++ /* Set/Get I2C */
++ VRT_SET_I2C0 = 0x0,
++ VRT_SET_I2C1 = 0x1,
++ VRT_SET_I2C2 = 0x2,
++ VRT_GET_I2C0 = 0x4,
++ VRT_GET_I2C1 = 0x5,
++ VRT_GET_I2C2 = 0x6,
++
++ /* Set/Get GPIO */
++ VRT_SET_GPIO = 0x8,
++ VRT_GET_GPIO = 0x9,
++
++ /* Set/Get GPIE */
++ VRT_SET_GPIE = 0xA,
++ VRT_GET_GPIE = 0xB,
++
++ /* Set/Get Register Control/Status */
++ VRT_SET_REGISTER = 0xC,
++ VRT_GET_REGISTER = 0xD,
++
++ /* Get Extended Compat ID Descriptor */
++ VRT_GET_EXTCID_DESC = 0xFF,
++};
++
++enum BYTE_ENABLE_MASK {
++ ENABLE_ONE_BYTE = 0x1,
++ ENABLE_TWE_BYTE = 0x3,
++ ENABLE_THREE_BYTE = 0x7,
++ ENABLE_FOUR_BYTE = 0xF,
++};
++
++#define SPEED_MASK 0x1
++enum USB_SPEED{
++ FULL_SPEED = 0x0, /* 0: full speed */
++ HIGH_SPEED = 0x1 /* 1: high speed */
++};
++
++enum _true_false{
++ FALSE = 0,
++ TRUE = 1
++};
++
++#define TS_MASK 0x6
++enum TS_PORT{
++ NO_TS_PORT = 0x0, /* 2'b00: Neither port used. PCB not a Hybrid,
++ only offers Analog TV or Video */
++ TS1_PORT = 0x4, /* 2'b10: TS1 Input (Hybrid mode :
++ Digital or External Analog/Compressed source) */
++ TS1_TS2_PORT = 0x6, /* 2'b11: TS1 & TS2 Inputs
++ (Dual inputs from Digital and/or
++ External Analog/Compressed sources) */
++ TS1_EXT_CLOCK = 0x6, /* 2'b11: TS1 & TS2 as selector
++ to external clock */
++ TS1VIP_TS2_PORT = 0x2 /* 2'b01: TS1 used as 656/VIP Output,
++ TS2 Input (from Compressor) */
++};
++
++#define EAVP_MASK 0x8
++enum EAV_PRESENT{
++ NO_EXTERNAL_AV = 0x0, /* 0: No External A/V inputs
++ (no need for i2s blcok),
++ Analog Tuner must be present */
++ EXTERNAL_AV = 0x8 /* 1: External A/V inputs
++ present (requires i2s blk) */
++};
++
++#define ATM_MASK 0x30
++enum AT_MODE{
++ DIF_TUNER = 0x30, /* 2'b11: IF Tuner (requires use of DIF) */
++ BASEBAND_SOUND = 0x20, /* 2'b10: Baseband Composite &
++ Sound-IF Signals present */
++ NO_TUNER = 0x10 /* 2'b0x: No Analog Tuner present */
++};
++
++#define PWR_SEL_MASK 0x40
++enum POWE_TYPE{
++ SELF_POWER = 0x0, /* 0: self power */
++ BUS_POWER = 0x40 /* 1: bus power */
++};
++
++enum USB_POWE_TYPE{
++ USB_SELF_POWER = 0,
++ USB_BUS_POWER
++};
++
++#define BO_0_MASK 0x80
++enum AVDEC_STATUS{
++ AVDEC_DISABLE = 0x0, /* 0: A/V Decoder Disabled */
++ AVDEC_ENABLE = 0x80 /* 1: A/V Decoder Enabled */
++};
++
++#define BO_1_MASK 0x100
++
++#define BUSPOWER_MASK 0xC4 /* for Polaris spec 0.8 */
++#define SELFPOWER_MASK 0x86
++
++/***************************************************************************/
++#define NOT_DECIDE_YET 0xFE
++#define NOT_SUPPORTED 0xFF
++
++/***************************************************************************
++ * for mod field use *
++***************************************************************************/
++#define MOD_DIGITAL 0x1
++#define MOD_ANALOG 0x2
++#define MOD_DIF 0x4
++#define MOD_EXTERNAL 0x8
++#define CAP_ALL_MOD 0x0f
++
++/***************************************************************************
++ * source define *
++***************************************************************************/
++#define SOURCE_DIGITAL 0x1
++#define SOURCE_ANALOG 0x2
++#define SOURCE_DIF 0x4
++#define SOURCE_EXTERNAL 0x8
++#define SOURCE_TS_BDA 0x10
++#define SOURCE_TS_ENCODE 0x20
++#define SOURCE_TS_EXTERNAL 0x40
++
++/***************************************************************************
++ * interface information define *
++***************************************************************************/
++struct INTERFACE_INFO {
++ u8 interrupt_index;
++ u8 ts1_index;
++ u8 ts2_index;
++ u8 audio_index;
++ u8 video_index;
++ u8 vanc_index; /* VBI */
++ u8 hanc_index; /* Sliced CC */
++ u8 ir_index;
++};
++
++enum INDEX_INTERFACE_INFO{
++ INDEX_INTERRUPT = 0x0,
++ INDEX_TS1,
++ INDEX_TS2,
++ INDEX_AUDIO,
++ INDEX_VIDEO,
++ INDEX_VANC,
++ INDEX_HANC,
++ INDEX_IR,
++};
++
++/***************************************************************************
++ * configuration information define *
++***************************************************************************/
++struct CONFIG_INFO {
++ u8 config_index;
++ struct INTERFACE_INFO interface_info;
++};
++
++struct pcb_config {
++ u8 index;
++ u8 type; /* bus power or self power,
++ self power--0, bus_power--1 */
++ u8 speed; /* usb speed, 2.0--1, 1.1--0 */
++ u8 mode; /* digital , anlog, dif or external A/V */
++ u32 ts1_source; /* three source -- BDA,External,encode */
++ u32 ts2_source;
++ u32 analog_source;
++ u8 digital_index; /* bus-power used */
++ u8 analog_index; /* bus-power used */
++ u8 dif_index; /* bus-power used */
++ u8 external_index; /* bus-power used */
++ u8 config_num; /* current config num, 0,1,2,
++ for self-power, always 0 */
++ struct CONFIG_INFO hs_config_info[3];
++ struct CONFIG_INFO fs_config_info[3];
++};
++
++enum INDEX_PCB_CONFIG{
++ INDEX_SELFPOWER_DIGITAL_ONLY = 0x0,
++ INDEX_SELFPOWER_DUAL_DIGITAL,
++ INDEX_SELFPOWER_ANALOG_ONLY,
++ INDEX_SELFPOWER_DUAL,
++ INDEX_SELFPOWER_TRIPLE,
++ INDEX_SELFPOWER_COMPRESSOR,
++ INDEX_BUSPOWER_DIGITAL_ONLY,
++ INDEX_BUSPOWER_ANALOG_ONLY,
++ INDEX_BUSPOWER_DIF_ONLY,
++ INDEX_BUSPOWER_EXTERNAL_ONLY,
++ INDEX_BUSPOWER_EXTERNAL_ANALOG,
++ INDEX_BUSPOWER_EXTERNAL_DIF,
++ INDEX_BUSPOWER_EXTERNAL_DIGITAL,
++ INDEX_BUSPOWER_DIGITAL_ANALOG,
++ INDEX_BUSPOWER_DIGITAL_DIF,
++ INDEX_BUSPOWER_DIGITAL_ANALOG_EXTERNAL,
++ INDEX_BUSPOWER_DIGITAL_DIF_EXTERNAL,
++};
++
++/***************************************************************************/
++struct cx231xx;
++
++u32 initialize_cx231xx(struct cx231xx *p_dev);
++
++#endif
+diff --git a/drivers/media/video/cx231xx/cx231xx-reg.h b/drivers/media/video/cx231xx/cx231xx-reg.h
+new file mode 100644
+index 0000000..750c5d3
+--- /dev/null
++++ b/drivers/media/video/cx231xx/cx231xx-reg.h
+@@ -0,0 +1,1564 @@
++/*
++ cx231xx-reg.h - driver for Conexant Cx23100/101/102
++ USB video capture devices
++
++ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#ifndef _CX231XX_REG_H
++#define _CX231XX_REG_H
++
++/*****************************************************************************
++ * VBI codes *
++*****************************************************************************/
++
++#define SAV_ACTIVE_VIDEO_FIELD1 0x80
++#define EAV_ACTIVE_VIDEO_FIELD1 0x90
++
++#define SAV_ACTIVE_VIDEO_FIELD2 0xc0
++#define EAV_ACTIVE_VIDEO_FIELD2 0xd0
++
++#define SAV_VBLANK_FIELD1 0xa0
++#define EAV_VBLANK_FIELD1 0xb0
++
++#define SAV_VBLANK_FIELD2 0xe0
++#define EAV_VBLANK_FIELD2 0xf0
++
++#define SAV_VBI_FIELD1 0x20
++#define EAV_VBI_FIELD1 0x30
++
++#define SAV_VBI_FIELD2 0x60
++#define EAV_VBI_FIELD2 0x70
++
++/*****************************************************************************/
++/* Audio ADC Registers */
++#define CH_PWR_CTRL1 0x0000000e
++#define CH_PWR_CTRL2 0x0000000f
++/*****************************************************************************/
++
++#define HOST_REG1 0x000
++#define FLD_FORCE_CHIP_SEL 0x80
++#define FLD_AUTO_INC_DIS 0x20
++#define FLD_PREFETCH_EN 0x10
++/* Reserved [2:3] */
++#define FLD_DIGITAL_PWR_DN 0x02
++#define FLD_SLEEP 0x01
++
++/*****************************************************************************/
++#define HOST_REG2 0x001
++
++/*****************************************************************************/
++#define HOST_REG3 0x002
++
++/*****************************************************************************/
++/* added for polaris */
++#define GPIO_PIN_CTL0 0x3
++#define GPIO_PIN_CTL1 0x4
++#define GPIO_PIN_CTL2 0x5
++#define GPIO_PIN_CTL3 0x6
++#define TS1_PIN_CTL0 0x7
++#define TS1_PIN_CTL1 0x8
++/*****************************************************************************/
++
++#define FLD_CLK_IN_EN 0x80
++#define FLD_XTAL_CTRL 0x70
++#define FLD_BB_CLK_MODE 0x0C
++#define FLD_REF_DIV_PLL 0x02
++#define FLD_REF_SEL_PLL1 0x01
++
++/*****************************************************************************/
++#define CHIP_CTRL 0x100
++/* Reserved [27] */
++/* Reserved [31:21] */
++#define FLD_CHIP_ACFG_DIS 0x00100000
++/* Reserved [19] */
++#define FLD_DUAL_MODE_ADC2 0x00040000
++#define FLD_SIF_EN 0x00020000
++#define FLD_SOFT_RST 0x00010000
++#define FLD_DEVICE_ID 0x0000ffff
++
++/*****************************************************************************/
++#define AFE_CTRL 0x104
++#define AFE_CTRL_C2HH_SRC_CTRL 0x104
++#define FLD_DIF_OUT_SEL 0xc0000000
++#define FLD_AUX_PLL_CLK_ALT_SEL 0x3c000000
++#define FLD_UV_ORDER_MODE 0x02000000
++#define FLD_FUNC_MODE 0x01800000
++#define FLD_ROT1_PHASE_CTL 0x007f8000
++#define FLD_AUD_IN_SEL 0x00004000
++#define FLD_LUMA_IN_SEL 0x00002000
++#define FLD_CHROMA_IN_SEL 0x00001000
++/* reserve [11:10] */
++#define FLD_INV_SPEC_DIS 0x00000200
++#define FLD_VGA_SEL_CH3 0x00000100
++#define FLD_VGA_SEL_CH2 0x00000080
++#define FLD_VGA_SEL_CH1 0x00000040
++#define FLD_DCR_BYP_CH1 0x00000020
++#define FLD_DCR_BYP_CH2 0x00000010
++#define FLD_DCR_BYP_CH3 0x00000008
++#define FLD_EN_12DB_CH3 0x00000004
++#define FLD_EN_12DB_CH2 0x00000002
++#define FLD_EN_12DB_CH1 0x00000001
++
++/* redefine in Cx231xx */
++/*****************************************************************************/
++#define DC_CTRL1 0x108
++/* reserve [31:30] */
++#define FLD_CLAMP_LVL_CH1 0x3fff8000
++#define FLD_CLAMP_LVL_CH2 0x00007fff
++/*****************************************************************************/
++
++/*****************************************************************************/
++#define DC_CTRL2 0x10c
++/* reserve [31:28] */
++#define FLD_CLAMP_LVL_CH3 0x00fffe00
++#define FLD_CLAMP_WIND_LENTH 0x000001e0
++#define FLD_C2HH_SAT_MIN 0x0000001e
++#define FLD_FLT_BYP_SEL 0x00000001
++/*****************************************************************************/
++
++/*****************************************************************************/
++#define DC_CTRL3 0x110
++/* reserve [31:16] */
++#define FLD_ERR_GAIN_CTL 0x00070000
++#define FLD_LPF_MIN 0x0000ffff
++/*****************************************************************************/
++
++/*****************************************************************************/
++#define DC_CTRL4 0x114
++/* reserve [31:31] */
++#define FLD_INTG_CH1 0x7fffffff
++/*****************************************************************************/
++
++/*****************************************************************************/
++#define DC_CTRL5 0x118
++/* reserve [31:31] */
++#define FLD_INTG_CH2 0x7fffffff
++/*****************************************************************************/
++
++/*****************************************************************************/
++#define DC_CTRL6 0x11c
++/* reserve [31:31] */
++#define FLD_INTG_CH3 0x7fffffff
++/*****************************************************************************/
++
++/*****************************************************************************/
++#define PIN_CTRL 0x120
++#define FLD_OEF_AGC_RF 0x00000001
++#define FLD_OEF_AGC_IFVGA 0x00000002
++#define FLD_OEF_AGC_IF 0x00000004
++#define FLD_REG_BO_PUD 0x80000000
++#define FLD_IR_IRQ_STAT 0x40000000
++#define FLD_AUD_IRQ_STAT 0x20000000
++#define FLD_VID_IRQ_STAT 0x10000000
++/* Reserved [27:26] */
++#define FLD_IRQ_N_OUT_EN 0x02000000
++#define FLD_IRQ_N_POLAR 0x01000000
++/* Reserved [23:6] */
++#define FLD_OE_AUX_PLL_CLK 0x00000020
++#define FLD_OE_I2S_BCLK 0x00000010
++#define FLD_OE_I2S_WCLK 0x00000008
++#define FLD_OE_AGC_IF 0x00000004
++#define FLD_OE_AGC_IFVGA 0x00000002
++#define FLD_OE_AGC_RF 0x00000001
++
++/*****************************************************************************/
++#define AUD_IO_CTRL 0x124
++/* Reserved [31:8] */
++#define FLD_I2S_PORT_DIR 0x00000080
++#define FLD_I2S_OUT_SRC 0x00000040
++#define FLD_AUD_CHAN3_SRC 0x00000030
++#define FLD_AUD_CHAN2_SRC 0x0000000c
++#define FLD_AUD_CHAN1_SRC 0x00000003
++
++/*****************************************************************************/
++#define AUD_LOCK1 0x128
++#define FLD_AUD_LOCK_KI_SHIFT 0xc0000000
++#define FLD_AUD_LOCK_KD_SHIFT 0x30000000
++/* Reserved [27:25] */
++#define FLD_EN_AV_LOCK 0x01000000
++#define FLD_VID_COUNT 0x00ffffff
++
++/*****************************************************************************/
++#define AUD_LOCK2 0x12c
++#define FLD_AUD_LOCK_KI_MULT 0xf0000000
++#define FLD_AUD_LOCK_KD_MULT 0x0F000000
++/* Reserved [23:22] */
++#define FLD_AUD_LOCK_FREQ_SHIFT 0x00300000
++#define FLD_AUD_COUNT 0x000fffff
++
++/*****************************************************************************/
++#define AFE_DIAG_CTRL1 0x134
++/* Reserved [31:16] */
++#define FLD_CUV_DLY_LENGTH 0x0000ff00
++#define FLD_YC_DLY_LENGTH 0x000000ff
++
++/*****************************************************************************/
++/* Poalris redefine */
++#define AFE_DIAG_CTRL3 0x138
++/* Reserved [31:26] */
++#define FLD_AUD_DUAL_FLAG_POL 0x02000000
++#define FLD_VID_DUAL_FLAG_POL 0x01000000
++/* Reserved [23:23] */
++#define FLD_COL_CLAMP_DIS_CH1 0x00400000
++#define FLD_COL_CLAMP_DIS_CH2 0x00200000
++#define FLD_COL_CLAMP_DIS_CH3 0x00100000
++
++#define TEST_CTRL1 0x144
++/* Reserved [31:29] */
++#define FLD_LBIST_EN 0x10000000
++/* Reserved [27:10] */
++#define FLD_FI_BIST_INTR_R 0x0000200
++#define FLD_FI_BIST_INTR_L 0x0000100
++#define FLD_BIST_FAIL_AUD_PLL 0x0000080
++#define FLD_BIST_INTR_AUD_PLL 0x0000040
++#define FLD_BIST_FAIL_VID_PLL 0x0000020
++#define FLD_BIST_INTR_VID_PLL 0x0000010
++/* Reserved [3:1] */
++#define FLD_CIR_TEST_DIS 0x00000001
++
++/*****************************************************************************/
++#define TEST_CTRL2 0x148
++#define FLD_TSXCLK_POL_CTL 0x80000000
++#define FLD_ISO_CTL_SEL 0x40000000
++#define FLD_ISO_CTL_EN 0x20000000
++#define FLD_BIST_DEBUGZ 0x10000000
++#define FLD_AUD_BIST_TEST_H 0x0f000000
++/* Reserved [23:22] */
++#define FLD_FLTRN_BIST_TEST_H 0x00020000
++#define FLD_VID_BIST_TEST_H 0x00010000
++/* Reserved [19:17] */
++#define FLD_BIST_TEST_H 0x00010000
++/* Reserved [15:13] */
++#define FLD_TAB_EN 0x00001000
++/* Reserved [11:0] */
++
++/*****************************************************************************/
++#define BIST_STAT 0x14c
++#define FLD_AUD_BIST_FAIL_H 0xfff00000
++#define FLD_FLTRN_BIST_FAIL_H 0x00180000
++#define FLD_VID_BIST_FAIL_H 0x00070000
++#define FLD_AUD_BIST_TST_DONE 0x0000fff0
++#define FLD_FLTRN_BIST_TST_DONE 0x00000008
++#define FLD_VID_BIST_TST_DONE 0x00000007
++
++/*****************************************************************************/
++/* DirectIF registers definition have been moved to DIF_reg.h */
++/*****************************************************************************/
++#define MODE_CTRL 0x400
++#define FLD_AFD_PAL60_DIS 0x20000000
++#define FLD_AFD_FORCE_SECAM 0x10000000
++#define FLD_AFD_FORCE_PALNC 0x08000000
++#define FLD_AFD_FORCE_PAL 0x04000000
++#define FLD_AFD_PALM_SEL 0x03000000
++#define FLD_CKILL_MODE 0x00300000
++#define FLD_COMB_NOTCH_MODE 0x00c00000 /* bit[19:18] */
++#define FLD_CLR_LOCK_STAT 0x00020000
++#define FLD_FAST_LOCK_MD 0x00010000
++#define FLD_WCEN 0x00008000
++#define FLD_CAGCEN 0x00004000
++#define FLD_CKILLEN 0x00002000
++#define FLD_AUTO_SC_LOCK 0x00001000
++#define FLD_MAN_SC_FAST_LOCK 0x00000800
++#define FLD_INPUT_MODE 0x00000600
++#define FLD_AFD_ACQUIRE 0x00000100
++#define FLD_AFD_NTSC_SEL 0x00000080
++#define FLD_AFD_PAL_SEL 0x00000040
++#define FLD_ACFG_DIS 0x00000020
++#define FLD_SQ_PIXEL 0x00000010
++#define FLD_VID_FMT_SEL 0x0000000f
++
++/*****************************************************************************/
++#define OUT_CTRL1 0x404
++#define FLD_POLAR 0x7f000000
++/* Reserved [23] */
++#define FLD_RND_MODE 0x00600000
++#define FLD_VIPCLAMP_EN 0x00100000
++#define FLD_VIPBLANK_EN 0x00080000
++#define FLD_VIP_OPT_AL 0x00040000
++#define FLD_IDID0_SOURCE 0x00020000
++#define FLD_DCMODE 0x00010000
++#define FLD_CLK_GATING 0x0000c000
++#define FLD_CLK_INVERT 0x00002000
++#define FLD_HSFMT 0x00001000
++#define FLD_VALIDFMT 0x00000800
++#define FLD_ACTFMT 0x00000400
++#define FLD_SWAPRAW 0x00000200
++#define FLD_CLAMPRAW_EN 0x00000100
++#define FLD_BLUE_FIELD_EN 0x00000080
++#define FLD_BLUE_FIELD_ACT 0x00000040
++#define FLD_TASKBIT_VAL 0x00000020
++#define FLD_ANC_DATA_EN 0x00000010
++#define FLD_VBIHACTRAW_EN 0x00000008
++#define FLD_MODE10B 0x00000004
++#define FLD_OUT_MODE 0x00000003
++
++/*****************************************************************************/
++#define OUT_CTRL2 0x408
++#define FLD_AUD_GRP 0xc0000000
++#define FLD_SAMPLE_RATE 0x30000000
++#define FLD_AUD_ANC_EN 0x08000000
++#define FLD_EN_C 0x04000000
++#define FLD_EN_B 0x02000000
++#define FLD_EN_A 0x01000000
++/* Reserved [23:20] */
++#define FLD_IDID1_LSB 0x000c0000
++#define FLD_IDID0_LSB 0x00030000
++#define FLD_IDID1_MSB 0x0000ff00
++#define FLD_IDID0_MSB 0x000000ff
++
++/*****************************************************************************/
++#define GEN_STAT 0x40c
++#define FLD_VCR_DETECT 0x00800000
++#define FLD_SPECIAL_PLAY_N 0x00400000
++#define FLD_VPRES 0x00200000
++#define FLD_AGC_LOCK 0x00100000
++#define FLD_CSC_LOCK 0x00080000
++#define FLD_VLOCK 0x00040000
++#define FLD_SRC_LOCK 0x00020000
++#define FLD_HLOCK 0x00010000
++#define FLD_VSYNC_N 0x00008000
++#define FLD_SRC_FIFO_UFLOW 0x00004000
++#define FLD_SRC_FIFO_OFLOW 0x00002000
++#define FLD_FIELD 0x00001000
++#define FLD_AFD_FMT_STAT 0x00000f00
++#define FLD_MV_TYPE2_PAIR 0x00000080
++#define FLD_MV_T3CS 0x00000040
++#define FLD_MV_CS 0x00000020
++#define FLD_MV_PSP 0x00000010
++/* Reserved [3] */
++#define FLD_MV_CDAT 0x00000003
++
++/*****************************************************************************/
++#define INT_STAT_MASK 0x410
++#define FLD_COMB_3D_FIFO_MSK 0x80000000
++#define FLD_WSS_DAT_AVAIL_MSK 0x40000000
++#define FLD_GS2_DAT_AVAIL_MSK 0x20000000
++#define FLD_GS1_DAT_AVAIL_MSK 0x10000000
++#define FLD_CC_DAT_AVAIL_MSK 0x08000000
++#define FLD_VPRES_CHANGE_MSK 0x04000000
++#define FLD_MV_CHANGE_MSK 0x02000000
++#define FLD_END_VBI_EVEN_MSK 0x01000000
++#define FLD_END_VBI_ODD_MSK 0x00800000
++#define FLD_FMT_CHANGE_MSK 0x00400000
++#define FLD_VSYNC_TRAIL_MSK 0x00200000
++#define FLD_HLOCK_CHANGE_MSK 0x00100000
++#define FLD_VLOCK_CHANGE_MSK 0x00080000
++#define FLD_CSC_LOCK_CHANGE_MSK 0x00040000
++#define FLD_SRC_FIFO_UFLOW_MSK 0x00020000
++#define FLD_SRC_FIFO_OFLOW_MSK 0x00010000
++#define FLD_COMB_3D_FIFO_STAT 0x00008000
++#define FLD_WSS_DAT_AVAIL_STAT 0x00004000
++#define FLD_GS2_DAT_AVAIL_STAT 0x00002000
++#define FLD_GS1_DAT_AVAIL_STAT 0x00001000
++#define FLD_CC_DAT_AVAIL_STAT 0x00000800
++#define FLD_VPRES_CHANGE_STAT 0x00000400
++#define FLD_MV_CHANGE_STAT 0x00000200
++#define FLD_END_VBI_EVEN_STAT 0x00000100
++#define FLD_END_VBI_ODD_STAT 0x00000080
++#define FLD_FMT_CHANGE_STAT 0x00000040
++#define FLD_VSYNC_TRAIL_STAT 0x00000020
++#define FLD_HLOCK_CHANGE_STAT 0x00000010
++#define FLD_VLOCK_CHANGE_STAT 0x00000008
++#define FLD_CSC_LOCK_CHANGE_STAT 0x00000004
++#define FLD_SRC_FIFO_UFLOW_STAT 0x00000002
++#define FLD_SRC_FIFO_OFLOW_STAT 0x00000001
++
++/*****************************************************************************/
++#define LUMA_CTRL 0x414
++#define BRIGHTNESS_CTRL_BYTE 0x414
++#define CONTRAST_CTRL_BYTE 0x415
++#define LUMA_CTRL_BYTE_3 0x416
++#define FLD_LUMA_CORE_SEL 0x00c00000
++#define FLD_RANGE 0x00300000
++/* Reserved [19] */
++#define FLD_PEAK_EN 0x00040000
++#define FLD_PEAK_SEL 0x00030000
++#define FLD_CNTRST 0x0000ff00
++#define FLD_BRITE 0x000000ff
++
++/*****************************************************************************/
++#define HSCALE_CTRL 0x418
++#define FLD_HFILT 0x03000000
++#define FLD_HSCALE 0x00ffffff
++
++/*****************************************************************************/
++#define VSCALE_CTRL 0x41c
++#define FLD_LINE_AVG_DIS 0x01000000
++/* Reserved [23:20] */
++#define FLD_VS_INTRLACE 0x00080000
++#define FLD_VFILT 0x00070000
++/* Reserved [15:13] */
++#define FLD_VSCALE 0x00001fff
++
++/*****************************************************************************/
++#define CHROMA_CTRL 0x420
++#define USAT_CTRL_BYTE 0x420
++#define VSAT_CTRL_BYTE 0x421
++#define HUE_CTRL_BYTE 0x422
++#define FLD_C_LPF_EN 0x20000000
++#define FLD_CHR_DELAY 0x1c000000
++#define FLD_C_CORE_SEL 0x03000000
++#define FLD_HUE 0x00ff0000
++#define FLD_VSAT 0x0000ff00
++#define FLD_USAT 0x000000ff
++
++/*****************************************************************************/
++#define VBI_LINE_CTRL1 0x424
++#define FLD_VBI_MD_LINE4 0xff000000
++#define FLD_VBI_MD_LINE3 0x00ff0000
++#define FLD_VBI_MD_LINE2 0x0000ff00
++#define FLD_VBI_MD_LINE1 0x000000ff
++
++/*****************************************************************************/
++#define VBI_LINE_CTRL2 0x428
++#define FLD_VBI_MD_LINE8 0xff000000
++#define FLD_VBI_MD_LINE7 0x00ff0000
++#define FLD_VBI_MD_LINE6 0x0000ff00
++#define FLD_VBI_MD_LINE5 0x000000ff
++
++/*****************************************************************************/
++#define VBI_LINE_CTRL3 0x42c
++#define FLD_VBI_MD_LINE12 0xff000000
++#define FLD_VBI_MD_LINE11 0x00ff0000
++#define FLD_VBI_MD_LINE10 0x0000ff00
++#define FLD_VBI_MD_LINE9 0x000000ff
++
++/*****************************************************************************/
++#define VBI_LINE_CTRL4 0x430
++#define FLD_VBI_MD_LINE16 0xff000000
++#define FLD_VBI_MD_LINE15 0x00ff0000
++#define FLD_VBI_MD_LINE14 0x0000ff00
++#define FLD_VBI_MD_LINE13 0x000000ff
++
++/*****************************************************************************/
++#define VBI_LINE_CTRL5 0x434
++#define FLD_VBI_MD_LINE17 0x000000ff
++
++/*****************************************************************************/
++#define VBI_FC_CFG 0x438
++#define FLD_FC_ALT2 0xff000000
++#define FLD_FC_ALT1 0x00ff0000
++#define FLD_FC_ALT2_TYPE 0x0000f000
++#define FLD_FC_ALT1_TYPE 0x00000f00
++/* Reserved [7:1] */
++#define FLD_FC_SEARCH_MODE 0x00000001
++
++/*****************************************************************************/
++#define VBI_MISC_CFG1 0x43c
++#define FLD_TTX_PKTADRU 0xfff00000
++#define FLD_TTX_PKTADRL 0x000fff00
++/* Reserved [7:6] */
++#define FLD_MOJI_PACK_DIS 0x00000020
++#define FLD_VPS_DEC_DIS 0x00000010
++#define FLD_CRI_MARG_SCALE 0x0000000c
++#define FLD_EDGE_RESYNC_EN 0x00000002
++#define FLD_ADAPT_SLICE_DIS 0x00000001
++
++/*****************************************************************************/
++#define VBI_MISC_CFG2 0x440
++#define FLD_HAMMING_TYPE 0x0f000000
++/* Reserved [23:20] */
++#define FLD_WSS_FIFO_RST 0x00080000
++#define FLD_GS2_FIFO_RST 0x00040000
++#define FLD_GS1_FIFO_RST 0x00020000
++#define FLD_CC_FIFO_RST 0x00010000
++/* Reserved [15:12] */
++#define FLD_VBI3_SDID 0x00000f00
++#define FLD_VBI2_SDID 0x000000f0
++#define FLD_VBI1_SDID 0x0000000f
++
++/*****************************************************************************/
++#define VBI_PAY1 0x444
++#define FLD_GS1_FIFO_DAT 0xFF000000
++#define FLD_GS1_STAT 0x00FF0000
++#define FLD_CC_FIFO_DAT 0x0000FF00
++#define FLD_CC_STAT 0x000000FF
++
++/*****************************************************************************/
++#define VBI_PAY2 0x448
++#define FLD_WSS_FIFO_DAT 0xff000000
++#define FLD_WSS_STAT 0x00ff0000
++#define FLD_GS2_FIFO_DAT 0x0000ff00
++#define FLD_GS2_STAT 0x000000ff
++
++/*****************************************************************************/
++#define VBI_CUST1_CFG1 0x44c
++/* Reserved [31] */
++#define FLD_VBI1_CRIWIN 0x7f000000
++#define FLD_VBI1_SLICE_DIST 0x00f00000
++#define FLD_VBI1_BITINC 0x000fff00
++#define FLD_VBI1_HDELAY 0x000000ff
++
++/*****************************************************************************/
++#define VBI_CUST1_CFG2 0x450
++#define FLD_VBI1_FC_LENGTH 0x1f000000
++#define FLD_VBI1_FRAME_CODE 0x00ffffff
++
++/*****************************************************************************/
++#define VBI_CUST1_CFG3 0x454
++#define FLD_VBI1_HAM_EN 0x80000000
++#define FLD_VBI1_FIFO_MODE 0x70000000
++#define FLD_VBI1_FORMAT_TYPE 0x0f000000
++#define FLD_VBI1_PAYLD_LENGTH 0x00ff0000
++#define FLD_VBI1_CRI_LENGTH 0x0000f000
++#define FLD_VBI1_CRI_MARGIN 0x00000f00
++#define FLD_VBI1_CRI_TIME 0x000000ff
++
++/*****************************************************************************/
++#define VBI_CUST2_CFG1 0x458
++/* Reserved [31] */
++#define FLD_VBI2_CRIWIN 0x7f000000
++#define FLD_VBI2_SLICE_DIST 0x00f00000
++#define FLD_VBI2_BITINC 0x000fff00
++#define FLD_VBI2_HDELAY 0x000000ff
++
++/*****************************************************************************/
++#define VBI_CUST2_CFG2 0x45c
++#define FLD_VBI2_FC_LENGTH 0x1f000000
++#define FLD_VBI2_FRAME_CODE 0x00ffffff
++
++/*****************************************************************************/
++#define VBI_CUST2_CFG3 0x460
++#define FLD_VBI2_HAM_EN 0x80000000
++#define FLD_VBI2_FIFO_MODE 0x70000000
++#define FLD_VBI2_FORMAT_TYPE 0x0f000000
++#define FLD_VBI2_PAYLD_LENGTH 0x00ff0000
++#define FLD_VBI2_CRI_LENGTH 0x0000f000
++#define FLD_VBI2_CRI_MARGIN 0x00000f00
++#define FLD_VBI2_CRI_TIME 0x000000ff
++
++/*****************************************************************************/
++#define VBI_CUST3_CFG1 0x464
++/* Reserved [31] */
++#define FLD_VBI3_CRIWIN 0x7f000000
++#define FLD_VBI3_SLICE_DIST 0x00f00000
++#define FLD_VBI3_BITINC 0x000fff00
++#define FLD_VBI3_HDELAY 0x000000ff
++
++/*****************************************************************************/
++#define VBI_CUST3_CFG2 0x468
++#define FLD_VBI3_FC_LENGTH 0x1f000000
++#define FLD_VBI3_FRAME_CODE 0x00ffffff
++
++/*****************************************************************************/
++#define VBI_CUST3_CFG3 0x46c
++#define FLD_VBI3_HAM_EN 0x80000000
++#define FLD_VBI3_FIFO_MODE 0x70000000
++#define FLD_VBI3_FORMAT_TYPE 0x0f000000
++#define FLD_VBI3_PAYLD_LENGTH 0x00ff0000
++#define FLD_VBI3_CRI_LENGTH 0x0000f000
++#define FLD_VBI3_CRI_MARGIN 0x00000f00
++#define FLD_VBI3_CRI_TIME 0x000000ff
++
++/*****************************************************************************/
++#define HORIZ_TIM_CTRL 0x470
++#define FLD_BGDEL_CNT 0xff000000
++/* Reserved [23:22] */
++#define FLD_HACTIVE_CNT 0x003ff000
++/* Reserved [11:10] */
++#define FLD_HBLANK_CNT 0x000003ff
++
++/*****************************************************************************/
++#define VERT_TIM_CTRL 0x474
++#define FLD_V656BLANK_CNT 0xff000000
++/* Reserved [23:22] */
++#define FLD_VACTIVE_CNT 0x003ff000
++/* Reserved [11:10] */
++#define FLD_VBLANK_CNT 0x000003ff
++
++/*****************************************************************************/
++#define SRC_COMB_CFG 0x478
++#define FLD_CCOMB_2LN_CHECK 0x80000000
++#define FLD_CCOMB_3LN_EN 0x40000000
++#define FLD_CCOMB_2LN_EN 0x20000000
++#define FLD_CCOMB_3D_EN 0x10000000
++/* Reserved [27] */
++#define FLD_LCOMB_3LN_EN 0x04000000
++#define FLD_LCOMB_2LN_EN 0x02000000
++#define FLD_LCOMB_3D_EN 0x01000000
++#define FLD_LUMA_LPF_SEL 0x00c00000
++#define FLD_UV_LPF_SEL 0x00300000
++#define FLD_BLEND_SLOPE 0x000f0000
++#define FLD_CCOMB_REDUCE_EN 0x00008000
++/* Reserved [14:10] */
++#define FLD_SRC_DECIM_RATIO 0x000003ff
++
++/*****************************************************************************/
++#define CHROMA_VBIOFF_CFG 0x47c
++#define FLD_VBI_VOFFSET 0x1f000000
++/* Reserved [23:20] */
++#define FLD_SC_STEP 0x000fffff
++
++/*****************************************************************************/
++#define FIELD_COUNT 0x480
++#define FLD_FIELD_COUNT_FLD 0x000003ff
++
++/*****************************************************************************/
++#define MISC_TIM_CTRL 0x484
++#define FLD_DEBOUNCE_COUNT 0xc0000000
++#define FLD_VT_LINE_CNT_HYST 0x30000000
++/* Reserved [27] */
++#define FLD_AFD_STAT 0x07ff0000
++#define FLD_VPRES_VERT_EN 0x00008000
++/* Reserved [14:12] */
++#define FLD_HR32 0x00000800
++#define FLD_TDALGN 0x00000400
++#define FLD_TDFIELD 0x00000200
++/* Reserved [8:6] */
++#define FLD_TEMPDEC 0x0000003f
++
++/*****************************************************************************/
++#define DFE_CTRL1 0x488
++#define FLD_CLAMP_AUTO_EN 0x80000000
++#define FLD_AGC_AUTO_EN 0x40000000
++#define FLD_VGA_CRUSH_EN 0x20000000
++#define FLD_VGA_AUTO_EN 0x10000000
++#define FLD_VBI_GATE_EN 0x08000000
++#define FLD_CLAMP_LEVEL 0x07000000
++/* Reserved [23:22] */
++#define FLD_CLAMP_SKIP_CNT 0x00300000
++#define FLD_AGC_GAIN 0x000fff00
++/* Reserved [7:6] */
++#define FLD_VGA_GAIN 0x0000003f
++
++/*****************************************************************************/
++#define DFE_CTRL2 0x48c
++#define FLD_VGA_ACQUIRE_RANGE 0x00ff0000
++#define FLD_VGA_TRACK_RANGE 0x0000ff00
++#define FLD_VGA_SYNC 0x000000ff
++
++/*****************************************************************************/
++#define DFE_CTRL3 0x490
++#define FLD_BP_PERCENT 0xff000000
++#define FLD_DFT_THRESHOLD 0x00ff0000
++/* Reserved [15:12] */
++#define FLD_SYNC_WIDTH_SEL 0x00000600
++#define FLD_BP_LOOP_GAIN 0x00000300
++#define FLD_SYNC_LOOP_GAIN 0x000000c0
++/* Reserved [5:4] */
++#define FLD_AGC_LOOP_GAIN 0x0000000c
++#define FLD_DCC_LOOP_GAIN 0x00000003
++
++/*****************************************************************************/
++#define PLL_CTRL 0x494
++#define FLD_PLL_KD 0xff000000
++#define FLD_PLL_KI 0x00ff0000
++#define FLD_PLL_MAX_OFFSET 0x0000ffff
++
++/*****************************************************************************/
++#define HTL_CTRL 0x498
++/* Reserved [31:24] */
++#define FLD_AUTO_LOCK_SPD 0x00080000
++#define FLD_MAN_FAST_LOCK 0x00040000
++#define FLD_HTL_15K_EN 0x00020000
++#define FLD_HTL_500K_EN 0x00010000
++#define FLD_HTL_KD 0x0000ff00
++#define FLD_HTL_KI 0x000000ff
++
++/*****************************************************************************/
++#define COMB_CTRL 0x49c
++#define FLD_COMB_PHASE_LIMIT 0xff000000
++#define FLD_CCOMB_ERR_LIMIT 0x00ff0000
++#define FLD_LUMA_THRESHOLD 0x0000ff00
++#define FLD_LCOMB_ERR_LIMIT 0x000000ff
++
++/*****************************************************************************/
++#define CRUSH_CTRL 0x4a0
++#define FLD_WTW_EN 0x00400000
++#define FLD_CRUSH_FREQ 0x00200000
++#define FLD_MAJ_SEL_EN 0x00100000
++#define FLD_MAJ_SEL 0x000c0000
++/* Reserved [17:15] */
++#define FLD_SYNC_TIP_REDUCE 0x00007e00
++/* Reserved [8:6] */
++#define FLD_SYNC_TIP_INC 0x0000003f
++
++/*****************************************************************************/
++#define SOFT_RST_CTRL 0x4a4
++#define FLD_VD_SOFT_RST 0x00008000
++/* Reserved [14:12] */
++#define FLD_REG_RST_MSK 0x00000800
++#define FLD_VOF_RST_MSK 0x00000400
++#define FLD_MVDET_RST_MSK 0x00000200
++#define FLD_VBI_RST_MSK 0x00000100
++#define FLD_SCALE_RST_MSK 0x00000080
++#define FLD_CHROMA_RST_MSK 0x00000040
++#define FLD_LUMA_RST_MSK 0x00000020
++#define FLD_VTG_RST_MSK 0x00000010
++#define FLD_YCSEP_RST_MSK 0x00000008
++#define FLD_SRC_RST_MSK 0x00000004
++#define FLD_DFE_RST_MSK 0x00000002
++/* Reserved [0] */
++
++/*****************************************************************************/
++#define MV_DT_CTRL1 0x4a8
++/* Reserved [31:29] */
++#define FLD_PSP_STOP_LINE 0x1f000000
++/* Reserved [23:21] */
++#define FLD_PSP_STRT_LINE 0x001f0000
++/* Reserved [15] */
++#define FLD_PSP_LLIMW 0x00007f00
++/* Reserved [7] */
++#define FLD_PSP_ULIMW 0x0000007f
++
++/*****************************************************************************/
++#define MV_DT_CTRL2 0x4aC
++#define FLD_CS_STOPWIN 0xff000000
++#define FLD_CS_STRTWIN 0x00ff0000
++#define FLD_CS_WIDTH 0x0000ff00
++#define FLD_PSP_SPEC_VAL 0x000000ff
++
++/*****************************************************************************/
++#define MV_DT_CTRL3 0x4B0
++#define FLD_AUTO_RATE_DIS 0x80000000
++#define FLD_HLOCK_DIS 0x40000000
++#define FLD_SEL_FIELD_CNT 0x20000000
++#define FLD_CS_TYPE2_SEL 0x10000000
++#define FLD_CS_LINE_THRSH_SEL 0x08000000
++#define FLD_CS_ATHRESH_SEL 0x04000000
++#define FLD_PSP_SPEC_SEL 0x02000000
++#define FLD_PSP_LINES_SEL 0x01000000
++#define FLD_FIELD_CNT 0x00f00000
++#define FLD_CS_TYPE2_CNT 0x000fc000
++#define FLD_CS_LINE_CNT 0x00003f00
++#define FLD_CS_ATHRESH_LEV 0x000000ff
++
++/*****************************************************************************/
++#define CHIP_VERSION 0x4b4
++/* Cx231xx redefine */
++#define VERSION 0x4b4
++#define FLD_REV_ID 0x000000ff
++
++/*****************************************************************************/
++#define MISC_DIAG_CTRL 0x4b8
++/* Reserved [31:24] */
++#define FLD_SC_CONVERGE_THRESH 0x00ff0000
++#define FLD_CCOMB_ERR_LIMIT_3D 0x0000ff00
++#define FLD_LCOMB_ERR_LIMIT_3D 0x000000ff
++
++/*****************************************************************************/
++#define VBI_PASS_CTRL 0x4bc
++#define FLD_VBI_PASS_MD 0x00200000
++#define FLD_VBI_SETUP_DIS 0x00100000
++#define FLD_PASS_LINE_CTRL 0x000fffff
++
++/*****************************************************************************/
++/* Cx231xx redefine */
++#define VCR_DET_CTRL 0x4c0
++#define FLD_EN_FIELD_PHASE_DET 0x80000000
++#define FLD_EN_HEAD_SW_DET 0x40000000
++#define FLD_FIELD_PHASE_LENGTH 0x01ff0000
++/* Reserved [29:25] */
++#define FLD_FIELD_PHASE_DELAY 0x0000ff00
++#define FLD_FIELD_PHASE_LIMIT 0x000000f0
++#define FLD_HEAD_SW_DET_LIMIT 0x0000000f
++
++/*****************************************************************************/
++#define DL_CTL 0x800
++#define DL_CTL_ADDRESS_LOW 0x800 /* Byte 1 in DL_CTL */
++#define DL_CTL_ADDRESS_HIGH 0x801 /* Byte 2 in DL_CTL */
++#define DL_CTL_DATA 0x802 /* Byte 3 in DL_CTL */
++#define DL_CTL_CONTROL 0x803 /* Byte 4 in DL_CTL */
++/* Reserved [31:5] */
++#define FLD_START_8051 0x10000000
++#define FLD_DL_ENABLE 0x08000000
++#define FLD_DL_AUTO_INC 0x04000000
++#define FLD_DL_MAP 0x03000000
++
++/*****************************************************************************/
++#define STD_DET_STATUS 0x804
++#define FLD_SPARE_STATUS1 0xff000000
++#define FLD_SPARE_STATUS0 0x00ff0000
++#define FLD_MOD_DET_STATUS1 0x0000ff00
++#define FLD_MOD_DET_STATUS0 0x000000ff
++
++/*****************************************************************************/
++#define AUD_BUILD_NUM 0x806
++#define AUD_VER_NUM 0x807
++#define STD_DET_CTL 0x808
++#define STD_DET_CTL_AUD_CTL 0x808 /* Byte 1 in STD_DET_CTL */
++#define STD_DET_CTL_PREF_MODE 0x809 /* Byte 2 in STD_DET_CTL */
++#define FLD_SPARE_CTL0 0xff000000
++#define FLD_DIS_DBX 0x00800000
++#define FLD_DIS_BTSC 0x00400000
++#define FLD_DIS_NICAM_A2 0x00200000
++#define FLD_VIDEO_PRESENT 0x00100000
++#define FLD_DW8051_VIDEO_FORMAT 0x000f0000
++#define FLD_PREF_DEC_MODE 0x0000ff00
++#define FLD_AUD_CONFIG 0x000000ff
++
++/*****************************************************************************/
++#define DW8051_INT 0x80c
++#define FLD_VIDEO_PRESENT_CHANGE 0x80000000
++#define FLD_VIDEO_CHANGE 0x40000000
++#define FLD_RDS_READY 0x20000000
++#define FLD_AC97_INT 0x10000000
++#define FLD_NICAM_BIT_ERROR_TOO_HIGH 0x08000000
++#define FLD_NICAM_LOCK 0x04000000
++#define FLD_NICAM_UNLOCK 0x02000000
++#define FLD_DFT4_TH_CMP 0x01000000
++/* Reserved [23:22] */
++#define FLD_LOCK_IND_INT 0x00200000
++#define FLD_DFT3_TH_CMP 0x00100000
++#define FLD_DFT2_TH_CMP 0x00080000
++#define FLD_DFT1_TH_CMP 0x00040000
++#define FLD_FM2_DFT_TH_CMP 0x00020000
++#define FLD_FM1_DFT_TH_CMP 0x00010000
++#define FLD_VIDEO_PRESENT_EN 0x00008000
++#define FLD_VIDEO_CHANGE_EN 0x00004000
++#define FLD_RDS_READY_EN 0x00002000
++#define FLD_AC97_INT_EN 0x00001000
++#define FLD_NICAM_BIT_ERROR_TOO_HIGH_EN 0x00000800
++#define FLD_NICAM_LOCK_EN 0x00000400
++#define FLD_NICAM_UNLOCK_EN 0x00000200
++#define FLD_DFT4_TH_CMP_EN 0x00000100
++/* Reserved [7] */
++#define FLD_DW8051_INT6_CTL1 0x00000040
++#define FLD_DW8051_INT5_CTL1 0x00000020
++#define FLD_DW8051_INT4_CTL1 0x00000010
++#define FLD_DW8051_INT3_CTL1 0x00000008
++#define FLD_DW8051_INT2_CTL1 0x00000004
++#define FLD_DW8051_INT1_CTL1 0x00000002
++#define FLD_DW8051_INT0_CTL1 0x00000001
++
++/*****************************************************************************/
++#define GENERAL_CTL 0x810
++#define FLD_RDS_INT 0x80000000
++#define FLD_NBER_INT 0x40000000
++#define FLD_NLL_INT 0x20000000
++#define FLD_IFL_INT 0x10000000
++#define FLD_FDL_INT 0x08000000
++#define FLD_AFC_INT 0x04000000
++#define FLD_AMC_INT 0x02000000
++#define FLD_AC97_INT_CTL 0x01000000
++#define FLD_RDS_INT_DIS 0x00800000
++#define FLD_NBER_INT_DIS 0x00400000
++#define FLD_NLL_INT_DIS 0x00200000
++#define FLD_IFL_INT_DIS 0x00100000
++#define FLD_FDL_INT_DIS 0x00080000
++#define FLD_FC_INT_DIS 0x00040000
++#define FLD_AMC_INT_DIS 0x00020000
++#define FLD_AC97_INT_DIS 0x00010000
++#define FLD_REV_NUM 0x0000ff00
++/* Reserved [7:5] */
++#define FLD_DBX_SOFT_RESET_REG 0x00000010
++#define FLD_AD_SOFT_RESET_REG 0x00000008
++#define FLD_SRC_SOFT_RESET_REG 0x00000004
++#define FLD_CDMOD_SOFT_RESET 0x00000002
++#define FLD_8051_SOFT_RESET 0x00000001
++
++/*****************************************************************************/
++#define AAGC_CTL 0x814
++#define FLD_AFE_12DB_EN 0x80000000
++#define FLD_AAGC_DEFAULT_EN 0x40000000
++#define FLD_AAGC_DEFAULT 0x3f000000
++/* Reserved [23] */
++#define FLD_AAGC_GAIN 0x00600000
++#define FLD_AAGC_TH 0x001f0000
++/* Reserved [15:14] */
++#define FLD_AAGC_HYST2 0x00003f00
++/* Reserved [7:6] */
++#define FLD_AAGC_HYST1 0x0000003f
++
++/*****************************************************************************/
++#define IF_SRC_CTL 0x818
++#define FLD_DBX_BYPASS 0x80000000
++/* Reserved [30:25] */
++#define FLD_IF_SRC_MODE 0x01000000
++/* Reserved [23:18] */
++#define FLD_IF_SRC_PHASE_INC 0x0001ffff
++
++/*****************************************************************************/
++#define ANALOG_DEMOD_CTL 0x81c
++#define FLD_ROT1_PHACC_PROG 0xffff0000
++/* Reserved [15] */
++#define FLD_FM1_DELAY_FIX 0x00007000
++#define FLD_PDF4_SHIFT 0x00000c00
++#define FLD_PDF3_SHIFT 0x00000300
++#define FLD_PDF2_SHIFT 0x000000c0
++#define FLD_PDF1_SHIFT 0x00000030
++#define FLD_FMBYPASS_MODE2 0x00000008
++#define FLD_FMBYPASS_MODE1 0x00000004
++#define FLD_NICAM_MODE 0x00000002
++#define FLD_BTSC_FMRADIO_MODE 0x00000001
++
++/*****************************************************************************/
++#define ROT_FREQ_CTL 0x820
++#define FLD_ROT3_PHACC_PROG 0xffff0000
++#define FLD_ROT2_PHACC_PROG 0x0000ffff
++
++/*****************************************************************************/
++#define FM_CTL 0x824
++#define FLD_FM2_DC_FB_SHIFT 0xf0000000
++#define FLD_FM2_DC_INT_SHIFT 0x0f000000
++#define FLD_FM2_AFC_RESET 0x00800000
++#define FLD_FM2_DC_PASS_IN 0x00400000
++#define FLD_FM2_DAGC_SHIFT 0x00380000
++#define FLD_FM2_CORDIC_SHIFT 0x00070000
++#define FLD_FM1_DC_FB_SHIFT 0x0000f000
++#define FLD_FM1_DC_INT_SHIFT 0x00000f00
++#define FLD_FM1_AFC_RESET 0x00000080
++#define FLD_FM1_DC_PASS_IN 0x00000040
++#define FLD_FM1_DAGC_SHIFT 0x00000038
++#define FLD_FM1_CORDIC_SHIFT 0x00000007
++
++/*****************************************************************************/
++#define LPF_PDF_CTL 0x828
++/* Reserved [31:30] */
++#define FLD_LPF32_SHIFT1 0x30000000
++#define FLD_LPF32_SHIFT2 0x0c000000
++#define FLD_LPF160_SHIFTA 0x03000000
++#define FLD_LPF160_SHIFTB 0x00c00000
++#define FLD_LPF160_SHIFTC 0x00300000
++#define FLD_LPF32_COEF_SEL2 0x000c0000
++#define FLD_LPF32_COEF_SEL1 0x00030000
++#define FLD_LPF160_COEF_SELC 0x0000c000
++#define FLD_LPF160_COEF_SELB 0x00003000
++#define FLD_LPF160_COEF_SELA 0x00000c00
++#define FLD_LPF160_IN_EN_REG 0x00000300
++#define FLD_PDF4_PDF_SEL 0x000000c0
++#define FLD_PDF3_PDF_SEL 0x00000030
++#define FLD_PDF2_PDF_SEL 0x0000000c
++#define FLD_PDF1_PDF_SEL 0x00000003
++
++/*****************************************************************************/
++#define DFT1_CTL1 0x82c
++#define FLD_DFT1_DWELL 0xffff0000
++#define FLD_DFT1_FREQ 0x0000ffff
++
++/*****************************************************************************/
++#define DFT1_CTL2 0x830
++#define FLD_DFT1_THRESHOLD 0xffffff00
++#define FLD_DFT1_CMP_CTL 0x00000080
++#define FLD_DFT1_AVG 0x00000070
++/* Reserved [3:1] */
++#define FLD_DFT1_START 0x00000001
++
++/*****************************************************************************/
++#define DFT1_STATUS 0x834
++#define FLD_DFT1_DONE 0x80000000
++#define FLD_DFT1_TH_CMP_STAT 0x40000000
++#define FLD_DFT1_RESULT 0x3fffffff
++
++/*****************************************************************************/
++#define DFT2_CTL1 0x838
++#define FLD_DFT2_DWELL 0xffff0000
++#define FLD_DFT2_FREQ 0x0000ffff
++
++/*****************************************************************************/
++#define DFT2_CTL2 0x83C
++#define FLD_DFT2_THRESHOLD 0xffffff00
++#define FLD_DFT2_CMP_CTL 0x00000080
++#define FLD_DFT2_AVG 0x00000070
++/* Reserved [3:1] */
++#define FLD_DFT2_START 0x00000001
++
++/*****************************************************************************/
++#define DFT2_STATUS 0x840
++#define FLD_DFT2_DONE 0x80000000
++#define FLD_DFT2_TH_CMP_STAT 0x40000000
++#define FLD_DFT2_RESULT 0x3fffffff
++
++/*****************************************************************************/
++#define DFT3_CTL1 0x844
++#define FLD_DFT3_DWELL 0xffff0000
++#define FLD_DFT3_FREQ 0x0000ffff
++
++/*****************************************************************************/
++#define DFT3_CTL2 0x848
++#define FLD_DFT3_THRESHOLD 0xffffff00
++#define FLD_DFT3_CMP_CTL 0x00000080
++#define FLD_DFT3_AVG 0x00000070
++/* Reserved [3:1] */
++#define FLD_DFT3_START 0x00000001
++
++/*****************************************************************************/
++#define DFT3_STATUS 0x84c
++#define FLD_DFT3_DONE 0x80000000
++#define FLD_DFT3_TH_CMP_STAT 0x40000000
++#define FLD_DFT3_RESULT 0x3fffffff
++
++/*****************************************************************************/
++#define DFT4_CTL1 0x850
++#define FLD_DFT4_DWELL 0xffff0000
++#define FLD_DFT4_FREQ 0x0000ffff
++
++/*****************************************************************************/
++#define DFT4_CTL2 0x854
++#define FLD_DFT4_THRESHOLD 0xffffff00
++#define FLD_DFT4_CMP_CTL 0x00000080
++#define FLD_DFT4_AVG 0x00000070
++/* Reserved [3:1] */
++#define FLD_DFT4_START 0x00000001
++
++/*****************************************************************************/
++#define DFT4_STATUS 0x858
++#define FLD_DFT4_DONE 0x80000000
++#define FLD_DFT4_TH_CMP_STAT 0x40000000
++#define FLD_DFT4_RESULT 0x3fffffff
++
++/*****************************************************************************/
++#define AM_MTS_DET 0x85c
++#define FLD_AM_MTS_MODE 0x80000000
++/* Reserved [30:26] */
++#define FLD_AM_SUB 0x02000000
++#define FLD_AM_GAIN_EN 0x01000000
++/* Reserved [23:16] */
++#define FLD_AMMTS_GAIN_SCALE 0x0000e000
++#define FLD_MTS_PDF_SHIFT 0x00001800
++#define FLD_AM_REG_GAIN 0x00000700
++#define FLD_AGC_REF 0x000000ff
++
++/*****************************************************************************/
++#define ANALOG_MUX_CTL 0x860
++/* Reserved [31:29] */
++#define FLD_MUX21_SEL 0x10000000
++#define FLD_MUX20_SEL 0x08000000
++#define FLD_MUX19_SEL 0x04000000
++#define FLD_MUX18_SEL 0x02000000
++#define FLD_MUX17_SEL 0x01000000
++#define FLD_MUX16_SEL 0x00800000
++#define FLD_MUX15_SEL 0x00400000
++#define FLD_MUX14_SEL 0x00300000
++#define FLD_MUX13_SEL 0x000C0000
++#define FLD_MUX12_SEL 0x00020000
++#define FLD_MUX11_SEL 0x00018000
++#define FLD_MUX10_SEL 0x00004000
++#define FLD_MUX9_SEL 0x00002000
++#define FLD_MUX8_SEL 0x00001000
++#define FLD_MUX7_SEL 0x00000800
++#define FLD_MUX6_SEL 0x00000600
++#define FLD_MUX5_SEL 0x00000100
++#define FLD_MUX4_SEL 0x000000c0
++#define FLD_MUX3_SEL 0x00000030
++#define FLD_MUX2_SEL 0x0000000c
++#define FLD_MUX1_SEL 0x00000003
++
++/*****************************************************************************/
++/* Cx231xx redefine */
++#define DPLL_CTRL1 0x864
++#define DIG_PLL_CTL1 0x864
++
++#define FLD_PLL_STATUS 0x07000000
++#define FLD_BANDWIDTH_SELECT 0x00030000
++#define FLD_PLL_SHIFT_REG 0x00007000
++#define FLD_PHASE_SHIFT 0x000007ff
++
++/*****************************************************************************/
++/* Cx231xx redefine */
++#define DPLL_CTRL2 0x868
++#define DIG_PLL_CTL2 0x868
++#define FLD_PLL_UNLOCK_THR 0xff000000
++#define FLD_PLL_LOCK_THR 0x00ff0000
++/* Reserved [15:8] */
++#define FLD_AM_PDF_SEL2 0x000000c0
++#define FLD_AM_PDF_SEL1 0x00000030
++#define FLD_DPLL_FSM_CTRL 0x0000000c
++/* Reserved [1] */
++#define FLD_PLL_PILOT_DET 0x00000001
++
++/*****************************************************************************/
++/* Cx231xx redefine */
++#define DPLL_CTRL3 0x86c
++#define DIG_PLL_CTL3 0x86c
++#define FLD_DISABLE_LOOP 0x01000000
++#define FLD_A1_DS1_SEL 0x000c0000
++#define FLD_A1_DS2_SEL 0x00030000
++#define FLD_A1_KI 0x0000ff00
++#define FLD_A1_KD 0x000000ff
++
++/*****************************************************************************/
++/* Cx231xx redefine */
++#define DPLL_CTRL4 0x870
++#define DIG_PLL_CTL4 0x870
++#define FLD_A2_DS1_SEL 0x000c0000
++#define FLD_A2_DS2_SEL 0x00030000
++#define FLD_A2_KI 0x0000ff00
++#define FLD_A2_KD 0x000000ff
++
++/*****************************************************************************/
++/* Cx231xx redefine */
++#define DPLL_CTRL5 0x874
++#define DIG_PLL_CTL5 0x874
++#define FLD_TRK_DS1_SEL 0x000c0000
++#define FLD_TRK_DS2_SEL 0x00030000
++#define FLD_TRK_KI 0x0000ff00
++#define FLD_TRK_KD 0x000000ff
++
++/*****************************************************************************/
++#define DEEMPH_GAIN_CTL 0x878
++#define FLD_DEEMPH2_GAIN 0xFFFF0000
++#define FLD_DEEMPH1_GAIN 0x0000FFFF
++
++/*****************************************************************************/
++/* Cx231xx redefine */
++#define DEEMPH_COEFF1 0x87c
++#define DEEMPH_COEF1 0x87c
++#define FLD_DEEMPH_B0 0xffff0000
++#define FLD_DEEMPH_A0 0x0000ffff
++
++/*****************************************************************************/
++/* Cx231xx redefine */
++#define DEEMPH_COEFF2 0x880
++#define DEEMPH_COEF2 0x880
++#define FLD_DEEMPH_B1 0xFFFF0000
++#define FLD_DEEMPH_A1 0x0000FFFF
++
++/*****************************************************************************/
++#define DBX1_CTL1 0x884
++#define FLD_DBX1_WBE_GAIN 0xffff0000
++#define FLD_DBX1_IN_GAIN 0x0000ffff
++
++/*****************************************************************************/
++#define DBX1_CTL2 0x888
++#define FLD_DBX1_SE_BYPASS 0xffff0000
++#define FLD_DBX1_SE_GAIN 0x0000ffff
++
++/*****************************************************************************/
++#define DBX1_RMS_SE 0x88C
++#define FLD_DBX1_RMS_WBE 0xffff0000
++#define FLD_DBX1_RMS_SE_FLD 0x0000ffff
++
++/*****************************************************************************/
++#define DBX2_CTL1 0x890
++#define FLD_DBX2_WBE_GAIN 0xffff0000
++#define FLD_DBX2_IN_GAIN 0x0000ffff
++
++/*****************************************************************************/
++#define DBX2_CTL2 0x894
++#define FLD_DBX2_SE_BYPASS 0xffff0000
++#define FLD_DBX2_SE_GAIN 0x0000ffff
++
++/*****************************************************************************/
++#define DBX2_RMS_SE 0x898
++#define FLD_DBX2_RMS_WBE 0xffff0000
++#define FLD_DBX2_RMS_SE_FLD 0x0000ffff
++
++/*****************************************************************************/
++#define AM_FM_DIFF 0x89c
++/* Reserved [31] */
++#define FLD_FM_DIFF_OUT 0x7fff0000
++/* Reserved [15] */
++#define FLD_AM_DIFF_OUT 0x00007fff
++
++/*****************************************************************************/
++#define NICAM_FAW 0x8a0
++#define FLD_FAWDETWINEND 0xFc000000
++#define FLD_FAWDETWINSTR 0x03ff0000
++/* Reserved [15:12] */
++#define FLD_FAWDETTHRSHLD3 0x00000f00
++#define FLD_FAWDETTHRSHLD2 0x000000f0
++#define FLD_FAWDETTHRSHLD1 0x0000000f
++
++/*****************************************************************************/
++/* Cx231xx redefine */
++#define DEEMPH_GAIN 0x8a4
++#define NICAM_DEEMPHGAIN 0x8a4
++/* Reserved [31:18] */
++#define FLD_DEEMPHGAIN 0x0003ffff
++
++/*****************************************************************************/
++/* Cx231xx redefine */
++#define DEEMPH_NUMER1 0x8a8
++#define NICAM_DEEMPHNUMER1 0x8a8
++/* Reserved [31:18] */
++#define FLD_DEEMPHNUMER1 0x0003ffff
++
++/*****************************************************************************/
++/* Cx231xx redefine */
++#define DEEMPH_NUMER2 0x8ac
++#define NICAM_DEEMPHNUMER2 0x8ac
++/* Reserved [31:18] */
++#define FLD_DEEMPHNUMER2 0x0003ffff
++
++/*****************************************************************************/
++/* Cx231xx redefine */
++#define DEEMPH_DENOM1 0x8b0
++#define NICAM_DEEMPHDENOM1 0x8b0
++/* Reserved [31:18] */
++#define FLD_DEEMPHDENOM1 0x0003ffff
++
++/*****************************************************************************/
++/* Cx231xx redefine */
++#define DEEMPH_DENOM2 0x8b4
++#define NICAM_DEEMPHDENOM2 0x8b4
++/* Reserved [31:18] */
++#define FLD_DEEMPHDENOM2 0x0003ffff
++
++/*****************************************************************************/
++#define NICAM_ERRLOG_CTL1 0x8B8
++/* Reserved [31:28] */
++#define FLD_ERRINTRPTTHSHLD1 0x0fff0000
++/* Reserved [15:12] */
++#define FLD_ERRLOGPERIOD 0x00000fff
++
++/*****************************************************************************/
++#define NICAM_ERRLOG_CTL2 0x8bc
++/* Reserved [31:28] */
++#define FLD_ERRINTRPTTHSHLD3 0x0fff0000
++/* Reserved [15:12] */
++#define FLD_ERRINTRPTTHSHLD2 0x00000fff
++
++/*****************************************************************************/
++#define NICAM_ERRLOG_STS1 0x8c0
++/* Reserved [31:28] */
++#define FLD_ERRLOG2 0x0fff0000
++/* Reserved [15:12] */
++#define FLD_ERRLOG1 0x00000fff
++
++/*****************************************************************************/
++#define NICAM_ERRLOG_STS2 0x8c4
++/* Reserved [31:12] */
++#define FLD_ERRLOG3 0x00000fff
++
++/*****************************************************************************/
++#define NICAM_STATUS 0x8c8
++/* Reserved [31:20] */
++#define FLD_NICAM_CIB 0x000c0000
++#define FLD_NICAM_LOCK_STAT 0x00020000
++#define FLD_NICAM_MUTE 0x00010000
++#define FLD_NICAMADDIT_DATA 0x0000ffe0
++#define FLD_NICAMCNTRL 0x0000001f
++
++/*****************************************************************************/
++#define DEMATRIX_CTL 0x8cc
++#define FLD_AC97_IN_SHIFT 0xf0000000
++#define FLD_I2S_IN_SHIFT 0x0f000000
++#define FLD_DEMATRIX_SEL_CTL 0x00ff0000
++/* Reserved [15:11] */
++#define FLD_DMTRX_BYPASS 0x00000400
++#define FLD_DEMATRIX_MODE 0x00000300
++/* Reserved [7:6] */
++#define FLD_PH_DBX_SEL 0x00000020
++#define FLD_PH_CH_SEL 0x00000010
++#define FLD_PHASE_FIX 0x0000000f
++
++/*****************************************************************************/
++#define PATH1_CTL1 0x8d0
++/* Reserved [31:29] */
++#define FLD_PATH1_MUTE_CTL 0x1f000000
++/* Reserved [23:22] */
++#define FLD_PATH1_AVC_CG 0x00300000
++#define FLD_PATH1_AVC_RT 0x000f0000
++#define FLD_PATH1_AVC_AT 0x0000f000
++#define FLD_PATH1_AVC_STEREO 0x00000800
++#define FLD_PATH1_AVC_CR 0x00000700
++#define FLD_PATH1_AVC_RMS_CON 0x000000f0
++#define FLD_PATH1_SEL_CTL 0x0000000f
++
++/*****************************************************************************/
++#define PATH1_VOL_CTL 0x8d4
++#define FLD_PATH1_AVC_THRESHOLD 0x7fff0000
++#define FLD_PATH1_BAL_LEFT 0x00008000
++#define FLD_PATH1_BAL_LEVEL 0x00007f00
++#define FLD_PATH1_VOLUME 0x000000ff
++
++/*****************************************************************************/
++#define PATH1_EQ_CTL 0x8d8
++/* Reserved [31:30] */
++#define FLD_PATH1_EQ_TREBLE_VOL 0x3f000000
++/* Reserved [23:22] */
++#define FLD_PATH1_EQ_MID_VOL 0x003f0000
++/* Reserved [15:14] */
++#define FLD_PATH1_EQ_BASS_VOL 0x00003f00
++/* Reserved [7:1] */
++#define FLD_PATH1_EQ_BAND_SEL 0x00000001
++
++/*****************************************************************************/
++#define PATH1_SC_CTL 0x8dc
++#define FLD_PATH1_SC_THRESHOLD 0x7fff0000
++#define FLD_PATH1_SC_RT 0x0000f000
++#define FLD_PATH1_SC_AT 0x00000f00
++#define FLD_PATH1_SC_STEREO 0x00000080
++#define FLD_PATH1_SC_CR 0x00000070
++#define FLD_PATH1_SC_RMS_CON 0x0000000f
++
++/*****************************************************************************/
++#define PATH2_CTL1 0x8e0
++/* Reserved [31:26] */
++#define FLD_PATH2_MUTE_CTL 0x03000000
++/* Reserved [23:22] */
++#define FLD_PATH2_AVC_CG 0x00300000
++#define FLD_PATH2_AVC_RT 0x000f0000
++#define FLD_PATH2_AVC_AT 0x0000f000
++#define FLD_PATH2_AVC_STEREO 0x00000800
++#define FLD_PATH2_AVC_CR 0x00000700
++#define FLD_PATH2_AVC_RMS_CON 0x000000f0
++#define FLD_PATH2_SEL_CTL 0x0000000f
++
++/*****************************************************************************/
++#define PATH2_VOL_CTL 0x8e4
++#define FLD_PATH2_AVC_THRESHOLD 0xffff0000
++#define FLD_PATH2_BAL_LEFT 0x00008000
++#define FLD_PATH2_BAL_LEVEL 0x00007f00
++#define FLD_PATH2_VOLUME 0x000000ff
++
++/*****************************************************************************/
++#define PATH2_EQ_CTL 0x8e8
++/* Reserved [31:30] */
++#define FLD_PATH2_EQ_TREBLE_VOL 0x3f000000
++/* Reserved [23:22] */
++#define FLD_PATH2_EQ_MID_VOL 0x003f0000
++/* Reserved [15:14] */
++#define FLD_PATH2_EQ_BASS_VOL 0x00003f00
++/* Reserved [7:1] */
++#define FLD_PATH2_EQ_BAND_SEL 0x00000001
++
++/*****************************************************************************/
++#define PATH2_SC_CTL 0x8eC
++#define FLD_PATH2_SC_THRESHOLD 0xffff0000
++#define FLD_PATH2_SC_RT 0x0000f000
++#define FLD_PATH2_SC_AT 0x00000f00
++#define FLD_PATH2_SC_STEREO 0x00000080
++#define FLD_PATH2_SC_CR 0x00000070
++#define FLD_PATH2_SC_RMS_CON 0x0000000f
++
++/*****************************************************************************/
++#define SRC_CTL 0x8f0
++#define FLD_SRC_STATUS 0xffffff00
++#define FLD_FIFO_LF_EN 0x000000fc
++#define FLD_BYPASS_LI 0x00000002
++#define FLD_BYPASS_PF 0x00000001
++
++/*****************************************************************************/
++#define SRC_LF_COEF 0x8f4
++#define FLD_LOOP_FILTER_COEF2 0xffff0000
++#define FLD_LOOP_FILTER_COEF1 0x0000ffff
++
++/*****************************************************************************/
++#define SRC1_CTL 0x8f8
++/* Reserved [31:28] */
++#define FLD_SRC1_FIFO_RD_TH 0x0f000000
++/* Reserved [23:18] */
++#define FLD_SRC1_PHASE_INC 0x0003ffff
++
++/*****************************************************************************/
++#define SRC2_CTL 0x8fc
++/* Reserved [31:28] */
++#define FLD_SRC2_FIFO_RD_TH 0x0f000000
++/* Reserved [23:18] */
++#define FLD_SRC2_PHASE_INC 0x0003ffff
++
++/*****************************************************************************/
++#define SRC3_CTL 0x900
++/* Reserved [31:28] */
++#define FLD_SRC3_FIFO_RD_TH 0x0f000000
++/* Reserved [23:18] */
++#define FLD_SRC3_PHASE_INC 0x0003ffff
++
++/*****************************************************************************/
++#define SRC4_CTL 0x904
++/* Reserved [31:28] */
++#define FLD_SRC4_FIFO_RD_TH 0x0f000000
++/* Reserved [23:18] */
++#define FLD_SRC4_PHASE_INC 0x0003ffff
++
++/*****************************************************************************/
++#define SRC5_CTL 0x908
++/* Reserved [31:28] */
++#define FLD_SRC5_FIFO_RD_TH 0x0f000000
++/* Reserved [23:18] */
++#define FLD_SRC5_PHASE_INC 0x0003ffff
++
++/*****************************************************************************/
++#define SRC6_CTL 0x90c
++/* Reserved [31:28] */
++#define FLD_SRC6_FIFO_RD_TH 0x0f000000
++/* Reserved [23:18] */
++#define FLD_SRC6_PHASE_INC 0x0003ffff
++
++/*****************************************************************************/
++#define BAND_OUT_SEL 0x910
++#define FLD_SRC6_IN_SEL 0xc0000000
++#define FLD_SRC6_CLK_SEL 0x30000000
++#define FLD_SRC5_IN_SEL 0x0c000000
++#define FLD_SRC5_CLK_SEL 0x03000000
++#define FLD_SRC4_IN_SEL 0x00c00000
++#define FLD_SRC4_CLK_SEL 0x00300000
++#define FLD_SRC3_IN_SEL 0x000c0000
++#define FLD_SRC3_CLK_SEL 0x00030000
++#define FLD_BASEBAND_BYPASS_CTL 0x0000ff00
++#define FLD_AC97_SRC_SEL 0x000000c0
++#define FLD_I2S_SRC_SEL 0x00000030
++#define FLD_PARALLEL2_SRC_SEL 0x0000000c
++#define FLD_PARALLEL1_SRC_SEL 0x00000003
++
++/*****************************************************************************/
++#define I2S_IN_CTL 0x914
++/* Reserved [31:11] */
++#define FLD_I2S_UP2X_BW20K 0x00000400
++#define FLD_I2S_UP2X_BYPASS 0x00000200
++#define FLD_I2S_IN_MASTER_MODE 0x00000100
++#define FLD_I2S_IN_SONY_MODE 0x00000080
++#define FLD_I2S_IN_RIGHT_JUST 0x00000040
++#define FLD_I2S_IN_WS_SEL 0x00000020
++#define FLD_I2S_IN_BCN_DEL 0x0000001f
++
++/*****************************************************************************/
++#define I2S_OUT_CTL 0x918
++/* Reserved [31:17] */
++#define FLD_I2S_OUT_SOFT_RESET_EN 0x00010000
++/* Reserved [15:9] */
++#define FLD_I2S_OUT_MASTER_MODE 0x00000100
++#define FLD_I2S_OUT_SONY_MODE 0x00000080
++#define FLD_I2S_OUT_RIGHT_JUST 0x00000040
++#define FLD_I2S_OUT_WS_SEL 0x00000020
++#define FLD_I2S_OUT_BCN_DEL 0x0000001f
++
++/*****************************************************************************/
++#define AC97_CTL 0x91c
++/* Reserved [31:26] */
++#define FLD_AC97_UP2X_BW20K 0x02000000
++#define FLD_AC97_UP2X_BYPASS 0x01000000
++/* Reserved [23:17] */
++#define FLD_AC97_RST_ACL 0x00010000
++/* Reserved [15:9] */
++#define FLD_AC97_WAKE_UP_SYNC 0x00000100
++/* Reserved [7:1] */
++#define FLD_AC97_SHUTDOWN 0x00000001
++
++/* Cx231xx redefine */
++#define QPSK_IAGC_CTL1 0x94c
++#define QPSK_IAGC_CTL2 0x950
++#define QPSK_FEPR_FREQ 0x954
++#define QPSK_BTL_CTL1 0x958
++#define QPSK_BTL_CTL2 0x95c
++#define QPSK_CTL_CTL1 0x960
++#define QPSK_CTL_CTL2 0x964
++#define QPSK_MF_FAGC_CTL 0x968
++#define QPSK_EQ_CTL 0x96c
++#define QPSK_LOCK_CTL 0x970
++
++/*****************************************************************************/
++#define FM1_DFT_CTL 0x9a8
++#define FLD_FM1_DFT_THRESHOLD 0xffff0000
++/* Reserved [15:8] */
++#define FLD_FM1_DFT_CMP_CTL 0x00000080
++#define FLD_FM1_DFT_AVG 0x00000070
++/* Reserved [3:1] */
++#define FLD_FM1_DFT_START 0x00000001
++
++/*****************************************************************************/
++#define FM1_DFT_STATUS 0x9ac
++#define FLD_FM1_DFT_DONE 0x80000000
++/* Reserved [30:19] */
++#define FLD_FM_DFT_TH_CMP 0x00040000
++#define FLD_FM1_DFT 0x0003ffff
++
++/*****************************************************************************/
++#define FM2_DFT_CTL 0x9b0
++#define FLD_FM2_DFT_THRESHOLD 0xffff0000
++/* Reserved [15:8] */
++#define FLD_FM2_DFT_CMP_CTL 0x00000080
++#define FLD_FM2_DFT_AVG 0x00000070
++/* Reserved [3:1] */
++#define FLD_FM2_DFT_START 0x00000001
++
++/*****************************************************************************/
++#define FM2_DFT_STATUS 0x9b4
++#define FLD_FM2_DFT_DONE 0x80000000
++/* Reserved [30:19] */
++#define FLD_FM2_DFT_TH_CMP_STAT 0x00040000
++#define FLD_FM2_DFT 0x0003ffff
++
++/*****************************************************************************/
++/* Cx231xx redefine */
++#define AAGC_STATUS_REG 0x9b8
++#define AAGC_STATUS 0x9b8
++/* Reserved [31:27] */
++#define FLD_FM2_DAGC_OUT 0x07000000
++/* Reserved [23:19] */
++#define FLD_FM1_DAGC_OUT 0x00070000
++/* Reserved [15:6] */
++#define FLD_AFE_VGA_OUT 0x0000003f
++
++/*****************************************************************************/
++#define MTS_GAIN_STATUS 0x9bc
++/* Reserved [31:14] */
++#define FLD_MTS_GAIN 0x00003fff
++
++#define RDS_OUT 0x9c0
++#define FLD_RDS_Q 0xffff0000
++#define FLD_RDS_I 0x0000ffff
++
++/*****************************************************************************/
++#define AUTOCONFIG_REG 0x9c4
++/* Reserved [31:4] */
++#define FLD_AUTOCONFIG_MODE 0x0000000f
++
++#define FM_AFC 0x9c8
++#define FLD_FM2_AFC 0xffff0000
++#define FLD_FM1_AFC 0x0000ffff
++
++/*****************************************************************************/
++/* Cx231xx redefine */
++#define NEW_SPARE 0x9cc
++#define NEW_SPARE_REG 0x9cc
++
++/*****************************************************************************/
++#define DBX_ADJ 0x9d0
++/* Reserved [31:28] */
++#define FLD_DBX2_ADJ 0x0fff0000
++/* Reserved [15:12] */
++#define FLD_DBX1_ADJ 0x00000fff
++
++#define VID_FMT_AUTO 0
++#define VID_FMT_NTSC_M 1
++#define VID_FMT_NTSC_J 2
++#define VID_FMT_NTSC_443 3
++#define VID_FMT_PAL_BDGHI 4
++#define VID_FMT_PAL_M 5
++#define VID_FMT_PAL_N 6
++#define VID_FMT_PAL_NC 7
++#define VID_FMT_PAL_60 8
++#define VID_FMT_SECAM 12
++#define VID_FMT_SECAM_60 13
++
++#define INPUT_MODE_CVBS_0 0 /* INPUT_MODE_VALUE(0) */
++#define INPUT_MODE_YC_1 1 /* INPUT_MODE_VALUE(1) */
++#define INPUT_MODE_YC2_2 2 /* INPUT_MODE_VALUE(2) */
++#define INPUT_MODE_YUV_3 3 /* INPUT_MODE_VALUE(3) */
++
++#define LUMA_LPF_LOW_BANDPASS 0 /* 0.6Mhz LPF BW */
++#define LUMA_LPF_MEDIUM_BANDPASS 1 /* 1.0Mhz LPF BW */
++#define LUMA_LPF_HIGH_BANDPASS 2 /* 1.5Mhz LPF BW */
++
++#define UV_LPF_LOW_BANDPASS 0 /* 0.6Mhz LPF BW */
++#define UV_LPF_MEDIUM_BANDPASS 1 /* 1.0Mhz LPF BW */
++#define UV_LPF_HIGH_BANDPASS 2 /* 1.5Mhz LPF BW */
++
++#define TWO_TAP_FILT 0
++#define THREE_TAP_FILT 1
++#define FOUR_TAP_FILT 2
++#define FIVE_TAP_FILT 3
++
++#define AUD_CHAN_SRC_PARALLEL 0
++#define AUD_CHAN_SRC_I2S_INPUT 1
++#define AUD_CHAN_SRC_FLATIRON 2
++#define AUD_CHAN_SRC_PARALLEL3 3
++
++#define OUT_MODE_601 0
++#define OUT_MODE_656 1
++#define OUT_MODE_VIP11 2
++#define OUT_MODE_VIP20 3
++
++#define PHASE_INC_49MHZ 0x0df22
++#define PHASE_INC_56MHZ 0x0fa5b
++#define PHASE_INC_28MHZ 0x010000
++
++#endif
+diff --git a/drivers/media/video/cx231xx/cx231xx-vbi.c b/drivers/media/video/cx231xx/cx231xx-vbi.c
+new file mode 100644
+index 0000000..9418052
+--- /dev/null
++++ b/drivers/media/video/cx231xx/cx231xx-vbi.c
+@@ -0,0 +1,701 @@
++/*
++ cx231xx_vbi.c - driver for Conexant Cx23100/101/102 USB video capture devices
++
++ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
++ Based on cx88 driver
++
++ 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 <linux/init.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/bitmap.h>
++#include <linux/usb.h>
++#include <linux/i2c.h>
++#include <linux/version.h>
++#include <linux/mm.h>
++#include <linux/mutex.h>
++
++#include <media/v4l2-common.h>
++#include <media/v4l2-ioctl.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/msp3400.h>
++#include <media/tuner.h>
++
++#include "cx231xx.h"
++#include "cx231xx-vbi.h"
++
++static inline void print_err_status(struct cx231xx *dev, int packet, int status)
++{
++ char *errmsg = "Unknown";
++
++ switch (status) {
++ case -ENOENT:
++ errmsg = "unlinked synchronuously";
++ break;
++ case -ECONNRESET:
++ errmsg = "unlinked asynchronuously";
++ break;
++ case -ENOSR:
++ errmsg = "Buffer error (overrun)";
++ break;
++ case -EPIPE:
++ errmsg = "Stalled (device not responding)";
++ break;
++ case -EOVERFLOW:
++ errmsg = "Babble (bad cable?)";
++ break;
++ case -EPROTO:
++ errmsg = "Bit-stuff error (bad cable?)";
++ break;
++ case -EILSEQ:
++ errmsg = "CRC/Timeout (could be anything)";
++ break;
++ case -ETIME:
++ errmsg = "Device does not respond";
++ break;
++ }
++ if (packet < 0) {
++ cx231xx_err(DRIVER_NAME "URB status %d [%s].\n", status,
++ errmsg);
++ } else {
++ cx231xx_err(DRIVER_NAME "URB packet %d, status %d [%s].\n",
++ packet, status, errmsg);
++ }
++}
++
++/*
++ * Controls the isoc copy of each urb packet
++ */
++static inline int cx231xx_isoc_vbi_copy(struct cx231xx *dev, struct urb *urb)
++{
++ struct cx231xx_buffer *buf;
++ struct cx231xx_dmaqueue *dma_q = urb->context;
++ int rc = 1;
++ unsigned char *p_buffer;
++ u32 bytes_parsed = 0, buffer_size = 0;
++ u8 sav_eav = 0;
++
++ if (!dev)
++ return 0;
++
++ if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
++ return 0;
++
++ if (urb->status < 0) {
++ print_err_status(dev, -1, urb->status);
++ if (urb->status == -ENOENT)
++ return 0;
++ }
++
++ buf = dev->vbi_mode.isoc_ctl.buf;
++
++ /* get buffer pointer and length */
++ p_buffer = urb->transfer_buffer;
++ buffer_size = urb->actual_length;
++
++ if (buffer_size > 0) {
++ bytes_parsed = 0;
++
++ if (dma_q->is_partial_line) {
++ /* Handle the case where we were working on a partial
++ line */
++ sav_eav = dma_q->last_sav;
++ } else {
++ /* Check for a SAV/EAV overlapping the
++ buffer boundary */
++
++ sav_eav = cx231xx_find_boundary_SAV_EAV(p_buffer,
++ dma_q->partial_buf,
++ &bytes_parsed);
++ }
++
++ sav_eav &= 0xF0;
++ /* Get the first line if we have some portion of an SAV/EAV from
++ the last buffer or a partial line */
++ if (sav_eav) {
++ bytes_parsed += cx231xx_get_vbi_line(dev, dma_q,
++ sav_eav, /* SAV/EAV */
++ p_buffer + bytes_parsed, /* p_buffer */
++ buffer_size - bytes_parsed); /* buffer size */
++ }
++
++ /* Now parse data that is completely in this buffer */
++ dma_q->is_partial_line = 0;
++
++ while (bytes_parsed < buffer_size) {
++ u32 bytes_used = 0;
++
++ sav_eav = cx231xx_find_next_SAV_EAV(
++ p_buffer + bytes_parsed, /* p_buffer */
++ buffer_size - bytes_parsed, /* buffer size */
++ &bytes_used); /* bytes used to get SAV/EAV */
++
++ bytes_parsed += bytes_used;
++
++ sav_eav &= 0xF0;
++ if (sav_eav && (bytes_parsed < buffer_size)) {
++ bytes_parsed += cx231xx_get_vbi_line(dev,
++ dma_q, sav_eav, /* SAV/EAV */
++ p_buffer+bytes_parsed, /* p_buffer */
++ buffer_size-bytes_parsed);/*buf size*/
++ }
++ }
++
++ /* Save the last four bytes of the buffer so we can
++ check the buffer boundary condition next time */
++ memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
++ bytes_parsed = 0;
++ }
++
++ return rc;
++}
++
++/* ------------------------------------------------------------------
++ Vbi buf operations
++ ------------------------------------------------------------------*/
++
++static int
++vbi_buffer_setup(struct videobuf_queue *vq, unsigned int *count,
++ unsigned int *size)
++{
++ struct cx231xx_fh *fh = vq->priv_data;
++ struct cx231xx *dev = fh->dev;
++ u32 height = 0;
++
++ height = ((dev->norm & V4L2_STD_625_50) ?
++ PAL_VBI_LINES : NTSC_VBI_LINES);
++
++ *size = (dev->width * height * 2);
++ if (0 == *count)
++ *count = CX231XX_DEF_VBI_BUF;
++
++ if (*count < CX231XX_MIN_BUF)
++ *count = CX231XX_MIN_BUF;
++
++ return 0;
++}
++
++/* This is called *without* dev->slock held; please keep it that way */
++static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf)
++{
++ struct cx231xx_fh *fh = vq->priv_data;
++ struct cx231xx *dev = fh->dev;
++ unsigned long flags = 0;
++ if (in_interrupt())
++ BUG();
++
++ /* We used to wait for the buffer to finish here, but this didn't work
++ because, as we were keeping the state as VIDEOBUF_QUEUED,
++ videobuf_queue_cancel marked it as finished for us.
++ (Also, it could wedge forever if the hardware was misconfigured.)
++
++ This should be safe; by the time we get here, the buffer isn't
++ queued anymore. If we ever start marking the buffers as
++ VIDEOBUF_ACTIVE, it won't be, though.
++ */
++ spin_lock_irqsave(&dev->vbi_mode.slock, flags);
++ if (dev->vbi_mode.isoc_ctl.buf == buf)
++ dev->vbi_mode.isoc_ctl.buf = NULL;
++ spin_unlock_irqrestore(&dev->vbi_mode.slock, flags);
++
++ videobuf_vmalloc_free(&buf->vb);
++ buf->vb.state = VIDEOBUF_NEEDS_INIT;
++}
++
++static int
++vbi_buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
++ enum v4l2_field field)
++{
++ struct cx231xx_fh *fh = vq->priv_data;
++ struct cx231xx_buffer *buf =
++ container_of(vb, struct cx231xx_buffer, vb);
++ struct cx231xx *dev = fh->dev;
++ int rc = 0, urb_init = 0;
++ u32 height = 0;
++
++ height = ((dev->norm & V4L2_STD_625_50) ?
++ PAL_VBI_LINES : NTSC_VBI_LINES);
++ buf->vb.size = ((dev->width << 1) * height);
++
++ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
++ return -EINVAL;
++
++ buf->vb.width = dev->width;
++ buf->vb.height = height;
++ buf->vb.field = field;
++ buf->vb.field = V4L2_FIELD_SEQ_TB;
++
++ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
++ rc = videobuf_iolock(vq, &buf->vb, NULL);
++ if (rc < 0)
++ goto fail;
++ }
++
++ if (!dev->vbi_mode.isoc_ctl.num_bufs)
++ urb_init = 1;
++
++ if (urb_init) {
++ rc = cx231xx_init_vbi_isoc(dev, CX231XX_NUM_VBI_PACKETS,
++ CX231XX_NUM_VBI_BUFS,
++ dev->vbi_mode.alt_max_pkt_size[0],
++ cx231xx_isoc_vbi_copy);
++ if (rc < 0)
++ goto fail;
++ }
++
++ buf->vb.state = VIDEOBUF_PREPARED;
++ return 0;
++
++fail:
++ free_buffer(vq, buf);
++ return rc;
++}
++
++static void
++vbi_buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
++{
++ struct cx231xx_buffer *buf =
++ container_of(vb, struct cx231xx_buffer, vb);
++ struct cx231xx_fh *fh = vq->priv_data;
++ struct cx231xx *dev = fh->dev;
++ struct cx231xx_dmaqueue *vidq = &dev->vbi_mode.vidq;
++
++ buf->vb.state = VIDEOBUF_QUEUED;
++ list_add_tail(&buf->vb.queue, &vidq->active);
++
++}
++
++static void vbi_buffer_release(struct videobuf_queue *vq,
++ struct videobuf_buffer *vb)
++{
++ struct cx231xx_buffer *buf =
++ container_of(vb, struct cx231xx_buffer, vb);
++
++
++ free_buffer(vq, buf);
++}
++
++struct videobuf_queue_ops cx231xx_vbi_qops = {
++ .buf_setup = vbi_buffer_setup,
++ .buf_prepare = vbi_buffer_prepare,
++ .buf_queue = vbi_buffer_queue,
++ .buf_release = vbi_buffer_release,
++};
++
++/* ------------------------------------------------------------------
++ URB control
++ ------------------------------------------------------------------*/
++
++/*
++ * IRQ callback, called by URB callback
++ */
++static void cx231xx_irq_vbi_callback(struct urb *urb)
++{
++ struct cx231xx_dmaqueue *dma_q = urb->context;
++ struct cx231xx_video_mode *vmode =
++ container_of(dma_q, struct cx231xx_video_mode, vidq);
++ struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode);
++ int rc;
++
++ switch (urb->status) {
++ case 0: /* success */
++ case -ETIMEDOUT: /* NAK */
++ break;
++ case -ECONNRESET: /* kill */
++ case -ENOENT:
++ case -ESHUTDOWN:
++ return;
++ default: /* error */
++ cx231xx_err(DRIVER_NAME "urb completition error %d.\n",
++ urb->status);
++ break;
++ }
++
++ /* Copy data from URB */
++ spin_lock(&dev->vbi_mode.slock);
++ rc = dev->vbi_mode.isoc_ctl.isoc_copy(dev, urb);
++ spin_unlock(&dev->vbi_mode.slock);
++
++ /* Reset status */
++ urb->status = 0;
++
++ urb->status = usb_submit_urb(urb, GFP_ATOMIC);
++ if (urb->status) {
++ cx231xx_err(DRIVER_NAME "urb resubmit failed (error=%i)\n",
++ urb->status);
++ }
++}
++
++/*
++ * Stop and Deallocate URBs
++ */
++void cx231xx_uninit_vbi_isoc(struct cx231xx *dev)
++{
++ struct urb *urb;
++ int i;
++
++ cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_uninit_vbi_isoc\n");
++
++ dev->vbi_mode.isoc_ctl.nfields = -1;
++ for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) {
++ urb = dev->vbi_mode.isoc_ctl.urb[i];
++ if (urb) {
++ if (!irqs_disabled())
++ usb_kill_urb(urb);
++ else
++ usb_unlink_urb(urb);
++
++ if (dev->vbi_mode.isoc_ctl.transfer_buffer[i]) {
++
++ kfree(dev->vbi_mode.isoc_ctl.
++ transfer_buffer[i]);
++ dev->vbi_mode.isoc_ctl.transfer_buffer[i] =
++ NULL;
++ }
++ usb_free_urb(urb);
++ dev->vbi_mode.isoc_ctl.urb[i] = NULL;
++ }
++ dev->vbi_mode.isoc_ctl.transfer_buffer[i] = NULL;
++ }
++
++ kfree(dev->vbi_mode.isoc_ctl.urb);
++ kfree(dev->vbi_mode.isoc_ctl.transfer_buffer);
++
++ dev->vbi_mode.isoc_ctl.urb = NULL;
++ dev->vbi_mode.isoc_ctl.transfer_buffer = NULL;
++ dev->vbi_mode.isoc_ctl.num_bufs = 0;
++
++ cx231xx_capture_start(dev, 0, Vbi);
++}
++EXPORT_SYMBOL_GPL(cx231xx_uninit_vbi_isoc);
++
++/*
++ * Allocate URBs and start IRQ
++ */
++int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
++ int num_bufs, int max_pkt_size,
++ int (*isoc_copy) (struct cx231xx *dev,
++ struct urb *urb))
++{
++ struct cx231xx_dmaqueue *dma_q = &dev->vbi_mode.vidq;
++ int i;
++ int sb_size, pipe;
++ struct urb *urb;
++ int rc;
++
++ cx231xx_info(DRIVER_NAME "cx231xx: called cx231xx_prepare_isoc\n");
++
++ /* De-allocates all pending stuff */
++ cx231xx_uninit_vbi_isoc(dev);
++
++ /* clear if any halt */
++ usb_clear_halt(dev->udev,
++ usb_rcvbulkpipe(dev->udev,
++ dev->vbi_mode.end_point_addr));
++
++ dev->vbi_mode.isoc_ctl.isoc_copy = isoc_copy;
++ dev->vbi_mode.isoc_ctl.num_bufs = num_bufs;
++ dma_q->pos = 0;
++ dma_q->is_partial_line = 0;
++ dma_q->last_sav = 0;
++ dma_q->current_field = -1;
++ dma_q->bytes_left_in_line = dev->width << 1;
++ dma_q->lines_per_field = ((dev->norm & V4L2_STD_625_50) ?
++ PAL_VBI_LINES : NTSC_VBI_LINES);
++ dma_q->lines_completed = 0;
++ for (i = 0; i < 8; i++)
++ dma_q->partial_buf[i] = 0;
++
++ dev->vbi_mode.isoc_ctl.urb = kzalloc(sizeof(void *) * num_bufs,
++ GFP_KERNEL);
++ if (!dev->vbi_mode.isoc_ctl.urb) {
++ cx231xx_errdev("cannot alloc memory for usb buffers\n");
++ return -ENOMEM;
++ }
++
++ dev->vbi_mode.isoc_ctl.transfer_buffer =
++ kzalloc(sizeof(void *) * num_bufs, GFP_KERNEL);
++ if (!dev->vbi_mode.isoc_ctl.transfer_buffer) {
++ cx231xx_errdev("cannot allocate memory for usbtransfer\n");
++ kfree(dev->vbi_mode.isoc_ctl.urb);
++ return -ENOMEM;
++ }
++
++ dev->vbi_mode.isoc_ctl.max_pkt_size = max_pkt_size;
++ dev->vbi_mode.isoc_ctl.buf = NULL;
++
++ sb_size = max_packets * dev->vbi_mode.isoc_ctl.max_pkt_size;
++
++ /* allocate urbs and transfer buffers */
++ for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) {
++
++ urb = usb_alloc_urb(0, GFP_KERNEL);
++ if (!urb) {
++ cx231xx_err(DRIVER_NAME
++ ": cannot alloc isoc_ctl.urb %i\n", i);
++ cx231xx_uninit_vbi_isoc(dev);
++ return -ENOMEM;
++ }
++ dev->vbi_mode.isoc_ctl.urb[i] = urb;
++ urb->transfer_flags = 0;
++
++ dev->vbi_mode.isoc_ctl.transfer_buffer[i] =
++ kzalloc(sb_size, GFP_KERNEL);
++ if (!dev->vbi_mode.isoc_ctl.transfer_buffer[i]) {
++ cx231xx_err(DRIVER_NAME
++ ": unable to allocate %i bytes for transfer"
++ " buffer %i%s\n", sb_size, i,
++ in_interrupt() ? " while in int" : "");
++ cx231xx_uninit_vbi_isoc(dev);
++ return -ENOMEM;
++ }
++
++ pipe = usb_rcvbulkpipe(dev->udev, dev->vbi_mode.end_point_addr);
++ usb_fill_bulk_urb(urb, dev->udev, pipe,
++ dev->vbi_mode.isoc_ctl.transfer_buffer[i],
++ sb_size, cx231xx_irq_vbi_callback, dma_q);
++ }
++
++ init_waitqueue_head(&dma_q->wq);
++
++ /* submit urbs and enables IRQ */
++ for (i = 0; i < dev->vbi_mode.isoc_ctl.num_bufs; i++) {
++ rc = usb_submit_urb(dev->vbi_mode.isoc_ctl.urb[i], GFP_ATOMIC);
++ if (rc) {
++ cx231xx_err(DRIVER_NAME
++ ": submit of urb %i failed (error=%i)\n", i,
++ rc);
++ cx231xx_uninit_vbi_isoc(dev);
++ return rc;
++ }
++ }
++
++ cx231xx_capture_start(dev, 1, Vbi);
++
++ return 0;
++}
++EXPORT_SYMBOL_GPL(cx231xx_init_vbi_isoc);
++
++u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
++ u8 sav_eav, u8 *p_buffer, u32 buffer_size)
++{
++ u32 bytes_copied = 0;
++ int current_field = -1;
++
++ switch (sav_eav) {
++
++ case SAV_VBI_FIELD1:
++ current_field = 1;
++ break;
++
++ case SAV_VBI_FIELD2:
++ current_field = 2;
++ break;
++ default:
++ break;
++ }
++
++ if (current_field < 0)
++ return bytes_copied;
++
++ dma_q->last_sav = sav_eav;
++
++ bytes_copied =
++ cx231xx_copy_vbi_line(dev, dma_q, p_buffer, buffer_size,
++ current_field);
++
++ return bytes_copied;
++}
++
++/*
++ * Announces that a buffer were filled and request the next
++ */
++static inline void vbi_buffer_filled(struct cx231xx *dev,
++ struct cx231xx_dmaqueue *dma_q,
++ struct cx231xx_buffer *buf)
++{
++ /* Advice that buffer was filled */
++ /* cx231xx_info(DRIVER_NAME "[%p/%d] wakeup\n", buf, buf->vb.i); */
++
++ buf->vb.state = VIDEOBUF_DONE;
++ buf->vb.field_count++;
++ do_gettimeofday(&buf->vb.ts);
++
++ dev->vbi_mode.isoc_ctl.buf = NULL;
++
++ list_del(&buf->vb.queue);
++ wake_up(&buf->vb.done);
++}
++
++u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
++ u8 *p_line, u32 length, int field_number)
++{
++ u32 bytes_to_copy;
++ struct cx231xx_buffer *buf;
++ u32 _line_size = dev->width * 2;
++
++ if (dma_q->current_field != field_number)
++ cx231xx_reset_vbi_buffer(dev, dma_q);
++
++ /* get the buffer pointer */
++ buf = dev->vbi_mode.isoc_ctl.buf;
++
++ /* Remember the field number for next time */
++ dma_q->current_field = field_number;
++
++ bytes_to_copy = dma_q->bytes_left_in_line;
++ if (bytes_to_copy > length)
++ bytes_to_copy = length;
++
++ if (dma_q->lines_completed >= dma_q->lines_per_field) {
++ dma_q->bytes_left_in_line -= bytes_to_copy;
++ dma_q->is_partial_line =
++ (dma_q->bytes_left_in_line == 0) ? 0 : 1;
++ return 0;
++ }
++
++ dma_q->is_partial_line = 1;
++
++ /* If we don't have a buffer, just return the number of bytes we would
++ have copied if we had a buffer. */
++ if (!buf) {
++ dma_q->bytes_left_in_line -= bytes_to_copy;
++ dma_q->is_partial_line =
++ (dma_q->bytes_left_in_line == 0) ? 0 : 1;
++ return bytes_to_copy;
++ }
++
++ /* copy the data to video buffer */
++ cx231xx_do_vbi_copy(dev, dma_q, p_line, bytes_to_copy);
++
++ dma_q->pos += bytes_to_copy;
++ dma_q->bytes_left_in_line -= bytes_to_copy;
++
++ if (dma_q->bytes_left_in_line == 0) {
++
++ dma_q->bytes_left_in_line = _line_size;
++ dma_q->lines_completed++;
++ dma_q->is_partial_line = 0;
++
++ if (cx231xx_is_vbi_buffer_done(dev, dma_q) && buf) {
++
++ vbi_buffer_filled(dev, dma_q, buf);
++
++ dma_q->pos = 0;
++ buf = NULL;
++ dma_q->lines_completed = 0;
++ }
++ }
++
++ return bytes_to_copy;
++}
++
++/*
++ * video-buf generic routine to get the next available buffer
++ */
++static inline void get_next_vbi_buf(struct cx231xx_dmaqueue *dma_q,
++ struct cx231xx_buffer **buf)
++{
++ struct cx231xx_video_mode *vmode =
++ container_of(dma_q, struct cx231xx_video_mode, vidq);
++ struct cx231xx *dev = container_of(vmode, struct cx231xx, vbi_mode);
++ char *outp;
++
++ if (list_empty(&dma_q->active)) {
++ cx231xx_err(DRIVER_NAME ": No active queue to serve\n");
++ dev->vbi_mode.isoc_ctl.buf = NULL;
++ *buf = NULL;
++ return;
++ }
++
++ /* Get the next buffer */
++ *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue);
++
++ /* Cleans up buffer - Usefull for testing for frame/URB loss */
++ outp = videobuf_to_vmalloc(&(*buf)->vb);
++ memset(outp, 0, (*buf)->vb.size);
++
++ dev->vbi_mode.isoc_ctl.buf = *buf;
++
++ return;
++}
++
++void cx231xx_reset_vbi_buffer(struct cx231xx *dev,
++ struct cx231xx_dmaqueue *dma_q)
++{
++ struct cx231xx_buffer *buf;
++
++ buf = dev->vbi_mode.isoc_ctl.buf;
++
++ if (buf == NULL) {
++ /* first try to get the buffer */
++ get_next_vbi_buf(dma_q, &buf);
++
++ dma_q->pos = 0;
++ dma_q->current_field = -1;
++ }
++
++ dma_q->bytes_left_in_line = dev->width << 1;
++ dma_q->lines_completed = 0;
++}
++
++int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
++ u8 *p_buffer, u32 bytes_to_copy)
++{
++ u8 *p_out_buffer = NULL;
++ u32 current_line_bytes_copied = 0;
++ struct cx231xx_buffer *buf;
++ u32 _line_size = dev->width << 1;
++ void *startwrite;
++ int offset, lencopy;
++
++ buf = dev->vbi_mode.isoc_ctl.buf;
++
++ if (buf == NULL)
++ return -EINVAL;
++
++ p_out_buffer = videobuf_to_vmalloc(&buf->vb);
++
++ if (dma_q->bytes_left_in_line != _line_size) {
++ current_line_bytes_copied =
++ _line_size - dma_q->bytes_left_in_line;
++ }
++
++ offset = (dma_q->lines_completed * _line_size) +
++ current_line_bytes_copied;
++
++ /* prepare destination address */
++ startwrite = p_out_buffer + offset;
++
++ lencopy = dma_q->bytes_left_in_line > bytes_to_copy ?
++ bytes_to_copy : dma_q->bytes_left_in_line;
++
++ memcpy(startwrite, p_buffer, lencopy);
++
++ return 0;
++}
++
++u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev,
++ struct cx231xx_dmaqueue *dma_q)
++{
++ u32 height = 0;
++
++ height = ((dev->norm & V4L2_STD_625_50) ?
++ PAL_VBI_LINES : NTSC_VBI_LINES);
++ return (dma_q->lines_completed == height) ? 1 : 0;
++}
+diff --git a/drivers/media/video/cx231xx/cx231xx-vbi.h b/drivers/media/video/cx231xx/cx231xx-vbi.h
+new file mode 100644
+index 0000000..89c7fe8
+--- /dev/null
++++ b/drivers/media/video/cx231xx/cx231xx-vbi.h
+@@ -0,0 +1,65 @@
++/*
++ cx231xx_vbi.h - driver for Conexant Cx23100/101/102 USB video capture devices
++
++ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
++ Based on cx88 driver
++
++ 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 _CX231XX_VBI_H
++#define _CX231XX_VBI_H
++
++extern struct videobuf_queue_ops cx231xx_vbi_qops;
++
++#define NTSC_VBI_START_LINE 10 /* line 10 - 21 */
++#define NTSC_VBI_END_LINE 21
++#define NTSC_VBI_LINES (NTSC_VBI_END_LINE-NTSC_VBI_START_LINE+1)
++
++#define PAL_VBI_START_LINE 6
++#define PAL_VBI_END_LINE 23
++#define PAL_VBI_LINES (PAL_VBI_END_LINE-PAL_VBI_START_LINE+1)
++
++#define VBI_STRIDE 1440
++#define VBI_SAMPLES_PER_LINE 1440
++
++#define CX231XX_NUM_VBI_PACKETS 4
++#define CX231XX_NUM_VBI_BUFS 5
++
++/* stream functions */
++int cx231xx_init_vbi_isoc(struct cx231xx *dev, int max_packets,
++ int num_bufs, int max_pkt_size,
++ int (*isoc_copy) (struct cx231xx *dev,
++ struct urb *urb));
++
++void cx231xx_uninit_vbi_isoc(struct cx231xx *dev);
++
++/* vbi data copy functions */
++u32 cx231xx_get_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
++ u8 sav_eav, u8 *p_buffer, u32 buffer_size);
++
++u32 cx231xx_copy_vbi_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
++ u8 *p_line, u32 length, int field_number);
++
++void cx231xx_reset_vbi_buffer(struct cx231xx *dev,
++ struct cx231xx_dmaqueue *dma_q);
++
++int cx231xx_do_vbi_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
++ u8 *p_buffer, u32 bytes_to_copy);
++
++u8 cx231xx_is_vbi_buffer_done(struct cx231xx *dev,
++ struct cx231xx_dmaqueue *dma_q);
++
++#endif
+diff --git a/drivers/media/video/cx231xx/cx231xx-video.c b/drivers/media/video/cx231xx/cx231xx-video.c
+new file mode 100644
+index 0000000..d660c08
+--- /dev/null
++++ b/drivers/media/video/cx231xx/cx231xx-video.c
+@@ -0,0 +1,2439 @@
++/*
++ cx231xx-video.c - driver for Conexant Cx23100/101/102
++ USB video capture devices
++
++ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
++ Based on em28xx driver
++ Based on cx23885 driver
++ Based on cx88 driver
++
++ 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 <linux/init.h>
++#include <linux/list.h>
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/bitmap.h>
++#include <linux/usb.h>
++#include <linux/i2c.h>
++#include <linux/version.h>
++#include <linux/mm.h>
++#include <linux/mutex.h>
++
++#include <media/v4l2-common.h>
++#include <media/v4l2-ioctl.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/msp3400.h>
++#include <media/tuner.h>
++
++#include "dvb_frontend.h"
++
++#include "cx231xx.h"
++#include "cx231xx-vbi.h"
++
++#define CX231XX_VERSION_CODE KERNEL_VERSION(0, 0, 1)
++
++#define DRIVER_AUTHOR "Srinivasa Deevi <srinivasa.deevi@conexant.com>"
++#define DRIVER_DESC "Conexant cx231xx based USB video device driver"
++
++#define cx231xx_videodbg(fmt, arg...) do {\
++ if (video_debug) \
++ printk(KERN_INFO "%s %s :"fmt, \
++ dev->name, __func__ , ##arg); } while (0)
++
++static unsigned int isoc_debug;
++module_param(isoc_debug, int, 0644);
++MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
++
++#define cx231xx_isocdbg(fmt, arg...) \
++do {\
++ if (isoc_debug) { \
++ printk(KERN_INFO "%s %s :"fmt, \
++ dev->name, __func__ , ##arg); \
++ } \
++ } while (0)
++
++MODULE_AUTHOR(DRIVER_AUTHOR);
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_LICENSE("GPL");
++
++static unsigned int card[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
++static unsigned int video_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
++static unsigned int vbi_nr[] = {[0 ... (CX231XX_MAXBOARDS - 1)] = UNSET };
++static unsigned int radio_nr[] = {[0 ... (CX231XX_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_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;
++module_param(video_debug, int, 0644);
++MODULE_PARM_DESC(video_debug, "enable debug messages [video]");
++
++/* supported video standards */
++static struct cx231xx_fmt format[] = {
++ {
++ .name = "16bpp YUY2, 4:2:2, packed",
++ .fourcc = V4L2_PIX_FMT_YUYV,
++ .depth = 16,
++ .reg = 0,
++ },
++};
++
++/* supported controls */
++/* Common to all boards */
++
++/* ------------------------------------------------------------------- */
++
++static const struct v4l2_queryctrl no_ctl = {
++ .name = "42",
++ .flags = V4L2_CTRL_FLAG_DISABLED,
++};
++
++static struct cx231xx_ctrl cx231xx_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 CX231XX_CTLS = ARRAY_SIZE(cx231xx_ctls);
++
++static const u32 cx231xx_user_ctrls[] = {
++ V4L2_CID_USER_CLASS,
++ V4L2_CID_BRIGHTNESS,
++ V4L2_CID_CONTRAST,
++ V4L2_CID_SATURATION,
++ V4L2_CID_HUE,
++ V4L2_CID_AUDIO_VOLUME,
++#if 0
++ V4L2_CID_AUDIO_BALANCE,
++#endif
++ V4L2_CID_AUDIO_MUTE,
++ 0
++};
++
++static const u32 *ctrl_classes[] = {
++ cx231xx_user_ctrls,
++ NULL
++};
++
++/* ------------------------------------------------------------------
++ Video buffer and parser functions
++ ------------------------------------------------------------------*/
++
++/*
++ * Announces that a buffer were filled and request the next
++ */
++static inline void buffer_filled(struct cx231xx *dev,
++ struct cx231xx_dmaqueue *dma_q,
++ struct cx231xx_buffer *buf)
++{
++ /* Advice that buffer was filled */
++ cx231xx_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
++ buf->vb.state = VIDEOBUF_DONE;
++ buf->vb.field_count++;
++ do_gettimeofday(&buf->vb.ts);
++
++ dev->video_mode.isoc_ctl.buf = NULL;
++
++ list_del(&buf->vb.queue);
++ wake_up(&buf->vb.done);
++}
++
++static inline void print_err_status(struct cx231xx *dev, int packet, int status)
++{
++ char *errmsg = "Unknown";
++
++ switch (status) {
++ case -ENOENT:
++ errmsg = "unlinked synchronuously";
++ break;
++ case -ECONNRESET:
++ errmsg = "unlinked asynchronuously";
++ break;
++ case -ENOSR:
++ errmsg = "Buffer error (overrun)";
++ break;
++ case -EPIPE:
++ errmsg = "Stalled (device not responding)";
++ break;
++ case -EOVERFLOW:
++ errmsg = "Babble (bad cable?)";
++ break;
++ case -EPROTO:
++ errmsg = "Bit-stuff error (bad cable?)";
++ break;
++ case -EILSEQ:
++ errmsg = "CRC/Timeout (could be anything)";
++ break;
++ case -ETIME:
++ errmsg = "Device does not respond";
++ break;
++ }
++ if (packet < 0) {
++ cx231xx_isocdbg("URB status %d [%s].\n", status, errmsg);
++ } else {
++ cx231xx_isocdbg("URB packet %d, status %d [%s].\n",
++ packet, status, errmsg);
++ }
++}
++
++/*
++ * video-buf generic routine to get the next available buffer
++ */
++static inline void get_next_buf(struct cx231xx_dmaqueue *dma_q,
++ struct cx231xx_buffer **buf)
++{
++ struct cx231xx_video_mode *vmode =
++ container_of(dma_q, struct cx231xx_video_mode, vidq);
++ struct cx231xx *dev = container_of(vmode, struct cx231xx, video_mode);
++
++ char *outp;
++
++ if (list_empty(&dma_q->active)) {
++ cx231xx_isocdbg("No active queue to serve\n");
++ dev->video_mode.isoc_ctl.buf = NULL;
++ *buf = NULL;
++ return;
++ }
++
++ /* Get the next buffer */
++ *buf = list_entry(dma_q->active.next, struct cx231xx_buffer, vb.queue);
++
++ /* Cleans up buffer - Usefull for testing for frame/URB loss */
++ outp = videobuf_to_vmalloc(&(*buf)->vb);
++ memset(outp, 0, (*buf)->vb.size);
++
++ dev->video_mode.isoc_ctl.buf = *buf;
++
++ return;
++}
++
++/*
++ * Controls the isoc copy of each urb packet
++ */
++static inline int cx231xx_isoc_copy(struct cx231xx *dev, struct urb *urb)
++{
++ struct cx231xx_buffer *buf;
++ struct cx231xx_dmaqueue *dma_q = urb->context;
++ unsigned char *outp = NULL;
++ int i, rc = 1;
++ unsigned char *p_buffer;
++ u32 bytes_parsed = 0, buffer_size = 0;
++ u8 sav_eav = 0;
++
++ if (!dev)
++ return 0;
++
++ if ((dev->state & DEV_DISCONNECTED) || (dev->state & DEV_MISCONFIGURED))
++ return 0;
++
++ if (urb->status < 0) {
++ print_err_status(dev, -1, urb->status);
++ if (urb->status == -ENOENT)
++ return 0;
++ }
++
++ buf = dev->video_mode.isoc_ctl.buf;
++ if (buf != NULL)
++ outp = videobuf_to_vmalloc(&buf->vb);
++
++ for (i = 0; i < urb->number_of_packets; i++) {
++ int status = urb->iso_frame_desc[i].status;
++
++ if (status < 0) {
++ print_err_status(dev, i, status);
++ if (urb->iso_frame_desc[i].status != -EPROTO)
++ continue;
++ }
++
++ if (urb->iso_frame_desc[i].actual_length <= 0) {
++ /* cx231xx_isocdbg("packet %d is empty",i); - spammy */
++ continue;
++ }
++ if (urb->iso_frame_desc[i].actual_length >
++ dev->video_mode.max_pkt_size) {
++ cx231xx_isocdbg("packet bigger than packet size");
++ continue;
++ }
++
++ /* get buffer pointer and length */
++ p_buffer = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
++ buffer_size = urb->iso_frame_desc[i].actual_length;
++ bytes_parsed = 0;
++
++ if (dma_q->is_partial_line) {
++ /* Handle the case of a partial line */
++ sav_eav = dma_q->last_sav;
++ } else {
++ /* Check for a SAV/EAV overlapping
++ the buffer boundary */
++ sav_eav =
++ cx231xx_find_boundary_SAV_EAV(p_buffer,
++ dma_q->partial_buf,
++ &bytes_parsed);
++ }
++
++ sav_eav &= 0xF0;
++ /* Get the first line if we have some portion of an SAV/EAV from
++ the last buffer or a partial line */
++ if (sav_eav) {
++ bytes_parsed += cx231xx_get_video_line(dev, dma_q,
++ sav_eav, /* SAV/EAV */
++ p_buffer + bytes_parsed, /* p_buffer */
++ buffer_size - bytes_parsed);/* buf size */
++ }
++
++ /* Now parse data that is completely in this buffer */
++ /* dma_q->is_partial_line = 0; */
++
++ while (bytes_parsed < buffer_size) {
++ u32 bytes_used = 0;
++
++ sav_eav = cx231xx_find_next_SAV_EAV(
++ p_buffer + bytes_parsed, /* p_buffer */
++ buffer_size - bytes_parsed, /* buf size */
++ &bytes_used);/* bytes used to get SAV/EAV */
++
++ bytes_parsed += bytes_used;
++
++ sav_eav &= 0xF0;
++ if (sav_eav && (bytes_parsed < buffer_size)) {
++ bytes_parsed += cx231xx_get_video_line(dev,
++ dma_q, sav_eav, /* SAV/EAV */
++ p_buffer + bytes_parsed,/* p_buffer */
++ buffer_size - bytes_parsed);/*buf size*/
++ }
++ }
++
++ /* Save the last four bytes of the buffer so we can check the
++ buffer boundary condition next time */
++ memcpy(dma_q->partial_buf, p_buffer + buffer_size - 4, 4);
++ bytes_parsed = 0;
++
++ }
++ return rc;
++}
++
++u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf,
++ u32 *p_bytes_used)
++{
++ u32 bytes_used;
++ u8 boundary_bytes[8];
++ u8 sav_eav = 0;
++
++ *p_bytes_used = 0;
++
++ /* Create an array of the last 4 bytes of the last buffer and the first
++ 4 bytes of the current buffer. */
++
++ memcpy(boundary_bytes, partial_buf, 4);
++ memcpy(boundary_bytes + 4, p_buffer, 4);
++
++ /* Check for the SAV/EAV in the boundary buffer */
++ sav_eav = cx231xx_find_next_SAV_EAV((u8 *)&boundary_bytes, 8,
++ &bytes_used);
++
++ if (sav_eav) {
++ /* found a boundary SAV/EAV. Updates the bytes used to reflect
++ only those used in the new buffer */
++ *p_bytes_used = bytes_used - 4;
++ }
++
++ return sav_eav;
++}
++
++u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size, u32 *p_bytes_used)
++{
++ u32 i;
++ u8 sav_eav = 0;
++
++ /*
++ * Don't search if the buffer size is less than 4. It causes a page
++ * fault since buffer_size - 4 evaluates to a large number in that
++ * case.
++ */
++ if (buffer_size < 4) {
++ *p_bytes_used = buffer_size;
++ return 0;
++ }
++
++ for (i = 0; i < (buffer_size - 3); i++) {
++
++ if ((p_buffer[i] == 0xFF) &&
++ (p_buffer[i + 1] == 0x00) && (p_buffer[i + 2] == 0x00)) {
++
++ *p_bytes_used = i + 4;
++ sav_eav = p_buffer[i + 3];
++ return sav_eav;
++ }
++ }
++
++ *p_bytes_used = buffer_size;
++ return 0;
++}
++
++u32 cx231xx_get_video_line(struct cx231xx *dev,
++ struct cx231xx_dmaqueue *dma_q, u8 sav_eav,
++ u8 *p_buffer, u32 buffer_size)
++{
++ u32 bytes_copied = 0;
++ int current_field = -1;
++
++ switch (sav_eav) {
++ case SAV_ACTIVE_VIDEO_FIELD1:
++ /* looking for skipped line which occurred in PAL 720x480 mode.
++ In this case, there will be no active data contained
++ between the SAV and EAV */
++ if ((buffer_size > 3) && (p_buffer[0] == 0xFF) &&
++ (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) &&
++ ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) ||
++ (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) ||
++ (p_buffer[3] == EAV_VBLANK_FIELD1) ||
++ (p_buffer[3] == EAV_VBLANK_FIELD2)))
++ return bytes_copied;
++ current_field = 1;
++ break;
++
++ case SAV_ACTIVE_VIDEO_FIELD2:
++ /* looking for skipped line which occurred in PAL 720x480 mode.
++ In this case, there will be no active data contained between
++ the SAV and EAV */
++ if ((buffer_size > 3) && (p_buffer[0] == 0xFF) &&
++ (p_buffer[1] == 0x00) && (p_buffer[2] == 0x00) &&
++ ((p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD1) ||
++ (p_buffer[3] == EAV_ACTIVE_VIDEO_FIELD2) ||
++ (p_buffer[3] == EAV_VBLANK_FIELD1) ||
++ (p_buffer[3] == EAV_VBLANK_FIELD2)))
++ return bytes_copied;
++ current_field = 2;
++ break;
++ }
++
++ dma_q->last_sav = sav_eav;
++
++ bytes_copied = cx231xx_copy_video_line(dev, dma_q, p_buffer,
++ buffer_size, current_field);
++
++ return bytes_copied;
++}
++
++u32 cx231xx_copy_video_line(struct cx231xx *dev,
++ struct cx231xx_dmaqueue *dma_q, u8 *p_line,
++ u32 length, int field_number)
++{
++ u32 bytes_to_copy;
++ struct cx231xx_buffer *buf;
++ u32 _line_size = dev->width * 2;
++
++ if (dma_q->current_field != field_number)
++ cx231xx_reset_video_buffer(dev, dma_q);
++
++ /* get the buffer pointer */
++ buf = dev->video_mode.isoc_ctl.buf;
++
++ /* Remember the field number for next time */
++ dma_q->current_field = field_number;
++
++ bytes_to_copy = dma_q->bytes_left_in_line;
++ if (bytes_to_copy > length)
++ bytes_to_copy = length;
++
++ if (dma_q->lines_completed >= dma_q->lines_per_field) {
++ dma_q->bytes_left_in_line -= bytes_to_copy;
++ dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0) ?
++ 0 : 1;
++ return 0;
++ }
++
++ dma_q->is_partial_line = 1;
++
++ /* If we don't have a buffer, just return the number of bytes we would
++ have copied if we had a buffer. */
++ if (!buf) {
++ dma_q->bytes_left_in_line -= bytes_to_copy;
++ dma_q->is_partial_line = (dma_q->bytes_left_in_line == 0)
++ ? 0 : 1;
++ return bytes_to_copy;
++ }
++
++ /* copy the data to video buffer */
++ cx231xx_do_copy(dev, dma_q, p_line, bytes_to_copy);
++
++ dma_q->pos += bytes_to_copy;
++ dma_q->bytes_left_in_line -= bytes_to_copy;
++
++ if (dma_q->bytes_left_in_line == 0) {
++ dma_q->bytes_left_in_line = _line_size;
++ dma_q->lines_completed++;
++ dma_q->is_partial_line = 0;
++
++ if (cx231xx_is_buffer_done(dev, dma_q) && buf) {
++ buffer_filled(dev, dma_q, buf);
++
++ dma_q->pos = 0;
++ buf = NULL;
++ dma_q->lines_completed = 0;
++ }
++ }
++
++ return bytes_to_copy;
++}
++
++void cx231xx_reset_video_buffer(struct cx231xx *dev,
++ struct cx231xx_dmaqueue *dma_q)
++{
++ struct cx231xx_buffer *buf;
++
++ /* handle the switch from field 1 to field 2 */
++ if (dma_q->current_field == 1) {
++ if (dma_q->lines_completed >= dma_q->lines_per_field)
++ dma_q->field1_done = 1;
++ else
++ dma_q->field1_done = 0;
++ }
++
++ buf = dev->video_mode.isoc_ctl.buf;
++
++ if (buf == NULL) {
++ u8 *outp = NULL;
++ /* first try to get the buffer */
++ get_next_buf(dma_q, &buf);
++
++ if (buf)
++ outp = videobuf_to_vmalloc(&buf->vb);
++
++ dma_q->pos = 0;
++ dma_q->field1_done = 0;
++ dma_q->current_field = -1;
++ }
++
++ /* reset the counters */
++ dma_q->bytes_left_in_line = dev->width << 1;
++ dma_q->lines_completed = 0;
++}
++
++int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
++ u8 *p_buffer, u32 bytes_to_copy)
++{
++ u8 *p_out_buffer = NULL;
++ u32 current_line_bytes_copied = 0;
++ struct cx231xx_buffer *buf;
++ u32 _line_size = dev->width << 1;
++ void *startwrite;
++ int offset, lencopy;
++
++ buf = dev->video_mode.isoc_ctl.buf;
++
++ if (buf == NULL)
++ return -1;
++
++ p_out_buffer = videobuf_to_vmalloc(&buf->vb);
++
++ current_line_bytes_copied = _line_size - dma_q->bytes_left_in_line;
++
++ /* Offset field 2 one line from the top of the buffer */
++ offset = (dma_q->current_field == 1) ? 0 : _line_size;
++
++ /* Offset for field 2 */
++ startwrite = p_out_buffer + offset;
++
++ /* lines already completed in the current field */
++ startwrite += (dma_q->lines_completed * _line_size * 2);
++
++ /* bytes already completed in the current line */
++ startwrite += current_line_bytes_copied;
++
++ lencopy = dma_q->bytes_left_in_line > bytes_to_copy ?
++ bytes_to_copy : dma_q->bytes_left_in_line;
++
++ if ((u8 *)(startwrite + lencopy) > (u8 *)(p_out_buffer + buf->vb.size))
++ return 0;
++
++ /* The below copies the UYVY data straight into video buffer */
++ cx231xx_swab((u16 *) p_buffer, (u16 *) startwrite, (u16) lencopy);
++
++ return 0;
++}
++
++void cx231xx_swab(u16 *from, u16 *to, u16 len)
++{
++ u16 i;
++
++ if (len <= 0)
++ return;
++
++ for (i = 0; i < len / 2; i++)
++ to[i] = (from[i] << 8) | (from[i] >> 8);
++}
++
++u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q)
++{
++ u8 buffer_complete = 0;
++
++ /* Dual field stream */
++ buffer_complete = ((dma_q->current_field == 2) &&
++ (dma_q->lines_completed >= dma_q->lines_per_field) &&
++ dma_q->field1_done);
++
++ return buffer_complete;
++}
++
++/* ------------------------------------------------------------------
++ Videobuf operations
++ ------------------------------------------------------------------*/
++
++static int
++buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size)
++{
++ struct cx231xx_fh *fh = vq->priv_data;
++ struct cx231xx *dev = fh->dev;
++ struct v4l2_frequency f;
++
++ *size = (fh->dev->width * fh->dev->height * dev->format->depth + 7)>>3;
++ if (0 == *count)
++ *count = CX231XX_DEF_BUF;
++
++ if (*count < CX231XX_MIN_BUF)
++ *count = CX231XX_MIN_BUF;
++
++ /* Ask tuner to go to analog mode */
++ memset(&f, 0, sizeof(f));
++ f.frequency = dev->ctl_freq;
++ f.type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
++
++ call_all(dev, tuner, s_frequency, &f);
++
++ return 0;
++}
++
++/* This is called *without* dev->slock held; please keep it that way */
++static void free_buffer(struct videobuf_queue *vq, struct cx231xx_buffer *buf)
++{
++ struct cx231xx_fh *fh = vq->priv_data;
++ struct cx231xx *dev = fh->dev;
++ unsigned long flags = 0;
++
++ if (in_interrupt())
++ BUG();
++
++ /* We used to wait for the buffer to finish here, but this didn't work
++ because, as we were keeping the state as VIDEOBUF_QUEUED,
++ videobuf_queue_cancel marked it as finished for us.
++ (Also, it could wedge forever if the hardware was misconfigured.)
++
++ This should be safe; by the time we get here, the buffer isn't
++ queued anymore. If we ever start marking the buffers as
++ VIDEOBUF_ACTIVE, it won't be, though.
++ */
++ spin_lock_irqsave(&dev->video_mode.slock, flags);
++ if (dev->video_mode.isoc_ctl.buf == buf)
++ dev->video_mode.isoc_ctl.buf = NULL;
++ spin_unlock_irqrestore(&dev->video_mode.slock, flags);
++
++ videobuf_vmalloc_free(&buf->vb);
++ buf->vb.state = VIDEOBUF_NEEDS_INIT;
++}
++
++static int
++buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
++ enum v4l2_field field)
++{
++ struct cx231xx_fh *fh = vq->priv_data;
++ struct cx231xx_buffer *buf =
++ container_of(vb, struct cx231xx_buffer, vb);
++ struct cx231xx *dev = fh->dev;
++ int rc = 0, urb_init = 0;
++
++ /* The only currently supported format is 16 bits/pixel */
++ buf->vb.size = (fh->dev->width * fh->dev->height * dev->format->depth
++ + 7) >> 3;
++ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
++ return -EINVAL;
++
++ buf->vb.width = dev->width;
++ buf->vb.height = dev->height;
++ buf->vb.field = field;
++
++ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
++ rc = videobuf_iolock(vq, &buf->vb, NULL);
++ if (rc < 0)
++ goto fail;
++ }
++
++ if (!dev->video_mode.isoc_ctl.num_bufs)
++ urb_init = 1;
++
++ if (urb_init) {
++ rc = cx231xx_init_isoc(dev, CX231XX_NUM_PACKETS,
++ CX231XX_NUM_BUFS,
++ dev->video_mode.max_pkt_size,
++ cx231xx_isoc_copy);
++ if (rc < 0)
++ goto fail;
++ }
++
++ buf->vb.state = VIDEOBUF_PREPARED;
++ return 0;
++
++fail:
++ free_buffer(vq, buf);
++ return rc;
++}
++
++static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
++{
++ struct cx231xx_buffer *buf =
++ container_of(vb, struct cx231xx_buffer, vb);
++ struct cx231xx_fh *fh = vq->priv_data;
++ struct cx231xx *dev = fh->dev;
++ struct cx231xx_dmaqueue *vidq = &dev->video_mode.vidq;
++
++ buf->vb.state = VIDEOBUF_QUEUED;
++ list_add_tail(&buf->vb.queue, &vidq->active);
++
++}
++
++static void buffer_release(struct videobuf_queue *vq,
++ struct videobuf_buffer *vb)
++{
++ struct cx231xx_buffer *buf =
++ container_of(vb, struct cx231xx_buffer, vb);
++ struct cx231xx_fh *fh = vq->priv_data;
++ struct cx231xx *dev = (struct cx231xx *)fh->dev;
++
++ cx231xx_isocdbg("cx231xx: called buffer_release\n");
++
++ free_buffer(vq, buf);
++}
++
++static struct videobuf_queue_ops cx231xx_video_qops = {
++ .buf_setup = buffer_setup,
++ .buf_prepare = buffer_prepare,
++ .buf_queue = buffer_queue,
++ .buf_release = buffer_release,
++};
++
++/********************* v4l2 interface **************************************/
++
++void video_mux(struct cx231xx *dev, int index)
++{
++
++ struct v4l2_routing route;
++
++ route.input = INPUT(index)->vmux;
++ route.output = 0;
++ dev->video_input = index;
++ dev->ctl_ainput = INPUT(index)->amux;
++
++ cx231xx_set_video_input_mux(dev, index);
++
++ cx25840_call(dev, video, s_routing, &route);
++
++ cx231xx_set_audio_input(dev, dev->ctl_ainput);
++
++ cx231xx_info("video_mux : %d\n", index);
++
++ /* do mode control overrides if required */
++ cx231xx_do_mode_ctrl_overrides(dev);
++}
++
++/* Usage lock check functions */
++static int res_get(struct cx231xx_fh *fh)
++{
++ struct cx231xx *dev = fh->dev;
++ int rc = 0;
++
++ /* This instance already has stream_on */
++ if (fh->stream_on)
++ return rc;
++
++ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
++ if (dev->stream_on)
++ return -EBUSY;
++ dev->stream_on = 1;
++ } else if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
++ if (dev->vbi_stream_on)
++ return -EBUSY;
++ dev->vbi_stream_on = 1;
++ } else
++ return -EINVAL;
++
++ fh->stream_on = 1;
++
++ return rc;
++}
++
++static int res_check(struct cx231xx_fh *fh)
++{
++ return fh->stream_on;
++}
++
++static void res_free(struct cx231xx_fh *fh)
++{
++ struct cx231xx *dev = fh->dev;
++
++ fh->stream_on = 0;
++
++ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
++ dev->stream_on = 0;
++ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)
++ dev->vbi_stream_on = 0;
++}
++
++static int check_dev(struct cx231xx *dev)
++{
++ if (dev->state & DEV_DISCONNECTED) {
++ cx231xx_errdev("v4l2 ioctl: device not present\n");
++ return -ENODEV;
++ }
++
++ if (dev->state & DEV_MISCONFIGURED) {
++ cx231xx_errdev("v4l2 ioctl: device is misconfigured; "
++ "close and open it again\n");
++ return -EIO;
++ }
++ return 0;
++}
++
++void get_scale(struct cx231xx *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);
++
++ *hscale = (((unsigned long)maxw) << 12) / width - 4096L;
++ if (*hscale >= 0x4000)
++ *hscale = 0x3fff;
++
++ *vscale = (((unsigned long)maxh) << 12) / height - 4096L;
++ if (*vscale >= 0x4000)
++ *vscale = 0x3fff;
++
++ dev->hscale = *hscale;
++ dev->vscale = *vscale;
++
++}
++
++/* ------------------------------------------------------------------
++ IOCTL vidioc handling
++ ------------------------------------------------------------------*/
++
++static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++
++ mutex_lock(&dev->lock);
++
++ f->fmt.pix.width = dev->width;
++ f->fmt.pix.height = dev->height;
++ f->fmt.pix.pixelformat = dev->format->fourcc;;
++ f->fmt.pix.bytesperline = (dev->width * dev->format->depth + 7) >> 3;;
++ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * dev->height;
++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
++
++ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
++
++ mutex_unlock(&dev->lock);
++
++ return 0;
++}
++
++static struct cx231xx_fmt *format_by_fourcc(unsigned int fourcc)
++{
++ unsigned int i;
++
++ for (i = 0; i < ARRAY_SIZE(format); i++)
++ if (format[i].fourcc == fourcc)
++ return &format[i];
++
++ return NULL;
++}
++
++static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *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;
++ struct cx231xx_fmt *fmt;
++
++ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
++ if (!fmt) {
++ cx231xx_videodbg("Fourcc format (%08x) invalid.\n",
++ f->fmt.pix.pixelformat);
++ return -EINVAL;
++ }
++
++ /* width must even because of the YUYV format
++ height must be even because of interlacing */
++ height &= 0xfffe;
++ width &= 0xfffe;
++
++ if (unlikely(height < 32))
++ height = 32;
++ if (unlikely(height > maxh))
++ height = maxh;
++ if (unlikely(width < 48))
++ width = 48;
++ if (unlikely(width > maxw))
++ width = maxw;
++
++ 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 = fmt->fourcc;
++ f->fmt.pix.bytesperline = (dev->width * fmt->depth + 7) >> 3;
++ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * height;
++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
++ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
++
++ return 0;
++}
++
++static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ int rc;
++ struct cx231xx_fmt *fmt;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ mutex_lock(&dev->lock);
++
++ vidioc_try_fmt_vid_cap(file, priv, f);
++
++ fmt = format_by_fourcc(f->fmt.pix.pixelformat);
++ if (!fmt) {
++ rc = -EINVAL;
++ goto out;
++ }
++
++ if (videobuf_queue_is_busy(&fh->vb_vidq)) {
++ cx231xx_errdev("%s queue busy\n", __func__);
++ rc = -EBUSY;
++ goto out;
++ }
++
++ if (dev->stream_on && !fh->stream_on) {
++ cx231xx_errdev("%s device in use by another fh\n", __func__);
++ rc = -EBUSY;
++ goto out;
++ }
++
++ /* set new image size */
++ dev->width = f->fmt.pix.width;
++ dev->height = f->fmt.pix.height;
++ dev->format = fmt;
++ get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
++
++ call_all(dev, video, s_fmt, f);
++
++ /* Set the correct alternate setting for this resolution */
++ cx231xx_resolution_set(dev);
++
++out:
++ mutex_unlock(&dev->lock);
++ return rc;
++}
++
++static int vidioc_g_std(struct file *file, void *priv, v4l2_std_id * id)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++
++ *id = dev->norm;
++ return 0;
++}
++
++static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ struct v4l2_format f;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ cx231xx_info("vidioc_s_std : 0x%x\n", (unsigned int)*norm);
++
++ mutex_lock(&dev->lock);
++ dev->norm = *norm;
++
++ /* Adjusts width/height, if needed */
++ f.fmt.pix.width = dev->width;
++ f.fmt.pix.height = dev->height;
++ vidioc_try_fmt_vid_cap(file, priv, &f);
++
++ /* set new image size */
++ dev->width = f.fmt.pix.width;
++ dev->height = f.fmt.pix.height;
++ get_scale(dev, dev->width, dev->height, &dev->hscale, &dev->vscale);
++
++ call_all(dev, tuner, s_std, dev->norm);
++
++ mutex_unlock(&dev->lock);
++
++ cx231xx_resolution_set(dev);
++
++ /* do mode control overrides */
++ cx231xx_do_mode_ctrl_overrides(dev);
++
++ return 0;
++}
++
++static const char *iname[] = {
++ [CX231XX_VMUX_COMPOSITE1] = "Composite1",
++ [CX231XX_VMUX_SVIDEO] = "S-Video",
++ [CX231XX_VMUX_TELEVISION] = "Television",
++ [CX231XX_VMUX_CABLE] = "Cable TV",
++ [CX231XX_VMUX_DVB] = "DVB",
++ [CX231XX_VMUX_DEBUG] = "for debug only",
++};
++
++static int vidioc_enum_input(struct file *file, void *priv,
++ struct v4l2_input *i)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ unsigned int n;
++
++ n = i->index;
++ if (n >= MAX_CX231XX_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 ((CX231XX_VMUX_TELEVISION == INPUT(n)->type) ||
++ (CX231XX_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 cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++
++ *i = dev->video_input;
++
++ return 0;
++}
++
++static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ if (i >= MAX_CX231XX_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 cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++
++ switch (a->index) {
++ case CX231XX_AMUX_VIDEO:
++ strcpy(a->name, "Television");
++ break;
++ case CX231XX_AMUX_LINE_IN:
++ strcpy(a->name, "Line In");
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ a->index = dev->ctl_ainput;
++ a->capability = V4L2_AUDCAP_STEREO;
++
++ return 0;
++}
++
++static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ int status = 0;
++
++ /* Doesn't allow manual routing */
++ if (a->index != dev->ctl_ainput)
++ return -EINVAL;
++
++ dev->ctl_ainput = INPUT(a->index)->amux;
++ status = cx231xx_set_audio_input(dev, dev->ctl_ainput);
++
++ return status;
++}
++
++static int vidioc_queryctrl(struct file *file, void *priv,
++ struct v4l2_queryctrl *qc)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ int id = qc->id;
++ int i;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ qc->id = v4l2_ctrl_next(ctrl_classes, qc->id);
++ if (unlikely(qc->id == 0))
++ return -EINVAL;
++
++ memset(qc, 0, sizeof(*qc));
++
++ qc->id = id;
++
++ if (qc->id < V4L2_CID_BASE || qc->id >= V4L2_CID_LASTP1)
++ return -EINVAL;
++
++ for (i = 0; i < CX231XX_CTLS; i++)
++ if (cx231xx_ctls[i].v.id == qc->id)
++ break;
++
++ if (i == CX231XX_CTLS) {
++ *qc = no_ctl;
++ return 0;
++ }
++ *qc = cx231xx_ctls[i].v;
++
++ mutex_lock(&dev->lock);
++ call_all(dev, core, queryctrl, qc);
++ mutex_unlock(&dev->lock);
++
++ if (qc->type)
++ return 0;
++ else
++ return -EINVAL;
++}
++
++static int vidioc_g_ctrl(struct file *file, void *priv,
++ struct v4l2_control *ctrl)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ mutex_lock(&dev->lock);
++ call_all(dev, core, g_ctrl, ctrl);
++ mutex_unlock(&dev->lock);
++ return rc;
++}
++
++static int vidioc_s_ctrl(struct file *file, void *priv,
++ struct v4l2_control *ctrl)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ mutex_lock(&dev->lock);
++ call_all(dev, core, s_ctrl, ctrl);
++ mutex_unlock(&dev->lock);
++ return rc;
++}
++
++static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ if (0 != t->index)
++ return -EINVAL;
++
++ strcpy(t->name, "Tuner");
++
++ 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 cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ if (0 != t->index)
++ return -EINVAL;
++#if 0
++ mutex_lock(&dev->lock);
++ call_all(dev, tuner, s_tuner, t);
++ mutex_unlock(&dev->lock);
++#endif
++ return 0;
++}
++
++static int vidioc_g_frequency(struct file *file, void *priv,
++ struct v4l2_frequency *f)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++
++ mutex_lock(&dev->lock);
++ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
++ f->frequency = dev->ctl_freq;
++
++ call_all(dev, tuner, g_frequency, f);
++
++ mutex_unlock(&dev->lock);
++
++ return 0;
++}
++
++static int vidioc_s_frequency(struct file *file, void *priv,
++ struct v4l2_frequency *f)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *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;
++
++ /* set pre channel change settings in DIF first */
++ rc = cx231xx_tuner_pre_channel_change(dev);
++
++ mutex_lock(&dev->lock);
++
++ dev->ctl_freq = f->frequency;
++
++ if (dev->tuner_type == TUNER_XC5000) {
++ if (dev->cx231xx_set_analog_freq != NULL)
++ dev->cx231xx_set_analog_freq(dev, f->frequency);
++ } else
++ call_all(dev, tuner, s_frequency, f);
++
++ mutex_unlock(&dev->lock);
++
++ /* set post channel change settings in DIF first */
++ rc = cx231xx_tuner_post_channel_change(dev);
++
++ cx231xx_info("Set New FREQUENCY to %d\n", f->frequency);
++
++ return rc;
++}
++
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++
++/*
++ -R, --list-registers=type=<host/i2cdrv/i2caddr>,
++ chip=<chip>[,min=<addr>,max=<addr>]
++ dump registers from <min> to <max> [VIDIOC_DBG_G_REGISTER]
++ -r, --set-register=type=<host/i2cdrv/i2caddr>,
++ chip=<chip>,reg=<addr>,val=<val>
++ set the register [VIDIOC_DBG_S_REGISTER]
++
++ if type == host, then <chip> is the hosts chip ID (default 0)
++ if type == i2cdrv (default), then <chip> is the I2C driver name or ID
++ if type == i2caddr, then <chip> is the 7-bit I2C address
++*/
++
++static int vidioc_g_register(struct file *file, void *priv,
++ struct v4l2_dbg_register *reg)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ int ret = 0;
++ u8 value[4] = { 0, 0, 0, 0 };
++ u32 data = 0;
++
++ switch (reg->match.type) {
++ case V4L2_CHIP_MATCH_HOST:
++ switch (reg->match.addr) {
++ case 0: /* Cx231xx - internal registers */
++ ret = cx231xx_read_ctrl_reg(dev, VRT_GET_REGISTER,
++ (u16)reg->reg, value, 4);
++ reg->val = value[0] | value[1] << 8 |
++ value[2] << 16 | value[3] << 24;
++ break;
++ case 1: /* AFE - read byte */
++ ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
++ (u16)reg->reg, 2, &data, 1);
++ reg->val = le32_to_cpu(data & 0xff);
++ break;
++ case 14: /* AFE - read dword */
++ ret = cx231xx_read_i2c_data(dev, AFE_DEVICE_ADDRESS,
++ (u16)reg->reg, 2, &data, 4);
++ reg->val = le32_to_cpu(data);
++ break;
++ case 2: /* Video Block - read byte */
++ ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
++ (u16)reg->reg, 2, &data, 1);
++ reg->val = le32_to_cpu(data & 0xff);
++ break;
++ case 24: /* Video Block - read dword */
++ ret = cx231xx_read_i2c_data(dev, VID_BLK_I2C_ADDRESS,
++ (u16)reg->reg, 2, &data, 4);
++ reg->val = le32_to_cpu(data);
++ break;
++ case 3: /* I2S block - read byte */
++ ret = cx231xx_read_i2c_data(dev,
++ I2S_BLK_DEVICE_ADDRESS,
++ (u16)reg->reg, 1,
++ &data, 1);
++ reg->val = le32_to_cpu(data & 0xff);
++ break;
++ case 34: /* I2S Block - read dword */
++ ret =
++ cx231xx_read_i2c_data(dev, I2S_BLK_DEVICE_ADDRESS,
++ (u16)reg->reg, 1, &data, 4);
++ reg->val = le32_to_cpu(data);
++ break;
++ }
++ return ret < 0 ? ret : 0;
++
++ case V4L2_CHIP_MATCH_I2C_DRIVER:
++ call_all(dev, core, g_register, reg);
++ return 0;
++ case V4L2_CHIP_MATCH_I2C_ADDR:
++ /* Not supported yet */
++ return -EINVAL;
++ default:
++ if (!v4l2_chip_match_host(&reg->match))
++ return -EINVAL;
++ }
++
++ mutex_lock(&dev->lock);
++ call_all(dev, core, g_register, reg);
++ mutex_unlock(&dev->lock);
++
++ return ret;
++}
++
++static int vidioc_s_register(struct file *file, void *priv,
++ struct v4l2_dbg_register *reg)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ int ret = 0;
++ __le64 buf;
++ u32 value;
++ u8 data[4] = { 0, 0, 0, 0 };
++
++ buf = cpu_to_le64(reg->val);
++
++ switch (reg->match.type) {
++ case V4L2_CHIP_MATCH_HOST:
++ {
++ value = (u32) buf & 0xffffffff;
++
++ switch (reg->match.addr) {
++ case 0: /* cx231xx internal registers */
++ data[0] = (u8) value;
++ data[1] = (u8) (value >> 8);
++ data[2] = (u8) (value >> 16);
++ data[3] = (u8) (value >> 24);
++ ret = cx231xx_write_ctrl_reg(dev,
++ VRT_SET_REGISTER,
++ (u16)reg->reg, data,
++ 4);
++ break;
++ case 1: /* AFE - read byte */
++ ret = cx231xx_write_i2c_data(dev,
++ AFE_DEVICE_ADDRESS,
++ (u16)reg->reg, 2,
++ value, 1);
++ break;
++ case 14: /* AFE - read dword */
++ ret = cx231xx_write_i2c_data(dev,
++ AFE_DEVICE_ADDRESS,
++ (u16)reg->reg, 2,
++ value, 4);
++ break;
++ case 2: /* Video Block - read byte */
++ ret =
++ cx231xx_write_i2c_data(dev,
++ VID_BLK_I2C_ADDRESS,
++ (u16)reg->reg, 2,
++ value, 1);
++ break;
++ case 24: /* Video Block - read dword */
++ ret =
++ cx231xx_write_i2c_data(dev,
++ VID_BLK_I2C_ADDRESS,
++ (u16)reg->reg, 2,
++ value, 4);
++ break;
++ case 3: /* I2S block - read byte */
++ ret =
++ cx231xx_write_i2c_data(dev,
++ I2S_BLK_DEVICE_ADDRESS,
++ (u16)reg->reg, 1,
++ value, 1);
++ break;
++ case 34: /* I2S block - read dword */
++ ret =
++ cx231xx_write_i2c_data(dev,
++ I2S_BLK_DEVICE_ADDRESS,
++ (u16)reg->reg, 1,
++ value, 4);
++ break;
++ }
++ }
++ return ret < 0 ? ret : 0;
++
++ default:
++ break;
++ }
++
++ mutex_lock(&dev->lock);
++ call_all(dev, core, s_register, reg);
++ mutex_unlock(&dev->lock);
++
++ return ret;
++}
++#endif
++
++static int vidioc_cropcap(struct file *file, void *priv,
++ struct v4l2_cropcap *cc)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *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 cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ mutex_lock(&dev->lock);
++ rc = res_get(fh);
++
++ if (likely(rc >= 0))
++ rc = videobuf_streamon(&fh->vb_vidq);
++
++ call_all(dev, video, s_stream, 1);
++
++ mutex_unlock(&dev->lock);
++
++ return rc;
++}
++
++static int vidioc_streamoff(struct file *file, void *priv,
++ enum v4l2_buf_type type)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ if ((fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) &&
++ (fh->type != V4L2_BUF_TYPE_VBI_CAPTURE))
++ return -EINVAL;
++ if (type != fh->type)
++ return -EINVAL;
++
++ mutex_lock(&dev->lock);
++
++ cx25840_call(dev, video, s_stream, 0);
++
++ videobuf_streamoff(&fh->vb_vidq);
++ res_free(fh);
++
++ mutex_unlock(&dev->lock);
++
++ return 0;
++}
++
++static int vidioc_querycap(struct file *file, void *priv,
++ struct v4l2_capability *cap)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++
++ strlcpy(cap->driver, "cx231xx", sizeof(cap->driver));
++ strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card));
++ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
++
++ cap->version = CX231XX_VERSION_CODE;
++
++ cap->capabilities = V4L2_CAP_VBI_CAPTURE |
++#if 0
++ V4L2_CAP_SLICED_VBI_CAPTURE |
++#endif
++ 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_vid_cap(struct file *file, void *priv,
++ struct v4l2_fmtdesc *f)
++{
++ if (unlikely(f->index >= ARRAY_SIZE(format)))
++ return -EINVAL;
++
++ strlcpy(f->description, format[f->index].name, sizeof(f->description));
++ f->pixelformat = format[f->index].fourcc;
++
++ return 0;
++}
++
++/* Sliced VBI ioctls */
++static int vidioc_g_fmt_sliced_vbi_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ mutex_lock(&dev->lock);
++
++ f->fmt.sliced.service_set = 0;
++
++ call_all(dev, video, g_fmt, f);
++
++ if (f->fmt.sliced.service_set == 0)
++ rc = -EINVAL;
++
++ mutex_unlock(&dev->lock);
++ return rc;
++}
++
++static int vidioc_try_set_sliced_vbi_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ mutex_lock(&dev->lock);
++ call_all(dev, video, g_fmt, f);
++ mutex_unlock(&dev->lock);
++
++ if (f->fmt.sliced.service_set == 0)
++ return -EINVAL;
++
++ return 0;
++}
++
++/* RAW VBI ioctls */
++
++static int vidioc_g_fmt_vbi_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++
++ f->fmt.vbi.sampling_rate = (dev->norm & V4L2_STD_625_50) ?
++ 35468950 : 28636363;
++ f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
++ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
++ f->fmt.vbi.offset = 64 * 4;
++ f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
++ PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
++ f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
++ PAL_VBI_LINES : NTSC_VBI_LINES;
++ f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ?
++ PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263;
++ f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
++
++ return 0;
++
++}
++
++static int vidioc_try_fmt_vbi_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++
++ if (dev->vbi_stream_on && !fh->stream_on) {
++ cx231xx_errdev("%s device in use by another fh\n", __func__);
++ return -EBUSY;
++ }
++
++ f->type = V4L2_BUF_TYPE_VBI_CAPTURE;
++ f->fmt.vbi.sampling_rate = (dev->norm & V4L2_STD_625_50) ?
++ 35468950 : 28636363;
++ f->fmt.vbi.samples_per_line = VBI_LINE_LENGTH;
++ f->fmt.vbi.sample_format = V4L2_PIX_FMT_GREY;
++ f->fmt.vbi.offset = 244;
++ f->fmt.vbi.flags = 0;
++ f->fmt.vbi.start[0] = (dev->norm & V4L2_STD_625_50) ?
++ PAL_VBI_START_LINE : NTSC_VBI_START_LINE;
++ f->fmt.vbi.count[0] = (dev->norm & V4L2_STD_625_50) ?
++ PAL_VBI_LINES : NTSC_VBI_LINES;
++ f->fmt.vbi.start[1] = (dev->norm & V4L2_STD_625_50) ?
++ PAL_VBI_START_LINE + 312 : NTSC_VBI_START_LINE + 263;
++ f->fmt.vbi.count[1] = f->fmt.vbi.count[0];
++
++ return 0;
++
++}
++
++static int vidioc_reqbufs(struct file *file, void *priv,
++ struct v4l2_requestbuffers *rb)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ return videobuf_reqbufs(&fh->vb_vidq, rb);
++}
++
++static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *b)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ return videobuf_querybuf(&fh->vb_vidq, b);
++}
++
++static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ return videobuf_qbuf(&fh->vb_vidq, b);
++}
++
++static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
++{
++ struct cx231xx_fh *fh = priv;
++ struct cx231xx *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
++}
++
++#ifdef CONFIG_VIDEO_V4L1_COMPAT
++static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
++{
++ struct cx231xx_fh *fh = priv;
++
++ return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8);
++}
++#endif
++
++/* ----------------------------------------------------------- */
++/* RADIO ESPECIFIC IOCTLS */
++/* ----------------------------------------------------------- */
++
++static int radio_querycap(struct file *file, void *priv,
++ struct v4l2_capability *cap)
++{
++ struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev;
++
++ strlcpy(cap->driver, "cx231xx", sizeof(cap->driver));
++ strlcpy(cap->card, cx231xx_boards[dev->model].name, sizeof(cap->card));
++ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
++
++ cap->version = CX231XX_VERSION_CODE;
++ cap->capabilities = V4L2_CAP_TUNER;
++ return 0;
++}
++
++static int radio_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
++{
++ struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev;
++
++ if (unlikely(t->index > 0))
++ return -EINVAL;
++
++ strcpy(t->name, "Radio");
++ t->type = V4L2_TUNER_RADIO;
++
++ mutex_lock(&dev->lock);
++ call_all(dev, tuner, s_tuner, t);
++ mutex_unlock(&dev->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)
++{
++ if (unlikely(a->index))
++ return -EINVAL;
++
++ strcpy(a->name, "Radio");
++ return 0;
++}
++
++static int radio_s_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
++{
++ struct cx231xx *dev = ((struct cx231xx_fh *)priv)->dev;
++
++ if (0 != t->index)
++ return -EINVAL;
++
++ mutex_lock(&dev->lock);
++ call_all(dev, tuner, s_tuner, t);
++ mutex_unlock(&dev->lock);
++
++ return 0;
++}
++
++static int radio_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
++{
++ return 0;
++}
++
++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 *c)
++{
++ int i;
++
++ if (c->id < V4L2_CID_BASE || c->id >= V4L2_CID_LASTP1)
++ return -EINVAL;
++ if (c->id == V4L2_CID_AUDIO_MUTE) {
++ for (i = 0; i < CX231XX_CTLS; i++)
++ if (cx231xx_ctls[i].v.id == c->id)
++ break;
++ *c = cx231xx_ctls[i].v;
++ } else
++ *c = no_ctl;
++ return 0;
++}
++
++/*
++ * cx231xx_v4l2_open()
++ * inits the device and starts isoc transfer
++ */
++static int cx231xx_v4l2_open(struct file *filp)
++{
++ int minor = video_devdata(filp)->minor;
++ int errCode = 0, radio = 0;
++ struct cx231xx *dev = NULL;
++ struct cx231xx_fh *fh;
++ enum v4l2_buf_type fh_type = 0;
++
++ dev = cx231xx_get_device(minor, &fh_type, &radio);
++ if (NULL == dev)
++ return -ENODEV;
++
++ mutex_lock(&dev->lock);
++
++ cx231xx_videodbg("open minor=%d type=%s users=%d\n",
++ minor, v4l2_type_names[fh_type], dev->users);
++
++#if 0
++ errCode = cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
++ if (errCode < 0) {
++ cx231xx_errdev
++ ("Device locked on digital mode. Can't open analog\n");
++ mutex_unlock(&dev->lock);
++ return -EBUSY;
++ }
++#endif
++
++ fh = kzalloc(sizeof(struct cx231xx_fh), GFP_KERNEL);
++ if (!fh) {
++ cx231xx_errdev("cx231xx-video.c: Out of memory?!\n");
++ mutex_unlock(&dev->lock);
++ return -ENOMEM;
++ }
++ fh->dev = dev;
++ fh->radio = radio;
++ fh->type = fh_type;
++ filp->private_data = fh;
++
++ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
++ dev->width = norm_maxw(dev);
++ dev->height = norm_maxh(dev);
++ dev->hscale = 0;
++ dev->vscale = 0;
++
++ /* Power up in Analog TV mode */
++ cx231xx_set_power_mode(dev, POLARIS_AVMODE_ANALOGT_TV);
++
++#if 0
++ cx231xx_set_mode(dev, CX231XX_ANALOG_MODE);
++#endif
++ cx231xx_resolution_set(dev);
++
++ /* set video alternate setting */
++ cx231xx_set_video_alternate(dev);
++
++ /* Needed, since GPIO might have disabled power of
++ some i2c device */
++ cx231xx_config_i2c(dev);
++
++ /* device needs to be initialized before isoc transfer */
++ dev->video_input = dev->video_input > 2 ? 2 : dev->video_input;
++ video_mux(dev, dev->video_input);
++
++ }
++ if (fh->radio) {
++ cx231xx_videodbg("video_open: setting radio device\n");
++
++ /* cx231xx_start_radio(dev); */
++
++ call_all(dev, tuner, s_radio);
++ }
++
++ dev->users++;
++
++ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
++ videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_video_qops,
++ NULL, &dev->video_mode.slock,
++ fh->type, V4L2_FIELD_INTERLACED,
++ sizeof(struct cx231xx_buffer), fh);
++ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
++ /* Set the required alternate setting VBI interface works in
++ Bulk mode only */
++ cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
++
++ videobuf_queue_vmalloc_init(&fh->vb_vidq, &cx231xx_vbi_qops,
++ NULL, &dev->vbi_mode.slock,
++ fh->type, V4L2_FIELD_SEQ_TB,
++ sizeof(struct cx231xx_buffer), fh);
++ }
++
++ mutex_unlock(&dev->lock);
++
++ return errCode;
++}
++
++/*
++ * cx231xx_realease_resources()
++ * unregisters the v4l2,i2c and usb devices
++ * called when the device gets disconected or at module unload
++*/
++void cx231xx_release_analog_resources(struct cx231xx *dev)
++{
++
++ /*FIXME: I2C IR should be disconnected */
++
++ 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) {
++ cx231xx_info("V4L2 device /dev/vbi%d deregistered\n",
++ dev->vbi_dev->num);
++ 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) {
++ cx231xx_info("V4L2 device /dev/video%d deregistered\n",
++ dev->vdev->num);
++ if (-1 != dev->vdev->minor)
++ video_unregister_device(dev->vdev);
++ else
++ video_device_release(dev->vdev);
++ dev->vdev = NULL;
++ }
++}
++
++/*
++ * cx231xx_v4l2_close()
++ * stops streaming and deallocates all resources allocated by the v4l2
++ * calls and ioctls
++ */
++static int cx231xx_v4l2_close(struct file *filp)
++{
++ struct cx231xx_fh *fh = filp->private_data;
++ struct cx231xx *dev = fh->dev;
++
++ cx231xx_videodbg("users=%d\n", dev->users);
++
++ mutex_lock(&dev->lock);
++
++ if (res_check(fh))
++ res_free(fh);
++
++ if (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
++ videobuf_stop(&fh->vb_vidq);
++ videobuf_mmap_free(&fh->vb_vidq);
++
++ /* the device is already disconnect,
++ free the remaining resources */
++ if (dev->state & DEV_DISCONNECTED) {
++ cx231xx_release_resources(dev);
++ mutex_unlock(&dev->lock);
++ kfree(dev);
++ return 0;
++ }
++
++ /* do this before setting alternate! */
++ cx231xx_uninit_vbi_isoc(dev);
++
++ /* set alternate 0 */
++ if (!dev->vbi_or_sliced_cc_mode)
++ cx231xx_set_alt_setting(dev, INDEX_VANC, 0);
++ else
++ cx231xx_set_alt_setting(dev, INDEX_HANC, 0);
++
++ kfree(fh);
++ dev->users--;
++ wake_up_interruptible_nr(&dev->open, 1);
++ mutex_unlock(&dev->lock);
++ return 0;
++ }
++
++ if (dev->users == 1) {
++ videobuf_stop(&fh->vb_vidq);
++ videobuf_mmap_free(&fh->vb_vidq);
++
++ /* the device is already disconnect,
++ free the remaining resources */
++ if (dev->state & DEV_DISCONNECTED) {
++ cx231xx_release_resources(dev);
++ mutex_unlock(&dev->lock);
++ kfree(dev);
++ return 0;
++ }
++
++ /* Save some power by putting tuner to sleep */
++ call_all(dev, core, s_standby, 0);
++
++ /* do this before setting alternate! */
++ cx231xx_uninit_isoc(dev);
++ cx231xx_set_mode(dev, CX231XX_SUSPEND);
++
++ /* set alternate 0 */
++ cx231xx_set_alt_setting(dev, INDEX_VIDEO, 0);
++ }
++ kfree(fh);
++ dev->users--;
++ wake_up_interruptible_nr(&dev->open, 1);
++ mutex_unlock(&dev->lock);
++ return 0;
++}
++
++/*
++ * cx231xx_v4l2_read()
++ * will allocate buffers when called for the first time
++ */
++static ssize_t
++cx231xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
++ loff_t *pos)
++{
++ struct cx231xx_fh *fh = filp->private_data;
++ struct cx231xx *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ if ((fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
++ (fh->type == V4L2_BUF_TYPE_VBI_CAPTURE)) {
++ mutex_lock(&dev->lock);
++ rc = res_get(fh);
++ mutex_unlock(&dev->lock);
++
++ if (unlikely(rc < 0))
++ return rc;
++
++ return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
++ filp->f_flags & O_NONBLOCK);
++ }
++ return 0;
++}
++
++/*
++ * cx231xx_v4l2_poll()
++ * will allocate buffers when called for the first time
++ */
++static unsigned int cx231xx_v4l2_poll(struct file *filp, poll_table * wait)
++{
++ struct cx231xx_fh *fh = filp->private_data;
++ struct cx231xx *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ mutex_lock(&dev->lock);
++ rc = res_get(fh);
++ mutex_unlock(&dev->lock);
++
++ if (unlikely(rc < 0))
++ return POLLERR;
++
++ if ((V4L2_BUF_TYPE_VIDEO_CAPTURE == fh->type) ||
++ (V4L2_BUF_TYPE_VBI_CAPTURE == fh->type))
++ return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
++ else
++ return POLLERR;
++}
++
++/*
++ * cx231xx_v4l2_mmap()
++ */
++static int cx231xx_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
++{
++ struct cx231xx_fh *fh = filp->private_data;
++ struct cx231xx *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ mutex_lock(&dev->lock);
++ rc = res_get(fh);
++ mutex_unlock(&dev->lock);
++
++ if (unlikely(rc < 0))
++ return rc;
++
++ rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
++
++ cx231xx_videodbg("vma start=0x%08lx, size=%ld, ret=%d\n",
++ (unsigned long)vma->vm_start,
++ (unsigned long)vma->vm_end -
++ (unsigned long)vma->vm_start, rc);
++
++ return rc;
++}
++
++static const struct v4l2_file_operations cx231xx_v4l_fops = {
++ .owner = THIS_MODULE,
++ .open = cx231xx_v4l2_open,
++ .release = cx231xx_v4l2_close,
++ .read = cx231xx_v4l2_read,
++ .poll = cx231xx_v4l2_poll,
++ .mmap = cx231xx_v4l2_mmap,
++ .ioctl = video_ioctl2,
++};
++
++static const struct v4l2_ioctl_ops video_ioctl_ops = {
++ .vidioc_querycap = vidioc_querycap,
++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
++ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
++ .vidioc_try_fmt_vbi_cap = vidioc_try_fmt_vbi_cap,
++ .vidioc_s_fmt_vbi_cap = vidioc_try_fmt_vbi_cap,
++ .vidioc_g_audio = vidioc_g_audio,
++ .vidioc_s_audio = vidioc_s_audio,
++ .vidioc_cropcap = vidioc_cropcap,
++ .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap,
++ .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
++ .vidioc_reqbufs = vidioc_reqbufs,
++ .vidioc_querybuf = vidioc_querybuf,
++ .vidioc_qbuf = vidioc_qbuf,
++ .vidioc_dqbuf = vidioc_dqbuf,
++ .vidioc_s_std = vidioc_s_std,
++ .vidioc_g_std = vidioc_g_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,
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++ .vidioc_g_register = vidioc_g_register,
++ .vidioc_s_register = vidioc_s_register,
++#endif
++#ifdef CONFIG_VIDEO_V4L1_COMPAT
++ .vidiocgmbuf = vidiocgmbuf,
++#endif
++};
++
++static struct video_device cx231xx_vbi_template;
++
++static const struct video_device cx231xx_video_template = {
++ .fops = &cx231xx_v4l_fops,
++ .release = video_device_release,
++ .ioctl_ops = &video_ioctl_ops,
++ .minor = -1,
++ .tvnorms = V4L2_STD_ALL,
++ .current_norm = V4L2_STD_PAL,
++};
++
++static const struct v4l2_file_operations radio_fops = {
++ .owner = THIS_MODULE,
++ .open = cx231xx_v4l2_open,
++ .release = cx231xx_v4l2_close,
++ .ioctl = video_ioctl2,
++};
++
++static const struct v4l2_ioctl_ops radio_ioctl_ops = {
++ .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,
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++ .vidioc_g_register = vidioc_g_register,
++ .vidioc_s_register = vidioc_s_register,
++#endif
++};
++
++static struct video_device cx231xx_radio_template = {
++ .name = "cx231xx-radio",
++ .fops = &radio_fops,
++ .ioctl_ops = &radio_ioctl_ops,
++ .minor = -1,
++};
++
++/******************************** usb interface ******************************/
++
++static struct video_device *cx231xx_vdev_init(struct cx231xx *dev,
++ const struct video_device
++ *template, const char *type_name)
++{
++ struct video_device *vfd;
++
++ vfd = video_device_alloc();
++ if (NULL == vfd)
++ return NULL;
++
++ *vfd = *template;
++ vfd->minor = -1;
++ vfd->v4l2_dev = &dev->v4l2_dev;
++ vfd->release = video_device_release;
++ vfd->debug = video_debug;
++
++ snprintf(vfd->name, sizeof(vfd->name), "%s %s", dev->name, type_name);
++
++ return vfd;
++}
++
++int cx231xx_register_analog_devices(struct cx231xx *dev)
++{
++ int ret;
++
++ cx231xx_info("%s: v4l2 driver version %d.%d.%d\n",
++ dev->name,
++ (CX231XX_VERSION_CODE >> 16) & 0xff,
++ (CX231XX_VERSION_CODE >> 8) & 0xff,
++ CX231XX_VERSION_CODE & 0xff);
++
++ /* set default norm */
++ /*dev->norm = cx231xx_video_template.current_norm; */
++ dev->width = norm_maxw(dev);
++ dev->height = norm_maxh(dev);
++ dev->interlaced = 0;
++ dev->hscale = 0;
++ dev->vscale = 0;
++
++ /* Analog specific initialization */
++ dev->format = &format[0];
++ /* video_mux(dev, dev->video_input); */
++
++ /* Audio defaults */
++ dev->mute = 1;
++ dev->volume = 0x1f;
++
++ /* enable vbi capturing */
++ /* write code here... */
++
++ /* allocate and fill video video_device struct */
++ dev->vdev = cx231xx_vdev_init(dev, &cx231xx_video_template, "video");
++ if (!dev->vdev) {
++ cx231xx_errdev("cannot allocate video_device.\n");
++ return -ENODEV;
++ }
++
++ /* register v4l2 video video_device */
++ ret = video_register_device(dev->vdev, VFL_TYPE_GRABBER,
++ video_nr[dev->devno]);
++ if (ret) {
++ cx231xx_errdev("unable to register video device (error=%i).\n",
++ ret);
++ return ret;
++ }
++
++ cx231xx_info("%s/0: registered device video%d [v4l2]\n",
++ dev->name, dev->vdev->num);
++
++ /* Initialize VBI template */
++ memcpy(&cx231xx_vbi_template, &cx231xx_video_template,
++ sizeof(cx231xx_vbi_template));
++ strcpy(cx231xx_vbi_template.name, "cx231xx-vbi");
++
++ /* Allocate and fill vbi video_device struct */
++ dev->vbi_dev = cx231xx_vdev_init(dev, &cx231xx_vbi_template, "vbi");
++
++ /* register v4l2 vbi video_device */
++ ret = video_register_device(dev->vbi_dev, VFL_TYPE_VBI,
++ vbi_nr[dev->devno]);
++ if (ret < 0) {
++ cx231xx_errdev("unable to register vbi device\n");
++ return ret;
++ }
++
++ cx231xx_info("%s/0: registered device vbi%d\n",
++ dev->name, dev->vbi_dev->num);
++
++ if (cx231xx_boards[dev->model].radio.type == CX231XX_RADIO) {
++ dev->radio_dev = cx231xx_vdev_init(dev, &cx231xx_radio_template,
++ "radio");
++ if (!dev->radio_dev) {
++ cx231xx_errdev("cannot allocate video_device.\n");
++ return -ENODEV;
++ }
++ ret = video_register_device(dev->radio_dev, VFL_TYPE_RADIO,
++ radio_nr[dev->devno]);
++ if (ret < 0) {
++ cx231xx_errdev("can't register radio device\n");
++ return ret;
++ }
++ cx231xx_info("Registered radio device as /dev/radio%d\n",
++ dev->radio_dev->num);
++ }
++
++ cx231xx_info("V4L2 device registered as /dev/video%d and /dev/vbi%d\n",
++ dev->vdev->num, dev->vbi_dev->num);
++
++ return 0;
++}
+diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h
+new file mode 100644
+index 0000000..aa4a23e
+--- /dev/null
++++ b/drivers/media/video/cx231xx/cx231xx.h
+@@ -0,0 +1,779 @@
++/*
++ cx231xx.h - driver for Conexant Cx23100/101/102 USB video capture devices
++
++ Copyright (C) 2008 <srinivasa.deevi at conexant dot com>
++ Based on em28xx driver
++
++ 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 _CX231XX_H
++#define _CX231XX_H
++
++#include <linux/videodev2.h>
++#include <linux/types.h>
++#include <linux/ioctl.h>
++#include <linux/i2c.h>
++#include <linux/i2c-algo-bit.h>
++#include <linux/mutex.h>
++
++
++#include <media/videobuf-vmalloc.h>
++#include <media/v4l2-device.h>
++#include <media/ir-kbd-i2c.h>
++#if defined(CONFIG_VIDEO_CX231XX_DVB) || \
++ defined(CONFIG_VIDEO_CX231XX_DVB_MODULE)
++#include <media/videobuf-dvb.h>
++#endif
++
++#include "cx231xx-reg.h"
++#include "cx231xx-pcb-cfg.h"
++#include "cx231xx-conf-reg.h"
++
++#define DRIVER_NAME "cx231xx"
++#define PWR_SLEEP_INTERVAL 5
++
++/* I2C addresses for control block in Cx231xx */
++#define AFE_DEVICE_ADDRESS 0x60
++#define I2S_BLK_DEVICE_ADDRESS 0x98
++#define VID_BLK_I2C_ADDRESS 0x88
++#define DIF_USE_BASEBAND 0xFFFFFFFF
++
++/* Boards supported by driver */
++#define CX231XX_BOARD_UNKNOWN 0
++#define CX231XX_BOARD_CNXT_RDE_250 1
++#define CX231XX_BOARD_CNXT_RDU_250 2
++
++/* Limits minimum and default number of buffers */
++#define CX231XX_MIN_BUF 4
++#define CX231XX_DEF_BUF 12
++#define CX231XX_DEF_VBI_BUF 6
++
++#define VBI_LINE_COUNT 17
++#define VBI_LINE_LENGTH 1440
++
++/*Limits the max URB message size */
++#define URB_MAX_CTRL_SIZE 80
++
++/* Params for validated field */
++#define CX231XX_BOARD_NOT_VALIDATED 1
++#define CX231XX_BOARD_VALIDATED 0
++
++/* maximum number of cx231xx boards */
++#define CX231XX_MAXBOARDS 8
++
++/* maximum number of frames that can be queued */
++#define CX231XX_NUM_FRAMES 5
++
++/* number of buffers for isoc transfers */
++#define CX231XX_NUM_BUFS 8
++
++/* number of packets for each buffer
++ windows requests only 40 packets .. so we better do the same
++ this is what I found out for all alternate numbers there!
++ */
++#define CX231XX_NUM_PACKETS 40
++
++/* default alternate; 0 means choose the best */
++#define CX231XX_PINOUT 0
++
++#define CX231XX_INTERLACED_DEFAULT 1
++
++/* time to wait when stopping the isoc transfer */
++#define CX231XX_URB_TIMEOUT \
++ msecs_to_jiffies(CX231XX_NUM_BUFS * CX231XX_NUM_PACKETS)
++
++enum cx231xx_mode {
++ CX231XX_SUSPEND,
++ CX231XX_ANALOG_MODE,
++ CX231XX_DIGITAL_MODE,
++};
++
++enum cx231xx_std_mode {
++ CX231XX_TV_AIR = 0,
++ CX231XX_TV_CABLE
++};
++
++enum cx231xx_stream_state {
++ STREAM_OFF,
++ STREAM_INTERRUPT,
++ STREAM_ON,
++};
++
++struct cx231xx;
++
++struct cx231xx_usb_isoc_ctl {
++ /* max packet size of isoc transaction */
++ int max_pkt_size;
++
++ /* number of allocated urbs */
++ int num_bufs;
++
++ /* urb for isoc transfers */
++ struct urb **urb;
++
++ /* transfer buffers for isoc transfer */
++ char **transfer_buffer;
++
++ /* Last buffer command and region */
++ u8 cmd;
++ int pos, size, pktsize;
++
++ /* Last field: ODD or EVEN? */
++ int field;
++
++ /* Stores incomplete commands */
++ u32 tmp_buf;
++ int tmp_buf_len;
++
++ /* Stores already requested buffers */
++ struct cx231xx_buffer *buf;
++
++ /* Stores the number of received fields */
++ int nfields;
++
++ /* isoc urb callback */
++ int (*isoc_copy) (struct cx231xx *dev, struct urb *urb);
++};
++
++struct cx231xx_fmt {
++ char *name;
++ u32 fourcc; /* v4l2 format id */
++ int depth;
++ int reg;
++};
++
++/* buffer for one video frame */
++struct cx231xx_buffer {
++ /* common v4l buffer stuff -- must be first */
++ struct videobuf_buffer vb;
++
++ struct list_head frame;
++ int top_field;
++ int receiving;
++};
++
++struct cx231xx_dmaqueue {
++ struct list_head active;
++ struct list_head queued;
++
++ wait_queue_head_t wq;
++
++ /* Counters to control buffer fill */
++ int pos;
++ u8 is_partial_line;
++ u8 partial_buf[8];
++ u8 last_sav;
++ int current_field;
++ u32 bytes_left_in_line;
++ u32 lines_completed;
++ u8 field1_done;
++ u32 lines_per_field;
++};
++
++/* inputs */
++
++#define MAX_CX231XX_INPUT 4
++
++enum cx231xx_itype {
++ CX231XX_VMUX_COMPOSITE1 = 1,
++ CX231XX_VMUX_SVIDEO,
++ CX231XX_VMUX_TELEVISION,
++ CX231XX_VMUX_CABLE,
++ CX231XX_RADIO,
++ CX231XX_VMUX_DVB,
++ CX231XX_VMUX_DEBUG
++};
++
++enum cx231xx_v_input {
++ CX231XX_VIN_1_1 = 0x1,
++ CX231XX_VIN_2_1,
++ CX231XX_VIN_3_1,
++ CX231XX_VIN_4_1,
++ CX231XX_VIN_1_2 = 0x01,
++ CX231XX_VIN_2_2,
++ CX231XX_VIN_3_2,
++ CX231XX_VIN_1_3 = 0x1,
++ CX231XX_VIN_2_3,
++ CX231XX_VIN_3_3,
++};
++
++/* cx231xx has two audio inputs: tuner and line in */
++enum cx231xx_amux {
++ /* This is the only entry for cx231xx tuner input */
++ CX231XX_AMUX_VIDEO, /* cx231xx tuner */
++ CX231XX_AMUX_LINE_IN, /* Line In */
++};
++
++struct cx231xx_reg_seq {
++ unsigned char bit;
++ unsigned char val;
++ int sleep;
++};
++
++struct cx231xx_input {
++ enum cx231xx_itype type;
++ unsigned int vmux;
++ enum cx231xx_amux amux;
++ struct cx231xx_reg_seq *gpio;
++};
++
++#define INPUT(nr) (&cx231xx_boards[dev->model].input[nr])
++
++enum cx231xx_decoder {
++ CX231XX_NODECODER,
++ CX231XX_AVDECODER
++};
++
++enum CX231XX_I2C_MASTER_PORT {
++ I2C_0 = 0,
++ I2C_1 = 1,
++ I2C_2 = 2,
++ I2C_3 = 3
++};
++
++struct cx231xx_board {
++ char *name;
++ int vchannels;
++ int tuner_type;
++ int tuner_addr;
++ v4l2_std_id norm; /* tv norm */
++
++ /* demod related */
++ int demod_addr;
++ u8 demod_xfer_mode; /* 0 - Serial; 1 - parallel */
++
++ /* GPIO Pins */
++ struct cx231xx_reg_seq *dvb_gpio;
++ struct cx231xx_reg_seq *suspend_gpio;
++ struct cx231xx_reg_seq *tuner_gpio;
++ u8 tuner_sif_gpio;
++ u8 tuner_scl_gpio;
++ u8 tuner_sda_gpio;
++
++ /* PIN ctrl */
++ u32 ctl_pin_status_mask;
++ u8 agc_analog_digital_select_gpio;
++ u32 gpio_pin_status_mask;
++
++ /* i2c masters */
++ u8 tuner_i2c_master;
++ u8 demod_i2c_master;
++
++ unsigned int max_range_640_480:1;
++ unsigned int has_dvb:1;
++ unsigned int valid:1;
++
++ unsigned char xclk, i2c_speed;
++
++ enum cx231xx_decoder decoder;
++
++ struct cx231xx_input input[MAX_CX231XX_INPUT];
++ struct cx231xx_input radio;
++ IR_KEYTAB_TYPE *ir_codes;
++};
++
++/* device states */
++enum cx231xx_dev_state {
++ DEV_INITIALIZED = 0x01,
++ DEV_DISCONNECTED = 0x02,
++ DEV_MISCONFIGURED = 0x04,
++};
++
++enum AFE_MODE {
++ AFE_MODE_LOW_IF,
++ AFE_MODE_BASEBAND,
++ AFE_MODE_EU_HI_IF,
++ AFE_MODE_US_HI_IF,
++ AFE_MODE_JAPAN_HI_IF
++};
++
++enum AUDIO_INPUT {
++ AUDIO_INPUT_MUTE,
++ AUDIO_INPUT_LINE,
++ AUDIO_INPUT_TUNER_TV,
++ AUDIO_INPUT_SPDIF,
++ AUDIO_INPUT_TUNER_FM
++};
++
++#define CX231XX_AUDIO_BUFS 5
++#define CX231XX_NUM_AUDIO_PACKETS 64
++#define CX231XX_CAPTURE_STREAM_EN 1
++#define CX231XX_STOP_AUDIO 0
++#define CX231XX_START_AUDIO 1
++
++/* cx231xx extensions */
++#define CX231XX_AUDIO 0x10
++#define CX231XX_DVB 0x20
++
++struct cx231xx_audio {
++ char name[50];
++ char *transfer_buffer[CX231XX_AUDIO_BUFS];
++ struct urb *urb[CX231XX_AUDIO_BUFS];
++ struct usb_device *udev;
++ unsigned int capture_transfer_done;
++ struct snd_pcm_substream *capture_pcm_substream;
++
++ unsigned int hwptr_done_capture;
++ struct snd_card *sndcard;
++
++ int users, shutdown;
++ enum cx231xx_stream_state capture_stream;
++ spinlock_t slock;
++
++ int alt; /* alternate */
++ int max_pkt_size; /* max packet size of isoc transaction */
++ int num_alt; /* Number of alternative settings */
++ unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
++ u16 end_point_addr;
++};
++
++struct cx231xx;
++
++struct cx231xx_fh {
++ struct cx231xx *dev;
++ unsigned int stream_on:1; /* Locks streams */
++ int radio;
++
++ struct videobuf_queue vb_vidq;
++
++ enum v4l2_buf_type type;
++};
++
++/*****************************************************************/
++/* set/get i2c */
++/* 00--1Mb/s, 01-400kb/s, 10--100kb/s, 11--5Mb/s */
++#define I2C_SPEED_1M 0x0
++#define I2C_SPEED_400K 0x1
++#define I2C_SPEED_100K 0x2
++#define I2C_SPEED_5M 0x3
++
++/* 0-- STOP transaction */
++#define I2C_STOP 0x0
++/* 1-- do not transmit STOP at end of transaction */
++#define I2C_NOSTOP 0x1
++/* 1--alllow slave to insert clock wait states */
++#define I2C_SYNC 0x1
++
++struct cx231xx_i2c {
++ struct cx231xx *dev;
++
++ int nr;
++
++ /* i2c i/o */
++ struct i2c_adapter i2c_adap;
++ struct i2c_algo_bit_data i2c_algo;
++ struct i2c_client i2c_client;
++ u32 i2c_rc;
++
++ /* different settings for each bus */
++ u8 i2c_period;
++ u8 i2c_nostop;
++ u8 i2c_reserve;
++};
++
++struct cx231xx_i2c_xfer_data {
++ u8 dev_addr;
++ u8 direction; /* 1 - IN, 0 - OUT */
++ u8 saddr_len; /* sub address len */
++ u16 saddr_dat; /* sub addr data */
++ u8 buf_size; /* buffer size */
++ u8 *p_buffer; /* pointer to the buffer */
++};
++
++struct VENDOR_REQUEST_IN {
++ u8 bRequest;
++ u16 wValue;
++ u16 wIndex;
++ u16 wLength;
++ u8 direction;
++ u8 bData;
++ u8 *pBuff;
++};
++
++struct cx231xx_ctrl {
++ struct v4l2_queryctrl v;
++ u32 off;
++ u32 reg;
++ u32 mask;
++ u32 shift;
++};
++
++enum TRANSFER_TYPE {
++ Raw_Video = 0,
++ Audio,
++ Vbi, /* VANC */
++ Sliced_cc, /* HANC */
++ TS1_serial_mode,
++ TS2,
++ TS1_parallel_mode
++} ;
++
++struct cx231xx_video_mode {
++ /* Isoc control struct */
++ struct cx231xx_dmaqueue vidq;
++ struct cx231xx_usb_isoc_ctl isoc_ctl;
++ spinlock_t slock;
++
++ /* usb transfer */
++ int alt; /* alternate */
++ int max_pkt_size; /* max packet size of isoc transaction */
++ int num_alt; /* Number of alternative settings */
++ unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
++ u16 end_point_addr;
++};
++
++/* main device struct */
++struct cx231xx {
++ /* generic device properties */
++ char name[30]; /* name (including minor) of the device */
++ int model; /* index in the device_data struct */
++ int devno; /* marks the number of this device */
++
++ struct cx231xx_board board;
++
++ unsigned int stream_on:1; /* Locks streams */
++ unsigned int vbi_stream_on:1; /* Locks streams for VBI */
++ unsigned int has_audio_class:1;
++ unsigned int has_alsa_audio:1;
++
++ struct cx231xx_fmt *format;
++
++ struct v4l2_device v4l2_dev;
++ struct v4l2_subdev *sd_cx25840;
++ struct v4l2_subdev *sd_tuner;
++
++ struct cx231xx_IR *ir;
++
++ struct list_head devlist;
++
++ int tuner_type; /* type of the tuner */
++ int tuner_addr; /* tuner address */
++
++ /* I2C adapters: Master 1 & 2 (External) & Master 3 (Internal only) */
++ struct cx231xx_i2c i2c_bus[3];
++ unsigned int xc_fw_load_done:1;
++ struct mutex gpio_i2c_lock;
++
++ /* video for linux */
++ int users; /* user count for exclusive use */
++ struct video_device *vdev; /* video for linux device struct */
++ v4l2_std_id norm; /* selected tv norm */
++ int ctl_freq; /* selected frequency */
++ unsigned int ctl_ainput; /* selected audio input */
++ int mute;
++ int volume;
++
++ /* frame properties */
++ int width; /* current frame width */
++ int height; /* current frame height */
++ unsigned hscale; /* horizontal scale factor (see datasheet) */
++ unsigned vscale; /* vertical scale factor (see datasheet) */
++ int interlaced; /* 1=interlace fileds, 0=just top fileds */
++
++ struct cx231xx_audio adev;
++
++ /* states */
++ enum cx231xx_dev_state state;
++
++ struct work_struct request_module_wk;
++
++ /* locks */
++ struct mutex lock;
++ struct mutex ctrl_urb_lock; /* protects urb_buf */
++ struct list_head inqueue, outqueue;
++ wait_queue_head_t open, wait_frame, wait_stream;
++ struct video_device *vbi_dev;
++ struct video_device *radio_dev;
++
++ unsigned char eedata[256];
++
++ struct cx231xx_video_mode video_mode;
++ struct cx231xx_video_mode vbi_mode;
++ struct cx231xx_video_mode sliced_cc_mode;
++ struct cx231xx_video_mode ts1_mode;
++
++ struct usb_device *udev; /* the usb device */
++ char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */
++
++ /* helper funcs that call usb_control_msg */
++ int (*cx231xx_read_ctrl_reg) (struct cx231xx *dev, u8 req, u16 reg,
++ char *buf, int len);
++ int (*cx231xx_write_ctrl_reg) (struct cx231xx *dev, u8 req, u16 reg,
++ char *buf, int len);
++ int (*cx231xx_send_usb_command) (struct cx231xx_i2c *i2c_bus,
++ struct cx231xx_i2c_xfer_data *req_data);
++ int (*cx231xx_gpio_i2c_read) (struct cx231xx *dev, u8 dev_addr,
++ u8 *buf, u8 len);
++ int (*cx231xx_gpio_i2c_write) (struct cx231xx *dev, u8 dev_addr,
++ u8 *buf, u8 len);
++
++ int (*cx231xx_set_analog_freq) (struct cx231xx *dev, u32 freq);
++ int (*cx231xx_reset_analog_tuner) (struct cx231xx *dev);
++
++ enum cx231xx_mode mode;
++
++ struct cx231xx_dvb *dvb;
++
++ /* Cx231xx supported PCB config's */
++ struct pcb_config current_pcb_config;
++ u8 current_scenario_idx;
++ u8 interface_count;
++ u8 max_iad_interface_count;
++
++ /* GPIO related register direction and values */
++ u32 gpio_dir;
++ u32 gpio_val;
++
++ /* Power Modes */
++ int power_mode;
++
++ /* afe parameters */
++ enum AFE_MODE afe_mode;
++ u32 afe_ref_count;
++
++ /* video related parameters */
++ u32 video_input;
++ u32 active_mode;
++ u8 vbi_or_sliced_cc_mode; /* 0 - vbi ; 1 - sliced cc mode */
++ enum cx231xx_std_mode std_mode; /* 0 - Air; 1 - cable */
++
++};
++
++#define cx25840_call(cx231xx, o, f, args...) \
++ v4l2_subdev_call(cx231xx->sd_cx25840, o, f, ##args)
++#define tuner_call(cx231xx, o, f, args...) \
++ v4l2_subdev_call(cx231xx->sd_tuner, o, f, ##args)
++#define call_all(dev, o, f, args...) \
++ v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args)
++
++struct cx231xx_ops {
++ struct list_head next;
++ char *name;
++ int id;
++ int (*init) (struct cx231xx *);
++ int (*fini) (struct cx231xx *);
++};
++
++/* call back functions in dvb module */
++int cx231xx_set_analog_freq(struct cx231xx *dev, u32 freq);
++int cx231xx_reset_analog_tuner(struct cx231xx *dev);
++
++/* Provided by cx231xx-i2c.c */
++void cx231xx_do_i2c_scan(struct cx231xx *dev, struct i2c_client *c);
++int cx231xx_i2c_register(struct cx231xx_i2c *bus);
++int cx231xx_i2c_unregister(struct cx231xx_i2c *bus);
++
++/* Internal block control functions */
++int cx231xx_read_i2c_data(struct cx231xx *dev, u8 dev_addr,
++ u16 saddr, u8 saddr_len, u32 *data, u8 data_len);
++int cx231xx_write_i2c_data(struct cx231xx *dev, u8 dev_addr,
++ u16 saddr, u8 saddr_len, u32 data, u8 data_len);
++int cx231xx_reg_mask_write(struct cx231xx *dev, u8 dev_addr, u8 size,
++ u16 register_address, u8 bit_start, u8 bit_end,
++ u32 value);
++int cx231xx_read_modify_write_i2c_dword(struct cx231xx *dev, u8 dev_addr,
++ u16 saddr, u32 mask, u32 value);
++u32 cx231xx_set_field(u32 field_mask, u32 data);
++
++/* afe related functions */
++int cx231xx_afe_init_super_block(struct cx231xx *dev, u32 ref_count);
++int cx231xx_afe_init_channels(struct cx231xx *dev);
++int cx231xx_afe_setup_AFE_for_baseband(struct cx231xx *dev);
++int cx231xx_afe_set_input_mux(struct cx231xx *dev, u32 input_mux);
++int cx231xx_afe_set_mode(struct cx231xx *dev, enum AFE_MODE mode);
++int cx231xx_afe_update_power_control(struct cx231xx *dev,
++ enum AV_MODE avmode);
++int cx231xx_afe_adjust_ref_count(struct cx231xx *dev, u32 video_input);
++
++/* i2s block related functions */
++int cx231xx_i2s_blk_initialize(struct cx231xx *dev);
++int cx231xx_i2s_blk_update_power_control(struct cx231xx *dev,
++ enum AV_MODE avmode);
++int cx231xx_i2s_blk_set_audio_input(struct cx231xx *dev, u8 audio_input);
++
++/* DIF related functions */
++int cx231xx_dif_configure_C2HH_for_low_IF(struct cx231xx *dev, u32 mode,
++ u32 function_mode, u32 standard);
++int cx231xx_dif_set_standard(struct cx231xx *dev, u32 standard);
++int cx231xx_tuner_pre_channel_change(struct cx231xx *dev);
++int cx231xx_tuner_post_channel_change(struct cx231xx *dev);
++
++/* video parser functions */
++u8 cx231xx_find_next_SAV_EAV(u8 *p_buffer, u32 buffer_size,
++ u32 *p_bytes_used);
++u8 cx231xx_find_boundary_SAV_EAV(u8 *p_buffer, u8 *partial_buf,
++ u32 *p_bytes_used);
++int cx231xx_do_copy(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
++ u8 *p_buffer, u32 bytes_to_copy);
++void cx231xx_reset_video_buffer(struct cx231xx *dev,
++ struct cx231xx_dmaqueue *dma_q);
++u8 cx231xx_is_buffer_done(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q);
++u32 cx231xx_copy_video_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
++ u8 *p_line, u32 length, int field_number);
++u32 cx231xx_get_video_line(struct cx231xx *dev, struct cx231xx_dmaqueue *dma_q,
++ u8 sav_eav, u8 *p_buffer, u32 buffer_size);
++void cx231xx_swab(u16 *from, u16 *to, u16 len);
++
++/* Provided by cx231xx-core.c */
++
++u32 cx231xx_request_buffers(struct cx231xx *dev, u32 count);
++void cx231xx_queue_unusedframes(struct cx231xx *dev);
++void cx231xx_release_buffers(struct cx231xx *dev);
++
++/* read from control pipe */
++int cx231xx_read_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg,
++ char *buf, int len);
++
++/* write to control pipe */
++int cx231xx_write_ctrl_reg(struct cx231xx *dev, u8 req, u16 reg,
++ char *buf, int len);
++int cx231xx_mode_register(struct cx231xx *dev, u16 address, u32 mode);
++
++int cx231xx_send_vendor_cmd(struct cx231xx *dev,
++ struct VENDOR_REQUEST_IN *ven_req);
++int cx231xx_send_usb_command(struct cx231xx_i2c *i2c_bus,
++ struct cx231xx_i2c_xfer_data *req_data);
++
++/* Gpio related functions */
++int cx231xx_send_gpio_cmd(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val,
++ u8 len, u8 request, u8 direction);
++int cx231xx_set_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val);
++int cx231xx_get_gpio_bit(struct cx231xx *dev, u32 gpio_bit, u8 *gpio_val);
++int cx231xx_set_gpio_value(struct cx231xx *dev, int pin_number, int pin_value);
++int cx231xx_set_gpio_direction(struct cx231xx *dev, int pin_number,
++ int pin_value);
++
++int cx231xx_gpio_i2c_start(struct cx231xx *dev);
++int cx231xx_gpio_i2c_end(struct cx231xx *dev);
++int cx231xx_gpio_i2c_write_byte(struct cx231xx *dev, u8 data);
++int cx231xx_gpio_i2c_read_byte(struct cx231xx *dev, u8 *buf);
++int cx231xx_gpio_i2c_read_ack(struct cx231xx *dev);
++int cx231xx_gpio_i2c_write_ack(struct cx231xx *dev);
++int cx231xx_gpio_i2c_write_nak(struct cx231xx *dev);
++
++int cx231xx_gpio_i2c_read(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len);
++int cx231xx_gpio_i2c_write(struct cx231xx *dev, u8 dev_addr, u8 *buf, u8 len);
++
++/* audio related functions */
++int cx231xx_set_audio_decoder_input(struct cx231xx *dev,
++ enum AUDIO_INPUT audio_input);
++
++int cx231xx_capture_start(struct cx231xx *dev, int start, u8 media_type);
++int cx231xx_resolution_set(struct cx231xx *dev);
++int cx231xx_set_video_alternate(struct cx231xx *dev);
++int cx231xx_set_alt_setting(struct cx231xx *dev, u8 index, u8 alt);
++int cx231xx_init_isoc(struct cx231xx *dev, int max_packets,
++ int num_bufs, int max_pkt_size,
++ int (*isoc_copy) (struct cx231xx *dev,
++ struct urb *urb));
++void cx231xx_uninit_isoc(struct cx231xx *dev);
++int cx231xx_set_mode(struct cx231xx *dev, enum cx231xx_mode set_mode);
++int cx231xx_gpio_set(struct cx231xx *dev, struct cx231xx_reg_seq *gpio);
++
++/* Device list functions */
++void cx231xx_release_resources(struct cx231xx *dev);
++void cx231xx_release_analog_resources(struct cx231xx *dev);
++int cx231xx_register_analog_devices(struct cx231xx *dev);
++void cx231xx_remove_from_devlist(struct cx231xx *dev);
++void cx231xx_add_into_devlist(struct cx231xx *dev);
++struct cx231xx *cx231xx_get_device(int minor,
++ enum v4l2_buf_type *fh_type, int *has_radio);
++void cx231xx_init_extension(struct cx231xx *dev);
++void cx231xx_close_extension(struct cx231xx *dev);
++
++/* hardware init functions */
++int cx231xx_dev_init(struct cx231xx *dev);
++void cx231xx_dev_uninit(struct cx231xx *dev);
++void cx231xx_config_i2c(struct cx231xx *dev);
++int cx231xx_config(struct cx231xx *dev);
++
++/* Stream control functions */
++int cx231xx_start_stream(struct cx231xx *dev, u32 ep_mask);
++int cx231xx_stop_stream(struct cx231xx *dev, u32 ep_mask);
++
++int cx231xx_initialize_stream_xfer(struct cx231xx *dev, u32 media_type);
++
++/* Power control functions */
++int cx231xx_set_power_mode(struct cx231xx *dev, enum AV_MODE mode);
++int cx231xx_power_suspend(struct cx231xx *dev);
++
++/* chip specific control functions */
++int cx231xx_init_ctrl_pin_status(struct cx231xx *dev);
++int cx231xx_set_agc_analog_digital_mux_select(struct cx231xx *dev,
++ u8 analog_or_digital);
++int cx231xx_enable_i2c_for_tuner(struct cx231xx *dev, u8 I2CIndex);
++
++/* video audio decoder related functions */
++void video_mux(struct cx231xx *dev, int index);
++int cx231xx_set_video_input_mux(struct cx231xx *dev, u8 input);
++int cx231xx_set_decoder_video_input(struct cx231xx *dev, u8 pin_type, u8 input);
++int cx231xx_do_mode_ctrl_overrides(struct cx231xx *dev);
++int cx231xx_set_audio_input(struct cx231xx *dev, u8 input);
++void get_scale(struct cx231xx *dev,
++ unsigned int width, unsigned int height,
++ unsigned int *hscale, unsigned int *vscale);
++
++/* Provided by cx231xx-video.c */
++int cx231xx_register_extension(struct cx231xx_ops *dev);
++void cx231xx_unregister_extension(struct cx231xx_ops *dev);
++void cx231xx_init_extension(struct cx231xx *dev);
++void cx231xx_close_extension(struct cx231xx *dev);
++
++/* Provided by cx231xx-cards.c */
++extern void cx231xx_pre_card_setup(struct cx231xx *dev);
++extern void cx231xx_card_setup(struct cx231xx *dev);
++extern struct cx231xx_board cx231xx_boards[];
++extern struct usb_device_id cx231xx_id_table[];
++extern const unsigned int cx231xx_bcount;
++void cx231xx_set_ir(struct cx231xx *dev, struct IR_i2c *ir);
++int cx231xx_tuner_callback(void *ptr, int component, int command, int arg);
++
++/* Provided by cx231xx-input.c */
++int cx231xx_ir_init(struct cx231xx *dev);
++int cx231xx_ir_fini(struct cx231xx *dev);
++
++/* printk macros */
++
++#define cx231xx_err(fmt, arg...) do {\
++ printk(KERN_ERR fmt , ##arg); } while (0)
++
++#define cx231xx_errdev(fmt, arg...) do {\
++ printk(KERN_ERR "%s: "fmt,\
++ dev->name , ##arg); } while (0)
++
++#define cx231xx_info(fmt, arg...) do {\
++ printk(KERN_INFO "%s: "fmt,\
++ dev->name , ##arg); } while (0)
++#define cx231xx_warn(fmt, arg...) do {\
++ printk(KERN_WARNING "%s: "fmt,\
++ dev->name , ##arg); } while (0)
++
++static inline unsigned int norm_maxw(struct cx231xx *dev)
++{
++ if (dev->board.max_range_640_480)
++ return 640;
++ else
++ return 720;
++}
++
++static inline unsigned int norm_maxh(struct cx231xx *dev)
++{
++ if (dev->board.max_range_640_480)
++ return 480;
++ else
++ return (dev->norm & V4L2_STD_625_50) ? 576 : 480;
++}
++#endif
+diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c
+index 93d74be..2f846f5 100644
+--- a/drivers/media/video/cx25840/cx25840-audio.c
++++ b/drivers/media/video/cx25840/cx25840-audio.c
+@@ -32,7 +32,7 @@ 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 */
+- if (!state->is_cx23885)
++ if (!state->is_cx23885 && !state->is_cx231xx)
+ cx25840_write(client, 0x127, 0x50);
+
+ if (state->aud_input != CX25840_AUDIO_SERIAL) {
+@@ -43,11 +43,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
+ * so avoid destroying registers. */
+ break;
+ }
+- /* VID_PLL and AUX_PLL */
+- cx25840_write4(client, 0x108, 0x1006040f);
+
+- /* AUX_PLL_FRAC */
+- cx25840_write4(client, 0x110, 0x01bb39ee);
++ if (!state->is_cx231xx) {
++ /* VID_PLL and AUX_PLL */
++ cx25840_write4(client, 0x108, 0x1006040f);
++
++ /* AUX_PLL_FRAC */
++ cx25840_write4(client, 0x110, 0x01bb39ee);
++ }
+
+ if (state->is_cx25836)
+ break;
+@@ -64,11 +67,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
+ * so avoid destroying registers. */
+ break;
+ }
+- /* VID_PLL and AUX_PLL */
+- cx25840_write4(client, 0x108, 0x1009040f);
+
+- /* AUX_PLL_FRAC */
+- cx25840_write4(client, 0x110, 0x00ec6bd6);
++ if (!state->is_cx231xx) {
++ /* VID_PLL and AUX_PLL */
++ cx25840_write4(client, 0x108, 0x1009040f);
++
++ /* AUX_PLL_FRAC */
++ cx25840_write4(client, 0x110, 0x00ec6bd6);
++ }
+
+ if (state->is_cx25836)
+ break;
+@@ -85,11 +91,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
+ * so avoid destroying registers. */
+ break;
+ }
+- /* VID_PLL and AUX_PLL */
+- cx25840_write4(client, 0x108, 0x100a040f);
+
+- /* AUX_PLL_FRAC */
+- cx25840_write4(client, 0x110, 0x0098d6e5);
++ if (!state->is_cx231xx) {
++ /* VID_PLL and AUX_PLL */
++ cx25840_write4(client, 0x108, 0x100a040f);
++
++ /* AUX_PLL_FRAC */
++ cx25840_write4(client, 0x110, 0x0098d6e5);
++ }
+
+ if (state->is_cx25836)
+ break;
+@@ -108,11 +117,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
+ * so avoid destroying registers. */
+ break;
+ }
+- /* VID_PLL and AUX_PLL */
+- cx25840_write4(client, 0x108, 0x1e08040f);
+
+- /* AUX_PLL_FRAC */
+- cx25840_write4(client, 0x110, 0x012a0869);
++ if (!state->is_cx231xx) {
++ /* VID_PLL and AUX_PLL */
++ cx25840_write4(client, 0x108, 0x1e08040f);
++
++ /* AUX_PLL_FRAC */
++ cx25840_write4(client, 0x110, 0x012a0869);
++ }
+
+ if (state->is_cx25836)
+ break;
+@@ -136,11 +148,14 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
+ break;
+ }
+
+- /* VID_PLL and AUX_PLL */
+- cx25840_write4(client, 0x108, 0x1809040f);
+
+- /* AUX_PLL_FRAC */
+- cx25840_write4(client, 0x110, 0x00ec6bd6);
++ if (!state->is_cx231xx) {
++ /* VID_PLL and AUX_PLL */
++ cx25840_write4(client, 0x108, 0x1809040f);
++
++ /* AUX_PLL_FRAC */
++ cx25840_write4(client, 0x110, 0x00ec6bd6);
++ }
+
+ if (state->is_cx25836)
+ break;
+@@ -155,7 +170,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
+ break;
+
+ case 48000:
+- if (!state->is_cx23885) {
++ if (!state->is_cx23885 && !state->is_cx231xx) {
+ /* VID_PLL and AUX_PLL */
+ cx25840_write4(client, 0x108, 0x180a040f);
+
+@@ -166,7 +181,7 @@ static int set_audclk_freq(struct i2c_client *client, u32 freq)
+ if (state->is_cx25836)
+ break;
+
+- if (!state->is_cx23885) {
++ if (!state->is_cx23885 && !state->is_cx231xx) {
+ /* src1_ctl */
+ cx25840_write4(client, 0x8f8, 0x08018000);
+
+@@ -227,10 +242,9 @@ 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 */
++ /* Ensure the controller is running when we exit */
++ if (state->is_cx23885 || state->is_cx231xx)
+ cx25840_and_or(client, 0x803, ~0x10, 0x10);
+- }
+ }
+
+ static int get_volume(struct i2c_client *client)
+diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
+index 737ee4e..f8ed3c0 100644
+--- a/drivers/media/video/cx25840/cx25840-core.c
++++ b/drivers/media/video/cx25840/cx25840-core.c
+@@ -345,6 +345,77 @@ static void cx23885_initialize(struct i2c_client *client)
+
+ /* ----------------------------------------------------------------------- */
+
++static void cx231xx_initialize(struct i2c_client *client)
++{
++ DEFINE_WAIT(wait);
++ struct cx25840_state *state = to_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);
++
++ /* Disable DIF bypass */
++ cx25840_write4(client, 0x33c, 0x00000001);
++
++ /* DIF Src phase inc */
++ cx25840_write4(client, 0x340, 0x0df7df83);
++
++ /* Luma */
++ cx25840_write4(client, 0x414, 0x00107d12);
++
++ /* Chroma */
++ cx25840_write4(client, 0x420, 0x3d008282);
++
++ /* 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);
++
++ /* 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_std_setup(client);
++
++ /* (re)set input */
++ set_input(client, state->vid_input, state->aud_input);
++
++ /* start microcontroller */
++ cx25840_and_or(client, 0x803, ~0x10, 0x10);
++}
++
++/* ----------------------------------------------------------------------- */
++
+ void cx25840_std_setup(struct i2c_client *client)
+ {
+ struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+@@ -414,39 +485,41 @@ void cx25840_std_setup(struct i2c_client *client)
+ }
+
+ /* DEBUG: Displays configured PLL frequency */
+- pll_int = cx25840_read(client, 0x108);
+- pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff;
+- pll_post = cx25840_read(client, 0x109);
+- v4l_dbg(1, cx25840_debug, client,
+- "PLL regs = int: %u, frac: %u, post: %u\n",
+- pll_int, pll_frac, pll_post);
+-
+- if (pll_post) {
+- int fin, fsc;
+- int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L;
+-
+- pll /= pll_post;
+- v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n",
+- pll / 1000000, pll % 1000000);
+- v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n",
+- pll / 8000000, (pll / 8) % 1000000);
+-
+- fin = ((u64)src_decimation * pll) >> 12;
++ if (!state->is_cx231xx) {
++ pll_int = cx25840_read(client, 0x108);
++ pll_frac = cx25840_read4(client, 0x10c) & 0x1ffffff;
++ pll_post = cx25840_read(client, 0x109);
+ v4l_dbg(1, cx25840_debug, client,
+- "ADC Sampling freq = %d.%06d MHz\n",
+- fin / 1000000, fin % 1000000);
+-
+- fsc = (((u64)sc) * pll) >> 24L;
+- v4l_dbg(1, cx25840_debug, client,
+- "Chroma sub-carrier freq = %d.%06d MHz\n",
+- fsc / 1000000, fsc % 1000000);
+-
+- v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %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,
+- src_decimation, burst, luma_lpf, uv_lpf, comb, sc);
++ "PLL regs = int: %u, frac: %u, post: %u\n",
++ pll_int, pll_frac, pll_post);
++
++ if (pll_post) {
++ int fin, fsc;
++ int pll = (28636363L * ((((u64)pll_int) << 25L) + pll_frac)) >> 25L;
++
++ pll /= pll_post;
++ v4l_dbg(1, cx25840_debug, client, "PLL = %d.%06d MHz\n",
++ pll / 1000000, pll % 1000000);
++ v4l_dbg(1, cx25840_debug, client, "PLL/8 = %d.%06d MHz\n",
++ pll / 8000000, (pll / 8) % 1000000);
++
++ fin = ((u64)src_decimation * pll) >> 12;
++ v4l_dbg(1, cx25840_debug, client,
++ "ADC Sampling freq = %d.%06d MHz\n",
++ fin / 1000000, fin % 1000000);
++
++ fsc = (((u64)sc) * pll) >> 24L;
++ v4l_dbg(1, cx25840_debug, client,
++ "Chroma sub-carrier freq = %d.%06d MHz\n",
++ fsc / 1000000, fsc % 1000000);
++
++ v4l_dbg(1, cx25840_debug, client, "hblank %i, hactive %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,
++ src_decimation, burst, luma_lpf, uv_lpf, comb, sc);
++ }
+ }
+
+ /* Sets horizontal blanking delay and active lines */
+@@ -596,7 +669,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
+ * configuration in reg (for the cx23885) so we have no
+ * need to attempt to flip bits for earlier av decoders.
+ */
+- if (!state->is_cx23885) {
++ if (!state->is_cx23885 && !state->is_cx231xx) {
+ switch (aud_input) {
+ case CX25840_AUDIO_SERIAL:
+ /* do nothing, use serial audio input */
+@@ -619,7 +692,7 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
+ /* Set INPUT_MODE to Composite (0) or S-Video (1) */
+ cx25840_and_or(client, 0x401, ~0x6, is_composite ? 0 : 0x02);
+
+- if (!state->is_cx23885) {
++ if (!state->is_cx23885 && !state->is_cx231xx) {
+ /* 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 */
+@@ -659,6 +732,19 @@ static int set_input(struct i2c_client *client, enum cx25840_video_input vid_inp
+ */
+ cx25840_write(client, 0x918, 0xa0);
+ cx25840_write(client, 0x919, 0x01);
++ } else if (state->is_cx231xx) {
++ /* Audio channel 1 src : Parallel 1 */
++ cx25840_write(client, 0x124, 0x03);
++
++ /* 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;
+@@ -1118,6 +1204,8 @@ static int cx25840_init(struct v4l2_subdev *sd, u32 val)
+ cx25836_initialize(client);
+ else if (state->is_cx23885)
+ cx23885_initialize(client);
++ else if (state->is_cx231xx)
++ cx231xx_initialize(client);
+ else
+ cx25840_initialize(client);
+ }
+@@ -1159,7 +1247,7 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
+ v4l_dbg(1, cx25840_debug, client, "%s output\n",
+ enable ? "enable" : "disable");
+ if (enable) {
+- if (state->is_cx23885) {
++ if (state->is_cx23885 || state->is_cx231xx) {
+ u8 v = (cx25840_read(client, 0x421) | 0x0b);
+ cx25840_write(client, 0x421, v);
+ } else {
+@@ -1169,7 +1257,7 @@ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
+ state->is_cx25836 ? 0x04 : 0x07);
+ }
+ } else {
+- if (state->is_cx23885) {
++ if (state->is_cx23885 || state->is_cx231xx) {
+ u8 v = cx25840_read(client, 0x421) & ~(0x0b);
+ cx25840_write(client, 0x421, v);
+ } else {
+@@ -1350,6 +1438,8 @@ static int cx25840_reset(struct v4l2_subdev *sd, u32 val)
+ cx25836_initialize(client);
+ else if (state->is_cx23885)
+ cx23885_initialize(client);
++ else if (state->is_cx231xx)
++ cx231xx_initialize(client);
+ else
+ cx25840_initialize(client);
+ return 0;
+@@ -1449,6 +1539,8 @@ static int cx25840_probe(struct i2c_client *client,
+ id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
+ } else if (device_id == 0x1313) {
+ id = V4L2_IDENT_CX25836 + ((device_id >> 4) & 0xf) - 6;
++ } else if ((device_id & 0xfff0) == 0x5A30) {
++ id = V4L2_IDENT_CX25840 + ((device_id >> 4) & 0xf);
+ }
+ else {
+ v4l_dbg(1, cx25840_debug, client, "cx25840 not found\n");
+@@ -1471,6 +1563,7 @@ static int cx25840_probe(struct i2c_client *client,
+ state->c = client;
+ state->is_cx25836 = ((device_id & 0xff00) == 0x8300);
+ state->is_cx23885 = (device_id == 0x0000) || (device_id == 0x1313);
++ state->is_cx231xx = (device_id == 0x5a3e);
+ state->vid_input = CX25840_COMPOSITE7;
+ state->aud_input = CX25840_AUDIO8;
+ state->audclk_freq = 48000;
+diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h
+index 9ad0eb8..814b565 100644
+--- a/drivers/media/video/cx25840/cx25840-core.h
++++ b/drivers/media/video/cx25840/cx25840-core.h
+@@ -50,6 +50,7 @@ struct cx25840_state {
+ u32 rev;
+ int is_cx25836;
+ int is_cx23885;
++ int is_cx231xx;
+ 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/drivers/media/video/cx25840/cx25840-firmware.c b/drivers/media/video/cx25840/cx25840-firmware.c
+index 0b2dceb..0df53b0 100644
+--- a/drivers/media/video/cx25840/cx25840-firmware.c
++++ b/drivers/media/video/cx25840/cx25840-firmware.c
+@@ -25,6 +25,7 @@
+
+ #define FWFILE "v4l-cx25840.fw"
+ #define FWFILE_CX23885 "v4l-cx23885-avcore-01.fw"
++#define FWFILE_CX231XX "v4l-cx231xx-avcore-01.fw"
+
+ /*
+ * Mike Isely <isely@pobox.com> - The FWSEND parameter controls the
+@@ -96,9 +97,17 @@ int cx25840_loadfw(struct i2c_client *client)
+ u8 buffer[FWSEND];
+ const u8 *ptr;
+ int size, retval;
++ int MAX_BUF_SIZE = FWSEND;
+
+ if (state->is_cx23885)
+ firmware = FWFILE_CX23885;
++ else if (state->is_cx231xx)
++ firmware = FWFILE_CX231XX;
++
++ if ((state->is_cx231xx) && MAX_BUF_SIZE > 16) {
++ v4l_err(client, " Firmware download size changed to 16 bytes max length\n");
++ MAX_BUF_SIZE = 16; /* cx231xx cannot accept more than 16 bytes at a time */
++ }
+
+ if (request_firmware(&fw, firmware, FWDEV(client)) != 0) {
+ v4l_err(client, "unable to open firmware %s\n", firmware);
+@@ -113,7 +122,7 @@ int cx25840_loadfw(struct i2c_client *client)
+ size = fw->size;
+ ptr = fw->data;
+ while (size > 0) {
+- int len = min(FWSEND - 2, size);
++ int len = min(MAX_BUF_SIZE - 2, size);
+
+ memcpy(buffer + 2, ptr, len);
+
+diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h
+index f27604a..f9d48c9 100644
+--- a/include/linux/i2c-id.h
++++ b/include/linux/i2c-id.h
+@@ -88,6 +88,7 @@
+ #define I2C_HW_B_CX2341X 0x010020 /* Conexant CX2341X MPEG encoder cards */
+ #define I2C_HW_B_CX23885 0x010022 /* conexant 23885 based tv cards (bus1) */
+ #define I2C_HW_B_AU0828 0x010023 /* auvitek au0828 usb bridge */
++#define I2C_HW_B_CX231XX 0x010024 /* Conexant CX231XX USB based cards */
+ #define I2C_HW_B_HDPVR 0x010025 /* Hauppauge HD PVR */
+
+ /* --- SGI adapters */
+diff --git a/include/linux/mmc/sdio_ids.h b/include/linux/mmc/sdio_ids.h
+index ea1bf5b..3d7533d 100644
+--- a/include/linux/mmc/sdio_ids.h
++++ b/include/linux/mmc/sdio_ids.h
+@@ -24,6 +24,14 @@
+ */
+
+ #define SDIO_VENDOR_ID_MARVELL 0x02df
++#define SDIO_VENDOR_ID_SIANO 0x039a
++
+ #define SDIO_DEVICE_ID_MARVELL_LIBERTAS 0x9103
++#define SDIO_DEVICE_ID_SIANO_STELLAR 0x5347
++#define SDIO_DEVICE_ID_SIANO_NOVA_A0 0x1100
++#define SDIO_DEVICE_ID_SIANO_NOVA_B0 0x0201
++#define SDIO_DEVICE_ID_SIANO_NICE 0x0202
++#define SDIO_DEVICE_ID_SIANO_VEGA_A0 0x0300
++#define SDIO_DEVICE_ID_SIANO_VENICE 0x0301
+
+ #endif
diff --git a/linux-2.6-v4l-dvb-fixes.patch b/linux-2.6-v4l-dvb-fixes.patch
new file mode 100644
index 0000000..322148f
--- /dev/null
+++ b/linux-2.6-v4l-dvb-fixes.patch
@@ -0,0 +1,113215 @@
+Abylay Ospan (5):
+ V4L/DVB (10796): Add init code for NetUP Dual DVB-S2 CI card
+ V4L/DVB (10797): Add EEPROM code for NetUP Dual DVB-S2 CI card.
+ V4L/DVB (10798): Add CIMax(R) SP2 Common Interface code for NetUP Dual DVB-S2 CI card
+ V4L/DVB (11056): Bug fix in NetUP: restore high address lines in CI
+ V4L/DVB (11057): Fix CiMax stability in Netup Dual DVB-S2 CI
+
+Adam Baker (2):
+ V4L/DVB (10639): gspca - sq905: New subdriver.
+ V4L/DVB (10829): Support alternate resolutions for sq905
+
+Alan Cox (2):
+ V4L/DVB (11243): cx88: Missing failure checks
+ V4L/DVB (11244): pluto2: silence spew of card hung up messages
+
+Alan McIvor (1):
+ V4L/DVB (11124): Add support for ProVideo PV-183 to bttv
+
+Alexey Klimov (18):
+ V4L/DVB (10316): v4l/dvb: use usb_make_path in usb-radio drivers
+ V4L/DVB (10324): em28xx: Correct mailing list
+ V4L/DVB (10335): gspca - all subdrivers: Fix CodingStyle in sd_mod_init function.
+ V4L/DVB (10336): gspca - all subdrivers: Return ret instead of -1 in sd_mod_init.
+ V4L/DVB (10455): radio-mr800: codingstyle cleanups
+ V4L/DVB (10456): radio-mr800: place dev_err instead of dev_warn
+ V4L/DVB (10457): radio-mr800: add more dev_err messages in probe
+ V4L/DVB (10458): radio-mr800: move radio start and stop in one function
+ V4L/DVB (10459): radio-mr800: fix amradio_set_freq
+ V4L/DVB (10460): radio-mr800: add stereo support
+ V4L/DVB (10461): radio-mr800: add few lost mutex locks
+ V4L/DVB (10462): radio-mr800: increase version and add comments
+ V4L/DVB (10463): radio-mr800: fix checking of retval after usb_bulk_msg
+ V4L/DVB (10464): radio-si470x: use usb_make_path in usb-radio drivers
+ V4L/DVB (10465): dsbr100: Add few lost mutex locks.
+ V4L/DVB (10522): em28xx-audio: replace printk with em28xx_errdev
+ V4L/DVB (10946): radio-rtrack2: fix double mutex_unlock
+ V4L/DVB (10961): radio-terratec: remove linux/delay.h which hadn't been used.
+
+Andy Walls (44):
+ V4L/DVB (10274): cx18: Fix a PLL divisor update for the I2S master clock
+ V4L/DVB (10275): cx18: Additional debug to display outgoing mailbox parameters
+ V4L/DVB (10276): cx18, cx2341x, ivtv: Add AC-3 audio encoding control to cx18
+ V4L/DVB (10277): cx18, cx2341x: Fix bugs in cx18 AC3 control and comply with V4L2 spec
+ V4L/DVB (10278): cx18: Fix bad audio in first analog capture.
+ V4L/DVB (10279): cx18: Print driver version number when logging status
+ V4L/DVB (10280): cx18: Rename structure members: dev to pci_dev and v4l2dev to video_dev
+ V4L/DVB (10281): cx18: Conversion to new V4L2 framework: use v4l2_device object
+ V4L/DVB (10283): cx18: Call request_module() with proper argument types.
+ V4L/DVB (10284): cx18: Add initial entry for a Leadtek DVR3100 H hybrid card
+ V4L/DVB (10433): cx18: Defer A/V core initialization until a valid cx18_av_cmd arrives
+ V4L/DVB (10434): cx18: Smarter verification of CX18_AUDIO_ENABLE register writes
+ V4L/DVB (10435): cx18: Normalize APU after second APU firmware load
+ V4L/DVB (10436): cx18: Fix coding style of a switch statement per checkpatch.pl
+ V4L/DVB (10437): cx18: Remove an unused spinlock
+ V4L/DVB (10439): cx18: Clean-up and enable sliced VBI handling
+ V4L/DVB (10440): cx18: Fix presentation timestamp (PTS) for VBI buffers
+ V4L/DVB (10441): cx18: Fix VBI ioctl() handling and Raw/Sliced VBI state management
+ V4L/DVB (10442): cx18: Fixes for enforcing when Encoder Raw VBI params can be set
+ V4L/DVB (10443): cx18: Use correct line counts per field in firmware API call
+ V4L/DVB (10444): cx18: Fix sliced VBI PTS and fix artifacts in last raw line of field
+ V4L/DVB (10445): cx18: Process Raw VBI on a whole frame basis; fix VBI buffer size
+ V4L/DVB (10446): cx18: Finally get sliced VBI working - for 525 line 60 Hz systems at least
+ V4L/DVB (10755): cx18: Convert the integrated A/V decoder core interface to a v4l2_subdev
+ V4L/DVB (10756): cx18: Slim down instance handling, build names from v4l2_device.name
+ V4L/DVB (10757): cx18, v4l2-chip-ident: Finish conversion of AV decoder core to v4l2_subdev
+ V4L/DVB (10758): cx18: Convert I2C devices to v4l2_subdevices
+ V4L/DVB (10759): cx18: Convert GPIO connected functions to act as v4l2_subdevices
+ V4L/DVB (10760): cx18: Fix a memory leak of buffers used for sliced VBI insertion
+ V4L/DVB (10761): cx18: Change log lines for internal subdevs and fix tveeprom reads
+ V4L/DVB (10762): cx18: Get rid of unused variables related to video output
+ V4L/DVB (10763): cx18: Increment version number due to significant changes for v4l2_subdevs
+ V4L/DVB (10764): cx18: Disable AC3 controls as the firmware doesn't support AC3
+ V4L/DVB (10850): cx18: Use strlcpy() instead of strncpy() for temp eeprom i2c_client setup
+ V4L/DVB (10851): cx18: Fix a video scaling check problem introduced by sliced VBI changes
+ V4L/DVB (10852): cx18: Include cx18-audio.h in cx18-audio.c to eliminate s-parse warning
+ V4L/DVB (10853): cx18: Fix s-parse warnings and a logic error about extracting the VBI PTS
+ V4L/DVB (10854): cx18: Correct comments about vertical and horizontal blanking timings
+ V4L/DVB (10855): cx18: Fix VPS service register codes
+ V4L/DVB (10856): cx18: Add interlock so sliced VBI insertion only happens for an MPEG PS
+ V4L/DVB (11042): v4l2-api: Add definitions for V4L2_MPEG_STREAM_VBI_FMT_IVTV payloads
+ V4L/DVB (11091): cx18, ivtv: Ensure endianess for linemasks in VBI embedded in MPEG stream
+ V4L/DVB (11092): cx18: Optimize processing of VBI buffers from the capture unit
+ V4L/DVB (11233): mxl5005s: Switch in mxl5005s_set_params should operate on correct values
+
+Antoine Jacquet (1):
+ V4L/DVB (10263): zr364xx: add support for Aiptek DV T300
+
+Antonio Ospite (2):
+ V4L/DVB (10344): gspca - ov534: Disable the Hercules webcams.
+ V4L/DVB (10676): mt9m111: Call icl->reset() on mt9m111_reset().
+
+Antti Palosaari (4):
+ V4L/DVB (10286): af9015: add new USB ID for KWorld DVB-T 395U
+ V4L/DVB (10329): af9015: remove dual_mode module param
+ V4L/DVB (11215): zl10353: add support for Intel CE6230 and Intel CE6231
+ V4L/DVB (11216): Add driver for Intel CE6230 DVB-T USB2.0
+
+Arne Luehrs (1):
+ V4L/DVB (10319): dib0700: enable IR receiver in Nova TD usb stick (52009)
+
+Artem Makhutov (1):
+ V4L/DVB (11248): Remove debug output from stb6100_cfg.h
+
+Bruno Christo (1):
+ V4L/DVB (10827): Add support for GeoVision GV-800(S)
+
+Daniel Glöckner (1):
+ V4L/DVB (11242): allow v4l2 drivers to provide a get_unmapped_area handler
+
+Devin Heitmueller (36):
+ V4L/DVB (10320): dib0700: fix i2c error message to make data type clear
+ V4L/DVB (10321): dib0700: Report dib0700_i2c_enumeration failures
+ V4L/DVB (11059): xc5000: fix bug for hybrid xc5000 devices with IF other than 5380
+ V4L/DVB (11060): au8522: rename the au8522.c source file
+ V4L/DVB (11061): au8522: move shared state and common functions into a separate header files
+ V4L/DVB (11062): au8522: fix register read/write high bits
+ V4L/DVB (11063): au8522: power down the digital demod when not in use
+ V4L/DVB (11064): au8522: make use of hybrid framework so analog/digital demod can share state
+ V4L/DVB (11065): au8522: add support for analog side of demodulator
+ V4L/DVB (11066): au0828: add support for analog functionality in bridge
+ V4L/DVB (11067): au0828: workaround a bug in the au0828 i2c handling
+ V4L/DVB (11068): au0828: add analog profile for the HVR-850
+ V4L/DVB (11069): au8522: add mutex protecting use of hybrid state
+ V4L/DVB (11070): au0828: Rework the way the analog video binding occurs
+ V4L/DVB (11071): tveeprom: add the xc5000 tuner to the tveeprom definition
+ V4L/DVB (11072): au0828: advertise only NTSC-M (as opposed to all NTSC standards)
+ V4L/DVB (11073): au0828: disable VBI code since it doesn't yet work
+ V4L/DVB (11074): au0828: fix i2c enumeration bug
+ V4L/DVB (11075): au0828: make register debug lines easier to read
+ V4L/DVB (11076): au0828: make g_chip_ident call work properly
+ V4L/DVB (11077): au0828: properly handle missing analog USB endpoint
+ V4L/DVB (11078): au0828: properly handle non-existent analog inputs
+ V4L/DVB (11079): au0828: fix panic on disconnect if analog initialization failed
+ V4L/DVB (11080): au0828: Convert to use v4l2_device/subdev framework
+ V4L/DVB (11081): au0828: make sure v4l2_device name is unique
+ V4L/DVB (11082): au0828: remove memset calls in v4l2 routines.
+ V4L/DVB (11083): au0828: remove some unneeded braces
+ V4L/DVB (11084): au0828: add entry for undefined input type
+ V4L/DVB (11085): au0828/au8522: Codingstyle fixes
+ V4L/DVB (11086): au0828: rename macro for currently non-function VBI support
+ V4L/DVB (11088): au0828: finish videodev/subdev conversion
+ V4L/DVB (11089): au8522: finish conversion to v4l2_device/subdev
+ V4L/DVB (11139): em28xx: add remote control definition for HVR-900 (both versions)
+ V4L/DVB (11140): usbvision: fix oops on ARM platform when allocating transfer buffers
+ V4L/DVB (11141): em28xx: fix oops on ARM platform when allocating transfer buffers
+ V4L/DVB (11142): au0828: fix oops on ARM platform when allocating transfer buffers
+
+Douglas Kosovic (1):
+ V4L/DVB (10299): bttv: Add support for IVCE-8784 support for V4L2 bttv driver
+
+Douglas Schilling Landgraf (13):
+ V4L/DVB (10323): em28xx: Add entry for GADMEI TVR200
+ V4L/DVB (10326): em28xx: Cleanup: fix bad whitespacing
+ V4L/DVB (10327): em28xx: Add check before call em28xx_isoc_audio_deinit()
+ V4L/DVB (10517): em28xx: remove bad check (changeset a31c595188af)
+ V4L/DVB (10520): em28xx-audio: Add spinlock for trigger
+ V4L/DVB (10521): em28xx-audio: Add lock for users
+ V4L/DVB (10523): em28xx-audio: Add macros EM28XX_START_AUDIO / EM28XX_STOP_AUDIO
+ V4L/DVB (10524): em28xx: Add DVC 101 model to Pinnacle Dazzle description
+ V4L/DVB (10556): em28xx-cards: Add Pinnacle Dazzle Video Creator Plus DVC107 description
+ V4L/DVB (10739): em28xx-cards: remove incorrect entry
+ V4L/DVB (10740): em28xx-cards: Add SIIG AVTuner-PVR board
+ V4L/DVB (10741): em28xx: Add Kaiser Baas Video to DVD maker support
+ V4L/DVB (11222): gspca - zc3xx: The webcam DLink DSB - C320 has the sensor pas106.
+
+Erik Andren (3):
+ V4L/DVB (10334): gspca - stv06xx: Rework control description.
+ V4L/DVB (10341): gspca - stv06xx: Plug a memory leak in the pb0100 sensor driver.
+ V4L/DVB (10342): gspca - stv06xx: Add ctrl caching to the vv6410.
+
+Erik S. Beiser (1):
+ V4L/DVB (10826): cx88: Add IR support to pcHDTV HD3000 & HD5500
+
+Guennadi Liakhovetski (9):
+ V4L/DVB (10665): soc-camera: add data signal polarity flags to drivers
+ V4L/DVB (10672): sh_mobile_ceu_camera: include NV* formats into the format list only once.
+ V4L/DVB (10673): mt9t031: fix gain and hflip controls, register update, and scaling
+ V4L/DVB (10674): soc-camera: camera host driver for i.MX3x SoCs
+ V4L/DVB (10675): soc-camera: extend soc_camera_bus_param_compatible with more tests
+ V4L/DVB (11024): soc-camera: separate S_FMT and S_CROP operations
+ V4L/DVB (11025): soc-camera: configure drivers with a default format on open
+ V4L/DVB (11026): sh-mobile-ceu-camera: set field to the value, configured at open()
+ V4L/DVB (11027): soc-camera: configure drivers with a default format at probe time
+
+Hans Verkuil (171):
+ V4L/DVB (10231): v4l2-subdev: add v4l2_ext_controls support
+ V4L/DVB (10244): v4l2: replace a few snprintfs with strlcpy
+ V4L/DVB (10246): saa6752hs: convert to v4l2_subdev.
+ V4L/DVB (10247): saa7134: convert to the new v4l2 framework.
+ V4L/DVB (10249): v4l2-common: added v4l2_i2c_tuner_addrs()
+ V4L/DVB (10251): cx25840: add comments explaining what the init() does.
+ V4L/DVB (10252): v4l2 doc: explain why v4l2_device_unregister_subdev() has to be called.
+ V4L/DVB (10271): saa7146: convert to video_ioctl2.
+ V4L/DVB (10272): av7110: test type field in VIDIOC_G_SLICED_VBI_CAP
+ V4L/DVB (10291): em28xx: fix VIDIOC_G_CTRL when there is no msp34xx device.
+ V4L/DVB (10313): saa7146: fix VIDIOC_ENUMSTD.
+ V4L/DVB (10406): gspca: fix compiler warning
+ V4L/DVB (10408): v4l2: fix incorrect hue range check
+ V4L/DVB (10409): v4l: remove unused I2C_DRIVERIDs.
+ V4L/DVB (10486): ivtv/cx18: fix g_fmt and try_fmt for raw video
+ V4L/DVB (10487): doc: update hm12 documentation.
+ V4L/DVB (10488): ivtv: cleanup naming conventions
+ V4L/DVB (10489): doc: use consistent naming conventions for vdev and v4l2_dev.
+ V4L/DVB (10490): v4l2: prefill ident and revision from v4l2_dbg_chip_ident.
+ V4L/DVB (10496): saa7146: implement v4l2_device support.
+ V4L/DVB (10497): saa7146: i2c adapdata now points to v4l2_device.
+ V4L/DVB (10498): saa7146: the adapter class will be NULL when v4l2_subdev is used.
+ V4L/DVB (10499): saa7146: convert saa7146 and mxb in particular to v4l2_subdev.
+ V4L/DVB (10500): saa7146: setting control while capturing should return EBUSY, not EINVAL.
+ V4L/DVB (10501): saa7146: prevent unnecessary loading of v4l2-common.
+ V4L/DVB (10502): saa7146: move v4l2 device registration to saa7146_vv.
+ V4L/DVB (10536): saa6588: convert to v4l2-i2c-drv-legacy.h
+ V4L/DVB (10537): saa6588: convert to v4l2_subdev.
+ V4L/DVB (10538): saa6588: add g_chip_ident support.
+ V4L/DVB (10539): saa6588: remove legacy_class, not needed for saa6588
+ V4L/DVB (10540): cx2341x: fixed bug causing several audio controls to be no longer listed
+ V4L/DVB (10542): v4l2-subdev: add querystd and g_input_status
+ V4L/DVB (10544): v4l2-common: add comments warning that about the sort order
+ V4L/DVB (10641): v4l2-dev: remove limit of 32 devices per driver in get_index()
+ V4L/DVB (10642): vivi: update comment to reflect that vivi can now create more than 32 devs.
+ V4L/DVB (10643): v4l2-device: allow a NULL parent device when registering.
+ V4L/DVB (10644): v4l2-subdev: rename dev field to v4l2_dev
+ V4L/DVB (10645): vivi: introduce v4l2_device and do several cleanups
+ V4L/DVB (10646): vivi: controls are per-device, not global.
+ V4L/DVB (10647): vivi: add slider flag to controls.
+ V4L/DVB (10685): v4l2: add colorfx support to v4l2-common.c, and add to 'Changes' in spec.
+ V4L/DVB (10686): v4l2: add V4L2_CTRL_FLAG_WRITE_ONLY flag.
+ V4L/DVB (10687): v4l2-common/v4l2-spec: support/document write-only and button controls
+ V4L/DVB (10691): v4l2-common: add v4l2_i2c_subdev_addr()
+ V4L/DVB (10692): usbvision: convert to v4l2_device/v4l2_subdev.
+ V4L/DVB (10698): v4l2-common: remove v4l2_ctrl_query_fill_std
+ V4L/DVB (10700): saa7115: don't access reg 0x87 if it is not present.
+ V4L/DVB (10701): saa7185: add colorbar support.
+ V4L/DVB (10702): saa7115: add querystd and g_input_status support for zoran.
+ V4L/DVB (10703): zoran: convert to video_ioctl2 and remove 'ready_to_be_freed' hack.
+ V4L/DVB (10704): zoran: remove broken BIGPHYS_AREA and BUZ_HIMEM code, and allow for kmallocs > 128 kB
+ V4L/DVB (10705): zoran: use slider flag with volume etc. controls.
+ V4L/DVB (10706): zoran: fix field typo.
+ V4L/DVB (10707): zoran: set bytesperline to 0 when using MJPEG.
+ V4L/DVB (10708): zoran: remove old V4L1 ioctls, use v4l1-compat instead.
+ V4L/DVB (10709): zoran: set correct parent of the video device.
+ V4L/DVB (10710): zoran: cleanups in an attempt to make the source a bit more readable.
+ V4L/DVB (10711): zoran: fix TRY_FMT support
+ V4L/DVB (10712): zoran: fix G_FMT
+ V4L/DVB (10713): zoran: if reqbufs is called with count == 0, do a streamoff.
+ V4L/DVB (10714): zoran et al: convert zoran i2c modules to V4L2.
+ V4L/DVB (10715): zoran: clean up some old V4L1 left-overs and remove the MAP_NR macro.
+ V4L/DVB (10716): zoran: change buffer defaults to something that works with tvtime
+ V4L/DVB (10717): zoran: TRY_FMT and S_FMT now do the same parameter checks.
+ V4L/DVB (10718): bt866: convert to v4l2_subdev.
+ V4L/DVB (10719): bt819: convert to v4l2_subdev.
+ V4L/DVB (10720): bt819: that delay include is needed after all.
+ V4L/DVB (10721): bt856: convert to v4l2_subdev.
+ V4L/DVB (10722): ks0127: convert to v4l2_subdev.
+ V4L/DVB (10723): ks0127: add supported ks0127 variants to the i2c device list.
+ V4L/DVB (10724): saa7110: convert to v4l2_subdev.
+ V4L/DVB (10725): saa7185: convert to v4l2_subdev.
+ V4L/DVB (10726): vpx3220: convert to v4l2_subdev.
+ V4L/DVB (10727): adv7170: convert to v4l2_subdev.
+ V4L/DVB (10728): adv7175: convert to v4l2-subdev.
+ V4L/DVB (10729): zoran: convert to v4l2_device/v4l2_subdev.
+ V4L/DVB (10730): v4l-dvb: cleanup obsolete references to v4l1 headers.
+ V4L/DVB (10731): zoran i2c modules: remove i2c autoprobing support.
+ V4L/DVB (10732): zoran: s_jpegcomp should return a proper result, not 0.
+ V4L/DVB (10733): zoran: increase bufsize to a value suitable for 768x576.
+ V4L/DVB (10858): vino: convert to video_ioctl2.
+ V4L/DVB (10859): vino: minor renames
+ V4L/DVB (10860): saa7191: convert to v4l2-i2c-drv-legacy.h
+ V4L/DVB (10861): vino/indycam/saa7191: convert to i2c modules to V4L2.
+ V4L/DVB (10862): indycam: convert to v4l2_subdev
+ V4L/DVB (10863): saa7191: convert to v4l2_subdev.
+ V4L/DVB (10864): vino: introduce v4l2_device.
+ V4L/DVB (10865): vino: convert to v4l2_subdev.
+ V4L/DVB (10866): saa7191, indycam: remove compat code.
+ V4L/DVB (10868): vino: add note that this conversion is untested.
+ V4L/DVB (10873): w9968cf: add v4l2_device.
+ V4L/DVB (10874): w9968cf/ovcamchip: convert to v4l2_subdev.
+ V4L/DVB (10880): radio-aimslab: convert to v4l2_device.
+ V4L/DVB (10881): radio-aztech: convert to v4l2_device.
+ V4L/DVB (10882): radio-cadet: convert to v4l2_device.
+ V4L/DVB (10883): radio-gemtek-pci: convert to v4l2_device.
+ V4L/DVB (10884): radio-gemtek: convert to v4l2_device.
+ V4L/DVB (10885): radio-maestro: convert to v4l2_device.
+ V4L/DVB (10886): radio-maxiradio: convert to v4l2_device.
+ V4L/DVB (10887): radio-rtrack2: convert to v4l2_device.
+ V4L/DVB (10888): radio-sf16fmi: convert to v4l2_device.
+ V4L/DVB (10889): radio-sf16fmr2: convert to v4l2_device.
+ V4L/DVB (10890): radio-terratec: convert to v4l2_device.
+ V4L/DVB (10891): radio-trust: convert to v4l2_device.
+ V4L/DVB (10892): radio-typhoon: convert to v4l2_device.
+ V4L/DVB (10893): radio-zoltrix: convert to v4l2_device.
+ V4L/DVB (10894): ISA radio drivers: improve kernel log message
+ V4L/DVB (10909): tvmixer: remove last remaining references to this deleted module.
+ V4L/DVB (10910): videodev2.h: remove deprecated VIDIOC_G_CHIP_IDENT_OLD
+ V4L/DVB (10912): vivi: fix compile warning.
+ V4L/DVB (10914): v4l2: fix compile warnings when printing u64 value.
+ V4L/DVB (10919): tlv320aic23b: use v4l2-i2c-drv.h instead of drv-legacy.h
+ V4L/DVB (10920): v4l2-ioctl: fix partial-copy code.
+ V4L/DVB (10921): msp3400: remove obsolete V4L1 code
+ V4L/DVB (10959): radio: remove uaccess include
+ V4L/DVB (10960): omap24xxcam: don't set vfl_type.
+ V4L/DVB (10962): fired-avc: fix printk formatting warning.
+ V4L/DVB (10965): ivtv: bump version
+ V4L/DVB (10980): doc: improve the v4l2-framework documentation.
+ V4L/DVB (10983): v4l2-common: add missing i2c_unregister_device.
+ V4L/DVB (10987): cx23885: fix crash on non-netup cards
+ V4L/DVB (10988): v4l2-dev: use parent field if the v4l2_device has no parent set.
+ V4L/DVB (11021): v4l2-device: add a notify callback.
+ V4L/DVB (11022): zoran/bt819: use new notify functionality.
+ V4L/DVB (11044): v4l2-device: add v4l2_device_disconnect
+ V4L/DVB (11045): v4l2: call v4l2_device_disconnect in USB drivers.
+ V4L/DVB (11046): bttv: convert to v4l2_device.
+ V4L/DVB (11047): cx88: convert to v4l2_device.
+ V4L/DVB (11048): zoran: fix incorrect return type of notify function.
+ V4L/DVB (11051): v4l-dvb: replace remaining references to the old mailinglist.
+ V4L/DVB (11052): bt819: remove an unused header
+ V4L/DVB (11053): saa7134: set v4l2_dev field of video_device
+ V4L/DVB (11098): v4l2-common: remove incorrect MODULE test
+ V4L/DVB (11100): au8522: fix compilation warning.
+ V4L/DVB (11112): v4l2-subdev: add support for TRY_FMT, ENUM_FMT and G/S_PARM.
+ V4L/DVB (11113): ov7670: convert to v4l2_subdev
+ V4L/DVB (11114): cafe_ccic: convert to v4l2_device.
+ V4L/DVB (11115): cafe_ccic: use v4l2_subdev to talk to the ov7670 sensor.
+ V4L/DVB (11116): ov7670: cleanup and remove legacy code.
+ V4L/DVB (11117): ov7670: add support to get/set registers
+ V4L/DVB (11118): cafe_ccic: replace debugfs with g/s_register ioctls.
+ V4L/DVB (11120): cafe_ccic: stick in a comment with a request for test results
+ V4L/DVB (11253): saa7134: fix RTD Embedded Technologies VFG7350 support.
+ V4L/DVB (11254): cs53l32a: remove legacy support.
+ V4L/DVB (11255): dst_ca: fix compile warning.
+ V4L/DVB (11256): dabusb: fix compile warning.
+ V4L/DVB (11275): tvaudio: fix mute and s/g_tuner handling
+ V4L/DVB (11276): tvaudio: add tda9875 support.
+ V4L/DVB (11277): tvaudio: always call init_timer to prevent rmmod crash.
+ V4L/DVB (11278): bttv: convert to v4l2_subdev since i2c autoprobing will disappear.
+ V4L/DVB (11279): bttv: tda9875 is no longer used by bttv, so remove from bt8xx/Kconfig.
+ V4L/DVB (11281): bttv: move saa6588 config to the helper chip config
+ V4L/DVB (11282): saa7134: add RDS support.
+ V4L/DVB (11283): saa6588: remove legacy code.
+ V4L/DVB (11295): cx23885: convert to v4l2_device.
+ V4L/DVB (11297): cx23885: convert to v4l2_subdev.
+ V4L/DVB (11298): cx25840: remove legacy code for old-style i2c API
+ V4L/DVB (11300): cx88: convert to v4l2_subdev.
+ V4L/DVB (11301): wm8775: remove legacy code for old-style i2c API
+ V4L/DVB (11302): tda9875: remove legacy code for old-style i2c API
+ V4L/DVB (11303): tda7432: remove legacy code for old-style i2c API
+ V4L/DVB (11304): v4l2: remove v4l2_subdev_command calls where they are no longer needed.
+ V4L/DVB (11305): cx88: prevent probing rtc and ir devices
+ V4L/DVB (11309): cx25840: cleanup: remove intermediate 'ioctl' step
+ V4L/DVB (11310): cx18: remove intermediate 'ioctl' step
+ V4L/DVB (11311): v4l: replace 'ioctl' references in v4l i2c drivers
+ V4L/DVB (11312): tuner: remove V4L1 code from this driver.
+ V4L/DVB (11313): v4l2-subdev: add enum_framesizes and enum_frameintervals.
+ V4L/DVB (11314): au8522: remove unused I2C_DRIVERID
+ V4L/DVB (11315): cx25840: fix 'unused variable' warning.
+ V4L/DVB (11316): saa7191: tuner ops wasn't set.
+
+Hans Werner (1):
+ V4L/DVB (10392): lnbp21: documentation about the system register
+
+Hans de Goede (1):
+ V4L/DVB (11221): gspca - sonixj: Prefer sonixj instead of sn9c102 for 0471:0327.
+
+Igor M. Liplianin (18):
+ V4L/DVB (10266): Add support for TurboSight TBS6920 DVB-S2 PCI-e card.
+ V4L/DVB (10267): Add support for TeVii S470 DVB-S2 PCI-e card.
+ V4L/DVB (10268): Proper implement set_voltage in cx24116.
+ V4L/DVB (10269): Add support for DVBWorld DVBS2 PCI-e 2005.
+ V4L/DVB (10413): Bug fix: Restore HVR-4000 tuning.
+ V4L/DVB (10743): dm1105: not demuxing from interrupt context.
+ V4L/DVB (10744): dm1105: infrared remote code is remaked.
+ V4L/DVB (10799): Add support for ST STV6110 silicon tuner.
+ V4L/DVB (10800): Add support for ST LNBH24 LNB power controller.
+ V4L/DVB (10801): Add headers for ST STV0900 dual demodulator.
+ V4L/DVB (10802): Add more headers for ST STV0900 dual demodulator.
+ V4L/DVB (10803): Add core code for ST STV0900 dual demodulator.
+ V4L/DVB (10804): Add support for ST STV0900 dual demodulator.
+ V4L/DVB (10805): Add support for NetUP Dual DVB-S2 CI card
+ V4L/DVB (10808): Fix typo in lnbp21.c
+ V4L/DVB (10871): stv0900: delete debug messages not related to stv0900 tuning algorythm
+ V4L/DVB (11054): Shorten some lines in stv0900 to less then 81 characters
+ V4L/DVB (11055): Fix typo in stv0900
+
+Indika Katugampala (1):
+ V4L/DVB (10528): em28xx: support added for IO-DATA GV/MVP SZ - EMPIA-2820 chipset
+
+Jan Engelhardt (1):
+ V4L/DVB (10391): dvb: constify VFTs
+
+Janne Grunau (12):
+ V4L/DVB (11095): adds V4L2_CID_SHARPNESS to v4l2_ctrl_query_fill()
+ V4L/DVB (11096): V4L2 Driver for the Hauppauge HD PVR usb capture device
+ V4L/DVB (11097): use video_ioctl2 as ioctl handler directly
+ V4L/DVB (11125): fix mispelled Hauppauge in HD PVR and PVR USB2 driver comments
+ V4L/DVB (11152): hdpvr: Fix build with Config_I2C not set
+ V4L/DVB (11228): hdpvr: use debugging macro for buffer status
+ V4L/DVB (11229): hdpvr: set usb interface dev as parent in struct video_device
+ V4L/DVB (11230): hdpvr: return immediately from hdpvr_poll if data is available
+ V4L/DVB (11231): hdpvr: locking fixes
+ V4L/DVB (11245): hdpvr: add struct v4l2_device
+ V4L/DVB (11246): hdpvr: convert printing macros to v4l2_* with struct v4l2_device
+ V4L/DVB (11247): hdpvr: empty internal device buffer after stopping streaming
+
+Jean Delvare (8):
+ V4L/DVB (10867): vino: fold i2c-algo-sgi code into vino.
+ V4L/DVB (10931): zoran: Drop the lock_norm module parameter
+ V4L/DVB (10932): zoran: Don't frighten users with failed buffer allocation
+ V4L/DVB (10938): em28xx: Prevent general protection fault on rmmod
+ V4L/DVB (10939): ir-kbd-i2c: Prevent general protection fault on rmmod
+ V4L/DVB (10940): saa6588: Prevent general protection fault on rmmod
+ V4L/DVB (10943): cx88: Prevent general protection fault on rmmod
+ V4L/DVB (11111a): MAINTAINERS: Drop references to deprecated video4linux list
+
+Jean-Francois Moine (75):
+ V4L/DVB (10332): gspca - main: Version change.
+ V4L/DVB (10333): gspca - main and many subdrivers: Remove the epaddr variable.
+ V4L/DVB (10337): gspca - common: Simplify the debug macros.
+ V4L/DVB (10343): gspca - zc3xx / zc0301: Handle the 0ac8:303b instead of zc0301.
+ V4L/DVB (10345): gspca - jpeg subdrivers: One quantization table per subdriver.
+ V4L/DVB (10346): gspca - zc3xx: Fix bad variable type with i2c read.
+ V4L/DVB (10347): gspca - mars: Optimize, rewrite initialization and add controls.
+ V4L/DVB (10348): gspca - mars: Bad isoc packet scanning.
+ V4L/DVB (10350): gspca - tv8532: Cleanup code.
+ V4L/DVB (10352): gspca - spca508: Cleanup code.
+ V4L/DVB (10353): gspca - some subdrivers: Don't get the control values from the webcam.
+ V4L/DVB (10354): gspca - tv8532: Change the max brightness.
+ V4L/DVB (10356): gspca - sonixj: Cleanup code.
+ V4L/DVB (10357): gspca - main: Cleanup code.
+ V4L/DVB (10360): gspca - mars: Bad interface/altsetting since 0a10a0e906be.
+ V4L/DVB (10361): gspca - sonixj: Gamma control added.
+ V4L/DVB (10363): gspca - spca500: Abnormal error message when starting ClickSmart310.
+ V4L/DVB (10367): gspca - spca561: Optimize the isoc scanning function.
+ V4L/DVB (10368): gspca - spca561: Fix bugs and rewrite the init/start of the rev72a.
+ V4L/DVB (10370): gspca - main: Have 3 URBs instead of 2 for ISOC transfers.
+ V4L/DVB (10371): gspca - spca561: Fix image problem in the 352x288 mode of rev72a.
+ V4L/DVB (10372): gspca - sonixj: Cleanup code.
+ V4L/DVB (10373): gspca - zc3xx: Sensor adcm2700 added.
+ V4L/DVB (10374): gspca - zc3xx: Bad probe of the sensor adcm2700.
+ V4L/DVB (10375): gspca - zc3xx: Remove duplicated sequence of sensor cs2102k.
+ V4L/DVB (10376): gspca - zc3xx: Remove some useless tables of sensor adcm2700.
+ V4L/DVB (10378): gspca - main: Avoid error on set interface on disconnection.
+ V4L/DVB (10380): gspca - t613: Cleanup and optimize code.
+ V4L/DVB (10381): gspca - t613: New unknown sensor added.
+ V4L/DVB (10382): gspca - t613: Bad returned value when no known sensor found.
+ V4L/DVB (10383): gspca - spca505: Cleanup and optimize code.
+ V4L/DVB (10384): gspca - spca505: Simplify and add the brightness in start.
+ V4L/DVB (10387): gspca - spca505: Move some sequences from probe to streamon.
+ V4L/DVB (10389): gspca - zc3xx: Do work the sensor adcm2700.
+ V4L/DVB (10419): gspca - sonixj: Sensor mt9v111 added.
+ V4L/DVB (10420): gspca - vc032x: Webcam 041e:405b added and mi1310_soc updated.
+ V4L/DVB (10421): gspca - documentation: Add the webcam 041e:405b.
+ V4L/DVB (10423): gspca - sonixj: Bad sensor definition of the webcams 0c45:60c0.
+ V4L/DVB (10424): gspca - vc032x: Add resolution 1280x1024 for sensor mi1310_soc.
+ V4L/DVB (10425): gspca - sonixj: Bad initialization of sensor mt9v111.
+ V4L/DVB (10427): gspca - sonixj: Sensor sp80708 added for webcam 0c45:6143.
+ V4L/DVB (10428): gspca - sonixj: Specific gamma tables per sensor.
+ V4L/DVB (10429): gspca - sonixj: Simplify the probe of the sensors mi0360/mt9v111.
+ V4L/DVB (10430): gspca - sonixj: Adjust some exchanges with the sensor mt9v111.
+ V4L/DVB (10431): gspca - vc032x: Bad revision for the webcam 041e:405b.
+ V4L/DVB (10432): gspca - vc032x: Cleanup source, optimize and check i2c_write.
+ V4L/DVB (10617): gspca - vc032x: Remove the vc0321 reset.
+ V4L/DVB (10618): gspca - some drivers: Fix compilation warnings.
+ V4L/DVB (10620): gspca - main: More checks of the device disconnection.
+ V4L/DVB (10635): gspca - sonixj: No vertical flip control for mt9v111.
+ V4L/DVB (10636): gspca - sonixj: Add autogain for ov7630/48 and vflip for ov7648.
+ V4L/DVB (10637): gspca - t613: Bad sensor name in kernel trace when 'other' sensor.
+ V4L/DVB (10638): gspca - t613: Bad debug level when displaying the sensor type.
+ V4L/DVB (10679): gspca - sonixj: Handle the webcam 0c45:613c instead of sn9c102.
+ V4L/DVB (10680): gspca - zc3xx: Bad probe of the ov7xxx sensors.
+ V4L/DVB (10681): gspca - zc3xx: Bad probe of the ov7630c sensor.
+ V4L/DVB (10787): gspca - mars: Bad webcam register values tied to saturation.
+ V4L/DVB (10788): gspca - vc032x: Bad matrix for sensor mi1310_soc.
+ V4L/DVB (11039): gspca - most jpeg subdrivers: Change the JPEG header creation.
+ V4L/DVB (11040): gspca - most jpeg subdrivers: Have the JPEG quality settable.
+ V4L/DVB (11103): gspca - main: May have isochronous transfers on altsetting 0
+ V4L/DVB (11104): gspca - ov534: Bad frame pointer after adding the last packet
+ V4L/DVB (11105): gspca - ov534: Adjust the packet scan function
+ V4L/DVB (11106): gspca - ov534: New sensor ov965x and re-enable the webcam 06f8:3003
+ V4L/DVB (11143): gspca - t613: Bad sensor detection.
+ V4L/DVB (11144): gspca - t613: Don't re-read the ID registers at probe time.
+ V4L/DVB (11145): gspca - t613: Greater delay after om6802 reset.
+ V4L/DVB (11146): gspca - vc032x: Change the probe sequence.
+ V4L/DVB (11209): gspca - vc032x: New sensor mi1320_soc and webcam 15b8:6001 added.
+ V4L/DVB (11211): gspca - vc032x: Simplify the i2c write function.
+ V4L/DVB (11212): gspca - vc032x: Use YVYU format for sensor mi1320_soc.
+ V4L/DVB (11218): gspca - sq905: Update the frame pointer after adding the last packet.
+ V4L/DVB (11219): gspca - sq905: Optimize the resolution setting.
+ V4L/DVB (11220): gspca - finepix: Use a workqueue for streaming.
+ V4L/DVB (11223): gspca - doc: Add the 15b8:6001 webcam to the Documentation.
+
+Jochen Friedrich (2):
+ V4L/DVB (10452): Add Freescale MC44S803 tuner driver
+ V4L/DVB (10453): af9015: add MC44S803 support
+
+Jose Alberto Reguero (1):
+ V4L/DVB (10330): af9015: New remote RM-KS for Avermedia Volar-X
+
+Kay Sievers (1):
+ V4L/DVB (10395): struct device - replace bus_id with dev_name(), dev_set_name()
+
+Klaus Flittner (1):
+ V4L/DVB (11290): Add Elgato EyeTV DTT to dibcom driver
+
+Kuninori Morimoto (8):
+ V4L/DVB (10616): tw9910: color format check is added on set_fmt
+ V4L/DVB (10666): ov772x: move configuration from start_capture() to set_fmt()
+ V4L/DVB (10667): ov772x: setting method to register is changed.
+ V4L/DVB (10668): ov772x: bit mask operation fix on ov772x_mask_set.
+ V4L/DVB (10669): ov772x: Add image flip support
+ V4L/DVB (10670): tw9910: bit mask operation fix on tw9910_mask_set.
+ V4L/DVB (10671): sh_mobile_ceu: SOCAM flags are not platform dependent
+ V4L/DVB (11028): ov772x: use soft sleep mode in stop_capture
+
+Kyle Guinn (3):
+ V4L/DVB (10365): Add Mars-Semi MR97310A format
+ V4L/DVB (10366): gspca - mr97310a: New subdriver.
+ V4L/DVB (10369): gspca - mr97310a: Fix camera initialization copy/paste bugs.
+
+Laurent Pinchart (8):
+ V4L/DVB (10293): uvcvideo: replace strn{cpy,cat} with strl{cpy,cat}.
+ V4L/DVB (10294): uvcvideo: Add support for the Alcor Micro AU3820 chipset.
+ V4L/DVB (10295): uvcvideo: Retry URB buffers allocation when the system is low on memory.
+ V4L/DVB (10296): uvcvideo: Fix memory leak in input device handling
+ V4L/DVB (10650): uvcvideo: Initialize streaming parameters with the probe control value
+ V4L/DVB (10651): uvcvideo: Ignore empty bulk URBs
+ V4L/DVB (10652): uvcvideo: Add quirk to override wrong bandwidth value for Vimicro devices
+ V4L/DVB (11292): uvcvideo: Add support for Syntek cameras found in JAOtech Smart Terminals
+
+Lierdakil (1):
+ V4L/DVB (10388): gspca - pac207: Webcam 093a:2474 added.
+
+Magnus Damm (2):
+ V4L/DVB (10304): buf-dma-contig: fix USERPTR free handling
+ V4L/DVB (11029): video: use videobuf_waiton() in sh_mobile_ceu free_buffer()
+
+Martin Fuzzey (1):
+ V4L/DVB (10945): pwc : fix LED and power setup for first open
+
+Matthias Schwarzott (4):
+ V4L/DVB (10662): remove redundant memset after kzalloc
+ V4L/DVB (10822): Add support for Zarlink ZL10036 DVB-S tuner.
+ V4L/DVB (10823): saa7134: add DVB support for Avermedia A700 cards
+ V4L/DVB (10948): flexcop-pci: Print a message in case the new stream watchdog detects a problem
+
+Mauro Carvalho Chehab (49):
+ V4L/DVB (10211): vivi: Implements 4 inputs on vivi
+ V4L/DVB (10298): remove err macro from few usb devices
+ V4L/DVB (10305): videobuf-vmalloc: Fix: videobuf memory were never freed
+ V4L/DVB (10394): KWorld ATSC 115 all static
+ V4L/DVB (10404): saa7134-core: remove oss option, since saa7134-oss doesn't exist anymore
+ V4L/DVB (10405): saa7134-core: loading saa7134-alsa is now the default
+ V4L/DVB (10504): tda827x: Be sure that gate will be open/closed at the proper time
+ V4L/DVB (10505): tda8290: Print an error if i2c_gate is not provided
+ V4L/DVB (10506): saa7134: move tuner init code to saa7134-cards
+ V4L/DVB (10507): saa7134: Fix analog mode on devices that need to open an i2c gate
+ V4L/DVB (10508): saa7134: Cleanup: remove unused waitqueue from struct
+ V4L/DVB (10509): saa7134-video: two int controls lack a step
+ V4L/DVB (10511): saa7134: get rid of KBL
+ V4L/DVB (10512): tda1004x: Fix eeprom firmware load on boards with 16MHz Xtal
+ V4L/DVB (10514): em28xx: Add support for Kaiomy TVnPC U2 stick
+ V4L/DVB (10515): Adds IR table for the IR provided with this board and includes it at
+ V4L/DVB (10516): em28xx: Add support for Easy Cap Capture DC-60
+ V4L/DVB (10570): v4l2-framework: documments videobuf usage on drivers
+ V4L/DVB (10571): v4l2-framework.txt: Fixes the videobuf init functions
+ V4L/DVB (10654): em28xx: VideoMate For You USB TV box requires tvaudio
+ V4L/DVB (10738): Get rid of video_decoder.h header were uneeded
+ V4L/DVB(10738a): remove include/linux/video_encoder.h
+ V4L/DVB (10769): Update dependencies of the modules converted to V4L2
+ V4L/DVB (10771): tea575x-tuner: convert it to V4L2 API
+ V4L/DVB (10835): Kconfig: Add some missing selects for a required frontends
+ V4L/DVB (10836): Kconfig: replace DVB_FE_CUSTOMIZE to DVB_FE_CUSTOMISE
+ V4L/DVB (10837): Kconfig: only open the customise menu if selected
+ V4L/DVB (10838): get rid of the other occurrences of DVB_FE_CUSTOMIZE typo
+ V4L/DVB (10840): em28xx-dvb: Remove an unused header
+ V4L/DVB (10842): Adds some missing frontend selects for saa7134 and dvb-usb
+ V4L/DVB (10870): v4l2-ioctl: get rid of video_decoder.h
+ V4L/DVB (10896): /frontends/Kconfig: Move af9013 Kconfig option to its proper place
+ V4L/DVB (10897): Fix Kbuild MEDIA_TUNER_CUSTOMIZE dependencies
+ V4L/DVB (10870a): remove all references for video_decoder.h
+ V4L/DVB (10907): avoid loading the entire videodev.h header on V4L2 drivers
+ V4L/DVB (10951): xc5000: Fix CodingStyle errors introduced by the last patch
+ V4L/DVB (10908): videobuf-core: also needs a minimal subset of V4L1 header
+ V4L/DVB (11108): get_dvb_firmware: Add option to download firmware for cx231xx
+ V4L/DVB (11109): au0828: Fix compilation when VIDEO_ADV_DEBUG = n
+ V4L/DVB (11110): au8522/au0828: Fix Kconfig dependencies
+ V4L/DVB (11111): dvb_dummy_fe: Fix compilation breakage
+ V4L/DVB (11127): Kconfig: replace all occurrences of CUSTOMIZE to CUSTOMISE
+ V4L/DVB (11136): get_dvb_firmware: Add download code for cx18 firmwares
+ V4L/DVB (11137): get_dvb_firmware: add cx23885 firmwares
+ V4L/DVB (11138): get_dvb_firmware: add support for downloading the cx2584x firmware for pvrusb2
+ V4L/DVB (11225): v4lgrab: fix compilation warnings
+ V4L/DVB (11226): avoid warnings for request_ihex_firmware on dabusb and vicam
+ V4L/DVB (11227): ce6230: avoid using unitialized var
+ V4L/DVB (11308): msp3400: use the V4L2 header since no V4L1 code is there
+
+Michael Krufky (36):
+ V4L/DVB (10415): dib0700: add data debug to dib0700_i2c_xfer_new
+ V4L/DVB (10416): tveeprom: update to include Hauppauge tuners 151-155
+ V4L/DVB (10417): sms1xxx: add missing usb id 2040:2011
+ V4L/DVB (10746): sms1xxx: enable rf switch on Hauppauge Tiger devices
+ V4L/DVB (10747): sms1xxx: move definition of struct smsdvb_client_t into smsdvb.c
+ V4L/DVB (10749): sms1xxx: move smsusb_id_table into smsusb.c
+ V4L/DVB (10751): sms1xxx: fix checkpatch.pl violations introduced by previous changeset
+ V4L/DVB (10752): sms1xxx: load smsdvb module automatically based on device id
+ V4L/DVB (10753): siano: convert EXPORT_SYMBOL to EXPORT_SYMBOL_GPL
+ V4L/DVB (10772): siano: prevent duplicate variable declaration
+ V4L/DVB (10779): mxl5007t: remove analog tuning code
+ V4L/DVB (10780): mxl5007t: remove function mxl5007t_check_rf_input_power
+ V4L/DVB (10781): mxl5007t: mxl5007t_get_status should report if tuner is locked
+ V4L/DVB (10782): mxl5007t: warn when unknown revisions are detected
+ V4L/DVB (10783): mxl5007t: fix devname for hybrid_tuner_request_state
+ V4L/DVB (10784): mxl5007t: update driver for MxL 5007T V4
+ V4L/DVB (10876): tda18271: add support for AGC configuration via tuner callback
+ V4L/DVB (10877): saa7134: add analog support for Hauppauge HVR1110r3 boards
+ V4L/DVB (10898): remove build-time dependencies on dib7000m
+ V4L/DVB (10899): remove build-time dependencies on dib7000p
+ V4L/DVB (10900): remove build-time dependencies on dib3000mc
+ V4L/DVB (10901): cleanup linewraps in dib7000p.h
+ V4L/DVB (10902): cleanup linewraps in dib7000m.h
+ V4L/DVB (10903): cleanup linewraps in dib3000mc.h
+ V4L/DVB (10904): remove dib0070_ctrl_agc_filter from dib0070.h
+ V4L/DVB (10905): dib0700: enable DVB_FE_CUSTOMISE for dibcom frontends
+ V4L/DVB (10923): saa7134: fix typo in product name
+ V4L/DVB (10924): saa7134: enable serial transport streaming interface
+ V4L/DVB (10925): add support for LG Electronics LGDT3305 ATSC/QAM-B Demodulator
+ V4L/DVB (10926): saa7134: enable digital tv support for Hauppauge WinTV-HVR1120
+ V4L/DVB (10927): dib0700: add support for Hauppauge ATSC MiniCard
+ V4L/DVB (10968): lgdt3305: add email address to MODULE_AUTHOR
+ V4L/DVB (10969): lgdt3305: add missing space in comment
+ V4L/DVB (10970): lgdt3305: add MODULE_VERSION
+ V4L/DVB (10984): lgdt3305: avoid OOPS in error path of lgdt3305_attach
+ V4L/DVB (11251): tuner: prevent invalid initialization of t->config in set_type
+
+Mike Isely (62):
+ V4L/DVB (10236): pvrusb2: Stop advertising VBI capability - it isn't there
+ V4L/DVB (10237): pvrusb2: Generate a device-unique identifier
+ V4L/DVB (10238): pvrusb2: Change sysfs serial number handling
+ V4L/DVB (10239): pvrusb2: Fix misleading comment caused by earlier commit
+ V4L/DVB (10258): pvrusb2: Issue VIDIOC_INT_INIT to v4l2 modules when they first attach
+ V4L/DVB (10259): pvrusb2: Code module name directly in printk
+ V4L/DVB (10303): pvrusb2: Use usb_make_path() to determine device bus location
+ V4L/DVB (11154): pvrusb2: Split i2c module handling from i2c adapter
+ V4L/DVB (11155): pvrusb2: Set up v4l2_device instance
+ V4L/DVB (11156): pvrusb2: Changes to further isolate old i2c layer
+ V4L/DVB (11157): pvrusb2: whitespace trivial tweaks
+ V4L/DVB (11158): pvrusb2: New device attribute mechanism to specify sub-devices
+ V4L/DVB (11159): pvrusb2: Providing means to stop tracking an old i2c module
+ V4L/DVB (11160): pvrusb2: whitespace tweaks
+ V4L/DVB (11161): pvrusb2: Set i2c autoprobing to be off by default
+ V4L/DVB (11162): pvrusb2: Tie up loose ends with v4l2-subdev setup
+ V4L/DVB (11163): pvrusb2: Lay foundation for triggering sub-device updates
+ V4L/DVB (11164): pvrusb2: Tie-in sub-device log requests
+ V4L/DVB (11165): pvrusb2: Tie in debug register access to sub-devices
+ V4L/DVB (11166): pvrusb2: Implement status fetching from sub-devices
+ V4L/DVB (11167): pvrusb2: Tie in various v4l2 operations into the sub-device mechanism
+ V4L/DVB (11168): pvrusb2: Define value for a null sub-device ID
+ V4L/DVB (11169): pvrusb2: Note who our video decoder sub-device is, and set it up
+ V4L/DVB (11170): pvrusb2: Clean-up / placeholders inserted for additional development
+ V4L/DVB (11171): pvrusb2: Tie in sub-device decoder start/stop
+ V4L/DVB (11172): pvrusb2: Cause overall initialization to fail if sub-driver(s) fail
+ V4L/DVB (11173): pvrusb2: Fix backwards function header comments
+ V4L/DVB (11174): pvrusb2: Implement reporting of connected sub-devices
+ V4L/DVB (11175): pvrusb2: Implement sub-device specific update framework
+ V4L/DVB (11176): pvrusb2: Tie in wm8775 sub-device handling
+ V4L/DVB (11177): pvrusb2: Tie in saa7115 sub-device handling
+ V4L/DVB (11178): pvrusb2: Make audio sample rate update into a sub-device broadcast
+ V4L/DVB (11179): pvrusb2: make sub-device specific update function names uniform
+ V4L/DVB (11180): pvrusb2: Tie in msp3400 sub-device support
+ V4L/DVB (11181): pvrusb2: Fix silly 80 column issue
+ V4L/DVB (11182): pvrusb2: Tie in cx25840 sub-device support
+ V4L/DVB (11183): pvrusb2: Implement more sub-device loading trace and improve error handling
+ V4L/DVB (11184): pvrusb2: Define default i2c address for wm8775 sub-device
+ V4L/DVB (11185): pvrusb2: Fix uninitialized counter
+ V4L/DVB (11186): pvrusb2: Fix bugs involved in listing of sub-devices
+ V4L/DVB (11187): pvrusb2: Allow sub-devices to insert correctly
+ V4L/DVB (11188): pvrusb2: Sub-device update must happen BEFORE state dirty bits are cleared
+ V4L/DVB (11189): pvrusb2: Deal with space-after-comma coding style idiocy
+ V4L/DVB (11190): pvrusb2: Broadcast tuner type change to sub-devices
+ V4L/DVB (11191): pvrusb2: Define default I2C address for cx25840 sub-device
+ V4L/DVB (11192): pvrusb2: Implement trace print for stream on / off action
+ V4L/DVB (11193): pvrusb2: Correct some trace print inaccuracies
+ V4L/DVB (11194): pvrusb2: Implement mechanism to force a full sub-device update
+ V4L/DVB (11195): pvrusb2: Issue required core init broadcast to all sub-devices
+ V4L/DVB (11196): pvrusb2: Define default I2C addresses for msp3400 and saa7115 sub-devices
+ V4L/DVB (11197): pvrusb2: Fix incorrectly named sub-device ID
+ V4L/DVB (11198): pvrusb2: Define default I2C address for CS53L32A sub-device
+ V4L/DVB (11199): pvrusb2: Convert all device definitions to use new sub-device declarations
+ V4L/DVB (11200): pvrusb2: Make a bunch of dvb config structures const (trivial)
+ V4L/DVB (11201): pvrusb2: Fix space-after-comma idiocy
+ V4L/DVB (11202): pvrusb2: Fix slightly mis-leading header in debug interface output
+ V4L/DVB (11203): pvrusb2: Implement better reporting on attached sub-devices
+ V4L/DVB (11204): pvrusb2: Remove old i2c layer; we use v4l2-subdev now
+ V4L/DVB (11205): pvrusb2: Remove ancient IVTV specific ioctl functions
+ V4L/DVB (11206): pvrusb2: Add sub-device for demod
+ V4L/DVB (11207): pvrusb2: Add composite and s-video input support for OnAir devices
+ V4L/DVB (11208): pvrusb2: Use v4l2_device_disconnect()
+
+Márton Németh (2):
+ V4L/DVB (10633): DAB: fix typo
+ V4L/DVB (11293): uvcvideo: Add zero fill for VIDIOC_ENUM_FMT
+
+Nam Phạm Thành (1):
+ V4L/DVB (10242): pwc: add support for webcam snapshot button
+
+Nicola Soranzo (2):
+ V4L/DVB (10525): em28xx: Coding style fixes and a typo correction
+ V4L/DVB (10555): em28xx: CodingStyle fixes
+
+Oldřich Jedlička (1):
+ V4L/DVB (10632): Added support for AVerMedia Cardbus Hybrid remote control
+
+Oliver Endriss (1):
+ V4L/DVB (10843): saa7146: Clean-up i2c error handling
+
+Pascal Terjan (1):
+ V4L/DVB (10825): Add ids for Yuan PD378S DVB adapter
+
+Patrick Boettcher (2):
+ V4L/DVB (11284): Fix i2c code of flexcop-driver for rare revisions
+ V4L/DVB (11285): Remove unecessary udelay
+
+Philippe Rétornaz (1):
+ V4L/DVB (11035): mt9t031 bugfix
+
+Randy Dunlap (4):
+ V4L/DVB (10631): zoran: fix printk format
+ V4L/DVB (10830): dm1105: uses ir_* functions, select VIDEO_IR
+ V4L/DVB (10846): dvb/frontends: fix duplicate 'debug' symbol
+ V4L/DVB (11237): media/zoran: fix printk format
+
+Robert Krakora (3):
+ V4L/DVB (10255): em28xx: Clock (XCLK) Cleanup
+ V4L/DVB (10518): em28xx: Fix for em28xx memory leak and function rename
+ V4L/DVB (10519): em28xx: Fix for em28xx audio startup
+
+Robert Millan (1):
+ V4L/DVB (10944): Conceptronic CTVFMI2 PCI Id
+
+Roel Kluin (3):
+ V4L/DVB (10629): tvp514x: try_count reaches 0, not -1
+ V4L/DVB: calibration still successful at 10
+ V4L/DVB (10657): [PATCH] V4L: missing parentheses?
+
+Sascha Hauer (5):
+ V4L/DVB (11030): soc-camera: add board hook to specify the buswidth for camera sensors
+ V4L/DVB (11031): pcm990 baseboard: add camera bus width switch setting
+ V4L/DVB (11032): mt9m001: allow setting of bus width from board code
+ V4L/DVB (11033): mt9v022: allow setting of bus width from board code
+ V4L/DVB (11034): soc-camera: remove now unused gpio member of struct soc_camera_link
+
+Scott James Remnant (1):
+ V4L/DVB (10947): Auto-load videodev module when device opened.
+
+Sebastian Andrzej Siewior (1):
+ V4L/DVB (10655): tvp514x: make the module aware of rich people
+
+Sergio Aguirre (1):
+ V4L/DVB (10575): V4L2: Add COLORFX user control
+
+Sri Deevi (1):
+ V4L/DVB (10950): xc5000: prepare it to be used by cx231xx module
+
+Stephan Wienczny (1):
+ V4L/DVB (10949): Add support for Terratec Cinergy HT PCI MKII
+
+Steven Toth (1):
+ V4L/DVB (11296): cx23885: bugfix error message if firmware is not found
+
+Stoyan Gaydarov (1):
+ V4L/DVB (11235): changed ioctls to unlocked
+
+Theodore Kilgore (2):
+ V4L/DVB (10986): mr97310a: don't discard frame headers on stream output
+ V4L/DVB (11213): gspca - sq905c: New subdriver.
+
+Thierry MERLE (5):
+ V4L/DVB (10306): usbvision: use usb_make_path to report bus info
+ V4L/DVB (10307): em28xx: use usb_make_path to report bus info
+ V4L/DVB (10308): uvcvideo: use usb_make_path to report bus info
+ V4L/DVB (10309): s2255drv: use usb_make_path to report bus info
+ V4L/DVB (10379): gspca - main: Use usb_make_path() for VIDIOC_QUERYCAP.
+
+Tim Farrington (1):
+ V4L/DVB (10574): saa7134: fix Avermedia E506R composite input
+
+Tobias Klauser (1):
+ V4L/DVB (10628): V4L: Storage class should be before const qualifier
+
+Tobias Lorenz (3):
+ V4L/DVB (10530): Documentation and code cleanups
+ V4L/DVB (10531): Code rearrangements in preparation for other report types
+ V4L/DVB (10534): Output HW/SW version from scratchpad
+
+Trent Piepho (41):
+ V4L/DVB (10558): bttv: norm value should be unsigned
+ V4L/DVB (10559): bttv: Fix TDA9880 norm setting code
+ V4L/DVB (10560): bttv: make tuner card info more consistent
+ V4L/DVB (10561): bttv: store card database more efficiently
+ V4L/DVB (10562): bttv: rework the way digital inputs are indicated
+ V4L/DVB (10563): bttv: clean up mux code for IVC-120G
+ V4L/DVB (10564): bttv: fix external mux for PHYTEC VD-009
+ V4L/DVB (10565): bttv: fix external mux for RemoteVision MX
+ V4L/DVB (10566): bttv: clean up mux code for IDS Eagle
+ V4L/DVB (10567): bttv: shrink muxsel data in card database
+ V4L/DVB (10568): bttv: dynamically allocate device data
+ V4L/DVB (10791): videodev: not possible to register NULL video_device
+ V4L/DVB (10792): cx88: remove unnecessary forward declaration of cx88_core
+ V4L/DVB (10794): v4l2: Move code to zero querybuf output struct to v4l2_ioctl
+ V4L/DVB (10811): videodev: only copy needed part of RW ioctl's parameter
+ V4L/DVB (10812): v4l2: Zero out read-only ioctls in one place
+ V4L/DVB (10813): v4l2: New function v4l2_video_std_frame_period
+ V4L/DVB (10814): saa7146: some small fixes
+ V4L/DVB (10815): bttv: Don't need to zero ioctl parameter fields
+ V4L/DVB (10816): cx88: Don't need to zero ioctl parameter fields
+ V4L/DVB (10817): stkwebcam: Don't need to zero ioctl parameter fields
+ V4L/DVB (10818): usbvision: Don't need to zero ioctl parameter fields
+ V4L/DVB (10819): gspca: Don't need to zero ioctl parameter fields
+ V4L/DVB (10820): meye: Don't need to zero ioctl parameter fields
+ V4L/DVB (10848): zoran: Change first argument to zoran_v4l2_buffer_status
+ V4L/DVB (10930): zoran: Unify buffer descriptors
+ V4L/DVB (10933): zoran: Pass zoran_fh pointers instead of file pointers
+ V4L/DVB (10934): zoran: replace functions names in strings with __func__
+ V4L/DVB (11260): v4l2-ioctl: Check format for S_PARM and G_PARM
+ V4L/DVB (11261): saa7146: Remove buffer type check from vidioc_g_parm
+ V4L/DVB (11262): bttv: Remove buffer type check from vidioc_g_parm
+ V4L/DVB (11263): gspca: Stop setting buffer type, and avoid memset in querycap
+ V4L/DVB (11264): omap24xxcam: Remove buffer type check from vidioc_s/g_parm
+ V4L/DVB (11265): stkwebcam: Remove buffer type check from g_parm and q/dq/reqbufs
+ V4L/DVB (11266): vino: Remove code for things already done by video_ioctl2
+ V4L/DVB (11267): cafe_ccic: Remove buffer type check from XXXbuf
+ V4L/DVB (11268): cx23885-417: Don't need to zero ioctl parameter fields
+ V4L/DVB (11269): cx88-blackbird: Stop setting buffer type in XXX_fmt_vid_cap
+ V4L/DVB (11270): meye: Remove buffer type checks from XXX_fmt_vid_cap, XXXbuf
+ V4L/DVB (11271): usbvision: Remove buffer type checks from enum_fmt_vid_cap, XXXbuf
+ V4L/DVB (11272): zr364xx: Remove code for things already done by video_ioctl2
+
+Uri Shkolnik (2):
+ V4L/DVB (10748): sms1xxx: restore smsusb_driver.name to smsusb
+ V4L/DVB (10750): import changes from Siano
+
+Uwe Bugla (2):
+ V4L/DVB (11287): Code cleanup (passes checkpatch now) of the b2c2-flexcop-drivers 1/2
+ V4L/DVB (11288): Code cleanup (passes checkpatch now) of the b2c2-flexcop-drivers 2/2
+
+Vitaly Wool (1):
+ V4L/DVB (10833): em28xx: enable Compro VideoMate ForYou sound
+
+Xoan Loureiro (1):
+ V4L/DVB (11289): Patch for Yuan MC770 DVB-T (1164:0871)
+
+klaas de waal (1):
+ V4L/DVB (11236): tda827x: fix locking issues with DVB-C
+
+sebastian.blanes@gmail.com (1):
+ V4L/DVB (10824): Add "Sony PlayTV" to dibcom driver
+
+diff --git a/Documentation/dvb/get_dvb_firmware b/Documentation/dvb/get_dvb_firmware
+index f2e908d..2f21ecd 100644
+--- a/Documentation/dvb/get_dvb_firmware
++++ b/Documentation/dvb/get_dvb_firmware
+@@ -25,7 +25,7 @@ use IO::Handle;
+ "tda10046lifeview", "av7110", "dec2000t", "dec2540t",
+ "dec3000s", "vp7041", "dibusb", "nxt2002", "nxt2004",
+ "or51211", "or51132_qam", "or51132_vsb", "bluebird",
+- "opera1");
++ "opera1", "cx231xx", "cx18", "cx23885", "pvrusb2" );
+
+ # Check args
+ syntax() if (scalar(@ARGV) != 1);
+@@ -37,8 +37,8 @@ for ($i=0; $i < scalar(@components); $i++) {
+ $outfile = eval($cid);
+ die $@ if $@;
+ print STDERR <<EOF;
+-Firmware $outfile extracted successfully.
+-Now copy it to either /usr/lib/hotplug/firmware or /lib/firmware
++Firmware(s) $outfile extracted successfully.
++Now copy it(they) to either /usr/lib/hotplug/firmware or /lib/firmware
+ (depending on configuration of firmware hotplug).
+ EOF
+ exit(0);
+@@ -345,6 +345,85 @@ sub or51211 {
+ $fwfile;
+ }
+
++sub cx231xx {
++ my $fwfile = "v4l-cx231xx-avcore-01.fw";
++ my $url = "http://linuxtv.org/downloads/firmware/$fwfile";
++ my $hash = "7d3bb956dc9df0eafded2b56ba57cc42";
++
++ checkstandard();
++
++ wgetfile($fwfile, $url);
++ verify($fwfile, $hash);
++
++ $fwfile;
++}
++
++sub cx18 {
++ my $url = "http://linuxtv.org/downloads/firmware/";
++
++ my %files = (
++ 'v4l-cx23418-apu.fw' => '588f081b562f5c653a3db1ad8f65939a',
++ 'v4l-cx23418-cpu.fw' => 'b6c7ed64bc44b1a6e0840adaeac39d79',
++ 'v4l-cx23418-dig.fw' => '95bc688d3e7599fd5800161e9971cc55',
++ );
++
++ checkstandard();
++
++ my $allfiles;
++ foreach my $fwfile (keys %files) {
++ wgetfile($fwfile, "$url/$fwfile");
++ verify($fwfile, $files{$fwfile});
++ $allfiles .= " $fwfile";
++ }
++
++ $allfiles =~ s/^\s//;
++
++ $allfiles;
++}
++
++sub cx23885 {
++ my $url = "http://linuxtv.org/downloads/firmware/";
++
++ my %files = (
++ 'v4l-cx23885-avcore-01.fw' => 'a9f8f5d901a7fb42f552e1ee6384f3bb',
++ 'v4l-cx23885-enc.fw' => 'a9f8f5d901a7fb42f552e1ee6384f3bb',
++ );
++
++ checkstandard();
++
++ my $allfiles;
++ foreach my $fwfile (keys %files) {
++ wgetfile($fwfile, "$url/$fwfile");
++ verify($fwfile, $files{$fwfile});
++ $allfiles .= " $fwfile";
++ }
++
++ $allfiles =~ s/^\s//;
++
++ $allfiles;
++}
++
++sub pvrusb2 {
++ my $url = "http://linuxtv.org/downloads/firmware/";
++
++ my %files = (
++ 'v4l-cx25840.fw' => 'dadb79e9904fc8af96e8111d9cb59320',
++ );
++
++ checkstandard();
++
++ my $allfiles;
++ foreach my $fwfile (keys %files) {
++ wgetfile($fwfile, "$url/$fwfile");
++ verify($fwfile, $files{$fwfile});
++ $allfiles .= " $fwfile";
++ }
++
++ $allfiles =~ s/^\s//;
++
++ $allfiles;
++}
++
+ sub or51132_qam {
+ my $fwfile = "dvb-fe-or51132-qam.fw";
+ my $url = "http://linuxtv.org/downloads/firmware/$fwfile";
+diff --git a/Documentation/feature-removal-schedule.txt b/Documentation/feature-removal-schedule.txt
+index 20d3b94..6e5b914 100644
+--- a/Documentation/feature-removal-schedule.txt
++++ b/Documentation/feature-removal-schedule.txt
+@@ -37,10 +37,10 @@ Who: Pavel Machek <pavel@suse.cz>
+
+ ---------------------------
+
+-What: Video4Linux API 1 ioctls and video_decoder.h from Video devices.
+-When: December 2008
+-Files: include/linux/video_decoder.h include/linux/videodev.h
+-Check: include/linux/video_decoder.h include/linux/videodev.h
++What: Video4Linux API 1 ioctls and from Video devices.
++When: July 2009
++Files: include/linux/videodev.h
++Check: include/linux/videodev.h
+ Why: V4L1 AP1 was replaced by V4L2 API during migration from 2.4 to 2.6
+ series. The old API have lots of drawbacks and don't provide enough
+ means to work with all video and audio standards. The newer API is
+diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt
+index f1d6399..1f779a2 100644
+--- a/Documentation/ioctl/ioctl-number.txt
++++ b/Documentation/ioctl/ioctl-number.txt
+@@ -122,10 +122,8 @@ Code Seq# Include File Comments
+ 'c' 00-7F linux/coda.h conflict!
+ 'c' 80-9F arch/s390/include/asm/chsc.h
+ 'd' 00-FF linux/char/drm/drm/h conflict!
+-'d' 00-DF linux/video_decoder.h conflict!
+ 'd' F0-FF linux/digi1.h
+ 'e' all linux/digi1.h conflict!
+-'e' 00-1F linux/video_encoder.h conflict!
+ 'e' 00-1F net/irda/irtty.h conflict!
+ 'f' 00-1F linux/ext2_fs.h
+ 'h' 00-7F Charon filesystem
+diff --git a/Documentation/video4linux/CARDLIST.bttv b/Documentation/video4linux/CARDLIST.bttv
+index 0d93fa1..f11c583 100644
+--- a/Documentation/video4linux/CARDLIST.bttv
++++ b/Documentation/video4linux/CARDLIST.bttv
+@@ -135,7 +135,7 @@
+ 134 -> Adlink RTV24
+ 135 -> DViCO FusionHDTV 5 Lite [18ac:d500]
+ 136 -> Acorp Y878F [9511:1540]
+-137 -> Conceptronic CTVFMi v2
++137 -> Conceptronic CTVFMi v2 [036e:109e]
+ 138 -> Prolink Pixelview PV-BT878P+ (Rev.2E)
+ 139 -> Prolink PixelView PlayTV MPEG2 PV-M4900
+ 140 -> Osprey 440 [0070:ff07]
+@@ -154,3 +154,7 @@
+ 153 -> PHYTEC VD-012 (bt878)
+ 154 -> PHYTEC VD-012-X1 (bt878)
+ 155 -> PHYTEC VD-012-X2 (bt878)
++156 -> IVCE-8784 [0000:f050,0001:f050,0002:f050,0003:f050]
++157 -> Geovision GV-800(S) (master) [800a:763d]
++158 -> Geovision GV-800(S) (slave) [800b:763d,800c:763d,800d:763d]
++159 -> ProVideo PV183 [1830:1540,1831:1540,1832:1540,1833:1540,1834:1540,1835:1540,1836:1540,1837:1540]
+diff --git a/Documentation/video4linux/CARDLIST.cx23885 b/Documentation/video4linux/CARDLIST.cx23885
+index 35ea130..91aa3c0 100644
+--- a/Documentation/video4linux/CARDLIST.cx23885
++++ b/Documentation/video4linux/CARDLIST.cx23885
+@@ -12,3 +12,7 @@
+ 11 -> DViCO FusionHDTV DVB-T Dual Express [18ac:db78]
+ 12 -> Leadtek Winfast PxDVR3200 H [107d:6681]
+ 13 -> Compro VideoMate E650F [185b:e800]
++ 14 -> TurboSight TBS 6920 [6920:8888]
++ 15 -> TeVii S470 [d470:9022]
++ 16 -> DVBWorld DVB-S2 2005 [0001:2005]
++ 17 -> NetUP Dual DVB-S2 CI [1b55:2a2c]
+diff --git a/Documentation/video4linux/CARDLIST.cx88 b/Documentation/video4linux/CARDLIST.cx88
+index 0d08f1e..71e9db0 100644
+--- a/Documentation/video4linux/CARDLIST.cx88
++++ b/Documentation/video4linux/CARDLIST.cx88
+@@ -77,3 +77,4 @@
+ 76 -> SATTRADE ST4200 DVB-S/S2 [b200:4200]
+ 77 -> TBS 8910 DVB-S [8910:8888]
+ 78 -> Prof 6200 DVB-S [b022:3022]
++ 79 -> Terratec Cinergy HT PCI MKII [153b:1177]
+diff --git a/Documentation/video4linux/CARDLIST.em28xx b/Documentation/video4linux/CARDLIST.em28xx
+index 75bded8..78d0a6e 100644
+--- a/Documentation/video4linux/CARDLIST.em28xx
++++ b/Documentation/video4linux/CARDLIST.em28xx
+@@ -7,12 +7,12 @@
+ 6 -> Terratec Cinergy 200 USB (em2800)
+ 7 -> Leadtek Winfast USB II (em2800) [0413:6023]
+ 8 -> Kworld USB2800 (em2800)
+- 9 -> Pinnacle Dazzle DVC 90/DVC 100 (em2820/em2840) [2304:0207,2304:021a]
++ 9 -> Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker (em2820/em2840) [1b80:e302,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) [0ccd:0047]
+- 14 -> Pixelview Prolink PlayTV USB 2.0 (em2820/em2840)
++ 14 -> SIIG AVTuner-PVR / Pixelview Prolink PlayTV USB 2.0 (em2820/em2840)
+ 15 -> V-Gear PocketTV (em2800)
+ 16 -> Hauppauge WinTV HVR 950 (em2883) [2040:6513,2040:6517,2040:651b]
+ 17 -> Pinnacle PCTV HD Pro Stick (em2880) [2304:0227]
+@@ -30,7 +30,6 @@
+ 30 -> Videology 20K14XUSB USB2.0 (em2820/em2840)
+ 31 -> Usbgear VD204v9 (em2821)
+ 32 -> Supercomp USB 2.0 TV (em2821)
+- 33 -> SIIG AVTuner-PVR/Prolink PlayTV USB 2.0 (em2821)
+ 34 -> Terratec Cinergy A Hybrid XS (em2860) [0ccd:004f]
+ 35 -> Typhoon DVD Maker (em2860)
+ 36 -> NetGMBH Cam (em2860)
+@@ -58,3 +57,7 @@
+ 58 -> Compro VideoMate ForYou/Stereo (em2820/em2840) [185b:2041]
+ 60 -> Hauppauge WinTV HVR 850 (em2883) [2040:651f]
+ 61 -> Pixelview PlayTV Box 4 USB 2.0 (em2820/em2840)
++ 62 -> Gadmei TVR200 (em2820/em2840)
++ 63 -> Kaiomy TVnPC U2 (em2860) [eb1a:e303]
++ 64 -> Easy Cap Capture DC-60 (em2860)
++ 65 -> IO-DATA GV-MVP/SZ (em2820/em2840) [04bb:0515]
+diff --git a/Documentation/video4linux/CARDLIST.saa7134 b/Documentation/video4linux/CARDLIST.saa7134
+index b8d4705..6dacf28 100644
+--- a/Documentation/video4linux/CARDLIST.saa7134
++++ b/Documentation/video4linux/CARDLIST.saa7134
+@@ -153,3 +153,5 @@
+ 152 -> Asus Tiger Rev:1.00 [1043:4857]
+ 153 -> Kworld Plus TV Analog Lite PCI [17de:7128]
+ 154 -> Avermedia AVerTV GO 007 FM Plus [1461:f31d]
++155 -> Hauppauge WinTV-HVR1120 ATSC/QAM-Hybrid [0070:6706,0070:6708]
++156 -> Hauppauge WinTV-HVR1110r3 [0070:6707,0070:6709,0070:670a]
+diff --git a/Documentation/video4linux/Zoran b/Documentation/video4linux/Zoran
+index 295462b..0e89e76 100644
+--- a/Documentation/video4linux/Zoran
++++ b/Documentation/video4linux/Zoran
+@@ -401,8 +401,7 @@ Additional notes for software developers:
+ first set the correct norm. Well, it seems logically correct: TV
+ standard is "more constant" for current country than geometry
+ settings of a variety of TV capture cards which may work in ITU or
+- square pixel format. Remember that users now can lock the norm to
+- avoid any ambiguity.
++ square pixel format.
+ --
+ Please note that lavplay/lavrec are also included in the MJPEG-tools
+ (http://mjpeg.sf.net/).
+diff --git a/Documentation/video4linux/bttv/Insmod-options b/Documentation/video4linux/bttv/Insmod-options
+index 5ef7578..bbe3ed6 100644
+--- a/Documentation/video4linux/bttv/Insmod-options
++++ b/Documentation/video4linux/bttv/Insmod-options
+@@ -81,16 +81,6 @@ tuner.o
+ pal=[bdgil] select PAL variant (used for some tuners
+ only, important for the audio carrier).
+
+-tvmixer.o
+- registers a mixer device for the TV card's volume/bass/treble
+- controls (requires a i2c audio control chip like the msp3400).
+-
+- insmod args:
+- debug=1 print some debug info to the syslog.
+- devnr=n allocate device #n (0 == /dev/mixer,
+- 1 = /dev/mixer1, ...), default is to
+- use the first free one.
+-
+ tvaudio.o
+ new, experimental module which is supported to provide a single
+ driver for all simple i2c audio control chips (tda/tea*).
+diff --git a/Documentation/video4linux/bttv/README b/Documentation/video4linux/bttv/README
+index 7ca2154..3a367cd 100644
+--- a/Documentation/video4linux/bttv/README
++++ b/Documentation/video4linux/bttv/README
+@@ -63,8 +63,8 @@ If you have some knowledge and spare time, please try to fix this
+ yourself (patches very welcome of course...) You know: The linux
+ slogan is "Do it yourself".
+
+-There is a mailing list: video4linux-list@redhat.com.
+-https://listman.redhat.com/mailman/listinfo/video4linux-list
++There is a mailing list: linux-media@vger.kernel.org
++http://vger.kernel.org/vger-lists.html#linux-media
+
+ If you have trouble with some specific TV card, try to ask there
+ instead of mailing me directly. The chance that someone with the
+diff --git a/Documentation/video4linux/cx2341x/README.hm12 b/Documentation/video4linux/cx2341x/README.hm12
+index 0e213ed..b36148e 100644
+--- a/Documentation/video4linux/cx2341x/README.hm12
++++ b/Documentation/video4linux/cx2341x/README.hm12
+@@ -32,6 +32,10 @@ Y, U and V planes. This code assumes frames of 720x576 (PAL) pixels.
+ The width of a frame is always 720 pixels, regardless of the actual specified
+ width.
+
++If the height is not a multiple of 32 lines, then the captured video is
++missing macroblocks at the end and is unusable. So the height must be a
++multiple of 32.
++
+ --------------------------------------------------------------------------
+
+ #include <stdio.h>
+diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt
+index 1c58a76..98529e0 100644
+--- a/Documentation/video4linux/gspca.txt
++++ b/Documentation/video4linux/gspca.txt
+@@ -32,6 +32,7 @@ spca561 041e:403b Creative Webcam Vista (VF0010)
+ zc3xx 041e:4051 Creative Live!Cam Notebook Pro (VF0250)
+ ov519 041e:4052 Creative Live! VISTA IM
+ zc3xx 041e:4053 Creative Live!Cam Video IM
++vc032x 041e:405b Creative Live! Cam Notebook Ultra (VC0130)
+ ov519 041e:405f Creative Live! VISTA VF0330
+ ov519 041e:4060 Creative Live! VISTA VF0350
+ ov519 041e:4061 Creative Live! VISTA VF0400
+@@ -193,6 +194,7 @@ spca500 084d:0003 D-Link DSC-350
+ spca500 08ca:0103 Aiptek PocketDV
+ sunplus 08ca:0104 Aiptek PocketDVII 1.3
+ sunplus 08ca:0106 Aiptek Pocket DV3100+
++mr97310a 08ca:0111 Aiptek PenCam VGA+
+ sunplus 08ca:2008 Aiptek Mini PenCam 2 M
+ sunplus 08ca:2010 Aiptek PocketCam 3M
+ sunplus 08ca:2016 Aiptek PocketCam 2 Mega
+@@ -215,6 +217,7 @@ pac207 093a:2468 PAC207
+ pac207 093a:2470 Genius GF112
+ pac207 093a:2471 Genius VideoCam ge111
+ pac207 093a:2472 Genius VideoCam ge110
++pac207 093a:2474 Genius iLook 111
+ pac207 093a:2476 Genius e-Messenger 112
+ pac7311 093a:2600 PAC7311 Typhoon
+ pac7311 093a:2601 Philips SPC 610 NC
+@@ -279,6 +282,7 @@ spca561 10fd:7e50 FlyCam Usb 100
+ zc3xx 10fd:8050 Typhoon Webshot II USB 300k
+ ov534 1415:2000 Sony HD Eye for PS3 (SLEH 00201)
+ pac207 145f:013a Trust WB-1300N
++vc032x 15b8:6001 HP 2.0 Megapixel
+ vc032x 15b8:6002 HP 2.0 Megapixel rz406aa
+ spca501 1776:501c Arowana 300K CMOS Camera
+ t613 17a1:0128 TASCORP JPEG Webcam, NGS Cyclops
+diff --git a/Documentation/video4linux/si470x.txt b/Documentation/video4linux/si470x.txt
+index 49679e6..3a7823e 100644
+--- a/Documentation/video4linux/si470x.txt
++++ b/Documentation/video4linux/si470x.txt
+@@ -1,6 +1,6 @@
+ Driver for USB radios for the Silicon Labs Si470x FM Radio Receivers
+
+-Copyright (c) 2008 Tobias Lorenz <tobias.lorenz@gmx.net>
++Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
+
+
+ Information from Silicon Labs
+@@ -41,7 +41,7 @@ chips are known to work:
+ - 10c4:818a: Silicon Labs USB FM Radio Reference Design
+ - 06e1:a155: ADS/Tech FM Radio Receiver (formerly Instant FM Music) (RDX-155-EF)
+ - 1b80:d700: KWorld USB FM Radio SnapMusic Mobile 700 (FM700)
+-- 10c5:819a: DealExtreme USB Radio
++- 10c5:819a: Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear)
+
+
+ Software
+@@ -52,6 +52,7 @@ Testing is usually done with most application under Debian/testing:
+ - gradio - GTK FM radio tuner
+ - kradio - Comfortable Radio Application for KDE
+ - radio - ncurses-based radio application
++- mplayer - The Ultimate Movie Player For Linux
+
+ There is also a library libv4l, which can be used. It's going to have a function
+ for frequency seeking, either by using hardware functionality as in radio-si470x
+@@ -69,7 +70,7 @@ Audio Listing
+ USB Audio is provided by the ALSA snd_usb_audio module. It is recommended to
+ also select SND_USB_AUDIO, as this is required to get sound from the radio. For
+ listing you have to redirect the sound, for example using one of the following
+-commands.
++commands. Please adjust the audio devices to your needs (/dev/dsp* and hw:x,x).
+
+ If you just want to test audio (very poor quality):
+ cat /dev/dsp1 > /dev/dsp
+@@ -80,6 +81,10 @@ sox -2 --endian little -r 96000 -t oss /dev/dsp1 -t oss /dev/dsp
+ If you use arts try:
+ arecord -D hw:1,0 -r96000 -c2 -f S16_LE | artsdsp aplay -B -
+
++If you use mplayer try:
++mplayer -radio adevice=hw=1.0:arate=96000 \
++ -rawaudio rate=96000 \
++ radio://<frequency>/capture
+
+ Module Parameters
+ =================
+diff --git a/Documentation/video4linux/v4l2-framework.txt b/Documentation/video4linux/v4l2-framework.txt
+index ff12437..a311773 100644
+--- a/Documentation/video4linux/v4l2-framework.txt
++++ b/Documentation/video4linux/v4l2-framework.txt
+@@ -47,7 +47,9 @@ All drivers have the following structure:
+ 3) Creating V4L2 device nodes (/dev/videoX, /dev/vbiX, /dev/radioX and
+ /dev/vtxX) and keeping track of device-node specific data.
+
+-4) Filehandle-specific structs containing per-filehandle data.
++4) Filehandle-specific structs containing per-filehandle data;
++
++5) video buffer handling.
+
+ This is a rough schematic of how it all relates:
+
+@@ -82,12 +84,20 @@ You must register the device instance:
+ v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);
+
+ Registration will initialize the v4l2_device struct and link dev->driver_data
+-to v4l2_dev. Registration will also set v4l2_dev->name to a value derived from
+-dev (driver name followed by the bus_id, to be precise). You may change the
+-name after registration if you want.
++to v4l2_dev. If v4l2_dev->name is empty then it will be set to a value derived
++from dev (driver name followed by the bus_id, to be precise). If you set it
++up before calling v4l2_device_register then it will be untouched. If dev is
++NULL, then you *must* setup v4l2_dev->name before calling v4l2_device_register.
+
+ The first 'dev' argument is normally the struct device pointer of a pci_dev,
+-usb_device or platform_device.
++usb_device or platform_device. It is rare for dev to be NULL, but it happens
++with ISA devices or when one device creates multiple PCI devices, thus making
++it impossible to associate v4l2_dev with a particular parent.
++
++You can also supply a notify() callback that can be called by sub-devices to
++notify you of events. Whether you need to set this depends on the sub-device.
++Any notifications a sub-device supports must be defined in a header in
++include/media/<subdevice>.h.
+
+ You unregister with:
+
+@@ -95,6 +105,17 @@ You unregister with:
+
+ Unregistering will also automatically unregister all subdevs from the device.
+
++If you have a hotpluggable device (e.g. a USB device), then when a disconnect
++happens the parent device becomes invalid. Since v4l2_device has a pointer to
++that parent device it has to be cleared as well to mark that the parent is
++gone. To do this call:
++
++ v4l2_device_disconnect(struct v4l2_device *v4l2_dev);
++
++This does *not* unregister the subdevs, so you still need to call the
++v4l2_device_unregister() function for that. If your driver is not hotpluggable,
++then there is no need to call v4l2_device_disconnect().
++
+ Sometimes you need to iterate over all devices registered by a specific
+ driver. This is usually the case if multiple device drivers use the same
+ hardware. E.g. the ivtvfb driver is a framebuffer driver that uses the ivtv
+@@ -134,7 +155,7 @@ The recommended approach is as follows:
+
+ static atomic_t drv_instance = ATOMIC_INIT(0);
+
+-static int __devinit drv_probe(struct pci_dev *dev,
++static int __devinit drv_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pci_id)
+ {
+ ...
+@@ -218,7 +239,7 @@ to add new ops and categories.
+
+ A sub-device driver initializes the v4l2_subdev struct using:
+
+- v4l2_subdev_init(subdev, &ops);
++ v4l2_subdev_init(sd, &ops);
+
+ Afterwards you need to initialize subdev->name with a unique name and set the
+ module owner. This is done for you if you use the i2c helper functions.
+@@ -226,7 +247,7 @@ module owner. This is done for you if you use the i2c helper functions.
+ A device (bridge) driver needs to register the v4l2_subdev with the
+ v4l2_device:
+
+- int err = v4l2_device_register_subdev(device, subdev);
++ int err = v4l2_device_register_subdev(v4l2_dev, sd);
+
+ This can fail if the subdev module disappeared before it could be registered.
+ After this function was called successfully the subdev->dev field points to
+@@ -234,17 +255,17 @@ the v4l2_device.
+
+ You can unregister a sub-device using:
+
+- v4l2_device_unregister_subdev(subdev);
++ v4l2_device_unregister_subdev(sd);
+
+-Afterwards the subdev module can be unloaded and subdev->dev == NULL.
++Afterwards the subdev module can be unloaded and sd->dev == NULL.
+
+ You can call an ops function either directly:
+
+- err = subdev->ops->core->g_chip_ident(subdev, &chip);
++ err = sd->ops->core->g_chip_ident(sd, &chip);
+
+ but it is better and easier to use this macro:
+
+- err = v4l2_subdev_call(subdev, core, g_chip_ident, &chip);
++ err = v4l2_subdev_call(sd, core, g_chip_ident, &chip);
+
+ The macro will to the right NULL pointer checks and returns -ENODEV if subdev
+ is NULL, -ENOIOCTLCMD if either subdev->core or subdev->core->g_chip_ident is
+@@ -252,19 +273,19 @@ NULL, or the actual result of the subdev->ops->core->g_chip_ident ops.
+
+ It is also possible to call all or a subset of the sub-devices:
+
+- v4l2_device_call_all(dev, 0, core, g_chip_ident, &chip);
++ v4l2_device_call_all(v4l2_dev, 0, core, g_chip_ident, &chip);
+
+ Any subdev that does not support this ops is skipped and error results are
+ ignored. If you want to check for errors use this:
+
+- err = v4l2_device_call_until_err(dev, 0, core, g_chip_ident, &chip);
++ err = v4l2_device_call_until_err(v4l2_dev, 0, core, g_chip_ident, &chip);
+
+ Any error except -ENOIOCTLCMD will exit the loop with that error. If no
+ errors (except -ENOIOCTLCMD) occured, then 0 is returned.
+
+ The second argument to both calls is a group ID. If 0, then all subdevs are
+ called. If non-zero, then only those whose group ID match that value will
+-be called. Before a bridge driver registers a subdev it can set subdev->grp_id
++be called. Before a bridge driver registers a subdev it can set sd->grp_id
+ to whatever value it wants (it's 0 by default). This value is owned by the
+ bridge driver and the sub-device driver will never modify or use it.
+
+@@ -276,6 +297,11 @@ e.g. AUDIO_CONTROLLER and specify that as the group ID value when calling
+ v4l2_device_call_all(). That ensures that it will only go to the subdev
+ that needs it.
+
++If the sub-device needs to notify its v4l2_device parent of an event, then
++it can call v4l2_subdev_notify(sd, notification, arg). This macro checks
++whether there is a notify() callback defined and returns -ENODEV if not.
++Otherwise the result of the notify() call is returned.
++
+ The advantage of using v4l2_subdev is that it is a generic struct and does
+ not contain any knowledge about the underlying hardware. So a driver might
+ contain several subdevs that use an I2C bus, but also a subdev that is
+@@ -340,6 +366,12 @@ Make sure to call v4l2_device_unregister_subdev(sd) when the remove() callback
+ is called. This will unregister the sub-device from the bridge driver. It is
+ safe to call this even if the sub-device was never registered.
+
++You need to do this because when the bridge driver destroys the i2c adapter
++the remove() callbacks are called of the i2c devices on that adapter.
++After that the corresponding v4l2_subdev structures are invalid, so they
++have to be unregistered first. Calling v4l2_device_unregister_subdev(sd)
++from the remove() callback ensures that this is always done correctly.
++
+
+ The bridge driver also has some helper functions it can use:
+
+@@ -349,8 +381,8 @@ This loads the given module (can be NULL if no module needs to be loaded) and
+ calls i2c_new_device() with the given i2c_adapter and chip/address arguments.
+ If all goes well, then it registers the subdev with the v4l2_device. It gets
+ the v4l2_device by calling i2c_get_adapdata(adapter), so you should make sure
+-that adapdata is set to v4l2_device when you setup the i2c_adapter in your
+-driver.
++to call i2c_set_adapdata(adapter, v4l2_device) when you setup the i2c_adapter
++in your driver.
+
+ You can also use v4l2_i2c_new_probed_subdev() which is very similar to
+ v4l2_i2c_new_subdev(), except that it has an array of possible I2C addresses
+@@ -358,6 +390,14 @@ that it should probe. Internally it calls i2c_new_probed_device().
+
+ Both functions return NULL if something went wrong.
+
++Note that the chipid you pass to v4l2_i2c_new_(probed_)subdev() is usually
++the same as the module name. It allows you to specify a chip variant, e.g.
++"saa7114" or "saa7115". In general though the i2c driver autodetects this.
++The use of chipid is something that needs to be looked at more closely at a
++later date. It differs between i2c drivers and as such can be confusing.
++To see which chip variants are supported you can look in the i2c driver code
++for the i2c_device_id table. This lists all the possibilities.
++
+
+ struct video_device
+ -------------------
+@@ -396,6 +436,15 @@ You should also set these fields:
+ - ioctl_ops: if you use the v4l2_ioctl_ops to simplify ioctl maintenance
+ (highly recommended to use this and it might become compulsory in the
+ future!), then set this to your v4l2_ioctl_ops struct.
++- parent: you only set this if v4l2_device was registered with NULL as
++ the parent device struct. This only happens in cases where one hardware
++ device has multiple PCI devices that all share the same v4l2_device core.
++
++ The cx88 driver is an example of this: one core v4l2_device struct, but
++ it is used by both an raw video PCI device (cx8800) and a MPEG PCI device
++ (cx8802). Since the v4l2_device cannot be associated with a particular
++ PCI device it is setup without a parent device. But when the struct
++ video_device is setup you do know which parent PCI device to use.
+
+ If you use v4l2_ioctl_ops, then you should set either .unlocked_ioctl or
+ .ioctl to video_ioctl2 in your v4l2_file_operations struct.
+@@ -499,8 +548,8 @@ There are a few useful helper functions:
+
+ You can set/get driver private data in the video_device struct using:
+
+-void *video_get_drvdata(struct video_device *dev);
+-void video_set_drvdata(struct video_device *dev, void *data);
++void *video_get_drvdata(struct video_device *vdev);
++void video_set_drvdata(struct video_device *vdev, void *data);
+
+ Note that you can safely call video_set_drvdata() before calling
+ video_register_device().
+@@ -519,3 +568,103 @@ void *video_drvdata(struct file *file);
+ You can go from a video_device struct to the v4l2_device struct using:
+
+ struct v4l2_device *v4l2_dev = vdev->v4l2_dev;
++
++video buffer helper functions
++-----------------------------
++
++The v4l2 core API provides a standard method for dealing with video
++buffers. Those methods allow a driver to implement read(), mmap() and
++overlay() on a consistent way.
++
++There are currently methods for using video buffers on devices that
++supports DMA with scatter/gather method (videobuf-dma-sg), DMA with
++linear access (videobuf-dma-contig), and vmalloced buffers, mostly
++used on USB drivers (videobuf-vmalloc).
++
++Any driver using videobuf should provide operations (callbacks) for
++four handlers:
++
++ops->buf_setup - calculates the size of the video buffers and avoid they
++ to waste more than some maximum limit of RAM;
++ops->buf_prepare - fills the video buffer structs and calls
++ videobuf_iolock() to alloc and prepare mmaped memory;
++ops->buf_queue - advices the driver that another buffer were
++ requested (by read() or by QBUF);
++ops->buf_release - frees any buffer that were allocated.
++
++In order to use it, the driver need to have a code (generally called at
++interrupt context) that will properly handle the buffer request lists,
++announcing that a new buffer were filled.
++
++The irq handling code should handle the videobuf task lists, in order
++to advice videobuf that a new frame were filled, in order to honor to a
++request. The code is generally like this one:
++ if (list_empty(&dma_q->active))
++ return;
++
++ buf = list_entry(dma_q->active.next, struct vbuffer, vb.queue);
++
++ if (!waitqueue_active(&buf->vb.done))
++ return;
++
++ /* Some logic to handle the buf may be needed here */
++
++ list_del(&buf->vb.queue);
++ do_gettimeofday(&buf->vb.ts);
++ wake_up(&buf->vb.done);
++
++Those are the videobuffer functions used on drivers, implemented on
++videobuf-core:
++
++- Videobuf init functions
++ videobuf_queue_sg_init()
++ Initializes the videobuf infrastructure. This function should be
++ called before any other videobuf function on drivers that uses DMA
++ Scatter/Gather buffers.
++
++ videobuf_queue_dma_contig_init
++ Initializes the videobuf infrastructure. This function should be
++ called before any other videobuf function on drivers that need DMA
++ contiguous buffers.
++
++ videobuf_queue_vmalloc_init()
++ Initializes the videobuf infrastructure. This function should be
++ called before any other videobuf function on USB (and other drivers)
++ that need a vmalloced type of videobuf.
++
++- videobuf_iolock()
++ Prepares the videobuf memory for the proper method (read, mmap, overlay).
++
++- videobuf_queue_is_busy()
++ Checks if a videobuf is streaming.
++
++- videobuf_queue_cancel()
++ Stops video handling.
++
++- videobuf_mmap_free()
++ frees mmap buffers.
++
++- videobuf_stop()
++ Stops video handling, ends mmap and frees mmap and other buffers.
++
++- V4L2 api functions. Those functions correspond to VIDIOC_foo ioctls:
++ videobuf_reqbufs(), videobuf_querybuf(), videobuf_qbuf(),
++ videobuf_dqbuf(), videobuf_streamon(), videobuf_streamoff().
++
++- V4L1 api function (corresponds to VIDIOCMBUF ioctl):
++ videobuf_cgmbuf()
++ This function is used to provide backward compatibility with V4L1
++ API.
++
++- Some help functions for read()/poll() operations:
++ videobuf_read_stream()
++ For continuous stream read()
++ videobuf_read_one()
++ For snapshot read()
++ videobuf_poll_stream()
++ polling help function
++
++The better way to understand it is to take a look at vivi driver. One
++of the main reasons for vivi is to be a videobuf usage example. the
++vivi_thread_tick() does the task that the IRQ callback would do on PCI
++drivers (or the irq callback on USB).
+diff --git a/Documentation/video4linux/v4lgrab.c b/Documentation/video4linux/v4lgrab.c
+index d6e70be..05769cf 100644
+--- a/Documentation/video4linux/v4lgrab.c
++++ b/Documentation/video4linux/v4lgrab.c
+@@ -105,8 +105,8 @@ int main(int argc, char ** argv)
+ struct video_picture vpic;
+
+ unsigned char *buffer, *src;
+- int bpp = 24, r, g, b;
+- unsigned int i, src_depth;
++ int bpp = 24, r = 0, g = 0, b = 0;
++ unsigned int i, src_depth = 16;
+
+ if (fd < 0) {
+ perror(VIDEO_DEV);
+diff --git a/Documentation/video4linux/zr364xx.txt b/Documentation/video4linux/zr364xx.txt
+index 5c81e3a..7f3d195 100644
+--- a/Documentation/video4linux/zr364xx.txt
++++ b/Documentation/video4linux/zr364xx.txt
+@@ -65,3 +65,4 @@ Vendor Product Distributor Model
+ 0x06d6 0x003b Trust Powerc@m 970Z
+ 0x0a17 0x004e Pentax Optio 50
+ 0x041e 0x405d Creative DiVi CAM 516
++0x08ca 0x2102 Aiptek DV T300
+diff --git a/MAINTAINERS b/MAINTAINERS
+index 5d460c9..b41a4dc 100644
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -1040,7 +1040,6 @@ BTTV VIDEO4LINUX DRIVER
+ P: Mauro Carvalho Chehab
+ M: mchehab@infradead.org
+ L: linux-media@vger.kernel.org
+-L: video4linux-list@redhat.com
+ W: http://linuxtv.org
+ T: git kernel.org:/pub/scm/linux/kernel/git/mchehab/linux-2.6.git
+ S: Maintained
+@@ -4746,7 +4745,6 @@ VIDEO FOR LINUX (V4L)
+ P: Mauro Carvalho Chehab
+ M: mchehab@infradead.org
+ L: linux-media@vger.kernel.org
+-L: video4linux-list@redhat.com
+ W: http://linuxtv.org
+ T: git kernel.org:/pub/scm/linux/kernel/git/mchehab/linux-2.6.git
+ S: Maintained
+diff --git a/arch/arm/mach-pxa/pcm990-baseboard.c b/arch/arm/mach-pxa/pcm990-baseboard.c
+index 34841c7..fd8f786 100644
+--- a/arch/arm/mach-pxa/pcm990-baseboard.c
++++ b/arch/arm/mach-pxa/pcm990-baseboard.c
+@@ -381,14 +381,49 @@ static struct pca953x_platform_data pca9536_data = {
+ .gpio_base = NR_BUILTIN_GPIO + 1,
+ };
+
+-static struct soc_camera_link iclink[] = {
+- {
+- .bus_id = 0, /* Must match with the camera ID above */
+- .gpio = NR_BUILTIN_GPIO + 1,
+- }, {
+- .bus_id = 0, /* Must match with the camera ID above */
+- .gpio = -ENXIO,
++static int gpio_bus_switch;
++
++static int pcm990_camera_set_bus_param(struct soc_camera_link *link,
++ unsigned long flags)
++{
++ if (gpio_bus_switch <= 0) {
++ if (flags == SOCAM_DATAWIDTH_10)
++ return 0;
++ else
++ return -EINVAL;
++ }
++
++ if (flags & SOCAM_DATAWIDTH_8)
++ gpio_set_value(gpio_bus_switch, 1);
++ else
++ gpio_set_value(gpio_bus_switch, 0);
++
++ return 0;
++}
++
++static unsigned long pcm990_camera_query_bus_param(struct soc_camera_link *link)
++{
++ int ret;
++
++ if (!gpio_bus_switch) {
++ ret = gpio_request(NR_BUILTIN_GPIO + 1, "camera");
++ if (!ret) {
++ gpio_bus_switch = NR_BUILTIN_GPIO + 1;
++ gpio_direction_output(gpio_bus_switch, 0);
++ } else
++ gpio_bus_switch = -EINVAL;
+ }
++
++ if (gpio_bus_switch > 0)
++ return SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_10;
++ else
++ return SOCAM_DATAWIDTH_10;
++}
++
++static struct soc_camera_link iclink = {
++ .bus_id = 0, /* Must match with the camera ID above */
++ .query_bus_param = pcm990_camera_query_bus_param,
++ .set_bus_param = pcm990_camera_set_bus_param,
+ };
+
+ /* Board I2C devices. */
+@@ -399,10 +434,10 @@ static struct i2c_board_info __initdata pcm990_i2c_devices[] = {
+ .platform_data = &pca9536_data,
+ }, {
+ I2C_BOARD_INFO("mt9v022", 0x48),
+- .platform_data = &iclink[0], /* With extender */
++ .platform_data = &iclink, /* With extender */
+ }, {
+ I2C_BOARD_INFO("mt9m001", 0x5d),
+- .platform_data = &iclink[0], /* With extender */
++ .platform_data = &iclink, /* With extender */
+ },
+ };
+ #endif /* CONFIG_VIDEO_PXA27x ||CONFIG_VIDEO_PXA27x_MODULE */
+diff --git a/arch/arm/plat-mxc/include/mach/mx3_camera.h b/arch/arm/plat-mxc/include/mach/mx3_camera.h
+new file mode 100644
+index 0000000..36d7ff2
+--- /dev/null
++++ b/arch/arm/plat-mxc/include/mach/mx3_camera.h
+@@ -0,0 +1,52 @@
++/*
++ * mx3_camera.h - i.MX3x camera driver header file
++ *
++ * Copyright (C) 2008, Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
++ *
++ * 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 _MX3_CAMERA_H_
++#define _MX3_CAMERA_H_
++
++#include <linux/device.h>
++
++#define MX3_CAMERA_CLK_SRC 1
++#define MX3_CAMERA_EXT_VSYNC 2
++#define MX3_CAMERA_DP 4
++#define MX3_CAMERA_PCP 8
++#define MX3_CAMERA_HSP 0x10
++#define MX3_CAMERA_VSP 0x20
++#define MX3_CAMERA_DATAWIDTH_4 0x40
++#define MX3_CAMERA_DATAWIDTH_8 0x80
++#define MX3_CAMERA_DATAWIDTH_10 0x100
++#define MX3_CAMERA_DATAWIDTH_15 0x200
++
++#define MX3_CAMERA_DATAWIDTH_MASK (MX3_CAMERA_DATAWIDTH_4 | MX3_CAMERA_DATAWIDTH_8 | \
++ MX3_CAMERA_DATAWIDTH_10 | MX3_CAMERA_DATAWIDTH_15)
++
++/**
++ * struct mx3_camera_pdata - i.MX3x camera platform data
++ * @flags: MX3_CAMERA_* flags
++ * @mclk_10khz: master clock frequency in 10kHz units
++ * @dma_dev: IPU DMA device to match against in channel allocation
++ */
++struct mx3_camera_pdata {
++ unsigned long flags;
++ unsigned long mclk_10khz;
++ struct device *dma_dev;
++};
++
++#endif
+diff --git a/arch/sh/boards/board-ap325rxa.c b/arch/sh/boards/board-ap325rxa.c
+index 15b6d45..5188adc 100644
+--- a/arch/sh/boards/board-ap325rxa.c
++++ b/arch/sh/boards/board-ap325rxa.c
+@@ -299,7 +299,8 @@ static struct platform_device camera_device = {
+
+ static struct sh_mobile_ceu_info sh_mobile_ceu_info = {
+ .flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_HSYNC_ACTIVE_HIGH |
+- SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_MASTER | SOCAM_DATAWIDTH_8,
++ SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER |
++ SOCAM_DATAWIDTH_8,
+ };
+
+ static struct resource ceu_resources[] = {
+diff --git a/arch/sh/boards/mach-migor/setup.c b/arch/sh/boards/mach-migor/setup.c
+index 28e56c5..6d63959 100644
+--- a/arch/sh/boards/mach-migor/setup.c
++++ b/arch/sh/boards/mach-migor/setup.c
+@@ -352,8 +352,9 @@ static int tw9910_power(struct device *dev, int mode)
+ }
+
+ static struct sh_mobile_ceu_info sh_mobile_ceu_info = {
+- .flags = SOCAM_MASTER | SOCAM_DATAWIDTH_8 | SOCAM_PCLK_SAMPLE_RISING \
+- | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH,
++ .flags = SOCAM_MASTER | SOCAM_DATAWIDTH_8 | SOCAM_PCLK_SAMPLE_RISING
++ | SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH
++ | SOCAM_DATA_ACTIVE_HIGH,
+ };
+
+ static struct resource migor_ceu_resources[] = {
+diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
+index 93ea201..223c36e 100644
+--- a/drivers/media/Kconfig
++++ b/drivers/media/Kconfig
+@@ -117,7 +117,7 @@ source "drivers/media/dvb/Kconfig"
+ config DAB
+ boolean "DAB adapters"
+ ---help---
+- Allow selecting support for for Digital Audio Broadcasting (DAB)
++ Allow selecting support for Digital Audio Broadcasting (DAB)
+ Receiver adapters.
+
+ if DAB
+diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c
+index d8229a0..3fe158a 100644
+--- a/drivers/media/common/ir-keymaps.c
++++ b/drivers/media/common/ir-keymaps.c
+@@ -153,6 +153,65 @@ IR_KEYTAB_TYPE ir_codes_avermedia_m135a[IR_KEYTAB_SIZE] = {
+ };
+ EXPORT_SYMBOL_GPL(ir_codes_avermedia_m135a);
+
++/* Oldrich Jedlicka <oldium.pro@seznam.cz> */
++IR_KEYTAB_TYPE ir_codes_avermedia_cardbus[IR_KEYTAB_SIZE] = {
++ [0x00] = KEY_POWER,
++ [0x01] = KEY_TUNER, /* TV/FM */
++ [0x03] = KEY_TEXT, /* Teletext */
++ [0x04] = KEY_EPG,
++ [0x05] = KEY_1,
++ [0x06] = KEY_2,
++ [0x07] = KEY_3,
++ [0x08] = KEY_AUDIO,
++ [0x09] = KEY_4,
++ [0x0a] = KEY_5,
++ [0x0b] = KEY_6,
++ [0x0c] = KEY_ZOOM, /* Full screen */
++ [0x0d] = KEY_7,
++ [0x0e] = KEY_8,
++ [0x0f] = KEY_9,
++ [0x10] = KEY_PAGEUP, /* 16-CH PREV */
++ [0x11] = KEY_0,
++ [0x12] = KEY_INFO,
++ [0x13] = KEY_AGAIN, /* CH RTN - channel return */
++ [0x14] = KEY_MUTE,
++ [0x15] = KEY_EDIT, /* Autoscan */
++ [0x17] = KEY_SAVE, /* Screenshot */
++ [0x18] = KEY_PLAYPAUSE,
++ [0x19] = KEY_RECORD,
++ [0x1a] = KEY_PLAY,
++ [0x1b] = KEY_STOP,
++ [0x1c] = KEY_FASTFORWARD,
++ [0x1d] = KEY_REWIND,
++ [0x1e] = KEY_VOLUMEDOWN,
++ [0x1f] = KEY_VOLUMEUP,
++ [0x22] = KEY_SLEEP, /* Sleep */
++ [0x23] = KEY_ZOOM, /* Aspect */
++ [0x26] = KEY_SCREEN, /* Pos */
++ [0x27] = KEY_ANGLE, /* Size */
++ [0x28] = KEY_SELECT, /* Select */
++ [0x29] = KEY_BLUE, /* Blue/Picture */
++ [0x2a] = KEY_BACKSPACE, /* Back */
++ [0x2b] = KEY_MEDIA, /* PIP (Picture-in-picture) */
++ [0x2c] = KEY_DOWN,
++ [0x2e] = KEY_DOT,
++ [0x2f] = KEY_TV, /* Live TV */
++ [0x32] = KEY_LEFT,
++ [0x33] = KEY_CLEAR, /* Clear */
++ [0x35] = KEY_RED, /* Red/TV */
++ [0x36] = KEY_UP,
++ [0x37] = KEY_HOME, /* Home */
++ [0x39] = KEY_GREEN, /* Green/Video */
++ [0x3d] = KEY_YELLOW, /* Yellow/Music */
++ [0x3e] = KEY_OK, /* Ok */
++ [0x3f] = KEY_RIGHT,
++ [0x40] = KEY_NEXT, /* Next */
++ [0x41] = KEY_PREVIOUS, /* Previous */
++ [0x42] = KEY_CHANNELDOWN, /* Channel down */
++ [0x43] = KEY_CHANNELUP /* Channel up */
++};
++EXPORT_SYMBOL_GPL(ir_codes_avermedia_cardbus);
++
+ /* Attila Kondoros <attila.kondoros@chello.hu> */
+ IR_KEYTAB_TYPE ir_codes_apac_viewcomp[IR_KEYTAB_SIZE] = {
+
+@@ -2452,6 +2511,55 @@ IR_KEYTAB_TYPE ir_codes_kworld_plus_tv_analog[IR_KEYTAB_SIZE] = {
+ };
+ EXPORT_SYMBOL_GPL(ir_codes_kworld_plus_tv_analog);
+
++/* Kaiomy TVnPC U2
++ Mauro Carvalho Chehab <mchehab@infradead.org>
++ */
++IR_KEYTAB_TYPE ir_codes_kaiomy[IR_KEYTAB_SIZE] = {
++ [0x43] = KEY_POWER2,
++ [0x01] = KEY_LIST,
++ [0x0b] = KEY_ZOOM,
++ [0x03] = KEY_POWER,
++
++ [0x04] = KEY_1,
++ [0x08] = KEY_2,
++ [0x02] = KEY_3,
++
++ [0x0f] = KEY_4,
++ [0x05] = KEY_5,
++ [0x06] = KEY_6,
++
++ [0x0c] = KEY_7,
++ [0x0d] = KEY_8,
++ [0x0a] = KEY_9,
++
++ [0x11] = KEY_0,
++
++ [0x09] = KEY_CHANNELUP,
++ [0x07] = KEY_CHANNELDOWN,
++
++ [0x0e] = KEY_VOLUMEUP,
++ [0x13] = KEY_VOLUMEDOWN,
++
++ [0x10] = KEY_HOME,
++ [0x12] = KEY_ENTER,
++
++ [0x14] = KEY_RECORD,
++ [0x15] = KEY_STOP,
++ [0x16] = KEY_PLAY,
++ [0x17] = KEY_MUTE,
++
++ [0x18] = KEY_UP,
++ [0x19] = KEY_DOWN,
++ [0x1a] = KEY_LEFT,
++ [0x1b] = KEY_RIGHT,
++
++ [0x1c] = KEY_RED,
++ [0x1d] = KEY_GREEN,
++ [0x1e] = KEY_YELLOW,
++ [0x1f] = KEY_BLUE,
++};
++EXPORT_SYMBOL_GPL(ir_codes_kaiomy);
++
+ IR_KEYTAB_TYPE ir_codes_avermedia_a16d[IR_KEYTAB_SIZE] = {
+ [0x20] = KEY_LIST,
+ [0x00] = KEY_POWER,
+@@ -2604,3 +2712,41 @@ IR_KEYTAB_TYPE ir_codes_ati_tv_wonder_hd_600[IR_KEYTAB_SIZE] = {
+ };
+
+ EXPORT_SYMBOL_GPL(ir_codes_ati_tv_wonder_hd_600);
++
++/* DVBWorld remotes
++ Igor M. Liplianin <liplianin@me.by>
++ */
++IR_KEYTAB_TYPE ir_codes_dm1105_nec[IR_KEYTAB_SIZE] = {
++ [0x0a] = KEY_Q, /*power*/
++ [0x0c] = KEY_M, /*mute*/
++ [0x11] = KEY_1,
++ [0x12] = KEY_2,
++ [0x13] = KEY_3,
++ [0x14] = KEY_4,
++ [0x15] = KEY_5,
++ [0x16] = KEY_6,
++ [0x17] = KEY_7,
++ [0x18] = KEY_8,
++ [0x19] = KEY_9,
++ [0x10] = KEY_0,
++ [0x1c] = KEY_PAGEUP, /*ch+*/
++ [0x0f] = KEY_PAGEDOWN, /*ch-*/
++ [0x1a] = KEY_O, /*vol+*/
++ [0x0e] = KEY_Z, /*vol-*/
++ [0x04] = KEY_R, /*rec*/
++ [0x09] = KEY_D, /*fav*/
++ [0x08] = KEY_BACKSPACE, /*rewind*/
++ [0x07] = KEY_A, /*fast*/
++ [0x0b] = KEY_P, /*pause*/
++ [0x02] = KEY_ESC, /*cancel*/
++ [0x03] = KEY_G, /*tab*/
++ [0x00] = KEY_UP, /*up*/
++ [0x1f] = KEY_ENTER, /*ok*/
++ [0x01] = KEY_DOWN, /*down*/
++ [0x05] = KEY_C, /*cap*/
++ [0x06] = KEY_S, /*stop*/
++ [0x40] = KEY_F, /*full*/
++ [0x1e] = KEY_W, /*tvmode*/
++ [0x1b] = KEY_B, /*recall*/
++};
++EXPORT_SYMBOL_GPL(ir_codes_dm1105_nec);
+diff --git a/drivers/media/common/saa7146_core.c b/drivers/media/common/saa7146_core.c
+index d599d36..982f000 100644
+--- a/drivers/media/common/saa7146_core.c
++++ b/drivers/media/common/saa7146_core.c
+@@ -452,8 +452,6 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
+ INFO(("found saa7146 @ mem %p (revision %d, irq %d) (0x%04x,0x%04x).\n", dev->mem, dev->revision, pci->irq, pci->subsystem_vendor, pci->subsystem_device));
+ dev->ext = ext;
+
+- pci_set_drvdata(pci, dev);
+-
+ mutex_init(&dev->lock);
+ spin_lock_init(&dev->int_slock);
+ spin_lock_init(&dev->slock);
+@@ -477,8 +475,12 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
+
+ if (ext->attach(dev, pci_ext)) {
+ DEB_D(("ext->attach() failed for %p. skipping device.\n",dev));
+- goto err_unprobe;
++ goto err_free_i2c;
+ }
++ /* V4L extensions will set the pci drvdata to the v4l2_device in the
++ attach() above. So for those cards that do not use V4L we have to
++ set it explicitly. */
++ pci_set_drvdata(pci, &dev->v4l2_dev);
+
+ INIT_LIST_HEAD(&dev->item);
+ list_add_tail(&dev->item,&saa7146_devices);
+@@ -488,8 +490,6 @@ static int saa7146_init_one(struct pci_dev *pci, const struct pci_device_id *ent
+ out:
+ return err;
+
+-err_unprobe:
+- pci_set_drvdata(pci, NULL);
+ err_free_i2c:
+ pci_free_consistent(pci, SAA7146_RPS_MEM, dev->d_i2c.cpu_addr,
+ dev->d_i2c.dma_handle);
+@@ -514,7 +514,8 @@ err_free:
+
+ static void saa7146_remove_one(struct pci_dev *pdev)
+ {
+- struct saa7146_dev* dev = pci_get_drvdata(pdev);
++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pdev);
++ struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev);
+ struct {
+ void *addr;
+ dma_addr_t dma;
+@@ -528,6 +529,8 @@ static void saa7146_remove_one(struct pci_dev *pdev)
+ DEB_EE(("dev:%p\n",dev));
+
+ dev->ext->detach(dev);
++ /* Zero the PCI drvdata after use. */
++ pci_set_drvdata(pdev, NULL);
+
+ /* shut down all video dma transfers */
+ saa7146_write(dev, MC1, 0x00ff0000);
+diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c
+index cf06f4d..620f655 100644
+--- a/drivers/media/common/saa7146_fops.c
++++ b/drivers/media/common/saa7146_fops.c
+@@ -308,14 +308,6 @@ static int fops_release(struct file *file)
+ return 0;
+ }
+
+-static long fops_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+-{
+-/*
+- DEB_EE(("file:%p, cmd:%d, arg:%li\n", file, cmd, arg));
+-*/
+- return video_usercopy(file, cmd, arg, saa7146_video_do_ioctl);
+-}
+-
+ static int fops_mmap(struct file *file, struct vm_area_struct * vma)
+ {
+ struct saa7146_fh *fh = file->private_data;
+@@ -425,7 +417,7 @@ static const struct v4l2_file_operations video_fops =
+ .write = fops_write,
+ .poll = fops_poll,
+ .mmap = fops_mmap,
+- .ioctl = fops_ioctl,
++ .ioctl = video_ioctl2,
+ };
+
+ static void vv_callback(struct saa7146_dev *dev, unsigned long status)
+@@ -452,19 +444,22 @@ static void vv_callback(struct saa7146_dev *dev, unsigned long status)
+ }
+ }
+
+-static struct video_device device_template =
+-{
+- .fops = &video_fops,
+- .minor = -1,
+-};
+-
+ int saa7146_vv_init(struct saa7146_dev* dev, struct saa7146_ext_vv *ext_vv)
+ {
+- struct saa7146_vv *vv = kzalloc (sizeof(struct saa7146_vv),GFP_KERNEL);
+- if( NULL == vv ) {
++ struct saa7146_vv *vv;
++ int err;
++
++ err = v4l2_device_register(&dev->pci->dev, &dev->v4l2_dev);
++ if (err)
++ return err;
++
++ vv = kzalloc(sizeof(struct saa7146_vv), GFP_KERNEL);
++ if (vv == NULL) {
+ ERR(("out of memory. aborting.\n"));
+- return -1;
++ return -ENOMEM;
+ }
++ ext_vv->ops = saa7146_video_ioctl_ops;
++ ext_vv->core_ops = &saa7146_video_ioctl_ops;
+
+ DEB_EE(("dev:%p\n",dev));
+
+@@ -507,6 +502,7 @@ int saa7146_vv_release(struct saa7146_dev* dev)
+
+ DEB_EE(("dev:%p\n",dev));
+
++ v4l2_device_unregister(&dev->v4l2_dev);
+ pci_free_consistent(dev->pci, SAA7146_CLIPPING_MEM, vv->d_clipping.cpu_addr, vv->d_clipping.dma_handle);
+ kfree(vv);
+ dev->vv_data = NULL;
+@@ -521,6 +517,8 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
+ {
+ struct saa7146_vv *vv = dev->vv_data;
+ struct video_device *vfd;
++ int err;
++ int i;
+
+ DEB_EE(("dev:%p, name:'%s', type:%d\n",dev,name,type));
+
+@@ -529,16 +527,20 @@ int saa7146_register_device(struct video_device **vid, struct saa7146_dev* dev,
+ if (vfd == NULL)
+ return -ENOMEM;
+
+- memcpy(vfd, &device_template, sizeof(struct video_device));
+- strlcpy(vfd->name, name, sizeof(vfd->name));
++ vfd->fops = &video_fops;
++ vfd->ioctl_ops = &dev->ext_vv_data->ops;
+ vfd->release = video_device_release;
++ vfd->tvnorms = 0;
++ for (i = 0; i < dev->ext_vv_data->num_stds; i++)
++ vfd->tvnorms |= dev->ext_vv_data->stds[i].id;
++ strlcpy(vfd->name, name, sizeof(vfd->name));
+ video_set_drvdata(vfd, dev);
+
+- // fixme: -1 should be an insmod parameter *for the extension* (like "video_nr");
+- if (video_register_device(vfd, type, -1) < 0) {
++ err = video_register_device(vfd, type, -1);
++ if (err < 0) {
+ ERR(("cannot register v4l2 device. skipping.\n"));
+ video_device_release(vfd);
+- return -1;
++ return err;
+ }
+
+ if( VFL_TYPE_GRABBER == type ) {
+diff --git a/drivers/media/common/saa7146_i2c.c b/drivers/media/common/saa7146_i2c.c
+index c11da4d..7e8f568 100644
+--- a/drivers/media/common/saa7146_i2c.c
++++ b/drivers/media/common/saa7146_i2c.c
+@@ -293,7 +293,6 @@ static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *m
+ int i = 0, count = 0;
+ __le32 *buffer = dev->d_i2c.cpu_addr;
+ int err = 0;
+- int address_err = 0;
+ int short_delay = 0;
+
+ if (mutex_lock_interruptible(&dev->i2c_lock))
+@@ -333,17 +332,10 @@ static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *m
+ i2c address probing, however, and address errors indicate that a
+ device is really *not* there. retrying in that case
+ increases the time the device needs to probe greatly, so
+- it should be avoided. because of the fact, that only
+- analog based cards use irq based i2c transactions (for dvb
+- cards, this screwes up other interrupt sources), we bail out
+- completely for analog cards after an address error and trust
+- the saa7146 address error detection. */
+- if ( -EREMOTEIO == err ) {
+- if( 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags)) {
+- goto out;
+- }
+- address_err++;
+- }
++ it should be avoided. So we bail out in irq mode after an
++ address error and trust the saa7146 address error detection. */
++ if (-EREMOTEIO == err && 0 != (SAA7146_USE_I2C_IRQ & dev->ext->flags))
++ goto out;
+ DEB_I2C(("error while sending message(s). starting again.\n"));
+ break;
+ }
+@@ -358,10 +350,9 @@ static int saa7146_i2c_transfer(struct saa7146_dev *dev, const struct i2c_msg *m
+
+ } while (err != num && retries--);
+
+- /* if every retry had an address error, exit right away */
+- if (address_err == retries) {
++ /* quit if any error occurred */
++ if (err != num)
+ goto out;
+- }
+
+ /* if any things had to be read, get the results */
+ if ( 0 != saa7146_i2c_msg_cleanup(msgs, num, buffer)) {
+@@ -390,7 +381,8 @@ out:
+ /* utility functions */
+ static int saa7146_i2c_xfer(struct i2c_adapter* adapter, struct i2c_msg *msg, int num)
+ {
+- struct saa7146_dev* dev = i2c_get_adapdata(adapter);
++ struct v4l2_device *v4l2_dev = i2c_get_adapdata(adapter);
++ struct saa7146_dev *dev = to_saa7146_dev(v4l2_dev);
+
+ /* use helper function to transfer data */
+ return saa7146_i2c_transfer(dev, msg, num, adapter->retries);
+@@ -417,9 +409,8 @@ int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c
+ dev->i2c_bitrate = bitrate;
+ saa7146_i2c_reset(dev);
+
+- if( NULL != i2c_adapter ) {
+- BUG_ON(!i2c_adapter->class);
+- i2c_set_adapdata(i2c_adapter,dev);
++ if (i2c_adapter) {
++ i2c_set_adapdata(i2c_adapter, &dev->v4l2_dev);
+ i2c_adapter->dev.parent = &dev->pci->dev;
+ i2c_adapter->algo = &saa7146_algo;
+ i2c_adapter->algo_data = NULL;
+diff --git a/drivers/media/common/saa7146_video.c b/drivers/media/common/saa7146_video.c
+index 47fee05..552dab4 100644
+--- a/drivers/media/common/saa7146_video.c
++++ b/drivers/media/common/saa7146_video.c
+@@ -1,4 +1,5 @@
+ #include <media/saa7146_vv.h>
++#include <media/v4l2-chip-ident.h>
+
+ static int max_memory = 32;
+
+@@ -97,172 +98,13 @@ struct saa7146_format* format_by_fourcc(struct saa7146_dev *dev, int fourcc)
+ return NULL;
+ }
+
+-static int g_fmt(struct saa7146_fh *fh, struct v4l2_format *f)
+-{
+- struct saa7146_dev *dev = fh->dev;
+- DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+-
+- switch (f->type) {
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+- f->fmt.pix = fh->video_fmt;
+- return 0;
+- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+- f->fmt.win = fh->ov.win;
+- return 0;
+- case V4L2_BUF_TYPE_VBI_CAPTURE:
+- {
+- f->fmt.vbi = fh->vbi_fmt;
+- return 0;
+- }
+- default:
+- DEB_D(("invalid format type '%d'.\n",f->type));
+- return -EINVAL;
+- }
+-}
+-
+-static int try_win(struct saa7146_dev *dev, struct v4l2_window *win)
+-{
+- struct saa7146_vv *vv = dev->vv_data;
+- enum v4l2_field field;
+- int maxw, maxh;
+-
+- DEB_EE(("dev:%p\n",dev));
+-
+- if (NULL == vv->ov_fb.base) {
+- DEB_D(("no fb base set.\n"));
+- return -EINVAL;
+- }
+- if (NULL == vv->ov_fmt) {
+- DEB_D(("no fb fmt set.\n"));
+- return -EINVAL;
+- }
+- if (win->w.width < 48 || win->w.height < 32) {
+- DEB_D(("min width/height. (%d,%d)\n",win->w.width,win->w.height));
+- return -EINVAL;
+- }
+- if (win->clipcount > 16) {
+- DEB_D(("clipcount too big.\n"));
+- return -EINVAL;
+- }
+-
+- field = win->field;
+- maxw = vv->standard->h_max_out;
+- maxh = vv->standard->v_max_out;
+-
+- if (V4L2_FIELD_ANY == field) {
+- field = (win->w.height > maxh/2)
+- ? V4L2_FIELD_INTERLACED
+- : V4L2_FIELD_TOP;
+- }
+- switch (field) {
+- case V4L2_FIELD_TOP:
+- case V4L2_FIELD_BOTTOM:
+- case V4L2_FIELD_ALTERNATE:
+- maxh = maxh / 2;
+- break;
+- case V4L2_FIELD_INTERLACED:
+- break;
+- default: {
+- DEB_D(("no known field mode '%d'.\n",field));
+- return -EINVAL;
+- }
+- }
+-
+- win->field = field;
+- if (win->w.width > maxw)
+- win->w.width = maxw;
+- if (win->w.height > maxh)
+- win->w.height = maxh;
+-
+- return 0;
+-}
+-
+-static int try_fmt(struct saa7146_fh *fh, struct v4l2_format *f)
+-{
+- struct saa7146_dev *dev = fh->dev;
+- struct saa7146_vv *vv = dev->vv_data;
+- int err;
+-
+- switch (f->type) {
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+- {
+- struct saa7146_format *fmt;
+- enum v4l2_field field;
+- int maxw, maxh;
+- int calc_bpl;
+-
+- DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n",dev,fh));
+-
+- fmt = format_by_fourcc(dev,f->fmt.pix.pixelformat);
+- if (NULL == fmt) {
+- return -EINVAL;
+- }
+-
+- field = f->fmt.pix.field;
+- maxw = vv->standard->h_max_out;
+- maxh = vv->standard->v_max_out;
+-
+- if (V4L2_FIELD_ANY == field) {
+- field = (f->fmt.pix.height > maxh/2)
+- ? V4L2_FIELD_INTERLACED
+- : V4L2_FIELD_BOTTOM;
+- }
+- switch (field) {
+- case V4L2_FIELD_ALTERNATE: {
+- vv->last_field = V4L2_FIELD_TOP;
+- maxh = maxh / 2;
+- break;
+- }
+- case V4L2_FIELD_TOP:
+- case V4L2_FIELD_BOTTOM:
+- vv->last_field = V4L2_FIELD_INTERLACED;
+- maxh = maxh / 2;
+- break;
+- case V4L2_FIELD_INTERLACED:
+- vv->last_field = V4L2_FIELD_INTERLACED;
+- break;
+- default: {
+- DEB_D(("no known field mode '%d'.\n",field));
+- return -EINVAL;
+- }
+- }
+-
+- f->fmt.pix.field = field;
+- if (f->fmt.pix.width > maxw)
+- f->fmt.pix.width = maxw;
+- if (f->fmt.pix.height > maxh)
+- f->fmt.pix.height = maxh;
+-
+- calc_bpl = (f->fmt.pix.width * fmt->depth)/8;
+-
+- if (f->fmt.pix.bytesperline < calc_bpl)
+- f->fmt.pix.bytesperline = calc_bpl;
+-
+- if (f->fmt.pix.bytesperline > (2*PAGE_SIZE * fmt->depth)/8) /* arbitrary constraint */
+- f->fmt.pix.bytesperline = calc_bpl;
+-
+- f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
+- DEB_D(("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n",f->fmt.pix.width,f->fmt.pix.height,f->fmt.pix.bytesperline,f->fmt.pix.sizeimage));
+-
+- return 0;
+- }
+- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+- DEB_EE(("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n",dev,fh));
+- err = try_win(dev,&f->fmt.win);
+- if (0 != err) {
+- return err;
+- }
+- return 0;
+- default:
+- DEB_EE(("unknown format type '%d'\n",f->type));
+- return -EINVAL;
+- }
+-}
++static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f);
+
+ int saa7146_start_preview(struct saa7146_fh *fh)
+ {
+ struct saa7146_dev *dev = fh->dev;
+ struct saa7146_vv *vv = dev->vv_data;
++ struct v4l2_format fmt;
+ int ret = 0, err = 0;
+
+ DEB_EE(("dev:%p, fh:%p\n",dev,fh));
+@@ -294,12 +136,13 @@ int saa7146_start_preview(struct saa7146_fh *fh)
+ return -EBUSY;
+ }
+
+- err = try_win(dev,&fh->ov.win);
++ fmt.fmt.win = fh->ov.win;
++ err = vidioc_try_fmt_vid_overlay(NULL, fh, &fmt);
+ if (0 != err) {
+ saa7146_res_free(vv->video_fh, RESOURCE_DMA1_HPS|RESOURCE_DMA2_CLP);
+ return -EBUSY;
+ }
+-
++ fh->ov.win = fmt.fmt.win;
+ vv->ov_data = &fh->ov;
+
+ DEB_D(("%dx%d+%d+%d %s field=%s\n",
+@@ -355,58 +198,6 @@ int saa7146_stop_preview(struct saa7146_fh *fh)
+ }
+ EXPORT_SYMBOL_GPL(saa7146_stop_preview);
+
+-static int s_fmt(struct saa7146_fh *fh, struct v4l2_format *f)
+-{
+- struct saa7146_dev *dev = fh->dev;
+- struct saa7146_vv *vv = dev->vv_data;
+-
+- int err;
+-
+- switch (f->type) {
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+- DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n",dev,fh));
+- if (IS_CAPTURE_ACTIVE(fh) != 0) {
+- DEB_EE(("streaming capture is active\n"));
+- return -EBUSY;
+- }
+- err = try_fmt(fh,f);
+- if (0 != err)
+- return err;
+- fh->video_fmt = f->fmt.pix;
+- DEB_EE(("set to pixelformat '%4.4s'\n",(char *)&fh->video_fmt.pixelformat));
+- return 0;
+- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+- DEB_EE(("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n",dev,fh));
+- err = try_win(dev,&f->fmt.win);
+- if (0 != err)
+- return err;
+- mutex_lock(&dev->lock);
+- fh->ov.win = f->fmt.win;
+- fh->ov.nclips = f->fmt.win.clipcount;
+- if (fh->ov.nclips > 16)
+- fh->ov.nclips = 16;
+- if (copy_from_user(fh->ov.clips,f->fmt.win.clips,sizeof(struct v4l2_clip)*fh->ov.nclips)) {
+- mutex_unlock(&dev->lock);
+- return -EFAULT;
+- }
+-
+- /* fh->ov.fh is used to indicate that we have valid overlay informations, too */
+- fh->ov.fh = fh;
+-
+- mutex_unlock(&dev->lock);
+-
+- /* check if our current overlay is active */
+- if (IS_OVERLAY_ACTIVE(fh) != 0) {
+- saa7146_stop_preview(fh);
+- saa7146_start_preview(fh);
+- }
+- return 0;
+- default:
+- DEB_D(("unknown format type '%d'\n",f->type));
+- return -EINVAL;
+- }
+-}
+-
+ /********************************************************************************/
+ /* device controls */
+
+@@ -419,6 +210,7 @@ static struct v4l2_queryctrl controls[] = {
+ .step = 1,
+ .default_value = 128,
+ .type = V4L2_CTRL_TYPE_INTEGER,
++ .flags = V4L2_CTRL_FLAG_SLIDER,
+ },{
+ .id = V4L2_CID_CONTRAST,
+ .name = "Contrast",
+@@ -427,6 +219,7 @@ static struct v4l2_queryctrl controls[] = {
+ .step = 1,
+ .default_value = 64,
+ .type = V4L2_CTRL_TYPE_INTEGER,
++ .flags = V4L2_CTRL_FLAG_SLIDER,
+ },{
+ .id = V4L2_CID_SATURATION,
+ .name = "Saturation",
+@@ -435,15 +228,16 @@ static struct v4l2_queryctrl controls[] = {
+ .step = 1,
+ .default_value = 64,
+ .type = V4L2_CTRL_TYPE_INTEGER,
++ .flags = V4L2_CTRL_FLAG_SLIDER,
+ },{
+ .id = V4L2_CID_VFLIP,
+- .name = "Vertical flip",
++ .name = "Vertical Flip",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_HFLIP,
+- .name = "Horizontal flip",
++ .name = "Horizontal Flip",
+ .minimum = 0,
+ .maximum = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+@@ -463,132 +257,6 @@ static struct v4l2_queryctrl* ctrl_by_id(int id)
+ return NULL;
+ }
+
+-static int get_control(struct saa7146_fh *fh, struct v4l2_control *c)
+-{
+- struct saa7146_dev *dev = fh->dev;
+- struct saa7146_vv *vv = dev->vv_data;
+-
+- const struct v4l2_queryctrl* ctrl;
+- u32 value = 0;
+-
+- ctrl = ctrl_by_id(c->id);
+- if (NULL == ctrl)
+- return -EINVAL;
+- switch (c->id) {
+- case V4L2_CID_BRIGHTNESS:
+- value = saa7146_read(dev, BCS_CTRL);
+- c->value = 0xff & (value >> 24);
+- DEB_D(("V4L2_CID_BRIGHTNESS: %d\n",c->value));
+- break;
+- case V4L2_CID_CONTRAST:
+- value = saa7146_read(dev, BCS_CTRL);
+- c->value = 0x7f & (value >> 16);
+- DEB_D(("V4L2_CID_CONTRAST: %d\n",c->value));
+- break;
+- case V4L2_CID_SATURATION:
+- value = saa7146_read(dev, BCS_CTRL);
+- c->value = 0x7f & (value >> 0);
+- DEB_D(("V4L2_CID_SATURATION: %d\n",c->value));
+- break;
+- case V4L2_CID_VFLIP:
+- c->value = vv->vflip;
+- DEB_D(("V4L2_CID_VFLIP: %d\n",c->value));
+- break;
+- case V4L2_CID_HFLIP:
+- c->value = vv->hflip;
+- DEB_D(("V4L2_CID_HFLIP: %d\n",c->value));
+- break;
+- default:
+- return -EINVAL;
+- }
+-
+- return 0;
+-}
+-
+-static int set_control(struct saa7146_fh *fh, struct v4l2_control *c)
+-{
+- struct saa7146_dev *dev = fh->dev;
+- struct saa7146_vv *vv = dev->vv_data;
+-
+- const struct v4l2_queryctrl* ctrl;
+-
+- ctrl = ctrl_by_id(c->id);
+- if (NULL == ctrl) {
+- DEB_D(("unknown control %d\n",c->id));
+- return -EINVAL;
+- }
+-
+- mutex_lock(&dev->lock);
+-
+- switch (ctrl->type) {
+- case V4L2_CTRL_TYPE_BOOLEAN:
+- case V4L2_CTRL_TYPE_MENU:
+- case V4L2_CTRL_TYPE_INTEGER:
+- if (c->value < ctrl->minimum)
+- c->value = ctrl->minimum;
+- if (c->value > ctrl->maximum)
+- c->value = ctrl->maximum;
+- break;
+- default:
+- /* nothing */;
+- };
+-
+- switch (c->id) {
+- case V4L2_CID_BRIGHTNESS: {
+- u32 value = saa7146_read(dev, BCS_CTRL);
+- value &= 0x00ffffff;
+- value |= (c->value << 24);
+- saa7146_write(dev, BCS_CTRL, value);
+- saa7146_write(dev, MC2, MASK_22 | MASK_06 );
+- break;
+- }
+- case V4L2_CID_CONTRAST: {
+- u32 value = saa7146_read(dev, BCS_CTRL);
+- value &= 0xff00ffff;
+- value |= (c->value << 16);
+- saa7146_write(dev, BCS_CTRL, value);
+- saa7146_write(dev, MC2, MASK_22 | MASK_06 );
+- break;
+- }
+- case V4L2_CID_SATURATION: {
+- u32 value = saa7146_read(dev, BCS_CTRL);
+- value &= 0xffffff00;
+- value |= (c->value << 0);
+- saa7146_write(dev, BCS_CTRL, value);
+- saa7146_write(dev, MC2, MASK_22 | MASK_06 );
+- break;
+- }
+- case V4L2_CID_HFLIP:
+- /* fixme: we can support changing VFLIP and HFLIP here... */
+- if (IS_CAPTURE_ACTIVE(fh) != 0) {
+- DEB_D(("V4L2_CID_HFLIP while active capture.\n"));
+- mutex_unlock(&dev->lock);
+- return -EINVAL;
+- }
+- vv->hflip = c->value;
+- break;
+- case V4L2_CID_VFLIP:
+- if (IS_CAPTURE_ACTIVE(fh) != 0) {
+- DEB_D(("V4L2_CID_VFLIP while active capture.\n"));
+- mutex_unlock(&dev->lock);
+- return -EINVAL;
+- }
+- vv->vflip = c->value;
+- break;
+- default: {
+- mutex_unlock(&dev->lock);
+- return -EINVAL;
+- }
+- }
+- mutex_unlock(&dev->lock);
+-
+- if (IS_OVERLAY_ACTIVE(fh) != 0) {
+- saa7146_stop_preview(fh);
+- saa7146_start_preview(fh);
+- }
+- return 0;
+-}
+-
+ /********************************************************************************/
+ /* common pagetable functions */
+
+@@ -829,231 +497,446 @@ static int video_end(struct saa7146_fh *fh, struct file *file)
+ return 0;
+ }
+
+-/*
+- * This function is _not_ called directly, but from
+- * video_generic_ioctl (and maybe others). userspace
+- * copying is done already, arg is a kernel pointer.
+- */
++static int vidioc_querycap(struct file *file, void *fh, struct v4l2_capability *cap)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++
++ strcpy((char *)cap->driver, "saa7146 v4l2");
++ strlcpy((char *)cap->card, dev->ext->name, sizeof(cap->card));
++ sprintf((char *)cap->bus_info, "PCI:%s", pci_name(dev->pci));
++ cap->version = SAA7146_VERSION_CODE;
++ cap->capabilities =
++ V4L2_CAP_VIDEO_CAPTURE |
++ V4L2_CAP_VIDEO_OVERLAY |
++ V4L2_CAP_READWRITE |
++ V4L2_CAP_STREAMING;
++ cap->capabilities |= dev->ext_vv_data->capabilities;
++ return 0;
++}
+
+-long saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg)
++static int vidioc_g_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
+ {
+- struct saa7146_fh *fh = file->private_data;
+- struct saa7146_dev *dev = fh->dev;
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct saa7146_vv *vv = dev->vv_data;
++
++ *fb = vv->ov_fb;
++ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
++ return 0;
++}
++
++static int vidioc_s_fbuf(struct file *file, void *fh, struct v4l2_framebuffer *fb)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct saa7146_vv *vv = dev->vv_data;
++ struct saa7146_format *fmt;
+
+- long err = 0;
+- int result = 0, ee = 0;
++ DEB_EE(("VIDIOC_S_FBUF\n"));
+
+- struct saa7146_use_ops *ops;
+- struct videobuf_queue *q;
++ if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
++ return -EPERM;
+
+- /* check if extension handles the command */
+- for(ee = 0; dev->ext_vv_data->ioctls[ee].flags != 0; ee++) {
+- if( cmd == dev->ext_vv_data->ioctls[ee].cmd )
+- break;
++ /* check args */
++ fmt = format_by_fourcc(dev, fb->fmt.pixelformat);
++ if (NULL == fmt)
++ return -EINVAL;
++
++ /* planar formats are not allowed for overlay video, clipping and video dma would clash */
++ if (fmt->flags & FORMAT_IS_PLANAR)
++ DEB_S(("planar pixelformat '%4.4s' not allowed for overlay\n",
++ (char *)&fmt->pixelformat));
++
++ /* check if overlay is running */
++ if (IS_OVERLAY_ACTIVE(fh) != 0) {
++ if (vv->video_fh != fh) {
++ DEB_D(("refusing to change framebuffer informations while overlay is active in another open.\n"));
++ return -EBUSY;
++ }
+ }
+
+- if( 0 != (dev->ext_vv_data->ioctls[ee].flags & SAA7146_EXCLUSIVE) ) {
+- DEB_D(("extension handles ioctl exclusive.\n"));
+- result = dev->ext_vv_data->ioctl(fh, cmd, arg);
+- return result;
++ mutex_lock(&dev->lock);
++
++ /* ok, accept it */
++ vv->ov_fb = *fb;
++ vv->ov_fmt = fmt;
++ if (0 == vv->ov_fb.fmt.bytesperline)
++ vv->ov_fb.fmt.bytesperline =
++ vv->ov_fb.fmt.width * fmt->depth / 8;
++
++ mutex_unlock(&dev->lock);
++ return 0;
++}
++
++static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh, struct v4l2_fmtdesc *f)
++{
++ if (f->index >= NUM_FORMATS)
++ return -EINVAL;
++ strlcpy((char *)f->description, formats[f->index].name,
++ sizeof(f->description));
++ f->pixelformat = formats[f->index].pixelformat;
++ return 0;
++}
++
++static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *c)
++{
++ const struct v4l2_queryctrl *ctrl;
++
++ 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;
++
++ ctrl = ctrl_by_id(c->id);
++ if (ctrl == NULL)
++ return -EINVAL;
++
++ DEB_EE(("VIDIOC_QUERYCTRL: id:%d\n", c->id));
++ *c = *ctrl;
++ return 0;
++}
++
++static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *c)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct saa7146_vv *vv = dev->vv_data;
++ const struct v4l2_queryctrl *ctrl;
++ u32 value = 0;
++
++ ctrl = ctrl_by_id(c->id);
++ if (NULL == ctrl)
++ return -EINVAL;
++ switch (c->id) {
++ case V4L2_CID_BRIGHTNESS:
++ value = saa7146_read(dev, BCS_CTRL);
++ c->value = 0xff & (value >> 24);
++ DEB_D(("V4L2_CID_BRIGHTNESS: %d\n", c->value));
++ break;
++ case V4L2_CID_CONTRAST:
++ value = saa7146_read(dev, BCS_CTRL);
++ c->value = 0x7f & (value >> 16);
++ DEB_D(("V4L2_CID_CONTRAST: %d\n", c->value));
++ break;
++ case V4L2_CID_SATURATION:
++ value = saa7146_read(dev, BCS_CTRL);
++ c->value = 0x7f & (value >> 0);
++ DEB_D(("V4L2_CID_SATURATION: %d\n", c->value));
++ break;
++ case V4L2_CID_VFLIP:
++ c->value = vv->vflip;
++ DEB_D(("V4L2_CID_VFLIP: %d\n", c->value));
++ break;
++ case V4L2_CID_HFLIP:
++ c->value = vv->hflip;
++ DEB_D(("V4L2_CID_HFLIP: %d\n", c->value));
++ break;
++ default:
++ return -EINVAL;
+ }
+- if( 0 != (dev->ext_vv_data->ioctls[ee].flags & SAA7146_BEFORE) ) {
+- DEB_D(("extension handles ioctl before.\n"));
+- result = dev->ext_vv_data->ioctl(fh, cmd, arg);
+- if( -EAGAIN != result ) {
+- return result;
+- }
++ return 0;
++}
++
++static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *c)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct saa7146_vv *vv = dev->vv_data;
++ const struct v4l2_queryctrl *ctrl;
++
++ ctrl = ctrl_by_id(c->id);
++ if (NULL == ctrl) {
++ DEB_D(("unknown control %d\n", c->id));
++ return -EINVAL;
+ }
+
+- /* fixme: add handle "after" case (is it still needed?) */
++ mutex_lock(&dev->lock);
+
+- switch (fh->type) {
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+- ops = &saa7146_video_uops;
+- q = &fh->video_q;
++ switch (ctrl->type) {
++ case V4L2_CTRL_TYPE_BOOLEAN:
++ case V4L2_CTRL_TYPE_MENU:
++ case V4L2_CTRL_TYPE_INTEGER:
++ if (c->value < ctrl->minimum)
++ c->value = ctrl->minimum;
++ if (c->value > ctrl->maximum)
++ c->value = ctrl->maximum;
+ break;
++ default:
++ /* nothing */;
++ }
++
++ switch (c->id) {
++ case V4L2_CID_BRIGHTNESS: {
++ u32 value = saa7146_read(dev, BCS_CTRL);
++ value &= 0x00ffffff;
++ value |= (c->value << 24);
++ saa7146_write(dev, BCS_CTRL, value);
++ saa7146_write(dev, MC2, MASK_22 | MASK_06);
++ break;
++ }
++ case V4L2_CID_CONTRAST: {
++ u32 value = saa7146_read(dev, BCS_CTRL);
++ value &= 0xff00ffff;
++ value |= (c->value << 16);
++ saa7146_write(dev, BCS_CTRL, value);
++ saa7146_write(dev, MC2, MASK_22 | MASK_06);
++ break;
++ }
++ case V4L2_CID_SATURATION: {
++ u32 value = saa7146_read(dev, BCS_CTRL);
++ value &= 0xffffff00;
++ value |= (c->value << 0);
++ saa7146_write(dev, BCS_CTRL, value);
++ saa7146_write(dev, MC2, MASK_22 | MASK_06);
++ break;
++ }
++ case V4L2_CID_HFLIP:
++ /* fixme: we can support changing VFLIP and HFLIP here... */
++ if (IS_CAPTURE_ACTIVE(fh) != 0) {
++ DEB_D(("V4L2_CID_HFLIP while active capture.\n"));
++ mutex_unlock(&dev->lock);
++ return -EBUSY;
+ }
+- case V4L2_BUF_TYPE_VBI_CAPTURE: {
+- ops = &saa7146_vbi_uops;
+- q = &fh->vbi_q;
++ vv->hflip = c->value;
+ break;
++ case V4L2_CID_VFLIP:
++ if (IS_CAPTURE_ACTIVE(fh) != 0) {
++ DEB_D(("V4L2_CID_VFLIP while active capture.\n"));
++ mutex_unlock(&dev->lock);
++ return -EBUSY;
+ }
++ vv->vflip = c->value;
++ break;
+ default:
+- BUG();
+- return 0;
++ mutex_unlock(&dev->lock);
++ return -EINVAL;
+ }
++ mutex_unlock(&dev->lock);
+
+- switch (cmd) {
+- case VIDIOC_QUERYCAP:
+- {
+- struct v4l2_capability *cap = arg;
+- memset(cap,0,sizeof(*cap));
+-
+- DEB_EE(("VIDIOC_QUERYCAP\n"));
+-
+- strcpy((char *)cap->driver, "saa7146 v4l2");
+- strlcpy((char *)cap->card, dev->ext->name, sizeof(cap->card));
+- sprintf((char *)cap->bus_info,"PCI:%s", pci_name(dev->pci));
+- cap->version = SAA7146_VERSION_CODE;
+- cap->capabilities =
+- V4L2_CAP_VIDEO_CAPTURE |
+- V4L2_CAP_VIDEO_OVERLAY |
+- V4L2_CAP_READWRITE |
+- V4L2_CAP_STREAMING;
+- cap->capabilities |= dev->ext_vv_data->capabilities;
+- return 0;
++ if (IS_OVERLAY_ACTIVE(fh) != 0) {
++ saa7146_stop_preview(fh);
++ saa7146_start_preview(fh);
+ }
+- case VIDIOC_G_FBUF:
+- {
+- struct v4l2_framebuffer *fb = arg;
+-
+- DEB_EE(("VIDIOC_G_FBUF\n"));
++ return 0;
++}
+
+- *fb = vv->ov_fb;
+- fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+- return 0;
+- }
+- case VIDIOC_S_FBUF:
+- {
+- struct v4l2_framebuffer *fb = arg;
+- struct saa7146_format *fmt;
++static int vidioc_g_parm(struct file *file, void *fh,
++ struct v4l2_streamparm *parm)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct saa7146_vv *vv = dev->vv_data;
+
+- DEB_EE(("VIDIOC_S_FBUF\n"));
++ parm->parm.capture.readbuffers = 1;
++ v4l2_video_std_frame_period(vv->standard->id,
++ &parm->parm.capture.timeperframe);
++ return 0;
++}
+
+- if(!capable(CAP_SYS_ADMIN) &&
+- !capable(CAP_SYS_RAWIO))
+- return -EPERM;
++static int vidioc_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
++{
++ f->fmt.pix = ((struct saa7146_fh *)fh)->video_fmt;
++ return 0;
++}
+
+- /* check args */
+- fmt = format_by_fourcc(dev,fb->fmt.pixelformat);
+- if (NULL == fmt) {
+- return -EINVAL;
+- }
++static int vidioc_g_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f)
++{
++ f->fmt.win = ((struct saa7146_fh *)fh)->ov.win;
++ return 0;
++}
+
+- /* planar formats are not allowed for overlay video, clipping and video dma would clash */
+- if (0 != (fmt->flags & FORMAT_IS_PLANAR)) {
+- DEB_S(("planar pixelformat '%4.4s' not allowed for overlay\n",(char *)&fmt->pixelformat));
+- }
++static int vidioc_g_fmt_vbi_cap(struct file *file, void *fh, struct v4l2_format *f)
++{
++ f->fmt.vbi = ((struct saa7146_fh *)fh)->vbi_fmt;
++ return 0;
++}
+
+- /* check if overlay is running */
+- if (IS_OVERLAY_ACTIVE(fh) != 0) {
+- if (vv->video_fh != fh) {
+- DEB_D(("refusing to change framebuffer informations while overlay is active in another open.\n"));
+- return -EBUSY;
+- }
+- }
++static int vidioc_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct saa7146_vv *vv = dev->vv_data;
++ struct saa7146_format *fmt;
++ enum v4l2_field field;
++ int maxw, maxh;
++ int calc_bpl;
+
+- mutex_lock(&dev->lock);
++ DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh));
+
+- /* ok, accept it */
+- vv->ov_fb = *fb;
+- vv->ov_fmt = fmt;
+- if (0 == vv->ov_fb.fmt.bytesperline)
+- vv->ov_fb.fmt.bytesperline =
+- vv->ov_fb.fmt.width*fmt->depth/8;
++ fmt = format_by_fourcc(dev, f->fmt.pix.pixelformat);
++ if (NULL == fmt)
++ return -EINVAL;
+
+- mutex_unlock(&dev->lock);
++ field = f->fmt.pix.field;
++ maxw = vv->standard->h_max_out;
++ maxh = vv->standard->v_max_out;
+
+- return 0;
++ if (V4L2_FIELD_ANY == field) {
++ field = (f->fmt.pix.height > maxh / 2)
++ ? V4L2_FIELD_INTERLACED
++ : V4L2_FIELD_BOTTOM;
+ }
+- case VIDIOC_ENUM_FMT:
+- {
+- struct v4l2_fmtdesc *f = arg;
+-
+- switch (f->type) {
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+- if (f->index >= NUM_FORMATS)
+- return -EINVAL;
+- strlcpy((char *)f->description, formats[f->index].name,
+- sizeof(f->description));
+- f->pixelformat = formats[f->index].pixelformat;
+- f->flags = 0;
+- memset(f->reserved, 0, sizeof(f->reserved));
+- break;
+- default:
+- return -EINVAL;
+- }
+-
+- DEB_EE(("VIDIOC_ENUM_FMT: type:%d, index:%d\n",f->type,f->index));
+- return 0;
++ switch (field) {
++ case V4L2_FIELD_ALTERNATE:
++ vv->last_field = V4L2_FIELD_TOP;
++ maxh = maxh / 2;
++ break;
++ case V4L2_FIELD_TOP:
++ case V4L2_FIELD_BOTTOM:
++ vv->last_field = V4L2_FIELD_INTERLACED;
++ maxh = maxh / 2;
++ break;
++ case V4L2_FIELD_INTERLACED:
++ vv->last_field = V4L2_FIELD_INTERLACED;
++ break;
++ default:
++ DEB_D(("no known field mode '%d'.\n", field));
++ return -EINVAL;
+ }
+- case VIDIOC_QUERYCTRL:
+- {
+- const struct v4l2_queryctrl *ctrl;
+- struct v4l2_queryctrl *c = 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;
++ f->fmt.pix.field = field;
++ if (f->fmt.pix.width > maxw)
++ f->fmt.pix.width = maxw;
++ if (f->fmt.pix.height > maxh)
++ f->fmt.pix.height = maxh;
+
+- ctrl = ctrl_by_id(c->id);
+- if( NULL == ctrl ) {
+- return -EINVAL;
+-/*
+- c->flags = V4L2_CTRL_FLAG_DISABLED;
+- return 0;
+-*/
+- }
++ calc_bpl = (f->fmt.pix.width * fmt->depth) / 8;
+
+- DEB_EE(("VIDIOC_QUERYCTRL: id:%d\n",c->id));
+- *c = *ctrl;
+- return 0;
++ if (f->fmt.pix.bytesperline < calc_bpl)
++ f->fmt.pix.bytesperline = calc_bpl;
++
++ if (f->fmt.pix.bytesperline > (2 * PAGE_SIZE * fmt->depth) / 8) /* arbitrary constraint */
++ f->fmt.pix.bytesperline = calc_bpl;
++
++ f->fmt.pix.sizeimage = f->fmt.pix.bytesperline * f->fmt.pix.height;
++ DEB_D(("w:%d, h:%d, bytesperline:%d, sizeimage:%d\n", f->fmt.pix.width,
++ f->fmt.pix.height, f->fmt.pix.bytesperline, f->fmt.pix.sizeimage));
++
++ return 0;
++}
++
++
++static int vidioc_try_fmt_vid_overlay(struct file *file, void *fh, struct v4l2_format *f)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct saa7146_vv *vv = dev->vv_data;
++ struct v4l2_window *win = &f->fmt.win;
++ enum v4l2_field field;
++ int maxw, maxh;
++
++ DEB_EE(("dev:%p\n", dev));
++
++ if (NULL == vv->ov_fb.base) {
++ DEB_D(("no fb base set.\n"));
++ return -EINVAL;
+ }
+- case VIDIOC_G_CTRL: {
+- DEB_EE(("VIDIOC_G_CTRL\n"));
+- return get_control(fh,arg);
++ if (NULL == vv->ov_fmt) {
++ DEB_D(("no fb fmt set.\n"));
++ return -EINVAL;
+ }
+- case VIDIOC_S_CTRL:
+- {
+- DEB_EE(("VIDIOC_S_CTRL\n"));
+- err = set_control(fh,arg);
+- return err;
++ if (win->w.width < 48 || win->w.height < 32) {
++ DEB_D(("min width/height. (%d,%d)\n", win->w.width, win->w.height));
++ return -EINVAL;
+ }
+- case VIDIOC_G_PARM:
+- {
+- struct v4l2_streamparm *parm = arg;
+- if( parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ) {
+- return -EINVAL;
+- }
+- memset(&parm->parm.capture,0,sizeof(struct v4l2_captureparm));
+- parm->parm.capture.readbuffers = 1;
+- // fixme: only for PAL!
+- parm->parm.capture.timeperframe.numerator = 1;
+- parm->parm.capture.timeperframe.denominator = 25;
+- return 0;
++ if (win->clipcount > 16) {
++ DEB_D(("clipcount too big.\n"));
++ return -EINVAL;
+ }
+- case VIDIOC_G_FMT:
+- {
+- struct v4l2_format *f = arg;
+- DEB_EE(("VIDIOC_G_FMT\n"));
+- return g_fmt(fh,f);
++
++ field = win->field;
++ maxw = vv->standard->h_max_out;
++ maxh = vv->standard->v_max_out;
++
++ if (V4L2_FIELD_ANY == field) {
++ field = (win->w.height > maxh / 2)
++ ? V4L2_FIELD_INTERLACED
++ : V4L2_FIELD_TOP;
++ }
++ switch (field) {
++ case V4L2_FIELD_TOP:
++ case V4L2_FIELD_BOTTOM:
++ case V4L2_FIELD_ALTERNATE:
++ maxh = maxh / 2;
++ break;
++ case V4L2_FIELD_INTERLACED:
++ break;
++ default:
++ DEB_D(("no known field mode '%d'.\n", field));
++ return -EINVAL;
+ }
+- case VIDIOC_S_FMT:
+- {
+- struct v4l2_format *f = arg;
+- DEB_EE(("VIDIOC_S_FMT\n"));
+- return s_fmt(fh,f);
++
++ win->field = field;
++ if (win->w.width > maxw)
++ win->w.width = maxw;
++ if (win->w.height > maxh)
++ win->w.height = maxh;
++
++ return 0;
++}
++
++static int vidioc_s_fmt_vid_cap(struct file *file, void *__fh, struct v4l2_format *f)
++{
++ struct saa7146_fh *fh = __fh;
++ struct saa7146_dev *dev = fh->dev;
++ struct saa7146_vv *vv = dev->vv_data;
++ int err;
++
++ DEB_EE(("V4L2_BUF_TYPE_VIDEO_CAPTURE: dev:%p, fh:%p\n", dev, fh));
++ if (IS_CAPTURE_ACTIVE(fh) != 0) {
++ DEB_EE(("streaming capture is active\n"));
++ return -EBUSY;
+ }
+- case VIDIOC_TRY_FMT:
+- {
+- struct v4l2_format *f = arg;
+- DEB_EE(("VIDIOC_TRY_FMT\n"));
+- return try_fmt(fh,f);
++ err = vidioc_try_fmt_vid_cap(file, fh, f);
++ if (0 != err)
++ return err;
++ fh->video_fmt = f->fmt.pix;
++ DEB_EE(("set to pixelformat '%4.4s'\n", (char *)&fh->video_fmt.pixelformat));
++ return 0;
++}
++
++static int vidioc_s_fmt_vid_overlay(struct file *file, void *__fh, struct v4l2_format *f)
++{
++ struct saa7146_fh *fh = __fh;
++ struct saa7146_dev *dev = fh->dev;
++ struct saa7146_vv *vv = dev->vv_data;
++ int err;
++
++ DEB_EE(("V4L2_BUF_TYPE_VIDEO_OVERLAY: dev:%p, fh:%p\n", dev, fh));
++ err = vidioc_try_fmt_vid_overlay(file, fh, f);
++ if (0 != err)
++ return err;
++ mutex_lock(&dev->lock);
++ fh->ov.win = f->fmt.win;
++ fh->ov.nclips = f->fmt.win.clipcount;
++ if (fh->ov.nclips > 16)
++ fh->ov.nclips = 16;
++ if (copy_from_user(fh->ov.clips, f->fmt.win.clips,
++ sizeof(struct v4l2_clip) * fh->ov.nclips)) {
++ mutex_unlock(&dev->lock);
++ return -EFAULT;
+ }
+- case VIDIOC_G_STD:
+- {
+- v4l2_std_id *id = arg;
+- DEB_EE(("VIDIOC_G_STD\n"));
+- *id = vv->standard->id;
+- return 0;
++
++ /* fh->ov.fh is used to indicate that we have valid overlay informations, too */
++ fh->ov.fh = fh;
++
++ mutex_unlock(&dev->lock);
++
++ /* check if our current overlay is active */
++ if (IS_OVERLAY_ACTIVE(fh) != 0) {
++ saa7146_stop_preview(fh);
++ saa7146_start_preview(fh);
+ }
++ return 0;
++}
++
++static int vidioc_g_std(struct file *file, void *fh, v4l2_std_id *norm)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct saa7146_vv *vv = dev->vv_data;
++
++ *norm = vv->standard->id;
++ return 0;
++}
++
+ /* the saa7146 supfhrts (used in conjunction with the saa7111a for example)
+ PAL / NTSC / SECAM. if your hardware does not (or does more)
+ -- override this function in your extension */
++/*
+ case VIDIOC_ENUMSTD:
+ {
+ struct v4l2_standard *e = arg;
+@@ -1066,162 +949,245 @@ long saa7146_video_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+ }
+ return -EINVAL;
+ }
+- case VIDIOC_S_STD:
+- {
+- v4l2_std_id *id = arg;
+- int found = 0;
+- int i;
+-
+- DEB_EE(("VIDIOC_S_STD\n"));
++ */
+
+- if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) {
+- DEB_D(("cannot change video standard while streaming capture is active\n"));
+- return -EBUSY;
+- }
++static int vidioc_s_std(struct file *file, void *fh, v4l2_std_id *id)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct saa7146_vv *vv = dev->vv_data;
++ int found = 0;
++ int err, i;
+
+- if ((vv->video_status & STATUS_OVERLAY) != 0) {
+- vv->ov_suspend = vv->video_fh;
+- err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
+- if (0 != err) {
+- DEB_D(("suspending video failed. aborting\n"));
+- return err;
+- }
+- }
++ DEB_EE(("VIDIOC_S_STD\n"));
+
+- mutex_lock(&dev->lock);
++ if ((vv->video_status & STATUS_CAPTURE) == STATUS_CAPTURE) {
++ DEB_D(("cannot change video standard while streaming capture is active\n"));
++ return -EBUSY;
++ }
+
+- for(i = 0; i < dev->ext_vv_data->num_stds; i++)
+- if (*id & dev->ext_vv_data->stds[i].id)
+- break;
+- if (i != dev->ext_vv_data->num_stds) {
+- vv->standard = &dev->ext_vv_data->stds[i];
+- if( NULL != dev->ext_vv_data->std_callback )
+- dev->ext_vv_data->std_callback(dev, vv->standard);
+- found = 1;
++ if ((vv->video_status & STATUS_OVERLAY) != 0) {
++ vv->ov_suspend = vv->video_fh;
++ err = saa7146_stop_preview(vv->video_fh); /* side effect: video_status is now 0, video_fh is NULL */
++ if (0 != err) {
++ DEB_D(("suspending video failed. aborting\n"));
++ return err;
+ }
++ }
+
+- mutex_unlock(&dev->lock);
++ mutex_lock(&dev->lock);
+
+- if (vv->ov_suspend != NULL) {
+- saa7146_start_preview(vv->ov_suspend);
+- vv->ov_suspend = NULL;
+- }
++ for (i = 0; i < dev->ext_vv_data->num_stds; i++)
++ if (*id & dev->ext_vv_data->stds[i].id)
++ break;
++ if (i != dev->ext_vv_data->num_stds) {
++ vv->standard = &dev->ext_vv_data->stds[i];
++ if (NULL != dev->ext_vv_data->std_callback)
++ dev->ext_vv_data->std_callback(dev, vv->standard);
++ found = 1;
++ }
+
+- if( 0 == found ) {
+- DEB_EE(("VIDIOC_S_STD: standard not found.\n"));
+- return -EINVAL;
+- }
++ mutex_unlock(&dev->lock);
+
+- DEB_EE(("VIDIOC_S_STD: set to standard to '%s'\n",vv->standard->name));
+- return 0;
++ if (vv->ov_suspend != NULL) {
++ saa7146_start_preview(vv->ov_suspend);
++ vv->ov_suspend = NULL;
+ }
+- case VIDIOC_OVERLAY:
+- {
+- int on = *(int *)arg;
+
+- DEB_D(("VIDIOC_OVERLAY on:%d\n",on));
+- if (on != 0) {
+- err = saa7146_start_preview(fh);
+- } else {
+- err = saa7146_stop_preview(fh);
+- }
+- return err;
+- }
+- case VIDIOC_REQBUFS: {
+- struct v4l2_requestbuffers *req = arg;
+- DEB_D(("VIDIOC_REQBUFS, type:%d\n",req->type));
+- return videobuf_reqbufs(q,req);
+- }
+- case VIDIOC_QUERYBUF: {
+- struct v4l2_buffer *buf = arg;
+- DEB_D(("VIDIOC_QUERYBUF, type:%d, offset:%d\n",buf->type,buf->m.offset));
+- return videobuf_querybuf(q,buf);
+- }
+- case VIDIOC_QBUF: {
+- struct v4l2_buffer *buf = arg;
+- int ret = 0;
+- ret = videobuf_qbuf(q,buf);
+- DEB_D(("VIDIOC_QBUF: ret:%d, index:%d\n",ret,buf->index));
+- return ret;
+- }
+- case VIDIOC_DQBUF: {
+- struct v4l2_buffer *buf = arg;
+- int ret = 0;
+- ret = videobuf_dqbuf(q,buf,file->f_flags & O_NONBLOCK);
+- DEB_D(("VIDIOC_DQBUF: ret:%d, index:%d\n",ret,buf->index));
+- return ret;
++ if (!found) {
++ DEB_EE(("VIDIOC_S_STD: standard not found.\n"));
++ return -EINVAL;
+ }
+- case VIDIOC_STREAMON: {
+- int *type = arg;
+- DEB_D(("VIDIOC_STREAMON, type:%d\n",*type));
+
+- err = video_begin(fh);
+- if( 0 != err) {
+- return err;
+- }
+- err = videobuf_streamon(q);
+- return err;
+- }
+- case VIDIOC_STREAMOFF: {
+- int *type = arg;
++ DEB_EE(("VIDIOC_S_STD: set to standard to '%s'\n", vv->standard->name));
++ return 0;
++}
+
+- DEB_D(("VIDIOC_STREAMOFF, type:%d\n",*type));
++static int vidioc_overlay(struct file *file, void *fh, unsigned int on)
++{
++ int err;
+
+- /* ugly: we need to copy some checks from video_end(),
+- because videobuf_streamoff() relies on the capture running.
+- check and fix this */
+- if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
+- DEB_S(("not capturing.\n"));
+- return 0;
+- }
++ DEB_D(("VIDIOC_OVERLAY on:%d\n", on));
++ if (on)
++ err = saa7146_start_preview(fh);
++ else
++ err = saa7146_stop_preview(fh);
++ return err;
++}
+
+- if (vv->video_fh != fh) {
+- DEB_S(("capturing, but in another open.\n"));
+- return -EBUSY;
+- }
++static int vidioc_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *b)
++{
++ struct saa7146_fh *fh = __fh;
+
+- err = videobuf_streamoff(q);
+- if (0 != err) {
+- DEB_D(("warning: videobuf_streamoff() failed.\n"));
+- video_end(fh, file);
+- } else {
+- err = video_end(fh, file);
+- }
++ if (b->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
++ return videobuf_reqbufs(&fh->video_q, b);
++ if (b->type == V4L2_BUF_TYPE_VBI_CAPTURE)
++ return videobuf_reqbufs(&fh->vbi_q, b);
++ return -EINVAL;
++}
++
++static int vidioc_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
++{
++ struct saa7146_fh *fh = __fh;
++
++ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
++ return videobuf_querybuf(&fh->video_q, buf);
++ if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
++ return videobuf_querybuf(&fh->vbi_q, buf);
++ return -EINVAL;
++}
++
++static int vidioc_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
++{
++ struct saa7146_fh *fh = __fh;
++
++ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
++ return videobuf_qbuf(&fh->video_q, buf);
++ if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
++ return videobuf_qbuf(&fh->vbi_q, buf);
++ return -EINVAL;
++}
++
++static int vidioc_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
++{
++ struct saa7146_fh *fh = __fh;
++
++ if (buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
++ return videobuf_dqbuf(&fh->video_q, buf, file->f_flags & O_NONBLOCK);
++ if (buf->type == V4L2_BUF_TYPE_VBI_CAPTURE)
++ return videobuf_dqbuf(&fh->vbi_q, buf, file->f_flags & O_NONBLOCK);
++ return -EINVAL;
++}
++
++static int vidioc_streamon(struct file *file, void *__fh, enum v4l2_buf_type type)
++{
++ struct saa7146_fh *fh = __fh;
++ int err;
++
++ DEB_D(("VIDIOC_STREAMON, type:%d\n", type));
++
++ err = video_begin(fh);
++ if (err)
+ return err;
++ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
++ return videobuf_streamon(&fh->video_q);
++ if (type == V4L2_BUF_TYPE_VBI_CAPTURE)
++ return videobuf_streamon(&fh->vbi_q);
++ return -EINVAL;
++}
++
++static int vidioc_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type)
++{
++ struct saa7146_fh *fh = __fh;
++ struct saa7146_dev *dev = fh->dev;
++ struct saa7146_vv *vv = dev->vv_data;
++ int err;
++
++ DEB_D(("VIDIOC_STREAMOFF, type:%d\n", type));
++
++ /* ugly: we need to copy some checks from video_end(),
++ because videobuf_streamoff() relies on the capture running.
++ check and fix this */
++ if ((vv->video_status & STATUS_CAPTURE) != STATUS_CAPTURE) {
++ DEB_S(("not capturing.\n"));
++ return 0;
+ }
+-#ifdef CONFIG_VIDEO_V4L1_COMPAT
+- case VIDIOCGMBUF:
+- {
+- struct video_mbuf *mbuf = arg;
+- int i;
+
+- /* fixme: number of capture buffers and sizes for v4l apps */
+- int gbuffers = 2;
+- int gbufsize = 768*576*4;
++ if (vv->video_fh != fh) {
++ DEB_S(("capturing, but in another open.\n"));
++ return -EBUSY;
++ }
+
+- DEB_D(("VIDIOCGMBUF \n"));
++ err = -EINVAL;
++ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
++ err = videobuf_streamoff(&fh->video_q);
++ else if (type == V4L2_BUF_TYPE_VBI_CAPTURE)
++ err = videobuf_streamoff(&fh->vbi_q);
++ if (0 != err) {
++ DEB_D(("warning: videobuf_streamoff() failed.\n"));
++ video_end(fh, file);
++ } else {
++ err = video_end(fh, file);
++ }
++ return err;
++}
+
+- q = &fh->video_q;
+- err = videobuf_mmap_setup(q,gbuffers,gbufsize,
+- V4L2_MEMORY_MMAP);
+- if (err < 0)
+- return err;
++static int vidioc_g_chip_ident(struct file *file, void *__fh,
++ struct v4l2_dbg_chip_ident *chip)
++{
++ struct saa7146_fh *fh = __fh;
++ struct saa7146_dev *dev = fh->dev;
+
+- gbuffers = err;
+- memset(mbuf,0,sizeof(*mbuf));
+- mbuf->frames = gbuffers;
+- mbuf->size = gbuffers * gbufsize;
+- for (i = 0; i < gbuffers; i++)
+- mbuf->offsets[i] = i * gbufsize;
++ chip->ident = V4L2_IDENT_NONE;
++ chip->revision = 0;
++ if (chip->match.type == V4L2_CHIP_MATCH_HOST && !chip->match.addr) {
++ chip->ident = V4L2_IDENT_SAA7146;
+ return 0;
+ }
+-#endif
+- default:
+- return v4l_compat_translate_ioctl(file, cmd, arg,
+- saa7146_video_do_ioctl);
+- }
++ return v4l2_device_call_until_err(&dev->v4l2_dev, 0,
++ core, g_chip_ident, chip);
++}
++
++#ifdef CONFIG_VIDEO_V4L1_COMPAT
++static int vidiocgmbuf(struct file *file, void *__fh, struct video_mbuf *mbuf)
++{
++ struct saa7146_fh *fh = __fh;
++ struct videobuf_queue *q = &fh->video_q;
++ int err, i;
++
++ /* fixme: number of capture buffers and sizes for v4l apps */
++ int gbuffers = 2;
++ int gbufsize = 768 * 576 * 4;
++
++ DEB_D(("VIDIOCGMBUF \n"));
++
++ q = &fh->video_q;
++ err = videobuf_mmap_setup(q, gbuffers, gbufsize,
++ V4L2_MEMORY_MMAP);
++ if (err < 0)
++ return err;
++
++ gbuffers = err;
++ 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;
+ }
++#endif
++
++const struct v4l2_ioctl_ops saa7146_video_ioctl_ops = {
++ .vidioc_querycap = vidioc_querycap,
++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
++ .vidioc_enum_fmt_vid_overlay = vidioc_enum_fmt_vid_cap,
++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
++ .vidioc_g_fmt_vid_overlay = vidioc_g_fmt_vid_overlay,
++ .vidioc_try_fmt_vid_overlay = vidioc_try_fmt_vid_overlay,
++ .vidioc_s_fmt_vid_overlay = vidioc_s_fmt_vid_overlay,
++ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
++ .vidioc_g_chip_ident = vidioc_g_chip_ident,
++
++ .vidioc_overlay = vidioc_overlay,
++ .vidioc_g_fbuf = vidioc_g_fbuf,
++ .vidioc_s_fbuf = vidioc_s_fbuf,
++ .vidioc_reqbufs = vidioc_reqbufs,
++ .vidioc_querybuf = vidioc_querybuf,
++ .vidioc_qbuf = vidioc_qbuf,
++ .vidioc_dqbuf = vidioc_dqbuf,
++ .vidioc_g_std = vidioc_g_std,
++ .vidioc_s_std = vidioc_s_std,
++ .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_parm = vidioc_g_parm,
++#ifdef CONFIG_VIDEO_V4L1_COMPAT
++ .vidiocgmbuf = vidiocgmbuf,
++#endif
++};
+
+ /*********************************************************************************/
+ /* buffer handling functions */
+diff --git a/drivers/media/common/tuners/Kconfig b/drivers/media/common/tuners/Kconfig
+index 6f92bea..52c3f65 100644
+--- a/drivers/media/common/tuners/Kconfig
++++ b/drivers/media/common/tuners/Kconfig
+@@ -21,16 +21,17 @@ config MEDIA_TUNER
+ tristate
+ default VIDEO_MEDIA && I2C
+ depends on VIDEO_MEDIA && I2C
+- select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_MT20XX if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_TEA5761 if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_TEA5767 if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_TDA9887 if !MEDIA_TUNER_CUSTOMIZE
+-
+-menuconfig MEDIA_TUNER_CUSTOMIZE
++ select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_MT20XX if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_TEA5761 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_TEA5767 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_TDA9887 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_MC44S803 if !MEDIA_TUNER_CUSTOMISE
++
++menuconfig MEDIA_TUNER_CUSTOMISE
+ bool "Customize analog and hybrid tuner modules to build"
+ depends on MEDIA_TUNER
+ default n
+@@ -43,13 +44,13 @@ menuconfig MEDIA_TUNER_CUSTOMIZE
+
+ If unsure say N.
+
+-if MEDIA_TUNER_CUSTOMIZE
++if MEDIA_TUNER_CUSTOMISE
+
+ config MEDIA_TUNER_SIMPLE
+ tristate "Simple tuner support"
+ depends on VIDEO_MEDIA && I2C
+ select MEDIA_TUNER_TDA9887
+- default m if MEDIA_TUNER_CUSTOMIZE
++ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to include support for various simple tuners.
+
+@@ -58,28 +59,28 @@ config MEDIA_TUNER_TDA8290
+ depends on VIDEO_MEDIA && I2C
+ select MEDIA_TUNER_TDA827X
+ select MEDIA_TUNER_TDA18271
+- default m if MEDIA_TUNER_CUSTOMIZE
++ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to include support for Philips TDA8290+8275(a) tuner.
+
+ config MEDIA_TUNER_TDA827X
+ tristate "Philips TDA827X silicon tuner"
+ depends on VIDEO_MEDIA && I2C
+- default m if DVB_FE_CUSTOMISE
++ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ A DVB-T silicon tuner module. Say Y when you want to support this tuner.
+
+ config MEDIA_TUNER_TDA18271
+ tristate "NXP TDA18271 silicon tuner"
+ depends on VIDEO_MEDIA && I2C
+- default m if DVB_FE_CUSTOMISE
++ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ A silicon tuner module. Say Y when you want to support this tuner.
+
+ config MEDIA_TUNER_TDA9887
+ tristate "TDA 9885/6/7 analog IF demodulator"
+ depends on VIDEO_MEDIA && I2C
+- default m if MEDIA_TUNER_CUSTOMIZE
++ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to include support for Philips TDA9885/6/7
+ analog IF demodulator.
+@@ -88,63 +89,63 @@ config MEDIA_TUNER_TEA5761
+ tristate "TEA 5761 radio tuner (EXPERIMENTAL)"
+ depends on VIDEO_MEDIA && I2C
+ depends on EXPERIMENTAL
+- default m if MEDIA_TUNER_CUSTOMIZE
++ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to include support for the Philips TEA5761 radio tuner.
+
+ config MEDIA_TUNER_TEA5767
+ tristate "TEA 5767 radio tuner"
+ depends on VIDEO_MEDIA && I2C
+- default m if MEDIA_TUNER_CUSTOMIZE
++ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to include support for the Philips TEA5767 radio tuner.
+
+ config MEDIA_TUNER_MT20XX
+ tristate "Microtune 2032 / 2050 tuners"
+ depends on VIDEO_MEDIA && I2C
+- default m if MEDIA_TUNER_CUSTOMIZE
++ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to include support for the MT2032 / MT2050 tuner.
+
+ config MEDIA_TUNER_MT2060
+ tristate "Microtune MT2060 silicon IF tuner"
+ depends on VIDEO_MEDIA && I2C
+- default m if DVB_FE_CUSTOMISE
++ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ A driver for the silicon IF tuner MT2060 from Microtune.
+
+ config MEDIA_TUNER_MT2266
+ tristate "Microtune MT2266 silicon tuner"
+ depends on VIDEO_MEDIA && I2C
+- default m if DVB_FE_CUSTOMISE
++ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ A driver for the silicon baseband tuner MT2266 from Microtune.
+
+ config MEDIA_TUNER_MT2131
+ tristate "Microtune MT2131 silicon tuner"
+ depends on VIDEO_MEDIA && I2C
+- default m if DVB_FE_CUSTOMISE
++ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ A driver for the silicon baseband tuner MT2131 from Microtune.
+
+ config MEDIA_TUNER_QT1010
+ tristate "Quantek QT1010 silicon tuner"
+ depends on VIDEO_MEDIA && I2C
+- default m if DVB_FE_CUSTOMISE
++ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ A driver for the silicon tuner QT1010 from Quantek.
+
+ config MEDIA_TUNER_XC2028
+ tristate "XCeive xc2028/xc3028 tuners"
+ depends on VIDEO_MEDIA && I2C
+- default m if MEDIA_TUNER_CUSTOMIZE
++ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to include support for the xc2028/xc3028 tuners.
+
+ config MEDIA_TUNER_XC5000
+ tristate "Xceive XC5000 silicon tuner"
+ depends on VIDEO_MEDIA && I2C
+- default m if DVB_FE_CUSTOMISE
++ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ A driver for the silicon tuner XC5000 from Xceive.
+ This device is only used inside a SiP called togther with a
+@@ -153,15 +154,22 @@ config MEDIA_TUNER_XC5000
+ config MEDIA_TUNER_MXL5005S
+ tristate "MaxLinear MSL5005S silicon tuner"
+ depends on VIDEO_MEDIA && I2C
+- default m if DVB_FE_CUSTOMISE
++ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ A driver for the silicon tuner MXL5005S from MaxLinear.
+
+ config MEDIA_TUNER_MXL5007T
+ tristate "MaxLinear MxL5007T silicon tuner"
+ depends on VIDEO_MEDIA && I2C
+- default m if DVB_FE_CUSTOMISE
++ default m if MEDIA_TUNER_CUSTOMISE
+ help
+ A driver for the silicon tuner MxL5007T from MaxLinear.
+
+-endif # MEDIA_TUNER_CUSTOMIZE
++config MEDIA_TUNER_MC44S803
++ tristate "Freescale MC44S803 Low Power CMOS Broadband tuners"
++ depends on VIDEO_MEDIA && I2C
++ default m if MEDIA_TUNER_CUSTOMISE
++ help
++ Say Y here to support the Freescale MC44S803 based tuners
++
++endif # MEDIA_TUNER_CUSTOMISE
+diff --git a/drivers/media/common/tuners/Makefile b/drivers/media/common/tuners/Makefile
+index 4dfbe5b..4132b2b 100644
+--- a/drivers/media/common/tuners/Makefile
++++ b/drivers/media/common/tuners/Makefile
+@@ -22,6 +22,7 @@ obj-$(CONFIG_MEDIA_TUNER_QT1010) += qt1010.o
+ obj-$(CONFIG_MEDIA_TUNER_MT2131) += mt2131.o
+ obj-$(CONFIG_MEDIA_TUNER_MXL5005S) += mxl5005s.o
+ obj-$(CONFIG_MEDIA_TUNER_MXL5007T) += mxl5007t.o
++obj-$(CONFIG_MEDIA_TUNER_MC44S803) += mc44s803.o
+
+ EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
+ EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
+diff --git a/drivers/media/common/tuners/mc44s803.c b/drivers/media/common/tuners/mc44s803.c
+new file mode 100644
+index 0000000..20c4485
+--- /dev/null
++++ b/drivers/media/common/tuners/mc44s803.c
+@@ -0,0 +1,371 @@
++/*
++ * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner
++ *
++ * Copyright (c) 2009 Jochen Friedrich <jochen@scram.de>
++ *
++ * 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 <linux/module.h>
++#include <linux/delay.h>
++#include <linux/dvb/frontend.h>
++#include <linux/i2c.h>
++
++#include "dvb_frontend.h"
++
++#include "mc44s803.h"
++#include "mc44s803_priv.h"
++
++#define mc_printk(level, format, arg...) \
++ printk(level "mc44s803: " format , ## arg)
++
++/* Writes a single register */
++static int mc44s803_writereg(struct mc44s803_priv *priv, u32 val)
++{
++ u8 buf[3];
++ struct i2c_msg msg = {
++ .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 3
++ };
++
++ buf[0] = (val & 0xff0000) >> 16;
++ buf[1] = (val & 0xff00) >> 8;
++ buf[2] = (val & 0xff);
++
++ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
++ mc_printk(KERN_WARNING, "I2C write failed\n");
++ return -EREMOTEIO;
++ }
++ return 0;
++}
++
++/* Reads a single register */
++static int mc44s803_readreg(struct mc44s803_priv *priv, u8 reg, u32 *val)
++{
++ u32 wval;
++ u8 buf[3];
++ int ret;
++ struct i2c_msg msg[] = {
++ { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD,
++ .buf = buf, .len = 3 },
++ };
++
++ wval = MC44S803_REG_SM(MC44S803_REG_DATAREG, MC44S803_ADDR) |
++ MC44S803_REG_SM(reg, MC44S803_D);
++
++ ret = mc44s803_writereg(priv, wval);
++ if (ret)
++ return ret;
++
++ if (i2c_transfer(priv->i2c, msg, 1) != 1) {
++ mc_printk(KERN_WARNING, "I2C read failed\n");
++ return -EREMOTEIO;
++ }
++
++ *val = (buf[0] << 16) | (buf[1] << 8) | buf[2];
++
++ return 0;
++}
++
++static int mc44s803_release(struct dvb_frontend *fe)
++{
++ struct mc44s803_priv *priv = fe->tuner_priv;
++
++ fe->tuner_priv = NULL;
++ kfree(priv);
++
++ return 0;
++}
++
++static int mc44s803_init(struct dvb_frontend *fe)
++{
++ struct mc44s803_priv *priv = fe->tuner_priv;
++ u32 val;
++ int err;
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 1);
++
++/* Reset chip */
++ val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR) |
++ MC44S803_REG_SM(1, MC44S803_RS);
++
++ err = mc44s803_writereg(priv, val);
++ if (err)
++ goto exit;
++
++ val = MC44S803_REG_SM(MC44S803_REG_RESET, MC44S803_ADDR);
++
++ err = mc44s803_writereg(priv, val);
++ if (err)
++ goto exit;
++
++/* Power Up and Start Osc */
++
++ val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) |
++ MC44S803_REG_SM(0xC0, MC44S803_REFOSC) |
++ MC44S803_REG_SM(1, MC44S803_OSCSEL);
++
++ err = mc44s803_writereg(priv, val);
++ if (err)
++ goto exit;
++
++ val = MC44S803_REG_SM(MC44S803_REG_POWER, MC44S803_ADDR) |
++ MC44S803_REG_SM(0x200, MC44S803_POWER);
++
++ err = mc44s803_writereg(priv, val);
++ if (err)
++ goto exit;
++
++ msleep(10);
++
++ val = MC44S803_REG_SM(MC44S803_REG_REFOSC, MC44S803_ADDR) |
++ MC44S803_REG_SM(0x40, MC44S803_REFOSC) |
++ MC44S803_REG_SM(1, MC44S803_OSCSEL);
++
++ err = mc44s803_writereg(priv, val);
++ if (err)
++ goto exit;
++
++ msleep(20);
++
++/* Setup Mixer */
++
++ val = MC44S803_REG_SM(MC44S803_REG_MIXER, MC44S803_ADDR) |
++ MC44S803_REG_SM(1, MC44S803_TRI_STATE) |
++ MC44S803_REG_SM(0x7F, MC44S803_MIXER_RES);
++
++ err = mc44s803_writereg(priv, val);
++ if (err)
++ goto exit;
++
++/* Setup Cirquit Adjust */
++
++ val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) |
++ MC44S803_REG_SM(1, MC44S803_G1) |
++ MC44S803_REG_SM(1, MC44S803_G3) |
++ MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) |
++ MC44S803_REG_SM(1, MC44S803_G6) |
++ MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) |
++ MC44S803_REG_SM(0x3, MC44S803_LP) |
++ MC44S803_REG_SM(1, MC44S803_CLRF) |
++ MC44S803_REG_SM(1, MC44S803_CLIF);
++
++ err = mc44s803_writereg(priv, val);
++ if (err)
++ goto exit;
++
++ val = MC44S803_REG_SM(MC44S803_REG_CIRCADJ, MC44S803_ADDR) |
++ MC44S803_REG_SM(1, MC44S803_G1) |
++ MC44S803_REG_SM(1, MC44S803_G3) |
++ MC44S803_REG_SM(0x3, MC44S803_CIRCADJ_RES) |
++ MC44S803_REG_SM(1, MC44S803_G6) |
++ MC44S803_REG_SM(priv->cfg->dig_out, MC44S803_S1) |
++ MC44S803_REG_SM(0x3, MC44S803_LP);
++
++ err = mc44s803_writereg(priv, val);
++ if (err)
++ goto exit;
++
++/* Setup Digtune */
++
++ val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) |
++ MC44S803_REG_SM(3, MC44S803_XOD);
++
++ err = mc44s803_writereg(priv, val);
++ if (err)
++ goto exit;
++
++/* Setup AGC */
++
++ val = MC44S803_REG_SM(MC44S803_REG_LNAAGC, MC44S803_ADDR) |
++ MC44S803_REG_SM(1, MC44S803_AT1) |
++ MC44S803_REG_SM(1, MC44S803_AT2) |
++ MC44S803_REG_SM(1, MC44S803_AGC_AN_DIG) |
++ MC44S803_REG_SM(1, MC44S803_AGC_READ_EN) |
++ MC44S803_REG_SM(1, MC44S803_LNA0);
++
++ err = mc44s803_writereg(priv, val);
++ if (err)
++ goto exit;
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0);
++ return 0;
++
++exit:
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0);
++
++ mc_printk(KERN_WARNING, "I/O Error\n");
++ return err;
++}
++
++static int mc44s803_set_params(struct dvb_frontend *fe,
++ struct dvb_frontend_parameters *params)
++{
++ struct mc44s803_priv *priv = fe->tuner_priv;
++ u32 r1, r2, n1, n2, lo1, lo2, freq, val;
++ int err;
++
++ priv->frequency = params->frequency;
++
++ r1 = MC44S803_OSC / 1000000;
++ r2 = MC44S803_OSC / 100000;
++
++ n1 = (params->frequency + MC44S803_IF1 + 500000) / 1000000;
++ freq = MC44S803_OSC / r1 * n1;
++ lo1 = ((60 * n1) + (r1 / 2)) / r1;
++ freq = freq - params->frequency;
++
++ n2 = (freq - MC44S803_IF2 + 50000) / 100000;
++ lo2 = ((60 * n2) + (r2 / 2)) / r2;
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 1);
++
++ val = MC44S803_REG_SM(MC44S803_REG_REFDIV, MC44S803_ADDR) |
++ MC44S803_REG_SM(r1-1, MC44S803_R1) |
++ MC44S803_REG_SM(r2-1, MC44S803_R2) |
++ MC44S803_REG_SM(1, MC44S803_REFBUF_EN);
++
++ err = mc44s803_writereg(priv, val);
++ if (err)
++ goto exit;
++
++ val = MC44S803_REG_SM(MC44S803_REG_LO1, MC44S803_ADDR) |
++ MC44S803_REG_SM(n1-2, MC44S803_LO1);
++
++ err = mc44s803_writereg(priv, val);
++ if (err)
++ goto exit;
++
++ val = MC44S803_REG_SM(MC44S803_REG_LO2, MC44S803_ADDR) |
++ MC44S803_REG_SM(n2-2, MC44S803_LO2);
++
++ err = mc44s803_writereg(priv, val);
++ if (err)
++ goto exit;
++
++ val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) |
++ MC44S803_REG_SM(1, MC44S803_DA) |
++ MC44S803_REG_SM(lo1, MC44S803_LO_REF) |
++ MC44S803_REG_SM(1, MC44S803_AT);
++
++ err = mc44s803_writereg(priv, val);
++ if (err)
++ goto exit;
++
++ val = MC44S803_REG_SM(MC44S803_REG_DIGTUNE, MC44S803_ADDR) |
++ MC44S803_REG_SM(2, MC44S803_DA) |
++ MC44S803_REG_SM(lo2, MC44S803_LO_REF) |
++ MC44S803_REG_SM(1, MC44S803_AT);
++
++ err = mc44s803_writereg(priv, val);
++ if (err)
++ goto exit;
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0);
++
++ return 0;
++
++exit:
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0);
++
++ mc_printk(KERN_WARNING, "I/O Error\n");
++ return err;
++}
++
++static int mc44s803_get_frequency(struct dvb_frontend *fe, u32 *frequency)
++{
++ struct mc44s803_priv *priv = fe->tuner_priv;
++ *frequency = priv->frequency;
++ return 0;
++}
++
++static const struct dvb_tuner_ops mc44s803_tuner_ops = {
++ .info = {
++ .name = "Freescale MC44S803",
++ .frequency_min = 48000000,
++ .frequency_max = 1000000000,
++ .frequency_step = 100000,
++ },
++
++ .release = mc44s803_release,
++ .init = mc44s803_init,
++ .set_params = mc44s803_set_params,
++ .get_frequency = mc44s803_get_frequency
++};
++
++/* This functions tries to identify a MC44S803 tuner by reading the ID
++ register. This is hasty. */
++struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe,
++ struct i2c_adapter *i2c, struct mc44s803_config *cfg)
++{
++ struct mc44s803_priv *priv;
++ u32 reg;
++ u8 id;
++ int ret;
++
++ reg = 0;
++
++ priv = kzalloc(sizeof(struct mc44s803_priv), GFP_KERNEL);
++ if (priv == NULL)
++ return NULL;
++
++ priv->cfg = cfg;
++ priv->i2c = i2c;
++ priv->fe = fe;
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
++
++ ret = mc44s803_readreg(priv, MC44S803_REG_ID, &reg);
++ if (ret)
++ goto error;
++
++ id = MC44S803_REG_MS(reg, MC44S803_ID);
++
++ if (id != 0x14) {
++ mc_printk(KERN_ERR, "unsupported ID "
++ "(%x should be 0x14)\n", id);
++ goto error;
++ }
++
++ mc_printk(KERN_INFO, "successfully identified (ID = %x)\n", id);
++ memcpy(&fe->ops.tuner_ops, &mc44s803_tuner_ops,
++ sizeof(struct dvb_tuner_ops));
++
++ fe->tuner_priv = priv;
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
++
++ return fe;
++
++error:
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
++
++ kfree(priv);
++ return NULL;
++}
++EXPORT_SYMBOL(mc44s803_attach);
++
++MODULE_AUTHOR("Jochen Friedrich");
++MODULE_DESCRIPTION("Freescale MC44S803 silicon tuner driver");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/media/common/tuners/mc44s803.h b/drivers/media/common/tuners/mc44s803.h
+new file mode 100644
+index 0000000..34f3892
+--- /dev/null
++++ b/drivers/media/common/tuners/mc44s803.h
+@@ -0,0 +1,46 @@
++/*
++ * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner
++ *
++ * Copyright (c) 2009 Jochen Friedrich <jochen@scram.de>
++ *
++ * 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 MC44S803_H
++#define MC44S803_H
++
++struct dvb_frontend;
++struct i2c_adapter;
++
++struct mc44s803_config {
++ u8 i2c_address;
++ u8 dig_out;
++};
++
++#if defined(CONFIG_MEDIA_TUNER_MC44S803) || \
++ (defined(CONFIG_MEDIA_TUNER_MC44S803_MODULE) && defined(MODULE))
++extern struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe,
++ struct i2c_adapter *i2c, struct mc44s803_config *cfg);
++#else
++static inline struct dvb_frontend *mc44s803_attach(struct dvb_frontend *fe,
++ struct i2c_adapter *i2c, struct mc44s803_config *cfg)
++{
++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
++ return NULL;
++}
++#endif /* CONFIG_MEDIA_TUNER_MC44S803 */
++
++#endif
+diff --git a/drivers/media/common/tuners/mc44s803_priv.h b/drivers/media/common/tuners/mc44s803_priv.h
+new file mode 100644
+index 0000000..14a9278
+--- /dev/null
++++ b/drivers/media/common/tuners/mc44s803_priv.h
+@@ -0,0 +1,208 @@
++/*
++ * Driver for Freescale MC44S803 Low Power CMOS Broadband Tuner
++ *
++ * Copyright (c) 2009 Jochen Friedrich <jochen@scram.de>
++ *
++ * 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 MC44S803_PRIV_H
++#define MC44S803_PRIV_H
++
++/* This driver is based on the information available in the datasheet
++ http://www.freescale.com/files/rf_if/doc/data_sheet/MC44S803.pdf
++
++ SPI or I2C Address : 0xc0-0xc6
++
++ Reg.No | Function
++ -------------------------------------------
++ 00 | Power Down
++ 01 | Reference Oszillator
++ 02 | Reference Dividers
++ 03 | Mixer and Reference Buffer
++ 04 | Reset/Serial Out
++ 05 | LO 1
++ 06 | LO 2
++ 07 | Circuit Adjust
++ 08 | Test
++ 09 | Digital Tune
++ 0A | LNA AGC
++ 0B | Data Register Address
++ 0C | Regulator Test
++ 0D | VCO Test
++ 0E | LNA Gain/Input Power
++ 0F | ID Bits
++
++*/
++
++#define MC44S803_OSC 26000000 /* 26 MHz */
++#define MC44S803_IF1 1086000000 /* 1086 MHz */
++#define MC44S803_IF2 36125000 /* 36.125 MHz */
++
++#define MC44S803_REG_POWER 0
++#define MC44S803_REG_REFOSC 1
++#define MC44S803_REG_REFDIV 2
++#define MC44S803_REG_MIXER 3
++#define MC44S803_REG_RESET 4
++#define MC44S803_REG_LO1 5
++#define MC44S803_REG_LO2 6
++#define MC44S803_REG_CIRCADJ 7
++#define MC44S803_REG_TEST 8
++#define MC44S803_REG_DIGTUNE 9
++#define MC44S803_REG_LNAAGC 0x0A
++#define MC44S803_REG_DATAREG 0x0B
++#define MC44S803_REG_REGTEST 0x0C
++#define MC44S803_REG_VCOTEST 0x0D
++#define MC44S803_REG_LNAGAIN 0x0E
++#define MC44S803_REG_ID 0x0F
++
++/* Register definitions */
++#define MC44S803_ADDR 0x0F
++#define MC44S803_ADDR_S 0
++/* REG_POWER */
++#define MC44S803_POWER 0xFFFFF0
++#define MC44S803_POWER_S 4
++/* REG_REFOSC */
++#define MC44S803_REFOSC 0x1FF0
++#define MC44S803_REFOSC_S 4
++#define MC44S803_OSCSEL 0x2000
++#define MC44S803_OSCSEL_S 13
++/* REG_REFDIV */
++#define MC44S803_R2 0x1FF0
++#define MC44S803_R2_S 4
++#define MC44S803_REFBUF_EN 0x2000
++#define MC44S803_REFBUF_EN_S 13
++#define MC44S803_R1 0x7C000
++#define MC44S803_R1_S 14
++/* REG_MIXER */
++#define MC44S803_R3 0x70
++#define MC44S803_R3_S 4
++#define MC44S803_MUX3 0x80
++#define MC44S803_MUX3_S 7
++#define MC44S803_MUX4 0x100
++#define MC44S803_MUX4_S 8
++#define MC44S803_OSC_SCR 0x200
++#define MC44S803_OSC_SCR_S 9
++#define MC44S803_TRI_STATE 0x400
++#define MC44S803_TRI_STATE_S 10
++#define MC44S803_BUF_GAIN 0x800
++#define MC44S803_BUF_GAIN_S 11
++#define MC44S803_BUF_IO 0x1000
++#define MC44S803_BUF_IO_S 12
++#define MC44S803_MIXER_RES 0xFE000
++#define MC44S803_MIXER_RES_S 13
++/* REG_RESET */
++#define MC44S803_RS 0x10
++#define MC44S803_RS_S 4
++#define MC44S803_SO 0x20
++#define MC44S803_SO_S 5
++/* REG_LO1 */
++#define MC44S803_LO1 0xFFF0
++#define MC44S803_LO1_S 4
++/* REG_LO2 */
++#define MC44S803_LO2 0x7FFF0
++#define MC44S803_LO2_S 4
++/* REG_CIRCADJ */
++#define MC44S803_G1 0x20
++#define MC44S803_G1_S 5
++#define MC44S803_G3 0x80
++#define MC44S803_G3_S 7
++#define MC44S803_CIRCADJ_RES 0x300
++#define MC44S803_CIRCADJ_RES_S 8
++#define MC44S803_G6 0x400
++#define MC44S803_G6_S 10
++#define MC44S803_G7 0x800
++#define MC44S803_G7_S 11
++#define MC44S803_S1 0x1000
++#define MC44S803_S1_S 12
++#define MC44S803_LP 0x7E000
++#define MC44S803_LP_S 13
++#define MC44S803_CLRF 0x80000
++#define MC44S803_CLRF_S 19
++#define MC44S803_CLIF 0x100000
++#define MC44S803_CLIF_S 20
++/* REG_TEST */
++/* REG_DIGTUNE */
++#define MC44S803_DA 0xF0
++#define MC44S803_DA_S 4
++#define MC44S803_XOD 0x300
++#define MC44S803_XOD_S 8
++#define MC44S803_RST 0x10000
++#define MC44S803_RST_S 16
++#define MC44S803_LO_REF 0x1FFF00
++#define MC44S803_LO_REF_S 8
++#define MC44S803_AT 0x200000
++#define MC44S803_AT_S 21
++#define MC44S803_MT 0x400000
++#define MC44S803_MT_S 22
++/* REG_LNAAGC */
++#define MC44S803_G 0x3F0
++#define MC44S803_G_S 4
++#define MC44S803_AT1 0x400
++#define MC44S803_AT1_S 10
++#define MC44S803_AT2 0x800
++#define MC44S803_AT2_S 11
++#define MC44S803_HL_GR_EN 0x8000
++#define MC44S803_HL_GR_EN_S 15
++#define MC44S803_AGC_AN_DIG 0x10000
++#define MC44S803_AGC_AN_DIG_S 16
++#define MC44S803_ATTEN_EN 0x20000
++#define MC44S803_ATTEN_EN_S 17
++#define MC44S803_AGC_READ_EN 0x40000
++#define MC44S803_AGC_READ_EN_S 18
++#define MC44S803_LNA0 0x80000
++#define MC44S803_LNA0_S 19
++#define MC44S803_AGC_SEL 0x100000
++#define MC44S803_AGC_SEL_S 20
++#define MC44S803_AT0 0x200000
++#define MC44S803_AT0_S 21
++#define MC44S803_B 0xC00000
++#define MC44S803_B_S 22
++/* REG_DATAREG */
++#define MC44S803_D 0xF0
++#define MC44S803_D_S 4
++/* REG_REGTEST */
++/* REG_VCOTEST */
++/* REG_LNAGAIN */
++#define MC44S803_IF_PWR 0x700
++#define MC44S803_IF_PWR_S 8
++#define MC44S803_RF_PWR 0x3800
++#define MC44S803_RF_PWR_S 11
++#define MC44S803_LNA_GAIN 0xFC000
++#define MC44S803_LNA_GAIN_S 14
++/* REG_ID */
++#define MC44S803_ID 0x3E00
++#define MC44S803_ID_S 9
++
++/* Some macros to read/write fields */
++
++/* First shift, then mask */
++#define MC44S803_REG_SM(_val, _reg) \
++ (((_val) << _reg##_S) & (_reg))
++
++/* First mask, then shift */
++#define MC44S803_REG_MS(_val, _reg) \
++ (((_val) & (_reg)) >> _reg##_S)
++
++struct mc44s803_priv {
++ struct mc44s803_config *cfg;
++ struct i2c_adapter *i2c;
++ struct dvb_frontend *fe;
++
++ u32 frequency;
++};
++
++#endif
+diff --git a/drivers/media/common/tuners/mt2060.c b/drivers/media/common/tuners/mt2060.c
+index 12206d7..c7abe3d 100644
+--- a/drivers/media/common/tuners/mt2060.c
++++ b/drivers/media/common/tuners/mt2060.c
+@@ -278,7 +278,7 @@ static void mt2060_calibrate(struct mt2060_priv *priv)
+ while (i++ < 10 && mt2060_readreg(priv, REG_MISC_STAT, &b) == 0 && (b & (1 << 6)) == 0)
+ msleep(20);
+
+- if (i < 10) {
++ if (i <= 10) {
+ mt2060_readreg(priv, REG_FM_FREQ, &priv->fmfreq); // now find out, what is fmreq used for :)
+ dprintk("calibration was successful: %d", (int)priv->fmfreq);
+ } else
+diff --git a/drivers/media/common/tuners/mt20xx.c b/drivers/media/common/tuners/mt20xx.c
+index 35b763a..44608ad 100644
+--- a/drivers/media/common/tuners/mt20xx.c
++++ b/drivers/media/common/tuners/mt20xx.c
+@@ -6,7 +6,7 @@
+ */
+ #include <linux/delay.h>
+ #include <linux/i2c.h>
+-#include <linux/videodev.h>
++#include <linux/videodev2.h>
+ #include "tuner-i2c.h"
+ #include "mt20xx.h"
+
+diff --git a/drivers/media/common/tuners/mxl5005s.c b/drivers/media/common/tuners/mxl5005s.c
+index 31522d2..0803dab 100644
+--- a/drivers/media/common/tuners/mxl5005s.c
++++ b/drivers/media/common/tuners/mxl5005s.c
+@@ -4003,12 +4003,11 @@ static int mxl5005s_set_params(struct dvb_frontend *fe,
+ /* Change tuner for new modulation type if reqd */
+ if (req_mode != state->current_mode) {
+ switch (req_mode) {
+- case VSB_8:
+- case QAM_64:
+- case QAM_256:
+- case QAM_AUTO:
++ case MXL_ATSC:
++ case MXL_QAM:
+ req_bw = MXL5005S_BANDWIDTH_6MHZ;
+ break;
++ case MXL_DVBT:
+ default:
+ /* Assume DVB-T */
+ switch (params->u.ofdm.bandwidth) {
+diff --git a/drivers/media/common/tuners/mxl5007t.c b/drivers/media/common/tuners/mxl5007t.c
+index 3ec2894..2d02698 100644
+--- a/drivers/media/common/tuners/mxl5007t.c
++++ b/drivers/media/common/tuners/mxl5007t.c
+@@ -1,7 +1,7 @@
+ /*
+ * mxl5007t.c - driver for the MaxLinear MxL5007T silicon tuner
+ *
+- * Copyright (C) 2008 Michael Krufky <mkrufky@linuxtv.org>
++ * Copyright (C) 2008, 2009 Michael Krufky <mkrufky@linuxtv.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -66,22 +66,17 @@ MODULE_PARM_DESC(debug, "set debug level");
+ #define MHz 1000000
+
+ enum mxl5007t_mode {
+- MxL_MODE_OTA_DVBT_ATSC = 0,
+- MxL_MODE_OTA_NTSC_PAL_GH = 1,
+- MxL_MODE_OTA_PAL_IB = 2,
+- MxL_MODE_OTA_PAL_D_SECAM_KL = 3,
+- MxL_MODE_OTA_ISDBT = 4,
+- MxL_MODE_CABLE_DIGITAL = 0x10,
+- MxL_MODE_CABLE_NTSC_PAL_GH = 0x11,
+- MxL_MODE_CABLE_PAL_IB = 0x12,
+- MxL_MODE_CABLE_PAL_D_SECAM_KL = 0x13,
+- MxL_MODE_CABLE_SCTE40 = 0x14,
++ MxL_MODE_ISDBT = 0,
++ MxL_MODE_DVBT = 1,
++ MxL_MODE_ATSC = 2,
++ MxL_MODE_CABLE = 0x10,
+ };
+
+ enum mxl5007t_chip_version {
+ MxL_UNKNOWN_ID = 0x00,
+ MxL_5007_V1_F1 = 0x11,
+ MxL_5007_V1_F2 = 0x12,
++ MxL_5007_V4 = 0x14,
+ MxL_5007_V2_100_F1 = 0x21,
+ MxL_5007_V2_100_F2 = 0x22,
+ MxL_5007_V2_200_F1 = 0x23,
+@@ -96,67 +91,61 @@ struct reg_pair_t {
+ /* ------------------------------------------------------------------------- */
+
+ static struct reg_pair_t init_tab[] = {
+- { 0x0b, 0x44 }, /* XTAL */
+- { 0x0c, 0x60 }, /* IF */
+- { 0x10, 0x00 }, /* MISC */
+- { 0x12, 0xca }, /* IDAC */
+- { 0x16, 0x90 }, /* MODE */
+- { 0x32, 0x38 }, /* MODE Analog/Digital */
+- { 0xd8, 0x18 }, /* CLK_OUT_ENABLE */
+- { 0x2c, 0x34 }, /* OVERRIDE */
+- { 0x4d, 0x40 }, /* OVERRIDE */
+- { 0x7f, 0x02 }, /* OVERRIDE */
+- { 0x9a, 0x52 }, /* OVERRIDE */
+- { 0x48, 0x5a }, /* OVERRIDE */
+- { 0x76, 0x1a }, /* OVERRIDE */
+- { 0x6a, 0x48 }, /* OVERRIDE */
+- { 0x64, 0x28 }, /* OVERRIDE */
+- { 0x66, 0xe6 }, /* OVERRIDE */
+- { 0x35, 0x0e }, /* OVERRIDE */
+- { 0x7e, 0x01 }, /* OVERRIDE */
+- { 0x83, 0x00 }, /* OVERRIDE */
+- { 0x04, 0x0b }, /* OVERRIDE */
+- { 0x05, 0x01 }, /* TOP_MASTER_ENABLE */
++ { 0x02, 0x06 },
++ { 0x03, 0x48 },
++ { 0x05, 0x04 },
++ { 0x06, 0x10 },
++ { 0x2e, 0x15 }, /* OVERRIDE */
++ { 0x30, 0x10 }, /* OVERRIDE */
++ { 0x45, 0x58 }, /* OVERRIDE */
++ { 0x48, 0x19 }, /* OVERRIDE */
++ { 0x52, 0x03 }, /* OVERRIDE */
++ { 0x53, 0x44 }, /* OVERRIDE */
++ { 0x6a, 0x4b }, /* OVERRIDE */
++ { 0x76, 0x00 }, /* OVERRIDE */
++ { 0x78, 0x18 }, /* OVERRIDE */
++ { 0x7a, 0x17 }, /* OVERRIDE */
++ { 0x85, 0x06 }, /* OVERRIDE */
++ { 0x01, 0x01 }, /* TOP_MASTER_ENABLE */
+ { 0, 0 }
+ };
+
+ static struct reg_pair_t init_tab_cable[] = {
+- { 0x0b, 0x44 }, /* XTAL */
+- { 0x0c, 0x60 }, /* IF */
+- { 0x10, 0x00 }, /* MISC */
+- { 0x12, 0xca }, /* IDAC */
+- { 0x16, 0x90 }, /* MODE */
+- { 0x32, 0x38 }, /* MODE A/D */
+- { 0x71, 0x3f }, /* TOP1 */
+- { 0x72, 0x3f }, /* TOP2 */
+- { 0x74, 0x3f }, /* TOP3 */
+- { 0xd8, 0x18 }, /* CLK_OUT_ENABLE */
+- { 0x2c, 0x34 }, /* OVERRIDE */
+- { 0x4d, 0x40 }, /* OVERRIDE */
+- { 0x7f, 0x02 }, /* OVERRIDE */
+- { 0x9a, 0x52 }, /* OVERRIDE */
+- { 0x48, 0x5a }, /* OVERRIDE */
+- { 0x76, 0x1a }, /* OVERRIDE */
+- { 0x6a, 0x48 }, /* OVERRIDE */
+- { 0x64, 0x28 }, /* OVERRIDE */
+- { 0x66, 0xe6 }, /* OVERRIDE */
+- { 0x35, 0x0e }, /* OVERRIDE */
+- { 0x7e, 0x01 }, /* OVERRIDE */
+- { 0x04, 0x0b }, /* OVERRIDE */
+- { 0x68, 0xb4 }, /* OVERRIDE */
+- { 0x36, 0x00 }, /* OVERRIDE */
+- { 0x05, 0x01 }, /* TOP_MASTER_ENABLE */
++ { 0x02, 0x06 },
++ { 0x03, 0x48 },
++ { 0x05, 0x04 },
++ { 0x06, 0x10 },
++ { 0x09, 0x3f },
++ { 0x0a, 0x3f },
++ { 0x0b, 0x3f },
++ { 0x2e, 0x15 }, /* OVERRIDE */
++ { 0x30, 0x10 }, /* OVERRIDE */
++ { 0x45, 0x58 }, /* OVERRIDE */
++ { 0x48, 0x19 }, /* OVERRIDE */
++ { 0x52, 0x03 }, /* OVERRIDE */
++ { 0x53, 0x44 }, /* OVERRIDE */
++ { 0x6a, 0x4b }, /* OVERRIDE */
++ { 0x76, 0x00 }, /* OVERRIDE */
++ { 0x78, 0x18 }, /* OVERRIDE */
++ { 0x7a, 0x17 }, /* OVERRIDE */
++ { 0x85, 0x06 }, /* OVERRIDE */
++ { 0x01, 0x01 }, /* TOP_MASTER_ENABLE */
+ { 0, 0 }
+ };
+
+ /* ------------------------------------------------------------------------- */
+
+ static struct reg_pair_t reg_pair_rftune[] = {
+- { 0x11, 0x00 }, /* abort tune */
+- { 0x13, 0x15 },
+- { 0x14, 0x40 },
+- { 0x15, 0x0e },
+- { 0x11, 0x02 }, /* start tune */
++ { 0x0f, 0x00 }, /* abort tune */
++ { 0x0c, 0x15 },
++ { 0x0d, 0x40 },
++ { 0x0e, 0x0e },
++ { 0x1f, 0x87 }, /* OVERRIDE */
++ { 0x20, 0x1f }, /* OVERRIDE */
++ { 0x21, 0x87 }, /* OVERRIDE */
++ { 0x22, 0x1f }, /* OVERRIDE */
++ { 0x80, 0x01 }, /* freq dependent */
++ { 0x0f, 0x01 }, /* start tune */
+ { 0, 0 }
+ };
+
+@@ -227,63 +216,20 @@ static void mxl5007t_set_mode_bits(struct mxl5007t_state *state,
+ s32 if_diff_out_level)
+ {
+ switch (mode) {
+- case MxL_MODE_OTA_DVBT_ATSC:
+- set_reg_bits(state->tab_init, 0x32, 0x0f, 0x06);
+- set_reg_bits(state->tab_init, 0x35, 0xff, 0x0e);
++ case MxL_MODE_ATSC:
++ set_reg_bits(state->tab_init, 0x06, 0x1f, 0x12);
+ break;
+- case MxL_MODE_OTA_ISDBT:
+- set_reg_bits(state->tab_init, 0x32, 0x0f, 0x06);
+- set_reg_bits(state->tab_init, 0x35, 0xff, 0x12);
++ case MxL_MODE_DVBT:
++ set_reg_bits(state->tab_init, 0x06, 0x1f, 0x11);
+ break;
+- case MxL_MODE_OTA_NTSC_PAL_GH:
+- set_reg_bits(state->tab_init, 0x16, 0x70, 0x00);
+- set_reg_bits(state->tab_init, 0x32, 0xff, 0x85);
++ case MxL_MODE_ISDBT:
++ set_reg_bits(state->tab_init, 0x06, 0x1f, 0x10);
+ break;
+- case MxL_MODE_OTA_PAL_IB:
+- set_reg_bits(state->tab_init, 0x16, 0x70, 0x10);
+- set_reg_bits(state->tab_init, 0x32, 0xff, 0x85);
+- break;
+- case MxL_MODE_OTA_PAL_D_SECAM_KL:
+- set_reg_bits(state->tab_init, 0x16, 0x70, 0x20);
+- set_reg_bits(state->tab_init, 0x32, 0xff, 0x85);
+- break;
+- case MxL_MODE_CABLE_DIGITAL:
+- set_reg_bits(state->tab_init_cable, 0x71, 0xff, 0x01);
+- set_reg_bits(state->tab_init_cable, 0x72, 0xff,
+- 8 - if_diff_out_level);
+- set_reg_bits(state->tab_init_cable, 0x74, 0xff, 0x17);
+- break;
+- case MxL_MODE_CABLE_NTSC_PAL_GH:
+- set_reg_bits(state->tab_init, 0x16, 0x70, 0x00);
+- set_reg_bits(state->tab_init, 0x32, 0xff, 0x85);
+- set_reg_bits(state->tab_init_cable, 0x71, 0xff, 0x01);
+- set_reg_bits(state->tab_init_cable, 0x72, 0xff,
+- 8 - if_diff_out_level);
+- set_reg_bits(state->tab_init_cable, 0x74, 0xff, 0x17);
+- break;
+- case MxL_MODE_CABLE_PAL_IB:
+- set_reg_bits(state->tab_init, 0x16, 0x70, 0x10);
+- set_reg_bits(state->tab_init, 0x32, 0xff, 0x85);
+- set_reg_bits(state->tab_init_cable, 0x71, 0xff, 0x01);
+- set_reg_bits(state->tab_init_cable, 0x72, 0xff,
++ case MxL_MODE_CABLE:
++ set_reg_bits(state->tab_init_cable, 0x09, 0xff, 0xc1);
++ set_reg_bits(state->tab_init_cable, 0x0a, 0xff,
+ 8 - if_diff_out_level);
+- set_reg_bits(state->tab_init_cable, 0x74, 0xff, 0x17);
+- break;
+- case MxL_MODE_CABLE_PAL_D_SECAM_KL:
+- set_reg_bits(state->tab_init, 0x16, 0x70, 0x20);
+- set_reg_bits(state->tab_init, 0x32, 0xff, 0x85);
+- set_reg_bits(state->tab_init_cable, 0x71, 0xff, 0x01);
+- set_reg_bits(state->tab_init_cable, 0x72, 0xff,
+- 8 - if_diff_out_level);
+- set_reg_bits(state->tab_init_cable, 0x74, 0xff, 0x17);
+- break;
+- case MxL_MODE_CABLE_SCTE40:
+- set_reg_bits(state->tab_init_cable, 0x36, 0xff, 0x08);
+- set_reg_bits(state->tab_init_cable, 0x68, 0xff, 0xbc);
+- set_reg_bits(state->tab_init_cable, 0x71, 0xff, 0x01);
+- set_reg_bits(state->tab_init_cable, 0x72, 0xff,
+- 8 - if_diff_out_level);
+- set_reg_bits(state->tab_init_cable, 0x74, 0xff, 0x17);
++ set_reg_bits(state->tab_init_cable, 0x0b, 0xff, 0x17);
+ break;
+ default:
+ mxl_fail(-EINVAL);
+@@ -302,43 +248,43 @@ static void mxl5007t_set_if_freq_bits(struct mxl5007t_state *state,
+ val = 0x00;
+ break;
+ case MxL_IF_4_5_MHZ:
+- val = 0x20;
++ val = 0x02;
+ break;
+ case MxL_IF_4_57_MHZ:
+- val = 0x30;
++ val = 0x03;
+ break;
+ case MxL_IF_5_MHZ:
+- val = 0x40;
++ val = 0x04;
+ break;
+ case MxL_IF_5_38_MHZ:
+- val = 0x50;
++ val = 0x05;
+ break;
+ case MxL_IF_6_MHZ:
+- val = 0x60;
++ val = 0x06;
+ break;
+ case MxL_IF_6_28_MHZ:
+- val = 0x70;
++ val = 0x07;
+ break;
+ case MxL_IF_9_1915_MHZ:
+- val = 0x80;
++ val = 0x08;
+ break;
+ case MxL_IF_35_25_MHZ:
+- val = 0x90;
++ val = 0x09;
+ break;
+ case MxL_IF_36_15_MHZ:
+- val = 0xa0;
++ val = 0x0a;
+ break;
+ case MxL_IF_44_MHZ:
+- val = 0xb0;
++ val = 0x0b;
+ break;
+ default:
+ mxl_fail(-EINVAL);
+ return;
+ }
+- set_reg_bits(state->tab_init, 0x0c, 0xf0, val);
++ set_reg_bits(state->tab_init, 0x02, 0x0f, val);
+
+ /* set inverted IF or normal IF */
+- set_reg_bits(state->tab_init, 0x0c, 0x08, invert_if ? 0x08 : 0x00);
++ set_reg_bits(state->tab_init, 0x02, 0x10, invert_if ? 0x10 : 0x00);
+
+ return;
+ }
+@@ -346,56 +292,68 @@ static void mxl5007t_set_if_freq_bits(struct mxl5007t_state *state,
+ static void mxl5007t_set_xtal_freq_bits(struct mxl5007t_state *state,
+ enum mxl5007t_xtal_freq xtal_freq)
+ {
+- u8 val;
+-
+ switch (xtal_freq) {
+ case MxL_XTAL_16_MHZ:
+- val = 0x00; /* select xtal freq & Ref Freq */
++ /* select xtal freq & ref freq */
++ set_reg_bits(state->tab_init, 0x03, 0xf0, 0x00);
++ set_reg_bits(state->tab_init, 0x05, 0x0f, 0x00);
+ break;
+ case MxL_XTAL_20_MHZ:
+- val = 0x11;
++ set_reg_bits(state->tab_init, 0x03, 0xf0, 0x10);
++ set_reg_bits(state->tab_init, 0x05, 0x0f, 0x01);
+ break;
+ case MxL_XTAL_20_25_MHZ:
+- val = 0x22;
++ set_reg_bits(state->tab_init, 0x03, 0xf0, 0x20);
++ set_reg_bits(state->tab_init, 0x05, 0x0f, 0x02);
+ break;
+ case MxL_XTAL_20_48_MHZ:
+- val = 0x33;
++ set_reg_bits(state->tab_init, 0x03, 0xf0, 0x30);
++ set_reg_bits(state->tab_init, 0x05, 0x0f, 0x03);
+ break;
+ case MxL_XTAL_24_MHZ:
+- val = 0x44;
++ set_reg_bits(state->tab_init, 0x03, 0xf0, 0x40);
++ set_reg_bits(state->tab_init, 0x05, 0x0f, 0x04);
+ break;
+ case MxL_XTAL_25_MHZ:
+- val = 0x55;
++ set_reg_bits(state->tab_init, 0x03, 0xf0, 0x50);
++ set_reg_bits(state->tab_init, 0x05, 0x0f, 0x05);
+ break;
+ case MxL_XTAL_25_14_MHZ:
+- val = 0x66;
++ set_reg_bits(state->tab_init, 0x03, 0xf0, 0x60);
++ set_reg_bits(state->tab_init, 0x05, 0x0f, 0x06);
+ break;
+ case MxL_XTAL_27_MHZ:
+- val = 0x77;
++ set_reg_bits(state->tab_init, 0x03, 0xf0, 0x70);
++ set_reg_bits(state->tab_init, 0x05, 0x0f, 0x07);
+ break;
+ case MxL_XTAL_28_8_MHZ:
+- val = 0x88;
++ set_reg_bits(state->tab_init, 0x03, 0xf0, 0x80);
++ set_reg_bits(state->tab_init, 0x05, 0x0f, 0x08);
+ break;
+ case MxL_XTAL_32_MHZ:
+- val = 0x99;
++ set_reg_bits(state->tab_init, 0x03, 0xf0, 0x90);
++ set_reg_bits(state->tab_init, 0x05, 0x0f, 0x09);
+ break;
+ case MxL_XTAL_40_MHZ:
+- val = 0xaa;
++ set_reg_bits(state->tab_init, 0x03, 0xf0, 0xa0);
++ set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0a);
+ break;
+ case MxL_XTAL_44_MHZ:
+- val = 0xbb;
++ set_reg_bits(state->tab_init, 0x03, 0xf0, 0xb0);
++ set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0b);
+ break;
+ case MxL_XTAL_48_MHZ:
+- val = 0xcc;
++ set_reg_bits(state->tab_init, 0x03, 0xf0, 0xc0);
++ set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0c);
+ break;
+ case MxL_XTAL_49_3811_MHZ:
+- val = 0xdd;
++ set_reg_bits(state->tab_init, 0x03, 0xf0, 0xd0);
++ set_reg_bits(state->tab_init, 0x05, 0x0f, 0x0d);
+ break;
+ default:
+ mxl_fail(-EINVAL);
+ return;
+ }
+- set_reg_bits(state->tab_init, 0x0b, 0xff, val);
+
+ return;
+ }
+@@ -412,16 +370,11 @@ static struct reg_pair_t *mxl5007t_calc_init_regs(struct mxl5007t_state *state,
+ mxl5007t_set_if_freq_bits(state, cfg->if_freq_hz, cfg->invert_if);
+ mxl5007t_set_xtal_freq_bits(state, cfg->xtal_freq_hz);
+
+- set_reg_bits(state->tab_init, 0x10, 0x40, cfg->loop_thru_enable << 6);
+-
+- set_reg_bits(state->tab_init, 0xd8, 0x08, cfg->clk_out_enable << 3);
+-
+- set_reg_bits(state->tab_init, 0x10, 0x07, cfg->clk_out_amp);
++ set_reg_bits(state->tab_init, 0x04, 0x01, cfg->loop_thru_enable);
++ set_reg_bits(state->tab_init, 0x03, 0x08, cfg->clk_out_enable << 3);
++ set_reg_bits(state->tab_init, 0x03, 0x07, cfg->clk_out_amp);
+
+- /* set IDAC to automatic mode control by AGC */
+- set_reg_bits(state->tab_init, 0x12, 0x80, 0x00);
+-
+- if (mode >= MxL_MODE_CABLE_DIGITAL) {
++ if (mode >= MxL_MODE_CABLE) {
+ copy_reg_bits(state->tab_init, state->tab_init_cable);
+ return state->tab_init_cable;
+ } else
+@@ -447,7 +400,7 @@ static void mxl5007t_set_bw_bits(struct mxl5007t_state *state,
+ * and DIG_MODEINDEX_CSF */
+ break;
+ case MxL_BW_7MHz:
+- val = 0x21;
++ val = 0x2a;
+ break;
+ case MxL_BW_8MHz:
+ val = 0x3f;
+@@ -456,7 +409,7 @@ static void mxl5007t_set_bw_bits(struct mxl5007t_state *state,
+ mxl_fail(-EINVAL);
+ return;
+ }
+- set_reg_bits(state->tab_rftune, 0x13, 0x3f, val);
++ set_reg_bits(state->tab_rftune, 0x0c, 0x3f, val);
+
+ return;
+ }
+@@ -493,8 +446,11 @@ reg_pair_t *mxl5007t_calc_rf_tune_regs(struct mxl5007t_state *state,
+ if (temp > 7812)
+ dig_rf_freq++;
+
+- set_reg_bits(state->tab_rftune, 0x14, 0xff, (u8)dig_rf_freq);
+- set_reg_bits(state->tab_rftune, 0x15, 0xff, (u8)(dig_rf_freq >> 8));
++ set_reg_bits(state->tab_rftune, 0x0d, 0xff, (u8) dig_rf_freq);
++ set_reg_bits(state->tab_rftune, 0x0e, 0xff, (u8) (dig_rf_freq >> 8));
++
++ if (rf_freq >= 333000000)
++ set_reg_bits(state->tab_rftune, 0x80, 0x40, 0x40);
+
+ return state->tab_rftune;
+ }
+@@ -551,9 +507,10 @@ static int mxl5007t_read_reg(struct mxl5007t_state *state, u8 reg, u8 *val)
+ static int mxl5007t_soft_reset(struct mxl5007t_state *state)
+ {
+ u8 d = 0xff;
+- struct i2c_msg msg = { .addr = state->i2c_props.addr, .flags = 0,
+- .buf = &d, .len = 1 };
+-
++ struct i2c_msg msg = {
++ .addr = state->i2c_props.addr, .flags = 0,
++ .buf = &d, .len = 1
++ };
+ int ret = i2c_transfer(state->i2c_props.adap, &msg, 1);
+
+ if (ret != 1) {
+@@ -580,9 +537,6 @@ static int mxl5007t_tuner_init(struct mxl5007t_state *state,
+ if (mxl_fail(ret))
+ goto fail;
+ mdelay(1);
+-
+- ret = mxl5007t_write_reg(state, 0x2c, 0x35);
+- mxl_fail(ret);
+ fail:
+ return ret;
+ }
+@@ -615,7 +569,7 @@ static int mxl5007t_synth_lock_status(struct mxl5007t_state *state,
+ *rf_locked = 0;
+ *ref_locked = 0;
+
+- ret = mxl5007t_read_reg(state, 0xcf, &d);
++ ret = mxl5007t_read_reg(state, 0xd8, &d);
+ if (mxl_fail(ret))
+ goto fail;
+
+@@ -628,37 +582,14 @@ fail:
+ return ret;
+ }
+
+-static int mxl5007t_check_rf_input_power(struct mxl5007t_state *state,
+- s32 *rf_input_level)
+-{
+- u8 d1, d2;
+- int ret;
+-
+- ret = mxl5007t_read_reg(state, 0xb7, &d1);
+- if (mxl_fail(ret))
+- goto fail;
+-
+- ret = mxl5007t_read_reg(state, 0xbf, &d2);
+- if (mxl_fail(ret))
+- goto fail;
+-
+- d2 = d2 >> 4;
+- if (d2 > 7)
+- d2 += 0xf0;
+-
+- *rf_input_level = (s32)(d1 + d2 - 113);
+-fail:
+- return ret;
+-}
+-
+ /* ------------------------------------------------------------------------- */
+
+ static int mxl5007t_get_status(struct dvb_frontend *fe, u32 *status)
+ {
+ struct mxl5007t_state *state = fe->tuner_priv;
+- int rf_locked, ref_locked;
+- s32 rf_input_level = 0;
+- int ret;
++ int rf_locked, ref_locked, ret;
++
++ *status = 0;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+@@ -669,10 +600,8 @@ static int mxl5007t_get_status(struct dvb_frontend *fe, u32 *status)
+ mxl_debug("%s%s", rf_locked ? "rf locked " : "",
+ ref_locked ? "ref locked" : "");
+
+- ret = mxl5007t_check_rf_input_power(state, &rf_input_level);
+- if (mxl_fail(ret))
+- goto fail;
+- mxl_debug("rf input power: %d", rf_input_level);
++ if ((rf_locked) || (ref_locked))
++ *status |= TUNER_STATUS_LOCKED;
+ fail:
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+@@ -695,11 +624,11 @@ static int mxl5007t_set_params(struct dvb_frontend *fe,
+ switch (params->u.vsb.modulation) {
+ case VSB_8:
+ case VSB_16:
+- mode = MxL_MODE_OTA_DVBT_ATSC;
++ mode = MxL_MODE_ATSC;
+ break;
+ case QAM_64:
+ case QAM_256:
+- mode = MxL_MODE_CABLE_DIGITAL;
++ mode = MxL_MODE_CABLE;
+ break;
+ default:
+ mxl_err("modulation not set!");
+@@ -721,7 +650,7 @@ static int mxl5007t_set_params(struct dvb_frontend *fe,
+ mxl_err("bandwidth not set!");
+ return -EINVAL;
+ }
+- mode = MxL_MODE_OTA_DVBT_ATSC;
++ mode = MxL_MODE_DVBT;
+ } else {
+ mxl_err("modulation type not supported!");
+ return -EINVAL;
+@@ -752,96 +681,20 @@ fail:
+ return ret;
+ }
+
+-static int mxl5007t_set_analog_params(struct dvb_frontend *fe,
+- struct analog_parameters *params)
+-{
+- struct mxl5007t_state *state = fe->tuner_priv;
+- enum mxl5007t_bw_mhz bw = 0; /* FIXME */
+- enum mxl5007t_mode cbl_mode;
+- enum mxl5007t_mode ota_mode;
+- char *mode_name;
+- int ret;
+- u32 freq = params->frequency * 62500;
+-
+-#define cable 1
+- if (params->std & V4L2_STD_MN) {
+- cbl_mode = MxL_MODE_CABLE_NTSC_PAL_GH;
+- ota_mode = MxL_MODE_OTA_NTSC_PAL_GH;
+- mode_name = "MN";
+- } else if (params->std & V4L2_STD_B) {
+- cbl_mode = MxL_MODE_CABLE_PAL_IB;
+- ota_mode = MxL_MODE_OTA_PAL_IB;
+- mode_name = "B";
+- } else if (params->std & V4L2_STD_GH) {
+- cbl_mode = MxL_MODE_CABLE_NTSC_PAL_GH;
+- ota_mode = MxL_MODE_OTA_NTSC_PAL_GH;
+- mode_name = "GH";
+- } else if (params->std & V4L2_STD_PAL_I) {
+- cbl_mode = MxL_MODE_CABLE_PAL_IB;
+- ota_mode = MxL_MODE_OTA_PAL_IB;
+- mode_name = "I";
+- } else if (params->std & V4L2_STD_DK) {
+- cbl_mode = MxL_MODE_CABLE_PAL_D_SECAM_KL;
+- ota_mode = MxL_MODE_OTA_PAL_D_SECAM_KL;
+- mode_name = "DK";
+- } else if (params->std & V4L2_STD_SECAM_L) {
+- cbl_mode = MxL_MODE_CABLE_PAL_D_SECAM_KL;
+- ota_mode = MxL_MODE_OTA_PAL_D_SECAM_KL;
+- mode_name = "L";
+- } else if (params->std & V4L2_STD_SECAM_LC) {
+- cbl_mode = MxL_MODE_CABLE_PAL_D_SECAM_KL;
+- ota_mode = MxL_MODE_OTA_PAL_D_SECAM_KL;
+- mode_name = "L'";
+- } else {
+- mode_name = "xx";
+- /* FIXME */
+- cbl_mode = MxL_MODE_CABLE_NTSC_PAL_GH;
+- ota_mode = MxL_MODE_OTA_NTSC_PAL_GH;
+- }
+- mxl_debug("setting mxl5007 to system %s", mode_name);
+-
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 1);
+-
+- mutex_lock(&state->lock);
+-
+- ret = mxl5007t_tuner_init(state, cable ? cbl_mode : ota_mode);
+- if (mxl_fail(ret))
+- goto fail;
+-
+- ret = mxl5007t_tuner_rf_tune(state, freq, bw);
+- if (mxl_fail(ret))
+- goto fail;
+-
+- state->frequency = freq;
+- state->bandwidth = 0;
+-fail:
+- mutex_unlock(&state->lock);
+-
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 0);
+-
+- return ret;
+-}
+-
+ /* ------------------------------------------------------------------------- */
+
+ static int mxl5007t_init(struct dvb_frontend *fe)
+ {
+ struct mxl5007t_state *state = fe->tuner_priv;
+ int ret;
+- u8 d;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+- ret = mxl5007t_read_reg(state, 0x05, &d);
+- if (mxl_fail(ret))
+- goto fail;
+-
+- ret = mxl5007t_write_reg(state, 0x05, d | 0x01);
++ /* wake from standby */
++ ret = mxl5007t_write_reg(state, 0x01, 0x01);
+ mxl_fail(ret);
+-fail:
++
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+@@ -852,18 +705,16 @@ static int mxl5007t_sleep(struct dvb_frontend *fe)
+ {
+ struct mxl5007t_state *state = fe->tuner_priv;
+ int ret;
+- u8 d;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+
+- ret = mxl5007t_read_reg(state, 0x05, &d);
+- if (mxl_fail(ret))
+- goto fail;
+-
+- ret = mxl5007t_write_reg(state, 0x05, d & ~0x01);
++ /* enter standby mode */
++ ret = mxl5007t_write_reg(state, 0x01, 0x00);
+ mxl_fail(ret);
+-fail:
++ ret = mxl5007t_write_reg(state, 0x0f, 0x00);
++ mxl_fail(ret);
++
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+@@ -911,7 +762,6 @@ static struct dvb_tuner_ops mxl5007t_tuner_ops = {
+ .init = mxl5007t_init,
+ .sleep = mxl5007t_sleep,
+ .set_params = mxl5007t_set_params,
+- .set_analog_params = mxl5007t_set_analog_params,
+ .get_status = mxl5007t_get_status,
+ .get_frequency = mxl5007t_get_frequency,
+ .get_bandwidth = mxl5007t_get_bandwidth,
+@@ -924,7 +774,7 @@ static int mxl5007t_get_chip_id(struct mxl5007t_state *state)
+ int ret;
+ u8 id;
+
+- ret = mxl5007t_read_reg(state, 0xd3, &id);
++ ret = mxl5007t_read_reg(state, 0xd9, &id);
+ if (mxl_fail(ret))
+ goto fail;
+
+@@ -947,8 +797,12 @@ static int mxl5007t_get_chip_id(struct mxl5007t_state *state)
+ case MxL_5007_V2_200_F2:
+ name = "MxL5007.v2.200.f2";
+ break;
++ case MxL_5007_V4:
++ name = "MxL5007T.v4";
++ break;
+ default:
+ name = "MxL5007T";
++ printk(KERN_WARNING "%s: unknown rev (%02x)\n", __func__, id);
+ id = MxL_UNKNOWN_ID;
+ }
+ state->chip_id = id;
+@@ -975,7 +829,7 @@ struct dvb_frontend *mxl5007t_attach(struct dvb_frontend *fe,
+ mutex_lock(&mxl5007t_list_mutex);
+ instance = hybrid_tuner_request_state(struct mxl5007t_state, state,
+ hybrid_tuner_instance_list,
+- i2c, addr, "mxl5007");
++ i2c, addr, "mxl5007t");
+ switch (instance) {
+ case 0:
+ goto fail;
+@@ -1018,7 +872,7 @@ EXPORT_SYMBOL_GPL(mxl5007t_attach);
+ MODULE_DESCRIPTION("MaxLinear MxL5007T Silicon IC tuner driver");
+ MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
+ MODULE_LICENSE("GPL");
+-MODULE_VERSION("0.1");
++MODULE_VERSION("0.2");
+
+ /*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+diff --git a/drivers/media/common/tuners/tda18271-common.c b/drivers/media/common/tuners/tda18271-common.c
+index 6fb5b45..fc76c30 100644
+--- a/drivers/media/common/tuners/tda18271-common.c
++++ b/drivers/media/common/tuners/tda18271-common.c
+@@ -490,9 +490,9 @@ int tda18271_set_standby_mode(struct dvb_frontend *fe,
+ 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;
++ regs[R_EP3] |= (sm ? (1 << 7) : 0) |
++ (sm_lt ? (1 << 6) : 0) |
++ (sm_xt ? (1 << 5) : 0);
+
+ return tda18271_write_regs(fe, R_EP3, 1);
+ }
+diff --git a/drivers/media/common/tuners/tda18271-fe.c b/drivers/media/common/tuners/tda18271-fe.c
+index 1b48b5d..b109356 100644
+--- a/drivers/media/common/tuners/tda18271-fe.c
++++ b/drivers/media/common/tuners/tda18271-fe.c
+@@ -818,6 +818,38 @@ fail:
+ return ret;
+ }
+
++/* ------------------------------------------------------------------ */
++
++static int tda18271_agc(struct dvb_frontend *fe)
++{
++ struct tda18271_priv *priv = fe->tuner_priv;
++ int ret = 0;
++
++ switch (priv->config) {
++ case 0:
++ /* no LNA */
++ tda_dbg("no agc configuration provided\n");
++ break;
++ case 3:
++ /* switch with GPIO of saa713x */
++ tda_dbg("invoking callback\n");
++ if (fe->callback)
++ ret = fe->callback(priv->i2c_props.adap->algo_data,
++ DVB_FRONTEND_COMPONENT_TUNER,
++ TDA18271_CALLBACK_CMD_AGC_ENABLE,
++ priv->mode);
++ break;
++ case 1:
++ case 2:
++ default:
++ /* n/a - currently not supported */
++ tda_err("unsupported configuration: %d\n", priv->config);
++ ret = -EINVAL;
++ break;
++ }
++ return ret;
++}
++
+ static int tda18271_tune(struct dvb_frontend *fe,
+ struct tda18271_std_map_item *map, u32 freq, u32 bw)
+ {
+@@ -827,6 +859,10 @@ static int tda18271_tune(struct dvb_frontend *fe,
+ tda_dbg("freq = %d, ifc = %d, bw = %d, agc_mode = %d, std = %d\n",
+ freq, map->if_freq, bw, map->agc_mode, map->std);
+
++ ret = tda18271_agc(fe);
++ if (tda_fail(ret))
++ tda_warn("failed to configure agc\n");
++
+ ret = tda18271_init(fe);
+ if (tda_fail(ret))
+ goto fail;
+@@ -1159,6 +1195,7 @@ struct dvb_frontend *tda18271_attach(struct dvb_frontend *fe, u8 addr,
+ /* new tuner instance */
+ priv->gate = (cfg) ? cfg->gate : TDA18271_GATE_AUTO;
+ priv->role = (cfg) ? cfg->role : TDA18271_MASTER;
++ priv->config = (cfg) ? cfg->config : 0;
+ priv->cal_initialized = false;
+ mutex_init(&priv->lock);
+
+diff --git a/drivers/media/common/tuners/tda18271-priv.h b/drivers/media/common/tuners/tda18271-priv.h
+index 81a7393..74beb28 100644
+--- a/drivers/media/common/tuners/tda18271-priv.h
++++ b/drivers/media/common/tuners/tda18271-priv.h
+@@ -91,11 +91,6 @@ enum tda18271_pll {
+ TDA18271_CAL_PLL,
+ };
+
+-enum tda18271_mode {
+- TDA18271_ANALOG,
+- TDA18271_DIGITAL,
+-};
+-
+ struct tda18271_map_layout;
+
+ enum tda18271_ver {
+@@ -114,6 +109,7 @@ struct tda18271_priv {
+ enum tda18271_i2c_gate gate;
+ enum tda18271_ver id;
+
++ unsigned int config; /* interface to saa713x / tda829x */
+ unsigned int tm_rfcal;
+ unsigned int cal_initialized:1;
+ unsigned int small_i2c:1;
+diff --git a/drivers/media/common/tuners/tda18271.h b/drivers/media/common/tuners/tda18271.h
+index 7db9831..53a9892 100644
+--- a/drivers/media/common/tuners/tda18271.h
++++ b/drivers/media/common/tuners/tda18271.h
+@@ -79,6 +79,16 @@ struct tda18271_config {
+
+ /* some i2c providers cant write all 39 registers at once */
+ unsigned int small_i2c:1;
++
++ /* interface to saa713x / tda829x */
++ unsigned int config;
++};
++
++#define TDA18271_CALLBACK_CMD_AGC_ENABLE 0
++
++enum tda18271_mode {
++ TDA18271_ANALOG = 0,
++ TDA18271_DIGITAL,
+ };
+
+ #if defined(CONFIG_MEDIA_TUNER_TDA18271) || (defined(CONFIG_MEDIA_TUNER_TDA18271_MODULE) && defined(MODULE))
+diff --git a/drivers/media/common/tuners/tda827x.c b/drivers/media/common/tuners/tda827x.c
+index f4d931f..36a7bc7 100644
+--- a/drivers/media/common/tuners/tda827x.c
++++ b/drivers/media/common/tuners/tda827x.c
+@@ -132,11 +132,31 @@ static const struct tda827x_data tda827x_table[] = {
+ { .lomax = 0, .spd = 0, .bs = 0, .bp = 0, .cp = 0, .gc3 = 0, .div1p5 = 0}
+ };
+
++static int tuner_transfer(struct dvb_frontend *fe,
++ struct i2c_msg *msg,
++ const int size)
++{
++ int rc;
++ struct tda827x_priv *priv = fe->tuner_priv;
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 1);
++ rc = i2c_transfer(priv->i2c_adap, msg, size);
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0);
++
++ if (rc >= 0 && rc != size)
++ return -EIO;
++
++ return rc;
++}
++
+ static int tda827xo_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+ {
+ struct tda827x_priv *priv = fe->tuner_priv;
+ u8 buf[14];
++ int rc;
+
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+ .buf = buf, .len = sizeof(buf) };
+@@ -183,27 +203,29 @@ static int tda827xo_set_params(struct dvb_frontend *fe,
+ buf[13] = 0x40;
+
+ msg.len = 14;
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 1);
+- if (i2c_transfer(priv->i2c_adap, &msg, 1) != 1) {
+- printk("%s: could not write to tuner at addr: 0x%02x\n",
+- __func__, priv->i2c_addr << 1);
+- return -EIO;
+- }
++ rc = tuner_transfer(fe, &msg, 1);
++ if (rc < 0)
++ goto err;
++
+ msleep(500);
+ /* correct CP value */
+ buf[0] = 0x30;
+ buf[1] = 0x50 + tda827x_table[i].cp;
+ msg.len = 2;
+
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 1);
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ rc = tuner_transfer(fe, &msg, 1);
++ if (rc < 0)
++ goto err;
+
+ priv->frequency = params->frequency;
+ priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
+
+ return 0;
++
++err:
++ printk(KERN_ERR "%s: could not write to tuner at addr: 0x%02x\n",
++ __func__, priv->i2c_addr << 1);
++ return rc;
+ }
+
+ static int tda827xo_sleep(struct dvb_frontend *fe)
+@@ -214,9 +236,7 @@ static int tda827xo_sleep(struct dvb_frontend *fe)
+ .buf = buf, .len = sizeof(buf) };
+
+ dprintk("%s:\n", __func__);
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 1);
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+
+ if (priv->cfg && priv->cfg->sleep)
+ priv->cfg->sleep(fe);
+@@ -266,44 +286,44 @@ static int tda827xo_set_analog_params(struct dvb_frontend *fe,
+
+ msg.buf = tuner_reg;
+ msg.len = 8;
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+
+ msg.buf = reg2;
+ msg.len = 2;
+ reg2[0] = 0x80;
+ reg2[1] = 0;
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+
+ reg2[0] = 0x60;
+ reg2[1] = 0xbf;
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+
+ reg2[0] = 0x30;
+ reg2[1] = tuner_reg[4] + 0x80;
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+
+ msleep(1);
+ reg2[0] = 0x30;
+ reg2[1] = tuner_reg[4] + 4;
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+
+ msleep(1);
+ reg2[0] = 0x30;
+ reg2[1] = tuner_reg[4];
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+
+ msleep(550);
+ reg2[0] = 0x30;
+ reg2[1] = (tuner_reg[4] & 0xfc) + tda827x_table[i].cp;
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+
+ reg2[0] = 0x60;
+ reg2[1] = 0x3f;
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+
+ reg2[0] = 0x80;
+ reg2[1] = 0x08; /* Vsync en */
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+
+ priv->frequency = params->frequency;
+
+@@ -317,7 +337,7 @@ static void tda827xo_agcf(struct dvb_frontend *fe)
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+ .buf = data, .len = 2};
+
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+ }
+
+ /* ------------------------------------------------------------------ */
+@@ -331,7 +351,7 @@ struct tda827xa_data {
+ u8 gc3;
+ };
+
+-static const struct tda827xa_data tda827xa_dvbt[] = {
++static struct tda827xa_data tda827xa_dvbt[] = {
+ { .lomax = 56875000, .svco = 3, .spd = 4, .scr = 0, .sbs = 0, .gc3 = 1},
+ { .lomax = 67250000, .svco = 0, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1},
+ { .lomax = 81250000, .svco = 1, .spd = 3, .scr = 0, .sbs = 0, .gc3 = 1},
+@@ -361,6 +381,36 @@ static const struct tda827xa_data tda827xa_dvbt[] = {
+ { .lomax = 0, .svco = 0, .spd = 0, .scr = 0, .sbs = 0, .gc3 = 0}
+ };
+
++static struct tda827xa_data tda827xa_dvbc[] = {
++ { .lomax = 50125000, .svco = 2, .spd = 4, .scr = 2, .sbs = 0, .gc3 = 3},
++ { .lomax = 58500000, .svco = 3, .spd = 4, .scr = 2, .sbs = 0, .gc3 = 3},
++ { .lomax = 69250000, .svco = 0, .spd = 3, .scr = 2, .sbs = 0, .gc3 = 3},
++ { .lomax = 83625000, .svco = 1, .spd = 3, .scr = 2, .sbs = 0, .gc3 = 3},
++ { .lomax = 97500000, .svco = 2, .spd = 3, .scr = 2, .sbs = 0, .gc3 = 3},
++ { .lomax = 100250000, .svco = 2, .spd = 3, .scr = 2, .sbs = 1, .gc3 = 1},
++ { .lomax = 117000000, .svco = 3, .spd = 3, .scr = 2, .sbs = 1, .gc3 = 1},
++ { .lomax = 138500000, .svco = 0, .spd = 2, .scr = 2, .sbs = 1, .gc3 = 1},
++ { .lomax = 167250000, .svco = 1, .spd = 2, .scr = 2, .sbs = 1, .gc3 = 1},
++ { .lomax = 187000000, .svco = 2, .spd = 2, .scr = 2, .sbs = 1, .gc3 = 1},
++ { .lomax = 200500000, .svco = 2, .spd = 2, .scr = 2, .sbs = 2, .gc3 = 1},
++ { .lomax = 234000000, .svco = 3, .spd = 2, .scr = 2, .sbs = 2, .gc3 = 3},
++ { .lomax = 277000000, .svco = 0, .spd = 1, .scr = 2, .sbs = 2, .gc3 = 3},
++ { .lomax = 325000000, .svco = 1, .spd = 1, .scr = 2, .sbs = 2, .gc3 = 1},
++ { .lomax = 334500000, .svco = 1, .spd = 1, .scr = 2, .sbs = 3, .gc3 = 3},
++ { .lomax = 401000000, .svco = 2, .spd = 1, .scr = 2, .sbs = 3, .gc3 = 3},
++ { .lomax = 468000000, .svco = 3, .spd = 1, .scr = 2, .sbs = 3, .gc3 = 1},
++ { .lomax = 535000000, .svco = 0, .spd = 0, .scr = 1, .sbs = 3, .gc3 = 1},
++ { .lomax = 554000000, .svco = 0, .spd = 0, .scr = 2, .sbs = 3, .gc3 = 1},
++ { .lomax = 638000000, .svco = 1, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 1},
++ { .lomax = 669000000, .svco = 1, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 1},
++ { .lomax = 720000000, .svco = 2, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 1},
++ { .lomax = 802000000, .svco = 2, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 1},
++ { .lomax = 835000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 1},
++ { .lomax = 885000000, .svco = 3, .spd = 0, .scr = 1, .sbs = 4, .gc3 = 1},
++ { .lomax = 911000000, .svco = 3, .spd = 0, .scr = 2, .sbs = 4, .gc3 = 1},
++ { .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},
+@@ -398,13 +448,8 @@ static int tda827xa_sleep(struct dvb_frontend *fe)
+ .buf = buf, .len = sizeof(buf) };
+
+ dprintk("%s:\n", __func__);
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 1);
+
+- i2c_transfer(priv->i2c_adap, &msg, 1);
+-
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 0);
++ tuner_transfer(fe, &msg, 1);
+
+ if (priv->cfg && priv->cfg->sleep)
+ priv->cfg->sleep(fe);
+@@ -455,7 +500,7 @@ static void tda827xa_lna_gain(struct dvb_frontend *fe, int high,
+ buf[1] = high ? 0 : 1;
+ if (priv->cfg->config == 2)
+ buf[1] = high ? 1 : 0;
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+ break;
+ case 3: /* switch with GPIO of saa713x */
+ if (fe->callback)
+@@ -469,12 +514,13 @@ static int tda827xa_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+ {
+ struct tda827x_priv *priv = fe->tuner_priv;
++ struct tda827xa_data *frequency_map = tda827xa_dvbt;
+ u8 buf[11];
+
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = 0,
+ .buf = buf, .len = sizeof(buf) };
+
+- int i, tuner_freq, if_freq;
++ int i, tuner_freq, if_freq, rc;
+ u32 N;
+
+ dprintk("%s:\n", __func__);
+@@ -495,56 +541,58 @@ static int tda827xa_set_params(struct dvb_frontend *fe,
+ }
+ tuner_freq = params->frequency + if_freq;
+
++ if (fe->ops.info.type == FE_QAM) {
++ dprintk("%s select tda827xa_dvbc\n", __func__);
++ frequency_map = tda827xa_dvbc;
++ }
++
+ i = 0;
+- while (tda827xa_dvbt[i].lomax < tuner_freq) {
+- if(tda827xa_dvbt[i + 1].lomax == 0)
++ while (frequency_map[i].lomax < tuner_freq) {
++ if (frequency_map[i + 1].lomax == 0)
+ break;
+ i++;
+ }
+
+- N = ((tuner_freq + 31250) / 62500) << tda827xa_dvbt[i].spd;
++ N = ((tuner_freq + 31250) / 62500) << frequency_map[i].spd;
+ buf[0] = 0; // subaddress
+ buf[1] = N >> 8;
+ buf[2] = N & 0xff;
+ buf[3] = 0;
+ buf[4] = 0x16;
+- buf[5] = (tda827xa_dvbt[i].spd << 5) + (tda827xa_dvbt[i].svco << 3) +
+- tda827xa_dvbt[i].sbs;
+- buf[6] = 0x4b + (tda827xa_dvbt[i].gc3 << 4);
++ buf[5] = (frequency_map[i].spd << 5) + (frequency_map[i].svco << 3) +
++ frequency_map[i].sbs;
++ buf[6] = 0x4b + (frequency_map[i].gc3 << 4);
+ buf[7] = 0x1c;
+ buf[8] = 0x06;
+ buf[9] = 0x24;
+ buf[10] = 0x00;
+ msg.len = 11;
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 1);
+- if (i2c_transfer(priv->i2c_adap, &msg, 1) != 1) {
+- printk("%s: could not write to tuner at addr: 0x%02x\n",
+- __func__, priv->i2c_addr << 1);
+- return -EIO;
+- }
++ rc = tuner_transfer(fe, &msg, 1);
++ if (rc < 0)
++ goto err;
++
+ buf[0] = 0x90;
+ buf[1] = 0xff;
+ buf[2] = 0x60;
+ buf[3] = 0x00;
+ buf[4] = 0x59; // lpsel, for 6MHz + 2
+ msg.len = 5;
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 1);
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ rc = tuner_transfer(fe, &msg, 1);
++ if (rc < 0)
++ goto err;
+
+ buf[0] = 0xa0;
+ buf[1] = 0x40;
+ msg.len = 2;
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 1);
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ rc = tuner_transfer(fe, &msg, 1);
++ if (rc < 0)
++ goto err;
+
+ msleep(11);
+ msg.flags = I2C_M_RD;
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 1);
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ rc = tuner_transfer(fe, &msg, 1);
++ if (rc < 0)
++ goto err;
+ msg.flags = 0;
+
+ buf[1] >>= 4;
+@@ -553,49 +601,55 @@ static int tda827xa_set_params(struct dvb_frontend *fe,
+ tda827xa_lna_gain(fe, 0, NULL);
+ buf[0] = 0x60;
+ buf[1] = 0x0c;
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 1);
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ rc = tuner_transfer(fe, &msg, 1);
++ if (rc < 0)
++ goto err;
+ }
+
+ buf[0] = 0xc0;
+ buf[1] = 0x99; // lpsel, for 6MHz + 2
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 1);
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ rc = tuner_transfer(fe, &msg, 1);
++ if (rc < 0)
++ goto err;
+
+ buf[0] = 0x60;
+ buf[1] = 0x3c;
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 1);
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ rc = tuner_transfer(fe, &msg, 1);
++ if (rc < 0)
++ goto err;
+
+ /* correct CP value */
+ buf[0] = 0x30;
+- buf[1] = 0x10 + tda827xa_dvbt[i].scr;
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 1);
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ buf[1] = 0x10 + frequency_map[i].scr;
++ rc = tuner_transfer(fe, &msg, 1);
++ if (rc < 0)
++ goto err;
+
+ msleep(163);
+ buf[0] = 0xc0;
+ buf[1] = 0x39; // lpsel, for 6MHz + 2
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 1);
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ rc = tuner_transfer(fe, &msg, 1);
++ if (rc < 0)
++ goto err;
+
+ msleep(3);
+ /* freeze AGC1 */
+ buf[0] = 0x50;
+- buf[1] = 0x4f + (tda827xa_dvbt[i].gc3 << 4);
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 1);
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ buf[1] = 0x4f + (frequency_map[i].gc3 << 4);
++ rc = tuner_transfer(fe, &msg, 1);
++ if (rc < 0)
++ goto err;
+
+ priv->frequency = params->frequency;
+ priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
+
++
+ return 0;
++
++err:
++ printk(KERN_ERR "%s: could not write to tuner at addr: 0x%02x\n",
++ __func__, priv->i2c_addr << 1);
++ return rc;
+ }
+
+
+@@ -643,7 +697,7 @@ static int tda827xa_set_analog_params(struct dvb_frontend *fe,
+ tuner_reg[9] = 0x20;
+ tuner_reg[10] = 0x00;
+ msg.len = 11;
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+
+ tuner_reg[0] = 0x90;
+ tuner_reg[1] = 0xff;
+@@ -651,19 +705,19 @@ static int tda827xa_set_analog_params(struct dvb_frontend *fe,
+ tuner_reg[3] = 0;
+ tuner_reg[4] = 0x99 + (priv->lpsel << 1);
+ msg.len = 5;
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+
+ tuner_reg[0] = 0xa0;
+ tuner_reg[1] = 0xc0;
+ msg.len = 2;
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+
+ tuner_reg[0] = 0x30;
+ tuner_reg[1] = 0x10 + tda827xa_analog[i].scr;
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+
+ msg.flags = I2C_M_RD;
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+ msg.flags = 0;
+ tuner_reg[1] >>= 4;
+ dprintk("AGC2 gain is: %d\n", tuner_reg[1]);
+@@ -673,24 +727,24 @@ static int tda827xa_set_analog_params(struct dvb_frontend *fe,
+ msleep(100);
+ tuner_reg[0] = 0x60;
+ tuner_reg[1] = 0x3c;
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &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_transfer(fe, &msg, 1);
+
+ tuner_reg[0] = 0x80;
+ tuner_reg[1] = 0x28;
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+
+ tuner_reg[0] = 0xb0;
+ tuner_reg[1] = 0x01;
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+
+ tuner_reg[0] = 0xc0;
+ tuner_reg[1] = 0x19 + (priv->lpsel << 1);
+- i2c_transfer(priv->i2c_adap, &msg, 1);
++ tuner_transfer(fe, &msg, 1);
+
+ priv->frequency = params->frequency;
+
+@@ -703,7 +757,7 @@ static void tda827xa_agcf(struct dvb_frontend *fe)
+ 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);
++ tuner_transfer(fe, &msg, 1);
+ }
+
+ /* ------------------------------------------------------------------ */
+@@ -792,16 +846,19 @@ static struct dvb_tuner_ops tda827xa_tuner_ops = {
+ };
+
+ static int tda827x_probe_version(struct dvb_frontend *fe)
+-{ u8 data;
++{
++ u8 data;
++ int rc;
+ struct tda827x_priv *priv = fe->tuner_priv;
+ struct i2c_msg msg = { .addr = priv->i2c_addr, .flags = I2C_M_RD,
+ .buf = &data, .len = 1 };
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 1);
+- if (i2c_transfer(priv->i2c_adap, &msg, 1) != 1) {
++
++ rc = tuner_transfer(fe, &msg, 1);
++
++ if (rc < 0) {
+ printk("%s: could not read from tuner at addr: 0x%02x\n",
+ __func__, msg.addr << 1);
+- return -EIO;
++ return rc;
+ }
+ if ((data & 0x3c) == 0) {
+ dprintk("tda827x tuner found\n");
+diff --git a/drivers/media/common/tuners/tda8290.c b/drivers/media/common/tuners/tda8290.c
+index 4b8662e..064d14c 100644
+--- a/drivers/media/common/tuners/tda8290.c
++++ b/drivers/media/common/tuners/tda8290.c
+@@ -22,7 +22,7 @@
+
+ #include <linux/i2c.h>
+ #include <linux/delay.h>
+-#include <linux/videodev.h>
++#include <linux/videodev2.h>
+ #include "tuner-i2c.h"
+ #include "tda8290.h"
+ #include "tda827x.h"
+@@ -566,8 +566,11 @@ static int tda829x_find_tuner(struct dvb_frontend *fe)
+ u8 data;
+ struct i2c_msg msg = { .flags = I2C_M_RD, .buf = &data, .len = 1 };
+
+- if (NULL == analog_ops->i2c_gate_ctrl)
++ if (!analog_ops->i2c_gate_ctrl) {
++ printk(KERN_ERR "tda8290: no gate control were provided!\n");
++
+ return -EINVAL;
++ }
+
+ analog_ops->i2c_gate_ctrl(fe, 1);
+
+@@ -615,11 +618,13 @@ static int tda829x_find_tuner(struct dvb_frontend *fe)
+
+ if (ret != 1) {
+ tuner_warn("tuner access failed!\n");
++ analog_ops->i2c_gate_ctrl(fe, 0);
+ return -EREMOTEIO;
+ }
+
+ if ((data == 0x83) || (data == 0x84)) {
+ priv->ver |= TDA18271;
++ tda829x_tda18271_config.config = priv->cfg.config;
+ dvb_attach(tda18271_attach, fe, priv->tda827x_addr,
+ priv->i2c_props.adap, &tda829x_tda18271_config);
+ } else {
+diff --git a/drivers/media/common/tuners/tea5761.c b/drivers/media/common/tuners/tea5761.c
+index b23dade..60ed872 100644
+--- a/drivers/media/common/tuners/tea5761.c
++++ b/drivers/media/common/tuners/tea5761.c
+@@ -9,7 +9,7 @@
+
+ #include <linux/i2c.h>
+ #include <linux/delay.h>
+-#include <linux/videodev.h>
++#include <linux/videodev2.h>
+ #include <media/tuner.h>
+ #include "tuner-i2c.h"
+ #include "tea5761.h"
+diff --git a/drivers/media/common/tuners/tea5767.c b/drivers/media/common/tuners/tea5767.c
+index 1f56463..223a226 100644
+--- a/drivers/media/common/tuners/tea5767.c
++++ b/drivers/media/common/tuners/tea5767.c
+@@ -12,7 +12,7 @@
+
+ #include <linux/i2c.h>
+ #include <linux/delay.h>
+-#include <linux/videodev.h>
++#include <linux/videodev2.h>
+ #include "tuner-i2c.h"
+ #include "tea5767.h"
+
+diff --git a/drivers/media/common/tuners/xc5000.c b/drivers/media/common/tuners/xc5000.c
+index 493ce93..b545985 100644
+--- a/drivers/media/common/tuners/xc5000.c
++++ b/drivers/media/common/tuners/xc5000.c
+@@ -739,7 +739,10 @@ static int xc5000_set_analog_params(struct dvb_frontend *fe,
+ dprintk(1, "%s() frequency=%d (in units of 62.5khz)\n",
+ __func__, params->frequency);
+
+- priv->rf_mode = XC_RF_MODE_CABLE; /* Fix me: it could be air. */
++ /* Fix me: it could be air. */
++ priv->rf_mode = params->mode;
++ if (params->mode > XC_RF_MODE_CABLE)
++ priv->rf_mode = XC_RF_MODE_CABLE;
+
+ /* params->frequency is in units of 62.5khz */
+ priv->freq_hz = params->frequency * 62500;
+@@ -970,8 +973,6 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
+ case 1:
+ /* new tuner instance */
+ priv->bandwidth = BANDWIDTH_6_MHZ;
+- priv->if_khz = cfg->if_khz;
+-
+ fe->tuner_priv = priv;
+ break;
+ default:
+@@ -980,6 +981,13 @@ struct dvb_frontend *xc5000_attach(struct dvb_frontend *fe,
+ break;
+ }
+
++ if (priv->if_khz == 0) {
++ /* If the IF hasn't been set yet, use the value provided by
++ the caller (occurs in hybrid devices where the analog
++ call to xc5000_attach occurs before the digital side) */
++ priv->if_khz = cfg->if_khz;
++ }
++
+ /* Check if firmware has been loaded. It is possible that another
+ instance of the driver has loaded the firmware.
+ */
+diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig
+index a8c6249..9e57814 100644
+--- a/drivers/media/dvb/b2c2/Kconfig
++++ b/drivers/media/dvb/b2c2/Kconfig
+@@ -13,7 +13,7 @@ config DVB_B2C2_FLEXCOP
+ select DVB_TUNER_ITD1000 if !DVB_FE_CUSTOMISE
+ select DVB_ISL6421 if !DVB_FE_CUSTOMISE
+ select DVB_CX24123 if !DVB_FE_CUSTOMISE
+- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE
++ select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
+ select DVB_TUNER_CX24113 if !DVB_FE_CUSTOMISE
+ help
+ Support for the digital TV receiver chip made by B2C2 Inc. included in
+diff --git a/drivers/media/dvb/b2c2/Makefile b/drivers/media/dvb/b2c2/Makefile
+index d9db066..b97cf72 100644
+--- a/drivers/media/dvb/b2c2/Makefile
++++ b/drivers/media/dvb/b2c2/Makefile
+@@ -2,7 +2,6 @@ b2c2-flexcop-objs = flexcop.o flexcop-fe-tuner.o flexcop-i2c.o \
+ flexcop-sram.o flexcop-eeprom.o flexcop-misc.o flexcop-hw-filter.o
+ obj-$(CONFIG_DVB_B2C2_FLEXCOP) += b2c2-flexcop.o
+
+-
+ ifneq ($(CONFIG_DVB_B2C2_FLEXCOP_PCI),)
+ b2c2-flexcop-objs += flexcop-dma.o
+ endif
+diff --git a/drivers/media/dvb/b2c2/flexcop-common.h b/drivers/media/dvb/b2c2/flexcop-common.h
+index 8ce0633..3e1c472 100644
+--- a/drivers/media/dvb/b2c2/flexcop-common.h
++++ b/drivers/media/dvb/b2c2/flexcop-common.h
+@@ -28,11 +28,14 @@
+
+ /* Steal from usb.h */
+ #undef err
+-#define err(format, arg...) printk(KERN_ERR FC_LOG_PREFIX ": " format "\n" , ## arg)
++#define err(format, arg...) \
++ printk(KERN_ERR FC_LOG_PREFIX ": " format "\n" , ## arg)
+ #undef info
+-#define info(format, arg...) printk(KERN_INFO FC_LOG_PREFIX ": " format "\n" , ## arg)
++#define info(format, arg...) \
++ printk(KERN_INFO FC_LOG_PREFIX ": " format "\n" , ## arg)
+ #undef warn
+-#define warn(format, arg...) printk(KERN_WARNING FC_LOG_PREFIX ": " format "\n" , ## arg)
++#define warn(format, arg...) \
++ printk(KERN_WARNING FC_LOG_PREFIX ": " format "\n" , ## arg)
+
+ struct flexcop_dma {
+ struct pci_dev *pdev;
+@@ -91,16 +94,14 @@ struct flexcop_device {
+ int fullts_streaming_state;
+
+ /* bus specific callbacks */
+- flexcop_ibi_value (*read_ibi_reg) (struct flexcop_device *, flexcop_ibi_register);
+- int (*write_ibi_reg) (struct flexcop_device *, flexcop_ibi_register, flexcop_ibi_value);
+-
+-
+- int (*i2c_request) (struct flexcop_i2c_adapter*,
++ flexcop_ibi_value(*read_ibi_reg) (struct flexcop_device *,
++ flexcop_ibi_register);
++ int (*write_ibi_reg) (struct flexcop_device *,
++ flexcop_ibi_register, flexcop_ibi_value);
++ int (*i2c_request) (struct flexcop_i2c_adapter *,
+ flexcop_access_op_t, u8 chipaddr, u8 addr, u8 *buf, u16 len);
+- int (*stream_control) (struct flexcop_device*, int);
+-
++ int (*stream_control) (struct flexcop_device *, int);
+ int (*get_mac_addr) (struct flexcop_device *fc, int extended);
+-
+ void *bus_specific;
+ };
+
+@@ -111,22 +112,28 @@ void flexcop_pass_dmx_data(struct flexcop_device *fc, u8 *buf, u32 len);
+ void flexcop_pass_dmx_packets(struct flexcop_device *fc, u8 *buf, u32 no);
+
+ struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len);
+-void flexcop_device_kfree(struct flexcop_device*);
++void flexcop_device_kfree(struct flexcop_device *);
+
+-int flexcop_device_initialize(struct flexcop_device*);
++int flexcop_device_initialize(struct flexcop_device *);
+ void flexcop_device_exit(struct flexcop_device *fc);
+-
+ void flexcop_reset_block_300(struct flexcop_device *fc);
+
+ /* from flexcop-dma.c */
+-int flexcop_dma_allocate(struct pci_dev *pdev, struct flexcop_dma *dma, u32 size);
++int flexcop_dma_allocate(struct pci_dev *pdev,
++ struct flexcop_dma *dma, u32 size);
+ void flexcop_dma_free(struct flexcop_dma *dma);
+
+-int flexcop_dma_control_timer_irq(struct flexcop_device *fc, flexcop_dma_index_t no, int onoff);
+-int flexcop_dma_control_size_irq(struct flexcop_device *fc, flexcop_dma_index_t no, int onoff);
+-int flexcop_dma_config(struct flexcop_device *fc, struct flexcop_dma *dma, flexcop_dma_index_t dma_idx);
+-int flexcop_dma_xfer_control(struct flexcop_device *fc, flexcop_dma_index_t dma_idx, flexcop_dma_addr_index_t index, int onoff);
+-int flexcop_dma_config_timer(struct flexcop_device *fc, flexcop_dma_index_t dma_idx, u8 cycles);
++int flexcop_dma_control_timer_irq(struct flexcop_device *fc,
++ flexcop_dma_index_t no, int onoff);
++int flexcop_dma_control_size_irq(struct flexcop_device *fc,
++ flexcop_dma_index_t no, int onoff);
++int flexcop_dma_config(struct flexcop_device *fc, struct flexcop_dma *dma,
++ flexcop_dma_index_t dma_idx);
++int flexcop_dma_xfer_control(struct flexcop_device *fc,
++ flexcop_dma_index_t dma_idx, flexcop_dma_addr_index_t index,
++ int onoff);
++int flexcop_dma_config_timer(struct flexcop_device *fc,
++ flexcop_dma_index_t dma_idx, u8 cycles);
+
+ /* from flexcop-eeprom.c */
+ /* the PCI part uses this call to get the MAC address, the USB part has its own */
+@@ -141,13 +148,15 @@ int flexcop_i2c_request(struct flexcop_i2c_adapter*, flexcop_access_op_t,
+ u8 chipaddr, u8 addr, u8 *buf, u16 len);
+
+ /* from flexcop-sram.c */
+-int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest, flexcop_sram_dest_target_t target);
++int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest,
++ flexcop_sram_dest_target_t target);
+ void flexcop_wan_set_speed(struct flexcop_device *fc, flexcop_wan_speed_t s);
+-void flexcop_sram_ctrl(struct flexcop_device *fc, int usb_wan, int sramdma, int maximumfill);
++void flexcop_sram_ctrl(struct flexcop_device *fc,
++ int usb_wan, int sramdma, int maximumfill);
+
+ /* global prototypes for the flexcop-chip */
+ /* from flexcop-fe-tuner.c */
+-int flexcop_frontend_init(struct flexcop_device *card);
++int flexcop_frontend_init(struct flexcop_device *fc);
+ void flexcop_frontend_exit(struct flexcop_device *fc);
+
+ /* from flexcop-i2c.c */
+@@ -159,11 +168,14 @@ int flexcop_sram_init(struct flexcop_device *fc);
+
+ /* from flexcop-misc.c */
+ void flexcop_determine_revision(struct flexcop_device *fc);
+-void flexcop_device_name(struct flexcop_device *fc,const char *prefix,const char *suffix);
+-void flexcop_dump_reg(struct flexcop_device *fc, flexcop_ibi_register reg, int num);
++void flexcop_device_name(struct flexcop_device *fc,
++ const char *prefix, const char *suffix);
++void flexcop_dump_reg(struct flexcop_device *fc,
++ flexcop_ibi_register reg, int num);
+
+ /* from flexcop-hw-filter.c */
+-int flexcop_pid_feed_control(struct flexcop_device *fc, struct dvb_demux_feed *dvbdmxfeed, int onoff);
++int flexcop_pid_feed_control(struct flexcop_device *fc,
++ struct dvb_demux_feed *dvbdmxfeed, int onoff);
+ void flexcop_hw_filter_init(struct flexcop_device *fc);
+
+ void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff);
+diff --git a/drivers/media/dvb/b2c2/flexcop-dma.c b/drivers/media/dvb/b2c2/flexcop-dma.c
+index 26f0011..2881e0d 100644
+--- a/drivers/media/dvb/b2c2/flexcop-dma.c
++++ b/drivers/media/dvb/b2c2/flexcop-dma.c
+@@ -1,13 +1,12 @@
+ /*
+- * This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
+- *
+- * flexcop-dma.c - methods for configuring and controlling the DMA of the FlexCop.
+- *
+- * see flexcop.c for copyright information.
++ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
++ * flexcop-dma.c - configuring and controlling the DMA of the FlexCop
++ * see flexcop.c for copyright information
+ */
+ #include "flexcop.h"
+
+-int flexcop_dma_allocate(struct pci_dev *pdev, struct flexcop_dma *dma, u32 size)
++int flexcop_dma_allocate(struct pci_dev *pdev,
++ struct flexcop_dma *dma, u32 size)
+ {
+ u8 *tcpu;
+ dma_addr_t tdma = 0;
+@@ -32,7 +31,8 @@ EXPORT_SYMBOL(flexcop_dma_allocate);
+
+ void flexcop_dma_free(struct flexcop_dma *dma)
+ {
+- pci_free_consistent(dma->pdev, dma->size*2,dma->cpu_addr0, dma->dma_addr0);
++ pci_free_consistent(dma->pdev, dma->size*2,
++ dma->cpu_addr0, dma->dma_addr0);
+ memset(dma,0,sizeof(struct flexcop_dma));
+ }
+ EXPORT_SYMBOL(flexcop_dma_free);
+@@ -44,8 +44,8 @@ int flexcop_dma_config(struct flexcop_device *fc,
+ flexcop_ibi_value v0x0,v0x4,v0xc;
+ v0x0.raw = v0x4.raw = v0xc.raw = 0;
+
+- v0x0.dma_0x0.dma_address0 = dma->dma_addr0 >> 2;
+- v0xc.dma_0xc.dma_address1 = dma->dma_addr1 >> 2;
++ v0x0.dma_0x0.dma_address0 = dma->dma_addr0 >> 2;
++ v0xc.dma_0xc.dma_address1 = dma->dma_addr1 >> 2;
+ v0x4.dma_0x4_write.dma_addr_size = dma->size / 4;
+
+ if ((dma_idx & FC_DMA_1) == dma_idx) {
+@@ -57,7 +57,8 @@ int flexcop_dma_config(struct flexcop_device *fc,
+ fc->write_ibi_reg(fc,dma2_014,v0x4);
+ fc->write_ibi_reg(fc,dma2_01c,v0xc);
+ } else {
+- err("either DMA1 or DMA2 can be configured at the within one flexcop_dma_config call.");
++ err("either DMA1 or DMA2 can be configured within one "
++ "flexcop_dma_config call.");
+ return -EINVAL;
+ }
+
+@@ -81,7 +82,8 @@ int flexcop_dma_xfer_control(struct flexcop_device *fc,
+ r0x0 = dma2_010;
+ r0xc = dma2_01c;
+ } else {
+- err("either transfer DMA1 or DMA2 can be started within one flexcop_dma_xfer_control call.");
++ err("either transfer DMA1 or DMA2 can be started within one "
++ "flexcop_dma_xfer_control call.");
+ return -EINVAL;
+ }
+
+@@ -154,8 +156,7 @@ EXPORT_SYMBOL(flexcop_dma_control_timer_irq);
+
+ /* 1 cycles = 1.97 msec */
+ int flexcop_dma_config_timer(struct flexcop_device *fc,
+- flexcop_dma_index_t dma_idx,
+- u8 cycles)
++ flexcop_dma_index_t dma_idx, u8 cycles)
+ {
+ flexcop_ibi_register r = (dma_idx & FC_DMA_1) ? dma1_004 : dma2_014;
+ flexcop_ibi_value v = fc->read_ibi_reg(fc,r);
+diff --git a/drivers/media/dvb/b2c2/flexcop-eeprom.c b/drivers/media/dvb/b2c2/flexcop-eeprom.c
+index 8a8ae8a..a25373a 100644
+--- a/drivers/media/dvb/b2c2/flexcop-eeprom.c
++++ b/drivers/media/dvb/b2c2/flexcop-eeprom.c
+@@ -1,9 +1,7 @@
+ /*
+- * This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
+- *
+- * flexcop-eeprom.c - eeprom access methods (currently only MAC address reading is used)
+- *
+- * see flexcop.c for copyright information.
++ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
++ * flexcop-eeprom.c - eeprom access methods (currently only MAC address reading)
++ * see flexcop.c for copyright information
+ */
+ #include "flexcop.h"
+
+@@ -14,17 +12,17 @@ static int eeprom_write(struct adapter *adapter, u16 addr, u8 *buf, u16 len)
+ return flex_i2c_write(adapter, 0x20000000, 0x50, addr, buf, len);
+ }
+
+-static int eeprom_lrc_write(struct adapter *adapter, u32 addr, u32 len, u8 *wbuf, u8 *rbuf, int retries)
++static int eeprom_lrc_write(struct adapter *adapter, u32 addr,
++ u32 len, u8 *wbuf, u8 *rbuf, int retries)
+ {
+- int i;
++int i;
+
+- for (i = 0; i < retries; i++) {
+- if (eeprom_write(adapter, addr, wbuf, len) == len) {
+- if (eeprom_lrc_read(adapter, addr, len, rbuf, retries) == 1)
+- return 1;
++for (i = 0; i < retries; i++) {
++ if (eeprom_write(adapter, addr, wbuf, len) == len) {
++ if (eeprom_lrc_read(adapter, addr, len, rbuf, retries) == 1)
++ return 1;
+ }
+ }
+-
+ return 0;
+ }
+
+@@ -39,12 +37,10 @@ static int eeprom_writeKey(struct adapter *adapter, u8 *key, u32 len)
+ return 0;
+
+ memcpy(wbuf, key, len);
+-
+ wbuf[16] = 0;
+ wbuf[17] = 0;
+ wbuf[18] = 0;
+ wbuf[19] = calc_lrc(wbuf, 19);
+-
+ return eeprom_lrc_write(adapter, 0x3e4, 20, wbuf, rbuf, 4);
+ }
+
+@@ -59,7 +55,6 @@ static int eeprom_readKey(struct adapter *adapter, u8 *key, u32 len)
+ return 0;
+
+ memcpy(key, buf, len);
+-
+ return 1;
+ }
+
+@@ -74,9 +69,7 @@ static char eeprom_set_mac_addr(struct adapter *adapter, char type, u8 *mac)
+ tmp[3] = mac[5];
+ tmp[4] = mac[6];
+ tmp[5] = mac[7];
+-
+ } else {
+-
+ tmp[0] = mac[0];
+ tmp[1] = mac[1];
+ tmp[2] = mac[2];
+@@ -90,11 +83,11 @@ static char eeprom_set_mac_addr(struct adapter *adapter, char type, u8 *mac)
+
+ if (eeprom_write(adapter, 0x3f8, tmp, 8) == 8)
+ return 1;
+-
+ return 0;
+ }
+
+-static int flexcop_eeprom_read(struct flexcop_device *fc, u16 addr, u8 *buf, u16 len)
++static int flexcop_eeprom_read(struct flexcop_device *fc,
++ u16 addr, u8 *buf, u16 len)
+ {
+ return fc->i2c_request(fc,FC_READ,FC_I2C_PORT_EEPROM,0x50,addr,buf,len);
+ }
+@@ -110,7 +103,8 @@ static u8 calc_lrc(u8 *buf, int len)
+ return sum;
+ }
+
+-static int flexcop_eeprom_request(struct flexcop_device *fc, flexcop_access_op_t op, u16 addr, u8 *buf, u16 len, int retries)
++static int flexcop_eeprom_request(struct flexcop_device *fc,
++ flexcop_access_op_t op, u16 addr, u8 *buf, u16 len, int retries)
+ {
+ int i,ret = 0;
+ u8 chipaddr = 0x50 | ((addr >> 8) & 3);
+@@ -123,7 +117,8 @@ static int flexcop_eeprom_request(struct flexcop_device *fc, flexcop_access_op_t
+ return ret;
+ }
+
+-static int flexcop_eeprom_lrc_read(struct flexcop_device *fc, u16 addr, u8 *buf, u16 len, int retries)
++static int flexcop_eeprom_lrc_read(struct flexcop_device *fc, u16 addr,
++ u8 *buf, u16 len, int retries)
+ {
+ int ret = flexcop_eeprom_request(fc, FC_READ, addr, buf, len, retries);
+ if (ret == 0)
+@@ -133,8 +128,7 @@ static int flexcop_eeprom_lrc_read(struct flexcop_device *fc, u16 addr, u8 *buf,
+ }
+
+ /* JJ's comment about extended == 1: it is not presently used anywhere but was
+- * added to the low-level functions for possible support of EUI64
+- */
++ * added to the low-level functions for possible support of EUI64 */
+ int flexcop_eeprom_check_mac_addr(struct flexcop_device *fc, int extended)
+ {
+ u8 buf[8];
+@@ -142,12 +136,9 @@ int flexcop_eeprom_check_mac_addr(struct flexcop_device *fc, int extended)
+
+ if ((ret = flexcop_eeprom_lrc_read(fc,0x3f8,buf,8,4)) == 0) {
+ if (extended != 0) {
+- err("TODO: extended (EUI64) MAC addresses aren't completely supported yet");
++ err("TODO: extended (EUI64) MAC addresses aren't "
++ "completely supported yet");
+ ret = -EINVAL;
+-/* memcpy(fc->dvb_adapter.proposed_mac,buf,3);
+- mac[3] = 0xfe;
+- mac[4] = 0xff;
+- memcpy(&fc->dvb_adapter.proposed_mac[3],&buf[5],3); */
+ } else
+ memcpy(fc->dvb_adapter.proposed_mac,buf,6);
+ }
+diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
+index 5cded37..f7afab5 100644
+--- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
++++ b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
+@@ -592,14 +592,14 @@ int flexcop_frontend_init(struct flexcop_device *fc)
+ fc->fe_sleep = ops->sleep;
+ ops->sleep = flexcop_sleep;
+
+- fc->dev_type = FC_SKY;
++ fc->dev_type = FC_SKY_REV26;
+ goto fe_found;
+ }
+
+ /* try the air dvb-t (mt352/Samsung tdtc9251dh0(??)) */
+ fc->fe = dvb_attach(mt352_attach, &samsung_tdtc9251dh0_config, i2c);
+ if (fc->fe != NULL) {
+- fc->dev_type = FC_AIR_DVB;
++ fc->dev_type = FC_AIR_DVBT;
+ fc->fe->ops.tuner_ops.calc_regs = samsung_tdtc9251dh0_calc_regs;
+ goto fe_found;
+ }
+@@ -653,7 +653,7 @@ int flexcop_frontend_init(struct flexcop_device *fc)
+ fc->fe_sleep = ops->sleep;
+ ops->sleep = flexcop_sleep;
+
+- fc->dev_type = FC_SKY_OLD;
++ fc->dev_type = FC_SKY_REV23;
+ goto fe_found;
+ }
+
+diff --git a/drivers/media/dvb/b2c2/flexcop-hw-filter.c b/drivers/media/dvb/b2c2/flexcop-hw-filter.c
+index 451974b..77e4547 100644
+--- a/drivers/media/dvb/b2c2/flexcop-hw-filter.c
++++ b/drivers/media/dvb/b2c2/flexcop-hw-filter.c
+@@ -1,33 +1,30 @@
+ /*
+- * This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
+- *
+- * flexcop-hw-filter.c - pid and mac address filtering and corresponding control functions.
+- *
+- * see flexcop.c for copyright information.
++ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
++ * flexcop-hw-filter.c - pid and mac address filtering and control functions
++ * see flexcop.c for copyright information
+ */
+ #include "flexcop.h"
+
+ static void flexcop_rcv_data_ctrl(struct flexcop_device *fc, int onoff)
+ {
+- flexcop_set_ibi_value(ctrl_208,Rcv_Data_sig,onoff);
+-
+- deb_ts("rcv_data is now: '%s'\n",onoff ? "on" : "off");
++ flexcop_set_ibi_value(ctrl_208, Rcv_Data_sig, onoff);
++ deb_ts("rcv_data is now: '%s'\n", onoff ? "on" : "off");
+ }
+
+ void flexcop_smc_ctrl(struct flexcop_device *fc, int onoff)
+ {
+- flexcop_set_ibi_value(ctrl_208,SMC_Enable_sig,onoff);
++ flexcop_set_ibi_value(ctrl_208, SMC_Enable_sig, onoff);
+ }
+
+ static void flexcop_null_filter_ctrl(struct flexcop_device *fc, int onoff)
+ {
+- flexcop_set_ibi_value(ctrl_208,Null_filter_sig,onoff);
++ flexcop_set_ibi_value(ctrl_208, Null_filter_sig, onoff);
+ }
+
+ void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6])
+ {
+- flexcop_ibi_value v418,v41c;
+- v41c = fc->read_ibi_reg(fc,mac_address_41c);
++ flexcop_ibi_value v418, v41c;
++ v41c = fc->read_ibi_reg(fc, mac_address_41c);
+
+ v418.mac_address_418.MAC1 = mac[0];
+ v418.mac_address_418.MAC2 = mac[1];
+@@ -36,27 +33,28 @@ void flexcop_set_mac_filter(struct flexcop_device *fc, u8 mac[6])
+ v41c.mac_address_41c.MAC7 = mac[4];
+ v41c.mac_address_41c.MAC8 = mac[5];
+
+- fc->write_ibi_reg(fc,mac_address_418,v418);
+- fc->write_ibi_reg(fc,mac_address_41c,v41c);
++ fc->write_ibi_reg(fc, mac_address_418, v418);
++ fc->write_ibi_reg(fc, mac_address_41c, v41c);
+ }
+
+ void flexcop_mac_filter_ctrl(struct flexcop_device *fc, int onoff)
+ {
+- flexcop_set_ibi_value(ctrl_208,MAC_filter_Mode_sig,onoff);
++ flexcop_set_ibi_value(ctrl_208, MAC_filter_Mode_sig, onoff);
+ }
+
+-static void flexcop_pid_group_filter(struct flexcop_device *fc, u16 pid, u16 mask)
++static void flexcop_pid_group_filter(struct flexcop_device *fc,
++ u16 pid, u16 mask)
+ {
+ /* index_reg_310.extra_index_reg need to 0 or 7 to work */
+ flexcop_ibi_value v30c;
+ v30c.pid_filter_30c_ext_ind_0_7.Group_PID = pid;
+ v30c.pid_filter_30c_ext_ind_0_7.Group_mask = mask;
+- fc->write_ibi_reg(fc,pid_filter_30c,v30c);
++ fc->write_ibi_reg(fc, pid_filter_30c, v30c);
+ }
+
+ static void flexcop_pid_group_filter_ctrl(struct flexcop_device *fc, int onoff)
+ {
+- flexcop_set_ibi_value(ctrl_208,Mask_filter_sig,onoff);
++ flexcop_set_ibi_value(ctrl_208, Mask_filter_sig, onoff);
+ }
+
+ /* this fancy define reduces the code size of the quite similar PID controlling of
+@@ -65,91 +63,112 @@ static void flexcop_pid_group_filter_ctrl(struct flexcop_device *fc, int onoff)
+
+ #define pid_ctrl(vregname,field,enablefield,trans_field,transval) \
+ flexcop_ibi_value vpid = fc->read_ibi_reg(fc, vregname), \
+- v208 = fc->read_ibi_reg(fc, ctrl_208); \
+-\
+- vpid.vregname.field = onoff ? pid : 0x1fff; \
+- vpid.vregname.trans_field = transval; \
+- v208.ctrl_208.enablefield = onoff; \
+-\
+- fc->write_ibi_reg(fc,vregname,vpid); \
+- fc->write_ibi_reg(fc,ctrl_208,v208);
++v208 = fc->read_ibi_reg(fc, ctrl_208); \
++vpid.vregname.field = onoff ? pid : 0x1fff; \
++vpid.vregname.trans_field = transval; \
++v208.ctrl_208.enablefield = onoff; \
++fc->write_ibi_reg(fc, vregname, vpid); \
++fc->write_ibi_reg(fc, ctrl_208, v208);
+
+-static void flexcop_pid_Stream1_PID_ctrl(struct flexcop_device *fc, u16 pid, int onoff)
++static void flexcop_pid_Stream1_PID_ctrl(struct flexcop_device *fc,
++ u16 pid, int onoff)
+ {
+- pid_ctrl(pid_filter_300,Stream1_PID,Stream1_filter_sig,Stream1_trans,0);
++ pid_ctrl(pid_filter_300, Stream1_PID, Stream1_filter_sig,
++ Stream1_trans, 0);
+ }
+
+-static void flexcop_pid_Stream2_PID_ctrl(struct flexcop_device *fc, u16 pid, int onoff)
++static void flexcop_pid_Stream2_PID_ctrl(struct flexcop_device *fc,
++ u16 pid, int onoff)
+ {
+- pid_ctrl(pid_filter_300,Stream2_PID,Stream2_filter_sig,Stream2_trans,0);
++ pid_ctrl(pid_filter_300, Stream2_PID, Stream2_filter_sig,
++ Stream2_trans, 0);
+ }
+
+-static void flexcop_pid_PCR_PID_ctrl(struct flexcop_device *fc, u16 pid, int onoff)
++static void flexcop_pid_PCR_PID_ctrl(struct flexcop_device *fc,
++ u16 pid, int onoff)
+ {
+- pid_ctrl(pid_filter_304,PCR_PID,PCR_filter_sig,PCR_trans,0);
++ pid_ctrl(pid_filter_304, PCR_PID, PCR_filter_sig, PCR_trans, 0);
+ }
+
+-static void flexcop_pid_PMT_PID_ctrl(struct flexcop_device *fc, u16 pid, int onoff)
++static void flexcop_pid_PMT_PID_ctrl(struct flexcop_device *fc,
++ u16 pid, int onoff)
+ {
+- pid_ctrl(pid_filter_304,PMT_PID,PMT_filter_sig,PMT_trans,0);
++ pid_ctrl(pid_filter_304, PMT_PID, PMT_filter_sig, PMT_trans, 0);
+ }
+
+-static void flexcop_pid_EMM_PID_ctrl(struct flexcop_device *fc, u16 pid, int onoff)
++static void flexcop_pid_EMM_PID_ctrl(struct flexcop_device *fc,
++ u16 pid, int onoff)
+ {
+- pid_ctrl(pid_filter_308,EMM_PID,EMM_filter_sig,EMM_trans,0);
++ pid_ctrl(pid_filter_308, EMM_PID, EMM_filter_sig, EMM_trans, 0);
+ }
+
+-static void flexcop_pid_ECM_PID_ctrl(struct flexcop_device *fc, u16 pid, int onoff)
++static void flexcop_pid_ECM_PID_ctrl(struct flexcop_device *fc,
++ u16 pid, int onoff)
+ {
+- pid_ctrl(pid_filter_308,ECM_PID,ECM_filter_sig,ECM_trans,0);
++ pid_ctrl(pid_filter_308, ECM_PID, ECM_filter_sig, ECM_trans, 0);
+ }
+
+-static void flexcop_pid_control(struct flexcop_device *fc, int index, u16 pid,int onoff)
++static void flexcop_pid_control(struct flexcop_device *fc,
++ int index, u16 pid, int onoff)
+ {
+ if (pid == 0x2000)
+ return;
+
+- deb_ts("setting pid: %5d %04x at index %d '%s'\n",pid,pid,index,onoff ? "on" : "off");
++ deb_ts("setting pid: %5d %04x at index %d '%s'\n",
++ pid, pid, index, onoff ? "on" : "off");
+
+ /* We could use bit magic here to reduce source code size.
+ * I decided against it, but to use the real register names */
+ switch (index) {
+- case 0: flexcop_pid_Stream1_PID_ctrl(fc,pid,onoff); break;
+- case 1: flexcop_pid_Stream2_PID_ctrl(fc,pid,onoff); break;
+- case 2: flexcop_pid_PCR_PID_ctrl(fc,pid,onoff); break;
+- case 3: flexcop_pid_PMT_PID_ctrl(fc,pid,onoff); break;
+- case 4: flexcop_pid_EMM_PID_ctrl(fc,pid,onoff); break;
+- case 5: flexcop_pid_ECM_PID_ctrl(fc,pid,onoff); break;
+- default:
+- if (fc->has_32_hw_pid_filter && index < 38) {
+- flexcop_ibi_value vpid,vid;
+-
+- /* set the index */
+- vid = fc->read_ibi_reg(fc,index_reg_310);
+- vid.index_reg_310.index_reg = index - 6;
+- fc->write_ibi_reg(fc,index_reg_310, vid);
+-
+- vpid = fc->read_ibi_reg(fc,pid_n_reg_314);
+- vpid.pid_n_reg_314.PID = onoff ? pid : 0x1fff;
+- vpid.pid_n_reg_314.PID_enable_bit = onoff;
+- fc->write_ibi_reg(fc,pid_n_reg_314, vpid);
+- }
+- break;
++ case 0:
++ flexcop_pid_Stream1_PID_ctrl(fc, pid, onoff);
++ break;
++ case 1:
++ flexcop_pid_Stream2_PID_ctrl(fc, pid, onoff);
++ break;
++ case 2:
++ flexcop_pid_PCR_PID_ctrl(fc, pid, onoff);
++ break;
++ case 3:
++ flexcop_pid_PMT_PID_ctrl(fc, pid, onoff);
++ break;
++ case 4:
++ flexcop_pid_EMM_PID_ctrl(fc, pid, onoff);
++ break;
++ case 5:
++ flexcop_pid_ECM_PID_ctrl(fc, pid, onoff);
++ break;
++ default:
++ if (fc->has_32_hw_pid_filter && index < 38) {
++ flexcop_ibi_value vpid, vid;
++
++ /* set the index */
++ vid = fc->read_ibi_reg(fc, index_reg_310);
++ vid.index_reg_310.index_reg = index - 6;
++ fc->write_ibi_reg(fc, index_reg_310, vid);
++
++ vpid = fc->read_ibi_reg(fc, pid_n_reg_314);
++ vpid.pid_n_reg_314.PID = onoff ? pid : 0x1fff;
++ vpid.pid_n_reg_314.PID_enable_bit = onoff;
++ fc->write_ibi_reg(fc, pid_n_reg_314, vpid);
++ }
++ break;
+ }
+ }
+
+-static int flexcop_toggle_fullts_streaming(struct flexcop_device *fc,int onoff)
++static int flexcop_toggle_fullts_streaming(struct flexcop_device *fc, int onoff)
+ {
+ if (fc->fullts_streaming_state != onoff) {
+ deb_ts("%s full TS transfer\n",onoff ? "enabling" : "disabling");
+ flexcop_pid_group_filter(fc, 0, 0x1fe0 * (!onoff));
+- flexcop_pid_group_filter_ctrl(fc,onoff);
++ flexcop_pid_group_filter_ctrl(fc, onoff);
+ fc->fullts_streaming_state = onoff;
+ }
+ return 0;
+ }
+
+-int flexcop_pid_feed_control(struct flexcop_device *fc, struct dvb_demux_feed *dvbdmxfeed, int onoff)
++int flexcop_pid_feed_control(struct flexcop_device *fc,
++ struct dvb_demux_feed *dvbdmxfeed, int onoff)
+ {
+ int max_pid_filter = 6 + fc->has_32_hw_pid_filter*32;
+
+@@ -164,24 +183,25 @@ int flexcop_pid_feed_control(struct flexcop_device *fc, struct dvb_demux_feed *d
+ * - or the requested pid is 0x2000 */
+
+ if (!fc->pid_filtering && fc->feedcount == onoff)
+- flexcop_toggle_fullts_streaming(fc,onoff);
++ flexcop_toggle_fullts_streaming(fc, onoff);
+
+ if (fc->pid_filtering) {
+- flexcop_pid_control(fc,dvbdmxfeed->index,dvbdmxfeed->pid,onoff);
++ flexcop_pid_control \
++ (fc, dvbdmxfeed->index, dvbdmxfeed->pid, onoff);
+
+ if (fc->extra_feedcount > 0)
+- flexcop_toggle_fullts_streaming(fc,1);
++ flexcop_toggle_fullts_streaming(fc, 1);
+ else if (dvbdmxfeed->pid == 0x2000)
+- flexcop_toggle_fullts_streaming(fc,onoff);
++ flexcop_toggle_fullts_streaming(fc, onoff);
+ else
+- flexcop_toggle_fullts_streaming(fc,0);
++ flexcop_toggle_fullts_streaming(fc, 0);
+ }
+
+ /* if it was the first or last feed request change the stream-status */
+ if (fc->feedcount == onoff) {
+- flexcop_rcv_data_ctrl(fc,onoff);
++ flexcop_rcv_data_ctrl(fc, onoff);
+ if (fc->stream_control) /* device specific stream control */
+- fc->stream_control(fc,onoff);
++ fc->stream_control(fc, onoff);
+
+ /* feeding stopped -> reset the flexcop filter*/
+ if (onoff == 0) {
+@@ -189,7 +209,6 @@ int flexcop_pid_feed_control(struct flexcop_device *fc, struct dvb_demux_feed *d
+ flexcop_hw_filter_init(fc);
+ }
+ }
+-
+ return 0;
+ }
+ EXPORT_SYMBOL(flexcop_pid_feed_control);
+@@ -199,15 +218,15 @@ void flexcop_hw_filter_init(struct flexcop_device *fc)
+ int i;
+ flexcop_ibi_value v;
+ for (i = 0; i < 6 + 32*fc->has_32_hw_pid_filter; i++)
+- flexcop_pid_control(fc,i,0x1fff,0);
++ flexcop_pid_control(fc, i, 0x1fff, 0);
+
+ flexcop_pid_group_filter(fc, 0, 0x1fe0);
+- flexcop_pid_group_filter_ctrl(fc,0);
++ flexcop_pid_group_filter_ctrl(fc, 0);
+
+- v = fc->read_ibi_reg(fc,pid_filter_308);
++ v = fc->read_ibi_reg(fc, pid_filter_308);
+ v.pid_filter_308.EMM_filter_4 = 1;
+ v.pid_filter_308.EMM_filter_6 = 0;
+- fc->write_ibi_reg(fc,pid_filter_308,v);
++ fc->write_ibi_reg(fc, pid_filter_308, v);
+
+ flexcop_null_filter_ctrl(fc, 1);
+ }
+diff --git a/drivers/media/dvb/b2c2/flexcop-i2c.c b/drivers/media/dvb/b2c2/flexcop-i2c.c
+index f13783f..e2bed50 100644
+--- a/drivers/media/dvb/b2c2/flexcop-i2c.c
++++ b/drivers/media/dvb/b2c2/flexcop-i2c.c
+@@ -1,17 +1,14 @@
+ /*
+- * This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
+- *
++ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * flexcop-i2c.c - flexcop internal 2Wire bus (I2C) and dvb i2c initialization
+- *
+- * see flexcop.c for copyright information.
++ * see flexcop.c for copyright information
+ */
+ #include "flexcop.h"
+
+ #define FC_MAX_I2C_RETRIES 100000
+
+-/* #define DUMP_I2C_MESSAGES */
+-
+-static int flexcop_i2c_operation(struct flexcop_device *fc, flexcop_ibi_value *r100)
++static int flexcop_i2c_operation(struct flexcop_device *fc,
++ flexcop_ibi_value *r100)
+ {
+ int i;
+ flexcop_ibi_value r;
+@@ -26,7 +23,7 @@ static int flexcop_i2c_operation(struct flexcop_device *fc, flexcop_ibi_value *r
+ r = fc->read_ibi_reg(fc, tw_sm_c_100);
+
+ if (!r.tw_sm_c_100.no_base_addr_ack_error) {
+- if (r.tw_sm_c_100.st_done) { /* && !r.tw_sm_c_100.working_start */
++ if (r.tw_sm_c_100.st_done) {
+ *r100 = r;
+ deb_i2c("i2c success\n");
+ return 0;
+@@ -36,17 +33,31 @@ static int flexcop_i2c_operation(struct flexcop_device *fc, flexcop_ibi_value *r
+ return -EREMOTEIO;
+ }
+ }
+- deb_i2c("tried %d times i2c operation, never finished or too many ack errors.\n",i);
++ deb_i2c("tried %d times i2c operation, "
++ "never finished or too many ack errors.\n", i);
+ return -EREMOTEIO;
+ }
+
+ static int flexcop_i2c_read4(struct flexcop_i2c_adapter *i2c,
+- flexcop_ibi_value r100, u8 *buf)
++ flexcop_ibi_value r100, u8 *buf)
+ {
+ flexcop_ibi_value r104;
+- int len = r100.tw_sm_c_100.total_bytes, /* remember total_bytes is buflen-1 */
++ int len = r100.tw_sm_c_100.total_bytes,
++ /* remember total_bytes is buflen-1 */
+ ret;
+
++ /* work-around to have CableStar2 and SkyStar2 rev 2.7 work
++ * correctly:
++ *
++ * the ITD1000 is behind an i2c-gate which closes automatically
++ * after an i2c-transaction the STV0297 needs 2 consecutive reads
++ * one with no_base_addr = 0 and one with 1
++ *
++ * those two work-arounds are conflictin: we check for the card
++ * type, it is set when probing the ITD1000 */
++ if (i2c->fc->dev_type == FC_SKY_REV27)
++ r100.tw_sm_c_100.no_base_addr_ack_error = i2c->no_base_addr;
++
+ ret = flexcop_i2c_operation(i2c->fc, &r100);
+ if (ret != 0) {
+ deb_i2c("Retrying operation\n");
+@@ -69,11 +80,11 @@ static int flexcop_i2c_read4(struct flexcop_i2c_adapter *i2c,
+ if (len > 1) buf[2] = r104.tw_sm_c_104.data3_reg;
+ if (len > 2) buf[3] = r104.tw_sm_c_104.data4_reg;
+ }
+-
+ return 0;
+ }
+
+-static int flexcop_i2c_write4(struct flexcop_device *fc, flexcop_ibi_value r100, u8 *buf)
++static int flexcop_i2c_write4(struct flexcop_device *fc,
++ flexcop_ibi_value r100, u8 *buf)
+ {
+ flexcop_ibi_value r104;
+ int len = r100.tw_sm_c_100.total_bytes; /* remember total_bytes is buflen-1 */
+@@ -81,7 +92,6 @@ static int flexcop_i2c_write4(struct flexcop_device *fc, flexcop_ibi_value r100,
+
+ /* there is at least one byte, otherwise we wouldn't be here */
+ r100.tw_sm_c_100.data1_reg = buf[0];
+-
+ r104.tw_sm_c_104.data2_reg = len > 0 ? buf[1] : 0;
+ r104.tw_sm_c_104.data3_reg = len > 1 ? buf[2] : 0;
+ r104.tw_sm_c_104.data4_reg = len > 2 ? buf[3] : 0;
+@@ -94,7 +104,7 @@ static int flexcop_i2c_write4(struct flexcop_device *fc, flexcop_ibi_value r100,
+ }
+
+ int flexcop_i2c_request(struct flexcop_i2c_adapter *i2c,
+- flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len)
++ flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len)
+ {
+ int ret;
+
+@@ -117,7 +127,6 @@ int flexcop_i2c_request(struct flexcop_i2c_adapter *i2c,
+ printk("rd(");
+ else
+ printk("wr(");
+-
+ printk("%02x): %02x ", chipaddr, addr);
+ #endif
+
+@@ -163,7 +172,8 @@ int flexcop_i2c_request(struct flexcop_i2c_adapter *i2c,
+ EXPORT_SYMBOL(flexcop_i2c_request);
+
+ /* master xfer callback for demodulator */
+-static int flexcop_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
++static int flexcop_master_xfer(struct i2c_adapter *i2c_adap,
++ struct i2c_msg msgs[], int num)
+ {
+ struct flexcop_i2c_adapter *i2c = i2c_get_adapdata(i2c_adap);
+ int i, ret = 0;
+@@ -182,12 +192,13 @@ static int flexcop_master_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs
+ /* reading */
+ if (i+1 < num && (msgs[i+1].flags == I2C_M_RD)) {
+ ret = i2c->fc->i2c_request(i2c, FC_READ, msgs[i].addr,
+- msgs[i].buf[0], msgs[i+1].buf, msgs[i+1].len);
++ msgs[i].buf[0], msgs[i+1].buf,
++ msgs[i+1].len);
+ i++; /* skip the following message */
+ } else /* writing */
+ ret = i2c->fc->i2c_request(i2c, FC_WRITE, msgs[i].addr,
+- msgs[i].buf[0], &msgs[i].buf[1],
+- msgs[i].len - 1);
++ msgs[i].buf[0], &msgs[i].buf[1],
++ msgs[i].len - 1);
+ if (ret < 0) {
+ err("i2c master_xfer failed");
+ break;
+@@ -214,23 +225,21 @@ static struct i2c_algorithm flexcop_algo = {
+ int flexcop_i2c_init(struct flexcop_device *fc)
+ {
+ int ret;
+-
+ mutex_init(&fc->i2c_mutex);
+
+ fc->fc_i2c_adap[0].fc = fc;
+ fc->fc_i2c_adap[1].fc = fc;
+ fc->fc_i2c_adap[2].fc = fc;
+-
+ fc->fc_i2c_adap[0].port = FC_I2C_PORT_DEMOD;
+ fc->fc_i2c_adap[1].port = FC_I2C_PORT_EEPROM;
+ fc->fc_i2c_adap[2].port = FC_I2C_PORT_TUNER;
+
+ strlcpy(fc->fc_i2c_adap[0].i2c_adap.name, "B2C2 FlexCop I2C to demod",
+- sizeof(fc->fc_i2c_adap[0].i2c_adap.name));
++ sizeof(fc->fc_i2c_adap[0].i2c_adap.name));
+ strlcpy(fc->fc_i2c_adap[1].i2c_adap.name, "B2C2 FlexCop I2C to eeprom",
+- sizeof(fc->fc_i2c_adap[1].i2c_adap.name));
++ sizeof(fc->fc_i2c_adap[1].i2c_adap.name));
+ strlcpy(fc->fc_i2c_adap[2].i2c_adap.name, "B2C2 FlexCop I2C to tuner",
+- sizeof(fc->fc_i2c_adap[2].i2c_adap.name));
++ sizeof(fc->fc_i2c_adap[2].i2c_adap.name));
+
+ i2c_set_adapdata(&fc->fc_i2c_adap[0].i2c_adap, &fc->fc_i2c_adap[0]);
+ i2c_set_adapdata(&fc->fc_i2c_adap[1].i2c_adap, &fc->fc_i2c_adap[1]);
+@@ -268,7 +277,6 @@ adap_2_failed:
+ i2c_del_adapter(&fc->fc_i2c_adap[1].i2c_adap);
+ adap_1_failed:
+ i2c_del_adapter(&fc->fc_i2c_adap[0].i2c_adap);
+-
+ return ret;
+ }
+
+@@ -279,6 +287,5 @@ void flexcop_i2c_exit(struct flexcop_device *fc)
+ i2c_del_adapter(&fc->fc_i2c_adap[1].i2c_adap);
+ i2c_del_adapter(&fc->fc_i2c_adap[0].i2c_adap);
+ }
+-
+ fc->init_state &= ~FC_STATE_I2C_INIT;
+ }
+diff --git a/drivers/media/dvb/b2c2/flexcop-misc.c b/drivers/media/dvb/b2c2/flexcop-misc.c
+index 93d20e5..e56627d 100644
+--- a/drivers/media/dvb/b2c2/flexcop-misc.c
++++ b/drivers/media/dvb/b2c2/flexcop-misc.c
+@@ -1,9 +1,7 @@
+ /*
+- * This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
+- *
+- * flexcop-misc.c - miscellaneous functions.
+- *
+- * see flexcop.c for copyright information.
++ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
++ * flexcop-misc.c - miscellaneous functions
++ * see flexcop.c for copyright information
+ */
+ #include "flexcop.h"
+
+@@ -12,39 +10,43 @@ void flexcop_determine_revision(struct flexcop_device *fc)
+ flexcop_ibi_value v = fc->read_ibi_reg(fc,misc_204);
+
+ switch (v.misc_204.Rev_N_sig_revision_hi) {
+- case 0x2:
+- deb_info("found a FlexCopII.\n");
+- fc->rev = FLEXCOP_II;
+- break;
+- case 0x3:
+- deb_info("found a FlexCopIIb.\n");
+- fc->rev = FLEXCOP_IIB;
+- break;
+- case 0x0:
+- deb_info("found a FlexCopIII.\n");
+- fc->rev = FLEXCOP_III;
+- break;
+- default:
+- err("unkown FlexCop Revision: %x. Please report the linux-dvb@linuxtv.org.",v.misc_204.Rev_N_sig_revision_hi);
+- break;
++ case 0x2:
++ deb_info("found a FlexCopII.\n");
++ fc->rev = FLEXCOP_II;
++ break;
++ case 0x3:
++ deb_info("found a FlexCopIIb.\n");
++ fc->rev = FLEXCOP_IIB;
++ break;
++ case 0x0:
++ deb_info("found a FlexCopIII.\n");
++ fc->rev = FLEXCOP_III;
++ break;
++ default:
++ err("unknown FlexCop Revision: %x. Please report this to "
++ "linux-dvb@linuxtv.org.",
++ v.misc_204.Rev_N_sig_revision_hi);
++ break;
+ }
+
+ if ((fc->has_32_hw_pid_filter = v.misc_204.Rev_N_sig_caps))
+- deb_info("this FlexCop has the additional 32 hardware pid filter.\n");
++ deb_info("this FlexCop has "
++ "the additional 32 hardware pid filter.\n");
+ else
+- deb_info("this FlexCop has only the 6 basic main hardware pid filter.\n");
++ deb_info("this FlexCop has "
++ "the 6 basic main hardware pid filter.\n");
+ /* bus parts have to decide if hw pid filtering is used or not. */
+ }
+
+ static const char *flexcop_revision_names[] = {
+- "Unkown chip",
++ "Unknown chip",
+ "FlexCopII",
+ "FlexCopIIb",
+ "FlexCopIII",
+ };
+
+ static const char *flexcop_device_names[] = {
+- "Unkown device",
++ "Unknown device",
+ "Air2PC/AirStar 2 DVB-T",
+ "Air2PC/AirStar 2 ATSC 1st generation",
+ "Air2PC/AirStar 2 ATSC 2nd generation",
+@@ -61,21 +63,23 @@ static const char *flexcop_bus_names[] = {
+ "PCI",
+ };
+
+-void flexcop_device_name(struct flexcop_device *fc,const char *prefix,const
+- char *suffix)
++void flexcop_device_name(struct flexcop_device *fc,
++ const char *prefix, const char *suffix)
+ {
+- info("%s '%s' at the '%s' bus controlled by a '%s' %s",prefix,
+- flexcop_device_names[fc->dev_type],flexcop_bus_names[fc->bus_type],
+- flexcop_revision_names[fc->rev],suffix);
++ info("%s '%s' at the '%s' bus controlled by a '%s' %s",
++ prefix, flexcop_device_names[fc->dev_type],
++ flexcop_bus_names[fc->bus_type],
++ flexcop_revision_names[fc->rev], suffix);
+ }
+
+-void flexcop_dump_reg(struct flexcop_device *fc, flexcop_ibi_register reg, int num)
++void flexcop_dump_reg(struct flexcop_device *fc,
++ flexcop_ibi_register reg, int num)
+ {
+ flexcop_ibi_value v;
+ int i;
+ for (i = 0; i < num; i++) {
+- v = fc->read_ibi_reg(fc,reg+4*i);
+- deb_rdump("0x%03x: %08x, ",reg+4*i, v.raw);
++ v = fc->read_ibi_reg(fc, reg+4*i);
++ deb_rdump("0x%03x: %08x, ", reg+4*i, v.raw);
+ }
+ deb_rdump("\n");
+ }
+diff --git a/drivers/media/dvb/b2c2/flexcop-pci.c b/drivers/media/dvb/b2c2/flexcop-pci.c
+index 76e37fd..227c020 100644
+--- a/drivers/media/dvb/b2c2/flexcop-pci.c
++++ b/drivers/media/dvb/b2c2/flexcop-pci.c
+@@ -1,9 +1,7 @@
+ /*
+- * This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
+- *
+- * flexcop-pci.c - covers the PCI part including DMA transfers.
+- *
+- * see flexcop.c for copyright information.
++ * Linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
++ * flexcop-pci.c - covers the PCI part including DMA transfers
++ * see flexcop.c for copyright information
+ */
+
+ #define FC_LOG_PREFIX "flexcop-pci"
+@@ -11,7 +9,8 @@
+
+ static int enable_pid_filtering = 1;
+ module_param(enable_pid_filtering, int, 0444);
+-MODULE_PARM_DESC(enable_pid_filtering, "enable hardware pid filtering: supported values: 0 (fullts), 1");
++MODULE_PARM_DESC(enable_pid_filtering,
++ "enable hardware pid filtering: supported values: 0 (fullts), 1");
+
+ static int irq_chk_intv = 100;
+ module_param(irq_chk_intv, int, 0644);
+@@ -26,17 +25,17 @@ MODULE_PARM_DESC(irq_chk_intv, "set the interval for IRQ streaming watchdog.");
+ #define DEBSTATUS " (debugging is not enabled)"
+ #endif
+
+-#define deb_info(args...) dprintk(0x01,args)
+-#define deb_reg(args...) dprintk(0x02,args)
+-#define deb_ts(args...) dprintk(0x04,args)
+-#define deb_irq(args...) dprintk(0x08,args)
+-#define deb_chk(args...) dprintk(0x10,args)
++#define deb_info(args...) dprintk(0x01, args)
++#define deb_reg(args...) dprintk(0x02, args)
++#define deb_ts(args...) dprintk(0x04, args)
++#define deb_irq(args...) dprintk(0x08, args)
++#define deb_chk(args...) dprintk(0x10, args)
+
+ static int debug;
+ module_param(debug, int, 0644);
+ MODULE_PARM_DESC(debug,
+ "set debug level (1=info,2=regs,4=TS,8=irqdma,16=check (|-able))."
+- DEBSTATUS);
++ DEBSTATUS);
+
+ #define DRIVER_VERSION "0.1"
+ #define DRIVER_NAME "Technisat/B2C2 FlexCop II/IIb/III Digital TV PCI Driver"
+@@ -51,30 +50,30 @@ struct flexcop_pci {
+
+ void __iomem *io_mem;
+ u32 irq;
+-/* buffersize (at least for DMA1, need to be % 188 == 0,
+- * this logic is required */
++ /* buffersize (at least for DMA1, need to be % 188 == 0,
++ * this logic is required */
+ #define FC_DEFAULT_DMA1_BUFSIZE (1280 * 188)
+ #define FC_DEFAULT_DMA2_BUFSIZE (10 * 188)
+ struct flexcop_dma dma[2];
+
+ int active_dma1_addr; /* 0 = addr0 of dma1; 1 = addr1 of dma1 */
+- u32 last_dma1_cur_pos; /* position of the pointer last time the timer/packet irq occured */
++ u32 last_dma1_cur_pos;
++ /* position of the pointer last time the timer/packet irq occured */
+ int count;
+ int count_prev;
+ int stream_problem;
+
+ spinlock_t irq_lock;
+-
+ unsigned long last_irq;
+
+ struct delayed_work irq_check_work;
+-
+ struct flexcop_device *fc_dev;
+ };
+
+-static int lastwreg,lastwval,lastrreg,lastrval;
++static int lastwreg, lastwval, lastrreg, lastrval;
+
+-static flexcop_ibi_value flexcop_pci_read_ibi_reg (struct flexcop_device *fc, flexcop_ibi_register r)
++static flexcop_ibi_value flexcop_pci_read_ibi_reg(struct flexcop_device *fc,
++ flexcop_ibi_register r)
+ {
+ struct flexcop_pci *fc_pci = fc->bus_specific;
+ flexcop_ibi_value v;
+@@ -82,19 +81,20 @@ static flexcop_ibi_value flexcop_pci_read_ibi_reg (struct flexcop_device *fc, fl
+
+ if (lastrreg != r || lastrval != v.raw) {
+ lastrreg = r; lastrval = v.raw;
+- deb_reg("new rd: %3x: %08x\n",r,v.raw);
++ deb_reg("new rd: %3x: %08x\n", r, v.raw);
+ }
+
+ return v;
+ }
+
+-static int flexcop_pci_write_ibi_reg(struct flexcop_device *fc, flexcop_ibi_register r, flexcop_ibi_value v)
++static int flexcop_pci_write_ibi_reg(struct flexcop_device *fc,
++ flexcop_ibi_register r, flexcop_ibi_value v)
+ {
+ struct flexcop_pci *fc_pci = fc->bus_specific;
+
+ if (lastwreg != r || lastwval != v.raw) {
+ lastwreg = r; lastwval = v.raw;
+- deb_reg("new wr: %3x: %08x\n",r,v.raw);
++ deb_reg("new wr: %3x: %08x\n", r, v.raw);
+ }
+
+ writel(v.raw, fc_pci->io_mem + r);
+@@ -113,15 +113,16 @@ static void flexcop_pci_irq_check_work(struct work_struct *work)
+ deb_chk("no IRQ since the last check\n");
+ if (fc_pci->stream_problem++ == 3) {
+ struct dvb_demux_feed *feed;
++ deb_info("flexcop-pci: stream problem, resetting pid filter\n");
+
+ spin_lock_irq(&fc->demux.lock);
+ list_for_each_entry(feed, &fc->demux.feed_list,
+- list_head) {
++ list_head) {
+ flexcop_pid_feed_control(fc, feed, 0);
+ }
+
+ list_for_each_entry(feed, &fc->demux.feed_list,
+- list_head) {
++ list_head) {
+ flexcop_pid_feed_control(fc, feed, 1);
+ }
+ spin_unlock_irq(&fc->demux.lock);
+@@ -149,11 +150,10 @@ static irqreturn_t flexcop_pci_isr(int irq, void *dev_id)
+ flexcop_ibi_value v;
+ irqreturn_t ret = IRQ_HANDLED;
+
+- spin_lock_irqsave(&fc_pci->irq_lock,flags);
+-
+- v = fc->read_ibi_reg(fc,irq_20c);
++ spin_lock_irqsave(&fc_pci->irq_lock, flags);
++ v = fc->read_ibi_reg(fc, irq_20c);
+
+- /* errors */
++ /* errors */
+ if (v.irq_20c.Data_receiver_error)
+ deb_chk("data receiver error\n");
+ if (v.irq_20c.Continuity_error_flag)
+@@ -164,24 +164,29 @@ static irqreturn_t flexcop_pci_isr(int irq, void *dev_id)
+ deb_chk("Transport error\n");
+
+ if ((fc_pci->count % 1000) == 0)
+- deb_chk("%d valid irq took place so far\n",fc_pci->count);
++ deb_chk("%d valid irq took place so far\n", fc_pci->count);
+
+ if (v.irq_20c.DMA1_IRQ_Status == 1) {
+ if (fc_pci->active_dma1_addr == 0)
+- flexcop_pass_dmx_packets(fc_pci->fc_dev,fc_pci->dma[0].cpu_addr0,fc_pci->dma[0].size / 188);
++ flexcop_pass_dmx_packets(fc_pci->fc_dev,
++ fc_pci->dma[0].cpu_addr0,
++ fc_pci->dma[0].size / 188);
+ else
+- flexcop_pass_dmx_packets(fc_pci->fc_dev,fc_pci->dma[0].cpu_addr1,fc_pci->dma[0].size / 188);
++ flexcop_pass_dmx_packets(fc_pci->fc_dev,
++ fc_pci->dma[0].cpu_addr1,
++ fc_pci->dma[0].size / 188);
+
+ deb_irq("page change to page: %d\n",!fc_pci->active_dma1_addr);
+ fc_pci->active_dma1_addr = !fc_pci->active_dma1_addr;
+- } else if (v.irq_20c.DMA1_Timer_Status == 1) {
+ /* for the timer IRQ we only can use buffer dmx feeding, because we don't have
+ * complete TS packets when reading from the DMA memory */
++ } else if (v.irq_20c.DMA1_Timer_Status == 1) {
+ dma_addr_t cur_addr =
+ fc->read_ibi_reg(fc,dma1_008).dma_0x8.dma_cur_addr << 2;
+ u32 cur_pos = cur_addr - fc_pci->dma[0].dma_addr0;
+
+- deb_irq("%u irq: %08x cur_addr: %llx: cur_pos: %08x, last_cur_pos: %08x ",
++ deb_irq("%u irq: %08x cur_addr: %llx: cur_pos: %08x, "
++ "last_cur_pos: %08x ",
+ jiffies_to_usecs(jiffies - fc_pci->last_irq),
+ v.raw, (unsigned long long)cur_addr, cur_pos,
+ fc_pci->last_dma1_cur_pos);
+@@ -191,30 +196,36 @@ static irqreturn_t flexcop_pci_isr(int irq, void *dev_id)
+ * pass the data from last_cur_pos to the buffer end to the demux
+ */
+ if (cur_pos < fc_pci->last_dma1_cur_pos) {
+- deb_irq(" end was reached: passing %d bytes ",(fc_pci->dma[0].size*2 - 1) - fc_pci->last_dma1_cur_pos);
++ deb_irq(" end was reached: passing %d bytes ",
++ (fc_pci->dma[0].size*2 - 1) -
++ fc_pci->last_dma1_cur_pos);
+ flexcop_pass_dmx_data(fc_pci->fc_dev,
+- fc_pci->dma[0].cpu_addr0 + fc_pci->last_dma1_cur_pos,
+- (fc_pci->dma[0].size*2) - fc_pci->last_dma1_cur_pos);
++ fc_pci->dma[0].cpu_addr0 +
++ fc_pci->last_dma1_cur_pos,
++ (fc_pci->dma[0].size*2) -
++ fc_pci->last_dma1_cur_pos);
+ fc_pci->last_dma1_cur_pos = 0;
+ }
+
+ if (cur_pos > fc_pci->last_dma1_cur_pos) {
+- deb_irq(" passing %d bytes ",cur_pos - fc_pci->last_dma1_cur_pos);
++ deb_irq(" passing %d bytes ",
++ cur_pos - fc_pci->last_dma1_cur_pos);
+ flexcop_pass_dmx_data(fc_pci->fc_dev,
+- fc_pci->dma[0].cpu_addr0 + fc_pci->last_dma1_cur_pos,
+- cur_pos - fc_pci->last_dma1_cur_pos);
++ fc_pci->dma[0].cpu_addr0 +
++ fc_pci->last_dma1_cur_pos,
++ cur_pos - fc_pci->last_dma1_cur_pos);
+ }
+ deb_irq("\n");
+
+ fc_pci->last_dma1_cur_pos = cur_pos;
+ fc_pci->count++;
+ } else {
+- deb_irq("isr for flexcop called, apparently without reason (%08x)\n",v.raw);
++ deb_irq("isr for flexcop called, "
++ "apparently without reason (%08x)\n", v.raw);
+ ret = IRQ_NONE;
+ }
+
+- spin_unlock_irqrestore(&fc_pci->irq_lock,flags);
+-
++ spin_unlock_irqrestore(&fc_pci->irq_lock, flags);
+ return ret;
+ }
+
+@@ -222,52 +233,48 @@ static int flexcop_pci_stream_control(struct flexcop_device *fc, int onoff)
+ {
+ struct flexcop_pci *fc_pci = fc->bus_specific;
+ if (onoff) {
+- flexcop_dma_config(fc,&fc_pci->dma[0],FC_DMA_1);
+- flexcop_dma_config(fc,&fc_pci->dma[1],FC_DMA_2);
+-
+- flexcop_dma_config_timer(fc,FC_DMA_1,0);
+-
+- flexcop_dma_xfer_control(fc,FC_DMA_1,FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1,1);
++ flexcop_dma_config(fc, &fc_pci->dma[0], FC_DMA_1);
++ flexcop_dma_config(fc, &fc_pci->dma[1], FC_DMA_2);
++ flexcop_dma_config_timer(fc, FC_DMA_1, 0);
++ flexcop_dma_xfer_control(fc, FC_DMA_1,
++ FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1, 1);
+ deb_irq("DMA xfer enabled\n");
+
+ fc_pci->last_dma1_cur_pos = 0;
+- flexcop_dma_control_timer_irq(fc,FC_DMA_1,1);
++ flexcop_dma_control_timer_irq(fc, FC_DMA_1, 1);
+ deb_irq("IRQ enabled\n");
+-
+ fc_pci->count_prev = fc_pci->count;
+-
+-// fc_pci->active_dma1_addr = 0;
+-// flexcop_dma_control_size_irq(fc,FC_DMA_1,1);
+-
+ } else {
+- flexcop_dma_control_timer_irq(fc,FC_DMA_1,0);
++ flexcop_dma_control_timer_irq(fc, FC_DMA_1, 0);
+ deb_irq("IRQ disabled\n");
+
+-// flexcop_dma_control_size_irq(fc,FC_DMA_1,0);
+-
+- flexcop_dma_xfer_control(fc,FC_DMA_1,FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1,0);
++ flexcop_dma_xfer_control(fc, FC_DMA_1,
++ FC_DMA_SUBADDR_0 | FC_DMA_SUBADDR_1, 0);
+ deb_irq("DMA xfer disabled\n");
+ }
+-
+ return 0;
+ }
+
+ static int flexcop_pci_dma_init(struct flexcop_pci *fc_pci)
+ {
+ int ret;
+- if ((ret = flexcop_dma_allocate(fc_pci->pdev,&fc_pci->dma[0],FC_DEFAULT_DMA1_BUFSIZE)) != 0)
++ ret = flexcop_dma_allocate(fc_pci->pdev, &fc_pci->dma[0],
++ FC_DEFAULT_DMA1_BUFSIZE);
++ if (ret != 0)
+ return ret;
+
+- if ((ret = flexcop_dma_allocate(fc_pci->pdev,&fc_pci->dma[1],FC_DEFAULT_DMA2_BUFSIZE)) != 0) {
++ ret = flexcop_dma_allocate(fc_pci->pdev, &fc_pci->dma[1],
++ FC_DEFAULT_DMA2_BUFSIZE);
++ if (ret != 0) {
+ flexcop_dma_free(&fc_pci->dma[0]);
+ return ret;
+ }
+
+- flexcop_sram_set_dest(fc_pci->fc_dev,FC_SRAM_DEST_MEDIA | FC_SRAM_DEST_NET, FC_SRAM_DEST_TARGET_DMA1);
+- flexcop_sram_set_dest(fc_pci->fc_dev,FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI, FC_SRAM_DEST_TARGET_DMA2);
+-
++ flexcop_sram_set_dest(fc_pci->fc_dev, FC_SRAM_DEST_MEDIA |
++ FC_SRAM_DEST_NET, FC_SRAM_DEST_TARGET_DMA1);
++ flexcop_sram_set_dest(fc_pci->fc_dev, FC_SRAM_DEST_CAO |
++ FC_SRAM_DEST_CAI, FC_SRAM_DEST_TARGET_DMA2);
+ fc_pci->init_state |= FC_PCI_DMA_INIT;
+-
+ return ret;
+ }
+
+@@ -290,12 +297,8 @@ static int flexcop_pci_init(struct flexcop_pci *fc_pci)
+
+ if ((ret = pci_enable_device(fc_pci->pdev)) != 0)
+ return ret;
+-
+ pci_set_master(fc_pci->pdev);
+
+- /* enable interrupts */
+- // pci_write_config_dword(pdev, 0x6c, 0x8000);
+-
+ if ((ret = pci_request_regions(fc_pci->pdev, DRIVER_NAME)) != 0)
+ goto err_pci_disable_device;
+
+@@ -338,8 +341,8 @@ static void flexcop_pci_exit(struct flexcop_pci *fc_pci)
+ fc_pci->init_state &= ~FC_PCI_INIT;
+ }
+
+-
+-static int flexcop_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
++static int flexcop_pci_probe(struct pci_dev *pdev,
++ const struct pci_device_id *ent)
+ {
+ struct flexcop_device *fc;
+ struct flexcop_pci *fc_pci;
+@@ -350,7 +353,7 @@ static int flexcop_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
+ return -ENOMEM;
+ }
+
+-/* general flexcop init */
++ /* general flexcop init */
+ fc_pci = fc->bus_specific;
+ fc_pci->fc_dev = fc;
+
+@@ -358,7 +361,6 @@ static int flexcop_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
+ fc->write_ibi_reg = flexcop_pci_write_ibi_reg;
+ fc->i2c_request = flexcop_i2c_request;
+ fc->get_mac_addr = flexcop_eeprom_check_mac_addr;
+-
+ fc->stream_control = flexcop_pci_stream_control;
+
+ if (enable_pid_filtering)
+@@ -368,29 +370,29 @@ static int flexcop_pci_probe(struct pci_dev *pdev, const struct pci_device_id *e
+
+ fc->pid_filtering = enable_pid_filtering;
+ fc->bus_type = FC_PCI;
+-
+ fc->dev = &pdev->dev;
+ fc->owner = THIS_MODULE;
+
+-/* bus specific part */
++ /* bus specific part */
+ fc_pci->pdev = pdev;
+ if ((ret = flexcop_pci_init(fc_pci)) != 0)
+ goto err_kfree;
+
+-/* init flexcop */
++ /* init flexcop */
+ if ((ret = flexcop_device_initialize(fc)) != 0)
+ goto err_pci_exit;
+
+-/* init dma */
++ /* init dma */
+ if ((ret = flexcop_pci_dma_init(fc_pci)) != 0)
+ goto err_fc_exit;
+
+ INIT_DELAYED_WORK(&fc_pci->irq_check_work, flexcop_pci_irq_check_work);
+
+- if (irq_chk_intv > 0)
+- schedule_delayed_work(&fc_pci->irq_check_work,
+- msecs_to_jiffies(irq_chk_intv < 100 ? 100 : irq_chk_intv));
+-
++ if (irq_chk_intv > 0)
++ schedule_delayed_work(&fc_pci->irq_check_work,
++ msecs_to_jiffies(irq_chk_intv < 100 ?
++ 100 :
++ irq_chk_intv));
+ return ret;
+
+ err_fc_exit:
+@@ -420,7 +422,6 @@ static void flexcop_pci_remove(struct pci_dev *pdev)
+
+ static struct pci_device_id flexcop_pci_tbl[] = {
+ { PCI_DEVICE(0x13d0, 0x2103) },
+-/* { PCI_DEVICE(0x13d0, 0x2200) }, ? */
+ { },
+ };
+
+diff --git a/drivers/media/dvb/b2c2/flexcop-reg.h b/drivers/media/dvb/b2c2/flexcop-reg.h
+index 7599fcc..dc4528d 100644
+--- a/drivers/media/dvb/b2c2/flexcop-reg.h
++++ b/drivers/media/dvb/b2c2/flexcop-reg.h
+@@ -1,14 +1,11 @@
+ /*
+- * This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
+- *
++ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * flexcop-reg.h - register abstraction for FlexCopII, FlexCopIIb and FlexCopIII
+- *
+- * see flexcop.c for copyright information.
++ * see flexcop.c for copyright information
+ */
+ #ifndef __FLEXCOP_REG_H__
+ #define __FLEXCOP_REG_H__
+
+-
+ typedef enum {
+ FLEXCOP_UNK = 0,
+ FLEXCOP_II,
+@@ -18,13 +15,13 @@ typedef enum {
+
+ typedef enum {
+ FC_UNK = 0,
+- FC_AIR_DVB,
++ FC_CABLE,
++ FC_AIR_DVBT,
+ FC_AIR_ATSC1,
+ FC_AIR_ATSC2,
+- FC_SKY,
+- FC_SKY_OLD,
+- FC_CABLE,
+ FC_AIR_ATSC3,
++ FC_SKY_REV23,
++ FC_SKY_REV26,
+ FC_SKY_REV27,
+ FC_SKY_REV28,
+ } flexcop_device_type_t;
+@@ -36,12 +33,12 @@ typedef enum {
+
+ /* FlexCop IBI Registers */
+ #if defined(__LITTLE_ENDIAN)
+- #include "flexcop_ibi_value_le.h"
++#include "flexcop_ibi_value_le.h"
+ #else
+ #if defined(__BIG_ENDIAN)
+- #include "flexcop_ibi_value_be.h"
++#include "flexcop_ibi_value_be.h"
+ #else
+- #error no endian defined
++#error no endian defined
+ #endif
+ #endif
+
+diff --git a/drivers/media/dvb/b2c2/flexcop-sram.c b/drivers/media/dvb/b2c2/flexcop-sram.c
+index cda6952..f2199e4 100644
+--- a/drivers/media/dvb/b2c2/flexcop-sram.c
++++ b/drivers/media/dvb/b2c2/flexcop-sram.c
+@@ -1,45 +1,43 @@
+ /*
+- * This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
+- *
+- * flexcop-sram.c - functions for controlling the SRAM.
+- *
+- * see flexcop.c for copyright information.
++ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
++ * flexcop-sram.c - functions for controlling the SRAM
++ * see flexcop.c for copyright information
+ */
+ #include "flexcop.h"
+
+-static void flexcop_sram_set_chip (struct flexcop_device *fc, flexcop_sram_type_t type)
++static void flexcop_sram_set_chip(struct flexcop_device *fc,
++ flexcop_sram_type_t type)
+ {
+- flexcop_set_ibi_value(wan_ctrl_reg_71c,sram_chip,type);
++ flexcop_set_ibi_value(wan_ctrl_reg_71c, sram_chip, type);
+ }
+
+ int flexcop_sram_init(struct flexcop_device *fc)
+ {
+ switch (fc->rev) {
+- case FLEXCOP_II:
+- case FLEXCOP_IIB:
+- flexcop_sram_set_chip(fc,FC_SRAM_1_32KB);
+- break;
+- case FLEXCOP_III:
+- flexcop_sram_set_chip(fc,FC_SRAM_1_48KB);
+- break;
+- default:
+- return -EINVAL;
++ case FLEXCOP_II:
++ case FLEXCOP_IIB:
++ flexcop_sram_set_chip(fc, FC_SRAM_1_32KB);
++ break;
++ case FLEXCOP_III:
++ flexcop_sram_set_chip(fc, FC_SRAM_1_48KB);
++ break;
++ default:
++ return -EINVAL;
+ }
+ return 0;
+ }
+
+-int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest, flexcop_sram_dest_target_t target)
++int flexcop_sram_set_dest(struct flexcop_device *fc, flexcop_sram_dest_t dest,
++ flexcop_sram_dest_target_t target)
+ {
+ flexcop_ibi_value v;
+-
+- v = fc->read_ibi_reg(fc,sram_dest_reg_714);
++ v = fc->read_ibi_reg(fc, sram_dest_reg_714);
+
+ if (fc->rev != FLEXCOP_III && target == FC_SRAM_DEST_TARGET_FC3_CA) {
+ err("SRAM destination target to available on FlexCopII(b)\n");
+ return -EINVAL;
+ }
+-
+- deb_sram("sram dest: %x target: %x\n",dest, target);
++ deb_sram("sram dest: %x target: %x\n", dest, target);
+
+ if (dest & FC_SRAM_DEST_NET)
+ v.sram_dest_reg_714.NET_Dest = target;
+@@ -154,14 +152,12 @@ static void sram_write_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len
+ else
+ bank = 0x10000000;
+ }
+-
+ flex_sram_write(adapter, bank, addr & 0x7fff, buf, len);
+ }
+
+ static void sram_read_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len)
+ {
+ u32 bank;
+-
+ bank = 0;
+
+ if (adapter->dw_sram_type == 0x20000) {
+@@ -174,26 +170,22 @@ static void sram_read_chunk(struct adapter *adapter, u32 addr, u8 *buf, u16 len)
+ else
+ bank = 0x10000000;
+ }
+-
+ flex_sram_read(adapter, bank, addr & 0x7fff, buf, len);
+ }
+
+ static void sram_read(struct adapter *adapter, u32 addr, u8 *buf, u32 len)
+ {
+ u32 length;
+-
+ while (len != 0) {
+ length = len;
+-
+- // check if the address range belongs to the same
+- // 32K memory chip. If not, the data is read from
+- // one chip at a time.
++ /* check if the address range belongs to the same
++ * 32K memory chip. If not, the data is read
++ * from one chip at a time */
+ if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) {
+ length = (((addr >> 0x0f) + 1) << 0x0f) - addr;
+ }
+
+ sram_read_chunk(adapter, addr, buf, length);
+-
+ addr = addr + length;
+ buf = buf + length;
+ len = len - length;
+@@ -203,19 +195,17 @@ static void sram_read(struct adapter *adapter, u32 addr, u8 *buf, u32 len)
+ static void sram_write(struct adapter *adapter, u32 addr, u8 *buf, u32 len)
+ {
+ u32 length;
+-
+ while (len != 0) {
+ length = len;
+
+- // check if the address range belongs to the same
+- // 32K memory chip. If not, the data is written to
+- // one chip at a time.
++ /* check if the address range belongs to the same
++ * 32K memory chip. If not, the data is
++ * written to one chip at a time */
+ if ((addr >> 0x0f) != ((addr + len - 1) >> 0x0f)) {
+ length = (((addr >> 0x0f) + 1) << 0x0f) - addr;
+ }
+
+ sram_write_chunk(adapter, addr, buf, length);
+-
+ addr = addr + length;
+ buf = buf + length;
+ len = len - length;
+@@ -224,39 +214,29 @@ static void sram_write(struct adapter *adapter, u32 addr, u8 *buf, u32 len)
+
+ static void sram_set_size(struct adapter *adapter, u32 mask)
+ {
+- write_reg_dw(adapter, 0x71c, (mask | (~0x30000 & read_reg_dw(adapter, 0x71c))));
++ write_reg_dw(adapter, 0x71c,
++ (mask | (~0x30000 & read_reg_dw(adapter, 0x71c))));
+ }
+
+ static void sram_init(struct adapter *adapter)
+ {
+ u32 tmp;
+-
+ tmp = read_reg_dw(adapter, 0x71c);
+-
+ write_reg_dw(adapter, 0x71c, 1);
+
+ if (read_reg_dw(adapter, 0x71c) != 0) {
+ write_reg_dw(adapter, 0x71c, tmp);
+-
+ adapter->dw_sram_type = tmp & 0x30000;
+-
+ ddprintk("%s: dw_sram_type = %x\n", __func__, adapter->dw_sram_type);
+-
+ } else {
+-
+ adapter->dw_sram_type = 0x10000;
+-
+ ddprintk("%s: dw_sram_type = %x\n", __func__, adapter->dw_sram_type);
+ }
+-
+- /* return value is never used? */
+-/* return adapter->dw_sram_type; */
+ }
+
+ static int sram_test_location(struct adapter *adapter, u32 mask, u32 addr)
+ {
+ u8 tmp1, tmp2;
+-
+ dprintk("%s: mask = %x, addr = %x\n", __func__, mask, addr);
+
+ sram_set_size(adapter, mask);
+@@ -269,7 +249,6 @@ static int sram_test_location(struct adapter *adapter, u32 mask, u32 addr)
+ sram_write(adapter, addr + 4, &tmp1, 1);
+
+ tmp2 = 0;
+-
+ mdelay(20);
+
+ sram_read(adapter, addr, &tmp2, 1);
+@@ -287,7 +266,6 @@ static int sram_test_location(struct adapter *adapter, u32 mask, u32 addr)
+ sram_write(adapter, addr + 4, &tmp1, 1);
+
+ tmp2 = 0;
+-
+ mdelay(20);
+
+ sram_read(adapter, addr, &tmp2, 1);
+@@ -297,26 +275,24 @@ static int sram_test_location(struct adapter *adapter, u32 mask, u32 addr)
+
+ if (tmp2 != 0x5a)
+ return 0;
+-
+ return 1;
+ }
+
+ static u32 sram_length(struct adapter *adapter)
+ {
+ if (adapter->dw_sram_type == 0x10000)
+- return 32768; // 32K
++ return 32768; /* 32K */
+ if (adapter->dw_sram_type == 0x00000)
+- return 65536; // 64K
++ return 65536; /* 64K */
+ if (adapter->dw_sram_type == 0x20000)
+- return 131072; // 128K
+-
+- return 32768; // 32K
++ return 131072; /* 128K */
++ return 32768; /* 32K */
+ }
+
+ /* FlexcopII can work with 32K, 64K or 128K of external SRAM memory.
+- - for 128K there are 4x32K chips at bank 0,1,2,3.
+- - for 64K there are 2x32K chips at bank 1,2.
+- - for 32K there is one 32K chip at bank 0.
++ - for 128K there are 4x32K chips at bank 0,1,2,3.
++ - for 64K there are 2x32K chips at bank 1,2.
++ - for 32K there is one 32K chip at bank 0.
+
+ FlexCop works only with one bank at a time. The bank is selected
+ by bits 28-29 of the 0x700 register.
+@@ -324,24 +300,18 @@ static u32 sram_length(struct adapter *adapter)
+ bank 0 covers addresses 0x00000-0x07fff
+ bank 1 covers addresses 0x08000-0x0ffff
+ bank 2 covers addresses 0x10000-0x17fff
+- bank 3 covers addresses 0x18000-0x1ffff
+-*/
++ bank 3 covers addresses 0x18000-0x1ffff */
+
+ static int flexcop_sram_detect(struct flexcop_device *fc)
+ {
+- flexcop_ibi_value r208,r71c_0,vr71c_1;
+-
++ flexcop_ibi_value r208, r71c_0, vr71c_1;
+ r208 = fc->read_ibi_reg(fc, ctrl_208);
+ fc->write_ibi_reg(fc, ctrl_208, ibi_zero);
+
+ r71c_0 = fc->read_ibi_reg(fc, wan_ctrl_reg_71c);
+-
+ write_reg_dw(adapter, 0x71c, 1);
+-
+ tmp3 = read_reg_dw(adapter, 0x71c);
+-
+ dprintk("%s: tmp3 = %x\n", __func__, tmp3);
+-
+ write_reg_dw(adapter, 0x71c, tmp2);
+
+ // check for internal SRAM ???
+@@ -350,9 +320,7 @@ static int flexcop_sram_detect(struct flexcop_device *fc)
+ sram_set_size(adapter, 0x10000);
+ sram_init(adapter);
+ write_reg_dw(adapter, 0x208, tmp);
+-
+ dprintk("%s: sram size = 32K\n", __func__);
+-
+ return 32;
+ }
+
+@@ -360,9 +328,7 @@ static int flexcop_sram_detect(struct flexcop_device *fc)
+ sram_set_size(adapter, 0x20000);
+ sram_init(adapter);
+ write_reg_dw(adapter, 0x208, tmp);
+-
+ dprintk("%s: sram size = 128K\n", __func__);
+-
+ return 128;
+ }
+
+@@ -370,9 +336,7 @@ static int flexcop_sram_detect(struct flexcop_device *fc)
+ sram_set_size(adapter, 0x00000);
+ sram_init(adapter);
+ write_reg_dw(adapter, 0x208, tmp);
+-
+ dprintk("%s: sram size = 64K\n", __func__);
+-
+ return 64;
+ }
+
+@@ -380,18 +344,14 @@ static int flexcop_sram_detect(struct flexcop_device *fc)
+ sram_set_size(adapter, 0x10000);
+ sram_init(adapter);
+ write_reg_dw(adapter, 0x208, tmp);
+-
+ dprintk("%s: sram size = 32K\n", __func__);
+-
+ return 32;
+ }
+
+ sram_set_size(adapter, 0x10000);
+ sram_init(adapter);
+ write_reg_dw(adapter, 0x208, tmp);
+-
+ dprintk("%s: SRAM detection failed. Set to 32K \n", __func__);
+-
+ return 0;
+ }
+
+diff --git a/drivers/media/dvb/b2c2/flexcop-usb.c b/drivers/media/dvb/b2c2/flexcop-usb.c
+index ae0d76a..bedcfb6 100644
+--- a/drivers/media/dvb/b2c2/flexcop-usb.c
++++ b/drivers/media/dvb/b2c2/flexcop-usb.c
+@@ -1,11 +1,8 @@
+ /*
+- * This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
+- *
+- * flexcop-usb.c - covers the USB part.
+- *
+- * see flexcop.c for copyright information.
++ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
++ * flexcop-usb.c - covers the USB part
++ * see flexcop.c for copyright information
+ */
+-
+ #define FC_LOG_PREFIX "flexcop_usb"
+ #include "flexcop-usb.h"
+ #include "flexcop-common.h"
+@@ -18,42 +15,47 @@
+ /* debug */
+ #ifdef CONFIG_DVB_B2C2_FLEXCOP_DEBUG
+ #define dprintk(level,args...) \
+- do { if ((debug & level)) { printk(args); } } while (0)
+-#define debug_dump(b,l,method) {\
++ do { if ((debug & level)) printk(args); } while (0)
++
++#define debug_dump(b, l, method) do {\
+ int i; \
+- for (i = 0; i < l; i++) method("%02x ", b[i]); \
+- method("\n");\
+-}
++ for (i = 0; i < l; i++) \
++ method("%02x ", b[i]); \
++ method("\n"); \
++} while (0)
+
+ #define DEBSTATUS ""
+ #else
+-#define dprintk(level,args...)
+-#define debug_dump(b,l,method)
++#define dprintk(level, args...)
++#define debug_dump(b, l, method)
+ #define DEBSTATUS " (debugging is not enabled)"
+ #endif
+
+ static int debug;
+ module_param(debug, int, 0644);
+-MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2,ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS);
++MODULE_PARM_DESC(debug, "set debugging level (1=info,ts=2,"
++ "ctrl=4,i2c=8,v8mem=16 (or-able))." DEBSTATUS);
+ #undef DEBSTATUS
+
+-#define deb_info(args...) dprintk(0x01,args)
+-#define deb_ts(args...) dprintk(0x02,args)
+-#define deb_ctrl(args...) dprintk(0x04,args)
+-#define deb_i2c(args...) dprintk(0x08,args)
+-#define deb_v8(args...) dprintk(0x10,args)
++#define deb_info(args...) dprintk(0x01, args)
++#define deb_ts(args...) dprintk(0x02, args)
++#define deb_ctrl(args...) dprintk(0x04, args)
++#define deb_i2c(args...) dprintk(0x08, args)
++#define deb_v8(args...) dprintk(0x10, args)
+
+ /* JLP 111700: we will include the 1 bit gap between the upper and lower 3 bits
+ * in the IBI address, to make the V8 code simpler.
+- * PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (these are the six bits used)
++ * PCI ADDRESS FORMAT: 0x71C -> 0000 0111 0001 1100 (the six bits used)
+ * in general: 0000 0HHH 000L LL00
+ * IBI ADDRESS FORMAT: RHHH BLLL
+ *
+ * where R is the read(1)/write(0) bit, B is the busy bit
+ * and HHH and LLL are the two sets of three bits from the PCI address.
+ */
+-#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) (((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70))
+-#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) (((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4))
++#define B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(usPCI) (u8) \
++ (((usPCI >> 2) & 0x07) + ((usPCI >> 4) & 0x70))
++#define B2C2_FLEX_INTERNALADDR_TO_PCIOFFSET(ucAddr) (u16) \
++ (((ucAddr & 0x07) << 2) + ((ucAddr & 0x70) << 4))
+
+ /*
+ * DKT 020228
+@@ -69,12 +71,13 @@ static int flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI,
+ struct flexcop_usb *fc_usb = fc->bus_specific;
+ u8 request = read ? B2C2_USB_READ_REG : B2C2_USB_WRITE_REG;
+ u8 request_type = (read ? USB_DIR_IN : USB_DIR_OUT) | USB_TYPE_VENDOR;
+- u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) | (read ? 0x80 : 0);
++ u8 wAddress = B2C2_FLEX_PCIOFFSET_TO_INTERNALADDR(wRegOffsPCI) |
++ (read ? 0x80 : 0);
+
+ int len = usb_control_msg(fc_usb->udev,
+ read ? B2C2_USB_CTRL_PIPE_IN : B2C2_USB_CTRL_PIPE_OUT,
+ request,
+- request_type, /* 0xc0 read or 0x40 write*/
++ request_type, /* 0xc0 read or 0x40 write */
+ wAddress,
+ 0,
+ val,
+@@ -82,55 +85,49 @@ static int flexcop_usb_readwrite_dw(struct flexcop_device *fc, u16 wRegOffsPCI,
+ B2C2_WAIT_FOR_OPERATION_RDW * HZ);
+
+ if (len != sizeof(u32)) {
+- err("error while %s dword from %d (%d).",read ? "reading" : "writing",
+- wAddress,wRegOffsPCI);
++ err("error while %s dword from %d (%d).", read ? "reading" :
++ "writing", wAddress, wRegOffsPCI);
+ return -EIO;
+ }
+ return 0;
+ }
+-
+ /*
+ * DKT 010817 - add support for V8 memory read/write and flash update
+ */
+ static int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb,
+ flexcop_usb_request_t req, u8 page, u16 wAddress,
+- u8 *pbBuffer,u32 buflen)
++ u8 *pbBuffer, u32 buflen)
+ {
+-// u8 dwRequestType;
+ u8 request_type = USB_TYPE_VENDOR;
+ u16 wIndex;
+- int nWaitTime,pipe,len;
+-
++ int nWaitTime, pipe, len;
+ wIndex = page << 8;
+
+ switch (req) {
+- case B2C2_USB_READ_V8_MEM:
+- nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ;
+- request_type |= USB_DIR_IN;
+-// dwRequestType = (u8) RTYPE_READ_V8_MEMORY;
+- pipe = B2C2_USB_CTRL_PIPE_IN;
++ case B2C2_USB_READ_V8_MEM:
++ nWaitTime = B2C2_WAIT_FOR_OPERATION_V8READ;
++ request_type |= USB_DIR_IN;
++ pipe = B2C2_USB_CTRL_PIPE_IN;
+ break;
+- case B2C2_USB_WRITE_V8_MEM:
+- wIndex |= pbBuffer[0];
+- request_type |= USB_DIR_OUT;
+- nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE;
+-// dwRequestType = (u8) RTYPE_WRITE_V8_MEMORY;
+- pipe = B2C2_USB_CTRL_PIPE_OUT;
++ case B2C2_USB_WRITE_V8_MEM:
++ wIndex |= pbBuffer[0];
++ request_type |= USB_DIR_OUT;
++ nWaitTime = B2C2_WAIT_FOR_OPERATION_V8WRITE;
++ pipe = B2C2_USB_CTRL_PIPE_OUT;
+ break;
+- case B2C2_USB_FLASH_BLOCK:
+- request_type |= USB_DIR_OUT;
+- nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH;
+-// dwRequestType = (u8) RTYPE_WRITE_V8_FLASH;
+- pipe = B2C2_USB_CTRL_PIPE_OUT;
++ case B2C2_USB_FLASH_BLOCK:
++ request_type |= USB_DIR_OUT;
++ nWaitTime = B2C2_WAIT_FOR_OPERATION_V8FLASH;
++ pipe = B2C2_USB_CTRL_PIPE_OUT;
+ break;
+- default:
+- deb_info("unsupported request for v8_mem_req %x.\n",req);
++ default:
++ deb_info("unsupported request for v8_mem_req %x.\n", req);
+ return -EINVAL;
+ }
+- deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n",request_type,req,
+- wAddress,wIndex,buflen);
++ deb_v8("v8mem: %02x %02x %04x %04x, len: %d\n", request_type, req,
++ wAddress, wIndex, buflen);
+
+- len = usb_control_msg(fc_usb->udev,pipe,
++ len = usb_control_msg(fc_usb->udev, pipe,
+ req,
+ request_type,
+ wAddress,
+@@ -139,39 +136,53 @@ static int flexcop_usb_v8_memory_req(struct flexcop_usb *fc_usb,
+ buflen,
+ nWaitTime * HZ);
+
+- debug_dump(pbBuffer,len,deb_v8);
+-
++ debug_dump(pbBuffer, len, deb_v8);
+ return len == buflen ? 0 : -EIO;
+ }
+
+ #define bytes_left_to_read_on_page(paddr,buflen) \
+- ((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \
+- ? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)))
++ ((V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)) > buflen \
++ ? buflen : (V8_MEMORY_PAGE_SIZE - (paddr & V8_MEMORY_PAGE_MASK)))
+
+-static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb,flexcop_usb_request_t req,
+- flexcop_usb_mem_page_t page_start, u32 addr, int extended, u8 *buf, u32 len)
++static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb,
++ flexcop_usb_request_t req, flexcop_usb_mem_page_t page_start,
++ u32 addr, int extended, u8 *buf, u32 len)
+ {
+ int i,ret = 0;
+ u16 wMax;
+ u32 pagechunk = 0;
+
+ switch(req) {
+- case B2C2_USB_READ_V8_MEM: wMax = USB_MEM_READ_MAX; break;
+- case B2C2_USB_WRITE_V8_MEM: wMax = USB_MEM_WRITE_MAX; break;
+- case B2C2_USB_FLASH_BLOCK: wMax = USB_FLASH_MAX; break;
+- default:
+- return -EINVAL;
++ case B2C2_USB_READ_V8_MEM:
++ wMax = USB_MEM_READ_MAX;
++ break;
++ case B2C2_USB_WRITE_V8_MEM:
++ wMax = USB_MEM_WRITE_MAX;
++ break;
++ case B2C2_USB_FLASH_BLOCK:
++ wMax = USB_FLASH_MAX;
++ break;
++ default:
++ return -EINVAL;
+ break;
+ }
+ for (i = 0; i < len;) {
+- pagechunk = wMax < bytes_left_to_read_on_page(addr,len) ? wMax : bytes_left_to_read_on_page(addr,len);
+- deb_info("%x\n",(addr & V8_MEMORY_PAGE_MASK) | (V8_MEMORY_EXTENDED*extended));
+- if ((ret = flexcop_usb_v8_memory_req(fc_usb,req,
+- page_start + (addr / V8_MEMORY_PAGE_SIZE), /* actual page */
+- (addr & V8_MEMORY_PAGE_MASK) | (V8_MEMORY_EXTENDED*extended),
+- &buf[i],pagechunk)) < 0)
++ pagechunk =
++ wMax < bytes_left_to_read_on_page(addr, len) ?
++ wMax :
++ bytes_left_to_read_on_page(addr, len);
++ deb_info("%x\n",
++ (addr & V8_MEMORY_PAGE_MASK) |
++ (V8_MEMORY_EXTENDED*extended));
++
++ ret = flexcop_usb_v8_memory_req(fc_usb, req,
++ page_start + (addr / V8_MEMORY_PAGE_SIZE),
++ (addr & V8_MEMORY_PAGE_MASK) |
++ (V8_MEMORY_EXTENDED*extended),
++ &buf[i], pagechunk);
++
++ if (ret < 0)
+ return ret;
+-
+ addr += pagechunk;
+ len -= pagechunk;
+ }
+@@ -180,8 +191,9 @@ static int flexcop_usb_memory_req(struct flexcop_usb *fc_usb,flexcop_usb_request
+
+ static int flexcop_usb_get_mac_addr(struct flexcop_device *fc, int extended)
+ {
+- return flexcop_usb_memory_req(fc->bus_specific,B2C2_USB_READ_V8_MEM,
+- V8_MEMORY_PAGE_FLASH,0x1f010,1,fc->dvb_adapter.proposed_mac,6);
++ return flexcop_usb_memory_req(fc->bus_specific, B2C2_USB_READ_V8_MEM,
++ V8_MEMORY_PAGE_FLASH, 0x1f010, 1,
++ fc->dvb_adapter.proposed_mac, 6);
+ }
+
+ #if 0
+@@ -191,11 +203,8 @@ static int flexcop_usb_utility_req(struct flexcop_usb *fc_usb, int set,
+ {
+ u16 wValue;
+ u8 request_type = (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR;
+-// u8 dwRequestType = (u8) RTYPE_GENERIC,
+ int nWaitTime = 2,
+- pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN,
+- len;
+-
++ pipe = set ? B2C2_USB_CTRL_PIPE_OUT : B2C2_USB_CTRL_PIPE_IN, len;
+ wValue = (func << 8) | extra;
+
+ len = usb_control_msg(fc_usb->udev,pipe,
+@@ -218,36 +227,35 @@ static int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c,
+ struct flexcop_usb *fc_usb = i2c->fc->bus_specific;
+ u16 wValue, wIndex;
+ int nWaitTime,pipe,len;
+-// u8 dwRequestType;
+ u8 request_type = USB_TYPE_VENDOR;
+
+ switch (func) {
+- case USB_FUNC_I2C_WRITE:
+- case USB_FUNC_I2C_MULTIWRITE:
+- case USB_FUNC_I2C_REPEATWRITE:
++ case USB_FUNC_I2C_WRITE:
++ case USB_FUNC_I2C_MULTIWRITE:
++ case USB_FUNC_I2C_REPEATWRITE:
+ /* DKT 020208 - add this to support special case of DiSEqC */
+- case USB_FUNC_I2C_CHECKWRITE:
+- pipe = B2C2_USB_CTRL_PIPE_OUT;
+- nWaitTime = 2;
+-// dwRequestType = (u8) RTYPE_GENERIC;
+- request_type |= USB_DIR_OUT;
++ case USB_FUNC_I2C_CHECKWRITE:
++ pipe = B2C2_USB_CTRL_PIPE_OUT;
++ nWaitTime = 2;
++ request_type |= USB_DIR_OUT;
+ break;
+- case USB_FUNC_I2C_READ:
+- case USB_FUNC_I2C_REPEATREAD:
+- pipe = B2C2_USB_CTRL_PIPE_IN;
+- nWaitTime = 2;
+-// dwRequestType = (u8) RTYPE_GENERIC;
+- request_type |= USB_DIR_IN;
++ case USB_FUNC_I2C_READ:
++ case USB_FUNC_I2C_REPEATREAD:
++ pipe = B2C2_USB_CTRL_PIPE_IN;
++ nWaitTime = 2;
++ request_type |= USB_DIR_IN;
+ break;
+- default:
+- deb_info("unsupported function for i2c_req %x\n",func);
+- return -EINVAL;
++ default:
++ deb_info("unsupported function for i2c_req %x\n", func);
++ return -EINVAL;
+ }
+ wValue = (func << 8) | (i2c->port << 4);
+ wIndex = (chipaddr << 8 ) | addr;
+
+- deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n",func,request_type,req,
+- wValue & 0xff, wValue >> 8, wIndex & 0xff, wIndex >> 8);
++ deb_i2c("i2c %2d: %02x %02x %02x %02x %02x %02x\n",
++ func, request_type, req,
++ wValue & 0xff, wValue >> 8,
++ wIndex & 0xff, wIndex >> 8);
+
+ len = usb_control_msg(fc_usb->udev,pipe,
+ req,
+@@ -257,44 +265,49 @@ static int flexcop_usb_i2c_req(struct flexcop_i2c_adapter *i2c,
+ buf,
+ buflen,
+ nWaitTime * HZ);
+-
+ return len == buflen ? 0 : -EREMOTEIO;
+ }
+
+-/* actual bus specific access functions, make sure prototype are/will be equal to pci */
+-static flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc, flexcop_ibi_register reg)
++/* actual bus specific access functions,
++ make sure prototype are/will be equal to pci */
++static flexcop_ibi_value flexcop_usb_read_ibi_reg(struct flexcop_device *fc,
++ flexcop_ibi_register reg)
+ {
+ flexcop_ibi_value val;
+ val.raw = 0;
+- flexcop_usb_readwrite_dw(fc,reg, &val.raw, 1);
++ flexcop_usb_readwrite_dw(fc, reg, &val.raw, 1);
+ return val;
+ }
+
+-static int flexcop_usb_write_ibi_reg(struct flexcop_device *fc, flexcop_ibi_register reg, flexcop_ibi_value val)
++static int flexcop_usb_write_ibi_reg(struct flexcop_device *fc,
++ flexcop_ibi_register reg, flexcop_ibi_value val)
+ {
+- return flexcop_usb_readwrite_dw(fc,reg, &val.raw, 0);
++ return flexcop_usb_readwrite_dw(fc, reg, &val.raw, 0);
+ }
+
+ static int flexcop_usb_i2c_request(struct flexcop_i2c_adapter *i2c,
+- flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len)
++ flexcop_access_op_t op, u8 chipaddr, u8 addr, u8 *buf, u16 len)
+ {
+ if (op == FC_READ)
+ return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST,
+- USB_FUNC_I2C_READ, chipaddr, addr, buf, len);
++ USB_FUNC_I2C_READ, chipaddr, addr, buf, len);
+ else
+ return flexcop_usb_i2c_req(i2c, B2C2_USB_I2C_REQUEST,
+- USB_FUNC_I2C_WRITE, chipaddr, addr, buf, len);
++ USB_FUNC_I2C_WRITE, chipaddr, addr, buf, len);
+ }
+
+-static void flexcop_usb_process_frame(struct flexcop_usb *fc_usb, u8 *buffer, int buffer_length)
++static void flexcop_usb_process_frame(struct flexcop_usb *fc_usb,
++ u8 *buffer, int buffer_length)
+ {
+ u8 *b;
+ int l;
+
+- deb_ts("tmp_buffer_length=%d, buffer_length=%d\n", fc_usb->tmp_buffer_length, buffer_length);
++ deb_ts("tmp_buffer_length=%d, buffer_length=%d\n",
++ fc_usb->tmp_buffer_length, buffer_length);
+
+ if (fc_usb->tmp_buffer_length > 0) {
+- memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer, buffer_length);
++ memcpy(fc_usb->tmp_buffer+fc_usb->tmp_buffer_length, buffer,
++ buffer_length);
+ fc_usb->tmp_buffer_length += buffer_length;
+ b = fc_usb->tmp_buffer;
+ l = fc_usb->tmp_buffer_length;
+@@ -304,23 +317,26 @@ static void flexcop_usb_process_frame(struct flexcop_usb *fc_usb, u8 *buffer, in
+ }
+
+ while (l >= 190) {
+- if (*b == 0xff)
++ if (*b == 0xff) {
+ switch (*(b+1) & 0x03) {
+- case 0x01: /* media packet */
+- if ( *(b+2) == 0x47 )
+- flexcop_pass_dmx_packets(fc_usb->fc_dev, b+2, 1);
+- else
+- deb_ts("not ts packet %02x %02x %02x %02x \n", *(b+2), *(b+3), *(b+4), *(b+5) );
+-
+- b += 190;
+- l -= 190;
++ case 0x01: /* media packet */
++ if (*(b+2) == 0x47)
++ flexcop_pass_dmx_packets(
++ fc_usb->fc_dev, b+2, 1);
++ else
++ deb_ts(
++ "not ts packet %02x %02x %02x %02x \n",
++ *(b+2), *(b+3),
++ *(b+4), *(b+5));
++ b += 190;
++ l -= 190;
+ break;
+- default:
+- deb_ts("wrong packet type\n");
+- l = 0;
++ default:
++ deb_ts("wrong packet type\n");
++ l = 0;
+ break;
+ }
+- else {
++ } else {
+ deb_ts("wrong header\n");
+ l = 0;
+ }
+@@ -337,23 +353,26 @@ static void flexcop_usb_urb_complete(struct urb *urb)
+ int i;
+
+ if (urb->actual_length > 0)
+- deb_ts("urb completed, bufsize: %d actlen; %d\n",urb->transfer_buffer_length, urb->actual_length);
++ deb_ts("urb completed, bufsize: %d actlen; %d\n",
++ urb->transfer_buffer_length, urb->actual_length);
+
+ for (i = 0; i < urb->number_of_packets; i++) {
+ if (urb->iso_frame_desc[i].status < 0) {
+- err("iso frame descriptor %d has an error: %d\n",i,urb->iso_frame_desc[i].status);
++ err("iso frame descriptor %d has an error: %d\n", i,
++ urb->iso_frame_desc[i].status);
+ } else
+ if (urb->iso_frame_desc[i].actual_length > 0) {
+- deb_ts("passed %d bytes to the demux\n",urb->iso_frame_desc[i].actual_length);
++ deb_ts("passed %d bytes to the demux\n",
++ urb->iso_frame_desc[i].actual_length);
+
+ flexcop_usb_process_frame(fc_usb,
+- urb->transfer_buffer + urb->iso_frame_desc[i].offset,
++ urb->transfer_buffer +
++ urb->iso_frame_desc[i].offset,
+ urb->iso_frame_desc[i].actual_length);
+- }
++ }
+ urb->iso_frame_desc[i].status = 0;
+ urb->iso_frame_desc[i].actual_length = 0;
+ }
+-
+ usb_submit_urb(urb,GFP_ATOMIC);
+ }
+
+@@ -374,35 +393,47 @@ static void flexcop_usb_transfer_exit(struct flexcop_usb *fc_usb)
+ }
+
+ if (fc_usb->iso_buffer != NULL)
+- pci_free_consistent(NULL,fc_usb->buffer_size, fc_usb->iso_buffer, fc_usb->dma_addr);
++ pci_free_consistent(NULL,
++ fc_usb->buffer_size, fc_usb->iso_buffer,
++ fc_usb->dma_addr);
+ }
+
+ static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
+ {
+- u16 frame_size = le16_to_cpu(fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize);
+- int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO * frame_size,i,j,ret;
++ u16 frame_size = le16_to_cpu(
++ fc_usb->uintf->cur_altsetting->endpoint[0].desc.wMaxPacketSize);
++ int bufsize = B2C2_USB_NUM_ISO_URB * B2C2_USB_FRAMES_PER_ISO *
++ frame_size, i, j, ret;
+ int buffer_offset = 0;
+
+- deb_ts("creating %d iso-urbs with %d frames each of %d bytes size = %d.\n",
+- B2C2_USB_NUM_ISO_URB, B2C2_USB_FRAMES_PER_ISO, frame_size,bufsize);
++ deb_ts("creating %d iso-urbs with %d frames "
++ "each of %d bytes size = %d.\n", B2C2_USB_NUM_ISO_URB,
++ B2C2_USB_FRAMES_PER_ISO, frame_size, bufsize);
+
+- fc_usb->iso_buffer = pci_alloc_consistent(NULL,bufsize,&fc_usb->dma_addr);
++ fc_usb->iso_buffer = pci_alloc_consistent(NULL,
++ bufsize, &fc_usb->dma_addr);
+ if (fc_usb->iso_buffer == NULL)
+ return -ENOMEM;
++
+ memset(fc_usb->iso_buffer, 0, bufsize);
+ fc_usb->buffer_size = bufsize;
+
+ /* creating iso urbs */
+- for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++)
+- if (!(fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO,GFP_ATOMIC))) {
++ for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
++ fc_usb->iso_urb[i] = usb_alloc_urb(B2C2_USB_FRAMES_PER_ISO,
++ GFP_ATOMIC);
++ if (fc_usb->iso_urb[i] == NULL) {
+ ret = -ENOMEM;
+ goto urb_error;
+ }
++ }
++
+ /* initialising and submitting iso urbs */
+ for (i = 0; i < B2C2_USB_NUM_ISO_URB; i++) {
+ int frame_offset = 0;
+ struct urb *urb = fc_usb->iso_urb[i];
+- deb_ts("initializing and submitting urb no. %d (buf_offset: %d).\n",i,buffer_offset);
++ deb_ts("initializing and submitting urb no. %d "
++ "(buf_offset: %d).\n", i, buffer_offset);
+
+ urb->dev = fc_usb->udev;
+ urb->context = fc_usb;
+@@ -416,26 +447,26 @@ static int flexcop_usb_transfer_init(struct flexcop_usb *fc_usb)
+
+ buffer_offset += frame_size * B2C2_USB_FRAMES_PER_ISO;
+ for (j = 0; j < B2C2_USB_FRAMES_PER_ISO; j++) {
+- deb_ts("urb no: %d, frame: %d, frame_offset: %d\n",i,j,frame_offset);
++ deb_ts("urb no: %d, frame: %d, frame_offset: %d\n",
++ i, j, frame_offset);
+ urb->iso_frame_desc[j].offset = frame_offset;
+ urb->iso_frame_desc[j].length = frame_size;
+ frame_offset += frame_size;
+ }
+
+ if ((ret = usb_submit_urb(fc_usb->iso_urb[i],GFP_ATOMIC))) {
+- err("submitting urb %d failed with %d.",i,ret);
++ err("submitting urb %d failed with %d.", i, ret);
+ goto urb_error;
+ }
+ deb_ts("submitted urb no. %d.\n",i);
+ }
+
+-/* SRAM */
+-
+- flexcop_sram_set_dest(fc_usb->fc_dev,FC_SRAM_DEST_MEDIA | FC_SRAM_DEST_NET |
+- FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI, FC_SRAM_DEST_TARGET_WAN_USB);
+- flexcop_wan_set_speed(fc_usb->fc_dev,FC_WAN_SPEED_8MBITS);
+- flexcop_sram_ctrl(fc_usb->fc_dev,1,1,1);
+-
++ /* SRAM */
++ flexcop_sram_set_dest(fc_usb->fc_dev, FC_SRAM_DEST_MEDIA |
++ FC_SRAM_DEST_NET | FC_SRAM_DEST_CAO | FC_SRAM_DEST_CAI,
++ FC_SRAM_DEST_TARGET_WAN_USB);
++ flexcop_wan_set_speed(fc_usb->fc_dev, FC_WAN_SPEED_8MBITS);
++ flexcop_sram_ctrl(fc_usb->fc_dev, 1, 1, 1);
+ return 0;
+
+ urb_error:
+@@ -448,20 +479,20 @@ static int flexcop_usb_init(struct flexcop_usb *fc_usb)
+ /* use the alternate setting with the larges buffer */
+ usb_set_interface(fc_usb->udev,0,1);
+ switch (fc_usb->udev->speed) {
+- case USB_SPEED_LOW:
+- err("cannot handle USB speed because it is to sLOW.");
+- return -ENODEV;
+- break;
+- case USB_SPEED_FULL:
+- info("running at FULL speed.");
+- break;
+- case USB_SPEED_HIGH:
+- info("running at HIGH speed.");
+- break;
+- case USB_SPEED_UNKNOWN: /* fall through */
+- default:
+- err("cannot handle USB speed because it is unkown.");
+- return -ENODEV;
++ case USB_SPEED_LOW:
++ err("cannot handle USB speed because it is too slow.");
++ return -ENODEV;
++ break;
++ case USB_SPEED_FULL:
++ info("running at FULL speed.");
++ break;
++ case USB_SPEED_HIGH:
++ info("running at HIGH speed.");
++ break;
++ case USB_SPEED_UNKNOWN: /* fall through */
++ default:
++ err("cannot handle USB speed because it is unknown.");
++ return -ENODEV;
+ }
+ usb_set_intfdata(fc_usb->uintf, fc_usb);
+ return 0;
+@@ -485,7 +516,7 @@ static int flexcop_usb_probe(struct usb_interface *intf,
+ return -ENOMEM;
+ }
+
+-/* general flexcop init */
++ /* general flexcop init */
+ fc_usb = fc->bus_specific;
+ fc_usb->fc_dev = fc;
+
+@@ -502,21 +533,21 @@ static int flexcop_usb_probe(struct usb_interface *intf,
+ fc->dev = &udev->dev;
+ fc->owner = THIS_MODULE;
+
+-/* bus specific part */
++ /* bus specific part */
+ fc_usb->udev = udev;
+ fc_usb->uintf = intf;
+ if ((ret = flexcop_usb_init(fc_usb)) != 0)
+ goto err_kfree;
+
+-/* init flexcop */
++ /* init flexcop */
+ if ((ret = flexcop_device_initialize(fc)) != 0)
+ goto err_usb_exit;
+
+-/* xfer init */
++ /* xfer init */
+ if ((ret = flexcop_usb_transfer_init(fc_usb)) != 0)
+ goto err_fc_exit;
+
+- info("%s successfully initialized and connected.",DRIVER_NAME);
++ info("%s successfully initialized and connected.", DRIVER_NAME);
+ return 0;
+
+ err_fc_exit:
+@@ -535,12 +566,12 @@ static void flexcop_usb_disconnect(struct usb_interface *intf)
+ flexcop_device_exit(fc_usb->fc_dev);
+ flexcop_usb_exit(fc_usb);
+ flexcop_device_kfree(fc_usb->fc_dev);
+- info("%s successfully deinitialized and disconnected.",DRIVER_NAME);
++ info("%s successfully deinitialized and disconnected.", DRIVER_NAME);
+ }
+
+ static struct usb_device_id flexcop_usb_table [] = {
+- { USB_DEVICE(0x0af7, 0x0101) },
+- { }
++ { USB_DEVICE(0x0af7, 0x0101) },
++ { }
+ };
+ MODULE_DEVICE_TABLE (usb, flexcop_usb_table);
+
+@@ -557,10 +588,9 @@ static int __init flexcop_usb_module_init(void)
+ {
+ int result;
+ if ((result = usb_register(&flexcop_usb_driver))) {
+- err("usb_register failed. (%d)",result);
++ err("usb_register failed. (%d)", result);
+ return result;
+ }
+-
+ return 0;
+ }
+
+diff --git a/drivers/media/dvb/b2c2/flexcop-usb.h b/drivers/media/dvb/b2c2/flexcop-usb.h
+index 630e647..92529a9 100644
+--- a/drivers/media/dvb/b2c2/flexcop-usb.h
++++ b/drivers/media/dvb/b2c2/flexcop-usb.h
+@@ -1,15 +1,20 @@
++/*
++ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
++ * flexcop-usb.h - header file for the USB part
++ * see flexcop.c for copyright information
++ */
+ #ifndef __FLEXCOP_USB_H_INCLUDED__
+ #define __FLEXCOP_USB_H_INCLUDED__
+
+ #include <linux/usb.h>
+
+ /* transfer parameters */
+-#define B2C2_USB_FRAMES_PER_ISO 4
+-#define B2C2_USB_NUM_ISO_URB 4
++#define B2C2_USB_FRAMES_PER_ISO 4
++#define B2C2_USB_NUM_ISO_URB 4
+
+-#define B2C2_USB_CTRL_PIPE_IN usb_rcvctrlpipe(fc_usb->udev,0)
+-#define B2C2_USB_CTRL_PIPE_OUT usb_sndctrlpipe(fc_usb->udev,0)
+-#define B2C2_USB_DATA_PIPE usb_rcvisocpipe(fc_usb->udev,0x81)
++#define B2C2_USB_CTRL_PIPE_IN usb_rcvctrlpipe(fc_usb->udev, 0)
++#define B2C2_USB_CTRL_PIPE_OUT usb_sndctrlpipe(fc_usb->udev, 0)
++#define B2C2_USB_DATA_PIPE usb_rcvisocpipe(fc_usb->udev, 0x81)
+
+ struct flexcop_usb {
+ struct usb_device *udev;
+@@ -18,8 +23,8 @@ struct flexcop_usb {
+ u8 *iso_buffer;
+ int buffer_size;
+ dma_addr_t dma_addr;
+- struct urb *iso_urb[B2C2_USB_NUM_ISO_URB];
+
++ struct urb *iso_urb[B2C2_USB_NUM_ISO_URB];
+ struct flexcop_device *fc_dev;
+
+ u8 tmp_buffer[1023+190];
+@@ -30,14 +35,6 @@ struct flexcop_usb {
+ /* request types TODO What is its use?*/
+ typedef enum {
+
+-/* something is wrong with this part
+- RTYPE_READ_DW = (1 << 6),
+- RTYPE_WRITE_DW_1 = (3 << 6),
+- RTYPE_READ_V8_MEMORY = (6 << 6),
+- RTYPE_WRITE_V8_MEMORY = (7 << 6),
+- RTYPE_WRITE_V8_FLASH = (8 << 6),
+- RTYPE_GENERIC = (9 << 6),
+-*/
+ } flexcop_usb_request_type_t;
+ #endif
+
+@@ -47,7 +44,6 @@ typedef enum {
+ B2C2_USB_READ_V8_MEM = 0x05,
+ B2C2_USB_READ_REG = 0x08,
+ B2C2_USB_WRITE_REG = 0x0A,
+-/* B2C2_USB_WRITEREGLO = 0x0A, */
+ B2C2_USB_WRITEREGHI = 0x0B,
+ B2C2_USB_FLASH_BLOCK = 0x10,
+ B2C2_USB_I2C_REQUEST = 0x11,
+@@ -62,15 +58,13 @@ typedef enum {
+ USB_FUNC_I2C_REPEATWRITE = 0x04,
+ USB_FUNC_GET_DESCRIPTOR = 0x05,
+ USB_FUNC_I2C_REPEATREAD = 0x06,
+-/* DKT 020208 - add this to support special case of DiSEqC */
++ /* DKT 020208 - add this to support special case of DiSEqC */
+ USB_FUNC_I2C_CHECKWRITE = 0x07,
+ USB_FUNC_I2C_CHECKRESULT = 0x08,
+ } flexcop_usb_i2c_function_t;
+
+-/*
+- * function definition for UTILITY request 0x12
+- * DKT 020304 - new utility function
+- */
++/* function definition for UTILITY request 0x12
++ * DKT 020304 - new utility function */
+ typedef enum {
+ UTILITY_SET_FILTER = 0x01,
+ UTILITY_DATA_ENABLE = 0x02,
+@@ -84,7 +78,7 @@ typedef enum {
+ UTILITY_DATA_RESET = 0x0A,
+ UTILITY_GET_DATA_STATUS = 0x10,
+ UTILITY_GET_V8_REG = 0x11,
+-/* DKT 020326 - add function for v1.14 */
++ /* DKT 020326 - add function for v1.14 */
+ UTILITY_SRAM_WRITE = 0x12,
+ UTILITY_SRAM_READ = 0x13,
+ UTILITY_SRAM_TESTFILL = 0x14,
+@@ -92,13 +86,13 @@ typedef enum {
+ UTILITY_SRAM_TESTVERIFY = 0x16,
+ } flexcop_usb_utility_function_t;
+
+-#define B2C2_WAIT_FOR_OPERATION_RW 1*HZ /* 1 s */
+-#define B2C2_WAIT_FOR_OPERATION_RDW 3*HZ /* 3 s */
+-#define B2C2_WAIT_FOR_OPERATION_WDW 1*HZ /* 1 s */
++#define B2C2_WAIT_FOR_OPERATION_RW (1*HZ)
++#define B2C2_WAIT_FOR_OPERATION_RDW (3*HZ)
++#define B2C2_WAIT_FOR_OPERATION_WDW (1*HZ)
+
+-#define B2C2_WAIT_FOR_OPERATION_V8READ 3*HZ /* 3 s */
+-#define B2C2_WAIT_FOR_OPERATION_V8WRITE 3*HZ /* 3 s */
+-#define B2C2_WAIT_FOR_OPERATION_V8FLASH 3*HZ /* 3 s */
++#define B2C2_WAIT_FOR_OPERATION_V8READ (3*HZ)
++#define B2C2_WAIT_FOR_OPERATION_V8WRITE (3*HZ)
++#define B2C2_WAIT_FOR_OPERATION_V8FLASH (3*HZ)
+
+ typedef enum {
+ V8_MEMORY_PAGE_DVB_CI = 0x20,
+@@ -107,13 +101,11 @@ typedef enum {
+ V8_MEMORY_PAGE_FLASH = 0x80
+ } flexcop_usb_mem_page_t;
+
+-#define V8_MEMORY_EXTENDED (1 << 15)
+-
+-#define USB_MEM_READ_MAX 32
+-#define USB_MEM_WRITE_MAX 1
+-#define USB_FLASH_MAX 8
+-
+-#define V8_MEMORY_PAGE_SIZE 0x8000 // 32K
+-#define V8_MEMORY_PAGE_MASK 0x7FFF
++#define V8_MEMORY_EXTENDED (1 << 15)
++#define USB_MEM_READ_MAX 32
++#define USB_MEM_WRITE_MAX 1
++#define USB_FLASH_MAX 8
++#define V8_MEMORY_PAGE_SIZE 0x8000 /* 32K */
++#define V8_MEMORY_PAGE_MASK 0x7FFF
+
+ #endif
+diff --git a/drivers/media/dvb/b2c2/flexcop.c b/drivers/media/dvb/b2c2/flexcop.c
+index 9106895..2df1b02 100644
+--- a/drivers/media/dvb/b2c2/flexcop.c
++++ b/drivers/media/dvb/b2c2/flexcop.c
+@@ -1,22 +1,20 @@
+ /*
+- * flexcop.c - driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+- *
+- * Copyright (C) 2004-5 Patrick Boettcher <patrick.boettcher@desy.de>
+- *
+- * based on the skystar2-driver
+- * Copyright (C) 2003 Vadim Catana, skystar@moldova.cc
++ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
++ * flexcop.c - main module part
++ * Copyright (C) 2004-9 Patrick Boettcher <patrick.boettcher@desy.de>
++ * based on skystar2-driver Copyright (C) 2003 Vadim Catana, skystar@moldova.cc
+ *
+ * Acknowledgements:
+- * John Jurrius from BBTI, Inc. for extensive support with
+- * code examples and data books
+- *
+- * Bjarne Steinsbo, bjarne at steinsbo.com (some ideas for rewriting)
++ * John Jurrius from BBTI, Inc. for extensive support
++ * with code examples and data books
++ * Bjarne Steinsbo, bjarne at steinsbo.com (some ideas for rewriting)
+ *
+ * Contributions to the skystar2-driver have been done by
+- * Vincenzo Di Massa, hawk.it at tiscalinet.it (several DiSEqC fixes)
+- * Roberto Ragusa, r.ragusa at libero.it (polishing, restyling the code)
+- * Niklas Peinecke, peinecke at gdv.uni-hannover.de (hardware pid/mac filtering)
+- *
++ * Vincenzo Di Massa, hawk.it at tiscalinet.it (several DiSEqC fixes)
++ * Roberto Ragusa, r.ragusa at libero.it (polishing, restyling the code)
++ * Uwe Bugla, uwe.bugla at gmx.de (doing tests, restyling code, writing docu)
++ * Niklas Peinecke, peinecke at gdv.uni-hannover.de (hardware pid/mac
++ * filtering)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+@@ -46,7 +44,10 @@
+
+ int b2c2_flexcop_debug;
+ module_param_named(debug, b2c2_flexcop_debug, int, 0644);
+-MODULE_PARM_DESC(debug, "set debug level (1=info,2=tuner,4=i2c,8=ts,16=sram,32=reg (|-able))." DEBSTATUS);
++MODULE_PARM_DESC(debug,
++ "set debug level (1=info,2=tuner,4=i2c,8=ts,"
++ "16=sram,32=reg (|-able))."
++ DEBSTATUS);
+ #undef DEBSTATUS
+
+ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+@@ -57,37 +58,36 @@ flexcop_ibi_value ibi_zero;
+ static int flexcop_dvb_start_feed(struct dvb_demux_feed *dvbdmxfeed)
+ {
+ struct flexcop_device *fc = dvbdmxfeed->demux->priv;
+- return flexcop_pid_feed_control(fc,dvbdmxfeed,1);
++ return flexcop_pid_feed_control(fc, dvbdmxfeed, 1);
+ }
+
+ static int flexcop_dvb_stop_feed(struct dvb_demux_feed *dvbdmxfeed)
+ {
+ struct flexcop_device *fc = dvbdmxfeed->demux->priv;
+- return flexcop_pid_feed_control(fc,dvbdmxfeed,0);
++ return flexcop_pid_feed_control(fc, dvbdmxfeed, 0);
+ }
+
+ static int flexcop_dvb_init(struct flexcop_device *fc)
+ {
+ int ret = dvb_register_adapter(&fc->dvb_adapter,
+- "FlexCop Digital TV device", fc->owner,
+- fc->dev, adapter_nr);
++ "FlexCop Digital TV device", fc->owner,
++ fc->dev, adapter_nr);
+ if (ret < 0) {
+ err("error registering DVB adapter");
+ return ret;
+ }
+ fc->dvb_adapter.priv = fc;
+
+- fc->demux.dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING | DMX_MEMORY_BASED_FILTERING);
++ fc->demux.dmx.capabilities = (DMX_TS_FILTERING | DMX_SECTION_FILTERING
++ | DMX_MEMORY_BASED_FILTERING);
+ fc->demux.priv = fc;
+-
+ fc->demux.filternum = fc->demux.feednum = FC_MAX_FEED;
+-
+ fc->demux.start_feed = flexcop_dvb_start_feed;
+ fc->demux.stop_feed = flexcop_dvb_stop_feed;
+ fc->demux.write_to_decoder = NULL;
+
+ if ((ret = dvb_dmx_init(&fc->demux)) < 0) {
+- err("dvb_dmx failed: error %d",ret);
++ err("dvb_dmx failed: error %d", ret);
+ goto err_dmx;
+ }
+
+@@ -97,23 +97,23 @@ static int flexcop_dvb_init(struct flexcop_device *fc)
+ fc->dmxdev.demux = &fc->demux.dmx;
+ fc->dmxdev.capabilities = 0;
+ if ((ret = dvb_dmxdev_init(&fc->dmxdev, &fc->dvb_adapter)) < 0) {
+- err("dvb_dmxdev_init failed: error %d",ret);
++ err("dvb_dmxdev_init failed: error %d", ret);
+ goto err_dmx_dev;
+ }
+
+ if ((ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->hw_frontend)) < 0) {
+- err("adding hw_frontend to dmx failed: error %d",ret);
++ err("adding hw_frontend to dmx failed: error %d", ret);
+ goto err_dmx_add_hw_frontend;
+ }
+
+ fc->mem_frontend.source = DMX_MEMORY_FE;
+ if ((ret = fc->demux.dmx.add_frontend(&fc->demux.dmx, &fc->mem_frontend)) < 0) {
+- err("adding mem_frontend to dmx failed: error %d",ret);
++ err("adding mem_frontend to dmx failed: error %d", ret);
+ goto err_dmx_add_mem_frontend;
+ }
+
+ if ((ret = fc->demux.dmx.connect_frontend(&fc->demux.dmx, &fc->hw_frontend)) < 0) {
+- err("connect frontend failed: error %d",ret);
++ err("connect frontend failed: error %d", ret);
+ goto err_connect_frontend;
+ }
+
+@@ -123,9 +123,9 @@ static int flexcop_dvb_init(struct flexcop_device *fc)
+ return 0;
+
+ err_connect_frontend:
+- fc->demux.dmx.remove_frontend(&fc->demux.dmx,&fc->mem_frontend);
++ fc->demux.dmx.remove_frontend(&fc->demux.dmx, &fc->mem_frontend);
+ err_dmx_add_mem_frontend:
+- fc->demux.dmx.remove_frontend(&fc->demux.dmx,&fc->hw_frontend);
++ fc->demux.dmx.remove_frontend(&fc->demux.dmx, &fc->hw_frontend);
+ err_dmx_add_hw_frontend:
+ dvb_dmxdev_release(&fc->dmxdev);
+ err_dmx_dev:
+@@ -141,12 +141,13 @@ static void flexcop_dvb_exit(struct flexcop_device *fc)
+ dvb_net_release(&fc->dvbnet);
+
+ fc->demux.dmx.close(&fc->demux.dmx);
+- fc->demux.dmx.remove_frontend(&fc->demux.dmx,&fc->mem_frontend);
+- fc->demux.dmx.remove_frontend(&fc->demux.dmx,&fc->hw_frontend);
++ fc->demux.dmx.remove_frontend(&fc->demux.dmx,
++ &fc->mem_frontend);
++ fc->demux.dmx.remove_frontend(&fc->demux.dmx,
++ &fc->hw_frontend);
+ dvb_dmxdev_release(&fc->dmxdev);
+ dvb_dmx_release(&fc->demux);
+ dvb_unregister_adapter(&fc->dvb_adapter);
+-
+ deb_info("deinitialized dvb stuff\n");
+ }
+ fc->init_state &= ~FC_STATE_DVB_INIT;
+@@ -168,9 +169,9 @@ EXPORT_SYMBOL(flexcop_pass_dmx_packets);
+
+ static void flexcop_reset(struct flexcop_device *fc)
+ {
+- flexcop_ibi_value v210,v204;
++ flexcop_ibi_value v210, v204;
+
+-/* reset the flexcop itself */
++ /* reset the flexcop itself */
+ fc->write_ibi_reg(fc,ctrl_208,ibi_zero);
+
+ v210.raw = 0;
+@@ -183,13 +184,11 @@ static void flexcop_reset(struct flexcop_device *fc)
+ v210.sw_reset_210.reset_block_600 = 1;
+ v210.sw_reset_210.reset_block_700 = 1;
+ v210.sw_reset_210.Block_reset_enable = 0xb2;
+-
+ v210.sw_reset_210.Special_controls = 0xc259;
+-
+ fc->write_ibi_reg(fc,sw_reset_210,v210);
+ msleep(1);
+
+-/* reset the periphical devices */
++ /* reset the periphical devices */
+
+ v204 = fc->read_ibi_reg(fc,misc_204);
+ v204.misc_204.Per_reset_sig = 0;
+@@ -201,25 +200,24 @@ static void flexcop_reset(struct flexcop_device *fc)
+
+ void flexcop_reset_block_300(struct flexcop_device *fc)
+ {
+- flexcop_ibi_value v208_save = fc->read_ibi_reg(fc,ctrl_208),
+- v210 = fc->read_ibi_reg(fc,sw_reset_210);
+-
+- deb_rdump("208: %08x, 210: %08x\n",v208_save.raw,v210.raw);
++ flexcop_ibi_value v208_save = fc->read_ibi_reg(fc, ctrl_208),
++ v210 = fc->read_ibi_reg(fc, sw_reset_210);
+
++ deb_rdump("208: %08x, 210: %08x\n", v208_save.raw, v210.raw);
+ fc->write_ibi_reg(fc,ctrl_208,ibi_zero);
+
+ v210.sw_reset_210.reset_block_300 = 1;
+ v210.sw_reset_210.Block_reset_enable = 0xb2;
+
+ fc->write_ibi_reg(fc,sw_reset_210,v210);
+- udelay(1000);
+ fc->write_ibi_reg(fc,ctrl_208,v208_save);
+ }
+
+ struct flexcop_device *flexcop_device_kmalloc(size_t bus_specific_len)
+ {
+ void *bus;
+- struct flexcop_device *fc = kzalloc(sizeof(struct flexcop_device), GFP_KERNEL);
++ struct flexcop_device *fc = kzalloc(sizeof(struct flexcop_device),
++ GFP_KERNEL);
+ if (!fc) {
+ err("no memory");
+ return NULL;
+@@ -254,7 +252,6 @@ int flexcop_device_initialize(struct flexcop_device *fc)
+ flexcop_determine_revision(fc);
+ flexcop_sram_init(fc);
+ flexcop_hw_filter_init(fc);
+-
+ flexcop_smc_ctrl(fc, 0);
+
+ if ((ret = flexcop_dvb_init(fc)))
+@@ -279,7 +276,6 @@ int flexcop_device_initialize(struct flexcop_device *fc)
+ goto error;
+
+ flexcop_device_name(fc,"initialization of","complete");
+-
+ return 0;
+
+ error:
+diff --git a/drivers/media/dvb/b2c2/flexcop.h b/drivers/media/dvb/b2c2/flexcop.h
+index 0cebe1d..897b10c 100644
+--- a/drivers/media/dvb/b2c2/flexcop.h
++++ b/drivers/media/dvb/b2c2/flexcop.h
+@@ -1,9 +1,7 @@
+ /*
+- * This file is part of linux driver the digital TV devices equipped with B2C2 FlexcopII(b)/III
+- *
+- * flexcop.h - private header file for all flexcop-chip-source files.
+- *
+- * see flexcop.c for copyright information.
++ * Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
++ * flexcop.h - private header file for all flexcop-chip-source files
++ * see flexcop.c for copyright information
+ */
+ #ifndef __FLEXCOP_H__
+ #define __FLEXCOP_H___
+@@ -21,11 +19,11 @@ extern int b2c2_flexcop_debug;
+ #define dprintk(level,args...)
+ #endif
+
+-#define deb_info(args...) dprintk(0x01,args)
+-#define deb_tuner(args...) dprintk(0x02,args)
+-#define deb_i2c(args...) dprintk(0x04,args)
+-#define deb_ts(args...) dprintk(0x08,args)
+-#define deb_sram(args...) dprintk(0x10,args)
+-#define deb_rdump(args...) dprintk(0x20,args)
++#define deb_info(args...) dprintk(0x01, args)
++#define deb_tuner(args...) dprintk(0x02, args)
++#define deb_i2c(args...) dprintk(0x04, args)
++#define deb_ts(args...) dprintk(0x08, args)
++#define deb_sram(args...) dprintk(0x10, args)
++#define deb_rdump(args...) dprintk(0x20, args)
+
+ #endif
+diff --git a/drivers/media/dvb/b2c2/flexcop_ibi_value_be.h b/drivers/media/dvb/b2c2/flexcop_ibi_value_be.h
+index ed9a675..8f64bdb 100644
+--- a/drivers/media/dvb/b2c2/flexcop_ibi_value_be.h
++++ b/drivers/media/dvb/b2c2/flexcop_ibi_value_be.h
+@@ -1,10 +1,7 @@
+-/* This file is part of linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+- *
++/* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * register descriptions
+- *
+- * see flexcop.c for copyright information.
++ * see flexcop.c for copyright information
+ */
+-
+ /* This file is automatically generated, do not edit things here. */
+ #ifndef __FLEXCOP_IBI_VALUE_INCLUDED__
+ #define __FLEXCOP_IBI_VALUE_INCLUDED__
+diff --git a/drivers/media/dvb/b2c2/flexcop_ibi_value_le.h b/drivers/media/dvb/b2c2/flexcop_ibi_value_le.h
+index 49f2315..c75830d 100644
+--- a/drivers/media/dvb/b2c2/flexcop_ibi_value_le.h
++++ b/drivers/media/dvb/b2c2/flexcop_ibi_value_le.h
+@@ -1,10 +1,7 @@
+-/* This file is part of linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+- *
++/* Linux driver for digital TV devices equipped with B2C2 FlexcopII(b)/III
+ * register descriptions
+- *
+- * see flexcop.c for copyright information.
++ * see flexcop.c for copyright information
+ */
+-
+ /* This file is automatically generated, do not edit things here. */
+ #ifndef __FLEXCOP_IBI_VALUE_INCLUDED__
+ #define __FLEXCOP_IBI_VALUE_INCLUDED__
+diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig
+index 27edb0e..8668e63 100644
+--- a/drivers/media/dvb/bt8xx/Kconfig
++++ b/drivers/media/dvb/bt8xx/Kconfig
+@@ -8,7 +8,7 @@ config DVB_BT8XX
+ select DVB_OR51211 if !DVB_FE_CUSTOMISE
+ select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+ select DVB_ZL10353 if !DVB_FE_CUSTOMISE
+- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE
++ select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
+ help
+ Support for PCI cards based on the Bt8xx PCI bridge. Examples are
+ the Nebula cards, the Pinnacle PCTV cards, the Twinhan DST cards,
+diff --git a/drivers/media/dvb/bt8xx/dst_ca.c b/drivers/media/dvb/bt8xx/dst_ca.c
+index 0258451..4601b05 100644
+--- a/drivers/media/dvb/bt8xx/dst_ca.c
++++ b/drivers/media/dvb/bt8xx/dst_ca.c
+@@ -552,16 +552,19 @@ free_mem_and_exit:
+ return result;
+ }
+
+-static int dst_ca_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long ioctl_arg)
++static long dst_ca_ioctl(struct file *file, unsigned int cmd, unsigned long ioctl_arg)
+ {
+- struct dvb_device* dvbdev = (struct dvb_device*) file->private_data;
+- struct dst_state* state = (struct dst_state*) dvbdev->priv;
++ struct dvb_device *dvbdev;
++ struct dst_state *state;
+ struct ca_slot_info *p_ca_slot_info;
+ struct ca_caps *p_ca_caps;
+ struct ca_msg *p_ca_message;
+ void __user *arg = (void __user *)ioctl_arg;
+ int result = 0;
+
++ lock_kernel();
++ dvbdev = (struct dvb_device *)file->private_data;
++ state = (struct dst_state *)dvbdev->priv;
+ p_ca_message = kmalloc(sizeof (struct ca_msg), GFP_KERNEL);
+ p_ca_slot_info = kmalloc(sizeof (struct ca_slot_info), GFP_KERNEL);
+ p_ca_caps = kmalloc(sizeof (struct ca_caps), GFP_KERNEL);
+@@ -647,6 +650,7 @@ static int dst_ca_ioctl(struct inode *inode, struct file *file, unsigned int cmd
+ kfree (p_ca_slot_info);
+ kfree (p_ca_caps);
+
++ unlock_kernel();
+ return result;
+ }
+
+@@ -682,9 +686,9 @@ static ssize_t dst_ca_write(struct file *file, const char __user *buffer, size_t
+ return 0;
+ }
+
+-static struct file_operations dst_ca_fops = {
++static const struct file_operations dst_ca_fops = {
+ .owner = THIS_MODULE,
+- .ioctl = dst_ca_ioctl,
++ .unlocked_ioctl = dst_ca_ioctl,
+ .open = dst_ca_open,
+ .release = dst_ca_release,
+ .read = dst_ca_read,
+diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
+index 48762a2..b1857c1 100644
+--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c
++++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
+@@ -814,7 +814,7 @@ static int __devinit dvb_bt8xx_probe(struct bttv_sub_device *sub)
+
+ mutex_init(&card->lock);
+ card->bttv_nr = sub->core->nr;
+- strncpy(card->card_name, sub->core->name, sizeof(sub->core->name));
++ strlcpy(card->card_name, sub->core->v4l2_dev.name, sizeof(card->card_name));
+ card->i2c_adapter = &sub->core->i2c_adap;
+
+ switch(sub->core->type) {
+diff --git a/drivers/media/dvb/dm1105/Kconfig b/drivers/media/dvb/dm1105/Kconfig
+index 43f4d44..de3eeb0 100644
+--- a/drivers/media/dvb/dm1105/Kconfig
++++ b/drivers/media/dvb/dm1105/Kconfig
+@@ -8,6 +8,7 @@ config DVB_DM1105
+ select DVB_STB6000 if !DVB_FE_CUSTOMISE
+ select DVB_CX24116 if !DVB_FE_CUSTOMISE
+ select DVB_SI21XX if !DVB_FE_CUSTOMISE
++ select VIDEO_IR
+ help
+ Support for cards based on the SDMC DM1105 PCI chip like
+ DvbWorld 2002
+diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c
+index f48f73a..5b20cf5 100644
+--- a/drivers/media/dvb/dm1105/dm1105.c
++++ b/drivers/media/dvb/dm1105/dm1105.c
+@@ -156,46 +156,12 @@ MODULE_PARM_DESC(ir_debug, "enable debugging information for IR decoding");
+
+ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+-static u16 ir_codes_dm1105_nec[128] = {
+- [0x0a] = KEY_Q, /*power*/
+- [0x0c] = KEY_M, /*mute*/
+- [0x11] = KEY_1,
+- [0x12] = KEY_2,
+- [0x13] = KEY_3,
+- [0x14] = KEY_4,
+- [0x15] = KEY_5,
+- [0x16] = KEY_6,
+- [0x17] = KEY_7,
+- [0x18] = KEY_8,
+- [0x19] = KEY_9,
+- [0x10] = KEY_0,
+- [0x1c] = KEY_PAGEUP, /*ch+*/
+- [0x0f] = KEY_PAGEDOWN, /*ch-*/
+- [0x1a] = KEY_O, /*vol+*/
+- [0x0e] = KEY_Z, /*vol-*/
+- [0x04] = KEY_R, /*rec*/
+- [0x09] = KEY_D, /*fav*/
+- [0x08] = KEY_BACKSPACE, /*rewind*/
+- [0x07] = KEY_A, /*fast*/
+- [0x0b] = KEY_P, /*pause*/
+- [0x02] = KEY_ESC, /*cancel*/
+- [0x03] = KEY_G, /*tab*/
+- [0x00] = KEY_UP, /*up*/
+- [0x1f] = KEY_ENTER, /*ok*/
+- [0x01] = KEY_DOWN, /*down*/
+- [0x05] = KEY_C, /*cap*/
+- [0x06] = KEY_S, /*stop*/
+- [0x40] = KEY_F, /*full*/
+- [0x1e] = KEY_W, /*tvmode*/
+- [0x1b] = KEY_B, /*recall*/
+-};
+-
+ /* infrared remote control */
+ struct infrared {
+- u16 key_map[128];
+ struct input_dev *input_dev;
++ struct ir_input_state ir;
+ char input_phys[32];
+- struct tasklet_struct ir_tasklet;
++ struct work_struct work;
+ u32 ir_command;
+ };
+
+@@ -220,10 +186,14 @@ struct dm1105dvb {
+ /* i2c */
+ struct i2c_adapter i2c_adap;
+
++ /* irq */
++ struct work_struct work;
++
+ /* dma */
+ dma_addr_t dma_addr;
+ unsigned char *ts_buf;
+ u32 wrp;
++ u32 nextwrp;
+ u32 buffer_size;
+ unsigned int PacketErrorCount;
+ unsigned int dmarst;
+@@ -233,8 +203,6 @@ struct dm1105dvb {
+
+ #define dm_io_mem(reg) ((unsigned long)(&dm1105dvb->io_mem[reg]))
+
+-static struct dm1105dvb *dm1105dvb_local;
+-
+ static int dm1105_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg *msgs, int num)
+ {
+@@ -407,38 +375,61 @@ static int dm1105dvb_stop_feed(struct dvb_demux_feed *f)
+ return 0;
+ }
+
+-/* ir tasklet */
+-static void dm1105_emit_key(unsigned long parm)
++/* ir work handler */
++static void dm1105_emit_key(struct work_struct *work)
+ {
+- struct infrared *ir = (struct infrared *) parm;
++ struct infrared *ir = container_of(work, struct infrared, work);
+ u32 ircom = ir->ir_command;
+ u8 data;
+- u16 keycode;
++
++ if (ir_debug)
++ printk(KERN_INFO "%s: received byte 0x%04x\n", __func__, ircom);
+
+ data = (ircom >> 8) & 0x7f;
+
+- input_event(ir->input_dev, EV_MSC, MSC_RAW, (0x0000f8 << 16) | data);
+- input_event(ir->input_dev, EV_MSC, MSC_SCAN, data);
+- keycode = ir->key_map[data];
++ ir_input_keydown(ir->input_dev, &ir->ir, data, data);
++ ir_input_nokey(ir->input_dev, &ir->ir);
++}
+
+- if (!keycode)
+- return;
++/* work handler */
++static void dm1105_dmx_buffer(struct work_struct *work)
++{
++ struct dm1105dvb *dm1105dvb =
++ container_of(work, struct dm1105dvb, work);
++ unsigned int nbpackets;
++ u32 oldwrp = dm1105dvb->wrp;
++ u32 nextwrp = dm1105dvb->nextwrp;
++
++ if (!((dm1105dvb->ts_buf[oldwrp] == 0x47) &&
++ (dm1105dvb->ts_buf[oldwrp + 188] == 0x47) &&
++ (dm1105dvb->ts_buf[oldwrp + 188 * 2] == 0x47))) {
++ dm1105dvb->PacketErrorCount++;
++ /* bad packet found */
++ if ((dm1105dvb->PacketErrorCount >= 2) &&
++ (dm1105dvb->dmarst == 0)) {
++ outb(1, dm_io_mem(DM1105_RST));
++ dm1105dvb->wrp = 0;
++ dm1105dvb->PacketErrorCount = 0;
++ dm1105dvb->dmarst = 0;
++ return;
++ }
++ }
+
+- input_event(ir->input_dev, EV_KEY, keycode, 1);
+- input_sync(ir->input_dev);
+- input_event(ir->input_dev, EV_KEY, keycode, 0);
+- input_sync(ir->input_dev);
++ if (nextwrp < oldwrp) {
++ memcpy(dm1105dvb->ts_buf + dm1105dvb->buffer_size,
++ dm1105dvb->ts_buf, nextwrp);
++ nbpackets = ((dm1105dvb->buffer_size - oldwrp) + nextwrp) / 188;
++ } else
++ nbpackets = (nextwrp - oldwrp) / 188;
+
++ dm1105dvb->wrp = nextwrp;
++ dvb_dmx_swfilter_packets(&dm1105dvb->demux,
++ &dm1105dvb->ts_buf[oldwrp], nbpackets);
+ }
+
+ static irqreturn_t dm1105dvb_irq(int irq, void *dev_id)
+ {
+ struct dm1105dvb *dm1105dvb = dev_id;
+- unsigned int piece;
+- unsigned int nbpackets;
+- u32 command;
+- u32 nextwrp;
+- u32 oldwrp;
+
+ /* Read-Write INSTS Ack's Interrupt for DM1105 chip 16.03.2008 */
+ unsigned int intsts = inb(dm_io_mem(DM1105_INTSTS));
+@@ -447,71 +438,25 @@ static irqreturn_t dm1105dvb_irq(int irq, void *dev_id)
+ switch (intsts) {
+ case INTSTS_TSIRQ:
+ case (INTSTS_TSIRQ | INTSTS_IR):
+- nextwrp = inl(dm_io_mem(DM1105_WRP)) -
+- inl(dm_io_mem(DM1105_STADR)) ;
+- oldwrp = dm1105dvb->wrp;
+- spin_lock(&dm1105dvb->lock);
+- if (!((dm1105dvb->ts_buf[oldwrp] == 0x47) &&
+- (dm1105dvb->ts_buf[oldwrp + 188] == 0x47) &&
+- (dm1105dvb->ts_buf[oldwrp + 188 * 2] == 0x47))) {
+- dm1105dvb->PacketErrorCount++;
+- /* bad packet found */
+- if ((dm1105dvb->PacketErrorCount >= 2) &&
+- (dm1105dvb->dmarst == 0)) {
+- outb(1, dm_io_mem(DM1105_RST));
+- dm1105dvb->wrp = 0;
+- dm1105dvb->PacketErrorCount = 0;
+- dm1105dvb->dmarst = 0;
+- spin_unlock(&dm1105dvb->lock);
+- return IRQ_HANDLED;
+- }
+- }
+- if (nextwrp < oldwrp) {
+- piece = dm1105dvb->buffer_size - oldwrp;
+- memcpy(dm1105dvb->ts_buf + dm1105dvb->buffer_size, dm1105dvb->ts_buf, nextwrp);
+- nbpackets = (piece + nextwrp)/188;
+- } else {
+- nbpackets = (nextwrp - oldwrp)/188;
+- }
+- dvb_dmx_swfilter_packets(&dm1105dvb->demux, &dm1105dvb->ts_buf[oldwrp], nbpackets);
+- dm1105dvb->wrp = nextwrp;
+- spin_unlock(&dm1105dvb->lock);
++ dm1105dvb->nextwrp = inl(dm_io_mem(DM1105_WRP)) -
++ inl(dm_io_mem(DM1105_STADR));
++ schedule_work(&dm1105dvb->work);
+ break;
+ case INTSTS_IR:
+- command = inl(dm_io_mem(DM1105_IRCODE));
+- if (ir_debug)
+- printk("dm1105: received byte 0x%04x\n", command);
+-
+- dm1105dvb->ir.ir_command = command;
+- tasklet_schedule(&dm1105dvb->ir.ir_tasklet);
++ dm1105dvb->ir.ir_command = inl(dm_io_mem(DM1105_IRCODE));
++ schedule_work(&dm1105dvb->ir.work);
+ break;
+ }
+- return IRQ_HANDLED;
+-
+-
+-}
+-
+-/* register with input layer */
+-static void input_register_keys(struct infrared *ir)
+-{
+- int i;
+
+- memset(ir->input_dev->keybit, 0, sizeof(ir->input_dev->keybit));
+-
+- for (i = 0; i < ARRAY_SIZE(ir->key_map); i++)
+- set_bit(ir->key_map[i], ir->input_dev->keybit);
+-
+- ir->input_dev->keycode = ir->key_map;
+- ir->input_dev->keycodesize = sizeof(ir->key_map[0]);
+- ir->input_dev->keycodemax = ARRAY_SIZE(ir->key_map);
++ return IRQ_HANDLED;
+ }
+
+ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105)
+ {
+ struct input_dev *input_dev;
+- int err;
+-
+- dm1105dvb_local = dm1105;
++ IR_KEYTAB_TYPE *ir_codes = ir_codes_dm1105_nec;
++ int ir_type = IR_TYPE_OTHER;
++ int err = -ENOMEM;
+
+ input_dev = input_allocate_device();
+ if (!input_dev)
+@@ -521,12 +466,11 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105)
+ snprintf(dm1105->ir.input_phys, sizeof(dm1105->ir.input_phys),
+ "pci-%s/ir0", pci_name(dm1105->pdev));
+
+- input_dev->evbit[0] = BIT(EV_KEY);
++ ir_input_init(input_dev, &dm1105->ir.ir, ir_type, ir_codes);
+ input_dev->name = "DVB on-card IR receiver";
+-
+ input_dev->phys = dm1105->ir.input_phys;
+ input_dev->id.bustype = BUS_PCI;
+- input_dev->id.version = 2;
++ input_dev->id.version = 1;
+ if (dm1105->pdev->subsystem_vendor) {
+ input_dev->id.vendor = dm1105->pdev->subsystem_vendor;
+ input_dev->id.product = dm1105->pdev->subsystem_device;
+@@ -534,25 +478,22 @@ int __devinit dm1105_ir_init(struct dm1105dvb *dm1105)
+ input_dev->id.vendor = dm1105->pdev->vendor;
+ input_dev->id.product = dm1105->pdev->device;
+ }
++
+ input_dev->dev.parent = &dm1105->pdev->dev;
+- /* initial keymap */
+- memcpy(dm1105->ir.key_map, ir_codes_dm1105_nec, sizeof dm1105->ir.key_map);
+- input_register_keys(&dm1105->ir);
++
++ INIT_WORK(&dm1105->ir.work, dm1105_emit_key);
++
+ err = input_register_device(input_dev);
+ if (err) {
+ input_free_device(input_dev);
+ return err;
+ }
+
+- tasklet_init(&dm1105->ir.ir_tasklet, dm1105_emit_key, (unsigned long) &dm1105->ir);
+-
+ return 0;
+ }
+
+-
+ void __devexit dm1105_ir_exit(struct dm1105dvb *dm1105)
+ {
+- tasklet_kill(&dm1105->ir.ir_tasklet);
+ input_unregister_device(dm1105->ir.input_dev);
+
+ }
+@@ -710,7 +651,7 @@ static int __devinit dm1105_probe(struct pci_dev *pdev,
+
+ dm1105dvb = kzalloc(sizeof(struct dm1105dvb), GFP_KERNEL);
+ if (!dm1105dvb)
+- goto out;
++ return -ENOMEM;
+
+ dm1105dvb->pdev = pdev;
+ dm1105dvb->buffer_size = 5 * DM1105_DMA_BYTES;
+@@ -740,13 +681,9 @@ static int __devinit dm1105_probe(struct pci_dev *pdev,
+ spin_lock_init(&dm1105dvb->lock);
+ pci_set_drvdata(pdev, dm1105dvb);
+
+- ret = request_irq(pdev->irq, dm1105dvb_irq, IRQF_SHARED, DRIVER_NAME, dm1105dvb);
+- if (ret < 0)
+- goto err_pci_iounmap;
+-
+ ret = dm1105dvb_hw_init(dm1105dvb);
+ if (ret < 0)
+- goto err_free_irq;
++ goto err_pci_iounmap;
+
+ /* i2c */
+ i2c_set_adapdata(&dm1105dvb->i2c_adap, dm1105dvb);
+@@ -813,8 +750,15 @@ static int __devinit dm1105_probe(struct pci_dev *pdev,
+
+ dvb_net_init(dvb_adapter, &dm1105dvb->dvbnet, dmx);
+ dm1105_ir_init(dm1105dvb);
+-out:
+- return ret;
++
++ INIT_WORK(&dm1105dvb->work, dm1105_dmx_buffer);
++
++ ret = request_irq(pdev->irq, dm1105dvb_irq, IRQF_SHARED,
++ DRIVER_NAME, dm1105dvb);
++ if (ret < 0)
++ goto err_free_irq;
++
++ return 0;
+
+ err_disconnect_frontend:
+ dmx->disconnect_frontend(dmx);
+@@ -843,7 +787,7 @@ err_pci_disable_device:
+ err_kfree:
+ pci_set_drvdata(pdev, NULL);
+ kfree(dm1105dvb);
+- goto out;
++ return ret;
+ }
+
+ static void __devexit dm1105_remove(struct pci_dev *pdev)
+diff --git a/drivers/media/dvb/dvb-core/dmxdev.c b/drivers/media/dvb/dvb-core/dmxdev.c
+index 069d847..c35fbb8 100644
+--- a/drivers/media/dvb/dvb-core/dmxdev.c
++++ b/drivers/media/dvb/dvb-core/dmxdev.c
+@@ -1024,7 +1024,7 @@ static int dvb_demux_release(struct inode *inode, struct file *file)
+ return ret;
+ }
+
+-static struct file_operations dvb_demux_fops = {
++static const struct file_operations dvb_demux_fops = {
+ .owner = THIS_MODULE,
+ .read = dvb_demux_read,
+ .ioctl = dvb_demux_ioctl,
+diff --git a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
+index 7e3aeaa..cb22da5 100644
+--- a/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
++++ b/drivers/media/dvb/dvb-core/dvb_ca_en50221.c
+@@ -1607,7 +1607,7 @@ static unsigned int dvb_ca_en50221_io_poll(struct file *file, poll_table * wait)
+ EXPORT_SYMBOL(dvb_ca_en50221_init);
+
+
+-static struct file_operations dvb_ca_fops = {
++static const struct file_operations dvb_ca_fops = {
+ .owner = THIS_MODULE,
+ .read = dvb_ca_en50221_io_read,
+ .write = dvb_ca_en50221_io_write,
+diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
+index 8dcb3fb..ebc7815 100644
+--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
++++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
+@@ -1875,7 +1875,7 @@ static int dvb_frontend_release(struct inode *inode, struct file *file)
+ return ret;
+ }
+
+-static struct file_operations dvb_frontend_fops = {
++static const struct file_operations dvb_frontend_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = dvb_generic_ioctl,
+ .poll = dvb_frontend_poll,
+diff --git a/drivers/media/dvb/dvb-core/dvb_net.c b/drivers/media/dvb/dvb-core/dvb_net.c
+index f6ba846..8280f8d 100644
+--- a/drivers/media/dvb/dvb-core/dvb_net.c
++++ b/drivers/media/dvb/dvb-core/dvb_net.c
+@@ -1459,7 +1459,7 @@ static int dvb_net_close(struct inode *inode, struct file *file)
+ }
+
+
+-static struct file_operations dvb_net_fops = {
++static const struct file_operations dvb_net_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = dvb_net_ioctl,
+ .open = dvb_generic_open,
+diff --git a/drivers/media/dvb/dvb-core/dvbdev.c b/drivers/media/dvb/dvb-core/dvbdev.c
+index 6a32680..a454ee8 100644
+--- a/drivers/media/dvb/dvb-core/dvbdev.c
++++ b/drivers/media/dvb/dvb-core/dvbdev.c
+@@ -228,8 +228,8 @@ int dvb_register_device(struct dvb_adapter *adap, struct dvb_device **pdvbdev,
+ dvbdev->fops = dvbdevfops;
+ init_waitqueue_head (&dvbdev->wait_queue);
+
+- memcpy(dvbdev->fops, template->fops, sizeof(struct file_operations));
+- dvbdev->fops->owner = adap->module;
++ memcpy(dvbdevfops, template->fops, sizeof(struct file_operations));
++ dvbdevfops->owner = adap->module;
+
+ list_add_tail (&dvbdev->list_head, &adap->device_list);
+
+diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h
+index dca49cf..7992730 100644
+--- a/drivers/media/dvb/dvb-core/dvbdev.h
++++ b/drivers/media/dvb/dvb-core/dvbdev.h
+@@ -71,7 +71,7 @@ struct dvb_adapter {
+
+ struct dvb_device {
+ struct list_head list_head;
+- struct file_operations *fops;
++ const struct file_operations *fops;
+ struct dvb_adapter *adapter;
+ int type;
+ int minor;
+diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
+index 49f7b20..6103caa 100644
+--- a/drivers/media/dvb/dvb-usb/Kconfig
++++ b/drivers/media/dvb/dvb-usb/Kconfig
+@@ -25,7 +25,7 @@ config DVB_USB_A800
+ depends on DVB_USB
+ select DVB_DIB3000MC
+ select DVB_PLL if !DVB_FE_CUSTOMISE
+- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE
++ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver.
+
+@@ -34,7 +34,7 @@ config DVB_USB_DIBUSB_MB
+ depends on DVB_USB
+ select DVB_PLL if !DVB_FE_CUSTOMISE
+ select DVB_DIB3000MB
+- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE
++ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
+ help
+ Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by
+ DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-B demodulator.
+@@ -55,7 +55,7 @@ config DVB_USB_DIBUSB_MC
+ tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)"
+ depends on DVB_USB
+ select DVB_DIB3000MC
+- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE
++ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
+ help
+ Support for USB2.0 DVB-T receivers based on reference designs made by
+ DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-C/P demodulator.
+@@ -69,15 +69,17 @@ config DVB_USB_DIBUSB_MC
+ config DVB_USB_DIB0700
+ tristate "DiBcom DiB0700 USB DVB devices (see help for supported devices)"
+ depends on DVB_USB
+- select DVB_DIB7000P
+- select DVB_DIB7000M
+- select DVB_DIB3000MC
++ select DVB_DIB7000P if !DVB_FE_CUSTOMISE
++ select DVB_DIB7000M if !DVB_FE_CUSTOMISE
++ select DVB_DIB3000MC if !DVB_FE_CUSTOMISE
+ select DVB_S5H1411 if !DVB_FE_CUSTOMISE
+- select DVB_TUNER_DIB0070
+- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_MT2266 if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMIZE
++ select DVB_LGDT3305 if !DVB_FE_CUSTOMISE
++ select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE
++ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_MT2266 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE
+ help
+ Support for USB2.0/1.1 DVB receivers based on the DiB0700 USB bridge. The
+ USB bridge is also present in devices having the DiB7700 DVB-T-USB
+@@ -95,7 +97,8 @@ config DVB_USB_UMT_010
+ depends on DVB_USB
+ select DVB_PLL if !DVB_FE_CUSTOMISE
+ select DVB_DIB3000MC
+- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE
++ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
++ select DVB_MT352 if !DVB_FE_CUSTOMISE
+ help
+ Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver.
+
+@@ -108,10 +111,11 @@ config DVB_USB_CXUSB
+ select DVB_MT352 if !DVB_FE_CUSTOMISE
+ select DVB_ZL10353 if !DVB_FE_CUSTOMISE
+ select DVB_DIB7000P if !DVB_FE_CUSTOMISE
++ select DVB_LGS8GL5 if !DVB_FE_CUSTOMISE
+ select DVB_TUNER_DIB0070 if !DVB_FE_CUSTOMISE
+- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE
++ select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to support the Conexant USB2.0 hybrid reference design.
+ Currently, only DVB and ATSC modes are supported, analog mode
+@@ -125,8 +129,8 @@ config DVB_USB_M920X
+ depends on DVB_USB
+ select DVB_MT352 if !DVB_FE_CUSTOMISE
+ select DVB_TDA1004X if !DVB_FE_CUSTOMISE
+- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMIZE
++ select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to support the MSI Mega Sky 580 USB2.0 DVB-T receiver.
+ Currently, only devices with a product id of
+@@ -137,7 +141,7 @@ config DVB_USB_GL861
+ tristate "Genesys Logic GL861 USB2.0 support"
+ depends on DVB_USB
+ select DVB_ZL10353 if !DVB_FE_CUSTOMISE
+- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE
++ select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to support the MSI Megasky 580 (55801) DVB-T USB2.0
+ receiver with USB ID 0db0:5581.
+@@ -146,7 +150,7 @@ config DVB_USB_AU6610
+ tristate "Alcor Micro AU6610 USB2.0 support"
+ depends on DVB_USB
+ select DVB_ZL10353 if !DVB_FE_CUSTOMISE
+- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE
++ select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to support the Sigmatek DVB-110 DVB-T USB2.0 receiver.
+
+@@ -199,7 +203,7 @@ config DVB_USB_NOVA_T_USB2
+ depends on DVB_USB
+ select DVB_DIB3000MC
+ select DVB_PLL if !DVB_FE_CUSTOMISE
+- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE
++ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver.
+
+@@ -235,8 +239,8 @@ config DVB_USB_OPERA1
+ config DVB_USB_AF9005
+ tristate "Afatech AF9005 DVB-T USB1.1 support"
+ depends on DVB_USB && EXPERIMENTAL
+- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE
++ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to support the Afatech AF9005 based DVB-T USB1.1 receiver
+ and the TerraTec Cinergy T USB XE (Rev.1)
+@@ -284,7 +288,7 @@ config DVB_USB_DTV5100
+ tristate "AME DTV-5100 USB2.0 DVB-T support"
+ depends on DVB_USB
+ select DVB_ZL10353 if !DVB_FE_CUSTOMISE
+- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE
++ select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to support the AME DTV-5100 USB2.0 DVB-T receiver.
+
+@@ -293,9 +297,18 @@ config DVB_USB_AF9015
+ depends on DVB_USB && EXPERIMENTAL
+ select DVB_AF9013
+ select DVB_PLL if !DVB_FE_CUSTOMISE
+- select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE
++ select MEDIA_TUNER_MT2060 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_QT1010 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_MC44S803 if !MEDIA_TUNER_CUSTOMISE
+ help
+ Say Y here to support the Afatech AF9015 based DVB-T USB2.0 receiver
++
++config DVB_USB_CE6230
++ tristate "Intel CE6230 DVB-T USB2.0 support"
++ depends on DVB_USB && EXPERIMENTAL
++ select DVB_ZL10353
++ select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE
++ help
++ Say Y here to support the Intel CE6230 DVB-T USB2.0 receiver
+diff --git a/drivers/media/dvb/dvb-usb/Makefile b/drivers/media/dvb/dvb-usb/Makefile
+index 3122b7c..f92734e 100644
+--- a/drivers/media/dvb/dvb-usb/Makefile
++++ b/drivers/media/dvb/dvb-usb/Makefile
+@@ -76,6 +76,8 @@ obj-$(CONFIG_DVB_USB_AF9015) += dvb-usb-af9015.o
+ dvb-usb-cinergyT2-objs = cinergyT2-core.o cinergyT2-fe.o
+ obj-$(CONFIG_DVB_USB_CINERGY_T2) += dvb-usb-cinergyT2.o
+
++dvb-usb-ce6230-objs = ce6230.o
++obj-$(CONFIG_DVB_USB_CE6230) += dvb-usb-ce6230.o
+
+ EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core/ -Idrivers/media/dvb/frontends/
+ # due to tuner-xc3028
+diff --git a/drivers/media/dvb/dvb-usb/af9015.c b/drivers/media/dvb/dvb-usb/af9015.c
+index 6a97a40..f0ba8b0 100644
+--- a/drivers/media/dvb/dvb-usb/af9015.c
++++ b/drivers/media/dvb/dvb-usb/af9015.c
+@@ -27,9 +27,7 @@
+ #include "qt1010.h"
+ #include "tda18271.h"
+ #include "mxl5005s.h"
+-#if 0
+-#include "mc44s80x.h"
+-#endif
++#include "mc44s803.h"
+
+ static int dvb_usb_af9015_debug;
+ module_param_named(debug, dvb_usb_af9015_debug, int, 0644);
+@@ -37,9 +35,6 @@ MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
+ static int dvb_usb_af9015_remote;
+ module_param_named(remote, dvb_usb_af9015_remote, int, 0644);
+ MODULE_PARM_DESC(remote, "select remote");
+-static int dvb_usb_af9015_dual_mode;
+-module_param_named(dual_mode, dvb_usb_af9015_dual_mode, int, 0644);
+-MODULE_PARM_DESC(dual_mode, "enable dual mode");
+ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
+ static DEFINE_MUTEX(af9015_usb_mutex);
+@@ -283,6 +278,21 @@ Due to that the only way to select correct tuner is use demodulator I2C-gate.
+ req.data = &msg[i+1].buf[0];
+ ret = af9015_ctrl_msg(d, &req);
+ i += 2;
++ } else if (msg[i].flags & I2C_M_RD) {
++ ret = -EINVAL;
++ if (msg[i].addr ==
++ af9015_af9013_config[0].demod_address)
++ goto error;
++ else
++ req.cmd = READ_I2C;
++ req.i2c_addr = msg[i].addr;
++ req.addr = addr;
++ req.mbox = mbox;
++ req.addr_len = addr_len;
++ req.data_len = msg[i].len;
++ req.data = &msg[i].buf[0];
++ ret = af9015_ctrl_msg(d, &req);
++ i += 1;
+ } else {
+ if (msg[i].addr ==
+ af9015_af9013_config[0].demod_address)
+@@ -748,6 +758,16 @@ static int af9015_read_config(struct usb_device *udev)
+ af9015_config.ir_table_size =
+ ARRAY_SIZE(af9015_ir_table_digittrade);
+ break;
++ case AF9015_REMOTE_AVERMEDIA_KS:
++ af9015_properties[i].rc_key_map =
++ af9015_rc_keys_avermedia;
++ af9015_properties[i].rc_key_map_size =
++ ARRAY_SIZE(af9015_rc_keys_avermedia);
++ af9015_config.ir_table =
++ af9015_ir_table_avermedia_ks;
++ af9015_config.ir_table_size =
++ ARRAY_SIZE(af9015_ir_table_avermedia_ks);
++ break;
+ }
+ } else {
+ switch (le16_to_cpu(udev->descriptor.idVendor)) {
+@@ -836,9 +856,6 @@ static int af9015_read_config(struct usb_device *udev)
+ goto error;
+ af9015_config.dual_mode = val;
+ deb_info("%s: TS mode:%d\n", __func__, af9015_config.dual_mode);
+- /* disable dual mode by default because it is buggy */
+- if (!dvb_usb_af9015_dual_mode)
+- af9015_config.dual_mode = 0;
+
+ /* Set adapter0 buffer size according to USB port speed, adapter1 buffer
+ size can be static because it is enabled only USB2.0 */
+@@ -935,7 +952,6 @@ static int af9015_read_config(struct usb_device *udev)
+ switch (val) {
+ case AF9013_TUNER_ENV77H11D5:
+ case AF9013_TUNER_MT2060:
+- case AF9013_TUNER_MC44S803:
+ case AF9013_TUNER_QT1010:
+ case AF9013_TUNER_UNKNOWN:
+ case AF9013_TUNER_MT2060_2:
+@@ -948,6 +964,10 @@ static int af9015_read_config(struct usb_device *udev)
+ case AF9013_TUNER_MXL5005R:
+ af9015_af9013_config[i].rf_spec_inv = 0;
+ break;
++ case AF9013_TUNER_MC44S803:
++ af9015_af9013_config[i].gpio[1] = AF9013_GPIO_LO;
++ af9015_af9013_config[i].rf_spec_inv = 1;
++ break;
+ default:
+ warn("tuner id:%d not supported, please report!", val);
+ return -ENODEV;
+@@ -1135,6 +1155,11 @@ static struct mxl5005s_config af9015_mxl5005_config = {
+ .AgcMasterByte = 0x00,
+ };
+
++static struct mc44s803_config af9015_mc44s803_config = {
++ .i2c_address = 0xc0,
++ .dig_out = 1,
++};
++
+ static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
+ {
+ struct af9015_state *state = adap->dev->priv;
+@@ -1179,15 +1204,8 @@ static int af9015_tuner_attach(struct dvb_usb_adapter *adap)
+ DVB_PLL_TDA665X) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_MC44S803:
+-#if 0
+- ret = dvb_attach(mc44s80x_attach, adap->fe, i2c_adap)
+- == NULL ? -ENODEV : 0;
+-#else
+- ret = -ENODEV;
+- info("Freescale MC44S803 tuner found but no driver for that" \
+- "tuner. Look at the Linuxtv.org for tuner driver" \
+- "status.");
+-#endif
++ ret = dvb_attach(mc44s803_attach, adap->fe, i2c_adap,
++ &af9015_mc44s803_config) == NULL ? -ENODEV : 0;
+ break;
+ case AF9013_TUNER_UNKNOWN:
+ default:
+@@ -1218,6 +1236,7 @@ static struct usb_device_id af9015_usb_table[] = {
+ {USB_DEVICE(USB_VID_AVERMEDIA, USB_PID_AVERMEDIA_A309)},
+ /* 15 */{USB_DEVICE(USB_VID_MSI_2, USB_PID_MSI_DIGI_VOX_MINI_III)},
+ {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U)},
++ {USB_DEVICE(USB_VID_KWORLD_2, USB_PID_KWORLD_395U_2)},
+ {0},
+ };
+ MODULE_DEVICE_TABLE(usb, af9015_usb_table);
+@@ -1417,7 +1436,8 @@ static struct dvb_usb_device_properties af9015_properties[] = {
+ {
+ .name = "KWorld USB DVB-T TV Stick II " \
+ "(VS-DVB-T 395U)",
+- .cold_ids = {&af9015_usb_table[16], NULL},
++ .cold_ids = {&af9015_usb_table[16],
++ &af9015_usb_table[17], NULL},
+ .warm_ids = {NULL},
+ },
+ }
+diff --git a/drivers/media/dvb/dvb-usb/af9015.h b/drivers/media/dvb/dvb-usb/af9015.h
+index 21c7782..00e2571 100644
+--- a/drivers/media/dvb/dvb-usb/af9015.h
++++ b/drivers/media/dvb/dvb-usb/af9015.h
+@@ -124,6 +124,7 @@ enum af9015_remote {
+ AF9015_REMOTE_MSI_DIGIVOX_MINI_II_V3,
+ AF9015_REMOTE_MYGICTV_U718,
+ AF9015_REMOTE_DIGITTRADE_DVB_T,
++ AF9015_REMOTE_AVERMEDIA_KS,
+ };
+
+ /* Leadtek WinFast DTV Dongle Gold */
+@@ -597,6 +598,36 @@ static u8 af9015_ir_table_avermedia[] = {
+ 0x03, 0xfc, 0x03, 0xfc, 0x0e, 0x05, 0x00,
+ };
+
++static u8 af9015_ir_table_avermedia_ks[] = {
++ 0x05, 0xfa, 0x01, 0xfe, 0x12, 0x05, 0x00,
++ 0x05, 0xfa, 0x02, 0xfd, 0x0e, 0x05, 0x00,
++ 0x05, 0xfa, 0x03, 0xfc, 0x0d, 0x05, 0x00,
++ 0x05, 0xfa, 0x04, 0xfb, 0x2e, 0x05, 0x00,
++ 0x05, 0xfa, 0x05, 0xfa, 0x2d, 0x05, 0x00,
++ 0x05, 0xfa, 0x06, 0xf9, 0x10, 0x05, 0x00,
++ 0x05, 0xfa, 0x07, 0xf8, 0x0f, 0x05, 0x00,
++ 0x05, 0xfa, 0x08, 0xf7, 0x3d, 0x05, 0x00,
++ 0x05, 0xfa, 0x09, 0xf6, 0x1e, 0x05, 0x00,
++ 0x05, 0xfa, 0x0a, 0xf5, 0x1f, 0x05, 0x00,
++ 0x05, 0xfa, 0x0b, 0xf4, 0x20, 0x05, 0x00,
++ 0x05, 0xfa, 0x0c, 0xf3, 0x21, 0x05, 0x00,
++ 0x05, 0xfa, 0x0d, 0xf2, 0x22, 0x05, 0x00,
++ 0x05, 0xfa, 0x0e, 0xf1, 0x23, 0x05, 0x00,
++ 0x05, 0xfa, 0x0f, 0xf0, 0x24, 0x05, 0x00,
++ 0x05, 0xfa, 0x10, 0xef, 0x25, 0x05, 0x00,
++ 0x05, 0xfa, 0x11, 0xee, 0x26, 0x05, 0x00,
++ 0x05, 0xfa, 0x12, 0xed, 0x27, 0x05, 0x00,
++ 0x05, 0xfa, 0x13, 0xec, 0x04, 0x05, 0x00,
++ 0x05, 0xfa, 0x15, 0xea, 0x0a, 0x05, 0x00,
++ 0x05, 0xfa, 0x16, 0xe9, 0x11, 0x05, 0x00,
++ 0x05, 0xfa, 0x17, 0xe8, 0x15, 0x05, 0x00,
++ 0x05, 0xfa, 0x18, 0xe7, 0x16, 0x05, 0x00,
++ 0x05, 0xfa, 0x1c, 0xe3, 0x05, 0x05, 0x00,
++ 0x05, 0xfa, 0x1d, 0xe2, 0x09, 0x05, 0x00,
++ 0x05, 0xfa, 0x4d, 0xb2, 0x3f, 0x05, 0x00,
++ 0x05, 0xfa, 0x56, 0xa9, 0x3e, 0x05, 0x00
++};
++
+ /* Digittrade DVB-T USB Stick */
+ static struct dvb_usb_rc_key af9015_rc_keys_digittrade[] = {
+ { 0x01, 0x0f, KEY_LAST }, /* RETURN */
+diff --git a/drivers/media/dvb/dvb-usb/ce6230.c b/drivers/media/dvb/dvb-usb/ce6230.c
+new file mode 100644
+index 0000000..5862820
+--- /dev/null
++++ b/drivers/media/dvb/dvb-usb/ce6230.c
+@@ -0,0 +1,328 @@
++/*
++ * DVB USB Linux driver for Intel CE6230 DVB-T USB2.0 receiver
++ *
++ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
++ *
++ * 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 "ce6230.h"
++#include "zl10353.h"
++#include "mxl5005s.h"
++
++/* debug */
++static int dvb_usb_ce6230_debug;
++module_param_named(debug, dvb_usb_ce6230_debug, int, 0644);
++MODULE_PARM_DESC(debug, "set debugging level" DVB_USB_DEBUG_STATUS);
++DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
++
++static struct zl10353_config ce6230_zl10353_config;
++
++static int ce6230_rw_udev(struct usb_device *udev, struct req_t *req)
++{
++ int ret;
++ unsigned int pipe;
++ u8 request;
++ u8 requesttype;
++ u16 value;
++ u16 index;
++ u8 buf[req->data_len];
++
++ request = req->cmd;
++ value = req->value;
++ index = req->index;
++
++ switch (req->cmd) {
++ case I2C_READ:
++ case DEMOD_READ:
++ case REG_READ:
++ requesttype = (USB_TYPE_VENDOR | USB_DIR_IN);
++ break;
++ case I2C_WRITE:
++ case DEMOD_WRITE:
++ case REG_WRITE:
++ requesttype = (USB_TYPE_VENDOR | USB_DIR_OUT);
++ break;
++ default:
++ err("unknown command:%02x", req->cmd);
++ ret = -EPERM;
++ goto error;
++ }
++
++ if (requesttype == (USB_TYPE_VENDOR | USB_DIR_OUT)) {
++ /* write */
++ memcpy(buf, req->data, req->data_len);
++ pipe = usb_sndctrlpipe(udev, 0);
++ } else {
++ /* read */
++ pipe = usb_rcvctrlpipe(udev, 0);
++ }
++
++ msleep(1); /* avoid I2C errors */
++
++ ret = usb_control_msg(udev, pipe, request, requesttype, value, index,
++ buf, sizeof(buf), CE6230_USB_TIMEOUT);
++
++ ce6230_debug_dump(request, requesttype, value, index, buf,
++ req->data_len, deb_xfer);
++
++ if (ret < 0)
++ deb_info("%s: usb_control_msg failed:%d\n", __func__, ret);
++ else
++ ret = 0;
++
++ /* read request, copy returned data to return buf */
++ if (!ret && requesttype == (USB_TYPE_VENDOR | USB_DIR_IN))
++ memcpy(req->data, buf, req->data_len);
++
++error:
++ return ret;
++}
++
++static int ce6230_ctrl_msg(struct dvb_usb_device *d, struct req_t *req)
++{
++ return ce6230_rw_udev(d->udev, req);
++}
++
++/* I2C */
++static int ce6230_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msg[],
++ int num)
++{
++ struct dvb_usb_device *d = i2c_get_adapdata(adap);
++ int i = 0;
++ struct req_t req;
++ int ret = 0;
++ memset(&req, 0, sizeof(&req));
++
++ if (num > 2)
++ return -EINVAL;
++
++ if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
++ return -EAGAIN;
++
++ while (i < num) {
++ if (num > i + 1 && (msg[i+1].flags & I2C_M_RD)) {
++ if (msg[i].addr ==
++ ce6230_zl10353_config.demod_address) {
++ req.cmd = DEMOD_READ;
++ req.value = msg[i].addr >> 1;
++ req.index = msg[i].buf[0];
++ req.data_len = msg[i+1].len;
++ req.data = &msg[i+1].buf[0];
++ ret = ce6230_ctrl_msg(d, &req);
++ } else {
++ err("i2c read not implemented");
++ ret = -EPERM;
++ }
++ i += 2;
++ } else {
++ if (msg[i].addr ==
++ ce6230_zl10353_config.demod_address) {
++ req.cmd = DEMOD_WRITE;
++ req.value = msg[i].addr >> 1;
++ req.index = msg[i].buf[0];
++ req.data_len = msg[i].len-1;
++ req.data = &msg[i].buf[1];
++ ret = ce6230_ctrl_msg(d, &req);
++ } else {
++ req.cmd = I2C_WRITE;
++ req.value = 0x2000 + (msg[i].addr >> 1);
++ req.index = 0x0000;
++ req.data_len = msg[i].len;
++ req.data = &msg[i].buf[0];
++ ret = ce6230_ctrl_msg(d, &req);
++ }
++ i += 1;
++ }
++ if (ret)
++ break;
++ }
++
++ mutex_unlock(&d->i2c_mutex);
++ return ret ? ret : i;
++}
++
++static u32 ce6230_i2c_func(struct i2c_adapter *adapter)
++{
++ return I2C_FUNC_I2C;
++}
++
++static struct i2c_algorithm ce6230_i2c_algo = {
++ .master_xfer = ce6230_i2c_xfer,
++ .functionality = ce6230_i2c_func,
++};
++
++/* Callbacks for DVB USB */
++static struct zl10353_config ce6230_zl10353_config = {
++ .demod_address = 0x1e,
++ .adc_clock = 450000,
++ .if2 = 45700,
++ .no_tuner = 1,
++ .parallel_ts = 1,
++ .clock_ctl_1 = 0x34,
++ .pll_0 = 0x0e,
++};
++
++static int ce6230_zl10353_frontend_attach(struct dvb_usb_adapter *adap)
++{
++ deb_info("%s:\n", __func__);
++ adap->fe = dvb_attach(zl10353_attach, &ce6230_zl10353_config,
++ &adap->dev->i2c_adap);
++ if (adap->fe == NULL)
++ return -ENODEV;
++ return 0;
++}
++
++static struct mxl5005s_config ce6230_mxl5003s_config = {
++ .i2c_address = 0xc6,
++ .if_freq = IF_FREQ_4570000HZ,
++ .xtal_freq = CRYSTAL_FREQ_16000000HZ,
++ .agc_mode = MXL_SINGLE_AGC,
++ .tracking_filter = MXL_TF_DEFAULT,
++ .rssi_enable = MXL_RSSI_ENABLE,
++ .cap_select = MXL_CAP_SEL_ENABLE,
++ .div_out = MXL_DIV_OUT_4,
++ .clock_out = MXL_CLOCK_OUT_DISABLE,
++ .output_load = MXL5005S_IF_OUTPUT_LOAD_200_OHM,
++ .top = MXL5005S_TOP_25P2,
++ .mod_mode = MXL_DIGITAL_MODE,
++ .if_mode = MXL_ZERO_IF,
++ .AgcMasterByte = 0x00,
++};
++
++static int ce6230_mxl5003s_tuner_attach(struct dvb_usb_adapter *adap)
++{
++ int ret;
++ deb_info("%s:\n", __func__);
++ ret = dvb_attach(mxl5005s_attach, adap->fe, &adap->dev->i2c_adap,
++ &ce6230_mxl5003s_config) == NULL ? -ENODEV : 0;
++ return ret;
++}
++
++static int ce6230_power_ctrl(struct dvb_usb_device *d, int onoff)
++{
++ int ret;
++ deb_info("%s: onoff:%d\n", __func__, onoff);
++
++ /* InterfaceNumber 1 / AlternateSetting 0 idle
++ InterfaceNumber 1 / AlternateSetting 1 streaming */
++ ret = usb_set_interface(d->udev, 1, onoff);
++ if (ret)
++ err("usb_set_interface failed with error:%d", ret);
++
++ return ret;
++}
++
++/* DVB USB Driver stuff */
++static struct dvb_usb_device_properties ce6230_properties;
++
++static int ce6230_probe(struct usb_interface *intf,
++ const struct usb_device_id *id)
++{
++ int ret = 0;
++ struct dvb_usb_device *d = NULL;
++
++ deb_info("%s: interface:%d\n", __func__,
++ intf->cur_altsetting->desc.bInterfaceNumber);
++
++ if (intf->cur_altsetting->desc.bInterfaceNumber == 1) {
++ ret = dvb_usb_device_init(intf, &ce6230_properties, THIS_MODULE,
++ &d, adapter_nr);
++ if (ret)
++ err("init failed with error:%d\n", ret);
++ }
++
++ return ret;
++}
++
++static struct usb_device_id ce6230_table[] = {
++ { USB_DEVICE(USB_VID_INTEL, USB_PID_INTEL_CE9500) },
++ { } /* Terminating entry */
++};
++MODULE_DEVICE_TABLE(usb, ce6230_table);
++
++static struct dvb_usb_device_properties ce6230_properties = {
++ .caps = DVB_USB_IS_AN_I2C_ADAPTER,
++
++ .usb_ctrl = DEVICE_SPECIFIC,
++ .no_reconnect = 1,
++
++ .size_of_priv = 0,
++
++ .num_adapters = 1,
++ .adapter = {
++ {
++ .frontend_attach = ce6230_zl10353_frontend_attach,
++ .tuner_attach = ce6230_mxl5003s_tuner_attach,
++ .stream = {
++ .type = USB_BULK,
++ .count = 6,
++ .endpoint = 0x82,
++ .u = {
++ .bulk = {
++ .buffersize = 512,
++ }
++ }
++ },
++ }
++ },
++
++ .power_ctrl = ce6230_power_ctrl,
++
++ .i2c_algo = &ce6230_i2c_algo,
++
++ .num_device_descs = 1,
++ .devices = {
++ {
++ .name = "Intel CE9500 reference design",
++ .cold_ids = {NULL},
++ .warm_ids = {&ce6230_table[0], NULL},
++ },
++ }
++};
++
++static struct usb_driver ce6230_driver = {
++ .name = "dvb_usb_ce6230",
++ .probe = ce6230_probe,
++ .disconnect = dvb_usb_device_exit,
++ .id_table = ce6230_table,
++};
++
++/* module stuff */
++static int __init ce6230_module_init(void)
++{
++ int ret;
++ deb_info("%s:\n", __func__);
++ ret = usb_register(&ce6230_driver);
++ if (ret)
++ err("usb_register failed with error:%d", ret);
++
++ return ret;
++}
++
++static void __exit ce6230_module_exit(void)
++{
++ deb_info("%s:\n", __func__);
++ /* deregister this driver from the USB subsystem */
++ usb_deregister(&ce6230_driver);
++}
++
++module_init(ce6230_module_init);
++module_exit(ce6230_module_exit);
++
++MODULE_AUTHOR("Antti Palosaari <crope@iki.fi>");
++MODULE_DESCRIPTION("Driver for Intel CE6230 DVB-T USB2.0");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/media/dvb/dvb-usb/ce6230.h b/drivers/media/dvb/dvb-usb/ce6230.h
+new file mode 100644
+index 0000000..97c4248
+--- /dev/null
++++ b/drivers/media/dvb/dvb-usb/ce6230.h
+@@ -0,0 +1,69 @@
++/*
++ * DVB USB Linux driver for Intel CE6230 DVB-T USB2.0 receiver
++ *
++ * Copyright (C) 2009 Antti Palosaari <crope@iki.fi>
++ *
++ * 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 _DVB_USB_CE6230_H_
++#define _DVB_USB_CE6230_H_
++
++#define DVB_USB_LOG_PREFIX "ce6230"
++#include "dvb-usb.h"
++
++#define deb_info(args...) dprintk(dvb_usb_ce6230_debug, 0x01, args)
++#define deb_rc(args...) dprintk(dvb_usb_ce6230_debug, 0x02, args)
++#define deb_xfer(args...) dprintk(dvb_usb_ce6230_debug, 0x04, args)
++#define deb_reg(args...) dprintk(dvb_usb_ce6230_debug, 0x08, args)
++#define deb_i2c(args...) dprintk(dvb_usb_ce6230_debug, 0x10, args)
++#define deb_fw(args...) dprintk(dvb_usb_ce6230_debug, 0x20, args)
++
++#define ce6230_debug_dump(r, t, v, i, b, l, func) { \
++ int loop_; \
++ func("%02x %02x %02x %02x %02x %02x %02x %02x", \
++ t, r, v & 0xff, v >> 8, i & 0xff, i >> 8, l & 0xff, l >> 8); \
++ if (t == (USB_TYPE_VENDOR | USB_DIR_OUT)) \
++ func(" >>> "); \
++ else \
++ func(" <<< "); \
++ for (loop_ = 0; loop_ < l; loop_++) \
++ func("%02x ", b[loop_]); \
++ func("\n");\
++}
++
++#define CE6230_USB_TIMEOUT 1000
++
++struct req_t {
++ u8 cmd; /* [1] */
++ u16 value; /* [2|3] */
++ u16 index; /* [4|5] */
++ u16 data_len; /* [6|7] */
++ u8 *data;
++};
++
++enum ce6230_cmd {
++ CONFIG_READ = 0xd0, /* rd 0 (unclear) */
++ UNKNOWN_WRITE = 0xc7, /* wr 7 (unclear) */
++ I2C_READ = 0xd9, /* rd 9 (unclear) */
++ I2C_WRITE = 0xca, /* wr a */
++ DEMOD_READ = 0xdb, /* rd b */
++ DEMOD_WRITE = 0xcc, /* wr c */
++ REG_READ = 0xde, /* rd e */
++ REG_WRITE = 0xcf, /* wr f */
++};
++
++#endif
+diff --git a/drivers/media/dvb/dvb-usb/dib0700_core.c b/drivers/media/dvb/dvb-usb/dib0700_core.c
+index 200b215..db7f7f7 100644
+--- a/drivers/media/dvb/dvb-usb/dib0700_core.c
++++ b/drivers/media/dvb/dvb-usb/dib0700_core.c
+@@ -158,6 +158,10 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
+ err("i2c read error (status = %d)\n", result);
+ break;
+ }
++
++ deb_data("<<< ");
++ debug_dump(msg[i].buf, msg[i].len, deb_data);
++
+ } else {
+ /* Write request */
+ buf[0] = REQUEST_NEW_I2C_WRITE;
+@@ -169,6 +173,9 @@ static int dib0700_i2c_xfer_new(struct i2c_adapter *adap, struct i2c_msg *msg,
+ /* The Actual i2c payload */
+ memcpy(&buf[4], msg[i].buf, msg[i].len);
+
++ deb_data(">>> ");
++ debug_dump(buf, msg[i].len + 4, deb_data);
++
+ result = usb_control_msg(d->udev,
+ usb_sndctrlpipe(d->udev, 0),
+ REQUEST_NEW_I2C_WRITE,
+@@ -211,7 +218,8 @@ static int dib0700_i2c_xfer_legacy(struct i2c_adapter *adap,
+
+ /* special thing in the current firmware: when length is zero the read-failed */
+ if ((len = dib0700_ctrl_rd(d, buf, msg[i].len + 2, msg[i+1].buf, msg[i+1].len)) <= 0) {
+- deb_info("I2C read failed on address %x\n", msg[i].addr);
++ deb_info("I2C read failed on address 0x%02x\n",
++ msg[i].addr);
+ break;
+ }
+
+diff --git a/drivers/media/dvb/dvb-usb/dib0700_devices.c b/drivers/media/dvb/dvb-usb/dib0700_devices.c
+index 635d30a..8ddbadf 100644
+--- a/drivers/media/dvb/dvb-usb/dib0700_devices.c
++++ b/drivers/media/dvb/dvb-usb/dib0700_devices.c
+@@ -17,6 +17,8 @@
+ #include "xc5000.h"
+ #include "s5h1411.h"
+ #include "dib0070.h"
++#include "lgdt3305.h"
++#include "mxl5007t.h"
+
+ static int force_lna_activation;
+ module_param(force_lna_activation, int, 0644);
+@@ -262,7 +264,12 @@ static int stk7700P2_frontend_attach(struct dvb_usb_adapter *adap)
+ 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);
++ if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18,
++ stk7700d_dib7000p_mt2266_config)
++ != 0) {
++ err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__);
++ return -ENODEV;
++ }
+ }
+
+ adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap,0x80+(adap->id << 1),
+@@ -284,7 +291,12 @@ static int stk7700d_frontend_attach(struct dvb_usb_adapter *adap)
+ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
+ msleep(10);
+ dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
+- dib7000p_i2c_enumeration(&adap->dev->i2c_adap,2,18,stk7700d_dib7000p_mt2266_config);
++ if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 2, 18,
++ stk7700d_dib7000p_mt2266_config)
++ != 0) {
++ err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n", __func__);
++ return -ENODEV;
++ }
+ }
+
+ adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap,0x80+(adap->id << 1),
+@@ -421,8 +433,12 @@ static int stk7700ph_frontend_attach(struct dvb_usb_adapter *adap)
+ dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
+ msleep(10);
+
+- dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18,
+- &stk7700ph_dib7700_xc3028_config);
++ if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18,
++ &stk7700ph_dib7700_xc3028_config) != 0) {
++ err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n",
++ __func__);
++ return -ENODEV;
++ }
+
+ adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
+ &stk7700ph_dib7700_xc3028_config);
+@@ -1187,8 +1203,12 @@ static int stk7070p_frontend_attach(struct dvb_usb_adapter *adap)
+ msleep(10);
+ dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
+
+- dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18,
+- &dib7070p_dib7000p_config);
++ if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 1, 18,
++ &dib7070p_dib7000p_config) != 0) {
++ err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n",
++ __func__);
++ return -ENODEV;
++ }
+
+ adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80,
+ &dib7070p_dib7000p_config);
+@@ -1244,7 +1264,12 @@ static int stk7070pd_frontend_attach0(struct dvb_usb_adapter *adap)
+ msleep(10);
+ dib0700_set_gpio(adap->dev, GPIO0, GPIO_OUT, 1);
+
+- dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 2, 18, stk7070pd_dib7000p_config);
++ if (dib7000p_i2c_enumeration(&adap->dev->i2c_adap, 2, 18,
++ stk7070pd_dib7000p_config) != 0) {
++ err("%s: dib7000p_i2c_enumeration failed. Cannot continue\n",
++ __func__);
++ return -ENODEV;
++ }
+
+ adap->fe = dvb_attach(dib7000p_attach, &adap->dev->i2c_adap, 0x80, &stk7070pd_dib7000p_config[0]);
+ return adap->fe == NULL ? -ENODEV : 0;
+@@ -1347,6 +1372,72 @@ static int xc5000_tuner_attach(struct dvb_usb_adapter *adap)
+ == NULL ? -ENODEV : 0;
+ }
+
++static struct lgdt3305_config hcw_lgdt3305_config = {
++ .i2c_addr = 0x0e,
++ .mpeg_mode = LGDT3305_MPEG_PARALLEL,
++ .tpclk_edge = LGDT3305_TPCLK_FALLING_EDGE,
++ .tpvalid_polarity = LGDT3305_TP_VALID_LOW,
++ .deny_i2c_rptr = 0,
++ .spectral_inversion = 1,
++ .qam_if_khz = 6000,
++ .vsb_if_khz = 6000,
++ .usref_8vsb = 0x0500,
++};
++
++static struct mxl5007t_config hcw_mxl5007t_config = {
++ .xtal_freq_hz = MxL_XTAL_25_MHZ,
++ .if_freq_hz = MxL_IF_6_MHZ,
++ .invert_if = 1,
++};
++
++/* TIGER-ATSC map:
++ GPIO0 - LNA_CTR (H: LNA power enabled, L: LNA power disabled)
++ GPIO1 - ANT_SEL (H: VPA, L: MCX)
++ GPIO4 - SCL2
++ GPIO6 - EN_TUNER
++ GPIO7 - SDA2
++ GPIO10 - DEM_RST
++
++ MXL is behind LG's i2c repeater. LG is on SCL2/SDA2 gpios on the DIB
++ */
++static int lgdt3305_frontend_attach(struct dvb_usb_adapter *adap)
++{
++ struct dib0700_state *st = adap->dev->priv;
++
++ /* Make use of the new i2c functions from FW 1.20 */
++ st->fw_use_new_i2c_api = 1;
++
++ st->disable_streaming_master_mode = 1;
++
++ /* fe power enable */
++ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 0);
++ msleep(30);
++ dib0700_set_gpio(adap->dev, GPIO6, GPIO_OUT, 1);
++ msleep(30);
++
++ /* demod reset */
++ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
++ msleep(30);
++ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 0);
++ msleep(30);
++ dib0700_set_gpio(adap->dev, GPIO10, GPIO_OUT, 1);
++ msleep(30);
++
++ adap->fe = dvb_attach(lgdt3305_attach,
++ &hcw_lgdt3305_config,
++ &adap->dev->i2c_adap);
++
++ return adap->fe == NULL ? -ENODEV : 0;
++}
++
++static int mxl5007t_tuner_attach(struct dvb_usb_adapter *adap)
++{
++ return dvb_attach(mxl5007t_attach, adap->fe,
++ &adap->dev->i2c_adap, 0x60,
++ &hcw_mxl5007t_config) == NULL ? -ENODEV : 0;
++}
++
++
+ /* DVB-USB and USB stuff follows */
+ struct usb_device_id dib0700_usb_id_table[] = {
+ /* 0 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_STK7700P) },
+@@ -1396,6 +1487,12 @@ struct usb_device_id dib0700_usb_id_table[] = {
+ { USB_DEVICE(USB_VID_TERRATEC, USB_PID_TERRATEC_CINERGY_T_EXPRESS) },
+ { USB_DEVICE(USB_VID_TERRATEC,
+ USB_PID_TERRATEC_CINERGY_DT_XS_DIVERSITY_2) },
++ { USB_DEVICE(USB_VID_SONY, USB_PID_SONY_PLAYTV) },
++/* 45 */{ USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_PD378S) },
++ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_TIGER_ATSC) },
++ { USB_DEVICE(USB_VID_HAUPPAUGE, USB_PID_HAUPPAUGE_TIGER_ATSC_B210) },
++ { USB_DEVICE(USB_VID_YUAN, USB_PID_YUAN_MC770) },
++ { USB_DEVICE(USB_VID_ELGATO, USB_PID_ELGATO_EYETV_DTT) },
+ { 0 } /* Terminating entry */
+ };
+ MODULE_DEVICE_TABLE(usb, dib0700_usb_id_table);
+@@ -1595,7 +1692,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
+ },
+ },
+
+- .num_device_descs = 9,
++ .num_device_descs = 11,
+ .devices = {
+ { "DiBcom STK7070P reference design",
+ { &dib0700_usb_id_table[15], NULL },
+@@ -1633,6 +1730,14 @@ struct dvb_usb_device_properties dib0700_devices[] = {
+ { &dib0700_usb_id_table[33], NULL },
+ { NULL },
+ },
++ { "Elgato EyeTV DTT",
++ { &dib0700_usb_id_table[49], NULL },
++ { NULL },
++ },
++ { "Yuan PD378S",
++ { &dib0700_usb_id_table[45], NULL },
++ { NULL },
++ },
+ },
+
+ .rc_interval = DEFAULT_RC_INTERVAL,
+@@ -1661,7 +1766,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
+ }
+ },
+
+- .num_device_descs = 5,
++ .num_device_descs = 6,
+ .devices = {
+ { "DiBcom STK7070PD reference design",
+ { &dib0700_usb_id_table[17], NULL },
+@@ -1682,8 +1787,16 @@ struct dvb_usb_device_properties dib0700_devices[] = {
+ { "Terratec Cinergy DT USB XS Diversity",
+ { &dib0700_usb_id_table[43], NULL },
+ { NULL },
++ },
++ { "Sony PlayTV",
++ { &dib0700_usb_id_table[44], 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 = 1,
+@@ -1699,7 +1812,7 @@ struct dvb_usb_device_properties dib0700_devices[] = {
+ },
+ },
+
+- .num_device_descs = 5,
++ .num_device_descs = 7,
+ .devices = {
+ { "Terratec Cinergy HT USB XE",
+ { &dib0700_usb_id_table[27], NULL },
+@@ -1725,6 +1838,10 @@ struct dvb_usb_device_properties dib0700_devices[] = {
+ { &dib0700_usb_id_table[39], NULL },
+ { NULL },
+ },
++ { "YUAN High-Tech MC770",
++ { &dib0700_usb_id_table[48], NULL },
++ { NULL },
++ },
+ },
+ .rc_interval = DEFAULT_RC_INTERVAL,
+ .rc_key_map = dib0700_rc_keys,
+@@ -1759,6 +1876,31 @@ struct dvb_usb_device_properties dib0700_devices[] = {
+ .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 = 1,
++ .adapter = {
++ {
++ .frontend_attach = lgdt3305_frontend_attach,
++ .tuner_attach = mxl5007t_tuner_attach,
++
++ DIB0700_DEFAULT_STREAMING_CONFIG(0x02),
++
++ .size_of_priv = sizeof(struct
++ dib0700_adapter_state),
++ },
++ },
++
++ .num_device_descs = 2,
++ .devices = {
++ { "Hauppauge ATSC MiniCard (B200)",
++ { &dib0700_usb_id_table[46], NULL },
++ { NULL },
++ },
++ { "Hauppauge ATSC MiniCard (B210)",
++ { &dib0700_usb_id_table[47], NULL },
++ { NULL },
++ },
++ },
+ },
+ };
+
+diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+index 0db0c06..dc7ea21 100644
+--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
++++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+@@ -27,12 +27,14 @@
+ #define USB_VID_DIBCOM 0x10b8
+ #define USB_VID_DPOSH 0x1498
+ #define USB_VID_DVICO 0x0fe9
++#define USB_VID_ELGATO 0x0fd9
+ #define USB_VID_EMPIA 0xeb1a
+ #define USB_VID_GENPIX 0x09c0
+ #define USB_VID_GRANDTEC 0x5032
+ #define USB_VID_HANFTEK 0x15f4
+ #define USB_VID_HAUPPAUGE 0x2040
+ #define USB_VID_HYPER_PALTEK 0x1025
++#define USB_VID_INTEL 0x8086
+ #define USB_VID_KWORLD 0xeb2a
+ #define USB_VID_KWORLD_2 0x1b80
+ #define USB_VID_KYE 0x0458
+@@ -48,6 +50,7 @@
+ #define USB_VID_TERRATEC 0x0ccd
+ #define USB_VID_TELESTAR 0x10b9
+ #define USB_VID_VISIONPLUS 0x13d3
++#define USB_VID_SONY 0x1415
+ #define USB_VID_TWINHAN 0x1822
+ #define USB_VID_ULTIMA_ELECTRONIC 0x05d8
+ #define USB_VID_UNIWILL 0x1584
+@@ -95,8 +98,10 @@
+ #define USB_PID_UNIWILL_STK7700P 0x6003
+ #define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
+ #define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1
++#define USB_PID_INTEL_CE9500 0x9500
+ #define USB_PID_KWORLD_399U 0xe399
+ #define USB_PID_KWORLD_395U 0xe396
++#define USB_PID_KWORLD_395U_2 0xe39b
+ #define USB_PID_KWORLD_PC160_2T 0xc160
+ #define USB_PID_KWORLD_VSTREAM_COLD 0x17de
+ #define USB_PID_KWORLD_VSTREAM_WARM 0x17df
+@@ -149,6 +154,8 @@
+ #define USB_PID_HAUPPAUGE_MYTV_T 0x7080
+ #define USB_PID_HAUPPAUGE_NOVA_TD_STICK 0x9580
+ #define USB_PID_HAUPPAUGE_NOVA_TD_STICK_52009 0x5200
++#define USB_PID_HAUPPAUGE_TIGER_ATSC 0xb200
++#define USB_PID_HAUPPAUGE_TIGER_ATSC_B210 0xb210
+ #define USB_PID_AVERMEDIA_EXPRESS 0xb568
+ #define USB_PID_AVERMEDIA_VOLAR 0xa807
+ #define USB_PID_AVERMEDIA_VOLAR_2 0xb808
+@@ -232,9 +239,13 @@
+ #define USB_PID_ASUS_U3100 0x173f
+ #define USB_PID_YUAN_EC372S 0x1edc
+ #define USB_PID_YUAN_STK7700PH 0x1f08
++#define USB_PID_YUAN_PD378S 0x2edc
++#define USB_PID_YUAN_MC770 0x0871
+ #define USB_PID_DW2102 0x2102
+ #define USB_PID_XTENSIONS_XD_380 0x0381
+ #define USB_PID_TELESTAR_STARSTICK_2 0x8000
+ #define USB_PID_MSI_DIGI_VOX_MINI_III 0x8807
++#define USB_PID_SONY_PLAYTV 0x0003
++#define USB_PID_ELGATO_EYETV_DTT 0x0021
+
+ #endif
+diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h
+index b1de0f7..2d5352e 100644
+--- a/drivers/media/dvb/dvb-usb/dvb-usb.h
++++ b/drivers/media/dvb/dvb-usb/dvb-usb.h
+@@ -223,7 +223,7 @@ struct dvb_usb_device_properties {
+ int generic_bulk_ctrl_endpoint;
+
+ int num_device_descs;
+- struct dvb_usb_device_description devices[9];
++ struct dvb_usb_device_description devices[11];
+ };
+
+ /**
+diff --git a/drivers/media/dvb/firewire/firedtv-avc.c b/drivers/media/dvb/firewire/firedtv-avc.c
+index b55d9cc..af8fdf7 100644
+--- a/drivers/media/dvb/firewire/firedtv-avc.c
++++ b/drivers/media/dvb/firewire/firedtv-avc.c
+@@ -150,7 +150,7 @@ static void debug_fcp(const u8 *data, size_t length)
+ subunit_type = data[1] >> 3;
+ subunit_id = data[1] & 7;
+ op = subunit_type == 0x1e || subunit_id == 5 ? ~0 : data[2];
+- printk(KERN_INFO "%ssu=%x.%x l=%d: %-8s - %s\n",
++ printk(KERN_INFO "%ssu=%x.%x l=%zu: %-8s - %s\n",
+ prefix, subunit_type, subunit_id, length,
+ debug_fcp_ctype(data[0]),
+ debug_fcp_opcode(op, data, length));
+diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
+index 0026956..a206cee 100644
+--- a/drivers/media/dvb/frontends/Kconfig
++++ b/drivers/media/dvb/frontends/Kconfig
+@@ -1,17 +1,21 @@
+-menu "Customise DVB Frontends"
+- depends on DVB_CORE
+-
+ config DVB_FE_CUSTOMISE
+ bool "Customise the frontend modules to build"
++ depends on DVB_CORE
+ default N
+ help
+- This allows the user to deselect frontend drivers unnecessary
+- for their hardware from the build. Use this option with care
+- as deselecting frontends which are in fact necessary will result
+- in DVB devices which cannot be tuned due to lack of driver support.
++ This allows the user to select/deselect frontend drivers for their
++ hardware from the build.
++
++ Use this option with care as deselecting frontends which are in fact
++ necessary will result in DVB devices which cannot be tuned due to lack
++ of driver support.
+
+ If unsure say N.
+
++if DVB_FE_CUSTOMISE
++
++menu "Customise DVB Frontends"
++
+ comment "Multistandard (satellite) frontends"
+ depends on DVB_CORE
+
+@@ -55,6 +59,13 @@ config DVB_MT312
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
++config DVB_ZL10036
++ tristate "Zarlink ZL10036 silicon tuner"
++ depends on DVB_CORE && I2C
++ default m if DVB_FE_CUSTOMISE
++ help
++ A DVB-S tuner module. Say Y when you want to support this frontend.
++
+ config DVB_S5H1420
+ tristate "Samsung S5H1420 based"
+ depends on DVB_CORE && I2C
+@@ -83,6 +94,20 @@ config DVB_STV0299
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
++config DVB_STV6110
++ tristate "ST STV6110 silicon tuner"
++ depends on DVB_CORE && I2C
++ default m if DVB_FE_CUSTOMISE
++ help
++ A DVB-S silicon tuner module. Say Y when you want to support this tuner.
++
++config DVB_STV0900
++ tristate "ST STV0900 based"
++ depends on DVB_CORE && I2C
++ default m if DVB_FE_CUSTOMISE
++ help
++ A DVB-S/S2 demodulator. Say Y when you want to support this frontend.
++
+ config DVB_TDA8083
+ tristate "Philips TDA8083 based"
+ depends on DVB_CORE && I2C
+@@ -288,6 +313,13 @@ config DVB_TDA10048
+ help
+ A DVB-T tuner module. Say Y when you want to support this frontend.
+
++config DVB_AF9013
++ tristate "Afatech AF9013 demodulator"
++ depends on DVB_CORE && I2C
++ default m if DVB_FE_CUSTOMISE
++ help
++ Say Y when you want to support this frontend.
++
+ comment "DVB-C (cable) frontends"
+ depends on DVB_CORE
+
+@@ -387,6 +419,14 @@ config DVB_LGDT3304
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+ to support this frontend.
+
++config DVB_LGDT3305
++ tristate "LG Electronics LGDT3305 based"
++ depends on DVB_CORE && I2C
++ default m if DVB_FE_CUSTOMISE
++ help
++ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
++ to support this frontend.
++
+ config DVB_S5H1409
+ tristate "Samsung S5H1409 based"
+ depends on DVB_CORE && I2C
+@@ -397,7 +437,7 @@ config DVB_S5H1409
+
+ config DVB_AU8522
+ tristate "Auvitek AU8522 based"
+- depends on DVB_CORE && I2C
++ depends on DVB_CORE && I2C && VIDEO_V4L2
+ default m if DVB_FE_CUSTOMISE
+ help
+ An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
+@@ -446,11 +486,11 @@ comment "SEC control devices for DVB-S"
+ depends on DVB_CORE
+
+ config DVB_LNBP21
+- tristate "LNBP21 SEC controller"
++ tristate "LNBP21/LNBH24 SEC controllers"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+- An SEC control chip.
++ An SEC control chips.
+
+ config DVB_ISL6405
+ tristate "ISL6405 SEC controller"
+@@ -478,11 +518,6 @@ comment "Tools to develop new frontends"
+ config DVB_DUMMY_FE
+ tristate "Dummy frontend driver"
+ default n
+-
+-config DVB_AF9013
+- tristate "Afatech AF9013 demodulator"
+- depends on DVB_CORE && I2C
+- default m if DVB_FE_CUSTOMISE
+- help
+- Say Y when you want to support this frontend.
+ endmenu
++
++endif
+diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
+index af7bdf0..65a336a 100644
+--- a/drivers/media/dvb/frontends/Makefile
++++ b/drivers/media/dvb/frontends/Makefile
+@@ -7,6 +7,8 @@ EXTRA_CFLAGS += -Idrivers/media/common/tuners/
+
+ s921-objs := s921_module.o s921_core.o
+ stb0899-objs = stb0899_drv.o stb0899_algo.o
++stv0900-objs = stv0900_core.o stv0900_sw.o
++au8522-objs = au8522_dig.o au8522_decoder.o
+
+ obj-$(CONFIG_DVB_PLL) += dvb-pll.o
+ obj-$(CONFIG_DVB_STV0299) += stv0299.o
+@@ -28,6 +30,7 @@ obj-$(CONFIG_DVB_TDA1004X) += tda1004x.o
+ obj-$(CONFIG_DVB_SP887X) += sp887x.o
+ obj-$(CONFIG_DVB_NXT6000) += nxt6000.o
+ obj-$(CONFIG_DVB_MT352) += mt352.o
++obj-$(CONFIG_DVB_ZL10036) += zl10036.o
+ obj-$(CONFIG_DVB_ZL10353) += zl10353.o
+ obj-$(CONFIG_DVB_CX22702) += cx22702.o
+ obj-$(CONFIG_DVB_DRX397XD) += drx397xD.o
+@@ -41,6 +44,7 @@ obj-$(CONFIG_DVB_BCM3510) += bcm3510.o
+ obj-$(CONFIG_DVB_S5H1420) += s5h1420.o
+ obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o
+ obj-$(CONFIG_DVB_LGDT3304) += lgdt3304.o
++obj-$(CONFIG_DVB_LGDT3305) += lgdt3305.o
+ obj-$(CONFIG_DVB_CX24123) += cx24123.o
+ obj-$(CONFIG_DVB_LNBP21) += lnbp21.o
+ obj-$(CONFIG_DVB_ISL6405) += isl6405.o
+@@ -64,4 +68,6 @@ obj-$(CONFIG_DVB_SI21XX) += si21xx.o
+ obj-$(CONFIG_DVB_STV0288) += stv0288.o
+ obj-$(CONFIG_DVB_STB6000) += stb6000.o
+ obj-$(CONFIG_DVB_S921) += s921.o
++obj-$(CONFIG_DVB_STV6110) += stv6110.o
++obj-$(CONFIG_DVB_STV0900) += stv0900.o
+
+diff --git a/drivers/media/dvb/frontends/au8522.c b/drivers/media/dvb/frontends/au8522.c
+deleted file mode 100644
+index eabf9a6..0000000
+--- a/drivers/media/dvb/frontends/au8522.c
++++ /dev/null
+@@ -1,874 +0,0 @@
+-/*
+- Auvitek AU8522 QAM/8VSB demodulator driver
+-
+- Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
+-
+- This program is free software; you can redistribute it and/or modify
+- it under the terms of the GNU General Public License as published by
+- the Free Software Foundation; either version 2 of the License, or
+- (at your option) any later version.
+-
+- This program is distributed in the hope that it will be useful,
+- but WITHOUT ANY WARRANTY; without even the implied warranty of
+- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- GNU General Public License for more details.
+-
+- You should have received a copy of the GNU General Public License
+- along with this program; if not, write to the Free Software
+- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+-
+-*/
+-
+-#include <linux/kernel.h>
+-#include <linux/init.h>
+-#include <linux/module.h>
+-#include <linux/string.h>
+-#include <linux/slab.h>
+-#include <linux/delay.h>
+-#include "dvb_frontend.h"
+-#include "au8522.h"
+-
+-struct au8522_state {
+-
+- struct i2c_adapter *i2c;
+-
+- /* configuration settings */
+- const struct au8522_config *config;
+-
+- struct dvb_frontend frontend;
+-
+- u32 current_frequency;
+- fe_modulation_t current_modulation;
+-
+- u32 fe_status;
+- unsigned int led_state;
+-};
+-
+-static int debug;
+-
+-#define dprintk(arg...) do { \
+- if (debug) \
+- printk(arg); \
+- } while (0)
+-
+-/* 16 bit registers, 8 bit values */
+-static int au8522_writereg(struct au8522_state *state, u16 reg, u8 data)
+-{
+- int ret;
+- u8 buf [] = { reg >> 8, reg & 0xff, data };
+-
+- struct i2c_msg msg = { .addr = state->config->demod_address,
+- .flags = 0, .buf = buf, .len = 3 };
+-
+- ret = i2c_transfer(state->i2c, &msg, 1);
+-
+- if (ret != 1)
+- printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, "
+- "ret == %i)\n", __func__, reg, data, ret);
+-
+- return (ret != 1) ? -1 : 0;
+-}
+-
+-static u8 au8522_readreg(struct au8522_state *state, u16 reg)
+-{
+- int ret;
+- u8 b0 [] = { reg >> 8, reg & 0xff };
+- u8 b1 [] = { 0 };
+-
+- struct i2c_msg msg [] = {
+- { .addr = state->config->demod_address, .flags = 0,
+- .buf = b0, .len = 2 },
+- { .addr = state->config->demod_address, .flags = I2C_M_RD,
+- .buf = b1, .len = 1 } };
+-
+- ret = i2c_transfer(state->i2c, msg, 2);
+-
+- if (ret != 2)
+- printk(KERN_ERR "%s: readreg error (ret == %i)\n",
+- __func__, ret);
+- return b1[0];
+-}
+-
+-static int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
+-{
+- struct au8522_state *state = fe->demodulator_priv;
+-
+- dprintk("%s(%d)\n", __func__, enable);
+-
+- if (enable)
+- return au8522_writereg(state, 0x106, 1);
+- else
+- return au8522_writereg(state, 0x106, 0);
+-}
+-
+-struct mse2snr_tab {
+- u16 val;
+- u16 data;
+-};
+-
+-/* VSB SNR lookup table */
+-static struct mse2snr_tab vsb_mse2snr_tab[] = {
+- { 0, 270 },
+- { 2, 250 },
+- { 3, 240 },
+- { 5, 230 },
+- { 7, 220 },
+- { 9, 210 },
+- { 12, 200 },
+- { 13, 195 },
+- { 15, 190 },
+- { 17, 185 },
+- { 19, 180 },
+- { 21, 175 },
+- { 24, 170 },
+- { 27, 165 },
+- { 31, 160 },
+- { 32, 158 },
+- { 33, 156 },
+- { 36, 152 },
+- { 37, 150 },
+- { 39, 148 },
+- { 40, 146 },
+- { 41, 144 },
+- { 43, 142 },
+- { 44, 140 },
+- { 48, 135 },
+- { 50, 130 },
+- { 43, 142 },
+- { 53, 125 },
+- { 56, 120 },
+- { 256, 115 },
+-};
+-
+-/* QAM64 SNR lookup table */
+-static struct mse2snr_tab qam64_mse2snr_tab[] = {
+- { 15, 0 },
+- { 16, 290 },
+- { 17, 288 },
+- { 18, 286 },
+- { 19, 284 },
+- { 20, 282 },
+- { 21, 281 },
+- { 22, 279 },
+- { 23, 277 },
+- { 24, 275 },
+- { 25, 273 },
+- { 26, 271 },
+- { 27, 269 },
+- { 28, 268 },
+- { 29, 266 },
+- { 30, 264 },
+- { 31, 262 },
+- { 32, 260 },
+- { 33, 259 },
+- { 34, 258 },
+- { 35, 256 },
+- { 36, 255 },
+- { 37, 254 },
+- { 38, 252 },
+- { 39, 251 },
+- { 40, 250 },
+- { 41, 249 },
+- { 42, 248 },
+- { 43, 246 },
+- { 44, 245 },
+- { 45, 244 },
+- { 46, 242 },
+- { 47, 241 },
+- { 48, 240 },
+- { 50, 239 },
+- { 51, 238 },
+- { 53, 237 },
+- { 54, 236 },
+- { 56, 235 },
+- { 57, 234 },
+- { 59, 233 },
+- { 60, 232 },
+- { 62, 231 },
+- { 63, 230 },
+- { 65, 229 },
+- { 67, 228 },
+- { 68, 227 },
+- { 70, 226 },
+- { 71, 225 },
+- { 73, 224 },
+- { 74, 223 },
+- { 76, 222 },
+- { 78, 221 },
+- { 80, 220 },
+- { 82, 219 },
+- { 85, 218 },
+- { 88, 217 },
+- { 90, 216 },
+- { 92, 215 },
+- { 93, 214 },
+- { 94, 212 },
+- { 95, 211 },
+- { 97, 210 },
+- { 99, 209 },
+- { 101, 208 },
+- { 102, 207 },
+- { 104, 206 },
+- { 107, 205 },
+- { 111, 204 },
+- { 114, 203 },
+- { 118, 202 },
+- { 122, 201 },
+- { 125, 200 },
+- { 128, 199 },
+- { 130, 198 },
+- { 132, 197 },
+- { 256, 190 },
+-};
+-
+-/* QAM256 SNR lookup table */
+-static struct mse2snr_tab qam256_mse2snr_tab[] = {
+- { 16, 0 },
+- { 17, 400 },
+- { 18, 398 },
+- { 19, 396 },
+- { 20, 394 },
+- { 21, 392 },
+- { 22, 390 },
+- { 23, 388 },
+- { 24, 386 },
+- { 25, 384 },
+- { 26, 382 },
+- { 27, 380 },
+- { 28, 379 },
+- { 29, 378 },
+- { 30, 377 },
+- { 31, 376 },
+- { 32, 375 },
+- { 33, 374 },
+- { 34, 373 },
+- { 35, 372 },
+- { 36, 371 },
+- { 37, 370 },
+- { 38, 362 },
+- { 39, 354 },
+- { 40, 346 },
+- { 41, 338 },
+- { 42, 330 },
+- { 43, 328 },
+- { 44, 326 },
+- { 45, 324 },
+- { 46, 322 },
+- { 47, 320 },
+- { 48, 319 },
+- { 49, 318 },
+- { 50, 317 },
+- { 51, 316 },
+- { 52, 315 },
+- { 53, 314 },
+- { 54, 313 },
+- { 55, 312 },
+- { 56, 311 },
+- { 57, 310 },
+- { 58, 308 },
+- { 59, 306 },
+- { 60, 304 },
+- { 61, 302 },
+- { 62, 300 },
+- { 63, 298 },
+- { 65, 295 },
+- { 68, 294 },
+- { 70, 293 },
+- { 73, 292 },
+- { 76, 291 },
+- { 78, 290 },
+- { 79, 289 },
+- { 81, 288 },
+- { 82, 287 },
+- { 83, 286 },
+- { 84, 285 },
+- { 85, 284 },
+- { 86, 283 },
+- { 88, 282 },
+- { 89, 281 },
+- { 256, 280 },
+-};
+-
+-static int au8522_mse2snr_lookup(struct mse2snr_tab *tab, int sz, int mse,
+- u16 *snr)
+-{
+- int i, ret = -EINVAL;
+- dprintk("%s()\n", __func__);
+-
+- for (i = 0; i < sz; i++) {
+- if (mse < tab[i].val) {
+- *snr = tab[i].data;
+- ret = 0;
+- break;
+- }
+- }
+- dprintk("%s() snr=%d\n", __func__, *snr);
+- return ret;
+-}
+-
+-static int au8522_set_if(struct dvb_frontend *fe, enum au8522_if_freq if_freq)
+-{
+- struct au8522_state *state = fe->demodulator_priv;
+- u8 r0b5, r0b6, r0b7;
+- char *ifmhz;
+-
+- switch (if_freq) {
+- case AU8522_IF_3_25MHZ:
+- ifmhz = "3.25";
+- r0b5 = 0x00;
+- r0b6 = 0x3d;
+- r0b7 = 0xa0;
+- break;
+- case AU8522_IF_4MHZ:
+- ifmhz = "4.00";
+- r0b5 = 0x00;
+- r0b6 = 0x4b;
+- r0b7 = 0xd9;
+- break;
+- case AU8522_IF_6MHZ:
+- ifmhz = "6.00";
+- r0b5 = 0xfb;
+- r0b6 = 0x8e;
+- r0b7 = 0x39;
+- break;
+- default:
+- dprintk("%s() IF Frequency not supported\n", __func__);
+- return -EINVAL;
+- }
+- dprintk("%s() %s MHz\n", __func__, ifmhz);
+- au8522_writereg(state, 0x80b5, r0b5);
+- au8522_writereg(state, 0x80b6, r0b6);
+- au8522_writereg(state, 0x80b7, r0b7);
+-
+- return 0;
+-}
+-
+-/* VSB Modulation table */
+-static struct {
+- u16 reg;
+- u16 data;
+-} VSB_mod_tab[] = {
+- { 0x8090, 0x84 },
+- { 0x4092, 0x11 },
+- { 0x2005, 0x00 },
+- { 0x8091, 0x80 },
+- { 0x80a3, 0x0c },
+- { 0x80a4, 0xe8 },
+- { 0x8081, 0xc4 },
+- { 0x80a5, 0x40 },
+- { 0x80a7, 0x40 },
+- { 0x80a6, 0x67 },
+- { 0x8262, 0x20 },
+- { 0x821c, 0x30 },
+- { 0x80d8, 0x1a },
+- { 0x8227, 0xa0 },
+- { 0x8121, 0xff },
+- { 0x80a8, 0xf0 },
+- { 0x80a9, 0x05 },
+- { 0x80aa, 0x77 },
+- { 0x80ab, 0xf0 },
+- { 0x80ac, 0x05 },
+- { 0x80ad, 0x77 },
+- { 0x80ae, 0x41 },
+- { 0x80af, 0x66 },
+- { 0x821b, 0xcc },
+- { 0x821d, 0x80 },
+- { 0x80a4, 0xe8 },
+- { 0x8231, 0x13 },
+-};
+-
+-/* QAM Modulation table */
+-static struct {
+- u16 reg;
+- u16 data;
+-} QAM_mod_tab[] = {
+- { 0x80a3, 0x09 },
+- { 0x80a4, 0x00 },
+- { 0x8081, 0xc4 },
+- { 0x80a5, 0x40 },
+- { 0x80aa, 0x77 },
+- { 0x80ad, 0x77 },
+- { 0x80a6, 0x67 },
+- { 0x8262, 0x20 },
+- { 0x821c, 0x30 },
+- { 0x80b8, 0x3e },
+- { 0x80b9, 0xf0 },
+- { 0x80ba, 0x01 },
+- { 0x80bb, 0x18 },
+- { 0x80bc, 0x50 },
+- { 0x80bd, 0x00 },
+- { 0x80be, 0xea },
+- { 0x80bf, 0xef },
+- { 0x80c0, 0xfc },
+- { 0x80c1, 0xbd },
+- { 0x80c2, 0x1f },
+- { 0x80c3, 0xfc },
+- { 0x80c4, 0xdd },
+- { 0x80c5, 0xaf },
+- { 0x80c6, 0x00 },
+- { 0x80c7, 0x38 },
+- { 0x80c8, 0x30 },
+- { 0x80c9, 0x05 },
+- { 0x80ca, 0x4a },
+- { 0x80cb, 0xd0 },
+- { 0x80cc, 0x01 },
+- { 0x80cd, 0xd9 },
+- { 0x80ce, 0x6f },
+- { 0x80cf, 0xf9 },
+- { 0x80d0, 0x70 },
+- { 0x80d1, 0xdf },
+- { 0x80d2, 0xf7 },
+- { 0x80d3, 0xc2 },
+- { 0x80d4, 0xdf },
+- { 0x80d5, 0x02 },
+- { 0x80d6, 0x9a },
+- { 0x80d7, 0xd0 },
+- { 0x8250, 0x0d },
+- { 0x8251, 0xcd },
+- { 0x8252, 0xe0 },
+- { 0x8253, 0x05 },
+- { 0x8254, 0xa7 },
+- { 0x8255, 0xff },
+- { 0x8256, 0xed },
+- { 0x8257, 0x5b },
+- { 0x8258, 0xae },
+- { 0x8259, 0xe6 },
+- { 0x825a, 0x3d },
+- { 0x825b, 0x0f },
+- { 0x825c, 0x0d },
+- { 0x825d, 0xea },
+- { 0x825e, 0xf2 },
+- { 0x825f, 0x51 },
+- { 0x8260, 0xf5 },
+- { 0x8261, 0x06 },
+- { 0x821a, 0x00 },
+- { 0x8546, 0x40 },
+- { 0x8210, 0x26 },
+- { 0x8211, 0xf6 },
+- { 0x8212, 0x84 },
+- { 0x8213, 0x02 },
+- { 0x8502, 0x01 },
+- { 0x8121, 0x04 },
+- { 0x8122, 0x04 },
+- { 0x852e, 0x10 },
+- { 0x80a4, 0xca },
+- { 0x80a7, 0x40 },
+- { 0x8526, 0x01 },
+-};
+-
+-static int au8522_enable_modulation(struct dvb_frontend *fe,
+- fe_modulation_t m)
+-{
+- struct au8522_state *state = fe->demodulator_priv;
+- int i;
+-
+- dprintk("%s(0x%08x)\n", __func__, m);
+-
+- switch (m) {
+- case VSB_8:
+- dprintk("%s() VSB_8\n", __func__);
+- for (i = 0; i < ARRAY_SIZE(VSB_mod_tab); i++)
+- au8522_writereg(state,
+- VSB_mod_tab[i].reg,
+- VSB_mod_tab[i].data);
+- au8522_set_if(fe, state->config->vsb_if);
+- break;
+- case QAM_64:
+- case QAM_256:
+- dprintk("%s() QAM 64/256\n", __func__);
+- for (i = 0; i < ARRAY_SIZE(QAM_mod_tab); i++)
+- au8522_writereg(state,
+- QAM_mod_tab[i].reg,
+- QAM_mod_tab[i].data);
+- au8522_set_if(fe, state->config->qam_if);
+- break;
+- default:
+- dprintk("%s() Invalid modulation\n", __func__);
+- return -EINVAL;
+- }
+-
+- state->current_modulation = m;
+-
+- return 0;
+-}
+-
+-/* Talk to the demod, set the FEC, GUARD, QAM settings etc */
+-static int au8522_set_frontend(struct dvb_frontend *fe,
+- struct dvb_frontend_parameters *p)
+-{
+- struct au8522_state *state = fe->demodulator_priv;
+- int ret = -EINVAL;
+-
+- dprintk("%s(frequency=%d)\n", __func__, p->frequency);
+-
+- if ((state->current_frequency == p->frequency) &&
+- (state->current_modulation == p->u.vsb.modulation))
+- return 0;
+-
+- au8522_enable_modulation(fe, p->u.vsb.modulation);
+-
+- /* Allow the demod to settle */
+- msleep(100);
+-
+- if (fe->ops.tuner_ops.set_params) {
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 1);
+- ret = fe->ops.tuner_ops.set_params(fe, p);
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 0);
+- }
+-
+- if (ret < 0)
+- return ret;
+-
+- state->current_frequency = p->frequency;
+-
+- return 0;
+-}
+-
+-/* Reset the demod hardware and reset all of the configuration registers
+- to a default state. */
+-static int au8522_init(struct dvb_frontend *fe)
+-{
+- struct au8522_state *state = fe->demodulator_priv;
+- dprintk("%s()\n", __func__);
+-
+- au8522_writereg(state, 0xa4, 1 << 5);
+-
+- au8522_i2c_gate_ctrl(fe, 1);
+-
+- return 0;
+-}
+-
+-static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
+-{
+- struct au8522_led_config *led_config = state->config->led_cfg;
+- u8 val;
+-
+- /* bail out if we cant control an LED */
+- if (!led_config || !led_config->gpio_output ||
+- !led_config->gpio_output_enable || !led_config->gpio_output_disable)
+- return 0;
+-
+- val = au8522_readreg(state, 0x4000 |
+- (led_config->gpio_output & ~0xc000));
+- if (onoff) {
+- /* enable GPIO output */
+- val &= ~((led_config->gpio_output_enable >> 8) & 0xff);
+- val |= (led_config->gpio_output_enable & 0xff);
+- } else {
+- /* disable GPIO output */
+- val &= ~((led_config->gpio_output_disable >> 8) & 0xff);
+- val |= (led_config->gpio_output_disable & 0xff);
+- }
+- return au8522_writereg(state, 0x8000 |
+- (led_config->gpio_output & ~0xc000), val);
+-}
+-
+-/* led = 0 | off
+- * led = 1 | signal ok
+- * led = 2 | signal strong
+- * led < 0 | only light led if leds are currently off
+- */
+-static int au8522_led_ctrl(struct au8522_state *state, int led)
+-{
+- struct au8522_led_config *led_config = state->config->led_cfg;
+- int i, ret = 0;
+-
+- /* bail out if we cant control an LED */
+- if (!led_config || !led_config->gpio_leds ||
+- !led_config->num_led_states || !led_config->led_states)
+- return 0;
+-
+- if (led < 0) {
+- /* if LED is already lit, then leave it as-is */
+- if (state->led_state)
+- return 0;
+- else
+- led *= -1;
+- }
+-
+- /* toggle LED if changing state */
+- if (state->led_state != led) {
+- u8 val;
+-
+- dprintk("%s: %d\n", __func__, led);
+-
+- au8522_led_gpio_enable(state, 1);
+-
+- val = au8522_readreg(state, 0x4000 |
+- (led_config->gpio_leds & ~0xc000));
+-
+- /* start with all leds off */
+- for (i = 0; i < led_config->num_led_states; i++)
+- val &= ~led_config->led_states[i];
+-
+- /* set selected LED state */
+- if (led < led_config->num_led_states)
+- val |= led_config->led_states[led];
+- else if (led_config->num_led_states)
+- val |=
+- led_config->led_states[led_config->num_led_states - 1];
+-
+- ret = au8522_writereg(state, 0x8000 |
+- (led_config->gpio_leds & ~0xc000), val);
+- if (ret < 0)
+- return ret;
+-
+- state->led_state = led;
+-
+- if (led == 0)
+- au8522_led_gpio_enable(state, 0);
+- }
+-
+- return 0;
+-}
+-
+-static int au8522_sleep(struct dvb_frontend *fe)
+-{
+- struct au8522_state *state = fe->demodulator_priv;
+- dprintk("%s()\n", __func__);
+-
+- /* turn off led */
+- au8522_led_ctrl(state, 0);
+-
+- state->current_frequency = 0;
+-
+- return 0;
+-}
+-
+-static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status)
+-{
+- struct au8522_state *state = fe->demodulator_priv;
+- u8 reg;
+- u32 tuner_status = 0;
+-
+- *status = 0;
+-
+- if (state->current_modulation == VSB_8) {
+- dprintk("%s() Checking VSB_8\n", __func__);
+- reg = au8522_readreg(state, 0x4088);
+- if ((reg & 0x03) == 0x03)
+- *status |= FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI;
+- } else {
+- dprintk("%s() Checking QAM\n", __func__);
+- reg = au8522_readreg(state, 0x4541);
+- if (reg & 0x80)
+- *status |= FE_HAS_VITERBI;
+- if (reg & 0x20)
+- *status |= FE_HAS_LOCK | FE_HAS_SYNC;
+- }
+-
+- switch (state->config->status_mode) {
+- case AU8522_DEMODLOCKING:
+- dprintk("%s() DEMODLOCKING\n", __func__);
+- if (*status & FE_HAS_VITERBI)
+- *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
+- break;
+- case AU8522_TUNERLOCKING:
+- /* Get the tuner status */
+- dprintk("%s() TUNERLOCKING\n", __func__);
+- if (fe->ops.tuner_ops.get_status) {
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 1);
+-
+- fe->ops.tuner_ops.get_status(fe, &tuner_status);
+-
+- if (fe->ops.i2c_gate_ctrl)
+- fe->ops.i2c_gate_ctrl(fe, 0);
+- }
+- if (tuner_status)
+- *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
+- break;
+- }
+- state->fe_status = *status;
+-
+- if (*status & FE_HAS_LOCK)
+- /* turn on LED, if it isn't on already */
+- au8522_led_ctrl(state, -1);
+- else
+- /* turn off LED */
+- au8522_led_ctrl(state, 0);
+-
+- dprintk("%s() status 0x%08x\n", __func__, *status);
+-
+- return 0;
+-}
+-
+-static int au8522_led_status(struct au8522_state *state, const u16 *snr)
+-{
+- struct au8522_led_config *led_config = state->config->led_cfg;
+- int led;
+- u16 strong;
+-
+- /* bail out if we cant control an LED */
+- if (!led_config)
+- return 0;
+-
+- if (0 == (state->fe_status & FE_HAS_LOCK))
+- return au8522_led_ctrl(state, 0);
+- else if (state->current_modulation == QAM_256)
+- strong = led_config->qam256_strong;
+- else if (state->current_modulation == QAM_64)
+- strong = led_config->qam64_strong;
+- else /* (state->current_modulation == VSB_8) */
+- strong = led_config->vsb8_strong;
+-
+- if (*snr >= strong)
+- led = 2;
+- else
+- led = 1;
+-
+- if ((state->led_state) &&
+- (((strong < *snr) ? (*snr - strong) : (strong - *snr)) <= 10))
+- /* snr didn't change enough to bother
+- * changing the color of the led */
+- return 0;
+-
+- return au8522_led_ctrl(state, led);
+-}
+-
+-static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr)
+-{
+- struct au8522_state *state = fe->demodulator_priv;
+- int ret = -EINVAL;
+-
+- dprintk("%s()\n", __func__);
+-
+- if (state->current_modulation == QAM_256)
+- ret = au8522_mse2snr_lookup(qam256_mse2snr_tab,
+- ARRAY_SIZE(qam256_mse2snr_tab),
+- au8522_readreg(state, 0x4522),
+- snr);
+- else if (state->current_modulation == QAM_64)
+- ret = au8522_mse2snr_lookup(qam64_mse2snr_tab,
+- ARRAY_SIZE(qam64_mse2snr_tab),
+- au8522_readreg(state, 0x4522),
+- snr);
+- else /* VSB_8 */
+- ret = au8522_mse2snr_lookup(vsb_mse2snr_tab,
+- ARRAY_SIZE(vsb_mse2snr_tab),
+- au8522_readreg(state, 0x4311),
+- snr);
+-
+- if (state->config->led_cfg)
+- au8522_led_status(state, snr);
+-
+- return ret;
+-}
+-
+-static int au8522_read_signal_strength(struct dvb_frontend *fe,
+- u16 *signal_strength)
+-{
+- return au8522_read_snr(fe, signal_strength);
+-}
+-
+-static int au8522_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
+-{
+- struct au8522_state *state = fe->demodulator_priv;
+-
+- if (state->current_modulation == VSB_8)
+- *ucblocks = au8522_readreg(state, 0x4087);
+- else
+- *ucblocks = au8522_readreg(state, 0x4543);
+-
+- return 0;
+-}
+-
+-static int au8522_read_ber(struct dvb_frontend *fe, u32 *ber)
+-{
+- return au8522_read_ucblocks(fe, ber);
+-}
+-
+-static int au8522_get_frontend(struct dvb_frontend *fe,
+- struct dvb_frontend_parameters *p)
+-{
+- struct au8522_state *state = fe->demodulator_priv;
+-
+- p->frequency = state->current_frequency;
+- p->u.vsb.modulation = state->current_modulation;
+-
+- return 0;
+-}
+-
+-static int au8522_get_tune_settings(struct dvb_frontend *fe,
+- struct dvb_frontend_tune_settings *tune)
+-{
+- tune->min_delay_ms = 1000;
+- return 0;
+-}
+-
+-static void au8522_release(struct dvb_frontend *fe)
+-{
+- struct au8522_state *state = fe->demodulator_priv;
+- kfree(state);
+-}
+-
+-static struct dvb_frontend_ops au8522_ops;
+-
+-struct dvb_frontend *au8522_attach(const struct au8522_config *config,
+- struct i2c_adapter *i2c)
+-{
+- struct au8522_state *state = NULL;
+-
+- /* allocate memory for the internal state */
+- state = kmalloc(sizeof(struct au8522_state), GFP_KERNEL);
+- if (state == NULL)
+- goto error;
+-
+- /* setup the state */
+- state->config = config;
+- state->i2c = i2c;
+- /* create dvb_frontend */
+- memcpy(&state->frontend.ops, &au8522_ops,
+- sizeof(struct dvb_frontend_ops));
+- state->frontend.demodulator_priv = state;
+-
+- if (au8522_init(&state->frontend) != 0) {
+- printk(KERN_ERR "%s: Failed to initialize correctly\n",
+- __func__);
+- goto error;
+- }
+-
+- /* Note: Leaving the I2C gate open here. */
+- au8522_i2c_gate_ctrl(&state->frontend, 1);
+-
+- return &state->frontend;
+-
+-error:
+- kfree(state);
+- return NULL;
+-}
+-EXPORT_SYMBOL(au8522_attach);
+-
+-static struct dvb_frontend_ops au8522_ops = {
+-
+- .info = {
+- .name = "Auvitek AU8522 QAM/8VSB Frontend",
+- .type = FE_ATSC,
+- .frequency_min = 54000000,
+- .frequency_max = 858000000,
+- .frequency_stepsize = 62500,
+- .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
+- },
+-
+- .init = au8522_init,
+- .sleep = au8522_sleep,
+- .i2c_gate_ctrl = au8522_i2c_gate_ctrl,
+- .set_frontend = au8522_set_frontend,
+- .get_frontend = au8522_get_frontend,
+- .get_tune_settings = au8522_get_tune_settings,
+- .read_status = au8522_read_status,
+- .read_ber = au8522_read_ber,
+- .read_signal_strength = au8522_read_signal_strength,
+- .read_snr = au8522_read_snr,
+- .read_ucblocks = au8522_read_ucblocks,
+- .release = au8522_release,
+-};
+-
+-module_param(debug, int, 0644);
+-MODULE_PARM_DESC(debug, "Enable verbose debug messages");
+-
+-MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver");
+-MODULE_AUTHOR("Steven Toth");
+-MODULE_LICENSE("GPL");
+diff --git a/drivers/media/dvb/frontends/au8522.h b/drivers/media/dvb/frontends/au8522.h
+index 7b94f55..565dcf3 100644
+--- a/drivers/media/dvb/frontends/au8522.h
++++ b/drivers/media/dvb/frontends/au8522.h
+@@ -74,6 +74,22 @@ struct dvb_frontend *au8522_attach(const struct au8522_config *config,
+ }
+ #endif /* CONFIG_DVB_AU8522 */
+
++/* Other modes may need to be added later */
++enum au8522_video_input {
++ AU8522_COMPOSITE_CH1 = 1,
++ AU8522_COMPOSITE_CH2,
++ AU8522_COMPOSITE_CH3,
++ AU8522_COMPOSITE_CH4,
++ AU8522_COMPOSITE_CH4_SIF,
++ AU8522_SVIDEO_CH13,
++ AU8522_SVIDEO_CH24,
++};
++
++enum au8522_audio_input {
++ AU8522_AUDIO_NONE,
++ AU8522_AUDIO_SIF,
++};
++
+ #endif /* __AU8522_H__ */
+
+ /*
+diff --git a/drivers/media/dvb/frontends/au8522_decoder.c b/drivers/media/dvb/frontends/au8522_decoder.c
+new file mode 100644
+index 0000000..d63e152
+--- /dev/null
++++ b/drivers/media/dvb/frontends/au8522_decoder.c
+@@ -0,0 +1,835 @@
++/*
++ * Auvitek AU8522 QAM/8VSB demodulator driver and video decoder
++ *
++ * Copyright (C) 2009 Devin Heitmueller <dheitmueller@linuxtv.org>
++ * Copyright (C) 2005-2008 Auvitek International, Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * As published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++ * 02110-1301, USA.
++ */
++
++/* Developer notes:
++ *
++ * VBI support is not yet working
++ * Saturation and hue setting are not yet working
++ * Enough is implemented here for CVBS and S-Video inputs, but the actual
++ * analog demodulator code isn't implemented (not needed for xc5000 since it
++ * has its own demodulator and outputs CVBS)
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/slab.h>
++#include <linux/videodev2.h>
++#include <linux/i2c.h>
++#include <linux/delay.h>
++#include <media/v4l2-common.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/v4l2-i2c-drv.h>
++#include <media/v4l2-device.h>
++#include "au8522.h"
++#include "au8522_priv.h"
++
++MODULE_AUTHOR("Devin Heitmueller");
++MODULE_LICENSE("GPL");
++
++static int au8522_analog_debug;
++
++
++module_param_named(analog_debug, au8522_analog_debug, int, 0644);
++
++MODULE_PARM_DESC(analog_debug,
++ "Analog debugging messages [0=Off (default) 1=On]");
++
++struct au8522_register_config {
++ u16 reg_name;
++ u8 reg_val[8];
++};
++
++
++/* Video Decoder Filter Coefficients
++ The values are as follows from left to right
++ 0="ATV RF" 1="ATV RF13" 2="CVBS" 3="S-Video" 4="PAL" 5=CVBS13" 6="SVideo13"
++*/
++struct au8522_register_config filter_coef[] = {
++ {AU8522_FILTER_COEF_R410, {0x25, 0x00, 0x25, 0x25, 0x00, 0x00, 0x00} },
++ {AU8522_FILTER_COEF_R411, {0x20, 0x00, 0x20, 0x20, 0x00, 0x00, 0x00} },
++ {AU8522_FILTER_COEF_R412, {0x03, 0x00, 0x03, 0x03, 0x00, 0x00, 0x00} },
++ {AU8522_FILTER_COEF_R413, {0xe6, 0x00, 0xe6, 0xe6, 0x00, 0x00, 0x00} },
++ {AU8522_FILTER_COEF_R414, {0x40, 0x00, 0x40, 0x40, 0x00, 0x00, 0x00} },
++ {AU8522_FILTER_COEF_R415, {0x1b, 0x00, 0x1b, 0x1b, 0x00, 0x00, 0x00} },
++ {AU8522_FILTER_COEF_R416, {0xc0, 0x00, 0xc0, 0x04, 0x00, 0x00, 0x00} },
++ {AU8522_FILTER_COEF_R417, {0x04, 0x00, 0x04, 0x04, 0x00, 0x00, 0x00} },
++ {AU8522_FILTER_COEF_R418, {0x8c, 0x00, 0x8c, 0x8c, 0x00, 0x00, 0x00} },
++ {AU8522_FILTER_COEF_R419, {0xa0, 0x40, 0xa0, 0xa0, 0x40, 0x40, 0x40} },
++ {AU8522_FILTER_COEF_R41A, {0x21, 0x09, 0x21, 0x21, 0x09, 0x09, 0x09} },
++ {AU8522_FILTER_COEF_R41B, {0x6c, 0x38, 0x6c, 0x6c, 0x38, 0x38, 0x38} },
++ {AU8522_FILTER_COEF_R41C, {0x03, 0xff, 0x03, 0x03, 0xff, 0xff, 0xff} },
++ {AU8522_FILTER_COEF_R41D, {0xbf, 0xc7, 0xbf, 0xbf, 0xc7, 0xc7, 0xc7} },
++ {AU8522_FILTER_COEF_R41E, {0xa0, 0xdf, 0xa0, 0xa0, 0xdf, 0xdf, 0xdf} },
++ {AU8522_FILTER_COEF_R41F, {0x10, 0x06, 0x10, 0x10, 0x06, 0x06, 0x06} },
++ {AU8522_FILTER_COEF_R420, {0xae, 0x30, 0xae, 0xae, 0x30, 0x30, 0x30} },
++ {AU8522_FILTER_COEF_R421, {0xc4, 0x01, 0xc4, 0xc4, 0x01, 0x01, 0x01} },
++ {AU8522_FILTER_COEF_R422, {0x54, 0xdd, 0x54, 0x54, 0xdd, 0xdd, 0xdd} },
++ {AU8522_FILTER_COEF_R423, {0xd0, 0xaf, 0xd0, 0xd0, 0xaf, 0xaf, 0xaf} },
++ {AU8522_FILTER_COEF_R424, {0x1c, 0xf7, 0x1c, 0x1c, 0xf7, 0xf7, 0xf7} },
++ {AU8522_FILTER_COEF_R425, {0x76, 0xdb, 0x76, 0x76, 0xdb, 0xdb, 0xdb} },
++ {AU8522_FILTER_COEF_R426, {0x61, 0xc0, 0x61, 0x61, 0xc0, 0xc0, 0xc0} },
++ {AU8522_FILTER_COEF_R427, {0xd1, 0x2f, 0xd1, 0xd1, 0x2f, 0x2f, 0x2f} },
++ {AU8522_FILTER_COEF_R428, {0x84, 0xd8, 0x84, 0x84, 0xd8, 0xd8, 0xd8} },
++ {AU8522_FILTER_COEF_R429, {0x06, 0xfb, 0x06, 0x06, 0xfb, 0xfb, 0xfb} },
++ {AU8522_FILTER_COEF_R42A, {0x21, 0xd5, 0x21, 0x21, 0xd5, 0xd5, 0xd5} },
++ {AU8522_FILTER_COEF_R42B, {0x0a, 0x3e, 0x0a, 0x0a, 0x3e, 0x3e, 0x3e} },
++ {AU8522_FILTER_COEF_R42C, {0xe6, 0x15, 0xe6, 0xe6, 0x15, 0x15, 0x15} },
++ {AU8522_FILTER_COEF_R42D, {0x01, 0x34, 0x01, 0x01, 0x34, 0x34, 0x34} },
++
++};
++#define NUM_FILTER_COEF (sizeof(filter_coef)\
++ / sizeof(struct au8522_register_config))
++
++
++/* Registers 0x060b through 0x0652 are the LP Filter coefficients
++ The values are as follows from left to right
++ 0="SIF" 1="ATVRF/ATVRF13"
++ Note: the "ATVRF/ATVRF13" mode has never been tested
++*/
++struct au8522_register_config lpfilter_coef[] = {
++ {0x060b, {0x21, 0x0b} },
++ {0x060c, {0xad, 0xad} },
++ {0x060d, {0x70, 0xf0} },
++ {0x060e, {0xea, 0xe9} },
++ {0x060f, {0xdd, 0xdd} },
++ {0x0610, {0x08, 0x64} },
++ {0x0611, {0x60, 0x60} },
++ {0x0612, {0xf8, 0xb2} },
++ {0x0613, {0x01, 0x02} },
++ {0x0614, {0xe4, 0xb4} },
++ {0x0615, {0x19, 0x02} },
++ {0x0616, {0xae, 0x2e} },
++ {0x0617, {0xee, 0xc5} },
++ {0x0618, {0x56, 0x56} },
++ {0x0619, {0x30, 0x58} },
++ {0x061a, {0xf9, 0xf8} },
++ {0x061b, {0x24, 0x64} },
++ {0x061c, {0x07, 0x07} },
++ {0x061d, {0x30, 0x30} },
++ {0x061e, {0xa9, 0xed} },
++ {0x061f, {0x09, 0x0b} },
++ {0x0620, {0x42, 0xc2} },
++ {0x0621, {0x1d, 0x2a} },
++ {0x0622, {0xd6, 0x56} },
++ {0x0623, {0x95, 0x8b} },
++ {0x0624, {0x2b, 0x2b} },
++ {0x0625, {0x30, 0x24} },
++ {0x0626, {0x3e, 0x3e} },
++ {0x0627, {0x62, 0xe2} },
++ {0x0628, {0xe9, 0xf5} },
++ {0x0629, {0x99, 0x19} },
++ {0x062a, {0xd4, 0x11} },
++ {0x062b, {0x03, 0x04} },
++ {0x062c, {0xb5, 0x85} },
++ {0x062d, {0x1e, 0x20} },
++ {0x062e, {0x2a, 0xea} },
++ {0x062f, {0xd7, 0xd2} },
++ {0x0630, {0x15, 0x15} },
++ {0x0631, {0xa3, 0xa9} },
++ {0x0632, {0x1f, 0x1f} },
++ {0x0633, {0xf9, 0xd1} },
++ {0x0634, {0xc0, 0xc3} },
++ {0x0635, {0x4d, 0x8d} },
++ {0x0636, {0x21, 0x31} },
++ {0x0637, {0x83, 0x83} },
++ {0x0638, {0x08, 0x8c} },
++ {0x0639, {0x19, 0x19} },
++ {0x063a, {0x45, 0xa5} },
++ {0x063b, {0xef, 0xec} },
++ {0x063c, {0x8a, 0x8a} },
++ {0x063d, {0xf4, 0xf6} },
++ {0x063e, {0x8f, 0x8f} },
++ {0x063f, {0x44, 0x0c} },
++ {0x0640, {0xef, 0xf0} },
++ {0x0641, {0x66, 0x66} },
++ {0x0642, {0xcc, 0xd2} },
++ {0x0643, {0x41, 0x41} },
++ {0x0644, {0x63, 0x93} },
++ {0x0645, {0x8e, 0x8e} },
++ {0x0646, {0xa2, 0x42} },
++ {0x0647, {0x7b, 0x7b} },
++ {0x0648, {0x04, 0x04} },
++ {0x0649, {0x00, 0x00} },
++ {0x064a, {0x40, 0x40} },
++ {0x064b, {0x8c, 0x98} },
++ {0x064c, {0x00, 0x00} },
++ {0x064d, {0x63, 0xc3} },
++ {0x064e, {0x04, 0x04} },
++ {0x064f, {0x20, 0x20} },
++ {0x0650, {0x00, 0x00} },
++ {0x0651, {0x40, 0x40} },
++ {0x0652, {0x01, 0x01} },
++};
++#define NUM_LPFILTER_COEF (sizeof(lpfilter_coef)\
++ / sizeof(struct au8522_register_config))
++
++static inline struct au8522_state *to_state(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct au8522_state, sd);
++}
++
++static void setup_vbi(struct au8522_state *state, int aud_input)
++{
++ int i;
++
++ /* These are set to zero regardless of what mode we're in */
++ au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_H_REG017H, 0x00);
++ au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_L_REG018H, 0x00);
++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_TOTAL_BITS_REG019H, 0x00);
++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_TUNIT_H_REG01AH, 0x00);
++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_TUNIT_L_REG01BH, 0x00);
++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_THRESH1_REG01CH, 0x00);
++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT2_REG01EH, 0x00);
++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT1_REG01FH, 0x00);
++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_PAT0_REG020H, 0x00);
++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK2_REG021H,
++ 0x00);
++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK1_REG022H,
++ 0x00);
++ au8522_writereg(state, AU8522_TVDEC_VBI_USER_FRAME_MASK0_REG023H,
++ 0x00);
++
++ /* Setup the VBI registers */
++ for (i = 0x30; i < 0x60; i++)
++ au8522_writereg(state, i, 0x40);
++
++ /* For some reason, every register is 0x40 except register 0x44
++ (confirmed via the HVR-950q USB capture) */
++ au8522_writereg(state, 0x44, 0x60);
++
++ /* Enable VBI (we always do this regardless of whether the user is
++ viewing closed caption info) */
++ au8522_writereg(state, AU8522_TVDEC_VBI_CTRL_H_REG017H,
++ AU8522_TVDEC_VBI_CTRL_H_REG017H_CCON);
++
++}
++
++static void setup_decoder_defaults(struct au8522_state *state, u8 input_mode)
++{
++ int i;
++ int filter_coef_type;
++
++ /* Provide reasonable defaults for picture tuning values */
++ au8522_writereg(state, AU8522_TVDEC_SHARPNESSREG009H, 0x07);
++ au8522_writereg(state, AU8522_TVDEC_BRIGHTNESS_REG00AH, 0xed);
++ state->brightness = 0xed - 128;
++ au8522_writereg(state, AU8522_TVDEC_CONTRAST_REG00BH, 0x79);
++ state->contrast = 0x79;
++ au8522_writereg(state, AU8522_TVDEC_SATURATION_CB_REG00CH, 0x80);
++ au8522_writereg(state, AU8522_TVDEC_SATURATION_CR_REG00DH, 0x80);
++ au8522_writereg(state, AU8522_TVDEC_HUE_H_REG00EH, 0x00);
++ au8522_writereg(state, AU8522_TVDEC_HUE_L_REG00FH, 0x00);
++
++ /* Other decoder registers */
++ au8522_writereg(state, AU8522_TVDEC_INT_MASK_REG010H, 0x00);
++
++ if (input_mode == 0x23) {
++ /* S-Video input mapping */
++ au8522_writereg(state, AU8522_VIDEO_MODE_REG011H, 0x04);
++ } else {
++ /* All other modes (CVBS/ATVRF etc.) */
++ au8522_writereg(state, AU8522_VIDEO_MODE_REG011H, 0x00);
++ }
++
++ au8522_writereg(state, AU8522_TVDEC_PGA_REG012H,
++ AU8522_TVDEC_PGA_REG012H_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_COMB_MODE_REG015H,
++ AU8522_TVDEC_COMB_MODE_REG015H_CVBS);
++ au8522_writereg(state, AU8522_TVDED_DBG_MODE_REG060H,
++ AU8522_TVDED_DBG_MODE_REG060H_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL1_REG061H,
++ AU8522_TVDEC_FORMAT_CTRL1_REG061H_CVBS13);
++ au8522_writereg(state, AU8522_TVDEC_FORMAT_CTRL2_REG062H,
++ AU8522_TVDEC_FORMAT_CTRL2_REG062H_CVBS13);
++ au8522_writereg(state, AU8522_TVDEC_VCR_DET_LLIM_REG063H,
++ AU8522_TVDEC_VCR_DET_LLIM_REG063H_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_VCR_DET_HLIM_REG064H,
++ AU8522_TVDEC_VCR_DET_HLIM_REG064H_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR1_REG065H,
++ AU8522_TVDEC_COMB_VDIF_THR1_REG065H_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR2_REG066H,
++ AU8522_TVDEC_COMB_VDIF_THR2_REG066H_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_COMB_VDIF_THR3_REG067H,
++ AU8522_TVDEC_COMB_VDIF_THR3_REG067H_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_COMB_NOTCH_THR_REG068H,
++ AU8522_TVDEC_COMB_NOTCH_THR_REG068H_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR1_REG069H,
++ AU8522_TVDEC_COMB_HDIF_THR1_REG069H_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR2_REG06AH,
++ AU8522_TVDEC_COMB_HDIF_THR2_REG06AH_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_COMB_HDIF_THR3_REG06BH,
++ AU8522_TVDEC_COMB_HDIF_THR3_REG06BH_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH,
++ AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH,
++ AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH,
++ AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_UV_SEP_THR_REG06FH,
++ AU8522_TVDEC_UV_SEP_THR_REG06FH_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H,
++ AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H_CVBS);
++ au8522_writereg(state, AU8522_REG071H, AU8522_REG071H_CVBS);
++ au8522_writereg(state, AU8522_REG072H, AU8522_REG072H_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H,
++ AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H_CVBS);
++ au8522_writereg(state, AU8522_REG074H, AU8522_REG074H_CVBS);
++ au8522_writereg(state, AU8522_REG075H, AU8522_REG075H_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_DCAGC_CTRL_REG077H,
++ AU8522_TVDEC_DCAGC_CTRL_REG077H_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_PIC_START_ADJ_REG078H,
++ AU8522_TVDEC_PIC_START_ADJ_REG078H_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H,
++ AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH,
++ AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_INTRP_CTRL_REG07BH,
++ AU8522_TVDEC_INTRP_CTRL_REG07BH_CVBS);
++ au8522_writereg(state, AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H,
++ AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H_CVBS);
++ au8522_writereg(state, AU8522_TOREGAAGC_REG0E5H,
++ AU8522_TOREGAAGC_REG0E5H_CVBS);
++ au8522_writereg(state, AU8522_REG016H, AU8522_REG016H_CVBS);
++
++ setup_vbi(state, 0);
++
++ if (input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 ||
++ input_mode == AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24) {
++ /* Despite what the table says, for the HVR-950q we still need
++ to be in CVBS mode for the S-Video input (reason uknown). */
++ /* filter_coef_type = 3; */
++ filter_coef_type = 5;
++ } else {
++ filter_coef_type = 5;
++ }
++
++ /* Load the Video Decoder Filter Coefficients */
++ for (i = 0; i < NUM_FILTER_COEF; i++) {
++ au8522_writereg(state, filter_coef[i].reg_name,
++ filter_coef[i].reg_val[filter_coef_type]);
++ }
++
++ /* It's not clear what these registers are for, but they are always
++ set to the same value regardless of what mode we're in */
++ au8522_writereg(state, AU8522_REG42EH, 0x87);
++ au8522_writereg(state, AU8522_REG42FH, 0xa2);
++ au8522_writereg(state, AU8522_REG430H, 0xbf);
++ au8522_writereg(state, AU8522_REG431H, 0xcb);
++ au8522_writereg(state, AU8522_REG432H, 0xa1);
++ au8522_writereg(state, AU8522_REG433H, 0x41);
++ au8522_writereg(state, AU8522_REG434H, 0x88);
++ au8522_writereg(state, AU8522_REG435H, 0xc2);
++ au8522_writereg(state, AU8522_REG436H, 0x3c);
++}
++
++static void au8522_setup_cvbs_mode(struct au8522_state *state)
++{
++ /* here we're going to try the pre-programmed route */
++ au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H,
++ AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS);
++
++ au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00);
++ au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x0e);
++ au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x10);
++
++ au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H,
++ AU8522_INPUT_CONTROL_REG081H_CVBS_CH1);
++
++ setup_decoder_defaults(state, AU8522_INPUT_CONTROL_REG081H_CVBS_CH1);
++
++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
++ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);
++}
++
++static void au8522_setup_cvbs_tuner_mode(struct au8522_state *state)
++{
++ /* here we're going to try the pre-programmed route */
++ au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H,
++ AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS);
++
++ /* It's not clear why they turn off the PGA before enabling the clamp
++ control, but the Windows trace does it so we will too... */
++ au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x00);
++
++ /* Enable clamping control */
++ au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x0e);
++
++ /* Turn on the PGA */
++ au8522_writereg(state, AU8522_PGA_CONTROL_REG082H, 0x10);
++
++ /* Set input mode to CVBS on channel 4 with SIF audio input enabled */
++ au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H,
++ AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF);
++
++ setup_decoder_defaults(state,
++ AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF);
++
++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
++ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);
++}
++
++static void au8522_setup_svideo_mode(struct au8522_state *state)
++{
++ au8522_writereg(state, AU8522_MODULE_CLOCK_CONTROL_REG0A3H,
++ AU8522_MODULE_CLOCK_CONTROL_REG0A3H_SVIDEO);
++
++ /* Set input to Y on Channe1, C on Channel 3 */
++ au8522_writereg(state, AU8522_INPUT_CONTROL_REG081H,
++ AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13);
++
++ /* Disable clamping control (required for S-video) */
++ au8522_writereg(state, AU8522_CLAMPING_CONTROL_REG083H, 0x00);
++
++ setup_decoder_defaults(state,
++ AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13);
++
++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
++ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);
++}
++
++/* ----------------------------------------------------------------------- */
++
++static void disable_audio_input(struct au8522_state *state)
++{
++ /* This can probably be optimized */
++ au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x00);
++ au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x00);
++ au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0x00);
++ au8522_writereg(state, AU8522_I2C_CONTROL_REG1_REG091H, 0x80);
++ au8522_writereg(state, AU8522_I2C_CONTROL_REG0_REG090H, 0x84);
++
++ au8522_writereg(state, AU8522_ENA_USB_REG101H, 0x00);
++ au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F);
++ au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F);
++ au8522_writereg(state, AU8522_REG0F9H, AU8522_REG0F9H_AUDIO);
++ au8522_writereg(state, AU8522_AUDIO_MODE_REG0F1H, 0x40);
++
++ au8522_writereg(state, AU8522_GPIO_DATA_REG0E2H, 0x11);
++ msleep(5);
++ au8522_writereg(state, AU8522_GPIO_DATA_REG0E2H, 0x00);
++
++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x04);
++ au8522_writereg(state, AU8522_AUDIOFREQ_REG606H, 0x03);
++ au8522_writereg(state, AU8522_I2S_CTRL_2_REG112H, 0x02);
++
++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
++ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);
++}
++
++/* 0=disable, 1=SIF */
++static void set_audio_input(struct au8522_state *state, int aud_input)
++{
++ int i;
++
++ /* Note that this function needs to be used in conjunction with setting
++ the input routing via register 0x81 */
++
++ if (aud_input == AU8522_AUDIO_NONE) {
++ disable_audio_input(state);
++ return;
++ }
++
++ if (aud_input != AU8522_AUDIO_SIF) {
++ /* The caller asked for a mode we don't currently support */
++ printk(KERN_ERR "Unsupported audio mode requested! mode=%d\n",
++ aud_input);
++ return;
++ }
++
++ /* Load the Audio Decoder Filter Coefficients */
++ for (i = 0; i < NUM_LPFILTER_COEF; i++) {
++ au8522_writereg(state, lpfilter_coef[i].reg_name,
++ lpfilter_coef[i].reg_val[0]);
++ }
++
++ /* Setup audio */
++ au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x00);
++ au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x00);
++ au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0x00);
++ au8522_writereg(state, AU8522_I2C_CONTROL_REG1_REG091H, 0x80);
++ au8522_writereg(state, AU8522_I2C_CONTROL_REG0_REG090H, 0x84);
++ msleep(150);
++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x00);
++ msleep(1);
++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H, 0x9d);
++ msleep(50);
++ au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F);
++ au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F);
++ au8522_writereg(state, AU8522_AUDIO_VOLUME_REG0F4H, 0xff);
++ msleep(80);
++ au8522_writereg(state, AU8522_AUDIO_VOLUME_L_REG0F2H, 0x7F);
++ au8522_writereg(state, AU8522_AUDIO_VOLUME_R_REG0F3H, 0x7F);
++ au8522_writereg(state, AU8522_REG0F9H, AU8522_REG0F9H_AUDIO);
++ au8522_writereg(state, AU8522_AUDIO_MODE_REG0F1H, 0x82);
++ msleep(70);
++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H, 0x09);
++ au8522_writereg(state, AU8522_AUDIOFREQ_REG606H, 0x03);
++ au8522_writereg(state, AU8522_I2S_CTRL_2_REG112H, 0xc2);
++}
++
++/* ----------------------------------------------------------------------- */
++
++static int au8522_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
++{
++ struct au8522_state *state = to_state(sd);
++
++ switch (ctrl->id) {
++ case V4L2_CID_BRIGHTNESS:
++ state->brightness = ctrl->value;
++ au8522_writereg(state, AU8522_TVDEC_BRIGHTNESS_REG00AH,
++ ctrl->value - 128);
++ break;
++ case V4L2_CID_CONTRAST:
++ state->contrast = ctrl->value;
++ au8522_writereg(state, AU8522_TVDEC_CONTRAST_REG00BH,
++ ctrl->value);
++ break;
++ case V4L2_CID_SATURATION:
++ case V4L2_CID_HUE:
++ case V4L2_CID_AUDIO_VOLUME:
++ case V4L2_CID_AUDIO_BASS:
++ case V4L2_CID_AUDIO_TREBLE:
++ case V4L2_CID_AUDIO_BALANCE:
++ case V4L2_CID_AUDIO_MUTE:
++ /* Not yet implemented */
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int au8522_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
++{
++ struct au8522_state *state = to_state(sd);
++
++ /* Note that we are using values cached in the state structure instead
++ of reading the registers due to issues with i2c reads not working
++ properly/consistently yet on the HVR-950q */
++
++ switch (ctrl->id) {
++ case V4L2_CID_BRIGHTNESS:
++ ctrl->value = state->brightness;
++ break;
++ case V4L2_CID_CONTRAST:
++ ctrl->value = state->contrast;
++ break;
++ case V4L2_CID_SATURATION:
++ case V4L2_CID_HUE:
++ case V4L2_CID_AUDIO_VOLUME:
++ case V4L2_CID_AUDIO_BASS:
++ case V4L2_CID_AUDIO_TREBLE:
++ case V4L2_CID_AUDIO_BALANCE:
++ case V4L2_CID_AUDIO_MUTE:
++ /* Not yet supported */
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++/* ----------------------------------------------------------------------- */
++
++static int au8522_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
++{
++ switch (fmt->type) {
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static int au8522_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
++{
++ switch (fmt->type) {
++ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
++ /* Not yet implemented */
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++/* ----------------------------------------------------------------------- */
++
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++static int au8522_g_register(struct v4l2_subdev *sd,
++ struct v4l2_dbg_register *reg)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct au8522_state *state = to_state(sd);
++
++ if (!v4l2_chip_match_i2c_client(client, &reg->match))
++ return -EINVAL;
++ if (!capable(CAP_SYS_ADMIN))
++ return -EPERM;
++ reg->val = au8522_readreg(state, reg->reg & 0xffff);
++ return 0;
++}
++
++static int au8522_s_register(struct v4l2_subdev *sd,
++ struct v4l2_dbg_register *reg)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct au8522_state *state = to_state(sd);
++
++ if (!v4l2_chip_match_i2c_client(client, &reg->match))
++ return -EINVAL;
++ if (!capable(CAP_SYS_ADMIN))
++ return -EPERM;
++ au8522_writereg(state, reg->reg, reg->val & 0xff);
++ return 0;
++}
++#endif
++
++static int au8522_s_stream(struct v4l2_subdev *sd, int enable)
++{
++ struct au8522_state *state = to_state(sd);
++
++ if (enable) {
++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
++ 0x01);
++ msleep(1);
++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
++ AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS);
++ } else {
++ /* This does not completely power down the device
++ (it only reduces it from around 140ma to 80ma) */
++ au8522_writereg(state, AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H,
++ 1 << 5);
++ }
++ return 0;
++}
++
++static int au8522_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
++{
++ switch (qc->id) {
++ case V4L2_CID_CONTRAST:
++ return v4l2_ctrl_query_fill(qc, 0, 255, 1,
++ AU8522_TVDEC_CONTRAST_REG00BH_CVBS);
++ case V4L2_CID_BRIGHTNESS:
++ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
++ case V4L2_CID_SATURATION:
++ case V4L2_CID_HUE:
++ /* Not yet implemented */
++ default:
++ break;
++ }
++
++ qc->type = 0;
++ return -EINVAL;
++}
++
++static int au8522_reset(struct v4l2_subdev *sd, u32 val)
++{
++ struct au8522_state *state = to_state(sd);
++
++ au8522_writereg(state, 0xa4, 1 << 5);
++
++ return 0;
++}
++
++static int au8522_s_video_routing(struct v4l2_subdev *sd,
++ const struct v4l2_routing *route)
++{
++ struct au8522_state *state = to_state(sd);
++
++ au8522_reset(sd, 0);
++
++ /* Jam open the i2c gate to the tuner. We do this here to handle the
++ case where the user went into digital mode (causing the gate to be
++ closed), and then came back to analog mode */
++ au8522_writereg(state, 0x106, 1);
++
++ if (route->input == AU8522_COMPOSITE_CH1) {
++ au8522_setup_cvbs_mode(state);
++ } else if (route->input == AU8522_SVIDEO_CH13) {
++ au8522_setup_svideo_mode(state);
++ } else if (route->input == AU8522_COMPOSITE_CH4_SIF) {
++ au8522_setup_cvbs_tuner_mode(state);
++ } else {
++ printk(KERN_ERR "au8522 mode not currently supported\n");
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static int au8522_s_audio_routing(struct v4l2_subdev *sd,
++ const struct v4l2_routing *route)
++{
++ struct au8522_state *state = to_state(sd);
++ set_audio_input(state, route->input);
++ return 0;
++}
++
++static int au8522_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
++{
++ int val = 0;
++ struct au8522_state *state = to_state(sd);
++ u8 lock_status;
++
++ /* Interrogate the decoder to see if we are getting a real signal */
++ lock_status = au8522_readreg(state, 0x00);
++ if (lock_status == 0xa2)
++ vt->signal = 0x01;
++ else
++ vt->signal = 0x00;
++
++ vt->capability |=
++ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
++ V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
++
++ val = V4L2_TUNER_SUB_MONO;
++ vt->rxsubchans = val;
++ vt->audmode = V4L2_TUNER_MODE_STEREO;
++ return 0;
++}
++
++static int au8522_g_chip_ident(struct v4l2_subdev *sd,
++ struct v4l2_dbg_chip_ident *chip)
++{
++ struct au8522_state *state = to_state(sd);
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
++ return v4l2_chip_ident_i2c_client(client, chip, state->id, state->rev);
++}
++
++static int au8522_log_status(struct v4l2_subdev *sd)
++{
++ /* FIXME: Add some status info here */
++ return 0;
++}
++
++/* ----------------------------------------------------------------------- */
++
++static const struct v4l2_subdev_core_ops au8522_core_ops = {
++ .log_status = au8522_log_status,
++ .g_chip_ident = au8522_g_chip_ident,
++ .g_ctrl = au8522_g_ctrl,
++ .s_ctrl = au8522_s_ctrl,
++ .queryctrl = au8522_queryctrl,
++ .reset = au8522_reset,
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++ .g_register = au8522_g_register,
++ .s_register = au8522_s_register,
++#endif
++};
++
++static const struct v4l2_subdev_tuner_ops au8522_tuner_ops = {
++ .g_tuner = au8522_g_tuner,
++};
++
++static const struct v4l2_subdev_audio_ops au8522_audio_ops = {
++ .s_routing = au8522_s_audio_routing,
++};
++
++static const struct v4l2_subdev_video_ops au8522_video_ops = {
++ .s_routing = au8522_s_video_routing,
++ .g_fmt = au8522_g_fmt,
++ .s_fmt = au8522_s_fmt,
++ .s_stream = au8522_s_stream,
++};
++
++static const struct v4l2_subdev_ops au8522_ops = {
++ .core = &au8522_core_ops,
++ .tuner = &au8522_tuner_ops,
++ .audio = &au8522_audio_ops,
++ .video = &au8522_video_ops,
++};
++
++/* ----------------------------------------------------------------------- */
++
++static int au8522_probe(struct i2c_client *client,
++ const struct i2c_device_id *did)
++{
++ struct au8522_state *state;
++ struct v4l2_subdev *sd;
++ int instance;
++ struct au8522_config *demod_config;
++
++ /* Check if the adapter supports the needed features */
++ if (!i2c_check_functionality(client->adapter,
++ I2C_FUNC_SMBUS_BYTE_DATA)) {
++ return -EIO;
++ }
++
++ /* allocate memory for the internal state */
++ instance = au8522_get_state(&state, client->adapter, client->addr);
++ switch (instance) {
++ case 0:
++ printk(KERN_ERR "au8522_decoder allocation failed\n");
++ return -EIO;
++ case 1:
++ /* new demod instance */
++ printk(KERN_INFO "au8522_decoder creating new instance...\n");
++ break;
++ default:
++ /* existing demod instance */
++ printk(KERN_INFO "au8522_decoder attach existing instance.\n");
++ break;
++ }
++
++ demod_config = kzalloc(sizeof(struct au8522_config), GFP_KERNEL);
++ demod_config->demod_address = 0x8e >> 1;
++
++ state->config = demod_config;
++ state->i2c = client->adapter;
++
++ sd = &state->sd;
++ v4l2_i2c_subdev_init(sd, client, &au8522_ops);
++
++ state->c = client;
++ state->vid_input = AU8522_COMPOSITE_CH1;
++ state->aud_input = AU8522_AUDIO_NONE;
++ state->id = 8522;
++ state->rev = 0;
++
++ /* Jam open the i2c gate to the tuner */
++ au8522_writereg(state, 0x106, 1);
++
++ return 0;
++}
++
++static int au8522_remove(struct i2c_client *client)
++{
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ v4l2_device_unregister_subdev(sd);
++ au8522_release_state(to_state(sd));
++ return 0;
++}
++
++static const struct i2c_device_id au8522_id[] = {
++ {"au8522", 0},
++ {}
++};
++
++MODULE_DEVICE_TABLE(i2c, au8522_id);
++
++static struct v4l2_i2c_driver_data v4l2_i2c_data = {
++ .name = "au8522",
++ .probe = au8522_probe,
++ .remove = au8522_remove,
++ .id_table = au8522_id,
++};
+diff --git a/drivers/media/dvb/frontends/au8522_dig.c b/drivers/media/dvb/frontends/au8522_dig.c
+new file mode 100644
+index 0000000..3573125
+--- /dev/null
++++ b/drivers/media/dvb/frontends/au8522_dig.c
+@@ -0,0 +1,902 @@
++/*
++ Auvitek AU8522 QAM/8VSB demodulator driver
++
++ Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
++
++ This program is free software; you can redistribute it and/or modify
++ it under the terms of the GNU General Public License as published by
++ the Free Software Foundation; either version 2 of the License, or
++ (at your option) any later version.
++
++ This program is distributed in the hope that it will be useful,
++ but WITHOUT ANY WARRANTY; without even the implied warranty of
++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ GNU General Public License for more details.
++
++ You should have received a copy of the GNU General Public License
++ along with this program; if not, write to the Free Software
++ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++
++*/
++
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/string.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include "dvb_frontend.h"
++#include "au8522.h"
++#include "au8522_priv.h"
++
++static int debug;
++
++/* Despite the name "hybrid_tuner", the framework works just as well for
++ hybrid demodulators as well... */
++static LIST_HEAD(hybrid_tuner_instance_list);
++static DEFINE_MUTEX(au8522_list_mutex);
++
++#define dprintk(arg...)\
++ do { if (debug)\
++ printk(arg);\
++ } while (0)
++
++/* 16 bit registers, 8 bit values */
++int au8522_writereg(struct au8522_state *state, u16 reg, u8 data)
++{
++ int ret;
++ u8 buf[] = { (reg >> 8) | 0x80, reg & 0xff, data };
++
++ struct i2c_msg msg = { .addr = state->config->demod_address,
++ .flags = 0, .buf = buf, .len = 3 };
++
++ ret = i2c_transfer(state->i2c, &msg, 1);
++
++ if (ret != 1)
++ printk("%s: writereg error (reg == 0x%02x, val == 0x%04x, "
++ "ret == %i)\n", __func__, reg, data, ret);
++
++ return (ret != 1) ? -1 : 0;
++}
++
++u8 au8522_readreg(struct au8522_state *state, u16 reg)
++{
++ int ret;
++ u8 b0[] = { (reg >> 8) | 0x40, reg & 0xff };
++ u8 b1[] = { 0 };
++
++ struct i2c_msg msg[] = {
++ { .addr = state->config->demod_address, .flags = 0,
++ .buf = b0, .len = 2 },
++ { .addr = state->config->demod_address, .flags = I2C_M_RD,
++ .buf = b1, .len = 1 } };
++
++ ret = i2c_transfer(state->i2c, msg, 2);
++
++ if (ret != 2)
++ printk(KERN_ERR "%s: readreg error (ret == %i)\n",
++ __func__, ret);
++ return b1[0];
++}
++
++static int au8522_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
++{
++ struct au8522_state *state = fe->demodulator_priv;
++
++ dprintk("%s(%d)\n", __func__, enable);
++
++ if (enable)
++ return au8522_writereg(state, 0x106, 1);
++ else
++ return au8522_writereg(state, 0x106, 0);
++}
++
++struct mse2snr_tab {
++ u16 val;
++ u16 data;
++};
++
++/* VSB SNR lookup table */
++static struct mse2snr_tab vsb_mse2snr_tab[] = {
++ { 0, 270 },
++ { 2, 250 },
++ { 3, 240 },
++ { 5, 230 },
++ { 7, 220 },
++ { 9, 210 },
++ { 12, 200 },
++ { 13, 195 },
++ { 15, 190 },
++ { 17, 185 },
++ { 19, 180 },
++ { 21, 175 },
++ { 24, 170 },
++ { 27, 165 },
++ { 31, 160 },
++ { 32, 158 },
++ { 33, 156 },
++ { 36, 152 },
++ { 37, 150 },
++ { 39, 148 },
++ { 40, 146 },
++ { 41, 144 },
++ { 43, 142 },
++ { 44, 140 },
++ { 48, 135 },
++ { 50, 130 },
++ { 43, 142 },
++ { 53, 125 },
++ { 56, 120 },
++ { 256, 115 },
++};
++
++/* QAM64 SNR lookup table */
++static struct mse2snr_tab qam64_mse2snr_tab[] = {
++ { 15, 0 },
++ { 16, 290 },
++ { 17, 288 },
++ { 18, 286 },
++ { 19, 284 },
++ { 20, 282 },
++ { 21, 281 },
++ { 22, 279 },
++ { 23, 277 },
++ { 24, 275 },
++ { 25, 273 },
++ { 26, 271 },
++ { 27, 269 },
++ { 28, 268 },
++ { 29, 266 },
++ { 30, 264 },
++ { 31, 262 },
++ { 32, 260 },
++ { 33, 259 },
++ { 34, 258 },
++ { 35, 256 },
++ { 36, 255 },
++ { 37, 254 },
++ { 38, 252 },
++ { 39, 251 },
++ { 40, 250 },
++ { 41, 249 },
++ { 42, 248 },
++ { 43, 246 },
++ { 44, 245 },
++ { 45, 244 },
++ { 46, 242 },
++ { 47, 241 },
++ { 48, 240 },
++ { 50, 239 },
++ { 51, 238 },
++ { 53, 237 },
++ { 54, 236 },
++ { 56, 235 },
++ { 57, 234 },
++ { 59, 233 },
++ { 60, 232 },
++ { 62, 231 },
++ { 63, 230 },
++ { 65, 229 },
++ { 67, 228 },
++ { 68, 227 },
++ { 70, 226 },
++ { 71, 225 },
++ { 73, 224 },
++ { 74, 223 },
++ { 76, 222 },
++ { 78, 221 },
++ { 80, 220 },
++ { 82, 219 },
++ { 85, 218 },
++ { 88, 217 },
++ { 90, 216 },
++ { 92, 215 },
++ { 93, 214 },
++ { 94, 212 },
++ { 95, 211 },
++ { 97, 210 },
++ { 99, 209 },
++ { 101, 208 },
++ { 102, 207 },
++ { 104, 206 },
++ { 107, 205 },
++ { 111, 204 },
++ { 114, 203 },
++ { 118, 202 },
++ { 122, 201 },
++ { 125, 200 },
++ { 128, 199 },
++ { 130, 198 },
++ { 132, 197 },
++ { 256, 190 },
++};
++
++/* QAM256 SNR lookup table */
++static struct mse2snr_tab qam256_mse2snr_tab[] = {
++ { 16, 0 },
++ { 17, 400 },
++ { 18, 398 },
++ { 19, 396 },
++ { 20, 394 },
++ { 21, 392 },
++ { 22, 390 },
++ { 23, 388 },
++ { 24, 386 },
++ { 25, 384 },
++ { 26, 382 },
++ { 27, 380 },
++ { 28, 379 },
++ { 29, 378 },
++ { 30, 377 },
++ { 31, 376 },
++ { 32, 375 },
++ { 33, 374 },
++ { 34, 373 },
++ { 35, 372 },
++ { 36, 371 },
++ { 37, 370 },
++ { 38, 362 },
++ { 39, 354 },
++ { 40, 346 },
++ { 41, 338 },
++ { 42, 330 },
++ { 43, 328 },
++ { 44, 326 },
++ { 45, 324 },
++ { 46, 322 },
++ { 47, 320 },
++ { 48, 319 },
++ { 49, 318 },
++ { 50, 317 },
++ { 51, 316 },
++ { 52, 315 },
++ { 53, 314 },
++ { 54, 313 },
++ { 55, 312 },
++ { 56, 311 },
++ { 57, 310 },
++ { 58, 308 },
++ { 59, 306 },
++ { 60, 304 },
++ { 61, 302 },
++ { 62, 300 },
++ { 63, 298 },
++ { 65, 295 },
++ { 68, 294 },
++ { 70, 293 },
++ { 73, 292 },
++ { 76, 291 },
++ { 78, 290 },
++ { 79, 289 },
++ { 81, 288 },
++ { 82, 287 },
++ { 83, 286 },
++ { 84, 285 },
++ { 85, 284 },
++ { 86, 283 },
++ { 88, 282 },
++ { 89, 281 },
++ { 256, 280 },
++};
++
++static int au8522_mse2snr_lookup(struct mse2snr_tab *tab, int sz, int mse,
++ u16 *snr)
++{
++ int i, ret = -EINVAL;
++ dprintk("%s()\n", __func__);
++
++ for (i = 0; i < sz; i++) {
++ if (mse < tab[i].val) {
++ *snr = tab[i].data;
++ ret = 0;
++ break;
++ }
++ }
++ dprintk("%s() snr=%d\n", __func__, *snr);
++ return ret;
++}
++
++static int au8522_set_if(struct dvb_frontend *fe, enum au8522_if_freq if_freq)
++{
++ struct au8522_state *state = fe->demodulator_priv;
++ u8 r0b5, r0b6, r0b7;
++ char *ifmhz;
++
++ switch (if_freq) {
++ case AU8522_IF_3_25MHZ:
++ ifmhz = "3.25";
++ r0b5 = 0x00;
++ r0b6 = 0x3d;
++ r0b7 = 0xa0;
++ break;
++ case AU8522_IF_4MHZ:
++ ifmhz = "4.00";
++ r0b5 = 0x00;
++ r0b6 = 0x4b;
++ r0b7 = 0xd9;
++ break;
++ case AU8522_IF_6MHZ:
++ ifmhz = "6.00";
++ r0b5 = 0xfb;
++ r0b6 = 0x8e;
++ r0b7 = 0x39;
++ break;
++ default:
++ dprintk("%s() IF Frequency not supported\n", __func__);
++ return -EINVAL;
++ }
++ dprintk("%s() %s MHz\n", __func__, ifmhz);
++ au8522_writereg(state, 0x80b5, r0b5);
++ au8522_writereg(state, 0x80b6, r0b6);
++ au8522_writereg(state, 0x80b7, r0b7);
++
++ return 0;
++}
++
++/* VSB Modulation table */
++static struct {
++ u16 reg;
++ u16 data;
++} VSB_mod_tab[] = {
++ { 0x8090, 0x84 },
++ { 0x4092, 0x11 },
++ { 0x2005, 0x00 },
++ { 0x8091, 0x80 },
++ { 0x80a3, 0x0c },
++ { 0x80a4, 0xe8 },
++ { 0x8081, 0xc4 },
++ { 0x80a5, 0x40 },
++ { 0x80a7, 0x40 },
++ { 0x80a6, 0x67 },
++ { 0x8262, 0x20 },
++ { 0x821c, 0x30 },
++ { 0x80d8, 0x1a },
++ { 0x8227, 0xa0 },
++ { 0x8121, 0xff },
++ { 0x80a8, 0xf0 },
++ { 0x80a9, 0x05 },
++ { 0x80aa, 0x77 },
++ { 0x80ab, 0xf0 },
++ { 0x80ac, 0x05 },
++ { 0x80ad, 0x77 },
++ { 0x80ae, 0x41 },
++ { 0x80af, 0x66 },
++ { 0x821b, 0xcc },
++ { 0x821d, 0x80 },
++ { 0x80a4, 0xe8 },
++ { 0x8231, 0x13 },
++};
++
++/* QAM Modulation table */
++static struct {
++ u16 reg;
++ u16 data;
++} QAM_mod_tab[] = {
++ { 0x80a3, 0x09 },
++ { 0x80a4, 0x00 },
++ { 0x8081, 0xc4 },
++ { 0x80a5, 0x40 },
++ { 0x80aa, 0x77 },
++ { 0x80ad, 0x77 },
++ { 0x80a6, 0x67 },
++ { 0x8262, 0x20 },
++ { 0x821c, 0x30 },
++ { 0x80b8, 0x3e },
++ { 0x80b9, 0xf0 },
++ { 0x80ba, 0x01 },
++ { 0x80bb, 0x18 },
++ { 0x80bc, 0x50 },
++ { 0x80bd, 0x00 },
++ { 0x80be, 0xea },
++ { 0x80bf, 0xef },
++ { 0x80c0, 0xfc },
++ { 0x80c1, 0xbd },
++ { 0x80c2, 0x1f },
++ { 0x80c3, 0xfc },
++ { 0x80c4, 0xdd },
++ { 0x80c5, 0xaf },
++ { 0x80c6, 0x00 },
++ { 0x80c7, 0x38 },
++ { 0x80c8, 0x30 },
++ { 0x80c9, 0x05 },
++ { 0x80ca, 0x4a },
++ { 0x80cb, 0xd0 },
++ { 0x80cc, 0x01 },
++ { 0x80cd, 0xd9 },
++ { 0x80ce, 0x6f },
++ { 0x80cf, 0xf9 },
++ { 0x80d0, 0x70 },
++ { 0x80d1, 0xdf },
++ { 0x80d2, 0xf7 },
++ { 0x80d3, 0xc2 },
++ { 0x80d4, 0xdf },
++ { 0x80d5, 0x02 },
++ { 0x80d6, 0x9a },
++ { 0x80d7, 0xd0 },
++ { 0x8250, 0x0d },
++ { 0x8251, 0xcd },
++ { 0x8252, 0xe0 },
++ { 0x8253, 0x05 },
++ { 0x8254, 0xa7 },
++ { 0x8255, 0xff },
++ { 0x8256, 0xed },
++ { 0x8257, 0x5b },
++ { 0x8258, 0xae },
++ { 0x8259, 0xe6 },
++ { 0x825a, 0x3d },
++ { 0x825b, 0x0f },
++ { 0x825c, 0x0d },
++ { 0x825d, 0xea },
++ { 0x825e, 0xf2 },
++ { 0x825f, 0x51 },
++ { 0x8260, 0xf5 },
++ { 0x8261, 0x06 },
++ { 0x821a, 0x00 },
++ { 0x8546, 0x40 },
++ { 0x8210, 0x26 },
++ { 0x8211, 0xf6 },
++ { 0x8212, 0x84 },
++ { 0x8213, 0x02 },
++ { 0x8502, 0x01 },
++ { 0x8121, 0x04 },
++ { 0x8122, 0x04 },
++ { 0x852e, 0x10 },
++ { 0x80a4, 0xca },
++ { 0x80a7, 0x40 },
++ { 0x8526, 0x01 },
++};
++
++static int au8522_enable_modulation(struct dvb_frontend *fe,
++ fe_modulation_t m)
++{
++ struct au8522_state *state = fe->demodulator_priv;
++ int i;
++
++ dprintk("%s(0x%08x)\n", __func__, m);
++
++ switch (m) {
++ case VSB_8:
++ dprintk("%s() VSB_8\n", __func__);
++ for (i = 0; i < ARRAY_SIZE(VSB_mod_tab); i++)
++ au8522_writereg(state,
++ VSB_mod_tab[i].reg,
++ VSB_mod_tab[i].data);
++ au8522_set_if(fe, state->config->vsb_if);
++ break;
++ case QAM_64:
++ case QAM_256:
++ dprintk("%s() QAM 64/256\n", __func__);
++ for (i = 0; i < ARRAY_SIZE(QAM_mod_tab); i++)
++ au8522_writereg(state,
++ QAM_mod_tab[i].reg,
++ QAM_mod_tab[i].data);
++ au8522_set_if(fe, state->config->qam_if);
++ break;
++ default:
++ dprintk("%s() Invalid modulation\n", __func__);
++ return -EINVAL;
++ }
++
++ state->current_modulation = m;
++
++ return 0;
++}
++
++/* Talk to the demod, set the FEC, GUARD, QAM settings etc */
++static int au8522_set_frontend(struct dvb_frontend *fe,
++ struct dvb_frontend_parameters *p)
++{
++ struct au8522_state *state = fe->demodulator_priv;
++ int ret = -EINVAL;
++
++ dprintk("%s(frequency=%d)\n", __func__, p->frequency);
++
++ if ((state->current_frequency == p->frequency) &&
++ (state->current_modulation == p->u.vsb.modulation))
++ return 0;
++
++ au8522_enable_modulation(fe, p->u.vsb.modulation);
++
++ /* Allow the demod to settle */
++ msleep(100);
++
++ if (fe->ops.tuner_ops.set_params) {
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 1);
++ ret = fe->ops.tuner_ops.set_params(fe, p);
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0);
++ }
++
++ if (ret < 0)
++ return ret;
++
++ state->current_frequency = p->frequency;
++
++ return 0;
++}
++
++/* Reset the demod hardware and reset all of the configuration registers
++ to a default state. */
++int au8522_init(struct dvb_frontend *fe)
++{
++ struct au8522_state *state = fe->demodulator_priv;
++ dprintk("%s()\n", __func__);
++
++ au8522_writereg(state, 0xa4, 1 << 5);
++
++ au8522_i2c_gate_ctrl(fe, 1);
++
++ return 0;
++}
++
++static int au8522_led_gpio_enable(struct au8522_state *state, int onoff)
++{
++ struct au8522_led_config *led_config = state->config->led_cfg;
++ u8 val;
++
++ /* bail out if we cant control an LED */
++ if (!led_config || !led_config->gpio_output ||
++ !led_config->gpio_output_enable || !led_config->gpio_output_disable)
++ return 0;
++
++ val = au8522_readreg(state, 0x4000 |
++ (led_config->gpio_output & ~0xc000));
++ if (onoff) {
++ /* enable GPIO output */
++ val &= ~((led_config->gpio_output_enable >> 8) & 0xff);
++ val |= (led_config->gpio_output_enable & 0xff);
++ } else {
++ /* disable GPIO output */
++ val &= ~((led_config->gpio_output_disable >> 8) & 0xff);
++ val |= (led_config->gpio_output_disable & 0xff);
++ }
++ return au8522_writereg(state, 0x8000 |
++ (led_config->gpio_output & ~0xc000), val);
++}
++
++/* led = 0 | off
++ * led = 1 | signal ok
++ * led = 2 | signal strong
++ * led < 0 | only light led if leds are currently off
++ */
++static int au8522_led_ctrl(struct au8522_state *state, int led)
++{
++ struct au8522_led_config *led_config = state->config->led_cfg;
++ int i, ret = 0;
++
++ /* bail out if we cant control an LED */
++ if (!led_config || !led_config->gpio_leds ||
++ !led_config->num_led_states || !led_config->led_states)
++ return 0;
++
++ if (led < 0) {
++ /* if LED is already lit, then leave it as-is */
++ if (state->led_state)
++ return 0;
++ else
++ led *= -1;
++ }
++
++ /* toggle LED if changing state */
++ if (state->led_state != led) {
++ u8 val;
++
++ dprintk("%s: %d\n", __func__, led);
++
++ au8522_led_gpio_enable(state, 1);
++
++ val = au8522_readreg(state, 0x4000 |
++ (led_config->gpio_leds & ~0xc000));
++
++ /* start with all leds off */
++ for (i = 0; i < led_config->num_led_states; i++)
++ val &= ~led_config->led_states[i];
++
++ /* set selected LED state */
++ if (led < led_config->num_led_states)
++ val |= led_config->led_states[led];
++ else if (led_config->num_led_states)
++ val |=
++ led_config->led_states[led_config->num_led_states - 1];
++
++ ret = au8522_writereg(state, 0x8000 |
++ (led_config->gpio_leds & ~0xc000), val);
++ if (ret < 0)
++ return ret;
++
++ state->led_state = led;
++
++ if (led == 0)
++ au8522_led_gpio_enable(state, 0);
++ }
++
++ return 0;
++}
++
++int au8522_sleep(struct dvb_frontend *fe)
++{
++ struct au8522_state *state = fe->demodulator_priv;
++ dprintk("%s()\n", __func__);
++
++ /* turn off led */
++ au8522_led_ctrl(state, 0);
++
++ /* Power down the chip */
++ au8522_writereg(state, 0xa4, 1 << 5);
++
++ state->current_frequency = 0;
++
++ return 0;
++}
++
++static int au8522_read_status(struct dvb_frontend *fe, fe_status_t *status)
++{
++ struct au8522_state *state = fe->demodulator_priv;
++ u8 reg;
++ u32 tuner_status = 0;
++
++ *status = 0;
++
++ if (state->current_modulation == VSB_8) {
++ dprintk("%s() Checking VSB_8\n", __func__);
++ reg = au8522_readreg(state, 0x4088);
++ if ((reg & 0x03) == 0x03)
++ *status |= FE_HAS_LOCK | FE_HAS_SYNC | FE_HAS_VITERBI;
++ } else {
++ dprintk("%s() Checking QAM\n", __func__);
++ reg = au8522_readreg(state, 0x4541);
++ if (reg & 0x80)
++ *status |= FE_HAS_VITERBI;
++ if (reg & 0x20)
++ *status |= FE_HAS_LOCK | FE_HAS_SYNC;
++ }
++
++ switch (state->config->status_mode) {
++ case AU8522_DEMODLOCKING:
++ dprintk("%s() DEMODLOCKING\n", __func__);
++ if (*status & FE_HAS_VITERBI)
++ *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
++ break;
++ case AU8522_TUNERLOCKING:
++ /* Get the tuner status */
++ dprintk("%s() TUNERLOCKING\n", __func__);
++ if (fe->ops.tuner_ops.get_status) {
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 1);
++
++ fe->ops.tuner_ops.get_status(fe, &tuner_status);
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0);
++ }
++ if (tuner_status)
++ *status |= FE_HAS_CARRIER | FE_HAS_SIGNAL;
++ break;
++ }
++ state->fe_status = *status;
++
++ if (*status & FE_HAS_LOCK)
++ /* turn on LED, if it isn't on already */
++ au8522_led_ctrl(state, -1);
++ else
++ /* turn off LED */
++ au8522_led_ctrl(state, 0);
++
++ dprintk("%s() status 0x%08x\n", __func__, *status);
++
++ return 0;
++}
++
++static int au8522_led_status(struct au8522_state *state, const u16 *snr)
++{
++ struct au8522_led_config *led_config = state->config->led_cfg;
++ int led;
++ u16 strong;
++
++ /* bail out if we cant control an LED */
++ if (!led_config)
++ return 0;
++
++ if (0 == (state->fe_status & FE_HAS_LOCK))
++ return au8522_led_ctrl(state, 0);
++ else if (state->current_modulation == QAM_256)
++ strong = led_config->qam256_strong;
++ else if (state->current_modulation == QAM_64)
++ strong = led_config->qam64_strong;
++ else /* (state->current_modulation == VSB_8) */
++ strong = led_config->vsb8_strong;
++
++ if (*snr >= strong)
++ led = 2;
++ else
++ led = 1;
++
++ if ((state->led_state) &&
++ (((strong < *snr) ? (*snr - strong) : (strong - *snr)) <= 10))
++ /* snr didn't change enough to bother
++ * changing the color of the led */
++ return 0;
++
++ return au8522_led_ctrl(state, led);
++}
++
++static int au8522_read_snr(struct dvb_frontend *fe, u16 *snr)
++{
++ struct au8522_state *state = fe->demodulator_priv;
++ int ret = -EINVAL;
++
++ dprintk("%s()\n", __func__);
++
++ if (state->current_modulation == QAM_256)
++ ret = au8522_mse2snr_lookup(qam256_mse2snr_tab,
++ ARRAY_SIZE(qam256_mse2snr_tab),
++ au8522_readreg(state, 0x4522),
++ snr);
++ else if (state->current_modulation == QAM_64)
++ ret = au8522_mse2snr_lookup(qam64_mse2snr_tab,
++ ARRAY_SIZE(qam64_mse2snr_tab),
++ au8522_readreg(state, 0x4522),
++ snr);
++ else /* VSB_8 */
++ ret = au8522_mse2snr_lookup(vsb_mse2snr_tab,
++ ARRAY_SIZE(vsb_mse2snr_tab),
++ au8522_readreg(state, 0x4311),
++ snr);
++
++ if (state->config->led_cfg)
++ au8522_led_status(state, snr);
++
++ return ret;
++}
++
++static int au8522_read_signal_strength(struct dvb_frontend *fe,
++ u16 *signal_strength)
++{
++ return au8522_read_snr(fe, signal_strength);
++}
++
++static int au8522_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
++{
++ struct au8522_state *state = fe->demodulator_priv;
++
++ if (state->current_modulation == VSB_8)
++ *ucblocks = au8522_readreg(state, 0x4087);
++ else
++ *ucblocks = au8522_readreg(state, 0x4543);
++
++ return 0;
++}
++
++static int au8522_read_ber(struct dvb_frontend *fe, u32 *ber)
++{
++ return au8522_read_ucblocks(fe, ber);
++}
++
++static int au8522_get_frontend(struct dvb_frontend *fe,
++ struct dvb_frontend_parameters *p)
++{
++ struct au8522_state *state = fe->demodulator_priv;
++
++ p->frequency = state->current_frequency;
++ p->u.vsb.modulation = state->current_modulation;
++
++ return 0;
++}
++
++static int au8522_get_tune_settings(struct dvb_frontend *fe,
++ struct dvb_frontend_tune_settings *tune)
++{
++ tune->min_delay_ms = 1000;
++ return 0;
++}
++
++static struct dvb_frontend_ops au8522_ops;
++
++int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
++ u8 client_address)
++{
++ int ret;
++
++ mutex_lock(&au8522_list_mutex);
++ ret = hybrid_tuner_request_state(struct au8522_state, (*state),
++ hybrid_tuner_instance_list,
++ i2c, client_address, "au8522");
++ mutex_unlock(&au8522_list_mutex);
++
++ return ret;
++}
++
++void au8522_release_state(struct au8522_state *state)
++{
++ mutex_lock(&au8522_list_mutex);
++ if (state != NULL)
++ hybrid_tuner_release_state(state);
++ mutex_unlock(&au8522_list_mutex);
++}
++
++
++static void au8522_release(struct dvb_frontend *fe)
++{
++ struct au8522_state *state = fe->demodulator_priv;
++ au8522_release_state(state);
++}
++
++struct dvb_frontend *au8522_attach(const struct au8522_config *config,
++ struct i2c_adapter *i2c)
++{
++ struct au8522_state *state = NULL;
++ int instance;
++
++ /* allocate memory for the internal state */
++ instance = au8522_get_state(&state, i2c, config->demod_address);
++ switch (instance) {
++ case 0:
++ dprintk("%s state allocation failed\n", __func__);
++ break;
++ case 1:
++ /* new demod instance */
++ dprintk("%s using new instance\n", __func__);
++ break;
++ default:
++ /* existing demod instance */
++ dprintk("%s using existing instance\n", __func__);
++ break;
++ }
++
++ /* setup the state */
++ state->config = config;
++ state->i2c = i2c;
++ /* create dvb_frontend */
++ memcpy(&state->frontend.ops, &au8522_ops,
++ sizeof(struct dvb_frontend_ops));
++ state->frontend.demodulator_priv = state;
++
++ if (au8522_init(&state->frontend) != 0) {
++ printk(KERN_ERR "%s: Failed to initialize correctly\n",
++ __func__);
++ goto error;
++ }
++
++ /* Note: Leaving the I2C gate open here. */
++ au8522_i2c_gate_ctrl(&state->frontend, 1);
++
++ return &state->frontend;
++
++error:
++ au8522_release_state(state);
++ return NULL;
++}
++EXPORT_SYMBOL(au8522_attach);
++
++static struct dvb_frontend_ops au8522_ops = {
++
++ .info = {
++ .name = "Auvitek AU8522 QAM/8VSB Frontend",
++ .type = FE_ATSC,
++ .frequency_min = 54000000,
++ .frequency_max = 858000000,
++ .frequency_stepsize = 62500,
++ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
++ },
++
++ .init = au8522_init,
++ .sleep = au8522_sleep,
++ .i2c_gate_ctrl = au8522_i2c_gate_ctrl,
++ .set_frontend = au8522_set_frontend,
++ .get_frontend = au8522_get_frontend,
++ .get_tune_settings = au8522_get_tune_settings,
++ .read_status = au8522_read_status,
++ .read_ber = au8522_read_ber,
++ .read_signal_strength = au8522_read_signal_strength,
++ .read_snr = au8522_read_snr,
++ .read_ucblocks = au8522_read_ucblocks,
++ .release = au8522_release,
++};
++
++module_param(debug, int, 0644);
++MODULE_PARM_DESC(debug, "Enable verbose debug messages");
++
++MODULE_DESCRIPTION("Auvitek AU8522 QAM-B/ATSC Demodulator driver");
++MODULE_AUTHOR("Steven Toth");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/media/dvb/frontends/au8522_priv.h b/drivers/media/dvb/frontends/au8522_priv.h
+new file mode 100644
+index 0000000..f328f2b
+--- /dev/null
++++ b/drivers/media/dvb/frontends/au8522_priv.h
+@@ -0,0 +1,412 @@
++/*
++ Auvitek AU8522 QAM/8VSB demodulator driver
++
++ Copyright (C) 2008 Steven Toth <stoth@linuxtv.org>
++ Copyright (C) 2008 Devin Heitmueller <dheitmueller@linuxtv.org>
++ Copyright (C) 2005-2008 Auvitek International, Ltd.
++
++ 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 <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/string.h>
++#include <linux/slab.h>
++#include <linux/delay.h>
++#include <linux/videodev2.h>
++#include <media/v4l2-device.h>
++#include <linux/i2c.h>
++#include "dvb_frontend.h"
++#include "au8522.h"
++#include "tuner-i2c.h"
++
++struct au8522_state {
++ struct i2c_client *c;
++ struct i2c_adapter *i2c;
++
++ /* Used for sharing of the state between analog and digital mode */
++ struct tuner_i2c_props i2c_props;
++ struct list_head hybrid_tuner_instance_list;
++
++ /* configuration settings */
++ const struct au8522_config *config;
++
++ struct dvb_frontend frontend;
++
++ u32 current_frequency;
++ fe_modulation_t current_modulation;
++
++ u32 fe_status;
++ unsigned int led_state;
++
++ /* Analog settings */
++ struct v4l2_subdev sd;
++ v4l2_std_id std;
++ int vid_input;
++ int aud_input;
++ u32 id;
++ u32 rev;
++ u8 brightness;
++ u8 contrast;
++};
++
++/* These are routines shared by both the VSB/QAM demodulator and the analog
++ decoder */
++int au8522_writereg(struct au8522_state *state, u16 reg, u8 data);
++u8 au8522_readreg(struct au8522_state *state, u16 reg);
++int au8522_init(struct dvb_frontend *fe);
++int au8522_sleep(struct dvb_frontend *fe);
++
++int au8522_get_state(struct au8522_state **state, struct i2c_adapter *i2c,
++ u8 client_address);
++void au8522_release_state(struct au8522_state *state);
++
++/* REGISTERS */
++#define AU8522_INPUT_CONTROL_REG081H 0x081
++#define AU8522_PGA_CONTROL_REG082H 0x082
++#define AU8522_CLAMPING_CONTROL_REG083H 0x083
++
++#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H 0x0A3
++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H 0x0A4
++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H 0x0A5
++#define AU8522_AGC_CONTROL_RANGE_REG0A6H 0x0A6
++#define AU8522_SYSTEM_GAIN_CONTROL_REG0A7H 0x0A7
++#define AU8522_TUNER_AGC_RF_STOP_REG0A8H 0x0A8
++#define AU8522_TUNER_AGC_RF_START_REG0A9H 0x0A9
++#define AU8522_TUNER_RF_AGC_DEFAULT_REG0AAH 0x0AA
++#define AU8522_TUNER_AGC_IF_STOP_REG0ABH 0x0AB
++#define AU8522_TUNER_AGC_IF_START_REG0ACH 0x0AC
++#define AU8522_TUNER_AGC_IF_DEFAULT_REG0ADH 0x0AD
++#define AU8522_TUNER_AGC_STEP_REG0AEH 0x0AE
++#define AU8522_TUNER_GAIN_STEP_REG0AFH 0x0AF
++
++/* Receiver registers */
++#define AU8522_FRMREGTHRD1_REG0B0H 0x0B0
++#define AU8522_FRMREGAGC1H_REG0B1H 0x0B1
++#define AU8522_FRMREGSHIFT1_REG0B2H 0x0B2
++#define AU8522_TOREGAGC1_REG0B3H 0x0B3
++#define AU8522_TOREGASHIFT1_REG0B4H 0x0B4
++#define AU8522_FRMREGBBH_REG0B5H 0x0B5
++#define AU8522_FRMREGBBM_REG0B6H 0x0B6
++#define AU8522_FRMREGBBL_REG0B7H 0x0B7
++/* 0xB8 TO 0xD7 are the filter coefficients */
++#define AU8522_FRMREGTHRD2_REG0D8H 0x0D8
++#define AU8522_FRMREGAGC2H_REG0D9H 0x0D9
++#define AU8522_TOREGAGC2_REG0DAH 0x0DA
++#define AU8522_TOREGSHIFT2_REG0DBH 0x0DB
++#define AU8522_FRMREGPILOTH_REG0DCH 0x0DC
++#define AU8522_FRMREGPILOTM_REG0DDH 0x0DD
++#define AU8522_FRMREGPILOTL_REG0DEH 0x0DE
++#define AU8522_TOREGFREQ_REG0DFH 0x0DF
++
++#define AU8522_RX_PGA_RFOUT_REG0EBH 0x0EB
++#define AU8522_RX_PGA_IFOUT_REG0ECH 0x0EC
++#define AU8522_RX_PGA_PGAOUT_REG0EDH 0x0ED
++
++#define AU8522_CHIP_MODE_REG0FEH 0x0FE
++
++/* I2C bus control registers */
++#define AU8522_I2C_CONTROL_REG0_REG090H 0x090
++#define AU8522_I2C_CONTROL_REG1_REG091H 0x091
++#define AU8522_I2C_STATUS_REG092H 0x092
++#define AU8522_I2C_WR_DATA0_REG093H 0x093
++#define AU8522_I2C_WR_DATA1_REG094H 0x094
++#define AU8522_I2C_WR_DATA2_REG095H 0x095
++#define AU8522_I2C_WR_DATA3_REG096H 0x096
++#define AU8522_I2C_WR_DATA4_REG097H 0x097
++#define AU8522_I2C_WR_DATA5_REG098H 0x098
++#define AU8522_I2C_WR_DATA6_REG099H 0x099
++#define AU8522_I2C_WR_DATA7_REG09AH 0x09A
++#define AU8522_I2C_RD_DATA0_REG09BH 0x09B
++#define AU8522_I2C_RD_DATA1_REG09CH 0x09C
++#define AU8522_I2C_RD_DATA2_REG09DH 0x09D
++#define AU8522_I2C_RD_DATA3_REG09EH 0x09E
++#define AU8522_I2C_RD_DATA4_REG09FH 0x09F
++#define AU8522_I2C_RD_DATA5_REG0A0H 0x0A0
++#define AU8522_I2C_RD_DATA6_REG0A1H 0x0A1
++#define AU8522_I2C_RD_DATA7_REG0A2H 0x0A2
++
++#define AU8522_ENA_USB_REG101H 0x101
++
++#define AU8522_I2S_CTRL_0_REG110H 0x110
++#define AU8522_I2S_CTRL_1_REG111H 0x111
++#define AU8522_I2S_CTRL_2_REG112H 0x112
++
++#define AU8522_FRMREGFFECONTROL_REG121H 0x121
++#define AU8522_FRMREGDFECONTROL_REG122H 0x122
++
++#define AU8522_CARRFREQOFFSET0_REG201H 0x201
++#define AU8522_CARRFREQOFFSET1_REG202H 0x202
++
++#define AU8522_DECIMATION_GAIN_REG21AH 0x21A
++#define AU8522_FRMREGIFSLP_REG21BH 0x21B
++#define AU8522_FRMREGTHRDL2_REG21CH 0x21C
++#define AU8522_FRMREGSTEP3DB_REG21DH 0x21D
++#define AU8522_DAGC_GAIN_ADJUSTMENT_REG21EH 0x21E
++#define AU8522_FRMREGPLLMODE_REG21FH 0x21F
++#define AU8522_FRMREGCSTHRD_REG220H 0x220
++#define AU8522_FRMREGCRLOCKDMAX_REG221H 0x221
++#define AU8522_FRMREGCRPERIODMASK_REG222H 0x222
++#define AU8522_FRMREGCRLOCK0THH_REG223H 0x223
++#define AU8522_FRMREGCRLOCK1THH_REG224H 0x224
++#define AU8522_FRMREGCRLOCK0THL_REG225H 0x225
++#define AU8522_FRMREGCRLOCK1THL_REG226H 0x226
++#define AU_FRMREGPLLACQPHASESCL_REG227H 0x227
++#define AU8522_FRMREGFREQFBCTRL_REG228H 0x228
++
++/* Analog TV Decoder */
++#define AU8522_TVDEC_STATUS_REG000H 0x000
++#define AU8522_TVDEC_INT_STATUS_REG001H 0x001
++#define AU8522_TVDEC_MACROVISION_STATUS_REG002H 0x002
++#define AU8522_TVDEC_SHARPNESSREG009H 0x009
++#define AU8522_TVDEC_BRIGHTNESS_REG00AH 0x00A
++#define AU8522_TVDEC_CONTRAST_REG00BH 0x00B
++#define AU8522_TVDEC_SATURATION_CB_REG00CH 0x00C
++#define AU8522_TVDEC_SATURATION_CR_REG00DH 0x00D
++#define AU8522_TVDEC_HUE_H_REG00EH 0x00E
++#define AU8522_TVDEC_HUE_L_REG00FH 0x00F
++#define AU8522_TVDEC_INT_MASK_REG010H 0x010
++#define AU8522_VIDEO_MODE_REG011H 0x011
++#define AU8522_TVDEC_PGA_REG012H 0x012
++#define AU8522_TVDEC_COMB_MODE_REG015H 0x015
++#define AU8522_REG016H 0x016
++#define AU8522_TVDED_DBG_MODE_REG060H 0x060
++#define AU8522_TVDEC_FORMAT_CTRL1_REG061H 0x061
++#define AU8522_TVDEC_FORMAT_CTRL2_REG062H 0x062
++#define AU8522_TVDEC_VCR_DET_LLIM_REG063H 0x063
++#define AU8522_TVDEC_VCR_DET_HLIM_REG064H 0x064
++#define AU8522_TVDEC_COMB_VDIF_THR1_REG065H 0x065
++#define AU8522_TVDEC_COMB_VDIF_THR2_REG066H 0x066
++#define AU8522_TVDEC_COMB_VDIF_THR3_REG067H 0x067
++#define AU8522_TVDEC_COMB_NOTCH_THR_REG068H 0x068
++#define AU8522_TVDEC_COMB_HDIF_THR1_REG069H 0x069
++#define AU8522_TVDEC_COMB_HDIF_THR2_REG06AH 0x06A
++#define AU8522_TVDEC_COMB_HDIF_THR3_REG06BH 0x06B
++#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH 0x06C
++#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH 0x06D
++#define AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH 0x06E
++#define AU8522_TVDEC_UV_SEP_THR_REG06FH 0x06F
++#define AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H 0x070
++#define AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H 0x073
++#define AU8522_TVDEC_DCAGC_CTRL_REG077H 0x077
++#define AU8522_TVDEC_PIC_START_ADJ_REG078H 0x078
++#define AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H 0x079
++#define AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH 0x07A
++#define AU8522_TVDEC_INTRP_CTRL_REG07BH 0x07B
++#define AU8522_TVDEC_PLL_STATUS_REG07EH 0x07E
++#define AU8522_TVDEC_FSC_FREQ_REG07FH 0x07F
++
++#define AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H 0x0E4
++#define AU8522_TOREGAAGC_REG0E5H 0x0E5
++
++#define AU8522_TVDEC_CHROMA_AGC_REG401H 0x401
++#define AU8522_TVDEC_CHROMA_SFT_REG402H 0x402
++#define AU8522_FILTER_COEF_R410 0x410
++#define AU8522_FILTER_COEF_R411 0x411
++#define AU8522_FILTER_COEF_R412 0x412
++#define AU8522_FILTER_COEF_R413 0x413
++#define AU8522_FILTER_COEF_R414 0x414
++#define AU8522_FILTER_COEF_R415 0x415
++#define AU8522_FILTER_COEF_R416 0x416
++#define AU8522_FILTER_COEF_R417 0x417
++#define AU8522_FILTER_COEF_R418 0x418
++#define AU8522_FILTER_COEF_R419 0x419
++#define AU8522_FILTER_COEF_R41A 0x41A
++#define AU8522_FILTER_COEF_R41B 0x41B
++#define AU8522_FILTER_COEF_R41C 0x41C
++#define AU8522_FILTER_COEF_R41D 0x41D
++#define AU8522_FILTER_COEF_R41E 0x41E
++#define AU8522_FILTER_COEF_R41F 0x41F
++#define AU8522_FILTER_COEF_R420 0x420
++#define AU8522_FILTER_COEF_R421 0x421
++#define AU8522_FILTER_COEF_R422 0x422
++#define AU8522_FILTER_COEF_R423 0x423
++#define AU8522_FILTER_COEF_R424 0x424
++#define AU8522_FILTER_COEF_R425 0x425
++#define AU8522_FILTER_COEF_R426 0x426
++#define AU8522_FILTER_COEF_R427 0x427
++#define AU8522_FILTER_COEF_R428 0x428
++#define AU8522_FILTER_COEF_R429 0x429
++#define AU8522_FILTER_COEF_R42A 0x42A
++#define AU8522_FILTER_COEF_R42B 0x42B
++#define AU8522_FILTER_COEF_R42C 0x42C
++#define AU8522_FILTER_COEF_R42D 0x42D
++
++/* VBI Control Registers */
++#define AU8522_TVDEC_VBI_RX_FIFO_CONTAIN_REG004H 0x004
++#define AU8522_TVDEC_VBI_TX_FIFO_CONTAIN_REG005H 0x005
++#define AU8522_TVDEC_VBI_RX_FIFO_READ_REG006H 0x006
++#define AU8522_TVDEC_VBI_FIFO_STATUS_REG007H 0x007
++#define AU8522_TVDEC_VBI_CTRL_H_REG017H 0x017
++#define AU8522_TVDEC_VBI_CTRL_L_REG018H 0x018
++#define AU8522_TVDEC_VBI_USER_TOTAL_BITS_REG019H 0x019
++#define AU8522_TVDEC_VBI_USER_TUNIT_H_REG01AH 0x01A
++#define AU8522_TVDEC_VBI_USER_TUNIT_L_REG01BH 0x01B
++#define AU8522_TVDEC_VBI_USER_THRESH1_REG01CH 0x01C
++#define AU8522_TVDEC_VBI_USER_FRAME_PAT2_REG01EH 0x01E
++#define AU8522_TVDEC_VBI_USER_FRAME_PAT1_REG01FH 0x01F
++#define AU8522_TVDEC_VBI_USER_FRAME_PAT0_REG020H 0x020
++#define AU8522_TVDEC_VBI_USER_FRAME_MASK2_REG021H 0x021
++#define AU8522_TVDEC_VBI_USER_FRAME_MASK1_REG022H 0x022
++#define AU8522_TVDEC_VBI_USER_FRAME_MASK0_REG023H 0x023
++
++#define AU8522_REG071H 0x071
++#define AU8522_REG072H 0x072
++#define AU8522_REG074H 0x074
++#define AU8522_REG075H 0x075
++
++/* Digital Demodulator Registers */
++#define AU8522_FRAME_COUNT0_REG084H 0x084
++#define AU8522_RS_STATUS_G0_REG085H 0x085
++#define AU8522_RS_STATUS_B0_REG086H 0x086
++#define AU8522_RS_STATUS_E_REG087H 0x087
++#define AU8522_DEMODULATION_STATUS_REG088H 0x088
++#define AU8522_TOREGTRESTATUS_REG0E6H 0x0E6
++#define AU8522_TSPORT_CONTROL_REG10BH 0x10B
++#define AU8522_TSTHES_REG10CH 0x10C
++#define AU8522_FRMREGDFEKEEP_REG301H 0x301
++#define AU8522_DFE_AVERAGE_REG302H 0x302
++#define AU8522_FRMREGEQLERRWIN_REG303H 0x303
++#define AU8522_FRMREGFFEKEEP_REG304H 0x304
++#define AU8522_FRMREGDFECONTROL1_REG305H 0x305
++#define AU8522_FRMREGEQLERRLOW_REG306H 0x306
++
++#define AU8522_REG42EH 0x42E
++#define AU8522_REG42FH 0x42F
++#define AU8522_REG430H 0x430
++#define AU8522_REG431H 0x431
++#define AU8522_REG432H 0x432
++#define AU8522_REG433H 0x433
++#define AU8522_REG434H 0x434
++#define AU8522_REG435H 0x435
++#define AU8522_REG436H 0x436
++
++/* GPIO Registers */
++#define AU8522_GPIO_CONTROL_REG0E0H 0x0E0
++#define AU8522_GPIO_STATUS_REG0E1H 0x0E1
++#define AU8522_GPIO_DATA_REG0E2H 0x0E2
++
++/* Audio Control Registers */
++#define AU8522_AUDIOAGC_REG0EEH 0x0EE
++#define AU8522_AUDIO_STATUS_REG0F0H 0x0F0
++#define AU8522_AUDIO_MODE_REG0F1H 0x0F1
++#define AU8522_AUDIO_VOLUME_L_REG0F2H 0x0F2
++#define AU8522_AUDIO_VOLUME_R_REG0F3H 0x0F3
++#define AU8522_AUDIO_VOLUME_REG0F4H 0x0F4
++#define AU8522_FRMREGAUPHASE_REG0F7H 0x0F7
++#define AU8522_REG0F9H 0x0F9
++
++#define AU8522_AUDIOAGC2_REG605H 0x605
++#define AU8522_AUDIOFREQ_REG606H 0x606
++
++
++/**************************************************************/
++
++#define AU8522_INPUT_CONTROL_REG081H_ATSC 0xC4
++#define AU8522_INPUT_CONTROL_REG081H_ATVRF 0xC4
++#define AU8522_INPUT_CONTROL_REG081H_ATVRF13 0xC4
++#define AU8522_INPUT_CONTROL_REG081H_J83B64 0xC4
++#define AU8522_INPUT_CONTROL_REG081H_J83B256 0xC4
++#define AU8522_INPUT_CONTROL_REG081H_CVBS 0x20
++#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH1 0xA2
++#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH2 0xA0
++#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH3 0x69
++#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH4 0x68
++#define AU8522_INPUT_CONTROL_REG081H_CVBS_CH4_SIF 0x28
++/* CH1 AS Y,CH3 AS C */
++#define AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH13 0x23
++/* CH2 AS Y,CH4 AS C */
++#define AU8522_INPUT_CONTROL_REG081H_SVIDEO_CH24 0x20
++#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATSC 0x0C
++#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_J83B64 0x09
++#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_J83B256 0x09
++#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_CVBS 0x12
++#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATVRF 0x1A
++#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_ATVRF13 0x1A
++#define AU8522_MODULE_CLOCK_CONTROL_REG0A3H_SVIDEO 0x02
++
++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CLEAR 0x00
++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_SVIDEO 0x9C
++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_CVBS 0x9D
++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATSC 0xE8
++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_J83B256 0xCA
++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_J83B64 0xCA
++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATVRF 0xDD
++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_ATVRF13 0xDD
++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_PAL 0xDD
++#define AU8522_SYSTEM_MODULE_CONTROL_0_REG0A4H_FM 0xDD
++
++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATSC 0x80
++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_J83B256 0x80
++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_J83B64 0x80
++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_ATSC 0x40
++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_J83B256 0x40
++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_J83B64 0x40
++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_DONGLE_CLEAR 0x00
++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATVRF 0x01
++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_ATVRF13 0x01
++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_SVIDEO 0x04
++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_CVBS 0x01
++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_PWM 0x03
++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_IIS 0x09
++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_PAL 0x01
++#define AU8522_SYSTEM_MODULE_CONTROL_1_REG0A5H_FM 0x01
++
++/* STILL NEED TO BE REFACTORED @@@@@@@@@@@@@@ */
++#define AU8522_TVDEC_CONTRAST_REG00BH_CVBS 0x79
++#define AU8522_TVDEC_SATURATION_CB_REG00CH_CVBS 0x80
++#define AU8522_TVDEC_SATURATION_CR_REG00DH_CVBS 0x80
++#define AU8522_TVDEC_HUE_H_REG00EH_CVBS 0x00
++#define AU8522_TVDEC_HUE_L_REG00FH_CVBS 0x00
++#define AU8522_TVDEC_PGA_REG012H_CVBS 0x0F
++#define AU8522_TVDEC_COMB_MODE_REG015H_CVBS 0x00
++#define AU8522_REG016H_CVBS 0x00
++#define AU8522_TVDED_DBG_MODE_REG060H_CVBS 0x00
++#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_CVBS 0x0B
++#define AU8522_TVDEC_FORMAT_CTRL1_REG061H_CVBS13 0x03
++#define AU8522_TVDEC_FORMAT_CTRL2_REG062H_CVBS13 0x00
++#define AU8522_TVDEC_VCR_DET_LLIM_REG063H_CVBS 0x19
++#define AU8522_REG0F9H_AUDIO 0x20
++#define AU8522_TVDEC_VCR_DET_HLIM_REG064H_CVBS 0xA7
++#define AU8522_TVDEC_COMB_VDIF_THR1_REG065H_CVBS 0x0A
++#define AU8522_TVDEC_COMB_VDIF_THR2_REG066H_CVBS 0x32
++#define AU8522_TVDEC_COMB_VDIF_THR3_REG067H_CVBS 0x19
++#define AU8522_TVDEC_COMB_NOTCH_THR_REG068H_CVBS 0x23
++#define AU8522_TVDEC_COMB_HDIF_THR1_REG069H_CVBS 0x41
++#define AU8522_TVDEC_COMB_HDIF_THR2_REG06AH_CVBS 0x0A
++#define AU8522_TVDEC_COMB_HDIF_THR3_REG06BH_CVBS 0x32
++#define AU8522_TVDEC_COMB_DCDIF_THR1_REG06CH_CVBS 0x34
++#define AU8522_TVDEC_COMB_DCDIF_THR2_REG06DH_CVBS 0x05
++#define AU8522_TVDEC_COMB_DCDIF_THR3_REG06EH_CVBS 0x6E
++#define AU8522_TVDEC_UV_SEP_THR_REG06FH_CVBS 0x0F
++#define AU8522_TVDEC_COMB_DC_THR1_NTSC_REG070H_CVBS 0x80
++#define AU8522_REG071H_CVBS 0x18
++#define AU8522_REG072H_CVBS 0x30
++#define AU8522_TVDEC_COMB_DC_THR2_NTSC_REG073H_CVBS 0xF0
++#define AU8522_REG074H_CVBS 0x80
++#define AU8522_REG075H_CVBS 0xF0
++#define AU8522_TVDEC_DCAGC_CTRL_REG077H_CVBS 0xFB
++#define AU8522_TVDEC_PIC_START_ADJ_REG078H_CVBS 0x04
++#define AU8522_TVDEC_AGC_HIGH_LIMIT_REG079H_CVBS 0x00
++#define AU8522_TVDEC_MACROVISION_SYNC_THR_REG07AH_CVBS 0x00
++#define AU8522_TVDEC_INTRP_CTRL_REG07BH_CVBS 0xEE
++#define AU8522_TVDEC_AGC_LOW_LIMIT_REG0E4H_CVBS 0xFE
++#define AU8522_TOREGAAGC_REG0E5H_CVBS 0x00
++#define AU8522_TVDEC_VBI6A_REG035H_CVBS 0x40
++
++/* Enables Closed captioning */
++#define AU8522_TVDEC_VBI_CTRL_H_REG017H_CCON 0x21
+diff --git a/drivers/media/dvb/frontends/cx24113.c b/drivers/media/dvb/frontends/cx24113.c
+index f6e7b03..e4fd533 100644
+--- a/drivers/media/dvb/frontends/cx24113.c
++++ b/drivers/media/dvb/frontends/cx24113.c
+@@ -559,7 +559,7 @@ struct dvb_frontend *cx24113_attach(struct dvb_frontend *fe,
+ kzalloc(sizeof(struct cx24113_state), GFP_KERNEL);
+ int rc;
+ if (state == NULL) {
+- err("Unable to kmalloc\n");
++ err("Unable to kzalloc\n");
+ goto error;
+ }
+
+diff --git a/drivers/media/dvb/frontends/cx24116.c b/drivers/media/dvb/frontends/cx24116.c
+index 28ad609..9b9f572 100644
+--- a/drivers/media/dvb/frontends/cx24116.c
++++ b/drivers/media/dvb/frontends/cx24116.c
+@@ -15,6 +15,9 @@
+ September, 9th 2008
+ Fixed locking on high symbol rates (>30000).
+ Implement MPEG initialization parameter.
++ January, 17th 2009
++ Fill set_voltage with actually control voltage code.
++ Correct set tone to not affect voltage.
+
+ 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
+@@ -146,7 +149,7 @@ enum cmds {
+ CMD_GETAGC = 0x19,
+ CMD_LNBCONFIG = 0x20,
+ CMD_LNBSEND = 0x21, /* Formerly CMD_SEND_DISEQC */
+- CMD_SET_TONEPRE = 0x22,
++ CMD_LNBDCLEVEL = 0x22,
+ CMD_SET_TONE = 0x23,
+ CMD_UPDFWVERS = 0x35,
+ CMD_TUNERSLEEP = 0x36,
+@@ -667,16 +670,6 @@ static int cx24116_load_firmware(struct dvb_frontend *fe,
+ return 0;
+ }
+
+-static int cx24116_set_voltage(struct dvb_frontend *fe,
+- fe_sec_voltage_t voltage)
+-{
+- /* The isl6421 module will override this function in the fops. */
+- dprintk("%s() This should never appear if the isl6421 module "
+- "is loaded correctly\n", __func__);
+-
+- return -EOPNOTSUPP;
+-}
+-
+ static int cx24116_read_status(struct dvb_frontend *fe, fe_status_t *status)
+ {
+ struct cx24116_state *state = fe->demodulator_priv;
+@@ -837,6 +830,34 @@ static int cx24116_wait_for_lnb(struct dvb_frontend *fe)
+ return -ETIMEDOUT; /* -EBUSY ? */
+ }
+
++static int cx24116_set_voltage(struct dvb_frontend *fe,
++ fe_sec_voltage_t voltage)
++{
++ struct cx24116_cmd cmd;
++ int ret;
++
++ dprintk("%s: %s\n", __func__,
++ voltage == SEC_VOLTAGE_13 ? "SEC_VOLTAGE_13" :
++ voltage == SEC_VOLTAGE_18 ? "SEC_VOLTAGE_18" : "??");
++
++ /* Wait for LNB ready */
++ ret = cx24116_wait_for_lnb(fe);
++ if (ret != 0)
++ return ret;
++
++ /* Wait for voltage/min repeat delay */
++ msleep(100);
++
++ cmd.args[0x00] = CMD_LNBDCLEVEL;
++ cmd.args[0x01] = (voltage == SEC_VOLTAGE_18 ? 0x01 : 0x00);
++ cmd.len = 0x02;
++
++ /* Min delay time before DiSEqC send */
++ msleep(15);
++
++ return cx24116_cmd_execute(fe, &cmd);
++}
++
+ static int cx24116_set_tone(struct dvb_frontend *fe,
+ fe_sec_tone_mode_t tone)
+ {
+@@ -857,14 +878,6 @@ static int cx24116_set_tone(struct dvb_frontend *fe,
+ /* Min delay time after DiSEqC send */
+ msleep(15); /* XXX determine is FW does this, see send_diseqc/burst */
+
+- /* This is always done before the tone is set */
+- cmd.args[0x00] = CMD_SET_TONEPRE;
+- cmd.args[0x01] = 0x00;
+- cmd.len = 0x02;
+- ret = cx24116_cmd_execute(fe, &cmd);
+- if (ret != 0)
+- return ret;
+-
+ /* Now we set the tone */
+ cmd.args[0x00] = CMD_SET_TONE;
+ cmd.args[0x01] = 0x00;
+@@ -1099,13 +1112,10 @@ struct dvb_frontend *cx24116_attach(const struct cx24116_config *config,
+ dprintk("%s\n", __func__);
+
+ /* allocate memory for the internal state */
+- state = kmalloc(sizeof(struct cx24116_state), GFP_KERNEL);
++ state = kzalloc(sizeof(struct cx24116_state), GFP_KERNEL);
+ if (state == NULL)
+ goto error1;
+
+- /* setup the state */
+- memset(state, 0, sizeof(struct cx24116_state));
+-
+ state->config = config;
+ state->i2c = i2c;
+
+@@ -1154,7 +1164,12 @@ static int cx24116_initfe(struct dvb_frontend *fe)
+ if (ret != 0)
+ return ret;
+
+- return cx24116_diseqc_init(fe);
++ ret = cx24116_diseqc_init(fe);
++ if (ret != 0)
++ return ret;
++
++ /* HVR-4000 needs this */
++ return cx24116_set_voltage(fe, SEC_VOLTAGE_13);
+ }
+
+ /*
+diff --git a/drivers/media/dvb/frontends/cx24123.c b/drivers/media/dvb/frontends/cx24123.c
+index 1a8c36f..0592f04 100644
+--- a/drivers/media/dvb/frontends/cx24123.c
++++ b/drivers/media/dvb/frontends/cx24123.c
+@@ -1069,13 +1069,13 @@ static struct dvb_frontend_ops cx24123_ops;
+ struct dvb_frontend *cx24123_attach(const struct cx24123_config *config,
+ struct i2c_adapter *i2c)
+ {
++ /* allocate memory for the internal state */
+ struct cx24123_state *state =
+ kzalloc(sizeof(struct cx24123_state), GFP_KERNEL);
+
+ dprintk("\n");
+- /* allocate memory for the internal state */
+ if (state == NULL) {
+- err("Unable to kmalloc\n");
++ err("Unable to kzalloc\n");
+ goto error;
+ }
+
+diff --git a/drivers/media/dvb/frontends/dib0070.h b/drivers/media/dvb/frontends/dib0070.h
+index 21f2c51..9670f5d 100644
+--- a/drivers/media/dvb/frontends/dib0070.h
++++ b/drivers/media/dvb/frontends/dib0070.h
+@@ -58,6 +58,4 @@ static inline u16 dib0070_wbd_offset(struct dvb_frontend *fe)
+ }
+ #endif
+
+-extern void dib0070_ctrl_agc_filter(struct dvb_frontend *, uint8_t open);
+-
+ #endif
+diff --git a/drivers/media/dvb/frontends/dib3000mc.h b/drivers/media/dvb/frontends/dib3000mc.h
+index 4142ed7..d75ffad 100644
+--- a/drivers/media/dvb/frontends/dib3000mc.h
++++ b/drivers/media/dvb/frontends/dib3000mc.h
+@@ -39,19 +39,43 @@ struct dib3000mc_config {
+ #define DEFAULT_DIB3000MC_I2C_ADDRESS 16
+ #define DEFAULT_DIB3000P_I2C_ADDRESS 24
+
+-#if defined(CONFIG_DVB_DIB3000MC) || (defined(CONFIG_DVB_DIB3000MC_MODULE) && defined(MODULE))
+-extern struct dvb_frontend * dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib3000mc_config *cfg);
++#if defined(CONFIG_DVB_DIB3000MC) || (defined(CONFIG_DVB_DIB3000MC_MODULE) && \
++ defined(MODULE))
++extern struct dvb_frontend *dib3000mc_attach(struct i2c_adapter *i2c_adap,
++ u8 i2c_addr,
++ struct dib3000mc_config *cfg);
++extern int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c,
++ int no_of_demods, u8 default_addr,
++ struct dib3000mc_config cfg[]);
++extern
++struct i2c_adapter *dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod,
++ int gating);
+ #else
+-static inline struct dvb_frontend * dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib3000mc_config *cfg)
++static inline
++struct dvb_frontend *dib3000mc_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr,
++ struct dib3000mc_config *cfg)
+ {
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+ }
+-#endif // CONFIG_DVB_DIB3000MC
+
+-extern int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c, int no_of_demods, u8 default_addr, struct dib3000mc_config cfg[]);
++static inline
++int dib3000mc_i2c_enumeration(struct i2c_adapter *i2c,
++ int no_of_demods, u8 default_addr,
++ struct dib3000mc_config cfg[])
++{
++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
++ return -ENODEV;
++}
+
+-extern struct i2c_adapter * dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, int gating);
++static inline
++struct i2c_adapter *dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod,
++ int gating)
++{
++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
++ return NULL;
++}
++#endif // CONFIG_DVB_DIB3000MC
+
+ extern int dib3000mc_pid_control(struct dvb_frontend *fe, int index, int pid,int onoff);
+ extern int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff);
+diff --git a/drivers/media/dvb/frontends/dib7000m.h b/drivers/media/dvb/frontends/dib7000m.h
+index 597e9cc..113819c 100644
+--- a/drivers/media/dvb/frontends/dib7000m.h
++++ b/drivers/media/dvb/frontends/dib7000m.h
+@@ -38,8 +38,32 @@ struct dib7000m_config {
+
+ #define DEFAULT_DIB7000M_I2C_ADDRESS 18
+
+-extern struct dvb_frontend * dib7000m_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr, struct dib7000m_config *cfg);
+-extern struct i2c_adapter * dib7000m_get_i2c_master(struct dvb_frontend *, enum dibx000_i2c_interface, int);
++#if defined(CONFIG_DVB_DIB7000M) || (defined(CONFIG_DVB_DIB7000M_MODULE) && \
++ defined(MODULE))
++extern struct dvb_frontend *dib7000m_attach(struct i2c_adapter *i2c_adap,
++ u8 i2c_addr,
++ struct dib7000m_config *cfg);
++extern struct i2c_adapter *dib7000m_get_i2c_master(struct dvb_frontend *,
++ enum dibx000_i2c_interface,
++ int);
++#else
++static inline
++struct dvb_frontend *dib7000m_attach(struct i2c_adapter *i2c_adap,
++ u8 i2c_addr, struct dib7000m_config *cfg)
++{
++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
++ return NULL;
++}
++
++static inline
++struct i2c_adapter *dib7000m_get_i2c_master(struct dvb_frontend *demod,
++ enum dibx000_i2c_interface intf,
++ int gating)
++{
++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
++ return NULL;
++}
++#endif
+
+ /* TODO
+ extern INT dib7000m_set_gpio(struct dibDemod *demod, UCHAR num, UCHAR dir, UCHAR val);
+diff --git a/drivers/media/dvb/frontends/dib7000p.h b/drivers/media/dvb/frontends/dib7000p.h
+index aab8112..02a4c82 100644
+--- a/drivers/media/dvb/frontends/dib7000p.h
++++ b/drivers/media/dvb/frontends/dib7000p.h
+@@ -37,7 +37,8 @@ struct dib7000p_config {
+
+ #define DEFAULT_DIB7000P_I2C_ADDRESS 18
+
+-#if defined(CONFIG_DVB_DIB7000P) || (defined(CONFIG_DVB_DIB7000P_MODULE) && defined(MODULE))
++#if defined(CONFIG_DVB_DIB7000P) || (defined(CONFIG_DVB_DIB7000P_MODULE) && \
++ defined(MODULE))
+ extern struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap,
+ u8 i2c_addr,
+ struct dib7000p_config *cfg);
+@@ -49,10 +50,11 @@ extern int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
+ struct dib7000p_config cfg[]);
+ extern int dib7000p_set_gpio(struct dvb_frontend *, u8 num, u8 dir, u8 val);
+ extern int dib7000p_set_wbd_ref(struct dvb_frontend *, u16 value);
++extern int dib7000pc_detection(struct i2c_adapter *i2c_adap);
+ #else
+-static inline struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap,
+- u8 i2c_addr,
+- struct dib7000p_config *cfg)
++static inline
++struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap, u8 i2c_addr,
++ struct dib7000p_config *cfg)
+ {
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+@@ -60,36 +62,39 @@ static inline struct dvb_frontend *dib7000p_attach(struct i2c_adapter *i2c_adap,
+
+ static inline
+ struct i2c_adapter *dib7000p_get_i2c_master(struct dvb_frontend *fe,
+- enum dibx000_i2c_interface i, int x)
++ enum dibx000_i2c_interface i,
++ int x)
+ {
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+ }
+
+-static inline
+-int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
+- int no_of_demods, u8 default_addr,
+- struct dib7000p_config cfg[])
++static inline int dib7000p_i2c_enumeration(struct i2c_adapter *i2c,
++ int no_of_demods, u8 default_addr,
++ struct dib7000p_config cfg[])
+ {
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+ }
+
+-static inline
+-int dib7000p_set_gpio(struct dvb_frontend *fe, u8 num, u8 dir, u8 val)
++static inline int dib7000p_set_gpio(struct dvb_frontend *fe,
++ u8 num, u8 dir, u8 val)
+ {
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+ }
+
+-static inline
+-int dib7000p_set_wbd_ref(struct dvb_frontend *fe, u16 value)
++static inline int dib7000p_set_wbd_ref(struct dvb_frontend *fe, u16 value)
+ {
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return -ENODEV;
+ }
+-#endif
+
+-extern int dib7000pc_detection(struct i2c_adapter *i2c_adap);
++static inline int dib7000pc_detection(struct i2c_adapter *i2c_adap)
++{
++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
++ return -ENODEV;
++}
++#endif
+
+ #endif
+diff --git a/drivers/media/dvb/frontends/dvb_dummy_fe.h b/drivers/media/dvb/frontends/dvb_dummy_fe.h
+index 8210f19..1fcb987 100644
+--- a/drivers/media/dvb/frontends/dvb_dummy_fe.h
++++ b/drivers/media/dvb/frontends/dvb_dummy_fe.h
+@@ -25,8 +25,27 @@
+ #include <linux/dvb/frontend.h>
+ #include "dvb_frontend.h"
+
++#if defined(CONFIG_DVB_DUMMY_FE) || (defined(CONFIG_DVB_DUMMY_FE_MODULE) && \
++defined(MODULE))
+ extern struct dvb_frontend* dvb_dummy_fe_ofdm_attach(void);
+ extern struct dvb_frontend* dvb_dummy_fe_qpsk_attach(void);
+ extern struct dvb_frontend* dvb_dummy_fe_qam_attach(void);
++#else
++static inline struct dvb_frontend *dvb_dummy_fe_ofdm_attach(void)
++{
++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
++ return NULL;
++}
++static inline struct dvb_frontend *dvb_dummy_fe_qpsk_attach(void)
++{
++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
++ return NULL;
++}
++static inline struct dvb_frontend *dvb_dummy_fe_qam_attach(void)
++{
++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
++ return NULL;
++}
++#endif /* CONFIG_DVB_DUMMY_FE */
+
+ #endif // DVB_DUMMY_FE_H
+diff --git a/drivers/media/dvb/frontends/itd1000_priv.h b/drivers/media/dvb/frontends/itd1000_priv.h
+index 8cdc54e..08ca851 100644
+--- a/drivers/media/dvb/frontends/itd1000_priv.h
++++ b/drivers/media/dvb/frontends/itd1000_priv.h
+@@ -31,7 +31,7 @@ struct itd1000_state {
+ /* ugly workaround for flexcop's incapable i2c-controller
+ * FIXME, if possible
+ */
+- u8 shadow[255];
++ u8 shadow[256];
+ };
+
+ enum itd1000_register {
+diff --git a/drivers/media/dvb/frontends/lgdt3304.c b/drivers/media/dvb/frontends/lgdt3304.c
+index 3bb0c43..eb72a98 100644
+--- a/drivers/media/dvb/frontends/lgdt3304.c
++++ b/drivers/media/dvb/frontends/lgdt3304.c
+@@ -363,7 +363,6 @@ struct dvb_frontend* lgdt3304_attach(const struct lgdt3304_config *config,
+
+ struct lgdt3304_state *state;
+ state = kzalloc(sizeof(struct lgdt3304_state), GFP_KERNEL);
+- memset(state, 0x0, sizeof(struct lgdt3304_state));
+ state->addr = config->i2c_address;
+ state->i2c = i2c;
+
+diff --git a/drivers/media/dvb/frontends/lgdt3305.c b/drivers/media/dvb/frontends/lgdt3305.c
+new file mode 100644
+index 0000000..d92d055
+--- /dev/null
++++ b/drivers/media/dvb/frontends/lgdt3305.c
+@@ -0,0 +1,1087 @@
++/*
++ * Support for LGDT3305 - VSB/QAM
++ *
++ * Copyright (C) 2008, 2009 Michael Krufky <mkrufky@linuxtv.org>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ */
++
++#include <linux/dvb/frontend.h>
++#include "dvb_math.h"
++#include "lgdt3305.h"
++
++static int debug;
++module_param(debug, int, 0644);
++MODULE_PARM_DESC(debug, "set debug level (info=1, reg=2 (or-able))");
++
++#define DBG_INFO 1
++#define DBG_REG 2
++
++#define lg_printk(kern, fmt, arg...) \
++ printk(kern "%s: " fmt, __func__, ##arg)
++
++#define lg_info(fmt, arg...) printk(KERN_INFO "lgdt3305: " fmt, ##arg)
++#define lg_warn(fmt, arg...) lg_printk(KERN_WARNING, fmt, ##arg)
++#define lg_err(fmt, arg...) lg_printk(KERN_ERR, fmt, ##arg)
++#define lg_dbg(fmt, arg...) if (debug & DBG_INFO) \
++ lg_printk(KERN_DEBUG, fmt, ##arg)
++#define lg_reg(fmt, arg...) if (debug & DBG_REG) \
++ lg_printk(KERN_DEBUG, fmt, ##arg)
++
++#define lg_fail(ret) \
++({ \
++ int __ret; \
++ __ret = (ret < 0); \
++ if (__ret) \
++ lg_err("error %d on line %d\n", ret, __LINE__); \
++ __ret; \
++})
++
++struct lgdt3305_state {
++ struct i2c_adapter *i2c_adap;
++ const struct lgdt3305_config *cfg;
++
++ struct dvb_frontend frontend;
++
++ fe_modulation_t current_modulation;
++ u32 current_frequency;
++ u32 snr;
++};
++
++/* ------------------------------------------------------------------------ */
++
++#define LGDT3305_GEN_CTRL_1 0x0000
++#define LGDT3305_GEN_CTRL_2 0x0001
++#define LGDT3305_GEN_CTRL_3 0x0002
++#define LGDT3305_GEN_STATUS 0x0003
++#define LGDT3305_GEN_CONTROL 0x0007
++#define LGDT3305_GEN_CTRL_4 0x000a
++#define LGDT3305_DGTL_AGC_REF_1 0x0012
++#define LGDT3305_DGTL_AGC_REF_2 0x0013
++#define LGDT3305_CR_CTR_FREQ_1 0x0106
++#define LGDT3305_CR_CTR_FREQ_2 0x0107
++#define LGDT3305_CR_CTR_FREQ_3 0x0108
++#define LGDT3305_CR_CTR_FREQ_4 0x0109
++#define LGDT3305_CR_MSE_1 0x011b
++#define LGDT3305_CR_MSE_2 0x011c
++#define LGDT3305_CR_LOCK_STATUS 0x011d
++#define LGDT3305_CR_CTRL_7 0x0126
++#define LGDT3305_AGC_POWER_REF_1 0x0300
++#define LGDT3305_AGC_POWER_REF_2 0x0301
++#define LGDT3305_AGC_DELAY_PT_1 0x0302
++#define LGDT3305_AGC_DELAY_PT_2 0x0303
++#define LGDT3305_RFAGC_LOOP_FLTR_BW_1 0x0306
++#define LGDT3305_RFAGC_LOOP_FLTR_BW_2 0x0307
++#define LGDT3305_IFBW_1 0x0308
++#define LGDT3305_IFBW_2 0x0309
++#define LGDT3305_AGC_CTRL_1 0x030c
++#define LGDT3305_AGC_CTRL_4 0x0314
++#define LGDT3305_EQ_MSE_1 0x0413
++#define LGDT3305_EQ_MSE_2 0x0414
++#define LGDT3305_EQ_MSE_3 0x0415
++#define LGDT3305_PT_MSE_1 0x0417
++#define LGDT3305_PT_MSE_2 0x0418
++#define LGDT3305_PT_MSE_3 0x0419
++#define LGDT3305_FEC_BLOCK_CTRL 0x0504
++#define LGDT3305_FEC_LOCK_STATUS 0x050a
++#define LGDT3305_FEC_PKT_ERR_1 0x050c
++#define LGDT3305_FEC_PKT_ERR_2 0x050d
++#define LGDT3305_TP_CTRL_1 0x050e
++#define LGDT3305_BERT_PERIOD 0x0801
++#define LGDT3305_BERT_ERROR_COUNT_1 0x080a
++#define LGDT3305_BERT_ERROR_COUNT_2 0x080b
++#define LGDT3305_BERT_ERROR_COUNT_3 0x080c
++#define LGDT3305_BERT_ERROR_COUNT_4 0x080d
++
++static int lgdt3305_write_reg(struct lgdt3305_state *state, u16 reg, u8 val)
++{
++ int ret;
++ u8 buf[] = { reg >> 8, reg & 0xff, val };
++ struct i2c_msg msg = {
++ .addr = state->cfg->i2c_addr, .flags = 0,
++ .buf = buf, .len = 3,
++ };
++
++ lg_reg("reg: 0x%04x, val: 0x%02x\n", reg, val);
++
++ ret = i2c_transfer(state->i2c_adap, &msg, 1);
++
++ if (ret != 1) {
++ lg_err("error (addr %02x %02x <- %02x, err = %i)\n",
++ msg.buf[0], msg.buf[1], msg.buf[2], ret);
++ if (ret < 0)
++ return ret;
++ else
++ return -EREMOTEIO;
++ }
++ return 0;
++}
++
++static int lgdt3305_read_reg(struct lgdt3305_state *state, u16 reg, u8 *val)
++{
++ int ret;
++ u8 reg_buf[] = { reg >> 8, reg & 0xff };
++ struct i2c_msg msg[] = {
++ { .addr = state->cfg->i2c_addr,
++ .flags = 0, .buf = reg_buf, .len = 2 },
++ { .addr = state->cfg->i2c_addr,
++ .flags = I2C_M_RD, .buf = val, .len = 1 },
++ };
++
++ lg_reg("reg: 0x%04x\n", reg);
++
++ ret = i2c_transfer(state->i2c_adap, msg, 2);
++
++ if (ret != 2) {
++ lg_err("error (addr %02x reg %04x error (ret == %i)\n",
++ state->cfg->i2c_addr, reg, ret);
++ if (ret < 0)
++ return ret;
++ else
++ return -EREMOTEIO;
++ }
++ return 0;
++}
++
++#define read_reg(state, reg) \
++({ \
++ u8 __val; \
++ int ret = lgdt3305_read_reg(state, reg, &__val); \
++ if (lg_fail(ret)) \
++ __val = 0; \
++ __val; \
++})
++
++static int lgdt3305_set_reg_bit(struct lgdt3305_state *state,
++ u16 reg, int bit, int onoff)
++{
++ u8 val;
++ int ret;
++
++ lg_reg("reg: 0x%04x, bit: %d, level: %d\n", reg, bit, onoff);
++
++ ret = lgdt3305_read_reg(state, reg, &val);
++ if (lg_fail(ret))
++ goto fail;
++
++ val &= ~(1 << bit);
++ val |= (onoff & 1) << bit;
++
++ ret = lgdt3305_write_reg(state, reg, val);
++fail:
++ return ret;
++}
++
++struct lgdt3305_reg {
++ u16 reg;
++ u8 val;
++};
++
++static int lgdt3305_write_regs(struct lgdt3305_state *state,
++ struct lgdt3305_reg *regs, int len)
++{
++ int i, ret;
++
++ lg_reg("writing %d registers...\n", len);
++
++ for (i = 0; i < len - 1; i++) {
++ ret = lgdt3305_write_reg(state, regs[i].reg, regs[i].val);
++ if (lg_fail(ret))
++ return ret;
++ }
++ return 0;
++}
++
++/* ------------------------------------------------------------------------ */
++
++static int lgdt3305_soft_reset(struct lgdt3305_state *state)
++{
++ int ret;
++
++ lg_dbg("\n");
++
++ ret = lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_3, 0, 0);
++ if (lg_fail(ret))
++ goto fail;
++
++ msleep(20);
++ ret = lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_3, 0, 1);
++fail:
++ return ret;
++}
++
++static inline int lgdt3305_mpeg_mode(struct lgdt3305_state *state,
++ enum lgdt3305_mpeg_mode mode)
++{
++ lg_dbg("(%d)\n", mode);
++ return lgdt3305_set_reg_bit(state, LGDT3305_TP_CTRL_1, 5, mode);
++}
++
++static int lgdt3305_mpeg_mode_polarity(struct lgdt3305_state *state,
++ enum lgdt3305_tp_clock_edge edge,
++ enum lgdt3305_tp_valid_polarity valid)
++{
++ u8 val;
++ int ret;
++
++ lg_dbg("edge = %d, valid = %d\n", edge, valid);
++
++ ret = lgdt3305_read_reg(state, LGDT3305_TP_CTRL_1, &val);
++ if (lg_fail(ret))
++ goto fail;
++
++ val &= ~0x09;
++
++ if (edge)
++ val |= 0x08;
++ if (valid)
++ val |= 0x01;
++
++ ret = lgdt3305_write_reg(state, LGDT3305_TP_CTRL_1, val);
++ if (lg_fail(ret))
++ goto fail;
++
++ ret = lgdt3305_soft_reset(state);
++fail:
++ return ret;
++}
++
++static int lgdt3305_set_modulation(struct lgdt3305_state *state,
++ struct dvb_frontend_parameters *param)
++{
++ u8 opermode;
++ int ret;
++
++ lg_dbg("\n");
++
++ ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_1, &opermode);
++ if (lg_fail(ret))
++ goto fail;
++
++ opermode &= ~0x03;
++
++ switch (param->u.vsb.modulation) {
++ case VSB_8:
++ opermode |= 0x03;
++ break;
++ case QAM_64:
++ opermode |= 0x00;
++ break;
++ case QAM_256:
++ opermode |= 0x01;
++ break;
++ default:
++ return -EINVAL;
++ }
++ ret = lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_1, opermode);
++fail:
++ return ret;
++}
++
++static int lgdt3305_set_filter_extension(struct lgdt3305_state *state,
++ struct dvb_frontend_parameters *param)
++{
++ int val;
++
++ switch (param->u.vsb.modulation) {
++ case VSB_8:
++ val = 0;
++ break;
++ case QAM_64:
++ case QAM_256:
++ val = 1;
++ break;
++ default:
++ return -EINVAL;
++ }
++ lg_dbg("val = %d\n", val);
++
++ return lgdt3305_set_reg_bit(state, 0x043f, 2, val);
++}
++
++/* ------------------------------------------------------------------------ */
++
++static int lgdt3305_passband_digital_agc(struct lgdt3305_state *state,
++ struct dvb_frontend_parameters *param)
++{
++ u16 agc_ref;
++
++ switch (param->u.vsb.modulation) {
++ case VSB_8:
++ agc_ref = 0x32c4;
++ break;
++ case QAM_64:
++ agc_ref = 0x2a00;
++ break;
++ case QAM_256:
++ agc_ref = 0x2a80;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ lg_dbg("agc ref: 0x%04x\n", agc_ref);
++
++ lgdt3305_write_reg(state, LGDT3305_DGTL_AGC_REF_1, agc_ref >> 8);
++ lgdt3305_write_reg(state, LGDT3305_DGTL_AGC_REF_2, agc_ref & 0xff);
++
++ return 0;
++}
++
++static int lgdt3305_rfagc_loop(struct lgdt3305_state *state,
++ struct dvb_frontend_parameters *param)
++{
++ u16 ifbw, rfbw, agcdelay;
++
++ switch (param->u.vsb.modulation) {
++ case VSB_8:
++ agcdelay = 0x04c0;
++ rfbw = 0x8000;
++ ifbw = 0x8000;
++ break;
++ case QAM_64:
++ case QAM_256:
++ agcdelay = 0x046b;
++ rfbw = 0x8889;
++ ifbw = 0x8888;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ if (state->cfg->rf_agc_loop) {
++ lg_dbg("agcdelay: 0x%04x, rfbw: 0x%04x\n", agcdelay, rfbw);
++
++ /* rf agc loop filter bandwidth */
++ lgdt3305_write_reg(state, LGDT3305_AGC_DELAY_PT_1,
++ agcdelay >> 8);
++ lgdt3305_write_reg(state, LGDT3305_AGC_DELAY_PT_2,
++ agcdelay & 0xff);
++
++ lgdt3305_write_reg(state, LGDT3305_RFAGC_LOOP_FLTR_BW_1,
++ rfbw >> 8);
++ lgdt3305_write_reg(state, LGDT3305_RFAGC_LOOP_FLTR_BW_2,
++ rfbw & 0xff);
++ } else {
++ lg_dbg("ifbw: 0x%04x\n", ifbw);
++
++ /* if agc loop filter bandwidth */
++ lgdt3305_write_reg(state, LGDT3305_IFBW_1, ifbw >> 8);
++ lgdt3305_write_reg(state, LGDT3305_IFBW_2, ifbw & 0xff);
++ }
++
++ return 0;
++}
++
++static int lgdt3305_agc_setup(struct lgdt3305_state *state,
++ struct dvb_frontend_parameters *param)
++{
++ int lockdten, acqen;
++
++ switch (param->u.vsb.modulation) {
++ case VSB_8:
++ lockdten = 0;
++ acqen = 0;
++ break;
++ case QAM_64:
++ case QAM_256:
++ lockdten = 1;
++ acqen = 1;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ lg_dbg("lockdten = %d, acqen = %d\n", lockdten, acqen);
++
++ /* control agc function */
++ lgdt3305_write_reg(state, LGDT3305_AGC_CTRL_4, 0xe1 | lockdten << 1);
++ lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 2, acqen);
++
++ return lgdt3305_rfagc_loop(state, param);
++}
++
++static int lgdt3305_set_agc_power_ref(struct lgdt3305_state *state,
++ struct dvb_frontend_parameters *param)
++{
++ u16 usref = 0;
++
++ switch (param->u.vsb.modulation) {
++ case VSB_8:
++ if (state->cfg->usref_8vsb)
++ usref = state->cfg->usref_8vsb;
++ break;
++ case QAM_64:
++ if (state->cfg->usref_qam64)
++ usref = state->cfg->usref_qam64;
++ break;
++ case QAM_256:
++ if (state->cfg->usref_qam256)
++ usref = state->cfg->usref_qam256;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ if (usref) {
++ lg_dbg("set manual mode: 0x%04x\n", usref);
++
++ lgdt3305_set_reg_bit(state, LGDT3305_AGC_CTRL_1, 3, 1);
++
++ lgdt3305_write_reg(state, LGDT3305_AGC_POWER_REF_1,
++ 0xff & (usref >> 8));
++ lgdt3305_write_reg(state, LGDT3305_AGC_POWER_REF_2,
++ 0xff & (usref >> 0));
++ }
++ return 0;
++}
++
++/* ------------------------------------------------------------------------ */
++
++static int lgdt3305_spectral_inversion(struct lgdt3305_state *state,
++ struct dvb_frontend_parameters *param,
++ int inversion)
++{
++ int ret;
++
++ lg_dbg("(%d)\n", inversion);
++
++ switch (param->u.vsb.modulation) {
++ case VSB_8:
++ ret = lgdt3305_write_reg(state, LGDT3305_CR_CTRL_7,
++ inversion ? 0xf9 : 0x79);
++ break;
++ case QAM_64:
++ case QAM_256:
++ ret = lgdt3305_write_reg(state, LGDT3305_FEC_BLOCK_CTRL,
++ inversion ? 0xfd : 0xff);
++ break;
++ default:
++ ret = -EINVAL;
++ }
++ return ret;
++}
++
++static int lgdt3305_set_if(struct lgdt3305_state *state,
++ struct dvb_frontend_parameters *param)
++{
++ u16 if_freq_khz;
++ u8 nco1, nco2, nco3, nco4;
++ u64 nco;
++
++ switch (param->u.vsb.modulation) {
++ case VSB_8:
++ if_freq_khz = state->cfg->vsb_if_khz;
++ break;
++ case QAM_64:
++ case QAM_256:
++ if_freq_khz = state->cfg->qam_if_khz;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ nco = if_freq_khz / 10;
++
++#define LGDT3305_64BIT_DIVISION_ENABLED 0
++ /* FIXME: 64bit division disabled to avoid linking error:
++ * WARNING: "__udivdi3" [lgdt3305.ko] undefined!
++ */
++ switch (param->u.vsb.modulation) {
++ case VSB_8:
++#if LGDT3305_64BIT_DIVISION_ENABLED
++ nco <<= 24;
++ nco /= 625;
++#else
++ nco *= ((1 << 24) / 625);
++#endif
++ break;
++ case QAM_64:
++ case QAM_256:
++#if LGDT3305_64BIT_DIVISION_ENABLED
++ nco <<= 28;
++ nco /= 625;
++#else
++ nco *= ((1 << 28) / 625);
++#endif
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ nco1 = (nco >> 24) & 0x3f;
++ nco1 |= 0x40;
++ nco2 = (nco >> 16) & 0xff;
++ nco3 = (nco >> 8) & 0xff;
++ nco4 = nco & 0xff;
++
++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_1, nco1);
++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_2, nco2);
++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_3, nco3);
++ lgdt3305_write_reg(state, LGDT3305_CR_CTR_FREQ_4, nco4);
++
++ lg_dbg("%d KHz -> [%02x%02x%02x%02x]\n",
++ if_freq_khz, nco1, nco2, nco3, nco4);
++
++ return 0;
++}
++
++/* ------------------------------------------------------------------------ */
++
++static int lgdt3305_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
++{
++ struct lgdt3305_state *state = fe->demodulator_priv;
++
++ if (state->cfg->deny_i2c_rptr)
++ return 0;
++
++ lg_dbg("(%d)\n", enable);
++
++ return lgdt3305_set_reg_bit(state, LGDT3305_GEN_CTRL_2, 5,
++ enable ? 0 : 1);
++}
++
++static int lgdt3305_sleep(struct dvb_frontend *fe)
++{
++ struct lgdt3305_state *state = fe->demodulator_priv;
++ u8 gen_ctrl_3, gen_ctrl_4;
++
++ lg_dbg("\n");
++
++ gen_ctrl_3 = read_reg(state, LGDT3305_GEN_CTRL_3);
++ gen_ctrl_4 = read_reg(state, LGDT3305_GEN_CTRL_4);
++
++ /* hold in software reset while sleeping */
++ gen_ctrl_3 &= ~0x01;
++ /* tristate the IF-AGC pin */
++ gen_ctrl_3 |= 0x02;
++ /* tristate the RF-AGC pin */
++ gen_ctrl_3 |= 0x04;
++
++ /* disable vsb/qam module */
++ gen_ctrl_4 &= ~0x01;
++ /* disable adc module */
++ gen_ctrl_4 &= ~0x02;
++
++ lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_3, gen_ctrl_3);
++ lgdt3305_write_reg(state, LGDT3305_GEN_CTRL_4, gen_ctrl_4);
++
++ return 0;
++}
++
++static int lgdt3305_init(struct dvb_frontend *fe)
++{
++ struct lgdt3305_state *state = fe->demodulator_priv;
++ int ret;
++
++ static struct lgdt3305_reg lgdt3305_init_data[] = {
++ { .reg = LGDT3305_GEN_CTRL_1,
++ .val = 0x03, },
++ { .reg = LGDT3305_GEN_CTRL_2,
++ .val = 0xb0, },
++ { .reg = LGDT3305_GEN_CTRL_3,
++ .val = 0x01, },
++ { .reg = LGDT3305_GEN_CONTROL,
++ .val = 0x6f, },
++ { .reg = LGDT3305_GEN_CTRL_4,
++ .val = 0x03, },
++ { .reg = LGDT3305_DGTL_AGC_REF_1,
++ .val = 0x32, },
++ { .reg = LGDT3305_DGTL_AGC_REF_2,
++ .val = 0xc4, },
++ { .reg = LGDT3305_CR_CTR_FREQ_1,
++ .val = 0x00, },
++ { .reg = LGDT3305_CR_CTR_FREQ_2,
++ .val = 0x00, },
++ { .reg = LGDT3305_CR_CTR_FREQ_3,
++ .val = 0x00, },
++ { .reg = LGDT3305_CR_CTR_FREQ_4,
++ .val = 0x00, },
++ { .reg = LGDT3305_CR_CTRL_7,
++ .val = 0x79, },
++ { .reg = LGDT3305_AGC_POWER_REF_1,
++ .val = 0x32, },
++ { .reg = LGDT3305_AGC_POWER_REF_2,
++ .val = 0xc4, },
++ { .reg = LGDT3305_AGC_DELAY_PT_1,
++ .val = 0x0d, },
++ { .reg = LGDT3305_AGC_DELAY_PT_2,
++ .val = 0x30, },
++ { .reg = LGDT3305_RFAGC_LOOP_FLTR_BW_1,
++ .val = 0x80, },
++ { .reg = LGDT3305_RFAGC_LOOP_FLTR_BW_2,
++ .val = 0x00, },
++ { .reg = LGDT3305_IFBW_1,
++ .val = 0x80, },
++ { .reg = LGDT3305_IFBW_2,
++ .val = 0x00, },
++ { .reg = LGDT3305_AGC_CTRL_1,
++ .val = 0x30, },
++ { .reg = LGDT3305_AGC_CTRL_4,
++ .val = 0x61, },
++ { .reg = LGDT3305_FEC_BLOCK_CTRL,
++ .val = 0xff, },
++ { .reg = LGDT3305_TP_CTRL_1,
++ .val = 0x1b, },
++ };
++
++ lg_dbg("\n");
++
++ ret = lgdt3305_write_regs(state, lgdt3305_init_data,
++ ARRAY_SIZE(lgdt3305_init_data));
++ if (lg_fail(ret))
++ goto fail;
++
++ ret = lgdt3305_soft_reset(state);
++fail:
++ return ret;
++}
++
++static int lgdt3305_set_parameters(struct dvb_frontend *fe,
++ struct dvb_frontend_parameters *param)
++{
++ struct lgdt3305_state *state = fe->demodulator_priv;
++ int ret;
++
++ lg_dbg("(%d, %d)\n", param->frequency, param->u.vsb.modulation);
++
++ if (fe->ops.tuner_ops.set_params) {
++ ret = fe->ops.tuner_ops.set_params(fe, param);
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0);
++ if (lg_fail(ret))
++ goto fail;
++ state->current_frequency = param->frequency;
++ }
++
++ ret = lgdt3305_set_modulation(state, param);
++ if (lg_fail(ret))
++ goto fail;
++
++ ret = lgdt3305_passband_digital_agc(state, param);
++ if (lg_fail(ret))
++ goto fail;
++ ret = lgdt3305_set_agc_power_ref(state, param);
++ if (lg_fail(ret))
++ goto fail;
++ ret = lgdt3305_agc_setup(state, param);
++ if (lg_fail(ret))
++ goto fail;
++
++ /* low if */
++ ret = lgdt3305_write_reg(state, LGDT3305_GEN_CONTROL, 0x2f);
++ if (lg_fail(ret))
++ goto fail;
++ ret = lgdt3305_set_reg_bit(state, LGDT3305_CR_CTR_FREQ_1, 6, 1);
++ if (lg_fail(ret))
++ goto fail;
++
++ ret = lgdt3305_set_if(state, param);
++ if (lg_fail(ret))
++ goto fail;
++ ret = lgdt3305_spectral_inversion(state, param,
++ state->cfg->spectral_inversion
++ ? 1 : 0);
++ if (lg_fail(ret))
++ goto fail;
++
++ ret = lgdt3305_set_filter_extension(state, param);
++ if (lg_fail(ret))
++ goto fail;
++
++ state->current_modulation = param->u.vsb.modulation;
++
++ ret = lgdt3305_mpeg_mode(state, state->cfg->mpeg_mode);
++ if (lg_fail(ret))
++ goto fail;
++
++ /* lgdt3305_mpeg_mode_polarity calls lgdt3305_soft_reset */
++ ret = lgdt3305_mpeg_mode_polarity(state,
++ state->cfg->tpclk_edge,
++ state->cfg->tpvalid_polarity);
++fail:
++ return ret;
++}
++
++static int lgdt3305_get_frontend(struct dvb_frontend *fe,
++ struct dvb_frontend_parameters *param)
++{
++ struct lgdt3305_state *state = fe->demodulator_priv;
++
++ lg_dbg("\n");
++
++ param->u.vsb.modulation = state->current_modulation;
++ param->frequency = state->current_frequency;
++ return 0;
++}
++
++/* ------------------------------------------------------------------------ */
++
++static int lgdt3305_read_cr_lock_status(struct lgdt3305_state *state,
++ int *locked)
++{
++ u8 val;
++ int ret;
++ char *cr_lock_state = "";
++
++ *locked = 0;
++
++ ret = lgdt3305_read_reg(state, LGDT3305_CR_LOCK_STATUS, &val);
++ if (lg_fail(ret))
++ goto fail;
++
++ switch (state->current_modulation) {
++ case QAM_256:
++ case QAM_64:
++ if (val & (1 << 1))
++ *locked = 1;
++
++ switch (val & 0x07) {
++ case 0:
++ cr_lock_state = "QAM UNLOCK";
++ break;
++ case 4:
++ cr_lock_state = "QAM 1stLock";
++ break;
++ case 6:
++ cr_lock_state = "QAM 2ndLock";
++ break;
++ case 7:
++ cr_lock_state = "QAM FinalLock";
++ break;
++ default:
++ cr_lock_state = "CLOCKQAM-INVALID!";
++ break;
++ }
++ break;
++ case VSB_8:
++ if (val & (1 << 7)) {
++ *locked = 1;
++ cr_lock_state = "CLOCKVSB";
++ }
++ break;
++ default:
++ ret = -EINVAL;
++ }
++ lg_dbg("(%d) %s\n", *locked, cr_lock_state);
++fail:
++ return ret;
++}
++
++static int lgdt3305_read_fec_lock_status(struct lgdt3305_state *state,
++ int *locked)
++{
++ u8 val;
++ int ret, mpeg_lock, fec_lock, viterbi_lock;
++
++ *locked = 0;
++
++ switch (state->current_modulation) {
++ case QAM_256:
++ case QAM_64:
++ ret = lgdt3305_read_reg(state,
++ LGDT3305_FEC_LOCK_STATUS, &val);
++ if (lg_fail(ret))
++ goto fail;
++
++ mpeg_lock = (val & (1 << 0)) ? 1 : 0;
++ fec_lock = (val & (1 << 2)) ? 1 : 0;
++ viterbi_lock = (val & (1 << 3)) ? 1 : 0;
++
++ *locked = mpeg_lock && fec_lock && viterbi_lock;
++
++ lg_dbg("(%d) %s%s%s\n", *locked,
++ mpeg_lock ? "mpeg lock " : "",
++ fec_lock ? "fec lock " : "",
++ viterbi_lock ? "viterbi lock" : "");
++ break;
++ case VSB_8:
++ default:
++ ret = -EINVAL;
++ }
++fail:
++ return ret;
++}
++
++static int lgdt3305_read_status(struct dvb_frontend *fe, fe_status_t *status)
++{
++ struct lgdt3305_state *state = fe->demodulator_priv;
++ u8 val;
++ int ret, signal, inlock, nofecerr, snrgood,
++ cr_lock, fec_lock, sync_lock;
++
++ *status = 0;
++
++ ret = lgdt3305_read_reg(state, LGDT3305_GEN_STATUS, &val);
++ if (lg_fail(ret))
++ goto fail;
++
++ signal = (val & (1 << 4)) ? 1 : 0;
++ inlock = (val & (1 << 3)) ? 0 : 1;
++ sync_lock = (val & (1 << 2)) ? 1 : 0;
++ nofecerr = (val & (1 << 1)) ? 1 : 0;
++ snrgood = (val & (1 << 0)) ? 1 : 0;
++
++ lg_dbg("%s%s%s%s%s\n",
++ signal ? "SIGNALEXIST " : "",
++ inlock ? "INLOCK " : "",
++ sync_lock ? "SYNCLOCK " : "",
++ nofecerr ? "NOFECERR " : "",
++ snrgood ? "SNRGOOD " : "");
++
++ ret = lgdt3305_read_cr_lock_status(state, &cr_lock);
++ if (lg_fail(ret))
++ goto fail;
++
++ if (signal)
++ *status |= FE_HAS_SIGNAL;
++ if (cr_lock)
++ *status |= FE_HAS_CARRIER;
++ if (nofecerr)
++ *status |= FE_HAS_VITERBI;
++ if (sync_lock)
++ *status |= FE_HAS_SYNC;
++
++ switch (state->current_modulation) {
++ case QAM_256:
++ case QAM_64:
++ ret = lgdt3305_read_fec_lock_status(state, &fec_lock);
++ if (lg_fail(ret))
++ goto fail;
++
++ if (fec_lock)
++ *status |= FE_HAS_LOCK;
++ break;
++ case VSB_8:
++ if (inlock)
++ *status |= FE_HAS_LOCK;
++ break;
++ default:
++ ret = -EINVAL;
++ }
++fail:
++ return ret;
++}
++
++/* ------------------------------------------------------------------------ */
++
++/* borrowed from lgdt330x.c */
++static u32 calculate_snr(u32 mse, u32 c)
++{
++ if (mse == 0) /* no signal */
++ return 0;
++
++ mse = intlog10(mse);
++ if (mse > c) {
++ /* Negative SNR, which is possible, but realisticly the
++ demod will lose lock before the signal gets this bad. The
++ API only allows for unsigned values, so just return 0 */
++ return 0;
++ }
++ return 10*(c - mse);
++}
++
++static int lgdt3305_read_snr(struct dvb_frontend *fe, u16 *snr)
++{
++ struct lgdt3305_state *state = fe->demodulator_priv;
++ u32 noise; /* noise value */
++ u32 c; /* per-modulation SNR calculation constant */
++
++ switch (state->current_modulation) {
++ case VSB_8:
++#ifdef USE_PTMSE
++ /* Use Phase Tracker Mean-Square Error Register */
++ /* SNR for ranges from -13.11 to +44.08 */
++ noise = ((read_reg(state, LGDT3305_PT_MSE_1) & 0x07) << 16) |
++ (read_reg(state, LGDT3305_PT_MSE_2) << 8) |
++ (read_reg(state, LGDT3305_PT_MSE_3) & 0xff);
++ c = 73957994; /* log10(25*32^2)*2^24 */
++#else
++ /* Use Equalizer Mean-Square Error Register */
++ /* SNR for ranges from -16.12 to +44.08 */
++ noise = ((read_reg(state, LGDT3305_EQ_MSE_1) & 0x0f) << 16) |
++ (read_reg(state, LGDT3305_EQ_MSE_2) << 8) |
++ (read_reg(state, LGDT3305_EQ_MSE_3) & 0xff);
++ c = 73957994; /* log10(25*32^2)*2^24 */
++#endif
++ break;
++ case QAM_64:
++ case QAM_256:
++ noise = (read_reg(state, LGDT3305_CR_MSE_1) << 8) |
++ (read_reg(state, LGDT3305_CR_MSE_2) & 0xff);
++
++ c = (state->current_modulation == QAM_64) ?
++ 97939837 : 98026066;
++ /* log10(688128)*2^24 and log10(696320)*2^24 */
++ break;
++ default:
++ return -EINVAL;
++ }
++ state->snr = calculate_snr(noise, c);
++ /* report SNR in dB * 10 */
++ *snr = (state->snr / ((1 << 24) / 10));
++ lg_dbg("noise = 0x%08x, snr = %d.%02d dB\n", noise,
++ state->snr >> 24, (((state->snr >> 8) & 0xffff) * 100) >> 16);
++
++ return 0;
++}
++
++static int lgdt3305_read_signal_strength(struct dvb_frontend *fe,
++ u16 *strength)
++{
++ /* borrowed from lgdt330x.c
++ *
++ * Calculate strength from SNR up to 35dB
++ * Even though the SNR can go higher than 35dB,
++ * there is some comfort factor in having a range of
++ * strong signals that can show at 100%
++ */
++ struct lgdt3305_state *state = fe->demodulator_priv;
++ u16 snr;
++ int ret;
++
++ *strength = 0;
++
++ ret = fe->ops.read_snr(fe, &snr);
++ if (lg_fail(ret))
++ goto fail;
++ /* Rather than use the 8.8 value snr, use state->snr which is 8.24 */
++ /* scale the range 0 - 35*2^24 into 0 - 65535 */
++ if (state->snr >= 8960 * 0x10000)
++ *strength = 0xffff;
++ else
++ *strength = state->snr / 8960;
++fail:
++ return ret;
++}
++
++/* ------------------------------------------------------------------------ */
++
++static int lgdt3305_read_ber(struct dvb_frontend *fe, u32 *ber)
++{
++ *ber = 0;
++ return 0;
++}
++
++static int lgdt3305_read_ucblocks(struct dvb_frontend *fe, u32 *ucblocks)
++{
++ struct lgdt3305_state *state = fe->demodulator_priv;
++
++ *ucblocks =
++ (read_reg(state, LGDT3305_FEC_PKT_ERR_1) << 8) |
++ (read_reg(state, LGDT3305_FEC_PKT_ERR_2) & 0xff);
++
++ return 0;
++}
++
++static int lgdt3305_get_tune_settings(struct dvb_frontend *fe,
++ struct dvb_frontend_tune_settings
++ *fe_tune_settings)
++{
++ fe_tune_settings->min_delay_ms = 500;
++ lg_dbg("\n");
++ return 0;
++}
++
++static void lgdt3305_release(struct dvb_frontend *fe)
++{
++ struct lgdt3305_state *state = fe->demodulator_priv;
++ lg_dbg("\n");
++ kfree(state);
++}
++
++static struct dvb_frontend_ops lgdt3305_ops;
++
++struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config,
++ struct i2c_adapter *i2c_adap)
++{
++ struct lgdt3305_state *state = NULL;
++ int ret;
++ u8 val;
++
++ lg_dbg("(%d-%04x)\n",
++ i2c_adap ? i2c_adapter_id(i2c_adap) : 0,
++ config ? config->i2c_addr : 0);
++
++ state = kzalloc(sizeof(struct lgdt3305_state), GFP_KERNEL);
++ if (state == NULL)
++ goto fail;
++
++ state->cfg = config;
++ state->i2c_adap = i2c_adap;
++
++ memcpy(&state->frontend.ops, &lgdt3305_ops,
++ sizeof(struct dvb_frontend_ops));
++ state->frontend.demodulator_priv = state;
++
++ /* verify that we're talking to a lg dt3305 */
++ ret = lgdt3305_read_reg(state, LGDT3305_GEN_CTRL_2, &val);
++ if ((lg_fail(ret)) | (val == 0))
++ goto fail;
++ ret = lgdt3305_write_reg(state, 0x0808, 0x80);
++ if (lg_fail(ret))
++ goto fail;
++ ret = lgdt3305_read_reg(state, 0x0808, &val);
++ if ((lg_fail(ret)) | (val != 0x80))
++ goto fail;
++ ret = lgdt3305_write_reg(state, 0x0808, 0x00);
++ if (lg_fail(ret))
++ goto fail;
++
++ state->current_frequency = -1;
++ state->current_modulation = -1;
++
++ return &state->frontend;
++fail:
++ lg_warn("unable to detect LGDT3305 hardware\n");
++ kfree(state);
++ return NULL;
++}
++EXPORT_SYMBOL(lgdt3305_attach);
++
++static struct dvb_frontend_ops lgdt3305_ops = {
++ .info = {
++ .name = "LG Electronics LGDT3305 VSB/QAM Frontend",
++ .type = FE_ATSC,
++ .frequency_min = 54000000,
++ .frequency_max = 858000000,
++ .frequency_stepsize = 62500,
++ .caps = FE_CAN_QAM_64 | FE_CAN_QAM_256 | FE_CAN_8VSB
++ },
++ .i2c_gate_ctrl = lgdt3305_i2c_gate_ctrl,
++ .init = lgdt3305_init,
++ .sleep = lgdt3305_sleep,
++ .set_frontend = lgdt3305_set_parameters,
++ .get_frontend = lgdt3305_get_frontend,
++ .get_tune_settings = lgdt3305_get_tune_settings,
++ .read_status = lgdt3305_read_status,
++ .read_ber = lgdt3305_read_ber,
++ .read_signal_strength = lgdt3305_read_signal_strength,
++ .read_snr = lgdt3305_read_snr,
++ .read_ucblocks = lgdt3305_read_ucblocks,
++ .release = lgdt3305_release,
++};
++
++MODULE_DESCRIPTION("LG Electronics LGDT3305 ATSC/QAM-B Demodulator Driver");
++MODULE_AUTHOR("Michael Krufky <mkrufky@linuxtv.org>");
++MODULE_LICENSE("GPL");
++MODULE_VERSION("0.1");
++
++/*
++ * Local variables:
++ * c-basic-offset: 8
++ * End:
++ */
+diff --git a/drivers/media/dvb/frontends/lgdt3305.h b/drivers/media/dvb/frontends/lgdt3305.h
+new file mode 100644
+index 0000000..4fa6e52
+--- /dev/null
++++ b/drivers/media/dvb/frontends/lgdt3305.h
+@@ -0,0 +1,85 @@
++/*
++ * Support for LGDT3305 - VSB/QAM
++ *
++ * Copyright (C) 2008, 2009 Michael Krufky <mkrufky@linuxtv.org>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ *
++ */
++
++#ifndef _LGDT3305_H_
++#define _LGDT3305_H_
++
++#include <linux/i2c.h>
++#include "dvb_frontend.h"
++
++
++enum lgdt3305_mpeg_mode {
++ LGDT3305_MPEG_PARALLEL = 0,
++ LGDT3305_MPEG_SERIAL = 1,
++};
++
++enum lgdt3305_tp_clock_edge {
++ LGDT3305_TPCLK_RISING_EDGE = 0,
++ LGDT3305_TPCLK_FALLING_EDGE = 1,
++};
++
++enum lgdt3305_tp_valid_polarity {
++ LGDT3305_TP_VALID_LOW = 0,
++ LGDT3305_TP_VALID_HIGH = 1,
++};
++
++struct lgdt3305_config {
++ u8 i2c_addr;
++
++ /* user defined IF frequency in KHz */
++ u16 qam_if_khz;
++ u16 vsb_if_khz;
++
++ /* AGC Power reference - defaults are used if left unset */
++ u16 usref_8vsb; /* default: 0x32c4 */
++ u16 usref_qam64; /* default: 0x5400 */
++ u16 usref_qam256; /* default: 0x2a80 */
++
++ /* disable i2c repeater - 0:repeater enabled 1:repeater disabled */
++ int deny_i2c_rptr:1;
++
++ /* spectral inversion - 0:disabled 1:enabled */
++ int spectral_inversion:1;
++
++ /* use RF AGC loop - 0:disabled 1:enabled */
++ int rf_agc_loop:1;
++
++ enum lgdt3305_mpeg_mode mpeg_mode;
++ enum lgdt3305_tp_clock_edge tpclk_edge;
++ enum lgdt3305_tp_valid_polarity tpvalid_polarity;
++};
++
++#if defined(CONFIG_DVB_LGDT3305) || (defined(CONFIG_DVB_LGDT3305_MODULE) && \
++ defined(MODULE))
++extern
++struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config,
++ struct i2c_adapter *i2c_adap);
++#else
++static inline
++struct dvb_frontend *lgdt3305_attach(const struct lgdt3305_config *config,
++ struct i2c_adapter *i2c_adap)
++{
++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
++ return NULL;
++}
++#endif /* CONFIG_DVB_LGDT3305 */
++
++#endif /* _LGDT3305_H_ */
+diff --git a/drivers/media/dvb/frontends/lnbh24.h b/drivers/media/dvb/frontends/lnbh24.h
+new file mode 100644
+index 0000000..c059b16
+--- /dev/null
++++ b/drivers/media/dvb/frontends/lnbh24.h
+@@ -0,0 +1,55 @@
++/*
++ * lnbh24.h - driver for lnb supply and control ic lnbh24
++ *
++ * Copyright (C) 2009 NetUP Inc.
++ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
++ *
++ * 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 _LNBH24_H
++#define _LNBH24_H
++
++/* system register bits */
++#define LNBH24_OLF 0x01
++#define LNBH24_OTF 0x02
++#define LNBH24_EN 0x04
++#define LNBH24_VSEL 0x08
++#define LNBH24_LLC 0x10
++#define LNBH24_TEN 0x20
++#define LNBH24_TTX 0x40
++#define LNBH24_PCL 0x80
++
++#include <linux/dvb/frontend.h>
++
++#if defined(CONFIG_DVB_LNBP21) || (defined(CONFIG_DVB_LNBP21_MODULE) \
++ && defined(MODULE))
++/* override_set and override_clear control which
++ system register bits (above) to always set & clear */
++extern struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe,
++ struct i2c_adapter *i2c, u8 override_set,
++ u8 override_clear, u8 i2c_addr);
++#else
++static inline struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe,
++ struct i2c_adapter *i2c, u8 override_set,
++ u8 override_clear, u8 i2c_addr)
++{
++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
++ return NULL;
++}
++#endif
++
++#endif
+diff --git a/drivers/media/dvb/frontends/lnbp21.c b/drivers/media/dvb/frontends/lnbp21.c
+index 76f935d..1dcc56f 100644
+--- a/drivers/media/dvb/frontends/lnbp21.c
++++ b/drivers/media/dvb/frontends/lnbp21.c
+@@ -1,7 +1,8 @@
+ /*
+- * lnbp21.h - driver for lnb supply and control ic lnbp21
++ * lnbp21.c - driver for lnb supply and control ic lnbp21
+ *
+ * Copyright (C) 2006 Oliver Endriss
++ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+@@ -33,18 +34,21 @@
+
+ #include "dvb_frontend.h"
+ #include "lnbp21.h"
++#include "lnbh24.h"
+
+ struct lnbp21 {
+ u8 config;
+ u8 override_or;
+ u8 override_and;
+ struct i2c_adapter *i2c;
++ u8 i2c_addr;
+ };
+
+-static int lnbp21_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
++static int lnbp21_set_voltage(struct dvb_frontend *fe,
++ fe_sec_voltage_t voltage)
+ {
+ struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv;
+- struct i2c_msg msg = { .addr = 0x08, .flags = 0,
++ struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0,
+ .buf = &lnbp21->config,
+ .len = sizeof(lnbp21->config) };
+
+@@ -72,7 +76,7 @@ static int lnbp21_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+ static int lnbp21_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
+ {
+ struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv;
+- struct i2c_msg msg = { .addr = 0x08, .flags = 0,
++ struct i2c_msg msg = { .addr = lnbp21->i2c_addr, .flags = 0,
+ .buf = &lnbp21->config,
+ .len = sizeof(lnbp21->config) };
+
+@@ -97,15 +101,18 @@ static void lnbp21_release(struct dvb_frontend *fe)
+ fe->sec_priv = NULL;
+ }
+
+-struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear)
++static struct dvb_frontend *lnbx2x_attach(struct dvb_frontend *fe,
++ struct i2c_adapter *i2c, u8 override_set,
++ u8 override_clear, u8 i2c_addr, u8 config)
+ {
+ struct lnbp21 *lnbp21 = kmalloc(sizeof(struct lnbp21), GFP_KERNEL);
+ if (!lnbp21)
+ return NULL;
+
+ /* default configuration */
+- lnbp21->config = LNBP21_ISEL;
++ lnbp21->config = config;
+ lnbp21->i2c = i2c;
++ lnbp21->i2c_addr = i2c_addr;
+ fe->sec_priv = lnbp21;
+
+ /* bits which should be forced to '1' */
+@@ -126,11 +133,29 @@ struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *
+ /* override frontend ops */
+ fe->ops.set_voltage = lnbp21_set_voltage;
+ fe->ops.enable_high_lnb_voltage = lnbp21_enable_high_lnb_voltage;
++ printk(KERN_INFO "LNBx2x attached on addr=%x", lnbp21->i2c_addr);
+
+ return fe;
+ }
++
++struct dvb_frontend *lnbh24_attach(struct dvb_frontend *fe,
++ struct i2c_adapter *i2c, u8 override_set,
++ u8 override_clear, u8 i2c_addr)
++{
++ return lnbx2x_attach(fe, i2c, override_set, override_clear,
++ i2c_addr, LNBH24_TTX);
++}
++EXPORT_SYMBOL(lnbh24_attach);
++
++struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe,
++ struct i2c_adapter *i2c, u8 override_set,
++ u8 override_clear)
++{
++ return lnbx2x_attach(fe, i2c, override_set, override_clear,
++ 0x08, LNBP21_ISEL);
++}
+ EXPORT_SYMBOL(lnbp21_attach);
+
+-MODULE_DESCRIPTION("Driver for lnb supply and control ic lnbp21");
+-MODULE_AUTHOR("Oliver Endriss");
++MODULE_DESCRIPTION("Driver for lnb supply and control ic lnbp21, lnbh24");
++MODULE_AUTHOR("Oliver Endriss, Igor M. Liplianin");
+ MODULE_LICENSE("GPL");
+diff --git a/drivers/media/dvb/frontends/lnbp21.h b/drivers/media/dvb/frontends/lnbp21.h
+index 8fe094b..fcdf1c6 100644
+--- a/drivers/media/dvb/frontends/lnbp21.h
++++ b/drivers/media/dvb/frontends/lnbp21.h
+@@ -28,26 +28,48 @@
+ #define _LNBP21_H
+
+ /* system register bits */
++/* [RO] 0=OK; 1=over current limit flag */
+ #define LNBP21_OLF 0x01
++/* [RO] 0=OK; 1=over temperature flag (150 C) */
+ #define LNBP21_OTF 0x02
++/* [RW] 0=disable LNB power, enable loopthrough
++ 1=enable LNB power, disable loopthrough */
+ #define LNBP21_EN 0x04
++/* [RW] 0=low voltage (13/14V, vert pol)
++ 1=high voltage (18/19V,horiz pol) */
+ #define LNBP21_VSEL 0x08
++/* [RW] increase LNB voltage by 1V:
++ 0=13/18V; 1=14/19V */
+ #define LNBP21_LLC 0x10
++/* [RW] 0=tone controlled by DSQIN pin
++ 1=tone enable, disable DSQIN */
+ #define LNBP21_TEN 0x20
++/* [RW] current limit select:
++ 0:Iout=500-650mA Isc=300mA
++ 1:Iout=400-550mA Isc=200mA */
+ #define LNBP21_ISEL 0x40
++/* [RW] short-circuit protect:
++ 0=pulsed (dynamic) curr limiting
++ 1=static curr limiting */
+ #define LNBP21_PCL 0x80
+
+ #include <linux/dvb/frontend.h>
+
+-#if defined(CONFIG_DVB_LNBP21) || (defined(CONFIG_DVB_LNBP21_MODULE) && defined(MODULE))
+-/* override_set and override_clear control which system register bits (above) to always set & clear */
+-extern struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear);
++#if defined(CONFIG_DVB_LNBP21) || (defined(CONFIG_DVB_LNBP21_MODULE) \
++ && defined(MODULE))
++/* override_set and override_clear control which
++ system register bits (above) to always set & clear */
++extern struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe,
++ struct i2c_adapter *i2c, u8 override_set,
++ u8 override_clear);
+ #else
+-static inline struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear)
++static inline struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe,
++ struct i2c_adapter *i2c, u8 override_set,
++ u8 override_clear)
+ {
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
+ return NULL;
+ }
+-#endif // CONFIG_DVB_LNBP21
++#endif
+
+-#endif // _LNBP21_H
++#endif
+diff --git a/drivers/media/dvb/frontends/s921_module.c b/drivers/media/dvb/frontends/s921_module.c
+index 892af8c..3f5a0e1 100644
+--- a/drivers/media/dvb/frontends/s921_module.c
++++ b/drivers/media/dvb/frontends/s921_module.c
+@@ -169,7 +169,6 @@ struct dvb_frontend* s921_attach(const struct s921_config *config,
+
+ struct s921_state *state;
+ state = kzalloc(sizeof(struct s921_state), GFP_KERNEL);
+- memset(state, 0x0, sizeof(struct s921_state));
+
+ state->addr = config->i2c_address;
+ state->i2c = i2c;
+diff --git a/drivers/media/dvb/frontends/stb6100_cfg.h b/drivers/media/dvb/frontends/stb6100_cfg.h
+index d313340..6314d18 100644
+--- a/drivers/media/dvb/frontends/stb6100_cfg.h
++++ b/drivers/media/dvb/frontends/stb6100_cfg.h
+@@ -36,7 +36,6 @@ static int stb6100_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+ return err;
+ }
+ *frequency = t_state.frequency;
+- printk("%s: Frequency=%d\n", __func__, t_state.frequency);
+ }
+ return 0;
+ }
+@@ -59,7 +58,6 @@ static int stb6100_set_frequency(struct dvb_frontend *fe, u32 frequency)
+ return err;
+ }
+ }
+- printk("%s: Frequency=%d\n", __func__, t_state.frequency);
+ return 0;
+ }
+
+@@ -81,7 +79,6 @@ static int stb6100_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+ }
+ *bandwidth = t_state.bandwidth;
+ }
+- printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth);
+ return 0;
+ }
+
+@@ -103,6 +100,5 @@ static int stb6100_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth)
+ return err;
+ }
+ }
+- printk("%s: Bandwidth=%d\n", __func__, t_state.bandwidth);
+ return 0;
+ }
+diff --git a/drivers/media/dvb/frontends/stv0900.h b/drivers/media/dvb/frontends/stv0900.h
+new file mode 100644
+index 0000000..8a1332c
+--- /dev/null
++++ b/drivers/media/dvb/frontends/stv0900.h
+@@ -0,0 +1,62 @@
++/*
++ * stv0900.h
++ *
++ * Driver for ST STV0900 satellite demodulator IC.
++ *
++ * Copyright (C) ST Microelectronics.
++ * Copyright (C) 2009 NetUP Inc.
++ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
++ *
++ * 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 STV0900_H
++#define STV0900_H
++
++#include <linux/dvb/frontend.h>
++#include "dvb_frontend.h"
++
++struct stv0900_config {
++ u8 demod_address;
++ u32 xtal;
++ u8 clkmode;/* 0 for CLKI, 2 for XTALI */
++
++ u8 diseqc_mode;
++
++ u8 path1_mode;
++ u8 path2_mode;
++
++ u8 tun1_maddress;/* 0, 1, 2, 3 for 0xc0, 0xc2, 0xc4, 0xc6 */
++ u8 tun2_maddress;
++ u8 tun1_adc;/* 1 for stv6110, 2 for stb6100 */
++ u8 tun2_adc;
++};
++
++#if defined(CONFIG_DVB_STV0900) || (defined(CONFIG_DVB_STV0900_MODULE) \
++ && defined(MODULE))
++extern struct dvb_frontend *stv0900_attach(const struct stv0900_config *config,
++ struct i2c_adapter *i2c, int demod);
++#else
++static inline struct dvb_frontend *stv0900_attach(const struct stv0900_config *config,
++ struct i2c_adapter *i2c, int demod)
++{
++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
++ return NULL;
++}
++#endif
++
++#endif
++
+diff --git a/drivers/media/dvb/frontends/stv0900_core.c b/drivers/media/dvb/frontends/stv0900_core.c
+new file mode 100644
+index 0000000..8499bcf
+--- /dev/null
++++ b/drivers/media/dvb/frontends/stv0900_core.c
+@@ -0,0 +1,1949 @@
++/*
++ * stv0900_core.c
++ *
++ * Driver for ST STV0900 satellite demodulator IC.
++ *
++ * Copyright (C) ST Microelectronics.
++ * Copyright (C) 2009 NetUP Inc.
++ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
++ *
++ * 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 <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/string.h>
++#include <linux/slab.h>
++#include <linux/i2c.h>
++
++#include "stv0900.h"
++#include "stv0900_reg.h"
++#include "stv0900_priv.h"
++#include "stv0900_init.h"
++
++static int stvdebug = 1;
++module_param_named(debug, stvdebug, int, 0644);
++
++/* internal params node */
++struct stv0900_inode {
++ /* pointer for internal params, one for each pair of demods */
++ struct stv0900_internal *internal;
++ struct stv0900_inode *next_inode;
++};
++
++/* first internal params */
++static struct stv0900_inode *stv0900_first_inode;
++
++/* find chip by i2c adapter and i2c address */
++static struct stv0900_inode *find_inode(struct i2c_adapter *i2c_adap,
++ u8 i2c_addr)
++{
++ struct stv0900_inode *temp_chip = stv0900_first_inode;
++
++ if (temp_chip != NULL) {
++ /*
++ Search of the last stv0900 chip or
++ find it by i2c adapter and i2c address */
++ while ((temp_chip != NULL) &&
++ ((temp_chip->internal->i2c_adap != i2c_adap) ||
++ (temp_chip->internal->i2c_addr != i2c_addr)))
++
++ temp_chip = temp_chip->next_inode;
++
++ }
++
++ return temp_chip;
++}
++
++/* deallocating chip */
++static void remove_inode(struct stv0900_internal *internal)
++{
++ struct stv0900_inode *prev_node = stv0900_first_inode;
++ struct stv0900_inode *del_node = find_inode(internal->i2c_adap,
++ internal->i2c_addr);
++
++ if (del_node != NULL) {
++ if (del_node == stv0900_first_inode) {
++ stv0900_first_inode = del_node->next_inode;
++ } else {
++ while (prev_node->next_inode != del_node)
++ prev_node = prev_node->next_inode;
++
++ if (del_node->next_inode == NULL)
++ prev_node->next_inode = NULL;
++ else
++ prev_node->next_inode =
++ prev_node->next_inode->next_inode;
++ }
++
++ kfree(del_node);
++ }
++}
++
++/* allocating new chip */
++static struct stv0900_inode *append_internal(struct stv0900_internal *internal)
++{
++ struct stv0900_inode *new_node = stv0900_first_inode;
++
++ if (new_node == NULL) {
++ new_node = kmalloc(sizeof(struct stv0900_inode), GFP_KERNEL);
++ stv0900_first_inode = new_node;
++ } else {
++ while (new_node->next_inode != NULL)
++ new_node = new_node->next_inode;
++
++ new_node->next_inode = kmalloc(sizeof(struct stv0900_inode), GFP_KERNEL);
++ if (new_node->next_inode != NULL)
++ new_node = new_node->next_inode;
++ else
++ new_node = NULL;
++ }
++
++ if (new_node != NULL) {
++ new_node->internal = internal;
++ new_node->next_inode = NULL;
++ }
++
++ return new_node;
++}
++
++s32 ge2comp(s32 a, s32 width)
++{
++ if (width == 32)
++ return a;
++ else
++ return (a >= (1 << (width - 1))) ? (a - (1 << width)) : a;
++}
++
++void stv0900_write_reg(struct stv0900_internal *i_params, u16 reg_addr,
++ u8 reg_data)
++{
++ u8 data[3];
++ int ret;
++ struct i2c_msg i2cmsg = {
++ .addr = i_params->i2c_addr,
++ .flags = 0,
++ .len = 3,
++ .buf = data,
++ };
++
++ data[0] = MSB(reg_addr);
++ data[1] = LSB(reg_addr);
++ data[2] = reg_data;
++
++ ret = i2c_transfer(i_params->i2c_adap, &i2cmsg, 1);
++ if (ret != 1)
++ dprintk(KERN_ERR "%s: i2c error %d\n", __func__, ret);
++}
++
++u8 stv0900_read_reg(struct stv0900_internal *i_params, u16 reg_addr)
++{
++ u8 data[2];
++ int ret;
++ struct i2c_msg i2cmsg = {
++ .addr = i_params->i2c_addr,
++ .flags = 0,
++ .len = 2,
++ .buf = data,
++ };
++
++ data[0] = MSB(reg_addr);
++ data[1] = LSB(reg_addr);
++
++ ret = i2c_transfer(i_params->i2c_adap, &i2cmsg, 1);
++ if (ret != 1)
++ dprintk(KERN_ERR "%s: i2c error %d\n", __func__, ret);
++
++ i2cmsg.flags = I2C_M_RD;
++ i2cmsg.len = 1;
++ ret = i2c_transfer(i_params->i2c_adap, &i2cmsg, 1);
++ if (ret != 1)
++ dprintk(KERN_ERR "%s: i2c error %d\n", __func__, ret);
++
++ return data[0];
++}
++
++void extract_mask_pos(u32 label, u8 *mask, u8 *pos)
++{
++ u8 position = 0, i = 0;
++
++ (*mask) = label & 0xff;
++
++ while ((position == 0) && (i < 8)) {
++ position = ((*mask) >> i) & 0x01;
++ i++;
++ }
++
++ (*pos) = (i - 1);
++}
++
++void stv0900_write_bits(struct stv0900_internal *i_params, u32 label, u8 val)
++{
++ u8 reg, mask, pos;
++
++ reg = stv0900_read_reg(i_params, (label >> 16) & 0xffff);
++ extract_mask_pos(label, &mask, &pos);
++
++ val = mask & (val << pos);
++
++ reg = (reg & (~mask)) | val;
++ stv0900_write_reg(i_params, (label >> 16) & 0xffff, reg);
++
++}
++
++u8 stv0900_get_bits(struct stv0900_internal *i_params, u32 label)
++{
++ u8 val = 0xff;
++ u8 mask, pos;
++
++ extract_mask_pos(label, &mask, &pos);
++
++ val = stv0900_read_reg(i_params, label >> 16);
++ val = (val & mask) >> pos;
++
++ return val;
++}
++
++enum fe_stv0900_error stv0900_initialize(struct stv0900_internal *i_params)
++{
++ s32 i;
++ enum fe_stv0900_error error;
++
++ if (i_params != NULL) {
++ i_params->chip_id = stv0900_read_reg(i_params, R0900_MID);
++ if (i_params->errs == STV0900_NO_ERROR) {
++ /*Startup sequence*/
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5c);
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5c);
++ stv0900_write_reg(i_params, R0900_P1_TNRCFG, 0x6c);
++ stv0900_write_reg(i_params, R0900_P2_TNRCFG, 0x6f);
++ stv0900_write_reg(i_params, R0900_P1_I2CRPT, 0x24);
++ stv0900_write_reg(i_params, R0900_P2_I2CRPT, 0x24);
++ stv0900_write_reg(i_params, R0900_NCOARSE, 0x13);
++ msleep(3);
++ stv0900_write_reg(i_params, R0900_I2CCFG, 0x08);
++
++ switch (i_params->clkmode) {
++ case 0:
++ case 2:
++ stv0900_write_reg(i_params, R0900_SYNTCTRL, 0x20
++ | i_params->clkmode);
++ break;
++ default:
++ /* preserve SELOSCI bit */
++ i = 0x02 & stv0900_read_reg(i_params, R0900_SYNTCTRL);
++ stv0900_write_reg(i_params, R0900_SYNTCTRL, 0x20 | i);
++ break;
++ }
++
++ msleep(3);
++ for (i = 0; i < 182; i++)
++ stv0900_write_reg(i_params, STV0900_InitVal[i][0], STV0900_InitVal[i][1]);
++
++ if (stv0900_read_reg(i_params, R0900_MID) >= 0x20) {
++ stv0900_write_reg(i_params, R0900_TSGENERAL, 0x0c);
++ for (i = 0; i < 32; i++)
++ stv0900_write_reg(i_params, STV0900_Cut20_AddOnVal[i][0], STV0900_Cut20_AddOnVal[i][1]);
++ }
++
++ stv0900_write_reg(i_params, R0900_P1_FSPYCFG, 0x6c);
++ stv0900_write_reg(i_params, R0900_P2_FSPYCFG, 0x6c);
++ stv0900_write_reg(i_params, R0900_TSTRES0, 0x80);
++ stv0900_write_reg(i_params, R0900_TSTRES0, 0x00);
++ }
++ error = i_params->errs;
++ } else
++ error = STV0900_INVALID_HANDLE;
++
++ return error;
++
++}
++
++u32 stv0900_get_mclk_freq(struct stv0900_internal *i_params, u32 ext_clk)
++{
++ u32 mclk = 90000000, div = 0, ad_div = 0;
++
++ div = stv0900_get_bits(i_params, F0900_M_DIV);
++ ad_div = ((stv0900_get_bits(i_params, F0900_SELX1RATIO) == 1) ? 4 : 6);
++
++ mclk = (div + 1) * ext_clk / ad_div;
++
++ dprintk(KERN_INFO "%s: Calculated Mclk = %d\n", __func__, mclk);
++
++ return mclk;
++}
++
++enum fe_stv0900_error stv0900_set_mclk(struct stv0900_internal *i_params, u32 mclk)
++{
++ enum fe_stv0900_error error = STV0900_NO_ERROR;
++ u32 m_div, clk_sel;
++
++ dprintk(KERN_INFO "%s: Mclk set to %d, Quartz = %d\n", __func__, mclk,
++ i_params->quartz);
++
++ if (i_params == NULL)
++ error = STV0900_INVALID_HANDLE;
++ else {
++ if (i_params->errs)
++ error = STV0900_I2C_ERROR;
++ else {
++ clk_sel = ((stv0900_get_bits(i_params, F0900_SELX1RATIO) == 1) ? 4 : 6);
++ m_div = ((clk_sel * mclk) / i_params->quartz) - 1;
++ stv0900_write_bits(i_params, F0900_M_DIV, m_div);
++ i_params->mclk = stv0900_get_mclk_freq(i_params,
++ i_params->quartz);
++
++ /*Set the DiseqC frequency to 22KHz */
++ /*
++ Formula:
++ DiseqC_TX_Freq= MasterClock/(32*F22TX_Reg)
++ DiseqC_RX_Freq= MasterClock/(32*F22RX_Reg)
++ */
++ m_div = i_params->mclk / 704000;
++ stv0900_write_reg(i_params, R0900_P1_F22TX, m_div);
++ stv0900_write_reg(i_params, R0900_P1_F22RX, m_div);
++
++ stv0900_write_reg(i_params, R0900_P2_F22TX, m_div);
++ stv0900_write_reg(i_params, R0900_P2_F22RX, m_div);
++
++ if ((i_params->errs))
++ error = STV0900_I2C_ERROR;
++ }
++ }
++
++ return error;
++}
++
++u32 stv0900_get_err_count(struct stv0900_internal *i_params, int cntr,
++ enum fe_stv0900_demod_num demod)
++{
++ u32 lsb, msb, hsb, err_val;
++ s32 err1field_hsb, err1field_msb, err1field_lsb;
++ s32 err2field_hsb, err2field_msb, err2field_lsb;
++
++ dmd_reg(err1field_hsb, F0900_P1_ERR_CNT12, F0900_P2_ERR_CNT12);
++ dmd_reg(err1field_msb, F0900_P1_ERR_CNT11, F0900_P2_ERR_CNT11);
++ dmd_reg(err1field_lsb, F0900_P1_ERR_CNT10, F0900_P2_ERR_CNT10);
++
++ dmd_reg(err2field_hsb, F0900_P1_ERR_CNT22, F0900_P2_ERR_CNT22);
++ dmd_reg(err2field_msb, F0900_P1_ERR_CNT21, F0900_P2_ERR_CNT21);
++ dmd_reg(err2field_lsb, F0900_P1_ERR_CNT20, F0900_P2_ERR_CNT20);
++
++ switch (cntr) {
++ case 0:
++ default:
++ hsb = stv0900_get_bits(i_params, err1field_hsb);
++ msb = stv0900_get_bits(i_params, err1field_msb);
++ lsb = stv0900_get_bits(i_params, err1field_lsb);
++ break;
++ case 1:
++ hsb = stv0900_get_bits(i_params, err2field_hsb);
++ msb = stv0900_get_bits(i_params, err2field_msb);
++ lsb = stv0900_get_bits(i_params, err2field_lsb);
++ break;
++ }
++
++ err_val = (hsb << 16) + (msb << 8) + (lsb);
++
++ return err_val;
++}
++
++static int stv0900_i2c_gate_ctrl(struct dvb_frontend *fe, int enable)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *i_params = state->internal;
++ enum fe_stv0900_demod_num demod = state->demod;
++
++ u32 fi2c;
++
++ dmd_reg(fi2c, F0900_P1_I2CT_ON, F0900_P2_I2CT_ON);
++ if (enable)
++ stv0900_write_bits(i_params, fi2c, 1);
++
++ return 0;
++}
++
++static void stv0900_set_ts_parallel_serial(struct stv0900_internal *i_params,
++ enum fe_stv0900_clock_type path1_ts,
++ enum fe_stv0900_clock_type path2_ts)
++{
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ if (i_params->chip_id >= 0x20) {
++ switch (path1_ts) {
++ case STV0900_PARALLEL_PUNCT_CLOCK:
++ case STV0900_DVBCI_CLOCK:
++ switch (path2_ts) {
++ case STV0900_SERIAL_PUNCT_CLOCK:
++ case STV0900_SERIAL_CONT_CLOCK:
++ default:
++ stv0900_write_reg(i_params, R0900_TSGENERAL,
++ 0x00);
++ break;
++ case STV0900_PARALLEL_PUNCT_CLOCK:
++ case STV0900_DVBCI_CLOCK:
++ stv0900_write_reg(i_params, R0900_TSGENERAL,
++ 0x06);
++ stv0900_write_bits(i_params,
++ F0900_P1_TSFIFO_MANSPEED, 3);
++ stv0900_write_bits(i_params,
++ F0900_P2_TSFIFO_MANSPEED, 0);
++ stv0900_write_reg(i_params,
++ R0900_P1_TSSPEED, 0x14);
++ stv0900_write_reg(i_params,
++ R0900_P2_TSSPEED, 0x28);
++ break;
++ }
++ break;
++ case STV0900_SERIAL_PUNCT_CLOCK:
++ case STV0900_SERIAL_CONT_CLOCK:
++ default:
++ switch (path2_ts) {
++ case STV0900_SERIAL_PUNCT_CLOCK:
++ case STV0900_SERIAL_CONT_CLOCK:
++ default:
++ stv0900_write_reg(i_params,
++ R0900_TSGENERAL, 0x0C);
++ break;
++ case STV0900_PARALLEL_PUNCT_CLOCK:
++ case STV0900_DVBCI_CLOCK:
++ stv0900_write_reg(i_params,
++ R0900_TSGENERAL, 0x0A);
++ dprintk(KERN_INFO "%s: 0x0a\n", __func__);
++ break;
++ }
++ break;
++ }
++ } else {
++ switch (path1_ts) {
++ case STV0900_PARALLEL_PUNCT_CLOCK:
++ case STV0900_DVBCI_CLOCK:
++ switch (path2_ts) {
++ case STV0900_SERIAL_PUNCT_CLOCK:
++ case STV0900_SERIAL_CONT_CLOCK:
++ default:
++ stv0900_write_reg(i_params, R0900_TSGENERAL1X,
++ 0x10);
++ break;
++ case STV0900_PARALLEL_PUNCT_CLOCK:
++ case STV0900_DVBCI_CLOCK:
++ stv0900_write_reg(i_params, R0900_TSGENERAL1X,
++ 0x16);
++ stv0900_write_bits(i_params,
++ F0900_P1_TSFIFO_MANSPEED, 3);
++ stv0900_write_bits(i_params,
++ F0900_P2_TSFIFO_MANSPEED, 0);
++ stv0900_write_reg(i_params, R0900_P1_TSSPEED,
++ 0x14);
++ stv0900_write_reg(i_params, R0900_P2_TSSPEED,
++ 0x28);
++ break;
++ }
++
++ break;
++ case STV0900_SERIAL_PUNCT_CLOCK:
++ case STV0900_SERIAL_CONT_CLOCK:
++ default:
++ switch (path2_ts) {
++ case STV0900_SERIAL_PUNCT_CLOCK:
++ case STV0900_SERIAL_CONT_CLOCK:
++ default:
++ stv0900_write_reg(i_params, R0900_TSGENERAL1X,
++ 0x14);
++ break;
++ case STV0900_PARALLEL_PUNCT_CLOCK:
++ case STV0900_DVBCI_CLOCK:
++ stv0900_write_reg(i_params, R0900_TSGENERAL1X,
++ 0x12);
++ dprintk(KERN_INFO "%s: 0x12\n", __func__);
++ break;
++ }
++
++ break;
++ }
++ }
++
++ switch (path1_ts) {
++ case STV0900_PARALLEL_PUNCT_CLOCK:
++ stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x00);
++ stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x00);
++ break;
++ case STV0900_DVBCI_CLOCK:
++ stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x00);
++ stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x01);
++ break;
++ case STV0900_SERIAL_PUNCT_CLOCK:
++ stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x01);
++ stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x00);
++ break;
++ case STV0900_SERIAL_CONT_CLOCK:
++ stv0900_write_bits(i_params, F0900_P1_TSFIFO_SERIAL, 0x01);
++ stv0900_write_bits(i_params, F0900_P1_TSFIFO_DVBCI, 0x01);
++ break;
++ default:
++ break;
++ }
++
++ switch (path2_ts) {
++ case STV0900_PARALLEL_PUNCT_CLOCK:
++ stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x00);
++ stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x00);
++ break;
++ case STV0900_DVBCI_CLOCK:
++ stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x00);
++ stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x01);
++ break;
++ case STV0900_SERIAL_PUNCT_CLOCK:
++ stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x01);
++ stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x00);
++ break;
++ case STV0900_SERIAL_CONT_CLOCK:
++ stv0900_write_bits(i_params, F0900_P2_TSFIFO_SERIAL, 0x01);
++ stv0900_write_bits(i_params, F0900_P2_TSFIFO_DVBCI, 0x01);
++ break;
++ default:
++ break;
++ }
++
++ stv0900_write_bits(i_params, F0900_P2_RST_HWARE, 1);
++ stv0900_write_bits(i_params, F0900_P2_RST_HWARE, 0);
++ stv0900_write_bits(i_params, F0900_P1_RST_HWARE, 1);
++ stv0900_write_bits(i_params, F0900_P1_RST_HWARE, 0);
++}
++
++void stv0900_set_tuner(struct dvb_frontend *fe, u32 frequency,
++ u32 bandwidth)
++{
++ struct dvb_frontend_ops *frontend_ops = NULL;
++ struct dvb_tuner_ops *tuner_ops = NULL;
++
++ if (&fe->ops)
++ frontend_ops = &fe->ops;
++
++ if (&frontend_ops->tuner_ops)
++ tuner_ops = &frontend_ops->tuner_ops;
++
++ if (tuner_ops->set_frequency) {
++ if ((tuner_ops->set_frequency(fe, frequency)) < 0)
++ dprintk("%s: Invalid parameter\n", __func__);
++ else
++ dprintk("%s: Frequency=%d\n", __func__, frequency);
++
++ }
++
++ if (tuner_ops->set_bandwidth) {
++ if ((tuner_ops->set_bandwidth(fe, bandwidth)) < 0)
++ dprintk("%s: Invalid parameter\n", __func__);
++ else
++ dprintk("%s: Bandwidth=%d\n", __func__, bandwidth);
++
++ }
++}
++
++void stv0900_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth)
++{
++ struct dvb_frontend_ops *frontend_ops = NULL;
++ struct dvb_tuner_ops *tuner_ops = NULL;
++
++ if (&fe->ops)
++ frontend_ops = &fe->ops;
++
++ if (&frontend_ops->tuner_ops)
++ tuner_ops = &frontend_ops->tuner_ops;
++
++ if (tuner_ops->set_bandwidth) {
++ if ((tuner_ops->set_bandwidth(fe, bandwidth)) < 0)
++ dprintk("%s: Invalid parameter\n", __func__);
++ else
++ dprintk("%s: Bandwidth=%d\n", __func__, bandwidth);
++
++ }
++}
++
++static s32 stv0900_get_rf_level(struct stv0900_internal *i_params,
++ const struct stv0900_table *lookup,
++ enum fe_stv0900_demod_num demod)
++{
++ s32 agc_gain = 0,
++ imin,
++ imax,
++ i,
++ rf_lvl = 0;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ if ((lookup != NULL) && lookup->size) {
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ agc_gain = MAKEWORD(stv0900_get_bits(i_params, F0900_P1_AGCIQ_VALUE1),
++ stv0900_get_bits(i_params, F0900_P1_AGCIQ_VALUE0));
++ break;
++ case STV0900_DEMOD_2:
++ agc_gain = MAKEWORD(stv0900_get_bits(i_params, F0900_P2_AGCIQ_VALUE1),
++ stv0900_get_bits(i_params, F0900_P2_AGCIQ_VALUE0));
++ break;
++ }
++
++ imin = 0;
++ imax = lookup->size - 1;
++ if (INRANGE(lookup->table[imin].regval, agc_gain, lookup->table[imax].regval)) {
++ while ((imax - imin) > 1) {
++ i = (imax + imin) >> 1;
++
++ if (INRANGE(lookup->table[imin].regval, agc_gain, lookup->table[i].regval))
++ imax = i;
++ else
++ imin = i;
++ }
++
++ rf_lvl = (((s32)agc_gain - lookup->table[imin].regval)
++ * (lookup->table[imax].realval - lookup->table[imin].realval)
++ / (lookup->table[imax].regval - lookup->table[imin].regval))
++ + lookup->table[imin].realval;
++ } else if (agc_gain > lookup->table[0].regval)
++ rf_lvl = 5;
++ else if (agc_gain < lookup->table[lookup->size-1].regval)
++ rf_lvl = -100;
++
++ }
++
++ dprintk(KERN_INFO "%s: RFLevel = %d\n", __func__, rf_lvl);
++
++ return rf_lvl;
++}
++
++static int stv0900_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *internal = state->internal;
++ s32 rflevel = stv0900_get_rf_level(internal, &stv0900_rf,
++ state->demod);
++
++ *strength = (rflevel + 100) * (16383 / 105);
++
++ return 0;
++}
++
++
++static s32 stv0900_carr_get_quality(struct dvb_frontend *fe,
++ const struct stv0900_table *lookup)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *i_params = state->internal;
++ enum fe_stv0900_demod_num demod = state->demod;
++
++ s32 c_n = -100,
++ regval, imin, imax,
++ i,
++ lock_flag_field,
++ noise_field1,
++ noise_field0;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ dmd_reg(lock_flag_field, F0900_P1_LOCK_DEFINITIF,
++ F0900_P2_LOCK_DEFINITIF);
++ if (stv0900_get_standard(fe, demod) == STV0900_DVBS2_STANDARD) {
++ dmd_reg(noise_field1, F0900_P1_NOSPLHT_NORMED1,
++ F0900_P2_NOSPLHT_NORMED1);
++ dmd_reg(noise_field0, F0900_P1_NOSPLHT_NORMED0,
++ F0900_P2_NOSPLHT_NORMED0);
++ } else {
++ dmd_reg(noise_field1, F0900_P1_NOSDATAT_NORMED1,
++ F0900_P2_NOSDATAT_NORMED1);
++ dmd_reg(noise_field0, F0900_P1_NOSDATAT_NORMED0,
++ F0900_P2_NOSDATAT_NORMED0);
++ }
++
++ if (stv0900_get_bits(i_params, lock_flag_field)) {
++ if ((lookup != NULL) && lookup->size) {
++ regval = 0;
++ msleep(5);
++ for (i = 0; i < 16; i++) {
++ regval += MAKEWORD(stv0900_get_bits(i_params,
++ noise_field1),
++ stv0900_get_bits(i_params,
++ noise_field0));
++ msleep(1);
++ }
++
++ regval /= 16;
++ imin = 0;
++ imax = lookup->size - 1;
++ if (INRANGE(lookup->table[imin].regval,
++ regval,
++ lookup->table[imax].regval)) {
++ while ((imax - imin) > 1) {
++ i = (imax + imin) >> 1;
++ if (INRANGE(lookup->table[imin].regval,
++ regval,
++ lookup->table[i].regval))
++ imax = i;
++ else
++ imin = i;
++ }
++
++ c_n = ((regval - lookup->table[imin].regval)
++ * (lookup->table[imax].realval
++ - lookup->table[imin].realval)
++ / (lookup->table[imax].regval
++ - lookup->table[imin].regval))
++ + lookup->table[imin].realval;
++ } else if (regval < lookup->table[imin].regval)
++ c_n = 1000;
++ }
++ }
++
++ return c_n;
++}
++
++static int stv0900_read_snr(struct dvb_frontend *fe, u16 *snr)
++{
++ *snr = stv0900_carr_get_quality(fe,
++ (const struct stv0900_table *)&stv0900_s2_cn);
++ *snr += 30;
++ *snr *= (16383 / 1030);
++
++ return 0;
++}
++
++static u32 stv0900_get_ber(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod)
++{
++ u32 ber = 10000000, i;
++ s32 dmd_state_reg;
++ s32 demod_state;
++ s32 vstatus_reg;
++ s32 prvit_field;
++ s32 pdel_status_reg;
++ s32 pdel_lock_field;
++
++ dmd_reg(dmd_state_reg, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE);
++ dmd_reg(vstatus_reg, R0900_P1_VSTATUSVIT, R0900_P2_VSTATUSVIT);
++ dmd_reg(prvit_field, F0900_P1_PRFVIT, F0900_P2_PRFVIT);
++ dmd_reg(pdel_status_reg, R0900_P1_PDELSTATUS1, R0900_P2_PDELSTATUS1);
++ dmd_reg(pdel_lock_field, F0900_P1_PKTDELIN_LOCK,
++ F0900_P2_PKTDELIN_LOCK);
++
++ demod_state = stv0900_get_bits(i_params, dmd_state_reg);
++
++ switch (demod_state) {
++ case STV0900_SEARCH:
++ case STV0900_PLH_DETECTED:
++ default:
++ ber = 10000000;
++ break;
++ case STV0900_DVBS_FOUND:
++ ber = 0;
++ for (i = 0; i < 5; i++) {
++ msleep(5);
++ ber += stv0900_get_err_count(i_params, 0, demod);
++ }
++
++ ber /= 5;
++ if (stv0900_get_bits(i_params, prvit_field)) {
++ ber *= 9766;
++ ber = ber >> 13;
++ }
++
++ break;
++ case STV0900_DVBS2_FOUND:
++ ber = 0;
++ for (i = 0; i < 5; i++) {
++ msleep(5);
++ ber += stv0900_get_err_count(i_params, 0, demod);
++ }
++
++ ber /= 5;
++ if (stv0900_get_bits(i_params, pdel_lock_field)) {
++ ber *= 9766;
++ ber = ber >> 13;
++ }
++
++ break;
++ }
++
++ return ber;
++}
++
++static int stv0900_read_ber(struct dvb_frontend *fe, u32 *ber)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *internal = state->internal;
++
++ *ber = stv0900_get_ber(internal, state->demod);
++
++ return 0;
++}
++
++int stv0900_get_demod_lock(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod, s32 time_out)
++{
++ s32 timer = 0,
++ lock = 0,
++ header_field,
++ lock_field;
++
++ enum fe_stv0900_search_state dmd_state;
++
++ dmd_reg(header_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE);
++ dmd_reg(lock_field, F0900_P1_LOCK_DEFINITIF, F0900_P2_LOCK_DEFINITIF);
++ while ((timer < time_out) && (lock == 0)) {
++ dmd_state = stv0900_get_bits(i_params, header_field);
++ dprintk("Demod State = %d\n", dmd_state);
++ switch (dmd_state) {
++ case STV0900_SEARCH:
++ case STV0900_PLH_DETECTED:
++ default:
++ lock = 0;
++ break;
++ case STV0900_DVBS2_FOUND:
++ case STV0900_DVBS_FOUND:
++ lock = stv0900_get_bits(i_params, lock_field);
++ break;
++ }
++
++ if (lock == 0)
++ msleep(10);
++
++ timer += 10;
++ }
++
++ if (lock)
++ dprintk("DEMOD LOCK OK\n");
++ else
++ dprintk("DEMOD LOCK FAIL\n");
++
++ return lock;
++}
++
++void stv0900_stop_all_s2_modcod(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod)
++{
++ s32 regflist,
++ i;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ dmd_reg(regflist, R0900_P1_MODCODLST0, R0900_P2_MODCODLST0);
++
++ for (i = 0; i < 16; i++)
++ stv0900_write_reg(i_params, regflist + i, 0xff);
++}
++
++void stv0900_activate_s2_modcode(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod)
++{
++ u32 matype,
++ mod_code,
++ fmod,
++ reg_index,
++ field_index;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ if (i_params->chip_id <= 0x11) {
++ msleep(5);
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ mod_code = stv0900_read_reg(i_params,
++ R0900_P1_PLHMODCOD);
++ matype = mod_code & 0x3;
++ mod_code = (mod_code & 0x7f) >> 2;
++
++ reg_index = R0900_P1_MODCODLSTF - mod_code / 2;
++ field_index = mod_code % 2;
++ break;
++ case STV0900_DEMOD_2:
++ mod_code = stv0900_read_reg(i_params,
++ R0900_P2_PLHMODCOD);
++ matype = mod_code & 0x3;
++ mod_code = (mod_code & 0x7f) >> 2;
++
++ reg_index = R0900_P2_MODCODLSTF - mod_code / 2;
++ field_index = mod_code % 2;
++ break;
++ }
++
++
++ switch (matype) {
++ case 0:
++ default:
++ fmod = 14;
++ break;
++ case 1:
++ fmod = 13;
++ break;
++ case 2:
++ fmod = 11;
++ break;
++ case 3:
++ fmod = 7;
++ break;
++ }
++
++ if ((INRANGE(STV0900_QPSK_12, mod_code, STV0900_8PSK_910))
++ && (matype <= 1)) {
++ if (field_index == 0)
++ stv0900_write_reg(i_params, reg_index,
++ 0xf0 | fmod);
++ else
++ stv0900_write_reg(i_params, reg_index,
++ (fmod << 4) | 0xf);
++ }
++ } else if (i_params->chip_id >= 0x12) {
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ for (reg_index = 0; reg_index < 7; reg_index++)
++ stv0900_write_reg(i_params, R0900_P1_MODCODLST0 + reg_index, 0xff);
++
++ stv0900_write_reg(i_params, R0900_P1_MODCODLSTE, 0xff);
++ stv0900_write_reg(i_params, R0900_P1_MODCODLSTF, 0xcf);
++ for (reg_index = 0; reg_index < 8; reg_index++)
++ stv0900_write_reg(i_params, R0900_P1_MODCODLST7 + reg_index, 0xcc);
++
++ break;
++ case STV0900_DEMOD_2:
++ for (reg_index = 0; reg_index < 7; reg_index++)
++ stv0900_write_reg(i_params, R0900_P2_MODCODLST0 + reg_index, 0xff);
++
++ stv0900_write_reg(i_params, R0900_P2_MODCODLSTE, 0xff);
++ stv0900_write_reg(i_params, R0900_P2_MODCODLSTF, 0xcf);
++ for (reg_index = 0; reg_index < 8; reg_index++)
++ stv0900_write_reg(i_params, R0900_P2_MODCODLST7 + reg_index, 0xcc);
++
++ break;
++ }
++
++ }
++}
++
++void stv0900_activate_s2_modcode_single(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod)
++{
++ u32 reg_index;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ stv0900_write_reg(i_params, R0900_P1_MODCODLST0, 0xff);
++ stv0900_write_reg(i_params, R0900_P1_MODCODLST1, 0xf0);
++ stv0900_write_reg(i_params, R0900_P1_MODCODLSTF, 0x0f);
++ for (reg_index = 0; reg_index < 13; reg_index++)
++ stv0900_write_reg(i_params,
++ R0900_P1_MODCODLST2 + reg_index, 0);
++
++ break;
++ case STV0900_DEMOD_2:
++ stv0900_write_reg(i_params, R0900_P2_MODCODLST0, 0xff);
++ stv0900_write_reg(i_params, R0900_P2_MODCODLST1, 0xf0);
++ stv0900_write_reg(i_params, R0900_P2_MODCODLSTF, 0x0f);
++ for (reg_index = 0; reg_index < 13; reg_index++)
++ stv0900_write_reg(i_params,
++ R0900_P2_MODCODLST2 + reg_index, 0);
++
++ break;
++ }
++}
++
++static enum dvbfe_algo stv0900_frontend_algo(struct dvb_frontend *fe)
++{
++ return DVBFE_ALGO_CUSTOM;
++}
++
++static int stb0900_set_property(struct dvb_frontend *fe,
++ struct dtv_property *tvp)
++{
++ dprintk(KERN_INFO "%s(..)\n", __func__);
++
++ return 0;
++}
++
++static int stb0900_get_property(struct dvb_frontend *fe,
++ struct dtv_property *tvp)
++{
++ dprintk(KERN_INFO "%s(..)\n", __func__);
++
++ return 0;
++}
++
++void stv0900_start_search(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod)
++{
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ stv0900_write_bits(i_params, F0900_P1_I2C_DEMOD_MODE, 0x1f);
++
++ if (i_params->chip_id == 0x10)
++ stv0900_write_reg(i_params, R0900_P1_CORRELEXP, 0xaa);
++
++ if (i_params->chip_id < 0x20)
++ stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x55);
++
++ if (i_params->dmd1_symbol_rate <= 5000000) {
++ stv0900_write_reg(i_params, R0900_P1_CARCFG, 0x44);
++ stv0900_write_reg(i_params, R0900_P1_CFRUP1, 0x0f);
++ stv0900_write_reg(i_params, R0900_P1_CFRUP0, 0xff);
++ stv0900_write_reg(i_params, R0900_P1_CFRLOW1, 0xf0);
++ stv0900_write_reg(i_params, R0900_P1_CFRLOW0, 0x00);
++ stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x68);
++ } else {
++ stv0900_write_reg(i_params, R0900_P1_CARCFG, 0xc4);
++ stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x44);
++ }
++
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT1, 0);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT0, 0);
++
++ if (i_params->chip_id >= 0x20) {
++ stv0900_write_reg(i_params, R0900_P1_EQUALCFG, 0x41);
++ stv0900_write_reg(i_params, R0900_P1_FFECFG, 0x41);
++
++ if ((i_params->dmd1_srch_standard == STV0900_SEARCH_DVBS1) || (i_params->dmd1_srch_standard == STV0900_SEARCH_DSS) || (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH)) {
++ stv0900_write_reg(i_params, R0900_P1_VITSCALE, 0x82);
++ stv0900_write_reg(i_params, R0900_P1_VAVSRVIT, 0x0);
++ }
++ }
++
++ stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x00);
++ stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0xe0);
++ stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0xc0);
++ stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0);
++ stv0900_write_bits(i_params, F0900_P1_S1S2_SEQUENTIAL, 0);
++ stv0900_write_reg(i_params, R0900_P1_RTC, 0x88);
++ if (i_params->chip_id >= 0x20) {
++ if (i_params->dmd1_symbol_rate < 2000000) {
++ stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x39);
++ stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x40);
++ }
++
++ if (i_params->dmd1_symbol_rate < 10000000) {
++ stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x4c);
++ stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x20);
++ } else {
++ stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x4b);
++ stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x20);
++ }
++
++ } else {
++ if (i_params->dmd1_symbol_rate < 10000000)
++ stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xef);
++ else
++ stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xed);
++ }
++
++ switch (i_params->dmd1_srch_algo) {
++ case STV0900_WARM_START:
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1f);
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
++ break;
++ case STV0900_COLD_START:
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1f);
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15);
++ break;
++ default:
++ break;
++ }
++
++ break;
++ case STV0900_DEMOD_2:
++ stv0900_write_bits(i_params, F0900_P2_I2C_DEMOD_MODE, 0x1f);
++ if (i_params->chip_id == 0x10)
++ stv0900_write_reg(i_params, R0900_P2_CORRELEXP, 0xaa);
++
++ if (i_params->chip_id < 0x20)
++ stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x55);
++
++ if (i_params->dmd2_symbol_rate <= 5000000) {
++ stv0900_write_reg(i_params, R0900_P2_CARCFG, 0x44);
++ stv0900_write_reg(i_params, R0900_P2_CFRUP1, 0x0f);
++ stv0900_write_reg(i_params, R0900_P2_CFRUP0, 0xff);
++ stv0900_write_reg(i_params, R0900_P2_CFRLOW1, 0xf0);
++ stv0900_write_reg(i_params, R0900_P2_CFRLOW0, 0x00);
++ stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x68);
++ } else {
++ stv0900_write_reg(i_params, R0900_P2_CARCFG, 0xc4);
++ stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x44);
++ }
++
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT1, 0);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT0, 0);
++
++ if (i_params->chip_id >= 0x20) {
++ stv0900_write_reg(i_params, R0900_P2_EQUALCFG, 0x41);
++ stv0900_write_reg(i_params, R0900_P2_FFECFG, 0x41);
++ if ((i_params->dmd2_srch_stndrd == STV0900_SEARCH_DVBS1) || (i_params->dmd2_srch_stndrd == STV0900_SEARCH_DSS) || (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH)) {
++ stv0900_write_reg(i_params, R0900_P2_VITSCALE, 0x82);
++ stv0900_write_reg(i_params, R0900_P2_VAVSRVIT, 0x0);
++ }
++ }
++
++ stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x00);
++ stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0xe0);
++ stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0xc0);
++ stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0);
++ stv0900_write_bits(i_params, F0900_P2_S1S2_SEQUENTIAL, 0);
++ stv0900_write_reg(i_params, R0900_P2_RTC, 0x88);
++ if (i_params->chip_id >= 0x20) {
++ if (i_params->dmd2_symbol_rate < 2000000) {
++ stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x39);
++ stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x40);
++ }
++
++ if (i_params->dmd2_symbol_rate < 10000000) {
++ stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x4c);
++ stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x20);
++ } else {
++ stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x4b);
++ stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x20);
++ }
++
++ } else {
++ if (i_params->dmd2_symbol_rate < 10000000)
++ stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xef);
++ else
++ stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xed);
++ }
++
++ switch (i_params->dmd2_srch_algo) {
++ case STV0900_WARM_START:
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1f);
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
++ break;
++ case STV0900_COLD_START:
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1f);
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15);
++ break;
++ default:
++ break;
++ }
++
++ break;
++ }
++}
++
++u8 stv0900_get_optim_carr_loop(s32 srate, enum fe_stv0900_modcode modcode,
++ s32 pilot, u8 chip_id)
++{
++ u8 aclc_value = 0x29;
++ s32 i;
++ const struct stv0900_car_loop_optim *car_loop_s2;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ if (chip_id <= 0x12)
++ car_loop_s2 = FE_STV0900_S2CarLoop;
++ else if (chip_id == 0x20)
++ car_loop_s2 = FE_STV0900_S2CarLoopCut20;
++ else
++ car_loop_s2 = FE_STV0900_S2CarLoop;
++
++ if (modcode < STV0900_QPSK_12) {
++ i = 0;
++ while ((i < 3) && (modcode != FE_STV0900_S2LowQPCarLoopCut20[i].modcode))
++ i++;
++
++ if (i >= 3)
++ i = 2;
++ } else {
++ i = 0;
++ while ((i < 14) && (modcode != car_loop_s2[i].modcode))
++ i++;
++
++ if (i >= 14) {
++ i = 0;
++ while ((i < 11) && (modcode != FE_STV0900_S2APSKCarLoopCut20[i].modcode))
++ i++;
++
++ if (i >= 11)
++ i = 10;
++ }
++ }
++
++ if (modcode <= STV0900_QPSK_25) {
++ if (pilot) {
++ if (srate <= 3000000)
++ aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_2;
++ else if (srate <= 7000000)
++ aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_5;
++ else if (srate <= 15000000)
++ aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_10;
++ else if (srate <= 25000000)
++ aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_20;
++ else
++ aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_on_30;
++ } else {
++ if (srate <= 3000000)
++ aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_2;
++ else if (srate <= 7000000)
++ aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_5;
++ else if (srate <= 15000000)
++ aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_10;
++ else if (srate <= 25000000)
++ aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_20;
++ else
++ aclc_value = FE_STV0900_S2LowQPCarLoopCut20[i].car_loop_pilots_off_30;
++ }
++
++ } else if (modcode <= STV0900_8PSK_910) {
++ if (pilot) {
++ if (srate <= 3000000)
++ aclc_value = car_loop_s2[i].car_loop_pilots_on_2;
++ else if (srate <= 7000000)
++ aclc_value = car_loop_s2[i].car_loop_pilots_on_5;
++ else if (srate <= 15000000)
++ aclc_value = car_loop_s2[i].car_loop_pilots_on_10;
++ else if (srate <= 25000000)
++ aclc_value = car_loop_s2[i].car_loop_pilots_on_20;
++ else
++ aclc_value = car_loop_s2[i].car_loop_pilots_on_30;
++ } else {
++ if (srate <= 3000000)
++ aclc_value = car_loop_s2[i].car_loop_pilots_off_2;
++ else if (srate <= 7000000)
++ aclc_value = car_loop_s2[i].car_loop_pilots_off_5;
++ else if (srate <= 15000000)
++ aclc_value = car_loop_s2[i].car_loop_pilots_off_10;
++ else if (srate <= 25000000)
++ aclc_value = car_loop_s2[i].car_loop_pilots_off_20;
++ else
++ aclc_value = car_loop_s2[i].car_loop_pilots_off_30;
++ }
++
++ } else {
++ if (srate <= 3000000)
++ aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_2;
++ else if (srate <= 7000000)
++ aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_5;
++ else if (srate <= 15000000)
++ aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_10;
++ else if (srate <= 25000000)
++ aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_20;
++ else
++ aclc_value = FE_STV0900_S2APSKCarLoopCut20[i].car_loop_pilots_on_30;
++ }
++
++ return aclc_value;
++}
++
++u8 stv0900_get_optim_short_carr_loop(s32 srate, enum fe_stv0900_modulation modulation, u8 chip_id)
++{
++ s32 mod_index = 0;
++
++ u8 aclc_value = 0x0b;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ switch (modulation) {
++ case STV0900_QPSK:
++ default:
++ mod_index = 0;
++ break;
++ case STV0900_8PSK:
++ mod_index = 1;
++ break;
++ case STV0900_16APSK:
++ mod_index = 2;
++ break;
++ case STV0900_32APSK:
++ mod_index = 3;
++ break;
++ }
++
++ switch (chip_id) {
++ case 0x20:
++ if (srate <= 3000000)
++ aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_2;
++ else if (srate <= 7000000)
++ aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_5;
++ else if (srate <= 15000000)
++ aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_10;
++ else if (srate <= 25000000)
++ aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_20;
++ else
++ aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut20_30;
++
++ break;
++ case 0x12:
++ default:
++ if (srate <= 3000000)
++ aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_2;
++ else if (srate <= 7000000)
++ aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_5;
++ else if (srate <= 15000000)
++ aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_10;
++ else if (srate <= 25000000)
++ aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_20;
++ else
++ aclc_value = FE_STV0900_S2ShortCarLoop[mod_index].car_loop_cut12_30;
++
++ break;
++ }
++
++ return aclc_value;
++}
++
++static enum fe_stv0900_error stv0900_st_dvbs2_single(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_mode LDPC_Mode,
++ enum fe_stv0900_demod_num demod)
++{
++ enum fe_stv0900_error error = STV0900_NO_ERROR;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ switch (LDPC_Mode) {
++ case STV0900_DUAL:
++ default:
++ if ((i_params->demod_mode != STV0900_DUAL)
++ || (stv0900_get_bits(i_params, F0900_DDEMOD) != 1)) {
++ stv0900_write_reg(i_params, R0900_GENCFG, 0x1d);
++
++ i_params->demod_mode = STV0900_DUAL;
++
++ stv0900_write_bits(i_params, F0900_FRESFEC, 1);
++ stv0900_write_bits(i_params, F0900_FRESFEC, 0);
++ }
++
++ break;
++ case STV0900_SINGLE:
++ if (demod == STV0900_DEMOD_2)
++ stv0900_write_reg(i_params, R0900_GENCFG, 0x06);
++ else
++ stv0900_write_reg(i_params, R0900_GENCFG, 0x04);
++
++ i_params->demod_mode = STV0900_SINGLE;
++
++ stv0900_write_bits(i_params, F0900_FRESFEC, 1);
++ stv0900_write_bits(i_params, F0900_FRESFEC, 0);
++ stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 1);
++ stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 0);
++ stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 1);
++ stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 0);
++ break;
++ }
++
++ return error;
++}
++
++static enum fe_stv0900_error stv0900_init_internal(struct dvb_frontend *fe,
++ struct stv0900_init_params *p_init)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ enum fe_stv0900_error error = STV0900_NO_ERROR;
++ enum fe_stv0900_error demodError = STV0900_NO_ERROR;
++ int selosci;
++
++ struct stv0900_inode *temp_int = find_inode(state->i2c_adap,
++ state->config->demod_address);
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ if (temp_int != NULL) {
++ state->internal = temp_int->internal;
++ (state->internal->dmds_used)++;
++ dprintk(KERN_INFO "%s: Find Internal Structure!\n", __func__);
++ return STV0900_NO_ERROR;
++ } else {
++ state->internal = kmalloc(sizeof(struct stv0900_internal), GFP_KERNEL);
++ temp_int = append_internal(state->internal);
++ state->internal->dmds_used = 1;
++ state->internal->i2c_adap = state->i2c_adap;
++ state->internal->i2c_addr = state->config->demod_address;
++ state->internal->clkmode = state->config->clkmode;
++ state->internal->errs = STV0900_NO_ERROR;
++ dprintk(KERN_INFO "%s: Create New Internal Structure!\n", __func__);
++ }
++
++ if (state->internal != NULL) {
++ demodError = stv0900_initialize(state->internal);
++ if (demodError == STV0900_NO_ERROR) {
++ error = STV0900_NO_ERROR;
++ } else {
++ if (demodError == STV0900_INVALID_HANDLE)
++ error = STV0900_INVALID_HANDLE;
++ else
++ error = STV0900_I2C_ERROR;
++ }
++
++ if (state->internal != NULL) {
++ if (error == STV0900_NO_ERROR) {
++ state->internal->demod_mode = p_init->demod_mode;
++
++ stv0900_st_dvbs2_single(state->internal, state->internal->demod_mode, STV0900_DEMOD_1);
++
++ state->internal->chip_id = stv0900_read_reg(state->internal, R0900_MID);
++ state->internal->rolloff = p_init->rolloff;
++ state->internal->quartz = p_init->dmd_ref_clk;
++
++ stv0900_write_bits(state->internal, F0900_P1_ROLLOFF_CONTROL, p_init->rolloff);
++ stv0900_write_bits(state->internal, F0900_P2_ROLLOFF_CONTROL, p_init->rolloff);
++
++ stv0900_set_ts_parallel_serial(state->internal, p_init->path1_ts_clock, p_init->path2_ts_clock);
++ stv0900_write_bits(state->internal, F0900_P1_TUN_MADDRESS, p_init->tun1_maddress);
++ switch (p_init->tuner1_adc) {
++ case 1:
++ stv0900_write_reg(state->internal, R0900_TSTTNR1, 0x26);
++ break;
++ default:
++ break;
++ }
++
++ stv0900_write_bits(state->internal, F0900_P2_TUN_MADDRESS, p_init->tun2_maddress);
++ switch (p_init->tuner2_adc) {
++ case 1:
++ stv0900_write_reg(state->internal, R0900_TSTTNR3, 0x26);
++ break;
++ default:
++ break;
++ }
++
++ stv0900_write_bits(state->internal, F0900_P1_TUN_IQSWAP, p_init->tun1_iq_inversion);
++ stv0900_write_bits(state->internal, F0900_P2_TUN_IQSWAP, p_init->tun2_iq_inversion);
++ stv0900_set_mclk(state->internal, 135000000);
++ msleep(3);
++
++ switch (state->internal->clkmode) {
++ case 0:
++ case 2:
++ stv0900_write_reg(state->internal, R0900_SYNTCTRL, 0x20 | state->internal->clkmode);
++ break;
++ default:
++ selosci = 0x02 & stv0900_read_reg(state->internal, R0900_SYNTCTRL);
++ stv0900_write_reg(state->internal, R0900_SYNTCTRL, 0x20 | selosci);
++ break;
++ }
++ msleep(3);
++
++ state->internal->mclk = stv0900_get_mclk_freq(state->internal, state->internal->quartz);
++ if (state->internal->errs)
++ error = STV0900_I2C_ERROR;
++ }
++ } else {
++ error = STV0900_INVALID_HANDLE;
++ }
++ }
++
++ return error;
++}
++
++static int stv0900_status(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod)
++{
++ enum fe_stv0900_search_state demod_state;
++ s32 mode_field, delin_field, lock_field, fifo_field, lockedvit_field;
++ int locked = FALSE;
++
++ dmd_reg(mode_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE);
++ dmd_reg(lock_field, F0900_P1_LOCK_DEFINITIF, F0900_P2_LOCK_DEFINITIF);
++ dmd_reg(delin_field, F0900_P1_PKTDELIN_LOCK, F0900_P2_PKTDELIN_LOCK);
++ dmd_reg(fifo_field, F0900_P1_TSFIFO_LINEOK, F0900_P2_TSFIFO_LINEOK);
++ dmd_reg(lockedvit_field, F0900_P1_LOCKEDVIT, F0900_P2_LOCKEDVIT);
++
++ demod_state = stv0900_get_bits(i_params, mode_field);
++ switch (demod_state) {
++ case STV0900_SEARCH:
++ case STV0900_PLH_DETECTED:
++ default:
++ locked = FALSE;
++ break;
++ case STV0900_DVBS2_FOUND:
++ locked = stv0900_get_bits(i_params, lock_field) &&
++ stv0900_get_bits(i_params, delin_field) &&
++ stv0900_get_bits(i_params, fifo_field);
++ break;
++ case STV0900_DVBS_FOUND:
++ locked = stv0900_get_bits(i_params, lock_field) &&
++ stv0900_get_bits(i_params, lockedvit_field) &&
++ stv0900_get_bits(i_params, fifo_field);
++ break;
++ }
++
++ return locked;
++}
++
++static enum dvbfe_search stv0900_search(struct dvb_frontend *fe,
++ struct dvb_frontend_parameters *params)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *i_params = state->internal;
++ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
++
++ struct stv0900_search_params p_search;
++ struct stv0900_signal_info p_result;
++
++ enum fe_stv0900_error error = STV0900_NO_ERROR;
++
++ dprintk(KERN_INFO "%s: ", __func__);
++
++ p_result.locked = FALSE;
++ p_search.path = state->demod;
++ p_search.frequency = c->frequency;
++ p_search.symbol_rate = c->symbol_rate;
++ p_search.search_range = 10000000;
++ p_search.fec = STV0900_FEC_UNKNOWN;
++ p_search.standard = STV0900_AUTO_SEARCH;
++ p_search.iq_inversion = STV0900_IQ_AUTO;
++ p_search.search_algo = STV0900_BLIND_SEARCH;
++
++ if ((INRANGE(100000, p_search.symbol_rate, 70000000)) &&
++ (INRANGE(100000, p_search.search_range, 50000000))) {
++ switch (p_search.path) {
++ case STV0900_DEMOD_1:
++ default:
++ i_params->dmd1_srch_standard = p_search.standard;
++ i_params->dmd1_symbol_rate = p_search.symbol_rate;
++ i_params->dmd1_srch_range = p_search.search_range;
++ i_params->tuner1_freq = p_search.frequency;
++ i_params->dmd1_srch_algo = p_search.search_algo;
++ i_params->dmd1_srch_iq_inv = p_search.iq_inversion;
++ i_params->dmd1_fec = p_search.fec;
++ break;
++
++ case STV0900_DEMOD_2:
++ i_params->dmd2_srch_stndrd = p_search.standard;
++ i_params->dmd2_symbol_rate = p_search.symbol_rate;
++ i_params->dmd2_srch_range = p_search.search_range;
++ i_params->tuner2_freq = p_search.frequency;
++ i_params->dmd2_srch_algo = p_search.search_algo;
++ i_params->dmd2_srch_iq_inv = p_search.iq_inversion;
++ i_params->dmd2_fec = p_search.fec;
++ break;
++ }
++
++ if ((stv0900_algo(fe) == STV0900_RANGEOK) &&
++ (i_params->errs == STV0900_NO_ERROR)) {
++ switch (p_search.path) {
++ case STV0900_DEMOD_1:
++ default:
++ p_result.locked = i_params->dmd1_rslts.locked;
++ p_result.standard = i_params->dmd1_rslts.standard;
++ p_result.frequency = i_params->dmd1_rslts.frequency;
++ p_result.symbol_rate = i_params->dmd1_rslts.symbol_rate;
++ p_result.fec = i_params->dmd1_rslts.fec;
++ p_result.modcode = i_params->dmd1_rslts.modcode;
++ p_result.pilot = i_params->dmd1_rslts.pilot;
++ p_result.frame_length = i_params->dmd1_rslts.frame_length;
++ p_result.spectrum = i_params->dmd1_rslts.spectrum;
++ p_result.rolloff = i_params->dmd1_rslts.rolloff;
++ p_result.modulation = i_params->dmd1_rslts.modulation;
++ break;
++ case STV0900_DEMOD_2:
++ p_result.locked = i_params->dmd2_rslts.locked;
++ p_result.standard = i_params->dmd2_rslts.standard;
++ p_result.frequency = i_params->dmd2_rslts.frequency;
++ p_result.symbol_rate = i_params->dmd2_rslts.symbol_rate;
++ p_result.fec = i_params->dmd2_rslts.fec;
++ p_result.modcode = i_params->dmd2_rslts.modcode;
++ p_result.pilot = i_params->dmd2_rslts.pilot;
++ p_result.frame_length = i_params->dmd2_rslts.frame_length;
++ p_result.spectrum = i_params->dmd2_rslts.spectrum;
++ p_result.rolloff = i_params->dmd2_rslts.rolloff;
++ p_result.modulation = i_params->dmd2_rslts.modulation;
++ break;
++ }
++
++ } else {
++ p_result.locked = FALSE;
++ switch (p_search.path) {
++ case STV0900_DEMOD_1:
++ switch (i_params->dmd1_err) {
++ case STV0900_I2C_ERROR:
++ error = STV0900_I2C_ERROR;
++ break;
++ case STV0900_NO_ERROR:
++ default:
++ error = STV0900_SEARCH_FAILED;
++ break;
++ }
++ break;
++ case STV0900_DEMOD_2:
++ switch (i_params->dmd2_err) {
++ case STV0900_I2C_ERROR:
++ error = STV0900_I2C_ERROR;
++ break;
++ case STV0900_NO_ERROR:
++ default:
++ error = STV0900_SEARCH_FAILED;
++ break;
++ }
++ break;
++ }
++ }
++
++ } else
++ error = STV0900_BAD_PARAMETER;
++
++ if ((p_result.locked == TRUE) && (error == STV0900_NO_ERROR)) {
++ dprintk(KERN_INFO "Search Success\n");
++ return DVBFE_ALGO_SEARCH_SUCCESS;
++ } else {
++ dprintk(KERN_INFO "Search Fail\n");
++ return DVBFE_ALGO_SEARCH_FAILED;
++ }
++
++ return DVBFE_ALGO_SEARCH_ERROR;
++}
++
++static int stv0900_read_status(struct dvb_frontend *fe, enum fe_status *status)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++
++ dprintk("%s: ", __func__);
++
++ if ((stv0900_status(state->internal, state->demod)) == TRUE) {
++ dprintk("DEMOD LOCK OK\n");
++ *status = FE_HAS_CARRIER
++ | FE_HAS_VITERBI
++ | FE_HAS_SYNC
++ | FE_HAS_LOCK;
++ } else
++ dprintk("DEMOD LOCK FAIL\n");
++
++ return 0;
++}
++
++static int stv0900_track(struct dvb_frontend *fe,
++ struct dvb_frontend_parameters *p)
++{
++ return 0;
++}
++
++static int stv0900_stop_ts(struct dvb_frontend *fe, int stop_ts)
++{
++
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *i_params = state->internal;
++ enum fe_stv0900_demod_num demod = state->demod;
++ s32 rst_field;
++
++ dmd_reg(rst_field, F0900_P1_RST_HWARE, F0900_P2_RST_HWARE);
++
++ if (stop_ts == TRUE)
++ stv0900_write_bits(i_params, rst_field, 1);
++ else
++ stv0900_write_bits(i_params, rst_field, 0);
++
++ return 0;
++}
++
++static int stv0900_diseqc_init(struct dvb_frontend *fe)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *i_params = state->internal;
++ enum fe_stv0900_demod_num demod = state->demod;
++ s32 mode_field, reset_field;
++
++ dmd_reg(mode_field, F0900_P1_DISTX_MODE, F0900_P2_DISTX_MODE);
++ dmd_reg(reset_field, F0900_P1_DISEQC_RESET, F0900_P2_DISEQC_RESET);
++
++ stv0900_write_bits(i_params, mode_field, state->config->diseqc_mode);
++ stv0900_write_bits(i_params, reset_field, 1);
++ stv0900_write_bits(i_params, reset_field, 0);
++
++ return 0;
++}
++
++static int stv0900_init(struct dvb_frontend *fe)
++{
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ stv0900_stop_ts(fe, 1);
++ stv0900_diseqc_init(fe);
++
++ return 0;
++}
++
++static int stv0900_diseqc_send(struct stv0900_internal *i_params , u8 *Data,
++ u32 NbData, enum fe_stv0900_demod_num demod)
++{
++ s32 i = 0;
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ stv0900_write_bits(i_params, F0900_P1_DIS_PRECHARGE, 1);
++ while (i < NbData) {
++ while (stv0900_get_bits(i_params, F0900_P1_FIFO_FULL))
++ ;/* checkpatch complains */
++ stv0900_write_reg(i_params, R0900_P1_DISTXDATA, Data[i]);
++ i++;
++ }
++
++ stv0900_write_bits(i_params, F0900_P1_DIS_PRECHARGE, 0);
++ i = 0;
++ while ((stv0900_get_bits(i_params, F0900_P1_TX_IDLE) != 1) && (i < 10)) {
++ msleep(10);
++ i++;
++ }
++
++ break;
++ case STV0900_DEMOD_2:
++ stv0900_write_bits(i_params, F0900_P2_DIS_PRECHARGE, 1);
++
++ while (i < NbData) {
++ while (stv0900_get_bits(i_params, F0900_P2_FIFO_FULL))
++ ;/* checkpatch complains */
++ stv0900_write_reg(i_params, R0900_P2_DISTXDATA, Data[i]);
++ i++;
++ }
++
++ stv0900_write_bits(i_params, F0900_P2_DIS_PRECHARGE, 0);
++ i = 0;
++ while ((stv0900_get_bits(i_params, F0900_P2_TX_IDLE) != 1) && (i < 10)) {
++ msleep(10);
++ i++;
++ }
++
++ break;
++ }
++
++ return 0;
++}
++
++static int stv0900_send_master_cmd(struct dvb_frontend *fe,
++ struct dvb_diseqc_master_cmd *cmd)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++
++ return stv0900_diseqc_send(state->internal,
++ cmd->msg,
++ cmd->msg_len,
++ state->demod);
++}
++
++static int stv0900_send_burst(struct dvb_frontend *fe, fe_sec_mini_cmd_t burst)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *i_params = state->internal;
++ enum fe_stv0900_demod_num demod = state->demod;
++ s32 mode_field;
++ u32 diseqc_fifo;
++
++ dmd_reg(mode_field, F0900_P1_DISTX_MODE, F0900_P2_DISTX_MODE);
++ dmd_reg(diseqc_fifo, R0900_P1_DISTXDATA, R0900_P2_DISTXDATA);
++
++ switch (burst) {
++ case SEC_MINI_A:
++ stv0900_write_bits(i_params, mode_field, 3);/* Unmodulated */
++ stv0900_write_reg(i_params, diseqc_fifo, 0x00);
++ break;
++ case SEC_MINI_B:
++ stv0900_write_bits(i_params, mode_field, 2);/* Modulated */
++ stv0900_write_reg(i_params, diseqc_fifo, 0xff);
++ break;
++ }
++
++ return 0;
++}
++
++static int stv0900_recv_slave_reply(struct dvb_frontend *fe,
++ struct dvb_diseqc_slave_reply *reply)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *i_params = state->internal;
++ s32 i = 0;
++
++ switch (state->demod) {
++ case STV0900_DEMOD_1:
++ default:
++ reply->msg_len = 0;
++
++ while ((stv0900_get_bits(i_params, F0900_P1_RX_END) != 1) && (i < 10)) {
++ msleep(10);
++ i++;
++ }
++
++ if (stv0900_get_bits(i_params, F0900_P1_RX_END)) {
++ reply->msg_len = stv0900_get_bits(i_params, F0900_P1_FIFO_BYTENBR);
++
++ for (i = 0; i < reply->msg_len; i++)
++ reply->msg[i] = stv0900_read_reg(i_params, R0900_P1_DISRXDATA);
++ }
++ break;
++ case STV0900_DEMOD_2:
++ reply->msg_len = 0;
++
++ while ((stv0900_get_bits(i_params, F0900_P2_RX_END) != 1) && (i < 10)) {
++ msleep(10);
++ i++;
++ }
++
++ if (stv0900_get_bits(i_params, F0900_P2_RX_END)) {
++ reply->msg_len = stv0900_get_bits(i_params, F0900_P2_FIFO_BYTENBR);
++
++ for (i = 0; i < reply->msg_len; i++)
++ reply->msg[i] = stv0900_read_reg(i_params, R0900_P2_DISRXDATA);
++ }
++ break;
++ }
++
++ return 0;
++}
++
++static int stv0900_set_tone(struct dvb_frontend *fe, fe_sec_tone_mode_t tone)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *i_params = state->internal;
++ enum fe_stv0900_demod_num demod = state->demod;
++ s32 mode_field, reset_field;
++
++ dprintk(KERN_INFO "%s: %s\n", __func__, ((tone == 0) ? "Off" : "On"));
++
++ dmd_reg(mode_field, F0900_P1_DISTX_MODE, F0900_P2_DISTX_MODE);
++ dmd_reg(reset_field, F0900_P1_DISEQC_RESET, F0900_P2_DISEQC_RESET);
++
++ if (tone) {
++ /*Set the DiseqC mode to 22Khz continues tone*/
++ stv0900_write_bits(i_params, mode_field, 0);
++ stv0900_write_bits(i_params, reset_field, 1);
++ /*release DiseqC reset to enable the 22KHz tone*/
++ stv0900_write_bits(i_params, reset_field, 0);
++ } else {
++ stv0900_write_bits(i_params, mode_field, 0);
++ /*maintain the DiseqC reset to disable the 22KHz tone*/
++ stv0900_write_bits(i_params, reset_field, 1);
++ }
++
++ return 0;
++}
++
++static void stv0900_release(struct dvb_frontend *fe)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ if ((--(state->internal->dmds_used)) <= 0) {
++
++ dprintk(KERN_INFO "%s: Actually removing\n", __func__);
++
++ remove_inode(state->internal);
++ kfree(state->internal);
++ }
++
++ kfree(state);
++}
++
++static struct dvb_frontend_ops stv0900_ops = {
++
++ .info = {
++ .name = "STV0900 frontend",
++ .type = FE_QPSK,
++ .frequency_min = 950000,
++ .frequency_max = 2150000,
++ .frequency_stepsize = 125,
++ .frequency_tolerance = 0,
++ .symbol_rate_min = 1000000,
++ .symbol_rate_max = 45000000,
++ .symbol_rate_tolerance = 500,
++ .caps = FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 |
++ FE_CAN_FEC_3_4 | FE_CAN_FEC_5_6 |
++ FE_CAN_FEC_7_8 | FE_CAN_QPSK |
++ FE_CAN_2G_MODULATION |
++ FE_CAN_FEC_AUTO
++ },
++ .release = stv0900_release,
++ .init = stv0900_init,
++ .get_frontend_algo = stv0900_frontend_algo,
++ .i2c_gate_ctrl = stv0900_i2c_gate_ctrl,
++ .diseqc_send_master_cmd = stv0900_send_master_cmd,
++ .diseqc_send_burst = stv0900_send_burst,
++ .diseqc_recv_slave_reply = stv0900_recv_slave_reply,
++ .set_tone = stv0900_set_tone,
++ .set_property = stb0900_set_property,
++ .get_property = stb0900_get_property,
++ .search = stv0900_search,
++ .track = stv0900_track,
++ .read_status = stv0900_read_status,
++ .read_ber = stv0900_read_ber,
++ .read_signal_strength = stv0900_read_signal_strength,
++ .read_snr = stv0900_read_snr,
++};
++
++struct dvb_frontend *stv0900_attach(const struct stv0900_config *config,
++ struct i2c_adapter *i2c,
++ int demod)
++{
++ struct stv0900_state *state = NULL;
++ struct stv0900_init_params init_params;
++ enum fe_stv0900_error err_stv0900;
++
++ state = kzalloc(sizeof(struct stv0900_state), GFP_KERNEL);
++ if (state == NULL)
++ goto error;
++
++ state->demod = demod;
++ state->config = config;
++ state->i2c_adap = i2c;
++
++ memcpy(&state->frontend.ops, &stv0900_ops,
++ sizeof(struct dvb_frontend_ops));
++ state->frontend.demodulator_priv = state;
++
++ switch (demod) {
++ case 0:
++ case 1:
++ init_params.dmd_ref_clk = config->xtal;
++ init_params.demod_mode = STV0900_DUAL;
++ init_params.rolloff = STV0900_35;
++ init_params.path1_ts_clock = config->path1_mode;
++ init_params.tun1_maddress = config->tun1_maddress;
++ init_params.tun1_iq_inversion = STV0900_IQ_NORMAL;
++ init_params.tuner1_adc = config->tun1_adc;
++ init_params.path2_ts_clock = config->path2_mode;
++ init_params.tun2_maddress = config->tun2_maddress;
++ init_params.tuner2_adc = config->tun2_adc;
++ init_params.tun2_iq_inversion = STV0900_IQ_SWAPPED;
++
++ err_stv0900 = stv0900_init_internal(&state->frontend,
++ &init_params);
++
++ if (err_stv0900)
++ goto error;
++
++ break;
++ default:
++ goto error;
++ break;
++ }
++
++ dprintk("%s: Attaching STV0900 demodulator(%d) \n", __func__, demod);
++ return &state->frontend;
++
++error:
++ dprintk("%s: Failed to attach STV0900 demodulator(%d) \n",
++ __func__, demod);
++ kfree(state);
++ return NULL;
++}
++EXPORT_SYMBOL(stv0900_attach);
++
++MODULE_PARM_DESC(debug, "Set debug");
++
++MODULE_AUTHOR("Igor M. Liplianin");
++MODULE_DESCRIPTION("ST STV0900 frontend");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/media/dvb/frontends/stv0900_init.h b/drivers/media/dvb/frontends/stv0900_init.h
+new file mode 100644
+index 0000000..ff388b4
+--- /dev/null
++++ b/drivers/media/dvb/frontends/stv0900_init.h
+@@ -0,0 +1,441 @@
++/*
++ * stv0900_init.h
++ *
++ * Driver for ST STV0900 satellite demodulator IC.
++ *
++ * Copyright (C) ST Microelectronics.
++ * Copyright (C) 2009 NetUP Inc.
++ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
++ *
++ * 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 STV0900_INIT_H
++#define STV0900_INIT_H
++
++#include "stv0900_priv.h"
++
++/* DVBS2 C/N Look-Up table */
++static const struct stv0900_table stv0900_s2_cn = {
++ 55,
++ {
++ { -30, 13348 }, /*C/N=-3dB*/
++ { -20, 12640 }, /*C/N=-2dB*/
++ { -10, 11883 }, /*C/N=-1dB*/
++ { 0, 11101 }, /*C/N=-0dB*/
++ { 5, 10718 }, /*C/N=0.5dB*/
++ { 10, 10339 }, /*C/N=1.0dB*/
++ { 15, 9947 }, /*C/N=1.5dB*/
++ { 20, 9552 }, /*C/N=2.0dB*/
++ { 25, 9183 }, /*C/N=2.5dB*/
++ { 30, 8799 }, /*C/N=3.0dB*/
++ { 35, 8422 }, /*C/N=3.5dB*/
++ { 40, 8062 }, /*C/N=4.0dB*/
++ { 45, 7707 }, /*C/N=4.5dB*/
++ { 50, 7353 }, /*C/N=5.0dB*/
++ { 55, 7025 }, /*C/N=5.5dB*/
++ { 60, 6684 }, /*C/N=6.0dB*/
++ { 65, 6331 }, /*C/N=6.5dB*/
++ { 70, 6036 }, /*C/N=7.0dB*/
++ { 75, 5727 }, /*C/N=7.5dB*/
++ { 80, 5437 }, /*C/N=8.0dB*/
++ { 85, 5164 }, /*C/N=8.5dB*/
++ { 90, 4902 }, /*C/N=9.0dB*/
++ { 95, 4653 }, /*C/N=9.5dB*/
++ { 100, 4408 }, /*C/N=10.0dB*/
++ { 105, 4187 }, /*C/N=10.5dB*/
++ { 110, 3961 }, /*C/N=11.0dB*/
++ { 115, 3751 }, /*C/N=11.5dB*/
++ { 120, 3558 }, /*C/N=12.0dB*/
++ { 125, 3368 }, /*C/N=12.5dB*/
++ { 130, 3191 }, /*C/N=13.0dB*/
++ { 135, 3017 }, /*C/N=13.5dB*/
++ { 140, 2862 }, /*C/N=14.0dB*/
++ { 145, 2710 }, /*C/N=14.5dB*/
++ { 150, 2565 }, /*C/N=15.0dB*/
++ { 160, 2300 }, /*C/N=16.0dB*/
++ { 170, 2058 }, /*C/N=17.0dB*/
++ { 180, 1849 }, /*C/N=18.0dB*/
++ { 190, 1663 }, /*C/N=19.0dB*/
++ { 200, 1495 }, /*C/N=20.0dB*/
++ { 210, 1349 }, /*C/N=21.0dB*/
++ { 220, 1222 }, /*C/N=22.0dB*/
++ { 230, 1110 }, /*C/N=23.0dB*/
++ { 240, 1011 }, /*C/N=24.0dB*/
++ { 250, 925 }, /*C/N=25.0dB*/
++ { 260, 853 }, /*C/N=26.0dB*/
++ { 270, 789 }, /*C/N=27.0dB*/
++ { 280, 734 }, /*C/N=28.0dB*/
++ { 290, 690 }, /*C/N=29.0dB*/
++ { 300, 650 }, /*C/N=30.0dB*/
++ { 310, 619 }, /*C/N=31.0dB*/
++ { 320, 593 }, /*C/N=32.0dB*/
++ { 330, 571 }, /*C/N=33.0dB*/
++ { 400, 498 }, /*C/N=40.0dB*/
++ { 450, 484 }, /*C/N=45.0dB*/
++ { 500, 481 } /*C/N=50.0dB*/
++ }
++};
++
++/* RF level C/N Look-Up table */
++static const struct stv0900_table stv0900_rf = {
++ 14,
++ {
++ { -5, 0xCAA1 }, /*-5dBm*/
++ { -10, 0xC229 }, /*-10dBm*/
++ { -15, 0xBB08 }, /*-15dBm*/
++ { -20, 0xB4BC }, /*-20dBm*/
++ { -25, 0xAD5A }, /*-25dBm*/
++ { -30, 0xA298 }, /*-30dBm*/
++ { -35, 0x98A8 }, /*-35dBm*/
++ { -40, 0x8389 }, /*-40dBm*/
++ { -45, 0x59BE }, /*-45dBm*/
++ { -50, 0x3A14 }, /*-50dBm*/
++ { -55, 0x2D11 }, /*-55dBm*/
++ { -60, 0x210D }, /*-60dBm*/
++ { -65, 0xA14F }, /*-65dBm*/
++ { -70, 0x7AA } /*-70dBm*/
++ }
++};
++
++struct stv0900_car_loop_optim {
++ enum fe_stv0900_modcode modcode;
++ u8 car_loop_pilots_on_2;
++ u8 car_loop_pilots_off_2;
++ u8 car_loop_pilots_on_5;
++ u8 car_loop_pilots_off_5;
++ u8 car_loop_pilots_on_10;
++ u8 car_loop_pilots_off_10;
++ u8 car_loop_pilots_on_20;
++ u8 car_loop_pilots_off_20;
++ u8 car_loop_pilots_on_30;
++ u8 car_loop_pilots_off_30;
++
++};
++
++struct stv0900_short_frames_car_loop_optim {
++ enum fe_stv0900_modulation modulation;
++ u8 car_loop_cut12_2; /* Cut 1.2, SR<=3msps */
++ u8 car_loop_cut20_2; /* Cut 2.0, SR<3msps */
++ u8 car_loop_cut12_5; /* Cut 1.2, 3<SR<=7msps */
++ u8 car_loop_cut20_5; /* Cut 2.0, 3<SR<=7msps */
++ u8 car_loop_cut12_10; /* Cut 1.2, 7<SR<=15msps */
++ u8 car_loop_cut20_10; /* Cut 2.0, 7<SR<=15msps */
++ u8 car_loop_cut12_20; /* Cut 1.2, 10<SR<=25msps */
++ u8 car_loop_cut20_20; /* Cut 2.0, 10<SR<=25msps */
++ u8 car_loop_cut12_30; /* Cut 1.2, 25<SR<=45msps */
++ u8 car_loop_cut20_30; /* Cut 2.0, 10<SR<=45msps */
++
++};
++
++/* Cut 1.x Tracking carrier loop carrier QPSK 1/2 to 8PSK 9/10 long Frame */
++static const struct stv0900_car_loop_optim FE_STV0900_S2CarLoop[14] = {
++ /*Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
++ { STV0900_QPSK_12, 0x1C, 0x0D, 0x1B, 0x2C, 0x3A, 0x1C, 0x2A, 0x3B, 0x2A, 0x1B },
++ { STV0900_QPSK_35, 0x2C, 0x0D, 0x2B, 0x2C, 0x3A, 0x0C, 0x3A, 0x2B, 0x2A, 0x0B },
++ { STV0900_QPSK_23, 0x2C, 0x0D, 0x2B, 0x2C, 0x0B, 0x0C, 0x3A, 0x1B, 0x2A, 0x3A },
++ { STV0900_QPSK_34, 0x3C, 0x0D, 0x3B, 0x1C, 0x0B, 0x3B, 0x3A, 0x0B, 0x2A, 0x3A },
++ { STV0900_QPSK_45, 0x3C, 0x0D, 0x3B, 0x1C, 0x0B, 0x3B, 0x3A, 0x0B, 0x2A, 0x3A },
++ { STV0900_QPSK_56, 0x0D, 0x0D, 0x3B, 0x1C, 0x0B, 0x3B, 0x3A, 0x0B, 0x2A, 0x3A },
++ { STV0900_QPSK_89, 0x0D, 0x0D, 0x3B, 0x1C, 0x1B, 0x3B, 0x3A, 0x0B, 0x2A, 0x3A },
++ { STV0900_QPSK_910, 0x1D, 0x0D, 0x3B, 0x1C, 0x1B, 0x3B, 0x3A, 0x0B, 0x2A, 0x3A },
++ { STV0900_8PSK_35, 0x29, 0x3B, 0x09, 0x2B, 0x38, 0x0B, 0x18, 0x1A, 0x08, 0x0A },
++ { STV0900_8PSK_23, 0x0A, 0x3B, 0x29, 0x2B, 0x19, 0x0B, 0x38, 0x1A, 0x18, 0x0A },
++ { STV0900_8PSK_34, 0x3A, 0x3B, 0x2A, 0x2B, 0x39, 0x0B, 0x19, 0x1A, 0x38, 0x0A },
++ { STV0900_8PSK_56, 0x1B, 0x3B, 0x0B, 0x2B, 0x1A, 0x0B, 0x39, 0x1A, 0x19, 0x0A },
++ { STV0900_8PSK_89, 0x3B, 0x3B, 0x0B, 0x2B, 0x2A, 0x0B, 0x39, 0x1A, 0x29, 0x39 },
++ { STV0900_8PSK_910, 0x3B, 0x3B, 0x0B, 0x2B, 0x2A, 0x0B, 0x39, 0x1A, 0x29, 0x39 }
++};
++
++
++/* Cut 2.0 Tracking carrier loop carrier QPSK 1/2 to 8PSK 9/10 long Frame */
++static const struct stv0900_car_loop_optim FE_STV0900_S2CarLoopCut20[14] = {
++ /* Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
++ { STV0900_QPSK_12, 0x1F, 0x3F, 0x1E, 0x3F, 0x3D, 0x1F, 0x3D, 0x3E, 0x3D, 0x1E },
++ { STV0900_QPSK_35, 0x2F, 0x3F, 0x2E, 0x2F, 0x3D, 0x0F, 0x0E, 0x2E, 0x3D, 0x0E },
++ { STV0900_QPSK_23, 0x2F, 0x3F, 0x2E, 0x2F, 0x0E, 0x0F, 0x0E, 0x1E, 0x3D, 0x3D },
++ { STV0900_QPSK_34, 0x3F, 0x3F, 0x3E, 0x1F, 0x0E, 0x3E, 0x0E, 0x1E, 0x3D, 0x3D },
++ { STV0900_QPSK_45, 0x3F, 0x3F, 0x3E, 0x1F, 0x0E, 0x3E, 0x0E, 0x1E, 0x3D, 0x3D },
++ { STV0900_QPSK_56, 0x3F, 0x3F, 0x3E, 0x1F, 0x0E, 0x3E, 0x0E, 0x1E, 0x3D, 0x3D },
++ { STV0900_QPSK_89, 0x3F, 0x3F, 0x3E, 0x1F, 0x1E, 0x3E, 0x0E, 0x1E, 0x3D, 0x3D },
++ { STV0900_QPSK_910, 0x3F, 0x3F, 0x3E, 0x1F, 0x1E, 0x3E, 0x0E, 0x1E, 0x3D, 0x3D },
++ { STV0900_8PSK_35, 0x3c, 0x0c, 0x1c, 0x3b, 0x0c, 0x3b, 0x2b, 0x2b, 0x1b, 0x2b },
++ { STV0900_8PSK_23, 0x1d, 0x0c, 0x3c, 0x0c, 0x2c, 0x3b, 0x0c, 0x2b, 0x2b, 0x2b },
++ { STV0900_8PSK_34, 0x0e, 0x1c, 0x3d, 0x0c, 0x0d, 0x3b, 0x2c, 0x3b, 0x0c, 0x2b },
++ { STV0900_8PSK_56, 0x2e, 0x3e, 0x1e, 0x2e, 0x2d, 0x1e, 0x3c, 0x2d, 0x2c, 0x1d },
++ { STV0900_8PSK_89, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, 0x1e, 0x0d, 0x2d, 0x3c, 0x1d },
++ { STV0900_8PSK_910, 0x3e, 0x3e, 0x1e, 0x2e, 0x3d, 0x1e, 0x1d, 0x2d, 0x0d, 0x1d }
++};
++
++
++
++/* Cut 2.0 Tracking carrier loop carrier 16APSK 2/3 to 32APSK 9/10 long Frame */
++static const struct stv0900_car_loop_optim FE_STV0900_S2APSKCarLoopCut20[11] = {
++ /* Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
++ { STV0900_16APSK_23, 0x0C, 0x0C, 0x0C, 0x0C, 0x1D, 0x0C, 0x3C, 0x0C, 0x2C, 0x0C },
++ { STV0900_16APSK_34, 0x0C, 0x0C, 0x0C, 0x0C, 0x0E, 0x0C, 0x2D, 0x0C, 0x1D, 0x0C },
++ { STV0900_16APSK_45, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x0C, 0x3D, 0x0C, 0x2D, 0x0C },
++ { STV0900_16APSK_56, 0x0C, 0x0C, 0x0C, 0x0C, 0x1E, 0x0C, 0x3D, 0x0C, 0x2D, 0x0C },
++ { STV0900_16APSK_89, 0x0C, 0x0C, 0x0C, 0x0C, 0x2E, 0x0C, 0x0E, 0x0C, 0x3D, 0x0C },
++ { STV0900_16APSK_910, 0x0C, 0x0C, 0x0C, 0x0C, 0x2E, 0x0C, 0x0E, 0x0C, 0x3D, 0x0C },
++ { STV0900_32APSK_34, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C },
++ { STV0900_32APSK_45, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C },
++ { STV0900_32APSK_56, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C },
++ { STV0900_32APSK_89, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C },
++ { STV0900_32APSK_910, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C }
++};
++
++
++/* Cut 2.0 Tracking carrier loop carrier QPSK 1/4 to QPSK 2/5 long Frame */
++static const struct stv0900_car_loop_optim FE_STV0900_S2LowQPCarLoopCut20[3] = {
++ /* Modcod 2MPon 2MPoff 5MPon 5MPoff 10MPon 10MPoff 20MPon 20MPoff 30MPon 30MPoff */
++ { STV0900_QPSK_14, 0x0F, 0x3F, 0x0E, 0x3F, 0x2D, 0x2F, 0x2D, 0x1F, 0x3D, 0x3E },
++ { STV0900_QPSK_13, 0x0F, 0x3F, 0x0E, 0x3F, 0x2D, 0x2F, 0x3D, 0x0F, 0x3D, 0x2E },
++ { STV0900_QPSK_25, 0x1F, 0x3F, 0x1E, 0x3F, 0x3D, 0x1F, 0x3D, 0x3E, 0x3D, 0x2E }
++};
++
++
++/* Cut 2.0 Tracking carrier loop carrier short Frame, cut 1.2 and 2.0 */
++static const struct stv0900_short_frames_car_loop_optim FE_STV0900_S2ShortCarLoop[4] = {
++ /*Mod 2M_cut1.2 2M_cut2.0 5M_cut1.2 5M_cut2.0 10M_cut1.2 10M_cut2.0 20M_cut1.2 20M_cut2.0 30M_cut1.2 30M_cut2.0 */
++ { STV0900_QPSK, 0x3C, 0x2F, 0x2B, 0x2E, 0x0B, 0x0E, 0x3A, 0x0E, 0x2A, 0x3D },
++ { STV0900_8PSK, 0x0B, 0x3E, 0x2A, 0x0E, 0x0A, 0x2D, 0x19, 0x0D, 0x09, 0x3C },
++ { STV0900_16APSK, 0x1B, 0x1E, 0x1B, 0x1E, 0x1B, 0x1E, 0x3A, 0x3D, 0x2A, 0x2D },
++ { STV0900_32APSK, 0x1B, 0x1E, 0x1B, 0x1E, 0x1B, 0x1E, 0x3A, 0x3D, 0x2A, 0x2D }
++};
++
++static const u16 STV0900_InitVal[182][2] = {
++ { R0900_OUTCFG , 0x00 },
++ { R0900_MODECFG , 0xff },
++ { R0900_AGCRF1CFG , 0x11 },
++ { R0900_AGCRF2CFG , 0x13 },
++ { R0900_TSGENERAL1X , 0x14 },
++ { R0900_TSTTNR2 , 0x21 },
++ { R0900_TSTTNR4 , 0x21 },
++ { R0900_P2_DISTXCTL , 0x22 },
++ { R0900_P2_F22TX , 0xc0 },
++ { R0900_P2_F22RX , 0xc0 },
++ { R0900_P2_DISRXCTL , 0x00 },
++ { R0900_P2_TNRSTEPS , 0x87 },
++ { R0900_P2_TNRGAIN , 0x09 },
++ { R0900_P2_DMDCFGMD , 0xF9 },
++ { R0900_P2_DEMOD , 0x08 },
++ { R0900_P2_DMDCFG3 , 0xc4 },
++ { R0900_P2_CARFREQ , 0xed },
++ { R0900_P2_TNRCFG2 , 0x02 },
++ { R0900_P2_TNRCFG3 , 0x02 },
++ { R0900_P2_LDT , 0xd0 },
++ { R0900_P2_LDT2 , 0xb8 },
++ { R0900_P2_TMGCFG , 0xd2 },
++ { R0900_P2_TMGTHRISE , 0x20 },
++ { R0900_P2_TMGTHFALL , 0x00 },
++ { R0900_P2_FECSPY , 0x88 },
++ { R0900_P2_FSPYDATA , 0x3a },
++ { R0900_P2_FBERCPT4 , 0x00 },
++ { R0900_P2_FSPYBER , 0x10 },
++ { R0900_P2_ERRCTRL1 , 0x35 },
++ { R0900_P2_ERRCTRL2 , 0xc1 },
++ { R0900_P2_CFRICFG , 0xf8 },
++ { R0900_P2_NOSCFG , 0x1c },
++ { R0900_P2_DMDT0M , 0x20 },
++ { R0900_P2_CORRELMANT , 0x70 },
++ { R0900_P2_CORRELABS , 0x88 },
++ { R0900_P2_AGC2O , 0x5b },
++ { R0900_P2_AGC2REF , 0x38 },
++ { R0900_P2_CARCFG , 0xe4 },
++ { R0900_P2_ACLC , 0x1A },
++ { R0900_P2_BCLC , 0x09 },
++ { R0900_P2_CARHDR , 0x08 },
++ { R0900_P2_KREFTMG , 0xc1 },
++ { R0900_P2_SFRUPRATIO , 0xf0 },
++ { R0900_P2_SFRLOWRATIO , 0x70 },
++ { R0900_P2_SFRSTEP , 0x58 },
++ { R0900_P2_TMGCFG2 , 0x01 },
++ { R0900_P2_CAR2CFG , 0x26 },
++ { R0900_P2_BCLC2S2Q , 0x86 },
++ { R0900_P2_BCLC2S28 , 0x86 },
++ { R0900_P2_SMAPCOEF7 , 0x77 },
++ { R0900_P2_SMAPCOEF6 , 0x85 },
++ { R0900_P2_SMAPCOEF5 , 0x77 },
++ { R0900_P2_TSCFGL , 0x20 },
++ { R0900_P2_DMDCFG2 , 0x3b },
++ { R0900_P2_MODCODLST0 , 0xff },
++ { R0900_P2_MODCODLST1 , 0xff },
++ { R0900_P2_MODCODLST2 , 0xff },
++ { R0900_P2_MODCODLST3 , 0xff },
++ { R0900_P2_MODCODLST4 , 0xff },
++ { R0900_P2_MODCODLST5 , 0xff },
++ { R0900_P2_MODCODLST6 , 0xff },
++ { R0900_P2_MODCODLST7 , 0xcc },
++ { R0900_P2_MODCODLST8 , 0xcc },
++ { R0900_P2_MODCODLST9 , 0xcc },
++ { R0900_P2_MODCODLSTA , 0xcc },
++ { R0900_P2_MODCODLSTB , 0xcc },
++ { R0900_P2_MODCODLSTC , 0xcc },
++ { R0900_P2_MODCODLSTD , 0xcc },
++ { R0900_P2_MODCODLSTE , 0xcc },
++ { R0900_P2_MODCODLSTF , 0xcf },
++ { R0900_P1_DISTXCTL , 0x22 },
++ { R0900_P1_F22TX , 0xc0 },
++ { R0900_P1_F22RX , 0xc0 },
++ { R0900_P1_DISRXCTL , 0x00 },
++ { R0900_P1_TNRSTEPS , 0x87 },
++ { R0900_P1_TNRGAIN , 0x09 },
++ { R0900_P1_DMDCFGMD , 0xf9 },
++ { R0900_P1_DEMOD , 0x08 },
++ { R0900_P1_DMDCFG3 , 0xc4 },
++ { R0900_P1_DMDT0M , 0x20 },
++ { R0900_P1_CARFREQ , 0xed },
++ { R0900_P1_TNRCFG2 , 0x82 },
++ { R0900_P1_TNRCFG3 , 0x02 },
++ { R0900_P1_LDT , 0xd0 },
++ { R0900_P1_LDT2 , 0xb8 },
++ { R0900_P1_TMGCFG , 0xd2 },
++ { R0900_P1_TMGTHRISE , 0x20 },
++ { R0900_P1_TMGTHFALL , 0x00 },
++ { R0900_P1_SFRUPRATIO , 0xf0 },
++ { R0900_P1_SFRLOWRATIO , 0x70 },
++ { R0900_P1_TSCFGL , 0x20 },
++ { R0900_P1_FECSPY , 0x88 },
++ { R0900_P1_FSPYDATA , 0x3a },
++ { R0900_P1_FBERCPT4 , 0x00 },
++ { R0900_P1_FSPYBER , 0x10 },
++ { R0900_P1_ERRCTRL1 , 0x35 },
++ { R0900_P1_ERRCTRL2 , 0xc1 },
++ { R0900_P1_CFRICFG , 0xf8 },
++ { R0900_P1_NOSCFG , 0x1c },
++ { R0900_P1_CORRELMANT , 0x70 },
++ { R0900_P1_CORRELABS , 0x88 },
++ { R0900_P1_AGC2O , 0x5b },
++ { R0900_P1_AGC2REF , 0x38 },
++ { R0900_P1_CARCFG , 0xe4 },
++ { R0900_P1_ACLC , 0x1A },
++ { R0900_P1_BCLC , 0x09 },
++ { R0900_P1_CARHDR , 0x08 },
++ { R0900_P1_KREFTMG , 0xc1 },
++ { R0900_P1_SFRSTEP , 0x58 },
++ { R0900_P1_TMGCFG2 , 0x01 },
++ { R0900_P1_CAR2CFG , 0x26 },
++ { R0900_P1_BCLC2S2Q , 0x86 },
++ { R0900_P1_BCLC2S28 , 0x86 },
++ { R0900_P1_SMAPCOEF7 , 0x77 },
++ { R0900_P1_SMAPCOEF6 , 0x85 },
++ { R0900_P1_SMAPCOEF5 , 0x77 },
++ { R0900_P1_DMDCFG2 , 0x3b },
++ { R0900_P1_MODCODLST0 , 0xff },
++ { R0900_P1_MODCODLST1 , 0xff },
++ { R0900_P1_MODCODLST2 , 0xff },
++ { R0900_P1_MODCODLST3 , 0xff },
++ { R0900_P1_MODCODLST4 , 0xff },
++ { R0900_P1_MODCODLST5 , 0xff },
++ { R0900_P1_MODCODLST6 , 0xff },
++ { R0900_P1_MODCODLST7 , 0xcc },
++ { R0900_P1_MODCODLST8 , 0xcc },
++ { R0900_P1_MODCODLST9 , 0xcc },
++ { R0900_P1_MODCODLSTA , 0xcc },
++ { R0900_P1_MODCODLSTB , 0xcc },
++ { R0900_P1_MODCODLSTC , 0xcc },
++ { R0900_P1_MODCODLSTD , 0xcc },
++ { R0900_P1_MODCODLSTE , 0xcc },
++ { R0900_P1_MODCODLSTF , 0xcf },
++ { R0900_GENCFG , 0x1d },
++ { R0900_NBITER_NF4 , 0x37 },
++ { R0900_NBITER_NF5 , 0x29 },
++ { R0900_NBITER_NF6 , 0x37 },
++ { R0900_NBITER_NF7 , 0x33 },
++ { R0900_NBITER_NF8 , 0x31 },
++ { R0900_NBITER_NF9 , 0x2f },
++ { R0900_NBITER_NF10 , 0x39 },
++ { R0900_NBITER_NF11 , 0x3a },
++ { R0900_NBITER_NF12 , 0x29 },
++ { R0900_NBITER_NF13 , 0x37 },
++ { R0900_NBITER_NF14 , 0x33 },
++ { R0900_NBITER_NF15 , 0x2f },
++ { R0900_NBITER_NF16 , 0x39 },
++ { R0900_NBITER_NF17 , 0x3a },
++ { R0900_NBITERNOERR , 0x04 },
++ { R0900_GAINLLR_NF4 , 0x0C },
++ { R0900_GAINLLR_NF5 , 0x0F },
++ { R0900_GAINLLR_NF6 , 0x11 },
++ { R0900_GAINLLR_NF7 , 0x14 },
++ { R0900_GAINLLR_NF8 , 0x17 },
++ { R0900_GAINLLR_NF9 , 0x19 },
++ { R0900_GAINLLR_NF10 , 0x20 },
++ { R0900_GAINLLR_NF11 , 0x21 },
++ { R0900_GAINLLR_NF12 , 0x0D },
++ { R0900_GAINLLR_NF13 , 0x0F },
++ { R0900_GAINLLR_NF14 , 0x13 },
++ { R0900_GAINLLR_NF15 , 0x1A },
++ { R0900_GAINLLR_NF16 , 0x1F },
++ { R0900_GAINLLR_NF17 , 0x21 },
++ { R0900_RCCFGH , 0x20 },
++ { R0900_P1_FECM , 0x01 }, /*disable DSS modes*/
++ { R0900_P2_FECM , 0x01 }, /*disable DSS modes*/
++ { R0900_P1_PRVIT , 0x2F }, /*disable puncture rate 6/7*/
++ { R0900_P2_PRVIT , 0x2F }, /*disable puncture rate 6/7*/
++ { R0900_STROUT1CFG , 0x4c },
++ { R0900_STROUT2CFG , 0x4c },
++ { R0900_CLKOUT1CFG , 0x50 },
++ { R0900_CLKOUT2CFG , 0x50 },
++ { R0900_DPN1CFG , 0x4a },
++ { R0900_DPN2CFG , 0x4a },
++ { R0900_DATA71CFG , 0x52 },
++ { R0900_DATA72CFG , 0x52 },
++ { R0900_P1_TSCFGM , 0xc0 },
++ { R0900_P2_TSCFGM , 0xc0 },
++ { R0900_P1_TSCFGH , 0xe0 }, /* DVB-CI timings */
++ { R0900_P2_TSCFGH , 0xe0 }, /* DVB-CI timings */
++ { R0900_P1_TSSPEED , 0x40 },
++ { R0900_P2_TSSPEED , 0x40 },
++};
++
++static const u16 STV0900_Cut20_AddOnVal[32][2] = {
++ { R0900_P2_DMDCFG3 , 0xe8 },
++ { R0900_P2_DMDCFG4 , 0x10 },
++ { R0900_P2_CARFREQ , 0x38 },
++ { R0900_P2_CARHDR , 0x20 },
++ { R0900_P2_KREFTMG , 0x5a },
++ { R0900_P2_SMAPCOEF7 , 0x06 },
++ { R0900_P2_SMAPCOEF6 , 0x00 },
++ { R0900_P2_SMAPCOEF5 , 0x04 },
++ { R0900_P2_NOSCFG , 0x0c },
++ { R0900_P1_DMDCFG3 , 0xe8 },
++ { R0900_P1_DMDCFG4 , 0x10 },
++ { R0900_P1_CARFREQ , 0x38 },
++ { R0900_P1_CARHDR , 0x20 },
++ { R0900_P1_KREFTMG , 0x5a },
++ { R0900_P1_SMAPCOEF7 , 0x06 },
++ { R0900_P1_SMAPCOEF6 , 0x00 },
++ { R0900_P1_SMAPCOEF5 , 0x04 },
++ { R0900_P1_NOSCFG , 0x0c },
++ { R0900_GAINLLR_NF4 , 0x21 },
++ { R0900_GAINLLR_NF5 , 0x21 },
++ { R0900_GAINLLR_NF6 , 0x20 },
++ { R0900_GAINLLR_NF7 , 0x1F },
++ { R0900_GAINLLR_NF8 , 0x1E },
++ { R0900_GAINLLR_NF9 , 0x1E },
++ { R0900_GAINLLR_NF10 , 0x1D },
++ { R0900_GAINLLR_NF11 , 0x1B },
++ { R0900_GAINLLR_NF12 , 0x20 },
++ { R0900_GAINLLR_NF13 , 0x20 },
++ { R0900_GAINLLR_NF14 , 0x20 },
++ { R0900_GAINLLR_NF15 , 0x20 },
++ { R0900_GAINLLR_NF16 , 0x20 },
++ { R0900_GAINLLR_NF17 , 0x21 }
++
++};
++
++#endif
+diff --git a/drivers/media/dvb/frontends/stv0900_priv.h b/drivers/media/dvb/frontends/stv0900_priv.h
+new file mode 100644
+index 0000000..762d5af
+--- /dev/null
++++ b/drivers/media/dvb/frontends/stv0900_priv.h
+@@ -0,0 +1,430 @@
++/*
++ * stv0900_priv.h
++ *
++ * Driver for ST STV0900 satellite demodulator IC.
++ *
++ * Copyright (C) ST Microelectronics.
++ * Copyright (C) 2009 NetUP Inc.
++ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
++ *
++ * 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 STV0900_PRIV_H
++#define STV0900_PRIV_H
++
++#include <linux/i2c.h>
++
++#define ABS(X) ((X) < 0 ? (-1 * (X)) : (X))
++#define INRANGE(X, Y, Z) ((((X) <= (Y)) && ((Y) <= (Z))) \
++ || (((Z) <= (Y)) && ((Y) <= (X))) ? 1 : 0)
++
++#ifndef MAKEWORD
++#define MAKEWORD(X, Y) (((X) << 8) + (Y))
++#endif
++
++#define LSB(X) (((X) & 0xFF))
++#define MSB(Y) (((Y) >> 8) & 0xFF)
++
++#ifndef TRUE
++#define TRUE (1 == 1)
++#endif
++#ifndef FALSE
++#define FALSE (!TRUE)
++#endif
++
++#define dmd_reg(a, b, c) \
++ do { \
++ a = 0; \
++ switch (demod) { \
++ case STV0900_DEMOD_1: \
++ default: \
++ a = b; \
++ break; \
++ case STV0900_DEMOD_2: \
++ a = c; \
++ break; \
++ } \
++ } while (0)
++
++#define dmd_choose(a, b) (demod = STV0900_DEMOD_2 ? b : a))
++
++static int stvdebug;
++
++#define dprintk(args...) \
++ do { \
++ if (stvdebug) \
++ printk(KERN_DEBUG args); \
++ } while (0)
++
++#define STV0900_MAXLOOKUPSIZE 500
++#define STV0900_BLIND_SEARCH_AGC2_TH 700
++
++/* One point of the lookup table */
++struct stv000_lookpoint {
++ s32 realval;/* real value */
++ s32 regval;/* binary value */
++};
++
++/* Lookup table definition */
++struct stv0900_table{
++ s32 size;/* Size of the lookup table */
++ struct stv000_lookpoint table[STV0900_MAXLOOKUPSIZE];/* Lookup table */
++};
++
++enum fe_stv0900_error {
++ STV0900_NO_ERROR = 0,
++ STV0900_INVALID_HANDLE,
++ STV0900_BAD_PARAMETER,
++ STV0900_I2C_ERROR,
++ STV0900_SEARCH_FAILED,
++};
++
++enum fe_stv0900_clock_type {
++ STV0900_USE_REGISTERS_DEFAULT,
++ STV0900_SERIAL_PUNCT_CLOCK,/*Serial punctured clock */
++ STV0900_SERIAL_CONT_CLOCK,/*Serial continues clock */
++ STV0900_PARALLEL_PUNCT_CLOCK,/*Parallel punctured clock */
++ STV0900_DVBCI_CLOCK/*Parallel continues clock : DVBCI */
++};
++
++enum fe_stv0900_search_state {
++ STV0900_SEARCH = 0,
++ STV0900_PLH_DETECTED,
++ STV0900_DVBS2_FOUND,
++ STV0900_DVBS_FOUND
++
++};
++
++enum fe_stv0900_ldpc_state {
++ STV0900_PATH1_OFF_PATH2_OFF = 0,
++ STV0900_PATH1_ON_PATH2_OFF = 1,
++ STV0900_PATH1_OFF_PATH2_ON = 2,
++ STV0900_PATH1_ON_PATH2_ON = 3
++};
++
++enum fe_stv0900_signal_type {
++ STV0900_NOAGC1 = 0,
++ STV0900_AGC1OK,
++ STV0900_NOTIMING,
++ STV0900_ANALOGCARRIER,
++ STV0900_TIMINGOK,
++ STV0900_NOAGC2,
++ STV0900_AGC2OK,
++ STV0900_NOCARRIER,
++ STV0900_CARRIEROK,
++ STV0900_NODATA,
++ STV0900_DATAOK,
++ STV0900_OUTOFRANGE,
++ STV0900_RANGEOK
++};
++
++enum fe_stv0900_demod_num {
++ STV0900_DEMOD_1,
++ STV0900_DEMOD_2
++};
++
++enum fe_stv0900_tracking_standard {
++ STV0900_DVBS1_STANDARD,/* Found Standard*/
++ STV0900_DVBS2_STANDARD,
++ STV0900_DSS_STANDARD,
++ STV0900_TURBOCODE_STANDARD,
++ STV0900_UNKNOWN_STANDARD
++};
++
++enum fe_stv0900_search_standard {
++ STV0900_AUTO_SEARCH,
++ STV0900_SEARCH_DVBS1,/* Search Standard*/
++ STV0900_SEARCH_DVBS2,
++ STV0900_SEARCH_DSS,
++ STV0900_SEARCH_TURBOCODE
++};
++
++enum fe_stv0900_search_algo {
++ STV0900_BLIND_SEARCH,/* offset freq and SR are Unknown */
++ STV0900_COLD_START,/* only the SR is known */
++ STV0900_WARM_START/* offset freq and SR are known */
++};
++
++enum fe_stv0900_modulation {
++ STV0900_QPSK,
++ STV0900_8PSK,
++ STV0900_16APSK,
++ STV0900_32APSK,
++ STV0900_UNKNOWN
++};
++
++enum fe_stv0900_modcode {
++ STV0900_DUMMY_PLF,
++ STV0900_QPSK_14,
++ STV0900_QPSK_13,
++ STV0900_QPSK_25,
++ STV0900_QPSK_12,
++ STV0900_QPSK_35,
++ STV0900_QPSK_23,
++ STV0900_QPSK_34,
++ STV0900_QPSK_45,
++ STV0900_QPSK_56,
++ STV0900_QPSK_89,
++ STV0900_QPSK_910,
++ STV0900_8PSK_35,
++ STV0900_8PSK_23,
++ STV0900_8PSK_34,
++ STV0900_8PSK_56,
++ STV0900_8PSK_89,
++ STV0900_8PSK_910,
++ STV0900_16APSK_23,
++ STV0900_16APSK_34,
++ STV0900_16APSK_45,
++ STV0900_16APSK_56,
++ STV0900_16APSK_89,
++ STV0900_16APSK_910,
++ STV0900_32APSK_34,
++ STV0900_32APSK_45,
++ STV0900_32APSK_56,
++ STV0900_32APSK_89,
++ STV0900_32APSK_910,
++ STV0900_MODCODE_UNKNOWN
++};
++
++enum fe_stv0900_fec {/*DVBS1, DSS and turbo code puncture rate*/
++ STV0900_FEC_1_2 = 0,
++ STV0900_FEC_2_3,
++ STV0900_FEC_3_4,
++ STV0900_FEC_4_5,/*for turbo code only*/
++ STV0900_FEC_5_6,
++ STV0900_FEC_6_7,/*for DSS only */
++ STV0900_FEC_7_8,
++ STV0900_FEC_8_9,/*for turbo code only*/
++ STV0900_FEC_UNKNOWN
++};
++
++enum fe_stv0900_frame_length {
++ STV0900_LONG_FRAME,
++ STV0900_SHORT_FRAME
++};
++
++enum fe_stv0900_pilot {
++ STV0900_PILOTS_OFF,
++ STV0900_PILOTS_ON
++};
++
++enum fe_stv0900_rolloff {
++ STV0900_35,
++ STV0900_25,
++ STV0900_20
++};
++
++enum fe_stv0900_search_iq {
++ STV0900_IQ_AUTO,
++ STV0900_IQ_AUTO_NORMAL_FIRST,
++ STV0900_IQ_FORCE_NORMAL,
++ STV0900_IQ_FORCE_SWAPPED
++};
++
++enum stv0900_iq_inversion {
++ STV0900_IQ_NORMAL,
++ STV0900_IQ_SWAPPED
++};
++
++enum fe_stv0900_diseqc_mode {
++ STV0900_22KHZ_Continues = 0,
++ STV0900_DISEQC_2_3_PWM = 2,
++ STV0900_DISEQC_3_3_PWM = 3,
++ STV0900_DISEQC_2_3_ENVELOP = 4,
++ STV0900_DISEQC_3_3_ENVELOP = 5
++};
++
++enum fe_stv0900_demod_mode {
++ STV0900_SINGLE = 0,
++ STV0900_DUAL
++};
++
++struct stv0900_init_params{
++ u32 dmd_ref_clk;/* Refrence,Input clock for the demod in Hz */
++
++ /* Demodulator Type (single demod or dual demod) */
++ enum fe_stv0900_demod_mode demod_mode;
++ enum fe_stv0900_rolloff rolloff;
++ enum fe_stv0900_clock_type path1_ts_clock;
++
++ u8 tun1_maddress;
++ int tuner1_adc;
++
++ /* IQ from the tuner1 to the demod */
++ enum stv0900_iq_inversion tun1_iq_inversion;
++ enum fe_stv0900_clock_type path2_ts_clock;
++
++ u8 tun2_maddress;
++ int tuner2_adc;
++
++ /* IQ from the tuner2 to the demod */
++ enum stv0900_iq_inversion tun2_iq_inversion;
++};
++
++struct stv0900_search_params {
++ enum fe_stv0900_demod_num path;/* Path Used demod1 or 2 */
++
++ u32 frequency;/* Transponder frequency (in KHz) */
++ u32 symbol_rate;/* Transponder symbol rate (in bds)*/
++ u32 search_range;/* Range of the search (in Hz) */
++
++ enum fe_stv0900_search_standard standard;
++ enum fe_stv0900_modulation modulation;
++ enum fe_stv0900_fec fec;
++ enum fe_stv0900_modcode modcode;
++ enum fe_stv0900_search_iq iq_inversion;
++ enum fe_stv0900_search_algo search_algo;
++
++};
++
++struct stv0900_signal_info {
++ int locked;/* Transponder locked */
++ u32 frequency;/* Transponder frequency (in KHz) */
++ u32 symbol_rate;/* Transponder symbol rate (in Mbds) */
++
++ enum fe_stv0900_tracking_standard standard;
++ enum fe_stv0900_fec fec;
++ enum fe_stv0900_modcode modcode;
++ enum fe_stv0900_modulation modulation;
++ enum fe_stv0900_pilot pilot;
++ enum fe_stv0900_frame_length frame_length;
++ enum stv0900_iq_inversion spectrum;
++ enum fe_stv0900_rolloff rolloff;
++
++ s32 Power;/* Power of the RF signal (dBm) */
++ s32 C_N;/* Carrier to noise ratio (dB x10)*/
++ u32 BER;/* Bit error rate (x10^7) */
++
++};
++
++struct stv0900_internal{
++ s32 quartz;
++ s32 mclk;
++ /* manual RollOff for DVBS1/DSS only */
++ enum fe_stv0900_rolloff rolloff;
++ /* Demodulator use for single demod or for dual demod) */
++ enum fe_stv0900_demod_mode demod_mode;
++
++ /*Demod 1*/
++ s32 tuner1_freq;
++ s32 tuner1_bw;
++ s32 dmd1_symbol_rate;
++ s32 dmd1_srch_range;
++
++ /* algorithm for search Blind, Cold or Warm*/
++ enum fe_stv0900_search_algo dmd1_srch_algo;
++ /* search standard: Auto, DVBS1/DSS only or DVBS2 only*/
++ enum fe_stv0900_search_standard dmd1_srch_standard;
++ /* inversion search : auto, auto norma first, normal or inverted */
++ enum fe_stv0900_search_iq dmd1_srch_iq_inv;
++ enum fe_stv0900_modcode dmd1_modcode;
++ enum fe_stv0900_modulation dmd1_modulation;
++ enum fe_stv0900_fec dmd1_fec;
++
++ struct stv0900_signal_info dmd1_rslts;
++ enum fe_stv0900_signal_type dmd1_state;
++
++ enum fe_stv0900_error dmd1_err;
++
++ /*Demod 2*/
++ s32 tuner2_freq;
++ s32 tuner2_bw;
++ s32 dmd2_symbol_rate;
++ s32 dmd2_srch_range;
++
++ enum fe_stv0900_search_algo dmd2_srch_algo;
++ enum fe_stv0900_search_standard dmd2_srch_stndrd;
++ /* inversion search : auto, auto normal first, normal or inverted */
++ enum fe_stv0900_search_iq dmd2_srch_iq_inv;
++ enum fe_stv0900_modcode dmd2_modcode;
++ enum fe_stv0900_modulation dmd2_modulation;
++ enum fe_stv0900_fec dmd2_fec;
++
++ /* results of the search*/
++ struct stv0900_signal_info dmd2_rslts;
++ /* current state of the search algorithm */
++ enum fe_stv0900_signal_type dmd2_state;
++
++ enum fe_stv0900_error dmd2_err;
++
++ struct i2c_adapter *i2c_adap;
++ u8 i2c_addr;
++ u8 clkmode;/* 0 for CLKI, 2 for XTALI */
++ u8 chip_id;
++ enum fe_stv0900_error errs;
++ int dmds_used;
++};
++
++/* state for each demod */
++struct stv0900_state {
++ /* pointer for internal params, one for each pair of demods */
++ struct stv0900_internal *internal;
++ struct i2c_adapter *i2c_adap;
++ const struct stv0900_config *config;
++ struct dvb_frontend frontend;
++ int demod;
++};
++
++extern s32 ge2comp(s32 a, s32 width);
++
++extern void stv0900_write_reg(struct stv0900_internal *i_params,
++ u16 reg_addr, u8 reg_data);
++
++extern u8 stv0900_read_reg(struct stv0900_internal *i_params,
++ u16 reg_addr);
++
++extern void stv0900_write_bits(struct stv0900_internal *i_params,
++ u32 label, u8 val);
++
++extern u8 stv0900_get_bits(struct stv0900_internal *i_params,
++ u32 label);
++
++extern int stv0900_get_demod_lock(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod, s32 time_out);
++extern int stv0900_check_signal_presence(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod);
++
++extern enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe);
++
++extern void stv0900_set_tuner(struct dvb_frontend *fe, u32 frequency,
++ u32 bandwidth);
++extern void stv0900_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth);
++
++extern void stv0900_start_search(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod);
++
++extern u8 stv0900_get_optim_carr_loop(s32 srate,
++ enum fe_stv0900_modcode modcode,
++ s32 pilot, u8 chip_id);
++
++extern u8 stv0900_get_optim_short_carr_loop(s32 srate,
++ enum fe_stv0900_modulation modulation,
++ u8 chip_id);
++
++extern void stv0900_stop_all_s2_modcod(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod);
++
++extern void stv0900_activate_s2_modcode(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod);
++
++extern void stv0900_activate_s2_modcode_single(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod);
++
++extern enum fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe,
++ enum fe_stv0900_demod_num demod);
++
++#endif
+diff --git a/drivers/media/dvb/frontends/stv0900_reg.h b/drivers/media/dvb/frontends/stv0900_reg.h
+new file mode 100644
+index 0000000..264f9cf
+--- /dev/null
++++ b/drivers/media/dvb/frontends/stv0900_reg.h
+@@ -0,0 +1,3787 @@
++/*
++ * stv0900_reg.h
++ *
++ * Driver for ST STV0900 satellite demodulator IC.
++ *
++ * Copyright (C) ST Microelectronics.
++ * Copyright (C) 2009 NetUP Inc.
++ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
++ *
++ * 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 STV0900_REG_H
++#define STV0900_REG_H
++
++/*MID*/
++#define R0900_MID 0xf100
++#define F0900_MCHIP_IDENT 0xf10000f0
++#define F0900_MRELEASE 0xf100000f
++
++/*DACR1*/
++#define R0900_DACR1 0xf113
++#define F0900_DAC_MODE 0xf11300e0
++#define F0900_DAC_VALUE1 0xf113000f
++
++/*DACR2*/
++#define R0900_DACR2 0xf114
++#define F0900_DAC_VALUE0 0xf11400ff
++
++/*OUTCFG*/
++#define R0900_OUTCFG 0xf11c
++#define F0900_INV_DATA6 0xf11c0080
++#define F0900_OUTSERRS1_HZ 0xf11c0040
++#define F0900_OUTSERRS2_HZ 0xf11c0020
++#define F0900_OUTSERRS3_HZ 0xf11c0010
++#define F0900_OUTPARRS3_HZ 0xf11c0008
++#define F0900_OUTHZ3_CONTROL 0xf11c0007
++
++/*MODECFG*/
++#define R0900_MODECFG 0xf11d
++#define F0900_FECSPY_SEL_2 0xf11d0020
++#define F0900_HWARE_SEL_2 0xf11d0010
++#define F0900_PKTDEL_SEL_2 0xf11d0008
++#define F0900_DISEQC_SEL_2 0xf11d0004
++#define F0900_VIT_SEL_2 0xf11d0002
++#define F0900_DEMOD_SEL_2 0xf11d0001
++
++/*IRQSTATUS3*/
++#define R0900_IRQSTATUS3 0xf120
++#define F0900_SPLL_LOCK 0xf1200020
++#define F0900_SSTREAM_LCK_3 0xf1200010
++#define F0900_SSTREAM_LCK_2 0xf1200008
++#define F0900_SSTREAM_LCK_1 0xf1200004
++#define F0900_SDVBS1_PRF_2 0xf1200002
++#define F0900_SDVBS1_PRF_1 0xf1200001
++
++/*IRQSTATUS2*/
++#define R0900_IRQSTATUS2 0xf121
++#define F0900_SSPY_ENDSIM_3 0xf1210080
++#define F0900_SSPY_ENDSIM_2 0xf1210040
++#define F0900_SSPY_ENDSIM_1 0xf1210020
++#define F0900_SPKTDEL_ERROR_2 0xf1210010
++#define F0900_SPKTDEL_LOCKB_2 0xf1210008
++#define F0900_SPKTDEL_LOCK_2 0xf1210004
++#define F0900_SPKTDEL_ERROR_1 0xf1210002
++#define F0900_SPKTDEL_LOCKB_1 0xf1210001
++
++/*IRQSTATUS1*/
++#define R0900_IRQSTATUS1 0xf122
++#define F0900_SPKTDEL_LOCK_1 0xf1220080
++#define F0900_SEXTPINB2 0xf1220040
++#define F0900_SEXTPIN2 0xf1220020
++#define F0900_SEXTPINB1 0xf1220010
++#define F0900_SEXTPIN1 0xf1220008
++#define F0900_SDEMOD_LOCKB_2 0xf1220004
++#define F0900_SDEMOD_LOCK_2 0xf1220002
++#define F0900_SDEMOD_IRQ_2 0xf1220001
++
++/*IRQSTATUS0*/
++#define R0900_IRQSTATUS0 0xf123
++#define F0900_SDEMOD_LOCKB_1 0xf1230080
++#define F0900_SDEMOD_LOCK_1 0xf1230040
++#define F0900_SDEMOD_IRQ_1 0xf1230020
++#define F0900_SBCH_ERRFLAG 0xf1230010
++#define F0900_SDISEQC2RX_IRQ 0xf1230008
++#define F0900_SDISEQC2TX_IRQ 0xf1230004
++#define F0900_SDISEQC1RX_IRQ 0xf1230002
++#define F0900_SDISEQC1TX_IRQ 0xf1230001
++
++/*IRQMASK3*/
++#define R0900_IRQMASK3 0xf124
++#define F0900_MPLL_LOCK 0xf1240020
++#define F0900_MSTREAM_LCK_3 0xf1240010
++#define F0900_MSTREAM_LCK_2 0xf1240008
++#define F0900_MSTREAM_LCK_1 0xf1240004
++#define F0900_MDVBS1_PRF_2 0xf1240002
++#define F0900_MDVBS1_PRF_1 0xf1240001
++
++/*IRQMASK2*/
++#define R0900_IRQMASK2 0xf125
++#define F0900_MSPY_ENDSIM_3 0xf1250080
++#define F0900_MSPY_ENDSIM_2 0xf1250040
++#define F0900_MSPY_ENDSIM_1 0xf1250020
++#define F0900_MPKTDEL_ERROR_2 0xf1250010
++#define F0900_MPKTDEL_LOCKB_2 0xf1250008
++#define F0900_MPKTDEL_LOCK_2 0xf1250004
++#define F0900_MPKTDEL_ERROR_1 0xf1250002
++#define F0900_MPKTDEL_LOCKB_1 0xf1250001
++
++/*IRQMASK1*/
++#define R0900_IRQMASK1 0xf126
++#define F0900_MPKTDEL_LOCK_1 0xf1260080
++#define F0900_MEXTPINB2 0xf1260040
++#define F0900_MEXTPIN2 0xf1260020
++#define F0900_MEXTPINB1 0xf1260010
++#define F0900_MEXTPIN1 0xf1260008
++#define F0900_MDEMOD_LOCKB_2 0xf1260004
++#define F0900_MDEMOD_LOCK_2 0xf1260002
++#define F0900_MDEMOD_IRQ_2 0xf1260001
++
++/*IRQMASK0*/
++#define R0900_IRQMASK0 0xf127
++#define F0900_MDEMOD_LOCKB_1 0xf1270080
++#define F0900_MDEMOD_LOCK_1 0xf1270040
++#define F0900_MDEMOD_IRQ_1 0xf1270020
++#define F0900_MBCH_ERRFLAG 0xf1270010
++#define F0900_MDISEQC2RX_IRQ 0xf1270008
++#define F0900_MDISEQC2TX_IRQ 0xf1270004
++#define F0900_MDISEQC1RX_IRQ 0xf1270002
++#define F0900_MDISEQC1TX_IRQ 0xf1270001
++
++/*I2CCFG*/
++#define R0900_I2CCFG 0xf129
++#define F0900_I2C2_FASTMODE 0xf1290080
++#define F0900_STATUS_WR2 0xf1290040
++#define F0900_I2C2ADDR_INC 0xf1290030
++#define F0900_I2C_FASTMODE 0xf1290008
++#define F0900_STATUS_WR 0xf1290004
++#define F0900_I2CADDR_INC 0xf1290003
++
++/*P1_I2CRPT*/
++#define R0900_P1_I2CRPT 0xf12a
++#define F0900_P1_I2CT_ON 0xf12a0080
++#define F0900_P1_ENARPT_LEVEL 0xf12a0070
++#define F0900_P1_SCLT_DELAY 0xf12a0008
++#define F0900_P1_STOP_ENABLE 0xf12a0004
++#define F0900_P1_STOP_SDAT2SDA 0xf12a0002
++
++/*P2_I2CRPT*/
++#define R0900_P2_I2CRPT 0xf12b
++#define F0900_P2_I2CT_ON 0xf12b0080
++#define F0900_P2_ENARPT_LEVEL 0xf12b0070
++#define F0900_P2_SCLT_DELAY 0xf12b0008
++#define F0900_P2_STOP_ENABLE 0xf12b0004
++#define F0900_P2_STOP_SDAT2SDA 0xf12b0002
++
++/*CLKI2CFG*/
++#define R0900_CLKI2CFG 0xf140
++#define F0900_CLKI2_OPD 0xf1400080
++#define F0900_CLKI2_CONFIG 0xf140007e
++#define F0900_CLKI2_XOR 0xf1400001
++
++/*GPIO1CFG*/
++#define R0900_GPIO1CFG 0xf141
++#define F0900_GPIO1_OPD 0xf1410080
++#define F0900_GPIO1_CONFIG 0xf141007e
++#define F0900_GPIO1_XOR 0xf1410001
++
++/*GPIO2CFG*/
++#define R0900_GPIO2CFG 0xf142
++#define F0900_GPIO2_OPD 0xf1420080
++#define F0900_GPIO2_CONFIG 0xf142007e
++#define F0900_GPIO2_XOR 0xf1420001
++
++/*GPIO3CFG*/
++#define R0900_GPIO3CFG 0xf143
++#define F0900_GPIO3_OPD 0xf1430080
++#define F0900_GPIO3_CONFIG 0xf143007e
++#define F0900_GPIO3_XOR 0xf1430001
++
++/*GPIO4CFG*/
++#define R0900_GPIO4CFG 0xf144
++#define F0900_GPIO4_OPD 0xf1440080
++#define F0900_GPIO4_CONFIG 0xf144007e
++#define F0900_GPIO4_XOR 0xf1440001
++
++/*GPIO5CFG*/
++#define R0900_GPIO5CFG 0xf145
++#define F0900_GPIO5_OPD 0xf1450080
++#define F0900_GPIO5_CONFIG 0xf145007e
++#define F0900_GPIO5_XOR 0xf1450001
++
++/*GPIO6CFG*/
++#define R0900_GPIO6CFG 0xf146
++#define F0900_GPIO6_OPD 0xf1460080
++#define F0900_GPIO6_CONFIG 0xf146007e
++#define F0900_GPIO6_XOR 0xf1460001
++
++/*GPIO7CFG*/
++#define R0900_GPIO7CFG 0xf147
++#define F0900_GPIO7_OPD 0xf1470080
++#define F0900_GPIO7_CONFIG 0xf147007e
++#define F0900_GPIO7_XOR 0xf1470001
++
++/*GPIO8CFG*/
++#define R0900_GPIO8CFG 0xf148
++#define F0900_GPIO8_OPD 0xf1480080
++#define F0900_GPIO8_CONFIG 0xf148007e
++#define F0900_GPIO8_XOR 0xf1480001
++
++/*GPIO9CFG*/
++#define R0900_GPIO9CFG 0xf149
++#define F0900_GPIO9_OPD 0xf1490080
++#define F0900_GPIO9_CONFIG 0xf149007e
++#define F0900_GPIO9_XOR 0xf1490001
++
++/*GPIO10CFG*/
++#define R0900_GPIO10CFG 0xf14a
++#define F0900_GPIO10_OPD 0xf14a0080
++#define F0900_GPIO10_CONFIG 0xf14a007e
++#define F0900_GPIO10_XOR 0xf14a0001
++
++/*GPIO11CFG*/
++#define R0900_GPIO11CFG 0xf14b
++#define F0900_GPIO11_OPD 0xf14b0080
++#define F0900_GPIO11_CONFIG 0xf14b007e
++#define F0900_GPIO11_XOR 0xf14b0001
++
++/*GPIO12CFG*/
++#define R0900_GPIO12CFG 0xf14c
++#define F0900_GPIO12_OPD 0xf14c0080
++#define F0900_GPIO12_CONFIG 0xf14c007e
++#define F0900_GPIO12_XOR 0xf14c0001
++
++/*GPIO13CFG*/
++#define R0900_GPIO13CFG 0xf14d
++#define F0900_GPIO13_OPD 0xf14d0080
++#define F0900_GPIO13_CONFIG 0xf14d007e
++#define F0900_GPIO13_XOR 0xf14d0001
++
++/*CS0CFG*/
++#define R0900_CS0CFG 0xf14e
++#define F0900_CS0_OPD 0xf14e0080
++#define F0900_CS0_CONFIG 0xf14e007e
++#define F0900_CS0_XOR 0xf14e0001
++
++/*CS1CFG*/
++#define R0900_CS1CFG 0xf14f
++#define F0900_CS1_OPD 0xf14f0080
++#define F0900_CS1_CONFIG 0xf14f007e
++#define F0900_CS1_XOR 0xf14f0001
++
++/*STDBYCFG*/
++#define R0900_STDBYCFG 0xf150
++#define F0900_STDBY_OPD 0xf1500080
++#define F0900_STDBY_CONFIG 0xf150007e
++#define F0900_STBDY_XOR 0xf1500001
++
++/*DIRCLKCFG*/
++#define R0900_DIRCLKCFG 0xf151
++#define F0900_DIRCLK_OPD 0xf1510080
++#define F0900_DIRCLK_CONFIG 0xf151007e
++#define F0900_DIRCLK_XOR 0xf1510001
++
++/*AGCRF1CFG*/
++#define R0900_AGCRF1CFG 0xf152
++#define F0900_AGCRF1_OPD 0xf1520080
++#define F0900_AGCRF1_CONFIG 0xf152007e
++#define F0900_AGCRF1_XOR 0xf1520001
++
++/*SDAT1CFG*/
++#define R0900_SDAT1CFG 0xf153
++#define F0900_SDAT1_OPD 0xf1530080
++#define F0900_SDAT1_CONFIG 0xf153007e
++#define F0900_SDAT1_XOR 0xf1530001
++
++/*SCLT1CFG*/
++#define R0900_SCLT1CFG 0xf154
++#define F0900_SCLT1_OPD 0xf1540080
++#define F0900_SCLT1_CONFIG 0xf154007e
++#define F0900_SCLT1_XOR 0xf1540001
++
++/*DISEQCO1CFG*/
++#define R0900_DISEQCO1CFG 0xf155
++#define F0900_DISEQCO1_OPD 0xf1550080
++#define F0900_DISEQCO1_CONFIG 0xf155007e
++#define F0900_DISEQC1_XOR 0xf1550001
++
++/*AGCRF2CFG*/
++#define R0900_AGCRF2CFG 0xf156
++#define F0900_AGCRF2_OPD 0xf1560080
++#define F0900_AGCRF2_CONFIG 0xf156007e
++#define F0900_AGCRF2_XOR 0xf1560001
++
++/*SDAT2CFG*/
++#define R0900_SDAT2CFG 0xf157
++#define F0900_SDAT2_OPD 0xf1570080
++#define F0900_SDAT2_CONFIG 0xf157007e
++#define F0900_SDAT2_XOR 0xf1570001
++
++/*SCLT2CFG*/
++#define R0900_SCLT2CFG 0xf158
++#define F0900_SCLT2_OPD 0xf1580080
++#define F0900_SCLT2_CONFIG 0xf158007e
++#define F0900_SCLT2_XOR 0xf1580001
++
++/*DISEQCO2CFG*/
++#define R0900_DISEQCO2CFG 0xf159
++#define F0900_DISEQCO2_OPD 0xf1590080
++#define F0900_DISEQCO2_CONFIG 0xf159007e
++#define F0900_DISEQC2_XOR 0xf1590001
++
++/*CLKOUT27CFG*/
++#define R0900_CLKOUT27CFG 0xf15a
++#define F0900_CLKOUT27_OPD 0xf15a0080
++#define F0900_CLKOUT27_CONFIG 0xf15a007e
++#define F0900_CLKOUT27_XOR 0xf15a0001
++
++/*ERROR1CFG*/
++#define R0900_ERROR1CFG 0xf15b
++#define F0900_ERROR1_OPD 0xf15b0080
++#define F0900_ERROR1_CONFIG 0xf15b007e
++#define F0900_ERROR1_XOR 0xf15b0001
++
++/*DPN1CFG*/
++#define R0900_DPN1CFG 0xf15c
++#define F0900_DPN1_OPD 0xf15c0080
++#define F0900_DPN1_CONFIG 0xf15c007e
++#define F0900_DPN1_XOR 0xf15c0001
++
++/*STROUT1CFG*/
++#define R0900_STROUT1CFG 0xf15d
++#define F0900_STROUT1_OPD 0xf15d0080
++#define F0900_STROUT1_CONFIG 0xf15d007e
++#define F0900_STROUT1_XOR 0xf15d0001
++
++/*CLKOUT1CFG*/
++#define R0900_CLKOUT1CFG 0xf15e
++#define F0900_CLKOUT1_OPD 0xf15e0080
++#define F0900_CLKOUT1_CONFIG 0xf15e007e
++#define F0900_CLKOUT1_XOR 0xf15e0001
++
++/*DATA71CFG*/
++#define R0900_DATA71CFG 0xf15f
++#define F0900_DATA71_OPD 0xf15f0080
++#define F0900_DATA71_CONFIG 0xf15f007e
++#define F0900_DATA71_XOR 0xf15f0001
++
++/*ERROR2CFG*/
++#define R0900_ERROR2CFG 0xf160
++#define F0900_ERROR2_OPD 0xf1600080
++#define F0900_ERROR2_CONFIG 0xf160007e
++#define F0900_ERROR2_XOR 0xf1600001
++
++/*DPN2CFG*/
++#define R0900_DPN2CFG 0xf161
++#define F0900_DPN2_OPD 0xf1610080
++#define F0900_DPN2_CONFIG 0xf161007e
++#define F0900_DPN2_XOR 0xf1610001
++
++/*STROUT2CFG*/
++#define R0900_STROUT2CFG 0xf162
++#define F0900_STROUT2_OPD 0xf1620080
++#define F0900_STROUT2_CONFIG 0xf162007e
++#define F0900_STROUT2_XOR 0xf1620001
++
++/*CLKOUT2CFG*/
++#define R0900_CLKOUT2CFG 0xf163
++#define F0900_CLKOUT2_OPD 0xf1630080
++#define F0900_CLKOUT2_CONFIG 0xf163007e
++#define F0900_CLKOUT2_XOR 0xf1630001
++
++/*DATA72CFG*/
++#define R0900_DATA72CFG 0xf164
++#define F0900_DATA72_OPD 0xf1640080
++#define F0900_DATA72_CONFIG 0xf164007e
++#define F0900_DATA72_XOR 0xf1640001
++
++/*ERROR3CFG*/
++#define R0900_ERROR3CFG 0xf165
++#define F0900_ERROR3_OPD 0xf1650080
++#define F0900_ERROR3_CONFIG 0xf165007e
++#define F0900_ERROR3_XOR 0xf1650001
++
++/*DPN3CFG*/
++#define R0900_DPN3CFG 0xf166
++#define F0900_DPN3_OPD 0xf1660080
++#define F0900_DPN3_CONFIG 0xf166007e
++#define F0900_DPN3_XOR 0xf1660001
++
++/*STROUT3CFG*/
++#define R0900_STROUT3CFG 0xf167
++#define F0900_STROUT3_OPD 0xf1670080
++#define F0900_STROUT3_CONFIG 0xf167007e
++#define F0900_STROUT3_XOR 0xf1670001
++
++/*CLKOUT3CFG*/
++#define R0900_CLKOUT3CFG 0xf168
++#define F0900_CLKOUT3_OPD 0xf1680080
++#define F0900_CLKOUT3_CONFIG 0xf168007e
++#define F0900_CLKOUT3_XOR 0xf1680001
++
++/*DATA73CFG*/
++#define R0900_DATA73CFG 0xf169
++#define F0900_DATA73_OPD 0xf1690080
++#define F0900_DATA73_CONFIG 0xf169007e
++#define F0900_DATA73_XOR 0xf1690001
++
++/*FSKTFC2*/
++#define R0900_FSKTFC2 0xf170
++#define F0900_FSKT_KMOD 0xf17000fc
++#define F0900_FSKT_CAR2 0xf1700003
++
++/*FSKTFC1*/
++#define R0900_FSKTFC1 0xf171
++#define F0900_FSKT_CAR1 0xf17100ff
++
++/*FSKTFC0*/
++#define R0900_FSKTFC0 0xf172
++#define F0900_FSKT_CAR0 0xf17200ff
++
++/*FSKTDELTAF1*/
++#define R0900_FSKTDELTAF1 0xf173
++#define F0900_FSKT_DELTAF1 0xf173000f
++
++/*FSKTDELTAF0*/
++#define R0900_FSKTDELTAF0 0xf174
++#define F0900_FSKT_DELTAF0 0xf17400ff
++
++/*FSKTCTRL*/
++#define R0900_FSKTCTRL 0xf175
++#define F0900_FSKT_EN_SGN 0xf1750040
++#define F0900_FSKT_MOD_SGN 0xf1750020
++#define F0900_FSKT_MOD_EN 0xf175001c
++#define F0900_FSKT_DACMODE 0xf1750003
++
++/*FSKRFC2*/
++#define R0900_FSKRFC2 0xf176
++#define F0900_FSKR_DETSGN 0xf1760040
++#define F0900_FSKR_OUTSGN 0xf1760020
++#define F0900_FSKR_KAGC 0xf176001c
++#define F0900_FSKR_CAR2 0xf1760003
++
++/*FSKRFC1*/
++#define R0900_FSKRFC1 0xf177
++#define F0900_FSKR_CAR1 0xf17700ff
++
++/*FSKRFC0*/
++#define R0900_FSKRFC0 0xf178
++#define F0900_FSKR_CAR0 0xf17800ff
++
++/*FSKRK1*/
++#define R0900_FSKRK1 0xf179
++#define F0900_FSKR_K1_EXP 0xf17900e0
++#define F0900_FSKR_K1_MANT 0xf179001f
++
++/*FSKRK2*/
++#define R0900_FSKRK2 0xf17a
++#define F0900_FSKR_K2_EXP 0xf17a00e0
++#define F0900_FSKR_K2_MANT 0xf17a001f
++
++/*FSKRAGCR*/
++#define R0900_FSKRAGCR 0xf17b
++#define F0900_FSKR_OUTCTL 0xf17b00c0
++#define F0900_FSKR_AGC_REF 0xf17b003f
++
++/*FSKRAGC*/
++#define R0900_FSKRAGC 0xf17c
++#define F0900_FSKR_AGC_ACCU 0xf17c00ff
++
++/*FSKRALPHA*/
++#define R0900_FSKRALPHA 0xf17d
++#define F0900_FSKR_ALPHA_EXP 0xf17d001c
++#define F0900_FSKR_ALPHA_M 0xf17d0003
++
++/*FSKRPLTH1*/
++#define R0900_FSKRPLTH1 0xf17e
++#define F0900_FSKR_BETA 0xf17e00f0
++#define F0900_FSKR_PLL_TRESH1 0xf17e000f
++
++/*FSKRPLTH0*/
++#define R0900_FSKRPLTH0 0xf17f
++#define F0900_FSKR_PLL_TRESH0 0xf17f00ff
++
++/*FSKRDF1*/
++#define R0900_FSKRDF1 0xf180
++#define F0900_FSKR_OUT 0xf1800080
++#define F0900_FSKR_DELTAF1 0xf180001f
++
++/*FSKRDF0*/
++#define R0900_FSKRDF0 0xf181
++#define F0900_FSKR_DELTAF0 0xf18100ff
++
++/*FSKRSTEPP*/
++#define R0900_FSKRSTEPP 0xf182
++#define F0900_FSKR_STEP_PLUS 0xf18200ff
++
++/*FSKRSTEPM*/
++#define R0900_FSKRSTEPM 0xf183
++#define F0900_FSKR_STEP_MINUS 0xf18300ff
++
++/*FSKRDET1*/
++#define R0900_FSKRDET1 0xf184
++#define F0900_FSKR_DETECT 0xf1840080
++#define F0900_FSKR_CARDET_ACCU1 0xf184000f
++
++/*FSKRDET0*/
++#define R0900_FSKRDET0 0xf185
++#define F0900_FSKR_CARDET_ACCU0 0xf18500ff
++
++/*FSKRDTH1*/
++#define R0900_FSKRDTH1 0xf186
++#define F0900_FSKR_CARLOSS_THRESH1 0xf18600f0
++#define F0900_FSKR_CARDET_THRESH1 0xf186000f
++
++/*FSKRDTH0*/
++#define R0900_FSKRDTH0 0xf187
++#define F0900_FSKR_CARDET_THRESH0 0xf18700ff
++
++/*FSKRLOSS*/
++#define R0900_FSKRLOSS 0xf188
++#define F0900_FSKR_CARLOSS_THRESH0 0xf18800ff
++
++/*P2_DISTXCTL*/
++#define R0900_P2_DISTXCTL 0xf190
++#define F0900_P2_TIM_OFF 0xf1900080
++#define F0900_P2_DISEQC_RESET 0xf1900040
++#define F0900_P2_TIM_CMD 0xf1900030
++#define F0900_P2_DIS_PRECHARGE 0xf1900008
++#define F0900_P2_DISTX_MODE 0xf1900007
++
++/*P2_DISRXCTL*/
++#define R0900_P2_DISRXCTL 0xf191
++#define F0900_P2_RECEIVER_ON 0xf1910080
++#define F0900_P2_IGNO_SHORT22K 0xf1910040
++#define F0900_P2_ONECHIP_TRX 0xf1910020
++#define F0900_P2_EXT_ENVELOP 0xf1910010
++#define F0900_P2_PIN_SELECT 0xf191000c
++#define F0900_P2_IRQ_RXEND 0xf1910002
++#define F0900_P2_IRQ_4NBYTES 0xf1910001
++
++/*P2_DISRX_ST0*/
++#define R0900_P2_DISRX_ST0 0xf194
++#define F0900_P2_RX_END 0xf1940080
++#define F0900_P2_RX_ACTIVE 0xf1940040
++#define F0900_P2_SHORT_22KHZ 0xf1940020
++#define F0900_P2_CONT_TONE 0xf1940010
++#define F0900_P2_FIFO_4BREADY 0xf1940008
++#define F0900_P2_FIFO_EMPTY 0xf1940004
++#define F0900_P2_ABORT_DISRX 0xf1940001
++
++/*P2_DISRX_ST1*/
++#define R0900_P2_DISRX_ST1 0xf195
++#define F0900_P2_RX_FAIL 0xf1950080
++#define F0900_P2_FIFO_PARITYFAIL 0xf1950040
++#define F0900_P2_RX_NONBYTE 0xf1950020
++#define F0900_P2_FIFO_OVERFLOW 0xf1950010
++#define F0900_P2_FIFO_BYTENBR 0xf195000f
++
++/*P2_DISRXDATA*/
++#define R0900_P2_DISRXDATA 0xf196
++#define F0900_P2_DISRX_DATA 0xf19600ff
++
++/*P2_DISTXDATA*/
++#define R0900_P2_DISTXDATA 0xf197
++#define F0900_P2_DISEQC_FIFO 0xf19700ff
++
++/*P2_DISTXSTATUS*/
++#define R0900_P2_DISTXSTATUS 0xf198
++#define F0900_P2_TX_FAIL 0xf1980080
++#define F0900_P2_FIFO_FULL 0xf1980040
++#define F0900_P2_TX_IDLE 0xf1980020
++#define F0900_P2_GAP_BURST 0xf1980010
++#define F0900_P2_TXFIFO_BYTES 0xf198000f
++
++/*P2_F22TX*/
++#define R0900_P2_F22TX 0xf199
++#define F0900_P2_F22_REG 0xf19900ff
++
++/*P2_F22RX*/
++#define R0900_P2_F22RX 0xf19a
++#define F0900_P2_F22RX_REG 0xf19a00ff
++
++/*P2_ACRPRESC*/
++#define R0900_P2_ACRPRESC 0xf19c
++#define F0900_P2_ACR_CODFRDY 0xf19c0008
++#define F0900_P2_ACR_PRESC 0xf19c0007
++
++/*P2_ACRDIV*/
++#define R0900_P2_ACRDIV 0xf19d
++#define F0900_P2_ACR_DIV 0xf19d00ff
++
++/*P1_DISTXCTL*/
++#define R0900_P1_DISTXCTL 0xf1a0
++#define F0900_P1_TIM_OFF 0xf1a00080
++#define F0900_P1_DISEQC_RESET 0xf1a00040
++#define F0900_P1_TIM_CMD 0xf1a00030
++#define F0900_P1_DIS_PRECHARGE 0xf1a00008
++#define F0900_P1_DISTX_MODE 0xf1a00007
++
++/*P1_DISRXCTL*/
++#define R0900_P1_DISRXCTL 0xf1a1
++#define F0900_P1_RECEIVER_ON 0xf1a10080
++#define F0900_P1_IGNO_SHORT22K 0xf1a10040
++#define F0900_P1_ONECHIP_TRX 0xf1a10020
++#define F0900_P1_EXT_ENVELOP 0xf1a10010
++#define F0900_P1_PIN_SELECT 0xf1a1000c
++#define F0900_P1_IRQ_RXEND 0xf1a10002
++#define F0900_P1_IRQ_4NBYTES 0xf1a10001
++
++/*P1_DISRX_ST0*/
++#define R0900_P1_DISRX_ST0 0xf1a4
++#define F0900_P1_RX_END 0xf1a40080
++#define F0900_P1_RX_ACTIVE 0xf1a40040
++#define F0900_P1_SHORT_22KHZ 0xf1a40020
++#define F0900_P1_CONT_TONE 0xf1a40010
++#define F0900_P1_FIFO_4BREADY 0xf1a40008
++#define F0900_P1_FIFO_EMPTY 0xf1a40004
++#define F0900_P1_ABORT_DISRX 0xf1a40001
++
++/*P1_DISRX_ST1*/
++#define R0900_P1_DISRX_ST1 0xf1a5
++#define F0900_P1_RX_FAIL 0xf1a50080
++#define F0900_P1_FIFO_PARITYFAIL 0xf1a50040
++#define F0900_P1_RX_NONBYTE 0xf1a50020
++#define F0900_P1_FIFO_OVERFLOW 0xf1a50010
++#define F0900_P1_FIFO_BYTENBR 0xf1a5000f
++
++/*P1_DISRXDATA*/
++#define R0900_P1_DISRXDATA 0xf1a6
++#define F0900_P1_DISRX_DATA 0xf1a600ff
++
++/*P1_DISTXDATA*/
++#define R0900_P1_DISTXDATA 0xf1a7
++#define F0900_P1_DISEQC_FIFO 0xf1a700ff
++
++/*P1_DISTXSTATUS*/
++#define R0900_P1_DISTXSTATUS 0xf1a8
++#define F0900_P1_TX_FAIL 0xf1a80080
++#define F0900_P1_FIFO_FULL 0xf1a80040
++#define F0900_P1_TX_IDLE 0xf1a80020
++#define F0900_P1_GAP_BURST 0xf1a80010
++#define F0900_P1_TXFIFO_BYTES 0xf1a8000f
++
++/*P1_F22TX*/
++#define R0900_P1_F22TX 0xf1a9
++#define F0900_P1_F22_REG 0xf1a900ff
++
++/*P1_F22RX*/
++#define R0900_P1_F22RX 0xf1aa
++#define F0900_P1_F22RX_REG 0xf1aa00ff
++
++/*P1_ACRPRESC*/
++#define R0900_P1_ACRPRESC 0xf1ac
++#define F0900_P1_ACR_CODFRDY 0xf1ac0008
++#define F0900_P1_ACR_PRESC 0xf1ac0007
++
++/*P1_ACRDIV*/
++#define R0900_P1_ACRDIV 0xf1ad
++#define F0900_P1_ACR_DIV 0xf1ad00ff
++
++/*NCOARSE*/
++#define R0900_NCOARSE 0xf1b3
++#define F0900_M_DIV 0xf1b300ff
++
++/*SYNTCTRL*/
++#define R0900_SYNTCTRL 0xf1b6
++#define F0900_STANDBY 0xf1b60080
++#define F0900_BYPASSPLLCORE 0xf1b60040
++#define F0900_SELX1RATIO 0xf1b60020
++#define F0900_I2C_TUD 0xf1b60010
++#define F0900_STOP_PLL 0xf1b60008
++#define F0900_BYPASSPLLFSK 0xf1b60004
++#define F0900_SELOSCI 0xf1b60002
++#define F0900_BYPASSPLLADC 0xf1b60001
++
++/*FILTCTRL*/
++#define R0900_FILTCTRL 0xf1b7
++#define F0900_INV_CLK135 0xf1b70080
++#define F0900_PERM_BYPDIS 0xf1b70040
++#define F0900_SEL_FSKCKDIV 0xf1b70004
++#define F0900_INV_CLKFSK 0xf1b70002
++#define F0900_BYPASS_APPLI 0xf1b70001
++
++/*PLLSTAT*/
++#define R0900_PLLSTAT 0xf1b8
++#define F0900_ACM_SEL 0xf1b80080
++#define F0900_DTV_SEL 0xf1b80040
++#define F0900_PLLLOCK 0xf1b80001
++
++/*STOPCLK1*/
++#define R0900_STOPCLK1 0xf1c2
++#define F0900_STOP_CLKPKDT2 0xf1c20040
++#define F0900_STOP_CLKPKDT1 0xf1c20020
++#define F0900_STOP_CLKFEC 0xf1c20010
++#define F0900_STOP_CLKADCI2 0xf1c20008
++#define F0900_INV_CLKADCI2 0xf1c20004
++#define F0900_STOP_CLKADCI1 0xf1c20002
++#define F0900_INV_CLKADCI1 0xf1c20001
++
++/*STOPCLK2*/
++#define R0900_STOPCLK2 0xf1c3
++#define F0900_STOP_CLKSAMP2 0xf1c30010
++#define F0900_STOP_CLKSAMP1 0xf1c30008
++#define F0900_STOP_CLKVIT2 0xf1c30004
++#define F0900_STOP_CLKVIT1 0xf1c30002
++#define F0900_STOP_CLKTS 0xf1c30001
++
++/*TSTTNR0*/
++#define R0900_TSTTNR0 0xf1df
++#define F0900_SEL_FSK 0xf1df0080
++#define F0900_FSK_PON 0xf1df0004
++#define F0900_FSK_OPENLOOP 0xf1df0002
++
++/*TSTTNR1*/
++#define R0900_TSTTNR1 0xf1e0
++#define F0900_BYPASS_ADC1 0xf1e00080
++#define F0900_INVADC1_CKOUT 0xf1e00040
++#define F0900_SELIQSRC1 0xf1e00030
++#define F0900_ADC1_PON 0xf1e00002
++#define F0900_ADC1_INMODE 0xf1e00001
++
++/*TSTTNR2*/
++#define R0900_TSTTNR2 0xf1e1
++#define F0900_DISEQC1_PON 0xf1e10020
++#define F0900_DISEQC1_TEST 0xf1e1001f
++
++/*TSTTNR3*/
++#define R0900_TSTTNR3 0xf1e2
++#define F0900_BYPASS_ADC2 0xf1e20080
++#define F0900_INVADC2_CKOUT 0xf1e20040
++#define F0900_SELIQSRC2 0xf1e20030
++#define F0900_ADC2_PON 0xf1e20002
++#define F0900_ADC2_INMODE 0xf1e20001
++
++/*TSTTNR4*/
++#define R0900_TSTTNR4 0xf1e3
++#define F0900_DISEQC2_PON 0xf1e30020
++#define F0900_DISEQC2_TEST 0xf1e3001f
++
++/*P2_IQCONST*/
++#define R0900_P2_IQCONST 0xf200
++#define F0900_P2_CONSTEL_SELECT 0xf2000060
++#define F0900_P2_IQSYMB_SEL 0xf200001f
++
++/*P2_NOSCFG*/
++#define R0900_P2_NOSCFG 0xf201
++#define F0900_P2_DUMMYPL_NOSDATA 0xf2010020
++#define F0900_P2_NOSPLH_BETA 0xf2010018
++#define F0900_P2_NOSDATA_BETA 0xf2010007
++
++/*P2_ISYMB*/
++#define R0900_P2_ISYMB 0xf202
++#define F0900_P2_I_SYMBOL 0xf20201ff
++
++/*P2_QSYMB*/
++#define R0900_P2_QSYMB 0xf203
++#define F0900_P2_Q_SYMBOL 0xf20301ff
++
++/*P2_AGC1CFG*/
++#define R0900_P2_AGC1CFG 0xf204
++#define F0900_P2_DC_FROZEN 0xf2040080
++#define F0900_P2_DC_CORRECT 0xf2040040
++#define F0900_P2_AMM_FROZEN 0xf2040020
++#define F0900_P2_AMM_CORRECT 0xf2040010
++#define F0900_P2_QUAD_FROZEN 0xf2040008
++#define F0900_P2_QUAD_CORRECT 0xf2040004
++#define F0900_P2_DCCOMP_SLOW 0xf2040002
++#define F0900_P2_IQMISM_SLOW 0xf2040001
++
++/*P2_AGC1CN*/
++#define R0900_P2_AGC1CN 0xf206
++#define F0900_P2_AGC1_LOCKED 0xf2060080
++#define F0900_P2_AGC1_OVERFLOW 0xf2060040
++#define F0900_P2_AGC1_NOSLOWLK 0xf2060020
++#define F0900_P2_AGC1_MINPOWER 0xf2060010
++#define F0900_P2_AGCOUT_FAST 0xf2060008
++#define F0900_P2_AGCIQ_BETA 0xf2060007
++
++/*P2_AGC1REF*/
++#define R0900_P2_AGC1REF 0xf207
++#define F0900_P2_AGCIQ_REF 0xf20700ff
++
++/*P2_IDCCOMP*/
++#define R0900_P2_IDCCOMP 0xf208
++#define F0900_P2_IAVERAGE_ADJ 0xf20801ff
++
++/*P2_QDCCOMP*/
++#define R0900_P2_QDCCOMP 0xf209
++#define F0900_P2_QAVERAGE_ADJ 0xf20901ff
++
++/*P2_POWERI*/
++#define R0900_P2_POWERI 0xf20a
++#define F0900_P2_POWER_I 0xf20a00ff
++
++/*P2_POWERQ*/
++#define R0900_P2_POWERQ 0xf20b
++#define F0900_P2_POWER_Q 0xf20b00ff
++
++/*P2_AGC1AMM*/
++#define R0900_P2_AGC1AMM 0xf20c
++#define F0900_P2_AMM_VALUE 0xf20c00ff
++
++/*P2_AGC1QUAD*/
++#define R0900_P2_AGC1QUAD 0xf20d
++#define F0900_P2_QUAD_VALUE 0xf20d01ff
++
++/*P2_AGCIQIN1*/
++#define R0900_P2_AGCIQIN1 0xf20e
++#define F0900_P2_AGCIQ_VALUE1 0xf20e00ff
++
++/*P2_AGCIQIN0*/
++#define R0900_P2_AGCIQIN0 0xf20f
++#define F0900_P2_AGCIQ_VALUE0 0xf20f00ff
++
++/*P2_DEMOD*/
++#define R0900_P2_DEMOD 0xf210
++#define F0900_P2_DEMOD_STOP 0xf2100040
++#define F0900_P2_SPECINV_CONTROL 0xf2100030
++#define F0900_P2_FORCE_ENASAMP 0xf2100008
++#define F0900_P2_MANUAL_ROLLOFF 0xf2100004
++#define F0900_P2_ROLLOFF_CONTROL 0xf2100003
++
++/*P2_DMDMODCOD*/
++#define R0900_P2_DMDMODCOD 0xf211
++#define F0900_P2_MANUAL_MODCOD 0xf2110080
++#define F0900_P2_DEMOD_MODCOD 0xf211007c
++#define F0900_P2_DEMOD_TYPE 0xf2110003
++
++/*P2_DSTATUS*/
++#define R0900_P2_DSTATUS 0xf212
++#define F0900_P2_CAR_LOCK 0xf2120080
++#define F0900_P2_TMGLOCK_QUALITY 0xf2120060
++#define F0900_P2_SDVBS1_ENABLE 0xf2120010
++#define F0900_P2_LOCK_DEFINITIF 0xf2120008
++#define F0900_P2_TIMING_IS_LOCKED 0xf2120004
++#define F0900_P2_COARSE_TMGLOCK 0xf2120002
++#define F0900_P2_COARSE_CARLOCK 0xf2120001
++
++/*P2_DSTATUS2*/
++#define R0900_P2_DSTATUS2 0xf213
++#define F0900_P2_DEMOD_DELOCK 0xf2130080
++#define F0900_P2_DEMOD_TIMEOUT 0xf2130040
++#define F0900_P2_MODCODRQ_SYNCTAG 0xf2130020
++#define F0900_P2_POLYPH_SATEVENT 0xf2130010
++#define F0900_P2_AGC1_NOSIGNALACK 0xf2130008
++#define F0900_P2_AGC2_OVERFLOW 0xf2130004
++#define F0900_P2_CFR_OVERFLOW 0xf2130002
++#define F0900_P2_GAMMA_OVERUNDER 0xf2130001
++
++/*P2_DMDCFGMD*/
++#define R0900_P2_DMDCFGMD 0xf214
++#define F0900_P2_DVBS2_ENABLE 0xf2140080
++#define F0900_P2_DVBS1_ENABLE 0xf2140040
++#define F0900_P2_CFR_AUTOSCAN 0xf2140020
++#define F0900_P2_SCAN_ENABLE 0xf2140010
++#define F0900_P2_TUN_AUTOSCAN 0xf2140008
++#define F0900_P2_NOFORCE_RELOCK 0xf2140004
++#define F0900_P2_TUN_RNG 0xf2140003
++
++/*P2_DMDCFG2*/
++#define R0900_P2_DMDCFG2 0xf215
++#define F0900_P2_AGC1_WAITLOCK 0xf2150080
++#define F0900_P2_S1S2_SEQUENTIAL 0xf2150040
++#define F0900_P2_OVERFLOW_TIMEOUT 0xf2150020
++#define F0900_P2_SCANFAIL_TIMEOUT 0xf2150010
++#define F0900_P2_DMDTOUT_BACK 0xf2150008
++#define F0900_P2_CARLOCK_S1ENABLE 0xf2150004
++#define F0900_P2_COARSE_LK3MODE 0xf2150002
++#define F0900_P2_COARSE_LK2MODE 0xf2150001
++
++/*P2_DMDISTATE*/
++#define R0900_P2_DMDISTATE 0xf216
++#define F0900_P2_I2C_NORESETDMODE 0xf2160080
++#define F0900_P2_FORCE_ETAPED 0xf2160040
++#define F0900_P2_SDMDRST_DIRCLK 0xf2160020
++#define F0900_P2_I2C_DEMOD_MODE 0xf216001f
++
++/*P2_DMDT0M*/
++#define R0900_P2_DMDT0M 0xf217
++#define F0900_P2_DMDT0_MIN 0xf21700ff
++
++/*P2_DMDSTATE*/
++#define R0900_P2_DMDSTATE 0xf21b
++#define F0900_P2_DEMOD_LOCKED 0xf21b0080
++#define F0900_P2_HEADER_MODE 0xf21b0060
++#define F0900_P2_DEMOD_MODE 0xf21b001f
++
++/*P2_DMDFLYW*/
++#define R0900_P2_DMDFLYW 0xf21c
++#define F0900_P2_I2C_IRQVAL 0xf21c00f0
++#define F0900_P2_FLYWHEEL_CPT 0xf21c000f
++
++/*P2_DSTATUS3*/
++#define R0900_P2_DSTATUS3 0xf21d
++#define F0900_P2_CFR_ZIGZAG 0xf21d0080
++#define F0900_P2_DEMOD_CFGMODE 0xf21d0060
++#define F0900_P2_GAMMA_LOWBAUDRATE 0xf21d0010
++#define F0900_P2_RELOCK_MODE 0xf21d0008
++#define F0900_P2_DEMOD_FAIL 0xf21d0004
++#define F0900_P2_ETAPE1A_DVBXMEM 0xf21d0003
++
++/*P2_DMDCFG3*/
++#define R0900_P2_DMDCFG3 0xf21e
++#define F0900_P2_DVBS1_TMGWAIT 0xf21e0080
++#define F0900_P2_NO_BWCENTERING 0xf21e0040
++#define F0900_P2_INV_SEQSRCH 0xf21e0020
++#define F0900_P2_DIS_SFRUPLOW_TRK 0xf21e0010
++#define F0900_P2_NOSTOP_FIFOFULL 0xf21e0008
++#define F0900_P2_LOCKTIME_MODE 0xf21e0007
++
++/*P2_DMDCFG4*/
++#define R0900_P2_DMDCFG4 0xf21f
++#define F0900_P2_TUNER_NRELAUNCH 0xf21f0008
++#define F0900_P2_DIS_CLKENABLE 0xf21f0004
++#define F0900_P2_DIS_HDRDIVLOCK 0xf21f0002
++#define F0900_P2_NO_TNRWBINIT 0xf21f0001
++
++/*P2_CORRELMANT*/
++#define R0900_P2_CORRELMANT 0xf220
++#define F0900_P2_CORREL_MANT 0xf22000ff
++
++/*P2_CORRELABS*/
++#define R0900_P2_CORRELABS 0xf221
++#define F0900_P2_CORREL_ABS 0xf22100ff
++
++/*P2_CORRELEXP*/
++#define R0900_P2_CORRELEXP 0xf222
++#define F0900_P2_CORREL_ABSEXP 0xf22200f0
++#define F0900_P2_CORREL_EXP 0xf222000f
++
++/*P2_PLHMODCOD*/
++#define R0900_P2_PLHMODCOD 0xf224
++#define F0900_P2_SPECINV_DEMOD 0xf2240080
++#define F0900_P2_PLH_MODCOD 0xf224007c
++#define F0900_P2_PLH_TYPE 0xf2240003
++
++/*P2_AGCK32*/
++#define R0900_P2_AGCK32 0xf22b
++#define F0900_P2_R3ADJOFF_32APSK 0xf22b0080
++#define F0900_P2_R2ADJOFF_32APSK 0xf22b0040
++#define F0900_P2_R1ADJOFF_32APSK 0xf22b0020
++#define F0900_P2_RADJ_32APSK 0xf22b001f
++
++/*P2_AGC2O*/
++#define R0900_P2_AGC2O 0xf22c
++#define F0900_P2_AGC2REF_ADJUSTING 0xf22c0080
++#define F0900_P2_AGC2_COARSEFAST 0xf22c0040
++#define F0900_P2_AGC2_LKSQRT 0xf22c0020
++#define F0900_P2_AGC2_LKMODE 0xf22c0010
++#define F0900_P2_AGC2_LKEQUA 0xf22c0008
++#define F0900_P2_AGC2_COEF 0xf22c0007
++
++/*P2_AGC2REF*/
++#define R0900_P2_AGC2REF 0xf22d
++#define F0900_P2_AGC2_REF 0xf22d00ff
++
++/*P2_AGC1ADJ*/
++#define R0900_P2_AGC1ADJ 0xf22e
++#define F0900_P2_AGC1ADJ_MANUAL 0xf22e0080
++#define F0900_P2_AGC1_ADJUSTED 0xf22e017f
++
++/*P2_AGC2I1*/
++#define R0900_P2_AGC2I1 0xf236
++#define F0900_P2_AGC2_INTEGRATOR1 0xf23600ff
++
++/*P2_AGC2I0*/
++#define R0900_P2_AGC2I0 0xf237
++#define F0900_P2_AGC2_INTEGRATOR0 0xf23700ff
++
++/*P2_CARCFG*/
++#define R0900_P2_CARCFG 0xf238
++#define F0900_P2_CFRUPLOW_AUTO 0xf2380080
++#define F0900_P2_CFRUPLOW_TEST 0xf2380040
++#define F0900_P2_EN_CAR2CENTER 0xf2380020
++#define F0900_P2_CARHDR_NODIV8 0xf2380010
++#define F0900_P2_I2C_ROTA 0xf2380008
++#define F0900_P2_ROTAON 0xf2380004
++#define F0900_P2_PH_DET_ALGO 0xf2380003
++
++/*P2_ACLC*/
++#define R0900_P2_ACLC 0xf239
++#define F0900_P2_STOP_S2ALPHA 0xf23900c0
++#define F0900_P2_CAR_ALPHA_MANT 0xf2390030
++#define F0900_P2_CAR_ALPHA_EXP 0xf239000f
++
++/*P2_BCLC*/
++#define R0900_P2_BCLC 0xf23a
++#define F0900_P2_STOP_S2BETA 0xf23a00c0
++#define F0900_P2_CAR_BETA_MANT 0xf23a0030
++#define F0900_P2_CAR_BETA_EXP 0xf23a000f
++
++/*P2_CARFREQ*/
++#define R0900_P2_CARFREQ 0xf23d
++#define F0900_P2_KC_COARSE_EXP 0xf23d00f0
++#define F0900_P2_BETA_FREQ 0xf23d000f
++
++/*P2_CARHDR*/
++#define R0900_P2_CARHDR 0xf23e
++#define F0900_P2_K_FREQ_HDR 0xf23e00ff
++
++/*P2_LDT*/
++#define R0900_P2_LDT 0xf23f
++#define F0900_P2_CARLOCK_THRES 0xf23f01ff
++
++/*P2_LDT2*/
++#define R0900_P2_LDT2 0xf240
++#define F0900_P2_CARLOCK_THRES2 0xf24001ff
++
++/*P2_CFRICFG*/
++#define R0900_P2_CFRICFG 0xf241
++#define F0900_P2_CFRINIT_UNVALRNG 0xf2410080
++#define F0900_P2_CFRINIT_LUNVALCPT 0xf2410040
++#define F0900_P2_CFRINIT_ABORTDBL 0xf2410020
++#define F0900_P2_CFRINIT_ABORTPRED 0xf2410010
++#define F0900_P2_CFRINIT_UNVALSKIP 0xf2410008
++#define F0900_P2_CFRINIT_CSTINC 0xf2410004
++#define F0900_P2_NEG_CFRSTEP 0xf2410001
++
++/*P2_CFRUP1*/
++#define R0900_P2_CFRUP1 0xf242
++#define F0900_P2_CFR_UP1 0xf24201ff
++
++/*P2_CFRUP0*/
++#define R0900_P2_CFRUP0 0xf243
++#define F0900_P2_CFR_UP0 0xf24300ff
++
++/*P2_CFRLOW1*/
++#define R0900_P2_CFRLOW1 0xf246
++#define F0900_P2_CFR_LOW1 0xf24601ff
++
++/*P2_CFRLOW0*/
++#define R0900_P2_CFRLOW0 0xf247
++#define F0900_P2_CFR_LOW0 0xf24700ff
++
++/*P2_CFRINIT1*/
++#define R0900_P2_CFRINIT1 0xf248
++#define F0900_P2_CFR_INIT1 0xf24801ff
++
++/*P2_CFRINIT0*/
++#define R0900_P2_CFRINIT0 0xf249
++#define F0900_P2_CFR_INIT0 0xf24900ff
++
++/*P2_CFRINC1*/
++#define R0900_P2_CFRINC1 0xf24a
++#define F0900_P2_MANUAL_CFRINC 0xf24a0080
++#define F0900_P2_CFR_INC1 0xf24a017f
++
++/*P2_CFRINC0*/
++#define R0900_P2_CFRINC0 0xf24b
++#define F0900_P2_CFR_INC0 0xf24b00f0
++
++/*P2_CFR2*/
++#define R0900_P2_CFR2 0xf24c
++#define F0900_P2_CAR_FREQ2 0xf24c01ff
++
++/*P2_CFR1*/
++#define R0900_P2_CFR1 0xf24d
++#define F0900_P2_CAR_FREQ1 0xf24d00ff
++
++/*P2_CFR0*/
++#define R0900_P2_CFR0 0xf24e
++#define F0900_P2_CAR_FREQ0 0xf24e00ff
++
++/*P2_LDI*/
++#define R0900_P2_LDI 0xf24f
++#define F0900_P2_LOCK_DET_INTEGR 0xf24f01ff
++
++/*P2_TMGCFG*/
++#define R0900_P2_TMGCFG 0xf250
++#define F0900_P2_TMGLOCK_BETA 0xf25000c0
++#define F0900_P2_NOTMG_GROUPDELAY 0xf2500020
++#define F0900_P2_DO_TIMING_CORR 0xf2500010
++#define F0900_P2_MANUAL_SCAN 0xf250000c
++#define F0900_P2_TMG_MINFREQ 0xf2500003
++
++/*P2_RTC*/
++#define R0900_P2_RTC 0xf251
++#define F0900_P2_TMGALPHA_EXP 0xf25100f0
++#define F0900_P2_TMGBETA_EXP 0xf251000f
++
++/*P2_RTCS2*/
++#define R0900_P2_RTCS2 0xf252
++#define F0900_P2_TMGALPHAS2_EXP 0xf25200f0
++#define F0900_P2_TMGBETAS2_EXP 0xf252000f
++
++/*P2_TMGTHRISE*/
++#define R0900_P2_TMGTHRISE 0xf253
++#define F0900_P2_TMGLOCK_THRISE 0xf25300ff
++
++/*P2_TMGTHFALL*/
++#define R0900_P2_TMGTHFALL 0xf254
++#define F0900_P2_TMGLOCK_THFALL 0xf25400ff
++
++/*P2_SFRUPRATIO*/
++#define R0900_P2_SFRUPRATIO 0xf255
++#define F0900_P2_SFR_UPRATIO 0xf25500ff
++
++/*P2_SFRLOWRATIO*/
++#define R0900_P2_SFRLOWRATIO 0xf256
++#define F0900_P2_SFR_LOWRATIO 0xf25600ff
++
++/*P2_KREFTMG*/
++#define R0900_P2_KREFTMG 0xf258
++#define F0900_P2_KREF_TMG 0xf25800ff
++
++/*P2_SFRSTEP*/
++#define R0900_P2_SFRSTEP 0xf259
++#define F0900_P2_SFR_SCANSTEP 0xf25900f0
++#define F0900_P2_SFR_CENTERSTEP 0xf259000f
++
++/*P2_TMGCFG2*/
++#define R0900_P2_TMGCFG2 0xf25a
++#define F0900_P2_DIS_AUTOSAMP 0xf25a0008
++#define F0900_P2_SCANINIT_QUART 0xf25a0004
++#define F0900_P2_NOTMG_DVBS1DERAT 0xf25a0002
++#define F0900_P2_SFRRATIO_FINE 0xf25a0001
++
++/*P2_SFRINIT1*/
++#define R0900_P2_SFRINIT1 0xf25e
++#define F0900_P2_SFR_INIT1 0xf25e00ff
++
++/*P2_SFRINIT0*/
++#define R0900_P2_SFRINIT0 0xf25f
++#define F0900_P2_SFR_INIT0 0xf25f00ff
++
++/*P2_SFRUP1*/
++#define R0900_P2_SFRUP1 0xf260
++#define F0900_P2_AUTO_GUP 0xf2600080
++#define F0900_P2_SYMB_FREQ_UP1 0xf260007f
++
++/*P2_SFRUP0*/
++#define R0900_P2_SFRUP0 0xf261
++#define F0900_P2_SYMB_FREQ_UP0 0xf26100ff
++
++/*P2_SFRLOW1*/
++#define R0900_P2_SFRLOW1 0xf262
++#define F0900_P2_AUTO_GLOW 0xf2620080
++#define F0900_P2_SYMB_FREQ_LOW1 0xf262007f
++
++/*P2_SFRLOW0*/
++#define R0900_P2_SFRLOW0 0xf263
++#define F0900_P2_SYMB_FREQ_LOW0 0xf26300ff
++
++/*P2_SFR3*/
++#define R0900_P2_SFR3 0xf264
++#define F0900_P2_SYMB_FREQ3 0xf26400ff
++
++/*P2_SFR2*/
++#define R0900_P2_SFR2 0xf265
++#define F0900_P2_SYMB_FREQ2 0xf26500ff
++
++/*P2_SFR1*/
++#define R0900_P2_SFR1 0xf266
++#define F0900_P2_SYMB_FREQ1 0xf26600ff
++
++/*P2_SFR0*/
++#define R0900_P2_SFR0 0xf267
++#define F0900_P2_SYMB_FREQ0 0xf26700ff
++
++/*P2_TMGREG2*/
++#define R0900_P2_TMGREG2 0xf268
++#define F0900_P2_TMGREG2 0xf26800ff
++
++/*P2_TMGREG1*/
++#define R0900_P2_TMGREG1 0xf269
++#define F0900_P2_TMGREG1 0xf26900ff
++
++/*P2_TMGREG0*/
++#define R0900_P2_TMGREG0 0xf26a
++#define F0900_P2_TMGREG0 0xf26a00ff
++
++/*P2_TMGLOCK1*/
++#define R0900_P2_TMGLOCK1 0xf26b
++#define F0900_P2_TMGLOCK_LEVEL1 0xf26b01ff
++
++/*P2_TMGLOCK0*/
++#define R0900_P2_TMGLOCK0 0xf26c
++#define F0900_P2_TMGLOCK_LEVEL0 0xf26c00ff
++
++/*P2_TMGOBS*/
++#define R0900_P2_TMGOBS 0xf26d
++#define F0900_P2_ROLLOFF_STATUS 0xf26d00c0
++#define F0900_P2_SCAN_SIGN 0xf26d0030
++#define F0900_P2_TMG_SCANNING 0xf26d0008
++#define F0900_P2_CHCENTERING_MODE 0xf26d0004
++#define F0900_P2_TMG_SCANFAIL 0xf26d0002
++
++/*P2_EQUALCFG*/
++#define R0900_P2_EQUALCFG 0xf26f
++#define F0900_P2_NOTMG_NEGALWAIT 0xf26f0080
++#define F0900_P2_EQUAL_ON 0xf26f0040
++#define F0900_P2_SEL_EQUALCOR 0xf26f0038
++#define F0900_P2_MU_EQUALDFE 0xf26f0007
++
++/*P2_EQUAI1*/
++#define R0900_P2_EQUAI1 0xf270
++#define F0900_P2_EQUA_ACCI1 0xf27001ff
++
++/*P2_EQUAQ1*/
++#define R0900_P2_EQUAQ1 0xf271
++#define F0900_P2_EQUA_ACCQ1 0xf27101ff
++
++/*P2_EQUAI2*/
++#define R0900_P2_EQUAI2 0xf272
++#define F0900_P2_EQUA_ACCI2 0xf27201ff
++
++/*P2_EQUAQ2*/
++#define R0900_P2_EQUAQ2 0xf273
++#define F0900_P2_EQUA_ACCQ2 0xf27301ff
++
++/*P2_EQUAI3*/
++#define R0900_P2_EQUAI3 0xf274
++#define F0900_P2_EQUA_ACCI3 0xf27401ff
++
++/*P2_EQUAQ3*/
++#define R0900_P2_EQUAQ3 0xf275
++#define F0900_P2_EQUA_ACCQ3 0xf27501ff
++
++/*P2_EQUAI4*/
++#define R0900_P2_EQUAI4 0xf276
++#define F0900_P2_EQUA_ACCI4 0xf27601ff
++
++/*P2_EQUAQ4*/
++#define R0900_P2_EQUAQ4 0xf277
++#define F0900_P2_EQUA_ACCQ4 0xf27701ff
++
++/*P2_EQUAI5*/
++#define R0900_P2_EQUAI5 0xf278
++#define F0900_P2_EQUA_ACCI5 0xf27801ff
++
++/*P2_EQUAQ5*/
++#define R0900_P2_EQUAQ5 0xf279
++#define F0900_P2_EQUA_ACCQ5 0xf27901ff
++
++/*P2_EQUAI6*/
++#define R0900_P2_EQUAI6 0xf27a
++#define F0900_P2_EQUA_ACCI6 0xf27a01ff
++
++/*P2_EQUAQ6*/
++#define R0900_P2_EQUAQ6 0xf27b
++#define F0900_P2_EQUA_ACCQ6 0xf27b01ff
++
++/*P2_EQUAI7*/
++#define R0900_P2_EQUAI7 0xf27c
++#define F0900_P2_EQUA_ACCI7 0xf27c01ff
++
++/*P2_EQUAQ7*/
++#define R0900_P2_EQUAQ7 0xf27d
++#define F0900_P2_EQUA_ACCQ7 0xf27d01ff
++
++/*P2_EQUAI8*/
++#define R0900_P2_EQUAI8 0xf27e
++#define F0900_P2_EQUA_ACCI8 0xf27e01ff
++
++/*P2_EQUAQ8*/
++#define R0900_P2_EQUAQ8 0xf27f
++#define F0900_P2_EQUA_ACCQ8 0xf27f01ff
++
++/*P2_NNOSDATAT1*/
++#define R0900_P2_NNOSDATAT1 0xf280
++#define F0900_P2_NOSDATAT_NORMED1 0xf28000ff
++
++/*P2_NNOSDATAT0*/
++#define R0900_P2_NNOSDATAT0 0xf281
++#define F0900_P2_NOSDATAT_NORMED0 0xf28100ff
++
++/*P2_NNOSDATA1*/
++#define R0900_P2_NNOSDATA1 0xf282
++#define F0900_P2_NOSDATA_NORMED1 0xf28200ff
++
++/*P2_NNOSDATA0*/
++#define R0900_P2_NNOSDATA0 0xf283
++#define F0900_P2_NOSDATA_NORMED0 0xf28300ff
++
++/*P2_NNOSPLHT1*/
++#define R0900_P2_NNOSPLHT1 0xf284
++#define F0900_P2_NOSPLHT_NORMED1 0xf28400ff
++
++/*P2_NNOSPLHT0*/
++#define R0900_P2_NNOSPLHT0 0xf285
++#define F0900_P2_NOSPLHT_NORMED0 0xf28500ff
++
++/*P2_NNOSPLH1*/
++#define R0900_P2_NNOSPLH1 0xf286
++#define F0900_P2_NOSPLH_NORMED1 0xf28600ff
++
++/*P2_NNOSPLH0*/
++#define R0900_P2_NNOSPLH0 0xf287
++#define F0900_P2_NOSPLH_NORMED0 0xf28700ff
++
++/*P2_NOSDATAT1*/
++#define R0900_P2_NOSDATAT1 0xf288
++#define F0900_P2_NOSDATAT_UNNORMED1 0xf28800ff
++
++/*P2_NOSDATAT0*/
++#define R0900_P2_NOSDATAT0 0xf289
++#define F0900_P2_NOSDATAT_UNNORMED0 0xf28900ff
++
++/*P2_NOSDATA1*/
++#define R0900_P2_NOSDATA1 0xf28a
++#define F0900_P2_NOSDATA_UNNORMED1 0xf28a00ff
++
++/*P2_NOSDATA0*/
++#define R0900_P2_NOSDATA0 0xf28b
++#define F0900_P2_NOSDATA_UNNORMED0 0xf28b00ff
++
++/*P2_NOSPLHT1*/
++#define R0900_P2_NOSPLHT1 0xf28c
++#define F0900_P2_NOSPLHT_UNNORMED1 0xf28c00ff
++
++/*P2_NOSPLHT0*/
++#define R0900_P2_NOSPLHT0 0xf28d
++#define F0900_P2_NOSPLHT_UNNORMED0 0xf28d00ff
++
++/*P2_NOSPLH1*/
++#define R0900_P2_NOSPLH1 0xf28e
++#define F0900_P2_NOSPLH_UNNORMED1 0xf28e00ff
++
++/*P2_NOSPLH0*/
++#define R0900_P2_NOSPLH0 0xf28f
++#define F0900_P2_NOSPLH_UNNORMED0 0xf28f00ff
++
++/*P2_CAR2CFG*/
++#define R0900_P2_CAR2CFG 0xf290
++#define F0900_P2_DESCRAMB_OFF 0xf2900080
++#define F0900_P2_PN4_SELECT 0xf2900040
++#define F0900_P2_CFR2_STOPDVBS1 0xf2900020
++#define F0900_P2_STOP_CFR2UPDATE 0xf2900010
++#define F0900_P2_STOP_NCO2UPDATE 0xf2900008
++#define F0900_P2_ROTA2ON 0xf2900004
++#define F0900_P2_PH_DET_ALGO2 0xf2900003
++
++/*P2_ACLC2*/
++#define R0900_P2_ACLC2 0xf291
++#define F0900_P2_CAR2_PUNCT_ADERAT 0xf2910040
++#define F0900_P2_CAR2_ALPHA_MANT 0xf2910030
++#define F0900_P2_CAR2_ALPHA_EXP 0xf291000f
++
++/*P2_BCLC2*/
++#define R0900_P2_BCLC2 0xf292
++#define F0900_P2_DVBS2_NIP 0xf2920080
++#define F0900_P2_CAR2_PUNCT_BDERAT 0xf2920040
++#define F0900_P2_CAR2_BETA_MANT 0xf2920030
++#define F0900_P2_CAR2_BETA_EXP 0xf292000f
++
++/*P2_CFR22*/
++#define R0900_P2_CFR22 0xf293
++#define F0900_P2_CAR2_FREQ2 0xf29301ff
++
++/*P2_CFR21*/
++#define R0900_P2_CFR21 0xf294
++#define F0900_P2_CAR2_FREQ1 0xf29400ff
++
++/*P2_CFR20*/
++#define R0900_P2_CFR20 0xf295
++#define F0900_P2_CAR2_FREQ0 0xf29500ff
++
++/*P2_ACLC2S2Q*/
++#define R0900_P2_ACLC2S2Q 0xf297
++#define F0900_P2_ENAB_SPSKSYMB 0xf2970080
++#define F0900_P2_CAR2S2_QADERAT 0xf2970040
++#define F0900_P2_CAR2S2_Q_ALPH_M 0xf2970030
++#define F0900_P2_CAR2S2_Q_ALPH_E 0xf297000f
++
++/*P2_ACLC2S28*/
++#define R0900_P2_ACLC2S28 0xf298
++#define F0900_P2_OLDI3Q_MODE 0xf2980080
++#define F0900_P2_CAR2S2_8ADERAT 0xf2980040
++#define F0900_P2_CAR2S2_8_ALPH_M 0xf2980030
++#define F0900_P2_CAR2S2_8_ALPH_E 0xf298000f
++
++/*P2_ACLC2S216A*/
++#define R0900_P2_ACLC2S216A 0xf299
++#define F0900_P2_CAR2S2_16ADERAT 0xf2990040
++#define F0900_P2_CAR2S2_16A_ALPH_M 0xf2990030
++#define F0900_P2_CAR2S2_16A_ALPH_E 0xf299000f
++
++/*P2_ACLC2S232A*/
++#define R0900_P2_ACLC2S232A 0xf29a
++#define F0900_P2_CAR2S2_32ADERAT 0xf29a0040
++#define F0900_P2_CAR2S2_32A_ALPH_M 0xf29a0030
++#define F0900_P2_CAR2S2_32A_ALPH_E 0xf29a000f
++
++/*P2_BCLC2S2Q*/
++#define R0900_P2_BCLC2S2Q 0xf29c
++#define F0900_P2_DVBS2S2Q_NIP 0xf29c0080
++#define F0900_P2_CAR2S2_QBDERAT 0xf29c0040
++#define F0900_P2_CAR2S2_Q_BETA_M 0xf29c0030
++#define F0900_P2_CAR2S2_Q_BETA_E 0xf29c000f
++
++/*P2_BCLC2S28*/
++#define R0900_P2_BCLC2S28 0xf29d
++#define F0900_P2_DVBS2S28_NIP 0xf29d0080
++#define F0900_P2_CAR2S2_8BDERAT 0xf29d0040
++#define F0900_P2_CAR2S2_8_BETA_M 0xf29d0030
++#define F0900_P2_CAR2S2_8_BETA_E 0xf29d000f
++
++/*P2_BCLC2S216A*/
++#define R0900_P2_BCLC2S216A 0xf29e
++#define F0900_P2_DVBS2S216A_NIP 0xf29e0080
++#define F0900_P2_CAR2S2_16BDERAT 0xf29e0040
++#define F0900_P2_CAR2S2_16A_BETA_M 0xf29e0030
++#define F0900_P2_CAR2S2_16A_BETA_E 0xf29e000f
++
++/*P2_BCLC2S232A*/
++#define R0900_P2_BCLC2S232A 0xf29f
++#define F0900_P2_DVBS2S232A_NIP 0xf29f0080
++#define F0900_P2_CAR2S2_32BDERAT 0xf29f0040
++#define F0900_P2_CAR2S2_32A_BETA_M 0xf29f0030
++#define F0900_P2_CAR2S2_32A_BETA_E 0xf29f000f
++
++/*P2_PLROOT2*/
++#define R0900_P2_PLROOT2 0xf2ac
++#define F0900_P2_SHORTFR_DISABLE 0xf2ac0080
++#define F0900_P2_LONGFR_DISABLE 0xf2ac0040
++#define F0900_P2_DUMMYPL_DISABLE 0xf2ac0020
++#define F0900_P2_SHORTFR_AVOID 0xf2ac0010
++#define F0900_P2_PLSCRAMB_MODE 0xf2ac000c
++#define F0900_P2_PLSCRAMB_ROOT2 0xf2ac0003
++
++/*P2_PLROOT1*/
++#define R0900_P2_PLROOT1 0xf2ad
++#define F0900_P2_PLSCRAMB_ROOT1 0xf2ad00ff
++
++/*P2_PLROOT0*/
++#define R0900_P2_PLROOT0 0xf2ae
++#define F0900_P2_PLSCRAMB_ROOT0 0xf2ae00ff
++
++/*P2_MODCODLST0*/
++#define R0900_P2_MODCODLST0 0xf2b0
++#define F0900_P2_EN_TOKEN31 0xf2b00080
++#define F0900_P2_SYNCTAG_SELECT 0xf2b00040
++#define F0900_P2_MODCODRQ_MODE 0xf2b00030
++
++/*P2_MODCODLST1*/
++#define R0900_P2_MODCODLST1 0xf2b1
++#define F0900_P2_DIS_MODCOD29 0xf2b100f0
++#define F0900_P2_DIS_32PSK_9_10 0xf2b1000f
++
++/*P2_MODCODLST2*/
++#define R0900_P2_MODCODLST2 0xf2b2
++#define F0900_P2_DIS_32PSK_8_9 0xf2b200f0
++#define F0900_P2_DIS_32PSK_5_6 0xf2b2000f
++
++/*P2_MODCODLST3*/
++#define R0900_P2_MODCODLST3 0xf2b3
++#define F0900_P2_DIS_32PSK_4_5 0xf2b300f0
++#define F0900_P2_DIS_32PSK_3_4 0xf2b3000f
++
++/*P2_MODCODLST4*/
++#define R0900_P2_MODCODLST4 0xf2b4
++#define F0900_P2_DIS_16PSK_9_10 0xf2b400f0
++#define F0900_P2_DIS_16PSK_8_9 0xf2b4000f
++
++/*P2_MODCODLST5*/
++#define R0900_P2_MODCODLST5 0xf2b5
++#define F0900_P2_DIS_16PSK_5_6 0xf2b500f0
++#define F0900_P2_DIS_16PSK_4_5 0xf2b5000f
++
++/*P2_MODCODLST6*/
++#define R0900_P2_MODCODLST6 0xf2b6
++#define F0900_P2_DIS_16PSK_3_4 0xf2b600f0
++#define F0900_P2_DIS_16PSK_2_3 0xf2b6000f
++
++/*P2_MODCODLST7*/
++#define R0900_P2_MODCODLST7 0xf2b7
++#define F0900_P2_DIS_8P_9_10 0xf2b700f0
++#define F0900_P2_DIS_8P_8_9 0xf2b7000f
++
++/*P2_MODCODLST8*/
++#define R0900_P2_MODCODLST8 0xf2b8
++#define F0900_P2_DIS_8P_5_6 0xf2b800f0
++#define F0900_P2_DIS_8P_3_4 0xf2b8000f
++
++/*P2_MODCODLST9*/
++#define R0900_P2_MODCODLST9 0xf2b9
++#define F0900_P2_DIS_8P_2_3 0xf2b900f0
++#define F0900_P2_DIS_8P_3_5 0xf2b9000f
++
++/*P2_MODCODLSTA*/
++#define R0900_P2_MODCODLSTA 0xf2ba
++#define F0900_P2_DIS_QP_9_10 0xf2ba00f0
++#define F0900_P2_DIS_QP_8_9 0xf2ba000f
++
++/*P2_MODCODLSTB*/
++#define R0900_P2_MODCODLSTB 0xf2bb
++#define F0900_P2_DIS_QP_5_6 0xf2bb00f0
++#define F0900_P2_DIS_QP_4_5 0xf2bb000f
++
++/*P2_MODCODLSTC*/
++#define R0900_P2_MODCODLSTC 0xf2bc
++#define F0900_P2_DIS_QP_3_4 0xf2bc00f0
++#define F0900_P2_DIS_QP_2_3 0xf2bc000f
++
++/*P2_MODCODLSTD*/
++#define R0900_P2_MODCODLSTD 0xf2bd
++#define F0900_P2_DIS_QP_3_5 0xf2bd00f0
++#define F0900_P2_DIS_QP_1_2 0xf2bd000f
++
++/*P2_MODCODLSTE*/
++#define R0900_P2_MODCODLSTE 0xf2be
++#define F0900_P2_DIS_QP_2_5 0xf2be00f0
++#define F0900_P2_DIS_QP_1_3 0xf2be000f
++
++/*P2_MODCODLSTF*/
++#define R0900_P2_MODCODLSTF 0xf2bf
++#define F0900_P2_DIS_QP_1_4 0xf2bf00f0
++#define F0900_P2_DDEMOD_SET 0xf2bf0002
++#define F0900_P2_DDEMOD_MASK 0xf2bf0001
++
++/*P2_DMDRESCFG*/
++#define R0900_P2_DMDRESCFG 0xf2c6
++#define F0900_P2_DMDRES_RESET 0xf2c60080
++#define F0900_P2_DMDRES_NOISESQR 0xf2c60010
++#define F0900_P2_DMDRES_STRALL 0xf2c60008
++#define F0900_P2_DMDRES_NEWONLY 0xf2c60004
++#define F0900_P2_DMDRES_NOSTORE 0xf2c60002
++#define F0900_P2_DMDRES_AGC2MEM 0xf2c60001
++
++/*P2_DMDRESADR*/
++#define R0900_P2_DMDRESADR 0xf2c7
++#define F0900_P2_SUSP_PREDCANAL 0xf2c70080
++#define F0900_P2_DMDRES_VALIDCFR 0xf2c70040
++#define F0900_P2_DMDRES_MEMFULL 0xf2c70030
++#define F0900_P2_DMDRES_RESNBR 0xf2c7000f
++
++/*P2_DMDRESDATA7*/
++#define R0900_P2_DMDRESDATA7 0xf2c8
++#define F0900_P2_DMDRES_DATA7 0xf2c800ff
++
++/*P2_DMDRESDATA6*/
++#define R0900_P2_DMDRESDATA6 0xf2c9
++#define F0900_P2_DMDRES_DATA6 0xf2c900ff
++
++/*P2_DMDRESDATA5*/
++#define R0900_P2_DMDRESDATA5 0xf2ca
++#define F0900_P2_DMDRES_DATA5 0xf2ca00ff
++
++/*P2_DMDRESDATA4*/
++#define R0900_P2_DMDRESDATA4 0xf2cb
++#define F0900_P2_DMDRES_DATA4 0xf2cb00ff
++
++/*P2_DMDRESDATA3*/
++#define R0900_P2_DMDRESDATA3 0xf2cc
++#define F0900_P2_DMDRES_DATA3 0xf2cc00ff
++
++/*P2_DMDRESDATA2*/
++#define R0900_P2_DMDRESDATA2 0xf2cd
++#define F0900_P2_DMDRES_DATA2 0xf2cd00ff
++
++/*P2_DMDRESDATA1*/
++#define R0900_P2_DMDRESDATA1 0xf2ce
++#define F0900_P2_DMDRES_DATA1 0xf2ce00ff
++
++/*P2_DMDRESDATA0*/
++#define R0900_P2_DMDRESDATA0 0xf2cf
++#define F0900_P2_DMDRES_DATA0 0xf2cf00ff
++
++/*P2_FFEI1*/
++#define R0900_P2_FFEI1 0xf2d0
++#define F0900_P2_FFE_ACCI1 0xf2d001ff
++
++/*P2_FFEQ1*/
++#define R0900_P2_FFEQ1 0xf2d1
++#define F0900_P2_FFE_ACCQ1 0xf2d101ff
++
++/*P2_FFEI2*/
++#define R0900_P2_FFEI2 0xf2d2
++#define F0900_P2_FFE_ACCI2 0xf2d201ff
++
++/*P2_FFEQ2*/
++#define R0900_P2_FFEQ2 0xf2d3
++#define F0900_P2_FFE_ACCQ2 0xf2d301ff
++
++/*P2_FFEI3*/
++#define R0900_P2_FFEI3 0xf2d4
++#define F0900_P2_FFE_ACCI3 0xf2d401ff
++
++/*P2_FFEQ3*/
++#define R0900_P2_FFEQ3 0xf2d5
++#define F0900_P2_FFE_ACCQ3 0xf2d501ff
++
++/*P2_FFEI4*/
++#define R0900_P2_FFEI4 0xf2d6
++#define F0900_P2_FFE_ACCI4 0xf2d601ff
++
++/*P2_FFEQ4*/
++#define R0900_P2_FFEQ4 0xf2d7
++#define F0900_P2_FFE_ACCQ4 0xf2d701ff
++
++/*P2_FFECFG*/
++#define R0900_P2_FFECFG 0xf2d8
++#define F0900_P2_EQUALFFE_ON 0xf2d80040
++#define F0900_P2_EQUAL_USEDSYMB 0xf2d80030
++#define F0900_P2_MU_EQUALFFE 0xf2d80007
++
++/*P2_TNRCFG*/
++#define R0900_P2_TNRCFG 0xf2e0
++#define F0900_P2_TUN_ACKFAIL 0xf2e00080
++#define F0900_P2_TUN_TYPE 0xf2e00070
++#define F0900_P2_TUN_SECSTOP 0xf2e00008
++#define F0900_P2_TUN_VCOSRCH 0xf2e00004
++#define F0900_P2_TUN_MADDRESS 0xf2e00003
++
++/*P2_TNRCFG2*/
++#define R0900_P2_TNRCFG2 0xf2e1
++#define F0900_P2_TUN_IQSWAP 0xf2e10080
++#define F0900_P2_STB6110_STEP2MHZ 0xf2e10040
++#define F0900_P2_STB6120_DBLI2C 0xf2e10020
++#define F0900_P2_DIS_FCCK 0xf2e10010
++#define F0900_P2_DIS_LPEN 0xf2e10008
++#define F0900_P2_DIS_BWCALC 0xf2e10004
++#define F0900_P2_SHORT_WAITSTATES 0xf2e10002
++#define F0900_P2_DIS_2BWAGC1 0xf2e10001
++
++/*P2_TNRXTAL*/
++#define R0900_P2_TNRXTAL 0xf2e4
++#define F0900_P2_TUN_MCLKDECIMAL 0xf2e400e0
++#define F0900_P2_TUN_XTALFREQ 0xf2e4001f
++
++/*P2_TNRSTEPS*/
++#define R0900_P2_TNRSTEPS 0xf2e7
++#define F0900_P2_TUNER_BW1P6 0xf2e70080
++#define F0900_P2_BWINC_OFFSET 0xf2e70070
++#define F0900_P2_SOFTSTEP_RNG 0xf2e70008
++#define F0900_P2_TUN_BWOFFSET 0xf2e70107
++
++/*P2_TNRGAIN*/
++#define R0900_P2_TNRGAIN 0xf2e8
++#define F0900_P2_TUN_KDIVEN 0xf2e800c0
++#define F0900_P2_STB6X00_OCK 0xf2e80030
++#define F0900_P2_TUN_GAIN 0xf2e8000f
++
++/*P2_TNRRF1*/
++#define R0900_P2_TNRRF1 0xf2e9
++#define F0900_P2_TUN_RFFREQ2 0xf2e900ff
++
++/*P2_TNRRF0*/
++#define R0900_P2_TNRRF0 0xf2ea
++#define F0900_P2_TUN_RFFREQ1 0xf2ea00ff
++
++/*P2_TNRBW*/
++#define R0900_P2_TNRBW 0xf2eb
++#define F0900_P2_TUN_RFFREQ0 0xf2eb00c0
++#define F0900_P2_TUN_BW 0xf2eb003f
++
++/*P2_TNRADJ*/
++#define R0900_P2_TNRADJ 0xf2ec
++#define F0900_P2_STB61X0_RCLK 0xf2ec0080
++#define F0900_P2_STB61X0_CALTIME 0xf2ec0040
++#define F0900_P2_STB6X00_DLB 0xf2ec0038
++#define F0900_P2_STB6000_FCL 0xf2ec0007
++
++/*P2_TNRCTL2*/
++#define R0900_P2_TNRCTL2 0xf2ed
++#define F0900_P2_STB61X0_LCP1_RCCKOFF 0xf2ed0080
++#define F0900_P2_STB61X0_LCP0 0xf2ed0040
++#define F0900_P2_STB61X0_XTOUT_RFOUTS 0xf2ed0020
++#define F0900_P2_STB61X0_XTON_MCKDV 0xf2ed0010
++#define F0900_P2_STB61X0_CALOFF_DCOFF 0xf2ed0008
++#define F0900_P2_STB6110_LPT 0xf2ed0004
++#define F0900_P2_STB6110_RX 0xf2ed0002
++#define F0900_P2_STB6110_SYN 0xf2ed0001
++
++/*P2_TNRCFG3*/
++#define R0900_P2_TNRCFG3 0xf2ee
++#define F0900_P2_STB6120_DISCTRL1 0xf2ee0080
++#define F0900_P2_STB6120_INVORDER 0xf2ee0040
++#define F0900_P2_STB6120_ENCTRL6 0xf2ee0020
++#define F0900_P2_TUN_PLLFREQ 0xf2ee001c
++#define F0900_P2_TUN_I2CFREQ_MODE 0xf2ee0003
++
++/*P2_TNRLAUNCH*/
++#define R0900_P2_TNRLAUNCH 0xf2f0
++
++/*P2_TNRLD*/
++#define R0900_P2_TNRLD 0xf2f0
++#define F0900_P2_TUNLD_VCOING 0xf2f00080
++#define F0900_P2_TUN_REG1FAIL 0xf2f00040
++#define F0900_P2_TUN_REG2FAIL 0xf2f00020
++#define F0900_P2_TUN_REG3FAIL 0xf2f00010
++#define F0900_P2_TUN_REG4FAIL 0xf2f00008
++#define F0900_P2_TUN_REG5FAIL 0xf2f00004
++#define F0900_P2_TUN_BWING 0xf2f00002
++#define F0900_P2_TUN_LOCKED 0xf2f00001
++
++/*P2_TNROBSL*/
++#define R0900_P2_TNROBSL 0xf2f6
++#define F0900_P2_TUN_I2CABORTED 0xf2f60080
++#define F0900_P2_TUN_LPEN 0xf2f60040
++#define F0900_P2_TUN_FCCK 0xf2f60020
++#define F0900_P2_TUN_I2CLOCKED 0xf2f60010
++#define F0900_P2_TUN_PROGDONE 0xf2f6000c
++#define F0900_P2_TUN_RFRESTE1 0xf2f60003
++
++/*P2_TNRRESTE*/
++#define R0900_P2_TNRRESTE 0xf2f7
++#define F0900_P2_TUN_RFRESTE0 0xf2f700ff
++
++/*P2_SMAPCOEF7*/
++#define R0900_P2_SMAPCOEF7 0xf300
++#define F0900_P2_DIS_QSCALE 0xf3000080
++#define F0900_P2_SMAPCOEF_Q_LLR12 0xf300017f
++
++/*P2_SMAPCOEF6*/
++#define R0900_P2_SMAPCOEF6 0xf301
++#define F0900_P2_DIS_NEWSCALE 0xf3010008
++#define F0900_P2_ADJ_8PSKLLR1 0xf3010004
++#define F0900_P2_OLD_8PSKLLR1 0xf3010002
++#define F0900_P2_DIS_AB8PSK 0xf3010001
++
++/*P2_SMAPCOEF5*/
++#define R0900_P2_SMAPCOEF5 0xf302
++#define F0900_P2_DIS_8SCALE 0xf3020080
++#define F0900_P2_SMAPCOEF_8P_LLR23 0xf302017f
++
++/*P2_DMDPLHSTAT*/
++#define R0900_P2_DMDPLHSTAT 0xf320
++#define F0900_P2_PLH_STATISTIC 0xf32000ff
++
++/*P2_LOCKTIME3*/
++#define R0900_P2_LOCKTIME3 0xf322
++#define F0900_P2_DEMOD_LOCKTIME3 0xf32200ff
++
++/*P2_LOCKTIME2*/
++#define R0900_P2_LOCKTIME2 0xf323
++#define F0900_P2_DEMOD_LOCKTIME2 0xf32300ff
++
++/*P2_LOCKTIME1*/
++#define R0900_P2_LOCKTIME1 0xf324
++#define F0900_P2_DEMOD_LOCKTIME1 0xf32400ff
++
++/*P2_LOCKTIME0*/
++#define R0900_P2_LOCKTIME0 0xf325
++#define F0900_P2_DEMOD_LOCKTIME0 0xf32500ff
++
++/*P2_VITSCALE*/
++#define R0900_P2_VITSCALE 0xf332
++#define F0900_P2_NVTH_NOSRANGE 0xf3320080
++#define F0900_P2_VERROR_MAXMODE 0xf3320040
++#define F0900_P2_KDIV_MODE 0xf3320030
++#define F0900_P2_NSLOWSN_LOCKED 0xf3320008
++#define F0900_P2_DELOCK_PRFLOSS 0xf3320004
++#define F0900_P2_DIS_RSFLOCK 0xf3320002
++
++/*P2_FECM*/
++#define R0900_P2_FECM 0xf333
++#define F0900_P2_DSS_DVB 0xf3330080
++#define F0900_P2_DEMOD_BYPASS 0xf3330040
++#define F0900_P2_CMP_SLOWMODE 0xf3330020
++#define F0900_P2_DSS_SRCH 0xf3330010
++#define F0900_P2_DIFF_MODEVIT 0xf3330004
++#define F0900_P2_SYNCVIT 0xf3330002
++#define F0900_P2_IQINV 0xf3330001
++
++/*P2_VTH12*/
++#define R0900_P2_VTH12 0xf334
++#define F0900_P2_VTH12 0xf33400ff
++
++/*P2_VTH23*/
++#define R0900_P2_VTH23 0xf335
++#define F0900_P2_VTH23 0xf33500ff
++
++/*P2_VTH34*/
++#define R0900_P2_VTH34 0xf336
++#define F0900_P2_VTH34 0xf33600ff
++
++/*P2_VTH56*/
++#define R0900_P2_VTH56 0xf337
++#define F0900_P2_VTH56 0xf33700ff
++
++/*P2_VTH67*/
++#define R0900_P2_VTH67 0xf338
++#define F0900_P2_VTH67 0xf33800ff
++
++/*P2_VTH78*/
++#define R0900_P2_VTH78 0xf339
++#define F0900_P2_VTH78 0xf33900ff
++
++/*P2_VITCURPUN*/
++#define R0900_P2_VITCURPUN 0xf33a
++#define F0900_P2_VIT_MAPPING 0xf33a00e0
++#define F0900_P2_VIT_CURPUN 0xf33a001f
++
++/*P2_VERROR*/
++#define R0900_P2_VERROR 0xf33b
++#define F0900_P2_REGERR_VIT 0xf33b00ff
++
++/*P2_PRVIT*/
++#define R0900_P2_PRVIT 0xf33c
++#define F0900_P2_DIS_VTHLOCK 0xf33c0040
++#define F0900_P2_E7_8VIT 0xf33c0020
++#define F0900_P2_E6_7VIT 0xf33c0010
++#define F0900_P2_E5_6VIT 0xf33c0008
++#define F0900_P2_E3_4VIT 0xf33c0004
++#define F0900_P2_E2_3VIT 0xf33c0002
++#define F0900_P2_E1_2VIT 0xf33c0001
++
++/*P2_VAVSRVIT*/
++#define R0900_P2_VAVSRVIT 0xf33d
++#define F0900_P2_AMVIT 0xf33d0080
++#define F0900_P2_FROZENVIT 0xf33d0040
++#define F0900_P2_SNVIT 0xf33d0030
++#define F0900_P2_TOVVIT 0xf33d000c
++#define F0900_P2_HYPVIT 0xf33d0003
++
++/*P2_VSTATUSVIT*/
++#define R0900_P2_VSTATUSVIT 0xf33e
++#define F0900_P2_VITERBI_ON 0xf33e0080
++#define F0900_P2_END_LOOPVIT 0xf33e0040
++#define F0900_P2_VITERBI_DEPRF 0xf33e0020
++#define F0900_P2_PRFVIT 0xf33e0010
++#define F0900_P2_LOCKEDVIT 0xf33e0008
++#define F0900_P2_VITERBI_DELOCK 0xf33e0004
++#define F0900_P2_VIT_DEMODSEL 0xf33e0002
++#define F0900_P2_VITERBI_COMPOUT 0xf33e0001
++
++/*P2_VTHINUSE*/
++#define R0900_P2_VTHINUSE 0xf33f
++#define F0900_P2_VIT_INUSE 0xf33f00ff
++
++/*P2_KDIV12*/
++#define R0900_P2_KDIV12 0xf340
++#define F0900_P2_KDIV12_MANUAL 0xf3400080
++#define F0900_P2_K_DIVIDER_12 0xf340007f
++
++/*P2_KDIV23*/
++#define R0900_P2_KDIV23 0xf341
++#define F0900_P2_KDIV23_MANUAL 0xf3410080
++#define F0900_P2_K_DIVIDER_23 0xf341007f
++
++/*P2_KDIV34*/
++#define R0900_P2_KDIV34 0xf342
++#define F0900_P2_KDIV34_MANUAL 0xf3420080
++#define F0900_P2_K_DIVIDER_34 0xf342007f
++
++/*P2_KDIV56*/
++#define R0900_P2_KDIV56 0xf343
++#define F0900_P2_KDIV56_MANUAL 0xf3430080
++#define F0900_P2_K_DIVIDER_56 0xf343007f
++
++/*P2_KDIV67*/
++#define R0900_P2_KDIV67 0xf344
++#define F0900_P2_KDIV67_MANUAL 0xf3440080
++#define F0900_P2_K_DIVIDER_67 0xf344007f
++
++/*P2_KDIV78*/
++#define R0900_P2_KDIV78 0xf345
++#define F0900_P2_KDIV78_MANUAL 0xf3450080
++#define F0900_P2_K_DIVIDER_78 0xf345007f
++
++/*P2_PDELCTRL1*/
++#define R0900_P2_PDELCTRL1 0xf350
++#define F0900_P2_INV_MISMASK 0xf3500080
++#define F0900_P2_FORCE_ACCEPTED 0xf3500040
++#define F0900_P2_FILTER_EN 0xf3500020
++#define F0900_P2_FORCE_PKTDELINUSE 0xf3500010
++#define F0900_P2_HYSTEN 0xf3500008
++#define F0900_P2_HYSTSWRST 0xf3500004
++#define F0900_P2_EN_MIS00 0xf3500002
++#define F0900_P2_ALGOSWRST 0xf3500001
++
++/*P2_PDELCTRL2*/
++#define R0900_P2_PDELCTRL2 0xf351
++#define F0900_P2_FORCE_CONTINUOUS 0xf3510080
++#define F0900_P2_RESET_UPKO_COUNT 0xf3510040
++#define F0900_P2_USER_PKTDELIN_NB 0xf3510020
++#define F0900_P2_FORCE_LOCKED 0xf3510010
++#define F0900_P2_DATA_UNBBSCRAM 0xf3510008
++#define F0900_P2_FORCE_LONGPKT 0xf3510004
++#define F0900_P2_FRAME_MODE 0xf3510002
++
++/*P2_HYSTTHRESH*/
++#define R0900_P2_HYSTTHRESH 0xf354
++#define F0900_P2_UNLCK_THRESH 0xf35400f0
++#define F0900_P2_DELIN_LCK_THRESH 0xf354000f
++
++/*P2_ISIENTRY*/
++#define R0900_P2_ISIENTRY 0xf35e
++#define F0900_P2_ISI_ENTRY 0xf35e00ff
++
++/*P2_ISIBITENA*/
++#define R0900_P2_ISIBITENA 0xf35f
++#define F0900_P2_ISI_BIT_EN 0xf35f00ff
++
++/*P2_MATSTR1*/
++#define R0900_P2_MATSTR1 0xf360
++#define F0900_P2_MATYPE_CURRENT1 0xf36000ff
++
++/*P2_MATSTR0*/
++#define R0900_P2_MATSTR0 0xf361
++#define F0900_P2_MATYPE_CURRENT0 0xf36100ff
++
++/*P2_UPLSTR1*/
++#define R0900_P2_UPLSTR1 0xf362
++#define F0900_P2_UPL_CURRENT1 0xf36200ff
++
++/*P2_UPLSTR0*/
++#define R0900_P2_UPLSTR0 0xf363
++#define F0900_P2_UPL_CURRENT0 0xf36300ff
++
++/*P2_DFLSTR1*/
++#define R0900_P2_DFLSTR1 0xf364
++#define F0900_P2_DFL_CURRENT1 0xf36400ff
++
++/*P2_DFLSTR0*/
++#define R0900_P2_DFLSTR0 0xf365
++#define F0900_P2_DFL_CURRENT0 0xf36500ff
++
++/*P2_SYNCSTR*/
++#define R0900_P2_SYNCSTR 0xf366
++#define F0900_P2_SYNC_CURRENT 0xf36600ff
++
++/*P2_SYNCDSTR1*/
++#define R0900_P2_SYNCDSTR1 0xf367
++#define F0900_P2_SYNCD_CURRENT1 0xf36700ff
++
++/*P2_SYNCDSTR0*/
++#define R0900_P2_SYNCDSTR0 0xf368
++#define F0900_P2_SYNCD_CURRENT0 0xf36800ff
++
++/*P2_PDELSTATUS1*/
++#define R0900_P2_PDELSTATUS1 0xf369
++#define F0900_P2_PKTDELIN_DELOCK 0xf3690080
++#define F0900_P2_SYNCDUPDFL_BADDFL 0xf3690040
++#define F0900_P2_CONTINUOUS_STREAM 0xf3690020
++#define F0900_P2_UNACCEPTED_STREAM 0xf3690010
++#define F0900_P2_BCH_ERROR_FLAG 0xf3690008
++#define F0900_P2_BBHCRCKO 0xf3690004
++#define F0900_P2_PKTDELIN_LOCK 0xf3690002
++#define F0900_P2_FIRST_LOCK 0xf3690001
++
++/*P2_PDELSTATUS2*/
++#define R0900_P2_PDELSTATUS2 0xf36a
++#define F0900_P2_PKTDEL_DEMODSEL 0xf36a0080
++#define F0900_P2_FRAME_MODCOD 0xf36a007c
++#define F0900_P2_FRAME_TYPE 0xf36a0003
++
++/*P2_BBFCRCKO1*/
++#define R0900_P2_BBFCRCKO1 0xf36b
++#define F0900_P2_BBHCRC_KOCNT1 0xf36b00ff
++
++/*P2_BBFCRCKO0*/
++#define R0900_P2_BBFCRCKO0 0xf36c
++#define F0900_P2_BBHCRC_KOCNT0 0xf36c00ff
++
++/*P2_UPCRCKO1*/
++#define R0900_P2_UPCRCKO1 0xf36d
++#define F0900_P2_PKTCRC_KOCNT1 0xf36d00ff
++
++/*P2_UPCRCKO0*/
++#define R0900_P2_UPCRCKO0 0xf36e
++#define F0900_P2_PKTCRC_KOCNT0 0xf36e00ff
++
++/*P2_TSSTATEM*/
++#define R0900_P2_TSSTATEM 0xf370
++#define F0900_P2_TSDIL_ON 0xf3700080
++#define F0900_P2_TSSKIPRS_ON 0xf3700040
++#define F0900_P2_TSRS_ON 0xf3700020
++#define F0900_P2_TSDESCRAMB_ON 0xf3700010
++#define F0900_P2_TSFRAME_MODE 0xf3700008
++#define F0900_P2_TS_DISABLE 0xf3700004
++#define F0900_P2_TSACM_MODE 0xf3700002
++#define F0900_P2_TSOUT_NOSYNC 0xf3700001
++
++/*P2_TSCFGH*/
++#define R0900_P2_TSCFGH 0xf372
++#define F0900_P2_TSFIFO_DVBCI 0xf3720080
++#define F0900_P2_TSFIFO_SERIAL 0xf3720040
++#define F0900_P2_TSFIFO_TEIUPDATE 0xf3720020
++#define F0900_P2_TSFIFO_DUTY50 0xf3720010
++#define F0900_P2_TSFIFO_HSGNLOUT 0xf3720008
++#define F0900_P2_TSFIFO_ERRMODE 0xf3720006
++#define F0900_P2_RST_HWARE 0xf3720001
++
++/*P2_TSCFGM*/
++#define R0900_P2_TSCFGM 0xf373
++#define F0900_P2_TSFIFO_MANSPEED 0xf37300c0
++#define F0900_P2_TSFIFO_PERMDATA 0xf3730020
++#define F0900_P2_TSFIFO_NONEWSGNL 0xf3730010
++#define F0900_P2_TSFIFO_BITSPEED 0xf3730008
++#define F0900_P2_NPD_SPECDVBS2 0xf3730004
++#define F0900_P2_TSFIFO_STOPCKDIS 0xf3730002
++#define F0900_P2_TSFIFO_INVDATA 0xf3730001
++
++/*P2_TSCFGL*/
++#define R0900_P2_TSCFGL 0xf374
++#define F0900_P2_TSFIFO_BCLKDEL1CK 0xf37400c0
++#define F0900_P2_BCHERROR_MODE 0xf3740030
++#define F0900_P2_TSFIFO_NSGNL2DATA 0xf3740008
++#define F0900_P2_TSFIFO_EMBINDVB 0xf3740004
++#define F0900_P2_TSFIFO_DPUNACT 0xf3740002
++#define F0900_P2_TSFIFO_NPDOFF 0xf3740001
++
++/*P2_TSINSDELH*/
++#define R0900_P2_TSINSDELH 0xf376
++#define F0900_P2_TSDEL_SYNCBYTE 0xf3760080
++#define F0900_P2_TSDEL_XXHEADER 0xf3760040
++#define F0900_P2_TSDEL_BBHEADER 0xf3760020
++#define F0900_P2_TSDEL_DATAFIELD 0xf3760010
++#define F0900_P2_TSINSDEL_ISCR 0xf3760008
++#define F0900_P2_TSINSDEL_NPD 0xf3760004
++#define F0900_P2_TSINSDEL_RSPARITY 0xf3760002
++#define F0900_P2_TSINSDEL_CRC8 0xf3760001
++
++/*P2_TSSPEED*/
++#define R0900_P2_TSSPEED 0xf380
++#define F0900_P2_TSFIFO_OUTSPEED 0xf38000ff
++
++/*P2_TSSTATUS*/
++#define R0900_P2_TSSTATUS 0xf381
++#define F0900_P2_TSFIFO_LINEOK 0xf3810080
++#define F0900_P2_TSFIFO_ERROR 0xf3810040
++#define F0900_P2_TSFIFO_DATA7 0xf3810020
++#define F0900_P2_TSFIFO_NOSYNC 0xf3810010
++#define F0900_P2_ISCR_INITIALIZED 0xf3810008
++#define F0900_P2_ISCR_UPDATED 0xf3810004
++#define F0900_P2_SOFFIFO_UNREGUL 0xf3810002
++#define F0900_P2_DIL_READY 0xf3810001
++
++/*P2_TSSTATUS2*/
++#define R0900_P2_TSSTATUS2 0xf382
++#define F0900_P2_TSFIFO_DEMODSEL 0xf3820080
++#define F0900_P2_TSFIFOSPEED_STORE 0xf3820040
++#define F0900_P2_DILXX_RESET 0xf3820020
++#define F0900_P2_TSSERIAL_IMPOS 0xf3820010
++#define F0900_P2_TSFIFO_LINENOK 0xf3820008
++#define F0900_P2_BITSPEED_EVENT 0xf3820004
++#define F0900_P2_SCRAMBDETECT 0xf3820002
++#define F0900_P2_ULDTV67_FALSELOCK 0xf3820001
++
++/*P2_TSBITRATE1*/
++#define R0900_P2_TSBITRATE1 0xf383
++#define F0900_P2_TSFIFO_BITRATE1 0xf38300ff
++
++/*P2_TSBITRATE0*/
++#define R0900_P2_TSBITRATE0 0xf384
++#define F0900_P2_TSFIFO_BITRATE0 0xf38400ff
++
++/*P2_ERRCTRL1*/
++#define R0900_P2_ERRCTRL1 0xf398
++#define F0900_P2_ERR_SOURCE1 0xf39800f0
++#define F0900_P2_NUM_EVENT1 0xf3980007
++
++/*P2_ERRCNT12*/
++#define R0900_P2_ERRCNT12 0xf399
++#define F0900_P2_ERRCNT1_OLDVALUE 0xf3990080
++#define F0900_P2_ERR_CNT12 0xf399007f
++
++/*P2_ERRCNT11*/
++#define R0900_P2_ERRCNT11 0xf39a
++#define F0900_P2_ERR_CNT11 0xf39a00ff
++
++/*P2_ERRCNT10*/
++#define R0900_P2_ERRCNT10 0xf39b
++#define F0900_P2_ERR_CNT10 0xf39b00ff
++
++/*P2_ERRCTRL2*/
++#define R0900_P2_ERRCTRL2 0xf39c
++#define F0900_P2_ERR_SOURCE2 0xf39c00f0
++#define F0900_P2_NUM_EVENT2 0xf39c0007
++
++/*P2_ERRCNT22*/
++#define R0900_P2_ERRCNT22 0xf39d
++#define F0900_P2_ERRCNT2_OLDVALUE 0xf39d0080
++#define F0900_P2_ERR_CNT22 0xf39d007f
++
++/*P2_ERRCNT21*/
++#define R0900_P2_ERRCNT21 0xf39e
++#define F0900_P2_ERR_CNT21 0xf39e00ff
++
++/*P2_ERRCNT20*/
++#define R0900_P2_ERRCNT20 0xf39f
++#define F0900_P2_ERR_CNT20 0xf39f00ff
++
++/*P2_FECSPY*/
++#define R0900_P2_FECSPY 0xf3a0
++#define F0900_P2_SPY_ENABLE 0xf3a00080
++#define F0900_P2_NO_SYNCBYTE 0xf3a00040
++#define F0900_P2_SERIAL_MODE 0xf3a00020
++#define F0900_P2_UNUSUAL_PACKET 0xf3a00010
++#define F0900_P2_BER_PACKMODE 0xf3a00008
++#define F0900_P2_BERMETER_LMODE 0xf3a00002
++#define F0900_P2_BERMETER_RESET 0xf3a00001
++
++/*P2_FSPYCFG*/
++#define R0900_P2_FSPYCFG 0xf3a1
++#define F0900_P2_FECSPY_INPUT 0xf3a100c0
++#define F0900_P2_RST_ON_ERROR 0xf3a10020
++#define F0900_P2_ONE_SHOT 0xf3a10010
++#define F0900_P2_I2C_MODE 0xf3a1000c
++#define F0900_P2_SPY_HYSTERESIS 0xf3a10003
++
++/*P2_FSPYDATA*/
++#define R0900_P2_FSPYDATA 0xf3a2
++#define F0900_P2_SPY_STUFFING 0xf3a20080
++#define F0900_P2_NOERROR_PKTJITTER 0xf3a20040
++#define F0900_P2_SPY_CNULLPKT 0xf3a20020
++#define F0900_P2_SPY_OUTDATA_MODE 0xf3a2001f
++
++/*P2_FSPYOUT*/
++#define R0900_P2_FSPYOUT 0xf3a3
++#define F0900_P2_FSPY_DIRECT 0xf3a30080
++#define F0900_P2_SPY_OUTDATA_BUS 0xf3a30038
++#define F0900_P2_STUFF_MODE 0xf3a30007
++
++/*P2_FSTATUS*/
++#define R0900_P2_FSTATUS 0xf3a4
++#define F0900_P2_SPY_ENDSIM 0xf3a40080
++#define F0900_P2_VALID_SIM 0xf3a40040
++#define F0900_P2_FOUND_SIGNAL 0xf3a40020
++#define F0900_P2_DSS_SYNCBYTE 0xf3a40010
++#define F0900_P2_RESULT_STATE 0xf3a4000f
++
++/*P2_FBERCPT4*/
++#define R0900_P2_FBERCPT4 0xf3a8
++#define F0900_P2_FBERMETER_CPT4 0xf3a800ff
++
++/*P2_FBERCPT3*/
++#define R0900_P2_FBERCPT3 0xf3a9
++#define F0900_P2_FBERMETER_CPT3 0xf3a900ff
++
++/*P2_FBERCPT2*/
++#define R0900_P2_FBERCPT2 0xf3aa
++#define F0900_P2_FBERMETER_CPT2 0xf3aa00ff
++
++/*P2_FBERCPT1*/
++#define R0900_P2_FBERCPT1 0xf3ab
++#define F0900_P2_FBERMETER_CPT1 0xf3ab00ff
++
++/*P2_FBERCPT0*/
++#define R0900_P2_FBERCPT0 0xf3ac
++#define F0900_P2_FBERMETER_CPT0 0xf3ac00ff
++
++/*P2_FBERERR2*/
++#define R0900_P2_FBERERR2 0xf3ad
++#define F0900_P2_FBERMETER_ERR2 0xf3ad00ff
++
++/*P2_FBERERR1*/
++#define R0900_P2_FBERERR1 0xf3ae
++#define F0900_P2_FBERMETER_ERR1 0xf3ae00ff
++
++/*P2_FBERERR0*/
++#define R0900_P2_FBERERR0 0xf3af
++#define F0900_P2_FBERMETER_ERR0 0xf3af00ff
++
++/*P2_FSPYBER*/
++#define R0900_P2_FSPYBER 0xf3b2
++#define F0900_P2_FSPYOBS_XORREAD 0xf3b20040
++#define F0900_P2_FSPYBER_OBSMODE 0xf3b20020
++#define F0900_P2_FSPYBER_SYNCBYTE 0xf3b20010
++#define F0900_P2_FSPYBER_UNSYNC 0xf3b20008
++#define F0900_P2_FSPYBER_CTIME 0xf3b20007
++
++/*P1_IQCONST*/
++#define R0900_P1_IQCONST 0xf400
++#define F0900_P1_CONSTEL_SELECT 0xf4000060
++#define F0900_P1_IQSYMB_SEL 0xf400001f
++
++/*P1_NOSCFG*/
++#define R0900_P1_NOSCFG 0xf401
++#define F0900_P1_DUMMYPL_NOSDATA 0xf4010020
++#define F0900_P1_NOSPLH_BETA 0xf4010018
++#define F0900_P1_NOSDATA_BETA 0xf4010007
++
++/*P1_ISYMB*/
++#define R0900_P1_ISYMB 0xf402
++#define F0900_P1_I_SYMBOL 0xf40201ff
++
++/*P1_QSYMB*/
++#define R0900_P1_QSYMB 0xf403
++#define F0900_P1_Q_SYMBOL 0xf40301ff
++
++/*P1_AGC1CFG*/
++#define R0900_P1_AGC1CFG 0xf404
++#define F0900_P1_DC_FROZEN 0xf4040080
++#define F0900_P1_DC_CORRECT 0xf4040040
++#define F0900_P1_AMM_FROZEN 0xf4040020
++#define F0900_P1_AMM_CORRECT 0xf4040010
++#define F0900_P1_QUAD_FROZEN 0xf4040008
++#define F0900_P1_QUAD_CORRECT 0xf4040004
++#define F0900_P1_DCCOMP_SLOW 0xf4040002
++#define F0900_P1_IQMISM_SLOW 0xf4040001
++
++/*P1_AGC1CN*/
++#define R0900_P1_AGC1CN 0xf406
++#define F0900_P1_AGC1_LOCKED 0xf4060080
++#define F0900_P1_AGC1_OVERFLOW 0xf4060040
++#define F0900_P1_AGC1_NOSLOWLK 0xf4060020
++#define F0900_P1_AGC1_MINPOWER 0xf4060010
++#define F0900_P1_AGCOUT_FAST 0xf4060008
++#define F0900_P1_AGCIQ_BETA 0xf4060007
++
++/*P1_AGC1REF*/
++#define R0900_P1_AGC1REF 0xf407
++#define F0900_P1_AGCIQ_REF 0xf40700ff
++
++/*P1_IDCCOMP*/
++#define R0900_P1_IDCCOMP 0xf408
++#define F0900_P1_IAVERAGE_ADJ 0xf40801ff
++
++/*P1_QDCCOMP*/
++#define R0900_P1_QDCCOMP 0xf409
++#define F0900_P1_QAVERAGE_ADJ 0xf40901ff
++
++/*P1_POWERI*/
++#define R0900_P1_POWERI 0xf40a
++#define F0900_P1_POWER_I 0xf40a00ff
++
++/*P1_POWERQ*/
++#define R0900_P1_POWERQ 0xf40b
++#define F0900_P1_POWER_Q 0xf40b00ff
++
++/*P1_AGC1AMM*/
++#define R0900_P1_AGC1AMM 0xf40c
++#define F0900_P1_AMM_VALUE 0xf40c00ff
++
++/*P1_AGC1QUAD*/
++#define R0900_P1_AGC1QUAD 0xf40d
++#define F0900_P1_QUAD_VALUE 0xf40d01ff
++
++/*P1_AGCIQIN1*/
++#define R0900_P1_AGCIQIN1 0xf40e
++#define F0900_P1_AGCIQ_VALUE1 0xf40e00ff
++
++/*P1_AGCIQIN0*/
++#define R0900_P1_AGCIQIN0 0xf40f
++#define F0900_P1_AGCIQ_VALUE0 0xf40f00ff
++
++/*P1_DEMOD*/
++#define R0900_P1_DEMOD 0xf410
++#define F0900_P1_DEMOD_STOP 0xf4100040
++#define F0900_P1_SPECINV_CONTROL 0xf4100030
++#define F0900_P1_FORCE_ENASAMP 0xf4100008
++#define F0900_P1_MANUAL_ROLLOFF 0xf4100004
++#define F0900_P1_ROLLOFF_CONTROL 0xf4100003
++
++/*P1_DMDMODCOD*/
++#define R0900_P1_DMDMODCOD 0xf411
++#define F0900_P1_MANUAL_MODCOD 0xf4110080
++#define F0900_P1_DEMOD_MODCOD 0xf411007c
++#define F0900_P1_DEMOD_TYPE 0xf4110003
++
++/*P1_DSTATUS*/
++#define R0900_P1_DSTATUS 0xf412
++#define F0900_P1_CAR_LOCK 0xf4120080
++#define F0900_P1_TMGLOCK_QUALITY 0xf4120060
++#define F0900_P1_SDVBS1_ENABLE 0xf4120010
++#define F0900_P1_LOCK_DEFINITIF 0xf4120008
++#define F0900_P1_TIMING_IS_LOCKED 0xf4120004
++#define F0900_P1_COARSE_TMGLOCK 0xf4120002
++#define F0900_P1_COARSE_CARLOCK 0xf4120001
++
++/*P1_DSTATUS2*/
++#define R0900_P1_DSTATUS2 0xf413
++#define F0900_P1_DEMOD_DELOCK 0xf4130080
++#define F0900_P1_DEMOD_TIMEOUT 0xf4130040
++#define F0900_P1_MODCODRQ_SYNCTAG 0xf4130020
++#define F0900_P1_POLYPH_SATEVENT 0xf4130010
++#define F0900_P1_AGC1_NOSIGNALACK 0xf4130008
++#define F0900_P1_AGC2_OVERFLOW 0xf4130004
++#define F0900_P1_CFR_OVERFLOW 0xf4130002
++#define F0900_P1_GAMMA_OVERUNDER 0xf4130001
++
++/*P1_DMDCFGMD*/
++#define R0900_P1_DMDCFGMD 0xf414
++#define F0900_P1_DVBS2_ENABLE 0xf4140080
++#define F0900_P1_DVBS1_ENABLE 0xf4140040
++#define F0900_P1_CFR_AUTOSCAN 0xf4140020
++#define F0900_P1_SCAN_ENABLE 0xf4140010
++#define F0900_P1_TUN_AUTOSCAN 0xf4140008
++#define F0900_P1_NOFORCE_RELOCK 0xf4140004
++#define F0900_P1_TUN_RNG 0xf4140003
++
++/*P1_DMDCFG2*/
++#define R0900_P1_DMDCFG2 0xf415
++#define F0900_P1_AGC1_WAITLOCK 0xf4150080
++#define F0900_P1_S1S2_SEQUENTIAL 0xf4150040
++#define F0900_P1_OVERFLOW_TIMEOUT 0xf4150020
++#define F0900_P1_SCANFAIL_TIMEOUT 0xf4150010
++#define F0900_P1_DMDTOUT_BACK 0xf4150008
++#define F0900_P1_CARLOCK_S1ENABLE 0xf4150004
++#define F0900_P1_COARSE_LK3MODE 0xf4150002
++#define F0900_P1_COARSE_LK2MODE 0xf4150001
++
++/*P1_DMDISTATE*/
++#define R0900_P1_DMDISTATE 0xf416
++#define F0900_P1_I2C_NORESETDMODE 0xf4160080
++#define F0900_P1_FORCE_ETAPED 0xf4160040
++#define F0900_P1_SDMDRST_DIRCLK 0xf4160020
++#define F0900_P1_I2C_DEMOD_MODE 0xf416001f
++
++/*P1_DMDT0M*/
++#define R0900_P1_DMDT0M 0xf417
++#define F0900_P1_DMDT0_MIN 0xf41700ff
++
++/*P1_DMDSTATE*/
++#define R0900_P1_DMDSTATE 0xf41b
++#define F0900_P1_DEMOD_LOCKED 0xf41b0080
++#define F0900_P1_HEADER_MODE 0xf41b0060
++#define F0900_P1_DEMOD_MODE 0xf41b001f
++
++/*P1_DMDFLYW*/
++#define R0900_P1_DMDFLYW 0xf41c
++#define F0900_P1_I2C_IRQVAL 0xf41c00f0
++#define F0900_P1_FLYWHEEL_CPT 0xf41c000f
++
++/*P1_DSTATUS3*/
++#define R0900_P1_DSTATUS3 0xf41d
++#define F0900_P1_CFR_ZIGZAG 0xf41d0080
++#define F0900_P1_DEMOD_CFGMODE 0xf41d0060
++#define F0900_P1_GAMMA_LOWBAUDRATE 0xf41d0010
++#define F0900_P1_RELOCK_MODE 0xf41d0008
++#define F0900_P1_DEMOD_FAIL 0xf41d0004
++#define F0900_P1_ETAPE1A_DVBXMEM 0xf41d0003
++
++/*P1_DMDCFG3*/
++#define R0900_P1_DMDCFG3 0xf41e
++#define F0900_P1_DVBS1_TMGWAIT 0xf41e0080
++#define F0900_P1_NO_BWCENTERING 0xf41e0040
++#define F0900_P1_INV_SEQSRCH 0xf41e0020
++#define F0900_P1_DIS_SFRUPLOW_TRK 0xf41e0010
++#define F0900_P1_NOSTOP_FIFOFULL 0xf41e0008
++#define F0900_P1_LOCKTIME_MODE 0xf41e0007
++
++/*P1_DMDCFG4*/
++#define R0900_P1_DMDCFG4 0xf41f
++#define F0900_P1_TUNER_NRELAUNCH 0xf41f0008
++#define F0900_P1_DIS_CLKENABLE 0xf41f0004
++#define F0900_P1_DIS_HDRDIVLOCK 0xf41f0002
++#define F0900_P1_NO_TNRWBINIT 0xf41f0001
++
++/*P1_CORRELMANT*/
++#define R0900_P1_CORRELMANT 0xf420
++#define F0900_P1_CORREL_MANT 0xf42000ff
++
++/*P1_CORRELABS*/
++#define R0900_P1_CORRELABS 0xf421
++#define F0900_P1_CORREL_ABS 0xf42100ff
++
++/*P1_CORRELEXP*/
++#define R0900_P1_CORRELEXP 0xf422
++#define F0900_P1_CORREL_ABSEXP 0xf42200f0
++#define F0900_P1_CORREL_EXP 0xf422000f
++
++/*P1_PLHMODCOD*/
++#define R0900_P1_PLHMODCOD 0xf424
++#define F0900_P1_SPECINV_DEMOD 0xf4240080
++#define F0900_P1_PLH_MODCOD 0xf424007c
++#define F0900_P1_PLH_TYPE 0xf4240003
++
++/*P1_AGCK32*/
++#define R0900_P1_AGCK32 0xf42b
++#define F0900_P1_R3ADJOFF_32APSK 0xf42b0080
++#define F0900_P1_R2ADJOFF_32APSK 0xf42b0040
++#define F0900_P1_R1ADJOFF_32APSK 0xf42b0020
++#define F0900_P1_RADJ_32APSK 0xf42b001f
++
++/*P1_AGC2O*/
++#define R0900_P1_AGC2O 0xf42c
++#define F0900_P1_AGC2REF_ADJUSTING 0xf42c0080
++#define F0900_P1_AGC2_COARSEFAST 0xf42c0040
++#define F0900_P1_AGC2_LKSQRT 0xf42c0020
++#define F0900_P1_AGC2_LKMODE 0xf42c0010
++#define F0900_P1_AGC2_LKEQUA 0xf42c0008
++#define F0900_P1_AGC2_COEF 0xf42c0007
++
++/*P1_AGC2REF*/
++#define R0900_P1_AGC2REF 0xf42d
++#define F0900_P1_AGC2_REF 0xf42d00ff
++
++/*P1_AGC1ADJ*/
++#define R0900_P1_AGC1ADJ 0xf42e
++#define F0900_P1_AGC1ADJ_MANUAL 0xf42e0080
++#define F0900_P1_AGC1_ADJUSTED 0xf42e017f
++
++/*P1_AGC2I1*/
++#define R0900_P1_AGC2I1 0xf436
++#define F0900_P1_AGC2_INTEGRATOR1 0xf43600ff
++
++/*P1_AGC2I0*/
++#define R0900_P1_AGC2I0 0xf437
++#define F0900_P1_AGC2_INTEGRATOR0 0xf43700ff
++
++/*P1_CARCFG*/
++#define R0900_P1_CARCFG 0xf438
++#define F0900_P1_CFRUPLOW_AUTO 0xf4380080
++#define F0900_P1_CFRUPLOW_TEST 0xf4380040
++#define F0900_P1_EN_CAR2CENTER 0xf4380020
++#define F0900_P1_CARHDR_NODIV8 0xf4380010
++#define F0900_P1_I2C_ROTA 0xf4380008
++#define F0900_P1_ROTAON 0xf4380004
++#define F0900_P1_PH_DET_ALGO 0xf4380003
++
++/*P1_ACLC*/
++#define R0900_P1_ACLC 0xf439
++#define F0900_P1_STOP_S2ALPHA 0xf43900c0
++#define F0900_P1_CAR_ALPHA_MANT 0xf4390030
++#define F0900_P1_CAR_ALPHA_EXP 0xf439000f
++
++/*P1_BCLC*/
++#define R0900_P1_BCLC 0xf43a
++#define F0900_P1_STOP_S2BETA 0xf43a00c0
++#define F0900_P1_CAR_BETA_MANT 0xf43a0030
++#define F0900_P1_CAR_BETA_EXP 0xf43a000f
++
++/*P1_CARFREQ*/
++#define R0900_P1_CARFREQ 0xf43d
++#define F0900_P1_KC_COARSE_EXP 0xf43d00f0
++#define F0900_P1_BETA_FREQ 0xf43d000f
++
++/*P1_CARHDR*/
++#define R0900_P1_CARHDR 0xf43e
++#define F0900_P1_K_FREQ_HDR 0xf43e00ff
++
++/*P1_LDT*/
++#define R0900_P1_LDT 0xf43f
++#define F0900_P1_CARLOCK_THRES 0xf43f01ff
++
++/*P1_LDT2*/
++#define R0900_P1_LDT2 0xf440
++#define F0900_P1_CARLOCK_THRES2 0xf44001ff
++
++/*P1_CFRICFG*/
++#define R0900_P1_CFRICFG 0xf441
++#define F0900_P1_CFRINIT_UNVALRNG 0xf4410080
++#define F0900_P1_CFRINIT_LUNVALCPT 0xf4410040
++#define F0900_P1_CFRINIT_ABORTDBL 0xf4410020
++#define F0900_P1_CFRINIT_ABORTPRED 0xf4410010
++#define F0900_P1_CFRINIT_UNVALSKIP 0xf4410008
++#define F0900_P1_CFRINIT_CSTINC 0xf4410004
++#define F0900_P1_NEG_CFRSTEP 0xf4410001
++
++/*P1_CFRUP1*/
++#define R0900_P1_CFRUP1 0xf442
++#define F0900_P1_CFR_UP1 0xf44201ff
++
++/*P1_CFRUP0*/
++#define R0900_P1_CFRUP0 0xf443
++#define F0900_P1_CFR_UP0 0xf44300ff
++
++/*P1_CFRLOW1*/
++#define R0900_P1_CFRLOW1 0xf446
++#define F0900_P1_CFR_LOW1 0xf44601ff
++
++/*P1_CFRLOW0*/
++#define R0900_P1_CFRLOW0 0xf447
++#define F0900_P1_CFR_LOW0 0xf44700ff
++
++/*P1_CFRINIT1*/
++#define R0900_P1_CFRINIT1 0xf448
++#define F0900_P1_CFR_INIT1 0xf44801ff
++
++/*P1_CFRINIT0*/
++#define R0900_P1_CFRINIT0 0xf449
++#define F0900_P1_CFR_INIT0 0xf44900ff
++
++/*P1_CFRINC1*/
++#define R0900_P1_CFRINC1 0xf44a
++#define F0900_P1_MANUAL_CFRINC 0xf44a0080
++#define F0900_P1_CFR_INC1 0xf44a017f
++
++/*P1_CFRINC0*/
++#define R0900_P1_CFRINC0 0xf44b
++#define F0900_P1_CFR_INC0 0xf44b00f0
++
++/*P1_CFR2*/
++#define R0900_P1_CFR2 0xf44c
++#define F0900_P1_CAR_FREQ2 0xf44c01ff
++
++/*P1_CFR1*/
++#define R0900_P1_CFR1 0xf44d
++#define F0900_P1_CAR_FREQ1 0xf44d00ff
++
++/*P1_CFR0*/
++#define R0900_P1_CFR0 0xf44e
++#define F0900_P1_CAR_FREQ0 0xf44e00ff
++
++/*P1_LDI*/
++#define R0900_P1_LDI 0xf44f
++#define F0900_P1_LOCK_DET_INTEGR 0xf44f01ff
++
++/*P1_TMGCFG*/
++#define R0900_P1_TMGCFG 0xf450
++#define F0900_P1_TMGLOCK_BETA 0xf45000c0
++#define F0900_P1_NOTMG_GROUPDELAY 0xf4500020
++#define F0900_P1_DO_TIMING_CORR 0xf4500010
++#define F0900_P1_MANUAL_SCAN 0xf450000c
++#define F0900_P1_TMG_MINFREQ 0xf4500003
++
++/*P1_RTC*/
++#define R0900_P1_RTC 0xf451
++#define F0900_P1_TMGALPHA_EXP 0xf45100f0
++#define F0900_P1_TMGBETA_EXP 0xf451000f
++
++/*P1_RTCS2*/
++#define R0900_P1_RTCS2 0xf452
++#define F0900_P1_TMGALPHAS2_EXP 0xf45200f0
++#define F0900_P1_TMGBETAS2_EXP 0xf452000f
++
++/*P1_TMGTHRISE*/
++#define R0900_P1_TMGTHRISE 0xf453
++#define F0900_P1_TMGLOCK_THRISE 0xf45300ff
++
++/*P1_TMGTHFALL*/
++#define R0900_P1_TMGTHFALL 0xf454
++#define F0900_P1_TMGLOCK_THFALL 0xf45400ff
++
++/*P1_SFRUPRATIO*/
++#define R0900_P1_SFRUPRATIO 0xf455
++#define F0900_P1_SFR_UPRATIO 0xf45500ff
++
++/*P1_SFRLOWRATIO*/
++#define R0900_P1_SFRLOWRATIO 0xf456
++#define F0900_P1_SFR_LOWRATIO 0xf45600ff
++
++/*P1_KREFTMG*/
++#define R0900_P1_KREFTMG 0xf458
++#define F0900_P1_KREF_TMG 0xf45800ff
++
++/*P1_SFRSTEP*/
++#define R0900_P1_SFRSTEP 0xf459
++#define F0900_P1_SFR_SCANSTEP 0xf45900f0
++#define F0900_P1_SFR_CENTERSTEP 0xf459000f
++
++/*P1_TMGCFG2*/
++#define R0900_P1_TMGCFG2 0xf45a
++#define F0900_P1_DIS_AUTOSAMP 0xf45a0008
++#define F0900_P1_SCANINIT_QUART 0xf45a0004
++#define F0900_P1_NOTMG_DVBS1DERAT 0xf45a0002
++#define F0900_P1_SFRRATIO_FINE 0xf45a0001
++
++/*P1_SFRINIT1*/
++#define R0900_P1_SFRINIT1 0xf45e
++#define F0900_P1_SFR_INIT1 0xf45e00ff
++
++/*P1_SFRINIT0*/
++#define R0900_P1_SFRINIT0 0xf45f
++#define F0900_P1_SFR_INIT0 0xf45f00ff
++
++/*P1_SFRUP1*/
++#define R0900_P1_SFRUP1 0xf460
++#define F0900_P1_AUTO_GUP 0xf4600080
++#define F0900_P1_SYMB_FREQ_UP1 0xf460007f
++
++/*P1_SFRUP0*/
++#define R0900_P1_SFRUP0 0xf461
++#define F0900_P1_SYMB_FREQ_UP0 0xf46100ff
++
++/*P1_SFRLOW1*/
++#define R0900_P1_SFRLOW1 0xf462
++#define F0900_P1_AUTO_GLOW 0xf4620080
++#define F0900_P1_SYMB_FREQ_LOW1 0xf462007f
++
++/*P1_SFRLOW0*/
++#define R0900_P1_SFRLOW0 0xf463
++#define F0900_P1_SYMB_FREQ_LOW0 0xf46300ff
++
++/*P1_SFR3*/
++#define R0900_P1_SFR3 0xf464
++#define F0900_P1_SYMB_FREQ3 0xf46400ff
++
++/*P1_SFR2*/
++#define R0900_P1_SFR2 0xf465
++#define F0900_P1_SYMB_FREQ2 0xf46500ff
++
++/*P1_SFR1*/
++#define R0900_P1_SFR1 0xf466
++#define F0900_P1_SYMB_FREQ1 0xf46600ff
++
++/*P1_SFR0*/
++#define R0900_P1_SFR0 0xf467
++#define F0900_P1_SYMB_FREQ0 0xf46700ff
++
++/*P1_TMGREG2*/
++#define R0900_P1_TMGREG2 0xf468
++#define F0900_P1_TMGREG2 0xf46800ff
++
++/*P1_TMGREG1*/
++#define R0900_P1_TMGREG1 0xf469
++#define F0900_P1_TMGREG1 0xf46900ff
++
++/*P1_TMGREG0*/
++#define R0900_P1_TMGREG0 0xf46a
++#define F0900_P1_TMGREG0 0xf46a00ff
++
++/*P1_TMGLOCK1*/
++#define R0900_P1_TMGLOCK1 0xf46b
++#define F0900_P1_TMGLOCK_LEVEL1 0xf46b01ff
++
++/*P1_TMGLOCK0*/
++#define R0900_P1_TMGLOCK0 0xf46c
++#define F0900_P1_TMGLOCK_LEVEL0 0xf46c00ff
++
++/*P1_TMGOBS*/
++#define R0900_P1_TMGOBS 0xf46d
++#define F0900_P1_ROLLOFF_STATUS 0xf46d00c0
++#define F0900_P1_SCAN_SIGN 0xf46d0030
++#define F0900_P1_TMG_SCANNING 0xf46d0008
++#define F0900_P1_CHCENTERING_MODE 0xf46d0004
++#define F0900_P1_TMG_SCANFAIL 0xf46d0002
++
++/*P1_EQUALCFG*/
++#define R0900_P1_EQUALCFG 0xf46f
++#define F0900_P1_NOTMG_NEGALWAIT 0xf46f0080
++#define F0900_P1_EQUAL_ON 0xf46f0040
++#define F0900_P1_SEL_EQUALCOR 0xf46f0038
++#define F0900_P1_MU_EQUALDFE 0xf46f0007
++
++/*P1_EQUAI1*/
++#define R0900_P1_EQUAI1 0xf470
++#define F0900_P1_EQUA_ACCI1 0xf47001ff
++
++/*P1_EQUAQ1*/
++#define R0900_P1_EQUAQ1 0xf471
++#define F0900_P1_EQUA_ACCQ1 0xf47101ff
++
++/*P1_EQUAI2*/
++#define R0900_P1_EQUAI2 0xf472
++#define F0900_P1_EQUA_ACCI2 0xf47201ff
++
++/*P1_EQUAQ2*/
++#define R0900_P1_EQUAQ2 0xf473
++#define F0900_P1_EQUA_ACCQ2 0xf47301ff
++
++/*P1_EQUAI3*/
++#define R0900_P1_EQUAI3 0xf474
++#define F0900_P1_EQUA_ACCI3 0xf47401ff
++
++/*P1_EQUAQ3*/
++#define R0900_P1_EQUAQ3 0xf475
++#define F0900_P1_EQUA_ACCQ3 0xf47501ff
++
++/*P1_EQUAI4*/
++#define R0900_P1_EQUAI4 0xf476
++#define F0900_P1_EQUA_ACCI4 0xf47601ff
++
++/*P1_EQUAQ4*/
++#define R0900_P1_EQUAQ4 0xf477
++#define F0900_P1_EQUA_ACCQ4 0xf47701ff
++
++/*P1_EQUAI5*/
++#define R0900_P1_EQUAI5 0xf478
++#define F0900_P1_EQUA_ACCI5 0xf47801ff
++
++/*P1_EQUAQ5*/
++#define R0900_P1_EQUAQ5 0xf479
++#define F0900_P1_EQUA_ACCQ5 0xf47901ff
++
++/*P1_EQUAI6*/
++#define R0900_P1_EQUAI6 0xf47a
++#define F0900_P1_EQUA_ACCI6 0xf47a01ff
++
++/*P1_EQUAQ6*/
++#define R0900_P1_EQUAQ6 0xf47b
++#define F0900_P1_EQUA_ACCQ6 0xf47b01ff
++
++/*P1_EQUAI7*/
++#define R0900_P1_EQUAI7 0xf47c
++#define F0900_P1_EQUA_ACCI7 0xf47c01ff
++
++/*P1_EQUAQ7*/
++#define R0900_P1_EQUAQ7 0xf47d
++#define F0900_P1_EQUA_ACCQ7 0xf47d01ff
++
++/*P1_EQUAI8*/
++#define R0900_P1_EQUAI8 0xf47e
++#define F0900_P1_EQUA_ACCI8 0xf47e01ff
++
++/*P1_EQUAQ8*/
++#define R0900_P1_EQUAQ8 0xf47f
++#define F0900_P1_EQUA_ACCQ8 0xf47f01ff
++
++/*P1_NNOSDATAT1*/
++#define R0900_P1_NNOSDATAT1 0xf480
++#define F0900_P1_NOSDATAT_NORMED1 0xf48000ff
++
++/*P1_NNOSDATAT0*/
++#define R0900_P1_NNOSDATAT0 0xf481
++#define F0900_P1_NOSDATAT_NORMED0 0xf48100ff
++
++/*P1_NNOSDATA1*/
++#define R0900_P1_NNOSDATA1 0xf482
++#define F0900_P1_NOSDATA_NORMED1 0xf48200ff
++
++/*P1_NNOSDATA0*/
++#define R0900_P1_NNOSDATA0 0xf483
++#define F0900_P1_NOSDATA_NORMED0 0xf48300ff
++
++/*P1_NNOSPLHT1*/
++#define R0900_P1_NNOSPLHT1 0xf484
++#define F0900_P1_NOSPLHT_NORMED1 0xf48400ff
++
++/*P1_NNOSPLHT0*/
++#define R0900_P1_NNOSPLHT0 0xf485
++#define F0900_P1_NOSPLHT_NORMED0 0xf48500ff
++
++/*P1_NNOSPLH1*/
++#define R0900_P1_NNOSPLH1 0xf486
++#define F0900_P1_NOSPLH_NORMED1 0xf48600ff
++
++/*P1_NNOSPLH0*/
++#define R0900_P1_NNOSPLH0 0xf487
++#define F0900_P1_NOSPLH_NORMED0 0xf48700ff
++
++/*P1_NOSDATAT1*/
++#define R0900_P1_NOSDATAT1 0xf488
++#define F0900_P1_NOSDATAT_UNNORMED1 0xf48800ff
++
++/*P1_NOSDATAT0*/
++#define R0900_P1_NOSDATAT0 0xf489
++#define F0900_P1_NOSDATAT_UNNORMED0 0xf48900ff
++
++/*P1_NOSDATA1*/
++#define R0900_P1_NOSDATA1 0xf48a
++#define F0900_P1_NOSDATA_UNNORMED1 0xf48a00ff
++
++/*P1_NOSDATA0*/
++#define R0900_P1_NOSDATA0 0xf48b
++#define F0900_P1_NOSDATA_UNNORMED0 0xf48b00ff
++
++/*P1_NOSPLHT1*/
++#define R0900_P1_NOSPLHT1 0xf48c
++#define F0900_P1_NOSPLHT_UNNORMED1 0xf48c00ff
++
++/*P1_NOSPLHT0*/
++#define R0900_P1_NOSPLHT0 0xf48d
++#define F0900_P1_NOSPLHT_UNNORMED0 0xf48d00ff
++
++/*P1_NOSPLH1*/
++#define R0900_P1_NOSPLH1 0xf48e
++#define F0900_P1_NOSPLH_UNNORMED1 0xf48e00ff
++
++/*P1_NOSPLH0*/
++#define R0900_P1_NOSPLH0 0xf48f
++#define F0900_P1_NOSPLH_UNNORMED0 0xf48f00ff
++
++/*P1_CAR2CFG*/
++#define R0900_P1_CAR2CFG 0xf490
++#define F0900_P1_DESCRAMB_OFF 0xf4900080
++#define F0900_P1_PN4_SELECT 0xf4900040
++#define F0900_P1_CFR2_STOPDVBS1 0xf4900020
++#define F0900_P1_STOP_CFR2UPDATE 0xf4900010
++#define F0900_P1_STOP_NCO2UPDATE 0xf4900008
++#define F0900_P1_ROTA2ON 0xf4900004
++#define F0900_P1_PH_DET_ALGO2 0xf4900003
++
++/*P1_ACLC2*/
++#define R0900_P1_ACLC2 0xf491
++#define F0900_P1_CAR2_PUNCT_ADERAT 0xf4910040
++#define F0900_P1_CAR2_ALPHA_MANT 0xf4910030
++#define F0900_P1_CAR2_ALPHA_EXP 0xf491000f
++
++/*P1_BCLC2*/
++#define R0900_P1_BCLC2 0xf492
++#define F0900_P1_DVBS2_NIP 0xf4920080
++#define F0900_P1_CAR2_PUNCT_BDERAT 0xf4920040
++#define F0900_P1_CAR2_BETA_MANT 0xf4920030
++#define F0900_P1_CAR2_BETA_EXP 0xf492000f
++
++/*P1_CFR22*/
++#define R0900_P1_CFR22 0xf493
++#define F0900_P1_CAR2_FREQ2 0xf49301ff
++
++/*P1_CFR21*/
++#define R0900_P1_CFR21 0xf494
++#define F0900_P1_CAR2_FREQ1 0xf49400ff
++
++/*P1_CFR20*/
++#define R0900_P1_CFR20 0xf495
++#define F0900_P1_CAR2_FREQ0 0xf49500ff
++
++/*P1_ACLC2S2Q*/
++#define R0900_P1_ACLC2S2Q 0xf497
++#define F0900_P1_ENAB_SPSKSYMB 0xf4970080
++#define F0900_P1_CAR2S2_QADERAT 0xf4970040
++#define F0900_P1_CAR2S2_Q_ALPH_M 0xf4970030
++#define F0900_P1_CAR2S2_Q_ALPH_E 0xf497000f
++
++/*P1_ACLC2S28*/
++#define R0900_P1_ACLC2S28 0xf498
++#define F0900_P1_OLDI3Q_MODE 0xf4980080
++#define F0900_P1_CAR2S2_8ADERAT 0xf4980040
++#define F0900_P1_CAR2S2_8_ALPH_M 0xf4980030
++#define F0900_P1_CAR2S2_8_ALPH_E 0xf498000f
++
++/*P1_ACLC2S216A*/
++#define R0900_P1_ACLC2S216A 0xf499
++#define F0900_P1_CAR2S2_16ADERAT 0xf4990040
++#define F0900_P1_CAR2S2_16A_ALPH_M 0xf4990030
++#define F0900_P1_CAR2S2_16A_ALPH_E 0xf499000f
++
++/*P1_ACLC2S232A*/
++#define R0900_P1_ACLC2S232A 0xf49a
++#define F0900_P1_CAR2S2_32ADERAT 0xf49a0040
++#define F0900_P1_CAR2S2_32A_ALPH_M 0xf49a0030
++#define F0900_P1_CAR2S2_32A_ALPH_E 0xf49a000f
++
++/*P1_BCLC2S2Q*/
++#define R0900_P1_BCLC2S2Q 0xf49c
++#define F0900_P1_DVBS2S2Q_NIP 0xf49c0080
++#define F0900_P1_CAR2S2_QBDERAT 0xf49c0040
++#define F0900_P1_CAR2S2_Q_BETA_M 0xf49c0030
++#define F0900_P1_CAR2S2_Q_BETA_E 0xf49c000f
++
++/*P1_BCLC2S28*/
++#define R0900_P1_BCLC2S28 0xf49d
++#define F0900_P1_DVBS2S28_NIP 0xf49d0080
++#define F0900_P1_CAR2S2_8BDERAT 0xf49d0040
++#define F0900_P1_CAR2S2_8_BETA_M 0xf49d0030
++#define F0900_P1_CAR2S2_8_BETA_E 0xf49d000f
++
++/*P1_BCLC2S216A*/
++#define R0900_P1_BCLC2S216A 0xf49e
++#define F0900_P1_DVBS2S216A_NIP 0xf49e0080
++#define F0900_P1_CAR2S2_16BDERAT 0xf49e0040
++#define F0900_P1_CAR2S2_16A_BETA_M 0xf49e0030
++#define F0900_P1_CAR2S2_16A_BETA_E 0xf49e000f
++
++/*P1_BCLC2S232A*/
++#define R0900_P1_BCLC2S232A 0xf49f
++#define F0900_P1_DVBS2S232A_NIP 0xf49f0080
++#define F0900_P1_CAR2S2_32BDERAT 0xf49f0040
++#define F0900_P1_CAR2S2_32A_BETA_M 0xf49f0030
++#define F0900_P1_CAR2S2_32A_BETA_E 0xf49f000f
++
++/*P1_PLROOT2*/
++#define R0900_P1_PLROOT2 0xf4ac
++#define F0900_P1_SHORTFR_DISABLE 0xf4ac0080
++#define F0900_P1_LONGFR_DISABLE 0xf4ac0040
++#define F0900_P1_DUMMYPL_DISABLE 0xf4ac0020
++#define F0900_P1_SHORTFR_AVOID 0xf4ac0010
++#define F0900_P1_PLSCRAMB_MODE 0xf4ac000c
++#define F0900_P1_PLSCRAMB_ROOT2 0xf4ac0003
++
++/*P1_PLROOT1*/
++#define R0900_P1_PLROOT1 0xf4ad
++#define F0900_P1_PLSCRAMB_ROOT1 0xf4ad00ff
++
++/*P1_PLROOT0*/
++#define R0900_P1_PLROOT0 0xf4ae
++#define F0900_P1_PLSCRAMB_ROOT0 0xf4ae00ff
++
++/*P1_MODCODLST0*/
++#define R0900_P1_MODCODLST0 0xf4b0
++#define F0900_P1_EN_TOKEN31 0xf4b00080
++#define F0900_P1_SYNCTAG_SELECT 0xf4b00040
++#define F0900_P1_MODCODRQ_MODE 0xf4b00030
++
++/*P1_MODCODLST1*/
++#define R0900_P1_MODCODLST1 0xf4b1
++#define F0900_P1_DIS_MODCOD29 0xf4b100f0
++#define F0900_P1_DIS_32PSK_9_10 0xf4b1000f
++
++/*P1_MODCODLST2*/
++#define R0900_P1_MODCODLST2 0xf4b2
++#define F0900_P1_DIS_32PSK_8_9 0xf4b200f0
++#define F0900_P1_DIS_32PSK_5_6 0xf4b2000f
++
++/*P1_MODCODLST3*/
++#define R0900_P1_MODCODLST3 0xf4b3
++#define F0900_P1_DIS_32PSK_4_5 0xf4b300f0
++#define F0900_P1_DIS_32PSK_3_4 0xf4b3000f
++
++/*P1_MODCODLST4*/
++#define R0900_P1_MODCODLST4 0xf4b4
++#define F0900_P1_DIS_16PSK_9_10 0xf4b400f0
++#define F0900_P1_DIS_16PSK_8_9 0xf4b4000f
++
++/*P1_MODCODLST5*/
++#define R0900_P1_MODCODLST5 0xf4b5
++#define F0900_P1_DIS_16PSK_5_6 0xf4b500f0
++#define F0900_P1_DIS_16PSK_4_5 0xf4b5000f
++
++/*P1_MODCODLST6*/
++#define R0900_P1_MODCODLST6 0xf4b6
++#define F0900_P1_DIS_16PSK_3_4 0xf4b600f0
++#define F0900_P1_DIS_16PSK_2_3 0xf4b6000f
++
++/*P1_MODCODLST7*/
++#define R0900_P1_MODCODLST7 0xf4b7
++#define F0900_P1_DIS_8P_9_10 0xf4b700f0
++#define F0900_P1_DIS_8P_8_9 0xf4b7000f
++
++/*P1_MODCODLST8*/
++#define R0900_P1_MODCODLST8 0xf4b8
++#define F0900_P1_DIS_8P_5_6 0xf4b800f0
++#define F0900_P1_DIS_8P_3_4 0xf4b8000f
++
++/*P1_MODCODLST9*/
++#define R0900_P1_MODCODLST9 0xf4b9
++#define F0900_P1_DIS_8P_2_3 0xf4b900f0
++#define F0900_P1_DIS_8P_3_5 0xf4b9000f
++
++/*P1_MODCODLSTA*/
++#define R0900_P1_MODCODLSTA 0xf4ba
++#define F0900_P1_DIS_QP_9_10 0xf4ba00f0
++#define F0900_P1_DIS_QP_8_9 0xf4ba000f
++
++/*P1_MODCODLSTB*/
++#define R0900_P1_MODCODLSTB 0xf4bb
++#define F0900_P1_DIS_QP_5_6 0xf4bb00f0
++#define F0900_P1_DIS_QP_4_5 0xf4bb000f
++
++/*P1_MODCODLSTC*/
++#define R0900_P1_MODCODLSTC 0xf4bc
++#define F0900_P1_DIS_QP_3_4 0xf4bc00f0
++#define F0900_P1_DIS_QP_2_3 0xf4bc000f
++
++/*P1_MODCODLSTD*/
++#define R0900_P1_MODCODLSTD 0xf4bd
++#define F0900_P1_DIS_QP_3_5 0xf4bd00f0
++#define F0900_P1_DIS_QP_1_2 0xf4bd000f
++
++/*P1_MODCODLSTE*/
++#define R0900_P1_MODCODLSTE 0xf4be
++#define F0900_P1_DIS_QP_2_5 0xf4be00f0
++#define F0900_P1_DIS_QP_1_3 0xf4be000f
++
++/*P1_MODCODLSTF*/
++#define R0900_P1_MODCODLSTF 0xf4bf
++#define F0900_P1_DIS_QP_1_4 0xf4bf00f0
++#define F0900_P1_DDEMOD_SET 0xf4bf0002
++#define F0900_P1_DDEMOD_MASK 0xf4bf0001
++
++/*P1_DMDRESCFG*/
++#define R0900_P1_DMDRESCFG 0xf4c6
++#define F0900_P1_DMDRES_RESET 0xf4c60080
++#define F0900_P1_DMDRES_NOISESQR 0xf4c60010
++#define F0900_P1_DMDRES_STRALL 0xf4c60008
++#define F0900_P1_DMDRES_NEWONLY 0xf4c60004
++#define F0900_P1_DMDRES_NOSTORE 0xf4c60002
++#define F0900_P1_DMDRES_AGC2MEM 0xf4c60001
++
++/*P1_DMDRESADR*/
++#define R0900_P1_DMDRESADR 0xf4c7
++#define F0900_P1_SUSP_PREDCANAL 0xf4c70080
++#define F0900_P1_DMDRES_VALIDCFR 0xf4c70040
++#define F0900_P1_DMDRES_MEMFULL 0xf4c70030
++#define F0900_P1_DMDRES_RESNBR 0xf4c7000f
++
++/*P1_DMDRESDATA7*/
++#define R0900_P1_DMDRESDATA7 0xf4c8
++#define F0900_P1_DMDRES_DATA7 0xf4c800ff
++
++/*P1_DMDRESDATA6*/
++#define R0900_P1_DMDRESDATA6 0xf4c9
++#define F0900_P1_DMDRES_DATA6 0xf4c900ff
++
++/*P1_DMDRESDATA5*/
++#define R0900_P1_DMDRESDATA5 0xf4ca
++#define F0900_P1_DMDRES_DATA5 0xf4ca00ff
++
++/*P1_DMDRESDATA4*/
++#define R0900_P1_DMDRESDATA4 0xf4cb
++#define F0900_P1_DMDRES_DATA4 0xf4cb00ff
++
++/*P1_DMDRESDATA3*/
++#define R0900_P1_DMDRESDATA3 0xf4cc
++#define F0900_P1_DMDRES_DATA3 0xf4cc00ff
++
++/*P1_DMDRESDATA2*/
++#define R0900_P1_DMDRESDATA2 0xf4cd
++#define F0900_P1_DMDRES_DATA2 0xf4cd00ff
++
++/*P1_DMDRESDATA1*/
++#define R0900_P1_DMDRESDATA1 0xf4ce
++#define F0900_P1_DMDRES_DATA1 0xf4ce00ff
++
++/*P1_DMDRESDATA0*/
++#define R0900_P1_DMDRESDATA0 0xf4cf
++#define F0900_P1_DMDRES_DATA0 0xf4cf00ff
++
++/*P1_FFEI1*/
++#define R0900_P1_FFEI1 0xf4d0
++#define F0900_P1_FFE_ACCI1 0xf4d001ff
++
++/*P1_FFEQ1*/
++#define R0900_P1_FFEQ1 0xf4d1
++#define F0900_P1_FFE_ACCQ1 0xf4d101ff
++
++/*P1_FFEI2*/
++#define R0900_P1_FFEI2 0xf4d2
++#define F0900_P1_FFE_ACCI2 0xf4d201ff
++
++/*P1_FFEQ2*/
++#define R0900_P1_FFEQ2 0xf4d3
++#define F0900_P1_FFE_ACCQ2 0xf4d301ff
++
++/*P1_FFEI3*/
++#define R0900_P1_FFEI3 0xf4d4
++#define F0900_P1_FFE_ACCI3 0xf4d401ff
++
++/*P1_FFEQ3*/
++#define R0900_P1_FFEQ3 0xf4d5
++#define F0900_P1_FFE_ACCQ3 0xf4d501ff
++
++/*P1_FFEI4*/
++#define R0900_P1_FFEI4 0xf4d6
++#define F0900_P1_FFE_ACCI4 0xf4d601ff
++
++/*P1_FFEQ4*/
++#define R0900_P1_FFEQ4 0xf4d7
++#define F0900_P1_FFE_ACCQ4 0xf4d701ff
++
++/*P1_FFECFG*/
++#define R0900_P1_FFECFG 0xf4d8
++#define F0900_P1_EQUALFFE_ON 0xf4d80040
++#define F0900_P1_EQUAL_USEDSYMB 0xf4d80030
++#define F0900_P1_MU_EQUALFFE 0xf4d80007
++
++/*P1_TNRCFG*/
++#define R0900_P1_TNRCFG 0xf4e0
++#define F0900_P1_TUN_ACKFAIL 0xf4e00080
++#define F0900_P1_TUN_TYPE 0xf4e00070
++#define F0900_P1_TUN_SECSTOP 0xf4e00008
++#define F0900_P1_TUN_VCOSRCH 0xf4e00004
++#define F0900_P1_TUN_MADDRESS 0xf4e00003
++
++/*P1_TNRCFG2*/
++#define R0900_P1_TNRCFG2 0xf4e1
++#define F0900_P1_TUN_IQSWAP 0xf4e10080
++#define F0900_P1_STB6110_STEP2MHZ 0xf4e10040
++#define F0900_P1_STB6120_DBLI2C 0xf4e10020
++#define F0900_P1_DIS_FCCK 0xf4e10010
++#define F0900_P1_DIS_LPEN 0xf4e10008
++#define F0900_P1_DIS_BWCALC 0xf4e10004
++#define F0900_P1_SHORT_WAITSTATES 0xf4e10002
++#define F0900_P1_DIS_2BWAGC1 0xf4e10001
++
++/*P1_TNRXTAL*/
++#define R0900_P1_TNRXTAL 0xf4e4
++#define F0900_P1_TUN_MCLKDECIMAL 0xf4e400e0
++#define F0900_P1_TUN_XTALFREQ 0xf4e4001f
++
++/*P1_TNRSTEPS*/
++#define R0900_P1_TNRSTEPS 0xf4e7
++#define F0900_P1_TUNER_BW1P6 0xf4e70080
++#define F0900_P1_BWINC_OFFSET 0xf4e70070
++#define F0900_P1_SOFTSTEP_RNG 0xf4e70008
++#define F0900_P1_TUN_BWOFFSET 0xf4e70107
++
++/*P1_TNRGAIN*/
++#define R0900_P1_TNRGAIN 0xf4e8
++#define F0900_P1_TUN_KDIVEN 0xf4e800c0
++#define F0900_P1_STB6X00_OCK 0xf4e80030
++#define F0900_P1_TUN_GAIN 0xf4e8000f
++
++/*P1_TNRRF1*/
++#define R0900_P1_TNRRF1 0xf4e9
++#define F0900_P1_TUN_RFFREQ2 0xf4e900ff
++
++/*P1_TNRRF0*/
++#define R0900_P1_TNRRF0 0xf4ea
++#define F0900_P1_TUN_RFFREQ1 0xf4ea00ff
++
++/*P1_TNRBW*/
++#define R0900_P1_TNRBW 0xf4eb
++#define F0900_P1_TUN_RFFREQ0 0xf4eb00c0
++#define F0900_P1_TUN_BW 0xf4eb003f
++
++/*P1_TNRADJ*/
++#define R0900_P1_TNRADJ 0xf4ec
++#define F0900_P1_STB61X0_RCLK 0xf4ec0080
++#define F0900_P1_STB61X0_CALTIME 0xf4ec0040
++#define F0900_P1_STB6X00_DLB 0xf4ec0038
++#define F0900_P1_STB6000_FCL 0xf4ec0007
++
++/*P1_TNRCTL2*/
++#define R0900_P1_TNRCTL2 0xf4ed
++#define F0900_P1_STB61X0_LCP1_RCCKOFF 0xf4ed0080
++#define F0900_P1_STB61X0_LCP0 0xf4ed0040
++#define F0900_P1_STB61X0_XTOUT_RFOUTS 0xf4ed0020
++#define F0900_P1_STB61X0_XTON_MCKDV 0xf4ed0010
++#define F0900_P1_STB61X0_CALOFF_DCOFF 0xf4ed0008
++#define F0900_P1_STB6110_LPT 0xf4ed0004
++#define F0900_P1_STB6110_RX 0xf4ed0002
++#define F0900_P1_STB6110_SYN 0xf4ed0001
++
++/*P1_TNRCFG3*/
++#define R0900_P1_TNRCFG3 0xf4ee
++#define F0900_P1_STB6120_DISCTRL1 0xf4ee0080
++#define F0900_P1_STB6120_INVORDER 0xf4ee0040
++#define F0900_P1_STB6120_ENCTRL6 0xf4ee0020
++#define F0900_P1_TUN_PLLFREQ 0xf4ee001c
++#define F0900_P1_TUN_I2CFREQ_MODE 0xf4ee0003
++
++/*P1_TNRLAUNCH*/
++#define R0900_P1_TNRLAUNCH 0xf4f0
++
++/*P1_TNRLD*/
++#define R0900_P1_TNRLD 0xf4f0
++#define F0900_P1_TUNLD_VCOING 0xf4f00080
++#define F0900_P1_TUN_REG1FAIL 0xf4f00040
++#define F0900_P1_TUN_REG2FAIL 0xf4f00020
++#define F0900_P1_TUN_REG3FAIL 0xf4f00010
++#define F0900_P1_TUN_REG4FAIL 0xf4f00008
++#define F0900_P1_TUN_REG5FAIL 0xf4f00004
++#define F0900_P1_TUN_BWING 0xf4f00002
++#define F0900_P1_TUN_LOCKED 0xf4f00001
++
++/*P1_TNROBSL*/
++#define R0900_P1_TNROBSL 0xf4f6
++#define F0900_P1_TUN_I2CABORTED 0xf4f60080
++#define F0900_P1_TUN_LPEN 0xf4f60040
++#define F0900_P1_TUN_FCCK 0xf4f60020
++#define F0900_P1_TUN_I2CLOCKED 0xf4f60010
++#define F0900_P1_TUN_PROGDONE 0xf4f6000c
++#define F0900_P1_TUN_RFRESTE1 0xf4f60003
++
++/*P1_TNRRESTE*/
++#define R0900_P1_TNRRESTE 0xf4f7
++#define F0900_P1_TUN_RFRESTE0 0xf4f700ff
++
++/*P1_SMAPCOEF7*/
++#define R0900_P1_SMAPCOEF7 0xf500
++#define F0900_P1_DIS_QSCALE 0xf5000080
++#define F0900_P1_SMAPCOEF_Q_LLR12 0xf500017f
++
++/*P1_SMAPCOEF6*/
++#define R0900_P1_SMAPCOEF6 0xf501
++#define F0900_P1_DIS_NEWSCALE 0xf5010008
++#define F0900_P1_ADJ_8PSKLLR1 0xf5010004
++#define F0900_P1_OLD_8PSKLLR1 0xf5010002
++#define F0900_P1_DIS_AB8PSK 0xf5010001
++
++/*P1_SMAPCOEF5*/
++#define R0900_P1_SMAPCOEF5 0xf502
++#define F0900_P1_DIS_8SCALE 0xf5020080
++#define F0900_P1_SMAPCOEF_8P_LLR23 0xf502017f
++
++/*P1_DMDPLHSTAT*/
++#define R0900_P1_DMDPLHSTAT 0xf520
++#define F0900_P1_PLH_STATISTIC 0xf52000ff
++
++/*P1_LOCKTIME3*/
++#define R0900_P1_LOCKTIME3 0xf522
++#define F0900_P1_DEMOD_LOCKTIME3 0xf52200ff
++
++/*P1_LOCKTIME2*/
++#define R0900_P1_LOCKTIME2 0xf523
++#define F0900_P1_DEMOD_LOCKTIME2 0xf52300ff
++
++/*P1_LOCKTIME1*/
++#define R0900_P1_LOCKTIME1 0xf524
++#define F0900_P1_DEMOD_LOCKTIME1 0xf52400ff
++
++/*P1_LOCKTIME0*/
++#define R0900_P1_LOCKTIME0 0xf525
++#define F0900_P1_DEMOD_LOCKTIME0 0xf52500ff
++
++/*P1_VITSCALE*/
++#define R0900_P1_VITSCALE 0xf532
++#define F0900_P1_NVTH_NOSRANGE 0xf5320080
++#define F0900_P1_VERROR_MAXMODE 0xf5320040
++#define F0900_P1_KDIV_MODE 0xf5320030
++#define F0900_P1_NSLOWSN_LOCKED 0xf5320008
++#define F0900_P1_DELOCK_PRFLOSS 0xf5320004
++#define F0900_P1_DIS_RSFLOCK 0xf5320002
++
++/*P1_FECM*/
++#define R0900_P1_FECM 0xf533
++#define F0900_P1_DSS_DVB 0xf5330080
++#define F0900_P1_DEMOD_BYPASS 0xf5330040
++#define F0900_P1_CMP_SLOWMODE 0xf5330020
++#define F0900_P1_DSS_SRCH 0xf5330010
++#define F0900_P1_DIFF_MODEVIT 0xf5330004
++#define F0900_P1_SYNCVIT 0xf5330002
++#define F0900_P1_IQINV 0xf5330001
++
++/*P1_VTH12*/
++#define R0900_P1_VTH12 0xf534
++#define F0900_P1_VTH12 0xf53400ff
++
++/*P1_VTH23*/
++#define R0900_P1_VTH23 0xf535
++#define F0900_P1_VTH23 0xf53500ff
++
++/*P1_VTH34*/
++#define R0900_P1_VTH34 0xf536
++#define F0900_P1_VTH34 0xf53600ff
++
++/*P1_VTH56*/
++#define R0900_P1_VTH56 0xf537
++#define F0900_P1_VTH56 0xf53700ff
++
++/*P1_VTH67*/
++#define R0900_P1_VTH67 0xf538
++#define F0900_P1_VTH67 0xf53800ff
++
++/*P1_VTH78*/
++#define R0900_P1_VTH78 0xf539
++#define F0900_P1_VTH78 0xf53900ff
++
++/*P1_VITCURPUN*/
++#define R0900_P1_VITCURPUN 0xf53a
++#define F0900_P1_VIT_MAPPING 0xf53a00e0
++#define F0900_P1_VIT_CURPUN 0xf53a001f
++
++/*P1_VERROR*/
++#define R0900_P1_VERROR 0xf53b
++#define F0900_P1_REGERR_VIT 0xf53b00ff
++
++/*P1_PRVIT*/
++#define R0900_P1_PRVIT 0xf53c
++#define F0900_P1_DIS_VTHLOCK 0xf53c0040
++#define F0900_P1_E7_8VIT 0xf53c0020
++#define F0900_P1_E6_7VIT 0xf53c0010
++#define F0900_P1_E5_6VIT 0xf53c0008
++#define F0900_P1_E3_4VIT 0xf53c0004
++#define F0900_P1_E2_3VIT 0xf53c0002
++#define F0900_P1_E1_2VIT 0xf53c0001
++
++/*P1_VAVSRVIT*/
++#define R0900_P1_VAVSRVIT 0xf53d
++#define F0900_P1_AMVIT 0xf53d0080
++#define F0900_P1_FROZENVIT 0xf53d0040
++#define F0900_P1_SNVIT 0xf53d0030
++#define F0900_P1_TOVVIT 0xf53d000c
++#define F0900_P1_HYPVIT 0xf53d0003
++
++/*P1_VSTATUSVIT*/
++#define R0900_P1_VSTATUSVIT 0xf53e
++#define F0900_P1_VITERBI_ON 0xf53e0080
++#define F0900_P1_END_LOOPVIT 0xf53e0040
++#define F0900_P1_VITERBI_DEPRF 0xf53e0020
++#define F0900_P1_PRFVIT 0xf53e0010
++#define F0900_P1_LOCKEDVIT 0xf53e0008
++#define F0900_P1_VITERBI_DELOCK 0xf53e0004
++#define F0900_P1_VIT_DEMODSEL 0xf53e0002
++#define F0900_P1_VITERBI_COMPOUT 0xf53e0001
++
++/*P1_VTHINUSE*/
++#define R0900_P1_VTHINUSE 0xf53f
++#define F0900_P1_VIT_INUSE 0xf53f00ff
++
++/*P1_KDIV12*/
++#define R0900_P1_KDIV12 0xf540
++#define F0900_P1_KDIV12_MANUAL 0xf5400080
++#define F0900_P1_K_DIVIDER_12 0xf540007f
++
++/*P1_KDIV23*/
++#define R0900_P1_KDIV23 0xf541
++#define F0900_P1_KDIV23_MANUAL 0xf5410080
++#define F0900_P1_K_DIVIDER_23 0xf541007f
++
++/*P1_KDIV34*/
++#define R0900_P1_KDIV34 0xf542
++#define F0900_P1_KDIV34_MANUAL 0xf5420080
++#define F0900_P1_K_DIVIDER_34 0xf542007f
++
++/*P1_KDIV56*/
++#define R0900_P1_KDIV56 0xf543
++#define F0900_P1_KDIV56_MANUAL 0xf5430080
++#define F0900_P1_K_DIVIDER_56 0xf543007f
++
++/*P1_KDIV67*/
++#define R0900_P1_KDIV67 0xf544
++#define F0900_P1_KDIV67_MANUAL 0xf5440080
++#define F0900_P1_K_DIVIDER_67 0xf544007f
++
++/*P1_KDIV78*/
++#define R0900_P1_KDIV78 0xf545
++#define F0900_P1_KDIV78_MANUAL 0xf5450080
++#define F0900_P1_K_DIVIDER_78 0xf545007f
++
++/*P1_PDELCTRL1*/
++#define R0900_P1_PDELCTRL1 0xf550
++#define F0900_P1_INV_MISMASK 0xf5500080
++#define F0900_P1_FORCE_ACCEPTED 0xf5500040
++#define F0900_P1_FILTER_EN 0xf5500020
++#define F0900_P1_FORCE_PKTDELINUSE 0xf5500010
++#define F0900_P1_HYSTEN 0xf5500008
++#define F0900_P1_HYSTSWRST 0xf5500004
++#define F0900_P1_EN_MIS00 0xf5500002
++#define F0900_P1_ALGOSWRST 0xf5500001
++
++/*P1_PDELCTRL2*/
++#define R0900_P1_PDELCTRL2 0xf551
++#define F0900_P1_FORCE_CONTINUOUS 0xf5510080
++#define F0900_P1_RESET_UPKO_COUNT 0xf5510040
++#define F0900_P1_USER_PKTDELIN_NB 0xf5510020
++#define F0900_P1_FORCE_LOCKED 0xf5510010
++#define F0900_P1_DATA_UNBBSCRAM 0xf5510008
++#define F0900_P1_FORCE_LONGPKT 0xf5510004
++#define F0900_P1_FRAME_MODE 0xf5510002
++
++/*P1_HYSTTHRESH*/
++#define R0900_P1_HYSTTHRESH 0xf554
++#define F0900_P1_UNLCK_THRESH 0xf55400f0
++#define F0900_P1_DELIN_LCK_THRESH 0xf554000f
++
++/*P1_ISIENTRY*/
++#define R0900_P1_ISIENTRY 0xf55e
++#define F0900_P1_ISI_ENTRY 0xf55e00ff
++
++/*P1_ISIBITENA*/
++#define R0900_P1_ISIBITENA 0xf55f
++#define F0900_P1_ISI_BIT_EN 0xf55f00ff
++
++/*P1_MATSTR1*/
++#define R0900_P1_MATSTR1 0xf560
++#define F0900_P1_MATYPE_CURRENT1 0xf56000ff
++
++/*P1_MATSTR0*/
++#define R0900_P1_MATSTR0 0xf561
++#define F0900_P1_MATYPE_CURRENT0 0xf56100ff
++
++/*P1_UPLSTR1*/
++#define R0900_P1_UPLSTR1 0xf562
++#define F0900_P1_UPL_CURRENT1 0xf56200ff
++
++/*P1_UPLSTR0*/
++#define R0900_P1_UPLSTR0 0xf563
++#define F0900_P1_UPL_CURRENT0 0xf56300ff
++
++/*P1_DFLSTR1*/
++#define R0900_P1_DFLSTR1 0xf564
++#define F0900_P1_DFL_CURRENT1 0xf56400ff
++
++/*P1_DFLSTR0*/
++#define R0900_P1_DFLSTR0 0xf565
++#define F0900_P1_DFL_CURRENT0 0xf56500ff
++
++/*P1_SYNCSTR*/
++#define R0900_P1_SYNCSTR 0xf566
++#define F0900_P1_SYNC_CURRENT 0xf56600ff
++
++/*P1_SYNCDSTR1*/
++#define R0900_P1_SYNCDSTR1 0xf567
++#define F0900_P1_SYNCD_CURRENT1 0xf56700ff
++
++/*P1_SYNCDSTR0*/
++#define R0900_P1_SYNCDSTR0 0xf568
++#define F0900_P1_SYNCD_CURRENT0 0xf56800ff
++
++/*P1_PDELSTATUS1*/
++#define R0900_P1_PDELSTATUS1 0xf569
++#define F0900_P1_PKTDELIN_DELOCK 0xf5690080
++#define F0900_P1_SYNCDUPDFL_BADDFL 0xf5690040
++#define F0900_P1_CONTINUOUS_STREAM 0xf5690020
++#define F0900_P1_UNACCEPTED_STREAM 0xf5690010
++#define F0900_P1_BCH_ERROR_FLAG 0xf5690008
++#define F0900_P1_BBHCRCKO 0xf5690004
++#define F0900_P1_PKTDELIN_LOCK 0xf5690002
++#define F0900_P1_FIRST_LOCK 0xf5690001
++
++/*P1_PDELSTATUS2*/
++#define R0900_P1_PDELSTATUS2 0xf56a
++#define F0900_P1_PKTDEL_DEMODSEL 0xf56a0080
++#define F0900_P1_FRAME_MODCOD 0xf56a007c
++#define F0900_P1_FRAME_TYPE 0xf56a0003
++
++/*P1_BBFCRCKO1*/
++#define R0900_P1_BBFCRCKO1 0xf56b
++#define F0900_P1_BBHCRC_KOCNT1 0xf56b00ff
++
++/*P1_BBFCRCKO0*/
++#define R0900_P1_BBFCRCKO0 0xf56c
++#define F0900_P1_BBHCRC_KOCNT0 0xf56c00ff
++
++/*P1_UPCRCKO1*/
++#define R0900_P1_UPCRCKO1 0xf56d
++#define F0900_P1_PKTCRC_KOCNT1 0xf56d00ff
++
++/*P1_UPCRCKO0*/
++#define R0900_P1_UPCRCKO0 0xf56e
++#define F0900_P1_PKTCRC_KOCNT0 0xf56e00ff
++
++/*P1_TSSTATEM*/
++#define R0900_P1_TSSTATEM 0xf570
++#define F0900_P1_TSDIL_ON 0xf5700080
++#define F0900_P1_TSSKIPRS_ON 0xf5700040
++#define F0900_P1_TSRS_ON 0xf5700020
++#define F0900_P1_TSDESCRAMB_ON 0xf5700010
++#define F0900_P1_TSFRAME_MODE 0xf5700008
++#define F0900_P1_TS_DISABLE 0xf5700004
++#define F0900_P1_TSACM_MODE 0xf5700002
++#define F0900_P1_TSOUT_NOSYNC 0xf5700001
++
++/*P1_TSCFGH*/
++#define R0900_P1_TSCFGH 0xf572
++#define F0900_P1_TSFIFO_DVBCI 0xf5720080
++#define F0900_P1_TSFIFO_SERIAL 0xf5720040
++#define F0900_P1_TSFIFO_TEIUPDATE 0xf5720020
++#define F0900_P1_TSFIFO_DUTY50 0xf5720010
++#define F0900_P1_TSFIFO_HSGNLOUT 0xf5720008
++#define F0900_P1_TSFIFO_ERRMODE 0xf5720006
++#define F0900_P1_RST_HWARE 0xf5720001
++
++/*P1_TSCFGM*/
++#define R0900_P1_TSCFGM 0xf573
++#define F0900_P1_TSFIFO_MANSPEED 0xf57300c0
++#define F0900_P1_TSFIFO_PERMDATA 0xf5730020
++#define F0900_P1_TSFIFO_NONEWSGNL 0xf5730010
++#define F0900_P1_TSFIFO_BITSPEED 0xf5730008
++#define F0900_P1_NPD_SPECDVBS2 0xf5730004
++#define F0900_P1_TSFIFO_STOPCKDIS 0xf5730002
++#define F0900_P1_TSFIFO_INVDATA 0xf5730001
++
++/*P1_TSCFGL*/
++#define R0900_P1_TSCFGL 0xf574
++#define F0900_P1_TSFIFO_BCLKDEL1CK 0xf57400c0
++#define F0900_P1_BCHERROR_MODE 0xf5740030
++#define F0900_P1_TSFIFO_NSGNL2DATA 0xf5740008
++#define F0900_P1_TSFIFO_EMBINDVB 0xf5740004
++#define F0900_P1_TSFIFO_DPUNACT 0xf5740002
++#define F0900_P1_TSFIFO_NPDOFF 0xf5740001
++
++/*P1_TSINSDELH*/
++#define R0900_P1_TSINSDELH 0xf576
++#define F0900_P1_TSDEL_SYNCBYTE 0xf5760080
++#define F0900_P1_TSDEL_XXHEADER 0xf5760040
++#define F0900_P1_TSDEL_BBHEADER 0xf5760020
++#define F0900_P1_TSDEL_DATAFIELD 0xf5760010
++#define F0900_P1_TSINSDEL_ISCR 0xf5760008
++#define F0900_P1_TSINSDEL_NPD 0xf5760004
++#define F0900_P1_TSINSDEL_RSPARITY 0xf5760002
++#define F0900_P1_TSINSDEL_CRC8 0xf5760001
++
++/*P1_TSSPEED*/
++#define R0900_P1_TSSPEED 0xf580
++#define F0900_P1_TSFIFO_OUTSPEED 0xf58000ff
++
++/*P1_TSSTATUS*/
++#define R0900_P1_TSSTATUS 0xf581
++#define F0900_P1_TSFIFO_LINEOK 0xf5810080
++#define F0900_P1_TSFIFO_ERROR 0xf5810040
++#define F0900_P1_TSFIFO_DATA7 0xf5810020
++#define F0900_P1_TSFIFO_NOSYNC 0xf5810010
++#define F0900_P1_ISCR_INITIALIZED 0xf5810008
++#define F0900_P1_ISCR_UPDATED 0xf5810004
++#define F0900_P1_SOFFIFO_UNREGUL 0xf5810002
++#define F0900_P1_DIL_READY 0xf5810001
++
++/*P1_TSSTATUS2*/
++#define R0900_P1_TSSTATUS2 0xf582
++#define F0900_P1_TSFIFO_DEMODSEL 0xf5820080
++#define F0900_P1_TSFIFOSPEED_STORE 0xf5820040
++#define F0900_P1_DILXX_RESET 0xf5820020
++#define F0900_P1_TSSERIAL_IMPOS 0xf5820010
++#define F0900_P1_TSFIFO_LINENOK 0xf5820008
++#define F0900_P1_BITSPEED_EVENT 0xf5820004
++#define F0900_P1_SCRAMBDETECT 0xf5820002
++#define F0900_P1_ULDTV67_FALSELOCK 0xf5820001
++
++/*P1_TSBITRATE1*/
++#define R0900_P1_TSBITRATE1 0xf583
++#define F0900_P1_TSFIFO_BITRATE1 0xf58300ff
++
++/*P1_TSBITRATE0*/
++#define R0900_P1_TSBITRATE0 0xf584
++#define F0900_P1_TSFIFO_BITRATE0 0xf58400ff
++
++/*P1_ERRCTRL1*/
++#define R0900_P1_ERRCTRL1 0xf598
++#define F0900_P1_ERR_SOURCE1 0xf59800f0
++#define F0900_P1_NUM_EVENT1 0xf5980007
++
++/*P1_ERRCNT12*/
++#define R0900_P1_ERRCNT12 0xf599
++#define F0900_P1_ERRCNT1_OLDVALUE 0xf5990080
++#define F0900_P1_ERR_CNT12 0xf599007f
++
++/*P1_ERRCNT11*/
++#define R0900_P1_ERRCNT11 0xf59a
++#define F0900_P1_ERR_CNT11 0xf59a00ff
++
++/*P1_ERRCNT10*/
++#define R0900_P1_ERRCNT10 0xf59b
++#define F0900_P1_ERR_CNT10 0xf59b00ff
++
++/*P1_ERRCTRL2*/
++#define R0900_P1_ERRCTRL2 0xf59c
++#define F0900_P1_ERR_SOURCE2 0xf59c00f0
++#define F0900_P1_NUM_EVENT2 0xf59c0007
++
++/*P1_ERRCNT22*/
++#define R0900_P1_ERRCNT22 0xf59d
++#define F0900_P1_ERRCNT2_OLDVALUE 0xf59d0080
++#define F0900_P1_ERR_CNT22 0xf59d007f
++
++/*P1_ERRCNT21*/
++#define R0900_P1_ERRCNT21 0xf59e
++#define F0900_P1_ERR_CNT21 0xf59e00ff
++
++/*P1_ERRCNT20*/
++#define R0900_P1_ERRCNT20 0xf59f
++#define F0900_P1_ERR_CNT20 0xf59f00ff
++
++/*P1_FECSPY*/
++#define R0900_P1_FECSPY 0xf5a0
++#define F0900_P1_SPY_ENABLE 0xf5a00080
++#define F0900_P1_NO_SYNCBYTE 0xf5a00040
++#define F0900_P1_SERIAL_MODE 0xf5a00020
++#define F0900_P1_UNUSUAL_PACKET 0xf5a00010
++#define F0900_P1_BER_PACKMODE 0xf5a00008
++#define F0900_P1_BERMETER_LMODE 0xf5a00002
++#define F0900_P1_BERMETER_RESET 0xf5a00001
++
++/*P1_FSPYCFG*/
++#define R0900_P1_FSPYCFG 0xf5a1
++#define F0900_P1_FECSPY_INPUT 0xf5a100c0
++#define F0900_P1_RST_ON_ERROR 0xf5a10020
++#define F0900_P1_ONE_SHOT 0xf5a10010
++#define F0900_P1_I2C_MODE 0xf5a1000c
++#define F0900_P1_SPY_HYSTERESIS 0xf5a10003
++
++/*P1_FSPYDATA*/
++#define R0900_P1_FSPYDATA 0xf5a2
++#define F0900_P1_SPY_STUFFING 0xf5a20080
++#define F0900_P1_NOERROR_PKTJITTER 0xf5a20040
++#define F0900_P1_SPY_CNULLPKT 0xf5a20020
++#define F0900_P1_SPY_OUTDATA_MODE 0xf5a2001f
++
++/*P1_FSPYOUT*/
++#define R0900_P1_FSPYOUT 0xf5a3
++#define F0900_P1_FSPY_DIRECT 0xf5a30080
++#define F0900_P1_SPY_OUTDATA_BUS 0xf5a30038
++#define F0900_P1_STUFF_MODE 0xf5a30007
++
++/*P1_FSTATUS*/
++#define R0900_P1_FSTATUS 0xf5a4
++#define F0900_P1_SPY_ENDSIM 0xf5a40080
++#define F0900_P1_VALID_SIM 0xf5a40040
++#define F0900_P1_FOUND_SIGNAL 0xf5a40020
++#define F0900_P1_DSS_SYNCBYTE 0xf5a40010
++#define F0900_P1_RESULT_STATE 0xf5a4000f
++
++/*P1_FBERCPT4*/
++#define R0900_P1_FBERCPT4 0xf5a8
++#define F0900_P1_FBERMETER_CPT4 0xf5a800ff
++
++/*P1_FBERCPT3*/
++#define R0900_P1_FBERCPT3 0xf5a9
++#define F0900_P1_FBERMETER_CPT3 0xf5a900ff
++
++/*P1_FBERCPT2*/
++#define R0900_P1_FBERCPT2 0xf5aa
++#define F0900_P1_FBERMETER_CPT2 0xf5aa00ff
++
++/*P1_FBERCPT1*/
++#define R0900_P1_FBERCPT1 0xf5ab
++#define F0900_P1_FBERMETER_CPT1 0xf5ab00ff
++
++/*P1_FBERCPT0*/
++#define R0900_P1_FBERCPT0 0xf5ac
++#define F0900_P1_FBERMETER_CPT0 0xf5ac00ff
++
++/*P1_FBERERR2*/
++#define R0900_P1_FBERERR2 0xf5ad
++#define F0900_P1_FBERMETER_ERR2 0xf5ad00ff
++
++/*P1_FBERERR1*/
++#define R0900_P1_FBERERR1 0xf5ae
++#define F0900_P1_FBERMETER_ERR1 0xf5ae00ff
++
++/*P1_FBERERR0*/
++#define R0900_P1_FBERERR0 0xf5af
++#define F0900_P1_FBERMETER_ERR0 0xf5af00ff
++
++/*P1_FSPYBER*/
++#define R0900_P1_FSPYBER 0xf5b2
++#define F0900_P1_FSPYOBS_XORREAD 0xf5b20040
++#define F0900_P1_FSPYBER_OBSMODE 0xf5b20020
++#define F0900_P1_FSPYBER_SYNCBYTE 0xf5b20010
++#define F0900_P1_FSPYBER_UNSYNC 0xf5b20008
++#define F0900_P1_FSPYBER_CTIME 0xf5b20007
++
++/*RCCFGH*/
++#define R0900_RCCFGH 0xf600
++#define F0900_TSRCFIFO_DVBCI 0xf6000080
++#define F0900_TSRCFIFO_SERIAL 0xf6000040
++#define F0900_TSRCFIFO_DISABLE 0xf6000020
++#define F0900_TSFIFO_2TORC 0xf6000010
++#define F0900_TSRCFIFO_HSGNLOUT 0xf6000008
++#define F0900_TSRCFIFO_ERRMODE 0xf6000006
++
++/*TSGENERAL*/
++#define R0900_TSGENERAL 0xf630
++#define F0900_TSFIFO_BCLK1ALL 0xf6300020
++#define F0900_MUXSTREAM_OUTMODE 0xf6300008
++#define F0900_TSFIFO_PERMPARAL 0xf6300006
++#define F0900_RST_REEDSOLO 0xf6300001
++
++/*TSGENERAL1X*/
++#define R0900_TSGENERAL1X 0xf670
++#define F0900_TSFIFO1X_BCLK1ALL 0xf6700020
++#define F0900_MUXSTREAM1X_OUTMODE 0xf6700008
++#define F0900_TSFIFO1X_PERMPARAL 0xf6700006
++#define F0900_RST1X_REEDSOLO 0xf6700001
++
++/*NBITER_NF4*/
++#define R0900_NBITER_NF4 0xfa03
++#define F0900_NBITER_NF_QP_1_2 0xfa0300ff
++
++/*NBITER_NF5*/
++#define R0900_NBITER_NF5 0xfa04
++#define F0900_NBITER_NF_QP_3_5 0xfa0400ff
++
++/*NBITER_NF6*/
++#define R0900_NBITER_NF6 0xfa05
++#define F0900_NBITER_NF_QP_2_3 0xfa0500ff
++
++/*NBITER_NF7*/
++#define R0900_NBITER_NF7 0xfa06
++#define F0900_NBITER_NF_QP_3_4 0xfa0600ff
++
++/*NBITER_NF8*/
++#define R0900_NBITER_NF8 0xfa07
++#define F0900_NBITER_NF_QP_4_5 0xfa0700ff
++
++/*NBITER_NF9*/
++#define R0900_NBITER_NF9 0xfa08
++#define F0900_NBITER_NF_QP_5_6 0xfa0800ff
++
++/*NBITER_NF10*/
++#define R0900_NBITER_NF10 0xfa09
++#define F0900_NBITER_NF_QP_8_9 0xfa0900ff
++
++/*NBITER_NF11*/
++#define R0900_NBITER_NF11 0xfa0a
++#define F0900_NBITER_NF_QP_9_10 0xfa0a00ff
++
++/*NBITER_NF12*/
++#define R0900_NBITER_NF12 0xfa0b
++#define F0900_NBITER_NF_8P_3_5 0xfa0b00ff
++
++/*NBITER_NF13*/
++#define R0900_NBITER_NF13 0xfa0c
++#define F0900_NBITER_NF_8P_2_3 0xfa0c00ff
++
++/*NBITER_NF14*/
++#define R0900_NBITER_NF14 0xfa0d
++#define F0900_NBITER_NF_8P_3_4 0xfa0d00ff
++
++/*NBITER_NF15*/
++#define R0900_NBITER_NF15 0xfa0e
++#define F0900_NBITER_NF_8P_5_6 0xfa0e00ff
++
++/*NBITER_NF16*/
++#define R0900_NBITER_NF16 0xfa0f
++#define F0900_NBITER_NF_8P_8_9 0xfa0f00ff
++
++/*NBITER_NF17*/
++#define R0900_NBITER_NF17 0xfa10
++#define F0900_NBITER_NF_8P_9_10 0xfa1000ff
++
++/*NBITERNOERR*/
++#define R0900_NBITERNOERR 0xfa3f
++#define F0900_NBITER_STOP_CRIT 0xfa3f000f
++
++/*GAINLLR_NF4*/
++#define R0900_GAINLLR_NF4 0xfa43
++#define F0900_GAINLLR_NF_QP_1_2 0xfa43007f
++
++/*GAINLLR_NF5*/
++#define R0900_GAINLLR_NF5 0xfa44
++#define F0900_GAINLLR_NF_QP_3_5 0xfa44007f
++
++/*GAINLLR_NF6*/
++#define R0900_GAINLLR_NF6 0xfa45
++#define F0900_GAINLLR_NF_QP_2_3 0xfa45007f
++
++/*GAINLLR_NF7*/
++#define R0900_GAINLLR_NF7 0xfa46
++#define F0900_GAINLLR_NF_QP_3_4 0xfa46007f
++
++/*GAINLLR_NF8*/
++#define R0900_GAINLLR_NF8 0xfa47
++#define F0900_GAINLLR_NF_QP_4_5 0xfa47007f
++
++/*GAINLLR_NF9*/
++#define R0900_GAINLLR_NF9 0xfa48
++#define F0900_GAINLLR_NF_QP_5_6 0xfa48007f
++
++/*GAINLLR_NF10*/
++#define R0900_GAINLLR_NF10 0xfa49
++#define F0900_GAINLLR_NF_QP_8_9 0xfa49007f
++
++/*GAINLLR_NF11*/
++#define R0900_GAINLLR_NF11 0xfa4a
++#define F0900_GAINLLR_NF_QP_9_10 0xfa4a007f
++
++/*GAINLLR_NF12*/
++#define R0900_GAINLLR_NF12 0xfa4b
++#define F0900_GAINLLR_NF_8P_3_5 0xfa4b007f
++
++/*GAINLLR_NF13*/
++#define R0900_GAINLLR_NF13 0xfa4c
++#define F0900_GAINLLR_NF_8P_2_3 0xfa4c007f
++
++/*GAINLLR_NF14*/
++#define R0900_GAINLLR_NF14 0xfa4d
++#define F0900_GAINLLR_NF_8P_3_4 0xfa4d007f
++
++/*GAINLLR_NF15*/
++#define R0900_GAINLLR_NF15 0xfa4e
++#define F0900_GAINLLR_NF_8P_5_6 0xfa4e007f
++
++/*GAINLLR_NF16*/
++#define R0900_GAINLLR_NF16 0xfa4f
++#define F0900_GAINLLR_NF_8P_8_9 0xfa4f007f
++
++/*GAINLLR_NF17*/
++#define R0900_GAINLLR_NF17 0xfa50
++#define F0900_GAINLLR_NF_8P_9_10 0xfa50007f
++
++/*CFGEXT*/
++#define R0900_CFGEXT 0xfa80
++#define F0900_STAGMODE 0xfa800080
++#define F0900_BYPBCH 0xfa800040
++#define F0900_BYPLDPC 0xfa800020
++#define F0900_LDPCMODE 0xfa800010
++#define F0900_INVLLRSIGN 0xfa800008
++#define F0900_SHORTMULT 0xfa800004
++#define F0900_EXTERNTX 0xfa800001
++
++/*GENCFG*/
++#define R0900_GENCFG 0xfa86
++#define F0900_BROADCAST 0xfa860010
++#define F0900_NOSHFRD2 0xfa860008
++#define F0900_BCHERRFLAG 0xfa860004
++#define F0900_PRIORITY 0xfa860002
++#define F0900_DDEMOD 0xfa860001
++
++/*LDPCERR1*/
++#define R0900_LDPCERR1 0xfa96
++#define F0900_LDPC_ERRORS_COUNTER1 0xfa9600ff
++
++/*LDPCERR0*/
++#define R0900_LDPCERR0 0xfa97
++#define F0900_LDPC_ERRORS_COUNTER0 0xfa9700ff
++
++/*BCHERR*/
++#define R0900_BCHERR 0xfa98
++#define F0900_ERRORFLAG 0xfa980010
++#define F0900_BCH_ERRORS_COUNTER 0xfa98000f
++
++/*TSTRES0*/
++#define R0900_TSTRES0 0xff11
++#define F0900_FRESFEC 0xff110080
++#define F0900_FRESTS 0xff110040
++#define F0900_FRESVIT1 0xff110020
++#define F0900_FRESVIT2 0xff110010
++#define F0900_FRESSYM1 0xff110008
++#define F0900_FRESSYM2 0xff110004
++#define F0900_FRESMAS 0xff110002
++#define F0900_FRESINT 0xff110001
++
++/*P2_TSTDISRX*/
++#define R0900_P2_TSTDISRX 0xff65
++#define F0900_P2_EN_DISRX 0xff650080
++#define F0900_P2_TST_CURRSRC 0xff650040
++#define F0900_P2_IN_DIGSIGNAL 0xff650020
++#define F0900_P2_HIZ_CURRENTSRC 0xff650010
++#define F0900_TST_P2_PIN_SELECT 0xff650008
++#define F0900_P2_TST_DISRX 0xff650007
++
++/*P1_TSTDISRX*/
++#define R0900_P1_TSTDISRX 0xff67
++#define F0900_P1_EN_DISRX 0xff670080
++#define F0900_P1_TST_CURRSRC 0xff670040
++#define F0900_P1_IN_DIGSIGNAL 0xff670020
++#define F0900_P1_HIZ_CURRENTSRC 0xff670010
++#define F0900_TST_P1_PIN_SELECT 0xff670008
++#define F0900_P1_TST_DISRX 0xff670007
++
++#define STV0900_NBREGS 684
++#define STV0900_NBFIELDS 1702
++
++#endif
++
+diff --git a/drivers/media/dvb/frontends/stv0900_sw.c b/drivers/media/dvb/frontends/stv0900_sw.c
+new file mode 100644
+index 0000000..a5a3153
+--- /dev/null
++++ b/drivers/media/dvb/frontends/stv0900_sw.c
+@@ -0,0 +1,2847 @@
++/*
++ * stv0900_sw.c
++ *
++ * Driver for ST STV0900 satellite demodulator IC.
++ *
++ * Copyright (C) ST Microelectronics.
++ * Copyright (C) 2009 NetUP Inc.
++ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
++ *
++ * 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 "stv0900.h"
++#include "stv0900_reg.h"
++#include "stv0900_priv.h"
++
++int stv0900_check_signal_presence(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod)
++{
++ s32 carr_offset,
++ agc2_integr,
++ max_carrier;
++
++ int no_signal;
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ carr_offset = (stv0900_read_reg(i_params, R0900_P1_CFR2) << 8)
++ | stv0900_read_reg(i_params,
++ R0900_P1_CFR1);
++ carr_offset = ge2comp(carr_offset, 16);
++ agc2_integr = (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8)
++ | stv0900_read_reg(i_params,
++ R0900_P1_AGC2I0);
++ max_carrier = i_params->dmd1_srch_range / 1000;
++ break;
++ case STV0900_DEMOD_2:
++ carr_offset = (stv0900_read_reg(i_params, R0900_P2_CFR2) << 8)
++ | stv0900_read_reg(i_params,
++ R0900_P2_CFR1);
++ carr_offset = ge2comp(carr_offset, 16);
++ agc2_integr = (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8)
++ | stv0900_read_reg(i_params,
++ R0900_P2_AGC2I0);
++ max_carrier = i_params->dmd2_srch_range / 1000;
++ break;
++ }
++
++ max_carrier += (max_carrier / 10);
++ max_carrier = 65536 * (max_carrier / 2);
++ max_carrier /= i_params->mclk / 1000;
++ if (max_carrier > 0x4000)
++ max_carrier = 0x4000;
++
++ if ((agc2_integr > 0x2000)
++ || (carr_offset > + 2*max_carrier)
++ || (carr_offset < -2*max_carrier))
++ no_signal = TRUE;
++ else
++ no_signal = FALSE;
++
++ return no_signal;
++}
++
++static void stv0900_get_sw_loop_params(struct stv0900_internal *i_params,
++ s32 *frequency_inc, s32 *sw_timeout,
++ s32 *steps,
++ enum fe_stv0900_demod_num demod)
++{
++ s32 timeout, freq_inc, max_steps, srate, max_carrier;
++
++ enum fe_stv0900_search_standard standard;
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ srate = i_params->dmd1_symbol_rate;
++ max_carrier = i_params->dmd1_srch_range / 1000;
++ max_carrier += max_carrier / 10;
++ standard = i_params->dmd1_srch_standard;
++ break;
++ case STV0900_DEMOD_2:
++ srate = i_params->dmd2_symbol_rate;
++ max_carrier = i_params->dmd2_srch_range / 1000;
++ max_carrier += max_carrier / 10;
++ standard = i_params->dmd2_srch_stndrd;
++ break;
++ }
++
++ max_carrier = 65536 * (max_carrier / 2);
++ max_carrier /= i_params->mclk / 1000;
++
++ if (max_carrier > 0x4000)
++ max_carrier = 0x4000;
++
++ freq_inc = srate;
++ freq_inc /= i_params->mclk >> 10;
++ freq_inc = freq_inc << 6;
++
++ switch (standard) {
++ case STV0900_SEARCH_DVBS1:
++ case STV0900_SEARCH_DSS:
++ freq_inc *= 3;
++ timeout = 20;
++ break;
++ case STV0900_SEARCH_DVBS2:
++ freq_inc *= 4;
++ timeout = 25;
++ break;
++ case STV0900_AUTO_SEARCH:
++ default:
++ freq_inc *= 3;
++ timeout = 25;
++ break;
++ }
++
++ freq_inc /= 100;
++
++ if ((freq_inc > max_carrier) || (freq_inc < 0))
++ freq_inc = max_carrier / 2;
++
++ timeout *= 27500;
++
++ if (srate > 0)
++ timeout /= srate / 1000;
++
++ if ((timeout > 100) || (timeout < 0))
++ timeout = 100;
++
++ max_steps = (max_carrier / freq_inc) + 1;
++
++ if ((max_steps > 100) || (max_steps < 0)) {
++ max_steps = 100;
++ freq_inc = max_carrier / max_steps;
++ }
++
++ *frequency_inc = freq_inc;
++ *sw_timeout = timeout;
++ *steps = max_steps;
++
++}
++
++static int stv0900_search_carr_sw_loop(struct stv0900_internal *i_params,
++ s32 FreqIncr, s32 Timeout, int zigzag,
++ s32 MaxStep, enum fe_stv0900_demod_num demod)
++{
++ int no_signal,
++ lock = FALSE;
++ s32 stepCpt,
++ freqOffset,
++ max_carrier;
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ max_carrier = i_params->dmd1_srch_range / 1000;
++ max_carrier += (max_carrier / 10);
++ break;
++ case STV0900_DEMOD_2:
++ max_carrier = i_params->dmd2_srch_range / 1000;
++ max_carrier += (max_carrier / 10);
++ break;
++ }
++
++ max_carrier = 65536 * (max_carrier / 2);
++ max_carrier /= i_params->mclk / 1000;
++
++ if (max_carrier > 0x4000)
++ max_carrier = 0x4000;
++
++ if (zigzag == TRUE)
++ freqOffset = 0;
++ else
++ freqOffset = -max_carrier + FreqIncr;
++
++ stepCpt = 0;
++
++ do {
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1C);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT1,
++ (freqOffset / 256) & 0xFF);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT0,
++ freqOffset & 0xFF);
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
++ stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 1);
++
++ if (i_params->chip_id == 0x12) {
++ stv0900_write_bits(i_params,
++ F0900_P1_RST_HWARE, 1);
++ stv0900_write_bits(i_params,
++ F0900_P1_RST_HWARE, 0);
++ }
++ break;
++ case STV0900_DEMOD_2:
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1C);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT1,
++ (freqOffset / 256) & 0xFF);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT0,
++ freqOffset & 0xFF);
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
++ stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 1);
++
++ if (i_params->chip_id == 0x12) {
++ stv0900_write_bits(i_params,
++ F0900_P2_RST_HWARE, 1);
++ stv0900_write_bits(i_params,
++ F0900_P2_RST_HWARE, 0);
++ }
++ break;
++ }
++
++ if (zigzag == TRUE) {
++ if (freqOffset >= 0)
++ freqOffset = -freqOffset - 2 * FreqIncr;
++ else
++ freqOffset = -freqOffset;
++ } else
++ freqOffset += + 2 * FreqIncr;
++
++ stepCpt++;
++ lock = stv0900_get_demod_lock(i_params, demod, Timeout);
++ no_signal = stv0900_check_signal_presence(i_params, demod);
++
++ } while ((lock == FALSE)
++ && (no_signal == FALSE)
++ && ((freqOffset - FreqIncr) < max_carrier)
++ && ((freqOffset + FreqIncr) > -max_carrier)
++ && (stepCpt < MaxStep));
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ stv0900_write_bits(i_params, F0900_P1_ALGOSWRST, 0);
++ break;
++ case STV0900_DEMOD_2:
++ stv0900_write_bits(i_params, F0900_P2_ALGOSWRST, 0);
++ break;
++ }
++
++ return lock;
++}
++
++int stv0900_sw_algo(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod)
++{
++ int lock = FALSE;
++
++ int no_signal,
++ zigzag;
++ s32 dvbs2_fly_wheel;
++
++ s32 freqIncrement, softStepTimeout, trialCounter, max_steps;
++
++ stv0900_get_sw_loop_params(i_params, &freqIncrement, &softStepTimeout,
++ &max_steps, demod);
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ switch (i_params->dmd1_srch_standard) {
++ case STV0900_SEARCH_DVBS1:
++ case STV0900_SEARCH_DSS:
++ if (i_params->chip_id >= 0x20)
++ stv0900_write_reg(i_params, R0900_P1_CARFREQ,
++ 0x3B);
++ else
++ stv0900_write_reg(i_params, R0900_P1_CARFREQ,
++ 0xef);
++
++ stv0900_write_reg(i_params, R0900_P1_DMDCFGMD, 0x49);
++ zigzag = FALSE;
++ break;
++ case STV0900_SEARCH_DVBS2:
++ if (i_params->chip_id >= 0x20)
++ stv0900_write_reg(i_params, R0900_P1_CORRELABS,
++ 0x79);
++ else
++ stv0900_write_reg(i_params, R0900_P1_CORRELABS,
++ 0x68);
++
++ stv0900_write_reg(i_params, R0900_P1_DMDCFGMD,
++ 0x89);
++
++ zigzag = TRUE;
++ break;
++ case STV0900_AUTO_SEARCH:
++ default:
++ if (i_params->chip_id >= 0x20) {
++ stv0900_write_reg(i_params, R0900_P1_CARFREQ,
++ 0x3B);
++ stv0900_write_reg(i_params, R0900_P1_CORRELABS,
++ 0x79);
++ } else {
++ stv0900_write_reg(i_params, R0900_P1_CARFREQ,
++ 0xef);
++ stv0900_write_reg(i_params, R0900_P1_CORRELABS,
++ 0x68);
++ }
++
++ stv0900_write_reg(i_params, R0900_P1_DMDCFGMD,
++ 0xc9);
++ zigzag = FALSE;
++ break;
++ }
++
++ trialCounter = 0;
++ do {
++ lock = stv0900_search_carr_sw_loop(i_params,
++ freqIncrement,
++ softStepTimeout,
++ zigzag,
++ max_steps,
++ demod);
++ no_signal = stv0900_check_signal_presence(i_params,
++ demod);
++ trialCounter++;
++ if ((lock == TRUE)
++ || (no_signal == TRUE)
++ || (trialCounter == 2)) {
++
++ if (i_params->chip_id >= 0x20) {
++ stv0900_write_reg(i_params,
++ R0900_P1_CARFREQ,
++ 0x49);
++ stv0900_write_reg(i_params,
++ R0900_P1_CORRELABS,
++ 0x9e);
++ } else {
++ stv0900_write_reg(i_params,
++ R0900_P1_CARFREQ,
++ 0xed);
++ stv0900_write_reg(i_params,
++ R0900_P1_CORRELABS,
++ 0x88);
++ }
++
++ if ((lock == TRUE) && (stv0900_get_bits(i_params, F0900_P1_HEADER_MODE) == STV0900_DVBS2_FOUND)) {
++ msleep(softStepTimeout);
++ dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P1_FLYWHEEL_CPT);
++
++ if (dvbs2_fly_wheel < 0xd) {
++ msleep(softStepTimeout);
++ dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P1_FLYWHEEL_CPT);
++ }
++
++ if (dvbs2_fly_wheel < 0xd) {
++ lock = FALSE;
++
++ if (trialCounter < 2) {
++ if (i_params->chip_id >= 0x20)
++ stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x79);
++ else
++ stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x68);
++
++ stv0900_write_reg(i_params, R0900_P1_DMDCFGMD, 0x89);
++ }
++ }
++ }
++ }
++
++ } while ((lock == FALSE)
++ && (trialCounter < 2)
++ && (no_signal == FALSE));
++
++ break;
++ case STV0900_DEMOD_2:
++ switch (i_params->dmd2_srch_stndrd) {
++ case STV0900_SEARCH_DVBS1:
++ case STV0900_SEARCH_DSS:
++ if (i_params->chip_id >= 0x20)
++ stv0900_write_reg(i_params, R0900_P2_CARFREQ,
++ 0x3b);
++ else
++ stv0900_write_reg(i_params, R0900_P2_CARFREQ,
++ 0xef);
++
++ stv0900_write_reg(i_params, R0900_P2_DMDCFGMD,
++ 0x49);
++ zigzag = FALSE;
++ break;
++ case STV0900_SEARCH_DVBS2:
++ if (i_params->chip_id >= 0x20)
++ stv0900_write_reg(i_params, R0900_P2_CORRELABS,
++ 0x79);
++ else
++ stv0900_write_reg(i_params, R0900_P2_CORRELABS,
++ 0x68);
++
++ stv0900_write_reg(i_params, R0900_P2_DMDCFGMD, 0x89);
++ zigzag = TRUE;
++ break;
++ case STV0900_AUTO_SEARCH:
++ default:
++ if (i_params->chip_id >= 0x20) {
++ stv0900_write_reg(i_params, R0900_P2_CARFREQ,
++ 0x3b);
++ stv0900_write_reg(i_params, R0900_P2_CORRELABS,
++ 0x79);
++ } else {
++ stv0900_write_reg(i_params, R0900_P2_CARFREQ,
++ 0xef);
++ stv0900_write_reg(i_params, R0900_P2_CORRELABS,
++ 0x68);
++ }
++
++ stv0900_write_reg(i_params, R0900_P2_DMDCFGMD, 0xc9);
++
++ zigzag = FALSE;
++ break;
++ }
++
++ trialCounter = 0;
++
++ do {
++ lock = stv0900_search_carr_sw_loop(i_params,
++ freqIncrement,
++ softStepTimeout,
++ zigzag,
++ max_steps,
++ demod);
++ no_signal = stv0900_check_signal_presence(i_params,
++ demod);
++ trialCounter++;
++ if ((lock == TRUE)
++ || (no_signal == TRUE)
++ || (trialCounter == 2)) {
++ if (i_params->chip_id >= 0x20) {
++ stv0900_write_reg(i_params,
++ R0900_P2_CARFREQ,
++ 0x49);
++ stv0900_write_reg(i_params,
++ R0900_P2_CORRELABS,
++ 0x9e);
++ } else {
++ stv0900_write_reg(i_params,
++ R0900_P2_CARFREQ,
++ 0xed);
++ stv0900_write_reg(i_params,
++ R0900_P2_CORRELABS,
++ 0x88);
++ }
++
++ if ((lock == TRUE) && (stv0900_get_bits(i_params, F0900_P2_HEADER_MODE) == STV0900_DVBS2_FOUND)) {
++ msleep(softStepTimeout);
++ dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P2_FLYWHEEL_CPT);
++ if (dvbs2_fly_wheel < 0xd) {
++ msleep(softStepTimeout);
++ dvbs2_fly_wheel = stv0900_get_bits(i_params, F0900_P2_FLYWHEEL_CPT);
++ }
++
++ if (dvbs2_fly_wheel < 0xd) {
++ lock = FALSE;
++ if (trialCounter < 2) {
++ if (i_params->chip_id >= 0x20)
++ stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x79);
++ else
++ stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x68);
++
++ stv0900_write_reg(i_params, R0900_P2_DMDCFGMD, 0x89);
++ }
++ }
++ }
++ }
++
++ } while ((lock == FALSE) && (trialCounter < 2) && (no_signal == FALSE));
++
++ break;
++ }
++
++ return lock;
++}
++
++static u32 stv0900_get_symbol_rate(struct stv0900_internal *i_params,
++ u32 mclk,
++ enum fe_stv0900_demod_num demod)
++{
++ s32 sfr_field3, sfr_field2, sfr_field1, sfr_field0,
++ rem1, rem2, intval1, intval2, srate;
++
++ dmd_reg(sfr_field3, F0900_P1_SYMB_FREQ3, F0900_P2_SYMB_FREQ3);
++ dmd_reg(sfr_field2, F0900_P1_SYMB_FREQ2, F0900_P2_SYMB_FREQ2);
++ dmd_reg(sfr_field1, F0900_P1_SYMB_FREQ1, F0900_P2_SYMB_FREQ1);
++ dmd_reg(sfr_field0, F0900_P1_SYMB_FREQ0, F0900_P2_SYMB_FREQ0);
++
++ srate = (stv0900_get_bits(i_params, sfr_field3) << 24) +
++ (stv0900_get_bits(i_params, sfr_field2) << 16) +
++ (stv0900_get_bits(i_params, sfr_field1) << 8) +
++ (stv0900_get_bits(i_params, sfr_field0));
++ dprintk("lock: srate=%d r0=0x%x r1=0x%x r2=0x%x r3=0x%x \n",
++ srate, stv0900_get_bits(i_params, sfr_field0),
++ stv0900_get_bits(i_params, sfr_field1),
++ stv0900_get_bits(i_params, sfr_field2),
++ stv0900_get_bits(i_params, sfr_field3));
++
++ intval1 = (mclk) >> 16;
++ intval2 = (srate) >> 16;
++
++ rem1 = (mclk) % 0x10000;
++ rem2 = (srate) % 0x10000;
++ srate = (intval1 * intval2) +
++ ((intval1 * rem2) >> 16) +
++ ((intval2 * rem1) >> 16);
++
++ return srate;
++}
++
++static void stv0900_set_symbol_rate(struct stv0900_internal *i_params,
++ u32 mclk, u32 srate,
++ enum fe_stv0900_demod_num demod)
++{
++ s32 sfr_init_reg;
++ u32 symb;
++
++ dprintk(KERN_INFO "%s: Mclk %d, SR %d, Dmd %d\n", __func__, mclk,
++ srate, demod);
++
++ dmd_reg(sfr_init_reg, R0900_P1_SFRINIT1, R0900_P2_SFRINIT1);
++
++ if (srate > 60000000) {
++ symb = srate << 4;
++ symb /= (mclk >> 12);
++ } else if (srate > 6000000) {
++ symb = srate << 6;
++ symb /= (mclk >> 10);
++ } else {
++ symb = srate << 9;
++ symb /= (mclk >> 7);
++ }
++
++ stv0900_write_reg(i_params, sfr_init_reg, (symb >> 8) & 0x7F);
++ stv0900_write_reg(i_params, sfr_init_reg + 1, (symb & 0xFF));
++}
++
++static void stv0900_set_max_symbol_rate(struct stv0900_internal *i_params,
++ u32 mclk, u32 srate,
++ enum fe_stv0900_demod_num demod)
++{
++ s32 sfr_max_reg;
++ u32 symb;
++
++ dmd_reg(sfr_max_reg, R0900_P1_SFRUP1, R0900_P2_SFRUP1);
++
++ srate = 105 * (srate / 100);
++
++ if (srate > 60000000) {
++ symb = srate << 4;
++ symb /= (mclk >> 12);
++ } else if (srate > 6000000) {
++ symb = srate << 6;
++ symb /= (mclk >> 10);
++ } else {
++ symb = srate << 9;
++ symb /= (mclk >> 7);
++ }
++
++ if (symb < 0x7fff) {
++ stv0900_write_reg(i_params, sfr_max_reg, (symb >> 8) & 0x7F);
++ stv0900_write_reg(i_params, sfr_max_reg + 1, (symb & 0xFF));
++ } else {
++ stv0900_write_reg(i_params, sfr_max_reg, 0x7F);
++ stv0900_write_reg(i_params, sfr_max_reg + 1, 0xFF);
++ }
++}
++
++static void stv0900_set_min_symbol_rate(struct stv0900_internal *i_params,
++ u32 mclk, u32 srate,
++ enum fe_stv0900_demod_num demod)
++{
++ s32 sfr_min_reg;
++ u32 symb;
++
++ dmd_reg(sfr_min_reg, R0900_P1_SFRLOW1, R0900_P2_SFRLOW1);
++
++ srate = 95 * (srate / 100);
++ if (srate > 60000000) {
++ symb = srate << 4;
++ symb /= (mclk >> 12);
++
++ } else if (srate > 6000000) {
++ symb = srate << 6;
++ symb /= (mclk >> 10);
++
++ } else {
++ symb = srate << 9;
++ symb /= (mclk >> 7);
++ }
++
++ stv0900_write_reg(i_params, sfr_min_reg, (symb >> 8) & 0xFF);
++ stv0900_write_reg(i_params, sfr_min_reg + 1, (symb & 0xFF));
++}
++
++static s32 stv0900_get_timing_offst(struct stv0900_internal *i_params,
++ u32 srate,
++ enum fe_stv0900_demod_num demod)
++{
++ s32 tmgreg,
++ timingoffset;
++
++ dmd_reg(tmgreg, R0900_P1_TMGREG2, R0900_P2_TMGREG2);
++
++ timingoffset = (stv0900_read_reg(i_params, tmgreg) << 16) +
++ (stv0900_read_reg(i_params, tmgreg + 1) << 8) +
++ (stv0900_read_reg(i_params, tmgreg + 2));
++
++ timingoffset = ge2comp(timingoffset, 24);
++
++
++ if (timingoffset == 0)
++ timingoffset = 1;
++
++ timingoffset = ((s32)srate * 10) / ((s32)0x1000000 / timingoffset);
++ timingoffset /= 320;
++
++ return timingoffset;
++}
++
++static void stv0900_set_dvbs2_rolloff(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod)
++{
++ s32 rolloff, man_fld, matstr_reg, rolloff_ctl_fld;
++
++ dmd_reg(man_fld, F0900_P1_MANUAL_ROLLOFF, F0900_P2_MANUAL_ROLLOFF);
++ dmd_reg(matstr_reg, R0900_P1_MATSTR1, R0900_P2_MATSTR1);
++ dmd_reg(rolloff_ctl_fld, F0900_P1_ROLLOFF_CONTROL,
++ F0900_P2_ROLLOFF_CONTROL);
++
++ if (i_params->chip_id == 0x10) {
++ stv0900_write_bits(i_params, man_fld, 1);
++ rolloff = stv0900_read_reg(i_params, matstr_reg) & 0x03;
++ stv0900_write_bits(i_params, rolloff_ctl_fld, rolloff);
++ } else
++ stv0900_write_bits(i_params, man_fld, 0);
++}
++
++static u32 stv0900_carrier_width(u32 srate, enum fe_stv0900_rolloff ro)
++{
++ u32 rolloff;
++
++ switch (ro) {
++ case STV0900_20:
++ rolloff = 20;
++ break;
++ case STV0900_25:
++ rolloff = 25;
++ break;
++ case STV0900_35:
++ default:
++ rolloff = 35;
++ break;
++ }
++
++ return srate + (srate * rolloff) / 100;
++}
++
++static int stv0900_check_timing_lock(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod)
++{
++ int timingLock = FALSE;
++ s32 i,
++ timingcpt = 0;
++ u8 carFreq,
++ tmgTHhigh,
++ tmgTHLow;
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ carFreq = stv0900_read_reg(i_params, R0900_P1_CARFREQ);
++ tmgTHhigh = stv0900_read_reg(i_params, R0900_P1_TMGTHRISE);
++ tmgTHLow = stv0900_read_reg(i_params, R0900_P1_TMGTHFALL);
++ stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0x20);
++ stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0x0);
++ stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0);
++ stv0900_write_reg(i_params, R0900_P1_RTC, 0x80);
++ stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x40);
++ stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x0);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT1, 0x0);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT0, 0x0);
++ stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x65);
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
++ msleep(7);
++
++ for (i = 0; i < 10; i++) {
++ if (stv0900_get_bits(i_params, F0900_P1_TMGLOCK_QUALITY) >= 2)
++ timingcpt++;
++
++ msleep(1);
++ }
++
++ if (timingcpt >= 3)
++ timingLock = TRUE;
++
++ stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38);
++ stv0900_write_reg(i_params, R0900_P1_RTC, 0x88);
++ stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x68);
++ stv0900_write_reg(i_params, R0900_P1_CARFREQ, carFreq);
++ stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, tmgTHhigh);
++ stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, tmgTHLow);
++ break;
++ case STV0900_DEMOD_2:
++ carFreq = stv0900_read_reg(i_params, R0900_P2_CARFREQ);
++ tmgTHhigh = stv0900_read_reg(i_params, R0900_P2_TMGTHRISE);
++ tmgTHLow = stv0900_read_reg(i_params, R0900_P2_TMGTHFALL);
++ stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0x20);
++ stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0);
++ stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0);
++ stv0900_write_reg(i_params, R0900_P2_RTC, 0x80);
++ stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x40);
++ stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x0);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT1, 0x0);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT0, 0x0);
++ stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x65);
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
++ msleep(5);
++ for (i = 0; i < 10; i++) {
++ if (stv0900_get_bits(i_params, F0900_P2_TMGLOCK_QUALITY) >= 2)
++ timingcpt++;
++
++ msleep(1);
++ }
++
++ if (timingcpt >= 3)
++ timingLock = TRUE;
++
++ stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38);
++ stv0900_write_reg(i_params, R0900_P2_RTC, 0x88);
++ stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x68);
++ stv0900_write_reg(i_params, R0900_P2_CARFREQ, carFreq);
++ stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, tmgTHhigh);
++ stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, tmgTHLow);
++ break;
++ }
++
++ return timingLock;
++}
++
++static int stv0900_get_demod_cold_lock(struct dvb_frontend *fe,
++ s32 demod_timeout)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *i_params = state->internal;
++ enum fe_stv0900_demod_num demod = state->demod;
++
++ int lock = FALSE;
++ s32 srate, search_range, locktimeout,
++ currier_step, nb_steps, current_step,
++ direction, tuner_freq, timeout;
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ srate = i_params->dmd1_symbol_rate;
++ search_range = i_params->dmd1_srch_range;
++ break;
++
++ case STV0900_DEMOD_2:
++ srate = i_params->dmd2_symbol_rate;
++ search_range = i_params->dmd2_srch_range;
++ break;
++ }
++
++ if (srate >= 10000000)
++ locktimeout = demod_timeout / 3;
++ else
++ locktimeout = demod_timeout / 2;
++
++ lock = stv0900_get_demod_lock(i_params, demod, locktimeout);
++
++ if (lock == FALSE) {
++ if (srate >= 10000000) {
++ if (stv0900_check_timing_lock(i_params, demod) == TRUE) {
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1f);
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15);
++ break;
++ case STV0900_DEMOD_2:
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1f);
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15);
++ break;
++ }
++
++ lock = stv0900_get_demod_lock(i_params, demod, demod_timeout);
++ } else
++ lock = FALSE;
++ } else {
++ if (srate <= 4000000)
++ currier_step = 1000;
++ else if (srate <= 7000000)
++ currier_step = 2000;
++ else if (srate <= 10000000)
++ currier_step = 3000;
++ else
++ currier_step = 5000;
++
++ nb_steps = ((search_range / 1000) / currier_step);
++ nb_steps /= 2;
++ nb_steps = (2 * (nb_steps + 1));
++ if (nb_steps < 0)
++ nb_steps = 2;
++ else if (nb_steps > 12)
++ nb_steps = 12;
++
++ current_step = 1;
++ direction = 1;
++ timeout = (demod_timeout / 3);
++ if (timeout > 1000)
++ timeout = 1000;
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ if (lock == FALSE) {
++ tuner_freq = i_params->tuner1_freq;
++ i_params->tuner1_bw = stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + i_params->dmd1_symbol_rate;
++
++ while ((current_step <= nb_steps) && (lock == FALSE)) {
++
++ if (direction > 0)
++ tuner_freq += (current_step * currier_step);
++ else
++ tuner_freq -= (current_step * currier_step);
++
++ stv0900_set_tuner(fe, tuner_freq, i_params->tuner1_bw);
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1C);
++ if (i_params->dmd1_srch_standard == STV0900_SEARCH_DVBS2) {
++ stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1);
++ }
++
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT1, 0);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT0, 0);
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F);
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15);
++ lock = stv0900_get_demod_lock(i_params, demod, timeout);
++ direction *= -1;
++ current_step++;
++ }
++ }
++ break;
++ case STV0900_DEMOD_2:
++ if (lock == FALSE) {
++ tuner_freq = i_params->tuner2_freq;
++ i_params->tuner2_bw = stv0900_carrier_width(srate, i_params->rolloff) + srate;
++
++ while ((current_step <= nb_steps) && (lock == FALSE)) {
++
++ if (direction > 0)
++ tuner_freq += (current_step * currier_step);
++ else
++ tuner_freq -= (current_step * currier_step);
++
++ stv0900_set_tuner(fe, tuner_freq, i_params->tuner2_bw);
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1C);
++ if (i_params->dmd2_srch_stndrd == STV0900_SEARCH_DVBS2) {
++ stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1);
++ }
++
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT1, 0);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT0, 0);
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F);
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15);
++ lock = stv0900_get_demod_lock(i_params, demod, timeout);
++ direction *= -1;
++ current_step++;
++ }
++ }
++ break;
++ }
++ }
++ }
++
++ return lock;
++}
++
++static void stv0900_get_lock_timeout(s32 *demod_timeout, s32 *fec_timeout,
++ s32 srate,
++ enum fe_stv0900_search_algo algo)
++{
++ switch (algo) {
++ case STV0900_BLIND_SEARCH:
++ if (srate <= 1500000) {
++ (*demod_timeout) = 1500;
++ (*fec_timeout) = 400;
++ } else if (srate <= 5000000) {
++ (*demod_timeout) = 1000;
++ (*fec_timeout) = 300;
++ } else {
++ (*demod_timeout) = 700;
++ (*fec_timeout) = 100;
++ }
++
++ break;
++ case STV0900_COLD_START:
++ case STV0900_WARM_START:
++ default:
++ if (srate <= 1000000) {
++ (*demod_timeout) = 3000;
++ (*fec_timeout) = 1700;
++ } else if (srate <= 2000000) {
++ (*demod_timeout) = 2500;
++ (*fec_timeout) = 1100;
++ } else if (srate <= 5000000) {
++ (*demod_timeout) = 1000;
++ (*fec_timeout) = 550;
++ } else if (srate <= 10000000) {
++ (*demod_timeout) = 700;
++ (*fec_timeout) = 250;
++ } else if (srate <= 20000000) {
++ (*demod_timeout) = 400;
++ (*fec_timeout) = 130;
++ }
++
++ else {
++ (*demod_timeout) = 300;
++ (*fec_timeout) = 100;
++ }
++
++ break;
++
++ }
++
++ if (algo == STV0900_WARM_START)
++ (*demod_timeout) /= 2;
++}
++
++static void stv0900_set_viterbi_tracq(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod)
++{
++
++ s32 vth_reg;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ dmd_reg(vth_reg, R0900_P1_VTH12, R0900_P2_VTH12);
++
++ stv0900_write_reg(i_params, vth_reg++, 0xd0);
++ stv0900_write_reg(i_params, vth_reg++, 0x7d);
++ stv0900_write_reg(i_params, vth_reg++, 0x53);
++ stv0900_write_reg(i_params, vth_reg++, 0x2F);
++ stv0900_write_reg(i_params, vth_reg++, 0x24);
++ stv0900_write_reg(i_params, vth_reg++, 0x1F);
++}
++
++static void stv0900_set_viterbi_standard(struct stv0900_internal *i_params,
++ enum fe_stv0900_search_standard Standard,
++ enum fe_stv0900_fec PunctureRate,
++ enum fe_stv0900_demod_num demod)
++{
++
++ s32 fecmReg,
++ prvitReg;
++
++ dprintk(KERN_INFO "%s: ViterbiStandard = ", __func__);
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ fecmReg = R0900_P1_FECM;
++ prvitReg = R0900_P1_PRVIT;
++ break;
++ case STV0900_DEMOD_2:
++ fecmReg = R0900_P2_FECM;
++ prvitReg = R0900_P2_PRVIT;
++ break;
++ }
++
++ switch (Standard) {
++ case STV0900_AUTO_SEARCH:
++ dprintk("Auto\n");
++ stv0900_write_reg(i_params, fecmReg, 0x10);
++ stv0900_write_reg(i_params, prvitReg, 0x3F);
++ break;
++ case STV0900_SEARCH_DVBS1:
++ dprintk("DVBS1\n");
++ stv0900_write_reg(i_params, fecmReg, 0x00);
++ switch (PunctureRate) {
++ case STV0900_FEC_UNKNOWN:
++ default:
++ stv0900_write_reg(i_params, prvitReg, 0x2F);
++ break;
++ case STV0900_FEC_1_2:
++ stv0900_write_reg(i_params, prvitReg, 0x01);
++ break;
++ case STV0900_FEC_2_3:
++ stv0900_write_reg(i_params, prvitReg, 0x02);
++ break;
++ case STV0900_FEC_3_4:
++ stv0900_write_reg(i_params, prvitReg, 0x04);
++ break;
++ case STV0900_FEC_5_6:
++ stv0900_write_reg(i_params, prvitReg, 0x08);
++ break;
++ case STV0900_FEC_7_8:
++ stv0900_write_reg(i_params, prvitReg, 0x20);
++ break;
++ }
++
++ break;
++ case STV0900_SEARCH_DSS:
++ dprintk("DSS\n");
++ stv0900_write_reg(i_params, fecmReg, 0x80);
++ switch (PunctureRate) {
++ case STV0900_FEC_UNKNOWN:
++ default:
++ stv0900_write_reg(i_params, prvitReg, 0x13);
++ break;
++ case STV0900_FEC_1_2:
++ stv0900_write_reg(i_params, prvitReg, 0x01);
++ break;
++ case STV0900_FEC_2_3:
++ stv0900_write_reg(i_params, prvitReg, 0x02);
++ break;
++ case STV0900_FEC_6_7:
++ stv0900_write_reg(i_params, prvitReg, 0x10);
++ break;
++ }
++ break;
++ default:
++ break;
++ }
++}
++
++static void stv0900_track_optimization(struct dvb_frontend *fe)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *i_params = state->internal;
++ enum fe_stv0900_demod_num demod = state->demod;
++
++ s32 srate, pilots, aclc, freq1, freq0,
++ i = 0, timed, timef, blindTunSw = 0;
++
++ enum fe_stv0900_rolloff rolloff;
++ enum fe_stv0900_modcode foundModcod;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
++ srate += stv0900_get_timing_offst(i_params, srate, demod);
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ switch (i_params->dmd1_rslts.standard) {
++ case STV0900_DVBS1_STANDARD:
++ if (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH) {
++ stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0);
++ }
++
++ stv0900_write_bits(i_params, F0900_P1_ROLLOFF_CONTROL, i_params->rolloff);
++ stv0900_write_bits(i_params, F0900_P1_MANUAL_ROLLOFF, 1);
++ stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x75);
++ break;
++ case STV0900_DSS_STANDARD:
++ if (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH) {
++ stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0);
++ }
++
++ stv0900_write_bits(i_params, F0900_P1_ROLLOFF_CONTROL, i_params->rolloff);
++ stv0900_write_bits(i_params, F0900_P1_MANUAL_ROLLOFF, 1);
++ stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x75);
++ break;
++ case STV0900_DVBS2_STANDARD:
++ stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1);
++ stv0900_write_reg(i_params, R0900_P1_ACLC, 0);
++ stv0900_write_reg(i_params, R0900_P1_BCLC, 0);
++ if (i_params->dmd1_rslts.frame_length == STV0900_LONG_FRAME) {
++ foundModcod = stv0900_get_bits(i_params, F0900_P1_DEMOD_MODCOD);
++ pilots = stv0900_get_bits(i_params, F0900_P1_DEMOD_TYPE) & 0x01;
++ aclc = stv0900_get_optim_carr_loop(srate, foundModcod, pilots, i_params->chip_id);
++ if (foundModcod <= STV0900_QPSK_910)
++ stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, aclc);
++ else if (foundModcod <= STV0900_8PSK_910) {
++ stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a);
++ stv0900_write_reg(i_params, R0900_P1_ACLC2S28, aclc);
++ }
++
++ if ((i_params->demod_mode == STV0900_SINGLE) && (foundModcod > STV0900_8PSK_910)) {
++ if (foundModcod <= STV0900_16APSK_910) {
++ stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a);
++ stv0900_write_reg(i_params, R0900_P1_ACLC2S216A, aclc);
++ } else if (foundModcod <= STV0900_32APSK_910) {
++ stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a);
++ stv0900_write_reg(i_params, R0900_P1_ACLC2S232A, aclc);
++ }
++ }
++
++ } else {
++ aclc = stv0900_get_optim_short_carr_loop(srate, i_params->dmd1_rslts.modulation, i_params->chip_id);
++ if (i_params->dmd1_rslts.modulation == STV0900_QPSK)
++ stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, aclc);
++
++ else if (i_params->dmd1_rslts.modulation == STV0900_8PSK) {
++ stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a);
++ stv0900_write_reg(i_params, R0900_P1_ACLC2S28, aclc);
++ } else if (i_params->dmd1_rslts.modulation == STV0900_16APSK) {
++ stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a);
++ stv0900_write_reg(i_params, R0900_P1_ACLC2S216A, aclc);
++ } else if (i_params->dmd1_rslts.modulation == STV0900_32APSK) {
++ stv0900_write_reg(i_params, R0900_P1_ACLC2S2Q, 0x2a);
++ stv0900_write_reg(i_params, R0900_P1_ACLC2S232A, aclc);
++ }
++
++ }
++
++ if (i_params->chip_id <= 0x11) {
++ if (i_params->demod_mode != STV0900_SINGLE)
++ stv0900_activate_s2_modcode(i_params, demod);
++
++ }
++
++ stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x67);
++ break;
++ case STV0900_UNKNOWN_STANDARD:
++ default:
++ stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1);
++ break;
++ }
++
++ freq1 = stv0900_read_reg(i_params, R0900_P1_CFR2);
++ freq0 = stv0900_read_reg(i_params, R0900_P1_CFR1);
++ rolloff = stv0900_get_bits(i_params, F0900_P1_ROLLOFF_STATUS);
++ if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) {
++ stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x00);
++ stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0);
++ stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x01);
++ stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod);
++ stv0900_set_max_symbol_rate(i_params, i_params->mclk, srate, demod);
++ stv0900_set_min_symbol_rate(i_params, i_params->mclk, srate, demod);
++ blindTunSw = 1;
++ }
++
++ if (i_params->chip_id >= 0x20) {
++ if ((i_params->dmd1_srch_standard == STV0900_SEARCH_DVBS1) || (i_params->dmd1_srch_standard == STV0900_SEARCH_DSS) || (i_params->dmd1_srch_standard == STV0900_AUTO_SEARCH)) {
++ stv0900_write_reg(i_params, R0900_P1_VAVSRVIT, 0x0a);
++ stv0900_write_reg(i_params, R0900_P1_VITSCALE, 0x0);
++ }
++ }
++
++ if (i_params->chip_id < 0x20)
++ stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x08);
++
++ if (i_params->chip_id == 0x10)
++ stv0900_write_reg(i_params, R0900_P1_CORRELEXP, 0x0A);
++
++ stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38);
++
++ if ((i_params->chip_id >= 0x20) || (blindTunSw == 1) || (i_params->dmd1_symbol_rate < 10000000)) {
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0);
++ i_params->tuner1_bw = stv0900_carrier_width(srate, i_params->rolloff) + 10000000;
++
++ if ((i_params->chip_id >= 0x20) || (blindTunSw == 1)) {
++ if (i_params->dmd1_srch_algo != STV0900_WARM_START)
++ stv0900_set_bandwidth(fe, i_params->tuner1_bw);
++ }
++
++ if ((i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd1_symbol_rate < 10000000))
++ msleep(50);
++ else
++ msleep(5);
++
++ stv0900_get_lock_timeout(&timed, &timef, srate, STV0900_WARM_START);
++
++ if (stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) {
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0);
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
++ i = 0;
++ while ((stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) && (i <= 2)) {
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0);
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
++ i++;
++ }
++ }
++
++ }
++
++ if (i_params->chip_id >= 0x20)
++ stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x49);
++
++ if ((i_params->dmd1_rslts.standard == STV0900_DVBS1_STANDARD) || (i_params->dmd1_rslts.standard == STV0900_DSS_STANDARD))
++ stv0900_set_viterbi_tracq(i_params, demod);
++
++ break;
++
++ case STV0900_DEMOD_2:
++ switch (i_params->dmd2_rslts.standard) {
++ case STV0900_DVBS1_STANDARD:
++
++ if (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH) {
++ stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0);
++ }
++
++ stv0900_write_bits(i_params, F0900_P2_ROLLOFF_CONTROL, i_params->rolloff);
++ stv0900_write_bits(i_params, F0900_P2_MANUAL_ROLLOFF, 1);
++ stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x75);
++ break;
++ case STV0900_DSS_STANDARD:
++ if (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH) {
++ stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0);
++ }
++
++ stv0900_write_bits(i_params, F0900_P2_ROLLOFF_CONTROL, i_params->rolloff);
++ stv0900_write_bits(i_params, F0900_P2_MANUAL_ROLLOFF, 1);
++ stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x75);
++ break;
++ case STV0900_DVBS2_STANDARD:
++ stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1);
++ stv0900_write_reg(i_params, R0900_P2_ACLC, 0);
++ stv0900_write_reg(i_params, R0900_P2_BCLC, 0);
++ if (i_params->dmd2_rslts.frame_length == STV0900_LONG_FRAME) {
++ foundModcod = stv0900_get_bits(i_params, F0900_P2_DEMOD_MODCOD);
++ pilots = stv0900_get_bits(i_params, F0900_P2_DEMOD_TYPE) & 0x01;
++ aclc = stv0900_get_optim_carr_loop(srate, foundModcod, pilots, i_params->chip_id);
++ if (foundModcod <= STV0900_QPSK_910)
++ stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, aclc);
++ else if (foundModcod <= STV0900_8PSK_910) {
++ stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a);
++ stv0900_write_reg(i_params, R0900_P2_ACLC2S28, aclc);
++ }
++
++ if ((i_params->demod_mode == STV0900_SINGLE) && (foundModcod > STV0900_8PSK_910)) {
++ if (foundModcod <= STV0900_16APSK_910) {
++ stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a);
++ stv0900_write_reg(i_params, R0900_P2_ACLC2S216A, aclc);
++ } else if (foundModcod <= STV0900_32APSK_910) {
++ stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a);
++ stv0900_write_reg(i_params, R0900_P2_ACLC2S232A, aclc);
++ }
++
++ }
++
++ } else {
++ aclc = stv0900_get_optim_short_carr_loop(srate,
++ i_params->dmd2_rslts.modulation,
++ i_params->chip_id);
++
++ if (i_params->dmd2_rslts.modulation == STV0900_QPSK)
++ stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, aclc);
++
++ else if (i_params->dmd2_rslts.modulation == STV0900_8PSK) {
++ stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a);
++ stv0900_write_reg(i_params, R0900_P2_ACLC2S28, aclc);
++ } else if (i_params->dmd2_rslts.modulation == STV0900_16APSK) {
++ stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a);
++ stv0900_write_reg(i_params, R0900_P2_ACLC2S216A, aclc);
++ } else if (i_params->dmd2_rslts.modulation == STV0900_32APSK) {
++ stv0900_write_reg(i_params, R0900_P2_ACLC2S2Q, 0x2a);
++ stv0900_write_reg(i_params, R0900_P2_ACLC2S232A, aclc);
++ }
++ }
++
++ stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x67);
++
++ break;
++ case STV0900_UNKNOWN_STANDARD:
++ default:
++ stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1);
++ break;
++ }
++
++ freq1 = stv0900_read_reg(i_params, R0900_P2_CFR2);
++ freq0 = stv0900_read_reg(i_params, R0900_P2_CFR1);
++ rolloff = stv0900_get_bits(i_params, F0900_P2_ROLLOFF_STATUS);
++ if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) {
++ stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x00);
++ stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0);
++ stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x01);
++ stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod);
++ stv0900_set_max_symbol_rate(i_params, i_params->mclk, srate, demod);
++ stv0900_set_min_symbol_rate(i_params, i_params->mclk, srate, demod);
++ blindTunSw = 1;
++ }
++
++ if (i_params->chip_id >= 0x20) {
++ if ((i_params->dmd2_srch_stndrd == STV0900_SEARCH_DVBS1) || (i_params->dmd2_srch_stndrd == STV0900_SEARCH_DSS) || (i_params->dmd2_srch_stndrd == STV0900_AUTO_SEARCH)) {
++ stv0900_write_reg(i_params, R0900_P2_VAVSRVIT, 0x0a);
++ stv0900_write_reg(i_params, R0900_P2_VITSCALE, 0x0);
++ }
++ }
++
++ if (i_params->chip_id < 0x20)
++ stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x08);
++
++ if (i_params->chip_id == 0x10)
++ stv0900_write_reg(i_params, R0900_P2_CORRELEXP, 0x0a);
++
++ stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38);
++ if ((i_params->chip_id >= 0x20) || (blindTunSw == 1) || (i_params->dmd2_symbol_rate < 10000000)) {
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0);
++ i_params->tuner2_bw = stv0900_carrier_width(srate, i_params->rolloff) + 10000000;
++
++ if ((i_params->chip_id >= 0x20) || (blindTunSw == 1)) {
++ if (i_params->dmd2_srch_algo != STV0900_WARM_START)
++ stv0900_set_bandwidth(fe, i_params->tuner2_bw);
++ }
++
++ if ((i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd2_symbol_rate < 10000000))
++ msleep(50);
++ else
++ msleep(5);
++
++ stv0900_get_lock_timeout(&timed, &timef, srate, STV0900_WARM_START);
++ if (stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) {
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0);
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
++ i = 0;
++ while ((stv0900_get_demod_lock(i_params, demod, timed / 2) == FALSE) && (i <= 2)) {
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0);
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
++ i++;
++ }
++ }
++ }
++
++ if (i_params->chip_id >= 0x20)
++ stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x49);
++
++ if ((i_params->dmd2_rslts.standard == STV0900_DVBS1_STANDARD) || (i_params->dmd2_rslts.standard == STV0900_DSS_STANDARD))
++ stv0900_set_viterbi_tracq(i_params, demod);
++
++ break;
++ }
++}
++
++static int stv0900_get_fec_lock(struct stv0900_internal *i_params, enum fe_stv0900_demod_num demod, s32 time_out)
++{
++ s32 timer = 0, lock = 0, header_field, pktdelin_field, lock_vit_field;
++
++ enum fe_stv0900_search_state dmd_state;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ dmd_reg(header_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE);
++ dmd_reg(pktdelin_field, F0900_P1_PKTDELIN_LOCK, F0900_P2_PKTDELIN_LOCK);
++ dmd_reg(lock_vit_field, F0900_P1_LOCKEDVIT, F0900_P2_LOCKEDVIT);
++
++ dmd_state = stv0900_get_bits(i_params, header_field);
++
++ while ((timer < time_out) && (lock == 0)) {
++ switch (dmd_state) {
++ case STV0900_SEARCH:
++ case STV0900_PLH_DETECTED:
++ default:
++ lock = 0;
++ break;
++ case STV0900_DVBS2_FOUND:
++ lock = stv0900_get_bits(i_params, pktdelin_field);
++ break;
++ case STV0900_DVBS_FOUND:
++ lock = stv0900_get_bits(i_params, lock_vit_field);
++ break;
++ }
++
++ if (lock == 0) {
++ msleep(10);
++ timer += 10;
++ }
++ }
++
++ if (lock)
++ dprintk("DEMOD FEC LOCK OK\n");
++ else
++ dprintk("DEMOD FEC LOCK FAIL\n");
++
++ return lock;
++}
++
++static int stv0900_wait_for_lock(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod,
++ s32 dmd_timeout, s32 fec_timeout)
++{
++
++ s32 timer = 0, lock = 0, str_merg_rst_fld, str_merg_lock_fld;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ dmd_reg(str_merg_rst_fld, F0900_P1_RST_HWARE, F0900_P2_RST_HWARE);
++ dmd_reg(str_merg_lock_fld, F0900_P1_TSFIFO_LINEOK, F0900_P2_TSFIFO_LINEOK);
++
++ lock = stv0900_get_demod_lock(i_params, demod, dmd_timeout);
++
++ if (lock)
++ lock = lock && stv0900_get_fec_lock(i_params, demod, fec_timeout);
++
++ if (lock) {
++ lock = 0;
++
++ dprintk(KERN_INFO "%s: Timer = %d, time_out = %d\n", __func__, timer, fec_timeout);
++
++ while ((timer < fec_timeout) && (lock == 0)) {
++ lock = stv0900_get_bits(i_params, str_merg_lock_fld);
++ msleep(1);
++ timer++;
++ }
++ }
++
++ if (lock)
++ dprintk(KERN_INFO "%s: DEMOD LOCK OK\n", __func__);
++ else
++ dprintk(KERN_INFO "%s: DEMOD LOCK FAIL\n", __func__);
++
++ if (lock)
++ return TRUE;
++ else
++ return FALSE;
++}
++
++enum fe_stv0900_tracking_standard stv0900_get_standard(struct dvb_frontend *fe,
++ enum fe_stv0900_demod_num demod)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *i_params = state->internal;
++ enum fe_stv0900_tracking_standard fnd_standard;
++ s32 state_field,
++ dss_dvb_field;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ dmd_reg(state_field, F0900_P1_HEADER_MODE, F0900_P2_HEADER_MODE);
++ dmd_reg(dss_dvb_field, F0900_P1_DSS_DVB, F0900_P2_DSS_DVB);
++
++ if (stv0900_get_bits(i_params, state_field) == 2)
++ fnd_standard = STV0900_DVBS2_STANDARD;
++
++ else if (stv0900_get_bits(i_params, state_field) == 3) {
++ if (stv0900_get_bits(i_params, dss_dvb_field) == 1)
++ fnd_standard = STV0900_DSS_STANDARD;
++ else
++ fnd_standard = STV0900_DVBS1_STANDARD;
++ } else
++ fnd_standard = STV0900_UNKNOWN_STANDARD;
++
++ return fnd_standard;
++}
++
++static s32 stv0900_get_carr_freq(struct stv0900_internal *i_params, u32 mclk,
++ enum fe_stv0900_demod_num demod)
++{
++ s32 cfr_field2, cfr_field1, cfr_field0,
++ derot, rem1, rem2, intval1, intval2;
++
++ dmd_reg(cfr_field2, F0900_P1_CAR_FREQ2, F0900_P2_CAR_FREQ2);
++ dmd_reg(cfr_field1, F0900_P1_CAR_FREQ1, F0900_P2_CAR_FREQ1);
++ dmd_reg(cfr_field0, F0900_P1_CAR_FREQ0, F0900_P2_CAR_FREQ0);
++
++ derot = (stv0900_get_bits(i_params, cfr_field2) << 16) +
++ (stv0900_get_bits(i_params, cfr_field1) << 8) +
++ (stv0900_get_bits(i_params, cfr_field0));
++
++ derot = ge2comp(derot, 24);
++ intval1 = mclk >> 12;
++ intval2 = derot >> 12;
++ rem1 = mclk % 0x1000;
++ rem2 = derot % 0x1000;
++ derot = (intval1 * intval2) +
++ ((intval1 * rem2) >> 12) +
++ ((intval2 * rem1) >> 12);
++
++ return derot;
++}
++
++static u32 stv0900_get_tuner_freq(struct dvb_frontend *fe)
++{
++ struct dvb_frontend_ops *frontend_ops = NULL;
++ struct dvb_tuner_ops *tuner_ops = NULL;
++ u32 frequency = 0;
++
++ if (&fe->ops)
++ frontend_ops = &fe->ops;
++
++ if (&frontend_ops->tuner_ops)
++ tuner_ops = &frontend_ops->tuner_ops;
++
++ if (tuner_ops->get_frequency) {
++ if ((tuner_ops->get_frequency(fe, &frequency)) < 0)
++ dprintk("%s: Invalid parameter\n", __func__);
++ else
++ dprintk("%s: Frequency=%d\n", __func__, frequency);
++
++ }
++
++ return frequency;
++}
++
++static enum fe_stv0900_fec stv0900_get_vit_fec(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod)
++{
++ s32 rate_fld, vit_curpun_fld;
++ enum fe_stv0900_fec prate;
++
++ dmd_reg(vit_curpun_fld, F0900_P1_VIT_CURPUN, F0900_P2_VIT_CURPUN);
++ rate_fld = stv0900_get_bits(i_params, vit_curpun_fld);
++
++ switch (rate_fld) {
++ case 13:
++ prate = STV0900_FEC_1_2;
++ break;
++ case 18:
++ prate = STV0900_FEC_2_3;
++ break;
++ case 21:
++ prate = STV0900_FEC_3_4;
++ break;
++ case 24:
++ prate = STV0900_FEC_5_6;
++ break;
++ case 25:
++ prate = STV0900_FEC_6_7;
++ break;
++ case 26:
++ prate = STV0900_FEC_7_8;
++ break;
++ default:
++ prate = STV0900_FEC_UNKNOWN;
++ break;
++ }
++
++ return prate;
++}
++
++static enum fe_stv0900_signal_type stv0900_get_signal_params(struct dvb_frontend *fe)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *i_params = state->internal;
++ enum fe_stv0900_demod_num demod = state->demod;
++ enum fe_stv0900_signal_type range = STV0900_OUTOFRANGE;
++ s32 offsetFreq,
++ srate_offset,
++ i = 0;
++
++ u8 timing;
++
++ msleep(5);
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) {
++ timing = stv0900_read_reg(i_params, R0900_P1_TMGREG2);
++ i = 0;
++ stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x5c);
++
++ while ((i <= 50) && (timing != 0) && (timing != 0xFF)) {
++ timing = stv0900_read_reg(i_params, R0900_P1_TMGREG2);
++ msleep(5);
++ i += 5;
++ }
++ }
++
++ i_params->dmd1_rslts.standard = stv0900_get_standard(fe, demod);
++ i_params->dmd1_rslts.frequency = stv0900_get_tuner_freq(fe);
++ offsetFreq = stv0900_get_carr_freq(i_params, i_params->mclk, demod) / 1000;
++ i_params->dmd1_rslts.frequency += offsetFreq;
++ i_params->dmd1_rslts.symbol_rate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
++ srate_offset = stv0900_get_timing_offst(i_params, i_params->dmd1_rslts.symbol_rate, demod);
++ i_params->dmd1_rslts.symbol_rate += srate_offset;
++ i_params->dmd1_rslts.fec = stv0900_get_vit_fec(i_params, demod);
++ i_params->dmd1_rslts.modcode = stv0900_get_bits(i_params, F0900_P1_DEMOD_MODCOD);
++ i_params->dmd1_rslts.pilot = stv0900_get_bits(i_params, F0900_P1_DEMOD_TYPE) & 0x01;
++ i_params->dmd1_rslts.frame_length = ((u32)stv0900_get_bits(i_params, F0900_P1_DEMOD_TYPE)) >> 1;
++ i_params->dmd1_rslts.rolloff = stv0900_get_bits(i_params, F0900_P1_ROLLOFF_STATUS);
++ switch (i_params->dmd1_rslts.standard) {
++ case STV0900_DVBS2_STANDARD:
++ i_params->dmd1_rslts.spectrum = stv0900_get_bits(i_params, F0900_P1_SPECINV_DEMOD);
++ if (i_params->dmd1_rslts.modcode <= STV0900_QPSK_910)
++ i_params->dmd1_rslts.modulation = STV0900_QPSK;
++ else if (i_params->dmd1_rslts.modcode <= STV0900_8PSK_910)
++ i_params->dmd1_rslts.modulation = STV0900_8PSK;
++ else if (i_params->dmd1_rslts.modcode <= STV0900_16APSK_910)
++ i_params->dmd1_rslts.modulation = STV0900_16APSK;
++ else if (i_params->dmd1_rslts.modcode <= STV0900_32APSK_910)
++ i_params->dmd1_rslts.modulation = STV0900_32APSK;
++ else
++ i_params->dmd1_rslts.modulation = STV0900_UNKNOWN;
++ break;
++ case STV0900_DVBS1_STANDARD:
++ case STV0900_DSS_STANDARD:
++ i_params->dmd1_rslts.spectrum = stv0900_get_bits(i_params, F0900_P1_IQINV);
++ i_params->dmd1_rslts.modulation = STV0900_QPSK;
++ break;
++ default:
++ break;
++ }
++
++ if ((i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd1_symbol_rate < 10000000)) {
++ offsetFreq = i_params->dmd1_rslts.frequency - i_params->tuner1_freq;
++ i_params->tuner1_freq = stv0900_get_tuner_freq(fe);
++ if (ABS(offsetFreq) <= ((i_params->dmd1_srch_range / 2000) + 500))
++ range = STV0900_RANGEOK;
++ else
++ if (ABS(offsetFreq) <= (stv0900_carrier_width(i_params->dmd1_rslts.symbol_rate, i_params->dmd1_rslts.rolloff) / 2000))
++ range = STV0900_RANGEOK;
++ else
++ range = STV0900_OUTOFRANGE;
++
++ } else {
++ if (ABS(offsetFreq) <= ((i_params->dmd1_srch_range / 2000) + 500))
++ range = STV0900_RANGEOK;
++ else
++ range = STV0900_OUTOFRANGE;
++ }
++ break;
++ case STV0900_DEMOD_2:
++ if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) {
++ timing = stv0900_read_reg(i_params, R0900_P2_TMGREG2);
++ i = 0;
++ stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x5c);
++
++ while ((i <= 50) && (timing != 0) && (timing != 0xff)) {
++ timing = stv0900_read_reg(i_params, R0900_P2_TMGREG2);
++ msleep(5);
++ i += 5;
++ }
++ }
++
++ i_params->dmd2_rslts.standard = stv0900_get_standard(fe, demod);
++ i_params->dmd2_rslts.frequency = stv0900_get_tuner_freq(fe);
++ offsetFreq = stv0900_get_carr_freq(i_params, i_params->mclk, demod) / 1000;
++ i_params->dmd2_rslts.frequency += offsetFreq;
++ i_params->dmd2_rslts.symbol_rate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
++ srate_offset = stv0900_get_timing_offst(i_params, i_params->dmd2_rslts.symbol_rate, demod);
++ i_params->dmd2_rslts.symbol_rate += srate_offset;
++ i_params->dmd2_rslts.fec = stv0900_get_vit_fec(i_params, demod);
++ i_params->dmd2_rslts.modcode = stv0900_get_bits(i_params, F0900_P2_DEMOD_MODCOD);
++ i_params->dmd2_rslts.pilot = stv0900_get_bits(i_params, F0900_P2_DEMOD_TYPE) & 0x01;
++ i_params->dmd2_rslts.frame_length = ((u32)stv0900_get_bits(i_params, F0900_P2_DEMOD_TYPE)) >> 1;
++ i_params->dmd2_rslts.rolloff = stv0900_get_bits(i_params, F0900_P2_ROLLOFF_STATUS);
++ switch (i_params->dmd2_rslts.standard) {
++ case STV0900_DVBS2_STANDARD:
++ i_params->dmd2_rslts.spectrum = stv0900_get_bits(i_params, F0900_P2_SPECINV_DEMOD);
++ if (i_params->dmd2_rslts.modcode <= STV0900_QPSK_910)
++ i_params->dmd2_rslts.modulation = STV0900_QPSK;
++ else if (i_params->dmd2_rslts.modcode <= STV0900_8PSK_910)
++ i_params->dmd2_rslts.modulation = STV0900_8PSK;
++ else if (i_params->dmd2_rslts.modcode <= STV0900_16APSK_910)
++ i_params->dmd2_rslts.modulation = STV0900_16APSK;
++ else if (i_params->dmd2_rslts.modcode <= STV0900_32APSK_910)
++ i_params->dmd2_rslts.modulation = STV0900_32APSK;
++ else
++ i_params->dmd2_rslts.modulation = STV0900_UNKNOWN;
++ break;
++ case STV0900_DVBS1_STANDARD:
++ case STV0900_DSS_STANDARD:
++ i_params->dmd2_rslts.spectrum = stv0900_get_bits(i_params, F0900_P2_IQINV);
++ i_params->dmd2_rslts.modulation = STV0900_QPSK;
++ break;
++ default:
++ break;
++ }
++
++ if ((i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) || (i_params->dmd2_symbol_rate < 10000000)) {
++ offsetFreq = i_params->dmd2_rslts.frequency - i_params->tuner2_freq;
++ i_params->tuner2_freq = stv0900_get_tuner_freq(fe);
++
++ if (ABS(offsetFreq) <= ((i_params->dmd2_srch_range / 2000) + 500))
++ range = STV0900_RANGEOK;
++ else
++ if (ABS(offsetFreq) <= (stv0900_carrier_width(i_params->dmd2_rslts.symbol_rate, i_params->dmd2_rslts.rolloff) / 2000))
++ range = STV0900_RANGEOK;
++ else
++ range = STV0900_OUTOFRANGE;
++ } else {
++ if (ABS(offsetFreq) <= ((i_params->dmd2_srch_range / 2000) + 500))
++ range = STV0900_RANGEOK;
++ else
++ range = STV0900_OUTOFRANGE;
++ }
++
++ break;
++ }
++
++ return range;
++}
++
++static enum fe_stv0900_signal_type stv0900_dvbs1_acq_workaround(struct dvb_frontend *fe)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *i_params = state->internal;
++ enum fe_stv0900_demod_num demod = state->demod;
++
++ s32 srate, demod_timeout,
++ fec_timeout, freq1, freq0;
++ enum fe_stv0900_signal_type signal_type = STV0900_NODATA;;
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ i_params->dmd1_rslts.locked = FALSE;
++ if (stv0900_get_bits(i_params, F0900_P1_HEADER_MODE) == STV0900_DVBS_FOUND) {
++ srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
++ srate += stv0900_get_timing_offst(i_params, srate, demod);
++ if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH)
++ stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod);
++
++ stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, srate, STV0900_WARM_START);
++ freq1 = stv0900_read_reg(i_params, R0900_P1_CFR2);
++ freq0 = stv0900_read_reg(i_params, R0900_P1_CFR1);
++ stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0);
++ stv0900_write_bits(i_params, F0900_P1_SPECINV_CONTROL, STV0900_IQ_FORCE_SWAPPED);
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1C);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0);
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
++ if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) {
++ i_params->dmd1_rslts.locked = TRUE;
++ signal_type = stv0900_get_signal_params(fe);
++ stv0900_track_optimization(fe);
++ } else {
++ stv0900_write_bits(i_params, F0900_P1_SPECINV_CONTROL, STV0900_IQ_FORCE_NORMAL);
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1c);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT1, freq1);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT0, freq0);
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x18);
++ if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) {
++ i_params->dmd1_rslts.locked = TRUE;
++ signal_type = stv0900_get_signal_params(fe);
++ stv0900_track_optimization(fe);
++ }
++
++ }
++
++ } else
++ i_params->dmd1_rslts.locked = FALSE;
++
++ break;
++ case STV0900_DEMOD_2:
++ i_params->dmd2_rslts.locked = FALSE;
++ if (stv0900_get_bits(i_params, F0900_P2_HEADER_MODE) == STV0900_DVBS_FOUND) {
++ srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
++ srate += stv0900_get_timing_offst(i_params, srate, demod);
++
++ if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH)
++ stv0900_set_symbol_rate(i_params, i_params->mclk, srate, demod);
++
++ stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, srate, STV0900_WARM_START);
++ freq1 = stv0900_read_reg(i_params, R0900_P2_CFR2);
++ freq0 = stv0900_read_reg(i_params, R0900_P2_CFR1);
++ stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0);
++ stv0900_write_bits(i_params, F0900_P2_SPECINV_CONTROL, STV0900_IQ_FORCE_SWAPPED);
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1C);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0);
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
++
++ if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) {
++ i_params->dmd2_rslts.locked = TRUE;
++ signal_type = stv0900_get_signal_params(fe);
++ stv0900_track_optimization(fe);
++ } else {
++ stv0900_write_bits(i_params, F0900_P2_SPECINV_CONTROL, STV0900_IQ_FORCE_NORMAL);
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1c);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT1, freq1);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT0, freq0);
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x18);
++
++ if (stv0900_wait_for_lock(i_params, demod, demod_timeout, fec_timeout) == TRUE) {
++ i_params->dmd2_rslts.locked = TRUE;
++ signal_type = stv0900_get_signal_params(fe);
++ stv0900_track_optimization(fe);
++ }
++
++ }
++
++ } else
++ i_params->dmd1_rslts.locked = FALSE;
++
++ break;
++ }
++
++ return signal_type;
++}
++
++static u16 stv0900_blind_check_agc2_min_level(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod)
++{
++ u32 minagc2level = 0xffff,
++ agc2level,
++ init_freq, freq_step;
++
++ s32 i, j, nb_steps, direction;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38);
++ stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 1);
++
++ stv0900_write_reg(i_params, R0900_P1_SFRUP1, 0x83);
++ stv0900_write_reg(i_params, R0900_P1_SFRUP0, 0xc0);
++
++ stv0900_write_reg(i_params, R0900_P1_SFRLOW1, 0x82);
++ stv0900_write_reg(i_params, R0900_P1_SFRLOW0, 0xa0);
++ stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x0);
++
++ stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod);
++ nb_steps = -1 + (i_params->dmd1_srch_range / 1000000);
++ nb_steps /= 2;
++ nb_steps = (2 * nb_steps) + 1;
++
++ if (nb_steps < 0)
++ nb_steps = 1;
++
++ direction = 1;
++
++ freq_step = (1000000 << 8) / (i_params->mclk >> 8);
++
++ init_freq = 0;
++
++ for (i = 0; i < nb_steps; i++) {
++ if (direction > 0)
++ init_freq = init_freq + (freq_step * i);
++ else
++ init_freq = init_freq - (freq_step * i);
++
++ direction *= -1;
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5C);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT1, (init_freq >> 8) & 0xff);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT0, init_freq & 0xff);
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x58);
++ msleep(10);
++ agc2level = 0;
++
++ for (j = 0; j < 10; j++)
++ agc2level += (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8)
++ | stv0900_read_reg(i_params, R0900_P1_AGC2I0);
++
++ agc2level /= 10;
++
++ if (agc2level < minagc2level)
++ minagc2level = agc2level;
++ }
++ break;
++ case STV0900_DEMOD_2:
++ stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38);
++ stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 1);
++ stv0900_write_reg(i_params, R0900_P2_SFRUP1, 0x83);
++ stv0900_write_reg(i_params, R0900_P2_SFRUP0, 0xc0);
++ stv0900_write_reg(i_params, R0900_P2_SFRLOW1, 0x82);
++ stv0900_write_reg(i_params, R0900_P2_SFRLOW0, 0xa0);
++ stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x0);
++ stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod);
++ nb_steps = -1 + (i_params->dmd2_srch_range / 1000000);
++ nb_steps /= 2;
++ nb_steps = (2 * nb_steps) + 1;
++
++ if (nb_steps < 0)
++ nb_steps = 1;
++
++ direction = 1;
++ freq_step = (1000000 << 8) / (i_params->mclk >> 8);
++ init_freq = 0;
++ for (i = 0; i < nb_steps; i++) {
++ if (direction > 0)
++ init_freq = init_freq + (freq_step * i);
++ else
++ init_freq = init_freq - (freq_step * i);
++
++ direction *= -1;
++
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5C);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT1, (init_freq >> 8) & 0xff);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT0, init_freq & 0xff);
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x58);
++
++ msleep(10);
++ agc2level = 0;
++ for (j = 0; j < 10; j++)
++ agc2level += (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8)
++ | stv0900_read_reg(i_params, R0900_P2_AGC2I0);
++
++ agc2level /= 10;
++
++ if (agc2level < minagc2level)
++ minagc2level = agc2level;
++ }
++ break;
++ }
++
++ return (u16)minagc2level;
++}
++
++static u32 stv0900_search_srate_coarse(struct dvb_frontend *fe)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *i_params = state->internal;
++ enum fe_stv0900_demod_num demod = state->demod;
++ int timingLock = FALSE;
++ s32 i, timingcpt = 0,
++ direction = 1,
++ nb_steps,
++ current_step = 0,
++ tuner_freq;
++
++ u32 coarse_srate = 0, agc2_integr = 0, currier_step = 1200;
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ stv0900_write_bits(i_params, F0900_P1_I2C_DEMOD_MODE, 0x1F);
++ stv0900_write_reg(i_params, R0900_P1_TMGCFG, 0x12);
++ stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0xf0);
++ stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0xe0);
++ stv0900_write_bits(i_params, F0900_P1_SCAN_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 1);
++ stv0900_write_reg(i_params, R0900_P1_SFRUP1, 0x83);
++ stv0900_write_reg(i_params, R0900_P1_SFRUP0, 0xc0);
++ stv0900_write_reg(i_params, R0900_P1_SFRLOW1, 0x82);
++ stv0900_write_reg(i_params, R0900_P1_SFRLOW0, 0xa0);
++ stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x0);
++ stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x50);
++
++ if (i_params->chip_id >= 0x20) {
++ stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x6a);
++ stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x95);
++ } else {
++ stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xed);
++ stv0900_write_reg(i_params, R0900_P1_SFRSTEP, 0x73);
++ }
++
++ if (i_params->dmd1_symbol_rate <= 2000000)
++ currier_step = 1000;
++ else if (i_params->dmd1_symbol_rate <= 5000000)
++ currier_step = 2000;
++ else if (i_params->dmd1_symbol_rate <= 12000000)
++ currier_step = 3000;
++ else
++ currier_step = 5000;
++
++ nb_steps = -1 + ((i_params->dmd1_srch_range / 1000) / currier_step);
++ nb_steps /= 2;
++ nb_steps = (2 * nb_steps) + 1;
++
++ if (nb_steps < 0)
++ nb_steps = 1;
++
++ else if (nb_steps > 10) {
++ nb_steps = 11;
++ currier_step = (i_params->dmd1_srch_range / 1000) / 10;
++ }
++
++ current_step = 0;
++
++ direction = 1;
++ tuner_freq = i_params->tuner1_freq;
++
++ while ((timingLock == FALSE) && (current_step < nb_steps)) {
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5F);
++ stv0900_write_bits(i_params, F0900_P1_I2C_DEMOD_MODE, 0x0);
++
++ msleep(50);
++
++ for (i = 0; i < 10; i++) {
++ if (stv0900_get_bits(i_params, F0900_P1_TMGLOCK_QUALITY) >= 2)
++ timingcpt++;
++
++ agc2_integr += (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8) | stv0900_read_reg(i_params, R0900_P1_AGC2I0);
++
++ }
++
++ agc2_integr /= 10;
++ coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
++ current_step++;
++ direction *= -1;
++
++ dprintk("lock: I2C_DEMOD_MODE_FIELD =0. Search started. tuner freq=%d agc2=0x%x srate_coarse=%d tmg_cpt=%d\n", tuner_freq, agc2_integr, coarse_srate, timingcpt);
++
++ if ((timingcpt >= 5) && (agc2_integr < 0x1F00) && (coarse_srate < 55000000) && (coarse_srate > 850000)) {
++ timingLock = TRUE;
++ }
++
++ else if (current_step < nb_steps) {
++ if (direction > 0)
++ tuner_freq += (current_step * currier_step);
++ else
++ tuner_freq -= (current_step * currier_step);
++
++ stv0900_set_tuner(fe, tuner_freq, i_params->tuner1_bw);
++ }
++ }
++
++ if (timingLock == FALSE)
++ coarse_srate = 0;
++ else
++ coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
++ break;
++ case STV0900_DEMOD_2:
++ stv0900_write_bits(i_params, F0900_P2_I2C_DEMOD_MODE, 0x1F);
++ stv0900_write_reg(i_params, R0900_P2_TMGCFG, 0x12);
++ stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0xf0);
++ stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0xe0);
++ stv0900_write_bits(i_params, F0900_P2_SCAN_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 1);
++ stv0900_write_reg(i_params, R0900_P2_SFRUP1, 0x83);
++ stv0900_write_reg(i_params, R0900_P2_SFRUP0, 0xc0);
++ stv0900_write_reg(i_params, R0900_P2_SFRLOW1, 0x82);
++ stv0900_write_reg(i_params, R0900_P2_SFRLOW0, 0xa0);
++ stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x0);
++ stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x50);
++
++ if (i_params->chip_id >= 0x20) {
++ stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x6a);
++ stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x95);
++ } else {
++ stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xed);
++ stv0900_write_reg(i_params, R0900_P2_SFRSTEP, 0x73);
++ }
++
++ if (i_params->dmd2_symbol_rate <= 2000000)
++ currier_step = 1000;
++ else if (i_params->dmd2_symbol_rate <= 5000000)
++ currier_step = 2000;
++ else if (i_params->dmd2_symbol_rate <= 12000000)
++ currier_step = 3000;
++ else
++ currier_step = 5000;
++
++
++ nb_steps = -1 + ((i_params->dmd2_srch_range / 1000) / currier_step);
++ nb_steps /= 2;
++ nb_steps = (2 * nb_steps) + 1;
++
++ if (nb_steps < 0)
++ nb_steps = 1;
++ else if (nb_steps > 10) {
++ nb_steps = 11;
++ currier_step = (i_params->dmd2_srch_range / 1000) / 10;
++ }
++
++ current_step = 0;
++ direction = 1;
++ tuner_freq = i_params->tuner2_freq;
++
++ while ((timingLock == FALSE) && (current_step < nb_steps)) {
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5F);
++ stv0900_write_bits(i_params, F0900_P2_I2C_DEMOD_MODE, 0x0);
++
++ msleep(50);
++ timingcpt = 0;
++
++ for (i = 0; i < 20; i++) {
++ if (stv0900_get_bits(i_params, F0900_P2_TMGLOCK_QUALITY) >= 2)
++ timingcpt++;
++ agc2_integr += (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8)
++ | stv0900_read_reg(i_params, R0900_P2_AGC2I0);
++ }
++
++ agc2_integr /= 20;
++ coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
++ if ((timingcpt >= 10) && (agc2_integr < 0x1F00) && (coarse_srate < 55000000) && (coarse_srate > 850000))
++ timingLock = TRUE;
++ else {
++ current_step++;
++ direction *= -1;
++
++ if (direction > 0)
++ tuner_freq += (current_step * currier_step);
++ else
++ tuner_freq -= (current_step * currier_step);
++
++ stv0900_set_tuner(fe, tuner_freq, i_params->tuner2_bw);
++ }
++ }
++
++ if (timingLock == FALSE)
++ coarse_srate = 0;
++ else
++ coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
++ break;
++ }
++
++ return coarse_srate;
++}
++
++static u32 stv0900_search_srate_fine(struct dvb_frontend *fe)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *i_params = state->internal;
++ enum fe_stv0900_demod_num demod = state->demod;
++ u32 coarse_srate,
++ coarse_freq,
++ symb;
++
++ coarse_srate = stv0900_get_symbol_rate(i_params, i_params->mclk, demod);
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ coarse_freq = (stv0900_read_reg(i_params, R0900_P1_CFR2) << 8)
++ | stv0900_read_reg(i_params, R0900_P1_CFR1);
++ symb = 13 * (coarse_srate / 10);
++
++ if (symb < i_params->dmd1_symbol_rate)
++ coarse_srate = 0;
++ else {
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x1F);
++ stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x01);
++ stv0900_write_reg(i_params, R0900_P1_TMGTHRISE, 0x20);
++ stv0900_write_reg(i_params, R0900_P1_TMGTHFALL, 0x00);
++ stv0900_write_reg(i_params, R0900_P1_TMGCFG, 0xd2);
++ stv0900_write_bits(i_params, F0900_P1_CFR_AUTOSCAN, 0);
++
++ if (i_params->chip_id >= 0x20)
++ stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0x49);
++ else
++ stv0900_write_reg(i_params, R0900_P1_CARFREQ, 0xed);
++
++ if (coarse_srate > 3000000) {
++ symb = 13 * (coarse_srate / 10);
++ symb = (symb / 1000) * 65536;
++ symb /= (i_params->mclk / 1000);
++ stv0900_write_reg(i_params, R0900_P1_SFRUP1, (symb >> 8) & 0x7F);
++ stv0900_write_reg(i_params, R0900_P1_SFRUP0, (symb & 0xFF));
++
++ symb = 10 * (coarse_srate / 13);
++ symb = (symb / 1000) * 65536;
++ symb /= (i_params->mclk / 1000);
++
++ stv0900_write_reg(i_params, R0900_P1_SFRLOW1, (symb >> 8) & 0x7F);
++ stv0900_write_reg(i_params, R0900_P1_SFRLOW0, (symb & 0xFF));
++
++ symb = (coarse_srate / 1000) * 65536;
++ symb /= (i_params->mclk / 1000);
++ stv0900_write_reg(i_params, R0900_P1_SFRINIT1, (symb >> 8) & 0xFF);
++ stv0900_write_reg(i_params, R0900_P1_SFRINIT0, (symb & 0xFF));
++ } else {
++ symb = 13 * (coarse_srate / 10);
++ symb = (symb / 100) * 65536;
++ symb /= (i_params->mclk / 100);
++ stv0900_write_reg(i_params, R0900_P1_SFRUP1, (symb >> 8) & 0x7F);
++ stv0900_write_reg(i_params, R0900_P1_SFRUP0, (symb & 0xFF));
++
++ symb = 10 * (coarse_srate / 14);
++ symb = (symb / 100) * 65536;
++ symb /= (i_params->mclk / 100);
++ stv0900_write_reg(i_params, R0900_P1_SFRLOW1, (symb >> 8) & 0x7F);
++ stv0900_write_reg(i_params, R0900_P1_SFRLOW0, (symb & 0xFF));
++
++ symb = (coarse_srate / 100) * 65536;
++ symb /= (i_params->mclk / 100);
++ stv0900_write_reg(i_params, R0900_P1_SFRINIT1, (symb >> 8) & 0xFF);
++ stv0900_write_reg(i_params, R0900_P1_SFRINIT0, (symb & 0xFF));
++ }
++
++ stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x20);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT1, (coarse_freq >> 8) & 0xff);
++ stv0900_write_reg(i_params, R0900_P1_CFRINIT0, coarse_freq & 0xff);
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x15);
++ }
++ break;
++ case STV0900_DEMOD_2:
++ coarse_freq = (stv0900_read_reg(i_params, R0900_P2_CFR2) << 8)
++ | stv0900_read_reg(i_params, R0900_P2_CFR1);
++
++ symb = 13 * (coarse_srate / 10);
++
++ if (symb < i_params->dmd2_symbol_rate)
++ coarse_srate = 0;
++ else {
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x1F);
++ stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x01);
++ stv0900_write_reg(i_params, R0900_P2_TMGTHRISE, 0x20);
++ stv0900_write_reg(i_params, R0900_P2_TMGTHFALL, 0x00);
++ stv0900_write_reg(i_params, R0900_P2_TMGCFG, 0xd2);
++ stv0900_write_bits(i_params, F0900_P2_CFR_AUTOSCAN, 0);
++
++ if (i_params->chip_id >= 0x20)
++ stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0x49);
++ else
++ stv0900_write_reg(i_params, R0900_P2_CARFREQ, 0xed);
++
++ if (coarse_srate > 3000000) {
++ symb = 13 * (coarse_srate / 10);
++ symb = (symb / 1000) * 65536;
++ symb /= (i_params->mclk / 1000);
++ stv0900_write_reg(i_params, R0900_P2_SFRUP1, (symb >> 8) & 0x7F);
++ stv0900_write_reg(i_params, R0900_P2_SFRUP0, (symb & 0xFF));
++
++ symb = 10 * (coarse_srate / 13);
++ symb = (symb / 1000) * 65536;
++ symb /= (i_params->mclk / 1000);
++
++ stv0900_write_reg(i_params, R0900_P2_SFRLOW1, (symb >> 8) & 0x7F);
++ stv0900_write_reg(i_params, R0900_P2_SFRLOW0, (symb & 0xFF));
++
++ symb = (coarse_srate / 1000) * 65536;
++ symb /= (i_params->mclk / 1000);
++ stv0900_write_reg(i_params, R0900_P2_SFRINIT1, (symb >> 8) & 0xFF);
++ stv0900_write_reg(i_params, R0900_P2_SFRINIT0, (symb & 0xFF));
++ } else {
++ symb = 13 * (coarse_srate / 10);
++ symb = (symb / 100) * 65536;
++ symb /= (i_params->mclk / 100);
++ stv0900_write_reg(i_params, R0900_P2_SFRUP1, (symb >> 8) & 0x7F);
++ stv0900_write_reg(i_params, R0900_P2_SFRUP0, (symb & 0xFF));
++
++ symb = 10 * (coarse_srate / 14);
++ symb = (symb / 100) * 65536;
++ symb /= (i_params->mclk / 100);
++ stv0900_write_reg(i_params, R0900_P2_SFRLOW1, (symb >> 8) & 0x7F);
++ stv0900_write_reg(i_params, R0900_P2_SFRLOW0, (symb & 0xFF));
++
++ symb = (coarse_srate / 100) * 65536;
++ symb /= (i_params->mclk / 100);
++ stv0900_write_reg(i_params, R0900_P2_SFRINIT1, (symb >> 8) & 0xFF);
++ stv0900_write_reg(i_params, R0900_P2_SFRINIT0, (symb & 0xFF));
++ }
++
++ stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x20);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT1, (coarse_freq >> 8) & 0xff);
++ stv0900_write_reg(i_params, R0900_P2_CFRINIT0, coarse_freq & 0xff);
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x15);
++ }
++
++ break;
++ }
++
++ return coarse_srate;
++}
++
++static int stv0900_blind_search_algo(struct dvb_frontend *fe)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *i_params = state->internal;
++ enum fe_stv0900_demod_num demod = state->demod;
++ u8 k_ref_tmg, k_ref_tmg_max, k_ref_tmg_min;
++ u32 coarse_srate;
++ int lock = FALSE, coarse_fail = FALSE;
++ s32 demod_timeout = 500, fec_timeout = 50, kref_tmg_reg, fail_cpt, i, agc2_overflow;
++ u16 agc2_integr;
++ u8 dstatus2;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ if (i_params->chip_id < 0x20) {
++ k_ref_tmg_max = 233;
++ k_ref_tmg_min = 143;
++ } else {
++ k_ref_tmg_max = 120;
++ k_ref_tmg_min = 30;
++ }
++
++ agc2_integr = stv0900_blind_check_agc2_min_level(i_params, demod);
++
++ if (agc2_integr > STV0900_BLIND_SEARCH_AGC2_TH) {
++ lock = FALSE;
++
++ } else {
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ if (i_params->chip_id == 0x10)
++ stv0900_write_reg(i_params, R0900_P1_CORRELEXP, 0xAA);
++
++ if (i_params->chip_id < 0x20)
++ stv0900_write_reg(i_params, R0900_P1_CARHDR, 0x55);
++
++ stv0900_write_reg(i_params, R0900_P1_CARCFG, 0xC4);
++ stv0900_write_reg(i_params, R0900_P1_RTCS2, 0x44);
++
++ if (i_params->chip_id >= 0x20) {
++ stv0900_write_reg(i_params, R0900_P1_EQUALCFG, 0x41);
++ stv0900_write_reg(i_params, R0900_P1_FFECFG, 0x41);
++ stv0900_write_reg(i_params, R0900_P1_VITSCALE, 0x82);
++ stv0900_write_reg(i_params, R0900_P1_VAVSRVIT, 0x0);
++ }
++
++ kref_tmg_reg = R0900_P1_KREFTMG;
++ break;
++ case STV0900_DEMOD_2:
++ if (i_params->chip_id == 0x10)
++ stv0900_write_reg(i_params, R0900_P2_CORRELEXP, 0xAA);
++
++ if (i_params->chip_id < 0x20)
++ stv0900_write_reg(i_params, R0900_P2_CARHDR, 0x55);
++
++ stv0900_write_reg(i_params, R0900_P2_CARCFG, 0xC4);
++ stv0900_write_reg(i_params, R0900_P2_RTCS2, 0x44);
++
++ if (i_params->chip_id >= 0x20) {
++ stv0900_write_reg(i_params, R0900_P2_EQUALCFG, 0x41);
++ stv0900_write_reg(i_params, R0900_P2_FFECFG, 0x41);
++ stv0900_write_reg(i_params, R0900_P2_VITSCALE, 0x82);
++ stv0900_write_reg(i_params, R0900_P2_VAVSRVIT, 0x0);
++ }
++
++ kref_tmg_reg = R0900_P2_KREFTMG;
++ break;
++ }
++
++ k_ref_tmg = k_ref_tmg_max;
++
++ do {
++ stv0900_write_reg(i_params, kref_tmg_reg, k_ref_tmg);
++ if (stv0900_search_srate_coarse(fe) != 0) {
++ coarse_srate = stv0900_search_srate_fine(fe);
++
++ if (coarse_srate != 0) {
++ stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, coarse_srate, STV0900_BLIND_SEARCH);
++ lock = stv0900_get_demod_lock(i_params, demod, demod_timeout);
++ } else
++ lock = FALSE;
++ } else {
++ fail_cpt = 0;
++ agc2_overflow = 0;
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ for (i = 0; i < 10; i++) {
++ agc2_integr = (stv0900_read_reg(i_params, R0900_P1_AGC2I1) << 8)
++ | stv0900_read_reg(i_params, R0900_P1_AGC2I0);
++
++ if (agc2_integr >= 0xff00)
++ agc2_overflow++;
++
++ dstatus2 = stv0900_read_reg(i_params, R0900_P1_DSTATUS2);
++
++ if (((dstatus2 & 0x1) == 0x1) && ((dstatus2 >> 7) == 1))
++ fail_cpt++;
++ }
++ break;
++ case STV0900_DEMOD_2:
++ for (i = 0; i < 10; i++) {
++ agc2_integr = (stv0900_read_reg(i_params, R0900_P2_AGC2I1) << 8)
++ | stv0900_read_reg(i_params, R0900_P2_AGC2I0);
++
++ if (agc2_integr >= 0xff00)
++ agc2_overflow++;
++
++ dstatus2 = stv0900_read_reg(i_params, R0900_P2_DSTATUS2);
++
++ if (((dstatus2 & 0x1) == 0x1) && ((dstatus2 >> 7) == 1))
++ fail_cpt++;
++ }
++ break;
++ }
++
++ if ((fail_cpt > 7) || (agc2_overflow > 7))
++ coarse_fail = TRUE;
++
++ lock = FALSE;
++ }
++ k_ref_tmg -= 30;
++ } while ((k_ref_tmg >= k_ref_tmg_min) && (lock == FALSE) && (coarse_fail == FALSE));
++ }
++
++ return lock;
++}
++
++static void stv0900_set_viterbi_acq(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod)
++{
++ s32 vth_reg;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ dmd_reg(vth_reg, R0900_P1_VTH12, R0900_P2_VTH12);
++
++ stv0900_write_reg(i_params, vth_reg++, 0x96);
++ stv0900_write_reg(i_params, vth_reg++, 0x64);
++ stv0900_write_reg(i_params, vth_reg++, 0x36);
++ stv0900_write_reg(i_params, vth_reg++, 0x23);
++ stv0900_write_reg(i_params, vth_reg++, 0x1E);
++ stv0900_write_reg(i_params, vth_reg++, 0x19);
++}
++
++static void stv0900_set_search_standard(struct stv0900_internal *i_params,
++ enum fe_stv0900_demod_num demod)
++{
++
++ int sstndrd;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ sstndrd = i_params->dmd1_srch_standard;
++ if (demod == 1)
++ sstndrd = i_params->dmd2_srch_stndrd;
++
++ switch (sstndrd) {
++ case STV0900_SEARCH_DVBS1:
++ dprintk("Search Standard = DVBS1\n");
++ break;
++ case STV0900_SEARCH_DSS:
++ dprintk("Search Standard = DSS\n");
++ case STV0900_SEARCH_DVBS2:
++ break;
++ dprintk("Search Standard = DVBS2\n");
++ case STV0900_AUTO_SEARCH:
++ default:
++ dprintk("Search Standard = AUTO\n");
++ break;
++ }
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ switch (i_params->dmd1_srch_standard) {
++ case STV0900_SEARCH_DVBS1:
++ case STV0900_SEARCH_DSS:
++ stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0);
++
++ stv0900_write_bits(i_params, F0900_STOP_CLKVIT1, 0);
++ stv0900_write_reg(i_params, R0900_P1_ACLC, 0x1a);
++ stv0900_write_reg(i_params, R0900_P1_BCLC, 0x09);
++ stv0900_write_reg(i_params, R0900_P1_CAR2CFG, 0x22);
++
++ stv0900_set_viterbi_acq(i_params, demod);
++ stv0900_set_viterbi_standard(i_params,
++ i_params->dmd1_srch_standard,
++ i_params->dmd1_fec, demod);
++
++ break;
++ case STV0900_SEARCH_DVBS2:
++ stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_STOP_CLKVIT1, 1);
++ stv0900_write_reg(i_params, R0900_P1_ACLC, 0x1a);
++ stv0900_write_reg(i_params, R0900_P1_BCLC, 0x09);
++ stv0900_write_reg(i_params, R0900_P1_CAR2CFG, 0x26);
++ if (i_params->demod_mode != STV0900_SINGLE) {
++ if (i_params->chip_id <= 0x11)
++ stv0900_stop_all_s2_modcod(i_params, demod);
++ else
++ stv0900_activate_s2_modcode(i_params, demod);
++
++ } else
++ stv0900_activate_s2_modcode_single(i_params, demod);
++
++ stv0900_set_viterbi_tracq(i_params, demod);
++
++ break;
++ case STV0900_AUTO_SEARCH:
++ default:
++ stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_P1_DVBS1_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_P1_DVBS2_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_STOP_CLKVIT1, 0);
++ stv0900_write_reg(i_params, R0900_P1_ACLC, 0x1a);
++ stv0900_write_reg(i_params, R0900_P1_BCLC, 0x09);
++ stv0900_write_reg(i_params, R0900_P1_CAR2CFG, 0x26);
++ if (i_params->demod_mode != STV0900_SINGLE) {
++ if (i_params->chip_id <= 0x11)
++ stv0900_stop_all_s2_modcod(i_params, demod);
++ else
++ stv0900_activate_s2_modcode(i_params, demod);
++
++ } else
++ stv0900_activate_s2_modcode_single(i_params, demod);
++
++ if (i_params->dmd1_symbol_rate >= 2000000)
++ stv0900_set_viterbi_acq(i_params, demod);
++ else
++ stv0900_set_viterbi_tracq(i_params, demod);
++
++ stv0900_set_viterbi_standard(i_params, i_params->dmd1_srch_standard, i_params->dmd1_fec, demod);
++
++ break;
++ }
++ break;
++ case STV0900_DEMOD_2:
++ switch (i_params->dmd2_srch_stndrd) {
++ case STV0900_SEARCH_DVBS1:
++ case STV0900_SEARCH_DSS:
++ stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_STOP_CLKVIT2, 0);
++ stv0900_write_reg(i_params, R0900_P2_ACLC, 0x1a);
++ stv0900_write_reg(i_params, R0900_P2_BCLC, 0x09);
++ stv0900_write_reg(i_params, R0900_P2_CAR2CFG, 0x22);
++ stv0900_set_viterbi_acq(i_params, demod);
++ stv0900_set_viterbi_standard(i_params, i_params->dmd2_srch_stndrd, i_params->dmd2_fec, demod);
++ break;
++ case STV0900_SEARCH_DVBS2:
++ stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_STOP_CLKVIT2, 1);
++ stv0900_write_reg(i_params, R0900_P2_ACLC, 0x1a);
++ stv0900_write_reg(i_params, R0900_P2_BCLC, 0x09);
++ stv0900_write_reg(i_params, R0900_P2_CAR2CFG, 0x26);
++ if (i_params->demod_mode != STV0900_SINGLE)
++ stv0900_activate_s2_modcode(i_params, demod);
++ else
++ stv0900_activate_s2_modcode_single(i_params, demod);
++
++ stv0900_set_viterbi_tracq(i_params, demod);
++ break;
++ case STV0900_AUTO_SEARCH:
++ default:
++ stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 0);
++ stv0900_write_bits(i_params, F0900_P2_DVBS1_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_P2_DVBS2_ENABLE, 1);
++ stv0900_write_bits(i_params, F0900_STOP_CLKVIT2, 0);
++ stv0900_write_reg(i_params, R0900_P2_ACLC, 0x1a);
++ stv0900_write_reg(i_params, R0900_P2_BCLC, 0x09);
++ stv0900_write_reg(i_params, R0900_P2_CAR2CFG, 0x26);
++ if (i_params->demod_mode != STV0900_SINGLE)
++ stv0900_activate_s2_modcode(i_params, demod);
++ else
++ stv0900_activate_s2_modcode_single(i_params, demod);
++
++ if (i_params->dmd2_symbol_rate >= 2000000)
++ stv0900_set_viterbi_acq(i_params, demod);
++ else
++ stv0900_set_viterbi_tracq(i_params, demod);
++
++ stv0900_set_viterbi_standard(i_params, i_params->dmd2_srch_stndrd, i_params->dmd2_fec, demod);
++
++ break;
++ }
++
++ break;
++ }
++}
++
++enum fe_stv0900_signal_type stv0900_algo(struct dvb_frontend *fe)
++{
++ struct stv0900_state *state = fe->demodulator_priv;
++ struct stv0900_internal *i_params = state->internal;
++ enum fe_stv0900_demod_num demod = state->demod;
++
++ s32 demod_timeout = 500, fec_timeout = 50, stream_merger_field;
++
++ int lock = FALSE, low_sr = FALSE;
++
++ enum fe_stv0900_signal_type signal_type = STV0900_NOCARRIER;
++ enum fe_stv0900_search_algo algo;
++ int no_signal = FALSE;
++
++ dprintk(KERN_INFO "%s\n", __func__);
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ algo = i_params->dmd1_srch_algo;
++
++ stv0900_write_bits(i_params, F0900_P1_RST_HWARE, 1);
++ stream_merger_field = F0900_P1_RST_HWARE;
++
++ stv0900_write_reg(i_params, R0900_P1_DMDISTATE, 0x5C);
++
++ if (i_params->chip_id >= 0x20)
++ stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x9e);
++ else
++ stv0900_write_reg(i_params, R0900_P1_CORRELABS, 0x88);
++
++ stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, i_params->dmd1_symbol_rate, i_params->dmd1_srch_algo);
++
++ if (i_params->dmd1_srch_algo == STV0900_BLIND_SEARCH) {
++ i_params->tuner1_bw = 2 * 36000000;
++
++ stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x00);
++ stv0900_write_reg(i_params, R0900_P1_CORRELMANT, 0x70);
++
++ stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod);
++ } else {
++ stv0900_write_reg(i_params, R0900_P1_DMDT0M, 0x20);
++ stv0900_write_reg(i_params, R0900_P1_TMGCFG, 0xd2);
++
++ if (i_params->dmd1_symbol_rate < 2000000)
++ stv0900_write_reg(i_params, R0900_P1_CORRELMANT, 0x63);
++ else
++ stv0900_write_reg(i_params, R0900_P1_CORRELMANT, 0x70);
++
++ stv0900_write_reg(i_params, R0900_P1_AGC2REF, 0x38);
++ if (i_params->chip_id >= 0x20) {
++ stv0900_write_reg(i_params, R0900_P1_KREFTMG, 0x5a);
++
++ if (i_params->dmd1_srch_algo == STV0900_COLD_START)
++ i_params->tuner1_bw = (15 * (stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + 10000000)) / 10;
++ else if (i_params->dmd1_srch_algo == STV0900_WARM_START)
++ i_params->tuner1_bw = stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + 10000000;
++ } else {
++ stv0900_write_reg(i_params, R0900_P1_KREFTMG, 0xc1);
++ i_params->tuner1_bw = (15 * (stv0900_carrier_width(i_params->dmd1_symbol_rate, i_params->rolloff) + 10000000)) / 10;
++ }
++
++ stv0900_write_reg(i_params, R0900_P1_TMGCFG2, 0x01);
++
++ stv0900_set_symbol_rate(i_params, i_params->mclk, i_params->dmd1_symbol_rate, demod);
++ stv0900_set_max_symbol_rate(i_params, i_params->mclk, i_params->dmd1_symbol_rate, demod);
++ stv0900_set_min_symbol_rate(i_params, i_params->mclk, i_params->dmd1_symbol_rate, demod);
++ if (i_params->dmd1_symbol_rate >= 10000000)
++ low_sr = FALSE;
++ else
++ low_sr = TRUE;
++
++ }
++
++ stv0900_set_tuner(fe, i_params->tuner1_freq, i_params->tuner1_bw);
++
++ stv0900_write_bits(i_params, F0900_P1_SPECINV_CONTROL, i_params->dmd1_srch_iq_inv);
++ stv0900_write_bits(i_params, F0900_P1_MANUAL_ROLLOFF, 1);
++
++ stv0900_set_search_standard(i_params, demod);
++
++ if (i_params->dmd1_srch_algo != STV0900_BLIND_SEARCH)
++ stv0900_start_search(i_params, demod);
++ break;
++ case STV0900_DEMOD_2:
++ algo = i_params->dmd2_srch_algo;
++
++ stv0900_write_bits(i_params, F0900_P2_RST_HWARE, 1);
++
++ stream_merger_field = F0900_P2_RST_HWARE;
++
++ stv0900_write_reg(i_params, R0900_P2_DMDISTATE, 0x5C);
++
++ if (i_params->chip_id >= 0x20)
++ stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x9e);
++ else
++ stv0900_write_reg(i_params, R0900_P2_CORRELABS, 0x88);
++
++ stv0900_get_lock_timeout(&demod_timeout, &fec_timeout, i_params->dmd2_symbol_rate, i_params->dmd2_srch_algo);
++
++ if (i_params->dmd2_srch_algo == STV0900_BLIND_SEARCH) {
++ i_params->tuner2_bw = 2 * 36000000;
++
++ stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x00);
++ stv0900_write_reg(i_params, R0900_P2_CORRELMANT, 0x70);
++
++ stv0900_set_symbol_rate(i_params, i_params->mclk, 1000000, demod);
++ } else {
++ stv0900_write_reg(i_params, R0900_P2_DMDT0M, 0x20);
++ stv0900_write_reg(i_params, R0900_P2_TMGCFG, 0xd2);
++
++ if (i_params->dmd2_symbol_rate < 2000000)
++ stv0900_write_reg(i_params, R0900_P2_CORRELMANT, 0x63);
++ else
++ stv0900_write_reg(i_params, R0900_P2_CORRELMANT, 0x70);
++
++ if (i_params->dmd2_symbol_rate >= 10000000)
++ stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x38);
++ else
++ stv0900_write_reg(i_params, R0900_P2_AGC2REF, 0x60);
++
++ if (i_params->chip_id >= 0x20) {
++ stv0900_write_reg(i_params, R0900_P2_KREFTMG, 0x5a);
++
++ if (i_params->dmd2_srch_algo == STV0900_COLD_START)
++ i_params->tuner2_bw = (15 * (stv0900_carrier_width(i_params->dmd2_symbol_rate,
++ i_params->rolloff) + 10000000)) / 10;
++ else if (i_params->dmd2_srch_algo == STV0900_WARM_START)
++ i_params->tuner2_bw = stv0900_carrier_width(i_params->dmd2_symbol_rate,
++ i_params->rolloff) + 10000000;
++ } else {
++ stv0900_write_reg(i_params, R0900_P2_KREFTMG, 0xc1);
++ i_params->tuner2_bw = (15 * (stv0900_carrier_width(i_params->dmd2_symbol_rate,
++ i_params->rolloff) + 10000000)) / 10;
++ }
++
++ stv0900_write_reg(i_params, R0900_P2_TMGCFG2, 0x01);
++
++ stv0900_set_symbol_rate(i_params, i_params->mclk, i_params->dmd2_symbol_rate, demod);
++ stv0900_set_max_symbol_rate(i_params, i_params->mclk, i_params->dmd2_symbol_rate, demod);
++ stv0900_set_min_symbol_rate(i_params, i_params->mclk, i_params->dmd2_symbol_rate, demod);
++ if (i_params->dmd2_symbol_rate >= 10000000)
++ low_sr = FALSE;
++ else
++ low_sr = TRUE;
++
++ }
++
++ stv0900_set_tuner(fe, i_params->tuner2_freq, i_params->tuner2_bw);
++
++ stv0900_write_bits(i_params, F0900_P2_SPECINV_CONTROL, i_params->dmd2_srch_iq_inv);
++ stv0900_write_bits(i_params, F0900_P2_MANUAL_ROLLOFF, 1);
++
++ stv0900_set_search_standard(i_params, demod);
++
++ if (i_params->dmd2_srch_algo != STV0900_BLIND_SEARCH)
++ stv0900_start_search(i_params, demod);
++ break;
++ }
++
++ if (i_params->chip_id == 0x12) {
++ stv0900_write_bits(i_params, stream_merger_field, 0);
++ msleep(3);
++ stv0900_write_bits(i_params, stream_merger_field, 1);
++ stv0900_write_bits(i_params, stream_merger_field, 0);
++ }
++
++ if (algo == STV0900_BLIND_SEARCH)
++ lock = stv0900_blind_search_algo(fe);
++ else if (algo == STV0900_COLD_START)
++ lock = stv0900_get_demod_cold_lock(fe, demod_timeout);
++ else if (algo == STV0900_WARM_START)
++ lock = stv0900_get_demod_lock(i_params, demod, demod_timeout);
++
++ if ((lock == FALSE) && (algo == STV0900_COLD_START)) {
++ if (low_sr == FALSE) {
++ if (stv0900_check_timing_lock(i_params, demod) == TRUE)
++ lock = stv0900_sw_algo(i_params, demod);
++ }
++ }
++
++ if (lock == TRUE)
++ signal_type = stv0900_get_signal_params(fe);
++
++ if ((lock == TRUE) && (signal_type == STV0900_RANGEOK)) {
++ stv0900_track_optimization(fe);
++ if (i_params->chip_id <= 0x11) {
++ if ((stv0900_get_standard(fe, STV0900_DEMOD_1) == STV0900_DVBS1_STANDARD) && (stv0900_get_standard(fe, STV0900_DEMOD_2) == STV0900_DVBS1_STANDARD)) {
++ msleep(20);
++ stv0900_write_bits(i_params, stream_merger_field, 0);
++ } else {
++ stv0900_write_bits(i_params, stream_merger_field, 0);
++ msleep(3);
++ stv0900_write_bits(i_params, stream_merger_field, 1);
++ stv0900_write_bits(i_params, stream_merger_field, 0);
++ }
++ } else if (i_params->chip_id == 0x20) {
++ stv0900_write_bits(i_params, stream_merger_field, 0);
++ msleep(3);
++ stv0900_write_bits(i_params, stream_merger_field, 1);
++ stv0900_write_bits(i_params, stream_merger_field, 0);
++ }
++
++ if (stv0900_wait_for_lock(i_params, demod, fec_timeout, fec_timeout) == TRUE) {
++ lock = TRUE;
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ i_params->dmd1_rslts.locked = TRUE;
++ if (i_params->dmd1_rslts.standard == STV0900_DVBS2_STANDARD) {
++ stv0900_set_dvbs2_rolloff(i_params, demod);
++ stv0900_write_reg(i_params, R0900_P1_PDELCTRL2, 0x40);
++ stv0900_write_reg(i_params, R0900_P1_PDELCTRL2, 0);
++ stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x67);
++ } else {
++ stv0900_write_reg(i_params, R0900_P1_ERRCTRL1, 0x75);
++ }
++
++ stv0900_write_reg(i_params, R0900_P1_FBERCPT4, 0);
++ stv0900_write_reg(i_params, R0900_P1_ERRCTRL2, 0xc1);
++ break;
++ case STV0900_DEMOD_2:
++ i_params->dmd2_rslts.locked = TRUE;
++
++ if (i_params->dmd2_rslts.standard == STV0900_DVBS2_STANDARD) {
++ stv0900_set_dvbs2_rolloff(i_params, demod);
++ stv0900_write_reg(i_params, R0900_P2_PDELCTRL2, 0x60);
++ stv0900_write_reg(i_params, R0900_P2_PDELCTRL2, 0x20);
++ stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x67);
++ } else {
++ stv0900_write_reg(i_params, R0900_P2_ERRCTRL1, 0x75);
++ }
++
++ stv0900_write_reg(i_params, R0900_P2_FBERCPT4, 0);
++
++ stv0900_write_reg(i_params, R0900_P2_ERRCTRL2, 0xc1);
++ break;
++ }
++ } else {
++ lock = FALSE;
++ signal_type = STV0900_NODATA;
++ no_signal = stv0900_check_signal_presence(i_params, demod);
++
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ i_params->dmd1_rslts.locked = FALSE;
++ break;
++ case STV0900_DEMOD_2:
++ i_params->dmd2_rslts.locked = FALSE;
++ break;
++ }
++ }
++ }
++
++ if ((signal_type == STV0900_NODATA) && (no_signal == FALSE)) {
++ switch (demod) {
++ case STV0900_DEMOD_1:
++ default:
++ if (i_params->chip_id <= 0x11) {
++ if ((stv0900_get_bits(i_params, F0900_P1_HEADER_MODE) == STV0900_DVBS_FOUND) &&
++ (i_params->dmd1_srch_iq_inv <= STV0900_IQ_AUTO_NORMAL_FIRST))
++ signal_type = stv0900_dvbs1_acq_workaround(fe);
++ } else
++ i_params->dmd1_rslts.locked = FALSE;
++
++ break;
++ case STV0900_DEMOD_2:
++ if (i_params->chip_id <= 0x11) {
++ if ((stv0900_get_bits(i_params, F0900_P2_HEADER_MODE) == STV0900_DVBS_FOUND) &&
++ (i_params->dmd2_srch_iq_inv <= STV0900_IQ_AUTO_NORMAL_FIRST))
++ signal_type = stv0900_dvbs1_acq_workaround(fe);
++ } else
++ i_params->dmd2_rslts.locked = FALSE;
++ break;
++ }
++ }
++
++ return signal_type;
++}
++
+diff --git a/drivers/media/dvb/frontends/stv6110.c b/drivers/media/dvb/frontends/stv6110.c
+new file mode 100644
+index 0000000..70efac8
+--- /dev/null
++++ b/drivers/media/dvb/frontends/stv6110.c
+@@ -0,0 +1,456 @@
++/*
++ * stv6110.c
++ *
++ * Driver for ST STV6110 satellite tuner IC.
++ *
++ * Copyright (C) 2009 NetUP Inc.
++ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
++ *
++ * 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 <linux/module.h>
++#include <linux/dvb/frontend.h>
++
++#include <linux/types.h>
++
++#include "stv6110.h"
++
++static int debug;
++
++struct stv6110_priv {
++ int i2c_address;
++ struct i2c_adapter *i2c;
++
++ u32 mclk;
++ u8 regs[8];
++};
++
++#define dprintk(args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG args); \
++ } while (0)
++
++static s32 abssub(s32 a, s32 b)
++{
++ if (a > b)
++ return a - b;
++ else
++ return b - a;
++};
++
++static int stv6110_release(struct dvb_frontend *fe)
++{
++ kfree(fe->tuner_priv);
++ fe->tuner_priv = NULL;
++ return 0;
++}
++
++static int stv6110_write_regs(struct dvb_frontend *fe, u8 buf[],
++ int start, int len)
++{
++ struct stv6110_priv *priv = fe->tuner_priv;
++ int rc;
++ u8 cmdbuf[len + 1];
++ struct i2c_msg msg = {
++ .addr = priv->i2c_address,
++ .flags = 0,
++ .buf = cmdbuf,
++ .len = len + 1
++ };
++
++ dprintk("%s\n", __func__);
++
++ if (start + len > 8)
++ return -EINVAL;
++
++ memcpy(&cmdbuf[1], buf, len);
++ cmdbuf[0] = start;
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 1);
++
++ rc = i2c_transfer(priv->i2c, &msg, 1);
++ if (rc != 1)
++ dprintk("%s: i2c error\n", __func__);
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0);
++
++ return 0;
++}
++
++static int stv6110_read_regs(struct dvb_frontend *fe, u8 regs[],
++ int start, int len)
++{
++ struct stv6110_priv *priv = fe->tuner_priv;
++ int rc;
++ u8 reg[] = { start };
++ struct i2c_msg msg_wr = {
++ .addr = priv->i2c_address,
++ .flags = 0,
++ .buf = reg,
++ .len = 1,
++ };
++
++ struct i2c_msg msg_rd = {
++ .addr = priv->i2c_address,
++ .flags = I2C_M_RD,
++ .buf = regs,
++ .len = len,
++ };
++ /* write subaddr */
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 1);
++
++ rc = i2c_transfer(priv->i2c, &msg_wr, 1);
++ if (rc != 1)
++ dprintk("%s: i2c error\n", __func__);
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0);
++ /* read registers */
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 1);
++
++ rc = i2c_transfer(priv->i2c, &msg_rd, 1);
++ if (rc != 1)
++ dprintk("%s: i2c error\n", __func__);
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0);
++
++ memcpy(&priv->regs[start], regs, len);
++
++ return 0;
++}
++
++static int stv6110_read_reg(struct dvb_frontend *fe, int start)
++{
++ u8 buf[] = { 0 };
++ stv6110_read_regs(fe, buf, start, 1);
++
++ return buf[0];
++}
++
++static int stv6110_sleep(struct dvb_frontend *fe)
++{
++ u8 reg[] = { 0 };
++ stv6110_write_regs(fe, reg, 0, 1);
++
++ return 0;
++}
++
++static u32 carrier_width(u32 symbol_rate, fe_rolloff_t rolloff)
++{
++ u32 rlf;
++
++ switch (rolloff) {
++ case ROLLOFF_20:
++ rlf = 20;
++ break;
++ case ROLLOFF_25:
++ rlf = 25;
++ break;
++ default:
++ rlf = 35;
++ break;
++ }
++
++ return symbol_rate + ((symbol_rate * rlf) / 100);
++}
++
++static int stv6110_set_bandwidth(struct dvb_frontend *fe, u32 bandwidth)
++{
++ struct stv6110_priv *priv = fe->tuner_priv;
++ u8 r8, ret = 0x04;
++ int i;
++
++ if ((bandwidth / 2) > 36000000) /*BW/2 max=31+5=36 mhz for r8=31*/
++ r8 = 31;
++ else if ((bandwidth / 2) < 5000000) /* BW/2 min=5Mhz for F=0 */
++ r8 = 0;
++ else /*if 5 < BW/2 < 36*/
++ r8 = (bandwidth / 2) / 1000000 - 5;
++
++ /* ctrl3, RCCLKOFF = 0 Activate the calibration Clock */
++ /* ctrl3, CF = r8 Set the LPF value */
++ priv->regs[RSTV6110_CTRL3] &= ~((1 << 6) | 0x1f);
++ priv->regs[RSTV6110_CTRL3] |= (r8 & 0x1f);
++ stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL3], RSTV6110_CTRL3, 1);
++ /* stat1, CALRCSTRT = 1 Start LPF auto calibration*/
++ priv->regs[RSTV6110_STAT1] |= 0x02;
++ stv6110_write_regs(fe, &priv->regs[RSTV6110_STAT1], RSTV6110_STAT1, 1);
++
++ i = 0;
++ /* Wait for CALRCSTRT == 0 */
++ while ((i < 10) && (ret != 0)) {
++ ret = ((stv6110_read_reg(fe, RSTV6110_STAT1)) & 0x02);
++ mdelay(1); /* wait for LPF auto calibration */
++ i++;
++ }
++
++ /* RCCLKOFF = 1 calibration done, desactivate the calibration Clock */
++ priv->regs[RSTV6110_CTRL3] |= (1 << 6);
++ stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL3], RSTV6110_CTRL3, 1);
++ return 0;
++}
++
++static int stv6110_init(struct dvb_frontend *fe)
++{
++ struct stv6110_priv *priv = fe->tuner_priv;
++ u8 buf0[] = { 0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e };
++
++ memcpy(priv->regs, buf0, 8);
++ /* K = (Reference / 1000000) - 16 */
++ priv->regs[RSTV6110_CTRL1] &= ~(0x1f << 3);
++ priv->regs[RSTV6110_CTRL1] |=
++ ((((priv->mclk / 1000000) - 16) & 0x1f) << 3);
++
++ stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL1], RSTV6110_CTRL1, 8);
++ msleep(1);
++ stv6110_set_bandwidth(fe, 72000000);
++
++ return 0;
++}
++
++static int stv6110_get_frequency(struct dvb_frontend *fe, u32 *frequency)
++{
++ struct stv6110_priv *priv = fe->tuner_priv;
++ u32 nbsteps, divider, psd2, freq;
++ u8 regs[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
++
++ stv6110_read_regs(fe, regs, 0, 8);
++ /*N*/
++ divider = (priv->regs[RSTV6110_TUNING2] & 0x0f) << 8;
++ divider += priv->regs[RSTV6110_TUNING1];
++
++ /*R*/
++ nbsteps = (priv->regs[RSTV6110_TUNING2] >> 6) & 3;
++ /*p*/
++ psd2 = (priv->regs[RSTV6110_TUNING2] >> 4) & 1;
++
++ freq = divider * (priv->mclk / 1000);
++ freq /= (1 << (nbsteps + psd2));
++ freq /= 4;
++
++ *frequency = freq;
++
++ return 0;
++}
++
++static int stv6110_set_frequency(struct dvb_frontend *fe, u32 frequency)
++{
++ struct stv6110_priv *priv = fe->tuner_priv;
++ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
++ u8 ret = 0x04;
++ u32 divider, ref, p, presc, i, result_freq, vco_freq;
++ s32 p_calc, p_calc_opt = 1000, r_div, r_div_opt = 0, p_val;
++ s32 srate; u8 gain;
++
++ dprintk("%s, freq=%d kHz, mclk=%d Hz\n", __func__,
++ frequency, priv->mclk);
++
++ /* K = (Reference / 1000000) - 16 */
++ priv->regs[RSTV6110_CTRL1] &= ~(0x1f << 3);
++ priv->regs[RSTV6110_CTRL1] |=
++ ((((priv->mclk / 1000000) - 16) & 0x1f) << 3);
++
++ /* BB_GAIN = db/2 */
++ if (fe->ops.set_property && fe->ops.get_property) {
++ srate = c->symbol_rate;
++ dprintk("%s: Get Frontend parameters: srate=%d\n",
++ __func__, srate);
++ } else
++ srate = 15000000;
++
++ if (srate >= 15000000)
++ gain = 3; /* +6 dB */
++ else if (srate >= 5000000)
++ gain = 3; /* +6 dB */
++ else
++ gain = 3; /* +6 dB */
++
++ priv->regs[RSTV6110_CTRL2] &= ~0x0f;
++ priv->regs[RSTV6110_CTRL2] |= (gain & 0x0f);
++
++ if (frequency <= 1023000) {
++ p = 1;
++ presc = 0;
++ } else if (frequency <= 1300000) {
++ p = 1;
++ presc = 1;
++ } else if (frequency <= 2046000) {
++ p = 0;
++ presc = 0;
++ } else {
++ p = 0;
++ presc = 1;
++ }
++ /* DIV4SEL = p*/
++ priv->regs[RSTV6110_TUNING2] &= ~(1 << 4);
++ priv->regs[RSTV6110_TUNING2] |= (p << 4);
++
++ /* PRESC32ON = presc */
++ priv->regs[RSTV6110_TUNING2] &= ~(1 << 5);
++ priv->regs[RSTV6110_TUNING2] |= (presc << 5);
++
++ p_val = (int)(1 << (p + 1)) * 10;/* P = 2 or P = 4 */
++ for (r_div = 0; r_div <= 3; r_div++) {
++ p_calc = (priv->mclk / 100000);
++ p_calc /= (1 << (r_div + 1));
++ if ((abssub(p_calc, p_val)) < (abssub(p_calc_opt, p_val)))
++ r_div_opt = r_div;
++
++ p_calc_opt = (priv->mclk / 100000);
++ p_calc_opt /= (1 << (r_div_opt + 1));
++ }
++
++ ref = priv->mclk / ((1 << (r_div_opt + 1)) * (1 << (p + 1)));
++ divider = (((frequency * 1000) + (ref >> 1)) / ref);
++
++ /* RDIV = r_div_opt */
++ priv->regs[RSTV6110_TUNING2] &= ~(3 << 6);
++ priv->regs[RSTV6110_TUNING2] |= (((r_div_opt) & 3) << 6);
++
++ /* NDIV_MSB = MSB(divider) */
++ priv->regs[RSTV6110_TUNING2] &= ~0x0f;
++ priv->regs[RSTV6110_TUNING2] |= (((divider) >> 8) & 0x0f);
++
++ /* NDIV_LSB, LSB(divider) */
++ priv->regs[RSTV6110_TUNING1] = (divider & 0xff);
++
++ /* CALVCOSTRT = 1 VCO Auto Calibration */
++ priv->regs[RSTV6110_STAT1] |= 0x04;
++ stv6110_write_regs(fe, &priv->regs[RSTV6110_CTRL1],
++ RSTV6110_CTRL1, 8);
++
++ i = 0;
++ /* Wait for CALVCOSTRT == 0 */
++ while ((i < 10) && (ret != 0)) {
++ ret = ((stv6110_read_reg(fe, RSTV6110_STAT1)) & 0x04);
++ msleep(1); /* wait for VCO auto calibration */
++ i++;
++ }
++
++ ret = stv6110_read_reg(fe, RSTV6110_STAT1);
++ stv6110_get_frequency(fe, &result_freq);
++
++ vco_freq = divider * ((priv->mclk / 1000) / ((1 << (r_div_opt + 1))));
++ dprintk("%s, stat1=%x, lo_freq=%d kHz, vco_frec=%d kHz\n", __func__,
++ ret, result_freq, vco_freq);
++
++ return 0;
++}
++
++static int stv6110_set_params(struct dvb_frontend *fe,
++ struct dvb_frontend_parameters *params)
++{
++ struct dtv_frontend_properties *c = &fe->dtv_property_cache;
++ u32 bandwidth = carrier_width(c->symbol_rate, c->rolloff);
++
++ stv6110_set_frequency(fe, c->frequency);
++ stv6110_set_bandwidth(fe, bandwidth);
++
++ return 0;
++}
++
++static int stv6110_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
++{
++ struct stv6110_priv *priv = fe->tuner_priv;
++ u8 r8 = 0;
++ u8 regs[] = { 0, 0, 0, 0, 0, 0, 0, 0 };
++ stv6110_read_regs(fe, regs, 0, 8);
++
++ /* CF */
++ r8 = priv->regs[RSTV6110_CTRL3] & 0x1f;
++ *bandwidth = (r8 + 5) * 2000000;/* x2 for ZIF tuner BW/2 = F+5 Mhz */
++
++ return 0;
++}
++
++static struct dvb_tuner_ops stv6110_tuner_ops = {
++ .info = {
++ .name = "ST STV6110",
++ .frequency_min = 950000,
++ .frequency_max = 2150000,
++ .frequency_step = 1000,
++ },
++ .init = stv6110_init,
++ .release = stv6110_release,
++ .sleep = stv6110_sleep,
++ .set_params = stv6110_set_params,
++ .get_frequency = stv6110_get_frequency,
++ .set_frequency = stv6110_set_frequency,
++ .get_bandwidth = stv6110_get_bandwidth,
++ .set_bandwidth = stv6110_set_bandwidth,
++
++};
++
++struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe,
++ const struct stv6110_config *config,
++ struct i2c_adapter *i2c)
++{
++ struct stv6110_priv *priv = NULL;
++ u8 reg0[] = { 0x00, 0x07, 0x11, 0xdc, 0x85, 0x17, 0x01, 0xe6, 0x1e };
++
++ struct i2c_msg msg[] = {
++ {
++ .addr = config->i2c_address,
++ .flags = 0,
++ .buf = reg0,
++ .len = 9
++ }
++ };
++ int ret;
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 1);
++
++ ret = i2c_transfer(i2c, msg, 1);
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0);
++
++ if (ret != 1)
++ return NULL;
++
++ priv = kzalloc(sizeof(struct stv6110_priv), GFP_KERNEL);
++ if (priv == NULL)
++ return NULL;
++
++ priv->i2c_address = config->i2c_address;
++ priv->i2c = i2c;
++ priv->mclk = config->mclk;
++
++ memcpy(&priv->regs, &reg0[1], 8);
++
++ memcpy(&fe->ops.tuner_ops, &stv6110_tuner_ops,
++ sizeof(struct dvb_tuner_ops));
++ fe->tuner_priv = priv;
++ printk(KERN_INFO "STV6110 attached on addr=%x!\n", priv->i2c_address);
++
++ return fe;
++}
++EXPORT_SYMBOL(stv6110_attach);
++
++module_param(debug, int, 0644);
++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
++
++MODULE_DESCRIPTION("ST STV6110 driver");
++MODULE_AUTHOR("Igor M. Liplianin");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/media/dvb/frontends/stv6110.h b/drivers/media/dvb/frontends/stv6110.h
+new file mode 100644
+index 0000000..1c0314d
+--- /dev/null
++++ b/drivers/media/dvb/frontends/stv6110.h
+@@ -0,0 +1,62 @@
++/*
++ * stv6110.h
++ *
++ * Driver for ST STV6110 satellite tuner IC.
++ *
++ * Copyright (C) 2009 NetUP Inc.
++ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
++ *
++ * 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 __DVB_STV6110_H__
++#define __DVB_STV6110_H__
++
++#include <linux/i2c.h>
++#include "dvb_frontend.h"
++
++/* registers */
++#define RSTV6110_CTRL1 0
++#define RSTV6110_CTRL2 1
++#define RSTV6110_TUNING1 2
++#define RSTV6110_TUNING2 3
++#define RSTV6110_CTRL3 4
++#define RSTV6110_STAT1 5
++#define RSTV6110_STAT2 6
++#define RSTV6110_STAT3 7
++
++struct stv6110_config {
++ u8 i2c_address;
++ u32 mclk;
++ int iq_wiring;
++};
++
++#if defined(CONFIG_DVB_STV6110) || (defined(CONFIG_DVB_STV6110_MODULE) \
++ && defined(MODULE))
++extern struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe,
++ const struct stv6110_config *config,
++ struct i2c_adapter *i2c);
++#else
++static inline struct dvb_frontend *stv6110_attach(struct dvb_frontend *fe,
++ const struct stv6110_config *config,
++ struct i2c_adapter *i2c)
++{
++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
++ return NULL;
++}
++#endif
++
++#endif
+diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c
+index 1465ff7..4981cef 100644
+--- a/drivers/media/dvb/frontends/tda1004x.c
++++ b/drivers/media/dvb/frontends/tda1004x.c
+@@ -162,7 +162,7 @@ static int tda1004x_read_byte(struct tda1004x_state *state, int reg)
+ if (ret != 2) {
+ dprintk("%s: error reg=0x%x, ret=%i\n", __func__, reg,
+ ret);
+- return -1;
++ return -EINVAL;
+ }
+
+ dprintk("%s: success reg=0x%x, data=0x%x, ret=%i\n", __func__,
+@@ -481,16 +481,18 @@ static void tda10046_init_plls(struct dvb_frontend* fe)
+ static int tda10046_fwupload(struct dvb_frontend* fe)
+ {
+ struct tda1004x_state* state = fe->demodulator_priv;
+- int ret;
++ int ret, confc4;
+ const struct firmware *fw;
+
+ /* reset + wake up chip */
+ if (state->config->xtal_freq == TDA10046_XTAL_4M) {
+- tda1004x_write_byteI(state, TDA1004X_CONFC4, 0);
++ confc4 = 0;
+ } else {
+ dprintk("%s: 16MHz Xtal, reducing I2C speed\n", __func__);
+- tda1004x_write_byteI(state, TDA1004X_CONFC4, 0x80);
++ confc4 = 0x80;
+ }
++ tda1004x_write_byteI(state, TDA1004X_CONFC4, confc4);
++
+ tda1004x_write_mask(state, TDA10046H_CONF_TRISTATE1, 1, 0);
+ /* set GPIO 1 and 3 */
+ if (state->config->gpio_config != TDA10046_GPTRI) {
+@@ -508,13 +510,29 @@ static int tda10046_fwupload(struct dvb_frontend* fe)
+ if (tda1004x_check_upload_ok(state) == 0)
+ return 0;
+
++ /*
++ For i2c normal work, we need to slow down the bus speed.
++ However, the slow down breaks the eeprom firmware load.
++ So, use normal speed for eeprom booting and then restore the
++ i2c speed after that. Tested with MSI TV @nyware A/D board,
++ that comes with firmware version 29 inside their eeprom.
++
++ It should also be noticed that no other I2C transfer should
++ be in course while booting from eeprom, otherwise, tda10046
++ goes into an instable state. So, proper locking are needed
++ at the i2c bus master.
++ */
+ printk(KERN_INFO "tda1004x: trying to boot from eeprom\n");
+- tda1004x_write_mask(state, TDA1004X_CONFC4, 4, 4);
++ tda1004x_write_byteI(state, TDA1004X_CONFC4, 4);
+ msleep(300);
+- /* don't re-upload unless necessary */
++ tda1004x_write_byteI(state, TDA1004X_CONFC4, confc4);
++
++ /* Checks if eeprom firmware went without troubles */
+ if (tda1004x_check_upload_ok(state) == 0)
+ return 0;
+
++ /* eeprom firmware didn't work. Load one manually. */
++
+ if (state->config->request_firmware != NULL) {
+ /* request the firmware, this will block until someone uploads it */
+ printk(KERN_INFO "tda1004x: waiting for firmware upload...\n");
+diff --git a/drivers/media/dvb/frontends/zl10036.c b/drivers/media/dvb/frontends/zl10036.c
+new file mode 100644
+index 0000000..e22a0b3
+--- /dev/null
++++ b/drivers/media/dvb/frontends/zl10036.c
+@@ -0,0 +1,519 @@
++/**
++ * Driver for Zarlink zl10036 DVB-S silicon tuner
++ *
++ * Copyright (C) 2006 Tino Reichardt
++ * Copyright (C) 2007-2009 Matthias Schwarzott <zzam@gentoo.de>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License Version 2, as
++ * published by the Free Software Foundation.
++ *
++ * 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.
++ *
++ **
++ * The data sheet for this tuner can be found at:
++ * http://www.mcmilk.de/projects/dvb-card/datasheets/ZL10036.pdf
++ *
++ * This one is working: (at my Avermedia DVB-S Pro)
++ * - zl10036 (40pin, FTA)
++ *
++ * A driver for zl10038 should be very similar.
++ */
++
++#include <linux/module.h>
++#include <linux/dvb/frontend.h>
++#include <asm/types.h>
++
++#include "zl10036.h"
++
++static int zl10036_debug;
++#define dprintk(level, args...) \
++ do { if (zl10036_debug & level) printk(KERN_DEBUG "zl10036: " args); \
++ } while (0)
++
++#define deb_info(args...) dprintk(0x01, args)
++#define deb_i2c(args...) dprintk(0x02, args)
++
++struct zl10036_state {
++ struct i2c_adapter *i2c;
++ const struct zl10036_config *config;
++ u32 frequency;
++ u8 br, bf;
++};
++
++
++/* This driver assumes the tuner is driven by a 10.111MHz Cristal */
++#define _XTAL 10111
++
++/* Some of the possible dividers:
++ * 64, (write 0x05 to reg), freq step size 158kHz
++ * 10, (write 0x0a to reg), freq step size 1.011kHz (used here)
++ * 5, (write 0x09 to reg), freq step size 2.022kHz
++ */
++
++#define _RDIV 10
++#define _RDIV_REG 0x0a
++#define _FR (_XTAL/_RDIV)
++
++#define STATUS_POR 0x80 /* Power on Reset */
++#define STATUS_FL 0x40 /* Frequency & Phase Lock */
++
++/* read/write for zl10036 and zl10038 */
++
++static int zl10036_read_status_reg(struct zl10036_state *state)
++{
++ u8 status;
++ struct i2c_msg msg[1] = {
++ { .addr = state->config->tuner_address, .flags = I2C_M_RD,
++ .buf = &status, .len = sizeof(status) },
++ };
++
++ if (i2c_transfer(state->i2c, msg, 1) != 1) {
++ printk(KERN_ERR "%s: i2c read failed at addr=%02x\n",
++ __func__, state->config->tuner_address);
++ return -EIO;
++ }
++
++ deb_i2c("R(status): %02x [FL=%d]\n", status,
++ (status & STATUS_FL) ? 1 : 0);
++ if (status & STATUS_POR)
++ deb_info("%s: Power-On-Reset bit enabled - "
++ "need to initialize the tuner\n", __func__);
++
++ return status;
++}
++
++static int zl10036_write(struct zl10036_state *state, u8 buf[], u8 count)
++{
++ struct i2c_msg msg[1] = {
++ { .addr = state->config->tuner_address, .flags = 0,
++ .buf = buf, .len = count },
++ };
++ u8 reg = 0;
++ int ret;
++
++ if (zl10036_debug & 0x02) {
++ /* every 8bit-value satisifes this!
++ * so only check for debug log */
++ if ((buf[0] & 0x80) == 0x00)
++ reg = 2;
++ else if ((buf[0] & 0xc0) == 0x80)
++ reg = 4;
++ else if ((buf[0] & 0xf0) == 0xc0)
++ reg = 6;
++ else if ((buf[0] & 0xf0) == 0xd0)
++ reg = 8;
++ else if ((buf[0] & 0xf0) == 0xe0)
++ reg = 10;
++ else if ((buf[0] & 0xf0) == 0xf0)
++ reg = 12;
++
++ deb_i2c("W(%d):", reg);
++ {
++ int i;
++ for (i = 0; i < count; i++)
++ printk(KERN_CONT " %02x", buf[i]);
++ printk(KERN_CONT "\n");
++ }
++ }
++
++ ret = i2c_transfer(state->i2c, msg, 1);
++ if (ret != 1) {
++ printk(KERN_ERR "%s: i2c error, ret=%d\n", __func__, ret);
++ return -EIO;
++ }
++
++ return 0;
++}
++
++static int zl10036_release(struct dvb_frontend *fe)
++{
++ struct zl10036_state *state = fe->tuner_priv;
++
++ fe->tuner_priv = NULL;
++ kfree(state);
++
++ return 0;
++}
++
++static int zl10036_sleep(struct dvb_frontend *fe)
++{
++ struct zl10036_state *state = fe->tuner_priv;
++ u8 buf[] = { 0xf0, 0x80 }; /* regs 12/13 */
++ int ret;
++
++ deb_info("%s\n", __func__);
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
++
++ ret = zl10036_write(state, buf, sizeof(buf));
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
++
++ return ret;
++}
++
++/**
++ * register map of the ZL10036/ZL10038
++ *
++ * reg[default] content
++ * 2[0x00]: 0 | N14 | N13 | N12 | N11 | N10 | N9 | N8
++ * 3[0x00]: N7 | N6 | N5 | N4 | N3 | N2 | N1 | N0
++ * 4[0x80]: 1 | 0 | RFG | BA1 | BA0 | BG1 | BG0 | LEN
++ * 5[0x00]: P0 | C1 | C0 | R4 | R3 | R2 | R1 | R0
++ * 6[0xc0]: 1 | 1 | 0 | 0 | RSD | 0 | 0 | 0
++ * 7[0x20]: P1 | BF6 | BF5 | BF4 | BF3 | BF2 | BF1 | 0
++ * 8[0xdb]: 1 | 1 | 0 | 1 | 0 | CC | 1 | 1
++ * 9[0x30]: VSD | V2 | V1 | V0 | S3 | S2 | S1 | S0
++ * 10[0xe1]: 1 | 1 | 1 | 0 | 0 | LS2 | LS1 | LS0
++ * 11[0xf5]: WS | WH2 | WH1 | WH0 | WL2 | WL1 | WL0 | WRE
++ * 12[0xf0]: 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0
++ * 13[0x28]: PD | BR4 | BR3 | BR2 | BR1 | BR0 | CLR | TL
++ */
++
++static int zl10036_set_frequency(struct zl10036_state *state, u32 frequency)
++{
++ u8 buf[2];
++ u32 div, foffset;
++
++ div = (frequency + _FR/2) / _FR;
++ state->frequency = div * _FR;
++
++ foffset = frequency - state->frequency;
++
++ buf[0] = (div >> 8) & 0x7f;
++ buf[1] = (div >> 0) & 0xff;
++
++ deb_info("%s: ftodo=%u fpriv=%u ferr=%d div=%u\n", __func__,
++ frequency, state->frequency, foffset, div);
++
++ return zl10036_write(state, buf, sizeof(buf));
++}
++
++static int zl10036_set_bandwidth(struct zl10036_state *state, u32 fbw)
++{
++ /* fbw is measured in kHz */
++ u8 br, bf;
++ int ret;
++ u8 buf_bf[] = {
++ 0xc0, 0x00, /* 6/7: rsd=0 bf=0 */
++ };
++ u8 buf_br[] = {
++ 0xf0, 0x00, /* 12/13: br=0xa clr=0 tl=0*/
++ };
++ u8 zl10036_rsd_off[] = { 0xc8 }; /* set RSD=1 */
++
++ /* ensure correct values */
++ if (fbw > 35000)
++ fbw = 35000;
++ if (fbw < 8000)
++ fbw = 8000;
++
++#define _BR_MAXIMUM (_XTAL/575) /* _XTAL / 575kHz = 17 */
++
++ /* <= 28,82 MHz */
++ if (fbw <= 28820) {
++ br = _BR_MAXIMUM;
++ } else {
++ /**
++ * f(bw)=34,6MHz f(xtal)=10.111MHz
++ * br = (10111/34600) * 63 * 1/K = 14;
++ */
++ br = ((_XTAL * 21 * 1000) / (fbw * 419));
++ }
++
++ /* ensure correct values */
++ if (br < 4)
++ br = 4;
++ if (br > _BR_MAXIMUM)
++ br = _BR_MAXIMUM;
++
++ /*
++ * k = 1.257
++ * bf = fbw/_XTAL * br * k - 1 */
++
++ bf = (fbw * br * 1257) / (_XTAL * 1000) - 1;
++
++ /* ensure correct values */
++ if (bf > 62)
++ bf = 62;
++
++ buf_bf[1] = (bf << 1) & 0x7e;
++ buf_br[1] = (br << 2) & 0x7c;
++ deb_info("%s: BW=%d br=%u bf=%u\n", __func__, fbw, br, bf);
++
++ if (br != state->br) {
++ ret = zl10036_write(state, buf_br, sizeof(buf_br));
++ if (ret < 0)
++ return ret;
++ }
++
++ if (bf != state->bf) {
++ ret = zl10036_write(state, buf_bf, sizeof(buf_bf));
++ if (ret < 0)
++ return ret;
++
++ /* time = br/(32* fxtal) */
++ /* minimal sleep time to be calculated
++ * maximum br is 63 -> max time = 2 /10 MHz = 2e-7 */
++ msleep(1);
++
++ ret = zl10036_write(state, zl10036_rsd_off,
++ sizeof(zl10036_rsd_off));
++ if (ret < 0)
++ return ret;
++ }
++
++ state->br = br;
++ state->bf = bf;
++
++ return 0;
++}
++
++static int zl10036_set_gain_params(struct zl10036_state *state,
++ int c)
++{
++ u8 buf[2];
++ u8 rfg, ba, bg;
++
++ /* default values */
++ rfg = 0; /* enable when using an lna */
++ ba = 1;
++ bg = 1;
++
++ /* reg 4 */
++ buf[0] = 0x80 | ((rfg << 5) & 0x20)
++ | ((ba << 3) & 0x18) | ((bg << 1) & 0x06);
++
++ if (!state->config->rf_loop_enable)
++ buf[0] |= 0x01;
++
++ /* P0=0 */
++ buf[1] = _RDIV_REG | ((c << 5) & 0x60);
++
++ deb_info("%s: c=%u rfg=%u ba=%u bg=%u\n", __func__, c, rfg, ba, bg);
++ return zl10036_write(state, buf, sizeof(buf));
++}
++
++static int zl10036_set_params(struct dvb_frontend *fe,
++ struct dvb_frontend_parameters *params)
++{
++ struct zl10036_state *state = fe->tuner_priv;
++ int ret = 0;
++ u32 frequency = params->frequency;
++ u32 fbw;
++ int i;
++ u8 c;
++
++ /* ensure correct values
++ * maybe redundant as core already checks this */
++ if ((frequency < fe->ops.info.frequency_min)
++ || (frequency > fe->ops.info.frequency_max))
++ return -EINVAL;
++
++ /**
++ * alpha = 1.35 for dvb-s
++ * fBW = (alpha*symbolrate)/(2*0.8)
++ * 1.35 / (2*0.8) = 27 / 32
++ */
++ fbw = (27 * params->u.qpsk.symbol_rate) / 32;
++
++ /* scale to kHz */
++ fbw /= 1000;
++
++ /* Add safe margin of 3MHz */
++ fbw += 3000;
++
++ /* setting the charge pump - guessed values */
++ if (frequency < 950000)
++ return -EINVAL;
++ else if (frequency < 1250000)
++ c = 0;
++ else if (frequency < 1750000)
++ c = 1;
++ else if (frequency < 2175000)
++ c = 2;
++ else
++ return -EINVAL;
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
++
++ ret = zl10036_set_gain_params(state, c);
++ if (ret < 0)
++ goto error;
++
++ ret = zl10036_set_frequency(state, params->frequency);
++ if (ret < 0)
++ goto error;
++
++ ret = zl10036_set_bandwidth(state, fbw);
++ if (ret < 0)
++ goto error;
++
++ /* wait for tuner lock - no idea if this is really needed */
++ for (i = 0; i < 20; i++) {
++ ret = zl10036_read_status_reg(state);
++ if (ret < 0)
++ goto error;
++
++ /* check Frequency & Phase Lock Bit */
++ if (ret & STATUS_FL)
++ break;
++
++ msleep(10);
++ }
++
++error:
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
++
++ return ret;
++}
++
++static int zl10036_get_frequency(struct dvb_frontend *fe, u32 *frequency)
++{
++ struct zl10036_state *state = fe->tuner_priv;
++
++ *frequency = state->frequency;
++
++ return 0;
++}
++
++static int zl10036_init_regs(struct zl10036_state *state)
++{
++ int ret;
++ int i;
++
++ /* could also be one block from reg 2 to 13 and additional 10/11 */
++ u8 zl10036_init_tab[][2] = {
++ { 0x04, 0x00 }, /* 2/3: div=0x400 - arbitrary value */
++ { 0x8b, _RDIV_REG }, /* 4/5: rfg=0 ba=1 bg=1 len=? */
++ /* p0=0 c=0 r=_RDIV_REG */
++ { 0xc0, 0x20 }, /* 6/7: rsd=0 bf=0x10 */
++ { 0xd3, 0x40 }, /* 8/9: from datasheet */
++ { 0xe3, 0x5b }, /* 10/11: lock window level */
++ { 0xf0, 0x28 }, /* 12/13: br=0xa clr=0 tl=0*/
++ { 0xe3, 0xf9 }, /* 10/11: unlock window level */
++ };
++
++ /* invalid values to trigger writing */
++ state->br = 0xff;
++ state->bf = 0xff;
++
++ if (!state->config->rf_loop_enable)
++ zl10036_init_tab[1][2] |= 0x01;
++
++ deb_info("%s\n", __func__);
++
++ for (i = 0; i < ARRAY_SIZE(zl10036_init_tab); i++) {
++ ret = zl10036_write(state, zl10036_init_tab[i], 2);
++ if (ret < 0)
++ return ret;
++ }
++
++ return 0;
++}
++
++static int zl10036_init(struct dvb_frontend *fe)
++{
++ struct zl10036_state *state = fe->tuner_priv;
++ int ret = 0;
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
++
++ ret = zl10036_read_status_reg(state);
++ if (ret < 0)
++ return ret;
++
++ /* Only init if Power-on-Reset bit is set? */
++ ret = zl10036_init_regs(state);
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
++
++ return ret;
++}
++
++static struct dvb_tuner_ops zl10036_tuner_ops = {
++ .info = {
++ .name = "Zarlink ZL10036",
++ .frequency_min = 950000,
++ .frequency_max = 2175000
++ },
++ .init = zl10036_init,
++ .release = zl10036_release,
++ .sleep = zl10036_sleep,
++ .set_params = zl10036_set_params,
++ .get_frequency = zl10036_get_frequency,
++};
++
++struct dvb_frontend *zl10036_attach(struct dvb_frontend *fe,
++ const struct zl10036_config *config,
++ struct i2c_adapter *i2c)
++{
++ struct zl10036_state *state = NULL;
++ int ret;
++
++ if (NULL == config) {
++ printk(KERN_ERR "%s: no config specified", __func__);
++ goto error;
++ }
++
++ state = kzalloc(sizeof(struct zl10036_state), GFP_KERNEL);
++ if (NULL == state)
++ return NULL;
++
++ state->config = config;
++ state->i2c = i2c;
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 1); /* open i2c_gate */
++
++ ret = zl10036_read_status_reg(state);
++ if (ret < 0) {
++ printk(KERN_ERR "%s: No zl10036 found\n", __func__);
++ goto error;
++ }
++
++ ret = zl10036_init_regs(state);
++ if (ret < 0) {
++ printk(KERN_ERR "%s: tuner initialization failed\n",
++ __func__);
++ goto error;
++ }
++
++ if (fe->ops.i2c_gate_ctrl)
++ fe->ops.i2c_gate_ctrl(fe, 0); /* close i2c_gate */
++
++ fe->tuner_priv = state;
++
++ memcpy(&fe->ops.tuner_ops, &zl10036_tuner_ops,
++ sizeof(struct dvb_tuner_ops));
++ printk(KERN_INFO "%s: tuner initialization (%s addr=0x%02x) ok\n",
++ __func__, fe->ops.tuner_ops.info.name, config->tuner_address);
++
++ return fe;
++
++error:
++ zl10036_release(fe);
++ return NULL;
++}
++EXPORT_SYMBOL(zl10036_attach);
++
++module_param_named(debug, zl10036_debug, int, 0644);
++MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
++MODULE_DESCRIPTION("DVB ZL10036 driver");
++MODULE_AUTHOR("Tino Reichardt");
++MODULE_AUTHOR("Matthias Schwarzott");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/media/dvb/frontends/zl10036.h b/drivers/media/dvb/frontends/zl10036.h
+new file mode 100644
+index 0000000..d84b8f8
+--- /dev/null
++++ b/drivers/media/dvb/frontends/zl10036.h
+@@ -0,0 +1,53 @@
++/**
++ * Driver for Zarlink ZL10036 DVB-S silicon tuner
++ *
++ * Copyright (C) 2006 Tino Reichardt
++ * Copyright (C) 2007-2009 Matthias Schwarzott <zzam@gentoo.de>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License Version 2, as
++ * published by the Free Software Foundation.
++ *
++ * 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 DVB_ZL10036_H
++#define DVB_ZL10036_H
++
++#include <linux/i2c.h>
++#include "dvb_frontend.h"
++
++/**
++ * Attach a zl10036 tuner to the supplied frontend structure.
++ *
++ * @param fe Frontend to attach to.
++ * @param config zl10036_config structure
++ * @return FE pointer on success, NULL on failure.
++ */
++
++struct zl10036_config {
++ u8 tuner_address;
++ int rf_loop_enable;
++};
++
++#if defined(CONFIG_DVB_ZL10036) || \
++ (defined(CONFIG_DVB_ZL10036_MODULE) && defined(MODULE))
++extern struct dvb_frontend *zl10036_attach(struct dvb_frontend *fe,
++ const struct zl10036_config *config, struct i2c_adapter *i2c);
++#else
++static inline struct dvb_frontend *zl10036_attach(struct dvb_frontend *fe,
++ const struct zl10036_config *config, struct i2c_adapter *i2c)
++{
++ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __func__);
++ return NULL;
++}
++#endif
++
++#endif /* DVB_ZL10036_H */
+diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb/frontends/zl10353.c
+index b150ed3..148b6f7 100644
+--- a/drivers/media/dvb/frontends/zl10353.c
++++ b/drivers/media/dvb/frontends/zl10353.c
+@@ -572,6 +572,10 @@ static int zl10353_init(struct dvb_frontend *fe)
+ zl10353_dump_regs(fe);
+ if (state->config.parallel_ts)
+ zl10353_reset_attach[2] &= ~0x20;
++ if (state->config.clock_ctl_1)
++ zl10353_reset_attach[3] = state->config.clock_ctl_1;
++ if (state->config.pll_0)
++ zl10353_reset_attach[4] = state->config.pll_0;
+
+ /* Do a "hard" reset if not already done */
+ if (zl10353_read_register(state, 0x50) != zl10353_reset_attach[1] ||
+@@ -614,6 +618,7 @@ struct dvb_frontend *zl10353_attach(const struct zl10353_config *config,
+ struct i2c_adapter *i2c)
+ {
+ struct zl10353_state *state = NULL;
++ int id;
+
+ /* allocate memory for the internal state */
+ state = kzalloc(sizeof(struct zl10353_state), GFP_KERNEL);
+@@ -625,7 +630,8 @@ struct dvb_frontend *zl10353_attach(const struct zl10353_config *config,
+ memcpy(&state->config, config, sizeof(struct zl10353_config));
+
+ /* check if the demod is there */
+- if (zl10353_read_register(state, CHIP_ID) != ID_ZL10353)
++ id = zl10353_read_register(state, CHIP_ID);
++ if ((id != ID_ZL10353) && (id != ID_CE6230) && (id != ID_CE6231))
+ goto error;
+
+ /* create dvb_frontend */
+diff --git a/drivers/media/dvb/frontends/zl10353.h b/drivers/media/dvb/frontends/zl10353.h
+index 2287bac..6e3ca9e 100644
+--- a/drivers/media/dvb/frontends/zl10353.h
++++ b/drivers/media/dvb/frontends/zl10353.h
+@@ -41,6 +41,10 @@ struct zl10353_config
+
+ /* set if i2c_gate_ctrl disable is required */
+ u8 disable_i2c_gate_ctrl:1;
++
++ /* clock control registers (0x51-0x54) */
++ u8 clock_ctl_1; /* default: 0x46 */
++ u8 pll_0; /* default: 0x15 */
+ };
+
+ #if defined(CONFIG_DVB_ZL10353) || (defined(CONFIG_DVB_ZL10353_MODULE) && defined(MODULE))
+diff --git a/drivers/media/dvb/frontends/zl10353_priv.h b/drivers/media/dvb/frontends/zl10353_priv.h
+index 055ff1f..e0dd1d3 100644
+--- a/drivers/media/dvb/frontends/zl10353_priv.h
++++ b/drivers/media/dvb/frontends/zl10353_priv.h
+@@ -22,7 +22,9 @@
+ #ifndef _ZL10353_PRIV_
+ #define _ZL10353_PRIV_
+
+-#define ID_ZL10353 0x14
++#define ID_ZL10353 0x14 /* Zarlink ZL10353 */
++#define ID_CE6230 0x18 /* Intel CE6230 */
++#define ID_CE6231 0x19 /* Intel CE6231 */
+
+ #define msb(x) (((x) >> 8) & 0xff)
+ #define lsb(x) ((x) & 0xff)
+@@ -50,6 +52,10 @@ enum zl10353_reg_addr {
+ TPS_RECEIVED_0 = 0x1E,
+ TPS_CURRENT_1 = 0x1F,
+ TPS_CURRENT_0 = 0x20,
++ CLOCK_CTL_0 = 0x51,
++ CLOCK_CTL_1 = 0x52,
++ PLL_0 = 0x53,
++ PLL_1 = 0x54,
+ RESET = 0x55,
+ AGC_TARGET = 0x56,
+ MCLK_RATIO = 0x5C,
+diff --git a/drivers/media/dvb/pluto2/pluto2.c b/drivers/media/dvb/pluto2/pluto2.c
+index d101b30..ee89623 100644
+--- a/drivers/media/dvb/pluto2/pluto2.c
++++ b/drivers/media/dvb/pluto2/pluto2.c
+@@ -116,6 +116,7 @@ struct pluto {
+
+ /* irq */
+ unsigned int overflow;
++ unsigned int dead;
+
+ /* dma */
+ dma_addr_t dma_addr;
+@@ -336,8 +337,10 @@ static irqreturn_t pluto_irq(int irq, void *dev_id)
+ return IRQ_NONE;
+
+ if (tscr == 0xffffffff) {
+- // FIXME: maybe recover somehow
+- dev_err(&pluto->pdev->dev, "card hung up :(\n");
++ if (pluto->dead == 0)
++ dev_err(&pluto->pdev->dev, "card has hung or been ejected.\n");
++ /* It's dead Jim */
++ pluto->dead = 1;
+ return IRQ_HANDLED;
+ }
+
+diff --git a/drivers/media/dvb/siano/Makefile b/drivers/media/dvb/siano/Makefile
+index ee0737a..bcf93f4 100644
+--- a/drivers/media/dvb/siano/Makefile
++++ b/drivers/media/dvb/siano/Makefile
+@@ -1,6 +1,8 @@
+-sms1xxx-objs := smscoreapi.o smsusb.o smsdvb.o sms-cards.o
++sms1xxx-objs := smscoreapi.o sms-cards.o
+
+ obj-$(CONFIG_DVB_SIANO_SMS1XXX) += sms1xxx.o
++obj-$(CONFIG_DVB_SIANO_SMS1XXX) += smsusb.o
++obj-$(CONFIG_DVB_SIANO_SMS1XXX) += smsdvb.o
+
+ EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
+
+diff --git a/drivers/media/dvb/siano/sms-cards.c b/drivers/media/dvb/siano/sms-cards.c
+index 4307e4e..63e4d0e 100644
+--- a/drivers/media/dvb/siano/sms-cards.c
++++ b/drivers/media/dvb/siano/sms-cards.c
+@@ -19,50 +19,9 @@
+
+ #include "sms-cards.h"
+
+-struct usb_device_id smsusb_id_table[] = {
+-#ifdef CONFIG_DVB_SIANO_SMS1XXX_SMS_IDS
+- { USB_DEVICE(0x187f, 0x0010),
+- .driver_info = SMS1XXX_BOARD_SIANO_STELLAR },
+- { USB_DEVICE(0x187f, 0x0100),
+- .driver_info = SMS1XXX_BOARD_SIANO_STELLAR },
+- { USB_DEVICE(0x187f, 0x0200),
+- .driver_info = SMS1XXX_BOARD_SIANO_NOVA_A },
+- { USB_DEVICE(0x187f, 0x0201),
+- .driver_info = SMS1XXX_BOARD_SIANO_NOVA_B },
+- { USB_DEVICE(0x187f, 0x0300),
+- .driver_info = SMS1XXX_BOARD_SIANO_VEGA },
+-#endif
+- { USB_DEVICE(0x2040, 0x1700),
+- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT },
+- { USB_DEVICE(0x2040, 0x1800),
+- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A },
+- { USB_DEVICE(0x2040, 0x1801),
+- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B },
+- { USB_DEVICE(0x2040, 0x2000),
+- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
+- { USB_DEVICE(0x2040, 0x2009),
+- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 },
+- { USB_DEVICE(0x2040, 0x200a),
+- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
+- { USB_DEVICE(0x2040, 0x2010),
+- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
+- { USB_DEVICE(0x2040, 0x2019),
+- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
+- { USB_DEVICE(0x2040, 0x5500),
+- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+- { USB_DEVICE(0x2040, 0x5510),
+- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+- { USB_DEVICE(0x2040, 0x5520),
+- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+- { USB_DEVICE(0x2040, 0x5530),
+- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+- { USB_DEVICE(0x2040, 0x5580),
+- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+- { USB_DEVICE(0x2040, 0x5590),
+- .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
+- { } /* Terminating entry */
+-};
+-MODULE_DEVICE_TABLE(usb, smsusb_id_table);
++static int sms_dbg;
++module_param_named(cards_dbg, sms_dbg, int, 0644);
++MODULE_PARM_DESC(cards_dbg, "set debug level (info=1, adv=2 (or-able))");
+
+ static struct sms_board sms_boards[] = {
+ [SMS_BOARD_UNKNOWN] = {
+@@ -115,6 +74,7 @@ static struct sms_board sms_boards[] = {
+ .type = SMS_NOVA_B0,
+ .fw[DEVICE_MODE_DVBT_BDA] = "sms1xxx-hcw-55xxx-dvbt-02.fw",
+ .lna_ctrl = 29,
++ .rf_switch = 17,
+ },
+ [SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2] = {
+ .name = "Hauppauge WinTV MiniCard",
+@@ -130,6 +90,7 @@ struct sms_board *sms_get_board(int id)
+
+ return &sms_boards[id];
+ }
++EXPORT_SYMBOL_GPL(sms_get_board);
+
+ static int sms_set_gpio(struct smscore_device_t *coredev, int pin, int enable)
+ {
+@@ -182,6 +143,7 @@ int sms_board_setup(struct smscore_device_t *coredev)
+ }
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(sms_board_setup);
+
+ int sms_board_power(struct smscore_device_t *coredev, int onoff)
+ {
+@@ -197,12 +159,13 @@ int sms_board_power(struct smscore_device_t *coredev, int onoff)
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
+ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD:
+ /* LNA */
+- sms_set_gpio(coredev,
+- board->lna_ctrl, onoff ? 1 : 0);
++ if (!onoff)
++ sms_set_gpio(coredev, board->lna_ctrl, 0);
+ break;
+ }
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(sms_board_power);
+
+ int sms_board_led_feedback(struct smscore_device_t *coredev, int led)
+ {
+@@ -225,3 +188,40 @@ int sms_board_led_feedback(struct smscore_device_t *coredev, int led)
+ }
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(sms_board_led_feedback);
++
++int sms_board_lna_control(struct smscore_device_t *coredev, int onoff)
++{
++ int board_id = smscore_get_board_id(coredev);
++ struct sms_board *board = sms_get_board(board_id);
++
++ sms_debug("%s: LNA %s", __func__, onoff ? "enabled" : "disabled");
++
++ switch (board_id) {
++ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2:
++ case SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD:
++ sms_set_gpio(coredev,
++ board->rf_switch, onoff ? 1 : 0);
++ return sms_set_gpio(coredev,
++ board->lna_ctrl, onoff ? 1 : 0);
++ }
++ return -EINVAL;
++}
++EXPORT_SYMBOL_GPL(sms_board_lna_control);
++
++int sms_board_load_modules(int id)
++{
++ switch (id) {
++ case SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT:
++ case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A:
++ case SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B:
++ case SMS1XXX_BOARD_HAUPPAUGE_WINDHAM:
++ request_module("smsdvb");
++ break;
++ default:
++ /* do nothing */
++ break;
++ }
++ return 0;
++}
++EXPORT_SYMBOL_GPL(sms_board_load_modules);
+diff --git a/drivers/media/dvb/siano/sms-cards.h b/drivers/media/dvb/siano/sms-cards.h
+index 8e0fe9f..64d74c5 100644
+--- a/drivers/media/dvb/siano/sms-cards.h
++++ b/drivers/media/dvb/siano/sms-cards.h
+@@ -40,7 +40,7 @@ struct sms_board {
+ char *name, *fw[DEVICE_MODE_MAX];
+
+ /* gpios */
+- int led_power, led_hi, led_lo, lna_ctrl;
++ int led_power, led_hi, led_lo, lna_ctrl, rf_switch;
+ };
+
+ struct sms_board *sms_get_board(int id);
+@@ -52,7 +52,8 @@ int sms_board_setup(struct smscore_device_t *coredev);
+ #define SMS_LED_HI 2
+ int sms_board_led_feedback(struct smscore_device_t *coredev, int led);
+ int sms_board_power(struct smscore_device_t *coredev, int onoff);
++int sms_board_lna_control(struct smscore_device_t *coredev, int onoff);
+
+-extern struct usb_device_id smsusb_id_table[];
++extern int sms_board_load_modules(int id);
+
+ #endif /* __SMS_CARDS_H__ */
+diff --git a/drivers/media/dvb/siano/smscoreapi.c b/drivers/media/dvb/siano/smscoreapi.c
+index cf613f2..7bd4d1d 100644
+--- a/drivers/media/dvb/siano/smscoreapi.c
++++ b/drivers/media/dvb/siano/smscoreapi.c
+@@ -3,7 +3,7 @@
+ *
+ * This file contains implementation for the interface to sms core component
+ *
+- * author: Anatoly Greenblat
++ * author: Uri Shkolnik
+ *
+ * Copyright (c), 2005-2008 Siano Mobile Silicon, Inc.
+ *
+@@ -34,8 +34,8 @@
+ #include "smscoreapi.h"
+ #include "sms-cards.h"
+
+-int sms_debug;
+-module_param_named(debug, sms_debug, int, 0644);
++static int sms_dbg;
++module_param_named(debug, sms_dbg, int, 0644);
+ MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))");
+
+ struct smscore_device_notifyee_t {
+@@ -105,11 +105,13 @@ int smscore_led_state(struct smscore_device_t *core, int led)
+ core->led_state = led;
+ return core->led_state;
+ }
++EXPORT_SYMBOL_GPL(smscore_set_board_id);
+
+ int smscore_get_board_id(struct smscore_device_t *core)
+ {
+ return core->board_id;
+ }
++EXPORT_SYMBOL_GPL(smscore_get_board_id);
+
+ struct smscore_registry_entry_t {
+ struct list_head entry;
+@@ -170,6 +172,7 @@ int smscore_registry_getmode(char *devpath)
+
+ return default_mode;
+ }
++EXPORT_SYMBOL_GPL(smscore_registry_getmode);
+
+ static enum sms_device_type_st smscore_registry_gettype(char *devpath)
+ {
+@@ -261,6 +264,7 @@ int smscore_register_hotplug(hotplug_t hotplug)
+
+ return rc;
+ }
++EXPORT_SYMBOL_GPL(smscore_register_hotplug);
+
+ /**
+ * unregister a client callback that called when device plugged in/unplugged
+@@ -289,6 +293,7 @@ void smscore_unregister_hotplug(hotplug_t hotplug)
+
+ kmutex_unlock(&g_smscore_deviceslock);
+ }
++EXPORT_SYMBOL_GPL(smscore_unregister_hotplug);
+
+ static void smscore_notify_clients(struct smscore_device_t *coredev)
+ {
+@@ -432,6 +437,7 @@ int smscore_register_device(struct smsdevice_params_t *params,
+
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(smscore_register_device);
+
+ /**
+ * sets initial device mode and notifies client hotplugs that device is ready
+@@ -460,6 +466,7 @@ int smscore_start_device(struct smscore_device_t *coredev)
+
+ return rc;
+ }
++EXPORT_SYMBOL_GPL(smscore_start_device);
+
+ static int smscore_sendrequest_and_wait(struct smscore_device_t *coredev,
+ void *buffer, size_t size,
+@@ -688,6 +695,7 @@ void smscore_unregister_device(struct smscore_device_t *coredev)
+
+ sms_info("device %p destroyed", coredev);
+ }
++EXPORT_SYMBOL_GPL(smscore_unregister_device);
+
+ static int smscore_detect_mode(struct smscore_device_t *coredev)
+ {
+@@ -732,7 +740,7 @@ static char *smscore_fw_lkup[][SMS_NUM_OF_DEVICE_TYPES] = {
+ /*DVBH*/
+ {"none", "dvb_nova_12mhz.inp", "dvb_nova_12mhz_b0.inp", "none"},
+ /*TDMB*/
+- {"none", "tdmb_nova_12mhz.inp", "none", "none"},
++ {"none", "tdmb_nova_12mhz.inp", "tdmb_nova_12mhz_b0.inp", "none"},
+ /*DABIP*/
+ {"none", "none", "none", "none"},
+ /*BDA*/
+@@ -879,6 +887,7 @@ int smscore_get_device_mode(struct smscore_device_t *coredev)
+ {
+ return coredev->mode;
+ }
++EXPORT_SYMBOL_GPL(smscore_get_device_mode);
+
+ /**
+ * find client by response id & type within the clients list.
+@@ -1006,6 +1015,7 @@ void smscore_onresponse(struct smscore_device_t *coredev,
+ smscore_putbuffer(coredev, cb);
+ }
+ }
++EXPORT_SYMBOL_GPL(smscore_onresponse);
+
+ /**
+ * return pointer to next free buffer descriptor from core pool
+@@ -1031,6 +1041,7 @@ struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev)
+
+ return cb;
+ }
++EXPORT_SYMBOL_GPL(smscore_getbuffer);
+
+ /**
+ * return buffer descriptor to a pool
+@@ -1045,6 +1056,7 @@ void smscore_putbuffer(struct smscore_device_t *coredev,
+ {
+ list_add_locked(&cb->entry, &coredev->buffers, &coredev->bufferslock);
+ }
++EXPORT_SYMBOL_GPL(smscore_putbuffer);
+
+ static int smscore_validate_client(struct smscore_device_t *coredev,
+ struct smscore_client_t *client,
+@@ -1124,6 +1136,7 @@ int smscore_register_client(struct smscore_device_t *coredev,
+
+ return 0;
+ }
++EXPORT_SYMBOL_GPL(smscore_register_client);
+
+ /**
+ * frees smsclient object and all subclients associated with it
+@@ -1154,6 +1167,7 @@ void smscore_unregister_client(struct smscore_client_t *client)
+
+ spin_unlock_irqrestore(&coredev->clientslock, flags);
+ }
++EXPORT_SYMBOL_GPL(smscore_unregister_client);
+
+ /**
+ * verifies that source id is not taken by another client,
+@@ -1193,6 +1207,7 @@ int smsclient_sendrequest(struct smscore_client_t *client,
+
+ return coredev->sendrequest_handler(coredev->context, buffer, size);
+ }
++EXPORT_SYMBOL_GPL(smsclient_sendrequest);
+
+
+ int smscore_configure_gpio(struct smscore_device_t *coredev, u32 pin,
+@@ -1276,12 +1291,12 @@ static int __init smscore_module_init(void)
+ INIT_LIST_HEAD(&g_smscore_registry);
+ kmutex_init(&g_smscore_registrylock);
+
+- /* USB Register */
+- rc = smsusb_register();
+
+- /* DVB Register */
+- rc = smsdvb_register();
+
++
++
++
++ return rc;
+ sms_debug("rc %d", rc);
+
+ return rc;
+@@ -1290,6 +1305,10 @@ static int __init smscore_module_init(void)
+ static void __exit smscore_module_exit(void)
+ {
+
++
++
++
++
+ kmutex_lock(&g_smscore_deviceslock);
+ while (!list_empty(&g_smscore_notifyees)) {
+ struct smscore_device_notifyee_t *notifyee =
+@@ -1312,18 +1331,12 @@ static void __exit smscore_module_exit(void)
+ }
+ kmutex_unlock(&g_smscore_registrylock);
+
+- /* DVB UnRegister */
+- smsdvb_unregister();
+-
+- /* Unregister USB */
+- smsusb_unregister();
+-
+ sms_debug("");
+ }
+
+ module_init(smscore_module_init);
+ module_exit(smscore_module_exit);
+
+-MODULE_DESCRIPTION("Driver for the Siano SMS1XXX USB dongle");
+-MODULE_AUTHOR("Siano Mobile Silicon,,, (doronc@siano-ms.com)");
++MODULE_DESCRIPTION("Siano MDTV Core module");
++MODULE_AUTHOR("Siano Mobile Silicon, Inc. (uris@siano-ms.com)");
+ MODULE_LICENSE("GPL");
+diff --git a/drivers/media/dvb/siano/smscoreapi.h b/drivers/media/dvb/siano/smscoreapi.h
+index 760e233..548de90 100644
+--- a/drivers/media/dvb/siano/smscoreapi.h
++++ b/drivers/media/dvb/siano/smscoreapi.h
+@@ -29,13 +29,13 @@
+ #include <linux/scatterlist.h>
+ #include <linux/types.h>
+ #include <asm/page.h>
++#include <linux/mutex.h>
+
+ #include "dmxdev.h"
+ #include "dvbdev.h"
+ #include "dvb_demux.h"
+ #include "dvb_frontend.h"
+
+-#include <linux/mutex.h>
+
+ #define kmutex_init(_p_) mutex_init(_p_)
+ #define kmutex_lock(_p_) mutex_lock(_p_)
+@@ -369,27 +369,6 @@ struct smscore_gpio_config {
+ u8 outputdriving;
+ };
+
+-struct smsdvb_client_t {
+- struct list_head entry;
+-
+- struct smscore_device_t *coredev;
+- struct smscore_client_t *smsclient;
+-
+- struct dvb_adapter adapter;
+- struct dvb_demux demux;
+- struct dmxdev dmxdev;
+- struct dvb_frontend frontend;
+-
+- fe_status_t fe_status;
+- int fe_ber, fe_snr, fe_unc, fe_signal_strength;
+-
+- struct completion tune_done, stat_done;
+-
+- /* todo: save freq/band instead whole struct */
+- struct dvb_frontend_parameters fe_params;
+-
+-};
+-
+ extern void smscore_registry_setmode(char *devpath, int mode);
+ extern int smscore_registry_getmode(char *devpath);
+
+@@ -418,6 +397,13 @@ extern int smsclient_sendrequest(struct smscore_client_t *client,
+ extern void smscore_onresponse(struct smscore_device_t *coredev,
+ struct smscore_buffer_t *cb);
+
++extern int smscore_get_common_buffer_size(struct smscore_device_t *coredev);
++extern int smscore_map_common_buffer(struct smscore_device_t *coredev,
++ struct vm_area_struct *vma);
++extern int smscore_get_fw_filename(struct smscore_device_t *coredev,
++ int mode, char *filename);
++extern int smscore_send_fw_file(struct smscore_device_t *coredev,
++ u8 *ufwbuf, int size);
+
+ extern
+ struct smscore_buffer_t *smscore_getbuffer(struct smscore_device_t *coredev);
+@@ -433,18 +419,9 @@ int smscore_get_board_id(struct smscore_device_t *core);
+
+ int smscore_led_state(struct smscore_device_t *core, int led);
+
+-/* smsdvb.c */
+-int smsdvb_register(void);
+-void smsdvb_unregister(void);
+-
+-/* smsusb.c */
+-int smsusb_register(void);
+-void smsusb_unregister(void);
+
+ /* ------------------------------------------------------------------------ */
+
+-extern int sms_debug;
+-
+ #define DBG_INFO 1
+ #define DBG_ADV 2
+
+@@ -452,7 +429,7 @@ extern int sms_debug;
+ printk(kern "%s: " fmt "\n", __func__, ##arg)
+
+ #define dprintk(kern, lvl, fmt, arg...) do {\
+- if (sms_debug & lvl) \
++ if (sms_dbg & lvl) \
+ sms_printk(kern, fmt, ##arg); } while (0)
+
+ #define sms_log(fmt, arg...) sms_printk(KERN_INFO, fmt, ##arg)
+diff --git a/drivers/media/dvb/siano/smsdvb.c b/drivers/media/dvb/siano/smsdvb.c
+index 2da953a..ba080b9 100644
+--- a/drivers/media/dvb/siano/smsdvb.c
++++ b/drivers/media/dvb/siano/smsdvb.c
+@@ -1,7 +1,7 @@
+ /*
+ * Driver for the Siano SMS1xxx USB dongle
+ *
+- * author: Anatoly Greenblat
++ * Author: Uri Shkolni
+ *
+ * Copyright (c), 2005-2008 Siano Mobile Silicon, Inc.
+ *
+@@ -27,9 +27,33 @@
+
+ DVB_DEFINE_MOD_OPT_ADAPTER_NR(adapter_nr);
+
++struct smsdvb_client_t {
++ struct list_head entry;
++
++ struct smscore_device_t *coredev;
++ struct smscore_client_t *smsclient;
++
++ struct dvb_adapter adapter;
++ struct dvb_demux demux;
++ struct dmxdev dmxdev;
++ struct dvb_frontend frontend;
++
++ fe_status_t fe_status;
++ int fe_ber, fe_snr, fe_unc, fe_signal_strength;
++
++ struct completion tune_done, stat_done;
++
++ /* todo: save freq/band instead whole struct */
++ struct dvb_frontend_parameters fe_params;
++};
++
+ static struct list_head g_smsdvb_clients;
+ static struct mutex g_smsdvb_clientslock;
+
++static int sms_dbg;
++module_param_named(debug, sms_dbg, int, 0644);
++MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))");
++
+ static int smsdvb_onresponse(void *context, struct smscore_buffer_t *cb)
+ {
+ struct smsdvb_client_t *client = (struct smsdvb_client_t *) context;
+@@ -262,6 +286,7 @@ static int smsdvb_set_frontend(struct dvb_frontend *fe,
+ struct SmsMsgHdr_ST Msg;
+ u32 Data[3];
+ } Msg;
++ int ret;
+
+ Msg.Msg.msgSrcId = DVBT_BDA_CONTROL_MSG_ID;
+ Msg.Msg.msgDstId = HIF_TASK;
+@@ -282,6 +307,24 @@ static int smsdvb_set_frontend(struct dvb_frontend *fe,
+ default: return -EINVAL;
+ }
+
++ /* Disable LNA, if any. An error is returned if no LNA is present */
++ ret = sms_board_lna_control(client->coredev, 0);
++ if (ret == 0) {
++ fe_status_t status;
++
++ /* tune with LNA off at first */
++ ret = smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg),
++ &client->tune_done);
++
++ smsdvb_read_status(fe, &status);
++
++ if (status & FE_HAS_LOCK)
++ return ret;
++
++ /* previous tune didnt lock - enable LNA and tune again */
++ sms_board_lna_control(client->coredev, 1);
++ }
++
+ return smsdvb_sendrequest_and_wait(client, &Msg, sizeof(Msg),
+ &client->tune_done);
+ }
+@@ -329,7 +372,7 @@ static void smsdvb_release(struct dvb_frontend *fe)
+
+ static struct dvb_frontend_ops smsdvb_fe_ops = {
+ .info = {
+- .name = "Siano Mobile Digital SMS1xxx",
++ .name = "Siano Mobile Digital MDTV Receiver",
+ .type = FE_OFDM,
+ .frequency_min = 44250000,
+ .frequency_max = 867250000,
+@@ -371,7 +414,7 @@ static int smsdvb_hotplug(struct smscore_device_t *coredev,
+ if (!arrival)
+ return 0;
+
+- if (smscore_get_device_mode(coredev) != 4) {
++ if (smscore_get_device_mode(coredev) != DEVICE_MODE_DVBT_BDA) {
+ sms_err("SMS Device mode is not set for "
+ "DVB operation.");
+ return 0;
+@@ -473,7 +516,7 @@ adapter_error:
+ return rc;
+ }
+
+-int smsdvb_register(void)
++int smsdvb_module_init(void)
+ {
+ int rc;
+
+@@ -487,7 +530,7 @@ int smsdvb_register(void)
+ return rc;
+ }
+
+-void smsdvb_unregister(void)
++void smsdvb_module_exit(void)
+ {
+ smscore_unregister_hotplug(smsdvb_hotplug);
+
+@@ -499,3 +542,10 @@ void smsdvb_unregister(void)
+
+ kmutex_unlock(&g_smsdvb_clientslock);
+ }
++
++module_init(smsdvb_module_init);
++module_exit(smsdvb_module_exit);
++
++MODULE_DESCRIPTION("SMS DVB subsystem adaptation module");
++MODULE_AUTHOR("Siano Mobile Silicon, INC. (uris@siano-ms.com)");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/media/dvb/siano/smsusb.c b/drivers/media/dvb/siano/smsusb.c
+index 5d7ca34..71c65f5 100644
+--- a/drivers/media/dvb/siano/smsusb.c
++++ b/drivers/media/dvb/siano/smsusb.c
+@@ -27,6 +27,10 @@
+ #include "smscoreapi.h"
+ #include "sms-cards.h"
+
++static int sms_dbg;
++module_param_named(debug, sms_dbg, int, 0644);
++MODULE_PARM_DESC(debug, "set debug level (info=1, adv=2 (or-able))");
++
+ #define USB1_BUFFER_SIZE 0x1000
+ #define USB2_BUFFER_SIZE 0x4000
+
+@@ -424,6 +428,7 @@ static int smsusb_probe(struct usb_interface *intf,
+
+ rc = smsusb_init_device(intf, id->driver_info);
+ sms_info("rc %d", rc);
++ sms_board_load_modules(id->driver_info);
+ return rc;
+ }
+
+@@ -436,7 +441,7 @@ static int smsusb_suspend(struct usb_interface *intf, pm_message_t msg)
+ {
+ struct smsusb_device_t *dev =
+ (struct smsusb_device_t *)usb_get_intfdata(intf);
+- printk(KERN_INFO "%s Entering status %d.\n", __func__, msg.event);
++ printk(KERN_INFO "%s: Entering status %d.\n", __func__, msg.event);
+ smsusb_stop_streaming(dev);
+ return 0;
+ }
+@@ -448,7 +453,7 @@ static int smsusb_resume(struct usb_interface *intf)
+ (struct smsusb_device_t *)usb_get_intfdata(intf);
+ struct usb_device *udev = interface_to_usbdev(intf);
+
+- printk(KERN_INFO "%s Entering.\n", __func__);
++ printk(KERN_INFO "%s: Entering.\n", __func__);
+ usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x81));
+ usb_clear_halt(udev, usb_rcvbulkpipe(udev, 0x02));
+
+@@ -463,9 +468,8 @@ static int smsusb_resume(struct usb_interface *intf)
+ intf->cur_altsetting->desc.
+ bInterfaceNumber, 0);
+ if (rc < 0) {
+- printk(KERN_INFO
+- "%s usb_set_interface failed, rc %d\n",
+- __func__, rc);
++ printk(KERN_INFO "%s usb_set_interface failed, "
++ "rc %d\n", __func__, rc);
+ return rc;
+ }
+ }
+@@ -474,8 +478,55 @@ static int smsusb_resume(struct usb_interface *intf)
+ return 0;
+ }
+
++struct usb_device_id smsusb_id_table[] = {
++#ifdef CONFIG_DVB_SIANO_SMS1XXX_SMS_IDS
++ { USB_DEVICE(0x187f, 0x0010),
++ .driver_info = SMS1XXX_BOARD_SIANO_STELLAR },
++ { USB_DEVICE(0x187f, 0x0100),
++ .driver_info = SMS1XXX_BOARD_SIANO_STELLAR },
++ { USB_DEVICE(0x187f, 0x0200),
++ .driver_info = SMS1XXX_BOARD_SIANO_NOVA_A },
++ { USB_DEVICE(0x187f, 0x0201),
++ .driver_info = SMS1XXX_BOARD_SIANO_NOVA_B },
++ { USB_DEVICE(0x187f, 0x0300),
++ .driver_info = SMS1XXX_BOARD_SIANO_VEGA },
++#endif
++ { USB_DEVICE(0x2040, 0x1700),
++ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_CATAMOUNT },
++ { USB_DEVICE(0x2040, 0x1800),
++ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_A },
++ { USB_DEVICE(0x2040, 0x1801),
++ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_OKEMO_B },
++ { USB_DEVICE(0x2040, 0x2000),
++ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
++ { USB_DEVICE(0x2040, 0x2009),
++ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD_R2 },
++ { USB_DEVICE(0x2040, 0x200a),
++ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
++ { USB_DEVICE(0x2040, 0x2010),
++ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
++ { USB_DEVICE(0x2040, 0x2011),
++ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
++ { USB_DEVICE(0x2040, 0x2019),
++ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_TIGER_MINICARD },
++ { USB_DEVICE(0x2040, 0x5500),
++ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
++ { USB_DEVICE(0x2040, 0x5510),
++ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
++ { USB_DEVICE(0x2040, 0x5520),
++ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
++ { USB_DEVICE(0x2040, 0x5530),
++ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
++ { USB_DEVICE(0x2040, 0x5580),
++ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
++ { USB_DEVICE(0x2040, 0x5590),
++ .driver_info = SMS1XXX_BOARD_HAUPPAUGE_WINDHAM },
++ { } /* Terminating entry */
++};
++MODULE_DEVICE_TABLE(usb, smsusb_id_table);
++
+ static struct usb_driver smsusb_driver = {
+- .name = "sms1xxx",
++ .name = "smsusb",
+ .probe = smsusb_probe,
+ .disconnect = smsusb_disconnect,
+ .id_table = smsusb_id_table,
+@@ -484,7 +535,7 @@ static struct usb_driver smsusb_driver = {
+ .resume = smsusb_resume,
+ };
+
+-int smsusb_register(void)
++int smsusb_module_init(void)
+ {
+ int rc = usb_register(&smsusb_driver);
+ if (rc)
+@@ -495,10 +546,16 @@ int smsusb_register(void)
+ return rc;
+ }
+
+-void smsusb_unregister(void)
++void smsusb_module_exit(void)
+ {
+ sms_debug("");
+ /* Regular USB Cleanup */
+ usb_deregister(&smsusb_driver);
+ }
+
++module_init(smsusb_module_init);
++module_exit(smsusb_module_exit);
++
++MODULE_DESCRIPTION("Driver for the Siano SMS1XXX USB dongle");
++MODULE_AUTHOR("Siano Mobile Silicon, INC. (uris@siano-ms.com)");
++MODULE_LICENSE("GPL");
+diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig
+index ab0bcd2..7729904 100644
+--- a/drivers/media/dvb/ttpci/Kconfig
++++ b/drivers/media/dvb/ttpci/Kconfig
+@@ -108,7 +108,7 @@ config DVB_BUDGET_CI
+ select DVB_STB6100 if !DVB_FE_CUSTOMISE
+ select DVB_LNBP21 if !DVB_FE_CUSTOMISE
+ select DVB_TDA10023 if !DVB_FE_CUSTOMISE
+- select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMIZE
++ select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE
+ select VIDEO_IR
+ help
+ Support for simple SAA7146 based DVB cards
+diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c
+index aa1ff52..4624cee 100644
+--- a/drivers/media/dvb/ttpci/av7110.c
++++ b/drivers/media/dvb/ttpci/av7110.c
+@@ -725,7 +725,7 @@ static int dvb_osd_ioctl(struct inode *inode, struct file *file,
+ }
+
+
+-static struct file_operations dvb_osd_fops = {
++static const struct file_operations dvb_osd_fops = {
+ .owner = THIS_MODULE,
+ .ioctl = dvb_generic_ioctl,
+ .open = dvb_generic_open,
+diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c
+index bdc62ac..e4d0900 100644
+--- a/drivers/media/dvb/ttpci/av7110_av.c
++++ b/drivers/media/dvb/ttpci/av7110_av.c
+@@ -1456,7 +1456,7 @@ static int dvb_audio_release(struct inode *inode, struct file *file)
+ * driver registration
+ ******************************************************************************/
+
+-static struct file_operations dvb_video_fops = {
++static const struct file_operations dvb_video_fops = {
+ .owner = THIS_MODULE,
+ .write = dvb_video_write,
+ .ioctl = dvb_generic_ioctl,
+@@ -1474,7 +1474,7 @@ static struct dvb_device dvbdev_video = {
+ .kernel_ioctl = dvb_video_ioctl,
+ };
+
+-static struct file_operations dvb_audio_fops = {
++static const struct file_operations dvb_audio_fops = {
+ .owner = THIS_MODULE,
+ .write = dvb_audio_write,
+ .ioctl = dvb_generic_ioctl,
+diff --git a/drivers/media/dvb/ttpci/av7110_ca.c b/drivers/media/dvb/ttpci/av7110_ca.c
+index 261135d..c7a65b1 100644
+--- a/drivers/media/dvb/ttpci/av7110_ca.c
++++ b/drivers/media/dvb/ttpci/av7110_ca.c
+@@ -345,7 +345,7 @@ static ssize_t dvb_ca_read(struct file *file, char __user *buf,
+ return ci_ll_read(&av7110->ci_rbuffer, file, buf, count, ppos);
+ }
+
+-static struct file_operations dvb_ca_fops = {
++static const struct file_operations dvb_ca_fops = {
+ .owner = THIS_MODULE,
+ .read = dvb_ca_read,
+ .write = dvb_ca_write,
+diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c
+index c5b9c70..2210cff 100644
+--- a/drivers/media/dvb/ttpci/av7110_v4l.c
++++ b/drivers/media/dvb/ttpci/av7110_v4l.c
+@@ -316,253 +316,261 @@ static int av7110_dvb_c_switch(struct saa7146_fh *fh)
+ return 0;
+ }
+
+-static long av7110_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
++static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
+ {
+- struct saa7146_dev *dev = fh->dev;
+- struct av7110 *av7110 = (struct av7110*) dev->ext_priv;
+- dprintk(4, "saa7146_dev: %p\n", dev);
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
++ u16 stereo_det;
++ s8 stereo;
+
+- switch (cmd) {
+- case VIDIOC_G_TUNER:
+- {
+- struct v4l2_tuner *t = arg;
+- u16 stereo_det;
+- s8 stereo;
++ dprintk(2, "VIDIOC_G_TUNER: %d\n", t->index);
+
+- dprintk(2, "VIDIOC_G_TUNER: %d\n", t->index);
++ if (!av7110->analog_tuner_flags || t->index != 0)
++ return -EINVAL;
+
+- if (!av7110->analog_tuner_flags || t->index != 0)
+- return -EINVAL;
++ memset(t, 0, sizeof(*t));
++ strcpy((char *)t->name, "Television");
++
++ t->type = V4L2_TUNER_ANALOG_TV;
++ t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
++ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
++ t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */
++ t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */
++ /* FIXME: add the real signal strength here */
++ t->signal = 0xffff;
++ t->afc = 0;
++
++ /* FIXME: standard / stereo detection is still broken */
++ msp_readreg(av7110, MSP_RD_DEM, 0x007e, &stereo_det);
++ dprintk(1, "VIDIOC_G_TUNER: msp3400 TV standard detection: 0x%04x\n", stereo_det);
++ msp_readreg(av7110, MSP_RD_DSP, 0x0018, &stereo_det);
++ dprintk(1, "VIDIOC_G_TUNER: msp3400 stereo detection: 0x%04x\n", stereo_det);
++ stereo = (s8)(stereo_det >> 8);
++ if (stereo > 0x10) {
++ /* stereo */
++ t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
++ t->audmode = V4L2_TUNER_MODE_STEREO;
++ } else if (stereo < -0x10) {
++ /* bilingual */
++ t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
++ t->audmode = V4L2_TUNER_MODE_LANG1;
++ } else /* mono */
++ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+
+- memset(t, 0, sizeof(*t));
+- strcpy((char *)t->name, "Television");
+-
+- t->type = V4L2_TUNER_ANALOG_TV;
+- t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
+- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+- t->rangelow = 772; /* 48.25 MHZ / 62.5 kHz = 772, see fi1216mk2-specs, page 2 */
+- t->rangehigh = 13684; /* 855.25 MHz / 62.5 kHz = 13684 */
+- /* FIXME: add the real signal strength here */
+- t->signal = 0xffff;
+- t->afc = 0;
+-
+- // FIXME: standard / stereo detection is still broken
+- msp_readreg(av7110, MSP_RD_DEM, 0x007e, &stereo_det);
+- dprintk(1, "VIDIOC_G_TUNER: msp3400 TV standard detection: 0x%04x\n", stereo_det);
+- msp_readreg(av7110, MSP_RD_DSP, 0x0018, &stereo_det);
+- dprintk(1, "VIDIOC_G_TUNER: msp3400 stereo detection: 0x%04x\n", stereo_det);
+- stereo = (s8)(stereo_det >> 8);
+- if (stereo > 0x10) {
+- /* stereo */
+- t->rxsubchans = V4L2_TUNER_SUB_STEREO | V4L2_TUNER_SUB_MONO;
+- t->audmode = V4L2_TUNER_MODE_STEREO;
+- }
+- else if (stereo < -0x10) {
+- /* bilingual */
+- t->rxsubchans = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+- t->audmode = V4L2_TUNER_MODE_LANG1;
+- }
+- else /* mono */
+- t->rxsubchans = V4L2_TUNER_SUB_MONO;
++ return 0;
++}
+
+- return 0;
+- }
+- case VIDIOC_S_TUNER:
+- {
+- struct v4l2_tuner *t = arg;
+- u16 fm_matrix, src;
+- dprintk(2, "VIDIOC_S_TUNER: %d\n", t->index);
++static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
++ u16 fm_matrix, src;
++ dprintk(2, "VIDIOC_S_TUNER: %d\n", t->index);
+
+- if (!av7110->analog_tuner_flags || av7110->current_input != 1)
+- return -EINVAL;
++ if (!av7110->analog_tuner_flags || av7110->current_input != 1)
++ return -EINVAL;
+
+- switch (t->audmode) {
+- case V4L2_TUNER_MODE_STEREO:
+- dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n");
+- fm_matrix = 0x3001; // stereo
+- src = 0x0020;
+- break;
+- case V4L2_TUNER_MODE_LANG1_LANG2:
+- dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n");
+- fm_matrix = 0x3000; // bilingual
+- src = 0x0020;
+- break;
+- case V4L2_TUNER_MODE_LANG1:
+- dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n");
+- fm_matrix = 0x3000; // mono
+- src = 0x0000;
+- break;
+- case V4L2_TUNER_MODE_LANG2:
+- dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n");
+- fm_matrix = 0x3000; // mono
+- src = 0x0010;
+- break;
+- default: /* case V4L2_TUNER_MODE_MONO: */
+- dprintk(2, "VIDIOC_S_TUNER: TDA9840_SET_MONO\n");
+- fm_matrix = 0x3000; // mono
+- src = 0x0030;
+- break;
+- }
+- msp_writereg(av7110, MSP_WR_DSP, 0x000e, fm_matrix);
+- msp_writereg(av7110, MSP_WR_DSP, 0x0008, src);
+- msp_writereg(av7110, MSP_WR_DSP, 0x0009, src);
+- msp_writereg(av7110, MSP_WR_DSP, 0x000a, src);
+- return 0;
++ switch (t->audmode) {
++ case V4L2_TUNER_MODE_STEREO:
++ dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_STEREO\n");
++ fm_matrix = 0x3001; /* stereo */
++ src = 0x0020;
++ break;
++ case V4L2_TUNER_MODE_LANG1_LANG2:
++ dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1_LANG2\n");
++ fm_matrix = 0x3000; /* bilingual */
++ src = 0x0020;
++ break;
++ case V4L2_TUNER_MODE_LANG1:
++ dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG1\n");
++ fm_matrix = 0x3000; /* mono */
++ src = 0x0000;
++ break;
++ case V4L2_TUNER_MODE_LANG2:
++ dprintk(2, "VIDIOC_S_TUNER: V4L2_TUNER_MODE_LANG2\n");
++ fm_matrix = 0x3000; /* mono */
++ src = 0x0010;
++ break;
++ default: /* case V4L2_TUNER_MODE_MONO: */
++ dprintk(2, "VIDIOC_S_TUNER: TDA9840_SET_MONO\n");
++ fm_matrix = 0x3000; /* mono */
++ src = 0x0030;
++ break;
+ }
+- case VIDIOC_G_FREQUENCY:
+- {
+- struct v4l2_frequency *f = arg;
++ msp_writereg(av7110, MSP_WR_DSP, 0x000e, fm_matrix);
++ msp_writereg(av7110, MSP_WR_DSP, 0x0008, src);
++ msp_writereg(av7110, MSP_WR_DSP, 0x0009, src);
++ msp_writereg(av7110, MSP_WR_DSP, 0x000a, src);
++ return 0;
++}
+
+- dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x.\n", f->frequency);
++static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+- if (!av7110->analog_tuner_flags || av7110->current_input != 1)
+- return -EINVAL;
++ dprintk(2, "VIDIOC_G_FREQ: freq:0x%08x.\n", f->frequency);
+
+- memset(f, 0, sizeof(*f));
+- f->type = V4L2_TUNER_ANALOG_TV;
+- f->frequency = av7110->current_freq;
+- return 0;
+- }
+- case VIDIOC_S_FREQUENCY:
+- {
+- struct v4l2_frequency *f = arg;
++ if (!av7110->analog_tuner_flags || av7110->current_input != 1)
++ return -EINVAL;
+
+- dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x.\n", f->frequency);
++ memset(f, 0, sizeof(*f));
++ f->type = V4L2_TUNER_ANALOG_TV;
++ f->frequency = av7110->current_freq;
++ return 0;
++}
+
+- if (!av7110->analog_tuner_flags || av7110->current_input != 1)
+- return -EINVAL;
++static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+- if (V4L2_TUNER_ANALOG_TV != f->type)
+- return -EINVAL;
++ dprintk(2, "VIDIOC_S_FREQUENCY: freq:0x%08x.\n", f->frequency);
+
+- msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0xffe0); // fast mute
+- msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0xffe0);
++ if (!av7110->analog_tuner_flags || av7110->current_input != 1)
++ return -EINVAL;
+
+- /* tune in desired frequency */
+- if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820) {
+- ves1820_set_tv_freq(dev, f->frequency);
+- } else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297) {
+- stv0297_set_tv_freq(dev, f->frequency);
+- }
+- av7110->current_freq = f->frequency;
++ if (V4L2_TUNER_ANALOG_TV != f->type)
++ return -EINVAL;
+
+- msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x003f); // start stereo detection
+- msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x0000);
+- msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); // loudspeaker + headphone
+- msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); // SCART 1 volume
+- return 0;
+- }
+- case VIDIOC_ENUMINPUT:
+- {
+- struct v4l2_input *i = arg;
++ msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0xffe0); /* fast mute */
++ msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0xffe0);
+
+- dprintk(2, "VIDIOC_ENUMINPUT: %d\n", i->index);
++ /* tune in desired frequency */
++ if (av7110->analog_tuner_flags & ANALOG_TUNER_VES1820)
++ ves1820_set_tv_freq(dev, f->frequency);
++ else if (av7110->analog_tuner_flags & ANALOG_TUNER_STV0297)
++ stv0297_set_tv_freq(dev, f->frequency);
++ av7110->current_freq = f->frequency;
+
+- if (av7110->analog_tuner_flags) {
+- if (i->index < 0 || i->index >= 4)
+- return -EINVAL;
+- } else {
+- if (i->index != 0)
+- return -EINVAL;
+- }
++ msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x003f); /* start stereo detection */
++ msp_writereg(av7110, MSP_WR_DSP, 0x0015, 0x0000);
++ msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x4f00); /* loudspeaker + headphone */
++ msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x4f00); /* SCART 1 volume */
++ return 0;
++}
+
+- memcpy(i, &inputs[i->index], sizeof(struct v4l2_input));
++static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
+
+- return 0;
++ dprintk(2, "VIDIOC_ENUMINPUT: %d\n", i->index);
++
++ if (av7110->analog_tuner_flags) {
++ if (i->index < 0 || i->index >= 4)
++ return -EINVAL;
++ } else {
++ if (i->index != 0)
++ return -EINVAL;
+ }
+- case VIDIOC_G_INPUT:
+- {
+- int *input = (int *)arg;
+- *input = av7110->current_input;
+- dprintk(2, "VIDIOC_G_INPUT: %d\n", *input);
++
++ memcpy(i, &inputs[i->index], sizeof(struct v4l2_input));
++
++ return 0;
++}
++
++static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
++
++ *input = av7110->current_input;
++ dprintk(2, "VIDIOC_G_INPUT: %d\n", *input);
++ return 0;
++}
++
++static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
++
++ dprintk(2, "VIDIOC_S_INPUT: %d\n", input);
++
++ if (!av7110->analog_tuner_flags)
+ return 0;
+- }
+- case VIDIOC_S_INPUT:
+- {
+- int input = *(int *)arg;
+
+- dprintk(2, "VIDIOC_S_INPUT: %d\n", input);
++ if (input < 0 || input >= 4)
++ return -EINVAL;
+
+- if (!av7110->analog_tuner_flags)
+- return 0;
++ av7110->current_input = input;
++ return av7110_dvb_c_switch(fh);
++}
+
+- if (input < 0 || input >= 4)
+- return -EINVAL;
++static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
++{
++ dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index);
++ if (a->index != 0)
++ return -EINVAL;
++ memcpy(a, &msp3400_v4l2_audio, sizeof(struct v4l2_audio));
++ return 0;
++}
+
+- av7110->current_input = input;
+- return av7110_dvb_c_switch(fh);
+- }
+- case VIDIOC_G_AUDIO:
+- {
+- struct v4l2_audio *a = arg;
++static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
++{
++ dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index);
++ return 0;
++}
+
+- dprintk(2, "VIDIOC_G_AUDIO: %d\n", a->index);
+- if (a->index != 0)
+- return -EINVAL;
+- memcpy(a, &msp3400_v4l2_audio, sizeof(struct v4l2_audio));
+- break;
+- }
+- case VIDIOC_S_AUDIO:
+- {
+- struct v4l2_audio *a = arg;
+- dprintk(2, "VIDIOC_S_AUDIO: %d\n", a->index);
+- break;
+- }
+- case VIDIOC_G_SLICED_VBI_CAP:
+- {
+- struct v4l2_sliced_vbi_cap *cap = arg;
+- dprintk(2, "VIDIOC_G_SLICED_VBI_CAP\n");
+- memset(cap, 0, sizeof *cap);
+- if (FW_VERSION(av7110->arm_app) >= 0x2623) {
+- cap->service_set = V4L2_SLICED_WSS_625;
+- cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
+- }
+- break;
+- }
+- case VIDIOC_G_FMT:
+- {
+- struct v4l2_format *f = arg;
+- dprintk(2, "VIDIOC_G_FMT:\n");
+- if (f->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT ||
+- FW_VERSION(av7110->arm_app) < 0x2623)
+- return -EAGAIN; /* handled by core driver */
+- memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced);
+- if (av7110->wssMode) {
+- f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
+- f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
+- f->fmt.sliced.io_size = sizeof (struct v4l2_sliced_vbi_data);
+- }
+- break;
++static int vidioc_g_sliced_vbi_cap(struct file *file, void *fh,
++ struct v4l2_sliced_vbi_cap *cap)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
++
++ dprintk(2, "VIDIOC_G_SLICED_VBI_CAP\n");
++ if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT)
++ return -EINVAL;
++ if (FW_VERSION(av7110->arm_app) >= 0x2623) {
++ cap->service_set = V4L2_SLICED_WSS_625;
++ cap->service_lines[0][23] = V4L2_SLICED_WSS_625;
+ }
+- case VIDIOC_S_FMT:
+- {
+- struct v4l2_format *f = arg;
+- dprintk(2, "VIDIOC_S_FMT\n");
+- if (f->type != V4L2_BUF_TYPE_SLICED_VBI_OUTPUT ||
+- FW_VERSION(av7110->arm_app) < 0x2623)
+- return -EAGAIN; /* handled by core driver */
+- if (f->fmt.sliced.service_set != V4L2_SLICED_WSS_625 &&
+- f->fmt.sliced.service_lines[0][23] != V4L2_SLICED_WSS_625) {
+- memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced);
+- /* WSS controlled by firmware */
+- av7110->wssMode = 0;
+- av7110->wssData = 0;
+- return av7110_fw_cmd(av7110, COMTYPE_ENCODER,
+- SetWSSConfig, 1, 0);
+- } else {
+- memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced);
+- f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
+- f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
+- f->fmt.sliced.io_size = sizeof (struct v4l2_sliced_vbi_data);
+- /* WSS controlled by userspace */
+- av7110->wssMode = 1;
+- av7110->wssData = 0;
+- }
+- break;
++ return 0;
++}
++
++static int vidioc_g_fmt_sliced_vbi_out(struct file *file, void *fh,
++ struct v4l2_format *f)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
++
++ dprintk(2, "VIDIOC_G_FMT:\n");
++ if (FW_VERSION(av7110->arm_app) < 0x2623)
++ return -EINVAL;
++ memset(&f->fmt.sliced, 0, sizeof f->fmt.sliced);
++ if (av7110->wssMode) {
++ f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
++ f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
++ f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data);
+ }
+- default:
+- printk("no such ioctl\n");
+- return -ENOIOCTLCMD;
++ return 0;
++}
++
++static int vidioc_s_fmt_sliced_vbi_out(struct file *file, void *fh,
++ struct v4l2_format *f)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct av7110 *av7110 = (struct av7110 *)dev->ext_priv;
++
++ dprintk(2, "VIDIOC_S_FMT\n");
++ if (FW_VERSION(av7110->arm_app) < 0x2623)
++ return -EINVAL;
++ if (f->fmt.sliced.service_set != V4L2_SLICED_WSS_625 &&
++ f->fmt.sliced.service_lines[0][23] != V4L2_SLICED_WSS_625) {
++ memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced));
++ /* WSS controlled by firmware */
++ av7110->wssMode = 0;
++ av7110->wssData = 0;
++ return av7110_fw_cmd(av7110, COMTYPE_ENCODER,
++ SetWSSConfig, 1, 0);
++ } else {
++ memset(&f->fmt.sliced, 0, sizeof(f->fmt.sliced));
++ f->fmt.sliced.service_set = V4L2_SLICED_WSS_625;
++ f->fmt.sliced.service_lines[0][23] = V4L2_SLICED_WSS_625;
++ f->fmt.sliced.io_size = sizeof(struct v4l2_sliced_vbi_data);
++ /* WSS controlled by userspace */
++ av7110->wssMode = 1;
++ av7110->wssData = 0;
+ }
+ return 0;
+ }
+@@ -609,22 +617,6 @@ static ssize_t av7110_vbi_write(struct file *file, const char __user *data, size
+ * INITIALIZATION
+ ****************************************************************************/
+
+-static struct saa7146_extension_ioctls ioctls[] = {
+- { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE },
+- { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE },
+- { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE },
+- { VIDIOC_G_FREQUENCY, SAA7146_EXCLUSIVE },
+- { VIDIOC_S_FREQUENCY, SAA7146_EXCLUSIVE },
+- { VIDIOC_G_TUNER, SAA7146_EXCLUSIVE },
+- { VIDIOC_S_TUNER, SAA7146_EXCLUSIVE },
+- { VIDIOC_G_AUDIO, SAA7146_EXCLUSIVE },
+- { VIDIOC_S_AUDIO, SAA7146_EXCLUSIVE },
+- { VIDIOC_G_SLICED_VBI_CAP, SAA7146_EXCLUSIVE },
+- { VIDIOC_G_FMT, SAA7146_BEFORE },
+- { VIDIOC_S_FMT, SAA7146_BEFORE },
+- { 0, 0 }
+-};
+-
+ static u8 saa7113_init_regs[] = {
+ 0x02, 0xd0,
+ 0x03, 0x23,
+@@ -788,20 +780,34 @@ int av7110_init_analog_module(struct av7110 *av7110)
+ int av7110_init_v4l(struct av7110 *av7110)
+ {
+ struct saa7146_dev* dev = av7110->dev;
++ struct saa7146_ext_vv *vv_data;
+ int ret;
+
+ /* special case DVB-C: these cards have an analog tuner
+ plus need some special handling, so we have separate
+ saa7146_ext_vv data for these... */
+ if (av7110->analog_tuner_flags)
+- ret = saa7146_vv_init(dev, &av7110_vv_data_c);
++ vv_data = &av7110_vv_data_c;
+ else
+- ret = saa7146_vv_init(dev, &av7110_vv_data_st);
++ vv_data = &av7110_vv_data_st;
++ ret = saa7146_vv_init(dev, vv_data);
+
+ if (ret) {
+ ERR(("cannot init capture device. skipping.\n"));
+ return -ENODEV;
+ }
++ vv_data->ops.vidioc_enum_input = vidioc_enum_input;
++ vv_data->ops.vidioc_g_input = vidioc_g_input;
++ vv_data->ops.vidioc_s_input = vidioc_s_input;
++ vv_data->ops.vidioc_g_tuner = vidioc_g_tuner;
++ vv_data->ops.vidioc_s_tuner = vidioc_s_tuner;
++ vv_data->ops.vidioc_g_frequency = vidioc_g_frequency;
++ vv_data->ops.vidioc_s_frequency = vidioc_s_frequency;
++ vv_data->ops.vidioc_g_audio = vidioc_g_audio;
++ vv_data->ops.vidioc_s_audio = vidioc_s_audio;
++ vv_data->ops.vidioc_g_sliced_vbi_cap = vidioc_g_sliced_vbi_cap;
++ vv_data->ops.vidioc_g_fmt_sliced_vbi_out = vidioc_g_fmt_sliced_vbi_out;
++ vv_data->ops.vidioc_s_fmt_sliced_vbi_out = vidioc_s_fmt_sliced_vbi_out;
+
+ if (saa7146_register_device(&av7110->v4l_dev, dev, "av7110", VFL_TYPE_GRABBER)) {
+ ERR(("cannot register capture device. skipping.\n"));
+@@ -900,9 +906,6 @@ static struct saa7146_ext_vv av7110_vv_data_st = {
+ .num_stds = ARRAY_SIZE(standard),
+ .std_callback = &std_callback,
+
+- .ioctls = &ioctls[0],
+- .ioctl = av7110_ioctl,
+-
+ .vbi_fops.open = av7110_vbi_reset,
+ .vbi_fops.release = av7110_vbi_reset,
+ .vbi_fops.write = av7110_vbi_write,
+@@ -918,9 +921,6 @@ static struct saa7146_ext_vv av7110_vv_data_c = {
+ .num_stds = ARRAY_SIZE(standard),
+ .std_callback = &std_callback,
+
+- .ioctls = &ioctls[0],
+- .ioctl = av7110_ioctl,
+-
+ .vbi_fops.open = av7110_vbi_reset,
+ .vbi_fops.release = av7110_vbi_reset,
+ .vbi_fops.write = av7110_vbi_write,
+diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c
+index 4182121..855fe74 100644
+--- a/drivers/media/dvb/ttpci/budget-av.c
++++ b/drivers/media/dvb/ttpci/budget-av.c
+@@ -1404,6 +1404,41 @@ static int budget_av_detach(struct saa7146_dev *dev)
+ return err;
+ }
+
++#define KNC1_INPUTS 2
++static struct v4l2_input knc1_inputs[KNC1_INPUTS] = {
++ {0, "Composite", V4L2_INPUT_TYPE_TUNER, 1, 0, V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0},
++ {1, "S-Video", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0},
++};
++
++static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
++{
++ dprintk(1, "VIDIOC_ENUMINPUT %d.\n", i->index);
++ if (i->index < 0 || i->index >= KNC1_INPUTS)
++ return -EINVAL;
++ memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input));
++ return 0;
++}
++
++static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct budget_av *budget_av = (struct budget_av *)dev->ext_priv;
++
++ *i = budget_av->cur_input;
++
++ dprintk(1, "VIDIOC_G_INPUT %d.\n", *i);
++ return 0;
++}
++
++static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct budget_av *budget_av = (struct budget_av *)dev->ext_priv;
++
++ dprintk(1, "VIDIOC_S_INPUT %d.\n", input);
++ return saa7113_setinput(budget_av, input);
++}
++
+ static struct saa7146_ext_vv vv_data;
+
+ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
+@@ -1442,6 +1477,9 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
+ ERR(("cannot init vv subsystem.\n"));
+ return err;
+ }
++ vv_data.ops.vidioc_enum_input = vidioc_enum_input;
++ vv_data.ops.vidioc_g_input = vidioc_g_input;
++ vv_data.ops.vidioc_s_input = vidioc_s_input;
+
+ if ((err = saa7146_register_device(&budget_av->vd, dev, "knc1", VFL_TYPE_GRABBER))) {
+ /* fixme: proper cleanup here */
+@@ -1480,54 +1518,6 @@ static int budget_av_attach(struct saa7146_dev *dev, struct saa7146_pci_extensio
+ return 0;
+ }
+
+-#define KNC1_INPUTS 2
+-static struct v4l2_input knc1_inputs[KNC1_INPUTS] = {
+- {0, "Composite", V4L2_INPUT_TYPE_TUNER, 1, 0, V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0},
+- {1, "S-Video", V4L2_INPUT_TYPE_CAMERA, 2, 0, V4L2_STD_PAL_BG | V4L2_STD_NTSC_M, 0},
+-};
+-
+-static struct saa7146_extension_ioctls ioctls[] = {
+- {VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE},
+- {VIDIOC_G_INPUT, SAA7146_EXCLUSIVE},
+- {VIDIOC_S_INPUT, SAA7146_EXCLUSIVE},
+- {0, 0}
+-};
+-
+-static long av_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
+-{
+- struct saa7146_dev *dev = fh->dev;
+- struct budget_av *budget_av = (struct budget_av *) dev->ext_priv;
+-
+- switch (cmd) {
+- case VIDIOC_ENUMINPUT:{
+- struct v4l2_input *i = arg;
+-
+- dprintk(1, "VIDIOC_ENUMINPUT %d.\n", i->index);
+- if (i->index < 0 || i->index >= KNC1_INPUTS) {
+- return -EINVAL;
+- }
+- memcpy(i, &knc1_inputs[i->index], sizeof(struct v4l2_input));
+- return 0;
+- }
+- case VIDIOC_G_INPUT:{
+- int *input = (int *) arg;
+-
+- *input = budget_av->cur_input;
+-
+- dprintk(1, "VIDIOC_G_INPUT %d.\n", *input);
+- return 0;
+- }
+- case VIDIOC_S_INPUT:{
+- int input = *(int *) arg;
+- dprintk(1, "VIDIOC_S_INPUT %d.\n", input);
+- return saa7113_setinput(budget_av, input);
+- }
+- default:
+- return -ENOIOCTLCMD;
+- }
+- return 0;
+-}
+-
+ static struct saa7146_standard standard[] = {
+ {.name = "PAL",.id = V4L2_STD_PAL,
+ .v_offset = 0x17,.v_field = 288,
+@@ -1546,8 +1536,6 @@ static struct saa7146_ext_vv vv_data = {
+ .flags = 0,
+ .stds = &standard[0],
+ .num_stds = ARRAY_SIZE(standard),
+- .ioctls = &ioctls[0],
+- .ioctl = av_ioctl,
+ };
+
+ static struct saa7146_extension budget_extension;
+diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c
+index bcbc5d4..371a716 100644
+--- a/drivers/media/dvb/ttpci/budget-ci.c
++++ b/drivers/media/dvb/ttpci/budget-ci.c
+@@ -1076,6 +1076,10 @@ static struct tda10023_config tda10023_config = {
+ .deltaf = 0xa511,
+ };
+
++static struct tda827x_config tda827x_config = {
++ .config = 0,
++};
++
+ /* TT S2-3200 DVB-S (STB0899) Inittab */
+ static const struct stb0899_s1_reg tt3200_stb0899_s1_init_1[] = {
+
+@@ -1414,7 +1418,7 @@ static void frontend_init(struct budget_ci *budget_ci)
+ case 0x101a: /* TT Budget-C-1501 (philips tda10023/philips tda8274A) */
+ budget_ci->budget.dvb_frontend = dvb_attach(tda10023_attach, &tda10023_config, &budget_ci->budget.i2c_adap, 0x48);
+ if (budget_ci->budget.dvb_frontend) {
+- if (dvb_attach(tda827x_attach, budget_ci->budget.dvb_frontend, 0x61, &budget_ci->budget.i2c_adap, NULL) == NULL) {
++ if (dvb_attach(tda827x_attach, budget_ci->budget.dvb_frontend, 0x61, &budget_ci->budget.i2c_adap, &tda827x_config) == NULL) {
+ printk(KERN_ERR "%s: No tda827x found!\n", __func__);
+ dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+ budget_ci->budget.dvb_frontend = NULL;
+diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c
+index 2014ebc..cc54ed4 100644
+--- a/drivers/media/radio/dsbr100.c
++++ b/drivers/media/radio/dsbr100.c
+@@ -390,9 +390,11 @@ static void usb_dsbr100_disconnect(struct usb_interface *intf)
+ static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+ {
++ struct dsbr100_device *radio = video_drvdata(file);
++
+ strlcpy(v->driver, "dsbr100", sizeof(v->driver));
+ strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof(v->card));
+- sprintf(v->bus_info, "USB");
++ usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER;
+ return 0;
+@@ -450,7 +452,10 @@ static int vidioc_s_frequency(struct file *file, void *priv,
+ if (radio->removed)
+ return -EIO;
+
++ mutex_lock(&radio->lock);
+ radio->curfreq = f->frequency;
++ mutex_unlock(&radio->lock);
++
+ retval = dsbr100_setfreq(radio, radio->curfreq);
+ if (retval < 0)
+ dev_warn(&radio->usbdev->dev, "Set frequency failed\n");
+@@ -601,7 +606,10 @@ static int usb_dsbr100_close(struct file *file)
+ if (!radio)
+ return -ENODEV;
+
++ mutex_lock(&radio->lock);
+ radio->users = 0;
++ mutex_unlock(&radio->lock);
++
+ if (!radio->removed) {
+ retval = dsbr100_stop(radio);
+ if (retval < 0) {
+diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c
+index bfa13b8..ac82e33 100644
+--- a/drivers/media/radio/radio-aimslab.c
++++ b/drivers/media/radio/radio-aimslab.c
+@@ -32,14 +32,15 @@
+ #include <linux/init.h> /* Initdata */
+ #include <linux/ioport.h> /* request_region */
+ #include <linux/delay.h> /* udelay */
+-#include <asm/io.h> /* outb, outb_p */
+-#include <asm/uaccess.h> /* copy to/from user */
+ #include <linux/videodev2.h> /* kernel radio structs */
+-#include <media/v4l2-common.h>
++#include <linux/version.h> /* for KERNEL_VERSION MACRO */
++#include <linux/io.h> /* outb, outb_p */
++#include <media/v4l2-device.h>
+ #include <media/v4l2-ioctl.h>
+
+-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+-#define RADIO_VERSION KERNEL_VERSION(0,0,2)
++MODULE_AUTHOR("M.Kirkwood");
++MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card.");
++MODULE_LICENSE("GPL");
+
+ #ifndef CONFIG_RADIO_RTRACK_PORT
+ #define CONFIG_RADIO_RTRACK_PORT -1
+@@ -47,86 +48,95 @@
+
+ static int io = CONFIG_RADIO_RTRACK_PORT;
+ static int radio_nr = -1;
+-static struct mutex lock;
+
+-struct rt_device
++module_param(io, int, 0);
++MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)");
++module_param(radio_nr, int, 0);
++
++#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
++
++struct rtrack
+ {
+- unsigned long in_use;
++ struct v4l2_device v4l2_dev;
++ struct video_device vdev;
+ int port;
+ int curvol;
+ unsigned long curfreq;
+ int muted;
++ int io;
++ struct mutex lock;
+ };
+
++static struct rtrack rtrack_card;
+
+ /* local things */
+
+ static void sleep_delay(long n)
+ {
+ /* Sleep nicely for 'n' uS */
+- int d=n/msecs_to_jiffies(1000);
+- if(!d)
++ int d = n / msecs_to_jiffies(1000);
++ if (!d)
+ udelay(n);
+ else
+ msleep(jiffies_to_msecs(d));
+ }
+
+-static void rt_decvol(void)
++static void rt_decvol(struct rtrack *rt)
+ {
+- outb(0x58, io); /* volume down + sigstr + on */
++ outb(0x58, rt->io); /* volume down + sigstr + on */
+ sleep_delay(100000);
+- outb(0xd8, io); /* volume steady + sigstr + on */
++ outb(0xd8, rt->io); /* volume steady + sigstr + on */
+ }
+
+-static void rt_incvol(void)
++static void rt_incvol(struct rtrack *rt)
+ {
+- outb(0x98, io); /* volume up + sigstr + on */
++ outb(0x98, rt->io); /* volume up + sigstr + on */
+ sleep_delay(100000);
+- outb(0xd8, io); /* volume steady + sigstr + on */
++ outb(0xd8, rt->io); /* volume steady + sigstr + on */
+ }
+
+-static void rt_mute(struct rt_device *dev)
++static void rt_mute(struct rtrack *rt)
+ {
+- dev->muted = 1;
+- mutex_lock(&lock);
+- outb(0xd0, io); /* volume steady, off */
+- mutex_unlock(&lock);
++ rt->muted = 1;
++ mutex_lock(&rt->lock);
++ outb(0xd0, rt->io); /* volume steady, off */
++ mutex_unlock(&rt->lock);
+ }
+
+-static int rt_setvol(struct rt_device *dev, int vol)
++static int rt_setvol(struct rtrack *rt, int vol)
+ {
+ int i;
+
+- mutex_lock(&lock);
++ mutex_lock(&rt->lock);
+
+- if(vol == dev->curvol) { /* requested volume = current */
+- if (dev->muted) { /* user is unmuting the card */
+- dev->muted = 0;
+- outb (0xd8, io); /* enable card */
++ if (vol == rt->curvol) { /* requested volume = current */
++ if (rt->muted) { /* user is unmuting the card */
++ rt->muted = 0;
++ outb(0xd8, rt->io); /* enable card */
+ }
+- mutex_unlock(&lock);
++ mutex_unlock(&rt->lock);
+ return 0;
+ }
+
+- if(vol == 0) { /* volume = 0 means mute the card */
+- outb(0x48, io); /* volume down but still "on" */
++ if (vol == 0) { /* volume = 0 means mute the card */
++ outb(0x48, rt->io); /* volume down but still "on" */
+ sleep_delay(2000000); /* make sure it's totally down */
+- outb(0xd0, io); /* volume steady, off */
+- dev->curvol = 0; /* track the volume state! */
+- mutex_unlock(&lock);
++ outb(0xd0, rt->io); /* volume steady, off */
++ rt->curvol = 0; /* track the volume state! */
++ mutex_unlock(&rt->lock);
+ return 0;
+ }
+
+- dev->muted = 0;
+- if(vol > dev->curvol)
+- for(i = dev->curvol; i < vol; i++)
+- rt_incvol();
++ rt->muted = 0;
++ if (vol > rt->curvol)
++ for (i = rt->curvol; i < vol; i++)
++ rt_incvol(rt);
+ else
+- for(i = dev->curvol; i > vol; i--)
+- rt_decvol();
++ for (i = rt->curvol; i > vol; i--)
++ rt_decvol(rt);
+
+- dev->curvol = vol;
+- mutex_unlock(&lock);
++ rt->curvol = vol;
++ mutex_unlock(&rt->lock);
+ return 0;
+ }
+
+@@ -135,155 +145,137 @@ static int rt_setvol(struct rt_device *dev, int vol)
+ * and bit 4 (+16) is to keep the signal strength meter enabled
+ */
+
+-static void send_0_byte(int port, struct rt_device *dev)
++static void send_0_byte(struct rtrack *rt)
+ {
+- if ((dev->curvol == 0) || (dev->muted)) {
+- outb_p(128+64+16+ 1, port); /* wr-enable + data low */
+- outb_p(128+64+16+2+1, port); /* clock */
++ if (rt->curvol == 0 || rt->muted) {
++ outb_p(128+64+16+ 1, rt->io); /* wr-enable + data low */
++ outb_p(128+64+16+2+1, rt->io); /* clock */
+ }
+ else {
+- outb_p(128+64+16+8+ 1, port); /* on + wr-enable + data low */
+- outb_p(128+64+16+8+2+1, port); /* clock */
++ outb_p(128+64+16+8+ 1, rt->io); /* on + wr-enable + data low */
++ outb_p(128+64+16+8+2+1, rt->io); /* clock */
+ }
+ sleep_delay(1000);
+ }
+
+-static void send_1_byte(int port, struct rt_device *dev)
++static void send_1_byte(struct rtrack *rt)
+ {
+- if ((dev->curvol == 0) || (dev->muted)) {
+- outb_p(128+64+16+4 +1, port); /* wr-enable+data high */
+- outb_p(128+64+16+4+2+1, port); /* clock */
++ if (rt->curvol == 0 || rt->muted) {
++ outb_p(128+64+16+4 +1, rt->io); /* wr-enable+data high */
++ outb_p(128+64+16+4+2+1, rt->io); /* clock */
+ }
+ else {
+- outb_p(128+64+16+8+4 +1, port); /* on+wr-enable+data high */
+- outb_p(128+64+16+8+4+2+1, port); /* clock */
++ outb_p(128+64+16+8+4 +1, rt->io); /* on+wr-enable+data high */
++ outb_p(128+64+16+8+4+2+1, rt->io); /* clock */
+ }
+
+ sleep_delay(1000);
+ }
+
+-static int rt_setfreq(struct rt_device *dev, unsigned long freq)
++static int rt_setfreq(struct rtrack *rt, unsigned long freq)
+ {
+ int i;
+
+- /* adapted from radio-aztech.c */
++ mutex_lock(&rt->lock); /* Stop other ops interfering */
++
++ rt->curfreq = freq;
+
+ /* now uses VIDEO_TUNER_LOW for fine tuning */
+
+ freq += 171200; /* Add 10.7 MHz IF */
+ freq /= 800; /* Convert to 50 kHz units */
+
+- mutex_lock(&lock); /* Stop other ops interfering */
+-
+- send_0_byte (io, dev); /* 0: LSB of frequency */
++ send_0_byte(rt); /* 0: LSB of frequency */
+
+ for (i = 0; i < 13; i++) /* : frequency bits (1-13) */
+ if (freq & (1 << i))
+- send_1_byte (io, dev);
++ send_1_byte(rt);
+ else
+- send_0_byte (io, dev);
++ send_0_byte(rt);
+
+- send_0_byte (io, dev); /* 14: test bit - always 0 */
+- send_0_byte (io, dev); /* 15: test bit - always 0 */
++ send_0_byte(rt); /* 14: test bit - always 0 */
++ send_0_byte(rt); /* 15: test bit - always 0 */
+
+- send_0_byte (io, dev); /* 16: band data 0 - always 0 */
+- send_0_byte (io, dev); /* 17: band data 1 - always 0 */
+- send_0_byte (io, dev); /* 18: band data 2 - always 0 */
+- send_0_byte (io, dev); /* 19: time base - always 0 */
++ send_0_byte(rt); /* 16: band data 0 - always 0 */
++ send_0_byte(rt); /* 17: band data 1 - always 0 */
++ send_0_byte(rt); /* 18: band data 2 - always 0 */
++ send_0_byte(rt); /* 19: time base - always 0 */
+
+- send_0_byte (io, dev); /* 20: spacing (0 = 25 kHz) */
+- send_1_byte (io, dev); /* 21: spacing (1 = 25 kHz) */
+- send_0_byte (io, dev); /* 22: spacing (0 = 25 kHz) */
+- send_1_byte (io, dev); /* 23: AM/FM (FM = 1, always) */
++ send_0_byte(rt); /* 20: spacing (0 = 25 kHz) */
++ send_1_byte(rt); /* 21: spacing (1 = 25 kHz) */
++ send_0_byte(rt); /* 22: spacing (0 = 25 kHz) */
++ send_1_byte(rt); /* 23: AM/FM (FM = 1, always) */
+
+- if ((dev->curvol == 0) || (dev->muted))
+- outb (0xd0, io); /* volume steady + sigstr */
++ if (rt->curvol == 0 || rt->muted)
++ outb(0xd0, rt->io); /* volume steady + sigstr */
+ else
+- outb (0xd8, io); /* volume steady + sigstr + on */
++ outb(0xd8, rt->io); /* volume steady + sigstr + on */
+
+- mutex_unlock(&lock);
++ mutex_unlock(&rt->lock);
+
+ return 0;
+ }
+
+-static int rt_getsigstr(struct rt_device *dev)
++static int rt_getsigstr(struct rtrack *rt)
+ {
+- if (inb(io) & 2) /* bit set = no signal present */
+- return 0;
+- return 1; /* signal present */
+-}
++ int sig = 1;
+
+-static struct v4l2_queryctrl radio_qctrl[] = {
+- {
+- .id = V4L2_CID_AUDIO_MUTE,
+- .name = "Mute",
+- .minimum = 0,
+- .maximum = 1,
+- .default_value = 1,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- },{
+- .id = V4L2_CID_AUDIO_VOLUME,
+- .name = "Volume",
+- .minimum = 0,
+- .maximum = 0xff,
+- .step = 1,
+- .default_value = 0xff,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- }
+-};
++ mutex_lock(&rt->lock);
++ if (inb(rt->io) & 2) /* bit set = no signal present */
++ sig = 0;
++ mutex_unlock(&rt->lock);
++ return sig;
++}
+
+ static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+ {
+ strlcpy(v->driver, "radio-aimslab", sizeof(v->driver));
+ strlcpy(v->card, "RadioTrack", sizeof(v->card));
+- sprintf(v->bus_info, "ISA");
++ strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
+ v->version = RADIO_VERSION;
+- v->capabilities = V4L2_CAP_TUNER;
++ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+ }
+
+ static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- struct rt_device *rt = video_drvdata(file);
++ struct rtrack *rt = video_drvdata(file);
+
+ if (v->index > 0)
+ return -EINVAL;
+
+- strcpy(v->name, "FM");
++ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+- v->rangelow = (87*16000);
+- v->rangehigh = (108*16000);
++ v->rangelow = 87 * 16000;
++ v->rangehigh = 108 * 16000;
+ v->rxsubchans = V4L2_TUNER_SUB_MONO;
+ v->capability = V4L2_TUNER_CAP_LOW;
+ v->audmode = V4L2_TUNER_MODE_MONO;
+- v->signal = 0xffff*rt_getsigstr(rt);
++ v->signal = 0xffff * rt_getsigstr(rt);
+ return 0;
+ }
+
+ static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- if (v->index > 0)
+- return -EINVAL;
+- return 0;
++ return v->index ? -EINVAL : 0;
+ }
+
+ static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct rt_device *rt = video_drvdata(file);
++ struct rtrack *rt = video_drvdata(file);
+
+- rt->curfreq = f->frequency;
+- rt_setfreq(rt, rt->curfreq);
++ rt_setfreq(rt, f->frequency);
+ return 0;
+ }
+
+ static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct rt_device *rt = video_drvdata(file);
++ struct rtrack *rt = video_drvdata(file);
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = rt->curfreq;
+@@ -293,14 +285,11 @@ static int vidioc_g_frequency(struct file *file, void *priv,
+ static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+ {
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+- if (qc->id && qc->id == radio_qctrl[i].id) {
+- memcpy(qc, &(radio_qctrl[i]),
+- sizeof(*qc));
+- return 0;
+- }
++ switch (qc->id) {
++ case V4L2_CID_AUDIO_MUTE:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
++ case V4L2_CID_AUDIO_VOLUME:
++ return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
+ }
+ return -EINVAL;
+ }
+@@ -308,14 +297,14 @@ static int vidioc_queryctrl(struct file *file, void *priv,
+ static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct rt_device *rt = video_drvdata(file);
++ struct rtrack *rt = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value = rt->muted;
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+- ctrl->value = rt->curvol * 6554;
++ ctrl->value = rt->curvol;
+ return 0;
+ }
+ return -EINVAL;
+@@ -324,33 +313,22 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
+ static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct rt_device *rt = video_drvdata(file);
++ struct rtrack *rt = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value)
+ rt_mute(rt);
+ else
+- rt_setvol(rt,rt->curvol);
++ rt_setvol(rt, rt->curvol);
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+- rt_setvol(rt,ctrl->value);
++ rt_setvol(rt, ctrl->value);
+ return 0;
+ }
+ return -EINVAL;
+ }
+
+-static int vidioc_g_audio (struct file *file, void *priv,
+- struct v4l2_audio *a)
+-{
+- if (a->index > 1)
+- return -EINVAL;
+-
+- strcpy(a->name, "Radio");
+- a->capability = V4L2_AUDCAP_STEREO;
+- return 0;
+-}
+-
+ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+ {
+ *i = 0;
+@@ -359,36 +337,38 @@ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+
+ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+ {
+- if (i != 0)
+- return -EINVAL;
+- return 0;
++ return i ? -EINVAL : 0;
+ }
+
+-static int vidioc_s_audio(struct file *file, void *priv,
++static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+ {
+- if (a->index != 0)
+- return -EINVAL;
++ a->index = 0;
++ strlcpy(a->name, "Radio", sizeof(a->name));
++ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+ }
+
+-static struct rt_device rtrack_unit;
++static int vidioc_s_audio(struct file *file, void *priv,
++ struct v4l2_audio *a)
++{
++ return a->index ? -EINVAL : 0;
++}
+
+-static int rtrack_exclusive_open(struct file *file)
++static int rtrack_open(struct file *file)
+ {
+- return test_and_set_bit(0, &rtrack_unit.in_use) ? -EBUSY : 0;
++ return 0;
+ }
+
+-static int rtrack_exclusive_release(struct file *file)
++static int rtrack_release(struct file *file)
+ {
+- clear_bit(0, &rtrack_unit.in_use);
+ return 0;
+ }
+
+ static const struct v4l2_file_operations rtrack_fops = {
+ .owner = THIS_MODULE,
+- .open = rtrack_exclusive_open,
+- .release = rtrack_exclusive_release,
++ .open = rtrack_open,
++ .release = rtrack_release,
+ .ioctl = video_ioctl2,
+ };
+
+@@ -407,64 +387,69 @@ static const struct v4l2_ioctl_ops rtrack_ioctl_ops = {
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ };
+
+-static struct video_device rtrack_radio = {
+- .name = "RadioTrack radio",
+- .fops = &rtrack_fops,
+- .ioctl_ops = &rtrack_ioctl_ops,
+- .release = video_device_release_empty,
+-};
+-
+ static int __init rtrack_init(void)
+ {
+- if(io==-1)
+- {
+- printk(KERN_ERR "You must set an I/O address with io=0x???\n");
++ struct rtrack *rt = &rtrack_card;
++ struct v4l2_device *v4l2_dev = &rt->v4l2_dev;
++ int res;
++
++ strlcpy(v4l2_dev->name, "rtrack", sizeof(v4l2_dev->name));
++ rt->io = io;
++
++ if (rt->io == -1) {
++ v4l2_err(v4l2_dev, "you must set an I/O address with io=0x20f or 0x30f\n");
+ return -EINVAL;
+ }
+
+- if (!request_region(io, 2, "rtrack"))
+- {
+- printk(KERN_ERR "rtrack: port 0x%x already in use\n", io);
++ if (!request_region(rt->io, 2, "rtrack")) {
++ v4l2_err(v4l2_dev, "port 0x%x already in use\n", rt->io);
+ return -EBUSY;
+ }
+
+- video_set_drvdata(&rtrack_radio, &rtrack_unit);
++ res = v4l2_device_register(NULL, v4l2_dev);
++ if (res < 0) {
++ release_region(rt->io, 2);
++ v4l2_err(v4l2_dev, "could not register v4l2_device\n");
++ return res;
++ }
+
+- if (video_register_device(&rtrack_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
+- release_region(io, 2);
++ strlcpy(rt->vdev.name, v4l2_dev->name, sizeof(rt->vdev.name));
++ rt->vdev.v4l2_dev = v4l2_dev;
++ rt->vdev.fops = &rtrack_fops;
++ rt->vdev.ioctl_ops = &rtrack_ioctl_ops;
++ rt->vdev.release = video_device_release_empty;
++ video_set_drvdata(&rt->vdev, rt);
++
++ if (video_register_device(&rt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
++ v4l2_device_unregister(&rt->v4l2_dev);
++ release_region(rt->io, 2);
+ return -EINVAL;
+ }
+- printk(KERN_INFO "AIMSlab RadioTrack/RadioReveal card driver.\n");
++ v4l2_info(v4l2_dev, "AIMSlab RadioTrack/RadioReveal card driver.\n");
+
+ /* Set up the I/O locking */
+
+- mutex_init(&lock);
++ mutex_init(&rt->lock);
+
+ /* mute card - prevents noisy bootups */
+
+ /* this ensures that the volume is all the way down */
+- outb(0x48, io); /* volume down but still "on" */
++ outb(0x48, rt->io); /* volume down but still "on" */
+ sleep_delay(2000000); /* make sure it's totally down */
+- outb(0xc0, io); /* steady volume, mute card */
+- rtrack_unit.curvol = 0;
++ outb(0xc0, rt->io); /* steady volume, mute card */
+
+ return 0;
+ }
+
+-MODULE_AUTHOR("M.Kirkwood");
+-MODULE_DESCRIPTION("A driver for the RadioTrack/RadioReveal radio card.");
+-MODULE_LICENSE("GPL");
+-
+-module_param(io, int, 0);
+-MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20f or 0x30f)");
+-module_param(radio_nr, int, 0);
+-
+-static void __exit cleanup_rtrack_module(void)
++static void __exit rtrack_exit(void)
+ {
+- video_unregister_device(&rtrack_radio);
+- release_region(io,2);
++ struct rtrack *rt = &rtrack_card;
++
++ video_unregister_device(&rt->vdev);
++ v4l2_device_unregister(&rt->v4l2_dev);
++ release_region(rt->io, 2);
+ }
+
+ module_init(rtrack_init);
+-module_exit(cleanup_rtrack_module);
++module_exit(rtrack_exit);
+
+diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c
+index 5604e88..49299f7 100644
+--- a/drivers/media/radio/radio-aztech.c
++++ b/drivers/media/radio/radio-aztech.c
+@@ -29,33 +29,15 @@
+ #include <linux/init.h> /* Initdata */
+ #include <linux/ioport.h> /* request_region */
+ #include <linux/delay.h> /* udelay */
+-#include <asm/io.h> /* outb, outb_p */
+-#include <asm/uaccess.h> /* copy to/from user */
+ #include <linux/videodev2.h> /* kernel radio structs */
+-#include <media/v4l2-common.h>
++#include <linux/version.h> /* for KERNEL_VERSION MACRO */
++#include <linux/io.h> /* outb, outb_p */
++#include <media/v4l2-device.h>
+ #include <media/v4l2-ioctl.h>
+
+-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+-#define RADIO_VERSION KERNEL_VERSION(0,0,2)
+-
+-static struct v4l2_queryctrl radio_qctrl[] = {
+- {
+- .id = V4L2_CID_AUDIO_MUTE,
+- .name = "Mute",
+- .minimum = 0,
+- .maximum = 1,
+- .default_value = 1,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- },{
+- .id = V4L2_CID_AUDIO_VOLUME,
+- .name = "Volume",
+- .minimum = 0,
+- .maximum = 0xff,
+- .step = 1,
+- .default_value = 0xff,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- }
+-};
++MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
++MODULE_DESCRIPTION("A driver for the Aztech radio card.");
++MODULE_LICENSE("GPL");
+
+ /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
+
+@@ -66,55 +48,64 @@ static struct v4l2_queryctrl radio_qctrl[] = {
+ static int io = CONFIG_RADIO_AZTECH_PORT;
+ static int radio_nr = -1;
+ static int radio_wait_time = 1000;
+-static struct mutex lock;
+
+-struct az_device
++module_param(io, int, 0);
++module_param(radio_nr, int, 0);
++MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
++
++#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
++
++struct aztech
+ {
+- unsigned long in_use;
++ struct v4l2_device v4l2_dev;
++ struct video_device vdev;
++ int io;
+ int curvol;
+ unsigned long curfreq;
+ int stereo;
++ struct mutex lock;
+ };
+
++static struct aztech aztech_card;
++
+ static int volconvert(int level)
+ {
+- level>>=14; /* Map 16bits down to 2 bit */
+- level&=3;
++ level >>= 14; /* Map 16bits down to 2 bit */
++ level &= 3;
+
+ /* convert to card-friendly values */
+- switch (level)
+- {
+- case 0:
+- return 0;
+- case 1:
+- return 1;
+- case 2:
+- return 4;
+- case 3:
+- return 5;
++ switch (level) {
++ case 0:
++ return 0;
++ case 1:
++ return 1;
++ case 2:
++ return 4;
++ case 3:
++ return 5;
+ }
+ return 0; /* Quieten gcc */
+ }
+
+-static void send_0_byte (struct az_device *dev)
++static void send_0_byte(struct aztech *az)
+ {
+ udelay(radio_wait_time);
+- outb_p(2+volconvert(dev->curvol), io);
+- outb_p(64+2+volconvert(dev->curvol), io);
++ outb_p(2 + volconvert(az->curvol), az->io);
++ outb_p(64 + 2 + volconvert(az->curvol), az->io);
+ }
+
+-static void send_1_byte (struct az_device *dev)
++static void send_1_byte(struct aztech *az)
+ {
+ udelay (radio_wait_time);
+- outb_p(128+2+volconvert(dev->curvol), io);
+- outb_p(128+64+2+volconvert(dev->curvol), io);
++ outb_p(128 + 2 + volconvert(az->curvol), az->io);
++ outb_p(128 + 64 + 2 + volconvert(az->curvol), az->io);
+ }
+
+-static int az_setvol(struct az_device *dev, int vol)
++static int az_setvol(struct aztech *az, int vol)
+ {
+- mutex_lock(&lock);
+- outb (volconvert(vol), io);
+- mutex_unlock(&lock);
++ mutex_lock(&az->lock);
++ outb(volconvert(vol), az->io);
++ mutex_unlock(&az->lock);
+ return 0;
+ }
+
+@@ -126,116 +117,110 @@ static int az_setvol(struct az_device *dev, int vol)
+ *
+ */
+
+-static int az_getsigstr(struct az_device *dev)
++static int az_getsigstr(struct aztech *az)
+ {
+- if (inb(io) & 2) /* bit set = no signal present */
+- return 0;
+- return 1; /* signal present */
++ int sig = 1;
++
++ mutex_lock(&az->lock);
++ if (inb(az->io) & 2) /* bit set = no signal present */
++ sig = 0;
++ mutex_unlock(&az->lock);
++ return sig;
+ }
+
+-static int az_getstereo(struct az_device *dev)
++static int az_getstereo(struct aztech *az)
+ {
+- if (inb(io) & 1) /* bit set = mono */
+- return 0;
+- return 1; /* stereo */
++ int stereo = 1;
++
++ mutex_lock(&az->lock);
++ if (inb(az->io) & 1) /* bit set = mono */
++ stereo = 0;
++ mutex_unlock(&az->lock);
++ return stereo;
+ }
+
+-static int az_setfreq(struct az_device *dev, unsigned long frequency)
++static int az_setfreq(struct aztech *az, unsigned long frequency)
+ {
+ int i;
+
++ mutex_lock(&az->lock);
++
++ az->curfreq = frequency;
+ frequency += 171200; /* Add 10.7 MHz IF */
+ frequency /= 800; /* Convert to 50 kHz units */
+
+- mutex_lock(&lock);
+-
+- send_0_byte (dev); /* 0: LSB of frequency */
++ send_0_byte(az); /* 0: LSB of frequency */
+
+ for (i = 0; i < 13; i++) /* : frequency bits (1-13) */
+ if (frequency & (1 << i))
+- send_1_byte (dev);
++ send_1_byte(az);
+ else
+- send_0_byte (dev);
++ send_0_byte(az);
+
+- send_0_byte (dev); /* 14: test bit - always 0 */
+- send_0_byte (dev); /* 15: test bit - always 0 */
+- send_0_byte (dev); /* 16: band data 0 - always 0 */
+- if (dev->stereo) /* 17: stereo (1 to enable) */
+- send_1_byte (dev);
++ send_0_byte(az); /* 14: test bit - always 0 */
++ send_0_byte(az); /* 15: test bit - always 0 */
++ send_0_byte(az); /* 16: band data 0 - always 0 */
++ if (az->stereo) /* 17: stereo (1 to enable) */
++ send_1_byte(az);
+ else
+- send_0_byte (dev);
++ send_0_byte(az);
+
+- send_1_byte (dev); /* 18: band data 1 - unknown */
+- send_0_byte (dev); /* 19: time base - always 0 */
+- send_0_byte (dev); /* 20: spacing (0 = 25 kHz) */
+- send_1_byte (dev); /* 21: spacing (1 = 25 kHz) */
+- send_0_byte (dev); /* 22: spacing (0 = 25 kHz) */
+- send_1_byte (dev); /* 23: AM/FM (FM = 1, always) */
++ send_1_byte(az); /* 18: band data 1 - unknown */
++ send_0_byte(az); /* 19: time base - always 0 */
++ send_0_byte(az); /* 20: spacing (0 = 25 kHz) */
++ send_1_byte(az); /* 21: spacing (1 = 25 kHz) */
++ send_0_byte(az); /* 22: spacing (0 = 25 kHz) */
++ send_1_byte(az); /* 23: AM/FM (FM = 1, always) */
+
+ /* latch frequency */
+
+- udelay (radio_wait_time);
+- outb_p(128+64+volconvert(dev->curvol), io);
++ udelay(radio_wait_time);
++ outb_p(128 + 64 + volconvert(az->curvol), az->io);
+
+- mutex_unlock(&lock);
++ mutex_unlock(&az->lock);
+
+ return 0;
+ }
+
+-static int vidioc_querycap (struct file *file, void *priv,
++static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+ {
+- strlcpy(v->driver, "radio-aztech", sizeof (v->driver));
+- strlcpy(v->card, "Aztech Radio", sizeof (v->card));
+- sprintf(v->bus_info,"ISA");
++ strlcpy(v->driver, "radio-aztech", sizeof(v->driver));
++ strlcpy(v->card, "Aztech Radio", sizeof(v->card));
++ strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
+ v->version = RADIO_VERSION;
+- v->capabilities = V4L2_CAP_TUNER;
++ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+ }
+
+-static int vidioc_g_tuner (struct file *file, void *priv,
++static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- struct az_device *az = video_drvdata(file);
++ struct aztech *az = video_drvdata(file);
+
+ if (v->index > 0)
+ return -EINVAL;
+
+- strcpy(v->name, "FM");
++ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+
+- v->rangelow=(87*16000);
+- v->rangehigh=(108*16000);
+- v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
+- v->capability=V4L2_TUNER_CAP_LOW;
+- if(az_getstereo(az))
++ v->rangelow = 87 * 16000;
++ v->rangehigh = 108 * 16000;
++ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
++ v->capability = V4L2_TUNER_CAP_LOW;
++ if (az_getstereo(az))
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ v->audmode = V4L2_TUNER_MODE_MONO;
+- v->signal=0xFFFF*az_getsigstr(az);
++ v->signal = 0xFFFF * az_getsigstr(az);
+
+ return 0;
+ }
+
+-
+-static int vidioc_s_tuner (struct file *file, void *priv,
++static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- if (v->index > 0)
+- return -EINVAL;
+-
+- return 0;
+-}
+-
+-static int vidioc_g_audio (struct file *file, void *priv,
+- struct v4l2_audio *a)
+-{
+- if (a->index > 1)
+- return -EINVAL;
+-
+- strcpy(a->name, "Radio");
+- a->capability = V4L2_AUDCAP_STEREO;
+- return 0;
++ return v->index ? -EINVAL : 0;
+ }
+
+ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+@@ -246,113 +231,107 @@ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+
+ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+ {
+- if (i != 0)
+- return -EINVAL;
+- return 0;
++ return i ? -EINVAL : 0;
+ }
+
+-
+-static int vidioc_s_audio (struct file *file, void *priv,
++static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+ {
+- if (a->index != 0)
+- return -EINVAL;
+-
++ a->index = 0;
++ strlcpy(a->name, "Radio", sizeof(a->name));
++ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+ }
+
+-static int vidioc_s_frequency (struct file *file, void *priv,
++static int vidioc_s_audio(struct file *file, void *priv,
++ struct v4l2_audio *a)
++{
++ return a->index ? -EINVAL : 0;
++}
++
++static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct az_device *az = video_drvdata(file);
++ struct aztech *az = video_drvdata(file);
+
+- az->curfreq = f->frequency;
+- az_setfreq(az, az->curfreq);
++ az_setfreq(az, f->frequency);
+ return 0;
+ }
+
+-static int vidioc_g_frequency (struct file *file, void *priv,
++static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct az_device *az = video_drvdata(file);
++ struct aztech *az = video_drvdata(file);
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = az->curfreq;
+-
+ return 0;
+ }
+
+-static int vidioc_queryctrl (struct file *file, void *priv,
++static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+ {
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+- if (qc->id && qc->id == radio_qctrl[i].id) {
+- memcpy(qc, &(radio_qctrl[i]),
+- sizeof(*qc));
+- return (0);
+- }
++ switch (qc->id) {
++ case V4L2_CID_AUDIO_MUTE:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
++ case V4L2_CID_AUDIO_VOLUME:
++ return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
+ }
+ return -EINVAL;
+ }
+
+-static int vidioc_g_ctrl (struct file *file, void *priv,
++static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct az_device *az = video_drvdata(file);
++ struct aztech *az = video_drvdata(file);
+
+ switch (ctrl->id) {
+- case V4L2_CID_AUDIO_MUTE:
+- if (az->curvol==0)
+- ctrl->value=1;
+- else
+- ctrl->value=0;
+- return (0);
+- case V4L2_CID_AUDIO_VOLUME:
+- ctrl->value=az->curvol * 6554;
+- return (0);
++ case V4L2_CID_AUDIO_MUTE:
++ if (az->curvol == 0)
++ ctrl->value = 1;
++ else
++ ctrl->value = 0;
++ return 0;
++ case V4L2_CID_AUDIO_VOLUME:
++ ctrl->value = az->curvol * 6554;
++ return 0;
+ }
+ return -EINVAL;
+ }
+
+-static int vidioc_s_ctrl (struct file *file, void *priv,
++static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct az_device *az = video_drvdata(file);
++ struct aztech *az = video_drvdata(file);
+
+ switch (ctrl->id) {
+- case V4L2_CID_AUDIO_MUTE:
+- if (ctrl->value) {
+- az_setvol(az,0);
+- } else {
+- az_setvol(az,az->curvol);
+- }
+- return (0);
+- case V4L2_CID_AUDIO_VOLUME:
+- az_setvol(az,ctrl->value);
+- return (0);
++ case V4L2_CID_AUDIO_MUTE:
++ if (ctrl->value)
++ az_setvol(az, 0);
++ else
++ az_setvol(az, az->curvol);
++ return 0;
++ case V4L2_CID_AUDIO_VOLUME:
++ az_setvol(az, ctrl->value);
++ return 0;
+ }
+ return -EINVAL;
+ }
+
+-static struct az_device aztech_unit;
+-
+-static int aztech_exclusive_open(struct file *file)
++static int aztech_open(struct file *file)
+ {
+- return test_and_set_bit(0, &aztech_unit.in_use) ? -EBUSY : 0;
++ return 0;
+ }
+
+-static int aztech_exclusive_release(struct file *file)
++static int aztech_release(struct file *file)
+ {
+- clear_bit(0, &aztech_unit.in_use);
+ return 0;
+ }
+
+ static const struct v4l2_file_operations aztech_fops = {
+ .owner = THIS_MODULE,
+- .open = aztech_exclusive_open,
+- .release = aztech_exclusive_release,
++ .open = aztech_open,
++ .release = aztech_release,
+ .ioctl = video_ioctl2,
+ };
+
+@@ -371,57 +350,60 @@ static const struct v4l2_ioctl_ops aztech_ioctl_ops = {
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ };
+
+-static struct video_device aztech_radio = {
+- .name = "Aztech radio",
+- .fops = &aztech_fops,
+- .ioctl_ops = &aztech_ioctl_ops,
+- .release = video_device_release_empty,
+-};
+-
+-module_param_named(debug,aztech_radio.debug, int, 0644);
+-MODULE_PARM_DESC(debug,"activates debug info");
+-
+ static int __init aztech_init(void)
+ {
+- if(io==-1)
+- {
+- printk(KERN_ERR "You must set an I/O address with io=0x???\n");
++ struct aztech *az = &aztech_card;
++ struct v4l2_device *v4l2_dev = &az->v4l2_dev;
++ int res;
++
++ strlcpy(v4l2_dev->name, "aztech", sizeof(v4l2_dev->name));
++ az->io = io;
++
++ if (az->io == -1) {
++ v4l2_err(v4l2_dev, "you must set an I/O address with io=0x350 or 0x358\n");
+ return -EINVAL;
+ }
+
+- if (!request_region(io, 2, "aztech"))
+- {
+- printk(KERN_ERR "aztech: port 0x%x already in use\n", io);
++ if (!request_region(az->io, 2, "aztech")) {
++ v4l2_err(v4l2_dev, "port 0x%x already in use\n", az->io);
+ return -EBUSY;
+ }
+
+- mutex_init(&lock);
+- video_set_drvdata(&aztech_radio, &aztech_unit);
++ res = v4l2_device_register(NULL, v4l2_dev);
++ if (res < 0) {
++ release_region(az->io, 2);
++ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
++ return res;
++ }
+
+- if (video_register_device(&aztech_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
+- release_region(io,2);
++ mutex_init(&az->lock);
++ strlcpy(az->vdev.name, v4l2_dev->name, sizeof(az->vdev.name));
++ az->vdev.v4l2_dev = v4l2_dev;
++ az->vdev.fops = &aztech_fops;
++ az->vdev.ioctl_ops = &aztech_ioctl_ops;
++ az->vdev.release = video_device_release_empty;
++ video_set_drvdata(&az->vdev, az);
++
++ if (video_register_device(&az->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
++ v4l2_device_unregister(v4l2_dev);
++ release_region(az->io, 2);
+ return -EINVAL;
+ }
+
+- printk(KERN_INFO "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
++ v4l2_info(v4l2_dev, "Aztech radio card driver v1.00/19990224 rkroll@exploits.org\n");
+ /* mute card - prevents noisy bootups */
+- outb (0, io);
++ outb(0, az->io);
+ return 0;
+ }
+
+-MODULE_AUTHOR("Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
+-MODULE_DESCRIPTION("A driver for the Aztech radio card.");
+-MODULE_LICENSE("GPL");
+-
+-module_param(io, int, 0);
+-module_param(radio_nr, int, 0);
+-MODULE_PARM_DESC(io, "I/O address of the Aztech card (0x350 or 0x358)");
+-
+-static void __exit aztech_cleanup(void)
++static void __exit aztech_exit(void)
+ {
+- video_unregister_device(&aztech_radio);
+- release_region(io,2);
++ struct aztech *az = &aztech_card;
++
++ video_unregister_device(&az->vdev);
++ v4l2_device_unregister(&az->v4l2_dev);
++ release_region(az->io, 2);
+ }
+
+ module_init(aztech_init);
+-module_exit(aztech_cleanup);
++module_exit(aztech_exit);
+diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c
+index cb3075a..d30fc0c 100644
+--- a/drivers/media/radio/radio-cadet.c
++++ b/drivers/media/radio/radio-cadet.c
+@@ -35,333 +35,318 @@
+ #include <linux/init.h> /* Initdata */
+ #include <linux/ioport.h> /* request_region */
+ #include <linux/delay.h> /* udelay */
+-#include <asm/io.h> /* outb, outb_p */
+-#include <asm/uaccess.h> /* copy to/from user */
+ #include <linux/videodev2.h> /* V4L2 API defs */
+-#include <media/v4l2-common.h>
+-#include <media/v4l2-ioctl.h>
+ #include <linux/param.h>
+ #include <linux/pnp.h>
++#include <linux/io.h> /* outb, outb_p */
++#include <media/v4l2-device.h>
++#include <media/v4l2-ioctl.h>
++
++MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
++MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card.");
++MODULE_LICENSE("GPL");
++
++static int io = -1; /* default to isapnp activation */
++static int radio_nr = -1;
++
++module_param(io, int, 0);
++MODULE_PARM_DESC(io, "I/O address of Cadet card (0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e)");
++module_param(radio_nr, int, 0);
++
++#define CADET_VERSION KERNEL_VERSION(0, 3, 3)
+
+ #define RDS_BUFFER 256
+ #define RDS_RX_FLAG 1
+ #define MBS_RX_FLAG 2
+
+-#define CADET_VERSION KERNEL_VERSION(0,3,3)
+-
+-static struct v4l2_queryctrl radio_qctrl[] = {
+- {
+- .id = V4L2_CID_AUDIO_MUTE,
+- .name = "Mute",
+- .minimum = 0,
+- .maximum = 1,
+- .default_value = 1,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- },{
+- .id = V4L2_CID_AUDIO_VOLUME,
+- .name = "Volume",
+- .minimum = 0,
+- .maximum = 0xff,
+- .step = 1,
+- .default_value = 0xff,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- }
++struct cadet {
++ struct v4l2_device v4l2_dev;
++ struct video_device vdev;
++ int io;
++ int users;
++ int curtuner;
++ int tunestat;
++ int sigstrength;
++ wait_queue_head_t read_queue;
++ struct timer_list readtimer;
++ __u8 rdsin, rdsout, rdsstat;
++ unsigned char rdsbuf[RDS_BUFFER];
++ struct mutex lock;
++ int reading;
+ };
+
+-static int io=-1; /* default to isapnp activation */
+-static int radio_nr = -1;
+-static int users;
+-static int curtuner;
+-static int tunestat;
+-static int sigstrength;
+-static wait_queue_head_t read_queue;
+-static struct timer_list readtimer;
+-static __u8 rdsin, rdsout, rdsstat;
+-static unsigned char rdsbuf[RDS_BUFFER];
+-static spinlock_t cadet_io_lock;
+-
+-static int cadet_probe(void);
++static struct cadet cadet_card;
+
+ /*
+ * Signal Strength Threshold Values
+ * The V4L API spec does not define any particular unit for the signal
+ * strength value. These values are in microvolts of RF at the tuner's input.
+ */
+-static __u16 sigtable[2][4]={{5,10,30,150},{28,40,63,1000}};
++static __u16 sigtable[2][4] = {
++ { 5, 10, 30, 150 },
++ { 28, 40, 63, 1000 }
++};
+
+
+-static int
+-cadet_getstereo(void)
++static int cadet_getstereo(struct cadet *dev)
+ {
+ int ret = V4L2_TUNER_SUB_MONO;
+- if(curtuner != 0) /* Only FM has stereo capability! */
++
++ if (dev->curtuner != 0) /* Only FM has stereo capability! */
+ return V4L2_TUNER_SUB_MONO;
+
+- spin_lock(&cadet_io_lock);
+- outb(7,io); /* Select tuner control */
+- if( (inb(io+1) & 0x40) == 0)
++ mutex_lock(&dev->lock);
++ outb(7, dev->io); /* Select tuner control */
++ if ((inb(dev->io + 1) & 0x40) == 0)
+ ret = V4L2_TUNER_SUB_STEREO;
+- spin_unlock(&cadet_io_lock);
++ mutex_unlock(&dev->lock);
+ return ret;
+ }
+
+-static unsigned
+-cadet_gettune(void)
++static unsigned cadet_gettune(struct cadet *dev)
+ {
+- int curvol,i;
+- unsigned fifo=0;
++ int curvol, i;
++ unsigned fifo = 0;
+
+ /*
+ * Prepare for read
+ */
+
+- spin_lock(&cadet_io_lock);
++ mutex_lock(&dev->lock);
+
+- outb(7,io); /* Select tuner control */
+- curvol=inb(io+1); /* Save current volume/mute setting */
+- outb(0x00,io+1); /* Ensure WRITE-ENABLE is LOW */
+- tunestat=0xffff;
++ outb(7, dev->io); /* Select tuner control */
++ curvol = inb(dev->io + 1); /* Save current volume/mute setting */
++ outb(0x00, dev->io + 1); /* Ensure WRITE-ENABLE is LOW */
++ dev->tunestat = 0xffff;
+
+ /*
+ * Read the shift register
+ */
+- for(i=0;i<25;i++) {
+- fifo=(fifo<<1)|((inb(io+1)>>7)&0x01);
+- if(i<24) {
+- outb(0x01,io+1);
+- tunestat&=inb(io+1);
+- outb(0x00,io+1);
++ for (i = 0; i < 25; i++) {
++ fifo = (fifo << 1) | ((inb(dev->io + 1) >> 7) & 0x01);
++ if (i < 24) {
++ outb(0x01, dev->io + 1);
++ dev->tunestat &= inb(dev->io + 1);
++ outb(0x00, dev->io + 1);
+ }
+ }
+
+ /*
+ * Restore volume/mute setting
+ */
+- outb(curvol,io+1);
+- spin_unlock(&cadet_io_lock);
++ outb(curvol, dev->io + 1);
++ mutex_unlock(&dev->lock);
+
+ return fifo;
+ }
+
+-static unsigned
+-cadet_getfreq(void)
++static unsigned cadet_getfreq(struct cadet *dev)
+ {
+ int i;
+- unsigned freq=0,test,fifo=0;
++ unsigned freq = 0, test, fifo = 0;
+
+ /*
+ * Read current tuning
+ */
+- fifo=cadet_gettune();
++ fifo = cadet_gettune(dev);
+
+ /*
+ * Convert to actual frequency
+ */
+- if(curtuner==0) { /* FM */
+- test=12500;
+- for(i=0;i<14;i++) {
+- if((fifo&0x01)!=0) {
+- freq+=test;
+- }
+- test=test<<1;
+- fifo=fifo>>1;
++ if (dev->curtuner == 0) { /* FM */
++ test = 12500;
++ for (i = 0; i < 14; i++) {
++ if ((fifo & 0x01) != 0)
++ freq += test;
++ test = test << 1;
++ fifo = fifo >> 1;
+ }
+- freq-=10700000; /* IF frequency is 10.7 MHz */
+- freq=(freq*16)/1000000; /* Make it 1/16 MHz */
+- }
+- if(curtuner==1) { /* AM */
+- freq=((fifo&0x7fff)-2010)*16;
++ freq -= 10700000; /* IF frequency is 10.7 MHz */
++ freq = (freq * 16) / 1000000; /* Make it 1/16 MHz */
+ }
++ if (dev->curtuner == 1) /* AM */
++ freq = ((fifo & 0x7fff) - 2010) * 16;
+
+ return freq;
+ }
+
+-static void
+-cadet_settune(unsigned fifo)
++static void cadet_settune(struct cadet *dev, unsigned fifo)
+ {
+ int i;
+ unsigned test;
+
+- spin_lock(&cadet_io_lock);
++ mutex_lock(&dev->lock);
+
+- outb(7,io); /* Select tuner control */
++ outb(7, dev->io); /* Select tuner control */
+ /*
+ * Write the shift register
+ */
+- test=0;
+- test=(fifo>>23)&0x02; /* Align data for SDO */
+- test|=0x1c; /* SDM=1, SWE=1, SEN=1, SCK=0 */
+- outb(7,io); /* Select tuner control */
+- outb(test,io+1); /* Initialize for write */
+- for(i=0;i<25;i++) {
+- test|=0x01; /* Toggle SCK High */
+- outb(test,io+1);
+- test&=0xfe; /* Toggle SCK Low */
+- outb(test,io+1);
+- fifo=fifo<<1; /* Prepare the next bit */
+- test=0x1c|((fifo>>23)&0x02);
+- outb(test,io+1);
++ test = 0;
++ test = (fifo >> 23) & 0x02; /* Align data for SDO */
++ test |= 0x1c; /* SDM=1, SWE=1, SEN=1, SCK=0 */
++ outb(7, dev->io); /* Select tuner control */
++ outb(test, dev->io + 1); /* Initialize for write */
++ for (i = 0; i < 25; i++) {
++ test |= 0x01; /* Toggle SCK High */
++ outb(test, dev->io + 1);
++ test &= 0xfe; /* Toggle SCK Low */
++ outb(test, dev->io + 1);
++ fifo = fifo << 1; /* Prepare the next bit */
++ test = 0x1c | ((fifo >> 23) & 0x02);
++ outb(test, dev->io + 1);
+ }
+- spin_unlock(&cadet_io_lock);
++ mutex_unlock(&dev->lock);
+ }
+
+-static void
+-cadet_setfreq(unsigned freq)
++static void cadet_setfreq(struct cadet *dev, unsigned freq)
+ {
+ unsigned fifo;
+- int i,j,test;
++ int i, j, test;
+ int curvol;
+
+ /*
+ * Formulate a fifo command
+ */
+- fifo=0;
+- if(curtuner==0) { /* FM */
+- test=102400;
+- freq=(freq*1000)/16; /* Make it kHz */
+- freq+=10700; /* IF is 10700 kHz */
+- for(i=0;i<14;i++) {
+- fifo=fifo<<1;
+- if(freq>=test) {
+- fifo|=0x01;
+- freq-=test;
++ fifo = 0;
++ if (dev->curtuner == 0) { /* FM */
++ test = 102400;
++ freq = (freq * 1000) / 16; /* Make it kHz */
++ freq += 10700; /* IF is 10700 kHz */
++ for (i = 0; i < 14; i++) {
++ fifo = fifo << 1;
++ if (freq >= test) {
++ fifo |= 0x01;
++ freq -= test;
+ }
+- test=test>>1;
++ test = test >> 1;
+ }
+ }
+- if(curtuner==1) { /* AM */
+- fifo=(freq/16)+2010; /* Make it kHz */
+- fifo|=0x100000; /* Select AM Band */
++ if (dev->curtuner == 1) { /* AM */
++ fifo = (freq / 16) + 2010; /* Make it kHz */
++ fifo |= 0x100000; /* Select AM Band */
+ }
+
+ /*
+ * Save current volume/mute setting
+ */
+
+- spin_lock(&cadet_io_lock);
+- outb(7,io); /* Select tuner control */
+- curvol=inb(io+1);
+- spin_unlock(&cadet_io_lock);
++ mutex_lock(&dev->lock);
++ outb(7, dev->io); /* Select tuner control */
++ curvol = inb(dev->io + 1);
++ mutex_unlock(&dev->lock);
+
+ /*
+ * Tune the card
+ */
+- for(j=3;j>-1;j--) {
+- cadet_settune(fifo|(j<<16));
++ for (j = 3; j > -1; j--) {
++ cadet_settune(dev, fifo | (j << 16));
+
+- spin_lock(&cadet_io_lock);
+- outb(7,io); /* Select tuner control */
+- outb(curvol,io+1);
+- spin_unlock(&cadet_io_lock);
++ mutex_lock(&dev->lock);
++ outb(7, dev->io); /* Select tuner control */
++ outb(curvol, dev->io + 1);
++ mutex_unlock(&dev->lock);
+
+ msleep(100);
+
+- cadet_gettune();
+- if((tunestat & 0x40) == 0) { /* Tuned */
+- sigstrength=sigtable[curtuner][j];
++ cadet_gettune(dev);
++ if ((dev->tunestat & 0x40) == 0) { /* Tuned */
++ dev->sigstrength = sigtable[dev->curtuner][j];
+ return;
+ }
+ }
+- sigstrength=0;
++ dev->sigstrength = 0;
+ }
+
+
+-static int
+-cadet_getvol(void)
++static int cadet_getvol(struct cadet *dev)
+ {
+ int ret = 0;
+
+- spin_lock(&cadet_io_lock);
++ mutex_lock(&dev->lock);
+
+- outb(7,io); /* Select tuner control */
+- if((inb(io + 1) & 0x20) != 0)
++ outb(7, dev->io); /* Select tuner control */
++ if ((inb(dev->io + 1) & 0x20) != 0)
+ ret = 0xffff;
+
+- spin_unlock(&cadet_io_lock);
++ mutex_unlock(&dev->lock);
+ return ret;
+ }
+
+
+-static void
+-cadet_setvol(int vol)
++static void cadet_setvol(struct cadet *dev, int vol)
+ {
+- spin_lock(&cadet_io_lock);
+- outb(7,io); /* Select tuner control */
+- if(vol>0)
+- outb(0x20,io+1);
++ mutex_lock(&dev->lock);
++ outb(7, dev->io); /* Select tuner control */
++ if (vol > 0)
++ outb(0x20, dev->io + 1);
+ else
+- outb(0x00,io+1);
+- spin_unlock(&cadet_io_lock);
++ outb(0x00, dev->io + 1);
++ mutex_unlock(&dev->lock);
+ }
+
+-static void
+-cadet_handler(unsigned long data)
++static void cadet_handler(unsigned long data)
+ {
+- /*
+- * Service the RDS fifo
+- */
++ struct cadet *dev = (void *)data;
+
+- if(spin_trylock(&cadet_io_lock))
+- {
+- outb(0x3,io); /* Select RDS Decoder Control */
+- if((inb(io+1)&0x20)!=0) {
++ /* Service the RDS fifo */
++ if (mutex_trylock(&dev->lock)) {
++ outb(0x3, dev->io); /* Select RDS Decoder Control */
++ if ((inb(dev->io + 1) & 0x20) != 0)
+ printk(KERN_CRIT "cadet: RDS fifo overflow\n");
+- }
+- outb(0x80,io); /* Select RDS fifo */
+- while((inb(io)&0x80)!=0) {
+- rdsbuf[rdsin]=inb(io+1);
+- if(rdsin==rdsout)
++ outb(0x80, dev->io); /* Select RDS fifo */
++ while ((inb(dev->io) & 0x80) != 0) {
++ dev->rdsbuf[dev->rdsin] = inb(dev->io + 1);
++ if (dev->rdsin == dev->rdsout)
+ printk(KERN_WARNING "cadet: RDS buffer overflow\n");
+ else
+- rdsin++;
++ dev->rdsin++;
+ }
+- spin_unlock(&cadet_io_lock);
++ mutex_unlock(&dev->lock);
+ }
+
+ /*
+ * Service pending read
+ */
+- if( rdsin!=rdsout)
+- wake_up_interruptible(&read_queue);
++ if (dev->rdsin != dev->rdsout)
++ wake_up_interruptible(&dev->read_queue);
+
+ /*
+ * Clean up and exit
+ */
+- init_timer(&readtimer);
+- readtimer.function=cadet_handler;
+- readtimer.data=(unsigned long)0;
+- readtimer.expires=jiffies+msecs_to_jiffies(50);
+- add_timer(&readtimer);
++ init_timer(&dev->readtimer);
++ dev->readtimer.function = cadet_handler;
++ dev->readtimer.data = (unsigned long)0;
++ dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
++ add_timer(&dev->readtimer);
+ }
+
+
+-
+-static ssize_t
+-cadet_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
++static ssize_t cadet_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+ {
+- int i=0;
++ struct cadet *dev = video_drvdata(file);
+ unsigned char readbuf[RDS_BUFFER];
+-
+- if(rdsstat==0) {
+- spin_lock(&cadet_io_lock);
+- rdsstat=1;
+- outb(0x80,io); /* Select RDS fifo */
+- spin_unlock(&cadet_io_lock);
+- init_timer(&readtimer);
+- readtimer.function=cadet_handler;
+- readtimer.data=(unsigned long)0;
+- readtimer.expires=jiffies+msecs_to_jiffies(50);
+- add_timer(&readtimer);
++ int i = 0;
++
++ if (dev->rdsstat == 0) {
++ mutex_lock(&dev->lock);
++ dev->rdsstat = 1;
++ outb(0x80, dev->io); /* Select RDS fifo */
++ mutex_unlock(&dev->lock);
++ init_timer(&dev->readtimer);
++ dev->readtimer.function = cadet_handler;
++ dev->readtimer.data = (unsigned long)dev;
++ dev->readtimer.expires = jiffies + msecs_to_jiffies(50);
++ add_timer(&dev->readtimer);
+ }
+- if(rdsin==rdsout) {
++ if (dev->rdsin == dev->rdsout) {
+ if (file->f_flags & O_NONBLOCK)
+ return -EWOULDBLOCK;
+- interruptible_sleep_on(&read_queue);
++ interruptible_sleep_on(&dev->read_queue);
+ }
+- while( i<count && rdsin!=rdsout)
+- readbuf[i++]=rdsbuf[rdsout++];
++ while (i < count && dev->rdsin != dev->rdsout)
++ readbuf[i++] = dev->rdsbuf[dev->rdsout++];
+
+- if (copy_to_user(data,readbuf,i))
++ if (copy_to_user(data, readbuf, i))
+ return -EFAULT;
+ return i;
+ }
+@@ -370,38 +355,40 @@ cadet_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
+ static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+ {
+- v->capabilities =
+- V4L2_CAP_TUNER |
+- V4L2_CAP_READWRITE;
++ strlcpy(v->driver, "ADS Cadet", sizeof(v->driver));
++ strlcpy(v->card, "ADS Cadet", sizeof(v->card));
++ strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
+ v->version = CADET_VERSION;
+- strcpy(v->driver, "ADS Cadet");
+- strcpy(v->card, "ADS Cadet");
++ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO | V4L2_CAP_READWRITE;
+ return 0;
+ }
+
+ static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
++ struct cadet *dev = video_drvdata(file);
++
+ v->type = V4L2_TUNER_RADIO;
+ switch (v->index) {
+ case 0:
+- strcpy(v->name, "FM");
++ strlcpy(v->name, "FM", sizeof(v->name));
+ v->capability = V4L2_TUNER_CAP_STEREO;
+ v->rangelow = 1400; /* 87.5 MHz */
+ v->rangehigh = 1728; /* 108.0 MHz */
+- v->rxsubchans=cadet_getstereo();
+- switch (v->rxsubchans){
++ v->rxsubchans = cadet_getstereo(dev);
++ switch (v->rxsubchans) {
+ case V4L2_TUNER_SUB_MONO:
+ v->audmode = V4L2_TUNER_MODE_MONO;
+ break;
+ case V4L2_TUNER_SUB_STEREO:
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ break;
+- default: ;
++ default:
++ break;
+ }
+ break;
+ case 1:
+- strcpy(v->name, "AM");
++ strlcpy(v->name, "AM", sizeof(v->name));
+ v->capability = V4L2_TUNER_CAP_LOW;
+ v->rangelow = 8320; /* 520 kHz */
+ v->rangehigh = 26400; /* 1650 kHz */
+@@ -411,25 +398,29 @@ static int vidioc_g_tuner(struct file *file, void *priv,
+ default:
+ return -EINVAL;
+ }
+- v->signal = sigstrength; /* We might need to modify scaling of this */
++ v->signal = dev->sigstrength; /* We might need to modify scaling of this */
+ return 0;
+ }
+
+ static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- if((v->index != 0)&&(v->index != 1))
++ struct cadet *dev = video_drvdata(file);
++
++ if (v->index != 0 && v->index != 1)
+ return -EINVAL;
+- curtuner = v->index;
++ dev->curtuner = v->index;
+ return 0;
+ }
+
+ static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- f->tuner = curtuner;
++ struct cadet *dev = video_drvdata(file);
++
++ f->tuner = dev->curtuner;
+ f->type = V4L2_TUNER_RADIO;
+- f->frequency = cadet_getfreq();
++ f->frequency = cadet_getfreq(dev);
+ return 0;
+ }
+
+@@ -437,27 +428,26 @@ static int vidioc_g_frequency(struct file *file, void *priv,
+ static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
++ struct cadet *dev = video_drvdata(file);
++
+ if (f->type != V4L2_TUNER_RADIO)
+ return -EINVAL;
+- if((curtuner==0)&&((f->frequency<1400)||(f->frequency>1728)))
++ if (dev->curtuner == 0 && (f->frequency < 1400 || f->frequency > 1728))
+ return -EINVAL;
+- if((curtuner==1)&&((f->frequency<8320)||(f->frequency>26400)))
++ if (dev->curtuner == 1 && (f->frequency < 8320 || f->frequency > 26400))
+ return -EINVAL;
+- cadet_setfreq(f->frequency);
++ cadet_setfreq(dev, f->frequency);
+ return 0;
+ }
+
+ static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+ {
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+- if (qc->id && qc->id == radio_qctrl[i].id) {
+- memcpy(qc, &(radio_qctrl[i]),
+- sizeof(*qc));
+- return 0;
+- }
++ switch (qc->id) {
++ case V4L2_CID_AUDIO_MUTE:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
++ case V4L2_CID_AUDIO_VOLUME:
++ return v4l2_ctrl_query_fill(qc, 0, 0xff, 1, 0xff);
+ }
+ return -EINVAL;
+ }
+@@ -465,12 +455,14 @@ static int vidioc_queryctrl(struct file *file, void *priv,
+ static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- switch (ctrl->id){
++ struct cadet *dev = video_drvdata(file);
++
++ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */
+- ctrl->value = (cadet_getvol() == 0);
++ ctrl->value = (cadet_getvol(dev) == 0);
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+- ctrl->value = cadet_getvol();
++ ctrl->value = cadet_getvol(dev);
+ break;
+ default:
+ return -EINVAL;
+@@ -481,15 +473,17 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
+ static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
++ struct cadet *dev = video_drvdata(file);
++
+ switch (ctrl->id){
+ case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */
+ if (ctrl->value)
+- cadet_setvol(0);
++ cadet_setvol(dev, 0);
+ else
+- cadet_setvol(0xffff);
++ cadet_setvol(dev, 0xffff);
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+- cadet_setvol(ctrl->value);
++ cadet_setvol(dev, ctrl->value);
+ break;
+ default:
+ return -EINVAL;
+@@ -497,16 +491,6 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
+ return 0;
+ }
+
+-static int vidioc_g_audio(struct file *file, void *priv,
+- struct v4l2_audio *a)
+-{
+- if (a->index > 1)
+- return -EINVAL;
+- strcpy(a->name, "Radio");
+- a->capability = V4L2_AUDCAP_STEREO;
+- return 0;
+-}
+-
+ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+ {
+ *i = 0;
+@@ -515,43 +499,52 @@ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+
+ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+ {
+- if (i != 0)
+- return -EINVAL;
++ return i ? -EINVAL : 0;
++}
++
++static int vidioc_g_audio(struct file *file, void *priv,
++ struct v4l2_audio *a)
++{
++ a->index = 0;
++ strlcpy(a->name, "Radio", sizeof(a->name));
++ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+ }
+
+ static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+ {
+- if (a->index != 0)
+- return -EINVAL;
+- return 0;
++ return a->index ? -EINVAL : 0;
+ }
+
+-static int
+-cadet_open(struct file *file)
++static int cadet_open(struct file *file)
+ {
+- users++;
+- if (1 == users) init_waitqueue_head(&read_queue);
++ struct cadet *dev = video_drvdata(file);
++
++ dev->users++;
++ if (1 == dev->users)
++ init_waitqueue_head(&dev->read_queue);
+ return 0;
+ }
+
+-static int
+-cadet_release(struct file *file)
++static int cadet_release(struct file *file)
+ {
+- users--;
+- if (0 == users){
+- del_timer_sync(&readtimer);
+- rdsstat=0;
++ struct cadet *dev = video_drvdata(file);
++
++ dev->users--;
++ if (0 == dev->users) {
++ del_timer_sync(&dev->readtimer);
++ dev->rdsstat = 0;
+ }
+ return 0;
+ }
+
+-static unsigned int
+-cadet_poll(struct file *file, struct poll_table_struct *wait)
++static unsigned int cadet_poll(struct file *file, struct poll_table_struct *wait)
+ {
+- poll_wait(file,&read_queue,wait);
+- if(rdsin != rdsout)
++ struct cadet *dev = video_drvdata(file);
++
++ poll_wait(file, &dev->read_queue, wait);
++ if (dev->rdsin != dev->rdsout)
+ return POLLIN | POLLRDNORM;
+ return 0;
+ }
+@@ -581,13 +574,6 @@ static const struct v4l2_ioctl_ops cadet_ioctl_ops = {
+ .vidioc_s_input = vidioc_s_input,
+ };
+
+-static struct video_device cadet_radio = {
+- .name = "Cadet radio",
+- .fops = &cadet_fops,
+- .ioctl_ops = &cadet_ioctl_ops,
+- .release = video_device_release_empty,
+-};
+-
+ #ifdef CONFIG_PNP
+
+ static struct pnp_device_id cadet_pnp_devices[] = {
+@@ -598,7 +584,7 @@ static struct pnp_device_id cadet_pnp_devices[] = {
+
+ MODULE_DEVICE_TABLE(pnp, cadet_pnp_devices);
+
+-static int cadet_pnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev_id)
++static int cadet_pnp_probe(struct pnp_dev *dev, const struct pnp_device_id *dev_id)
+ {
+ if (!dev)
+ return -ENODEV;
+@@ -606,13 +592,12 @@ static int cadet_pnp_probe(struct pnp_dev * dev, const struct pnp_device_id *dev
+ if (io > 0)
+ return -EBUSY;
+
+- if (!pnp_port_valid(dev, 0)) {
++ if (!pnp_port_valid(dev, 0))
+ return -ENODEV;
+- }
+
+ io = pnp_port_start(dev, 0);
+
+- printk ("radio-cadet: PnP reports device at %#x\n", io);
++ printk(KERN_INFO "radio-cadet: PnP reports device at %#x\n", io);
+
+ return io;
+ }
+@@ -628,23 +613,23 @@ static struct pnp_driver cadet_pnp_driver = {
+ static struct pnp_driver cadet_pnp_driver;
+ #endif
+
+-static int cadet_probe(void)
++static void cadet_probe(struct cadet *dev)
+ {
+- static int iovals[8]={0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e};
++ static int iovals[8] = { 0x330, 0x332, 0x334, 0x336, 0x338, 0x33a, 0x33c, 0x33e };
+ int i;
+
+- for(i=0;i<8;i++) {
+- io=iovals[i];
+- if (request_region(io, 2, "cadet-probe")) {
+- cadet_setfreq(1410);
+- if(cadet_getfreq()==1410) {
+- release_region(io, 2);
+- return io;
++ for (i = 0; i < 8; i++) {
++ dev->io = iovals[i];
++ if (request_region(dev->io, 2, "cadet-probe")) {
++ cadet_setfreq(dev, 1410);
++ if (cadet_getfreq(dev) == 1410) {
++ release_region(dev->io, 2);
++ return;
+ }
+- release_region(io, 2);
++ release_region(dev->io, 2);
+ }
+ }
+- return -1;
++ dev->io = -1;
+ }
+
+ /*
+@@ -654,59 +639,69 @@ static int cadet_probe(void)
+
+ static int __init cadet_init(void)
+ {
+- spin_lock_init(&cadet_io_lock);
++ struct cadet *dev = &cadet_card;
++ struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
++ int res;
+
+- /*
+- * If a probe was requested then probe ISAPnP first (safest)
+- */
++ strlcpy(v4l2_dev->name, "cadet", sizeof(v4l2_dev->name));
++ mutex_init(&dev->lock);
++
++ /* If a probe was requested then probe ISAPnP first (safest) */
+ if (io < 0)
+ pnp_register_driver(&cadet_pnp_driver);
+- /*
+- * If that fails then probe unsafely if probe is requested
+- */
+- if(io < 0)
+- io = cadet_probe ();
++ dev->io = io;
+
+- /*
+- * Else we bail out
+- */
++ /* If that fails then probe unsafely if probe is requested */
++ if (dev->io < 0)
++ cadet_probe(dev);
+
+- if(io < 0) {
++ /* Else we bail out */
++ if (dev->io < 0) {
+ #ifdef MODULE
+- printk(KERN_ERR "You must set an I/O address with io=0x???\n");
++ v4l2_err(v4l2_dev, "you must set an I/O address with io=0x330, 0x332, 0x334,\n");
++ v4l2_err(v4l2_dev, "0x336, 0x338, 0x33a, 0x33c or 0x33e\n");
+ #endif
+ goto fail;
+ }
+- if (!request_region(io,2,"cadet"))
++ if (!request_region(dev->io, 2, "cadet"))
++ goto fail;
++
++ res = v4l2_device_register(NULL, v4l2_dev);
++ if (res < 0) {
++ release_region(dev->io, 2);
++ v4l2_err(v4l2_dev, "could not register v4l2_device\n");
+ goto fail;
+- if (video_register_device(&cadet_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
+- release_region(io,2);
++ }
++
++ strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
++ dev->vdev.v4l2_dev = v4l2_dev;
++ dev->vdev.fops = &cadet_fops;
++ dev->vdev.ioctl_ops = &cadet_ioctl_ops;
++ dev->vdev.release = video_device_release_empty;
++ video_set_drvdata(&dev->vdev, dev);
++
++ if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
++ v4l2_device_unregister(v4l2_dev);
++ release_region(dev->io, 2);
+ goto fail;
+ }
+- printk(KERN_INFO "ADS Cadet Radio Card at 0x%x\n",io);
++ v4l2_info(v4l2_dev, "ADS Cadet Radio Card at 0x%x\n", dev->io);
+ return 0;
+ fail:
+ pnp_unregister_driver(&cadet_pnp_driver);
+- return -1;
++ return -ENODEV;
+ }
+
+-
+-
+-MODULE_AUTHOR("Fred Gleason, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
+-MODULE_DESCRIPTION("A driver for the ADS Cadet AM/FM/RDS radio card.");
+-MODULE_LICENSE("GPL");
+-
+-module_param(io, int, 0);
+-MODULE_PARM_DESC(io, "I/O address of Cadet card (0x330,0x332,0x334,0x336,0x338,0x33a,0x33c,0x33e)");
+-module_param(radio_nr, int, 0);
+-
+-static void __exit cadet_cleanup_module(void)
++static void __exit cadet_exit(void)
+ {
+- video_unregister_device(&cadet_radio);
+- release_region(io,2);
++ struct cadet *dev = &cadet_card;
++
++ video_unregister_device(&dev->vdev);
++ v4l2_device_unregister(&dev->v4l2_dev);
++ release_region(dev->io, 2);
+ pnp_unregister_driver(&cadet_pnp_driver);
+ }
+
+ module_init(cadet_init);
+-module_exit(cadet_cleanup_module);
++module_exit(cadet_exit);
+
+diff --git a/drivers/media/radio/radio-gemtek-pci.c b/drivers/media/radio/radio-gemtek-pci.c
+index 0c96bf8..09265d2 100644
+--- a/drivers/media/radio/radio-gemtek-pci.c
++++ b/drivers/media/radio/radio-gemtek-pci.c
+@@ -45,34 +45,25 @@
+ #include <linux/init.h>
+ #include <linux/pci.h>
+ #include <linux/videodev2.h>
+-#include <media/v4l2-common.h>
+-#include <media/v4l2-ioctl.h>
+ #include <linux/errno.h>
+-
+ #include <linux/version.h> /* for KERNEL_VERSION MACRO */
+-#define RADIO_VERSION KERNEL_VERSION(0,0,2)
+-
+-static struct v4l2_queryctrl radio_qctrl[] = {
+- {
+- .id = V4L2_CID_AUDIO_MUTE,
+- .name = "Mute",
+- .minimum = 0,
+- .maximum = 1,
+- .default_value = 1,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- },{
+- .id = V4L2_CID_AUDIO_VOLUME,
+- .name = "Volume",
+- .minimum = 0,
+- .maximum = 65535,
+- .step = 65535,
+- .default_value = 0xff,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- }
+-};
++#include <linux/io.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-ioctl.h>
+
+-#include <asm/io.h>
+-#include <asm/uaccess.h>
++MODULE_AUTHOR("Vladimir Shebordaev <vshebordaev@mail.ru>");
++MODULE_DESCRIPTION("The video4linux driver for the Gemtek PCI Radio Card");
++MODULE_LICENSE("GPL");
++
++static int nr_radio = -1;
++static int mx = 1;
++
++module_param(mx, bool, 0);
++MODULE_PARM_DESC(mx, "single digit: 1 - turn off the turner upon module exit (default), 0 - do not");
++module_param(nr_radio, int, 0);
++MODULE_PARM_DESC(nr_radio, "video4linux device number to use");
++
++#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
+
+ #ifndef PCI_VENDOR_ID_GEMTEK
+ #define PCI_VENDOR_ID_GEMTEK 0x5046
+@@ -90,8 +81,11 @@ static struct v4l2_queryctrl radio_qctrl[] = {
+ #define GEMTEK_PCI_RANGE_HIGH (108*16000)
+ #endif
+
+-struct gemtek_pci_card {
+- struct video_device *videodev;
++struct gemtek_pci {
++ struct v4l2_device v4l2_dev;
++ struct video_device vdev;
++ struct mutex lock;
++ struct pci_dev *pdev;
+
+ u32 iobase;
+ u32 length;
+@@ -100,116 +94,133 @@ struct gemtek_pci_card {
+ u8 mute;
+ };
+
+-static int nr_radio = -1;
+-static unsigned long in_use;
++static inline struct gemtek_pci *to_gemtek_pci(struct v4l2_device *v4l2_dev)
++{
++ return container_of(v4l2_dev, struct gemtek_pci, v4l2_dev);
++}
+
+-static inline u8 gemtek_pci_out( u16 value, u32 port )
++static inline u8 gemtek_pci_out(u16 value, u32 port)
+ {
+- outw( value, port );
++ outw(value, port);
+
+ return (u8)value;
+ }
+
+-#define _b0( v ) *((u8 *)&v)
+-static void __gemtek_pci_cmd( u16 value, u32 port, u8 *last_byte, int keep )
++#define _b0(v) (*((u8 *)&v))
++
++static void __gemtek_pci_cmd(u16 value, u32 port, u8 *last_byte, int keep)
+ {
+- register u8 byte = *last_byte;
++ u8 byte = *last_byte;
+
+- if ( !value ) {
+- if ( !keep )
++ if (!value) {
++ if (!keep)
+ value = (u16)port;
+ byte &= 0xfd;
+ } else
+ byte |= 2;
+
+- _b0( value ) = byte;
+- outw( value, port );
++ _b0(value) = byte;
++ outw(value, port);
+ byte |= 1;
+- _b0( value ) = byte;
+- outw( value, port );
++ _b0(value) = byte;
++ outw(value, port);
+ byte &= 0xfe;
+- _b0( value ) = byte;
+- outw( value, port );
++ _b0(value) = byte;
++ outw(value, port);
+
+ *last_byte = byte;
+ }
+
+-static inline void gemtek_pci_nil( u32 port, u8 *last_byte )
++static inline void gemtek_pci_nil(u32 port, u8 *last_byte)
+ {
+- __gemtek_pci_cmd( 0x00, port, last_byte, false );
++ __gemtek_pci_cmd(0x00, port, last_byte, false);
+ }
+
+-static inline void gemtek_pci_cmd( u16 cmd, u32 port, u8 *last_byte )
++static inline void gemtek_pci_cmd(u16 cmd, u32 port, u8 *last_byte)
+ {
+- __gemtek_pci_cmd( cmd, port, last_byte, true );
++ __gemtek_pci_cmd(cmd, port, last_byte, true);
+ }
+
+-static void gemtek_pci_setfrequency( struct gemtek_pci_card *card, unsigned long frequency )
++static void gemtek_pci_setfrequency(struct gemtek_pci *card, unsigned long frequency)
+ {
+- register int i;
+- register u32 value = frequency / 200 + 856;
+- register u16 mask = 0x8000;
++ int i;
++ u32 value = frequency / 200 + 856;
++ u16 mask = 0x8000;
+ u8 last_byte;
+ u32 port = card->iobase;
+
+- last_byte = gemtek_pci_out( 0x06, port );
++ mutex_lock(&card->lock);
++ card->current_frequency = frequency;
++ last_byte = gemtek_pci_out(0x06, port);
+
+ i = 0;
+ do {
+- gemtek_pci_nil( port, &last_byte );
++ gemtek_pci_nil(port, &last_byte);
+ i++;
+- } while ( i < 9 );
++ } while (i < 9);
+
+ i = 0;
+ do {
+- gemtek_pci_cmd( value & mask, port, &last_byte );
++ gemtek_pci_cmd(value & mask, port, &last_byte);
+ mask >>= 1;
+ i++;
+- } while ( i < 16 );
++ } while (i < 16);
+
+- outw( 0x10, port );
++ outw(0x10, port);
++ mutex_unlock(&card->lock);
+ }
+
+
+-static inline void gemtek_pci_mute( struct gemtek_pci_card *card )
++static void gemtek_pci_mute(struct gemtek_pci *card)
+ {
+- outb( 0x1f, card->iobase );
++ mutex_lock(&card->lock);
++ outb(0x1f, card->iobase);
+ card->mute = true;
++ mutex_unlock(&card->lock);
+ }
+
+-static inline void gemtek_pci_unmute( struct gemtek_pci_card *card )
++static void gemtek_pci_unmute(struct gemtek_pci *card)
+ {
+- if ( card->mute ) {
+- gemtek_pci_setfrequency( card, card->current_frequency );
++ mutex_lock(&card->lock);
++ if (card->mute) {
++ gemtek_pci_setfrequency(card, card->current_frequency);
+ card->mute = false;
+ }
++ mutex_unlock(&card->lock);
+ }
+
+-static inline unsigned int gemtek_pci_getsignal( struct gemtek_pci_card *card )
++static int gemtek_pci_getsignal(struct gemtek_pci *card)
+ {
+- return ( inb( card->iobase ) & 0x08 ) ? 0 : 1;
++ int sig;
++
++ mutex_lock(&card->lock);
++ sig = (inb(card->iobase) & 0x08) ? 0 : 1;
++ mutex_unlock(&card->lock);
++ return sig;
+ }
+
+ static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+ {
++ struct gemtek_pci *card = video_drvdata(file);
++
+ strlcpy(v->driver, "radio-gemtek-pci", sizeof(v->driver));
+ strlcpy(v->card, "GemTek PCI Radio", sizeof(v->card));
+- sprintf(v->bus_info, "ISA");
++ snprintf(v->bus_info, sizeof(v->bus_info), "PCI:%s", pci_name(card->pdev));
+ v->version = RADIO_VERSION;
+- v->capabilities = V4L2_CAP_TUNER;
++ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+ }
+
+ static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- struct gemtek_pci_card *card = video_drvdata(file);
++ struct gemtek_pci *card = video_drvdata(file);
+
+ if (v->index > 0)
+ return -EINVAL;
+
+- strcpy(v->name, "FM");
++ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+ v->rangelow = GEMTEK_PCI_RANGE_LOW;
+ v->rangehigh = GEMTEK_PCI_RANGE_HIGH;
+@@ -223,21 +234,18 @@ static int vidioc_g_tuner(struct file *file, void *priv,
+ static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- if (v->index > 0)
+- return -EINVAL;
+- return 0;
++ return v->index ? -EINVAL : 0;
+ }
+
+ static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct gemtek_pci_card *card = video_drvdata(file);
++ struct gemtek_pci *card = video_drvdata(file);
+
+- if ( (f->frequency < GEMTEK_PCI_RANGE_LOW) ||
+- (f->frequency > GEMTEK_PCI_RANGE_HIGH) )
++ if (f->frequency < GEMTEK_PCI_RANGE_LOW ||
++ f->frequency > GEMTEK_PCI_RANGE_HIGH)
+ return -EINVAL;
+ gemtek_pci_setfrequency(card, f->frequency);
+- card->current_frequency = f->frequency;
+ card->mute = false;
+ return 0;
+ }
+@@ -245,7 +253,7 @@ static int vidioc_s_frequency(struct file *file, void *priv,
+ static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct gemtek_pci_card *card = video_drvdata(file);
++ struct gemtek_pci *card = video_drvdata(file);
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = card->current_frequency;
+@@ -255,13 +263,11 @@ static int vidioc_g_frequency(struct file *file, void *priv,
+ static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+ {
+- int i;
+- for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+- if (qc->id && qc->id == radio_qctrl[i].id) {
+- memcpy(qc, &(radio_qctrl[i]),
+- sizeof(*qc));
+- return 0;
+- }
++ switch (qc->id) {
++ case V4L2_CID_AUDIO_MUTE:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
++ case V4L2_CID_AUDIO_VOLUME:
++ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535, 65535);
+ }
+ return -EINVAL;
+ }
+@@ -269,7 +275,7 @@ static int vidioc_queryctrl(struct file *file, void *priv,
+ static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct gemtek_pci_card *card = video_drvdata(file);
++ struct gemtek_pci *card = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+@@ -288,7 +294,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
+ static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct gemtek_pci_card *card = video_drvdata(file);
++ struct gemtek_pci *card = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+@@ -307,17 +313,6 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
+ return -EINVAL;
+ }
+
+-static int vidioc_g_audio(struct file *file, void *priv,
+- struct v4l2_audio *a)
+-{
+- if (a->index > 1)
+- return -EINVAL;
+-
+- strcpy(a->name, "Radio");
+- a->capability = V4L2_AUDCAP_STEREO;
+- return 0;
+-}
+-
+ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+ {
+ *i = 0;
+@@ -326,17 +321,22 @@ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+
+ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+ {
+- if (i != 0)
+- return -EINVAL;
++ return i ? -EINVAL : 0;
++}
++
++static int vidioc_g_audio(struct file *file, void *priv,
++ struct v4l2_audio *a)
++{
++ a->index = 0;
++ strlcpy(a->name, "Radio", sizeof(a->name));
++ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+ }
+
+ static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+ {
+- if (a->index != 0)
+- return -EINVAL;
+- return 0;
++ return a->index ? -EINVAL : 0;
+ }
+
+ enum {
+@@ -354,25 +354,22 @@ static struct pci_device_id gemtek_pci_id[] =
+ { 0 }
+ };
+
+-MODULE_DEVICE_TABLE( pci, gemtek_pci_id );
+-
+-static int mx = 1;
++MODULE_DEVICE_TABLE(pci, gemtek_pci_id);
+
+-static int gemtek_pci_exclusive_open(struct file *file)
++static int gemtek_pci_open(struct file *file)
+ {
+- return test_and_set_bit(0, &in_use) ? -EBUSY : 0;
++ return 0;
+ }
+
+-static int gemtek_pci_exclusive_release(struct file *file)
++static int gemtek_pci_release(struct file *file)
+ {
+- clear_bit(0, &in_use);
+ return 0;
+ }
+
+ static const struct v4l2_file_operations gemtek_pci_fops = {
+ .owner = THIS_MODULE,
+- .open = gemtek_pci_exclusive_open,
+- .release = gemtek_pci_exclusive_release,
++ .open = gemtek_pci_open,
++ .release = gemtek_pci_release,
+ .ioctl = video_ioctl2,
+ };
+
+@@ -391,108 +388,100 @@ static const struct v4l2_ioctl_ops gemtek_pci_ioctl_ops = {
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ };
+
+-static struct video_device vdev_template = {
+- .name = "Gemtek PCI Radio",
+- .fops = &gemtek_pci_fops,
+- .ioctl_ops = &gemtek_pci_ioctl_ops,
+- .release = video_device_release_empty,
+-};
+-
+-static int __devinit gemtek_pci_probe( struct pci_dev *pci_dev, const struct pci_device_id *pci_id )
++static int __devinit gemtek_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id)
+ {
+- struct gemtek_pci_card *card;
+- struct video_device *devradio;
++ struct gemtek_pci *card;
++ struct v4l2_device *v4l2_dev;
++ int res;
+
+- if ( (card = kzalloc( sizeof( struct gemtek_pci_card ), GFP_KERNEL )) == NULL ) {
+- printk( KERN_ERR "gemtek_pci: out of memory\n" );
++ card = kzalloc(sizeof(struct gemtek_pci), GFP_KERNEL);
++ if (card == NULL) {
++ dev_err(&pdev->dev, "out of memory\n");
+ return -ENOMEM;
+ }
+
+- if ( pci_enable_device( pci_dev ) )
+- goto err_pci;
++ v4l2_dev = &card->v4l2_dev;
++ mutex_init(&card->lock);
++ card->pdev = pdev;
+
+- card->iobase = pci_resource_start( pci_dev, 0 );
+- card->length = pci_resource_len( pci_dev, 0 );
++ strlcpy(v4l2_dev->name, "gemtek_pci", sizeof(v4l2_dev->name));
+
+- if ( request_region( card->iobase, card->length, card_names[pci_id->driver_data] ) == NULL ) {
+- printk( KERN_ERR "gemtek_pci: i/o port already in use\n" );
+- goto err_pci;
++ res = v4l2_device_register(&pdev->dev, v4l2_dev);
++ if (res < 0) {
++ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
++ kfree(card);
++ return res;
+ }
+
+- pci_set_drvdata( pci_dev, card );
++ if (pci_enable_device(pdev))
++ goto err_pci;
+
+- if ( (devradio = kmalloc( sizeof( struct video_device ), GFP_KERNEL )) == NULL ) {
+- printk( KERN_ERR "gemtek_pci: out of memory\n" );
+- goto err_video;
++ card->iobase = pci_resource_start(pdev, 0);
++ card->length = pci_resource_len(pdev, 0);
++
++ if (request_region(card->iobase, card->length, card_names[pci_id->driver_data]) == NULL) {
++ v4l2_err(v4l2_dev, "i/o port already in use\n");
++ goto err_pci;
+ }
+- *devradio = vdev_template;
+
+- if (video_register_device(devradio, VFL_TYPE_RADIO, nr_radio) < 0) {
+- kfree( devradio );
++ strlcpy(card->vdev.name, v4l2_dev->name, sizeof(card->vdev.name));
++ card->vdev.v4l2_dev = v4l2_dev;
++ card->vdev.fops = &gemtek_pci_fops;
++ card->vdev.ioctl_ops = &gemtek_pci_ioctl_ops;
++ card->vdev.release = video_device_release_empty;
++ video_set_drvdata(&card->vdev, card);
++
++ if (video_register_device(&card->vdev, VFL_TYPE_RADIO, nr_radio) < 0)
+ goto err_video;
+- }
+
+- card->videodev = devradio;
+- video_set_drvdata(devradio, card);
+- gemtek_pci_mute( card );
++ gemtek_pci_mute(card);
+
+- printk( KERN_INFO "Gemtek PCI Radio (rev. %d) found at 0x%04x-0x%04x.\n",
+- pci_dev->revision, card->iobase, card->iobase + card->length - 1 );
++ v4l2_info(v4l2_dev, "Gemtek PCI Radio (rev. %d) found at 0x%04x-0x%04x.\n",
++ pdev->revision, card->iobase, card->iobase + card->length - 1);
+
+ return 0;
+
+ err_video:
+- release_region( card->iobase, card->length );
++ release_region(card->iobase, card->length);
+
+ err_pci:
+- kfree( card );
++ v4l2_device_unregister(v4l2_dev);
++ kfree(card);
+ return -ENODEV;
+ }
+
+-static void __devexit gemtek_pci_remove( struct pci_dev *pci_dev )
++static void __devexit gemtek_pci_remove(struct pci_dev *pdev)
+ {
+- struct gemtek_pci_card *card = pci_get_drvdata( pci_dev );
++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
++ struct gemtek_pci *card = to_gemtek_pci(v4l2_dev);
+
+- video_unregister_device( card->videodev );
+- kfree( card->videodev );
++ video_unregister_device(&card->vdev);
++ v4l2_device_unregister(v4l2_dev);
+
+- release_region( card->iobase, card->length );
++ release_region(card->iobase, card->length);
+
+- if ( mx )
+- gemtek_pci_mute( card );
++ if (mx)
++ gemtek_pci_mute(card);
+
+- kfree( card );
+-
+- pci_set_drvdata( pci_dev, NULL );
++ kfree(card);
+ }
+
+-static struct pci_driver gemtek_pci_driver =
+-{
++static struct pci_driver gemtek_pci_driver = {
+ .name = "gemtek_pci",
+ .id_table = gemtek_pci_id,
+ .probe = gemtek_pci_probe,
+ .remove = __devexit_p(gemtek_pci_remove),
+ };
+
+-static int __init gemtek_pci_init_module( void )
++static int __init gemtek_pci_init(void)
+ {
+- return pci_register_driver( &gemtek_pci_driver );
++ return pci_register_driver(&gemtek_pci_driver);
+ }
+
+-static void __exit gemtek_pci_cleanup_module( void )
++static void __exit gemtek_pci_exit(void)
+ {
+ pci_unregister_driver(&gemtek_pci_driver);
+ }
+
+-MODULE_AUTHOR( "Vladimir Shebordaev <vshebordaev@mail.ru>" );
+-MODULE_DESCRIPTION( "The video4linux driver for the Gemtek PCI Radio Card" );
+-MODULE_LICENSE("GPL");
+-
+-module_param(mx, bool, 0);
+-MODULE_PARM_DESC( mx, "single digit: 1 - turn off the turner upon module exit (default), 0 - do not" );
+-module_param(nr_radio, int, 0);
+-MODULE_PARM_DESC( nr_radio, "video4linux device number to use");
+-
+-module_init( gemtek_pci_init_module );
+-module_exit( gemtek_pci_cleanup_module );
+-
++module_init(gemtek_pci_init);
++module_exit(gemtek_pci_exit);
+diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c
+index 2b68be7..1504644 100644
+--- a/drivers/media/radio/radio-gemtek.c
++++ b/drivers/media/radio/radio-gemtek.c
+@@ -20,16 +20,14 @@
+ #include <linux/init.h> /* Initdata */
+ #include <linux/ioport.h> /* request_region */
+ #include <linux/delay.h> /* udelay */
+-#include <asm/io.h> /* outb, outb_p */
+-#include <asm/uaccess.h> /* copy to/from user */
+ #include <linux/videodev2.h> /* kernel radio structs */
++#include <linux/version.h> /* for KERNEL_VERSION MACRO */
++#include <linux/mutex.h>
++#include <linux/io.h> /* outb, outb_p */
+ #include <media/v4l2-ioctl.h>
+-#include <media/v4l2-common.h>
+-#include <linux/spinlock.h>
++#include <media/v4l2-device.h>
+
+-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+-#define RADIO_VERSION KERNEL_VERSION(0,0,3)
+-#define RADIO_BANNER "GemTek Radio card driver: v0.0.3"
++#define RADIO_VERSION KERNEL_VERSION(0, 0, 3)
+
+ /*
+ * Module info.
+@@ -57,7 +55,6 @@ static int shutdown = 1;
+ static int keepmuted = 1;
+ static int initmute = 1;
+ static int radio_nr = -1;
+-static unsigned long in_use;
+
+ module_param(io, int, 0444);
+ MODULE_PARM_DESC(io, "Force I/O port for the GemTek Radio card if automatic "
+@@ -112,12 +109,19 @@ module_param(radio_nr, int, 0444);
+ #define SHORT_DELAY 5 /* usec */
+ #define LONG_DELAY 75 /* usec */
+
+-struct gemtek_device {
++struct gemtek {
++ struct v4l2_device v4l2_dev;
++ struct video_device vdev;
++ struct mutex lock;
+ unsigned long lastfreq;
+ int muted;
++ int verified;
++ int io;
+ u32 bu2614data;
+ };
+
++static struct gemtek gemtek_card;
++
+ #define BU2614_FREQ_BITS 16 /* D0..D15, Frequency data */
+ #define BU2614_PORT_BITS 3 /* P0..P2, Output port control data */
+ #define BU2614_VOID_BITS 4 /* unused */
+@@ -153,10 +157,6 @@ struct gemtek_device {
+ #define BU2614_FMUN_MASK MKMASK(FMUN)
+ #define BU2614_TEST_MASK MKMASK(TEST)
+
+-static struct gemtek_device gemtek_unit;
+-
+-static spinlock_t lock;
+-
+ /*
+ * Set data which will be sent to BU2614FS.
+ */
+@@ -166,33 +166,33 @@ static spinlock_t lock;
+ /*
+ * Transmit settings to BU2614FS over GemTek IC.
+ */
+-static void gemtek_bu2614_transmit(struct gemtek_device *dev)
++static void gemtek_bu2614_transmit(struct gemtek *gt)
+ {
+ int i, bit, q, mute;
+
+- spin_lock(&lock);
++ mutex_lock(&gt->lock);
+
+- mute = dev->muted ? GEMTEK_MT : 0x00;
++ mute = gt->muted ? GEMTEK_MT : 0x00;
+
+- outb_p(mute | GEMTEK_DA | GEMTEK_CK, io);
++ outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io);
+ udelay(SHORT_DELAY);
+- outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, io);
++ outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io);
+ udelay(LONG_DELAY);
+
+- for (i = 0, q = dev->bu2614data; i < 32; i++, q >>= 1) {
+- bit = (q & 1) ? GEMTEK_DA : 0;
+- outb_p(mute | GEMTEK_CE | bit, io);
+- udelay(SHORT_DELAY);
+- outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, io);
+- udelay(SHORT_DELAY);
++ for (i = 0, q = gt->bu2614data; i < 32; i++, q >>= 1) {
++ bit = (q & 1) ? GEMTEK_DA : 0;
++ outb_p(mute | GEMTEK_CE | bit, gt->io);
++ udelay(SHORT_DELAY);
++ outb_p(mute | GEMTEK_CE | bit | GEMTEK_CK, gt->io);
++ udelay(SHORT_DELAY);
+ }
+
+- outb_p(mute | GEMTEK_DA | GEMTEK_CK, io);
++ outb_p(mute | GEMTEK_DA | GEMTEK_CK, gt->io);
+ udelay(SHORT_DELAY);
+- outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, io);
++ outb_p(mute | GEMTEK_CE | GEMTEK_DA | GEMTEK_CK, gt->io);
+ udelay(LONG_DELAY);
+
+- spin_unlock(&lock);
++ mutex_unlock(&gt->lock);
+ }
+
+ /*
+@@ -206,107 +206,109 @@ static unsigned long gemtek_convfreq(unsigned long freq)
+ /*
+ * Set FM-frequency.
+ */
+-static void gemtek_setfreq(struct gemtek_device *dev, unsigned long freq)
++static void gemtek_setfreq(struct gemtek *gt, unsigned long freq)
+ {
+-
+- if (keepmuted && hardmute && dev->muted)
++ if (keepmuted && hardmute && gt->muted)
+ return;
+
+- if (freq < GEMTEK_LOWFREQ)
+- freq = GEMTEK_LOWFREQ;
+- else if (freq > GEMTEK_HIGHFREQ)
+- freq = GEMTEK_HIGHFREQ;
++ freq = clamp_val(freq, GEMTEK_LOWFREQ, GEMTEK_HIGHFREQ);
+
+- dev->lastfreq = freq;
+- dev->muted = 0;
++ gt->lastfreq = freq;
++ gt->muted = 0;
+
+- gemtek_bu2614_set(dev, BU2614_PORT, 0);
+- gemtek_bu2614_set(dev, BU2614_FMES, 0);
+- gemtek_bu2614_set(dev, BU2614_SWIN, 0); /* FM-mode */
+- gemtek_bu2614_set(dev, BU2614_SWAL, 0);
+- gemtek_bu2614_set(dev, BU2614_FMUN, 1); /* GT bit set */
+- gemtek_bu2614_set(dev, BU2614_TEST, 0);
++ gemtek_bu2614_set(gt, BU2614_PORT, 0);
++ gemtek_bu2614_set(gt, BU2614_FMES, 0);
++ gemtek_bu2614_set(gt, BU2614_SWIN, 0); /* FM-mode */
++ gemtek_bu2614_set(gt, BU2614_SWAL, 0);
++ gemtek_bu2614_set(gt, BU2614_FMUN, 1); /* GT bit set */
++ gemtek_bu2614_set(gt, BU2614_TEST, 0);
+
+- gemtek_bu2614_set(dev, BU2614_STDF, GEMTEK_STDF_3_125_KHZ);
+- gemtek_bu2614_set(dev, BU2614_FREQ, gemtek_convfreq(freq));
++ gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_STDF_3_125_KHZ);
++ gemtek_bu2614_set(gt, BU2614_FREQ, gemtek_convfreq(freq));
+
+- gemtek_bu2614_transmit(dev);
++ gemtek_bu2614_transmit(gt);
+ }
+
+ /*
+ * Set mute flag.
+ */
+-static void gemtek_mute(struct gemtek_device *dev)
++static void gemtek_mute(struct gemtek *gt)
+ {
+ int i;
+- dev->muted = 1;
++
++ gt->muted = 1;
+
+ if (hardmute) {
+ /* Turn off PLL, disable data output */
+- gemtek_bu2614_set(dev, BU2614_PORT, 0);
+- gemtek_bu2614_set(dev, BU2614_FMES, 0); /* CT bit off */
+- gemtek_bu2614_set(dev, BU2614_SWIN, 0); /* FM-mode */
+- gemtek_bu2614_set(dev, BU2614_SWAL, 0);
+- gemtek_bu2614_set(dev, BU2614_FMUN, 0); /* GT bit off */
+- gemtek_bu2614_set(dev, BU2614_TEST, 0);
+- gemtek_bu2614_set(dev, BU2614_STDF, GEMTEK_PLL_OFF);
+- gemtek_bu2614_set(dev, BU2614_FREQ, 0);
+- gemtek_bu2614_transmit(dev);
+- } else {
+- spin_lock(&lock);
++ gemtek_bu2614_set(gt, BU2614_PORT, 0);
++ gemtek_bu2614_set(gt, BU2614_FMES, 0); /* CT bit off */
++ gemtek_bu2614_set(gt, BU2614_SWIN, 0); /* FM-mode */
++ gemtek_bu2614_set(gt, BU2614_SWAL, 0);
++ gemtek_bu2614_set(gt, BU2614_FMUN, 0); /* GT bit off */
++ gemtek_bu2614_set(gt, BU2614_TEST, 0);
++ gemtek_bu2614_set(gt, BU2614_STDF, GEMTEK_PLL_OFF);
++ gemtek_bu2614_set(gt, BU2614_FREQ, 0);
++ gemtek_bu2614_transmit(gt);
++ return;
++ }
+
+- /* Read bus contents (CE, CK and DA). */
+- i = inb_p(io);
+- /* Write it back with mute flag set. */
+- outb_p((i >> 5) | GEMTEK_MT, io);
+- udelay(SHORT_DELAY);
++ mutex_lock(&gt->lock);
+
+- spin_unlock(&lock);
+- }
++ /* Read bus contents (CE, CK and DA). */
++ i = inb_p(gt->io);
++ /* Write it back with mute flag set. */
++ outb_p((i >> 5) | GEMTEK_MT, gt->io);
++ udelay(SHORT_DELAY);
++
++ mutex_unlock(&gt->lock);
+ }
+
+ /*
+ * Unset mute flag.
+ */
+-static void gemtek_unmute(struct gemtek_device *dev)
++static void gemtek_unmute(struct gemtek *gt)
+ {
+ int i;
+- dev->muted = 0;
+
++ gt->muted = 0;
+ if (hardmute) {
+ /* Turn PLL back on. */
+- gemtek_setfreq(dev, dev->lastfreq);
+- } else {
+- spin_lock(&lock);
++ gemtek_setfreq(gt, gt->lastfreq);
++ return;
++ }
++ mutex_lock(&gt->lock);
+
+- i = inb_p(io);
+- outb_p(i >> 5, io);
+- udelay(SHORT_DELAY);
++ i = inb_p(gt->io);
++ outb_p(i >> 5, gt->io);
++ udelay(SHORT_DELAY);
+
+- spin_unlock(&lock);
+- }
++ mutex_unlock(&gt->lock);
+ }
+
+ /*
+ * Get signal strength (= stereo status).
+ */
+-static inline int gemtek_getsigstr(void)
++static inline int gemtek_getsigstr(struct gemtek *gt)
+ {
+- return inb_p(io) & GEMTEK_NS ? 0 : 1;
++ int sig;
++
++ mutex_lock(&gt->lock);
++ sig = inb_p(gt->io) & GEMTEK_NS ? 0 : 1;
++ mutex_unlock(&gt->lock);
++ return sig;
+ }
+
+ /*
+ * Check if requested card acts like GemTek Radio card.
+ */
+-static int gemtek_verify(int port)
++static int gemtek_verify(struct gemtek *gt, int port)
+ {
+- static int verified = -1;
+ int i, q;
+
+- if (verified == port)
++ if (gt->verified == port)
+ return 1;
+
+- spin_lock(&lock);
++ mutex_lock(&gt->lock);
+
+ q = inb_p(port); /* Read bus contents before probing. */
+ /* Try to turn on CE, CK and DA respectively and check if card responds
+@@ -316,15 +318,15 @@ static int gemtek_verify(int port)
+ udelay(SHORT_DELAY);
+
+ if ((inb_p(port) & (~GEMTEK_NS)) != (0x17 | (1 << (i + 5)))) {
+- spin_unlock(&lock);
++ mutex_unlock(&gt->lock);
+ return 0;
+ }
+ }
+ outb_p(q >> 5, port); /* Write bus contents back. */
+ udelay(SHORT_DELAY);
+
+- spin_unlock(&lock);
+- verified = port;
++ mutex_unlock(&gt->lock);
++ gt->verified = port;
+
+ return 1;
+ }
+@@ -332,83 +334,61 @@ static int gemtek_verify(int port)
+ /*
+ * Automatic probing for card.
+ */
+-static int gemtek_probe(void)
++static int gemtek_probe(struct gemtek *gt)
+ {
++ struct v4l2_device *v4l2_dev = &gt->v4l2_dev;
+ int ioports[] = { 0x20c, 0x30c, 0x24c, 0x34c, 0x248, 0x28c };
+ int i;
+
+ if (!probe) {
+- printk(KERN_INFO "Automatic device probing disabled.\n");
++ v4l2_info(v4l2_dev, "Automatic device probing disabled.\n");
+ return -1;
+ }
+
+- printk(KERN_INFO "Automatic device probing enabled.\n");
++ v4l2_info(v4l2_dev, "Automatic device probing enabled.\n");
+
+ for (i = 0; i < ARRAY_SIZE(ioports); ++i) {
+- printk(KERN_INFO "Trying I/O port 0x%x...\n", ioports[i]);
++ v4l2_info(v4l2_dev, "Trying I/O port 0x%x...\n", ioports[i]);
+
+ if (!request_region(ioports[i], 1, "gemtek-probe")) {
+- printk(KERN_WARNING "I/O port 0x%x busy!\n",
++ v4l2_warn(v4l2_dev, "I/O port 0x%x busy!\n",
+ ioports[i]);
+ continue;
+ }
+
+- if (gemtek_verify(ioports[i])) {
+- printk(KERN_INFO "Card found from I/O port "
++ if (gemtek_verify(gt, ioports[i])) {
++ v4l2_info(v4l2_dev, "Card found from I/O port "
+ "0x%x!\n", ioports[i]);
+
+ release_region(ioports[i], 1);
+-
+- io = ioports[i];
+- return io;
++ gt->io = ioports[i];
++ return gt->io;
+ }
+
+ release_region(ioports[i], 1);
+ }
+
+- printk(KERN_ERR "Automatic probing failed!\n");
+-
++ v4l2_err(v4l2_dev, "Automatic probing failed!\n");
+ return -1;
+ }
+
+ /*
+ * Video 4 Linux stuff.
+ */
+-
+-static struct v4l2_queryctrl radio_qctrl[] = {
+- {
+- .id = V4L2_CID_AUDIO_MUTE,
+- .name = "Mute",
+- .minimum = 0,
+- .maximum = 1,
+- .default_value = 1,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- }, {
+- .id = V4L2_CID_AUDIO_VOLUME,
+- .name = "Volume",
+- .minimum = 0,
+- .maximum = 65535,
+- .step = 65535,
+- .default_value = 0xff,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- }
+-};
+-
+-static int gemtek_exclusive_open(struct file *file)
++static int gemtek_open(struct file *file)
+ {
+- return test_and_set_bit(0, &in_use) ? -EBUSY : 0;
++ return 0;
+ }
+
+-static int gemtek_exclusive_release(struct file *file)
++static int gemtek_release(struct file *file)
+ {
+- clear_bit(0, &in_use);
+ return 0;
+ }
+
+ static const struct v4l2_file_operations gemtek_fops = {
+ .owner = THIS_MODULE,
+- .open = gemtek_exclusive_open,
+- .release = gemtek_exclusive_release,
++ .open = gemtek_open,
++ .release = gemtek_release,
+ .ioctl = video_ioctl2,
+ };
+
+@@ -417,23 +397,25 @@ static int vidioc_querycap(struct file *file, void *priv,
+ {
+ strlcpy(v->driver, "radio-gemtek", sizeof(v->driver));
+ strlcpy(v->card, "GemTek", sizeof(v->card));
+- sprintf(v->bus_info, "ISA");
++ strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
+ v->version = RADIO_VERSION;
+- v->capabilities = V4L2_CAP_TUNER;
++ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+ }
+
+ static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v)
+ {
++ struct gemtek *gt = video_drvdata(file);
++
+ if (v->index > 0)
+ return -EINVAL;
+
+- strcpy(v->name, "FM");
++ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+ v->rangelow = GEMTEK_LOWFREQ;
+ v->rangehigh = GEMTEK_HIGHFREQ;
+ v->capability = V4L2_TUNER_CAP_LOW | V4L2_TUNER_CAP_STEREO;
+- v->signal = 0xffff * gemtek_getsigstr();
++ v->signal = 0xffff * gemtek_getsigstr(gt);
+ if (v->signal) {
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ v->rxsubchans = V4L2_TUNER_SUB_STEREO;
+@@ -441,65 +423,56 @@ static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *v)
+ v->audmode = V4L2_TUNER_MODE_MONO;
+ v->rxsubchans = V4L2_TUNER_SUB_MONO;
+ }
+-
+ return 0;
+ }
+
+ static int vidioc_s_tuner(struct file *file, void *priv, struct v4l2_tuner *v)
+ {
+- if (v->index > 0)
+- return -EINVAL;
+- return 0;
++ return (v->index != 0) ? -EINVAL : 0;
+ }
+
+-static int vidioc_s_frequency(struct file *file, void *priv,
++static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct gemtek_device *rt = video_drvdata(file);
+-
+- gemtek_setfreq(rt, f->frequency);
++ struct gemtek *gt = video_drvdata(file);
+
++ if (f->tuner != 0)
++ return -EINVAL;
++ f->type = V4L2_TUNER_RADIO;
++ f->frequency = gt->lastfreq;
+ return 0;
+ }
+
+-static int vidioc_g_frequency(struct file *file, void *priv,
++static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct gemtek_device *rt = video_drvdata(file);
++ struct gemtek *gt = video_drvdata(file);
+
+- f->type = V4L2_TUNER_RADIO;
+- f->frequency = rt->lastfreq;
++ if (f->tuner != 0 || f->type != V4L2_TUNER_RADIO)
++ return -EINVAL;
++ gemtek_setfreq(gt, f->frequency);
+ return 0;
+ }
+
+ static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+ {
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(radio_qctrl); ++i) {
+- if (qc->id && qc->id == radio_qctrl[i].id) {
+- memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
+- return 0;
+- }
++ switch (qc->id) {
++ case V4L2_CID_AUDIO_MUTE:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
++ default:
++ return -EINVAL;
+ }
+- return -EINVAL;
+ }
+
+ static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct gemtek_device *rt = video_drvdata(file);
++ struct gemtek *gt = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+- ctrl->value = rt->muted;
+- return 0;
+- case V4L2_CID_AUDIO_VOLUME:
+- if (rt->muted)
+- ctrl->value = 0;
+- else
+- ctrl->value = 65535;
++ ctrl->value = gt->muted;
+ return 0;
+ }
+ return -EINVAL;
+@@ -508,35 +481,19 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
+ static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct gemtek_device *rt = video_drvdata(file);
++ struct gemtek *gt = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value)
+- gemtek_mute(rt);
+- else
+- gemtek_unmute(rt);
+- return 0;
+- case V4L2_CID_AUDIO_VOLUME:
+- if (ctrl->value)
+- gemtek_unmute(rt);
++ gemtek_mute(gt);
+ else
+- gemtek_mute(rt);
++ gemtek_unmute(gt);
+ return 0;
+ }
+ return -EINVAL;
+ }
+
+-static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
+-{
+- if (a->index > 1)
+- return -EINVAL;
+-
+- strcpy(a->name, "Radio");
+- a->capability = V4L2_AUDCAP_STEREO;
+- return 0;
+-}
+-
+ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+ {
+ *i = 0;
+@@ -545,16 +502,20 @@ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+
+ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+ {
+- if (i != 0)
+- return -EINVAL;
++ return (i != 0) ? -EINVAL : 0;
++}
++
++static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
++{
++ a->index = 0;
++ strlcpy(a->name, "Radio", sizeof(a->name));
++ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+ }
+
+ static int vidioc_s_audio(struct file *file, void *priv, struct v4l2_audio *a)
+ {
+- if (a->index != 0)
+- return -EINVAL;
+- return 0;
++ return (a->index != 0) ? -EINVAL : 0;
+ }
+
+ static const struct v4l2_ioctl_ops gemtek_ioctl_ops = {
+@@ -572,62 +533,73 @@ static const struct v4l2_ioctl_ops gemtek_ioctl_ops = {
+ .vidioc_s_ctrl = vidioc_s_ctrl
+ };
+
+-static struct video_device gemtek_radio = {
+- .name = "GemTek Radio card",
+- .fops = &gemtek_fops,
+- .ioctl_ops = &gemtek_ioctl_ops,
+- .release = video_device_release_empty,
+-};
+-
+ /*
+ * Initialization / cleanup related stuff.
+ */
+
+-/*
+- * Initilize card.
+- */
+ static int __init gemtek_init(void)
+ {
+- printk(KERN_INFO RADIO_BANNER "\n");
++ struct gemtek *gt = &gemtek_card;
++ struct v4l2_device *v4l2_dev = &gt->v4l2_dev;
++ int res;
+
+- spin_lock_init(&lock);
++ strlcpy(v4l2_dev->name, "gemtek", sizeof(v4l2_dev->name));
+
+- gemtek_probe();
+- if (io) {
+- if (!request_region(io, 1, "gemtek")) {
+- printk(KERN_ERR "I/O port 0x%x already in use.\n", io);
++ v4l2_info(v4l2_dev, "GemTek Radio card driver: v0.0.3\n");
++
++ mutex_init(&gt->lock);
++
++ gt->verified = -1;
++ gt->io = io;
++ gemtek_probe(gt);
++ if (gt->io) {
++ if (!request_region(gt->io, 1, "gemtek")) {
++ v4l2_err(v4l2_dev, "I/O port 0x%x already in use.\n", gt->io);
+ return -EBUSY;
+ }
+
+- if (!gemtek_verify(io))
+- printk(KERN_WARNING "Card at I/O port 0x%x does not "
++ if (!gemtek_verify(gt, gt->io))
++ v4l2_warn(v4l2_dev, "Card at I/O port 0x%x does not "
+ "respond properly, check your "
+- "configuration.\n", io);
++ "configuration.\n", gt->io);
+ else
+- printk(KERN_INFO "Using I/O port 0x%x.\n", io);
++ v4l2_info(v4l2_dev, "Using I/O port 0x%x.\n", gt->io);
+ } else if (probe) {
+- printk(KERN_ERR "Automatic probing failed and no "
++ v4l2_err(v4l2_dev, "Automatic probing failed and no "
+ "fixed I/O port defined.\n");
+ return -ENODEV;
+ } else {
+- printk(KERN_ERR "Automatic probing disabled but no fixed "
++ v4l2_err(v4l2_dev, "Automatic probing disabled but no fixed "
+ "I/O port defined.");
+ return -EINVAL;
+ }
+
+- video_set_drvdata(&gemtek_radio, &gemtek_unit);
++ res = v4l2_device_register(NULL, v4l2_dev);
++ if (res < 0) {
++ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
++ release_region(gt->io, 1);
++ return res;
++ }
+
+- if (video_register_device(&gemtek_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
+- release_region(io, 1);
++ strlcpy(gt->vdev.name, v4l2_dev->name, sizeof(gt->vdev.name));
++ gt->vdev.v4l2_dev = v4l2_dev;
++ gt->vdev.fops = &gemtek_fops;
++ gt->vdev.ioctl_ops = &gemtek_ioctl_ops;
++ gt->vdev.release = video_device_release_empty;
++ video_set_drvdata(&gt->vdev, gt);
++
++ if (video_register_device(&gt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
++ v4l2_device_unregister(v4l2_dev);
++ release_region(gt->io, 1);
+ return -EBUSY;
+ }
+
+ /* Set defaults */
+- gemtek_unit.lastfreq = GEMTEK_LOWFREQ;
+- gemtek_unit.bu2614data = 0;
++ gt->lastfreq = GEMTEK_LOWFREQ;
++ gt->bu2614data = 0;
+
+ if (initmute)
+- gemtek_mute(&gemtek_unit);
++ gemtek_mute(gt);
+
+ return 0;
+ }
+@@ -637,15 +609,19 @@ static int __init gemtek_init(void)
+ */
+ static void __exit gemtek_exit(void)
+ {
++ struct gemtek *gt = &gemtek_card;
++ struct v4l2_device *v4l2_dev = &gt->v4l2_dev;
++
+ if (shutdown) {
+ hardmute = 1; /* Turn off PLL */
+- gemtek_mute(&gemtek_unit);
++ gemtek_mute(gt);
+ } else {
+- printk(KERN_INFO "Module unloaded but card not muted!\n");
++ v4l2_info(v4l2_dev, "Module unloaded but card not muted!\n");
+ }
+
+- video_unregister_device(&gemtek_radio);
+- release_region(io, 1);
++ video_unregister_device(&gt->vdev);
++ v4l2_device_unregister(&gt->v4l2_dev);
++ release_region(gt->io, 1);
+ }
+
+ module_init(gemtek_init);
+diff --git a/drivers/media/radio/radio-maestro.c b/drivers/media/radio/radio-maestro.c
+index ba3a13a..01a6d22 100644
+--- a/drivers/media/radio/radio-maestro.c
++++ b/drivers/media/radio/radio-maestro.c
+@@ -22,27 +22,22 @@
+ #include <linux/init.h>
+ #include <linux/ioport.h>
+ #include <linux/delay.h>
+-#include <asm/io.h>
+-#include <asm/uaccess.h>
++#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+ #include <linux/pci.h>
+ #include <linux/videodev2.h>
+-#include <media/v4l2-common.h>
++#include <linux/io.h>
++#include <media/v4l2-device.h>
+ #include <media/v4l2-ioctl.h>
+
+-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+-#define RADIO_VERSION KERNEL_VERSION(0,0,6)
+-#define DRIVER_VERSION "0.06"
++MODULE_AUTHOR("Adam Tlalka, atlka@pg.gda.pl");
++MODULE_DESCRIPTION("Radio driver for the Maestro PCI sound card radio.");
++MODULE_LICENSE("GPL");
+
+-static struct v4l2_queryctrl radio_qctrl[] = {
+- {
+- .id = V4L2_CID_AUDIO_MUTE,
+- .name = "Mute",
+- .minimum = 0,
+- .maximum = 1,
+- .default_value = 1,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- }
+-};
++static int radio_nr = -1;
++module_param(radio_nr, int, 0);
++
++#define RADIO_VERSION KERNEL_VERSION(0, 0, 6)
++#define DRIVER_VERSION "0.06"
+
+ #define GPIO_DATA 0x60 /* port offset from ESS_IO_BASE */
+
+@@ -72,62 +67,27 @@ static struct v4l2_queryctrl radio_qctrl[] = {
+
+ #define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF)
+
+-static int radio_nr = -1;
+-module_param(radio_nr, int, 0);
++struct maestro {
++ struct v4l2_device v4l2_dev;
++ struct video_device vdev;
++ struct pci_dev *pdev;
++ struct mutex lock;
+
+-static unsigned long in_use;
+-
+-static int maestro_probe(struct pci_dev *pdev, const struct pci_device_id *ent);
++ u16 io; /* base of Maestro card radio io (GPIO_DATA)*/
++ u16 muted; /* VIDEO_AUDIO_MUTE */
++ u16 stereo; /* VIDEO_TUNER_STEREO_ON */
++ u16 tuned; /* signal strength (0 or 0xffff) */
++};
+
+-static int maestro_exclusive_open(struct file *file)
++static inline struct maestro *to_maestro(struct v4l2_device *v4l2_dev)
+ {
+- return test_and_set_bit(0, &in_use) ? -EBUSY : 0;
++ return container_of(v4l2_dev, struct maestro, v4l2_dev);
+ }
+
+-static int maestro_exclusive_release(struct file *file)
++static u32 radio_bits_get(struct maestro *dev)
+ {
+- clear_bit(0, &in_use);
+- return 0;
+-}
+-
+-static void maestro_remove(struct pci_dev *pdev);
+-
+-static struct pci_device_id maestro_r_pci_tbl[] = {
+- { PCI_DEVICE(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ESS1968),
+- .class = PCI_CLASS_MULTIMEDIA_AUDIO << 8,
+- .class_mask = 0xffff00 },
+- { PCI_DEVICE(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ESS1978),
+- .class = PCI_CLASS_MULTIMEDIA_AUDIO << 8,
+- .class_mask = 0xffff00 },
+- { 0 }
+-};
+-MODULE_DEVICE_TABLE(pci, maestro_r_pci_tbl);
+-
+-static struct pci_driver maestro_r_driver = {
+- .name = "maestro_radio",
+- .id_table = maestro_r_pci_tbl,
+- .probe = maestro_probe,
+- .remove = __devexit_p(maestro_remove),
+-};
+-
+-static const struct v4l2_file_operations maestro_fops = {
+- .owner = THIS_MODULE,
+- .open = maestro_exclusive_open,
+- .release = maestro_exclusive_release,
+- .ioctl = video_ioctl2,
+-};
+-
+-struct radio_device {
+- u16 io, /* base of Maestro card radio io (GPIO_DATA)*/
+- muted, /* VIDEO_AUDIO_MUTE */
+- stereo, /* VIDEO_TUNER_STEREO_ON */
+- tuned; /* signal strength (0 or 0xffff) */
+-};
+-
+-static u32 radio_bits_get(struct radio_device *dev)
+-{
+- register u16 io=dev->io, l, rdata;
+- register u32 data=0;
++ u16 io = dev->io, l, rdata;
++ u32 data = 0;
+ u16 omask;
+
+ omask = inw(io + IO_MASK);
+@@ -135,25 +95,23 @@ static u32 radio_bits_get(struct radio_device *dev)
+ outw(0, io);
+ udelay(16);
+
+- for (l=24;l--;) {
++ for (l = 24; l--;) {
+ outw(STR_CLK, io); /* HI state */
+ udelay(2);
+- if(!l)
++ if (!l)
+ dev->tuned = inw(io) & STR_MOST ? 0 : 0xffff;
+ outw(0, io); /* LO state */
+ udelay(2);
+ data <<= 1; /* shift data */
+ rdata = inw(io);
+- if(!l)
+- dev->stereo = rdata & STR_MOST ?
+- 0 : 1;
+- else
+- if(rdata & STR_DATA)
+- data++;
++ if (!l)
++ dev->stereo = (rdata & STR_MOST) ? 0 : 1;
++ else if (rdata & STR_DATA)
++ data++;
+ udelay(2);
+ }
+
+- if(dev->muted)
++ if (dev->muted)
+ outw(STR_WREN, io);
+
+ udelay(4);
+@@ -162,18 +120,18 @@ static u32 radio_bits_get(struct radio_device *dev)
+ return data & 0x3ffe;
+ }
+
+-static void radio_bits_set(struct radio_device *dev, u32 data)
++static void radio_bits_set(struct maestro *dev, u32 data)
+ {
+- register u16 io=dev->io, l, bits;
++ u16 io = dev->io, l, bits;
+ u16 omask, odir;
+
+ omask = inw(io + IO_MASK);
+- odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN);
++ odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN);
+ outw(odir | STR_DATA, io + IO_DIR);
+ outw(~(STR_DATA | STR_CLK | STR_WREN), io + IO_MASK);
+ udelay(16);
+- for (l=25;l;l--) {
+- bits = ((data >> 18) & STR_DATA) | STR_WREN ;
++ for (l = 25; l; l--) {
++ bits = ((data >> 18) & STR_DATA) | STR_WREN;
+ data <<= 1; /* shift data */
+ outw(bits, io); /* start strobe */
+ udelay(2);
+@@ -183,7 +141,7 @@ static void radio_bits_set(struct radio_device *dev, u32 data)
+ udelay(4);
+ }
+
+- if(!dev->muted)
++ if (!dev->muted)
+ outw(0, io);
+
+ udelay(4);
+@@ -195,78 +153,79 @@ static void radio_bits_set(struct radio_device *dev, u32 data)
+ static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+ {
++ struct maestro *dev = video_drvdata(file);
++
+ strlcpy(v->driver, "radio-maestro", sizeof(v->driver));
+ strlcpy(v->card, "Maestro Radio", sizeof(v->card));
+- sprintf(v->bus_info, "PCI");
++ snprintf(v->bus_info, sizeof(v->bus_info), "PCI:%s", pci_name(dev->pdev));
+ v->version = RADIO_VERSION;
+- v->capabilities = V4L2_CAP_TUNER;
++ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+ }
+
+ static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- struct radio_device *card = video_drvdata(file);
++ struct maestro *dev = video_drvdata(file);
+
+ if (v->index > 0)
+ return -EINVAL;
+
+- (void)radio_bits_get(card);
++ mutex_lock(&dev->lock);
++ radio_bits_get(dev);
+
+- strcpy(v->name, "FM");
++ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+ v->rangelow = FREQ_LO;
+ v->rangehigh = FREQ_HI;
+- v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
++ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ v->capability = V4L2_TUNER_CAP_LOW;
+- if(card->stereo)
++ if (dev->stereo)
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ v->audmode = V4L2_TUNER_MODE_MONO;
+- v->signal = card->tuned;
++ v->signal = dev->tuned;
++ mutex_unlock(&dev->lock);
+ return 0;
+ }
+
+ static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- if (v->index > 0)
+- return -EINVAL;
+- return 0;
++ return v->index ? -EINVAL : 0;
+ }
+
+ static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct radio_device *card = video_drvdata(file);
++ struct maestro *dev = video_drvdata(file);
+
+ if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
+ return -EINVAL;
+- radio_bits_set(card, FREQ2BITS(f->frequency));
++ mutex_lock(&dev->lock);
++ radio_bits_set(dev, FREQ2BITS(f->frequency));
++ mutex_unlock(&dev->lock);
+ return 0;
+ }
+
+ static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct radio_device *card = video_drvdata(file);
++ struct maestro *dev = video_drvdata(file);
+
+ f->type = V4L2_TUNER_RADIO;
+- f->frequency = BITS2FREQ(radio_bits_get(card));
++ mutex_lock(&dev->lock);
++ f->frequency = BITS2FREQ(radio_bits_get(dev));
++ mutex_unlock(&dev->lock);
+ return 0;
+ }
+
+ static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+ {
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+- if (qc->id && qc->id == radio_qctrl[i].id) {
+- memcpy(qc, &(radio_qctrl[i]),
+- sizeof(*qc));
+- return 0;
+- }
++ switch (qc->id) {
++ case V4L2_CID_AUDIO_MUTE:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
+ }
+ return -EINVAL;
+ }
+@@ -274,11 +233,11 @@ static int vidioc_queryctrl(struct file *file, void *priv,
+ static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct radio_device *card = video_drvdata(file);
++ struct maestro *dev = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+- ctrl->value = card->muted;
++ ctrl->value = dev->muted;
+ return 0;
+ }
+ return -EINVAL;
+@@ -287,56 +246,85 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
+ static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct radio_device *card = video_drvdata(file);
+- register u16 io = card->io;
+- register u16 omask = inw(io + IO_MASK);
++ struct maestro *dev = video_drvdata(file);
++ u16 io = dev->io;
++ u16 omask;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
++ mutex_lock(&dev->lock);
++ omask = inw(io + IO_MASK);
+ outw(~STR_WREN, io + IO_MASK);
+- outw((card->muted = ctrl->value ) ?
+- STR_WREN : 0, io);
++ dev->muted = ctrl->value;
++ outw(dev->muted ? STR_WREN : 0, io);
+ udelay(4);
+ outw(omask, io + IO_MASK);
+ msleep(125);
++ mutex_unlock(&dev->lock);
+ return 0;
+ }
+ return -EINVAL;
+ }
+
++static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
++{
++ *i = 0;
++ return 0;
++}
++
++static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
++{
++ return i ? -EINVAL : 0;
++}
++
+ static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+ {
+- if (a->index > 1)
+- return -EINVAL;
+-
+- strcpy(a->name, "Radio");
++ a->index = 0;
++ strlcpy(a->name, "Radio", sizeof(a->name));
+ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+ }
+
+-static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
++static int vidioc_s_audio(struct file *file, void *priv,
++ struct v4l2_audio *a)
+ {
+- *i = 0;
+- return 0;
++ return a->index ? -EINVAL : 0;
+ }
+
+-static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
++static int maestro_open(struct file *file)
+ {
+- if (i != 0)
+- return -EINVAL;
+ return 0;
+ }
+
+-static int vidioc_s_audio(struct file *file, void *priv,
+- struct v4l2_audio *a)
++static int maestro_release(struct file *file)
+ {
+- if (a->index != 0)
+- return -EINVAL;
+ return 0;
+ }
+
+-static u16 __devinit radio_power_on(struct radio_device *dev)
++static const struct v4l2_file_operations maestro_fops = {
++ .owner = THIS_MODULE,
++ .open = maestro_open,
++ .release = maestro_release,
++ .ioctl = video_ioctl2,
++};
++
++static const struct v4l2_ioctl_ops maestro_ioctl_ops = {
++ .vidioc_querycap = vidioc_querycap,
++ .vidioc_g_tuner = vidioc_g_tuner,
++ .vidioc_s_tuner = vidioc_s_tuner,
++ .vidioc_g_audio = vidioc_g_audio,
++ .vidioc_s_audio = vidioc_s_audio,
++ .vidioc_g_input = vidioc_g_input,
++ .vidioc_s_input = vidioc_s_input,
++ .vidioc_g_frequency = vidioc_g_frequency,
++ .vidioc_s_frequency = vidioc_s_frequency,
++ .vidioc_queryctrl = vidioc_queryctrl,
++ .vidioc_g_ctrl = vidioc_g_ctrl,
++ .vidioc_s_ctrl = vidioc_s_ctrl,
++};
++
++static u16 __devinit radio_power_on(struct maestro *dev)
+ {
+ register u16 io = dev->io;
+ register u32 ofreq;
+@@ -360,33 +348,11 @@ static u16 __devinit radio_power_on(struct radio_device *dev)
+ return (ofreq == radio_bits_get(dev));
+ }
+
+-static const struct v4l2_ioctl_ops maestro_ioctl_ops = {
+- .vidioc_querycap = vidioc_querycap,
+- .vidioc_g_tuner = vidioc_g_tuner,
+- .vidioc_s_tuner = vidioc_s_tuner,
+- .vidioc_g_audio = vidioc_g_audio,
+- .vidioc_s_audio = vidioc_s_audio,
+- .vidioc_g_input = vidioc_g_input,
+- .vidioc_s_input = vidioc_s_input,
+- .vidioc_g_frequency = vidioc_g_frequency,
+- .vidioc_s_frequency = vidioc_s_frequency,
+- .vidioc_queryctrl = vidioc_queryctrl,
+- .vidioc_g_ctrl = vidioc_g_ctrl,
+- .vidioc_s_ctrl = vidioc_s_ctrl,
+-};
+-
+-static struct video_device maestro_radio = {
+- .name = "Maestro radio",
+- .fops = &maestro_fops,
+- .ioctl_ops = &maestro_ioctl_ops,
+- .release = video_device_release,
+-};
+-
+ static int __devinit maestro_probe(struct pci_dev *pdev,
+ const struct pci_device_id *ent)
+ {
+- struct radio_device *radio_unit;
+- struct video_device *maestro_radio_inst;
++ struct maestro *dev;
++ struct v4l2_device *v4l2_dev;
+ int retval;
+
+ retval = pci_enable_device(pdev);
+@@ -397,46 +363,53 @@ static int __devinit maestro_probe(struct pci_dev *pdev,
+
+ retval = -ENOMEM;
+
+- radio_unit = kzalloc(sizeof(*radio_unit), GFP_KERNEL);
+- if (radio_unit == NULL) {
++ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
++ if (dev == NULL) {
+ dev_err(&pdev->dev, "not enough memory\n");
+ goto err;
+ }
+
+- radio_unit->io = pci_resource_start(pdev, 0) + GPIO_DATA;
++ v4l2_dev = &dev->v4l2_dev;
++ mutex_init(&dev->lock);
++ dev->pdev = pdev;
+
+- maestro_radio_inst = video_device_alloc();
+- if (maestro_radio_inst == NULL) {
+- dev_err(&pdev->dev, "not enough memory\n");
++ strlcpy(v4l2_dev->name, "maestro", sizeof(v4l2_dev->name));
++
++ retval = v4l2_device_register(&pdev->dev, v4l2_dev);
++ if (retval < 0) {
++ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
+ goto errfr;
+ }
+
+- memcpy(maestro_radio_inst, &maestro_radio, sizeof(maestro_radio));
+- video_set_drvdata(maestro_radio_inst, radio_unit);
+- pci_set_drvdata(pdev, maestro_radio_inst);
++ dev->io = pci_resource_start(pdev, 0) + GPIO_DATA;
+
+- retval = video_register_device(maestro_radio_inst, VFL_TYPE_RADIO, radio_nr);
++ strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
++ dev->vdev.v4l2_dev = v4l2_dev;
++ dev->vdev.fops = &maestro_fops;
++ dev->vdev.ioctl_ops = &maestro_ioctl_ops;
++ dev->vdev.release = video_device_release_empty;
++ video_set_drvdata(&dev->vdev, dev);
++
++ retval = video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr);
+ if (retval) {
+- printk(KERN_ERR "can't register video device!\n");
++ v4l2_err(v4l2_dev, "can't register video device!\n");
+ goto errfr1;
+ }
+
+- if (!radio_power_on(radio_unit)) {
++ if (!radio_power_on(dev)) {
+ retval = -EIO;
+ goto errunr;
+ }
+
+- dev_info(&pdev->dev, "version " DRIVER_VERSION " time " __TIME__ " "
+- __DATE__ "\n");
+- dev_info(&pdev->dev, "radio chip initialized\n");
++ v4l2_info(v4l2_dev, "version " DRIVER_VERSION "\n");
+
+ return 0;
+ errunr:
+- video_unregister_device(maestro_radio_inst);
++ video_unregister_device(&dev->vdev);
+ errfr1:
+- video_device_release(maestro_radio_inst);
++ v4l2_device_unregister(v4l2_dev);
+ errfr:
+- kfree(radio_unit);
++ kfree(dev);
+ err:
+ return retval;
+
+@@ -444,11 +417,31 @@ err:
+
+ static void __devexit maestro_remove(struct pci_dev *pdev)
+ {
+- struct video_device *vdev = pci_get_drvdata(pdev);
++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
++ struct maestro *dev = to_maestro(v4l2_dev);
+
+- video_unregister_device(vdev);
++ video_unregister_device(&dev->vdev);
++ v4l2_device_unregister(&dev->v4l2_dev);
+ }
+
++static struct pci_device_id maestro_r_pci_tbl[] = {
++ { PCI_DEVICE(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ESS1968),
++ .class = PCI_CLASS_MULTIMEDIA_AUDIO << 8,
++ .class_mask = 0xffff00 },
++ { PCI_DEVICE(PCI_VENDOR_ID_ESS, PCI_DEVICE_ID_ESS_ESS1978),
++ .class = PCI_CLASS_MULTIMEDIA_AUDIO << 8,
++ .class_mask = 0xffff00 },
++ { 0 }
++};
++MODULE_DEVICE_TABLE(pci, maestro_r_pci_tbl);
++
++static struct pci_driver maestro_r_driver = {
++ .name = "maestro_radio",
++ .id_table = maestro_r_pci_tbl,
++ .probe = maestro_probe,
++ .remove = __devexit_p(maestro_remove),
++};
++
+ static int __init maestro_radio_init(void)
+ {
+ int retval = pci_register_driver(&maestro_r_driver);
+@@ -466,7 +459,3 @@ static void __exit maestro_radio_exit(void)
+
+ module_init(maestro_radio_init);
+ module_exit(maestro_radio_exit);
+-
+-MODULE_AUTHOR("Adam Tlalka, atlka@pg.gda.pl");
+-MODULE_DESCRIPTION("Radio driver for the Maestro PCI sound card radio.");
+-MODULE_LICENSE("GPL");
+diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c
+index c5dc00a..2606f0b 100644
+--- a/drivers/media/radio/radio-maxiradio.c
++++ b/drivers/media/radio/radio-maxiradio.c
+@@ -37,38 +37,32 @@
+ #include <linux/init.h>
+ #include <linux/ioport.h>
+ #include <linux/delay.h>
+-#include <asm/io.h>
+-#include <asm/uaccess.h>
+ #include <linux/mutex.h>
+-
+ #include <linux/pci.h>
+ #include <linux/videodev2.h>
+-#include <media/v4l2-common.h>
++#include <linux/version.h> /* for KERNEL_VERSION MACRO */
++#include <linux/io.h>
++#include <media/v4l2-device.h>
+ #include <media/v4l2-ioctl.h>
+
++MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net");
++MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio.");
++MODULE_LICENSE("GPL");
++
++static int radio_nr = -1;
++module_param(radio_nr, int, 0);
++
++static int debug;
++
++module_param(debug, int, 0644);
++MODULE_PARM_DESC(debug, "activates debug info");
++
+ #define DRIVER_VERSION "0.77"
+
+-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+-#define RADIO_VERSION KERNEL_VERSION(0,7,7)
+-
+-static struct video_device maxiradio_radio;
+-
+-#define dprintk(num, fmt, arg...) \
+- do { \
+- if (maxiradio_radio.debug >= num) \
+- printk(KERN_DEBUG "%s: " fmt, \
+- maxiradio_radio.name, ## arg); } while (0)
+-
+-static struct v4l2_queryctrl radio_qctrl[] = {
+- {
+- .id = V4L2_CID_AUDIO_MUTE,
+- .name = "Mute",
+- .minimum = 0,
+- .maximum = 1,
+- .default_value = 1,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- }
+-};
++#define RADIO_VERSION KERNEL_VERSION(0, 7, 7)
++
++#define dprintk(dev, num, fmt, arg...) \
++ v4l2_dbg(num, debug, &dev->v4l2_dev, fmt, ## arg)
+
+ #ifndef PCI_VENDOR_ID_GUILLEMOT
+ #define PCI_VENDOR_ID_GUILLEMOT 0x5046
+@@ -80,90 +74,70 @@ static struct v4l2_queryctrl radio_qctrl[] = {
+
+
+ /* TEA5757 pin mappings */
+-static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16 ;
+-
+-static int radio_nr = -1;
+-module_param(radio_nr, int, 0);
++static const int clk = 1, data = 2, wren = 4, mo_st = 8, power = 16;
+
+-static unsigned long in_use;
+-
+-#define FREQ_LO 50*16000
+-#define FREQ_HI 150*16000
++#define FREQ_LO (50 * 16000)
++#define FREQ_HI (150 * 16000)
+
+ #define FREQ_IF 171200 /* 10.7*16000 */
+ #define FREQ_STEP 200 /* 12.5*16 */
+
+ /* (x==fmhz*16*1000) -> bits */
+-#define FREQ2BITS(x) ((( (unsigned int)(x)+FREQ_IF+(FREQ_STEP<<1)) \
+- /(FREQ_STEP<<2))<<2)
++#define FREQ2BITS(x) \
++ ((((unsigned int)(x) + FREQ_IF + (FREQ_STEP << 1)) / (FREQ_STEP << 2)) << 2)
+
+ #define BITS2FREQ(x) ((x) * FREQ_STEP - FREQ_IF)
+
+
+-static int maxiradio_exclusive_open(struct file *file)
++struct maxiradio
+ {
+- return test_and_set_bit(0, &in_use) ? -EBUSY : 0;
+-}
+-
+-static int maxiradio_exclusive_release(struct file *file)
+-{
+- clear_bit(0, &in_use);
+- return 0;
+-}
+-
+-static const struct v4l2_file_operations maxiradio_fops = {
+- .owner = THIS_MODULE,
+- .open = maxiradio_exclusive_open,
+- .release = maxiradio_exclusive_release,
+- .ioctl = video_ioctl2,
+-};
++ struct v4l2_device v4l2_dev;
++ struct video_device vdev;
++ struct pci_dev *pdev;
+
+-static struct radio_device
+-{
+- __u16 io, /* base of radio io */
+- muted, /* VIDEO_AUDIO_MUTE */
+- stereo, /* VIDEO_TUNER_STEREO_ON */
+- tuned; /* signal strength (0 or 0xffff) */
++ u16 io; /* base of radio io */
++ u16 muted; /* VIDEO_AUDIO_MUTE */
++ u16 stereo; /* VIDEO_TUNER_STEREO_ON */
++ u16 tuned; /* signal strength (0 or 0xffff) */
+
+ unsigned long freq;
+
+ struct mutex lock;
+-} radio_unit = {
+- .muted =1,
+- .freq = FREQ_LO,
+ };
+
+-static void outbit(unsigned long bit, __u16 io)
++static inline struct maxiradio *to_maxiradio(struct v4l2_device *v4l2_dev)
+ {
+- if (bit != 0)
+- {
+- outb( power|wren|data ,io); udelay(4);
+- outb( power|wren|data|clk ,io); udelay(4);
+- outb( power|wren|data ,io); udelay(4);
+- }
+- else
+- {
+- outb( power|wren ,io); udelay(4);
+- outb( power|wren|clk ,io); udelay(4);
+- outb( power|wren ,io); udelay(4);
+- }
++ return container_of(v4l2_dev, struct maxiradio, v4l2_dev);
+ }
+
+-static void turn_power(__u16 io, int p)
++static void outbit(unsigned long bit, u16 io)
++{
++ int val = power | wren | (bit ? data : 0);
++
++ outb(val, io);
++ udelay(4);
++ outb(val | clk, io);
++ udelay(4);
++ outb(val, io);
++ udelay(4);
++}
++
++static void turn_power(struct maxiradio *dev, int p)
+ {
+ if (p != 0) {
+- dprintk(1, "Radio powered on\n");
+- outb(power, io);
++ dprintk(dev, 1, "Radio powered on\n");
++ outb(power, dev->io);
+ } else {
+- dprintk(1, "Radio powered off\n");
+- outb(0,io);
++ dprintk(dev, 1, "Radio powered off\n");
++ outb(0, dev->io);
+ }
+ }
+
+-static void set_freq(__u16 io, __u32 freq)
++static void set_freq(struct maxiradio *dev, u32 freq)
+ {
+ unsigned long int si;
+ int bl;
++ int io = dev->io;
+ int val = FREQ2BITS(freq);
+
+ /* TEA5757 shift register bits (see pdf) */
+@@ -188,14 +162,14 @@ static void set_freq(__u16 io, __u32 freq)
+ si >>= 1;
+ }
+
+- dprintk(1, "Radio freq set to %d.%02d MHz\n",
++ dprintk(dev, 1, "Radio freq set to %d.%02d MHz\n",
+ freq / 16000,
+ freq % 16000 * 100 / 16000);
+
+- turn_power(io, 1);
++ turn_power(dev, 1);
+ }
+
+-static int get_stereo(__u16 io)
++static int get_stereo(u16 io)
+ {
+ outb(power,io);
+ udelay(4);
+@@ -203,7 +177,7 @@ static int get_stereo(__u16 io)
+ return !(inb(io) & mo_st);
+ }
+
+-static int get_tune(__u16 io)
++static int get_tune(u16 io)
+ {
+ outb(power+clk,io);
+ udelay(4);
+@@ -212,95 +186,84 @@ static int get_tune(__u16 io)
+ }
+
+
+-static int vidioc_querycap (struct file *file, void *priv,
++static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+ {
+- strlcpy(v->driver, "radio-maxiradio", sizeof (v->driver));
+- strlcpy(v->card, "Maxi Radio FM2000 radio", sizeof (v->card));
+- sprintf(v->bus_info,"ISA");
+- v->version = RADIO_VERSION;
+- v->capabilities = V4L2_CAP_TUNER;
++ struct maxiradio *dev = video_drvdata(file);
+
++ strlcpy(v->driver, "radio-maxiradio", sizeof(v->driver));
++ strlcpy(v->card, "Maxi Radio FM2000 radio", sizeof(v->card));
++ snprintf(v->bus_info, sizeof(v->bus_info), "PCI:%s", pci_name(dev->pdev));
++ v->version = RADIO_VERSION;
++ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+ }
+
+-static int vidioc_g_tuner (struct file *file, void *priv,
++static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- struct radio_device *card = video_drvdata(file);
++ struct maxiradio *dev = video_drvdata(file);
+
+ if (v->index > 0)
+ return -EINVAL;
+
+- memset(v,0,sizeof(*v));
+- strcpy(v->name, "FM");
++ mutex_lock(&dev->lock);
++ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+-
+- v->rangelow=FREQ_LO;
+- v->rangehigh=FREQ_HI;
+- v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
+- v->capability=V4L2_TUNER_CAP_LOW;
+- if(get_stereo(card->io))
++ v->rangelow = FREQ_LO;
++ v->rangehigh = FREQ_HI;
++ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
++ v->capability = V4L2_TUNER_CAP_LOW;
++ if (get_stereo(dev->io))
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ v->audmode = V4L2_TUNER_MODE_MONO;
+- v->signal=0xffff*get_tune(card->io);
++ v->signal = 0xffff * get_tune(dev->io);
++ mutex_unlock(&dev->lock);
+
+ return 0;
+ }
+
+-static int vidioc_s_tuner (struct file *file, void *priv,
++static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- if (v->index > 0)
+- return -EINVAL;
+-
+- return 0;
+-}
+-
+-static int vidioc_g_audio (struct file *file, void *priv,
+- struct v4l2_audio *a)
+-{
+- if (a->index > 1)
+- return -EINVAL;
+-
+- strcpy(a->name, "FM");
+- a->capability = V4L2_AUDCAP_STEREO;
+- return 0;
++ return v->index ? -EINVAL : 0;
+ }
+
+ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+ {
+ *i = 0;
+-
+ return 0;
+ }
+
+ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+ {
+- if (i != 0)
+- return -EINVAL;
++ return i ? -EINVAL : 0;
++}
+
++static int vidioc_g_audio(struct file *file, void *priv,
++ struct v4l2_audio *a)
++{
++ a->index = 0;
++ strlcpy(a->name, "Radio", sizeof(a->name));
++ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+ }
+
+
+-static int vidioc_s_audio (struct file *file, void *priv,
++static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+ {
+- if (a->index != 0)
+- return -EINVAL;
+-
+- return 0;
++ return a->index ? -EINVAL : 0;
+ }
+
+-static int vidioc_s_frequency (struct file *file, void *priv,
++static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct radio_device *card = video_drvdata(file);
++ struct maxiradio *dev = video_drvdata(file);
+
+ if (f->frequency < FREQ_LO || f->frequency > FREQ_HI) {
+- dprintk(1, "radio freq (%d.%02d MHz) out of range (%d-%d)\n",
++ dprintk(dev, 1, "radio freq (%d.%02d MHz) out of range (%d-%d)\n",
+ f->frequency / 16000,
+ f->frequency % 16000 * 100 / 16000,
+ FREQ_LO / 16000, FREQ_HI / 16000);
+@@ -308,75 +271,91 @@ static int vidioc_s_frequency (struct file *file, void *priv,
+ return -EINVAL;
+ }
+
+- card->freq = f->frequency;
+- set_freq(card->io, card->freq);
++ mutex_lock(&dev->lock);
++ dev->freq = f->frequency;
++ set_freq(dev, dev->freq);
+ msleep(125);
++ mutex_unlock(&dev->lock);
+
+ return 0;
+ }
+
+-static int vidioc_g_frequency (struct file *file, void *priv,
++static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct radio_device *card = video_drvdata(file);
++ struct maxiradio *dev = video_drvdata(file);
+
+ f->type = V4L2_TUNER_RADIO;
+- f->frequency = card->freq;
++ f->frequency = dev->freq;
+
+- dprintk(4, "radio freq is %d.%02d MHz",
++ dprintk(dev, 4, "radio freq is %d.%02d MHz",
+ f->frequency / 16000,
+ f->frequency % 16000 * 100 / 16000);
+
+ return 0;
+ }
+
+-static int vidioc_queryctrl (struct file *file, void *priv,
++static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+ {
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+- if (qc->id && qc->id == radio_qctrl[i].id) {
+- memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
+- return (0);
+- }
++ switch (qc->id) {
++ case V4L2_CID_AUDIO_MUTE:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
+ }
+-
+ return -EINVAL;
+ }
+
+-static int vidioc_g_ctrl (struct file *file, void *priv,
+- struct v4l2_control *ctrl)
++static int vidioc_g_ctrl(struct file *file, void *priv,
++ struct v4l2_control *ctrl)
+ {
+- struct radio_device *card = video_drvdata(file);
++ struct maxiradio *dev = video_drvdata(file);
+
+ switch (ctrl->id) {
+- case V4L2_CID_AUDIO_MUTE:
+- ctrl->value=card->muted;
+- return (0);
++ case V4L2_CID_AUDIO_MUTE:
++ ctrl->value = dev->muted;
++ return 0;
+ }
+
+ return -EINVAL;
+ }
+
+-static int vidioc_s_ctrl (struct file *file, void *priv,
+- struct v4l2_control *ctrl)
++static int vidioc_s_ctrl(struct file *file, void *priv,
++ struct v4l2_control *ctrl)
+ {
+- struct radio_device *card = video_drvdata(file);
++ struct maxiradio *dev = video_drvdata(file);
+
+ switch (ctrl->id) {
+- case V4L2_CID_AUDIO_MUTE:
+- card->muted = ctrl->value;
+- if(card->muted)
+- turn_power(card->io, 0);
+- else
+- set_freq(card->io, card->freq);
+- return 0;
++ case V4L2_CID_AUDIO_MUTE:
++ mutex_lock(&dev->lock);
++ dev->muted = ctrl->value;
++ if (dev->muted)
++ turn_power(dev, 0);
++ else
++ set_freq(dev, dev->freq);
++ mutex_unlock(&dev->lock);
++ return 0;
+ }
+
+ return -EINVAL;
+ }
+
++static int maxiradio_open(struct file *file)
++{
++ return 0;
++}
++
++static int maxiradio_release(struct file *file)
++{
++ return 0;
++}
++
++static const struct v4l2_file_operations maxiradio_fops = {
++ .owner = THIS_MODULE,
++ .open = maxiradio_open,
++ .release = maxiradio_release,
++ .ioctl = video_ioctl2,
++};
++
+ static const struct v4l2_ioctl_ops maxiradio_ioctl_ops = {
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+@@ -392,60 +371,84 @@ static const struct v4l2_ioctl_ops maxiradio_ioctl_ops = {
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ };
+
+-static struct video_device maxiradio_radio = {
+- .name = "Maxi Radio FM2000 radio",
+- .fops = &maxiradio_fops,
+- .ioctl_ops = &maxiradio_ioctl_ops,
+- .release = video_device_release_empty,
+-};
+-
+ static int __devinit maxiradio_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
+ {
+- if(!request_region(pci_resource_start(pdev, 0),
++ struct maxiradio *dev;
++ struct v4l2_device *v4l2_dev;
++ int retval = -ENOMEM;
++
++ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
++ if (dev == NULL) {
++ dev_err(&pdev->dev, "not enough memory\n");
++ return -ENOMEM;
++ }
++
++ v4l2_dev = &dev->v4l2_dev;
++ mutex_init(&dev->lock);
++ dev->pdev = pdev;
++ dev->muted = 1;
++ dev->freq = FREQ_LO;
++
++ strlcpy(v4l2_dev->name, "maxiradio", sizeof(v4l2_dev->name));
++
++ retval = v4l2_device_register(&pdev->dev, v4l2_dev);
++ if (retval < 0) {
++ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
++ goto errfr;
++ }
++
++ if (!request_region(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0), "Maxi Radio FM 2000")) {
+- printk(KERN_ERR "radio-maxiradio: can't reserve I/O ports\n");
++ v4l2_err(v4l2_dev, "can't reserve I/O ports\n");
+ goto err_out;
+ }
+
+ if (pci_enable_device(pdev))
+ goto err_out_free_region;
+
+- radio_unit.io = pci_resource_start(pdev, 0);
+- mutex_init(&radio_unit.lock);
+- video_set_drvdata(&maxiradio_radio, &radio_unit);
++ dev->io = pci_resource_start(pdev, 0);
++ strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
++ dev->vdev.v4l2_dev = v4l2_dev;
++ dev->vdev.fops = &maxiradio_fops;
++ dev->vdev.ioctl_ops = &maxiradio_ioctl_ops;
++ dev->vdev.release = video_device_release_empty;
++ video_set_drvdata(&dev->vdev, dev);
+
+- if (video_register_device(&maxiradio_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
+- printk("radio-maxiradio: can't register device!");
++ if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
++ v4l2_err(v4l2_dev, "can't register device!");
+ goto err_out_free_region;
+ }
+
+- printk(KERN_INFO "radio-maxiradio: version "
+- DRIVER_VERSION
+- " time "
+- __TIME__ " "
+- __DATE__
+- "\n");
++ v4l2_info(v4l2_dev, "version " DRIVER_VERSION
++ " time " __TIME__ " " __DATE__ "\n");
+
+- printk(KERN_INFO "radio-maxiradio: found Guillemot MAXI Radio device (io = 0x%x)\n",
+- radio_unit.io);
++ v4l2_info(v4l2_dev, "found Guillemot MAXI Radio device (io = 0x%x)\n",
++ dev->io);
+ return 0;
+
+ err_out_free_region:
+ release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+ err_out:
++ v4l2_device_unregister(v4l2_dev);
++errfr:
++ kfree(dev);
+ return -ENODEV;
+ }
+
+ static void __devexit maxiradio_remove_one(struct pci_dev *pdev)
+ {
+- video_unregister_device(&maxiradio_radio);
++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
++ struct maxiradio *dev = to_maxiradio(v4l2_dev);
++
++ video_unregister_device(&dev->vdev);
++ v4l2_device_unregister(&dev->v4l2_dev);
+ release_region(pci_resource_start(pdev, 0), pci_resource_len(pdev, 0));
+ }
+
+ static struct pci_device_id maxiradio_pci_tbl[] = {
+ { PCI_VENDOR_ID_GUILLEMOT, PCI_DEVICE_ID_GUILLEMOT_MAXIRADIO,
+ PCI_ANY_ID, PCI_ANY_ID, },
+- { 0,}
++ { 0 }
+ };
+
+ MODULE_DEVICE_TABLE(pci, maxiradio_pci_tbl);
+@@ -469,10 +472,3 @@ static void __exit maxiradio_radio_exit(void)
+
+ module_init(maxiradio_radio_init);
+ module_exit(maxiradio_radio_exit);
+-
+-MODULE_AUTHOR("Dimitromanolakis Apostolos, apdim@grecian.net");
+-MODULE_DESCRIPTION("Radio driver for the Guillemot Maxi Radio FM2000 radio.");
+-MODULE_LICENSE("GPL");
+-
+-module_param_named(debug,maxiradio_radio.debug, int, 0644);
+-MODULE_PARM_DESC(debug,"activates debug info");
+diff --git a/drivers/media/radio/radio-mr800.c b/drivers/media/radio/radio-mr800.c
+index fdfc7bf..ded25bf 100644
+--- a/drivers/media/radio/radio-mr800.c
++++ b/drivers/media/radio/radio-mr800.c
+@@ -22,7 +22,7 @@
+ */
+
+ /*
+- * Big thanks to authors of dsbr100.c and radio-si470x.c
++ * Big thanks to authors and contributors of dsbr100.c and radio-si470x.c
+ *
+ * When work was looked pretty good, i discover this:
+ * http://av-usbradio.sourceforge.net/index.php
+@@ -30,18 +30,23 @@
+ * Latest release of theirs project was in 2005.
+ * Probably, this driver could be improved trough using their
+ * achievements (specifications given).
+- * So, we have smth to begin with.
++ * Also, Faidon Liambotis <paravoid@debian.org> wrote nice driver for this radio
++ * in 2007. He allowed to use his driver to improve current mr800 radio driver.
++ * http://kerneltrap.org/mailarchive/linux-usb-devel/2007/10/11/342492
+ *
+- * History:
+ * Version 0.01: First working version.
+ * It's required to blacklist AverMedia USB Radio
+ * in usbhid/hid-quirks.c
++ * Version 0.10: A lot of cleanups and fixes: unpluging the device,
++ * few mutex locks were added, codinstyle issues, etc.
++ * Added stereo support. Thanks to
++ * Douglas Schilling Landgraf <dougsland@gmail.com> and
++ * David Ellingsworth <david@identd.dyndns.org>
++ * for discussion, help and support.
+ *
+ * Many things to do:
+ * - Correct power managment of device (suspend & resume)
+- * - Make x86 independance (little-endian and big-endian stuff)
+ * - Add code for scanning and smooth tuning
+- * - Checked and add stereo&mono stuff
+ * - Add code for sensitivity value
+ * - Correct mistakes
+ * - In Japan another FREQ_MIN and FREQ_MAX
+@@ -62,8 +67,8 @@
+ /* driver and module definitions */
+ #define DRIVER_AUTHOR "Alexey Klimov <klimov.linux@gmail.com>"
+ #define DRIVER_DESC "AverMedia MR 800 USB FM radio driver"
+-#define DRIVER_VERSION "0.01"
+-#define RADIO_VERSION KERNEL_VERSION(0, 0, 1)
++#define DRIVER_VERSION "0.10"
++#define RADIO_VERSION KERNEL_VERSION(0, 1, 0)
+
+ MODULE_AUTHOR(DRIVER_AUTHOR);
+ MODULE_DESCRIPTION(DRIVER_DESC);
+@@ -87,6 +92,22 @@ devices, that would be 76 and 91. */
+ #define FREQ_MAX 108.0
+ #define FREQ_MUL 16000
+
++/*
++ * Commands that device should understand
++ * List isnt full and will be updated with implementation of new functions
++ */
++#define AMRADIO_SET_FREQ 0xa4
++#define AMRADIO_SET_MUTE 0xab
++#define AMRADIO_SET_MONO 0xae
++
++/* Comfortable defines for amradio_set_mute */
++#define AMRADIO_START 0x00
++#define AMRADIO_STOP 0x01
++
++/* Comfortable defines for amradio_set_stereo */
++#define WANT_STEREO 0x00
++#define WANT_MONO 0x01
++
+ /* module parameter */
+ static int radio_nr = -1;
+ module_param(radio_nr, int, 0);
+@@ -169,43 +190,48 @@ static struct usb_driver usb_amradio_driver = {
+ .supports_autosuspend = 0,
+ };
+
+-/* switch on radio. Send 8 bytes to device. */
+-static int amradio_start(struct amradio_device *radio)
++/* switch on/off the radio. Send 8 bytes to device */
++static int amradio_set_mute(struct amradio_device *radio, char argument)
+ {
+ int retval;
+ int size;
+
++ /* safety check */
++ if (radio->removed)
++ return -EIO;
++
+ mutex_lock(&radio->lock);
+
+ radio->buffer[0] = 0x00;
+ radio->buffer[1] = 0x55;
+ radio->buffer[2] = 0xaa;
+ radio->buffer[3] = 0x00;
+- radio->buffer[4] = 0xab;
+- radio->buffer[5] = 0x00;
++ radio->buffer[4] = AMRADIO_SET_MUTE;
++ radio->buffer[5] = argument;
+ radio->buffer[6] = 0x00;
+ radio->buffer[7] = 0x00;
+
+ retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
+ (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
+
+- if (retval) {
++ if (retval < 0 || size != BUFFER_LENGTH) {
+ mutex_unlock(&radio->lock);
+ return retval;
+ }
+
+- radio->muted = 0;
++ radio->muted = argument;
+
+ mutex_unlock(&radio->lock);
+
+ return retval;
+ }
+
+-/* switch off radio */
+-static int amradio_stop(struct amradio_device *radio)
++/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
++static int amradio_setfreq(struct amradio_device *radio, int freq)
+ {
+ int retval;
+ int size;
++ unsigned short freq_send = 0x10 + (freq >> 3) / 25;
+
+ /* safety check */
+ if (radio->removed)
+@@ -216,33 +242,46 @@ static int amradio_stop(struct amradio_device *radio)
+ radio->buffer[0] = 0x00;
+ radio->buffer[1] = 0x55;
+ radio->buffer[2] = 0xaa;
+- radio->buffer[3] = 0x00;
+- radio->buffer[4] = 0xab;
+- radio->buffer[5] = 0x01;
++ radio->buffer[3] = 0x03;
++ radio->buffer[4] = AMRADIO_SET_FREQ;
++ radio->buffer[5] = 0x00;
+ radio->buffer[6] = 0x00;
+- radio->buffer[7] = 0x00;
++ radio->buffer[7] = 0x08;
+
+ retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
+ (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
+
+- if (retval) {
++ if (retval < 0 || size != BUFFER_LENGTH) {
+ mutex_unlock(&radio->lock);
+ return retval;
+ }
+
+- radio->muted = 1;
++ /* frequency is calculated from freq_send and placed in first 2 bytes */
++ radio->buffer[0] = (freq_send >> 8) & 0xff;
++ radio->buffer[1] = freq_send & 0xff;
++ radio->buffer[2] = 0x01;
++ radio->buffer[3] = 0x00;
++ radio->buffer[4] = 0x00;
++ /* 5 and 6 bytes of buffer already = 0x00 */
++ radio->buffer[7] = 0x00;
++
++ retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
++ (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
++
++ if (retval < 0 || size != BUFFER_LENGTH) {
++ mutex_unlock(&radio->lock);
++ return retval;
++ }
+
+ mutex_unlock(&radio->lock);
+
+ return retval;
+ }
+
+-/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
+-static int amradio_setfreq(struct amradio_device *radio, int freq)
++static int amradio_set_stereo(struct amradio_device *radio, char argument)
+ {
+ int retval;
+ int size;
+- unsigned short freq_send = 0x13 + (freq >> 3) / 25;
+
+ /* safety check */
+ if (radio->removed)
+@@ -253,50 +292,33 @@ static int amradio_setfreq(struct amradio_device *radio, int freq)
+ radio->buffer[0] = 0x00;
+ radio->buffer[1] = 0x55;
+ radio->buffer[2] = 0xaa;
+- radio->buffer[3] = 0x03;
+- radio->buffer[4] = 0xa4;
+- radio->buffer[5] = 0x00;
+- radio->buffer[6] = 0x00;
+- radio->buffer[7] = 0x08;
+-
+- retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
+- (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
+-
+- if (retval) {
+- mutex_unlock(&radio->lock);
+- return retval;
+- }
+-
+- /* frequency is calculated from freq_send and placed in first 2 bytes */
+- radio->buffer[0] = (freq_send >> 8) & 0xff;
+- radio->buffer[1] = freq_send & 0xff;
+- radio->buffer[2] = 0x01;
+ radio->buffer[3] = 0x00;
+- radio->buffer[4] = 0x00;
+- /* 5 and 6 bytes of buffer already = 0x00 */
++ radio->buffer[4] = AMRADIO_SET_MONO;
++ radio->buffer[5] = argument;
++ radio->buffer[6] = 0x00;
+ radio->buffer[7] = 0x00;
+
+ retval = usb_bulk_msg(radio->usbdev, usb_sndintpipe(radio->usbdev, 2),
+ (void *) (radio->buffer), BUFFER_LENGTH, &size, USB_TIMEOUT);
+
+- if (retval) {
++ if (retval < 0 || size != BUFFER_LENGTH) {
++ radio->stereo = -1;
+ mutex_unlock(&radio->lock);
+ return retval;
+ }
+
+- radio->stereo = 0;
++ radio->stereo = 1;
+
+ mutex_unlock(&radio->lock);
+
+ return retval;
+ }
+
+-/* USB subsystem interface begins here */
+-
+-/* 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. */
++/* Handle unplugging the device.
++ * We call video_unregister_device in any case.
++ * The last function called in this procedure is
++ * usb_amradio_device_release.
++ */
+ static void usb_amradio_disconnect(struct usb_interface *intf)
+ {
+ struct amradio_device *radio = usb_get_intfdata(intf);
+@@ -313,9 +335,11 @@ static void usb_amradio_disconnect(struct usb_interface *intf)
+ static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *v)
+ {
++ struct amradio_device *radio = video_drvdata(file);
++
+ strlcpy(v->driver, "radio-mr800", sizeof(v->driver));
+ strlcpy(v->card, "AverMedia MR 800 USB FM Radio", sizeof(v->card));
+- sprintf(v->bus_info, "USB");
++ usb_make_path(radio->usbdev, v->bus_info, sizeof(v->bus_info));
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER;
+ return 0;
+@@ -326,6 +350,7 @@ static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
++ int retval;
+
+ /* safety check */
+ if (radio->removed)
+@@ -337,7 +362,16 @@ static int vidioc_g_tuner(struct file *file, void *priv,
+ /* TODO: Add function which look is signal stereo or not
+ * amradio_getstat(radio);
+ */
+- radio->stereo = -1;
++
++/* we call amradio_set_stereo to set radio->stereo
++ * Honestly, amradio_getstat should cover this in future and
++ * amradio_set_stereo shouldn't be here
++ */
++ retval = amradio_set_stereo(radio, WANT_STEREO);
++ if (retval < 0)
++ amradio_dev_warn(&radio->videodev->dev,
++ "set stereo failed\n");
++
+ strcpy(v->name, "FM");
+ v->type = V4L2_TUNER_RADIO;
+ v->rangelow = FREQ_MIN * FREQ_MUL;
+@@ -358,6 +392,7 @@ static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
++ int retval;
+
+ /* safety check */
+ if (radio->removed)
+@@ -365,6 +400,25 @@ static int vidioc_s_tuner(struct file *file, void *priv,
+
+ if (v->index > 0)
+ return -EINVAL;
++
++ /* mono/stereo selector */
++ switch (v->audmode) {
++ case V4L2_TUNER_MODE_MONO:
++ retval = amradio_set_stereo(radio, WANT_MONO);
++ if (retval < 0)
++ amradio_dev_warn(&radio->videodev->dev,
++ "set mono failed\n");
++ break;
++ case V4L2_TUNER_MODE_STEREO:
++ retval = amradio_set_stereo(radio, WANT_STEREO);
++ if (retval < 0)
++ amradio_dev_warn(&radio->videodev->dev,
++ "set stereo failed\n");
++ break;
++ default:
++ return -EINVAL;
++ }
++
+ return 0;
+ }
+
+@@ -373,13 +427,18 @@ static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
++ int retval;
+
+ /* safety check */
+ if (radio->removed)
+ return -EIO;
+
++ mutex_lock(&radio->lock);
+ radio->curfreq = f->frequency;
+- if (amradio_setfreq(radio, radio->curfreq) < 0)
++ mutex_unlock(&radio->lock);
++
++ retval = amradio_setfreq(radio, radio->curfreq);
++ if (retval < 0)
+ amradio_dev_warn(&radio->videodev->dev,
+ "set frequency failed\n");
+ return 0;
+@@ -438,6 +497,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
++ int retval;
+
+ /* safety check */
+ if (radio->removed)
+@@ -446,13 +506,15 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value) {
+- if (amradio_stop(radio) < 0) {
++ retval = amradio_set_mute(radio, AMRADIO_STOP);
++ if (retval < 0) {
+ amradio_dev_warn(&radio->videodev->dev,
+ "amradio_stop failed\n");
+ return -1;
+ }
+ } else {
+- if (amradio_start(radio) < 0) {
++ retval = amradio_set_mute(radio, AMRADIO_START);
++ if (retval < 0) {
+ amradio_dev_warn(&radio->videodev->dev,
+ "amradio_start failed\n");
+ return -1;
+@@ -503,20 +565,29 @@ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+ static int usb_amradio_open(struct file *file)
+ {
+ struct amradio_device *radio = video_get_drvdata(video_devdata(file));
++ int retval;
+
+ lock_kernel();
+
+ radio->users = 1;
+ radio->muted = 1;
+
+- if (amradio_start(radio) < 0) {
++ retval = amradio_set_mute(radio, AMRADIO_START);
++ if (retval < 0) {
+ amradio_dev_warn(&radio->videodev->dev,
+ "radio did not start up properly\n");
+ radio->users = 0;
+ unlock_kernel();
+ return -EIO;
+ }
+- if (amradio_setfreq(radio, radio->curfreq) < 0)
++
++ retval = amradio_set_stereo(radio, WANT_STEREO);
++ if (retval < 0)
++ amradio_dev_warn(&radio->videodev->dev,
++ "set stereo failed\n");
++
++ retval = amradio_setfreq(radio, radio->curfreq);
++ if (retval < 0)
+ amradio_dev_warn(&radio->videodev->dev,
+ "set frequency failed\n");
+
+@@ -533,10 +604,12 @@ static int usb_amradio_close(struct file *file)
+ if (!radio)
+ return -ENODEV;
+
++ mutex_lock(&radio->lock);
+ radio->users = 0;
++ mutex_unlock(&radio->lock);
+
+ if (!radio->removed) {
+- retval = amradio_stop(radio);
++ retval = amradio_set_mute(radio, AMRADIO_STOP);
+ if (retval < 0)
+ amradio_dev_warn(&radio->videodev->dev,
+ "amradio_stop failed\n");
+@@ -549,8 +622,10 @@ static int usb_amradio_close(struct file *file)
+ static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message)
+ {
+ struct amradio_device *radio = usb_get_intfdata(intf);
++ int retval;
+
+- if (amradio_stop(radio) < 0)
++ retval = amradio_set_mute(radio, AMRADIO_STOP);
++ if (retval < 0)
+ dev_warn(&intf->dev, "amradio_stop failed\n");
+
+ dev_info(&intf->dev, "going into suspend..\n");
+@@ -562,8 +637,10 @@ static int usb_amradio_suspend(struct usb_interface *intf, pm_message_t message)
+ static int usb_amradio_resume(struct usb_interface *intf)
+ {
+ struct amradio_device *radio = usb_get_intfdata(intf);
++ int retval;
+
+- if (amradio_start(radio) < 0)
++ retval = amradio_set_mute(radio, AMRADIO_START);
++ if (retval < 0)
+ dev_warn(&intf->dev, "amradio_start failed\n");
+
+ dev_info(&intf->dev, "coming out of suspend..\n");
+@@ -614,28 +691,32 @@ static struct video_device amradio_videodev_template = {
+ .release = usb_amradio_device_release,
+ };
+
+-/* check if the device is present and register with v4l and
+-usb if it is */
++/* check if the device is present and register with v4l and usb if it is */
+ static int usb_amradio_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+ {
+ struct amradio_device *radio;
++ int retval;
+
+ radio = kmalloc(sizeof(struct amradio_device), GFP_KERNEL);
+
+- if (!(radio))
++ if (!radio) {
++ dev_err(&intf->dev, "kmalloc for amradio_device failed\n");
+ return -ENOMEM;
++ }
+
+ radio->buffer = kmalloc(BUFFER_LENGTH, GFP_KERNEL);
+
+- if (!(radio->buffer)) {
++ if (!radio->buffer) {
++ dev_err(&intf->dev, "kmalloc for radio->buffer failed\n");
+ kfree(radio);
+ return -ENOMEM;
+ }
+
+ radio->videodev = video_device_alloc();
+
+- if (!(radio->videodev)) {
++ if (!radio->videodev) {
++ dev_err(&intf->dev, "video_device_alloc failed\n");
+ kfree(radio->buffer);
+ kfree(radio);
+ return -ENOMEM;
+@@ -648,12 +729,14 @@ static int usb_amradio_probe(struct usb_interface *intf,
+ radio->users = 0;
+ radio->usbdev = interface_to_usbdev(intf);
+ radio->curfreq = 95.16 * FREQ_MUL;
++ radio->stereo = -1;
+
+ mutex_init(&radio->lock);
+
+ video_set_drvdata(radio->videodev, radio);
+- if (video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr)) {
+- dev_warn(&intf->dev, "could not register video device\n");
++ retval = video_register_device(radio->videodev, VFL_TYPE_RADIO, radio_nr);
++ if (retval < 0) {
++ dev_err(&intf->dev, "could not register video device\n");
+ video_device_release(radio->videodev);
+ kfree(radio->buffer);
+ kfree(radio);
+diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c
+index 2587227..d1e6b01 100644
+--- a/drivers/media/radio/radio-rtrack2.c
++++ b/drivers/media/radio/radio-rtrack2.c
+@@ -13,34 +13,16 @@
+ #include <linux/init.h> /* Initdata */
+ #include <linux/ioport.h> /* request_region */
+ #include <linux/delay.h> /* udelay */
+-#include <asm/io.h> /* outb, outb_p */
+-#include <asm/uaccess.h> /* copy to/from user */
+ #include <linux/videodev2.h> /* kernel radio structs */
+-#include <media/v4l2-common.h>
++#include <linux/mutex.h>
++#include <linux/version.h> /* for KERNEL_VERSION MACRO */
++#include <linux/io.h> /* outb, outb_p */
++#include <media/v4l2-device.h>
+ #include <media/v4l2-ioctl.h>
+-#include <linux/spinlock.h>
+
+-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+-#define RADIO_VERSION KERNEL_VERSION(0,0,2)
+-
+-static struct v4l2_queryctrl radio_qctrl[] = {
+- {
+- .id = V4L2_CID_AUDIO_MUTE,
+- .name = "Mute",
+- .minimum = 0,
+- .maximum = 1,
+- .default_value = 1,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- },{
+- .id = V4L2_CID_AUDIO_VOLUME,
+- .name = "Volume",
+- .minimum = 0,
+- .maximum = 65535,
+- .step = 65535,
+- .default_value = 0xff,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- }
+-};
++MODULE_AUTHOR("Ben Pfaff");
++MODULE_DESCRIPTION("A driver for the RadioTrack II radio card.");
++MODULE_LICENSE("GPL");
+
+ #ifndef CONFIG_RADIO_RTRACK2_PORT
+ #define CONFIG_RADIO_RTRACK2_PORT -1
+@@ -48,79 +30,88 @@ static struct v4l2_queryctrl radio_qctrl[] = {
+
+ static int io = CONFIG_RADIO_RTRACK2_PORT;
+ static int radio_nr = -1;
+-static spinlock_t lock;
+
+-struct rt_device
++module_param(io, int, 0);
++MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20c or 0x30c)");
++module_param(radio_nr, int, 0);
++
++#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
++
++struct rtrack2
+ {
+- unsigned long in_use;
+- int port;
++ struct v4l2_device v4l2_dev;
++ struct video_device vdev;
++ int io;
+ unsigned long curfreq;
+ int muted;
++ struct mutex lock;
+ };
+
++static struct rtrack2 rtrack2_card;
++
+
+ /* local things */
+
+-static void rt_mute(struct rt_device *dev)
++static void rt_mute(struct rtrack2 *dev)
+ {
+- if(dev->muted)
++ if (dev->muted)
+ return;
+- spin_lock(&lock);
+- outb(1, io);
+- spin_unlock(&lock);
++ mutex_lock(&dev->lock);
++ outb(1, dev->io);
++ mutex_unlock(&dev->lock);
+ dev->muted = 1;
+ }
+
+-static void rt_unmute(struct rt_device *dev)
++static void rt_unmute(struct rtrack2 *dev)
+ {
+ if(dev->muted == 0)
+ return;
+- spin_lock(&lock);
+- outb(0, io);
+- spin_unlock(&lock);
++ mutex_lock(&dev->lock);
++ outb(0, dev->io);
++ mutex_unlock(&dev->lock);
+ dev->muted = 0;
+ }
+
+-static void zero(void)
++static void zero(struct rtrack2 *dev)
+ {
+- outb_p(1, io);
+- outb_p(3, io);
+- outb_p(1, io);
++ outb_p(1, dev->io);
++ outb_p(3, dev->io);
++ outb_p(1, dev->io);
+ }
+
+-static void one(void)
++static void one(struct rtrack2 *dev)
+ {
+- outb_p(5, io);
+- outb_p(7, io);
+- outb_p(5, io);
++ outb_p(5, dev->io);
++ outb_p(7, dev->io);
++ outb_p(5, dev->io);
+ }
+
+-static int rt_setfreq(struct rt_device *dev, unsigned long freq)
++static int rt_setfreq(struct rtrack2 *dev, unsigned long freq)
+ {
+ int i;
+
++ mutex_lock(&dev->lock);
++ dev->curfreq = freq;
+ freq = freq / 200 + 856;
+
+- spin_lock(&lock);
+-
+- outb_p(0xc8, io);
+- outb_p(0xc9, io);
+- outb_p(0xc9, io);
++ outb_p(0xc8, dev->io);
++ outb_p(0xc9, dev->io);
++ outb_p(0xc9, dev->io);
+
+ for (i = 0; i < 10; i++)
+- zero ();
++ zero(dev);
+
+ for (i = 14; i >= 0; i--)
+ if (freq & (1 << i))
+- one ();
++ one(dev);
+ else
+- zero ();
++ zero(dev);
+
+- outb_p(0xc8, io);
++ outb_p(0xc8, dev->io);
+ if (!dev->muted)
+- outb_p(0, io);
++ outb_p(0, dev->io);
+
+- spin_unlock(&lock);
++ mutex_unlock(&dev->lock);
+ return 0;
+ }
+
+@@ -129,61 +120,61 @@ static int vidioc_querycap(struct file *file, void *priv,
+ {
+ strlcpy(v->driver, "radio-rtrack2", sizeof(v->driver));
+ strlcpy(v->card, "RadioTrack II", sizeof(v->card));
+- sprintf(v->bus_info, "ISA");
++ strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
+ v->version = RADIO_VERSION;
+- v->capabilities = V4L2_CAP_TUNER;
++ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+ }
+
+ static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- if (v->index > 0)
+- return -EINVAL;
+-
+- return 0;
++ return v->index ? -EINVAL : 0;
+ }
+
+-static int rt_getsigstr(struct rt_device *dev)
++static int rt_getsigstr(struct rtrack2 *dev)
+ {
+- if (inb(io) & 2) /* bit set = no signal present */
+- return 0;
+- return 1; /* signal present */
++ int sig = 1;
++
++ mutex_lock(&dev->lock);
++ if (inb(dev->io) & 2) /* bit set = no signal present */
++ sig = 0;
++ mutex_unlock(&dev->lock);
++ return sig;
+ }
+
+ static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- struct rt_device *rt = video_drvdata(file);
++ struct rtrack2 *rt = video_drvdata(file);
+
+ if (v->index > 0)
+ return -EINVAL;
+
+- strcpy(v->name, "FM");
++ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+- v->rangelow = (88*16000);
+- v->rangehigh = (108*16000);
++ v->rangelow = 88 * 16000;
++ v->rangehigh = 108 * 16000;
+ v->rxsubchans = V4L2_TUNER_SUB_MONO;
+ v->capability = V4L2_TUNER_CAP_LOW;
+ v->audmode = V4L2_TUNER_MODE_MONO;
+- v->signal = 0xFFFF*rt_getsigstr(rt);
++ v->signal = 0xFFFF * rt_getsigstr(rt);
+ return 0;
+ }
+
+ static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct rt_device *rt = video_drvdata(file);
++ struct rtrack2 *rt = video_drvdata(file);
+
+- rt->curfreq = f->frequency;
+- rt_setfreq(rt, rt->curfreq);
++ rt_setfreq(rt, f->frequency);
+ return 0;
+ }
+
+ static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct rt_device *rt = video_drvdata(file);
++ struct rtrack2 *rt = video_drvdata(file);
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = rt->curfreq;
+@@ -193,14 +184,11 @@ static int vidioc_g_frequency(struct file *file, void *priv,
+ static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+ {
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+- if (qc->id && qc->id == radio_qctrl[i].id) {
+- memcpy(qc, &(radio_qctrl[i]),
+- sizeof(*qc));
+- return 0;
+- }
++ switch (qc->id) {
++ case V4L2_CID_AUDIO_MUTE:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
++ case V4L2_CID_AUDIO_VOLUME:
++ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535, 65535);
+ }
+ return -EINVAL;
+ }
+@@ -208,7 +196,7 @@ static int vidioc_queryctrl(struct file *file, void *priv,
+ static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct rt_device *rt = video_drvdata(file);
++ struct rtrack2 *rt = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+@@ -227,7 +215,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
+ static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct rt_device *rt = video_drvdata(file);
++ struct rtrack2 *rt = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+@@ -246,17 +234,6 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
+ return -EINVAL;
+ }
+
+-static int vidioc_g_audio(struct file *file, void *priv,
+- struct v4l2_audio *a)
+-{
+- if (a->index > 1)
+- return -EINVAL;
+-
+- strcpy(a->name, "Radio");
+- a->capability = V4L2_AUDCAP_STEREO;
+- return 0;
+-}
+-
+ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+ {
+ *i = 0;
+@@ -265,36 +242,38 @@ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+
+ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+ {
+- if (i != 0)
+- return -EINVAL;
+- return 0;
++ return i ? -EINVAL : 0;
+ }
+
+-static int vidioc_s_audio(struct file *file, void *priv,
++static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+ {
+- if (a->index != 0)
+- return -EINVAL;
++ a->index = 0;
++ strlcpy(a->name, "Radio", sizeof(a->name));
++ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+ }
+
+-static struct rt_device rtrack2_unit;
++static int vidioc_s_audio(struct file *file, void *priv,
++ struct v4l2_audio *a)
++{
++ return a->index ? -EINVAL : 0;
++}
+
+-static int rtrack2_exclusive_open(struct file *file)
++static int rtrack2_open(struct file *file)
+ {
+- return test_and_set_bit(0, &rtrack2_unit.in_use) ? -EBUSY : 0;
++ return 0;
+ }
+
+-static int rtrack2_exclusive_release(struct file *file)
++static int rtrack2_release(struct file *file)
+ {
+- clear_bit(0, &rtrack2_unit.in_use);
+ return 0;
+ }
+
+ static const struct v4l2_file_operations rtrack2_fops = {
+ .owner = THIS_MODULE,
+- .open = rtrack2_exclusive_open,
+- .release = rtrack2_exclusive_release,
++ .open = rtrack2_open,
++ .release = rtrack2_release,
+ .ioctl = video_ioctl2,
+ };
+
+@@ -313,62 +292,61 @@ static const struct v4l2_ioctl_ops rtrack2_ioctl_ops = {
+ .vidioc_s_input = vidioc_s_input,
+ };
+
+-static struct video_device rtrack2_radio = {
+- .name = "RadioTrack II radio",
+- .fops = &rtrack2_fops,
+- .ioctl_ops = &rtrack2_ioctl_ops,
+- .release = video_device_release_empty,
+-};
+-
+ static int __init rtrack2_init(void)
+ {
+- if(io==-1)
+- {
+- printk(KERN_ERR "You must set an I/O address with io=0x20c or io=0x30c\n");
++ struct rtrack2 *dev = &rtrack2_card;
++ struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
++ int res;
++
++ strlcpy(v4l2_dev->name, "rtrack2", sizeof(v4l2_dev->name));
++ dev->io = io;
++ if (dev->io == -1) {
++ v4l2_err(v4l2_dev, "You must set an I/O address with io=0x20c or io=0x30c\n");
+ return -EINVAL;
+ }
+- if (!request_region(io, 4, "rtrack2"))
+- {
+- printk(KERN_ERR "rtrack2: port 0x%x already in use\n", io);
++ if (!request_region(dev->io, 4, "rtrack2")) {
++ v4l2_err(v4l2_dev, "port 0x%x already in use\n", dev->io);
+ return -EBUSY;
+ }
+
+- video_set_drvdata(&rtrack2_radio, &rtrack2_unit);
++ res = v4l2_device_register(NULL, v4l2_dev);
++ if (res < 0) {
++ release_region(dev->io, 4);
++ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
++ return res;
++ }
+
+- spin_lock_init(&lock);
+- if (video_register_device(&rtrack2_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
+- release_region(io, 4);
++ strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
++ dev->vdev.v4l2_dev = v4l2_dev;
++ dev->vdev.fops = &rtrack2_fops;
++ dev->vdev.ioctl_ops = &rtrack2_ioctl_ops;
++ dev->vdev.release = video_device_release_empty;
++ video_set_drvdata(&dev->vdev, dev);
++
++ mutex_init(&dev->lock);
++ if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
++ v4l2_device_unregister(v4l2_dev);
++ release_region(dev->io, 4);
+ return -EINVAL;
+ }
+
+- printk(KERN_INFO "AIMSlab Radiotrack II card driver.\n");
++ v4l2_info(v4l2_dev, "AIMSlab Radiotrack II card driver.\n");
+
+ /* mute card - prevents noisy bootups */
+- outb(1, io);
+- rtrack2_unit.muted = 1;
++ outb(1, dev->io);
++ dev->muted = 1;
+
+ return 0;
+ }
+
+-MODULE_AUTHOR("Ben Pfaff");
+-MODULE_DESCRIPTION("A driver for the RadioTrack II radio card.");
+-MODULE_LICENSE("GPL");
+-
+-module_param(io, int, 0);
+-MODULE_PARM_DESC(io, "I/O address of the RadioTrack card (0x20c or 0x30c)");
+-module_param(radio_nr, int, 0);
+-
+-static void __exit rtrack2_cleanup_module(void)
++static void __exit rtrack2_exit(void)
+ {
+- video_unregister_device(&rtrack2_radio);
+- release_region(io,4);
++ struct rtrack2 *dev = &rtrack2_card;
++
++ video_unregister_device(&dev->vdev);
++ v4l2_device_unregister(&dev->v4l2_dev);
++ release_region(dev->io, 4);
+ }
+
+ module_init(rtrack2_init);
+-module_exit(rtrack2_cleanup_module);
+-
+-/*
+- Local variables:
+- compile-command: "mmake"
+- End:
+-*/
++module_exit(rtrack2_exit);
+diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
+index d358e48..f4784f0 100644
+--- a/drivers/media/radio/radio-sf16fmi.c
++++ b/drivers/media/radio/radio-sf16fmi.c
+@@ -22,113 +22,109 @@
+ #include <linux/init.h> /* Initdata */
+ #include <linux/ioport.h> /* request_region */
+ #include <linux/delay.h> /* udelay */
+-#include <linux/videodev2.h> /* kernel radio structs */
+-#include <media/v4l2-common.h>
+-#include <media/v4l2-ioctl.h>
+ #include <linux/isapnp.h>
+-#include <asm/io.h> /* outb, outb_p */
+-#include <asm/uaccess.h> /* copy to/from user */
+ #include <linux/mutex.h>
++#include <linux/videodev2.h> /* kernel radio structs */
++#include <linux/io.h> /* outb, outb_p */
++#include <media/v4l2-device.h>
++#include <media/v4l2-ioctl.h>
+
+-#define RADIO_VERSION KERNEL_VERSION(0,0,2)
++MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
++MODULE_DESCRIPTION("A driver for the SF16MI radio.");
++MODULE_LICENSE("GPL");
+
+-static struct v4l2_queryctrl radio_qctrl[] = {
+- {
+- .id = V4L2_CID_AUDIO_MUTE,
+- .name = "Mute",
+- .minimum = 0,
+- .maximum = 1,
+- .default_value = 1,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- }
+-};
++static int io = -1;
++static int radio_nr = -1;
++
++module_param(io, int, 0);
++MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)");
++module_param(radio_nr, int, 0);
++
++#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
+
+-struct fmi_device
++struct fmi
+ {
+- unsigned long in_use;
+- int port;
++ struct v4l2_device v4l2_dev;
++ struct video_device vdev;
++ int io;
+ int curvol; /* 1 or 0 */
+ unsigned long curfreq; /* freq in kHz */
+ __u32 flags;
++ struct mutex lock;
+ };
+
+-static int io = -1;
+-static int radio_nr = -1;
+-static struct pnp_dev *dev = NULL;
+-static struct mutex lock;
++static struct fmi fmi_card;
++static struct pnp_dev *dev;
+
+ /* freq is in 1/16 kHz to internal number, hw precision is 50 kHz */
+ /* It is only useful to give freq in intervall of 800 (=0.05Mhz),
+ * other bits will be truncated, e.g 92.7400016 -> 92.7, but
+ * 92.7400017 -> 92.75
+ */
+-#define RSF16_ENCODE(x) ((x)/800+214)
+-#define RSF16_MINFREQ 87*16000
+-#define RSF16_MAXFREQ 108*16000
++#define RSF16_ENCODE(x) ((x) / 800 + 214)
++#define RSF16_MINFREQ (87 * 16000)
++#define RSF16_MAXFREQ (108 * 16000)
+
+-static void outbits(int bits, unsigned int data, int port)
++static void outbits(int bits, unsigned int data, int io)
+ {
+- while(bits--) {
+- if(data & 1) {
+- outb(5, port);
++ while (bits--) {
++ if (data & 1) {
++ outb(5, io);
+ udelay(6);
+- outb(7, port);
++ outb(7, io);
+ udelay(6);
+ } else {
+- outb(1, port);
++ outb(1, io);
+ udelay(6);
+- outb(3, port);
++ outb(3, io);
+ udelay(6);
+ }
+- data>>=1;
++ data >>= 1;
+ }
+ }
+
+-static inline void fmi_mute(int port)
++static inline void fmi_mute(struct fmi *fmi)
+ {
+- mutex_lock(&lock);
+- outb(0x00, port);
+- mutex_unlock(&lock);
++ mutex_lock(&fmi->lock);
++ outb(0x00, fmi->io);
++ mutex_unlock(&fmi->lock);
+ }
+
+-static inline void fmi_unmute(int port)
++static inline void fmi_unmute(struct fmi *fmi)
+ {
+- mutex_lock(&lock);
+- outb(0x08, port);
+- mutex_unlock(&lock);
++ mutex_lock(&fmi->lock);
++ outb(0x08, fmi->io);
++ mutex_unlock(&fmi->lock);
+ }
+
+-static inline int fmi_setfreq(struct fmi_device *dev)
++static inline int fmi_setfreq(struct fmi *fmi, unsigned long freq)
+ {
+- int myport = dev->port;
+- unsigned long freq = dev->curfreq;
++ mutex_lock(&fmi->lock);
++ fmi->curfreq = freq;
+
+- mutex_lock(&lock);
+-
+- outbits(16, RSF16_ENCODE(freq), myport);
+- outbits(8, 0xC0, myport);
++ outbits(16, RSF16_ENCODE(freq), fmi->io);
++ outbits(8, 0xC0, fmi->io);
+ msleep(143); /* was schedule_timeout(HZ/7) */
+- mutex_unlock(&lock);
+- if (dev->curvol) fmi_unmute(myport);
++ mutex_unlock(&fmi->lock);
++ if (fmi->curvol)
++ fmi_unmute(fmi);
+ return 0;
+ }
+
+-static inline int fmi_getsigstr(struct fmi_device *dev)
++static inline int fmi_getsigstr(struct fmi *fmi)
+ {
+ int val;
+ int res;
+- int myport = dev->port;
+-
+
+- mutex_lock(&lock);
+- val = dev->curvol ? 0x08 : 0x00; /* unmute/mute */
+- outb(val, myport);
+- outb(val | 0x10, myport);
++ mutex_lock(&fmi->lock);
++ val = fmi->curvol ? 0x08 : 0x00; /* unmute/mute */
++ outb(val, fmi->io);
++ outb(val | 0x10, fmi->io);
+ msleep(143); /* was schedule_timeout(HZ/7) */
+- res = (int)inb(myport+1);
+- outb(val, myport);
++ res = (int)inb(fmi->io + 1);
++ outb(val, fmi->io);
+
+- mutex_unlock(&lock);
++ mutex_unlock(&fmi->lock);
+ return (res & 2) ? 0 : 0xFFFF;
+ }
+
+@@ -137,9 +133,9 @@ static int vidioc_querycap(struct file *file, void *priv,
+ {
+ strlcpy(v->driver, "radio-sf16fmi", sizeof(v->driver));
+ strlcpy(v->card, "SF16-FMx radio", sizeof(v->card));
+- sprintf(v->bus_info, "ISA");
++ strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
+ v->version = RADIO_VERSION;
+- v->capabilities = V4L2_CAP_TUNER;
++ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+ }
+
+@@ -147,18 +143,18 @@ static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+ int mult;
+- struct fmi_device *fmi = video_drvdata(file);
++ struct fmi *fmi = video_drvdata(file);
+
+ if (v->index > 0)
+ return -EINVAL;
+
+- strcpy(v->name, "FM");
++ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+ mult = (fmi->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000;
+- v->rangelow = RSF16_MINFREQ/mult;
+- v->rangehigh = RSF16_MAXFREQ/mult;
++ v->rangelow = RSF16_MINFREQ / mult;
++ v->rangehigh = RSF16_MAXFREQ / mult;
+ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO;
+- v->capability = fmi->flags&V4L2_TUNER_CAP_LOW;
++ v->capability = fmi->flags & V4L2_TUNER_CAP_LOW;
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ v->signal = fmi_getsigstr(fmi);
+ return 0;
+@@ -167,32 +163,29 @@ static int vidioc_g_tuner(struct file *file, void *priv,
+ static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- if (v->index > 0)
+- return -EINVAL;
+- return 0;
++ return v->index ? -EINVAL : 0;
+ }
+
+ static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct fmi_device *fmi = video_drvdata(file);
++ struct fmi *fmi = video_drvdata(file);
+
+ if (!(fmi->flags & V4L2_TUNER_CAP_LOW))
+ f->frequency *= 1000;
+ if (f->frequency < RSF16_MINFREQ ||
+- f->frequency > RSF16_MAXFREQ )
++ f->frequency > RSF16_MAXFREQ)
+ return -EINVAL;
+- /*rounding in steps of 800 to match th freq
+- that will be used */
+- fmi->curfreq = (f->frequency/800)*800;
+- fmi_setfreq(fmi);
++ /* rounding in steps of 800 to match the freq
++ that will be used */
++ fmi_setfreq(fmi, (f->frequency / 800) * 800);
+ return 0;
+ }
+
+ static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct fmi_device *fmi = video_drvdata(file);
++ struct fmi *fmi = video_drvdata(file);
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = fmi->curfreq;
+@@ -204,14 +197,9 @@ static int vidioc_g_frequency(struct file *file, void *priv,
+ static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+ {
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+- if (qc->id && qc->id == radio_qctrl[i].id) {
+- memcpy(qc, &(radio_qctrl[i]),
+- sizeof(*qc));
+- return 0;
+- }
++ switch (qc->id) {
++ case V4L2_CID_AUDIO_MUTE:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
+ }
+ return -EINVAL;
+ }
+@@ -219,7 +207,7 @@ static int vidioc_queryctrl(struct file *file, void *priv,
+ static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct fmi_device *fmi = video_drvdata(file);
++ struct fmi *fmi = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+@@ -232,31 +220,20 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
+ static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct fmi_device *fmi = video_drvdata(file);
++ struct fmi *fmi = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value)
+- fmi_mute(fmi->port);
++ fmi_mute(fmi);
+ else
+- fmi_unmute(fmi->port);
++ fmi_unmute(fmi);
+ fmi->curvol = ctrl->value;
+ return 0;
+ }
+ return -EINVAL;
+ }
+
+-static int vidioc_g_audio(struct file *file, void *priv,
+- struct v4l2_audio *a)
+-{
+- if (a->index > 1)
+- return -EINVAL;
+-
+- strcpy(a->name, "Radio");
+- a->capability = V4L2_AUDCAP_STEREO;
+- return 0;
+-}
+-
+ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+ {
+ *i = 0;
+@@ -265,36 +242,38 @@ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+
+ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+ {
+- if (i != 0)
+- return -EINVAL;
+- return 0;
++ return i ? -EINVAL : 0;
+ }
+
+-static int vidioc_s_audio(struct file *file, void *priv,
++static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+ {
+- if (a->index != 0)
+- return -EINVAL;
++ a->index = 0;
++ strlcpy(a->name, "Radio", sizeof(a->name));
++ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+ }
+
+-static struct fmi_device fmi_unit;
++static int vidioc_s_audio(struct file *file, void *priv,
++ struct v4l2_audio *a)
++{
++ return a->index ? -EINVAL : 0;
++}
+
+-static int fmi_exclusive_open(struct file *file)
++static int fmi_open(struct file *file)
+ {
+- return test_and_set_bit(0, &fmi_unit.in_use) ? -EBUSY : 0;
++ return 0;
+ }
+
+-static int fmi_exclusive_release(struct file *file)
++static int fmi_release(struct file *file)
+ {
+- clear_bit(0, &fmi_unit.in_use);
+ return 0;
+ }
+
+ static const struct v4l2_file_operations fmi_fops = {
+ .owner = THIS_MODULE,
+- .open = fmi_exclusive_open,
+- .release = fmi_exclusive_release,
++ .open = fmi_open,
++ .release = fmi_release,
+ .ioctl = video_ioctl2,
+ };
+
+@@ -313,13 +292,6 @@ static const struct v4l2_ioctl_ops fmi_ioctl_ops = {
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ };
+
+-static struct video_device fmi_radio = {
+- .name = "SF16FMx radio",
+- .fops = &fmi_fops,
+- .ioctl_ops = &fmi_ioctl_ops,
+- .release = video_device_release_empty,
+-};
+-
+ /* ladis: this is my card. does any other types exist? */
+ static struct isapnp_device_id id_table[] __devinitdata = {
+ { ISAPNP_ANY_ID, ISAPNP_ANY_ID,
+@@ -344,7 +316,7 @@ static int __init isapnp_fmi_probe(void)
+ if (pnp_device_attach(dev) < 0)
+ return -EAGAIN;
+ if (pnp_activate_dev(dev) < 0) {
+- printk ("radio-sf16fmi: PnP configure failed (out of resources?)\n");
++ printk(KERN_ERR "radio-sf16fmi: PnP configure failed (out of resources?)\n");
+ pnp_device_detach(dev);
+ return -ENOMEM;
+ }
+@@ -354,59 +326,72 @@ static int __init isapnp_fmi_probe(void)
+ }
+
+ i = pnp_port_start(dev, 0);
+- printk ("radio-sf16fmi: PnP reports card at %#x\n", i);
++ printk(KERN_INFO "radio-sf16fmi: PnP reports card at %#x\n", i);
+
+ return i;
+ }
+
+ static int __init fmi_init(void)
+ {
++ struct fmi *fmi = &fmi_card;
++ struct v4l2_device *v4l2_dev = &fmi->v4l2_dev;
++ int res;
++
+ if (io < 0)
+ io = isapnp_fmi_probe();
+- if (io < 0) {
+- printk(KERN_ERR "radio-sf16fmi: No PnP card found.\n");
+- return io;
++ strlcpy(v4l2_dev->name, "sf16fmi", sizeof(v4l2_dev->name));
++ fmi->io = io;
++ if (fmi->io < 0) {
++ v4l2_err(v4l2_dev, "No PnP card found.\n");
++ return fmi->io;
+ }
+ if (!request_region(io, 2, "radio-sf16fmi")) {
+- printk(KERN_ERR "radio-sf16fmi: port 0x%x already in use\n", io);
++ v4l2_err(v4l2_dev, "port 0x%x already in use\n", fmi->io);
+ pnp_device_detach(dev);
+ return -EBUSY;
+ }
+
+- fmi_unit.port = io;
+- fmi_unit.curvol = 0;
+- fmi_unit.curfreq = 0;
+- fmi_unit.flags = V4L2_TUNER_CAP_LOW;
+- video_set_drvdata(&fmi_radio, &fmi_unit);
++ res = v4l2_device_register(NULL, v4l2_dev);
++ if (res < 0) {
++ release_region(fmi->io, 2);
++ pnp_device_detach(dev);
++ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
++ return res;
++ }
++
++ fmi->flags = V4L2_TUNER_CAP_LOW;
++ strlcpy(fmi->vdev.name, v4l2_dev->name, sizeof(fmi->vdev.name));
++ fmi->vdev.v4l2_dev = v4l2_dev;
++ fmi->vdev.fops = &fmi_fops;
++ fmi->vdev.ioctl_ops = &fmi_ioctl_ops;
++ fmi->vdev.release = video_device_release_empty;
++ video_set_drvdata(&fmi->vdev, fmi);
+
+- mutex_init(&lock);
++ mutex_init(&fmi->lock);
+
+- if (video_register_device(&fmi_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
+- release_region(io, 2);
++ if (video_register_device(&fmi->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
++ v4l2_device_unregister(v4l2_dev);
++ release_region(fmi->io, 2);
++ pnp_device_detach(dev);
+ return -EINVAL;
+ }
+
+- printk(KERN_INFO "SF16FMx radio card driver at 0x%x\n", io);
++ v4l2_info(v4l2_dev, "card driver at 0x%x\n", fmi->io);
+ /* mute card - prevents noisy bootups */
+- fmi_mute(io);
++ fmi_mute(fmi);
+ return 0;
+ }
+
+-MODULE_AUTHOR("Petr Vandrovec, vandrove@vc.cvut.cz and M. Kirkwood");
+-MODULE_DESCRIPTION("A driver for the SF16MI radio.");
+-MODULE_LICENSE("GPL");
+-
+-module_param(io, int, 0);
+-MODULE_PARM_DESC(io, "I/O address of the SF16MI card (0x284 or 0x384)");
+-module_param(radio_nr, int, 0);
+-
+-static void __exit fmi_cleanup_module(void)
++static void __exit fmi_exit(void)
+ {
+- video_unregister_device(&fmi_radio);
+- release_region(io, 2);
++ struct fmi *fmi = &fmi_card;
++
++ video_unregister_device(&fmi->vdev);
++ v4l2_device_unregister(&fmi->v4l2_dev);
++ release_region(fmi->io, 2);
+ if (dev)
+ pnp_device_detach(dev);
+ }
+
+ module_init(fmi_init);
+-module_exit(fmi_cleanup_module);
++module_exit(fmi_exit);
+diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c
+index 92f17a3..0ba9d88 100644
+--- a/drivers/media/radio/radio-sf16fmr2.c
++++ b/drivers/media/radio/radio-sf16fmr2.c
+@@ -18,40 +18,28 @@
+ #include <linux/init.h> /* Initdata */
+ #include <linux/ioport.h> /* request_region */
+ #include <linux/delay.h> /* udelay */
+-#include <asm/io.h> /* outb, outb_p */
+-#include <asm/uaccess.h> /* copy to/from user */
+ #include <linux/videodev2.h> /* kernel radio structs */
+-#include <media/v4l2-common.h>
+-#include <media/v4l2-ioctl.h>
+ #include <linux/mutex.h>
++#include <linux/version.h> /* for KERNEL_VERSION MACRO */
++#include <linux/io.h> /* outb, outb_p */
++#include <media/v4l2-device.h>
++#include <media/v4l2-ioctl.h>
+
+-static struct mutex lock;
++MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com");
++MODULE_DESCRIPTION("A driver for the SF16FMR2 radio.");
++MODULE_LICENSE("GPL");
++
++static int io = 0x384;
++static int radio_nr = -1;
++
++module_param(io, int, 0);
++MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)");
++module_param(radio_nr, int, 0);
+
+-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+ #define RADIO_VERSION KERNEL_VERSION(0,0,2)
+
+ #define AUD_VOL_INDEX 1
+
+-static struct v4l2_queryctrl radio_qctrl[] = {
+- {
+- .id = V4L2_CID_AUDIO_MUTE,
+- .name = "Mute",
+- .minimum = 0,
+- .maximum = 1,
+- .default_value = 1,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- },
+- [AUD_VOL_INDEX] = {
+- .id = V4L2_CID_AUDIO_VOLUME,
+- .name = "Volume",
+- .minimum = 0,
+- .maximum = 15,
+- .step = 1,
+- .default_value = 0,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- }
+-};
+-
+ #undef DEBUG
+ //#define DEBUG 1
+
+@@ -62,156 +50,160 @@ static struct v4l2_queryctrl radio_qctrl[] = {
+ #endif
+
+ /* this should be static vars for module size */
+-struct fmr2_device
++struct fmr2
+ {
+- unsigned long in_use;
+- int port;
++ struct v4l2_device v4l2_dev;
++ struct video_device vdev;
++ struct mutex lock;
++ int io;
+ int curvol; /* 0-15 */
+ int mute;
+ int stereo; /* card is producing stereo audio */
+ unsigned long curfreq; /* freq in kHz */
+ int card_type;
+- __u32 flags;
++ u32 flags;
+ };
+
+-static int io = 0x384;
+-static int radio_nr = -1;
++static struct fmr2 fmr2_card;
+
+ /* hw precision is 12.5 kHz
+ * It is only useful to give freq in intervall of 200 (=0.0125Mhz),
+ * other bits will be truncated
+ */
+-#define RSF16_ENCODE(x) ((x)/200+856)
+-#define RSF16_MINFREQ 87*16000
+-#define RSF16_MAXFREQ 108*16000
++#define RSF16_ENCODE(x) ((x) / 200 + 856)
++#define RSF16_MINFREQ (87 * 16000)
++#define RSF16_MAXFREQ (108 * 16000)
+
+-static inline void wait(int n,int port)
++static inline void wait(int n, int io)
+ {
+- for (;n;--n) inb(port);
++ for (; n; --n)
++ inb(io);
+ }
+
+-static void outbits(int bits, unsigned int data, int nWait, int port)
++static void outbits(int bits, unsigned int data, int nWait, int io)
+ {
+ int bit;
+- for(;--bits>=0;) {
+- bit = (data>>bits) & 1;
+- outb(bit,port);
+- wait(nWait,port);
+- outb(bit|2,port);
+- wait(nWait,port);
+- outb(bit,port);
+- wait(nWait,port);
++
++ for (; --bits >= 0;) {
++ bit = (data >> bits) & 1;
++ outb(bit, io);
++ wait(nWait, io);
++ outb(bit | 2, io);
++ wait(nWait, io);
++ outb(bit, io);
++ wait(nWait, io);
+ }
+ }
+
+-static inline void fmr2_mute(int port)
++static inline void fmr2_mute(int io)
+ {
+- outb(0x00, port);
+- wait(4,port);
++ outb(0x00, io);
++ wait(4, io);
+ }
+
+-static inline void fmr2_unmute(int port)
++static inline void fmr2_unmute(int io)
+ {
+- outb(0x04, port);
+- wait(4,port);
++ outb(0x04, io);
++ wait(4, io);
+ }
+
+-static inline int fmr2_stereo_mode(int port)
++static inline int fmr2_stereo_mode(int io)
+ {
+- int n = inb(port);
+- outb(6,port);
+- inb(port);
+- n = ((n>>3)&1)^1;
++ int n = inb(io);
++
++ outb(6, io);
++ inb(io);
++ n = ((n >> 3) & 1) ^ 1;
+ debug_print((KERN_DEBUG "stereo: %d\n", n));
+ return n;
+ }
+
+-static int fmr2_product_info(struct fmr2_device *dev)
++static int fmr2_product_info(struct fmr2 *dev)
+ {
+- int n = inb(dev->port);
++ int n = inb(dev->io);
++
+ n &= 0xC1;
+- if (n == 0)
+- {
++ if (n == 0) {
+ /* this should support volume set */
+ dev->card_type = 12;
+ return 0;
+ }
+ /* not volume (mine is 11) */
+- dev->card_type = (n==128)?11:0;
++ dev->card_type = (n == 128) ? 11 : 0;
+ return n;
+ }
+
+-static inline int fmr2_getsigstr(struct fmr2_device *dev)
++static inline int fmr2_getsigstr(struct fmr2 *dev)
+ {
+- /* !!! work only if scanning freq */
+- int port = dev->port, res = 0xffff;
+- outb(5,port);
+- wait(4,port);
+- if (!(inb(port)&1)) res = 0;
++ /* !!! works only if scanning freq */
++ int res = 0xffff;
++
++ outb(5, dev->io);
++ wait(4, dev->io);
++ if (!(inb(dev->io) & 1))
++ res = 0;
+ debug_print((KERN_DEBUG "signal: %d\n", res));
+ return res;
+ }
+
+ /* set frequency and unmute card */
+-static int fmr2_setfreq(struct fmr2_device *dev)
++static int fmr2_setfreq(struct fmr2 *dev)
+ {
+- int port = dev->port;
+ unsigned long freq = dev->curfreq;
+
+- fmr2_mute(port);
++ fmr2_mute(dev->io);
+
+ /* 0x42 for mono output
+ * 0x102 forward scanning
+ * 0x182 scansione avanti
+ */
+- outbits(9,0x2,3,port);
+- outbits(16,RSF16_ENCODE(freq),2,port);
++ outbits(9, 0x2, 3, dev->io);
++ outbits(16, RSF16_ENCODE(freq), 2, dev->io);
+
+- fmr2_unmute(port);
++ fmr2_unmute(dev->io);
+
+ /* wait 0.11 sec */
+ msleep(110);
+
+ /* NOTE if mute this stop radio
+ you must set freq on unmute */
+- dev->stereo = fmr2_stereo_mode(port);
++ dev->stereo = fmr2_stereo_mode(dev->io);
+ return 0;
+ }
+
+ /* !!! not tested, in my card this does't work !!! */
+-static int fmr2_setvolume(struct fmr2_device *dev)
++static int fmr2_setvolume(struct fmr2 *dev)
+ {
+ int vol[16] = { 0x021, 0x084, 0x090, 0x104,
+ 0x110, 0x204, 0x210, 0x402,
+ 0x404, 0x408, 0x410, 0x801,
+ 0x802, 0x804, 0x808, 0x810 };
+- int i, a, port = dev->port;
++ int i, a;
+ int n = vol[dev->curvol & 0x0f];
+
+ if (dev->card_type != 11)
+ return 1;
+
+ for (i = 12; --i >= 0; ) {
+- a = ((n >> i) & 1) << 6; /* if (a=0) a= 0; else a= 0x40; */
+- outb(a | 4, port);
+- wait(4, port);
+- outb(a | 0x24, port);
+- wait(4, port);
+- outb(a | 4, port);
+- wait(4, port);
++ a = ((n >> i) & 1) << 6; /* if (a==0) a = 0; else a = 0x40; */
++ outb(a | 4, dev->io);
++ wait(4, dev->io);
++ outb(a | 0x24, dev->io);
++ wait(4, dev->io);
++ outb(a | 4, dev->io);
++ wait(4, dev->io);
+ }
+ for (i = 6; --i >= 0; ) {
+ a = ((0x18 >> i) & 1) << 6;
+- outb(a | 4, port);
+- wait(4,port);
+- outb(a | 0x24, port);
+- wait(4,port);
+- outb(a|4, port);
+- wait(4,port);
++ outb(a | 4, dev->io);
++ wait(4, dev->io);
++ outb(a | 0x24, dev->io);
++ wait(4, dev->io);
++ outb(a | 4, dev->io);
++ wait(4, dev->io);
+ }
+- wait(4, port);
+- outb(0x14, port);
+-
++ wait(4, dev->io);
++ outb(0x14, dev->io);
+ return 0;
+ }
+
+@@ -220,9 +212,9 @@ static int vidioc_querycap(struct file *file, void *priv,
+ {
+ strlcpy(v->driver, "radio-sf16fmr2", sizeof(v->driver));
+ strlcpy(v->card, "SF16-FMR2 radio", sizeof(v->card));
+- sprintf(v->bus_info, "ISA");
++ strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
+ v->version = RADIO_VERSION;
+- v->capabilities = V4L2_CAP_TUNER;
++ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+ }
+
+@@ -230,54 +222,52 @@ static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+ int mult;
+- struct fmr2_device *fmr2 = video_drvdata(file);
++ struct fmr2 *fmr2 = video_drvdata(file);
+
+ if (v->index > 0)
+ return -EINVAL;
+
+- strcpy(v->name, "FM");
++ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+
+ mult = (fmr2->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000;
+- v->rangelow = RSF16_MINFREQ/mult;
+- v->rangehigh = RSF16_MAXFREQ/mult;
++ v->rangelow = RSF16_MINFREQ / mult;
++ v->rangehigh = RSF16_MAXFREQ / mult;
+ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO;
+ v->capability = fmr2->flags&V4L2_TUNER_CAP_LOW;
+ v->audmode = fmr2->stereo ? V4L2_TUNER_MODE_STEREO:
+ V4L2_TUNER_MODE_MONO;
+- mutex_lock(&lock);
++ mutex_lock(&fmr2->lock);
+ v->signal = fmr2_getsigstr(fmr2);
+- mutex_unlock(&lock);
++ mutex_unlock(&fmr2->lock);
+ return 0;
+ }
+
+ static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- if (v->index > 0)
+- return -EINVAL;
+- return 0;
++ return v->index ? -EINVAL : 0;
+ }
+
+ static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct fmr2_device *fmr2 = video_drvdata(file);
++ struct fmr2 *fmr2 = video_drvdata(file);
+
+ if (!(fmr2->flags & V4L2_TUNER_CAP_LOW))
+ f->frequency *= 1000;
+ if (f->frequency < RSF16_MINFREQ ||
+- f->frequency > RSF16_MAXFREQ )
++ f->frequency > RSF16_MAXFREQ)
+ return -EINVAL;
+- /*rounding in steps of 200 to match th freq
+- that will be used */
+- fmr2->curfreq = (f->frequency/200)*200;
++ /* rounding in steps of 200 to match the freq
++ that will be used */
++ fmr2->curfreq = (f->frequency / 200) * 200;
+
+ /* set card freq (if not muted) */
+ if (fmr2->curvol && !fmr2->mute) {
+- mutex_lock(&lock);
++ mutex_lock(&fmr2->lock);
+ fmr2_setfreq(fmr2);
+- mutex_unlock(&lock);
++ mutex_unlock(&fmr2->lock);
+ }
+ return 0;
+ }
+@@ -285,7 +275,7 @@ static int vidioc_s_frequency(struct file *file, void *priv,
+ static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct fmr2_device *fmr2 = video_drvdata(file);
++ struct fmr2 *fmr2 = video_drvdata(file);
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = fmr2->curfreq;
+@@ -297,13 +287,16 @@ static int vidioc_g_frequency(struct file *file, void *priv,
+ static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+ {
+- int i;
++ struct fmr2 *fmr2 = video_drvdata(file);
+
+- for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+- if (qc->id && qc->id == radio_qctrl[i].id) {
+- memcpy(qc, &radio_qctrl[i], sizeof(*qc));
+- return 0;
+- }
++ switch (qc->id) {
++ case V4L2_CID_AUDIO_MUTE:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
++ case V4L2_CID_AUDIO_VOLUME:
++ /* Only card_type == 11 implements volume */
++ if (fmr2->card_type == 11)
++ return v4l2_ctrl_query_fill(qc, 0, 15, 1, 0);
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ }
+ return -EINVAL;
+ }
+@@ -311,7 +304,7 @@ static int vidioc_queryctrl(struct file *file, void *priv,
+ static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct fmr2_device *fmr2 = video_drvdata(file);
++ struct fmr2 *fmr2 = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+@@ -327,18 +320,14 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
+ static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct fmr2_device *fmr2 = video_drvdata(file);
++ struct fmr2 *fmr2 = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ fmr2->mute = ctrl->value;
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+- if (ctrl->value > radio_qctrl[AUD_VOL_INDEX].maximum)
+- fmr2->curvol = radio_qctrl[AUD_VOL_INDEX].maximum;
+- else
+- fmr2->curvol = ctrl->value;
+-
++ fmr2->curvol = ctrl->value;
+ break;
+ default:
+ return -EINVAL;
+@@ -351,25 +340,14 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
+ printk(KERN_DEBUG "mute\n");
+ #endif
+
+- mutex_lock(&lock);
++ mutex_lock(&fmr2->lock);
+ if (fmr2->curvol && !fmr2->mute) {
+ fmr2_setvolume(fmr2);
+ /* Set frequency and unmute card */
+ fmr2_setfreq(fmr2);
+ } else
+- fmr2_mute(fmr2->port);
+- mutex_unlock(&lock);
+- return 0;
+-}
+-
+-static int vidioc_g_audio(struct file *file, void *priv,
+- struct v4l2_audio *a)
+-{
+- if (a->index > 1)
+- return -EINVAL;
+-
+- strcpy(a->name, "Radio");
+- a->capability = V4L2_AUDCAP_STEREO;
++ fmr2_mute(fmr2->io);
++ mutex_unlock(&fmr2->lock);
+ return 0;
+ }
+
+@@ -381,36 +359,38 @@ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+
+ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+ {
+- if (i != 0)
+- return -EINVAL;
+- return 0;
++ return i ? -EINVAL : 0;
+ }
+
+-static int vidioc_s_audio(struct file *file, void *priv,
++static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+ {
+- if (a->index != 0)
+- return -EINVAL;
++ a->index = 0;
++ strlcpy(a->name, "Radio", sizeof(a->name));
++ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+ }
+
+-static struct fmr2_device fmr2_unit;
++static int vidioc_s_audio(struct file *file, void *priv,
++ struct v4l2_audio *a)
++{
++ return a->index ? -EINVAL : 0;
++}
+
+-static int fmr2_exclusive_open(struct file *file)
++static int fmr2_open(struct file *file)
+ {
+- return test_and_set_bit(0, &fmr2_unit.in_use) ? -EBUSY : 0;
++ return 0;
+ }
+
+-static int fmr2_exclusive_release(struct file *file)
++static int fmr2_release(struct file *file)
+ {
+- clear_bit(0, &fmr2_unit.in_use);
+ return 0;
+ }
+
+ static const struct v4l2_file_operations fmr2_fops = {
+ .owner = THIS_MODULE,
+- .open = fmr2_exclusive_open,
+- .release = fmr2_exclusive_release,
++ .open = fmr2_open,
++ .release = fmr2_release,
+ .ioctl = video_ioctl2,
+ };
+
+@@ -429,67 +409,64 @@ static const struct v4l2_ioctl_ops fmr2_ioctl_ops = {
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ };
+
+-static struct video_device fmr2_radio = {
+- .name = "SF16FMR2 radio",
+- .fops = &fmr2_fops,
+- .ioctl_ops = &fmr2_ioctl_ops,
+- .release = video_device_release_empty,
+-};
+-
+ static int __init fmr2_init(void)
+ {
+- fmr2_unit.port = io;
+- fmr2_unit.curvol = 0;
+- fmr2_unit.mute = 0;
+- fmr2_unit.curfreq = 0;
+- fmr2_unit.stereo = 1;
+- fmr2_unit.flags = V4L2_TUNER_CAP_LOW;
+- fmr2_unit.card_type = 0;
+- video_set_drvdata(&fmr2_radio, &fmr2_unit);
+-
+- mutex_init(&lock);
+-
+- if (!request_region(io, 2, "sf16fmr2")) {
+- printk(KERN_ERR "radio-sf16fmr2: request_region failed!\n");
++ struct fmr2 *fmr2 = &fmr2_card;
++ struct v4l2_device *v4l2_dev = &fmr2->v4l2_dev;
++ int res;
++
++ strlcpy(v4l2_dev->name, "sf16fmr2", sizeof(v4l2_dev->name));
++ fmr2->io = io;
++ fmr2->stereo = 1;
++ fmr2->flags = V4L2_TUNER_CAP_LOW;
++ mutex_init(&fmr2->lock);
++
++ if (!request_region(fmr2->io, 2, "sf16fmr2")) {
++ v4l2_err(v4l2_dev, "request_region failed!\n");
+ return -EBUSY;
+ }
+
+- if (video_register_device(&fmr2_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
+- release_region(io, 2);
+- return -EINVAL;
++ res = v4l2_device_register(NULL, v4l2_dev);
++ if (res < 0) {
++ release_region(fmr2->io, 2);
++ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
++ return res;
+ }
+
+- printk(KERN_INFO "SF16FMR2 radio card driver at 0x%x.\n", io);
+- /* mute card - prevents noisy bootups */
+- mutex_lock(&lock);
+- fmr2_mute(io);
+- fmr2_product_info(&fmr2_unit);
+- mutex_unlock(&lock);
+- debug_print((KERN_DEBUG "card_type %d\n", fmr2_unit.card_type));
++ strlcpy(fmr2->vdev.name, v4l2_dev->name, sizeof(fmr2->vdev.name));
++ fmr2->vdev.v4l2_dev = v4l2_dev;
++ fmr2->vdev.fops = &fmr2_fops;
++ fmr2->vdev.ioctl_ops = &fmr2_ioctl_ops;
++ fmr2->vdev.release = video_device_release_empty;
++ video_set_drvdata(&fmr2->vdev, fmr2);
+
+- /* Only card_type == 11 implements volume */
+- if (fmr2_unit.card_type != 11)
+- radio_qctrl[AUD_VOL_INDEX].maximum = 1;
++ if (video_register_device(&fmr2->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
++ v4l2_device_unregister(v4l2_dev);
++ release_region(fmr2->io, 2);
++ return -EINVAL;
++ }
+
++ v4l2_info(v4l2_dev, "SF16FMR2 radio card driver at 0x%x.\n", fmr2->io);
++ /* mute card - prevents noisy bootups */
++ mutex_lock(&fmr2->lock);
++ fmr2_mute(fmr2->io);
++ fmr2_product_info(fmr2);
++ mutex_unlock(&fmr2->lock);
++ debug_print((KERN_DEBUG "card_type %d\n", fmr2->card_type));
+ return 0;
+ }
+
+-MODULE_AUTHOR("Ziglio Frediano, freddy77@angelfire.com");
+-MODULE_DESCRIPTION("A driver for the SF16FMR2 radio.");
+-MODULE_LICENSE("GPL");
+-
+-module_param(io, int, 0);
+-MODULE_PARM_DESC(io, "I/O address of the SF16FMR2 card (should be 0x384, if do not work try 0x284)");
+-module_param(radio_nr, int, 0);
+-
+-static void __exit fmr2_cleanup_module(void)
++static void __exit fmr2_exit(void)
+ {
+- video_unregister_device(&fmr2_radio);
+- release_region(io,2);
++ struct fmr2 *fmr2 = &fmr2_card;
++
++ video_unregister_device(&fmr2->vdev);
++ v4l2_device_unregister(&fmr2->v4l2_dev);
++ release_region(fmr2->io, 2);
+ }
+
+ module_init(fmr2_init);
+-module_exit(fmr2_cleanup_module);
++module_exit(fmr2_exit);
+
+ #ifndef MODULE
+
+diff --git a/drivers/media/radio/radio-si470x.c b/drivers/media/radio/radio-si470x.c
+index 4dfed6a..713e242 100644
+--- a/drivers/media/radio/radio-si470x.c
++++ b/drivers/media/radio/radio-si470x.c
+@@ -5,8 +5,9 @@
+ * - Silicon Labs USB FM Radio Reference Design
+ * - ADS/Tech FM Radio Receiver (formerly Instant FM Music) (RDX-155-EF)
+ * - KWorld USB FM Radio SnapMusic Mobile 700 (FM700)
++ * - Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear)
+ *
+- * Copyright (c) 2008 Tobias Lorenz <tobias.lorenz@gmx.net>
++ * Copyright (c) 2009 Tobias Lorenz <tobias.lorenz@gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -29,7 +30,7 @@
+ * 2008-01-12 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.0
+ * - First working version
+- * 2008-01-13 Tobias Lorenz <tobias.lorenz@gmx.net>
++ * 2008-01-13 Tobias Lorenz <tobias.lorenz@gmx.net>
+ * Version 1.0.1
+ * - Improved error handling, every function now returns errno
+ * - Improved multi user access (start/mute/stop)
+@@ -104,6 +105,7 @@
+ * 2009-01-31 Rick Bronson <rick@efn.org>
+ * Tobias Lorenz <tobias.lorenz@gmx.net>
+ * - add LED status output
++ * - get HW/SW version from scratchpad
+ *
+ * ToDo:
+ * - add firmware download/update support
+@@ -114,10 +116,10 @@
+ /* driver definitions */
+ #define DRIVER_AUTHOR "Tobias Lorenz <tobias.lorenz@gmx.net>"
+ #define DRIVER_NAME "radio-si470x"
+-#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 8)
++#define DRIVER_KERNEL_VERSION KERNEL_VERSION(1, 0, 9)
+ #define DRIVER_CARD "Silicon Labs Si470x FM Radio Receiver"
+ #define DRIVER_DESC "USB radio driver for Si470x FM Radio Receivers"
+-#define DRIVER_VERSION "1.0.8"
++#define DRIVER_VERSION "1.0.9"
+
+
+ /* kernel includes */
+@@ -145,7 +147,7 @@ static struct usb_device_id si470x_usb_driver_id_table[] = {
+ { USB_DEVICE_AND_INTERFACE_INFO(0x06e1, 0xa155, USB_CLASS_HID, 0, 0) },
+ /* KWorld USB FM Radio SnapMusic Mobile 700 (FM700) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x1b80, 0xd700, USB_CLASS_HID, 0, 0) },
+- /* DealExtreme USB Radio */
++ /* Sanei Electric, Inc. FM USB Radio (sold as DealExtreme.com PCear) */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x10c5, 0x819a, USB_CLASS_HID, 0, 0) },
+ /* Terminating entry */
+ { }
+@@ -345,7 +347,7 @@ MODULE_PARM_DESC(rds_poll_time, "RDS poll time (ms): *40*");
+
+ /* Report 19: stream */
+ #define STREAM_REPORT_SIZE 3
+-#define STREAM_REPORT 19
++#define STREAM_REPORT 19
+
+ /* Report 20: scratch */
+ #define SCRATCH_PAGE_SIZE 63
+@@ -353,9 +355,13 @@ MODULE_PARM_DESC(rds_poll_time, "RDS poll time (ms): *40*");
+ #define SCRATCH_REPORT 20
+
+ /* Reports 19-22: flash upgrade of the C8051F321 */
++#define WRITE_REPORT_SIZE 4
+ #define WRITE_REPORT 19
++#define FLASH_REPORT_SIZE 64
+ #define FLASH_REPORT 20
++#define CRC_REPORT_SIZE 3
+ #define CRC_REPORT 21
++#define RESPONSE_REPORT_SIZE 2
+ #define RESPONSE_REPORT 22
+
+ /* Report 23: currently unused, but can accept 60 byte reports on the HID */
+@@ -414,7 +420,7 @@ MODULE_PARM_DESC(rds_poll_time, "RDS poll time (ms): *40*");
+
+ /* bootloader commands */
+ #define GET_SW_VERSION_COMMAND 0x00
+-#define SET_PAGE_COMMAND 0x01
++#define SET_PAGE_COMMAND 0x01
+ #define ERASE_PAGE_COMMAND 0x02
+ #define WRITE_PAGE_COMMAND 0x03
+ #define CRC_ON_PAGE_COMMAND 0x04
+@@ -428,12 +434,6 @@ MODULE_PARM_DESC(rds_poll_time, "RDS poll time (ms): *40*");
+ #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
+-
+
+
+ /**************************************************************************
+@@ -465,6 +465,10 @@ struct si470x_device {
+ unsigned int buf_size;
+ unsigned int rd_index;
+ unsigned int wr_index;
++
++ /* scratch page */
++ unsigned char software_version;
++ unsigned char hardware_version;
+ };
+
+
+@@ -480,7 +484,7 @@ struct si470x_device {
+
+
+ /**************************************************************************
+- * General Driver Functions
++ * General Driver Functions - REGISTER_REPORTs
+ **************************************************************************/
+
+ /*
+@@ -566,60 +570,6 @@ static int si470x_set_register(struct si470x_device *radio, int regnr)
+
+
+ /*
+- * si470x_get_all_registers - read entire registers
+- */
+-static int si470x_get_all_registers(struct si470x_device *radio)
+-{
+- unsigned char buf[ENTIRE_REPORT_SIZE];
+- int retval;
+- unsigned char regnr;
+-
+- buf[0] = ENTIRE_REPORT;
+-
+- retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
+-
+- if (retval >= 0)
+- for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++)
+- radio->registers[regnr] = get_unaligned_be16(
+- &buf[regnr * RADIO_REGISTER_SIZE + 1]);
+-
+- return (retval < 0) ? -EINVAL : 0;
+-}
+-
+-
+-/*
+- * si470x_get_rds_registers - read rds registers
+- */
+-static int si470x_get_rds_registers(struct si470x_device *radio)
+-{
+- unsigned char buf[RDS_REPORT_SIZE];
+- int retval;
+- int size;
+- unsigned char regnr;
+-
+- buf[0] = RDS_REPORT;
+-
+- retval = usb_interrupt_msg(radio->usbdev,
+- usb_rcvintpipe(radio->usbdev, 1),
+- (void *) &buf, sizeof(buf), &size, usb_timeout);
+- if (size != sizeof(buf))
+- printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: "
+- "return size differs: %d != %zu\n", size, sizeof(buf));
+- if (retval < 0)
+- printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: "
+- "usb_interrupt_msg returned %d\n", retval);
+-
+- if (retval >= 0)
+- for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)
+- radio->registers[STATUSRSSI + regnr] =
+- get_unaligned_be16(
+- &buf[regnr * RADIO_REGISTER_SIZE + 1]);
+-
+- return (retval < 0) ? -EINVAL : 0;
+-}
+-
+-
+-/*
+ * si470x_set_chan - set the channel
+ */
+ static int si470x_set_chan(struct si470x_device *radio, unsigned short chan)
+@@ -887,6 +837,70 @@ static int si470x_rds_on(struct si470x_device *radio)
+
+
+ /**************************************************************************
++ * General Driver Functions - ENTIRE_REPORT
++ **************************************************************************/
++
++/*
++ * si470x_get_all_registers - read entire registers
++ */
++static int si470x_get_all_registers(struct si470x_device *radio)
++{
++ unsigned char buf[ENTIRE_REPORT_SIZE];
++ int retval;
++ unsigned char regnr;
++
++ buf[0] = ENTIRE_REPORT;
++
++ retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
++
++ if (retval >= 0)
++ for (regnr = 0; regnr < RADIO_REGISTER_NUM; regnr++)
++ radio->registers[regnr] = get_unaligned_be16(
++ &buf[regnr * RADIO_REGISTER_SIZE + 1]);
++
++ return (retval < 0) ? -EINVAL : 0;
++}
++
++
++
++/**************************************************************************
++ * General Driver Functions - RDS_REPORT
++ **************************************************************************/
++
++/*
++ * si470x_get_rds_registers - read rds registers
++ */
++static int si470x_get_rds_registers(struct si470x_device *radio)
++{
++ unsigned char buf[RDS_REPORT_SIZE];
++ int retval;
++ int size;
++ unsigned char regnr;
++
++ buf[0] = RDS_REPORT;
++
++ retval = usb_interrupt_msg(radio->usbdev,
++ usb_rcvintpipe(radio->usbdev, 1),
++ (void *) &buf, sizeof(buf), &size, usb_timeout);
++ if (size != sizeof(buf))
++ printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: "
++ "return size differs: %d != %zu\n", size, sizeof(buf));
++ if (retval < 0)
++ printk(KERN_WARNING DRIVER_NAME ": si470x_get_rds_registers: "
++ "usb_interrupt_msg returned %d\n", retval);
++
++ if (retval >= 0)
++ for (regnr = 0; regnr < RDS_REGISTER_NUM; regnr++)
++ radio->registers[STATUSRSSI + regnr] =
++ get_unaligned_be16(
++ &buf[regnr * RADIO_REGISTER_SIZE + 1]);
++
++ return (retval < 0) ? -EINVAL : 0;
++}
++
++
++
++/**************************************************************************
+ * General Driver Functions - LED_REPORT
+ **************************************************************************/
+
+@@ -911,6 +925,35 @@ static int si470x_set_led_state(struct si470x_device *radio,
+
+
+ /**************************************************************************
++ * General Driver Functions - SCRATCH_REPORT
++ **************************************************************************/
++
++/*
++ * si470x_get_scratch_versions - gets the scratch page and version infos
++ */
++static int si470x_get_scratch_page_versions(struct si470x_device *radio)
++{
++ unsigned char buf[SCRATCH_REPORT_SIZE];
++ int retval;
++
++ buf[0] = SCRATCH_REPORT;
++
++ retval = si470x_get_report(radio, (void *) &buf, sizeof(buf));
++
++ if (retval < 0)
++ printk(KERN_WARNING DRIVER_NAME ": si470x_get_scratch: "
++ "si470x_get_report returned %d\n", retval);
++ else {
++ radio->software_version = buf[1];
++ radio->hardware_version = buf[2];
++ }
++
++ return (retval < 0) ? -EINVAL : 0;
++}
++
++
++
++/**************************************************************************
+ * RDS Driver Functions
+ **************************************************************************/
+
+@@ -1124,6 +1167,7 @@ static int si470x_fops_open(struct file *file)
+ }
+
+ if (radio->users == 1) {
++ /* start radio */
+ retval = si470x_start(radio);
+ if (retval < 0)
+ usb_autopm_put_interface(radio->intf);
+@@ -1165,6 +1209,7 @@ static int si470x_fops_release(struct file *file)
+ /* cancel read processes */
+ wake_up_interruptible(&radio->read_queue);
+
++ /* stop radio */
+ retval = si470x_stop(radio);
+ usb_autopm_put_interface(radio->intf);
+ }
+@@ -1226,9 +1271,11 @@ static struct v4l2_queryctrl si470x_v4l2_queryctrl[] = {
+ static int si470x_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *capability)
+ {
++ struct si470x_device *radio = video_drvdata(file);
++
+ strlcpy(capability->driver, DRIVER_NAME, sizeof(capability->driver));
+ strlcpy(capability->card, DRIVER_CARD, sizeof(capability->card));
+- sprintf(capability->bus_info, "USB");
++ usb_make_path(radio->usbdev, capability->bus_info, sizeof(capability->bus_info));
+ capability->version = DRIVER_KERNEL_VERSION;
+ capability->capabilities = V4L2_CAP_HW_FREQ_SEEK |
+ V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+@@ -1636,7 +1683,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
+ sizeof(si470x_viddev_template));
+ video_set_drvdata(radio->videodev, radio);
+
+- /* show some infos about the specific device */
++ /* show some infos about the specific si470x device */
+ if (si470x_get_all_registers(radio) < 0) {
+ retval = -EIO;
+ goto err_all;
+@@ -1644,7 +1691,16 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
+ printk(KERN_INFO DRIVER_NAME ": DeviceID=0x%4.4hx ChipID=0x%4.4hx\n",
+ radio->registers[DEVICEID], radio->registers[CHIPID]);
+
+- /* check if firmware is current */
++ /* get software and hardware versions */
++ if (si470x_get_scratch_page_versions(radio) < 0) {
++ retval = -EIO;
++ goto err_all;
++ }
++ printk(KERN_INFO DRIVER_NAME
++ ": software version %d, hardware version %d\n",
++ radio->software_version, radio->hardware_version);
++
++ /* check if device and firmware is current */
+ if ((radio->registers[CHIPID] & CHIPID_FIRMWARE)
+ < RADIO_SW_VERSION_CURRENT) {
+ printk(KERN_WARNING DRIVER_NAME
+@@ -1657,7 +1713,7 @@ static int si470x_usb_driver_probe(struct usb_interface *intf,
+ ": If you have some trouble using this driver,\n");
+ printk(KERN_WARNING DRIVER_NAME
+ ": please report to V4L ML at "
+- "video4linux-list@redhat.com\n");
++ "linux-media@vger.kernel.org\n");
+ }
+
+ /* set initial frequency */
+diff --git a/drivers/media/radio/radio-tea5764.c b/drivers/media/radio/radio-tea5764.c
+index 4d35308..3936238 100644
+--- a/drivers/media/radio/radio-tea5764.c
++++ b/drivers/media/radio/radio-tea5764.c
+@@ -298,7 +298,8 @@ static int vidioc_querycap(struct file *file, void *priv,
+
+ strlcpy(v->driver, dev->dev.driver->name, sizeof(v->driver));
+ strlcpy(v->card, dev->name, sizeof(v->card));
+- snprintf(v->bus_info, sizeof(v->bus_info), "I2C:%s", dev->dev.bus_id);
++ snprintf(v->bus_info, sizeof(v->bus_info),
++ "I2C:%s", dev_name(&dev->dev));
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c
+index 0798d71..5b007f5 100644
+--- a/drivers/media/radio/radio-terratec.c
++++ b/drivers/media/radio/radio-terratec.c
+@@ -27,16 +27,29 @@
+ #include <linux/module.h> /* Modules */
+ #include <linux/init.h> /* Initdata */
+ #include <linux/ioport.h> /* request_region */
+-#include <linux/delay.h> /* udelay */
+-#include <asm/io.h> /* outb, outb_p */
+-#include <asm/uaccess.h> /* copy to/from user */
+ #include <linux/videodev2.h> /* kernel radio structs */
+-#include <media/v4l2-common.h>
++#include <linux/mutex.h>
++#include <linux/version.h> /* for KERNEL_VERSION MACRO */
++#include <linux/io.h> /* outb, outb_p */
++#include <media/v4l2-device.h>
+ #include <media/v4l2-ioctl.h>
+-#include <linux/spinlock.h>
+
+-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+-#define RADIO_VERSION KERNEL_VERSION(0,0,2)
++MODULE_AUTHOR("R.OFFERMANNS & others");
++MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");
++MODULE_LICENSE("GPL");
++
++#ifndef CONFIG_RADIO_TERRATEC_PORT
++#define CONFIG_RADIO_TERRATEC_PORT 0x590
++#endif
++
++static int io = CONFIG_RADIO_TERRATEC_PORT;
++static int radio_nr = -1;
++
++module_param(io, int, 0);
++MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)");
++module_param(radio_nr, int, 0);
++
++#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
+
+ static struct v4l2_queryctrl radio_qctrl[] = {
+ {
+@@ -57,13 +70,6 @@ static struct v4l2_queryctrl radio_qctrl[] = {
+ }
+ };
+
+-#ifndef CONFIG_RADIO_TERRATEC_PORT
+-#define CONFIG_RADIO_TERRATEC_PORT 0x590
+-#endif
+-
+-/**************** this ones are for the terratec *******************/
+-#define BASEPORT 0x590
+-#define VOLPORT 0x591
+ #define WRT_DIS 0x00
+ #define CLK_OFF 0x00
+ #define IIC_DATA 0x01
+@@ -71,138 +77,124 @@ static struct v4l2_queryctrl radio_qctrl[] = {
+ #define DATA 0x04
+ #define CLK_ON 0x08
+ #define WRT_EN 0x10
+-/*******************************************************************/
+
+-static int io = CONFIG_RADIO_TERRATEC_PORT;
+-static int radio_nr = -1;
+-static spinlock_t lock;
+-
+-struct tt_device
++struct terratec
+ {
+- unsigned long in_use;
+- int port;
++ struct v4l2_device v4l2_dev;
++ struct video_device vdev;
++ int io;
+ int curvol;
+ unsigned long curfreq;
+ int muted;
++ struct mutex lock;
+ };
+
++static struct terratec terratec_card;
+
+ /* local things */
+
+-static void cardWriteVol(int volume)
++static void tt_write_vol(struct terratec *tt, int volume)
+ {
+ int i;
+- volume = volume+(volume * 32); // change both channels
+- spin_lock(&lock);
+- for (i=0;i<8;i++)
+- {
+- if (volume & (0x80>>i))
+- outb(0x80, VOLPORT);
+- else outb(0x00, VOLPORT);
++
++ volume = volume + (volume * 32); /* change both channels */
++ mutex_lock(&tt->lock);
++ for (i = 0; i < 8; i++) {
++ if (volume & (0x80 >> i))
++ outb(0x80, tt->io + 1);
++ else
++ outb(0x00, tt->io + 1);
+ }
+- spin_unlock(&lock);
++ mutex_unlock(&tt->lock);
+ }
+
+
+
+-static void tt_mute(struct tt_device *dev)
++static void tt_mute(struct terratec *tt)
+ {
+- dev->muted = 1;
+- cardWriteVol(0);
++ tt->muted = 1;
++ tt_write_vol(tt, 0);
+ }
+
+-static int tt_setvol(struct tt_device *dev, int vol)
++static int tt_setvol(struct terratec *tt, int vol)
+ {
+-
+-// printk(KERN_ERR "setvol called, vol = %d\n", vol);
+-
+- if(vol == dev->curvol) { /* requested volume = current */
+- if (dev->muted) { /* user is unmuting the card */
+- dev->muted = 0;
+- cardWriteVol(vol); /* enable card */
++ if (vol == tt->curvol) { /* requested volume = current */
++ if (tt->muted) { /* user is unmuting the card */
++ tt->muted = 0;
++ tt_write_vol(tt, vol); /* enable card */
+ }
+-
+ return 0;
+ }
+
+- if(vol == 0) { /* volume = 0 means mute the card */
+- cardWriteVol(0); /* "turn off card" by setting vol to 0 */
+- dev->curvol = vol; /* track the volume state! */
++ if (vol == 0) { /* volume = 0 means mute the card */
++ tt_write_vol(tt, 0); /* "turn off card" by setting vol to 0 */
++ tt->curvol = vol; /* track the volume state! */
+ return 0;
+ }
+
+- dev->muted = 0;
+-
+- cardWriteVol(vol);
+-
+- dev->curvol = vol;
+-
++ tt->muted = 0;
++ tt_write_vol(tt, vol);
++ tt->curvol = vol;
+ return 0;
+-
+ }
+
+
+ /* this is the worst part in this driver */
+ /* many more or less strange things are going on here, but hey, it works :) */
+
+-static int tt_setfreq(struct tt_device *dev, unsigned long freq1)
++static int tt_setfreq(struct terratec *tt, unsigned long freq1)
+ {
+ int freq;
+ int i;
+ int p;
+ int temp;
+ long rest;
+-
+ unsigned char buffer[25]; /* we have to bit shift 25 registers */
+- freq = freq1/160; /* convert the freq. to a nice to handle value */
+- for(i=24;i>-1;i--)
+- buffer[i]=0;
+
+- rest = freq*10+10700; /* i once had understood what is going on here */
++ mutex_lock(&tt->lock);
++
++ tt->curfreq = freq1;
++
++ freq = freq1 / 160; /* convert the freq. to a nice to handle value */
++ memset(buffer, 0, sizeof(buffer));
++
++ rest = freq * 10 + 10700; /* I once had understood what is going on here */
+ /* maybe some wise guy (friedhelm?) can comment this stuff */
+- i=13;
+- p=10;
+- temp=102400;
+- while (rest!=0)
+- {
+- if (rest%temp == rest)
++ i = 13;
++ p = 10;
++ temp = 102400;
++ while (rest != 0) {
++ if (rest % temp == rest)
+ buffer[i] = 0;
+- else
+- {
++ else {
+ buffer[i] = 1;
+- rest = rest-temp;
++ rest = rest - temp;
+ }
+ i--;
+ p--;
+- temp = temp/2;
++ temp = temp / 2;
+ }
+
+- spin_lock(&lock);
+-
+- for (i=24;i>-1;i--) /* bit shift the values to the radiocard */
+- {
+- if (buffer[i]==1)
+- {
+- outb(WRT_EN|DATA, BASEPORT);
+- outb(WRT_EN|DATA|CLK_ON , BASEPORT);
+- outb(WRT_EN|DATA, BASEPORT);
+- }
+- else
+- {
+- outb(WRT_EN|0x00, BASEPORT);
+- outb(WRT_EN|0x00|CLK_ON , BASEPORT);
++ for (i = 24; i > -1; i--) { /* bit shift the values to the radiocard */
++ if (buffer[i] == 1) {
++ outb(WRT_EN | DATA, tt->io);
++ outb(WRT_EN | DATA | CLK_ON, tt->io);
++ outb(WRT_EN | DATA, tt->io);
++ } else {
++ outb(WRT_EN | 0x00, tt->io);
++ outb(WRT_EN | 0x00 | CLK_ON, tt->io);
+ }
+ }
+- outb(0x00, BASEPORT);
++ outb(0x00, tt->io);
+
+- spin_unlock(&lock);
++ mutex_unlock(&tt->lock);
+
+ return 0;
+ }
+
+-static int tt_getsigstr(struct tt_device *dev) /* TODO */
++static int tt_getsigstr(struct terratec *tt)
+ {
+- if (inb(io) & 2) /* bit set = no signal present */
++ if (inb(tt->io) & 2) /* bit set = no signal present */
+ return 0;
+ return 1; /* signal present */
+ }
+@@ -212,53 +204,50 @@ static int vidioc_querycap(struct file *file, void *priv,
+ {
+ strlcpy(v->driver, "radio-terratec", sizeof(v->driver));
+ strlcpy(v->card, "ActiveRadio", sizeof(v->card));
+- sprintf(v->bus_info, "ISA");
++ strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
+ v->version = RADIO_VERSION;
+- v->capabilities = V4L2_CAP_TUNER;
++ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+ }
+
+ static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- struct tt_device *tt = video_drvdata(file);
++ struct terratec *tt = video_drvdata(file);
+
+ if (v->index > 0)
+ return -EINVAL;
+
+- strcpy(v->name, "FM");
++ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+- v->rangelow = (87*16000);
+- v->rangehigh = (108*16000);
++ v->rangelow = 87 * 16000;
++ v->rangehigh = 108 * 16000;
+ v->rxsubchans = V4L2_TUNER_SUB_MONO;
+ v->capability = V4L2_TUNER_CAP_LOW;
+ v->audmode = V4L2_TUNER_MODE_MONO;
+- v->signal = 0xFFFF*tt_getsigstr(tt);
++ v->signal = 0xFFFF * tt_getsigstr(tt);
+ return 0;
+ }
+
+ static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- if (v->index > 0)
+- return -EINVAL;
+- return 0;
++ return v->index ? -EINVAL : 0;
+ }
+
+ static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct tt_device *tt = video_drvdata(file);
++ struct terratec *tt = video_drvdata(file);
+
+- tt->curfreq = f->frequency;
+- tt_setfreq(tt, tt->curfreq);
++ tt_setfreq(tt, f->frequency);
+ return 0;
+ }
+
+ static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct tt_device *tt = video_drvdata(file);
++ struct terratec *tt = video_drvdata(file);
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = tt->curfreq;
+@@ -272,8 +261,7 @@ static int vidioc_queryctrl(struct file *file, void *priv,
+
+ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+ if (qc->id && qc->id == radio_qctrl[i].id) {
+- memcpy(qc, &(radio_qctrl[i]),
+- sizeof(*qc));
++ memcpy(qc, &(radio_qctrl[i]), sizeof(*qc));
+ return 0;
+ }
+ }
+@@ -283,7 +271,7 @@ static int vidioc_queryctrl(struct file *file, void *priv,
+ static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct tt_device *tt = video_drvdata(file);
++ struct terratec *tt = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+@@ -302,7 +290,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
+ static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct tt_device *tt = video_drvdata(file);
++ struct terratec *tt = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+@@ -318,17 +306,6 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
+ return -EINVAL;
+ }
+
+-static int vidioc_g_audio(struct file *file, void *priv,
+- struct v4l2_audio *a)
+-{
+- if (a->index > 1)
+- return -EINVAL;
+-
+- strcpy(a->name, "Radio");
+- a->capability = V4L2_AUDCAP_STEREO;
+- return 0;
+-}
+-
+ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+ {
+ *i = 0;
+@@ -337,36 +314,38 @@ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+
+ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+ {
+- if (i != 0)
+- return -EINVAL;
+- return 0;
++ return i ? -EINVAL : 0;
+ }
+
+-static int vidioc_s_audio(struct file *file, void *priv,
++static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+ {
+- if (a->index != 0)
+- return -EINVAL;
++ a->index = 0;
++ strlcpy(a->name, "Radio", sizeof(a->name));
++ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+ }
+
+-static struct tt_device terratec_unit;
++static int vidioc_s_audio(struct file *file, void *priv,
++ struct v4l2_audio *a)
++{
++ return a->index ? -EINVAL : 0;
++}
+
+-static int terratec_exclusive_open(struct file *file)
++static int terratec_open(struct file *file)
+ {
+- return test_and_set_bit(0, &terratec_unit.in_use) ? -EBUSY : 0;
++ return 0;
+ }
+
+-static int terratec_exclusive_release(struct file *file)
++static int terratec_release(struct file *file)
+ {
+- clear_bit(0, &terratec_unit.in_use);
+ return 0;
+ }
+
+ static const struct v4l2_file_operations terratec_fops = {
+ .owner = THIS_MODULE,
+- .open = terratec_exclusive_open,
+- .release = terratec_exclusive_release,
++ .open = terratec_open,
++ .release = terratec_release,
+ .ioctl = video_ioctl2,
+ };
+
+@@ -385,60 +364,63 @@ static const struct v4l2_ioctl_ops terratec_ioctl_ops = {
+ .vidioc_s_input = vidioc_s_input,
+ };
+
+-static struct video_device terratec_radio = {
+- .name = "TerraTec ActiveRadio",
+- .fops = &terratec_fops,
+- .ioctl_ops = &terratec_ioctl_ops,
+- .release = video_device_release_empty,
+-};
+-
+ static int __init terratec_init(void)
+ {
+- if(io==-1)
+- {
+- printk(KERN_ERR "You must set an I/O address with io=0x???\n");
++ struct terratec *tt = &terratec_card;
++ struct v4l2_device *v4l2_dev = &tt->v4l2_dev;
++ int res;
++
++ strlcpy(v4l2_dev->name, "terratec", sizeof(v4l2_dev->name));
++ tt->io = io;
++ if (tt->io == -1) {
++ v4l2_err(v4l2_dev, "you must set an I/O address with io=0x590 or 0x591\n");
+ return -EINVAL;
+ }
+- if (!request_region(io, 2, "terratec"))
+- {
+- printk(KERN_ERR "TerraTec: port 0x%x already in use\n", io);
++ if (!request_region(tt->io, 2, "terratec")) {
++ v4l2_err(v4l2_dev, "port 0x%x already in use\n", io);
+ return -EBUSY;
+ }
+
+- video_set_drvdata(&terratec_radio, &terratec_unit);
++ res = v4l2_device_register(NULL, v4l2_dev);
++ if (res < 0) {
++ release_region(tt->io, 2);
++ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
++ return res;
++ }
++
++ strlcpy(tt->vdev.name, v4l2_dev->name, sizeof(tt->vdev.name));
++ tt->vdev.v4l2_dev = v4l2_dev;
++ tt->vdev.fops = &terratec_fops;
++ tt->vdev.ioctl_ops = &terratec_ioctl_ops;
++ tt->vdev.release = video_device_release_empty;
++ video_set_drvdata(&tt->vdev, tt);
+
+- spin_lock_init(&lock);
++ mutex_init(&tt->lock);
+
+- if (video_register_device(&terratec_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
+- release_region(io,2);
++ if (video_register_device(&tt->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
++ v4l2_device_unregister(&tt->v4l2_dev);
++ release_region(tt->io, 2);
+ return -EINVAL;
+ }
+
+- printk(KERN_INFO "TERRATEC ActivRadio Standalone card driver.\n");
++ v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver.\n");
+
+ /* mute card - prevents noisy bootups */
+-
+- /* this ensures that the volume is all the way down */
+- cardWriteVol(0);
+- terratec_unit.curvol = 0;
+-
++ tt_write_vol(tt, 0);
+ return 0;
+ }
+
+-MODULE_AUTHOR("R.OFFERMANNS & others");
+-MODULE_DESCRIPTION("A driver for the TerraTec ActiveRadio Standalone radio card.");
+-MODULE_LICENSE("GPL");
+-module_param(io, int, 0);
+-MODULE_PARM_DESC(io, "I/O address of the TerraTec ActiveRadio card (0x590 or 0x591)");
+-module_param(radio_nr, int, 0);
+-
+-static void __exit terratec_cleanup_module(void)
++static void __exit terratec_exit(void)
+ {
+- video_unregister_device(&terratec_radio);
+- release_region(io,2);
+- printk(KERN_INFO "TERRATEC ActivRadio Standalone card driver unloaded.\n");
++ struct terratec *tt = &terratec_card;
++ struct v4l2_device *v4l2_dev = &tt->v4l2_dev;
++
++ video_unregister_device(&tt->vdev);
++ v4l2_device_unregister(&tt->v4l2_dev);
++ release_region(tt->io, 2);
++ v4l2_info(v4l2_dev, "TERRATEC ActivRadio Standalone card driver unloaded.\n");
+ }
+
+ module_init(terratec_init);
+-module_exit(terratec_cleanup_module);
++module_exit(terratec_exit);
+
+diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c
+index bdf9cb6..d1be649 100644
+--- a/drivers/media/radio/radio-trust.c
++++ b/drivers/media/radio/radio-trust.c
+@@ -19,49 +19,15 @@
+ #include <linux/module.h>
+ #include <linux/init.h>
+ #include <linux/ioport.h>
+-#include <asm/io.h>
+-#include <asm/uaccess.h>
++#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+ #include <linux/videodev2.h>
+-#include <media/v4l2-common.h>
++#include <linux/io.h>
++#include <media/v4l2-device.h>
+ #include <media/v4l2-ioctl.h>
+
+-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+-#define RADIO_VERSION KERNEL_VERSION(0,0,2)
+-
+-static struct v4l2_queryctrl radio_qctrl[] = {
+- {
+- .id = V4L2_CID_AUDIO_MUTE,
+- .name = "Mute",
+- .minimum = 0,
+- .maximum = 1,
+- .default_value = 1,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- },{
+- .id = V4L2_CID_AUDIO_VOLUME,
+- .name = "Volume",
+- .minimum = 0,
+- .maximum = 65535,
+- .step = 2048,
+- .default_value = 65535,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- },{
+- .id = V4L2_CID_AUDIO_BASS,
+- .name = "Bass",
+- .minimum = 0,
+- .maximum = 65535,
+- .step = 4370,
+- .default_value = 32768,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- },{
+- .id = V4L2_CID_AUDIO_TREBLE,
+- .name = "Treble",
+- .minimum = 0,
+- .maximum = 65535,
+- .step = 4370,
+- .default_value = 32768,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- },
+-};
++MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
++MODULE_DESCRIPTION("A driver for the Trust FM Radio card.");
++MODULE_LICENSE("GPL");
+
+ /* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
+
+@@ -71,26 +37,41 @@ static struct v4l2_queryctrl radio_qctrl[] = {
+
+ static int io = CONFIG_RADIO_TRUST_PORT;
+ static int radio_nr = -1;
+-static int ioval = 0xf;
+-static __u16 curvol;
+-static __u16 curbass;
+-static __u16 curtreble;
+-static unsigned long curfreq;
+-static int curstereo;
+-static int curmute;
+-static unsigned long in_use;
++
++module_param(io, int, 0);
++MODULE_PARM_DESC(io, "I/O address of the Trust FM Radio card (0x350 or 0x358)");
++module_param(radio_nr, int, 0);
++
++#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
++
++struct trust {
++ struct v4l2_device v4l2_dev;
++ struct video_device vdev;
++ int io;
++ int ioval;
++ __u16 curvol;
++ __u16 curbass;
++ __u16 curtreble;
++ int muted;
++ unsigned long curfreq;
++ int curstereo;
++ int curmute;
++ struct mutex lock;
++};
++
++static struct trust trust_card;
+
+ /* i2c addresses */
+ #define TDA7318_ADDR 0x88
+ #define TSA6060T_ADDR 0xc4
+
+-#define TR_DELAY do { inb(io); inb(io); inb(io); } while(0)
+-#define TR_SET_SCL outb(ioval |= 2, io)
+-#define TR_CLR_SCL outb(ioval &= 0xfd, io)
+-#define TR_SET_SDA outb(ioval |= 1, io)
+-#define TR_CLR_SDA outb(ioval &= 0xfe, io)
++#define TR_DELAY do { inb(tr->io); inb(tr->io); inb(tr->io); } while (0)
++#define TR_SET_SCL outb(tr->ioval |= 2, tr->io)
++#define TR_CLR_SCL outb(tr->ioval &= 0xfd, tr->io)
++#define TR_SET_SDA outb(tr->ioval |= 1, tr->io)
++#define TR_CLR_SDA outb(tr->ioval &= 0xfe, tr->io)
+
+-static void write_i2c(int n, ...)
++static void write_i2c(struct trust *tr, int n, ...)
+ {
+ unsigned char val, mask;
+ va_list args;
+@@ -136,62 +117,77 @@ static void write_i2c(int n, ...)
+ va_end(args);
+ }
+
+-static void tr_setvol(__u16 vol)
++static void tr_setvol(struct trust *tr, __u16 vol)
+ {
+- curvol = vol / 2048;
+- write_i2c(2, TDA7318_ADDR, curvol ^ 0x1f);
++ mutex_lock(&tr->lock);
++ tr->curvol = vol / 2048;
++ write_i2c(tr, 2, TDA7318_ADDR, tr->curvol ^ 0x1f);
++ mutex_unlock(&tr->lock);
+ }
+
+ static int basstreble2chip[15] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 14, 13, 12, 11, 10, 9, 8
+ };
+
+-static void tr_setbass(__u16 bass)
++static void tr_setbass(struct trust *tr, __u16 bass)
+ {
+- curbass = bass / 4370;
+- write_i2c(2, TDA7318_ADDR, 0x60 | basstreble2chip[curbass]);
++ mutex_lock(&tr->lock);
++ tr->curbass = bass / 4370;
++ write_i2c(tr, 2, TDA7318_ADDR, 0x60 | basstreble2chip[tr->curbass]);
++ mutex_unlock(&tr->lock);
+ }
+
+-static void tr_settreble(__u16 treble)
++static void tr_settreble(struct trust *tr, __u16 treble)
+ {
+- curtreble = treble / 4370;
+- write_i2c(2, TDA7318_ADDR, 0x70 | basstreble2chip[curtreble]);
++ mutex_lock(&tr->lock);
++ tr->curtreble = treble / 4370;
++ write_i2c(tr, 2, TDA7318_ADDR, 0x70 | basstreble2chip[tr->curtreble]);
++ mutex_unlock(&tr->lock);
+ }
+
+-static void tr_setstereo(int stereo)
++static void tr_setstereo(struct trust *tr, int stereo)
+ {
+- curstereo = !!stereo;
+- ioval = (ioval & 0xfb) | (!curstereo << 2);
+- outb(ioval, io);
++ mutex_lock(&tr->lock);
++ tr->curstereo = !!stereo;
++ tr->ioval = (tr->ioval & 0xfb) | (!tr->curstereo << 2);
++ outb(tr->ioval, tr->io);
++ mutex_unlock(&tr->lock);
+ }
+
+-static void tr_setmute(int mute)
++static void tr_setmute(struct trust *tr, int mute)
+ {
+- curmute = !!mute;
+- ioval = (ioval & 0xf7) | (curmute << 3);
+- outb(ioval, io);
++ mutex_lock(&tr->lock);
++ tr->curmute = !!mute;
++ tr->ioval = (tr->ioval & 0xf7) | (tr->curmute << 3);
++ outb(tr->ioval, tr->io);
++ mutex_unlock(&tr->lock);
+ }
+
+-static int tr_getsigstr(void)
++static int tr_getsigstr(struct trust *tr)
+ {
+ int i, v;
+
+- for(i = 0, v = 0; i < 100; i++) v |= inb(io);
+- return (v & 1)? 0 : 0xffff;
++ mutex_lock(&tr->lock);
++ for (i = 0, v = 0; i < 100; i++)
++ v |= inb(tr->io);
++ mutex_unlock(&tr->lock);
++ return (v & 1) ? 0 : 0xffff;
+ }
+
+-static int tr_getstereo(void)
++static int tr_getstereo(struct trust *tr)
+ {
+ /* don't know how to determine it, just return the setting */
+- return curstereo;
++ return tr->curstereo;
+ }
+
+-static void tr_setfreq(unsigned long f)
++static void tr_setfreq(struct trust *tr, unsigned long f)
+ {
++ mutex_lock(&tr->lock);
++ tr->curfreq = f;
+ f /= 160; /* Convert to 10 kHz units */
+- f += 1070; /* Add 10.7 MHz IF */
+-
+- write_i2c(5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0);
++ f += 1070; /* Add 10.7 MHz IF */
++ write_i2c(tr, 5, TSA6060T_ADDR, (f << 1) | 1, f >> 7, 0x60 | ((f >> 15) & 1), 0);
++ mutex_unlock(&tr->lock);
+ }
+
+ static int vidioc_querycap(struct file *file, void *priv,
+@@ -199,68 +195,75 @@ static int vidioc_querycap(struct file *file, void *priv,
+ {
+ strlcpy(v->driver, "radio-trust", sizeof(v->driver));
+ strlcpy(v->card, "Trust FM Radio", sizeof(v->card));
+- sprintf(v->bus_info, "ISA");
++ strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
+ v->version = RADIO_VERSION;
+- v->capabilities = V4L2_CAP_TUNER;
++ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+ }
+
+ static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
++ struct trust *tr = video_drvdata(file);
++
+ if (v->index > 0)
+ return -EINVAL;
+
+- strcpy(v->name, "FM");
++ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+- v->rangelow = (87.5*16000);
+- v->rangehigh = (108*16000);
+- v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
++ v->rangelow = 87.5 * 16000;
++ v->rangehigh = 108 * 16000;
++ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ v->capability = V4L2_TUNER_CAP_LOW;
+- if (tr_getstereo())
++ if (tr_getstereo(tr))
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ v->audmode = V4L2_TUNER_MODE_MONO;
+- v->signal = tr_getsigstr();
++ v->signal = tr_getsigstr(tr);
+ return 0;
+ }
+
+ static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- if (v->index > 0)
+- return -EINVAL;
++ struct trust *tr = video_drvdata(file);
+
++ if (v->index)
++ return -EINVAL;
++ tr_setstereo(tr, v->audmode == V4L2_TUNER_MODE_STEREO);
+ return 0;
+ }
+
+ static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- curfreq = f->frequency;
+- tr_setfreq(curfreq);
++ struct trust *tr = video_drvdata(file);
++
++ tr_setfreq(tr, f->frequency);
+ return 0;
+ }
+
+ static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
++ struct trust *tr = video_drvdata(file);
++
+ f->type = V4L2_TUNER_RADIO;
+- f->frequency = curfreq;
++ f->frequency = tr->curfreq;
+ return 0;
+ }
+
+ static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+ {
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+- if (qc->id && qc->id == radio_qctrl[i].id) {
+- memcpy(qc, &(radio_qctrl[i]),
+- sizeof(*qc));
+- return 0;
+- }
++ switch (qc->id) {
++ case V4L2_CID_AUDIO_MUTE:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
++ case V4L2_CID_AUDIO_VOLUME:
++ return v4l2_ctrl_query_fill(qc, 0, 65535, 2048, 65535);
++ case V4L2_CID_AUDIO_BASS:
++ case V4L2_CID_AUDIO_TREBLE:
++ return v4l2_ctrl_query_fill(qc, 0, 65535, 4370, 32768);
+ }
+ return -EINVAL;
+ }
+@@ -268,18 +271,20 @@ static int vidioc_queryctrl(struct file *file, void *priv,
+ static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
++ struct trust *tr = video_drvdata(file);
++
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+- ctrl->value = curmute;
++ ctrl->value = tr->curmute;
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+- ctrl->value = curvol * 2048;
++ ctrl->value = tr->curvol * 2048;
+ return 0;
+ case V4L2_CID_AUDIO_BASS:
+- ctrl->value = curbass * 4370;
++ ctrl->value = tr->curbass * 4370;
+ return 0;
+ case V4L2_CID_AUDIO_TREBLE:
+- ctrl->value = curtreble * 4370;
++ ctrl->value = tr->curtreble * 4370;
+ return 0;
+ }
+ return -EINVAL;
+@@ -288,34 +293,25 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
+ static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
++ struct trust *tr = video_drvdata(file);
++
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+- tr_setmute(ctrl->value);
++ tr_setmute(tr, ctrl->value);
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+- tr_setvol(ctrl->value);
++ tr_setvol(tr, ctrl->value);
+ return 0;
+ case V4L2_CID_AUDIO_BASS:
+- tr_setbass(ctrl->value);
++ tr_setbass(tr, ctrl->value);
+ return 0;
+ case V4L2_CID_AUDIO_TREBLE:
+- tr_settreble(ctrl->value);
++ tr_settreble(tr, ctrl->value);
+ return 0;
+ }
+ return -EINVAL;
+ }
+
+-static int vidioc_g_audio(struct file *file, void *priv,
+- struct v4l2_audio *a)
+-{
+- if (a->index > 1)
+- return -EINVAL;
+-
+- strcpy(a->name, "Radio");
+- a->capability = V4L2_AUDCAP_STEREO;
+- return 0;
+-}
+-
+ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+ {
+ *i = 0;
+@@ -324,34 +320,38 @@ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+
+ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+ {
+- if (i != 0)
+- return -EINVAL;
++ return i ? -EINVAL : 0;
++}
++
++static int vidioc_g_audio(struct file *file, void *priv,
++ struct v4l2_audio *a)
++{
++ a->index = 0;
++ strlcpy(a->name, "Radio", sizeof(a->name));
++ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+ }
+
+ static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+ {
+- if (a->index != 0)
+- return -EINVAL;
+- return 0;
++ return a->index ? -EINVAL : 0;
+ }
+
+-static int trust_exclusive_open(struct file *file)
++static int trust_open(struct file *file)
+ {
+- return test_and_set_bit(0, &in_use) ? -EBUSY : 0;
++ return 0;
+ }
+
+-static int trust_exclusive_release(struct file *file)
++static int trust_release(struct file *file)
+ {
+- clear_bit(0, &in_use);
+ return 0;
+ }
+
+ static const struct v4l2_file_operations trust_fops = {
+ .owner = THIS_MODULE,
+- .open = trust_exclusive_open,
+- .release = trust_exclusive_release,
++ .open = trust_open,
++ .release = trust_release,
+ .ioctl = video_ioctl2,
+ };
+
+@@ -370,59 +370,72 @@ static const struct v4l2_ioctl_ops trust_ioctl_ops = {
+ .vidioc_s_input = vidioc_s_input,
+ };
+
+-static struct video_device trust_radio = {
+- .name = "Trust FM Radio",
+- .fops = &trust_fops,
+- .ioctl_ops = &trust_ioctl_ops,
+- .release = video_device_release_empty,
+-};
+-
+ static int __init trust_init(void)
+ {
+- if(io == -1) {
+- printk(KERN_ERR "You must set an I/O address with io=0x???\n");
++ struct trust *tr = &trust_card;
++ struct v4l2_device *v4l2_dev = &tr->v4l2_dev;
++ int res;
++
++ strlcpy(v4l2_dev->name, "trust", sizeof(v4l2_dev->name));
++ tr->io = io;
++ tr->ioval = 0xf;
++ mutex_init(&tr->lock);
++
++ if (tr->io == -1) {
++ v4l2_err(v4l2_dev, "You must set an I/O address with io=0x0x350 or 0x358\n");
+ return -EINVAL;
+ }
+- if(!request_region(io, 2, "Trust FM Radio")) {
+- printk(KERN_ERR "trust: port 0x%x already in use\n", io);
++ if (!request_region(tr->io, 2, "Trust FM Radio")) {
++ v4l2_err(v4l2_dev, "port 0x%x already in use\n", tr->io);
+ return -EBUSY;
+ }
+- if (video_register_device(&trust_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
+- release_region(io, 2);
++
++ res = v4l2_device_register(NULL, v4l2_dev);
++ if (res < 0) {
++ release_region(tr->io, 2);
++ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
++ return res;
++ }
++
++ strlcpy(tr->vdev.name, v4l2_dev->name, sizeof(tr->vdev.name));
++ tr->vdev.v4l2_dev = v4l2_dev;
++ tr->vdev.fops = &trust_fops;
++ tr->vdev.ioctl_ops = &trust_ioctl_ops;
++ tr->vdev.release = video_device_release_empty;
++ video_set_drvdata(&tr->vdev, tr);
++
++ if (video_register_device(&tr->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
++ v4l2_device_unregister(v4l2_dev);
++ release_region(tr->io, 2);
+ return -EINVAL;
+ }
+
+- printk(KERN_INFO "Trust FM Radio card driver v1.0.\n");
++ v4l2_info(v4l2_dev, "Trust FM Radio card driver v1.0.\n");
+
+- write_i2c(2, TDA7318_ADDR, 0x80); /* speaker att. LF = 0 dB */
+- write_i2c(2, TDA7318_ADDR, 0xa0); /* speaker att. RF = 0 dB */
+- write_i2c(2, TDA7318_ADDR, 0xc0); /* speaker att. LR = 0 dB */
+- write_i2c(2, TDA7318_ADDR, 0xe0); /* speaker att. RR = 0 dB */
+- write_i2c(2, TDA7318_ADDR, 0x40); /* stereo 1 input, gain = 18.75 dB */
++ write_i2c(tr, 2, TDA7318_ADDR, 0x80); /* speaker att. LF = 0 dB */
++ write_i2c(tr, 2, TDA7318_ADDR, 0xa0); /* speaker att. RF = 0 dB */
++ write_i2c(tr, 2, TDA7318_ADDR, 0xc0); /* speaker att. LR = 0 dB */
++ write_i2c(tr, 2, TDA7318_ADDR, 0xe0); /* speaker att. RR = 0 dB */
++ write_i2c(tr, 2, TDA7318_ADDR, 0x40); /* stereo 1 input, gain = 18.75 dB */
+
+- tr_setvol(0x8000);
+- tr_setbass(0x8000);
+- tr_settreble(0x8000);
+- tr_setstereo(1);
++ tr_setvol(tr, 0xffff);
++ tr_setbass(tr, 0x8000);
++ tr_settreble(tr, 0x8000);
++ tr_setstereo(tr, 1);
+
+ /* mute card - prevents noisy bootups */
+- tr_setmute(1);
++ tr_setmute(tr, 1);
+
+ return 0;
+ }
+
+-MODULE_AUTHOR("Eric Lammerts, Russell Kroll, Quay Lu, Donald Song, Jason Lewis, Scott McGrath, William McGrath");
+-MODULE_DESCRIPTION("A driver for the Trust FM Radio card.");
+-MODULE_LICENSE("GPL");
+-
+-module_param(io, int, 0);
+-MODULE_PARM_DESC(io, "I/O address of the Trust FM Radio card (0x350 or 0x358)");
+-module_param(radio_nr, int, 0);
+-
+ static void __exit cleanup_trust_module(void)
+ {
+- video_unregister_device(&trust_radio);
+- release_region(io, 2);
++ struct trust *tr = &trust_card;
++
++ video_unregister_device(&tr->vdev);
++ v4l2_device_unregister(&tr->v4l2_dev);
++ release_region(tr->io, 2);
+ }
+
+ module_init(trust_init);
+diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c
+index 5c3b319..92d923c 100644
+--- a/drivers/media/radio/radio-typhoon.c
++++ b/drivers/media/radio/radio-typhoon.c
+@@ -34,37 +34,15 @@
+ #include <linux/module.h> /* Modules */
+ #include <linux/init.h> /* Initdata */
+ #include <linux/ioport.h> /* request_region */
+-#include <linux/proc_fs.h> /* radio card status report */
+-#include <linux/seq_file.h>
+-#include <asm/io.h> /* outb, outb_p */
+-#include <asm/uaccess.h> /* copy to/from user */
++#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+ #include <linux/videodev2.h> /* kernel radio structs */
+-#include <media/v4l2-common.h>
++#include <linux/io.h> /* outb, outb_p */
++#include <media/v4l2-device.h>
+ #include <media/v4l2-ioctl.h>
+
+-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+-#define RADIO_VERSION KERNEL_VERSION(0,1,1)
+-#define BANNER "Typhoon Radio Card driver v0.1.1\n"
+-
+-static struct v4l2_queryctrl radio_qctrl[] = {
+- {
+- .id = V4L2_CID_AUDIO_MUTE,
+- .name = "Mute",
+- .minimum = 0,
+- .maximum = 1,
+- .default_value = 1,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- },{
+- .id = V4L2_CID_AUDIO_VOLUME,
+- .name = "Volume",
+- .minimum = 0,
+- .maximum = 65535,
+- .step = 1<<14,
+- .default_value = 0xff,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- }
+-};
+-
++MODULE_AUTHOR("Dr. Henrik Seidel");
++MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio).");
++MODULE_LICENSE("GPL");
+
+ #ifndef CONFIG_RADIO_TYPHOON_PORT
+ #define CONFIG_RADIO_TYPHOON_PORT -1
+@@ -74,13 +52,26 @@ static struct v4l2_queryctrl radio_qctrl[] = {
+ #define CONFIG_RADIO_TYPHOON_MUTEFREQ 0
+ #endif
+
+-#ifndef CONFIG_PROC_FS
+-#undef CONFIG_RADIO_TYPHOON_PROC_FS
+-#endif
++static int io = CONFIG_RADIO_TYPHOON_PORT;
++static int radio_nr = -1;
+
+-struct typhoon_device {
+- unsigned long in_use;
+- int iobase;
++module_param(io, int, 0);
++MODULE_PARM_DESC(io, "I/O address of the Typhoon card (0x316 or 0x336)");
++
++module_param(radio_nr, int, 0);
++
++static unsigned long mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ;
++module_param(mutefreq, ulong, 0);
++MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)");
++
++#define RADIO_VERSION KERNEL_VERSION(0, 1, 1)
++
++#define BANNER "Typhoon Radio Card driver v0.1.1\n"
++
++struct typhoon {
++ struct v4l2_device v4l2_dev;
++ struct video_device vdev;
++ int io;
+ int curvol;
+ int muted;
+ unsigned long curfreq;
+@@ -88,25 +79,19 @@ struct typhoon_device {
+ struct mutex lock;
+ };
+
+-static void typhoon_setvol_generic(struct typhoon_device *dev, int vol);
+-static int typhoon_setfreq_generic(struct typhoon_device *dev,
+- unsigned long frequency);
+-static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency);
+-static void typhoon_mute(struct typhoon_device *dev);
+-static void typhoon_unmute(struct typhoon_device *dev);
+-static int typhoon_setvol(struct typhoon_device *dev, int vol);
++static struct typhoon typhoon_card;
+
+-static void typhoon_setvol_generic(struct typhoon_device *dev, int vol)
++static void typhoon_setvol_generic(struct typhoon *dev, int vol)
+ {
+ mutex_lock(&dev->lock);
+ vol >>= 14; /* Map 16 bit to 2 bit */
+ vol &= 3;
+- outb_p(vol / 2, dev->iobase); /* Set the volume, high bit. */
+- outb_p(vol % 2, dev->iobase + 2); /* Set the volume, low bit. */
++ outb_p(vol / 2, dev->io); /* Set the volume, high bit. */
++ outb_p(vol % 2, dev->io + 2); /* Set the volume, low bit. */
+ mutex_unlock(&dev->lock);
+ }
+
+-static int typhoon_setfreq_generic(struct typhoon_device *dev,
++static int typhoon_setfreq_generic(struct typhoon *dev,
+ unsigned long frequency)
+ {
+ unsigned long outval;
+@@ -130,22 +115,22 @@ static int typhoon_setfreq_generic(struct typhoon_device *dev,
+ outval -= (10 * x * x + 10433) / 20866;
+ outval += 4 * x - 11505;
+
+- outb_p((outval >> 8) & 0x01, dev->iobase + 4);
+- outb_p(outval >> 9, dev->iobase + 6);
+- outb_p(outval & 0xff, dev->iobase + 8);
++ outb_p((outval >> 8) & 0x01, dev->io + 4);
++ outb_p(outval >> 9, dev->io + 6);
++ outb_p(outval & 0xff, dev->io + 8);
+ mutex_unlock(&dev->lock);
+
+ return 0;
+ }
+
+-static int typhoon_setfreq(struct typhoon_device *dev, unsigned long frequency)
++static int typhoon_setfreq(struct typhoon *dev, unsigned long frequency)
+ {
+ typhoon_setfreq_generic(dev, frequency);
+ dev->curfreq = frequency;
+ return 0;
+ }
+
+-static void typhoon_mute(struct typhoon_device *dev)
++static void typhoon_mute(struct typhoon *dev)
+ {
+ if (dev->muted == 1)
+ return;
+@@ -154,7 +139,7 @@ static void typhoon_mute(struct typhoon_device *dev)
+ dev->muted = 1;
+ }
+
+-static void typhoon_unmute(struct typhoon_device *dev)
++static void typhoon_unmute(struct typhoon *dev)
+ {
+ if (dev->muted == 0)
+ return;
+@@ -163,7 +148,7 @@ static void typhoon_unmute(struct typhoon_device *dev)
+ dev->muted = 0;
+ }
+
+-static int typhoon_setvol(struct typhoon_device *dev, int vol)
++static int typhoon_setvol(struct typhoon *dev, int vol)
+ {
+ if (dev->muted && vol != 0) { /* user is unmuting the card */
+ dev->curvol = vol;
+@@ -188,9 +173,9 @@ static int vidioc_querycap(struct file *file, void *priv,
+ {
+ strlcpy(v->driver, "radio-typhoon", sizeof(v->driver));
+ strlcpy(v->card, "Typhoon Radio", sizeof(v->card));
+- sprintf(v->bus_info, "ISA");
++ strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
+ v->version = RADIO_VERSION;
+- v->capabilities = V4L2_CAP_TUNER;
++ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+ }
+
+@@ -200,10 +185,10 @@ static int vidioc_g_tuner(struct file *file, void *priv,
+ if (v->index > 0)
+ return -EINVAL;
+
+- strcpy(v->name, "FM");
++ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+- v->rangelow = (87.5*16000);
+- v->rangehigh = (108*16000);
++ v->rangelow = 87.5 * 16000;
++ v->rangehigh = 108 * 16000;
+ v->rxsubchans = V4L2_TUNER_SUB_MONO;
+ v->capability = V4L2_TUNER_CAP_LOW;
+ v->audmode = V4L2_TUNER_MODE_MONO;
+@@ -214,44 +199,37 @@ static int vidioc_g_tuner(struct file *file, void *priv,
+ static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- if (v->index > 0)
+- return -EINVAL;
+-
+- return 0;
++ return v->index ? -EINVAL : 0;
+ }
+
+-static int vidioc_s_frequency(struct file *file, void *priv,
++static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct typhoon_device *typhoon = video_drvdata(file);
++ struct typhoon *dev = video_drvdata(file);
+
+- typhoon->curfreq = f->frequency;
+- typhoon_setfreq(typhoon, typhoon->curfreq);
++ f->type = V4L2_TUNER_RADIO;
++ f->frequency = dev->curfreq;
+ return 0;
+ }
+
+-static int vidioc_g_frequency(struct file *file, void *priv,
++static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct typhoon_device *typhoon = video_drvdata(file);
+-
+- f->type = V4L2_TUNER_RADIO;
+- f->frequency = typhoon->curfreq;
++ struct typhoon *dev = video_drvdata(file);
+
++ dev->curfreq = f->frequency;
++ typhoon_setfreq(dev, dev->curfreq);
+ return 0;
+ }
+
+ static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+ {
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+- if (qc->id && qc->id == radio_qctrl[i].id) {
+- memcpy(qc, &(radio_qctrl[i]),
+- sizeof(*qc));
+- return 0;
+- }
++ switch (qc->id) {
++ case V4L2_CID_AUDIO_MUTE:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
++ case V4L2_CID_AUDIO_VOLUME:
++ return v4l2_ctrl_query_fill(qc, 0, 65535, 16384, 65535);
+ }
+ return -EINVAL;
+ }
+@@ -259,14 +237,14 @@ static int vidioc_queryctrl(struct file *file, void *priv,
+ static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct typhoon_device *typhoon = video_drvdata(file);
++ struct typhoon *dev = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+- ctrl->value = typhoon->muted;
++ ctrl->value = dev->muted;
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+- ctrl->value = typhoon->curvol;
++ ctrl->value = dev->curvol;
+ return 0;
+ }
+ return -EINVAL;
+@@ -275,33 +253,22 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
+ static int vidioc_s_ctrl (struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct typhoon_device *typhoon = video_drvdata(file);
++ struct typhoon *dev = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value)
+- typhoon_mute(typhoon);
++ typhoon_mute(dev);
+ else
+- typhoon_unmute(typhoon);
++ typhoon_unmute(dev);
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+- typhoon_setvol(typhoon, ctrl->value);
++ typhoon_setvol(dev, ctrl->value);
+ return 0;
+ }
+ return -EINVAL;
+ }
+
+-static int vidioc_g_audio(struct file *file, void *priv,
+- struct v4l2_audio *a)
+-{
+- if (a->index > 1)
+- return -EINVAL;
+-
+- strcpy(a->name, "Radio");
+- a->capability = V4L2_AUDCAP_STEREO;
+- return 0;
+-}
+-
+ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+ {
+ *i = 0;
+@@ -310,45 +277,62 @@ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+
+ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+ {
+- if (i != 0)
+- return -EINVAL;
++ return i ? -EINVAL : 0;
++}
++
++static int vidioc_g_audio(struct file *file, void *priv,
++ struct v4l2_audio *a)
++{
++ a->index = 0;
++ strlcpy(a->name, "Radio", sizeof(a->name));
++ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+ }
+
+ static int vidioc_s_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+ {
+- if (a->index != 0)
+- return -EINVAL;
+- return 0;
++ return a->index ? -EINVAL : 0;
+ }
+
+-static struct typhoon_device typhoon_unit =
++static int vidioc_log_status(struct file *file, void *priv)
+ {
+- .iobase = CONFIG_RADIO_TYPHOON_PORT,
+- .curfreq = CONFIG_RADIO_TYPHOON_MUTEFREQ,
+- .mutefreq = CONFIG_RADIO_TYPHOON_MUTEFREQ,
+-};
++ struct typhoon *dev = video_drvdata(file);
++ struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
++
++ v4l2_info(v4l2_dev, BANNER);
++#ifdef MODULE
++ v4l2_info(v4l2_dev, "Load type: Driver loaded as a module\n\n");
++#else
++ v4l2_info(v4l2_dev, "Load type: Driver compiled into kernel\n\n");
++#endif
++ v4l2_info(v4l2_dev, "frequency = %lu kHz\n", dev->curfreq >> 4);
++ v4l2_info(v4l2_dev, "volume = %d\n", dev->curvol);
++ v4l2_info(v4l2_dev, "mute = %s\n", dev->muted ? "on" : "off");
++ v4l2_info(v4l2_dev, "io = 0x%x\n", dev->io);
++ v4l2_info(v4l2_dev, "mute frequency = %lu kHz\n", dev->mutefreq >> 4);
++ return 0;
++}
+
+-static int typhoon_exclusive_open(struct file *file)
++static int typhoon_open(struct file *file)
+ {
+- return test_and_set_bit(0, &typhoon_unit.in_use) ? -EBUSY : 0;
++ return 0;
+ }
+
+-static int typhoon_exclusive_release(struct file *file)
++static int typhoon_release(struct file *file)
+ {
+- clear_bit(0, &typhoon_unit.in_use);
+ return 0;
+ }
+
+ static const struct v4l2_file_operations typhoon_fops = {
+ .owner = THIS_MODULE,
+- .open = typhoon_exclusive_open,
+- .release = typhoon_exclusive_release,
++ .open = typhoon_open,
++ .release = typhoon_release,
+ .ioctl = video_ioctl2,
+ };
+
+ static const struct v4l2_ioctl_ops typhoon_ioctl_ops = {
++ .vidioc_log_status = vidioc_log_status,
+ .vidioc_querycap = vidioc_querycap,
+ .vidioc_g_tuner = vidioc_g_tuner,
+ .vidioc_s_tuner = vidioc_s_tuner,
+@@ -363,125 +347,72 @@ static const struct v4l2_ioctl_ops typhoon_ioctl_ops = {
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ };
+
+-static struct video_device typhoon_radio = {
+- .name = "Typhoon Radio",
+- .fops = &typhoon_fops,
+- .ioctl_ops = &typhoon_ioctl_ops,
+- .release = video_device_release_empty,
+-};
+-
+-#ifdef CONFIG_RADIO_TYPHOON_PROC_FS
+-
+-static int typhoon_proc_show(struct seq_file *m, void *v)
+-{
+- #ifdef MODULE
+- #define MODULEPROCSTRING "Driver loaded as a module"
+- #else
+- #define MODULEPROCSTRING "Driver compiled into kernel"
+- #endif
+-
+- seq_puts(m, BANNER);
+- seq_puts(m, "Load type: " MODULEPROCSTRING "\n\n");
+- seq_printf(m, "frequency = %lu kHz\n",
+- typhoon_unit.curfreq >> 4);
+- seq_printf(m, "volume = %d\n", typhoon_unit.curvol);
+- seq_printf(m, "mute = %s\n", typhoon_unit.muted ?
+- "on" : "off");
+- seq_printf(m, "iobase = 0x%x\n", typhoon_unit.iobase);
+- seq_printf(m, "mute frequency = %lu kHz\n",
+- typhoon_unit.mutefreq >> 4);
+- return 0;
+-}
+-
+-static int typhoon_proc_open(struct inode *inode, struct file *file)
++static int __init typhoon_init(void)
+ {
+- return single_open(file, typhoon_proc_show, NULL);
+-}
+-
+-static const struct file_operations typhoon_proc_fops = {
+- .owner = THIS_MODULE,
+- .open = typhoon_proc_open,
+- .read = seq_read,
+- .llseek = seq_lseek,
+- .release = single_release,
+-};
+-#endif /* CONFIG_RADIO_TYPHOON_PROC_FS */
+-
+-MODULE_AUTHOR("Dr. Henrik Seidel");
+-MODULE_DESCRIPTION("A driver for the Typhoon radio card (a.k.a. EcoRadio).");
+-MODULE_LICENSE("GPL");
+-
+-static int io = -1;
+-static int radio_nr = -1;
+-
+-module_param(io, int, 0);
+-MODULE_PARM_DESC(io, "I/O address of the Typhoon card (0x316 or 0x336)");
+-module_param(radio_nr, int, 0);
++ struct typhoon *dev = &typhoon_card;
++ struct v4l2_device *v4l2_dev = &dev->v4l2_dev;
++ int res;
+
+-#ifdef MODULE
+-static unsigned long mutefreq;
+-module_param(mutefreq, ulong, 0);
+-MODULE_PARM_DESC(mutefreq, "Frequency used when muting the card (in kHz)");
+-#endif
++ strlcpy(v4l2_dev->name, "typhoon", sizeof(v4l2_dev->name));
++ dev->io = io;
++ dev->curfreq = dev->mutefreq = mutefreq;
+
+-static int __init typhoon_init(void)
+-{
+-#ifdef MODULE
+- if (io == -1) {
+- printk(KERN_ERR "radio-typhoon: You must set an I/O address with io=0x316 or io=0x336\n");
++ if (dev->io == -1) {
++ v4l2_err(v4l2_dev, "You must set an I/O address with io=0x316 or io=0x336\n");
+ return -EINVAL;
+ }
+- typhoon_unit.iobase = io;
+
+- if (mutefreq < 87000 || mutefreq > 108500) {
+- printk(KERN_ERR "radio-typhoon: You must set a frequency (in kHz) used when muting the card,\n");
+- printk(KERN_ERR "radio-typhoon: e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108500)\n");
++ if (dev->mutefreq < 87000 || dev->mutefreq > 108500) {
++ v4l2_err(v4l2_dev, "You must set a frequency (in kHz) used when muting the card,\n");
++ v4l2_err(v4l2_dev, "e.g. with \"mutefreq=87500\" (87000 <= mutefreq <= 108500)\n");
+ return -EINVAL;
+ }
+- typhoon_unit.mutefreq = mutefreq;
+-#endif /* MODULE */
+-
+- printk(KERN_INFO BANNER);
+- mutex_init(&typhoon_unit.lock);
+- io = typhoon_unit.iobase;
+- if (!request_region(io, 8, "typhoon")) {
+- printk(KERN_ERR "radio-typhoon: port 0x%x already in use\n",
+- typhoon_unit.iobase);
++
++ mutex_init(&dev->lock);
++ if (!request_region(dev->io, 8, "typhoon")) {
++ v4l2_err(v4l2_dev, "port 0x%x already in use\n",
++ dev->io);
+ return -EBUSY;
+ }
+
+- video_set_drvdata(&typhoon_radio, &typhoon_unit);
+- if (video_register_device(&typhoon_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
+- release_region(io, 8);
++ res = v4l2_device_register(NULL, v4l2_dev);
++ if (res < 0) {
++ release_region(dev->io, 8);
++ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
++ return res;
++ }
++ v4l2_info(v4l2_dev, BANNER);
++
++ strlcpy(dev->vdev.name, v4l2_dev->name, sizeof(dev->vdev.name));
++ dev->vdev.v4l2_dev = v4l2_dev;
++ dev->vdev.fops = &typhoon_fops;
++ dev->vdev.ioctl_ops = &typhoon_ioctl_ops;
++ dev->vdev.release = video_device_release_empty;
++ video_set_drvdata(&dev->vdev, dev);
++ if (video_register_device(&dev->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
++ v4l2_device_unregister(&dev->v4l2_dev);
++ release_region(dev->io, 8);
+ return -EINVAL;
+ }
+- printk(KERN_INFO "radio-typhoon: port 0x%x.\n", typhoon_unit.iobase);
+- printk(KERN_INFO "radio-typhoon: mute frequency is %lu kHz.\n",
+- typhoon_unit.mutefreq);
+- typhoon_unit.mutefreq <<= 4;
++ v4l2_info(v4l2_dev, "port 0x%x.\n", dev->io);
++ v4l2_info(v4l2_dev, "mute frequency is %lu kHz.\n", dev->mutefreq);
++ dev->mutefreq <<= 4;
+
+ /* mute card - prevents noisy bootups */
+- typhoon_mute(&typhoon_unit);
+-
+-#ifdef CONFIG_RADIO_TYPHOON_PROC_FS
+- if (!proc_create("driver/radio-typhoon", 0, NULL, &typhoon_proc_fops))
+- printk(KERN_ERR "radio-typhoon: registering /proc/driver/radio-typhoon failed\n");
+-#endif
++ typhoon_mute(dev);
+
+ return 0;
+ }
+
+-static void __exit typhoon_cleanup_module(void)
++static void __exit typhoon_exit(void)
+ {
++ struct typhoon *dev = &typhoon_card;
+
+-#ifdef CONFIG_RADIO_TYPHOON_PROC_FS
+- remove_proc_entry("driver/radio-typhoon", NULL);
+-#endif
+-
+- video_unregister_device(&typhoon_radio);
+- release_region(io, 8);
++ video_unregister_device(&dev->vdev);
++ v4l2_device_unregister(&dev->v4l2_dev);
++ release_region(dev->io, 8);
+ }
+
+ module_init(typhoon_init);
+-module_exit(typhoon_cleanup_module);
++module_exit(typhoon_exit);
+
+diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c
+index d2ac17e..1f85f20 100644
+--- a/drivers/media/radio/radio-zoltrix.c
++++ b/drivers/media/radio/radio-zoltrix.c
+@@ -33,33 +33,16 @@
+ #include <linux/init.h> /* Initdata */
+ #include <linux/ioport.h> /* request_region */
+ #include <linux/delay.h> /* udelay, msleep */
+-#include <asm/io.h> /* outb, outb_p */
+-#include <asm/uaccess.h> /* copy to/from user */
+ #include <linux/videodev2.h> /* kernel radio structs */
+-#include <media/v4l2-common.h>
++#include <linux/mutex.h>
++#include <linux/version.h> /* for KERNEL_VERSION MACRO */
++#include <linux/io.h> /* outb, outb_p */
++#include <media/v4l2-device.h>
+ #include <media/v4l2-ioctl.h>
+
+-#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+-#define RADIO_VERSION KERNEL_VERSION(0,0,2)
+-
+-static struct v4l2_queryctrl radio_qctrl[] = {
+- {
+- .id = V4L2_CID_AUDIO_MUTE,
+- .name = "Mute",
+- .minimum = 0,
+- .maximum = 1,
+- .default_value = 1,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- },{
+- .id = V4L2_CID_AUDIO_VOLUME,
+- .name = "Volume",
+- .minimum = 0,
+- .maximum = 65535,
+- .step = 4096,
+- .default_value = 0xff,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- }
+-};
++MODULE_AUTHOR("C.van Schaik");
++MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus.");
++MODULE_LICENSE("GPL");
+
+ #ifndef CONFIG_RADIO_ZOLTRIX_PORT
+ #define CONFIG_RADIO_ZOLTRIX_PORT -1
+@@ -68,9 +51,16 @@ static struct v4l2_queryctrl radio_qctrl[] = {
+ static int io = CONFIG_RADIO_ZOLTRIX_PORT;
+ static int radio_nr = -1;
+
+-struct zol_device {
+- unsigned long in_use;
+- int port;
++module_param(io, int, 0);
++MODULE_PARM_DESC(io, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)");
++module_param(radio_nr, int, 0);
++
++#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
++
++struct zoltrix {
++ struct v4l2_device v4l2_dev;
++ struct video_device vdev;
++ int io;
+ int curvol;
+ unsigned long curfreq;
+ int muted;
+@@ -78,161 +68,158 @@ struct zol_device {
+ struct mutex lock;
+ };
+
+-static int zol_setvol(struct zol_device *dev, int vol)
++static struct zoltrix zoltrix_card;
++
++static int zol_setvol(struct zoltrix *zol, int vol)
+ {
+- dev->curvol = vol;
+- if (dev->muted)
++ zol->curvol = vol;
++ if (zol->muted)
+ return 0;
+
+- mutex_lock(&dev->lock);
++ mutex_lock(&zol->lock);
+ if (vol == 0) {
+- outb(0, io);
+- outb(0, io);
+- inb(io + 3); /* Zoltrix needs to be read to confirm */
+- mutex_unlock(&dev->lock);
++ outb(0, zol->io);
++ outb(0, zol->io);
++ inb(zol->io + 3); /* Zoltrix needs to be read to confirm */
++ mutex_unlock(&zol->lock);
+ return 0;
+ }
+
+- outb(dev->curvol-1, io);
++ outb(zol->curvol-1, zol->io);
+ msleep(10);
+- inb(io + 2);
+- mutex_unlock(&dev->lock);
++ inb(zol->io + 2);
++ mutex_unlock(&zol->lock);
+ return 0;
+ }
+
+-static void zol_mute(struct zol_device *dev)
++static void zol_mute(struct zoltrix *zol)
+ {
+- dev->muted = 1;
+- mutex_lock(&dev->lock);
+- outb(0, io);
+- outb(0, io);
+- inb(io + 3); /* Zoltrix needs to be read to confirm */
+- mutex_unlock(&dev->lock);
++ zol->muted = 1;
++ mutex_lock(&zol->lock);
++ outb(0, zol->io);
++ outb(0, zol->io);
++ inb(zol->io + 3); /* Zoltrix needs to be read to confirm */
++ mutex_unlock(&zol->lock);
+ }
+
+-static void zol_unmute(struct zol_device *dev)
++static void zol_unmute(struct zoltrix *zol)
+ {
+- dev->muted = 0;
+- zol_setvol(dev, dev->curvol);
++ zol->muted = 0;
++ zol_setvol(zol, zol->curvol);
+ }
+
+-static int zol_setfreq(struct zol_device *dev, unsigned long freq)
++static int zol_setfreq(struct zoltrix *zol, unsigned long freq)
+ {
+ /* tunes the radio to the desired frequency */
++ struct v4l2_device *v4l2_dev = &zol->v4l2_dev;
+ unsigned long long bitmask, f, m;
+- unsigned int stereo = dev->stereo;
++ unsigned int stereo = zol->stereo;
+ int i;
+
+ if (freq == 0) {
+- printk(KERN_WARNING "zoltrix: received zero freq. Failed to set.\n");
++ v4l2_warn(v4l2_dev, "cannot set a frequency of 0.\n");
+ return -EINVAL;
+ }
+
+ m = (freq / 160 - 8800) * 2;
+- f = (unsigned long long) m + 0x4d1c;
++ f = (unsigned long long)m + 0x4d1c;
+
+ bitmask = 0xc480402c10080000ull;
+ i = 45;
+
+- mutex_lock(&dev->lock);
++ mutex_lock(&zol->lock);
+
+- outb(0, io);
+- outb(0, io);
+- inb(io + 3); /* Zoltrix needs to be read to confirm */
++ zol->curfreq = freq;
+
+- outb(0x40, io);
+- outb(0xc0, io);
++ outb(0, zol->io);
++ outb(0, zol->io);
++ inb(zol->io + 3); /* Zoltrix needs to be read to confirm */
+
+- bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ ( stereo << 31));
++ outb(0x40, zol->io);
++ outb(0xc0, zol->io);
++
++ bitmask = (bitmask ^ ((f & 0xff) << 47) ^ ((f & 0xff00) << 30) ^ (stereo << 31));
+ while (i--) {
+ if ((bitmask & 0x8000000000000000ull) != 0) {
+- outb(0x80, io);
++ outb(0x80, zol->io);
+ udelay(50);
+- outb(0x00, io);
++ outb(0x00, zol->io);
+ udelay(50);
+- outb(0x80, io);
++ outb(0x80, zol->io);
+ udelay(50);
+ } else {
+- outb(0xc0, io);
++ outb(0xc0, zol->io);
+ udelay(50);
+- outb(0x40, io);
++ outb(0x40, zol->io);
+ udelay(50);
+- outb(0xc0, io);
++ outb(0xc0, zol->io);
+ udelay(50);
+ }
+ bitmask *= 2;
+ }
+ /* termination sequence */
+- outb(0x80, io);
+- outb(0xc0, io);
+- outb(0x40, io);
++ outb(0x80, zol->io);
++ outb(0xc0, zol->io);
++ outb(0x40, zol->io);
+ udelay(1000);
+- inb(io+2);
++ inb(zol->io + 2);
+
+ udelay(1000);
+
+- if (dev->muted)
+- {
+- outb(0, io);
+- outb(0, io);
+- inb(io + 3);
++ if (zol->muted) {
++ outb(0, zol->io);
++ outb(0, zol->io);
++ inb(zol->io + 3);
+ udelay(1000);
+ }
+
+- mutex_unlock(&dev->lock);
++ mutex_unlock(&zol->lock);
+
+- if(!dev->muted)
+- {
+- zol_setvol(dev, dev->curvol);
+- }
++ if (!zol->muted)
++ zol_setvol(zol, zol->curvol);
+ return 0;
+ }
+
+ /* Get signal strength */
+-
+-static int zol_getsigstr(struct zol_device *dev)
++static int zol_getsigstr(struct zoltrix *zol)
+ {
+ int a, b;
+
+- mutex_lock(&dev->lock);
+- outb(0x00, io); /* This stuff I found to do nothing */
+- outb(dev->curvol, io);
++ mutex_lock(&zol->lock);
++ outb(0x00, zol->io); /* This stuff I found to do nothing */
++ outb(zol->curvol, zol->io);
+ msleep(20);
+
+- a = inb(io);
++ a = inb(zol->io);
+ msleep(10);
+- b = inb(io);
++ b = inb(zol->io);
+
+- mutex_unlock(&dev->lock);
++ mutex_unlock(&zol->lock);
+
+ if (a != b)
+- return (0);
++ return 0;
+
+- if ((a == 0xcf) || (a == 0xdf) /* I found this out by playing */
+- || (a == 0xef)) /* with a binary scanner on the card io */
+- return (1);
+- return (0);
++ /* I found this out by playing with a binary scanner on the card io */
++ return a == 0xcf || a == 0xdf || a == 0xef;
+ }
+
+-static int zol_is_stereo (struct zol_device *dev)
++static int zol_is_stereo(struct zoltrix *zol)
+ {
+ int x1, x2;
+
+- mutex_lock(&dev->lock);
++ mutex_lock(&zol->lock);
+
+- outb(0x00, io);
+- outb(dev->curvol, io);
++ outb(0x00, zol->io);
++ outb(zol->curvol, zol->io);
+ msleep(20);
+
+- x1 = inb(io);
++ x1 = inb(zol->io);
+ msleep(10);
+- x2 = inb(io);
++ x2 = inb(zol->io);
+
+- mutex_unlock(&dev->lock);
++ mutex_unlock(&zol->lock);
+
+- if ((x1 == x2) && (x1 == 0xcf))
+- return 1;
+- return 0;
++ return x1 == x2 && x1 == 0xcf;
+ }
+
+ static int vidioc_querycap(struct file *file, void *priv,
+@@ -240,59 +227,54 @@ static int vidioc_querycap(struct file *file, void *priv,
+ {
+ strlcpy(v->driver, "radio-zoltrix", sizeof(v->driver));
+ strlcpy(v->card, "Zoltrix Radio", sizeof(v->card));
+- sprintf(v->bus_info, "ISA");
++ strlcpy(v->bus_info, "ISA", sizeof(v->bus_info));
+ v->version = RADIO_VERSION;
+- v->capabilities = V4L2_CAP_TUNER;
++ v->capabilities = V4L2_CAP_TUNER | V4L2_CAP_RADIO;
+ return 0;
+ }
+
+ static int vidioc_g_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- struct zol_device *zol = video_drvdata(file);
++ struct zoltrix *zol = video_drvdata(file);
+
+ if (v->index > 0)
+ return -EINVAL;
+
+- strcpy(v->name, "FM");
++ strlcpy(v->name, "FM", sizeof(v->name));
+ v->type = V4L2_TUNER_RADIO;
+- v->rangelow = (88*16000);
+- v->rangehigh = (108*16000);
+- v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
++ v->rangelow = 88 * 16000;
++ v->rangehigh = 108 * 16000;
++ v->rxsubchans = V4L2_TUNER_SUB_MONO | V4L2_TUNER_SUB_STEREO;
+ v->capability = V4L2_TUNER_CAP_LOW;
+ if (zol_is_stereo(zol))
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ v->audmode = V4L2_TUNER_MODE_MONO;
+- v->signal = 0xFFFF*zol_getsigstr(zol);
++ v->signal = 0xFFFF * zol_getsigstr(zol);
+ return 0;
+ }
+
+ static int vidioc_s_tuner(struct file *file, void *priv,
+ struct v4l2_tuner *v)
+ {
+- if (v->index > 0)
+- return -EINVAL;
+- return 0;
++ return v->index ? -EINVAL : 0;
+ }
+
+ static int vidioc_s_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct zol_device *zol = video_drvdata(file);
++ struct zoltrix *zol = video_drvdata(file);
+
+- zol->curfreq = f->frequency;
+- if (zol_setfreq(zol, zol->curfreq) != 0) {
+- printk(KERN_WARNING "zoltrix: Set frequency failed.\n");
++ if (zol_setfreq(zol, f->frequency) != 0)
+ return -EINVAL;
+- }
+ return 0;
+ }
+
+ static int vidioc_g_frequency(struct file *file, void *priv,
+ struct v4l2_frequency *f)
+ {
+- struct zol_device *zol = video_drvdata(file);
++ struct zoltrix *zol = video_drvdata(file);
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = zol->curfreq;
+@@ -302,14 +284,11 @@ static int vidioc_g_frequency(struct file *file, void *priv,
+ static int vidioc_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *qc)
+ {
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+- if (qc->id && qc->id == radio_qctrl[i].id) {
+- memcpy(qc, &(radio_qctrl[i]),
+- sizeof(*qc));
+- return 0;
+- }
++ switch (qc->id) {
++ case V4L2_CID_AUDIO_MUTE:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 1);
++ case V4L2_CID_AUDIO_VOLUME:
++ return v4l2_ctrl_query_fill(qc, 0, 65535, 4096, 65535);
+ }
+ return -EINVAL;
+ }
+@@ -317,7 +296,7 @@ static int vidioc_queryctrl(struct file *file, void *priv,
+ static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct zol_device *zol = video_drvdata(file);
++ struct zoltrix *zol = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+@@ -333,7 +312,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
+ static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct zol_device *zol = video_drvdata(file);
++ struct zoltrix *zol = video_drvdata(file);
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+@@ -341,43 +320,30 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
+ zol_mute(zol);
+ else {
+ zol_unmute(zol);
+- zol_setvol(zol,zol->curvol);
++ zol_setvol(zol, zol->curvol);
+ }
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+- zol_setvol(zol,ctrl->value/4096);
++ zol_setvol(zol, ctrl->value / 4096);
+ return 0;
+ }
+ zol->stereo = 1;
+- if (zol_setfreq(zol, zol->curfreq) != 0) {
+- printk(KERN_WARNING "zoltrix: Set frequency failed.\n");
++ if (zol_setfreq(zol, zol->curfreq) != 0)
+ return -EINVAL;
+- }
+ #if 0
+ /* FIXME: Implement stereo/mono switch on V4L2 */
+- if (v->mode & VIDEO_SOUND_STEREO) {
+- zol->stereo = 1;
+- zol_setfreq(zol, zol->curfreq);
+- }
+- if (v->mode & VIDEO_SOUND_MONO) {
+- zol->stereo = 0;
+- zol_setfreq(zol, zol->curfreq);
+- }
++ if (v->mode & VIDEO_SOUND_STEREO) {
++ zol->stereo = 1;
++ zol_setfreq(zol, zol->curfreq);
++ }
++ if (v->mode & VIDEO_SOUND_MONO) {
++ zol->stereo = 0;
++ zol_setfreq(zol, zol->curfreq);
++ }
+ #endif
+ return -EINVAL;
+ }
+
+-static int vidioc_g_audio(struct file *file, void *priv,
+- struct v4l2_audio *a)
+-{
+- if (a->index > 1)
+- return -EINVAL;
+-
+- strcpy(a->name, "Radio");
+- a->capability = V4L2_AUDCAP_STEREO;
+- return 0;
+-}
+-
+ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+ {
+ *i = 0;
+@@ -386,37 +352,39 @@ static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
+
+ static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+ {
+- if (i != 0)
+- return -EINVAL;
+- return 0;
++ return i ? -EINVAL : 0;
+ }
+
+-static int vidioc_s_audio(struct file *file, void *priv,
++static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *a)
+ {
+- if (a->index != 0)
+- return -EINVAL;
++ a->index = 0;
++ strlcpy(a->name, "Radio", sizeof(a->name));
++ a->capability = V4L2_AUDCAP_STEREO;
+ return 0;
+ }
+
+-static struct zol_device zoltrix_unit;
++static int vidioc_s_audio(struct file *file, void *priv,
++ struct v4l2_audio *a)
++{
++ return a->index ? -EINVAL : 0;
++}
+
+-static int zoltrix_exclusive_open(struct file *file)
++static int zoltrix_open(struct file *file)
+ {
+- return test_and_set_bit(0, &zoltrix_unit.in_use) ? -EBUSY : 0;
++ return 0;
+ }
+
+-static int zoltrix_exclusive_release(struct file *file)
++static int zoltrix_release(struct file *file)
+ {
+- clear_bit(0, &zoltrix_unit.in_use);
+ return 0;
+ }
+
+ static const struct v4l2_file_operations zoltrix_fops =
+ {
+ .owner = THIS_MODULE,
+- .open = zoltrix_exclusive_open,
+- .release = zoltrix_exclusive_release,
++ .open = zoltrix_open,
++ .release = zoltrix_release,
+ .ioctl = video_ioctl2,
+ };
+
+@@ -435,67 +403,75 @@ static const struct v4l2_ioctl_ops zoltrix_ioctl_ops = {
+ .vidioc_s_ctrl = vidioc_s_ctrl,
+ };
+
+-static struct video_device zoltrix_radio = {
+- .name = "Zoltrix Radio Plus",
+- .fops = &zoltrix_fops,
+- .ioctl_ops = &zoltrix_ioctl_ops,
+- .release = video_device_release_empty,
+-};
+-
+ static int __init zoltrix_init(void)
+ {
+- if (io == -1) {
+- printk(KERN_ERR "You must set an I/O address with io=0x???\n");
++ struct zoltrix *zol = &zoltrix_card;
++ struct v4l2_device *v4l2_dev = &zol->v4l2_dev;
++ int res;
++
++ strlcpy(v4l2_dev->name, "zoltrix", sizeof(v4l2_dev->name));
++ zol->io = io;
++ if (zol->io == -1) {
++ v4l2_err(v4l2_dev, "You must set an I/O address with io=0x20c or 0x30c\n");
+ return -EINVAL;
+ }
+- if ((io != 0x20c) && (io != 0x30c)) {
+- printk(KERN_ERR "zoltrix: invalid port, try 0x20c or 0x30c\n");
++ if (zol->io != 0x20c && zol->io != 0x30c) {
++ v4l2_err(v4l2_dev, "invalid port, try 0x20c or 0x30c\n");
+ return -ENXIO;
+ }
+
+- video_set_drvdata(&zoltrix_radio, &zoltrix_unit);
+- if (!request_region(io, 2, "zoltrix")) {
+- printk(KERN_ERR "zoltrix: port 0x%x already in use\n", io);
++ if (!request_region(zol->io, 2, "zoltrix")) {
++ v4l2_err(v4l2_dev, "port 0x%x already in use\n", zol->io);
+ return -EBUSY;
+ }
+
+- if (video_register_device(&zoltrix_radio, VFL_TYPE_RADIO, radio_nr) < 0) {
+- release_region(io, 2);
++ res = v4l2_device_register(NULL, v4l2_dev);
++ if (res < 0) {
++ release_region(zol->io, 2);
++ v4l2_err(v4l2_dev, "Could not register v4l2_device\n");
++ return res;
++ }
++
++ strlcpy(zol->vdev.name, v4l2_dev->name, sizeof(zol->vdev.name));
++ zol->vdev.v4l2_dev = v4l2_dev;
++ zol->vdev.fops = &zoltrix_fops;
++ zol->vdev.ioctl_ops = &zoltrix_ioctl_ops;
++ zol->vdev.release = video_device_release_empty;
++ video_set_drvdata(&zol->vdev, zol);
++
++ if (video_register_device(&zol->vdev, VFL_TYPE_RADIO, radio_nr) < 0) {
++ v4l2_device_unregister(v4l2_dev);
++ release_region(zol->io, 2);
+ return -EINVAL;
+ }
+- printk(KERN_INFO "Zoltrix Radio Plus card driver.\n");
++ v4l2_info(v4l2_dev, "Zoltrix Radio Plus card driver.\n");
+
+- mutex_init(&zoltrix_unit.lock);
++ mutex_init(&zol->lock);
+
+ /* mute card - prevents noisy bootups */
+
+ /* this ensures that the volume is all the way down */
+
+- outb(0, io);
+- outb(0, io);
++ outb(0, zol->io);
++ outb(0, zol->io);
+ msleep(20);
+- inb(io + 3);
++ inb(zol->io + 3);
+
+- zoltrix_unit.curvol = 0;
+- zoltrix_unit.stereo = 1;
++ zol->curvol = 0;
++ zol->stereo = 1;
+
+ return 0;
+ }
+
+-MODULE_AUTHOR("C.van Schaik");
+-MODULE_DESCRIPTION("A driver for the Zoltrix Radio Plus.");
+-MODULE_LICENSE("GPL");
+-
+-module_param(io, int, 0);
+-MODULE_PARM_DESC(io, "I/O address of the Zoltrix Radio Plus (0x20c or 0x30c)");
+-module_param(radio_nr, int, 0);
+-
+-static void __exit zoltrix_cleanup_module(void)
++static void __exit zoltrix_exit(void)
+ {
+- video_unregister_device(&zoltrix_radio);
+- release_region(io, 2);
++ struct zoltrix *zol = &zoltrix_card;
++
++ video_unregister_device(&zol->vdev);
++ v4l2_device_unregister(&zol->v4l2_dev);
++ release_region(zol->io, 2);
+ }
+
+ module_init(zoltrix_init);
+-module_exit(zoltrix_cleanup_module);
++module_exit(zoltrix_exit);
+
+diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
+index 19cf3b8..76bad58 100644
+--- a/drivers/media/video/Kconfig
++++ b/drivers/media/video/Kconfig
+@@ -249,11 +249,25 @@ config VIDEO_VP27SMPX
+ To compile this driver as a module, choose M here: the
+ module will be called vp27smpx.
+
++comment "RDS decoders"
++
++config VIDEO_SAA6588
++ tristate "SAA6588 Radio Chip RDS decoder support"
++ depends on VIDEO_V4L2 && I2C
++
++ help
++ Support for this Radio Data System (RDS) decoder. This allows
++ seeing radio station identification transmitted using this
++ standard.
++
++ To compile this driver as a module, choose M here: the
++ module will be called saa6588.
++
+ comment "Video decoders"
+
+ config VIDEO_BT819
+ tristate "BT819A VideoStream decoder"
+- depends on VIDEO_V4L1 && I2C
++ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for BT819A video decoder.
+
+@@ -262,7 +276,7 @@ config VIDEO_BT819
+
+ config VIDEO_BT856
+ tristate "BT856 VideoStream decoder"
+- depends on VIDEO_V4L1 && I2C
++ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for BT856 video decoder.
+
+@@ -271,7 +285,7 @@ config VIDEO_BT856
+
+ config VIDEO_BT866
+ tristate "BT866 VideoStream decoder"
+- depends on VIDEO_V4L1 && I2C
++ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for BT866 video decoder.
+
+@@ -280,7 +294,7 @@ config VIDEO_BT866
+
+ config VIDEO_KS0127
+ tristate "KS0127 video decoder"
+- depends on VIDEO_V4L1 && I2C
++ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for KS0127 video decoder.
+
+@@ -307,38 +321,18 @@ config VIDEO_TCM825X
+
+ config VIDEO_SAA7110
+ tristate "Philips SAA7110 video decoder"
+- depends on VIDEO_V4L1 && I2C
++ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Philips SAA7110 video decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7110.
+
+-config VIDEO_SAA7111
+- tristate "Philips SAA7111 video decoder"
+- depends on VIDEO_V4L1 && I2C
+- ---help---
+- Support for the Philips SAA711 video decoder.
+-
+- To compile this driver as a module, choose M here: the
+- module will be called saa7111.
+-
+-config VIDEO_SAA7114
+- tristate "Philips SAA7114 video decoder"
+- depends on VIDEO_V4L1 && I2C
+- ---help---
+- Support for the Philips SAA7114 video decoder. This driver
+- is used only on Zoran driver and should be moved soon to
+- SAA711x module.
+-
+- To compile this driver as a module, choose M here: the
+- module will be called saa7114.
+-
+ config VIDEO_SAA711X
+- tristate "Philips SAA7113/4/5 video decoders"
++ tristate "Philips SAA7111/3/4/5 video decoders"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+- Support for the Philips SAA7113/4/5 video decoders.
++ Support for the Philips SAA7111/3/4/5 video decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7115.
+@@ -383,7 +377,7 @@ config VIDEO_TVP5150
+
+ config VIDEO_VPX3220
+ tristate "vpx3220a, vpx3216b & vpx3214c video decoders"
+- depends on VIDEO_V4L1 && I2C
++ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for VPX322x video decoders.
+
+@@ -421,7 +415,7 @@ config VIDEO_SAA7127
+
+ config VIDEO_SAA7185
+ tristate "Philips SAA7185 video encoder"
+- depends on VIDEO_V4L1 && I2C
++ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Philips SAA7185 video encoder.
+
+@@ -430,7 +424,7 @@ config VIDEO_SAA7185
+
+ config VIDEO_ADV7170
+ tristate "Analog Devices ADV7170 video encoder"
+- depends on VIDEO_V4L1 && I2C
++ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Analog Devices ADV7170 video encoder driver
+
+@@ -439,7 +433,7 @@ config VIDEO_ADV7170
+
+ config VIDEO_ADV7175
+ tristate "Analog Devices ADV7175 video encoder"
+- depends on VIDEO_V4L1 && I2C
++ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Analog Devices ADV7175 video encoder driver
+
+@@ -487,18 +481,6 @@ config VIDEO_VIVI
+
+ source "drivers/media/video/bt8xx/Kconfig"
+
+-config VIDEO_SAA6588
+- tristate "SAA6588 Radio Chip RDS decoder support on BT848 cards"
+- depends on I2C && VIDEO_BT848
+-
+- help
+- Support for Radio Data System (RDS) decoder. This allows seeing
+- radio station identification transmitted using this standard.
+- Currently, it works only with bt8x8 chips.
+-
+- To compile this driver as a module, choose M here: the
+- module will be called saa6588.
+-
+ config VIDEO_PMS
+ tristate "Mediavision Pro Movie Studio Video For Linux"
+ depends on ISA && VIDEO_V4L1
+@@ -602,7 +584,6 @@ config VIDEO_SAA5249
+ config VIDEO_VINO
+ tristate "SGI Vino Video For Linux (EXPERIMENTAL)"
+ depends on I2C && SGI_IP22 && EXPERIMENTAL && VIDEO_V4L2
+- select I2C_ALGO_SGI
+ select VIDEO_SAA7191 if VIDEO_HELPER_CHIPS_AUTO
+ help
+ Say Y here to build in support for the Vino video input system found
+@@ -639,7 +620,7 @@ config VIDEO_MXB
+ depends on PCI && VIDEO_V4L1 && I2C
+ select VIDEO_SAA7146_VV
+ select VIDEO_TUNER
+- select VIDEO_SAA7115 if VIDEO_HELPER_CHIPS_AUTO
++ select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_TDA9840 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_TEA6415C if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_TEA6420 if VIDEO_HELPER_CHIPS_AUTO
+@@ -728,13 +709,6 @@ config SOC_CAMERA_MT9M001
+ This driver supports MT9M001 cameras from Micron, monochrome
+ and colour models.
+
+-config MT9M001_PCA9536_SWITCH
+- bool "pca9536 datawidth switch for mt9m001"
+- depends on SOC_CAMERA_MT9M001 && GENERIC_GPIO
+- help
+- Select this if your MT9M001 camera uses a PCA9536 I2C GPIO
+- extender to switch between 8 and 10 bit datawidth modes
+-
+ config SOC_CAMERA_MT9M111
+ tristate "mt9m111 and mt9m112 support"
+ depends on SOC_CAMERA && I2C
+@@ -754,13 +728,6 @@ config SOC_CAMERA_MT9V022
+ help
+ This driver supports MT9V022 cameras from Micron
+
+-config MT9V022_PCA9536_SWITCH
+- bool "pca9536 datawidth switch for mt9v022"
+- depends on SOC_CAMERA_MT9V022 && GENERIC_GPIO
+- help
+- Select this if your MT9V022 camera uses a PCA9536 I2C GPIO
+- extender to switch between 8 and 10 bit datawidth modes
+-
+ config SOC_CAMERA_TW9910
+ tristate "tw9910 support"
+ depends on SOC_CAMERA && I2C
+@@ -779,6 +746,13 @@ config SOC_CAMERA_OV772X
+ help
+ This is a ov772x camera driver
+
++config VIDEO_MX3
++ tristate "i.MX3x Camera Sensor Interface driver"
++ depends on VIDEO_DEV && MX3_IPU && SOC_CAMERA
++ select VIDEOBUF_DMA_CONTIG
++ ---help---
++ This is a v4l2 driver for the i.MX3x Camera Sensor Interface
++
+ config VIDEO_PXA27x
+ tristate "PXA27x Quick Capture Interface driver"
+ depends on VIDEO_DEV && PXA27x && SOC_CAMERA
+@@ -817,6 +791,8 @@ source "drivers/media/video/gspca/Kconfig"
+
+ source "drivers/media/video/pvrusb2/Kconfig"
+
++source "drivers/media/video/hdpvr/Kconfig"
++
+ source "drivers/media/video/em28xx/Kconfig"
+
+ source "drivers/media/video/usbvision/Kconfig"
+diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
+index 72f6d03..b904674 100644
+--- a/drivers/media/video/Makefile
++++ b/drivers/media/video/Makefile
+@@ -30,7 +30,6 @@ obj-$(CONFIG_VIDEO_IR_I2C) += ir-kbd-i2c.o
+ obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o
+ obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o
+ obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o
+-obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o
+
+ obj-$(CONFIG_VIDEO_SAA6588) += saa6588.o
+ obj-$(CONFIG_VIDEO_SAA5246A) += saa5246a.o
+@@ -43,8 +42,6 @@ obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o
+ obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o
+ obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o
+ obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o
+-obj-$(CONFIG_VIDEO_SAA7111) += saa7111.o
+-obj-$(CONFIG_VIDEO_SAA7114) += saa7114.o
+ obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o
+ obj-$(CONFIG_VIDEO_SAA717X) += saa717x.o
+ obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o
+@@ -122,6 +119,8 @@ obj-$(CONFIG_USB_PWC) += pwc/
+ obj-$(CONFIG_USB_ZC0301) += zc0301/
+ obj-$(CONFIG_USB_GSPCA) += gspca/
+
++obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/
++
+ obj-$(CONFIG_USB_IBMCAM) += usbvideo/
+ obj-$(CONFIG_USB_KONICAWC) += usbvideo/
+ obj-$(CONFIG_USB_VICAM) += usbvideo/
+@@ -134,10 +133,11 @@ obj-$(CONFIG_VIDEO_CX18) += cx18/
+ obj-$(CONFIG_VIDEO_VIVI) += vivi.o
+ obj-$(CONFIG_VIDEO_CX23885) += cx23885/
+
+-obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
++obj-$(CONFIG_VIDEO_MX3) += mx3_camera.o
++obj-$(CONFIG_VIDEO_PXA27x) += pxa_camera.o
+ obj-$(CONFIG_VIDEO_SH_MOBILE_CEU) += sh_mobile_ceu_camera.o
+ obj-$(CONFIG_VIDEO_OMAP2) += omap2cam.o
+-obj-$(CONFIG_SOC_CAMERA) += soc_camera.o
++obj-$(CONFIG_SOC_CAMERA) += soc_camera.o
+ obj-$(CONFIG_SOC_CAMERA_MT9M001) += mt9m001.o
+ obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o
+ obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o
+diff --git a/drivers/media/video/adv7170.c b/drivers/media/video/adv7170.c
+index e0eb4f3..873c30a 100644
+--- a/drivers/media/video/adv7170.c
++++ b/drivers/media/video/adv7170.c
+@@ -34,15 +34,16 @@
+ #include <asm/uaccess.h>
+ #include <linux/i2c.h>
+ #include <linux/i2c-id.h>
+-#include <linux/videodev.h>
+-#include <linux/video_encoder.h>
+-#include <media/v4l2-common.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <linux/videodev2.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/v4l2-i2c-drv.h>
+
+ MODULE_DESCRIPTION("Analog Devices ADV7170 video encoder driver");
+ MODULE_AUTHOR("Maxim Yevtyushkin");
+ MODULE_LICENSE("GPL");
+
++
+ static int debug;
+ module_param(debug, int, 0);
+ MODULE_PARM_DESC(debug, "Debug level (0-1)");
+@@ -50,38 +51,43 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
+ /* ----------------------------------------------------------------------- */
+
+ struct adv7170 {
++ struct v4l2_subdev sd;
+ unsigned char reg[128];
+
+- int norm;
++ v4l2_std_id norm;
+ int input;
+- int enable;
+- int bright;
+- int contrast;
+- int hue;
+- int sat;
+ };
+
++static inline struct adv7170 *to_adv7170(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct adv7170, sd);
++}
++
+ static char *inputs[] = { "pass_through", "play_back" };
+-static char *norms[] = { "PAL", "NTSC" };
+
+ /* ----------------------------------------------------------------------- */
+
+-static inline int adv7170_write(struct i2c_client *client, u8 reg, u8 value)
++static inline int adv7170_write(struct v4l2_subdev *sd, u8 reg, u8 value)
+ {
+- struct adv7170 *encoder = i2c_get_clientdata(client);
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct adv7170 *encoder = to_adv7170(sd);
+
+ encoder->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+ }
+
+-static inline int adv7170_read(struct i2c_client *client, u8 reg)
++static inline int adv7170_read(struct v4l2_subdev *sd, u8 reg)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
+ return i2c_smbus_read_byte_data(client, reg);
+ }
+
+-static int adv7170_write_block(struct i2c_client *client,
++static int adv7170_write_block(struct v4l2_subdev *sd,
+ const u8 *data, unsigned int len)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct adv7170 *encoder = to_adv7170(sd);
+ int ret = -1;
+ u8 reg;
+
+@@ -89,7 +95,6 @@ static int adv7170_write_block(struct i2c_client *client,
+ * the adapter understands raw I2C */
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ /* do raw I2C, not smbus compatible */
+- struct adv7170 *encoder = i2c_get_clientdata(client);
+ u8 block_data[32];
+ int block_len;
+
+@@ -110,7 +115,7 @@ static int adv7170_write_block(struct i2c_client *client,
+ /* do some slow I2C emulation kind of thing */
+ while (len >= 2) {
+ reg = *data++;
+- ret = adv7170_write(client, reg, *data++);
++ ret = adv7170_write(sd, reg, *data++);
+ if (ret < 0)
+ break;
+ len -= 2;
+@@ -128,203 +133,161 @@ static int adv7170_write_block(struct i2c_client *client,
+ #define TR1PLAY 0x00
+
+ static const unsigned char init_NTSC[] = {
+- 0x00, 0x10, // MR0
+- 0x01, 0x20, // MR1
+- 0x02, 0x0e, // MR2 RTC control: bits 2 and 1
+- 0x03, 0x80, // MR3
+- 0x04, 0x30, // MR4
+- 0x05, 0x00, // Reserved
+- 0x06, 0x00, // Reserved
+- 0x07, TR0MODE, // TM0
+- 0x08, TR1CAPT, // TM1
+- 0x09, 0x16, // Fsc0
+- 0x0a, 0x7c, // Fsc1
+- 0x0b, 0xf0, // Fsc2
+- 0x0c, 0x21, // Fsc3
+- 0x0d, 0x00, // Subcarrier Phase
+- 0x0e, 0x00, // Closed Capt. Ext 0
+- 0x0f, 0x00, // Closed Capt. Ext 1
+- 0x10, 0x00, // Closed Capt. 0
+- 0x11, 0x00, // Closed Capt. 1
+- 0x12, 0x00, // Pedestal Ctl 0
+- 0x13, 0x00, // Pedestal Ctl 1
+- 0x14, 0x00, // Pedestal Ctl 2
+- 0x15, 0x00, // Pedestal Ctl 3
+- 0x16, 0x00, // CGMS_WSS_0
+- 0x17, 0x00, // CGMS_WSS_1
+- 0x18, 0x00, // CGMS_WSS_2
+- 0x19, 0x00, // Teletext Ctl
++ 0x00, 0x10, /* MR0 */
++ 0x01, 0x20, /* MR1 */
++ 0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */
++ 0x03, 0x80, /* MR3 */
++ 0x04, 0x30, /* MR4 */
++ 0x05, 0x00, /* Reserved */
++ 0x06, 0x00, /* Reserved */
++ 0x07, TR0MODE, /* TM0 */
++ 0x08, TR1CAPT, /* TM1 */
++ 0x09, 0x16, /* Fsc0 */
++ 0x0a, 0x7c, /* Fsc1 */
++ 0x0b, 0xf0, /* Fsc2 */
++ 0x0c, 0x21, /* Fsc3 */
++ 0x0d, 0x00, /* Subcarrier Phase */
++ 0x0e, 0x00, /* Closed Capt. Ext 0 */
++ 0x0f, 0x00, /* Closed Capt. Ext 1 */
++ 0x10, 0x00, /* Closed Capt. 0 */
++ 0x11, 0x00, /* Closed Capt. 1 */
++ 0x12, 0x00, /* Pedestal Ctl 0 */
++ 0x13, 0x00, /* Pedestal Ctl 1 */
++ 0x14, 0x00, /* Pedestal Ctl 2 */
++ 0x15, 0x00, /* Pedestal Ctl 3 */
++ 0x16, 0x00, /* CGMS_WSS_0 */
++ 0x17, 0x00, /* CGMS_WSS_1 */
++ 0x18, 0x00, /* CGMS_WSS_2 */
++ 0x19, 0x00, /* Teletext Ctl */
+ };
+
+ static const unsigned char init_PAL[] = {
+- 0x00, 0x71, // MR0
+- 0x01, 0x20, // MR1
+- 0x02, 0x0e, // MR2 RTC control: bits 2 and 1
+- 0x03, 0x80, // MR3
+- 0x04, 0x30, // MR4
+- 0x05, 0x00, // Reserved
+- 0x06, 0x00, // Reserved
+- 0x07, TR0MODE, // TM0
+- 0x08, TR1CAPT, // TM1
+- 0x09, 0xcb, // Fsc0
+- 0x0a, 0x8a, // Fsc1
+- 0x0b, 0x09, // Fsc2
+- 0x0c, 0x2a, // Fsc3
+- 0x0d, 0x00, // Subcarrier Phase
+- 0x0e, 0x00, // Closed Capt. Ext 0
+- 0x0f, 0x00, // Closed Capt. Ext 1
+- 0x10, 0x00, // Closed Capt. 0
+- 0x11, 0x00, // Closed Capt. 1
+- 0x12, 0x00, // Pedestal Ctl 0
+- 0x13, 0x00, // Pedestal Ctl 1
+- 0x14, 0x00, // Pedestal Ctl 2
+- 0x15, 0x00, // Pedestal Ctl 3
+- 0x16, 0x00, // CGMS_WSS_0
+- 0x17, 0x00, // CGMS_WSS_1
+- 0x18, 0x00, // CGMS_WSS_2
+- 0x19, 0x00, // Teletext Ctl
++ 0x00, 0x71, /* MR0 */
++ 0x01, 0x20, /* MR1 */
++ 0x02, 0x0e, /* MR2 RTC control: bits 2 and 1 */
++ 0x03, 0x80, /* MR3 */
++ 0x04, 0x30, /* MR4 */
++ 0x05, 0x00, /* Reserved */
++ 0x06, 0x00, /* Reserved */
++ 0x07, TR0MODE, /* TM0 */
++ 0x08, TR1CAPT, /* TM1 */
++ 0x09, 0xcb, /* Fsc0 */
++ 0x0a, 0x8a, /* Fsc1 */
++ 0x0b, 0x09, /* Fsc2 */
++ 0x0c, 0x2a, /* Fsc3 */
++ 0x0d, 0x00, /* Subcarrier Phase */
++ 0x0e, 0x00, /* Closed Capt. Ext 0 */
++ 0x0f, 0x00, /* Closed Capt. Ext 1 */
++ 0x10, 0x00, /* Closed Capt. 0 */
++ 0x11, 0x00, /* Closed Capt. 1 */
++ 0x12, 0x00, /* Pedestal Ctl 0 */
++ 0x13, 0x00, /* Pedestal Ctl 1 */
++ 0x14, 0x00, /* Pedestal Ctl 2 */
++ 0x15, 0x00, /* Pedestal Ctl 3 */
++ 0x16, 0x00, /* CGMS_WSS_0 */
++ 0x17, 0x00, /* CGMS_WSS_1 */
++ 0x18, 0x00, /* CGMS_WSS_2 */
++ 0x19, 0x00, /* Teletext Ctl */
+ };
+
+
+-static int adv7170_command(struct i2c_client *client, unsigned cmd, void *arg)
++static int adv7170_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
+ {
+- struct adv7170 *encoder = i2c_get_clientdata(client);
+-
+- switch (cmd) {
+- case 0:
+-#if 0
+- /* This is just for testing!!! */
+- adv7170_write_block(client, init_common,
+- sizeof(init_common));
+- adv7170_write(client, 0x07, TR0MODE | TR0RST);
+- adv7170_write(client, 0x07, TR0MODE);
+-#endif
+- break;
+-
+- case ENCODER_GET_CAPABILITIES:
+- {
+- struct video_encoder_capability *cap = arg;
+-
+- cap->flags = VIDEO_ENCODER_PAL |
+- VIDEO_ENCODER_NTSC;
+- cap->inputs = 2;
+- cap->outputs = 1;
+- break;
++ struct adv7170 *encoder = to_adv7170(sd);
++
++ v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std);
++
++ if (std & V4L2_STD_NTSC) {
++ adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC));
++ if (encoder->input == 0)
++ adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */
++ adv7170_write(sd, 0x07, TR0MODE | TR0RST);
++ adv7170_write(sd, 0x07, TR0MODE);
++ } else if (std & V4L2_STD_PAL) {
++ adv7170_write_block(sd, init_PAL, sizeof(init_PAL));
++ if (encoder->input == 0)
++ adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */
++ adv7170_write(sd, 0x07, TR0MODE | TR0RST);
++ adv7170_write(sd, 0x07, TR0MODE);
++ } else {
++ v4l2_dbg(1, debug, sd, "illegal norm: %llx\n",
++ (unsigned long long)std);
++ return -EINVAL;
+ }
++ v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std);
++ encoder->norm = std;
++ return 0;
++}
+
+- case ENCODER_SET_NORM:
+- {
+- int iarg = *(int *) arg;
+-
+- v4l_dbg(1, debug, client, "set norm %d\n", iarg);
+-
+- switch (iarg) {
+- case VIDEO_MODE_NTSC:
+- adv7170_write_block(client, init_NTSC,
+- sizeof(init_NTSC));
+- if (encoder->input == 0)
+- adv7170_write(client, 0x02, 0x0e); // Enable genlock
+- adv7170_write(client, 0x07, TR0MODE | TR0RST);
+- adv7170_write(client, 0x07, TR0MODE);
+- break;
+-
+- case VIDEO_MODE_PAL:
+- adv7170_write_block(client, init_PAL,
+- sizeof(init_PAL));
+- if (encoder->input == 0)
+- adv7170_write(client, 0x02, 0x0e); // Enable genlock
+- adv7170_write(client, 0x07, TR0MODE | TR0RST);
+- adv7170_write(client, 0x07, TR0MODE);
+- break;
+-
+- default:
+- v4l_dbg(1, debug, client, "illegal norm: %d\n", iarg);
+- return -EINVAL;
+- }
+- v4l_dbg(1, debug, client, "switched to %s\n", norms[iarg]);
+- encoder->norm = iarg;
+- break;
+- }
++static int adv7170_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
++{
++ struct adv7170 *encoder = to_adv7170(sd);
+
+- case ENCODER_SET_INPUT:
+- {
+- int iarg = *(int *) arg;
+-
+- /* RJ: *iarg = 0: input is from decoder
+- *iarg = 1: input is from ZR36060
+- *iarg = 2: color bar */
+-
+- v4l_dbg(1, debug, client, "set input from %s\n",
+- iarg == 0 ? "decoder" : "ZR36060");
+-
+- switch (iarg) {
+- case 0:
+- adv7170_write(client, 0x01, 0x20);
+- adv7170_write(client, 0x08, TR1CAPT); /* TR1 */
+- adv7170_write(client, 0x02, 0x0e); // Enable genlock
+- adv7170_write(client, 0x07, TR0MODE | TR0RST);
+- adv7170_write(client, 0x07, TR0MODE);
+- /* udelay(10); */
+- break;
+-
+- case 1:
+- adv7170_write(client, 0x01, 0x00);
+- adv7170_write(client, 0x08, TR1PLAY); /* TR1 */
+- adv7170_write(client, 0x02, 0x08);
+- adv7170_write(client, 0x07, TR0MODE | TR0RST);
+- adv7170_write(client, 0x07, TR0MODE);
+- /* udelay(10); */
+- break;
+-
+- default:
+- v4l_dbg(1, debug, client, "illegal input: %d\n", iarg);
+- return -EINVAL;
+- }
+- v4l_dbg(1, debug, client, "switched to %s\n", inputs[iarg]);
+- encoder->input = iarg;
+- break;
+- }
++ /* RJ: route->input = 0: input is from decoder
++ route->input = 1: input is from ZR36060
++ route->input = 2: color bar */
+
+- case ENCODER_SET_OUTPUT:
+- {
+- int *iarg = arg;
++ v4l2_dbg(1, debug, sd, "set input from %s\n",
++ route->input == 0 ? "decoder" : "ZR36060");
+
+- /* not much choice of outputs */
+- if (*iarg != 0) {
+- return -EINVAL;
+- }
++ switch (route->input) {
++ case 0:
++ adv7170_write(sd, 0x01, 0x20);
++ adv7170_write(sd, 0x08, TR1CAPT); /* TR1 */
++ adv7170_write(sd, 0x02, 0x0e); /* Enable genlock */
++ adv7170_write(sd, 0x07, TR0MODE | TR0RST);
++ adv7170_write(sd, 0x07, TR0MODE);
++ /* udelay(10); */
+ break;
+- }
+-
+- case ENCODER_ENABLE_OUTPUT:
+- {
+- int *iarg = arg;
+
+- encoder->enable = !!*iarg;
++ case 1:
++ adv7170_write(sd, 0x01, 0x00);
++ adv7170_write(sd, 0x08, TR1PLAY); /* TR1 */
++ adv7170_write(sd, 0x02, 0x08);
++ adv7170_write(sd, 0x07, TR0MODE | TR0RST);
++ adv7170_write(sd, 0x07, TR0MODE);
++ /* udelay(10); */
+ break;
+- }
+
+ default:
++ v4l2_dbg(1, debug, sd, "illegal input: %d\n", route->input);
+ return -EINVAL;
+ }
+-
++ v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[route->input]);
++ encoder->input = route->input;
+ return 0;
+ }
+
++static int adv7170_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7170, 0);
++}
++
+ /* ----------------------------------------------------------------------- */
+
+-static unsigned short normal_i2c[] = {
+- 0xd4 >> 1, 0xd6 >> 1, /* adv7170 IDs */
+- 0x54 >> 1, 0x56 >> 1, /* adv7171 IDs */
+- I2C_CLIENT_END
++static const struct v4l2_subdev_core_ops adv7170_core_ops = {
++ .g_chip_ident = adv7170_g_chip_ident,
+ };
+
+-I2C_CLIENT_INSMOD;
++static const struct v4l2_subdev_video_ops adv7170_video_ops = {
++ .s_std_output = adv7170_s_std_output,
++ .s_routing = adv7170_s_routing,
++};
++
++static const struct v4l2_subdev_ops adv7170_ops = {
++ .core = &adv7170_core_ops,
++ .video = &adv7170_video_ops,
++};
++
++/* ----------------------------------------------------------------------- */
+
+ static int adv7170_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+ {
+ struct adv7170 *encoder;
++ struct v4l2_subdev *sd;
+ int i;
+
+ /* Check if the adapter supports the needed features */
+@@ -337,26 +300,29 @@ static int adv7170_probe(struct i2c_client *client,
+ encoder = kzalloc(sizeof(struct adv7170), GFP_KERNEL);
+ if (encoder == NULL)
+ return -ENOMEM;
+- encoder->norm = VIDEO_MODE_NTSC;
++ sd = &encoder->sd;
++ v4l2_i2c_subdev_init(sd, client, &adv7170_ops);
++ encoder->norm = V4L2_STD_NTSC;
+ encoder->input = 0;
+- encoder->enable = 1;
+- i2c_set_clientdata(client, encoder);
+
+- i = adv7170_write_block(client, init_NTSC, sizeof(init_NTSC));
++ i = adv7170_write_block(sd, init_NTSC, sizeof(init_NTSC));
+ if (i >= 0) {
+- i = adv7170_write(client, 0x07, TR0MODE | TR0RST);
+- i = adv7170_write(client, 0x07, TR0MODE);
+- i = adv7170_read(client, 0x12);
+- v4l_dbg(1, debug, client, "revision %d\n", i & 1);
++ i = adv7170_write(sd, 0x07, TR0MODE | TR0RST);
++ i = adv7170_write(sd, 0x07, TR0MODE);
++ i = adv7170_read(sd, 0x12);
++ v4l2_dbg(1, debug, sd, "revision %d\n", i & 1);
+ }
+ if (i < 0)
+- v4l_dbg(1, debug, client, "init error 0x%x\n", i);
++ v4l2_dbg(1, debug, sd, "init error 0x%x\n", i);
+ return 0;
+ }
+
+ static int adv7170_remove(struct i2c_client *client)
+ {
+- kfree(i2c_get_clientdata(client));
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++
++ v4l2_device_unregister_subdev(sd);
++ kfree(to_adv7170(sd));
+ return 0;
+ }
+
+@@ -371,8 +337,6 @@ MODULE_DEVICE_TABLE(i2c, adv7170_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "adv7170",
+- .driverid = I2C_DRIVERID_ADV7170,
+- .command = adv7170_command,
+ .probe = adv7170_probe,
+ .remove = adv7170_remove,
+ .id_table = adv7170_id,
+diff --git a/drivers/media/video/adv7175.c b/drivers/media/video/adv7175.c
+index 6008e84..ff12103 100644
+--- a/drivers/media/video/adv7175.c
++++ b/drivers/media/video/adv7175.c
+@@ -30,15 +30,19 @@
+ #include <asm/uaccess.h>
+ #include <linux/i2c.h>
+ #include <linux/i2c-id.h>
+-#include <linux/videodev.h>
+-#include <linux/video_encoder.h>
+-#include <media/v4l2-common.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <linux/videodev2.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/v4l2-i2c-drv.h>
+
+ MODULE_DESCRIPTION("Analog Devices ADV7175 video encoder driver");
+ MODULE_AUTHOR("Dave Perks");
+ MODULE_LICENSE("GPL");
+
++#define I2C_ADV7175 0xd4
++#define I2C_ADV7176 0x54
++
++
+ static int debug;
+ module_param(debug, int, 0);
+ MODULE_PARM_DESC(debug, "Debug level (0-1)");
+@@ -46,36 +50,38 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
+ /* ----------------------------------------------------------------------- */
+
+ struct adv7175 {
+- int norm;
++ struct v4l2_subdev sd;
++ v4l2_std_id norm;
+ int input;
+- int enable;
+- int bright;
+- int contrast;
+- int hue;
+- int sat;
+ };
+
+-#define I2C_ADV7175 0xd4
+-#define I2C_ADV7176 0x54
++static inline struct adv7175 *to_adv7175(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct adv7175, sd);
++}
+
+ static char *inputs[] = { "pass_through", "play_back", "color_bar" };
+-static char *norms[] = { "PAL", "NTSC", "SECAM->PAL (may not work!)" };
+
+ /* ----------------------------------------------------------------------- */
+
+-static inline int adv7175_write(struct i2c_client *client, u8 reg, u8 value)
++static inline int adv7175_write(struct v4l2_subdev *sd, u8 reg, u8 value)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
+ return i2c_smbus_write_byte_data(client, reg, value);
+ }
+
+-static inline int adv7175_read(struct i2c_client *client, u8 reg)
++static inline int adv7175_read(struct v4l2_subdev *sd, u8 reg)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
+ return i2c_smbus_read_byte_data(client, reg);
+ }
+
+-static int adv7175_write_block(struct i2c_client *client,
++static int adv7175_write_block(struct v4l2_subdev *sd,
+ const u8 *data, unsigned int len)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret = -1;
+ u8 reg;
+
+@@ -103,7 +109,7 @@ static int adv7175_write_block(struct i2c_client *client,
+ /* do some slow I2C emulation kind of thing */
+ while (len >= 2) {
+ reg = *data++;
+- ret = adv7175_write(client, reg, *data++);
++ ret = adv7175_write(sd, reg, *data++);
+ if (ret < 0)
+ break;
+ len -= 2;
+@@ -113,18 +119,18 @@ static int adv7175_write_block(struct i2c_client *client,
+ return ret;
+ }
+
+-static void set_subcarrier_freq(struct i2c_client *client, int pass_through)
++static void set_subcarrier_freq(struct v4l2_subdev *sd, int pass_through)
+ {
+ /* for some reason pass_through NTSC needs
+ * a different sub-carrier freq to remain stable. */
+ if (pass_through)
+- adv7175_write(client, 0x02, 0x00);
++ adv7175_write(sd, 0x02, 0x00);
+ else
+- adv7175_write(client, 0x02, 0x55);
++ adv7175_write(sd, 0x02, 0x55);
+
+- adv7175_write(client, 0x03, 0x55);
+- adv7175_write(client, 0x04, 0x55);
+- adv7175_write(client, 0x05, 0x25);
++ adv7175_write(sd, 0x03, 0x55);
++ adv7175_write(sd, 0x04, 0x55);
++ adv7175_write(sd, 0x05, 0x25);
+ }
+
+ /* ----------------------------------------------------------------------- */
+@@ -184,180 +190,144 @@ static const unsigned char init_ntsc[] = {
+ 0x06, 0x1a, /* subc. phase */
+ };
+
+-static int adv7175_command(struct i2c_client *client, unsigned cmd, void *arg)
++static int adv7175_init(struct v4l2_subdev *sd, u32 val)
+ {
+- struct adv7175 *encoder = i2c_get_clientdata(client);
++ /* This is just for testing!!! */
++ adv7175_write_block(sd, init_common, sizeof(init_common));
++ adv7175_write(sd, 0x07, TR0MODE | TR0RST);
++ adv7175_write(sd, 0x07, TR0MODE);
++ return 0;
++}
+
+- switch (cmd) {
+- case 0:
+- /* This is just for testing!!! */
+- adv7175_write_block(client, init_common,
+- sizeof(init_common));
+- adv7175_write(client, 0x07, TR0MODE | TR0RST);
+- adv7175_write(client, 0x07, TR0MODE);
+- break;
++static int adv7175_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
++{
++ struct adv7175 *encoder = to_adv7175(sd);
++
++ if (std & V4L2_STD_NTSC) {
++ adv7175_write_block(sd, init_ntsc, sizeof(init_ntsc));
++ if (encoder->input == 0)
++ adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */
++ adv7175_write(sd, 0x07, TR0MODE | TR0RST);
++ adv7175_write(sd, 0x07, TR0MODE);
++ } else if (std & V4L2_STD_PAL) {
++ adv7175_write_block(sd, init_pal, sizeof(init_pal));
++ if (encoder->input == 0)
++ adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */
++ adv7175_write(sd, 0x07, TR0MODE | TR0RST);
++ adv7175_write(sd, 0x07, TR0MODE);
++ } else if (std & V4L2_STD_SECAM) {
++ /* This is an attempt to convert
++ * SECAM->PAL (typically it does not work
++ * due to genlock: when decoder is in SECAM
++ * and encoder in in PAL the subcarrier can
++ * not be syncronized with horizontal
++ * quency) */
++ adv7175_write_block(sd, init_pal, sizeof(init_pal));
++ if (encoder->input == 0)
++ adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */
++ adv7175_write(sd, 0x07, TR0MODE | TR0RST);
++ adv7175_write(sd, 0x07, TR0MODE);
++ } else {
++ v4l2_dbg(1, debug, sd, "illegal norm: %llx\n",
++ (unsigned long long)std);
++ return -EINVAL;
++ }
++ v4l2_dbg(1, debug, sd, "switched to %llx\n", (unsigned long long)std);
++ encoder->norm = std;
++ return 0;
++}
+
+- case ENCODER_GET_CAPABILITIES:
+- {
+- struct video_encoder_capability *cap = arg;
++static int adv7175_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
++{
++ struct adv7175 *encoder = to_adv7175(sd);
+
+- cap->flags = VIDEO_ENCODER_PAL |
+- VIDEO_ENCODER_NTSC |
+- VIDEO_ENCODER_SECAM; /* well, hacky */
+- cap->inputs = 2;
+- cap->outputs = 1;
+- break;
+- }
++ /* RJ: route->input = 0: input is from decoder
++ route->input = 1: input is from ZR36060
++ route->input = 2: color bar */
+
+- case ENCODER_SET_NORM:
+- {
+- int iarg = *(int *) arg;
+-
+- switch (iarg) {
+- case VIDEO_MODE_NTSC:
+- adv7175_write_block(client, init_ntsc,
+- sizeof(init_ntsc));
+- if (encoder->input == 0)
+- adv7175_write(client, 0x0d, 0x4f); // Enable genlock
+- adv7175_write(client, 0x07, TR0MODE | TR0RST);
+- adv7175_write(client, 0x07, TR0MODE);
+- break;
+-
+- case VIDEO_MODE_PAL:
+- adv7175_write_block(client, init_pal,
+- sizeof(init_pal));
+- if (encoder->input == 0)
+- adv7175_write(client, 0x0d, 0x4f); // Enable genlock
+- adv7175_write(client, 0x07, TR0MODE | TR0RST);
+- adv7175_write(client, 0x07, TR0MODE);
+- break;
+-
+- case VIDEO_MODE_SECAM: // WARNING! ADV7176 does not support SECAM.
+- /* This is an attempt to convert
+- * SECAM->PAL (typically it does not work
+- * due to genlock: when decoder is in SECAM
+- * and encoder in in PAL the subcarrier can
+- * not be syncronized with horizontal
+- * quency) */
+- adv7175_write_block(client, init_pal,
+- sizeof(init_pal));
+- if (encoder->input == 0)
+- adv7175_write(client, 0x0d, 0x49); // Disable genlock
+- adv7175_write(client, 0x07, TR0MODE | TR0RST);
+- adv7175_write(client, 0x07, TR0MODE);
+- break;
+- default:
+- v4l_dbg(1, debug, client, "illegal norm: %d\n", iarg);
+- return -EINVAL;
+- }
+- v4l_dbg(1, debug, client, "switched to %s\n", norms[iarg]);
+- encoder->norm = iarg;
++ switch (route->input) {
++ case 0:
++ adv7175_write(sd, 0x01, 0x00);
++
++ if (encoder->norm & V4L2_STD_NTSC)
++ set_subcarrier_freq(sd, 1);
++
++ adv7175_write(sd, 0x0c, TR1CAPT); /* TR1 */
++ if (encoder->norm & V4L2_STD_SECAM)
++ adv7175_write(sd, 0x0d, 0x49); /* Disable genlock */
++ else
++ adv7175_write(sd, 0x0d, 0x4f); /* Enable genlock */
++ adv7175_write(sd, 0x07, TR0MODE | TR0RST);
++ adv7175_write(sd, 0x07, TR0MODE);
++ /*udelay(10);*/
+ break;
+- }
+
+- case ENCODER_SET_INPUT:
+- {
+- int iarg = *(int *) arg;
+-
+- /* RJ: *iarg = 0: input is from SAA7110
+- *iarg = 1: input is from ZR36060
+- *iarg = 2: color bar */
+-
+- switch (iarg) {
+- case 0:
+- adv7175_write(client, 0x01, 0x00);
+-
+- if (encoder->norm == VIDEO_MODE_NTSC)
+- set_subcarrier_freq(client, 1);
+-
+- adv7175_write(client, 0x0c, TR1CAPT); /* TR1 */
+- if (encoder->norm == VIDEO_MODE_SECAM)
+- adv7175_write(client, 0x0d, 0x49); // Disable genlock
+- else
+- adv7175_write(client, 0x0d, 0x4f); // Enable genlock
+- adv7175_write(client, 0x07, TR0MODE | TR0RST);
+- adv7175_write(client, 0x07, TR0MODE);
+- //udelay(10);
+- break;
+-
+- case 1:
+- adv7175_write(client, 0x01, 0x00);
+-
+- if (encoder->norm == VIDEO_MODE_NTSC)
+- set_subcarrier_freq(client, 0);
+-
+- adv7175_write(client, 0x0c, TR1PLAY); /* TR1 */
+- adv7175_write(client, 0x0d, 0x49);
+- adv7175_write(client, 0x07, TR0MODE | TR0RST);
+- adv7175_write(client, 0x07, TR0MODE);
+- /* udelay(10); */
+- break;
+-
+- case 2:
+- adv7175_write(client, 0x01, 0x80);
+-
+- if (encoder->norm == VIDEO_MODE_NTSC)
+- set_subcarrier_freq(client, 0);
+-
+- adv7175_write(client, 0x0d, 0x49);
+- adv7175_write(client, 0x07, TR0MODE | TR0RST);
+- adv7175_write(client, 0x07, TR0MODE);
+- /* udelay(10); */
+- break;
+-
+- default:
+- v4l_dbg(1, debug, client, "illegal input: %d\n", iarg);
+- return -EINVAL;
+- }
+- v4l_dbg(1, debug, client, "switched to %s\n", inputs[iarg]);
+- encoder->input = iarg;
+- break;
+- }
++ case 1:
++ adv7175_write(sd, 0x01, 0x00);
+
+- case ENCODER_SET_OUTPUT:
+- {
+- int *iarg = arg;
++ if (encoder->norm & V4L2_STD_NTSC)
++ set_subcarrier_freq(sd, 0);
+
+- /* not much choice of outputs */
+- if (*iarg != 0)
+- return -EINVAL;
++ adv7175_write(sd, 0x0c, TR1PLAY); /* TR1 */
++ adv7175_write(sd, 0x0d, 0x49);
++ adv7175_write(sd, 0x07, TR0MODE | TR0RST);
++ adv7175_write(sd, 0x07, TR0MODE);
++ /* udelay(10); */
+ break;
+- }
+
+- case ENCODER_ENABLE_OUTPUT:
+- {
+- int *iarg = arg;
++ case 2:
++ adv7175_write(sd, 0x01, 0x80);
++
++ if (encoder->norm & V4L2_STD_NTSC)
++ set_subcarrier_freq(sd, 0);
+
+- encoder->enable = !!*iarg;
++ adv7175_write(sd, 0x0d, 0x49);
++ adv7175_write(sd, 0x07, TR0MODE | TR0RST);
++ adv7175_write(sd, 0x07, TR0MODE);
++ /* udelay(10); */
+ break;
+- }
+
+ default:
++ v4l2_dbg(1, debug, sd, "illegal input: %d\n", route->input);
+ return -EINVAL;
+ }
+-
++ v4l2_dbg(1, debug, sd, "switched to %s\n", inputs[route->input]);
++ encoder->input = route->input;
+ return 0;
+ }
+
++static int adv7175_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_ADV7175, 0);
++}
++
+ /* ----------------------------------------------------------------------- */
+
+-/*
+- * Generic i2c probe
+- * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+- */
+-static unsigned short normal_i2c[] = {
+- I2C_ADV7175 >> 1, (I2C_ADV7175 >> 1) + 1,
+- I2C_ADV7176 >> 1, (I2C_ADV7176 >> 1) + 1,
+- I2C_CLIENT_END
++static const struct v4l2_subdev_core_ops adv7175_core_ops = {
++ .g_chip_ident = adv7175_g_chip_ident,
++ .init = adv7175_init,
+ };
+
+-I2C_CLIENT_INSMOD;
++static const struct v4l2_subdev_video_ops adv7175_video_ops = {
++ .s_std_output = adv7175_s_std_output,
++ .s_routing = adv7175_s_routing,
++};
++
++static const struct v4l2_subdev_ops adv7175_ops = {
++ .core = &adv7175_core_ops,
++ .video = &adv7175_video_ops,
++};
++
++/* ----------------------------------------------------------------------- */
+
+ static int adv7175_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+ {
+ int i;
+ struct adv7175 *encoder;
++ struct v4l2_subdev *sd;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+@@ -369,26 +339,29 @@ static int adv7175_probe(struct i2c_client *client,
+ encoder = kzalloc(sizeof(struct adv7175), GFP_KERNEL);
+ if (encoder == NULL)
+ return -ENOMEM;
+- encoder->norm = VIDEO_MODE_PAL;
++ sd = &encoder->sd;
++ v4l2_i2c_subdev_init(sd, client, &adv7175_ops);
++ encoder->norm = V4L2_STD_NTSC;
+ encoder->input = 0;
+- encoder->enable = 1;
+- i2c_set_clientdata(client, encoder);
+
+- i = adv7175_write_block(client, init_common, sizeof(init_common));
++ i = adv7175_write_block(sd, init_common, sizeof(init_common));
+ if (i >= 0) {
+- i = adv7175_write(client, 0x07, TR0MODE | TR0RST);
+- i = adv7175_write(client, 0x07, TR0MODE);
+- i = adv7175_read(client, 0x12);
+- v4l_dbg(1, debug, client, "revision %d\n", i & 1);
++ i = adv7175_write(sd, 0x07, TR0MODE | TR0RST);
++ i = adv7175_write(sd, 0x07, TR0MODE);
++ i = adv7175_read(sd, 0x12);
++ v4l2_dbg(1, debug, sd, "revision %d\n", i & 1);
+ }
+ if (i < 0)
+- v4l_dbg(1, debug, client, "init error 0x%x\n", i);
++ v4l2_dbg(1, debug, sd, "init error 0x%x\n", i);
+ return 0;
+ }
+
+ static int adv7175_remove(struct i2c_client *client)
+ {
+- kfree(i2c_get_clientdata(client));
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++
++ v4l2_device_unregister_subdev(sd);
++ kfree(to_adv7175(sd));
+ return 0;
+ }
+
+@@ -403,8 +376,6 @@ MODULE_DEVICE_TABLE(i2c, adv7175_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "adv7175",
+- .driverid = I2C_DRIVERID_ADV7175,
+- .command = adv7175_command,
+ .probe = adv7175_probe,
+ .remove = adv7175_remove,
+ .id_table = adv7175_id,
+diff --git a/drivers/media/video/au0828/Kconfig b/drivers/media/video/au0828/Kconfig
+index 018f72b..05cdf49 100644
+--- a/drivers/media/video/au0828/Kconfig
++++ b/drivers/media/video/au0828/Kconfig
+@@ -1,13 +1,13 @@
+
+ config VIDEO_AU0828
+ tristate "Auvitek AU0828 support"
+- depends on I2C && INPUT && DVB_CORE && USB
++ depends on I2C && INPUT && DVB_CORE && USB && VIDEO_V4L2
+ select I2C_ALGOBIT
+ select VIDEO_TVEEPROM
+- select DVB_AU8522 if !DVB_FE_CUSTOMIZE
+- select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
+- select MEDIA_TUNER_MXL5007T if !DVB_FE_CUSTOMIZE
+- select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE
++ select DVB_AU8522 if !DVB_FE_CUSTOMISE
++ select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_MXL5007T if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
+ ---help---
+ This is a video4linux driver for Auvitek's USB device.
+
+diff --git a/drivers/media/video/au0828/Makefile b/drivers/media/video/au0828/Makefile
+index cd2c582..4d26231 100644
+--- a/drivers/media/video/au0828/Makefile
++++ b/drivers/media/video/au0828/Makefile
+@@ -1,4 +1,4 @@
+-au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o
++au0828-objs := au0828-core.o au0828-i2c.o au0828-cards.o au0828-dvb.o au0828-video.o
+
+ obj-$(CONFIG_VIDEO_AU0828) += au0828.o
+
+diff --git a/drivers/media/video/au0828/au0828-cards.c b/drivers/media/video/au0828/au0828-cards.c
+index d60123b..1aabaa7 100644
+--- a/drivers/media/video/au0828/au0828-cards.c
++++ b/drivers/media/video/au0828/au0828-cards.c
+@@ -21,25 +21,89 @@
+
+ #include "au0828.h"
+ #include "au0828-cards.h"
++#include "au8522.h"
++#include "media/tuner.h"
++#include "media/v4l2-common.h"
++
++void hvr950q_cs5340_audio(void *priv, int enable)
++{
++ /* Because the HVR-950q shares an i2s bus between the cs5340 and the
++ au8522, we need to hold cs5340 in reset when using the au8522 */
++ struct au0828_dev *dev = priv;
++ if (enable == 1)
++ au0828_set(dev, REG_000, 0x10);
++ else
++ au0828_clear(dev, REG_000, 0x10);
++}
+
+ struct au0828_board au0828_boards[] = {
+ [AU0828_BOARD_UNKNOWN] = {
+ .name = "Unknown board",
++ .tuner_type = UNSET,
++ .tuner_addr = ADDR_UNSET,
+ },
+ [AU0828_BOARD_HAUPPAUGE_HVR850] = {
+ .name = "Hauppauge HVR850",
++ .tuner_type = TUNER_XC5000,
++ .tuner_addr = 0x61,
++ .input = {
++ {
++ .type = AU0828_VMUX_TELEVISION,
++ .vmux = AU8522_COMPOSITE_CH4_SIF,
++ .amux = AU8522_AUDIO_SIF,
++ },
++ {
++ .type = AU0828_VMUX_COMPOSITE,
++ .vmux = AU8522_COMPOSITE_CH1,
++ .amux = AU8522_AUDIO_NONE,
++ .audio_setup = hvr950q_cs5340_audio,
++ },
++ {
++ .type = AU0828_VMUX_SVIDEO,
++ .vmux = AU8522_SVIDEO_CH13,
++ .amux = AU8522_AUDIO_NONE,
++ .audio_setup = hvr950q_cs5340_audio,
++ },
++ },
+ },
+ [AU0828_BOARD_HAUPPAUGE_HVR950Q] = {
+ .name = "Hauppauge HVR950Q",
++ .tuner_type = TUNER_XC5000,
++ .tuner_addr = 0x61,
++ .input = {
++ {
++ .type = AU0828_VMUX_TELEVISION,
++ .vmux = AU8522_COMPOSITE_CH4_SIF,
++ .amux = AU8522_AUDIO_SIF,
++ },
++ {
++ .type = AU0828_VMUX_COMPOSITE,
++ .vmux = AU8522_COMPOSITE_CH1,
++ .amux = AU8522_AUDIO_NONE,
++ .audio_setup = hvr950q_cs5340_audio,
++ },
++ {
++ .type = AU0828_VMUX_SVIDEO,
++ .vmux = AU8522_SVIDEO_CH13,
++ .amux = AU8522_AUDIO_NONE,
++ .audio_setup = hvr950q_cs5340_audio,
++ },
++ },
+ },
+ [AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL] = {
+ .name = "Hauppauge HVR950Q rev xxF8",
++ .tuner_type = UNSET,
++ .tuner_addr = ADDR_UNSET,
+ },
+ [AU0828_BOARD_DVICO_FUSIONHDTV7] = {
+ .name = "DViCO FusionHDTV USB",
++ .tuner_type = UNSET,
++ .tuner_addr = ADDR_UNSET,
+ },
+ [AU0828_BOARD_HAUPPAUGE_WOODBURY] = {
+ .name = "Hauppauge Woodbury",
++ .tuner_type = UNSET,
++ .tuner_addr = ADDR_UNSET,
+ },
+ };
+
+@@ -52,7 +116,7 @@ int au0828_tuner_callback(void *priv, int component, int command, int arg)
+
+ dprintk(1, "%s()\n", __func__);
+
+- switch (dev->board) {
++ switch (dev->boardnr) {
+ case AU0828_BOARD_HAUPPAUGE_HVR850:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
+@@ -81,17 +145,18 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data)
+ struct tveeprom tv;
+
+ tveeprom_hauppauge_analog(&dev->i2c_client, &tv, eeprom_data);
++ dev->board.tuner_type = tv.tuner_type;
+
+ /* Make sure we support the board model */
+ switch (tv.model) {
+ case 72000: /* WinTV-HVR950q (Retail, IR, ATSC/QAM */
+- case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and basic analog video */
+- case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */
+- case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */
+- case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and basic analog video */
+- case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and basic analog video */
+- case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and basic analog video */
+- case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and basic analog video */
++ case 72001: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */
++ case 72211: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
++ case 72221: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
++ case 72231: /* WinTV-HVR950q (OEM, IR, ATSC/QAM and analog video */
++ case 72241: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM and analog video */
++ case 72251: /* WinTV-HVR950q (Retail, IR, ATSC/QAM and analog video */
++ case 72301: /* WinTV-HVR850 (Retail, IR, ATSC and analog video */
+ case 72500: /* WinTV-HVR950q (OEM, No IR, ATSC/QAM */
+ break;
+ default:
+@@ -107,15 +172,21 @@ static void hauppauge_eeprom(struct au0828_dev *dev, u8 *eeprom_data)
+ void au0828_card_setup(struct au0828_dev *dev)
+ {
+ static u8 eeprom[256];
++ struct tuner_setup tun_setup;
++ struct v4l2_subdev *sd;
++ unsigned int mode_mask = T_ANALOG_TV |
++ T_DIGITAL_TV;
+
+ dprintk(1, "%s()\n", __func__);
+
++ memcpy(&dev->board, &au0828_boards[dev->boardnr], sizeof(dev->board));
++
+ if (dev->i2c_rc == 0) {
+ dev->i2c_client.addr = 0xa0 >> 1;
+ tveeprom_read(&dev->i2c_client, eeprom, sizeof(eeprom));
+ }
+
+- switch (dev->board) {
++ switch (dev->boardnr) {
+ case AU0828_BOARD_HAUPPAUGE_HVR850:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
+@@ -124,6 +195,32 @@ void au0828_card_setup(struct au0828_dev *dev)
+ hauppauge_eeprom(dev, eeprom+0xa0);
+ break;
+ }
++
++ if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED) {
++ /* Load the analog demodulator driver (note this would need to
++ be abstracted out if we ever need to support a different
++ demod) */
++ sd = v4l2_i2c_new_subdev(&dev->i2c_adap, "au8522", "au8522",
++ 0x8e >> 1);
++ if (sd == NULL)
++ printk(KERN_ERR "analog subdev registration failed\n");
++ }
++
++ /* Setup tuners */
++ if (dev->board.tuner_type != TUNER_ABSENT) {
++ /* Load the tuner module, which does the attach */
++ sd = v4l2_i2c_new_subdev(&dev->i2c_adap, "tuner", "tuner",
++ dev->board.tuner_addr);
++ if (sd == NULL)
++ printk(KERN_ERR "tuner subdev registration fail\n");
++
++ tun_setup.mode_mask = mode_mask;
++ tun_setup.type = dev->board.tuner_type;
++ tun_setup.addr = dev->board.tuner_addr;
++ tun_setup.tuner_callback = au0828_tuner_callback;
++ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_type_addr,
++ &tun_setup);
++ }
+ }
+
+ /*
+@@ -135,7 +232,7 @@ void au0828_gpio_setup(struct au0828_dev *dev)
+ {
+ dprintk(1, "%s()\n", __func__);
+
+- switch (dev->board) {
++ switch (dev->boardnr) {
+ case AU0828_BOARD_HAUPPAUGE_HVR850:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q_MXL:
+@@ -144,21 +241,23 @@ void au0828_gpio_setup(struct au0828_dev *dev)
+ * 4 - CS5340
+ * 5 - AU8522 Demodulator
+ * 6 - eeprom W/P
++ * 7 - power supply
+ * 9 - XC5000 Tuner
+ */
+
+ /* Into reset */
+ au0828_write(dev, REG_003, 0x02);
+- au0828_write(dev, REG_002, 0x88 | 0x20);
++ au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10);
+ au0828_write(dev, REG_001, 0x0);
+ au0828_write(dev, REG_000, 0x0);
+ msleep(100);
+
+- /* Out of reset */
++ /* Out of reset (leave the cs5340 in reset until needed) */
+ au0828_write(dev, REG_003, 0x02);
+ au0828_write(dev, REG_001, 0x02);
+- au0828_write(dev, REG_002, 0x88 | 0x20);
+- au0828_write(dev, REG_000, 0x88 | 0x20 | 0x40);
++ au0828_write(dev, REG_002, 0x80 | 0x20 | 0x10);
++ au0828_write(dev, REG_000, 0x80 | 0x40 | 0x20);
++
+ msleep(250);
+ break;
+ case AU0828_BOARD_DVICO_FUSIONHDTV7:
+diff --git a/drivers/media/video/au0828/au0828-core.c b/drivers/media/video/au0828/au0828-core.c
+index 5765e86..8c761d1 100644
+--- a/drivers/media/video/au0828/au0828-core.c
++++ b/drivers/media/video/au0828/au0828-core.c
+@@ -36,6 +36,8 @@ int au0828_debug;
+ module_param_named(debug, au0828_debug, int, 0644);
+ MODULE_PARM_DESC(debug, "enable debug messages");
+
++static atomic_t au0828_instance = ATOMIC_INIT(0);
++
+ #define _AU0828_BULKPIPE 0x03
+ #define _BULKPIPESIZE 0xffff
+
+@@ -51,13 +53,13 @@ static int recv_control_msg(struct au0828_dev *dev, u16 request, u32 value,
+ u32 au0828_readreg(struct au0828_dev *dev, u16 reg)
+ {
+ recv_control_msg(dev, CMD_REQUEST_IN, 0, reg, dev->ctrlmsg, 1);
+- dprintk(8, "%s(0x%x) = 0x%x\n", __func__, reg, dev->ctrlmsg[0]);
++ dprintk(8, "%s(0x%04x) = 0x%02x\n", __func__, reg, dev->ctrlmsg[0]);
+ return dev->ctrlmsg[0];
+ }
+
+ u32 au0828_writereg(struct au0828_dev *dev, u16 reg, u32 val)
+ {
+- dprintk(8, "%s(0x%x, 0x%x)\n", __func__, reg, val);
++ dprintk(8, "%s(0x%04x, 0x%02x)\n", __func__, reg, val);
+ return send_control_msg(dev, CMD_REQUEST_OUT, val, reg,
+ dev->ctrlmsg, 0);
+ }
+@@ -146,9 +148,14 @@ static void au0828_usb_disconnect(struct usb_interface *interface)
+ /* Digital TV */
+ au0828_dvb_unregister(dev);
+
++ if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED)
++ au0828_analog_unregister(dev);
++
+ /* I2C */
+ au0828_i2c_unregister(dev);
+
++ v4l2_device_unregister(&dev->v4l2_dev);
++
+ usb_set_intfdata(interface, NULL);
+
+ mutex_lock(&dev->mutex);
+@@ -162,7 +169,7 @@ static void au0828_usb_disconnect(struct usb_interface *interface)
+ static int au0828_usb_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+ {
+- int ifnum;
++ int ifnum, retval, i;
+ struct au0828_dev *dev;
+ struct usb_device *usbdev = interface_to_usbdev(interface);
+
+@@ -185,10 +192,22 @@ static int au0828_usb_probe(struct usb_interface *interface,
+ mutex_init(&dev->mutex);
+ mutex_init(&dev->dvb.lock);
+ dev->usbdev = usbdev;
+- dev->board = id->driver_info;
++ dev->boardnr = id->driver_info;
+
+ usb_set_intfdata(interface, dev);
+
++ /* Create the v4l2_device */
++ i = atomic_inc_return(&au0828_instance) - 1;
++ snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name), "%s-%03d",
++ "au0828", i);
++ retval = v4l2_device_register(&dev->usbdev->dev, &dev->v4l2_dev);
++ if (retval) {
++ printk(KERN_ERR "%s() v4l2_device_register failed\n",
++ __func__);
++ kfree(dev);
++ return -EIO;
++ }
++
+ /* Power Up the bridge */
+ au0828_write(dev, REG_600, 1 << 4);
+
+@@ -201,12 +220,15 @@ static int au0828_usb_probe(struct usb_interface *interface,
+ /* Setup */
+ au0828_card_setup(dev);
+
++ /* Analog TV */
++ if (AUVI_INPUT(0).type != AU0828_VMUX_UNDEFINED)
++ au0828_analog_register(dev, interface);
++
+ /* Digital TV */
+ au0828_dvb_register(dev);
+
+ printk(KERN_INFO "Registered device AU0828 [%s]\n",
+- au0828_boards[dev->board].name == NULL ? "Unset" :
+- au0828_boards[dev->board].name);
++ dev->board.name == NULL ? "Unset" : dev->board.name);
+
+ return 0;
+ }
+diff --git a/drivers/media/video/au0828/au0828-dvb.c b/drivers/media/video/au0828/au0828-dvb.c
+index a882cf5..14baffc 100644
+--- a/drivers/media/video/au0828/au0828-dvb.c
++++ b/drivers/media/video/au0828/au0828-dvb.c
+@@ -378,7 +378,7 @@ int au0828_dvb_register(struct au0828_dev *dev)
+ dprintk(1, "%s()\n", __func__);
+
+ /* init frontend */
+- switch (dev->board) {
++ switch (dev->boardnr) {
+ case AU0828_BOARD_HAUPPAUGE_HVR850:
+ case AU0828_BOARD_HAUPPAUGE_HVR950Q:
+ dvb->frontend = dvb_attach(au8522_attach,
+diff --git a/drivers/media/video/au0828/au0828-i2c.c b/drivers/media/video/au0828/au0828-i2c.c
+index d618fba..f9a958d 100644
+--- a/drivers/media/video/au0828/au0828-i2c.c
++++ b/drivers/media/video/au0828/au0828-i2c.c
+@@ -140,13 +140,39 @@ static int i2c_sendbytes(struct i2c_adapter *i2c_adap,
+ dprintk(4, "%s()\n", __func__);
+
+ au0828_write(dev, REG_2FF, 0x01);
+- au0828_write(dev, REG_202, 0x07);
++
++ /* FIXME: There is a problem with i2c communications with xc5000 that
++ requires us to slow down the i2c clock until we have a better
++ strategy (such as using the secondary i2c bus to do firmware
++ loading */
++ if ((msg->addr << 1) == 0xc2)
++ au0828_write(dev, REG_202, 0x40);
++ else
++ au0828_write(dev, REG_202, 0x07);
+
+ /* Hardware needs 8 bit addresses */
+ au0828_write(dev, REG_203, msg->addr << 1);
+
+ dprintk(4, "SEND: %02x\n", msg->addr);
+
++ /* Deal with i2c_scan */
++ if (msg->len == 0) {
++ /* The analog tuner detection code makes use of the SMBUS_QUICK
++ message (which involves a zero length i2c write). To avoid
++ checking the status register when we didn't strobe out any
++ actual bytes to the bus, just do a read check. This is
++ consistent with how I saw i2c device checking done in the
++ USB trace of the Windows driver */
++ au0828_write(dev, REG_200, 0x20);
++ if (!i2c_wait_done(i2c_adap))
++ return -EIO;
++
++ if (i2c_wait_read_ack(i2c_adap))
++ return -EIO;
++
++ return 0;
++ }
++
+ for (i = 0; i < msg->len;) {
+
+ dprintk(4, " %02x\n", msg->buf[i]);
+@@ -191,7 +217,15 @@ static int i2c_readbytes(struct i2c_adapter *i2c_adap,
+ dprintk(4, "%s()\n", __func__);
+
+ au0828_write(dev, REG_2FF, 0x01);
+- au0828_write(dev, REG_202, 0x07);
++
++ /* FIXME: There is a problem with i2c communications with xc5000 that
++ requires us to slow down the i2c clock until we have a better
++ strategy (such as using the secondary i2c bus to do firmware
++ loading */
++ if ((msg->addr << 1) == 0xc2)
++ au0828_write(dev, REG_202, 0x40);
++ else
++ au0828_write(dev, REG_202, 0x07);
+
+ /* Hardware needs 8 bit addresses */
+ au0828_write(dev, REG_203, msg->addr << 1);
+@@ -265,33 +299,6 @@ err:
+ return retval;
+ }
+
+-static int attach_inform(struct i2c_client *client)
+-{
+- dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n",
+- client->driver->driver.name, client->addr, client->name);
+-
+- if (!client->driver->command)
+- return 0;
+-
+- return 0;
+-}
+-
+-static int detach_inform(struct i2c_client *client)
+-{
+- dprintk(1, "i2c detach [client=%s]\n", client->name);
+-
+- return 0;
+-}
+-
+-void au0828_call_i2c_clients(struct au0828_dev *dev,
+- unsigned int cmd, void *arg)
+-{
+- if (dev->i2c_rc != 0)
+- return;
+-
+- i2c_clients_command(&dev->i2c_adap, cmd, arg);
+-}
+-
+ static u32 au0828_functionality(struct i2c_adapter *adap)
+ {
+ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
+@@ -309,9 +316,6 @@ static struct i2c_adapter au0828_i2c_adap_template = {
+ .owner = THIS_MODULE,
+ .id = I2C_HW_B_AU0828,
+ .algo = &au0828_i2c_algo_template,
+- .class = I2C_CLASS_TV_ANALOG,
+- .client_register = attach_inform,
+- .client_unregister = detach_inform,
+ };
+
+ static struct i2c_client au0828_i2c_client_template = {
+@@ -356,9 +360,9 @@ int au0828_i2c_register(struct au0828_dev *dev)
+ strlcpy(dev->i2c_adap.name, DRIVER_NAME,
+ sizeof(dev->i2c_adap.name));
+
+- dev->i2c_algo.data = dev;
++ dev->i2c_adap.algo = &dev->i2c_algo;
+ dev->i2c_adap.algo_data = dev;
+- i2c_set_adapdata(&dev->i2c_adap, dev);
++ i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
+ i2c_add_adapter(&dev->i2c_adap);
+
+ dev->i2c_client.adapter = &dev->i2c_adap;
+diff --git a/drivers/media/video/au0828/au0828-reg.h b/drivers/media/video/au0828/au0828-reg.h
+index 1e87fa0..b15e4a3 100644
+--- a/drivers/media/video/au0828/au0828-reg.h
++++ b/drivers/media/video/au0828/au0828-reg.h
+@@ -27,6 +27,9 @@
+ #define REG_002 0x002
+ #define REG_003 0x003
+
++#define AU0828_SENSORCTRL_100 0x100
++#define AU0828_SENSORCTRL_VBI_103 0x103
++
+ #define REG_200 0x200
+ #define REG_201 0x201
+ #define REG_202 0x202
+@@ -35,4 +38,7 @@
+ #define REG_209 0x209
+ #define REG_2FF 0x2ff
+
++/* Audio registers */
++#define AU0828_AUDIOCTRL_50C 0x50C
++
+ #define REG_600 0x600
+diff --git a/drivers/media/video/au0828/au0828-video.c b/drivers/media/video/au0828/au0828-video.c
+new file mode 100644
+index 0000000..f7ad495
+--- /dev/null
++++ b/drivers/media/video/au0828/au0828-video.c
+@@ -0,0 +1,1712 @@
++/*
++ * Auvitek AU0828 USB Bridge (Analog video support)
++ *
++ * Copyright (C) 2009 Devin Heitmueller <dheitmueller@linuxtv.org>
++ * Copyright (C) 2005-2008 Auvitek International, Ltd.
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License
++ * As published by the Free Software Foundation; either version 2
++ * of the License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++ * 02110-1301, USA.
++ */
++
++/* Developer Notes:
++ *
++ * VBI support is not yet working
++ * The hardware scaler supported is unimplemented
++ * AC97 audio support is unimplemented (only i2s audio mode)
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/device.h>
++#include <linux/suspend.h>
++#include <linux/version.h>
++#include <media/v4l2-common.h>
++#include <media/v4l2-ioctl.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/tuner.h>
++#include "au0828.h"
++#include "au0828-reg.h"
++
++static LIST_HEAD(au0828_devlist);
++static DEFINE_MUTEX(au0828_sysfs_lock);
++
++#define AU0828_VERSION_CODE KERNEL_VERSION(0, 0, 1)
++
++/* ------------------------------------------------------------------
++ Videobuf operations
++ ------------------------------------------------------------------*/
++
++static unsigned int isoc_debug;
++module_param(isoc_debug, int, 0644);
++MODULE_PARM_DESC(isoc_debug, "enable debug messages [isoc transfers]");
++
++#define au0828_isocdbg(fmt, arg...) \
++do {\
++ if (isoc_debug) { \
++ printk(KERN_INFO "au0828 %s :"fmt, \
++ __func__ , ##arg); \
++ } \
++ } while (0)
++
++static inline void print_err_status(struct au0828_dev *dev,
++ int packet, int status)
++{
++ char *errmsg = "Unknown";
++
++ switch (status) {
++ case -ENOENT:
++ errmsg = "unlinked synchronuously";
++ break;
++ case -ECONNRESET:
++ errmsg = "unlinked asynchronuously";
++ break;
++ case -ENOSR:
++ errmsg = "Buffer error (overrun)";
++ break;
++ case -EPIPE:
++ errmsg = "Stalled (device not responding)";
++ break;
++ case -EOVERFLOW:
++ errmsg = "Babble (bad cable?)";
++ break;
++ case -EPROTO:
++ errmsg = "Bit-stuff error (bad cable?)";
++ break;
++ case -EILSEQ:
++ errmsg = "CRC/Timeout (could be anything)";
++ break;
++ case -ETIME:
++ errmsg = "Device does not respond";
++ break;
++ }
++ if (packet < 0) {
++ au0828_isocdbg("URB status %d [%s].\n", status, errmsg);
++ } else {
++ au0828_isocdbg("URB packet %d, status %d [%s].\n",
++ packet, status, errmsg);
++ }
++}
++
++static int check_dev(struct au0828_dev *dev)
++{
++ if (dev->dev_state & DEV_DISCONNECTED) {
++ printk(KERN_INFO "v4l2 ioctl: device not present\n");
++ return -ENODEV;
++ }
++
++ if (dev->dev_state & DEV_MISCONFIGURED) {
++ printk(KERN_INFO "v4l2 ioctl: device is misconfigured; "
++ "close and open it again\n");
++ return -EIO;
++ }
++ return 0;
++}
++
++/*
++ * IRQ callback, called by URB callback
++ */
++static void au0828_irq_callback(struct urb *urb)
++{
++ struct au0828_dmaqueue *dma_q = urb->context;
++ struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq);
++ int rc, i;
++
++ switch (urb->status) {
++ case 0: /* success */
++ case -ETIMEDOUT: /* NAK */
++ break;
++ case -ECONNRESET: /* kill */
++ case -ENOENT:
++ case -ESHUTDOWN:
++ au0828_isocdbg("au0828_irq_callback called: status kill\n");
++ return;
++ default: /* unknown error */
++ au0828_isocdbg("urb completition error %d.\n", urb->status);
++ break;
++ }
++
++ /* Copy data from URB */
++ spin_lock(&dev->slock);
++ rc = dev->isoc_ctl.isoc_copy(dev, urb);
++ spin_unlock(&dev->slock);
++
++ /* Reset urb buffers */
++ for (i = 0; i < urb->number_of_packets; i++) {
++ urb->iso_frame_desc[i].status = 0;
++ urb->iso_frame_desc[i].actual_length = 0;
++ }
++ urb->status = 0;
++
++ urb->status = usb_submit_urb(urb, GFP_ATOMIC);
++ if (urb->status) {
++ au0828_isocdbg("urb resubmit failed (error=%i)\n",
++ urb->status);
++ }
++}
++
++/*
++ * Stop and Deallocate URBs
++ */
++void au0828_uninit_isoc(struct au0828_dev *dev)
++{
++ struct urb *urb;
++ int i;
++
++ au0828_isocdbg("au0828: called au0828_uninit_isoc\n");
++
++ dev->isoc_ctl.nfields = -1;
++ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
++ urb = dev->isoc_ctl.urb[i];
++ if (urb) {
++ if (!irqs_disabled())
++ usb_kill_urb(urb);
++ else
++ usb_unlink_urb(urb);
++
++ if (dev->isoc_ctl.transfer_buffer[i]) {
++ usb_buffer_free(dev->usbdev,
++ urb->transfer_buffer_length,
++ dev->isoc_ctl.transfer_buffer[i],
++ urb->transfer_dma);
++ }
++ usb_free_urb(urb);
++ dev->isoc_ctl.urb[i] = NULL;
++ }
++ dev->isoc_ctl.transfer_buffer[i] = NULL;
++ }
++
++ kfree(dev->isoc_ctl.urb);
++ kfree(dev->isoc_ctl.transfer_buffer);
++
++ dev->isoc_ctl.urb = NULL;
++ dev->isoc_ctl.transfer_buffer = NULL;
++ dev->isoc_ctl.num_bufs = 0;
++}
++
++/*
++ * Allocate URBs and start IRQ
++ */
++int au0828_init_isoc(struct au0828_dev *dev, int max_packets,
++ int num_bufs, int max_pkt_size,
++ int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb))
++{
++ struct au0828_dmaqueue *dma_q = &dev->vidq;
++ int i;
++ int sb_size, pipe;
++ struct urb *urb;
++ int j, k;
++ int rc;
++
++ au0828_isocdbg("au0828: called au0828_prepare_isoc\n");
++
++ /* De-allocates all pending stuff */
++ au0828_uninit_isoc(dev);
++
++ dev->isoc_ctl.isoc_copy = isoc_copy;
++ dev->isoc_ctl.num_bufs = num_bufs;
++
++ dev->isoc_ctl.urb = kzalloc(sizeof(void *)*num_bufs, GFP_KERNEL);
++ if (!dev->isoc_ctl.urb) {
++ au0828_isocdbg("cannot alloc memory for usb buffers\n");
++ return -ENOMEM;
++ }
++
++ dev->isoc_ctl.transfer_buffer = kzalloc(sizeof(void *)*num_bufs,
++ GFP_KERNEL);
++ if (!dev->isoc_ctl.transfer_buffer) {
++ au0828_isocdbg("cannot allocate memory for usb transfer\n");
++ kfree(dev->isoc_ctl.urb);
++ return -ENOMEM;
++ }
++
++ dev->isoc_ctl.max_pkt_size = max_pkt_size;
++ dev->isoc_ctl.buf = NULL;
++
++ sb_size = max_packets * dev->isoc_ctl.max_pkt_size;
++
++ /* allocate urbs and transfer buffers */
++ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
++ urb = usb_alloc_urb(max_packets, GFP_KERNEL);
++ if (!urb) {
++ au0828_isocdbg("cannot alloc isoc_ctl.urb %i\n", i);
++ au0828_uninit_isoc(dev);
++ return -ENOMEM;
++ }
++ dev->isoc_ctl.urb[i] = urb;
++
++ dev->isoc_ctl.transfer_buffer[i] = usb_buffer_alloc(dev->usbdev,
++ sb_size, GFP_KERNEL, &urb->transfer_dma);
++ if (!dev->isoc_ctl.transfer_buffer[i]) {
++ printk("unable to allocate %i bytes for transfer"
++ " buffer %i%s\n",
++ sb_size, i,
++ in_interrupt() ? " while in int" : "");
++ au0828_uninit_isoc(dev);
++ return -ENOMEM;
++ }
++ memset(dev->isoc_ctl.transfer_buffer[i], 0, sb_size);
++
++ pipe = usb_rcvisocpipe(dev->usbdev,
++ dev->isoc_in_endpointaddr),
++
++ usb_fill_int_urb(urb, dev->usbdev, pipe,
++ dev->isoc_ctl.transfer_buffer[i], sb_size,
++ au0828_irq_callback, dma_q, 1);
++
++ urb->number_of_packets = max_packets;
++ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
++
++ k = 0;
++ for (j = 0; j < max_packets; j++) {
++ urb->iso_frame_desc[j].offset = k;
++ urb->iso_frame_desc[j].length =
++ dev->isoc_ctl.max_pkt_size;
++ k += dev->isoc_ctl.max_pkt_size;
++ }
++ }
++
++ init_waitqueue_head(&dma_q->wq);
++
++ /* submit urbs and enables IRQ */
++ for (i = 0; i < dev->isoc_ctl.num_bufs; i++) {
++ rc = usb_submit_urb(dev->isoc_ctl.urb[i], GFP_ATOMIC);
++ if (rc) {
++ au0828_isocdbg("submit of urb %i failed (error=%i)\n",
++ i, rc);
++ au0828_uninit_isoc(dev);
++ return rc;
++ }
++ }
++
++ return 0;
++}
++
++/*
++ * Announces that a buffer were filled and request the next
++ */
++static inline void buffer_filled(struct au0828_dev *dev,
++ struct au0828_dmaqueue *dma_q,
++ struct au0828_buffer *buf)
++{
++ /* Advice that buffer was filled */
++ au0828_isocdbg("[%p/%d] wakeup\n", buf, buf->vb.i);
++
++ buf->vb.state = VIDEOBUF_DONE;
++ buf->vb.field_count++;
++ do_gettimeofday(&buf->vb.ts);
++
++ dev->isoc_ctl.buf = NULL;
++
++ list_del(&buf->vb.queue);
++ wake_up(&buf->vb.done);
++}
++
++/*
++ * Identify the buffer header type and properly handles
++ */
++static void au0828_copy_video(struct au0828_dev *dev,
++ struct au0828_dmaqueue *dma_q,
++ struct au0828_buffer *buf,
++ unsigned char *p,
++ unsigned char *outp, unsigned long len)
++{
++ void *fieldstart, *startwrite, *startread;
++ int linesdone, currlinedone, offset, lencopy, remain;
++ int bytesperline = dev->width << 1; /* Assumes 16-bit depth @@@@ */
++
++ if (dma_q->pos + len > buf->vb.size)
++ len = buf->vb.size - dma_q->pos;
++
++ startread = p;
++ remain = len;
++
++ /* Interlaces frame */
++ if (buf->top_field)
++ fieldstart = outp;
++ else
++ fieldstart = outp + bytesperline;
++
++ linesdone = dma_q->pos / bytesperline;
++ currlinedone = dma_q->pos % bytesperline;
++ offset = linesdone * bytesperline * 2 + currlinedone;
++ startwrite = fieldstart + offset;
++ lencopy = bytesperline - currlinedone;
++ lencopy = lencopy > remain ? remain : lencopy;
++
++ if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) {
++ au0828_isocdbg("Overflow of %zi bytes past buffer end (1)\n",
++ ((char *)startwrite + lencopy) -
++ ((char *)outp + buf->vb.size));
++ remain = (char *)outp + buf->vb.size - (char *)startwrite;
++ lencopy = remain;
++ }
++ if (lencopy <= 0)
++ return;
++ memcpy(startwrite, startread, lencopy);
++
++ remain -= lencopy;
++
++ while (remain > 0) {
++ startwrite += lencopy + bytesperline;
++ startread += lencopy;
++ if (bytesperline > remain)
++ lencopy = remain;
++ else
++ lencopy = bytesperline;
++
++ if ((char *)startwrite + lencopy > (char *)outp +
++ buf->vb.size) {
++ au0828_isocdbg("Overflow %zi bytes past buf end (2)\n",
++ ((char *)startwrite + lencopy) -
++ ((char *)outp + buf->vb.size));
++ lencopy = remain = (char *)outp + buf->vb.size -
++ (char *)startwrite;
++ }
++ if (lencopy <= 0)
++ break;
++
++ memcpy(startwrite, startread, lencopy);
++
++ remain -= lencopy;
++ }
++
++ if (offset > 1440) {
++ /* We have enough data to check for greenscreen */
++ if (outp[0] < 0x60 && outp[1440] < 0x60)
++ dev->greenscreen_detected = 1;
++ }
++
++ dma_q->pos += len;
++}
++
++/*
++ * video-buf generic routine to get the next available buffer
++ */
++static inline void get_next_buf(struct au0828_dmaqueue *dma_q,
++ struct au0828_buffer **buf)
++{
++ struct au0828_dev *dev = container_of(dma_q, struct au0828_dev, vidq);
++
++ if (list_empty(&dma_q->active)) {
++ au0828_isocdbg("No active queue to serve\n");
++ dev->isoc_ctl.buf = NULL;
++ *buf = NULL;
++ return;
++ }
++
++ /* Get the next buffer */
++ *buf = list_entry(dma_q->active.next, struct au0828_buffer, vb.queue);
++ dev->isoc_ctl.buf = *buf;
++
++ return;
++}
++
++/*
++ * Controls the isoc copy of each urb packet
++ */
++static inline int au0828_isoc_copy(struct au0828_dev *dev, struct urb *urb)
++{
++ struct au0828_buffer *buf;
++ struct au0828_dmaqueue *dma_q = urb->context;
++ unsigned char *outp = NULL;
++ int i, len = 0, rc = 1;
++ unsigned char *p;
++ unsigned char fbyte;
++
++ if (!dev)
++ return 0;
++
++ if ((dev->dev_state & DEV_DISCONNECTED) ||
++ (dev->dev_state & DEV_MISCONFIGURED))
++ return 0;
++
++ if (urb->status < 0) {
++ print_err_status(dev, -1, urb->status);
++ if (urb->status == -ENOENT)
++ return 0;
++ }
++
++ buf = dev->isoc_ctl.buf;
++ if (buf != NULL)
++ outp = videobuf_to_vmalloc(&buf->vb);
++
++ for (i = 0; i < urb->number_of_packets; i++) {
++ int status = urb->iso_frame_desc[i].status;
++
++ if (status < 0) {
++ print_err_status(dev, i, status);
++ if (urb->iso_frame_desc[i].status != -EPROTO)
++ continue;
++ }
++
++ if (urb->iso_frame_desc[i].actual_length <= 0)
++ continue;
++
++ if (urb->iso_frame_desc[i].actual_length >
++ dev->max_pkt_size) {
++ au0828_isocdbg("packet bigger than packet size");
++ continue;
++ }
++
++ p = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
++ fbyte = p[0];
++ len = urb->iso_frame_desc[i].actual_length - 4;
++ p += 4;
++
++ if (fbyte & 0x80) {
++ len -= 4;
++ p += 4;
++ au0828_isocdbg("Video frame %s\n",
++ (fbyte & 0x40) ? "odd" : "even");
++ if (!(fbyte & 0x40)) {
++ if (buf != NULL)
++ buffer_filled(dev, dma_q, buf);
++ get_next_buf(dma_q, &buf);
++ if (buf == NULL)
++ outp = NULL;
++ else
++ outp = videobuf_to_vmalloc(&buf->vb);
++ }
++
++ if (buf != NULL) {
++ if (fbyte & 0x40)
++ buf->top_field = 1;
++ else
++ buf->top_field = 0;
++ }
++
++ dma_q->pos = 0;
++ }
++ if (buf != NULL)
++ au0828_copy_video(dev, dma_q, buf, p, outp, len);
++ }
++ return rc;
++}
++
++static int
++buffer_setup(struct videobuf_queue *vq, unsigned int *count,
++ unsigned int *size)
++{
++ struct au0828_fh *fh = vq->priv_data;
++ *size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3;
++
++ if (0 == *count)
++ *count = AU0828_DEF_BUF;
++
++ if (*count < AU0828_MIN_BUF)
++ *count = AU0828_MIN_BUF;
++ return 0;
++}
++
++/* This is called *without* dev->slock held; please keep it that way */
++static void free_buffer(struct videobuf_queue *vq, struct au0828_buffer *buf)
++{
++ struct au0828_fh *fh = vq->priv_data;
++ struct au0828_dev *dev = fh->dev;
++ unsigned long flags = 0;
++ if (in_interrupt())
++ BUG();
++
++ /* We used to wait for the buffer to finish here, but this didn't work
++ because, as we were keeping the state as VIDEOBUF_QUEUED,
++ videobuf_queue_cancel marked it as finished for us.
++ (Also, it could wedge forever if the hardware was misconfigured.)
++
++ This should be safe; by the time we get here, the buffer isn't
++ queued anymore. If we ever start marking the buffers as
++ VIDEOBUF_ACTIVE, it won't be, though.
++ */
++ spin_lock_irqsave(&dev->slock, flags);
++ if (dev->isoc_ctl.buf == buf)
++ dev->isoc_ctl.buf = NULL;
++ spin_unlock_irqrestore(&dev->slock, flags);
++
++ videobuf_vmalloc_free(&buf->vb);
++ buf->vb.state = VIDEOBUF_NEEDS_INIT;
++}
++
++static int
++buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb,
++ enum v4l2_field field)
++{
++ struct au0828_fh *fh = vq->priv_data;
++ struct au0828_buffer *buf = container_of(vb, struct au0828_buffer, vb);
++ struct au0828_dev *dev = fh->dev;
++ int rc = 0, urb_init = 0;
++
++ buf->vb.size = (fh->dev->width * fh->dev->height * 16 + 7) >> 3;
++
++ if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size)
++ return -EINVAL;
++
++ buf->vb.width = dev->width;
++ buf->vb.height = dev->height;
++ buf->vb.field = field;
++
++ if (VIDEOBUF_NEEDS_INIT == buf->vb.state) {
++ rc = videobuf_iolock(vq, &buf->vb, NULL);
++ if (rc < 0) {
++ printk(KERN_INFO "videobuf_iolock failed\n");
++ goto fail;
++ }
++ }
++
++ if (!dev->isoc_ctl.num_bufs)
++ urb_init = 1;
++
++ if (urb_init) {
++ rc = au0828_init_isoc(dev, AU0828_ISO_PACKETS_PER_URB,
++ AU0828_MAX_ISO_BUFS, dev->max_pkt_size,
++ au0828_isoc_copy);
++ if (rc < 0) {
++ printk(KERN_INFO "au0828_init_isoc failed\n");
++ goto fail;
++ }
++ }
++
++ buf->vb.state = VIDEOBUF_PREPARED;
++ return 0;
++
++fail:
++ free_buffer(vq, buf);
++ return rc;
++}
++
++static void
++buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
++{
++ struct au0828_buffer *buf = container_of(vb,
++ struct au0828_buffer,
++ vb);
++ struct au0828_fh *fh = vq->priv_data;
++ struct au0828_dev *dev = fh->dev;
++ struct au0828_dmaqueue *vidq = &dev->vidq;
++
++ buf->vb.state = VIDEOBUF_QUEUED;
++ list_add_tail(&buf->vb.queue, &vidq->active);
++}
++
++static void buffer_release(struct videobuf_queue *vq,
++ struct videobuf_buffer *vb)
++{
++ struct au0828_buffer *buf = container_of(vb,
++ struct au0828_buffer,
++ vb);
++
++ free_buffer(vq, buf);
++}
++
++static struct videobuf_queue_ops au0828_video_qops = {
++ .buf_setup = buffer_setup,
++ .buf_prepare = buffer_prepare,
++ .buf_queue = buffer_queue,
++ .buf_release = buffer_release,
++};
++
++/* ------------------------------------------------------------------
++ V4L2 interface
++ ------------------------------------------------------------------*/
++
++static int au0828_i2s_init(struct au0828_dev *dev)
++{
++ /* Enable i2s mode */
++ au0828_writereg(dev, AU0828_AUDIOCTRL_50C, 0x01);
++ return 0;
++}
++
++/*
++ * Auvitek au0828 analog stream enable
++ * Please set interface0 to AS5 before enable the stream
++ */
++int au0828_analog_stream_enable(struct au0828_dev *d)
++{
++ dprintk(1, "au0828_analog_stream_enable called\n");
++ au0828_writereg(d, AU0828_SENSORCTRL_VBI_103, 0x00);
++ au0828_writereg(d, 0x106, 0x00);
++ /* set x position */
++ au0828_writereg(d, 0x110, 0x00);
++ au0828_writereg(d, 0x111, 0x00);
++ au0828_writereg(d, 0x114, 0xa0);
++ au0828_writereg(d, 0x115, 0x05);
++ /* set y position */
++ au0828_writereg(d, 0x112, 0x02);
++ au0828_writereg(d, 0x113, 0x00);
++ au0828_writereg(d, 0x116, 0xf2);
++ au0828_writereg(d, 0x117, 0x00);
++ au0828_writereg(d, AU0828_SENSORCTRL_100, 0xb3);
++
++ return 0;
++}
++
++int au0828_analog_stream_disable(struct au0828_dev *d)
++{
++ dprintk(1, "au0828_analog_stream_disable called\n");
++ au0828_writereg(d, AU0828_SENSORCTRL_100, 0x0);
++ return 0;
++}
++
++void au0828_analog_stream_reset(struct au0828_dev *dev)
++{
++ dprintk(1, "au0828_analog_stream_reset called\n");
++ au0828_writereg(dev, AU0828_SENSORCTRL_100, 0x0);
++ mdelay(30);
++ au0828_writereg(dev, AU0828_SENSORCTRL_100, 0xb3);
++}
++
++/*
++ * Some operations needs to stop current streaming
++ */
++static int au0828_stream_interrupt(struct au0828_dev *dev)
++{
++ int ret = 0;
++
++ dev->stream_state = STREAM_INTERRUPT;
++ if (dev->dev_state == DEV_DISCONNECTED)
++ return -ENODEV;
++ else if (ret) {
++ dev->dev_state = DEV_MISCONFIGURED;
++ dprintk(1, "%s device is misconfigured!\n", __func__);
++ return ret;
++ }
++ return 0;
++}
++
++/*
++ * au0828_release_resources
++ * unregister v4l2 devices
++ */
++void au0828_analog_unregister(struct au0828_dev *dev)
++{
++ dprintk(1, "au0828_release_resources called\n");
++ mutex_lock(&au0828_sysfs_lock);
++
++ if (dev->vdev) {
++ list_del(&dev->au0828list);
++ video_unregister_device(dev->vdev);
++ }
++ if (dev->vbi_dev)
++ video_unregister_device(dev->vbi_dev);
++
++ mutex_unlock(&au0828_sysfs_lock);
++}
++
++
++/* Usage lock check functions */
++static int res_get(struct au0828_fh *fh)
++{
++ struct au0828_dev *dev = fh->dev;
++ int rc = 0;
++
++ /* This instance already has stream_on */
++ if (fh->stream_on)
++ return rc;
++
++ if (dev->stream_on)
++ return -EBUSY;
++
++ dev->stream_on = 1;
++ fh->stream_on = 1;
++ return rc;
++}
++
++static int res_check(struct au0828_fh *fh)
++{
++ return fh->stream_on;
++}
++
++static void res_free(struct au0828_fh *fh)
++{
++ struct au0828_dev *dev = fh->dev;
++
++ fh->stream_on = 0;
++ dev->stream_on = 0;
++}
++
++static int au0828_v4l2_open(struct file *filp)
++{
++ int minor = video_devdata(filp)->minor;
++ int ret = 0;
++ struct au0828_dev *h, *dev = NULL;
++ struct au0828_fh *fh;
++ int type = 0;
++ struct list_head *list;
++
++ list_for_each(list, &au0828_devlist) {
++ h = list_entry(list, struct au0828_dev, au0828list);
++ if (h->vdev->minor == minor) {
++ dev = h;
++ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ }
++#ifdef VBI_IS_WORKING
++ if (h->vbi_dev->minor == minor) {
++ dev = h;
++ type = V4L2_BUF_TYPE_VBI_CAPTURE;
++ }
++#endif
++ }
++
++ if (NULL == dev)
++ return -ENODEV;
++
++ fh = kzalloc(sizeof(struct au0828_fh), GFP_KERNEL);
++ if (NULL == fh) {
++ dprintk(1, "Failed allocate au0828_fh struct!\n");
++ return -ENOMEM;
++ }
++
++ fh->type = type;
++ fh->dev = dev;
++ filp->private_data = fh;
++
++ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE && dev->users == 0) {
++ /* set au0828 interface0 to AS5 here again */
++ ret = usb_set_interface(dev->usbdev, 0, 5);
++ if (ret < 0) {
++ printk(KERN_INFO "Au0828 can't set alternate to 5!\n");
++ return -EBUSY;
++ }
++ dev->width = NTSC_STD_W;
++ dev->height = NTSC_STD_H;
++ dev->frame_size = dev->width * dev->height * 2;
++ dev->field_size = dev->width * dev->height;
++ dev->bytesperline = dev->width * 2;
++
++ au0828_analog_stream_enable(dev);
++ au0828_analog_stream_reset(dev);
++
++ /* If we were doing ac97 instead of i2s, it would go here...*/
++ au0828_i2s_init(dev);
++
++ dev->stream_state = STREAM_OFF;
++ dev->dev_state |= DEV_INITIALIZED;
++ }
++
++ dev->users++;
++
++ videobuf_queue_vmalloc_init(&fh->vb_vidq, &au0828_video_qops,
++ NULL, &dev->slock, fh->type,
++ V4L2_FIELD_INTERLACED,
++ sizeof(struct au0828_buffer), fh);
++
++ return ret;
++}
++
++static int au0828_v4l2_close(struct file *filp)
++{
++ int ret;
++ struct au0828_fh *fh = filp->private_data;
++ struct au0828_dev *dev = fh->dev;
++
++ mutex_lock(&dev->lock);
++ if (res_check(fh))
++ res_free(fh);
++
++ if (dev->users == 1) {
++ videobuf_stop(&fh->vb_vidq);
++ videobuf_mmap_free(&fh->vb_vidq);
++
++ if (dev->dev_state & DEV_DISCONNECTED) {
++ au0828_analog_unregister(dev);
++ mutex_unlock(&dev->lock);
++ kfree(dev);
++ return 0;
++ }
++
++ au0828_analog_stream_disable(dev);
++
++ au0828_uninit_isoc(dev);
++
++ /* When close the device, set the usb intf0 into alt0 to free
++ USB bandwidth */
++ ret = usb_set_interface(dev->usbdev, 0, 0);
++ if (ret < 0)
++ printk(KERN_INFO "Au0828 can't set alternate to 0!\n");
++ }
++
++ kfree(fh);
++ dev->users--;
++ wake_up_interruptible_nr(&dev->open, 1);
++ mutex_unlock(&dev->lock);
++ return 0;
++}
++
++static ssize_t au0828_v4l2_read(struct file *filp, char __user *buf,
++ size_t count, loff_t *pos)
++{
++ struct au0828_fh *fh = filp->private_data;
++ struct au0828_dev *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ if (fh->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
++ mutex_lock(&dev->lock);
++ rc = res_get(fh);
++ mutex_unlock(&dev->lock);
++
++ if (unlikely(rc < 0))
++ return rc;
++
++ return videobuf_read_stream(&fh->vb_vidq, buf, count, pos, 0,
++ filp->f_flags & O_NONBLOCK);
++ }
++ return 0;
++}
++
++static unsigned int au0828_v4l2_poll(struct file *filp, poll_table *wait)
++{
++ struct au0828_fh *fh = filp->private_data;
++ struct au0828_dev *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ mutex_lock(&dev->lock);
++ rc = res_get(fh);
++ mutex_unlock(&dev->lock);
++
++ if (unlikely(rc < 0))
++ return POLLERR;
++
++ if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type)
++ return POLLERR;
++
++ return videobuf_poll_stream(filp, &fh->vb_vidq, wait);
++}
++
++static int au0828_v4l2_mmap(struct file *filp, struct vm_area_struct *vma)
++{
++ struct au0828_fh *fh = filp->private_data;
++ struct au0828_dev *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ mutex_lock(&dev->lock);
++ rc = res_get(fh);
++ mutex_unlock(&dev->lock);
++
++ if (unlikely(rc < 0))
++ return rc;
++
++ rc = videobuf_mmap_mapper(&fh->vb_vidq, vma);
++
++ dprintk(2, "vma start=0x%08lx, size=%ld, ret=%d\n",
++ (unsigned long)vma->vm_start,
++ (unsigned long)vma->vm_end-(unsigned long)vma->vm_start,
++ rc);
++
++ return rc;
++}
++
++static int au0828_set_format(struct au0828_dev *dev, unsigned int cmd,
++ struct v4l2_format *format)
++{
++ int ret;
++ int width = format->fmt.pix.width;
++ int height = format->fmt.pix.height;
++ unsigned int maxwidth, maxheight;
++
++ maxwidth = 720;
++ maxheight = 480;
++
++#ifdef VBI_IS_WORKING
++ if (format->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
++ dprintk(1, "VBI format set: to be supported!\n");
++ return 0;
++ }
++ if (format->type == V4L2_BUF_TYPE_VBI_CAPTURE)
++ return 0;
++#endif
++ if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
++ return -EINVAL;
++
++ /* If they are demanding a format other than the one we support,
++ bail out (tvtime asks for UYVY and then retries with YUYV) */
++ if (format->fmt.pix.pixelformat != V4L2_PIX_FMT_UYVY)
++ return -EINVAL;
++
++ /* format->fmt.pix.width only support 720 and height 480 */
++ if (width != 720)
++ width = 720;
++ if (height != 480)
++ height = 480;
++
++ format->fmt.pix.width = width;
++ format->fmt.pix.height = height;
++ format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
++ format->fmt.pix.bytesperline = width * 2;
++ format->fmt.pix.sizeimage = width * height * 2;
++ format->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
++ format->fmt.pix.field = V4L2_FIELD_INTERLACED;
++
++ if (cmd == VIDIOC_TRY_FMT)
++ return 0;
++
++ /* maybe set new image format, driver current only support 720*480 */
++ dev->width = width;
++ dev->height = height;
++ dev->frame_size = width * height * 2;
++ dev->field_size = width * height;
++ dev->bytesperline = width * 2;
++
++ if (dev->stream_state == STREAM_ON) {
++ dprintk(1, "VIDIOC_SET_FMT: interrupting stream!\n");
++ ret = au0828_stream_interrupt(dev);
++ if (ret != 0) {
++ dprintk(1, "error interrupting video stream!\n");
++ return ret;
++ }
++ }
++
++ /* set au0828 interface0 to AS5 here again */
++ ret = usb_set_interface(dev->usbdev, 0, 5);
++ if (ret < 0) {
++ printk(KERN_INFO "Au0828 can't set alt setting to 5!\n");
++ return -EBUSY;
++ }
++
++ au0828_analog_stream_enable(dev);
++
++ return 0;
++}
++
++
++static int vidioc_queryctrl(struct file *file, void *priv,
++ struct v4l2_queryctrl *qc)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++ v4l2_device_call_all(&dev->v4l2_dev, 0, core, queryctrl, qc);
++ if (qc->type)
++ return 0;
++ else
++ return -EINVAL;
++}
++
++static int vidioc_querycap(struct file *file, void *priv,
++ struct v4l2_capability *cap)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++
++ strlcpy(cap->driver, "au0828", sizeof(cap->driver));
++ strlcpy(cap->card, dev->board.name, sizeof(cap->card));
++ strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
++
++ cap->version = AU0828_VERSION_CODE;
++
++ /*set the device capabilities */
++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
++#ifdef VBI_IS_WORKING
++ V4L2_CAP_VBI_CAPTURE |
++#endif
++ V4L2_CAP_AUDIO |
++ V4L2_CAP_READWRITE |
++ V4L2_CAP_STREAMING |
++ V4L2_CAP_TUNER;
++ return 0;
++}
++
++static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_fmtdesc *f)
++{
++ if (f->index)
++ return -EINVAL;
++
++ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ strcpy(f->description, "Packed YUV2");
++
++ f->flags = 0;
++ f->pixelformat = V4L2_PIX_FMT_UYVY;
++
++ return 0;
++}
++
++static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++
++ f->fmt.pix.width = dev->width;
++ f->fmt.pix.height = dev->height;
++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
++ f->fmt.pix.bytesperline = dev->bytesperline;
++ f->fmt.pix.sizeimage = dev->frame_size;
++ f->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M; /* NTSC/PAL */
++ f->fmt.pix.field = V4L2_FIELD_INTERLACED;
++ return 0;
++}
++
++static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++
++ return au0828_set_format(dev, VIDIOC_TRY_FMT, f);
++}
++
++static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++ int rc;
++
++ if (videobuf_queue_is_busy(&fh->vb_vidq)) {
++ printk(KERN_INFO "%s queue busy\n", __func__);
++ rc = -EBUSY;
++ goto out;
++ }
++
++ if (dev->stream_on && !fh->stream_on) {
++ printk(KERN_INFO "%s device in use by another fh\n", __func__);
++ rc = -EBUSY;
++ goto out;
++ }
++
++ return au0828_set_format(dev, VIDIOC_S_FMT, f);
++out:
++ return rc;
++}
++
++static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++
++ /* FIXME: when we support something other than NTSC, we are going to
++ have to make the au0828 bridge adjust the size of its capture
++ buffer, which is currently hardcoded at 720x480 */
++
++ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_std, *norm);
++ return 0;
++}
++
++static int vidioc_enum_input(struct file *file, void *priv,
++ struct v4l2_input *input)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++ unsigned int tmp;
++
++ static const char *inames[] = {
++ [AU0828_VMUX_UNDEFINED] = "Undefined",
++ [AU0828_VMUX_COMPOSITE] = "Composite",
++ [AU0828_VMUX_SVIDEO] = "S-Video",
++ [AU0828_VMUX_CABLE] = "Cable TV",
++ [AU0828_VMUX_TELEVISION] = "Television",
++ [AU0828_VMUX_DVB] = "DVB",
++ [AU0828_VMUX_DEBUG] = "tv debug"
++ };
++
++ tmp = input->index;
++
++ if (tmp > AU0828_MAX_INPUT)
++ return -EINVAL;
++ if (AUVI_INPUT(tmp).type == 0)
++ return -EINVAL;
++
++ input->index = tmp;
++ strcpy(input->name, inames[AUVI_INPUT(tmp).type]);
++ if ((AUVI_INPUT(tmp).type == AU0828_VMUX_TELEVISION) ||
++ (AUVI_INPUT(tmp).type == AU0828_VMUX_CABLE))
++ input->type |= V4L2_INPUT_TYPE_TUNER;
++ else
++ input->type |= V4L2_INPUT_TYPE_CAMERA;
++
++ input->std = dev->vdev->tvnorms;
++
++ return 0;
++}
++
++static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++ *i = dev->ctrl_input;
++ return 0;
++}
++
++static int vidioc_s_input(struct file *file, void *priv, unsigned int index)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++ int i;
++ struct v4l2_routing route;
++
++ dprintk(1, "VIDIOC_S_INPUT in function %s, input=%d\n", __func__,
++ index);
++ if (index >= AU0828_MAX_INPUT)
++ return -EINVAL;
++ if (AUVI_INPUT(index).type == 0)
++ return -EINVAL;
++ dev->ctrl_input = index;
++
++ switch (AUVI_INPUT(index).type) {
++ case AU0828_VMUX_SVIDEO:
++ dev->input_type = AU0828_VMUX_SVIDEO;
++ break;
++ case AU0828_VMUX_COMPOSITE:
++ dev->input_type = AU0828_VMUX_COMPOSITE;
++ break;
++ case AU0828_VMUX_TELEVISION:
++ dev->input_type = AU0828_VMUX_TELEVISION;
++ break;
++ default:
++ dprintk(1, "VIDIOC_S_INPUT unknown input type set [%d]\n",
++ AUVI_INPUT(index).type);
++ break;
++ }
++
++ route.input = AUVI_INPUT(index).vmux;
++ route.output = 0;
++ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_routing, &route);
++
++ for (i = 0; i < AU0828_MAX_INPUT; i++) {
++ int enable = 0;
++ if (AUVI_INPUT(i).audio_setup == NULL)
++ continue;
++
++ if (i == index)
++ enable = 1;
++ else
++ enable = 0;
++ if (enable) {
++ (AUVI_INPUT(i).audio_setup)(dev, enable);
++ } else {
++ /* Make sure we leave it turned on if some
++ other input is routed to this callback */
++ if ((AUVI_INPUT(i).audio_setup) !=
++ ((AUVI_INPUT(index).audio_setup))) {
++ (AUVI_INPUT(i).audio_setup)(dev, enable);
++ }
++ }
++ }
++
++ route.input = AUVI_INPUT(index).amux;
++ v4l2_device_call_all(&dev->v4l2_dev, 0, audio, s_routing, &route);
++ return 0;
++}
++
++static int vidioc_g_audio(struct file *file, void *priv, struct v4l2_audio *a)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++ unsigned int index = a->index;
++
++ if (a->index > 1)
++ return -EINVAL;
++
++ index = dev->ctrl_ainput;
++ if (index == 0)
++ strcpy(a->name, "Television");
++ else
++ 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 au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++ if (a->index != dev->ctrl_ainput)
++ return -EINVAL;
++ return 0;
++}
++
++static int vidioc_g_ctrl(struct file *file, void *priv,
++ struct v4l2_control *ctrl)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++
++ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_ctrl, ctrl);
++ return 0;
++
++}
++
++static int vidioc_s_ctrl(struct file *file, void *priv,
++ struct v4l2_control *ctrl)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_ctrl, ctrl);
++ return 0;
++}
++
++static int vidioc_g_tuner(struct file *file, void *priv, struct v4l2_tuner *t)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++
++ if (t->index != 0)
++ return -EINVAL;
++
++ strcpy(t->name, "Auvitek tuner");
++ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, g_tuner, t);
++ return 0;
++}
++
++static int vidioc_s_tuner(struct file *file, void *priv,
++ struct v4l2_tuner *t)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++
++ if (t->index != 0)
++ return -EINVAL;
++
++ t->type = V4L2_TUNER_ANALOG_TV;
++ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_tuner, t);
++ dprintk(1, "VIDIOC_S_TUNER: signal = %x, afc = %x\n", t->signal,
++ t->afc);
++ return 0;
++
++}
++
++static int vidioc_g_frequency(struct file *file, void *priv,
++ struct v4l2_frequency *freq)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++
++ freq->type = V4L2_TUNER_ANALOG_TV;
++ freq->frequency = dev->ctrl_freq;
++ return 0;
++}
++
++static int vidioc_s_frequency(struct file *file, void *priv,
++ struct v4l2_frequency *freq)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++
++ if (freq->tuner != 0)
++ return -EINVAL;
++ if (freq->type != V4L2_TUNER_ANALOG_TV)
++ return -EINVAL;
++
++ dev->ctrl_freq = freq->frequency;
++
++ v4l2_device_call_all(&dev->v4l2_dev, 0, tuner, s_frequency, freq);
++
++ au0828_analog_stream_reset(dev);
++
++ return 0;
++}
++
++static int vidioc_g_chip_ident(struct file *file, void *priv,
++ struct v4l2_dbg_chip_ident *chip)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++ chip->ident = V4L2_IDENT_NONE;
++ chip->revision = 0;
++
++ if (v4l2_chip_match_host(&chip->match)) {
++ chip->ident = V4L2_IDENT_AU0828;
++ return 0;
++ }
++
++ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_chip_ident, chip);
++ if (chip->ident == V4L2_IDENT_NONE)
++ return -EINVAL;
++
++ return 0;
++}
++
++static int vidioc_cropcap(struct file *file, void *priv,
++ struct v4l2_cropcap *cc)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *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;
++ cc->pixelaspect.denominator = 59;
++
++ return 0;
++}
++
++static int vidioc_streamon(struct file *file, void *priv,
++ enum v4l2_buf_type type)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
++ au0828_analog_stream_enable(dev);
++ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 1);
++ }
++
++ mutex_lock(&dev->lock);
++ rc = res_get(fh);
++
++ if (likely(rc >= 0))
++ rc = videobuf_streamon(&fh->vb_vidq);
++ mutex_unlock(&dev->lock);
++
++ return rc;
++}
++
++static int vidioc_streamoff(struct file *file, void *priv,
++ enum v4l2_buf_type type)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++ int i;
++ int ret;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
++ return -EINVAL;
++ if (type != fh->type)
++ return -EINVAL;
++
++ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
++ v4l2_device_call_all(&dev->v4l2_dev, 0, video, s_stream, 0);
++ ret = au0828_stream_interrupt(dev);
++ if (ret != 0)
++ return ret;
++ }
++
++ for (i = 0; i < AU0828_MAX_INPUT; i++) {
++ if (AUVI_INPUT(i).audio_setup == NULL)
++ continue;
++ (AUVI_INPUT(i).audio_setup)(dev, 0);
++ }
++
++ mutex_lock(&dev->lock);
++ videobuf_streamoff(&fh->vb_vidq);
++ res_free(fh);
++ mutex_unlock(&dev->lock);
++
++ return 0;
++}
++
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++static int vidioc_g_register(struct file *file, void *priv,
++ struct v4l2_dbg_register *reg)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++
++ switch (reg->match.type) {
++ case V4L2_CHIP_MATCH_I2C_DRIVER:
++ v4l2_device_call_all(&dev->v4l2_dev, 0, core, g_register, reg);
++ return 0;
++ default:
++ return -EINVAL;
++ }
++}
++
++static int vidioc_s_register(struct file *file, void *priv,
++ struct v4l2_dbg_register *reg)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++
++ switch (reg->match.type) {
++ case V4L2_CHIP_MATCH_I2C_DRIVER:
++ v4l2_device_call_all(&dev->v4l2_dev, 0, core, s_register, reg);
++ return 0;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++#endif
++
++static int vidioc_reqbufs(struct file *file, void *priv,
++ struct v4l2_requestbuffers *rb)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ return videobuf_reqbufs(&fh->vb_vidq, rb);
++}
++
++static int vidioc_querybuf(struct file *file, void *priv,
++ struct v4l2_buffer *b)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ return videobuf_querybuf(&fh->vb_vidq, b);
++}
++
++static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ return videobuf_qbuf(&fh->vb_vidq, b);
++}
++
++static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
++{
++ struct au0828_fh *fh = priv;
++ struct au0828_dev *dev = fh->dev;
++ int rc;
++
++ rc = check_dev(dev);
++ if (rc < 0)
++ return rc;
++
++ /* Workaround for a bug in the au0828 hardware design that sometimes
++ results in the colorspace being inverted */
++ if (dev->greenscreen_detected == 1) {
++ dprintk(1, "Detected green frame. Resetting stream...\n");
++ au0828_analog_stream_reset(dev);
++ dev->greenscreen_detected = 0;
++ }
++
++ return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
++}
++
++#ifdef CONFIG_VIDEO_V4L1_COMPAT
++static int vidiocgmbuf(struct file *file, void *priv, struct video_mbuf *mbuf)
++{
++ struct au0828_fh *fh = priv;
++
++ return videobuf_cgmbuf(&fh->vb_vidq, mbuf, 8);
++}
++#endif
++
++static struct v4l2_file_operations au0828_v4l_fops = {
++ .owner = THIS_MODULE,
++ .open = au0828_v4l2_open,
++ .release = au0828_v4l2_close,
++ .read = au0828_v4l2_read,
++ .poll = au0828_v4l2_poll,
++ .mmap = au0828_v4l2_mmap,
++ .ioctl = video_ioctl2,
++};
++
++static const struct v4l2_ioctl_ops video_ioctl_ops = {
++ .vidioc_querycap = vidioc_querycap,
++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
++ .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap,
++ .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap,
++#ifdef VBI_IS_WORKING
++ .vidioc_g_fmt_vbi_cap = vidioc_g_fmt_vbi_cap,
++ .vidioc_try_fmt_vbi_cap = vidioc_s_fmt_vbi_cap,
++ .vidioc_s_fmt_vbi_cap = vidioc_s_fmt_vbi_cap,
++#endif
++ .vidioc_g_audio = vidioc_g_audio,
++ .vidioc_s_audio = vidioc_s_audio,
++ .vidioc_cropcap = vidioc_cropcap,
++#ifdef VBI_IS_WORKING
++ .vidioc_g_fmt_sliced_vbi_cap = vidioc_g_fmt_sliced_vbi_cap,
++ .vidioc_try_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
++ .vidioc_s_fmt_sliced_vbi_cap = vidioc_try_set_sliced_vbi_cap,
++#endif
++ .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,
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++ .vidioc_g_register = vidioc_g_register,
++ .vidioc_s_register = vidioc_s_register,
++#endif
++ .vidioc_g_chip_ident = vidioc_g_chip_ident,
++#ifdef CONFIG_VIDEO_V4L1_COMPAT
++ .vidiocgmbuf = vidiocgmbuf,
++#endif
++};
++
++static const struct video_device au0828_video_template = {
++ .fops = &au0828_v4l_fops,
++ .release = video_device_release,
++ .ioctl_ops = &video_ioctl_ops,
++ .minor = -1,
++ .tvnorms = V4L2_STD_NTSC_M,
++ .current_norm = V4L2_STD_NTSC_M,
++};
++
++/**************************************************************************/
++
++int au0828_analog_register(struct au0828_dev *dev,
++ struct usb_interface *interface)
++{
++ int retval = -ENOMEM;
++ struct usb_host_interface *iface_desc;
++ struct usb_endpoint_descriptor *endpoint;
++ int i;
++
++ dprintk(1, "au0828_analog_register called!\n");
++
++ /* set au0828 usb interface0 to as5 */
++ retval = usb_set_interface(dev->usbdev,
++ interface->cur_altsetting->desc.bInterfaceNumber, 5);
++ if (retval != 0) {
++ printk(KERN_INFO "Failure setting usb interface0 to as5\n");
++ return retval;
++ }
++
++ /* Figure out which endpoint has the isoc interface */
++ iface_desc = interface->cur_altsetting;
++ for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
++ endpoint = &iface_desc->endpoint[i].desc;
++ if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
++ == USB_DIR_IN) &&
++ ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
++ == USB_ENDPOINT_XFER_ISOC)) {
++
++ /* we find our isoc in endpoint */
++ u16 tmp = le16_to_cpu(endpoint->wMaxPacketSize);
++ dev->max_pkt_size = (tmp & 0x07ff) *
++ (((tmp & 0x1800) >> 11) + 1);
++ dev->isoc_in_endpointaddr = endpoint->bEndpointAddress;
++ }
++ }
++ if (!(dev->isoc_in_endpointaddr)) {
++ printk(KERN_INFO "Could not locate isoc endpoint\n");
++ kfree(dev);
++ return -ENODEV;
++ }
++
++ init_waitqueue_head(&dev->open);
++ spin_lock_init(&dev->slock);
++ mutex_init(&dev->lock);
++
++ INIT_LIST_HEAD(&dev->vidq.active);
++ INIT_LIST_HEAD(&dev->vidq.queued);
++
++ dev->width = NTSC_STD_W;
++ dev->height = NTSC_STD_H;
++ dev->field_size = dev->width * dev->height;
++ dev->frame_size = dev->field_size << 1;
++ dev->bytesperline = dev->width << 1;
++ dev->ctrl_ainput = 0;
++
++ /* allocate and fill v4l2 video struct */
++ dev->vdev = video_device_alloc();
++ if (NULL == dev->vdev) {
++ dprintk(1, "Can't allocate video_device.\n");
++ return -ENOMEM;
++ }
++
++#ifdef VBI_IS_WORKING
++ dev->vbi_dev = video_device_alloc();
++ if (NULL == dev->vbi_dev) {
++ dprintk(1, "Can't allocate vbi_device.\n");
++ kfree(dev->vdev);
++ return -ENOMEM;
++ }
++#endif
++
++ /* Fill the video capture device struct */
++ *dev->vdev = au0828_video_template;
++ dev->vdev->parent = &dev->usbdev->dev;
++ strcpy(dev->vdev->name, "au0828a video");
++
++#ifdef VBI_IS_WORKING
++ /* Setup the VBI device */
++ *dev->vbi_dev = au0828_video_template;
++ dev->vbi_dev->parent = &dev->usbdev->dev;
++ strcpy(dev->vbi_dev->name, "au0828a vbi");
++#endif
++
++ list_add_tail(&dev->au0828list, &au0828_devlist);
++
++ /* Register the v4l2 device */
++ retval = video_register_device(dev->vdev, VFL_TYPE_GRABBER, -1);
++ if (retval != 0) {
++ dprintk(1, "unable to register video device (error = %d).\n",
++ retval);
++ list_del(&dev->au0828list);
++ video_device_release(dev->vdev);
++ return -ENODEV;
++ }
++
++#ifdef VBI_IS_WORKING
++ /* Register the vbi device */
++ retval = video_register_device(dev->vbi_dev, VFL_TYPE_VBI, -1);
++ if (retval != 0) {
++ dprintk(1, "unable to register vbi device (error = %d).\n",
++ retval);
++ list_del(&dev->au0828list);
++ video_device_release(dev->vbi_dev);
++ video_device_release(dev->vdev);
++ return -ENODEV;
++ }
++#endif
++
++ dprintk(1, "%s completed!\n", __func__);
++
++ return 0;
++}
++
+diff --git a/drivers/media/video/au0828/au0828.h b/drivers/media/video/au0828/au0828.h
+index 9d6a116..6ed1a61 100644
+--- a/drivers/media/video/au0828/au0828.h
++++ b/drivers/media/video/au0828/au0828.h
+@@ -24,6 +24,11 @@
+ #include <linux/i2c-algo-bit.h>
+ #include <media/tveeprom.h>
+
++/* Analog */
++#include <linux/videodev2.h>
++#include <media/videobuf-vmalloc.h>
++#include <media/v4l2-device.h>
++
+ /* DVB */
+ #include "demux.h"
+ #include "dmxdev.h"
+@@ -39,8 +44,45 @@
+ #define URB_COUNT 16
+ #define URB_BUFSIZE (0xe522)
+
++/* Analog constants */
++#define NTSC_STD_W 720
++#define NTSC_STD_H 480
++
++#define AU0828_INTERLACED_DEFAULT 1
++#define V4L2_CID_PRIVATE_SHARPNESS (V4L2_CID_PRIVATE_BASE + 0)
++
++/* Defination for AU0828 USB transfer */
++#define AU0828_MAX_ISO_BUFS 12 /* maybe resize this value in the future */
++#define AU0828_ISO_PACKETS_PER_URB 10
++
++#define AU0828_MIN_BUF 4
++#define AU0828_DEF_BUF 8
++
++#define AU0828_MAX_INPUT 4
++
++enum au0828_itype {
++ AU0828_VMUX_UNDEFINED = 0,
++ AU0828_VMUX_COMPOSITE,
++ AU0828_VMUX_SVIDEO,
++ AU0828_VMUX_CABLE,
++ AU0828_VMUX_TELEVISION,
++ AU0828_VMUX_DVB,
++ AU0828_VMUX_DEBUG
++};
++
++struct au0828_input {
++ enum au0828_itype type;
++ unsigned int vmux;
++ unsigned int amux;
++ void (*audio_setup) (void *priv, int enable);
++};
++
+ struct au0828_board {
+ char *name;
++ unsigned int tuner_type;
++ unsigned char tuner_addr;
++ struct au0828_input input[AU0828_MAX_INPUT];
++
+ };
+
+ struct au0828_dvb {
+@@ -55,31 +97,143 @@ struct au0828_dvb {
+ int feeding;
+ };
+
++enum au0828_stream_state {
++ STREAM_OFF,
++ STREAM_INTERRUPT,
++ STREAM_ON
++};
++
++#define AUVI_INPUT(nr) (dev->board.input[nr])
++
++/* device state */
++enum au0828_dev_state {
++ DEV_INITIALIZED = 0x01,
++ DEV_DISCONNECTED = 0x02,
++ DEV_MISCONFIGURED = 0x04
++};
++
++struct au0828_fh {
++ struct au0828_dev *dev;
++ unsigned int stream_on:1; /* Locks streams */
++ struct videobuf_queue vb_vidq;
++ enum v4l2_buf_type type;
++};
++
++struct au0828_usb_isoc_ctl {
++ /* max packet size of isoc transaction */
++ int max_pkt_size;
++
++ /* number of allocated urbs */
++ int num_bufs;
++
++ /* urb for isoc transfers */
++ struct urb **urb;
++
++ /* transfer buffers for isoc transfer */
++ char **transfer_buffer;
++
++ /* Last buffer command and region */
++ u8 cmd;
++ int pos, size, pktsize;
++
++ /* Last field: ODD or EVEN? */
++ int field;
++
++ /* Stores incomplete commands */
++ u32 tmp_buf;
++ int tmp_buf_len;
++
++ /* Stores already requested buffers */
++ struct au0828_buffer *buf;
++
++ /* Stores the number of received fields */
++ int nfields;
++
++ /* isoc urb callback */
++ int (*isoc_copy) (struct au0828_dev *dev, struct urb *urb);
++
++};
++
++/* buffer for one video frame */
++struct au0828_buffer {
++ /* common v4l buffer stuff -- must be first */
++ struct videobuf_buffer vb;
++
++ struct list_head frame;
++ int top_field;
++ int receiving;
++};
++
++struct au0828_dmaqueue {
++ struct list_head active;
++ struct list_head queued;
++
++ wait_queue_head_t wq;
++
++ /* Counters to control buffer fill */
++ int pos;
++};
++
+ struct au0828_dev {
+ struct mutex mutex;
+ struct usb_device *usbdev;
+- int board;
++ int boardnr;
++ struct au0828_board board;
+ u8 ctrlmsg[64];
+
+ /* I2C */
+ struct i2c_adapter i2c_adap;
+- struct i2c_algo_bit_data i2c_algo;
++ struct i2c_algorithm i2c_algo;
+ struct i2c_client i2c_client;
+ u32 i2c_rc;
+
+ /* Digital */
+ struct au0828_dvb dvb;
+
++ /* Analog */
++ struct list_head au0828list;
++ struct v4l2_device v4l2_dev;
++ int users;
++ unsigned int stream_on:1; /* Locks streams */
++ struct video_device *vdev;
++ struct video_device *vbi_dev;
++ int width;
++ int height;
++ u32 field_size;
++ u32 frame_size;
++ u32 bytesperline;
++ int type;
++ u8 ctrl_ainput;
++ __u8 isoc_in_endpointaddr;
++ u8 isoc_init_ok;
++ int greenscreen_detected;
++ unsigned int frame_count;
++ int ctrl_freq;
++ int input_type;
++ unsigned int ctrl_input;
++ enum au0828_dev_state dev_state;
++ enum au0828_stream_state stream_state;
++ wait_queue_head_t open;
++
++ struct mutex lock;
++
++ /* Isoc control struct */
++ struct au0828_dmaqueue vidq;
++ struct au0828_usb_isoc_ctl isoc_ctl;
++ spinlock_t slock;
++
++ /* usb transfer */
++ int alt; /* alternate */
++ int max_pkt_size; /* max packet size of isoc transaction */
++ int num_alt; /* Number of alternative settings */
++ unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
++ struct urb *urb[AU0828_MAX_ISO_BUFS]; /* urb for isoc transfers */
++ char *transfer_buffer[AU0828_MAX_ISO_BUFS];/* transfer buffers for isoc
++ transfer */
++
+ /* USB / URB Related */
+ int urb_streaming;
+ struct urb *urbs[URB_COUNT];
+-
+-};
+-
+-struct au0828_buff {
+- struct au0828_dev *dev;
+- struct urb *purb;
+- struct list_head buff_list;
+ };
+
+ /* ----------------------------------------------------------- */
+@@ -111,8 +265,13 @@ extern void au0828_card_setup(struct au0828_dev *dev);
+ /* au0828-i2c.c */
+ extern int au0828_i2c_register(struct au0828_dev *dev);
+ extern int au0828_i2c_unregister(struct au0828_dev *dev);
+-extern void au0828_call_i2c_clients(struct au0828_dev *dev,
+- unsigned int cmd, void *arg);
++
++/* ----------------------------------------------------------- */
++/* au0828-video.c */
++int au0828_analog_register(struct au0828_dev *dev,
++ struct usb_interface *interface);
++int au0828_analog_stream_disable(struct au0828_dev *d);
++void au0828_analog_unregister(struct au0828_dev *dev);
+
+ /* ----------------------------------------------------------- */
+ /* au0828-dvb.c */
+diff --git a/drivers/media/video/bt819.c b/drivers/media/video/bt819.c
+index a07b7b8..df4516d 100644
+--- a/drivers/media/video/bt819.c
++++ b/drivers/media/video/bt819.c
+@@ -29,16 +29,16 @@
+ */
+
+ #include <linux/module.h>
+-#include <linux/delay.h>
+ #include <linux/types.h>
+ #include <linux/ioctl.h>
+-#include <asm/uaccess.h>
++#include <linux/delay.h>
+ #include <linux/i2c.h>
+ #include <linux/i2c-id.h>
+-#include <linux/videodev.h>
+-#include <linux/video_decoder.h>
+-#include <media/v4l2-common.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <linux/videodev2.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/v4l2-i2c-drv.h>
++#include <media/bt819.h>
+
+ MODULE_DESCRIPTION("Brooktree-819 video decoder driver");
+ MODULE_AUTHOR("Mike Bernson & Dave Perks");
+@@ -48,13 +48,15 @@ static int debug;
+ module_param(debug, int, 0);
+ MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
++
+ /* ----------------------------------------------------------------------- */
+
+ struct bt819 {
++ struct v4l2_subdev sd;
+ unsigned char reg[32];
+
+- int initialized;
+- int norm;
++ v4l2_std_id norm;
++ int ident;
+ int input;
+ int enable;
+ int bright;
+@@ -63,6 +65,11 @@ struct bt819 {
+ int sat;
+ };
+
++static inline struct bt819 *to_bt819(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct bt819, sd);
++}
++
+ struct timing {
+ int hactive;
+ int hdelay;
+@@ -80,24 +87,23 @@ static struct timing timing_data[] = {
+
+ /* ----------------------------------------------------------------------- */
+
+-static inline int bt819_write(struct i2c_client *client, u8 reg, u8 value)
++static inline int bt819_write(struct bt819 *decoder, u8 reg, u8 value)
+ {
+- struct bt819 *decoder = i2c_get_clientdata(client);
++ struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd);
+
+ decoder->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+ }
+
+-static inline int bt819_setbit(struct i2c_client *client, u8 reg, u8 bit, u8 value)
++static inline int bt819_setbit(struct bt819 *decoder, u8 reg, u8 bit, u8 value)
+ {
+- struct bt819 *decoder = i2c_get_clientdata(client);
+-
+- return bt819_write(client, reg,
++ return bt819_write(decoder, reg,
+ (decoder->reg[reg] & ~(1 << bit)) | (value ? (1 << bit) : 0));
+ }
+
+-static int bt819_write_block(struct i2c_client *client, const u8 *data, unsigned int len)
++static int bt819_write_block(struct bt819 *decoder, const u8 *data, unsigned int len)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd);
+ int ret = -1;
+ u8 reg;
+
+@@ -105,7 +111,6 @@ static int bt819_write_block(struct i2c_client *client, const u8 *data, unsigned
+ * the adapter understands raw I2C */
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ /* do raw I2C, not smbus compatible */
+- struct bt819 *decoder = i2c_get_clientdata(client);
+ u8 block_data[32];
+ int block_len;
+
+@@ -126,7 +131,8 @@ static int bt819_write_block(struct i2c_client *client, const u8 *data, unsigned
+ /* do some slow I2C emulation kind of thing */
+ while (len >= 2) {
+ reg = *data++;
+- if ((ret = bt819_write(client, reg, *data++)) < 0)
++ ret = bt819_write(decoder, reg, *data++);
++ if (ret < 0)
+ break;
+ len -= 2;
+ }
+@@ -135,15 +141,15 @@ static int bt819_write_block(struct i2c_client *client, const u8 *data, unsigned
+ return ret;
+ }
+
+-static inline int bt819_read(struct i2c_client *client, u8 reg)
++static inline int bt819_read(struct bt819 *decoder, u8 reg)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(&decoder->sd);
++
+ return i2c_smbus_read_byte_data(client, reg);
+ }
+
+-static int bt819_init(struct i2c_client *client)
++static int bt819_init(struct v4l2_subdev *sd)
+ {
+- struct bt819 *decoder = i2c_get_clientdata(client);
+-
+ static unsigned char init[] = {
+ /*0x1f, 0x00,*/ /* Reset */
+ 0x01, 0x59, /* 0x01 input format */
+@@ -178,7 +184,8 @@ static int bt819_init(struct i2c_client *client)
+ 0x1a, 0x80, /* 0x1a ADC Interface */
+ };
+
+- struct timing *timing = &timing_data[decoder->norm];
++ struct bt819 *decoder = to_bt819(sd);
++ struct timing *timing = &timing_data[(decoder->norm & V4L2_STD_525_60) ? 1 : 0];
+
+ init[0x03 * 2 - 1] =
+ (((timing->vdelay >> 8) & 0x03) << 6) |
+@@ -192,266 +199,306 @@ static int bt819_init(struct i2c_client *client)
+ init[0x08 * 2 - 1] = timing->hscale >> 8;
+ init[0x09 * 2 - 1] = timing->hscale & 0xff;
+ /* 0x15 in array is address 0x19 */
+- init[0x15 * 2 - 1] = (decoder->norm == 0) ? 115 : 93; /* Chroma burst delay */
++ init[0x15 * 2 - 1] = (decoder->norm & V4L2_STD_625_50) ? 115 : 93; /* Chroma burst delay */
+ /* reset */
+- bt819_write(client, 0x1f, 0x00);
++ bt819_write(decoder, 0x1f, 0x00);
+ mdelay(1);
+
+ /* init */
+- return bt819_write_block(client, init, sizeof(init));
++ return bt819_write_block(decoder, init, sizeof(init));
+ }
+
+ /* ----------------------------------------------------------------------- */
+
+-static int bt819_command(struct i2c_client *client, unsigned cmd, void *arg)
++static int bt819_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
+ {
+- int temp;
++ struct bt819 *decoder = to_bt819(sd);
++ int status = bt819_read(decoder, 0x00);
++ int res = V4L2_IN_ST_NO_SIGNAL;
++ v4l2_std_id std;
+
+- struct bt819 *decoder = i2c_get_clientdata(client);
++ if ((status & 0x80))
++ res = 0;
+
+- if (!decoder->initialized) { /* First call to bt819_init could be */
+- bt819_init(client); /* without #FRST = 0 */
+- decoder->initialized = 1;
+- }
++ if ((status & 0x10))
++ std = V4L2_STD_PAL;
++ else
++ std = V4L2_STD_NTSC;
++ if (pstd)
++ *pstd = std;
++ if (pstatus)
++ *pstatus = status;
+
+- switch (cmd) {
+- case 0:
+- /* This is just for testing!!! */
+- bt819_init(client);
+- break;
++ v4l2_dbg(1, debug, sd, "get status %x\n", status);
++ return 0;
++}
+
+- case DECODER_GET_CAPABILITIES:
+- {
+- struct video_decoder_capability *cap = arg;
++static int bt819_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
++{
++ return bt819_status(sd, NULL, std);
++}
+
+- cap->flags = VIDEO_DECODER_PAL |
+- VIDEO_DECODER_NTSC |
+- VIDEO_DECODER_AUTO |
+- VIDEO_DECODER_CCIR;
+- cap->inputs = 8;
+- cap->outputs = 1;
+- break;
++static int bt819_g_input_status(struct v4l2_subdev *sd, u32 *status)
++{
++ return bt819_status(sd, status, NULL);
++}
++
++static int bt819_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
++{
++ struct bt819 *decoder = to_bt819(sd);
++ struct timing *timing = NULL;
++
++ v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std);
++
++ if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL)
++ v4l2_err(sd, "no notify found!\n");
++
++ if (std & V4L2_STD_NTSC) {
++ v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, 0);
++ bt819_setbit(decoder, 0x01, 0, 1);
++ bt819_setbit(decoder, 0x01, 1, 0);
++ bt819_setbit(decoder, 0x01, 5, 0);
++ bt819_write(decoder, 0x18, 0x68);
++ bt819_write(decoder, 0x19, 0x5d);
++ /* bt819_setbit(decoder, 0x1a, 5, 1); */
++ timing = &timing_data[1];
++ } else if (std & V4L2_STD_PAL) {
++ v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, 0);
++ bt819_setbit(decoder, 0x01, 0, 1);
++ bt819_setbit(decoder, 0x01, 1, 1);
++ bt819_setbit(decoder, 0x01, 5, 1);
++ bt819_write(decoder, 0x18, 0x7f);
++ bt819_write(decoder, 0x19, 0x72);
++ /* bt819_setbit(decoder, 0x1a, 5, 0); */
++ timing = &timing_data[0];
++ } else {
++ v4l2_dbg(1, debug, sd, "unsupported norm %llx\n",
++ (unsigned long long)std);
++ return -EINVAL;
+ }
++ bt819_write(decoder, 0x03,
++ (((timing->vdelay >> 8) & 0x03) << 6) |
++ (((timing->vactive >> 8) & 0x03) << 4) |
++ (((timing->hdelay >> 8) & 0x03) << 2) |
++ ((timing->hactive >> 8) & 0x03));
++ bt819_write(decoder, 0x04, timing->vdelay & 0xff);
++ bt819_write(decoder, 0x05, timing->vactive & 0xff);
++ bt819_write(decoder, 0x06, timing->hdelay & 0xff);
++ bt819_write(decoder, 0x07, timing->hactive & 0xff);
++ bt819_write(decoder, 0x08, (timing->hscale >> 8) & 0xff);
++ bt819_write(decoder, 0x09, timing->hscale & 0xff);
++ decoder->norm = std;
++ v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, 0);
++ return 0;
++}
+
+- case DECODER_GET_STATUS:
+- {
+- int *iarg = arg;
+- int status;
+- int res;
++static int bt819_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
++{
++ struct bt819 *decoder = to_bt819(sd);
+
+- status = bt819_read(client, 0x00);
+- res = 0;
+- if ((status & 0x80))
+- res |= DECODER_STATUS_GOOD;
++ v4l2_dbg(1, debug, sd, "set input %x\n", route->input);
+
+- switch (decoder->norm) {
+- case VIDEO_MODE_NTSC:
+- res |= DECODER_STATUS_NTSC;
+- break;
+- case VIDEO_MODE_PAL:
+- res |= DECODER_STATUS_PAL;
+- break;
+- default:
+- case VIDEO_MODE_AUTO:
+- if ((status & 0x10))
+- res |= DECODER_STATUS_PAL;
+- else
+- res |= DECODER_STATUS_NTSC;
+- break;
+- }
+- res |= DECODER_STATUS_COLOR;
+- *iarg = res;
++ if (route->input < 0 || route->input > 7)
++ return -EINVAL;
+
+- v4l_dbg(1, debug, client, "get status %x\n", *iarg);
+- break;
++ if (sd->v4l2_dev == NULL || sd->v4l2_dev->notify == NULL)
++ v4l2_err(sd, "no notify found!\n");
++
++ if (decoder->input != route->input) {
++ v4l2_subdev_notify(sd, BT819_FIFO_RESET_LOW, 0);
++ decoder->input = route->input;
++ /* select mode */
++ if (decoder->input == 0) {
++ bt819_setbit(decoder, 0x0b, 6, 0);
++ bt819_setbit(decoder, 0x1a, 1, 1);
++ } else {
++ bt819_setbit(decoder, 0x0b, 6, 1);
++ bt819_setbit(decoder, 0x1a, 1, 0);
++ }
++ v4l2_subdev_notify(sd, BT819_FIFO_RESET_HIGH, 0);
+ }
++ return 0;
++}
+
+- case DECODER_SET_NORM:
+- {
+- int *iarg = arg;
+- struct timing *timing = NULL;
+-
+- v4l_dbg(1, debug, client, "set norm %x\n", *iarg);
+-
+- switch (*iarg) {
+- case VIDEO_MODE_NTSC:
+- bt819_setbit(client, 0x01, 0, 1);
+- bt819_setbit(client, 0x01, 1, 0);
+- bt819_setbit(client, 0x01, 5, 0);
+- bt819_write(client, 0x18, 0x68);
+- bt819_write(client, 0x19, 0x5d);
+- /* bt819_setbit(client, 0x1a, 5, 1); */
+- timing = &timing_data[VIDEO_MODE_NTSC];
+- break;
+- case VIDEO_MODE_PAL:
+- bt819_setbit(client, 0x01, 0, 1);
+- bt819_setbit(client, 0x01, 1, 1);
+- bt819_setbit(client, 0x01, 5, 1);
+- bt819_write(client, 0x18, 0x7f);
+- bt819_write(client, 0x19, 0x72);
+- /* bt819_setbit(client, 0x1a, 5, 0); */
+- timing = &timing_data[VIDEO_MODE_PAL];
+- break;
+- case VIDEO_MODE_AUTO:
+- bt819_setbit(client, 0x01, 0, 0);
+- bt819_setbit(client, 0x01, 1, 0);
+- break;
+- default:
+- v4l_dbg(1, debug, client, "unsupported norm %x\n", *iarg);
+- return -EINVAL;
+- }
++static int bt819_s_stream(struct v4l2_subdev *sd, int enable)
++{
++ struct bt819 *decoder = to_bt819(sd);
+
+- if (timing) {
+- bt819_write(client, 0x03,
+- (((timing->vdelay >> 8) & 0x03) << 6) |
+- (((timing->vactive >> 8) & 0x03) << 4) |
+- (((timing->hdelay >> 8) & 0x03) << 2) |
+- ((timing->hactive >> 8) & 0x03) );
+- bt819_write(client, 0x04, timing->vdelay & 0xff);
+- bt819_write(client, 0x05, timing->vactive & 0xff);
+- bt819_write(client, 0x06, timing->hdelay & 0xff);
+- bt819_write(client, 0x07, timing->hactive & 0xff);
+- bt819_write(client, 0x08, (timing->hscale >> 8) & 0xff);
+- bt819_write(client, 0x09, timing->hscale & 0xff);
+- }
++ v4l2_dbg(1, debug, sd, "enable output %x\n", enable);
+
+- decoder->norm = *iarg;
+- break;
++ if (decoder->enable != enable) {
++ decoder->enable = enable;
++ bt819_setbit(decoder, 0x16, 7, !enable);
+ }
++ return 0;
++}
+
+- case DECODER_SET_INPUT:
+- {
+- int *iarg = arg;
+-
+- v4l_dbg(1, debug, client, "set input %x\n", *iarg);
+-
+- if (*iarg < 0 || *iarg > 7)
+- return -EINVAL;
+-
+- if (decoder->input != *iarg) {
+- decoder->input = *iarg;
+- /* select mode */
+- if (decoder->input == 0) {
+- bt819_setbit(client, 0x0b, 6, 0);
+- bt819_setbit(client, 0x1a, 1, 1);
+- } else {
+- bt819_setbit(client, 0x0b, 6, 1);
+- bt819_setbit(client, 0x1a, 1, 0);
+- }
+- }
++static int bt819_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
++{
++ switch (qc->id) {
++ case V4L2_CID_BRIGHTNESS:
++ v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
+ break;
+- }
+
+- case DECODER_SET_OUTPUT:
+- {
+- int *iarg = arg;
++ case V4L2_CID_CONTRAST:
++ v4l2_ctrl_query_fill(qc, 0, 511, 1, 256);
++ break;
+
+- v4l_dbg(1, debug, client, "set output %x\n", *iarg);
++ case V4L2_CID_SATURATION:
++ v4l2_ctrl_query_fill(qc, 0, 511, 1, 256);
++ break;
+
+- /* not much choice of outputs */
+- if (*iarg != 0)
+- return -EINVAL;
++ case V4L2_CID_HUE:
++ v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
+ break;
+- }
+
+- case DECODER_ENABLE_OUTPUT:
+- {
+- int *iarg = arg;
+- int enable = (*iarg != 0);
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
+
+- v4l_dbg(1, debug, client, "enable output %x\n", *iarg);
++static int bt819_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
++{
++ struct bt819 *decoder = to_bt819(sd);
++ int temp;
+
+- if (decoder->enable != enable) {
+- decoder->enable = enable;
+- bt819_setbit(client, 0x16, 7, !enable);
+- }
++ switch (ctrl->id) {
++ case V4L2_CID_BRIGHTNESS:
++ if (decoder->bright == ctrl->value)
++ break;
++ decoder->bright = ctrl->value;
++ bt819_write(decoder, 0x0a, decoder->bright);
+ break;
+- }
+
+- case DECODER_SET_PICTURE:
+- {
+- struct video_picture *pic = arg;
+-
+- v4l_dbg(1, debug, client,
+- "set picture brightness %d contrast %d colour %d\n",
+- pic->brightness, pic->contrast, pic->colour);
++ case V4L2_CID_CONTRAST:
++ if (decoder->contrast == ctrl->value)
++ break;
++ decoder->contrast = ctrl->value;
++ bt819_write(decoder, 0x0c, decoder->contrast & 0xff);
++ bt819_setbit(decoder, 0x0b, 2, ((decoder->contrast >> 8) & 0x01));
++ break;
+
++ case V4L2_CID_SATURATION:
++ if (decoder->sat == ctrl->value)
++ break;
++ decoder->sat = ctrl->value;
++ bt819_write(decoder, 0x0d, (decoder->sat >> 7) & 0xff);
++ bt819_setbit(decoder, 0x0b, 1, ((decoder->sat >> 15) & 0x01));
++
++ /* Ratio between U gain and V gain must stay the same as
++ the ratio between the default U and V gain values. */
++ temp = (decoder->sat * 180) / 254;
++ bt819_write(decoder, 0x0e, (temp >> 7) & 0xff);
++ bt819_setbit(decoder, 0x0b, 0, (temp >> 15) & 0x01);
++ break;
+
+- if (decoder->bright != pic->brightness) {
+- /* We want -128 to 127 we get 0-65535 */
+- decoder->bright = pic->brightness;
+- bt819_write(client, 0x0a,
+- (decoder->bright >> 8) - 128);
+- }
++ case V4L2_CID_HUE:
++ if (decoder->hue == ctrl->value)
++ break;
++ decoder->hue = ctrl->value;
++ bt819_write(decoder, 0x0f, decoder->hue);
++ break;
+
+- if (decoder->contrast != pic->contrast) {
+- /* We want 0 to 511 we get 0-65535 */
+- decoder->contrast = pic->contrast;
+- bt819_write(client, 0x0c,
+- (decoder->contrast >> 7) & 0xff);
+- bt819_setbit(client, 0x0b, 2,
+- ((decoder->contrast >> 15) & 0x01));
+- }
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
+
+- if (decoder->sat != pic->colour) {
+- /* We want 0 to 511 we get 0-65535 */
+- decoder->sat = pic->colour;
+- bt819_write(client, 0x0d,
+- (decoder->sat >> 7) & 0xff);
+- bt819_setbit(client, 0x0b, 1,
+- ((decoder->sat >> 15) & 0x01));
+-
+- temp = (decoder->sat * 201) / 237;
+- bt819_write(client, 0x0e, (temp >> 7) & 0xff);
+- bt819_setbit(client, 0x0b, 0, (temp >> 15) & 0x01);
+- }
++static int bt819_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
++{
++ struct bt819 *decoder = to_bt819(sd);
+
+- if (decoder->hue != pic->hue) {
+- /* We want -128 to 127 we get 0-65535 */
+- decoder->hue = pic->hue;
+- bt819_write(client, 0x0f,
+- 128 - (decoder->hue >> 8));
+- }
++ switch (ctrl->id) {
++ case V4L2_CID_BRIGHTNESS:
++ ctrl->value = decoder->bright;
++ break;
++ case V4L2_CID_CONTRAST:
++ ctrl->value = decoder->contrast;
++ break;
++ case V4L2_CID_SATURATION:
++ ctrl->value = decoder->sat;
++ break;
++ case V4L2_CID_HUE:
++ ctrl->value = decoder->hue;
+ break;
+- }
+-
+ default:
+ return -EINVAL;
+ }
+-
+ return 0;
+ }
+
++static int bt819_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
++{
++ struct bt819 *decoder = to_bt819(sd);
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
++ return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0);
++}
++
+ /* ----------------------------------------------------------------------- */
+
+-static unsigned short normal_i2c[] = { 0x8a >> 1, I2C_CLIENT_END };
++static const struct v4l2_subdev_core_ops bt819_core_ops = {
++ .g_chip_ident = bt819_g_chip_ident,
++ .g_ctrl = bt819_g_ctrl,
++ .s_ctrl = bt819_s_ctrl,
++ .queryctrl = bt819_queryctrl,
++};
++
++static const struct v4l2_subdev_tuner_ops bt819_tuner_ops = {
++ .s_std = bt819_s_std,
++};
++
++static const struct v4l2_subdev_video_ops bt819_video_ops = {
++ .s_routing = bt819_s_routing,
++ .s_stream = bt819_s_stream,
++ .querystd = bt819_querystd,
++ .g_input_status = bt819_g_input_status,
++};
++
++static const struct v4l2_subdev_ops bt819_ops = {
++ .core = &bt819_core_ops,
++ .tuner = &bt819_tuner_ops,
++ .video = &bt819_video_ops,
++};
+
+-I2C_CLIENT_INSMOD;
++/* ----------------------------------------------------------------------- */
+
+ static int bt819_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+ {
+ int i, ver;
+ struct bt819 *decoder;
++ struct v4l2_subdev *sd;
+ const char *name;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
+
+- ver = bt819_read(client, 0x17);
++ decoder = kzalloc(sizeof(struct bt819), GFP_KERNEL);
++ if (decoder == NULL)
++ return -ENOMEM;
++ sd = &decoder->sd;
++ v4l2_i2c_subdev_init(sd, client, &bt819_ops);
++
++ ver = bt819_read(decoder, 0x17);
+ switch (ver & 0xf0) {
+ case 0x70:
+ name = "bt819a";
++ decoder->ident = V4L2_IDENT_BT819A;
+ break;
+ case 0x60:
+ name = "bt817a";
++ decoder->ident = V4L2_IDENT_BT817A;
+ break;
+ case 0x20:
+ name = "bt815a";
++ decoder->ident = V4L2_IDENT_BT815A;
+ break;
+ default:
+- v4l_dbg(1, debug, client,
++ v4l2_dbg(1, debug, sd,
+ "unknown chip version 0x%02x\n", ver);
+ return -ENODEV;
+ }
+@@ -459,28 +506,26 @@ static int bt819_probe(struct i2c_client *client,
+ v4l_info(client, "%s found @ 0x%x (%s)\n", name,
+ client->addr << 1, client->adapter->name);
+
+- decoder = kzalloc(sizeof(struct bt819), GFP_KERNEL);
+- if (decoder == NULL)
+- return -ENOMEM;
+- decoder->norm = VIDEO_MODE_NTSC;
++ decoder->norm = V4L2_STD_NTSC;
+ decoder->input = 0;
+ decoder->enable = 1;
+- decoder->bright = 32768;
+- decoder->contrast = 32768;
+- decoder->hue = 32768;
+- decoder->sat = 32768;
+- decoder->initialized = 0;
+- i2c_set_clientdata(client, decoder);
+-
+- i = bt819_init(client);
++ decoder->bright = 0;
++ decoder->contrast = 0xd8; /* 100% of original signal */
++ decoder->hue = 0;
++ decoder->sat = 0xfe; /* 100% of original signal */
++
++ i = bt819_init(sd);
+ if (i < 0)
+- v4l_dbg(1, debug, client, "init status %d\n", i);
++ v4l2_dbg(1, debug, sd, "init status %d\n", i);
+ return 0;
+ }
+
+ static int bt819_remove(struct i2c_client *client)
+ {
+- kfree(i2c_get_clientdata(client));
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++
++ v4l2_device_unregister_subdev(sd);
++ kfree(to_bt819(sd));
+ return 0;
+ }
+
+@@ -496,8 +541,6 @@ MODULE_DEVICE_TABLE(i2c, bt819_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "bt819",
+- .driverid = I2C_DRIVERID_BT819,
+- .command = bt819_command,
+ .probe = bt819_probe,
+ .remove = bt819_remove,
+ .id_table = bt819_id,
+diff --git a/drivers/media/video/bt856.c b/drivers/media/video/bt856.c
+index 4213867..78db395 100644
+--- a/drivers/media/video/bt856.c
++++ b/drivers/media/video/bt856.c
+@@ -34,10 +34,10 @@
+ #include <asm/uaccess.h>
+ #include <linux/i2c.h>
+ #include <linux/i2c-id.h>
+-#include <linux/videodev.h>
+-#include <linux/video_encoder.h>
+-#include <media/v4l2-common.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <linux/videodev2.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/v4l2-i2c-drv.h>
+
+ MODULE_DESCRIPTION("Brooktree-856A video encoder driver");
+ MODULE_AUTHOR("Mike Bernson & Dave Perks");
+@@ -47,43 +47,46 @@ static int debug;
+ module_param(debug, int, 0);
+ MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
++
+ /* ----------------------------------------------------------------------- */
+
+ #define BT856_REG_OFFSET 0xDA
+ #define BT856_NR_REG 6
+
+ struct bt856 {
++ struct v4l2_subdev sd;
+ unsigned char reg[BT856_NR_REG];
+
+- int norm;
+- int enable;
++ v4l2_std_id norm;
+ };
+
++static inline struct bt856 *to_bt856(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct bt856, sd);
++}
++
+ /* ----------------------------------------------------------------------- */
+
+-static inline int bt856_write(struct i2c_client *client, u8 reg, u8 value)
++static inline int bt856_write(struct bt856 *encoder, u8 reg, u8 value)
+ {
+- struct bt856 *encoder = i2c_get_clientdata(client);
++ struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd);
+
+ encoder->reg[reg - BT856_REG_OFFSET] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+ }
+
+-static inline int bt856_setbit(struct i2c_client *client, u8 reg, u8 bit, u8 value)
++static inline int bt856_setbit(struct bt856 *encoder, u8 reg, u8 bit, u8 value)
+ {
+- struct bt856 *encoder = i2c_get_clientdata(client);
+-
+- return bt856_write(client, reg,
++ return bt856_write(encoder, reg,
+ (encoder->reg[reg - BT856_REG_OFFSET] & ~(1 << bit)) |
+ (value ? (1 << bit) : 0));
+ }
+
+-static void bt856_dump(struct i2c_client *client)
++static void bt856_dump(struct bt856 *encoder)
+ {
+ int i;
+- struct bt856 *encoder = i2c_get_clientdata(client);
+
+- v4l_info(client, "register dump:\n");
++ v4l2_info(&encoder->sd, "register dump:\n");
+ for (i = 0; i < BT856_NR_REG; i += 2)
+ printk(KERN_CONT " %02x", encoder->reg[i]);
+ printk(KERN_CONT "\n");
+@@ -91,153 +94,120 @@ static void bt856_dump(struct i2c_client *client)
+
+ /* ----------------------------------------------------------------------- */
+
+-static int bt856_command(struct i2c_client *client, unsigned cmd, void *arg)
++static int bt856_init(struct v4l2_subdev *sd, u32 arg)
+ {
+- struct bt856 *encoder = i2c_get_clientdata(client);
+-
+- switch (cmd) {
+- case 0:
+- /* This is just for testing!!! */
+- v4l_dbg(1, debug, client, "init\n");
+- bt856_write(client, 0xdc, 0x18);
+- bt856_write(client, 0xda, 0);
+- bt856_write(client, 0xde, 0);
+-
+- bt856_setbit(client, 0xdc, 3, 1);
+- //bt856_setbit(client, 0xdc, 6, 0);
+- bt856_setbit(client, 0xdc, 4, 1);
+-
+- switch (encoder->norm) {
+- case VIDEO_MODE_NTSC:
+- bt856_setbit(client, 0xdc, 2, 0);
+- break;
+-
+- case VIDEO_MODE_PAL:
+- bt856_setbit(client, 0xdc, 2, 1);
+- break;
+- }
+-
+- bt856_setbit(client, 0xdc, 1, 1);
+- bt856_setbit(client, 0xde, 4, 0);
+- bt856_setbit(client, 0xde, 3, 1);
+- if (debug != 0)
+- bt856_dump(client);
+- break;
+-
+- case ENCODER_GET_CAPABILITIES:
+- {
+- struct video_encoder_capability *cap = arg;
+-
+- v4l_dbg(1, debug, client, "get capabilities\n");
++ struct bt856 *encoder = to_bt856(sd);
++
++ /* This is just for testing!!! */
++ v4l2_dbg(1, debug, sd, "init\n");
++ bt856_write(encoder, 0xdc, 0x18);
++ bt856_write(encoder, 0xda, 0);
++ bt856_write(encoder, 0xde, 0);
++
++ bt856_setbit(encoder, 0xdc, 3, 1);
++ /*bt856_setbit(encoder, 0xdc, 6, 0);*/
++ bt856_setbit(encoder, 0xdc, 4, 1);
++
++ if (encoder->norm & V4L2_STD_NTSC)
++ bt856_setbit(encoder, 0xdc, 2, 0);
++ else
++ bt856_setbit(encoder, 0xdc, 2, 1);
++
++ bt856_setbit(encoder, 0xdc, 1, 1);
++ bt856_setbit(encoder, 0xde, 4, 0);
++ bt856_setbit(encoder, 0xde, 3, 1);
++ if (debug != 0)
++ bt856_dump(encoder);
++ return 0;
++}
+
+- cap->flags = VIDEO_ENCODER_PAL |
+- VIDEO_ENCODER_NTSC |
+- VIDEO_ENCODER_CCIR;
+- cap->inputs = 2;
+- cap->outputs = 1;
+- break;
+- }
++static int bt856_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
++{
++ struct bt856 *encoder = to_bt856(sd);
+
+- case ENCODER_SET_NORM:
+- {
+- int *iarg = arg;
+-
+- v4l_dbg(1, debug, client, "set norm %d\n", *iarg);
+-
+- switch (*iarg) {
+- case VIDEO_MODE_NTSC:
+- bt856_setbit(client, 0xdc, 2, 0);
+- break;
+-
+- case VIDEO_MODE_PAL:
+- bt856_setbit(client, 0xdc, 2, 1);
+- bt856_setbit(client, 0xda, 0, 0);
+- //bt856_setbit(client, 0xda, 0, 1);
+- break;
+-
+- default:
+- return -EINVAL;
+- }
+- encoder->norm = *iarg;
+- if (debug != 0)
+- bt856_dump(client);
+- break;
+- }
++ v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std);
+
+- case ENCODER_SET_INPUT:
+- {
+- int *iarg = arg;
+-
+- v4l_dbg(1, debug, client, "set input %d\n", *iarg);
+-
+- /* We only have video bus.
+- * iarg = 0: input is from bt819
+- * iarg = 1: input is from ZR36060 */
+- switch (*iarg) {
+- case 0:
+- bt856_setbit(client, 0xde, 4, 0);
+- bt856_setbit(client, 0xde, 3, 1);
+- bt856_setbit(client, 0xdc, 3, 1);
+- bt856_setbit(client, 0xdc, 6, 0);
+- break;
+- case 1:
+- bt856_setbit(client, 0xde, 4, 0);
+- bt856_setbit(client, 0xde, 3, 1);
+- bt856_setbit(client, 0xdc, 3, 1);
+- bt856_setbit(client, 0xdc, 6, 1);
+- break;
+- case 2: // Color bar
+- bt856_setbit(client, 0xdc, 3, 0);
+- bt856_setbit(client, 0xde, 4, 1);
+- break;
+- default:
+- return -EINVAL;
+- }
+-
+- if (debug != 0)
+- bt856_dump(client);
+- break;
++ if (std & V4L2_STD_NTSC) {
++ bt856_setbit(encoder, 0xdc, 2, 0);
++ } else if (std & V4L2_STD_PAL) {
++ bt856_setbit(encoder, 0xdc, 2, 1);
++ bt856_setbit(encoder, 0xda, 0, 0);
++ /*bt856_setbit(encoder, 0xda, 0, 1);*/
++ } else {
++ return -EINVAL;
+ }
++ encoder->norm = std;
++ if (debug != 0)
++ bt856_dump(encoder);
++ return 0;
++}
+
+- case ENCODER_SET_OUTPUT:
+- {
+- int *iarg = arg;
++static int bt856_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
++{
++ struct bt856 *encoder = to_bt856(sd);
+
+- v4l_dbg(1, debug, client, "set output %d\n", *iarg);
++ v4l2_dbg(1, debug, sd, "set input %d\n", route->input);
+
+- /* not much choice of outputs */
+- if (*iarg != 0)
+- return -EINVAL;
++ /* We only have video bus.
++ * route->input= 0: input is from bt819
++ * route->input= 1: input is from ZR36060 */
++ switch (route->input) {
++ case 0:
++ bt856_setbit(encoder, 0xde, 4, 0);
++ bt856_setbit(encoder, 0xde, 3, 1);
++ bt856_setbit(encoder, 0xdc, 3, 1);
++ bt856_setbit(encoder, 0xdc, 6, 0);
+ break;
+- }
+-
+- case ENCODER_ENABLE_OUTPUT:
+- {
+- int *iarg = arg;
+-
+- encoder->enable = !!*iarg;
+-
+- v4l_dbg(1, debug, client, "enable output %d\n", encoder->enable);
++ case 1:
++ bt856_setbit(encoder, 0xde, 4, 0);
++ bt856_setbit(encoder, 0xde, 3, 1);
++ bt856_setbit(encoder, 0xdc, 3, 1);
++ bt856_setbit(encoder, 0xdc, 6, 1);
++ break;
++ case 2: /* Color bar */
++ bt856_setbit(encoder, 0xdc, 3, 0);
++ bt856_setbit(encoder, 0xde, 4, 1);
+ break;
+- }
+-
+ default:
+ return -EINVAL;
+ }
+
++ if (debug != 0)
++ bt856_dump(encoder);
+ return 0;
+ }
+
++static int bt856_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT856, 0);
++}
++
+ /* ----------------------------------------------------------------------- */
+
+-static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END };
++static const struct v4l2_subdev_core_ops bt856_core_ops = {
++ .g_chip_ident = bt856_g_chip_ident,
++ .init = bt856_init,
++};
++
++static const struct v4l2_subdev_video_ops bt856_video_ops = {
++ .s_std_output = bt856_s_std_output,
++ .s_routing = bt856_s_routing,
++};
+
+-I2C_CLIENT_INSMOD;
++static const struct v4l2_subdev_ops bt856_ops = {
++ .core = &bt856_core_ops,
++ .video = &bt856_video_ops,
++};
++
++/* ----------------------------------------------------------------------- */
+
+ static int bt856_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+ {
+ struct bt856 *encoder;
++ struct v4l2_subdev *sd;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+@@ -249,41 +219,38 @@ static int bt856_probe(struct i2c_client *client,
+ encoder = kzalloc(sizeof(struct bt856), GFP_KERNEL);
+ if (encoder == NULL)
+ return -ENOMEM;
+- encoder->norm = VIDEO_MODE_NTSC;
+- encoder->enable = 1;
+- i2c_set_clientdata(client, encoder);
++ sd = &encoder->sd;
++ v4l2_i2c_subdev_init(sd, client, &bt856_ops);
++ encoder->norm = V4L2_STD_NTSC;
+
+- bt856_write(client, 0xdc, 0x18);
+- bt856_write(client, 0xda, 0);
+- bt856_write(client, 0xde, 0);
++ bt856_write(encoder, 0xdc, 0x18);
++ bt856_write(encoder, 0xda, 0);
++ bt856_write(encoder, 0xde, 0);
+
+- bt856_setbit(client, 0xdc, 3, 1);
+- //bt856_setbit(client, 0xdc, 6, 0);
+- bt856_setbit(client, 0xdc, 4, 1);
++ bt856_setbit(encoder, 0xdc, 3, 1);
++ /*bt856_setbit(encoder, 0xdc, 6, 0);*/
++ bt856_setbit(encoder, 0xdc, 4, 1);
+
+- switch (encoder->norm) {
++ if (encoder->norm & V4L2_STD_NTSC)
++ bt856_setbit(encoder, 0xdc, 2, 0);
++ else
++ bt856_setbit(encoder, 0xdc, 2, 1);
+
+- case VIDEO_MODE_NTSC:
+- bt856_setbit(client, 0xdc, 2, 0);
+- break;
+-
+- case VIDEO_MODE_PAL:
+- bt856_setbit(client, 0xdc, 2, 1);
+- break;
+- }
+-
+- bt856_setbit(client, 0xdc, 1, 1);
+- bt856_setbit(client, 0xde, 4, 0);
+- bt856_setbit(client, 0xde, 3, 1);
++ bt856_setbit(encoder, 0xdc, 1, 1);
++ bt856_setbit(encoder, 0xde, 4, 0);
++ bt856_setbit(encoder, 0xde, 3, 1);
+
+ if (debug != 0)
+- bt856_dump(client);
++ bt856_dump(encoder);
+ return 0;
+ }
+
+ static int bt856_remove(struct i2c_client *client)
+ {
+- kfree(i2c_get_clientdata(client));
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++
++ v4l2_device_unregister_subdev(sd);
++ kfree(to_bt856(sd));
+ return 0;
+ }
+
+@@ -295,8 +262,6 @@ MODULE_DEVICE_TABLE(i2c, bt856_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "bt856",
+- .driverid = I2C_DRIVERID_BT856,
+- .command = bt856_command,
+ .probe = bt856_probe,
+ .remove = bt856_remove,
+ .id_table = bt856_id,
+diff --git a/drivers/media/video/bt866.c b/drivers/media/video/bt866.c
+index 596f9e2..350cae4 100644
+--- a/drivers/media/video/bt866.c
++++ b/drivers/media/video/bt866.c
+@@ -34,10 +34,10 @@
+ #include <asm/uaccess.h>
+ #include <linux/i2c.h>
+ #include <linux/i2c-id.h>
+-#include <linux/videodev.h>
+-#include <linux/video_encoder.h>
+-#include <media/v4l2-common.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <linux/videodev2.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/v4l2-i2c-drv.h>
+
+ MODULE_DESCRIPTION("Brooktree-866 video encoder driver");
+ MODULE_AUTHOR("Mike Bernson & Dave Perks");
+@@ -47,22 +47,22 @@ static int debug;
+ module_param(debug, int, 0);
+ MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
++
+ /* ----------------------------------------------------------------------- */
+
+ struct bt866 {
++ struct v4l2_subdev sd;
+ u8 reg[256];
+-
+- int norm;
+- int enable;
+- int bright;
+- int contrast;
+- int hue;
+- int sat;
+ };
+
+-static int bt866_write(struct i2c_client *client, u8 subaddr, u8 data)
++static inline struct bt866 *to_bt866(struct v4l2_subdev *sd)
+ {
+- struct bt866 *encoder = i2c_get_clientdata(client);
++ return container_of(sd, struct bt866, sd);
++}
++
++static int bt866_write(struct bt866 *encoder, u8 subaddr, u8 data)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(&encoder->sd);
+ u8 buffer[2];
+ int err;
+
+@@ -89,163 +89,120 @@ static int bt866_write(struct i2c_client *client, u8 subaddr, u8 data)
+ return 0;
+ }
+
+-static int bt866_command(struct i2c_client *client, unsigned cmd, void *arg)
++static int bt866_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
+ {
+- struct bt866 *encoder = i2c_get_clientdata(client);
+-
+- switch (cmd) {
+- case ENCODER_GET_CAPABILITIES:
+- {
+- struct video_encoder_capability *cap = arg;
+-
+- v4l_dbg(1, debug, client, "get capabilities\n");
+-
+- cap->flags
+- = VIDEO_ENCODER_PAL
+- | VIDEO_ENCODER_NTSC
+- | VIDEO_ENCODER_CCIR;
+- cap->inputs = 2;
+- cap->outputs = 1;
+- break;
+- }
+-
+- case ENCODER_SET_NORM:
+- {
+- int *iarg = arg;
+-
+- v4l_dbg(1, debug, client, "set norm %d\n", *iarg);
+-
+- switch (*iarg) {
+- case VIDEO_MODE_NTSC:
+- break;
+-
+- case VIDEO_MODE_PAL:
+- break;
+-
+- default:
+- return -EINVAL;
+- }
+- encoder->norm = *iarg;
+- break;
+- }
+-
+- case ENCODER_SET_INPUT:
+- {
+- int *iarg = arg;
+- static const __u8 init[] = {
+- 0xc8, 0xcc, /* CRSCALE */
+- 0xca, 0x91, /* CBSCALE */
+- 0xcc, 0x24, /* YC16 | OSDNUM */
+- 0xda, 0x00, /* */
+- 0xdc, 0x24, /* SETMODE | PAL */
+- 0xde, 0x02, /* EACTIVE */
+-
+- /* overlay colors */
+- 0x70, 0xEB, 0x90, 0x80, 0xB0, 0x80, /* white */
+- 0x72, 0xA2, 0x92, 0x8E, 0xB2, 0x2C, /* yellow */
+- 0x74, 0x83, 0x94, 0x2C, 0xB4, 0x9C, /* cyan */
+- 0x76, 0x70, 0x96, 0x3A, 0xB6, 0x48, /* green */
+- 0x78, 0x54, 0x98, 0xC6, 0xB8, 0xB8, /* magenta */
+- 0x7A, 0x41, 0x9A, 0xD4, 0xBA, 0x64, /* red */
+- 0x7C, 0x23, 0x9C, 0x72, 0xBC, 0xD4, /* blue */
+- 0x7E, 0x10, 0x9E, 0x80, 0xBE, 0x80, /* black */
+-
+- 0x60, 0xEB, 0x80, 0x80, 0xc0, 0x80, /* white */
+- 0x62, 0xA2, 0x82, 0x8E, 0xc2, 0x2C, /* yellow */
+- 0x64, 0x83, 0x84, 0x2C, 0xc4, 0x9C, /* cyan */
+- 0x66, 0x70, 0x86, 0x3A, 0xc6, 0x48, /* green */
+- 0x68, 0x54, 0x88, 0xC6, 0xc8, 0xB8, /* magenta */
+- 0x6A, 0x41, 0x8A, 0xD4, 0xcA, 0x64, /* red */
+- 0x6C, 0x23, 0x8C, 0x72, 0xcC, 0xD4, /* blue */
+- 0x6E, 0x10, 0x8E, 0x80, 0xcE, 0x80, /* black */
+- };
+- int i;
+- u8 val;
+-
+- for (i = 0; i < ARRAY_SIZE(init) / 2; i += 2)
+- bt866_write(client, init[i], init[i+1]);
+-
+- val = encoder->reg[0xdc];
+-
+- if (*iarg == 0)
+- val |= 0x40; /* CBSWAP */
+- else
+- val &= ~0x40; /* !CBSWAP */
+-
+- bt866_write(client, 0xdc, val);
+-
+- val = encoder->reg[0xcc];
+- if (*iarg == 2)
+- val |= 0x01; /* OSDBAR */
+- else
+- val &= ~0x01; /* !OSDBAR */
+- bt866_write(client, 0xcc, val);
+-
+- v4l_dbg(1, debug, client, "set input %d\n", *iarg);
+-
+- switch (*iarg) {
+- case 0:
+- break;
+- case 1:
+- break;
+- default:
+- return -EINVAL;
+- }
+- break;
+- }
+-
+- case ENCODER_SET_OUTPUT:
+- {
+- int *iarg = arg;
+-
+- v4l_dbg(1, debug, client, "set output %d\n", *iarg);
+-
+- /* not much choice of outputs */
+- if (*iarg != 0)
+- return -EINVAL;
+- break;
+- }
+-
+- case ENCODER_ENABLE_OUTPUT:
+- {
+- int *iarg = arg;
+- encoder->enable = !!*iarg;
++ v4l2_dbg(1, debug, sd, "set norm %llx\n", (unsigned long long)std);
+
+- v4l_dbg(1, debug, client, "enable output %d\n", encoder->enable);
+- break;
+- }
+-
+- case 4711:
+- {
+- int *iarg = arg;
+- __u8 val;
+-
+- v4l_dbg(1, debug, client, "square %d\n", *iarg);
++ /* Only PAL supported by this driver at the moment! */
++ if (!(std & V4L2_STD_NTSC))
++ return -EINVAL;
++ return 0;
++}
+
+- val = encoder->reg[0xdc];
+- if (*iarg)
+- val |= 1; /* SQUARE */
+- else
+- val &= ~1; /* !SQUARE */
+- bt866_write(client, 0xdc, val);
++static int bt866_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
++{
++ static const __u8 init[] = {
++ 0xc8, 0xcc, /* CRSCALE */
++ 0xca, 0x91, /* CBSCALE */
++ 0xcc, 0x24, /* YC16 | OSDNUM */
++ 0xda, 0x00, /* */
++ 0xdc, 0x24, /* SETMODE | PAL */
++ 0xde, 0x02, /* EACTIVE */
++
++ /* overlay colors */
++ 0x70, 0xEB, 0x90, 0x80, 0xB0, 0x80, /* white */
++ 0x72, 0xA2, 0x92, 0x8E, 0xB2, 0x2C, /* yellow */
++ 0x74, 0x83, 0x94, 0x2C, 0xB4, 0x9C, /* cyan */
++ 0x76, 0x70, 0x96, 0x3A, 0xB6, 0x48, /* green */
++ 0x78, 0x54, 0x98, 0xC6, 0xB8, 0xB8, /* magenta */
++ 0x7A, 0x41, 0x9A, 0xD4, 0xBA, 0x64, /* red */
++ 0x7C, 0x23, 0x9C, 0x72, 0xBC, 0xD4, /* blue */
++ 0x7E, 0x10, 0x9E, 0x80, 0xBE, 0x80, /* black */
++
++ 0x60, 0xEB, 0x80, 0x80, 0xc0, 0x80, /* white */
++ 0x62, 0xA2, 0x82, 0x8E, 0xc2, 0x2C, /* yellow */
++ 0x64, 0x83, 0x84, 0x2C, 0xc4, 0x9C, /* cyan */
++ 0x66, 0x70, 0x86, 0x3A, 0xc6, 0x48, /* green */
++ 0x68, 0x54, 0x88, 0xC6, 0xc8, 0xB8, /* magenta */
++ 0x6A, 0x41, 0x8A, 0xD4, 0xcA, 0x64, /* red */
++ 0x6C, 0x23, 0x8C, 0x72, 0xcC, 0xD4, /* blue */
++ 0x6E, 0x10, 0x8E, 0x80, 0xcE, 0x80, /* black */
++ };
++ struct bt866 *encoder = to_bt866(sd);
++ u8 val;
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(init) / 2; i += 2)
++ bt866_write(encoder, init[i], init[i+1]);
++
++ val = encoder->reg[0xdc];
++
++ if (route->input == 0)
++ val |= 0x40; /* CBSWAP */
++ else
++ val &= ~0x40; /* !CBSWAP */
++
++ bt866_write(encoder, 0xdc, val);
++
++ val = encoder->reg[0xcc];
++ if (route->input == 2)
++ val |= 0x01; /* OSDBAR */
++ else
++ val &= ~0x01; /* !OSDBAR */
++ bt866_write(encoder, 0xcc, val);
++
++ v4l2_dbg(1, debug, sd, "set input %d\n", route->input);
++
++ switch (route->input) {
++ case 0:
++ case 1:
++ case 2:
+ break;
+- }
+-
+ default:
+ return -EINVAL;
+ }
+-
+ return 0;
+ }
+
+-static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END };
++#if 0
++/* Code to setup square pixels, might be of some use in the future,
++ but is currently unused. */
++ val = encoder->reg[0xdc];
++ if (*iarg)
++ val |= 1; /* SQUARE */
++ else
++ val &= ~1; /* !SQUARE */
++ bt866_write(client, 0xdc, val);
++#endif
++
++static int bt866_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_BT866, 0);
++}
++
++/* ----------------------------------------------------------------------- */
++
++static const struct v4l2_subdev_core_ops bt866_core_ops = {
++ .g_chip_ident = bt866_g_chip_ident,
++};
++
++static const struct v4l2_subdev_video_ops bt866_video_ops = {
++ .s_std_output = bt866_s_std_output,
++ .s_routing = bt866_s_routing,
++};
+
+-I2C_CLIENT_INSMOD;
++static const struct v4l2_subdev_ops bt866_ops = {
++ .core = &bt866_core_ops,
++ .video = &bt866_video_ops,
++};
+
+ static int bt866_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+ {
+ struct bt866 *encoder;
++ struct v4l2_subdev *sd;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+@@ -253,20 +210,18 @@ static int bt866_probe(struct i2c_client *client,
+ encoder = kzalloc(sizeof(*encoder), GFP_KERNEL);
+ if (encoder == NULL)
+ return -ENOMEM;
+-
+- i2c_set_clientdata(client, encoder);
++ sd = &encoder->sd;
++ v4l2_i2c_subdev_init(sd, client, &bt866_ops);
+ return 0;
+ }
+
+ static int bt866_remove(struct i2c_client *client)
+ {
+- kfree(i2c_get_clientdata(client));
+- return 0;
+-}
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+-static int bt866_legacy_probe(struct i2c_adapter *adapter)
+-{
+- return adapter->id == I2C_HW_B_ZR36067;
++ v4l2_device_unregister_subdev(sd);
++ kfree(to_bt866(sd));
++ return 0;
+ }
+
+ static const struct i2c_device_id bt866_id[] = {
+@@ -277,10 +232,7 @@ MODULE_DEVICE_TABLE(i2c, bt866_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "bt866",
+- .driverid = I2C_DRIVERID_BT866,
+- .command = bt866_command,
+ .probe = bt866_probe,
+ .remove = bt866_remove,
+- .legacy_probe = bt866_legacy_probe,
+ .id_table = bt866_id,
+ };
+diff --git a/drivers/media/video/bt8xx/Kconfig b/drivers/media/video/bt8xx/Kconfig
+index ce71e8e..3077c45 100644
+--- a/drivers/media/video/bt8xx/Kconfig
++++ b/drivers/media/video/bt8xx/Kconfig
+@@ -10,7 +10,7 @@ config VIDEO_BT848
+ select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_TVAUDIO if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_TDA7432 if VIDEO_HELPER_CHIPS_AUTO
+- select VIDEO_TDA9875 if VIDEO_HELPER_CHIPS_AUTO
++ select VIDEO_SAA6588 if VIDEO_HELPER_CHIPS_AUTO
+ ---help---
+ Support for BT848 based frame grabber/overlay boards. This includes
+ the Miro, Hauppauge and STB boards. Please read the material in
+diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c
+index d24dcc0..b9c3ba5 100644
+--- a/drivers/media/video/bt8xx/bttv-cards.c
++++ b/drivers/media/video/bt8xx/bttv-cards.c
+@@ -73,6 +73,11 @@ static void sigmaSQ_muxsel(struct bttv *btv, unsigned int input);
+
+ static void geovision_muxsel(struct bttv *btv, unsigned int input);
+
++static void phytec_muxsel(struct bttv *btv, unsigned int input);
++
++static void gv800s_muxsel(struct bttv *btv, unsigned int input);
++static void gv800s_init(struct bttv *btv);
++
+ static int terratec_active_radio_upgrade(struct bttv *btv);
+ static int tea5757_read(struct bttv *btv);
+ static int tea5757_write(struct bttv *btv, int value);
+@@ -91,12 +96,10 @@ static unsigned int pll[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+ static unsigned int tuner[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+ static unsigned int svhs[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
+ static unsigned int remote[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = UNSET };
++static unsigned int audiodev[BTTV_MAX];
++static unsigned int saa6588[BTTV_MAX];
+ static struct bttv *master[BTTV_MAX] = { [ 0 ... (BTTV_MAX-1) ] = NULL };
+-#ifdef MODULE
+-static unsigned int autoload = 1;
+-#else
+-static unsigned int autoload;
+-#endif
++static unsigned int autoload = UNSET;
+ static unsigned int gpiomask = UNSET;
+ static unsigned int audioall = UNSET;
+ static unsigned int audiomux[5] = { [ 0 ... 4 ] = UNSET };
+@@ -115,6 +118,7 @@ module_param_array(pll, int, NULL, 0444);
+ module_param_array(tuner, int, NULL, 0444);
+ module_param_array(svhs, int, NULL, 0444);
+ module_param_array(remote, int, NULL, 0444);
++module_param_array(audiodev, int, NULL, 0444);
+ module_param_array(audiomux, int, NULL, 0444);
+
+ MODULE_PARM_DESC(triton1,"set ETBF pci config bit "
+@@ -125,7 +129,14 @@ MODULE_PARM_DESC(latency,"pci latency timer");
+ MODULE_PARM_DESC(card,"specify TV/grabber card model, see CARDLIST file for a list");
+ MODULE_PARM_DESC(pll,"specify installed crystal (0=none, 28=28 MHz, 35=35 MHz)");
+ MODULE_PARM_DESC(tuner,"specify installed tuner type");
+-MODULE_PARM_DESC(autoload,"automatically load i2c modules like tuner.o, default is 1 (yes)");
++MODULE_PARM_DESC(autoload, "obsolete option, please do not use anymore");
++MODULE_PARM_DESC(audiodev, "specify audio device:\n"
++ "\t\t-1 = no audio\n"
++ "\t\t 0 = autodetect (default)\n"
++ "\t\t 1 = msp3400\n"
++ "\t\t 2 = tda7432\n"
++ "\t\t 3 = tvaudio");
++MODULE_PARM_DESC(saa6588, "if 1, then load the saa6588 RDS module, default (0) is to use the card definition.");
+ MODULE_PARM_DESC(no_overlay,"allow override overlay default (0 disables, 1 enables)"
+ " [some VIA/SIS chipsets are known to have problem with overlay]");
+
+@@ -246,6 +257,10 @@ static struct CARD {
+ { 0xa182ff0d, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff0e, BTTV_BOARD_IVC120, "IVC-120G" },
+ { 0xa182ff0f, BTTV_BOARD_IVC120, "IVC-120G" },
++ { 0xf0500000, BTTV_BOARD_IVCE8784, "IVCE-8784" },
++ { 0xf0500001, BTTV_BOARD_IVCE8784, "IVCE-8784" },
++ { 0xf0500002, BTTV_BOARD_IVCE8784, "IVCE-8784" },
++ { 0xf0500003, BTTV_BOARD_IVCE8784, "IVCE-8784" },
+
+ { 0x41424344, BTTV_BOARD_GRANDTEC, "GrandTec Multi Capture" },
+ { 0x01020304, BTTV_BOARD_XGUARD, "Grandtec Grand X-Guard" },
+@@ -289,6 +304,8 @@ static struct CARD {
+ /* Duplicate PCI ID, reconfigure for this board during the eeprom read.
+ * { 0x13eb0070, BTTV_BOARD_HAUPPAUGE_IMPACTVCB, "Hauppauge ImpactVCB" }, */
+
++ { 0x109e036e, BTTV_BOARD_CONCEPTRONIC_CTVFMI2, "Conceptronic CTVFMi v2"},
++
+ /* DVB cards (using pci function .1 for mpeg data xfer) */
+ { 0x001c11bd, BTTV_BOARD_PINNACLESAT, "Pinnacle PCTV Sat" },
+ { 0x01010071, BTTV_BOARD_NEBULA_DIGITV, "Nebula Electronics DigiTV" },
+@@ -305,6 +322,20 @@ static struct CARD {
+ { 0xd200dbc0, BTTV_BOARD_DVICO_FUSIONHDTV_2, "DViCO FusionHDTV 2" },
+ { 0x763c008a, BTTV_BOARD_GEOVISION_GV600, "GeoVision GV-600" },
+ { 0x18011000, BTTV_BOARD_ENLTV_FM_2, "Encore ENL TV-FM-2" },
++ { 0x763d800a, BTTV_BOARD_GEOVISION_GV800S, "GeoVision GV-800(S) (master)" },
++ { 0x763d800b, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" },
++ { 0x763d800c, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" },
++ { 0x763d800d, BTTV_BOARD_GEOVISION_GV800S_SL, "GeoVision GV-800(S) (slave)" },
++
++ { 0x15401830, BTTV_BOARD_PV183, "Provideo PV183-1" },
++ { 0x15401831, BTTV_BOARD_PV183, "Provideo PV183-2" },
++ { 0x15401832, BTTV_BOARD_PV183, "Provideo PV183-3" },
++ { 0x15401833, BTTV_BOARD_PV183, "Provideo PV183-4" },
++ { 0x15401834, BTTV_BOARD_PV183, "Provideo PV183-5" },
++ { 0x15401835, BTTV_BOARD_PV183, "Provideo PV183-6" },
++ { 0x15401836, BTTV_BOARD_PV183, "Provideo PV183-7" },
++ { 0x15401837, BTTV_BOARD_PV183, "Provideo PV183-8" },
++
+ { 0, -1, NULL }
+ };
+
+@@ -316,59 +347,50 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_UNKNOWN] = {
+ .name = " *** UNKNOWN/GENERIC *** ",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
+ .svhs = 2,
+- .muxsel = { 2, 3, 1, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MIRO] = {
+ .name = "MIRO PCTV",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 15,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 2, 0, 0, 0 },
+ .gpiomute = 10,
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_HAUPPAUGE] = {
+ .name = "Hauppauge (bt848)",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 7,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 3 },
+ .gpiomute = 4,
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_STB] = {
+ .name = "STB, Gateway P/N 6000699 (bt848)",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 7,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 4, 0, 2, 3 },
+ .gpiomute = 1,
+ .no_msp34xx = 1,
+ .needs_tvaudio = 1,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ },
+@@ -377,202 +399,177 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_INTEL] = {
+ .name = "Intel Create and Share PCI/ Smart Video Recorder III",
+ .video_inputs = 4,
+- .audio_inputs = 0,
+- .tuner = UNSET,
++ /* .audio_inputs= 0, */
+ .svhs = 2,
+ .gpiomask = 0,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 0,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_DIAMOND] = {
+ .name = "Diamond DTV2000",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 3,
+- .muxsel = { 2, 3, 1, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 0, 1, 0, 1 },
+ .gpiomute = 3,
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_AVERMEDIA] = {
+ .name = "AVerMedia TVPhone",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 3,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomask = 0x0f,
+ .gpiomux = { 0x0c, 0x04, 0x08, 0x04 },
+ /* 0x04 for some cards ?? */
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .audio_mode_gpio= avermedia_tvphone_audio,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_MATRIX_VISION] = {
+ .name = "MATRIX-Vision MV-Delta",
+ .video_inputs = 5,
+- .audio_inputs = 1,
+- .tuner = UNSET,
++ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .gpiomask = 0,
+- .muxsel = { 2, 3, 1, 0, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0, 0),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 1,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x08 ---------------------------------- */
+ [BTTV_BOARD_FLYVIDEO] = {
+ .name = "Lifeview FlyVideo II (Bt848) LR26 / MAXI TV Video PCI2 LR26",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xc00,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0xc00, 0x800, 0x400 },
+ .gpiomute = 0xc00,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TURBOTV] = {
+ .name = "IMS/IXmicro TurboTV",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 3,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 1, 1, 2, 3 },
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_HAUPPAUGE878] = {
+ .name = "Hauppauge (bt878)",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x0f, /* old: 7 */
+- .muxsel = { 2, 0, 1, 1 },
++ .muxsel = MUXSEL(2, 0, 1, 1),
+ .gpiomux = { 0, 1, 2, 3 },
+ .gpiomute = 4,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MIROPRO] = {
+ .name = "MIRO PCTV pro",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x3014f,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x20001,0x10001, 0, 0 },
+ .gpiomute = 10,
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x0c ---------------------------------- */
+ [BTTV_BOARD_ADSTECH_TV] = {
+ .name = "ADS Technologies Channel Surfer TV (bt848)",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 15,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 13, 14, 11, 7 },
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_AVERMEDIA98] = {
+ .name = "AVerMedia TVCapture 98",
+ .video_inputs = 3,
+- .audio_inputs = 4,
+- .tuner = 0,
++ /* .audio_inputs= 4, */
+ .svhs = 2,
+ .gpiomask = 15,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 13, 14, 11, 7 },
+ .needs_tvaudio = 1,
+ .msp34xx_alt = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .audio_mode_gpio= avermedia_tv_stereo_audio,
+ .no_gpioirq = 1,
+ },
+ [BTTV_BOARD_VHX] = {
+ .name = "Aimslab Video Highway Xtreme (VHX)",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 7,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 2, 1, 3 }, /* old: {0, 1, 2, 3, 4} */
+ .gpiomute = 4,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_ZOLTRIX] = {
+ .name = "Zoltrix TV-Max",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 15,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0, 1, 0 },
+ .gpiomute = 10,
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x10 ---------------------------------- */
+ [BTTV_BOARD_PIXVIEWPLAYTV] = {
+ .name = "Prolink Pixelview PlayTV (bt878)",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x01fe00,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ /* 2003-10-20 by "Anton A. Arapov" <arapov@mail.ru> */
+ .gpiomux = { 0x001e00, 0, 0x018000, 0x014000 },
+ .gpiomute = 0x002000,
+@@ -580,194 +577,170 @@ struct tvcard bttv_tvcards[] = {
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_WINVIEW_601] = {
+ .name = "Leadtek WinView 601",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x8300f8,
+- .muxsel = { 2, 3, 1, 1,0 },
++ .muxsel = MUXSEL(2, 3, 1, 1, 0),
+ .gpiomux = { 0x4fa007,0xcfa007,0xcfa007,0xcfa007 },
+ .gpiomute = 0xcfa007,
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .volume_gpio = winview_volume,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_AVEC_INTERCAP] = {
+ .name = "AVEC Intercapture",
+ .video_inputs = 3,
+- .audio_inputs = 2,
+- .tuner = 0,
++ /* .audio_inputs= 2, */
+ .svhs = 2,
+ .gpiomask = 0,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 1, 0, 0, 0 },
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_LIFE_FLYKIT] = {
+ .name = "Lifeview FlyVideo II EZ /FlyKit LR38 Bt848 (capture only)",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = UNSET,
+- .svhs = UNSET,
++ /* .audio_inputs= 1, */
++ .svhs = NO_SVHS,
+ .gpiomask = 0x8dff00,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0 },
+ .no_msp34xx = 1,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x14 ---------------------------------- */
+ [BTTV_BOARD_CEI_RAFFLES] = {
+ .name = "CEI Raffles Card",
+ .video_inputs = 3,
+- .audio_inputs = 3,
+- .tuner = 0,
++ /* .audio_inputs= 3, */
+ .svhs = 2,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_CONFERENCETV] = {
+ .name = "Lifeview FlyVideo 98/ Lucky Star Image World ConferenceTV LR50",
+ .video_inputs = 4,
+- .audio_inputs = 2, /* tuner, line in */
+- .tuner = 0,
++ /* .audio_inputs= 2, tuner, line in */
+ .svhs = 2,
+ .gpiomask = 0x1800,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0x800, 0x1000, 0x1000 },
+ .gpiomute = 0x1800,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_PHOEBE_TVMAS] = {
+ .name = "Askey CPH050/ Phoebe Tv Master + FM",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xc00,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 0x800, 0x400 },
+ .gpiomute = 0xc00,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MODTEC_205] = {
+ .name = "Modular Technology MM201/MM202/MM205/MM210/MM215 PCTV, bt878",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
+- .svhs = UNSET,
++ /* .audio_inputs= 1, */
++ .svhs = NO_SVHS,
++ .has_dig_in = 1,
+ .gpiomask = 7,
+- .muxsel = { 2, 3, -1 },
+- .digital_mode = DIGITAL_MODE_CAMERA,
++ .muxsel = MUXSEL(2, 3, 0), /* input 2 is digital */
++ /* .digital_mode= DIGITAL_MODE_CAMERA, */
+ .gpiomux = { 0, 0, 0, 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ALPS_TSBB5_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x18 ---------------------------------- */
+ [BTTV_BOARD_MAGICTVIEW061] = {
+ .name = "Askey CPH05X/06X (bt878) [many vendors]",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xe00,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = {0x400, 0x400, 0x400, 0x400 },
+ .gpiomute = 0xc00,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_VOBIS_BOOSTAR] = {
+ .name = "Terratec TerraTV+ Version 1.0 (Bt848)/ Terra TValue Version 1.0/ Vobis TV-Boostar",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x1f0fff,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x20000, 0x30000, 0x10000, 0 },
+ .gpiomute = 0x40000,
+ .needs_tvaudio = 0,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .audio_mode_gpio= terratv_audio,
+ },
+ [BTTV_BOARD_HAUPPAUG_WCAM] = {
+ .name = "Hauppauge WinCam newer (bt878)",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .gpiomask = 7,
+- .muxsel = { 2, 0, 1, 1 },
++ .muxsel = MUXSEL(2, 0, 1, 1),
+ .gpiomux = { 0, 1, 2, 3 },
+ .gpiomute = 4,
+ .needs_tvaudio = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MAXI] = {
+ .name = "Lifeview FlyVideo 98/ MAXI TV Video PCI2 LR50",
+ .video_inputs = 4,
+- .audio_inputs = 2,
+- .tuner = 0,
++ /* .audio_inputs= 2, */
+ .svhs = 2,
+ .gpiomask = 0x1800,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0x800, 0x1000, 0x1000 },
+ .gpiomute = 0x1800,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_SECAM,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x1c ---------------------------------- */
+ [BTTV_BOARD_TERRATV] = {
+ .name = "Terratec TerraTV+ Version 1.1 (bt878)",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x1f0fff,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x20000, 0x30000, 0x10000, 0x00000 },
+ .gpiomute = 0x40000,
+ .needs_tvaudio = 0,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .audio_mode_gpio= terratv_audio,
+ /* GPIO wiring:
+ External 20 pin connector (for Active Radio Upgrade board)
+@@ -805,87 +778,77 @@ struct tvcard bttv_tvcards[] = {
+ /* Jannik Fritsch <jannik@techfak.uni-bielefeld.de> */
+ .name = "Imagenation PXC200",
+ .video_inputs = 5,
+- .audio_inputs = 1,
+- .tuner = UNSET,
++ /* .audio_inputs= 1, */
+ .svhs = 1, /* was: 4 */
+ .gpiomask = 0,
+- .muxsel = { 2, 3, 1, 0, 0},
++ .muxsel = MUXSEL(2, 3, 1, 0, 0),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 1,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .muxsel_hook = PXC200_muxsel,
+
+ },
+ [BTTV_BOARD_FLYVIDEO_98] = {
+ .name = "Lifeview FlyVideo 98 LR50",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x1800, /* 0x8dfe00 */
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0x0800, 0x1000, 0x1000 },
+ .gpiomute = 0x1800,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_IPROTV] = {
+ .name = "Formac iProTV, Formac ProTV I (bt848)",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .gpiomask = 1,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 1, 0, 0, 0 },
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x20 ---------------------------------- */
+ [BTTV_BOARD_INTEL_C_S_PCI] = {
+ .name = "Intel Create and Share PCI/ Smart Video Recorder III",
+ .video_inputs = 4,
+- .audio_inputs = 0,
+- .tuner = UNSET,
++ /* .audio_inputs= 0, */
+ .svhs = 2,
+ .gpiomask = 0,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 0,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TERRATVALUE] = {
+ .name = "Terratec TerraTValue Version Bt878",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xffff00,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x500, 0, 0x300, 0x900 },
+ .gpiomute = 0x900,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_WINFAST2000] = {
+ .name = "Leadtek WinFast 2000/ WinFast 2000 XP",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+- .muxsel = { 2, 3, 1, 1, 0 }, /* TV, CVid, SVid, CVid over SVid connector */
++ /* TV, CVid, SVid, CVid over SVid connector */
++ .muxsel = MUXSEL(2, 3, 1, 1, 0),
+ /* Alexander Varakin <avarakin@hotmail.com> [stereo version] */
+ .gpiomask = 0xb33000,
+ .gpiomux = { 0x122000,0x1000,0x0000,0x620000 },
+@@ -906,217 +869,191 @@ struct tvcard bttv_tvcards[] = {
+ .has_radio = 1,
+ .tuner_type = TUNER_PHILIPS_PAL, /* default for now, gpio reads BFFF06 for Pal bg+dk */
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .audio_mode_gpio= winfast2000_audio,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_CHRONOS_VS2] = {
+ .name = "Lifeview FlyVideo 98 LR50 / Chronos Video Shuttle II",
+ .video_inputs = 4,
+- .audio_inputs = 3,
+- .tuner = 0,
++ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .gpiomask = 0x1800,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0x800, 0x1000, 0x1000 },
+ .gpiomute = 0x1800,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x24 ---------------------------------- */
+ [BTTV_BOARD_TYPHOON_TVIEW] = {
+ .name = "Lifeview FlyVideo 98FM LR50 / Typhoon TView TV/FM Tuner",
+ .video_inputs = 4,
+- .audio_inputs = 3,
+- .tuner = 0,
++ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .gpiomask = 0x1800,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0x800, 0x1000, 0x1000 },
+ .gpiomute = 0x1800,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_PXELVWPLTVPRO] = {
+ .name = "Prolink PixelView PlayTV pro",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xff,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x21, 0x20, 0x24, 0x2c },
+ .gpiomute = 0x29,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MAGICTVIEW063] = {
+ .name = "Askey CPH06X TView99",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x551e00,
+- .muxsel = { 2, 3, 1, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 0x551400, 0x551200, 0, 0 },
+ .gpiomute = 0x551c00,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_PINNACLE] = {
+ .name = "Pinnacle PCTV Studio/Rave",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x03000F,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 2, 0xd0001, 0, 0 },
+ .gpiomute = 1,
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x28 ---------------------------------- */
+ [BTTV_BOARD_STB2] = {
+ .name = "STB TV PCI FM, Gateway P/N 6000704 (bt878), 3Dfx VoodooTV 100",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 7,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 4, 0, 2, 3 },
+ .gpiomute = 1,
+ .no_msp34xx = 1,
+ .needs_tvaudio = 1,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_AVPHONE98] = {
+ .name = "AVerMedia TVPhone 98",
+ .video_inputs = 3,
+- .audio_inputs = 4,
+- .tuner = 0,
++ /* .audio_inputs= 4, */
+ .svhs = 2,
+ .gpiomask = 15,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 13, 4, 11, 7 },
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_radio = 1,
+ .audio_mode_gpio= avermedia_tvphone_audio,
+ },
+ [BTTV_BOARD_PV951] = {
+ .name = "ProVideo PV951", /* pic16c54 */
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0,
+- .muxsel = { 2, 3, 1, 1},
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0, 0, 0},
+ .needs_tvaudio = 1,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_ONAIR_TV] = {
+ .name = "Little OnAir TV",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xe00b,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0xff9ff6, 0xff9ff6, 0xff1ff7, 0 },
+ .gpiomute = 0xff3ffc,
+ .no_msp34xx = 1,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x2c ---------------------------------- */
+ [BTTV_BOARD_SIGMA_TVII_FM] = {
+ .name = "Sigma TVII-FM",
+ .video_inputs = 2,
+- .audio_inputs = 1,
+- .tuner = 0,
+- .svhs = UNSET,
++ /* .audio_inputs= 1, */
++ .svhs = NO_SVHS,
+ .gpiomask = 3,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 1, 1, 0, 2 },
+ .gpiomute = 3,
+ .no_msp34xx = 1,
+ .pll = PLL_NONE,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MATRIX_VISION2] = {
+ .name = "MATRIX-Vision MV-Delta 2",
+ .video_inputs = 5,
+- .audio_inputs = 1,
+- .tuner = UNSET,
++ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .gpiomask = 0,
+- .muxsel = { 2, 3, 1, 0, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0, 0),
+ .gpiomux = { 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_ZOLTRIX_GENIE] = {
+ .name = "Zoltrix Genie TV/FM",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xbcf03f,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0xbc803f, 0xbc903f, 0xbcb03f, 0 },
+ .gpiomute = 0xbcb03f,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_4039FR5_NTSC,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TERRATVRADIO] = {
+ .name = "Terratec TV/Radio+",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x70000,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x20000, 0x30000, 0x10000, 0 },
+ .gpiomute = 0x40000,
+ .needs_tvaudio = 1,
+@@ -1124,7 +1061,6 @@ struct tvcard bttv_tvcards[] = {
+ .pll = PLL_35,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_radio = 1,
+ },
+
+@@ -1132,51 +1068,46 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_DYNALINK] = {
+ .name = "Askey CPH03x/ Dynalink Magic TView",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 15,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = {2,0,0,0 },
+ .gpiomute = 1,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_GVBCTV3PCI] = {
+ .name = "IODATA GV-BCTV3/PCI",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x010f00,
+- .muxsel = {2, 3, 0, 0 },
++ .muxsel = MUXSEL(2, 3, 0, 0),
+ .gpiomux = {0x10000, 0, 0x10000, 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_ALPS_TSHC6_NTSC,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .audio_mode_gpio= gvbctv3pci_audio,
+ },
+ [BTTV_BOARD_PXELVWPLTVPAK] = {
+ .name = "Prolink PV-BT878P+4E / PixelView PlayTV PAK / Lenco MXTV-9578 CP",
+ .video_inputs = 5,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 3,
++ .has_dig_in = 1,
+ .gpiomask = 0xAA0000,
+- .muxsel = { 2,3,1,1,-1 },
+- .digital_mode = DIGITAL_MODE_CAMERA,
++ .muxsel = MUXSEL(2, 3, 1, 1, 0), /* in 4 is digital */
++ /* .digital_mode= DIGITAL_MODE_CAMERA, */
+ .gpiomux = { 0x20000, 0, 0x80000, 0x80000 },
+ .gpiomute = 0xa8000,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_remote = 1,
+ /* GPIO wiring: (different from Rev.4C !)
+ GPIO17: U4.A0 (first hef4052bt)
+@@ -1191,17 +1122,15 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_EAGLE] = {
+ .name = "Eagle Wireless Capricorn2 (bt878A)",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 7,
+- .muxsel = { 2, 0, 1, 1 },
++ .muxsel = MUXSEL(2, 0, 1, 1),
+ .gpiomux = { 0, 1, 2, 3 },
+ .gpiomute = 4,
+ .pll = PLL_28,
+ .tuner_type = UNSET /* TUNER_ALPS_TMDH2_NTSC */,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x34 ---------------------------------- */
+@@ -1209,11 +1138,10 @@ struct tvcard bttv_tvcards[] = {
+ /* David Härdeman <david@2gen.com> */
+ .name = "Pinnacle PCTV Studio Pro",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .gpiomask = 0x03000F,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 1, 0xd0001, 0, 0 },
+ .gpiomute = 10,
+ /* sound path (5 sources):
+@@ -1229,25 +1157,22 @@ struct tvcard bttv_tvcards[] = {
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TVIEW_RDS_FM] = {
+ /* Claas Langbehn <claas@bigfoot.com>,
+ Sven Grothklags <sven@upb.de> */
+ .name = "Typhoon TView RDS + FM Stereo / KNC1 TV Station RDS",
+ .video_inputs = 4,
+- .audio_inputs = 3,
+- .tuner = 0,
++ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .gpiomask = 0x1c,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0, 0x10, 8 },
+ .gpiomute = 4,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_LIFETEC_9415] = {
+@@ -1258,11 +1183,10 @@ struct tvcard bttv_tvcards[] = {
+ options tuner type=5 */
+ .name = "Lifeview FlyVideo 2000 /FlyVideo A2/ Lifetec LT 9415 TV [LR90]",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x18e0,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x0000,0x0800,0x1000,0x1000 },
+ .gpiomute = 0x18e0,
+ /* For cards with tda9820/tda9821:
+@@ -1272,25 +1196,22 @@ struct tvcard bttv_tvcards[] = {
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_BESTBUY_EASYTV] = {
+ /* Miguel Angel Alvarez <maacruz@navegalia.com>
+ old Easy TV BT848 version (model CPH031) */
+ .name = "Askey CPH031/ BESTBUY Easy TV",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xF,
+- .muxsel = { 2, 3, 1, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 2, 0, 0, 0 },
+ .gpiomute = 10,
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x38 ---------------------------------- */
+@@ -1298,17 +1219,15 @@ struct tvcard bttv_tvcards[] = {
+ /* Gordon Heydon <gjheydon@bigfoot.com ('98) */
+ .name = "Lifeview FlyVideo 98FM LR50",
+ .video_inputs = 4,
+- .audio_inputs = 3,
+- .tuner = 0,
++ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .gpiomask = 0x1800,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0x800, 0x1000, 0x1000 },
+ .gpiomute = 0x1800,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ /* This is the ultimate cheapo capture card
+ * just a BT848A on a small PCB!
+@@ -1316,51 +1235,45 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_GRANDTEC] = {
+ .name = "GrandTec 'Grand Video Capture' (Bt848)",
+ .video_inputs = 2,
+- .audio_inputs = 0,
+- .tuner = UNSET,
++ /* .audio_inputs= 0, */
+ .svhs = 1,
+ .gpiomask = 0,
+- .muxsel = { 3, 1 },
++ .muxsel = MUXSEL(3, 1),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 0,
+ .no_msp34xx = 1,
+ .pll = PLL_35,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_ASKEY_CPH060] = {
+ /* Daniel Herrington <daniel.herrington@home.com> */
+ .name = "Askey CPH060/ Phoebe TV Master Only (No FM)",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xe00,
+- .muxsel = { 2, 3, 1, 1},
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x400, 0x400, 0x400, 0x400 },
+ .gpiomute = 0x800,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_4036FY5_NTSC,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_ASKEY_CPH03X] = {
+ /* Matti Mottus <mottus@physic.ut.ee> */
+ .name = "Askey CPH03x TV Capturer",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x03000F,
+- .muxsel = { 2, 3, 1, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 2, 0, 0, 0 },
+ .gpiomute = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x3c ---------------------------------- */
+@@ -1368,34 +1281,30 @@ struct tvcard bttv_tvcards[] = {
+ /* Philip Blundell <philb@gnu.org> */
+ .name = "Modular Technology MM100PCTV",
+ .video_inputs = 2,
+- .audio_inputs = 2,
+- .tuner = 0,
+- .svhs = UNSET,
++ /* .audio_inputs= 2, */
++ .svhs = NO_SVHS,
+ .gpiomask = 11,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 2, 0, 0, 1 },
+ .gpiomute = 8,
+ .pll = PLL_35,
+ .tuner_type = TUNER_TEMIC_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_GMV1] = {
+ /* Adrian Cox <adrian@humboldt.co.uk */
+ .name = "AG Electronics GMV1",
+ .video_inputs = 2,
+- .audio_inputs = 0,
+- .tuner = UNSET,
++ /* .audio_inputs= 0, */
+ .svhs = 1,
+ .gpiomask = 0xF,
+- .muxsel = { 2, 2 },
++ .muxsel = MUXSEL(2, 2),
+ .gpiomux = { },
+ .no_msp34xx = 1,
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_BESTBUY_EASYTV2] = {
+ /* Miguel Angel Alvarez <maacruz@navegalia.com>
+@@ -1403,34 +1312,30 @@ struct tvcard bttv_tvcards[] = {
+ special thanks to Informatica Mieres for providing the card */
+ .name = "Askey CPH061/ BESTBUY Easy TV (bt878)",
+ .video_inputs = 3,
+- .audio_inputs = 2,
+- .tuner = 0,
++ /* .audio_inputs= 2, */
+ .svhs = 2,
+ .gpiomask = 0xFF,
+- .muxsel = { 2, 3, 1, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 1, 0, 4, 4 },
+ .gpiomute = 9,
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_ATI_TVWONDER] = {
+ /* Lukas Gebauer <geby@volny.cz> */
+ .name = "ATI TV-Wonder",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xf03f,
+- .muxsel = { 2, 3, 1, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 0xbffe, 0, 0xbfff, 0 },
+ .gpiomute = 0xbffe,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_4006FN5_MULTI_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x40 ---------------------------------- */
+@@ -1438,27 +1343,24 @@ struct tvcard bttv_tvcards[] = {
+ /* Lukas Gebauer <geby@volny.cz> */
+ .name = "ATI TV-Wonder VE",
+ .video_inputs = 2,
+- .audio_inputs = 1,
+- .tuner = 0,
+- .svhs = UNSET,
++ /* .audio_inputs= 1, */
++ .svhs = NO_SVHS,
+ .gpiomask = 1,
+- .muxsel = { 2, 3, 0, 1 },
++ .muxsel = MUXSEL(2, 3, 0, 1),
+ .gpiomux = { 0, 0, 1, 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TEMIC_4006FN5_MULTI_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_FLYVIDEO2000] = {
+ /* DeeJay <deejay@westel900.net (2000S) */
+ .name = "Lifeview FlyVideo 2000S LR90",
+ .video_inputs = 3,
+- .audio_inputs = 3,
+- .tuner = 0,
++ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .gpiomask = 0x18e0,
+- .muxsel = { 2, 3, 0, 1 },
++ .muxsel = MUXSEL(2, 3, 0, 1),
+ /* Radio changed from 1e80 to 0x800 to make
+ FlyVideo2000S in .hu happy (gm)*/
+ /* -dk-???: set mute=0x1800 for tda9874h daughterboard */
+@@ -1471,40 +1373,35 @@ struct tvcard bttv_tvcards[] = {
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TERRATVALUER] = {
+ .name = "Terratec TValueRadio",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0xffff00,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x500, 0x500, 0x300, 0x900 },
+ .gpiomute = 0x900,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_GVBCTV4PCI] = {
+ /* TANAKA Kei <peg00625@nifty.com> */
+ .name = "IODATA GV-BCTV4/PCI",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x010f00,
+- .muxsel = {2, 3, 0, 0 },
++ .muxsel = MUXSEL(2, 3, 0, 0),
+ .gpiomux = {0x10000, 0, 0x10000, 0 },
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_SHARP_2U5JF5540_NTSC,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .audio_mode_gpio= gvbctv3pci_audio,
+ },
+
+@@ -1514,9 +1411,8 @@ struct tvcard bttv_tvcards[] = {
+ /* try "insmod msp3400 simple=0" if you have
+ * sound problems with this card. */
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
+- .svhs = UNSET,
++ /* .audio_inputs= 1, */
++ .svhs = NO_SVHS,
+ .gpiomask = 0x4f8a00,
+ /* 0x100000: 1=MSP enabled (0=disable again)
+ * 0x010000: Connected to "S0" on tda9880 (0=Pal/BG, 1=NTSC) */
+@@ -1524,10 +1420,9 @@ struct tvcard bttv_tvcards[] = {
+ .gpiomute = 0x947fff,
+ /* tvtuner, radio, external,internal, mute, stereo
+ * tuner, Composit, SVid, Composit-on-Svid-adapter */
+- .muxsel = { 2, 3 ,0 ,1 },
++ .muxsel = MUXSEL(2, 3, 0, 1),
+ .tuner_type = TUNER_MT2032,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ },
+@@ -1536,9 +1431,8 @@ struct tvcard bttv_tvcards[] = {
+ /* try "insmod msp3400 simple=0" if you have
+ * sound problems with this card. */
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
+- .svhs = UNSET,
++ /* .audio_inputs= 1, */
++ .svhs = NO_SVHS,
+ .gpiomask = 0x4f8a00,
+ /* 0x100000: 1=MSP enabled (0=disable again)
+ * 0x010000: Connected to "S0" on tda9880 (0=Pal/BG, 1=NTSC) */
+@@ -1546,10 +1440,9 @@ struct tvcard bttv_tvcards[] = {
+ .gpiomute = 0x947fff,
+ /* tvtuner, radio, external,internal, mute, stereo
+ * tuner, Composit, SVid, Composit-on-Svid-adapter */
+- .muxsel = { 2, 3 ,0 ,1 },
++ .muxsel = MUXSEL(2, 3, 0, 1),
+ .tuner_type = TUNER_MT2032,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ },
+@@ -1557,31 +1450,27 @@ struct tvcard bttv_tvcards[] = {
+ /* Philip Blundell <pb@nexus.co.uk> */
+ .name = "Active Imaging AIMMS",
+ .video_inputs = 1,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .tuner_type = UNSET,
++ /* .audio_inputs= 0, */
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .pll = PLL_28,
+- .muxsel = { 2 },
++ .muxsel = MUXSEL(2),
+ .gpiomask = 0
+ },
+ [BTTV_BOARD_PV_BT878P_PLUS] = {
+ /* Tomasz Pyra <hellfire@sedez.iq.pl> */
+ .name = "Prolink Pixelview PV-BT878P+ (Rev.4C,8E)",
+ .video_inputs = 3,
+- .audio_inputs = 4,
+- .tuner = 0,
++ /* .audio_inputs= 4, */
+ .svhs = 2,
+ .gpiomask = 15,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0, 11, 7 }, /* TV and Radio with same GPIO ! */
+ .gpiomute = 13,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_LG_PAL_I_FM,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_remote = 1,
+ /* GPIO wiring:
+ GPIO0: U4.A0 (hef4052bt)
+@@ -1594,15 +1483,14 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_FLYVIDEO98EZ] = {
+ .name = "Lifeview FlyVideo 98EZ (capture only) LR51",
+ .video_inputs = 4,
+- .audio_inputs = 0,
+- .tuner = UNSET,
++ /* .audio_inputs= 0, */
+ .svhs = 2,
+- .muxsel = { 2, 3, 1, 1 }, /* AV1, AV2, SVHS, CVid adapter on SVHS */
++ /* AV1, AV2, SVHS, CVid adapter on SVHS */
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .pll = PLL_28,
+ .no_msp34xx = 1,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x48 ---------------------------------- */
+@@ -1610,11 +1498,10 @@ struct tvcard bttv_tvcards[] = {
+ /* Dariusz Kowalewski <darekk@automex.pl> */
+ .name = "Prolink Pixelview PV-BT878P+9B (PlayTV Pro rev.9B FM+NICAM)",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x3f,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x01, 0x00, 0x03, 0x03 },
+ .gpiomute = 0x09,
+ .needs_tvaudio = 1,
+@@ -1623,7 +1510,6 @@ struct tvcard bttv_tvcards[] = {
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .audio_mode_gpio= pvbt878p9b_audio, /* Note: not all cards have stereo */
+ .has_radio = 1, /* Note: not all cards have radio */
+ .has_remote = 1,
+@@ -1640,49 +1526,42 @@ struct tvcard bttv_tvcards[] = {
+ /* you must jumper JP5 for the card to work */
+ .name = "Sensoray 311",
+ .video_inputs = 5,
+- .audio_inputs = 0,
+- .tuner = UNSET,
++ /* .audio_inputs= 0, */
+ .svhs = 4,
+ .gpiomask = 0,
+- .muxsel = { 2, 3, 1, 0, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0, 0),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 0,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_RV605] = {
+ /* Miguel Freitas <miguel@cetuc.puc-rio.br> */
+ .name = "RemoteVision MX (RV605)",
+ .video_inputs = 16,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .svhs = UNSET,
++ /* .audio_inputs= 0, */
++ .svhs = NO_SVHS,
+ .gpiomask = 0x00,
+ .gpiomask2 = 0x07ff,
+- .muxsel = { 0x33, 0x13, 0x23, 0x43, 0xf3, 0x73, 0xe3, 0x03,
+- 0xd3, 0xb3, 0xc3, 0x63, 0x93, 0x53, 0x83, 0xa3 },
++ .muxsel = MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3),
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .muxsel_hook = rv605_muxsel,
+ },
+ [BTTV_BOARD_POWERCLR_MTV878] = {
+ .name = "Powercolor MTV878/ MTV878R/ MTV878F",
+ .video_inputs = 3,
+- .audio_inputs = 2,
+- .tuner = 0,
++ /* .audio_inputs= 2, */
+ .svhs = 2,
+ .gpiomask = 0x1C800F, /* Bit0-2: Audio select, 8-12:remote control 14:remote valid 15:remote reset */
+- .muxsel = { 2, 1, 1, },
++ .muxsel = MUXSEL(2, 1, 1),
+ .gpiomux = { 0, 1, 2, 2 },
+ .gpiomute = 4,
+ .needs_tvaudio = 0,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ },
+@@ -1692,42 +1571,38 @@ struct tvcard bttv_tvcards[] = {
+ /* Masaki Suzuki <masaki@btree.org> */
+ .name = "Canopus WinDVR PCI (COMPAQ Presario 3524JP, 5112JP)",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x140007,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 3 },
+ .gpiomute = 4,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .audio_mode_gpio= windvr_audio,
+ },
+ [BTTV_BOARD_GRANDTEC_MULTI] = {
+ .name = "GrandTec Multi Capture Card (Bt878)",
+ .video_inputs = 4,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .svhs = UNSET,
++ /* .audio_inputs= 0, */
++ .svhs = NO_SVHS,
+ .gpiomask = 0,
+- .muxsel = { 2, 3, 1, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 0,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_KWORLD] = {
+ .name = "Jetway TV/Capture JW-TV878-FBK, Kworld KW-TV878RF",
+ .video_inputs = 4,
+- .audio_inputs = 3,
+- .tuner = 0,
++ /* .audio_inputs= 3, */
+ .svhs = 2,
+ .gpiomask = 7,
+- .muxsel = { 2, 3, 1, 1 }, /* Tuner, SVid, SVHS, SVid to SVHS connector */
++ /* Tuner, SVid, SVHS, SVid to SVHS connector */
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0, 4, 4 },/* Yes, this tuner uses the same audio output for TV and FM radio!
+ * This card lacks external Audio In, so we mute it on Ext. & Int.
+ * The PCB can take a sbx1637/sbx1673, wiring unknown.
+@@ -1741,7 +1616,6 @@ struct tvcard bttv_tvcards[] = {
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ /* Samsung TCPA9095PC27A (BG+DK), philips compatible, w/FM, stereo and
+ radio signal strength indicators work fine. */
+ .has_radio = 1,
+@@ -1759,27 +1633,24 @@ struct tvcard bttv_tvcards[] = {
+ /* Arthur Tetzlaff-Deas, DSP Design Ltd <software@dspdesign.com> */
+ .name = "DSP Design TCVIDEO",
+ .video_inputs = 4,
+- .svhs = UNSET,
+- .muxsel = { 2, 3, 1, 0 },
++ .svhs = NO_SVHS,
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x50 ---------------------------------- */
+ [BTTV_BOARD_HAUPPAUGEPVR] = {
+ .name = "Hauppauge WinTV PVR",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+- .muxsel = { 2, 0, 1, 1 },
++ .muxsel = MUXSEL(2, 0, 1, 1),
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+
+ .gpiomask = 7,
+ .gpiomux = {7},
+@@ -1787,32 +1658,28 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_GVBCTV5PCI] = {
+ .name = "IODATA GV-BCTV5/PCI",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x0f0f80,
+- .muxsel = {2, 3, 1, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = {0x030000, 0x010000, 0, 0 },
+ .gpiomute = 0x020000,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .audio_mode_gpio= gvbctv5pci_audio,
+ .has_radio = 1,
+ },
+ [BTTV_BOARD_OSPREY1x0] = {
+ .name = "Osprey 100/150 (878)", /* 0x1(2|3)-45C6-C1 */
+ .video_inputs = 4, /* id-inputs-clock */
+- .audio_inputs = 0,
+- .tuner = UNSET,
++ /* .audio_inputs= 0, */
+ .svhs = 3,
+- .muxsel = { 3, 2, 0, 1 },
++ .muxsel = MUXSEL(3, 2, 0, 1),
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+@@ -1820,14 +1687,12 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_OSPREY1x0_848] = {
+ .name = "Osprey 100/150 (848)", /* 0x04-54C0-C1 & older boards */
+ .video_inputs = 3,
+- .audio_inputs = 0,
+- .tuner = UNSET,
++ /* .audio_inputs= 0, */
+ .svhs = 2,
+- .muxsel = { 2, 3, 1 },
++ .muxsel = MUXSEL(2, 3, 1),
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+@@ -1837,14 +1702,12 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_OSPREY101_848] = {
+ .name = "Osprey 101 (848)", /* 0x05-40C0-C1 */
+ .video_inputs = 2,
+- .audio_inputs = 0,
+- .tuner = UNSET,
++ /* .audio_inputs= 0, */
+ .svhs = 1,
+- .muxsel = { 3, 1 },
++ .muxsel = MUXSEL(3, 1),
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+@@ -1852,14 +1715,12 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_OSPREY1x1] = {
+ .name = "Osprey 101/151", /* 0x1(4|5)-0004-C4 */
+ .video_inputs = 1,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .svhs = UNSET,
+- .muxsel = { 0 },
++ /* .audio_inputs= 0, */
++ .svhs = NO_SVHS,
++ .muxsel = MUXSEL(0),
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+@@ -1867,14 +1728,12 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_OSPREY1x1_SVID] = {
+ .name = "Osprey 101/151 w/ svid", /* 0x(16|17|20)-00C4-C1 */
+ .video_inputs = 2,
+- .audio_inputs = 0,
+- .tuner = UNSET,
++ /* .audio_inputs= 0, */
+ .svhs = 1,
+- .muxsel = { 0, 1 },
++ .muxsel = MUXSEL(0, 1),
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+@@ -1882,14 +1741,12 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_OSPREY2xx] = {
+ .name = "Osprey 200/201/250/251", /* 0x1(8|9|E|F)-0004-C4 */
+ .video_inputs = 1,
+- .audio_inputs = 1,
+- .tuner = UNSET,
+- .svhs = UNSET,
+- .muxsel = { 0 },
++ /* .audio_inputs= 1, */
++ .svhs = NO_SVHS,
++ .muxsel = MUXSEL(0),
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+@@ -1899,14 +1756,12 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_OSPREY2x0_SVID] = {
+ .name = "Osprey 200/250", /* 0x1(A|B)-00C4-C1 */
+ .video_inputs = 2,
+- .audio_inputs = 1,
+- .tuner = UNSET,
++ /* .audio_inputs= 1, */
+ .svhs = 1,
+- .muxsel = { 0, 1 },
++ .muxsel = MUXSEL(0, 1),
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+@@ -1914,14 +1769,12 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_OSPREY2x0] = {
+ .name = "Osprey 210/220/230", /* 0x1(A|B)-04C0-C1 */
+ .video_inputs = 2,
+- .audio_inputs = 1,
+- .tuner = UNSET,
++ /* .audio_inputs= 1, */
+ .svhs = 1,
+- .muxsel = { 2, 3 },
++ .muxsel = MUXSEL(2, 3),
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+@@ -1929,14 +1782,12 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_OSPREY500] = {
+ .name = "Osprey 500", /* 500 */
+ .video_inputs = 2,
+- .audio_inputs = 1,
+- .tuner = UNSET,
++ /* .audio_inputs= 1, */
+ .svhs = 1,
+- .muxsel = { 2, 3 },
++ .muxsel = MUXSEL(2, 3),
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+@@ -1944,12 +1795,10 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_OSPREY540] = {
+ .name = "Osprey 540", /* 540 */
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = UNSET,
++ /* .audio_inputs= 1, */
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+@@ -1959,14 +1808,12 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_OSPREY2000] = {
+ .name = "Osprey 2000", /* 2000 */
+ .video_inputs = 2,
+- .audio_inputs = 1,
+- .tuner = UNSET,
++ /* .audio_inputs= 1, */
+ .svhs = 1,
+- .muxsel = { 2, 3 },
++ .muxsel = MUXSEL(2, 3),
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1, /* must avoid, conflicts with the bt860 */
+@@ -1975,14 +1822,12 @@ struct tvcard bttv_tvcards[] = {
+ /* M G Berberich <berberic@forwiss.uni-passau.de> */
+ .name = "IDS Eagle",
+ .video_inputs = 4,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .tuner_type = UNSET,
++ /* .audio_inputs= 0, */
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+- .svhs = UNSET,
++ .svhs = NO_SVHS,
+ .gpiomask = 0,
+- .muxsel = { 0, 1, 2, 3 },
++ .muxsel = MUXSEL(2, 2, 2, 2),
+ .muxsel_hook = eagle_muxsel,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+@@ -1991,16 +1836,14 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_PINNACLESAT] = {
+ .name = "Pinnacle PCTV Sat",
+ .video_inputs = 2,
+- .audio_inputs = 0,
++ /* .audio_inputs= 0, */
+ .svhs = 1,
+- .tuner = UNSET,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+- .muxsel = { 3, 1 },
++ .muxsel = MUXSEL(3, 1),
+ .pll = PLL_28,
+ .no_gpioirq = 1,
+ .has_dvb = 1,
+@@ -2008,18 +1851,16 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_FORMAC_PROTV] = {
+ .name = "Formac ProTV II (bt878)",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 3,
+ .gpiomask = 2,
+ /* TV, Comp1, Composite over SVID con, SVID */
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 2, 2, 0, 0 },
+ .pll = PLL_28,
+ .has_radio = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ /* sound routing:
+ GPIO=0x00,0x01,0x03: mute (?)
+ 0x02: both TV and radio (tuner: FM1216/I)
+@@ -2033,62 +1874,55 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_MACHTV] = {
+ .name = "MachTV",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
+- .svhs = UNSET,
++ /* .audio_inputs= 1, */
++ .svhs = NO_SVHS,
+ .gpiomask = 7,
+- .muxsel = { 2, 3, 1, 1},
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 3},
+ .gpiomute = 4,
+ .needs_tvaudio = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ },
+ [BTTV_BOARD_EURESYS_PICOLO] = {
+ .name = "Euresys Picolo",
+ .video_inputs = 3,
+- .audio_inputs = 0,
+- .tuner = UNSET,
++ /* .audio_inputs= 0, */
+ .svhs = 2,
+ .gpiomask = 0,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+- .muxsel = { 2, 0, 1},
++ .muxsel = MUXSEL(2, 0, 1),
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_PV150] = {
+ /* Luc Van Hoeylandt <luc@e-magic.be> */
+ .name = "ProVideo PV150", /* 0x4f */
+ .video_inputs = 2,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .svhs = UNSET,
++ /* .audio_inputs= 0, */
++ .svhs = NO_SVHS,
+ .gpiomask = 0,
+- .muxsel = { 2, 3 },
++ .muxsel = MUXSEL(2, 3),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 0,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_AD_TVK503] = {
+ /* Hiroshi Takekawa <sian@big.or.jp> */
+ /* This card lacks subsystem ID */
+ .name = "AD-TVK503", /* 0x63 */
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x001e8007,
+- .muxsel = { 2, 3, 1, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ /* Tuner, Radio, external, internal, off, on */
+ .gpiomux = { 0x08, 0x0f, 0x0a, 0x08 },
+ .gpiomute = 0x0f,
+@@ -2097,7 +1931,6 @@ struct tvcard bttv_tvcards[] = {
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .audio_mode_gpio= adtvk503_audio,
+ },
+
+@@ -2105,17 +1938,15 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_HERCULES_SM_TV] = {
+ .name = "Hercules Smart TV Stereo",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x00,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .needs_tvaudio = 1,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ /* Notes:
+ - card lacks subsystem ID
+ - stereo variant w/ daughter board with tda9874a @0xb0
+@@ -2129,16 +1960,15 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_PACETV] = {
+ .name = "Pace TV & Radio Card",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+- .muxsel = { 2, 3, 1, 1 }, /* Tuner, CVid, SVid, CVid over SVid connector */
++ /* Tuner, CVid, SVid, CVid over SVid connector */
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomask = 0,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_radio = 1,
+ .pll = PLL_28,
+ /* Bt878, Bt832, FI1246 tuner; no pci subsystem id
+@@ -2152,27 +1982,34 @@ struct tvcard bttv_tvcards[] = {
+ /* Chris Willing <chris@vislab.usyd.edu.au> */
+ .name = "IVC-200",
+ .video_inputs = 1,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .tuner_type = UNSET,
++ /* .audio_inputs= 0, */
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+- .svhs = UNSET,
++ .svhs = NO_SVHS,
++ .gpiomask = 0xdf,
++ .muxsel = MUXSEL(2),
++ .pll = PLL_28,
++ },
++ [BTTV_BOARD_IVCE8784] = {
++ .name = "IVCE-8784",
++ .video_inputs = 1,
++ /* .audio_inputs= 0, */
++ .tuner_type = TUNER_ABSENT,
++ .tuner_addr = ADDR_UNSET,
++ .svhs = NO_SVHS,
+ .gpiomask = 0xdf,
+- .muxsel = { 2 },
++ .muxsel = MUXSEL(2),
+ .pll = PLL_28,
+ },
+ [BTTV_BOARD_XGUARD] = {
+ .name = "Grand X-Guard / Trust 814PCI",
+ .video_inputs = 16,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .svhs = UNSET,
++ /* .audio_inputs= 0, */
++ .svhs = NO_SVHS,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .gpiomask2 = 0xff,
+- .muxsel = { 2,2,2,2, 3,3,3,3, 1,1,1,1, 0,0,0,0 },
++ .muxsel = MUXSEL(2,2,2,2, 3,3,3,3, 1,1,1,1, 0,0,0,0),
+ .muxsel_hook = xguard_muxsel,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+@@ -2184,16 +2021,14 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_NEBULA_DIGITV] = {
+ .name = "Nebula Electronics DigiTV",
+ .video_inputs = 1,
+- .tuner = UNSET,
+- .svhs = UNSET,
+- .muxsel = { 2, 3, 1, 0 },
++ .svhs = NO_SVHS,
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_dvb = 1,
+ .has_remote = 1,
+ .gpiomask = 0x1b,
+@@ -2203,118 +2038,101 @@ struct tvcard bttv_tvcards[] = {
+ /* Jorge Boncompte - DTI2 <jorge@dti2.net> */
+ .name = "ProVideo PV143",
+ .video_inputs = 4,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .svhs = UNSET,
++ /* .audio_inputs= 0, */
++ .svhs = NO_SVHS,
+ .gpiomask = 0,
+- .muxsel = { 2, 3, 1, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 0 },
+ .needs_tvaudio = 0,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD009X1_VD011_MINIDIN] = {
+ /* M.Klahr@phytec.de */
+ .name = "PHYTEC VD-009-X1 VD-011 MiniDIN (bt878)",
+ .video_inputs = 4,
+- .audio_inputs = 0,
+- .tuner = UNSET, /* card has no tuner */
++ /* .audio_inputs= 0, */
+ .svhs = 3,
+ .gpiomask = 0x00,
+- .muxsel = { 2, 3, 1, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD009X1_VD011_COMBI] = {
+ .name = "PHYTEC VD-009-X1 VD-011 Combi (bt878)",
+ .video_inputs = 4,
+- .audio_inputs = 0,
+- .tuner = UNSET, /* card has no tuner */
++ /* .audio_inputs= 0, */
+ .svhs = 3,
+ .gpiomask = 0x00,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+
+ /* ---- card 0x6c ---------------------------------- */
+ [BTTV_BOARD_VD009_MINIDIN] = {
+ .name = "PHYTEC VD-009 MiniDIN (bt878)",
+ .video_inputs = 10,
+- .audio_inputs = 0,
+- .tuner = UNSET, /* card has no tuner */
++ /* .audio_inputs= 0, */
+ .svhs = 9,
+ .gpiomask = 0x00,
+- .gpiomask2 = 0x03, /* gpiomask2 defines the bits used to switch audio
+- via the upper nibble of muxsel. here: used for
+- xternal video-mux */
+- .muxsel = { 0x02, 0x12, 0x22, 0x32, 0x03, 0x13, 0x23, 0x33, 0x01, 0x00 },
++ .gpiomask2 = 0x03, /* used for external vodeo mux */
++ .muxsel = MUXSEL(2, 2, 2, 2, 3, 3, 3, 3, 1, 0),
++ .muxsel_hook = phytec_muxsel,
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD009_COMBI] = {
+ .name = "PHYTEC VD-009 Combi (bt878)",
+ .video_inputs = 10,
+- .audio_inputs = 0,
+- .tuner = UNSET, /* card has no tuner */
++ /* .audio_inputs= 0, */
+ .svhs = 9,
+ .gpiomask = 0x00,
+- .gpiomask2 = 0x03, /* gpiomask2 defines the bits used to switch audio
+- via the upper nibble of muxsel. here: used for
+- xternal video-mux */
+- .muxsel = { 0x02, 0x12, 0x22, 0x32, 0x03, 0x13, 0x23, 0x33, 0x01, 0x01 },
++ .gpiomask2 = 0x03, /* used for external vodeo mux */
++ .muxsel = MUXSEL(2, 2, 2, 2, 3, 3, 3, 3, 1, 1),
++ .muxsel_hook = phytec_muxsel,
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_IVC100] = {
+ .name = "IVC-100",
+ .video_inputs = 4,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .tuner_type = UNSET,
++ /* .audio_inputs= 0, */
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+- .svhs = UNSET,
++ .svhs = NO_SVHS,
+ .gpiomask = 0xdf,
+- .muxsel = { 2, 3, 1, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .pll = PLL_28,
+ },
+ [BTTV_BOARD_IVC120] = {
+ /* IVC-120G - Alan Garfield <alan@fromorbit.com> */
+ .name = "IVC-120G",
+ .video_inputs = 16,
+- .audio_inputs = 0, /* card has no audio */
+- .tuner = UNSET, /* card has no tuner */
+- .tuner_type = UNSET,
++ /* .audio_inputs= 0, */
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+- .svhs = UNSET, /* card has no svhs */
++ .svhs = NO_SVHS, /* card has no svhs */
+ .needs_tvaudio = 0,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+ .gpiomask = 0x00,
+- .muxsel = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+- 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 },
++ .muxsel = MUXSEL(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0),
+ .muxsel_hook = ivc120_muxsel,
+ .pll = PLL_28,
+ },
+@@ -2323,13 +2141,11 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_PC_HDTV] = {
+ .name = "pcHDTV HD-2000 TV",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+- .muxsel = { 2, 3, 1, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .tuner_type = TUNER_PHILIPS_FCV1236D,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_dvb = 1,
+ },
+ [BTTV_BOARD_TWINHAN_DST] = {
+@@ -2339,38 +2155,34 @@ struct tvcard bttv_tvcards[] = {
+ .no_tda7432 = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .no_video = 1,
+ .has_dvb = 1,
+ },
+ [BTTV_BOARD_WINFASTVC100] = {
+ .name = "Winfast VC100",
+ .video_inputs = 3,
+- .audio_inputs = 0,
++ /* .audio_inputs= 0, */
+ .svhs = 1,
+- .tuner = UNSET,
+- .muxsel = { 3, 1, 1, 3 }, /* Vid In, SVid In, Vid over SVid in connector */
++ /* Vid In, SVid In, Vid over SVid in connector */
++ .muxsel = MUXSEL(3, 1, 1, 3),
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ },
+ [BTTV_BOARD_TEV560] = {
+ .name = "Teppro TEV-560/InterVision IV-560",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 3,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 1, 1, 1, 1 },
+ .needs_tvaudio = 1,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .pll = PLL_35,
+ },
+
+@@ -2378,14 +2190,12 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_SIMUS_GVC1100] = {
+ .name = "SIMUS GVC1100",
+ .video_inputs = 4,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .svhs = UNSET,
+- .tuner_type = UNSET,
++ /* .audio_inputs= 0, */
++ .svhs = NO_SVHS,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .pll = PLL_28,
+- .muxsel = { 2, 2, 2, 2 },
++ .muxsel = MUXSEL(2, 2, 2, 2),
+ .gpiomask = 0x3F,
+ .muxsel_hook = gvc1100_muxsel,
+ },
+@@ -2393,47 +2203,41 @@ struct tvcard bttv_tvcards[] = {
+ /* Carlos Silva r3pek@r3pek.homelinux.org || card 0x75 */
+ .name = "NGS NGSTV+",
+ .video_inputs = 3,
+- .tuner = 0,
+ .svhs = 2,
+ .gpiomask = 0x008007,
+- .muxsel = { 2, 3, 0, 0 },
++ .muxsel = MUXSEL(2, 3, 0, 0),
+ .gpiomux = { 0, 0, 0, 0 },
+ .gpiomute = 0x000003,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_LMLBT4] = {
+ /* http://linuxmedialabs.com */
+ .name = "LMLBT4",
+ .video_inputs = 4, /* IN1,IN2,IN3,IN4 */
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .svhs = UNSET,
+- .muxsel = { 2, 3, 1, 0 },
++ /* .audio_inputs= 0, */
++ .svhs = NO_SVHS,
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+ .needs_tvaudio = 0,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_TEKRAM_M205] = {
+ /* Helmroos Harri <harri.helmroos@pp.inet.fi> */
+ .name = "Tekram M205 PRO",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .svhs = 2,
+ .needs_tvaudio = 0,
+ .gpiomask = 0x68,
+- .muxsel = { 2, 3, 1 },
++ .muxsel = MUXSEL(2, 3, 1),
+ .gpiomux = { 0x68, 0x68, 0x61, 0x61 },
+ .pll = PLL_28,
+ },
+@@ -2444,18 +2248,16 @@ struct tvcard bttv_tvcards[] = {
+ /* bt878 TV + FM without subsystem ID */
+ .name = "Conceptronic CONTVFMi",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x008007,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 2 },
+ .gpiomute = 3,
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_remote = 1,
+ .has_radio = 1,
+ },
+@@ -2466,37 +2268,34 @@ struct tvcard bttv_tvcards[] = {
+ /*0x79 in bttv.h*/
+ .name = "Euresys Picolo Tetra",
+ .video_inputs = 4,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .svhs = UNSET,
++ /* .audio_inputs= 0, */
++ .svhs = NO_SVHS,
+ .gpiomask = 0,
+ .gpiomask2 = 0x3C<<16,/*Set the GPIO[18]->GPIO[21] as output pin.==> drive the video inputs through analog multiplexers*/
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+- .muxsel = {2,2,2,2},/*878A input is always MUX0, see above.*/
++ /*878A input is always MUX0, see above.*/
++ .muxsel = MUXSEL(2, 2, 2, 2),
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .pll = PLL_28,
+ .needs_tvaudio = 0,
+ .muxsel_hook = picolo_tetra_muxsel,/*Required as it doesn't follow the classic input selection policy*/
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_SPIRIT_TV] = {
+ /* Spirit TV Tuner from http://spiritmodems.com.au */
+ /* Stafford Goodsell <surge@goliath.homeunix.org> */
+ .name = "Spirit TV Tuner",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x0000000f,
+- .muxsel = { 2, 1, 1 },
++ .muxsel = MUXSEL(2, 1, 1),
+ .gpiomux = { 0x02, 0x00, 0x00, 0x00 },
+ .tuner_type = TUNER_TEMIC_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ },
+@@ -2505,11 +2304,9 @@ struct tvcard bttv_tvcards[] = {
+ .name = "AVerMedia AVerTV DVB-T 771",
+ .video_inputs = 2,
+ .svhs = 1,
+- .tuner = UNSET,
+ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+- .muxsel = { 3 , 3 },
++ .muxsel = MUXSEL(3, 3),
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+@@ -2524,54 +2321,47 @@ struct tvcard bttv_tvcards[] = {
+ /* Based on the Nebula card data - added remote and new card number - BTTV_BOARD_AVDVBT_761, see also ir-kbd-gpio.c */
+ .name = "AverMedia AverTV DVB-T 761",
+ .video_inputs = 2,
+- .tuner = UNSET,
+ .svhs = 1,
+- .muxsel = { 3, 1, 2, 0 }, /* Comp0, S-Video, ?, ? */
++ .muxsel = MUXSEL(3, 1, 2, 0), /* Comp0, S-Video, ?, ? */
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_dvb = 1,
+ .no_gpioirq = 1,
+ .has_remote = 1,
+ },
+ [BTTV_BOARD_MATRIX_VISIONSQ] = {
+ /* andre.schwarz@matrix-vision.de */
+- .name = "MATRIX Vision Sigma-SQ",
+- .video_inputs = 16,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .svhs = UNSET,
+- .gpiomask = 0x0,
+- .muxsel = { 2, 2, 2, 2, 2, 2, 2, 2,
+- 3, 3, 3, 3, 3, 3, 3, 3 },
+- .muxsel_hook = sigmaSQ_muxsel,
+- .gpiomux = { 0 },
+- .no_msp34xx = 1,
+- .pll = PLL_28,
+- .tuner_type = UNSET,
+- .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
++ .name = "MATRIX Vision Sigma-SQ",
++ .video_inputs = 16,
++ /* .audio_inputs= 0, */
++ .svhs = NO_SVHS,
++ .gpiomask = 0x0,
++ .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3),
++ .muxsel_hook = sigmaSQ_muxsel,
++ .gpiomux = { 0 },
++ .no_msp34xx = 1,
++ .pll = PLL_28,
++ .tuner_type = TUNER_ABSENT,
++ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MATRIX_VISIONSLC] = {
+ /* andre.schwarz@matrix-vision.de */
+- .name = "MATRIX Vision Sigma-SLC",
+- .video_inputs = 4,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .svhs = UNSET,
+- .gpiomask = 0x0,
+- .muxsel = { 2, 2, 2, 2 },
+- .muxsel_hook = sigmaSLC_muxsel,
+- .gpiomux = { 0 },
+- .no_msp34xx = 1,
+- .pll = PLL_28,
+- .tuner_type = UNSET,
+- .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
++ .name = "MATRIX Vision Sigma-SLC",
++ .video_inputs = 4,
++ /* .audio_inputs= 0, */
++ .svhs = NO_SVHS,
++ .gpiomask = 0x0,
++ .muxsel = MUXSEL(2, 2, 2, 2),
++ .muxsel_hook = sigmaSLC_muxsel,
++ .gpiomux = { 0 },
++ .no_msp34xx = 1,
++ .pll = PLL_28,
++ .tuner_type = TUNER_ABSENT,
++ .tuner_addr = ADDR_UNSET,
+ },
+ /* BTTV_BOARD_APAC_VIEWCOMP */
+ [BTTV_BOARD_APAC_VIEWCOMP] = {
+@@ -2579,18 +2369,16 @@ struct tvcard bttv_tvcards[] = {
+ /* bt878 TV + FM 0x00000000 subsystem ID */
+ .name = "APAC Viewcomp 878(AMAX)",
+ .video_inputs = 2,
+- .audio_inputs = 1,
+- .tuner = 0,
+- .svhs = UNSET,
++ /* .audio_inputs= 1, */
++ .svhs = NO_SVHS,
+ .gpiomask = 0xFF,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 2, 0, 0, 0 },
+ .gpiomute = 10,
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_remote = 1, /* miniremote works, see ir-kbd-gpio.c */
+ .has_radio = 1, /* not every card has radio */
+ },
+@@ -2599,46 +2387,40 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_DVICO_DVBT_LITE] = {
+ /* Chris Pascoe <c.pascoe@itee.uq.edu.au> */
+ .name = "DViCO FusionHDTV DVB-T Lite",
+- .tuner = UNSET,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+ .pll = PLL_28,
+ .no_video = 1,
+ .has_dvb = 1,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VGEAR_MYVCD] = {
+ /* Steven <photon38@pchome.com.tw> */
+ .name = "V-Gear MyVCD",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x3f,
+- .muxsel = {2, 3, 1, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .gpiomux = {0x31, 0x31, 0x31, 0x31 },
+ .gpiomute = 0x31,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_NTSC_M,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_radio = 0,
+ },
+ [BTTV_BOARD_SUPER_TV] = {
+ /* Rick C <cryptdragoon@gmail.com> */
+ .name = "Super TV Tuner",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+- .muxsel = { 2, 3, 1, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .gpiomask = 0x008007,
+ .gpiomux = { 0, 0x000001,0,0 },
+ .needs_tvaudio = 1,
+@@ -2648,17 +2430,15 @@ struct tvcard bttv_tvcards[] = {
+ /* Chris Fanning <video4linux@haydon.net> */
+ .name = "Tibet Systems 'Progress DVR' CS16",
+ .video_inputs = 16,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .svhs = UNSET,
+- .muxsel = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 },
++ /* .audio_inputs= 0, */
++ .svhs = NO_SVHS,
++ .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2),
+ .pll = PLL_28,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .muxsel_hook = tibetCS16_muxsel,
+ },
+ [BTTV_BOARD_KODICOM_4400R] = {
+@@ -2675,12 +2455,10 @@ struct tvcard bttv_tvcards[] = {
+ */
+ .name = "Kodicom 4400R (master)",
+ .video_inputs = 16,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .tuner_type = UNSET,
++ /* .audio_inputs= 0, */
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+- .svhs = UNSET,
++ .svhs = NO_SVHS,
+ /* GPIO bits 0-9 used for analog switch:
+ * 00 - 03: camera selector
+ * 04 - 06: channel (controller) selector
+@@ -2691,7 +2469,7 @@ struct tvcard bttv_tvcards[] = {
+ */
+ .gpiomask = 0x0003ff,
+ .no_gpioirq = 1,
+- .muxsel = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
++ .muxsel = MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3),
+ .pll = PLL_28,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+@@ -2707,15 +2485,13 @@ struct tvcard bttv_tvcards[] = {
+ */
+ .name = "Kodicom 4400R (slave)",
+ .video_inputs = 16,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .tuner_type = UNSET,
++ /* .audio_inputs= 0, */
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+- .svhs = UNSET,
++ .svhs = NO_SVHS,
+ .gpiomask = 0x010000,
+ .no_gpioirq = 1,
+- .muxsel = { 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3 },
++ .muxsel = MUXSEL(3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3),
+ .pll = PLL_28,
+ .no_msp34xx = 1,
+ .no_tda7432 = 1,
+@@ -2728,27 +2504,23 @@ struct tvcard bttv_tvcards[] = {
+ /* Adlink RTV24 with special unlock codes */
+ .name = "Adlink RTV24",
+ .video_inputs = 4,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+- .muxsel = { 2, 3, 1, 0 },
++ .muxsel = MUXSEL(2, 3, 1, 0),
+ .tuner_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ },
+ /* ---- card 0x87---------------------------------- */
+ [BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE] = {
+ /* Michael Krufky <mkrufky@m1k.net> */
+ .name = "DViCO FusionHDTV 5 Lite",
+- .tuner = 0,
+ .tuner_type = TUNER_LG_TDVS_H06XF, /* TDVS-H064F */
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .video_inputs = 3,
+- .audio_inputs = 1,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+- .muxsel = { 2, 3, 1 },
++ .muxsel = MUXSEL(2, 3, 1),
+ .gpiomask = 0x00e00007,
+ .gpiomux = { 0x00400005, 0, 0x00000001, 0 },
+ .gpiomute = 0x00c00007,
+@@ -2762,75 +2534,68 @@ struct tvcard bttv_tvcards[] = {
+ /* Mauro Carvalho Chehab <mchehab@infradead.org> */
+ .name = "Acorp Y878F",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x01fe00,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x001e00, 0, 0x018000, 0x014000 },
+ .gpiomute = 0x002000,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_YMEC_TVF66T5_B_DFF,
+ .tuner_addr = 0xc1 >>1,
+- .radio_addr = 0xc1 >>1,
+ .has_radio = 1,
+ },
+ /* ---- card 0x89 ---------------------------------- */
+ [BTTV_BOARD_CONCEPTRONIC_CTVFMI2] = {
+ .name = "Conceptronic CTVFMi v2",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x001c0007,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 2 },
+ .gpiomute = 3,
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+ .tuner_type = TUNER_TENA_9533_DI,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_remote = 1,
+ .has_radio = 1,
+ },
+ /* ---- card 0x8a ---------------------------------- */
+ [BTTV_BOARD_PV_BT878P_2E] = {
+- .name = "Prolink Pixelview PV-BT878P+ (Rev.2E)",
+- .video_inputs = 5,
+- .audio_inputs = 1,
+- .tuner = 0,
+- .svhs = 3,
+- .gpiomask = 0x01fe00,
+- .muxsel = { 2,3,1,1,-1 },
+- .digital_mode = DIGITAL_MODE_CAMERA,
+- .gpiomux = { 0x00400, 0x10400, 0x04400, 0x80000 },
+- .gpiomute = 0x12400,
+- .no_msp34xx = 1,
+- .pll = PLL_28,
+- .tuner_type = TUNER_LG_PAL_FM,
+- .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+- .has_remote = 1,
++ .name = "Prolink Pixelview PV-BT878P+ (Rev.2E)",
++ .video_inputs = 5,
++ /* .audio_inputs= 1, */
++ .svhs = 3,
++ .has_dig_in = 1,
++ .gpiomask = 0x01fe00,
++ .muxsel = MUXSEL(2, 3, 1, 1, 0), /* in 4 is digital */
++ /* .digital_mode= DIGITAL_MODE_CAMERA, */
++ .gpiomux = { 0x00400, 0x10400, 0x04400, 0x80000 },
++ .gpiomute = 0x12400,
++ .no_msp34xx = 1,
++ .pll = PLL_28,
++ .tuner_type = TUNER_LG_PAL_FM,
++ .tuner_addr = ADDR_UNSET,
++ .has_remote = 1,
+ },
+ /* ---- card 0x8b ---------------------------------- */
+ [BTTV_BOARD_PV_M4900] = {
+ /* Sérgio Fortier <sergiofortier@yahoo.com.br> */
+ .name = "Prolink PixelView PlayTV MPEG2 PV-M4900",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x3f,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x21, 0x20, 0x24, 0x2c },
+ .gpiomute = 0x29,
+ .no_msp34xx = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_YMEC_TVF_5533MF,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .has_radio = 1,
+ .has_remote = 1,
+ },
+@@ -2850,17 +2615,15 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_OSPREY440] = {
+ .name = "Osprey 440",
+ .video_inputs = 4,
+- .audio_inputs = 2, /* this is meaningless */
+- .tuner = UNSET,
+- .svhs = UNSET,
+- .muxsel = { 2, 3, 0, 1 }, /* 3,0,1 are guesses */
++ /* .audio_inputs= 2, */
++ .svhs = NO_SVHS,
++ .muxsel = MUXSEL(2, 3, 0, 1), /* 3,0,1 are guesses */
+ .gpiomask = 0x303,
+ .gpiomute = 0x000, /* int + 32kHz */
+ .gpiomux = { 0, 0, 0x000, 0x100},
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+@@ -2869,28 +2632,25 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_ASOUND_SKYEYE] = {
+ .name = "Asound Skyeye PCTV",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 15,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 2, 0, 0, 0 },
+ .gpiomute = 1,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_NTSC,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ /* ---- card 0x8e ---------------------------------- */
+ [BTTV_BOARD_SABRENT_TVFM] = {
+ .name = "Sabrent TV-FM (bttv version)",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x108007,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 100000, 100002, 100002, 100000 },
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+@@ -2904,17 +2664,15 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_HAUPPAUGE_IMPACTVCB] = {
+ .name = "Hauppauge ImpactVCB (bt878)",
+ .video_inputs = 4,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .svhs = UNSET,
++ /* .audio_inputs= 0, */
++ .svhs = NO_SVHS,
+ .gpiomask = 0x0f, /* old: 7 */
+- .muxsel = { 0, 1, 3, 2 }, /* Composite 0-3 */
++ .muxsel = MUXSEL(0, 1, 3, 2), /* Composite 0-3 */
+ .no_msp34xx = 1,
+ .no_tda9875 = 1,
+ .no_tda7432 = 1,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_MACHTV_MAGICTV] = {
+ /* Julian Calaby <julian.calaby@gmail.com>
+@@ -2926,16 +2684,14 @@ struct tvcard bttv_tvcards[] = {
+
+ .name = "MagicTV", /* rebranded MachTV */
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 7,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 3 },
+ .gpiomute = 4,
+ .tuner_type = TUNER_TEMIC_4009FR5_PAL,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ .has_remote = 1,
+@@ -2943,36 +2699,30 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_SSAI_SECURITY] = {
+ .name = "SSAI Security Video Interface",
+ .video_inputs = 4,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .svhs = UNSET,
+- .muxsel = { 0, 1, 2, 3 },
+- .tuner_type = UNSET,
++ /* .audio_inputs= 0, */
++ .svhs = NO_SVHS,
++ .muxsel = MUXSEL(0, 1, 2, 3),
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_SSAI_ULTRASOUND] = {
+ .name = "SSAI Ultrasound Video Interface",
+ .video_inputs = 2,
+- .audio_inputs = 0,
+- .tuner = UNSET,
++ /* .audio_inputs= 0, */
+ .svhs = 1,
+- .muxsel = { 2, 0, 1, 3 },
+- .tuner_type = UNSET,
++ .muxsel = MUXSEL(2, 0, 1, 3),
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ /* ---- card 0x94---------------------------------- */
+ [BTTV_BOARD_DVICO_FUSIONHDTV_2] = {
+ .name = "DViCO FusionHDTV 2",
+- .tuner = 0,
+ .tuner_type = TUNER_PHILIPS_FCV1236D,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .video_inputs = 3,
+- .audio_inputs = 1,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+- .muxsel = { 2, 3, 1 },
++ .muxsel = MUXSEL(2, 3, 1),
+ .gpiomask = 0x00e00007,
+ .gpiomux = { 0x00400005, 0, 0x00000001, 0 },
+ .gpiomute = 0x00c00007,
+@@ -2984,36 +2734,31 @@ struct tvcard bttv_tvcards[] = {
+ [BTTV_BOARD_TYPHOON_TVTUNERPCI] = {
+ .name = "Typhoon TV-Tuner PCI (50684)",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x3014f,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0x20001,0x10001, 0, 0 },
+ .gpiomute = 10,
+ .needs_tvaudio = 1,
+ .pll = PLL_28,
+ .tuner_type = TUNER_PHILIPS_PAL_I,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_GEOVISION_GV600] = {
+ /* emhn@usb.ve */
+- .name = "Geovision GV-600",
+- .video_inputs = 16,
+- .audio_inputs = 0,
+- .tuner = UNSET,
+- .svhs = UNSET,
+- .gpiomask = 0x0,
+- .muxsel = { 2, 2, 2, 2, 2, 2, 2, 2,
+- 2, 2, 2, 2, 2, 2, 2, 2 },
+- .muxsel_hook = geovision_muxsel,
+- .gpiomux = { 0 },
+- .no_msp34xx = 1,
+- .pll = PLL_28,
+- .tuner_type = UNSET,
+- .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
++ .name = "Geovision GV-600",
++ .video_inputs = 16,
++ /* .audio_inputs= 0, */
++ .svhs = NO_SVHS,
++ .gpiomask = 0x0,
++ .muxsel = MUXSEL(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2),
++ .muxsel_hook = geovision_muxsel,
++ .gpiomux = { 0 },
++ .no_msp34xx = 1,
++ .pll = PLL_28,
++ .tuner_type = TUNER_ABSENT,
++ .tuner_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_KOZUMI_KTV_01C] = {
+ /* Mauro Lacy <mauro@lacy.com.ar>
+@@ -3021,17 +2766,15 @@ struct tvcard bttv_tvcards[] = {
+
+ .name = "Kozumi KTV-01C",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ .gpiomask = 0x008007,
+- .muxsel = { 2, 3, 1, 1 },
++ .muxsel = MUXSEL(2, 3, 1, 1),
+ .gpiomux = { 0, 1, 2, 2 }, /* CONTVFMi */
+ .gpiomute = 3, /* CONTVFMi */
+ .needs_tvaudio = 0,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3, /* TCL MK3 */
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ .has_remote = 1,
+@@ -3041,8 +2784,7 @@ struct tvcard bttv_tvcards[] = {
+ Mauro Carvalho Chehab <mchehab@infradead.org */
+ .name = "Encore ENL TV-FM-2",
+ .video_inputs = 3,
+- .audio_inputs = 1,
+- .tuner = 0,
++ /* .audio_inputs= 1, */
+ .svhs = 2,
+ /* bit 6 -> IR disabled
+ bit 18/17 = 00 -> mute
+@@ -3051,12 +2793,11 @@ struct tvcard bttv_tvcards[] = {
+ 11 -> internal audio input
+ */
+ .gpiomask = 0x060040,
+- .muxsel = { 2, 3, 3 },
++ .muxsel = MUXSEL(2, 3, 3),
+ .gpiomux = { 0x60000, 0x60000, 0x20000, 0x20000 },
+ .gpiomute = 0,
+ .tuner_type = TUNER_TCL_MF02GIP_5N,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ .pll = PLL_28,
+ .has_radio = 1,
+ .has_remote = 1,
+@@ -3065,50 +2806,111 @@ struct tvcard bttv_tvcards[] = {
+ /* D.Heer@Phytec.de */
+ .name = "PHYTEC VD-012 (bt878)",
+ .video_inputs = 4,
+- .audio_inputs = 0,
+- .tuner = UNSET, /* card has no tuner */
+- .svhs = UNSET, /* card has no s-video */
++ /* .audio_inputs= 0, */
++ .svhs = NO_SVHS,
+ .gpiomask = 0x00,
+- .muxsel = { 0, 2, 3, 1 },
++ .muxsel = MUXSEL(0, 2, 3, 1),
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD012_X1] = {
+ /* D.Heer@Phytec.de */
+ .name = "PHYTEC VD-012-X1 (bt878)",
+ .video_inputs = 4,
+- .audio_inputs = 0,
+- .tuner = UNSET, /* card has no tuner */
++ /* .audio_inputs= 0, */
+ .svhs = 3,
+ .gpiomask = 0x00,
+- .muxsel = { 2, 3, 1 },
++ .muxsel = MUXSEL(2, 3, 1),
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+ },
+ [BTTV_BOARD_VD012_X2] = {
+ /* D.Heer@Phytec.de */
+ .name = "PHYTEC VD-012-X2 (bt878)",
+ .video_inputs = 4,
+- .audio_inputs = 0,
+- .tuner = UNSET, /* card has no tuner */
++ /* .audio_inputs= 0, */
+ .svhs = 3,
+ .gpiomask = 0x00,
+- .muxsel = { 3, 2, 1 },
++ .muxsel = MUXSEL(3, 2, 1),
+ .gpiomux = { 0, 0, 0, 0 }, /* card has no audio */
+ .needs_tvaudio = 0,
+ .pll = PLL_28,
+- .tuner_type = UNSET,
++ .tuner_type = TUNER_ABSENT,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
+- }
++ },
++ [BTTV_BOARD_GEOVISION_GV800S] = {
++ /* Bruno Christo <bchristo@inf.ufsm.br>
++ *
++ * GeoVision GV-800(S) has 4 Conexant Fusion 878A:
++ * 1 audio input per BT878A = 4 audio inputs
++ * 4 video inputs per BT878A = 16 video inputs
++ * This is the first BT878A chip of the GV-800(S). It's the
++ * "master" chip and it controls the video inputs through an
++ * analog multiplexer (a CD22M3494) via some GPIO pins. The
++ * slaves should use card type 0x9e (following this one).
++ * There is a EEPROM on the card which is currently not handled.
++ * The audio input is not working yet.
++ */
++ .name = "Geovision GV-800(S) (master)",
++ .video_inputs = 4,
++ /* .audio_inputs= 1, */
++ .tuner_type = TUNER_ABSENT,
++ .tuner_addr = ADDR_UNSET,
++ .svhs = NO_SVHS,
++ .gpiomask = 0xf107f,
++ .no_gpioirq = 1,
++ .muxsel = MUXSEL(2, 2, 2, 2),
++ .pll = PLL_28,
++ .no_msp34xx = 1,
++ .no_tda7432 = 1,
++ .no_tda9875 = 1,
++ .muxsel_hook = gv800s_muxsel,
++ },
++ [BTTV_BOARD_GEOVISION_GV800S_SL] = {
++ /* Bruno Christo <bchristo@inf.ufsm.br>
++ *
++ * GeoVision GV-800(S) has 4 Conexant Fusion 878A:
++ * 1 audio input per BT878A = 4 audio inputs
++ * 4 video inputs per BT878A = 16 video inputs
++ * The 3 other BT878A chips are "slave" chips of the GV-800(S)
++ * and should use this card type.
++ * The audio input is not working yet.
++ */
++ .name = "Geovision GV-800(S) (slave)",
++ .video_inputs = 4,
++ /* .audio_inputs= 1, */
++ .tuner_type = TUNER_ABSENT,
++ .tuner_addr = ADDR_UNSET,
++ .svhs = NO_SVHS,
++ .gpiomask = 0x00,
++ .no_gpioirq = 1,
++ .muxsel = MUXSEL(2, 2, 2, 2),
++ .pll = PLL_28,
++ .no_msp34xx = 1,
++ .no_tda7432 = 1,
++ .no_tda9875 = 1,
++ .muxsel_hook = gv800s_muxsel,
++ },
++ [BTTV_BOARD_PV183] = {
++ .name = "ProVideo PV183", /* 0x9f */
++ .video_inputs = 2,
++ /* .audio_inputs= 0, */
++ .svhs = NO_SVHS,
++ .gpiomask = 0,
++ .muxsel = MUXSEL(2, 3),
++ .gpiomux = { 0 },
++ .needs_tvaudio = 0,
++ .no_msp34xx = 1,
++ .pll = PLL_28,
++ .tuner_type = TUNER_ABSENT,
++ .tuner_addr = ADDR_UNSET,
++ },
+ };
+
+ static const unsigned int bttv_num_tvcards = ARRAY_SIZE(bttv_tvcards);
+@@ -3152,7 +2954,7 @@ void __devinit bttv_idcard(struct bttv *btv)
+ btv->c.nr, btv->cardid & 0xffff,
+ (btv->cardid >> 16) & 0xffff);
+ printk(KERN_DEBUG "please mail id, board name and "
+- "the correct card= insmod option to video4linux-list@redhat.com\n");
++ "the correct card= insmod option to linux-media@vger.kernel.org\n");
+ }
+ }
+
+@@ -3403,8 +3205,7 @@ static void init_ids_eagle(struct bttv *btv)
+ * has its own multiplexer */
+ static void eagle_muxsel(struct bttv *btv, unsigned int input)
+ {
+- btaor((2)<<5, ~(3<<5), BT848_IFORM);
+- gpio_bits(3,bttv_tvcards[btv->c.type].muxsel[input&7]);
++ gpio_bits(3, input & 3);
+
+ /* composite */
+ /* set chroma ADC to sleep */
+@@ -3523,6 +3324,17 @@ void __devinit bttv_init_card1(struct bttv *btv)
+ /* initialization part two -- after registering i2c bus */
+ void __devinit bttv_init_card2(struct bttv *btv)
+ {
++ static const unsigned short tvaudio_addrs[] = {
++ I2C_ADDR_TDA8425 >> 1,
++ I2C_ADDR_TEA6300 >> 1,
++ I2C_ADDR_TEA6420 >> 1,
++ I2C_ADDR_TDA9840 >> 1,
++ I2C_ADDR_TDA985x_L >> 1,
++ I2C_ADDR_TDA985x_H >> 1,
++ I2C_ADDR_TDA9874 >> 1,
++ I2C_ADDR_PIC16C54 >> 1,
++ I2C_CLIENT_END
++ };
+ int addr=ADDR_UNSET;
+
+ btv->tuner_type = UNSET;
+@@ -3629,6 +3441,9 @@ void __devinit bttv_init_card2(struct bttv *btv)
+ case BTTV_BOARD_KODICOM_4400R:
+ kodicom4400r_init(btv);
+ break;
++ case BTTV_BOARD_GEOVISION_GV800S:
++ gv800s_init(btv);
++ break;
+ }
+
+ /* pll configuration */
+@@ -3670,13 +3485,12 @@ void __devinit bttv_init_card2(struct bttv *btv)
+ addr = bttv_tvcards[btv->c.type].tuner_addr;
+
+ if (UNSET != bttv_tvcards[btv->c.type].tuner_type)
+- if(UNSET == btv->tuner_type)
++ if (UNSET == btv->tuner_type)
+ btv->tuner_type = bttv_tvcards[btv->c.type].tuner_type;
+ if (UNSET != tuner[btv->c.nr])
+ btv->tuner_type = tuner[btv->c.nr];
+
+- if (btv->tuner_type == TUNER_ABSENT ||
+- bttv_tvcards[btv->c.type].tuner == UNSET)
++ if (btv->tuner_type == TUNER_ABSENT)
+ printk(KERN_INFO "bttv%d: tuner absent\n", btv->c.nr);
+ else if(btv->tuner_type == UNSET)
+ printk(KERN_WARNING "bttv%d: tuner type unset\n", btv->c.nr);
+@@ -3684,14 +3498,35 @@ void __devinit bttv_init_card2(struct bttv *btv)
+ printk(KERN_INFO "bttv%d: tuner type=%d\n", btv->c.nr,
+ btv->tuner_type);
+
+- if (btv->tuner_type != UNSET) {
++ if (autoload != UNSET) {
++ printk(KERN_WARNING "bttv%d: the autoload option is obsolete.\n", btv->c.nr);
++ printk(KERN_WARNING "bttv%d: use option msp3400, tda7432 or tvaudio to\n", btv->c.nr);
++ printk(KERN_WARNING "bttv%d: override which audio module should be used.\n", btv->c.nr);
++ }
++
++ if (UNSET == btv->tuner_type)
++ btv->tuner_type = TUNER_ABSENT;
++
++ if (btv->tuner_type != TUNER_ABSENT) {
+ struct tuner_setup tun_setup;
+
++ /* Load tuner module before issuing tuner config call! */
++ if (bttv_tvcards[btv->c.type].has_radio)
++ v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap,
++ "tuner", "tuner", v4l2_i2c_tuner_addrs(ADDRS_RADIO));
++ v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap, "tuner",
++ "tuner", v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
++ v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap, "tuner",
++ "tuner", v4l2_i2c_tuner_addrs(ADDRS_TV_WITH_DEMOD));
++
+ tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV;
+ tun_setup.type = btv->tuner_type;
+ tun_setup.addr = addr;
+
+- bttv_call_i2c_clients(btv, TUNER_SET_TYPE_ADDR, &tun_setup);
++ if (bttv_tvcards[btv->c.type].has_radio)
++ tun_setup.mode_mask |= T_RADIO;
++
++ bttv_call_all(btv, tuner, s_type_addr, &tun_setup);
+ }
+
+ if (btv->tda9887_conf) {
+@@ -3700,10 +3535,13 @@ void __devinit bttv_init_card2(struct bttv *btv)
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &btv->tda9887_conf;
+
+- bttv_call_i2c_clients(btv, TUNER_SET_CONFIG, &tda9887_cfg);
++ bttv_call_all(btv, tuner, s_config, &tda9887_cfg);
+ }
+
+- btv->svhs = bttv_tvcards[btv->c.type].svhs;
++ btv->dig = bttv_tvcards[btv->c.type].has_dig_in ?
++ bttv_tvcards[btv->c.type].video_inputs - 1 : UNSET;
++ btv->svhs = bttv_tvcards[btv->c.type].svhs == NO_SVHS ?
++ UNSET : bttv_tvcards[btv->c.type].svhs;
+ if (svhs[btv->c.nr] != UNSET)
+ btv->svhs = svhs[btv->c.nr];
+ if (remote[btv->c.nr] != UNSET)
+@@ -3720,34 +3558,127 @@ void __devinit bttv_init_card2(struct bttv *btv)
+ if (bttv_tvcards[btv->c.type].audio_mode_gpio)
+ btv->audio_mode_gpio=bttv_tvcards[btv->c.type].audio_mode_gpio;
+
+- if (!autoload)
+- return;
+-
+- if (bttv_tvcards[btv->c.type].tuner == UNSET)
++ if (btv->tuner_type == TUNER_ABSENT)
+ return; /* no tuner or related drivers to load */
+
++ if (btv->has_saa6588 || saa6588[btv->c.nr]) {
++ /* Probe for RDS receiver chip */
++ static const unsigned short addrs[] = {
++ 0x20 >> 1,
++ 0x22 >> 1,
++ I2C_CLIENT_END
++ };
++ struct v4l2_subdev *sd;
++
++ sd = v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap,
++ "saa6588", "saa6588", addrs);
++ btv->has_saa6588 = (sd != NULL);
++ }
++
+ /* try to detect audio/fader chips */
+- if (!bttv_tvcards[btv->c.type].no_msp34xx &&
+- bttv_I2CRead(btv, I2C_ADDR_MSP3400, "MSP34xx") >=0)
+- request_module("msp3400");
+
+- if (bttv_tvcards[btv->c.type].msp34xx_alt &&
+- bttv_I2CRead(btv, I2C_ADDR_MSP3400_ALT, "MSP34xx (alternate address)") >=0)
+- request_module("msp3400");
++ /* First check if the user specified the audio chip via a module
++ option. */
++
++ switch (audiodev[btv->c.nr]) {
++ case -1:
++ return; /* do not load any audio module */
++
++ case 0: /* autodetect */
++ break;
++
++ case 1: {
++ /* The user specified that we should probe for msp3400 */
++ static const unsigned short addrs[] = {
++ I2C_ADDR_MSP3400 >> 1,
++ I2C_ADDR_MSP3400_ALT >> 1,
++ I2C_CLIENT_END
++ };
++
++ btv->sd_msp34xx = v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap,
++ "msp3400", "msp3400", addrs);
++ if (btv->sd_msp34xx)
++ return;
++ goto no_audio;
++ }
++
++ case 2: {
++ /* The user specified that we should probe for tda7432 */
++ static const unsigned short addrs[] = {
++ I2C_ADDR_TDA7432 >> 1,
++ I2C_CLIENT_END
++ };
++
++ if (v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap,
++ "tda7432", "tda7432", addrs))
++ return;
++ goto no_audio;
++ }
++
++ case 3: {
++ /* The user specified that we should probe for tvaudio */
++ btv->sd_tvaudio = v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap,
++ "tvaudio", "tvaudio", tvaudio_addrs);
++ if (btv->sd_tvaudio)
++ return;
++ goto no_audio;
++ }
++
++ default:
++ printk(KERN_WARNING "bttv%d: unknown audiodev value!\n",
++ btv->c.nr);
++ return;
++ }
+
+- if (!bttv_tvcards[btv->c.type].no_tda9875 &&
+- bttv_I2CRead(btv, I2C_ADDR_TDA9875, "TDA9875") >=0)
+- request_module("tda9875");
++ /* There were no overrides, so now we try to discover this through the
++ card definition */
++
++ /* probe for msp3400 first: this driver can detect whether or not
++ it really is a msp3400, so it will return NULL when the device
++ found is really something else (e.g. a tea6300). */
++ if (!bttv_tvcards[btv->c.type].no_msp34xx) {
++ static const unsigned short addrs[] = {
++ I2C_ADDR_MSP3400 >> 1,
++ I2C_CLIENT_END
++ };
++
++ btv->sd_msp34xx = v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap,
++ "msp3400", "msp3400", addrs);
++ } else if (bttv_tvcards[btv->c.type].msp34xx_alt) {
++ static const unsigned short addrs[] = {
++ I2C_ADDR_MSP3400_ALT >> 1,
++ I2C_CLIENT_END
++ };
++
++ btv->sd_msp34xx = v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap,
++ "msp3400", "msp3400", addrs);
++ }
+
+- if (!bttv_tvcards[btv->c.type].no_tda7432 &&
+- bttv_I2CRead(btv, I2C_ADDR_TDA7432, "TDA7432") >=0)
+- request_module("tda7432");
++ /* If we found a msp34xx, then we're done. */
++ if (btv->sd_msp34xx)
++ return;
+
+- if (bttv_tvcards[btv->c.type].needs_tvaudio)
+- request_module("tvaudio");
++ /* it might also be a tda7432. */
++ if (!bttv_tvcards[btv->c.type].no_tda7432) {
++ static const unsigned short addrs[] = {
++ I2C_ADDR_TDA7432 >> 1,
++ I2C_CLIENT_END
++ };
+
+- if (btv->tuner_type != UNSET && btv->tuner_type != TUNER_ABSENT)
+- request_module("tuner");
++ if (v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap,
++ "tda7432", "tda7432", addrs))
++ return;
++ }
++
++ /* Now see if we can find one of the tvaudio devices. */
++ btv->sd_tvaudio = v4l2_i2c_new_probed_subdev(&btv->c.i2c_adap,
++ "tvaudio", "tvaudio", tvaudio_addrs);
++ if (btv->sd_tvaudio)
++ return;
++
++no_audio:
++ printk(KERN_WARNING "bttv%d: audio absent, no audio device found!\n",
++ btv->c.nr);
+ }
+
+
+@@ -3819,6 +3750,7 @@ static int terratec_active_radio_upgrade(struct bttv *btv)
+ printk("bttv%d: Terratec Active Radio Upgrade found.\n",
+ btv->c.nr);
+ btv->has_radio = 1;
++ btv->has_saa6588 = 1;
+ btv->has_matchbox = 1;
+ } else {
+ btv->has_radio = 0;
+@@ -4067,27 +3999,26 @@ static void __devinit avermedia_eeprom(struct bttv *btv)
+ btv->has_remote ? "yes" : "no");
+ }
+
+-/* used on Voodoo TV/FM (Voodoo 200), S0 wired to 0x10000 */
+-void bttv_tda9880_setnorm(struct bttv *btv, int norm)
++/*
++ * For Voodoo TV/FM and Voodoo 200. These cards' tuners use a TDA9880
++ * analog demod, which is not I2C controlled like the newer and more common
++ * TDA9887 series. Instead is has two tri-state input pins, S0 and S1,
++ * that control the IF for the video and audio. Apparently, bttv GPIO
++ * 0x10000 is connected to S0. S0 low selects a 38.9 MHz VIF for B/G/D/K/I
++ * (i.e., PAL) while high selects 45.75 MHz for M/N (i.e., NTSC).
++ */
++u32 bttv_tda9880_setnorm(struct bttv *btv, u32 gpiobits)
+ {
+- /* fix up our card entry */
+- 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;
+- bttv_tvcards[BTTV_BOARD_VOODOOTV_200].gpiomute=0x957fff;
+- dprintk("bttv_tda9880_setnorm to NTSC\n");
+- }
+- else {
+- bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].gpiomux[TVAUDIO_INPUT_TUNER]=0x947fff;
+- bttv_tvcards[BTTV_BOARD_VOODOOTV_FM].gpiomute=0x947fff;
+- bttv_tvcards[BTTV_BOARD_VOODOOTV_200].gpiomux[TVAUDIO_INPUT_TUNER]=0x947fff;
+- bttv_tvcards[BTTV_BOARD_VOODOOTV_200].gpiomute=0x947fff;
+- dprintk("bttv_tda9880_setnorm to PAL\n");
++
++ if (btv->audio == TVAUDIO_INPUT_TUNER) {
++ if (bttv_tvnorms[btv->tvnorm].v4l2_id & V4L2_STD_MN)
++ gpiobits |= 0x10000;
++ else
++ gpiobits &= ~0x10000;
+ }
+- /* set GPIO according */
+- gpio_bits(bttv_tvcards[btv->c.type].gpiomask,
+- bttv_tvcards[btv->c.type].gpiomux[btv->audio]);
++
++ gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpiobits);
++ return gpiobits;
+ }
+
+
+@@ -4463,6 +4394,11 @@ void tea5757_set_freq(struct bttv *btv, unsigned short freq)
+ */
+ static void rv605_muxsel(struct bttv *btv, unsigned int input)
+ {
++ static const u8 muxgpio[] = { 0x3, 0x1, 0x2, 0x4, 0xf, 0x7, 0xe, 0x0,
++ 0xd, 0xb, 0xc, 0x6, 0x9, 0x5, 0x8, 0xa };
++
++ gpio_bits(0x07f, muxgpio[input]);
++
+ /* reset all conections */
+ gpio_bits(0x200,0x200);
+ mdelay(1);
+@@ -4470,7 +4406,6 @@ static void rv605_muxsel(struct bttv *btv, unsigned int input)
+ mdelay(1);
+
+ /* create a new connection */
+- gpio_bits(0x480,0x080);
+ gpio_bits(0x480,0x480);
+ mdelay(1);
+ gpio_bits(0x480,0x080);
+@@ -4729,8 +4664,7 @@ static void ivc120_muxsel(struct bttv *btv, unsigned int input)
+ bttv_I2CWrite(btv, I2C_TDA8540_ALT6, 0x02,
+ ((matrix == 2) ? 0x03 : 0x00), 1); /* 9-12 */
+
+- /* Selects MUX0 for input on the 878 */
+- btaor((0)<<5, ~(3<<5), BT848_IFORM);
++ /* 878's MUX0 is already selected for input via muxsel values */
+ }
+
+
+@@ -4814,6 +4748,132 @@ static void PXC200_muxsel(struct bttv *btv, unsigned int input)
+ printk(KERN_DEBUG "bttv%d: setting input channel to:%d\n", btv->c.nr,(int)mux);
+ }
+
++static void phytec_muxsel(struct bttv *btv, unsigned int input)
++{
++ unsigned int mux = input % 4;
++
++ if (input == btv->svhs)
++ mux = 0;
++
++ gpio_bits(0x3, mux);
++}
++
++/*
++ * GeoVision GV-800(S) functions
++ * Bruno Christo <bchristo@inf.ufsm.br>
++*/
++
++/* This is a function to control the analog switch, which determines which
++ * camera is routed to which controller. The switch comprises an X-address
++ * (gpio bits 0-3, representing the camera, ranging from 0-15), and a
++ * Y-address (gpio bits 4-6, representing the controller, ranging from 0-3).
++ * A data value (gpio bit 18) of '1' enables the switch, and '0' disables
++ * the switch. A STROBE bit (gpio bit 17) latches the data value into the
++ * specified address. There is also a chip select (gpio bit 16).
++ * The idea is to set the address and chip select together, bring
++ * STROBE high, write the data, and finally bring STROBE back to low.
++ */
++static void gv800s_write(struct bttv *btv,
++ unsigned char xaddr,
++ unsigned char yaddr,
++ unsigned char data) {
++ /* On the "master" 878A:
++ * GPIO bits 0-9 are used for the analog switch:
++ * 00 - 03: camera selector
++ * 04 - 06: 878A (controller) selector
++ * 16: cselect
++ * 17: strobe
++ * 18: data (1->on, 0->off)
++ * 19: reset
++ */
++ const u32 ADDRESS = ((xaddr&0xf) | (yaddr&3)<<4);
++ const u32 CSELECT = 1<<16;
++ const u32 STROBE = 1<<17;
++ const u32 DATA = data<<18;
++
++ gpio_bits(0x1007f, ADDRESS | CSELECT); /* write ADDRESS and CSELECT */
++ gpio_bits(0x20000, STROBE); /* STROBE high */
++ gpio_bits(0x40000, DATA); /* write DATA */
++ gpio_bits(0x20000, ~STROBE); /* STROBE low */
++}
++
++/*
++ * GeoVision GV-800(S) muxsel
++ *
++ * Each of the 4 cards (controllers) use this function.
++ * The controller using this function selects the input through the GPIO pins
++ * of the "master" card. A pointer to this card is stored in master[btv->c.nr].
++ *
++ * The parameter 'input' is the requested camera number (0-4) on the controller.
++ * The map array has the address of each input. Note that the addresses in the
++ * array are in the sequence the original GeoVision driver uses, that is, set
++ * every controller to input 0, then to input 1, 2, 3, repeat. This means that
++ * the physical "camera 1" connector corresponds to controller 0 input 0,
++ * "camera 2" corresponds to controller 1 input 0, and so on.
++ *
++ * After getting the input address, the function then writes the appropriate
++ * data to the analog switch, and housekeeps the local copy of the switch
++ * information.
++ */
++static void gv800s_muxsel(struct bttv *btv, unsigned int input)
++{
++ struct bttv *mctlr;
++ char *sw_status;
++ int xaddr, yaddr;
++ static unsigned int map[4][4] = { { 0x0, 0x4, 0xa, 0x6 },
++ { 0x1, 0x5, 0xb, 0x7 },
++ { 0x2, 0x8, 0xc, 0xe },
++ { 0x3, 0x9, 0xd, 0xf } };
++ input = input%4;
++ mctlr = master[btv->c.nr];
++ if (mctlr == NULL) {
++ /* do nothing until the "master" is detected */
++ return;
++ }
++ yaddr = (btv->c.nr - mctlr->c.nr) & 3;
++ sw_status = (char *)(&mctlr->mbox_we);
++ xaddr = map[yaddr][input] & 0xf;
++
++ /* Check if the controller/camera pair has changed, ignore otherwise */
++ if (sw_status[yaddr] != xaddr) {
++ /* disable the old switch, enable the new one and save status */
++ gv800s_write(mctlr, sw_status[yaddr], yaddr, 0);
++ sw_status[yaddr] = xaddr;
++ gv800s_write(mctlr, xaddr, yaddr, 1);
++ }
++}
++
++/* GeoVision GV-800(S) "master" chip init */
++static void gv800s_init(struct bttv *btv)
++{
++ char *sw_status = (char *)(&btv->mbox_we);
++ int ix;
++
++ gpio_inout(0xf107f, 0xf107f);
++ gpio_write(1<<19); /* reset the analog MUX */
++ gpio_write(0);
++
++ /* Preset camera 0 to the 4 controllers */
++ for (ix = 0; ix < 4; ix++) {
++ sw_status[ix] = ix;
++ gv800s_write(btv, ix, ix, 1);
++ }
++
++ /* Inputs on the "master" controller need this brightness fix */
++ bttv_I2CWrite(btv, 0x18, 0x5, 0x90, 1);
++
++ if (btv->c.nr > BTTV_MAX-4)
++ return;
++ /*
++ * Store the "master" controller pointer in the master
++ * array for later use in the muxsel function.
++ */
++ master[btv->c.nr] = btv;
++ master[btv->c.nr+1] = btv;
++ master[btv->c.nr+2] = btv;
++ master[btv->c.nr+3] = btv;
++}
++
+ /* ----------------------------------------------------------------------- */
+ /* motherboard chipset specific stuff */
+
+diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
+index c71f394..7a8ca0d 100644
+--- a/drivers/media/video/bt8xx/bttv-driver.c
++++ b/drivers/media/video/bt8xx/bttv-driver.c
+@@ -58,7 +58,7 @@
+
+
+ unsigned int bttv_num; /* number of Bt848s in use */
+-struct bttv bttvs[BTTV_MAX];
++struct bttv *bttvs[BTTV_MAX];
+
+ unsigned int bttv_debug;
+ unsigned int bttv_verbose = 1;
+@@ -167,7 +167,7 @@ static ssize_t show_card(struct device *cd,
+ struct device_attribute *attr, char *buf)
+ {
+ struct video_device *vfd = container_of(cd, struct video_device, dev);
+- struct bttv *btv = dev_get_drvdata(vfd->parent);
++ struct bttv *btv = video_get_drvdata(vfd);
+ return sprintf(buf, "%d\n", btv ? btv->c.type : UNSET);
+ }
+ static DEVICE_ATTR(card, S_IRUGO, show_card, NULL);
+@@ -1040,7 +1040,7 @@ static void bt848A_set_timing(struct bttv *btv)
+ int table_idx = bttv_tvnorms[btv->tvnorm].sram;
+ int fsc = bttv_tvnorms[btv->tvnorm].Fsc;
+
+- if (UNSET == bttv_tvcards[btv->c.type].muxsel[btv->input]) {
++ if (btv->input == btv->dig) {
+ dprintk("bttv%d: load digital timing table (table_idx=%d)\n",
+ btv->c.nr,table_idx);
+
+@@ -1142,7 +1142,7 @@ video_mux(struct bttv *btv, unsigned int input)
+ btand(~BT848_CONTROL_COMP, BT848_E_CONTROL);
+ btand(~BT848_CONTROL_COMP, BT848_O_CONTROL);
+ }
+- mux = bttv_tvcards[btv->c.type].muxsel[input] & 3;
++ mux = bttv_muxsel(btv, input);
+ btaor(mux<<5, ~(3<<5), BT848_IFORM);
+ dprintk(KERN_DEBUG "bttv%d: video mux: input=%d mux=%d\n",
+ btv->c.nr,input,mux);
+@@ -1163,7 +1163,6 @@ audio_mux(struct bttv *btv, int input, int mute)
+ {
+ int gpio_val, signal;
+ struct v4l2_control ctrl;
+- struct i2c_client *c;
+
+ gpio_inout(bttv_tvcards[btv->c.type].gpiomask,
+ bttv_tvcards[btv->c.type].gpiomask);
+@@ -1180,7 +1179,16 @@ audio_mux(struct bttv *btv, int input, int mute)
+ else
+ gpio_val = bttv_tvcards[btv->c.type].gpiomux[input];
+
+- gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpio_val);
++ switch (btv->c.type) {
++ case BTTV_BOARD_VOODOOTV_FM:
++ case BTTV_BOARD_VOODOOTV_200:
++ gpio_val = bttv_tda9880_setnorm(btv, gpio_val);
++ break;
++
++ default:
++ gpio_bits(bttv_tvcards[btv->c.type].gpiomask, gpio_val);
++ }
++
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv, audio_modes[mute ? 4 : input]);
+ if (in_interrupt())
+@@ -1188,9 +1196,8 @@ audio_mux(struct bttv *btv, int input, int mute)
+
+ ctrl.id = V4L2_CID_AUDIO_MUTE;
+ ctrl.value = btv->mute;
+- bttv_call_i2c_clients(btv, VIDIOC_S_CTRL, &ctrl);
+- c = btv->i2c_msp34xx_client;
+- if (c) {
++ bttv_call_all(btv, core, s_ctrl, &ctrl);
++ if (btv->sd_msp34xx) {
+ struct v4l2_routing route;
+
+ /* Note: the inputs tuner/radio/extern/intern are translated
+@@ -1229,15 +1236,14 @@ audio_mux(struct bttv *btv, int input, int mute)
+ break;
+ }
+ route.output = MSP_OUTPUT_DEFAULT;
+- c->driver->command(c, VIDIOC_INT_S_AUDIO_ROUTING, &route);
++ v4l2_subdev_call(btv->sd_msp34xx, audio, s_routing, &route);
+ }
+- c = btv->i2c_tvaudio_client;
+- if (c) {
++ if (btv->sd_tvaudio) {
+ struct v4l2_routing route;
+
+ route.input = input;
+ route.output = 0;
+- c->driver->command(c, VIDIOC_INT_S_AUDIO_ROUTING, &route);
++ v4l2_subdev_call(btv->sd_tvaudio, audio, s_routing, &route);
+ }
+ return 0;
+ }
+@@ -1277,7 +1283,7 @@ bttv_crop_calc_limits(struct bttv_crop *c)
+ }
+
+ static void
+-bttv_crop_reset(struct bttv_crop *c, int norm)
++bttv_crop_reset(struct bttv_crop *c, unsigned int norm)
+ {
+ c->rect = bttv_tvnorms[norm].cropcap.defrect;
+ bttv_crop_calc_limits(c);
+@@ -1290,16 +1296,13 @@ 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;
++ BUG_ON(norm >= BTTV_TVNORMS);
++ BUG_ON(btv->tvnorm >= BTTV_TVNORMS);
+
+ tvnorm = &bttv_tvnorms[norm];
+
+- if (btv->tvnorm < 0 ||
+- btv->tvnorm >= BTTV_TVNORMS ||
+- 0 != memcmp(&bttv_tvnorms[btv->tvnorm].cropcap,
+- &tvnorm->cropcap,
+- sizeof (tvnorm->cropcap))) {
++ if (!memcmp(&bttv_tvnorms[btv->tvnorm].cropcap, &tvnorm->cropcap,
++ sizeof (tvnorm->cropcap))) {
+ bttv_crop_reset(&btv->crop[0], norm);
+ btv->crop[1] = btv->crop[0]; /* current = default */
+
+@@ -1322,11 +1325,11 @@ set_tvnorm(struct bttv *btv, unsigned int norm)
+ switch (btv->c.type) {
+ case BTTV_BOARD_VOODOOTV_FM:
+ case BTTV_BOARD_VOODOOTV_200:
+- bttv_tda9880_setnorm(btv,norm);
++ bttv_tda9880_setnorm(btv, gpio_read());
+ break;
+ }
+ id = tvnorm->v4l2_id;
+- bttv_call_i2c_clients(btv, VIDIOC_S_STD, &id);
++ bttv_call_all(btv, tuner, s_std, id);
+
+ return 0;
+ }
+@@ -1350,8 +1353,8 @@ set_input(struct bttv *btv, unsigned int input, unsigned int norm)
+ } else {
+ video_mux(btv,input);
+ }
+- audio_input(btv,(input == bttv_tvcards[btv->c.type].tuner ?
+- TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN));
++ audio_input(btv, (btv->tuner_type != TUNER_ABSENT && input == 0) ?
++ TVAUDIO_INPUT_TUNER : TVAUDIO_INPUT_EXTERN);
+ set_tvnorm(btv, norm);
+ }
+
+@@ -1470,7 +1473,7 @@ static int bttv_g_ctrl(struct file *file, void *priv,
+ 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_all(btv, core, g_ctrl, c);
+ break;
+
+ case V4L2_CID_PRIVATE_CHROMA_AGC:
+@@ -1544,12 +1547,12 @@ static int bttv_s_ctrl(struct file *file, void *f,
+ if (btv->volume_gpio)
+ btv->volume_gpio(btv, c->value);
+
+- bttv_call_i2c_clients(btv, VIDIOC_S_CTRL, c);
++ bttv_call_all(btv, core, 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_all(btv, core, s_ctrl, c);
+ break;
+
+ case V4L2_CID_PRIVATE_CHROMA_AGC:
+@@ -1888,20 +1891,15 @@ static int bttv_enum_input(struct file *file, void *priv,
+ {
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+- unsigned int n;
+-
+- n = i->index;
++ int n;
+
+- if (n >= bttv_tvcards[btv->c.type].video_inputs)
++ if (i->index >= 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) {
++ if (btv->tuner_type != TUNER_ABSENT && i->index == 0) {
+ sprintf(i->name, "Television");
+ i->type = V4L2_INPUT_TYPE_TUNER;
+ i->tuner = 0;
+@@ -1965,14 +1963,14 @@ static int bttv_s_tuner(struct file *file, void *priv,
+ if (0 != err)
+ return err;
+
+- if (UNSET == bttv_tvcards[btv->c.type].tuner)
++ if (btv->tuner_type == TUNER_ABSENT)
+ return -EINVAL;
+
+ if (0 != t->index)
+ return -EINVAL;
+
+ mutex_lock(&btv->lock);
+- bttv_call_i2c_clients(btv, VIDIOC_S_TUNER, t);
++ bttv_call_all(btv, tuner, s_tuner, t);
+
+ if (btv->audio_mode_gpio)
+ btv->audio_mode_gpio(btv, t, 1);
+@@ -2017,7 +2015,7 @@ static int bttv_s_frequency(struct file *file, void *priv,
+ return -EINVAL;
+ mutex_lock(&btv->lock);
+ btv->freq = f->frequency;
+- bttv_call_i2c_clients(btv, VIDIOC_S_FREQUENCY, f);
++ bttv_call_all(btv, tuner, s_frequency, f);
+ if (btv->has_matchbox && btv->radio_user)
+ tea5757_set_freq(btv, btv->freq);
+ mutex_unlock(&btv->lock);
+@@ -2031,7 +2029,7 @@ static int bttv_log_status(struct file *file, void *f)
+
+ 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);
++ bttv_call_all(btv, core, log_status);
+ printk(KERN_INFO "bttv%d: ======== END STATUS CARD #%d ========\n",
+ btv->c.nr, btv->c.nr);
+ return 0;
+@@ -2659,8 +2657,7 @@ static int bttv_querycap(struct file *file, void *priv,
+ 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)
++ if (btv->tuner_type != TUNER_ABSENT)
+ cap->capabilities |= V4L2_CAP_TUNER;
+ return 0;
+ }
+@@ -2927,13 +2924,9 @@ static int bttv_g_parm(struct file *file, void *f,
+ {
+ struct bttv_fh *fh = f;
+ struct bttv *btv = fh->btv;
+- struct v4l2_standard s;
+
+- 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;
++ v4l2_video_std_frame_period(bttv_tvnorms[btv->tvnorm].v4l2_id,
++ &parm->parm.capture.timeperframe);
+ return 0;
+ }
+
+@@ -2943,15 +2936,14 @@ static int bttv_g_tuner(struct file *file, void *priv,
+ struct bttv_fh *fh = priv;
+ struct bttv *btv = fh->btv;
+
+- if (UNSET == bttv_tvcards[btv->c.type].tuner)
++ if (btv->tuner_type == TUNER_ABSENT)
+ 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);
++ bttv_call_all(btv, tuner, g_tuner, t);
+ strcpy(t->name, "Television");
+ t->capability = V4L2_TUNER_CAP_NORM;
+ t->type = V4L2_TUNER_ANALOG_TV;
+@@ -3212,29 +3204,19 @@ err:
+ static int bttv_open(struct file *file)
+ {
+ int minor = video_devdata(file)->minor;
+- struct bttv *btv = NULL;
++ struct bttv *btv = video_drvdata(file);
+ struct bttv_fh *fh;
+ enum v4l2_buf_type type = 0;
+- unsigned int i;
+
+ dprintk(KERN_DEBUG "bttv: open minor=%d\n",minor);
+
+ lock_kernel();
+- for (i = 0; i < bttv_num; i++) {
+- if (bttvs[i].video_dev &&
+- bttvs[i].video_dev->minor == minor) {
+- btv = &bttvs[i];
+- type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+- break;
+- }
+- if (bttvs[i].vbi_dev &&
+- bttvs[i].vbi_dev->minor == minor) {
+- btv = &bttvs[i];
+- type = V4L2_BUF_TYPE_VBI_CAPTURE;
+- break;
+- }
+- }
+- if (NULL == btv) {
++ if (btv->video_dev->minor == minor) {
++ type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ } else if (btv->vbi_dev->minor == minor) {
++ type = V4L2_BUF_TYPE_VBI_CAPTURE;
++ } else {
++ WARN_ON(1);
+ unlock_kernel();
+ return -ENODEV;
+ }
+@@ -3424,20 +3406,14 @@ static struct video_device bttv_video_template = {
+ static int radio_open(struct file *file)
+ {
+ int minor = video_devdata(file)->minor;
+- struct bttv *btv = NULL;
++ struct bttv *btv = video_drvdata(file);
+ struct bttv_fh *fh;
+- unsigned int i;
+
+ dprintk("bttv: open minor=%d\n",minor);
+
+ lock_kernel();
+- for (i = 0; i < bttv_num; i++) {
+- if (bttvs[i].radio_dev && bttvs[i].radio_dev->minor == minor) {
+- btv = &bttvs[i];
+- break;
+- }
+- }
+- if (NULL == btv) {
++ WARN_ON(btv->radio_dev && btv->radio_dev->minor != minor);
++ if (!btv->radio_dev || btv->radio_dev->minor != minor) {
+ unlock_kernel();
+ return -ENODEV;
+ }
+@@ -3458,7 +3434,7 @@ static int radio_open(struct file *file)
+
+ btv->radio_user++;
+
+- bttv_call_i2c_clients(btv,AUDC_SET_RADIO,NULL);
++ bttv_call_all(btv, tuner, s_radio);
+ audio_input(btv,TVAUDIO_INPUT_RADIO);
+
+ mutex_unlock(&btv->lock);
+@@ -3478,7 +3454,7 @@ static int radio_release(struct file *file)
+
+ btv->radio_user--;
+
+- bttv_call_i2c_clients(btv, RDS_CMD_CLOSE, &cmd);
++ bttv_call_all(btv, core, ioctl, RDS_CMD_CLOSE, &cmd);
+
+ return 0;
+ }
+@@ -3503,16 +3479,15 @@ 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 (UNSET == bttv_tvcards[btv->c.type].tuner)
++ if (btv->tuner_type == TUNER_ABSENT)
+ 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;
+
+- bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t);
++ bttv_call_all(btv, tuner, g_tuner, t);
+
+ if (btv->audio_mode_gpio)
+ btv->audio_mode_gpio(btv, t, 0);
+@@ -3554,7 +3529,7 @@ static int radio_s_tuner(struct file *file, void *priv,
+ if (0 != t->index)
+ return -EINVAL;
+
+- bttv_call_i2c_clients(btv, VIDIOC_G_TUNER, t);
++ bttv_call_all(btv, tuner, g_tuner, t);
+ return 0;
+ }
+
+@@ -3615,7 +3590,7 @@ static ssize_t radio_read(struct file *file, char __user *data,
+ cmd.instance = file;
+ cmd.result = -ENODEV;
+
+- bttv_call_i2c_clients(btv, RDS_CMD_READ, &cmd);
++ bttv_call_all(btv, core, ioctl, RDS_CMD_READ, &cmd);
+
+ return cmd.result;
+ }
+@@ -3628,7 +3603,7 @@ static unsigned int radio_poll(struct file *file, poll_table *wait)
+ cmd.instance = file;
+ cmd.event_list = wait;
+ cmd.result = -ENODEV;
+- bttv_call_i2c_clients(btv, RDS_CMD_POLL, &cmd);
++ bttv_call_all(btv, core, ioctl, RDS_CMD_POLL, &cmd);
+
+ return cmd.result;
+ }
+@@ -3712,14 +3687,14 @@ static void bttv_risc_disasm(struct bttv *btv,
+ unsigned int i,j,n;
+
+ printk("%s: risc disasm: %p [dma=0x%08lx]\n",
+- btv->c.name, risc->cpu, (unsigned long)risc->dma);
++ btv->c.v4l2_dev.name, risc->cpu, (unsigned long)risc->dma);
+ for (i = 0; i < (risc->size >> 2); i += n) {
+- printk("%s: 0x%lx: ", btv->c.name,
++ printk("%s: 0x%lx: ", btv->c.v4l2_dev.name,
+ (unsigned long)(risc->dma + (i<<2)));
+ n = bttv_risc_decode(le32_to_cpu(risc->cpu[i]));
+ for (j = 1; j < n; j++)
+ printk("%s: 0x%lx: 0x%08x [ arg #%d ]\n",
+- btv->c.name, (unsigned long)(risc->dma + ((i+j)<<2)),
++ btv->c.v4l2_dev.name, (unsigned long)(risc->dma + ((i+j)<<2)),
+ risc->cpu[i+j], j);
+ if (0 == risc->cpu[i])
+ break;
+@@ -4195,9 +4170,10 @@ static struct video_device *vdev_init(struct bttv *btv,
+ return NULL;
+ *vfd = *template;
+ vfd->minor = -1;
+- vfd->parent = &btv->c.pci->dev;
++ vfd->v4l2_dev = &btv->c.v4l2_dev;
+ vfd->release = video_device_release;
+ vfd->debug = bttv_debug;
++ video_set_drvdata(vfd, btv);
+ snprintf(vfd->name, sizeof(vfd->name), "BT%d%s %s (%s)",
+ btv->id, (btv->id==848 && btv->revision==0x12) ? "A" : "",
+ type_name, bttv_tvcards[btv->c.type].name);
+@@ -4307,10 +4283,14 @@ static int __devinit bttv_probe(struct pci_dev *dev,
+ if (bttv_num == BTTV_MAX)
+ return -ENOMEM;
+ printk(KERN_INFO "bttv: Bt8xx card found (%d).\n", bttv_num);
+- btv=&bttvs[bttv_num];
+- memset(btv,0,sizeof(*btv));
++ bttvs[bttv_num] = btv = kzalloc(sizeof(*btv), GFP_KERNEL);
++ if (btv == NULL) {
++ printk(KERN_ERR "bttv: out of memory.\n");
++ return -ENOMEM;
++ }
+ btv->c.nr = bttv_num;
+- sprintf(btv->c.name,"bttv%d",btv->c.nr);
++ snprintf(btv->c.v4l2_dev.name, sizeof(btv->c.v4l2_dev.name),
++ "bttv%d", btv->c.nr);
+
+ /* initialize structs / fill in defaults */
+ mutex_init(&btv->lock);
+@@ -4347,7 +4327,7 @@ static int __devinit bttv_probe(struct pci_dev *dev,
+ }
+ if (!request_mem_region(pci_resource_start(dev,0),
+ pci_resource_len(dev,0),
+- btv->c.name)) {
++ btv->c.v4l2_dev.name)) {
+ printk(KERN_WARNING "bttv%d: can't request iomem (0x%llx).\n",
+ btv->c.nr,
+ (unsigned long long)pci_resource_start(dev,0));
+@@ -4355,7 +4335,12 @@ static int __devinit bttv_probe(struct pci_dev *dev,
+ }
+ pci_set_master(dev);
+ pci_set_command(dev);
+- pci_set_drvdata(dev,btv);
++
++ result = v4l2_device_register(&dev->dev, &btv->c.v4l2_dev);
++ if (result < 0) {
++ printk(KERN_WARNING "bttv%d: v4l2_device_register() failed\n", btv->c.nr);
++ goto fail0;
++ }
+
+ pci_read_config_byte(dev, PCI_CLASS_REVISION, &btv->revision);
+ pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
+@@ -4379,7 +4364,7 @@ static int __devinit bttv_probe(struct pci_dev *dev,
+ /* disable irqs, register irq handler */
+ btwrite(0, BT848_INT_MASK);
+ result = request_irq(btv->c.pci->irq, bttv_irq,
+- IRQF_SHARED | IRQF_DISABLED,btv->c.name,(void *)btv);
++ IRQF_SHARED | IRQF_DISABLED, btv->c.v4l2_dev.name, (void *)btv);
+ if (result < 0) {
+ printk(KERN_ERR "bttv%d: can't get IRQ %d\n",
+ bttv_num,btv->c.pci->irq);
+@@ -4463,21 +4448,24 @@ static int __devinit bttv_probe(struct pci_dev *dev,
+ bttv_num++;
+ return 0;
+
+- fail2:
++fail2:
+ free_irq(btv->c.pci->irq,btv);
+
+- fail1:
++fail1:
++ v4l2_device_unregister(&btv->c.v4l2_dev);
++
++fail0:
+ if (btv->bt848_mmio)
+ iounmap(btv->bt848_mmio);
+ release_mem_region(pci_resource_start(btv->c.pci,0),
+ pci_resource_len(btv->c.pci,0));
+- pci_set_drvdata(dev,NULL);
+ return result;
+ }
+
+ static void __devexit bttv_remove(struct pci_dev *pci_dev)
+ {
+- struct bttv *btv = pci_get_drvdata(pci_dev);
++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
++ struct bttv *btv = to_bttv(v4l2_dev);
+
+ if (bttv_verbose)
+ printk("bttv%d: unloading\n",btv->c.nr);
+@@ -4511,14 +4499,18 @@ static void __devexit bttv_remove(struct pci_dev *pci_dev)
+ release_mem_region(pci_resource_start(btv->c.pci,0),
+ pci_resource_len(btv->c.pci,0));
+
+- pci_set_drvdata(pci_dev, NULL);
++ v4l2_device_unregister(&btv->c.v4l2_dev);
++ bttvs[btv->c.nr] = NULL;
++ kfree(btv);
++
+ return;
+ }
+
+ #ifdef CONFIG_PM
+ static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state)
+ {
+- struct bttv *btv = pci_get_drvdata(pci_dev);
++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
++ struct bttv *btv = to_bttv(v4l2_dev);
+ struct bttv_buffer_set idle;
+ unsigned long flags;
+
+@@ -4553,7 +4545,8 @@ static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state)
+
+ static int bttv_resume(struct pci_dev *pci_dev)
+ {
+- struct bttv *btv = pci_get_drvdata(pci_dev);
++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
++ struct bttv *btv = to_bttv(v4l2_dev);
+ unsigned long flags;
+ int err;
+
+diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c
+index bcd2cd2..a99d92f 100644
+--- a/drivers/media/video/bt8xx/bttv-i2c.c
++++ b/drivers/media/video/bt8xx/bttv-i2c.c
+@@ -36,8 +36,6 @@
+ #include <linux/jiffies.h>
+ #include <asm/io.h>
+
+-static int attach_inform(struct i2c_client *client);
+-
+ static int i2c_debug;
+ static int i2c_hw;
+ static int i2c_scan;
+@@ -231,7 +229,8 @@ bttv_i2c_readbytes(struct bttv *btv, const struct i2c_msg *msg, int last)
+
+ static int bttv_i2c_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
+ {
+- struct bttv *btv = i2c_get_adapdata(i2c_adap);
++ struct v4l2_device *v4l2_dev = i2c_get_adapdata(i2c_adap);
++ struct bttv *btv = to_bttv(v4l2_dev);
+ int retval = 0;
+ int i;
+
+@@ -265,52 +264,6 @@ static const struct i2c_algorithm bttv_algo = {
+ /* ----------------------------------------------------------------------- */
+ /* I2C functions - common stuff */
+
+-static int attach_inform(struct i2c_client *client)
+-{
+- struct bttv *btv = i2c_get_adapdata(client->adapter);
+- int addr=ADDR_UNSET;
+-
+-
+- if (ADDR_UNSET != bttv_tvcards[btv->c.type].tuner_addr)
+- addr = bttv_tvcards[btv->c.type].tuner_addr;
+-
+-
+- if (bttv_debug)
+- printk(KERN_DEBUG "bttv%d: %s i2c attach [addr=0x%x,client=%s]\n",
+- btv->c.nr, client->driver->driver.name, client->addr,
+- client->name);
+- if (!client->driver->command)
+- return 0;
+-
+- if (client->driver->id == I2C_DRIVERID_MSP3400)
+- btv->i2c_msp34xx_client = client;
+- if (client->driver->id == I2C_DRIVERID_TVAUDIO)
+- btv->i2c_tvaudio_client = client;
+- if (btv->tuner_type != UNSET) {
+- struct tuner_setup tun_setup;
+-
+- if ((addr==ADDR_UNSET) ||
+- (addr==client->addr)) {
+-
+- tun_setup.mode_mask = T_ANALOG_TV | T_DIGITAL_TV | T_RADIO;
+- tun_setup.type = btv->tuner_type;
+- tun_setup.addr = addr;
+- bttv_call_i2c_clients(btv, TUNER_SET_TYPE_ADDR, &tun_setup);
+- }
+-
+- }
+-
+- return 0;
+-}
+-
+-void bttv_call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg)
+-{
+- if (0 != btv->i2c_rc)
+- return;
+- i2c_clients_command(&btv->c.i2c_adap, cmd, arg);
+-}
+-
+-
+ /* read I2C */
+ int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for)
+ {
+@@ -417,21 +370,15 @@ int __devinit init_bttv_i2c(struct bttv *btv)
+ btv->c.i2c_adap.algo_data = &btv->i2c_algo;
+ }
+ btv->c.i2c_adap.owner = THIS_MODULE;
+- btv->c.i2c_adap.class = I2C_CLASS_TV_ANALOG;
+- btv->c.i2c_adap.client_register = attach_inform;
+
+ btv->c.i2c_adap.dev.parent = &btv->c.pci->dev;
+ snprintf(btv->c.i2c_adap.name, sizeof(btv->c.i2c_adap.name),
+ "bt%d #%d [%s]", btv->id, btv->c.nr,
+ btv->use_i2c_hw ? "hw" : "sw");
+
+- i2c_set_adapdata(&btv->c.i2c_adap, btv);
++ i2c_set_adapdata(&btv->c.i2c_adap, &btv->c.v4l2_dev);
+ btv->i2c_client.adapter = &btv->c.i2c_adap;
+
+- if (bttv_tvcards[btv->c.type].no_video)
+- btv->c.i2c_adap.class &= ~I2C_CLASS_TV_ANALOG;
+- if (bttv_tvcards[btv->c.type].has_dvb)
+- btv->c.i2c_adap.class |= I2C_CLASS_TV_DIGITAL;
+
+ if (btv->use_i2c_hw) {
+ btv->i2c_rc = i2c_add_adapter(&btv->c.i2c_adap);
+@@ -441,7 +388,7 @@ int __devinit init_bttv_i2c(struct bttv *btv)
+ btv->i2c_rc = i2c_bit_add_bus(&btv->c.i2c_adap);
+ }
+ if (0 == btv->i2c_rc && i2c_scan)
+- do_i2c_scan(btv->c.name,&btv->i2c_client);
++ do_i2c_scan(btv->c.v4l2_dev.name, &btv->i2c_client);
+ return btv->i2c_rc;
+ }
+
+diff --git a/drivers/media/video/bt8xx/bttv-if.c b/drivers/media/video/bt8xx/bttv-if.c
+index ecf0798..a6a540d 100644
+--- a/drivers/media/video/bt8xx/bttv-if.c
++++ b/drivers/media/video/bt8xx/bttv-if.c
+@@ -47,7 +47,10 @@ struct pci_dev* bttv_get_pcidev(unsigned int card)
+ {
+ if (card >= bttv_num)
+ return NULL;
+- return bttvs[card].c.pci;
++ if (!bttvs[card])
++ return NULL;
++
++ return bttvs[card]->c.pci;
+ }
+
+
+@@ -59,7 +62,10 @@ int bttv_gpio_enable(unsigned int card, unsigned long mask, unsigned long data)
+ return -EINVAL;
+ }
+
+- btv = &bttvs[card];
++ btv = bttvs[card];
++ if (!btv)
++ return -ENODEV;
++
+ gpio_inout(mask,data);
+ if (bttv_gpio)
+ bttv_gpio_tracking(btv,"extern enable");
+@@ -74,7 +80,9 @@ int bttv_read_gpio(unsigned int card, unsigned long *data)
+ return -EINVAL;
+ }
+
+- btv = &bttvs[card];
++ btv = bttvs[card];
++ if (!btv)
++ return -ENODEV;
+
+ if(btv->shutdown) {
+ return -ENODEV;
+@@ -94,7 +102,9 @@ int bttv_write_gpio(unsigned int card, unsigned long mask, unsigned long data)
+ return -EINVAL;
+ }
+
+- btv = &bttvs[card];
++ btv = bttvs[card];
++ if (!btv)
++ return -ENODEV;
+
+ /* prior setting BT848_GPIO_REG_INP is (probably) not needed
+ because direct input is set on init */
+diff --git a/drivers/media/video/bt8xx/bttv-risc.c b/drivers/media/video/bt8xx/bttv-risc.c
+index 5b1b8e4..d16af28 100644
+--- a/drivers/media/video/bt8xx/bttv-risc.c
++++ b/drivers/media/video/bt8xx/bttv-risc.c
+@@ -341,7 +341,7 @@ bttv_calc_geo_old(struct bttv *btv, struct bttv_geometry *geo,
+ int totalwidth = tvnorm->totalwidth;
+ int scaledtwidth = tvnorm->scaledtwidth;
+
+- if (bttv_tvcards[btv->c.type].muxsel[btv->input] < 0) {
++ if (btv->input == btv->dig) {
+ swidth = 720;
+ totalwidth = 858;
+ scaledtwidth = 858;
+@@ -391,7 +391,7 @@ bttv_calc_geo (struct bttv * btv,
+ && crop->width == tvnorm->cropcap.defrect.width
+ && crop->height == tvnorm->cropcap.defrect.height
+ && width <= tvnorm->swidth /* see PAL-Nc et al */)
+- || bttv_tvcards[btv->c.type].muxsel[btv->input] < 0) {
++ || btv->input == btv->dig) {
+ bttv_calc_geo_old(btv, geo, width, height,
+ both_fields, tvnorm);
+ return;
+diff --git a/drivers/media/video/bt8xx/bttv-vbi.c b/drivers/media/video/bt8xx/bttv-vbi.c
+index 6819e21..e79a402 100644
+--- a/drivers/media/video/bt8xx/bttv-vbi.c
++++ b/drivers/media/video/bt8xx/bttv-vbi.c
+@@ -411,7 +411,7 @@ int bttv_g_fmt_vbi_cap(struct file *file, void *f, struct v4l2_format *frt)
+ return 0;
+ }
+
+-void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, int norm)
++void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, unsigned int norm)
+ {
+ const struct bttv_tvnorm *tvnorm;
+ unsigned int real_samples_per_line;
+diff --git a/drivers/media/video/bt8xx/bttv.h b/drivers/media/video/bt8xx/bttv.h
+index 529bf6c..3d36daf 100644
+--- a/drivers/media/video/bt8xx/bttv.h
++++ b/drivers/media/video/bt8xx/bttv.h
+@@ -14,8 +14,9 @@
+ #ifndef _BTTV_H_
+ #define _BTTV_H_
+
+-#include <linux/videodev.h>
++#include <linux/videodev2.h>
+ #include <linux/i2c.h>
++#include <media/v4l2-device.h>
+ #include <media/ir-common.h>
+ #include <media/ir-kbd-i2c.h>
+ #include <media/i2c-addr.h>
+@@ -180,6 +181,10 @@
+ #define BTTV_BOARD_VD012 0x99
+ #define BTTV_BOARD_VD012_X1 0x9a
+ #define BTTV_BOARD_VD012_X2 0x9b
++#define BTTV_BOARD_IVCE8784 0x9c
++#define BTTV_BOARD_GEOVISION_GV800S 0x9d
++#define BTTV_BOARD_GEOVISION_GV800S_SL 0x9e
++#define BTTV_BOARD_PV183 0x9f
+
+
+ /* more card-specific defines */
+@@ -191,12 +196,9 @@
+ #define WINVIEW_PT2254_DATA 0x20
+ #define WINVIEW_PT2254_STROBE 0x80
+
+-/* digital_mode */
+-#define DIGITAL_MODE_VIDEO 1
+-#define DIGITAL_MODE_CAMERA 2
+-
+ struct bttv_core {
+ /* device structs */
++ struct v4l2_device v4l2_dev;
+ struct pci_dev *pci;
+ struct i2c_adapter i2c_adap;
+ struct list_head subs; /* struct bttv_sub_device */
+@@ -204,59 +206,79 @@ struct bttv_core {
+ /* device config */
+ unsigned int nr; /* dev nr (for printk("bttv%d: ..."); */
+ unsigned int type; /* card type (pointer into tvcards[]) */
+- char name[8]; /* dev name */
+ };
+
+ struct bttv;
+
+-
+-struct tvcard
+-{
++struct tvcard {
+ char *name;
+- unsigned int video_inputs;
+- unsigned int audio_inputs;
+- unsigned int tuner;
+- unsigned int svhs;
+- unsigned int digital_mode; // DIGITAL_MODE_CAMERA or DIGITAL_MODE_VIDEO
++ 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);
++
++ /* MUX bits for each input, two bits per input starting with the LSB */
++ u32 muxsel; /* Use MUXSEL() to set */
++
+ u32 gpiomask;
+- u32 muxsel[16];
+ u32 gpiomux[4]; /* Tuner, Radio, external, internal */
+ u32 gpiomute; /* GPIO mute setting */
+ u32 gpiomask2; /* GPIO MUX mask */
+
++ unsigned int tuner_type;
++ u8 tuner_addr;
++ u8 video_inputs; /* Number of inputs */
++ unsigned int svhs:4; /* Which input is s-video */
++#define NO_SVHS 15
++ unsigned int pll:2;
++#define PLL_NONE 0
++#define PLL_28 1
++#define PLL_35 2
++
+ /* i2c audio flags */
+ unsigned int no_msp34xx:1;
+ unsigned int no_tda9875:1;
+ unsigned int no_tda7432:1;
+ unsigned int needs_tvaudio:1;
+ unsigned int msp34xx_alt:1;
++ /* Note: currently no card definition needs to mark the presence
++ of a RDS saa6588 chip. If this is ever needed, then add a new
++ 'has_saa6588' bit here. */
+
+- /* flag: video pci function is unused */
+- unsigned int no_video:1;
++ unsigned int no_video:1; /* video pci function is unused */
+ unsigned int has_dvb:1;
+ unsigned int has_remote:1;
++ unsigned int has_radio:1;
++ unsigned int has_dig_in:1; /* Has digital input (always last input) */
+ unsigned int no_gpioirq:1;
+-
+- /* other settings */
+- unsigned int pll;
+-#define PLL_NONE 0
+-#define PLL_28 1
+-#define PLL_35 2
+-
+- unsigned int tuner_type;
+- unsigned int tuner_addr;
+- unsigned int radio_addr;
+-
+- unsigned int has_radio;
+-
+- 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);
+ };
+
+ extern struct tvcard bttv_tvcards[];
+
++/*
++ * This bit of cpp voodoo is used to create a macro with a variable number of
++ * arguments (1 to 16). It will pack each argument into a word two bits at a
++ * time. It can't be a function because it needs to be compile time constant to
++ * initialize structures. Since each argument must fit in two bits, it's ok
++ * that they are changed to octal. One should not use hex number, macros, or
++ * anything else with this macro. Just use plain integers from 0 to 3.
++ */
++#define _MUXSELf(a) 0##a << 30
++#define _MUXSELe(a, b...) 0##a << 28 | _MUXSELf(b)
++#define _MUXSELd(a, b...) 0##a << 26 | _MUXSELe(b)
++#define _MUXSELc(a, b...) 0##a << 24 | _MUXSELd(b)
++#define _MUXSELb(a, b...) 0##a << 22 | _MUXSELc(b)
++#define _MUXSELa(a, b...) 0##a << 20 | _MUXSELb(b)
++#define _MUXSEL9(a, b...) 0##a << 18 | _MUXSELa(b)
++#define _MUXSEL8(a, b...) 0##a << 16 | _MUXSEL9(b)
++#define _MUXSEL7(a, b...) 0##a << 14 | _MUXSEL8(b)
++#define _MUXSEL6(a, b...) 0##a << 12 | _MUXSEL7(b)
++#define _MUXSEL5(a, b...) 0##a << 10 | _MUXSEL6(b)
++#define _MUXSEL4(a, b...) 0##a << 8 | _MUXSEL5(b)
++#define _MUXSEL3(a, b...) 0##a << 6 | _MUXSEL4(b)
++#define _MUXSEL2(a, b...) 0##a << 4 | _MUXSEL3(b)
++#define _MUXSEL1(a, b...) 0##a << 2 | _MUXSEL2(b)
++#define MUXSEL(a, b...) (a | _MUXSEL1(b))
++
+ /* identification / initialization of the card */
+ extern void bttv_idcard(struct bttv *btv);
+ extern void bttv_init_card1(struct bttv *btv);
+@@ -264,7 +286,7 @@ extern void bttv_init_card2(struct bttv *btv);
+
+ /* card-specific funtions */
+ extern void tea5757_set_freq(struct bttv *btv, unsigned short freq);
+-extern void bttv_tda9880_setnorm(struct bttv *btv, int norm);
++extern u32 bttv_tda9880_setnorm(struct bttv *btv, u32 gpiobits);
+
+ /* extra tweaks for some chipsets */
+ extern void bttv_check_chipset(void);
+@@ -336,7 +358,9 @@ void bttv_gpio_bits(struct bttv_core *core, u32 mask, u32 bits);
+ /* ---------------------------------------------------------- */
+ /* i2c */
+
+-extern void bttv_call_i2c_clients(struct bttv *btv, unsigned int cmd, void *arg);
++#define bttv_call_all(btv, o, f, args...) \
++ v4l2_device_call_all(&btv->c.v4l2_dev, 0, o, f, ##args)
++
+ extern int bttv_I2CRead(struct bttv *btv, unsigned char addr, char *probe_for);
+ extern int bttv_I2CWrite(struct bttv *btv, unsigned char addr, unsigned char b1,
+ unsigned char b2, int both);
+diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h
+index 199a4d2..9649848 100644
+--- a/drivers/media/video/bt8xx/bttvp.h
++++ b/drivers/media/video/bt8xx/bttvp.h
+@@ -32,7 +32,6 @@
+ #include <linux/wait.h>
+ #include <linux/i2c.h>
+ #include <linux/i2c-algo-bit.h>
+-#include <linux/videodev.h>
+ #include <linux/pci.h>
+ #include <linux/input.h>
+ #include <linux/mutex.h>
+@@ -135,7 +134,7 @@ struct bttv_buffer {
+
+ /* bttv specific */
+ const struct bttv_format *fmt;
+- int tvnorm;
++ unsigned int tvnorm;
+ int btformat;
+ int btswap;
+ struct bttv_geometry geo;
+@@ -154,7 +153,7 @@ struct bttv_buffer_set {
+ };
+
+ struct bttv_overlay {
+- int tvnorm;
++ unsigned int tvnorm;
+ struct v4l2_rect w;
+ enum v4l2_field field;
+ struct v4l2_clip *clips;
+@@ -174,7 +173,7 @@ struct bttv_vbi_fmt {
+ };
+
+ /* bttv-vbi.c */
+-void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, int norm);
++void bttv_vbi_fmt_reset(struct bttv_vbi_fmt *f, unsigned int norm);
+
+ struct bttv_crop {
+ /* A cropping rectangle in struct bttv_tvnorm.cropcap units. */
+@@ -329,7 +328,8 @@ struct bttv {
+ unsigned int cardid; /* pci subsystem id (bt878 based ones) */
+ unsigned int tuner_type; /* tuner chip type */
+ unsigned int tda9887_conf;
+- unsigned int svhs;
++ unsigned int svhs, dig;
++ unsigned int has_saa6588:1;
+ struct bttv_pll_info pll;
+ int triton1;
+ int gpioirq;
+@@ -353,8 +353,8 @@ struct bttv {
+ int i2c_state, i2c_rc;
+ int i2c_done;
+ wait_queue_head_t i2c_queue;
+- struct i2c_client *i2c_msp34xx_client;
+- struct i2c_client *i2c_tvaudio_client;
++ struct v4l2_subdev *sd_msp34xx;
++ struct v4l2_subdev *sd_tvaudio;
+
+ /* video4linux (1) */
+ struct video_device *video_dev;
+@@ -378,7 +378,8 @@ struct bttv {
+ unsigned int audio;
+ unsigned int mute;
+ unsigned long freq;
+- int tvnorm,hue,contrast,bright,saturation;
++ unsigned int tvnorm;
++ int hue, contrast, bright, saturation;
+ struct v4l2_framebuffer fbuf;
+ unsigned int field_count;
+
+@@ -458,10 +459,21 @@ struct bttv {
+ __s32 crop_start;
+ };
+
++static inline struct bttv *to_bttv(struct v4l2_device *v4l2_dev)
++{
++ return container_of(v4l2_dev, struct bttv, c.v4l2_dev);
++}
++
+ /* our devices */
+ #define BTTV_MAX 32
+ extern unsigned int bttv_num;
+-extern struct bttv bttvs[BTTV_MAX];
++extern struct bttv *bttvs[BTTV_MAX];
++
++static inline unsigned int bttv_muxsel(const struct bttv *btv,
++ unsigned int input)
++{
++ return (bttv_tvcards[btv->c.type].muxsel >> (input * 2)) & 3;
++}
+
+ #endif
+
+diff --git a/drivers/media/video/cafe_ccic.c b/drivers/media/video/cafe_ccic.c
+index 46fd573..7abe94d 100644
+--- a/drivers/media/video/cafe_ccic.c
++++ b/drivers/media/video/cafe_ccic.c
+@@ -11,6 +11,12 @@
+ *
+ * Written by Jonathan Corbet, corbet@lwn.net.
+ *
++ * v4l2_device/v4l2_subdev conversion by:
++ * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl>
++ *
++ * Note: this conversion is untested! Please contact the linux-media
++ * mailinglist if you can test this, together with the test results.
++ *
+ * This file may be distributed under the terms of the GNU General
+ * Public License, version 2.
+ */
+@@ -25,7 +31,7 @@
+ #include <linux/interrupt.h>
+ #include <linux/spinlock.h>
+ #include <linux/videodev2.h>
+-#include <media/v4l2-common.h>
++#include <media/v4l2-device.h>
+ #include <media/v4l2-ioctl.h>
+ #include <media/v4l2-chip-ident.h>
+ #include <linux/device.h>
+@@ -33,7 +39,6 @@
+ #include <linux/list.h>
+ #include <linux/dma-mapping.h>
+ #include <linux/delay.h>
+-#include <linux/debugfs.h>
+ #include <linux/jiffies.h>
+ #include <linux/vmalloc.h>
+
+@@ -136,6 +141,7 @@ struct cafe_sio_buffer {
+ */
+ struct cafe_camera
+ {
++ struct v4l2_device v4l2_dev;
+ enum cafe_state state;
+ unsigned long flags; /* Buffer status, mainly (dev_lock) */
+ int users; /* How many open FDs */
+@@ -145,9 +151,10 @@ struct cafe_camera
+ * Subsystem structures.
+ */
+ struct pci_dev *pdev;
+- struct video_device v4ldev;
++ struct video_device vdev;
+ struct i2c_adapter i2c_adapter;
+- struct i2c_client *sensor;
++ struct v4l2_subdev *sensor;
++ unsigned short sensor_addr;
+
+ unsigned char __iomem *regs;
+ struct list_head dev_list; /* link to other devices */
+@@ -180,10 +187,6 @@ struct cafe_camera
+ /* Misc */
+ wait_queue_head_t smbus_wait; /* Waiting on i2c events */
+ wait_queue_head_t iowait; /* Waiting on frame data */
+-#ifdef CONFIG_VIDEO_ADV_DEBUG
+- struct dentry *dfs_regs;
+- struct dentry *dfs_cam_regs;
+-#endif
+ };
+
+ /*
+@@ -195,6 +198,13 @@ struct cafe_camera
+ #define CF_DMA_ACTIVE 3 /* A frame is incoming */
+ #define CF_CONFIG_NEEDED 4 /* Must configure hardware */
+
++#define sensor_call(cam, o, f, args...) \
++ v4l2_subdev_call(cam->sensor, o, f, ##args)
++
++static inline struct cafe_camera *to_cam(struct v4l2_device *dev)
++{
++ return container_of(dev, struct cafe_camera, v4l2_dev);
++}
+
+
+ /*
+@@ -238,59 +248,7 @@ static void cafe_set_config_needed(struct cafe_camera *cam, int needed)
+
+
+ /* ---------------------------------------------------------------------*/
+-/*
+- * We keep a simple list of known devices to search at open time.
+- */
+-static LIST_HEAD(cafe_dev_list);
+-static DEFINE_MUTEX(cafe_dev_list_lock);
+-
+-static void cafe_add_dev(struct cafe_camera *cam)
+-{
+- mutex_lock(&cafe_dev_list_lock);
+- list_add_tail(&cam->dev_list, &cafe_dev_list);
+- mutex_unlock(&cafe_dev_list_lock);
+-}
+-
+-static void cafe_remove_dev(struct cafe_camera *cam)
+-{
+- mutex_lock(&cafe_dev_list_lock);
+- list_del(&cam->dev_list);
+- mutex_unlock(&cafe_dev_list_lock);
+-}
+-
+-static struct cafe_camera *cafe_find_dev(int minor)
+-{
+- struct cafe_camera *cam;
+-
+- mutex_lock(&cafe_dev_list_lock);
+- list_for_each_entry(cam, &cafe_dev_list, dev_list) {
+- if (cam->v4ldev.minor == minor)
+- goto done;
+- }
+- cam = NULL;
+- done:
+- mutex_unlock(&cafe_dev_list_lock);
+- return cam;
+-}
+-
+-
+-static struct cafe_camera *cafe_find_by_pdev(struct pci_dev *pdev)
+-{
+- struct cafe_camera *cam;
+
+- mutex_lock(&cafe_dev_list_lock);
+- list_for_each_entry(cam, &cafe_dev_list, dev_list) {
+- if (cam->pdev == pdev)
+- goto done;
+- }
+- cam = NULL;
+- done:
+- mutex_unlock(&cafe_dev_list_lock);
+- return cam;
+-}
+-
+-
+-/* ------------------------------------------------------------------------ */
+ /*
+ * Device register I/O
+ */
+@@ -481,18 +439,11 @@ static int cafe_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
+ unsigned short flags, char rw, u8 command,
+ int size, union i2c_smbus_data *data)
+ {
+- struct cafe_camera *cam = i2c_get_adapdata(adapter);
++ struct v4l2_device *v4l2_dev = i2c_get_adapdata(adapter);
++ struct cafe_camera *cam = to_cam(v4l2_dev);
+ int ret = -EINVAL;
+
+ /*
+- * Refuse to talk to anything but OV cam chips. We should
+- * never even see an attempt to do so, but one never knows.
+- */
+- if (cam->sensor && addr != cam->sensor->addr) {
+- cam_err(cam, "funky smbus addr %d\n", addr);
+- return -EINVAL;
+- }
+- /*
+ * This interface would appear to only do byte data ops. OK
+ * it can do word too, but the cam chip has no use for that.
+ */
+@@ -530,38 +481,9 @@ static struct i2c_algorithm cafe_smbus_algo = {
+ };
+
+ /* Somebody is on the bus */
+-static int cafe_cam_init(struct cafe_camera *cam);
+ static void cafe_ctlr_stop_dma(struct cafe_camera *cam);
+ static void cafe_ctlr_power_down(struct cafe_camera *cam);
+
+-static int cafe_smbus_attach(struct i2c_client *client)
+-{
+- struct cafe_camera *cam = i2c_get_adapdata(client->adapter);
+-
+- /*
+- * Don't talk to chips we don't recognize.
+- */
+- if (client->driver->id == I2C_DRIVERID_OV7670) {
+- cam->sensor = client;
+- return cafe_cam_init(cam);
+- }
+- return -EINVAL;
+-}
+-
+-static int cafe_smbus_detach(struct i2c_client *client)
+-{
+- struct cafe_camera *cam = i2c_get_adapdata(client->adapter);
+-
+- if (cam->sensor == client) {
+- cafe_ctlr_stop_dma(cam);
+- cafe_ctlr_power_down(cam);
+- cam_err(cam, "lost the sensor!\n");
+- cam->sensor = NULL; /* Bummer, no camera */
+- cam->state = S_NOTREADY;
+- }
+- return 0;
+-}
+-
+ static int cafe_smbus_setup(struct cafe_camera *cam)
+ {
+ struct i2c_adapter *adap = &cam->i2c_adapter;
+@@ -570,12 +492,10 @@ static int cafe_smbus_setup(struct cafe_camera *cam)
+ cafe_smbus_enable_irq(cam);
+ adap->id = I2C_HW_SMBUS_CAFE;
+ adap->owner = THIS_MODULE;
+- adap->client_register = cafe_smbus_attach;
+- adap->client_unregister = cafe_smbus_detach;
+ adap->algo = &cafe_smbus_algo;
+ strcpy(adap->name, "cafe_ccic");
+ adap->dev.parent = &cam->pdev->dev;
+- i2c_set_adapdata(adap, cam);
++ i2c_set_adapdata(adap, &cam->v4l2_dev);
+ ret = i2c_add_adapter(adap);
+ if (ret)
+ printk(KERN_ERR "Unable to register cafe i2c adapter\n");
+@@ -809,9 +729,9 @@ static void cafe_ctlr_power_up(struct cafe_camera *cam)
+ * Control 1 is power down, set to 0 to operate.
+ */
+ cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN); /* pwr up, reset */
+-// mdelay(1); /* Marvell says 1ms will do it */
++/* mdelay(1); */ /* Marvell says 1ms will do it */
+ cafe_reg_write(cam, REG_GPR, GPR_C1EN|GPR_C0EN|GPR_C0);
+-// mdelay(1); /* Enough? */
++/* mdelay(1); */ /* Enough? */
+ spin_unlock_irqrestore(&cam->dev_lock, flags);
+ msleep(5); /* Just to be sure */
+ }
+@@ -833,23 +753,9 @@ static void cafe_ctlr_power_down(struct cafe_camera *cam)
+ * Communications with the sensor.
+ */
+
+-static int __cafe_cam_cmd(struct cafe_camera *cam, int cmd, void *arg)
+-{
+- struct i2c_client *sc = cam->sensor;
+- int ret;
+-
+- if (sc == NULL || sc->driver == NULL || sc->driver->command == NULL)
+- return -EINVAL;
+- ret = sc->driver->command(sc, cmd, arg);
+- if (ret == -EPERM) /* Unsupported command */
+- return 0;
+- return ret;
+-}
+-
+ static int __cafe_cam_reset(struct cafe_camera *cam)
+ {
+- int zero = 0;
+- return __cafe_cam_cmd(cam, VIDIOC_INT_RESET, &zero);
++ return sensor_call(cam, core, reset, 0);
+ }
+
+ /*
+@@ -869,14 +775,13 @@ static int cafe_cam_init(struct cafe_camera *cam)
+ if (ret)
+ goto out;
+ chip.match.type = V4L2_CHIP_MATCH_I2C_ADDR;
+- chip.match.addr = cam->sensor->addr;
+- ret = __cafe_cam_cmd(cam, VIDIOC_DBG_G_CHIP_IDENT, &chip);
++ chip.match.addr = cam->sensor_addr;
++ ret = sensor_call(cam, core, g_chip_ident, &chip);
+ if (ret)
+ goto out;
+ cam->sensor_type = chip.ident;
+-// if (cam->sensor->addr != OV7xx0_SID) {
+ if (cam->sensor_type != V4L2_IDENT_OV7670) {
+- cam_err(cam, "Unsupported sensor type %d", cam->sensor->addr);
++ cam_err(cam, "Unsupported sensor type 0x%x", cam->sensor_type);
+ ret = -EINVAL;
+ goto out;
+ }
+@@ -900,21 +805,21 @@ static int cafe_cam_set_flip(struct cafe_camera *cam)
+ memset(&ctrl, 0, sizeof(ctrl));
+ ctrl.id = V4L2_CID_VFLIP;
+ ctrl.value = flip;
+- return __cafe_cam_cmd(cam, VIDIOC_S_CTRL, &ctrl);
++ return sensor_call(cam, core, s_ctrl, &ctrl);
+ }
+
+
+ static int cafe_cam_configure(struct cafe_camera *cam)
+ {
+ struct v4l2_format fmt;
+- int ret, zero = 0;
++ int ret;
+
+ if (cam->state != S_IDLE)
+ return -EINVAL;
+ fmt.fmt.pix = cam->pix_format;
+- ret = __cafe_cam_cmd(cam, VIDIOC_INT_INIT, &zero);
++ ret = sensor_call(cam, core, init, 0);
+ if (ret == 0)
+- ret = __cafe_cam_cmd(cam, VIDIOC_S_FMT, &fmt);
++ ret = sensor_call(cam, video, s_fmt, &fmt);
+ /*
+ * OV7670 does weird things if flip is set *before* format...
+ */
+@@ -1246,8 +1151,6 @@ static int cafe_vidioc_reqbufs(struct file *filp, void *priv,
+ * Make sure it's something we can do. User pointers could be
+ * implemented without great pain, but that's not been done yet.
+ */
+- if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+ if (req->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+ /*
+@@ -1311,9 +1214,7 @@ static int cafe_vidioc_querybuf(struct file *filp, void *priv,
+ int ret = -EINVAL;
+
+ mutex_lock(&cam->s_mutex);
+- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- goto out;
+- if (buf->index < 0 || buf->index >= cam->n_sbufs)
++ if (buf->index >= cam->n_sbufs)
+ goto out;
+ *buf = cam->sb_bufs[buf->index].v4lbuf;
+ ret = 0;
+@@ -1331,9 +1232,7 @@ static int cafe_vidioc_qbuf(struct file *filp, void *priv,
+ unsigned long flags;
+
+ mutex_lock(&cam->s_mutex);
+- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- goto out;
+- if (buf->index < 0 || buf->index >= cam->n_sbufs)
++ if (buf->index >= cam->n_sbufs)
+ goto out;
+ sbuf = cam->sb_bufs + buf->index;
+ if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) {
+@@ -1364,8 +1263,6 @@ static int cafe_vidioc_dqbuf(struct file *filp, void *priv,
+ unsigned long flags;
+
+ mutex_lock(&cam->s_mutex);
+- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- goto out_unlock;
+ if (cam->state != S_STREAMING)
+ goto out_unlock;
+ if (list_empty(&cam->sb_full) && filp->f_flags & O_NONBLOCK) {
+@@ -1474,11 +1371,8 @@ static int cafe_v4l_mmap(struct file *filp, struct vm_area_struct *vma)
+
+ static int cafe_v4l_open(struct file *filp)
+ {
+- struct cafe_camera *cam;
++ struct cafe_camera *cam = video_drvdata(filp);
+
+- cam = cafe_find_dev(video_devdata(filp)->minor);
+- if (cam == NULL)
+- return -ENODEV;
+ filp->private_data = cam;
+
+ mutex_lock(&cam->s_mutex);
+@@ -1532,11 +1426,11 @@ static unsigned int cafe_v4l_poll(struct file *filp,
+ static int cafe_vidioc_queryctrl(struct file *filp, void *priv,
+ struct v4l2_queryctrl *qc)
+ {
+- struct cafe_camera *cam = filp->private_data;
++ struct cafe_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+- ret = __cafe_cam_cmd(cam, VIDIOC_QUERYCTRL, qc);
++ ret = sensor_call(cam, core, queryctrl, qc);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+ }
+@@ -1545,11 +1439,11 @@ static int cafe_vidioc_queryctrl(struct file *filp, void *priv,
+ static int cafe_vidioc_g_ctrl(struct file *filp, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct cafe_camera *cam = filp->private_data;
++ struct cafe_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+- ret = __cafe_cam_cmd(cam, VIDIOC_G_CTRL, ctrl);
++ ret = sensor_call(cam, core, g_ctrl, ctrl);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+ }
+@@ -1558,11 +1452,11 @@ static int cafe_vidioc_g_ctrl(struct file *filp, void *priv,
+ static int cafe_vidioc_s_ctrl(struct file *filp, void *priv,
+ struct v4l2_control *ctrl)
+ {
+- struct cafe_camera *cam = filp->private_data;
++ struct cafe_camera *cam = priv;
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+- ret = __cafe_cam_cmd(cam, VIDIOC_S_CTRL, ctrl);
++ ret = sensor_call(cam, core, s_ctrl, ctrl);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+ }
+@@ -1601,10 +1495,8 @@ static int cafe_vidioc_enum_fmt_vid_cap(struct file *filp,
+ struct cafe_camera *cam = priv;
+ int ret;
+
+- if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+ mutex_lock(&cam->s_mutex);
+- ret = __cafe_cam_cmd(cam, VIDIOC_ENUM_FMT, fmt);
++ ret = sensor_call(cam, video, enum_fmt, fmt);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+ }
+@@ -1617,7 +1509,7 @@ static int cafe_vidioc_try_fmt_vid_cap(struct file *filp, void *priv,
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+- ret = __cafe_cam_cmd(cam, VIDIOC_TRY_FMT, fmt);
++ ret = sensor_call(cam, video, try_fmt, fmt);
+ mutex_unlock(&cam->s_mutex);
+ return ret;
+ }
+@@ -1726,7 +1618,7 @@ static int cafe_vidioc_g_parm(struct file *filp, void *priv,
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+- ret = __cafe_cam_cmd(cam, VIDIOC_G_PARM, parms);
++ ret = sensor_call(cam, video, g_parm, parms);
+ mutex_unlock(&cam->s_mutex);
+ parms->parm.capture.readbuffers = n_dma_bufs;
+ return ret;
+@@ -1739,20 +1631,52 @@ static int cafe_vidioc_s_parm(struct file *filp, void *priv,
+ int ret;
+
+ mutex_lock(&cam->s_mutex);
+- ret = __cafe_cam_cmd(cam, VIDIOC_S_PARM, parms);
++ ret = sensor_call(cam, video, s_parm, parms);
+ mutex_unlock(&cam->s_mutex);
+ parms->parm.capture.readbuffers = n_dma_bufs;
+ return ret;
+ }
+
++static int cafe_vidioc_g_chip_ident(struct file *file, void *priv,
++ struct v4l2_dbg_chip_ident *chip)
++{
++ struct cafe_camera *cam = priv;
+
+-static void cafe_v4l_dev_release(struct video_device *vd)
++ chip->ident = V4L2_IDENT_NONE;
++ chip->revision = 0;
++ if (v4l2_chip_match_host(&chip->match)) {
++ chip->ident = V4L2_IDENT_CAFE;
++ return 0;
++ }
++ return sensor_call(cam, core, g_chip_ident, chip);
++}
++
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++static int cafe_vidioc_g_register(struct file *file, void *priv,
++ struct v4l2_dbg_register *reg)
+ {
+- struct cafe_camera *cam = container_of(vd, struct cafe_camera, v4ldev);
++ struct cafe_camera *cam = priv;
+
+- kfree(cam);
++ if (v4l2_chip_match_host(&reg->match)) {
++ reg->val = cafe_reg_read(cam, reg->reg);
++ reg->size = 4;
++ return 0;
++ }
++ return sensor_call(cam, core, g_register, reg);
+ }
+
++static int cafe_vidioc_s_register(struct file *file, void *priv,
++ struct v4l2_dbg_register *reg)
++{
++ struct cafe_camera *cam = priv;
++
++ if (v4l2_chip_match_host(&reg->match)) {
++ cafe_reg_write(cam, reg->reg, reg->val);
++ return 0;
++ }
++ return sensor_call(cam, core, s_register, reg);
++}
++#endif
+
+ /*
+ * This template device holds all of those v4l2 methods; we
+@@ -1790,6 +1714,11 @@ static const struct v4l2_ioctl_ops cafe_v4l_ioctl_ops = {
+ .vidioc_s_ctrl = cafe_vidioc_s_ctrl,
+ .vidioc_g_parm = cafe_vidioc_g_parm,
+ .vidioc_s_parm = cafe_vidioc_s_parm,
++ .vidioc_g_chip_ident = cafe_vidioc_g_chip_ident,
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++ .vidioc_g_register = cafe_vidioc_g_register,
++ .vidioc_s_register = cafe_vidioc_s_register,
++#endif
+ };
+
+ static struct video_device cafe_v4l_template = {
+@@ -1800,15 +1729,10 @@ static struct video_device cafe_v4l_template = {
+
+ .fops = &cafe_v4l_fops,
+ .ioctl_ops = &cafe_v4l_ioctl_ops,
+- .release = cafe_v4l_dev_release,
++ .release = video_device_release_empty,
+ };
+
+
+-
+-
+-
+-
+-
+ /* ---------------------------------------------------------------------- */
+ /*
+ * Interrupt handler stuff
+@@ -1962,127 +1886,6 @@ static irqreturn_t cafe_irq(int irq, void *data)
+
+
+ /* -------------------------------------------------------------------------- */
+-#ifdef CONFIG_VIDEO_ADV_DEBUG
+-/*
+- * Debugfs stuff.
+- */
+-
+-static char cafe_debug_buf[1024];
+-static struct dentry *cafe_dfs_root;
+-
+-static void cafe_dfs_setup(void)
+-{
+- cafe_dfs_root = debugfs_create_dir("cafe_ccic", NULL);
+- if (IS_ERR(cafe_dfs_root)) {
+- cafe_dfs_root = NULL; /* Never mind */
+- printk(KERN_NOTICE "cafe_ccic unable to set up debugfs\n");
+- }
+-}
+-
+-static void cafe_dfs_shutdown(void)
+-{
+- if (cafe_dfs_root)
+- debugfs_remove(cafe_dfs_root);
+-}
+-
+-static int cafe_dfs_open(struct inode *inode, struct file *file)
+-{
+- file->private_data = inode->i_private;
+- return 0;
+-}
+-
+-static ssize_t cafe_dfs_read_regs(struct file *file,
+- char __user *buf, size_t count, loff_t *ppos)
+-{
+- struct cafe_camera *cam = file->private_data;
+- char *s = cafe_debug_buf;
+- int offset;
+-
+- for (offset = 0; offset < 0x44; offset += 4)
+- s += sprintf(s, "%02x: %08x\n", offset,
+- cafe_reg_read(cam, offset));
+- for (offset = 0x88; offset <= 0x90; offset += 4)
+- s += sprintf(s, "%02x: %08x\n", offset,
+- cafe_reg_read(cam, offset));
+- for (offset = 0xb4; offset <= 0xbc; offset += 4)
+- s += sprintf(s, "%02x: %08x\n", offset,
+- cafe_reg_read(cam, offset));
+- for (offset = 0x3000; offset <= 0x300c; offset += 4)
+- s += sprintf(s, "%04x: %08x\n", offset,
+- cafe_reg_read(cam, offset));
+- return simple_read_from_buffer(buf, count, ppos, cafe_debug_buf,
+- s - cafe_debug_buf);
+-}
+-
+-static const struct file_operations cafe_dfs_reg_ops = {
+- .owner = THIS_MODULE,
+- .read = cafe_dfs_read_regs,
+- .open = cafe_dfs_open
+-};
+-
+-static ssize_t cafe_dfs_read_cam(struct file *file,
+- char __user *buf, size_t count, loff_t *ppos)
+-{
+- struct cafe_camera *cam = file->private_data;
+- char *s = cafe_debug_buf;
+- int offset;
+-
+- if (! cam->sensor)
+- return -EINVAL;
+- for (offset = 0x0; offset < 0x8a; offset++)
+- {
+- u8 v;
+-
+- cafe_smbus_read_data(cam, cam->sensor->addr, offset, &v);
+- s += sprintf(s, "%02x: %02x\n", offset, v);
+- }
+- return simple_read_from_buffer(buf, count, ppos, cafe_debug_buf,
+- s - cafe_debug_buf);
+-}
+-
+-static const struct file_operations cafe_dfs_cam_ops = {
+- .owner = THIS_MODULE,
+- .read = cafe_dfs_read_cam,
+- .open = cafe_dfs_open
+-};
+-
+-
+-
+-static void cafe_dfs_cam_setup(struct cafe_camera *cam)
+-{
+- char fname[40];
+-
+- if (!cafe_dfs_root)
+- return;
+- sprintf(fname, "regs-%d", cam->v4ldev.num);
+- cam->dfs_regs = debugfs_create_file(fname, 0444, cafe_dfs_root,
+- cam, &cafe_dfs_reg_ops);
+- sprintf(fname, "cam-%d", cam->v4ldev.num);
+- cam->dfs_cam_regs = debugfs_create_file(fname, 0444, cafe_dfs_root,
+- cam, &cafe_dfs_cam_ops);
+-}
+-
+-
+-static void cafe_dfs_cam_shutdown(struct cafe_camera *cam)
+-{
+- if (! IS_ERR(cam->dfs_regs))
+- debugfs_remove(cam->dfs_regs);
+- if (! IS_ERR(cam->dfs_cam_regs))
+- debugfs_remove(cam->dfs_cam_regs);
+-}
+-
+-#else
+-
+-#define cafe_dfs_setup()
+-#define cafe_dfs_shutdown()
+-#define cafe_dfs_cam_setup(cam)
+-#define cafe_dfs_cam_shutdown(cam)
+-#endif /* CONFIG_VIDEO_ADV_DEBUG */
+-
+-
+-
+-
+-/* ------------------------------------------------------------------------*/
+ /*
+ * PCI interface stuff.
+ */
+@@ -2100,6 +1903,10 @@ static int cafe_pci_probe(struct pci_dev *pdev,
+ cam = kzalloc(sizeof(struct cafe_camera), GFP_KERNEL);
+ if (cam == NULL)
+ goto out;
++ ret = v4l2_device_register(&pdev->dev, &cam->v4l2_dev);
++ if (ret)
++ goto out_free;
++
+ mutex_init(&cam->s_mutex);
+ mutex_lock(&cam->s_mutex);
+ spin_lock_init(&cam->dev_lock);
+@@ -2118,14 +1925,14 @@ static int cafe_pci_probe(struct pci_dev *pdev,
+ */
+ ret = pci_enable_device(pdev);
+ if (ret)
+- goto out_free;
++ goto out_unreg;
+ pci_set_master(pdev);
+
+ ret = -EIO;
+ cam->regs = pci_iomap(pdev, 0, 0);
+ if (! cam->regs) {
+ printk(KERN_ERR "Unable to ioremap cafe-ccic regs\n");
+- goto out_free;
++ goto out_unreg;
+ }
+ ret = request_irq(pdev->irq, cafe_irq, IRQF_SHARED, "cafe-ccic", cam);
+ if (ret)
+@@ -2145,17 +1952,31 @@ static int cafe_pci_probe(struct pci_dev *pdev,
+ ret = cafe_smbus_setup(cam);
+ if (ret)
+ goto out_freeirq;
++
++ cam->sensor_addr = 0x42;
++ cam->sensor = v4l2_i2c_new_subdev(&cam->i2c_adapter,
++ "ov7670", "ov7670", cam->sensor_addr);
++ if (cam->sensor == NULL) {
++ ret = -ENODEV;
++ goto out_smbus;
++ }
++ ret = cafe_cam_init(cam);
++ if (ret)
++ goto out_smbus;
++
+ /*
+ * Get the v4l2 setup done.
+ */
+ mutex_lock(&cam->s_mutex);
+- cam->v4ldev = cafe_v4l_template;
+- cam->v4ldev.debug = 0;
+-// cam->v4ldev.debug = V4L2_DEBUG_IOCTL_ARG;
+- cam->v4ldev.parent = &pdev->dev;
+- ret = video_register_device(&cam->v4ldev, VFL_TYPE_GRABBER, -1);
++ cam->vdev = cafe_v4l_template;
++ cam->vdev.debug = 0;
++/* cam->vdev.debug = V4L2_DEBUG_IOCTL_ARG;*/
++ cam->vdev.v4l2_dev = &cam->v4l2_dev;
++ ret = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1);
+ if (ret)
+ goto out_smbus;
++ video_set_drvdata(&cam->vdev, cam);
++
+ /*
+ * If so requested, try to get our DMA buffers now.
+ */
+@@ -2165,21 +1986,21 @@ static int cafe_pci_probe(struct pci_dev *pdev,
+ " will try again later.");
+ }
+
+- cafe_dfs_cam_setup(cam);
+ mutex_unlock(&cam->s_mutex);
+- cafe_add_dev(cam);
+ return 0;
+
+- out_smbus:
++out_smbus:
+ cafe_smbus_shutdown(cam);
+- out_freeirq:
++out_freeirq:
+ cafe_ctlr_power_down(cam);
+ free_irq(pdev->irq, cam);
+- out_iounmap:
++out_iounmap:
+ pci_iounmap(pdev, cam->regs);
+- out_free:
++out_free:
++ v4l2_device_unregister(&cam->v4l2_dev);
++out_unreg:
+ kfree(cam);
+- out:
++out:
+ return ret;
+ }
+
+@@ -2190,25 +2011,23 @@ static int cafe_pci_probe(struct pci_dev *pdev,
+ static void cafe_shutdown(struct cafe_camera *cam)
+ {
+ /* FIXME: Make sure we take care of everything here */
+- cafe_dfs_cam_shutdown(cam);
+ if (cam->n_sbufs > 0)
+ /* What if they are still mapped? Shouldn't be, but... */
+ cafe_free_sio_buffers(cam);
+- cafe_remove_dev(cam);
+ cafe_ctlr_stop_dma(cam);
+ cafe_ctlr_power_down(cam);
+ cafe_smbus_shutdown(cam);
+ cafe_free_dma_bufs(cam);
+ free_irq(cam->pdev->irq, cam);
+ pci_iounmap(cam->pdev, cam->regs);
+- video_unregister_device(&cam->v4ldev);
+- /* kfree(cam); done in v4l_release () */
++ video_unregister_device(&cam->vdev);
+ }
+
+
+ static void cafe_pci_remove(struct pci_dev *pdev)
+ {
+- struct cafe_camera *cam = cafe_find_by_pdev(pdev);
++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
++ struct cafe_camera *cam = to_cam(v4l2_dev);
+
+ if (cam == NULL) {
+ printk(KERN_WARNING "pci_remove on unknown pdev %p\n", pdev);
+@@ -2218,6 +2037,8 @@ static void cafe_pci_remove(struct pci_dev *pdev)
+ if (cam->users > 0)
+ cam_warn(cam, "Removing a device with users!\n");
+ cafe_shutdown(cam);
++ v4l2_device_unregister(&cam->v4l2_dev);
++ kfree(cam);
+ /* No unlock - it no longer exists */
+ }
+
+@@ -2228,7 +2049,8 @@ static void cafe_pci_remove(struct pci_dev *pdev)
+ */
+ static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+ {
+- struct cafe_camera *cam = cafe_find_by_pdev(pdev);
++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
++ struct cafe_camera *cam = to_cam(v4l2_dev);
+ int ret;
+ enum cafe_state cstate;
+
+@@ -2246,7 +2068,8 @@ static int cafe_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+
+ static int cafe_pci_resume(struct pci_dev *pdev)
+ {
+- struct cafe_camera *cam = cafe_find_by_pdev(pdev);
++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
++ struct cafe_camera *cam = to_cam(v4l2_dev);
+ int ret = 0;
+
+ ret = pci_restore_state(pdev);
+@@ -2307,13 +2130,11 @@ static int __init cafe_init(void)
+
+ printk(KERN_NOTICE "Marvell M88ALP01 'CAFE' Camera Controller version %d\n",
+ CAFE_VERSION);
+- cafe_dfs_setup();
+ ret = pci_register_driver(&cafe_pci_driver);
+ if (ret) {
+ printk(KERN_ERR "Unable to register cafe_ccic driver\n");
+ goto out;
+ }
+- request_module("ov7670"); /* FIXME want something more general */
+ ret = 0;
+
+ out:
+@@ -2324,7 +2145,6 @@ static int __init cafe_init(void)
+ static void __exit cafe_exit(void)
+ {
+ pci_unregister_driver(&cafe_pci_driver);
+- cafe_dfs_shutdown();
+ }
+
+ module_init(cafe_init);
+diff --git a/drivers/media/video/cpia2/cpia2_v4l.c b/drivers/media/video/cpia2/cpia2_v4l.c
+index 9c25894..d4099f5 100644
+--- a/drivers/media/video/cpia2/cpia2_v4l.c
++++ b/drivers/media/video/cpia2/cpia2_v4l.c
+@@ -37,6 +37,7 @@
+ #include <linux/sched.h>
+ #include <linux/slab.h>
+ #include <linux/init.h>
++#include <linux/videodev.h>
+ #include <media/v4l2-ioctl.h>
+
+ #include "cpia2.h"
+diff --git a/drivers/media/video/cs5345.c b/drivers/media/video/cs5345.c
+index 87e9107..9714059 100644
+--- a/drivers/media/video/cs5345.c
++++ b/drivers/media/video/cs5345.c
+@@ -141,11 +141,6 @@ static int cs5345_log_status(struct v4l2_subdev *sd)
+ return 0;
+ }
+
+-static int cs5345_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+-}
+-
+ /* ----------------------------------------------------------------------- */
+
+ static const struct v4l2_subdev_core_ops cs5345_core_ops = {
+@@ -214,8 +209,6 @@ MODULE_DEVICE_TABLE(i2c, cs5345_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "cs5345",
+- .driverid = I2C_DRIVERID_CS5345,
+- .command = cs5345_command,
+ .probe = cs5345_probe,
+ .remove = cs5345_remove,
+ .id_table = cs5345_id,
+diff --git a/drivers/media/video/cs53l32a.c b/drivers/media/video/cs53l32a.c
+index 7292a63..5aeb066 100644
+--- a/drivers/media/video/cs53l32a.c
++++ b/drivers/media/video/cs53l32a.c
+@@ -29,7 +29,7 @@
+ #include <linux/videodev2.h>
+ #include <media/v4l2-device.h>
+ #include <media/v4l2-chip-ident.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <media/v4l2-i2c-drv.h>
+
+ MODULE_DESCRIPTION("i2c device driver for cs53l32a Audio ADC");
+ MODULE_AUTHOR("Martin Vaughan");
+@@ -41,9 +41,6 @@ module_param(debug, bool, 0644);
+
+ MODULE_PARM_DESC(debug, "Debugging messages, 0=Off (default), 1=On");
+
+-static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END };
+-
+-I2C_CLIENT_INSMOD;
+
+ /* ----------------------------------------------------------------------- */
+
+@@ -122,11 +119,6 @@ static int cs53l32a_log_status(struct v4l2_subdev *sd)
+ return 0;
+ }
+
+-static int cs53l32a_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+-}
+-
+ /* ----------------------------------------------------------------------- */
+
+ static const struct v4l2_subdev_core_ops cs53l32a_core_ops = {
+@@ -218,8 +210,6 @@ MODULE_DEVICE_TABLE(i2c, cs53l32a_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "cs53l32a",
+- .driverid = I2C_DRIVERID_CS53L32A,
+- .command = cs53l32a_command,
+ .remove = cs53l32a_remove,
+ .probe = cs53l32a_probe,
+ .id_table = cs53l32a_id,
+diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig
+index 8940b53..e8a50a6 100644
+--- a/drivers/media/video/cx18/Kconfig
++++ b/drivers/media/video/cx18/Kconfig
+@@ -9,7 +9,7 @@ config VIDEO_CX18
+ select VIDEO_CX2341X
+ select VIDEO_CS5345
+ select DVB_S5H1409 if !DVB_FE_CUSTOMISE
+- select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMIZE
++ select MEDIA_TUNER_MXL5005S if !MEDIA_TUNER_CUSTOMISE
+ ---help---
+ This is a video4linux driver for Conexant cx23418 based
+ PCI combo video recorder devices.
+diff --git a/drivers/media/video/cx18/cx18-audio.c b/drivers/media/video/cx18/cx18-audio.c
+index 57beddf..bb5c516 100644
+--- a/drivers/media/video/cx18/cx18-audio.c
++++ b/drivers/media/video/cx18/cx18-audio.c
+@@ -23,7 +23,6 @@
+
+ #include "cx18-driver.h"
+ #include "cx18-io.h"
+-#include "cx18-i2c.h"
+ #include "cx18-cards.h"
+ #include "cx18-audio.h"
+
+@@ -33,55 +32,32 @@
+ settings. */
+ int cx18_audio_set_io(struct cx18 *cx)
+ {
++ const struct cx18_card_audio_input *in;
+ struct v4l2_routing route;
+- u32 audio_input;
+ u32 val;
+- int mux_input;
+ int err;
+
+ /* Determine which input to use */
+- if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
+- audio_input = cx->card->radio_input.audio_input;
+- mux_input = cx->card->radio_input.muxer_input;
+- } else {
+- audio_input =
+- cx->card->audio_inputs[cx->audio_input].audio_input;
+- mux_input =
+- cx->card->audio_inputs[cx->audio_input].muxer_input;
+- }
++ if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags))
++ in = &cx->card->radio_input;
++ else
++ in = &cx->card->audio_inputs[cx->audio_input];
+
+ /* handle muxer chips */
+- route.input = mux_input;
++ route.input = in->muxer_input;
+ route.output = 0;
+- cx18_i2c_hw(cx, cx->card->hw_muxer, VIDIOC_INT_S_AUDIO_ROUTING, &route);
++ v4l2_subdev_call(cx->sd_extmux, audio, s_routing, &route);
+
+- route.input = audio_input;
+- err = cx18_i2c_hw(cx, cx->card->hw_audio_ctrl,
+- VIDIOC_INT_S_AUDIO_ROUTING, &route);
++ route.input = in->audio_input;
++ err = cx18_call_hw_err(cx, cx->card->hw_audio_ctrl,
++ audio, s_routing, &route);
+ if (err)
+ return err;
+
++ /* FIXME - this internal mux should be abstracted to a subdev */
+ val = cx18_read_reg(cx, CX18_AUDIO_ENABLE) & ~0x30;
+- val |= (audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 :
+- (audio_input << 4);
+- cx18_write_reg(cx, val | 0xb00, CX18_AUDIO_ENABLE);
+- cx18_vapi(cx, CX18_APU_RESETAI, 1, 0);
++ val |= (in->audio_input > CX18_AV_AUDIO_SERIAL2) ? 0x20 :
++ (in->audio_input << 4);
++ cx18_write_reg_expect(cx, val | 0xb00, CX18_AUDIO_ENABLE, val, 0x30);
+ return 0;
+ }
+-
+-void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route)
+-{
+- cx18_i2c_hw(cx, cx->card->hw_audio_ctrl,
+- VIDIOC_INT_S_AUDIO_ROUTING, route);
+-}
+-
+-void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq)
+-{
+- static u32 freqs[3] = { 44100, 48000, 32000 };
+-
+- /* The audio clock of the digitizer must match the codec sample
+- rate otherwise you get some very strange effects. */
+- if (freq > 2)
+- return;
+- cx18_call_i2c_clients(cx, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freqs[freq]);
+-}
+diff --git a/drivers/media/video/cx18/cx18-audio.h b/drivers/media/video/cx18/cx18-audio.h
+index cb569a6..2731d29 100644
+--- a/drivers/media/video/cx18/cx18-audio.h
++++ b/drivers/media/video/cx18/cx18-audio.h
+@@ -22,5 +22,3 @@
+ */
+
+ int cx18_audio_set_io(struct cx18 *cx);
+-void cx18_audio_set_route(struct cx18 *cx, struct v4l2_routing *route);
+-void cx18_audio_set_audio_clock_freq(struct cx18 *cx, u8 freq);
+diff --git a/drivers/media/video/cx18/cx18-av-audio.c b/drivers/media/video/cx18/cx18-av-audio.c
+index a2f0ad5..9e30983 100644
+--- a/drivers/media/video/cx18/cx18-av-audio.c
++++ b/drivers/media/video/cx18/cx18-av-audio.c
+@@ -464,82 +464,76 @@ static void set_mute(struct cx18 *cx, int mute)
+ }
+ }
+
+-int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg)
++int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+ {
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
+ struct cx18_av_state *state = &cx->av_state;
+- struct v4l2_control *ctrl = arg;
+ int retval;
++ u8 v;
+
+- switch (cmd) {
+- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+- {
+- u8 v;
+- if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
+- v = cx18_av_read(cx, 0x803) & ~0x10;
+- cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+- cx18_av_write(cx, 0x8d3, 0x1f);
+- }
+- v = cx18_av_read(cx, 0x810) | 0x1;
+- cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
++ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
++ v = cx18_av_read(cx, 0x803) & ~0x10;
++ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
++ cx18_av_write(cx, 0x8d3, 0x1f);
++ }
++ v = cx18_av_read(cx, 0x810) | 0x1;
++ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
+
+- retval = set_audclk_freq(cx, *(u32 *)arg);
++ retval = set_audclk_freq(cx, freq);
+
+- v = cx18_av_read(cx, 0x810) & ~0x1;
+- cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
+- if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
+- v = cx18_av_read(cx, 0x803) | 0x10;
+- cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+- }
+- return retval;
++ v = cx18_av_read(cx, 0x810) & ~0x1;
++ cx18_av_write_expect(cx, 0x810, v, v, 0x0f);
++ if (state->aud_input > CX18_AV_AUDIO_SERIAL2) {
++ v = cx18_av_read(cx, 0x803) | 0x10;
++ cx18_av_write_expect(cx, 0x803, v, v, 0x1f);
+ }
++ return retval;
++}
+
+- case VIDIOC_G_CTRL:
+- switch (ctrl->id) {
+- case V4L2_CID_AUDIO_VOLUME:
+- ctrl->value = get_volume(cx);
+- break;
+- case V4L2_CID_AUDIO_BASS:
+- ctrl->value = get_bass(cx);
+- break;
+- case V4L2_CID_AUDIO_TREBLE:
+- ctrl->value = get_treble(cx);
+- break;
+- case V4L2_CID_AUDIO_BALANCE:
+- ctrl->value = get_balance(cx);
+- break;
+- case V4L2_CID_AUDIO_MUTE:
+- ctrl->value = get_mute(cx);
+- break;
+- default:
+- return -EINVAL;
+- }
++int cx18_av_audio_g_ctrl(struct cx18 *cx, struct v4l2_control *ctrl)
++{
++ switch (ctrl->id) {
++ case V4L2_CID_AUDIO_VOLUME:
++ ctrl->value = get_volume(cx);
+ break;
+-
+- case VIDIOC_S_CTRL:
+- switch (ctrl->id) {
+- case V4L2_CID_AUDIO_VOLUME:
+- set_volume(cx, ctrl->value);
+- break;
+- case V4L2_CID_AUDIO_BASS:
+- set_bass(cx, ctrl->value);
+- break;
+- case V4L2_CID_AUDIO_TREBLE:
+- set_treble(cx, ctrl->value);
+- break;
+- case V4L2_CID_AUDIO_BALANCE:
+- set_balance(cx, ctrl->value);
+- break;
+- case V4L2_CID_AUDIO_MUTE:
+- set_mute(cx, ctrl->value);
+- break;
+- default:
+- return -EINVAL;
+- }
++ case V4L2_CID_AUDIO_BASS:
++ ctrl->value = get_bass(cx);
++ break;
++ case V4L2_CID_AUDIO_TREBLE:
++ ctrl->value = get_treble(cx);
++ break;
++ case V4L2_CID_AUDIO_BALANCE:
++ ctrl->value = get_balance(cx);
++ break;
++ case V4L2_CID_AUDIO_MUTE:
++ ctrl->value = get_mute(cx);
+ break;
+-
+ default:
+ return -EINVAL;
+ }
++ return 0;
++}
+
++int cx18_av_audio_s_ctrl(struct cx18 *cx, struct v4l2_control *ctrl)
++{
++ switch (ctrl->id) {
++ case V4L2_CID_AUDIO_VOLUME:
++ set_volume(cx, ctrl->value);
++ break;
++ case V4L2_CID_AUDIO_BASS:
++ set_bass(cx, ctrl->value);
++ break;
++ case V4L2_CID_AUDIO_TREBLE:
++ set_treble(cx, ctrl->value);
++ break;
++ case V4L2_CID_AUDIO_BALANCE:
++ set_balance(cx, ctrl->value);
++ break;
++ case V4L2_CID_AUDIO_MUTE:
++ set_mute(cx, ctrl->value);
++ break;
++ default:
++ return -EINVAL;
++ }
+ return 0;
+ }
+diff --git a/drivers/media/video/cx18/cx18-av-core.c b/drivers/media/video/cx18/cx18-av-core.c
+index 0b1c84b..f4dd9d7 100644
+--- a/drivers/media/video/cx18/cx18-av-core.c
++++ b/drivers/media/video/cx18/cx18-av-core.c
+@@ -22,8 +22,10 @@
+ * 02110-1301, USA.
+ */
+
++#include <media/v4l2-chip-ident.h>
+ #include "cx18-driver.h"
+ #include "cx18-io.h"
++#include "cx18-cards.h"
+
+ int cx18_av_write(struct cx18 *cx, u16 addr, u8 value)
+ {
+@@ -97,15 +99,6 @@ int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 and_mask,
+ or_value);
+ }
+
+-/* ----------------------------------------------------------------------- */
+-
+-static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
+- enum cx18_av_audio_input aud_input);
+-static void log_audio_status(struct cx18 *cx);
+-static void log_video_status(struct cx18 *cx);
+-
+-/* ----------------------------------------------------------------------- */
+-
+ static void cx18_av_initialize(struct cx18 *cx)
+ {
+ struct cx18_av_state *state = &cx->av_state;
+@@ -169,9 +162,14 @@ static void cx18_av_initialize(struct cx18 *cx)
+ /* Set VGA_TRACK_RANGE to 0x20 */
+ cx18_av_and_or4(cx, CXADEC_DFE_CTRL2, 0xFFFF00FF, 0x00002000);
+
+- /* Enable VBI capture */
+- cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253F);
+- /* cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4010253E); */
++ /*
++ * Initial VBI setup
++ * VIP-1.1, 10 bit mode, enable Raw, disable sliced,
++ * don't clamp raw samples when codes are in use, 1 byte user D-words,
++ * IDID0 has line #, RP code V bit transition on VBLANK, data during
++ * blanking intervals
++ */
++ cx18_av_write4(cx, CXADEC_OUT_CTRL1, 0x4013252e);
+
+ /* Set the video input.
+ The setting in MODE_CTRL gets lost when we do the above setup */
+@@ -195,11 +193,61 @@ static void cx18_av_initialize(struct cx18 *cx)
+ state->default_volume = ((state->default_volume / 2) + 23) << 9;
+ }
+
+-/* ----------------------------------------------------------------------- */
++static int cx18_av_reset(struct v4l2_subdev *sd, u32 val)
++{
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++
++ cx18_av_initialize(cx);
++ return 0;
++}
++
++static int cx18_av_init(struct v4l2_subdev *sd, u32 val)
++{
++ struct cx18_av_state *state = to_cx18_av_state(sd);
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++
++ switch (val) {
++ case CX18_AV_INIT_PLLS:
++ /*
++ * The crystal freq used in calculations in this driver will be
++ * 28.636360 MHz.
++ * Aim to run the PLLs' VCOs near 400 MHz to minimze errors.
++ */
++
++ /*
++ * VDCLK Integer = 0x0f, Post Divider = 0x04
++ * AIMCLK Integer = 0x0e, Post Divider = 0x16
++ */
++ cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f);
++
++ /* VDCLK Fraction = 0x2be2fe */
++ /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */
++ cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe);
++
++ /* AIMCLK Fraction = 0x05227ad */
++ /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz pre post-div*/
++ cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad);
++
++ /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
++ cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56);
++ break;
++
++ case CX18_AV_INIT_NORMAL:
++ default:
++ if (!state->is_initialized) {
++ /* initialize on first use */
++ state->is_initialized = 1;
++ cx18_av_initialize(cx);
++ }
++ break;
++ }
++ return 0;
++}
+
+ void cx18_av_std_setup(struct cx18 *cx)
+ {
+ struct cx18_av_state *state = &cx->av_state;
++ struct v4l2_subdev *sd = &state->sd;
+ v4l2_std_id std = state->std;
+ int hblank, hactive, burst, vblank, vactive, sc;
+ int vblank656, src_decimation;
+@@ -213,6 +261,7 @@ void cx18_av_std_setup(struct cx18 *cx)
+ cx18_av_write(cx, 0x49f, 0x14);
+
+ if (std & V4L2_STD_625_50) {
++ /* FIXME - revisit these for Sliced VBI */
+ hblank = 132;
+ hactive = 720;
+ burst = 93;
+@@ -236,13 +285,40 @@ void cx18_av_std_setup(struct cx18 *cx)
+ sc = 672351;
+ }
+ } else {
++ /*
++ * The following relationships of half line counts should hold:
++ * 525 = vsync + vactive + vblank656
++ * 12 = vblank656 - vblank
++ *
++ * vsync: always 6 half-lines of vsync pulses
++ * vactive: half lines of active video
++ * vblank656: half lines, after line 3/mid-266, of blanked video
++ * vblank: half lines, after line 9/272, of blanked video
++ *
++ * As far as I can tell:
++ * vblank656 starts counting from the falling edge of the first
++ * vsync pulse (start of line 4 or mid-266)
++ * vblank starts counting from the after the 6 vsync pulses and
++ * 6 or 5 equalization pulses (start of line 10 or 272)
++ *
++ * For 525 line systems the driver will extract VBI information
++ * from lines 10-21 and lines 273-284.
++ */
++ vblank656 = 38; /* lines 4 - 22 & 266 - 284 */
++ vblank = 26; /* lines 10 - 22 & 272 - 284 */
++ vactive = 481; /* lines 23 - 263 & 285 - 525 */
++
++ /*
++ * For a 13.5 Mpps clock and 15,734.26 Hz line rate, a line is
++ * is 858 pixels = 720 active + 138 blanking. The Hsync leading
++ * edge should happen 1.2 us * 13.5 Mpps ~= 16 pixels after the
++ * end of active video, leaving 122 pixels of hblank to ignore
++ * before active video starts.
++ */
+ hactive = 720;
+ hblank = 122;
+- vactive = 487;
+ luma_lpf = 1;
+ uv_lpf = 1;
+- vblank = 26;
+- vblank656 = 26;
+
+ src_decimation = 0x21f;
+ if (std == V4L2_STD_PAL_60) {
+@@ -265,33 +341,35 @@ void cx18_av_std_setup(struct cx18 *cx)
+ pll_int = cx18_av_read(cx, 0x108);
+ pll_frac = cx18_av_read4(cx, 0x10c) & 0x1ffffff;
+ pll_post = cx18_av_read(cx, 0x109);
+- CX18_DEBUG_INFO("PLL regs = int: %u, frac: %u, post: %u\n",
+- pll_int, pll_frac, pll_post);
++ CX18_DEBUG_INFO_DEV(sd, "PLL regs = int: %u, frac: %u, post: %u\n",
++ pll_int, pll_frac, pll_post);
+
+ if (pll_post) {
+ int fin, fsc, pll;
+
+ pll = (28636360L * ((((u64)pll_int) << 25) + pll_frac)) >> 25;
+ pll /= pll_post;
+- CX18_DEBUG_INFO("PLL = %d.%06d MHz\n",
+- pll / 1000000, pll % 1000000);
+- CX18_DEBUG_INFO("PLL/8 = %d.%06d MHz\n",
+- pll / 8000000, (pll / 8) % 1000000);
++ CX18_DEBUG_INFO_DEV(sd, "PLL = %d.%06d MHz\n",
++ pll / 1000000, pll % 1000000);
++ CX18_DEBUG_INFO_DEV(sd, "PLL/8 = %d.%06d MHz\n",
++ pll / 8000000, (pll / 8) % 1000000);
+
+ fin = ((u64)src_decimation * pll) >> 12;
+- CX18_DEBUG_INFO("ADC Sampling freq = %d.%06d MHz\n",
+- fin / 1000000, fin % 1000000);
++ CX18_DEBUG_INFO_DEV(sd, "ADC Sampling freq = %d.%06d MHz\n",
++ fin / 1000000, fin % 1000000);
+
+ fsc = (((u64)sc) * pll) >> 24L;
+- CX18_DEBUG_INFO("Chroma sub-carrier freq = %d.%06d MHz\n",
+- fsc / 1000000, fsc % 1000000);
+-
+- CX18_DEBUG_INFO("hblank %i, hactive %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,
+- src_decimation, burst, luma_lpf, uv_lpf, comb, sc);
++ CX18_DEBUG_INFO_DEV(sd,
++ "Chroma sub-carrier freq = %d.%06d MHz\n",
++ fsc / 1000000, fsc % 1000000);
++
++ CX18_DEBUG_INFO_DEV(sd, "hblank %i, hactive %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,
++ src_decimation, burst, luma_lpf, uv_lpf,
++ comb, sc);
+ }
+
+ /* Sets horizontal blanking delay and active lines */
+@@ -325,18 +403,16 @@ void cx18_av_std_setup(struct cx18 *cx)
+ cx18_av_write(cx, 0x47d, 0xff & sc >> 8);
+ cx18_av_write(cx, 0x47e, 0xff & sc >> 16);
+
+- /* Sets VBI parameters */
+ if (std & V4L2_STD_625_50) {
+- cx18_av_write(cx, 0x47f, 0x01);
+- state->vbi_line_offset = 5;
++ state->slicer_line_delay = 1;
++ state->slicer_line_offset = (6 + state->slicer_line_delay - 2);
+ } else {
+- cx18_av_write(cx, 0x47f, 0x00);
+- state->vbi_line_offset = 8;
++ state->slicer_line_delay = 0;
++ state->slicer_line_offset = (10 + state->slicer_line_delay - 2);
+ }
++ cx18_av_write(cx, 0x47f, state->slicer_line_delay);
+ }
+
+-/* ----------------------------------------------------------------------- */
+-
+ static void input_change(struct cx18 *cx)
+ {
+ struct cx18_av_state *state = &cx->av_state;
+@@ -382,17 +458,26 @@ static void input_change(struct cx18 *cx)
+ }
+ }
+
++static int cx18_av_s_frequency(struct v4l2_subdev *sd,
++ struct v4l2_frequency *freq)
++{
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++ input_change(cx);
++ return 0;
++}
++
+ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
+ enum cx18_av_audio_input aud_input)
+ {
+ struct cx18_av_state *state = &cx->av_state;
++ struct v4l2_subdev *sd = &state->sd;
+ u8 is_composite = (vid_input >= CX18_AV_COMPOSITE1 &&
+ vid_input <= CX18_AV_COMPOSITE8);
+ u8 reg;
+ u8 v;
+
+- CX18_DEBUG_INFO("decoder set video input %d, audio input %d\n",
+- vid_input, aud_input);
++ CX18_DEBUG_INFO_DEV(sd, "decoder set video input %d, audio input %d\n",
++ vid_input, aud_input);
+
+ if (is_composite) {
+ reg = 0xf0 + (vid_input - CX18_AV_COMPOSITE1);
+@@ -405,8 +490,8 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
+ luma > CX18_AV_SVIDEO_LUMA8 ||
+ chroma < CX18_AV_SVIDEO_CHROMA4 ||
+ chroma > CX18_AV_SVIDEO_CHROMA8) {
+- CX18_ERR("0x%04x is not a valid video input!\n",
+- vid_input);
++ CX18_ERR_DEV(sd, "0x%04x is not a valid video input!\n",
++ vid_input);
+ return -EINVAL;
+ }
+ reg = 0xf0 + ((luma - CX18_AV_SVIDEO_LUMA1) >> 4);
+@@ -431,7 +516,8 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
+ case CX18_AV_AUDIO8: reg &= ~0xc0; reg |= 0x40; break;
+
+ default:
+- CX18_ERR("0x%04x is not a valid audio input!\n", aud_input);
++ CX18_ERR_DEV(sd, "0x%04x is not a valid audio input!\n",
++ aud_input);
+ return -EINVAL;
+ }
+
+@@ -461,14 +547,118 @@ static int set_input(struct cx18 *cx, enum cx18_av_video_input vid_input,
+ return 0;
+ }
+
+-/* ----------------------------------------------------------------------- */
++static int cx18_av_s_video_routing(struct v4l2_subdev *sd,
++ const struct v4l2_routing *route)
++{
++ struct cx18_av_state *state = to_cx18_av_state(sd);
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++ return set_input(cx, route->input, state->aud_input);
++}
++
++static int cx18_av_s_audio_routing(struct v4l2_subdev *sd,
++ const struct v4l2_routing *route)
++{
++ struct cx18_av_state *state = to_cx18_av_state(sd);
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++ return set_input(cx, state->vid_input, route->input);
++}
+
+-static int set_v4lstd(struct cx18 *cx)
++static int cx18_av_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+ {
+- struct cx18_av_state *state = &cx->av_state;
++ struct cx18_av_state *state = to_cx18_av_state(sd);
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++ u8 vpres;
++ u8 mode;
++ int val = 0;
++
++ if (state->radio)
++ return 0;
++
++ vpres = cx18_av_read(cx, 0x40e) & 0x20;
++ vt->signal = vpres ? 0xffff : 0x0;
++
++ vt->capability |=
++ V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
++ V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
++
++ mode = cx18_av_read(cx, 0x804);
++
++ /* get rxsubchans and audmode */
++ if ((mode & 0xf) == 1)
++ val |= V4L2_TUNER_SUB_STEREO;
++ else
++ val |= V4L2_TUNER_SUB_MONO;
++
++ if (mode == 2 || mode == 4)
++ val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
++
++ if (mode & 0x10)
++ val |= V4L2_TUNER_SUB_SAP;
++
++ vt->rxsubchans = val;
++ vt->audmode = state->audmode;
++ return 0;
++}
++
++static int cx18_av_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
++{
++ struct cx18_av_state *state = to_cx18_av_state(sd);
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++ u8 v;
++
++ if (state->radio)
++ return 0;
++
++ v = cx18_av_read(cx, 0x809);
++ v &= ~0xf;
++
++ switch (vt->audmode) {
++ case V4L2_TUNER_MODE_MONO:
++ /* mono -> mono
++ stereo -> mono
++ bilingual -> lang1 */
++ break;
++ case V4L2_TUNER_MODE_STEREO:
++ case V4L2_TUNER_MODE_LANG1:
++ /* mono -> mono
++ stereo -> stereo
++ bilingual -> lang1 */
++ v |= 0x4;
++ break;
++ case V4L2_TUNER_MODE_LANG1_LANG2:
++ /* mono -> mono
++ stereo -> stereo
++ bilingual -> lang1/lang2 */
++ v |= 0x7;
++ break;
++ case V4L2_TUNER_MODE_LANG2:
++ /* mono -> mono
++ stereo -> stereo
++ bilingual -> lang2 */
++ v |= 0x1;
++ break;
++ default:
++ return -EINVAL;
++ }
++ cx18_av_write_expect(cx, 0x809, v, v, 0xff);
++ state->audmode = vt->audmode;
++ return 0;
++}
++
++static int cx18_av_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
++{
++ struct cx18_av_state *state = to_cx18_av_state(sd);
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++
+ u8 fmt = 0; /* zero is autodetect */
+ u8 pal_m = 0;
+
++ if (state->radio == 0 && state->std == norm)
++ return 0;
++
++ state->radio = 0;
++ state->std = norm;
++
+ /* First tests should be against specific std */
+ if (state->std == V4L2_STD_NTSC_M_JP) {
+ fmt = 0x2;
+@@ -493,7 +683,7 @@ static int set_v4lstd(struct cx18 *cx)
+ fmt = 0xc;
+ }
+
+- CX18_DEBUG_INFO("changing video std to fmt %i\n", fmt);
++ CX18_DEBUG_INFO_DEV(sd, "changing video std to fmt %i\n", fmt);
+
+ /* Follow step 9 of section 3.16 in the cx18_av datasheet.
+ Without this PAL may display a vertical ghosting effect.
+@@ -511,15 +701,22 @@ static int set_v4lstd(struct cx18 *cx)
+ return 0;
+ }
+
+-/* ----------------------------------------------------------------------- */
++static int cx18_av_s_radio(struct v4l2_subdev *sd)
++{
++ struct cx18_av_state *state = to_cx18_av_state(sd);
++ state->radio = 1;
++ return 0;
++}
+
+-static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
++static int cx18_av_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+ {
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+- CX18_ERR("invalid brightness setting %d\n",
+- ctrl->value);
++ CX18_ERR_DEV(sd, "invalid brightness setting %d\n",
++ ctrl->value);
+ return -ERANGE;
+ }
+
+@@ -528,8 +725,8 @@ static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
+
+ case V4L2_CID_CONTRAST:
+ if (ctrl->value < 0 || ctrl->value > 127) {
+- CX18_ERR("invalid contrast setting %d\n",
+- ctrl->value);
++ CX18_ERR_DEV(sd, "invalid contrast setting %d\n",
++ ctrl->value);
+ return -ERANGE;
+ }
+
+@@ -538,8 +735,8 @@ static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
+
+ case V4L2_CID_SATURATION:
+ if (ctrl->value < 0 || ctrl->value > 127) {
+- CX18_ERR("invalid saturation setting %d\n",
+- ctrl->value);
++ CX18_ERR_DEV(sd, "invalid saturation setting %d\n",
++ ctrl->value);
+ return -ERANGE;
+ }
+
+@@ -548,8 +745,9 @@ static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
+ break;
+
+ case V4L2_CID_HUE:
+- if (ctrl->value < -127 || ctrl->value > 127) {
+- CX18_ERR("invalid hue setting %d\n", ctrl->value);
++ if (ctrl->value < -128 || ctrl->value > 127) {
++ CX18_ERR_DEV(sd, "invalid hue setting %d\n",
++ ctrl->value);
+ return -ERANGE;
+ }
+
+@@ -561,17 +759,18 @@ static int set_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_MUTE:
+- return cx18_av_audio(cx, VIDIOC_S_CTRL, ctrl);
++ return cx18_av_audio_s_ctrl(cx, ctrl);
+
+ default:
+ return -EINVAL;
+ }
+-
+ return 0;
+ }
+
+-static int get_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
++static int cx18_av_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+ {
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ ctrl->value = (s8)cx18_av_read(cx, 0x414) + 128;
+@@ -590,31 +789,57 @@ static int get_v4lctrl(struct cx18 *cx, struct v4l2_control *ctrl)
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_MUTE:
+- return cx18_av_audio(cx, VIDIOC_G_CTRL, ctrl);
++ return cx18_av_audio_g_ctrl(cx, ctrl);
+ default:
+ return -EINVAL;
+ }
+-
+ return 0;
+ }
+
+-/* ----------------------------------------------------------------------- */
+-
+-static int get_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt)
++static int cx18_av_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+ {
+- switch (fmt->type) {
+- case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+- return cx18_av_vbi(cx, VIDIOC_G_FMT, fmt);
++ struct cx18_av_state *state = to_cx18_av_state(sd);
++
++ switch (qc->id) {
++ case V4L2_CID_BRIGHTNESS:
++ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
++ case V4L2_CID_CONTRAST:
++ case V4L2_CID_SATURATION:
++ return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
++ case V4L2_CID_HUE:
++ return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
++ default:
++ break;
++ }
++
++ switch (qc->id) {
++ case V4L2_CID_AUDIO_VOLUME:
++ return v4l2_ctrl_query_fill(qc, 0, 65535,
++ 65535 / 100, state->default_volume);
++ case V4L2_CID_AUDIO_MUTE:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
++ case V4L2_CID_AUDIO_BALANCE:
++ case V4L2_CID_AUDIO_BASS:
++ case V4L2_CID_AUDIO_TREBLE:
++ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
+ default:
+ return -EINVAL;
+ }
++ return -EINVAL;
++}
+
+- return 0;
++static int cx18_av_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
++{
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++
++ return cx18_av_vbi_g_fmt(cx, fmt);
+ }
+
+-static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt)
++static int cx18_av_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+ {
+- struct cx18_av_state *state = &cx->av_state;
++ struct cx18_av_state *state = to_cx18_av_state(sd);
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++
+ struct v4l2_pix_format *pix;
+ int HSC, VSC, Vsrc, Hsrc, filter, Vlines;
+ int is_50Hz = !(state->std & V4L2_STD_525_60);
+@@ -629,12 +854,26 @@ static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt)
+ Hsrc = (cx18_av_read(cx, 0x472) & 0x3f) << 4;
+ Hsrc |= (cx18_av_read(cx, 0x471) & 0xf0) >> 4;
+
+- Vlines = pix->height + (is_50Hz ? 4 : 7);
+-
++ /*
++ * This adjustment reflects the excess of vactive, set in
++ * cx18_av_std_setup(), above standard values:
++ *
++ * 480 + 1 for 60 Hz systems
++ * 576 + 4 for 50 Hz systems
++ */
++ Vlines = pix->height + (is_50Hz ? 4 : 1);
++
++ /*
++ * Invalid height and width scaling requests are:
++ * 1. width less than 1/16 of the source width
++ * 2. width greater than the source width
++ * 3. height less than 1/8 of the source height
++ * 4. height greater than the source height
++ */
+ if ((pix->width * 16 < Hsrc) || (Hsrc < pix->width) ||
+ (Vlines * 8 < Vsrc) || (Vsrc < Vlines)) {
+- CX18_ERR("%dx%d is not a valid size!\n",
+- pix->width, pix->height);
++ CX18_ERR_DEV(sd, "%dx%d is not a valid size!\n",
++ pix->width, pix->height);
+ return -ERANGE;
+ }
+
+@@ -651,8 +890,9 @@ static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt)
+ else
+ filter = 3;
+
+- CX18_DEBUG_INFO("decoder set size %dx%d -> scale %ux%u\n",
+- pix->width, pix->height, HSC, VSC);
++ CX18_DEBUG_INFO_DEV(sd,
++ "decoder set size %dx%d -> scale %ux%u\n",
++ pix->width, pix->height, HSC, VSC);
+
+ /* HSCALE=HSC */
+ cx18_av_write(cx, 0x418, HSC & 0xff);
+@@ -666,231 +906,32 @@ static int set_v4lfmt(struct cx18 *cx, struct v4l2_format *fmt)
+ break;
+
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+- return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt);
++ return cx18_av_vbi_s_fmt(cx, fmt);
+
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+- return cx18_av_vbi(cx, VIDIOC_S_FMT, fmt);
++ return cx18_av_vbi_s_fmt(cx, fmt);
+
+ default:
+ return -EINVAL;
+ }
+-
+ return 0;
+ }
+
+-/* ----------------------------------------------------------------------- */
+-
+-int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg)
++static int cx18_av_s_stream(struct v4l2_subdev *sd, int enable)
+ {
+- struct cx18_av_state *state = &cx->av_state;
+- struct v4l2_tuner *vt = arg;
+- struct v4l2_routing *route = arg;
+-
+- /* ignore these commands */
+- switch (cmd) {
+- case TUNER_SET_TYPE_ADDR:
+- return 0;
+- }
+-
+- if (!state->is_initialized) {
+- CX18_DEBUG_INFO("cmd %08x triggered fw load\n", cmd);
+- /* initialize on first use */
+- state->is_initialized = 1;
+- cx18_av_initialize(cx);
+- }
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+- switch (cmd) {
+- case VIDIOC_INT_DECODE_VBI_LINE:
+- return cx18_av_vbi(cx, cmd, arg);
+-
+- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+- return cx18_av_audio(cx, cmd, arg);
+-
+- case VIDIOC_STREAMON:
+- CX18_DEBUG_INFO("enable output\n");
++ CX18_DEBUG_INFO_DEV(sd, "%s output\n", enable ? "enable" : "disable");
++ if (enable) {
+ cx18_av_write(cx, 0x115, 0x8c);
+ cx18_av_write(cx, 0x116, 0x07);
+- break;
+-
+- case VIDIOC_STREAMOFF:
+- CX18_DEBUG_INFO("disable output\n");
++ } else {
+ cx18_av_write(cx, 0x115, 0x00);
+ cx18_av_write(cx, 0x116, 0x00);
+- break;
+-
+- case VIDIOC_LOG_STATUS:
+- log_video_status(cx);
+- log_audio_status(cx);
+- break;
+-
+- case VIDIOC_G_CTRL:
+- return get_v4lctrl(cx, (struct v4l2_control *)arg);
+-
+- case VIDIOC_S_CTRL:
+- return set_v4lctrl(cx, (struct v4l2_control *)arg);
+-
+- case VIDIOC_QUERYCTRL:
+- {
+- struct v4l2_queryctrl *qc = arg;
+-
+- switch (qc->id) {
+- case V4L2_CID_BRIGHTNESS:
+- case V4L2_CID_CONTRAST:
+- case V4L2_CID_SATURATION:
+- case V4L2_CID_HUE:
+- return v4l2_ctrl_query_fill_std(qc);
+- default:
+- break;
+- }
+-
+- switch (qc->id) {
+- case V4L2_CID_AUDIO_VOLUME:
+- return v4l2_ctrl_query_fill(qc, 0, 65535,
+- 65535 / 100, state->default_volume);
+- case V4L2_CID_AUDIO_MUTE:
+- case V4L2_CID_AUDIO_BALANCE:
+- case V4L2_CID_AUDIO_BASS:
+- case V4L2_CID_AUDIO_TREBLE:
+- return v4l2_ctrl_query_fill_std(qc);
+- default:
+- return -EINVAL;
+- }
+- return -EINVAL;
+- }
+-
+- case VIDIOC_G_STD:
+- *(v4l2_std_id *)arg = state->std;
+- break;
+-
+- case VIDIOC_S_STD:
+- if (state->radio == 0 && state->std == *(v4l2_std_id *)arg)
+- return 0;
+- state->radio = 0;
+- state->std = *(v4l2_std_id *)arg;
+- return set_v4lstd(cx);
+-
+- case AUDC_SET_RADIO:
+- state->radio = 1;
+- break;
+-
+- case VIDIOC_INT_G_VIDEO_ROUTING:
+- route->input = state->vid_input;
+- route->output = 0;
+- break;
+-
+- case VIDIOC_INT_S_VIDEO_ROUTING:
+- return set_input(cx, route->input, state->aud_input);
+-
+- case VIDIOC_INT_G_AUDIO_ROUTING:
+- route->input = state->aud_input;
+- route->output = 0;
+- break;
+-
+- case VIDIOC_INT_S_AUDIO_ROUTING:
+- return set_input(cx, state->vid_input, route->input);
+-
+- case VIDIOC_S_FREQUENCY:
+- input_change(cx);
+- break;
+-
+- case VIDIOC_G_TUNER:
+- {
+- u8 vpres = cx18_av_read(cx, 0x40e) & 0x20;
+- u8 mode;
+- int val = 0;
+-
+- if (state->radio)
+- break;
+-
+- vt->signal = vpres ? 0xffff : 0x0;
+-
+- vt->capability |=
+- V4L2_TUNER_CAP_STEREO | V4L2_TUNER_CAP_LANG1 |
+- V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+-
+- mode = cx18_av_read(cx, 0x804);
+-
+- /* get rxsubchans and audmode */
+- if ((mode & 0xf) == 1)
+- val |= V4L2_TUNER_SUB_STEREO;
+- else
+- val |= V4L2_TUNER_SUB_MONO;
+-
+- if (mode == 2 || mode == 4)
+- val = V4L2_TUNER_SUB_LANG1 | V4L2_TUNER_SUB_LANG2;
+-
+- if (mode & 0x10)
+- val |= V4L2_TUNER_SUB_SAP;
+-
+- vt->rxsubchans = val;
+- vt->audmode = state->audmode;
+- break;
+- }
+-
+- case VIDIOC_S_TUNER:
+- {
+- u8 v;
+-
+- if (state->radio)
+- break;
+-
+- v = cx18_av_read(cx, 0x809);
+- v &= ~0xf;
+-
+- switch (vt->audmode) {
+- case V4L2_TUNER_MODE_MONO:
+- /* mono -> mono
+- stereo -> mono
+- bilingual -> lang1 */
+- break;
+- case V4L2_TUNER_MODE_STEREO:
+- case V4L2_TUNER_MODE_LANG1:
+- /* mono -> mono
+- stereo -> stereo
+- bilingual -> lang1 */
+- v |= 0x4;
+- break;
+- case V4L2_TUNER_MODE_LANG1_LANG2:
+- /* mono -> mono
+- stereo -> stereo
+- bilingual -> lang1/lang2 */
+- v |= 0x7;
+- break;
+- case V4L2_TUNER_MODE_LANG2:
+- /* mono -> mono
+- stereo -> stereo
+- bilingual -> lang2 */
+- v |= 0x1;
+- break;
+- default:
+- return -EINVAL;
+- }
+- cx18_av_write_expect(cx, 0x809, v, v, 0xff);
+- state->audmode = vt->audmode;
+- break;
+ }
+-
+- case VIDIOC_G_FMT:
+- return get_v4lfmt(cx, (struct v4l2_format *)arg);
+-
+- case VIDIOC_S_FMT:
+- return set_v4lfmt(cx, (struct v4l2_format *)arg);
+-
+- case VIDIOC_INT_RESET:
+- cx18_av_initialize(cx);
+- break;
+-
+- default:
+- return -EINVAL;
+- }
+-
+ return 0;
+ }
+
+-/* ----------------------------------------------------------------------- */
+-
+-/* ----------------------------------------------------------------------- */
+-
+ static void log_video_status(struct cx18 *cx)
+ {
+ static const char *const fmt_strs[] = {
+@@ -903,36 +944,40 @@ static void log_video_status(struct cx18 *cx)
+ };
+
+ struct cx18_av_state *state = &cx->av_state;
++ struct v4l2_subdev *sd = &state->sd;
+ u8 vidfmt_sel = cx18_av_read(cx, 0x400) & 0xf;
+ u8 gen_stat1 = cx18_av_read(cx, 0x40d);
+ u8 gen_stat2 = cx18_av_read(cx, 0x40e);
+ int vid_input = state->vid_input;
+
+- CX18_INFO("Video signal: %spresent\n",
+- (gen_stat2 & 0x20) ? "" : "not ");
+- CX18_INFO("Detected format: %s\n",
+- fmt_strs[gen_stat1 & 0xf]);
++ CX18_INFO_DEV(sd, "Video signal: %spresent\n",
++ (gen_stat2 & 0x20) ? "" : "not ");
++ CX18_INFO_DEV(sd, "Detected format: %s\n",
++ fmt_strs[gen_stat1 & 0xf]);
+
+- CX18_INFO("Specified standard: %s\n",
+- vidfmt_sel ? fmt_strs[vidfmt_sel] : "automatic detection");
++ CX18_INFO_DEV(sd, "Specified standard: %s\n",
++ vidfmt_sel ? fmt_strs[vidfmt_sel]
++ : "automatic detection");
+
+ if (vid_input >= CX18_AV_COMPOSITE1 &&
+ vid_input <= CX18_AV_COMPOSITE8) {
+- CX18_INFO("Specified video input: Composite %d\n",
+- vid_input - CX18_AV_COMPOSITE1 + 1);
++ CX18_INFO_DEV(sd, "Specified video input: Composite %d\n",
++ vid_input - CX18_AV_COMPOSITE1 + 1);
+ } else {
+- CX18_INFO("Specified video input: S-Video (Luma In%d, Chroma In%d)\n",
+- (vid_input & 0xf0) >> 4, (vid_input & 0xf00) >> 8);
++ CX18_INFO_DEV(sd, "Specified video input: "
++ "S-Video (Luma In%d, Chroma In%d)\n",
++ (vid_input & 0xf0) >> 4,
++ (vid_input & 0xf00) >> 8);
+ }
+
+- CX18_INFO("Specified audioclock freq: %d Hz\n", state->audclk_freq);
++ CX18_INFO_DEV(sd, "Specified audioclock freq: %d Hz\n",
++ state->audclk_freq);
+ }
+
+-/* ----------------------------------------------------------------------- */
+-
+ static void log_audio_status(struct cx18 *cx)
+ {
+ struct cx18_av_state *state = &cx->av_state;
++ struct v4l2_subdev *sd = &state->sd;
+ u8 download_ctl = cx18_av_read(cx, 0x803);
+ u8 mod_det_stat0 = cx18_av_read(cx, 0x804);
+ u8 mod_det_stat1 = cx18_av_read(cx, 0x805);
+@@ -955,7 +1000,7 @@ static void log_audio_status(struct cx18 *cx)
+ case 0xfe: p = "forced mode"; break;
+ default: p = "not defined"; break;
+ }
+- CX18_INFO("Detected audio mode: %s\n", p);
++ CX18_INFO_DEV(sd, "Detected audio mode: %s\n", p);
+
+ switch (mod_det_stat1) {
+ case 0x00: p = "not defined"; break;
+@@ -980,11 +1025,11 @@ static void log_audio_status(struct cx18 *cx)
+ case 0xff: p = "no detected audio standard"; break;
+ default: p = "not defined"; break;
+ }
+- CX18_INFO("Detected audio standard: %s\n", p);
+- CX18_INFO("Audio muted: %s\n",
+- (mute_ctl & 0x2) ? "yes" : "no");
+- CX18_INFO("Audio microcontroller: %s\n",
+- (download_ctl & 0x10) ? "running" : "stopped");
++ CX18_INFO_DEV(sd, "Detected audio standard: %s\n", p);
++ CX18_INFO_DEV(sd, "Audio muted: %s\n",
++ (mute_ctl & 0x2) ? "yes" : "no");
++ CX18_INFO_DEV(sd, "Audio microcontroller: %s\n",
++ (download_ctl & 0x10) ? "running" : "stopped");
+
+ switch (audio_config >> 4) {
+ case 0x00: p = "undefined"; break;
+@@ -1005,7 +1050,7 @@ static void log_audio_status(struct cx18 *cx)
+ case 0x0f: p = "automatic detection"; break;
+ default: p = "undefined"; break;
+ }
+- CX18_INFO("Configured audio standard: %s\n", p);
++ CX18_INFO_DEV(sd, "Configured audio standard: %s\n", p);
+
+ if ((audio_config >> 4) < 0xF) {
+ switch (audio_config & 0xF) {
+@@ -1019,7 +1064,7 @@ static void log_audio_status(struct cx18 *cx)
+ case 0x07: p = "DUAL3 (AB)"; break;
+ default: p = "undefined";
+ }
+- CX18_INFO("Configured audio mode: %s\n", p);
++ CX18_INFO_DEV(sd, "Configured audio mode: %s\n", p);
+ } else {
+ switch (audio_config & 0xF) {
+ case 0x00: p = "BG"; break;
+@@ -1037,14 +1082,14 @@ static void log_audio_status(struct cx18 *cx)
+ case 0x0f: p = "automatic standard and mode detection"; break;
+ default: p = "undefined"; break;
+ }
+- CX18_INFO("Configured audio system: %s\n", p);
++ CX18_INFO_DEV(sd, "Configured audio system: %s\n", p);
+ }
+
+ if (aud_input)
+- CX18_INFO("Specified audio input: Tuner (In%d)\n",
+- aud_input);
++ CX18_INFO_DEV(sd, "Specified audio input: Tuner (In%d)\n",
++ aud_input);
+ else
+- CX18_INFO("Specified audio input: External\n");
++ CX18_INFO_DEV(sd, "Specified audio input: External\n");
+
+ switch (pref_mode & 0xf) {
+ case 0: p = "mono/language A"; break;
+@@ -1057,14 +1102,14 @@ static void log_audio_status(struct cx18 *cx)
+ case 7: p = "language AB"; break;
+ default: p = "undefined"; break;
+ }
+- CX18_INFO("Preferred audio mode: %s\n", p);
++ CX18_INFO_DEV(sd, "Preferred audio mode: %s\n", p);
+
+ if ((audio_config & 0xf) == 0xf) {
+ switch ((afc0 >> 3) & 0x1) {
+ case 0: p = "system DK"; break;
+ case 1: p = "system L"; break;
+ }
+- CX18_INFO("Selected 65 MHz format: %s\n", p);
++ CX18_INFO_DEV(sd, "Selected 65 MHz format: %s\n", p);
+
+ switch (afc0 & 0x7) {
+ case 0: p = "Chroma"; break;
+@@ -1074,6 +1119,131 @@ static void log_audio_status(struct cx18 *cx)
+ case 4: p = "autodetect"; break;
+ default: p = "undefined"; break;
+ }
+- CX18_INFO("Selected 45 MHz format: %s\n", p);
++ CX18_INFO_DEV(sd, "Selected 45 MHz format: %s\n", p);
+ }
+ }
++
++static int cx18_av_log_status(struct v4l2_subdev *sd)
++{
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++ log_video_status(cx);
++ log_audio_status(cx);
++ return 0;
++}
++
++static inline int cx18_av_dbg_match(const struct v4l2_dbg_match *match)
++{
++ return match->type == V4L2_CHIP_MATCH_HOST && match->addr == 1;
++}
++
++static int cx18_av_g_chip_ident(struct v4l2_subdev *sd,
++ struct v4l2_dbg_chip_ident *chip)
++{
++ struct cx18_av_state *state = to_cx18_av_state(sd);
++
++ if (cx18_av_dbg_match(&chip->match)) {
++ chip->ident = state->id;
++ chip->revision = state->rev;
++ }
++ return 0;
++}
++
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++static int cx18_av_g_register(struct v4l2_subdev *sd,
++ struct v4l2_dbg_register *reg)
++{
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++
++ if (!cx18_av_dbg_match(&reg->match))
++ return -EINVAL;
++ if ((reg->reg & 0x3) != 0)
++ return -EINVAL;
++ if (!capable(CAP_SYS_ADMIN))
++ return -EPERM;
++ reg->size = 4;
++ reg->val = cx18_av_read4(cx, reg->reg & 0x00000ffc);
++ return 0;
++}
++
++static int cx18_av_s_register(struct v4l2_subdev *sd,
++ struct v4l2_dbg_register *reg)
++{
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++
++ if (!cx18_av_dbg_match(&reg->match))
++ return -EINVAL;
++ if ((reg->reg & 0x3) != 0)
++ return -EINVAL;
++ if (!capable(CAP_SYS_ADMIN))
++ return -EPERM;
++ cx18_av_write4(cx, reg->reg & 0x00000ffc, reg->val);
++ return 0;
++}
++#endif
++
++static const struct v4l2_subdev_core_ops cx18_av_general_ops = {
++ .g_chip_ident = cx18_av_g_chip_ident,
++ .log_status = cx18_av_log_status,
++ .init = cx18_av_init,
++ .reset = cx18_av_reset,
++ .queryctrl = cx18_av_queryctrl,
++ .g_ctrl = cx18_av_g_ctrl,
++ .s_ctrl = cx18_av_s_ctrl,
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++ .g_register = cx18_av_g_register,
++ .s_register = cx18_av_s_register,
++#endif
++};
++
++static const struct v4l2_subdev_tuner_ops cx18_av_tuner_ops = {
++ .s_radio = cx18_av_s_radio,
++ .s_frequency = cx18_av_s_frequency,
++ .g_tuner = cx18_av_g_tuner,
++ .s_tuner = cx18_av_s_tuner,
++ .s_std = cx18_av_s_std,
++};
++
++static const struct v4l2_subdev_audio_ops cx18_av_audio_ops = {
++ .s_clock_freq = cx18_av_s_clock_freq,
++ .s_routing = cx18_av_s_audio_routing,
++};
++
++static const struct v4l2_subdev_video_ops cx18_av_video_ops = {
++ .s_routing = cx18_av_s_video_routing,
++ .decode_vbi_line = cx18_av_decode_vbi_line,
++ .s_stream = cx18_av_s_stream,
++ .g_fmt = cx18_av_g_fmt,
++ .s_fmt = cx18_av_s_fmt,
++};
++
++static const struct v4l2_subdev_ops cx18_av_ops = {
++ .core = &cx18_av_general_ops,
++ .tuner = &cx18_av_tuner_ops,
++ .audio = &cx18_av_audio_ops,
++ .video = &cx18_av_video_ops,
++};
++
++int cx18_av_probe(struct cx18 *cx)
++{
++ struct cx18_av_state *state = &cx->av_state;
++ struct v4l2_subdev *sd;
++
++ state->rev = cx18_av_read4(cx, CXADEC_CHIP_CTRL) & 0xffff;
++ state->id = ((state->rev >> 4) == CXADEC_CHIP_TYPE_MAKO)
++ ? V4L2_IDENT_CX23418_843 : V4L2_IDENT_UNKNOWN;
++
++ state->vid_input = CX18_AV_COMPOSITE7;
++ state->aud_input = CX18_AV_AUDIO8;
++ state->audclk_freq = 48000;
++ state->audmode = V4L2_TUNER_MODE_LANG1;
++ state->slicer_line_delay = 0;
++ state->slicer_line_offset = (10 + state->slicer_line_delay - 2);
++
++ sd = &state->sd;
++ v4l2_subdev_init(sd, &cx18_av_ops);
++ v4l2_set_subdevdata(sd, cx);
++ snprintf(sd->name, sizeof(sd->name),
++ "%s %03x", cx->v4l2_dev.name, (state->rev >> 4));
++ sd->grp_id = CX18_HW_418_AV;
++ return v4l2_device_register_subdev(&cx->v4l2_dev, sd);
++}
+diff --git a/drivers/media/video/cx18/cx18-av-core.h b/drivers/media/video/cx18/cx18-av-core.h
+index cf68a60..c458120 100644
+--- a/drivers/media/video/cx18/cx18-av-core.h
++++ b/drivers/media/video/cx18/cx18-av-core.h
+@@ -25,6 +25,8 @@
+ #ifndef _CX18_AV_CORE_H_
+ #define _CX18_AV_CORE_H_
+
++#include <media/v4l2-device.h>
++
+ struct cx18;
+
+ enum cx18_av_video_input {
+@@ -73,17 +75,40 @@ enum cx18_av_audio_input {
+ };
+
+ struct cx18_av_state {
++ struct v4l2_subdev sd;
+ int radio;
+ v4l2_std_id std;
+ enum cx18_av_video_input vid_input;
+ enum cx18_av_audio_input aud_input;
+ u32 audclk_freq;
+ int audmode;
+- int vbi_line_offset;
+ int default_volume;
+ u32 id;
+ u32 rev;
+ int is_initialized;
++
++ /*
++ * The VBI slicer starts operating and counting lines, begining at
++ * slicer line count of 1, at D lines after the deassertion of VRESET.
++ * This staring field line, S, is 6 (& 319) or 10 (& 273) for 625 or 525
++ * line systems respectively. Sliced ancillary data captured on VBI
++ * slicer line M is inserted after the VBI slicer is done with line M,
++ * when VBI slicer line count is N = M+1. Thus when the VBI slicer
++ * reports a VBI slicer line number with ancillary data, the IDID0 byte
++ * indicates VBI slicer line N. The actual field line that the captured
++ * data comes from is
++ *
++ * L = M+(S+D-1) = N-1+(S+D-1) = N + (S+D-2).
++ *
++ * L is the line in the field, not frame, from which the VBI data came.
++ * N is the line reported by the slicer in the ancillary data.
++ * D is the slicer_line_delay value programmed into register 0x47f.
++ * S is 6 for 625 line systems or 10 for 525 line systems
++ * (S+D-2) is the slicer_line_offset used to convert slicer reported
++ * line counts to actual field lines.
++ */
++ int slicer_line_delay;
++ int slicer_line_offset;
+ };
+
+
+@@ -298,6 +323,16 @@ struct cx18_av_state {
+ #define CXADEC_SELECT_AUDIO_STANDARD_FM 0xF9 /* FM radio */
+ #define CXADEC_SELECT_AUDIO_STANDARD_AUTO 0xFF /* Auto detect */
+
++static inline struct cx18_av_state *to_cx18_av_state(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct cx18_av_state, sd);
++}
++
++enum cx18_av_subdev_init_arg {
++ CX18_AV_INIT_NORMAL = 0,
++ CX18_AV_INIT_PLLS = 1,
++};
++
+ /* ----------------------------------------------------------------------- */
+ /* cx18_av-core.c */
+ int cx18_av_write(struct cx18 *cx, u16 addr, u8 value);
+@@ -310,20 +345,26 @@ u8 cx18_av_read(struct cx18 *cx, u16 addr);
+ u32 cx18_av_read4(struct cx18 *cx, u16 addr);
+ int cx18_av_and_or(struct cx18 *cx, u16 addr, unsigned mask, u8 value);
+ int cx18_av_and_or4(struct cx18 *cx, u16 addr, u32 mask, u32 value);
+-int cx18_av_cmd(struct cx18 *cx, unsigned int cmd, void *arg);
+ void cx18_av_std_setup(struct cx18 *cx);
+
++int cx18_av_probe(struct cx18 *cx);
++
+ /* ----------------------------------------------------------------------- */
+ /* cx18_av-firmware.c */
+ int cx18_av_loadfw(struct cx18 *cx);
+
+ /* ----------------------------------------------------------------------- */
+ /* cx18_av-audio.c */
+-int cx18_av_audio(struct cx18 *cx, unsigned int cmd, void *arg);
++int cx18_av_audio_g_ctrl(struct cx18 *cx, struct v4l2_control *ctrl);
++int cx18_av_audio_s_ctrl(struct cx18 *cx, struct v4l2_control *ctrl);
++int cx18_av_s_clock_freq(struct v4l2_subdev *sd, u32 freq);
+ void cx18_av_audio_set_path(struct cx18 *cx);
+
+ /* ----------------------------------------------------------------------- */
+ /* cx18_av-vbi.c */
+-int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg);
++int cx18_av_decode_vbi_line(struct v4l2_subdev *sd,
++ struct v4l2_decode_vbi_line *vbi);
++int cx18_av_vbi_g_fmt(struct cx18 *cx, struct v4l2_format *fmt);
++int cx18_av_vbi_s_fmt(struct cx18 *cx, struct v4l2_format *fmt);
+
+ #endif
+diff --git a/drivers/media/video/cx18/cx18-av-firmware.c b/drivers/media/video/cx18/cx18-av-firmware.c
+index c64fd0a..49a55cc 100644
+--- a/drivers/media/video/cx18/cx18-av-firmware.c
++++ b/drivers/media/video/cx18/cx18-av-firmware.c
+@@ -29,6 +29,7 @@
+
+ int cx18_av_loadfw(struct cx18 *cx)
+ {
++ struct v4l2_subdev *sd = &cx->av_state.sd;
+ const struct firmware *fw = NULL;
+ u32 size;
+ u32 v;
+@@ -36,8 +37,8 @@ int cx18_av_loadfw(struct cx18 *cx)
+ int i;
+ int retries1 = 0;
+
+- if (request_firmware(&fw, FWFILE, &cx->dev->dev) != 0) {
+- CX18_ERR("unable to open firmware %s\n", FWFILE);
++ if (request_firmware(&fw, FWFILE, &cx->pci_dev->dev) != 0) {
++ CX18_ERR_DEV(sd, "unable to open firmware %s\n", FWFILE);
+ return -EINVAL;
+ }
+
+@@ -88,7 +89,7 @@ int cx18_av_loadfw(struct cx18 *cx)
+ retries1++;
+ }
+ if (retries1 >= 5) {
+- CX18_ERR("unable to load firmware %s\n", FWFILE);
++ CX18_ERR_DEV(sd, "unable to load firmware %s\n", FWFILE);
+ release_firmware(fw);
+ return -EIO;
+ }
+@@ -115,9 +116,9 @@ int cx18_av_loadfw(struct cx18 *cx)
+ are generated) */
+ cx18_av_write4(cx, CXADEC_I2S_OUT_CTL, 0x000001A0);
+
+- /* set alt I2s master clock to /16 and enable alt divider i2s
++ /* set alt I2s master clock to /0x16 and enable alt divider i2s
+ passthrough */
+- cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5000B687);
++ cx18_av_write4(cx, CXADEC_PIN_CFG3, 0x5600B687);
+
+ cx18_av_write4_expect(cx, CXADEC_STD_DET_CTL, 0x000000F6, 0x000000F6,
+ 0x3F00FFFF);
+@@ -131,7 +132,8 @@ int cx18_av_loadfw(struct cx18 *cx)
+ v = cx18_read_reg(cx, CX18_AUDIO_ENABLE);
+ /* If bit 11 is 1, clear bit 10 */
+ if (v & 0x800)
+- cx18_write_reg(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE);
++ cx18_write_reg_expect(cx, v & 0xFFFFFBFF, CX18_AUDIO_ENABLE,
++ 0, 0x400);
+
+ /* Enable WW auto audio standard detection */
+ v = cx18_av_read4(cx, CXADEC_STD_DET_CTL);
+@@ -142,6 +144,6 @@ int cx18_av_loadfw(struct cx18 *cx)
+
+ release_firmware(fw);
+
+- CX18_INFO("loaded %s firmware (%d bytes)\n", FWFILE, size);
++ CX18_INFO_DEV(sd, "loaded %s firmware (%d bytes)\n", FWFILE, size);
+ return 0;
+ }
+diff --git a/drivers/media/video/cx18/cx18-av-vbi.c b/drivers/media/video/cx18/cx18-av-vbi.c
+index 1527ea4..23b3167 100644
+--- a/drivers/media/video/cx18/cx18-av-vbi.c
++++ b/drivers/media/video/cx18/cx18-av-vbi.c
+@@ -24,6 +24,52 @@
+
+ #include "cx18-driver.h"
+
++/*
++ * For sliced VBI output, we set up to use VIP-1.1, 8-bit mode,
++ * NN counts 1 byte Dwords, an IDID with the VBI line # in it.
++ * Thus, according to the VIP-2 Spec, our VBI ancillary data lines
++ * (should!) look like:
++ * 4 byte EAV code: 0xff 0x00 0x00 0xRP
++ * unknown number of possible idle bytes
++ * 3 byte Anc data preamble: 0x00 0xff 0xff
++ * 1 byte data identifier: ne010iii (parity bits, 010, DID bits)
++ * 1 byte secondary data id: nessssss (parity bits, SDID bits)
++ * 1 byte data word count: necccccc (parity bits, NN Dword count)
++ * 2 byte Internal DID: VBI-line-# 0x80
++ * NN data bytes
++ * 1 byte checksum
++ * Fill bytes needed to fil out to 4*NN bytes of payload
++ *
++ * The RP codes for EAVs when in VIP-1.1 mode, not in raw mode, &
++ * in the vertical blanking interval are:
++ * 0xb0 (Task 0 VerticalBlank HorizontalBlank 0 0 0 0)
++ * 0xf0 (Task EvenField VerticalBlank HorizontalBlank 0 0 0 0)
++ *
++ * Since the V bit is only allowed to toggle in the EAV RP code, just
++ * before the first active region line and for active lines, they are:
++ * 0x90 (Task 0 0 HorizontalBlank 0 0 0 0)
++ * 0xd0 (Task EvenField 0 HorizontalBlank 0 0 0 0)
++ *
++ * The user application DID bytes we care about are:
++ * 0x91 (1 0 010 0 !ActiveLine AncDataPresent)
++ * 0x55 (0 1 010 2ndField !ActiveLine AncDataPresent)
++ *
++ */
++static const u8 sliced_vbi_did[2] = { 0x91, 0x55 };
++
++struct vbi_anc_data {
++ /* u8 eav[4]; */
++ /* u8 idle[]; Variable number of idle bytes */
++ u8 preamble[3];
++ u8 did;
++ u8 sdid;
++ u8 data_count;
++ u8 idid[2];
++ u8 payload[1]; /* data_count of payload */
++ /* u8 checksum; */
++ /* u8 fill[]; Variable number of fill bytes */
++};
++
+ static int odd_parity(u8 c)
+ {
+ c ^= (c >> 4);
+@@ -83,188 +129,189 @@ static int decode_vps(u8 *dst, u8 *p)
+ return err & 0xf0;
+ }
+
+-int cx18_av_vbi(struct cx18 *cx, unsigned int cmd, void *arg)
++int cx18_av_vbi_g_fmt(struct cx18 *cx, struct v4l2_format *fmt)
+ {
+ struct cx18_av_state *state = &cx->av_state;
+- struct v4l2_format *fmt;
+ struct v4l2_sliced_vbi_format *svbi;
++ static const u16 lcr2vbi[] = {
++ 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
++ 0, V4L2_SLICED_WSS_625, 0, /* 4 */
++ V4L2_SLICED_CAPTION_525, /* 6 */
++ 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */
++ 0, 0, 0, 0
++ };
++ int is_pal = !(state->std & V4L2_STD_525_60);
++ int i;
+
+- switch (cmd) {
+- case VIDIOC_G_FMT:
+- {
+- static u16 lcr2vbi[] = {
+- 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
+- 0, V4L2_SLICED_WSS_625, 0, /* 4 */
+- V4L2_SLICED_CAPTION_525, /* 6 */
+- 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */
+- 0, 0, 0, 0
+- };
+- int is_pal = !(state->std & V4L2_STD_525_60);
+- int i;
+-
+- fmt = arg;
+- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+- return -EINVAL;
+- svbi = &fmt->fmt.sliced;
+- memset(svbi, 0, sizeof(*svbi));
+- /* we're done if raw VBI is active */
+- if ((cx18_av_read(cx, 0x404) & 0x10) == 0)
+- break;
+-
+- if (is_pal) {
+- for (i = 7; i <= 23; i++) {
+- u8 v = cx18_av_read(cx, 0x424 + i - 7);
+-
+- svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+- svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+- svbi->service_set |= svbi->service_lines[0][i] |
+- svbi->service_lines[1][i];
+- }
+- } else {
+- for (i = 10; i <= 21; i++) {
+- u8 v = cx18_av_read(cx, 0x424 + i - 10);
+-
+- svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+- svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+- svbi->service_set |= svbi->service_lines[0][i] |
+- svbi->service_lines[1][i];
+- }
+- }
+- break;
+- }
++ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
++ return -EINVAL;
++ svbi = &fmt->fmt.sliced;
++ memset(svbi, 0, sizeof(*svbi));
++ /* we're done if raw VBI is active */
++ if ((cx18_av_read(cx, 0x404) & 0x10) == 0)
++ return 0;
+
+- case VIDIOC_S_FMT:
+- {
+- int is_pal = !(state->std & V4L2_STD_525_60);
+- int vbi_offset = is_pal ? 1 : 0;
+- int i, x;
+- u8 lcr[24];
+-
+- fmt = arg;
+- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE &&
+- fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE)
+- return -EINVAL;
+- svbi = &fmt->fmt.sliced;
+- if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+- /* raw VBI */
+- memset(svbi, 0, sizeof(*svbi));
+-
+- /* Setup standard */
+- cx18_av_std_setup(cx);
+-
+- /* VBI Offset */
+- cx18_av_write(cx, 0x47f, vbi_offset);
+- cx18_av_write(cx, 0x404, 0x2e);
+- break;
++ if (is_pal) {
++ for (i = 7; i <= 23; i++) {
++ u8 v = cx18_av_read(cx, 0x424 + i - 7);
++
++ svbi->service_lines[0][i] = lcr2vbi[v >> 4];
++ svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
++ svbi->service_set |= svbi->service_lines[0][i] |
++ svbi->service_lines[1][i];
++ }
++ } else {
++ for (i = 10; i <= 21; i++) {
++ u8 v = cx18_av_read(cx, 0x424 + i - 10);
++
++ svbi->service_lines[0][i] = lcr2vbi[v >> 4];
++ svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
++ svbi->service_set |= svbi->service_lines[0][i] |
++ svbi->service_lines[1][i];
+ }
++ }
++ return 0;
++}
+
+- for (x = 0; x <= 23; x++)
+- lcr[x] = 0x00;
++int cx18_av_vbi_s_fmt(struct cx18 *cx, struct v4l2_format *fmt)
++{
++ struct cx18_av_state *state = &cx->av_state;
++ struct v4l2_sliced_vbi_format *svbi;
++ int is_pal = !(state->std & V4L2_STD_525_60);
++ int i, x;
++ u8 lcr[24];
++
++ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE &&
++ fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE)
++ return -EINVAL;
++ svbi = &fmt->fmt.sliced;
++ if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
++ /* raw VBI */
++ memset(svbi, 0, sizeof(*svbi));
+
+ /* Setup standard */
+ cx18_av_std_setup(cx);
+
+- /* Sliced VBI */
+- cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */
+- cx18_av_write(cx, 0x406, 0x13);
+- cx18_av_write(cx, 0x47f, vbi_offset);
+-
+- if (is_pal) {
+- for (i = 0; i <= 6; i++)
+- svbi->service_lines[0][i] =
+- svbi->service_lines[1][i] = 0;
+- } else {
+- for (i = 0; i <= 9; i++)
+- svbi->service_lines[0][i] =
+- svbi->service_lines[1][i] = 0;
+-
+- for (i = 22; i <= 23; i++)
+- svbi->service_lines[0][i] =
+- svbi->service_lines[1][i] = 0;
+- }
++ /* VBI Offset */
++ cx18_av_write(cx, 0x47f, state->slicer_line_delay);
++ cx18_av_write(cx, 0x404, 0x2e);
++ return 0;
++ }
+
+- for (i = 7; i <= 23; i++) {
+- for (x = 0; x <= 1; x++) {
+- switch (svbi->service_lines[1-x][i]) {
+- case V4L2_SLICED_TELETEXT_B:
+- lcr[i] |= 1 << (4 * x);
+- break;
+- case V4L2_SLICED_WSS_625:
+- lcr[i] |= 4 << (4 * x);
+- break;
+- case V4L2_SLICED_CAPTION_525:
+- lcr[i] |= 6 << (4 * x);
+- break;
+- case V4L2_SLICED_VPS:
+- lcr[i] |= 9 << (4 * x);
+- break;
+- }
+- }
+- }
++ for (x = 0; x <= 23; x++)
++ lcr[x] = 0x00;
++
++ /* Setup standard */
++ cx18_av_std_setup(cx);
++
++ /* Sliced VBI */
++ cx18_av_write(cx, 0x404, 0x32); /* Ancillary data */
++ cx18_av_write(cx, 0x406, 0x13);
++ cx18_av_write(cx, 0x47f, state->slicer_line_delay);
++
++ /* Force impossible lines to 0 */
++ if (is_pal) {
++ for (i = 0; i <= 6; i++)
++ svbi->service_lines[0][i] =
++ svbi->service_lines[1][i] = 0;
++ } else {
++ for (i = 0; i <= 9; i++)
++ svbi->service_lines[0][i] =
++ svbi->service_lines[1][i] = 0;
++
++ for (i = 22; i <= 23; i++)
++ svbi->service_lines[0][i] =
++ svbi->service_lines[1][i] = 0;
++ }
+
+- if (is_pal) {
+- for (x = 1, i = 0x424; i <= 0x434; i++, x++)
+- cx18_av_write(cx, i, lcr[6 + x]);
+- } else {
+- for (x = 1, i = 0x424; i <= 0x430; i++, x++)
+- cx18_av_write(cx, i, lcr[9 + x]);
+- for (i = 0x431; i <= 0x434; i++)
+- cx18_av_write(cx, i, 0);
++ /* Build register values for requested service lines */
++ for (i = 7; i <= 23; i++) {
++ for (x = 0; x <= 1; x++) {
++ switch (svbi->service_lines[1-x][i]) {
++ case V4L2_SLICED_TELETEXT_B:
++ lcr[i] |= 1 << (4 * x);
++ break;
++ case V4L2_SLICED_WSS_625:
++ lcr[i] |= 4 << (4 * x);
++ break;
++ case V4L2_SLICED_CAPTION_525:
++ lcr[i] |= 6 << (4 * x);
++ break;
++ case V4L2_SLICED_VPS:
++ lcr[i] |= 9 << (4 * x);
++ break;
++ }
+ }
++ }
+
+- cx18_av_write(cx, 0x43c, 0x16);
+- cx18_av_write(cx, 0x474, is_pal ? 0x2a : 0x22);
+- break;
++ if (is_pal) {
++ for (x = 1, i = 0x424; i <= 0x434; i++, x++)
++ cx18_av_write(cx, i, lcr[6 + x]);
++ } else {
++ for (x = 1, i = 0x424; i <= 0x430; i++, x++)
++ cx18_av_write(cx, i, lcr[9 + x]);
++ for (i = 0x431; i <= 0x434; i++)
++ cx18_av_write(cx, i, 0);
+ }
+
+- case VIDIOC_INT_DECODE_VBI_LINE:
+- {
+- struct v4l2_decode_vbi_line *vbi = arg;
+- u8 *p = vbi->p;
+- int id1, id2, l, err = 0;
++ cx18_av_write(cx, 0x43c, 0x16);
++ /* FIXME - should match vblank set in cx18_av_std_setup() */
++ cx18_av_write(cx, 0x474, is_pal ? 0x2a : 26);
++ return 0;
++}
++
++int cx18_av_decode_vbi_line(struct v4l2_subdev *sd,
++ struct v4l2_decode_vbi_line *vbi)
++{
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++ struct cx18_av_state *state = &cx->av_state;
++ struct vbi_anc_data *anc = (struct vbi_anc_data *)vbi->p;
++ u8 *p;
++ int did, sdid, l, err = 0;
++
++ /*
++ * Check for the ancillary data header for sliced VBI
++ */
++ if (anc->preamble[0] ||
++ anc->preamble[1] != 0xff || anc->preamble[2] != 0xff ||
++ (anc->did != sliced_vbi_did[0] &&
++ anc->did != sliced_vbi_did[1])) {
++ vbi->line = vbi->type = 0;
++ return 0;
++ }
+
+- if (p[0] || p[1] != 0xff || p[2] != 0xff ||
+- (p[3] != 0x55 && p[3] != 0x91)) {
+- vbi->line = vbi->type = 0;
+- break;
+- }
++ did = anc->did;
++ sdid = anc->sdid & 0xf;
++ l = anc->idid[0] & 0x3f;
++ l += state->slicer_line_offset;
++ p = anc->payload;
+
+- p += 4;
+- id1 = p[-1];
+- id2 = p[0] & 0xf;
+- l = p[2] & 0x3f;
+- l += state->vbi_line_offset;
+- p += 4;
+-
+- switch (id2) {
+- case 1:
+- id2 = V4L2_SLICED_TELETEXT_B;
+- break;
+- case 4:
+- id2 = V4L2_SLICED_WSS_625;
+- break;
+- case 6:
+- id2 = V4L2_SLICED_CAPTION_525;
+- err = !odd_parity(p[0]) || !odd_parity(p[1]);
+- break;
+- case 9:
+- id2 = V4L2_SLICED_VPS;
+- if (decode_vps(p, p) != 0)
+- err = 1;
+- break;
+- default:
+- id2 = 0;
++ /* Decode the SDID set by the slicer */
++ switch (sdid) {
++ case 1:
++ sdid = V4L2_SLICED_TELETEXT_B;
++ break;
++ case 4:
++ sdid = V4L2_SLICED_WSS_625;
++ break;
++ case 6:
++ sdid = V4L2_SLICED_CAPTION_525;
++ err = !odd_parity(p[0]) || !odd_parity(p[1]);
++ break;
++ case 9:
++ sdid = V4L2_SLICED_VPS;
++ if (decode_vps(p, p) != 0)
+ err = 1;
+- break;
+- }
+-
+- vbi->type = err ? 0 : id2;
+- vbi->line = err ? 0 : l;
+- vbi->is_second_field = err ? 0 : (id1 == 0x55);
+- vbi->p = p;
+ break;
+- }
++ default:
++ sdid = 0;
++ err = 1;
++ break;
+ }
+
++ vbi->type = err ? 0 : sdid;
++ vbi->line = err ? 0 : l;
++ vbi->is_second_field = err ? 0 : (did == sliced_vbi_did[1]);
++ vbi->p = p;
+ return 0;
+ }
+diff --git a/drivers/media/video/cx18/cx18-cards.c b/drivers/media/video/cx18/cx18-cards.c
+index e274043..9bc2218 100644
+--- a/drivers/media/video/cx18/cx18-cards.c
++++ b/drivers/media/video/cx18/cx18-cards.c
+@@ -51,12 +51,12 @@ static struct cx18_card_tuner_i2c cx18_i2c_std = {
+ static const struct cx18_card cx18_card_hvr1600_esmt = {
+ .type = CX18_CARD_HVR_1600_ESMT,
+ .name = "Hauppauge HVR-1600",
+- .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
++ .comment = "Simultaneous Digital and Analog TV capture supported\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+- .hw_audio_ctrl = CX18_HW_CX23418,
++ .hw_audio_ctrl = CX18_HW_418_AV,
+ .hw_muxer = CX18_HW_CS5345,
+- .hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER |
+- CX18_HW_CS5345 | CX18_HW_DVB,
++ .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
++ CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 },
+ { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 },
+@@ -97,12 +97,12 @@ static const struct cx18_card cx18_card_hvr1600_esmt = {
+ static const struct cx18_card cx18_card_hvr1600_samsung = {
+ .type = CX18_CARD_HVR_1600_SAMSUNG,
+ .name = "Hauppauge HVR-1600 (Preproduction)",
+- .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
++ .comment = "Simultaneous Digital and Analog TV capture supported\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+- .hw_audio_ctrl = CX18_HW_CX23418,
++ .hw_audio_ctrl = CX18_HW_418_AV,
+ .hw_muxer = CX18_HW_CS5345,
+- .hw_all = CX18_HW_TVEEPROM | CX18_HW_TUNER |
+- CX18_HW_CS5345 | CX18_HW_DVB,
++ .hw_all = CX18_HW_TVEEPROM | CX18_HW_418_AV | CX18_HW_TUNER |
++ CX18_HW_CS5345 | CX18_HW_DVB | CX18_HW_GPIO_RESET_CTRL,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE7 },
+ { CX18_CARD_INPUT_SVIDEO1, 1, CX18_AV_SVIDEO1 },
+@@ -152,10 +152,10 @@ static const struct cx18_card_pci_info cx18_pci_h900[] = {
+ static const struct cx18_card cx18_card_h900 = {
+ .type = CX18_CARD_COMPRO_H900,
+ .name = "Compro VideoMate H900",
+- .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
++ .comment = "Analog TV capture supported\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+- .hw_audio_ctrl = CX18_HW_CX23418,
+- .hw_all = CX18_HW_TUNER,
++ .hw_audio_ctrl = CX18_HW_418_AV,
++ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
+ { CX18_CARD_INPUT_SVIDEO1, 1,
+@@ -201,8 +201,8 @@ static const struct cx18_card cx18_card_mpc718 = {
+ .name = "Yuan MPC718",
+ .comment = "Analog video capture works; some audio line in may not.\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+- .hw_audio_ctrl = CX18_HW_CX23418,
+- .hw_all = CX18_HW_TUNER,
++ .hw_audio_ctrl = CX18_HW_418_AV,
++ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
+ { CX18_CARD_INPUT_SVIDEO1, 1,
+@@ -249,11 +249,11 @@ static const struct cx18_card_pci_info cx18_pci_cnxt_raptor_pal[] = {
+ static const struct cx18_card cx18_card_cnxt_raptor_pal = {
+ .type = CX18_CARD_CNXT_RAPTOR_PAL,
+ .name = "Conexant Raptor PAL/SECAM",
+- .comment = "Raw VBI supported; Sliced VBI is not yet supported\n",
++ .comment = "Analog TV capture supported\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+- .hw_audio_ctrl = CX18_HW_CX23418,
+- .hw_muxer = CX18_HW_GPIO,
+- .hw_all = CX18_HW_TUNER | CX18_HW_GPIO,
++ .hw_audio_ctrl = CX18_HW_418_AV,
++ .hw_muxer = CX18_HW_GPIO_MUX,
++ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
+ { CX18_CARD_INPUT_SVIDEO1, 1,
+@@ -306,8 +306,8 @@ static const struct cx18_card cx18_card_toshiba_qosmio_dvbt = {
+ .comment = "Experimenters and photos needed for device to work well.\n"
+ "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+- .hw_audio_ctrl = CX18_HW_CX23418,
+- .hw_all = CX18_HW_TUNER,
++ .hw_audio_ctrl = CX18_HW_418_AV,
++ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_RESET_CTRL,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE6 },
+ { CX18_CARD_INPUT_SVIDEO1, 1,
+@@ -339,19 +339,21 @@ static const struct cx18_card cx18_card_toshiba_qosmio_dvbt = {
+ /* Leadtek WinFast PVR2100 */
+
+ static const struct cx18_card_pci_info cx18_pci_leadtek_pvr2100[] = {
+- { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6f27 },
++ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6f27 }, /* PVR2100 */
++ { PCI_DEVICE_ID_CX23418, CX18_PCI_ID_LEADTEK, 0x6690 }, /* DVR3100 H */
+ { 0, 0, 0 }
+ };
+
+ static const struct cx18_card cx18_card_leadtek_pvr2100 = {
+ .type = CX18_CARD_LEADTEK_PVR2100,
+- .name = "Leadtek WinFast PVR2100",
++ .name = "Leadtek WinFast PVR2100/DVR3100 H",
+ .comment = "Experimenters and photos needed for device to work well.\n"
+ "\tTo help, mail the ivtv-devel list (www.ivtvdriver.org).\n",
+ .v4l2_capabilities = CX18_CAP_ENCODER,
+- .hw_audio_ctrl = CX18_HW_CX23418,
+- .hw_muxer = CX18_HW_GPIO,
+- .hw_all = CX18_HW_TUNER | CX18_HW_GPIO,
++ .hw_audio_ctrl = CX18_HW_418_AV,
++ .hw_muxer = CX18_HW_GPIO_MUX,
++ .hw_all = CX18_HW_418_AV | CX18_HW_TUNER | CX18_HW_GPIO_MUX |
++ CX18_HW_GPIO_RESET_CTRL,
+ .video_inputs = {
+ { CX18_CARD_INPUT_VID_TUNER, 0, CX18_AV_COMPOSITE2 },
+ { CX18_CARD_INPUT_SVIDEO1, 1,
+diff --git a/drivers/media/video/cx18/cx18-cards.h b/drivers/media/video/cx18/cx18-cards.h
+index 6fa7bcb..3c552b6 100644
+--- a/drivers/media/video/cx18/cx18-cards.h
++++ b/drivers/media/video/cx18/cx18-cards.h
+@@ -22,12 +22,13 @@
+ */
+
+ /* hardware flags */
+-#define CX18_HW_TUNER (1 << 0)
+-#define CX18_HW_TVEEPROM (1 << 1)
+-#define CX18_HW_CS5345 (1 << 2)
+-#define CX18_HW_GPIO (1 << 3)
+-#define CX18_HW_CX23418 (1 << 4)
+-#define CX18_HW_DVB (1 << 5)
++#define CX18_HW_TUNER (1 << 0)
++#define CX18_HW_TVEEPROM (1 << 1)
++#define CX18_HW_CS5345 (1 << 2)
++#define CX18_HW_DVB (1 << 3)
++#define CX18_HW_418_AV (1 << 4)
++#define CX18_HW_GPIO_MUX (1 << 5)
++#define CX18_HW_GPIO_RESET_CTRL (1 << 6)
+
+ /* video inputs */
+ #define CX18_CARD_INPUT_VID_TUNER 1
+@@ -49,8 +50,7 @@
+ /* V4L2 capability aliases */
+ #define CX18_CAP_ENCODER (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_TUNER | \
+ V4L2_CAP_AUDIO | V4L2_CAP_READWRITE | \
+- V4L2_CAP_VBI_CAPTURE)
+-/* | V4L2_CAP_SLICED_VBI_CAPTURE) not yet */
++ V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_CAPTURE)
+
+ struct cx18_card_video_input {
+ u8 video_type; /* video input type */
+@@ -122,7 +122,7 @@ struct cx18_card {
+ char *comment;
+ u32 v4l2_capabilities;
+ u32 hw_audio_ctrl; /* hardware used for the V4L2 controls (only
+- 1 dev allowed) */
++ 1 dev allowed currently) */
+ u32 hw_muxer; /* hardware used to multiplex audio input */
+ u32 hw_all; /* all hardware used by the board */
+ struct cx18_card_video_input video_inputs[CX18_CARD_MAX_VIDEO_INPUTS];
+diff --git a/drivers/media/video/cx18/cx18-controls.c b/drivers/media/video/cx18/cx18-controls.c
+index 17edf30..82fc2f9 100644
+--- a/drivers/media/video/cx18/cx18-controls.c
++++ b/drivers/media/video/cx18/cx18-controls.c
+@@ -22,14 +22,13 @@
+ */
+
+ #include "cx18-driver.h"
+-#include "cx18-av-core.h"
+ #include "cx18-cards.h"
+ #include "cx18-ioctl.h"
+ #include "cx18-audio.h"
+-#include "cx18-i2c.h"
+ #include "cx18-mailbox.h"
+ #include "cx18-controls.h"
+
++/* Must be sorted from low to high control ID! */
+ static const u32 user_ctrls[] = {
+ V4L2_CID_USER_CLASS,
+ V4L2_CID_BRIGHTNESS,
+@@ -66,7 +65,7 @@ int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
+ case V4L2_CID_HUE:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_CONTRAST:
+- if (cx18_av_cmd(cx, VIDIOC_QUERYCTRL, qctrl))
++ if (v4l2_subdev_call(cx->sd_av, core, queryctrl, qctrl))
+ qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+ return 0;
+
+@@ -76,7 +75,7 @@ int cx18_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qctrl)
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_AUDIO_LOUDNESS:
+- if (cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_QUERYCTRL, qctrl))
++ if (v4l2_subdev_call(cx->sd_av, core, queryctrl, qctrl))
+ qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
+ return 0;
+
+@@ -125,7 +124,7 @@ static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
+ case V4L2_CID_HUE:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_CONTRAST:
+- return cx18_av_cmd(cx, VIDIOC_S_CTRL, vctrl);
++ return v4l2_subdev_call(cx->sd_av, core, s_ctrl, vctrl);
+
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_MUTE:
+@@ -133,7 +132,7 @@ static int cx18_s_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_AUDIO_LOUDNESS:
+- return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_S_CTRL, vctrl);
++ return v4l2_subdev_call(cx->sd_av, core, s_ctrl, vctrl);
+
+ default:
+ CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
+@@ -150,7 +149,7 @@ static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
+ case V4L2_CID_HUE:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_CONTRAST:
+- return cx18_av_cmd(cx, VIDIOC_G_CTRL, vctrl);
++ return v4l2_subdev_call(cx->sd_av, core, g_ctrl, vctrl);
+
+ case V4L2_CID_AUDIO_VOLUME:
+ case V4L2_CID_AUDIO_MUTE:
+@@ -158,7 +157,8 @@ static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+ case V4L2_CID_AUDIO_LOUDNESS:
+- return cx18_i2c_hw(cx, cx->card->hw_audio_ctrl, VIDIOC_G_CTRL, vctrl);
++ return v4l2_subdev_call(cx->sd_av, core, g_ctrl, vctrl);
++
+ default:
+ CX18_DEBUG_IOCTL("invalid control 0x%x\n", vctrl->id);
+ return -EINVAL;
+@@ -166,38 +166,57 @@ static int cx18_g_ctrl(struct cx18 *cx, struct v4l2_control *vctrl)
+ return 0;
+ }
+
+-static int cx18_setup_vbi_fmt(struct cx18 *cx, enum v4l2_mpeg_stream_vbi_fmt fmt)
++static int cx18_setup_vbi_fmt(struct cx18 *cx,
++ enum v4l2_mpeg_stream_vbi_fmt fmt,
++ enum v4l2_mpeg_stream_type type)
+ {
+ if (!(cx->v4l2_cap & V4L2_CAP_SLICED_VBI_CAPTURE))
+ return -EINVAL;
+ if (atomic_read(&cx->ana_capturing) > 0)
+ return -EBUSY;
+
+- /* First try to allocate sliced VBI buffers if needed. */
+- if (fmt && cx->vbi.sliced_mpeg_data[0] == NULL) {
++ if (fmt != V4L2_MPEG_STREAM_VBI_FMT_IVTV ||
++ type != V4L2_MPEG_STREAM_TYPE_MPEG2_PS) {
++ /* We don't do VBI insertion aside from IVTV format in a PS */
++ cx->vbi.insert_mpeg = V4L2_MPEG_STREAM_VBI_FMT_NONE;
++ CX18_DEBUG_INFO("disabled insertion of sliced VBI data into "
++ "the MPEG stream\n");
++ return 0;
++ }
++
++ /* Allocate sliced VBI buffers if needed. */
++ if (cx->vbi.sliced_mpeg_data[0] == NULL) {
+ int i;
+
+ for (i = 0; i < CX18_VBI_FRAMES; i++) {
+- /* Yuck, hardcoded. Needs to be a define */
+- cx->vbi.sliced_mpeg_data[i] = kmalloc(2049, GFP_KERNEL);
++ cx->vbi.sliced_mpeg_data[i] =
++ kmalloc(CX18_SLICED_MPEG_DATA_BUFSZ, GFP_KERNEL);
+ if (cx->vbi.sliced_mpeg_data[i] == NULL) {
+ while (--i >= 0) {
+ kfree(cx->vbi.sliced_mpeg_data[i]);
+ cx->vbi.sliced_mpeg_data[i] = NULL;
+ }
++ cx->vbi.insert_mpeg =
++ V4L2_MPEG_STREAM_VBI_FMT_NONE;
++ CX18_WARN("Unable to allocate buffers for "
++ "sliced VBI data insertion\n");
+ return -ENOMEM;
+ }
+ }
+ }
+
+ cx->vbi.insert_mpeg = fmt;
++ CX18_DEBUG_INFO("enabled insertion of sliced VBI data into the MPEG PS,"
++ "when sliced VBI is enabled\n");
+
+- if (cx->vbi.insert_mpeg == 0)
+- return 0;
+- /* Need sliced data for mpeg insertion */
++ /*
++ * If our current settings have no lines set for capture, store a valid,
++ * default set of service lines to capture, in our current settings.
++ */
+ if (cx18_get_service_set(cx->vbi.sliced_in) == 0) {
+ if (cx->is_60hz)
+- cx->vbi.sliced_in->service_set = V4L2_SLICED_CAPTION_525;
++ cx->vbi.sliced_in->service_set =
++ V4L2_SLICED_CAPTION_525;
+ else
+ cx->vbi.sliced_in->service_set = V4L2_SLICED_WSS_625;
+ cx18_expand_service_set(cx->vbi.sliced_in, cx->is_50hz);
+@@ -259,10 +278,12 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
+ return err;
+ }
+ if (c->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
++ static u32 freqs[3] = { 44100, 48000, 32000 };
+ struct cx18_api_func_private priv;
+ struct cx2341x_mpeg_params p = cx->params;
+ int err = cx2341x_ext_ctrls(&p, atomic_read(&cx->ana_capturing),
+ c, VIDIOC_S_EXT_CTRLS);
++ unsigned int idx;
+
+ if (err)
+ return err;
+@@ -277,16 +298,23 @@ int cx18_s_ext_ctrls(struct file *file, void *fh, struct v4l2_ext_controls *c)
+ fmt.fmt.pix.width = cx->params.width
+ / (is_mpeg1 ? 2 : 1);
+ fmt.fmt.pix.height = cx->params.height;
+- cx18_av_cmd(cx, VIDIOC_S_FMT, &fmt);
++ v4l2_subdev_call(cx->sd_av, video, s_fmt, &fmt);
+ }
+ priv.cx = cx;
+ priv.s = &cx->streams[id->type];
+ err = cx2341x_update(&priv, cx18_api_func, &cx->params, &p);
+- if (!err && cx->params.stream_vbi_fmt != p.stream_vbi_fmt)
+- err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt);
++ if (!err &&
++ (cx->params.stream_vbi_fmt != p.stream_vbi_fmt ||
++ cx->params.stream_type != p.stream_type))
++ err = cx18_setup_vbi_fmt(cx, p.stream_vbi_fmt,
++ p.stream_type);
+ cx->params = p;
+ cx->dualwatch_stereo_mode = p.audio_properties & 0x0300;
+- cx18_audio_set_audio_clock_freq(cx, p.audio_properties & 0x03);
++ idx = p.audio_properties & 0x03;
++ /* The audio clock of the digitizer must match the codec sample
++ rate otherwise you get some very strange effects. */
++ if (idx < sizeof(freqs))
++ cx18_call_all(cx, audio, s_clock_freq, freqs[idx]);
+ return err;
+ }
+ return -EINVAL;
+diff --git a/drivers/media/video/cx18/cx18-driver.c b/drivers/media/video/cx18/cx18-driver.c
+index f50cf21..210c68a 100644
+--- a/drivers/media/video/cx18/cx18-driver.c
++++ b/drivers/media/video/cx18/cx18-driver.c
+@@ -39,10 +39,6 @@
+
+ #include <media/tveeprom.h>
+
+-
+-/* var to keep track of the number of array elements in use */
+-int cx18_cards_active;
+-
+ /* If you have already X v4l cards, then set this to X. This way
+ the device numbers stay matched. Example: you have a WinTV card
+ without radio and a Compro H900 with. Normally this would give a
+@@ -50,12 +46,6 @@ int cx18_cards_active;
+ setting this to 1 you ensure that radio0 is now also radio1. */
+ int cx18_first_minor;
+
+-/* Master variable for all cx18 info */
+-struct cx18 *cx18_cards[CX18_MAX_CARDS];
+-
+-/* Protects cx18_cards_active */
+-DEFINE_SPINLOCK(cx18_cards_lock);
+-
+ /* add your revision and whatnot here */
+ static struct pci_device_id cx18_pci_tbl[] __devinitdata = {
+ {PCI_VENDOR_ID_CX, PCI_DEVICE_ID_CX23418,
+@@ -65,6 +55,8 @@ static struct pci_device_id cx18_pci_tbl[] __devinitdata = {
+
+ MODULE_DEVICE_TABLE(pci, cx18_pci_tbl);
+
++static atomic_t cx18_instance = ATOMIC_INIT(0);
++
+ /* Parameter declarations */
+ static int cardtype[CX18_MAX_CARDS];
+ static int tuner[CX18_MAX_CARDS] = { -1, -1, -1, -1, -1, -1, -1, -1,
+@@ -159,7 +151,7 @@ MODULE_PARM_DESC(cardtype,
+ "\t\t\t 4 = Yuan MPC718\n"
+ "\t\t\t 5 = Conexant Raptor PAL/SECAM\n"
+ "\t\t\t 6 = Toshiba Qosmio DVB-T/Analog\n"
+- "\t\t\t 7 = Leadtek WinFast PVR2100\n"
++ "\t\t\t 7 = Leadtek WinFast PVR2100/DVR3100 H\n"
+ "\t\t\t 0 = Autodetect (default)\n"
+ "\t\t\t-1 = Ignore this card\n\t\t");
+ MODULE_PARM_DESC(pal, "Set PAL standard: B, G, H, D, K, I, M, N, Nc, 60");
+@@ -277,11 +269,16 @@ static void cx18_iounmap(struct cx18 *cx)
+ /* Hauppauge card? get values from tveeprom */
+ void cx18_read_eeprom(struct cx18 *cx, struct tveeprom *tv)
+ {
++ struct i2c_client c;
+ u8 eedata[256];
+
+- cx->i2c_client[0].addr = 0xA0 >> 1;
+- tveeprom_read(&cx->i2c_client[0], eedata, sizeof(eedata));
+- tveeprom_hauppauge_analog(&cx->i2c_client[0], tv, eedata);
++ memset(&c, 0, sizeof(c));
++ strlcpy(c.name, "cx18 tveeprom tmp", sizeof(c.name));
++ c.adapter = &cx->i2c_adap[0];
++ c.addr = 0xA0 >> 1;
++
++ tveeprom_read(&c, eedata, sizeof(eedata));
++ tveeprom_hauppauge_analog(&c, tv, eedata);
+ }
+
+ static void cx18_process_eeprom(struct cx18 *cx)
+@@ -448,34 +445,38 @@ static void cx18_process_options(struct cx18 *cx)
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_MPG] = enc_mpg_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_IDX] = enc_idx_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_YUV] = enc_yuv_bufsize;
+- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = 0; /* computed later */
++ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_active_samples * 36;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_PCM] = enc_pcm_bufsize;
+ cx->stream_buf_size[CX18_ENC_STREAM_TYPE_RAD] = 0; /* control no data */
+
+- /* Except for VBI ensure stream_buffers & stream_buf_size are valid */
++ /* Ensure stream_buffers & stream_buf_size are valid */
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+- /* User said to use 0 buffers */
+- if (cx->stream_buffers[i] == 0) {
+- cx->options.megabytes[i] = 0;
+- cx->stream_buf_size[i] = 0;
+- continue;
+- }
+- /* User said to use 0 MB total */
+- if (cx->options.megabytes[i] <= 0) {
++ if (cx->stream_buffers[i] == 0 || /* User said 0 buffers */
++ cx->options.megabytes[i] <= 0 || /* User said 0 MB total */
++ cx->stream_buf_size[i] <= 0) { /* User said buf size 0 */
+ cx->options.megabytes[i] = 0;
+ cx->stream_buffers[i] = 0;
+ cx->stream_buf_size[i] = 0;
+ continue;
+ }
+- /* VBI is computed later or user said buffer has size 0 */
+- if (cx->stream_buf_size[i] <= 0) {
+- if (i != CX18_ENC_STREAM_TYPE_VBI) {
+- cx->options.megabytes[i] = 0;
+- cx->stream_buffers[i] = 0;
+- cx->stream_buf_size[i] = 0;
++ /*
++ * VBI is a special case where the stream_buf_size is fixed
++ * and already in bytes
++ */
++ if (i == CX18_ENC_STREAM_TYPE_VBI) {
++ if (cx->stream_buffers[i] < 0) {
++ cx->stream_buffers[i] =
++ cx->options.megabytes[i] * 1024 * 1024
++ / cx->stream_buf_size[i];
++ } else {
++ /* N.B. This might round down to 0 */
++ cx->options.megabytes[i] =
++ cx->stream_buffers[i]
++ * cx->stream_buf_size[i]/(1024 * 1024);
+ }
+ continue;
+ }
++ /* All other streams have stream_buf_size in kB at this point */
+ if (cx->stream_buffers[i] < 0) {
+ cx->stream_buffers[i] = cx->options.megabytes[i] * 1024
+ / cx->stream_buf_size[i];
+@@ -487,9 +488,9 @@ static void cx18_process_options(struct cx18 *cx)
+ cx->stream_buf_size[i] *= 1024; /* convert from kB to bytes */
+ }
+
+- cx->options.cardtype = cardtype[cx->num];
+- cx->options.tuner = tuner[cx->num];
+- cx->options.radio = radio[cx->num];
++ cx->options.cardtype = cardtype[cx->instance];
++ cx->options.tuner = tuner[cx->instance];
++ cx->options.radio = radio[cx->instance];
+
+ cx->std = cx18_parse_std(cx);
+ if (cx->options.cardtype == -1) {
+@@ -502,7 +503,7 @@ static void cx18_process_options(struct cx18 *cx)
+ else if (cx->options.cardtype != 0)
+ CX18_ERR("Unknown user specified type, trying to autodetect card\n");
+ if (cx->card == NULL) {
+- if (cx->dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) {
++ if (cx->pci_dev->subsystem_vendor == CX18_PCI_ID_HAUPPAUGE) {
+ cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+ CX18_INFO("Autodetected Hauppauge card\n");
+ }
+@@ -512,13 +513,13 @@ static void cx18_process_options(struct cx18 *cx)
+ if (cx->card->pci_list == NULL)
+ continue;
+ for (j = 0; cx->card->pci_list[j].device; j++) {
+- if (cx->dev->device !=
++ if (cx->pci_dev->device !=
+ cx->card->pci_list[j].device)
+ continue;
+- if (cx->dev->subsystem_vendor !=
++ if (cx->pci_dev->subsystem_vendor !=
+ cx->card->pci_list[j].subsystem_vendor)
+ continue;
+- if (cx->dev->subsystem_device !=
++ if (cx->pci_dev->subsystem_device !=
+ cx->card->pci_list[j].subsystem_device)
+ continue;
+ CX18_INFO("Autodetected %s card\n", cx->card->name);
+@@ -531,9 +532,10 @@ done:
+ if (cx->card == NULL) {
+ cx->card = cx18_get_card(CX18_CARD_HVR_1600_ESMT);
+ CX18_ERR("Unknown card: vendor/device: [%04x:%04x]\n",
+- cx->dev->vendor, cx->dev->device);
++ cx->pci_dev->vendor, cx->pci_dev->device);
+ CX18_ERR(" subsystem vendor/device: [%04x:%04x]\n",
+- cx->dev->subsystem_vendor, cx->dev->subsystem_device);
++ cx->pci_dev->subsystem_vendor,
++ cx->pci_dev->subsystem_device);
+ CX18_ERR("Defaulting to %s card\n", cx->card->name);
+ CX18_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
+ CX18_ERR("card you have to the ivtv-devel mailinglist (www.ivtvdriver.org)\n");
+@@ -545,7 +547,7 @@ done:
+ }
+
+ /* Precondition: the cx18 structure has been memset to 0. Only
+- the dev and num fields have been filled in.
++ the dev and instance fields have been filled in.
+ No assumptions on the card type may be made here (see cx18_init_struct2
+ for that).
+ */
+@@ -553,18 +555,14 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
+ {
+ int i;
+
+- cx->base_addr = pci_resource_start(cx->dev, 0);
++ cx->base_addr = pci_resource_start(cx->pci_dev, 0);
+
+ mutex_init(&cx->serialize_lock);
+- mutex_init(&cx->i2c_bus_lock[0]);
+- mutex_init(&cx->i2c_bus_lock[1]);
+ mutex_init(&cx->gpio_lock);
+ mutex_init(&cx->epu2apu_mb_lock);
+ mutex_init(&cx->epu2cpu_mb_lock);
+
+- spin_lock_init(&cx->lock);
+-
+- cx->work_queue = create_singlethread_workqueue(cx->name);
++ cx->work_queue = create_singlethread_workqueue(cx->v4l2_dev.name);
+ if (cx->work_queue == NULL) {
+ CX18_ERR("Unable to create work hander thread\n");
+ return -ENOMEM;
+@@ -587,7 +585,8 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
+ (cx->params.video_temporal_filter_mode << 1) |
+ (cx->params.video_median_filter_type << 2);
+ cx->params.port = CX2341X_PORT_MEMORY;
+- cx->params.capabilities = CX2341X_CAP_HAS_TS;
++ cx->params.capabilities =
++ CX2341X_CAP_HAS_TS | CX2341X_CAP_HAS_SLICED_VBI;
+ init_waitqueue_head(&cx->cap_w);
+ init_waitqueue_head(&cx->mb_apu_waitq);
+ init_waitqueue_head(&cx->mb_cpu_waitq);
+@@ -597,49 +596,6 @@ static int __devinit cx18_init_struct1(struct cx18 *cx)
+ cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+ cx->vbi.sliced_in = &cx->vbi.in.fmt.sliced;
+
+- /*
+- * The VBI line sizes depend on the pixel clock and the horiz rate
+- *
+- * (1/Fh)*(2*Fp) = Samples/line
+- * = 4 bytes EAV + Anc data in hblank + 4 bytes SAV + active samples
+- *
+- * Sliced VBI is sent as ancillary data during horizontal blanking
+- * Raw VBI is sent as active video samples during vertcal blanking
+- *
+- * We use a BT.656 pxiel clock of 13.5 MHz and a BT.656 active line
+- * length of 720 pixels @ 4:2:2 sampling. Thus...
+- *
+- * For systems that use a 15.734 kHz horizontal rate, such as
+- * NTSC-M, PAL-M, PAL-60, and other 60 Hz/525 line systems, we have:
+- *
+- * (1/15.734 kHz) * 2 * 13.5 MHz = 1716 samples/line =
+- * 4 bytes SAV + 268 bytes anc data + 4 bytes SAV + 1440 active samples
+- *
+- * For systems that use a 15.625 kHz horizontal rate, such as
+- * PAL-B/G/H, PAL-I, SECAM-L and other 50 Hz/625 line systems, we have:
+- *
+- * (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line =
+- * 4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples
+- *
+- */
+-
+- /* FIXME: init these based on tuner std & modify when std changes */
+- /* CX18-AV-Core number of VBI samples output per horizontal line */
+- cx->vbi.raw_decoder_line_size = 1444; /* 4 byte SAV + 2 * 720 */
+- cx->vbi.sliced_decoder_line_size = 272; /* 60 Hz: 268+4, 50 Hz: 280+4 */
+-
+- /* CX18-AV-Core VBI samples/line possibly rounded up */
+- cx->vbi.raw_size = 1444; /* Real max size is 1444 */
+- cx->vbi.sliced_size = 284; /* Real max size is 284 */
+-
+- /*
+- * CX18-AV-Core SAV/EAV RP codes in VIP 1.x mode
+- * Task Field VerticalBlank HorizontalBlank 0 0 0 0
+- */
+- cx->vbi.raw_decoder_sav_odd_field = 0x20; /* V */
+- cx->vbi.raw_decoder_sav_even_field = 0x60; /* FV */
+- cx->vbi.sliced_decoder_sav_odd_field = 0xB0; /* T VH - actually EAV */
+- cx->vbi.sliced_decoder_sav_even_field = 0xF0; /* TFVH - actually EAV */
+ return 0;
+ }
+
+@@ -668,15 +624,9 @@ static void __devinit cx18_init_struct2(struct cx18 *cx)
+ i = 0;
+ cx->active_input = i;
+ cx->audio_input = cx->card->video_inputs[i].audio_index;
+- cx->av_state.vid_input = CX18_AV_COMPOSITE7;
+- cx->av_state.aud_input = CX18_AV_AUDIO8;
+- cx->av_state.audclk_freq = 48000;
+- cx->av_state.audmode = V4L2_TUNER_MODE_LANG1;
+- /* FIXME - 8 is NTSC value, investigate */
+- cx->av_state.vbi_line_offset = 8;
+ }
+
+-static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *dev,
++static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+ {
+ u16 cmd;
+@@ -684,124 +634,125 @@ static int cx18_setup_pci(struct cx18 *cx, struct pci_dev *dev,
+
+ CX18_DEBUG_INFO("Enabling pci device\n");
+
+- if (pci_enable_device(dev)) {
+- CX18_ERR("Can't enable device %d!\n", cx->num);
++ if (pci_enable_device(pci_dev)) {
++ CX18_ERR("Can't enable device %d!\n", cx->instance);
+ return -EIO;
+ }
+- if (pci_set_dma_mask(dev, 0xffffffff)) {
+- CX18_ERR("No suitable DMA available on card %d.\n", cx->num);
++ if (pci_set_dma_mask(pci_dev, 0xffffffff)) {
++ CX18_ERR("No suitable DMA available, card %d\n", cx->instance);
+ return -EIO;
+ }
+ if (!request_mem_region(cx->base_addr, CX18_MEM_SIZE, "cx18 encoder")) {
+- CX18_ERR("Cannot request encoder memory region on card %d.\n", cx->num);
++ CX18_ERR("Cannot request encoder memory region, card %d\n",
++ cx->instance);
+ return -EIO;
+ }
+
+ /* Enable bus mastering and memory mapped IO for the CX23418 */
+- pci_read_config_word(dev, PCI_COMMAND, &cmd);
++ pci_read_config_word(pci_dev, PCI_COMMAND, &cmd);
+ cmd |= PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER;
+- pci_write_config_word(dev, PCI_COMMAND, cmd);
++ pci_write_config_word(pci_dev, PCI_COMMAND, cmd);
+
+- pci_read_config_byte(dev, PCI_CLASS_REVISION, &cx->card_rev);
+- pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
++ pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &cx->card_rev);
++ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency);
+
+ if (pci_latency < 64 && cx18_pci_latency) {
+ CX18_INFO("Unreasonably low latency timer, "
+ "setting to 64 (was %d)\n", pci_latency);
+- pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
+- pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
++ pci_write_config_byte(pci_dev, PCI_LATENCY_TIMER, 64);
++ pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &pci_latency);
+ }
+
+ CX18_DEBUG_INFO("cx%d (rev %d) at %02x:%02x.%x, "
+ "irq: %d, latency: %d, memory: 0x%lx\n",
+- cx->dev->device, cx->card_rev, dev->bus->number,
+- PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
+- cx->dev->irq, pci_latency, (unsigned long)cx->base_addr);
++ cx->pci_dev->device, cx->card_rev, pci_dev->bus->number,
++ PCI_SLOT(pci_dev->devfn), PCI_FUNC(pci_dev->devfn),
++ cx->pci_dev->irq, pci_latency, (unsigned long)cx->base_addr);
+
+ return 0;
+ }
+
+-#ifdef MODULE
+-static u32 cx18_request_module(struct cx18 *cx, u32 hw,
+- const char *name, u32 id)
+-{
+- if ((hw & id) == 0)
+- return hw;
+- if (request_module(name) != 0) {
+- CX18_ERR("Failed to load module %s\n", name);
+- return hw & ~id;
+- }
+- CX18_DEBUG_INFO("Loaded module %s\n", name);
+- return hw;
+-}
+-#endif
+-
+-static void cx18_load_and_init_modules(struct cx18 *cx)
++static void cx18_init_subdevs(struct cx18 *cx)
+ {
+ u32 hw = cx->card->hw_all;
++ u32 device;
+ int i;
+
+-#ifdef MODULE
+- /* load modules */
+-#ifdef CONFIG_MEDIA_TUNER_MODULE
+- hw = cx18_request_module(cx, hw, "tuner", CX18_HW_TUNER);
+-#endif
+-#ifdef CONFIG_VIDEO_CS5345_MODULE
+- hw = cx18_request_module(cx, hw, "cs5345", CX18_HW_CS5345);
+-#endif
+-#endif
+-
+- /* check which i2c devices are actually found */
+- for (i = 0; i < 32; i++) {
+- u32 device = 1 << i;
++ for (i = 0, device = 1; i < 32; i++, device <<= 1) {
+
+ if (!(device & hw))
+ continue;
+- if (device == CX18_HW_GPIO || device == CX18_HW_TVEEPROM ||
+- device == CX18_HW_CX23418 || device == CX18_HW_DVB) {
+- /* These 'devices' do not use i2c probing */
++
++ switch (device) {
++ case CX18_HW_DVB:
++ case CX18_HW_TVEEPROM:
++ /* These subordinate devices do not use probing */
+ cx->hw_flags |= device;
+- continue;
+- }
+- cx18_i2c_register(cx, i);
+- if (cx18_i2c_hw_addr(cx, device) > 0)
++ break;
++ case CX18_HW_418_AV:
++ /* The A/V decoder gets probed earlier to set PLLs */
++ /* Just note that the card uses it (i.e. has analog) */
+ cx->hw_flags |= device;
++ break;
++ case CX18_HW_GPIO_RESET_CTRL:
++ /*
++ * The Reset Controller gets probed and added to
++ * hw_flags earlier for i2c adapter/bus initialization
++ */
++ break;
++ case CX18_HW_GPIO_MUX:
++ if (cx18_gpio_register(cx, device) == 0)
++ cx->hw_flags |= device;
++ break;
++ default:
++ if (cx18_i2c_register(cx, i) == 0)
++ cx->hw_flags |= device;
++ break;
++ }
+ }
+
+- hw = cx->hw_flags;
++ if (cx->hw_flags & CX18_HW_418_AV)
++ cx->sd_av = cx18_find_hw(cx, CX18_HW_418_AV);
++
++ if (cx->card->hw_muxer != 0)
++ cx->sd_extmux = cx18_find_hw(cx, cx->card->hw_muxer);
+ }
+
+-static int __devinit cx18_probe(struct pci_dev *dev,
++static int __devinit cx18_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *pci_id)
+ {
+ int retval = 0;
+ int i;
+- int vbi_buf_size;
+ u32 devtype;
+ struct cx18 *cx;
+
+- spin_lock(&cx18_cards_lock);
+-
+- /* Make sure we've got a place for this card */
+- if (cx18_cards_active == CX18_MAX_CARDS) {
+- printk(KERN_ERR "cx18: Maximum number of cards detected (%d).\n",
+- cx18_cards_active);
+- spin_unlock(&cx18_cards_lock);
++ /* FIXME - module parameter arrays constrain max instances */
++ i = atomic_inc_return(&cx18_instance) - 1;
++ if (i >= CX18_MAX_CARDS) {
++ printk(KERN_ERR "cx18: cannot manage card %d, driver has a "
++ "limit of 0 - %d\n", i, CX18_MAX_CARDS - 1);
+ return -ENOMEM;
+ }
+
+ cx = kzalloc(sizeof(struct cx18), GFP_ATOMIC);
+- if (!cx) {
+- spin_unlock(&cx18_cards_lock);
++ if (cx == NULL) {
++ printk(KERN_ERR "cx18: cannot manage card %d, out of memory\n",
++ i);
+ return -ENOMEM;
+ }
+- cx18_cards[cx18_cards_active] = cx;
+- cx->dev = dev;
+- cx->num = cx18_cards_active++;
+- snprintf(cx->name, sizeof(cx->name), "cx18-%d", cx->num);
+- CX18_INFO("Initializing card #%d\n", cx->num);
++ cx->pci_dev = pci_dev;
++ cx->instance = i;
+
+- spin_unlock(&cx18_cards_lock);
++ retval = v4l2_device_register(&pci_dev->dev, &cx->v4l2_dev);
++ if (retval) {
++ printk(KERN_ERR "cx18: v4l2_device_register of card %d failed"
++ "\n", cx->instance);
++ kfree(cx);
++ return retval;
++ }
++ snprintf(cx->v4l2_dev.name, sizeof(cx->v4l2_dev.name), "cx18-%d",
++ cx->instance);
++ CX18_INFO("Initializing card %d\n", cx->instance);
+
+ cx18_process_options(cx);
+ if (cx->options.cardtype == -1) {
+@@ -816,13 +767,10 @@ static int __devinit cx18_probe(struct pci_dev *dev,
+ CX18_DEBUG_INFO("base addr: 0x%08x\n", cx->base_addr);
+
+ /* PCI Device Setup */
+- retval = cx18_setup_pci(cx, dev, pci_id);
++ retval = cx18_setup_pci(cx, pci_dev, pci_id);
+ if (retval != 0)
+ goto free_workqueue;
+
+- /* save cx in the pci struct for later use */
+- pci_set_drvdata(dev, cx);
+-
+ /* map io memory */
+ CX18_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
+ cx->base_addr + CX18_MEM_OFFSET, CX18_MEM_SIZE);
+@@ -856,6 +804,23 @@ static int __devinit cx18_probe(struct pci_dev *dev,
+
+ cx18_gpio_init(cx);
+
++ /* Initialize integrated A/V decoder early to set PLLs, just in case */
++ retval = cx18_av_probe(cx);
++ if (retval) {
++ CX18_ERR("Could not register A/V decoder subdevice\n");
++ goto free_map;
++ }
++ cx18_call_hw(cx, CX18_HW_418_AV, core, init, (u32) CX18_AV_INIT_PLLS);
++
++ /* Initialize GPIO Reset Controller to do chip resets during i2c init */
++ if (cx->card->hw_all & CX18_HW_GPIO_RESET_CTRL) {
++ if (cx18_gpio_register(cx, CX18_HW_GPIO_RESET_CTRL) != 0)
++ CX18_WARN("Could not register GPIO reset controller"
++ "subdevice; proceeding anyway.\n");
++ else
++ cx->hw_flags |= CX18_HW_GPIO_RESET_CTRL;
++ }
++
+ /* active i2c */
+ CX18_DEBUG_INFO("activating i2c...\n");
+ retval = init_cx18_i2c(cx);
+@@ -864,8 +829,6 @@ static int __devinit cx18_probe(struct pci_dev *dev,
+ goto free_map;
+ }
+
+- CX18_DEBUG_INFO("Active card count: %d.\n", cx18_cards_active);
+-
+ if (cx->card->hw_all & CX18_HW_TVEEPROM) {
+ /* Based on the model number the cardtype may be changed.
+ The PCI IDs are not always reliable. */
+@@ -881,8 +844,9 @@ static int __devinit cx18_probe(struct pci_dev *dev,
+ cx18_init_scb(cx);
+
+ /* Register IRQ */
+- retval = request_irq(cx->dev->irq, cx18_irq_handler,
+- IRQF_SHARED | IRQF_DISABLED, cx->name, (void *)cx);
++ retval = request_irq(cx->pci_dev->irq, cx18_irq_handler,
++ IRQF_SHARED | IRQF_DISABLED,
++ cx->v4l2_dev.name, (void *)cx);
+ if (retval) {
+ CX18_ERR("Failed to register irq %d\n", retval);
+ goto free_i2c;
+@@ -917,33 +881,14 @@ static int __devinit cx18_probe(struct pci_dev *dev,
+ initialization. */
+ cx18_init_struct2(cx);
+
+- cx18_load_and_init_modules(cx);
++ cx18_init_subdevs(cx);
+
+- if (cx->std & V4L2_STD_525_60) {
++ if (cx->std & V4L2_STD_525_60)
+ cx->is_60hz = 1;
+- cx->is_out_60hz = 1;
+- } else {
++ else
+ cx->is_50hz = 1;
+- cx->is_out_50hz = 1;
+- }
+- cx->params.video_gop_size = cx->is_60hz ? 15 : 12;
+-
+- /*
+- * FIXME: setting the buffer size based on the tuner standard is
+- * suboptimal, as the CVBS and SVideo inputs could use a different std
+- * and the buffer could end up being too small in that case.
+- */
+- vbi_buf_size = cx->vbi.raw_size * (cx->is_60hz ? 24 : 36) / 2;
+- cx->stream_buf_size[CX18_ENC_STREAM_TYPE_VBI] = vbi_buf_size;
+
+- if (cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] < 0)
+- cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] =
+- cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] * 1024 * 1024
+- / vbi_buf_size;
+- else
+- cx->options.megabytes[CX18_ENC_STREAM_TYPE_VBI] =
+- cx->stream_buffers[CX18_ENC_STREAM_TYPE_VBI] * vbi_buf_size
+- / (1024 * 1024);
++ cx->params.video_gop_size = cx->is_60hz ? 15 : 12;
+
+ if (cx->options.radio > 0)
+ cx->v4l2_cap |= V4L2_CAP_RADIO;
+@@ -956,7 +901,7 @@ static int __devinit cx18_probe(struct pci_dev *dev,
+ setup.mode_mask = T_ANALOG_TV; /* matches TV tuners */
+ setup.tuner_callback = (setup.type == TUNER_XC2028) ?
+ cx18_reset_tuner_gpio : NULL;
+- cx18_call_i2c_clients(cx, TUNER_SET_TYPE_ADDR, &setup);
++ cx18_call_all(cx, tuner, s_type_addr, &setup);
+ if (setup.type == TUNER_XC2028) {
+ static struct xc2028_ctrl ctrl = {
+ .fname = XC2028_DEFAULT_FIRMWARE,
+@@ -966,7 +911,7 @@ static int __devinit cx18_probe(struct pci_dev *dev,
+ .tuner = cx->options.tuner,
+ .priv = &ctrl,
+ };
+- cx18_call_i2c_clients(cx, TUNER_SET_CONFIG, &cfg);
++ cx18_call_all(cx, tuner, s_config, &cfg);
+ }
+ }
+
+@@ -985,14 +930,13 @@ static int __devinit cx18_probe(struct pci_dev *dev,
+ goto free_streams;
+ }
+
+- CX18_INFO("Initialized card #%d: %s\n", cx->num, cx->card_name);
+-
++ CX18_INFO("Initialized card: %s\n", cx->card_name);
+ return 0;
+
+ free_streams:
+ cx18_streams_cleanup(cx, 1);
+ free_irq:
+- free_irq(cx->dev->irq, (void *)cx);
++ free_irq(cx->pci_dev->irq, (void *)cx);
+ free_i2c:
+ exit_cx18_i2c(cx);
+ free_map:
+@@ -1006,11 +950,8 @@ err:
+ retval = -ENODEV;
+ CX18_ERR("Error %d on initialization\n", retval);
+
+- i = cx->num;
+- spin_lock(&cx18_cards_lock);
+- kfree(cx18_cards[i]);
+- cx18_cards[i] = NULL;
+- spin_unlock(&cx18_cards_lock);
++ v4l2_device_unregister(&cx->v4l2_dev);
++ kfree(cx);
+ return retval;
+ }
+
+@@ -1043,8 +984,21 @@ int cx18_init_on_first_open(struct cx18 *cx)
+ }
+ set_bit(CX18_F_I_LOADED_FW, &cx->i_flags);
+
+- /* Init the firmware twice to work around a silicon bug
+- * transport related. */
++ /*
++ * Init the firmware twice to work around a silicon bug
++ * with the digital TS.
++ *
++ * The second firmware load requires us to normalize the APU state,
++ * or the audio for the first analog capture will be badly incorrect.
++ *
++ * I can't seem to call APU_RESETAI and have it succeed without the
++ * APU capturing audio, so we start and stop it here to do the reset
++ */
++
++ /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */
++ cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0);
++ cx18_vapi(cx, CX18_APU_RESETAI, 0);
++ cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
+
+ fw_retry_count = 3;
+ while (--fw_retry_count > 0) {
+@@ -1060,6 +1014,22 @@ int cx18_init_on_first_open(struct cx18 *cx)
+ return -ENXIO;
+ }
+
++ /*
++ * The second firmware load requires us to normalize the APU state,
++ * or the audio for the first analog capture will be badly incorrect.
++ *
++ * I can't seem to call APU_RESETAI and have it succeed without the
++ * APU capturing audio, so we start and stop it here to do the reset
++ */
++
++ /* MPEG Encoding, 224 kbps, MPEG Layer II, 48 ksps */
++ cx18_vapi(cx, CX18_APU_START, 2, CX18_APU_ENCODING_METHOD_MPEG|0xb9, 0);
++ cx18_vapi(cx, CX18_APU_RESETAI, 0);
++ cx18_vapi(cx, CX18_APU_STOP, 1, CX18_APU_ENCODING_METHOD_MPEG);
++
++ /* Init the A/V decoder, if it hasn't been already */
++ v4l2_subdev_call(cx->sd_av, core, init, (u32) CX18_AV_INIT_NORMAL);
++
+ vf.tuner = 0;
+ vf.type = V4L2_TUNER_ANALOG_TV;
+ vf.frequency = 6400; /* the tuner 'baseline' frequency */
+@@ -1092,9 +1062,11 @@ static void cx18_cancel_epu_work_orders(struct cx18 *cx)
+
+ static void cx18_remove(struct pci_dev *pci_dev)
+ {
+- struct cx18 *cx = pci_get_drvdata(pci_dev);
++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
++ struct cx18 *cx = to_cx18(v4l2_dev);
++ int i;
+
+- CX18_DEBUG_INFO("Removing Card #%d\n", cx->num);
++ CX18_DEBUG_INFO("Removing Card\n");
+
+ /* Stop all captures */
+ CX18_DEBUG_INFO("Stopping all streams\n");
+@@ -1115,15 +1087,22 @@ static void cx18_remove(struct pci_dev *pci_dev)
+
+ exit_cx18_i2c(cx);
+
+- free_irq(cx->dev->irq, (void *)cx);
++ free_irq(cx->pci_dev->irq, (void *)cx);
+
+ cx18_iounmap(cx);
+
+ release_mem_region(cx->base_addr, CX18_MEM_SIZE);
+
+- pci_disable_device(cx->dev);
++ pci_disable_device(cx->pci_dev);
++
++ if (cx->vbi.sliced_mpeg_data[0] != NULL)
++ for (i = 0; i < CX18_VBI_FRAMES; i++)
++ kfree(cx->vbi.sliced_mpeg_data[i]);
++
++ CX18_INFO("Removed %s\n", cx->card_name);
+
+- CX18_INFO("Removed %s, card #%d\n", cx->card_name, cx->num);
++ v4l2_device_unregister(v4l2_dev);
++ kfree(cx);
+ }
+
+ /* define a pci_driver for card detection */
+@@ -1138,8 +1117,6 @@ static int module_start(void)
+ {
+ printk(KERN_INFO "cx18: Start initialization, version %s\n", CX18_VERSION);
+
+- memset(cx18_cards, 0, sizeof(cx18_cards));
+-
+ /* Validate parameters */
+ if (cx18_first_minor < 0 || cx18_first_minor >= CX18_MAX_CARDS) {
+ printk(KERN_ERR "cx18: Exiting, cx18_first_minor must be between 0 and %d\n",
+@@ -1162,16 +1139,7 @@ static int module_start(void)
+
+ static void module_cleanup(void)
+ {
+- int i;
+-
+ pci_unregister_driver(&cx18_pci_driver);
+-
+- for (i = 0; i < cx18_cards_active; i++) {
+- if (cx18_cards[i] == NULL)
+- continue;
+- kfree(cx18_cards[i]);
+- }
+-
+ }
+
+ module_init(module_start);
+diff --git a/drivers/media/video/cx18/cx18-driver.h b/drivers/media/video/cx18/cx18-driver.h
+index 0d2edeb..ece4f28 100644
+--- a/drivers/media/video/cx18/cx18-driver.h
++++ b/drivers/media/video/cx18/cx18-driver.h
+@@ -48,6 +48,7 @@
+ #include <linux/dvb/audio.h>
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-ioctl.h>
++#include <media/v4l2-device.h>
+ #include <media/tuner.h>
+ #include "cx18-mailbox.h"
+ #include "cx18-av-core.h"
+@@ -79,7 +80,7 @@
+ #define CX18_CARD_YUAN_MPC718 3 /* Yuan MPC718 */
+ #define CX18_CARD_CNXT_RAPTOR_PAL 4 /* Conexant Raptor PAL */
+ #define CX18_CARD_TOSHIBA_QOSMIO_DVBT 5 /* Toshiba Qosmio Interal DVB-T/Analog*/
+-#define CX18_CARD_LEADTEK_PVR2100 6 /* Leadtek WinFast PVR2100 */
++#define CX18_CARD_LEADTEK_PVR2100 6 /* Leadtek WinFast PVR2100/DVR3100 H */
+ #define CX18_CARD_LAST 6
+
+ #define CX18_ENC_STREAM_TYPE_MPG 0
+@@ -143,12 +144,12 @@
+ /* Flag to turn on high volume debugging */
+ #define CX18_DBGFLG_HIGHVOL (1 << 8)
+
+-/* NOTE: extra space before comma in 'cx->num , ## args' is required for
++/* NOTE: extra space before comma in 'fmt , ## args' is required for
+ gcc-2.95, otherwise it won't compile. */
+ #define CX18_DEBUG(x, type, fmt, args...) \
+ do { \
+ if ((x) & cx18_debug) \
+- printk(KERN_INFO "cx18-%d " type ": " fmt, cx->num , ## args); \
++ v4l2_info(&cx->v4l2_dev, " " type ": " fmt , ## args); \
+ } while (0)
+ #define CX18_DEBUG_WARN(fmt, args...) CX18_DEBUG(CX18_DBGFLG_WARN, "warning", fmt , ## args)
+ #define CX18_DEBUG_INFO(fmt, args...) CX18_DEBUG(CX18_DBGFLG_INFO, "info", fmt , ## args)
+@@ -162,7 +163,7 @@
+ #define CX18_DEBUG_HIGH_VOL(x, type, fmt, args...) \
+ do { \
+ if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \
+- printk(KERN_INFO "cx18%d " type ": " fmt, cx->num , ## args); \
++ v4l2_info(&cx->v4l2_dev, " " type ": " fmt , ## args); \
+ } while (0)
+ #define CX18_DEBUG_HI_WARN(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_WARN, "warning", fmt , ## args)
+ #define CX18_DEBUG_HI_INFO(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_INFO, "info", fmt , ## args)
+@@ -174,9 +175,58 @@
+ #define CX18_DEBUG_HI_IRQ(fmt, args...) CX18_DEBUG_HIGH_VOL(CX18_DBGFLG_IRQ, "irq", fmt , ## args)
+
+ /* Standard kernel messages */
+-#define CX18_ERR(fmt, args...) printk(KERN_ERR "cx18-%d: " fmt, cx->num , ## args)
+-#define CX18_WARN(fmt, args...) printk(KERN_WARNING "cx18-%d: " fmt, cx->num , ## args)
+-#define CX18_INFO(fmt, args...) printk(KERN_INFO "cx18-%d: " fmt, cx->num , ## args)
++#define CX18_ERR(fmt, args...) v4l2_err(&cx->v4l2_dev, fmt , ## args)
++#define CX18_WARN(fmt, args...) v4l2_warn(&cx->v4l2_dev, fmt , ## args)
++#define CX18_INFO(fmt, args...) v4l2_info(&cx->v4l2_dev, fmt , ## args)
++
++/* Messages for internal subdevs to use */
++#define CX18_DEBUG_DEV(x, dev, type, fmt, args...) \
++ do { \
++ if ((x) & cx18_debug) \
++ v4l2_info(dev, " " type ": " fmt , ## args); \
++ } while (0)
++#define CX18_DEBUG_WARN_DEV(dev, fmt, args...) \
++ CX18_DEBUG_DEV(CX18_DBGFLG_WARN, dev, "warning", fmt , ## args)
++#define CX18_DEBUG_INFO_DEV(dev, fmt, args...) \
++ CX18_DEBUG_DEV(CX18_DBGFLG_INFO, dev, "info", fmt , ## args)
++#define CX18_DEBUG_API_DEV(dev, fmt, args...) \
++ CX18_DEBUG_DEV(CX18_DBGFLG_API, dev, "api", fmt , ## args)
++#define CX18_DEBUG_DMA_DEV(dev, fmt, args...) \
++ CX18_DEBUG_DEV(CX18_DBGFLG_DMA, dev, "dma", fmt , ## args)
++#define CX18_DEBUG_IOCTL_DEV(dev, fmt, args...) \
++ CX18_DEBUG_DEV(CX18_DBGFLG_IOCTL, dev, "ioctl", fmt , ## args)
++#define CX18_DEBUG_FILE_DEV(dev, fmt, args...) \
++ CX18_DEBUG_DEV(CX18_DBGFLG_FILE, dev, "file", fmt , ## args)
++#define CX18_DEBUG_I2C_DEV(dev, fmt, args...) \
++ CX18_DEBUG_DEV(CX18_DBGFLG_I2C, dev, "i2c", fmt , ## args)
++#define CX18_DEBUG_IRQ_DEV(dev, fmt, args...) \
++ CX18_DEBUG_DEV(CX18_DBGFLG_IRQ, dev, "irq", fmt , ## args)
++
++#define CX18_DEBUG_HIGH_VOL_DEV(x, dev, type, fmt, args...) \
++ do { \
++ if (((x) & cx18_debug) && (cx18_debug & CX18_DBGFLG_HIGHVOL)) \
++ v4l2_info(dev, " " type ": " fmt , ## args); \
++ } while (0)
++#define CX18_DEBUG_HI_WARN_DEV(dev, fmt, args...) \
++ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_WARN, dev, "warning", fmt , ## args)
++#define CX18_DEBUG_HI_INFO_DEV(dev, fmt, args...) \
++ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_INFO, dev, "info", fmt , ## args)
++#define CX18_DEBUG_HI_API_DEV(dev, fmt, args...) \
++ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_API, dev, "api", fmt , ## args)
++#define CX18_DEBUG_HI_DMA_DEV(dev, fmt, args...) \
++ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_DMA, dev, "dma", fmt , ## args)
++#define CX18_DEBUG_HI_IOCTL_DEV(dev, fmt, args...) \
++ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_IOCTL, dev, "ioctl", fmt , ## args)
++#define CX18_DEBUG_HI_FILE_DEV(dev, fmt, args...) \
++ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_FILE, dev, "file", fmt , ## args)
++#define CX18_DEBUG_HI_I2C_DEV(dev, fmt, args...) \
++ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_I2C, dev, "i2c", fmt , ## args)
++#define CX18_DEBUG_HI_IRQ_DEV(dev, fmt, args...) \
++ CX18_DEBUG_HIGH_VOL_DEV(CX18_DBGFLG_IRQ, dev, "irq", fmt , ## args)
++
++#define CX18_ERR_DEV(dev, fmt, args...) v4l2_err(dev, fmt , ## args)
++#define CX18_WARN_DEV(dev, fmt, args...) v4l2_warn(dev, fmt , ## args)
++#define CX18_INFO_DEV(dev, fmt, args...) v4l2_info(dev, fmt , ## args)
+
+ /* Values for CX18_API_DEC_PLAYBACK_SPEED mpeg_frame_type_mask parameter: */
+ #define MPEG_FRAME_TYPE_IFRAME 1
+@@ -279,7 +329,7 @@ struct cx18_epu_work_order {
+ struct cx18_stream {
+ /* These first four fields are always set, even if the stream
+ is not actually created. */
+- struct video_device *v4l2dev; /* NULL when stream not created */
++ struct video_device *video_dev; /* NULL when stream not created */
+ struct cx18 *cx; /* for ease of use */
+ const char *name; /* name of the stream */
+ int type; /* stream type */
+@@ -292,7 +342,6 @@ struct cx18_stream {
+ int dma; /* can be PCI_DMA_TODEVICE,
+ PCI_DMA_FROMDEVICE or
+ PCI_DMA_NONE */
+- u64 dma_pts;
+ wait_queue_head_t waitq;
+
+ /* Buffer Stats */
+@@ -318,59 +367,121 @@ struct cx18_open_id {
+ /* forward declaration of struct defined in cx18-cards.h */
+ struct cx18_card;
+
++/*
++ * A note about "sliced" VBI data as implemented in this driver:
++ *
++ * Currently we collect the sliced VBI in the form of Ancillary Data
++ * packets, inserted by the AV core decoder/digitizer/slicer in the
++ * horizontal blanking region of the VBI lines, in "raw" mode as far as
++ * the Encoder is concerned. We don't ever tell the Encoder itself
++ * to provide sliced VBI. (AV Core: sliced mode - Encoder: raw mode)
++ *
++ * We then process the ancillary data ourselves to send the sliced data
++ * to the user application directly or build up MPEG-2 private stream 1
++ * packets to splice into (only!) MPEG-2 PS streams for the user app.
++ *
++ * (That's how ivtv essentially does it.)
++ *
++ * The Encoder should be able to extract certain sliced VBI data for
++ * us and provide it in a separate stream or splice it into any type of
++ * MPEG PS or TS stream, but this isn't implemented yet.
++ */
++
++/*
++ * Number of "raw" VBI samples per horizontal line we tell the Encoder to
++ * grab from the decoder/digitizer/slicer output for raw or sliced VBI.
++ * It depends on the pixel clock and the horiz rate:
++ *
++ * (1/Fh)*(2*Fp) = Samples/line
++ * = 4 bytes EAV + Anc data in hblank + 4 bytes SAV + active samples
++ *
++ * Sliced VBI data is sent as ancillary data during horizontal blanking
++ * Raw VBI is sent as active video samples during vertcal blanking
++ *
++ * We use a BT.656 pxiel clock of 13.5 MHz and a BT.656 active line
++ * length of 720 pixels @ 4:2:2 sampling. Thus...
++ *
++ * For systems that use a 15.734 kHz horizontal rate, such as
++ * NTSC-M, PAL-M, PAL-60, and other 60 Hz/525 line systems, we have:
++ *
++ * (1/15.734 kHz) * 2 * 13.5 MHz = 1716 samples/line =
++ * 4 bytes SAV + 268 bytes anc data + 4 bytes SAV + 1440 active samples
++ *
++ * For systems that use a 15.625 kHz horizontal rate, such as
++ * PAL-B/G/H, PAL-I, SECAM-L and other 50 Hz/625 line systems, we have:
++ *
++ * (1/15.625 kHz) * 2 * 13.5 MHz = 1728 samples/line =
++ * 4 bytes SAV + 280 bytes anc data + 4 bytes SAV + 1440 active samples
++ */
++static const u32 vbi_active_samples = 1444; /* 4 byte SAV + 720 Y + 720 U/V */
++static const u32 vbi_hblank_samples_60Hz = 272; /* 4 byte EAV + 268 anc/fill */
++static const u32 vbi_hblank_samples_50Hz = 284; /* 4 byte EAV + 280 anc/fill */
+
+ #define CX18_VBI_FRAMES 32
+
+-/* VBI data */
+ struct vbi_info {
+- u32 enc_size;
+- u32 frame;
+- u8 cc_data_odd[256];
+- u8 cc_data_even[256];
+- int cc_pos;
+- u8 cc_no_update;
+- u8 vps[5];
+- u8 vps_found;
+- int wss;
+- u8 wss_found;
+- u8 wss_no_update;
+- u32 raw_decoder_line_size;
+- u8 raw_decoder_sav_odd_field;
+- u8 raw_decoder_sav_even_field;
+- u32 sliced_decoder_line_size;
+- u8 sliced_decoder_sav_odd_field;
+- u8 sliced_decoder_sav_even_field;
++ /* Current state of v4l2 VBI settings for this device */
+ struct v4l2_format in;
+- /* convenience pointer to sliced struct in vbi_in union */
+- struct v4l2_sliced_vbi_format *sliced_in;
+- u32 service_set_in;
+- int insert_mpeg;
++ struct v4l2_sliced_vbi_format *sliced_in; /* pointer to in.fmt.sliced */
++ u32 count; /* Count of VBI data lines: 60 Hz: 12 or 50 Hz: 18 */
++ u32 start[2]; /* First VBI data line per field: 10 & 273 or 6 & 318 */
+
+- /* Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines.
+- One for /dev/vbi0 and one for /dev/vbi8 */
+- struct v4l2_sliced_vbi_data sliced_data[36];
++ u32 frame; /* Count of VBI buffers/frames received from Encoder */
+
+- /* Buffer for VBI data inserted into MPEG stream.
+- The first byte is a dummy byte that's never used.
+- The next 16 bytes contain the MPEG header for the VBI data,
+- the remainder is the actual VBI data.
+- The max size accepted by the MPEG VBI reinsertion turns out
+- to be 1552 bytes, which happens to be 4 + (1 + 42) * (2 * 18) bytes,
+- where 4 is a four byte header, 42 is the max sliced VBI payload, 1 is
+- a single line header byte and 2 * 18 is the number of VBI lines per frame.
++ /*
++ * Vars for creation and insertion of MPEG Private Stream 1 packets
++ * of sliced VBI data into an MPEG PS
++ */
+
+- However, it seems that the data must be 1K aligned, so we have to
+- pad the data until the 1 or 2 K boundary.
++ /* Boolean: create and insert Private Stream 1 packets into the PS */
++ int insert_mpeg;
++
++ /*
++ * Buffer for the maximum of 2 * 18 * packet_size sliced VBI lines.
++ * Used in cx18-vbi.c only for collecting sliced data, and as a source
++ * during conversion of sliced VBI data into MPEG Priv Stream 1 packets.
++ * We don't need to save state here, but the array may have been a bit
++ * too big (2304 bytes) to alloc from the stack.
++ */
++ struct v4l2_sliced_vbi_data sliced_data[36];
+
+- This pointer array will allocate 2049 bytes to store each VBI frame. */
++ /*
++ * A ring buffer of driver-generated MPEG-2 PS
++ * Program Pack/Private Stream 1 packets for sliced VBI data insertion
++ * into the MPEG PS stream.
++ *
++ * In each sliced_mpeg_data[] buffer is:
++ * 16 byte MPEG-2 PS Program Pack Header
++ * 16 byte MPEG-2 Private Stream 1 PES Header
++ * 4 byte magic number: "itv0" or "ITV0"
++ * 4 byte first field line mask, if "itv0"
++ * 4 byte second field line mask, if "itv0"
++ * 36 lines, if "ITV0"; or <36 lines, if "itv0"; of sliced VBI data
++ *
++ * Each line in the payload is
++ * 1 byte line header derived from the SDID (WSS, CC, VPS, etc.)
++ * 42 bytes of line data
++ *
++ * That's a maximum 1552 bytes of payload in the Private Stream 1 packet
++ * which is the payload size a PVR-350 (CX23415) MPEG decoder will
++ * accept for VBI data. So, including the headers, it's a maximum 1584
++ * bytes total.
++ */
++#define CX18_SLICED_MPEG_DATA_MAXSZ 1584
++ /* copy_vbi_buf() needs 8 temp bytes on the end for the worst case */
++#define CX18_SLICED_MPEG_DATA_BUFSZ (CX18_SLICED_MPEG_DATA_MAXSZ+8)
+ u8 *sliced_mpeg_data[CX18_VBI_FRAMES];
+ u32 sliced_mpeg_size[CX18_VBI_FRAMES];
+- struct cx18_buffer sliced_mpeg_buf;
++
++ /* Count of Program Pack/Program Stream 1 packets inserted into PS */
+ u32 inserted_frame;
+
+- u32 start[2], count;
+- u32 raw_size;
+- u32 sliced_size;
++ /*
++ * A dummy driver stream transfer buffer with a copy of the next
++ * sliced_mpeg_data[] buffer for output to userland apps.
++ * Only used in cx18-fileops.c, but its state needs to persist at times.
++ */
++ struct cx18_buffer sliced_mpeg_buf;
+ };
+
+ /* Per cx23418, per I2C bus private algo callback data */
+@@ -383,16 +494,17 @@ struct cx18_i2c_algo_callback_data {
+
+ /* Struct to hold info about cx18 cards */
+ struct cx18 {
+- int num; /* board number, -1 during init! */
+- char name[8]; /* board name for printk and interrupts (e.g. 'cx180') */
+- struct pci_dev *dev; /* PCI device */
++ int instance;
++ struct pci_dev *pci_dev;
++ struct v4l2_device v4l2_dev;
++ struct v4l2_subdev *sd_av; /* A/V decoder/digitizer sub-device */
++ struct v4l2_subdev *sd_extmux; /* External multiplexer sub-dev */
++
+ const struct cx18_card *card; /* card information */
+ const char *card_name; /* full name of the card */
+ const struct cx18_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */
+ u8 is_50hz;
+ u8 is_60hz;
+- u8 is_out_50hz;
+- u8 is_out_60hz;
+ u8 nof_inputs; /* number of video inputs */
+ u8 nof_audio_inputs; /* number of audio inputs */
+ u16 buffer_id; /* buffer ID counter */
+@@ -413,10 +525,7 @@ struct cx18 {
+
+ /* dualwatch */
+ unsigned long dualwatch_jiffies;
+- u16 dualwatch_stereo_mode;
+-
+- /* Digitizer type */
+- int digitizer; /* 0x00EF = saa7114 0x00FO = saa7115 0x0106 = mic */
++ u32 dualwatch_stereo_mode;
+
+ struct mutex serialize_lock; /* mutex used to serialize open/close/start/stop/ioctl operations */
+ struct cx18_options options; /* User options */
+@@ -426,7 +535,6 @@ struct cx18 {
+ unsigned long i_flags; /* global cx18 flags */
+ atomic_t ana_capturing; /* count number of active analog capture streams */
+ atomic_t tot_capturing; /* total count number of active capture streams */
+- spinlock_t lock; /* lock access to this struct */
+ int search_pack_header;
+
+ int open_id; /* incremented each time an open occurs, used as
+@@ -468,30 +576,30 @@ struct cx18 {
+ struct i2c_adapter i2c_adap[2];
+ struct i2c_algo_bit_data i2c_algo[2];
+ struct cx18_i2c_algo_callback_data i2c_algo_cb_data[2];
+- struct i2c_client i2c_client[2];
+- struct mutex i2c_bus_lock[2];
+- struct i2c_client *i2c_clients[I2C_CLIENTS_MAX];
+
+ /* gpio */
+ u32 gpio_dir;
+ u32 gpio_val;
+ struct mutex gpio_lock;
++ struct v4l2_subdev sd_gpiomux;
++ struct v4l2_subdev sd_resetctrl;
+
+ /* v4l2 and User settings */
+
+ /* codec settings */
+ u32 audio_input;
+ u32 active_input;
+- u32 active_output;
+ v4l2_std_id std;
+ v4l2_std_id tuner_std; /* The norm of the tuner (fixed) */
+ };
+
++static inline struct cx18 *to_cx18(struct v4l2_device *v4l2_dev)
++{
++ return container_of(v4l2_dev, struct cx18, v4l2_dev);
++}
++
+ /* Globals */
+-extern struct cx18 *cx18_cards[];
+-extern int cx18_cards_active;
+ extern int cx18_first_minor;
+-extern spinlock_t cx18_cards_lock;
+
+ /*==============Prototypes==================*/
+
+@@ -511,4 +619,22 @@ static inline int cx18_raw_vbi(const struct cx18 *cx)
+ return cx->vbi.in.type == V4L2_BUF_TYPE_VBI_CAPTURE;
+ }
+
++/* Call the specified callback for all subdevs with a grp_id bit matching the
++ * mask in hw (if 0, then match them all). Ignore any errors. */
++#define cx18_call_hw(cx, hw, o, f, args...) \
++ __v4l2_device_call_subdevs(&(cx)->v4l2_dev, \
++ !(hw) || (sd->grp_id & (hw)), o, f , ##args)
++
++#define cx18_call_all(cx, o, f, args...) cx18_call_hw(cx, 0, o, f , ##args)
++
++/* Call the specified callback for all subdevs with a grp_id bit matching the
++ * mask in hw (if 0, then match them all). If the callback returns an error
++ * other than 0 or -ENOIOCTLCMD, then return with that error code. */
++#define cx18_call_hw_err(cx, hw, o, f, args...) \
++ __v4l2_device_call_subdevs_until_err( \
++ &(cx)->v4l2_dev, !(hw) || (sd->grp_id & (hw)), o, f , ##args)
++
++#define cx18_call_all_err(cx, o, f, args...) \
++ cx18_call_hw_err(cx, 0, o, f , ##args)
++
+ #endif /* CX18_DRIVER_H */
+diff --git a/drivers/media/video/cx18/cx18-dvb.c b/drivers/media/video/cx18/cx18-dvb.c
+index bd5e6f3..3b86f57 100644
+--- a/drivers/media/video/cx18/cx18-dvb.c
++++ b/drivers/media/video/cx18/cx18-dvb.c
+@@ -167,7 +167,7 @@ int cx18_dvb_register(struct cx18_stream *stream)
+
+ ret = dvb_register_adapter(&dvb->dvb_adapter,
+ CX18_DRIVER_NAME,
+- THIS_MODULE, &cx->dev->dev, adapter_nr);
++ THIS_MODULE, &cx->pci_dev->dev, adapter_nr);
+ if (ret < 0)
+ goto err_out;
+
+diff --git a/drivers/media/video/cx18/cx18-fileops.c b/drivers/media/video/cx18/cx18-fileops.c
+index 055f6e0..4d7d6d5 100644
+--- a/drivers/media/video/cx18/cx18-fileops.c
++++ b/drivers/media/video/cx18/cx18-fileops.c
+@@ -128,15 +128,15 @@ static void cx18_release_stream(struct cx18_stream *s)
+ static void cx18_dualwatch(struct cx18 *cx)
+ {
+ struct v4l2_tuner vt;
+- u16 new_bitmap;
+- u16 new_stereo_mode;
+- const u16 stereo_mask = 0x0300;
+- const u16 dual = 0x0200;
++ u32 new_bitmap;
++ u32 new_stereo_mode;
++ const u32 stereo_mask = 0x0300;
++ const u32 dual = 0x0200;
+ u32 h;
+
+ new_stereo_mode = cx->params.audio_properties & stereo_mask;
+ memset(&vt, 0, sizeof(vt));
+- cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, &vt);
++ cx18_call_all(cx, tuner, g_tuner, &vt);
+ if (vt.audmode == V4L2_TUNER_MODE_LANG1_LANG2 &&
+ (vt.rxsubchans & V4L2_TUNER_SUB_LANG2))
+ new_stereo_mode = dual;
+@@ -176,6 +176,8 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
+ *err = 0;
+ while (1) {
+ if (s->type == CX18_ENC_STREAM_TYPE_MPG) {
++ /* Process pending program info updates and pending
++ VBI data */
+
+ if (time_after(jiffies, cx->dualwatch_jiffies + msecs_to_jiffies(1000))) {
+ cx->dualwatch_jiffies = jiffies;
+@@ -186,7 +188,6 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
+ while ((buf = cx18_dequeue(s_vbi, &s_vbi->q_full))) {
+ /* byteswap and process VBI data */
+ cx18_process_vbi_data(cx, buf,
+- s_vbi->dma_pts,
+ s_vbi->type);
+ cx18_stream_put_buf_fw(s_vbi, buf);
+ }
+@@ -207,8 +208,7 @@ static struct cx18_buffer *cx18_get_buffer(struct cx18_stream *s, int non_block,
+ cx18_buf_swap(buf);
+ else {
+ /* byteswap and process VBI data */
+- cx18_process_vbi_data(cx, buf,
+- s->dma_pts, s->type);
++ cx18_process_vbi_data(cx, buf, s->type);
+ }
+ return buf;
+ }
+@@ -260,6 +260,20 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
+ len = ucount;
+ if (cx->vbi.insert_mpeg && s->type == CX18_ENC_STREAM_TYPE_MPG &&
+ !cx18_raw_vbi(cx) && buf != &cx->vbi.sliced_mpeg_buf) {
++ /*
++ * Try to find a good splice point in the PS, just before
++ * an MPEG-2 Program Pack start code, and provide only
++ * up to that point to the user, so it's easy to insert VBI data
++ * the next time around.
++ */
++ /* FIXME - This only works for an MPEG-2 PS, not a TS */
++ /*
++ * An MPEG-2 Program Stream (PS) is a series of
++ * MPEG-2 Program Packs terminated by an
++ * MPEG Program End Code after the last Program Pack.
++ * A Program Pack may hold a PS System Header packet and any
++ * number of Program Elementary Stream (PES) Packets
++ */
+ const char *start = buf->buf + buf->readpos;
+ const char *p = start + 1;
+ const u8 *q;
+@@ -267,38 +281,54 @@ static size_t cx18_copy_buf_to_user(struct cx18_stream *s,
+ int stuffing, i;
+
+ while (start + len > p) {
++ /* Scan for a 0 to find a potential MPEG-2 start code */
+ q = memchr(p, 0, start + len - p);
+ if (q == NULL)
+ break;
+ p = q + 1;
++ /*
++ * Keep looking if not a
++ * MPEG-2 Pack header start code: 0x00 0x00 0x01 0xba
++ * or MPEG-2 video PES start code: 0x00 0x00 0x01 0xe0
++ */
+ if ((char *)q + 15 >= buf->buf + buf->bytesused ||
+ q[1] != 0 || q[2] != 1 || q[3] != ch)
+ continue;
++
++ /* If expecting the primary video PES */
+ if (!cx->search_pack_header) {
++ /* Continue if it couldn't be a PES packet */
+ if ((q[6] & 0xc0) != 0x80)
+ continue;
+- if (((q[7] & 0xc0) == 0x80 &&
+- (q[9] & 0xf0) == 0x20) ||
+- ((q[7] & 0xc0) == 0xc0 &&
+- (q[9] & 0xf0) == 0x30)) {
+- ch = 0xba;
++ /* Check if a PTS or PTS & DTS follow */
++ if (((q[7] & 0xc0) == 0x80 && /* PTS only */
++ (q[9] & 0xf0) == 0x20) || /* PTS only */
++ ((q[7] & 0xc0) == 0xc0 && /* PTS & DTS */
++ (q[9] & 0xf0) == 0x30)) { /* DTS follows */
++ /* Assume we found the video PES hdr */
++ ch = 0xba; /* next want a Program Pack*/
+ cx->search_pack_header = 1;
+- p = q + 9;
++ p = q + 9; /* Skip this video PES hdr */
+ }
+ continue;
+ }
++
++ /* We may have found a Program Pack start code */
++
++ /* Get the count of stuffing bytes & verify them */
+ stuffing = q[13] & 7;
+ /* all stuffing bytes must be 0xff */
+ for (i = 0; i < stuffing; i++)
+ if (q[14 + i] != 0xff)
+ break;
+- if (i == stuffing &&
+- (q[4] & 0xc4) == 0x44 &&
+- (q[12] & 3) == 3 &&
+- q[14 + stuffing] == 0 &&
++ if (i == stuffing && /* right number of stuffing bytes*/
++ (q[4] & 0xc4) == 0x44 && /* marker check */
++ (q[12] & 3) == 3 && /* marker check */
++ q[14 + stuffing] == 0 && /* PES Pack or Sys Hdr */
+ q[15 + stuffing] == 0 &&
+ q[16 + stuffing] == 1) {
+- cx->search_pack_header = 0;
++ /* We declare we actually found a Program Pack*/
++ cx->search_pack_header = 0; /* expect vid PES */
+ len = (char *)q - start;
+ cx18_setup_sliced_vbi_buf(cx);
+ break;
+@@ -578,7 +608,7 @@ int cx18_v4l2_close(struct file *filp)
+ /* Mark that the radio is no longer in use */
+ clear_bit(CX18_F_I_RADIO_USER, &cx->i_flags);
+ /* Switch tuner to TV */
+- cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
++ cx18_call_all(cx, tuner, s_std, cx->std);
+ /* Select correct audio input (i.e. TV tuner or Line in) */
+ cx18_audio_set_io(cx);
+ if (atomic_read(&cx->ana_capturing) > 0) {
+@@ -641,7 +671,7 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp)
+ /* We have the radio */
+ cx18_mute(cx);
+ /* Switch tuner to radio */
+- cx18_call_i2c_clients(cx, AUDC_SET_RADIO, NULL);
++ cx18_call_all(cx, tuner, s_radio);
+ /* Select the correct audio input (i.e. radio tuner) */
+ cx18_audio_set_io(cx);
+ /* Done! Unmute and continue. */
+@@ -652,38 +682,15 @@ static int cx18_serialized_open(struct cx18_stream *s, struct file *filp)
+
+ int cx18_v4l2_open(struct file *filp)
+ {
+- int res, x, y = 0;
+- struct cx18 *cx = NULL;
+- struct cx18_stream *s = NULL;
+- int minor = video_devdata(filp)->minor;
+-
+- /* Find which card this open was on */
+- spin_lock(&cx18_cards_lock);
+- for (x = 0; cx == NULL && x < cx18_cards_active; x++) {
+- /* find out which stream this open was on */
+- for (y = 0; y < CX18_MAX_STREAMS; y++) {
+- if (cx18_cards[x] == NULL)
+- continue;
+- s = &cx18_cards[x]->streams[y];
+- if (s->v4l2dev && s->v4l2dev->minor == minor) {
+- cx = cx18_cards[x];
+- break;
+- }
+- }
+- }
+- spin_unlock(&cx18_cards_lock);
+-
+- if (cx == NULL) {
+- /* Couldn't find a device registered
+- on that minor, shouldn't happen! */
+- printk(KERN_WARNING "No cx18 device found on minor %d\n",
+- minor);
+- return -ENXIO;
+- }
++ int res;
++ struct video_device *video_dev = video_devdata(filp);
++ struct cx18_stream *s = video_get_drvdata(video_dev);
++ struct cx18 *cx = s->cx;;
+
+ mutex_lock(&cx->serialize_lock);
+ if (cx18_init_on_first_open(cx)) {
+- CX18_ERR("Failed to initialize on minor %d\n", minor);
++ CX18_ERR("Failed to initialize on minor %d\n",
++ video_dev->minor);
+ mutex_unlock(&cx->serialize_lock);
+ return -ENXIO;
+ }
+diff --git a/drivers/media/video/cx18/cx18-firmware.c b/drivers/media/video/cx18/cx18-firmware.c
+index 1fa95da..83cd559 100644
+--- a/drivers/media/video/cx18/cx18-firmware.c
++++ b/drivers/media/video/cx18/cx18-firmware.c
+@@ -26,7 +26,6 @@
+ #include "cx18-irq.h"
+ #include "cx18-firmware.h"
+ #include "cx18-cards.h"
+-#include "cx18-av-core.h"
+ #include <linux/firmware.h>
+
+ #define CX18_PROC_SOFT_RESET 0xc70010
+@@ -107,7 +106,7 @@ static int load_cpu_fw_direct(const char *fn, u8 __iomem *mem, struct cx18 *cx)
+ u32 __iomem *dst = (u32 __iomem *)mem;
+ const u32 *src;
+
+- if (request_firmware(&fw, fn, &cx->dev->dev)) {
++ if (request_firmware(&fw, fn, &cx->pci_dev->dev)) {
+ CX18_ERR("Unable to open firmware %s\n", fn);
+ CX18_ERR("Did you put the firmware in the hotplug firmware directory?\n");
+ return -ENOMEM;
+@@ -151,7 +150,7 @@ static int load_apu_fw_direct(const char *fn, u8 __iomem *dst, struct cx18 *cx,
+ u32 apu_version = 0;
+ int sz;
+
+- if (request_firmware(&fw, fn, &cx->dev->dev)) {
++ if (request_firmware(&fw, fn, &cx->pci_dev->dev)) {
+ CX18_ERR("unable to open firmware %s\n", fn);
+ CX18_ERR("did you put the firmware in the hotplug firmware directory?\n");
+ cx18_setup_page(cx, 0);
+@@ -286,23 +285,6 @@ void cx18_init_power(struct cx18 *cx, int lowpwr)
+ cx18_write_reg(cx, 0x2BE2FE, CX18_MPEG_CLOCK_PLL_FRAC);
+ cx18_write_reg(cx, 8, CX18_MPEG_CLOCK_PLL_POST);
+
+- /*
+- * VDCLK Integer = 0x0f, Post Divider = 0x04
+- * AIMCLK Integer = 0x0e, Post Divider = 0x16
+- */
+- cx18_av_write4(cx, CXADEC_PLL_CTRL1, 0x160e040f);
+-
+- /* VDCLK Fraction = 0x2be2fe */
+- /* xtal * 0xf.15f17f0/4 = 108 MHz: 432 MHz before post divide */
+- cx18_av_write4(cx, CXADEC_VID_PLL_FRAC, 0x002be2fe);
+-
+- /* AIMCLK Fraction = 0x05227ad */
+- /* xtal * 0xe.2913d68/0x16 = 48000 * 384: 406 MHz before post-divide */
+- cx18_av_write4(cx, CXADEC_AUX_PLL_FRAC, 0x005227ad);
+-
+- /* SA_MCLK_SEL=1, SA_MCLK_DIV=0x16 */
+- cx18_av_write(cx, CXADEC_I2S_MCLK, 0x56);
+-
+ /* Defaults */
+ /* APU = SC or SC/2 = 125/62.5 */
+ /* EPU = SC = 125 */
+diff --git a/drivers/media/video/cx18/cx18-gpio.c b/drivers/media/video/cx18/cx18-gpio.c
+index 1a99329..5518d14 100644
+--- a/drivers/media/video/cx18/cx18-gpio.c
++++ b/drivers/media/video/cx18/cx18-gpio.c
+@@ -46,6 +46,9 @@
+ * gpio13: cs5345 reset pin
+ */
+
++/*
++ * File scope utility functions
++ */
+ static void gpio_write(struct cx18 *cx)
+ {
+ u32 dir_lo = cx->gpio_dir & 0xffff;
+@@ -63,73 +66,201 @@ static void gpio_write(struct cx18 *cx)
+ CX18_REG_GPIO_OUT2, val_hi, dir_hi);
+ }
+
+-void cx18_reset_i2c_slaves_gpio(struct cx18 *cx)
++static void gpio_update(struct cx18 *cx, u32 mask, u32 data)
+ {
+- const struct cx18_gpio_i2c_slave_reset *p;
++ if (mask == 0)
++ return;
+
+- p = &cx->card->gpio_i2c_slave_reset;
++ mutex_lock(&cx->gpio_lock);
++ cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask);
++ gpio_write(cx);
++ mutex_unlock(&cx->gpio_lock);
++}
++
++static void gpio_reset_seq(struct cx18 *cx, u32 active_lo, u32 active_hi,
++ unsigned int assert_msecs,
++ unsigned int recovery_msecs)
++{
++ u32 mask;
+
+- if ((p->active_lo_mask | p->active_hi_mask) == 0)
++ mask = active_lo | active_hi;
++ if (mask == 0)
+ return;
+
+- /* Assuming that the masks are a subset of the bits in gpio_dir */
++ /*
++ * Assuming that active_hi and active_lo are a subsets of the bits in
++ * gpio_dir. Also assumes that active_lo and active_hi don't overlap
++ * in any bit position
++ */
+
+ /* Assert */
+- mutex_lock(&cx->gpio_lock);
+- cx->gpio_val =
+- (cx->gpio_val | p->active_hi_mask) & ~(p->active_lo_mask);
+- gpio_write(cx);
+- schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted));
++ gpio_update(cx, mask, ~active_lo);
++ schedule_timeout_uninterruptible(msecs_to_jiffies(assert_msecs));
+
+ /* Deassert */
+- cx->gpio_val =
+- (cx->gpio_val | p->active_lo_mask) & ~(p->active_hi_mask);
+- gpio_write(cx);
+- schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery));
++ gpio_update(cx, mask, ~active_hi);
++ schedule_timeout_uninterruptible(msecs_to_jiffies(recovery_msecs));
++}
++
++/*
++ * GPIO Multiplexer - logical device
++ */
++static int gpiomux_log_status(struct v4l2_subdev *sd)
++{
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++
++ mutex_lock(&cx->gpio_lock);
++ CX18_INFO_DEV(sd, "GPIO: direction 0x%08x, value 0x%08x\n",
++ cx->gpio_dir, cx->gpio_val);
+ mutex_unlock(&cx->gpio_lock);
++ return 0;
+ }
+
+-void cx18_reset_ir_gpio(void *data)
++static int gpiomux_s_radio(struct v4l2_subdev *sd)
+ {
+- struct cx18 *cx = ((struct cx18_i2c_algo_callback_data *)data)->cx;
+- const struct cx18_gpio_i2c_slave_reset *p;
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+- p = &cx->card->gpio_i2c_slave_reset;
++ /*
++ * FIXME - work out the cx->active/audio_input mess - this is
++ * intended to handle the switch to radio mode and set the
++ * audio routing, but we need to update the state in cx
++ */
++ gpio_update(cx, cx->card->gpio_audio_input.mask,
++ cx->card->gpio_audio_input.radio);
++ return 0;
++}
+
+- if (p->ir_reset_mask == 0)
+- return;
++static int gpiomux_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
++{
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++ u32 data;
+
+- CX18_DEBUG_INFO("Resetting IR microcontroller\n");
++ switch (cx->card->audio_inputs[cx->audio_input].muxer_input) {
++ case 1:
++ data = cx->card->gpio_audio_input.linein;
++ break;
++ case 0:
++ data = cx->card->gpio_audio_input.tuner;
++ break;
++ default:
++ /*
++ * FIXME - work out the cx->active/audio_input mess - this is
++ * intended to handle the switch from radio mode and set the
++ * audio routing, but we need to update the state in cx
++ */
++ data = cx->card->gpio_audio_input.tuner;
++ break;
++ }
++ gpio_update(cx, cx->card->gpio_audio_input.mask, data);
++ return 0;
++}
+
+- /*
+- Assert timing for the Z8F0811 on HVR-1600 boards:
+- 1. Assert RESET for min of 4 clock cycles at 18.432 MHz to initiate
+- 2. Reset then takes 66 WDT cycles at 10 kHz + 16 xtal clock cycles
+- (6,601,085 nanoseconds ~= 7 milliseconds)
+- 3. DBG pin must be high before chip exits reset for normal operation.
+- DBG is open drain and hopefully pulled high since we don't
+- normally drive it (GPIO 1?) for the HVR-1600
+- 4. Z8F0811 won't exit reset until RESET is deasserted
+- */
+- mutex_lock(&cx->gpio_lock);
+- cx->gpio_val = cx->gpio_val & ~p->ir_reset_mask;
+- gpio_write(cx);
+- mutex_unlock(&cx->gpio_lock);
+- schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_asserted));
++static int gpiomux_s_audio_routing(struct v4l2_subdev *sd,
++ const struct v4l2_routing *route)
++{
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++ u32 data;
++
++ switch (route->input) {
++ case 0:
++ data = cx->card->gpio_audio_input.tuner;
++ break;
++ case 1:
++ data = cx->card->gpio_audio_input.linein;
++ break;
++ case 2:
++ data = cx->card->gpio_audio_input.radio;
++ break;
++ default:
++ return -EINVAL;
++ }
++ gpio_update(cx, cx->card->gpio_audio_input.mask, data);
++ return 0;
++}
++
++static const struct v4l2_subdev_core_ops gpiomux_core_ops = {
++ .log_status = gpiomux_log_status,
++};
++
++static const struct v4l2_subdev_tuner_ops gpiomux_tuner_ops = {
++ .s_std = gpiomux_s_std,
++ .s_radio = gpiomux_s_radio,
++};
++
++static const struct v4l2_subdev_audio_ops gpiomux_audio_ops = {
++ .s_routing = gpiomux_s_audio_routing,
++};
++
++static const struct v4l2_subdev_ops gpiomux_ops = {
++ .core = &gpiomux_core_ops,
++ .tuner = &gpiomux_tuner_ops,
++ .audio = &gpiomux_audio_ops,
++};
++
++/*
++ * GPIO Reset Controller - logical device
++ */
++static int resetctrl_log_status(struct v4l2_subdev *sd)
++{
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
+
+- /*
+- Zilog comes out of reset, loads reset vector address and executes
+- from there. Required recovery delay unknown.
+- */
+ mutex_lock(&cx->gpio_lock);
+- cx->gpio_val = cx->gpio_val | p->ir_reset_mask;
+- gpio_write(cx);
++ CX18_INFO_DEV(sd, "GPIO: direction 0x%08x, value 0x%08x\n",
++ cx->gpio_dir, cx->gpio_val);
+ mutex_unlock(&cx->gpio_lock);
+- schedule_timeout_uninterruptible(msecs_to_jiffies(p->msecs_recovery));
++ return 0;
+ }
+-EXPORT_SYMBOL(cx18_reset_ir_gpio);
+-/* This symbol is exported for use by an infrared module for the IR-blaster */
+
++static int resetctrl_reset(struct v4l2_subdev *sd, u32 val)
++{
++ struct cx18 *cx = v4l2_get_subdevdata(sd);
++ const struct cx18_gpio_i2c_slave_reset *p;
++
++ p = &cx->card->gpio_i2c_slave_reset;
++ switch (val) {
++ case CX18_GPIO_RESET_I2C:
++ gpio_reset_seq(cx, p->active_lo_mask, p->active_hi_mask,
++ p->msecs_asserted, p->msecs_recovery);
++ break;
++ case CX18_GPIO_RESET_Z8F0811:
++ /*
++ * Assert timing for the Z8F0811 on HVR-1600 boards:
++ * 1. Assert RESET for min of 4 clock cycles at 18.432 MHz to
++ * initiate
++ * 2. Reset then takes 66 WDT cycles at 10 kHz + 16 xtal clock
++ * cycles (6,601,085 nanoseconds ~= 7 milliseconds)
++ * 3. DBG pin must be high before chip exits reset for normal
++ * operation. DBG is open drain and hopefully pulled high
++ * since we don't normally drive it (GPIO 1?) for the
++ * HVR-1600
++ * 4. Z8F0811 won't exit reset until RESET is deasserted
++ * 5. Zilog comes out of reset, loads reset vector address and
++ * executes from there. Required recovery delay unknown.
++ */
++ gpio_reset_seq(cx, p->ir_reset_mask, 0,
++ p->msecs_asserted, p->msecs_recovery);
++ break;
++ case CX18_GPIO_RESET_XC2028:
++ if (cx->card->tuners[0].tuner == TUNER_XC2028)
++ gpio_reset_seq(cx, (1 << cx->card->xceive_pin), 0,
++ 1, 1);
++ break;
++ }
++ return 0;
++}
++
++static const struct v4l2_subdev_core_ops resetctrl_core_ops = {
++ .log_status = resetctrl_log_status,
++ .reset = resetctrl_reset,
++};
++
++static const struct v4l2_subdev_ops resetctrl_ops = {
++ .core = &resetctrl_core_ops,
++};
++
++/*
++ * External entry points
++ */
+ void cx18_gpio_init(struct cx18 *cx)
+ {
+ mutex_lock(&cx->gpio_lock);
+@@ -156,6 +287,49 @@ void cx18_gpio_init(struct cx18 *cx)
+ mutex_unlock(&cx->gpio_lock);
+ }
+
++int cx18_gpio_register(struct cx18 *cx, u32 hw)
++{
++ struct v4l2_subdev *sd;
++ const struct v4l2_subdev_ops *ops;
++ char *str;
++
++ switch (hw) {
++ case CX18_HW_GPIO_MUX:
++ sd = &cx->sd_gpiomux;
++ ops = &gpiomux_ops;
++ str = "gpio-mux";
++ break;
++ case CX18_HW_GPIO_RESET_CTRL:
++ sd = &cx->sd_resetctrl;
++ ops = &resetctrl_ops;
++ str = "gpio-reset-ctrl";
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ v4l2_subdev_init(sd, ops);
++ v4l2_set_subdevdata(sd, cx);
++ snprintf(sd->name, sizeof(sd->name), "%s %s", cx->v4l2_dev.name, str);
++ sd->grp_id = hw;
++ return v4l2_device_register_subdev(&cx->v4l2_dev, sd);
++}
++
++void cx18_reset_ir_gpio(void *data)
++{
++ struct cx18 *cx = to_cx18((struct v4l2_device *)data);
++
++ if (cx->card->gpio_i2c_slave_reset.ir_reset_mask == 0)
++ return;
++
++ CX18_DEBUG_INFO("Resetting IR microcontroller\n");
++
++ v4l2_subdev_call(&cx->sd_resetctrl,
++ core, reset, CX18_GPIO_RESET_Z8F0811);
++}
++EXPORT_SYMBOL(cx18_reset_ir_gpio);
++/* This symbol is exported for use by lirc_pvr150 for the IR-blaster */
++
+ /* Xceive tuner reset function */
+ int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value)
+ {
+@@ -163,56 +337,11 @@ int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value)
+ struct cx18_i2c_algo_callback_data *cb_data = algo->data;
+ struct cx18 *cx = cb_data->cx;
+
+- if (cmd != XC2028_TUNER_RESET)
++ if (cmd != XC2028_TUNER_RESET ||
++ cx->card->tuners[0].tuner != TUNER_XC2028)
+ return 0;
+- CX18_DEBUG_INFO("Resetting tuner\n");
+
+- mutex_lock(&cx->gpio_lock);
+- cx->gpio_val &= ~(1 << cx->card->xceive_pin);
+- gpio_write(cx);
+- mutex_unlock(&cx->gpio_lock);
+- schedule_timeout_interruptible(msecs_to_jiffies(1));
+-
+- mutex_lock(&cx->gpio_lock);
+- cx->gpio_val |= 1 << cx->card->xceive_pin;
+- gpio_write(cx);
+- mutex_unlock(&cx->gpio_lock);
+- schedule_timeout_interruptible(msecs_to_jiffies(1));
+- return 0;
+-}
+-
+-int cx18_gpio(struct cx18 *cx, unsigned int command, void *arg)
+-{
+- struct v4l2_routing *route = arg;
+- u32 mask, data;
+-
+- switch (command) {
+- case VIDIOC_INT_S_AUDIO_ROUTING:
+- if (route->input > 2)
+- return -EINVAL;
+- mask = cx->card->gpio_audio_input.mask;
+- switch (route->input) {
+- case 0:
+- data = cx->card->gpio_audio_input.tuner;
+- break;
+- case 1:
+- data = cx->card->gpio_audio_input.linein;
+- break;
+- case 2:
+- default:
+- data = cx->card->gpio_audio_input.radio;
+- break;
+- }
+- break;
+-
+- default:
+- return -EINVAL;
+- }
+- if (mask) {
+- mutex_lock(&cx->gpio_lock);
+- cx->gpio_val = (cx->gpio_val & ~mask) | (data & mask);
+- gpio_write(cx);
+- mutex_unlock(&cx->gpio_lock);
+- }
+- return 0;
++ CX18_DEBUG_INFO("Resetting XCeive tuner\n");
++ return v4l2_subdev_call(&cx->sd_resetctrl,
++ core, reset, CX18_GPIO_RESET_XC2028);
+ }
+diff --git a/drivers/media/video/cx18/cx18-gpio.h b/drivers/media/video/cx18/cx18-gpio.h
+index 39ffccc..f9a5ca3 100644
+--- a/drivers/media/video/cx18/cx18-gpio.h
++++ b/drivers/media/video/cx18/cx18-gpio.h
+@@ -22,7 +22,13 @@
+ */
+
+ void cx18_gpio_init(struct cx18 *cx);
+-void cx18_reset_i2c_slaves_gpio(struct cx18 *cx);
++int cx18_gpio_register(struct cx18 *cx, u32 hw);
++
++enum cx18_gpio_reset_type {
++ CX18_GPIO_RESET_I2C = 0,
++ CX18_GPIO_RESET_Z8F0811 = 1,
++ CX18_GPIO_RESET_XC2028 = 2,
++};
++
+ void cx18_reset_ir_gpio(void *data);
+ int cx18_reset_tuner_gpio(void *dev, int component, int cmd, int value);
+-int cx18_gpio(struct cx18 *cx, unsigned int command, void *arg);
+diff --git a/drivers/media/video/cx18/cx18-i2c.c b/drivers/media/video/cx18/cx18-i2c.c
+index 83e1c63..d092643 100644
+--- a/drivers/media/video/cx18/cx18-i2c.c
++++ b/drivers/media/video/cx18/cx18-i2c.c
+@@ -26,7 +26,6 @@
+ #include "cx18-io.h"
+ #include "cx18-cards.h"
+ #include "cx18-gpio.h"
+-#include "cx18-av-core.h"
+ #include "cx18-i2c.h"
+ #include "cx18-irq.h"
+
+@@ -43,31 +42,37 @@
+ #define CX18_CS5345_I2C_ADDR 0x4c
+
+ /* This array should match the CX18_HW_ defines */
+-static const u8 hw_driverids[] = {
+- I2C_DRIVERID_TUNER,
+- I2C_DRIVERID_TVEEPROM,
+- I2C_DRIVERID_CS5345,
+- 0, /* CX18_HW_GPIO dummy driver ID */
+- 0 /* CX18_HW_CX23418 dummy driver ID */
+-};
+-
+-/* This array should match the CX18_HW_ defines */
+ static const u8 hw_addrs[] = {
+- 0,
+- 0,
+- CX18_CS5345_I2C_ADDR,
+- 0, /* CX18_HW_GPIO dummy driver ID */
+- 0, /* CX18_HW_CX23418 dummy driver ID */
++ 0, /* CX18_HW_TUNER */
++ 0, /* CX18_HW_TVEEPROM */
++ CX18_CS5345_I2C_ADDR, /* CX18_HW_CS5345 */
++ 0, /* CX18_HW_DVB */
++ 0, /* CX18_HW_418_AV */
++ 0, /* CX18_HW_GPIO_MUX */
++ 0, /* CX18_HW_GPIO_RESET_CTRL */
+ };
+
+ /* This array should match the CX18_HW_ defines */
+ /* This might well become a card-specific array */
+ static const u8 hw_bus[] = {
+- 0,
+- 0,
+- 0,
+- 0, /* CX18_HW_GPIO dummy driver ID */
+- 0, /* CX18_HW_CX23418 dummy driver ID */
++ 1, /* CX18_HW_TUNER */
++ 0, /* CX18_HW_TVEEPROM */
++ 0, /* CX18_HW_CS5345 */
++ 0, /* CX18_HW_DVB */
++ 0, /* CX18_HW_418_AV */
++ 0, /* CX18_HW_GPIO_MUX */
++ 0, /* CX18_HW_GPIO_RESET_CTRL */
++};
++
++/* This array should match the CX18_HW_ defines */
++static const char * const hw_modules[] = {
++ "tuner", /* CX18_HW_TUNER */
++ NULL, /* CX18_HW_TVEEPROM */
++ "cs5345", /* CX18_HW_CS5345 */
++ NULL, /* CX18_HW_DVB */
++ NULL, /* CX18_HW_418_AV */
++ NULL, /* CX18_HW_GPIO_MUX */
++ NULL, /* CX18_HW_GPIO_RESET_CTRL */
+ };
+
+ /* This array should match the CX18_HW_ defines */
+@@ -75,83 +80,67 @@ static const char * const hw_devicenames[] = {
+ "tuner",
+ "tveeprom",
+ "cs5345",
+- "gpio",
+- "cx23418",
++ "cx23418_DTV",
++ "cx23418_AV",
++ "gpio_mux",
++ "gpio_reset_ctrl",
+ };
+
+ int cx18_i2c_register(struct cx18 *cx, unsigned idx)
+ {
+- struct i2c_board_info info;
+- struct i2c_client *c;
+- u8 id, bus;
+- int i;
+-
+- CX18_DEBUG_I2C("i2c client register\n");
+- if (idx >= ARRAY_SIZE(hw_driverids) || hw_driverids[idx] == 0)
++ struct v4l2_subdev *sd;
++ int bus = hw_bus[idx];
++ struct i2c_adapter *adap = &cx->i2c_adap[bus];
++ const char *mod = hw_modules[idx];
++ const char *type = hw_devicenames[idx];
++ u32 hw = 1 << idx;
++
++ if (idx >= ARRAY_SIZE(hw_addrs))
+ return -1;
+- id = hw_driverids[idx];
+- bus = hw_bus[idx];
+- memset(&info, 0, sizeof(info));
+- strlcpy(info.type, hw_devicenames[idx], sizeof(info.type));
+- info.addr = hw_addrs[idx];
+- for (i = 0; i < I2C_CLIENTS_MAX; i++)
+- if (cx->i2c_clients[i] == NULL)
+- break;
+-
+- if (i == I2C_CLIENTS_MAX) {
+- CX18_ERR("insufficient room for new I2C client!\n");
+- return -ENOMEM;
+- }
+
+- if (id != I2C_DRIVERID_TUNER) {
+- c = i2c_new_device(&cx->i2c_adap[bus], &info);
+- if (c->driver == NULL)
+- i2c_unregister_device(c);
+- else
+- cx->i2c_clients[i] = c;
+- return cx->i2c_clients[i] ? 0 : -ENODEV;
++ if (hw == CX18_HW_TUNER) {
++ /* special tuner group handling */
++ sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
++ cx->card_i2c->radio);
++ if (sd != NULL)
++ sd->grp_id = hw;
++ sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
++ cx->card_i2c->demod);
++ if (sd != NULL)
++ sd->grp_id = hw;
++ sd = v4l2_i2c_new_probed_subdev(adap, mod, type,
++ cx->card_i2c->tv);
++ if (sd != NULL)
++ sd->grp_id = hw;
++ return sd != NULL ? 0 : -1;
+ }
+
+- /* special tuner handling */
+- c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->radio);
+- if (c && c->driver == NULL)
+- i2c_unregister_device(c);
+- else if (c)
+- cx->i2c_clients[i++] = c;
+- c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->demod);
+- if (c && c->driver == NULL)
+- i2c_unregister_device(c);
+- else if (c)
+- cx->i2c_clients[i++] = c;
+- c = i2c_new_probed_device(&cx->i2c_adap[1], &info, cx->card_i2c->tv);
+- if (c && c->driver == NULL)
+- i2c_unregister_device(c);
+- else if (c)
+- cx->i2c_clients[i++] = c;
+- return 0;
+-}
++ /* Is it not an I2C device or one we do not wish to register? */
++ if (!hw_addrs[idx])
++ return -1;
+
+-static int attach_inform(struct i2c_client *client)
+-{
+- return 0;
++ /* It's an I2C device other than an analog tuner */
++ sd = v4l2_i2c_new_subdev(adap, mod, type, hw_addrs[idx]);
++ if (sd != NULL)
++ sd->grp_id = hw;
++ return sd != NULL ? 0 : -1;
+ }
+
+-static int detach_inform(struct i2c_client *client)
++/* Find the first member of the subdev group id in hw */
++struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw)
+ {
+- int i;
+- struct cx18 *cx = (struct cx18 *)i2c_get_adapdata(client->adapter);
++ struct v4l2_subdev *result = NULL;
++ struct v4l2_subdev *sd;
+
+- CX18_DEBUG_I2C("i2c client detach\n");
+- for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+- if (cx->i2c_clients[i] == client) {
+- cx->i2c_clients[i] = NULL;
++ spin_lock(&cx->v4l2_dev.lock);
++ v4l2_device_for_each_subdev(sd, &cx->v4l2_dev) {
++ if (sd->grp_id == hw) {
++ result = sd;
+ break;
+ }
+ }
+- CX18_DEBUG_I2C("i2c detach [client=%s,%s]\n",
+- client->name, (i < I2C_CLIENTS_MAX) ? "ok" : "failed");
+-
+- return 0;
++ spin_unlock(&cx->v4l2_dev.lock);
++ return result;
+ }
+
+ static void cx18_setscl(void *data, int state)
+@@ -204,8 +193,6 @@ static struct i2c_adapter cx18_i2c_adap_template = {
+ .id = I2C_HW_B_CX2341X,
+ .algo = NULL, /* set by i2c-algo-bit */
+ .algo_data = NULL, /* filled from template */
+- .client_register = attach_inform,
+- .client_unregister = detach_inform,
+ .owner = THIS_MODULE,
+ };
+
+@@ -221,152 +208,28 @@ static struct i2c_algo_bit_data cx18_i2c_algo_template = {
+ .timeout = CX18_ALGO_BIT_TIMEOUT*HZ /* jiffies */
+ };
+
+-static struct i2c_client cx18_i2c_client_template = {
+- .name = "cx18 internal",
+-};
+-
+-int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg)
+-{
+- struct i2c_client *client;
+- int retval;
+- int i;
+-
+- CX18_DEBUG_I2C("call_i2c_client addr=%02x\n", addr);
+- for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+- client = cx->i2c_clients[i];
+- if (client == NULL || client->driver == NULL ||
+- client->driver->command == NULL)
+- continue;
+- if (addr == client->addr) {
+- retval = client->driver->command(client, cmd, arg);
+- return retval;
+- }
+- }
+- if (cmd != VIDIOC_DBG_G_CHIP_IDENT)
+- CX18_ERR("i2c addr 0x%02x not found for cmd 0x%x!\n",
+- addr, cmd);
+- return -ENODEV;
+-}
+-
+-/* Find the i2c device based on the driver ID and return
+- its i2c address or -ENODEV if no matching device was found. */
+-static int cx18_i2c_id_addr(struct cx18 *cx, u32 id)
+-{
+- struct i2c_client *client;
+- int retval = -ENODEV;
+- int i;
+-
+- for (i = 0; i < I2C_CLIENTS_MAX; i++) {
+- client = cx->i2c_clients[i];
+- if (client == NULL || client->driver == NULL)
+- continue;
+- if (id == client->driver->id) {
+- retval = client->addr;
+- break;
+- }
+- }
+- return retval;
+-}
+-
+-/* Find the i2c device name matching the CX18_HW_ flag */
+-static const char *cx18_i2c_hw_name(u32 hw)
+-{
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
+- if (1 << i == hw)
+- return hw_devicenames[i];
+- return "unknown device";
+-}
+-
+-/* Find the i2c device matching the CX18_HW_ flag and return
+- its i2c address or -ENODEV if no matching device was found. */
+-int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw)
+-{
+- int i;
+-
+- for (i = 0; i < ARRAY_SIZE(hw_driverids); i++)
+- if (1 << i == hw)
+- return cx18_i2c_id_addr(cx, hw_driverids[i]);
+- return -ENODEV;
+-}
+-
+-/* Calls i2c device based on CX18_HW_ flag. If hw == 0, then do nothing.
+- If hw == CX18_HW_GPIO then call the gpio handler. */
+-int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg)
+-{
+- int addr;
+-
+- if (hw == 0)
+- return 0;
+-
+- if (hw == CX18_HW_GPIO)
+- return cx18_gpio(cx, cmd, arg);
+-
+- if (hw == CX18_HW_CX23418)
+- return cx18_av_cmd(cx, cmd, arg);
+-
+- addr = cx18_i2c_hw_addr(cx, hw);
+- if (addr < 0) {
+- CX18_ERR("i2c hardware 0x%08x (%s) not found for cmd 0x%x!\n",
+- hw, cx18_i2c_hw_name(hw), cmd);
+- return addr;
+- }
+- return cx18_call_i2c_client(cx, addr, cmd, arg);
+-}
+-
+-/* broadcast cmd for all I2C clients and for the gpio subsystem */
+-void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg)
+-{
+- if (cx->i2c_adap[0].algo == NULL || cx->i2c_adap[1].algo == NULL) {
+- CX18_ERR("adapter is not set\n");
+- return;
+- }
+- cx18_av_cmd(cx, cmd, arg);
+- i2c_clients_command(&cx->i2c_adap[0], cmd, arg);
+- i2c_clients_command(&cx->i2c_adap[1], cmd, arg);
+- if (cx->hw_flags & CX18_HW_GPIO)
+- cx18_gpio(cx, cmd, arg);
+-}
+-
+ /* init + register i2c algo-bit adapter */
+ int init_cx18_i2c(struct cx18 *cx)
+ {
+ int i;
+ CX18_DEBUG_I2C("i2c init\n");
+
+- /* Sanity checks for the I2C hardware arrays. They must be the
+- * same size and GPIO/CX23418 must be the last entries.
+- */
+- if (ARRAY_SIZE(hw_driverids) != ARRAY_SIZE(hw_addrs) ||
+- ARRAY_SIZE(hw_devicenames) != ARRAY_SIZE(hw_addrs) ||
+- CX18_HW_GPIO != (1 << (ARRAY_SIZE(hw_addrs) - 2)) ||
+- CX18_HW_CX23418 != (1 << (ARRAY_SIZE(hw_addrs) - 1)) ||
+- hw_driverids[ARRAY_SIZE(hw_addrs) - 1]) {
+- CX18_ERR("Mismatched I2C hardware arrays\n");
+- return -ENODEV;
+- }
+-
+ for (i = 0; i < 2; i++) {
+- memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template,
+- sizeof(struct i2c_adapter));
++ /* Setup algorithm for adapter */
+ memcpy(&cx->i2c_algo[i], &cx18_i2c_algo_template,
+ sizeof(struct i2c_algo_bit_data));
+ cx->i2c_algo_cb_data[i].cx = cx;
+ cx->i2c_algo_cb_data[i].bus_index = i;
+ cx->i2c_algo[i].data = &cx->i2c_algo_cb_data[i];
+- cx->i2c_adap[i].algo_data = &cx->i2c_algo[i];
+
++ /* Setup adapter */
++ memcpy(&cx->i2c_adap[i], &cx18_i2c_adap_template,
++ sizeof(struct i2c_adapter));
++ cx->i2c_adap[i].algo_data = &cx->i2c_algo[i];
+ sprintf(cx->i2c_adap[i].name + strlen(cx->i2c_adap[i].name),
+- " #%d-%d", cx->num, i);
+- i2c_set_adapdata(&cx->i2c_adap[i], cx);
+-
+- memcpy(&cx->i2c_client[i], &cx18_i2c_client_template,
+- sizeof(struct i2c_client));
+- sprintf(cx->i2c_client[i].name +
+- strlen(cx->i2c_client[i].name), "%d", i);
+- cx->i2c_client[i].adapter = &cx->i2c_adap[i];
+- cx->i2c_adap[i].dev.parent = &cx->dev->dev;
++ " #%d-%d", cx->instance, i);
++ i2c_set_adapdata(&cx->i2c_adap[i], &cx->v4l2_dev);
++ cx->i2c_adap[i].dev.parent = &cx->pci_dev->dev;
+ }
+
+ if (cx18_read_reg(cx, CX18_REG_I2C_2_WR) != 0x0003c02f) {
+@@ -402,7 +265,8 @@ int init_cx18_i2c(struct cx18 *cx)
+ cx18_setscl(&cx->i2c_algo_cb_data[1], 1);
+ cx18_setsda(&cx->i2c_algo_cb_data[1], 1);
+
+- cx18_reset_i2c_slaves_gpio(cx);
++ cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL,
++ core, reset, (u32) CX18_GPIO_RESET_I2C);
+
+ return i2c_bit_add_bus(&cx->i2c_adap[0]) ||
+ i2c_bit_add_bus(&cx->i2c_adap[1]);
+diff --git a/drivers/media/video/cx18/cx18-i2c.h b/drivers/media/video/cx18/cx18-i2c.h
+index 4869739..bdfd192 100644
+--- a/drivers/media/video/cx18/cx18-i2c.h
++++ b/drivers/media/video/cx18/cx18-i2c.h
+@@ -21,11 +21,8 @@
+ * 02111-1307 USA
+ */
+
+-int cx18_i2c_hw_addr(struct cx18 *cx, u32 hw);
+-int cx18_i2c_hw(struct cx18 *cx, u32 hw, unsigned int cmd, void *arg);
+-int cx18_call_i2c_client(struct cx18 *cx, int addr, unsigned cmd, void *arg);
+-void cx18_call_i2c_clients(struct cx18 *cx, unsigned int cmd, void *arg);
+ int cx18_i2c_register(struct cx18 *cx, unsigned idx);
++struct v4l2_subdev *cx18_find_hw(struct cx18 *cx, u32 hw);
+
+ /* init + register i2c algo-bit adapter */
+ int init_cx18_i2c(struct cx18 *cx);
+diff --git a/drivers/media/video/cx18/cx18-ioctl.c b/drivers/media/video/cx18/cx18-ioctl.c
+index 7086aab..e4c9e3d 100644
+--- a/drivers/media/video/cx18/cx18-ioctl.c
++++ b/drivers/media/video/cx18/cx18-ioctl.c
+@@ -58,12 +58,21 @@ u16 cx18_service2vbi(int type)
+ }
+ }
+
++/* Check if VBI services are allowed on the (field, line) for the video std */
+ static int valid_service_line(int field, int line, int is_pal)
+ {
+- return (is_pal && line >= 6 && (line != 23 || field == 0)) ||
++ return (is_pal && line >= 6 &&
++ ((field == 0 && line <= 23) || (field == 1 && line <= 22))) ||
+ (!is_pal && line >= 10 && line < 22);
+ }
+
++/*
++ * For a (field, line, std) and inbound potential set of services for that line,
++ * return the first valid service of those passed in the incoming set for that
++ * line in priority order:
++ * CC, VPS, or WSS over TELETEXT for well known lines
++ * TELETEXT, before VPS, before CC, before WSS, for other lines
++ */
+ static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
+ {
+ u16 valid_set = (is_pal ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525);
+@@ -90,6 +99,10 @@ static u16 select_service_from_set(int field, int line, u16 set, int is_pal)
+ return 0;
+ }
+
++/*
++ * Expand the service_set of *fmt into valid service_lines for the std,
++ * and clear the passed in fmt->service_set
++ */
+ void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+ {
+ u16 set = fmt->service_set;
+@@ -102,7 +115,25 @@ void cx18_expand_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
+ }
+ }
+
++/*
++ * Sanitize the service_lines in *fmt per the video std, and return 1
++ * if any service_line is left as valid after santization
++ */
++static int check_service_set(struct v4l2_sliced_vbi_format *fmt, int is_pal)
++{
++ int f, l;
++ u16 set = 0;
++
++ for (f = 0; f < 2; f++) {
++ for (l = 0; l < 24; l++) {
++ fmt->service_lines[f][l] = select_service_from_set(f, l, fmt->service_lines[f][l], is_pal);
++ set |= fmt->service_lines[f][l];
++ }
++ }
++ return set != 0;
++}
+
++/* Compute the service_set from the assumed valid service_lines of *fmt */
+ u16 cx18_get_service_set(struct v4l2_sliced_vbi_format *fmt)
+ {
+ int f, l;
+@@ -129,10 +160,8 @@ static int cx18_g_fmt_vid_cap(struct file *file, void *fh,
+ pixfmt->priv = 0;
+ if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
+ pixfmt->pixelformat = V4L2_PIX_FMT_HM12;
+- /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
+- pixfmt->sizeimage =
+- pixfmt->height * pixfmt->width +
+- pixfmt->height * (pixfmt->width / 2);
++ /* YUV size is (Y=(h*720) + UV=(h*(720/2))) */
++ pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2;
+ pixfmt->bytesperline = 720;
+ } else {
+ pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
+@@ -149,8 +178,8 @@ static int cx18_g_fmt_vbi_cap(struct file *file, void *fh,
+ struct v4l2_vbi_format *vbifmt = &fmt->fmt.vbi;
+
+ vbifmt->sampling_rate = 27000000;
+- vbifmt->offset = 248;
+- vbifmt->samples_per_line = cx->vbi.raw_decoder_line_size - 4;
++ vbifmt->offset = 248; /* FIXME - slightly wrong for both 50 & 60 Hz */
++ vbifmt->samples_per_line = vbi_active_samples - 4;
+ vbifmt->sample_format = V4L2_PIX_FMT_GREY;
+ vbifmt->start[0] = cx->vbi.start[0];
+ vbifmt->start[1] = cx->vbi.start[1];
+@@ -164,7 +193,30 @@ static int cx18_g_fmt_vbi_cap(struct file *file, void *fh,
+ static int cx18_g_fmt_sliced_vbi_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+ {
+- return -EINVAL;
++ struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
++ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
++
++ /* sane, V4L2 spec compliant, defaults */
++ vbifmt->reserved[0] = 0;
++ vbifmt->reserved[1] = 0;
++ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
++ memset(vbifmt->service_lines, 0, sizeof(vbifmt->service_lines));
++ vbifmt->service_set = 0;
++
++ /*
++ * Fetch the configured service_lines and total service_set from the
++ * digitizer/slicer. Note, cx18_av_vbi() wipes the passed in
++ * fmt->fmt.sliced under valid calling conditions
++ */
++ if (v4l2_subdev_call(cx->sd_av, video, g_fmt, fmt))
++ return -EINVAL;
++
++ /* Ensure V4L2 spec compliant output */
++ vbifmt->reserved[0] = 0;
++ vbifmt->reserved[1] = 0;
++ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
++ vbifmt->service_set = cx18_get_service_set(vbifmt);
++ return 0;
+ }
+
+ static int cx18_try_fmt_vid_cap(struct file *file, void *fh,
+@@ -174,11 +226,18 @@ static int cx18_try_fmt_vid_cap(struct file *file, void *fh,
+ struct cx18 *cx = id->cx;
+ int w = fmt->fmt.pix.width;
+ int h = fmt->fmt.pix.height;
++ int min_h = 2;
+
+ w = min(w, 720);
+- w = max(w, 1);
++ w = max(w, 2);
++ if (id->type == CX18_ENC_STREAM_TYPE_YUV) {
++ /* YUV height must be a multiple of 32 */
++ h &= ~0x1f;
++ min_h = 32;
++ }
+ h = min(h, cx->is_50hz ? 576 : 480);
+- h = max(h, 2);
++ h = max(h, min_h);
++
+ cx18_g_fmt_vid_cap(file, fh, fmt);
+ fmt->fmt.pix.width = w;
+ fmt->fmt.pix.height = h;
+@@ -194,7 +253,20 @@ static int cx18_try_fmt_vbi_cap(struct file *file, void *fh,
+ static int cx18_try_fmt_sliced_vbi_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+ {
+- return -EINVAL;
++ struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
++ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
++
++ vbifmt->io_size = sizeof(struct v4l2_sliced_vbi_data) * 36;
++ vbifmt->reserved[0] = 0;
++ vbifmt->reserved[1] = 0;
++
++ /* If given a service set, expand it validly & clear passed in set */
++ if (vbifmt->service_set)
++ cx18_expand_service_set(vbifmt, cx->is_50hz);
++ /* Sanitize the service_lines, and compute the new set if any valid */
++ if (check_service_set(vbifmt, cx->is_50hz))
++ vbifmt->service_set = cx18_get_service_set(vbifmt);
++ return 0;
+ }
+
+ static int cx18_s_fmt_vid_cap(struct file *file, void *fh,
+@@ -223,7 +295,7 @@ static int cx18_s_fmt_vid_cap(struct file *file, void *fh,
+
+ cx->params.width = w;
+ cx->params.height = h;
+- cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
++ v4l2_subdev_call(cx->sd_av, video, s_fmt, fmt);
+ return cx18_g_fmt_vid_cap(file, fh, fmt);
+ }
+
+@@ -238,54 +310,131 @@ static int cx18_s_fmt_vbi_cap(struct file *file, void *fh,
+ if (ret)
+ return ret;
+
++ /*
++ * Changing the Encoder's Raw VBI parameters won't have any effect
++ * if any analog capture is ongoing
++ */
+ if (!cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
+ return -EBUSY;
+
++ /*
++ * Set the digitizer registers for raw active VBI.
++ * Note cx18_av_vbi_wipes out alot of the passed in fmt under valid
++ * calling conditions
++ */
++ ret = v4l2_subdev_call(cx->sd_av, video, s_fmt, fmt);
++ if (ret)
++ return ret;
++
++ /* Store our new v4l2 (non-)sliced VBI state */
+ cx->vbi.sliced_in->service_set = 0;
+ cx->vbi.in.type = V4L2_BUF_TYPE_VBI_CAPTURE;
+- cx18_av_cmd(cx, VIDIOC_S_FMT, fmt);
++
+ return cx18_g_fmt_vbi_cap(file, fh, fmt);
+ }
+
+ static int cx18_s_fmt_sliced_vbi_cap(struct file *file, void *fh,
+ struct v4l2_format *fmt)
+ {
+- return -EINVAL;
++ struct cx18_open_id *id = fh;
++ struct cx18 *cx = id->cx;
++ int ret;
++ struct v4l2_sliced_vbi_format *vbifmt = &fmt->fmt.sliced;
++
++ ret = v4l2_prio_check(&cx->prio, &id->prio);
++ if (ret)
++ return ret;
++
++ cx18_try_fmt_sliced_vbi_cap(file, fh, fmt);
++
++ /*
++ * Changing the Encoder's Raw VBI parameters won't have any effect
++ * if any analog capture is ongoing
++ */
++ if (cx18_raw_vbi(cx) && atomic_read(&cx->ana_capturing) > 0)
++ return -EBUSY;
++
++ /*
++ * Set the service_lines requested in the digitizer/slicer registers.
++ * Note, cx18_av_vbi() wipes some "impossible" service lines in the
++ * passed in fmt->fmt.sliced under valid calling conditions
++ */
++ ret = v4l2_subdev_call(cx->sd_av, video, s_fmt, fmt);
++ if (ret)
++ return ret;
++ /* Store our current v4l2 sliced VBI settings */
++ cx->vbi.in.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
++ memcpy(cx->vbi.sliced_in, vbifmt, sizeof(*cx->vbi.sliced_in));
++ return 0;
+ }
+
+ static int cx18_g_chip_ident(struct file *file, void *fh,
+ struct v4l2_dbg_chip_ident *chip)
+ {
+ struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
++ int err = 0;
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+- if (v4l2_chip_match_host(&chip->match)) {
+- chip->ident = V4L2_IDENT_CX23418;
+- return 0;
++ switch (chip->match.type) {
++ case V4L2_CHIP_MATCH_HOST:
++ switch (chip->match.addr) {
++ case 0:
++ chip->ident = V4L2_IDENT_CX23418;
++ chip->revision = cx18_read_reg(cx, 0xC72028);
++ break;
++ case 1:
++ /*
++ * The A/V decoder is always present, but in the rare
++ * case that the card doesn't have analog, we don't
++ * use it. We find it w/o using the cx->sd_av pointer
++ */
++ cx18_call_hw(cx, CX18_HW_418_AV,
++ core, g_chip_ident, chip);
++ break;
++ default:
++ /*
++ * Could return ident = V4L2_IDENT_UNKNOWN if we had
++ * other host chips at higher addresses, but we don't
++ */
++ err = -EINVAL; /* per V4L2 spec */
++ break;
++ }
++ break;
++ case V4L2_CHIP_MATCH_I2C_DRIVER:
++ /* If needed, returns V4L2_IDENT_AMBIGUOUS without extra work */
++ cx18_call_all(cx, core, g_chip_ident, chip);
++ break;
++ case V4L2_CHIP_MATCH_I2C_ADDR:
++ /*
++ * We could return V4L2_IDENT_UNKNOWN, but we don't do the work
++ * to look if a chip is at the address with no driver. That's a
++ * dangerous thing to do with EEPROMs anyway.
++ */
++ cx18_call_all(cx, core, g_chip_ident, chip);
++ break;
++ default:
++ err = -EINVAL;
++ break;
+ }
+- cx18_call_i2c_clients(cx, VIDIOC_DBG_G_CHIP_IDENT, chip);
+- return 0;
++ return err;
+ }
+
+ #ifdef CONFIG_VIDEO_ADV_DEBUG
+ static int cx18_cxc(struct cx18 *cx, unsigned int cmd, void *arg)
+ {
+ struct v4l2_dbg_register *regs = arg;
+- unsigned long flags;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EPERM;
+ if (regs->reg >= CX18_MEM_OFFSET + CX18_MEM_SIZE)
+ return -EINVAL;
+
+- spin_lock_irqsave(&cx18_cards_lock, flags);
+ regs->size = 4;
+- if (cmd == VIDIOC_DBG_G_REGISTER)
+- regs->val = cx18_read_enc(cx, regs->reg);
+- else
++ if (cmd == VIDIOC_DBG_S_REGISTER)
+ cx18_write_enc(cx, regs->val, regs->reg);
+- spin_unlock_irqrestore(&cx18_cards_lock, flags);
++ else
++ regs->val = cx18_read_enc(cx, regs->reg);
+ return 0;
+ }
+
+@@ -296,7 +445,8 @@ static int cx18_g_register(struct file *file, void *fh,
+
+ if (v4l2_chip_match_host(&reg->match))
+ return cx18_cxc(cx, VIDIOC_DBG_G_REGISTER, reg);
+- cx18_call_i2c_clients(cx, VIDIOC_DBG_G_REGISTER, reg);
++ /* FIXME - errors shouldn't be ignored */
++ cx18_call_all(cx, core, g_register, reg);
+ return 0;
+ }
+
+@@ -307,7 +457,8 @@ static int cx18_s_register(struct file *file, void *fh,
+
+ if (v4l2_chip_match_host(&reg->match))
+ return cx18_cxc(cx, VIDIOC_DBG_S_REGISTER, reg);
+- cx18_call_i2c_clients(cx, VIDIOC_DBG_S_REGISTER, reg);
++ /* FIXME - errors shouldn't be ignored */
++ cx18_call_all(cx, core, s_register, reg);
+ return 0;
+ }
+ #endif
+@@ -335,7 +486,8 @@ static int cx18_querycap(struct file *file, void *fh,
+
+ strlcpy(vcap->driver, CX18_DRIVER_NAME, sizeof(vcap->driver));
+ strlcpy(vcap->card, cx->card_name, sizeof(vcap->card));
+- snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(cx->dev));
++ snprintf(vcap->bus_info, sizeof(vcap->bus_info),
++ "PCI:%s", pci_name(cx->pci_dev));
+ vcap->version = CX18_DRIVER_VERSION; /* version */
+ vcap->capabilities = cx->v4l2_cap; /* capabilities */
+ return 0;
+@@ -403,7 +555,8 @@ static int cx18_s_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+- return cx18_av_cmd(cx, VIDIOC_S_CROP, crop);
++ CX18_DEBUG_WARN("VIDIOC_S_CROP not implemented\n");
++ return -EINVAL;
+ }
+
+ static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+@@ -412,7 +565,8 @@ static int cx18_g_crop(struct file *file, void *fh, struct v4l2_crop *crop)
+
+ if (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+- return cx18_av_cmd(cx, VIDIOC_G_CROP, crop);
++ CX18_DEBUG_WARN("VIDIOC_G_CROP not implemented\n");
++ return -EINVAL;
+ }
+
+ static int cx18_enum_fmt_vid_cap(struct file *file, void *fh,
+@@ -483,7 +637,7 @@ static int cx18_g_frequency(struct file *file, void *fh,
+ if (vf->tuner != 0)
+ return -EINVAL;
+
+- cx18_call_i2c_clients(cx, VIDIOC_G_FREQUENCY, vf);
++ cx18_call_all(cx, tuner, g_frequency, vf);
+ return 0;
+ }
+
+@@ -502,7 +656,7 @@ int cx18_s_frequency(struct file *file, void *fh, struct v4l2_frequency *vf)
+
+ cx18_mute(cx);
+ CX18_DEBUG_INFO("v4l2 ioctl: set frequency %d\n", vf->frequency);
+- cx18_call_i2c_clients(cx, VIDIOC_S_FREQUENCY, vf);
++ cx18_call_all(cx, tuner, s_frequency, vf);
+ cx18_unmute(cx);
+ return 0;
+ }
+@@ -547,12 +701,11 @@ int cx18_s_std(struct file *file, void *fh, v4l2_std_id *std)
+ cx->vbi.count = cx->is_50hz ? 18 : 12;
+ cx->vbi.start[0] = cx->is_50hz ? 6 : 10;
+ cx->vbi.start[1] = cx->is_50hz ? 318 : 273;
+- cx->vbi.sliced_decoder_line_size = cx->is_60hz ? 272 : 284;
+ CX18_DEBUG_INFO("Switching standard to %llx.\n",
+ (unsigned long long) cx->std);
+
+ /* Tuner */
+- cx18_call_i2c_clients(cx, VIDIOC_S_STD, &cx->std);
++ cx18_call_all(cx, tuner, s_std, cx->std);
+ return 0;
+ }
+
+@@ -569,9 +722,7 @@ static int cx18_s_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+ if (vt->index != 0)
+ return -EINVAL;
+
+- /* Setting tuner can only set audio mode */
+- cx18_call_i2c_clients(cx, VIDIOC_S_TUNER, vt);
+-
++ cx18_call_all(cx, tuner, s_tuner, vt);
+ return 0;
+ }
+
+@@ -582,7 +733,7 @@ static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+ if (vt->index != 0)
+ return -EINVAL;
+
+- cx18_call_i2c_clients(cx, VIDIOC_G_TUNER, vt);
++ cx18_call_all(cx, tuner, g_tuner, vt);
+
+ if (test_bit(CX18_F_I_RADIO_USER, &cx->i_flags)) {
+ strlcpy(vt->name, "cx18 Radio Tuner", sizeof(vt->name));
+@@ -598,7 +749,30 @@ static int cx18_g_tuner(struct file *file, void *fh, struct v4l2_tuner *vt)
+ static int cx18_g_sliced_vbi_cap(struct file *file, void *fh,
+ struct v4l2_sliced_vbi_cap *cap)
+ {
+- return -EINVAL;
++ struct cx18 *cx = ((struct cx18_open_id *)fh)->cx;
++ int set = cx->is_50hz ? V4L2_SLICED_VBI_625 : V4L2_SLICED_VBI_525;
++ int f, l;
++
++ if (cap->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
++ return -EINVAL;
++
++ cap->service_set = 0;
++ for (f = 0; f < 2; f++) {
++ for (l = 0; l < 24; l++) {
++ if (valid_service_line(f, l, cx->is_50hz)) {
++ /*
++ * We can find all v4l2 supported vbi services
++ * for the standard, on a valid line for the std
++ */
++ cap->service_lines[f][l] = set;
++ cap->service_set |= set;
++ } else
++ cap->service_lines[f][l] = 0;
++ }
++ }
++ for (f = 0; f < 3; f++)
++ cap->reserved[f] = 0;
++ return 0;
+ }
+
+ static int cx18_g_enc_index(struct file *file, void *fh,
+@@ -708,13 +882,15 @@ static int cx18_log_status(struct file *file, void *fh)
+ struct v4l2_audio audin;
+ int i;
+
+- CX18_INFO("================= START STATUS CARD #%d =================\n", cx->num);
++ CX18_INFO("================= START STATUS CARD #%d "
++ "=================\n", cx->instance);
++ CX18_INFO("Version: %s Card: %s\n", CX18_VERSION, cx->card_name);
+ if (cx->hw_flags & CX18_HW_TVEEPROM) {
+ struct tveeprom tv;
+
+ cx18_read_eeprom(cx, &tv);
+ }
+- cx18_call_i2c_clients(cx, VIDIOC_LOG_STATUS, NULL);
++ cx18_call_all(cx, core, log_status);
+ cx18_get_input(cx, cx->active_input, &vidin);
+ cx18_get_audio_input(cx, cx->audio_input, &audin);
+ CX18_INFO("Video Input: %s\n", vidin.name);
+@@ -725,12 +901,12 @@ static int cx18_log_status(struct file *file, void *fh)
+ mutex_unlock(&cx->gpio_lock);
+ CX18_INFO("Tuner: %s\n",
+ test_bit(CX18_F_I_RADIO_USER, &cx->i_flags) ? "Radio" : "TV");
+- cx2341x_log_status(&cx->params, cx->name);
++ cx2341x_log_status(&cx->params, cx->v4l2_dev.name);
+ CX18_INFO("Status flags: 0x%08lx\n", cx->i_flags);
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ struct cx18_stream *s = &cx->streams[i];
+
+- if (s->v4l2dev == NULL || s->buffers == 0)
++ if (s->video_dev == NULL || s->buffers == 0)
+ continue;
+ CX18_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n",
+ s->name, s->s_flags,
+@@ -740,7 +916,8 @@ static int cx18_log_status(struct file *file, void *fh)
+ CX18_INFO("Read MPEG/VBI: %lld/%lld bytes\n",
+ (long long)cx->mpg_data_received,
+ (long long)cx->vbi_data_inserted);
+- CX18_INFO("================== END STATUS CARD #%d ==================\n", cx->num);
++ CX18_INFO("================== END STATUS CARD #%d "
++ "==================\n", cx->instance);
+ return 0;
+ }
+
+@@ -754,7 +931,8 @@ static long cx18_default(struct file *file, void *fh, int cmd, void *arg)
+
+ CX18_DEBUG_IOCTL("VIDIOC_INT_S_AUDIO_ROUTING(%d, %d)\n",
+ route->input, route->output);
+- cx18_audio_set_route(cx, route);
++ cx18_call_hw(cx, cx->card->hw_audio_ctrl, audio, s_routing,
++ route);
+ break;
+ }
+
+@@ -762,7 +940,8 @@ static long cx18_default(struct file *file, void *fh, int cmd, void *arg)
+ u32 val = *(u32 *)arg;
+
+ if ((val == 0) || (val & 0x01))
+- cx18_reset_ir_gpio(&cx->i2c_algo_cb_data[0]);
++ cx18_call_hw(cx, CX18_HW_GPIO_RESET_CTRL, core, reset,
++ (u32) CX18_GPIO_RESET_Z8F0811);
+ break;
+ }
+
+@@ -782,6 +961,8 @@ long cx18_v4l2_ioctl(struct file *filp, unsigned int cmd,
+
+ mutex_lock(&cx->serialize_lock);
+
++ /* FIXME - consolidate v4l2_prio_check()'s here */
++
+ if (cx18_debug & CX18_DBGFLG_IOCTL)
+ vfd->debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG;
+ res = video_ioctl2(filp, cmd, arg);
+diff --git a/drivers/media/video/cx18/cx18-mailbox.c b/drivers/media/video/cx18/cx18-mailbox.c
+index de5e723..2226e57 100644
+--- a/drivers/media/video/cx18/cx18-mailbox.c
++++ b/drivers/media/video/cx18/cx18-mailbox.c
+@@ -83,6 +83,8 @@ static const struct cx18_api_info api_info[] = {
+ API_ENTRY(CPU, CX18_CPU_DE_SET_MDL_ACK, 0),
+ API_ENTRY(CPU, CX18_CPU_DE_SET_MDL, API_FAST),
+ API_ENTRY(CPU, CX18_CPU_DE_RELEASE_MDL, API_SLOW),
++ API_ENTRY(APU, CX18_APU_START, 0),
++ API_ENTRY(APU, CX18_APU_STOP, 0),
+ API_ENTRY(APU, CX18_APU_RESETAI, 0),
+ API_ENTRY(CPU, CX18_CPU_DEBUG_PEEK32, 0),
+ API_ENTRY(0, 0, 0),
+@@ -98,21 +100,30 @@ static const struct cx18_api_info *find_api_info(u32 cmd)
+ return NULL;
+ }
+
+-static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name)
++/* Call with buf of n*11+1 bytes */
++static char *u32arr2hex(u32 data[], int n, char *buf)
+ {
+- char argstr[MAX_MB_ARGUMENTS*11+1];
+ char *p;
+ int i;
+
++ for (i = 0, p = buf; i < n; i++, p += 11) {
++ /* kernel snprintf() appends '\0' always */
++ snprintf(p, 12, " %#010x", data[i]);
++ }
++ *p = '\0';
++ return buf;
++}
++
++static void dump_mb(struct cx18 *cx, struct cx18_mailbox *mb, char *name)
++{
++ char argstr[MAX_MB_ARGUMENTS*11+1];
++
+ if (!(cx18_debug & CX18_DBGFLG_API))
+ return;
+
+- for (i = 0, p = argstr; i < MAX_MB_ARGUMENTS; i++, p += 11) {
+- /* kernel snprintf() appends '\0' always */
+- snprintf(p, 12, " %#010x", mb->args[i]);
+- }
+ CX18_DEBUG_API("%s: req %#010x ack %#010x cmd %#010x err %#010x args%s"
+- "\n", name, mb->request, mb->ack, mb->cmd, mb->error, argstr);
++ "\n", name, mb->request, mb->ack, mb->cmd, mb->error,
++ u32arr2hex(mb->args, MAX_MB_ARGUMENTS, argstr));
+ }
+
+
+@@ -439,7 +450,8 @@ void cx18_api_epu_cmd_irq(struct cx18 *cx, int rpu)
+ "incoming %s to EPU mailbox (sequence no. %u)"
+ "\n",
+ rpu_str[rpu], rpu_str[rpu], order_mb->request);
+- dump_mb(cx, order_mb, "incoming");
++ if (cx18_debug & CX18_DBGFLG_WARN)
++ dump_mb(cx, order_mb, "incoming");
+ order->flags = CX18_F_EWO_MB_STALE_UPON_RECEIPT;
+ }
+
+@@ -468,16 +480,24 @@ static int cx18_api_call(struct cx18 *cx, u32 cmd, int args, u32 data[])
+ struct mutex *mb_lock;
+ long int timeout, ret;
+ int i;
++ char argstr[MAX_MB_ARGUMENTS*11+1];
+
+ if (info == NULL) {
+ CX18_WARN("unknown cmd %x\n", cmd);
+ return -EINVAL;
+ }
+
+- if (cmd == CX18_CPU_DE_SET_MDL)
+- CX18_DEBUG_HI_API("%s\n", info->name);
+- else
+- CX18_DEBUG_API("%s\n", info->name);
++ if (cx18_debug & CX18_DBGFLG_API) { /* only call u32arr2hex if needed */
++ if (cmd == CX18_CPU_DE_SET_MDL) {
++ if (cx18_debug & CX18_DBGFLG_HIGHVOL)
++ CX18_DEBUG_HI_API("%s\tcmd %#010x args%s\n",
++ info->name, cmd,
++ u32arr2hex(data, args, argstr));
++ } else
++ CX18_DEBUG_API("%s\tcmd %#010x args%s\n",
++ info->name, cmd,
++ u32arr2hex(data, args, argstr));
++ }
+
+ switch (info->rpu) {
+ case APU:
+diff --git a/drivers/media/video/cx18/cx18-queue.c b/drivers/media/video/cx18/cx18-queue.c
+index 8d9441e..3046b8e 100644
+--- a/drivers/media/video/cx18/cx18-queue.c
++++ b/drivers/media/video/cx18/cx18-queue.c
+@@ -204,7 +204,7 @@ int cx18_stream_alloc(struct cx18_stream *s)
+ }
+ buf->id = cx->buffer_id++;
+ INIT_LIST_HEAD(&buf->list);
+- buf->dma_handle = pci_map_single(s->cx->dev,
++ buf->dma_handle = pci_map_single(s->cx->pci_dev,
+ buf->buf, s->buf_size, s->dma);
+ cx18_buf_sync_for_cpu(s, buf);
+ cx18_enqueue(s, buf, &s->q_free);
+@@ -227,7 +227,7 @@ void cx18_stream_free(struct cx18_stream *s)
+
+ /* empty q_free */
+ while ((buf = cx18_dequeue(s, &s->q_free))) {
+- pci_unmap_single(s->cx->dev, buf->dma_handle,
++ pci_unmap_single(s->cx->pci_dev, buf->dma_handle,
+ s->buf_size, s->dma);
+ kfree(buf->buf);
+ kfree(buf);
+diff --git a/drivers/media/video/cx18/cx18-queue.h b/drivers/media/video/cx18/cx18-queue.h
+index 456cec3..4de0626 100644
+--- a/drivers/media/video/cx18/cx18-queue.h
++++ b/drivers/media/video/cx18/cx18-queue.h
+@@ -29,14 +29,14 @@
+ static inline void cx18_buf_sync_for_cpu(struct cx18_stream *s,
+ struct cx18_buffer *buf)
+ {
+- pci_dma_sync_single_for_cpu(s->cx->dev, buf->dma_handle,
++ pci_dma_sync_single_for_cpu(s->cx->pci_dev, buf->dma_handle,
+ s->buf_size, s->dma);
+ }
+
+ static inline void cx18_buf_sync_for_device(struct cx18_stream *s,
+ struct cx18_buffer *buf)
+ {
+- pci_dma_sync_single_for_device(s->cx->dev, buf->dma_handle,
++ pci_dma_sync_single_for_device(s->cx->pci_dev, buf->dma_handle,
+ s->buf_size, s->dma);
+ }
+
+diff --git a/drivers/media/video/cx18/cx18-streams.c b/drivers/media/video/cx18/cx18-streams.c
+index 89c1ec9..0932b76 100644
+--- a/drivers/media/video/cx18/cx18-streams.c
++++ b/drivers/media/video/cx18/cx18-streams.c
+@@ -32,7 +32,6 @@
+ #include "cx18-streams.h"
+ #include "cx18-cards.h"
+ #include "cx18-scb.h"
+-#include "cx18-av-core.h"
+ #include "cx18-dvb.h"
+
+ #define CX18_DSP0_INTERRUPT_MASK 0xd0004C
+@@ -101,11 +100,11 @@ static struct {
+ static void cx18_stream_init(struct cx18 *cx, int type)
+ {
+ struct cx18_stream *s = &cx->streams[type];
+- struct video_device *dev = s->v4l2dev;
++ struct video_device *video_dev = s->video_dev;
+
+- /* we need to keep v4l2dev, so restore it afterwards */
++ /* we need to keep video_dev, so restore it afterwards */
+ memset(s, 0, sizeof(*s));
+- s->v4l2dev = dev;
++ s->video_dev = video_dev;
+
+ /* initialize cx18_stream fields */
+ s->cx = cx;
+@@ -130,12 +129,12 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
+ struct cx18_stream *s = &cx->streams[type];
+ u32 cap = cx->v4l2_cap;
+ int num_offset = cx18_stream_info[type].num_offset;
+- int num = cx->num + cx18_first_minor + num_offset;
++ int num = cx->instance + cx18_first_minor + num_offset;
+
+- /* These four fields are always initialized. If v4l2dev == NULL, then
++ /* These four fields are always initialized. If video_dev == NULL, then
+ this stream is not in use. In that case no other fields but these
+ four can be used. */
+- s->v4l2dev = NULL;
++ s->video_dev = NULL;
+ s->cx = cx;
+ s->type = type;
+ s->name = cx18_stream_info[type].name;
+@@ -163,22 +162,22 @@ static int cx18_prep_dev(struct cx18 *cx, int type)
+ return 0;
+
+ /* allocate and initialize the v4l2 video device structure */
+- s->v4l2dev = video_device_alloc();
+- if (s->v4l2dev == NULL) {
++ s->video_dev = video_device_alloc();
++ if (s->video_dev == NULL) {
+ CX18_ERR("Couldn't allocate v4l2 video_device for %s\n",
+ s->name);
+ return -ENOMEM;
+ }
+
+- snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "cx18-%d",
+- cx->num);
++ snprintf(s->video_dev->name, sizeof(s->video_dev->name), "%s %s",
++ cx->v4l2_dev.name, s->name);
+
+- s->v4l2dev->num = num;
+- s->v4l2dev->parent = &cx->dev->dev;
+- s->v4l2dev->fops = &cx18_v4l2_enc_fops;
+- s->v4l2dev->release = video_device_release;
+- s->v4l2dev->tvnorms = V4L2_STD_ALL;
+- cx18_set_funcs(s->v4l2dev);
++ s->video_dev->num = num;
++ s->video_dev->v4l2_dev = &cx->v4l2_dev;
++ s->video_dev->fops = &cx18_v4l2_enc_fops;
++ s->video_dev->release = video_device_release;
++ s->video_dev->tvnorms = V4L2_STD_ALL;
++ cx18_set_funcs(s->video_dev);
+ return 0;
+ }
+
+@@ -227,28 +226,30 @@ static int cx18_reg_dev(struct cx18 *cx, int type)
+ }
+ }
+
+- if (s->v4l2dev == NULL)
++ if (s->video_dev == NULL)
+ return 0;
+
+- num = s->v4l2dev->num;
++ num = s->video_dev->num;
+ /* card number + user defined offset + device offset */
+ if (type != CX18_ENC_STREAM_TYPE_MPG) {
+ struct cx18_stream *s_mpg = &cx->streams[CX18_ENC_STREAM_TYPE_MPG];
+
+- if (s_mpg->v4l2dev)
+- num = s_mpg->v4l2dev->num + cx18_stream_info[type].num_offset;
++ if (s_mpg->video_dev)
++ num = s_mpg->video_dev->num
++ + cx18_stream_info[type].num_offset;
+ }
++ video_set_drvdata(s->video_dev, s);
+
+ /* Register device. First try the desired minor, then any free one. */
+- ret = video_register_device(s->v4l2dev, vfl_type, num);
++ ret = video_register_device(s->video_dev, vfl_type, num);
+ if (ret < 0) {
+ CX18_ERR("Couldn't register v4l2 device for %s kernel number %d\n",
+ s->name, num);
+- video_device_release(s->v4l2dev);
+- s->v4l2dev = NULL;
++ video_device_release(s->video_dev);
++ s->video_dev = NULL;
+ return ret;
+ }
+- num = s->v4l2dev->num;
++ num = s->video_dev->num;
+
+ switch (vfl_type) {
+ case VFL_TYPE_GRABBER:
+@@ -312,9 +313,9 @@ void cx18_streams_cleanup(struct cx18 *cx, int unregister)
+ cx->streams[type].dvb.enabled = false;
+ }
+
+- vdev = cx->streams[type].v4l2dev;
++ vdev = cx->streams[type].video_dev;
+
+- cx->streams[type].v4l2dev = NULL;
++ cx->streams[type].video_dev = NULL;
+ if (vdev == NULL)
+ continue;
+
+@@ -346,46 +347,88 @@ static void cx18_vbi_setup(struct cx18_stream *s)
+ }
+
+ /* setup VBI registers */
+- cx18_av_cmd(cx, VIDIOC_S_FMT, &cx->vbi.in);
+-
+- /* determine number of lines and total number of VBI bytes.
+- A raw line takes 1444 bytes: 4 byte SAV code + 2 * 720
+- A sliced line takes 51 bytes: 4 byte frame header, 4 byte internal
+- header, 42 data bytes + checksum (to be confirmed) */
++ v4l2_subdev_call(cx->sd_av, video, s_fmt, &cx->vbi.in);
++
++ /*
++ * Send the CX18_CPU_SET_RAW_VBI_PARAM API command to setup Encoder Raw
++ * VBI when the first analog capture channel starts, as once it starts
++ * (e.g. MPEG), we can't effect any change in the Encoder Raw VBI setup
++ * (i.e. for the VBI capture channels). We also send it for each
++ * analog capture channel anyway just to make sure we get the proper
++ * behavior
++ */
+ if (raw) {
+ lines = cx->vbi.count * 2;
+ } else {
+- lines = cx->is_60hz ? 24 : 38;
+- if (cx->is_60hz)
+- lines += 2;
++ /*
++ * For 525/60 systems, according to the VIP 2 & BT.656 std:
++ * The EAV RP code's Field bit toggles on line 4, a few lines
++ * after the Vertcal Blank bit has already toggled.
++ * Tell the encoder to capture 21-4+1=18 lines per field,
++ * since we want lines 10 through 21.
++ *
++ * FIXME - revisit for 625/50 systems
++ */
++ lines = cx->is_60hz ? (21 - 4 + 1) * 2 : 38;
+ }
+
+- cx->vbi.enc_size = lines *
+- (raw ? cx->vbi.raw_size : cx->vbi.sliced_size);
+-
+ data[0] = s->handle;
+ /* Lines per field */
+ data[1] = (lines / 2) | ((lines / 2) << 16);
+ /* bytes per line */
+- data[2] = (raw ? cx->vbi.raw_decoder_line_size
+- : cx->vbi.sliced_decoder_line_size);
++ data[2] = (raw ? vbi_active_samples
++ : (cx->is_60hz ? vbi_hblank_samples_60Hz
++ : vbi_hblank_samples_50Hz));
+ /* Every X number of frames a VBI interrupt arrives
+ (frames as in 25 or 30 fps) */
+ data[3] = 1;
+- /* Setup VBI for the cx25840 digitizer */
++ /*
++ * Set the SAV/EAV RP codes to look for as start/stop points
++ * when in VIP-1.1 mode
++ */
+ if (raw) {
++ /*
++ * Start codes for beginning of "active" line in vertical blank
++ * 0x20 ( VerticalBlank )
++ * 0x60 ( EvenField VerticalBlank )
++ */
+ data[4] = 0x20602060;
++ /*
++ * End codes for end of "active" raw lines and regular lines
++ * 0x30 ( VerticalBlank HorizontalBlank)
++ * 0x70 ( EvenField VerticalBlank HorizontalBlank)
++ * 0x90 (Task HorizontalBlank)
++ * 0xd0 (Task EvenField HorizontalBlank)
++ */
+ data[5] = 0x307090d0;
+ } else {
++ /*
++ * End codes for active video, we want data in the hblank region
++ * 0xb0 (Task 0 VerticalBlank HorizontalBlank)
++ * 0xf0 (Task EvenField VerticalBlank HorizontalBlank)
++ *
++ * Since the V bit is only allowed to toggle in the EAV RP code,
++ * just before the first active region line, these two
++ * are problematic:
++ * 0x90 (Task HorizontalBlank)
++ * 0xd0 (Task EvenField HorizontalBlank)
++ *
++ * We have set the digitzer such that we don't have to worry
++ * about these problem codes.
++ */
+ data[4] = 0xB0F0B0F0;
++ /*
++ * Start codes for beginning of active line in vertical blank
++ * 0xa0 (Task VerticalBlank )
++ * 0xe0 (Task EvenField VerticalBlank )
++ */
+ data[5] = 0xA0E0A0E0;
+ }
+
+ CX18_DEBUG_INFO("Setup VBI h: %d lines %x bpl %d fr %d %x %x\n",
+ data[0], data[1], data[2], data[3], data[4], data[5]);
+
+- if (s->type == CX18_ENC_STREAM_TYPE_VBI)
+- cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
++ cx18_api(cx, CX18_CPU_SET_RAW_VBI_PARAM, 6, data);
+ }
+
+ struct cx18_queue *cx18_stream_put_buf_fw(struct cx18_stream *s,
+@@ -434,10 +477,10 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
+ u32 data[MAX_MB_ARGUMENTS];
+ struct cx18 *cx = s->cx;
+ struct cx18_buffer *buf;
+- int ts = 0;
+ int captype = 0;
++ struct cx18_api_func_private priv;
+
+- if (s->v4l2dev == NULL && s->dvb.enabled == 0)
++ if (s->video_dev == NULL && s->dvb.enabled == 0)
+ return -EINVAL;
+
+ CX18_DEBUG_INFO("Start encoder stream %s\n", s->name);
+@@ -453,7 +496,6 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
+
+ case CX18_ENC_STREAM_TYPE_TS:
+ captype = CAPTURE_CHANNEL_TYPE_TS;
+- ts = 1;
+ break;
+ case CX18_ENC_STREAM_TYPE_YUV:
+ captype = CAPTURE_CHANNEL_TYPE_YUV;
+@@ -462,8 +504,16 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
+ captype = CAPTURE_CHANNEL_TYPE_PCM;
+ break;
+ case CX18_ENC_STREAM_TYPE_VBI:
++#ifdef CX18_ENCODER_PARSES_SLICED
+ captype = cx18_raw_vbi(cx) ?
+ CAPTURE_CHANNEL_TYPE_VBI : CAPTURE_CHANNEL_TYPE_SLICED_VBI;
++#else
++ /*
++ * Currently we set things up so that Sliced VBI from the
++ * digitizer is handled as Raw VBI by the encoder
++ */
++ captype = CAPTURE_CHANNEL_TYPE_VBI;
++#endif
+ cx->vbi.frame = 0;
+ cx->vbi.inserted_frame = 0;
+ memset(cx->vbi.sliced_mpeg_size,
+@@ -473,10 +523,6 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
+ return -EINVAL;
+ }
+
+- /* mute/unmute video */
+- cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2,
+- s->handle, !!test_bit(CX18_F_I_RADIO_USER, &cx->i_flags));
+-
+ /* Clear Streamoff flags in case left from last capture */
+ clear_bit(CX18_F_S_STREAMOFF, &s->s_flags);
+
+@@ -484,31 +530,63 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
+ s->handle = data[0];
+ cx18_vapi(cx, CX18_CPU_SET_CHANNEL_TYPE, 2, s->handle, captype);
+
+- if (atomic_read(&cx->ana_capturing) == 0 && !ts) {
+- struct cx18_api_func_private priv;
+-
+- /* Stuff from Windows, we don't know what it is */
++ /*
++ * For everything but CAPTURE_CHANNEL_TYPE_TS, play it safe and
++ * set up all the parameters, as it is not obvious which parameters the
++ * firmware shares across capture channel types and which it does not.
++ *
++ * Some of the cx18_vapi() calls below apply to only certain capture
++ * channel types. We're hoping there's no harm in calling most of them
++ * anyway, as long as the values are all consistent. Setting some
++ * shared parameters will have no effect once an analog capture channel
++ * has started streaming.
++ */
++ if (captype != CAPTURE_CHANNEL_TYPE_TS) {
+ cx18_vapi(cx, CX18_CPU_SET_VER_CROP_LINE, 2, s->handle, 0);
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 3, 1);
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 8, 0);
+ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 3, s->handle, 4, 1);
+- cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2, s->handle, 12);
+
++ /*
++ * Audio related reset according to
++ * Documentation/video4linux/cx2341x/fw-encoder-api.txt
++ */
++ if (atomic_read(&cx->ana_capturing) == 0)
++ cx18_vapi(cx, CX18_CPU_SET_MISC_PARAMETERS, 2,
++ s->handle, 12);
++
++ /*
++ * Number of lines for Field 1 & Field 2 according to
++ * Documentation/video4linux/cx2341x/fw-encoder-api.txt
++ * Field 1 is 312 for 625 line systems in BT.656
++ * Field 2 is 313 for 625 line systems in BT.656
++ */
+ cx18_vapi(cx, CX18_CPU_SET_CAPTURE_LINE_NO, 3,
+- s->handle, cx->digitizer, cx->digitizer);
++ s->handle, 312, 313);
+
+- /* Setup VBI */
+ if (cx->v4l2_cap & V4L2_CAP_VBI_CAPTURE)
+ cx18_vbi_setup(s);
+
+- /* assign program index info.
+- Mask 7: select I/P/B, Num_req: 400 max */
++ /*
++ * assign program index info.
++ * Mask 7: select I/P/B, Num_req: 400 max
++ * FIXME - currently we have this hardcoded as disabled
++ */
+ cx18_vapi_result(cx, data, CX18_CPU_SET_INDEXTABLE, 1, 0);
+
+- /* Setup API for Stream */
++ /* Call out to the common CX2341x API setup for user controls */
+ priv.cx = cx;
+ priv.s = s;
+ cx2341x_update(&priv, cx18_api_func, NULL, &cx->params);
++
++ /*
++ * When starting a capture and we're set for radio,
++ * ensure the video is muted, despite the user control.
++ */
++ if (!cx->params.video_mute &&
++ test_bit(CX18_F_I_RADIO_USER, &cx->i_flags))
++ cx18_vapi(cx, CX18_CPU_SET_VIDEO_MUTE, 2, s->handle,
++ (cx->params.video_mute_yuv << 8) | 1);
+ }
+
+ if (atomic_read(&cx->tot_capturing) == 0) {
+@@ -552,7 +630,7 @@ int cx18_start_v4l2_encode_stream(struct cx18_stream *s)
+ }
+
+ /* you're live! sit back and await interrupts :) */
+- if (!ts)
++ if (captype != CAPTURE_CHANNEL_TYPE_TS)
+ atomic_inc(&cx->ana_capturing);
+ atomic_inc(&cx->tot_capturing);
+ return 0;
+@@ -565,7 +643,7 @@ void cx18_stop_all_captures(struct cx18 *cx)
+ for (i = CX18_MAX_STREAMS - 1; i >= 0; i--) {
+ struct cx18_stream *s = &cx->streams[i];
+
+- if (s->v4l2dev == NULL && s->dvb.enabled == 0)
++ if (s->video_dev == NULL && s->dvb.enabled == 0)
+ continue;
+ if (test_bit(CX18_F_S_STREAMING, &s->s_flags))
+ cx18_stop_v4l2_encode_stream(s, 0);
+@@ -577,7 +655,7 @@ int cx18_stop_v4l2_encode_stream(struct cx18_stream *s, int gop_end)
+ struct cx18 *cx = s->cx;
+ unsigned long then;
+
+- if (s->v4l2dev == NULL && s->dvb.enabled == 0)
++ if (s->video_dev == NULL && s->dvb.enabled == 0)
+ return -EINVAL;
+
+ /* This function assumes that you are allowed to stop the capture
+@@ -629,7 +707,7 @@ u32 cx18_find_handle(struct cx18 *cx)
+ for (i = 0; i < CX18_MAX_STREAMS; i++) {
+ struct cx18_stream *s = &cx->streams[i];
+
+- if (s->v4l2dev && (s->handle != CX18_INVALID_TASK_HANDLE))
++ if (s->video_dev && (s->handle != CX18_INVALID_TASK_HANDLE))
+ return s->handle;
+ }
+ return CX18_INVALID_TASK_HANDLE;
+@@ -647,7 +725,7 @@ struct cx18_stream *cx18_handle_to_stream(struct cx18 *cx, u32 handle)
+ s = &cx->streams[i];
+ if (s->handle != handle)
+ continue;
+- if (s->v4l2dev || s->dvb.enabled)
++ if (s->video_dev || s->dvb.enabled)
+ return s;
+ }
+ return NULL;
+diff --git a/drivers/media/video/cx18/cx18-vbi.c b/drivers/media/video/cx18/cx18-vbi.c
+index fb595bd..c2aef4a 100644
+--- a/drivers/media/video/cx18/cx18-vbi.c
++++ b/drivers/media/video/cx18/cx18-vbi.c
+@@ -25,7 +25,16 @@
+ #include "cx18-vbi.h"
+ #include "cx18-ioctl.h"
+ #include "cx18-queue.h"
+-#include "cx18-av-core.h"
++
++/*
++ * Raster Reference/Protection (RP) bytes, used in Start/End Active
++ * Video codes emitted from the digitzer in VIP 1.x mode, that flag the start
++ * of VBI sample or VBI ancilliary data regions in the digitial ratser line.
++ *
++ * Task FieldEven VerticalBlank HorizontalBlank 0 0 0 0
++ */
++static const u8 raw_vbi_sav_rp[2] = { 0x20, 0x60 }; /* __V_, _FV_ */
++static const u8 sliced_vbi_eav_rp[2] = { 0xb0, 0xf0 }; /* T_VH, TFVH */
+
+ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp)
+ {
+@@ -34,10 +43,17 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp)
+ u32 linemask[2] = { 0, 0 };
+ unsigned short size;
+ static const u8 mpeg_hdr_data[] = {
+- 0x00, 0x00, 0x01, 0xba, 0x44, 0x00, 0x0c, 0x66,
+- 0x24, 0x01, 0x01, 0xd1, 0xd3, 0xfa, 0xff, 0xff,
+- 0x00, 0x00, 0x01, 0xbd, 0x00, 0x1a, 0x84, 0x80,
+- 0x07, 0x21, 0x00, 0x5d, 0x63, 0xa7, 0xff, 0xff
++ /* MPEG-2 Program Pack */
++ 0x00, 0x00, 0x01, 0xba, /* Prog Pack start code */
++ 0x44, 0x00, 0x0c, 0x66, 0x24, 0x01, /* SCR, SCR Ext, markers */
++ 0x01, 0xd1, 0xd3, /* Mux Rate, markers */
++ 0xfa, 0xff, 0xff, /* Res, Suff cnt, Stuff */
++ /* MPEG-2 Private Stream 1 PES Packet */
++ 0x00, 0x00, 0x01, 0xbd, /* Priv Stream 1 start */
++ 0x00, 0x1a, /* length */
++ 0x84, 0x80, 0x07, /* flags, hdr data len */
++ 0x21, 0x00, 0x5d, 0x63, 0xa7, /* PTS, markers */
++ 0xff, 0xff /* stuffing */
+ };
+ const int sd = sizeof(mpeg_hdr_data); /* start of vbi data */
+ int idx = cx->vbi.frame % CX18_VBI_FRAMES;
+@@ -71,7 +87,9 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp)
+ memcpy(dst + sd + 4, dst + sd + 12, line * 43);
+ size = 4 + ((43 * line + 3) & ~3);
+ } else {
+- memcpy(dst + sd, "cx0", 4);
++ memcpy(dst + sd, "itv0", 4);
++ cpu_to_le32s(&linemask[0]);
++ cpu_to_le32s(&linemask[1]);
+ memcpy(dst + sd + 4, &linemask[0], 8);
+ size = 12 + ((43 * line + 3) & ~3);
+ }
+@@ -86,58 +104,76 @@ static void copy_vbi_data(struct cx18 *cx, int lines, u32 pts_stamp)
+ }
+
+ /* Compress raw VBI format, removes leading SAV codes and surplus space
+- after the field.
+- Returns new compressed size. */
+-static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size)
++ after the frame. Returns new compressed size. */
++static u32 compress_raw_buf(struct cx18 *cx, u8 *buf, u32 size, u32 hdr_size)
+ {
+- u32 line_size = cx->vbi.raw_decoder_line_size;
+- u32 lines = cx->vbi.count;
+- u8 sav1 = cx->vbi.raw_decoder_sav_odd_field;
+- u8 sav2 = cx->vbi.raw_decoder_sav_even_field;
++ u32 line_size = vbi_active_samples;
++ u32 lines = cx->vbi.count * 2;
+ u8 *q = buf;
+ u8 *p;
+ int i;
+
++ /* Skip the header */
++ buf += hdr_size;
++
+ for (i = 0; i < lines; i++) {
+ p = buf + i * line_size;
+
+ /* Look for SAV code */
+ if (p[0] != 0xff || p[1] || p[2] ||
+- (p[3] != sav1 && p[3] != sav2))
++ (p[3] != raw_vbi_sav_rp[0] &&
++ p[3] != raw_vbi_sav_rp[1]))
+ break;
+- memcpy(q, p + 4, line_size - 4);
+- q += line_size - 4;
++ if (i == lines - 1) {
++ /* last line is hdr_size bytes short - extrapolate it */
++ memcpy(q, p + 4, line_size - 4 - hdr_size);
++ q += line_size - 4 - hdr_size;
++ p += line_size - hdr_size - 1;
++ memset(q, (int) *p, hdr_size);
++ } else {
++ memcpy(q, p + 4, line_size - 4);
++ q += line_size - 4;
++ }
+ }
+ return lines * (line_size - 4);
+ }
+
+-
+-/* Compressed VBI format, all found sliced blocks put next to one another
+- Returns new compressed size */
+-static u32 compress_sliced_buf(struct cx18 *cx, u32 line, u8 *buf,
+- u32 size, u8 sav)
++static u32 compress_sliced_buf(struct cx18 *cx, u8 *buf, u32 size,
++ const u32 hdr_size)
+ {
+- u32 line_size = cx->vbi.sliced_decoder_line_size;
+ struct v4l2_decode_vbi_line vbi;
+ int i;
++ u32 line = 0;
++ u32 line_size = cx->is_60hz ? vbi_hblank_samples_60Hz
++ : vbi_hblank_samples_50Hz;
+
+ /* find the first valid line */
+- for (i = 0; i < size; i++, buf++) {
+- if (buf[0] == 0xff && !buf[1] && !buf[2] && buf[3] == sav)
++ for (i = hdr_size, buf += hdr_size; i < size; i++, buf++) {
++ if (buf[0] == 0xff && !buf[1] && !buf[2] &&
++ (buf[3] == sliced_vbi_eav_rp[0] ||
++ buf[3] == sliced_vbi_eav_rp[1]))
+ break;
+ }
+
+- size -= i;
++ /*
++ * The last line is short by hdr_size bytes, but for the remaining
++ * checks against size, we pretend that it is not, by counting the
++ * header bytes we knowingly skipped
++ */
++ size -= (i - hdr_size);
+ if (size < line_size)
+ return line;
++
+ for (i = 0; i < size / line_size; i++) {
+ u8 *p = buf + i * line_size;
+
+- /* Look for SAV code */
+- if (p[0] != 0xff || p[1] || p[2] || p[3] != sav)
++ /* Look for EAV code */
++ if (p[0] != 0xff || p[1] || p[2] ||
++ (p[3] != sliced_vbi_eav_rp[0] &&
++ p[3] != sliced_vbi_eav_rp[1]))
+ continue;
+ vbi.p = p + 4;
+- cx18_av_cmd(cx, VIDIOC_INT_DECODE_VBI_LINE, &vbi);
++ v4l2_subdev_call(cx->sd_av, video, decode_vbi_line, &vbi);
+ if (vbi.type) {
+ cx->vbi.sliced_data[line].id = vbi.type;
+ cx->vbi.sliced_data[line].field = vbi.is_second_field;
+@@ -150,51 +186,56 @@ static u32 compress_sliced_buf(struct cx18 *cx, u32 line, u8 *buf,
+ }
+
+ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
+- u64 pts_stamp, int streamtype)
++ int streamtype)
+ {
++ /*
++ * The CX23418 provides a 12 byte header in its raw VBI buffers to us:
++ * 0x3fffffff [4 bytes of something] [4 byte presentation time stamp]
++ */
++ struct vbi_data_hdr {
++ __be32 magic;
++ __be32 unknown;
++ __be32 pts;
++ } *hdr = (struct vbi_data_hdr *) buf->buf;
++
+ u8 *p = (u8 *) buf->buf;
+ u32 size = buf->bytesused;
++ u32 pts;
+ int lines;
+
+ if (streamtype != CX18_ENC_STREAM_TYPE_VBI)
+ return;
+
++ /*
++ * The CX23418 sends us data that is 32 bit little-endian swapped,
++ * but we want the raw VBI bytes in the order they were in the raster
++ * line. This has a side effect of making the header big endian
++ */
++ cx18_buf_swap(buf);
++
+ /* Raw VBI data */
+ if (cx18_raw_vbi(cx)) {
+- u8 type;
+-
+- cx18_buf_swap(buf);
+-
+- /* Skip 12 bytes of header that gets stuffed in */
+- size -= 12;
+- memcpy(p, &buf->buf[12], size);
+- type = p[3];
+
+- size = buf->bytesused = compress_raw_buf(cx, p, size);
++ size = buf->bytesused =
++ compress_raw_buf(cx, p, size, sizeof(struct vbi_data_hdr));
+
+- /* second field of the frame? */
+- if (type == cx->vbi.raw_decoder_sav_even_field) {
+- /* Dirty hack needed for backwards
+- compatibility of old VBI software. */
+- p += size - 4;
+- memcpy(p, &cx->vbi.frame, 4);
+- cx->vbi.frame++;
+- }
++ /*
++ * Hack needed for compatibility with old VBI software.
++ * Write the frame # at the last 4 bytes of the frame
++ */
++ p += size - 4;
++ memcpy(p, &cx->vbi.frame, 4);
++ cx->vbi.frame++;
+ return;
+ }
+
+ /* Sliced VBI data with data insertion */
+- cx18_buf_swap(buf);
+
+- /* first field */
+- lines = compress_sliced_buf(cx, 0, p, size / 2,
+- cx->vbi.sliced_decoder_sav_odd_field);
+- /* second field */
+- /* experimentation shows that the second half does not always
+- begin at the exact address. So start a bit earlier
+- (hence 32). */
+- lines = compress_sliced_buf(cx, lines, p + size / 2 - 32,
+- size / 2 + 32, cx->vbi.sliced_decoder_sav_even_field);
++ pts = (be32_to_cpu(hdr->magic) == 0x3fffffff) ? be32_to_cpu(hdr->pts)
++ : 0;
++
++ lines = compress_sliced_buf(cx, p, size, sizeof(struct vbi_data_hdr));
++
+ /* always return at least one empty line */
+ if (lines == 0) {
+ cx->vbi.sliced_data[0].id = 0;
+@@ -206,6 +247,6 @@ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
+ memcpy(p, &cx->vbi.sliced_data[0], size);
+
+ if (cx->vbi.insert_mpeg)
+- copy_vbi_data(cx, lines, pts_stamp);
++ copy_vbi_data(cx, lines, pts);
+ cx->vbi.frame++;
+ }
+diff --git a/drivers/media/video/cx18/cx18-vbi.h b/drivers/media/video/cx18/cx18-vbi.h
+index c56ff7d..e7e1ae4 100644
+--- a/drivers/media/video/cx18/cx18-vbi.h
++++ b/drivers/media/video/cx18/cx18-vbi.h
+@@ -22,5 +22,5 @@
+ */
+
+ void cx18_process_vbi_data(struct cx18 *cx, struct cx18_buffer *buf,
+- u64 pts_stamp, int streamtype);
++ int streamtype);
+ int cx18_used_line(struct cx18 *cx, int line, int field);
+diff --git a/drivers/media/video/cx18/cx18-version.h b/drivers/media/video/cx18/cx18-version.h
+index 84c0ff1..bd9bd44 100644
+--- a/drivers/media/video/cx18/cx18-version.h
++++ b/drivers/media/video/cx18/cx18-version.h
+@@ -24,8 +24,8 @@
+
+ #define CX18_DRIVER_NAME "cx18"
+ #define CX18_DRIVER_VERSION_MAJOR 1
+-#define CX18_DRIVER_VERSION_MINOR 0
+-#define CX18_DRIVER_VERSION_PATCHLEVEL 4
++#define CX18_DRIVER_VERSION_MINOR 1
++#define CX18_DRIVER_VERSION_PATCHLEVEL 0
+
+ #define CX18_VERSION __stringify(CX18_DRIVER_VERSION_MAJOR) "." __stringify(CX18_DRIVER_VERSION_MINOR) "." __stringify(CX18_DRIVER_VERSION_PATCHLEVEL)
+ #define CX18_DRIVER_VERSION KERNEL_VERSION(CX18_DRIVER_VERSION_MAJOR, \
+diff --git a/drivers/media/video/cx18/cx18-video.c b/drivers/media/video/cx18/cx18-video.c
+index 2e5c419..6fdaded 100644
+--- a/drivers/media/video/cx18/cx18-video.c
++++ b/drivers/media/video/cx18/cx18-video.c
+@@ -21,7 +21,6 @@
+
+ #include "cx18-driver.h"
+ #include "cx18-video.h"
+-#include "cx18-av-core.h"
+ #include "cx18-cards.h"
+
+ void cx18_video_set_io(struct cx18 *cx)
+@@ -32,7 +31,7 @@ void cx18_video_set_io(struct cx18 *cx)
+
+ route.input = cx->card->video_inputs[inp].video_input;
+ route.output = 0;
+- cx18_av_cmd(cx, VIDIOC_INT_S_VIDEO_ROUTING, &route);
++ v4l2_subdev_call(cx->sd_av, video, s_routing, &route);
+
+ type = cx->card->video_inputs[inp].video_type;
+
+diff --git a/drivers/media/video/cx18/cx23418.h b/drivers/media/video/cx18/cx23418.h
+index 601f3a2..9956abf 100644
+--- a/drivers/media/video/cx18/cx23418.h
++++ b/drivers/media/video/cx18/cx23418.h
+@@ -56,6 +56,22 @@
+ #define APU_CMD_MASK 0x10000000
+ #define APU_CMD_MASK_ACK (APU_CMD_MASK | 0x80000000)
+
++#define CX18_APU_ENCODING_METHOD_MPEG (0 << 28)
++#define CX18_APU_ENCODING_METHOD_AC3 (1 << 28)
++
++/* Description: Command APU to start audio
++ IN[0] - audio parameters (same as CX18_CPU_SET_AUDIO_PARAMETERS?)
++ IN[1] - caller buffer address, or 0
++ ReturnCode - ??? */
++#define CX18_APU_START (APU_CMD_MASK | 0x01)
++
++/* Description: Command APU to stop audio
++ IN[0] - encoding method to stop
++ ReturnCode - ??? */
++#define CX18_APU_STOP (APU_CMD_MASK | 0x02)
++
++/* Description: Command APU to reset the AI
++ ReturnCode - ??? */
+ #define CX18_APU_RESETAI (APU_CMD_MASK | 0x05)
+
+ /* Description: This command indicates that a Memory Descriptor List has been
+diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c
+index cbbe47f..8ded529 100644
+--- a/drivers/media/video/cx2341x.c
++++ b/drivers/media/video/cx2341x.c
+@@ -1,5 +1,5 @@
+ /*
+- * cx2341x - generic code for cx23415/6 based devices
++ * cx2341x - generic code for cx23415/6/8 based devices
+ *
+ * Copyright (C) 2006 Hans Verkuil <hverkuil@xs4all.nl>
+ *
+@@ -30,7 +30,7 @@
+ #include <media/cx2341x.h>
+ #include <media/v4l2-common.h>
+
+-MODULE_DESCRIPTION("cx23415/6 driver");
++MODULE_DESCRIPTION("cx23415/6/8 driver");
+ MODULE_AUTHOR("Hans Verkuil");
+ MODULE_LICENSE("GPL");
+
+@@ -38,6 +38,7 @@ static int debug;
+ module_param(debug, int, 0644);
+ MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
++/* Must be sorted from low to high control ID! */
+ const u32 cx2341x_mpeg_ctrls[] = {
+ V4L2_CID_MPEG_CLASS,
+ V4L2_CID_MPEG_STREAM_TYPE,
+@@ -50,6 +51,7 @@ const u32 cx2341x_mpeg_ctrls[] = {
+ V4L2_CID_MPEG_AUDIO_EMPHASIS,
+ V4L2_CID_MPEG_AUDIO_CRC,
+ V4L2_CID_MPEG_AUDIO_MUTE,
++ V4L2_CID_MPEG_AUDIO_AC3_BITRATE,
+ V4L2_CID_MPEG_VIDEO_ENCODING,
+ V4L2_CID_MPEG_VIDEO_ASPECT,
+ V4L2_CID_MPEG_VIDEO_B_FRAMES,
+@@ -94,6 +96,7 @@ static const struct cx2341x_mpeg_params default_params = {
+ .audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
+ .audio_encoding = V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+ .audio_l2_bitrate = V4L2_MPEG_AUDIO_L2_BITRATE_224K,
++ .audio_ac3_bitrate = V4L2_MPEG_AUDIO_AC3_BITRATE_224K,
+ .audio_mode = V4L2_MPEG_AUDIO_MODE_STEREO,
+ .audio_mode_extension = V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4,
+ .audio_emphasis = V4L2_MPEG_AUDIO_EMPHASIS_NONE,
+@@ -148,6 +151,9 @@ static int cx2341x_get_ctrl(const struct cx2341x_mpeg_params *params,
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+ ctrl->value = params->audio_l2_bitrate;
+ break;
++ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
++ ctrl->value = params->audio_ac3_bitrate;
++ break;
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ ctrl->value = params->audio_mode;
+ break;
+@@ -256,6 +262,12 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, int busy,
+ params->audio_sampling_freq = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
++ if (busy)
++ return -EBUSY;
++ if (params->capabilities & CX2341X_CAP_HAS_AC3)
++ if (ctrl->value != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 &&
++ ctrl->value != V4L2_MPEG_AUDIO_ENCODING_AC3)
++ return -ERANGE;
+ params->audio_encoding = ctrl->value;
+ break;
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+@@ -263,6 +275,13 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params, int busy,
+ return -EBUSY;
+ params->audio_l2_bitrate = ctrl->value;
+ break;
++ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
++ if (busy)
++ return -EBUSY;
++ if (!(params->capabilities & CX2341X_CAP_HAS_AC3))
++ return -EINVAL;
++ params->audio_ac3_bitrate = ctrl->value;
++ break;
+ case V4L2_CID_MPEG_AUDIO_MODE:
+ params->audio_mode = ctrl->value;
+ break;
+@@ -481,29 +500,106 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params,
+ int err;
+
+ switch (qctrl->id) {
++ case V4L2_CID_MPEG_STREAM_TYPE:
++ return v4l2_ctrl_query_fill(qctrl,
++ V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
++ V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, 1,
++ V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
++
++ case V4L2_CID_MPEG_STREAM_VBI_FMT:
++ if (params->capabilities & CX2341X_CAP_HAS_SLICED_VBI)
++ return v4l2_ctrl_query_fill(qctrl,
++ V4L2_MPEG_STREAM_VBI_FMT_NONE,
++ V4L2_MPEG_STREAM_VBI_FMT_IVTV, 1,
++ V4L2_MPEG_STREAM_VBI_FMT_NONE);
++ return cx2341x_ctrl_query_fill(qctrl,
++ V4L2_MPEG_STREAM_VBI_FMT_NONE,
++ V4L2_MPEG_STREAM_VBI_FMT_NONE, 1,
++ default_params.stream_vbi_fmt);
++
++ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
++ return v4l2_ctrl_query_fill(qctrl,
++ V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100,
++ V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 1,
++ V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000);
++
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
++ if (params->capabilities & CX2341X_CAP_HAS_AC3) {
++ /*
++ * The state of L2 & AC3 bitrate controls can change
++ * when this control changes, but v4l2_ctrl_query_fill()
++ * already sets V4L2_CTRL_FLAG_UPDATE for
++ * V4L2_CID_MPEG_AUDIO_ENCODING, so we don't here.
++ */
++ return v4l2_ctrl_query_fill(qctrl,
++ V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
++ V4L2_MPEG_AUDIO_ENCODING_AC3, 1,
++ default_params.audio_encoding);
++ }
++
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2, 1,
+ default_params.audio_encoding);
+
+ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+- return v4l2_ctrl_query_fill(qctrl,
++ err = v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_AUDIO_L2_BITRATE_192K,
+ V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1,
+ default_params.audio_l2_bitrate);
++ if (err)
++ return err;
++ if (params->capabilities & CX2341X_CAP_HAS_AC3 &&
++ params->audio_encoding != V4L2_MPEG_AUDIO_ENCODING_LAYER_2)
++ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
++ return 0;
+
+- case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+- case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+- return -EINVAL;
++ case V4L2_CID_MPEG_AUDIO_MODE:
++ return v4l2_ctrl_query_fill(qctrl,
++ V4L2_MPEG_AUDIO_MODE_STEREO,
++ V4L2_MPEG_AUDIO_MODE_MONO, 1,
++ V4L2_MPEG_AUDIO_MODE_STEREO);
+
+ case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+- err = v4l2_ctrl_query_fill_std(qctrl);
++ err = v4l2_ctrl_query_fill(qctrl,
++ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4,
++ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 1,
++ V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4);
+ if (err == 0 &&
+ params->audio_mode != V4L2_MPEG_AUDIO_MODE_JOINT_STEREO)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return err;
+
++ case V4L2_CID_MPEG_AUDIO_EMPHASIS:
++ return v4l2_ctrl_query_fill(qctrl,
++ V4L2_MPEG_AUDIO_EMPHASIS_NONE,
++ V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 1,
++ V4L2_MPEG_AUDIO_EMPHASIS_NONE);
++
++ case V4L2_CID_MPEG_AUDIO_CRC:
++ return v4l2_ctrl_query_fill(qctrl,
++ V4L2_MPEG_AUDIO_CRC_NONE,
++ V4L2_MPEG_AUDIO_CRC_CRC16, 1,
++ V4L2_MPEG_AUDIO_CRC_NONE);
++
++ case V4L2_CID_MPEG_AUDIO_MUTE:
++ return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0);
++
++ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
++ err = v4l2_ctrl_query_fill(qctrl,
++ V4L2_MPEG_AUDIO_AC3_BITRATE_48K,
++ V4L2_MPEG_AUDIO_AC3_BITRATE_448K, 1,
++ default_params.audio_ac3_bitrate);
++ if (err)
++ return err;
++ if (params->capabilities & CX2341X_CAP_HAS_AC3) {
++ if (params->audio_encoding !=
++ V4L2_MPEG_AUDIO_ENCODING_AC3)
++ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
++ } else
++ qctrl->flags |= V4L2_CTRL_FLAG_DISABLED;
++ return 0;
++
+ case V4L2_CID_MPEG_VIDEO_ENCODING:
+ /* this setting is read-only for the cx2341x since the
+ V4L2_CID_MPEG_STREAM_TYPE really determines the
+@@ -516,32 +612,51 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params,
+ qctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ return err;
+
++ case V4L2_CID_MPEG_VIDEO_ASPECT:
++ return v4l2_ctrl_query_fill(qctrl,
++ V4L2_MPEG_VIDEO_ASPECT_1x1,
++ V4L2_MPEG_VIDEO_ASPECT_221x100, 1,
++ V4L2_MPEG_VIDEO_ASPECT_4x3);
++
++ case V4L2_CID_MPEG_VIDEO_B_FRAMES:
++ return v4l2_ctrl_query_fill(qctrl, 0, 33, 1, 2);
++
++ case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
++ return v4l2_ctrl_query_fill(qctrl, 1, 34, 1,
++ params->is_50hz ? 12 : 15);
++
++ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
++ return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1);
++
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+- err = v4l2_ctrl_query_fill_std(qctrl);
++ err = v4l2_ctrl_query_fill(qctrl,
++ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
++ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1,
++ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+ 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:
++ return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000);
++
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+- err = v4l2_ctrl_query_fill_std(qctrl);
++ err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000);
+ if (err == 0 &&
+ params->video_bitrate_mode ==
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+ qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return err;
+
+- case V4L2_CID_MPEG_STREAM_VBI_FMT:
+- if (params->capabilities & CX2341X_CAP_HAS_SLICED_VBI)
+- return v4l2_ctrl_query_fill_std(qctrl);
+- return cx2341x_ctrl_query_fill(qctrl,
+- V4L2_MPEG_STREAM_VBI_FMT_NONE,
+- V4L2_MPEG_STREAM_VBI_FMT_NONE, 1,
+- default_params.stream_vbi_fmt);
++ case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
++ return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0);
+
+- case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+- return v4l2_ctrl_query_fill(qctrl, 1, 34, 1,
+- params->is_50hz ? 12 : 15);
++ case V4L2_CID_MPEG_VIDEO_MUTE:
++ return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0);
++
++ case V4L2_CID_MPEG_VIDEO_MUTE_YUV: /* Init YUV (really YCbCr) to black */
++ return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080);
+
+ /* CX23415/6 specific */
+ case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
+@@ -643,7 +758,7 @@ int cx2341x_ctrl_query(const struct cx2341x_mpeg_params *params,
+ default_params.stream_insert_nav_packets);
+
+ default:
+- return v4l2_ctrl_query_fill_std(qctrl);
++ return -EINVAL;
+
+ }
+ }
+@@ -671,6 +786,15 @@ const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id)
+ NULL
+ };
+
++ static const char *mpeg_audio_encoding_l2_ac3[] = {
++ "",
++ "MPEG-1/2 Layer II",
++ "",
++ "",
++ "AC-3",
++ NULL
++ };
++
+ static const char *cx2341x_video_spatial_filter_mode_menu[] = {
+ "Manual",
+ "Auto",
+@@ -711,6 +835,9 @@ const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id)
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ return (p->capabilities & CX2341X_CAP_HAS_TS) ?
+ mpeg_stream_type_with_ts : mpeg_stream_type_without_ts;
++ case V4L2_CID_MPEG_AUDIO_ENCODING:
++ return (p->capabilities & CX2341X_CAP_HAS_AC3) ?
++ mpeg_audio_encoding_l2_ac3 : v4l2_ctrl_get_menu(id);
+ case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+ case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+ return NULL;
+@@ -730,16 +857,34 @@ const char **cx2341x_ctrl_get_menu(const struct cx2341x_mpeg_params *p, u32 id)
+ }
+ EXPORT_SYMBOL(cx2341x_ctrl_get_menu);
+
++/* definitions for audio properties bits 29-28 */
++#define CX2341X_AUDIO_ENCODING_METHOD_MPEG 0
++#define CX2341X_AUDIO_ENCODING_METHOD_AC3 1
++#define CX2341X_AUDIO_ENCODING_METHOD_LPCM 2
++
+ static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params)
+ {
+- params->audio_properties = (params->audio_sampling_freq << 0) |
+- ((3 - params->audio_encoding) << 2) |
+- ((1 + params->audio_l2_bitrate) << 4) |
++ params->audio_properties =
++ (params->audio_sampling_freq << 0) |
+ (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_crc << 14);
++
++ if ((params->capabilities & CX2341X_CAP_HAS_AC3) &&
++ params->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3) {
++ params->audio_properties |=
++ /* Not sure if this MPEG Layer II setting is required */
++ ((3 - V4L2_MPEG_AUDIO_ENCODING_LAYER_2) << 2) |
++ (params->audio_ac3_bitrate << 4) |
++ (CX2341X_AUDIO_ENCODING_METHOD_AC3 << 28);
++ } else {
++ /* Assuming MPEG Layer II */
++ params->audio_properties |=
++ ((3 - params->audio_encoding) << 2) |
++ ((1 + params->audio_l2_bitrate) << 4);
++ }
+ }
+
+ int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params, int busy,
+@@ -1022,7 +1167,10 @@ void cx2341x_log_status(const struct cx2341x_mpeg_params *p, const char *prefix)
+ prefix,
+ cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ),
+ cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING),
+- cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_L2_BITRATE),
++ cx2341x_menu_item(p,
++ p->audio_encoding == V4L2_MPEG_AUDIO_ENCODING_AC3
++ ? V4L2_CID_MPEG_AUDIO_AC3_BITRATE
++ : 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)
+diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig
+index 00f1e2e..fd3fc3e 100644
+--- a/drivers/media/video/cx23885/Kconfig
++++ b/drivers/media/video/cx23885/Kconfig
+@@ -15,12 +15,15 @@ config VIDEO_CX23885
+ select DVB_S5H1411 if !DVB_FE_CUSTOMISE
+ select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+ select DVB_ZL10353 if !DVB_FE_CUSTOMISE
+- select DVB_TDA10048 if !DVB_FE_CUSTOMIZE
+- select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_XC2028 if !DVB_FE_CUSTOMIZE
+- select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
+- select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE
+- select MEDIA_TUNER_XC5000 if !DVB_FE_CUSTOMIZE
++ select DVB_TDA10048 if !DVB_FE_CUSTOMISE
++ select DVB_LNBP21 if !DVB_FE_CUSTOMISE
++ select DVB_STV6110 if !DVB_FE_CUSTOMISE
++ select DVB_STV0900 if !DVB_FE_CUSTOMISE
++ select MEDIA_TUNER_MT2131 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_XC2028 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_XC5000 if !MEDIA_TUNER_CUSTOMISE
+ ---help---
+ This is a video4linux driver for Conexant 23885 based
+ TV cards.
+diff --git a/drivers/media/video/cx23885/Makefile b/drivers/media/video/cx23885/Makefile
+index 29c23b4..ab8ea35 100644
+--- a/drivers/media/video/cx23885/Makefile
++++ b/drivers/media/video/cx23885/Makefile
+@@ -1,4 +1,6 @@
+-cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o
++cx23885-objs := cx23885-cards.o cx23885-video.o cx23885-vbi.o \
++ cx23885-core.o cx23885-i2c.o cx23885-dvb.o cx23885-417.o \
++ netup-init.o cimax2.o netup-eeprom.o
+
+ obj-$(CONFIG_VIDEO_CX23885) += cx23885.o
+
+diff --git a/drivers/media/video/cx23885/cimax2.c b/drivers/media/video/cx23885/cimax2.c
+new file mode 100644
+index 0000000..9a65369
+--- /dev/null
++++ b/drivers/media/video/cx23885/cimax2.c
+@@ -0,0 +1,472 @@
++/*
++ * cimax2.c
++ *
++ * CIMax2(R) SP2 driver in conjunction with NetUp Dual DVB-S2 CI card
++ *
++ * Copyright (C) 2009 NetUP Inc.
++ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
++ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
++ *
++ * 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 "cx23885.h"
++#include "dvb_ca_en50221.h"
++/**** Bit definitions for MC417_RWD and MC417_OEN registers ***
++ bits 31-16
+++-----------+
++| Reserved |
+++-----------+
++ bit 15 bit 14 bit 13 bit 12 bit 11 bit 10 bit 9 bit 8
+++-------+-------+-------+-------+-------+-------+-------+-------+
++| WR# | RD# | | ACK# | ADHI | ADLO | CS1# | CS0# |
+++-------+-------+-------+-------+-------+-------+-------+-------+
++ bit 7 bit 6 bit 5 bit 4 bit 3 bit 2 bit 1 bit 0
+++-------+-------+-------+-------+-------+-------+-------+-------+
++| DATA7| DATA6| DATA5| DATA4| DATA3| DATA2| DATA1| DATA0|
+++-------+-------+-------+-------+-------+-------+-------+-------+
++***/
++/* MC417 */
++#define NETUP_DATA 0x000000ff
++#define NETUP_WR 0x00008000
++#define NETUP_RD 0x00004000
++#define NETUP_ACK 0x00001000
++#define NETUP_ADHI 0x00000800
++#define NETUP_ADLO 0x00000400
++#define NETUP_CS1 0x00000200
++#define NETUP_CS0 0x00000100
++#define NETUP_EN_ALL 0x00001000
++#define NETUP_CTRL_OFF (NETUP_CS1 | NETUP_CS0 | NETUP_WR | NETUP_RD)
++#define NETUP_CI_CTL 0x04
++#define NETUP_CI_RD 1
++
++
++static unsigned int ci_dbg;
++module_param(ci_dbg, int, 0644);
++MODULE_PARM_DESC(ci_dbg, "Enable CI debugging");
++
++#define ci_dbg_print(args...) \
++ do { \
++ if (ci_dbg) \
++ printk(KERN_DEBUG args); \
++ } while (0)
++
++/* stores all private variables for communication with CI */
++struct netup_ci_state {
++ struct dvb_ca_en50221 ca;
++ struct mutex ca_mutex;
++ struct i2c_adapter *i2c_adap;
++ u8 ci_i2c_addr;
++ int status;
++ struct work_struct work;
++ void *priv;
++};
++
++struct mutex gpio_mutex;/* Two CiMax's uses same GPIO lines */
++
++int netup_read_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg,
++ u8 *buf, int len)
++{
++ int ret;
++ struct i2c_msg msg[] = {
++ {
++ .addr = addr,
++ .flags = 0,
++ .buf = &reg,
++ .len = 1
++ }, {
++ .addr = addr,
++ .flags = I2C_M_RD,
++ .buf = buf,
++ .len = len
++ }
++ };
++
++ ret = i2c_transfer(i2c_adap, msg, 2);
++
++ if (ret != 2) {
++ ci_dbg_print("%s: i2c read error, Reg = 0x%02x, Status = %d\n",
++ __func__, reg, ret);
++
++ return -1;
++ }
++
++ ci_dbg_print("%s: i2c read Addr=0x%04x, Reg = 0x%02x, data = %02x\n",
++ __func__, addr, reg, buf[0]);
++
++ return 0;
++}
++
++int netup_write_i2c(struct i2c_adapter *i2c_adap, u8 addr, u8 reg,
++ u8 *buf, int len)
++{
++ int ret;
++ u8 buffer[len + 1];
++
++ struct i2c_msg msg = {
++ .addr = addr,
++ .flags = 0,
++ .buf = &buffer[0],
++ .len = len + 1
++ };
++
++ buffer[0] = reg;
++ memcpy(&buffer[1], buf, len);
++
++ ret = i2c_transfer(i2c_adap, &msg, 1);
++
++ if (ret != 1) {
++ ci_dbg_print("%s: i2c write error, Reg=[0x%02x], Status=%d\n",
++ __func__, reg, ret);
++ return -1;
++ }
++
++ return 0;
++}
++
++int netup_ci_get_mem(struct cx23885_dev *dev)
++{
++ int mem;
++ unsigned long timeout = jiffies + msecs_to_jiffies(1);
++
++ for (;;) {
++ mem = cx_read(MC417_RWD);
++ if ((mem & NETUP_ACK) == 0)
++ break;
++ if (time_after(jiffies, timeout))
++ break;
++ udelay(1);
++ }
++
++ cx_set(MC417_RWD, NETUP_CTRL_OFF);
++
++ return mem & 0xff;
++}
++
++int netup_ci_op_cam(struct dvb_ca_en50221 *en50221, int slot,
++ u8 flag, u8 read, int addr, u8 data)
++{
++ struct netup_ci_state *state = en50221->data;
++ struct cx23885_tsport *port = state->priv;
++ struct cx23885_dev *dev = port->dev;
++
++ u8 store;
++ int mem;
++ int ret;
++
++ if (0 != slot)
++ return -EINVAL;
++
++ ret = netup_read_i2c(state->i2c_adap, state->ci_i2c_addr,
++ 0, &store, 1);
++ if (ret != 0)
++ return ret;
++
++ store &= ~0x0c;
++ store |= flag;
++
++ ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
++ 0, &store, 1);
++ if (ret != 0)
++ return ret;
++
++ mutex_lock(&gpio_mutex);
++
++ /* write addr */
++ cx_write(MC417_OEN, NETUP_EN_ALL);
++ cx_write(MC417_RWD, NETUP_CTRL_OFF |
++ NETUP_ADLO | (0xff & addr));
++ cx_clear(MC417_RWD, NETUP_ADLO);
++ cx_write(MC417_RWD, NETUP_CTRL_OFF |
++ NETUP_ADHI | (0xff & (addr >> 8)));
++ cx_clear(MC417_RWD, NETUP_ADHI);
++
++ if (read) /* data in */
++ cx_write(MC417_OEN, NETUP_EN_ALL | NETUP_DATA);
++ else /* data out */
++ cx_write(MC417_RWD, NETUP_CTRL_OFF | data);
++
++ /* choose chip */
++ cx_clear(MC417_RWD,
++ (state->ci_i2c_addr == 0x40) ? NETUP_CS0 : NETUP_CS1);
++ /* read/write */
++ cx_clear(MC417_RWD, (read) ? NETUP_RD : NETUP_WR);
++ mem = netup_ci_get_mem(dev);
++
++ mutex_unlock(&gpio_mutex);
++
++ if (!read)
++ if (mem < 0)
++ return -EREMOTEIO;
++
++ ci_dbg_print("%s: %s: addr=[0x%02x], %s=%x\n", __func__,
++ (read) ? "read" : "write", addr,
++ (flag == NETUP_CI_CTL) ? "ctl" : "mem",
++ (read) ? mem : data);
++
++ if (read)
++ return mem;
++
++ return 0;
++}
++
++int netup_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
++ int slot, int addr)
++{
++ return netup_ci_op_cam(en50221, slot, 0, NETUP_CI_RD, addr, 0);
++}
++
++int netup_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
++ int slot, int addr, u8 data)
++{
++ return netup_ci_op_cam(en50221, slot, 0, 0, addr, data);
++}
++
++int netup_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221, int slot, u8 addr)
++{
++ return netup_ci_op_cam(en50221, slot, NETUP_CI_CTL,
++ NETUP_CI_RD, addr, 0);
++}
++
++int netup_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221, int slot,
++ u8 addr, u8 data)
++{
++ return netup_ci_op_cam(en50221, slot, NETUP_CI_CTL, 0, addr, data);
++}
++
++int netup_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot)
++{
++ struct netup_ci_state *state = en50221->data;
++ u8 buf = 0x80;
++ int ret;
++
++ if (0 != slot)
++ return -EINVAL;
++
++ udelay(500);
++ ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
++ 0, &buf, 1);
++
++ if (ret != 0)
++ return ret;
++
++ udelay(500);
++
++ buf = 0x00;
++ ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
++ 0, &buf, 1);
++
++ msleep(1000);
++ dvb_ca_en50221_camready_irq(&state->ca, 0);
++
++ return 0;
++
++}
++
++int netup_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot)
++{
++ /* not implemented */
++ return 0;
++}
++
++int netup_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot)
++{
++ struct netup_ci_state *state = en50221->data;
++ u8 buf = 0x60;
++
++ if (0 != slot)
++ return -EINVAL;
++
++ return netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
++ 0, &buf, 1);
++}
++
++/* work handler */
++static void netup_read_ci_status(struct work_struct *work)
++{
++ struct netup_ci_state *state =
++ container_of(work, struct netup_ci_state, work);
++ u8 buf[33];
++ int ret;
++
++ ret = netup_read_i2c(state->i2c_adap, state->ci_i2c_addr,
++ 0, &buf[0], 33);
++
++ if (ret != 0)
++ return;
++
++ ci_dbg_print("%s: Slot Status Addr=[0x%04x], Reg=[0x%02x], data=%02x, "
++ "TS config = %02x\n", __func__, state->ci_i2c_addr, 0, buf[0],
++ buf[32]);
++
++ if (buf[0] && 1)
++ state->status = DVB_CA_EN50221_POLL_CAM_PRESENT |
++ DVB_CA_EN50221_POLL_CAM_READY;
++ else
++ state->status = 0;
++}
++
++/* CI irq handler */
++int netup_ci_slot_status(struct cx23885_dev *dev, u32 pci_status)
++{
++ struct cx23885_tsport *port = NULL;
++ struct netup_ci_state *state = NULL;
++
++ if (pci_status & PCI_MSK_GPIO0)
++ port = &dev->ts1;
++ else if (pci_status & PCI_MSK_GPIO1)
++ port = &dev->ts2;
++ else /* who calls ? */
++ return 0;
++
++ state = port->port_priv;
++
++ schedule_work(&state->work);
++
++ return 1;
++}
++
++int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221, int slot, int open)
++{
++ struct netup_ci_state *state = en50221->data;
++
++ if (0 != slot)
++ return -EINVAL;
++
++ return state->status;
++}
++
++int netup_ci_init(struct cx23885_tsport *port)
++{
++ struct netup_ci_state *state;
++ u8 cimax_init[34] = {
++ 0x00, /* module A control*/
++ 0x00, /* auto select mask high A */
++ 0x00, /* auto select mask low A */
++ 0x00, /* auto select pattern high A */
++ 0x00, /* auto select pattern low A */
++ 0x44, /* memory access time A */
++ 0x00, /* invert input A */
++ 0x00, /* RFU */
++ 0x00, /* RFU */
++ 0x00, /* module B control*/
++ 0x00, /* auto select mask high B */
++ 0x00, /* auto select mask low B */
++ 0x00, /* auto select pattern high B */
++ 0x00, /* auto select pattern low B */
++ 0x44, /* memory access time B */
++ 0x00, /* invert input B */
++ 0x00, /* RFU */
++ 0x00, /* RFU */
++ 0x00, /* auto select mask high Ext */
++ 0x00, /* auto select mask low Ext */
++ 0x00, /* auto select pattern high Ext */
++ 0x00, /* auto select pattern low Ext */
++ 0x00, /* RFU */
++ 0x02, /* destination - module A */
++ 0x01, /* power on (use it like store place) */
++ 0x00, /* RFU */
++ 0x00, /* int status read only */
++ 0x01, /* all int unmasked */
++ 0x04, /* int config */
++ 0x00, /* USCG1 */
++ 0x04, /* ack active low */
++ 0x00, /* LOCK = 0 */
++ 0x33, /* serial mode, rising in, rising out, MSB first*/
++ 0x31, /* syncronization */
++ };
++ int ret;
++
++ ci_dbg_print("%s\n", __func__);
++ state = kzalloc(sizeof(struct netup_ci_state), GFP_KERNEL);
++ if (!state) {
++ ci_dbg_print("%s: Unable create CI structure!\n", __func__);
++ ret = -ENOMEM;
++ goto err;
++ }
++
++ port->port_priv = state;
++
++ switch (port->nr) {
++ case 1:
++ state->ci_i2c_addr = 0x40;
++ mutex_init(&gpio_mutex);
++ break;
++ case 2:
++ state->ci_i2c_addr = 0x41;
++ break;
++ }
++
++ state->i2c_adap = &port->dev->i2c_bus[0].i2c_adap;
++ state->ca.owner = THIS_MODULE;
++ state->ca.read_attribute_mem = netup_ci_read_attribute_mem;
++ state->ca.write_attribute_mem = netup_ci_write_attribute_mem;
++ state->ca.read_cam_control = netup_ci_read_cam_ctl;
++ state->ca.write_cam_control = netup_ci_write_cam_ctl;
++ state->ca.slot_reset = netup_ci_slot_reset;
++ state->ca.slot_shutdown = netup_ci_slot_shutdown;
++ state->ca.slot_ts_enable = netup_ci_slot_ts_ctl;
++ state->ca.poll_slot_status = netup_poll_ci_slot_status;
++ state->ca.data = state;
++ state->priv = port;
++
++ ret = netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
++ 0, &cimax_init[0], 34);
++ /* lock registers */
++ ret |= netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
++ 0x1f, &cimax_init[0x18], 1);
++ /* power on slots */
++ ret |= netup_write_i2c(state->i2c_adap, state->ci_i2c_addr,
++ 0x18, &cimax_init[0x18], 1);
++
++ if (0 != ret)
++ goto err;
++
++ ret = dvb_ca_en50221_init(&port->frontends.adapter,
++ &state->ca,
++ /* flags */ 0,
++ /* n_slots */ 1);
++ if (0 != ret)
++ goto err;
++
++ INIT_WORK(&state->work, netup_read_ci_status);
++
++ ci_dbg_print("%s: CI initialized!\n", __func__);
++
++ return 0;
++err:
++ ci_dbg_print("%s: Cannot initialize CI: Error %d.\n", __func__, ret);
++ kfree(state);
++ return ret;
++}
++
++void netup_ci_exit(struct cx23885_tsport *port)
++{
++ struct netup_ci_state *state;
++
++ if (NULL == port)
++ return;
++
++ state = (struct netup_ci_state *)port->port_priv;
++ if (NULL == state)
++ return;
++
++ if (NULL == state->ca.data)
++ return;
++
++ dvb_ca_en50221_release(&state->ca);
++ kfree(state);
++}
+diff --git a/drivers/media/video/cx23885/cimax2.h b/drivers/media/video/cx23885/cimax2.h
+new file mode 100644
+index 0000000..518744a
+--- /dev/null
++++ b/drivers/media/video/cx23885/cimax2.h
+@@ -0,0 +1,47 @@
++/*
++ * cimax2.h
++ *
++ * CIMax(R) SP2 driver in conjunction with NetUp Dual DVB-S2 CI card
++ *
++ * Copyright (C) 2009 NetUP Inc.
++ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
++ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
++ *
++ * 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 CIMAX2_H
++#define CIMAX2_H
++#include "dvb_ca_en50221.h"
++
++extern int netup_ci_read_attribute_mem(struct dvb_ca_en50221 *en50221,
++ int slot, int addr);
++extern int netup_ci_write_attribute_mem(struct dvb_ca_en50221 *en50221,
++ int slot, int addr, u8 data);
++extern int netup_ci_read_cam_ctl(struct dvb_ca_en50221 *en50221,
++ int slot, u8 addr);
++extern int netup_ci_write_cam_ctl(struct dvb_ca_en50221 *en50221,
++ int slot, u8 addr, u8 data);
++extern int netup_ci_slot_reset(struct dvb_ca_en50221 *en50221, int slot);
++extern int netup_ci_slot_shutdown(struct dvb_ca_en50221 *en50221, int slot);
++extern int netup_ci_slot_ts_ctl(struct dvb_ca_en50221 *en50221, int slot);
++extern int netup_ci_slot_status(struct cx23885_dev *dev, u32 pci_status);
++extern int netup_poll_ci_slot_status(struct dvb_ca_en50221 *en50221,
++ int slot, int open);
++extern int netup_ci_init(struct cx23885_tsport *port);
++extern void netup_ci_exit(struct cx23885_tsport *port);
++
++#endif
+diff --git a/drivers/media/video/cx23885/cx23885-417.c b/drivers/media/video/cx23885/cx23885-417.c
+index bfe2584..6f5df90 100644
+--- a/drivers/media/video/cx23885/cx23885-417.c
++++ b/drivers/media/video/cx23885/cx23885-417.c
+@@ -896,7 +896,7 @@ static int cx23885_load_firmware(struct cx23885_dev *dev)
+ if (retval != 0) {
+ printk(KERN_ERR
+ "ERROR: Hotplug firmware request failed (%s).\n",
+- CX2341X_FIRM_ENC_FILENAME);
++ CX23885_FIRM_IMAGE_NAME);
+ printk(KERN_ERR "Please fix your hotplug setup, the board will "
+ "not work without firmware loaded!\n");
+ return -1;
+@@ -1198,21 +1198,16 @@ static int vidioc_enum_input(struct file *file, void *priv,
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_input *input;
+- unsigned int n;
++ int n;
+
+- n = i->index;
+-
+- if (n >= 4)
++ if (i->index >= 4)
+ return -EINVAL;
+
+- input = &cx23885_boards[dev->board].input[n];
++ input = &cx23885_boards[dev->board].input[i->index];
+
+ if (input->type == 0)
+ return -EINVAL;
+
+- memset(i, 0, sizeof(*i));
+- i->index = n;
+-
+ /* FIXME
+ * strcpy(i->name, input->name); */
+ strcpy(i->name, "unset");
+@@ -1255,10 +1250,8 @@ static int vidioc_g_tuner(struct file *file, void *priv,
+ return -EINVAL;
+ if (0 != t->index)
+ return -EINVAL;
+- memset(t, 0, sizeof(*t));
+ strcpy(t->name, "Television");
+- cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_TUNER, t);
+- cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_TUNER, t);
++ call_all(dev, tuner, g_tuner, t);
+
+ dprintk(1, "VIDIOC_G_TUNER: tuner type %d\n", t->type);
+
+@@ -1275,7 +1268,7 @@ static int vidioc_s_tuner(struct file *file, void *priv,
+ return -EINVAL;
+
+ /* Update the A/V core */
+- cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_TUNER, t);
++ call_all(dev, tuner, s_tuner, t);
+
+ return 0;
+ }
+@@ -1286,14 +1279,12 @@ static int vidioc_g_frequency(struct file *file, void *priv,
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+- memset(f, 0, sizeof(*f));
+ if (UNSET == dev->tuner_type)
+ return -EINVAL;
+ f->type = V4L2_TUNER_ANALOG_TV;
+ f->frequency = dev->freq;
+
+- /* Assumption that tuner is always on bus 1 */
+- cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_G_FREQUENCY, f);
++ call_all(dev, tuner, g_frequency, f);
+
+ return 0;
+ }
+@@ -1320,8 +1311,7 @@ static int vidioc_s_frequency(struct file *file, void *priv,
+ return -EINVAL;
+ dev->freq = f->frequency;
+
+- /* Assumption that tuner is always on bus 1 */
+- cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_FREQUENCY, f);
++ call_all(dev, tuner, s_frequency, f);
+
+ cx23885_initialize_codec(dev);
+
+@@ -1335,7 +1325,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct cx23885_dev *dev = fh->dev;
+
+ /* Update the A/V core */
+- cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_CTRL, ctl);
++ call_all(dev, core, s_ctrl, ctl);
+ return 0;
+ }
+
+@@ -1346,7 +1336,6 @@ static int vidioc_querycap(struct file *file, void *priv,
+ struct cx23885_dev *dev = fh->dev;
+ struct cx23885_tsport *tsport = &dev->ts1;
+
+- memset(cap, 0, sizeof(*cap));
+ strcpy(cap->driver, dev->name);
+ strlcpy(cap->card, cx23885_boards[tsport->dev->board].name,
+ sizeof(cap->card));
+@@ -1366,16 +1355,10 @@ static int vidioc_querycap(struct file *file, void *priv,
+ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ struct v4l2_fmtdesc *f)
+ {
+- int index;
+-
+- index = f->index;
+- if (index != 0)
++ if (f->index != 0)
+ return -EINVAL;
+
+- memset(f, 0, sizeof(*f));
+- f->index = index;
+ strlcpy(f->description, "MPEG", sizeof(f->description));
+- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->pixelformat = V4L2_PIX_FMT_MPEG;
+
+ return 0;
+@@ -1387,8 +1370,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+- memset(f, 0, sizeof(*f));
+- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+@@ -1408,12 +1389,10 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+ dev->ts1.ts_packet_size * dev->ts1.ts_packet_count;
+- f->fmt.pix.sizeimage =
+ f->fmt.pix.colorspace = 0;
+ dprintk(1, "VIDIOC_TRY_FMT: w: %d, h: %d, f: %d\n",
+ dev->ts1.width, dev->ts1.height, fh->mpegq.field);
+@@ -1426,7 +1405,6 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ struct cx23885_fh *fh = file->private_data;
+ struct cx23885_dev *dev = fh->dev;
+
+- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage =
+@@ -1543,12 +1521,7 @@ static int vidioc_log_status(struct file *file, void *priv)
+ printk(KERN_INFO
+ "%s/2: ============ START LOG STATUS ============\n",
+ dev->name);
+- cx23885_call_i2c_clients(&dev->i2c_bus[0], VIDIOC_LOG_STATUS,
+- NULL);
+- cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_LOG_STATUS,
+- NULL);
+- cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_LOG_STATUS,
+- NULL);
++ call_all(dev, core, log_status);
+ cx2341x_log_status(&dev->mpeg_params, name);
+ printk(KERN_INFO
+ "%s/2: ============= END LOG STATUS =============\n",
+diff --git a/drivers/media/video/cx23885/cx23885-cards.c b/drivers/media/video/cx23885/cx23885-cards.c
+index caa098b..5e4b7e7 100644
+--- a/drivers/media/video/cx23885/cx23885-cards.c
++++ b/drivers/media/video/cx23885/cx23885-cards.c
+@@ -27,6 +27,7 @@
+
+ #include "cx23885.h"
+ #include "tuner-xc2028.h"
++#include "netup-init.h"
+
+ /* ------------------------------------------------------------------ */
+ /* board config info */
+@@ -162,6 +163,24 @@ struct cx23885_board cx23885_boards[] = {
+ .name = "Compro VideoMate E650F",
+ .portc = CX23885_MPEG_DVB,
+ },
++ [CX23885_BOARD_TBS_6920] = {
++ .name = "TurboSight TBS 6920",
++ .portb = CX23885_MPEG_DVB,
++ },
++ [CX23885_BOARD_TEVII_S470] = {
++ .name = "TeVii S470",
++ .portb = CX23885_MPEG_DVB,
++ },
++ [CX23885_BOARD_DVBWORLD_2005] = {
++ .name = "DVBWorld DVB-S2 2005",
++ .portb = CX23885_MPEG_DVB,
++ },
++ [CX23885_BOARD_NETUP_DUAL_DVBS2_CI] = {
++ .cimax = 1,
++ .name = "NetUP Dual DVB-S2 CI",
++ .portb = CX23885_MPEG_DVB,
++ .portc = CX23885_MPEG_DVB,
++ },
+ };
+ const unsigned int cx23885_bcount = ARRAY_SIZE(cx23885_boards);
+
+@@ -245,6 +264,22 @@ struct cx23885_subid cx23885_subids[] = {
+ .subvendor = 0x185b,
+ .subdevice = 0xe800,
+ .card = CX23885_BOARD_COMPRO_VIDEOMATE_E650F,
++ }, {
++ .subvendor = 0x6920,
++ .subdevice = 0x8888,
++ .card = CX23885_BOARD_TBS_6920,
++ }, {
++ .subvendor = 0xd470,
++ .subdevice = 0x9022,
++ .card = CX23885_BOARD_TEVII_S470,
++ }, {
++ .subvendor = 0x0001,
++ .subdevice = 0x2005,
++ .card = CX23885_BOARD_DVBWORLD_2005,
++ }, {
++ .subvendor = 0x1b55,
++ .subdevice = 0x2a2c,
++ .card = CX23885_BOARD_NETUP_DUAL_DVBS2_CI,
+ },
+ };
+ const unsigned int cx23885_idcount = ARRAY_SIZE(cx23885_subids);
+@@ -552,6 +587,38 @@ void cx23885_gpio_setup(struct cx23885_dev *dev)
+ mdelay(20);
+ cx_set(GP0_IO, 0x00040004);
+ break;
++ case CX23885_BOARD_TBS_6920:
++ case CX23885_BOARD_TEVII_S470:
++ cx_write(MC417_CTL, 0x00000036);
++ cx_write(MC417_OEN, 0x00001000);
++ cx_write(MC417_RWD, 0x00001800);
++ break;
++ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
++ /* GPIO-0 INTA from CiMax1
++ GPIO-1 INTB from CiMax2
++ GPIO-2 reset chips
++ GPIO-3 to GPIO-10 data/addr for CA
++ GPIO-11 ~CS0 to CiMax1
++ GPIO-12 ~CS1 to CiMax2
++ GPIO-13 ADL0 load LSB addr
++ GPIO-14 ADL1 load MSB addr
++ GPIO-15 ~RDY from CiMax
++ GPIO-17 ~RD to CiMax
++ GPIO-18 ~WR to CiMax
++ */
++ cx_set(GP0_IO, 0x00040000); /* GPIO as out */
++ /* GPIO1 and GPIO2 as INTA and INTB from CiMaxes, reset low */
++ cx_clear(GP0_IO, 0x00030004);
++ mdelay(100);/* reset delay */
++ cx_set(GP0_IO, 0x00040004); /* GPIO as out, reset high */
++ cx_write(MC417_CTL, 0x00000037);/* enable GPIO3-18 pins */
++ /* GPIO-15 IN as ~ACK, rest as OUT */
++ cx_write(MC417_OEN, 0x00001000);
++ /* ~RD, ~WR high; ADL0, ADL1 low; ~CS0, ~CS1 high */
++ cx_write(MC417_RWD, 0x0000c300);
++ /* enable irq */
++ cx_write(GPIO_ISM, 0x00000000);/* INTERRUPTS active low*/
++ break;
+ }
+ }
+
+@@ -632,6 +699,21 @@ void cx23885_card_setup(struct cx23885_dev *dev)
+ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
+ ts2->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
+ break;
++ case CX23885_BOARD_TEVII_S470:
++ case CX23885_BOARD_TBS_6920:
++ case CX23885_BOARD_DVBWORLD_2005:
++ ts1->gen_ctrl_val = 0x5; /* Parallel */
++ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
++ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
++ break;
++ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
++ ts1->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
++ ts1->ts_clk_en_val = 0x1; /* Enable TS_CLK */
++ ts1->src_sel_val = CX23885_SRC_SEL_PARALLEL_MPEG_VIDEO;
++ ts2->gen_ctrl_val = 0xc; /* Serial bus + punctured clock */
++ ts2->ts_clk_en_val = 0x1; /* Enable TS_CLK */
++ ts2->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:
+@@ -656,7 +738,17 @@ void cx23885_card_setup(struct cx23885_dev *dev)
+ case CX23885_BOARD_HAUPPAUGE_HVR1700:
+ case CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H:
+ case CX23885_BOARD_COMPRO_VIDEOMATE_E650F:
+- request_module("cx25840");
++ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
++ dev->sd_cx25840 = v4l2_i2c_new_subdev(&dev->i2c_bus[2].i2c_adap,
++ "cx25840", "cx25840", 0x88 >> 1);
++ v4l2_subdev_call(dev->sd_cx25840, core, init, 0);
++ break;
++ }
++
++ /* AUX-PLL 27MHz CLK */
++ switch (dev->board) {
++ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
++ netup_initialize(dev);
+ break;
+ }
+ }
+diff --git a/drivers/media/video/cx23885/cx23885-core.c b/drivers/media/video/cx23885/cx23885-core.c
+index 8f6fb2a..dc7fff2 100644
+--- a/drivers/media/video/cx23885/cx23885-core.c
++++ b/drivers/media/video/cx23885/cx23885-core.c
+@@ -31,6 +31,7 @@
+ #include <asm/div64.h>
+
+ #include "cx23885.h"
++#include "cimax2.h"
+
+ MODULE_DESCRIPTION("Driver for cx23885 based TV cards");
+ MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
+@@ -791,6 +792,8 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
+ dev->pci_bus = dev->pci->bus->number;
+ dev->pci_slot = PCI_SLOT(dev->pci->devfn);
+ dev->pci_irqmask = 0x001f00;
++ if (cx23885_boards[dev->board].cimax > 0)
++ dev->pci_irqmask |= 0x01800000; /* for CiMaxes */
+
+ /* External Master 1 Bus */
+ dev->i2c_bus[0].nr = 0;
+@@ -872,7 +875,7 @@ static int cx23885_dev_setup(struct cx23885_dev *dev)
+ cx23885_i2c_register(&dev->i2c_bus[1]);
+ cx23885_i2c_register(&dev->i2c_bus[2]);
+ cx23885_card_setup(dev);
+- cx23885_call_i2c_clients(&dev->i2c_bus[0], TUNER_SET_STANDBY, NULL);
++ call_all(dev, core, s_standby, 0);
+ cx23885_ir_init(dev);
+
+ if (cx23885_boards[dev->board].porta == CX23885_ANALOG_VIDEO) {
+@@ -1643,7 +1646,9 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
+ (pci_status & PCI_MSK_VID_B) ||
+ (pci_status & PCI_MSK_VID_A) ||
+ (pci_status & PCI_MSK_AUD_INT) ||
+- (pci_status & PCI_MSK_AUD_EXT)) {
++ (pci_status & PCI_MSK_AUD_EXT) ||
++ (pci_status & PCI_MSK_GPIO0) ||
++ (pci_status & PCI_MSK_GPIO1)) {
+
+ if (pci_status & PCI_MSK_RISC_RD)
+ dprintk(7, " (PCI_MSK_RISC_RD 0x%08x)\n",
+@@ -1685,8 +1690,20 @@ static irqreturn_t cx23885_irq(int irq, void *dev_id)
+ dprintk(7, " (PCI_MSK_AUD_EXT 0x%08x)\n",
+ PCI_MSK_AUD_EXT);
+
++ if (pci_status & PCI_MSK_GPIO0)
++ dprintk(7, " (PCI_MSK_GPIO0 0x%08x)\n",
++ PCI_MSK_GPIO0);
++
++ if (pci_status & PCI_MSK_GPIO1)
++ dprintk(7, " (PCI_MSK_GPIO1 0x%08x)\n",
++ PCI_MSK_GPIO1);
+ }
+
++ if (cx23885_boards[dev->board].cimax > 0 &&
++ ((pci_status & PCI_MSK_GPIO0) || (pci_status & PCI_MSK_GPIO1)))
++ /* handled += cx23885_irq_gpio(dev, pci_status); */
++ handled += netup_ci_slot_status(dev, pci_status);
++
+ if (ts1_status) {
+ if (cx23885_boards[dev->board].portb == CX23885_MPEG_DVB)
+ handled += cx23885_irq_ts(ts1, ts1_status);
+@@ -1722,16 +1739,20 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
+ if (NULL == dev)
+ return -ENOMEM;
+
++ err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev);
++ if (err < 0)
++ goto fail_free;
++
+ /* pci init */
+ dev->pci = pci_dev;
+ if (pci_enable_device(pci_dev)) {
+ err = -EIO;
+- goto fail_free;
++ goto fail_unreg;
+ }
+
+ if (cx23885_dev_setup(dev) < 0) {
+ err = -EINVAL;
+- goto fail_free;
++ goto fail_unreg;
+ }
+
+ /* print pci info */
+@@ -1758,11 +1779,18 @@ static int __devinit cx23885_initdev(struct pci_dev *pci_dev,
+ goto fail_irq;
+ }
+
+- pci_set_drvdata(pci_dev, dev);
++ switch (dev->board) {
++ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
++ cx_set(PCI_INT_MSK, 0x01800000); /* for NetUP */
++ break;
++ }
++
+ return 0;
+
+ fail_irq:
+ cx23885_dev_unregister(dev);
++fail_unreg:
++ v4l2_device_unregister(&dev->v4l2_dev);
+ fail_free:
+ kfree(dev);
+ return err;
+@@ -1770,7 +1798,8 @@ fail_free:
+
+ static void __devexit cx23885_finidev(struct pci_dev *pci_dev)
+ {
+- struct cx23885_dev *dev = pci_get_drvdata(pci_dev);
++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
++ struct cx23885_dev *dev = to_cx23885(v4l2_dev);
+
+ cx23885_shutdown(dev);
+
+@@ -1778,13 +1807,13 @@ static void __devexit cx23885_finidev(struct pci_dev *pci_dev)
+
+ /* unregister stuff */
+ free_irq(pci_dev->irq, dev);
+- pci_set_drvdata(pci_dev, NULL);
+
+ mutex_lock(&devlist);
+ list_del(&dev->devlist);
+ mutex_unlock(&devlist);
+
+ cx23885_dev_unregister(dev);
++ v4l2_device_unregister(v4l2_dev);
+ kfree(dev);
+ }
+
+diff --git a/drivers/media/video/cx23885/cx23885-dvb.c b/drivers/media/video/cx23885/cx23885-dvb.c
+index 1c45412..d43c743 100644
+--- a/drivers/media/video/cx23885/cx23885-dvb.c
++++ b/drivers/media/video/cx23885/cx23885-dvb.c
+@@ -30,6 +30,7 @@
+ #include "cx23885.h"
+ #include <media/v4l2-common.h>
+
++#include "dvb_ca_en50221.h"
+ #include "s5h1409.h"
+ #include "s5h1411.h"
+ #include "mt2131.h"
+@@ -43,6 +44,13 @@
+ #include "dib7000p.h"
+ #include "dibx000_common.h"
+ #include "zl10353.h"
++#include "stv0900.h"
++#include "stv6110.h"
++#include "lnbh24.h"
++#include "cx24116.h"
++#include "cimax2.h"
++#include "netup-eeprom.h"
++#include "netup-init.h"
+
+ static unsigned int debug;
+
+@@ -308,11 +316,63 @@ static struct zl10353_config dvico_fusionhdtv_xc3028 = {
+ .no_tuner = 1,
+ };
+
++static struct stv0900_config netup_stv0900_config = {
++ .demod_address = 0x68,
++ .xtal = 27000000,
++ .clkmode = 3,/* 0-CLKI, 2-XTALI, else AUTO */
++ .diseqc_mode = 2,/* 2/3 PWM */
++ .path1_mode = 2,/*Serial continues clock */
++ .path2_mode = 2,/*Serial continues clock */
++ .tun1_maddress = 0,/* 0x60 */
++ .tun2_maddress = 3,/* 0x63 */
++ .tun1_adc = 1,/* 1 Vpp */
++ .tun2_adc = 1,/* 1 Vpp */
++};
++
++static struct stv6110_config netup_stv6110_tunerconfig_a = {
++ .i2c_address = 0x60,
++ .mclk = 27000000,
++ .iq_wiring = 0,
++};
++
++static struct stv6110_config netup_stv6110_tunerconfig_b = {
++ .i2c_address = 0x63,
++ .mclk = 27000000,
++ .iq_wiring = 1,
++};
++
++static int tbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
++{
++ struct cx23885_tsport *port = fe->dvb->priv;
++ struct cx23885_dev *dev = port->dev;
++
++ if (voltage == SEC_VOLTAGE_18)
++ cx_write(MC417_RWD, 0x00001e00);/* GPIO-13 high */
++ else if (voltage == SEC_VOLTAGE_13)
++ cx_write(MC417_RWD, 0x00001a00);/* GPIO-13 low */
++ else
++ cx_write(MC417_RWD, 0x00001800);/* GPIO-12 low */
++ return 0;
++}
++
++static struct cx24116_config tbs_cx24116_config = {
++ .demod_address = 0x05,
++};
++
++static struct cx24116_config tevii_cx24116_config = {
++ .demod_address = 0x55,
++};
++
++static struct cx24116_config dvbworld_cx24116_config = {
++ .demod_address = 0x05,
++};
++
+ static int dvb_register(struct cx23885_tsport *port)
+ {
+ struct cx23885_dev *dev = port->dev;
+ struct cx23885_i2c *i2c_bus = NULL;
+ struct videobuf_dvb_frontend *fe0;
++ int ret;
+
+ /* Get the first frontend */
+ fe0 = videobuf_dvb_get_frontend(&port->frontends, 1);
+@@ -526,6 +586,78 @@ static int dvb_register(struct cx23885_tsport *port)
+ fe->ops.tuner_ops.set_config(fe, &ctl);
+ }
+ break;
++ case CX23885_BOARD_TBS_6920:
++ i2c_bus = &dev->i2c_bus[0];
++
++ fe0->dvb.frontend = dvb_attach(cx24116_attach,
++ &tbs_cx24116_config,
++ &i2c_bus->i2c_adap);
++ if (fe0->dvb.frontend != NULL)
++ fe0->dvb.frontend->ops.set_voltage = tbs_set_voltage;
++
++ break;
++ case CX23885_BOARD_TEVII_S470:
++ i2c_bus = &dev->i2c_bus[1];
++
++ fe0->dvb.frontend = dvb_attach(cx24116_attach,
++ &tevii_cx24116_config,
++ &i2c_bus->i2c_adap);
++ if (fe0->dvb.frontend != NULL)
++ fe0->dvb.frontend->ops.set_voltage = tbs_set_voltage;
++
++ break;
++ case CX23885_BOARD_DVBWORLD_2005:
++ i2c_bus = &dev->i2c_bus[1];
++
++ fe0->dvb.frontend = dvb_attach(cx24116_attach,
++ &dvbworld_cx24116_config,
++ &i2c_bus->i2c_adap);
++ break;
++ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
++ i2c_bus = &dev->i2c_bus[0];
++ switch (port->nr) {
++ /* port B */
++ case 1:
++ fe0->dvb.frontend = dvb_attach(stv0900_attach,
++ &netup_stv0900_config,
++ &i2c_bus->i2c_adap, 0);
++ if (fe0->dvb.frontend != NULL) {
++ if (dvb_attach(stv6110_attach,
++ fe0->dvb.frontend,
++ &netup_stv6110_tunerconfig_a,
++ &i2c_bus->i2c_adap)) {
++ if (!dvb_attach(lnbh24_attach,
++ fe0->dvb.frontend,
++ &i2c_bus->i2c_adap,
++ LNBH24_PCL, 0, 0x09))
++ printk(KERN_ERR
++ "No LNBH24 found!\n");
++
++ }
++ }
++ break;
++ /* port C */
++ case 2:
++ fe0->dvb.frontend = dvb_attach(stv0900_attach,
++ &netup_stv0900_config,
++ &i2c_bus->i2c_adap, 1);
++ if (fe0->dvb.frontend != NULL) {
++ if (dvb_attach(stv6110_attach,
++ fe0->dvb.frontend,
++ &netup_stv6110_tunerconfig_b,
++ &i2c_bus->i2c_adap)) {
++ if (!dvb_attach(lnbh24_attach,
++ fe0->dvb.frontend,
++ &i2c_bus->i2c_adap,
++ LNBH24_PCL, 0, 0x0a))
++ printk(KERN_ERR
++ "No LNBH24 found!\n");
++
++ }
++ }
++ break;
++ }
++ break;
+ default:
+ printk(KERN_INFO "%s: The frontend of your DVB/ATSC card "
+ " isn't supported yet\n",
+@@ -541,15 +673,39 @@ static int dvb_register(struct cx23885_tsport *port)
+ fe0->dvb.frontend->callback = cx23885_tuner_callback;
+
+ /* Put the analog decoder in standby to keep it quiet */
+- cx23885_call_i2c_clients(i2c_bus, TUNER_SET_STANDBY, NULL);
++ call_all(dev, core, s_standby, 0);
+
+ if (fe0->dvb.frontend->ops.analog_ops.standby)
+ fe0->dvb.frontend->ops.analog_ops.standby(fe0->dvb.frontend);
+
+ /* register everything */
+- return videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port,
++ ret = videobuf_dvb_register_bus(&port->frontends, THIS_MODULE, port,
+ &dev->pci->dev, adapter_nr, 0);
+
++ /* init CI & MAC */
++ switch (dev->board) {
++ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI: {
++ static struct netup_card_info cinfo;
++
++ netup_get_card_info(&dev->i2c_bus[0].i2c_adap, &cinfo);
++ memcpy(port->frontends.adapter.proposed_mac,
++ cinfo.port[port->nr - 1].mac, 6);
++ printk(KERN_INFO "NetUP Dual DVB-S2 CI card port%d MAC="
++ "%02X:%02X:%02X:%02X:%02X:%02X\n",
++ port->nr,
++ port->frontends.adapter.proposed_mac[0],
++ port->frontends.adapter.proposed_mac[1],
++ port->frontends.adapter.proposed_mac[2],
++ port->frontends.adapter.proposed_mac[3],
++ port->frontends.adapter.proposed_mac[4],
++ port->frontends.adapter.proposed_mac[5]);
++
++ netup_ci_init(port);
++ break;
++ }
++ }
++
++ return ret;
+ }
+
+ int cx23885_dvb_register(struct cx23885_tsport *port)
+@@ -622,6 +778,12 @@ int cx23885_dvb_unregister(struct cx23885_tsport *port)
+ if (fe0->dvb.frontend)
+ videobuf_dvb_unregister_bus(&port->frontends);
+
++ switch (port->dev->board) {
++ case CX23885_BOARD_NETUP_DUAL_DVBS2_CI:
++ netup_ci_exit(port);
++ break;
++ }
++
+ return 0;
+ }
+
+diff --git a/drivers/media/video/cx23885/cx23885-i2c.c b/drivers/media/video/cx23885/cx23885-i2c.c
+index bb7f71a..3421bd1 100644
+--- a/drivers/media/video/cx23885/cx23885-i2c.c
++++ b/drivers/media/video/cx23885/cx23885-i2c.c
+@@ -268,64 +268,6 @@ static int i2c_xfer(struct i2c_adapter *i2c_adap,
+ return retval;
+ }
+
+-static int attach_inform(struct i2c_client *client)
+-{
+- 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);
+-
+- 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;
+-}
+-
+-static int detach_inform(struct i2c_client *client)
+-{
+- struct cx23885_dev *dev = i2c_get_adapdata(client->adapter);
+-
+- dprintk(1, "i2c detach [client=%s]\n", client->name);
+-
+- return 0;
+-}
+-
+-void cx23885_call_i2c_clients(struct cx23885_i2c *bus,
+- unsigned int cmd, void *arg)
+-{
+- if (bus->i2c_rc != 0)
+- return;
+-
+- i2c_clients_command(&bus->i2c_adap, cmd, arg);
+-}
+-
+ static u32 cx23885_functionality(struct i2c_adapter *adap)
+ {
+ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
+@@ -343,9 +285,6 @@ 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,
+ };
+
+ static struct i2c_client cx23885_i2c_client_template = {
+@@ -402,15 +341,18 @@ 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_set_adapdata(&bus->i2c_adap, &dev->v4l2_dev);
+ i2c_add_adapter(&bus->i2c_adap);
+
+ bus->i2c_client.adapter = &bus->i2c_adap;
+
+ if (0 == bus->i2c_rc) {
+ dprintk(1, "%s: i2c bus %d registered\n", dev->name, bus->nr);
+- if (i2c_scan)
++ if (i2c_scan) {
++ printk(KERN_INFO "%s: scan bus %d:\n",
++ dev->name, bus->nr);
+ do_i2c_scan(dev->name, &bus->i2c_client);
++ }
+ } else
+ printk(KERN_WARNING "%s: i2c bus %d register FAILED\n",
+ dev->name, bus->nr);
+diff --git a/drivers/media/video/cx23885/cx23885-reg.h b/drivers/media/video/cx23885/cx23885-reg.h
+index 20b68a2..eafbe52 100644
+--- a/drivers/media/video/cx23885/cx23885-reg.h
++++ b/drivers/media/video/cx23885/cx23885-reg.h
+@@ -212,6 +212,8 @@ Channel manager Data Structure entry = 20 DWORD
+
+ #define DEV_CNTRL2 0x00040000
+
++#define PCI_MSK_GPIO1 (1 << 24)
++#define PCI_MSK_GPIO0 (1 << 23)
+ #define PCI_MSK_APB_DMA (1 << 12)
+ #define PCI_MSK_AL_WR (1 << 11)
+ #define PCI_MSK_AL_RD (1 << 10)
+diff --git a/drivers/media/video/cx23885/cx23885-video.c b/drivers/media/video/cx23885/cx23885-video.c
+index eaa1189..f0ac62c 100644
+--- a/drivers/media/video/cx23885/cx23885-video.c
++++ b/drivers/media/video/cx23885/cx23885-video.c
+@@ -35,11 +35,6 @@
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-ioctl.h>
+
+-#ifdef CONFIG_VIDEO_V4L1_COMPAT
+-/* Include V4L1 specific functions. Should be removed soon */
+-#include <linux/videodev.h>
+-#endif
+-
+ MODULE_DESCRIPTION("v4l2 driver module for cx23885 based TV cards");
+ MODULE_AUTHOR("Steven Toth <stoth@linuxtv.org>");
+ MODULE_LICENSE("GPL");
+@@ -244,6 +239,7 @@ static struct cx23885_ctrl cx23885_ctls[] = {
+ };
+ static const int CX23885_CTLS = ARRAY_SIZE(cx23885_ctls);
+
++/* Must be sorted from low to high control ID! */
+ static const u32 cx23885_user_ctrls[] = {
+ V4L2_CID_USER_CLASS,
+ V4L2_CID_BRIGHTNESS,
+@@ -303,11 +299,7 @@ static int cx23885_set_tvnorm(struct cx23885_dev *dev, v4l2_std_id 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);
++ call_all(dev, tuner, s_std, norm);
+
+ return 0;
+ }
+@@ -324,8 +316,8 @@ static struct video_device *cx23885_vdev_init(struct cx23885_dev *dev,
+ if (NULL == vfd)
+ return NULL;
+ *vfd = *template;
+- vfd->minor = -1;
+- vfd->parent = &pci->dev;
++ vfd->minor = -1;
++ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->release = video_device_release;
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
+ dev->name, type, cx23885_boards[dev->board].name);
+@@ -414,8 +406,7 @@ static int cx23885_video_mux(struct cx23885_dev *dev, unsigned int 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);
++ v4l2_subdev_call(dev->sd_cx25840, video, s_routing, &route);
+
+ return 0;
+ }
+@@ -891,7 +882,7 @@ static int cx23885_get_control(struct cx23885_dev *dev,
+ struct v4l2_control *ctl)
+ {
+ dprintk(1, "%s() calling cx25840(VIDIOC_G_CTRL)\n", __func__);
+- cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_G_CTRL, ctl);
++ call_all(dev, core, g_ctrl, ctl);
+ return 0;
+ }
+
+@@ -1005,7 +996,7 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ fh->vidq.field = f->fmt.pix.field;
+ dprintk(2, "%s() width=%d height=%d field=%d\n", __func__,
+ fh->width, fh->height, fh->vidq.field);
+- cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_S_FMT, f);
++ call_all(dev, video, s_fmt, f);
+ return 0;
+ }
+
+@@ -1285,7 +1276,7 @@ static int vidioc_g_frequency(struct file *file, void *priv,
+ 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);
++ call_all(dev, tuner, g_frequency, f);
+
+ return 0;
+ }
+@@ -1300,7 +1291,7 @@ static int cx23885_set_freq(struct cx23885_dev *dev, struct v4l2_frequency *f)
+ mutex_lock(&dev->lock);
+ dev->freq = f->frequency;
+
+- cx23885_call_i2c_clients(&dev->i2c_bus[1], VIDIOC_S_FREQUENCY, f);
++ call_all(dev, tuner, s_frequency, f);
+
+ /* When changing channels it is required to reset TVAUDIO */
+ msleep(10);
+@@ -1334,7 +1325,7 @@ static int vidioc_g_register(struct file *file, void *fh,
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+
+- cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_DBG_G_REGISTER, reg);
++ call_all(dev, core, g_register, reg);
+
+ return 0;
+ }
+@@ -1347,7 +1338,7 @@ static int vidioc_s_register(struct file *file, void *fh,
+ if (!v4l2_chip_match_host(&reg->match))
+ return -EINVAL;
+
+- cx23885_call_i2c_clients(&dev->i2c_bus[2], VIDIOC_DBG_S_REGISTER, reg);
++ call_all(dev, core, s_register, reg);
+
+ return 0;
+ }
+@@ -1528,6 +1519,26 @@ int cx23885_video_register(struct cx23885_dev *dev)
+ /* Don't enable VBI yet */
+ cx_set(PCI_INT_MSK, 1);
+
++ if (TUNER_ABSENT != dev->tuner_type) {
++ struct v4l2_subdev *sd = NULL;
++
++ if (dev->tuner_addr)
++ sd = v4l2_i2c_new_subdev(&dev->i2c_bus[1].i2c_adap,
++ "tuner", "tuner", dev->tuner_addr);
++ else
++ sd = v4l2_i2c_new_probed_subdev(&dev->i2c_bus[1].i2c_adap,
++ "tuner", "tuner", v4l2_i2c_tuner_addrs(ADDRS_TV));
++ if (sd) {
++ struct tuner_setup tun_setup;
++
++ tun_setup.mode_mask = T_ANALOG_TV;
++ tun_setup.type = dev->tuner_type;
++ tun_setup.addr = v4l2_i2c_subdev_addr(sd);
++
++ v4l2_subdev_call(sd, tuner, s_type_addr, &tun_setup);
++ }
++ }
++
+
+ /* register v4l devices */
+ dev->video_dev = cx23885_vdev_init(dev, dev->pci,
+diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h
+index 6782802..02d980a 100644
+--- a/drivers/media/video/cx23885/cx23885.h
++++ b/drivers/media/video/cx23885/cx23885.h
+@@ -24,7 +24,7 @@
+ #include <linux/i2c-algo-bit.h>
+ #include <linux/kdev_t.h>
+
+-#include <media/v4l2-common.h>
++#include <media/v4l2-device.h>
+ #include <media/tuner.h>
+ #include <media/tveeprom.h>
+ #include <media/videobuf-dma-sg.h>
+@@ -67,6 +67,10 @@
+ #define CX23885_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL_EXP 11
+ #define CX23885_BOARD_LEADTEK_WINFAST_PXDVR3200_H 12
+ #define CX23885_BOARD_COMPRO_VIDEOMATE_E650F 13
++#define CX23885_BOARD_TBS_6920 14
++#define CX23885_BOARD_TEVII_S470 15
++#define CX23885_BOARD_DVBWORLD_2005 16
++#define CX23885_BOARD_NETUP_DUAL_DVBS2_CI 17
+
+ /* Currently unsupported by the driver: PAL/H, NTSC/Kr, SECAM B/G/H/LC */
+ #define CX23885_NORMS (\
+@@ -184,6 +188,7 @@ struct cx23885_board {
+ */
+ u32 clk_freq;
+ struct cx23885_input input[MAX_CX23885_INPUT];
++ int cimax; /* for NetUP */
+ };
+
+ struct cx23885_subid {
+@@ -266,11 +271,13 @@ struct cx23885_tsport {
+
+ /* Allow a single tsport to have multiple frontends */
+ u32 num_frontends;
++ void *port_priv;
+ };
+
+ struct cx23885_dev {
+ struct list_head devlist;
+ atomic_t refcount;
++ struct v4l2_device v4l2_dev;
+
+ /* pci stuff */
+ struct pci_dev *pci;
+@@ -316,6 +323,7 @@ struct cx23885_dev {
+ unsigned int radio_type;
+ unsigned char radio_addr;
+ unsigned int has_radio;
++ struct v4l2_subdev *sd_cx25840;
+
+ /* V4l */
+ u32 freq;
+@@ -336,6 +344,14 @@ struct cx23885_dev {
+
+ };
+
++static inline struct cx23885_dev *to_cx23885(struct v4l2_device *v4l2_dev)
++{
++ return container_of(v4l2_dev, struct cx23885_dev, v4l2_dev);
++}
++
++#define call_all(dev, o, f, args...) \
++ v4l2_device_call_all(&dev->v4l2_dev, 0, o, f, ##args)
++
+ extern struct list_head cx23885_devlist;
+
+ #define SRAM_CH01 0 /* Video A */
+@@ -452,8 +468,6 @@ 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);
+ extern void cx23885_av_clk(struct cx23885_dev *dev, int enable);
+
+ /* ----------------------------------------------------------- */
+diff --git a/drivers/media/video/cx23885/netup-eeprom.c b/drivers/media/video/cx23885/netup-eeprom.c
+new file mode 100644
+index 0000000..042bbbb
+--- /dev/null
++++ b/drivers/media/video/cx23885/netup-eeprom.c
+@@ -0,0 +1,107 @@
++
++/*
++ * netup-eeprom.c
++ *
++ * 24LC02 EEPROM driver in conjunction with NetUP Dual DVB-S2 CI card
++ *
++ * Copyright (C) 2009 NetUP Inc.
++ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
++ *
++ * 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 "cx23885.h"
++#include "netup-eeprom.h"
++
++#define EEPROM_I2C_ADDR 0x50
++
++int netup_eeprom_read(struct i2c_adapter *i2c_adap, u8 addr)
++{
++ int ret;
++ unsigned char buf[2];
++
++ /* Read from EEPROM */
++ struct i2c_msg msg[] = {
++ {
++ .addr = EEPROM_I2C_ADDR,
++ .flags = 0,
++ .buf = &buf[0],
++ .len = 1
++ }, {
++ .addr = EEPROM_I2C_ADDR,
++ .flags = I2C_M_RD,
++ .buf = &buf[1],
++ .len = 1
++ }
++
++ };
++
++ buf[0] = addr;
++ buf[1] = 0x0;
++
++ ret = i2c_transfer(i2c_adap, msg, 2);
++
++ if (ret != 2) {
++ printk(KERN_ERR "eeprom i2c read error, status=%d\n", ret);
++ return -1;
++ }
++
++ return buf[1];
++};
++
++int netup_eeprom_write(struct i2c_adapter *i2c_adap, u8 addr, u8 data)
++{
++ int ret;
++ unsigned char bufw[2];
++
++ /* Write into EEPROM */
++ struct i2c_msg msg[] = {
++ {
++ .addr = EEPROM_I2C_ADDR,
++ .flags = 0,
++ .buf = &bufw[0],
++ .len = 2
++ }
++ };
++
++ bufw[0] = addr;
++ bufw[1] = data;
++
++ ret = i2c_transfer(i2c_adap, msg, 1);
++
++ if (ret != 1) {
++ printk(KERN_ERR "eeprom i2c write error, status=%d\n", ret);
++ return -1;
++ }
++
++ mdelay(10); /* prophylactic delay, datasheet write cycle time = 5 ms */
++ return 0;
++};
++
++void netup_get_card_info(struct i2c_adapter *i2c_adap,
++ struct netup_card_info *cinfo)
++{
++ int i, j;
++
++ cinfo->rev = netup_eeprom_read(i2c_adap, 13);
++
++ for (i = 0, j = 0; i < 6; i++, j++)
++ cinfo->port[0].mac[j] = netup_eeprom_read(i2c_adap, i);
++
++ for (i = 6, j = 0; i < 12; i++, j++)
++ cinfo->port[1].mac[j] = netup_eeprom_read(i2c_adap, i);
++};
+diff --git a/drivers/media/video/cx23885/netup-eeprom.h b/drivers/media/video/cx23885/netup-eeprom.h
+new file mode 100644
+index 0000000..13926e1
+--- /dev/null
++++ b/drivers/media/video/cx23885/netup-eeprom.h
+@@ -0,0 +1,42 @@
++/*
++ * netup-eeprom.h
++ *
++ * 24LC02 EEPROM driver in conjunction with NetUP Dual DVB-S2 CI card
++ *
++ * Copyright (C) 2009 NetUP Inc.
++ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
++ *
++ * 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 NETUP_EEPROM_H
++#define NETUP_EEPROM_H
++
++struct netup_port_info {
++ u8 mac[6];/* card MAC address */
++};
++
++struct netup_card_info {
++ struct netup_port_info port[2];/* ports - 1,2 */
++ u8 rev;/* card revision */
++};
++
++extern int netup_eeprom_read(struct i2c_adapter *i2c_adap, u8 addr);
++extern int netup_eeprom_write(struct i2c_adapter *i2c_adap, u8 addr, u8 data);
++extern void netup_get_card_info(struct i2c_adapter *i2c_adap,
++ struct netup_card_info *cinfo);
++
++#endif
+diff --git a/drivers/media/video/cx23885/netup-init.c b/drivers/media/video/cx23885/netup-init.c
+new file mode 100644
+index 0000000..f4893e6
+--- /dev/null
++++ b/drivers/media/video/cx23885/netup-init.c
+@@ -0,0 +1,125 @@
++/*
++ * netup-init.c
++ *
++ * NetUP Dual DVB-S2 CI driver
++ *
++ * Copyright (C) 2009 NetUP Inc.
++ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
++ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
++ *
++ * 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 "cx23885.h"
++
++static void i2c_av_write(struct i2c_adapter *i2c, u16 reg, u8 val)
++{
++ int ret;
++ u8 buf[3];
++ struct i2c_msg msg = {
++ .addr = 0x88 >> 1,
++ .flags = 0,
++ .buf = buf,
++ .len = 3
++ };
++
++ buf[0] = reg >> 8;
++ buf[1] = reg & 0xff;
++ buf[2] = val;
++
++ ret = i2c_transfer(i2c, &msg, 1);
++
++ if (ret != 1)
++ printk(KERN_ERR "%s: i2c write error!\n", __func__);
++}
++
++static void i2c_av_write4(struct i2c_adapter *i2c, u16 reg, u32 val)
++{
++ int ret;
++ u8 buf[6];
++ struct i2c_msg msg = {
++ .addr = 0x88 >> 1,
++ .flags = 0,
++ .buf = buf,
++ .len = 6
++ };
++
++ buf[0] = reg >> 8;
++ buf[1] = reg & 0xff;
++ buf[2] = val & 0xff;
++ buf[3] = (val >> 8) & 0xff;
++ buf[4] = (val >> 16) & 0xff;
++ buf[5] = val >> 24;
++
++ ret = i2c_transfer(i2c, &msg, 1);
++
++ if (ret != 1)
++ printk(KERN_ERR "%s: i2c write error!\n", __func__);
++}
++
++static u8 i2c_av_read(struct i2c_adapter *i2c, u16 reg)
++{
++ int ret;
++ u8 buf[2];
++ struct i2c_msg msg = {
++ .addr = 0x88 >> 1,
++ .flags = 0,
++ .buf = buf,
++ .len = 2
++ };
++
++ buf[0] = reg >> 8;
++ buf[1] = reg & 0xff;
++
++ ret = i2c_transfer(i2c, &msg, 1);
++
++ if (ret != 1)
++ printk(KERN_ERR "%s: i2c write error!\n", __func__);
++
++ msg.flags = I2C_M_RD;
++ msg.len = 1;
++
++ ret = i2c_transfer(i2c, &msg, 1);
++
++ if (ret != 1)
++ printk(KERN_ERR "%s: i2c read error!\n", __func__);
++
++ return buf[0];
++}
++
++static void i2c_av_and_or(struct i2c_adapter *i2c, u16 reg, unsigned and_mask,
++ u8 or_value)
++{
++ i2c_av_write(i2c, reg, (i2c_av_read(i2c, reg) & and_mask) | or_value);
++}
++/* set 27MHz on AUX_CLK */
++void netup_initialize(struct cx23885_dev *dev)
++{
++ struct cx23885_i2c *i2c_bus = &dev->i2c_bus[2];
++ struct i2c_adapter *i2c = &i2c_bus->i2c_adap;
++
++ /* Stop microcontroller */
++ i2c_av_and_or(i2c, 0x803, ~0x10, 0x00);
++
++ /* Aux PLL frac for 27 MHz */
++ i2c_av_write4(i2c, 0x114, 0xea0eb3);
++
++ /* Aux PLL int for 27 MHz */
++ i2c_av_write4(i2c, 0x110, 0x090319);
++
++ /* start microcontroller */
++ i2c_av_and_or(i2c, 0x803, ~0x10, 0x10);
++}
+diff --git a/drivers/media/video/cx23885/netup-init.h b/drivers/media/video/cx23885/netup-init.h
+new file mode 100644
+index 0000000..d26ae4b
+--- /dev/null
++++ b/drivers/media/video/cx23885/netup-init.h
+@@ -0,0 +1,25 @@
++/*
++ * netup-init.h
++ *
++ * NetUP Dual DVB-S2 CI driver
++ *
++ * Copyright (C) 2009 NetUP Inc.
++ * Copyright (C) 2009 Igor M. Liplianin <liplianin@netup.ru>
++ * Copyright (C) 2009 Abylay Ospan <aospan@netup.ru>
++ *
++ * 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.
++ */
++extern void netup_initialize(struct cx23885_dev *dev);
+diff --git a/drivers/media/video/cx25840/cx25840-audio.c b/drivers/media/video/cx25840/cx25840-audio.c
+index d199d80..93d74be 100644
+--- a/drivers/media/video/cx25840/cx25840-audio.c
++++ b/drivers/media/video/cx25840/cx25840-audio.c
+@@ -363,75 +363,74 @@ static void set_mute(struct i2c_client *client, int mute)
+ }
+ }
+
+-int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg)
++int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+ {
+- struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+- struct v4l2_control *ctrl = arg;
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct cx25840_state *state = to_state(sd);
+ int retval;
+
+- switch (cmd) {
+- case VIDIOC_INT_AUDIO_CLOCK_FREQ:
+- if (!state->is_cx25836)
+- cx25840_and_or(client, 0x810, ~0x1, 1);
+- if (state->aud_input != CX25840_AUDIO_SERIAL) {
+- cx25840_and_or(client, 0x803, ~0x10, 0);
+- cx25840_write(client, 0x8d3, 0x1f);
+- }
+- retval = set_audclk_freq(client, *(u32 *)arg);
+- if (state->aud_input != CX25840_AUDIO_SERIAL) {
+- cx25840_and_or(client, 0x803, ~0x10, 0x10);
+- }
+- if (!state->is_cx25836)
+- cx25840_and_or(client, 0x810, ~0x1, 0);
+- return retval;
+-
+- case VIDIOC_G_CTRL:
+- switch (ctrl->id) {
+- case V4L2_CID_AUDIO_VOLUME:
+- ctrl->value = get_volume(client);
+- break;
+- case V4L2_CID_AUDIO_BASS:
+- ctrl->value = get_bass(client);
+- break;
+- case V4L2_CID_AUDIO_TREBLE:
+- ctrl->value = get_treble(client);
+- break;
+- case V4L2_CID_AUDIO_BALANCE:
+- ctrl->value = get_balance(client);
+- break;
+- case V4L2_CID_AUDIO_MUTE:
+- ctrl->value = get_mute(client);
+- break;
+- default:
+- return -EINVAL;
+- }
+- break;
++ if (!state->is_cx25836)
++ cx25840_and_or(client, 0x810, ~0x1, 1);
++ if (state->aud_input != CX25840_AUDIO_SERIAL) {
++ cx25840_and_or(client, 0x803, ~0x10, 0);
++ cx25840_write(client, 0x8d3, 0x1f);
++ }
++ retval = set_audclk_freq(client, freq);
++ if (state->aud_input != CX25840_AUDIO_SERIAL)
++ cx25840_and_or(client, 0x803, ~0x10, 0x10);
++ if (!state->is_cx25836)
++ cx25840_and_or(client, 0x810, ~0x1, 0);
++ return retval;
++}
+
+- case VIDIOC_S_CTRL:
+- switch (ctrl->id) {
+- case V4L2_CID_AUDIO_VOLUME:
+- set_volume(client, ctrl->value);
+- break;
+- case V4L2_CID_AUDIO_BASS:
+- set_bass(client, ctrl->value);
+- break;
+- case V4L2_CID_AUDIO_TREBLE:
+- set_treble(client, ctrl->value);
+- break;
+- case V4L2_CID_AUDIO_BALANCE:
+- set_balance(client, ctrl->value);
+- break;
+- case V4L2_CID_AUDIO_MUTE:
+- set_mute(client, ctrl->value);
+- break;
+- default:
+- return -EINVAL;
+- }
+- break;
++int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
++ switch (ctrl->id) {
++ case V4L2_CID_AUDIO_VOLUME:
++ ctrl->value = get_volume(client);
++ break;
++ case V4L2_CID_AUDIO_BASS:
++ ctrl->value = get_bass(client);
++ break;
++ case V4L2_CID_AUDIO_TREBLE:
++ ctrl->value = get_treble(client);
++ break;
++ case V4L2_CID_AUDIO_BALANCE:
++ ctrl->value = get_balance(client);
++ break;
++ case V4L2_CID_AUDIO_MUTE:
++ ctrl->value = get_mute(client);
++ break;
+ default:
+ return -EINVAL;
+ }
++ return 0;
++}
+
++int cx25840_audio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
++ switch (ctrl->id) {
++ case V4L2_CID_AUDIO_VOLUME:
++ set_volume(client, ctrl->value);
++ break;
++ case V4L2_CID_AUDIO_BASS:
++ set_bass(client, ctrl->value);
++ break;
++ case V4L2_CID_AUDIO_TREBLE:
++ set_treble(client, ctrl->value);
++ break;
++ case V4L2_CID_AUDIO_BALANCE:
++ set_balance(client, ctrl->value);
++ break;
++ case V4L2_CID_AUDIO_MUTE:
++ set_mute(client, ctrl->value);
++ break;
++ default:
++ return -EINVAL;
++ }
+ return 0;
+ }
+diff --git a/drivers/media/video/cx25840/cx25840-core.c b/drivers/media/video/cx25840/cx25840-core.c
+index 25eb3be..737ee4e 100644
+--- a/drivers/media/video/cx25840/cx25840-core.c
++++ b/drivers/media/video/cx25840/cx25840-core.c
+@@ -39,7 +39,7 @@
+ #include <linux/delay.h>
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-chip-ident.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <media/v4l2-i2c-drv.h>
+ #include <media/cx25840.h>
+
+ #include "cx25840-core.h"
+@@ -48,15 +48,12 @@ MODULE_DESCRIPTION("Conexant CX25840 audio/video decoder driver");
+ MODULE_AUTHOR("Ulf Eklund, Chris Kennedy, Hans Verkuil, Tyler Trafford");
+ MODULE_LICENSE("GPL");
+
+-static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END };
+-
+ static int cx25840_debug;
+
+ module_param_named(debug,cx25840_debug, int, 0644);
+
+ MODULE_PARM_DESC(debug, "Debugging messages [0=Off (default) 1=On]");
+
+-I2C_CLIENT_INSMOD;
+
+ /* ----------------------------------------------------------------------- */
+
+@@ -763,7 +760,7 @@ static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+ break;
+
+ case V4L2_CID_HUE:
+- if (ctrl->value < -127 || ctrl->value > 127) {
++ if (ctrl->value < -128 || ctrl->value > 127) {
+ v4l_err(client, "invalid hue setting %d\n", ctrl->value);
+ return -ERANGE;
+ }
+@@ -778,7 +775,7 @@ static int cx25840_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+ case V4L2_CID_AUDIO_MUTE:
+ if (state->is_cx25836)
+ return -EINVAL;
+- return cx25840_audio(client, VIDIOC_S_CTRL, ctrl);
++ return cx25840_audio_s_ctrl(sd, ctrl);
+
+ default:
+ return -EINVAL;
+@@ -815,7 +812,7 @@ static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+ case V4L2_CID_AUDIO_MUTE:
+ if (state->is_cx25836)
+ return -EINVAL;
+- return cx25840_audio(client, VIDIOC_G_CTRL, ctrl);
++ return cx25840_audio_g_ctrl(sd, ctrl);
+ default:
+ return -EINVAL;
+ }
+@@ -827,11 +824,9 @@ static int cx25840_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+
+ static int cx25840_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+ {
+- struct i2c_client *client = v4l2_get_subdevdata(sd);
+-
+ switch (fmt->type) {
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+- return cx25840_vbi(client, VIDIOC_G_FMT, fmt);
++ return cx25840_vbi_g_fmt(sd, fmt);
+ default:
+ return -EINVAL;
+ }
+@@ -893,10 +888,10 @@ static int cx25840_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+ break;
+
+ case V4L2_BUF_TYPE_SLICED_VBI_CAPTURE:
+- return cx25840_vbi(client, VIDIOC_S_FMT, fmt);
++ return cx25840_vbi_s_fmt(sd, fmt);
+
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+- return cx25840_vbi(client, VIDIOC_S_FMT, fmt);
++ return cx25840_vbi_s_fmt(sd, fmt);
+
+ default:
+ return -EINVAL;
+@@ -1101,6 +1096,16 @@ static void log_audio_status(struct i2c_client *client)
+
+ /* ----------------------------------------------------------------------- */
+
++/* This init operation must be called to load the driver's firmware.
++ Without this the audio standard detection will fail and you will
++ only get mono.
++
++ Since loading the firmware is often problematic when the driver is
++ compiled into the kernel I recommend postponing calling this function
++ until the first open of the video device. Another reason for
++ postponing it is that loading this firmware takes a long time (seconds)
++ due to the slow i2c bus speed. So it will speed up the boot process if
++ you can avoid loading the fw as long as the video device isn't used. */
+ static int cx25840_init(struct v4l2_subdev *sd, u32 val)
+ {
+ struct cx25840_state *state = to_state(sd);
+@@ -1146,20 +1151,6 @@ static int cx25840_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *
+ }
+ #endif
+
+-static int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
+-{
+- struct i2c_client *client = v4l2_get_subdevdata(sd);
+-
+- return cx25840_vbi(client, VIDIOC_INT_DECODE_VBI_LINE, vbi);
+-}
+-
+-static int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq)
+-{
+- struct i2c_client *client = v4l2_get_subdevdata(sd);
+-
+- return cx25840_audio(client, VIDIOC_INT_AUDIO_CLOCK_FREQ, &freq);
+-}
+-
+ static int cx25840_s_stream(struct v4l2_subdev *sd, int enable)
+ {
+ struct cx25840_state *state = to_state(sd);
+@@ -1195,10 +1186,12 @@ static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
++ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
++ return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
+ case V4L2_CID_HUE:
+- return v4l2_ctrl_query_fill_std(qc);
++ return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
+ default:
+ break;
+ }
+@@ -1210,10 +1203,11 @@ static int cx25840_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+ return v4l2_ctrl_query_fill(qc, 0, 65535,
+ 65535 / 100, state->default_volume);
+ case V4L2_CID_AUDIO_MUTE:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+- return v4l2_ctrl_query_fill_std(qc);
++ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
+ default:
+ return -EINVAL;
+ }
+@@ -1380,19 +1374,6 @@ static int cx25840_log_status(struct v4l2_subdev *sd)
+ return 0;
+ }
+
+-static int cx25840_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- /* ignore this command */
+- if (cmd == TUNER_SET_TYPE_ADDR || cmd == TUNER_SET_CONFIG)
+- return 0;
+-
+- /* Old-style drivers rely on initialization on first use, so
+- call the init whenever a command is issued to this driver.
+- New-style drivers using v4l2_subdev should call init explicitly. */
+- cx25840_init(i2c_get_clientdata(client), 0);
+- return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+-}
+-
+ /* ----------------------------------------------------------------------- */
+
+ static const struct v4l2_subdev_core_ops cx25840_core_ops = {
+@@ -1528,8 +1509,6 @@ MODULE_DEVICE_TABLE(i2c, cx25840_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "cx25840",
+- .driverid = I2C_DRIVERID_CX25840,
+- .command = cx25840_command,
+ .probe = cx25840_probe,
+ .remove = cx25840_remove,
+ .id_table = cx25840_id,
+diff --git a/drivers/media/video/cx25840/cx25840-core.h b/drivers/media/video/cx25840/cx25840-core.h
+index be05582..9ad0eb8 100644
+--- a/drivers/media/video/cx25840/cx25840-core.h
++++ b/drivers/media/video/cx25840/cx25840-core.h
+@@ -75,11 +75,15 @@ int cx25840_loadfw(struct i2c_client *client);
+
+ /* ----------------------------------------------------------------------- */
+ /* cx25850-audio.c */
+-int cx25840_audio(struct i2c_client *client, unsigned int cmd, void *arg);
+ void cx25840_audio_set_path(struct i2c_client *client);
++int cx25840_s_clock_freq(struct v4l2_subdev *sd, u32 freq);
++int cx25840_audio_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
++int cx25840_audio_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
+
+ /* ----------------------------------------------------------------------- */
+ /* cx25850-vbi.c */
+-int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg);
++int cx25840_vbi_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt);
++int cx25840_vbi_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt);
++int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi);
+
+ #endif
+diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c
+index 03f09b2..35f6592 100644
+--- a/drivers/media/video/cx25840/cx25840-vbi.c
++++ b/drivers/media/video/cx25840/cx25840-vbi.c
+@@ -82,199 +82,181 @@ static int decode_vps(u8 * dst, u8 * p)
+ return err & 0xf0;
+ }
+
+-int cx25840_vbi(struct i2c_client *client, unsigned int cmd, void *arg)
++int cx25840_vbi_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+ {
+- struct cx25840_state *state = to_state(i2c_get_clientdata(client));
+- struct v4l2_format *fmt;
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct cx25840_state *state = to_state(sd);
+ struct v4l2_sliced_vbi_format *svbi;
++ static const u16 lcr2vbi[] = {
++ 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
++ 0, V4L2_SLICED_WSS_625, 0, /* 4 */
++ V4L2_SLICED_CAPTION_525, /* 6 */
++ 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */
++ 0, 0, 0, 0
++ };
++ int is_pal = !(state->std & V4L2_STD_525_60);
++ int i;
+
+- switch (cmd) {
+- case VIDIOC_G_FMT:
+- {
+- static u16 lcr2vbi[] = {
+- 0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
+- 0, V4L2_SLICED_WSS_625, 0, /* 4 */
+- V4L2_SLICED_CAPTION_525, /* 6 */
+- 0, 0, V4L2_SLICED_VPS, 0, 0, /* 9 */
+- 0, 0, 0, 0
+- };
+- int is_pal = !(state->std & V4L2_STD_525_60);
+- int i;
+-
+- fmt = arg;
+- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
+- return -EINVAL;
+- svbi = &fmt->fmt.sliced;
+- memset(svbi, 0, sizeof(*svbi));
+- /* we're done if raw VBI is active */
+- if ((cx25840_read(client, 0x404) & 0x10) == 0)
+- break;
++ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE)
++ return -EINVAL;
++ svbi = &fmt->fmt.sliced;
++ memset(svbi, 0, sizeof(*svbi));
++ /* we're done if raw VBI is active */
++ if ((cx25840_read(client, 0x404) & 0x10) == 0)
++ return 0;
+
+- if (is_pal) {
+- for (i = 7; i <= 23; i++) {
+- u8 v = cx25840_read(client, 0x424 + i - 7);
++ if (is_pal) {
++ for (i = 7; i <= 23; i++) {
++ u8 v = cx25840_read(client, 0x424 + i - 7);
+
+- svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+- svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+- svbi->service_set |=
+- svbi->service_lines[0][i] | svbi->service_lines[1][i];
+- }
++ svbi->service_lines[0][i] = lcr2vbi[v >> 4];
++ svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
++ svbi->service_set |= svbi->service_lines[0][i] |
++ svbi->service_lines[1][i];
+ }
+- else {
+- for (i = 10; i <= 21; i++) {
+- u8 v = cx25840_read(client, 0x424 + i - 10);
+-
+- svbi->service_lines[0][i] = lcr2vbi[v >> 4];
+- svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
+- svbi->service_set |=
+- svbi->service_lines[0][i] | svbi->service_lines[1][i];
+- }
++ } else {
++ for (i = 10; i <= 21; i++) {
++ u8 v = cx25840_read(client, 0x424 + i - 10);
++
++ svbi->service_lines[0][i] = lcr2vbi[v >> 4];
++ svbi->service_lines[1][i] = lcr2vbi[v & 0xf];
++ svbi->service_set |= svbi->service_lines[0][i] |
++ svbi->service_lines[1][i];
+ }
+- break;
+ }
++ return 0;
++}
+
+- case VIDIOC_S_FMT:
+- {
+- int is_pal = !(state->std & V4L2_STD_525_60);
+- int vbi_offset = is_pal ? 1 : 0;
+- int i, x;
+- u8 lcr[24];
+-
+- fmt = arg;
+- if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE &&
+- fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE)
+- return -EINVAL;
+- svbi = &fmt->fmt.sliced;
+- if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
+- /* raw VBI */
+- memset(svbi, 0, sizeof(*svbi));
+-
+- /* Setup standard */
+- cx25840_std_setup(client);
+-
+- /* VBI Offset */
+- cx25840_write(client, 0x47f, vbi_offset);
+- cx25840_write(client, 0x404, 0x2e);
+- break;
+- }
+-
+- for (x = 0; x <= 23; x++)
+- lcr[x] = 0x00;
++int cx25840_vbi_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct cx25840_state *state = to_state(sd);
++ struct v4l2_sliced_vbi_format *svbi;
++ int is_pal = !(state->std & V4L2_STD_525_60);
++ int vbi_offset = is_pal ? 1 : 0;
++ int i, x;
++ u8 lcr[24];
++
++ if (fmt->type != V4L2_BUF_TYPE_SLICED_VBI_CAPTURE &&
++ fmt->type != V4L2_BUF_TYPE_VBI_CAPTURE)
++ return -EINVAL;
++ svbi = &fmt->fmt.sliced;
++ if (fmt->type == V4L2_BUF_TYPE_VBI_CAPTURE) {
++ /* raw VBI */
++ memset(svbi, 0, sizeof(*svbi));
+
+ /* Setup standard */
+ cx25840_std_setup(client);
+
+- /* Sliced VBI */
+- cx25840_write(client, 0x404, 0x32); /* Ancillary data */
+- cx25840_write(client, 0x406, 0x13);
++ /* VBI Offset */
+ cx25840_write(client, 0x47f, vbi_offset);
++ cx25840_write(client, 0x404, 0x2e);
++ return 0;
++ }
+
+- if (is_pal) {
+- for (i = 0; i <= 6; i++)
+- svbi->service_lines[0][i] =
+- svbi->service_lines[1][i] = 0;
+- } else {
+- for (i = 0; i <= 9; i++)
+- svbi->service_lines[0][i] =
+- svbi->service_lines[1][i] = 0;
+-
+- for (i = 22; i <= 23; i++)
+- svbi->service_lines[0][i] =
+- svbi->service_lines[1][i] = 0;
+- }
+-
+- for (i = 7; i <= 23; i++) {
+- for (x = 0; x <= 1; x++) {
+- switch (svbi->service_lines[1-x][i]) {
+- case V4L2_SLICED_TELETEXT_B:
+- lcr[i] |= 1 << (4 * x);
+- break;
+- case V4L2_SLICED_WSS_625:
+- lcr[i] |= 4 << (4 * x);
+- break;
+- case V4L2_SLICED_CAPTION_525:
+- lcr[i] |= 6 << (4 * x);
+- break;
+- case V4L2_SLICED_VPS:
+- lcr[i] |= 9 << (4 * x);
+- break;
+- }
+- }
+- }
++ for (x = 0; x <= 23; x++)
++ lcr[x] = 0x00;
++
++ /* Setup standard */
++ cx25840_std_setup(client);
++
++ /* Sliced VBI */
++ cx25840_write(client, 0x404, 0x32); /* Ancillary data */
++ cx25840_write(client, 0x406, 0x13);
++ cx25840_write(client, 0x47f, vbi_offset);
++
++ if (is_pal) {
++ for (i = 0; i <= 6; i++)
++ svbi->service_lines[0][i] =
++ svbi->service_lines[1][i] = 0;
++ } else {
++ for (i = 0; i <= 9; i++)
++ svbi->service_lines[0][i] =
++ svbi->service_lines[1][i] = 0;
++
++ for (i = 22; i <= 23; i++)
++ svbi->service_lines[0][i] =
++ svbi->service_lines[1][i] = 0;
++ }
+
+- if (is_pal) {
+- for (x = 1, i = 0x424; i <= 0x434; i++, x++) {
+- cx25840_write(client, i, lcr[6 + x]);
+- }
+- }
+- else {
+- for (x = 1, i = 0x424; i <= 0x430; i++, x++) {
+- cx25840_write(client, i, lcr[9 + x]);
+- }
+- for (i = 0x431; i <= 0x434; i++) {
+- cx25840_write(client, i, 0);
++ for (i = 7; i <= 23; i++) {
++ for (x = 0; x <= 1; x++) {
++ switch (svbi->service_lines[1-x][i]) {
++ case V4L2_SLICED_TELETEXT_B:
++ lcr[i] |= 1 << (4 * x);
++ break;
++ case V4L2_SLICED_WSS_625:
++ lcr[i] |= 4 << (4 * x);
++ break;
++ case V4L2_SLICED_CAPTION_525:
++ lcr[i] |= 6 << (4 * x);
++ break;
++ case V4L2_SLICED_VPS:
++ lcr[i] |= 9 << (4 * x);
++ break;
+ }
+ }
++ }
+
+- cx25840_write(client, 0x43c, 0x16);
+-
+- if (is_pal) {
+- cx25840_write(client, 0x474, 0x2a);
+- } else {
+- cx25840_write(client, 0x474, 0x22);
+- }
+- break;
++ if (is_pal) {
++ for (x = 1, i = 0x424; i <= 0x434; i++, x++)
++ cx25840_write(client, i, lcr[6 + x]);
++ } else {
++ for (x = 1, i = 0x424; i <= 0x430; i++, x++)
++ cx25840_write(client, i, lcr[9 + x]);
++ for (i = 0x431; i <= 0x434; i++)
++ cx25840_write(client, i, 0);
+ }
+
+- case VIDIOC_INT_DECODE_VBI_LINE:
+- {
+- struct v4l2_decode_vbi_line *vbi = arg;
+- u8 *p = vbi->p;
+- int id1, id2, l, err = 0;
++ cx25840_write(client, 0x43c, 0x16);
++ cx25840_write(client, 0x474, is_pal ? 0x2a : 0x22);
++ return 0;
++}
+
+- if (p[0] || p[1] != 0xff || p[2] != 0xff ||
+- (p[3] != 0x55 && p[3] != 0x91)) {
+- vbi->line = vbi->type = 0;
+- break;
+- }
++int cx25840_decode_vbi_line(struct v4l2_subdev *sd, struct v4l2_decode_vbi_line *vbi)
++{
++ struct cx25840_state *state = to_state(sd);
++ u8 *p = vbi->p;
++ int id1, id2, l, err = 0;
++
++ if (p[0] || p[1] != 0xff || p[2] != 0xff ||
++ (p[3] != 0x55 && p[3] != 0x91)) {
++ vbi->line = vbi->type = 0;
++ return 0;
++ }
+
+- p += 4;
+- id1 = p[-1];
+- id2 = p[0] & 0xf;
+- l = p[2] & 0x3f;
+- l += state->vbi_line_offset;
+- p += 4;
++ p += 4;
++ id1 = p[-1];
++ id2 = p[0] & 0xf;
++ l = p[2] & 0x3f;
++ l += state->vbi_line_offset;
++ p += 4;
+
+- switch (id2) {
+- case 1:
+- id2 = V4L2_SLICED_TELETEXT_B;
+- break;
+- case 4:
+- id2 = V4L2_SLICED_WSS_625;
+- break;
+- case 6:
+- id2 = V4L2_SLICED_CAPTION_525;
+- err = !odd_parity(p[0]) || !odd_parity(p[1]);
+- break;
+- case 9:
+- id2 = V4L2_SLICED_VPS;
+- if (decode_vps(p, p) != 0) {
+- err = 1;
+- }
+- break;
+- default:
+- id2 = 0;
++ switch (id2) {
++ case 1:
++ id2 = V4L2_SLICED_TELETEXT_B;
++ break;
++ case 4:
++ id2 = V4L2_SLICED_WSS_625;
++ break;
++ case 6:
++ id2 = V4L2_SLICED_CAPTION_525;
++ err = !odd_parity(p[0]) || !odd_parity(p[1]);
++ break;
++ case 9:
++ id2 = V4L2_SLICED_VPS;
++ if (decode_vps(p, p) != 0)
+ err = 1;
+- break;
+- }
+-
+- vbi->type = err ? 0 : id2;
+- vbi->line = err ? 0 : l;
+- vbi->is_second_field = err ? 0 : (id1 == 0x55);
+- vbi->p = p;
+ break;
+- }
++ default:
++ id2 = 0;
++ err = 1;
++ break;
+ }
+
++ vbi->type = err ? 0 : id2;
++ vbi->line = err ? 0 : l;
++ vbi->is_second_field = err ? 0 : (id1 == 0x55);
++ vbi->p = p;
+ return 0;
+ }
+diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig
+index 2d250a2..4995298 100644
+--- a/drivers/media/video/cx88/Kconfig
++++ b/drivers/media/video/cx88/Kconfig
+@@ -61,7 +61,7 @@ config VIDEO_CX88_DVB
+ select DVB_STV0299 if !DVB_FE_CUSTOMISE
+ select DVB_STV0288 if !DVB_FE_CUSTOMISE
+ select DVB_STB6000 if !DVB_FE_CUSTOMISE
+- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE
++ select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
+ ---help---
+ This adds support for DVB/ATSC cards based on the
+ Conexant 2388x chip.
+diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
+index 7f5b8bf..44eacfb 100644
+--- a/drivers/media/video/cx88/cx88-blackbird.c
++++ b/drivers/media/video/cx88/cx88-blackbird.c
+@@ -746,7 +746,6 @@ static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv,
+ return -EINVAL;
+
+ strlcpy(f->description, "MPEG", sizeof(f->description));
+- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->pixelformat = V4L2_PIX_FMT_MPEG;
+ return 0;
+ }
+@@ -757,7 +756,6 @@ static int vidioc_g_fmt_vid_cap (struct file *file, void *priv,
+ struct cx8802_fh *fh = priv;
+ struct cx8802_dev *dev = fh->dev;
+
+- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */
+@@ -776,7 +774,6 @@ static int vidioc_try_fmt_vid_cap (struct file *file, void *priv,
+ struct cx8802_fh *fh = priv;
+ struct cx8802_dev *dev = fh->dev;
+
+- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */;
+@@ -793,7 +790,6 @@ static int vidioc_s_fmt_vid_cap (struct file *file, void *priv,
+ struct cx8802_dev *dev = fh->dev;
+ struct cx88_core *core = dev->core;
+
+- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.bytesperline = 0;
+ f->fmt.pix.sizeimage = dev->ts_packet_size * dev->ts_packet_count; /* 188 * 4 * 1024; */;
+@@ -919,7 +915,7 @@ static int vidioc_log_status (struct file *file, void *priv)
+ snprintf(name, sizeof(name), "%s/2", core->name);
+ printk("%s/2: ============ START LOG STATUS ============\n",
+ core->name);
+- cx88_call_i2c_clients(core, VIDIOC_LOG_STATUS, NULL);
++ call_all(core, core, log_status);
+ cx2341x_log_status(&dev->params, name);
+ printk("%s/2: ============= END LOG STATUS =============\n",
+ core->name);
+@@ -974,7 +970,7 @@ static int vidioc_g_frequency (struct file *file, void *priv,
+
+ f->type = V4L2_TUNER_ANALOG_TV;
+ f->frequency = core->freq;
+- cx88_call_i2c_clients(core,VIDIOC_G_FREQUENCY,f);
++ call_all(core, tuner, g_frequency, f);
+
+ return 0;
+ }
+diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
+index 733ede3..0363971 100644
+--- a/drivers/media/video/cx88/cx88-cards.c
++++ b/drivers/media/video/cx88/cx88-cards.c
+@@ -732,6 +732,8 @@ static const struct cx88_board cx88_boards[] = {
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
++ /* Some variants use a tda9874 and so need the tvaudio module. */
++ .audio_chip = V4L2_IDENT_TVAUDIO,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+@@ -1934,6 +1936,39 @@ static const struct cx88_board cx88_boards[] = {
+ } },
+ .mpeg = CX88_MPEG_DVB,
+ },
++ [CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII] = {
++ .name = "Terratec Cinergy HT PCI MKII",
++ .tuner_type = TUNER_XC2028,
++ .tuner_addr = 0x61,
++ .radio_type = TUNER_XC2028,
++ .radio_addr = 0x61,
++ .input = { {
++ .type = CX88_VMUX_TELEVISION,
++ .vmux = 0,
++ .gpio0 = 0x004ff,
++ .gpio1 = 0x010ff,
++ .gpio2 = 0x00001,
++ }, {
++ .type = CX88_VMUX_COMPOSITE1,
++ .vmux = 1,
++ .gpio0 = 0x004fb,
++ .gpio1 = 0x010ef,
++ .audioroute = 1,
++ }, {
++ .type = CX88_VMUX_SVIDEO,
++ .vmux = 2,
++ .gpio0 = 0x004fb,
++ .gpio1 = 0x010ef,
++ .audioroute = 1,
++ } },
++ .radio = {
++ .type = CX88_RADIO,
++ .gpio0 = 0x004ff,
++ .gpio1 = 0x010ff,
++ .gpio2 = 0x0ff,
++ },
++ .mpeg = CX88_MPEG_DVB,
++ },
+ };
+
+ /* ------------------------------------------------------------------ */
+@@ -2343,6 +2378,10 @@ static const struct cx88_subid cx88_subids[] = {
+ .subvendor = 0xb200,
+ .subdevice = 0x4200,
+ .card = CX88_BOARD_SATTRADE_ST4200,
++ }, {
++ .subvendor = 0x153b,
++ .subdevice = 0x1177,
++ .card = CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII,
+ },
+ };
+
+@@ -2819,6 +2858,7 @@ void cx88_setup_xc3028(struct cx88_core *core, struct xc2028_ctrl *ctl)
+ */
+ break;
+ case CX88_BOARD_PINNACLE_HYBRID_PCTV:
++ case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII:
+ ctl->demod = XC3028_FE_ZARLINK456;
+ ctl->mts = 1;
+ break;
+@@ -2947,7 +2987,7 @@ static void cx88_card_setup(struct cx88_core *core)
+ tea5767_cfg.tuner = TUNER_TEA5767;
+ tea5767_cfg.priv = &ctl;
+
+- cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &tea5767_cfg);
++ call_all(core, tuner, s_config, &tea5767_cfg);
+ break;
+ }
+ case CX88_BOARD_TEVII_S420:
+@@ -2972,7 +3012,7 @@ static void cx88_card_setup(struct cx88_core *core)
+ tun_setup.type = core->board.radio_type;
+ tun_setup.addr = core->board.radio_addr;
+ tun_setup.tuner_callback = cx88_tuner_callback;
+- cx88_call_i2c_clients(core, TUNER_SET_TYPE_ADDR, &tun_setup);
++ call_all(core, tuner, s_type_addr, &tun_setup);
+ mode_mask &= ~T_RADIO;
+ }
+
+@@ -2982,7 +3022,7 @@ static void cx88_card_setup(struct cx88_core *core)
+ tun_setup.addr = core->board.tuner_addr;
+ tun_setup.tuner_callback = cx88_tuner_callback;
+
+- cx88_call_i2c_clients(core, TUNER_SET_TYPE_ADDR, &tun_setup);
++ call_all(core, tuner, s_type_addr, &tun_setup);
+ }
+
+ if (core->board.tda9887_conf) {
+@@ -2991,7 +3031,7 @@ static void cx88_card_setup(struct cx88_core *core)
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &core->board.tda9887_conf;
+
+- cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &tda9887_cfg);
++ call_all(core, tuner, s_config, &tda9887_cfg);
+ }
+
+ if (core->board.tuner_type == TUNER_XC2028) {
+@@ -3007,9 +3047,9 @@ static void cx88_card_setup(struct cx88_core *core)
+ xc2028_cfg.priv = &ctl;
+ info_printk(core, "Asking xc2028/3028 to load firmware %s\n",
+ ctl.fname);
+- cx88_call_i2c_clients(core, TUNER_SET_CONFIG, &xc2028_cfg);
++ call_all(core, tuner, s_config, &xc2028_cfg);
+ }
+- cx88_call_i2c_clients (core, TUNER_SET_STANDBY, NULL);
++ call_all(core, core, s_standby, 0);
+ }
+
+ /* ------------------------------------------------------------------ */
+@@ -3089,6 +3129,8 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr)
+ int i;
+
+ core = kzalloc(sizeof(*core), GFP_KERNEL);
++ if (core == NULL)
++ return NULL;
+
+ atomic_inc(&core->refcount);
+ core->pci_bus = pci->bus->number;
+@@ -3100,7 +3142,15 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr)
+
+ core->nr = nr;
+ sprintf(core->name, "cx88[%d]", core->nr);
++
++ strcpy(core->v4l2_dev.name, core->name);
++ if (v4l2_device_register(NULL, &core->v4l2_dev)) {
++ kfree(core);
++ return NULL;
++ }
++
+ if (0 != cx88_get_resources(core, pci)) {
++ v4l2_device_unregister(&core->v4l2_dev);
+ kfree(core);
+ return NULL;
+ }
+@@ -3111,6 +3161,11 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr)
+ pci_resource_len(pci, 0));
+ core->bmmio = (u8 __iomem *)core->lmmio;
+
++ if (core->lmmio == NULL) {
++ kfree(core);
++ return NULL;
++ }
++
+ /* board config */
+ core->boardnr = UNSET;
+ if (card[core->nr] < ARRAY_SIZE(cx88_boards))
+@@ -3149,8 +3204,36 @@ struct cx88_core *cx88_core_create(struct pci_dev *pci, int nr)
+ cx88_i2c_init(core, pci);
+
+ /* load tuner module, if needed */
+- if (TUNER_ABSENT != core->board.tuner_type)
+- request_module("tuner");
++ if (TUNER_ABSENT != core->board.tuner_type) {
++ /* Ignore 0x6b and 0x6f on cx88 boards.
++ * FusionHDTV5 RT Gold has an ir receiver at 0x6b
++ * and an RTC at 0x6f which can get corrupted if probed. */
++ static const unsigned short tv_addrs[] = {
++ 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
++ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
++ 0x68, 0x69, 0x6a, 0x6c, 0x6d, 0x6e,
++ I2C_CLIENT_END
++ };
++ int has_demod = (core->board.tda9887_conf & TDA9887_PRESENT);
++
++ /* I don't trust the radio_type as is stored in the card
++ definitions, so we just probe for it.
++ The radio_type is sometimes missing, or set to UNSET but
++ later code configures a tea5767.
++ */
++ v4l2_i2c_new_probed_subdev(&core->i2c_adap, "tuner", "tuner",
++ v4l2_i2c_tuner_addrs(ADDRS_RADIO));
++ if (has_demod)
++ v4l2_i2c_new_probed_subdev(&core->i2c_adap, "tuner",
++ "tuner", v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
++ if (core->board.tuner_addr == ADDR_UNSET) {
++ v4l2_i2c_new_probed_subdev(&core->i2c_adap, "tuner",
++ "tuner", has_demod ? tv_addrs + 4 : tv_addrs);
++ } else {
++ v4l2_i2c_new_subdev(&core->i2c_adap,
++ "tuner", "tuner", core->board.tuner_addr);
++ }
++ }
+
+ cx88_card_setup(core);
+ cx88_ir_init(core, pci);
+diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c
+index b045874..f2fb9f3 100644
+--- a/drivers/media/video/cx88/cx88-core.c
++++ b/drivers/media/video/cx88/cx88-core.c
+@@ -991,7 +991,7 @@ int cx88_set_tvnorm(struct cx88_core *core, v4l2_std_id norm)
+ set_tvaudio(core);
+
+ // tell i2c chips
+- cx88_call_i2c_clients(core,VIDIOC_S_STD,&norm);
++ call_all(core, tuner, s_std, norm);
+
+ // done
+ return 0;
+@@ -1011,7 +1011,8 @@ struct video_device *cx88_vdev_init(struct cx88_core *core,
+ return NULL;
+ *vfd = *template;
+ vfd->minor = -1;
+- vfd->parent = &pci->dev;
++ vfd->v4l2_dev = &core->v4l2_dev;
++ vfd->parent = &pci->dev;
+ vfd->release = video_device_release;
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
+ core->name, type, core->board.name);
+@@ -1058,12 +1059,16 @@ void cx88_core_put(struct cx88_core *core, struct pci_dev *pci)
+
+ mutex_lock(&devlist);
+ cx88_ir_fini(core);
+- if (0 == core->i2c_rc)
++ if (0 == core->i2c_rc) {
++ if (core->i2c_rtc)
++ i2c_unregister_device(core->i2c_rtc);
+ i2c_del_adapter(&core->i2c_adap);
++ }
+ list_del(&core->devlist);
+ iounmap(core->lmmio);
+ cx88_devcount--;
+ mutex_unlock(&devlist);
++ v4l2_device_unregister(&core->v4l2_dev);
+ kfree(core);
+ }
+
+diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
+index aef5297..4ff4d9f 100644
+--- a/drivers/media/video/cx88/cx88-dvb.c
++++ b/drivers/media/video/cx88/cx88-dvb.c
+@@ -241,6 +241,12 @@ static struct mt352_config dvico_fusionhdtv_dual = {
+ .demod_init = dvico_dual_demod_init,
+ };
+
++static struct zl10353_config cx88_terratec_cinergy_ht_pci_mkii_config = {
++ .demod_address = (0x1e >> 1),
++ .no_tuner = 1,
++ .if2 = 45600,
++};
++
+ #if defined(CONFIG_VIDEO_CX88_VP3054) || (defined(CONFIG_VIDEO_CX88_VP3054_MODULE) && defined(MODULE))
+ static int dntv_live_dvbt_pro_demod_init(struct dvb_frontend* fe)
+ {
+@@ -1131,6 +1137,16 @@ static int dvb_register(struct cx8802_dev *dev)
+ if (fe0->dvb.frontend != NULL)
+ fe0->dvb.frontend->ops.set_voltage = tevii_dvbs_set_voltage;
+ break;
++ case CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII:
++ fe0->dvb.frontend = dvb_attach(zl10353_attach,
++ &cx88_terratec_cinergy_ht_pci_mkii_config,
++ &core->i2c_adap);
++ if (fe0->dvb.frontend) {
++ fe0->dvb.frontend->ops.i2c_gate_ctrl = NULL;
++ if (attach_xc3028(0x61, dev) < 0)
++ goto frontend_detach;
++ }
++ break;
+ default:
+ printk(KERN_ERR "%s/2: The frontend of your DVB/ATSC card isn't supported yet\n",
+ core->name);
+@@ -1152,7 +1168,7 @@ static int dvb_register(struct cx8802_dev *dev)
+ fe1->dvb.frontend->ops.ts_bus_ctrl = cx88_dvb_bus_ctrl;
+
+ /* Put the analog decoder in standby to keep it quiet */
+- cx88_call_i2c_clients(core, TUNER_SET_STANDBY, NULL);
++ call_all(core, core, s_standby, 0);
+
+ /* register everything */
+ return videobuf_dvb_register_bus(&dev->frontends, THIS_MODULE, dev,
+diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c
+index c0ff230..996b4ed 100644
+--- a/drivers/media/video/cx88/cx88-i2c.c
++++ b/drivers/media/video/cx88/cx88-i2c.c
+@@ -97,37 +97,6 @@ static int cx8800_bit_getsda(void *data)
+
+ /* ----------------------------------------------------------------------- */
+
+-static int attach_inform(struct i2c_client *client)
+-{
+- struct cx88_core *core = i2c_get_adapdata(client->adapter);
+-
+- dprintk(1, "%s i2c attach [addr=0x%x,client=%s]\n",
+- client->driver->driver.name, client->addr, client->name);
+- return 0;
+-}
+-
+-static int detach_inform(struct i2c_client *client)
+-{
+- struct cx88_core *core = i2c_get_adapdata(client->adapter);
+-
+- dprintk(1, "i2c detach [client=%s]\n", client->name);
+- return 0;
+-}
+-
+-void cx88_call_i2c_clients(struct cx88_core *core, unsigned int cmd, void *arg)
+-{
+- if (0 != core->i2c_rc)
+- return;
+-
+- if (core->gate_ctrl)
+- core->gate_ctrl(core, 1);
+-
+- i2c_clients_command(&core->i2c_adap, cmd, arg);
+-
+- if (core->gate_ctrl)
+- core->gate_ctrl(core, 0);
+-}
+-
+ static const struct i2c_algo_bit_data cx8800_i2c_algo_template = {
+ .setsda = cx8800_bit_setsda,
+ .setscl = cx8800_bit_setscl,
+@@ -173,20 +142,14 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci)
+ memcpy(&core->i2c_algo, &cx8800_i2c_algo_template,
+ sizeof(core->i2c_algo));
+
+- if (core->board.tuner_type != TUNER_ABSENT)
+- core->i2c_adap.class |= I2C_CLASS_TV_ANALOG;
+- if (core->board.mpeg & CX88_MPEG_DVB)
+- core->i2c_adap.class |= I2C_CLASS_TV_DIGITAL;
+
+ core->i2c_adap.dev.parent = &pci->dev;
+ strlcpy(core->i2c_adap.name,core->name,sizeof(core->i2c_adap.name));
+ core->i2c_adap.owner = THIS_MODULE;
+ core->i2c_adap.id = I2C_HW_B_CX2388x;
+- core->i2c_adap.client_register = attach_inform;
+- core->i2c_adap.client_unregister = detach_inform;
+ core->i2c_algo.udelay = i2c_udelay;
+ core->i2c_algo.data = core;
+- i2c_set_adapdata(&core->i2c_adap,core);
++ i2c_set_adapdata(&core->i2c_adap, &core->v4l2_dev);
+ core->i2c_adap.algo_data = &core->i2c_algo;
+ core->i2c_client.adapter = &core->i2c_adap;
+ strlcpy(core->i2c_client.name, "cx88xx internal", I2C_NAME_SIZE);
+@@ -222,8 +185,6 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci)
+
+ /* ----------------------------------------------------------------------- */
+
+-EXPORT_SYMBOL(cx88_call_i2c_clients);
+-
+ /*
+ * Local variables:
+ * c-basic-offset: 8
+diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
+index 8683d10..ec05312 100644
+--- a/drivers/media/video/cx88/cx88-input.c
++++ b/drivers/media/video/cx88/cx88-input.c
+@@ -48,8 +48,7 @@ struct cx88_IR {
+
+ /* poll external decoder */
+ int polling;
+- struct work_struct work;
+- struct timer_list timer;
++ struct delayed_work work;
+ u32 gpio_addr;
+ u32 last_gpio;
+ u32 mask_keycode;
+@@ -143,27 +142,19 @@ static void cx88_ir_handle_key(struct cx88_IR *ir)
+ }
+ }
+
+-static void ir_timer(unsigned long data)
+-{
+- struct cx88_IR *ir = (struct cx88_IR *)data;
+-
+- schedule_work(&ir->work);
+-}
+-
+ static void cx88_ir_work(struct work_struct *work)
+ {
+- struct cx88_IR *ir = container_of(work, struct cx88_IR, work);
++ struct cx88_IR *ir = container_of(work, struct cx88_IR, work.work);
+
+ cx88_ir_handle_key(ir);
+- mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
++ schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
+ }
+
+ void cx88_ir_start(struct cx88_core *core, struct cx88_IR *ir)
+ {
+ if (ir->polling) {
+- setup_timer(&ir->timer, ir_timer, (unsigned long)ir);
+- INIT_WORK(&ir->work, cx88_ir_work);
+- schedule_work(&ir->work);
++ INIT_DELAYED_WORK(&ir->work, cx88_ir_work);
++ schedule_delayed_work(&ir->work, 0);
+ }
+ if (ir->sampling) {
+ core->pci_irqmask |= PCI_INT_IR_SMPINT;
+@@ -179,10 +170,8 @@ void cx88_ir_stop(struct cx88_core *core, struct cx88_IR *ir)
+ core->pci_irqmask &= ~PCI_INT_IR_SMPINT;
+ }
+
+- if (ir->polling) {
+- del_timer_sync(&ir->timer);
+- flush_scheduled_work();
+- }
++ if (ir->polling)
++ cancel_delayed_work_sync(&ir->work);
+ }
+
+ /* ---------------------------------------------------------------------- */
+@@ -226,6 +215,8 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
++ case CX88_BOARD_PCHDTV_HD3000:
++ case CX88_BOARD_PCHDTV_HD5500:
+ ir_codes = ir_codes_hauppauge_new;
+ ir_type = IR_TYPE_RC5;
+ ir->sampling = 1;
+@@ -466,6 +457,8 @@ void cx88_ir_irq(struct cx88_core *core)
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000:
+ case CX88_BOARD_HAUPPAUGE_HVR4000LITE:
++ case CX88_BOARD_PCHDTV_HD3000:
++ case CX88_BOARD_PCHDTV_HD5500:
+ ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7);
+ ir_dprintk("biphase decoded: %x\n", ircode);
+ /*
+diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
+index 791e69d..434237a 100644
+--- a/drivers/media/video/cx88/cx88-video.c
++++ b/drivers/media/video/cx88/cx88-video.c
+@@ -41,11 +41,6 @@
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-ioctl.h>
+
+-#ifdef CONFIG_VIDEO_V4L1_COMPAT
+-/* Include V4L1 specific functions. Should be removed soon */
+-#include <linux/videodev.h>
+-#endif
+-
+ MODULE_DESCRIPTION("v4l2 driver module for cx2388x based TV cards");
+ MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+ MODULE_LICENSE("GPL");
+@@ -298,6 +293,7 @@ static struct cx88_ctrl cx8800_ctls[] = {
+ };
+ static const int CX8800_CTLS = ARRAY_SIZE(cx8800_ctls);
+
++/* Must be sorted from low to high control ID! */
+ const u32 cx88_user_ctrls[] = {
+ V4L2_CID_USER_CLASS,
+ V4L2_CID_BRIGHTNESS,
+@@ -435,8 +431,7 @@ int cx88_video_mux(struct cx88_core *core, unsigned int input)
+ struct v4l2_routing route;
+
+ route.input = INPUT(input).audioroute;
+- cx88_call_i2c_clients(core,
+- VIDIOC_INT_S_AUDIO_ROUTING, &route);
++ call_all(core, audio, s_routing, &route);
+ }
+ /* cx2388's C-ADC is connected to the tuner only.
+ When used with S-Video, that ADC is busy dealing with
+@@ -831,8 +826,7 @@ static int video_open(struct file *file)
+ struct v4l2_routing route;
+
+ route.input = core->board.radio.audioroute;
+- cx88_call_i2c_clients(core,
+- VIDIOC_INT_S_AUDIO_ROUTING, &route);
++ call_all(core, audio, s_routing, &route);
+ }
+ /* "I2S ADC mode" */
+ core->tvaudio = WW_I2SADC;
+@@ -843,7 +837,7 @@ static int video_open(struct file *file)
+ cx88_set_tvaudio(core);
+ cx88_set_stereo(core,V4L2_TUNER_MODE_STEREO,1);
+ }
+- cx88_call_i2c_clients(core,AUDC_SET_RADIO,NULL);
++ call_all(core, tuner, s_radio);
+ }
+ unlock_kernel();
+
+@@ -937,7 +931,7 @@ static int video_release(struct file *file)
+ kfree(fh);
+
+ if(atomic_dec_and_test(&dev->core->users))
+- cx88_call_i2c_clients (dev->core, TUNER_SET_STANDBY, NULL);
++ call_all(dev->core, core, s_standby, 0);
+
+ return 0;
+ }
+@@ -1276,15 +1270,12 @@ int cx88_enum_input (struct cx88_core *core,struct v4l2_input *i)
+ [ CX88_VMUX_DVB ] = "DVB",
+ [ CX88_VMUX_DEBUG ] = "for debug only",
+ };
+- unsigned int n;
++ unsigned int n = i->index;
+
+- 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 ((CX88_VMUX_TELEVISION == INPUT(n).type) ||
+@@ -1402,7 +1393,7 @@ static int vidioc_g_frequency (struct file *file, void *priv,
+ f->type = fh->radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV;
+ f->frequency = core->freq;
+
+- cx88_call_i2c_clients(core,VIDIOC_G_FREQUENCY,f);
++ call_all(core, tuner, g_frequency, f);
+
+ return 0;
+ }
+@@ -1418,7 +1409,7 @@ int cx88_set_freq (struct cx88_core *core,
+ mutex_lock(&core->lock);
+ core->freq = f->frequency;
+ cx88_newstation(core);
+- cx88_call_i2c_clients(core,VIDIOC_S_FREQUENCY,f);
++ call_all(core, tuner, s_frequency, f);
+
+ /* When changing channels it is required to reset TVAUDIO */
+ msleep (10);
+@@ -1500,7 +1491,7 @@ static int radio_g_tuner (struct file *file, void *priv,
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+- cx88_call_i2c_clients(core,VIDIOC_G_TUNER,t);
++ call_all(core, tuner, g_tuner, t);
+ return 0;
+ }
+
+@@ -1520,7 +1511,6 @@ static int radio_g_audio (struct file *file, void *priv, struct v4l2_audio *a)
+ if (unlikely(a->index))
+ return -EINVAL;
+
+- memset(a,0,sizeof(*a));
+ strcpy(a->name,"Radio");
+ return 0;
+ }
+@@ -1535,7 +1525,7 @@ static int radio_s_tuner (struct file *file, void *priv,
+ if (0 != t->index)
+ return -EINVAL;
+
+- cx88_call_i2c_clients(core,VIDIOC_S_TUNER,t);
++ call_all(core, tuner, s_tuner, t);
+
+ return 0;
+ }
+@@ -1892,12 +1882,30 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
+ /* load and configure helper modules */
+
+ if (core->board.audio_chip == V4L2_IDENT_WM8775)
+- request_module("wm8775");
++ v4l2_i2c_new_subdev(&core->i2c_adap,
++ "wm8775", "wm8775", 0x36 >> 1);
++
++ if (core->board.audio_chip == V4L2_IDENT_TVAUDIO) {
++ /* This probes for a tda9874 as is used on some
++ Pixelview Ultra boards. */
++ static const unsigned short i2c_addr[] = {
++ 0xb0 >> 1, I2C_CLIENT_END
++ };
++
++ v4l2_i2c_new_probed_subdev(&core->i2c_adap,
++ "tvaudio", "tvaudio", i2c_addr);
++ }
+
+ switch (core->boardnr) {
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_GOLD:
+- case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD:
++ case CX88_BOARD_DVICO_FUSIONHDTV_7_GOLD: {
++ static struct i2c_board_info rtc_info = {
++ I2C_BOARD_INFO("isl1208", 0x6f)
++ };
++
+ request_module("rtc-isl1208");
++ core->i2c_rtc = i2c_new_device(&core->i2c_adap, &rtc_info);
++ }
+ /* break intentionally omitted */
+ case CX88_BOARD_DVICO_FUSIONHDTV_5_PCI_NANO:
+ request_module("ir-kbd-i2c");
+diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
+index 6025fdd..9a43fdf 100644
+--- a/drivers/media/video/cx88/cx88.h
++++ b/drivers/media/video/cx88/cx88.h
+@@ -25,7 +25,7 @@
+ #include <linux/videodev2.h>
+ #include <linux/kdev_t.h>
+
+-#include <media/v4l2-common.h>
++#include <media/v4l2-device.h>
+ #include <media/tuner.h>
+ #include <media/tveeprom.h>
+ #include <media/videobuf-dma-sg.h>
+@@ -231,6 +231,7 @@ extern struct sram_channel cx88_sram_channels[];
+ #define CX88_BOARD_SATTRADE_ST4200 76
+ #define CX88_BOARD_TBS_8910 77
+ #define CX88_BOARD_PROF_6200 78
++#define CX88_BOARD_TERRATEC_CINERGY_HT_PCI_MKII 79
+
+ enum cx88_itype {
+ CX88_VMUX_COMPOSITE1 = 1,
+@@ -302,7 +303,6 @@ struct cx88_dmaqueue {
+ struct btcx_riscmem stopper;
+ u32 count;
+ };
+-struct cx88_core;
+
+ struct cx88_core {
+ struct list_head devlist;
+@@ -327,6 +327,8 @@ struct cx88_core {
+ u32 i2c_state, i2c_rc;
+
+ /* config info -- analog */
++ struct v4l2_device v4l2_dev;
++ struct i2c_client *i2c_rtc;
+ unsigned int boardnr;
+ struct cx88_board board;
+
+@@ -365,6 +367,22 @@ struct cx88_core {
+ int active_fe_id;
+ };
+
++static inline struct cx88_core *to_core(struct v4l2_device *v4l2_dev)
++{
++ return container_of(v4l2_dev, struct cx88_core, v4l2_dev);
++}
++
++#define call_all(core, o, f, args...) \
++ do { \
++ if (!core->i2c_rc) { \
++ if (core->gate_ctrl) \
++ core->gate_ctrl(core, 1); \
++ v4l2_device_call_all(&core->v4l2_dev, 0, o, f, ##args); \
++ if (core->gate_ctrl) \
++ core->gate_ctrl(core, 0); \
++ } \
++ } while (0)
++
+ struct cx8800_dev;
+ struct cx8802_dev;
+
+@@ -610,8 +628,6 @@ extern struct videobuf_queue_ops cx8800_vbi_qops;
+ /* cx88-i2c.c */
+
+ extern int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci);
+-extern void cx88_call_i2c_clients(struct cx88_core *core,
+- unsigned int cmd, void *arg);
+
+
+ /* ----------------------------------------------------------- */
+diff --git a/drivers/media/video/dabusb.c b/drivers/media/video/dabusb.c
+index 298810d..ba3709b 100644
+--- a/drivers/media/video/dabusb.c
++++ b/drivers/media/video/dabusb.c
+@@ -189,17 +189,20 @@ static void dabusb_iso_complete (struct urb *purb)
+ dst += len;
+ }
+ else
+- err("dabusb_iso_complete: invalid len %d", len);
++ dev_err(&purb->dev->dev,
++ "dabusb_iso_complete: invalid len %d\n", len);
+ }
+ else
+ dev_warn(&purb->dev->dev, "dabusb_iso_complete: corrupted packet status: %d\n", purb->iso_frame_desc[i].status);
+ if (dst != purb->actual_length)
+- err("dst!=purb->actual_length:%d!=%d", dst, purb->actual_length);
++ dev_err(&purb->dev->dev,
++ "dst!=purb->actual_length:%d!=%d\n",
++ dst, purb->actual_length);
+ }
+
+ if (atomic_dec_and_test (&s->pending_io) && !s->remove_pending && s->state != _stopped) {
+ s->overruns++;
+- err("overrun (%d)", s->overruns);
++ dev_err(&purb->dev->dev, "overrun (%d)\n", s->overruns);
+ }
+ wake_up (&s->wait);
+ }
+@@ -220,13 +223,14 @@ static int dabusb_alloc_buffers (pdabusb_t s)
+ while (transfer_len < (s->total_buffer_size << 10)) {
+ b = kzalloc(sizeof (buff_t), GFP_KERNEL);
+ if (!b) {
+- err("kzalloc(sizeof(buff_t))==NULL");
++ dev_err(&s->usbdev->dev,
++ "kzalloc(sizeof(buff_t))==NULL\n");
+ goto err;
+ }
+ b->s = s;
+ b->purb = usb_alloc_urb(packets, GFP_KERNEL);
+ if (!b->purb) {
+- err("usb_alloc_urb == NULL");
++ dev_err(&s->usbdev->dev, "usb_alloc_urb == NULL\n");
+ kfree (b);
+ goto err;
+ }
+@@ -235,7 +239,8 @@ static int dabusb_alloc_buffers (pdabusb_t s)
+ if (!b->purb->transfer_buffer) {
+ kfree (b->purb);
+ kfree (b);
+- err("kmalloc(%d)==NULL", transfer_buffer_length);
++ dev_err(&s->usbdev->dev,
++ "kmalloc(%d)==NULL\n", transfer_buffer_length);
+ goto err;
+ }
+
+@@ -279,10 +284,11 @@ static int dabusb_bulk (pdabusb_t s, pbulk_transfer_t pb)
+
+ ret=usb_bulk_msg(s->usbdev, pipe, pb->data, pb->size, &actual_length, 100);
+ if(ret<0) {
+- err("dabusb: usb_bulk_msg failed(%d)",ret);
++ dev_err(&s->usbdev->dev,
++ "usb_bulk_msg failed(%d)\n", ret);
+
+ if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) {
+- err("set_interface failed");
++ dev_err(&s->usbdev->dev, "set_interface failed\n");
+ return -EINVAL;
+ }
+
+@@ -291,7 +297,7 @@ static int dabusb_bulk (pdabusb_t s, pbulk_transfer_t pb)
+ if( ret == -EPIPE ) {
+ dev_warn(&s->usbdev->dev, "CLEAR_FEATURE request to remove STALL condition.\n");
+ if(usb_clear_halt(s->usbdev, usb_pipeendpoint(pipe)))
+- err("request failed");
++ dev_err(&s->usbdev->dev, "request failed\n");
+ }
+
+ pb->size = actual_length;
+@@ -305,7 +311,8 @@ static int dabusb_writemem (pdabusb_t s, int pos, const unsigned char *data,
+ unsigned char *transfer_buffer = kmalloc (len, GFP_KERNEL);
+
+ if (!transfer_buffer) {
+- err("dabusb_writemem: kmalloc(%d) failed.", len);
++ dev_err(&s->usbdev->dev,
++ "dabusb_writemem: kmalloc(%d) failed.\n", len);
+ return -ENOMEM;
+ }
+
+@@ -327,13 +334,14 @@ static int dabusb_loadmem (pdabusb_t s, const char *fname)
+ {
+ int ret;
+ const struct ihex_binrec *rec;
+- const struct firmware *fw;
++ const struct firmware *uninitialized_var(fw);
+
+ dbg("Enter dabusb_loadmem (internal)");
+
+ ret = request_ihex_firmware(&fw, "dabusb/firmware.fw", &s->usbdev->dev);
+ if (ret) {
+- err("Failed to load \"dabusb/firmware.fw\": %d\n", ret);
++ dev_err(&s->usbdev->dev,
++ "Failed to load \"dabusb/firmware.fw\": %d\n", ret);
+ goto out;
+ }
+ ret = dabusb_8051_reset (s, 1);
+@@ -346,9 +354,10 @@ static int dabusb_loadmem (pdabusb_t s, const char *fname)
+ ret = dabusb_writemem(s, be32_to_cpu(rec->addr), rec->data,
+ be16_to_cpu(rec->len));
+ if (ret < 0) {
+- err("dabusb_writemem failed (%d %04X %p %d)", ret,
+- be32_to_cpu(rec->addr), rec->data,
+- be16_to_cpu(rec->len));
++ dev_err(&s->usbdev->dev,
++ "dabusb_writemem failed (%d %04X %p %d)\n",
++ ret, be32_to_cpu(rec->addr),
++ rec->data, be16_to_cpu(rec->len));
+ break;
+ }
+ }
+@@ -396,13 +405,15 @@ static int dabusb_fpga_download (pdabusb_t s, const char *fname)
+ dbg("Enter dabusb_fpga_download (internal)");
+
+ if (!b) {
+- err("kmalloc(sizeof(bulk_transfer_t))==NULL");
++ dev_err(&s->usbdev->dev,
++ "kmalloc(sizeof(bulk_transfer_t))==NULL\n");
+ return -ENOMEM;
+ }
+
+ ret = request_firmware(&fw, "dabusb/bitstream.bin", &s->usbdev->dev);
+ if (ret) {
+- err("Failed to load \"dabusb/bitstream.bin\": %d\n", ret);
++ dev_err(&s->usbdev->dev,
++ "Failed to load \"dabusb/bitstream.bin\": %d\n", ret);
+ kfree(b);
+ return ret;
+ }
+@@ -425,7 +436,7 @@ static int dabusb_fpga_download (pdabusb_t s, const char *fname)
+ memcpy (b->data + 4, fw->data + 74 + n, 60);
+ ret = dabusb_bulk (s, b);
+ if (ret < 0) {
+- err("dabusb_bulk failed.");
++ dev_err(&s->usbdev->dev, "dabusb_bulk failed.\n");
+ break;
+ }
+ mdelay (1);
+@@ -478,9 +489,11 @@ static int dabusb_startrek (pdabusb_t s)
+
+ ret = usb_submit_urb (end->purb, GFP_KERNEL);
+ if (ret) {
+- err("usb_submit_urb returned:%d", ret);
++ dev_err(&s->usbdev->dev,
++ "usb_submit_urb returned:%d\n", ret);
+ if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list))
+- err("startrek: dabusb_add_buf_tail failed");
++ dev_err(&s->usbdev->dev,
++ "startrek: dabusb_add_buf_tail failed\n");
+ break;
+ }
+ else
+@@ -523,7 +536,8 @@ static ssize_t dabusb_read (struct file *file, char __user *buf, size_t count, l
+
+ spin_unlock_irqrestore(&s->lock, flags);
+
+- err("error: rec_buf_list is empty");
++ dev_err(&s->usbdev->dev,
++ "error: rec_buf_list is empty\n");
+ goto err;
+ }
+
+@@ -552,7 +566,8 @@ static ssize_t dabusb_read (struct file *file, char __user *buf, size_t count, l
+
+ if (list_empty (&s->rec_buff_list)) {
+ spin_unlock_irqrestore(&s->lock, flags);
+- err("error: still no buffer available.");
++ dev_err(&s->usbdev->dev,
++ "error: still no buffer available.\n");
+ goto err;
+ }
+ spin_unlock_irqrestore(&s->lock, flags);
+@@ -573,7 +588,7 @@ static ssize_t dabusb_read (struct file *file, char __user *buf, size_t count, l
+ dbg("copy_to_user:%p %p %d",buf, purb->transfer_buffer + s->readptr, cnt);
+
+ if (copy_to_user (buf, purb->transfer_buffer + s->readptr, cnt)) {
+- err("read: copy_to_user failed");
++ dev_err(&s->usbdev->dev, "read: copy_to_user failed\n");
+ if (!ret)
+ ret = -EFAULT;
+ goto err;
+@@ -587,7 +602,8 @@ static ssize_t dabusb_read (struct file *file, char __user *buf, size_t count, l
+ if (s->readptr == purb->actual_length) {
+ // finished, take next buffer
+ if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list))
+- err("read: dabusb_add_buf_tail failed");
++ dev_err(&s->usbdev->dev,
++ "read: dabusb_add_buf_tail failed\n");
+ s->readptr = 0;
+ }
+ }
+@@ -623,7 +639,7 @@ static int dabusb_open (struct inode *inode, struct file *file)
+ }
+ if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) {
+ mutex_unlock(&s->mutex);
+- err("set_interface failed");
++ dev_err(&s->usbdev->dev, "set_interface failed\n");
+ return -EINVAL;
+ }
+ s->opened = 1;
+@@ -648,7 +664,7 @@ static int dabusb_release (struct inode *inode, struct file *file)
+
+ if (!s->remove_pending) {
+ if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0)
+- err("set_interface failed");
++ dev_err(&s->usbdev->dev, "set_interface failed\n");
+ }
+ else
+ wake_up (&s->remove_ok);
+@@ -657,7 +673,7 @@ static int dabusb_release (struct inode *inode, struct file *file)
+ return 0;
+ }
+
+-static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
++static long dabusb_ioctl (struct file *file, unsigned int cmd, unsigned long arg)
+ {
+ pdabusb_t s = (pdabusb_t) file->private_data;
+ pbulk_transfer_t pbulk;
+@@ -666,13 +682,17 @@ static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cm
+
+ dbg("dabusb_ioctl");
+
+- if (s->remove_pending)
++ lock_kernel();
++ if (s->remove_pending) {
++ unlock_kernel();
+ return -EIO;
++ }
+
+ mutex_lock(&s->mutex);
+
+ if (!s->usbdev) {
+ mutex_unlock(&s->mutex);
++ unlock_kernel();
+ return -EIO;
+ }
+
+@@ -713,6 +733,7 @@ static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cm
+ break;
+ }
+ mutex_unlock(&s->mutex);
++ unlock_kernel();
+ return ret;
+ }
+
+@@ -721,7 +742,7 @@ static const struct file_operations dabusb_fops =
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .read = dabusb_read,
+- .ioctl = dabusb_ioctl,
++ .unlocked_ioctl = dabusb_ioctl,
+ .open = dabusb_open,
+ .release = dabusb_release,
+ };
+@@ -764,7 +785,7 @@ static int dabusb_probe (struct usb_interface *intf,
+ s->devnum = intf->minor;
+
+ if (usb_reset_configuration (usbdev) < 0) {
+- err("reset_configuration failed");
++ dev_err(&intf->dev, "reset_configuration failed\n");
+ goto reject;
+ }
+ if (le16_to_cpu(usbdev->descriptor.idProduct) == 0x2131) {
+@@ -775,7 +796,7 @@ static int dabusb_probe (struct usb_interface *intf,
+ dabusb_fpga_download (s, NULL);
+
+ if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) {
+- err("set_interface failed");
++ dev_err(&intf->dev, "set_interface failed\n");
+ goto reject;
+ }
+ }
+diff --git a/drivers/media/video/em28xx/em28xx-audio.c b/drivers/media/video/em28xx/em28xx-audio.c
+index 2ac738f..6b96275 100644
+--- a/drivers/media/video/em28xx/em28xx-audio.c
++++ b/drivers/media/video/em28xx/em28xx-audio.c
+@@ -56,7 +56,7 @@ MODULE_PARM_DESC(debug, "activates debug info");
+
+ static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX;
+
+-static int em28xx_isoc_audio_deinit(struct em28xx *dev)
++static int em28xx_deinit_isoc_audio(struct em28xx *dev)
+ {
+ int i;
+
+@@ -66,6 +66,7 @@ static int em28xx_isoc_audio_deinit(struct em28xx *dev)
+ usb_kill_urb(dev->adev.urb[i]);
+ else
+ usb_unlink_urb(dev->adev.urb[i]);
++
+ usb_free_urb(dev->adev.urb[i]);
+ dev->adev.urb[i] = NULL;
+
+@@ -87,6 +88,20 @@ static void em28xx_audio_isocirq(struct urb *urb)
+ unsigned int stride;
+ struct snd_pcm_substream *substream;
+ struct snd_pcm_runtime *runtime;
++
++ switch (urb->status) {
++ case 0: /* success */
++ case -ETIMEDOUT: /* NAK */
++ break;
++ case -ECONNRESET: /* kill */
++ case -ENOENT:
++ case -ESHUTDOWN:
++ return;
++ default: /* error */
++ dprintk("urb completition error %d.\n", urb->status);
++ break;
++ }
++
+ if (dev->adev.capture_pcm_substream) {
+ substream = dev->adev.capture_pcm_substream;
+ runtime = substream->runtime;
+@@ -137,9 +152,6 @@ static void em28xx_audio_isocirq(struct urb *urb)
+ }
+ 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",
+@@ -197,8 +209,7 @@ static int em28xx_init_audio_isoc(struct em28xx *dev)
+ 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);
+-
++ em28xx_deinit_isoc_audio(dev);
+ return errCode;
+ }
+ }
+@@ -213,14 +224,16 @@ static int em28xx_cmd(struct em28xx *dev, int cmd, int arg)
+
+ switch (cmd) {
+ case EM28XX_CAPTURE_STREAM_EN:
+- if (dev->adev.capture_stream == STREAM_OFF && arg == 1) {
++ if (dev->adev.capture_stream == STREAM_OFF &&
++ arg == EM28XX_START_AUDIO) {
+ dev->adev.capture_stream = STREAM_ON;
+ em28xx_init_audio_isoc(dev);
+- } else if (dev->adev.capture_stream == STREAM_ON && arg == 0) {
++ } else if (dev->adev.capture_stream == STREAM_ON &&
++ arg == EM28XX_STOP_AUDIO) {
+ dev->adev.capture_stream = STREAM_OFF;
+- em28xx_isoc_audio_deinit(dev);
++ em28xx_deinit_isoc_audio(dev);
+ } else {
+- printk(KERN_ERR "An underrun very likely occurred. "
++ em28xx_errdev("An underrun very likely occurred. "
+ "Ignoring it.\n");
+ }
+ return 0;
+@@ -234,7 +247,7 @@ static int snd_pcm_alloc_vmalloc_buffer(struct snd_pcm_substream *subs,
+ {
+ struct snd_pcm_runtime *runtime = subs->runtime;
+
+- dprintk("Alocating vbuffer\n");
++ dprintk("Allocating vbuffer\n");
+ if (runtime->dma_area) {
+ if (runtime->dma_bytes > size)
+ return 0;
+@@ -302,7 +315,9 @@ static int snd_em28xx_capture_open(struct snd_pcm_substream *substream)
+ dprintk("changing alternate number to 7\n");
+ }
+
++ mutex_lock(&dev->lock);
+ dev->adev.users++;
++ mutex_unlock(&dev->lock);
+
+ snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS);
+ dev->adev.capture_pcm_substream = substream;
+@@ -317,22 +332,15 @@ err:
+ 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;
+ mutex_lock(&dev->lock);
++ dev->adev.users--;
+ em28xx_audio_analog_set(dev);
+ mutex_unlock(&dev->lock);
+
+- 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;
+ }
+
+@@ -363,7 +371,7 @@ static int snd_em28xx_hw_capture_free(struct snd_pcm_substream *substream)
+ dprintk("Stop capture, if needed\n");
+
+ if (dev->adev.capture_stream == STREAM_ON)
+- em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 0);
++ em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, EM28XX_STOP_AUDIO);
+
+ return 0;
+ }
+@@ -377,33 +385,40 @@ static int snd_em28xx_capture_trigger(struct snd_pcm_substream *substream,
+ int cmd)
+ {
+ struct em28xx *dev = snd_pcm_substream_chip(substream);
++ int retval;
+
+- dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START)?
+- "start": "stop");
++ dprintk("Should %s capture\n", (cmd == SNDRV_PCM_TRIGGER_START) ?
++ "start" : "stop");
++
++ spin_lock(&dev->adev.slock);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+- em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, 1);
+- return 0;
++ em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, EM28XX_START_AUDIO);
++ retval = 0;
++ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+- dev->adev.shutdown = 1;
+- return 0;
++ em28xx_cmd(dev, EM28XX_CAPTURE_STREAM_EN, EM28XX_STOP_AUDIO);
++ retval = 0;
++ break;
+ default:
+- return -EINVAL;
++ retval = -EINVAL;
+ }
++
++ spin_unlock(&dev->adev.slock);
++ return retval;
+ }
+
+ static snd_pcm_uframes_t snd_em28xx_capture_pointer(struct snd_pcm_substream
+ *substream)
+ {
+- unsigned long flags;
+-
++ unsigned long flags;
+ struct em28xx *dev;
+ snd_pcm_uframes_t hwptr_done;
+
+ dev = snd_pcm_substream_chip(substream);
+- spin_lock_irqsave(&dev->adev.slock, flags);
++ spin_lock_irqsave(&dev->adev.slock, flags);
+ hwptr_done = dev->adev.hwptr_done_capture;
+- spin_unlock_irqrestore(&dev->adev.slock, flags);
++ spin_unlock_irqrestore(&dev->adev.slock, flags);
+
+ return hwptr_done;
+ }
+diff --git a/drivers/media/video/em28xx/em28xx-cards.c b/drivers/media/video/em28xx/em28xx-cards.c
+index 3b3ca3f..0f48c0f 100644
+--- a/drivers/media/video/em28xx/em28xx-cards.c
++++ b/drivers/media/video/em28xx/em28xx-cards.c
+@@ -122,6 +122,22 @@ static struct em28xx_reg_seq default_tuner_gpio[] = {
+ { -1, -1, -1, -1},
+ };
+
++/* Mute/unmute */
++static struct em28xx_reg_seq compro_unmute_tv_gpio[] = {
++ {EM28XX_R08_GPIO, 5, 7, 10},
++ { -1, -1, -1, -1},
++};
++
++static struct em28xx_reg_seq compro_unmute_svid_gpio[] = {
++ {EM28XX_R08_GPIO, 4, 7, 10},
++ { -1, -1, -1, -1},
++};
++
++static struct em28xx_reg_seq compro_mute_gpio[] = {
++ {EM28XX_R08_GPIO, 6, 7, 10},
++ { -1, -1, -1, -1},
++};
++
+ /*
+ * Board definitions
+ */
+@@ -183,6 +199,25 @@ struct em28xx_board em28xx_boards[] = {
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
+ },
++ [EM2820_BOARD_GADMEI_TVR200] = {
++ .name = "Gadmei TVR200",
++ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
++ .tda9887_conf = TDA9887_PRESENT,
++ .decoder = EM28XX_SAA711X,
++ .input = { {
++ .type = EM28XX_VMUX_TELEVISION,
++ .vmux = SAA7115_COMPOSITE2,
++ .amux = EM28XX_AMUX_LINE_IN,
++ }, {
++ .type = EM28XX_VMUX_COMPOSITE1,
++ .vmux = SAA7115_COMPOSITE0,
++ .amux = EM28XX_AMUX_LINE_IN,
++ }, {
++ .type = EM28XX_VMUX_SVIDEO,
++ .vmux = SAA7115_SVIDEO3,
++ .amux = EM28XX_AMUX_LINE_IN,
++ } },
++ },
+ [EM2820_BOARD_TERRATEC_CINERGY_250] = {
+ .name = "Terratec Cinergy 250 USB",
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+@@ -225,7 +260,7 @@ struct em28xx_board em28xx_boards[] = {
+ .name = "Hauppauge WinTV USB 2",
+ .tuner_type = TUNER_PHILIPS_FM1236_MK3,
+ .tda9887_conf = TDA9887_PRESENT |
+- TDA9887_PORT1_ACTIVE|
++ TDA9887_PORT1_ACTIVE |
+ TDA9887_PORT2_ACTIVE,
+ .decoder = EM28XX_TVP5150,
+ .has_msp34xx = 1,
+@@ -350,26 +385,6 @@ struct em28xx_board em28xx_boards[] = {
+ .amux = EM28XX_AMUX_VIDEO,
+ } },
+ },
+- [EM2821_BOARD_PROLINK_PLAYTV_USB2] = {
+- .name = "SIIG AVTuner-PVR/Prolink PlayTV USB 2.0",
+- .valid = EM28XX_BOARD_NOT_VALIDATED,
+- .tuner_type = TUNER_LG_PAL_NEW_TAPC, /* unknown? */
+- .tda9887_conf = TDA9887_PRESENT, /* unknown? */
+- .decoder = EM28XX_SAA711X,
+- .input = { {
+- .type = EM28XX_VMUX_TELEVISION,
+- .vmux = SAA7115_COMPOSITE2,
+- .amux = EM28XX_AMUX_LINE_IN,
+- }, {
+- .type = EM28XX_VMUX_COMPOSITE1,
+- .vmux = SAA7115_COMPOSITE0,
+- .amux = EM28XX_AMUX_LINE_IN,
+- }, {
+- .type = EM28XX_VMUX_SVIDEO,
+- .vmux = SAA7115_SVIDEO3,
+- .amux = EM28XX_AMUX_LINE_IN,
+- } },
+- },
+ [EM2821_BOARD_SUPERCOMP_USB_2] = {
+ .name = "Supercomp USB 2.0 TV",
+ .valid = EM28XX_BOARD_NOT_VALIDATED,
+@@ -498,7 +513,7 @@ struct em28xx_board em28xx_boards[] = {
+ },
+ [EM2861_BOARD_YAKUMO_MOVIE_MIXER] = {
+ .name = "Yakumo MovieMixer",
+- .tuner_type = TUNER_ABSENT, /* Capture only device */
++ .tuner_type = TUNER_ABSENT, /* Capture only device */
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+@@ -604,6 +619,7 @@ struct em28xx_board em28xx_boards[] = {
+ .mts_firmware = 1,
+ .has_dvb = 1,
+ .dvb_gpio = hauppauge_wintv_hvr_900_digital,
++ .ir_codes = ir_codes_hauppauge_new,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+@@ -628,6 +644,7 @@ struct em28xx_board em28xx_boards[] = {
+ .tuner_type = TUNER_XC2028,
+ .tuner_gpio = default_tuner_gpio,
+ .mts_firmware = 1,
++ .ir_codes = ir_codes_hauppauge_new,
+ .decoder = EM28XX_TVP5150,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+@@ -842,11 +859,11 @@ struct em28xx_board em28xx_boards[] = {
+ } },
+ },
+ [EM2800_BOARD_GRABBEEX_USB2800] = {
+- .name = "eMPIA Technology, Inc. GrabBeeX+ Video Encoder",
+- .is_em2800 = 1,
+- .decoder = EM28XX_SAA711X,
+- .tuner_type = TUNER_ABSENT, /* capture only board */
+- .input = { {
++ .name = "eMPIA Technology, Inc. GrabBeeX+ Video Encoder",
++ .is_em2800 = 1,
++ .decoder = EM28XX_SAA711X,
++ .tuner_type = TUNER_ABSENT, /* capture only board */
++ .input = { {
+ .type = EM28XX_VMUX_COMPOSITE1,
+ .vmux = SAA7115_COMPOSITE0,
+ .amux = EM28XX_AMUX_LINE_IN,
+@@ -897,7 +914,7 @@ struct em28xx_board em28xx_boards[] = {
+ } },
+ },
+ [EM2820_BOARD_PINNACLE_DVC_90] = {
+- .name = "Pinnacle Dazzle DVC 90/DVC 100",
++ .name = "Pinnacle Dazzle DVC 90/100/101/107 / Kaiser Baas Video to DVD maker",
+ .tuner_type = TUNER_ABSENT, /* capture only board */
+ .decoder = EM28XX_SAA711X,
+ .input = { {
+@@ -952,7 +969,7 @@ struct em28xx_board em28xx_boards[] = {
+ } },
+ },
+ [EM2820_BOARD_PROLINK_PLAYTV_USB2] = {
+- .name = "Pixelview Prolink PlayTV USB 2.0",
++ .name = "SIIG AVTuner-PVR / Pixelview Prolink PlayTV USB 2.0",
+ .has_snapshot_button = 1,
+ .tda9887_conf = TDA9887_PRESENT,
+ .tuner_type = TUNER_YMEC_TVF_5533MF,
+@@ -1198,7 +1215,9 @@ struct em28xx_board em28xx_boards[] = {
+ .has_dvb = 1,
+ .dvb_gpio = kworld_330u_digital,
+ .xclk = EM28XX_XCLK_FREQUENCY_12MHZ,
+- .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE | EM28XX_I2C_EEPROM_ON_BOARD | EM28XX_I2C_EEPROM_KEY_VALID,
++ .i2c_speed = EM28XX_I2C_CLK_WAIT_ENABLE |
++ EM28XX_I2C_EEPROM_ON_BOARD |
++ EM28XX_I2C_EEPROM_KEY_VALID,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
+@@ -1223,21 +1242,88 @@ struct em28xx_board em28xx_boards[] = {
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .tda9887_conf = TDA9887_PRESENT,
+ .decoder = EM28XX_TVP5150,
++ .adecoder = EM28XX_TVAUDIO,
++ .mute_gpio = compro_mute_gpio,
+ .input = { {
+ .type = EM28XX_VMUX_TELEVISION,
+ .vmux = TVP5150_COMPOSITE0,
++ .amux = EM28XX_AMUX_VIDEO,
++ .gpio = compro_unmute_tv_gpio,
++ }, {
++ .type = EM28XX_VMUX_SVIDEO,
++ .vmux = TVP5150_SVIDEO,
++ .amux = EM28XX_AMUX_LINE_IN,
++ .gpio = compro_unmute_svid_gpio,
++ } },
++ },
++ [EM2860_BOARD_KAIOMY_TVNPC_U2] = {
++ .name = "Kaiomy TVnPC U2",
++ .vchannels = 3,
++ .tuner_type = TUNER_XC2028,
++ .tuner_addr = 0x61,
++ .mts_firmware = 1,
++ .decoder = EM28XX_TVP5150,
++ .tuner_gpio = default_tuner_gpio,
++ .ir_codes = ir_codes_kaiomy,
++ .input = { {
++ .type = EM28XX_VMUX_TELEVISION,
++ .vmux = TVP5150_COMPOSITE0,
++ .amux = EM28XX_AMUX_VIDEO,
++
++ }, {
++ .type = EM28XX_VMUX_COMPOSITE1,
++ .vmux = TVP5150_COMPOSITE1,
+ .amux = EM28XX_AMUX_LINE_IN,
+ }, {
+ .type = EM28XX_VMUX_SVIDEO,
+ .vmux = TVP5150_SVIDEO,
+ .amux = EM28XX_AMUX_LINE_IN,
+ } },
++ .radio = {
++ .type = EM28XX_RADIO,
++ .amux = EM28XX_AMUX_LINE_IN,
++ }
++ },
++ [EM2860_BOARD_EASYCAP] = {
++ .name = "Easy Cap Capture DC-60",
++ .vchannels = 2,
++ .tuner_type = TUNER_ABSENT,
++ .decoder = EM28XX_SAA711X,
++ .input = { {
++ .type = EM28XX_VMUX_COMPOSITE1,
++ .vmux = SAA7115_COMPOSITE0,
++ .amux = EM28XX_AMUX_LINE_IN,
++ }, {
++ .type = EM28XX_VMUX_SVIDEO,
++ .vmux = SAA7115_SVIDEO3,
++ .amux = EM28XX_AMUX_LINE_IN,
++ } },
++ },
++ [EM2820_BOARD_IODATA_GVMVP_SZ] = {
++ .name = "IO-DATA GV-MVP/SZ",
++ .tuner_type = TUNER_PHILIPS_FM1236_MK3,
++ .tuner_gpio = default_tuner_gpio,
++ .tda9887_conf = TDA9887_PRESENT,
++ .decoder = EM28XX_TVP5150,
++ .input = { {
++ .type = EM28XX_VMUX_TELEVISION,
++ .vmux = TVP5150_COMPOSITE0,
++ .amux = EM28XX_AMUX_VIDEO,
++ }, { /* Composite has not been tested yet */
++ .type = EM28XX_VMUX_COMPOSITE1,
++ .vmux = TVP5150_COMPOSITE1,
++ .amux = EM28XX_AMUX_VIDEO,
++ }, { /* S-video has not been tested yet */
++ .type = EM28XX_VMUX_SVIDEO,
++ .vmux = TVP5150_SVIDEO,
++ .amux = EM28XX_AMUX_VIDEO,
++ } },
+ },
+ };
+ const unsigned int em28xx_bcount = ARRAY_SIZE(em28xx_boards);
+
+ /* table of devices that work with this driver */
+-struct usb_device_id em28xx_id_table [] = {
++struct usb_device_id em28xx_id_table[] = {
+ { USB_DEVICE(0xeb1a, 0x2750),
+ .driver_info = EM2750_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0x2751),
+@@ -1260,6 +1346,8 @@ struct usb_device_id em28xx_id_table [] = {
+ .driver_info = EM2820_BOARD_UNKNOWN },
+ { USB_DEVICE(0xeb1a, 0xe300),
+ .driver_info = EM2861_BOARD_KWORLD_PVRTV_300U },
++ { USB_DEVICE(0xeb1a, 0xe303),
++ .driver_info = EM2860_BOARD_KAIOMY_TVNPC_U2 },
+ { USB_DEVICE(0xeb1a, 0xe305),
+ .driver_info = EM2880_BOARD_KWORLD_DVB_305U },
+ { USB_DEVICE(0xeb1a, 0xe310),
+@@ -1278,6 +1366,8 @@ struct usb_device_id em28xx_id_table [] = {
+ .driver_info = EM2800_BOARD_GRABBEEX_USB2800 },
+ { USB_DEVICE(0xeb1a, 0xe357),
+ .driver_info = EM2870_BOARD_KWORLD_355U },
++ { USB_DEVICE(0x1b80, 0xe302),
++ .driver_info = EM2820_BOARD_PINNACLE_DVC_90 }, /* Kaiser Baas Video to DVD maker */
+ { USB_DEVICE(0x0ccd, 0x0036),
+ .driver_info = EM2820_BOARD_TERRATEC_CINERGY_250 },
+ { USB_DEVICE(0x0ccd, 0x004c),
+@@ -1330,6 +1420,8 @@ struct usb_device_id em28xx_id_table [] = {
+ .driver_info = EM2800_BOARD_LEADTEK_WINFAST_USBII },
+ { USB_DEVICE(0x093b, 0xa005),
+ .driver_info = EM2861_BOARD_PLEXTOR_PX_TV100U },
++ { USB_DEVICE(0x04bb, 0x0515),
++ .driver_info = EM2820_BOARD_IODATA_GVMVP_SZ },
+ { },
+ };
+ MODULE_DEVICE_TABLE(usb, em28xx_id_table);
+@@ -1337,7 +1429,7 @@ MODULE_DEVICE_TABLE(usb, em28xx_id_table);
+ /*
+ * EEPROM hash table for devices with generic USB IDs
+ */
+-static struct em28xx_hash_table em28xx_eeprom_hash [] = {
++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},
+ {0x72cc5a8b, EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2, TUNER_YMEC_TVF_5533MF},
+@@ -1349,6 +1441,7 @@ 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},
+ {0x1ba50080, EM2860_BOARD_POINTNIX_INTRAORAL_CAMERA, TUNER_ABSENT},
++ {0xc51200e3, EM2820_BOARD_GADMEI_TVR200, TUNER_LG_PAL_NEW_TAPC},
+ };
+
+ int em28xx_tuner_callback(void *ptr, int component, int command, int arg)
+@@ -1368,7 +1461,7 @@ int em28xx_tuner_callback(void *ptr, int component, int command, int arg)
+ }
+ EXPORT_SYMBOL_GPL(em28xx_tuner_callback);
+
+-static void inline em28xx_set_model(struct em28xx *dev)
++static inline void em28xx_set_model(struct em28xx *dev)
+ {
+ memcpy(&dev->board, &em28xx_boards[dev->model], sizeof(dev->board));
+
+@@ -1504,6 +1597,34 @@ void em28xx_pre_card_setup(struct em28xx *dev)
+ /* enables audio for that devices */
+ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
+ break;
++
++ case EM2860_BOARD_KAIOMY_TVNPC_U2:
++ em28xx_write_regs(dev, EM28XX_R0F_XCLK, "\x07", 1);
++ em28xx_write_regs(dev, EM28XX_R06_I2C_CLK, "\x40", 1);
++ em28xx_write_regs(dev, 0x0d, "\x42", 1);
++ em28xx_write_regs(dev, 0x08, "\xfd", 1);
++ msleep(10);
++ em28xx_write_regs(dev, 0x08, "\xff", 1);
++ msleep(10);
++ em28xx_write_regs(dev, 0x08, "\x7f", 1);
++ msleep(10);
++ em28xx_write_regs(dev, 0x08, "\x6b", 1);
++
++ break;
++ case EM2860_BOARD_EASYCAP:
++ em28xx_write_regs(dev, 0x08, "\xf8", 1);
++ break;
++
++ case EM2820_BOARD_IODATA_GVMVP_SZ:
++ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xff);
++ msleep(70);
++ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xf7);
++ msleep(10);
++ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfe);
++ msleep(70);
++ em28xx_write_reg(dev, EM28XX_R08_GPIO, 0xfd);
++ msleep(70);
++ break;
+ }
+
+ em28xx_gpio_set(dev, dev->board.tuner_gpio);
+@@ -1610,7 +1731,7 @@ static int em28xx_hint_board(struct em28xx *dev)
+ em28xx_errdev("If the board were missdetected, "
+ "please email this log to:\n");
+ em28xx_errdev("\tV4L Mailing List "
+- " <video4linux-list@redhat.com>\n");
++ " <linux-media@vger.kernel.org>\n");
+ em28xx_errdev("Board detected as %s\n",
+ em28xx_boards[dev->model].name);
+
+@@ -1642,7 +1763,7 @@ static int em28xx_hint_board(struct em28xx *dev)
+ em28xx_errdev("If the board were missdetected, "
+ "please email this log to:\n");
+ em28xx_errdev("\tV4L Mailing List "
+- " <video4linux-list@redhat.com>\n");
++ " <linux-media@vger.kernel.org>\n");
+ em28xx_errdev("Board detected as %s\n",
+ em28xx_boards[dev->model].name);
+
+@@ -1655,7 +1776,7 @@ static int em28xx_hint_board(struct em28xx *dev)
+ em28xx_errdev("You may try to use card=<n> insmod option to "
+ "workaround that.\n");
+ em28xx_errdev("Please send an email with this log to:\n");
+- em28xx_errdev("\tV4L Mailing List <video4linux-list@redhat.com>\n");
++ em28xx_errdev("\tV4L Mailing List <linux-media@vger.kernel.org>\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);
+
+@@ -1800,6 +1921,8 @@ void em28xx_card_setup(struct em28xx *dev)
+ request_module("tvp5150");
+ if (dev->board.tuner_type != TUNER_ABSENT)
+ request_module("tuner");
++ if (dev->board.adecoder == EM28XX_TVAUDIO)
++ request_module("tvaudio");
+ #endif
+
+ em28xx_config_tuner(dev);
+diff --git a/drivers/media/video/em28xx/em28xx-core.c b/drivers/media/video/em28xx/em28xx-core.c
+index 94fb1b6..8f1999c 100644
+--- a/drivers/media/video/em28xx/em28xx-core.c
++++ b/drivers/media/video/em28xx/em28xx-core.c
+@@ -33,8 +33,8 @@
+ /* #define ENABLE_DEBUG_ISOC_FRAMES */
+
+ static unsigned int core_debug;
+-module_param(core_debug,int,0644);
+-MODULE_PARM_DESC(core_debug,"enable debug messages [core]");
++module_param(core_debug, int, 0644);
++MODULE_PARM_DESC(core_debug, "enable debug messages [core]");
+
+ #define em28xx_coredbg(fmt, arg...) do {\
+ if (core_debug) \
+@@ -42,8 +42,8 @@ MODULE_PARM_DESC(core_debug,"enable debug messages [core]");
+ dev->name, __func__ , ##arg); } while (0)
+
+ static unsigned int reg_debug;
+-module_param(reg_debug,int,0644);
+-MODULE_PARM_DESC(reg_debug,"enable debug messages [URB reg]");
++module_param(reg_debug, int, 0644);
++MODULE_PARM_DESC(reg_debug, "enable debug messages [URB reg]");
+
+ #define em28xx_regdbg(fmt, arg...) do {\
+ if (reg_debug) \
+@@ -77,7 +77,7 @@ int em28xx_read_reg_req_len(struct em28xx *dev, u8 req, u16 reg,
+ return -EINVAL;
+
+ if (reg_debug) {
+- printk( KERN_DEBUG "(pipe 0x%08x): "
++ printk(KERN_DEBUG "(pipe 0x%08x): "
+ "IN: %02x %02x %02x %02x %02x %02x %02x %02x ",
+ pipe,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+@@ -154,7 +154,7 @@ int em28xx_write_regs_req(struct em28xx *dev, u8 req, u16 reg, char *buf,
+ if (reg_debug) {
+ int byte;
+
+- printk( KERN_DEBUG "(pipe 0x%08x): "
++ printk(KERN_DEBUG "(pipe 0x%08x): "
+ "OUT: %02x %02x %02x %02x %02x %02x %02x %02x >>>",
+ pipe,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+@@ -378,6 +378,11 @@ static int em28xx_set_audio_source(struct em28xx *dev)
+ }
+ }
+
++ if (dev->board.mute_gpio && dev->mute)
++ em28xx_gpio_set(dev, dev->board.mute_gpio);
++ else
++ em28xx_gpio_set(dev, INPUT(dev->ctl_input)->gpio);
++
+ ret = em28xx_write_reg_bits(dev, EM28XX_R0E_AUDIOSRC, input, 0xc0);
+ if (ret < 0)
+ return ret;
+@@ -424,7 +429,7 @@ int em28xx_audio_analog_set(struct em28xx *dev)
+
+ xclk = dev->board.xclk & 0x7f;
+ if (!dev->mute)
+- xclk |= 0x80;
++ xclk |= EM28XX_XCLK_AUDIO_UNMUTE;
+
+ ret = em28xx_write_reg(dev, EM28XX_R0F_XCLK, xclk);
+ if (ret < 0)
+@@ -462,7 +467,8 @@ int em28xx_audio_analog_set(struct em28xx *dev)
+ if (dev->ctl_aoutput & EM28XX_AOUT_PCM_IN) {
+ int sel = ac97_return_record_select(dev->ctl_aoutput);
+
+- /* Use the same input for both left and right channels */
++ /* Use the same input for both left and right
++ channels */
+ sel |= (sel << 8);
+
+ em28xx_write_ac97(dev, AC97_RECORD_SELECT, sel);
+@@ -698,7 +704,7 @@ static int em28xx_scaler_set(struct em28xx *dev, u16 h, u16 v)
+ em28xx_write_regs(dev, EM28XX_R32_VSCALELOW, (char *)buf, 2);
+ /* it seems that both H and V scalers must be active
+ to work correctly */
+- mode = (h || v)? 0x30: 0x00;
++ mode = (h || v) ? 0x30 : 0x00;
+ }
+ return em28xx_write_reg_bits(dev, EM28XX_R26_COMPR, mode, 0x30);
+ }
+@@ -827,6 +833,19 @@ static void em28xx_irq_callback(struct urb *urb)
+ struct em28xx *dev = container_of(dma_q, struct em28xx, vidq);
+ int rc, i;
+
++ switch (urb->status) {
++ case 0: /* success */
++ case -ETIMEDOUT: /* NAK */
++ break;
++ case -ECONNRESET: /* kill */
++ case -ENOENT:
++ case -ESHUTDOWN:
++ return;
++ default: /* error */
++ em28xx_isocdbg("urb completition error %d.\n", urb->status);
++ break;
++ }
++
+ /* Copy data from URB */
+ spin_lock(&dev->slock);
+ rc = dev->isoc_ctl.isoc_copy(dev, urb);
+@@ -945,7 +964,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
+ em28xx_err("unable to allocate %i bytes for transfer"
+ " buffer %i%s\n",
+ sb_size, i,
+- in_interrupt()?" while in int":"");
++ in_interrupt() ? " while in int" : "");
+ em28xx_uninit_isoc(dev);
+ return -ENOMEM;
+ }
+@@ -963,7 +982,7 @@ int em28xx_init_isoc(struct em28xx *dev, int max_packets,
+ em28xx_irq_callback, dma_q, 1);
+
+ urb->number_of_packets = max_packets;
+- urb->transfer_flags = URB_ISO_ASAP;
++ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+
+ k = 0;
+ for (j = 0; j < max_packets; j++) {
+diff --git a/drivers/media/video/em28xx/em28xx-dvb.c b/drivers/media/video/em28xx/em28xx-dvb.c
+index 9ad8527..fcd2551 100644
+--- a/drivers/media/video/em28xx/em28xx-dvb.c
++++ b/drivers/media/video/em28xx/em28xx-dvb.c
+@@ -29,9 +29,6 @@
+ #include "lgdt330x.h"
+ #include "zl10353.h"
+ #include "s5h1409.h"
+-#ifdef EM28XX_DRX397XD_SUPPORT
+-#include "drx397xD.h"
+-#endif
+
+ MODULE_DESCRIPTION("driver for em28xx based DVB cards");
+ MODULE_AUTHOR("Mauro Carvalho Chehab <mchehab@infradead.org>");
+diff --git a/drivers/media/video/em28xx/em28xx-i2c.c b/drivers/media/video/em28xx/em28xx-i2c.c
+index d69f0ef..02c12fe 100644
+--- a/drivers/media/video/em28xx/em28xx-i2c.c
++++ b/drivers/media/video/em28xx/em28xx-i2c.c
+@@ -402,10 +402,12 @@ static int em28xx_i2c_eeprom(struct em28xx *dev, unsigned char *eedata, int len)
+ dev->name);
+ break;
+ case 2:
+- printk(KERN_INFO "%s:\tI2S audio, sample rate=32k\n", dev->name);
++ printk(KERN_INFO "%s:\tI2S audio, sample rate=32k\n",
++ dev->name);
+ break;
+ case 3:
+- printk(KERN_INFO "%s:\tI2S audio, 3 sample rates\n", dev->name);
++ printk(KERN_INFO "%s:\tI2S audio, 3 sample rates\n",
++ dev->name);
+ break;
+ }
+
+@@ -508,12 +510,17 @@ static int attach_inform(struct i2c_client *client)
+ dprintk1(1, "attach_inform: tvp5150 detected.\n");
+ break;
+
++ case 0xb0:
++ dprintk1(1, "attach_inform: tda9874 detected\n");
++ break;
++
+ default:
+ if (!dev->tuner_addr)
+ dev->tuner_addr = client->addr;
+
+ dprintk1(1, "attach inform: detected I2C address %x\n",
+ client->addr << 1);
++ dprintk1(1, "driver id %d\n", client->driver->id);
+
+ }
+
+@@ -552,6 +559,7 @@ static char *i2c_devs[128] = {
+ [0x80 >> 1] = "msp34xx",
+ [0x88 >> 1] = "msp34xx",
+ [0xa0 >> 1] = "eeprom",
++ [0xb0 >> 1] = "tda9874",
+ [0xb8 >> 1] = "tvp5150a",
+ [0xba >> 1] = "tvp5150a",
+ [0xc0 >> 1] = "tuner (analog)",
+diff --git a/drivers/media/video/em28xx/em28xx-input.c b/drivers/media/video/em28xx/em28xx-input.c
+index 0443afe..a5abfd7 100644
+--- a/drivers/media/video/em28xx/em28xx-input.c
++++ b/drivers/media/video/em28xx/em28xx-input.c
+@@ -68,8 +68,7 @@ struct em28xx_IR {
+
+ /* poll external decoder */
+ int polling;
+- struct work_struct work;
+- struct timer_list timer;
++ struct delayed_work work;
+ unsigned int last_toggle:1;
+ unsigned int last_readcount;
+ unsigned int repeat_interval;
+@@ -292,32 +291,23 @@ static void em28xx_ir_handle_key(struct em28xx_IR *ir)
+ return;
+ }
+
+-static void ir_timer(unsigned long data)
+-{
+- struct em28xx_IR *ir = (struct em28xx_IR *)data;
+-
+- schedule_work(&ir->work);
+-}
+-
+ static void em28xx_ir_work(struct work_struct *work)
+ {
+- struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work);
++ struct em28xx_IR *ir = container_of(work, struct em28xx_IR, work.work);
+
+ em28xx_ir_handle_key(ir);
+- mod_timer(&ir->timer, jiffies + msecs_to_jiffies(ir->polling));
++ schedule_delayed_work(&ir->work, msecs_to_jiffies(ir->polling));
+ }
+
+ static void em28xx_ir_start(struct em28xx_IR *ir)
+ {
+- setup_timer(&ir->timer, ir_timer, (unsigned long)ir);
+- INIT_WORK(&ir->work, em28xx_ir_work);
+- schedule_work(&ir->work);
++ INIT_DELAYED_WORK(&ir->work, em28xx_ir_work);
++ schedule_delayed_work(&ir->work, 0);
+ }
+
+ static void em28xx_ir_stop(struct em28xx_IR *ir)
+ {
+- del_timer_sync(&ir->timer);
+- flush_scheduled_work();
++ cancel_delayed_work_sync(&ir->work);
+ }
+
+ int em28xx_ir_init(struct em28xx *dev)
+diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
+index 8e61b2c..575472f 100644
+--- a/drivers/media/video/em28xx/em28xx-video.c
++++ b/drivers/media/video/em28xx/em28xx-video.c
+@@ -186,7 +186,8 @@ static void em28xx_copy_video(struct em28xx *dev,
+ em28xx_isocdbg("Overflow of %zi bytes past buffer end (1)\n",
+ ((char *)startwrite + lencopy) -
+ ((char *)outp + buf->vb.size));
+- lencopy = remain = (char *)outp + buf->vb.size - (char *)startwrite;
++ remain = (char *)outp + buf->vb.size - (char *)startwrite;
++ lencopy = remain;
+ }
+ if (lencopy <= 0)
+ return;
+@@ -202,7 +203,8 @@ static void em28xx_copy_video(struct em28xx *dev,
+ else
+ lencopy = bytesperline;
+
+- if ((char *)startwrite + lencopy > (char *)outp + buf->vb.size) {
++ if ((char *)startwrite + lencopy > (char *)outp +
++ buf->vb.size) {
+ em28xx_isocdbg("Overflow of %zi bytes past buffer end (2)\n",
+ ((char *)startwrite + lencopy) -
+ ((char *)outp + buf->vb.size));
+@@ -347,7 +349,7 @@ static inline int em28xx_isoc_copy(struct em28xx *dev, struct urb *urb)
+ }
+ if (p[0] == 0x22 && p[1] == 0x5a) {
+ em28xx_isocdbg("Video frame %d, length=%i, %s\n", p[2],
+- len, (p[2] & 1)? "odd" : "even");
++ len, (p[2] & 1) ? "odd" : "even");
+
+ if (!(p[2] & 1)) {
+ if (buf != NULL)
+@@ -476,7 +478,9 @@ fail:
+ static void
+ buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+ {
+- struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
++ struct em28xx_buffer *buf = container_of(vb,
++ struct em28xx_buffer,
++ vb);
+ struct em28xx_fh *fh = vq->priv_data;
+ struct em28xx *dev = fh->dev;
+ struct em28xx_dmaqueue *vidq = &dev->vidq;
+@@ -489,7 +493,9 @@ buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb)
+ static void buffer_release(struct videobuf_queue *vq,
+ struct videobuf_buffer *vb)
+ {
+- struct em28xx_buffer *buf = container_of(vb, struct em28xx_buffer, vb);
++ struct em28xx_buffer *buf = container_of(vb,
++ struct em28xx_buffer,
++ vb);
+ struct em28xx_fh *fh = vq->priv_data;
+ struct em28xx *dev = (struct em28xx *)fh->dev;
+
+@@ -534,6 +540,13 @@ static void video_mux(struct em28xx *dev, int index)
+ &route);
+ }
+
++ if (dev->board.adecoder != EM28XX_NOADECODER) {
++ route.input = dev->ctl_ainput;
++ route.output = dev->ctl_aoutput;
++ em28xx_i2c_call_clients(dev, VIDIOC_INT_S_AUDIO_ROUTING,
++ &route);
++ }
++
+ em28xx_audio_analog_set(dev);
+ }
+
+@@ -557,7 +570,7 @@ static int res_get(struct em28xx_fh *fh)
+
+ static int res_check(struct em28xx_fh *fh)
+ {
+- return (fh->stream_on);
++ return fh->stream_on;
+ }
+
+ static void res_free(struct em28xx_fh *fh)
+@@ -791,7 +804,7 @@ out:
+ return rc;
+ }
+
+-static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id * norm)
++static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *norm)
+ {
+ struct em28xx_fh *fh = priv;
+ struct em28xx *dev = fh->dev;
+@@ -1008,8 +1021,13 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
+
+ if (dev->board.has_msp34xx)
+ em28xx_i2c_call_clients(dev, VIDIOC_G_CTRL, ctrl);
+- else
++ else {
+ rc = em28xx_get_ctrl(dev, ctrl);
++ if (rc < 0) {
++ em28xx_i2c_call_clients(dev, VIDIOC_G_CTRL, ctrl);
++ rc = 0;
++ }
++ }
+
+ mutex_unlock(&dev->lock);
+ return rc;
+@@ -1345,7 +1363,7 @@ static int vidioc_querycap(struct file *file, void *priv,
+
+ strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
+ strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
+- strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info));
++ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+
+ cap->version = EM28XX_VERSION_CODE;
+
+@@ -1431,7 +1449,7 @@ static int vidioc_reqbufs(struct file *file, void *priv,
+ if (rc < 0)
+ return rc;
+
+- return (videobuf_reqbufs(&fh->vb_vidq, rb));
++ return videobuf_reqbufs(&fh->vb_vidq, rb);
+ }
+
+ static int vidioc_querybuf(struct file *file, void *priv,
+@@ -1445,7 +1463,7 @@ static int vidioc_querybuf(struct file *file, void *priv,
+ if (rc < 0)
+ return rc;
+
+- return (videobuf_querybuf(&fh->vb_vidq, b));
++ return videobuf_querybuf(&fh->vb_vidq, b);
+ }
+
+ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+@@ -1458,7 +1476,7 @@ static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+ if (rc < 0)
+ return rc;
+
+- return (videobuf_qbuf(&fh->vb_vidq, b));
++ return videobuf_qbuf(&fh->vb_vidq, b);
+ }
+
+ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+@@ -1471,8 +1489,7 @@ static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *b)
+ if (rc < 0)
+ return rc;
+
+- return (videobuf_dqbuf(&fh->vb_vidq, b,
+- file->f_flags & O_NONBLOCK));
++ return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK);
+ }
+
+ #ifdef CONFIG_VIDEO_V4L1_COMPAT
+@@ -1496,7 +1513,7 @@ static int radio_querycap(struct file *file, void *priv,
+
+ strlcpy(cap->driver, "em28xx", sizeof(cap->driver));
+ strlcpy(cap->card, em28xx_boards[dev->model].name, sizeof(cap->card));
+- strlcpy(cap->bus_info, dev_name(&dev->udev->dev), sizeof(cap->bus_info));
++ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+
+ cap->version = EM28XX_VERSION_CODE;
+ cap->capabilities = V4L2_CAP_TUNER;
+@@ -1781,7 +1798,7 @@ em28xx_v4l2_read(struct file *filp, char __user *buf, size_t count,
+ * 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 unsigned int em28xx_v4l2_poll(struct file *filp, poll_table *wait)
+ {
+ struct em28xx_fh *fh = filp->private_data;
+ struct em28xx *dev = fh->dev;
+@@ -1934,8 +1951,8 @@ static struct video_device em28xx_radio_template = {
+
+
+ static struct video_device *em28xx_vdev_init(struct em28xx *dev,
+- const struct video_device *template,
+- const char *type_name)
++ const struct video_device *template,
++ const char *type_name)
+ {
+ struct video_device *vfd;
+
+@@ -1984,8 +2001,9 @@ int em28xx_register_analog_devices(struct em28xx *dev)
+ /* enable vbi capturing */
+
+ /* em28xx_write_reg(dev, EM28XX_R0E_AUDIOSRC, 0xc0); audio register */
+- val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK);
+- em28xx_write_reg(dev, EM28XX_R0F_XCLK, (EM28XX_XCLK_AUDIO_UNMUTE | val));
++ val = (u8)em28xx_read_reg(dev, EM28XX_R0F_XCLK);
++ em28xx_write_reg(dev, EM28XX_R0F_XCLK,
++ (EM28XX_XCLK_AUDIO_UNMUTE | val));
+ em28xx_write_reg(dev, EM28XX_R11_VINCTRL, 0x51);
+
+ em28xx_set_outfmt(dev);
+@@ -2020,7 +2038,8 @@ int em28xx_register_analog_devices(struct em28xx *dev)
+ }
+
+ if (em28xx_boards[dev->model].radio.type == EM28XX_RADIO) {
+- dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template, "radio");
++ dev->radio_dev = em28xx_vdev_init(dev, &em28xx_radio_template,
++ "radio");
+ if (!dev->radio_dev) {
+ em28xx_errdev("cannot allocate video_device.\n");
+ return -ENODEV;
+diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h
+index dd2cd36..a33a58d 100644
+--- a/drivers/media/video/em28xx/em28xx.h
++++ b/drivers/media/video/em28xx/em28xx.h
+@@ -70,7 +70,6 @@
+ #define EM2820_BOARD_VIDEOLOGY_20K14XUSB 30
+ #define EM2821_BOARD_USBGEAR_VD204 31
+ #define EM2821_BOARD_SUPERCOMP_USB_2 32
+-#define EM2821_BOARD_PROLINK_PLAYTV_USB2 33
+ #define EM2860_BOARD_TERRATEC_HYBRID_XS 34
+ #define EM2860_BOARD_TYPHOON_DVD_MAKER 35
+ #define EM2860_BOARD_NETGMBH_CAM 36
+@@ -98,6 +97,10 @@
+ #define EM2820_BOARD_COMPRO_VIDEOMATE_FORYOU 58
+ #define EM2883_BOARD_HAUPPAUGE_WINTV_HVR_850 60
+ #define EM2820_BOARD_PROLINK_PLAYTV_BOX4_USB2 61
++#define EM2820_BOARD_GADMEI_TVR200 62
++#define EM2860_BOARD_KAIOMY_TVNPC_U2 63
++#define EM2860_BOARD_EASYCAP 64
++#define EM2820_BOARD_IODATA_GVMVP_SZ 65
+
+ /* Limits minimum and default number of buffers */
+ #define EM28XX_MIN_BUF 4
+@@ -110,6 +113,10 @@
+ #define EM28XX_BOARD_NOT_VALIDATED 1
+ #define EM28XX_BOARD_VALIDATED 0
+
++/* Params for em28xx_cmd() audio */
++#define EM28XX_START_AUDIO 1
++#define EM28XX_STOP_AUDIO 0
++
+ /* maximum number of em28xx boards */
+ #define EM28XX_MAXBOARDS 4 /*FIXME: should be bigger */
+
+@@ -154,7 +161,8 @@
+ */
+
+ /* time to wait when stopping the isoc transfer */
+-#define EM28XX_URB_TIMEOUT msecs_to_jiffies(EM28XX_NUM_BUFS * EM28XX_NUM_PACKETS)
++#define EM28XX_URB_TIMEOUT \
++ msecs_to_jiffies(EM28XX_NUM_BUFS * EM28XX_NUM_PACKETS)
+
+ /* time in msecs to wait for i2c writes to finish */
+ #define EM2800_I2C_WRITE_TIMEOUT 20
+@@ -348,6 +356,11 @@ enum em28xx_decoder {
+ EM28XX_SAA711X,
+ };
+
++enum em28xx_adecoder {
++ EM28XX_NOADECODER = 0,
++ EM28XX_TVAUDIO,
++};
++
+ struct em28xx_board {
+ char *name;
+ int vchannels;
+@@ -361,6 +374,7 @@ struct em28xx_board {
+ struct em28xx_reg_seq *dvb_gpio;
+ struct em28xx_reg_seq *suspend_gpio;
+ struct em28xx_reg_seq *tuner_gpio;
++ struct em28xx_reg_seq *mute_gpio;
+
+ unsigned int is_em2800:1;
+ unsigned int has_msp34xx:1;
+@@ -373,6 +387,7 @@ struct em28xx_board {
+ unsigned char xclk, i2c_speed;
+
+ enum em28xx_decoder decoder;
++ enum em28xx_adecoder adecoder;
+
+ struct em28xx_input input[MAX_EM28XX_INPUT];
+ struct em28xx_input radio;
+@@ -420,7 +435,7 @@ struct em28xx_audio {
+ unsigned int hwptr_done_capture;
+ struct snd_card *sndcard;
+
+- int users, shutdown;
++ int users;
+ enum em28xx_stream_state capture_stream;
+ spinlock_t slock;
+ };
+@@ -523,7 +538,8 @@ struct em28xx {
+ int num_alt; /* Number of alternative settings */
+ unsigned int *alt_max_pkt_size; /* array of wMaxPacketSize */
+ struct urb *urb[EM28XX_NUM_BUFS]; /* urb for isoc transfers */
+- char *transfer_buffer[EM28XX_NUM_BUFS]; /* transfer buffers for isoc transfer */
++ char *transfer_buffer[EM28XX_NUM_BUFS]; /* transfer buffers for isoc
++ transfer */
+ char urb_buf[URB_MAX_CTRL_SIZE]; /* urb control msg buffer */
+
+ /* helper funcs that call usb_control_msg */
+diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig
+index ee6a691..578dc4f 100644
+--- a/drivers/media/video/gspca/Kconfig
++++ b/drivers/media/video/gspca/Kconfig
+@@ -56,6 +56,15 @@ config USB_GSPCA_MARS
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_mars.
+
++config USB_GSPCA_MR97310A
++ tristate "Mars-Semi MR97310A USB Camera Driver"
++ depends on VIDEO_V4L2 && USB_GSPCA
++ help
++ Say Y here if you want support for cameras based on the MR97310A chip.
++
++ To compile this driver as a module, choose M here: the
++ module will be called gspca_mr97310a.
++
+ config USB_GSPCA_OV519
+ tristate "OV519 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+@@ -167,6 +176,24 @@ config USB_GSPCA_SPCA561
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_spca561.
+
++config USB_GSPCA_SQ905
++ tristate "SQ Technologies SQ905 based USB Camera Driver"
++ depends on VIDEO_V4L2 && USB_GSPCA
++ help
++ Say Y here if you want support for cameras based on the SQ905 chip.
++
++ To compile this driver as a module, choose M here: the
++ module will be called gspca_sq905.
++
++config USB_GSPCA_SQ905C
++ tristate "SQ Technologies SQ905C based USB Camera Driver"
++ depends on VIDEO_V4L2 && USB_GSPCA
++ help
++ Say Y here if you want support for cameras based on the SQ905C chip.
++
++ To compile this driver as a module, choose M here: the
++ module will be called gspca_sq905c.
++
+ config USB_GSPCA_STK014
+ tristate "Syntek DV4000 (STK014) USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile
+index bd8d9ee..8a6643e 100644
+--- a/drivers/media/video/gspca/Makefile
++++ b/drivers/media/video/gspca/Makefile
+@@ -1,50 +1,56 @@
+-obj-$(CONFIG_USB_GSPCA) += gspca_main.o
+-obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o
+-obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o
+-obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o
+-obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o
+-obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o
+-obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o
+-obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o
+-obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o
+-obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o
+-obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o
+-obj-$(CONFIG_USB_GSPCA_SPCA500) += gspca_spca500.o
+-obj-$(CONFIG_USB_GSPCA_SPCA501) += gspca_spca501.o
+-obj-$(CONFIG_USB_GSPCA_SPCA505) += gspca_spca505.o
+-obj-$(CONFIG_USB_GSPCA_SPCA506) += gspca_spca506.o
+-obj-$(CONFIG_USB_GSPCA_SPCA508) += gspca_spca508.o
+-obj-$(CONFIG_USB_GSPCA_SPCA561) += gspca_spca561.o
+-obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o
+-obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o
+-obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o
+-obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o
+-obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o
+-obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o
++obj-$(CONFIG_USB_GSPCA) += gspca_main.o
++obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o
++obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o
++obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o
++obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o
++obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o
++obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o
++obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o
++obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o
++obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o
++obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o
++obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o
++obj-$(CONFIG_USB_GSPCA_SPCA500) += gspca_spca500.o
++obj-$(CONFIG_USB_GSPCA_SPCA501) += gspca_spca501.o
++obj-$(CONFIG_USB_GSPCA_SPCA505) += gspca_spca505.o
++obj-$(CONFIG_USB_GSPCA_SPCA506) += gspca_spca506.o
++obj-$(CONFIG_USB_GSPCA_SPCA508) += gspca_spca508.o
++obj-$(CONFIG_USB_GSPCA_SPCA561) += gspca_spca561.o
++obj-$(CONFIG_USB_GSPCA_SQ905) += gspca_sq905.o
++obj-$(CONFIG_USB_GSPCA_SQ905C) += gspca_sq905c.o
++obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o
++obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o
++obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o
++obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o
++obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o
++obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o
+
+-gspca_main-objs := gspca.o
+-gspca_conex-objs := conex.o
+-gspca_etoms-objs := etoms.o
+-gspca_finepix-objs := finepix.o
+-gspca_mars-objs := mars.o
+-gspca_ov519-objs := ov519.o
+-gspca_ov534-objs := ov534.o
+-gspca_pac207-objs := pac207.o
+-gspca_pac7311-objs := pac7311.o
+-gspca_sonixb-objs := sonixb.o
+-gspca_sonixj-objs := sonixj.o
+-gspca_spca500-objs := spca500.o
+-gspca_spca501-objs := spca501.o
+-gspca_spca505-objs := spca505.o
+-gspca_spca506-objs := spca506.o
+-gspca_spca508-objs := spca508.o
+-gspca_spca561-objs := spca561.o
+-gspca_stk014-objs := stk014.o
+-gspca_sunplus-objs := sunplus.o
+-gspca_t613-objs := t613.o
+-gspca_tv8532-objs := tv8532.o
+-gspca_vc032x-objs := vc032x.o
+-gspca_zc3xx-objs := zc3xx.o
++gspca_main-objs := gspca.o
++gspca_conex-objs := conex.o
++gspca_etoms-objs := etoms.o
++gspca_finepix-objs := finepix.o
++gspca_mars-objs := mars.o
++gspca_mr97310a-objs := mr97310a.o
++gspca_ov519-objs := ov519.o
++gspca_ov534-objs := ov534.o
++gspca_pac207-objs := pac207.o
++gspca_pac7311-objs := pac7311.o
++gspca_sonixb-objs := sonixb.o
++gspca_sonixj-objs := sonixj.o
++gspca_spca500-objs := spca500.o
++gspca_spca501-objs := spca501.o
++gspca_spca505-objs := spca505.o
++gspca_spca506-objs := spca506.o
++gspca_spca508-objs := spca508.o
++gspca_spca561-objs := spca561.o
++gspca_sq905-objs := sq905.o
++gspca_sq905c-objs := sq905c.o
++gspca_stk014-objs := stk014.o
++gspca_sunplus-objs := sunplus.o
++gspca_t613-objs := t613.o
++gspca_tv8532-objs := tv8532.o
++gspca_vc032x-objs := vc032x.o
++gspca_zc3xx-objs := zc3xx.o
+
+-obj-$(CONFIG_USB_M5602) += m5602/
+-obj-$(CONFIG_USB_STV06XX) += stv06xx/
++obj-$(CONFIG_USB_M5602) += m5602/
++obj-$(CONFIG_USB_STV06XX) += stv06xx/
+diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c
+index 1753f5b..219cfa6 100644
+--- a/drivers/media/video/gspca/conex.c
++++ b/drivers/media/video/gspca/conex.c
+@@ -36,8 +36,12 @@ struct sd {
+ unsigned char brightness;
+ unsigned char contrast;
+ unsigned char colors;
++ u8 quality;
++#define QUALITY_MIN 30
++#define QUALITY_MAX 60
++#define QUALITY_DEF 40
+
+- unsigned char qindex;
++ u8 *jpeg_hdr;
+ };
+
+ /* V4L2 controls supported by the driver */
+@@ -815,14 +819,13 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = 0x01;
+ cam->cam_mode = vga_mode;
+ cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+
+- sd->qindex = 0; /* set the quantization */
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->colors = COLOR_DEF;
++ sd->quality = QUALITY_DEF;
+ return 0;
+ }
+
+@@ -839,6 +842,14 @@ static int sd_init(struct gspca_dev *gspca_dev)
+
+ static int sd_start(struct gspca_dev *gspca_dev)
+ {
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ /* create the JPEG header */
++ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
++ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
++ 0x22); /* JPEG 411 */
++ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
++
+ cx11646_initsize(gspca_dev);
+ cx11646_fw(gspca_dev);
+ cx_sensor(gspca_dev);
+@@ -849,8 +860,11 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ /* called on streamoff with alt 0 and on disconnect */
+ static void sd_stop0(struct gspca_dev *gspca_dev)
+ {
++ struct sd *sd = (struct sd *) gspca_dev;
+ int retry = 50;
+
++ kfree(sd->jpeg_hdr);
++
+ if (!gspca_dev->present)
+ return;
+ reg_w_val(gspca_dev, 0x0000, 0x00);
+@@ -876,6 +890,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ __u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
++ struct sd *sd = (struct sd *) gspca_dev;
++
+ if (data[0] == 0xff && data[1] == 0xd8) {
+
+ /* start of frame */
+@@ -883,9 +899,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ data, 0);
+
+ /* put the JPEG header in the new frame */
+- jpeg_put_header(gspca_dev, frame,
+- ((struct sd *) gspca_dev)->qindex,
+- 0x22);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ sd->jpeg_hdr, JPEG_HDR_SZ);
+ data += 2;
+ len -= 2;
+ }
+@@ -988,6 +1003,34 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+ return 0;
+ }
+
++static int sd_set_jcomp(struct gspca_dev *gspca_dev,
++ struct v4l2_jpegcompression *jcomp)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (jcomp->quality < QUALITY_MIN)
++ sd->quality = QUALITY_MIN;
++ else if (jcomp->quality > QUALITY_MAX)
++ sd->quality = QUALITY_MAX;
++ else
++ sd->quality = jcomp->quality;
++ if (gspca_dev->streaming)
++ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
++ return 0;
++}
++
++static int sd_get_jcomp(struct gspca_dev *gspca_dev,
++ struct v4l2_jpegcompression *jcomp)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ memset(jcomp, 0, sizeof *jcomp);
++ jcomp->quality = sd->quality;
++ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
++ | V4L2_JPEG_MARKER_DQT;
++ return 0;
++}
++
+ /* sub-driver description */
+ static struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+@@ -998,6 +1041,8 @@ static struct sd_desc sd_desc = {
+ .start = sd_start,
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
++ .get_jcomp = sd_get_jcomp,
++ .set_jcomp = sd_set_jcomp,
+ };
+
+ /* -- module initialisation -- */
+@@ -1029,8 +1074,10 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c
+index f3cd8ff..2c20d06 100644
+--- a/drivers/media/video/gspca/etoms.c
++++ b/drivers/media/video/gspca/etoms.c
+@@ -472,19 +472,6 @@ static void setbrightness(struct gspca_dev *gspca_dev)
+ reg_w_val(gspca_dev, ET_O_RED + i, brightness);
+ }
+
+-static void getbrightness(struct gspca_dev *gspca_dev)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+- int i;
+- int brightness = 0;
+-
+- for (i = 0; i < 4; i++) {
+- reg_r(gspca_dev, ET_O_RED + i, 1);
+- brightness += gspca_dev->usb_buf[0];
+- }
+- sd->brightness = brightness >> 3;
+-}
+-
+ static void setcontrast(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -495,19 +482,6 @@ static void setcontrast(struct gspca_dev *gspca_dev)
+ reg_w(gspca_dev, ET_G_RED, RGBG, 6);
+ }
+
+-static void getcontrast(struct gspca_dev *gspca_dev)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+- int i;
+- int contrast = 0;
+-
+- for (i = 0; i < 4; i++) {
+- reg_r(gspca_dev, ET_G_RED + i, 1);
+- contrast += gspca_dev->usb_buf[0];
+- }
+- sd->contrast = contrast >> 2;
+-}
+-
+ static void setcolors(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -658,7 +632,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = 1;
+ sd->sensor = id->driver_info;
+ if (sd->sensor == SENSOR_PAS106) {
+ cam->cam_mode = sif_mode;
+@@ -821,7 +794,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- getbrightness(gspca_dev);
+ *val = sd->brightness;
+ return 0;
+ }
+@@ -840,7 +812,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- getcontrast(gspca_dev);
+ *val = sd->contrast;
+ return 0;
+ }
+@@ -859,7 +830,6 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- getcolors(gspca_dev);
+ *val = sd->colors;
+ return 0;
+ }
+@@ -928,8 +898,10 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c
+index afc8b2d..00e6863 100644
+--- a/drivers/media/video/gspca/finepix.c
++++ b/drivers/media/video/gspca/finepix.c
+@@ -27,7 +27,7 @@ MODULE_DESCRIPTION("Fujifilm FinePix USB V4L2 driver");
+ MODULE_LICENSE("GPL");
+
+ /* Default timeout, in ms */
+-#define FPIX_TIMEOUT (HZ / 10)
++#define FPIX_TIMEOUT 250
+
+ /* Maximum transfer size to use. The windows driver reads by chunks of
+ * 0x2000 bytes, so do the same. Note: reading more seems to work
+@@ -38,38 +38,15 @@ MODULE_LICENSE("GPL");
+ struct usb_fpix {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+- /*
+- * USB stuff
+- */
+- struct usb_ctrlrequest ctrlreq;
+- struct urb *control_urb;
+- struct timer_list bulk_timer;
+-
+- enum {
+- FPIX_NOP, /* inactive, else streaming */
+- FPIX_RESET, /* must reset */
+- FPIX_REQ_FRAME, /* requesting a frame */
+- FPIX_READ_FRAME, /* reading frame */
+- } state;
+-
+- /*
+- * Driver stuff
+- */
+- struct delayed_work wqe;
+- struct completion can_close;
+- int streaming;
++ struct work_struct work_struct;
++ struct workqueue_struct *work_thread;
+ };
+
+ /* Delay after which claim the next frame. If the delay is too small,
+ * the camera will return old frames. On the 4800Z, 20ms is bad, 25ms
+- * will fail every 4 or 5 frames, but 30ms is perfect. */
+-#define NEXT_FRAME_DELAY (((HZ * 30) + 999) / 1000)
+-
+-#define dev_new_state(new_state) { \
+- PDEBUG(D_STREAM, "new state from %d to %d at %s:%d", \
+- dev->state, new_state, __func__, __LINE__); \
+- dev->state = new_state; \
+-}
++ * will fail every 4 or 5 frames, but 30ms is perfect. On the A210,
++ * 30ms is bad while 35ms is perfect. */
++#define NEXT_FRAME_DELAY 35
+
+ /* These cameras only support 320x200. */
+ static const struct v4l2_pix_format fpix_mode[1] = {
+@@ -80,316 +57,183 @@ static const struct v4l2_pix_format fpix_mode[1] = {
+ .priv = 0}
+ };
+
+-/* Reads part of a frame */
+-static void read_frame_part(struct usb_fpix *dev)
++/* send a command to the webcam */
++static int command(struct gspca_dev *gspca_dev,
++ int order) /* 0: reset, 1: frame request */
+ {
+- int ret;
++ static u8 order_values[2][12] = {
++ {0xc6, 0, 0, 0, 0, 0, 0, 0, 0x20, 0, 0, 0}, /* reset */
++ {0xd3, 0, 0, 0, 0, 0, 0, 0x01, 0, 0, 0, 0}, /* fr req */
++ };
+
+- PDEBUG(D_STREAM, "read_frame_part");
+-
+- /* Reads part of a frame */
+- ret = usb_submit_urb(dev->gspca_dev.urb[0], GFP_ATOMIC);
+- if (ret) {
+- dev_new_state(FPIX_RESET);
+- schedule_delayed_work(&dev->wqe, 1);
+- PDEBUG(D_STREAM, "usb_submit_urb failed with %d",
+- ret);
+- } else {
+- /* Sometimes we never get a callback, so use a timer.
+- * Is this masking a bug somewhere else? */
+- dev->bulk_timer.expires = jiffies + msecs_to_jiffies(150);
+- add_timer(&dev->bulk_timer);
+- }
++ memcpy(gspca_dev->usb_buf, order_values[order], 12);
++ return usb_control_msg(gspca_dev->dev,
++ usb_sndctrlpipe(gspca_dev->dev, 0),
++ USB_REQ_GET_STATUS,
++ USB_DIR_OUT | USB_TYPE_CLASS |
++ USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf,
++ 12, FPIX_TIMEOUT);
+ }
+
+-/* Callback for URBs. */
+-static void urb_callback(struct urb *urb)
++/* workqueue */
++static void dostream(struct work_struct *work)
+ {
+- struct gspca_dev *gspca_dev = urb->context;
+- struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+-
+- PDEBUG(D_PACK,
+- "enter urb_callback - status=%d, length=%d",
+- urb->status, urb->actual_length);
+-
+- if (dev->state == FPIX_READ_FRAME)
+- del_timer(&dev->bulk_timer);
+-
+- if (urb->status != 0) {
+- /* We kill a stuck urb every 50 frames on average, so don't
+- * display a log message for that. */
+- if (urb->status != -ECONNRESET)
+- PDEBUG(D_STREAM, "bad URB status %d", urb->status);
+- dev_new_state(FPIX_RESET);
+- schedule_delayed_work(&dev->wqe, 1);
+- }
+-
+- switch (dev->state) {
+- case FPIX_REQ_FRAME:
+- dev_new_state(FPIX_READ_FRAME);
+- read_frame_part(dev);
+- break;
+-
+- case FPIX_READ_FRAME: {
+- unsigned char *data = urb->transfer_buffer;
+- struct gspca_frame *frame;
+-
+- frame = gspca_get_i_frame(&dev->gspca_dev);
+- if (frame == NULL)
+- gspca_dev->last_packet_type = DISCARD_PACKET;
+- if (urb->actual_length < FPIX_MAX_TRANSFER ||
+- (data[urb->actual_length-2] == 0xff &&
+- data[urb->actual_length-1] == 0xd9)) {
+-
+- /* If the result is less than what was asked
+- * for, then it's the end of the
+- * frame. Sometime the jpeg is not complete,
+- * but there's nothing we can do. We also end
+- * here if the the jpeg ends right at the end
+- * of the frame. */
+- if (frame)
+- gspca_frame_add(gspca_dev, LAST_PACKET,
+- frame,
+- data, urb->actual_length);
+- dev_new_state(FPIX_REQ_FRAME);
+- schedule_delayed_work(&dev->wqe, NEXT_FRAME_DELAY);
+- } else {
++ struct usb_fpix *dev = container_of(work, struct usb_fpix, work_struct);
++ struct gspca_dev *gspca_dev = &dev->gspca_dev;
++ struct urb *urb = gspca_dev->urb[0];
++ u8 *data = urb->transfer_buffer;
++ struct gspca_frame *frame;
++ int ret = 0;
++ int len;
++
++ /* synchronize with the main driver */
++ mutex_lock(&gspca_dev->usb_lock);
++ mutex_unlock(&gspca_dev->usb_lock);
++ PDEBUG(D_STREAM, "dostream started");
++
++ /* loop reading a frame */
++again:
++ while (gspca_dev->present && gspca_dev->streaming) {
++
++ /* request a frame */
++ mutex_lock(&gspca_dev->usb_lock);
++ ret = command(gspca_dev, 1);
++ mutex_unlock(&gspca_dev->usb_lock);
++ if (ret < 0)
++ break;
++ if (!gspca_dev->present || !gspca_dev->streaming)
++ break;
++
++ /* the frame comes in parts */
++ for (;;) {
++ ret = usb_bulk_msg(gspca_dev->dev,
++ urb->pipe,
++ data,
++ FPIX_MAX_TRANSFER,
++ &len, FPIX_TIMEOUT);
++ if (ret < 0) {
++ /* Most of the time we get a timeout
++ * error. Just restart. */
++ goto again;
++ }
++ if (!gspca_dev->present || !gspca_dev->streaming)
++ goto out;
++ frame = gspca_get_i_frame(&dev->gspca_dev);
++ if (frame == NULL)
++ gspca_dev->last_packet_type = DISCARD_PACKET;
++
++ if (len < FPIX_MAX_TRANSFER ||
++ (data[len - 2] == 0xff &&
++ data[len - 1] == 0xd9)) {
++
++ /* If the result is less than what was asked
++ * for, then it's the end of the
++ * frame. Sometimes the jpeg is not complete,
++ * but there's nothing we can do. We also end
++ * here if the the jpeg ends right at the end
++ * of the frame. */
++ if (frame)
++ frame = gspca_frame_add(gspca_dev,
++ LAST_PACKET,
++ frame,
++ data, len);
++ break;
++ }
+
+ /* got a partial image */
+ if (frame)
+ gspca_frame_add(gspca_dev,
+ gspca_dev->last_packet_type
+- == LAST_PACKET
++ == LAST_PACKET
+ ? FIRST_PACKET : INTER_PACKET,
+- frame,
+- data, urb->actual_length);
+- read_frame_part(dev);
++ frame, data, len);
+ }
+- break;
+- }
+-
+- case FPIX_NOP:
+- case FPIX_RESET:
+- PDEBUG(D_STREAM, "invalid state %d", dev->state);
+- break;
+- }
+-}
+
+-/* Request a new frame */
+-static void request_frame(struct usb_fpix *dev)
+-{
+- int ret;
+- struct gspca_dev *gspca_dev = &dev->gspca_dev;
+-
+- /* Setup command packet */
+- memset(gspca_dev->usb_buf, 0, 12);
+- gspca_dev->usb_buf[0] = 0xd3;
+- gspca_dev->usb_buf[7] = 0x01;
+-
+- /* Request a frame */
+- dev->ctrlreq.bRequestType =
+- USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE;
+- dev->ctrlreq.bRequest = USB_REQ_GET_STATUS;
+- dev->ctrlreq.wValue = 0;
+- dev->ctrlreq.wIndex = 0;
+- dev->ctrlreq.wLength = cpu_to_le16(12);
+-
+- usb_fill_control_urb(dev->control_urb,
+- gspca_dev->dev,
+- usb_sndctrlpipe(gspca_dev->dev, 0),
+- (unsigned char *) &dev->ctrlreq,
+- gspca_dev->usb_buf,
+- 12, urb_callback, gspca_dev);
+-
+- ret = usb_submit_urb(dev->control_urb, GFP_ATOMIC);
+- if (ret) {
+- dev_new_state(FPIX_RESET);
+- schedule_delayed_work(&dev->wqe, 1);
+- PDEBUG(D_STREAM, "usb_submit_urb failed with %d", ret);
+- }
+-}
+-
+-/*--------------------------------------------------------------------------*/
+-
+-/* State machine. */
+-static void fpix_sm(struct work_struct *work)
+-{
+- struct usb_fpix *dev = container_of(work, struct usb_fpix, wqe.work);
+-
+- PDEBUG(D_STREAM, "fpix_sm state %d", dev->state);
+-
+- /* verify that the device wasn't unplugged */
+- if (!dev->gspca_dev.present) {
+- PDEBUG(D_STREAM, "device is gone");
+- dev_new_state(FPIX_NOP);
+- complete(&dev->can_close);
+- return;
+- }
+-
+- if (!dev->streaming) {
+- PDEBUG(D_STREAM, "stopping state machine");
+- dev_new_state(FPIX_NOP);
+- complete(&dev->can_close);
+- return;
++ /* We must wait before trying reading the next
++ * frame. If we don't, or if the delay is too short,
++ * the camera will disconnect. */
++ msleep(NEXT_FRAME_DELAY);
+ }
+
+- switch (dev->state) {
+- case FPIX_RESET:
+- dev_new_state(FPIX_REQ_FRAME);
+- schedule_delayed_work(&dev->wqe, HZ / 10);
+- break;
+-
+- case FPIX_REQ_FRAME:
+- /* get an image */
+- request_frame(dev);
+- break;
+-
+- case FPIX_NOP:
+- case FPIX_READ_FRAME:
+- PDEBUG(D_STREAM, "invalid state %d", dev->state);
+- break;
+- }
++out:
++ PDEBUG(D_STREAM, "dostream stopped");
+ }
+
+ /* this function is called at probe time */
+ static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+ {
++ struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+ struct cam *cam = &gspca_dev->cam;
+
+ cam->cam_mode = fpix_mode;
+ cam->nmodes = 1;
+- cam->epaddr = 0x01; /* todo: correct for all cams? */
+ cam->bulk_size = FPIX_MAX_TRANSFER;
+
+-/* gspca_dev->nbalt = 1; * use bulk transfer */
+- return 0;
+-}
+-
+-/* Stop streaming and free the ressources allocated by sd_start. */
+-static void sd_stopN(struct gspca_dev *gspca_dev)
+-{
+- struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+-
+- dev->streaming = 0;
+-
+- /* Stop the state machine */
+- if (dev->state != FPIX_NOP)
+- wait_for_completion(&dev->can_close);
+-}
+-
+-/* called on streamoff with alt 0 and disconnect */
+-static void sd_stop0(struct gspca_dev *gspca_dev)
+-{
+- struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+-
+- usb_free_urb(dev->control_urb);
+- dev->control_urb = NULL;
+-}
+-
+-/* Kill an URB that hasn't completed. */
+-static void timeout_kill(unsigned long data)
+-{
+- struct urb *urb = (struct urb *) data;
++ INIT_WORK(&dev->work_struct, dostream);
+
+- usb_unlink_urb(urb);
++ return 0;
+ }
+
+ /* this function is called at probe and resume time */
+ static int sd_init(struct gspca_dev *gspca_dev)
+ {
+- struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+-
+- INIT_DELAYED_WORK(&dev->wqe, fpix_sm);
+-
+- init_timer(&dev->bulk_timer);
+- dev->bulk_timer.function = timeout_kill;
+-
+ return 0;
+ }
+
++/* start the camera */
+ static int sd_start(struct gspca_dev *gspca_dev)
+ {
+ struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+- int ret;
+- int size_ret;
++ int ret, len;
+
+ /* Init the device */
+- memset(gspca_dev->usb_buf, 0, 12);
+- gspca_dev->usb_buf[0] = 0xc6;
+- gspca_dev->usb_buf[8] = 0x20;
+-
+- ret = usb_control_msg(gspca_dev->dev,
+- usb_sndctrlpipe(gspca_dev->dev, 0),
+- USB_REQ_GET_STATUS,
+- USB_DIR_OUT | USB_TYPE_CLASS |
+- USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf,
+- 12, FPIX_TIMEOUT);
+-
+- if (ret != 12) {
+- PDEBUG(D_STREAM, "usb_control_msg failed (%d)", ret);
+- ret = -EIO;
+- goto error;
++ ret = command(gspca_dev, 0);
++ if (ret < 0) {
++ PDEBUG(D_STREAM, "init failed %d", ret);
++ return ret;
+ }
+
+ /* Read the result of the command. Ignore the result, for it
+ * varies with the device. */
+ ret = usb_bulk_msg(gspca_dev->dev,
+- usb_rcvbulkpipe(gspca_dev->dev,
+- gspca_dev->cam.epaddr),
+- gspca_dev->usb_buf, FPIX_MAX_TRANSFER, &size_ret,
++ gspca_dev->urb[0]->pipe,
++ gspca_dev->urb[0]->transfer_buffer,
++ FPIX_MAX_TRANSFER, &len,
+ FPIX_TIMEOUT);
+- if (ret != 0) {
+- PDEBUG(D_STREAM, "usb_bulk_msg failed (%d)", ret);
+- ret = -EIO;
+- goto error;
++ if (ret < 0) {
++ PDEBUG(D_STREAM, "usb_bulk_msg failed %d", ret);
++ return ret;
+ }
+
+ /* Request a frame, but don't read it */
+- memset(gspca_dev->usb_buf, 0, 12);
+- gspca_dev->usb_buf[0] = 0xd3;
+- gspca_dev->usb_buf[7] = 0x01;
+-
+- ret = usb_control_msg(gspca_dev->dev,
+- usb_sndctrlpipe(gspca_dev->dev, 0),
+- USB_REQ_GET_STATUS,
+- USB_DIR_OUT | USB_TYPE_CLASS |
+- USB_RECIP_INTERFACE, 0, 0, gspca_dev->usb_buf,
+- 12, FPIX_TIMEOUT);
+- if (ret != 12) {
+- PDEBUG(D_STREAM, "usb_control_msg failed (%d)", ret);
+- ret = -EIO;
+- goto error;
++ ret = command(gspca_dev, 1);
++ if (ret < 0) {
++ PDEBUG(D_STREAM, "frame request failed %d", ret);
++ return ret;
+ }
+
+ /* Again, reset bulk in endpoint */
+- usb_clear_halt(gspca_dev->dev, gspca_dev->cam.epaddr);
+-
+- /* Allocate a control URB */
+- dev->control_urb = usb_alloc_urb(0, GFP_KERNEL);
+- if (!dev->control_urb) {
+- PDEBUG(D_STREAM, "No free urbs available");
+- ret = -EIO;
+- goto error;
+- }
+-
+- /* Various initializations. */
+- init_completion(&dev->can_close);
+- dev->bulk_timer.data = (unsigned long)dev->gspca_dev.urb[0];
+- dev->gspca_dev.urb[0]->complete = urb_callback;
+- dev->streaming = 1;
++ usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe);
+
+- /* Schedule a frame request. */
+- dev_new_state(FPIX_REQ_FRAME);
+- schedule_delayed_work(&dev->wqe, 1);
++ /* Start the workqueue function to do the streaming */
++ dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
++ queue_work(dev->work_thread, &dev->work_struct);
+
+ return 0;
++}
++
++/* called on streamoff with alt==0 and on disconnect */
++/* the usb_lock is held at entry - restore on exit */
++static void sd_stop0(struct gspca_dev *gspca_dev)
++{
++ struct usb_fpix *dev = (struct usb_fpix *) gspca_dev;
+
+-error:
+- /* Free the ressources */
+- sd_stopN(gspca_dev);
+- sd_stop0(gspca_dev);
+- return ret;
++ /* wait for the work queue to terminate */
++ mutex_unlock(&gspca_dev->usb_lock);
++ destroy_workqueue(dev->work_thread);
++ mutex_lock(&gspca_dev->usb_lock);
++ dev->work_thread = NULL;
+ }
+
+ /* Table of supported USB devices */
+@@ -424,12 +268,11 @@ MODULE_DEVICE_TABLE(usb, device_table);
+
+ /* sub-driver description */
+ static const struct sd_desc sd_desc = {
+- .name = MODULE_NAME,
++ .name = MODULE_NAME,
+ .config = sd_config,
+- .init = sd_init,
+- .start = sd_start,
+- .stopN = sd_stopN,
+- .stop0 = sd_stop0,
++ .init = sd_init,
++ .start = sd_start,
++ .stop0 = sd_stop0,
+ };
+
+ /* -- device connect -- */
+@@ -443,24 +286,28 @@ static int sd_probe(struct usb_interface *intf,
+ }
+
+ static struct usb_driver sd_driver = {
+- .name = MODULE_NAME,
+- .id_table = device_table,
+- .probe = sd_probe,
++ .name = MODULE_NAME,
++ .id_table = device_table,
++ .probe = sd_probe,
+ .disconnect = gspca_disconnect,
+ #ifdef CONFIG_PM
+ .suspend = gspca_suspend,
+- .resume = gspca_resume,
++ .resume = gspca_resume,
+ #endif
+ };
+
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
++
+ static void __exit sd_mod_exit(void)
+ {
+ usb_deregister(&sd_driver);
+diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
+index 65e4901..a75c1ca 100644
+--- a/drivers/media/video/gspca/gspca.c
++++ b/drivers/media/video/gspca/gspca.c
+@@ -38,15 +38,16 @@
+ #include "gspca.h"
+
+ /* global values */
+-#define DEF_NURBS 2 /* default number of URBs */
++#define DEF_NURBS 3 /* default number of URBs */
++#if DEF_NURBS > MAX_NURBS
++#error "DEF_NURBS too big"
++#endif
+
+ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
+ MODULE_DESCRIPTION("GSPCA USB Camera Driver");
+ MODULE_LICENSE("GPL");
+
+-#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 4, 0)
+-
+-static int video_nr = -1;
++#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 5, 0)
+
+ #ifdef GSPCA_DEBUG
+ int gspca_debug = D_ERR | D_PROBE;
+@@ -126,16 +127,18 @@ static void fill_frame(struct gspca_dev *gspca_dev,
+ struct urb *urb)
+ {
+ struct gspca_frame *frame;
+- __u8 *data; /* address of data in the iso message */
++ u8 *data; /* address of data in the iso message */
+ int i, len, st;
+ cam_pkt_op pkt_scan;
+
+ if (urb->status != 0) {
++ if (urb->status == -ESHUTDOWN)
++ return; /* disconnection */
+ #ifdef CONFIG_PM
+ if (!gspca_dev->frozen)
+ #endif
+ PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+- return; /* disconnection ? */
++ return;
+ }
+ pkt_scan = gspca_dev->sd_desc->pkt_scan;
+ for (i = 0; i < urb->number_of_packets; i++) {
+@@ -166,7 +169,7 @@ static void fill_frame(struct gspca_dev *gspca_dev,
+ /* let the packet be analyzed by the subdriver */
+ PDEBUG(D_PACK, "packet [%d] o:%d l:%d",
+ i, urb->iso_frame_desc[i].offset, len);
+- data = (__u8 *) urb->transfer_buffer
++ data = (u8 *) urb->transfer_buffer
+ + urb->iso_frame_desc[i].offset;
+ pkt_scan(gspca_dev, frame, data, len);
+ }
+@@ -182,8 +185,7 @@ static void fill_frame(struct gspca_dev *gspca_dev,
+ *
+ * Analyse each packet and call the subdriver for copy to the frame buffer.
+ */
+-static void isoc_irq(struct urb *urb
+-)
++static void isoc_irq(struct urb *urb)
+ {
+ struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+
+@@ -196,8 +198,7 @@ static void isoc_irq(struct urb *urb
+ /*
+ * bulk message interrupt from the USB device
+ */
+-static void bulk_irq(struct urb *urb
+-)
++static void bulk_irq(struct urb *urb)
+ {
+ struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+ struct gspca_frame *frame;
+@@ -209,6 +210,8 @@ static void bulk_irq(struct urb *urb
+ switch (urb->status) {
+ case 0:
+ break;
++ case -ESHUTDOWN:
++ return; /* disconnection */
+ case -ECONNRESET:
+ urb->status = 0;
+ break;
+@@ -217,7 +220,7 @@ static void bulk_irq(struct urb *urb
+ if (!gspca_dev->frozen)
+ #endif
+ PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+- return; /* disconnection ? */
++ return;
+ }
+
+ /* check the availability of the frame buffer */
+@@ -322,6 +325,7 @@ static int gspca_is_compressed(__u32 format)
+ case V4L2_PIX_FMT_JPEG:
+ case V4L2_PIX_FMT_SPCA561:
+ case V4L2_PIX_FMT_PAC207:
++ case V4L2_PIX_FMT_MR97310A:
+ return 1;
+ }
+ return 0;
+@@ -422,10 +426,8 @@ static void destroy_urbs(struct gspca_dev *gspca_dev)
+ if (urb == NULL)
+ break;
+
+- BUG_ON(!gspca_dev->dev);
+ gspca_dev->urb[i] = NULL;
+- if (!gspca_dev->present)
+- usb_kill_urb(urb);
++ usb_kill_urb(urb);
+ if (urb->transfer_buffer != NULL)
+ usb_buffer_free(gspca_dev->dev,
+ urb->transfer_buffer_length,
+@@ -439,22 +441,16 @@ static void destroy_urbs(struct gspca_dev *gspca_dev)
+ * look for an input transfer endpoint in an alternate setting
+ */
+ static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt,
+- __u8 epaddr,
+ __u8 xfer)
+ {
+ struct usb_host_endpoint *ep;
+ int i, attr;
+
+- epaddr |= USB_DIR_IN;
+ for (i = 0; i < alt->desc.bNumEndpoints; i++) {
+ ep = &alt->endpoint[i];
+- if (ep->desc.bEndpointAddress == epaddr) {
+- attr = ep->desc.bmAttributes
+- & USB_ENDPOINT_XFERTYPE_MASK;
+- if (attr == xfer)
+- return ep;
+- break;
+- }
++ attr = ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
++ if (attr == xfer)
++ return ep;
+ }
+ return NULL;
+ }
+@@ -478,23 +474,23 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev)
+ i = gspca_dev->alt; /* previous alt setting */
+
+ /* try isoc */
+- while (--i > 0) { /* alt 0 is unusable */
++ while (--i >= 0) {
+ ep = alt_xfer(&intf->altsetting[i],
+- gspca_dev->cam.epaddr,
+ USB_ENDPOINT_XFER_ISOC);
+ if (ep)
+ break;
+ }
+
+- /* if no isoc, try bulk */
++ /* if no isoc, try bulk (alt 0 only) */
+ if (ep == NULL) {
+ ep = alt_xfer(&intf->altsetting[0],
+- gspca_dev->cam.epaddr,
+ USB_ENDPOINT_XFER_BULK);
+ if (ep == NULL) {
+ err("no transfer endpoint found");
+ return NULL;
+ }
++ i = 0;
++ gspca_dev->bulk = 1;
+ }
+ PDEBUG(D_STREAM, "use alt %d ep 0x%02x",
+ i, ep->desc.bEndpointAddress);
+@@ -521,7 +517,7 @@ static int create_urbs(struct gspca_dev *gspca_dev,
+ /* calculate the packet size and the number of packets */
+ psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+
+- if (gspca_dev->alt != 0) { /* isoc */
++ if (!gspca_dev->bulk) { /* isoc */
+
+ /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */
+ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+@@ -601,6 +597,11 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+
++ if (!gspca_dev->present) {
++ ret = -ENODEV;
++ goto out;
++ }
++
+ /* set the higher alternate setting and
+ * loop until urb submit succeeds */
+ gspca_dev->alt = gspca_dev->nbalt;
+@@ -616,10 +617,9 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
+ goto out;
+
+ /* clear the bulk endpoint */
+- if (gspca_dev->alt == 0) /* if bulk transfer */
++ if (gspca_dev->bulk)
+ usb_clear_halt(gspca_dev->dev,
+- usb_rcvintpipe(gspca_dev->dev,
+- gspca_dev->cam.epaddr));
++ gspca_dev->urb[0]->pipe);
+
+ /* start the cam */
+ ret = gspca_dev->sd_desc->start(gspca_dev);
+@@ -630,7 +630,7 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
+ gspca_dev->streaming = 1;
+
+ /* some bulk transfers are started by the subdriver */
+- if (gspca_dev->alt == 0 && gspca_dev->cam.bulk_nurbs == 0)
++ if (gspca_dev->bulk && gspca_dev->cam.bulk_nurbs == 0)
+ break;
+
+ /* submit the URBs */
+@@ -671,11 +671,14 @@ static int gspca_set_alt0(struct gspca_dev *gspca_dev)
+ static void gspca_stream_off(struct gspca_dev *gspca_dev)
+ {
+ gspca_dev->streaming = 0;
+- if (gspca_dev->present
+- && gspca_dev->sd_desc->stopN)
+- gspca_dev->sd_desc->stopN(gspca_dev);
+- destroy_urbs(gspca_dev);
+- gspca_set_alt0(gspca_dev);
++ if (gspca_dev->present) {
++ if (gspca_dev->sd_desc->stopN)
++ gspca_dev->sd_desc->stopN(gspca_dev);
++ destroy_urbs(gspca_dev);
++ gspca_set_alt0(gspca_dev);
++ }
++
++ /* always call stop0 to free the subdriver's resources */
+ if (gspca_dev->sd_desc->stop0)
+ gspca_dev->sd_desc->stop0(gspca_dev);
+ PDEBUG(D_STREAM, "stream off OK");
+@@ -762,7 +765,6 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv,
+ fmtdesc->pixelformat = fmt_tb[index];
+ if (gspca_is_compressed(fmt_tb[index]))
+ fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED;
+- fmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmtdesc->description[0] = fmtdesc->pixelformat & 0xff;
+ fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff;
+ fmtdesc->description[2] = (fmtdesc->pixelformat >> 16) & 0xff;
+@@ -957,8 +959,15 @@ static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+ {
+ struct gspca_dev *gspca_dev = priv;
++ int ret;
+
+- memset(cap, 0, sizeof *cap);
++ /* protect the access to the usb device */
++ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
++ return -ERESTARTSYS;
++ if (!gspca_dev->present) {
++ ret = -ENODEV;
++ goto out;
++ }
+ strncpy(cap->driver, gspca_dev->sd_desc->name, sizeof cap->driver);
+ if (gspca_dev->dev->product != NULL) {
+ strncpy(cap->card, gspca_dev->dev->product,
+@@ -969,13 +978,15 @@ static int vidioc_querycap(struct file *file, void *priv,
+ le16_to_cpu(gspca_dev->dev->descriptor.idVendor),
+ le16_to_cpu(gspca_dev->dev->descriptor.idProduct));
+ }
+- strncpy(cap->bus_info, gspca_dev->dev->bus->bus_name,
+- sizeof cap->bus_info);
++ usb_make_path(gspca_dev->dev, cap->bus_info, sizeof(cap->bus_info));
+ cap->version = DRIVER_VERSION_NUMBER;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
+ | V4L2_CAP_STREAMING
+ | V4L2_CAP_READWRITE;
+- return 0;
++ ret = 0;
++out:
++ mutex_unlock(&gspca_dev->usb_lock);
++ return ret;
+ }
+
+ static int vidioc_queryctrl(struct file *file, void *priv,
+@@ -1038,7 +1049,10 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
+ PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value);
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+- ret = ctrls->set(gspca_dev, ctrl->value);
++ if (gspca_dev->present)
++ ret = ctrls->set(gspca_dev, ctrl->value);
++ else
++ ret = -ENODEV;
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+ }
+@@ -1062,7 +1076,10 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
+ return -EINVAL;
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+- ret = ctrls->get(gspca_dev, &ctrl->value);
++ if (gspca_dev->present)
++ ret = ctrls->get(gspca_dev, &ctrl->value);
++ else
++ ret = -ENODEV;
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+ }
+@@ -1081,7 +1098,6 @@ static int vidioc_s_audio(struct file *file, void *priv,
+ static int vidioc_g_audio(struct file *file, void *priv,
+ struct v4l2_audio *audio)
+ {
+- memset(audio, 0, sizeof *audio);
+ strcpy(audio->name, "Microphone");
+ return 0;
+ }
+@@ -1115,7 +1131,6 @@ static int vidioc_enum_input(struct file *file, void *priv,
+
+ if (input->index != 0)
+ return -EINVAL;
+- memset(input, 0, sizeof *input);
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ strncpy(input->name, gspca_dev->sd_desc->name,
+ sizeof input->name);
+@@ -1224,10 +1239,7 @@ static int vidioc_streamon(struct file *file, void *priv,
+ return -EINVAL;
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+- if (!gspca_dev->present) {
+- ret = -ENODEV;
+- goto out;
+- }
++
+ if (gspca_dev->nframes == 0
+ || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) {
+ ret = -EINVAL;
+@@ -1295,7 +1307,10 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv,
+ return -EINVAL;
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+- ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp);
++ if (gspca_dev->present)
++ ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp);
++ else
++ ret = -ENODEV;
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+ }
+@@ -1310,7 +1325,10 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv,
+ return -EINVAL;
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+- ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
++ if (gspca_dev->present)
++ ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
++ else
++ ret = -ENODEV;
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+ }
+@@ -1320,8 +1338,6 @@ static int vidioc_g_parm(struct file *filp, void *priv,
+ {
+ struct gspca_dev *gspca_dev = priv;
+
+- memset(parm, 0, sizeof *parm);
+- parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ parm->parm.capture.readbuffers = gspca_dev->nbufread;
+
+ if (gspca_dev->sd_desc->get_streamparm) {
+@@ -1329,7 +1345,11 @@ static int vidioc_g_parm(struct file *filp, void *priv,
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+- ret = gspca_dev->sd_desc->get_streamparm(gspca_dev, parm);
++ if (gspca_dev->present)
++ ret = gspca_dev->sd_desc->get_streamparm(gspca_dev,
++ parm);
++ else
++ ret = -ENODEV;
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+ }
+@@ -1354,7 +1374,11 @@ static int vidioc_s_parm(struct file *filp, void *priv,
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
+- ret = gspca_dev->sd_desc->set_streamparm(gspca_dev, parm);
++ if (gspca_dev->present)
++ ret = gspca_dev->sd_desc->set_streamparm(gspca_dev,
++ parm);
++ else
++ ret = -ENODEV;
+ mutex_unlock(&gspca_dev->usb_lock);
+ return ret;
+ }
+@@ -1382,7 +1406,6 @@ static int vidiocgmbuf(struct file *file, void *priv,
+ {
+ struct v4l2_format fmt;
+
+- memset(&fmt, 0, sizeof fmt);
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ i = gspca_dev->cam.nmodes - 1; /* highest mode */
+ fmt.fmt.pix.width = gspca_dev->cam.cam_mode[i].width;
+@@ -1528,7 +1551,8 @@ static int frame_wait(struct gspca_dev *gspca_dev,
+
+ if (gspca_dev->sd_desc->dq_callback) {
+ mutex_lock(&gspca_dev->usb_lock);
+- gspca_dev->sd_desc->dq_callback(gspca_dev);
++ if (gspca_dev->present)
++ gspca_dev->sd_desc->dq_callback(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
+ return j;
+@@ -1550,6 +1574,9 @@ static int vidioc_dqbuf(struct file *file, void *priv,
+ if (v4l2_buf->memory != gspca_dev->memory)
+ return -EINVAL;
+
++ if (!gspca_dev->present)
++ return -ENODEV;
++
+ /* if not streaming, be sure the application will not loop forever */
+ if (!(file->f_flags & O_NONBLOCK)
+ && !gspca_dev->streaming && gspca_dev->users == 1)
+@@ -1700,8 +1727,6 @@ static unsigned int dev_poll(struct file *file, poll_table *wait)
+ PDEBUG(D_FRAM, "poll");
+
+ poll_wait(file, &gspca_dev->wq, wait);
+- if (!gspca_dev->present)
+- return POLLERR;
+
+ /* if reqbufs is not done, the user would use read() */
+ if (gspca_dev->nframes == 0) {
+@@ -1714,10 +1739,6 @@ static unsigned int dev_poll(struct file *file, poll_table *wait)
+
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0)
+ return POLLERR;
+- if (!gspca_dev->present) {
+- ret = POLLERR;
+- goto out;
+- }
+
+ /* check the next incoming buffer */
+ i = gspca_dev->fr_o;
+@@ -1726,8 +1747,9 @@ static unsigned int dev_poll(struct file *file, poll_table *wait)
+ ret = POLLIN | POLLRDNORM; /* something to read */
+ else
+ ret = 0;
+-out:
+ mutex_unlock(&gspca_dev->queue_lock);
++ if (!gspca_dev->present)
++ return POLLHUP;
+ return ret;
+ }
+
+@@ -1925,7 +1947,7 @@ int gspca_dev_probe(struct usb_interface *intf,
+ gspca_dev->present = 1;
+ ret = video_register_device(&gspca_dev->vdev,
+ VFL_TYPE_GRABBER,
+- video_nr);
++ -1);
+ if (ret < 0) {
+ err("video_register_device err %d", ret);
+ goto out;
+@@ -1953,10 +1975,16 @@ void gspca_disconnect(struct usb_interface *intf)
+
+ mutex_lock(&gspca_dev->usb_lock);
+ gspca_dev->present = 0;
+- mutex_unlock(&gspca_dev->usb_lock);
+
+- destroy_urbs(gspca_dev);
++ if (gspca_dev->streaming) {
++ destroy_urbs(gspca_dev);
++ wake_up_interruptible(&gspca_dev->wq);
++ }
++
++ /* the device is freed at exit of this function */
+ gspca_dev->dev = NULL;
++ mutex_unlock(&gspca_dev->usb_lock);
++
+ usb_set_intfdata(intf, NULL);
+
+ /* release the device */
+diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h
+index c90af9c..e4d4cf6 100644
+--- a/drivers/media/video/gspca/gspca.h
++++ b/drivers/media/video/gspca/gspca.h
+@@ -33,19 +33,13 @@ extern int gspca_debug;
+ #endif
+ #undef err
+ #define err(fmt, args...) \
+- do {\
+- printk(KERN_ERR MODULE_NAME ": " fmt "\n", ## args); \
+- } while (0)
++ printk(KERN_ERR MODULE_NAME ": " fmt "\n", ## args)
+ #undef info
+ #define info(fmt, args...) \
+- do {\
+- printk(KERN_INFO MODULE_NAME ": " fmt "\n", ## args); \
+- } while (0)
++ printk(KERN_INFO MODULE_NAME ": " fmt "\n", ## args)
+ #undef warn
+ #define warn(fmt, args...) \
+- do {\
+- printk(KERN_WARNING MODULE_NAME ": " fmt "\n", ## args); \
+- } while (0)
++ printk(KERN_WARNING MODULE_NAME ": " fmt "\n", ## args)
+
+ #define GSPCA_MAX_FRAMES 16 /* maximum number of video frame buffers */
+ /* image transfers */
+@@ -62,7 +56,6 @@ struct cam {
+ * - cannot be > MAX_NURBS
+ * - when 0 and bulk_size != 0 means
+ * 1 URB and submit done by subdriver */
+- __u8 epaddr;
+ };
+
+ struct gspca_dev;
+@@ -174,6 +167,7 @@ struct gspca_dev {
+ __u8 iface; /* USB interface number */
+ __u8 alt; /* USB alternate setting */
+ __u8 nbalt; /* number of USB alternate settings */
++ u8 bulk; /* image transfer by 0:isoc / 1:bulk */
+ };
+
+ int gspca_dev_probe(struct usb_interface *intf,
+diff --git a/drivers/media/video/gspca/jpeg.h b/drivers/media/video/gspca/jpeg.h
+index d823b47..de63c36 100644
+--- a/drivers/media/video/gspca/jpeg.h
++++ b/drivers/media/video/gspca/jpeg.h
+@@ -24,171 +24,39 @@
+ *
+ */
+
+-/* start of jpeg frame + quantization table */
+-static const unsigned char quant[][0x88] = {
+-/* index 0 - Q40*/
+- {
++/*
++ * generation options
++ * CONEX_CAM Conexant if present
++ */
++
++/* JPEG header */
++static const u8 jpeg_head[] = {
+ 0xff, 0xd8, /* jpeg */
+- 0xff, 0xdb, 0x00, 0x84, /* DQT */
+-0, /* quantization table part 1 */
+- 20, 14, 15, 18, 15, 13, 20, 18, 16, 18, 23, 21, 20, 24, 30, 50,
+- 33, 30, 28, 28, 30, 61, 44, 46, 36, 50, 73, 64, 76, 75, 71, 64,
+- 70, 69, 80, 90, 115, 98, 80, 85, 109, 86, 69, 70, 100, 136, 101,
+- 109,
+- 119, 123, 129, 130, 129, 78, 96, 141, 151, 140, 125, 150, 115,
+- 126, 129, 124,
+-1, /* quantization table part 2 */
+- 21, 23, 23, 30, 26, 30, 59, 33, 33, 59, 124, 83, 70, 83, 124, 124,
+- 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
+- 124, 124, 124,
+- 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
+- 124, 124, 124,
+- 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
+- 124, 124, 124},
+-/* index 1 - Q50 */
+- {
+- 0xff, 0xd8,
+- 0xff, 0xdb, 0x00, 0x84, /* DQT */
+-0,
+- 16, 11, 12, 14, 12, 10, 16, 14, 13, 14, 18, 17, 16, 19, 24, 40,
+- 26, 24, 22, 22, 24, 49, 35, 37, 29, 40, 58, 51, 61, 60, 57, 51,
+- 56, 55, 64, 72, 92, 78, 64, 68, 87, 69, 55, 56, 80, 109, 81, 87,
+- 95, 98, 103, 104, 103, 62, 77, 113, 121, 112, 100, 120, 92, 101,
+- 103, 99,
+-1,
+- 17, 18, 18, 24, 21, 24, 47, 26, 26, 47, 99, 66, 56, 66, 99, 99,
+- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
+- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
+- 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99},
+-/* index 2 Q60 */
+- {
+- 0xff, 0xd8,
+- 0xff, 0xdb, 0x00, 0x84, /* DQT */
+-0,
+- 13, 9, 10, 11, 10, 8, 13, 11, 10, 11, 14, 14, 13, 15, 19, 32,
+- 21, 19, 18, 18, 19, 39, 28, 30, 23, 32, 46, 41, 49, 48, 46, 41,
+- 45, 44, 51, 58, 74, 62, 51, 54, 70, 55, 44, 45, 64, 87, 65, 70,
+- 76, 78, 82, 83, 82, 50, 62, 90, 97, 90, 80, 96, 74, 81, 82, 79,
+-1,
+- 14, 14, 14, 19, 17, 19, 38, 21, 21, 38, 79, 53, 45, 53, 79, 79,
+- 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79,
+- 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79,
+- 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79, 79},
+-/* index 3 - Q70 */
+- {
+- 0xff, 0xd8,
+- 0xff, 0xdb, 0x00, 0x84, /* DQT */
+-0,
+- 10, 7, 7, 8, 7, 6, 10, 8, 8, 8, 11, 10, 10, 11, 14, 24,
+- 16, 14, 13, 13, 14, 29, 21, 22, 17, 24, 35, 31, 37, 36, 34, 31,
+- 34, 33, 38, 43, 55, 47, 38, 41, 52, 41, 33, 34, 48, 65, 49, 52,
+- 57, 59, 62, 62, 62, 37, 46, 68, 73, 67, 60, 72, 55, 61, 62, 59,
+-1,
+- 10, 11, 11, 14, 13, 14, 28, 16, 16, 28, 59, 40, 34, 40, 59, 59,
+- 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+- 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59,
+- 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59, 59},
+-/* index 4 - Q80 */
+- {
+- 0xff, 0xd8,
+- 0xff, 0xdb, 0x00, 0x84, /* DQT */
+-0,
+- 6, 4, 5, 6, 5, 4, 6, 6, 5, 6, 7, 7, 6, 8, 10, 16,
+- 10, 10, 9, 9, 10, 20, 14, 15, 12, 16, 23, 20, 24, 24, 23, 20,
+- 22, 22, 26, 29, 37, 31, 26, 27, 35, 28, 22, 22, 32, 44, 32, 35,
+- 38, 39, 41, 42, 41, 25, 31, 45, 48, 45, 40, 48, 37, 40, 41, 40,
+-1,
+- 7, 7, 7, 10, 8, 10, 19, 10, 10, 19, 40, 26, 22, 26, 40, 40,
+- 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
+- 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
+- 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40},
+-/* index 5 - Q85 */
+- {
+- 0xff, 0xd8,
+- 0xff, 0xdb, 0x00, 0x84, /* DQT */
+-0,
+- 5, 3, 4, 4, 4, 3, 5, 4, 4, 4, 5, 5, 5, 6, 7, 12,
+- 8, 7, 7, 7, 7, 15, 11, 11, 9, 12, 17, 15, 18, 18, 17, 15,
+- 17, 17, 19, 22, 28, 23, 19, 20, 26, 21, 17, 17, 24, 33, 24, 26,
+- 29, 29, 31, 31, 31, 19, 23, 34, 36, 34, 30, 36, 28, 30, 31, 30,
+-1,
+- 5, 5, 5, 7, 6, 7, 14, 8, 8, 14, 30, 20, 17, 20, 30, 30,
+- 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+- 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
+- 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30},
+-/* index 6 - 86 */
+-{
+- 0xff, 0xd8,
+- 0xff, 0xdb, 0x00, 0x84, /* DQT */
+-0,
+- 0x04, 0x03, 0x03, 0x04, 0x03, 0x03, 0x04, 0x04,
+- 0x04, 0x04, 0x05, 0x05, 0x04, 0x05, 0x07, 0x0B,
+- 0x07, 0x07, 0x06, 0x06, 0x07, 0x0E, 0x0A, 0x0A,
+- 0x08, 0x0B, 0x10, 0x0E, 0x11, 0x11, 0x10, 0x0E,
+- 0x10, 0x0F, 0x12, 0x14, 0x1A, 0x16, 0x12, 0x13,
+- 0x18, 0x13, 0x0F, 0x10, 0x16, 0x1F, 0x17, 0x18,
+- 0x1B, 0x1B, 0x1D, 0x1D, 0x1D, 0x11, 0x16, 0x20,
+- 0x22, 0x1F, 0x1C, 0x22, 0x1A, 0x1C, 0x1D, 0x1C,
+-1,
+- 0x05, 0x05, 0x05, 0x07, 0x06, 0x07, 0x0D, 0x07,
+- 0x07, 0x0D, 0x1C, 0x12, 0x10, 0x12, 0x1C, 0x1C,
+- 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
+- 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
+- 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
+- 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
+- 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
+- 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C, 0x1C,
+- },
+-/* index 7 - 88 */
+-{
+- 0xff, 0xd8,
+- 0xff, 0xdb, 0x00, 0x84, /* DQT */
+-0,
+- 0x04, 0x03, 0x03, 0x03, 0x03, 0x02, 0x04, 0x03,
+- 0x03, 0x03, 0x04, 0x04, 0x04, 0x05, 0x06, 0x0A,
+- 0x06, 0x06, 0x05, 0x05, 0x06, 0x0C, 0x08, 0x09,
+- 0x07, 0x0A, 0x0E, 0x0C, 0x0F, 0x0E, 0x0E, 0x0C,
+- 0x0D, 0x0D, 0x0F, 0x11, 0x16, 0x13, 0x0F, 0x10,
+- 0x15, 0x11, 0x0D, 0x0D, 0x13, 0x1A, 0x13, 0x15,
+- 0x17, 0x18, 0x19, 0x19, 0x19, 0x0F, 0x12, 0x1B,
+- 0x1D, 0x1B, 0x18, 0x1D, 0x16, 0x18, 0x19, 0x18,
+-1,
+- 0x04, 0x04, 0x04, 0x06, 0x05, 0x06, 0x0B, 0x06,
+- 0x06, 0x0B, 0x18, 0x10, 0x0D, 0x10, 0x18, 0x18,
+- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+- 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+-},
+-/* index 8 - ?? */
+-{
+- 0xff, 0xd8,
++
++/* quantization table quality 50% */
+ 0xff, 0xdb, 0x00, 0x84, /* DQT */
+ 0,
+- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
+- 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x03, 0x05,
+- 0x03, 0x03, 0x03, 0x03, 0x03, 0x06, 0x04, 0x05,
+- 0x04, 0x05, 0x07, 0x06, 0x08, 0x08, 0x07, 0x06,
+- 0x07, 0x07, 0x08, 0x09, 0x0C, 0x0A, 0x08, 0x09,
+- 0x0B, 0x09, 0x07, 0x07, 0x0A, 0x0E, 0x0A, 0x0B,
+- 0x0C, 0x0C, 0x0D, 0x0D, 0x0D, 0x08, 0x0A, 0x0E,
+- 0x0F, 0x0E, 0x0D, 0x0F, 0x0C, 0x0D, 0x0D, 0x0C,
++#define JPEG_QT0_OFFSET 7
++ 0x10, 0x0b, 0x0c, 0x0e, 0x0c, 0x0a, 0x10, 0x0e,
++ 0x0d, 0x0e, 0x12, 0x11, 0x10, 0x13, 0x18, 0x28,
++ 0x1a, 0x18, 0x16, 0x16, 0x18, 0x31, 0x23, 0x25,
++ 0x1d, 0x28, 0x3a, 0x33, 0x3d, 0x3c, 0x39, 0x33,
++ 0x38, 0x37, 0x40, 0x48, 0x5c, 0x4e, 0x40, 0x44,
++ 0x57, 0x45, 0x37, 0x38, 0x50, 0x6d, 0x51, 0x57,
++ 0x5f, 0x62, 0x67, 0x68, 0x67, 0x3e, 0x4d, 0x71,
++ 0x79, 0x70, 0x64, 0x78, 0x5c, 0x65, 0x67, 0x63,
+ 1,
+- 0x02, 0x02, 0x02, 0x03, 0x03, 0x03, 0x06, 0x03,
+- 0x03, 0x06, 0x0C, 0x08, 0x07, 0x08, 0x0C, 0x0C,
+- 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+- 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+- 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+- 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+- 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C,
+- 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C
+-}
+-};
++#define JPEG_QT1_OFFSET 72
++ 0x11, 0x12, 0x12, 0x18, 0x15, 0x18, 0x2f, 0x1a,
++ 0x1a, 0x2f, 0x63, 0x42, 0x38, 0x42, 0x63, 0x63,
++ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
++ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
++ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
++ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
++ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
++ 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63, 0x63,
+
+-/* huffman table + start of SOF0 */
+-static unsigned char huffman[] = {
++/* huffman table */
+ 0xff, 0xc4, 0x01, 0xa2,
+ 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+@@ -244,58 +112,57 @@ static unsigned char huffman[] = {
+ 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa,
+ #ifdef CONEX_CAM
+ /* the Conexant frames start with SOF0 */
++#define JPEG_HDR_SZ 556
+ #else
+ 0xff, 0xc0, 0x00, 0x11, /* SOF0 (start of frame 0 */
+ 0x08, /* data precision */
+-#endif
+-};
+-
+-#ifndef CONEX_CAM
+-/* variable part:
+- * 0x01, 0xe0, height
+- * 0x02, 0x80, width
+- * 0x03, component number
+- * 0x01,
+- * 0x21, samples Y
+- */
+-
+-/* end of header */
+-static unsigned char eoh[] = {
++#define JPEG_HEIGHT_OFFSET 561
++ 0x01, 0xe0, /* height */
++ 0x02, 0x80, /* width */
++ 0x03, /* component number */
++ 0x01,
++ 0x21, /* samples Y */
+ 0x00, /* quant Y */
+ 0x02, 0x11, 0x01, /* samples CbCr - quant CbCr */
+ 0x03, 0x11, 0x01,
+
+ 0xff, 0xda, 0x00, 0x0c, /* SOS (start of scan) */
+ 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
+-};
++#define JPEG_HDR_SZ 589
+ #endif
++};
+
+-/* -- output the JPEG header -- */
+-static void jpeg_put_header(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame,
+- int qindex,
+- int samplesY)
++/* define the JPEG header */
++static void jpeg_define(u8 *jpeg_hdr,
++ int height,
++ int width,
++ int samplesY)
+ {
++ memcpy(jpeg_hdr, jpeg_head, sizeof jpeg_head);
+ #ifndef CONEX_CAM
+- unsigned char tmpbuf[8];
++ jpeg_hdr[JPEG_HEIGHT_OFFSET + 0] = height >> 8;
++ jpeg_hdr[JPEG_HEIGHT_OFFSET + 1] = height & 0xff;
++ jpeg_hdr[JPEG_HEIGHT_OFFSET + 2] = width >> 8;
++ jpeg_hdr[JPEG_HEIGHT_OFFSET + 3] = width & 0xff;
++ jpeg_hdr[JPEG_HEIGHT_OFFSET + 6] = samplesY;
+ #endif
++}
+
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+- (unsigned char *) quant[qindex], sizeof quant[0]);
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+- (unsigned char *) huffman, sizeof huffman);
+-#ifndef CONEX_CAM
+- tmpbuf[0] = gspca_dev->height >> 8;
+- tmpbuf[1] = gspca_dev->height & 0xff;
+- tmpbuf[2] = gspca_dev->width >> 8;
+- tmpbuf[3] = gspca_dev->width & 0xff;
+- tmpbuf[4] = 0x03; /* component number */
+- tmpbuf[5] = 0x01; /* first component */
+- tmpbuf[6] = samplesY;
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+- tmpbuf, 7);
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+- eoh, sizeof eoh);
+-#endif
++/* set the JPEG quality */
++static void jpeg_set_qual(u8 *jpeg_hdr,
++ int quality)
++{
++ int i, sc;
++
++ if (quality < 50)
++ sc = 5000 / quality;
++ else
++ sc = 200 - quality * 2;
++ for (i = 0; i < 64; i++) {
++ jpeg_hdr[JPEG_QT0_OFFSET + i] =
++ (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100;
++ jpeg_hdr[JPEG_QT1_OFFSET + i] =
++ (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100;
++ }
+ }
+ #endif
+diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c
+index ed906fe..b35e483 100644
+--- a/drivers/media/video/gspca/m5602/m5602_core.c
++++ b/drivers/media/video/gspca/m5602/m5602_core.c
+@@ -332,7 +332,6 @@ static int m5602_configure(struct gspca_dev *gspca_dev,
+ int err;
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = M5602_ISOC_ENDPOINT_ADDR;
+ sd->desc = &sd_desc;
+
+ if (dump_bridge)
+@@ -374,8 +373,10 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init mod_m5602_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c
+index 3d2090e..75e8d14 100644
+--- a/drivers/media/video/gspca/mars.c
++++ b/drivers/media/video/gspca/mars.c
+@@ -32,17 +32,91 @@ MODULE_LICENSE("GPL");
+ struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+- char qindex;
++ u8 brightness;
++ u8 colors;
++ u8 gamma;
++ u8 sharpness;
++ u8 quality;
++#define QUALITY_MIN 40
++#define QUALITY_MAX 70
++#define QUALITY_DEF 50
++
++ u8 *jpeg_hdr;
+ };
+
+ /* V4L2 controls supported by the driver */
++static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
++
+ static struct ctrl sd_ctrls[] = {
++ {
++ {
++ .id = V4L2_CID_BRIGHTNESS,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Brightness",
++ .minimum = 0,
++ .maximum = 30,
++ .step = 1,
++#define BRIGHTNESS_DEF 15
++ .default_value = BRIGHTNESS_DEF,
++ },
++ .set = sd_setbrightness,
++ .get = sd_getbrightness,
++ },
++ {
++ {
++ .id = V4L2_CID_SATURATION,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Color",
++ .minimum = 1,
++ .maximum = 255,
++ .step = 1,
++#define COLOR_DEF 200
++ .default_value = COLOR_DEF,
++ },
++ .set = sd_setcolors,
++ .get = sd_getcolors,
++ },
++ {
++ {
++ .id = V4L2_CID_GAMMA,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Gamma",
++ .minimum = 0,
++ .maximum = 3,
++ .step = 1,
++#define GAMMA_DEF 1
++ .default_value = GAMMA_DEF,
++ },
++ .set = sd_setgamma,
++ .get = sd_getgamma,
++ },
++ {
++ {
++ .id = V4L2_CID_SHARPNESS,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Sharpness",
++ .minimum = 0,
++ .maximum = 2,
++ .step = 1,
++#define SHARPNESS_DEF 1
++ .default_value = SHARPNESS_DEF,
++ },
++ .set = sd_setsharpness,
++ .get = sd_getsharpness,
++ },
+ };
+
+ static const struct v4l2_pix_format vga_mode[] = {
+ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+- .sizeimage = 320 * 240 * 3 / 8 + 589,
++ .sizeimage = 320 * 240 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 2},
+ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+@@ -52,65 +126,45 @@ static const struct v4l2_pix_format vga_mode[] = {
+ .priv = 1},
+ };
+
+-/* MI Register table //elvis */
+-enum {
+- REG_HW_MI_0,
+- REG_HW_MI_1,
+- REG_HW_MI_2,
+- REG_HW_MI_3,
+- REG_HW_MI_4,
+- REG_HW_MI_5,
+- REG_HW_MI_6,
+- REG_HW_MI_7,
+- REG_HW_MI_9 = 0x09,
+- REG_HW_MI_B = 0x0B,
+- REG_HW_MI_C,
+- REG_HW_MI_D,
+- REG_HW_MI_1E = 0x1E,
+- REG_HW_MI_20 = 0x20,
+- REG_HW_MI_2B = 0x2B,
+- REG_HW_MI_2C,
+- REG_HW_MI_2D,
+- REG_HW_MI_2E,
+- REG_HW_MI_35 = 0x35,
+- REG_HW_MI_5F = 0x5f,
+- REG_HW_MI_60,
+- REG_HW_MI_61,
+- REG_HW_MI_62,
+- REG_HW_MI_63,
+- REG_HW_MI_64,
+- REG_HW_MI_F1 = 0xf1,
+- ATTR_TOTAL_MI_REG = 0xf2
++static const __u8 mi_data[0x20] = {
++/* 01 02 03 04 05 06 07 08 */
++ 0x48, 0x22, 0x01, 0x47, 0x10, 0x00, 0x00, 0x00,
++/* 09 0a 0b 0c 0d 0e 0f 10 */
++ 0x00, 0x01, 0x30, 0x01, 0x30, 0x01, 0x30, 0x01,
++/* 11 12 13 14 15 16 17 18 */
++ 0x30, 0x00, 0x04, 0x00, 0x06, 0x01, 0xe2, 0x02,
++/* 19 1a 1b 1c 1d 1e 1f 20 */
++ 0x82, 0x00, 0x20, 0x17, 0x80, 0x08, 0x0c, 0x00
+ };
+
+-/* the bytes to write are in gspca_dev->usb_buf */
++/* write <len> bytes from gspca_dev->usb_buf */
+ static int reg_w(struct gspca_dev *gspca_dev,
+- __u16 index, int len)
++ int len)
+ {
+- int rc;
+-
+- rc = usb_control_msg(gspca_dev->dev,
+- usb_sndbulkpipe(gspca_dev->dev, 4),
+- 0x12,
+- 0xc8, /* ?? */
+- 0, /* value */
+- index, gspca_dev->usb_buf, len, 500);
+- if (rc < 0)
+- PDEBUG(D_ERR, "reg write [%02x] error %d", index, rc);
+- return rc;
++ int alen, ret;
++
++ ret = usb_bulk_msg(gspca_dev->dev,
++ usb_sndbulkpipe(gspca_dev->dev, 4),
++ gspca_dev->usb_buf,
++ len,
++ &alen,
++ 500); /* timeout in milliseconds */
++ if (ret < 0)
++ PDEBUG(D_ERR, "reg write [%02x] error %d",
++ gspca_dev->usb_buf[0], ret);
++ return ret;
+ }
+
+-static void bulk_w(struct gspca_dev *gspca_dev,
+- __u16 *pch,
+- __u16 Address)
++static void mi_w(struct gspca_dev *gspca_dev,
++ u8 addr,
++ u8 value)
+ {
+ gspca_dev->usb_buf[0] = 0x1f;
+ gspca_dev->usb_buf[1] = 0; /* control byte */
+- gspca_dev->usb_buf[2] = Address;
+- gspca_dev->usb_buf[3] = *pch >> 8; /* high byte */
+- gspca_dev->usb_buf[4] = *pch; /* low byte */
++ gspca_dev->usb_buf[2] = addr;
++ gspca_dev->usb_buf[3] = value;
+
+- reg_w(gspca_dev, Address, 5);
++ reg_w(gspca_dev, 4);
+ }
+
+ /* this function is called at probe time */
+@@ -121,10 +175,14 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = 0x01;
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+- sd->qindex = 1; /* set the quantization table */
++ sd->brightness = BRIGHTNESS_DEF;
++ sd->colors = COLOR_DEF;
++ sd->gamma = GAMMA_DEF;
++ sd->sharpness = SHARPNESS_DEF;
++ sd->quality = QUALITY_DEF;
++ gspca_dev->nbalt = 9; /* use the altsetting 08 */
+ return 0;
+ }
+
+@@ -136,24 +194,22 @@ static int sd_init(struct gspca_dev *gspca_dev)
+
+ static int sd_start(struct gspca_dev *gspca_dev)
+ {
++ struct sd *sd = (struct sd *) gspca_dev;
+ int err_code;
+- __u8 *data;
+- __u16 *MI_buf;
+- int h_size, v_size;
+- int intpipe;
+-
+- PDEBUG(D_STREAM, "camera start, iface %d, alt 8", gspca_dev->iface);
+- err_code = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 8);
+- if (err_code < 0) {
+- PDEBUG(D_ERR|D_STREAM, "Set packet size: set interface error");
+- return err_code;
+- }
++ u8 *data;
++ int i;
++
++ /* create the JPEG header */
++ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
++ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
++ 0x21); /* JPEG 422 */
++ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+
+ data = gspca_dev->usb_buf;
++
+ data[0] = 0x01; /* address */
+ data[1] = 0x01;
+-
+- err_code = reg_w(gspca_dev, data[0], 2);
++ err_code = reg_w(gspca_dev, 2);
+ if (err_code < 0)
+ return err_code;
+
+@@ -163,30 +219,28 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ data[0] = 0x00; /* address */
+ data[1] = 0x0c | 0x01; /* reg 0 */
+ data[2] = 0x01; /* reg 1 */
+- h_size = gspca_dev->width;
+- v_size = gspca_dev->height;
+- data[3] = h_size / 8; /* h_size , reg 2 */
+- data[4] = v_size / 8; /* v_size , reg 3 */
++ data[3] = gspca_dev->width / 8; /* h_size , reg 2 */
++ data[4] = gspca_dev->height / 8; /* v_size , reg 3 */
+ data[5] = 0x30; /* reg 4, MI, PAS5101 :
+ * 0x30 for 24mhz , 0x28 for 12mhz */
+- data[6] = 4; /* reg 5, H start */
+- data[7] = 0xc0; /* reg 6, gamma 1.5 */
+- data[8] = 3; /* reg 7, V start */
++ data[6] = 0x02; /* reg 5, H start - was 0x04 */
++ data[7] = sd->gamma * 0x40; /* reg 0x06: gamma */
++ data[8] = 0x01; /* reg 7, V start - was 0x03 */
+ /* if (h_size == 320 ) */
+ /* data[9]= 0x56; * reg 8, 24MHz, 2:1 scale down */
+ /* else */
+ data[9] = 0x52; /* reg 8, 24MHz, no scale down */
+- data[10] = 0x5d; /* reg 9, I2C device address
+- * [for PAS5101 (0x40)] [for MI (0x5d)] */
++/*jfm: from win trace*/
++ data[10] = 0x18;
+
+- err_code = reg_w(gspca_dev, data[0], 11);
++ err_code = reg_w(gspca_dev, 11);
+ if (err_code < 0)
+ return err_code;
+
+ data[0] = 0x23; /* address */
+ data[1] = 0x09; /* reg 35, append frame header */
+
+- err_code = reg_w(gspca_dev, data[0], 2);
++ err_code = reg_w(gspca_dev, 2);
+ if (err_code < 0)
+ return err_code;
+
+@@ -197,137 +251,57 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ /* else */
+ data[1] = 50; /* 50 reg 60, pc-cam frame size
+ * (unit: 4KB) 200KB */
+- err_code = reg_w(gspca_dev, data[0], 2);
++ err_code = reg_w(gspca_dev, 2);
+ if (err_code < 0)
+ return err_code;
+
+- if (0) { /* fixed dark-gain */
+- data[1] = 0; /* reg 94, Y Gain (1.75) */
+- data[2] = 0; /* reg 95, UV Gain (1.75) */
+- data[3] = 0x3f; /* reg 96, Y Gain/UV Gain/disable
+- * auto dark-gain */
+- data[4] = 0; /* reg 97, set fixed dark level */
+- data[5] = 0; /* reg 98, don't care */
+- } else { /* auto dark-gain */
+- data[1] = 0; /* reg 94, Y Gain (auto) */
+- data[2] = 0; /* reg 95, UV Gain (1.75) */
+- data[3] = 0x78; /* reg 96, Y Gain/UV Gain/disable
+- * auto dark-gain */
+- switch (gspca_dev->width) {
+-/* case 1280: */
+-/* data[4] = 154;
+- * reg 97, %3 shadow point (unit: 256 pixel) */
+-/* data[5] = 51;
+- * reg 98, %1 highlight point
+- * (uint: 256 pixel) */
+-/* break; */
+- default:
+-/* case 640: */
+- data[4] = 36; /* reg 97, %3 shadow point
+- * (unit: 256 pixel) */
+- data[5] = 12; /* reg 98, %1 highlight point
+- * (uint: 256 pixel) */
+- break;
+- case 320:
+- data[4] = 9; /* reg 97, %3 shadow point
+- * (unit: 256 pixel) */
+- data[5] = 3; /* reg 98, %1 highlight point
+- * (uint: 256 pixel) */
+- break;
+- }
+- }
+ /* auto dark-gain */
+ data[0] = 0x5e; /* address */
+-
+- err_code = reg_w(gspca_dev, data[0], 6);
++ data[1] = 0; /* reg 94, Y Gain (auto) */
++/*jfm: from win trace*/
++ /* reg 0x5f/0x60 (LE) = saturation */
++ /* h (60): xxxx x100
++ * l (5f): xxxx x000 */
++ data[2] = sd->colors << 3;
++ data[3] = ((sd->colors >> 2) & 0xf8) | 0x04;
++ data[4] = sd->brightness; /* reg 0x61 = brightness */
++ data[5] = 0x00;
++
++ err_code = reg_w(gspca_dev, 6);
+ if (err_code < 0)
+ return err_code;
+
+ data[0] = 0x67;
+- data[1] = 0x13; /* reg 103, first pixel B, disable sharpness */
+- err_code = reg_w(gspca_dev, data[0], 2);
++/*jfm: from win trace*/
++ data[1] = sd->sharpness * 4 + 3;
++ data[2] = 0x14;
++ err_code = reg_w(gspca_dev, 3);
+ if (err_code < 0)
+ return err_code;
+
+- /*
+- * initialize the value of MI sensor...
+- */
+- MI_buf = kzalloc(ATTR_TOTAL_MI_REG * sizeof *MI_buf, GFP_KERNEL);
+- MI_buf[REG_HW_MI_1] = 0x000a;
+- MI_buf[REG_HW_MI_2] = 0x000c;
+- MI_buf[REG_HW_MI_3] = 0x0405;
+- MI_buf[REG_HW_MI_4] = 0x0507;
+- /* mi_Attr_Reg_[REG_HW_MI_5] = 0x01ff;//13 */
+- MI_buf[REG_HW_MI_5] = 0x0013; /* 13 */
+- MI_buf[REG_HW_MI_6] = 0x001f; /* vertical blanking */
+- /* mi_Attr_Reg_[REG_HW_MI_6] = 0x0400; // vertical blanking */
+- MI_buf[REG_HW_MI_7] = 0x0002;
+- /* mi_Attr_Reg_[REG_HW_MI_9] = 0x015f; */
+- /* mi_Attr_Reg_[REG_HW_MI_9] = 0x030f; */
+- MI_buf[REG_HW_MI_9] = 0x0374;
+- MI_buf[REG_HW_MI_B] = 0x0000;
+- MI_buf[REG_HW_MI_C] = 0x0000;
+- MI_buf[REG_HW_MI_D] = 0x0000;
+- MI_buf[REG_HW_MI_1E] = 0x8000;
+-/* mi_Attr_Reg_[REG_HW_MI_20] = 0x1104; */
+- MI_buf[REG_HW_MI_20] = 0x1104; /* 0x111c; */
+- MI_buf[REG_HW_MI_2B] = 0x0008;
+-/* mi_Attr_Reg_[REG_HW_MI_2C] = 0x000f; */
+- MI_buf[REG_HW_MI_2C] = 0x001f; /* lita suggest */
+- MI_buf[REG_HW_MI_2D] = 0x0008;
+- MI_buf[REG_HW_MI_2E] = 0x0008;
+- MI_buf[REG_HW_MI_35] = 0x0051;
+- MI_buf[REG_HW_MI_5F] = 0x0904; /* fail to write */
+- MI_buf[REG_HW_MI_60] = 0x0000;
+- MI_buf[REG_HW_MI_61] = 0x0000;
+- MI_buf[REG_HW_MI_62] = 0x0498;
+- MI_buf[REG_HW_MI_63] = 0x0000;
+- MI_buf[REG_HW_MI_64] = 0x0000;
+- MI_buf[REG_HW_MI_F1] = 0x0001;
+- /* changing while setting up the different value of dx/dy */
+-
+- if (gspca_dev->width != 1280) {
+- MI_buf[0x01] = 0x010a;
+- MI_buf[0x02] = 0x014c;
+- MI_buf[0x03] = 0x01e5;
+- MI_buf[0x04] = 0x0287;
+- }
+- MI_buf[0x20] = 0x1104;
+-
+- bulk_w(gspca_dev, MI_buf + 1, 1);
+- bulk_w(gspca_dev, MI_buf + 2, 2);
+- bulk_w(gspca_dev, MI_buf + 3, 3);
+- bulk_w(gspca_dev, MI_buf + 4, 4);
+- bulk_w(gspca_dev, MI_buf + 5, 5);
+- bulk_w(gspca_dev, MI_buf + 6, 6);
+- bulk_w(gspca_dev, MI_buf + 7, 7);
+- bulk_w(gspca_dev, MI_buf + 9, 9);
+- bulk_w(gspca_dev, MI_buf + 0x0b, 0x0b);
+- bulk_w(gspca_dev, MI_buf + 0x0c, 0x0c);
+- bulk_w(gspca_dev, MI_buf + 0x0d, 0x0d);
+- bulk_w(gspca_dev, MI_buf + 0x1e, 0x1e);
+- bulk_w(gspca_dev, MI_buf + 0x20, 0x20);
+- bulk_w(gspca_dev, MI_buf + 0x2b, 0x2b);
+- bulk_w(gspca_dev, MI_buf + 0x2c, 0x2c);
+- bulk_w(gspca_dev, MI_buf + 0x2d, 0x2d);
+- bulk_w(gspca_dev, MI_buf + 0x2e, 0x2e);
+- bulk_w(gspca_dev, MI_buf + 0x35, 0x35);
+- bulk_w(gspca_dev, MI_buf + 0x5f, 0x5f);
+- bulk_w(gspca_dev, MI_buf + 0x60, 0x60);
+- bulk_w(gspca_dev, MI_buf + 0x61, 0x61);
+- bulk_w(gspca_dev, MI_buf + 0x62, 0x62);
+- bulk_w(gspca_dev, MI_buf + 0x63, 0x63);
+- bulk_w(gspca_dev, MI_buf + 0x64, 0x64);
+- bulk_w(gspca_dev, MI_buf + 0xf1, 0xf1);
+- kfree(MI_buf);
+-
+- intpipe = usb_sndintpipe(gspca_dev->dev, 0);
+- err_code = usb_clear_halt(gspca_dev->dev, intpipe);
++ data[0] = 0x69;
++ data[1] = 0x2f;
++ data[2] = 0x28;
++ data[3] = 0x42;
++ err_code = reg_w(gspca_dev, 4);
++ if (err_code < 0)
++ return err_code;
++
++ data[0] = 0x63;
++ data[1] = 0x07;
++ err_code = reg_w(gspca_dev, 2);
++/*jfm: win trace - many writes here to reg 0x64*/
++ if (err_code < 0)
++ return err_code;
++
++ /* initialize the MI sensor */
++ for (i = 0; i < sizeof mi_data; i++)
++ mi_w(gspca_dev, i + 1, mi_data[i]);
+
+ data[0] = 0x00;
+ data[1] = 0x4d; /* ISOC transfering enable... */
+- reg_w(gspca_dev, data[0], 2);
+- return err_code;
++ reg_w(gspca_dev, 2);
++ return 0;
+ }
+
+ static void sd_stopN(struct gspca_dev *gspca_dev)
+@@ -336,11 +310,18 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
+
+ gspca_dev->usb_buf[0] = 1;
+ gspca_dev->usb_buf[1] = 0;
+- result = reg_w(gspca_dev, gspca_dev->usb_buf[0], 2);
++ result = reg_w(gspca_dev, 2);
+ if (result < 0)
+ PDEBUG(D_ERR, "Camera Stop failed");
+ }
+
++static void sd_stop0(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ kfree(sd->jpeg_hdr);
++}
++
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ struct gspca_frame *frame, /* target */
+ __u8 *data, /* isoc packet */
+@@ -363,16 +344,16 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ || data[5 + p] == 0x65
+ || data[5 + p] == 0x66
+ || data[5 + p] == 0x67) {
+- PDEBUG(D_PACK, "sof offset: %d leng: %d",
++ PDEBUG(D_PACK, "sof offset: %d len: %d",
+ p, len);
+ frame = gspca_frame_add(gspca_dev, LAST_PACKET,
+- frame, data, 0);
++ frame, data, p);
+
+ /* put the JPEG header */
+- jpeg_put_header(gspca_dev, frame,
+- sd->qindex, 0x21);
+- data += 16;
+- len -= 16;
++ gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ sd->jpeg_hdr, JPEG_HDR_SZ);
++ data += p + 16;
++ len -= p + 16;
+ break;
+ }
+ }
+@@ -380,6 +361,121 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ }
+
++static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->brightness = val;
++ if (gspca_dev->streaming) {
++ gspca_dev->usb_buf[0] = 0x61;
++ gspca_dev->usb_buf[1] = val;
++ reg_w(gspca_dev, 2);
++ }
++ return 0;
++}
++
++static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->brightness;
++ return 0;
++}
++
++static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->colors = val;
++ if (gspca_dev->streaming) {
++
++ /* see sd_start */
++ gspca_dev->usb_buf[0] = 0x5f;
++ gspca_dev->usb_buf[1] = sd->colors << 3;
++ gspca_dev->usb_buf[2] = ((sd->colors >> 2) & 0xf8) | 0x04;
++ reg_w(gspca_dev, 3);
++ }
++ return 0;
++}
++
++static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->colors;
++ return 0;
++}
++
++static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->gamma = val;
++ if (gspca_dev->streaming) {
++ gspca_dev->usb_buf[0] = 0x06;
++ gspca_dev->usb_buf[1] = val * 0x40;
++ reg_w(gspca_dev, 2);
++ }
++ return 0;
++}
++
++static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->gamma;
++ return 0;
++}
++
++static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->sharpness = val;
++ if (gspca_dev->streaming) {
++ gspca_dev->usb_buf[0] = 0x67;
++ gspca_dev->usb_buf[1] = val * 4 + 3;
++ reg_w(gspca_dev, 2);
++ }
++ return 0;
++}
++
++static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->sharpness;
++ return 0;
++}
++
++static int sd_set_jcomp(struct gspca_dev *gspca_dev,
++ struct v4l2_jpegcompression *jcomp)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (jcomp->quality < QUALITY_MIN)
++ sd->quality = QUALITY_MIN;
++ else if (jcomp->quality > QUALITY_MAX)
++ sd->quality = QUALITY_MAX;
++ else
++ sd->quality = jcomp->quality;
++ if (gspca_dev->streaming)
++ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
++ return 0;
++}
++
++static int sd_get_jcomp(struct gspca_dev *gspca_dev,
++ struct v4l2_jpegcompression *jcomp)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ memset(jcomp, 0, sizeof *jcomp);
++ jcomp->quality = sd->quality;
++ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
++ | V4L2_JPEG_MARKER_DQT;
++ return 0;
++}
++
+ /* sub-driver description */
+ static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+@@ -389,7 +485,10 @@ static const struct sd_desc sd_desc = {
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
++ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
++ .get_jcomp = sd_get_jcomp,
++ .set_jcomp = sd_set_jcomp,
+ };
+
+ /* -- module initialisation -- */
+@@ -421,8 +520,11 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c
+new file mode 100644
+index 0000000..2a901a4
+--- /dev/null
++++ b/drivers/media/video/gspca/mr97310a.c
+@@ -0,0 +1,362 @@
++/*
++ * Mars MR97310A library
++ *
++ * Copyright (C) 2009 Kyle Guinn <elyk03@gmail.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * 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
++ */
++
++#define MODULE_NAME "mr97310a"
++
++#include "gspca.h"
++
++MODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>");
++MODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver");
++MODULE_LICENSE("GPL");
++
++/* specific webcam descriptor */
++struct sd {
++ struct gspca_dev gspca_dev; /* !! must be the first item */
++ u8 sof_read;
++};
++
++/* V4L2 controls supported by the driver */
++static struct ctrl sd_ctrls[] = {
++};
++
++static const struct v4l2_pix_format vga_mode[] = {
++ {160, 120, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
++ .bytesperline = 160,
++ .sizeimage = 160 * 120,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 4},
++ {176, 144, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
++ .bytesperline = 176,
++ .sizeimage = 176 * 144,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 3},
++ {320, 240, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
++ .bytesperline = 320,
++ .sizeimage = 320 * 240,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 2},
++ {352, 288, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
++ .bytesperline = 352,
++ .sizeimage = 352 * 288,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 1},
++ {640, 480, V4L2_PIX_FMT_MR97310A, V4L2_FIELD_NONE,
++ .bytesperline = 640,
++ .sizeimage = 640 * 480,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 0},
++};
++
++/* the bytes to write are in gspca_dev->usb_buf */
++static int reg_w(struct gspca_dev *gspca_dev, int len)
++{
++ int rc;
++
++ rc = usb_bulk_msg(gspca_dev->dev,
++ usb_sndbulkpipe(gspca_dev->dev, 4),
++ gspca_dev->usb_buf, len, NULL, 500);
++ if (rc < 0)
++ PDEBUG(D_ERR, "reg write [%02x] error %d",
++ gspca_dev->usb_buf[0], rc);
++ return rc;
++}
++
++/* this function is called at probe time */
++static int sd_config(struct gspca_dev *gspca_dev,
++ const struct usb_device_id *id)
++{
++ struct cam *cam;
++
++ cam = &gspca_dev->cam;
++ cam->cam_mode = vga_mode;
++ cam->nmodes = ARRAY_SIZE(vga_mode);
++ return 0;
++}
++
++/* this function is called at probe and resume time */
++static int sd_init(struct gspca_dev *gspca_dev)
++{
++ return 0;
++}
++
++static int sd_start(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ __u8 *data = gspca_dev->usb_buf;
++ int err_code;
++
++ sd->sof_read = 0;
++
++ /* Note: register descriptions guessed from MR97113A driver */
++
++ data[0] = 0x01;
++ data[1] = 0x01;
++ err_code = reg_w(gspca_dev, 2);
++ if (err_code < 0)
++ return err_code;
++
++ data[0] = 0x00;
++ data[1] = 0x0d;
++ data[2] = 0x01;
++ data[5] = 0x2b;
++ data[7] = 0x00;
++ data[9] = 0x50; /* reg 8, no scale down */
++ data[10] = 0xc0;
++
++ switch (gspca_dev->width) {
++ case 160:
++ data[9] |= 0x0c; /* reg 8, 4:1 scale down */
++ /* fall thru */
++ case 320:
++ data[9] |= 0x04; /* reg 8, 2:1 scale down */
++ /* fall thru */
++ case 640:
++ default:
++ data[3] = 0x50; /* reg 2, H size */
++ data[4] = 0x78; /* reg 3, V size */
++ data[6] = 0x04; /* reg 5, H start */
++ data[8] = 0x03; /* reg 7, V start */
++ break;
++
++ case 176:
++ data[9] |= 0x04; /* reg 8, 2:1 scale down */
++ /* fall thru */
++ case 352:
++ data[3] = 0x2c; /* reg 2, H size */
++ data[4] = 0x48; /* reg 3, V size */
++ data[6] = 0x94; /* reg 5, H start */
++ data[8] = 0x63; /* reg 7, V start */
++ break;
++ }
++
++ err_code = reg_w(gspca_dev, 11);
++ if (err_code < 0)
++ return err_code;
++
++ data[0] = 0x0a;
++ data[1] = 0x80;
++ err_code = reg_w(gspca_dev, 2);
++ if (err_code < 0)
++ return err_code;
++
++ data[0] = 0x14;
++ data[1] = 0x0a;
++ err_code = reg_w(gspca_dev, 2);
++ if (err_code < 0)
++ return err_code;
++
++ data[0] = 0x1b;
++ data[1] = 0x00;
++ err_code = reg_w(gspca_dev, 2);
++ if (err_code < 0)
++ return err_code;
++
++ data[0] = 0x15;
++ data[1] = 0x16;
++ err_code = reg_w(gspca_dev, 2);
++ if (err_code < 0)
++ return err_code;
++
++ data[0] = 0x16;
++ data[1] = 0x10;
++ err_code = reg_w(gspca_dev, 2);
++ if (err_code < 0)
++ return err_code;
++
++ data[0] = 0x17;
++ data[1] = 0x3a;
++ err_code = reg_w(gspca_dev, 2);
++ if (err_code < 0)
++ return err_code;
++
++ data[0] = 0x18;
++ data[1] = 0x68;
++ err_code = reg_w(gspca_dev, 2);
++ if (err_code < 0)
++ return err_code;
++
++ data[0] = 0x1f;
++ data[1] = 0x00;
++ data[2] = 0x02;
++ data[3] = 0x06;
++ data[4] = 0x59;
++ data[5] = 0x0c;
++ data[6] = 0x16;
++ data[7] = 0x00;
++ data[8] = 0x07;
++ data[9] = 0x00;
++ data[10] = 0x01;
++ err_code = reg_w(gspca_dev, 11);
++ if (err_code < 0)
++ return err_code;
++
++ data[0] = 0x1f;
++ data[1] = 0x04;
++ data[2] = 0x11;
++ data[3] = 0x01;
++ err_code = reg_w(gspca_dev, 4);
++ if (err_code < 0)
++ return err_code;
++
++ data[0] = 0x1f;
++ data[1] = 0x00;
++ data[2] = 0x0a;
++ data[3] = 0x00;
++ data[4] = 0x01;
++ data[5] = 0x00;
++ data[6] = 0x00;
++ data[7] = 0x01;
++ data[8] = 0x00;
++ data[9] = 0x0a;
++ err_code = reg_w(gspca_dev, 10);
++ if (err_code < 0)
++ return err_code;
++
++ data[0] = 0x1f;
++ data[1] = 0x04;
++ data[2] = 0x11;
++ data[3] = 0x01;
++ err_code = reg_w(gspca_dev, 4);
++ if (err_code < 0)
++ return err_code;
++
++ data[0] = 0x1f;
++ data[1] = 0x00;
++ data[2] = 0x12;
++ data[3] = 0x00;
++ data[4] = 0x63;
++ data[5] = 0x00;
++ data[6] = 0x70;
++ data[7] = 0x00;
++ data[8] = 0x00;
++ err_code = reg_w(gspca_dev, 9);
++ if (err_code < 0)
++ return err_code;
++
++ data[0] = 0x1f;
++ data[1] = 0x04;
++ data[2] = 0x11;
++ data[3] = 0x01;
++ err_code = reg_w(gspca_dev, 4);
++ if (err_code < 0)
++ return err_code;
++
++ data[0] = 0x00;
++ data[1] = 0x4d; /* ISOC transfering enable... */
++ err_code = reg_w(gspca_dev, 2);
++ return err_code;
++}
++
++static void sd_stopN(struct gspca_dev *gspca_dev)
++{
++ int result;
++
++ gspca_dev->usb_buf[0] = 1;
++ gspca_dev->usb_buf[1] = 0;
++ result = reg_w(gspca_dev, 2);
++ if (result < 0)
++ PDEBUG(D_ERR, "Camera Stop failed");
++}
++
++/* Include pac common sof detection functions */
++#include "pac_common.h"
++
++static void sd_pkt_scan(struct gspca_dev *gspca_dev,
++ struct gspca_frame *frame, /* target */
++ __u8 *data, /* isoc packet */
++ int len) /* iso packet length */
++{
++ unsigned char *sof;
++
++ sof = pac_find_sof(gspca_dev, data, len);
++ if (sof) {
++ int n;
++
++ /* finish decoding current frame */
++ n = sof - data;
++ if (n > sizeof pac_sof_marker)
++ n -= sizeof pac_sof_marker;
++ else
++ n = 0;
++ frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
++ data, n);
++ /* Start next frame. */
++ gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ pac_sof_marker, sizeof pac_sof_marker);
++ len -= sof - data;
++ data = sof;
++ }
++ gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
++}
++
++/* sub-driver description */
++static const struct sd_desc sd_desc = {
++ .name = MODULE_NAME,
++ .ctrls = sd_ctrls,
++ .nctrls = ARRAY_SIZE(sd_ctrls),
++ .config = sd_config,
++ .init = sd_init,
++ .start = sd_start,
++ .stopN = sd_stopN,
++ .pkt_scan = sd_pkt_scan,
++};
++
++/* -- module initialisation -- */
++static const __devinitdata struct usb_device_id device_table[] = {
++ {USB_DEVICE(0x08ca, 0x0111)},
++ {}
++};
++MODULE_DEVICE_TABLE(usb, device_table);
++
++/* -- device connect -- */
++static int sd_probe(struct usb_interface *intf,
++ const struct usb_device_id *id)
++{
++ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
++ THIS_MODULE);
++}
++
++static struct usb_driver sd_driver = {
++ .name = MODULE_NAME,
++ .id_table = device_table,
++ .probe = sd_probe,
++ .disconnect = gspca_disconnect,
++#ifdef CONFIG_PM
++ .suspend = gspca_suspend,
++ .resume = gspca_resume,
++#endif
++};
++
++/* -- module insert / remove -- */
++static int __init sd_mod_init(void)
++{
++ if (usb_register(&sd_driver) < 0)
++ return -1;
++ PDEBUG(D_PROBE, "registered");
++ return 0;
++}
++static void __exit sd_mod_exit(void)
++{
++ usb_deregister(&sd_driver);
++ PDEBUG(D_PROBE, "deregistered");
++}
++
++module_init(sd_mod_init);
++module_exit(sd_mod_exit);
+diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c
+index ee23295..1fff37b 100644
+--- a/drivers/media/video/gspca/ov519.c
++++ b/drivers/media/video/gspca/ov519.c
+@@ -1360,7 +1360,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ }
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = OV511_ENDPOINT_ADDRESS;
+ if (!sd->sif) {
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+@@ -2177,8 +2176,10 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c
+index 3bf15e4..19e0bc6 100644
+--- a/drivers/media/video/gspca/ov534.c
++++ b/drivers/media/video/gspca/ov534.c
+@@ -1,7 +1,8 @@
+ /*
+- * ov534/ov772x gspca driver
++ * ov534 gspca driver
+ * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it>
+ * Copyright (C) 2008 Jim Paris <jim@jtan.com>
++ * Copyright (C) 2009 Jean-Francois Moine http://moinejf.free.fr
+ *
+ * Based on a prototype written by Mark Ferrell <majortrips@gmail.com>
+ * USB protocol reverse engineered by Jim Paris <jim@jtan.com>
+@@ -26,7 +27,7 @@
+
+ #include "gspca.h"
+
+-#define OV534_REG_ADDRESS 0xf1 /* ? */
++#define OV534_REG_ADDRESS 0xf1 /* sensor address */
+ #define OV534_REG_SUBADDR 0xf2
+ #define OV534_REG_WRITE 0xf3
+ #define OV534_REG_READ 0xf4
+@@ -46,9 +47,13 @@ MODULE_LICENSE("GPL");
+ /* specific webcam descriptor */
+ struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+- __u32 last_fid;
+ __u32 last_pts;
+- int frame_rate;
++ u16 last_fid;
++ u8 frame_rate;
++
++ u8 sensor;
++#define SENSOR_OV772X 0
++#define SENSOR_OV965X 1
+ };
+
+ /* V4L2 controls supported by the driver */
+@@ -63,114 +68,7 @@ static const struct v4l2_pix_format vga_mode[] = {
+ .priv = 0},
+ };
+
+-static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val)
+-{
+- struct usb_device *udev = gspca_dev->dev;
+- int ret;
+-
+- PDEBUG(D_USBO, "reg=0x%04x, val=0%02x", reg, val);
+- gspca_dev->usb_buf[0] = val;
+- ret = usb_control_msg(udev,
+- usb_sndctrlpipe(udev, 0),
+- 0x1,
+- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+- 0x0, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
+- if (ret < 0)
+- PDEBUG(D_ERR, "write failed");
+-}
+-
+-static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg)
+-{
+- struct usb_device *udev = gspca_dev->dev;
+- int ret;
+-
+- ret = usb_control_msg(udev,
+- usb_rcvctrlpipe(udev, 0),
+- 0x1,
+- USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+- 0x0, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
+- PDEBUG(D_USBI, "reg=0x%04x, data=0x%02x", reg, gspca_dev->usb_buf[0]);
+- if (ret < 0)
+- PDEBUG(D_ERR, "read failed");
+- return gspca_dev->usb_buf[0];
+-}
+-
+-/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7.
+- * (direction and output)? */
+-static void ov534_set_led(struct gspca_dev *gspca_dev, int status)
+-{
+- u8 data;
+-
+- PDEBUG(D_CONF, "led status: %d", status);
+-
+- data = ov534_reg_read(gspca_dev, 0x21);
+- data |= 0x80;
+- ov534_reg_write(gspca_dev, 0x21, data);
+-
+- data = ov534_reg_read(gspca_dev, 0x23);
+- if (status)
+- data |= 0x80;
+- else
+- data &= ~(0x80);
+-
+- ov534_reg_write(gspca_dev, 0x23, data);
+-}
+-
+-static int sccb_check_status(struct gspca_dev *gspca_dev)
+-{
+- u8 data;
+- int i;
+-
+- for (i = 0; i < 5; i++) {
+- data = ov534_reg_read(gspca_dev, OV534_REG_STATUS);
+-
+- switch (data) {
+- case 0x00:
+- return 1;
+- case 0x04:
+- return 0;
+- case 0x03:
+- break;
+- default:
+- PDEBUG(D_ERR, "sccb status 0x%02x, attempt %d/5",
+- data, i + 1);
+- }
+- }
+- return 0;
+-}
+-
+-static void sccb_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val)
+-{
+- PDEBUG(D_USBO, "reg: 0x%04x, val: 0x%02x", reg, val);
+- ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg);
+- ov534_reg_write(gspca_dev, OV534_REG_WRITE, val);
+- ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
+-
+- if (!sccb_check_status(gspca_dev))
+- PDEBUG(D_ERR, "sccb_reg_write failed");
+-}
+-
+-#ifdef GSPCA_DEBUG
+-static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg)
+-{
+- ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg);
+- ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
+- if (!sccb_check_status(gspca_dev))
+- PDEBUG(D_ERR, "sccb_reg_read failed 1");
+-
+- ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2);
+- if (!sccb_check_status(gspca_dev))
+- PDEBUG(D_ERR, "sccb_reg_read failed 2");
+-
+- return ov534_reg_read(gspca_dev, OV534_REG_READ);
+-}
+-#endif
+-
+-static const __u8 ov534_reg_initdata[][2] = {
+- { 0xe7, 0x3a },
+-
+- { OV534_REG_ADDRESS, 0x42 }, /* select OV772x sensor */
+-
++static const u8 bridge_init_ov722x[][2] = {
+ { 0xc2, 0x0c },
+ { 0x88, 0xf8 },
+ { 0xc3, 0x69 },
+@@ -228,7 +126,7 @@ static const __u8 ov534_reg_initdata[][2] = {
+ { 0xc2, 0x0c },
+ };
+
+-static const __u8 ov772x_reg_initdata[][2] = {
++static const u8 sensor_init_ov722x[][2] = {
+ { 0x12, 0x80 },
+ { 0x11, 0x01 },
+
+@@ -311,6 +209,456 @@ static const __u8 ov772x_reg_initdata[][2] = {
+ { 0x0c, 0xd0 }
+ };
+
++static const u8 bridge_init_ov965x[][2] = {
++ {0x88, 0xf8},
++ {0x89, 0xff},
++ {0x76, 0x03},
++ {0x92, 0x03},
++ {0x95, 0x10},
++ {0xe2, 0x00},
++ {0xe7, 0x3e},
++ {0x8d, 0x1c},
++ {0x8e, 0x00},
++ {0x8f, 0x00},
++ {0x1f, 0x00},
++ {0xc3, 0xf9},
++ {0x89, 0xff},
++ {0x88, 0xf8},
++ {0x76, 0x03},
++ {0x92, 0x01},
++ {0x93, 0x18},
++ {0x1c, 0x0a},
++ {0x1d, 0x48},
++ {0xc0, 0x50},
++ {0xc1, 0x3c},
++ {0x34, 0x05},
++ {0xc2, 0x0c},
++ {0xc3, 0xf9},
++ {0x34, 0x05},
++ {0xe7, 0x2e},
++ {0x31, 0xf9},
++ {0x35, 0x02},
++ {0xd9, 0x10},
++ {0x25, 0x42},
++ {0x94, 0x11},
++};
++
++static const u8 sensor_init_ov965x[][2] = {
++ {0x12, 0x80}, /* com7 - reset */
++ {0x00, 0x00}, /* gain */
++ {0x01, 0x80}, /* blue */
++ {0x02, 0x80}, /* red */
++ {0x03, 0x1b}, /* vref */
++ {0x04, 0x03}, /* com1 - exposure low bits */
++ {0x0b, 0x57}, /* ver */
++ {0x0e, 0x61}, /* com5 */
++ {0x0f, 0x42}, /* com6 */
++ {0x11, 0x00}, /* clkrc */
++ {0x12, 0x02}, /* com7 */
++ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
++ {0x14, 0x28}, /* com9 */
++ {0x16, 0x24}, /* rsvd16 */
++ {0x17, 0x1d}, /* hstart*/
++ {0x18, 0xbd}, /* hstop */
++ {0x19, 0x01}, /* vstrt */
++ {0x1a, 0x81}, /* vstop*/
++ {0x1e, 0x04}, /* mvfp */
++ {0x24, 0x3c}, /* aew */
++ {0x25, 0x36}, /* aeb */
++ {0x26, 0x71}, /* vpt */
++ {0x27, 0x08}, /* bbias */
++ {0x28, 0x08}, /* gbbias */
++ {0x29, 0x15}, /* gr com */
++ {0x2a, 0x00},
++ {0x2b, 0x00},
++ {0x2c, 0x08}, /* rbias */
++ {0x32, 0xff}, /* href */
++ {0x33, 0x00}, /* chlf */
++ {0x34, 0x3f}, /* arblm */
++ {0x35, 0x00}, /* rsvd35 */
++ {0x36, 0xf8}, /* rsvd36 */
++ {0x38, 0x72}, /* acom38 */
++ {0x39, 0x57}, /* ofon */
++ {0x3a, 0x80}, /* tslb */
++ {0x3b, 0xc4},
++ {0x3d, 0x99}, /* com13 */
++ {0x3f, 0xc1},
++ {0x40, 0xc0}, /* com15 */
++ {0x41, 0x40}, /* com16 */
++ {0x42, 0xc0},
++ {0x43, 0x0a},
++ {0x44, 0xf0},
++ {0x45, 0x46},
++ {0x46, 0x62},
++ {0x47, 0x2a},
++ {0x48, 0x3c},
++ {0x4a, 0xfc},
++ {0x4b, 0xfc},
++ {0x4c, 0x7f},
++ {0x4d, 0x7f},
++ {0x4e, 0x7f},
++ {0x4f, 0x98},
++ {0x50, 0x98},
++ {0x51, 0x00},
++ {0x52, 0x28},
++ {0x53, 0x70},
++ {0x54, 0x98},
++ {0x58, 0x1a},
++ {0x59, 0x85},
++ {0x5a, 0xa9},
++ {0x5b, 0x64},
++ {0x5c, 0x84},
++ {0x5d, 0x53},
++ {0x5e, 0x0e},
++ {0x5f, 0xf0},
++ {0x60, 0xf0},
++ {0x61, 0xf0},
++ {0x62, 0x00}, /* lcc1 */
++ {0x63, 0x00}, /* lcc2 */
++ {0x64, 0x02}, /* lcc3 */
++ {0x65, 0x16}, /* lcc4 */
++ {0x66, 0x01}, /* lcc5 */
++ {0x69, 0x02}, /* hv */
++ {0x6b, 0x5a}, /* dbvl */
++ {0x6c, 0x04},
++ {0x6d, 0x55},
++ {0x6e, 0x00},
++ {0x6f, 0x9d},
++ {0x70, 0x21},
++ {0x71, 0x78},
++ {0x72, 0x00},
++ {0x73, 0x01},
++ {0x74, 0x3a},
++ {0x75, 0x35},
++ {0x76, 0x01},
++ {0x77, 0x02},
++ {0x7a, 0x12},
++ {0x7b, 0x08},
++ {0x7c, 0x16},
++ {0x7d, 0x30},
++ {0x7e, 0x5e},
++ {0x7f, 0x72},
++ {0x80, 0x82},
++ {0x81, 0x8e},
++ {0x82, 0x9a},
++ {0x83, 0xa4},
++ {0x84, 0xac},
++ {0x85, 0xb8},
++ {0x86, 0xc3},
++ {0x87, 0xd6},
++ {0x88, 0xe6},
++ {0x89, 0xf2},
++ {0x8a, 0x03},
++ {0x8c, 0x89},
++ {0x14, 0x28}, /* com9 */
++ {0x90, 0x7d},
++ {0x91, 0x7b},
++ {0x9d, 0x03},
++ {0x9e, 0x04},
++ {0x9f, 0x7a},
++ {0xa0, 0x79},
++ {0xa1, 0x40}, /* aechm */
++ {0xa4, 0x50},
++ {0xa5, 0x68}, /* com26 */
++ {0xa6, 0x4a},
++ {0xa8, 0xc1}, /* acoma8 */
++ {0xa9, 0xef}, /* acoma9 */
++ {0xaa, 0x92},
++ {0xab, 0x04},
++ {0xac, 0x80},
++ {0xad, 0x80},
++ {0xae, 0x80},
++ {0xaf, 0x80},
++ {0xb2, 0xf2},
++ {0xb3, 0x20},
++ {0xb4, 0x20},
++ {0xb5, 0x00},
++ {0xb6, 0xaf},
++ {0xbb, 0xae},
++ {0xbc, 0x7f},
++ {0xdb, 0x7f},
++ {0xbe, 0x7f},
++ {0xbf, 0x7f},
++ {0xc0, 0xe2},
++ {0xc1, 0xc0},
++ {0xc2, 0x01},
++ {0xc3, 0x4e},
++ {0xc6, 0x85},
++ {0xc7, 0x80},
++ {0xc9, 0xe0},
++ {0xca, 0xe8},
++ {0xcb, 0xf0},
++ {0xcc, 0xd8},
++ {0xcd, 0xf1},
++ {0x4f, 0x98},
++ {0x50, 0x98},
++ {0x51, 0x00},
++ {0x52, 0x28},
++ {0x53, 0x70},
++ {0x54, 0x98},
++ {0x58, 0x1a},
++ {0xff, 0x41}, /* read 41, write ff 00 */
++ {0x41, 0x40}, /* com16 */
++ {0xc5, 0x03},
++ {0x6a, 0x02},
++
++ {0x12, 0x62}, /* com7 - VGA + CIF */
++ {0x36, 0xfa}, /* rsvd36 */
++ {0x69, 0x0a}, /* hv */
++ {0x8c, 0x89}, /* com22 */
++ {0x14, 0x28}, /* com9 */
++ {0x3e, 0x0c},
++ {0x41, 0x40}, /* com16 */
++ {0x72, 0x00},
++ {0x73, 0x00},
++ {0x74, 0x3a},
++ {0x75, 0x35},
++ {0x76, 0x01},
++ {0xc7, 0x80},
++ {0x03, 0x12}, /* vref */
++ {0x17, 0x16}, /* hstart */
++ {0x18, 0x02}, /* hstop */
++ {0x19, 0x01}, /* vstrt */
++ {0x1a, 0x3d}, /* vstop */
++ {0x32, 0xff}, /* href */
++ {0xc0, 0xaa},
++};
++
++static const u8 bridge_init_ov965x_2[][2] = {
++ {0x94, 0xaa},
++ {0xf1, 0x60},
++ {0xe5, 0x04},
++ {0xc0, 0x50},
++ {0xc1, 0x3c},
++ {0x8c, 0x00},
++ {0x8d, 0x1c},
++ {0x34, 0x05},
++
++ {0xc2, 0x0c},
++ {0xc3, 0xf9},
++ {0xda, 0x01},
++ {0x50, 0x00},
++ {0x51, 0xa0},
++ {0x52, 0x3c},
++ {0x53, 0x00},
++ {0x54, 0x00},
++ {0x55, 0x00},
++ {0x57, 0x00},
++ {0x5c, 0x00},
++ {0x5a, 0xa0},
++ {0x5b, 0x78},
++ {0x35, 0x02},
++ {0xd9, 0x10},
++ {0x94, 0x11},
++};
++
++static const u8 sensor_init_ov965x_2[][2] = {
++ {0x3b, 0xc4},
++ {0x1e, 0x04}, /* mvfp */
++ {0x13, 0xe0}, /* com8 */
++ {0x00, 0x00}, /* gain */
++ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
++ {0x11, 0x03}, /* clkrc */
++ {0x6b, 0x5a}, /* dblv */
++ {0x6a, 0x05},
++ {0xc5, 0x07},
++ {0xa2, 0x4b},
++ {0xa3, 0x3e},
++ {0x2d, 0x00},
++ {0xff, 0x42}, /* read 42, write ff 00 */
++ {0x42, 0xc0},
++ {0x2d, 0x00},
++ {0xff, 0x42}, /* read 42, write ff 00 */
++ {0x42, 0xc1},
++ {0x3f, 0x01},
++ {0xff, 0x42}, /* read 42, write ff 00 */
++ {0x42, 0xc1},
++ {0x4f, 0x98},
++ {0x50, 0x98},
++ {0x51, 0x00},
++ {0x52, 0x28},
++ {0x53, 0x70},
++ {0x54, 0x98},
++ {0x58, 0x1a},
++ {0xff, 0x41}, /* read 41, write ff 00 */
++ {0x41, 0x40}, /* com16 */
++ {0x56, 0x40},
++ {0x55, 0x8f},
++ {0x10, 0x25}, /* aech - exposure high bits */
++ {0xff, 0x13}, /* read 13, write ff 00 */
++ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
++};
++
++static const u8 bridge_start_ov965x[][2] = {
++ {0xc2, 0x4c},
++ {0xc3, 0xf9},
++ {0x50, 0x00},
++ {0x51, 0xa0},
++ {0x52, 0x78},
++ {0x53, 0x00},
++ {0x54, 0x00},
++ {0x55, 0x00},
++ {0x57, 0x00},
++ {0x5c, 0x00},
++ {0x5a, 0x28},
++ {0x5b, 0x1e},
++ {0x35, 0x00},
++ {0xd9, 0x21},
++ {0x94, 0x11},
++};
++
++static const u8 sensor_start_ov965x[][2] = {
++ {0x3b, 0xe4},
++ {0x1e, 0x04}, /* mvfp */
++ {0x13, 0xe0}, /* com8 */
++ {0x00, 0x00},
++ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
++ {0x11, 0x01}, /* clkrc */
++ {0x6b, 0x5a}, /* dblv */
++ {0x6a, 0x02},
++ {0xc5, 0x03},
++ {0xa2, 0x96},
++ {0xa3, 0x7d},
++ {0xff, 0x13}, /* read 13, write ff 00 */
++ {0x13, 0xe7},
++ {0x3a, 0x80},
++ {0xff, 0x42}, /* read 42, write ff 00 */
++ {0x42, 0xc1},
++};
++
++
++static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val)
++{
++ struct usb_device *udev = gspca_dev->dev;
++ int ret;
++
++ PDEBUG(D_USBO, "reg=0x%04x, val=0%02x", reg, val);
++ gspca_dev->usb_buf[0] = val;
++ ret = usb_control_msg(udev,
++ usb_sndctrlpipe(udev, 0),
++ 0x01,
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
++ if (ret < 0)
++ PDEBUG(D_ERR, "write failed");
++}
++
++static u8 ov534_reg_read(struct gspca_dev *gspca_dev, u16 reg)
++{
++ struct usb_device *udev = gspca_dev->dev;
++ int ret;
++
++ ret = usb_control_msg(udev,
++ usb_rcvctrlpipe(udev, 0),
++ 0x01,
++ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
++ PDEBUG(D_USBI, "reg=0x%04x, data=0x%02x", reg, gspca_dev->usb_buf[0]);
++ if (ret < 0)
++ PDEBUG(D_ERR, "read failed");
++ return gspca_dev->usb_buf[0];
++}
++
++/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7.
++ * (direction and output)? */
++static void ov534_set_led(struct gspca_dev *gspca_dev, int status)
++{
++ u8 data;
++
++ PDEBUG(D_CONF, "led status: %d", status);
++
++ data = ov534_reg_read(gspca_dev, 0x21);
++ data |= 0x80;
++ ov534_reg_write(gspca_dev, 0x21, data);
++
++ data = ov534_reg_read(gspca_dev, 0x23);
++ if (status)
++ data |= 0x80;
++ else
++ data &= ~0x80;
++
++ ov534_reg_write(gspca_dev, 0x23, data);
++
++ if (!status) {
++ data = ov534_reg_read(gspca_dev, 0x21);
++ data &= ~0x80;
++ ov534_reg_write(gspca_dev, 0x21, data);
++ }
++}
++
++static int sccb_check_status(struct gspca_dev *gspca_dev)
++{
++ u8 data;
++ int i;
++
++ for (i = 0; i < 5; i++) {
++ data = ov534_reg_read(gspca_dev, OV534_REG_STATUS);
++
++ switch (data) {
++ case 0x00:
++ return 1;
++ case 0x04:
++ return 0;
++ case 0x03:
++ break;
++ default:
++ PDEBUG(D_ERR, "sccb status 0x%02x, attempt %d/5",
++ data, i + 1);
++ }
++ }
++ return 0;
++}
++
++static void sccb_reg_write(struct gspca_dev *gspca_dev, u8 reg, u8 val)
++{
++ PDEBUG(D_USBO, "reg: 0x%02x, val: 0x%02x", reg, val);
++ ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg);
++ ov534_reg_write(gspca_dev, OV534_REG_WRITE, val);
++ ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
++
++ if (!sccb_check_status(gspca_dev))
++ PDEBUG(D_ERR, "sccb_reg_write failed");
++}
++
++static u8 sccb_reg_read(struct gspca_dev *gspca_dev, u16 reg)
++{
++ ov534_reg_write(gspca_dev, OV534_REG_SUBADDR, reg);
++ ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
++ if (!sccb_check_status(gspca_dev))
++ PDEBUG(D_ERR, "sccb_reg_read failed 1");
++
++ ov534_reg_write(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2);
++ if (!sccb_check_status(gspca_dev))
++ PDEBUG(D_ERR, "sccb_reg_read failed 2");
++
++ return ov534_reg_read(gspca_dev, OV534_REG_READ);
++}
++
++/* output a bridge sequence (reg - val) */
++static void reg_w_array(struct gspca_dev *gspca_dev,
++ const u8 (*data)[2], int len)
++{
++ while (--len >= 0) {
++ ov534_reg_write(gspca_dev, (*data)[0], (*data)[1]);
++ data++;
++ }
++}
++
++/* output a sensor sequence (reg - val) */
++static void sccb_w_array(struct gspca_dev *gspca_dev,
++ const u8 (*data)[2], int len)
++{
++ while (--len >= 0) {
++ if ((*data)[0] != 0xff) {
++ sccb_reg_write(gspca_dev, (*data)[0], (*data)[1]);
++ } else {
++ sccb_reg_read(gspca_dev, (*data)[1]);
++ sccb_reg_write(gspca_dev, 0xff, 0x00);
++ }
++ data++;
++ }
++}
++
+ /* set framerate */
+ static void ov534_set_frame_rate(struct gspca_dev *gspca_dev)
+ {
+@@ -346,40 +694,17 @@ static void ov534_set_frame_rate(struct gspca_dev *gspca_dev)
+ PDEBUG(D_PROBE, "frame_rate: %d", fr);
+ }
+
+-/* setup method */
+-static void ov534_setup(struct gspca_dev *gspca_dev)
+-{
+- int i;
+-
+- /* Initialize bridge chip */
+- for (i = 0; i < ARRAY_SIZE(ov534_reg_initdata); i++)
+- ov534_reg_write(gspca_dev, ov534_reg_initdata[i][0],
+- ov534_reg_initdata[i][1]);
+-
+- PDEBUG(D_PROBE, "sensor is ov%02x%02x",
+- sccb_reg_read(gspca_dev, 0x0a),
+- sccb_reg_read(gspca_dev, 0x0b));
+-
+- ov534_set_led(gspca_dev, 1);
+-
+- /* Initialize sensor */
+- for (i = 0; i < ARRAY_SIZE(ov772x_reg_initdata); i++)
+- sccb_reg_write(gspca_dev, ov772x_reg_initdata[i][0],
+- ov772x_reg_initdata[i][1]);
+-
+- ov534_reg_write(gspca_dev, 0xe0, 0x09);
+- ov534_set_led(gspca_dev, 0);
+-}
+-
+ /* this function is called at probe time */
+ static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+ {
++ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
++ sd->sensor = id->driver_info;
++
+ cam = &gspca_dev->cam;
+
+- cam->epaddr = 0x01;
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+
+@@ -392,26 +717,102 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ /* this function is called at probe and resume time */
+ static int sd_init(struct gspca_dev *gspca_dev)
+ {
+- ov534_setup(gspca_dev);
+- ov534_set_frame_rate(gspca_dev);
++ struct sd *sd = (struct sd *) gspca_dev;
++ u16 sensor_id;
++ static const u8 sensor_addr[2] = {
++ 0x42, /* 0 SENSOR_OV772X */
++ 0x60, /* 1 SENSOR_OV965X */
++ };
++
++ /* reset bridge */
++ ov534_reg_write(gspca_dev, 0xe7, 0x3a);
++ ov534_reg_write(gspca_dev, 0xe0, 0x08);
++ msleep(100);
++
++ /* initialize the sensor address */
++ ov534_reg_write(gspca_dev, OV534_REG_ADDRESS,
++ sensor_addr[sd->sensor]);
++
++ /* reset sensor */
++ sccb_reg_write(gspca_dev, 0x12, 0x80);
++ msleep(10);
++
++ /* probe the sensor */
++ sccb_reg_read(gspca_dev, 0x0a);
++ sensor_id = sccb_reg_read(gspca_dev, 0x0a) << 8;
++ sccb_reg_read(gspca_dev, 0x0b);
++ sensor_id |= sccb_reg_read(gspca_dev, 0x0b);
++ PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id);
++
++ /* initialize */
++ switch (sd->sensor) {
++ case SENSOR_OV772X:
++ reg_w_array(gspca_dev, bridge_init_ov722x,
++ ARRAY_SIZE(bridge_init_ov722x));
++ ov534_set_led(gspca_dev, 1);
++ sccb_w_array(gspca_dev, sensor_init_ov722x,
++ ARRAY_SIZE(sensor_init_ov722x));
++ ov534_reg_write(gspca_dev, 0xe0, 0x09);
++ ov534_set_led(gspca_dev, 0);
++ ov534_set_frame_rate(gspca_dev);
++ break;
++ default:
++/* case SENSOR_OV965X: */
++ reg_w_array(gspca_dev, bridge_init_ov965x,
++ ARRAY_SIZE(bridge_init_ov965x));
++ sccb_w_array(gspca_dev, sensor_init_ov965x,
++ ARRAY_SIZE(sensor_init_ov965x));
++ reg_w_array(gspca_dev, bridge_init_ov965x_2,
++ ARRAY_SIZE(bridge_init_ov965x_2));
++ sccb_w_array(gspca_dev, sensor_init_ov965x_2,
++ ARRAY_SIZE(sensor_init_ov965x_2));
++ ov534_reg_write(gspca_dev, 0xe0, 0x00);
++ ov534_reg_write(gspca_dev, 0xe0, 0x01);
++ ov534_set_led(gspca_dev, 0);
++ ov534_reg_write(gspca_dev, 0xe0, 0x00);
++ }
+
+ return 0;
+ }
+
+ static int sd_start(struct gspca_dev *gspca_dev)
+ {
+- /* start streaming data */
+- ov534_set_led(gspca_dev, 1);
+- ov534_reg_write(gspca_dev, 0xe0, 0x00);
++ struct sd *sd = (struct sd *) gspca_dev;
+
++ switch (sd->sensor) {
++ case SENSOR_OV772X:
++ ov534_set_led(gspca_dev, 1);
++ ov534_reg_write(gspca_dev, 0xe0, 0x00);
++ break;
++ default:
++/* case SENSOR_OV965X: */
++ reg_w_array(gspca_dev, bridge_start_ov965x,
++ ARRAY_SIZE(bridge_start_ov965x));
++ sccb_w_array(gspca_dev, sensor_start_ov965x,
++ ARRAY_SIZE(sensor_start_ov965x));
++ ov534_reg_write(gspca_dev, 0xe0, 0x00);
++ ov534_set_led(gspca_dev, 1);
++/*fixme: other sensor start omitted*/
++ }
+ return 0;
+ }
+
+ static void sd_stopN(struct gspca_dev *gspca_dev)
+ {
+- /* stop streaming data */
+- ov534_reg_write(gspca_dev, 0xe0, 0x09);
+- ov534_set_led(gspca_dev, 0);
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ switch (sd->sensor) {
++ case SENSOR_OV772X:
++ ov534_reg_write(gspca_dev, 0xe0, 0x09);
++ ov534_set_led(gspca_dev, 0);
++ break;
++ default:
++/* case SENSOR_OV965X: */
++ ov534_reg_write(gspca_dev, 0xe0, 0x01);
++ ov534_set_led(gspca_dev, 0);
++ ov534_reg_write(gspca_dev, 0xe0, 0x00);
++ break;
++ }
+ }
+
+ /* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
+@@ -429,75 +830,75 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame,
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u32 this_pts;
+- int this_fid;
++ u16 this_fid;
+ int remaining_len = len;
+- __u8 *next_data = data;
+
+-scan_next:
+- if (remaining_len <= 0)
+- return;
+-
+- data = next_data;
+- len = min(remaining_len, 2048);
+- remaining_len -= len;
+- next_data += len;
+-
+- /* Payloads are prefixed with a UVC-style header. We
+- consider a frame to start when the FID toggles, or the PTS
+- changes. A frame ends when EOF is set, and we've received
+- the correct number of bytes. */
+-
+- /* Verify UVC header. Header length is always 12 */
+- if (data[0] != 12 || len < 12) {
+- PDEBUG(D_PACK, "bad header");
+- goto discard;
+- }
+-
+- /* Check errors */
+- if (data[1] & UVC_STREAM_ERR) {
+- PDEBUG(D_PACK, "payload error");
+- goto discard;
+- }
++ do {
++ len = min(remaining_len, 2040); /*fixme: was 2048*/
+
+- /* Extract PTS and FID */
+- if (!(data[1] & UVC_STREAM_PTS)) {
+- PDEBUG(D_PACK, "PTS not present");
+- goto discard;
+- }
+- this_pts = (data[5] << 24) | (data[4] << 16) | (data[3] << 8) | data[2];
+- this_fid = (data[1] & UVC_STREAM_FID) ? 1 : 0;
+-
+- /* If PTS or FID has changed, start a new frame. */
+- if (this_pts != sd->last_pts || this_fid != sd->last_fid) {
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0);
+- sd->last_pts = this_pts;
+- sd->last_fid = this_fid;
+- }
++ /* Payloads are prefixed with a UVC-style header. We
++ consider a frame to start when the FID toggles, or the PTS
++ changes. A frame ends when EOF is set, and we've received
++ the correct number of bytes. */
+
+- /* Add the data from this payload */
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+- data + 12, len - 12);
++ /* Verify UVC header. Header length is always 12 */
++ if (data[0] != 12 || len < 12) {
++ PDEBUG(D_PACK, "bad header");
++ goto discard;
++ }
+
+- /* If this packet is marked as EOF, end the frame */
+- if (data[1] & UVC_STREAM_EOF) {
+- sd->last_pts = 0;
++ /* Check errors */
++ if (data[1] & UVC_STREAM_ERR) {
++ PDEBUG(D_PACK, "payload error");
++ goto discard;
++ }
+
+- if ((frame->data_end - frame->data) !=
+- (gspca_dev->width * gspca_dev->height * 2)) {
+- PDEBUG(D_PACK, "short frame");
++ /* Extract PTS and FID */
++ if (!(data[1] & UVC_STREAM_PTS)) {
++ PDEBUG(D_PACK, "PTS not present");
+ goto discard;
+ }
++ this_pts = (data[5] << 24) | (data[4] << 16)
++ | (data[3] << 8) | data[2];
++ this_fid = (data[1] & UVC_STREAM_FID) ? 1 : 0;
++
++ /* If PTS or FID has changed, start a new frame. */
++ if (this_pts != sd->last_pts || this_fid != sd->last_fid) {
++ gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ NULL, 0);
++ sd->last_pts = this_pts;
++ sd->last_fid = this_fid;
++ }
+
+- gspca_frame_add(gspca_dev, LAST_PACKET, frame, NULL, 0);
+- }
++ /* Add the data from this payload */
++ gspca_frame_add(gspca_dev, INTER_PACKET, frame,
++ data + 12, len - 12);
+
+- /* Done this payload */
+- goto scan_next;
++ /* If this packet is marked as EOF, end the frame */
++ if (data[1] & UVC_STREAM_EOF) {
++ sd->last_pts = 0;
++
++ if (frame->data_end - frame->data !=
++ gspca_dev->width * gspca_dev->height * 2) {
++ PDEBUG(D_PACK, "short frame");
++ goto discard;
++ }
++
++ frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
++ NULL, 0);
++ }
++
++ /* Done this payload */
++ goto scan_next;
+
+ discard:
+- /* Discard data until a new frame starts. */
+- gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0);
+- goto scan_next;
++ /* Discard data until a new frame starts. */
++ gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0);
++
++scan_next:
++ remaining_len -= len;
++ data += len;
++ } while (remaining_len > 0);
+ }
+
+ /* get stream parameters (framerate) */
+@@ -556,9 +957,8 @@ static const struct sd_desc sd_desc = {
+
+ /* -- module initialisation -- */
+ static const __devinitdata struct usb_device_id device_table[] = {
+- {USB_DEVICE(0x06f8, 0x3002)}, /* Hercules Blog Webcam */
+- {USB_DEVICE(0x06f8, 0x3003)}, /* Hercules Dualpix HD Weblog */
+- {USB_DEVICE(0x1415, 0x2000)}, /* Sony HD Eye for PS3 (SLEH 00201) */
++ {USB_DEVICE(0x06f8, 0x3003), .driver_info = SENSOR_OV965X},
++ {USB_DEVICE(0x1415, 0x2000), .driver_info = SENSOR_OV772X},
+ {}
+ };
+
+@@ -585,8 +985,10 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c
+index c90ac85..95a97ab 100644
+--- a/drivers/media/video/gspca/pac207.c
++++ b/drivers/media/video/gspca/pac207.c
+@@ -256,7 +256,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = 0x05;
+ cam->cam_mode = sif_mode;
+ cam->nmodes = ARRAY_SIZE(sif_mode);
+ sd->brightness = PAC207_BRIGHTNESS_DEFAULT;
+@@ -536,6 +535,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x093a, 0x2470)},
+ {USB_DEVICE(0x093a, 0x2471)},
+ {USB_DEVICE(0x093a, 0x2472)},
++ {USB_DEVICE(0x093a, 0x2474)},
+ {USB_DEVICE(0x093a, 0x2476)},
+ {USB_DEVICE(0x145f, 0x013a)},
+ {USB_DEVICE(0x2001, 0xf115)},
+@@ -565,8 +565,10 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c
+index a9c95cb..e1e3a3a 100644
+--- a/drivers/media/video/gspca/pac7311.c
++++ b/drivers/media/video/gspca/pac7311.c
+@@ -498,7 +498,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = 0x05;
+
+ sd->sensor = id->driver_info;
+ if (sd->sensor == SENSOR_PAC7302) {
+@@ -1097,8 +1096,10 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c
+index b3e4e06..153d0a9 100644
+--- a/drivers/media/video/gspca/sonixb.c
++++ b/drivers/media/video/gspca/sonixb.c
+@@ -870,7 +870,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ gspca_dev->ctrl_dis = sensor_data[sd->sensor].ctrl_dis;
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = 0x01;
+ if (!(sensor_data[sd->sensor].flags & F_SIF)) {
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+@@ -1272,8 +1271,10 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
+index 3373b8d..c72e19d 100644
+--- a/drivers/media/video/gspca/sonixj.c
++++ b/drivers/media/video/gspca/sonixj.c
+@@ -35,36 +35,47 @@ struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ atomic_t avg_lum;
+- unsigned int exposure;
+-
+- __u16 brightness;
+- __u8 contrast;
+- __u8 colors;
+- __u8 autogain;
+- __u8 blue;
+- __u8 red;
+- __u8 vflip; /* ov7630 only */
+- __u8 infrared; /* mi0360 only */
+-
+- __s8 ag_cnt;
++ u32 exposure;
++
++ u16 brightness;
++ u8 contrast;
++ u8 colors;
++ u8 autogain;
++ u8 blue;
++ u8 red;
++ u8 gamma;
++ u8 vflip; /* ov7630/ov7648 only */
++ u8 infrared; /* mt9v111 only */
++ u8 quality; /* image quality */
++#define QUALITY_MIN 60
++#define QUALITY_MAX 95
++#define QUALITY_DEF 80
++ u8 jpegqual; /* webcam quality */
++
++ u8 reg18;
++
++ s8 ag_cnt;
+ #define AG_CNT_START 13
+
+- __u8 qindex;
+- __u8 bridge;
++ u8 bridge;
+ #define BRIDGE_SN9C102P 0
+ #define BRIDGE_SN9C105 1
+ #define BRIDGE_SN9C110 2
+ #define BRIDGE_SN9C120 3
+ #define BRIDGE_SN9C325 4
+- __u8 sensor; /* Type of image sensor chip */
++ u8 sensor; /* Type of image sensor chip */
+ #define SENSOR_HV7131R 0
+ #define SENSOR_MI0360 1
+ #define SENSOR_MO4000 2
+-#define SENSOR_OM6802 3
+-#define SENSOR_OV7630 4
+-#define SENSOR_OV7648 5
+-#define SENSOR_OV7660 6
+- __u8 i2c_base;
++#define SENSOR_MT9V111 3
++#define SENSOR_OM6802 4
++#define SENSOR_OV7630 5
++#define SENSOR_OV7648 6
++#define SENSOR_OV7660 7
++#define SENSOR_SP80708 8
++ u8 i2c_base;
++
++ u8 *jpeg_hdr;
+ };
+
+ /* V4L2 controls supported by the driver */
+@@ -78,6 +89,8 @@ static int sd_setblue_balance(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
+@@ -158,6 +171,20 @@ static struct ctrl sd_ctrls[] = {
+ .set = sd_setred_balance,
+ .get = sd_getred_balance,
+ },
++ {
++ {
++ .id = V4L2_CID_GAMMA,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Gamma",
++ .minimum = 0,
++ .maximum = 40,
++ .step = 1,
++#define GAMMA_DEF 20
++ .default_value = GAMMA_DEF,
++ },
++ .set = sd_setgamma,
++ .get = sd_getgamma,
++ },
+ #define AUTOGAIN_IDX 5
+ {
+ {
+@@ -173,7 +200,7 @@ static struct ctrl sd_ctrls[] = {
+ .set = sd_setautogain,
+ .get = sd_getautogain,
+ },
+-/* ov7630 only */
++/* ov7630/ov7648 only */
+ #define VFLIP_IDX 6
+ {
+ {
+@@ -183,13 +210,13 @@ static struct ctrl sd_ctrls[] = {
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+-#define VFLIP_DEF 1
++#define VFLIP_DEF 0 /* vflip def = 1 for ov7630 */
+ .default_value = VFLIP_DEF,
+ },
+ .set = sd_setvflip,
+ .get = sd_getvflip,
+ },
+-/* mi0360 only */
++/* mt9v111 only */
+ #define INFRARED_IDX 7
+ {
+ {
+@@ -211,18 +238,22 @@ static struct ctrl sd_ctrls[] = {
+ static __u32 ctrl_dis[] = {
+ (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ /* SENSOR_HV7131R 0 */
+- (1 << VFLIP_IDX),
++ (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ /* SENSOR_MI0360 1 */
+ (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+ /* SENSOR_MO4000 2 */
++ (1 << VFLIP_IDX),
++ /* SENSOR_MT9V111 3 */
+ (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+- /* SENSOR_OM6802 3 */
++ /* SENSOR_OM6802 4 */
+ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX),
+- /* SENSOR_OV7630 4 */
++ /* SENSOR_OV7630 5 */
++ (1 << INFRARED_IDX),
++ /* SENSOR_OV7648 6 */
+ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+- /* SENSOR_OV7648 5 */
++ /* SENSOR_OV7660 7 */
+ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+- /* SENSOR_OV7660 6 */
++ /* SENSOR_SP80708 8 */
+ };
+
+ static const struct v4l2_pix_format vga_mode[] = {
+@@ -243,196 +274,228 @@ static const struct v4l2_pix_format vga_mode[] = {
+ .priv = 0},
+ };
+
+-/*Data from sn9c102p+hv71331r */
+-static const __u8 sn_hv7131[] = {
++/*Data from sn9c102p+hv7131r */
++static const u8 sn_hv7131[0x1c] = {
+ /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x03, 0x64, 0x00, 0x1a, 0x20, 0x20, 0x20,
+ /* reg8 reg9 rega regb regc regd rege regf */
+ 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10,
+ /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x01, 0x03, 0x28, 0x1e, 0x41,
+-/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */
+- 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++/* reg18 reg19 reg1a reg1b */
++ 0x0a, 0x00, 0x00, 0x00
+ };
+
+-static const __u8 sn_mi0360[] = {
++static const u8 sn_mi0360[0x1c] = {
+ /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x61, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20,
+ /* reg8 reg9 rega regb regc regd rege regf */
+ 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10,
+ /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61,
+-/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */
+- 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++/* reg18 reg19 reg1a reg1b */
++ 0x06, 0x00, 0x00, 0x00
+ };
+
+-static const __u8 sn_mo4000[] = {
++static const u8 sn_mo4000[0x1c] = {
+ /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+- 0x12, 0x23, 0x60, 0x00, 0x1a, 0x00, 0x20, 0x18,
++ 0x00, 0x23, 0x60, 0x00, 0x1a, 0x00, 0x20, 0x18,
+ /* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x0b, 0x0f, 0x14, 0x28, 0x1e, 0x40,
+-/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */
+- 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
++/* reg18 reg19 reg1a reg1b */
++ 0x08, 0x00, 0x00, 0x00
+ };
+
+-static const __u8 sn_om6802[] = {
++static const u8 sn_mt9v111[0x1c] = {
++/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
++ 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
++/* reg8 reg9 rega regb regc regd rege regf */
++ 0x81, 0x5c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
++ 0x03, 0x00, 0x00, 0x02, 0x1c, 0x28, 0x1e, 0x40,
++/* reg18 reg19 reg1a reg1b */
++ 0x06, 0x00, 0x00, 0x00
++};
++
++static const u8 sn_om6802[0x1c] = {
+ /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x23, 0x72, 0x00, 0x1a, 0x34, 0x27, 0x20,
+ /* reg8 reg9 rega regb regc regd rege regf */
+ 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x51, 0x01, 0x00, 0x28, 0x1e, 0x40,
+-/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */
+- 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+- 0x08, 0x22, 0x44, 0x63, 0x7d, 0x92, 0xa3, 0xaf,
+- 0xbc, 0xc4, 0xcd, 0xd5, 0xdc, 0xe1, 0xe8, 0xef,
+- 0xf7
++/* reg18 reg19 reg1a reg1b */
++ 0x05, 0x00, 0x00, 0x00
+ };
+
+-static const __u8 sn_ov7630[] = {
++static const u8 sn_ov7630[0x1c] = {
+ /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x21, 0x40, 0x00, 0x1a, 0x20, 0x1f, 0x20,
+ /* reg8 reg9 rega regb regc regd rege regf */
+ 0xa1, 0x21, 0x76, 0x21, 0x00, 0x00, 0x00, 0x10,
+ /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x04, 0x01, 0x0a, 0x28, 0x1e, 0xc2,
+-/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */
+- 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00
++/* reg18 reg19 reg1a reg1b */
++ 0x0b, 0x00, 0x00, 0x00
+ };
+
+-static const __u8 sn_ov7648[] = {
++static const u8 sn_ov7648[0x1c] = {
+ /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
+ /* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
+ /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x01, 0x00, 0x28, 0x1e, 0x00,
+-/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */
+- 0x0b, 0x00, 0x00, 0x00, 0x00, 0x00
++/* reg18 reg19 reg1a reg1b */
++ 0x0b, 0x00, 0x00, 0x00
+ };
+
+-static const __u8 sn_ov7660[] = {
++static const u8 sn_ov7660[0x1c] = {
+ /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
+ /* reg8 reg9 rega regb regc regd rege regf */
+ 0x81, 0x21, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10,
+ /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x01, 0x01, 0x08, 0x28, 0x1e, 0x20,
+-/* reg18 reg19 reg1a reg1b reg1c reg1d reg1e reg1f */
+- 0x07, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* reg18 reg19 reg1a reg1b */
++ 0x07, 0x00, 0x00, 0x00
++};
++
++static const u8 sn_sp80708[0x1c] = {
++/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
++ 0x00, 0x63, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20,
++/* reg8 reg9 rega regb regc regd rege regf */
++ 0x81, 0x18, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
++ 0x03, 0x00, 0x00, 0x03, 0x04, 0x28, 0x1e, 0x00,
++/* reg18 reg19 reg1a reg1b */
++ 0x07, 0x00, 0x00, 0x00
+ };
+
+ /* sequence specific to the sensors - !! index = SENSOR_xxx */
+-static const __u8 *sn_tb[] = {
++static const u8 *sn_tb[] = {
+ sn_hv7131,
+ sn_mi0360,
+ sn_mo4000,
++ sn_mt9v111,
+ sn_om6802,
+ sn_ov7630,
+ sn_ov7648,
+- sn_ov7660
++ sn_ov7660,
++ sn_sp80708
+ };
+
+-static const __u8 gamma_def[] = {
++/* default gamma table */
++static const u8 gamma_def[17] = {
+ 0x00, 0x2d, 0x46, 0x5a, 0x6c, 0x7c, 0x8b, 0x99,
+ 0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff
+ };
++/* gamma for sensors HV7131R and MT9V111 */
++static const u8 gamma_spec_1[17] = {
++ 0x08, 0x3a, 0x52, 0x65, 0x75, 0x83, 0x91, 0x9d,
++ 0xa9, 0xb4, 0xbe, 0xc8, 0xd2, 0xdb, 0xe4, 0xed, 0xf5
++};
++/* gamma for sensor SP80708 */
++static const u8 gamma_spec_2[17] = {
++ 0x0a, 0x2d, 0x4e, 0x68, 0x7d, 0x8f, 0x9f, 0xab,
++ 0xb7, 0xc2, 0xcc, 0xd3, 0xd8, 0xde, 0xe2, 0xe5, 0xe6
++};
+
+ /* color matrix and offsets */
+-static const __u8 reg84[] = {
++static const u8 reg84[] = {
+ 0x14, 0x00, 0x27, 0x00, 0x07, 0x00, /* YR YG YB gains */
+ 0xe8, 0x0f, 0xda, 0x0f, 0x40, 0x00, /* UR UG UB */
+ 0x3e, 0x00, 0xcd, 0x0f, 0xf7, 0x0f, /* VR VG VB */
+ 0x00, 0x00, 0x00 /* YUV offsets */
+ };
+-static const __u8 hv7131r_sensor_init[][8] = {
+- {0xC1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10},
+- {0xB1, 0x11, 0x34, 0x17, 0x7F, 0x00, 0x00, 0x10},
+- {0xD1, 0x11, 0x40, 0xFF, 0x7F, 0x7F, 0x7F, 0x10},
+- {0x91, 0x11, 0x44, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xD1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xD1, 0x11, 0x14, 0x01, 0xE2, 0x02, 0x82, 0x10},
+- {0x91, 0x11, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10},
+-
+- {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+- {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+- {0xC1, 0x11, 0x25, 0x00, 0x61, 0xA8, 0x00, 0x10},
+- {0xA1, 0x11, 0x30, 0x22, 0x00, 0x00, 0x00, 0x10},
+- {0xC1, 0x11, 0x31, 0x20, 0x2E, 0x20, 0x00, 0x10},
+- {0xC1, 0x11, 0x25, 0x00, 0xC3, 0x50, 0x00, 0x10},
+- {0xA1, 0x11, 0x30, 0x07, 0x00, 0x00, 0x00, 0x10}, /* gain14 */
+- {0xC1, 0x11, 0x31, 0x10, 0x10, 0x10, 0x00, 0x10}, /* r g b 101a10 */
+-
+- {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+- {0xA1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xA1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10},
+- {0xA1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xA1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10},
+-
+- {0xA1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+- {0xA1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xA1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10},
+- {0xA1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xA1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10},
++static const u8 hv7131r_sensor_init[][8] = {
++ {0xc1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10},
++ {0xb1, 0x11, 0x34, 0x17, 0x7f, 0x00, 0x00, 0x10},
++ {0xd1, 0x11, 0x40, 0xff, 0x7f, 0x7f, 0x7f, 0x10},
++/* {0x91, 0x11, 0x44, 0x00, 0x00, 0x00, 0x00, 0x10}, */
++ {0xd1, 0x11, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x11, 0x14, 0x01, 0xe2, 0x02, 0x82, 0x10},
++/* {0x91, 0x11, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10}, */
++
++ {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
++ {0xc1, 0x11, 0x25, 0x00, 0x61, 0xa8, 0x00, 0x10},
++ {0xa1, 0x11, 0x30, 0x22, 0x00, 0x00, 0x00, 0x10},
++ {0xc1, 0x11, 0x31, 0x20, 0x2e, 0x20, 0x00, 0x10},
++ {0xc1, 0x11, 0x25, 0x00, 0xc3, 0x50, 0x00, 0x10},
++ {0xa1, 0x11, 0x30, 0x07, 0x00, 0x00, 0x00, 0x10}, /* gain14 */
++ {0xc1, 0x11, 0x31, 0x10, 0x10, 0x10, 0x00, 0x10}, /* r g b 101a10 */
++
++ {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10},
++
++ {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10},
+ {}
+ };
+-static const __u8 mi0360_sensor_init[][8] = {
+- {0xB1, 0x5D, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10},
+- {0xB1, 0x5D, 0x0D, 0x00, 0x01, 0x00, 0x00, 0x10},
+- {0xB1, 0x5D, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10},
+- {0xD1, 0x5D, 0x03, 0x01, 0xE2, 0x02, 0x82, 0x10},
+- {0xD1, 0x5D, 0x05, 0x00, 0x09, 0x00, 0x53, 0x10},
+- {0xB1, 0x5D, 0x0D, 0x00, 0x02, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x1A, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xB1, 0x5D, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10},
+- {0xD1, 0x5D, 0x2F, 0xF7, 0xB0, 0x00, 0x04, 0x10},
+- {0xD1, 0x5D, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10},
+- {0xB1, 0x5D, 0x3D, 0x06, 0x8F, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x40, 0x01, 0xE0, 0x00, 0xD1, 0x10},
+- {0xB1, 0x5D, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10},
+- {0xD1, 0x5D, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x5C, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x5E, 0x00, 0x00, 0xA3, 0x1D, 0x10},
+- {0xB1, 0x5D, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10},
+-
+- {0xB1, 0x5D, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10},
+- {0xB1, 0x5D, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
+- {0xB1, 0x5D, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10},
+- {0xD1, 0x5D, 0x2B, 0x00, 0xA0, 0x00, 0xB0, 0x10},
+- {0xD1, 0x5D, 0x2D, 0x00, 0xA0, 0x00, 0xA0, 0x10},
+-
+- {0xB1, 0x5D, 0x0A, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor clck ?2 */
+- {0xB1, 0x5D, 0x06, 0x00, 0x30, 0x00, 0x00, 0x10},
+- {0xB1, 0x5D, 0x05, 0x00, 0x0A, 0x00, 0x00, 0x10},
+- {0xB1, 0x5D, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */
+-
+- {0xD1, 0x5D, 0x2B, 0x00, 0xB9, 0x00, 0xE3, 0x10},
+- {0xD1, 0x5D, 0x2D, 0x00, 0x5f, 0x00, 0xB9, 0x10}, /* 42 */
+-/* {0xB1, 0x5D, 0x35, 0x00, 0x67, 0x00, 0x00, 0x10}, * gain orig */
+-/* {0xB1, 0x5D, 0x35, 0x00, 0x20, 0x00, 0x00, 0x10}, * gain */
+- {0xB1, 0x5D, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */
+- {0xB1, 0x5D, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */
++static const u8 mi0360_sensor_init[][8] = {
++ {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10},
++ {0xb1, 0x5d, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10},
++ {0xb1, 0x5d, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x01, 0x00, 0x08, 0x00, 0x16, 0x10},
++ {0xd1, 0x5d, 0x03, 0x01, 0xe2, 0x02, 0x82, 0x10},
++ {0xd1, 0x5d, 0x05, 0x00, 0x09, 0x00, 0x53, 0x10},
++ {0xb1, 0x5d, 0x0d, 0x00, 0x02, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x0e, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x14, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x18, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x1c, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xb1, 0x5d, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x24, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x26, 0x00, 0x00, 0x00, 0x24, 0x10},
++ {0xd1, 0x5d, 0x2f, 0xf7, 0xB0, 0x00, 0x04, 0x10},
++ {0xd1, 0x5d, 0x31, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x33, 0x00, 0x00, 0x01, 0x00, 0x10},
++ {0xb1, 0x5d, 0x3d, 0x06, 0x8f, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x40, 0x01, 0xe0, 0x00, 0xd1, 0x10},
++ {0xb1, 0x5d, 0x44, 0x00, 0x82, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x58, 0x00, 0x78, 0x00, 0x43, 0x10},
++ {0xd1, 0x5d, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x5e, 0x00, 0x00, 0xa3, 0x1d, 0x10},
++ {0xb1, 0x5d, 0x62, 0x04, 0x11, 0x00, 0x00, 0x10},
++
++ {0xb1, 0x5d, 0x20, 0x91, 0x01, 0x00, 0x00, 0x10},
++ {0xb1, 0x5d, 0x20, 0x11, 0x01, 0x00, 0x00, 0x10},
++ {0xb1, 0x5d, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10},
++ {0xd1, 0x5d, 0x2b, 0x00, 0xa0, 0x00, 0xb0, 0x10},
++ {0xd1, 0x5d, 0x2d, 0x00, 0xa0, 0x00, 0xa0, 0x10},
++
++ {0xb1, 0x5d, 0x0a, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor clck ?2 */
++ {0xb1, 0x5d, 0x06, 0x00, 0x30, 0x00, 0x00, 0x10},
++ {0xb1, 0x5d, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x10},
++ {0xb1, 0x5d, 0x09, 0x02, 0x35, 0x00, 0x00, 0x10}, /* exposure 2 */
++
++ {0xd1, 0x5d, 0x2b, 0x00, 0xb9, 0x00, 0xe3, 0x10},
++ {0xd1, 0x5d, 0x2d, 0x00, 0x5f, 0x00, 0xb9, 0x10}, /* 42 */
++/* {0xb1, 0x5d, 0x35, 0x00, 0x67, 0x00, 0x00, 0x10}, * gain orig */
++/* {0xb1, 0x5d, 0x35, 0x00, 0x20, 0x00, 0x00, 0x10}, * gain */
++ {0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10}, /* update */
++ {0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10}, /* sensor on */
+ {}
+ };
+-static const __u8 mo4000_sensor_init[][8] = {
++static const u8 mo4000_sensor_init[][8] = {
+ {0xa1, 0x21, 0x01, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x02, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x03, 0x00, 0x00, 0x00, 0x00, 0x10},
+@@ -455,7 +518,49 @@ static const __u8 mo4000_sensor_init[][8] = {
+ {0xa1, 0x21, 0x11, 0x38, 0x00, 0x00, 0x00, 0x10},
+ {}
+ };
+-static __u8 om6802_sensor_init[][8] = {
++static const u8 mt9v111_sensor_init[][8] = {
++ {0xb1, 0x5c, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, /* reset? */
++ /* delay 20 ms */
++ {0xb1, 0x5c, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xb1, 0x5c, 0x01, 0x00, 0x01, 0x00, 0x00, 0x10}, /* IFP select */
++ {0xb1, 0x5c, 0x08, 0x04, 0x80, 0x00, 0x00, 0x10}, /* output fmt ctrl */
++ {0xb1, 0x5c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10}, /* op mode ctrl */
++ {0xb1, 0x5c, 0x02, 0x00, 0x16, 0x00, 0x00, 0x10},
++ {0xb1, 0x5c, 0x03, 0x01, 0xe1, 0x00, 0x00, 0x10},
++ {0xb1, 0x5c, 0x04, 0x02, 0x81, 0x00, 0x00, 0x10},
++ {0xb1, 0x5c, 0x05, 0x00, 0x04, 0x00, 0x00, 0x10},
++ {0xb1, 0x5c, 0x01, 0x00, 0x04, 0x00, 0x00, 0x10}, /* sensor select */
++ {0xb1, 0x5c, 0x02, 0x00, 0x16, 0x00, 0x00, 0x10},
++ {0xb1, 0x5c, 0x03, 0x01, 0xe6, 0x00, 0x00, 0x10},
++ {0xb1, 0x5c, 0x04, 0x02, 0x86, 0x00, 0x00, 0x10},
++ {0xb1, 0x5c, 0x05, 0x00, 0x04, 0x00, 0x00, 0x10},
++ {0xb1, 0x5c, 0x06, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xb1, 0x5c, 0x08, 0x00, 0x08, 0x00, 0x00, 0x10}, /* row start */
++ {0xb1, 0x5c, 0x0e, 0x00, 0x08, 0x00, 0x00, 0x10},
++ {0xb1, 0x5c, 0x02, 0x00, 0x16, 0x00, 0x00, 0x10}, /* col start */
++ {0xb1, 0x5c, 0x03, 0x01, 0xe7, 0x00, 0x00, 0x10}, /* window height */
++ {0xb1, 0x5c, 0x04, 0x02, 0x87, 0x00, 0x00, 0x10}, /* window width */
++ {0xb1, 0x5c, 0x07, 0x30, 0x02, 0x00, 0x00, 0x10}, /* output ctrl */
++ {0xb1, 0x5c, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x10}, /* shutter delay */
++ {0xb1, 0x5c, 0x12, 0x00, 0xb0, 0x00, 0x00, 0x10}, /* zoom col start */
++ {0xb1, 0x5c, 0x13, 0x00, 0x7c, 0x00, 0x00, 0x10}, /* zoom row start */
++ {0xb1, 0x5c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* digital zoom */
++ {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, /* read mode */
++ {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
++ /*******/
++ {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xb1, 0x5c, 0x09, 0x01, 0x2c, 0x00, 0x00, 0x10},
++ {0xd1, 0x5c, 0x2b, 0x00, 0x33, 0x00, 0xa0, 0x10}, /* green1 gain */
++ {0xd1, 0x5c, 0x2d, 0x00, 0xa0, 0x00, 0x33, 0x10}, /* red gain */
++ /*******/
++ {0xb1, 0x5c, 0x06, 0x00, 0x1e, 0x00, 0x00, 0x10}, /* vert blanking */
++ {0xb1, 0x5c, 0x05, 0x00, 0x0a, 0x00, 0x00, 0x10}, /* horiz blanking */
++ {0xd1, 0x5c, 0x2c, 0x00, 0xad, 0x00, 0xad, 0x10}, /* blue gain */
++ {0xb1, 0x5c, 0x35, 0x01, 0xc0, 0x00, 0x00, 0x10}, /* global gain */
++ {}
++};
++static const u8 om6802_sensor_init[][8] = {
+ {0xa0, 0x34, 0x90, 0x05, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x34, 0x49, 0x85, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10},
+@@ -489,7 +594,7 @@ static __u8 om6802_sensor_init[][8] = {
+ /* {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10}, */
+ {}
+ };
+-static const __u8 ov7630_sensor_init[][8] = {
++static const u8 ov7630_sensor_init[][8] = {
+ {0xa1, 0x21, 0x76, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10},
+ /* win: delay 20ms */
+@@ -543,7 +648,7 @@ static const __u8 ov7630_sensor_init[][8] = {
+ {}
+ };
+
+-static const __u8 ov7648_sensor_init[][8] = {
++static const u8 ov7648_sensor_init[][8] = {
+ {0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */
+ {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+@@ -572,7 +677,8 @@ static const __u8 ov7648_sensor_init[][8] = {
+ {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10},
+ /*...*/
+ /* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */
+-/* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, jfm done */
++/* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, * COMN
++ * set by setvflip */
+ {0xa1, 0x21, 0x19, 0x02, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x10, 0x32, 0x00, 0x00, 0x00, 0x10},
+ /* {0xa1, 0x21, 0x16, 0x00, 0x00, 0x00, 0x00, 0x10}, jfm done */
+@@ -589,7 +695,7 @@ static const __u8 ov7648_sensor_init[][8] = {
+ {}
+ };
+
+-static const __u8 ov7660_sensor_init[][8] = {
++static const u8 ov7660_sensor_init[][8] = {
+ {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */
+ /* (delay 20ms) */
+ {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10},
+@@ -678,28 +784,92 @@ static const __u8 ov7660_sensor_init[][8] = {
+ {}
+ };
+
+-static const __u8 qtable4[] = {
+- 0x06, 0x04, 0x04, 0x06, 0x04, 0x04, 0x06, 0x06, 0x06, 0x06, 0x08, 0x06,
+- 0x06, 0x08, 0x0A, 0x11,
+- 0x0A, 0x0A, 0x08, 0x08, 0x0A, 0x15, 0x0F, 0x0F, 0x0C, 0x11, 0x19, 0x15,
+- 0x19, 0x19, 0x17, 0x15,
+- 0x17, 0x17, 0x1B, 0x1D, 0x25, 0x21, 0x1B, 0x1D, 0x23, 0x1D, 0x17, 0x17,
+- 0x21, 0x2E, 0x21, 0x23,
+- 0x27, 0x29, 0x2C, 0x2C, 0x2C, 0x19, 0x1F, 0x30, 0x32, 0x2E, 0x29, 0x32,
+- 0x25, 0x29, 0x2C, 0x29,
+- 0x06, 0x08, 0x08, 0x0A, 0x08, 0x0A, 0x13, 0x0A, 0x0A, 0x13, 0x29, 0x1B,
+- 0x17, 0x1B, 0x29, 0x29,
+- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+- 0x29, 0x29, 0x29, 0x29,
+- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+- 0x29, 0x29, 0x29, 0x29,
+- 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29,
+- 0x29, 0x29, 0x29, 0x29
++static const u8 sp80708_sensor_init[][8] = {
++ {0xa1, 0x18, 0x06, 0xf9, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x09, 0x1f, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x0d, 0xc0, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x0f, 0x0f, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x10, 0x40, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x11, 0x4e, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x12, 0x53, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x15, 0x80, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x19, 0x18, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x1a, 0x10, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x1b, 0x10, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x1c, 0x28, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x1d, 0x02, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x1e, 0x10, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x26, 0x04, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x27, 0x1e, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x28, 0x5a, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x29, 0x28, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x2a, 0x78, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x2b, 0x01, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x2c, 0xf7, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x2d, 0x2d, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x2e, 0xd5, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x39, 0x42, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x3a, 0x67, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x3b, 0x87, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x3c, 0xa3, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x3d, 0xb0, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x3e, 0xbc, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x3f, 0xc8, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x40, 0xd4, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x41, 0xdf, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x42, 0xea, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x43, 0xf5, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x45, 0x80, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x46, 0x60, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x47, 0x50, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x48, 0x30, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x49, 0x01, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x4d, 0xae, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x4e, 0x03, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x4f, 0x66, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x50, 0x1c, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x44, 0x10, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x4a, 0x30, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x51, 0x80, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x52, 0x80, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x53, 0x80, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x54, 0x80, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x55, 0x80, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x56, 0x80, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x57, 0xe0, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x58, 0xc0, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x59, 0xab, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x5a, 0xa0, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x5b, 0x99, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x5c, 0x90, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x5e, 0x24, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x5f, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x60, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x61, 0x73, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x63, 0x42, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x64, 0x42, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x65, 0x42, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x66, 0x24, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x67, 0x24, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x68, 0x08, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x2f, 0xc9, 0x00, 0x00, 0x00, 0x10},
++ /********/
++ {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x03, 0x01, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x04, 0xa4, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x14, 0x3f, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x18, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10},
++ {0xb1, 0x18, 0x11, 0x40, 0x40, 0x00, 0x00, 0x10},
++ {}
+ };
+
+ /* read <len> bytes to gspca_dev->usb_buf */
+ static void reg_r(struct gspca_dev *gspca_dev,
+- __u16 value, int len)
++ u16 value, int len)
+ {
+ #ifdef GSPCA_DEBUG
+ if (len > USB_BUF_SZ) {
+@@ -718,10 +888,10 @@ static void reg_r(struct gspca_dev *gspca_dev,
+ }
+
+ static void reg_w1(struct gspca_dev *gspca_dev,
+- __u16 value,
+- __u8 data)
++ u16 value,
++ u8 data)
+ {
+- PDEBUG(D_USBO, "reg_w1 [%02x] = %02x", value, data);
++ PDEBUG(D_USBO, "reg_w1 [%04x] = %02x", value, data);
+ gspca_dev->usb_buf[0] = data;
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+@@ -733,11 +903,11 @@ static void reg_w1(struct gspca_dev *gspca_dev,
+ 500);
+ }
+ static void reg_w(struct gspca_dev *gspca_dev,
+- __u16 value,
+- const __u8 *buffer,
++ u16 value,
++ const u8 *buffer,
+ int len)
+ {
+- PDEBUG(D_USBO, "reg_w [%02x] = %02x %02x ..",
++ PDEBUG(D_USBO, "reg_w [%04x] = %02x %02x ..",
+ value, buffer[0], buffer[1]);
+ #ifdef GSPCA_DEBUG
+ if (len > USB_BUF_SZ) {
+@@ -756,7 +926,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
+ }
+
+ /* I2C write 1 byte */
+-static void i2c_w1(struct gspca_dev *gspca_dev, __u8 reg, __u8 val)
++static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+@@ -781,7 +951,7 @@ static void i2c_w1(struct gspca_dev *gspca_dev, __u8 reg, __u8 val)
+
+ /* I2C write 8 bytes */
+ static void i2c_w8(struct gspca_dev *gspca_dev,
+- const __u8 *buffer)
++ const u8 *buffer)
+ {
+ memcpy(gspca_dev->usb_buf, buffer, 8);
+ usb_control_msg(gspca_dev->dev,
+@@ -795,10 +965,10 @@ static void i2c_w8(struct gspca_dev *gspca_dev,
+ }
+
+ /* read 5 bytes in gspca_dev->usb_buf */
+-static void i2c_r5(struct gspca_dev *gspca_dev, __u8 reg)
++static void i2c_r5(struct gspca_dev *gspca_dev, u8 reg)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- __u8 mode[8];
++ u8 mode[8];
+
+ mode[0] = 0x81 | 0x10;
+ mode[1] = sd->i2c_base;
+@@ -817,7 +987,7 @@ static void i2c_r5(struct gspca_dev *gspca_dev, __u8 reg)
+ reg_r(gspca_dev, 0x0a, 5);
+ }
+
+-static int probesensor(struct gspca_dev *gspca_dev)
++static int hv7131r_probe(struct gspca_dev *gspca_dev)
+ {
+ i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */
+ msleep(10);
+@@ -839,16 +1009,66 @@ static int probesensor(struct gspca_dev *gspca_dev)
+ return -ENODEV;
+ }
+
++static void mi0360_probe(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int i, j;
++ u16 val = 0;
++ static const u8 probe_tb[][4][8] = {
++ { /* mi0360 */
++ {0xb0, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10},
++ {0x90, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xa2, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xb0, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10}
++ },
++ { /* mt9v111 */
++ {0xb0, 0x5c, 0x01, 0x00, 0x04, 0x00, 0x00, 0x10},
++ {0x90, 0x5c, 0x36, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xa2, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {}
++ },
++ };
++
++ for (i = 0; i < ARRAY_SIZE(probe_tb); i++) {
++ reg_w1(gspca_dev, 0x17, 0x62);
++ reg_w1(gspca_dev, 0x01, 0x08);
++ for (j = 0; j < 3; j++)
++ i2c_w8(gspca_dev, probe_tb[i][j]);
++ msleep(2);
++ reg_r(gspca_dev, 0x0a, 5);
++ val = (gspca_dev->usb_buf[3] << 8) | gspca_dev->usb_buf[4];
++ if (probe_tb[i][3][0] != 0)
++ i2c_w8(gspca_dev, probe_tb[i][3]);
++ reg_w1(gspca_dev, 0x01, 0x29);
++ reg_w1(gspca_dev, 0x17, 0x42);
++ if (val != 0xffff)
++ break;
++ }
++ switch (val) {
++ case 0x823a:
++ PDEBUG(D_PROBE, "Sensor mt9v111");
++ sd->sensor = SENSOR_MT9V111;
++ sd->i2c_base = 0x5c;
++ break;
++ case 0x8243:
++ PDEBUG(D_PROBE, "Sensor mi0360");
++ break;
++ default:
++ PDEBUG(D_PROBE, "Unknown sensor %04x - forced to mi0360", val);
++ break;
++ }
++}
++
+ static int configure_gpio(struct gspca_dev *gspca_dev,
+- const __u8 *sn9c1xx)
++ const u8 *sn9c1xx)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- const __u8 *reg9a;
+- static const __u8 reg9a_def[] =
++ const u8 *reg9a;
++ static const u8 reg9a_def[] =
+ {0x08, 0x40, 0x20, 0x10, 0x00, 0x04};
+- static const __u8 reg9a_sn9c325[] =
++ static const u8 reg9a_sn9c325[] =
+ {0x0a, 0x40, 0x38, 0x30, 0x00, 0x20};
+- static const __u8 regd4[] = {0x60, 0x00, 0x00};
++ static const u8 regd4[] = {0x60, 0x00, 0x00};
+
+ reg_w1(gspca_dev, 0xf1, 0x00);
+ reg_w1(gspca_dev, 0x01, sn9c1xx[1]);
+@@ -872,6 +1092,12 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
+ reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f);
+
+ switch (sd->sensor) {
++ case SENSOR_MT9V111:
++ reg_w1(gspca_dev, 0x01, 0x61);
++ reg_w1(gspca_dev, 0x17, 0x61);
++ reg_w1(gspca_dev, 0x01, 0x60);
++ reg_w1(gspca_dev, 0x01, 0x40);
++ break;
+ case SENSOR_OM6802:
+ reg_w1(gspca_dev, 0x02, 0x71);
+ reg_w1(gspca_dev, 0x01, 0x42);
+@@ -900,12 +1126,20 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
+ break;
+ }
+ /* fall thru */
++ case SENSOR_SP80708:
++ reg_w1(gspca_dev, 0x01, 0x63);
++ reg_w1(gspca_dev, 0x17, 0x20);
++ reg_w1(gspca_dev, 0x01, 0x62);
++ reg_w1(gspca_dev, 0x01, 0x42);
++ mdelay(100);
++ reg_w1(gspca_dev, 0x02, 0x62);
++ break;
+ default:
+ reg_w1(gspca_dev, 0x01, 0x43);
+ reg_w1(gspca_dev, 0x17, 0x61);
+ reg_w1(gspca_dev, 0x01, 0x42);
+ if (sd->sensor == SENSOR_HV7131R) {
+- if (probesensor(gspca_dev) < 0)
++ if (hv7131r_probe(gspca_dev) < 0)
+ return -ENODEV;
+ }
+ break;
+@@ -916,7 +1150,7 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
+ static void hv7131R_InitSensor(struct gspca_dev *gspca_dev)
+ {
+ int i = 0;
+- static const __u8 SetSensorClk[] = /* 0x08 Mclk */
++ static const u8 SetSensorClk[] = /* 0x08 Mclk */
+ { 0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10 };
+
+ while (hv7131r_sensor_init[i][0]) {
+@@ -946,6 +1180,19 @@ static void mo4000_InitSensor(struct gspca_dev *gspca_dev)
+ }
+ }
+
++static void mt9v111_InitSensor(struct gspca_dev *gspca_dev)
++{
++ int i = 0;
++
++ i2c_w8(gspca_dev, mt9v111_sensor_init[i]);
++ i++;
++ msleep(20);
++ while (mt9v111_sensor_init[i][0]) {
++ i2c_w8(gspca_dev, mt9v111_sensor_init[i]);
++ i++;
++ }
++}
++
+ static void om6802_InitSensor(struct gspca_dev *gspca_dev)
+ {
+ int i = 0;
+@@ -1010,6 +1257,19 @@ static void ov7660_InitSensor(struct gspca_dev *gspca_dev)
+ }
+ }
+
++static void sp80708_InitSensor(struct gspca_dev *gspca_dev)
++{
++ int i = 0;
++
++ i2c_w8(gspca_dev, sp80708_sensor_init[i]); /* reset SCCB */
++ i++;
++ msleep(20);
++ while (sp80708_sensor_init[i][0]) {
++ i2c_w8(gspca_dev, sp80708_sensor_init[i]);
++ i++;
++ }
++}
++
+ /* this function is called at probe time */
+ static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+@@ -1018,7 +1278,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = 0x01;
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
+
+@@ -1026,16 +1285,21 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ sd->sensor = id->driver_info >> 8;
+ sd->i2c_base = id->driver_info;
+
+- sd->qindex = 4; /* set the quantization table */
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->colors = COLOR_DEF;
+ sd->blue = BLUE_BALANCE_DEF;
+ sd->red = RED_BALANCE_DEF;
++ sd->gamma = GAMMA_DEF;
+ sd->autogain = AUTOGAIN_DEF;
+ sd->ag_cnt = -1;
+- sd->vflip = VFLIP_DEF;
++ if (sd->sensor != SENSOR_OV7630)
++ sd->vflip = 0;
++ else
++ sd->vflip = 1;
+ sd->infrared = INFRARED_DEF;
++ sd->quality = QUALITY_DEF;
++ sd->jpegqual = 80;
+
+ gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
+ return 0;
+@@ -1045,8 +1309,8 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ static int sd_init(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- __u8 regGpio[] = { 0x29, 0x74 };
+- __u8 regF1;
++ u8 regGpio[] = { 0x29, 0x74 };
++ u8 regF1;
+
+ /* setup a selector by bridge */
+ reg_w1(gspca_dev, 0xf1, 0x01);
+@@ -1064,11 +1328,15 @@ static int sd_init(struct gspca_dev *gspca_dev)
+ case BRIDGE_SN9C105:
+ if (regF1 != 0x11)
+ return -ENODEV;
++ if (sd->sensor == SENSOR_MI0360)
++ mi0360_probe(gspca_dev);
+ reg_w(gspca_dev, 0x01, regGpio, 2);
+ break;
+ case BRIDGE_SN9C120:
+ if (regF1 != 0x12)
+ return -ENODEV;
++ if (sd->sensor == SENSOR_MI0360)
++ mi0360_probe(gspca_dev);
+ regGpio[1] = 0x70;
+ reg_w(gspca_dev, 0x01, regGpio, 2);
+ break;
+@@ -1086,20 +1354,14 @@ static int sd_init(struct gspca_dev *gspca_dev)
+ return 0;
+ }
+
+-static unsigned int setexposure(struct gspca_dev *gspca_dev,
+- unsigned int expo)
++static u32 setexposure(struct gspca_dev *gspca_dev,
++ u32 expo)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- static const __u8 doit[] = /* update sensor */
+- { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 };
+- static const __u8 sensorgo[] = /* sensor on */
+- { 0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10 };
+- static const __u8 gainMo[] =
+- { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d };
+
+ switch (sd->sensor) {
+ case SENSOR_HV7131R: {
+- __u8 Expodoit[] =
++ u8 Expodoit[] =
+ { 0xc1, 0x11, 0x25, 0x07, 0x27, 0xc0, 0x00, 0x16 };
+
+ Expodoit[3] = expo >> 16;
+@@ -1109,8 +1371,12 @@ static unsigned int setexposure(struct gspca_dev *gspca_dev,
+ break;
+ }
+ case SENSOR_MI0360: {
+- __u8 expoMi[] = /* exposure 0x0635 -> 4 fp/s 0x10 */
++ u8 expoMi[] = /* exposure 0x0635 -> 4 fp/s 0x10 */
+ { 0xb1, 0x5d, 0x09, 0x06, 0x35, 0x00, 0x00, 0x16 };
++ static const u8 doit[] = /* update sensor */
++ { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 };
++ static const u8 sensorgo[] = /* sensor on */
++ { 0xb1, 0x5d, 0x07, 0x00, 0x02, 0x00, 0x00, 0x10 };
+
+ if (expo > 0x0635)
+ expo = 0x0635;
+@@ -1124,10 +1390,12 @@ static unsigned int setexposure(struct gspca_dev *gspca_dev,
+ break;
+ }
+ case SENSOR_MO4000: {
+- __u8 expoMof[] =
++ u8 expoMof[] =
+ { 0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10 };
+- __u8 expoMo10[] =
++ u8 expoMo10[] =
+ { 0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10 };
++ static const u8 gainMo[] =
++ { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d };
+
+ if (expo > 0x1fff)
+ expo = 0x1fff;
+@@ -1139,14 +1407,27 @@ static unsigned int setexposure(struct gspca_dev *gspca_dev,
+ | ((expo & 0x0003) << 4);
+ i2c_w8(gspca_dev, expoMo10);
+ i2c_w8(gspca_dev, gainMo);
+- PDEBUG(D_CONF, "set exposure %d",
++ PDEBUG(D_FRAM, "set exposure %d",
+ ((expoMo10[3] & 0x07) << 10)
+ | (expoMof[3] << 2)
+ | ((expoMo10[3] & 0x30) >> 4));
+ break;
+ }
++ case SENSOR_MT9V111: {
++ u8 expo_c1[] =
++ { 0xb1, 0x5c, 0x09, 0x00, 0x00, 0x00, 0x00, 0x10 };
++
++ if (expo > 0x0280)
++ expo = 0x0280;
++ else if (expo < 0x0040)
++ expo = 0x0040;
++ expo_c1[3] = expo >> 8;
++ expo_c1[4] = expo;
++ i2c_w8(gspca_dev, expo_c1);
++ break;
++ }
+ case SENSOR_OM6802: {
+- __u8 gainOm[] =
++ u8 gainOm[] =
+ { 0xa0, 0x34, 0xe5, 0x00, 0x00, 0x00, 0x00, 0x10 };
+
+ if (expo > 0x03ff)
+@@ -1156,7 +1437,7 @@ static unsigned int setexposure(struct gspca_dev *gspca_dev,
+ gainOm[3] = expo >> 2;
+ i2c_w8(gspca_dev, gainOm);
+ reg_w1(gspca_dev, 0x96, (expo >> 5) & 0x1f);
+- PDEBUG(D_CONF, "set exposure %d", gainOm[3]);
++ PDEBUG(D_FRAM, "set exposure %d", gainOm[3]);
+ break;
+ }
+ }
+@@ -1167,7 +1448,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ unsigned int expo;
+- __u8 k2;
++ u8 k2;
+
+ k2 = ((int) sd->brightness - 0x8000) >> 10;
+ switch (sd->sensor) {
+@@ -1184,6 +1465,10 @@ static void setbrightness(struct gspca_dev *gspca_dev)
+ expo = sd->brightness >> 4;
+ sd->exposure = setexposure(gspca_dev, expo);
+ break;
++ case SENSOR_MT9V111:
++ expo = sd->brightness >> 8;
++ sd->exposure = setexposure(gspca_dev, expo);
++ break;
+ case SENSOR_OM6802:
+ expo = sd->brightness >> 6;
+ sd->exposure = setexposure(gspca_dev, expo);
+@@ -1191,14 +1476,15 @@ static void setbrightness(struct gspca_dev *gspca_dev)
+ break;
+ }
+
+- reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */
++ if (sd->sensor != SENSOR_MT9V111)
++ reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */
+ }
+
+ static void setcontrast(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- __u8 k2;
+- __u8 contrast[6];
++ u8 k2;
++ u8 contrast[6];
+
+ k2 = sd->contrast * 0x30 / (CONTRAST_MAX + 1) + 0x10; /* 10..40 */
+ contrast[0] = (k2 + 1) / 2; /* red */
+@@ -1214,8 +1500,8 @@ static void setcolors(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, v;
+- __u8 reg8a[12]; /* U & V gains */
+- static __s16 uv[6] = { /* same as reg84 in signed decimal */
++ u8 reg8a[12]; /* U & V gains */
++ static s16 uv[6] = { /* same as reg84 in signed decimal */
+ -24, -38, 64, /* UR UG UB */
+ 62, -51, -9 /* VR VG VB */
+ };
+@@ -1236,22 +1522,75 @@ static void setredblue(struct gspca_dev *gspca_dev)
+ reg_w1(gspca_dev, 0x06, sd->blue);
+ }
+
++static void setgamma(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int i;
++ u8 gamma[17];
++ const u8 *gamma_base;
++ static const u8 delta[17] = {
++ 0x00, 0x14, 0x1c, 0x1c, 0x1c, 0x1c, 0x1b, 0x1a,
++ 0x18, 0x13, 0x10, 0x0e, 0x08, 0x07, 0x04, 0x02, 0x00
++ };
++
++ switch (sd->sensor) {
++ case SENSOR_HV7131R:
++ case SENSOR_MT9V111:
++ gamma_base = gamma_spec_1;
++ break;
++ case SENSOR_SP80708:
++ gamma_base = gamma_spec_2;
++ break;
++ default:
++ gamma_base = gamma_def;
++ break;
++ }
++
++ for (i = 0; i < sizeof gamma; i++)
++ gamma[i] = gamma_base[i]
++ + delta[i] * (sd->gamma - GAMMA_DEF) / 32;
++ reg_w(gspca_dev, 0x20, gamma, sizeof gamma);
++}
++
+ static void setautogain(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (gspca_dev->ctrl_dis & (1 << AUTOGAIN_IDX))
+ return;
++ switch (sd->sensor) {
++ case SENSOR_OV7630:
++ case SENSOR_OV7648: {
++ u8 comb;
++
++ if (sd->sensor == SENSOR_OV7630)
++ comb = 0xc0;
++ else
++ comb = 0xa0;
++ if (sd->autogain)
++ comb |= 0x02;
++ i2c_w1(&sd->gspca_dev, 0x13, comb);
++ return;
++ }
++ }
+ if (sd->autogain)
+ sd->ag_cnt = AG_CNT_START;
+ else
+ sd->ag_cnt = -1;
+ }
+
++/* ov7630/ov7648 only */
+ static void setvflip(struct sd *sd)
+ {
+- i2c_w1(&sd->gspca_dev, 0x75, /* COMN */
+- sd->vflip ? 0x82 : 0x02);
++ u8 comn;
++
++ if (sd->sensor == SENSOR_OV7630)
++ comn = 0x02;
++ else
++ comn = 0x06;
++ if (sd->vflip)
++ comn |= 0x80;
++ i2c_w1(&sd->gspca_dev, 0x75, comn);
+ }
+
+ static void setinfrared(struct sd *sd)
+@@ -1262,20 +1601,63 @@ static void setinfrared(struct sd *sd)
+ sd->infrared ? 0x66 : 0x64);
+ }
+
++static void setjpegqual(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int i, sc;
++
++ if (sd->jpegqual < 50)
++ sc = 5000 / sd->jpegqual;
++ else
++ sc = 200 - sd->jpegqual * 2;
++#if USB_BUF_SZ < 64
++#error "No room enough in usb_buf for quantization table"
++#endif
++ for (i = 0; i < 64; i++)
++ gspca_dev->usb_buf[i] =
++ (jpeg_head[JPEG_QT0_OFFSET + i] * sc + 50) / 100;
++ usb_control_msg(gspca_dev->dev,
++ usb_sndctrlpipe(gspca_dev->dev, 0),
++ 0x08,
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
++ 0x0100, 0,
++ gspca_dev->usb_buf, 64,
++ 500);
++ for (i = 0; i < 64; i++)
++ gspca_dev->usb_buf[i] =
++ (jpeg_head[JPEG_QT1_OFFSET + i] * sc + 50) / 100;
++ usb_control_msg(gspca_dev->dev,
++ usb_sndctrlpipe(gspca_dev->dev, 0),
++ 0x08,
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
++ 0x0140, 0,
++ gspca_dev->usb_buf, 64,
++ 500);
++
++ sd->reg18 ^= 0x40;
++ reg_w1(gspca_dev, 0x18, sd->reg18);
++}
++
+ /* -- start the camera -- */
+ static int sd_start(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+- __u8 reg1, reg17, reg18;
+- const __u8 *sn9c1xx;
++ u8 reg1, reg17;
++ const u8 *sn9c1xx;
+ int mode;
+- static const __u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f };
+- static const __u8 CA[] = { 0x28, 0xd8, 0x14, 0xec };
+- static const __u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */
+- static const __u8 CE_ov76xx[] =
++ static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f };
++ static const u8 CA[] = { 0x28, 0xd8, 0x14, 0xec };
++ static const u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */
++ static const u8 CE_ov76xx[] =
+ { 0x32, 0xdd, 0x32, 0xdd };
+
++ /* create the JPEG header */
++ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
++ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
++ 0x21); /* JPEG 422 */
++ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
++
+ sn9c1xx = sn_tb[(int) sd->sensor];
+ configure_gpio(gspca_dev, sn9c1xx);
+
+@@ -1292,6 +1674,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ reg_w1(gspca_dev, 0xc9, 0x3c);
+ reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]);
+ switch (sd->sensor) {
++ case SENSOR_MT9V111:
++ reg17 = 0xe0;
++ break;
+ case SENSOR_OV7630:
+ reg17 = 0xe2;
+ break;
+@@ -1315,14 +1700,24 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ reg_w1(gspca_dev, 0x07, sn9c1xx[7]); /* green */
+ reg_w1(gspca_dev, 0x06, sn9c1xx[6]); /* blue */
+ reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]);
+- reg_w(gspca_dev, 0x20, gamma_def, sizeof gamma_def);
++
++ setgamma(gspca_dev);
++
+ for (i = 0; i < 8; i++)
+ reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
+ switch (sd->sensor) {
++ case SENSOR_MT9V111:
++ reg_w1(gspca_dev, 0x9a, 0x07);
++ reg_w1(gspca_dev, 0x99, 0x59);
++ break;
+ case SENSOR_OV7648:
+ reg_w1(gspca_dev, 0x9a, 0x0a);
+ reg_w1(gspca_dev, 0x99, 0x60);
+ break;
++ case SENSOR_SP80708:
++ reg_w1(gspca_dev, 0x9a, 0x05);
++ reg_w1(gspca_dev, 0x99, 0x59);
++ break;
+ case SENSOR_OV7660:
+ if (sd->bridge == BRIDGE_SN9C120) {
+ reg_w1(gspca_dev, 0x9a, 0x05);
+@@ -1358,6 +1753,15 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ /* reg1 = 0x06; * 640 clk 24Mz (done) */
+ }
+ break;
++ case SENSOR_MT9V111:
++ mt9v111_InitSensor(gspca_dev);
++ if (mode) {
++ reg1 = 0x04; /* 320 clk 48Mhz */
++ } else {
++/* reg1 = 0x06; * 640 clk 24Mz (done) */
++ reg17 = 0xc2;
++ }
++ break;
+ case SENSOR_OM6802:
+ om6802_InitSensor(gspca_dev);
+ reg17 = 0x64; /* 640 MCKSIZE */
+@@ -1373,8 +1777,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ reg17 = 0x21;
+ /* reg1 = 0x42; * 42 - 46? */
+ break;
+- default:
+-/* case SENSOR_OV7660: */
++ case SENSOR_OV7660:
+ ov7660_InitSensor(gspca_dev);
+ if (sd->bridge == BRIDGE_SN9C120) {
+ if (mode) { /* 320x240 - 160x120 */
+@@ -1387,6 +1790,16 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ * inverse power down */
+ }
+ break;
++ default:
++/* case SENSOR_SP80708: */
++ sp80708_InitSensor(gspca_dev);
++ if (mode) {
++/*?? reg1 = 0x04; * 320 clk 48Mhz */
++ } else {
++ reg1 = 0x46; /* 640 clk 48Mz */
++ reg17 = 0xa2;
++ }
++ break;
+ }
+ reg_w(gspca_dev, 0xc0, C0, 6);
+ reg_w(gspca_dev, 0xca, CA, 4);
+@@ -1403,20 +1816,13 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ }
+
+ /* here change size mode 0 -> VGA; 1 -> CIF */
+- reg18 = sn9c1xx[0x18] | (mode << 4);
+- reg_w1(gspca_dev, 0x18, reg18 | 0x40);
+-
+- reg_w(gspca_dev, 0x100, qtable4, 0x40);
+- reg_w(gspca_dev, 0x140, qtable4 + 0x40, 0x40);
+-
+- reg_w1(gspca_dev, 0x18, reg18);
++ sd->reg18 = sn9c1xx[0x18] | (mode << 4) | 0x40;
++ reg_w1(gspca_dev, 0x18, sd->reg18);
++ setjpegqual(gspca_dev);
+
+ reg_w1(gspca_dev, 0x17, reg17);
+ reg_w1(gspca_dev, 0x01, reg1);
+ switch (sd->sensor) {
+- case SENSOR_MI0360:
+- setinfrared(sd);
+- break;
+ case SENSOR_OV7630:
+ setvflip(sd);
+ break;
+@@ -1430,14 +1836,14 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ static void sd_stopN(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- static const __u8 stophv7131[] =
++ static const u8 stophv7131[] =
+ { 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10 };
+- static const __u8 stopmi0360[] =
++ static const u8 stopmi0360[] =
+ { 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10 };
+- static const __u8 stopov7648[] =
++ static const u8 stopov7648[] =
+ { 0xa1, 0x21, 0x76, 0x20, 0x00, 0x00, 0x00, 0x10 };
+- __u8 data;
+- const __u8 *sn9c1xx;
++ u8 data;
++ const u8 *sn9c1xx;
+
+ data = 0x0b;
+ switch (sd->sensor) {
+@@ -1452,6 +1858,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
+ case SENSOR_OV7648:
+ i2c_w8(gspca_dev, stopov7648);
+ /* fall thru */
++ case SENSOR_MT9V111:
+ case SENSOR_OV7630:
+ data = 0x29;
+ break;
+@@ -1468,13 +1875,20 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
+ reg_w1(gspca_dev, 0xf1, 0x00);
+ }
+
++static void sd_stop0(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ kfree(sd->jpeg_hdr);
++}
++
+ static void do_autogain(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ int delta;
+ int expotimes;
+- __u8 luma_mean = 130;
+- __u8 luma_delta = 20;
++ u8 luma_mean = 130;
++ u8 luma_delta = 20;
+
+ /* Thanks S., without your advice, autobright should not work :) */
+ if (sd->ag_cnt < 0)
+@@ -1499,6 +1913,7 @@ static void do_autogain(struct gspca_dev *gspca_dev)
+ default:
+ /* case SENSOR_MO4000: */
+ /* case SENSOR_MI0360: */
++/* case SENSOR_MT9V111: */
+ /* case SENSOR_OM6802: */
+ expotimes = sd->exposure;
+ expotimes += (luma_mean - delta) >> 6;
+@@ -1516,7 +1931,7 @@ static void do_autogain(struct gspca_dev *gspca_dev)
+ /* This function is run at interrupt level. */
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ struct gspca_frame *frame, /* target */
+- __u8 *data, /* isoc packet */
++ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -1550,7 +1965,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ if (gspca_dev->last_packet_type == LAST_PACKET) {
+
+ /* put the JPEG 422 header */
+- jpeg_put_header(gspca_dev, frame, sd->qindex, 0x21);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ sd->jpeg_hdr, JPEG_HDR_SZ);
+ }
+ gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ }
+@@ -1645,6 +2061,24 @@ static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val)
+ return 0;
+ }
+
++static int sd_setgamma(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->gamma = val;
++ if (gspca_dev->streaming)
++ setgamma(gspca_dev);
++ return 0;
++}
++
++static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->gamma;
++ return 0;
++}
++
+ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -1699,6 +2133,34 @@ static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val)
+ return 0;
+ }
+
++static int sd_set_jcomp(struct gspca_dev *gspca_dev,
++ struct v4l2_jpegcompression *jcomp)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (jcomp->quality < QUALITY_MIN)
++ sd->quality = QUALITY_MIN;
++ else if (jcomp->quality > QUALITY_MAX)
++ sd->quality = QUALITY_MAX;
++ else
++ sd->quality = jcomp->quality;
++ if (gspca_dev->streaming)
++ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
++ return 0;
++}
++
++static int sd_get_jcomp(struct gspca_dev *gspca_dev,
++ struct v4l2_jpegcompression *jcomp)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ memset(jcomp, 0, sizeof *jcomp);
++ jcomp->quality = sd->quality;
++ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
++ | V4L2_JPEG_MARKER_DQT;
++ return 0;
++}
++
+ /* sub-driver description */
+ static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+@@ -1708,8 +2170,11 @@ static const struct sd_desc sd_desc = {
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
++ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .dq_callback = do_autogain,
++ .get_jcomp = sd_get_jcomp,
++ .set_jcomp = sd_set_jcomp,
+ };
+
+ /* -- module initialisation -- */
+@@ -1724,9 +2189,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
+ #endif
+ {USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)},
+ {USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)},
+-#if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
+ {USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)},
+-#endif
+ {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)},
+ {USB_DEVICE(0x0471, 0x0330), BSI(SN9C105, MI0360, 0x5d)},
+ {USB_DEVICE(0x06f8, 0x3004), BSI(SN9C105, OV7660, 0x21)},
+@@ -1764,10 +2227,10 @@ static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x21)},
+ #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
+ {USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)},
++#endif
+ {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)},
+ /* {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x??)}, */
+-#endif
+- {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, MI0360, 0x5d)},
++ {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)},
+ {}
+ };
+ MODULE_DEVICE_TABLE(usb, device_table);
+@@ -1794,8 +2257,10 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ info("registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c
+index 942f04c..6f38fa6 100644
+--- a/drivers/media/video/gspca/spca500.c
++++ b/drivers/media/video/gspca/spca500.c
+@@ -38,8 +38,11 @@ struct sd {
+ unsigned char brightness;
+ unsigned char contrast;
+ unsigned char colors;
++ u8 quality;
++#define QUALITY_MIN 70
++#define QUALITY_MAX 95
++#define QUALITY_DEF 85
+
+- char qindex;
+ char subtype;
+ #define AgfaCl20 0
+ #define AiptekPocketDV 1
+@@ -56,6 +59,8 @@ struct sd {
+ #define Optimedia 12
+ #define PalmPixDC85 13
+ #define ToptroIndus 14
++
++ u8 *jpeg_hdr;
+ };
+
+ /* V4L2 controls supported by the driver */
+@@ -629,7 +634,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = 0x01;
+ sd->subtype = id->driver_info;
+ if (sd->subtype != LogitechClickSmart310) {
+ cam->cam_mode = vga_mode;
+@@ -638,10 +642,10 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ cam->cam_mode = sif_mode;
+ cam->nmodes = ARRAY_SIZE(sif_mode);
+ }
+- sd->qindex = 5;
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->colors = COLOR_DEF;
++ sd->quality = QUALITY_DEF;
+ return 0;
+ }
+
+@@ -667,6 +671,12 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ __u8 Data;
+ __u8 xmult, ymult;
+
++ /* create the JPEG header */
++ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
++ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
++ 0x22); /* JPEG 411 */
++ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
++
+ if (sd->subtype == LogitechClickSmart310) {
+ xmult = 0x16;
+ ymult = 0x12;
+@@ -713,7 +723,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ write_vector(gspca_dev, spca500_visual_defaults);
+ spca500_setmode(gspca_dev, xmult, ymult);
+ /* enable drop packet */
+- reg_w(gspca_dev, 0x00, 0x850a, 0x0001);
++ err = reg_w(gspca_dev, 0x00, 0x850a, 0x0001);
++ if (err < 0)
+ PDEBUG(D_ERR, "failed to enable drop packet");
+ reg_w(gspca_dev, 0x00, 0x8880, 3);
+ err = spca50x_setup_qtable(gspca_dev,
+@@ -881,6 +892,13 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
+ gspca_dev->usb_buf[0]);
+ }
+
++static void sd_stop0(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ kfree(sd->jpeg_hdr);
++}
++
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ struct gspca_frame *frame, /* target */
+ __u8 *data, /* isoc packet */
+@@ -901,7 +919,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ ffd9, 2);
+
+ /* put the JPEG header in the new frame */
+- jpeg_put_header(gspca_dev, frame, sd->qindex, 0x22);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ sd->jpeg_hdr, JPEG_HDR_SZ);
+
+ data += SPCA500_OFFSET_DATA;
+ len -= SPCA500_OFFSET_DATA;
+@@ -937,16 +956,6 @@ static void setbrightness(struct gspca_dev *gspca_dev)
+ (__u8) (sd->brightness - 128));
+ }
+
+-static void getbrightness(struct gspca_dev *gspca_dev)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+- int ret;
+-
+- ret = reg_r_12(gspca_dev, 0x00, 0x8167, 1);
+- if (ret >= 0)
+- sd->brightness = ret + 128;
+-}
+-
+ static void setcontrast(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -954,16 +963,6 @@ static void setcontrast(struct gspca_dev *gspca_dev)
+ reg_w(gspca_dev, 0x00, 0x8168, sd->contrast);
+ }
+
+-static void getcontrast(struct gspca_dev *gspca_dev)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+- int ret;
+-
+- ret = reg_r_12(gspca_dev, 0x0, 0x8168, 1);
+- if (ret >= 0)
+- sd->contrast = ret;
+-}
+-
+ static void setcolors(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -971,16 +970,6 @@ static void setcolors(struct gspca_dev *gspca_dev)
+ reg_w(gspca_dev, 0x00, 0x8169, sd->colors);
+ }
+
+-static void getcolors(struct gspca_dev *gspca_dev)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+- int ret;
+-
+- ret = reg_r_12(gspca_dev, 0x0, 0x8169, 1);
+- if (ret >= 0)
+- sd->colors = ret;
+-}
+-
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -995,7 +984,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- getbrightness(gspca_dev);
+ *val = sd->brightness;
+ return 0;
+ }
+@@ -1014,7 +1002,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- getcontrast(gspca_dev);
+ *val = sd->contrast;
+ return 0;
+ }
+@@ -1033,11 +1020,38 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- getcolors(gspca_dev);
+ *val = sd->colors;
+ return 0;
+ }
+
++static int sd_set_jcomp(struct gspca_dev *gspca_dev,
++ struct v4l2_jpegcompression *jcomp)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (jcomp->quality < QUALITY_MIN)
++ sd->quality = QUALITY_MIN;
++ else if (jcomp->quality > QUALITY_MAX)
++ sd->quality = QUALITY_MAX;
++ else
++ sd->quality = jcomp->quality;
++ if (gspca_dev->streaming)
++ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
++ return 0;
++}
++
++static int sd_get_jcomp(struct gspca_dev *gspca_dev,
++ struct v4l2_jpegcompression *jcomp)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ memset(jcomp, 0, sizeof *jcomp);
++ jcomp->quality = sd->quality;
++ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
++ | V4L2_JPEG_MARKER_DQT;
++ return 0;
++}
++
+ /* sub-driver description */
+ static struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+@@ -1047,7 +1061,10 @@ static struct sd_desc sd_desc = {
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
++ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
++ .get_jcomp = sd_get_jcomp,
++ .set_jcomp = sd_set_jcomp,
+ };
+
+ /* -- module initialisation -- */
+@@ -1093,8 +1110,10 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c
+index 82e3e3e..d48b27c 100644
+--- a/drivers/media/video/gspca/spca501.c
++++ b/drivers/media/video/gspca/spca501.c
+@@ -1883,10 +1883,6 @@ static void setbrightness(struct gspca_dev *gspca_dev)
+ reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x12, sd->brightness);
+ }
+
+-static void getbrightness(struct gspca_dev *gspca_dev)
+-{
+-}
+-
+ static void setcontrast(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -1897,10 +1893,6 @@ static void setcontrast(struct gspca_dev *gspca_dev)
+ sd->contrast & 0xff);
+ }
+
+-static void getcontrast(struct gspca_dev *gspca_dev)
+-{
+-}
+-
+ static void setcolors(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -1908,10 +1900,6 @@ static void setcolors(struct gspca_dev *gspca_dev)
+ reg_write(gspca_dev->dev, SPCA501_REG_CCDSP, 0x0c, sd->colors);
+ }
+
+-static void getcolors(struct gspca_dev *gspca_dev)
+-{
+-}
+-
+ static void setblue_balance(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -1934,7 +1922,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = 0x01;
+ cam->cam_mode = vga_mode;
+ cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+ sd->subtype = id->driver_info;
+@@ -2084,7 +2071,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- getbrightness(gspca_dev);
+ *val = sd->brightness;
+ return 0;
+ }
+@@ -2103,7 +2089,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- getcontrast(gspca_dev);
+ *val = sd->contrast;
+ return 0;
+ }
+@@ -2122,7 +2107,6 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- getcolors(gspca_dev);
+ *val = sd->colors;
+ return 0;
+ }
+@@ -2211,8 +2195,10 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c
+index 2a33a29..2acec58 100644
+--- a/drivers/media/video/gspca/spca505.c
++++ b/drivers/media/video/gspca/spca505.c
+@@ -31,9 +31,9 @@ MODULE_LICENSE("GPL");
+ struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+- unsigned char brightness;
++ u8 brightness;
+
+- char subtype;
++ u8 subtype;
+ #define IntelPCCameraPro 0
+ #define Nxultra 1
+ };
+@@ -43,7 +43,6 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+
+ static struct ctrl sd_ctrls[] = {
+-#define SD_BRIGHTNESS 0
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+@@ -52,7 +51,8 @@ static struct ctrl sd_ctrls[] = {
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+- .default_value = 127,
++#define BRIGHTNESS_DEF 127
++ .default_value = BRIGHTNESS_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+@@ -64,12 +64,12 @@ static const struct v4l2_pix_format vga_mode[] = {
+ .bytesperline = 160,
+ .sizeimage = 160 * 120 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+- .priv = 5},
++ .priv = 4},
+ {176, 144, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+ .sizeimage = 176 * 144 * 3 / 2,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+- .priv = 4},
++ .priv = 3},
+ {320, 240, V4L2_PIX_FMT_SPCA505, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+ .sizeimage = 320 * 240 * 3 / 2,
+@@ -93,6 +93,7 @@ static const struct v4l2_pix_format vga_mode[] = {
+
+ #define SPCA50X_USB_CTRL 0x00 /* spca505 */
+ #define SPCA50X_CUSB_ENABLE 0x01 /* spca505 */
++
+ #define SPCA50X_REG_GLOBAL 0x03 /* spca505 */
+ #define SPCA50X_GMISC0_IDSEL 0x01 /* Global control device ID select spca505 */
+ #define SPCA50X_GLOBAL_MISC0 0x00 /* Global control miscellaneous 0 spca505 */
+@@ -101,230 +102,230 @@ static const struct v4l2_pix_format vga_mode[] = {
+ #define SPCA50X_GLOBAL_MISC3 0x03 /* 505 */
+ #define SPCA50X_GMISC3_SAA7113RST 0x20 /* Not sure about this one spca505 */
+
++/* Image format and compression control */
++#define SPCA50X_REG_COMPRESS 0x04
++
+ /*
+ * Data to initialize a SPCA505. Common to the CCD and external modes
+ */
+-static const __u16 spca505_init_data[][3] = {
+- /* line bmRequest,value,index */
+- /* 1819 */
++static const u8 spca505_init_data[][3] = {
++ /* bmRequest,value,index */
+ {SPCA50X_REG_GLOBAL, SPCA50X_GMISC3_SAA7113RST, SPCA50X_GLOBAL_MISC3},
+ /* Sensor reset */
+- /* 1822 */ {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC3},
+- /* 1825 */ {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC1},
++ {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC3},
++ {SPCA50X_REG_GLOBAL, 0x00, SPCA50X_GLOBAL_MISC1},
+ /* Block USB reset */
+- /* 1828 */ {SPCA50X_REG_GLOBAL, SPCA50X_GMISC0_IDSEL,
+- SPCA50X_GLOBAL_MISC0},
++ {SPCA50X_REG_GLOBAL, SPCA50X_GMISC0_IDSEL, SPCA50X_GLOBAL_MISC0},
+
+- /* 1831 */ {0x5, 0x01, 0x10},
++ {0x05, 0x01, 0x10},
+ /* Maybe power down some stuff */
+- /* 1834 */ {0x5, 0x0f, 0x11},
++ {0x05, 0x0f, 0x11},
+
+ /* Setup internal CCD ? */
+- /* 1837 */ {0x6, 0x10, 0x08},
+- /* 1840 */ {0x6, 0x00, 0x09},
+- /* 1843 */ {0x6, 0x00, 0x0a},
+- /* 1846 */ {0x6, 0x00, 0x0b},
+- /* 1849 */ {0x6, 0x10, 0x0c},
+- /* 1852 */ {0x6, 0x00, 0x0d},
+- /* 1855 */ {0x6, 0x00, 0x0e},
+- /* 1858 */ {0x6, 0x00, 0x0f},
+- /* 1861 */ {0x6, 0x10, 0x10},
+- /* 1864 */ {0x6, 0x02, 0x11},
+- /* 1867 */ {0x6, 0x00, 0x12},
+- /* 1870 */ {0x6, 0x04, 0x13},
+- /* 1873 */ {0x6, 0x02, 0x14},
+- /* 1876 */ {0x6, 0x8a, 0x51},
+- /* 1879 */ {0x6, 0x40, 0x52},
+- /* 1882 */ {0x6, 0xb6, 0x53},
+- /* 1885 */ {0x6, 0x3d, 0x54},
++ {0x06, 0x10, 0x08},
++ {0x06, 0x00, 0x09},
++ {0x06, 0x00, 0x0a},
++ {0x06, 0x00, 0x0b},
++ {0x06, 0x10, 0x0c},
++ {0x06, 0x00, 0x0d},
++ {0x06, 0x00, 0x0e},
++ {0x06, 0x00, 0x0f},
++ {0x06, 0x10, 0x10},
++ {0x06, 0x02, 0x11},
++ {0x06, 0x00, 0x12},
++ {0x06, 0x04, 0x13},
++ {0x06, 0x02, 0x14},
++ {0x06, 0x8a, 0x51},
++ {0x06, 0x40, 0x52},
++ {0x06, 0xb6, 0x53},
++ {0x06, 0x3d, 0x54},
+ {}
+ };
+
+ /*
+ * Data to initialize the camera using the internal CCD
+ */
+-static const __u16 spca505_open_data_ccd[][3] = {
+- /* line bmRequest,value,index */
++static const u8 spca505_open_data_ccd[][3] = {
++ /* bmRequest,value,index */
+ /* Internal CCD data set */
+- /* 1891 */ {0x3, 0x04, 0x01},
++ {0x03, 0x04, 0x01},
+ /* This could be a reset */
+- /* 1894 */ {0x3, 0x00, 0x01},
++ {0x03, 0x00, 0x01},
+
+ /* Setup compression and image registers. 0x6 and 0x7 seem to be
+ related to H&V hold, and are resolution mode specific */
+- /* 1897 */ {0x4, 0x10, 0x01},
++ {0x04, 0x10, 0x01},
+ /* DIFF(0x50), was (0x10) */
+- /* 1900 */ {0x4, 0x00, 0x04},
+- /* 1903 */ {0x4, 0x00, 0x05},
+- /* 1906 */ {0x4, 0x20, 0x06},
+- /* 1909 */ {0x4, 0x20, 0x07},
++ {0x04, 0x00, 0x04},
++ {0x04, 0x00, 0x05},
++ {0x04, 0x20, 0x06},
++ {0x04, 0x20, 0x07},
+
+- /* 1912 */ {0x8, 0x0a, 0x00},
++ {0x08, 0x0a, 0x00},
+ /* DIFF (0x4a), was (0xa) */
+
+- /* 1915 */ {0x5, 0x00, 0x10},
+- /* 1918 */ {0x5, 0x00, 0x11},
+- /* 1921 */ {0x5, 0x00, 0x00},
++ {0x05, 0x00, 0x10},
++ {0x05, 0x00, 0x11},
++ {0x05, 0x00, 0x00},
+ /* DIFF not written */
+- /* 1924 */ {0x5, 0x00, 0x01},
++ {0x05, 0x00, 0x01},
+ /* DIFF not written */
+- /* 1927 */ {0x5, 0x00, 0x02},
++ {0x05, 0x00, 0x02},
+ /* DIFF not written */
+- /* 1930 */ {0x5, 0x00, 0x03},
++ {0x05, 0x00, 0x03},
+ /* DIFF not written */
+- /* 1933 */ {0x5, 0x00, 0x04},
++ {0x05, 0x00, 0x04},
+ /* DIFF not written */
+- /* 1936 */ {0x5, 0x80, 0x05},
++ {0x05, 0x80, 0x05},
+ /* DIFF not written */
+- /* 1939 */ {0x5, 0xe0, 0x06},
++ {0x05, 0xe0, 0x06},
+ /* DIFF not written */
+- /* 1942 */ {0x5, 0x20, 0x07},
++ {0x05, 0x20, 0x07},
+ /* DIFF not written */
+- /* 1945 */ {0x5, 0xa0, 0x08},
++ {0x05, 0xa0, 0x08},
+ /* DIFF not written */
+- /* 1948 */ {0x5, 0x0, 0x12},
++ {0x05, 0x0, 0x12},
+ /* DIFF not written */
+- /* 1951 */ {0x5, 0x02, 0x0f},
++ {0x05, 0x02, 0x0f},
+ /* DIFF not written */
+- /* 1954 */ {0x5, 0x10, 0x46},
++ {0x05, 0x10, 0x46},
+ /* DIFF not written */
+- /* 1957 */ {0x5, 0x8, 0x4a},
++ {0x05, 0x8, 0x4a},
+ /* DIFF not written */
+
+- /* 1960 */ {0x3, 0x08, 0x03},
++ {0x03, 0x08, 0x03},
+ /* DIFF (0x3,0x28,0x3) */
+- /* 1963 */ {0x3, 0x08, 0x01},
+- /* 1966 */ {0x3, 0x0c, 0x03},
++ {0x03, 0x08, 0x01},
++ {0x03, 0x0c, 0x03},
+ /* DIFF not written */
+- /* 1969 */ {0x3, 0x21, 0x00},
++ {0x03, 0x21, 0x00},
+ /* DIFF (0x39) */
+
+ /* Extra block copied from init to hopefully ensure CCD is in a sane state */
+- /* 1837 */ {0x6, 0x10, 0x08},
+- /* 1840 */ {0x6, 0x00, 0x09},
+- /* 1843 */ {0x6, 0x00, 0x0a},
+- /* 1846 */ {0x6, 0x00, 0x0b},
+- /* 1849 */ {0x6, 0x10, 0x0c},
+- /* 1852 */ {0x6, 0x00, 0x0d},
+- /* 1855 */ {0x6, 0x00, 0x0e},
+- /* 1858 */ {0x6, 0x00, 0x0f},
+- /* 1861 */ {0x6, 0x10, 0x10},
+- /* 1864 */ {0x6, 0x02, 0x11},
+- /* 1867 */ {0x6, 0x00, 0x12},
+- /* 1870 */ {0x6, 0x04, 0x13},
+- /* 1873 */ {0x6, 0x02, 0x14},
+- /* 1876 */ {0x6, 0x8a, 0x51},
+- /* 1879 */ {0x6, 0x40, 0x52},
+- /* 1882 */ {0x6, 0xb6, 0x53},
+- /* 1885 */ {0x6, 0x3d, 0x54},
++ {0x06, 0x10, 0x08},
++ {0x06, 0x00, 0x09},
++ {0x06, 0x00, 0x0a},
++ {0x06, 0x00, 0x0b},
++ {0x06, 0x10, 0x0c},
++ {0x06, 0x00, 0x0d},
++ {0x06, 0x00, 0x0e},
++ {0x06, 0x00, 0x0f},
++ {0x06, 0x10, 0x10},
++ {0x06, 0x02, 0x11},
++ {0x06, 0x00, 0x12},
++ {0x06, 0x04, 0x13},
++ {0x06, 0x02, 0x14},
++ {0x06, 0x8a, 0x51},
++ {0x06, 0x40, 0x52},
++ {0x06, 0xb6, 0x53},
++ {0x06, 0x3d, 0x54},
+ /* End of extra block */
+
+- /* 1972 */ {0x6, 0x3f, 0x1},
++ {0x06, 0x3f, 0x1},
+ /* Block skipped */
+- /* 1975 */ {0x6, 0x10, 0x02},
+- /* 1978 */ {0x6, 0x64, 0x07},
+- /* 1981 */ {0x6, 0x10, 0x08},
+- /* 1984 */ {0x6, 0x00, 0x09},
+- /* 1987 */ {0x6, 0x00, 0x0a},
+- /* 1990 */ {0x6, 0x00, 0x0b},
+- /* 1993 */ {0x6, 0x10, 0x0c},
+- /* 1996 */ {0x6, 0x00, 0x0d},
+- /* 1999 */ {0x6, 0x00, 0x0e},
+- /* 2002 */ {0x6, 0x00, 0x0f},
+- /* 2005 */ {0x6, 0x10, 0x10},
+- /* 2008 */ {0x6, 0x02, 0x11},
+- /* 2011 */ {0x6, 0x00, 0x12},
+- /* 2014 */ {0x6, 0x04, 0x13},
+- /* 2017 */ {0x6, 0x02, 0x14},
+- /* 2020 */ {0x6, 0x8a, 0x51},
+- /* 2023 */ {0x6, 0x40, 0x52},
+- /* 2026 */ {0x6, 0xb6, 0x53},
+- /* 2029 */ {0x6, 0x3d, 0x54},
+- /* 2032 */ {0x6, 0x60, 0x57},
+- /* 2035 */ {0x6, 0x20, 0x58},
+- /* 2038 */ {0x6, 0x15, 0x59},
+- /* 2041 */ {0x6, 0x05, 0x5a},
+-
+- /* 2044 */ {0x5, 0x01, 0xc0},
+- /* 2047 */ {0x5, 0x10, 0xcb},
+- /* 2050 */ {0x5, 0x80, 0xc1},
++ {0x06, 0x10, 0x02},
++ {0x06, 0x64, 0x07},
++ {0x06, 0x10, 0x08},
++ {0x06, 0x00, 0x09},
++ {0x06, 0x00, 0x0a},
++ {0x06, 0x00, 0x0b},
++ {0x06, 0x10, 0x0c},
++ {0x06, 0x00, 0x0d},
++ {0x06, 0x00, 0x0e},
++ {0x06, 0x00, 0x0f},
++ {0x06, 0x10, 0x10},
++ {0x06, 0x02, 0x11},
++ {0x06, 0x00, 0x12},
++ {0x06, 0x04, 0x13},
++ {0x06, 0x02, 0x14},
++ {0x06, 0x8a, 0x51},
++ {0x06, 0x40, 0x52},
++ {0x06, 0xb6, 0x53},
++ {0x06, 0x3d, 0x54},
++ {0x06, 0x60, 0x57},
++ {0x06, 0x20, 0x58},
++ {0x06, 0x15, 0x59},
++ {0x06, 0x05, 0x5a},
++
++ {0x05, 0x01, 0xc0},
++ {0x05, 0x10, 0xcb},
++ {0x05, 0x80, 0xc1},
+ /* */
+- /* 2053 */ {0x5, 0x0, 0xc2},
++ {0x05, 0x0, 0xc2},
+ /* 4 was 0 */
+- /* 2056 */ {0x5, 0x00, 0xca},
+- /* 2059 */ {0x5, 0x80, 0xc1},
++ {0x05, 0x00, 0xca},
++ {0x05, 0x80, 0xc1},
+ /* */
+- /* 2062 */ {0x5, 0x04, 0xc2},
+- /* 2065 */ {0x5, 0x00, 0xca},
+- /* 2068 */ {0x5, 0x0, 0xc1},
++ {0x05, 0x04, 0xc2},
++ {0x05, 0x00, 0xca},
++ {0x05, 0x0, 0xc1},
+ /* */
+- /* 2071 */ {0x5, 0x00, 0xc2},
+- /* 2074 */ {0x5, 0x00, 0xca},
+- /* 2077 */ {0x5, 0x40, 0xc1},
++ {0x05, 0x00, 0xc2},
++ {0x05, 0x00, 0xca},
++ {0x05, 0x40, 0xc1},
+ /* */
+- /* 2080 */ {0x5, 0x17, 0xc2},
+- /* 2083 */ {0x5, 0x00, 0xca},
+- /* 2086 */ {0x5, 0x80, 0xc1},
++ {0x05, 0x17, 0xc2},
++ {0x05, 0x00, 0xca},
++ {0x05, 0x80, 0xc1},
+ /* */
+- /* 2089 */ {0x5, 0x06, 0xc2},
+- /* 2092 */ {0x5, 0x00, 0xca},
+- /* 2095 */ {0x5, 0x80, 0xc1},
++ {0x05, 0x06, 0xc2},
++ {0x05, 0x00, 0xca},
++ {0x05, 0x80, 0xc1},
+ /* */
+- /* 2098 */ {0x5, 0x04, 0xc2},
+- /* 2101 */ {0x5, 0x00, 0xca},
++ {0x05, 0x04, 0xc2},
++ {0x05, 0x00, 0xca},
+
+- /* 2104 */ {0x3, 0x4c, 0x3},
+- /* 2107 */ {0x3, 0x18, 0x1},
++ {0x03, 0x4c, 0x3},
++ {0x03, 0x18, 0x1},
+
+- /* 2110 */ {0x6, 0x70, 0x51},
+- /* 2113 */ {0x6, 0xbe, 0x53},
+- /* 2116 */ {0x6, 0x71, 0x57},
+- /* 2119 */ {0x6, 0x20, 0x58},
+- /* 2122 */ {0x6, 0x05, 0x59},
+- /* 2125 */ {0x6, 0x15, 0x5a},
++ {0x06, 0x70, 0x51},
++ {0x06, 0xbe, 0x53},
++ {0x06, 0x71, 0x57},
++ {0x06, 0x20, 0x58},
++ {0x06, 0x05, 0x59},
++ {0x06, 0x15, 0x5a},
+
+- /* 2128 */ {0x4, 0x00, 0x08},
++ {0x04, 0x00, 0x08},
+ /* Compress = OFF (0x1 to turn on) */
+- /* 2131 */ {0x4, 0x12, 0x09},
+- /* 2134 */ {0x4, 0x21, 0x0a},
+- /* 2137 */ {0x4, 0x10, 0x0b},
+- /* 2140 */ {0x4, 0x21, 0x0c},
+- /* 2143 */ {0x4, 0x05, 0x00},
++ {0x04, 0x12, 0x09},
++ {0x04, 0x21, 0x0a},
++ {0x04, 0x10, 0x0b},
++ {0x04, 0x21, 0x0c},
++ {0x04, 0x05, 0x00},
+ /* was 5 (Image Type ? ) */
+- /* 2146 */ {0x4, 0x00, 0x01},
+-
+- /* 2149 */ {0x6, 0x3f, 0x01},
+-
+- /* 2152 */ {0x4, 0x00, 0x04},
+- /* 2155 */ {0x4, 0x00, 0x05},
+- /* 2158 */ {0x4, 0x40, 0x06},
+- /* 2161 */ {0x4, 0x40, 0x07},
+-
+- /* 2164 */ {0x6, 0x1c, 0x17},
+- /* 2167 */ {0x6, 0xe2, 0x19},
+- /* 2170 */ {0x6, 0x1c, 0x1b},
+- /* 2173 */ {0x6, 0xe2, 0x1d},
+- /* 2176 */ {0x6, 0xaa, 0x1f},
+- /* 2179 */ {0x6, 0x70, 0x20},
+-
+- /* 2182 */ {0x5, 0x01, 0x10},
+- /* 2185 */ {0x5, 0x00, 0x11},
+- /* 2188 */ {0x5, 0x01, 0x00},
+- /* 2191 */ {0x5, 0x05, 0x01},
+- /* 2194 */ {0x5, 0x00, 0xc1},
++ {0x04, 0x00, 0x01},
++
++ {0x06, 0x3f, 0x01},
++
++ {0x04, 0x00, 0x04},
++ {0x04, 0x00, 0x05},
++ {0x04, 0x40, 0x06},
++ {0x04, 0x40, 0x07},
++
++ {0x06, 0x1c, 0x17},
++ {0x06, 0xe2, 0x19},
++ {0x06, 0x1c, 0x1b},
++ {0x06, 0xe2, 0x1d},
++ {0x06, 0xaa, 0x1f},
++ {0x06, 0x70, 0x20},
++
++ {0x05, 0x01, 0x10},
++ {0x05, 0x00, 0x11},
++ {0x05, 0x01, 0x00},
++ {0x05, 0x05, 0x01},
++ {0x05, 0x00, 0xc1},
+ /* */
+- /* 2197 */ {0x5, 0x00, 0xc2},
+- /* 2200 */ {0x5, 0x00, 0xca},
++ {0x05, 0x00, 0xc2},
++ {0x05, 0x00, 0xca},
+
+- /* 2203 */ {0x6, 0x70, 0x51},
+- /* 2206 */ {0x6, 0xbe, 0x53},
++ {0x06, 0x70, 0x51},
++ {0x06, 0xbe, 0x53},
+ {}
+ };
+
+ /*
+- Made by Tomasz Zablocki (skalamandra@poczta.onet.pl)
++ * Made by Tomasz Zablocki (skalamandra@poczta.onet.pl)
+ * SPCA505b chip based cameras initialization data
+- *
+ */
+ /* jfm */
+ #define initial_brightness 0x7f /* 0x0(white)-0xff(black) */
+@@ -332,7 +333,7 @@ static const __u16 spca505_open_data_ccd[][3] = {
+ /*
+ * Data to initialize a SPCA505. Common to the CCD and external modes
+ */
+-static const __u16 spca505b_init_data[][3] = {
++static const u8 spca505b_init_data[][3] = {
+ /* start */
+ {0x02, 0x00, 0x00}, /* init */
+ {0x02, 0x00, 0x01},
+@@ -396,7 +397,7 @@ static const __u16 spca505b_init_data[][3] = {
+ /*
+ * Data to initialize the camera using the internal CCD
+ */
+-static const __u16 spca505b_open_data_ccd[][3] = {
++static const u8 spca505b_open_data_ccd[][3] = {
+
+ /* {0x02,0x00,0x00}, */
+ {0x03, 0x04, 0x01}, /* rst */
+@@ -426,7 +427,7 @@ static const __u16 spca505b_open_data_ccd[][3] = {
+ {0x05, 0x00, 0x12},
+ {0x05, 0x6f, 0x00},
+ {0x05, initial_brightness >> 6, 0x00},
+- {0x05, initial_brightness << 2, 0x01},
++ {0x05, (initial_brightness << 2) & 0xff, 0x01},
+ {0x05, 0x00, 0x02},
+ {0x05, 0x01, 0x03},
+ {0x05, 0x00, 0x04},
+@@ -436,7 +437,7 @@ static const __u16 spca505b_open_data_ccd[][3] = {
+ {0x05, 0xa0, 0x08},
+ {0x05, 0x00, 0x12},
+ {0x05, 0x02, 0x0f},
+- {0x05, 128, 0x14}, /* max exposure off (0=on) */
++ {0x05, 0x80, 0x14}, /* max exposure off (0=on) */
+ {0x05, 0x01, 0xb0},
+ {0x05, 0x01, 0xbf},
+ {0x03, 0x02, 0x06},
+@@ -560,26 +561,26 @@ static const __u16 spca505b_open_data_ccd[][3] = {
+ {0x06, 0x32, 0x20},
+
+ {0x05, initial_brightness >> 6, 0x00},
+- {0x05, initial_brightness << 2, 0x01},
++ {0x05, (initial_brightness << 2) & 0xff, 0x01},
+ {0x05, 0x06, 0xc1},
+ {0x05, 0x58, 0xc2},
+- {0x05, 0x0, 0xca},
+- {0x05, 0x0, 0x11},
++ {0x05, 0x00, 0xca},
++ {0x05, 0x00, 0x11},
+ {}
+ };
+
+ static int reg_write(struct usb_device *dev,
+- __u16 reg, __u16 index, __u16 value)
++ u16 req, u16 index, u16 value)
+ {
+ int ret;
+
+ ret = usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
+- reg,
++ req,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 500);
+- PDEBUG(D_PACK, "reg write: 0x%02x,0x%02x:0x%02x, 0x%x",
+- reg, index, value, ret);
++ PDEBUG(D_USBO, "reg write: 0x%02x,0x%02x:0x%02x, %d",
++ req, index, value, ret);
+ if (ret < 0)
+ PDEBUG(D_ERR, "reg write: error %d", ret);
+ return ret;
+@@ -587,42 +588,34 @@ static int reg_write(struct usb_device *dev,
+
+ /* returns: negative is error, pos or zero is data */
+ static int reg_read(struct gspca_dev *gspca_dev,
+- __u16 reg, /* bRequest */
+- __u16 index, /* wIndex */
+- __u16 length) /* wLength (1 or 2 only) */
++ u16 req, /* bRequest */
++ u16 index) /* wIndex */
+ {
+ int ret;
+
+- gspca_dev->usb_buf[1] = 0;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+- reg,
++ req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+- (__u16) 0, /* value */
+- (__u16) index,
+- gspca_dev->usb_buf, length,
++ 0, /* value */
++ index,
++ gspca_dev->usb_buf, 2,
+ 500); /* timeout */
+- if (ret < 0) {
+- PDEBUG(D_ERR, "reg_read err %d", ret);
+- return -1;
+- }
++ if (ret < 0)
++ return ret;
+ return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0];
+ }
+
+ static int write_vector(struct gspca_dev *gspca_dev,
+- const __u16 data[][3])
++ const u8 data[][3])
+ {
+ struct usb_device *dev = gspca_dev->dev;
+ int ret, i = 0;
+
+- while (data[i][0] != 0 || data[i][1] != 0 || data[i][2] != 0) {
++ while (data[i][0] != 0) {
+ ret = reg_write(dev, data[i][0], data[i][2], data[i][1]);
+- if (ret < 0) {
+- PDEBUG(D_ERR,
+- "Register write failed for 0x%x,0x%x,0x%x",
+- data[i][0], data[i][1], data[i][2]);
++ if (ret < 0)
+ return ret;
+- }
+ i++;
+ }
+ return 0;
+@@ -636,14 +629,13 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = 0x01;
+ cam->cam_mode = vga_mode;
+ sd->subtype = id->driver_info;
+ if (sd->subtype != IntelPCCameraPro)
+- cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
++ cam->nmodes = ARRAY_SIZE(vga_mode);
+ else /* no 640x480 for IntelPCCameraPro */
+- cam->nmodes = sizeof vga_mode / sizeof vga_mode[0] - 1;
+- sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
++ cam->nmodes = ARRAY_SIZE(vga_mode) - 1;
++ sd->brightness = BRIGHTNESS_DEF;
+
+ if (sd->subtype == Nxultra) {
+ if (write_vector(gspca_dev, spca505b_init_data))
+@@ -658,81 +650,71 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ /* this function is called at probe and resume time */
+ static int sd_init(struct gspca_dev *gspca_dev)
+ {
++ return 0;
++}
++
++static void setbrightness(struct gspca_dev *gspca_dev)
++{
+ struct sd *sd = (struct sd *) gspca_dev;
+- int ret;
++ u8 brightness = sd->brightness;
++
++ reg_write(gspca_dev->dev, 0x05, 0x00, (255 - brightness) >> 6);
++ reg_write(gspca_dev->dev, 0x05, 0x01, (255 - brightness) << 2);
++}
++
++static int sd_start(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ struct usb_device *dev = gspca_dev->dev;
++ int ret, mode;
++ static u8 mode_tb[][3] = {
++ /* r00 r06 r07 */
++ {0x00, 0x10, 0x10}, /* 640x480 */
++ {0x01, 0x1a, 0x1a}, /* 352x288 */
++ {0x02, 0x1c, 0x1d}, /* 320x240 */
++ {0x04, 0x34, 0x34}, /* 176x144 */
++ {0x05, 0x40, 0x40} /* 160x120 */
++ };
+
+- PDEBUG(D_STREAM, "Initializing SPCA505");
+ if (sd->subtype == Nxultra)
+ write_vector(gspca_dev, spca505b_open_data_ccd);
+ else
+ write_vector(gspca_dev, spca505_open_data_ccd);
+- ret = reg_read(gspca_dev, 6, 0x16, 2);
++ ret = reg_read(gspca_dev, 0x06, 0x16);
+
+ if (ret < 0) {
+- PDEBUG(D_ERR|D_STREAM,
+- "register read failed for after vector read err = %d",
++ PDEBUG(D_ERR|D_CONF,
++ "register read failed err: %d",
+ ret);
+- return -EIO;
++ return ret;
+ }
+- PDEBUG(D_STREAM,
+- "After vector read returns : 0x%x should be 0x0101",
+- ret & 0xffff);
+-
+- ret = reg_write(gspca_dev->dev, 6, 0x16, 0x0a);
+- if (ret < 0) {
+- PDEBUG(D_ERR, "register write failed for (6,0xa,0x16) err=%d",
+- ret);
+- return -EIO;
++ if (ret != 0x0101) {
++ PDEBUG(D_ERR|D_CONF,
++ "After vector read returns 0x%04x should be 0x0101",
++ ret);
+ }
+- reg_write(gspca_dev->dev, 5, 0xc2, 18);
+- return 0;
+-}
+
+-static int sd_start(struct gspca_dev *gspca_dev)
+-{
+- struct usb_device *dev = gspca_dev->dev;
+- int ret;
++ ret = reg_write(gspca_dev->dev, 0x06, 0x16, 0x0a);
++ if (ret < 0)
++ return ret;
++ reg_write(gspca_dev->dev, 0x05, 0xc2, 0x12);
+
+ /* necessary because without it we can see stream
+ * only once after loading module */
+ /* stopping usb registers Tomasz change */
+- reg_write(dev, 0x02, 0x0, 0x0);
+- switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+- case 0:
+- reg_write(dev, 0x04, 0x00, 0x00);
+- reg_write(dev, 0x04, 0x06, 0x10);
+- reg_write(dev, 0x04, 0x07, 0x10);
+- break;
+- case 1:
+- reg_write(dev, 0x04, 0x00, 0x01);
+- reg_write(dev, 0x04, 0x06, 0x1a);
+- reg_write(dev, 0x04, 0x07, 0x1a);
+- break;
+- case 2:
+- reg_write(dev, 0x04, 0x00, 0x02);
+- reg_write(dev, 0x04, 0x06, 0x1c);
+- reg_write(dev, 0x04, 0x07, 0x1d);
+- break;
+- case 4:
+- reg_write(dev, 0x04, 0x00, 0x04);
+- reg_write(dev, 0x04, 0x06, 0x34);
+- reg_write(dev, 0x04, 0x07, 0x34);
+- break;
+- default:
+-/* case 5: */
+- reg_write(dev, 0x04, 0x00, 0x05);
+- reg_write(dev, 0x04, 0x06, 0x40);
+- reg_write(dev, 0x04, 0x07, 0x40);
+- break;
+- }
+-/* Enable ISO packet machine - should we do this here or in ISOC init ? */
++ reg_write(dev, 0x02, 0x00, 0x00);
++
++ mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
++ reg_write(dev, SPCA50X_REG_COMPRESS, 0x00, mode_tb[mode][0]);
++ reg_write(dev, SPCA50X_REG_COMPRESS, 0x06, mode_tb[mode][1]);
++ reg_write(dev, SPCA50X_REG_COMPRESS, 0x07, mode_tb[mode][2]);
++
+ ret = reg_write(dev, SPCA50X_REG_USB,
+ SPCA50X_USB_CTRL,
+ SPCA50X_CUSB_ENABLE);
+
+-/* reg_write(dev, 0x5, 0x0, 0x0); */
+-/* reg_write(dev, 0x5, 0x0, 0x1); */
+-/* reg_write(dev, 0x5, 0x11, 0x2); */
++ setbrightness(gspca_dev);
++
+ return ret;
+ }
+
+@@ -750,15 +732,15 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
+
+ /* This maybe reset or power control */
+ reg_write(gspca_dev->dev, 0x03, 0x03, 0x20);
+- reg_write(gspca_dev->dev, 0x03, 0x01, 0x0);
+- reg_write(gspca_dev->dev, 0x03, 0x00, 0x1);
+- reg_write(gspca_dev->dev, 0x05, 0x10, 0x1);
+- reg_write(gspca_dev->dev, 0x05, 0x11, 0xf);
++ reg_write(gspca_dev->dev, 0x03, 0x01, 0x00);
++ reg_write(gspca_dev->dev, 0x03, 0x00, 0x01);
++ reg_write(gspca_dev->dev, 0x05, 0x10, 0x01);
++ reg_write(gspca_dev->dev, 0x05, 0x11, 0x0f);
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ struct gspca_frame *frame, /* target */
+- __u8 *data, /* isoc packet */
++ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ switch (data[0]) {
+@@ -771,7 +753,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ data, len);
+ break;
+ case 0xff: /* drop */
+-/* gspca_dev->last_packet_type = DISCARD_PACKET; */
+ break;
+ default:
+ data += 1;
+@@ -782,24 +763,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ }
+ }
+
+-static void setbrightness(struct gspca_dev *gspca_dev)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- __u8 brightness = sd->brightness;
+- reg_write(gspca_dev->dev, 5, 0x00, (255 - brightness) >> 6);
+- reg_write(gspca_dev->dev, 5, 0x01, (255 - brightness) << 2);
+-
+-}
+-static void getbrightness(struct gspca_dev *gspca_dev)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- sd->brightness = 255
+- - ((reg_read(gspca_dev, 5, 0x01, 1) >> 2)
+- + (reg_read(gspca_dev, 5, 0x0, 1) << 6));
+-}
+-
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -814,7 +777,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- getbrightness(gspca_dev);
+ *val = sd->brightness;
+ return 0;
+ }
+@@ -863,8 +825,11 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c
+index 96e2512..3a0c893 100644
+--- a/drivers/media/video/gspca/spca506.c
++++ b/drivers/media/video/gspca/spca506.c
+@@ -193,24 +193,6 @@ static void spca506_WriteI2c(struct gspca_dev *gspca_dev, __u16 valeur,
+ }
+ }
+
+-static int spca506_ReadI2c(struct gspca_dev *gspca_dev, __u16 reg)
+-{
+- int retry = 60;
+-
+- reg_w(gspca_dev->dev, 0x07, SAA7113_I2C_BASE_WRITE, 0x0004);
+- reg_w(gspca_dev->dev, 0x07, reg, 0x0001);
+- reg_w(gspca_dev->dev, 0x07, 0x01, 0x0002);
+- while (--retry) {
+- reg_r(gspca_dev, 0x07, 0x0003, 2);
+- if ((gspca_dev->usb_buf[0] | gspca_dev->usb_buf[1]) == 0x00)
+- break;
+- }
+- if (retry == 0)
+- return -1;
+- reg_r(gspca_dev, 0x07, 0x0000, 1);
+- return gspca_dev->usb_buf[0];
+-}
+-
+ static void spca506_SetNormeInput(struct gspca_dev *gspca_dev,
+ __u16 norme,
+ __u16 channel)
+@@ -303,7 +285,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = 0x01;
+ cam->cam_mode = vga_mode;
+ cam->nmodes = sizeof vga_mode / sizeof vga_mode[0];
+ sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
+@@ -596,13 +577,6 @@ static void setbrightness(struct gspca_dev *gspca_dev)
+ spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+ }
+
+-static void getbrightness(struct gspca_dev *gspca_dev)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- sd->brightness = spca506_ReadI2c(gspca_dev, SAA7113_bright);
+-}
+-
+ static void setcontrast(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -612,13 +586,6 @@ static void setcontrast(struct gspca_dev *gspca_dev)
+ spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+ }
+
+-static void getcontrast(struct gspca_dev *gspca_dev)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- sd->contrast = spca506_ReadI2c(gspca_dev, SAA7113_contrast);
+-}
+-
+ static void setcolors(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -628,13 +595,6 @@ static void setcolors(struct gspca_dev *gspca_dev)
+ spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+ }
+
+-static void getcolors(struct gspca_dev *gspca_dev)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- sd->colors = spca506_ReadI2c(gspca_dev, SAA7113_saturation);
+-}
+-
+ static void sethue(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -644,13 +604,6 @@ static void sethue(struct gspca_dev *gspca_dev)
+ spca506_WriteI2c(gspca_dev, 0x01, 0x09);
+ }
+
+-static void gethue(struct gspca_dev *gspca_dev)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- sd->hue = spca506_ReadI2c(gspca_dev, SAA7113_hue);
+-}
+-
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -665,7 +618,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- getbrightness(gspca_dev);
+ *val = sd->brightness;
+ return 0;
+ }
+@@ -684,7 +636,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- getcontrast(gspca_dev);
+ *val = sd->contrast;
+ return 0;
+ }
+@@ -703,7 +654,6 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- getcolors(gspca_dev);
+ *val = sd->colors;
+ return 0;
+ }
+@@ -722,7 +672,6 @@ static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- gethue(gspca_dev);
+ *val = sd->hue;
+ return 0;
+ }
+@@ -772,8 +721,10 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c
+index be5d740..adacf84 100644
+--- a/drivers/media/video/gspca/spca508.c
++++ b/drivers/media/video/gspca/spca508.c
+@@ -101,8 +101,7 @@ static const struct v4l2_pix_format sif_mode[] = {
+ * Initialization data: this is the first set-up data written to the
+ * device (before the open data).
+ */
+-static const __u16 spca508_init_data[][3] =
+-#define IGN(x) /* nothing */
++static const u16 spca508_init_data[][2] =
+ {
+ /* line URB value, index */
+ /* 44274 1804 */ {0x0000, 0x870b},
+@@ -589,11 +588,10 @@ static const __u16 spca508_init_data[][3] =
+ {}
+ };
+
+-
+ /*
+ * Initialization data for Intel EasyPC Camera CS110
+ */
+-static const __u16 spca508cs110_init_data[][3] = {
++static const u16 spca508cs110_init_data[][2] = {
+ {0x0000, 0x870b}, /* Reset CTL3 */
+ {0x0003, 0x8111}, /* Soft Reset compression, memory, TG & CDSP */
+ {0x0000, 0x8111}, /* Normal operation on reset */
+@@ -677,7 +675,7 @@ static const __u16 spca508cs110_init_data[][3] = {
+ {}
+ };
+
+-static const __u16 spca508_sightcam_init_data[][3] = {
++static const u16 spca508_sightcam_init_data[][2] = {
+ /* This line seems to setup the frame/canvas */
+ /*368 */ {0x000f, 0x8402},
+
+@@ -760,7 +758,7 @@ static const __u16 spca508_sightcam_init_data[][3] = {
+ {}
+ };
+
+-static const __u16 spca508_sightcam2_init_data[][3] = {
++static const u16 spca508_sightcam2_init_data[][2] = {
+ /* 35 */ {0x0020, 0x8112},
+
+ /* 36 */ {0x000f, 0x8402},
+@@ -1107,7 +1105,7 @@ static const __u16 spca508_sightcam2_init_data[][3] = {
+ /*
+ * Initialization data for Creative Webcam Vista
+ */
+-static const __u16 spca508_vista_init_data[][3] = {
++static const u16 spca508_vista_init_data[][2] = {
+ {0x0008, 0x8200}, /* Clear register */
+ {0x0000, 0x870b}, /* Reset CTL3 */
+ {0x0020, 0x8112}, /* Video Drop packet enable */
+@@ -1309,18 +1307,18 @@ static const __u16 spca508_vista_init_data[][3] = {
+
+ {0x0050, 0x8703},
+ {0x0002, 0x8704}, /* External input CKIx1 */
+- {0x0001, 0x870C}, /* Select CKOx2 output */
+- {0x009A, 0x8600}, /* Line memory Read Counter (L) */
++ {0x0001, 0x870c}, /* Select CKOx2 output */
++ {0x009a, 0x8600}, /* Line memory Read Counter (L) */
+ {0x0001, 0x8606}, /* 1 Line memory Read Counter (H) Result: (d)410 */
+ {0x0023, 0x8601},
+ {0x0010, 0x8602},
+- {0x000A, 0x8603},
++ {0x000a, 0x8603},
+ {0x009A, 0x8600},
+- {0x0001, 0x865B}, /* 1 Horizontal Offset for Valid Pixel(L) */
+- {0x0003, 0x865C}, /* Vertical offset for valid lines (L) */
+- {0x0058, 0x865D}, /* Horizontal valid pixels window (L) */
+- {0x0048, 0x865E}, /* Vertical valid lines window (L) */
+- {0x0000, 0x865F},
++ {0x0001, 0x865b}, /* 1 Horizontal Offset for Valid Pixel(L) */
++ {0x0003, 0x865c}, /* Vertical offset for valid lines (L) */
++ {0x0058, 0x865d}, /* Horizontal valid pixels window (L) */
++ {0x0048, 0x865e}, /* Vertical valid lines window (L) */
++ {0x0000, 0x865f},
+
+ {0x0006, 0x8660},
+ /* Enable nibble data input, select nibble input order */
+@@ -1328,63 +1326,63 @@ static const __u16 spca508_vista_init_data[][3] = {
+ {0x0013, 0x8608}, /* A11 Coeficients for color correction */
+ {0x0028, 0x8609},
+ /* Note: these values are confirmed at the end of array */
+- {0x0005, 0x860A}, /* ... */
+- {0x0025, 0x860B},
+- {0x00E1, 0x860C},
+- {0x00FA, 0x860D},
+- {0x00F4, 0x860E},
+- {0x00E8, 0x860F},
++ {0x0005, 0x860a}, /* ... */
++ {0x0025, 0x860b},
++ {0x00e1, 0x860c},
++ {0x00fa, 0x860D},
++ {0x00f4, 0x860e},
++ {0x00e8, 0x860f},
+ {0x0025, 0x8610}, /* A33 Coef. */
+- {0x00FC, 0x8611}, /* White balance offset: R */
++ {0x00fc, 0x8611}, /* White balance offset: R */
+ {0x0001, 0x8612}, /* White balance offset: Gr */
+- {0x00FE, 0x8613}, /* White balance offset: B */
++ {0x00fe, 0x8613}, /* White balance offset: B */
+ {0x0000, 0x8614}, /* White balance offset: Gb */
+
+ {0x0064, 0x8651}, /* R gain for white balance (L) */
+ {0x0040, 0x8652}, /* Gr gain for white balance (L) */
+ {0x0066, 0x8653}, /* B gain for white balance (L) */
+ {0x0040, 0x8654}, /* Gb gain for white balance (L) */
+- {0x0001, 0x863F}, /* Enable fixed gamma correction */
++ {0x0001, 0x863f}, /* Enable fixed gamma correction */
+
+- {0x00A1, 0x8656}, /* Size - Window1: 256x256, Window2: 128x128 */
++ {0x00a1, 0x8656}, /* Size - Window1: 256x256, Window2: 128x128 */
+ /* UV division: UV no change, Enable New edge enhancement */
+ {0x0018, 0x8657}, /* Edge gain high threshold */
+ {0x0020, 0x8658}, /* Edge gain low threshold */
+ {0x000A, 0x8659}, /* Edge bandwidth high threshold */
+- {0x0005, 0x865A}, /* Edge bandwidth low threshold */
++ {0x0005, 0x865a}, /* Edge bandwidth low threshold */
+ {0x0064, 0x8607}, /* UV filter enable */
+
+ {0x0016, 0x8660},
+- {0x0000, 0x86B0}, /* Bad pixels compensation address */
+- {0x00DC, 0x86B1}, /* X coord for bad pixels compensation (L) */
+- {0x0000, 0x86B2},
+- {0x0009, 0x86B3}, /* Y coord for bad pixels compensation (L) */
+- {0x0000, 0x86B4},
+-
+- {0x0001, 0x86B0},
+- {0x00F5, 0x86B1},
+- {0x0000, 0x86B2},
+- {0x00C6, 0x86B3},
+- {0x0000, 0x86B4},
+-
+- {0x0002, 0x86B0},
+- {0x001C, 0x86B1},
+- {0x0001, 0x86B2},
+- {0x00D7, 0x86B3},
+- {0x0000, 0x86B4},
+-
+- {0x0003, 0x86B0},
+- {0x001C, 0x86B1},
+- {0x0001, 0x86B2},
+- {0x00D8, 0x86B3},
+- {0x0000, 0x86B4},
+-
+- {0x0004, 0x86B0},
+- {0x001D, 0x86B1},
+- {0x0001, 0x86B2},
+- {0x00D8, 0x86B3},
+- {0x0000, 0x86B4},
+- {0x001E, 0x8660},
++ {0x0000, 0x86b0}, /* Bad pixels compensation address */
++ {0x00dc, 0x86b1}, /* X coord for bad pixels compensation (L) */
++ {0x0000, 0x86b2},
++ {0x0009, 0x86b3}, /* Y coord for bad pixels compensation (L) */
++ {0x0000, 0x86b4},
++
++ {0x0001, 0x86b0},
++ {0x00f5, 0x86b1},
++ {0x0000, 0x86b2},
++ {0x00c6, 0x86b3},
++ {0x0000, 0x86b4},
++
++ {0x0002, 0x86b0},
++ {0x001c, 0x86b1},
++ {0x0001, 0x86b2},
++ {0x00d7, 0x86b3},
++ {0x0000, 0x86b4},
++
++ {0x0003, 0x86b0},
++ {0x001c, 0x86b1},
++ {0x0001, 0x86b2},
++ {0x00d8, 0x86b3},
++ {0x0000, 0x86b4},
++
++ {0x0004, 0x86b0},
++ {0x001d, 0x86b1},
++ {0x0001, 0x86b2},
++ {0x00d8, 0x86b3},
++ {0x0000, 0x86b4},
++ {0x001e, 0x8660},
+
+ /* READ { 0, 0x0000, 0x8608 } ->
+ 0000: 13 */
+@@ -1449,7 +1447,7 @@ static int reg_read(struct gspca_dev *gspca_dev,
+ }
+
+ static int write_vector(struct gspca_dev *gspca_dev,
+- const __u16 data[][3])
++ const u16 data[][2])
+ {
+ struct usb_device *dev = gspca_dev->dev;
+ int ret, i = 0;
+@@ -1487,7 +1485,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ PDEBUG(D_PROBE, "Window 1 average luminance: %d", data1);
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = 0x01;
+ cam->cam_mode = sif_mode;
+ cam->nmodes = ARRAY_SIZE(sif_mode);
+
+@@ -1593,13 +1590,6 @@ static void setbrightness(struct gspca_dev *gspca_dev)
+ reg_write(gspca_dev->dev, 0x8654, brightness);
+ }
+
+-static void getbrightness(struct gspca_dev *gspca_dev)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- sd->brightness = reg_read(gspca_dev, 0x8651);
+-}
+-
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -1614,7 +1604,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- getbrightness(gspca_dev);
+ *val = sd->brightness;
+ return 0;
+ }
+@@ -1666,8 +1655,11 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c
+index 3c92880..c99c5e3 100644
+--- a/drivers/media/video/gspca/spca561.c
++++ b/drivers/media/video/gspca/spca561.c
+@@ -141,38 +141,38 @@ static const struct v4l2_pix_format sif_072a_mode[] = {
+ #define SPCA561_OFFSET_WIN1GBAVE 14
+ #define SPCA561_OFFSET_FREQ 15
+ #define SPCA561_OFFSET_VSYNC 16
+-#define SPCA561_OFFSET_DATA 1
+ #define SPCA561_INDEX_I2C_BASE 0x8800
+ #define SPCA561_SNAPBIT 0x20
+ #define SPCA561_SNAPCTRL 0x40
+
+-static const __u16 rev72a_init_data1[][2] = {
++static const u16 rev72a_reset[][2] = {
+ {0x0000, 0x8114}, /* Software GPIO output data */
+ {0x0001, 0x8114}, /* Software GPIO output data */
+ {0x0000, 0x8112}, /* Some kind of reset */
++ {}
++};
++static const __u16 rev72a_init_data1[][2] = {
+ {0x0003, 0x8701}, /* PCLK clock delay adjustment */
+ {0x0001, 0x8703}, /* HSYNC from cmos inverted */
+ {0x0011, 0x8118}, /* Enable and conf sensor */
+ {0x0001, 0x8118}, /* Conf sensor */
+ {0x0092, 0x8804}, /* I know nothing about these */
+ {0x0010, 0x8802}, /* 0x88xx registers, so I won't */
+- {0x000d, 0x8805}, /* sensor default setting */
+ {}
+ };
+-static const __u16 rev72a_init_sensor1[][2] = {
+- /* ms-win values */
+- {0x0001, 0x0018}, /* 0x01 <- 0x0d */
+- {0x0002, 0x0065}, /* 0x02 <- 0x18 */
+- {0x0004, 0x0121}, /* 0x04 <- 0x0165 */
+- {0x0005, 0x00aa}, /* 0x05 <- 0x21 */
+- {0x0007, 0x0004}, /* 0x07 <- 0xaa */
+- {0x0020, 0x1502}, /* 0x20 <- 0x1504 */
+- {0x0039, 0x0010}, /* 0x39 <- 0x02 */
+- {0x0035, 0x0049}, /* 0x35 <- 0x10 */
+- {0x0009, 0x100b}, /* 0x09 <- 0x1049 */
+- {0x0028, 0x000f}, /* 0x28 <- 0x0b */
+- {0x003b, 0x003c}, /* 0x3b <- 0x0f */
+- {0x003c, 0x0000}, /* 0x3c <- 0x00 */
++static const u16 rev72a_init_sensor1[][2] = {
++ {0x0001, 0x000d},
++ {0x0002, 0x0018},
++ {0x0004, 0x0165},
++ {0x0005, 0x0021},
++ {0x0007, 0x00aa},
++ {0x0020, 0x1504},
++ {0x0039, 0x0002},
++ {0x0035, 0x0010},
++ {0x0009, 0x1049},
++ {0x0028, 0x000b},
++ {0x003b, 0x000f},
++ {0x003c, 0x0000},
+ {}
+ };
+ static const __u16 rev72a_init_data2[][2] = {
+@@ -190,15 +190,10 @@ static const __u16 rev72a_init_data2[][2] = {
+ {0x0002, 0x8201}, /* Output address for r/w serial EEPROM */
+ {0x0008, 0x8200}, /* Clear valid bit for serial EEPROM */
+ {0x0001, 0x8200}, /* OprMode to be executed by hardware */
+- {0x0007, 0x8201}, /* Output address for r/w serial EEPROM */
+- {0x0008, 0x8200}, /* Clear valid bit for serial EEPROM */
+- {0x0001, 0x8200}, /* OprMode to be executed by hardware */
+- {0x0010, 0x8660}, /* Compensation memory stuff */
+- {0x0018, 0x8660}, /* Compensation memory stuff */
+-
+- {0x0004, 0x8611}, /* R offset for white balance */
+- {0x0004, 0x8612}, /* Gr offset for white balance */
+- {0x0007, 0x8613}, /* B offset for white balance */
++/* from ms-win */
++ {0x0000, 0x8611}, /* R offset for white balance */
++ {0x00fd, 0x8612}, /* Gr offset for white balance */
++ {0x0003, 0x8613}, /* B offset for white balance */
+ {0x0000, 0x8614}, /* Gb offset for white balance */
+ /* from ms-win */
+ {0x0035, 0x8651}, /* R gain for white balance */
+@@ -206,8 +201,8 @@ static const __u16 rev72a_init_data2[][2] = {
+ {0x005f, 0x8653}, /* B gain for white balance */
+ {0x0040, 0x8654}, /* Gb gain for white balance */
+ {0x0002, 0x8502}, /* Maximum average bit rate stuff */
+-
+ {0x0011, 0x8802},
++
+ {0x0087, 0x8700}, /* Set master clock (96Mhz????) */
+ {0x0081, 0x8702}, /* Master clock output enable */
+
+@@ -218,104 +213,15 @@ static const __u16 rev72a_init_data2[][2] = {
+ {0x0003, 0x865c}, /* Vertical offset for valid lines */
+ {}
+ };
+-static const __u16 rev72a_init_sensor2[][2] = {
+- /* ms-win values */
+- {0x0003, 0x0121}, /* 0x03 <- 0x01 0x21 //289 */
+- {0x0004, 0x0165}, /* 0x04 <- 0x01 0x65 //357 */
+- {0x0005, 0x002f}, /* 0x05 <- 0x2f */
+- {0x0006, 0x0000}, /* 0x06 <- 0 */
+- {0x000a, 0x0002}, /* 0x0a <- 2 */
+- {0x0009, 0x1061}, /* 0x09 <- 0x1061 */
+- {0x0035, 0x0014}, /* 0x35 <- 0x14 */
+- {}
+-};
+-static const __u16 rev72a_init_data3[][2] = {
+- {0x0030, 0x8112}, /* ISO and drop packet enable */
+-/*fixme: should stop here*/
+- {0x0000, 0x8112}, /* Some kind of reset ???? */
+- {0x0009, 0x8118}, /* Enable sensor and set standby */
+- {0x0000, 0x8114}, /* Software GPIO output data */
+- {0x0000, 0x8114}, /* Software GPIO output data */
+- {0x0001, 0x8114}, /* Software GPIO output data */
+- {0x0000, 0x8112}, /* Some kind of reset ??? */
+- {0x0003, 0x8701},
+- {0x0001, 0x8703},
+- {0x0011, 0x8118},
+- {0x0001, 0x8118},
+- /***************/
+- {0x0092, 0x8804},
+- {0x0010, 0x8802},
+- {0x000d, 0x8805},
+- {0x0001, 0x8801},
+- {0x0000, 0x8800},
+- {0x0018, 0x8805},
+- {0x0002, 0x8801},
+- {0x0000, 0x8800},
+- {0x0065, 0x8805},
+- {0x0004, 0x8801},
+- {0x0001, 0x8800},
+- {0x0021, 0x8805},
+- {0x0005, 0x8801},
+- {0x0000, 0x8800},
+- {0x00aa, 0x8805},
+- {0x0007, 0x8801}, /* mode 0xaa */
+- {0x0000, 0x8800},
+- {0x0004, 0x8805},
+- {0x0020, 0x8801},
+- {0x0015, 0x8800}, /* mode 0x0415 */
+- {0x0002, 0x8805},
+- {0x0039, 0x8801},
+- {0x0000, 0x8800},
+- {0x0010, 0x8805},
+- {0x0035, 0x8801},
+- {0x0000, 0x8800},
+- {0x0049, 0x8805},
+- {0x0009, 0x8801},
+- {0x0010, 0x8800},
+- {0x000b, 0x8805},
+- {0x0028, 0x8801},
+- {0x0000, 0x8800},
+- {0x000f, 0x8805},
+- {0x003b, 0x8801},
+- {0x0000, 0x8800},
+- {0x0000, 0x8805},
+- {0x003c, 0x8801},
+- {0x0000, 0x8800},
+- {0x0002, 0x8502},
+- {0x0039, 0x8801},
+- {0x0000, 0x8805},
+- {0x0000, 0x8800},
+-
+- {0x0087, 0x8700}, /* overwrite by start */
+- {0x0081, 0x8702},
+- {0x0000, 0x8500},
+-/* {0x0010, 0x8500}, -- Previous line was this */
+- {0x0002, 0x865b},
+- {0x0003, 0x865c},
+- /***************/
+- {0x0003, 0x8801}, /* 0x121-> 289 */
+- {0x0021, 0x8805},
+- {0x0001, 0x8800},
+- {0x0004, 0x8801}, /* 0x165 -> 357 */
+- {0x0065, 0x8805},
+- {0x0001, 0x8800},
+- {0x0005, 0x8801}, /* 0x2f //blanking control colonne */
+- {0x002f, 0x8805},
+- {0x0000, 0x8800},
+- {0x0006, 0x8801}, /* 0x00 //blanking mode row */
+- {0x0000, 0x8805},
+- {0x0000, 0x8800},
+- {0x000a, 0x8801}, /* 0x01 //0x02 */
+- {0x0001, 0x8805},
+- {0x0000, 0x8800},
+- {0x0009, 0x8801}, /* 0x1061 - setexposure times && pixel clock
++static const u16 rev72a_init_sensor2[][2] = {
++ {0x0003, 0x0121},
++ {0x0004, 0x0165},
++ {0x0005, 0x002f}, /* blanking control column */
++ {0x0006, 0x0000}, /* blanking mode row*/
++ {0x000a, 0x0002},
++ {0x0009, 0x1061}, /* setexposure times && pixel clock
+ * 0001 0 | 000 0110 0001 */
+- {0x0061, 0x8805}, /* 61 31 */
+- {0x0008, 0x8800}, /* 08 */
+- {0x0035, 0x8801}, /* 0x14 - set gain general */
+- {0x001f, 0x8805}, /* 0x14 */
+- {0x0000, 0x8800},
+- {0x000e, 0x8112}, /* white balance - was 30 */
++ {0x0035, 0x0014},
+ {}
+ };
+
+@@ -460,6 +366,7 @@ static void i2c_write(struct gspca_dev *gspca_dev, __u16 value, __u16 reg)
+ reg_r(gspca_dev, 0x8803, 1);
+ if (!gspca_dev->usb_buf[0])
+ return;
++ msleep(10);
+ } while (--retry);
+ }
+
+@@ -479,6 +386,7 @@ static int i2c_read(struct gspca_dev *gspca_dev, __u16 reg, __u8 mode)
+ reg_r(gspca_dev, 0x8805, 1);
+ return ((int) value << 8) | gspca_dev->usb_buf[0];
+ }
++ msleep(10);
+ } while (--retry);
+ return -1;
+ }
+@@ -541,7 +449,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ }
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = 0x01;
+ gspca_dev->nbalt = 7 + 1; /* choose alternate 7 first */
+
+ sd->chip_revision = id->driver_info;
+@@ -572,11 +479,13 @@ static int sd_init_12a(struct gspca_dev *gspca_dev)
+ static int sd_init_72a(struct gspca_dev *gspca_dev)
+ {
+ PDEBUG(D_STREAM, "Chip revision: 072a");
++ write_vector(gspca_dev, rev72a_reset);
++ msleep(200);
+ write_vector(gspca_dev, rev72a_init_data1);
+ write_sensor_72a(gspca_dev, rev72a_init_sensor1);
+ write_vector(gspca_dev, rev72a_init_data2);
+ write_sensor_72a(gspca_dev, rev72a_init_sensor2);
+- write_vector(gspca_dev, rev72a_init_data3);
++ reg_w_val(gspca_dev->dev, 0x8112, 0x30);
+ return 0;
+ }
+
+@@ -731,11 +640,18 @@ static int sd_start_72a(struct gspca_dev *gspca_dev)
+ int Clck;
+ int mode;
+
++ write_vector(gspca_dev, rev72a_reset);
++ msleep(200);
++ write_vector(gspca_dev, rev72a_init_data1);
++ write_sensor_72a(gspca_dev, rev72a_init_sensor1);
++
+ mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+ switch (mode) {
+ default:
+-/* case 0:
+- case 1: */
++ case 0:
++ Clck = 0x27; /* ms-win 0x87 */
++ break;
++ case 1:
+ Clck = 0x25;
+ break;
+ case 2:
+@@ -745,13 +661,14 @@ static int sd_start_72a(struct gspca_dev *gspca_dev)
+ Clck = 0x21;
+ break;
+ }
+- reg_w_val(dev, 0x8500, mode); /* mode */
+ reg_w_val(dev, 0x8700, Clck); /* 0x27 clock */
+- reg_w_val(dev, 0x8112, 0x10 | 0x20);
++ reg_w_val(dev, 0x8702, 0x81);
++ reg_w_val(dev, 0x8500, mode); /* mode */
++ write_sensor_72a(gspca_dev, rev72a_init_sensor2);
+ setcontrast(gspca_dev);
+ /* setbrightness(gspca_dev); * fixme: bad values */
+- setwhite(gspca_dev);
+ setautogain(gspca_dev);
++ reg_w_val(dev, 0x8112, 0x10 | 0x20);
+ return 0;
+ }
+
+@@ -867,12 +784,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- switch (data[0]) { /* sequence number */
++ len--;
++ switch (*data++) { /* sequence number */
+ case 0: /* start of frame */
+ frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+ data, 0);
+- data += SPCA561_OFFSET_DATA;
+- len -= SPCA561_OFFSET_DATA;
+ if (data[1] & 0x10) {
+ /* compressed bayer */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+@@ -893,8 +809,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ case 0xff: /* drop (empty mpackets) */
+ return;
+ }
+- data++;
+- len--;
+ gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+ }
+
+@@ -1197,8 +1111,10 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c
+new file mode 100644
+index 0000000..04e3ae5
+--- /dev/null
++++ b/drivers/media/video/gspca/sq905.c
+@@ -0,0 +1,456 @@
++/*
++ * SQ905 subdriver
++ *
++ * Copyright (C) 2008, 2009 Adam Baker and Theodore Kilgore
++ *
++ * 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
++ * 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 and Acknowledgments
++ *
++ * The original Linux driver for SQ905 based cameras was written by
++ * Marcell Lengyel and furter developed by many other contributers
++ * and is available from http://sourceforge.net/projects/sqcam/
++ *
++ * This driver takes advantage of the reverse engineering work done for
++ * that driver and for libgphoto2 but shares no code with them.
++ *
++ * This driver has used as a base the finepix driver and other gspca
++ * based drivers and may still contain code fragments taken from those
++ * drivers.
++ */
++
++#define MODULE_NAME "sq905"
++
++#include <linux/workqueue.h>
++#include "gspca.h"
++
++MODULE_AUTHOR("Adam Baker <linux@baker-net.org.uk>, "
++ "Theodore Kilgore <kilgota@auburn.edu>");
++MODULE_DESCRIPTION("GSPCA/SQ905 USB Camera Driver");
++MODULE_LICENSE("GPL");
++
++/* Default timeouts, in ms */
++#define SQ905_CMD_TIMEOUT 500
++#define SQ905_DATA_TIMEOUT 1000
++
++/* Maximum transfer size to use. */
++#define SQ905_MAX_TRANSFER 0x8000
++#define FRAME_HEADER_LEN 64
++
++/* The known modes, or registers. These go in the "value" slot. */
++
++/* 00 is "none" obviously */
++
++#define SQ905_BULK_READ 0x03 /* precedes any bulk read */
++#define SQ905_COMMAND 0x06 /* precedes the command codes below */
++#define SQ905_PING 0x07 /* when reading an "idling" command */
++#define SQ905_READ_DONE 0xc0 /* ack bulk read completed */
++
++/* Any non-zero value in the bottom 2 bits of the 2nd byte of
++ * the ID appears to indicate the camera can do 640*480. If the
++ * LSB of that byte is set the image is just upside down, otherwise
++ * it is rotated 180 degrees. */
++#define SQ905_HIRES_MASK 0x00000300
++#define SQ905_ORIENTATION_MASK 0x00000100
++
++/* Some command codes. These go in the "index" slot. */
++
++#define SQ905_ID 0xf0 /* asks for model string */
++#define SQ905_CONFIG 0x20 /* gets photo alloc. table, not used here */
++#define SQ905_DATA 0x30 /* accesses photo data, not used here */
++#define SQ905_CLEAR 0xa0 /* clear everything */
++#define SQ905_CAPTURE_LOW 0x60 /* Starts capture at 160x120 */
++#define SQ905_CAPTURE_MED 0x61 /* Starts capture at 320x240 */
++#define SQ905_CAPTURE_HIGH 0x62 /* Starts capture at 640x480 (some cams only) */
++/* note that the capture command also controls the output dimensions */
++
++/* Structure to hold all of our device specific stuff */
++struct sd {
++ struct gspca_dev gspca_dev; /* !! must be the first item */
++
++ /*
++ * Driver stuff
++ */
++ struct work_struct work_struct;
++ struct workqueue_struct *work_thread;
++};
++
++static struct v4l2_pix_format sq905_mode[] = {
++ { 160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
++ .bytesperline = 160,
++ .sizeimage = 160 * 120,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 0},
++ { 320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
++ .bytesperline = 320,
++ .sizeimage = 320 * 240,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 0},
++ { 640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
++ .bytesperline = 640,
++ .sizeimage = 640 * 480,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 0}
++};
++
++/*
++ * Send a command to the camera.
++ */
++static int sq905_command(struct gspca_dev *gspca_dev, u16 index)
++{
++ int ret;
++
++ gspca_dev->usb_buf[0] = '\0';
++ ret = usb_control_msg(gspca_dev->dev,
++ usb_sndctrlpipe(gspca_dev->dev, 0),
++ USB_REQ_SYNCH_FRAME, /* request */
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ SQ905_COMMAND, index, gspca_dev->usb_buf, 1,
++ SQ905_CMD_TIMEOUT);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)",
++ __func__, ret);
++ return ret;
++ }
++
++ ret = usb_control_msg(gspca_dev->dev,
++ usb_sndctrlpipe(gspca_dev->dev, 0),
++ USB_REQ_SYNCH_FRAME, /* request */
++ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ SQ905_PING, 0, gspca_dev->usb_buf, 1,
++ SQ905_CMD_TIMEOUT);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "%s: usb_control_msg failed 2 (%d)",
++ __func__, ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++/*
++ * Acknowledge the end of a frame - see warning on sq905_command.
++ */
++static int sq905_ack_frame(struct gspca_dev *gspca_dev)
++{
++ int ret;
++
++ gspca_dev->usb_buf[0] = '\0';
++ ret = usb_control_msg(gspca_dev->dev,
++ usb_sndctrlpipe(gspca_dev->dev, 0),
++ USB_REQ_SYNCH_FRAME, /* request */
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ SQ905_READ_DONE, 0, gspca_dev->usb_buf, 1,
++ SQ905_CMD_TIMEOUT);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)", __func__, ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++/*
++ * request and read a block of data - see warning on sq905_command.
++ */
++static int
++sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size)
++{
++ int ret;
++ int act_len;
++
++ gspca_dev->usb_buf[0] = '\0';
++ ret = usb_control_msg(gspca_dev->dev,
++ usb_sndctrlpipe(gspca_dev->dev, 0),
++ USB_REQ_SYNCH_FRAME, /* request */
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ SQ905_BULK_READ, size, gspca_dev->usb_buf,
++ 1, SQ905_CMD_TIMEOUT);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)", __func__, ret);
++ return ret;
++ }
++ ret = usb_bulk_msg(gspca_dev->dev,
++ usb_rcvbulkpipe(gspca_dev->dev, 0x81),
++ data, size, &act_len, SQ905_DATA_TIMEOUT);
++
++ /* successful, it returns 0, otherwise negative */
++ if (ret < 0 || act_len != size) {
++ PDEBUG(D_ERR, "bulk read fail (%d) len %d/%d",
++ ret, act_len, size);
++ return -EIO;
++ }
++ return 0;
++}
++
++/* This function is called as a workqueue function and runs whenever the camera
++ * is streaming data. Because it is a workqueue function it is allowed to sleep
++ * so we can use synchronous USB calls. To avoid possible collisions with other
++ * threads attempting to use the camera's USB interface we take the gspca
++ * usb_lock when performing USB operations. In practice the only thing we need
++ * to protect against is the usb_set_interface call that gspca makes during
++ * stream_off as the camera doesn't provide any controls that the user could try
++ * to change.
++ */
++static void sq905_dostream(struct work_struct *work)
++{
++ struct sd *dev = container_of(work, struct sd, work_struct);
++ struct gspca_dev *gspca_dev = &dev->gspca_dev;
++ struct gspca_frame *frame;
++ int bytes_left; /* bytes remaining in current frame. */
++ int data_len; /* size to use for the next read. */
++ int header_read; /* true if we have already read the frame header. */
++ int discarding; /* true if we failed to get space for frame. */
++ int packet_type;
++ int frame_sz;
++ int ret;
++ u8 *data;
++ u8 *buffer;
++
++ buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
++ mutex_lock(&gspca_dev->usb_lock);
++ if (!buffer) {
++ PDEBUG(D_ERR, "Couldn't allocate USB buffer");
++ goto quit_stream;
++ }
++
++ frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage
++ + FRAME_HEADER_LEN;
++
++ while (gspca_dev->present && gspca_dev->streaming) {
++ /* Need a short delay to ensure streaming flag was set by
++ * gspca and to make sure gspca can grab the mutex. */
++ mutex_unlock(&gspca_dev->usb_lock);
++ msleep(1);
++
++ /* request some data and then read it until we have
++ * a complete frame. */
++ bytes_left = frame_sz;
++ header_read = 0;
++ discarding = 0;
++
++ while (bytes_left > 0) {
++ data_len = bytes_left > SQ905_MAX_TRANSFER ?
++ SQ905_MAX_TRANSFER : bytes_left;
++ mutex_lock(&gspca_dev->usb_lock);
++ if (!gspca_dev->present)
++ goto quit_stream;
++ ret = sq905_read_data(gspca_dev, buffer, data_len);
++ if (ret < 0)
++ goto quit_stream;
++ mutex_unlock(&gspca_dev->usb_lock);
++ PDEBUG(D_STREAM,
++ "Got %d bytes out of %d for frame",
++ data_len, bytes_left);
++ bytes_left -= data_len;
++ data = buffer;
++ if (!header_read) {
++ packet_type = FIRST_PACKET;
++ /* The first 64 bytes of each frame are
++ * a header full of FF 00 bytes */
++ data += FRAME_HEADER_LEN;
++ data_len -= FRAME_HEADER_LEN;
++ header_read = 1;
++ } else if (bytes_left == 0) {
++ packet_type = LAST_PACKET;
++ } else {
++ packet_type = INTER_PACKET;
++ }
++ frame = gspca_get_i_frame(gspca_dev);
++ if (frame && !discarding) {
++ frame = gspca_frame_add(gspca_dev, packet_type,
++ frame, data, data_len);
++ /* If entire frame fits in one packet we still
++ need to add a LAST_PACKET */
++ if (packet_type == FIRST_PACKET &&
++ bytes_left == 0)
++ frame = gspca_frame_add(gspca_dev,
++ LAST_PACKET,
++ frame, data, 0);
++ } else {
++ discarding = 1;
++ }
++ }
++ /* acknowledge the frame */
++ mutex_lock(&gspca_dev->usb_lock);
++ if (!gspca_dev->present)
++ goto quit_stream;
++ ret = sq905_ack_frame(gspca_dev);
++ if (ret < 0)
++ goto quit_stream;
++ }
++quit_stream:
++ /* the usb_lock is already acquired */
++ if (gspca_dev->present)
++ sq905_command(gspca_dev, SQ905_CLEAR);
++ mutex_unlock(&gspca_dev->usb_lock);
++ kfree(buffer);
++}
++
++/* This function is called at probe time just before sd_init */
++static int sd_config(struct gspca_dev *gspca_dev,
++ const struct usb_device_id *id)
++{
++ struct cam *cam = &gspca_dev->cam;
++ struct sd *dev = (struct sd *) gspca_dev;
++
++ /* We don't use the buffer gspca allocates so make it small. */
++ cam->bulk_size = 64;
++
++ INIT_WORK(&dev->work_struct, sq905_dostream);
++
++ return 0;
++}
++
++/* called on streamoff with alt==0 and on disconnect */
++/* the usb_lock is held at entry - restore on exit */
++static void sd_stop0(struct gspca_dev *gspca_dev)
++{
++ struct sd *dev = (struct sd *) gspca_dev;
++
++ /* wait for the work queue to terminate */
++ mutex_unlock(&gspca_dev->usb_lock);
++ /* This waits for sq905_dostream to finish */
++ destroy_workqueue(dev->work_thread);
++ dev->work_thread = NULL;
++ mutex_lock(&gspca_dev->usb_lock);
++}
++
++/* this function is called at probe and resume time */
++static int sd_init(struct gspca_dev *gspca_dev)
++{
++ u32 ident;
++ int ret;
++
++ /* connect to the camera and read
++ * the model ID and process that and put it away.
++ */
++ ret = sq905_command(gspca_dev, SQ905_CLEAR);
++ if (ret < 0)
++ return ret;
++ ret = sq905_command(gspca_dev, SQ905_ID);
++ if (ret < 0)
++ return ret;
++ ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4);
++ if (ret < 0)
++ return ret;
++ /* usb_buf is allocated with kmalloc so is aligned.
++ * Camera model number is the right way round if we assume this
++ * reverse engineered ID is supposed to be big endian. */
++ ident = be32_to_cpup((__be32 *)gspca_dev->usb_buf);
++ ret = sq905_command(gspca_dev, SQ905_CLEAR);
++ if (ret < 0)
++ return ret;
++ PDEBUG(D_CONF, "SQ905 camera ID %08x detected", ident);
++ gspca_dev->cam.cam_mode = sq905_mode;
++ gspca_dev->cam.nmodes = ARRAY_SIZE(sq905_mode);
++ if (!(ident & SQ905_HIRES_MASK))
++ gspca_dev->cam.nmodes--;
++ return 0;
++}
++
++/* Set up for getting frames. */
++static int sd_start(struct gspca_dev *gspca_dev)
++{
++ struct sd *dev = (struct sd *) gspca_dev;
++ int ret;
++
++ /* "Open the shutter" and set size, to start capture */
++ switch (gspca_dev->curr_mode) {
++ default:
++/* case 2: */
++ PDEBUG(D_STREAM, "Start streaming at high resolution");
++ ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_HIGH);
++ break;
++ case 1:
++ PDEBUG(D_STREAM, "Start streaming at medium resolution");
++ ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_MED);
++ break;
++ case 0:
++ PDEBUG(D_STREAM, "Start streaming at low resolution");
++ ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_LOW);
++ }
++
++ if (ret < 0) {
++ PDEBUG(D_ERR, "Start streaming command failed");
++ return ret;
++ }
++ /* Start the workqueue function to do the streaming */
++ dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
++ queue_work(dev->work_thread, &dev->work_struct);
++
++ return 0;
++}
++
++/* Table of supported USB devices */
++static const __devinitdata struct usb_device_id device_table[] = {
++ {USB_DEVICE(0x2770, 0x9120)},
++ {}
++};
++
++MODULE_DEVICE_TABLE(usb, device_table);
++
++/* sub-driver description */
++static const struct sd_desc sd_desc = {
++ .name = MODULE_NAME,
++ .config = sd_config,
++ .init = sd_init,
++ .start = sd_start,
++ .stop0 = sd_stop0,
++};
++
++/* -- device connect -- */
++static int sd_probe(struct usb_interface *intf,
++ const struct usb_device_id *id)
++{
++ return gspca_dev_probe(intf, id,
++ &sd_desc,
++ sizeof(struct sd),
++ THIS_MODULE);
++}
++
++static struct usb_driver sd_driver = {
++ .name = MODULE_NAME,
++ .id_table = device_table,
++ .probe = sd_probe,
++ .disconnect = gspca_disconnect,
++#ifdef CONFIG_PM
++ .suspend = gspca_suspend,
++ .resume = gspca_resume,
++#endif
++};
++
++/* -- module insert / remove -- */
++static int __init sd_mod_init(void)
++{
++ int ret;
++
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
++ PDEBUG(D_PROBE, "registered");
++ return 0;
++}
++
++static void __exit sd_mod_exit(void)
++{
++ usb_deregister(&sd_driver);
++ PDEBUG(D_PROBE, "deregistered");
++}
++
++module_init(sd_mod_init);
++module_exit(sd_mod_exit);
+diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c
+new file mode 100644
+index 0000000..0bcb74a
+--- /dev/null
++++ b/drivers/media/video/gspca/sq905c.c
+@@ -0,0 +1,328 @@
++/*
++ * SQ905C subdriver
++ *
++ * Copyright (C) 2009 Theodore Kilgore
++ *
++ * 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
++ * 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
++ */
++
++/*
++ *
++ * This driver uses work done in
++ * libgphoto2/camlibs/digigr8, Copyright (C) Theodore Kilgore.
++ *
++ * This driver has also used as a base the sq905c driver
++ * and may contain code fragments from it.
++ */
++
++#define MODULE_NAME "sq905c"
++
++#include <linux/workqueue.h>
++#include "gspca.h"
++
++MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
++MODULE_DESCRIPTION("GSPCA/SQ905C USB Camera Driver");
++MODULE_LICENSE("GPL");
++
++/* Default timeouts, in ms */
++#define SQ905C_CMD_TIMEOUT 500
++#define SQ905C_DATA_TIMEOUT 1000
++
++/* Maximum transfer size to use. */
++#define SQ905C_MAX_TRANSFER 0x8000
++
++#define FRAME_HEADER_LEN 0x50
++
++/* Commands. These go in the "value" slot. */
++#define SQ905C_CLEAR 0xa0 /* clear everything */
++#define SQ905C_CAPTURE_LOW 0xa040 /* Starts capture at 160x120 */
++#define SQ905C_CAPTURE_MED 0x1440 /* Starts capture at 320x240 */
++#define SQ905C_CAPTURE_HI 0x2840 /* Starts capture at 320x240 */
++
++/* For capture, this must go in the "index" slot. */
++#define SQ905C_CAPTURE_INDEX 0x110f
++
++/* Structure to hold all of our device specific stuff */
++struct sd {
++ struct gspca_dev gspca_dev; /* !! must be the first item */
++ const struct v4l2_pix_format *cap_mode;
++ /* Driver stuff */
++ struct work_struct work_struct;
++ struct workqueue_struct *work_thread;
++};
++
++/*
++ * Most of these cameras will do 640x480 and 320x240. 160x120 works
++ * in theory but gives very poor output. Therefore, not supported.
++ * The 0x2770:0x9050 cameras have max resolution of 320x240.
++ */
++static struct v4l2_pix_format sq905c_mode[] = {
++ { 320, 240, V4L2_PIX_FMT_SQ905C, V4L2_FIELD_NONE,
++ .bytesperline = 320,
++ .sizeimage = 320 * 240,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 0},
++ { 640, 480, V4L2_PIX_FMT_SQ905C, V4L2_FIELD_NONE,
++ .bytesperline = 640,
++ .sizeimage = 640 * 480,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 0}
++};
++
++/* Send a command to the camera. */
++static int sq905c_command(struct gspca_dev *gspca_dev, u16 command, u16 index)
++{
++ int ret;
++
++ ret = usb_control_msg(gspca_dev->dev,
++ usb_sndctrlpipe(gspca_dev->dev, 0),
++ USB_REQ_SYNCH_FRAME, /* request */
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ command, index, NULL, 0,
++ SQ905C_CMD_TIMEOUT);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)",
++ __func__, ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++/* This function is called as a workqueue function and runs whenever the camera
++ * is streaming data. Because it is a workqueue function it is allowed to sleep
++ * so we can use synchronous USB calls. To avoid possible collisions with other
++ * threads attempting to use the camera's USB interface the gspca usb_lock is
++ * used when performing the one USB control operation inside the workqueue,
++ * which tells the camera to close the stream. In practice the only thing
++ * which needs to be protected against is the usb_set_interface call that
++ * gspca makes during stream_off. Otherwise the camera doesn't provide any
++ * controls that the user could try to change.
++ */
++static void sq905c_dostream(struct work_struct *work)
++{
++ struct sd *dev = container_of(work, struct sd, work_struct);
++ struct gspca_dev *gspca_dev = &dev->gspca_dev;
++ struct gspca_frame *frame;
++ int bytes_left; /* bytes remaining in current frame. */
++ int data_len; /* size to use for the next read. */
++ int act_len;
++ int discarding = 0; /* true if we failed to get space for frame. */
++ int packet_type;
++ int ret;
++ u8 *buffer;
++
++ buffer = kmalloc(SQ905C_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
++ if (!buffer) {
++ PDEBUG(D_ERR, "Couldn't allocate USB buffer");
++ goto quit_stream;
++ }
++
++ while (gspca_dev->present && gspca_dev->streaming) {
++ if (!gspca_dev->present)
++ goto quit_stream;
++ /* Request the header, which tells the size to download */
++ ret = usb_bulk_msg(gspca_dev->dev,
++ usb_rcvbulkpipe(gspca_dev->dev, 0x81),
++ buffer, FRAME_HEADER_LEN, &act_len,
++ SQ905C_DATA_TIMEOUT);
++ PDEBUG(D_STREAM,
++ "Got %d bytes out of %d for header",
++ act_len, FRAME_HEADER_LEN);
++ if (ret < 0 || act_len < FRAME_HEADER_LEN)
++ goto quit_stream;
++ /* size is read from 4 bytes starting 0x40, little endian */
++ bytes_left = buffer[0x40]|(buffer[0x41]<<8)|(buffer[0x42]<<16)
++ |(buffer[0x43]<<24);
++ PDEBUG(D_STREAM, "bytes_left = 0x%x", bytes_left);
++ /* We keep the header. It has other information, too. */
++ packet_type = FIRST_PACKET;
++ frame = gspca_get_i_frame(gspca_dev);
++ if (frame && !discarding) {
++ gspca_frame_add(gspca_dev, packet_type,
++ frame, buffer, FRAME_HEADER_LEN);
++ } else
++ discarding = 1;
++ while (bytes_left > 0) {
++ data_len = bytes_left > SQ905C_MAX_TRANSFER ?
++ SQ905C_MAX_TRANSFER : bytes_left;
++ if (!gspca_dev->present)
++ goto quit_stream;
++ ret = usb_bulk_msg(gspca_dev->dev,
++ usb_rcvbulkpipe(gspca_dev->dev, 0x81),
++ buffer, data_len, &act_len,
++ SQ905C_DATA_TIMEOUT);
++ if (ret < 0 || act_len < data_len)
++ goto quit_stream;
++ PDEBUG(D_STREAM,
++ "Got %d bytes out of %d for frame",
++ data_len, bytes_left);
++ bytes_left -= data_len;
++ if (bytes_left == 0)
++ packet_type = LAST_PACKET;
++ else
++ packet_type = INTER_PACKET;
++ frame = gspca_get_i_frame(gspca_dev);
++ if (frame && !discarding)
++ gspca_frame_add(gspca_dev, packet_type,
++ frame, buffer, data_len);
++ else
++ discarding = 1;
++ }
++ }
++quit_stream:
++ mutex_lock(&gspca_dev->usb_lock);
++ if (gspca_dev->present)
++ sq905c_command(gspca_dev, SQ905C_CLEAR, 0);
++ mutex_unlock(&gspca_dev->usb_lock);
++ kfree(buffer);
++}
++
++/* This function is called at probe time just before sd_init */
++static int sd_config(struct gspca_dev *gspca_dev,
++ const struct usb_device_id *id)
++{
++ struct cam *cam = &gspca_dev->cam;
++ struct sd *dev = (struct sd *) gspca_dev;
++
++ PDEBUG(D_PROBE,
++ "SQ9050 camera detected"
++ " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
++ cam->cam_mode = sq905c_mode;
++ cam->nmodes = 2;
++ if (id->idProduct == 0x9050)
++ cam->nmodes = 1;
++ /* We don't use the buffer gspca allocates so make it small. */
++ cam->bulk_size = 32;
++ INIT_WORK(&dev->work_struct, sq905c_dostream);
++ return 0;
++}
++
++/* called on streamoff with alt==0 and on disconnect */
++/* the usb_lock is held at entry - restore on exit */
++static void sd_stop0(struct gspca_dev *gspca_dev)
++{
++ struct sd *dev = (struct sd *) gspca_dev;
++
++ /* wait for the work queue to terminate */
++ mutex_unlock(&gspca_dev->usb_lock);
++ /* This waits for sq905c_dostream to finish */
++ destroy_workqueue(dev->work_thread);
++ dev->work_thread = NULL;
++ mutex_lock(&gspca_dev->usb_lock);
++}
++
++/* this function is called at probe and resume time */
++static int sd_init(struct gspca_dev *gspca_dev)
++{
++ int ret;
++
++ /* connect to the camera and reset it. */
++ ret = sq905c_command(gspca_dev, SQ905C_CLEAR, 0);
++ return ret;
++}
++
++/* Set up for getting frames. */
++static int sd_start(struct gspca_dev *gspca_dev)
++{
++ struct sd *dev = (struct sd *) gspca_dev;
++ int ret;
++
++ dev->cap_mode = gspca_dev->cam.cam_mode;
++ /* "Open the shutter" and set size, to start capture */
++ switch (gspca_dev->width) {
++ case 640:
++ PDEBUG(D_STREAM, "Start streaming at high resolution");
++ dev->cap_mode++;
++ ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_HI,
++ SQ905C_CAPTURE_INDEX);
++ break;
++ default: /* 320 */
++ PDEBUG(D_STREAM, "Start streaming at medium resolution");
++ ret = sq905c_command(gspca_dev, SQ905C_CAPTURE_MED,
++ SQ905C_CAPTURE_INDEX);
++ }
++
++ if (ret < 0) {
++ PDEBUG(D_ERR, "Start streaming command failed");
++ return ret;
++ }
++ /* Start the workqueue function to do the streaming */
++ dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
++ queue_work(dev->work_thread, &dev->work_struct);
++
++ return 0;
++}
++
++/* Table of supported USB devices */
++static const __devinitdata struct usb_device_id device_table[] = {
++ {USB_DEVICE(0x2770, 0x905c)},
++ {USB_DEVICE(0x2770, 0x9050)},
++ {USB_DEVICE(0x2770, 0x913d)},
++ {}
++};
++
++MODULE_DEVICE_TABLE(usb, device_table);
++
++/* sub-driver description */
++static const struct sd_desc sd_desc = {
++ .name = MODULE_NAME,
++ .config = sd_config,
++ .init = sd_init,
++ .start = sd_start,
++ .stop0 = sd_stop0,
++};
++
++/* -- device connect -- */
++static int sd_probe(struct usb_interface *intf,
++ const struct usb_device_id *id)
++{
++ return gspca_dev_probe(intf, id,
++ &sd_desc,
++ sizeof(struct sd),
++ THIS_MODULE);
++}
++
++static struct usb_driver sd_driver = {
++ .name = MODULE_NAME,
++ .id_table = device_table,
++ .probe = sd_probe,
++ .disconnect = gspca_disconnect,
++#ifdef CONFIG_PM
++ .suspend = gspca_suspend,
++ .resume = gspca_resume,
++#endif
++};
++
++/* -- module insert / remove -- */
++static int __init sd_mod_init(void)
++{
++ int ret;
++
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
++ PDEBUG(D_PROBE, "registered");
++ return 0;
++}
++
++static void __exit sd_mod_exit(void)
++{
++ usb_deregister(&sd_driver);
++ PDEBUG(D_PROBE, "deregistered");
++}
++
++module_init(sd_mod_init);
++module_exit(sd_mod_exit);
+diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c
+index 60de9af..f25be20 100644
+--- a/drivers/media/video/gspca/stk014.c
++++ b/drivers/media/video/gspca/stk014.c
+@@ -35,10 +35,13 @@ struct sd {
+ unsigned char contrast;
+ unsigned char colors;
+ unsigned char lightfreq;
+-};
++ u8 quality;
++#define QUALITY_MIN 60
++#define QUALITY_MAX 95
++#define QUALITY_DEF 80
+
+-/* global parameters */
+-static int sd_quant = 7; /* <= 4 KO - 7: good (enough!) */
++ u8 *jpeg_hdr;
++};
+
+ /* V4L2 controls supported by the driver */
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+@@ -180,7 +183,7 @@ static int rcv_val(struct gspca_dev *gspca_dev,
+ reg_w(gspca_dev, 0x63b, 0);
+ reg_w(gspca_dev, 0x630, 5);
+ ret = usb_bulk_msg(dev,
+- usb_rcvbulkpipe(dev, 5),
++ usb_rcvbulkpipe(dev, 0x05),
+ gspca_dev->usb_buf,
+ 4, /* length */
+ &alen,
+@@ -294,15 +297,14 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- struct cam *cam = &gspca_dev->cam;
+
+- cam->epaddr = 0x02;
+ gspca_dev->cam.cam_mode = vga_mode;
+ gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->colors = COLOR_DEF;
+ sd->lightfreq = FREQ_DEF;
++ sd->quality = QUALITY_DEF;
+ return 0;
+ }
+
+@@ -326,8 +328,15 @@ static int sd_init(struct gspca_dev *gspca_dev)
+ /* -- start the camera -- */
+ static int sd_start(struct gspca_dev *gspca_dev)
+ {
++ struct sd *sd = (struct sd *) gspca_dev;
+ int ret, value;
+
++ /* create the JPEG header */
++ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
++ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
++ 0x22); /* JPEG 411 */
++ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
++
+ /* work on alternate 1 */
+ usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
+
+@@ -399,11 +408,19 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
+ PDEBUG(D_STREAM, "camera stopped");
+ }
+
++static void sd_stop0(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ kfree(sd->jpeg_hdr);
++}
++
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ struct gspca_frame *frame, /* target */
+ __u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
++ struct sd *sd = (struct sd *) gspca_dev;
+ static unsigned char ffd9[] = {0xff, 0xd9};
+
+ /* a frame starts with:
+@@ -420,7 +437,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ ffd9, 2);
+
+ /* put the JPEG 411 header */
+- jpeg_put_header(gspca_dev, frame, sd_quant, 0x22);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ sd->jpeg_hdr, JPEG_HDR_SZ);
+
+ /* beginning of the frame */
+ #define STKHDRSZ 12
+@@ -520,6 +538,34 @@ static int sd_querymenu(struct gspca_dev *gspca_dev,
+ return -EINVAL;
+ }
+
++static int sd_set_jcomp(struct gspca_dev *gspca_dev,
++ struct v4l2_jpegcompression *jcomp)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (jcomp->quality < QUALITY_MIN)
++ sd->quality = QUALITY_MIN;
++ else if (jcomp->quality > QUALITY_MAX)
++ sd->quality = QUALITY_MAX;
++ else
++ sd->quality = jcomp->quality;
++ if (gspca_dev->streaming)
++ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
++ return 0;
++}
++
++static int sd_get_jcomp(struct gspca_dev *gspca_dev,
++ struct v4l2_jpegcompression *jcomp)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ memset(jcomp, 0, sizeof *jcomp);
++ jcomp->quality = sd->quality;
++ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
++ | V4L2_JPEG_MARKER_DQT;
++ return 0;
++}
++
+ /* sub-driver description */
+ static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+@@ -529,8 +575,11 @@ static const struct sd_desc sd_desc = {
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
++ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .querymenu = sd_querymenu,
++ .get_jcomp = sd_get_jcomp,
++ .set_jcomp = sd_set_jcomp,
+ };
+
+ /* -- module initialisation -- */
+@@ -562,8 +611,10 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ info("registered");
+ return 0;
+ }
+@@ -575,6 +626,3 @@ static void __exit sd_mod_exit(void)
+
+ module_init(sd_mod_init);
+ module_exit(sd_mod_exit);
+-
+-module_param_named(quant, sd_quant, int, 0644);
+-MODULE_PARM_DESC(quant, "Quantization index (0..8)");
+diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c
+index 13a021e..9dff2e6 100644
+--- a/drivers/media/video/gspca/stv06xx/stv06xx.c
++++ b/drivers/media/video/gspca/stv06xx/stv06xx.c
+@@ -429,7 +429,6 @@ static int stv06xx_config(struct gspca_dev *gspca_dev,
+ PDEBUG(D_PROBE, "Configuring camera");
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = STV_ISOC_ENDPOINT_ADDR;
+ sd->desc = sd_desc;
+ gspca_dev->sd_desc = &sd->desc;
+
+@@ -501,8 +500,10 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
+index 14335a9..b169038 100644
+--- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
++++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.c
+@@ -30,6 +30,66 @@
+
+ #include "stv06xx_hdcs.h"
+
++static const struct ctrl hdcs1x00_ctrl[] = {
++ {
++ {
++ .id = V4L2_CID_EXPOSURE,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "exposure",
++ .minimum = 0x00,
++ .maximum = 0xffff,
++ .step = 0x1,
++ .default_value = HDCS_DEFAULT_EXPOSURE,
++ .flags = V4L2_CTRL_FLAG_SLIDER
++ },
++ .set = hdcs_set_exposure,
++ .get = hdcs_get_exposure
++ }, {
++ {
++ .id = V4L2_CID_GAIN,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "gain",
++ .minimum = 0x00,
++ .maximum = 0xff,
++ .step = 0x1,
++ .default_value = HDCS_DEFAULT_GAIN,
++ .flags = V4L2_CTRL_FLAG_SLIDER
++ },
++ .set = hdcs_set_gain,
++ .get = hdcs_get_gain
++ }
++};
++
++static struct v4l2_pix_format hdcs1x00_mode[] = {
++ {
++ HDCS_1X00_DEF_WIDTH,
++ HDCS_1X00_DEF_HEIGHT,
++ V4L2_PIX_FMT_SBGGR8,
++ V4L2_FIELD_NONE,
++ .sizeimage =
++ HDCS_1X00_DEF_WIDTH * HDCS_1X00_DEF_HEIGHT,
++ .bytesperline = HDCS_1X00_DEF_WIDTH,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 1
++ }
++};
++
++static const struct ctrl hdcs1020_ctrl[] = {};
++
++static struct v4l2_pix_format hdcs1020_mode[] = {
++ {
++ HDCS_1020_DEF_WIDTH,
++ HDCS_1020_DEF_HEIGHT,
++ V4L2_PIX_FMT_SBGGR8,
++ V4L2_FIELD_NONE,
++ .sizeimage =
++ HDCS_1020_DEF_WIDTH * HDCS_1020_DEF_HEIGHT,
++ .bytesperline = HDCS_1020_DEF_WIDTH,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 1
++ }
++};
++
+ enum hdcs_power_state {
+ HDCS_STATE_SLEEP,
+ HDCS_STATE_IDLE,
+@@ -353,10 +413,10 @@ static int hdcs_probe_1x00(struct sd *sd)
+
+ info("HDCS-1000/1100 sensor detected");
+
+- sd->gspca_dev.cam.cam_mode = stv06xx_sensor_hdcs1x00.modes;
+- sd->gspca_dev.cam.nmodes = stv06xx_sensor_hdcs1x00.nmodes;
+- sd->desc.ctrls = stv06xx_sensor_hdcs1x00.ctrls;
+- sd->desc.nctrls = stv06xx_sensor_hdcs1x00.nctrls;
++ sd->gspca_dev.cam.cam_mode = hdcs1x00_mode;
++ sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1x00_mode);
++ sd->desc.ctrls = hdcs1x00_ctrl;
++ sd->desc.nctrls = ARRAY_SIZE(hdcs1x00_ctrl);
+
+ hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL);
+ if (!hdcs)
+@@ -412,10 +472,10 @@ static int hdcs_probe_1020(struct sd *sd)
+
+ info("HDCS-1020 sensor detected");
+
+- sd->gspca_dev.cam.cam_mode = stv06xx_sensor_hdcs1020.modes;
+- sd->gspca_dev.cam.nmodes = stv06xx_sensor_hdcs1020.nmodes;
+- sd->desc.ctrls = stv06xx_sensor_hdcs1020.ctrls;
+- sd->desc.nctrls = stv06xx_sensor_hdcs1020.nctrls;
++ sd->gspca_dev.cam.cam_mode = hdcs1020_mode;
++ sd->gspca_dev.cam.nmodes = ARRAY_SIZE(hdcs1020_mode);
++ sd->desc.ctrls = hdcs1020_ctrl;
++ sd->desc.nctrls = ARRAY_SIZE(hdcs1020_ctrl);
+
+ hdcs = kmalloc(sizeof(struct hdcs), GFP_KERNEL);
+ if (!hdcs)
+diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
+index 9c7279a..412f06c 100644
+--- a/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
++++ b/drivers/media/video/gspca/stv06xx/stv06xx_hdcs.h
+@@ -152,53 +152,6 @@ const struct stv06xx_sensor stv06xx_sensor_hdcs1x00 = {
+ .stop = hdcs_stop,
+ .disconnect = hdcs_disconnect,
+ .dump = hdcs_dump,
+-
+- .nctrls = 2,
+- .ctrls = {
+- {
+- {
+- .id = V4L2_CID_EXPOSURE,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- .name = "exposure",
+- .minimum = 0x00,
+- .maximum = 0xffff,
+- .step = 0x1,
+- .default_value = HDCS_DEFAULT_EXPOSURE,
+- .flags = V4L2_CTRL_FLAG_SLIDER
+- },
+- .set = hdcs_set_exposure,
+- .get = hdcs_get_exposure
+- },
+- {
+- {
+- .id = V4L2_CID_GAIN,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- .name = "gain",
+- .minimum = 0x00,
+- .maximum = 0xff,
+- .step = 0x1,
+- .default_value = HDCS_DEFAULT_GAIN,
+- .flags = V4L2_CTRL_FLAG_SLIDER
+- },
+- .set = hdcs_set_gain,
+- .get = hdcs_get_gain
+- }
+- },
+-
+- .nmodes = 1,
+- .modes = {
+- {
+- HDCS_1X00_DEF_WIDTH,
+- HDCS_1X00_DEF_HEIGHT,
+- V4L2_PIX_FMT_SBGGR8,
+- V4L2_FIELD_NONE,
+- .sizeimage =
+- HDCS_1X00_DEF_WIDTH * HDCS_1X00_DEF_HEIGHT,
+- .bytesperline = HDCS_1X00_DEF_WIDTH,
+- .colorspace = V4L2_COLORSPACE_SRGB,
+- .priv = 1
+- }
+- }
+ };
+
+ const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = {
+@@ -207,29 +160,11 @@ const struct stv06xx_sensor stv06xx_sensor_hdcs1020 = {
+ .i2c_addr = (0x55 << 1),
+ .i2c_len = 1,
+
+- .nctrls = 0,
+- .ctrls = {},
+-
+ .init = hdcs_init,
+ .probe = hdcs_probe_1020,
+ .start = hdcs_start,
+ .stop = hdcs_stop,
+ .dump = hdcs_dump,
+-
+- .nmodes = 1,
+- .modes = {
+- {
+- HDCS_1020_DEF_WIDTH,
+- HDCS_1020_DEF_HEIGHT,
+- V4L2_PIX_FMT_SBGGR8,
+- V4L2_FIELD_NONE,
+- .sizeimage =
+- HDCS_1020_DEF_WIDTH * HDCS_1020_DEF_HEIGHT,
+- .bytesperline = HDCS_1020_DEF_WIDTH,
+- .colorspace = V4L2_COLORSPACE_SRGB,
+- .priv = 1
+- }
+- }
+ };
+
+ static const u16 stv_bridge_init[][2] = {
+diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
+index d0a0f85..285221e 100644
+--- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
++++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.c
+@@ -46,6 +46,132 @@
+
+ #include "stv06xx_pb0100.h"
+
++static const struct ctrl pb0100_ctrl[] = {
++#define GAIN_IDX 0
++ {
++ {
++ .id = V4L2_CID_GAIN,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Gain",
++ .minimum = 0,
++ .maximum = 255,
++ .step = 1,
++ .default_value = 128
++ },
++ .set = pb0100_set_gain,
++ .get = pb0100_get_gain
++ },
++#define RED_BALANCE_IDX 1
++ {
++ {
++ .id = V4L2_CID_RED_BALANCE,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Red Balance",
++ .minimum = -255,
++ .maximum = 255,
++ .step = 1,
++ .default_value = 0
++ },
++ .set = pb0100_set_red_balance,
++ .get = pb0100_get_red_balance
++ },
++#define BLUE_BALANCE_IDX 2
++ {
++ {
++ .id = V4L2_CID_BLUE_BALANCE,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Blue Balance",
++ .minimum = -255,
++ .maximum = 255,
++ .step = 1,
++ .default_value = 0
++ },
++ .set = pb0100_set_blue_balance,
++ .get = pb0100_get_blue_balance
++ },
++#define EXPOSURE_IDX 3
++ {
++ {
++ .id = V4L2_CID_EXPOSURE,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Exposure",
++ .minimum = 0,
++ .maximum = 511,
++ .step = 1,
++ .default_value = 12
++ },
++ .set = pb0100_set_exposure,
++ .get = pb0100_get_exposure
++ },
++#define AUTOGAIN_IDX 4
++ {
++ {
++ .id = V4L2_CID_AUTOGAIN,
++ .type = V4L2_CTRL_TYPE_BOOLEAN,
++ .name = "Automatic Gain and Exposure",
++ .minimum = 0,
++ .maximum = 1,
++ .step = 1,
++ .default_value = 1
++ },
++ .set = pb0100_set_autogain,
++ .get = pb0100_get_autogain
++ },
++#define AUTOGAIN_TARGET_IDX 5
++ {
++ {
++ .id = V4L2_CTRL_CLASS_USER + 0x1000,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Automatic Gain Target",
++ .minimum = 0,
++ .maximum = 255,
++ .step = 1,
++ .default_value = 128
++ },
++ .set = pb0100_set_autogain_target,
++ .get = pb0100_get_autogain_target
++ },
++#define NATURAL_IDX 6
++ {
++ {
++ .id = V4L2_CTRL_CLASS_USER + 0x1001,
++ .type = V4L2_CTRL_TYPE_BOOLEAN,
++ .name = "Natural Light Source",
++ .minimum = 0,
++ .maximum = 1,
++ .step = 1,
++ .default_value = 1
++ },
++ .set = pb0100_set_natural,
++ .get = pb0100_get_natural
++ }
++};
++
++static struct v4l2_pix_format pb0100_mode[] = {
++/* low res / subsample modes disabled as they are only half res horizontal,
++ halving the vertical resolution does not seem to work */
++ {
++ 320,
++ 240,
++ V4L2_PIX_FMT_SGRBG8,
++ V4L2_FIELD_NONE,
++ .sizeimage = 320 * 240,
++ .bytesperline = 320,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = PB0100_CROP_TO_VGA
++ },
++ {
++ 352,
++ 288,
++ V4L2_PIX_FMT_SGRBG8,
++ V4L2_FIELD_NONE,
++ .sizeimage = 352 * 288,
++ .bytesperline = 352,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 0
++ }
++};
++
+ static int pb0100_probe(struct sd *sd)
+ {
+ u16 sensor;
+@@ -59,20 +185,19 @@ static int pb0100_probe(struct sd *sd)
+
+ if ((sensor >> 8) == 0x64) {
+ sensor_settings = kmalloc(
+- stv06xx_sensor_pb0100.nctrls * sizeof(s32),
++ ARRAY_SIZE(pb0100_ctrl) * sizeof(s32),
+ GFP_KERNEL);
+ if (!sensor_settings)
+ return -ENOMEM;
+
+ info("Photobit pb0100 sensor detected");
+
+- sd->gspca_dev.cam.cam_mode = stv06xx_sensor_pb0100.modes;
+- sd->gspca_dev.cam.nmodes = stv06xx_sensor_pb0100.nmodes;
+- sd->desc.ctrls = stv06xx_sensor_pb0100.ctrls;
+- sd->desc.nctrls = stv06xx_sensor_pb0100.nctrls;
+- for (i = 0; i < stv06xx_sensor_pb0100.nctrls; i++)
+- sensor_settings[i] = stv06xx_sensor_pb0100.
+- ctrls[i].qctrl.default_value;
++ sd->gspca_dev.cam.cam_mode = pb0100_mode;
++ sd->gspca_dev.cam.nmodes = ARRAY_SIZE(pb0100_mode);
++ sd->desc.ctrls = pb0100_ctrl;
++ sd->desc.nctrls = ARRAY_SIZE(pb0100_ctrl);
++ for (i = 0; i < sd->desc.nctrls; i++)
++ sensor_settings[i] = pb0100_ctrl[i].qctrl.default_value;
+ sd->sensor_priv = sensor_settings;
+
+ return 0;
+@@ -143,6 +268,12 @@ out:
+ return (err < 0) ? err : 0;
+ }
+
++static void pb0100_disconnect(struct sd *sd)
++{
++ sd->sensor = NULL;
++ kfree(sd->sensor_priv);
++}
++
+ /* FIXME: Sort the init commands out and put them into tables,
+ this is only for getting the camera to work */
+ /* FIXME: No error handling for now,
+diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h
+index 5ea21a1..4de4fa5 100644
+--- a/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h
++++ b/drivers/media/video/gspca/stv06xx/stv06xx_pb0100.h
+@@ -114,6 +114,7 @@ static int pb0100_start(struct sd *sd);
+ static int pb0100_init(struct sd *sd);
+ static int pb0100_stop(struct sd *sd);
+ static int pb0100_dump(struct sd *sd);
++static void pb0100_disconnect(struct sd *sd);
+
+ /* V4L2 controls supported by the driver */
+ static int pb0100_get_gain(struct gspca_dev *gspca_dev, __s32 *val);
+@@ -137,139 +138,12 @@ const struct stv06xx_sensor stv06xx_sensor_pb0100 = {
+ .i2c_addr = 0xba,
+ .i2c_len = 2,
+
+- .nctrls = 7,
+- .ctrls = {
+-#define GAIN_IDX 0
+- {
+- {
+- .id = V4L2_CID_GAIN,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- .name = "Gain",
+- .minimum = 0,
+- .maximum = 255,
+- .step = 1,
+- .default_value = 128
+- },
+- .set = pb0100_set_gain,
+- .get = pb0100_get_gain
+- },
+-#define RED_BALANCE_IDX 1
+- {
+- {
+- .id = V4L2_CID_RED_BALANCE,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- .name = "Red Balance",
+- .minimum = -255,
+- .maximum = 255,
+- .step = 1,
+- .default_value = 0
+- },
+- .set = pb0100_set_red_balance,
+- .get = pb0100_get_red_balance
+- },
+-#define BLUE_BALANCE_IDX 2
+- {
+- {
+- .id = V4L2_CID_BLUE_BALANCE,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- .name = "Blue Balance",
+- .minimum = -255,
+- .maximum = 255,
+- .step = 1,
+- .default_value = 0
+- },
+- .set = pb0100_set_blue_balance,
+- .get = pb0100_get_blue_balance
+- },
+-#define EXPOSURE_IDX 3
+- {
+- {
+- .id = V4L2_CID_EXPOSURE,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- .name = "Exposure",
+- .minimum = 0,
+- .maximum = 511,
+- .step = 1,
+- .default_value = 12
+- },
+- .set = pb0100_set_exposure,
+- .get = pb0100_get_exposure
+- },
+-#define AUTOGAIN_IDX 4
+- {
+- {
+- .id = V4L2_CID_AUTOGAIN,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- .name = "Automatic Gain and Exposure",
+- .minimum = 0,
+- .maximum = 1,
+- .step = 1,
+- .default_value = 1
+- },
+- .set = pb0100_set_autogain,
+- .get = pb0100_get_autogain
+- },
+-#define AUTOGAIN_TARGET_IDX 5
+- {
+- {
+- .id = V4L2_CTRL_CLASS_USER + 0x1000,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- .name = "Automatic Gain Target",
+- .minimum = 0,
+- .maximum = 255,
+- .step = 1,
+- .default_value = 128
+- },
+- .set = pb0100_set_autogain_target,
+- .get = pb0100_get_autogain_target
+- },
+-#define NATURAL_IDX 6
+- {
+- {
+- .id = V4L2_CTRL_CLASS_USER + 0x1001,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- .name = "Natural Light Source",
+- .minimum = 0,
+- .maximum = 1,
+- .step = 1,
+- .default_value = 1
+- },
+- .set = pb0100_set_natural,
+- .get = pb0100_get_natural
+- },
+- },
+-
+ .init = pb0100_init,
+ .probe = pb0100_probe,
+ .start = pb0100_start,
+ .stop = pb0100_stop,
+ .dump = pb0100_dump,
+-
+- .nmodes = 2,
+- .modes = {
+-/* low res / subsample modes disabled as they are only half res horizontal,
+- halving the vertical resolution does not seem to work */
+- {
+- 320,
+- 240,
+- V4L2_PIX_FMT_SGRBG8,
+- V4L2_FIELD_NONE,
+- .sizeimage = 320 * 240,
+- .bytesperline = 320,
+- .colorspace = V4L2_COLORSPACE_SRGB,
+- .priv = PB0100_CROP_TO_VGA
+- },
+- {
+- 352,
+- 288,
+- V4L2_PIX_FMT_SGRBG8,
+- V4L2_FIELD_NONE,
+- .sizeimage = 352 * 288,
+- .bytesperline = 352,
+- .colorspace = V4L2_COLORSPACE_SRGB,
+- .priv = 0
+- },
+- }
++ .disconnect = pb0100_disconnect,
+ };
+
+ #endif
+diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h
+index c726dac..e88c42f 100644
+--- a/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h
++++ b/drivers/media/video/gspca/stv06xx/stv06xx_sensor.h
+@@ -41,8 +41,6 @@ extern const struct stv06xx_sensor stv06xx_sensor_hdcs1x00;
+ extern const struct stv06xx_sensor stv06xx_sensor_hdcs1020;
+ extern const struct stv06xx_sensor stv06xx_sensor_pb0100;
+
+-#define STV06XX_MAX_CTRLS (V4L2_CID_LASTP1 - V4L2_CID_BASE + 10)
+-
+ struct stv06xx_sensor {
+ /* Defines the name of a sensor */
+ char name[32];
+@@ -81,12 +79,6 @@ struct stv06xx_sensor {
+
+ /* Instructs the sensor to dump all its contents */
+ int (*dump)(struct sd *sd);
+-
+- int nctrls;
+- struct ctrl ctrls[STV06XX_MAX_CTRLS];
+-
+- char nmodes;
+- struct v4l2_pix_format modes[];
+ };
+
+ #endif
+diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
+index 1ca91f2..69c77c9 100644
+--- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
++++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.c
+@@ -29,26 +29,92 @@
+
+ #include "stv06xx_vv6410.h"
+
++static struct v4l2_pix_format vv6410_mode[] = {
++ {
++ 356,
++ 292,
++ V4L2_PIX_FMT_SGRBG8,
++ V4L2_FIELD_NONE,
++ .sizeimage = 356 * 292,
++ .bytesperline = 356,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 0
++ }
++};
++
++static const struct ctrl vv6410_ctrl[] = {
++#define HFLIP_IDX 0
++ {
++ {
++ .id = V4L2_CID_HFLIP,
++ .type = V4L2_CTRL_TYPE_BOOLEAN,
++ .name = "horizontal flip",
++ .minimum = 0,
++ .maximum = 1,
++ .step = 1,
++ .default_value = 0
++ },
++ .set = vv6410_set_hflip,
++ .get = vv6410_get_hflip
++ },
++#define VFLIP_IDX 1
++ {
++ {
++ .id = V4L2_CID_VFLIP,
++ .type = V4L2_CTRL_TYPE_BOOLEAN,
++ .name = "vertical flip",
++ .minimum = 0,
++ .maximum = 1,
++ .step = 1,
++ .default_value = 0
++ },
++ .set = vv6410_set_vflip,
++ .get = vv6410_get_vflip
++ },
++#define GAIN_IDX 2
++ {
++ {
++ .id = V4L2_CID_GAIN,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "analog gain",
++ .minimum = 0,
++ .maximum = 15,
++ .step = 1,
++ .default_value = 0
++ },
++ .set = vv6410_set_analog_gain,
++ .get = vv6410_get_analog_gain
++ }
++};
++
+ static int vv6410_probe(struct sd *sd)
+ {
+ u16 data;
+- int err;
++ int err, i;
++ s32 *sensor_settings;
+
+ err = stv06xx_read_sensor(sd, VV6410_DEVICEH, &data);
+-
+ if (err < 0)
+ return -ENODEV;
+
+ if (data == 0x19) {
+ info("vv6410 sensor detected");
+
+- sd->gspca_dev.cam.cam_mode = stv06xx_sensor_vv6410.modes;
+- sd->gspca_dev.cam.nmodes = stv06xx_sensor_vv6410.nmodes;
+- sd->desc.ctrls = stv06xx_sensor_vv6410.ctrls;
+- sd->desc.nctrls = stv06xx_sensor_vv6410.nctrls;
++ sensor_settings = kmalloc(ARRAY_SIZE(vv6410_ctrl) * sizeof(s32),
++ GFP_KERNEL);
++ if (!sensor_settings)
++ return -ENOMEM;
++
++ sd->gspca_dev.cam.cam_mode = vv6410_mode;
++ sd->gspca_dev.cam.nmodes = ARRAY_SIZE(vv6410_mode);
++ sd->desc.ctrls = vv6410_ctrl;
++ sd->desc.nctrls = ARRAY_SIZE(vv6410_ctrl);
++
++ for (i = 0; i < sd->desc.nctrls; i++)
++ sensor_settings[i] = vv6410_ctrl[i].qctrl.default_value;
++ sd->sensor_priv = sensor_settings;
+ return 0;
+ }
+-
+ return -ENODEV;
+ }
+
+@@ -80,6 +146,12 @@ static int vv6410_init(struct sd *sd)
+ return (err < 0) ? err : 0;
+ }
+
++static void vv6410_disconnect(struct sd *sd)
++{
++ sd->sensor = NULL;
++ kfree(sd->sensor_priv);
++}
++
+ static int vv6410_start(struct sd *sd)
+ {
+ int err;
+@@ -156,17 +228,13 @@ static int vv6410_dump(struct sd *sd)
+
+ static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+- int err;
+- u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
++ s32 *sensor_settings = sd->sensor_priv;
+
+- err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+-
+- *val = (i2c_data & VV6410_HFLIP) ? 1 : 0;
+-
++ *val = sensor_settings[HFLIP_IDX];
+ PDEBUG(D_V4L2, "Read horizontal flip %d", *val);
+
+- return (err < 0) ? err : 0;
++ return 0;
+ }
+
+ static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+@@ -174,6 +242,9 @@ static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
++ s32 *sensor_settings = sd->sensor_priv;
++
++ sensor_settings[HFLIP_IDX] = val;
+ err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+ if (err < 0)
+ return err;
+@@ -191,17 +262,13 @@ static int vv6410_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+
+ static int vv6410_get_vflip(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+- int err;
+- u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
++ s32 *sensor_settings = sd->sensor_priv;
+
+- err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+-
+- *val = (i2c_data & VV6410_VFLIP) ? 1 : 0;
+-
++ *val = sensor_settings[VFLIP_IDX];
+ PDEBUG(D_V4L2, "Read vertical flip %d", *val);
+
+- return (err < 0) ? err : 0;
++ return 0;
+ }
+
+ static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+@@ -209,6 +276,9 @@ static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+ int err;
+ u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
++ s32 *sensor_settings = sd->sensor_priv;
++
++ sensor_settings[VFLIP_IDX] = val;
+ err = stv06xx_read_sensor(sd, VV6410_DATAFORMAT, &i2c_data);
+ if (err < 0)
+ return err;
+@@ -226,24 +296,23 @@ static int vv6410_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+
+ static int vv6410_get_analog_gain(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+- int err;
+- u16 i2c_data;
+ struct sd *sd = (struct sd *) gspca_dev;
++ s32 *sensor_settings = sd->sensor_priv;
+
+- err = stv06xx_read_sensor(sd, VV6410_ANALOGGAIN, &i2c_data);
+-
+- *val = i2c_data & 0xf;
++ *val = sensor_settings[GAIN_IDX];
+
+ PDEBUG(D_V4L2, "Read analog gain %d", *val);
+
+- return (err < 0) ? err : 0;
++ return 0;
+ }
+
+ static int vv6410_set_analog_gain(struct gspca_dev *gspca_dev, __s32 val)
+ {
+ int err;
+ struct sd *sd = (struct sd *) gspca_dev;
++ s32 *sensor_settings = sd->sensor_priv;
+
++ sensor_settings[GAIN_IDX] = val;
+ PDEBUG(D_V4L2, "Set analog gain to %d", val);
+ err = stv06xx_write_sensor(sd, VV6410_ANALOGGAIN, 0xf0 | (val & 0xf));
+
+diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
+index 3ff8c4e..95ac558 100644
+--- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
++++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
+@@ -178,6 +178,7 @@ static int vv6410_start(struct sd *sd);
+ static int vv6410_init(struct sd *sd);
+ static int vv6410_stop(struct sd *sd);
+ static int vv6410_dump(struct sd *sd);
++static void vv6410_disconnect(struct sd *sd);
+
+ /* V4L2 controls supported by the driver */
+ static int vv6410_get_hflip(struct gspca_dev *gspca_dev, __s32 *val);
+@@ -197,62 +198,7 @@ const struct stv06xx_sensor stv06xx_sensor_vv6410 = {
+ .start = vv6410_start,
+ .stop = vv6410_stop,
+ .dump = vv6410_dump,
+-
+- .nctrls = 3,
+- .ctrls = {
+- {
+- {
+- .id = V4L2_CID_HFLIP,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- .name = "horizontal flip",
+- .minimum = 0,
+- .maximum = 1,
+- .step = 1,
+- .default_value = 0
+- },
+- .set = vv6410_set_hflip,
+- .get = vv6410_get_hflip
+- }, {
+- {
+- .id = V4L2_CID_VFLIP,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- .name = "vertical flip",
+- .minimum = 0,
+- .maximum = 1,
+- .step = 1,
+- .default_value = 0
+- },
+- .set = vv6410_set_vflip,
+- .get = vv6410_get_vflip
+- }, {
+- {
+- .id = V4L2_CID_GAIN,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- .name = "analog gain",
+- .minimum = 0,
+- .maximum = 15,
+- .step = 1,
+- .default_value = 0
+- },
+- .set = vv6410_set_analog_gain,
+- .get = vv6410_get_analog_gain
+- }
+- },
+-
+- .nmodes = 1,
+- .modes = {
+- {
+- 356,
+- 292,
+- V4L2_PIX_FMT_SGRBG8,
+- V4L2_FIELD_NONE,
+- .sizeimage =
+- 356 * 292,
+- .bytesperline = 356,
+- .colorspace = V4L2_COLORSPACE_SRGB,
+- .priv = 0
+- }
+- }
++ .disconnect = vv6410_disconnect,
+ };
+
+ /* If NULL, only single value to write, stored in len */
+diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c
+index 6d904d5..c2b8c10 100644
+--- a/drivers/media/video/gspca/sunplus.c
++++ b/drivers/media/video/gspca/sunplus.c
+@@ -39,8 +39,11 @@ struct sd {
+ unsigned char contrast;
+ unsigned char colors;
+ unsigned char autogain;
++ u8 quality;
++#define QUALITY_MIN 70
++#define QUALITY_MAX 95
++#define QUALITY_DEF 85
+
+- char qindex;
+ char bridge;
+ #define BRIDGE_SPCA504 0
+ #define BRIDGE_SPCA504B 1
+@@ -52,6 +55,8 @@ struct sd {
+ #define LogitechClickSmart420 2
+ #define LogitechClickSmart820 3
+ #define MegapixV4 4
++
++ u8 *jpeg_hdr;
+ };
+
+ /* V4L2 controls supported by the driver */
+@@ -812,7 +817,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = 0x01;
+
+ sd->bridge = id->driver_info >> 8;
+ sd->subtype = id->driver_info;
+@@ -850,10 +854,10 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ cam->nmodes = sizeof vga_mode2 / sizeof vga_mode2[0];
+ break;
+ }
+- sd->qindex = 5; /* set the quantization table */
+ sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
+ sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
+ sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value;
++ sd->quality = QUALITY_DEF;
+ return 0;
+ }
+
+@@ -970,6 +974,12 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ __u8 i;
+ __u8 info[6];
+
++ /* create the JPEG header */
++ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
++ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
++ 0x22); /* JPEG 411 */
++ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
++
+ if (sd->bridge == BRIDGE_SPCA504B)
+ spca504B_setQtable(gspca_dev);
+ spca504B_SetSizeType(gspca_dev);
+@@ -1079,6 +1089,13 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
+ }
+ }
+
++static void sd_stop0(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ kfree(sd->jpeg_hdr);
++}
++
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ struct gspca_frame *frame, /* target */
+ __u8 *data, /* isoc packet */
+@@ -1155,9 +1172,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ ffd9, 2);
+
+ /* put the JPEG header in the new frame */
+- jpeg_put_header(gspca_dev, frame,
+- ((struct sd *) gspca_dev)->qindex,
+- 0x22);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ sd->jpeg_hdr, JPEG_HDR_SZ);
+ }
+
+ /* add 0x00 after 0xff */
+@@ -1198,26 +1214,6 @@ static void setbrightness(struct gspca_dev *gspca_dev)
+ }
+ }
+
+-static void getbrightness(struct gspca_dev *gspca_dev)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+- __u16 brightness = 0;
+-
+- switch (sd->bridge) {
+- default:
+-/* case BRIDGE_SPCA533: */
+-/* case BRIDGE_SPCA504B: */
+-/* case BRIDGE_SPCA504: */
+-/* case BRIDGE_SPCA504C: */
+- brightness = reg_r_12(gspca_dev, 0x00, 0x21a7, 2);
+- break;
+- case BRIDGE_SPCA536:
+- brightness = reg_r_12(gspca_dev, 0x00, 0x20f0, 2);
+- break;
+- }
+- sd->brightness = ((brightness & 0xff) - 128) % 255;
+-}
+-
+ static void setcontrast(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -1237,24 +1233,6 @@ static void setcontrast(struct gspca_dev *gspca_dev)
+ }
+ }
+
+-static void getcontrast(struct gspca_dev *gspca_dev)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- switch (sd->bridge) {
+- default:
+-/* case BRIDGE_SPCA533: */
+-/* case BRIDGE_SPCA504B: */
+-/* case BRIDGE_SPCA504: */
+-/* case BRIDGE_SPCA504C: */
+- sd->contrast = reg_r_12(gspca_dev, 0x00, 0x21a8, 2);
+- break;
+- case BRIDGE_SPCA536:
+- sd->contrast = reg_r_12(gspca_dev, 0x00, 0x20f1, 2);
+- break;
+- }
+-}
+-
+ static void setcolors(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -1274,24 +1252,6 @@ static void setcolors(struct gspca_dev *gspca_dev)
+ }
+ }
+
+-static void getcolors(struct gspca_dev *gspca_dev)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- switch (sd->bridge) {
+- default:
+-/* case BRIDGE_SPCA533: */
+-/* case BRIDGE_SPCA504B: */
+-/* case BRIDGE_SPCA504: */
+-/* case BRIDGE_SPCA504C: */
+- sd->colors = reg_r_12(gspca_dev, 0x00, 0x21ae, 2) >> 1;
+- break;
+- case BRIDGE_SPCA536:
+- sd->colors = reg_r_12(gspca_dev, 0x00, 0x20f6, 2) >> 1;
+- break;
+- }
+-}
+-
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -1306,7 +1266,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- getbrightness(gspca_dev);
+ *val = sd->brightness;
+ return 0;
+ }
+@@ -1325,7 +1284,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- getcontrast(gspca_dev);
+ *val = sd->contrast;
+ return 0;
+ }
+@@ -1344,7 +1302,6 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- getcolors(gspca_dev);
+ *val = sd->colors;
+ return 0;
+ }
+@@ -1365,6 +1322,34 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+ return 0;
+ }
+
++static int sd_set_jcomp(struct gspca_dev *gspca_dev,
++ struct v4l2_jpegcompression *jcomp)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (jcomp->quality < QUALITY_MIN)
++ sd->quality = QUALITY_MIN;
++ else if (jcomp->quality > QUALITY_MAX)
++ sd->quality = QUALITY_MAX;
++ else
++ sd->quality = jcomp->quality;
++ if (gspca_dev->streaming)
++ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
++ return 0;
++}
++
++static int sd_get_jcomp(struct gspca_dev *gspca_dev,
++ struct v4l2_jpegcompression *jcomp)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ memset(jcomp, 0, sizeof *jcomp);
++ jcomp->quality = sd->quality;
++ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
++ | V4L2_JPEG_MARKER_DQT;
++ return 0;
++}
++
+ /* sub-driver description */
+ static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+@@ -1374,7 +1359,10 @@ static const struct sd_desc sd_desc = {
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
++ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
++ .get_jcomp = sd_get_jcomp,
++ .set_jcomp = sd_set_jcomp,
+ };
+
+ /* -- module initialisation -- */
+@@ -1465,8 +1453,10 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c
+index 6ee111a..f63e37e 100644
+--- a/drivers/media/video/gspca/t613.c
++++ b/drivers/media/video/gspca/t613.c
+@@ -37,20 +37,21 @@ MODULE_LICENSE("GPL");
+ struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+- unsigned char brightness;
+- unsigned char contrast;
+- unsigned char colors;
+- unsigned char autogain;
+- unsigned char gamma;
+- unsigned char sharpness;
+- unsigned char freq;
+- unsigned char whitebalance;
+- unsigned char mirror;
+- unsigned char effect;
+-
+- __u8 sensor;
+-#define SENSOR_TAS5130A 0
+-#define SENSOR_OM6802 1
++ u8 brightness;
++ u8 contrast;
++ u8 colors;
++ u8 autogain;
++ u8 gamma;
++ u8 sharpness;
++ u8 freq;
++ u8 whitebalance;
++ u8 mirror;
++ u8 effect;
++
++ u8 sensor;
++#define SENSOR_OM6802 0
++#define SENSOR_OTHER 1
++#define SENSOR_TAS5130A 2
+ };
+
+ /* V4L2 controls supported by the driver */
+@@ -78,7 +79,6 @@ static int sd_querymenu(struct gspca_dev *gspca_dev,
+ struct v4l2_querymenu *menu);
+
+ static struct ctrl sd_ctrls[] = {
+-#define SD_BRIGHTNESS 0
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+@@ -87,12 +87,12 @@ static struct ctrl sd_ctrls[] = {
+ .minimum = 0,
+ .maximum = 14,
+ .step = 1,
+- .default_value = 8,
++#define BRIGHTNESS_DEF 8
++ .default_value = BRIGHTNESS_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+-#define SD_CONTRAST 1
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+@@ -101,12 +101,12 @@ static struct ctrl sd_ctrls[] = {
+ .minimum = 0,
+ .maximum = 0x0d,
+ .step = 1,
+- .default_value = 0x07,
++#define CONTRAST_DEF 0x07
++ .default_value = CONTRAST_DEF,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+-#define SD_COLOR 2
+ {
+ {
+ .id = V4L2_CID_SATURATION,
+@@ -115,7 +115,8 @@ static struct ctrl sd_ctrls[] = {
+ .minimum = 0,
+ .maximum = 0x0f,
+ .step = 1,
+- .default_value = 0x05,
++#define COLORS_DEF 0x05
++ .default_value = COLORS_DEF,
+ },
+ .set = sd_setcolors,
+ .get = sd_getcolors,
+@@ -135,7 +136,6 @@ static struct ctrl sd_ctrls[] = {
+ .set = sd_setgamma,
+ .get = sd_getgamma,
+ },
+-#define SD_AUTOGAIN 4
+ {
+ {
+ .id = V4L2_CID_GAIN, /* here, i activate only the lowlight,
+@@ -146,12 +146,12 @@ static struct ctrl sd_ctrls[] = {
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+- .default_value = 0x01,
++#define AUTOGAIN_DEF 0x01
++ .default_value = AUTOGAIN_DEF,
+ },
+ .set = sd_setlowlight,
+ .get = sd_getlowlight,
+ },
+-#define SD_MIRROR 5
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+@@ -160,12 +160,12 @@ static struct ctrl sd_ctrls[] = {
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+- .default_value = 0,
++#define MIRROR_DEF 0
++ .default_value = MIRROR_DEF,
+ },
+ .set = sd_setflip,
+ .get = sd_getflip
+ },
+-#define SD_LIGHTFREQ 6
+ {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+@@ -174,12 +174,12 @@ static struct ctrl sd_ctrls[] = {
+ .minimum = 1, /* 1 -> 0x50, 2->0x60 */
+ .maximum = 2,
+ .step = 1,
+- .default_value = 1,
++#define FREQ_DEF 1
++ .default_value = FREQ_DEF,
+ },
+ .set = sd_setfreq,
+ .get = sd_getfreq},
+
+-#define SD_WHITE_BALANCE 7
+ {
+ {
+ .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
+@@ -188,12 +188,12 @@ static struct ctrl sd_ctrls[] = {
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+- .default_value = 0,
++#define WHITE_BALANCE_DEF 0
++ .default_value = WHITE_BALANCE_DEF,
+ },
+ .set = sd_setwhitebalance,
+ .get = sd_getwhitebalance
+ },
+-#define SD_SHARPNESS 8 /* (aka definition on win) */
+ {
+ {
+ .id = V4L2_CID_SHARPNESS,
+@@ -202,12 +202,12 @@ static struct ctrl sd_ctrls[] = {
+ .minimum = 0,
+ .maximum = 15,
+ .step = 1,
+- .default_value = 0x06,
++#define SHARPNESS_DEF 0x06
++ .default_value = SHARPNESS_DEF,
+ },
+ .set = sd_setsharpness,
+ .get = sd_getsharpness,
+ },
+-#define SD_EFFECTS 9
+ {
+ {
+ .id = V4L2_CID_EFFECTS,
+@@ -216,7 +216,8 @@ static struct ctrl sd_ctrls[] = {
+ .minimum = 0,
+ .maximum = 4,
+ .step = 1,
+- .default_value = 0,
++#define EFFECTS_DEF 0
++ .default_value = EFFECTS_DEF,
+ },
+ .set = sd_seteffect,
+ .get = sd_geteffect
+@@ -263,28 +264,50 @@ static const struct v4l2_pix_format vga_mode_t16[] = {
+
+ /* sensor specific data */
+ struct additional_sensor_data {
+- const __u8 data1[20];
+- const __u8 data2[18];
+- const __u8 data3[18];
+- const __u8 data4[4];
+- const __u8 data5[6];
+- const __u8 stream[4];
++ const u8 data1[10];
++ const u8 data2[9];
++ const u8 data3[9];
++ const u8 data4[4];
++ const u8 data5[6];
++ const u8 stream[4];
+ };
+
+-const static struct additional_sensor_data sensor_data[] = {
++static const struct additional_sensor_data sensor_data[] = {
++ { /* OM6802 */
++ .data1 =
++ {0xc2, 0x28, 0x0f, 0x22, 0xcd, 0x27, 0x2c, 0x06,
++ 0xb3, 0xfc},
++ .data2 =
++ {0x80, 0xff, 0xff, 0x80, 0xff, 0xff, 0x80, 0xff,
++ 0xff},
++ .data4 = /*Freq (50/60Hz). Splitted for test purpose */
++ {0x66, 0xca, 0xa8, 0xf0},
++ .data5 = /* this could be removed later */
++ {0x0c, 0x03, 0xab, 0x13, 0x81, 0x23},
++ .stream =
++ {0x0b, 0x04, 0x0a, 0x78},
++ },
++ { /* OTHER */
++ .data1 =
++ {0xc1, 0x48, 0x04, 0x1b, 0xca, 0x2e, 0x33, 0x3a,
++ 0xe8, 0xfc},
++ .data2 =
++ {0x4e, 0x9c, 0xec, 0x40, 0x80, 0xc0, 0x48, 0x96,
++ 0xd9},
++ .data4 =
++ {0x66, 0x00, 0xa8, 0xa8},
++ .data5 =
++ {0x0c, 0x03, 0xab, 0x29, 0x81, 0x69},
++ .stream =
++ {0x0b, 0x04, 0x0a, 0x00},
++ },
+ { /* TAS5130A */
+ .data1 =
+- {0xd0, 0xbb, 0xd1, 0x28, 0xd2, 0x10, 0xd3, 0x10,
+- 0xd4, 0xbb, 0xd5, 0x28, 0xd6, 0x1e, 0xd7, 0x27,
+- 0xd8, 0xc8, 0xd9, 0xfc},
++ {0xbb, 0x28, 0x10, 0x10, 0xbb, 0x28, 0x1e, 0x27,
++ 0xc8, 0xfc},
+ .data2 =
+- {0xe0, 0x60, 0xe1, 0xa8, 0xe2, 0xe0, 0xe3, 0x60,
+- 0xe4, 0xa8, 0xe5, 0xe0, 0xe6, 0x60, 0xe7, 0xa8,
+- 0xe8, 0xe0},
+- .data3 =
+- {0xc7, 0x60, 0xc8, 0xa8, 0xc9, 0xe0, 0xca, 0x60,
+- 0xcb, 0xa8, 0xcc, 0xe0, 0xcd, 0x60, 0xce, 0xa8,
+- 0xcf, 0xe0},
++ {0x60, 0xa8, 0xe0, 0x60, 0xa8, 0xe0, 0x60, 0xa8,
++ 0xe0},
+ .data4 = /* Freq (50/60Hz). Splitted for test purpose */
+ {0x66, 0x00, 0xa8, 0xe8},
+ .data5 =
+@@ -292,32 +315,12 @@ const static struct additional_sensor_data sensor_data[] = {
+ .stream =
+ {0x0b, 0x04, 0x0a, 0x40},
+ },
+- { /* OM6802 */
+- .data1 =
+- {0xd0, 0xc2, 0xd1, 0x28, 0xd2, 0x0f, 0xd3, 0x22,
+- 0xd4, 0xcd, 0xd5, 0x27, 0xd6, 0x2c, 0xd7, 0x06,
+- 0xd8, 0xb3, 0xd9, 0xfc},
+- .data2 =
+- {0xe0, 0x80, 0xe1, 0xff, 0xe2, 0xff, 0xe3, 0x80,
+- 0xe4, 0xff, 0xe5, 0xff, 0xe6, 0x80, 0xe7, 0xff,
+- 0xe8, 0xff},
+- .data3 =
+- {0xc7, 0x80, 0xc8, 0xff, 0xc9, 0xff, 0xca, 0x80,
+- 0xcb, 0xff, 0xcc, 0xff, 0xcd, 0x80, 0xce, 0xff,
+- 0xcf, 0xff},
+- .data4 = /*Freq (50/60Hz). Splitted for test purpose */
+- {0x66, 0xca, 0xa8, 0xf0 },
+- .data5 = /* this could be removed later */
+- {0x0c, 0x03, 0xab, 0x13, 0x81, 0x23},
+- .stream =
+- {0x0b, 0x04, 0x0a, 0x78},
+- }
+ };
+
+ #define MAX_EFFECTS 7
+ /* easily done by soft, this table could be removed,
+ * i keep it here just in case */
+-static const __u8 effects_table[MAX_EFFECTS][6] = {
++static const u8 effects_table[MAX_EFFECTS][6] = {
+ {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x00}, /* Normal */
+ {0xa8, 0xc8, 0xc6, 0x52, 0xc0, 0x04}, /* Repujar */
+ {0xa8, 0xe8, 0xc6, 0xd2, 0xc0, 0x20}, /* Monochrome */
+@@ -327,90 +330,58 @@ static const __u8 effects_table[MAX_EFFECTS][6] = {
+ {0xa8, 0xc8, 0xc6, 0xd2, 0xc0, 0x40}, /* Negative */
+ };
+
+-static const __u8 gamma_table[GAMMA_MAX][34] = {
+- {0x90, 0x00, 0x91, 0x3e, 0x92, 0x69, 0x93, 0x85, /* 0 */
+- 0x94, 0x95, 0x95, 0xa1, 0x96, 0xae, 0x97, 0xb9,
+- 0x98, 0xc2, 0x99, 0xcb, 0x9a, 0xd4, 0x9b, 0xdb,
+- 0x9c, 0xe3, 0x9d, 0xea, 0x9e, 0xf1, 0x9f, 0xf8,
+- 0xa0, 0xff},
+- {0x90, 0x00, 0x91, 0x33, 0x92, 0x5a, 0x93, 0x75, /* 1 */
+- 0x94, 0x85, 0x95, 0x93, 0x96, 0xa1, 0x97, 0xad,
+- 0x98, 0xb7, 0x99, 0xc2, 0x9a, 0xcb, 0x9b, 0xd4,
+- 0x9c, 0xde, 0x9D, 0xe7, 0x9e, 0xf0, 0x9f, 0xf7,
+- 0xa0, 0xff},
+- {0x90, 0x00, 0x91, 0x2f, 0x92, 0x51, 0x93, 0x6b, /* 2 */
+- 0x94, 0x7c, 0x95, 0x8a, 0x96, 0x99, 0x97, 0xa6,
+- 0x98, 0xb1, 0x99, 0xbc, 0x9a, 0xc6, 0x9b, 0xd0,
+- 0x9c, 0xdb, 0x9d, 0xe4, 0x9e, 0xed, 0x9f, 0xf6,
+- 0xa0, 0xff},
+- {0x90, 0x00, 0x91, 0x29, 0x92, 0x48, 0x93, 0x60, /* 3 */
+- 0x94, 0x72, 0x95, 0x81, 0x96, 0x90, 0x97, 0x9e,
+- 0x98, 0xaa, 0x99, 0xb5, 0x9a, 0xbf, 0x9b, 0xcb,
+- 0x9c, 0xd6, 0x9d, 0xe1, 0x9e, 0xeb, 0x9f, 0xf5,
+- 0xa0, 0xff},
+- {0x90, 0x00, 0x91, 0x23, 0x92, 0x3f, 0x93, 0x55, /* 4 */
+- 0x94, 0x68, 0x95, 0x77, 0x96, 0x86, 0x97, 0x95,
+- 0x98, 0xa2, 0x99, 0xad, 0x9a, 0xb9, 0x9b, 0xc6,
+- 0x9c, 0xd2, 0x9d, 0xde, 0x9e, 0xe9, 0x9f, 0xf4,
+- 0xa0, 0xff},
+- {0x90, 0x00, 0x91, 0x1b, 0x92, 0x33, 0x93, 0x48, /* 5 */
+- 0x94, 0x59, 0x95, 0x69, 0x96, 0x79, 0x97, 0x87,
+- 0x98, 0x96, 0x99, 0xa3, 0x9a, 0xb1, 0x9b, 0xbe,
+- 0x9c, 0xcc, 0x9d, 0xda, 0x9e, 0xe7, 0x9f, 0xf3,
+- 0xa0, 0xff},
+- {0x90, 0x00, 0x91, 0x02, 0x92, 0x10, 0x93, 0x20, /* 6 */
+- 0x94, 0x32, 0x95, 0x40, 0x96, 0x57, 0x97, 0x67,
+- 0x98, 0x77, 0x99, 0x88, 0x9a, 0x99, 0x9b, 0xaa,
+- 0x9c, 0xbb, 0x9d, 0xcc, 0x9e, 0xdd, 0x9f, 0xee,
+- 0xa0, 0xff},
+- {0x90, 0x00, 0x91, 0x02, 0x92, 0x14, 0x93, 0x26, /* 7 */
+- 0x94, 0x38, 0x95, 0x4a, 0x96, 0x60, 0x97, 0x70,
+- 0x98, 0x80, 0x99, 0x90, 0x9a, 0xa0, 0x9b, 0xb0,
+- 0x9c, 0xc0, 0x9D, 0xd0, 0x9e, 0xe0, 0x9f, 0xf0,
+- 0xa0, 0xff},
+- {0x90, 0x00, 0x91, 0x10, 0x92, 0x22, 0x93, 0x35, /* 8 */
+- 0x94, 0x47, 0x95, 0x5a, 0x96, 0x69, 0x97, 0x79,
+- 0x98, 0x88, 0x99, 0x97, 0x9a, 0xa7, 0x9b, 0xb6,
+- 0x9c, 0xc4, 0x9d, 0xd3, 0x9e, 0xe0, 0x9f, 0xf0,
+- 0xa0, 0xff},
+- {0x90, 0x00, 0x91, 0x10, 0x92, 0x26, 0x93, 0x40, /* 9 */
+- 0x94, 0x54, 0x95, 0x65, 0x96, 0x75, 0x97, 0x84,
+- 0x98, 0x93, 0x99, 0xa1, 0x9a, 0xb0, 0x9b, 0xbd,
+- 0x9c, 0xca, 0x9d, 0xd6, 0x9e, 0xe0, 0x9f, 0xf0,
+- 0xa0, 0xff},
+- {0x90, 0x00, 0x91, 0x18, 0x92, 0x2b, 0x93, 0x44, /* 10 */
+- 0x94, 0x60, 0x95, 0x70, 0x96, 0x80, 0x97, 0x8e,
+- 0x98, 0x9c, 0x99, 0xaa, 0x9a, 0xb7, 0x9b, 0xc4,
+- 0x9c, 0xd0, 0x9d, 0xd8, 0x9e, 0xe2, 0x9f, 0xf0,
+- 0xa0, 0xff},
+- {0x90, 0x00, 0x91, 0x1a, 0x92, 0x34, 0x93, 0x52, /* 11 */
+- 0x94, 0x66, 0x95, 0x7e, 0x96, 0x8D, 0x97, 0x9B,
+- 0x98, 0xa8, 0x99, 0xb4, 0x9a, 0xc0, 0x9b, 0xcb,
+- 0x9c, 0xd6, 0x9d, 0xe1, 0x9e, 0xeb, 0x9f, 0xf5,
+- 0xa0, 0xff},
+- {0x90, 0x00, 0x91, 0x3f, 0x92, 0x5a, 0x93, 0x6e, /* 12 */
+- 0x94, 0x7f, 0x95, 0x8e, 0x96, 0x9c, 0x97, 0xa8,
+- 0x98, 0xb4, 0x99, 0xbf, 0x9a, 0xc9, 0x9b, 0xd3,
+- 0x9c, 0xdc, 0x9d, 0xe5, 0x9e, 0xee, 0x9f, 0xf6,
+- 0xa0, 0xff},
+- {0x90, 0x00, 0x91, 0x54, 0x92, 0x6f, 0x93, 0x83, /* 13 */
+- 0x94, 0x93, 0x95, 0xa0, 0x96, 0xad, 0x97, 0xb7,
+- 0x98, 0xc2, 0x99, 0xcb, 0x9a, 0xd4, 0x9b, 0xdc,
+- 0x9c, 0xe4, 0x9d, 0xeb, 0x9e, 0xf2, 0x9f, 0xf9,
+- 0xa0, 0xff},
+- {0x90, 0x00, 0x91, 0x6e, 0x92, 0x88, 0x93, 0x9a, /* 14 */
+- 0x94, 0xa8, 0x95, 0xb3, 0x96, 0xbd, 0x97, 0xc6,
+- 0x98, 0xcf, 0x99, 0xd6, 0x9a, 0xdd, 0x9b, 0xe3,
+- 0x9c, 0xe9, 0x9d, 0xef, 0x9e, 0xf4, 0x9f, 0xfa,
+- 0xa0, 0xff},
+- {0x90, 0x00, 0x91, 0x93, 0x92, 0xa8, 0x93, 0xb7, /* 15 */
+- 0x94, 0xc1, 0x95, 0xca, 0x96, 0xd2, 0x97, 0xd8,
+- 0x98, 0xde, 0x99, 0xe3, 0x9a, 0xe8, 0x9b, 0xed,
+- 0x9c, 0xf1, 0x9d, 0xf5, 0x9e, 0xf8, 0x9f, 0xfc,
+- 0xa0, 0xff}
++static const u8 gamma_table[GAMMA_MAX][17] = {
++ {0x00, 0x3e, 0x69, 0x85, 0x95, 0xa1, 0xae, 0xb9, /* 0 */
++ 0xc2, 0xcb, 0xd4, 0xdb, 0xe3, 0xea, 0xf1, 0xf8,
++ 0xff},
++ {0x00, 0x33, 0x5a, 0x75, 0x85, 0x93, 0xa1, 0xad, /* 1 */
++ 0xb7, 0xc2, 0xcb, 0xd4, 0xde, 0xe7, 0xf0, 0xf7,
++ 0xff},
++ {0x00, 0x2f, 0x51, 0x6b, 0x7c, 0x8a, 0x99, 0xa6, /* 2 */
++ 0xb1, 0xbc, 0xc6, 0xd0, 0xdb, 0xe4, 0xed, 0xf6,
++ 0xff},
++ {0x00, 0x29, 0x48, 0x60, 0x72, 0x81, 0x90, 0x9e, /* 3 */
++ 0xaa, 0xb5, 0xbf, 0xcb, 0xd6, 0xe1, 0xeb, 0xf5,
++ 0xff},
++ {0x00, 0x23, 0x3f, 0x55, 0x68, 0x77, 0x86, 0x95, /* 4 */
++ 0xa2, 0xad, 0xb9, 0xc6, 0xd2, 0xde, 0xe9, 0xf4,
++ 0xff},
++ {0x00, 0x1b, 0x33, 0x48, 0x59, 0x69, 0x79, 0x87, /* 5 */
++ 0x96, 0xa3, 0xb1, 0xbe, 0xcc, 0xda, 0xe7, 0xf3,
++ 0xff},
++ {0x00, 0x02, 0x10, 0x20, 0x32, 0x40, 0x57, 0x67, /* 6 */
++ 0x77, 0x88, 0x99, 0xaa, 0xbb, 0xcc, 0xdd, 0xee,
++ 0xff},
++ {0x00, 0x02, 0x14, 0x26, 0x38, 0x4a, 0x60, 0x70, /* 7 */
++ 0x80, 0x90, 0xa0, 0xb0, 0xc0, 0xd0, 0xe0, 0xf0,
++ 0xff},
++ {0x00, 0x10, 0x22, 0x35, 0x47, 0x5a, 0x69, 0x79, /* 8 */
++ 0x88, 0x97, 0xa7, 0xb6, 0xc4, 0xd3, 0xe0, 0xf0,
++ 0xff},
++ {0x00, 0x10, 0x26, 0x40, 0x54, 0x65, 0x75, 0x84, /* 9 */
++ 0x93, 0xa1, 0xb0, 0xbd, 0xca, 0xd6, 0xe0, 0xf0,
++ 0xff},
++ {0x00, 0x18, 0x2b, 0x44, 0x60, 0x70, 0x80, 0x8e, /* 10 */
++ 0x9c, 0xaa, 0xb7, 0xc4, 0xd0, 0xd8, 0xe2, 0xf0,
++ 0xff},
++ {0x00, 0x1a, 0x34, 0x52, 0x66, 0x7e, 0x8D, 0x9B, /* 11 */
++ 0xa8, 0xb4, 0xc0, 0xcb, 0xd6, 0xe1, 0xeb, 0xf5,
++ 0xff},
++ {0x00, 0x3f, 0x5a, 0x6e, 0x7f, 0x8e, 0x9c, 0xa8, /* 12 */
++ 0xb4, 0xbf, 0xc9, 0xd3, 0xdc, 0xe5, 0xee, 0xf6,
++ 0xff},
++ {0x00, 0x54, 0x6f, 0x83, 0x93, 0xa0, 0xad, 0xb7, /* 13 */
++ 0xc2, 0xcb, 0xd4, 0xdc, 0xe4, 0xeb, 0xf2, 0xf9,
++ 0xff},
++ {0x00, 0x6e, 0x88, 0x9a, 0xa8, 0xb3, 0xbd, 0xc6, /* 14 */
++ 0xcf, 0xd6, 0xdd, 0xe3, 0xe9, 0xef, 0xf4, 0xfa,
++ 0xff},
++ {0x00, 0x93, 0xa8, 0xb7, 0xc1, 0xca, 0xd2, 0xd8, /* 15 */
++ 0xde, 0xe3, 0xe8, 0xed, 0xf1, 0xf5, 0xf8, 0xfc,
++ 0xff}
+ };
+
+-static const __u8 tas5130a_sensor_init[][8] = {
++static const u8 tas5130a_sensor_init[][8] = {
+ {0x62, 0x08, 0x63, 0x70, 0x64, 0x1d, 0x60, 0x09},
+ {0x62, 0x20, 0x63, 0x01, 0x64, 0x02, 0x60, 0x09},
+ {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09},
+@@ -418,11 +389,11 @@ static const __u8 tas5130a_sensor_init[][8] = {
+ {},
+ };
+
+-static __u8 sensor_reset[] = {0x61, 0x68, 0x62, 0xff, 0x60, 0x07};
++static u8 sensor_reset[] = {0x61, 0x68, 0x62, 0xff, 0x60, 0x07};
+
+ /* read 1 byte */
+-static int reg_r(struct gspca_dev *gspca_dev,
+- __u16 index)
++static u8 reg_r(struct gspca_dev *gspca_dev,
++ u16 index)
+ {
+ usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+@@ -435,7 +406,7 @@ static int reg_r(struct gspca_dev *gspca_dev,
+ }
+
+ static void reg_w(struct gspca_dev *gspca_dev,
+- __u16 index)
++ u16 index)
+ {
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+@@ -446,7 +417,7 @@ static void reg_w(struct gspca_dev *gspca_dev,
+ }
+
+ static void reg_w_buf(struct gspca_dev *gspca_dev,
+- const __u8 *buffer, __u16 len)
++ const u8 *buffer, u16 len)
+ {
+ if (len <= USB_BUF_SZ) {
+ memcpy(gspca_dev->usb_buf, buffer, len);
+@@ -457,7 +428,7 @@ static void reg_w_buf(struct gspca_dev *gspca_dev,
+ 0x01, 0,
+ gspca_dev->usb_buf, len, 500);
+ } else {
+- __u8 *tmpbuf;
++ u8 *tmpbuf;
+
+ tmpbuf = kmalloc(len, GFP_KERNEL);
+ memcpy(tmpbuf, buffer, len);
+@@ -471,14 +442,41 @@ static void reg_w_buf(struct gspca_dev *gspca_dev,
+ }
+ }
+
++/* write values to consecutive registers */
++static void reg_w_ixbuf(struct gspca_dev *gspca_dev,
++ u8 reg,
++ const u8 *buffer, u16 len)
++{
++ int i;
++ u8 *p, *tmpbuf;
++
++ if (len * 2 <= USB_BUF_SZ)
++ p = tmpbuf = gspca_dev->usb_buf;
++ else
++ p = tmpbuf = kmalloc(len * 2, GFP_KERNEL);
++ i = len;
++ while (--i >= 0) {
++ *p++ = reg++;
++ *p++ = *buffer++;
++ }
++ usb_control_msg(gspca_dev->dev,
++ usb_sndctrlpipe(gspca_dev->dev, 0),
++ 0,
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ 0x01, 0,
++ tmpbuf, len * 2, 500);
++ if (len * 2 > USB_BUF_SZ)
++ kfree(tmpbuf);
++}
++
+ /* Reported as OM6802*/
+ static void om6802_sensor_init(struct gspca_dev *gspca_dev)
+ {
+ int i;
+- const __u8 *p;
+- __u8 byte;
+- __u8 val[6] = {0x62, 0, 0x64, 0, 0x60, 0x05};
+- static const __u8 sensor_init[] = {
++ const u8 *p;
++ u8 byte;
++ u8 val[6] = {0x62, 0, 0x64, 0, 0x60, 0x05};
++ static const u8 sensor_init[] = {
+ 0xdf, 0x6d,
+ 0xdd, 0x18,
+ 0x5a, 0xe0,
+@@ -497,7 +495,7 @@ static void om6802_sensor_init(struct gspca_dev *gspca_dev)
+ };
+
+ reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset);
+- msleep(5);
++ msleep(100);
+ i = 4;
+ while (--i > 0) {
+ byte = reg_r(gspca_dev, 0x0060);
+@@ -538,20 +536,20 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ struct cam *cam;
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = 0x01;
+
+ cam->cam_mode = vga_mode_t16;
+ cam->nmodes = ARRAY_SIZE(vga_mode_t16);
+
+- sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
+- sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
+- sd->colors = sd_ctrls[SD_COLOR].qctrl.default_value;
++ sd->brightness = BRIGHTNESS_DEF;
++ sd->contrast = CONTRAST_DEF;
++ sd->colors = COLORS_DEF;
+ sd->gamma = GAMMA_DEF;
+- sd->mirror = sd_ctrls[SD_MIRROR].qctrl.default_value;
+- sd->freq = sd_ctrls[SD_LIGHTFREQ].qctrl.default_value;
+- sd->whitebalance = sd_ctrls[SD_WHITE_BALANCE].qctrl.default_value;
+- sd->sharpness = sd_ctrls[SD_SHARPNESS].qctrl.default_value;
+- sd->effect = sd_ctrls[SD_EFFECTS].qctrl.default_value;
++ sd->autogain = AUTOGAIN_DEF;
++ sd->mirror = MIRROR_DEF;
++ sd->freq = FREQ_DEF;
++ sd->whitebalance = WHITE_BALANCE_DEF;
++ sd->sharpness = SHARPNESS_DEF;
++ sd->effect = EFFECTS_DEF;
+ return 0;
+ }
+
+@@ -559,7 +557,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ unsigned int brightness;
+- __u8 set6[4] = { 0x8f, 0x24, 0xc3, 0x00 };
++ u8 set6[4] = { 0x8f, 0x24, 0xc3, 0x00 };
+
+ brightness = sd->brightness;
+ if (brightness < 7) {
+@@ -576,7 +574,7 @@ static void setcontrast(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ unsigned int contrast = sd->contrast;
+- __u16 reg_to_write;
++ u16 reg_to_write;
+
+ if (contrast < 7)
+ reg_to_write = 0x8ea9 - contrast * 0x200;
+@@ -589,7 +587,7 @@ static void setcontrast(struct gspca_dev *gspca_dev)
+ static void setcolors(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- __u16 reg_to_write;
++ u16 reg_to_write;
+
+ reg_to_write = 0x80bb + sd->colors * 0x100; /* was 0xc0 */
+ reg_w(gspca_dev, reg_to_write);
+@@ -600,14 +598,15 @@ static void setgamma(struct gspca_dev *gspca_dev)
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(D_CONF, "Gamma: %d", sd->gamma);
+- reg_w_buf(gspca_dev, gamma_table[sd->gamma], sizeof gamma_table[0]);
++ reg_w_ixbuf(gspca_dev, 0x90,
++ gamma_table[sd->gamma], sizeof gamma_table[0]);
+ }
+
+ static void setwhitebalance(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- __u8 white_balance[8] =
++ u8 white_balance[8] =
+ {0x87, 0x20, 0x88, 0x20, 0x89, 0x20, 0x80, 0x38};
+
+ if (sd->whitebalance)
+@@ -619,7 +618,7 @@ static void setwhitebalance(struct gspca_dev *gspca_dev)
+ static void setsharpness(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- __u16 reg_to_write;
++ u16 reg_to_write;
+
+ reg_to_write = 0x0aa6 + 0x1000 * sd->sharpness;
+
+@@ -635,18 +634,22 @@ static int sd_init(struct gspca_dev *gspca_dev)
+ * to see the initial parameters.*/
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+- __u8 byte, test_byte;
+-
+- static const __u8 read_indexs[] =
+- { 0x06, 0x07, 0x0a, 0x0b, 0x66, 0x80, 0x81, 0x8e, 0x8f, 0xa5,
+- 0xa6, 0xa8, 0xbb, 0xbc, 0xc6, 0x00, 0x00 };
+- static const __u8 n1[] =
++ u16 sensor_id;
++ u8 test_byte = 0;
++ u16 reg80, reg8e;
++
++ static const u8 read_indexs[] =
++ { 0x0a, 0x0b, 0x66, 0x80, 0x81, 0x8e, 0x8f, 0xa5,
++ 0xa6, 0xa8, 0xbb, 0xbc, 0xc6, 0x00 };
++ static const u8 n1[] =
+ {0x08, 0x03, 0x09, 0x03, 0x12, 0x04};
+- static const __u8 n2[] =
++ static const u8 n2[] =
+ {0x08, 0x00};
+- static const __u8 n3[] =
++ static const u8 n3[6] =
+ {0x61, 0x68, 0x65, 0x0a, 0x60, 0x04};
+- static const __u8 n4[] =
++ static const u8 n3_other[6] =
++ {0x61, 0xc2, 0x65, 0x88, 0x60, 0x00};
++ static const u8 n4[] =
+ {0x09, 0x01, 0x12, 0x04, 0x66, 0x8a, 0x80, 0x3c,
+ 0x81, 0x22, 0x84, 0x50, 0x8a, 0x78, 0x8b, 0x68,
+ 0x8c, 0x88, 0x8e, 0x33, 0x8f, 0x24, 0xaa, 0xb1,
+@@ -656,40 +659,61 @@ static int sd_init(struct gspca_dev *gspca_dev)
+ 0x65, 0x0a, 0xbb, 0x86, 0xaf, 0x58, 0xb0, 0x68,
+ 0x87, 0x40, 0x89, 0x2b, 0x8d, 0xff, 0x83, 0x40,
+ 0xac, 0x84, 0xad, 0x86, 0xaf, 0x46};
+- static const __u8 nset9[4] =
+- { 0x0b, 0x04, 0x0a, 0x78 };
+- static const __u8 nset8[6] =
++ static const u8 n4_other[] =
++ {0x66, 0x00, 0x7f, 0x00, 0x80, 0xac, 0x81, 0x69,
++ 0x84, 0x40, 0x85, 0x70, 0x86, 0x20, 0x8a, 0x68,
++ 0x8b, 0x58, 0x8c, 0x88, 0x8d, 0xff, 0x8e, 0xb8,
++ 0x8f, 0x28, 0xa2, 0x60, 0xa5, 0x40, 0xa8, 0xa8,
++ 0xac, 0x84, 0xad, 0x84, 0xae, 0x24, 0xaf, 0x56,
++ 0xb0, 0x68, 0xb1, 0x00, 0xb2, 0x88, 0xbb, 0xc5,
++ 0xbc, 0x4a, 0xbe, 0x36, 0xc2, 0x88, 0xc5, 0xc0,
++ 0xc6, 0xda, 0xe9, 0x26, 0xeb, 0x00};
++ static const u8 nset8[6] =
+ { 0xa8, 0xf0, 0xc6, 0x88, 0xc0, 0x00 };
+-
+- byte = reg_r(gspca_dev, 0x06);
+- test_byte = reg_r(gspca_dev, 0x07);
+- if (byte == 0x08 && test_byte == 0x07) {
+- PDEBUG(D_CONF, "sensor om6802");
+- sd->sensor = SENSOR_OM6802;
+- } else if (byte == 0x08 && test_byte == 0x01) {
+- PDEBUG(D_CONF, "sensor tas5130a");
+- sd->sensor = SENSOR_TAS5130A;
+- } else {
+- PDEBUG(D_CONF, "unknown sensor %02x %02x", byte, test_byte);
++ static const u8 nset8_other[6] =
++ { 0xa8, 0xa8, 0xc6, 0xda, 0xc0, 0x00 };
++ static const u8 nset9[4] =
++ { 0x0b, 0x04, 0x0a, 0x78 };
++ static const u8 nset9_other[4] =
++ { 0x0b, 0x04, 0x0a, 0x00 };
++
++ sensor_id = (reg_r(gspca_dev, 0x06) << 8)
++ | reg_r(gspca_dev, 0x07);
++ switch (sensor_id & 0xff0f) {
++ case 0x0801:
++ PDEBUG(D_PROBE, "sensor tas5130a");
+ sd->sensor = SENSOR_TAS5130A;
++ break;
++ case 0x0803:
++ PDEBUG(D_PROBE, "sensor 'other'");
++ sd->sensor = SENSOR_OTHER;
++ break;
++ case 0x0807:
++ PDEBUG(D_PROBE, "sensor om6802");
++ sd->sensor = SENSOR_OM6802;
++ break;
++ default:
++ PDEBUG(D_ERR|D_PROBE, "unknown sensor %04x", sensor_id);
++ return -EINVAL;
+ }
+
+- reg_w_buf(gspca_dev, n1, sizeof n1);
+- test_byte = 0;
+- i = 5;
+- while (--i >= 0) {
+- reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset);
+- test_byte = reg_r(gspca_dev, 0x0063);
+- msleep(100);
+- if (test_byte == 0x17)
+- break; /* OK */
+- }
+- if (i < 0) {
+- err("Bad sensor reset %02x", test_byte);
+-/* return -EIO; */
++ if (sd->sensor != SENSOR_OTHER) {
++ reg_w_buf(gspca_dev, n1, sizeof n1);
++ i = 5;
++ while (--i >= 0) {
++ reg_w_buf(gspca_dev, sensor_reset, sizeof sensor_reset);
++ test_byte = reg_r(gspca_dev, 0x0063);
++ msleep(100);
++ if (test_byte == 0x17)
++ break; /* OK */
++ }
++ if (i < 0) {
++ err("Bad sensor reset %02x", test_byte);
++/* return -EIO; */
+ /*fixme: test - continue */
++ }
++ reg_w_buf(gspca_dev, n2, sizeof n2);
+ }
+- reg_w_buf(gspca_dev, n2, sizeof n2);
+
+ i = 0;
+ while (read_indexs[i] != 0x00) {
+@@ -699,21 +723,31 @@ static int sd_init(struct gspca_dev *gspca_dev)
+ i++;
+ }
+
+- reg_w_buf(gspca_dev, n3, sizeof n3);
+- reg_w_buf(gspca_dev, n4, sizeof n4);
+- reg_r(gspca_dev, 0x0080);
+- reg_w(gspca_dev, 0x2c80);
++ if (sd->sensor != SENSOR_OTHER) {
++ reg_w_buf(gspca_dev, n3, sizeof n3);
++ reg_w_buf(gspca_dev, n4, sizeof n4);
++ reg_r(gspca_dev, 0x0080);
++ reg_w(gspca_dev, 0x2c80);
++ reg80 = 0x3880;
++ reg8e = 0x338e;
++ } else {
++ reg_w_buf(gspca_dev, n3_other, sizeof n3_other);
++ reg_w_buf(gspca_dev, n4_other, sizeof n4_other);
++ sd->gamma = 5;
++ reg80 = 0xac80;
++ reg8e = 0xb88e;
++ }
+
+- reg_w_buf(gspca_dev, sensor_data[sd->sensor].data1,
++ reg_w_ixbuf(gspca_dev, 0xd0, sensor_data[sd->sensor].data1,
+ sizeof sensor_data[sd->sensor].data1);
+- reg_w_buf(gspca_dev, sensor_data[sd->sensor].data3,
+- sizeof sensor_data[sd->sensor].data3);
+- reg_w_buf(gspca_dev, sensor_data[sd->sensor].data2,
++ reg_w_ixbuf(gspca_dev, 0xc7, sensor_data[sd->sensor].data2,
++ sizeof sensor_data[sd->sensor].data2);
++ reg_w_ixbuf(gspca_dev, 0xe0, sensor_data[sd->sensor].data2,
+ sizeof sensor_data[sd->sensor].data2);
+
+- reg_w(gspca_dev, 0x3880);
+- reg_w(gspca_dev, 0x3880);
+- reg_w(gspca_dev, 0x338e);
++ reg_w(gspca_dev, reg80);
++ reg_w(gspca_dev, reg80);
++ reg_w(gspca_dev, reg8e);
+
+ setbrightness(gspca_dev);
+ setcontrast(gspca_dev);
+@@ -730,16 +764,20 @@ static int sd_init(struct gspca_dev *gspca_dev)
+ sizeof sensor_data[sd->sensor].data4);
+ reg_w_buf(gspca_dev, sensor_data[sd->sensor].data5,
+ sizeof sensor_data[sd->sensor].data5);
+- reg_w_buf(gspca_dev, nset8, sizeof nset8);
+- reg_w_buf(gspca_dev, nset9, sizeof nset9);
+-
+- reg_w(gspca_dev, 0x2880);
++ if (sd->sensor != SENSOR_OTHER) {
++ reg_w_buf(gspca_dev, nset8, sizeof nset8);
++ reg_w_buf(gspca_dev, nset9, sizeof nset9);
++ reg_w(gspca_dev, 0x2880);
++ } else {
++ reg_w_buf(gspca_dev, nset8_other, sizeof nset8_other);
++ reg_w_buf(gspca_dev, nset9_other, sizeof nset9_other);
++ }
+
+- reg_w_buf(gspca_dev, sensor_data[sd->sensor].data1,
++ reg_w_ixbuf(gspca_dev, 0xd0, sensor_data[sd->sensor].data1,
+ sizeof sensor_data[sd->sensor].data1);
+- reg_w_buf(gspca_dev, sensor_data[sd->sensor].data3,
+- sizeof sensor_data[sd->sensor].data3);
+- reg_w_buf(gspca_dev, sensor_data[sd->sensor].data2,
++ reg_w_ixbuf(gspca_dev, 0xc7, sensor_data[sd->sensor].data2,
++ sizeof sensor_data[sd->sensor].data2);
++ reg_w_ixbuf(gspca_dev, 0xe0, sensor_data[sd->sensor].data2,
+ sizeof sensor_data[sd->sensor].data2);
+
+ return 0;
+@@ -748,7 +786,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
+ static void setflip(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- __u8 flipcmd[8] =
++ u8 flipcmd[8] =
+ {0x62, 0x07, 0x63, 0x03, 0x64, 0x00, 0x60, 0x09};
+
+ if (sd->mirror)
+@@ -778,7 +816,7 @@ static void seteffect(struct gspca_dev *gspca_dev)
+ static void setlightfreq(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- __u8 freq[4] = { 0x66, 0x40, 0xa8, 0xe8 };
++ u8 freq[4] = { 0x66, 0x40, 0xa8, 0xe8 };
+
+ if (sd->freq == 2) /* 60hz */
+ freq[1] = 0x00;
+@@ -791,22 +829,22 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
+ static void poll_sensor(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- static const __u8 poll1[] =
++ static const u8 poll1[] =
+ {0x67, 0x05, 0x68, 0x81, 0x69, 0x80, 0x6a, 0x82,
+ 0x6b, 0x68, 0x6c, 0x69, 0x72, 0xd9, 0x73, 0x34,
+ 0x74, 0x32, 0x75, 0x92, 0x76, 0x00, 0x09, 0x01,
+ 0x60, 0x14};
+- static const __u8 poll2[] =
++ static const u8 poll2[] =
+ {0x67, 0x02, 0x68, 0x71, 0x69, 0x72, 0x72, 0xa9,
+ 0x73, 0x02, 0x73, 0x02, 0x60, 0x14};
+- static const __u8 poll3[] =
++ static const u8 poll3[] =
+ {0x87, 0x3f, 0x88, 0x20, 0x89, 0x2d};
+- static const __u8 poll4[] =
++ static const u8 poll4[] =
+ {0xa6, 0x0a, 0xea, 0xcf, 0xbe, 0x26, 0xb1, 0x5f,
+ 0xa1, 0xb1, 0xda, 0x6b, 0xdb, 0x98, 0xdf, 0x0c,
+ 0xc2, 0x80, 0xc3, 0x10};
+
+- if (sd->sensor != SENSOR_TAS5130A) {
++ if (sd->sensor == SENSOR_OM6802) {
+ PDEBUG(D_STREAM, "[Sensor requires polling]");
+ reg_w_buf(gspca_dev, poll1, sizeof poll1);
+ reg_w_buf(gspca_dev, poll2, sizeof poll2);
+@@ -819,13 +857,14 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i, mode;
+- __u8 t2[] = { 0x07, 0x00, 0x0d, 0x60, 0x0e, 0x80 };
+- static const __u8 t3[] =
+- { 0xb3, 0x07, 0xb4, 0x00, 0xb5, 0x88, 0xb6, 0x02, 0xb7, 0x06,
+- 0xb8, 0x00, 0xb9, 0xe7, 0xba, 0x01 };
++ u8 t2[] = { 0x07, 0x00, 0x0d, 0x60, 0x0e, 0x80 };
++ static const u8 t3[] =
++ { 0x07, 0x00, 0x88, 0x02, 0x06, 0x00, 0xe7, 0x01 };
+
+ mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode]. priv;
+ switch (mode) {
++ case 0: /* 640x480 (0x00) */
++ break;
+ case 1: /* 352x288 */
+ t2[1] = 0x40;
+ break;
+@@ -835,14 +874,20 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ case 3: /* 176x144 */
+ t2[1] = 0x50;
+ break;
+- case 4: /* 160x120 */
++ default:
++/* case 4: * 160x120 */
+ t2[1] = 0x20;
+ break;
+- default: /* 640x480 (0x00) */
+- break;
+ }
+
+- if (sd->sensor == SENSOR_TAS5130A) {
++ switch (sd->sensor) {
++ case SENSOR_OM6802:
++ om6802_sensor_init(gspca_dev);
++ break;
++ case SENSOR_OTHER:
++ break;
++ default:
++/* case SENSOR_TAS5130A: */
+ i = 0;
+ while (tas5130a_sensor_init[i][0] != 0) {
+ reg_w_buf(gspca_dev, tas5130a_sensor_init[i],
+@@ -854,14 +899,13 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ reg_w_buf(gspca_dev, tas5130a_sensor_init[3],
+ sizeof tas5130a_sensor_init[0]);
+ reg_w(gspca_dev, 0x3c80);
+- } else {
+- om6802_sensor_init(gspca_dev);
++ break;
+ }
+ reg_w_buf(gspca_dev, sensor_data[sd->sensor].data4,
+ sizeof sensor_data[sd->sensor].data4);
+ reg_r(gspca_dev, 0x0012);
+ reg_w_buf(gspca_dev, t2, sizeof t2);
+- reg_w_buf(gspca_dev, t3, sizeof t3);
++ reg_w_ixbuf(gspca_dev, 0xb3, t3, sizeof t3);
+ reg_w(gspca_dev, 0x0013);
+ msleep(15);
+ reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream,
+@@ -885,16 +929,18 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
+ msleep(20);
+ reg_w_buf(gspca_dev, sensor_data[sd->sensor].stream,
+ sizeof sensor_data[sd->sensor].stream);
+- msleep(20);
+- reg_w(gspca_dev, 0x0309);
++ if (sd->sensor != SENSOR_OTHER) {
++ msleep(20);
++ reg_w(gspca_dev, 0x0309);
++ }
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ struct gspca_frame *frame, /* target */
+- __u8 *data, /* isoc packet */
++ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+- static __u8 ffd9[] = { 0xff, 0xd9 };
++ static u8 ffd9[] = { 0xff, 0xd9 };
+
+ if (data[0] == 0x5a) {
+ /* Control Packet, after this came the header again,
+@@ -1172,8 +1218,10 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c
+index 94163cc..9f243d7 100644
+--- a/drivers/media/video/gspca/tv8532.c
++++ b/drivers/media/video/gspca/tv8532.c
+@@ -31,7 +31,6 @@ struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+ __u16 brightness;
+- __u16 contrast;
+
+ __u8 packet;
+ };
+@@ -39,38 +38,22 @@ struct sd {
+ /* V4L2 controls supported by the driver */
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+
+ static struct ctrl sd_ctrls[] = {
+-#define SD_BRIGHTNESS 0
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 1,
+- .maximum = 0x2ff,
++ .maximum = 0x15f, /* = 352 - 1 */
+ .step = 1,
+- .default_value = 0x18f,
++#define BRIGHTNESS_DEF 0x14c
++ .default_value = BRIGHTNESS_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+-#define SD_CONTRAST 1
+- {
+- {
+- .id = V4L2_CID_CONTRAST,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- .name = "Contrast",
+- .minimum = 0,
+- .maximum = 0xffff,
+- .step = 1,
+- .default_value = 0x7fff,
+- },
+- .set = sd_setcontrast,
+- .get = sd_getcontrast,
+- },
+ };
+
+ static const struct v4l2_pix_format sif_mode[] = {
+@@ -86,78 +69,64 @@ static const struct v4l2_pix_format sif_mode[] = {
+ .priv = 0},
+ };
+
+-/*
+- * Initialization data: this is the first set-up data written to the
+- * device (before the open data).
+- */
+-#define TESTCLK 0x10 /* reg 0x2c -> 0x12 //10 */
+-#define TESTCOMP 0x90 /* reg 0x28 -> 0x80 */
+-#define TESTLINE 0x81 /* reg 0x29 -> 0x81 */
+-#define QCIFLINE 0x41 /* reg 0x29 -> 0x81 */
+-#define TESTPTL 0x14 /* reg 0x2D -> 0x14 */
+-#define TESTPTH 0x01 /* reg 0x2E -> 0x01 */
+-#define TESTPTBL 0x12 /* reg 0x2F -> 0x0a */
+-#define TESTPTBH 0x01 /* reg 0x30 -> 0x01 */
+-#define ADWIDTHL 0xe8 /* reg 0x0c -> 0xe8 */
+-#define ADWIDTHH 0x03 /* reg 0x0d -> 0x03 */
+-#define ADHEIGHL 0x90 /* reg 0x0e -> 0x91 //93 */
+-#define ADHEIGHH 0x01 /* reg 0x0f -> 0x01 */
+-#define EXPOL 0x8f /* reg 0x1c -> 0x8f */
+-#define EXPOH 0x01 /* reg 0x1d -> 0x01 */
+-#define ADCBEGINL 0x44 /* reg 0x10 -> 0x46 //47 */
+-#define ADCBEGINH 0x00 /* reg 0x11 -> 0x00 */
+-#define ADRBEGINL 0x0a /* reg 0x14 -> 0x0b //0x0c */
+-#define ADRBEGINH 0x00 /* reg 0x15 -> 0x00 */
+-#define TV8532_CMD_UPDATE 0x84
+-
+-#define TV8532_EEprom_Add 0x03
+-#define TV8532_EEprom_DataL 0x04
+-#define TV8532_EEprom_DataM 0x05
+-#define TV8532_EEprom_DataH 0x06
+-#define TV8532_EEprom_TableLength 0x07
+-#define TV8532_EEprom_Write 0x08
+-#define TV8532_PART_CTRL 0x00
+-#define TV8532_CTRL 0x01
+-#define TV8532_CMD_EEprom_Open 0x30
+-#define TV8532_CMD_EEprom_Close 0x29
+-#define TV8532_UDP_UPDATE 0x31
+-#define TV8532_GPIO 0x39
+-#define TV8532_GPIO_OE 0x3B
+-#define TV8532_REQ_RegWrite 0x02
+-#define TV8532_REQ_RegRead 0x03
+-
+-#define TV8532_ADWIDTH_L 0x0C
+-#define TV8532_ADWIDTH_H 0x0D
+-#define TV8532_ADHEIGHT_L 0x0E
+-#define TV8532_ADHEIGHT_H 0x0F
+-#define TV8532_EXPOSURE 0x1C
+-#define TV8532_QUANT_COMP 0x28
+-#define TV8532_MODE_PACKET 0x29
+-#define TV8532_SETCLK 0x2C
+-#define TV8532_POINT_L 0x2D
+-#define TV8532_POINT_H 0x2E
+-#define TV8532_POINTB_L 0x2F
+-#define TV8532_POINTB_H 0x30
+-#define TV8532_BUDGET_L 0x2A
+-#define TV8532_BUDGET_H 0x2B
+-#define TV8532_VID_L 0x34
+-#define TV8532_VID_H 0x35
+-#define TV8532_PID_L 0x36
+-#define TV8532_PID_H 0x37
+-#define TV8532_DeviceID 0x83
+-#define TV8532_AD_SLOPE 0x91
+-#define TV8532_AD_BITCTRL 0x94
+-#define TV8532_AD_COLBEGIN_L 0x10
+-#define TV8532_AD_COLBEGIN_H 0x11
+-#define TV8532_AD_ROWBEGIN_L 0x14
+-#define TV8532_AD_ROWBEGIN_H 0x15
+-
+-static const __u32 tv_8532_eeprom_data[] = {
+-/* add dataL dataM dataH */
+- 0x00010001, 0x01018011, 0x02050014, 0x0305001c,
+- 0x040d001e, 0x0505001f, 0x06050519, 0x0705011b,
+- 0x0805091e, 0x090d892e, 0x0a05892f, 0x0b050dd9,
+- 0x0c0509f1, 0
++/* TV-8532A (ICM532A) registers (LE) */
++#define R00_PART_CONTROL 0x00
++#define LATENT_CHANGE 0x80
++#define EXPO_CHANGE 0x04
++#define R01_TIMING_CONTROL_LOW 0x01
++#define CMD_EEprom_Open 0x30
++#define CMD_EEprom_Close 0x29
++#define R03_TABLE_ADDR 0x03
++#define R04_WTRAM_DATA_L 0x04
++#define R05_WTRAM_DATA_M 0x05
++#define R06_WTRAM_DATA_H 0x06
++#define R07_TABLE_LEN 0x07
++#define R08_RAM_WRITE_ACTION 0x08
++#define R0C_AD_WIDTHL 0x0c
++#define R0D_AD_WIDTHH 0x0d
++#define R0E_AD_HEIGHTL 0x0e
++#define R0F_AD_HEIGHTH 0x0f
++#define R10_AD_COL_BEGINL 0x10
++#define R11_AD_COL_BEGINH 0x11
++#define MIRROR 0x04 /* [10] */
++#define R14_AD_ROW_BEGINL 0x14
++#define R15_AD_ROWBEGINH 0x15
++#define R1C_AD_EXPOSE_TIMEL 0x1c
++#define R28_QUANT 0x28
++#define R29_LINE 0x29
++#define R2C_POLARITY 0x2c
++#define R2D_POINT 0x2d
++#define R2E_POINTH 0x2e
++#define R2F_POINTB 0x2f
++#define R30_POINTBH 0x30
++#define R31_UPD 0x31
++#define R2A_HIGH_BUDGET 0x2a
++#define R2B_LOW_BUDGET 0x2b
++#define R34_VID 0x34
++#define R35_VIDH 0x35
++#define R36_PID 0x36
++#define R37_PIDH 0x37
++#define R39_Test1 0x39 /* GPIO */
++#define R3B_Test3 0x3B /* GPIO */
++#define R83_AD_IDH 0x83
++#define R91_AD_SLOPEREG 0x91
++#define R94_AD_BITCONTROL 0x94
++
++static const u8 eeprom_data[][3] = {
++/* dataH dataM dataL */
++ {0x01, 0x00, 0x01},
++ {0x01, 0x80, 0x11},
++ {0x05, 0x00, 0x14},
++ {0x05, 0x00, 0x1c},
++ {0x0d, 0x00, 0x1e},
++ {0x05, 0x00, 0x1f},
++ {0x05, 0x05, 0x19},
++ {0x05, 0x01, 0x1b},
++ {0x05, 0x09, 0x1e},
++ {0x0d, 0x89, 0x2e},
++ {0x05, 0x89, 0x2f},
++ {0x05, 0x0d, 0xd9},
++ {0x05, 0x09, 0xf1},
+ };
+
+ static int reg_r(struct gspca_dev *gspca_dev,
+@@ -165,7 +134,7 @@ static int reg_r(struct gspca_dev *gspca_dev,
+ {
+ usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+- TV8532_REQ_RegRead,
++ 0x03,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, 1,
+@@ -174,27 +143,27 @@ static int reg_r(struct gspca_dev *gspca_dev,
+ }
+
+ /* write 1 byte */
+-static void reg_w_1(struct gspca_dev *gspca_dev,
++static void reg_w1(struct gspca_dev *gspca_dev,
+ __u16 index, __u8 value)
+ {
+ gspca_dev->usb_buf[0] = value;
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+- TV8532_REQ_RegWrite,
++ 0x02,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, 1, 500);
+ }
+
+ /* write 2 bytes */
+-static void reg_w_2(struct gspca_dev *gspca_dev,
+- __u16 index, __u8 val1, __u8 val2)
++static void reg_w2(struct gspca_dev *gspca_dev,
++ u16 index, u16 value)
+ {
+- gspca_dev->usb_buf[0] = val1;
+- gspca_dev->usb_buf[1] = val2;
++ gspca_dev->usb_buf[0] = value;
++ gspca_dev->usb_buf[1] = value >> 8;
+ usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+- TV8532_REQ_RegWrite,
++ 0x02,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, 2, 500);
+@@ -202,32 +171,18 @@ static void reg_w_2(struct gspca_dev *gspca_dev,
+
+ static void tv_8532WriteEEprom(struct gspca_dev *gspca_dev)
+ {
+- int i = 0;
+- __u8 reg, data0, data1, data2;
+-
+- reg_w_1(gspca_dev, TV8532_GPIO, 0xb0);
+- reg_w_1(gspca_dev, TV8532_CTRL, TV8532_CMD_EEprom_Open);
+-/* msleep(1); */
+- while (tv_8532_eeprom_data[i]) {
+- reg = (tv_8532_eeprom_data[i] & 0xff000000) >> 24;
+- reg_w_1(gspca_dev, TV8532_EEprom_Add, reg);
+- /* msleep(1); */
+- data0 = (tv_8532_eeprom_data[i] & 0x000000ff);
+- reg_w_1(gspca_dev, TV8532_EEprom_DataL, data0);
+- /* msleep(1); */
+- data1 = (tv_8532_eeprom_data[i] & 0x0000ff00) >> 8;
+- reg_w_1(gspca_dev, TV8532_EEprom_DataM, data1);
+- /* msleep(1); */
+- data2 = (tv_8532_eeprom_data[i] & 0x00ff0000) >> 16;
+- reg_w_1(gspca_dev, TV8532_EEprom_DataH, data2);
+- /* msleep(1); */
+- reg_w_1(gspca_dev, TV8532_EEprom_Write, 0);
+- /* msleep(10); */
+- i++;
++ int i;
++
++ reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Open);
++ for (i = 0; i < ARRAY_SIZE(eeprom_data); i++) {
++ reg_w1(gspca_dev, R03_TABLE_ADDR, i);
++ reg_w1(gspca_dev, R04_WTRAM_DATA_L, eeprom_data[i][2]);
++ reg_w1(gspca_dev, R05_WTRAM_DATA_M, eeprom_data[i][1]);
++ reg_w1(gspca_dev, R06_WTRAM_DATA_H, eeprom_data[i][0]);
++ reg_w1(gspca_dev, R08_RAM_WRITE_ACTION, 0);
+ }
+- reg_w_1(gspca_dev, TV8532_EEprom_TableLength, i);
+-/* msleep(1); */
+- reg_w_1(gspca_dev, TV8532_CTRL, TV8532_CMD_EEprom_Close);
++ reg_w1(gspca_dev, R07_TABLE_LEN, i);
++ reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Close);
+ msleep(10);
+ }
+
+@@ -238,79 +193,76 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+- tv_8532WriteEEprom(gspca_dev);
+-
+ cam = &gspca_dev->cam;
+- cam->epaddr = 1;
+ cam->cam_mode = sif_mode;
+- cam->nmodes = sizeof sif_mode / sizeof sif_mode[0];
++ cam->nmodes = ARRAY_SIZE(sif_mode);
+
+- sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
+- sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
++ sd->brightness = BRIGHTNESS_DEF;
+ return 0;
+ }
+
+ static void tv_8532ReadRegisters(struct gspca_dev *gspca_dev)
+ {
+- __u8 data;
+-
+- data = reg_r(gspca_dev, 0x0001);
+- PDEBUG(D_USBI, "register 0x01-> %x", data);
+- data = reg_r(gspca_dev, 0x0002);
+- PDEBUG(D_USBI, "register 0x02-> %x", data);
+- reg_r(gspca_dev, TV8532_ADWIDTH_L);
+- reg_r(gspca_dev, TV8532_ADWIDTH_H);
+- reg_r(gspca_dev, TV8532_QUANT_COMP);
+- reg_r(gspca_dev, TV8532_MODE_PACKET);
+- reg_r(gspca_dev, TV8532_SETCLK);
+- reg_r(gspca_dev, TV8532_POINT_L);
+- reg_r(gspca_dev, TV8532_POINT_H);
+- reg_r(gspca_dev, TV8532_POINTB_L);
+- reg_r(gspca_dev, TV8532_POINTB_H);
+- reg_r(gspca_dev, TV8532_BUDGET_L);
+- reg_r(gspca_dev, TV8532_BUDGET_H);
+- reg_r(gspca_dev, TV8532_VID_L);
+- reg_r(gspca_dev, TV8532_VID_H);
+- reg_r(gspca_dev, TV8532_PID_L);
+- reg_r(gspca_dev, TV8532_PID_H);
+- reg_r(gspca_dev, TV8532_DeviceID);
+- reg_r(gspca_dev, TV8532_AD_COLBEGIN_L);
+- reg_r(gspca_dev, TV8532_AD_COLBEGIN_H);
+- reg_r(gspca_dev, TV8532_AD_ROWBEGIN_L);
+- reg_r(gspca_dev, TV8532_AD_ROWBEGIN_H);
++ int i;
++ static u8 reg_tb[] = {
++ R0C_AD_WIDTHL,
++ R0D_AD_WIDTHH,
++ R28_QUANT,
++ R29_LINE,
++ R2C_POLARITY,
++ R2D_POINT,
++ R2E_POINTH,
++ R2F_POINTB,
++ R30_POINTBH,
++ R2A_HIGH_BUDGET,
++ R2B_LOW_BUDGET,
++ R34_VID,
++ R35_VIDH,
++ R36_PID,
++ R37_PIDH,
++ R83_AD_IDH,
++ R10_AD_COL_BEGINL,
++ R11_AD_COL_BEGINH,
++ R14_AD_ROW_BEGINL,
++ R15_AD_ROWBEGINH,
++ 0
++ };
++
++ i = 0;
++ do {
++ reg_r(gspca_dev, reg_tb[i]);
++ i++;
++ } while (reg_tb[i] != 0);
+ }
+
+ static void tv_8532_setReg(struct gspca_dev *gspca_dev)
+ {
+- reg_w_1(gspca_dev, TV8532_AD_COLBEGIN_L,
+- ADCBEGINL); /* 0x10 */
+- reg_w_1(gspca_dev, TV8532_AD_COLBEGIN_H,
+- ADCBEGINH); /* also digital gain */
+- reg_w_1(gspca_dev, TV8532_PART_CTRL,
+- TV8532_CMD_UPDATE); /* 0x00<-0x84 */
+-
+- reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0a);
++ reg_w1(gspca_dev, R10_AD_COL_BEGINL, 0x44);
++ /* begin active line */
++ reg_w1(gspca_dev, R11_AD_COL_BEGINH, 0x00);
++ /* mirror and digital gain */
++ reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE);
++ /* = 0x84 */
++
++ reg_w1(gspca_dev, R3B_Test3, 0x0a); /* Test0Sel = 10 */
+ /******************************************************/
+- reg_w_1(gspca_dev, TV8532_ADHEIGHT_L, ADHEIGHL); /* 0e */
+- reg_w_1(gspca_dev, TV8532_ADHEIGHT_H, ADHEIGHH); /* 0f */
+- reg_w_2(gspca_dev, TV8532_EXPOSURE,
+- EXPOL, EXPOH); /* 350d 0x014c; 1c */
+- reg_w_1(gspca_dev, TV8532_AD_COLBEGIN_L,
+- ADCBEGINL); /* 0x10 */
+- reg_w_1(gspca_dev, TV8532_AD_COLBEGIN_H,
+- ADCBEGINH); /* also digital gain */
+- reg_w_1(gspca_dev, TV8532_AD_ROWBEGIN_L,
+- ADRBEGINL); /* 0x14 */
+-
+- reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x00); /* 0x91 */
+- reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x02); /* 0x94 */
+-
+- reg_w_1(gspca_dev, TV8532_CTRL,
+- TV8532_CMD_EEprom_Close); /* 0x01 */
+-
+- reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x00); /* 0x91 */
+- reg_w_1(gspca_dev, TV8532_PART_CTRL,
+- TV8532_CMD_UPDATE); /* 0x00<-0x84 */
++ reg_w1(gspca_dev, R0E_AD_HEIGHTL, 0x90);
++ reg_w1(gspca_dev, R0F_AD_HEIGHTH, 0x01);
++ reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, 0x018f);
++ reg_w1(gspca_dev, R10_AD_COL_BEGINL, 0x44);
++ /* begin active line */
++ reg_w1(gspca_dev, R11_AD_COL_BEGINH, 0x00);
++ /* mirror and digital gain */
++ reg_w1(gspca_dev, R14_AD_ROW_BEGINL, 0x0a);
++
++ reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x00);
++ reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x02);
++
++ reg_w1(gspca_dev, R01_TIMING_CONTROL_LOW, CMD_EEprom_Close);
++
++ reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x00);
++ reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE);
++ /* = 0x84 */
+ }
+
+ static void tv_8532_PollReg(struct gspca_dev *gspca_dev)
+@@ -319,54 +271,55 @@ static void tv_8532_PollReg(struct gspca_dev *gspca_dev)
+
+ /* strange polling from tgc */
+ for (i = 0; i < 10; i++) {
+- reg_w_1(gspca_dev, TV8532_SETCLK,
+- TESTCLK); /* 0x48; //0x08; 0x2c */
+- reg_w_1(gspca_dev, TV8532_PART_CTRL, TV8532_CMD_UPDATE);
+- reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x01); /* 0x31 */
++ reg_w1(gspca_dev, R2C_POLARITY, 0x10);
++ reg_w1(gspca_dev, R00_PART_CONTROL,
++ LATENT_CHANGE | EXPO_CHANGE);
++ reg_w1(gspca_dev, R31_UPD, 0x01);
+ }
+ }
+
+ /* this function is called at probe and resume time */
+ static int sd_init(struct gspca_dev *gspca_dev)
+ {
+- reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x32);
+- reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x00);
++ tv_8532WriteEEprom(gspca_dev);
++
++ reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x32); /* slope begin 1,7V,
++ * slope rate 2 */
++ reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x00);
+ tv_8532ReadRegisters(gspca_dev);
+- reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b);
+- reg_w_2(gspca_dev, TV8532_ADHEIGHT_L, ADHEIGHL,
+- ADHEIGHH); /* 401d 0x0169; 0e */
+- reg_w_2(gspca_dev, TV8532_EXPOSURE, EXPOL,
+- EXPOH); /* 350d 0x014c; 1c */
+- reg_w_1(gspca_dev, TV8532_ADWIDTH_L, ADWIDTHL); /* 0x20; 0x0c */
+- reg_w_1(gspca_dev, TV8532_ADWIDTH_H, ADWIDTHH); /* 0x0d */
++ reg_w1(gspca_dev, R3B_Test3, 0x0b);
++ reg_w2(gspca_dev, R0E_AD_HEIGHTL, 0x0190);
++ reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, 0x018f);
++ reg_w1(gspca_dev, R0C_AD_WIDTHL, 0xe8);
++ reg_w1(gspca_dev, R0D_AD_WIDTHH, 0x03);
+
+ /*******************************************************************/
+- reg_w_1(gspca_dev, TV8532_QUANT_COMP,
+- TESTCOMP); /* 0x72 compressed mode 0x28 */
+- reg_w_1(gspca_dev, TV8532_MODE_PACKET,
+- TESTLINE); /* 0x84; // CIF | 4 packet 0x29 */
++ reg_w1(gspca_dev, R28_QUANT, 0x90);
++ /* no compress - fixed Q - quant 0 */
++ reg_w1(gspca_dev, R29_LINE, 0x81);
++ /* 0x84; // CIF | 4 packet 0x29 */
+
+ /************************************************/
+- reg_w_1(gspca_dev, TV8532_SETCLK,
+- TESTCLK); /* 0x48; //0x08; 0x2c */
+- reg_w_1(gspca_dev, TV8532_POINT_L,
+- TESTPTL); /* 0x38; 0x2d */
+- reg_w_1(gspca_dev, TV8532_POINT_H,
+- TESTPTH); /* 0x04; 0x2e */
+- reg_w_1(gspca_dev, TV8532_POINTB_L,
+- TESTPTBL); /* 0x04; 0x2f */
+- reg_w_1(gspca_dev, TV8532_POINTB_H,
+- TESTPTBH); /* 0x04; 0x30 */
+- reg_w_1(gspca_dev, TV8532_PART_CTRL,
+- TV8532_CMD_UPDATE); /* 0x00<-0x84 */
++ reg_w1(gspca_dev, R2C_POLARITY, 0x10);
++ /* 0x48; //0x08; 0x2c */
++ reg_w1(gspca_dev, R2D_POINT, 0x14);
++ /* 0x38; 0x2d */
++ reg_w1(gspca_dev, R2E_POINTH, 0x01);
++ /* 0x04; 0x2e */
++ reg_w1(gspca_dev, R2F_POINTB, 0x12);
++ /* 0x04; 0x2f */
++ reg_w1(gspca_dev, R30_POINTBH, 0x01);
++ /* 0x04; 0x30 */
++ reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE);
++ /* 0x00<-0x84 */
+ /*************************************************/
+- reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x01); /* 0x31 */
++ reg_w1(gspca_dev, R31_UPD, 0x01); /* update registers */
+ msleep(200);
+- reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x00); /* 0x31 */
++ reg_w1(gspca_dev, R31_UPD, 0x00); /* end update */
+ /*************************************************/
+ tv_8532_setReg(gspca_dev);
+ /*************************************************/
+- reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b);
++ reg_w1(gspca_dev, R3B_Test3, 0x0b); /* Test0Sel = 11 = GPIO */
+ /*************************************************/
+ tv_8532_setReg(gspca_dev);
+ /*************************************************/
+@@ -377,11 +330,10 @@ static int sd_init(struct gspca_dev *gspca_dev)
+ static void setbrightness(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- int brightness = sd->brightness;
+
+- reg_w_2(gspca_dev, TV8532_EXPOSURE,
+- brightness >> 8, brightness); /* 1c */
+- reg_w_1(gspca_dev, TV8532_PART_CTRL, TV8532_CMD_UPDATE);
++ reg_w2(gspca_dev, R1C_AD_EXPOSE_TIMEL, sd->brightness);
++ reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE);
++ /* 0x84 */
+ }
+
+ /* -- start the camera -- */
+@@ -389,57 +341,50 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- reg_w_1(gspca_dev, TV8532_AD_SLOPE, 0x32);
+- reg_w_1(gspca_dev, TV8532_AD_BITCTRL, 0x00);
++ reg_w1(gspca_dev, R91_AD_SLOPEREG, 0x32); /* slope begin 1,7V,
++ * slope rate 2 */
++ reg_w1(gspca_dev, R94_AD_BITCONTROL, 0x00);
+ tv_8532ReadRegisters(gspca_dev);
+- reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b);
+- reg_w_2(gspca_dev, TV8532_ADHEIGHT_L,
+- ADHEIGHL, ADHEIGHH); /* 401d 0x0169; 0e */
+-/* reg_w_2(gspca_dev, TV8532_EXPOSURE,
+- EXPOL, EXPOH); * 350d 0x014c; 1c */
++ reg_w1(gspca_dev, R3B_Test3, 0x0b);
++
++ reg_w2(gspca_dev, R0E_AD_HEIGHTL, 0x0190);
+ setbrightness(gspca_dev);
+
+- reg_w_1(gspca_dev, TV8532_ADWIDTH_L, ADWIDTHL); /* 0x20; 0x0c */
+- reg_w_1(gspca_dev, TV8532_ADWIDTH_H, ADWIDTHH); /* 0x0d */
++ reg_w1(gspca_dev, R0C_AD_WIDTHL, 0xe8); /* 0x20; 0x0c */
++ reg_w1(gspca_dev, R0D_AD_WIDTHH, 0x03);
+
+ /************************************************/
+- reg_w_1(gspca_dev, TV8532_QUANT_COMP,
+- TESTCOMP); /* 0x72 compressed mode 0x28 */
++ reg_w1(gspca_dev, R28_QUANT, 0x90);
++ /* 0x72 compressed mode 0x28 */
+ if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
+ /* 176x144 */
+- reg_w_1(gspca_dev, TV8532_MODE_PACKET,
+- QCIFLINE); /* 0x84; // CIF | 4 packet 0x29 */
++ reg_w1(gspca_dev, R29_LINE, 0x41);
++ /* CIF - 2 lines/packet */
+ } else {
+ /* 352x288 */
+- reg_w_1(gspca_dev, TV8532_MODE_PACKET,
+- TESTLINE); /* 0x84; // CIF | 4 packet 0x29 */
++ reg_w1(gspca_dev, R29_LINE, 0x81);
++ /* CIF - 2 lines/packet */
+ }
+ /************************************************/
+- reg_w_1(gspca_dev, TV8532_SETCLK,
+- TESTCLK); /* 0x48; //0x08; 0x2c */
+- reg_w_1(gspca_dev, TV8532_POINT_L,
+- TESTPTL); /* 0x38; 0x2d */
+- reg_w_1(gspca_dev, TV8532_POINT_H,
+- TESTPTH); /* 0x04; 0x2e */
+- reg_w_1(gspca_dev, TV8532_POINTB_L,
+- TESTPTBL); /* 0x04; 0x2f */
+- reg_w_1(gspca_dev, TV8532_POINTB_H,
+- TESTPTBH); /* 0x04; 0x30 */
+- reg_w_1(gspca_dev, TV8532_PART_CTRL,
+- TV8532_CMD_UPDATE); /* 0x00<-0x84 */
++ reg_w1(gspca_dev, R2C_POLARITY, 0x10); /* slow clock */
++ reg_w1(gspca_dev, R2D_POINT, 0x14);
++ reg_w1(gspca_dev, R2E_POINTH, 0x01);
++ reg_w1(gspca_dev, R2F_POINTB, 0x12);
++ reg_w1(gspca_dev, R30_POINTBH, 0x01);
++ reg_w1(gspca_dev, R00_PART_CONTROL, LATENT_CHANGE | EXPO_CHANGE);
+ /************************************************/
+- reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x01); /* 0x31 */
++ reg_w1(gspca_dev, R31_UPD, 0x01); /* update registers */
+ msleep(200);
+- reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x00); /* 0x31 */
++ reg_w1(gspca_dev, R31_UPD, 0x00); /* end update */
+ /************************************************/
+ tv_8532_setReg(gspca_dev);
+ /************************************************/
+- reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b);
++ reg_w1(gspca_dev, R3B_Test3, 0x0b); /* Test0Sel = 11 = GPIO */
+ /************************************************/
+ tv_8532_setReg(gspca_dev);
+ /************************************************/
+ tv_8532_PollReg(gspca_dev);
+- reg_w_1(gspca_dev, TV8532_UDP_UPDATE, 0x00); /* 0x31 */
++ reg_w1(gspca_dev, R31_UPD, 0x00); /* end update */
+
+ gspca_dev->empty_packet = 0; /* check the empty packets */
+ sd->packet = 0; /* ignore the first packets */
+@@ -449,7 +394,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
+
+ static void sd_stopN(struct gspca_dev *gspca_dev)
+ {
+- reg_w_1(gspca_dev, TV8532_GPIO_OE, 0x0b);
++ reg_w1(gspca_dev, R3B_Test3, 0x0b); /* Test0Sel = 11 = GPIO */
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+@@ -473,9 +418,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+
+ /* each packet contains:
+ * - header 2 bytes
+- * - RG line
++ * - RGRG line
+ * - 4 bytes
+- * - GB line
++ * - GBGB line
+ * - 4 bytes
+ */
+ gspca_frame_add(gspca_dev, packet_type0,
+@@ -484,10 +429,6 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ frame, data + gspca_dev->width + 6, gspca_dev->width);
+ }
+
+-static void setcontrast(struct gspca_dev *gspca_dev)
+-{
+-}
+-
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -506,24 +447,6 @@ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+ return 0;
+ }
+
+-static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- sd->contrast = val;
+- if (gspca_dev->streaming)
+- setcontrast(gspca_dev);
+- return 0;
+-}
+-
+-static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- *val = sd->contrast;
+- return 0;
+-}
+-
+ /* sub-driver description */
+ static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+@@ -570,8 +493,10 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c
+index 0525ea5..4c802fb 100644
+--- a/drivers/media/video/gspca/vc032x.c
++++ b/drivers/media/video/gspca/vc032x.c
+@@ -37,18 +37,21 @@ struct sd {
+ __u8 lightfreq;
+ __u8 sharpness;
+
++ u8 image_offset;
++
+ char bridge;
+ #define BRIDGE_VC0321 0
+ #define BRIDGE_VC0323 1
+ char sensor;
+ #define SENSOR_HV7131R 0
+ #define SENSOR_MI0360 1
+-#define SENSOR_MI1320 2
+-#define SENSOR_MI1310_SOC 3
+-#define SENSOR_OV7660 4
+-#define SENSOR_OV7670 5
+-#define SENSOR_PO1200 6
+-#define SENSOR_PO3130NC 7
++#define SENSOR_MI1310_SOC 2
++#define SENSOR_MI1320 3
++#define SENSOR_MI1320_SOC 4
++#define SENSOR_OV7660 5
++#define SENSOR_OV7670 6
++#define SENSOR_PO1200 7
++#define SENSOR_PO3130NC 8
+ };
+
+ /* V4L2 controls supported by the driver */
+@@ -149,8 +152,50 @@ static const struct v4l2_pix_format vc0323_mode[] = {
+ .sizeimage = 640 * 480 * 3 / 8 + 590,
+ .colorspace = V4L2_COLORSPACE_JPEG,
+ .priv = 0},
++ {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, /* mi13x0_soc only */
++ .bytesperline = 1280,
++ .sizeimage = 1280 * 1024 * 1 / 4 + 590,
++ .colorspace = V4L2_COLORSPACE_JPEG,
++ .priv = 2},
++};
++static const struct v4l2_pix_format bi_mode[] = {
++/*fixme: jeg does not work
++ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
++ .bytesperline = 320,
++ .sizeimage = 320 * 240 * 3 / 8 + 590,
++ .colorspace = V4L2_COLORSPACE_JPEG,
++ .priv = 5},
++*/
++ {320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
++ .bytesperline = 320,
++ .sizeimage = 320 * 240 * 2,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 4},
++/*
++ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
++ .bytesperline = 640,
++ .sizeimage = 640 * 480 * 3 / 8 + 590,
++ .colorspace = V4L2_COLORSPACE_JPEG,
++ .priv = 3},
++*/
++ {640, 480, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
++ .bytesperline = 640,
++ .sizeimage = 640 * 480 * 2,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 2},
++/*
++ {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
++ .bytesperline = 1280,
++ .sizeimage = 1280 * 1024 * 1 / 4 + 590,
++ .colorspace = V4L2_COLORSPACE_JPEG,
++ .priv = 1},
++*/
++ {1280, 1024, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
++ .bytesperline = 1280,
++ .sizeimage = 1280 * 1024 * 2,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 0},
+ };
+-
+ static const struct v4l2_pix_format svga_mode[] = {
+ {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 800,
+@@ -400,92 +445,208 @@ static const __u8 mi0360_initQVGA_JPG[][4] = {
+ static const __u8 mi1310_socinitVGA_JPG[][4] = {
+ {0xb0, 0x03, 0x19, 0xcc},
+ {0xb0, 0x04, 0x02, 0xcc},
+- {0xb3, 0x00, 0x64, 0xcc},
+- {0xb3, 0x00, 0x65, 0xcc},
+- {0xb3, 0x05, 0x00, 0xcc},
+- {0xb3, 0x06, 0x00, 0xcc},
++ {0xb3, 0x00, 0x24, 0xcc},
++ {0xb3, 0x00, 0x25, 0xcc},
++ {0xb3, 0x05, 0x01, 0xcc},
++ {0xb3, 0x06, 0x03, 0xcc},
++ {0xb3, 0x5c, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x35, 0xdd, 0xcc},
+- {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+- {0xb3, 0x04, 0x05, 0xcc},
++ {0xb3, 0x04, 0x0d, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc},
+ {0xb3, 0x21, 0x00, 0xcc},
+- {0xb3, 0x22, 0x03, 0xcc},
+- {0xb3, 0x23, 0xc0, 0xcc},
++ {0xb3, 0x22, 0x01, 0xcc},
++ {0xb3, 0x23, 0xe0, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc},
+ {0xb3, 0x15, 0x00, 0xcc},
+- {0xb3, 0x16, 0x04, 0xcc},
+- {0xb3, 0x17, 0xff, 0xcc},
+- {0xb3, 0x00, 0x65, 0xcc},
+- {0xb8, 0x00, 0x00, 0xcc},
+- {0xbc, 0x00, 0xd0, 0xcc},
+- {0xbc, 0x01, 0x01, 0xcc},
+- {0xf0, 0x00, 0x02, 0xbb},
+- {0xc8, 0x9f, 0x0b, 0xbb},
+- {0x5b, 0x00, 0x01, 0xbb},
+- {0x2f, 0xde, 0x20, 0xbb},
++ {0xb3, 0x16, 0x02, 0xcc},
++ {0xb3, 0x17, 0x7f, 0xcc},
++ {0xb8, 0x01, 0x7d, 0xcc},
++ {0xb8, 0x81, 0x09, 0xcc},
++ {0xb8, 0x27, 0x20, 0xcc},
++ {0xb8, 0x26, 0x80, 0xcc},
++ {0xb3, 0x00, 0x25, 0xcc},
++ {0xb8, 0x00, 0x13, 0xcc},
++ {0xbc, 0x00, 0x71, 0xcc},
++ {0xb8, 0x81, 0x01, 0xcc},
++ {0xb8, 0x2c, 0x5a, 0xcc},
++ {0xb8, 0x2d, 0xff, 0xcc},
++ {0xb8, 0x2e, 0xee, 0xcc},
++ {0xb8, 0x2f, 0xfb, 0xcc},
++ {0xb8, 0x30, 0x52, 0xcc},
++ {0xb8, 0x31, 0xf8, 0xcc},
++ {0xb8, 0x32, 0xf1, 0xcc},
++ {0xb8, 0x33, 0xff, 0xcc},
++ {0xb8, 0x34, 0x54, 0xcc},
++ {0xb8, 0x35, 0x00, 0xcc},
++ {0xb8, 0x36, 0x00, 0xcc},
++ {0xb8, 0x37, 0x00, 0xcc},
+ {0xf0, 0x00, 0x00, 0xbb},
+- {0x20, 0x03, 0x02, 0xbb},
++ {0x00, 0x01, 0x00, 0xdd},
++ {0x0d, 0x00, 0x09, 0xbb},
++ {0x0d, 0x00, 0x08, 0xbb},
+ {0xf0, 0x00, 0x01, 0xbb},
+- {0x05, 0x00, 0x07, 0xbb},
+- {0x34, 0x00, 0x00, 0xbb},
+- {0x35, 0xff, 0x00, 0xbb},
+- {0xdc, 0x07, 0x02, 0xbb},
+- {0xdd, 0x3c, 0x18, 0xbb},
+- {0xde, 0x92, 0x6d, 0xbb},
+- {0xdf, 0xcd, 0xb1, 0xbb},
+- {0xe0, 0xff, 0xe7, 0xbb},
+- {0x06, 0xf0, 0x0d, 0xbb},
+- {0x06, 0x70, 0x0e, 0xbb},
+- {0x4c, 0x00, 0x01, 0xbb},
+- {0x4d, 0x00, 0x01, 0xbb},
+- {0xf0, 0x00, 0x02, 0xbb},
+- {0x2e, 0x0c, 0x55, 0xbb},
+- {0x21, 0xb6, 0x6e, 0xbb},
+- {0x36, 0x30, 0x10, 0xbb},
+- {0x37, 0x00, 0xc1, 0xbb},
++ {0x00, 0x01, 0x00, 0xdd},
++ {0x06, 0x00, 0x14, 0xbb},
++ {0x3a, 0x10, 0x00, 0xbb},
++ {0x00, 0x00, 0x10, 0xdd},
++ {0x9b, 0x10, 0x00, 0xbb},
++ {0x00, 0x00, 0x10, 0xdd},
+ {0xf0, 0x00, 0x00, 0xbb},
+- {0x07, 0x00, 0x84, 0xbb},
+- {0x08, 0x02, 0x4a, 0xbb},
+- {0x05, 0x01, 0x10, 0xbb},
+- {0x06, 0x00, 0x39, 0xbb},
+- {0xf0, 0x00, 0x02, 0xbb},
+- {0x58, 0x02, 0x67, 0xbb},
+- {0x57, 0x02, 0x00, 0xbb},
+- {0x5a, 0x02, 0x67, 0xbb},
+- {0x59, 0x02, 0x00, 0xbb},
+- {0x5c, 0x12, 0x0d, 0xbb},
+- {0x5d, 0x16, 0x11, 0xbb},
+- {0x39, 0x06, 0x18, 0xbb},
+- {0x3a, 0x06, 0x18, 0xbb},
+- {0x3b, 0x06, 0x18, 0xbb},
+- {0x3c, 0x06, 0x18, 0xbb},
+- {0x64, 0x7b, 0x5b, 0xbb},
+- {0xf0, 0x00, 0x02, 0xbb},
+- {0x36, 0x30, 0x10, 0xbb},
+- {0x37, 0x00, 0xc0, 0xbb},
+- {0xbc, 0x0e, 0x00, 0xcc},
+- {0xbc, 0x0f, 0x05, 0xcc},
+- {0xbc, 0x10, 0xc0, 0xcc},
+- {0xbc, 0x11, 0x03, 0xcc},
++ {0x00, 0x01, 0x00, 0xdd},
++ {0x2b, 0x00, 0x28, 0xbb},
++ {0x2c, 0x00, 0x30, 0xbb},
++ {0x2d, 0x00, 0x30, 0xbb},
++ {0x2e, 0x00, 0x28, 0xbb},
++ {0x41, 0x00, 0xd7, 0xbb},
++ {0x09, 0x02, 0x3a, 0xbb},
++ {0x0c, 0x00, 0x00, 0xbb},
++ {0x20, 0x00, 0x00, 0xbb},
++ {0x05, 0x00, 0x8c, 0xbb},
++ {0x06, 0x00, 0x32, 0xbb},
++ {0x07, 0x00, 0xc6, 0xbb},
++ {0x08, 0x00, 0x19, 0xbb},
++ {0x24, 0x80, 0x6f, 0xbb},
++ {0xc8, 0x00, 0x0f, 0xbb},
++ {0x20, 0x00, 0x0f, 0xbb},
+ {0xb6, 0x00, 0x00, 0xcc},
+ {0xb6, 0x03, 0x02, 0xcc},
+ {0xb6, 0x02, 0x80, 0xcc},
+ {0xb6, 0x05, 0x01, 0xcc},
+ {0xb6, 0x04, 0xe0, 0xcc},
+- {0xb6, 0x12, 0xf8, 0xcc},
+- {0xb6, 0x13, 0x25, 0xcc},
++ {0xb6, 0x12, 0x78, 0xcc},
+ {0xb6, 0x18, 0x02, 0xcc},
+ {0xb6, 0x17, 0x58, 0xcc},
+ {0xb6, 0x16, 0x00, 0xcc},
+ {0xb6, 0x22, 0x12, 0xcc},
+ {0xb6, 0x23, 0x0b, 0xcc},
++ {0xb3, 0x02, 0x02, 0xcc},
+ {0xbf, 0xc0, 0x39, 0xcc},
+ {0xbf, 0xc1, 0x04, 0xcc},
+- {0xbf, 0xcc, 0x00, 0xcc},
++ {0xbf, 0xcc, 0x10, 0xcc},
++ {0xb9, 0x12, 0x00, 0xcc},
++ {0xb9, 0x13, 0x0a, 0xcc},
++ {0xb9, 0x14, 0x0a, 0xcc},
++ {0xb9, 0x15, 0x0a, 0xcc},
++ {0xb9, 0x16, 0x0a, 0xcc},
++ {0xb9, 0x18, 0x00, 0xcc},
++ {0xb9, 0x19, 0x0f, 0xcc},
++ {0xb9, 0x1a, 0x0f, 0xcc},
++ {0xb9, 0x1b, 0x0f, 0xcc},
++ {0xb9, 0x1c, 0x0f, 0xcc},
++ {0xb8, 0x8e, 0x00, 0xcc},
++ {0xb8, 0x8f, 0xff, 0xcc},
++ {0xb3, 0x01, 0x41, 0xcc},
++ {0x03, 0x03, 0xc0, 0xbb},
++ {0x06, 0x00, 0x10, 0xbb},
++ {0xb6, 0x12, 0xf8, 0xcc},
++ {0xb8, 0x0c, 0x20, 0xcc},
++ {0xb8, 0x0d, 0x70, 0xcc},
++ {0xb6, 0x13, 0x13, 0xcc},
++ {0x2f, 0x00, 0xC0, 0xbb},
++ {0xb8, 0xa0, 0x12, 0xcc},
++ {},
++};
++static const __u8 mi1310_socinitQVGA_JPG[][4] = {
++ {0xb0, 0x03, 0x19, 0xcc},
++ {0xb0, 0x04, 0x02, 0xcc},
++ {0xb3, 0x00, 0x24, 0xcc},
++ {0xb3, 0x00, 0x25, 0xcc},
++ {0xb3, 0x05, 0x01, 0xcc},
++ {0xb3, 0x06, 0x03, 0xcc},
++ {0xb3, 0x5c, 0x01, 0xcc},
++ {0xb3, 0x08, 0x01, 0xcc},
++ {0xb3, 0x09, 0x0c, 0xcc},
++ {0xb3, 0x34, 0x02, 0xcc},
++ {0xb3, 0x35, 0xdd, 0xcc},
++ {0xb3, 0x03, 0x0a, 0xcc},
++ {0xb3, 0x04, 0x0d, 0xcc},
++ {0xb3, 0x20, 0x00, 0xcc},
++ {0xb3, 0x21, 0x00, 0xcc},
++ {0xb3, 0x22, 0x01, 0xcc},
++ {0xb3, 0x23, 0xe0, 0xcc},
++ {0xb3, 0x14, 0x00, 0xcc},
++ {0xb3, 0x15, 0x00, 0xcc},
++ {0xb3, 0x16, 0x02, 0xcc},
++ {0xb3, 0x17, 0x7f, 0xcc},
++ {0xb8, 0x01, 0x7d, 0xcc},
++ {0xb8, 0x81, 0x09, 0xcc},
++ {0xb8, 0x27, 0x20, 0xcc},
++ {0xb8, 0x26, 0x80, 0xcc},
++ {0xb3, 0x00, 0x25, 0xcc},
++ {0xb8, 0x00, 0x13, 0xcc},
++ {0xbc, 0x00, 0xd1, 0xcc},
++ {0xb8, 0x81, 0x01, 0xcc},
++ {0xb8, 0x2c, 0x5a, 0xcc},
++ {0xb8, 0x2d, 0xff, 0xcc},
++ {0xb8, 0x2e, 0xee, 0xcc},
++ {0xb8, 0x2f, 0xfb, 0xcc},
++ {0xb8, 0x30, 0x52, 0xcc},
++ {0xb8, 0x31, 0xf8, 0xcc},
++ {0xb8, 0x32, 0xf1, 0xcc},
++ {0xb8, 0x33, 0xff, 0xcc},
++ {0xb8, 0x34, 0x54, 0xcc},
++ {0xb8, 0x35, 0x00, 0xcc},
++ {0xb8, 0x36, 0x00, 0xcc},
++ {0xb8, 0x37, 0x00, 0xcc},
++ {0xf0, 0x00, 0x00, 0xbb},
++ {0x00, 0x01, 0x00, 0xdd},
++ {0x0d, 0x00, 0x09, 0xbb},
++ {0x0d, 0x00, 0x08, 0xbb},
++ {0xf0, 0x00, 0x01, 0xbb},
++ {0x00, 0x01, 0x00, 0xdd},
++ {0x06, 0x00, 0x14, 0xbb},
++ {0x3a, 0x10, 0x00, 0xbb},
++ {0x00, 0x00, 0x10, 0xdd},
++ {0x9b, 0x10, 0x00, 0xbb},
++ {0x00, 0x00, 0x10, 0xdd},
++ {0xf0, 0x00, 0x00, 0xbb},
++ {0x00, 0x01, 0x00, 0xdd},
++ {0x2b, 0x00, 0x28, 0xbb},
++ {0x2c, 0x00, 0x30, 0xbb},
++ {0x2d, 0x00, 0x30, 0xbb},
++ {0x2e, 0x00, 0x28, 0xbb},
++ {0x41, 0x00, 0xd7, 0xbb},
++ {0x09, 0x02, 0x3a, 0xbb},
++ {0x0c, 0x00, 0x00, 0xbb},
++ {0x20, 0x00, 0x00, 0xbb},
++ {0x05, 0x00, 0x8c, 0xbb},
++ {0x06, 0x00, 0x32, 0xbb},
++ {0x07, 0x00, 0xc6, 0xbb},
++ {0x08, 0x00, 0x19, 0xbb},
++ {0x24, 0x80, 0x6f, 0xbb},
++ {0xc8, 0x00, 0x0f, 0xbb},
++ {0x20, 0x00, 0x0f, 0xbb},
++ {0xb6, 0x00, 0x00, 0xcc},
++ {0xb6, 0x03, 0x01, 0xcc},
++ {0xb6, 0x02, 0x40, 0xcc},
++ {0xb6, 0x05, 0x00, 0xcc},
++ {0xb6, 0x04, 0xf0, 0xcc},
++ {0xb6, 0x12, 0x78, 0xcc},
++ {0xb6, 0x18, 0x00, 0xcc},
++ {0xb6, 0x17, 0x96, 0xcc},
++ {0xb6, 0x16, 0x00, 0xcc},
++ {0xb6, 0x22, 0x12, 0xcc},
++ {0xb6, 0x23, 0x0b, 0xcc},
++ {0xb3, 0x02, 0x02, 0xcc},
++ {0xbf, 0xc0, 0x39, 0xcc},
++ {0xbf, 0xc1, 0x04, 0xcc},
++ {0xbf, 0xcc, 0x10, 0xcc},
++ {0xb9, 0x12, 0x00, 0xcc},
++ {0xb9, 0x13, 0x0a, 0xcc},
++ {0xb9, 0x14, 0x0a, 0xcc},
++ {0xb9, 0x15, 0x0a, 0xcc},
++ {0xb9, 0x16, 0x0a, 0xcc},
++ {0xb9, 0x18, 0x00, 0xcc},
++ {0xb9, 0x19, 0x0f, 0xcc},
++ {0xb9, 0x1a, 0x0f, 0xcc},
++ {0xb9, 0x1b, 0x0f, 0xcc},
++ {0xb9, 0x1c, 0x0f, 0xcc},
++ {0xb8, 0x8e, 0x00, 0xcc},
++ {0xb8, 0x8f, 0xff, 0xcc},
+ {0xbc, 0x02, 0x18, 0xcc},
+ {0xbc, 0x03, 0x50, 0xcc},
+ {0xbc, 0x04, 0x18, 0xcc},
+@@ -496,131 +657,123 @@ static const __u8 mi1310_socinitVGA_JPG[][4] = {
+ {0xbc, 0x0a, 0x10, 0xcc},
+ {0xbc, 0x0b, 0x00, 0xcc},
+ {0xbc, 0x0c, 0x00, 0xcc},
+- {0xb3, 0x5c, 0x01, 0xcc},
+- {0xf0, 0x00, 0x01, 0xbb},
+- {0x80, 0x00, 0x03, 0xbb},
+- {0x81, 0xc7, 0x14, 0xbb},
+- {0x82, 0xeb, 0xe8, 0xbb},
+- {0x83, 0xfe, 0xf4, 0xbb},
+- {0x84, 0xcd, 0x10, 0xbb},
+- {0x85, 0xf3, 0xee, 0xbb},
+- {0x86, 0xff, 0xf1, 0xbb},
+- {0x87, 0xcd, 0x10, 0xbb},
+- {0x88, 0xf3, 0xee, 0xbb},
+- {0x89, 0x01, 0xf1, 0xbb},
+- {0x8a, 0xe5, 0x17, 0xbb},
+- {0x8b, 0xe8, 0xe2, 0xbb},
+- {0x8c, 0xf7, 0xed, 0xbb},
+- {0x8d, 0x00, 0xff, 0xbb},
+- {0x8e, 0xec, 0x10, 0xbb},
+- {0x8f, 0xf0, 0xed, 0xbb},
+- {0x90, 0xf9, 0xf2, 0xbb},
+- {0x91, 0x00, 0x00, 0xbb},
+- {0x92, 0xe9, 0x0d, 0xbb},
+- {0x93, 0xf4, 0xf2, 0xbb},
+- {0x94, 0xfb, 0xf5, 0xbb},
+- {0x95, 0x00, 0xff, 0xbb},
+- {0xb6, 0x0f, 0x08, 0xbb},
+- {0xb7, 0x3d, 0x16, 0xbb},
+- {0xb8, 0x0c, 0x04, 0xbb},
+- {0xb9, 0x1c, 0x07, 0xbb},
+- {0xba, 0x0a, 0x03, 0xbb},
+- {0xbb, 0x1b, 0x09, 0xbb},
+- {0xbc, 0x17, 0x0d, 0xbb},
+- {0xbd, 0x23, 0x1d, 0xbb},
+- {0xbe, 0x00, 0x28, 0xbb},
+- {0xbf, 0x11, 0x09, 0xbb},
+- {0xc0, 0x16, 0x15, 0xbb},
+- {0xc1, 0x00, 0x1b, 0xbb},
+- {0xc2, 0x0e, 0x07, 0xbb},
+- {0xc3, 0x14, 0x10, 0xbb},
+- {0xc4, 0x00, 0x17, 0xbb},
+- {0x06, 0x74, 0x8e, 0xbb},
+- {0xf0, 0x00, 0x01, 0xbb},
+- {0x06, 0xf4, 0x8e, 0xbb},
+- {0x00, 0x00, 0x50, 0xdd},
+- {0x06, 0x74, 0x8e, 0xbb},
+- {0xf0, 0x00, 0x02, 0xbb},
+- {0x24, 0x50, 0x20, 0xbb},
+- {0xf0, 0x00, 0x02, 0xbb},
+- {0x34, 0x0c, 0x50, 0xbb},
+ {0xb3, 0x01, 0x41, 0xcc},
+- {0xf0, 0x00, 0x00, 0xbb},
+ {0x03, 0x03, 0xc0, 0xbb},
++ {0x06, 0x00, 0x10, 0xbb},
++ {0xb6, 0x12, 0xf8, 0xcc},
++ {0xb8, 0x0c, 0x20, 0xcc},
++ {0xb8, 0x0d, 0x70, 0xcc},
++ {0xb6, 0x13, 0x13, 0xcc},
++ {0x2f, 0x00, 0xC0, 0xbb},
++ {0xb8, 0xa0, 0x12, 0xcc},
+ {},
+ };
+-static const __u8 mi1310_socinitQVGA_JPG[][4] = {
+- {0xb0, 0x03, 0x19, 0xcc}, {0xb0, 0x04, 0x02, 0xcc},
+- {0xb3, 0x00, 0x64, 0xcc}, {0xb3, 0x00, 0x65, 0xcc},
+- {0xb3, 0x05, 0x00, 0xcc}, {0xb3, 0x06, 0x00, 0xcc},
+- {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc},
+- {0xb3, 0x34, 0x02, 0xcc}, {0xb3, 0x35, 0xdd, 0xcc},
+- {0xb3, 0x02, 0x00, 0xcc}, {0xb3, 0x03, 0x0a, 0xcc},
+- {0xb3, 0x04, 0x05, 0xcc}, {0xb3, 0x20, 0x00, 0xcc},
+- {0xb3, 0x21, 0x00, 0xcc}, {0xb3, 0x22, 0x03, 0xcc},
+- {0xb3, 0x23, 0xc0, 0xcc}, {0xb3, 0x14, 0x00, 0xcc},
+- {0xb3, 0x15, 0x00, 0xcc}, {0xb3, 0x16, 0x04, 0xcc},
+- {0xb3, 0x17, 0xff, 0xcc}, {0xb3, 0x00, 0x65, 0xcc},
+- {0xb8, 0x00, 0x00, 0xcc}, {0xbc, 0x00, 0xf0, 0xcc},
+- {0xbc, 0x01, 0x01, 0xcc}, {0xf0, 0x00, 0x02, 0xbb},
+- {0xc8, 0x9f, 0x0b, 0xbb}, {0x5b, 0x00, 0x01, 0xbb},
+- {0x2f, 0xde, 0x20, 0xbb}, {0xf0, 0x00, 0x00, 0xbb},
+- {0x20, 0x03, 0x02, 0xbb}, {0xf0, 0x00, 0x01, 0xbb},
+- {0x05, 0x00, 0x07, 0xbb}, {0x34, 0x00, 0x00, 0xbb},
+- {0x35, 0xff, 0x00, 0xbb}, {0xdc, 0x07, 0x02, 0xbb},
+- {0xdd, 0x3c, 0x18, 0xbb}, {0xde, 0x92, 0x6d, 0xbb},
+- {0xdf, 0xcd, 0xb1, 0xbb}, {0xe0, 0xff, 0xe7, 0xbb},
+- {0x06, 0xf0, 0x0d, 0xbb}, {0x06, 0x70, 0x0e, 0xbb},
+- {0x4c, 0x00, 0x01, 0xbb}, {0x4d, 0x00, 0x01, 0xbb},
+- {0xf0, 0x00, 0x02, 0xbb}, {0x2e, 0x0c, 0x55, 0xbb},
+- {0x21, 0xb6, 0x6e, 0xbb}, {0x36, 0x30, 0x10, 0xbb},
+- {0x37, 0x00, 0xc1, 0xbb}, {0xf0, 0x00, 0x00, 0xbb},
+- {0x07, 0x00, 0x84, 0xbb}, {0x08, 0x02, 0x4a, 0xbb},
+- {0x05, 0x01, 0x10, 0xbb}, {0x06, 0x00, 0x39, 0xbb},
+- {0xf0, 0x00, 0x02, 0xbb}, {0x58, 0x02, 0x67, 0xbb},
+- {0x57, 0x02, 0x00, 0xbb}, {0x5a, 0x02, 0x67, 0xbb},
+- {0x59, 0x02, 0x00, 0xbb}, {0x5c, 0x12, 0x0d, 0xbb},
+- {0x5d, 0x16, 0x11, 0xbb}, {0x39, 0x06, 0x18, 0xbb},
+- {0x3a, 0x06, 0x18, 0xbb}, {0x3b, 0x06, 0x18, 0xbb},
+- {0x3c, 0x06, 0x18, 0xbb}, {0x64, 0x7b, 0x5b, 0xbb},
+- {0xf0, 0x00, 0x02, 0xbb}, {0x36, 0x30, 0x10, 0xbb},
+- {0x37, 0x00, 0xc0, 0xbb}, {0xbc, 0x0e, 0x00, 0xcc},
+- {0xbc, 0x0f, 0x05, 0xcc}, {0xbc, 0x10, 0xc0, 0xcc},
+- {0xbc, 0x11, 0x03, 0xcc}, {0xb6, 0x00, 0x00, 0xcc},
+- {0xb6, 0x03, 0x01, 0xcc}, {0xb6, 0x02, 0x40, 0xcc},
+- {0xb6, 0x05, 0x00, 0xcc}, {0xb6, 0x04, 0xf0, 0xcc},
+- {0xb6, 0x12, 0xf8, 0xcc}, {0xb6, 0x13, 0x25, 0xcc},
+- {0xb6, 0x18, 0x00, 0xcc}, {0xb6, 0x17, 0x96, 0xcc},
+- {0xb6, 0x16, 0x00, 0xcc}, {0xb6, 0x22, 0x12, 0xcc},
+- {0xb6, 0x23, 0x0b, 0xcc}, {0xbf, 0xc0, 0x39, 0xcc},
+- {0xbf, 0xc1, 0x04, 0xcc}, {0xbf, 0xcc, 0x00, 0xcc},
+- {0xb3, 0x5c, 0x01, 0xcc}, {0xf0, 0x00, 0x01, 0xbb},
+- {0x80, 0x00, 0x03, 0xbb}, {0x81, 0xc7, 0x14, 0xbb},
+- {0x82, 0xeb, 0xe8, 0xbb}, {0x83, 0xfe, 0xf4, 0xbb},
+- {0x84, 0xcd, 0x10, 0xbb}, {0x85, 0xf3, 0xee, 0xbb},
+- {0x86, 0xff, 0xf1, 0xbb}, {0x87, 0xcd, 0x10, 0xbb},
+- {0x88, 0xf3, 0xee, 0xbb}, {0x89, 0x01, 0xf1, 0xbb},
+- {0x8a, 0xe5, 0x17, 0xbb}, {0x8b, 0xe8, 0xe2, 0xbb},
+- {0x8c, 0xf7, 0xed, 0xbb}, {0x8d, 0x00, 0xff, 0xbb},
+- {0x8e, 0xec, 0x10, 0xbb}, {0x8f, 0xf0, 0xed, 0xbb},
+- {0x90, 0xf9, 0xf2, 0xbb}, {0x91, 0x00, 0x00, 0xbb},
+- {0x92, 0xe9, 0x0d, 0xbb}, {0x93, 0xf4, 0xf2, 0xbb},
+- {0x94, 0xfb, 0xf5, 0xbb}, {0x95, 0x00, 0xff, 0xbb},
+- {0xb6, 0x0f, 0x08, 0xbb}, {0xb7, 0x3d, 0x16, 0xbb},
+- {0xb8, 0x0c, 0x04, 0xbb}, {0xb9, 0x1c, 0x07, 0xbb},
+- {0xba, 0x0a, 0x03, 0xbb}, {0xbb, 0x1b, 0x09, 0xbb},
+- {0xbc, 0x17, 0x0d, 0xbb}, {0xbd, 0x23, 0x1d, 0xbb},
+- {0xbe, 0x00, 0x28, 0xbb}, {0xbf, 0x11, 0x09, 0xbb},
+- {0xc0, 0x16, 0x15, 0xbb}, {0xc1, 0x00, 0x1b, 0xbb},
+- {0xc2, 0x0e, 0x07, 0xbb}, {0xc3, 0x14, 0x10, 0xbb},
+- {0xc4, 0x00, 0x17, 0xbb}, {0x06, 0x74, 0x8e, 0xbb},
+- {0xf0, 0x00, 0x01, 0xbb}, {0x06, 0xf4, 0x8e, 0xbb},
+- {0x00, 0x00, 0x50, 0xdd}, {0x06, 0x74, 0x8e, 0xbb},
+- {0xf0, 0x00, 0x02, 0xbb}, {0x24, 0x50, 0x20, 0xbb},
+- {0xf0, 0x00, 0x02, 0xbb}, {0x34, 0x0c, 0x50, 0xbb},
+- {0xb3, 0x01, 0x41, 0xcc}, {0xf0, 0x00, 0x00, 0xbb},
+- {0x03, 0x03, 0xc0, 0xbb},
+- {},
++static const u8 mi1310_soc_InitSXGA_JPG[][4] = {
++ {0xb0, 0x03, 0x19, 0xcc},
++ {0xb0, 0x04, 0x02, 0xcc},
++ {0xb3, 0x00, 0x24, 0xcc},
++ {0xb3, 0x00, 0x25, 0xcc},
++ {0xb3, 0x05, 0x00, 0xcc},
++ {0xb3, 0x06, 0x01, 0xcc},
++ {0xb3, 0x5c, 0x01, 0xcc},
++ {0xb3, 0x08, 0x01, 0xcc},
++ {0xb3, 0x09, 0x0c, 0xcc},
++ {0xb3, 0x34, 0x02, 0xcc},
++ {0xb3, 0x35, 0xdd, 0xcc},
++ {0xb3, 0x03, 0x0a, 0xcc},
++ {0xb3, 0x04, 0x0d, 0xcc},
++ {0xb3, 0x20, 0x00, 0xcc},
++ {0xb3, 0x21, 0x00, 0xcc},
++ {0xb3, 0x22, 0x04, 0xcc},
++ {0xb3, 0x23, 0x00, 0xcc},
++ {0xb3, 0x14, 0x00, 0xcc},
++ {0xb3, 0x15, 0x00, 0xcc},
++ {0xb3, 0x16, 0x04, 0xcc},
++ {0xb3, 0x17, 0xff, 0xcc},
++ {0xb8, 0x01, 0x7d, 0xcc},
++ {0xb8, 0x81, 0x09, 0xcc},
++ {0xb8, 0x27, 0x20, 0xcc},
++ {0xb8, 0x26, 0x80, 0xcc},
++ {0xb8, 0x06, 0x00, 0xcc},
++ {0xb8, 0x07, 0x05, 0xcc},
++ {0xb8, 0x08, 0x00, 0xcc},
++ {0xb8, 0x09, 0x04, 0xcc},
++ {0xb3, 0x00, 0x25, 0xcc},
++ {0xb8, 0x00, 0x11, 0xcc},
++ {0xbc, 0x00, 0x71, 0xcc},
++ {0xb8, 0x81, 0x01, 0xcc},
++ {0xb8, 0x2c, 0x5a, 0xcc},
++ {0xb8, 0x2d, 0xff, 0xcc},
++ {0xb8, 0x2e, 0xee, 0xcc},
++ {0xb8, 0x2f, 0xfb, 0xcc},
++ {0xb8, 0x30, 0x52, 0xcc},
++ {0xb8, 0x31, 0xf8, 0xcc},
++ {0xb8, 0x32, 0xf1, 0xcc},
++ {0xb8, 0x33, 0xff, 0xcc},
++ {0xb8, 0x34, 0x54, 0xcc},
++ {0xf0, 0x00, 0x00, 0xbb},
++ {0x00, 0x01, 0x00, 0xdd},
++ {0x0d, 0x00, 0x09, 0xbb},
++ {0x0d, 0x00, 0x08, 0xbb},
++ {0xf0, 0x00, 0x01, 0xbb},
++ {0x00, 0x01, 0x00, 0xdd},
++ {0x06, 0x00, 0x14, 0xbb},
++ {0x3a, 0x10, 0x00, 0xbb},
++ {0x00, 0x00, 0x10, 0xdd},
++ {0x9b, 0x10, 0x00, 0xbb},
++ {0x00, 0x00, 0x10, 0xdd},
++ {0xf0, 0x00, 0x00, 0xbb},
++ {0x00, 0x01, 0x00, 0xdd},
++ {0x2b, 0x00, 0x28, 0xbb},
++ {0x2c, 0x00, 0x30, 0xbb},
++ {0x2d, 0x00, 0x30, 0xbb},
++ {0x2e, 0x00, 0x28, 0xbb},
++ {0x41, 0x00, 0xd7, 0xbb},
++ {0x09, 0x02, 0x3a, 0xbb},
++ {0x0c, 0x00, 0x00, 0xbb},
++ {0x20, 0x00, 0x00, 0xbb},
++ {0x05, 0x00, 0x8c, 0xbb},
++ {0x06, 0x00, 0x32, 0xbb},
++ {0x07, 0x00, 0xc6, 0xbb},
++ {0x08, 0x00, 0x19, 0xbb},
++ {0x24, 0x80, 0x6f, 0xbb},
++ {0xc8, 0x00, 0x0f, 0xbb},
++ {0x20, 0x00, 0x03, 0xbb},
++ {0xb6, 0x00, 0x00, 0xcc},
++ {0xb6, 0x03, 0x05, 0xcc},
++ {0xb6, 0x02, 0x00, 0xcc},
++ {0xb6, 0x05, 0x04, 0xcc},
++ {0xb6, 0x04, 0x00, 0xcc},
++ {0xb6, 0x12, 0xf8, 0xcc},
++ {0xb6, 0x18, 0x0a, 0xcc},
++ {0xb6, 0x17, 0x00, 0xcc},
++ {0xb6, 0x16, 0x00, 0xcc},
++ {0xb6, 0x22, 0x12, 0xcc},
++ {0xb6, 0x23, 0x0b, 0xcc},
++ {0xb3, 0x02, 0x02, 0xcc},
++ {0xbf, 0xc0, 0x39, 0xcc},
++ {0xbf, 0xc1, 0x04, 0xcc},
++ {0xbf, 0xcc, 0x10, 0xcc},
++ {0xb9, 0x12, 0x00, 0xcc},
++ {0xb9, 0x13, 0x14, 0xcc},
++ {0xb9, 0x14, 0x14, 0xcc},
++ {0xb9, 0x15, 0x14, 0xcc},
++ {0xb9, 0x16, 0x14, 0xcc},
++ {0xb9, 0x18, 0x00, 0xcc},
++ {0xb9, 0x19, 0x1e, 0xcc},
++ {0xb9, 0x1a, 0x1e, 0xcc},
++ {0xb9, 0x1b, 0x1e, 0xcc},
++ {0xb9, 0x1c, 0x1e, 0xcc},
++ {0xb3, 0x01, 0x41, 0xcc},
++ {0xb8, 0x8e, 0x00, 0xcc},
++ {0xb8, 0x8f, 0xff, 0xcc},
++ {0xb6, 0x12, 0xf8, 0xcc},
++ {0xb8, 0x0c, 0x20, 0xcc},
++ {0xb8, 0x0d, 0x70, 0xcc},
++ {0xb6, 0x13, 0x13, 0xcc},
++ {0x2f, 0x00, 0xC0, 0xbb},
++ {0xb8, 0xa0, 0x12, 0xcc},
++ {}
+ };
+
+ static const __u8 mi1320_gamma[17] = {
+@@ -778,6 +931,722 @@ static const __u8 mi1320_initQVGA_data[][4] = {
+ {}
+ };
+
++static const u8 mi1320_soc_InitVGA[][4] = {
++ {0xb3, 0x01, 0x01, 0xcc},
++ {0xb0, 0x03, 0x19, 0xcc},
++ {0xb0, 0x04, 0x02, 0xcc},
++ {0x00, 0x00, 0x30, 0xdd},
++ {0xb3, 0x00, 0x64, 0xcc},
++ {0xb3, 0x00, 0x67, 0xcc},
++ {0xb3, 0x05, 0x01, 0xcc},
++ {0xb3, 0x06, 0x01, 0xcc},
++ {0xb3, 0x08, 0x01, 0xcc},
++ {0xb3, 0x09, 0x0c, 0xcc},
++ {0xb3, 0x34, 0x02, 0xcc},
++ {0xb3, 0x35, 0xc8, 0xcc},
++ {0xb3, 0x02, 0x00, 0xcc},
++ {0xb3, 0x03, 0x0a, 0xcc},
++ {0xb3, 0x04, 0x05, 0xcc},
++ {0xb3, 0x20, 0x00, 0xcc},
++ {0xb3, 0x21, 0x00, 0xcc},
++ {0xb3, 0x22, 0x01, 0xcc},
++ {0xb3, 0x23, 0xe0, 0xcc},
++ {0xb3, 0x14, 0x00, 0xcc},
++ {0xb3, 0x15, 0x00, 0xcc},
++ {0xb3, 0x16, 0x02, 0xcc},
++ {0xb3, 0x17, 0x7f, 0xcc},
++ {0xb3, 0x00, 0x67, 0xcc},
++ {0xb8, 0x00, 0x00, 0xcc},
++ {0xbc, 0x00, 0x71, 0xcc},
++ {0xbc, 0x01, 0x01, 0xcc},
++ {0xb3, 0x5c, 0x01, 0xcc},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x00, 0x00, 0x10, 0xdd},
++ {0xc8, 0x00, 0x00, 0xbb},
++ {0x00, 0x00, 0x30, 0xdd},
++ {0xf0, 0x00, 0x00, 0xbb},
++ {0x00, 0x00, 0x10, 0xdd},
++ {0x07, 0x00, 0xe0, 0xbb},
++ {0x08, 0x00, 0x0b, 0xbb},
++ {0x21, 0x00, 0x0c, 0xbb},
++ {0x20, 0x01, 0x03, 0xbb},
++ {0xbf, 0xc0, 0x26, 0xcc},
++ {0xbf, 0xc1, 0x02, 0xcc},
++ {0xbf, 0xcc, 0x04, 0xcc},
++ {0xb3, 0x01, 0x41, 0xcc},
++ {0xf0, 0x00, 0x00, 0xbb},
++ {0x05, 0x01, 0x78, 0xbb},
++ {0x06, 0x00, 0x11, 0xbb},
++ {0x07, 0x01, 0x42, 0xbb},
++ {0x08, 0x00, 0x11, 0xbb},
++ {0x20, 0x01, 0x03, 0xbb},
++ {0x21, 0x80, 0x00, 0xbb},
++ {0x22, 0x0d, 0x0f, 0xbb},
++ {0x24, 0x80, 0x00, 0xbb},
++ {0x59, 0x00, 0xff, 0xbb},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x39, 0x03, 0xca, 0xbb},
++ {0x3a, 0x06, 0x80, 0xbb},
++ {0x3b, 0x01, 0x52, 0xbb},
++ {0x3c, 0x05, 0x40, 0xbb},
++ {0x57, 0x01, 0x9c, 0xbb},
++ {0x58, 0x01, 0xee, 0xbb},
++ {0x59, 0x00, 0xf0, 0xbb},
++ {0x5a, 0x01, 0x20, 0xbb},
++ {0x5c, 0x1d, 0x17, 0xbb},
++ {0x5d, 0x22, 0x1c, 0xbb},
++ {0x64, 0x1e, 0x1c, 0xbb},
++ {0x5b, 0x00, 0x00, 0xbb},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x22, 0xa0, 0x78, 0xbb},
++ {0x23, 0xa0, 0x78, 0xbb},
++ {0x24, 0x7f, 0x00, 0xbb},
++ {0x28, 0xea, 0x02, 0xbb},
++ {0x29, 0x86, 0x7a, 0xbb},
++ {0x5e, 0x52, 0x4c, 0xbb},
++ {0x5f, 0x20, 0x24, 0xbb},
++ {0x60, 0x00, 0x02, 0xbb},
++ {0x02, 0x00, 0xee, 0xbb},
++ {0x03, 0x39, 0x23, 0xbb},
++ {0x04, 0x07, 0x24, 0xbb},
++ {0x09, 0x00, 0xc0, 0xbb},
++ {0x0a, 0x00, 0x79, 0xbb},
++ {0x0b, 0x00, 0x04, 0xbb},
++ {0x0c, 0x00, 0x5c, 0xbb},
++ {0x0d, 0x00, 0xd9, 0xbb},
++ {0x0e, 0x00, 0x53, 0xbb},
++ {0x0f, 0x00, 0x21, 0xbb},
++ {0x10, 0x00, 0xa4, 0xbb},
++ {0x11, 0x00, 0xe5, 0xbb},
++ {0x15, 0x00, 0x00, 0xbb},
++ {0x16, 0x00, 0x00, 0xbb},
++ {0x17, 0x00, 0x00, 0xbb},
++ {0x18, 0x00, 0x00, 0xbb},
++ {0x19, 0x00, 0x00, 0xbb},
++ {0x1a, 0x00, 0x00, 0xbb},
++ {0x1b, 0x00, 0x00, 0xbb},
++ {0x1c, 0x00, 0x00, 0xbb},
++ {0x1d, 0x00, 0x00, 0xbb},
++ {0x1e, 0x00, 0x00, 0xbb},
++ {0xf0, 0x00, 0x01, 0xbb},
++ {0x06, 0xe0, 0x0e, 0xbb},
++ {0x06, 0x60, 0x0e, 0xbb},
++ {0xb3, 0x5c, 0x01, 0xcc},
++ {}
++};
++static const u8 mi1320_soc_InitVGA_JPG[][4] = {
++ {0xb3, 0x01, 0x01, 0xcc},
++ {0xb0, 0x03, 0x19, 0xcc},
++ {0xb0, 0x04, 0x02, 0xcc},
++ {0x00, 0x00, 0x30, 0xdd},
++ {0xb3, 0x00, 0x64, 0xcc},
++ {0xb3, 0x00, 0x67, 0xcc},
++ {0xb3, 0x05, 0x01, 0xcc},
++ {0xb3, 0x06, 0x01, 0xcc},
++ {0xb3, 0x08, 0x01, 0xcc},
++ {0xb3, 0x09, 0x0c, 0xcc},
++ {0xb3, 0x34, 0x02, 0xcc},
++ {0xb3, 0x35, 0xc8, 0xcc},
++ {0xb3, 0x02, 0x00, 0xcc},
++ {0xb3, 0x03, 0x0a, 0xcc},
++ {0xb3, 0x04, 0x05, 0xcc},
++ {0xb3, 0x20, 0x00, 0xcc},
++ {0xb3, 0x21, 0x00, 0xcc},
++ {0xb3, 0x22, 0x01, 0xcc},
++ {0xb3, 0x23, 0xe0, 0xcc},
++ {0xb3, 0x14, 0x00, 0xcc},
++ {0xb3, 0x15, 0x00, 0xcc},
++ {0xb3, 0x16, 0x02, 0xcc},
++ {0xb3, 0x17, 0x7f, 0xcc},
++ {0xb3, 0x00, 0x67, 0xcc},
++ {0xb8, 0x00, 0x00, 0xcc},
++ {0xbc, 0x00, 0x71, 0xcc},
++ {0xbc, 0x01, 0x01, 0xcc},
++ {0xb3, 0x5c, 0x01, 0xcc},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x00, 0x00, 0x10, 0xdd},
++ {0xc8, 0x00, 0x00, 0xbb},
++ {0x00, 0x00, 0x30, 0xdd},
++ {0xf0, 0x00, 0x00, 0xbb},
++ {0x00, 0x00, 0x10, 0xdd},
++ {0x07, 0x00, 0xe0, 0xbb},
++ {0x08, 0x00, 0x0b, 0xbb},
++ {0x21, 0x00, 0x0c, 0xbb},
++ {0x20, 0x01, 0x03, 0xbb},
++ {0xb6, 0x00, 0x00, 0xcc},
++ {0xb6, 0x03, 0x02, 0xcc},
++ {0xb6, 0x02, 0x80, 0xcc},
++ {0xb6, 0x05, 0x01, 0xcc},
++ {0xb6, 0x04, 0xe0, 0xcc},
++ {0xb6, 0x12, 0xf8, 0xcc},
++ {0xb6, 0x13, 0x05, 0xcc},
++ {0xb6, 0x18, 0x02, 0xcc},
++ {0xb6, 0x17, 0x58, 0xcc},
++ {0xb6, 0x16, 0x00, 0xcc},
++ {0xb6, 0x22, 0x12, 0xcc},
++ {0xb6, 0x23, 0x0b, 0xcc},
++ {0xbf, 0xc0, 0x39, 0xcc},
++ {0xbf, 0xc1, 0x04, 0xcc},
++ {0xbf, 0xcc, 0x00, 0xcc},
++ {0xb3, 0x01, 0x41, 0xcc},
++ {0xf0, 0x00, 0x00, 0xbb},
++ {0x05, 0x01, 0x78, 0xbb},
++ {0x06, 0x00, 0x11, 0xbb},
++ {0x07, 0x01, 0x42, 0xbb},
++ {0x08, 0x00, 0x11, 0xbb},
++ {0x20, 0x01, 0x03, 0xbb},
++ {0x21, 0x80, 0x00, 0xbb},
++ {0x22, 0x0d, 0x0f, 0xbb},
++ {0x24, 0x80, 0x00, 0xbb},
++ {0x59, 0x00, 0xff, 0xbb},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x39, 0x03, 0xca, 0xbb},
++ {0x3a, 0x06, 0x80, 0xbb},
++ {0x3b, 0x01, 0x52, 0xbb},
++ {0x3c, 0x05, 0x40, 0xbb},
++ {0x57, 0x01, 0x9c, 0xbb},
++ {0x58, 0x01, 0xee, 0xbb},
++ {0x59, 0x00, 0xf0, 0xbb},
++ {0x5a, 0x01, 0x20, 0xbb},
++ {0x5c, 0x1d, 0x17, 0xbb},
++ {0x5d, 0x22, 0x1c, 0xbb},
++ {0x64, 0x1e, 0x1c, 0xbb},
++ {0x5b, 0x00, 0x00, 0xbb},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x22, 0xa0, 0x78, 0xbb},
++ {0x23, 0xa0, 0x78, 0xbb},
++ {0x24, 0x7f, 0x00, 0xbb},
++ {0x28, 0xea, 0x02, 0xbb},
++ {0x29, 0x86, 0x7a, 0xbb},
++ {0x5e, 0x52, 0x4c, 0xbb},
++ {0x5f, 0x20, 0x24, 0xbb},
++ {0x60, 0x00, 0x02, 0xbb},
++ {0x02, 0x00, 0xee, 0xbb},
++ {0x03, 0x39, 0x23, 0xbb},
++ {0x04, 0x07, 0x24, 0xbb},
++ {0x09, 0x00, 0xc0, 0xbb},
++ {0x0a, 0x00, 0x79, 0xbb},
++ {0x0b, 0x00, 0x04, 0xbb},
++ {0x0c, 0x00, 0x5c, 0xbb},
++ {0x0d, 0x00, 0xd9, 0xbb},
++ {0x0e, 0x00, 0x53, 0xbb},
++ {0x0f, 0x00, 0x21, 0xbb},
++ {0x10, 0x00, 0xa4, 0xbb},
++ {0x11, 0x00, 0xe5, 0xbb},
++ {0x15, 0x00, 0x00, 0xbb},
++ {0x16, 0x00, 0x00, 0xbb},
++ {0x17, 0x00, 0x00, 0xbb},
++ {0x18, 0x00, 0x00, 0xbb},
++ {0x19, 0x00, 0x00, 0xbb},
++ {0x1a, 0x00, 0x00, 0xbb},
++ {0x1b, 0x00, 0x00, 0xbb},
++ {0x1c, 0x00, 0x00, 0xbb},
++ {0x1d, 0x00, 0x00, 0xbb},
++ {0x1e, 0x00, 0x00, 0xbb},
++ {0xf0, 0x00, 0x01, 0xbb},
++ {0x06, 0xe0, 0x0e, 0xbb},
++ {0x06, 0x60, 0x0e, 0xbb},
++ {0xb3, 0x5c, 0x01, 0xcc},
++ {}
++};
++static const u8 mi1320_soc_InitQVGA[][4] = {
++ {0xb3, 0x01, 0x01, 0xcc},
++ {0xb0, 0x03, 0x19, 0xcc},
++ {0xb0, 0x04, 0x02, 0xcc},
++ {0x00, 0x00, 0x30, 0xdd},
++ {0xb3, 0x00, 0x64, 0xcc},
++ {0xb3, 0x00, 0x67, 0xcc},
++ {0xb3, 0x05, 0x01, 0xcc},
++ {0xb3, 0x06, 0x01, 0xcc},
++ {0xb3, 0x08, 0x01, 0xcc},
++ {0xb3, 0x09, 0x0c, 0xcc},
++ {0xb3, 0x34, 0x02, 0xcc},
++ {0xb3, 0x35, 0xc8, 0xcc},
++ {0xb3, 0x02, 0x00, 0xcc},
++ {0xb3, 0x03, 0x0a, 0xcc},
++ {0xb3, 0x04, 0x05, 0xcc},
++ {0xb3, 0x20, 0x00, 0xcc},
++ {0xb3, 0x21, 0x00, 0xcc},
++ {0xb3, 0x22, 0x01, 0xcc},
++ {0xb3, 0x23, 0xe0, 0xcc},
++ {0xb3, 0x14, 0x00, 0xcc},
++ {0xb3, 0x15, 0x00, 0xcc},
++ {0xb3, 0x16, 0x02, 0xcc},
++ {0xb3, 0x17, 0x7f, 0xcc},
++ {0xb3, 0x00, 0x67, 0xcc},
++ {0xb8, 0x00, 0x00, 0xcc},
++ {0xbc, 0x00, 0xd1, 0xcc},
++ {0xbc, 0x01, 0x01, 0xcc},
++ {0xb3, 0x5c, 0x01, 0xcc},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x00, 0x00, 0x10, 0xdd},
++ {0xc8, 0x00, 0x00, 0xbb},
++ {0x00, 0x00, 0x30, 0xdd},
++ {0xf0, 0x00, 0x00, 0xbb},
++ {0x00, 0x00, 0x10, 0xdd},
++ {0x07, 0x00, 0xe0, 0xbb},
++ {0x08, 0x00, 0x0b, 0xbb},
++ {0x21, 0x00, 0x0c, 0xbb},
++ {0x20, 0x01, 0x03, 0xbb},
++ {0xbf, 0xc0, 0x26, 0xcc},
++ {0xbf, 0xc1, 0x02, 0xcc},
++ {0xbf, 0xcc, 0x04, 0xcc},
++ {0xbc, 0x02, 0x18, 0xcc},
++ {0xbc, 0x03, 0x50, 0xcc},
++ {0xbc, 0x04, 0x18, 0xcc},
++ {0xbc, 0x05, 0x00, 0xcc},
++ {0xbc, 0x06, 0x00, 0xcc},
++ {0xbc, 0x08, 0x30, 0xcc},
++ {0xbc, 0x09, 0x40, 0xcc},
++ {0xbc, 0x0a, 0x10, 0xcc},
++ {0xbc, 0x0b, 0x00, 0xcc},
++ {0xbc, 0x0c, 0x00, 0xcc},
++ {0xb3, 0x01, 0x41, 0xcc},
++ {0xf0, 0x00, 0x00, 0xbb},
++ {0x05, 0x01, 0x78, 0xbb},
++ {0x06, 0x00, 0x11, 0xbb},
++ {0x07, 0x01, 0x42, 0xbb},
++ {0x08, 0x00, 0x11, 0xbb},
++ {0x20, 0x01, 0x03, 0xbb},
++ {0x21, 0x80, 0x00, 0xbb},
++ {0x22, 0x0d, 0x0f, 0xbb},
++ {0x24, 0x80, 0x00, 0xbb},
++ {0x59, 0x00, 0xff, 0xbb},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x39, 0x03, 0xca, 0xbb},
++ {0x3a, 0x06, 0x80, 0xbb},
++ {0x3b, 0x01, 0x52, 0xbb},
++ {0x3c, 0x05, 0x40, 0xbb},
++ {0x57, 0x01, 0x9c, 0xbb},
++ {0x58, 0x01, 0xee, 0xbb},
++ {0x59, 0x00, 0xf0, 0xbb},
++ {0x5a, 0x01, 0x20, 0xbb},
++ {0x5c, 0x1d, 0x17, 0xbb},
++ {0x5d, 0x22, 0x1c, 0xbb},
++ {0x64, 0x1e, 0x1c, 0xbb},
++ {0x5b, 0x00, 0x00, 0xbb},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x22, 0xa0, 0x78, 0xbb},
++ {0x23, 0xa0, 0x78, 0xbb},
++ {0x24, 0x7f, 0x00, 0xbb},
++ {0x28, 0xea, 0x02, 0xbb},
++ {0x29, 0x86, 0x7a, 0xbb},
++ {0x5e, 0x52, 0x4c, 0xbb},
++ {0x5f, 0x20, 0x24, 0xbb},
++ {0x60, 0x00, 0x02, 0xbb},
++ {0x02, 0x00, 0xee, 0xbb},
++ {0x03, 0x39, 0x23, 0xbb},
++ {0x04, 0x07, 0x24, 0xbb},
++ {0x09, 0x00, 0xc0, 0xbb},
++ {0x0a, 0x00, 0x79, 0xbb},
++ {0x0b, 0x00, 0x04, 0xbb},
++ {0x0c, 0x00, 0x5c, 0xbb},
++ {0x0d, 0x00, 0xd9, 0xbb},
++ {0x0e, 0x00, 0x53, 0xbb},
++ {0x0f, 0x00, 0x21, 0xbb},
++ {0x10, 0x00, 0xa4, 0xbb},
++ {0x11, 0x00, 0xe5, 0xbb},
++ {0x15, 0x00, 0x00, 0xbb},
++ {0x16, 0x00, 0x00, 0xbb},
++ {0x17, 0x00, 0x00, 0xbb},
++ {0x18, 0x00, 0x00, 0xbb},
++ {0x19, 0x00, 0x00, 0xbb},
++ {0x1a, 0x00, 0x00, 0xbb},
++ {0x1b, 0x00, 0x00, 0xbb},
++ {0x1c, 0x00, 0x00, 0xbb},
++ {0x1d, 0x00, 0x00, 0xbb},
++ {0x1e, 0x00, 0x00, 0xbb},
++ {0xf0, 0x00, 0x01, 0xbb},
++ {0x06, 0xe0, 0x0e, 0xbb},
++ {0x06, 0x60, 0x0e, 0xbb},
++ {0xb3, 0x5c, 0x01, 0xcc},
++ {}
++};
++static const u8 mi1320_soc_InitQVGA_JPG[][4] = {
++ {0xb3, 0x01, 0x01, 0xcc},
++ {0xb0, 0x03, 0x19, 0xcc},
++ {0xb0, 0x04, 0x02, 0xcc},
++ {0x00, 0x00, 0x30, 0xdd},
++ {0xb3, 0x00, 0x64, 0xcc},
++ {0xb3, 0x00, 0x67, 0xcc},
++ {0xb3, 0x05, 0x01, 0xcc},
++ {0xb3, 0x06, 0x01, 0xcc},
++ {0xb3, 0x08, 0x01, 0xcc},
++ {0xb3, 0x09, 0x0c, 0xcc},
++ {0xb3, 0x34, 0x02, 0xcc},
++ {0xb3, 0x35, 0xc8, 0xcc},
++ {0xb3, 0x02, 0x00, 0xcc},
++ {0xb3, 0x03, 0x0a, 0xcc},
++ {0xb3, 0x04, 0x05, 0xcc},
++ {0xb3, 0x20, 0x00, 0xcc},
++ {0xb3, 0x21, 0x00, 0xcc},
++ {0xb3, 0x22, 0x01, 0xcc},
++ {0xb3, 0x23, 0xe0, 0xcc},
++ {0xb3, 0x14, 0x00, 0xcc},
++ {0xb3, 0x15, 0x00, 0xcc},
++ {0xb3, 0x16, 0x02, 0xcc},
++ {0xb3, 0x17, 0x7f, 0xcc},
++ {0xb3, 0x00, 0x67, 0xcc},
++ {0xb8, 0x00, 0x00, 0xcc},
++ {0xbc, 0x00, 0xd1, 0xcc},
++ {0xbc, 0x01, 0x01, 0xcc},
++ {0xb3, 0x5c, 0x01, 0xcc},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x00, 0x00, 0x10, 0xdd},
++ {0xc8, 0x00, 0x00, 0xbb},
++ {0x00, 0x00, 0x30, 0xdd},
++ {0xf0, 0x00, 0x00, 0xbb},
++ {0x00, 0x00, 0x10, 0xdd},
++ {0x07, 0x00, 0xe0, 0xbb},
++ {0x08, 0x00, 0x0b, 0xbb},
++ {0x21, 0x00, 0x0c, 0xbb},
++ {0x20, 0x01, 0x03, 0xbb},
++ {0xb6, 0x00, 0x00, 0xcc},
++ {0xb6, 0x03, 0x01, 0xcc},
++ {0xb6, 0x02, 0x40, 0xcc},
++ {0xb6, 0x05, 0x00, 0xcc},
++ {0xb6, 0x04, 0xf0, 0xcc},
++ {0xb6, 0x12, 0xf8, 0xcc},
++ {0xb6, 0x13, 0x05, 0xcc},
++ {0xb6, 0x18, 0x00, 0xcc},
++ {0xb6, 0x17, 0x96, 0xcc},
++ {0xb6, 0x16, 0x00, 0xcc},
++ {0xb6, 0x22, 0x12, 0xcc},
++ {0xb6, 0x23, 0x0b, 0xcc},
++ {0xbf, 0xc0, 0x39, 0xcc},
++ {0xbf, 0xc1, 0x04, 0xcc},
++ {0xbf, 0xcc, 0x00, 0xcc},
++ {0xbc, 0x02, 0x18, 0xcc},
++ {0xbc, 0x03, 0x50, 0xcc},
++ {0xbc, 0x04, 0x18, 0xcc},
++ {0xbc, 0x05, 0x00, 0xcc},
++ {0xbc, 0x06, 0x00, 0xcc},
++ {0xbc, 0x08, 0x30, 0xcc},
++ {0xbc, 0x09, 0x40, 0xcc},
++ {0xbc, 0x0a, 0x10, 0xcc},
++ {0xbc, 0x0b, 0x00, 0xcc},
++ {0xbc, 0x0c, 0x00, 0xcc},
++ {0xb3, 0x01, 0x41, 0xcc},
++ {0xf0, 0x00, 0x00, 0xbb},
++ {0x05, 0x01, 0x78, 0xbb},
++ {0x06, 0x00, 0x11, 0xbb},
++ {0x07, 0x01, 0x42, 0xbb},
++ {0x08, 0x00, 0x11, 0xbb},
++ {0x20, 0x01, 0x03, 0xbb},
++ {0x21, 0x80, 0x00, 0xbb},
++ {0x22, 0x0d, 0x0f, 0xbb},
++ {0x24, 0x80, 0x00, 0xbb},
++ {0x59, 0x00, 0xff, 0xbb},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x39, 0x03, 0xca, 0xbb},
++ {0x3a, 0x06, 0x80, 0xbb},
++ {0x3b, 0x01, 0x52, 0xbb},
++ {0x3c, 0x05, 0x40, 0xbb},
++ {0x57, 0x01, 0x9c, 0xbb},
++ {0x58, 0x01, 0xee, 0xbb},
++ {0x59, 0x00, 0xf0, 0xbb},
++ {0x5a, 0x01, 0x20, 0xbb},
++ {0x5c, 0x1d, 0x17, 0xbb},
++ {0x5d, 0x22, 0x1c, 0xbb},
++ {0x64, 0x1e, 0x1c, 0xbb},
++ {0x5b, 0x00, 0x00, 0xbb},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x22, 0xa0, 0x78, 0xbb},
++ {0x23, 0xa0, 0x78, 0xbb},
++ {0x24, 0x7f, 0x00, 0xbb},
++ {0x28, 0xea, 0x02, 0xbb},
++ {0x29, 0x86, 0x7a, 0xbb},
++ {0x5e, 0x52, 0x4c, 0xbb},
++ {0x5f, 0x20, 0x24, 0xbb},
++ {0x60, 0x00, 0x02, 0xbb},
++ {0x02, 0x00, 0xee, 0xbb},
++ {0x03, 0x39, 0x23, 0xbb},
++ {0x04, 0x07, 0x24, 0xbb},
++ {0x09, 0x00, 0xc0, 0xbb},
++ {0x0a, 0x00, 0x79, 0xbb},
++ {0x0b, 0x00, 0x04, 0xbb},
++ {0x0c, 0x00, 0x5c, 0xbb},
++ {0x0d, 0x00, 0xd9, 0xbb},
++ {0x0e, 0x00, 0x53, 0xbb},
++ {0x0f, 0x00, 0x21, 0xbb},
++ {0x10, 0x00, 0xa4, 0xbb},
++ {0x11, 0x00, 0xe5, 0xbb},
++ {0x15, 0x00, 0x00, 0xbb},
++ {0x16, 0x00, 0x00, 0xbb},
++ {0x17, 0x00, 0x00, 0xbb},
++ {0x18, 0x00, 0x00, 0xbb},
++ {0x19, 0x00, 0x00, 0xbb},
++ {0x1a, 0x00, 0x00, 0xbb},
++ {0x1b, 0x00, 0x00, 0xbb},
++ {0x1c, 0x00, 0x00, 0xbb},
++ {0x1d, 0x00, 0x00, 0xbb},
++ {0x1e, 0x00, 0x00, 0xbb},
++ {0xf0, 0x00, 0x01, 0xbb},
++ {0x06, 0xe0, 0x0e, 0xbb},
++ {0x06, 0x60, 0x0e, 0xbb},
++ {0xb3, 0x5c, 0x01, 0xcc},
++ {}
++};
++static const u8 mi1320_soc_InitSXGA_JPG[][4] = {
++ {0xb3, 0x01, 0x01, 0xcc},
++ {0xb0, 0x03, 0x19, 0xcc},
++ {0xb0, 0x04, 0x02, 0xcc},
++ {0x00, 0x00, 0x33, 0xdd},
++ {0xb3, 0x00, 0x64, 0xcc},
++ {0xb3, 0x00, 0x67, 0xcc},
++ {0xb3, 0x05, 0x00, 0xcc},
++ {0xb3, 0x06, 0x00, 0xcc},
++ {0xb3, 0x08, 0x01, 0xcc},
++ {0xb3, 0x09, 0x0c, 0xcc},
++ {0xb3, 0x34, 0x02, 0xcc},
++ {0xb3, 0x35, 0xc8, 0xcc},
++ {0xb3, 0x02, 0x00, 0xcc},
++ {0xb3, 0x03, 0x0a, 0xcc},
++ {0xb3, 0x04, 0x05, 0xcc},
++ {0xb3, 0x20, 0x00, 0xcc},
++ {0xb3, 0x21, 0x00, 0xcc},
++ {0xb3, 0x22, 0x04, 0xcc},
++ {0xb3, 0x23, 0x00, 0xcc},
++ {0xb3, 0x14, 0x00, 0xcc},
++ {0xb3, 0x15, 0x00, 0xcc},
++ {0xb3, 0x16, 0x04, 0xcc},
++ {0xb3, 0x17, 0xff, 0xcc},
++ {0xb3, 0x00, 0x67, 0xcc},
++ {0xbc, 0x00, 0x71, 0xcc},
++ {0xbc, 0x01, 0x01, 0xcc},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x00, 0x00, 0x30, 0xdd},
++ {0xc8, 0x9f, 0x0b, 0xbb},
++ {0x00, 0x00, 0x20, 0xdd},
++ {0x5b, 0x00, 0x01, 0xbb},
++ {0x00, 0x00, 0x20, 0xdd},
++ {0xf0, 0x00, 0x00, 0xbb},
++ {0x00, 0x00, 0x30, 0xdd},
++ {0x20, 0x01, 0x03, 0xbb},
++ {0x00, 0x00, 0x20, 0xdd},
++ {0xb6, 0x00, 0x00, 0xcc},
++ {0xb6, 0x03, 0x05, 0xcc},
++ {0xb6, 0x02, 0x00, 0xcc},
++ {0xb6, 0x05, 0x04, 0xcc},
++ {0xb6, 0x04, 0x00, 0xcc},
++ {0xb6, 0x12, 0xf8, 0xcc},
++ {0xb6, 0x13, 0x29, 0xcc},
++ {0xb6, 0x18, 0x0a, 0xcc},
++ {0xb6, 0x17, 0x00, 0xcc},
++ {0xb6, 0x16, 0x00, 0xcc},
++ {0xb6, 0x22, 0x12, 0xcc},
++ {0xb6, 0x23, 0x0b, 0xcc},
++ {0xbf, 0xc0, 0x39, 0xcc},
++ {0xbf, 0xc1, 0x04, 0xcc},
++ {0xbf, 0xcc, 0x00, 0xcc},
++ {0xb3, 0x5c, 0x01, 0xcc},
++ {0xb3, 0x01, 0x41, 0xcc},
++ {0xf0, 0x00, 0x00, 0xbb},
++ {0x05, 0x01, 0x78, 0xbb},
++ {0x06, 0x00, 0x11, 0xbb},
++ {0x07, 0x01, 0x42, 0xbb},
++ {0x08, 0x00, 0x11, 0xbb},
++ {0x20, 0x01, 0x03, 0xbb},
++ {0x21, 0x80, 0x00, 0xbb},
++ {0x22, 0x0d, 0x0f, 0xbb},
++ {0x24, 0x80, 0x00, 0xbb},
++ {0x59, 0x00, 0xff, 0xbb},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x39, 0x03, 0xca, 0xbb},
++ {0x3a, 0x06, 0x80, 0xbb},
++ {0x3b, 0x01, 0x52, 0xbb},
++ {0x3c, 0x05, 0x40, 0xbb},
++ {0x57, 0x01, 0x9c, 0xbb},
++ {0x58, 0x01, 0xee, 0xbb},
++ {0x59, 0x00, 0xf0, 0xbb},
++ {0x5a, 0x01, 0x20, 0xbb},
++ {0x5c, 0x1d, 0x17, 0xbb},
++ {0x5d, 0x22, 0x1c, 0xbb},
++ {0x64, 0x1e, 0x1c, 0xbb},
++ {0x5b, 0x00, 0x00, 0xbb},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x22, 0xa0, 0x78, 0xbb},
++ {0x23, 0xa0, 0x78, 0xbb},
++ {0x24, 0x7f, 0x00, 0xbb},
++ {0x28, 0xea, 0x02, 0xbb},
++ {0x29, 0x86, 0x7a, 0xbb},
++ {0x5e, 0x52, 0x4c, 0xbb},
++ {0x5f, 0x20, 0x24, 0xbb},
++ {0x60, 0x00, 0x02, 0xbb},
++ {0x02, 0x00, 0xee, 0xbb},
++ {0x03, 0x39, 0x23, 0xbb},
++ {0x04, 0x07, 0x24, 0xbb},
++ {0x09, 0x00, 0xc0, 0xbb},
++ {0x0a, 0x00, 0x79, 0xbb},
++ {0x0b, 0x00, 0x04, 0xbb},
++ {0x0c, 0x00, 0x5c, 0xbb},
++ {0x0d, 0x00, 0xd9, 0xbb},
++ {0x0e, 0x00, 0x53, 0xbb},
++ {0x0f, 0x00, 0x21, 0xbb},
++ {0x10, 0x00, 0xa4, 0xbb},
++ {0x11, 0x00, 0xe5, 0xbb},
++ {0x15, 0x00, 0x00, 0xbb},
++ {0x16, 0x00, 0x00, 0xbb},
++ {0x17, 0x00, 0x00, 0xbb},
++ {0x18, 0x00, 0x00, 0xbb},
++ {0x19, 0x00, 0x00, 0xbb},
++ {0x1a, 0x00, 0x00, 0xbb},
++ {0x1b, 0x00, 0x00, 0xbb},
++ {0x1c, 0x00, 0x00, 0xbb},
++ {0x1d, 0x00, 0x00, 0xbb},
++ {0x1e, 0x00, 0x00, 0xbb},
++ {0xf0, 0x00, 0x01, 0xbb},
++ {0x06, 0xe0, 0x0e, 0xbb},
++ {0x06, 0x60, 0x0e, 0xbb},
++ {0xb3, 0x5c, 0x01, 0xcc},
++ {0xf0, 0x00, 0x00, 0xbb},
++ {0x05, 0x01, 0x13, 0xbb},
++ {0x06, 0x00, 0x11, 0xbb},
++ {0x07, 0x00, 0x85, 0xbb},
++ {0x08, 0x00, 0x27, 0xbb},
++ {0x20, 0x01, 0x03, 0xbb},
++ {0x21, 0x80, 0x00, 0xbb},
++ {0x22, 0x0d, 0x0f, 0xbb},
++ {0x24, 0x80, 0x00, 0xbb},
++ {0x59, 0x00, 0xff, 0xbb},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x39, 0x03, 0x0d, 0xbb},
++ {0x3a, 0x06, 0x1b, 0xbb},
++ {0x3b, 0x00, 0x95, 0xbb},
++ {0x3c, 0x04, 0xdb, 0xbb},
++ {0x57, 0x02, 0x00, 0xbb},
++ {0x58, 0x02, 0x66, 0xbb},
++ {0x59, 0x00, 0xff, 0xbb},
++ {0x5a, 0x01, 0x33, 0xbb},
++ {0x5c, 0x12, 0x0d, 0xbb},
++ {0x5d, 0x16, 0x11, 0xbb},
++ {0x64, 0x5e, 0x1c, 0xbb},
++ {0x2f, 0x90, 0x00, 0xbb},
++ {}
++};
++static const u8 mi1320_soc_InitSXGA[][4] = {
++ {0xb3, 0x01, 0x01, 0xcc},
++ {0xb0, 0x03, 0x19, 0xcc},
++ {0x00, 0x00, 0x30, 0xdd},
++ {0xb3, 0x00, 0x64, 0xcc},
++ {0xb3, 0x00, 0x67, 0xcc},
++ {0xb3, 0x05, 0x01, 0xcc},
++ {0xb3, 0x06, 0x01, 0xcc},
++ {0xb3, 0x08, 0x01, 0xcc},
++ {0xb3, 0x09, 0x0c, 0xcc},
++ {0xb3, 0x34, 0x02, 0xcc},
++ {0xb3, 0x35, 0xc8, 0xcc},
++ {0xb3, 0x02, 0x00, 0xcc},
++ {0xb3, 0x03, 0x0a, 0xcc},
++ {0xb3, 0x04, 0x05, 0xcc},
++ {0xb3, 0x20, 0x00, 0xcc},
++ {0xb3, 0x21, 0x00, 0xcc},
++ {0xb3, 0x22, 0x04, 0xcc},
++ {0xb3, 0x23, 0x00, 0xcc},
++ {0xb3, 0x14, 0x00, 0xcc},
++ {0xb3, 0x15, 0x00, 0xcc},
++ {0xb3, 0x16, 0x04, 0xcc},
++ {0xb3, 0x17, 0xff, 0xcc},
++ {0xb3, 0x00, 0x67, 0xcc},
++ {0xbc, 0x00, 0x71, 0xcc},
++ {0xbc, 0x01, 0x01, 0xcc},
++ {0xb3, 0x5c, 0x01, 0xcc},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x00, 0x00, 0x30, 0xdd},
++ {0xc8, 0x9f, 0x0b, 0xbb},
++ {0x00, 0x00, 0x20, 0xdd},
++ {0x5b, 0x00, 0x01, 0xbb},
++ {0x00, 0x00, 0x20, 0xdd},
++ {0xf0, 0x00, 0x00, 0xbb},
++ {0x00, 0x00, 0x30, 0xdd},
++ {0x20, 0x01, 0x03, 0xbb},
++ {0x00, 0x00, 0x20, 0xdd},
++ {0xbf, 0xc0, 0x26, 0xcc},
++ {0xbf, 0xc1, 0x02, 0xcc},
++ {0xbf, 0xcc, 0x04, 0xcc},
++ {0xb3, 0x01, 0x41, 0xcc},
++ {0xf0, 0x00, 0x00, 0xbb},
++ {0x05, 0x01, 0x78, 0xbb},
++ {0x06, 0x00, 0x11, 0xbb},
++ {0x07, 0x01, 0x42, 0xbb},
++ {0x08, 0x00, 0x11, 0xbb},
++ {0x20, 0x01, 0x03, 0xbb},
++ {0x21, 0x80, 0x00, 0xbb},
++ {0x22, 0x0d, 0x0f, 0xbb},
++ {0x24, 0x80, 0x00, 0xbb},
++ {0x59, 0x00, 0xff, 0xbb},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x39, 0x03, 0xca, 0xbb},
++ {0x3a, 0x06, 0x80, 0xbb},
++ {0x3b, 0x01, 0x52, 0xbb},
++ {0x3c, 0x05, 0x40, 0xbb},
++ {0x57, 0x01, 0x9c, 0xbb},
++ {0x58, 0x01, 0xee, 0xbb},
++ {0x59, 0x00, 0xf0, 0xbb},
++ {0x5a, 0x01, 0x20, 0xbb},
++ {0x5c, 0x1d, 0x17, 0xbb},
++ {0x5d, 0x22, 0x1c, 0xbb},
++ {0x64, 0x1e, 0x1c, 0xbb},
++ {0x5b, 0x00, 0x00, 0xbb},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x22, 0xa0, 0x78, 0xbb},
++ {0x23, 0xa0, 0x78, 0xbb},
++ {0x24, 0x7f, 0x00, 0xbb},
++ {0x28, 0xea, 0x02, 0xbb},
++ {0x29, 0x86, 0x7a, 0xbb},
++ {0x5e, 0x52, 0x4c, 0xbb},
++ {0x5f, 0x20, 0x24, 0xbb},
++ {0x60, 0x00, 0x02, 0xbb},
++ {0x02, 0x00, 0xee, 0xbb},
++ {0x03, 0x39, 0x23, 0xbb},
++ {0x04, 0x07, 0x24, 0xbb},
++ {0x09, 0x00, 0xc0, 0xbb},
++ {0x0a, 0x00, 0x79, 0xbb},
++ {0x0b, 0x00, 0x04, 0xbb},
++ {0x0c, 0x00, 0x5c, 0xbb},
++ {0x0d, 0x00, 0xd9, 0xbb},
++ {0x0e, 0x00, 0x53, 0xbb},
++ {0x0f, 0x00, 0x21, 0xbb},
++ {0x10, 0x00, 0xa4, 0xbb},
++ {0x11, 0x00, 0xe5, 0xbb},
++ {0x15, 0x00, 0x00, 0xbb},
++ {0x16, 0x00, 0x00, 0xbb},
++ {0x17, 0x00, 0x00, 0xbb},
++ {0x18, 0x00, 0x00, 0xbb},
++ {0x19, 0x00, 0x00, 0xbb},
++ {0x1a, 0x00, 0x00, 0xbb},
++ {0x1b, 0x00, 0x00, 0xbb},
++ {0x1c, 0x00, 0x00, 0xbb},
++ {0x1d, 0x00, 0x00, 0xbb},
++ {0x1e, 0x00, 0x00, 0xbb},
++ {0xf0, 0x00, 0x01, 0xbb},
++ {0x06, 0xe0, 0x0e, 0xbb},
++ {0x06, 0x60, 0x0e, 0xbb},
++ {0xb3, 0x5c, 0x01, 0xcc},
++ {0xf0, 0x00, 0x00, 0xbb},
++ {0x05, 0x01, 0x13, 0xbb},
++ {0x06, 0x00, 0x11, 0xbb},
++ {0x07, 0x00, 0x85, 0xbb},
++ {0x08, 0x00, 0x27, 0xbb},
++ {0x20, 0x01, 0x03, 0xbb},
++ {0x21, 0x80, 0x00, 0xbb},
++ {0x22, 0x0d, 0x0f, 0xbb},
++ {0x24, 0x80, 0x00, 0xbb},
++ {0x59, 0x00, 0xff, 0xbb},
++ {0xf0, 0x00, 0x02, 0xbb},
++ {0x39, 0x03, 0x0d, 0xbb},
++ {0x3a, 0x06, 0x1b, 0xbb},
++ {0x3b, 0x00, 0x95, 0xbb},
++ {0x3c, 0x04, 0xdb, 0xbb},
++ {0x57, 0x02, 0x00, 0xbb},
++ {0x58, 0x02, 0x66, 0xbb},
++ {0x59, 0x00, 0xff, 0xbb},
++ {0x5a, 0x01, 0x33, 0xbb},
++ {0x5c, 0x12, 0x0d, 0xbb},
++ {0x5d, 0x16, 0x11, 0xbb},
++ {0x64, 0x5e, 0x1c, 0xbb},
++ {}
++};
+ static const __u8 po3130_gamma[17] = {
+ 0x00, 0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+ 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff
+@@ -1764,26 +2633,43 @@ static const __u8 po1200_initVGA_data[][4] = {
+ };
+
+ struct sensor_info {
+- int sensorId;
+- __u8 I2cAdd;
+- __u8 IdAdd;
+- __u16 VpId;
+- __u8 m1;
+- __u8 m2;
+- __u8 op;
+- };
++ s8 sensorId;
++ u8 I2cAdd;
++ u8 IdAdd;
++ u16 VpId;
++ u8 m1;
++ u8 m2;
++ u8 op;
++};
+
+ static const struct sensor_info sensor_info_data[] = {
+ /* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */
+- {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01},
+- {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05},
++ {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
++ {-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01},
++/* (tested in vc032x_probe_sensor) */
++/* {-1, 0x80 | 0x20, 0x83, 0x0000, 0x24, 0x25, 0x01}, */
+ {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01},
+- {SENSOR_MI1320, 0x80 | 0xc8, 0x00, 0x148c, 0x64, 0x65, 0x01},
+- {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05},
+ {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01},
+ /* (tested in vc032x_probe_sensor) */
+ /* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */
++ {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01},
++ {-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05},
++ {-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05},
++ {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05},
++/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01}, */
++ {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
++/* {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01}, */
++/* {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, */
++ {-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01},
+ {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01},
++ {-1, 0x80 | 0x2d, 0x00, 0x0000, 0x65, 0x67, 0x01},
++ {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
++ {-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01},
++ {SENSOR_MI1320_SOC, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x67, 0x01},
++/*fixme: previously detected?*/
++ {SENSOR_MI1320, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x65, 0x01},
++/*fixme: not in the ms-win probe - may be found before?*/
++ {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05},
+ };
+
+ /* read 'len' bytes in gspca_dev->usb_buf */
+@@ -1814,51 +2700,49 @@ static void reg_w(struct usb_device *dev,
+ 500);
+ }
+
+-static void read_sensor_register(struct gspca_dev *gspca_dev,
+- __u16 address, __u16 *value)
++static u16 read_sensor_register(struct gspca_dev *gspca_dev,
++ u16 address)
+ {
+ struct usb_device *dev = gspca_dev->dev;
+- __u8 ldata, mdata, hdata;
++ u8 ldata, mdata, hdata;
+ int retry = 50;
+
+- *value = 0;
+-
+ reg_r(gspca_dev, 0xa1, 0xb33f, 1);
+- /*PDEBUG(D_PROBE, " I2c Bus Busy Wait 0x%02X ", tmpvalue); */
+ if (!(gspca_dev->usb_buf[0] & 0x02)) {
+- PDEBUG(D_ERR, "I2c Bus Busy Wait %d",
+- gspca_dev->usb_buf[0] & 0x02);
+- return;
++ PDEBUG(D_ERR, "I2c Bus Busy Wait %02x",
++ gspca_dev->usb_buf[0]);
++ return 0;
+ }
+ reg_w(dev, 0xa0, address, 0xb33a);
+ reg_w(dev, 0xa0, 0x02, 0xb339);
+
+- reg_r(gspca_dev, 0xa1, 0xb33b, 1);
+- while (retry-- && gspca_dev->usb_buf[0]) {
++ do {
+ reg_r(gspca_dev, 0xa1, 0xb33b, 1);
+-/* PDEBUG(D_PROBE, "Read again 0xb33b %d", tmpvalue); */
+- msleep(1);
+- }
++ if (gspca_dev->usb_buf[0] == 0x00)
++ break;
++ msleep(40);
++ } while (--retry >= 0);
++
+ reg_r(gspca_dev, 0xa1, 0xb33e, 1);
+ ldata = gspca_dev->usb_buf[0];
+ reg_r(gspca_dev, 0xa1, 0xb33d, 1);
+ mdata = gspca_dev->usb_buf[0];
+ reg_r(gspca_dev, 0xa1, 0xb33c, 1);
+ hdata = gspca_dev->usb_buf[0];
+- PDEBUG(D_PROBE, "Read Sensor %02x%02x %02x",
+- hdata, mdata, ldata);
++ if (hdata != 0 && mdata != 0 && ldata != 0)
++ PDEBUG(D_PROBE, "Read Sensor %02x%02x %02x",
++ hdata, mdata, ldata);
+ reg_r(gspca_dev, 0xa1, 0xb334, 1);
+ if (gspca_dev->usb_buf[0] == 0x02)
+- *value = (hdata << 8) + mdata;
+- else
+- *value = hdata;
++ return (hdata << 8) + mdata;
++ return hdata;
+ }
+
+ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
+ {
+ struct usb_device *dev = gspca_dev->dev;
+ int i;
+- __u16 value;
++ u16 value;
+ const struct sensor_info *ptsensor_info;
+
+ reg_r(gspca_dev, 0xa1, 0xbfcf, 1);
+@@ -1872,48 +2756,51 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
+ reg_w(dev, 0xa0, 0x0c, 0xb309);
+ reg_w(dev, 0xa0, ptsensor_info->I2cAdd, 0xb335);
+ reg_w(dev, 0xa0, ptsensor_info->op, 0xb301);
+- read_sensor_register(gspca_dev, ptsensor_info->IdAdd, &value);
+- if (value == ptsensor_info->VpId)
+- return ptsensor_info->sensorId;
+-
+- /* special case for MI0360 */
+- if (ptsensor_info->sensorId == SENSOR_MI1310_SOC
+- && value == 0x8243)
+- return SENSOR_MI0360;
++ value = read_sensor_register(gspca_dev, ptsensor_info->IdAdd);
++ if (value == 0 && ptsensor_info->IdAdd == 0x82)
++ value = read_sensor_register(gspca_dev, 0x83);
++ if (value != 0) {
++ PDEBUG(D_ERR|D_PROBE, "Sensor ID %04x (%d)",
++ value, i);
++ if (value == ptsensor_info->VpId)
++ return ptsensor_info->sensorId;
++
++ switch (value) {
++ case 0x7673:
++ return SENSOR_OV7670;
++ case 0x8243:
++ return SENSOR_MI0360;
++ }
++/*fixme: should return here*/
++ }
+ }
+ return -1;
+ }
+
+-static __u8 i2c_write(struct gspca_dev *gspca_dev,
+- __u8 reg, const __u8 *val, __u8 size)
++static void i2c_write(struct gspca_dev *gspca_dev,
++ u8 reg, const u8 *val,
++ u8 size) /* 1 or 2 */
+ {
+ struct usb_device *dev = gspca_dev->dev;
++ int retry;
+
+- if (size > 3 || size < 1)
+- return -EINVAL;
+ reg_r(gspca_dev, 0xa1, 0xb33f, 1);
++/*fixme:should check if (!(gspca_dev->usb_buf[0] & 0x02)) error*/
+ reg_w(dev, 0xa0, size, 0xb334);
+ reg_w(dev, 0xa0, reg, 0xb33a);
+- switch (size) {
+- case 1:
+- reg_w(dev, 0xa0, val[0], 0xb336);
+- break;
+- case 2:
+- reg_w(dev, 0xa0, val[0], 0xb336);
++ reg_w(dev, 0xa0, val[0], 0xb336);
++ if (size > 1)
+ reg_w(dev, 0xa0, val[1], 0xb337);
+- break;
+- case 3:
+- reg_w(dev, 0xa0, val[0], 0xb336);
+- reg_w(dev, 0xa0, val[1], 0xb337);
+- reg_w(dev, 0xa0, val[2], 0xb338);
+- break;
+- default:
+- reg_w(dev, 0xa0, 0x01, 0xb334);
+- return -EINVAL;
+- }
+ reg_w(dev, 0xa0, 0x01, 0xb339);
+- reg_r(gspca_dev, 0xa1, 0xb33b, 1);
+- return gspca_dev->usb_buf[0] == 0;
++ retry = 4;
++ do {
++ reg_r(gspca_dev, 0xa1, 0xb33b, 1);
++ if (gspca_dev->usb_buf[0] == 0)
++ break;
++ msleep(20);
++ } while (--retry > 0);
++ if (retry <= 0)
++ PDEBUG(D_ERR, "i2c_write failed");
+ }
+
+ static void put_tab_to_reg(struct gspca_dev *gspca_dev,
+@@ -1938,7 +2825,7 @@ static void usb_exchange(struct gspca_dev *gspca_dev,
+ return;
+ case 0xcc: /* normal write */
+ reg_w(dev, 0xa0, data[i][2],
+- ((data[i][0])<<8) | data[i][1]);
++ (data[i][0]) << 8 | data[i][1]);
+ break;
+ case 0xaa: /* i2c op */
+ i2c_write(gspca_dev, data[i][1], &data[i][2], 1);
+@@ -1955,19 +2842,6 @@ static void usb_exchange(struct gspca_dev *gspca_dev,
+ /*not reached*/
+ }
+
+-/*
+- "GammaT"=hex:04,17,31,4f,6a,83,99,ad,bf,ce,da,e5,ee,f5,fb,ff,ff
+- "MatrixT"=hex:60,f9,e5,e7,50,05,f3,e6,66
+- */
+-
+-static void vc0321_reset(struct gspca_dev *gspca_dev)
+-{
+- reg_w(gspca_dev->dev, 0xa0, 0x00, 0xb04d);
+- reg_w(gspca_dev->dev, 0xa0, 0x01, 0xb301);
+- msleep(100);
+- reg_w(gspca_dev->dev, 0xa0, 0x01, 0xb003);
+- msleep(100);
+-}
+
+ /* this function is called at probe time */
+ static int sd_config(struct gspca_dev *gspca_dev,
+@@ -1979,10 +2853,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ int sensor;
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = 0x02;
+ sd->bridge = id->driver_info;
+-
+- vc0321_reset(gspca_dev);
+ sensor = vc032x_probe_sensor(gspca_dev);
+ switch (sensor) {
+ case -1:
+@@ -2001,6 +2872,9 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ case SENSOR_MI1320:
+ PDEBUG(D_PROBE, "Find Sensor MI1320");
+ break;
++ case SENSOR_MI1320_SOC:
++ PDEBUG(D_PROBE, "Find Sensor MI1320_SOC");
++ break;
+ case SENSOR_OV7660:
+ PDEBUG(D_PROBE, "Find Sensor OV7660");
+ break;
+@@ -2020,12 +2894,23 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ cam->cam_mode = vc0321_mode;
+ cam->nmodes = ARRAY_SIZE(vc0321_mode);
+ } else {
+- if (sensor != SENSOR_PO1200) {
+- cam->cam_mode = vc0323_mode;
+- cam->nmodes = ARRAY_SIZE(vc0323_mode);
+- } else {
++ switch (sensor) {
++ case SENSOR_PO1200:
+ cam->cam_mode = svga_mode;
+ cam->nmodes = ARRAY_SIZE(svga_mode);
++ break;
++ case SENSOR_MI1310_SOC:
++ cam->cam_mode = vc0323_mode;
++ cam->nmodes = ARRAY_SIZE(vc0323_mode);
++ break;
++ case SENSOR_MI1320_SOC:
++ cam->cam_mode = bi_mode;
++ cam->nmodes = ARRAY_SIZE(bi_mode);
++ break;
++ default:
++ cam->cam_mode = vc0323_mode;
++ cam->nmodes = ARRAY_SIZE(vc0323_mode) - 1;
++ break;
+ }
+ }
+
+@@ -2061,7 +2946,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ return 0;
+ }
+
+-/* this function is called at probe and time */
++/* this function is called at probe and resume time */
+ static int sd_init(struct gspca_dev *gspca_dev)
+ {
+ return 0;
+@@ -2124,9 +3009,18 @@ static void setsharpness(struct gspca_dev *gspca_dev)
+ static int sd_start(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
++ const __u8 (*init)[4];
+ const __u8 *GammaT = NULL;
+ const __u8 *MatrixT = NULL;
+ int mode;
++ static const u8 (*mi1320_soc_init[])[4] = {
++ mi1320_soc_InitSXGA,
++ mi1320_soc_InitSXGA_JPG,
++ mi1320_soc_InitVGA,
++ mi1320_soc_InitVGA_JPG,
++ mi1320_soc_InitQVGA,
++ mi1320_soc_InitQVGA_JPG
++ };
+
+ /* Assume start use the good resolution from gspca_dev->mode */
+ if (sd->bridge == BRIDGE_VC0321) {
+@@ -2134,6 +3028,13 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfed);
+ reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfee);
+ reg_w(gspca_dev->dev, 0xa0, 0xff, 0xbfef);
++ sd->image_offset = 46;
++ } else {
++ if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].pixelformat
++ == V4L2_PIX_FMT_JPEG)
++ sd->image_offset = 0;
++ else
++ sd->image_offset = 32;
+ }
+
+ mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+@@ -2141,115 +3042,87 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ case SENSOR_HV7131R:
+ GammaT = hv7131r_gamma;
+ MatrixT = hv7131r_matrix;
+- if (mode) {
+- /* 320x240 */
+- usb_exchange(gspca_dev, hv7131r_initQVGA_data);
+- } else {
+- /* 640x480 */
+- usb_exchange(gspca_dev, hv7131r_initVGA_data);
+- }
++ if (mode)
++ init = hv7131r_initQVGA_data; /* 320x240 */
++ else
++ init = hv7131r_initVGA_data; /* 640x480 */
+ break;
+ case SENSOR_OV7660:
+ GammaT = ov7660_gamma;
+ MatrixT = ov7660_matrix;
+- if (mode) {
+- /* 320x240 */
+- usb_exchange(gspca_dev, ov7660_initQVGA_data);
+- } else {
+- /* 640x480 */
+- usb_exchange(gspca_dev, ov7660_initVGA_data);
+- }
++ if (mode)
++ init = ov7660_initQVGA_data; /* 320x240 */
++ else
++ init = ov7660_initVGA_data; /* 640x480 */
+ break;
+ case SENSOR_OV7670:
+ /*GammaT = ov7660_gamma; */
+ /*MatrixT = ov7660_matrix; */
+- if (mode) {
+- /* 320x240 */
+- usb_exchange(gspca_dev, ov7670_initQVGA_JPG);
+- } else {
+- /* 640x480 */
+- usb_exchange(gspca_dev, ov7670_initVGA_JPG);
+- }
++ if (mode)
++ init = ov7670_initQVGA_JPG; /* 320x240 */
++ else
++ init = ov7670_initVGA_JPG; /* 640x480 */
+ break;
+ case SENSOR_MI0360:
+ GammaT = mi1320_gamma;
+ MatrixT = mi0360_matrix;
+- if (mode) {
+- /* 320x240 */
+- usb_exchange(gspca_dev, mi0360_initQVGA_JPG);
+- } else {
+- /* 640x480 */
+- usb_exchange(gspca_dev, mi0360_initVGA_JPG);
+- }
++ if (mode)
++ init = mi0360_initQVGA_JPG; /* 320x240 */
++ else
++ init = mi0360_initVGA_JPG; /* 640x480 */
+ break;
+ case SENSOR_MI1310_SOC:
+- if (mode) {
+- /* 320x240 */
+- usb_exchange(gspca_dev, mi1310_socinitQVGA_JPG);
+- } else {
+- /* 640x480 */
+- usb_exchange(gspca_dev, mi1310_socinitVGA_JPG);
++ GammaT = mi1320_gamma;
++ MatrixT = mi1320_matrix;
++ switch (mode) {
++ case 1:
++ init = mi1310_socinitQVGA_JPG; /* 320x240 */
++ break;
++ case 0:
++ init = mi1310_socinitVGA_JPG; /* 640x480 */
++ break;
++ default:
++ init = mi1310_soc_InitSXGA_JPG; /* 1280x1024 */
++ break;
+ }
+ break;
+ case SENSOR_MI1320:
+ GammaT = mi1320_gamma;
+ MatrixT = mi1320_matrix;
+- if (mode) {
+- /* 320x240 */
+- usb_exchange(gspca_dev, mi1320_initQVGA_data);
+- } else {
+- /* 640x480 */
+- usb_exchange(gspca_dev, mi1320_initVGA_data);
+- }
++ if (mode)
++ init = mi1320_initQVGA_data; /* 320x240 */
++ else
++ init = mi1320_initVGA_data; /* 640x480 */
++ break;
++ case SENSOR_MI1320_SOC:
++ GammaT = mi1320_gamma;
++ MatrixT = mi1320_matrix;
++ init = mi1320_soc_init[mode];
+ break;
+ case SENSOR_PO3130NC:
+ GammaT = po3130_gamma;
+ MatrixT = po3130_matrix;
+- if (mode) {
+- /* 320x240 */
+- usb_exchange(gspca_dev, po3130_initQVGA_data);
+- } else {
+- /* 640x480 */
+- usb_exchange(gspca_dev, po3130_initVGA_data);
+- }
+- usb_exchange(gspca_dev, po3130_rundata);
++ if (mode)
++ init = po3130_initQVGA_data; /* 320x240 */
++ else
++ init = po3130_initVGA_data; /* 640x480 */
++ usb_exchange(gspca_dev, init);
++ init = po3130_rundata;
+ break;
+- case SENSOR_PO1200:
++ default:
++/* case SENSOR_PO1200: */
+ GammaT = po1200_gamma;
+ MatrixT = po1200_matrix;
+- usb_exchange(gspca_dev, po1200_initVGA_data);
++ init = po1200_initVGA_data;
+ break;
+- default:
+- PDEBUG(D_PROBE, "Damned !! no sensor found Bye");
+- return -EMEDIUMTYPE;
+ }
++ usb_exchange(gspca_dev, init);
+ if (GammaT && MatrixT) {
+ put_tab_to_reg(gspca_dev, GammaT, 17, 0xb84a);
+ put_tab_to_reg(gspca_dev, GammaT, 17, 0xb85b);
+ put_tab_to_reg(gspca_dev, GammaT, 17, 0xb86c);
+ put_tab_to_reg(gspca_dev, MatrixT, 9, 0xb82c);
+
+- /* Seem SHARPNESS */
+- /*
+- reg_w(gspca_dev->dev, 0xa0, 0x80, 0xb80a);
+- reg_w(gspca_dev->dev, 0xa0, 0xff, 0xb80b);
+- reg_w(gspca_dev->dev, 0xa0, 0xff, 0xb80e);
+- */
+- /* all 0x40 ??? do nothing
+- reg_w(gspca_dev->dev, 0xa0, 0x40, 0xb822);
+- reg_w(gspca_dev->dev, 0xa0, 0x40, 0xb823);
+- reg_w(gspca_dev->dev, 0xa0, 0x40, 0xb824);
+- */
+- /* Only works for HV7131R ??
+- reg_r (gspca_dev, 0xa1, 0xb881, 1);
+- reg_w(gspca_dev->dev, 0xa0, 0xfe01, 0xb881);
+- reg_w(gspca_dev->dev, 0xa0, 0x79, 0xb801);
+- */
+- /* only hv7131r et ov7660
+- reg_w(gspca_dev->dev, 0xa0, 0x20, 0xb827);
+- reg_w(gspca_dev->dev, 0xa0, 0xff, 0xb826); * ISP_GAIN 80
+- reg_w(gspca_dev->dev, 0xa0, 0x23, 0xb800); * ISP CTRL_BAS
+- */
+ /* set the led on 0x0892 0x0896 */
+ if (sd->sensor != SENSOR_PO1200) {
+ reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff);
+@@ -2296,12 +3169,8 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ "vc032x header packet found len %d", len);
+ frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+ data, 0);
+- if (sd->bridge == BRIDGE_VC0321) {
+-#define VCHDRSZ 46
+- data += VCHDRSZ;
+- len -= VCHDRSZ;
+-#undef VCHDRSZ
+- }
++ data += sd->image_offset;
++ len -= sd->image_offset;
+ gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+ data, len);
+ return;
+@@ -2399,7 +3268,8 @@ static int sd_querymenu(struct gspca_dev *gspca_dev,
+ case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
+ strcpy((char *) menu->name, "50 Hz");
+ return 0;
+- case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
++ default:
++/* case 2: * V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
+ strcpy((char *) menu->name, "60 Hz");
+ return 0;
+ }
+@@ -2424,6 +3294,7 @@ static const struct sd_desc sd_desc = {
+
+ /* -- module initialisation -- */
+ static const __devinitdata struct usb_device_id device_table[] = {
++ {USB_DEVICE(0x041e, 0x405b), .driver_info = BRIDGE_VC0323},
+ {USB_DEVICE(0x046d, 0x0892), .driver_info = BRIDGE_VC0321},
+ {USB_DEVICE(0x046d, 0x0896), .driver_info = BRIDGE_VC0321},
+ {USB_DEVICE(0x046d, 0x0897), .driver_info = BRIDGE_VC0321},
+@@ -2432,6 +3303,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0ac8, 0x0328), .driver_info = BRIDGE_VC0321},
+ {USB_DEVICE(0x0ac8, 0xc001), .driver_info = BRIDGE_VC0321},
+ {USB_DEVICE(0x0ac8, 0xc002), .driver_info = BRIDGE_VC0321},
++ {USB_DEVICE(0x15b8, 0x6001), .driver_info = BRIDGE_VC0323},
+ {USB_DEVICE(0x15b8, 0x6002), .driver_info = BRIDGE_VC0323},
+ {USB_DEVICE(0x17ef, 0x4802), .driver_info = BRIDGE_VC0323},
+ {}
+@@ -2460,8 +3332,11 @@ static struct usb_driver sd_driver = {
+ /* -- module insert / remove -- */
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
+index ec2a53d..4fe01d8 100644
+--- a/drivers/media/video/gspca/zc3xx.c
++++ b/drivers/media/video/gspca/zc3xx.c
+@@ -23,6 +23,7 @@
+ #define MODULE_NAME "zc3xx"
+
+ #include "gspca.h"
++#include "jpeg.h"
+
+ MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>, "
+ "Serge A. Suchkov <Serge.A.S@tochka.ru>");
+@@ -31,7 +32,7 @@ MODULE_LICENSE("GPL");
+
+ static int force_sensor = -1;
+
+-#include "jpeg.h"
++#define QUANT_VAL 1 /* quantization table */
+ #include "zc3xx-reg.h"
+
+ /* specific webcam descriptor */
+@@ -44,30 +45,36 @@ struct sd {
+ __u8 autogain;
+ __u8 lightfreq;
+ __u8 sharpness;
++ u8 quality; /* image quality */
++#define QUALITY_MIN 40
++#define QUALITY_MAX 60
++#define QUALITY_DEF 50
+
+- char qindex;
+ signed char sensor; /* Type of image sensor chip */
+ /* !! values used in different tables */
+-#define SENSOR_CS2102 0
+-#define SENSOR_CS2102K 1
+-#define SENSOR_GC0305 2
+-#define SENSOR_HDCS2020b 3
+-#define SENSOR_HV7131B 4
+-#define SENSOR_HV7131C 5
+-#define SENSOR_ICM105A 6
+-#define SENSOR_MC501CB 7
+-#define SENSOR_OV7620 8
+-/*#define SENSOR_OV7648 8 - same values */
+-#define SENSOR_OV7630C 9
+-#define SENSOR_PAS106 10
+-#define SENSOR_PAS202B 11
+-#define SENSOR_PB0330 12
+-#define SENSOR_PO2030 13
+-#define SENSOR_TAS5130CK 14
+-#define SENSOR_TAS5130CXX 15
+-#define SENSOR_TAS5130C_VF0250 16
+-#define SENSOR_MAX 17
++#define SENSOR_ADCM2700 0
++#define SENSOR_CS2102 1
++#define SENSOR_CS2102K 2
++#define SENSOR_GC0305 3
++#define SENSOR_HDCS2020b 4
++#define SENSOR_HV7131B 5
++#define SENSOR_HV7131C 6
++#define SENSOR_ICM105A 7
++#define SENSOR_MC501CB 8
++#define SENSOR_OV7620 9
++/*#define SENSOR_OV7648 9 - same values */
++#define SENSOR_OV7630C 10
++#define SENSOR_PAS106 11
++#define SENSOR_PAS202B 12
++#define SENSOR_PB0330 13
++#define SENSOR_PO2030 14
++#define SENSOR_TAS5130CK 15
++#define SENSOR_TAS5130CXX 16
++#define SENSOR_TAS5130C_VF0250 17
++#define SENSOR_MAX 18
+ unsigned short chip_revision;
++
++ u8 *jpeg_hdr;
+ };
+
+ /* V4L2 controls supported by the driver */
+@@ -206,6 +213,213 @@ struct usb_action {
+ __u16 idx;
+ };
+
++static const struct usb_action adcm2700_Initial[] = {
++ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
++ {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */
++ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
++ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
++ {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */
++ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
++ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
++ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
++ {0xa0, 0xd8, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d8,cc */
++ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
++ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
++ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
++ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
++ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
++ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
++ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
++ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
++ {0xa0, 0xde, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,de,cc */
++ {0xa0, 0x86, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,86,cc */
++ {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */
++ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
++ {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */
++ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
++ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
++ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
++ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
++ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
++ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
++ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
++ {0xa0, 0x58, ZC3XX_R116_RGAIN}, /* 01,16,58,cc */
++ {0xa0, 0x5a, ZC3XX_R118_BGAIN}, /* 01,18,5a,cc */
++ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
++ {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */
++ {0xbb, 0x00, 0x0408}, /* 04,00,08,bb */
++ {0xdd, 0x00, 0x0200}, /* 00,02,00,dd */
++ {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */
++ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
++ {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */
++ {0xbb, 0xe0, 0x0c2e}, /* 0c,e0,2e,bb */
++ {0xbb, 0x01, 0x2000}, /* 20,01,00,bb */
++ {0xbb, 0x96, 0x2400}, /* 24,96,00,bb */
++ {0xbb, 0x06, 0x1006}, /* 10,06,06,bb */
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
++ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
++ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
++ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
++ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
++ {0xbb, 0x5f, 0x2090}, /* 20,5f,90,bb */
++ {0xbb, 0x01, 0x8000}, /* 80,01,00,bb */
++ {0xbb, 0x09, 0x8400}, /* 84,09,00,bb */
++ {0xbb, 0x86, 0x0002}, /* 00,86,02,bb */
++ {0xbb, 0xe6, 0x0401}, /* 04,e6,01,bb */
++ {0xbb, 0x86, 0x0802}, /* 08,86,02,bb */
++ {0xbb, 0xe6, 0x0c01}, /* 0c,e6,01,bb */
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
++ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
++ {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */
++ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
++ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
++ {0xaa, 0xfe, 0x0020}, /* 00,fe,20,aa */
++/*mswin+*/
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
++ {0xaa, 0xfe, 0x0002},
++ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
++ {0xaa, 0xb4, 0xcd37},
++ {0xaa, 0xa4, 0x0004},
++ {0xaa, 0xa8, 0x0007},
++ {0xaa, 0xac, 0x0004},
++/*mswin-*/
++ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
++ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
++ {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */
++ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
++ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
++ {0xbb, 0x04, 0x0400}, /* 04,04,00,bb */
++ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
++ {0xbb, 0x01, 0x0400}, /* 04,01,00,bb */
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
++ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
++ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
++ {0xbb, 0x41, 0x2803}, /* 28,41,03,bb */
++ {0xbb, 0x40, 0x2c03}, /* 2c,40,03,bb */
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
++ {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
++ {}
++};
++static const struct usb_action adcm2700_InitialScale[] = {
++ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
++ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
++ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
++ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
++ {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */
++ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
++ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
++ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
++ {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */
++ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
++ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
++ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
++ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,05,cc */
++ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW}, /* 00,98,00,cc */
++ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW}, /* 00,9a,00,cc */
++ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,00,cc */
++ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,00,cc */
++ {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */
++ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
++ {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */
++ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
++ {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */
++ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /* 01,01,37,cc */
++ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0d,cc */
++ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS}, /* 01,89,06,cc */
++ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE}, /* 01,c5,03,cc */
++ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05}, /* 01,cb,13,cc */
++ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
++ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
++ {0xa0, 0x58, ZC3XX_R116_RGAIN}, /* 01,16,58,cc */
++ {0xa0, 0x5a, ZC3XX_R118_BGAIN}, /* 01,18,5a,cc */
++ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
++ {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */
++ {0xbb, 0x00, 0x0408}, /* 04,00,08,bb */
++ {0xdd, 0x00, 0x0200}, /* 00,02,00,dd */
++ {0xbb, 0x00, 0x0400}, /* 04,00,00,bb */
++ {0xdd, 0x00, 0x0050}, /* 00,00,50,dd */
++ {0xbb, 0x0f, 0x140f}, /* 14,0f,0f,bb */
++ {0xbb, 0xe0, 0x0c2e}, /* 0c,e0,2e,bb */
++ {0xbb, 0x01, 0x2000}, /* 20,01,00,bb */
++ {0xbb, 0x96, 0x2400}, /* 24,96,00,bb */
++ {0xbb, 0x06, 0x1006}, /* 10,06,06,bb */
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
++ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
++ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
++ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
++ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
++ {0xbb, 0x5f, 0x2090}, /* 20,5f,90,bb */
++ {0xbb, 0x01, 0x8000}, /* 80,01,00,bb */
++ {0xbb, 0x09, 0x8400}, /* 84,09,00,bb */
++ {0xbb, 0x86, 0x0002}, /* 00,88,02,bb */
++ {0xbb, 0xe6, 0x0401}, /* 04,e6,01,bb */
++ {0xbb, 0x86, 0x0802}, /* 08,88,02,bb */
++ {0xbb, 0xe6, 0x0c01}, /* 0c,e6,01,bb */
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
++ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
++ {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */
++ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
++ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
++ {0xaa, 0xfe, 0x0020}, /* 00,fe,20,aa */
++ /*******/
++ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
++ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
++ {0xaa, 0xfe, 0x0000}, /* 00,fe,00,aa */
++ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
++ {0xdd, 0x00, 0x0010}, /* 00,00,10,dd */
++ {0xbb, 0x04, 0x0400}, /* 04,04,00,bb */
++ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
++ {0xbb, 0x01, 0x0400}, /* 04,01,00,bb */
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
++ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
++ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
++ {0xbb, 0x41, 0x2803}, /* 28,41,03,bb */
++ {0xbb, 0x40, 0x2c03}, /* 2c,40,03,bb */
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
++ {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
++ {}
++};
++static const struct usb_action adcm2700_50HZ[] = {
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
++ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
++ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
++ {0xbb, 0x05, 0x8400}, /* 84,05,00,bb */
++ {0xbb, 0xd0, 0xb007}, /* b0,d0,07,bb */
++ {0xbb, 0xa0, 0xb80f}, /* b8,a0,0f,bb */
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
++ {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
++ {0xaa, 0x26, 0x00d0}, /* 00,26,d0,aa */
++ {0xaa, 0x28, 0x0002}, /* 00,28,02,aa */
++ {}
++};
++static const struct usb_action adcm2700_60HZ[] = {
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
++ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
++ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
++ {0xbb, 0x07, 0x8400}, /* 84,07,00,bb */
++ {0xbb, 0x82, 0xb006}, /* b0,82,06,bb */
++ {0xbb, 0x04, 0xb80d}, /* b8,04,0d,bb */
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
++ {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
++ {0xaa, 0x26, 0x0057}, /* 00,26,57,aa */
++ {0xaa, 0x28, 0x0002}, /* 00,28,02,aa */
++ {}
++};
++static const struct usb_action adcm2700_NoFliker[] = {
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
++ {0xaa, 0xfe, 0x0002}, /* 00,fe,02,aa */
++ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
++ {0xbb, 0x07, 0x8400}, /* 84,07,00,bb */
++ {0xbb, 0x05, 0xb000}, /* b0,05,00,bb */
++ {0xbb, 0xa0, 0xb801}, /* b8,a0,01,bb */
++ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
++ {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
++ {}
++};
+ static const struct usb_action cs2102_Initial[] = {
+ {0xa1, 0x01, 0x0008},
+ {0xa1, 0x01, 0x0008},
+@@ -877,7 +1091,7 @@ static const struct usb_action cs2102K_Initial[] = {
+ };
+
+ static const struct usb_action cs2102K_InitialScale[] = {
+- {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT},
++ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
+@@ -894,6 +1108,7 @@ static const struct usb_action cs2102K_InitialScale[] = {
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
++/*fixme: next sequence = i2c exchanges*/
+ {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+@@ -1077,207 +1292,6 @@ static const struct usb_action cs2102K_InitialScale[] = {
+ {0xa0, 0x60, ZC3XX_R116_RGAIN},
+ {0xa0, 0x40, ZC3XX_R117_GGAIN},
+ {0xa0, 0x4c, ZC3XX_R118_BGAIN},
+- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+- {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+- {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
+- {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+- {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+- {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+- {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+- {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+- {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+- {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+- {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+- {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+- {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+- {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW},
+- {0xa0, 0x55, ZC3XX_R08B_I2CDEVICEADDR},
+- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x0A, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x0B, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x02, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x0C, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x7b, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x0D, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0xA3, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x03, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0xfb, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x05, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x06, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x03, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x09, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x08, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x0E, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x0f, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x10, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x11, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x12, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x18, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x15, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x16, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x0c, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x17, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x0C, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION},
+- {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+- {0xa0, 0x78, ZC3XX_R18D_YTARGET},
+- {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+- {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+- {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+- {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+- {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID},
+- {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW},
+- {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+- {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+- {0xa0, 0x00, 0x01ad},
+- {0xa0, 0x01, 0x01b1},
+- {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa0, 0x60, ZC3XX_R116_RGAIN},
+- {0xa0, 0x40, ZC3XX_R117_GGAIN},
+- {0xa0, 0x4c, ZC3XX_R118_BGAIN},
+- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+- {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */
+- {0xa0, 0x38, ZC3XX_R121_GAMMA01},
+- {0xa0, 0x59, ZC3XX_R122_GAMMA02},
+- {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+- {0xa0, 0x92, ZC3XX_R124_GAMMA04},
+- {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
+- {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
+- {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
+- {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
+- {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
+- {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
+- {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
+- {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
+- {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
+- {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
+- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+- {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+- {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+- {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+- {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+- {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+- {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+- {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+- {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+- {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+- {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+- {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+- {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+- {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+- {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
+- {0xa0, 0xf4, ZC3XX_R10B_RGB01},
+- {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+- {0xa0, 0xf4, ZC3XX_R10D_RGB10},
+- {0xa0, 0x58, ZC3XX_R10E_RGB11},
+- {0xa0, 0xf4, ZC3XX_R10F_RGB12},
+- {0xa0, 0xf4, ZC3XX_R110_RGB20},
+- {0xa0, 0xf4, ZC3XX_R111_RGB21},
+- {0xa0, 0x58, ZC3XX_R112_RGB22},
+- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x00, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x13, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x14, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x21, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x22, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x18, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x04, ZC3XX_R093_I2CSETVALUE},
+- {0xa0, 0x00, ZC3XX_R094_I2CWRITEACK},
+- {0xa0, 0x01, ZC3XX_R090_I2CCOMMAND},
+- {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH},
+- {0xa0, 0x22, ZC3XX_R0A4_EXPOSURETIMELOW},
+- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+- {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW},
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+- {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW},
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+- {0xa0, 0x28, ZC3XX_R1AA_DIGITALGAINSTEP},
+- {0xa0, 0x04, ZC3XX_R01D_HSYNC_0},
+- {0xa0, 0x0f, ZC3XX_R01E_HSYNC_1},
+- {0xa0, 0x19, ZC3XX_R01F_HSYNC_2},
+- {0xa0, 0x1f, ZC3XX_R020_HSYNC_3},
+- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa0, 0x60, ZC3XX_R116_RGAIN},
+- {0xa0, 0x40, ZC3XX_R117_GGAIN},
+- {0xa0, 0x4c, ZC3XX_R118_BGAIN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x20, ZC3XX_R092_I2CADDRESSSELECT},
+ {0xa0, 0x01, ZC3XX_R093_I2CSETVALUE},
+@@ -1334,6 +1348,7 @@ static const struct usb_action cs2102K_InitialScale[] = {
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
++/*fixme:what does the next sequence?*/
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x00, ZC3XX_R1A7_CALCGLOBALMEAN},
+ {0xa0, 0x04, ZC3XX_R1A7_CALCGLOBALMEAN},
+@@ -6237,7 +6252,7 @@ static const struct usb_action tas5130c_vf0250_NoFlikerScale[] = {
+ {}
+ };
+
+-static int reg_r_i(struct gspca_dev *gspca_dev,
++static u8 reg_r_i(struct gspca_dev *gspca_dev,
+ __u16 index)
+ {
+ usb_control_msg(gspca_dev->dev,
+@@ -6250,10 +6265,10 @@ static int reg_r_i(struct gspca_dev *gspca_dev,
+ return gspca_dev->usb_buf[0];
+ }
+
+-static int reg_r(struct gspca_dev *gspca_dev,
++static u8 reg_r(struct gspca_dev *gspca_dev,
+ __u16 index)
+ {
+- int ret;
++ u8 ret;
+
+ ret = reg_r_i(gspca_dev, index);
+ PDEBUG(D_USBI, "reg r [%04x] -> %02x", index, ret);
+@@ -6286,8 +6301,8 @@ static __u16 i2c_read(struct gspca_dev *gspca_dev,
+ __u8 retbyte;
+ __u16 retval;
+
+- reg_w_i(gspca_dev->dev, reg, 0x92);
+- reg_w_i(gspca_dev->dev, 0x02, 0x90); /* <- read command */
++ reg_w_i(gspca_dev->dev, reg, 0x0092);
++ reg_w_i(gspca_dev->dev, 0x02, 0x0090); /* <- read command */
+ msleep(25);
+ retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
+ retval = reg_r_i(gspca_dev, 0x0095); /* read Lowbyte */
+@@ -6332,6 +6347,12 @@ static void usb_exchange(struct gspca_dev *gspca_dev,
+ action->idx & 0xff, /* valL */
+ action->idx >> 8); /* valH */
+ break;
++ case 0xbb:
++ i2c_write(gspca_dev,
++ action->idx >> 8, /* reg */
++ action->idx & 0xff, /* valL */
++ action->val); /* valH */
++ break;
+ default:
+ /* case 0xdd: * delay */
+ msleep(action->val / 64 + 10);
+@@ -6347,6 +6368,10 @@ static void setmatrix(struct gspca_dev *gspca_dev)
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+ const __u8 *matrix;
++ static const u8 adcm2700_matrix[9] =
++/* {0x66, 0xed, 0xed, 0xed, 0x66, 0xed, 0xed, 0xed, 0x66}; */
++/*ms-win*/
++ {0x74, 0xed, 0xed, 0xed, 0x74, 0xed, 0xed, 0xed, 0x74};
+ static const __u8 gc0305_matrix[9] =
+ {0x50, 0xf8, 0xf8, 0xf8, 0x50, 0xf8, 0xf8, 0xf8, 0x50};
+ static const __u8 ov7620_matrix[9] =
+@@ -6358,23 +6383,24 @@ static void setmatrix(struct gspca_dev *gspca_dev)
+ static const __u8 vf0250_matrix[9] =
+ {0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b};
+ static const __u8 *matrix_tb[SENSOR_MAX] = {
+- NULL, /* SENSOR_CS2102 0 */
+- NULL, /* SENSOR_CS2102K 1 */
+- gc0305_matrix, /* SENSOR_GC0305 2 */
+- NULL, /* SENSOR_HDCS2020b 3 */
+- NULL, /* SENSOR_HV7131B 4 */
+- NULL, /* SENSOR_HV7131C 5 */
+- NULL, /* SENSOR_ICM105A 6 */
+- NULL, /* SENSOR_MC501CB 7 */
+- ov7620_matrix, /* SENSOR_OV7620 8 */
+- NULL, /* SENSOR_OV7630C 9 */
+- NULL, /* SENSOR_PAS106 10 */
+- pas202b_matrix, /* SENSOR_PAS202B 11 */
+- NULL, /* SENSOR_PB0330 12 */
+- po2030_matrix, /* SENSOR_PO2030 13 */
+- NULL, /* SENSOR_TAS5130CK 14 */
+- NULL, /* SENSOR_TAS5130CXX 15 */
+- vf0250_matrix, /* SENSOR_TAS5130C_VF0250 16 */
++ adcm2700_matrix, /* SENSOR_ADCM2700 0 */
++ NULL, /* SENSOR_CS2102 1 */
++ NULL, /* SENSOR_CS2102K 2 */
++ gc0305_matrix, /* SENSOR_GC0305 3 */
++ NULL, /* SENSOR_HDCS2020b 4 */
++ NULL, /* SENSOR_HV7131B 5 */
++ NULL, /* SENSOR_HV7131C 6 */
++ NULL, /* SENSOR_ICM105A 7 */
++ NULL, /* SENSOR_MC501CB 8 */
++ ov7620_matrix, /* SENSOR_OV7620 9 */
++ NULL, /* SENSOR_OV7630C 10 */
++ NULL, /* SENSOR_PAS106 11 */
++ pas202b_matrix, /* SENSOR_PAS202B 12 */
++ NULL, /* SENSOR_PB0330 13 */
++ po2030_matrix, /* SENSOR_PO2030 14 */
++ NULL, /* SENSOR_TAS5130CK 15 */
++ NULL, /* SENSOR_TAS5130CXX 16 */
++ vf0250_matrix, /* SENSOR_TAS5130C_VF0250 17 */
+ };
+
+ matrix = matrix_tb[sd->sensor];
+@@ -6398,8 +6424,11 @@ static void setbrightness(struct gspca_dev *gspca_dev)
+ /*fixme: is it really write to 011d and 018d for all other sensors? */
+ brightness = sd->brightness;
+ reg_w(gspca_dev->dev, brightness, 0x011d);
+- if (sd->sensor == SENSOR_HV7131B)
++ switch (sd->sensor) {
++ case SENSOR_ADCM2700:
++ case SENSOR_HV7131B:
+ return;
++ }
+ if (brightness < 0x70)
+ brightness += 0x10;
+ else
+@@ -6536,10 +6565,10 @@ static void setquality(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct usb_device *dev = gspca_dev->dev;
+- __u8 quality;
+ __u8 frxt;
+
+ switch (sd->sensor) {
++ case SENSOR_ADCM2700:
+ case SENSOR_GC0305:
+ case SENSOR_HV7131B:
+ case SENSOR_OV7620:
+@@ -6547,26 +6576,18 @@ static void setquality(struct gspca_dev *gspca_dev)
+ return;
+ }
+ /*fixme: is it really 0008 0007 0018 for all other sensors? */
+- quality = sd->qindex;
+- reg_w(dev, quality, 0x0008);
++ reg_w(dev, QUANT_VAL, 0x0008);
+ frxt = 0x30;
+ reg_w(dev, frxt, 0x0007);
+- switch (quality) {
+- case 0:
+- case 1:
+- case 2:
+- frxt = 0xff;
+- break;
+- case 3:
+- frxt = 0xf0;
+- break;
+- case 4:
+- frxt = 0xe0;
+- break;
+- case 5:
+- frxt = 0x20;
+- break;
+- }
++#if QUANT_VAL == 0 || QUANT_VAL == 1 || QUANT_VAL == 2
++ frxt = 0xff;
++#elif QUANT_VAL == 3
++ frxt = 0xf0;
++#elif QUANT_VAL == 4
++ frxt = 0xe0;
++#else
++ frxt = 0x20;
++#endif
+ reg_w(dev, frxt, 0x0018);
+ }
+
+@@ -6583,71 +6604,75 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
+ int i, mode;
+ const struct usb_action *zc3_freq;
+ static const struct usb_action *freq_tb[SENSOR_MAX][6] = {
+-/* SENSOR_CS2102 0 */
++/* SENSOR_ADCM2700 0 */
++ {adcm2700_NoFliker, adcm2700_NoFliker,
++ adcm2700_50HZ, adcm2700_50HZ,
++ adcm2700_60HZ, adcm2700_60HZ},
++/* SENSOR_CS2102 1 */
+ {cs2102_NoFliker, cs2102_NoFlikerScale,
+ cs2102_50HZ, cs2102_50HZScale,
+ cs2102_60HZ, cs2102_60HZScale},
+-/* SENSOR_CS2102K 1 */
++/* SENSOR_CS2102K 2 */
+ {cs2102_NoFliker, cs2102_NoFlikerScale,
+ NULL, NULL, /* currently disabled */
+ NULL, NULL},
+-/* SENSOR_GC0305 2 */
++/* SENSOR_GC0305 3 */
+ {gc0305_NoFliker, gc0305_NoFliker,
+ gc0305_50HZ, gc0305_50HZ,
+ gc0305_60HZ, gc0305_60HZ},
+-/* SENSOR_HDCS2020b 3 */
++/* SENSOR_HDCS2020b 4 */
+ {hdcs2020b_NoFliker, hdcs2020b_NoFliker,
+ hdcs2020b_50HZ, hdcs2020b_50HZ,
+ hdcs2020b_60HZ, hdcs2020b_60HZ},
+-/* SENSOR_HV7131B 4 */
++/* SENSOR_HV7131B 5 */
+ {hv7131b_NoFlikerScale, hv7131b_NoFliker,
+ hv7131b_50HZScale, hv7131b_50HZ,
+ hv7131b_60HZScale, hv7131b_60HZ},
+-/* SENSOR_HV7131C 5 */
++/* SENSOR_HV7131C 6 */
+ {NULL, NULL,
+ NULL, NULL,
+ NULL, NULL},
+-/* SENSOR_ICM105A 6 */
++/* SENSOR_ICM105A 7 */
+ {icm105a_NoFliker, icm105a_NoFlikerScale,
+ icm105a_50HZ, icm105a_50HZScale,
+ icm105a_60HZ, icm105a_60HZScale},
+-/* SENSOR_MC501CB 7 */
++/* SENSOR_MC501CB 8 */
+ {MC501CB_NoFliker, MC501CB_NoFlikerScale,
+ MC501CB_50HZ, MC501CB_50HZScale,
+ MC501CB_60HZ, MC501CB_60HZScale},
+-/* SENSOR_OV7620 8 */
++/* SENSOR_OV7620 9 */
+ {OV7620_NoFliker, OV7620_NoFliker,
+ OV7620_50HZ, OV7620_50HZ,
+ OV7620_60HZ, OV7620_60HZ},
+-/* SENSOR_OV7630C 9 */
++/* SENSOR_OV7630C 10 */
+ {NULL, NULL,
+ NULL, NULL,
+ NULL, NULL},
+-/* SENSOR_PAS106 10 */
++/* SENSOR_PAS106 11 */
+ {pas106b_NoFliker, pas106b_NoFliker,
+ pas106b_50HZ, pas106b_50HZ,
+ pas106b_60HZ, pas106b_60HZ},
+-/* SENSOR_PAS202B 11 */
++/* SENSOR_PAS202B 12 */
+ {pas202b_NoFlikerScale, pas202b_NoFliker,
+ pas202b_50HZScale, pas202b_50HZ,
+ pas202b_60HZScale, pas202b_60HZ},
+-/* SENSOR_PB0330 12 */
++/* SENSOR_PB0330 13 */
+ {pb0330_NoFliker, pb0330_NoFlikerScale,
+ pb0330_50HZ, pb0330_50HZScale,
+ pb0330_60HZ, pb0330_60HZScale},
+-/* SENSOR_PO2030 13 */
++/* SENSOR_PO2030 14 */
+ {PO2030_NoFliker, PO2030_NoFliker,
+ PO2030_50HZ, PO2030_50HZ,
+ PO2030_60HZ, PO2030_60HZ},
+-/* SENSOR_TAS5130CK 14 */
++/* SENSOR_TAS5130CK 15 */
+ {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale,
+ tas5130cxx_50HZ, tas5130cxx_50HZScale,
+ tas5130cxx_60HZ, tas5130cxx_60HZScale},
+-/* SENSOR_TAS5130CXX 15 */
++/* SENSOR_TAS5130CXX 16 */
+ {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale,
+ tas5130cxx_50HZ, tas5130cxx_50HZScale,
+ tas5130cxx_60HZ, tas5130cxx_60HZScale},
+-/* SENSOR_TAS5130C_VF0250 16 */
++/* SENSOR_TAS5130C_VF0250 17 */
+ {tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale,
+ tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale,
+ tas5130c_vf0250_60HZ, tas5130c_vf0250_60HZScale},
+@@ -6701,6 +6726,7 @@ static void send_unknown(struct usb_device *dev, int sensor)
+ reg_w(dev, 0x0c, 0x003b);
+ reg_w(dev, 0x08, 0x0038);
+ break;
++ case SENSOR_ADCM2700:
+ case SENSOR_GC0305:
+ case SENSOR_OV7620:
+ case SENSOR_PB0330:
+@@ -6743,26 +6769,25 @@ static int sif_probe(struct gspca_dev *gspca_dev)
+ static int vga_2wr_probe(struct gspca_dev *gspca_dev)
+ {
+ struct usb_device *dev = gspca_dev->dev;
+- __u8 retbyte;
+- __u16 checkword;
++ u16 retword;
+
+ start_2wr_probe(dev, 0x00); /* HV7131B */
+ i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
+- retbyte = i2c_read(gspca_dev, 0x01);
+- if (retbyte != 0)
++ retword = i2c_read(gspca_dev, 0x01);
++ if (retword != 0)
+ return 0x00; /* HV7131B */
+
+ start_2wr_probe(dev, 0x04); /* CS2102 */
+ i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
+- retbyte = i2c_read(gspca_dev, 0x01);
+- if (retbyte != 0)
++ retword = i2c_read(gspca_dev, 0x01);
++ if (retword != 0)
+ return 0x04; /* CS2102 */
+
+ start_2wr_probe(dev, 0x06); /* OmniVision */
+ reg_w(dev, 0x08, 0x008d);
+ i2c_write(gspca_dev, 0x11, 0xaa, 0x00);
+- retbyte = i2c_read(gspca_dev, 0x11);
+- if (retbyte != 0) {
++ retword = i2c_read(gspca_dev, 0x11);
++ if (retword != 0) {
+ /* (should have returned 0xaa) --> Omnivision? */
+ /* reg_r 0x10 -> 0x06 --> */
+ goto ov_check;
+@@ -6770,40 +6795,40 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev)
+
+ start_2wr_probe(dev, 0x08); /* HDCS2020 */
+ i2c_write(gspca_dev, 0x15, 0xaa, 0x00);
+- retbyte = i2c_read(gspca_dev, 0x15);
+- if (retbyte != 0)
++ retword = i2c_read(gspca_dev, 0x15);
++ if (retword != 0)
+ return 0x08; /* HDCS2020 */
+
+ start_2wr_probe(dev, 0x0a); /* PB0330 */
+ i2c_write(gspca_dev, 0x07, 0xaa, 0xaa);
+- retbyte = i2c_read(gspca_dev, 0x07);
+- if (retbyte != 0)
++ retword = i2c_read(gspca_dev, 0x07);
++ if (retword != 0)
+ return 0x0a; /* PB0330 */
+- retbyte = i2c_read(gspca_dev, 0x03);
+- if (retbyte != 0)
++ retword = i2c_read(gspca_dev, 0x03);
++ if (retword != 0)
+ return 0x0a; /* PB0330 ?? */
+- retbyte = i2c_read(gspca_dev, 0x04);
+- if (retbyte != 0)
++ retword = i2c_read(gspca_dev, 0x04);
++ if (retword != 0)
+ return 0x0a; /* PB0330 ?? */
+
+ start_2wr_probe(dev, 0x0c); /* ICM105A */
+ i2c_write(gspca_dev, 0x01, 0x11, 0x00);
+- retbyte = i2c_read(gspca_dev, 0x01);
+- if (retbyte != 0)
++ retword = i2c_read(gspca_dev, 0x01);
++ if (retword != 0)
+ return 0x0c; /* ICM105A */
+
+ start_2wr_probe(dev, 0x0e); /* PAS202BCB */
+ reg_w(dev, 0x08, 0x008d);
+ i2c_write(gspca_dev, 0x03, 0xaa, 0x00);
+ msleep(500);
+- retbyte = i2c_read(gspca_dev, 0x03);
+- if (retbyte != 0)
++ retword = i2c_read(gspca_dev, 0x03);
++ if (retword != 0)
+ return 0x0e; /* PAS202BCB */
+
+ start_2wr_probe(dev, 0x02); /* ?? */
+ i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
+- retbyte = i2c_read(gspca_dev, 0x01);
+- if (retbyte != 0)
++ retword = i2c_read(gspca_dev, 0x01);
++ if (retword != 0)
+ return 0x02; /* ?? */
+ ov_check:
+ reg_r(gspca_dev, 0x0010); /* ?? */
+@@ -6817,12 +6842,10 @@ ov_check:
+ msleep(500);
+ reg_w(dev, 0x01, 0x0012);
+ i2c_write(gspca_dev, 0x12, 0x80, 0x00); /* sensor reset */
+- retbyte = i2c_read(gspca_dev, 0x0a);
+- checkword = retbyte << 8;
+- retbyte = i2c_read(gspca_dev, 0x0b);
+- checkword |= retbyte;
+- PDEBUG(D_PROBE, "probe 2wr ov vga 0x%04x", checkword);
+- switch (checkword) {
++ retword = i2c_read(gspca_dev, 0x0a) << 8;
++ retword |= i2c_read(gspca_dev, 0x0b);
++ PDEBUG(D_PROBE, "probe 2wr ov vga 0x%04x", retword);
++ switch (retword) {
+ case 0x7631: /* OV7630C */
+ reg_w(dev, 0x06, 0x0010);
+ break;
+@@ -6832,7 +6855,7 @@ ov_check:
+ default:
+ return -1; /* not OmniVision */
+ }
+- return checkword;
++ return retword;
+ }
+
+ struct sensor_by_chipset_revision {
+@@ -6845,6 +6868,7 @@ static const struct sensor_by_chipset_revision chipset_revision_sensor[] = {
+ {0x8001, 0x13},
+ {0x8000, 0x14}, /* CS2102K */
+ {0x8400, 0x15}, /* TAS5130K */
++ {0x4001, 0x16}, /* ADCM2700 */
+ };
+
+ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+@@ -6853,7 +6877,7 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+ struct usb_device *dev = gspca_dev->dev;
+ int i;
+ __u8 retbyte;
+- __u16 checkword;
++ u16 retword;
+
+ /*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/
+ reg_w(dev, 0x02, 0x0010);
+@@ -6865,27 +6889,25 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+ reg_w(dev, 0x03, 0x0012);
+ reg_w(dev, 0x01, 0x0012);
+ reg_w(dev, 0x05, 0x0012);
+- retbyte = i2c_read(gspca_dev, 0x14);
+- if (retbyte != 0)
++ retword = i2c_read(gspca_dev, 0x14);
++ if (retword != 0)
+ return 0x11; /* HV7131R */
+- retbyte = i2c_read(gspca_dev, 0x15);
+- if (retbyte != 0)
++ retword = i2c_read(gspca_dev, 0x15);
++ if (retword != 0)
+ return 0x11; /* HV7131R */
+- retbyte = i2c_read(gspca_dev, 0x16);
+- if (retbyte != 0)
++ retword = i2c_read(gspca_dev, 0x16);
++ if (retword != 0)
+ return 0x11; /* HV7131R */
+
+ reg_w(dev, 0x02, 0x0010);
+- retbyte = reg_r(gspca_dev, 0x000b);
+- checkword = retbyte << 8;
+- retbyte = reg_r(gspca_dev, 0x000a);
+- checkword |= retbyte;
+- PDEBUG(D_PROBE, "probe 3wr vga 1 0x%04x", checkword);
++ retword = reg_r(gspca_dev, 0x000b) << 8;
++ retword |= reg_r(gspca_dev, 0x000a);
++ PDEBUG(D_PROBE, "probe 3wr vga 1 0x%04x", retword);
+ reg_r(gspca_dev, 0x0010);
+ /* this is tested only once anyway */
+ for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) {
+- if (chipset_revision_sensor[i].revision == checkword) {
+- sd->chip_revision = checkword;
++ if (chipset_revision_sensor[i].revision == retword) {
++ sd->chip_revision = retword;
+ send_unknown(dev, SENSOR_PB0330);
+ return chipset_revision_sensor[i].internal_sensor_id;
+ }
+@@ -6897,8 +6919,8 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+ reg_w(dev, 0x0a, 0x0010);
+ reg_w(dev, 0x03, 0x0012);
+ reg_w(dev, 0x01, 0x0012);
+- retbyte = i2c_read(gspca_dev, 0x00);
+- if (retbyte != 0) {
++ retword = i2c_read(gspca_dev, 0x00);
++ if (retword != 0) {
+ PDEBUG(D_PROBE, "probe 3wr vga type 0a ?");
+ return 0x0a; /* ?? */
+ }
+@@ -6910,14 +6932,14 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+ reg_w(dev, 0x03, 0x0012);
+ msleep(2);
+ reg_w(dev, 0x01, 0x0012);
+- retbyte = i2c_read(gspca_dev, 0x00);
+- if (retbyte != 0) {
+- PDEBUG(D_PROBE, "probe 3wr vga type %02x", retbyte);
+- if (retbyte == 0x11) /* VF0250 */
++ retword = i2c_read(gspca_dev, 0x00);
++ if (retword != 0) {
++ PDEBUG(D_PROBE, "probe 3wr vga type %02x", retword);
++ if (retword == 0x0011) /* VF0250 */
+ return 0x0250;
+- if (retbyte == 0x29) /* gc0305 */
++ if (retword == 0x0029) /* gc0305 */
+ send_unknown(dev, SENSOR_GC0305);
+- return retbyte;
++ return retword;
+ }
+
+ reg_w(dev, 0x01, 0x0000); /* check OmniVision */
+@@ -6927,8 +6949,8 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+ reg_w(dev, 0x06, 0x0010);
+ reg_w(dev, 0x01, 0x0012);
+ reg_w(dev, 0x05, 0x0012);
+- if (i2c_read(gspca_dev, 0x1c) == 0x7f /* OV7610 - manufacturer ID */
+- && i2c_read(gspca_dev, 0x1d) == 0xa2) {
++ if (i2c_read(gspca_dev, 0x1c) == 0x007f /* OV7610 - manufacturer ID */
++ && i2c_read(gspca_dev, 0x1d) == 0x00a2) {
+ send_unknown(dev, SENSOR_OV7620);
+ return 0x06; /* OmniVision confirm ? */
+ }
+@@ -6942,16 +6964,14 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+ /* msleep(150); */
+ reg_w(dev, 0x01, 0x0012);
+ reg_w(dev, 0x05, 0x0012);
+- retbyte = i2c_read(gspca_dev, 0x0000); /* ID 0 */
+- checkword = retbyte << 8;
+- retbyte = i2c_read(gspca_dev, 0x0001); /* ID 1 */
+- checkword |= retbyte;
+- PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", checkword);
+- if (checkword == 0x2030) {
++ retword = i2c_read(gspca_dev, 0x00) << 8; /* ID 0 */
++ retword |= i2c_read(gspca_dev, 0x01); /* ID 1 */
++ PDEBUG(D_PROBE, "probe 3wr vga 2 0x%04x", retword);
++ if (retword == 0x2030) {
+ retbyte = i2c_read(gspca_dev, 0x02); /* revision number */
+ PDEBUG(D_PROBE, "sensor PO2030 rev 0x%02x", retbyte);
+ send_unknown(dev, SENSOR_PO2030);
+- return checkword;
++ return retword;
+ }
+
+ reg_w(dev, 0x01, 0x0000);
+@@ -6962,10 +6982,10 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+ reg_w(dev, 0x01, 0x0012);
+ reg_w(dev, 0x05, 0x0001);
+ reg_w(dev, 0xd3, 0x008b);
+- retbyte = i2c_read(gspca_dev, 0x01);
+- if (retbyte != 0) {
+- PDEBUG(D_PROBE, "probe 3wr vga type 0a ?");
+- return 0x0a; /* ?? */
++ retword = i2c_read(gspca_dev, 0x01);
++ if (retword != 0) {
++ PDEBUG(D_PROBE, "probe 3wr vga type 0a ? ret: %04x", retword);
++ return retword;
+ }
+ return -1;
+ }
+@@ -6973,7 +6993,7 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+ static int zcxx_probeSensor(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- int sensor, sensor2;
++ int sensor;
+
+ switch (sd->sensor) {
+ case SENSOR_MC501CB:
+@@ -6988,16 +7008,9 @@ static int zcxx_probeSensor(struct gspca_dev *gspca_dev)
+ break;
+ }
+ sensor = vga_2wr_probe(gspca_dev);
+- if (sensor >= 0) {
+- if (sensor < 0x7600)
+- return sensor;
+- /* next probe is needed for OmniVision ? */
+- }
+- sensor2 = vga_3wr_probe(gspca_dev);
+- if (sensor2 >= 0
+- && sensor >= 0)
++ if (sensor >= 0)
+ return sensor;
+- return sensor2;
++ return vga_3wr_probe(gspca_dev);
+ }
+
+ /* this function is called at probe time */
+@@ -7009,23 +7022,24 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ int sensor;
+ int vga = 1; /* 1: vga, 0: sif */
+ static const __u8 gamma[SENSOR_MAX] = {
+- 5, /* SENSOR_CS2102 0 */
+- 5, /* SENSOR_CS2102K 1 */
+- 4, /* SENSOR_GC0305 2 */
+- 4, /* SENSOR_HDCS2020b 3 */
+- 4, /* SENSOR_HV7131B 4 */
+- 4, /* SENSOR_HV7131C 5 */
+- 4, /* SENSOR_ICM105A 6 */
+- 4, /* SENSOR_MC501CB 7 */
+- 3, /* SENSOR_OV7620 8 */
+- 4, /* SENSOR_OV7630C 9 */
+- 4, /* SENSOR_PAS106 10 */
+- 4, /* SENSOR_PAS202B 11 */
+- 4, /* SENSOR_PB0330 12 */
+- 4, /* SENSOR_PO2030 13 */
+- 4, /* SENSOR_TAS5130CK 14 */
+- 4, /* SENSOR_TAS5130CXX 15 */
+- 3, /* SENSOR_TAS5130C_VF0250 16 */
++ 4, /* SENSOR_ADCM2700 0 */
++ 5, /* SENSOR_CS2102 1 */
++ 5, /* SENSOR_CS2102K 2 */
++ 4, /* SENSOR_GC0305 3 */
++ 4, /* SENSOR_HDCS2020b 4 */
++ 4, /* SENSOR_HV7131B 5 */
++ 4, /* SENSOR_HV7131C 6 */
++ 4, /* SENSOR_ICM105A 7 */
++ 4, /* SENSOR_MC501CB 8 */
++ 3, /* SENSOR_OV7620 9 */
++ 4, /* SENSOR_OV7630C 10 */
++ 4, /* SENSOR_PAS106 11 */
++ 4, /* SENSOR_PAS202B 12 */
++ 4, /* SENSOR_PB0330 13 */
++ 4, /* SENSOR_PO2030 14 */
++ 4, /* SENSOR_TAS5130CK 15 */
++ 4, /* SENSOR_TAS5130CXX 16 */
++ 3, /* SENSOR_TAS5130C_VF0250 17 */
+ };
+
+ /* define some sensors from the vendor/product */
+@@ -7033,7 +7047,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ sd->sensor = id->driver_info;
+ sensor = zcxx_probeSensor(gspca_dev);
+ if (sensor >= 0)
+- PDEBUG(D_PROBE, "probe sensor -> %02x", sensor);
++ PDEBUG(D_PROBE, "probe sensor -> %04x", sensor);
+ if ((unsigned) force_sensor < SENSOR_MAX) {
+ sd->sensor = force_sensor;
+ PDEBUG(D_PROBE, "sensor forced to %d", force_sensor);
+@@ -7112,6 +7126,10 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ sd->chip_revision);
+ sd->sensor = SENSOR_TAS5130CK;
+ break;
++ case 0x16:
++ PDEBUG(D_PROBE, "Find Sensor ADCM2700");
++ sd->sensor = SENSOR_ADCM2700;
++ break;
+ case 0x29:
+ PDEBUG(D_PROBE, "Find Sensor GC0305");
+ sd->sensor = SENSOR_GC0305;
+@@ -7129,12 +7147,16 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ PDEBUG(D_PROBE, "Find Sensor OV7620");
+ sd->sensor = SENSOR_OV7620;
+ break;
++ case 0x7631:
++ PDEBUG(D_PROBE, "Find Sensor OV7630C");
++ sd->sensor = SENSOR_OV7630C;
++ break;
+ case 0x7648:
+ PDEBUG(D_PROBE, "Find Sensor OV7648");
+ sd->sensor = SENSOR_OV7620; /* same sensor (?) */
+ break;
+ default:
+- PDEBUG(D_ERR|D_PROBE, "Unknown sensor %02x", sensor);
++ PDEBUG(D_ERR|D_PROBE, "Unknown sensor %04x", sensor);
+ return -EINVAL;
+ }
+ }
+@@ -7147,7 +7169,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ }
+
+ cam = &gspca_dev->cam;
+- cam->epaddr = 0x01;
+ /*fixme:test*/
+ gspca_dev->nbalt--;
+ if (vga) {
+@@ -7157,12 +7178,12 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ cam->cam_mode = sif_mode;
+ cam->nmodes = ARRAY_SIZE(sif_mode);
+ }
+- sd->qindex = 1;
+ sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
+ sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
+ sd->gamma = gamma[(int) sd->sensor];
+ sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value;
+ sd->lightfreq = sd_ctrls[SD_FREQ].qctrl.default_value;
++ sd->quality = QUALITY_DEF;
+
+ switch (sd->sensor) {
+ case SENSOR_GC0305:
+@@ -7196,27 +7217,34 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ const struct usb_action *zc3_init;
+ int mode;
+ static const struct usb_action *init_tb[SENSOR_MAX][2] = {
+- {cs2102_InitialScale, cs2102_Initial}, /* 0 */
+- {cs2102K_InitialScale, cs2102K_Initial}, /* 1 */
+- {gc0305_Initial, gc0305_InitialScale}, /* 2 */
+- {hdcs2020xb_InitialScale, hdcs2020xb_Initial}, /* 3 */
+- {hv7131bxx_InitialScale, hv7131bxx_Initial}, /* 4 */
+- {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 5 */
+- {icm105axx_InitialScale, icm105axx_Initial}, /* 6 */
+- {MC501CB_InitialScale, MC501CB_Initial}, /* 7 */
+- {OV7620_mode0, OV7620_mode1}, /* 8 */
+- {ov7630c_InitialScale, ov7630c_Initial}, /* 9 */
+- {pas106b_InitialScale, pas106b_Initial}, /* 10 */
+- {pas202b_Initial, pas202b_InitialScale}, /* 11 */
+- {pb0330xx_InitialScale, pb0330xx_Initial}, /* 12 */
++ {adcm2700_Initial, adcm2700_InitialScale}, /* 0 */
++ {cs2102_InitialScale, cs2102_Initial}, /* 1 */
++ {cs2102K_InitialScale, cs2102K_Initial}, /* 2 */
++ {gc0305_Initial, gc0305_InitialScale}, /* 3 */
++ {hdcs2020xb_InitialScale, hdcs2020xb_Initial}, /* 4 */
++ {hv7131bxx_InitialScale, hv7131bxx_Initial}, /* 5 */
++ {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 6 */
++ {icm105axx_InitialScale, icm105axx_Initial}, /* 7 */
++ {MC501CB_InitialScale, MC501CB_Initial}, /* 8 */
++ {OV7620_mode0, OV7620_mode1}, /* 9 */
++ {ov7630c_InitialScale, ov7630c_Initial}, /* 10 */
++ {pas106b_InitialScale, pas106b_Initial}, /* 11 */
++ {pas202b_Initial, pas202b_InitialScale}, /* 12 */
++ {pb0330xx_InitialScale, pb0330xx_Initial}, /* 13 */
+ /* or {pb03303x_InitialScale, pb03303x_Initial}, */
+- {PO2030_mode0, PO2030_mode1}, /* 13 */
+- {tas5130CK_InitialScale, tas5130CK_Initial}, /* 14 */
+- {tas5130cxx_InitialScale, tas5130cxx_Initial}, /* 15 */
++ {PO2030_mode0, PO2030_mode1}, /* 14 */
++ {tas5130CK_InitialScale, tas5130CK_Initial}, /* 15 */
++ {tas5130cxx_InitialScale, tas5130cxx_Initial}, /* 16 */
+ {tas5130c_vf0250_InitialScale, tas5130c_vf0250_Initial},
+- /* 16 */
++ /* 17 */
+ };
+
++ /* create the JPEG header */
++ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
++ jpeg_define(sd->jpeg_hdr, gspca_dev->height, gspca_dev->width,
++ 0x21); /* JPEG 422 */
++ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
++
+ mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+ zc3_init = init_tb[(int) sd->sensor][mode];
+ switch (sd->sensor) {
+@@ -7243,11 +7271,12 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ usb_exchange(gspca_dev, zc3_init);
+
+ switch (sd->sensor) {
++ case SENSOR_ADCM2700:
+ case SENSOR_GC0305:
+ case SENSOR_OV7620:
+ case SENSOR_PO2030:
+ case SENSOR_TAS5130C_VF0250:
+- msleep(100); /* ?? */
++/* msleep(100); * ?? */
+ reg_r(gspca_dev, 0x0002); /* --> 0x40 */
+ reg_w(dev, 0x09, 0x01ad); /* (from win traces) */
+ reg_w(dev, 0x15, 0x01ae);
+@@ -7260,6 +7289,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ setmatrix(gspca_dev);
+ setbrightness(gspca_dev);
+ switch (sd->sensor) {
++ case SENSOR_ADCM2700:
+ case SENSOR_OV7620:
+ reg_r(gspca_dev, 0x0008);
+ reg_w(dev, 0x00, 0x0008);
+@@ -7301,6 +7331,13 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ setlightfreq(gspca_dev);
+
+ switch (sd->sensor) {
++ case SENSOR_ADCM2700:
++ reg_w(dev, 0x09, 0x01ad); /* (from win traces) */
++ reg_w(dev, 0x15, 0x01ae);
++ reg_w(dev, 0x02, 0x0180);
++ /* ms-win + */
++ reg_w(dev, 0x40, 0x0117);
++ break;
+ case SENSOR_GC0305:
+ reg_w(dev, 0x09, 0x01ad); /* (from win traces) */
+ reg_w(dev, 0x15, 0x01ae);
+@@ -7323,19 +7360,16 @@ static int sd_start(struct gspca_dev *gspca_dev)
+
+ setautogain(gspca_dev);
+ switch (sd->sensor) {
+- case SENSOR_PAS202B:
+- reg_w(dev, 0x00, 0x0007); /* (from win traces) */
+- break;
+ case SENSOR_PO2030:
+ msleep(500);
+ reg_r(gspca_dev, 0x0008);
+ reg_r(gspca_dev, 0x0007);
++ /*fall thru*/
++ case SENSOR_PAS202B:
+ reg_w(dev, 0x00, 0x0007); /* (from win traces) */
+- reg_w(dev, 0x02, 0x0008);
++ reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING);
+ break;
+ }
+- if (sd->sensor == SENSOR_PAS202B)
+- reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING);
+ return 0;
+ }
+
+@@ -7344,6 +7378,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
++ kfree(sd->jpeg_hdr);
+ if (!gspca_dev->present)
+ return;
+ send_unknown(gspca_dev->dev, sd->sensor);
+@@ -7354,14 +7389,15 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ __u8 *data,
+ int len)
+ {
++ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (data[0] == 0xff && data[1] == 0xd8) { /* start of frame */
+ frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+ data, 0);
+ /* put the JPEG header in the new frame */
+- jpeg_put_header(gspca_dev, frame,
+- ((struct sd *) gspca_dev)->qindex,
+- 0x21);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ sd->jpeg_hdr, JPEG_HDR_SZ);
++
+ /* remove the webcam's header:
+ * ff d8 ff fe 00 0e 00 00 ss ss 00 01 ww ww hh hh pp pp
+ * - 'ss ss' is the frame sequence number (BE)
+@@ -7503,6 +7539,34 @@ static int sd_querymenu(struct gspca_dev *gspca_dev,
+ return -EINVAL;
+ }
+
++static int sd_set_jcomp(struct gspca_dev *gspca_dev,
++ struct v4l2_jpegcompression *jcomp)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (jcomp->quality < QUALITY_MIN)
++ sd->quality = QUALITY_MIN;
++ else if (jcomp->quality > QUALITY_MAX)
++ sd->quality = QUALITY_MAX;
++ else
++ sd->quality = jcomp->quality;
++ if (gspca_dev->streaming)
++ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
++ return 0;
++}
++
++static int sd_get_jcomp(struct gspca_dev *gspca_dev,
++ struct v4l2_jpegcompression *jcomp)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ memset(jcomp, 0, sizeof *jcomp);
++ jcomp->quality = sd->quality;
++ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT
++ | V4L2_JPEG_MARKER_DQT;
++ return 0;
++}
++
+ static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+@@ -7513,6 +7577,8 @@ static const struct sd_desc sd_desc = {
+ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .querymenu = sd_querymenu,
++ .get_jcomp = sd_get_jcomp,
++ .set_jcomp = sd_set_jcomp,
+ };
+
+ static const __devinitdata struct usb_device_id device_table[] = {
+@@ -7563,11 +7629,9 @@ static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x055f, 0xd004)},
+ {USB_DEVICE(0x0698, 0x2003)},
+ {USB_DEVICE(0x0ac8, 0x0301), .driver_info = SENSOR_PAS106},
+- {USB_DEVICE(0x0ac8, 0x0302)},
++ {USB_DEVICE(0x0ac8, 0x0302), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x0ac8, 0x301b)},
+-#if !defined CONFIG_USB_ZC0301 && !defined CONFIG_USB_ZC0301_MODULE
+ {USB_DEVICE(0x0ac8, 0x303b)},
+-#endif
+ {USB_DEVICE(0x0ac8, 0x305b), .driver_info = SENSOR_TAS5130C_VF0250},
+ {USB_DEVICE(0x0ac8, 0x307b)},
+ {USB_DEVICE(0x10fd, 0x0128)},
+@@ -7600,8 +7664,10 @@ static struct usb_driver sd_driver = {
+
+ static int __init sd_mod_init(void)
+ {
+- if (usb_register(&sd_driver) < 0)
+- return -1;
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
+ PDEBUG(D_PROBE, "registered");
+ return 0;
+ }
+diff --git a/drivers/media/video/hdpvr/Kconfig b/drivers/media/video/hdpvr/Kconfig
+new file mode 100644
+index 0000000..de247f3
+--- /dev/null
++++ b/drivers/media/video/hdpvr/Kconfig
+@@ -0,0 +1,10 @@
++
++config VIDEO_HDPVR
++ tristate "Hauppauge HD PVR support"
++ depends on VIDEO_DEV
++ ---help---
++ This is a video4linux driver for Hauppauge's HD PVR USB device.
++
++ To compile this driver as a module, choose M here: the
++ module will be called hdpvr
++
+diff --git a/drivers/media/video/hdpvr/Makefile b/drivers/media/video/hdpvr/Makefile
+new file mode 100644
+index 0000000..e0230fc
+--- /dev/null
++++ b/drivers/media/video/hdpvr/Makefile
+@@ -0,0 +1,9 @@
++hdpvr-objs := hdpvr-control.o hdpvr-core.o hdpvr-video.o
++
++hdpvr-$(CONFIG_I2C) += hdpvr-i2c.o
++
++obj-$(CONFIG_VIDEO_HDPVR) += hdpvr.o
++
++EXTRA_CFLAGS += -Idrivers/media/video
++
++EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m)
+diff --git a/drivers/media/video/hdpvr/hdpvr-control.c b/drivers/media/video/hdpvr/hdpvr-control.c
+new file mode 100644
+index 0000000..0679174
+--- /dev/null
++++ b/drivers/media/video/hdpvr/hdpvr-control.c
+@@ -0,0 +1,201 @@
++/*
++ * Hauppauge HD PVR USB driver - video 4 linux 2 interface
++ *
++ * Copyright (C) 2008 Janne Grunau (j@jannau.net)
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation, version 2.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/usb.h>
++#include <linux/mutex.h>
++
++#include <linux/videodev2.h>
++
++#include <media/v4l2-common.h>
++
++#include "hdpvr.h"
++
++
++int hdpvr_config_call(struct hdpvr_device *dev, uint value, u8 valbuf)
++{
++ int ret;
++ char request_type = 0x38, snd_request = 0x01;
++
++ msleep(10);
++
++ mutex_lock(&dev->usbc_mutex);
++ dev->usbc_buf[0] = valbuf;
++ ret = usb_control_msg(dev->udev,
++ usb_sndctrlpipe(dev->udev, 0),
++ snd_request, 0x00 | request_type,
++ value, CTRL_DEFAULT_INDEX,
++ dev->usbc_buf, 1, 10000);
++
++ mutex_unlock(&dev->usbc_mutex);
++ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
++ "config call request for value 0x%x returned %d\n", value,
++ ret);
++
++ return ret < 0 ? ret : 0;
++}
++
++struct hdpvr_video_info *get_video_info(struct hdpvr_device *dev)
++{
++ struct hdpvr_video_info *vidinf = NULL;
++#ifdef HDPVR_DEBUG
++ char print_buf[15];
++#endif
++ int ret;
++
++ vidinf = kzalloc(sizeof(struct hdpvr_video_info), GFP_KERNEL);
++ if (!vidinf) {
++ v4l2_err(&dev->v4l2_dev, "out of memory\n");
++ goto err;
++ }
++
++ mutex_lock(&dev->usbc_mutex);
++ ret = usb_control_msg(dev->udev,
++ usb_rcvctrlpipe(dev->udev, 0),
++ 0x81, 0x80 | 0x38,
++ 0x1400, 0x0003,
++ dev->usbc_buf, 5,
++ 1000);
++ if (ret == 5) {
++ vidinf->width = dev->usbc_buf[1] << 8 | dev->usbc_buf[0];
++ vidinf->height = dev->usbc_buf[3] << 8 | dev->usbc_buf[2];
++ vidinf->fps = dev->usbc_buf[4];
++ }
++
++#ifdef HDPVR_DEBUG
++ if (hdpvr_debug & MSG_INFO) {
++ hex_dump_to_buffer(dev->usbc_buf, 5, 16, 1, print_buf,
++ sizeof(print_buf), 0);
++ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
++ "get video info returned: %d, %s\n", ret, print_buf);
++ }
++#endif
++ mutex_unlock(&dev->usbc_mutex);
++
++ if (!vidinf->width || !vidinf->height || !vidinf->fps) {
++ kfree(vidinf);
++ vidinf = NULL;
++ }
++err:
++ return vidinf;
++}
++
++int get_input_lines_info(struct hdpvr_device *dev)
++{
++#ifdef HDPVR_DEBUG
++ char print_buf[9];
++#endif
++ int ret, lines;
++
++ mutex_lock(&dev->usbc_mutex);
++ ret = usb_control_msg(dev->udev,
++ usb_rcvctrlpipe(dev->udev, 0),
++ 0x81, 0x80 | 0x38,
++ 0x1800, 0x0003,
++ dev->usbc_buf, 3,
++ 1000);
++
++#ifdef HDPVR_DEBUG
++ if (hdpvr_debug & MSG_INFO) {
++ hex_dump_to_buffer(dev->usbc_buf, 3, 16, 1, print_buf,
++ sizeof(print_buf), 0);
++ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
++ "get input lines info returned: %d, %s\n", ret,
++ print_buf);
++ }
++#endif
++ lines = dev->usbc_buf[1] << 8 | dev->usbc_buf[0];
++ mutex_unlock(&dev->usbc_mutex);
++ return lines;
++}
++
++
++int hdpvr_set_bitrate(struct hdpvr_device *dev)
++{
++ int ret;
++
++ mutex_lock(&dev->usbc_mutex);
++ memset(dev->usbc_buf, 0, 4);
++ dev->usbc_buf[0] = dev->options.bitrate;
++ dev->usbc_buf[2] = dev->options.peak_bitrate;
++
++ ret = usb_control_msg(dev->udev,
++ usb_sndctrlpipe(dev->udev, 0),
++ 0x01, 0x38, CTRL_BITRATE_VALUE,
++ CTRL_DEFAULT_INDEX, dev->usbc_buf, 4, 1000);
++ mutex_unlock(&dev->usbc_mutex);
++
++ return ret;
++}
++
++int hdpvr_set_audio(struct hdpvr_device *dev, u8 input,
++ enum v4l2_mpeg_audio_encoding codec)
++{
++ int ret = 0;
++
++ if (dev->flags & HDPVR_FLAG_AC3_CAP) {
++ mutex_lock(&dev->usbc_mutex);
++ memset(dev->usbc_buf, 0, 2);
++ dev->usbc_buf[0] = input;
++ if (codec == V4L2_MPEG_AUDIO_ENCODING_AAC)
++ dev->usbc_buf[1] = 0;
++ else if (codec == V4L2_MPEG_AUDIO_ENCODING_AC3)
++ dev->usbc_buf[1] = 1;
++ else {
++ mutex_unlock(&dev->usbc_mutex);
++ v4l2_err(&dev->v4l2_dev, "invalid audio codec %d\n",
++ codec);
++ ret = -EINVAL;
++ goto error;
++ }
++
++ ret = usb_control_msg(dev->udev,
++ usb_sndctrlpipe(dev->udev, 0),
++ 0x01, 0x38, CTRL_AUDIO_INPUT_VALUE,
++ CTRL_DEFAULT_INDEX, dev->usbc_buf, 2,
++ 1000);
++ mutex_unlock(&dev->usbc_mutex);
++ if (ret == 2)
++ ret = 0;
++ } else
++ ret = hdpvr_config_call(dev, CTRL_AUDIO_INPUT_VALUE,
++ dev->options.audio_input+1);
++error:
++ return ret;
++}
++
++int hdpvr_set_options(struct hdpvr_device *dev)
++{
++ hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, dev->options.video_std);
++
++ hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE,
++ dev->options.video_input+1);
++
++ hdpvr_set_audio(dev, dev->options.audio_input+1,
++ dev->options.audio_codec);
++
++ hdpvr_set_bitrate(dev);
++ hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
++ dev->options.bitrate_mode);
++ hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, dev->options.gop_mode);
++
++ hdpvr_config_call(dev, CTRL_BRIGHTNESS, dev->options.brightness);
++ hdpvr_config_call(dev, CTRL_CONTRAST, dev->options.contrast);
++ hdpvr_config_call(dev, CTRL_HUE, dev->options.hue);
++ hdpvr_config_call(dev, CTRL_SATURATION, dev->options.saturation);
++ hdpvr_config_call(dev, CTRL_SHARPNESS, dev->options.sharpness);
++
++ return 0;
++}
+diff --git a/drivers/media/video/hdpvr/hdpvr-core.c b/drivers/media/video/hdpvr/hdpvr-core.c
+new file mode 100644
+index 0000000..188bd5a
+--- /dev/null
++++ b/drivers/media/video/hdpvr/hdpvr-core.c
+@@ -0,0 +1,466 @@
++/*
++ * Hauppauge HD PVR USB driver
++ *
++ * Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
++ * Copyright (C) 2008 Janne Grunau (j@jannau.net)
++ * Copyright (C) 2008 John Poet
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation, version 2.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/uaccess.h>
++#include <asm/atomic.h>
++#include <linux/usb.h>
++#include <linux/mutex.h>
++#include <linux/i2c.h>
++
++#include <linux/videodev2.h>
++#include <media/v4l2-dev.h>
++#include <media/v4l2-common.h>
++
++#include "hdpvr.h"
++
++static int video_nr[HDPVR_MAX] = {[0 ... (HDPVR_MAX - 1)] = UNSET};
++module_param_array(video_nr, int, NULL, 0);
++MODULE_PARM_DESC(video_nr, "video device number (-1=Auto)");
++
++/* holds the number of currently registered devices */
++static atomic_t dev_nr = ATOMIC_INIT(-1);
++
++int hdpvr_debug;
++module_param(hdpvr_debug, int, S_IRUGO|S_IWUSR);
++MODULE_PARM_DESC(hdpvr_debug, "enable debugging output");
++
++uint default_video_input = HDPVR_VIDEO_INPUTS;
++module_param(default_video_input, uint, S_IRUGO|S_IWUSR);
++MODULE_PARM_DESC(default_video_input, "default video input: 0=Component / "
++ "1=S-Video / 2=Composite");
++
++uint default_audio_input = HDPVR_AUDIO_INPUTS;
++module_param(default_audio_input, uint, S_IRUGO|S_IWUSR);
++MODULE_PARM_DESC(default_audio_input, "default audio input: 0=RCA back / "
++ "1=RCA front / 2=S/PDIF");
++
++static int boost_audio;
++module_param(boost_audio, bool, S_IRUGO|S_IWUSR);
++MODULE_PARM_DESC(boost_audio, "boost the audio signal");
++
++
++/* table of devices that work with this driver */
++static struct usb_device_id hdpvr_table[] = {
++ { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID) },
++ { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID1) },
++ { USB_DEVICE(HD_PVR_VENDOR_ID, HD_PVR_PRODUCT_ID2) },
++ { } /* Terminating entry */
++};
++MODULE_DEVICE_TABLE(usb, hdpvr_table);
++
++
++void hdpvr_delete(struct hdpvr_device *dev)
++{
++ hdpvr_free_buffers(dev);
++
++ if (dev->video_dev)
++ video_device_release(dev->video_dev);
++
++ usb_put_dev(dev->udev);
++}
++
++static void challenge(u8 *bytes)
++{
++ u64 *i64P, tmp64;
++ uint i, idx;
++
++ for (idx = 0; idx < 32; ++idx) {
++
++ if (idx & 0x3)
++ bytes[(idx >> 3) + 3] = bytes[(idx >> 2) & 0x3];
++
++ switch (idx & 0x3) {
++ case 0x3:
++ bytes[2] += bytes[3] * 4 + bytes[4] + bytes[5];
++ bytes[4] += bytes[(idx & 0x1) * 2] * 9 + 9;
++ break;
++ case 0x1:
++ bytes[0] *= 8;
++ bytes[0] += 7*idx + 4;
++ bytes[6] += bytes[3] * 3;
++ break;
++ case 0x0:
++ bytes[3 - (idx >> 3)] = bytes[idx >> 2];
++ bytes[5] += bytes[6] * 3;
++ for (i = 0; i < 3; i++)
++ bytes[3] *= bytes[3] + 1;
++ break;
++ case 0x2:
++ for (i = 0; i < 3; i++)
++ bytes[1] *= bytes[6] + 1;
++ for (i = 0; i < 3; i++) {
++ i64P = (u64 *)bytes;
++ tmp64 = le64_to_cpup(i64P);
++ tmp64 <<= bytes[7] & 0x0f;
++ *i64P += cpu_to_le64(tmp64);
++ }
++ break;
++ }
++ }
++}
++
++/* try to init the device like the windows driver */
++static int device_authorization(struct hdpvr_device *dev)
++{
++
++ int ret, retval = -ENOMEM;
++ char request_type = 0x38, rcv_request = 0x81;
++ char *response;
++#ifdef HDPVR_DEBUG
++ size_t buf_size = 46;
++ char *print_buf = kzalloc(5*buf_size+1, GFP_KERNEL);
++ if (!print_buf) {
++ v4l2_err(&dev->v4l2_dev, "Out of memory\n");
++ goto error;
++ }
++#endif
++
++ mutex_lock(&dev->usbc_mutex);
++ ret = usb_control_msg(dev->udev,
++ usb_rcvctrlpipe(dev->udev, 0),
++ rcv_request, 0x80 | request_type,
++ 0x0400, 0x0003,
++ dev->usbc_buf, 46,
++ 10000);
++ if (ret != 46) {
++ v4l2_err(&dev->v4l2_dev,
++ "unexpected answer of status request, len %d\n", ret);
++ goto error;
++ }
++#ifdef HDPVR_DEBUG
++ else {
++ hex_dump_to_buffer(dev->usbc_buf, 46, 16, 1, print_buf,
++ sizeof(print_buf), 0);
++ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
++ "Status request returned, len %d: %s\n",
++ ret, print_buf);
++ }
++#endif
++ if (dev->usbc_buf[1] == HDPVR_FIRMWARE_VERSION) {
++ dev->flags &= ~HDPVR_FLAG_AC3_CAP;
++ } else if (dev->usbc_buf[1] == HDPVR_FIRMWARE_VERSION_AC3) {
++ dev->flags |= HDPVR_FLAG_AC3_CAP;
++ } else if (dev->usbc_buf[1] > HDPVR_FIRMWARE_VERSION_AC3) {
++ v4l2_info(&dev->v4l2_dev, "untested firmware version 0x%x, "
++ "the driver might not work\n", dev->usbc_buf[1]);
++ dev->flags |= HDPVR_FLAG_AC3_CAP;
++ } else {
++ v4l2_err(&dev->v4l2_dev, "unknown firmware version 0x%x\n",
++ dev->usbc_buf[1]);
++ ret = -EINVAL;
++ goto error;
++ }
++
++ response = dev->usbc_buf+38;
++#ifdef HDPVR_DEBUG
++ hex_dump_to_buffer(response, 8, 16, 1, print_buf, sizeof(print_buf), 0);
++ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, "challenge: %s\n",
++ print_buf);
++#endif
++ challenge(response);
++#ifdef HDPVR_DEBUG
++ hex_dump_to_buffer(response, 8, 16, 1, print_buf, sizeof(print_buf), 0);
++ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev, " response: %s\n",
++ print_buf);
++#endif
++
++ msleep(100);
++ ret = usb_control_msg(dev->udev,
++ usb_sndctrlpipe(dev->udev, 0),
++ 0xd1, 0x00 | request_type,
++ 0x0000, 0x0000,
++ response, 8,
++ 10000);
++ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
++ "magic request returned %d\n", ret);
++ mutex_unlock(&dev->usbc_mutex);
++
++ retval = ret != 8;
++error:
++ return retval;
++}
++
++static int hdpvr_device_init(struct hdpvr_device *dev)
++{
++ int ret;
++ u8 *buf;
++ struct hdpvr_video_info *vidinf;
++
++ if (device_authorization(dev))
++ return -EACCES;
++
++ /* default options for init */
++ hdpvr_set_options(dev);
++
++ /* set filter options */
++ mutex_lock(&dev->usbc_mutex);
++ buf = dev->usbc_buf;
++ buf[0] = 0x03; buf[1] = 0x03; buf[2] = 0x00; buf[3] = 0x00;
++ ret = usb_control_msg(dev->udev,
++ usb_sndctrlpipe(dev->udev, 0),
++ 0x01, 0x38,
++ CTRL_LOW_PASS_FILTER_VALUE, CTRL_DEFAULT_INDEX,
++ buf, 4,
++ 1000);
++ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
++ "control request returned %d\n", ret);
++ mutex_unlock(&dev->usbc_mutex);
++
++ vidinf = get_video_info(dev);
++ if (!vidinf)
++ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
++ "no valid video signal or device init failed\n");
++ else
++ kfree(vidinf);
++
++ /* enable fan and bling leds */
++ mutex_lock(&dev->usbc_mutex);
++ buf[0] = 0x1;
++ ret = usb_control_msg(dev->udev,
++ usb_sndctrlpipe(dev->udev, 0),
++ 0xd4, 0x38, 0, 0, buf, 1,
++ 1000);
++ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
++ "control request returned %d\n", ret);
++
++ /* boost analog audio */
++ buf[0] = boost_audio;
++ ret = usb_control_msg(dev->udev,
++ usb_sndctrlpipe(dev->udev, 0),
++ 0xd5, 0x38, 0, 0, buf, 1,
++ 1000);
++ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
++ "control request returned %d\n", ret);
++ mutex_unlock(&dev->usbc_mutex);
++
++ dev->status = STATUS_IDLE;
++ return 0;
++}
++
++static const struct hdpvr_options hdpvr_default_options = {
++ .video_std = HDPVR_60HZ,
++ .video_input = HDPVR_COMPONENT,
++ .audio_input = HDPVR_RCA_BACK,
++ .bitrate = 65, /* 6 mbps */
++ .peak_bitrate = 90, /* 9 mbps */
++ .bitrate_mode = HDPVR_CONSTANT,
++ .gop_mode = HDPVR_SIMPLE_IDR_GOP,
++ .audio_codec = V4L2_MPEG_AUDIO_ENCODING_AAC,
++ .brightness = 0x86,
++ .contrast = 0x80,
++ .hue = 0x80,
++ .saturation = 0x80,
++ .sharpness = 0x80,
++};
++
++static int hdpvr_probe(struct usb_interface *interface,
++ const struct usb_device_id *id)
++{
++ struct hdpvr_device *dev;
++ struct usb_host_interface *iface_desc;
++ struct usb_endpoint_descriptor *endpoint;
++ size_t buffer_size;
++ int i;
++ int retval = -ENOMEM;
++
++ /* allocate memory for our device state and initialize it */
++ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
++ if (!dev) {
++ err("Out of memory");
++ goto error;
++ }
++
++ /* register v4l2_device early so it can be used for printks */
++ if (v4l2_device_register(&interface->dev, &dev->v4l2_dev)) {
++ err("v4l2_device_register failed");
++ goto error;
++ }
++
++ mutex_init(&dev->io_mutex);
++ mutex_init(&dev->i2c_mutex);
++ mutex_init(&dev->usbc_mutex);
++ dev->usbc_buf = kmalloc(64, GFP_KERNEL);
++ if (!dev->usbc_buf) {
++ v4l2_err(&dev->v4l2_dev, "Out of memory\n");
++ goto error;
++ }
++
++ init_waitqueue_head(&dev->wait_buffer);
++ init_waitqueue_head(&dev->wait_data);
++
++ dev->workqueue = create_singlethread_workqueue("hdpvr_buffer");
++ if (!dev->workqueue)
++ goto error;
++
++ /* init video transfer queues */
++ INIT_LIST_HEAD(&dev->free_buff_list);
++ INIT_LIST_HEAD(&dev->rec_buff_list);
++
++ dev->options = hdpvr_default_options;
++
++ if (default_video_input < HDPVR_VIDEO_INPUTS)
++ dev->options.video_input = default_video_input;
++
++ if (default_audio_input < HDPVR_AUDIO_INPUTS)
++ dev->options.audio_input = default_audio_input;
++
++ dev->udev = usb_get_dev(interface_to_usbdev(interface));
++
++ /* set up the endpoint information */
++ /* use only the first bulk-in and bulk-out endpoints */
++ iface_desc = interface->cur_altsetting;
++ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
++ endpoint = &iface_desc->endpoint[i].desc;
++
++ if (!dev->bulk_in_endpointAddr &&
++ usb_endpoint_is_bulk_in(endpoint)) {
++ /* USB interface description is buggy, reported max
++ * packet size is 512 bytes, windows driver uses 8192 */
++ buffer_size = 8192;
++ dev->bulk_in_size = buffer_size;
++ dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
++ }
++
++ }
++ if (!dev->bulk_in_endpointAddr) {
++ v4l2_err(&dev->v4l2_dev, "Could not find bulk-in endpoint\n");
++ goto error;
++ }
++
++ /* init the device */
++ if (hdpvr_device_init(dev)) {
++ v4l2_err(&dev->v4l2_dev, "device init failed\n");
++ goto error;
++ }
++
++ mutex_lock(&dev->io_mutex);
++ if (hdpvr_alloc_buffers(dev, NUM_BUFFERS)) {
++ v4l2_err(&dev->v4l2_dev,
++ "allocating transfer buffers failed\n");
++ goto error;
++ }
++ mutex_unlock(&dev->io_mutex);
++
++ if (hdpvr_register_videodev(dev, &interface->dev,
++ video_nr[atomic_inc_return(&dev_nr)])) {
++ v4l2_err(&dev->v4l2_dev, "registering videodev failed\n");
++ goto error;
++ }
++
++#ifdef CONFIG_I2C
++ /* until i2c is working properly */
++ retval = 0; /* hdpvr_register_i2c_adapter(dev); */
++ if (retval < 0) {
++ v4l2_err(&dev->v4l2_dev, "registering i2c adapter failed\n");
++ goto error;
++ }
++#endif /* CONFIG_I2C */
++
++ /* save our data pointer in this interface device */
++ usb_set_intfdata(interface, dev);
++
++ /* let the user know what node this device is now attached to */
++ v4l2_info(&dev->v4l2_dev, "device now attached to /dev/video%d\n",
++ dev->video_dev->minor);
++ return 0;
++
++error:
++ if (dev) {
++ mutex_unlock(&dev->io_mutex);
++ /* this frees allocated memory */
++ hdpvr_delete(dev);
++ }
++ return retval;
++}
++
++static void hdpvr_disconnect(struct usb_interface *interface)
++{
++ struct hdpvr_device *dev;
++ int minor;
++
++ dev = usb_get_intfdata(interface);
++ usb_set_intfdata(interface, NULL);
++
++ minor = dev->video_dev->minor;
++
++ /* prevent more I/O from starting and stop any ongoing */
++ mutex_lock(&dev->io_mutex);
++ dev->status = STATUS_DISCONNECTED;
++ v4l2_device_disconnect(&dev->v4l2_dev);
++ video_unregister_device(dev->video_dev);
++ wake_up_interruptible(&dev->wait_data);
++ wake_up_interruptible(&dev->wait_buffer);
++ mutex_unlock(&dev->io_mutex);
++ msleep(100);
++ flush_workqueue(dev->workqueue);
++ mutex_lock(&dev->io_mutex);
++ hdpvr_cancel_queue(dev);
++ destroy_workqueue(dev->workqueue);
++ mutex_unlock(&dev->io_mutex);
++
++ /* deregister I2C adapter */
++#ifdef CONFIG_I2C
++ mutex_lock(&dev->i2c_mutex);
++ if (dev->i2c_adapter)
++ i2c_del_adapter(dev->i2c_adapter);
++ kfree(dev->i2c_adapter);
++ dev->i2c_adapter = NULL;
++ mutex_unlock(&dev->i2c_mutex);
++#endif /* CONFIG_I2C */
++
++ atomic_dec(&dev_nr);
++
++ v4l2_info(&dev->v4l2_dev, "device /dev/video%d disconnected\n", minor);
++
++ v4l2_device_unregister(&dev->v4l2_dev);
++ kfree(dev->usbc_buf);
++ kfree(dev);
++}
++
++
++static struct usb_driver hdpvr_usb_driver = {
++ .name = "hdpvr",
++ .probe = hdpvr_probe,
++ .disconnect = hdpvr_disconnect,
++ .id_table = hdpvr_table,
++};
++
++static int __init hdpvr_init(void)
++{
++ int result;
++
++ /* register this driver with the USB subsystem */
++ result = usb_register(&hdpvr_usb_driver);
++ if (result)
++ err("usb_register failed. Error number %d", result);
++
++ return result;
++}
++
++static void __exit hdpvr_exit(void)
++{
++ /* deregister this driver with the USB subsystem */
++ usb_deregister(&hdpvr_usb_driver);
++}
++
++module_init(hdpvr_init);
++module_exit(hdpvr_exit);
++
++MODULE_LICENSE("GPL");
++MODULE_AUTHOR("Janne Grunau");
++MODULE_DESCRIPTION("Hauppauge HD PVR driver");
+diff --git a/drivers/media/video/hdpvr/hdpvr-i2c.c b/drivers/media/video/hdpvr/hdpvr-i2c.c
+new file mode 100644
+index 0000000..c4b5d15
+--- /dev/null
++++ b/drivers/media/video/hdpvr/hdpvr-i2c.c
+@@ -0,0 +1,145 @@
++
++/*
++ * Hauppauge HD PVR USB driver
++ *
++ * Copyright (C) 2008 Janne Grunau (j@jannau.net)
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation, version 2.
++ *
++ */
++
++#include <linux/i2c.h>
++
++#include "hdpvr.h"
++
++#define CTRL_READ_REQUEST 0xb8
++#define CTRL_WRITE_REQUEST 0x38
++
++#define REQTYPE_I2C_READ 0xb1
++#define REQTYPE_I2C_WRITE 0xb0
++#define REQTYPE_I2C_WRITE_STATT 0xd0
++
++static int hdpvr_i2c_read(struct hdpvr_device *dev, unsigned char addr,
++ char *data, int len)
++{
++ int ret;
++ char *buf = kmalloc(len, GFP_KERNEL);
++ if (!buf)
++ return -ENOMEM;
++
++ ret = usb_control_msg(dev->udev,
++ usb_rcvctrlpipe(dev->udev, 0),
++ REQTYPE_I2C_READ, CTRL_READ_REQUEST,
++ 0x100|addr, 0, buf, len, 1000);
++
++ if (ret == len) {
++ memcpy(data, buf, len);
++ ret = 0;
++ } else if (ret >= 0)
++ ret = -EIO;
++
++ kfree(buf);
++
++ return ret;
++}
++
++static int hdpvr_i2c_write(struct hdpvr_device *dev, unsigned char addr,
++ char *data, int len)
++{
++ int ret;
++ char *buf = kmalloc(len, GFP_KERNEL);
++ if (!buf)
++ return -ENOMEM;
++
++ memcpy(buf, data, len);
++ ret = usb_control_msg(dev->udev,
++ usb_sndctrlpipe(dev->udev, 0),
++ REQTYPE_I2C_WRITE, CTRL_WRITE_REQUEST,
++ 0x100|addr, 0, buf, len, 1000);
++
++ if (ret < 0)
++ goto error;
++
++ ret = usb_control_msg(dev->udev,
++ usb_rcvctrlpipe(dev->udev, 0),
++ REQTYPE_I2C_WRITE_STATT, CTRL_READ_REQUEST,
++ 0, 0, buf, 2, 1000);
++
++ if (ret == 2)
++ ret = 0;
++ else if (ret >= 0)
++ ret = -EIO;
++
++error:
++ kfree(buf);
++ return ret;
++}
++
++static int hdpvr_transfer(struct i2c_adapter *i2c_adapter, struct i2c_msg *msgs,
++ int num)
++{
++ struct hdpvr_device *dev = i2c_get_adapdata(i2c_adapter);
++ int retval = 0, i, addr;
++
++ if (num <= 0)
++ return 0;
++
++ mutex_lock(&dev->i2c_mutex);
++
++ for (i = 0; i < num && !retval; i++) {
++ addr = msgs[i].addr << 1;
++
++ if (msgs[i].flags & I2C_M_RD)
++ retval = hdpvr_i2c_read(dev, addr, msgs[i].buf,
++ msgs[i].len);
++ else
++ retval = hdpvr_i2c_write(dev, addr, msgs[i].buf,
++ msgs[i].len);
++ }
++
++ mutex_unlock(&dev->i2c_mutex);
++
++ return retval ? retval : num;
++}
++
++static u32 hdpvr_functionality(struct i2c_adapter *adapter)
++{
++ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
++}
++
++static struct i2c_algorithm hdpvr_algo = {
++ .master_xfer = hdpvr_transfer,
++ .functionality = hdpvr_functionality,
++};
++
++int hdpvr_register_i2c_adapter(struct hdpvr_device *dev)
++{
++ struct i2c_adapter *i2c_adap;
++ int retval = -ENOMEM;
++
++ i2c_adap = kzalloc(sizeof(struct i2c_adapter), GFP_KERNEL);
++ if (i2c_adap == NULL)
++ goto error;
++
++ strlcpy(i2c_adap->name, "Hauppauge HD PVR I2C",
++ sizeof(i2c_adap->name));
++ i2c_adap->algo = &hdpvr_algo;
++ i2c_adap->class = I2C_CLASS_TV_ANALOG;
++ i2c_adap->id = I2C_HW_B_HDPVR;
++ i2c_adap->owner = THIS_MODULE;
++ i2c_adap->dev.parent = &dev->udev->dev;
++
++ i2c_set_adapdata(i2c_adap, dev);
++
++ retval = i2c_add_adapter(i2c_adap);
++
++ if (!retval)
++ dev->i2c_adapter = i2c_adap;
++ else
++ kfree(i2c_adap);
++
++error:
++ return retval;
++}
+diff --git a/drivers/media/video/hdpvr/hdpvr-video.c b/drivers/media/video/hdpvr/hdpvr-video.c
+new file mode 100644
+index 0000000..3e6ffee
+--- /dev/null
++++ b/drivers/media/video/hdpvr/hdpvr-video.c
+@@ -0,0 +1,1248 @@
++/*
++ * Hauppauge HD PVR USB driver - video 4 linux 2 interface
++ *
++ * Copyright (C) 2008 Janne Grunau (j@jannau.net)
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation, version 2.
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/uaccess.h>
++#include <linux/usb.h>
++#include <linux/mutex.h>
++#include <linux/version.h>
++#include <linux/workqueue.h>
++
++#include <linux/videodev2.h>
++#include <media/v4l2-dev.h>
++#include <media/v4l2-common.h>
++#include <media/v4l2-ioctl.h>
++#include "hdpvr.h"
++
++#define BULK_URB_TIMEOUT 1250 /* 1.25 seconds */
++
++#define print_buffer_status() { \
++ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev, \
++ "%s:%d buffer stat: %d free, %d proc\n", \
++ __func__, __LINE__, \
++ list_size(&dev->free_buff_list), \
++ list_size(&dev->rec_buff_list)); }
++
++struct hdpvr_fh {
++ struct hdpvr_device *dev;
++};
++
++static uint list_size(struct list_head *list)
++{
++ struct list_head *tmp;
++ uint count = 0;
++
++ list_for_each(tmp, list) {
++ count++;
++ }
++
++ return count;
++}
++
++/*=========================================================================*/
++/* urb callback */
++static void hdpvr_read_bulk_callback(struct urb *urb)
++{
++ struct hdpvr_buffer *buf = (struct hdpvr_buffer *)urb->context;
++ struct hdpvr_device *dev = buf->dev;
++
++ /* marking buffer as received and wake waiting */
++ buf->status = BUFSTAT_READY;
++ wake_up_interruptible(&dev->wait_data);
++}
++
++/*=========================================================================*/
++/* bufffer bits */
++
++/* function expects dev->io_mutex to be hold by caller */
++int hdpvr_cancel_queue(struct hdpvr_device *dev)
++{
++ struct hdpvr_buffer *buf;
++
++ list_for_each_entry(buf, &dev->rec_buff_list, buff_list) {
++ usb_kill_urb(buf->urb);
++ buf->status = BUFSTAT_AVAILABLE;
++ }
++
++ list_splice_init(&dev->rec_buff_list, dev->free_buff_list.prev);
++
++ return 0;
++}
++
++static int hdpvr_free_queue(struct list_head *q)
++{
++ struct list_head *tmp;
++ struct list_head *p;
++ struct hdpvr_buffer *buf;
++ struct urb *urb;
++
++ for (p = q->next; p != q;) {
++ buf = list_entry(p, struct hdpvr_buffer, buff_list);
++
++ urb = buf->urb;
++ usb_buffer_free(urb->dev, urb->transfer_buffer_length,
++ urb->transfer_buffer, urb->transfer_dma);
++ usb_free_urb(urb);
++ tmp = p->next;
++ list_del(p);
++ kfree(buf);
++ p = tmp;
++ }
++
++ return 0;
++}
++
++/* function expects dev->io_mutex to be hold by caller */
++int hdpvr_free_buffers(struct hdpvr_device *dev)
++{
++ hdpvr_cancel_queue(dev);
++
++ hdpvr_free_queue(&dev->free_buff_list);
++ hdpvr_free_queue(&dev->rec_buff_list);
++
++ return 0;
++}
++
++/* function expects dev->io_mutex to be hold by caller */
++int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count)
++{
++ uint i;
++ int retval = -ENOMEM;
++ u8 *mem;
++ struct hdpvr_buffer *buf;
++ struct urb *urb;
++
++ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
++ "allocating %u buffers\n", count);
++
++ for (i = 0; i < count; i++) {
++
++ buf = kzalloc(sizeof(struct hdpvr_buffer), GFP_KERNEL);
++ if (!buf) {
++ v4l2_err(&dev->v4l2_dev, "cannot allocate buffer\n");
++ goto exit;
++ }
++ buf->dev = dev;
++
++ urb = usb_alloc_urb(0, GFP_KERNEL);
++ if (!urb) {
++ v4l2_err(&dev->v4l2_dev, "cannot allocate urb\n");
++ goto exit;
++ }
++ buf->urb = urb;
++
++ mem = usb_buffer_alloc(dev->udev, dev->bulk_in_size, GFP_KERNEL,
++ &urb->transfer_dma);
++ if (!mem) {
++ v4l2_err(&dev->v4l2_dev,
++ "cannot allocate usb transfer buffer\n");
++ goto exit;
++ }
++
++ usb_fill_bulk_urb(buf->urb, dev->udev,
++ usb_rcvbulkpipe(dev->udev,
++ dev->bulk_in_endpointAddr),
++ mem, dev->bulk_in_size,
++ hdpvr_read_bulk_callback, buf);
++
++ buf->status = BUFSTAT_AVAILABLE;
++ list_add_tail(&buf->buff_list, &dev->free_buff_list);
++ }
++ return 0;
++exit:
++ hdpvr_free_buffers(dev);
++ return retval;
++}
++
++static int hdpvr_submit_buffers(struct hdpvr_device *dev)
++{
++ struct hdpvr_buffer *buf;
++ struct urb *urb;
++ int ret = 0, err_count = 0;
++
++ mutex_lock(&dev->io_mutex);
++
++ while (dev->status == STATUS_STREAMING &&
++ !list_empty(&dev->free_buff_list)) {
++
++ buf = list_entry(dev->free_buff_list.next, struct hdpvr_buffer,
++ buff_list);
++ if (buf->status != BUFSTAT_AVAILABLE) {
++ v4l2_err(&dev->v4l2_dev,
++ "buffer not marked as availbale\n");
++ ret = -EFAULT;
++ goto err;
++ }
++
++ urb = buf->urb;
++ urb->status = 0;
++ urb->actual_length = 0;
++ ret = usb_submit_urb(urb, GFP_KERNEL);
++ if (ret) {
++ v4l2_err(&dev->v4l2_dev,
++ "usb_submit_urb in %s returned %d\n",
++ __func__, ret);
++ if (++err_count > 2)
++ break;
++ continue;
++ }
++ buf->status = BUFSTAT_INPROGRESS;
++ list_move_tail(&buf->buff_list, &dev->rec_buff_list);
++ }
++err:
++ print_buffer_status();
++ mutex_unlock(&dev->io_mutex);
++ return ret;
++}
++
++static struct hdpvr_buffer *hdpvr_get_next_buffer(struct hdpvr_device *dev)
++{
++ struct hdpvr_buffer *buf;
++
++ mutex_lock(&dev->io_mutex);
++
++ if (list_empty(&dev->rec_buff_list)) {
++ mutex_unlock(&dev->io_mutex);
++ return NULL;
++ }
++
++ buf = list_entry(dev->rec_buff_list.next, struct hdpvr_buffer,
++ buff_list);
++ mutex_unlock(&dev->io_mutex);
++
++ return buf;
++}
++
++static void hdpvr_transmit_buffers(struct work_struct *work)
++{
++ struct hdpvr_device *dev = container_of(work, struct hdpvr_device,
++ worker);
++
++ while (dev->status == STATUS_STREAMING) {
++
++ if (hdpvr_submit_buffers(dev)) {
++ v4l2_err(&dev->v4l2_dev, "couldn't submit buffers\n");
++ goto error;
++ }
++ if (wait_event_interruptible(dev->wait_buffer,
++ !list_empty(&dev->free_buff_list) ||
++ dev->status != STATUS_STREAMING))
++ goto error;
++ }
++
++ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
++ "transmit worker exited\n");
++ return;
++error:
++ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
++ "transmit buffers errored\n");
++ dev->status = STATUS_ERROR;
++}
++
++/* function expects dev->io_mutex to be hold by caller */
++static int hdpvr_start_streaming(struct hdpvr_device *dev)
++{
++ int ret;
++ struct hdpvr_video_info *vidinf;
++
++ if (dev->status == STATUS_STREAMING)
++ return 0;
++ else if (dev->status != STATUS_IDLE)
++ return -EAGAIN;
++
++ vidinf = get_video_info(dev);
++
++ if (vidinf) {
++ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
++ "video signal: %dx%d@%dhz\n", vidinf->width,
++ vidinf->height, vidinf->fps);
++ kfree(vidinf);
++
++ /* start streaming 2 request */
++ ret = usb_control_msg(dev->udev,
++ usb_sndctrlpipe(dev->udev, 0),
++ 0xb8, 0x38, 0x1, 0, NULL, 0, 8000);
++ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
++ "encoder start control request returned %d\n", ret);
++
++ hdpvr_config_call(dev, CTRL_START_STREAMING_VALUE, 0x00);
++
++ INIT_WORK(&dev->worker, hdpvr_transmit_buffers);
++ queue_work(dev->workqueue, &dev->worker);
++
++ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
++ "streaming started\n");
++ dev->status = STATUS_STREAMING;
++
++ return 0;
++ }
++ msleep(250);
++ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
++ "no video signal at input %d\n", dev->options.video_input);
++ return -EAGAIN;
++}
++
++
++/* function expects dev->io_mutex to be hold by caller */
++static int hdpvr_stop_streaming(struct hdpvr_device *dev)
++{
++ uint actual_length, c = 0;
++ u8 *buf;
++
++ if (dev->status == STATUS_IDLE)
++ return 0;
++ else if (dev->status != STATUS_STREAMING)
++ return -EAGAIN;
++
++ buf = kmalloc(dev->bulk_in_size, GFP_KERNEL);
++ if (!buf)
++ v4l2_err(&dev->v4l2_dev, "failed to allocate temporary buffer "
++ "for emptying the internal device buffer. "
++ "Next capture start will be slow\n");
++
++ dev->status = STATUS_SHUTTING_DOWN;
++ hdpvr_config_call(dev, CTRL_STOP_STREAMING_VALUE, 0x00);
++ mutex_unlock(&dev->io_mutex);
++
++ wake_up_interruptible(&dev->wait_buffer);
++ msleep(50);
++
++ flush_workqueue(dev->workqueue);
++
++ mutex_lock(&dev->io_mutex);
++ /* kill the still outstanding urbs */
++ hdpvr_cancel_queue(dev);
++
++ /* emptying the device buffer beforeshutting it down */
++ while (buf && ++c < 500 &&
++ !usb_bulk_msg(dev->udev,
++ usb_rcvbulkpipe(dev->udev,
++ dev->bulk_in_endpointAddr),
++ buf, dev->bulk_in_size, &actual_length,
++ BULK_URB_TIMEOUT)) {
++ /* wait */
++ msleep(5);
++ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
++ "%2d: got %d bytes\n", c, actual_length);
++ }
++ kfree(buf);
++ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
++ "used %d urbs to empty device buffers\n", c-1);
++ msleep(10);
++
++ dev->status = STATUS_IDLE;
++
++ return 0;
++}
++
++
++/*=======================================================================*/
++/*
++ * video 4 linux 2 file operations
++ */
++
++static int hdpvr_open(struct file *file)
++{
++ struct hdpvr_device *dev;
++ struct hdpvr_fh *fh;
++ int retval = -ENOMEM;
++
++ dev = (struct hdpvr_device *)video_get_drvdata(video_devdata(file));
++ if (!dev) {
++ v4l2_err(&dev->v4l2_dev, "open failing with with ENODEV\n");
++ retval = -ENODEV;
++ goto err;
++ }
++
++ fh = kzalloc(sizeof(struct hdpvr_fh), GFP_KERNEL);
++ if (!fh) {
++ v4l2_err(&dev->v4l2_dev, "Out of memory\n");
++ goto err;
++ }
++ /* lock the device to allow correctly handling errors
++ * in resumption */
++ mutex_lock(&dev->io_mutex);
++ dev->open_count++;
++
++ fh->dev = dev;
++
++ /* save our object in the file's private structure */
++ file->private_data = fh;
++
++ retval = 0;
++err:
++ mutex_unlock(&dev->io_mutex);
++ return retval;
++}
++
++static int hdpvr_release(struct file *file)
++{
++ struct hdpvr_fh *fh = (struct hdpvr_fh *)file->private_data;
++ struct hdpvr_device *dev = fh->dev;
++
++ if (!dev)
++ return -ENODEV;
++
++ mutex_lock(&dev->io_mutex);
++ if (!(--dev->open_count) && dev->status == STATUS_STREAMING)
++ hdpvr_stop_streaming(dev);
++
++ mutex_unlock(&dev->io_mutex);
++
++ return 0;
++}
++
++/*
++ * hdpvr_v4l2_read()
++ * will allocate buffers when called for the first time
++ */
++static ssize_t hdpvr_read(struct file *file, char __user *buffer, size_t count,
++ loff_t *pos)
++{
++ struct hdpvr_fh *fh = file->private_data;
++ struct hdpvr_device *dev = fh->dev;
++ struct hdpvr_buffer *buf = NULL;
++ struct urb *urb;
++ unsigned int ret = 0;
++ int rem, cnt;
++
++ if (*pos)
++ return -ESPIPE;
++
++ if (!dev)
++ return -ENODEV;
++
++ mutex_lock(&dev->io_mutex);
++ if (dev->status == STATUS_IDLE) {
++ if (hdpvr_start_streaming(dev)) {
++ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
++ "start_streaming failed\n");
++ ret = -EIO;
++ msleep(200);
++ dev->status = STATUS_IDLE;
++ mutex_unlock(&dev->io_mutex);
++ goto err;
++ }
++ print_buffer_status();
++ }
++ mutex_unlock(&dev->io_mutex);
++
++ /* wait for the first buffer */
++ if (!(file->f_flags & O_NONBLOCK)) {
++ if (wait_event_interruptible(dev->wait_data,
++ hdpvr_get_next_buffer(dev)))
++ return -ERESTARTSYS;
++ }
++
++ buf = hdpvr_get_next_buffer(dev);
++
++ while (count > 0 && buf) {
++
++ if (buf->status != BUFSTAT_READY &&
++ dev->status != STATUS_DISCONNECTED) {
++ /* return nonblocking */
++ if (file->f_flags & O_NONBLOCK) {
++ if (!ret)
++ ret = -EAGAIN;
++ goto err;
++ }
++
++ if (wait_event_interruptible(dev->wait_data,
++ buf->status == BUFSTAT_READY)) {
++ ret = -ERESTARTSYS;
++ goto err;
++ }
++ }
++
++ if (buf->status != BUFSTAT_READY)
++ break;
++
++ /* set remaining bytes to copy */
++ urb = buf->urb;
++ rem = urb->actual_length - buf->pos;
++ cnt = rem > count ? count : rem;
++
++ if (copy_to_user(buffer, urb->transfer_buffer + buf->pos,
++ cnt)) {
++ v4l2_err(&dev->v4l2_dev, "read: copy_to_user failed\n");
++ if (!ret)
++ ret = -EFAULT;
++ goto err;
++ }
++
++ buf->pos += cnt;
++ count -= cnt;
++ buffer += cnt;
++ ret += cnt;
++
++ /* finished, take next buffer */
++ if (buf->pos == urb->actual_length) {
++ mutex_lock(&dev->io_mutex);
++ buf->pos = 0;
++ buf->status = BUFSTAT_AVAILABLE;
++
++ list_move_tail(&buf->buff_list, &dev->free_buff_list);
++
++ print_buffer_status();
++
++ mutex_unlock(&dev->io_mutex);
++
++ wake_up_interruptible(&dev->wait_buffer);
++
++ buf = hdpvr_get_next_buffer(dev);
++ }
++ }
++err:
++ if (!ret && !buf)
++ ret = -EAGAIN;
++ return ret;
++}
++
++static unsigned int hdpvr_poll(struct file *filp, poll_table *wait)
++{
++ struct hdpvr_buffer *buf = NULL;
++ struct hdpvr_fh *fh = (struct hdpvr_fh *)filp->private_data;
++ struct hdpvr_device *dev = fh->dev;
++ unsigned int mask = 0;
++
++ mutex_lock(&dev->io_mutex);
++
++ if (video_is_unregistered(dev->video_dev))
++ return -EIO;
++
++ if (dev->status == STATUS_IDLE) {
++ if (hdpvr_start_streaming(dev)) {
++ v4l2_dbg(MSG_BUFFER, hdpvr_debug, &dev->v4l2_dev,
++ "start_streaming failed\n");
++ dev->status = STATUS_IDLE;
++ }
++
++ print_buffer_status();
++ }
++ mutex_unlock(&dev->io_mutex);
++
++ buf = hdpvr_get_next_buffer(dev);
++ /* only wait if no data is available */
++ if (!buf || buf->status != BUFSTAT_READY) {
++ poll_wait(filp, &dev->wait_data, wait);
++ buf = hdpvr_get_next_buffer(dev);
++ }
++ if (buf && buf->status == BUFSTAT_READY)
++ mask |= POLLIN | POLLRDNORM;
++
++ return mask;
++}
++
++
++static const struct v4l2_file_operations hdpvr_fops = {
++ .owner = THIS_MODULE,
++ .open = hdpvr_open,
++ .release = hdpvr_release,
++ .read = hdpvr_read,
++ .poll = hdpvr_poll,
++ .unlocked_ioctl = video_ioctl2,
++};
++
++/*=======================================================================*/
++/*
++ * V4L2 ioctl handling
++ */
++
++static int vidioc_querycap(struct file *file, void *priv,
++ struct v4l2_capability *cap)
++{
++ struct hdpvr_device *dev = video_drvdata(file);
++
++ strcpy(cap->driver, "hdpvr");
++ strcpy(cap->card, "Haupauge HD PVR");
++ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
++ cap->version = HDPVR_VERSION;
++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
++ V4L2_CAP_AUDIO |
++ V4L2_CAP_READWRITE;
++ return 0;
++}
++
++static int vidioc_s_std(struct file *file, void *private_data,
++ v4l2_std_id *std)
++{
++ struct hdpvr_fh *fh = file->private_data;
++ struct hdpvr_device *dev = fh->dev;
++ u8 std_type = 1;
++
++ if (*std & (V4L2_STD_NTSC | V4L2_STD_PAL_60))
++ std_type = 0;
++
++ return hdpvr_config_call(dev, CTRL_VIDEO_STD_TYPE, std_type);
++}
++
++static const char *iname[] = {
++ [HDPVR_COMPONENT] = "Component",
++ [HDPVR_SVIDEO] = "S-Video",
++ [HDPVR_COMPOSITE] = "Composite",
++};
++
++static int vidioc_enum_input(struct file *file, void *priv,
++ struct v4l2_input *i)
++{
++ struct hdpvr_fh *fh = file->private_data;
++ struct hdpvr_device *dev = fh->dev;
++ unsigned int n;
++
++ n = i->index;
++ if (n >= HDPVR_VIDEO_INPUTS)
++ return -EINVAL;
++
++ i->type = V4L2_INPUT_TYPE_CAMERA;
++
++ strncpy(i->name, iname[n], sizeof(i->name) - 1);
++ i->name[sizeof(i->name) - 1] = '\0';
++
++ i->audioset = 1<<HDPVR_RCA_FRONT | 1<<HDPVR_RCA_BACK | 1<<HDPVR_SPDIF;
++
++ i->std = dev->video_dev->tvnorms;
++
++ return 0;
++}
++
++static int vidioc_s_input(struct file *file, void *private_data,
++ unsigned int index)
++{
++ struct hdpvr_fh *fh = file->private_data;
++ struct hdpvr_device *dev = fh->dev;
++ int retval;
++
++ if (index >= HDPVR_VIDEO_INPUTS)
++ return -EINVAL;
++
++ if (dev->status != STATUS_IDLE)
++ return -EAGAIN;
++
++ retval = hdpvr_config_call(dev, CTRL_VIDEO_INPUT_VALUE, index+1);
++ if (!retval)
++ dev->options.video_input = index;
++
++ return retval;
++}
++
++static int vidioc_g_input(struct file *file, void *private_data,
++ unsigned int *index)
++{
++ struct hdpvr_fh *fh = file->private_data;
++ struct hdpvr_device *dev = fh->dev;
++
++ *index = dev->options.video_input;
++ return 0;
++}
++
++
++static const char *audio_iname[] = {
++ [HDPVR_RCA_FRONT] = "RCA front",
++ [HDPVR_RCA_BACK] = "RCA back",
++ [HDPVR_SPDIF] = "SPDIF",
++};
++
++static int vidioc_enumaudio(struct file *file, void *priv,
++ struct v4l2_audio *audio)
++{
++ unsigned int n;
++
++ n = audio->index;
++ if (n >= HDPVR_AUDIO_INPUTS)
++ return -EINVAL;
++
++ audio->capability = V4L2_AUDCAP_STEREO;
++
++ strncpy(audio->name, audio_iname[n], sizeof(audio->name) - 1);
++ audio->name[sizeof(audio->name) - 1] = '\0';
++
++ return 0;
++}
++
++static int vidioc_s_audio(struct file *file, void *private_data,
++ struct v4l2_audio *audio)
++{
++ struct hdpvr_fh *fh = file->private_data;
++ struct hdpvr_device *dev = fh->dev;
++ int retval;
++
++ if (audio->index >= HDPVR_AUDIO_INPUTS)
++ return -EINVAL;
++
++ if (dev->status != STATUS_IDLE)
++ return -EAGAIN;
++
++ retval = hdpvr_set_audio(dev, audio->index+1, dev->options.audio_codec);
++ if (!retval)
++ dev->options.audio_input = audio->index;
++
++ return retval;
++}
++
++static int vidioc_g_audio(struct file *file, void *private_data,
++ struct v4l2_audio *audio)
++{
++ struct hdpvr_fh *fh = file->private_data;
++ struct hdpvr_device *dev = fh->dev;
++
++ audio->index = dev->options.audio_input;
++ audio->capability = V4L2_AUDCAP_STEREO;
++ strncpy(audio->name, audio_iname[audio->index], sizeof(audio->name));
++ audio->name[sizeof(audio->name) - 1] = '\0';
++ return 0;
++}
++
++static const s32 supported_v4l2_ctrls[] = {
++ V4L2_CID_BRIGHTNESS,
++ V4L2_CID_CONTRAST,
++ V4L2_CID_SATURATION,
++ V4L2_CID_HUE,
++ V4L2_CID_SHARPNESS,
++ V4L2_CID_MPEG_AUDIO_ENCODING,
++ V4L2_CID_MPEG_VIDEO_ENCODING,
++ V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
++ V4L2_CID_MPEG_VIDEO_BITRATE,
++ V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
++};
++
++static int fill_queryctrl(struct hdpvr_options *opt, struct v4l2_queryctrl *qc,
++ int ac3)
++{
++ int err;
++
++ switch (qc->id) {
++ case V4L2_CID_BRIGHTNESS:
++ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x86);
++ case V4L2_CID_CONTRAST:
++ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
++ case V4L2_CID_SATURATION:
++ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
++ case V4L2_CID_HUE:
++ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
++ case V4L2_CID_SHARPNESS:
++ return v4l2_ctrl_query_fill(qc, 0x0, 0xff, 1, 0x80);
++ case V4L2_CID_MPEG_AUDIO_ENCODING:
++ return v4l2_ctrl_query_fill(
++ qc, V4L2_MPEG_AUDIO_ENCODING_AAC,
++ ac3 ? V4L2_MPEG_AUDIO_ENCODING_AC3
++ : V4L2_MPEG_AUDIO_ENCODING_AAC,
++ 1, V4L2_MPEG_AUDIO_ENCODING_AAC);
++ case V4L2_CID_MPEG_VIDEO_ENCODING:
++ return v4l2_ctrl_query_fill(
++ qc, V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC,
++ V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1,
++ V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC);
++
++/* case V4L2_CID_MPEG_VIDEO_? maybe keyframe interval: */
++/* return v4l2_ctrl_query_fill(qc, 0, 128, 128, 0); */
++ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
++ return v4l2_ctrl_query_fill(
++ qc, V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
++ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1,
++ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR);
++
++ case V4L2_CID_MPEG_VIDEO_BITRATE:
++ return v4l2_ctrl_query_fill(qc, 1000000, 13500000, 100000,
++ 6500000);
++ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
++ err = v4l2_ctrl_query_fill(qc, 1100000, 20200000, 100000,
++ 9000000);
++ if (!err && opt->bitrate_mode == HDPVR_CONSTANT)
++ qc->flags |= V4L2_CTRL_FLAG_INACTIVE;
++ return err;
++ default:
++ return -EINVAL;
++ }
++}
++
++static int vidioc_queryctrl(struct file *file, void *private_data,
++ struct v4l2_queryctrl *qc)
++{
++ struct hdpvr_fh *fh = file->private_data;
++ struct hdpvr_device *dev = fh->dev;
++ int i, next;
++ u32 id = qc->id;
++
++ memset(qc, 0, sizeof(*qc));
++
++ next = !!(id & V4L2_CTRL_FLAG_NEXT_CTRL);
++ qc->id = id & ~V4L2_CTRL_FLAG_NEXT_CTRL;
++
++ for (i = 0; i < ARRAY_SIZE(supported_v4l2_ctrls); i++) {
++ if (next) {
++ if (qc->id < supported_v4l2_ctrls[i])
++ qc->id = supported_v4l2_ctrls[i];
++ else
++ continue;
++ }
++
++ if (qc->id == supported_v4l2_ctrls[i])
++ return fill_queryctrl(&dev->options, qc,
++ dev->flags & HDPVR_FLAG_AC3_CAP);
++
++ if (qc->id < supported_v4l2_ctrls[i])
++ break;
++ }
++
++ return -EINVAL;
++}
++
++static int vidioc_g_ctrl(struct file *file, void *private_data,
++ struct v4l2_control *ctrl)
++{
++ struct hdpvr_fh *fh = file->private_data;
++ struct hdpvr_device *dev = fh->dev;
++
++ switch (ctrl->id) {
++ case V4L2_CID_BRIGHTNESS:
++ ctrl->value = dev->options.brightness;
++ break;
++ case V4L2_CID_CONTRAST:
++ ctrl->value = dev->options.contrast;
++ break;
++ case V4L2_CID_SATURATION:
++ ctrl->value = dev->options.saturation;
++ break;
++ case V4L2_CID_HUE:
++ ctrl->value = dev->options.hue;
++ break;
++ case V4L2_CID_SHARPNESS:
++ ctrl->value = dev->options.sharpness;
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static int vidioc_s_ctrl(struct file *file, void *private_data,
++ struct v4l2_control *ctrl)
++{
++ struct hdpvr_fh *fh = file->private_data;
++ struct hdpvr_device *dev = fh->dev;
++ int retval;
++
++ switch (ctrl->id) {
++ case V4L2_CID_BRIGHTNESS:
++ retval = hdpvr_config_call(dev, CTRL_BRIGHTNESS, ctrl->value);
++ if (!retval)
++ dev->options.brightness = ctrl->value;
++ break;
++ case V4L2_CID_CONTRAST:
++ retval = hdpvr_config_call(dev, CTRL_CONTRAST, ctrl->value);
++ if (!retval)
++ dev->options.contrast = ctrl->value;
++ break;
++ case V4L2_CID_SATURATION:
++ retval = hdpvr_config_call(dev, CTRL_SATURATION, ctrl->value);
++ if (!retval)
++ dev->options.saturation = ctrl->value;
++ break;
++ case V4L2_CID_HUE:
++ retval = hdpvr_config_call(dev, CTRL_HUE, ctrl->value);
++ if (!retval)
++ dev->options.hue = ctrl->value;
++ break;
++ case V4L2_CID_SHARPNESS:
++ retval = hdpvr_config_call(dev, CTRL_SHARPNESS, ctrl->value);
++ if (!retval)
++ dev->options.sharpness = ctrl->value;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ return retval;
++}
++
++
++static int hdpvr_get_ctrl(struct hdpvr_options *opt,
++ struct v4l2_ext_control *ctrl)
++{
++ switch (ctrl->id) {
++ case V4L2_CID_MPEG_AUDIO_ENCODING:
++ ctrl->value = opt->audio_codec;
++ break;
++ case V4L2_CID_MPEG_VIDEO_ENCODING:
++ ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC;
++ break;
++/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
++/* ctrl->value = (opt->gop_mode & 0x2) ? 0 : 128; */
++/* break; */
++ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
++ ctrl->value = opt->bitrate_mode == HDPVR_CONSTANT
++ ? V4L2_MPEG_VIDEO_BITRATE_MODE_CBR
++ : V4L2_MPEG_VIDEO_BITRATE_MODE_VBR;
++ break;
++ case V4L2_CID_MPEG_VIDEO_BITRATE:
++ ctrl->value = opt->bitrate * 100000;
++ break;
++ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
++ ctrl->value = opt->peak_bitrate * 100000;
++ break;
++ case V4L2_CID_MPEG_STREAM_TYPE:
++ ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_TS;
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static int vidioc_g_ext_ctrls(struct file *file, void *priv,
++ struct v4l2_ext_controls *ctrls)
++{
++ struct hdpvr_fh *fh = file->private_data;
++ struct hdpvr_device *dev = fh->dev;
++ int i, err = 0;
++
++ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
++ for (i = 0; i < ctrls->count; i++) {
++ struct v4l2_ext_control *ctrl = ctrls->controls + i;
++
++ err = hdpvr_get_ctrl(&dev->options, ctrl);
++ if (err) {
++ ctrls->error_idx = i;
++ break;
++ }
++ }
++ return err;
++
++ }
++
++ return -EINVAL;
++}
++
++
++static int hdpvr_try_ctrl(struct v4l2_ext_control *ctrl, int ac3)
++{
++ int ret = -EINVAL;
++
++ switch (ctrl->id) {
++ case V4L2_CID_MPEG_AUDIO_ENCODING:
++ if (ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AAC ||
++ (ac3 && ctrl->value == V4L2_MPEG_AUDIO_ENCODING_AC3))
++ ret = 0;
++ break;
++ case V4L2_CID_MPEG_VIDEO_ENCODING:
++ if (ctrl->value == V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC)
++ ret = 0;
++ break;
++/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
++/* if (ctrl->value == 0 || ctrl->value == 128) */
++/* ret = 0; */
++/* break; */
++ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
++ if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR ||
++ ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR)
++ ret = 0;
++ break;
++ case V4L2_CID_MPEG_VIDEO_BITRATE:
++ {
++ uint bitrate = ctrl->value / 100000;
++ if (bitrate >= 10 && bitrate <= 135)
++ ret = 0;
++ break;
++ }
++ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
++ {
++ uint peak_bitrate = ctrl->value / 100000;
++ if (peak_bitrate >= 10 && peak_bitrate <= 202)
++ ret = 0;
++ break;
++ }
++ case V4L2_CID_MPEG_STREAM_TYPE:
++ if (ctrl->value == V4L2_MPEG_STREAM_TYPE_MPEG2_TS)
++ ret = 0;
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static int vidioc_try_ext_ctrls(struct file *file, void *priv,
++ struct v4l2_ext_controls *ctrls)
++{
++ struct hdpvr_fh *fh = file->private_data;
++ struct hdpvr_device *dev = fh->dev;
++ int i, err = 0;
++
++ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
++ for (i = 0; i < ctrls->count; i++) {
++ struct v4l2_ext_control *ctrl = ctrls->controls + i;
++
++ err = hdpvr_try_ctrl(ctrl,
++ dev->flags & HDPVR_FLAG_AC3_CAP);
++ if (err) {
++ ctrls->error_idx = i;
++ break;
++ }
++ }
++ return err;
++ }
++
++ return -EINVAL;
++}
++
++
++static int hdpvr_set_ctrl(struct hdpvr_device *dev,
++ struct v4l2_ext_control *ctrl)
++{
++ struct hdpvr_options *opt = &dev->options;
++ int ret = 0;
++
++ switch (ctrl->id) {
++ case V4L2_CID_MPEG_AUDIO_ENCODING:
++ if (dev->flags & HDPVR_FLAG_AC3_CAP) {
++ opt->audio_codec = ctrl->value;
++ ret = hdpvr_set_audio(dev, opt->audio_input,
++ opt->audio_codec);
++ }
++ break;
++ case V4L2_CID_MPEG_VIDEO_ENCODING:
++ break;
++/* case V4L2_CID_MPEG_VIDEO_B_FRAMES: */
++/* if (ctrl->value == 0 && !(opt->gop_mode & 0x2)) { */
++/* opt->gop_mode |= 0x2; */
++/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */
++/* opt->gop_mode); */
++/* } */
++/* if (ctrl->value == 128 && opt->gop_mode & 0x2) { */
++/* opt->gop_mode &= ~0x2; */
++/* hdpvr_config_call(dev, CTRL_GOP_MODE_VALUE, */
++/* opt->gop_mode); */
++/* } */
++/* break; */
++ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
++ if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_CBR &&
++ opt->bitrate_mode != HDPVR_CONSTANT) {
++ opt->bitrate_mode = HDPVR_CONSTANT;
++ hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
++ opt->bitrate_mode);
++ }
++ if (ctrl->value == V4L2_MPEG_VIDEO_BITRATE_MODE_VBR &&
++ opt->bitrate_mode == HDPVR_CONSTANT) {
++ opt->bitrate_mode = HDPVR_VARIABLE_AVERAGE;
++ hdpvr_config_call(dev, CTRL_BITRATE_MODE_VALUE,
++ opt->bitrate_mode);
++ }
++ break;
++ case V4L2_CID_MPEG_VIDEO_BITRATE: {
++ uint bitrate = ctrl->value / 100000;
++
++ opt->bitrate = bitrate;
++ if (bitrate >= opt->peak_bitrate)
++ opt->peak_bitrate = bitrate+1;
++
++ hdpvr_set_bitrate(dev);
++ break;
++ }
++ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK: {
++ uint peak_bitrate = ctrl->value / 100000;
++
++ if (opt->bitrate_mode == HDPVR_CONSTANT)
++ break;
++
++ if (opt->bitrate < peak_bitrate) {
++ opt->peak_bitrate = peak_bitrate;
++ hdpvr_set_bitrate(dev);
++ } else
++ ret = -EINVAL;
++ break;
++ }
++ case V4L2_CID_MPEG_STREAM_TYPE:
++ break;
++ default:
++ return -EINVAL;
++ }
++ return ret;
++}
++
++static int vidioc_s_ext_ctrls(struct file *file, void *priv,
++ struct v4l2_ext_controls *ctrls)
++{
++ struct hdpvr_fh *fh = file->private_data;
++ struct hdpvr_device *dev = fh->dev;
++ int i, err = 0;
++
++ if (ctrls->ctrl_class == V4L2_CTRL_CLASS_MPEG) {
++ for (i = 0; i < ctrls->count; i++) {
++ struct v4l2_ext_control *ctrl = ctrls->controls + i;
++
++ err = hdpvr_try_ctrl(ctrl,
++ dev->flags & HDPVR_FLAG_AC3_CAP);
++ if (err) {
++ ctrls->error_idx = i;
++ break;
++ }
++ err = hdpvr_set_ctrl(dev, ctrl);
++ if (err) {
++ ctrls->error_idx = i;
++ break;
++ }
++ }
++ return err;
++
++ }
++
++ return -EINVAL;
++}
++
++static int vidioc_enum_fmt_vid_cap(struct file *file, void *private_data,
++ struct v4l2_fmtdesc *f)
++{
++
++ if (f->index != 0 || f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
++ return -EINVAL;
++
++ f->flags = V4L2_FMT_FLAG_COMPRESSED;
++ strncpy(f->description, "MPEG2-TS with AVC/AAC streams", 32);
++ f->pixelformat = V4L2_PIX_FMT_MPEG;
++
++ return 0;
++}
++
++static int vidioc_g_fmt_vid_cap(struct file *file, void *private_data,
++ struct v4l2_format *f)
++{
++ struct hdpvr_fh *fh = file->private_data;
++ struct hdpvr_device *dev = fh->dev;
++ struct hdpvr_video_info *vid_info;
++
++ if (!dev)
++ return -ENODEV;
++
++ vid_info = get_video_info(dev);
++ if (!vid_info)
++ return -EFAULT;
++
++ f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
++ f->fmt.pix.width = vid_info->width;
++ f->fmt.pix.height = vid_info->height;
++ f->fmt.pix.sizeimage = dev->bulk_in_size;
++ f->fmt.pix.colorspace = 0;
++ f->fmt.pix.bytesperline = 0;
++ f->fmt.pix.field = V4L2_FIELD_ANY;
++
++ kfree(vid_info);
++ return 0;
++}
++
++static int vidioc_encoder_cmd(struct file *filp, void *priv,
++ struct v4l2_encoder_cmd *a)
++{
++ struct hdpvr_fh *fh = filp->private_data;
++ struct hdpvr_device *dev = fh->dev;
++ int res;
++
++ mutex_lock(&dev->io_mutex);
++
++ memset(&a->raw, 0, sizeof(a->raw));
++ switch (a->cmd) {
++ case V4L2_ENC_CMD_START:
++ a->flags = 0;
++ res = hdpvr_start_streaming(dev);
++ break;
++ case V4L2_ENC_CMD_STOP:
++ res = hdpvr_stop_streaming(dev);
++ break;
++ default:
++ v4l2_dbg(MSG_INFO, hdpvr_debug, &dev->v4l2_dev,
++ "Unsupported encoder cmd %d\n", a->cmd);
++ res = -EINVAL;
++ }
++ mutex_unlock(&dev->io_mutex);
++ return res;
++}
++
++static int vidioc_try_encoder_cmd(struct file *filp, void *priv,
++ struct v4l2_encoder_cmd *a)
++{
++ switch (a->cmd) {
++ case V4L2_ENC_CMD_START:
++ case V4L2_ENC_CMD_STOP:
++ return 0;
++ default:
++ return -EINVAL;
++ }
++}
++
++static const struct v4l2_ioctl_ops hdpvr_ioctl_ops = {
++ .vidioc_querycap = vidioc_querycap,
++ .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_enumaudio = vidioc_enumaudio,
++ .vidioc_g_audio = vidioc_g_audio,
++ .vidioc_s_audio = vidioc_s_audio,
++ .vidioc_queryctrl = vidioc_queryctrl,
++ .vidioc_g_ctrl = vidioc_g_ctrl,
++ .vidioc_s_ctrl = vidioc_s_ctrl,
++ .vidioc_g_ext_ctrls = vidioc_g_ext_ctrls,
++ .vidioc_s_ext_ctrls = vidioc_s_ext_ctrls,
++ .vidioc_try_ext_ctrls = vidioc_try_ext_ctrls,
++ .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap,
++ .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap,
++ .vidioc_encoder_cmd = vidioc_encoder_cmd,
++ .vidioc_try_encoder_cmd = vidioc_try_encoder_cmd,
++};
++
++static void hdpvr_device_release(struct video_device *vdev)
++{
++ struct hdpvr_device *dev = video_get_drvdata(vdev);
++
++ hdpvr_delete(dev);
++}
++
++static const struct video_device hdpvr_video_template = {
++/* .type = VFL_TYPE_GRABBER, */
++/* .type2 = VID_TYPE_CAPTURE | VID_TYPE_MPEG_ENCODER, */
++ .fops = &hdpvr_fops,
++ .release = hdpvr_device_release,
++ .ioctl_ops = &hdpvr_ioctl_ops,
++ .tvnorms =
++ V4L2_STD_NTSC | V4L2_STD_SECAM | V4L2_STD_PAL_B |
++ V4L2_STD_PAL_G | V4L2_STD_PAL_H | V4L2_STD_PAL_I |
++ V4L2_STD_PAL_D | V4L2_STD_PAL_M | V4L2_STD_PAL_N |
++ V4L2_STD_PAL_60,
++};
++
++int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent,
++ int devnum)
++{
++ /* setup and register video device */
++ dev->video_dev = video_device_alloc();
++ if (!dev->video_dev) {
++ v4l2_err(&dev->v4l2_dev, "video_device_alloc() failed\n");
++ goto error;
++ }
++
++ *(dev->video_dev) = hdpvr_video_template;
++ strcpy(dev->video_dev->name, "Hauppauge HD PVR");
++ dev->video_dev->parent = parent;
++ video_set_drvdata(dev->video_dev, dev);
++
++ if (video_register_device(dev->video_dev, VFL_TYPE_GRABBER, devnum)) {
++ v4l2_err(&dev->v4l2_dev, "video_device registration failed\n");
++ goto error;
++ }
++
++ return 0;
++error:
++ return -ENOMEM;
++}
+diff --git a/drivers/media/video/hdpvr/hdpvr.h b/drivers/media/video/hdpvr/hdpvr.h
+new file mode 100644
+index 0000000..1edd875
+--- /dev/null
++++ b/drivers/media/video/hdpvr/hdpvr.h
+@@ -0,0 +1,303 @@
++/*
++ * Hauppauge HD PVR USB driver
++ *
++ * Copyright (C) 2008 Janne Grunau (j@jannau.net)
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation, version 2.
++ *
++ */
++
++#include <linux/usb.h>
++#include <linux/i2c.h>
++#include <linux/mutex.h>
++#include <linux/workqueue.h>
++#include <linux/videodev2.h>
++
++#include <media/v4l2-device.h>
++
++#define HDPVR_MAJOR_VERSION 0
++#define HDPVR_MINOR_VERSION 2
++#define HDPVR_RELEASE 0
++#define HDPVR_VERSION \
++ KERNEL_VERSION(HDPVR_MAJOR_VERSION, HDPVR_MINOR_VERSION, HDPVR_RELEASE)
++
++#define HDPVR_MAX 8
++
++/* Define these values to match your devices */
++#define HD_PVR_VENDOR_ID 0x2040
++#define HD_PVR_PRODUCT_ID 0x4900
++#define HD_PVR_PRODUCT_ID1 0x4901
++#define HD_PVR_PRODUCT_ID2 0x4902
++
++#define UNSET (-1U)
++
++#define NUM_BUFFERS 64
++
++#define HDPVR_FIRMWARE_VERSION 0x8
++#define HDPVR_FIRMWARE_VERSION_AC3 0xd
++
++/* #define HDPVR_DEBUG */
++
++extern int hdpvr_debug;
++
++#define MSG_INFO 1
++#define MSG_BUFFER 2
++
++struct hdpvr_options {
++ u8 video_std;
++ u8 video_input;
++ u8 audio_input;
++ u8 bitrate; /* in 100kbps */
++ u8 peak_bitrate; /* in 100kbps */
++ u8 bitrate_mode;
++ u8 gop_mode;
++ enum v4l2_mpeg_audio_encoding audio_codec;
++ u8 brightness;
++ u8 contrast;
++ u8 hue;
++ u8 saturation;
++ u8 sharpness;
++};
++
++/* Structure to hold all of our device specific stuff */
++struct hdpvr_device {
++ /* the v4l device for this device */
++ struct video_device *video_dev;
++ /* the usb device for this device */
++ struct usb_device *udev;
++ /* v4l2-device unused */
++ struct v4l2_device v4l2_dev;
++
++ /* the max packet size of the bulk endpoint */
++ size_t bulk_in_size;
++ /* the address of the bulk in endpoint */
++ __u8 bulk_in_endpointAddr;
++
++ /* holds the current device status */
++ __u8 status;
++ /* count the number of openers */
++ uint open_count;
++
++ /* holds the cureent set options */
++ struct hdpvr_options options;
++
++ uint flags;
++
++ /* synchronize I/O */
++ struct mutex io_mutex;
++ /* available buffers */
++ struct list_head free_buff_list;
++ /* in progress buffers */
++ struct list_head rec_buff_list;
++ /* waitqueue for buffers */
++ wait_queue_head_t wait_buffer;
++ /* waitqueue for data */
++ wait_queue_head_t wait_data;
++ /**/
++ struct workqueue_struct *workqueue;
++ /**/
++ struct work_struct worker;
++
++ /* I2C adapter */
++ struct i2c_adapter *i2c_adapter;
++ /* I2C lock */
++ struct mutex i2c_mutex;
++
++ /* usb control transfer buffer and lock */
++ struct mutex usbc_mutex;
++ u8 *usbc_buf;
++};
++
++
++/* buffer one bulk urb of data */
++struct hdpvr_buffer {
++ struct list_head buff_list;
++
++ struct urb *urb;
++
++ struct hdpvr_device *dev;
++
++ uint pos;
++
++ __u8 status;
++};
++
++/* */
++
++struct hdpvr_video_info {
++ u16 width;
++ u16 height;
++ u8 fps;
++};
++
++enum {
++ STATUS_UNINITIALIZED = 0,
++ STATUS_IDLE,
++ STATUS_STARTING,
++ STATUS_SHUTTING_DOWN,
++ STATUS_STREAMING,
++ STATUS_ERROR,
++ STATUS_DISCONNECTED,
++};
++
++enum {
++ HDPVR_FLAG_AC3_CAP = 1,
++};
++
++enum {
++ BUFSTAT_UNINITIALIZED = 0,
++ BUFSTAT_AVAILABLE,
++ BUFSTAT_INPROGRESS,
++ BUFSTAT_READY,
++};
++
++#define CTRL_START_STREAMING_VALUE 0x0700
++#define CTRL_STOP_STREAMING_VALUE 0x0800
++#define CTRL_BITRATE_VALUE 0x1000
++#define CTRL_BITRATE_MODE_VALUE 0x1200
++#define CTRL_GOP_MODE_VALUE 0x1300
++#define CTRL_VIDEO_INPUT_VALUE 0x1500
++#define CTRL_VIDEO_STD_TYPE 0x1700
++#define CTRL_AUDIO_INPUT_VALUE 0x2500
++#define CTRL_BRIGHTNESS 0x2900
++#define CTRL_CONTRAST 0x2a00
++#define CTRL_HUE 0x2b00
++#define CTRL_SATURATION 0x2c00
++#define CTRL_SHARPNESS 0x2d00
++#define CTRL_LOW_PASS_FILTER_VALUE 0x3100
++
++#define CTRL_DEFAULT_INDEX 0x0003
++
++
++ /* :0 s 38 01 1000 0003 0004 4 = 0a00ca00
++ * BITRATE SETTING
++ * 1st and 2nd byte (little endian): average bitrate in 100 000 bit/s
++ * min: 1 mbit/s, max: 13.5 mbit/s
++ * 3rd and 4th byte (little endian): peak bitrate in 100 000 bit/s
++ * min: average + 100kbit/s,
++ * max: 20.2 mbit/s
++ */
++
++ /* :0 s 38 01 1200 0003 0001 1 = 02
++ * BIT RATE MODE
++ * constant = 1, variable (peak) = 2, variable (average) = 3
++ */
++
++ /* :0 s 38 01 1300 0003 0001 1 = 03
++ * GOP MODE (2 bit)
++ * low bit 0/1: advanced/simple GOP
++ * high bit 0/1: IDR(4/32/128) / no IDR (4/32/0)
++ */
++
++ /* :0 s 38 01 1700 0003 0001 1 = 00
++ * VIDEO STANDARD or FREQUNCY 0 = 60hz, 1 = 50hz
++ */
++
++ /* :0 s 38 01 3100 0003 0004 4 = 03030000
++ * FILTER CONTROL
++ * 1st byte luma low pass filter strength,
++ * 2nd byte chroma low pass filter strength,
++ * 3rd byte MF enable chroma, min=0, max=1
++ * 4th byte n
++ */
++
++
++ /* :0 s 38 b9 0001 0000 0000 0 */
++
++
++
++/* :0 s 38 d3 0000 0000 0001 1 = 00 */
++/* ret = usb_control_msg(dev->udev, */
++/* usb_sndctrlpipe(dev->udev, 0), */
++/* 0xd3, 0x38, */
++/* 0, 0, */
++/* "\0", 1, */
++/* 1000); */
++
++/* info("control request returned %d", ret); */
++/* msleep(5000); */
++
++
++ /* :0 s b8 81 1400 0003 0005 5 <
++ * :0 0 5 = d0024002 19
++ * QUERY FRAME SIZE AND RATE
++ * 1st and 2nd byte (little endian): horizontal resolution
++ * 3rd and 4th byte (little endian): vertical resolution
++ * 5th byte: frame rate
++ */
++
++ /* :0 s b8 81 1800 0003 0003 3 <
++ * :0 0 3 = 030104
++ * QUERY SIGNAL AND DETECTED LINES, maybe INPUT
++ */
++
++enum hdpvr_video_std {
++ HDPVR_60HZ = 0,
++ HDPVR_50HZ,
++};
++
++enum hdpvr_video_input {
++ HDPVR_COMPONENT = 0,
++ HDPVR_SVIDEO,
++ HDPVR_COMPOSITE,
++ HDPVR_VIDEO_INPUTS
++};
++
++enum hdpvr_audio_inputs {
++ HDPVR_RCA_BACK = 0,
++ HDPVR_RCA_FRONT,
++ HDPVR_SPDIF,
++ HDPVR_AUDIO_INPUTS
++};
++
++enum hdpvr_bitrate_mode {
++ HDPVR_CONSTANT = 1,
++ HDPVR_VARIABLE_PEAK,
++ HDPVR_VARIABLE_AVERAGE,
++};
++
++enum hdpvr_gop_mode {
++ HDPVR_ADVANCED_IDR_GOP = 0,
++ HDPVR_SIMPLE_IDR_GOP,
++ HDPVR_ADVANCED_NOIDR_GOP,
++ HDPVR_SIMPLE_NOIDR_GOP,
++};
++
++void hdpvr_delete(struct hdpvr_device *dev);
++
++/*========================================================================*/
++/* hardware control functions */
++int hdpvr_set_options(struct hdpvr_device *dev);
++
++int hdpvr_set_bitrate(struct hdpvr_device *dev);
++
++int hdpvr_set_audio(struct hdpvr_device *dev, u8 input,
++ enum v4l2_mpeg_audio_encoding codec);
++
++int hdpvr_config_call(struct hdpvr_device *dev, uint value,
++ unsigned char valbuf);
++
++struct hdpvr_video_info *get_video_info(struct hdpvr_device *dev);
++
++/* :0 s b8 81 1800 0003 0003 3 < */
++/* :0 0 3 = 0301ff */
++int get_input_lines_info(struct hdpvr_device *dev);
++
++
++/*========================================================================*/
++/* v4l2 registration */
++int hdpvr_register_videodev(struct hdpvr_device *dev, struct device *parent,
++ int devnumber);
++
++int hdpvr_cancel_queue(struct hdpvr_device *dev);
++
++/*========================================================================*/
++/* i2c adapter registration */
++int hdpvr_register_i2c_adapter(struct hdpvr_device *dev);
++
++/*========================================================================*/
++/* buffer management */
++int hdpvr_free_buffers(struct hdpvr_device *dev);
++int hdpvr_alloc_buffers(struct hdpvr_device *dev, uint count);
+diff --git a/drivers/media/video/hexium_gemini.c b/drivers/media/video/hexium_gemini.c
+index 79393d1..8e1463e 100644
+--- a/drivers/media/video/hexium_gemini.c
++++ b/drivers/media/video/hexium_gemini.c
+@@ -56,17 +56,6 @@ struct hexium_data
+ u8 byte;
+ };
+
+-static struct saa7146_extension_ioctls ioctls[] = {
+- { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE },
+- { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE },
+- { VIDIOC_QUERYCTRL, SAA7146_BEFORE },
+- { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE },
+- { VIDIOC_S_STD, SAA7146_AFTER },
+- { VIDIOC_G_CTRL, SAA7146_BEFORE },
+- { VIDIOC_S_CTRL, SAA7146_BEFORE },
+- { 0, 0 }
+-};
+-
+ #define HEXIUM_CONTROLS 1
+ static struct v4l2_queryctrl hexium_controls[] = {
+ { V4L2_CID_PRIVATE_BASE, V4L2_CTRL_TYPE_BOOLEAN, "B/W", 0, 1, 1, 0, 0 },
+@@ -231,6 +220,132 @@ static int hexium_set_standard(struct hexium *hexium, struct hexium_data *vdec)
+ return 0;
+ }
+
++static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
++{
++ DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
++
++ if (i->index < 0 || i->index >= HEXIUM_INPUTS)
++ return -EINVAL;
++
++ memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
++
++ DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index));
++ return 0;
++}
++
++static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct hexium *hexium = (struct hexium *) dev->ext_priv;
++
++ *input = hexium->cur_input;
++
++ DEB_D(("VIDIOC_G_INPUT: %d\n", *input));
++ return 0;
++}
++
++static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct hexium *hexium = (struct hexium *) dev->ext_priv;
++
++ DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
++
++ if (input < 0 || input >= HEXIUM_INPUTS)
++ return -EINVAL;
++
++ hexium->cur_input = input;
++ hexium_set_input(hexium, input);
++ return 0;
++}
++
++/* the saa7146 provides some controls (brightness, contrast, saturation)
++ which gets registered *after* this function. because of this we have
++ to return with a value != 0 even if the function succeded.. */
++static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ int i;
++
++ for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
++ if (hexium_controls[i].id == qc->id) {
++ *qc = hexium_controls[i];
++ DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id));
++ return 0;
++ }
++ }
++ return dev->ext_vv_data->core_ops->vidioc_queryctrl(file, fh, qc);
++}
++
++static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct hexium *hexium = (struct hexium *) dev->ext_priv;
++ int i;
++
++ for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
++ if (hexium_controls[i].id == vc->id)
++ break;
++ }
++
++ if (i < 0)
++ return dev->ext_vv_data->core_ops->vidioc_g_ctrl(file, fh, vc);
++
++ if (vc->id == V4L2_CID_PRIVATE_BASE) {
++ vc->value = hexium->cur_bw;
++ DEB_D(("VIDIOC_G_CTRL BW:%d.\n", vc->value));
++ return 0;
++ }
++ return -EINVAL;
++}
++
++static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct hexium *hexium = (struct hexium *) dev->ext_priv;
++ int i = 0;
++
++ for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
++ if (hexium_controls[i].id == vc->id)
++ break;
++ }
++
++ if (i < 0)
++ return dev->ext_vv_data->core_ops->vidioc_s_ctrl(file, fh, vc);
++
++ if (vc->id == V4L2_CID_PRIVATE_BASE)
++ hexium->cur_bw = vc->value;
++
++ DEB_D(("VIDIOC_S_CTRL BW:%d.\n", hexium->cur_bw));
++
++ if (0 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
++ hexium_set_standard(hexium, hexium_pal);
++ return 0;
++ }
++ if (0 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
++ hexium_set_standard(hexium, hexium_ntsc);
++ return 0;
++ }
++ if (0 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) {
++ hexium_set_standard(hexium, hexium_secam);
++ return 0;
++ }
++ if (1 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
++ hexium_set_standard(hexium, hexium_pal_bw);
++ return 0;
++ }
++ if (1 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
++ hexium_set_standard(hexium, hexium_ntsc_bw);
++ return 0;
++ }
++ if (1 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std)
++ /* fixme: is there no bw secam mode? */
++ return -EINVAL;
++
++ return -EINVAL;
++}
++
++
+ static struct saa7146_ext_vv vv_data;
+
+ /* this function only gets called when the probing was successful */
+@@ -279,6 +394,12 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d
+ hexium->cur_input = 0;
+
+ saa7146_vv_init(dev, &vv_data);
++ vv_data.ops.vidioc_queryctrl = vidioc_queryctrl;
++ vv_data.ops.vidioc_g_ctrl = vidioc_g_ctrl;
++ vv_data.ops.vidioc_s_ctrl = vidioc_s_ctrl;
++ vv_data.ops.vidioc_enum_input = vidioc_enum_input;
++ vv_data.ops.vidioc_g_input = vidioc_g_input;
++ vv_data.ops.vidioc_s_input = vidioc_s_input;
+ if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium gemini", VFL_TYPE_GRABBER)) {
+ printk("hexium_gemini: cannot register capture v4l2 device. skipping.\n");
+ return -1;
+@@ -306,153 +427,6 @@ static int hexium_detach(struct saa7146_dev *dev)
+ return 0;
+ }
+
+-static long hexium_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
+-{
+- struct saa7146_dev *dev = fh->dev;
+- struct hexium *hexium = (struct hexium *) dev->ext_priv;
+-/*
+- struct saa7146_vv *vv = dev->vv_data;
+-*/
+- switch (cmd) {
+- case VIDIOC_ENUMINPUT:
+- {
+- struct v4l2_input *i = arg;
+- DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
+-
+- if (i->index < 0 || i->index >= HEXIUM_INPUTS) {
+- return -EINVAL;
+- }
+-
+- memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
+-
+- DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index));
+- return 0;
+- }
+- case VIDIOC_G_INPUT:
+- {
+- int *input = (int *) arg;
+- *input = hexium->cur_input;
+-
+- DEB_D(("VIDIOC_G_INPUT: %d\n", *input));
+- return 0;
+- }
+- case VIDIOC_S_INPUT:
+- {
+- int input = *(int *) arg;
+-
+- DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
+-
+- if (input < 0 || input >= HEXIUM_INPUTS) {
+- return -EINVAL;
+- }
+-
+- hexium->cur_input = input;
+- hexium_set_input(hexium, input);
+-
+- return 0;
+- }
+- /* the saa7146 provides some controls (brightness, contrast, saturation)
+- which gets registered *after* this function. because of this we have
+- to return with a value != 0 even if the function succeded.. */
+- case VIDIOC_QUERYCTRL:
+- {
+- struct v4l2_queryctrl *qc = arg;
+- int i;
+-
+- for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
+- if (hexium_controls[i].id == qc->id) {
+- *qc = hexium_controls[i];
+- DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id));
+- return 0;
+- }
+- }
+- return -EAGAIN;
+- }
+- case VIDIOC_G_CTRL:
+- {
+- struct v4l2_control *vc = arg;
+- int i;
+-
+- for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
+- if (hexium_controls[i].id == vc->id) {
+- break;
+- }
+- }
+-
+- if (i < 0) {
+- return -EAGAIN;
+- }
+-
+- switch (vc->id) {
+- case V4L2_CID_PRIVATE_BASE:{
+- vc->value = hexium->cur_bw;
+- DEB_D(("VIDIOC_G_CTRL BW:%d.\n", vc->value));
+- return 0;
+- }
+- }
+- return -EINVAL;
+- }
+-
+- case VIDIOC_S_CTRL:
+- {
+- struct v4l2_control *vc = arg;
+- int i = 0;
+-
+- for (i = HEXIUM_CONTROLS - 1; i >= 0; i--) {
+- if (hexium_controls[i].id == vc->id) {
+- break;
+- }
+- }
+-
+- if (i < 0) {
+- return -EAGAIN;
+- }
+-
+- switch (vc->id) {
+- case V4L2_CID_PRIVATE_BASE:{
+- hexium->cur_bw = vc->value;
+- break;
+- }
+- }
+-
+- DEB_D(("VIDIOC_S_CTRL BW:%d.\n", hexium->cur_bw));
+-
+- if (0 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
+- hexium_set_standard(hexium, hexium_pal);
+- return 0;
+- }
+- if (0 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
+- hexium_set_standard(hexium, hexium_ntsc);
+- return 0;
+- }
+- if (0 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) {
+- hexium_set_standard(hexium, hexium_secam);
+- return 0;
+- }
+- if (1 == hexium->cur_bw && V4L2_STD_PAL == hexium->cur_std) {
+- hexium_set_standard(hexium, hexium_pal_bw);
+- return 0;
+- }
+- if (1 == hexium->cur_bw && V4L2_STD_NTSC == hexium->cur_std) {
+- hexium_set_standard(hexium, hexium_ntsc_bw);
+- return 0;
+- }
+- if (1 == hexium->cur_bw && V4L2_STD_SECAM == hexium->cur_std) {
+- /* fixme: is there no bw secam mode? */
+- return -EINVAL;
+- }
+-
+- return -EINVAL;
+- }
+- default:
+-/*
+- DEB_D(("hexium_ioctl() does not handle this ioctl.\n"));
+-*/
+- return -ENOIOCTLCMD;
+- }
+- return 0;
+-}
+-
+ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
+ {
+ struct hexium *hexium = (struct hexium *) dev->ext_priv;
+@@ -514,8 +488,6 @@ static struct saa7146_ext_vv vv_data = {
+ .stds = &hexium_standards[0],
+ .num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard),
+ .std_callback = &std_callback,
+- .ioctls = &ioctls[0],
+- .ioctl = hexium_ioctl,
+ };
+
+ static struct saa7146_extension hexium_extension = {
+diff --git a/drivers/media/video/hexium_orion.c b/drivers/media/video/hexium_orion.c
+index 074bec7..2bc39f6 100644
+--- a/drivers/media/video/hexium_orion.c
++++ b/drivers/media/video/hexium_orion.c
+@@ -57,14 +57,6 @@ struct hexium_data
+ u8 byte;
+ };
+
+-static struct saa7146_extension_ioctls ioctls[] = {
+- { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE },
+- { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE },
+- { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE },
+- { VIDIOC_S_STD, SAA7146_AFTER },
+- { 0, 0 }
+-};
+-
+ struct hexium
+ {
+ int type;
+@@ -329,6 +321,44 @@ static int hexium_set_input(struct hexium *hexium, int input)
+ return 0;
+ }
+
++static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
++{
++ DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
++
++ if (i->index < 0 || i->index >= HEXIUM_INPUTS)
++ return -EINVAL;
++
++ memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
++
++ DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index));
++ return 0;
++}
++
++static int vidioc_g_input(struct file *file, void *fh, unsigned int *input)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct hexium *hexium = (struct hexium *) dev->ext_priv;
++
++ *input = hexium->cur_input;
++
++ DEB_D(("VIDIOC_G_INPUT: %d\n", *input));
++ return 0;
++}
++
++static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct hexium *hexium = (struct hexium *) dev->ext_priv;
++
++ if (input < 0 || input >= HEXIUM_INPUTS)
++ return -EINVAL;
++
++ hexium->cur_input = input;
++ hexium_set_input(hexium, input);
++
++ return 0;
++}
++
+ static struct saa7146_ext_vv vv_data;
+
+ /* this function only gets called when the probing was successful */
+@@ -339,6 +369,9 @@ static int hexium_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_d
+ DEB_EE((".\n"));
+
+ saa7146_vv_init(dev, &vv_data);
++ vv_data.ops.vidioc_enum_input = vidioc_enum_input;
++ vv_data.ops.vidioc_g_input = vidioc_g_input;
++ vv_data.ops.vidioc_s_input = vidioc_s_input;
+ if (0 != saa7146_register_device(&hexium->video_dev, dev, "hexium orion", VFL_TYPE_GRABBER)) {
+ printk("hexium_orion: cannot register capture v4l2 device. skipping.\n");
+ return -1;
+@@ -370,58 +403,6 @@ static int hexium_detach(struct saa7146_dev *dev)
+ return 0;
+ }
+
+-static long hexium_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
+-{
+- struct saa7146_dev *dev = fh->dev;
+- struct hexium *hexium = (struct hexium *) dev->ext_priv;
+-/*
+- struct saa7146_vv *vv = dev->vv_data;
+-*/
+- switch (cmd) {
+- case VIDIOC_ENUMINPUT:
+- {
+- struct v4l2_input *i = arg;
+- DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
+-
+- if (i->index < 0 || i->index >= HEXIUM_INPUTS) {
+- return -EINVAL;
+- }
+-
+- memcpy(i, &hexium_inputs[i->index], sizeof(struct v4l2_input));
+-
+- DEB_D(("v4l2_ioctl: VIDIOC_ENUMINPUT %d.\n", i->index));
+- return 0;
+- }
+- case VIDIOC_G_INPUT:
+- {
+- int *input = (int *) arg;
+- *input = hexium->cur_input;
+-
+- DEB_D(("VIDIOC_G_INPUT: %d\n", *input));
+- return 0;
+- }
+- case VIDIOC_S_INPUT:
+- {
+- int input = *(int *) arg;
+-
+- if (input < 0 || input >= HEXIUM_INPUTS) {
+- return -EINVAL;
+- }
+-
+- hexium->cur_input = input;
+- hexium_set_input(hexium, input);
+-
+- return 0;
+- }
+- default:
+-/*
+- DEB_D(("hexium_ioctl() does not handle this ioctl.\n"));
+-*/
+- return -ENOIOCTLCMD;
+- }
+- return 0;
+-}
+-
+ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *std)
+ {
+ return 0;
+@@ -479,8 +460,6 @@ static struct saa7146_ext_vv vv_data = {
+ .stds = &hexium_standards[0],
+ .num_stds = sizeof(hexium_standards) / sizeof(struct saa7146_standard),
+ .std_callback = &std_callback,
+- .ioctls = &ioctls[0],
+- .ioctl = hexium_ioctl,
+ };
+
+ static struct saa7146_extension extension = {
+diff --git a/drivers/media/video/indycam.c b/drivers/media/video/indycam.c
+index 84b9e4f..3d69401 100644
+--- a/drivers/media/video/indycam.c
++++ b/drivers/media/video/indycam.c
+@@ -19,10 +19,12 @@
+ #include <linux/mm.h>
+ #include <linux/slab.h>
+
+-#include <linux/videodev.h>
+ /* IndyCam decodes stream of photons into digital image representation ;-) */
+-#include <linux/video_decoder.h>
++#include <linux/videodev2.h>
+ #include <linux/i2c.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/v4l2-i2c-drv.h>
+
+ #include "indycam.h"
+
+@@ -33,6 +35,7 @@ MODULE_VERSION(INDYCAM_MODULE_VERSION);
+ MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
+ MODULE_LICENSE("GPL");
+
++
+ // #define INDYCAM_DEBUG
+
+ #ifdef INDYCAM_DEBUG
+@@ -44,11 +47,14 @@ MODULE_LICENSE("GPL");
+ #endif
+
+ struct indycam {
+- struct i2c_client *client;
++ struct v4l2_subdev sd;
+ u8 version;
+ };
+
+-static struct i2c_driver i2c_driver_indycam;
++static inline struct indycam *to_indycam(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct indycam, sd);
++}
+
+ static const u8 initseq[] = {
+ INDYCAM_CONTROL_AGCENA, /* INDYCAM_CONTROL */
+@@ -63,8 +69,9 @@ static const u8 initseq[] = {
+
+ /* IndyCam register handling */
+
+-static int indycam_read_reg(struct i2c_client *client, u8 reg, u8 *value)
++static int indycam_read_reg(struct v4l2_subdev *sd, u8 reg, u8 *value)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ if (reg == INDYCAM_REG_RESET) {
+@@ -87,12 +94,12 @@ static int indycam_read_reg(struct i2c_client *client, u8 reg, u8 *value)
+ return 0;
+ }
+
+-static int indycam_write_reg(struct i2c_client *client, u8 reg, u8 value)
++static int indycam_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int err;
+
+- if ((reg == INDYCAM_REG_BRIGHTNESS)
+- || (reg == INDYCAM_REG_VERSION)) {
++ if (reg == INDYCAM_REG_BRIGHTNESS || reg == INDYCAM_REG_VERSION) {
+ dprintk("indycam_write_reg(): "
+ "skipping read-only register %d\n", reg);
+ return 0;
+@@ -108,13 +115,13 @@ static int indycam_write_reg(struct i2c_client *client, u8 reg, u8 value)
+ return err;
+ }
+
+-static int indycam_write_block(struct i2c_client *client, u8 reg,
++static int indycam_write_block(struct v4l2_subdev *sd, u8 reg,
+ u8 length, u8 *data)
+ {
+ int i, err;
+
+ for (i = 0; i < length; i++) {
+- err = indycam_write_reg(client, reg + i, data[i]);
++ err = indycam_write_reg(sd, reg + i, data[i]);
+ if (err)
+ return err;
+ }
+@@ -125,79 +132,78 @@ static int indycam_write_block(struct i2c_client *client, u8 reg,
+ /* Helper functions */
+
+ #ifdef INDYCAM_DEBUG
+-static void indycam_regdump_debug(struct i2c_client *client)
++static void indycam_regdump_debug(struct v4l2_subdev *sd)
+ {
+ int i;
+ u8 val;
+
+ for (i = 0; i < 9; i++) {
+- indycam_read_reg(client, i, &val);
++ indycam_read_reg(sd, i, &val);
+ dprintk("Reg %d = 0x%02x\n", i, val);
+ }
+ }
+ #endif
+
+-static int indycam_get_control(struct i2c_client *client,
+- struct indycam_control *ctrl)
++static int indycam_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+ {
+- struct indycam *camera = i2c_get_clientdata(client);
++ struct indycam *camera = to_indycam(sd);
+ u8 reg;
+ int ret = 0;
+
+- switch (ctrl->type) {
+- case INDYCAM_CONTROL_AGC:
+- case INDYCAM_CONTROL_AWB:
+- ret = indycam_read_reg(client, INDYCAM_REG_CONTROL, &reg);
++ switch (ctrl->id) {
++ case V4L2_CID_AUTOGAIN:
++ case V4L2_CID_AUTO_WHITE_BALANCE:
++ ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, &reg);
+ if (ret)
+ return -EIO;
+- if (ctrl->type == INDYCAM_CONTROL_AGC)
++ if (ctrl->id == V4L2_CID_AUTOGAIN)
+ ctrl->value = (reg & INDYCAM_CONTROL_AGCENA)
+ ? 1 : 0;
+ else
+ ctrl->value = (reg & INDYCAM_CONTROL_AWBCTL)
+ ? 1 : 0;
+ break;
+- case INDYCAM_CONTROL_SHUTTER:
+- ret = indycam_read_reg(client, INDYCAM_REG_SHUTTER, &reg);
++ case V4L2_CID_EXPOSURE:
++ ret = indycam_read_reg(sd, INDYCAM_REG_SHUTTER, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = ((s32)reg == 0x00) ? 0xff : ((s32)reg - 1);
+ break;
+- case INDYCAM_CONTROL_GAIN:
+- ret = indycam_read_reg(client, INDYCAM_REG_GAIN, &reg);
++ case V4L2_CID_GAIN:
++ ret = indycam_read_reg(sd, INDYCAM_REG_GAIN, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = (s32)reg;
+ break;
+- case INDYCAM_CONTROL_RED_BALANCE:
+- ret = indycam_read_reg(client, INDYCAM_REG_RED_BALANCE, &reg);
++ case V4L2_CID_RED_BALANCE:
++ ret = indycam_read_reg(sd, INDYCAM_REG_RED_BALANCE, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = (s32)reg;
+ break;
+- case INDYCAM_CONTROL_BLUE_BALANCE:
+- ret = indycam_read_reg(client, INDYCAM_REG_BLUE_BALANCE, &reg);
++ case V4L2_CID_BLUE_BALANCE:
++ ret = indycam_read_reg(sd, INDYCAM_REG_BLUE_BALANCE, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = (s32)reg;
+ break;
+ case INDYCAM_CONTROL_RED_SATURATION:
+- ret = indycam_read_reg(client,
++ ret = indycam_read_reg(sd,
+ INDYCAM_REG_RED_SATURATION, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = (s32)reg;
+ break;
+ case INDYCAM_CONTROL_BLUE_SATURATION:
+- ret = indycam_read_reg(client,
++ ret = indycam_read_reg(sd,
+ INDYCAM_REG_BLUE_SATURATION, &reg);
+ if (ret)
+ return -EIO;
+ ctrl->value = (s32)reg;
+ break;
+- case INDYCAM_CONTROL_GAMMA:
++ case V4L2_CID_GAMMA:
+ if (camera->version == CAMERA_VERSION_MOOSE) {
+- ret = indycam_read_reg(client,
++ ret = indycam_read_reg(sd,
+ INDYCAM_REG_GAMMA, &reg);
+ if (ret)
+ return -EIO;
+@@ -213,21 +219,20 @@ static int indycam_get_control(struct i2c_client *client,
+ return ret;
+ }
+
+-static int indycam_set_control(struct i2c_client *client,
+- struct indycam_control *ctrl)
++static int indycam_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+ {
+- struct indycam *camera = i2c_get_clientdata(client);
++ struct indycam *camera = to_indycam(sd);
+ u8 reg;
+ int ret = 0;
+
+- switch (ctrl->type) {
+- case INDYCAM_CONTROL_AGC:
+- case INDYCAM_CONTROL_AWB:
+- ret = indycam_read_reg(client, INDYCAM_REG_CONTROL, &reg);
++ switch (ctrl->id) {
++ case V4L2_CID_AUTOGAIN:
++ case V4L2_CID_AUTO_WHITE_BALANCE:
++ ret = indycam_read_reg(sd, INDYCAM_REG_CONTROL, &reg);
+ if (ret)
+ break;
+
+- if (ctrl->type == INDYCAM_CONTROL_AGC) {
++ if (ctrl->id == V4L2_CID_AUTOGAIN) {
+ if (ctrl->value)
+ reg |= INDYCAM_CONTROL_AGCENA;
+ else
+@@ -239,34 +244,34 @@ static int indycam_set_control(struct i2c_client *client,
+ reg &= ~INDYCAM_CONTROL_AWBCTL;
+ }
+
+- ret = indycam_write_reg(client, INDYCAM_REG_CONTROL, reg);
++ ret = indycam_write_reg(sd, INDYCAM_REG_CONTROL, reg);
+ break;
+- case INDYCAM_CONTROL_SHUTTER:
++ case V4L2_CID_EXPOSURE:
+ reg = (ctrl->value == 0xff) ? 0x00 : (ctrl->value + 1);
+- ret = indycam_write_reg(client, INDYCAM_REG_SHUTTER, reg);
++ ret = indycam_write_reg(sd, INDYCAM_REG_SHUTTER, reg);
+ break;
+- case INDYCAM_CONTROL_GAIN:
+- ret = indycam_write_reg(client, INDYCAM_REG_GAIN, ctrl->value);
++ case V4L2_CID_GAIN:
++ ret = indycam_write_reg(sd, INDYCAM_REG_GAIN, ctrl->value);
+ break;
+- case INDYCAM_CONTROL_RED_BALANCE:
+- ret = indycam_write_reg(client, INDYCAM_REG_RED_BALANCE,
++ case V4L2_CID_RED_BALANCE:
++ ret = indycam_write_reg(sd, INDYCAM_REG_RED_BALANCE,
+ ctrl->value);
+ break;
+- case INDYCAM_CONTROL_BLUE_BALANCE:
+- ret = indycam_write_reg(client, INDYCAM_REG_BLUE_BALANCE,
++ case V4L2_CID_BLUE_BALANCE:
++ ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_BALANCE,
+ ctrl->value);
+ break;
+ case INDYCAM_CONTROL_RED_SATURATION:
+- ret = indycam_write_reg(client, INDYCAM_REG_RED_SATURATION,
++ ret = indycam_write_reg(sd, INDYCAM_REG_RED_SATURATION,
+ ctrl->value);
+ break;
+ case INDYCAM_CONTROL_BLUE_SATURATION:
+- ret = indycam_write_reg(client, INDYCAM_REG_BLUE_SATURATION,
++ ret = indycam_write_reg(sd, INDYCAM_REG_BLUE_SATURATION,
+ ctrl->value);
+ break;
+- case INDYCAM_CONTROL_GAMMA:
++ case V4L2_CID_GAMMA:
+ if (camera->version == CAMERA_VERSION_MOOSE) {
+- ret = indycam_write_reg(client, INDYCAM_REG_GAMMA,
++ ret = indycam_write_reg(sd, INDYCAM_REG_GAMMA,
+ ctrl->value);
+ }
+ break;
+@@ -279,192 +284,103 @@ static int indycam_set_control(struct i2c_client *client,
+
+ /* I2C-interface */
+
+-static int indycam_attach(struct i2c_adapter *adap, int addr, int kind)
++static int indycam_g_chip_ident(struct v4l2_subdev *sd,
++ struct v4l2_dbg_chip_ident *chip)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct indycam *camera = to_indycam(sd);
++
++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_INDYCAM,
++ camera->version);
++}
++
++/* ----------------------------------------------------------------------- */
++
++static const struct v4l2_subdev_core_ops indycam_core_ops = {
++ .g_chip_ident = indycam_g_chip_ident,
++ .g_ctrl = indycam_g_ctrl,
++ .s_ctrl = indycam_s_ctrl,
++};
++
++static const struct v4l2_subdev_ops indycam_ops = {
++ .core = &indycam_core_ops,
++};
++
++static int indycam_probe(struct i2c_client *client,
++ const struct i2c_device_id *id)
+ {
+ int err = 0;
+ struct indycam *camera;
+- struct i2c_client *client;
++ struct v4l2_subdev *sd;
+
+- printk(KERN_INFO "SGI IndyCam driver version %s\n",
+- INDYCAM_MODULE_VERSION);
++ v4l_info(client, "chip found @ 0x%x (%s)\n",
++ client->addr << 1, client->adapter->name);
+
+- client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
+- if (!client)
+- return -ENOMEM;
+ camera = kzalloc(sizeof(struct indycam), GFP_KERNEL);
+- if (!camera) {
+- err = -ENOMEM;
+- goto out_free_client;
+- }
+-
+- client->addr = addr;
+- client->adapter = adap;
+- client->driver = &i2c_driver_indycam;
+- client->flags = 0;
+- strcpy(client->name, "IndyCam client");
+- i2c_set_clientdata(client, camera);
+-
+- camera->client = client;
++ if (!camera)
++ return -ENOMEM;
+
+- err = i2c_attach_client(client);
+- if (err)
+- goto out_free_camera;
++ sd = &camera->sd;
++ v4l2_i2c_subdev_init(sd, client, &indycam_ops);
+
+ camera->version = i2c_smbus_read_byte_data(client,
+ INDYCAM_REG_VERSION);
+ if (camera->version != CAMERA_VERSION_INDY &&
+ camera->version != CAMERA_VERSION_MOOSE) {
+- err = -ENODEV;
+- goto out_detach_client;
++ kfree(camera);
++ return -ENODEV;
+ }
++
+ printk(KERN_INFO "IndyCam v%d.%d detected\n",
+ INDYCAM_VERSION_MAJOR(camera->version),
+ INDYCAM_VERSION_MINOR(camera->version));
+
+- indycam_regdump(client);
++ indycam_regdump(sd);
+
+ // initialize
+- err = indycam_write_block(client, 0, sizeof(initseq), (u8 *)&initseq);
++ err = indycam_write_block(sd, 0, sizeof(initseq), (u8 *)&initseq);
+ if (err) {
+ printk(KERN_ERR "IndyCam initialization failed\n");
+- err = -EIO;
+- goto out_detach_client;
++ kfree(camera);
++ return -EIO;
+ }
+
+- indycam_regdump(client);
++ indycam_regdump(sd);
+
+ // white balance
+- err = indycam_write_reg(client, INDYCAM_REG_CONTROL,
++ err = indycam_write_reg(sd, INDYCAM_REG_CONTROL,
+ INDYCAM_CONTROL_AGCENA | INDYCAM_CONTROL_AWBCTL);
+ if (err) {
+ printk(KERN_ERR "IndyCam: White balancing camera failed\n");
+- err = -EIO;
+- goto out_detach_client;
++ kfree(camera);
++ return -EIO;
+ }
+
+- indycam_regdump(client);
++ indycam_regdump(sd);
+
+ printk(KERN_INFO "IndyCam initialized\n");
+
+ return 0;
+-
+-out_detach_client:
+- i2c_detach_client(client);
+-out_free_camera:
+- kfree(camera);
+-out_free_client:
+- kfree(client);
+- return err;
+ }
+
+-static int indycam_probe(struct i2c_adapter *adap)
++static int indycam_remove(struct i2c_client *client)
+ {
+- /* Indy specific crap */
+- if (adap->id == I2C_HW_SGI_VINO)
+- return indycam_attach(adap, INDYCAM_ADDR, 0);
+- /* Feel free to add probe here :-) */
+- return -ENODEV;
+-}
+-
+-static int indycam_detach(struct i2c_client *client)
+-{
+- struct indycam *camera = i2c_get_clientdata(client);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+- i2c_detach_client(client);
+- kfree(camera);
+- kfree(client);
++ v4l2_device_unregister_subdev(sd);
++ kfree(to_indycam(sd));
+ return 0;
+ }
+
+-static int indycam_command(struct i2c_client *client, unsigned int cmd,
+- void *arg)
+-{
+- // struct indycam *camera = i2c_get_clientdata(client);
+-
+- /* The old video_decoder interface just isn't enough,
+- * so we'll use some custom commands. */
+- switch (cmd) {
+- case DECODER_GET_CAPABILITIES: {
+- struct video_decoder_capability *cap = arg;
+-
+- cap->flags = VIDEO_DECODER_NTSC;
+- cap->inputs = 1;
+- cap->outputs = 1;
+- break;
+- }
+- case DECODER_GET_STATUS: {
+- int *iarg = arg;
+-
+- *iarg = DECODER_STATUS_GOOD | DECODER_STATUS_NTSC |
+- DECODER_STATUS_COLOR;
+- break;
+- }
+- case DECODER_SET_NORM: {
+- int *iarg = arg;
+-
+- switch (*iarg) {
+- case VIDEO_MODE_NTSC:
+- break;
+- default:
+- return -EINVAL;
+- }
+- break;
+- }
+- case DECODER_SET_INPUT: {
+- int *iarg = arg;
+-
+- if (*iarg != 0)
+- return -EINVAL;
+- break;
+- }
+- case DECODER_SET_OUTPUT: {
+- int *iarg = arg;
+-
+- if (*iarg != 0)
+- return -EINVAL;
+- break;
+- }
+- case DECODER_ENABLE_OUTPUT: {
+- /* Always enabled */
+- break;
+- }
+- case DECODER_SET_PICTURE: {
+- // struct video_picture *pic = arg;
+- /* TODO: convert values for indycam_set_controls() */
+- break;
+- }
+- case DECODER_INDYCAM_GET_CONTROL: {
+- return indycam_get_control(client, arg);
+- }
+- case DECODER_INDYCAM_SET_CONTROL: {
+- return indycam_set_control(client, arg);
+- }
+- default:
+- return -EINVAL;
+- }
+-
+- return 0;
+-}
+-
+-static struct i2c_driver i2c_driver_indycam = {
+- .driver = {
+- .name = "indycam",
+- },
+- .id = I2C_DRIVERID_INDYCAM,
+- .attach_adapter = indycam_probe,
+- .detach_client = indycam_detach,
+- .command = indycam_command,
++static const struct i2c_device_id indycam_id[] = {
++ { "indycam", 0 },
++ { }
+ };
++MODULE_DEVICE_TABLE(i2c, indycam_id);
+
+-static int __init indycam_init(void)
+-{
+- return i2c_add_driver(&i2c_driver_indycam);
+-}
+-
+-static void __exit indycam_exit(void)
+-{
+- i2c_del_driver(&i2c_driver_indycam);
+-}
+-
+-module_init(indycam_init);
+-module_exit(indycam_exit);
++static struct v4l2_i2c_driver_data v4l2_i2c_data = {
++ .name = "indycam",
++ .probe = indycam_probe,
++ .remove = indycam_remove,
++ .id_table = indycam_id,
++};
+diff --git a/drivers/media/video/indycam.h b/drivers/media/video/indycam.h
+index e6ee820..881f21c 100644
+--- a/drivers/media/video/indycam.h
++++ b/drivers/media/video/indycam.h
+@@ -87,22 +87,7 @@
+
+ /* Driver interface definitions */
+
+-#define INDYCAM_CONTROL_AGC 0 /* boolean */
+-#define INDYCAM_CONTROL_AWB 1 /* boolean */
+-#define INDYCAM_CONTROL_SHUTTER 2
+-#define INDYCAM_CONTROL_GAIN 3
+-#define INDYCAM_CONTROL_RED_BALANCE 4
+-#define INDYCAM_CONTROL_BLUE_BALANCE 5
+-#define INDYCAM_CONTROL_RED_SATURATION 6
+-#define INDYCAM_CONTROL_BLUE_SATURATION 7
+-#define INDYCAM_CONTROL_GAMMA 8
+-
+-struct indycam_control {
+- u8 type;
+- s32 value;
+-};
+-
+-#define DECODER_INDYCAM_GET_CONTROL _IOR('d', 193, struct indycam_control)
+-#define DECODER_INDYCAM_SET_CONTROL _IOW('d', 194, struct indycam_control)
++#define INDYCAM_CONTROL_RED_SATURATION (V4L2_CID_PRIVATE_BASE + 0)
++#define INDYCAM_CONTROL_BLUE_SATURATION (V4L2_CID_PRIVATE_BASE + 1)
+
+ #endif
+diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c
+index d4658c5..092c7da 100644
+--- a/drivers/media/video/ir-kbd-i2c.c
++++ b/drivers/media/video/ir-kbd-i2c.c
+@@ -16,6 +16,8 @@
+ * Henry Wong <henry@stuffedcow.net>
+ * Mark Schultz <n9xmj@yahoo.com>
+ * Brian Rogers <brian_rogers@comcast.net>
++ * modified for AVerMedia Cardbus by
++ * Oldrich Jedlicka <oldium.pro@seznam.cz>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -216,6 +218,46 @@ static int get_key_knc1(struct IR_i2c *ir, u32 *ir_key, u32 *ir_raw)
+ return 1;
+ }
+
++static int get_key_avermedia_cardbus(struct IR_i2c *ir,
++ u32 *ir_key, u32 *ir_raw)
++{
++ unsigned char subaddr, key, keygroup;
++ struct i2c_msg msg[] = { { .addr = ir->c.addr, .flags = 0,
++ .buf = &subaddr, .len = 1},
++ { .addr = ir->c.addr, .flags = I2C_M_RD,
++ .buf = &key, .len = 1} };
++ subaddr = 0x0d;
++ if (2 != i2c_transfer(ir->c.adapter, msg, 2)) {
++ dprintk(1, "read error\n");
++ return -EIO;
++ }
++
++ if (key == 0xff)
++ return 0;
++
++ subaddr = 0x0b;
++ msg[1].buf = &keygroup;
++ if (2 != i2c_transfer(ir->c.adapter, msg, 2)) {
++ dprintk(1, "read error\n");
++ return -EIO;
++ }
++
++ if (keygroup == 0xff)
++ return 0;
++
++ dprintk(1, "read key 0x%02x/0x%02x\n", key, keygroup);
++ if (keygroup < 2 || keygroup > 3) {
++ /* Only a warning */
++ dprintk(1, "warning: invalid key group 0x%02x for key 0x%02x\n",
++ keygroup, key);
++ }
++ key |= (keygroup & 1) << 6;
++
++ *ir_key = key;
++ *ir_raw = key;
++ return 1;
++}
++
+ /* ----------------------------------------------------------------------- */
+
+ static void ir_key_poll(struct IR_i2c *ir)
+@@ -237,15 +279,9 @@ static void ir_key_poll(struct IR_i2c *ir)
+ }
+ }
+
+-static void ir_timer(unsigned long data)
+-{
+- struct IR_i2c *ir = (struct IR_i2c*)data;
+- schedule_work(&ir->work);
+-}
+-
+ static void ir_work(struct work_struct *work)
+ {
+- struct IR_i2c *ir = container_of(work, struct IR_i2c, work);
++ struct IR_i2c *ir = container_of(work, struct IR_i2c, work.work);
+ int polling_interval = 100;
+
+ /* MSI TV@nywhere Plus requires more frequent polling
+@@ -254,7 +290,7 @@ static void ir_work(struct work_struct *work)
+ polling_interval = 50;
+
+ ir_key_poll(ir);
+- mod_timer(&ir->timer, jiffies + msecs_to_jiffies(polling_interval));
++ schedule_delayed_work(&ir->work, msecs_to_jiffies(polling_interval));
+ }
+
+ /* ----------------------------------------------------------------------- */
+@@ -360,6 +396,12 @@ static int ir_attach(struct i2c_adapter *adap, int addr,
+ ir_type = IR_TYPE_OTHER;
+ }
+ break;
++ case 0x40:
++ name = "AVerMedia Cardbus remote";
++ ir->get_key = get_key_avermedia_cardbus;
++ ir_type = IR_TYPE_OTHER;
++ ir_codes = ir_codes_avermedia_cardbus;
++ break;
+ default:
+ /* shouldn't happen */
+ printk(DEVNAME ": Huh? unknown i2c address (0x%02x)?\n", addr);
+@@ -404,11 +446,8 @@ static int ir_attach(struct i2c_adapter *adap, int addr,
+ ir->input->name, ir->input->phys, adap->name);
+
+ /* start polling via eventd */
+- INIT_WORK(&ir->work, ir_work);
+- init_timer(&ir->timer);
+- ir->timer.function = ir_timer;
+- ir->timer.data = (unsigned long)ir;
+- schedule_work(&ir->work);
++ INIT_DELAYED_WORK(&ir->work, ir_work);
++ schedule_delayed_work(&ir->work, 0);
+
+ return 0;
+
+@@ -425,8 +464,7 @@ static int ir_detach(struct i2c_client *client)
+ struct IR_i2c *ir = i2c_get_clientdata(client);
+
+ /* kill outstanding polls */
+- del_timer_sync(&ir->timer);
+- flush_scheduled_work();
++ cancel_delayed_work_sync(&ir->work);
+
+ /* unregister devices */
+ input_unregister_device(ir->input);
+@@ -524,6 +562,22 @@ static int ir_probe(struct i2c_adapter *adap)
+ ir_attach(adap, msg.addr, 0, 0);
+ }
+
++ /* Special case for AVerMedia Cardbus remote */
++ if (adap->id == I2C_HW_SAA7134) {
++ unsigned char subaddr, data;
++ struct i2c_msg msg[] = { { .addr = 0x40, .flags = 0,
++ .buf = &subaddr, .len = 1},
++ { .addr = 0x40, .flags = I2C_M_RD,
++ .buf = &data, .len = 1} };
++ subaddr = 0x0d;
++ rc = i2c_transfer(adap, msg, 2);
++ dprintk(1, "probe 0x%02x/0x%02x @ %s: %s\n",
++ msg[0].addr, subaddr, adap->name,
++ (2 == rc) ? "yes" : "no");
++ if (2 == rc)
++ ir_attach(adap, msg[0].addr, 0, 0);
++ }
++
+ return 0;
+ }
+
+diff --git a/drivers/media/video/ivtv/ivtv-controls.c b/drivers/media/video/ivtv/ivtv-controls.c
+index 62aa06f..84995bc 100644
+--- a/drivers/media/video/ivtv/ivtv-controls.c
++++ b/drivers/media/video/ivtv/ivtv-controls.c
+@@ -26,6 +26,7 @@
+ #include "ivtv-mailbox.h"
+ #include "ivtv-controls.h"
+
++/* Must be sorted from low to high control ID! */
+ static const u32 user_ctrls[] = {
+ V4L2_CID_USER_CLASS,
+ V4L2_CID_BRIGHTNESS,
+diff --git a/drivers/media/video/ivtv/ivtv-driver.c b/drivers/media/video/ivtv/ivtv-driver.c
+index c46c990..eca8bf9 100644
+--- a/drivers/media/video/ivtv/ivtv-driver.c
++++ b/drivers/media/video/ivtv/ivtv-driver.c
+@@ -357,7 +357,7 @@ void ivtv_read_eeprom(struct ivtv *itv, struct tveeprom *tv)
+ static void ivtv_process_eeprom(struct ivtv *itv)
+ {
+ struct tveeprom tv;
+- int pci_slot = PCI_SLOT(itv->dev->devfn);
++ int pci_slot = PCI_SLOT(itv->pdev->devfn);
+
+ ivtv_read_eeprom(itv, &tv);
+
+@@ -604,7 +604,7 @@ static void ivtv_process_options(struct ivtv *itv)
+ itv->std = ivtv_parse_std(itv);
+ if (itv->std == 0 && tunertype >= 0)
+ itv->std = tunertype ? V4L2_STD_MN : (V4L2_STD_ALL & ~V4L2_STD_MN);
+- itv->has_cx23415 = (itv->dev->device == PCI_DEVICE_ID_IVTV15);
++ itv->has_cx23415 = (itv->pdev->device == PCI_DEVICE_ID_IVTV15);
+ chipname = itv->has_cx23415 ? "cx23415" : "cx23416";
+ if (itv->options.cardtype == -1) {
+ IVTV_INFO("Ignore card (detected %s based chip)\n", chipname);
+@@ -617,9 +617,9 @@ static void ivtv_process_options(struct ivtv *itv)
+ IVTV_ERR("Unknown user specified type, trying to autodetect card\n");
+ }
+ if (itv->card == NULL) {
+- if (itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE ||
+- itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT1 ||
+- itv->dev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT2) {
++ if (itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE ||
++ itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT1 ||
++ itv->pdev->subsystem_vendor == IVTV_PCI_ID_HAUPPAUGE_ALT2) {
+ itv->card = ivtv_get_card(itv->has_cx23415 ? IVTV_CARD_PVR_350 : IVTV_CARD_PVR_150);
+ IVTV_INFO("Autodetected Hauppauge card (%s based)\n",
+ chipname);
+@@ -630,13 +630,13 @@ static void ivtv_process_options(struct ivtv *itv)
+ if (itv->card->pci_list == NULL)
+ continue;
+ for (j = 0; itv->card->pci_list[j].device; j++) {
+- if (itv->dev->device !=
++ if (itv->pdev->device !=
+ itv->card->pci_list[j].device)
+ continue;
+- if (itv->dev->subsystem_vendor !=
++ if (itv->pdev->subsystem_vendor !=
+ itv->card->pci_list[j].subsystem_vendor)
+ continue;
+- if (itv->dev->subsystem_device !=
++ if (itv->pdev->subsystem_device !=
+ itv->card->pci_list[j].subsystem_device)
+ continue;
+ IVTV_INFO("Autodetected %s card (%s based)\n",
+@@ -650,9 +650,9 @@ done:
+ if (itv->card == NULL) {
+ itv->card = ivtv_get_card(IVTV_CARD_PVR_150);
+ IVTV_ERR("Unknown card: vendor/device: [%04x:%04x]\n",
+- itv->dev->vendor, itv->dev->device);
++ itv->pdev->vendor, itv->pdev->device);
+ IVTV_ERR(" subsystem vendor/device: [%04x:%04x]\n",
+- itv->dev->subsystem_vendor, itv->dev->subsystem_device);
++ itv->pdev->subsystem_vendor, itv->pdev->subsystem_device);
+ IVTV_ERR(" %s based\n", chipname);
+ IVTV_ERR("Defaulting to %s card\n", itv->card->name);
+ IVTV_ERR("Please mail the vendor/device and subsystem vendor/device IDs and what kind of\n");
+@@ -671,7 +671,7 @@ done:
+ */
+ static int __devinit ivtv_init_struct1(struct ivtv *itv)
+ {
+- itv->base_addr = pci_resource_start(itv->dev, 0);
++ itv->base_addr = pci_resource_start(itv->pdev, 0);
+ itv->enc_mbox.max_mbox = 2; /* the encoder has 3 mailboxes (0-2) */
+ itv->dec_mbox.max_mbox = 1; /* the decoder has 2 mailboxes (0-1) */
+
+@@ -682,7 +682,7 @@ static int __devinit ivtv_init_struct1(struct ivtv *itv)
+ spin_lock_init(&itv->lock);
+ spin_lock_init(&itv->dma_reg_lock);
+
+- itv->irq_work_queues = create_singlethread_workqueue(itv->device.name);
++ itv->irq_work_queues = create_singlethread_workqueue(itv->v4l2_dev.name);
+ if (itv->irq_work_queues == NULL) {
+ IVTV_ERR("Could not create ivtv workqueue\n");
+ return -1;
+@@ -766,7 +766,7 @@ static void __devinit ivtv_init_struct2(struct ivtv *itv)
+ itv->audio_input = itv->card->video_inputs[i].audio_index;
+ }
+
+-static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
++static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *pdev,
+ const struct pci_device_id *pci_id)
+ {
+ u16 cmd;
+@@ -775,11 +775,11 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
+
+ IVTV_DEBUG_INFO("Enabling pci device\n");
+
+- if (pci_enable_device(dev)) {
++ if (pci_enable_device(pdev)) {
+ IVTV_ERR("Can't enable device!\n");
+ return -EIO;
+ }
+- if (pci_set_dma_mask(dev, 0xffffffff)) {
++ if (pci_set_dma_mask(pdev, 0xffffffff)) {
+ IVTV_ERR("No suitable DMA available.\n");
+ return -EIO;
+ }
+@@ -805,11 +805,11 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
+ }
+
+ /* Check for bus mastering */
+- pci_read_config_word(dev, PCI_COMMAND, &cmd);
++ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ if (!(cmd & PCI_COMMAND_MASTER)) {
+ IVTV_DEBUG_INFO("Attempting to enable Bus Mastering\n");
+- pci_set_master(dev);
+- pci_read_config_word(dev, PCI_COMMAND, &cmd);
++ pci_set_master(pdev);
++ pci_read_config_word(pdev, PCI_COMMAND, &cmd);
+ if (!(cmd & PCI_COMMAND_MASTER)) {
+ IVTV_ERR("Bus Mastering is not enabled\n");
+ return -ENXIO;
+@@ -817,26 +817,26 @@ static int ivtv_setup_pci(struct ivtv *itv, struct pci_dev *dev,
+ }
+ IVTV_DEBUG_INFO("Bus Mastering Enabled.\n");
+
+- pci_read_config_byte(dev, PCI_CLASS_REVISION, &card_rev);
+- pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
++ pci_read_config_byte(pdev, PCI_CLASS_REVISION, &card_rev);
++ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
+
+ if (pci_latency < 64 && ivtv_pci_latency) {
+ IVTV_INFO("Unreasonably low latency timer, "
+ "setting to 64 (was %d)\n", pci_latency);
+- pci_write_config_byte(dev, PCI_LATENCY_TIMER, 64);
+- pci_read_config_byte(dev, PCI_LATENCY_TIMER, &pci_latency);
++ pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 64);
++ pci_read_config_byte(pdev, PCI_LATENCY_TIMER, &pci_latency);
+ }
+ /* This config space value relates to DMA latencies. The
+ default value 0x8080 is too low however and will lead
+ to DMA errors. 0xffff is the max value which solves
+ these problems. */
+- pci_write_config_dword(dev, 0x40, 0xffff);
++ pci_write_config_dword(pdev, 0x40, 0xffff);
+
+ IVTV_DEBUG_INFO("%d (rev %d) at %02x:%02x.%x, "
+ "irq: %d, latency: %d, memory: 0x%lx\n",
+- itv->dev->device, card_rev, dev->bus->number,
+- PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn),
+- itv->dev->irq, pci_latency, (unsigned long)itv->base_addr);
++ pdev->device, card_rev, pdev->bus->number,
++ PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn),
++ pdev->irq, pci_latency, (unsigned long)itv->base_addr);
+
+ return 0;
+ }
+@@ -935,7 +935,7 @@ static void ivtv_load_and_init_modules(struct ivtv *itv)
+ }
+ }
+
+-static int __devinit ivtv_probe(struct pci_dev *dev,
++static int __devinit ivtv_probe(struct pci_dev *pdev,
+ const struct pci_device_id *pci_id)
+ {
+ int retval = 0;
+@@ -945,17 +945,17 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
+ itv = kzalloc(sizeof(struct ivtv), GFP_ATOMIC);
+ if (itv == NULL)
+ return -ENOMEM;
+- itv->dev = dev;
++ itv->pdev = pdev;
+ itv->instance = atomic_inc_return(&ivtv_instance) - 1;
+
+- retval = v4l2_device_register(&dev->dev, &itv->device);
++ retval = v4l2_device_register(&pdev->dev, &itv->v4l2_dev);
+ if (retval) {
+ kfree(itv);
+ return retval;
+ }
+ /* "ivtv + PCI ID" is a bit of a mouthful, so use
+ "ivtv + instance" instead. */
+- snprintf(itv->device.name, sizeof(itv->device.name),
++ snprintf(itv->v4l2_dev.name, sizeof(itv->v4l2_dev.name),
+ "ivtv%d", itv->instance);
+ IVTV_INFO("Initializing card %d\n", itv->instance);
+
+@@ -972,12 +972,11 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
+ IVTV_DEBUG_INFO("base addr: 0x%08x\n", itv->base_addr);
+
+ /* PCI Device Setup */
+- if ((retval = ivtv_setup_pci(itv, dev, pci_id)) != 0) {
+- if (retval == -EIO)
+- goto free_workqueue;
+- else if (retval == -ENXIO)
+- goto free_mem;
+- }
++ retval = ivtv_setup_pci(itv, pdev, pci_id);
++ if (retval == -EIO)
++ goto free_workqueue;
++ if (retval == -ENXIO)
++ goto free_mem;
+
+ /* map io memory */
+ IVTV_DEBUG_INFO("attempting ioremap at 0x%08x len 0x%08x\n",
+@@ -1154,8 +1153,8 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
+ ivtv_set_irq_mask(itv, 0xffffffff);
+
+ /* Register IRQ */
+- retval = request_irq(itv->dev->irq, ivtv_irq_handler,
+- IRQF_SHARED | IRQF_DISABLED, itv->device.name, (void *)itv);
++ retval = request_irq(itv->pdev->irq, ivtv_irq_handler,
++ IRQF_SHARED | IRQF_DISABLED, itv->v4l2_dev.name, (void *)itv);
+ if (retval) {
+ IVTV_ERR("Failed to register irq %d\n", retval);
+ goto free_i2c;
+@@ -1177,7 +1176,7 @@ static int __devinit ivtv_probe(struct pci_dev *dev,
+ free_streams:
+ ivtv_streams_cleanup(itv, 1);
+ free_irq:
+- free_irq(itv->dev->irq, (void *)itv);
++ free_irq(itv->pdev->irq, (void *)itv);
+ free_i2c:
+ exit_ivtv_i2c(itv);
+ free_io:
+@@ -1194,7 +1193,7 @@ err:
+ retval = -ENODEV;
+ IVTV_ERR("Error %d on initialization\n", retval);
+
+- v4l2_device_unregister(&itv->device);
++ v4l2_device_unregister(&itv->v4l2_dev);
+ kfree(itv);
+ return retval;
+ }
+@@ -1292,10 +1291,10 @@ int ivtv_init_on_first_open(struct ivtv *itv)
+ return 0;
+ }
+
+-static void ivtv_remove(struct pci_dev *pci_dev)
++static void ivtv_remove(struct pci_dev *pdev)
+ {
+- struct v4l2_device *dev = dev_get_drvdata(&pci_dev->dev);
+- struct ivtv *itv = to_ivtv(dev);
++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
++ struct ivtv *itv = to_ivtv(v4l2_dev);
+ int i;
+
+ IVTV_DEBUG_INFO("Removing card\n");
+@@ -1336,11 +1335,9 @@ static void ivtv_remove(struct pci_dev *pci_dev)
+ ivtv_streams_cleanup(itv, 1);
+ ivtv_udma_free(itv);
+
+- v4l2_device_unregister(&itv->device);
+-
+ exit_ivtv_i2c(itv);
+
+- free_irq(itv->dev->irq, (void *)itv);
++ free_irq(itv->pdev->irq, (void *)itv);
+ ivtv_iounmap(itv);
+
+ release_mem_region(itv->base_addr, IVTV_ENCODER_SIZE);
+@@ -1348,11 +1345,13 @@ static void ivtv_remove(struct pci_dev *pci_dev)
+ if (itv->has_cx23415)
+ release_mem_region(itv->base_addr + IVTV_DECODER_OFFSET, IVTV_DECODER_SIZE);
+
+- pci_disable_device(itv->dev);
++ pci_disable_device(itv->pdev);
+ for (i = 0; i < IVTV_VBI_FRAMES; i++)
+ kfree(itv->vbi.sliced_mpeg_data[i]);
+
+ printk(KERN_INFO "ivtv: Removed %s\n", itv->card_name);
++
++ v4l2_device_unregister(&itv->v4l2_dev);
+ kfree(itv);
+ }
+
+diff --git a/drivers/media/video/ivtv/ivtv-driver.h b/drivers/media/video/ivtv/ivtv-driver.h
+index ce8d9b7..440f732 100644
+--- a/drivers/media/video/ivtv/ivtv-driver.h
++++ b/drivers/media/video/ivtv/ivtv-driver.h
+@@ -133,7 +133,7 @@ extern int ivtv_debug;
+ #define IVTV_DEBUG(x, type, fmt, args...) \
+ do { \
+ if ((x) & ivtv_debug) \
+- v4l2_info(&itv->device, " " type ": " fmt , ##args); \
++ v4l2_info(&itv->v4l2_dev, " " type ": " fmt , ##args); \
+ } while (0)
+ #define IVTV_DEBUG_WARN(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_WARN, "warn", fmt , ## args)
+ #define IVTV_DEBUG_INFO(fmt, args...) IVTV_DEBUG(IVTV_DBGFLG_INFO, "info", fmt , ## args)
+@@ -149,7 +149,7 @@ extern int ivtv_debug;
+ #define IVTV_DEBUG_HIGH_VOL(x, type, fmt, args...) \
+ do { \
+ if (((x) & ivtv_debug) && (ivtv_debug & IVTV_DBGFLG_HIGHVOL)) \
+- v4l2_info(&itv->device, " " type ": " fmt , ##args); \
++ v4l2_info(&itv->v4l2_dev, " " type ": " fmt , ##args); \
+ } while (0)
+ #define IVTV_DEBUG_HI_WARN(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_WARN, "warn", fmt , ## args)
+ #define IVTV_DEBUG_HI_INFO(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_INFO, "info", fmt , ## args)
+@@ -163,9 +163,9 @@ extern int ivtv_debug;
+ #define IVTV_DEBUG_HI_YUV(fmt, args...) IVTV_DEBUG_HIGH_VOL(IVTV_DBGFLG_YUV, "yuv", fmt , ## args)
+
+ /* Standard kernel messages */
+-#define IVTV_ERR(fmt, args...) v4l2_err(&itv->device, fmt , ## args)
+-#define IVTV_WARN(fmt, args...) v4l2_warn(&itv->device, fmt , ## args)
+-#define IVTV_INFO(fmt, args...) v4l2_info(&itv->device, fmt , ## args)
++#define IVTV_ERR(fmt, args...) v4l2_err(&itv->v4l2_dev, fmt , ## args)
++#define IVTV_WARN(fmt, args...) v4l2_warn(&itv->v4l2_dev, fmt , ## args)
++#define IVTV_INFO(fmt, args...) v4l2_info(&itv->v4l2_dev, fmt , ## args)
+
+ /* output modes (cx23415 only) */
+ #define OUT_NONE 0
+@@ -315,7 +315,7 @@ struct ivtv; /* forward reference */
+ struct ivtv_stream {
+ /* These first four fields are always set, even if the stream
+ is not actually created. */
+- struct video_device *v4l2dev; /* NULL when stream not created */
++ struct video_device *vdev; /* NULL when stream not created */
+ struct ivtv *itv; /* for ease of use */
+ const char *name; /* name of the stream */
+ int type; /* stream type */
+@@ -592,7 +592,7 @@ struct ivtv_card;
+ /* Struct to hold info about ivtv cards */
+ struct ivtv {
+ /* General fixed card data */
+- struct pci_dev *dev; /* PCI device */
++ struct pci_dev *pdev; /* PCI device */
+ const struct ivtv_card *card; /* card information */
+ const char *card_name; /* full name of the card */
+ const struct ivtv_card_tuner_i2c *card_i2c; /* i2c addresses to probe for tuner */
+@@ -612,7 +612,7 @@ struct ivtv {
+ volatile void __iomem *reg_mem; /* pointer to mapped registers */
+ struct ivtv_options options; /* user options */
+
+- struct v4l2_device device;
++ struct v4l2_device v4l2_dev;
+ struct v4l2_subdev sd_gpio; /* GPIO sub-device */
+ u16 instance;
+
+@@ -696,7 +696,7 @@ struct ivtv {
+ u64 vbi_data_inserted; /* number of VBI bytes inserted into the MPEG stream */
+ u32 last_dec_timing[3]; /* cache last retrieved pts/scr/frame values */
+ unsigned long dualwatch_jiffies;/* jiffies value of the previous dualwatch check */
+- u16 dualwatch_stereo_mode; /* current detected dualwatch stereo mode */
++ u32 dualwatch_stereo_mode; /* current detected dualwatch stereo mode */
+
+
+ /* VBI state info */
+@@ -719,9 +719,9 @@ struct ivtv {
+ struct osd_info *osd_info; /* ivtvfb private OSD info */
+ };
+
+-static inline struct ivtv *to_ivtv(struct v4l2_device *dev)
++static inline struct ivtv *to_ivtv(struct v4l2_device *v4l2_dev)
+ {
+- return container_of(dev, struct ivtv, device);
++ return container_of(v4l2_dev, struct ivtv, v4l2_dev);
+ }
+
+ /* Globals */
+@@ -788,7 +788,7 @@ static inline int ivtv_raw_vbi(const struct ivtv *itv)
+ /* Call the specified callback for all subdevs matching hw (if 0, then
+ match them all). Ignore any errors. */
+ #define ivtv_call_hw(itv, hw, o, f, args...) \
+- __v4l2_device_call_subdevs(&(itv)->device, !(hw) || (sd->grp_id & (hw)), o, f , ##args)
++ __v4l2_device_call_subdevs(&(itv)->v4l2_dev, !(hw) || (sd->grp_id & (hw)), o, f , ##args)
+
+ #define ivtv_call_all(itv, o, f, args...) ivtv_call_hw(itv, 0, o, f , ##args)
+
+@@ -796,7 +796,7 @@ static inline int ivtv_raw_vbi(const struct ivtv *itv)
+ match them all). If the callback returns an error other than 0 or
+ -ENOIOCTLCMD, then return with that error code. */
+ #define ivtv_call_hw_err(itv, hw, o, f, args...) \
+- __v4l2_device_call_subdevs_until_err(&(itv)->device, !(hw) || (sd->grp_id & (hw)), o, f , ##args)
++ __v4l2_device_call_subdevs_until_err(&(itv)->v4l2_dev, !(hw) || (sd->grp_id & (hw)), o, f , ##args)
+
+ #define ivtv_call_all_err(itv, o, f, args...) ivtv_call_hw_err(itv, 0, o, f , ##args)
+
+diff --git a/drivers/media/video/ivtv/ivtv-fileops.c b/drivers/media/video/ivtv/ivtv-fileops.c
+index d594bc2..cfaacf6 100644
+--- a/drivers/media/video/ivtv/ivtv-fileops.c
++++ b/drivers/media/video/ivtv/ivtv-fileops.c
+@@ -148,10 +148,10 @@ void ivtv_release_stream(struct ivtv_stream *s)
+ static void ivtv_dualwatch(struct ivtv *itv)
+ {
+ struct v4l2_tuner vt;
+- u16 new_bitmap;
+- u16 new_stereo_mode;
+- const u16 stereo_mask = 0x0300;
+- const u16 dual = 0x0200;
++ u32 new_bitmap;
++ u32 new_stereo_mode;
++ const u32 stereo_mask = 0x0300;
++ const u32 dual = 0x0200;
+
+ new_stereo_mode = itv->params.audio_properties & stereo_mask;
+ memset(&vt, 0, sizeof(vt));
+@@ -991,7 +991,7 @@ int ivtv_v4l2_open(struct file *filp)
+ mutex_lock(&itv->serialize_lock);
+ if (ivtv_init_on_first_open(itv)) {
+ IVTV_ERR("Failed to initialize on minor %d\n",
+- s->v4l2dev->minor);
++ vdev->minor);
+ mutex_unlock(&itv->serialize_lock);
+ return -ENXIO;
+ }
+diff --git a/drivers/media/video/ivtv/ivtv-firmware.c b/drivers/media/video/ivtv/ivtv-firmware.c
+index 6dba55b..c1b7ec4 100644
+--- a/drivers/media/video/ivtv/ivtv-firmware.c
++++ b/drivers/media/video/ivtv/ivtv-firmware.c
+@@ -52,7 +52,7 @@ static int load_fw_direct(const char *fn, volatile u8 __iomem *mem, struct ivtv
+ int retries = 3;
+
+ retry:
+- if (retries && request_firmware(&fw, fn, &itv->dev->dev) == 0) {
++ if (retries && request_firmware(&fw, fn, &itv->pdev->dev) == 0) {
+ int i;
+ volatile u32 __iomem *dst = (volatile u32 __iomem *)mem;
+ const u32 *src = (const u32 *)fw->data;
+diff --git a/drivers/media/video/ivtv/ivtv-gpio.c b/drivers/media/video/ivtv/ivtv-gpio.c
+index dc2850e..3321983 100644
+--- a/drivers/media/video/ivtv/ivtv-gpio.c
++++ b/drivers/media/video/ivtv/ivtv-gpio.c
+@@ -384,7 +384,7 @@ int ivtv_gpio_init(struct ivtv *itv)
+ write_reg(itv->card->gpio_init.initial_value | pin, IVTV_REG_GPIO_OUT);
+ write_reg(itv->card->gpio_init.direction | pin, IVTV_REG_GPIO_DIR);
+ v4l2_subdev_init(&itv->sd_gpio, &subdev_ops);
+- snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->device.name);
++ snprintf(itv->sd_gpio.name, sizeof(itv->sd_gpio.name), "%s-gpio", itv->v4l2_dev.name);
+ itv->sd_gpio.grp_id = IVTV_HW_GPIO;
+- return v4l2_device_register_subdev(&itv->device, &itv->sd_gpio);
++ return v4l2_device_register_subdev(&itv->v4l2_dev, &itv->sd_gpio);
+ }
+diff --git a/drivers/media/video/ivtv/ivtv-i2c.c b/drivers/media/video/ivtv/ivtv-i2c.c
+index ca1d955..e73a196 100644
+--- a/drivers/media/video/ivtv/ivtv-i2c.c
++++ b/drivers/media/video/ivtv/ivtv-i2c.c
+@@ -194,14 +194,14 @@ struct v4l2_subdev *ivtv_find_hw(struct ivtv *itv, u32 hw)
+ struct v4l2_subdev *result = NULL;
+ struct v4l2_subdev *sd;
+
+- spin_lock(&itv->device.lock);
+- v4l2_device_for_each_subdev(sd, &itv->device) {
++ spin_lock(&itv->v4l2_dev.lock);
++ v4l2_device_for_each_subdev(sd, &itv->v4l2_dev) {
+ if (sd->grp_id == hw) {
+ result = sd;
+ break;
+ }
+ }
+- spin_unlock(&itv->device.lock);
++ spin_unlock(&itv->v4l2_dev.lock);
+ return result;
+ }
+
+@@ -472,8 +472,8 @@ static int ivtv_read(struct ivtv *itv, unsigned char addr, unsigned char *data,
+ intervening stop condition */
+ static int ivtv_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
+ {
+- struct v4l2_device *drv = i2c_get_adapdata(i2c_adap);
+- struct ivtv *itv = to_ivtv(drv);
++ struct v4l2_device *v4l2_dev = i2c_get_adapdata(i2c_adap);
++ struct ivtv *itv = to_ivtv(v4l2_dev);
+ int retval;
+ int i;
+
+@@ -604,12 +604,12 @@ int init_ivtv_i2c(struct ivtv *itv)
+
+ sprintf(itv->i2c_adap.name + strlen(itv->i2c_adap.name), " #%d",
+ itv->instance);
+- i2c_set_adapdata(&itv->i2c_adap, &itv->device);
++ i2c_set_adapdata(&itv->i2c_adap, &itv->v4l2_dev);
+
+ memcpy(&itv->i2c_client, &ivtv_i2c_client_template,
+ sizeof(struct i2c_client));
+ itv->i2c_client.adapter = &itv->i2c_adap;
+- itv->i2c_adap.dev.parent = &itv->dev->dev;
++ itv->i2c_adap.dev.parent = &itv->pdev->dev;
+
+ IVTV_DEBUG_I2C("setting scl and sda to 1\n");
+ ivtv_setscl(itv, 1);
+diff --git a/drivers/media/video/ivtv/ivtv-ioctl.c b/drivers/media/video/ivtv/ivtv-ioctl.c
+index c13bd2a..9a04242 100644
+--- a/drivers/media/video/ivtv/ivtv-ioctl.c
++++ b/drivers/media/video/ivtv/ivtv-ioctl.c
+@@ -345,10 +345,8 @@ static int ivtv_g_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *f
+ pixfmt->priv = 0;
+ if (id->type == IVTV_ENC_STREAM_TYPE_YUV) {
+ pixfmt->pixelformat = V4L2_PIX_FMT_HM12;
+- /* YUV size is (Y=(h*w) + UV=(h*(w/2))) */
+- pixfmt->sizeimage =
+- pixfmt->height * pixfmt->width +
+- pixfmt->height * (pixfmt->width / 2);
++ /* YUV size is (Y=(h*720) + UV=(h*(720/2))) */
++ pixfmt->sizeimage = pixfmt->height * 720 * 3 / 2;
+ pixfmt->bytesperline = 720;
+ } else {
+ pixfmt->pixelformat = V4L2_PIX_FMT_MPEG;
+@@ -469,11 +467,17 @@ static int ivtv_try_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format
+ struct ivtv *itv = id->itv;
+ int w = fmt->fmt.pix.width;
+ int h = fmt->fmt.pix.height;
++ int min_h = 2;
+
+ w = min(w, 720);
+ w = max(w, 2);
++ if (id->type == IVTV_ENC_STREAM_TYPE_YUV) {
++ /* YUV height must be a multiple of 32 */
++ h &= ~0x1f;
++ min_h = 32;
++ }
+ h = min(h, itv->is_50hz ? 576 : 480);
+- h = max(h, 2);
++ h = max(h, min_h);
+ ivtv_g_fmt_vid_cap(file, fh, fmt);
+ fmt->fmt.pix.width = w;
+ fmt->fmt.pix.height = h;
+@@ -766,7 +770,7 @@ static int ivtv_querycap(struct file *file, void *fh, struct v4l2_capability *vc
+
+ strlcpy(vcap->driver, IVTV_DRIVER_NAME, sizeof(vcap->driver));
+ strlcpy(vcap->card, itv->card_name, sizeof(vcap->card));
+- snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(itv->dev));
++ snprintf(vcap->bus_info, sizeof(vcap->bus_info), "PCI:%s", pci_name(itv->pdev));
+ vcap->version = IVTV_DRIVER_VERSION; /* version */
+ vcap->capabilities = itv->v4l2_cap; /* capabilities */
+ return 0;
+@@ -1513,12 +1517,12 @@ static int ivtv_log_status(struct file *file, void *fh)
+ }
+ IVTV_INFO("Tuner: %s\n",
+ test_bit(IVTV_F_I_RADIO_USER, &itv->i_flags) ? "Radio" : "TV");
+- cx2341x_log_status(&itv->params, itv->device.name);
++ cx2341x_log_status(&itv->params, itv->v4l2_dev.name);
+ IVTV_INFO("Status flags: 0x%08lx\n", itv->i_flags);
+ for (i = 0; i < IVTV_MAX_STREAMS; i++) {
+ struct ivtv_stream *s = &itv->streams[i];
+
+- if (s->v4l2dev == NULL || s->buffers == 0)
++ if (s->vdev == NULL || s->buffers == 0)
+ continue;
+ IVTV_INFO("Stream %s: status 0x%04lx, %d%% of %d KiB (%d buffers) in use\n", s->name, s->s_flags,
+ (s->buffers - s->q_free.buffers) * 100 / s->buffers,
+diff --git a/drivers/media/video/ivtv/ivtv-irq.c b/drivers/media/video/ivtv/ivtv-irq.c
+index f5d00ec..01c14d2 100644
+--- a/drivers/media/video/ivtv/ivtv-irq.c
++++ b/drivers/media/video/ivtv/ivtv-irq.c
+@@ -46,7 +46,7 @@ static void ivtv_pio_work_handler(struct ivtv *itv)
+
+ IVTV_DEBUG_HI_DMA("ivtv_pio_work_handler\n");
+ if (itv->cur_pio_stream < 0 || itv->cur_pio_stream >= IVTV_MAX_STREAMS ||
+- s->v4l2dev == NULL || !ivtv_use_pio(s)) {
++ s->vdev == NULL || !ivtv_use_pio(s)) {
+ itv->cur_pio_stream = -1;
+ /* trigger PIO complete user interrupt */
+ write_reg(IVTV_IRQ_ENC_PIO_COMPLETE, 0x44);
+@@ -109,7 +109,7 @@ static int stream_enc_dma_append(struct ivtv_stream *s, u32 data[CX2341X_MBOX_MA
+ int rc;
+
+ /* sanity checks */
+- if (s->v4l2dev == NULL) {
++ if (s->vdev == NULL) {
+ IVTV_DEBUG_WARN("Stream %s not started\n", s->name);
+ return -1;
+ }
+diff --git a/drivers/media/video/ivtv/ivtv-queue.c b/drivers/media/video/ivtv/ivtv-queue.c
+index 71bd13e..ff7b7de 100644
+--- a/drivers/media/video/ivtv/ivtv-queue.c
++++ b/drivers/media/video/ivtv/ivtv-queue.c
+@@ -230,7 +230,7 @@ int ivtv_stream_alloc(struct ivtv_stream *s)
+ return -ENOMEM;
+ }
+ if (ivtv_might_use_dma(s)) {
+- s->sg_handle = pci_map_single(itv->dev, s->sg_dma, sizeof(struct ivtv_sg_element), s->dma);
++ s->sg_handle = pci_map_single(itv->pdev, s->sg_dma, sizeof(struct ivtv_sg_element), s->dma);
+ ivtv_stream_sync_for_cpu(s);
+ }
+
+@@ -248,7 +248,7 @@ int ivtv_stream_alloc(struct ivtv_stream *s)
+ }
+ INIT_LIST_HEAD(&buf->list);
+ if (ivtv_might_use_dma(s)) {
+- buf->dma_handle = pci_map_single(s->itv->dev,
++ buf->dma_handle = pci_map_single(s->itv->pdev,
+ buf->buf, s->buf_size + 256, s->dma);
+ ivtv_buf_sync_for_cpu(s, buf);
+ }
+@@ -271,7 +271,7 @@ void ivtv_stream_free(struct ivtv_stream *s)
+ /* empty q_free */
+ while ((buf = ivtv_dequeue(s, &s->q_free))) {
+ if (ivtv_might_use_dma(s))
+- pci_unmap_single(s->itv->dev, buf->dma_handle,
++ pci_unmap_single(s->itv->pdev, buf->dma_handle,
+ s->buf_size + 256, s->dma);
+ kfree(buf->buf);
+ kfree(buf);
+@@ -280,7 +280,7 @@ void ivtv_stream_free(struct ivtv_stream *s)
+ /* Free SG Array/Lists */
+ if (s->sg_dma != NULL) {
+ if (s->sg_handle != IVTV_DMA_UNMAPPED) {
+- pci_unmap_single(s->itv->dev, s->sg_handle,
++ pci_unmap_single(s->itv->pdev, s->sg_handle,
+ sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE);
+ s->sg_handle = IVTV_DMA_UNMAPPED;
+ }
+diff --git a/drivers/media/video/ivtv/ivtv-queue.h b/drivers/media/video/ivtv/ivtv-queue.h
+index 476556a..9123383 100644
+--- a/drivers/media/video/ivtv/ivtv-queue.h
++++ b/drivers/media/video/ivtv/ivtv-queue.h
+@@ -53,14 +53,14 @@ static inline int ivtv_use_dma(struct ivtv_stream *s)
+ static inline void ivtv_buf_sync_for_cpu(struct ivtv_stream *s, struct ivtv_buffer *buf)
+ {
+ if (ivtv_use_dma(s))
+- pci_dma_sync_single_for_cpu(s->itv->dev, buf->dma_handle,
++ pci_dma_sync_single_for_cpu(s->itv->pdev, buf->dma_handle,
+ s->buf_size + 256, s->dma);
+ }
+
+ static inline void ivtv_buf_sync_for_device(struct ivtv_stream *s, struct ivtv_buffer *buf)
+ {
+ if (ivtv_use_dma(s))
+- pci_dma_sync_single_for_device(s->itv->dev, buf->dma_handle,
++ pci_dma_sync_single_for_device(s->itv->pdev, buf->dma_handle,
+ s->buf_size + 256, s->dma);
+ }
+
+@@ -82,14 +82,14 @@ void ivtv_stream_free(struct ivtv_stream *s);
+ static inline void ivtv_stream_sync_for_cpu(struct ivtv_stream *s)
+ {
+ if (ivtv_use_dma(s))
+- pci_dma_sync_single_for_cpu(s->itv->dev, s->sg_handle,
++ pci_dma_sync_single_for_cpu(s->itv->pdev, s->sg_handle,
+ sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE);
+ }
+
+ static inline void ivtv_stream_sync_for_device(struct ivtv_stream *s)
+ {
+ if (ivtv_use_dma(s))
+- pci_dma_sync_single_for_device(s->itv->dev, s->sg_handle,
++ pci_dma_sync_single_for_device(s->itv->pdev, s->sg_handle,
+ sizeof(struct ivtv_sg_element), PCI_DMA_TODEVICE);
+ }
+
+diff --git a/drivers/media/video/ivtv/ivtv-streams.c b/drivers/media/video/ivtv/ivtv-streams.c
+index 854a950..15da017 100644
+--- a/drivers/media/video/ivtv/ivtv-streams.c
++++ b/drivers/media/video/ivtv/ivtv-streams.c
+@@ -137,11 +137,11 @@ static struct {
+ static void ivtv_stream_init(struct ivtv *itv, int type)
+ {
+ struct ivtv_stream *s = &itv->streams[type];
+- struct video_device *dev = s->v4l2dev;
++ struct video_device *vdev = s->vdev;
+
+- /* we need to keep v4l2dev, so restore it afterwards */
++ /* we need to keep vdev, so restore it afterwards */
+ memset(s, 0, sizeof(*s));
+- s->v4l2dev = dev;
++ s->vdev = vdev;
+
+ /* initialize ivtv_stream fields */
+ s->itv = itv;
+@@ -172,10 +172,10 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
+ int num_offset = ivtv_stream_info[type].num_offset;
+ int num = itv->instance + ivtv_first_minor + num_offset;
+
+- /* These four fields are always initialized. If v4l2dev == NULL, then
++ /* These four fields are always initialized. If vdev == NULL, then
+ this stream is not in use. In that case no other fields but these
+ four can be used. */
+- s->v4l2dev = NULL;
++ s->vdev = NULL;
+ s->itv = itv;
+ s->type = type;
+ s->name = ivtv_stream_info[type].name;
+@@ -197,21 +197,21 @@ static int ivtv_prep_dev(struct ivtv *itv, int type)
+ ivtv_stream_init(itv, type);
+
+ /* allocate and initialize the v4l2 video device structure */
+- s->v4l2dev = video_device_alloc();
+- if (s->v4l2dev == NULL) {
++ s->vdev = video_device_alloc();
++ if (s->vdev == NULL) {
+ IVTV_ERR("Couldn't allocate v4l2 video_device for %s\n", s->name);
+ return -ENOMEM;
+ }
+
+- snprintf(s->v4l2dev->name, sizeof(s->v4l2dev->name), "%s %s",
+- itv->device.name, s->name);
++ snprintf(s->vdev->name, sizeof(s->vdev->name), "%s %s",
++ itv->v4l2_dev.name, s->name);
+
+- s->v4l2dev->num = num;
+- s->v4l2dev->v4l2_dev = &itv->device;
+- s->v4l2dev->fops = ivtv_stream_info[type].fops;
+- s->v4l2dev->release = video_device_release;
+- s->v4l2dev->tvnorms = V4L2_STD_ALL;
+- ivtv_set_funcs(s->v4l2dev);
++ s->vdev->num = num;
++ s->vdev->v4l2_dev = &itv->v4l2_dev;
++ s->vdev->fops = ivtv_stream_info[type].fops;
++ s->vdev->release = video_device_release;
++ s->vdev->tvnorms = V4L2_STD_ALL;
++ ivtv_set_funcs(s->vdev);
+ return 0;
+ }
+
+@@ -226,7 +226,7 @@ int ivtv_streams_setup(struct ivtv *itv)
+ if (ivtv_prep_dev(itv, type))
+ break;
+
+- if (itv->streams[type].v4l2dev == NULL)
++ if (itv->streams[type].vdev == NULL)
+ continue;
+
+ /* Allocate Stream */
+@@ -247,28 +247,28 @@ static int ivtv_reg_dev(struct ivtv *itv, int type)
+ int vfl_type = ivtv_stream_info[type].vfl_type;
+ int num;
+
+- if (s->v4l2dev == NULL)
++ if (s->vdev == NULL)
+ return 0;
+
+- num = s->v4l2dev->num;
++ num = s->vdev->num;
+ /* card number + user defined offset + device offset */
+ if (type != IVTV_ENC_STREAM_TYPE_MPG) {
+ struct ivtv_stream *s_mpg = &itv->streams[IVTV_ENC_STREAM_TYPE_MPG];
+
+- if (s_mpg->v4l2dev)
+- num = s_mpg->v4l2dev->num + ivtv_stream_info[type].num_offset;
++ if (s_mpg->vdev)
++ num = s_mpg->vdev->num + ivtv_stream_info[type].num_offset;
+ }
+- video_set_drvdata(s->v4l2dev, s);
++ video_set_drvdata(s->vdev, s);
+
+ /* Register device. First try the desired minor, then any free one. */
+- if (video_register_device(s->v4l2dev, vfl_type, num)) {
++ if (video_register_device(s->vdev, vfl_type, num)) {
+ IVTV_ERR("Couldn't register v4l2 device for %s kernel number %d\n",
+ s->name, num);
+- video_device_release(s->v4l2dev);
+- s->v4l2dev = NULL;
++ video_device_release(s->vdev);
++ s->vdev = NULL;
+ return -ENOMEM;
+ }
+- num = s->v4l2dev->num;
++ num = s->vdev->num;
+
+ switch (vfl_type) {
+ case VFL_TYPE_GRABBER:
+@@ -316,9 +316,9 @@ void ivtv_streams_cleanup(struct ivtv *itv, int unregister)
+
+ /* Teardown all streams */
+ for (type = 0; type < IVTV_MAX_STREAMS; type++) {
+- struct video_device *vdev = itv->streams[type].v4l2dev;
++ struct video_device *vdev = itv->streams[type].vdev;
+
+- itv->streams[type].v4l2dev = NULL;
++ itv->streams[type].vdev = NULL;
+ if (vdev == NULL)
+ continue;
+
+@@ -449,7 +449,7 @@ int ivtv_start_v4l2_encode_stream(struct ivtv_stream *s)
+ int captype = 0, subtype = 0;
+ int enable_passthrough = 0;
+
+- if (s->v4l2dev == NULL)
++ if (s->vdev == NULL)
+ return -EINVAL;
+
+ IVTV_DEBUG_INFO("Start encoder stream %s\n", s->name);
+@@ -611,7 +611,7 @@ static int ivtv_setup_v4l2_decode_stream(struct ivtv_stream *s)
+ struct cx2341x_mpeg_params *p = &itv->params;
+ int datatype;
+
+- if (s->v4l2dev == NULL)
++ if (s->vdev == NULL)
+ return -EINVAL;
+
+ IVTV_DEBUG_INFO("Setting some initial decoder settings\n");
+@@ -657,7 +657,7 @@ int ivtv_start_v4l2_decode_stream(struct ivtv_stream *s, int gop_offset)
+ {
+ struct ivtv *itv = s->itv;
+
+- if (s->v4l2dev == NULL)
++ if (s->vdev == NULL)
+ return -EINVAL;
+
+ if (test_and_set_bit(IVTV_F_S_STREAMING, &s->s_flags))
+@@ -705,7 +705,7 @@ void ivtv_stop_all_captures(struct ivtv *itv)
+ for (i = IVTV_MAX_STREAMS - 1; i >= 0; i--) {
+ struct ivtv_stream *s = &itv->streams[i];
+
+- if (s->v4l2dev == NULL)
++ if (s->vdev == NULL)
+ continue;
+ if (test_bit(IVTV_F_S_STREAMING, &s->s_flags)) {
+ ivtv_stop_v4l2_encode_stream(s, 0);
+@@ -720,7 +720,7 @@ int ivtv_stop_v4l2_encode_stream(struct ivtv_stream *s, int gop_end)
+ int cap_type;
+ int stopmode;
+
+- if (s->v4l2dev == NULL)
++ if (s->vdev == NULL)
+ return -EINVAL;
+
+ /* This function assumes that you are allowed to stop the capture
+@@ -834,7 +834,7 @@ int ivtv_stop_v4l2_decode_stream(struct ivtv_stream *s, int flags, u64 pts)
+ {
+ struct ivtv *itv = s->itv;
+
+- if (s->v4l2dev == NULL)
++ if (s->vdev == NULL)
+ return -EINVAL;
+
+ if (s->type != IVTV_DEC_STREAM_TYPE_YUV && s->type != IVTV_DEC_STREAM_TYPE_MPG)
+@@ -895,7 +895,7 @@ int ivtv_passthrough_mode(struct ivtv *itv, int enable)
+ struct ivtv_stream *yuv_stream = &itv->streams[IVTV_ENC_STREAM_TYPE_YUV];
+ struct ivtv_stream *dec_stream = &itv->streams[IVTV_DEC_STREAM_TYPE_YUV];
+
+- if (yuv_stream->v4l2dev == NULL || dec_stream->v4l2dev == NULL)
++ if (yuv_stream->vdev == NULL || dec_stream->vdev == NULL)
+ return -EINVAL;
+
+ IVTV_DEBUG_INFO("ivtv ioctl: Select passthrough mode\n");
+diff --git a/drivers/media/video/ivtv/ivtv-udma.c b/drivers/media/video/ivtv/ivtv-udma.c
+index 460db03..d07ad6c 100644
+--- a/drivers/media/video/ivtv/ivtv-udma.c
++++ b/drivers/media/video/ivtv/ivtv-udma.c
+@@ -93,7 +93,7 @@ void ivtv_udma_alloc(struct ivtv *itv)
+ {
+ if (itv->udma.SG_handle == 0) {
+ /* Map DMA Page Array Buffer */
+- itv->udma.SG_handle = pci_map_single(itv->dev, itv->udma.SGarray,
++ itv->udma.SG_handle = pci_map_single(itv->pdev, itv->udma.SGarray,
+ sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+ ivtv_udma_sync_for_cpu(itv);
+ }
+@@ -147,7 +147,7 @@ int ivtv_udma_setup(struct ivtv *itv, unsigned long ivtv_dest_addr,
+ }
+
+ /* Map SG List */
+- dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
++ dma->SG_length = pci_map_sg(itv->pdev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
+
+ /* Fill SG Array with new values */
+ ivtv_udma_fill_sg_array (dma, ivtv_dest_addr, 0, -1);
+@@ -172,7 +172,7 @@ void ivtv_udma_unmap(struct ivtv *itv)
+
+ /* Unmap Scatterlist */
+ if (dma->SG_length) {
+- pci_unmap_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
++ pci_unmap_sg(itv->pdev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
+ dma->SG_length = 0;
+ }
+ /* sync DMA */
+@@ -191,13 +191,13 @@ void ivtv_udma_free(struct ivtv *itv)
+
+ /* Unmap SG Array */
+ if (itv->udma.SG_handle) {
+- pci_unmap_single(itv->dev, itv->udma.SG_handle,
++ pci_unmap_single(itv->pdev, itv->udma.SG_handle,
+ sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+ }
+
+ /* Unmap Scatterlist */
+ if (itv->udma.SG_length) {
+- pci_unmap_sg(itv->dev, itv->udma.SGlist, itv->udma.page_count, PCI_DMA_TODEVICE);
++ pci_unmap_sg(itv->pdev, itv->udma.SGlist, itv->udma.page_count, PCI_DMA_TODEVICE);
+ }
+
+ for (i = 0; i < IVTV_DMA_SG_OSD_ENT; i++) {
+diff --git a/drivers/media/video/ivtv/ivtv-udma.h b/drivers/media/video/ivtv/ivtv-udma.h
+index df727e2..ee3c9ef 100644
+--- a/drivers/media/video/ivtv/ivtv-udma.h
++++ b/drivers/media/video/ivtv/ivtv-udma.h
+@@ -35,13 +35,13 @@ void ivtv_udma_start(struct ivtv *itv);
+
+ static inline void ivtv_udma_sync_for_device(struct ivtv *itv)
+ {
+- pci_dma_sync_single_for_device((struct pci_dev *)itv->dev, itv->udma.SG_handle,
++ pci_dma_sync_single_for_device(itv->pdev, itv->udma.SG_handle,
+ sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+ }
+
+ static inline void ivtv_udma_sync_for_cpu(struct ivtv *itv)
+ {
+- pci_dma_sync_single_for_cpu((struct pci_dev *)itv->dev, itv->udma.SG_handle,
++ pci_dma_sync_single_for_cpu(itv->pdev, itv->udma.SG_handle,
+ sizeof(itv->udma.SGarray), PCI_DMA_TODEVICE);
+ }
+
+diff --git a/drivers/media/video/ivtv/ivtv-vbi.c b/drivers/media/video/ivtv/ivtv-vbi.c
+index 5c5d1c4..f420d31 100644
+--- a/drivers/media/video/ivtv/ivtv-vbi.c
++++ b/drivers/media/video/ivtv/ivtv-vbi.c
+@@ -185,6 +185,8 @@ static void copy_vbi_data(struct ivtv *itv, int lines, u32 pts_stamp)
+ size = 4 + ((43 * line + 3) & ~3);
+ } else {
+ memcpy(dst + sd, "itv0", 4);
++ cpu_to_le32s(&linemask[0]);
++ cpu_to_le32s(&linemask[1]);
+ memcpy(dst + sd + 4, &linemask[0], 8);
+ size = 12 + ((43 * line + 3) & ~3);
+ }
+diff --git a/drivers/media/video/ivtv/ivtv-version.h b/drivers/media/video/ivtv/ivtv-version.h
+index 8cd753d..b530dec 100644
+--- a/drivers/media/video/ivtv/ivtv-version.h
++++ b/drivers/media/video/ivtv/ivtv-version.h
+@@ -23,7 +23,7 @@
+ #define IVTV_DRIVER_NAME "ivtv"
+ #define IVTV_DRIVER_VERSION_MAJOR 1
+ #define IVTV_DRIVER_VERSION_MINOR 4
+-#define IVTV_DRIVER_VERSION_PATCHLEVEL 0
++#define IVTV_DRIVER_VERSION_PATCHLEVEL 1
+
+ #define IVTV_VERSION __stringify(IVTV_DRIVER_VERSION_MAJOR) "." __stringify(IVTV_DRIVER_VERSION_MINOR) "." __stringify(IVTV_DRIVER_VERSION_PATCHLEVEL)
+ #define IVTV_DRIVER_VERSION KERNEL_VERSION(IVTV_DRIVER_VERSION_MAJOR,IVTV_DRIVER_VERSION_MINOR,IVTV_DRIVER_VERSION_PATCHLEVEL)
+diff --git a/drivers/media/video/ivtv/ivtv-yuv.c b/drivers/media/video/ivtv/ivtv-yuv.c
+index ee91107..7912ed6 100644
+--- a/drivers/media/video/ivtv/ivtv-yuv.c
++++ b/drivers/media/video/ivtv/ivtv-yuv.c
+@@ -103,7 +103,7 @@ static int ivtv_yuv_prep_user_dma(struct ivtv *itv, struct ivtv_user_dma *dma,
+ dma->page_count = 0;
+ return -ENOMEM;
+ }
+- dma->SG_length = pci_map_sg(itv->dev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
++ dma->SG_length = pci_map_sg(itv->pdev, dma->SGlist, dma->page_count, PCI_DMA_TODEVICE);
+
+ /* Fill SG Array with new values */
+ ivtv_udma_fill_sg_array(dma, y_buffer_offset, uv_buffer_offset, y_size);
+@@ -910,7 +910,7 @@ static void ivtv_yuv_init(struct ivtv *itv)
+ /* We need a buffer for blanking when Y plane is offset - non-fatal if we can't get one */
+ yi->blanking_ptr = kzalloc(720 * 16, GFP_KERNEL|__GFP_NOWARN);
+ if (yi->blanking_ptr) {
+- yi->blanking_dmaptr = pci_map_single(itv->dev, yi->blanking_ptr, 720*16, PCI_DMA_TODEVICE);
++ yi->blanking_dmaptr = pci_map_single(itv->pdev, yi->blanking_ptr, 720*16, PCI_DMA_TODEVICE);
+ } else {
+ yi->blanking_dmaptr = 0;
+ IVTV_DEBUG_WARN("Failed to allocate yuv blanking buffer\n");
+@@ -1237,7 +1237,7 @@ void ivtv_yuv_close(struct ivtv *itv)
+ if (yi->blanking_ptr) {
+ kfree(yi->blanking_ptr);
+ yi->blanking_ptr = NULL;
+- pci_unmap_single(itv->dev, yi->blanking_dmaptr, 720*16, PCI_DMA_TODEVICE);
++ pci_unmap_single(itv->pdev, yi->blanking_dmaptr, 720*16, PCI_DMA_TODEVICE);
+ }
+
+ /* Invalidate the old dimension information */
+diff --git a/drivers/media/video/ivtv/ivtvfb.c b/drivers/media/video/ivtv/ivtvfb.c
+index 36abd2a..66e6eb5 100644
+--- a/drivers/media/video/ivtv/ivtvfb.c
++++ b/drivers/media/video/ivtv/ivtvfb.c
+@@ -1192,12 +1192,12 @@ static int ivtvfb_init_card(struct ivtv *itv)
+ static int __init ivtvfb_callback_init(struct device *dev, void *p)
+ {
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
+- struct ivtv *itv = container_of(v4l2_dev, struct ivtv, device);
++ struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev);
+
+ if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
+ if (ivtvfb_init_card(itv) == 0) {
+ IVTVFB_INFO("Framebuffer registered on %s\n",
+- itv->device.name);
++ itv->v4l2_dev.name);
+ (*(int *)p)++;
+ }
+ }
+@@ -1207,7 +1207,7 @@ static int __init ivtvfb_callback_init(struct device *dev, void *p)
+ static int ivtvfb_callback_cleanup(struct device *dev, void *p)
+ {
+ struct v4l2_device *v4l2_dev = dev_get_drvdata(dev);
+- struct ivtv *itv = container_of(v4l2_dev, struct ivtv, device);
++ struct ivtv *itv = container_of(v4l2_dev, struct ivtv, v4l2_dev);
+
+ if (itv && (itv->v4l2_cap & V4L2_CAP_VIDEO_OUTPUT)) {
+ if (unregister_framebuffer(&itv->osd_info->ivtvfb_info)) {
+diff --git a/drivers/media/video/ks0127.c b/drivers/media/video/ks0127.c
+index bae2d2b..841024b 100644
+--- a/drivers/media/video/ks0127.c
++++ b/drivers/media/video/ks0127.c
+@@ -39,19 +39,20 @@
+ #include <linux/errno.h>
+ #include <linux/kernel.h>
+ #include <linux/i2c.h>
+-#include <linux/video_decoder.h>
+-#include <media/v4l2-common.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <linux/videodev2.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/v4l2-i2c-drv.h>
+ #include "ks0127.h"
+
+ MODULE_DESCRIPTION("KS0127 video decoder driver");
+ MODULE_AUTHOR("Ryan Drake");
+ MODULE_LICENSE("GPL");
+
+-#define KS_TYPE_UNKNOWN 0
+-#define KS_TYPE_0122S 1
+-#define KS_TYPE_0127 2
+-#define KS_TYPE_0127B 3
++/* Addresses */
++#define I2C_KS0127_ADDON 0xD8
++#define I2C_KS0127_ONBOARD 0xDA
++
+
+ /* ks0127 control registers */
+ #define KS_STAT 0x00
+@@ -197,15 +198,17 @@ struct adjust {
+ };
+
+ struct ks0127 {
+- int format_width;
+- int format_height;
+- int cap_width;
+- int cap_height;
+- int norm;
+- int ks_type;
++ struct v4l2_subdev sd;
++ v4l2_std_id norm;
++ int ident;
+ u8 regs[256];
+ };
+
++static inline struct ks0127 *to_ks0127(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct ks0127, sd);
++}
++
+
+ static int debug; /* insmod parameter */
+
+@@ -311,43 +314,45 @@ static void init_reg_defaults(void)
+ */
+
+
+-static u8 ks0127_read(struct i2c_client *c, u8 reg)
++static u8 ks0127_read(struct v4l2_subdev *sd, u8 reg)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ char val = 0;
+ struct i2c_msg msgs[] = {
+- { c->addr, 0, sizeof(reg), &reg },
+- { c->addr, I2C_M_RD | I2C_M_NO_RD_ACK, sizeof(val), &val }
++ { client->addr, 0, sizeof(reg), &reg },
++ { client->addr, I2C_M_RD | I2C_M_NO_RD_ACK, sizeof(val), &val }
+ };
+ int ret;
+
+- ret = i2c_transfer(c->adapter, msgs, ARRAY_SIZE(msgs));
++ ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+ if (ret != ARRAY_SIZE(msgs))
+- v4l_dbg(1, debug, c, "read error\n");
++ v4l2_dbg(1, debug, sd, "read error\n");
+
+ return val;
+ }
+
+
+-static void ks0127_write(struct i2c_client *c, u8 reg, u8 val)
++static void ks0127_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+ {
+- struct ks0127 *ks = i2c_get_clientdata(c);
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct ks0127 *ks = to_ks0127(sd);
+ char msg[] = { reg, val };
+
+- if (i2c_master_send(c, msg, sizeof(msg)) != sizeof(msg))
+- v4l_dbg(1, debug, c, "write error\n");
++ if (i2c_master_send(client, msg, sizeof(msg)) != sizeof(msg))
++ v4l2_dbg(1, debug, sd, "write error\n");
+
+ ks->regs[reg] = val;
+ }
+
+
+ /* generic bit-twiddling */
+-static void ks0127_and_or(struct i2c_client *client, u8 reg, u8 and_v, u8 or_v)
++static void ks0127_and_or(struct v4l2_subdev *sd, u8 reg, u8 and_v, u8 or_v)
+ {
+- struct ks0127 *ks = i2c_get_clientdata(client);
++ struct ks0127 *ks = to_ks0127(sd);
+
+ u8 val = ks->regs[reg];
+ val = (val & and_v) | or_v;
+- ks0127_write(client, reg, val);
++ ks0127_write(sd, reg, val);
+ }
+
+
+@@ -355,439 +360,363 @@ static void ks0127_and_or(struct i2c_client *client, u8 reg, u8 and_v, u8 or_v)
+ /****************************************************************************
+ * ks0127 private api
+ ****************************************************************************/
+-static void ks0127_reset(struct i2c_client *c)
++static void ks0127_init(struct v4l2_subdev *sd)
+ {
+- struct ks0127 *ks = i2c_get_clientdata(c);
++ struct ks0127 *ks = to_ks0127(sd);
+ u8 *table = reg_defaults;
+ int i;
+
+- ks->ks_type = KS_TYPE_UNKNOWN;
++ ks->ident = V4L2_IDENT_KS0127;
+
+- v4l_dbg(1, debug, c, "reset\n");
++ v4l2_dbg(1, debug, sd, "reset\n");
+ msleep(1);
+
+ /* initialize all registers to known values */
+ /* (except STAT, 0x21, 0x22, TEST and 0x38,0x39) */
+
+ for (i = 1; i < 33; i++)
+- ks0127_write(c, i, table[i]);
++ ks0127_write(sd, i, table[i]);
+
+ for (i = 35; i < 40; i++)
+- ks0127_write(c, i, table[i]);
++ ks0127_write(sd, i, table[i]);
+
+ for (i = 41; i < 56; i++)
+- ks0127_write(c, i, table[i]);
++ ks0127_write(sd, i, table[i]);
+
+ for (i = 58; i < 64; i++)
+- ks0127_write(c, i, table[i]);
++ ks0127_write(sd, i, table[i]);
+
+
+- if ((ks0127_read(c, KS_STAT) & 0x80) == 0) {
+- ks->ks_type = KS_TYPE_0122S;
+- v4l_dbg(1, debug, c, "ks0122s found\n");
++ if ((ks0127_read(sd, KS_STAT) & 0x80) == 0) {
++ ks->ident = V4L2_IDENT_KS0122S;
++ v4l2_dbg(1, debug, sd, "ks0122s found\n");
+ return;
+ }
+
+- switch (ks0127_read(c, KS_CMDE) & 0x0f) {
++ switch (ks0127_read(sd, KS_CMDE) & 0x0f) {
+ case 0:
+- ks->ks_type = KS_TYPE_0127;
+- v4l_dbg(1, debug, c, "ks0127 found\n");
++ v4l2_dbg(1, debug, sd, "ks0127 found\n");
+ break;
+
+ case 9:
+- ks->ks_type = KS_TYPE_0127B;
+- v4l_dbg(1, debug, c, "ks0127B Revision A found\n");
++ ks->ident = V4L2_IDENT_KS0127B;
++ v4l2_dbg(1, debug, sd, "ks0127B Revision A found\n");
+ break;
+
+ default:
+- v4l_dbg(1, debug, c, "unknown revision\n");
++ v4l2_dbg(1, debug, sd, "unknown revision\n");
+ break;
+ }
+ }
+
+-static int ks0127_command(struct i2c_client *c, unsigned cmd, void *arg)
++static int ks0127_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+ {
+- struct ks0127 *ks = i2c_get_clientdata(c);
+- int *iarg = (int *)arg;
+- int status;
+-
+- if (!ks)
+- return -ENODEV;
++ struct ks0127 *ks = to_ks0127(sd);
++
++ switch (route->input) {
++ case KS_INPUT_COMPOSITE_1:
++ case KS_INPUT_COMPOSITE_2:
++ case KS_INPUT_COMPOSITE_3:
++ case KS_INPUT_COMPOSITE_4:
++ case KS_INPUT_COMPOSITE_5:
++ case KS_INPUT_COMPOSITE_6:
++ v4l2_dbg(1, debug, sd,
++ "s_routing %d: Composite\n", route->input);
++ /* autodetect 50/60 Hz */
++ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00);
++ /* VSE=0 */
++ ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00);
++ /* set input line */
++ ks0127_and_or(sd, KS_CMDB, 0xb0, route->input);
++ /* non-freerunning mode */
++ ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a);
++ /* analog input */
++ ks0127_and_or(sd, KS_CMDD, 0x03, 0x00);
++ /* enable chroma demodulation */
++ ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00);
++ /* chroma trap, HYBWR=1 */
++ ks0127_and_or(sd, KS_LUMA, 0x00,
++ (reg_defaults[KS_LUMA])|0x0c);
++ /* scaler fullbw, luma comb off */
++ ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81);
++ /* manual chroma comb .25 .5 .25 */
++ ks0127_and_or(sd, KS_VERTIC, 0x0f, 0x90);
++
++ /* chroma path delay */
++ ks0127_and_or(sd, KS_CHROMB, 0x0f, 0x90);
++
++ ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]);
++ ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]);
++ ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]);
++ ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]);
++ break;
+
+- switch (cmd) {
+- case DECODER_INIT:
+- v4l_dbg(1, debug, c, "DECODER_INIT\n");
+- ks0127_reset(c);
++ case KS_INPUT_SVIDEO_1:
++ case KS_INPUT_SVIDEO_2:
++ case KS_INPUT_SVIDEO_3:
++ v4l2_dbg(1, debug, sd,
++ "s_routing %d: S-Video\n", route->input);
++ /* autodetect 50/60 Hz */
++ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x00);
++ /* VSE=0 */
++ ks0127_and_or(sd, KS_CMDA, ~0x40, 0x00);
++ /* set input line */
++ ks0127_and_or(sd, KS_CMDB, 0xb0, route->input);
++ /* non-freerunning mode */
++ ks0127_and_or(sd, KS_CMDC, 0x70, 0x0a);
++ /* analog input */
++ ks0127_and_or(sd, KS_CMDD, 0x03, 0x00);
++ /* enable chroma demodulation */
++ ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x00);
++ ks0127_and_or(sd, KS_LUMA, 0x00,
++ reg_defaults[KS_LUMA]);
++ /* disable luma comb */
++ ks0127_and_or(sd, KS_VERTIA, 0x08,
++ (reg_defaults[KS_VERTIA]&0xf0)|0x01);
++ ks0127_and_or(sd, KS_VERTIC, 0x0f,
++ reg_defaults[KS_VERTIC]&0xf0);
++
++ ks0127_and_or(sd, KS_CHROMB, 0x0f,
++ reg_defaults[KS_CHROMB]&0xf0);
++
++ ks0127_write(sd, KS_UGAIN, reg_defaults[KS_UGAIN]);
++ ks0127_write(sd, KS_VGAIN, reg_defaults[KS_VGAIN]);
++ ks0127_write(sd, KS_UVOFFH, reg_defaults[KS_UVOFFH]);
++ ks0127_write(sd, KS_UVOFFL, reg_defaults[KS_UVOFFL]);
+ break;
+
+- case DECODER_SET_INPUT:
+- switch(*iarg) {
+- case KS_INPUT_COMPOSITE_1:
+- case KS_INPUT_COMPOSITE_2:
+- case KS_INPUT_COMPOSITE_3:
+- case KS_INPUT_COMPOSITE_4:
+- case KS_INPUT_COMPOSITE_5:
+- case KS_INPUT_COMPOSITE_6:
+- v4l_dbg(1, debug, c,
+- "DECODER_SET_INPUT %d: Composite\n", *iarg);
+- /* autodetect 50/60 Hz */
+- ks0127_and_or(c, KS_CMDA, 0xfc, 0x00);
+- /* VSE=0 */
+- ks0127_and_or(c, KS_CMDA, ~0x40, 0x00);
+- /* set input line */
+- ks0127_and_or(c, KS_CMDB, 0xb0, *iarg);
+- /* non-freerunning mode */
+- ks0127_and_or(c, KS_CMDC, 0x70, 0x0a);
+- /* analog input */
+- ks0127_and_or(c, KS_CMDD, 0x03, 0x00);
+- /* enable chroma demodulation */
+- ks0127_and_or(c, KS_CTRACK, 0xcf, 0x00);
+- /* chroma trap, HYBWR=1 */
+- ks0127_and_or(c, KS_LUMA, 0x00,
+- (reg_defaults[KS_LUMA])|0x0c);
+- /* scaler fullbw, luma comb off */
+- ks0127_and_or(c, KS_VERTIA, 0x08, 0x81);
+- /* manual chroma comb .25 .5 .25 */
+- ks0127_and_or(c, KS_VERTIC, 0x0f, 0x90);
+-
+- /* chroma path delay */
+- ks0127_and_or(c, KS_CHROMB, 0x0f, 0x90);
+-
+- ks0127_write(c, KS_UGAIN, reg_defaults[KS_UGAIN]);
+- ks0127_write(c, KS_VGAIN, reg_defaults[KS_VGAIN]);
+- ks0127_write(c, KS_UVOFFH, reg_defaults[KS_UVOFFH]);
+- ks0127_write(c, KS_UVOFFL, reg_defaults[KS_UVOFFL]);
+- break;
+-
+- case KS_INPUT_SVIDEO_1:
+- case KS_INPUT_SVIDEO_2:
+- case KS_INPUT_SVIDEO_3:
+- v4l_dbg(1, debug, c,
+- "DECODER_SET_INPUT %d: S-Video\n", *iarg);
+- /* autodetect 50/60 Hz */
+- ks0127_and_or(c, KS_CMDA, 0xfc, 0x00);
+- /* VSE=0 */
+- ks0127_and_or(c, KS_CMDA, ~0x40, 0x00);
+- /* set input line */
+- ks0127_and_or(c, KS_CMDB, 0xb0, *iarg);
+- /* non-freerunning mode */
+- ks0127_and_or(c, KS_CMDC, 0x70, 0x0a);
+- /* analog input */
+- ks0127_and_or(c, KS_CMDD, 0x03, 0x00);
+- /* enable chroma demodulation */
+- ks0127_and_or(c, KS_CTRACK, 0xcf, 0x00);
+- ks0127_and_or(c, KS_LUMA, 0x00,
+- reg_defaults[KS_LUMA]);
+- /* disable luma comb */
+- ks0127_and_or(c, KS_VERTIA, 0x08,
+- (reg_defaults[KS_VERTIA]&0xf0)|0x01);
+- ks0127_and_or(c, KS_VERTIC, 0x0f,
+- reg_defaults[KS_VERTIC]&0xf0);
+-
+- ks0127_and_or(c, KS_CHROMB, 0x0f,
+- reg_defaults[KS_CHROMB]&0xf0);
+-
+- ks0127_write(c, KS_UGAIN, reg_defaults[KS_UGAIN]);
+- ks0127_write(c, KS_VGAIN, reg_defaults[KS_VGAIN]);
+- ks0127_write(c, KS_UVOFFH, reg_defaults[KS_UVOFFH]);
+- ks0127_write(c, KS_UVOFFL, reg_defaults[KS_UVOFFL]);
+- break;
+-
+- case KS_INPUT_YUV656:
+- v4l_dbg(1, debug, c,
+- "DECODER_SET_INPUT 15: YUV656\n");
+- if (ks->norm == VIDEO_MODE_NTSC ||
+- ks->norm == KS_STD_PAL_M)
+- /* force 60 Hz */
+- ks0127_and_or(c, KS_CMDA, 0xfc, 0x03);
+- else
+- /* force 50 Hz */
+- ks0127_and_or(c, KS_CMDA, 0xfc, 0x02);
+-
+- ks0127_and_or(c, KS_CMDA, 0xff, 0x40); /* VSE=1 */
+- /* set input line and VALIGN */
+- ks0127_and_or(c, KS_CMDB, 0xb0, (*iarg | 0x40));
+- /* freerunning mode, */
+- /* TSTGEN = 1 TSTGFR=11 TSTGPH=0 TSTGPK=0 VMEM=1*/
+- ks0127_and_or(c, KS_CMDC, 0x70, 0x87);
+- /* digital input, SYNDIR = 0 INPSL=01 CLKDIR=0 EAV=0 */
+- ks0127_and_or(c, KS_CMDD, 0x03, 0x08);
+- /* disable chroma demodulation */
+- ks0127_and_or(c, KS_CTRACK, 0xcf, 0x30);
+- /* HYPK =01 CTRAP = 0 HYBWR=0 PED=1 RGBH=1 UNIT=1 */
+- ks0127_and_or(c, KS_LUMA, 0x00, 0x71);
+- ks0127_and_or(c, KS_VERTIC, 0x0f,
+- reg_defaults[KS_VERTIC]&0xf0);
+-
+- /* scaler fullbw, luma comb off */
+- ks0127_and_or(c, KS_VERTIA, 0x08, 0x81);
+-
+- ks0127_and_or(c, KS_CHROMB, 0x0f,
+- reg_defaults[KS_CHROMB]&0xf0);
+-
+- ks0127_and_or(c, KS_CON, 0x00, 0x00);
+- ks0127_and_or(c, KS_BRT, 0x00, 32); /* spec: 34 */
+- /* spec: 229 (e5) */
+- ks0127_and_or(c, KS_SAT, 0x00, 0xe8);
+- ks0127_and_or(c, KS_HUE, 0x00, 0);
+-
+- ks0127_and_or(c, KS_UGAIN, 0x00, 238);
+- ks0127_and_or(c, KS_VGAIN, 0x00, 0x00);
+-
+- /*UOFF:0x30, VOFF:0x30, TSTCGN=1 */
+- ks0127_and_or(c, KS_UVOFFH, 0x00, 0x4f);
+- ks0127_and_or(c, KS_UVOFFL, 0x00, 0x00);
+- break;
+-
+- default:
+- v4l_dbg(1, debug, c,
+- "DECODER_SET_INPUT: Unknown input %d\n", *iarg);
+- break;
+- }
+-
+- /* hack: CDMLPF sometimes spontaneously switches on; */
+- /* force back off */
+- ks0127_write(c, KS_DEMOD, reg_defaults[KS_DEMOD]);
++ case KS_INPUT_YUV656:
++ v4l2_dbg(1, debug, sd, "s_routing 15: YUV656\n");
++ if (ks->norm & V4L2_STD_525_60)
++ /* force 60 Hz */
++ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x03);
++ else
++ /* force 50 Hz */
++ ks0127_and_or(sd, KS_CMDA, 0xfc, 0x02);
++
++ ks0127_and_or(sd, KS_CMDA, 0xff, 0x40); /* VSE=1 */
++ /* set input line and VALIGN */
++ ks0127_and_or(sd, KS_CMDB, 0xb0, (route->input | 0x40));
++ /* freerunning mode, */
++ /* TSTGEN = 1 TSTGFR=11 TSTGPH=0 TSTGPK=0 VMEM=1*/
++ ks0127_and_or(sd, KS_CMDC, 0x70, 0x87);
++ /* digital input, SYNDIR = 0 INPSL=01 CLKDIR=0 EAV=0 */
++ ks0127_and_or(sd, KS_CMDD, 0x03, 0x08);
++ /* disable chroma demodulation */
++ ks0127_and_or(sd, KS_CTRACK, 0xcf, 0x30);
++ /* HYPK =01 CTRAP = 0 HYBWR=0 PED=1 RGBH=1 UNIT=1 */
++ ks0127_and_or(sd, KS_LUMA, 0x00, 0x71);
++ ks0127_and_or(sd, KS_VERTIC, 0x0f,
++ reg_defaults[KS_VERTIC]&0xf0);
++
++ /* scaler fullbw, luma comb off */
++ ks0127_and_or(sd, KS_VERTIA, 0x08, 0x81);
++
++ ks0127_and_or(sd, KS_CHROMB, 0x0f,
++ reg_defaults[KS_CHROMB]&0xf0);
++
++ ks0127_and_or(sd, KS_CON, 0x00, 0x00);
++ ks0127_and_or(sd, KS_BRT, 0x00, 32); /* spec: 34 */
++ /* spec: 229 (e5) */
++ ks0127_and_or(sd, KS_SAT, 0x00, 0xe8);
++ ks0127_and_or(sd, KS_HUE, 0x00, 0);
++
++ ks0127_and_or(sd, KS_UGAIN, 0x00, 238);
++ ks0127_and_or(sd, KS_VGAIN, 0x00, 0x00);
++
++ /*UOFF:0x30, VOFF:0x30, TSTCGN=1 */
++ ks0127_and_or(sd, KS_UVOFFH, 0x00, 0x4f);
++ ks0127_and_or(sd, KS_UVOFFL, 0x00, 0x00);
+ break;
+
+- case DECODER_SET_OUTPUT:
+- switch(*iarg) {
+- case KS_OUTPUT_YUV656E:
+- v4l_dbg(1, debug, c,
+- "DECODER_SET_OUTPUT: OUTPUT_YUV656E (Missing)\n");
+- return -EINVAL;
+-
+- case KS_OUTPUT_EXV:
+- v4l_dbg(1, debug, c,
+- "DECODER_SET_OUTPUT: OUTPUT_EXV\n");
+- ks0127_and_or(c, KS_OFMTA, 0xf0, 0x09);
+- break;
+- }
++ default:
++ v4l2_dbg(1, debug, sd,
++ "s_routing: Unknown input %d\n", route->input);
+ break;
++ }
+
+- case DECODER_SET_NORM: /* sam This block mixes old and new norm names... */
+- /* Set to automatic SECAM/Fsc mode */
+- ks0127_and_or(c, KS_DEMOD, 0xf0, 0x00);
+-
+- ks->norm = *iarg;
+- switch (*iarg) {
+- /* this is untested !! */
+- /* It just detects PAL_N/NTSC_M (no special frequencies) */
+- /* And you have to set the standard a second time afterwards */
+- case VIDEO_MODE_AUTO:
+- v4l_dbg(1, debug, c,
+- "DECODER_SET_NORM: AUTO\n");
+-
+- /* The chip determines the format */
+- /* based on the current field rate */
+- ks0127_and_or(c, KS_CMDA, 0xfc, 0x00);
+- ks0127_and_or(c, KS_CHROMA, 0x9f, 0x20);
+- /* This is wrong for PAL ! As I said, */
+- /* you need to set the standard once again !! */
+- ks->format_height = 240;
+- ks->format_width = 704;
+- break;
+-
+- case VIDEO_MODE_NTSC:
+- v4l_dbg(1, debug, c,
+- "DECODER_SET_NORM: NTSC_M\n");
+- ks0127_and_or(c, KS_CHROMA, 0x9f, 0x20);
+- ks->format_height = 240;
+- ks->format_width = 704;
+- break;
+-
+- case KS_STD_NTSC_N:
+- v4l_dbg(1, debug, c,
+- "KS0127_SET_NORM: NTSC_N (fixme)\n");
+- ks0127_and_or(c, KS_CHROMA, 0x9f, 0x40);
+- ks->format_height = 240;
+- ks->format_width = 704;
+- break;
+-
+- case VIDEO_MODE_PAL:
+- v4l_dbg(1, debug, c,
+- "DECODER_SET_NORM: PAL_N\n");
+- ks0127_and_or(c, KS_CHROMA, 0x9f, 0x20);
+- ks->format_height = 290;
+- ks->format_width = 704;
+- break;
+-
+- case KS_STD_PAL_M:
+- v4l_dbg(1, debug, c,
+- "KS0127_SET_NORM: PAL_M (fixme)\n");
+- ks0127_and_or(c, KS_CHROMA, 0x9f, 0x40);
+- ks->format_height = 290;
+- ks->format_width = 704;
+- break;
+-
+- case VIDEO_MODE_SECAM:
+- v4l_dbg(1, debug, c,
+- "KS0127_SET_NORM: SECAM\n");
+- ks->format_height = 290;
+- ks->format_width = 704;
+-
+- /* set to secam autodetection */
+- ks0127_and_or(c, KS_CHROMA, 0xdf, 0x20);
+- ks0127_and_or(c, KS_DEMOD, 0xf0, 0x00);
+- schedule_timeout_interruptible(HZ/10+1);
+-
+- /* did it autodetect? */
+- if (ks0127_read(c, KS_DEMOD) & 0x40)
+- break;
++ /* hack: CDMLPF sometimes spontaneously switches on; */
++ /* force back off */
++ ks0127_write(sd, KS_DEMOD, reg_defaults[KS_DEMOD]);
++ return 0;
++}
+
++static int ks0127_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
++{
++ struct ks0127 *ks = to_ks0127(sd);
++
++ /* Set to automatic SECAM/Fsc mode */
++ ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00);
++
++ ks->norm = std;
++ if (std & V4L2_STD_NTSC) {
++ v4l2_dbg(1, debug, sd,
++ "s_std: NTSC_M\n");
++ ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20);
++ } else if (std & V4L2_STD_PAL_N) {
++ v4l2_dbg(1, debug, sd,
++ "s_std: NTSC_N (fixme)\n");
++ ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40);
++ } else if (std & V4L2_STD_PAL) {
++ v4l2_dbg(1, debug, sd,
++ "s_std: PAL_N\n");
++ ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x20);
++ } else if (std & V4L2_STD_PAL_M) {
++ v4l2_dbg(1, debug, sd,
++ "s_std: PAL_M (fixme)\n");
++ ks0127_and_or(sd, KS_CHROMA, 0x9f, 0x40);
++ } else if (std & V4L2_STD_SECAM) {
++ v4l2_dbg(1, debug, sd,
++ "s_std: SECAM\n");
++
++ /* set to secam autodetection */
++ ks0127_and_or(sd, KS_CHROMA, 0xdf, 0x20);
++ ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x00);
++ schedule_timeout_interruptible(HZ/10+1);
++
++ /* did it autodetect? */
++ if (!(ks0127_read(sd, KS_DEMOD) & 0x40))
+ /* force to secam mode */
+- ks0127_and_or(c, KS_DEMOD, 0xf0, 0x0f);
+- break;
+-
+- default:
+- v4l_dbg(1, debug, c,
+- "DECODER_SET_NORM: Unknown norm %d\n", *iarg);
+- break;
+- }
+- break;
+-
+- case DECODER_SET_PICTURE:
+- v4l_dbg(1, debug, c,
+- "DECODER_SET_PICTURE: not yet supported\n");
+- return -EINVAL;
+-
+- /* sam todo: KS0127_SET_BRIGHTNESS: Merge into DECODER_SET_PICTURE */
+- /* sam todo: KS0127_SET_CONTRAST: Merge into DECODER_SET_PICTURE */
+- /* sam todo: KS0127_SET_HUE: Merge into DECODER_SET_PICTURE? */
+- /* sam todo: KS0127_SET_SATURATION: Merge into DECODER_SET_PICTURE */
+- /* sam todo: KS0127_SET_AGC_MODE: */
+- /* sam todo: KS0127_SET_AGC: */
+- /* sam todo: KS0127_SET_CHROMA_MODE: */
+- /* sam todo: KS0127_SET_PIXCLK_MODE: */
+- /* sam todo: KS0127_SET_GAMMA_MODE: */
+- /* sam todo: KS0127_SET_UGAIN: */
+- /* sam todo: KS0127_SET_VGAIN: */
+- /* sam todo: KS0127_SET_INVALY: */
+- /* sam todo: KS0127_SET_INVALU: */
+- /* sam todo: KS0127_SET_INVALV: */
+- /* sam todo: KS0127_SET_UNUSEY: */
+- /* sam todo: KS0127_SET_UNUSEU: */
+- /* sam todo: KS0127_SET_UNUSEV: */
+- /* sam todo: KS0127_SET_VSALIGN_MODE: */
+-
+- case DECODER_ENABLE_OUTPUT:
+- {
+- int enable;
+-
+- iarg = arg;
+- enable = (*iarg != 0);
+- if (enable) {
+- v4l_dbg(1, debug, c,
+- "DECODER_ENABLE_OUTPUT on\n");
+- /* All output pins on */
+- ks0127_and_or(c, KS_OFMTA, 0xcf, 0x30);
+- /* Obey the OEN pin */
+- ks0127_and_or(c, KS_CDEM, 0x7f, 0x00);
+- } else {
+- v4l_dbg(1, debug, c,
+- "DECODER_ENABLE_OUTPUT off\n");
+- /* Video output pins off */
+- ks0127_and_or(c, KS_OFMTA, 0xcf, 0x00);
+- /* Ignore the OEN pin */
+- ks0127_and_or(c, KS_CDEM, 0x7f, 0x80);
+- }
+- break;
++ ks0127_and_or(sd, KS_DEMOD, 0xf0, 0x0f);
++ } else {
++ v4l2_dbg(1, debug, sd, "s_std: Unknown norm %llx\n",
++ (unsigned long long)std);
+ }
++ return 0;
++}
+
+- /* sam todo: KS0127_SET_OUTPUT_MODE: */
+- /* sam todo: KS0127_SET_WIDTH: */
+- /* sam todo: KS0127_SET_HEIGHT: */
+- /* sam todo: KS0127_SET_HSCALE: */
+-
+- case DECODER_GET_STATUS:
+- v4l_dbg(1, debug, c, "DECODER_GET_STATUS\n");
+- *iarg = 0;
+- status = ks0127_read(c, KS_STAT);
+- if (!(status & 0x20)) /* NOVID not set */
+- *iarg = (*iarg | DECODER_STATUS_GOOD);
+- if ((status & 0x01)) /* CLOCK set */
+- *iarg = (*iarg | DECODER_STATUS_COLOR);
+- if ((status & 0x08)) /* PALDET set */
+- *iarg = (*iarg | DECODER_STATUS_PAL);
+- else
+- *iarg = (*iarg | DECODER_STATUS_NTSC);
+- break;
+-
+- /* Catch any unknown command */
+- default:
+- v4l_dbg(1, debug, c, "unknown: 0x%08x\n", cmd);
+- return -EINVAL;
++static int ks0127_s_stream(struct v4l2_subdev *sd, int enable)
++{
++ v4l2_dbg(1, debug, sd, "s_stream(%d)\n", enable);
++ if (enable) {
++ /* All output pins on */
++ ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x30);
++ /* Obey the OEN pin */
++ ks0127_and_or(sd, KS_CDEM, 0x7f, 0x00);
++ } else {
++ /* Video output pins off */
++ ks0127_and_or(sd, KS_OFMTA, 0xcf, 0x00);
++ /* Ignore the OEN pin */
++ ks0127_and_or(sd, KS_CDEM, 0x7f, 0x80);
+ }
+ return 0;
+ }
+
++static int ks0127_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
++{
++ int stat = V4L2_IN_ST_NO_SIGNAL;
++ u8 status;
++ v4l2_std_id std = V4L2_STD_ALL;
++
++ status = ks0127_read(sd, KS_STAT);
++ if (!(status & 0x20)) /* NOVID not set */
++ stat = 0;
++ if (!(status & 0x01)) /* CLOCK set */
++ stat |= V4L2_IN_ST_NO_COLOR;
++ if ((status & 0x08)) /* PALDET set */
++ std = V4L2_STD_PAL;
++ else
++ std = V4L2_STD_NTSC;
++ if (pstd)
++ *pstd = std;
++ if (pstatus)
++ *pstatus = stat;
++ return 0;
++}
++
++static int ks0127_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
++{
++ v4l2_dbg(1, debug, sd, "querystd\n");
++ return ks0127_status(sd, NULL, std);
++}
+
+-/* Addresses to scan */
+-#define I2C_KS0127_ADDON 0xD8
+-#define I2C_KS0127_ONBOARD 0xDA
++static int ks0127_g_input_status(struct v4l2_subdev *sd, u32 *status)
++{
++ v4l2_dbg(1, debug, sd, "g_input_status\n");
++ return ks0127_status(sd, status, NULL);
++}
++
++static int ks0127_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct ks0127 *ks = to_ks0127(sd);
++
++ return v4l2_chip_ident_i2c_client(client, chip, ks->ident, 0);
++}
++
++/* ----------------------------------------------------------------------- */
+
+-static unsigned short normal_i2c[] = {
+- I2C_KS0127_ADDON >> 1,
+- I2C_KS0127_ONBOARD >> 1,
+- I2C_CLIENT_END
++static const struct v4l2_subdev_core_ops ks0127_core_ops = {
++ .g_chip_ident = ks0127_g_chip_ident,
+ };
+
+-I2C_CLIENT_INSMOD;
++static const struct v4l2_subdev_tuner_ops ks0127_tuner_ops = {
++ .s_std = ks0127_s_std,
++};
++
++static const struct v4l2_subdev_video_ops ks0127_video_ops = {
++ .s_routing = ks0127_s_routing,
++ .s_stream = ks0127_s_stream,
++ .querystd = ks0127_querystd,
++ .g_input_status = ks0127_g_input_status,
++};
++
++static const struct v4l2_subdev_ops ks0127_ops = {
++ .core = &ks0127_core_ops,
++ .tuner = &ks0127_tuner_ops,
++ .video = &ks0127_video_ops,
++};
++
++/* ----------------------------------------------------------------------- */
++
+
+-static int ks0127_probe(struct i2c_client *c, const struct i2c_device_id *id)
++static int ks0127_probe(struct i2c_client *client, const struct i2c_device_id *id)
+ {
+ struct ks0127 *ks;
++ struct v4l2_subdev *sd;
+
+- v4l_info(c, "%s chip found @ 0x%x (%s)\n",
+- c->addr == (I2C_KS0127_ADDON >> 1) ? "addon" : "on-board",
+- c->addr << 1, c->adapter->name);
++ v4l_info(client, "%s chip found @ 0x%x (%s)\n",
++ client->addr == (I2C_KS0127_ADDON >> 1) ? "addon" : "on-board",
++ client->addr << 1, client->adapter->name);
+
+ ks = kzalloc(sizeof(*ks), GFP_KERNEL);
+ if (ks == NULL)
+ return -ENOMEM;
+-
+- i2c_set_clientdata(c, ks);
+-
+- ks->ks_type = KS_TYPE_UNKNOWN;
++ sd = &ks->sd;
++ v4l2_i2c_subdev_init(sd, client, &ks0127_ops);
+
+ /* power up */
+ init_reg_defaults();
+- ks0127_write(c, KS_CMDA, 0x2c);
++ ks0127_write(sd, KS_CMDA, 0x2c);
+ mdelay(10);
+
+ /* reset the device */
+- ks0127_reset(c);
++ ks0127_init(sd);
+ return 0;
+ }
+
+-static int ks0127_remove(struct i2c_client *c)
++static int ks0127_remove(struct i2c_client *client)
+ {
+- struct ks0127 *ks = i2c_get_clientdata(c);
+-
+- ks0127_write(c, KS_OFMTA, 0x20); /* tristate */
+- ks0127_write(c, KS_CMDA, 0x2c | 0x80); /* power down */
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+- kfree(ks);
++ v4l2_device_unregister_subdev(sd);
++ ks0127_write(sd, KS_OFMTA, 0x20); /* tristate */
++ ks0127_write(sd, KS_CMDA, 0x2c | 0x80); /* power down */
++ kfree(to_ks0127(sd));
+ return 0;
+ }
+
+-static int ks0127_legacy_probe(struct i2c_adapter *adapter)
+-{
+- return adapter->id == I2C_HW_B_ZR36067;
+-}
+-
+ static const struct i2c_device_id ks0127_id[] = {
+ { "ks0127", 0 },
++ { "ks0127b", 0 },
++ { "ks0122s", 0 },
+ { }
+ };
+ MODULE_DEVICE_TABLE(i2c, ks0127_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "ks0127",
+- .driverid = I2C_DRIVERID_KS0127,
+- .command = ks0127_command,
+ .probe = ks0127_probe,
+ .remove = ks0127_remove,
+- .legacy_probe = ks0127_legacy_probe,
+ .id_table = ks0127_id,
+ };
+diff --git a/drivers/media/video/ks0127.h b/drivers/media/video/ks0127.h
+index 1ec5788..cb8abd5 100644
+--- a/drivers/media/video/ks0127.h
++++ b/drivers/media/video/ks0127.h
+@@ -24,8 +24,6 @@
+ #ifndef KS0127_H
+ #define KS0127_H
+
+-#include <linux/videodev.h>
+-
+ /* input channels */
+ #define KS_INPUT_COMPOSITE_1 0
+ #define KS_INPUT_COMPOSITE_2 1
+diff --git a/drivers/media/video/m52790.c b/drivers/media/video/m52790.c
+index de397ef..1f340fe 100644
+--- a/drivers/media/video/m52790.c
++++ b/drivers/media/video/m52790.c
+@@ -132,11 +132,6 @@ static int m52790_log_status(struct v4l2_subdev *sd)
+ return 0;
+ }
+
+-static int m52790_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+-}
+-
+ /* ----------------------------------------------------------------------- */
+
+ static const struct v4l2_subdev_core_ops m52790_core_ops = {
+@@ -210,8 +205,6 @@ MODULE_DEVICE_TABLE(i2c, m52790_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "m52790",
+- .driverid = I2C_DRIVERID_M52790,
+- .command = m52790_command,
+ .probe = m52790_probe,
+ .remove = m52790_remove,
+ .id_table = m52790_id,
+diff --git a/drivers/media/video/meye.c b/drivers/media/video/meye.c
+index b76e33d..2ad11f0 100644
+--- a/drivers/media/video/meye.c
++++ b/drivers/media/video/meye.c
+@@ -1017,7 +1017,6 @@ static int meyeioc_stilljcapt(int *len)
+ static int vidioc_querycap(struct file *file, void *fh,
+ struct v4l2_capability *cap)
+ {
+- memset(cap, 0, sizeof(*cap));
+ strcpy(cap->driver, "meye");
+ strcpy(cap->card, "meye");
+ sprintf(cap->bus_info, "PCI:%s", pci_name(meye.mchip_dev));
+@@ -1036,8 +1035,6 @@ static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
+ if (i->index != 0)
+ return -EINVAL;
+
+- memset(i, 0, sizeof(*i));
+- i->index = 0;
+ strcpy(i->name, "Camera");
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+
+@@ -1259,22 +1256,13 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
+ if (f->index > 1)
+ return -EINVAL;
+
+- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+-
+ if (f->index == 0) {
+ /* standard YUV 422 capture */
+- memset(f, 0, sizeof(*f));
+- f->index = 0;
+- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->flags = 0;
+ strcpy(f->description, "YUV422");
+ f->pixelformat = V4L2_PIX_FMT_YUYV;
+ } else {
+ /* compressed MJPEG capture */
+- memset(f, 0, sizeof(*f));
+- f->index = 1;
+- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->flags = V4L2_FMT_FLAG_COMPRESSED;
+ strcpy(f->description, "MJPEG");
+ f->pixelformat = V4L2_PIX_FMT_MJPEG;
+@@ -1286,9 +1274,6 @@ static int vidioc_enum_fmt_vid_cap(struct file *file, void *fh,
+ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+ {
+- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+-
+ if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV &&
+ f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
+ return -EINVAL;
+@@ -1319,12 +1304,6 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *fh,
+ static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+ {
+- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+-
+- memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format));
+- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+-
+ switch (meye.mchip_mode) {
+ case MCHIP_HIC_MODE_CONT_OUT:
+ default:
+@@ -1341,8 +1320,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+ f->fmt.pix.bytesperline = f->fmt.pix.width * 2;
+ f->fmt.pix.sizeimage = f->fmt.pix.height *
+ f->fmt.pix.bytesperline;
+- f->fmt.pix.colorspace = 0;
+- f->fmt.pix.priv = 0;
+
+ return 0;
+ }
+@@ -1350,9 +1327,6 @@ static int vidioc_g_fmt_vid_cap(struct file *file, void *fh,
+ static int vidioc_s_fmt_vid_cap(struct file *file, void *fh,
+ struct v4l2_format *f)
+ {
+- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+-
+ if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_YUYV &&
+ f->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
+ return -EINVAL;
+@@ -1398,9 +1372,6 @@ static int vidioc_reqbufs(struct file *file, void *fh,
+ {
+ int i;
+
+- if (req->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+-
+ if (req->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+@@ -1441,15 +1412,11 @@ static int vidioc_reqbufs(struct file *file, void *fh,
+
+ static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+ {
+- int index = buf->index;
++ unsigned int index = buf->index;
+
+- if (index < 0 || index >= gbuffers)
++ if (index >= gbuffers)
+ return -EINVAL;
+
+- memset(buf, 0, sizeof(*buf));
+-
+- buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+- buf->index = index;
+ buf->bytesused = meye.grab_buffer[index].size;
+ buf->flags = V4L2_BUF_FLAG_MAPPED;
+
+@@ -1471,13 +1438,10 @@ static int vidioc_querybuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+
+ static int vidioc_qbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+ {
+- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+-
+ if (buf->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+- if (buf->index < 0 || buf->index >= gbuffers)
++ if (buf->index >= gbuffers)
+ return -EINVAL;
+
+ if (meye.grab_buffer[buf->index].state != MEYE_BUF_UNUSED)
+@@ -1497,9 +1461,6 @@ static int vidioc_dqbuf(struct file *file, void *fh, struct v4l2_buffer *buf)
+ {
+ int reqnr;
+
+- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+-
+ if (buf->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+diff --git a/drivers/media/video/msp3400-driver.c b/drivers/media/video/msp3400-driver.c
+index 4d7a918..9e8e06c 100644
+--- a/drivers/media/video/msp3400-driver.c
++++ b/drivers/media/video/msp3400-driver.c
+@@ -366,29 +366,6 @@ int msp_sleep(struct msp_state *state, int timeout)
+ }
+
+ /* ------------------------------------------------------------------------ */
+-#ifdef CONFIG_VIDEO_ALLOW_V4L1
+-static int msp_mode_v4l2_to_v4l1(int rxsubchans, int audmode)
+-{
+- if (rxsubchans == V4L2_TUNER_SUB_MONO)
+- return VIDEO_SOUND_MONO;
+- if (rxsubchans == V4L2_TUNER_SUB_STEREO)
+- return VIDEO_SOUND_STEREO;
+- if (audmode == V4L2_TUNER_MODE_LANG2)
+- return VIDEO_SOUND_LANG2;
+- return VIDEO_SOUND_LANG1;
+-}
+-
+-static int msp_mode_v4l1_to_v4l2(int mode)
+-{
+- if (mode & VIDEO_SOUND_STEREO)
+- return V4L2_TUNER_MODE_STEREO;
+- if (mode & VIDEO_SOUND_LANG2)
+- return V4L2_TUNER_MODE_LANG2;
+- if (mode & VIDEO_SOUND_LANG1)
+- return V4L2_TUNER_MODE_LANG1;
+- return V4L2_TUNER_MODE_MONO;
+-}
+-#endif
+
+ static int msp_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+ {
+@@ -482,96 +459,6 @@ static int msp_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+ return 0;
+ }
+
+-#ifdef CONFIG_VIDEO_ALLOW_V4L1
+-static long msp_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+-{
+- struct msp_state *state = to_state(sd);
+- struct i2c_client *client = v4l2_get_subdevdata(sd);
+-
+- switch (cmd) {
+- /* --- v4l ioctls --- */
+- /* take care: bttv does userspace copying, we'll get a
+- kernel pointer here... */
+- case VIDIOCGAUDIO:
+- {
+- struct video_audio *va = arg;
+-
+- va->flags |= VIDEO_AUDIO_VOLUME | VIDEO_AUDIO_MUTABLE;
+- if (state->has_sound_processing)
+- va->flags |= VIDEO_AUDIO_BALANCE |
+- VIDEO_AUDIO_BASS |
+- VIDEO_AUDIO_TREBLE;
+- if (state->muted)
+- va->flags |= VIDEO_AUDIO_MUTE;
+- va->volume = state->volume;
+- va->balance = state->volume ? state->balance : 32768;
+- va->bass = state->bass;
+- va->treble = state->treble;
+-
+- if (state->radio)
+- break;
+- if (state->opmode == OPMODE_AUTOSELECT)
+- msp_detect_stereo(client);
+- va->mode = msp_mode_v4l2_to_v4l1(state->rxsubchans, state->audmode);
+- break;
+- }
+-
+- case VIDIOCSAUDIO:
+- {
+- struct video_audio *va = arg;
+-
+- state->muted = (va->flags & VIDEO_AUDIO_MUTE);
+- state->volume = va->volume;
+- state->balance = va->balance;
+- state->bass = va->bass;
+- state->treble = va->treble;
+- msp_set_audio(client);
+-
+- if (va->mode != 0 && state->radio == 0 &&
+- state->audmode != msp_mode_v4l1_to_v4l2(va->mode)) {
+- state->audmode = msp_mode_v4l1_to_v4l2(va->mode);
+- msp_set_audmode(client);
+- }
+- break;
+- }
+-
+- case VIDIOCSCHAN:
+- {
+- struct video_channel *vc = arg;
+- int update = 0;
+- v4l2_std_id std;
+-
+- if (state->radio)
+- update = 1;
+- state->radio = 0;
+- if (vc->norm == VIDEO_MODE_PAL)
+- std = V4L2_STD_PAL;
+- else if (vc->norm == VIDEO_MODE_SECAM)
+- std = V4L2_STD_SECAM;
+- else
+- std = V4L2_STD_NTSC;
+- if (std != state->v4l2_std) {
+- state->v4l2_std = std;
+- update = 1;
+- }
+- if (update)
+- msp_wake_thread(client);
+- break;
+- }
+-
+- case VIDIOCSFREQ:
+- {
+- /* new channel -- kick audio carrier scan */
+- msp_wake_thread(client);
+- break;
+- }
+- default:
+- return -ENOIOCTLCMD;
+- }
+- return 0;
+-}
+-#endif
+-
+ /* --- v4l2 ioctls --- */
+ static int msp_s_radio(struct v4l2_subdev *sd)
+ {
+@@ -713,22 +600,24 @@ static int msp_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+ struct msp_state *state = to_state(sd);
+
+ switch (qc->id) {
+- case V4L2_CID_AUDIO_VOLUME:
+- case V4L2_CID_AUDIO_MUTE:
+- return v4l2_ctrl_query_fill_std(qc);
+- default:
+- break;
++ case V4L2_CID_AUDIO_VOLUME:
++ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880);
++ case V4L2_CID_AUDIO_MUTE:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
++ default:
++ break;
+ }
+ if (!state->has_sound_processing)
+ return -EINVAL;
+ switch (qc->id) {
+- case V4L2_CID_AUDIO_LOUDNESS:
+- case V4L2_CID_AUDIO_BALANCE:
+- case V4L2_CID_AUDIO_BASS:
+- case V4L2_CID_AUDIO_TREBLE:
+- return v4l2_ctrl_query_fill_std(qc);
+- default:
+- return -EINVAL;
++ case V4L2_CID_AUDIO_LOUDNESS:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
++ case V4L2_CID_AUDIO_BALANCE:
++ case V4L2_CID_AUDIO_BASS:
++ case V4L2_CID_AUDIO_TREBLE:
++ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
++ default:
++ return -EINVAL;
+ }
+ return 0;
+ }
+@@ -820,9 +709,6 @@ static const struct v4l2_subdev_core_ops msp_core_ops = {
+ .g_ctrl = msp_g_ctrl,
+ .s_ctrl = msp_s_ctrl,
+ .queryctrl = msp_queryctrl,
+-#ifdef CONFIG_VIDEO_ALLOW_V4L1
+- .ioctl = msp_ioctl,
+-#endif
+ };
+
+ static const struct v4l2_subdev_tuner_ops msp_tuner_ops = {
+diff --git a/drivers/media/video/mt9m001.c b/drivers/media/video/mt9m001.c
+index c1bf75e..fa7e509 100644
+--- a/drivers/media/video/mt9m001.c
++++ b/drivers/media/video/mt9m001.c
+@@ -12,7 +12,6 @@
+ #include <linux/slab.h>
+ #include <linux/i2c.h>
+ #include <linux/log2.h>
+-#include <linux/gpio.h>
+
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-chip-ident.h>
+@@ -73,9 +72,7 @@ struct mt9m001 {
+ struct i2c_client *client;
+ struct soc_camera_device icd;
+ int model; /* V4L2_IDENT_MT9M001* codes from v4l2-chip-ident.h */
+- int switch_gpio;
+ unsigned char autoexposure;
+- unsigned char datawidth;
+ };
+
+ static int reg_read(struct soc_camera_device *icd, const u8 reg)
+@@ -181,92 +178,28 @@ static int mt9m001_stop_capture(struct soc_camera_device *icd)
+ return 0;
+ }
+
+-static int bus_switch_request(struct mt9m001 *mt9m001,
+- struct soc_camera_link *icl)
+-{
+-#ifdef CONFIG_MT9M001_PCA9536_SWITCH
+- int ret;
+- unsigned int gpio = icl->gpio;
+-
+- if (gpio_is_valid(gpio)) {
+- /* We have a data bus switch. */
+- ret = gpio_request(gpio, "mt9m001");
+- if (ret < 0) {
+- dev_err(&mt9m001->client->dev, "Cannot get GPIO %u\n",
+- gpio);
+- return ret;
+- }
+-
+- ret = gpio_direction_output(gpio, 0);
+- if (ret < 0) {
+- dev_err(&mt9m001->client->dev,
+- "Cannot set GPIO %u to output\n", gpio);
+- gpio_free(gpio);
+- return ret;
+- }
+- }
+-
+- mt9m001->switch_gpio = gpio;
+-#else
+- mt9m001->switch_gpio = -EINVAL;
+-#endif
+- return 0;
+-}
+-
+-static void bus_switch_release(struct mt9m001 *mt9m001)
+-{
+-#ifdef CONFIG_MT9M001_PCA9536_SWITCH
+- if (gpio_is_valid(mt9m001->switch_gpio))
+- gpio_free(mt9m001->switch_gpio);
+-#endif
+-}
+-
+-static int bus_switch_act(struct mt9m001 *mt9m001, int go8bit)
+-{
+-#ifdef CONFIG_MT9M001_PCA9536_SWITCH
+- if (!gpio_is_valid(mt9m001->switch_gpio))
+- return -ENODEV;
+-
+- gpio_set_value_cansleep(mt9m001->switch_gpio, go8bit);
+- return 0;
+-#else
+- return -ENODEV;
+-#endif
+-}
+-
+-static int bus_switch_possible(struct mt9m001 *mt9m001)
+-{
+-#ifdef CONFIG_MT9M001_PCA9536_SWITCH
+- return gpio_is_valid(mt9m001->switch_gpio);
+-#else
+- return 0;
+-#endif
+-}
+-
+ static int mt9m001_set_bus_param(struct soc_camera_device *icd,
+ unsigned long flags)
+ {
+ struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
+- unsigned int width_flag = flags & SOCAM_DATAWIDTH_MASK;
+- int ret;
++ struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
++ unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK;
+
+- /* Flags validity verified in test_bus_param */
++ /* Only one width bit may be set */
++ if (!is_power_of_2(width_flag))
++ return -EINVAL;
+
+- if ((mt9m001->datawidth != 10 && (width_flag == SOCAM_DATAWIDTH_10)) ||
+- (mt9m001->datawidth != 9 && (width_flag == SOCAM_DATAWIDTH_9)) ||
+- (mt9m001->datawidth != 8 && (width_flag == SOCAM_DATAWIDTH_8))) {
+- /* Well, we actually only can do 10 or 8 bits... */
+- if (width_flag == SOCAM_DATAWIDTH_9)
+- return -EINVAL;
+- ret = bus_switch_act(mt9m001,
+- width_flag == SOCAM_DATAWIDTH_8);
+- if (ret < 0)
+- return ret;
++ if (icl->set_bus_param)
++ return icl->set_bus_param(icl, width_flag);
+
+- mt9m001->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10;
+- }
++ /*
++ * Without board specific bus width settings we only support the
++ * sensors native bus width
++ */
++ if (width_flag == SOCAM_DATAWIDTH_10)
++ return 0;
+
+- return 0;
++ return -EINVAL;
+ }
+
+ static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd)
+@@ -274,18 +207,20 @@ static unsigned long mt9m001_query_bus_param(struct soc_camera_device *icd)
+ struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
+ struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
+ /* MT9M001 has all capture_format parameters fixed */
+- unsigned long flags = SOCAM_DATAWIDTH_10 | SOCAM_PCLK_SAMPLE_RISING |
++ unsigned long flags = SOCAM_PCLK_SAMPLE_RISING |
+ SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
+- SOCAM_MASTER;
++ SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER;
+
+- if (bus_switch_possible(mt9m001))
+- flags |= SOCAM_DATAWIDTH_8;
++ if (icl->query_bus_param)
++ flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK;
++ else
++ flags |= SOCAM_DATAWIDTH_10;
+
+ return soc_camera_apply_sensor_flags(icl, flags);
+ }
+
+-static int mt9m001_set_fmt(struct soc_camera_device *icd,
+- __u32 pixfmt, struct v4l2_rect *rect)
++static int mt9m001_set_crop(struct soc_camera_device *icd,
++ struct v4l2_rect *rect)
+ {
+ struct mt9m001 *mt9m001 = container_of(icd, struct mt9m001, icd);
+ int ret;
+@@ -324,6 +259,20 @@ static int mt9m001_set_fmt(struct soc_camera_device *icd,
+ return ret;
+ }
+
++static int mt9m001_set_fmt(struct soc_camera_device *icd,
++ struct v4l2_format *f)
++{
++ struct v4l2_rect rect = {
++ .left = icd->x_current,
++ .top = icd->y_current,
++ .width = f->fmt.pix.width,
++ .height = f->fmt.pix.height,
++ };
++
++ /* No support for scaling so far, just crop. TODO: use skipping */
++ return mt9m001_set_crop(icd, &rect);
++}
++
+ static int mt9m001_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+ {
+@@ -449,6 +398,7 @@ static struct soc_camera_ops mt9m001_ops = {
+ .release = mt9m001_release,
+ .start_capture = mt9m001_start_capture,
+ .stop_capture = mt9m001_stop_capture,
++ .set_crop = mt9m001_set_crop,
+ .set_fmt = mt9m001_set_fmt,
+ .try_fmt = mt9m001_try_fmt,
+ .set_bus_param = mt9m001_set_bus_param,
+@@ -583,6 +533,7 @@ static int mt9m001_video_probe(struct soc_camera_device *icd)
+ struct soc_camera_link *icl = mt9m001->client->dev.platform_data;
+ s32 data;
+ int ret;
++ unsigned long flags;
+
+ /* We must have a parent by now. And it cannot be a wrong one.
+ * So this entire test is completely redundant. */
+@@ -603,18 +554,10 @@ static int mt9m001_video_probe(struct soc_camera_device *icd)
+ case 0x8421:
+ mt9m001->model = V4L2_IDENT_MT9M001C12ST;
+ icd->formats = mt9m001_colour_formats;
+- if (gpio_is_valid(icl->gpio))
+- icd->num_formats = ARRAY_SIZE(mt9m001_colour_formats);
+- else
+- icd->num_formats = 1;
+ break;
+ case 0x8431:
+ mt9m001->model = V4L2_IDENT_MT9M001C12STM;
+ icd->formats = mt9m001_monochrome_formats;
+- if (gpio_is_valid(icl->gpio))
+- icd->num_formats = ARRAY_SIZE(mt9m001_monochrome_formats);
+- else
+- icd->num_formats = 1;
+ break;
+ default:
+ ret = -ENODEV;
+@@ -623,6 +566,26 @@ static int mt9m001_video_probe(struct soc_camera_device *icd)
+ goto ei2c;
+ }
+
++ icd->num_formats = 0;
++
++ /*
++ * This is a 10bit sensor, so by default we only allow 10bit.
++ * The platform may support different bus widths due to
++ * different routing of the data lines.
++ */
++ if (icl->query_bus_param)
++ flags = icl->query_bus_param(icl);
++ else
++ flags = SOCAM_DATAWIDTH_10;
++
++ if (flags & SOCAM_DATAWIDTH_10)
++ icd->num_formats++;
++ else
++ icd->formats++;
++
++ if (flags & SOCAM_DATAWIDTH_8)
++ icd->num_formats++;
++
+ dev_info(&icd->dev, "Detected a MT9M001 chip ID %x (%s)\n", data,
+ data == 0x8431 ? "C12STM" : "C12ST");
+
+@@ -688,18 +651,10 @@ static int mt9m001_probe(struct i2c_client *client,
+ icd->height_max = 1024;
+ icd->y_skip_top = 1;
+ icd->iface = icl->bus_id;
+- /* Default datawidth - this is the only width this camera (normally)
+- * supports. It is only with extra logic that it can support
+- * other widths. Therefore it seems to be a sensible default. */
+- mt9m001->datawidth = 10;
+ /* Simulated autoexposure. If enabled, we calculate shutter width
+ * ourselves in the driver based on vertical blanking and frame width */
+ mt9m001->autoexposure = 1;
+
+- ret = bus_switch_request(mt9m001, icl);
+- if (ret)
+- goto eswinit;
+-
+ ret = soc_camera_device_register(icd);
+ if (ret)
+ goto eisdr;
+@@ -707,8 +662,6 @@ static int mt9m001_probe(struct i2c_client *client,
+ return 0;
+
+ eisdr:
+- bus_switch_release(mt9m001);
+-eswinit:
+ kfree(mt9m001);
+ return ret;
+ }
+@@ -718,7 +671,6 @@ static int mt9m001_remove(struct i2c_client *client)
+ struct mt9m001 *mt9m001 = i2c_get_clientdata(client);
+
+ soc_camera_device_unregister(&mt9m001->icd);
+- bus_switch_release(mt9m001);
+ kfree(mt9m001);
+
+ return 0;
+diff --git a/drivers/media/video/mt9m111.c b/drivers/media/video/mt9m111.c
+index 5b8e209..cdd1ddb 100644
+--- a/drivers/media/video/mt9m111.c
++++ b/drivers/media/video/mt9m111.c
+@@ -152,7 +152,7 @@ struct mt9m111 {
+ struct soc_camera_device icd;
+ int model; /* V4L2_IDENT_MT9M11x* codes from v4l2-chip-ident.h */
+ enum mt9m111_context context;
+- unsigned int left, top, width, height;
++ struct v4l2_rect rect;
+ u32 pixfmt;
+ unsigned char autoexposure;
+ unsigned char datawidth;
+@@ -249,12 +249,13 @@ static int mt9m111_set_context(struct soc_camera_device *icd,
+ return reg_write(CONTEXT_CONTROL, valA);
+ }
+
+-static int mt9m111_setup_rect(struct soc_camera_device *icd)
++static int mt9m111_setup_rect(struct soc_camera_device *icd,
++ struct v4l2_rect *rect)
+ {
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+ int ret, is_raw_format;
+- int width = mt9m111->width;
+- int height = mt9m111->height;
++ int width = rect->width;
++ int height = rect->height;
+
+ if ((mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR8)
+ || (mt9m111->pixfmt == V4L2_PIX_FMT_SBGGR16))
+@@ -262,9 +263,9 @@ static int mt9m111_setup_rect(struct soc_camera_device *icd)
+ else
+ is_raw_format = 0;
+
+- ret = reg_write(COLUMN_START, mt9m111->left);
++ ret = reg_write(COLUMN_START, rect->left);
+ if (!ret)
+- ret = reg_write(ROW_START, mt9m111->top);
++ ret = reg_write(ROW_START, rect->top);
+
+ if (is_raw_format) {
+ if (!ret)
+@@ -393,6 +394,8 @@ static int mt9m111_disable(struct soc_camera_device *icd)
+
+ static int mt9m111_reset(struct soc_camera_device *icd)
+ {
++ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
++ struct soc_camera_link *icl = mt9m111->client->dev.platform_data;
+ int ret;
+
+ ret = reg_set(RESET, MT9M111_RESET_RESET_MODE);
+@@ -401,6 +404,10 @@ static int mt9m111_reset(struct soc_camera_device *icd)
+ if (!ret)
+ ret = reg_clear(RESET, MT9M111_RESET_RESET_MODE
+ | MT9M111_RESET_RESET_SOC);
++
++ if (icl->reset)
++ icl->reset(&mt9m111->client->dev);
++
+ return ret;
+ }
+
+@@ -420,7 +427,7 @@ static unsigned long mt9m111_query_bus_param(struct soc_camera_device *icd)
+ struct soc_camera_link *icl = mt9m111->client->dev.platform_data;
+ unsigned long flags = SOCAM_MASTER | SOCAM_PCLK_SAMPLE_RISING |
+ SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_HIGH |
+- SOCAM_DATAWIDTH_8;
++ SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATAWIDTH_8;
+
+ return soc_camera_apply_sensor_flags(icl, flags);
+ }
+@@ -430,6 +437,22 @@ static int mt9m111_set_bus_param(struct soc_camera_device *icd, unsigned long f)
+ return 0;
+ }
+
++static int mt9m111_set_crop(struct soc_camera_device *icd,
++ struct v4l2_rect *rect)
++{
++ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
++ int ret;
++
++ dev_dbg(&icd->dev, "%s left=%d, top=%d, width=%d, height=%d\n",
++ __func__, rect->left, rect->top, rect->width,
++ rect->height);
++
++ ret = mt9m111_setup_rect(icd, rect);
++ if (!ret)
++ mt9m111->rect = *rect;
++ return ret;
++}
++
+ static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt)
+ {
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
+@@ -480,23 +503,27 @@ static int mt9m111_set_pixfmt(struct soc_camera_device *icd, u32 pixfmt)
+ }
+
+ static int mt9m111_set_fmt(struct soc_camera_device *icd,
+- __u32 pixfmt, struct v4l2_rect *rect)
++ struct v4l2_format *f)
+ {
+ struct mt9m111 *mt9m111 = container_of(icd, struct mt9m111, icd);
++ struct v4l2_pix_format *pix = &f->fmt.pix;
++ struct v4l2_rect rect = {
++ .left = mt9m111->rect.left,
++ .top = mt9m111->rect.top,
++ .width = pix->width,
++ .height = pix->height,
++ };
+ int ret;
+
+- mt9m111->left = rect->left;
+- mt9m111->top = rect->top;
+- mt9m111->width = rect->width;
+- mt9m111->height = rect->height;
+-
+ dev_dbg(&icd->dev, "%s fmt=%x left=%d, top=%d, width=%d, height=%d\n",
+- __func__, pixfmt, mt9m111->left, mt9m111->top, mt9m111->width,
+- mt9m111->height);
++ __func__, pix->pixelformat, rect.left, rect.top, rect.width,
++ rect.height);
+
+- ret = mt9m111_setup_rect(icd);
++ ret = mt9m111_setup_rect(icd, &rect);
++ if (!ret)
++ ret = mt9m111_set_pixfmt(icd, pix->pixelformat);
+ if (!ret)
+- ret = mt9m111_set_pixfmt(icd, pixfmt);
++ mt9m111->rect = rect;
+ return ret;
+ }
+
+@@ -627,6 +654,7 @@ static struct soc_camera_ops mt9m111_ops = {
+ .release = mt9m111_release,
+ .start_capture = mt9m111_start_capture,
+ .stop_capture = mt9m111_stop_capture,
++ .set_crop = mt9m111_set_crop,
+ .set_fmt = mt9m111_set_fmt,
+ .try_fmt = mt9m111_try_fmt,
+ .query_bus_param = mt9m111_query_bus_param,
+@@ -811,7 +839,7 @@ static int mt9m111_restore_state(struct soc_camera_device *icd)
+
+ mt9m111_set_context(icd, mt9m111->context);
+ mt9m111_set_pixfmt(icd, mt9m111->pixfmt);
+- mt9m111_setup_rect(icd);
++ mt9m111_setup_rect(icd, &mt9m111->rect);
+ mt9m111_set_flip(icd, mt9m111->hflip, MT9M111_RMB_MIRROR_COLS);
+ mt9m111_set_flip(icd, mt9m111->vflip, MT9M111_RMB_MIRROR_ROWS);
+ mt9m111_set_global_gain(icd, icd->gain);
+diff --git a/drivers/media/video/mt9t031.c b/drivers/media/video/mt9t031.c
+index 349d8e3..23f9ce9 100644
+--- a/drivers/media/video/mt9t031.c
++++ b/drivers/media/video/mt9t031.c
+@@ -144,13 +144,11 @@ static int mt9t031_init(struct soc_camera_device *icd)
+ int ret;
+
+ /* Disable chip output, synchronous option update */
+- dev_dbg(icd->vdev->parent, "%s\n", __func__);
+-
+ ret = reg_write(icd, MT9T031_RESET, 1);
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_RESET, 0);
+ if (ret >= 0)
+- ret = reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3);
++ ret = reg_clear(icd, MT9T031_OUTPUT_CONTROL, 2);
+
+ return ret >= 0 ? 0 : -EIO;
+ }
+@@ -158,14 +156,14 @@ static int mt9t031_init(struct soc_camera_device *icd)
+ static int mt9t031_release(struct soc_camera_device *icd)
+ {
+ /* Disable the chip */
+- reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3);
++ reg_clear(icd, MT9T031_OUTPUT_CONTROL, 2);
+ return 0;
+ }
+
+ static int mt9t031_start_capture(struct soc_camera_device *icd)
+ {
+ /* Switch to master "normal" mode */
+- if (reg_set(icd, MT9T031_OUTPUT_CONTROL, 3) < 0)
++ if (reg_set(icd, MT9T031_OUTPUT_CONTROL, 2) < 0)
+ return -EIO;
+ return 0;
+ }
+@@ -173,7 +171,7 @@ static int mt9t031_start_capture(struct soc_camera_device *icd)
+ static int mt9t031_stop_capture(struct soc_camera_device *icd)
+ {
+ /* Stop sensor readout */
+- if (reg_clear(icd, MT9T031_OUTPUT_CONTROL, 3) < 0)
++ if (reg_clear(icd, MT9T031_OUTPUT_CONTROL, 2) < 0)
+ return -EIO;
+ return 0;
+ }
+@@ -186,9 +184,9 @@ static int mt9t031_set_bus_param(struct soc_camera_device *icd,
+ return -EINVAL;
+
+ if (flags & SOCAM_PCLK_SAMPLE_FALLING)
+- reg_set(icd, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
+- else
+ reg_clear(icd, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
++ else
++ reg_set(icd, MT9T031_PIXEL_CLOCK_CONTROL, 0x8000);
+
+ return 0;
+ }
+@@ -201,67 +199,73 @@ static unsigned long mt9t031_query_bus_param(struct soc_camera_device *icd)
+ return soc_camera_apply_sensor_flags(icl, MT9T031_BUS_PARAM);
+ }
+
+-static int mt9t031_set_fmt(struct soc_camera_device *icd,
+- __u32 pixfmt, struct v4l2_rect *rect)
++/* Round up minima and round down maxima */
++static void recalculate_limits(struct soc_camera_device *icd,
++ u16 xskip, u16 yskip)
++{
++ icd->x_min = (MT9T031_COLUMN_SKIP + xskip - 1) / xskip;
++ icd->y_min = (MT9T031_ROW_SKIP + yskip - 1) / yskip;
++ icd->width_min = (MT9T031_MIN_WIDTH + xskip - 1) / xskip;
++ icd->height_min = (MT9T031_MIN_HEIGHT + yskip - 1) / yskip;
++ icd->width_max = MT9T031_MAX_WIDTH / xskip;
++ icd->height_max = MT9T031_MAX_HEIGHT / yskip;
++}
++
++static int mt9t031_set_params(struct soc_camera_device *icd,
++ struct v4l2_rect *rect, u16 xskip, u16 yskip)
+ {
+ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
+ int ret;
++ u16 xbin, ybin, width, height, left, top;
+ const u16 hblank = MT9T031_HORIZONTAL_BLANK,
+ vblank = MT9T031_VERTICAL_BLANK;
+- u16 xbin, xskip = mt9t031->xskip, ybin, yskip = mt9t031->yskip,
+- width = rect->width * xskip, height = rect->height * yskip;
+
+- if (pixfmt) {
+- /* S_FMT - use binning and skipping for scaling, recalculate */
+- /* Is this more optimal than just a division? */
+- for (xskip = 8; xskip > 1; xskip--)
+- if (rect->width * xskip <= icd->width_max)
+- break;
++ /* Make sure we don't exceed sensor limits */
++ if (rect->left + rect->width > icd->width_max)
++ rect->left = (icd->width_max - rect->width) / 2 + icd->x_min;
+
+- for (yskip = 8; yskip > 1; yskip--)
+- if (rect->height * yskip <= icd->height_max)
+- break;
++ if (rect->top + rect->height > icd->height_max)
++ rect->top = (icd->height_max - rect->height) / 2 + icd->y_min;
+
+- width = rect->width * xskip;
+- height = rect->height * yskip;
+-
+- dev_dbg(&icd->dev, "xskip %u, width %u, yskip %u, height %u\n",
+- xskip, width, yskip, height);
+- }
++ width = rect->width * xskip;
++ height = rect->height * yskip;
++ left = rect->left * xskip;
++ top = rect->top * yskip;
+
+ xbin = min(xskip, (u16)3);
+ ybin = min(yskip, (u16)3);
+
+- /* Make sure we don't exceed frame limits */
+- if (rect->left + width > icd->width_max)
+- rect->left = (icd->width_max - width) / 2;
+-
+- if (rect->top + height > icd->height_max)
+- rect->top = (icd->height_max - height) / 2;
++ dev_dbg(&icd->dev, "xskip %u, width %u/%u, yskip %u, height %u/%u\n",
++ xskip, width, rect->width, yskip, height, rect->height);
+
+- /* Could just do roundup(rect->left, [xy]bin); but this is cheaper */
++ /* Could just do roundup(rect->left, [xy]bin * 2); but this is cheaper */
+ switch (xbin) {
+ case 2:
+- rect->left = (rect->left + 1) & ~1;
++ left = (left + 3) & ~3;
+ break;
+ case 3:
+- rect->left = roundup(rect->left, 3);
++ left = roundup(left, 6);
+ }
+
+ switch (ybin) {
+ case 2:
+- rect->top = (rect->top + 1) & ~1;
++ top = (top + 3) & ~3;
+ break;
+ case 3:
+- rect->top = roundup(rect->top, 3);
++ top = roundup(top, 6);
+ }
+
++ /* Disable register update, reconfigure atomically */
++ ret = reg_set(icd, MT9T031_OUTPUT_CONTROL, 1);
++ if (ret < 0)
++ return ret;
++
+ /* Blanking and start values - default... */
+ ret = reg_write(icd, MT9T031_HORIZONTAL_BLANKING, hblank);
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_VERTICAL_BLANKING, vblank);
+
+- if (pixfmt) {
++ if (yskip != mt9t031->yskip || xskip != mt9t031->xskip) {
+ /* Binning, skipping */
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_COLUMN_ADDRESS_MODE,
+@@ -270,14 +274,14 @@ static int mt9t031_set_fmt(struct soc_camera_device *icd,
+ ret = reg_write(icd, MT9T031_ROW_ADDRESS_MODE,
+ ((ybin - 1) << 4) | (yskip - 1));
+ }
+- dev_dbg(&icd->dev, "new left %u, top %u\n", rect->left, rect->top);
++ dev_dbg(&icd->dev, "new physical left %u, top %u\n", left, top);
+
+ /* The caller provides a supported format, as guaranteed by
+ * icd->try_fmt_cap(), soc_camera_s_crop() and soc_camera_cropcap() */
+ if (ret >= 0)
+- ret = reg_write(icd, MT9T031_COLUMN_START, rect->left);
++ ret = reg_write(icd, MT9T031_COLUMN_START, left);
+ if (ret >= 0)
+- ret = reg_write(icd, MT9T031_ROW_START, rect->top);
++ ret = reg_write(icd, MT9T031_ROW_START, top);
+ if (ret >= 0)
+ ret = reg_write(icd, MT9T031_WINDOW_WIDTH, width - 1);
+ if (ret >= 0)
+@@ -297,12 +301,58 @@ static int mt9t031_set_fmt(struct soc_camera_device *icd,
+ }
+ }
+
+- if (!ret && pixfmt) {
++ /* Re-enable register update, commit all changes */
++ if (ret >= 0)
++ ret = reg_clear(icd, MT9T031_OUTPUT_CONTROL, 1);
++
++ return ret < 0 ? ret : 0;
++}
++
++static int mt9t031_set_crop(struct soc_camera_device *icd,
++ struct v4l2_rect *rect)
++{
++ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
++
++ /* CROP - no change in scaling, or in limits */
++ return mt9t031_set_params(icd, rect, mt9t031->xskip, mt9t031->yskip);
++}
++
++static int mt9t031_set_fmt(struct soc_camera_device *icd,
++ struct v4l2_format *f)
++{
++ struct mt9t031 *mt9t031 = container_of(icd, struct mt9t031, icd);
++ int ret;
++ u16 xskip, yskip;
++ struct v4l2_rect rect = {
++ .left = icd->x_current,
++ .top = icd->y_current,
++ .width = f->fmt.pix.width,
++ .height = f->fmt.pix.height,
++ };
++
++ /*
++ * try_fmt has put rectangle within limits.
++ * S_FMT - use binning and skipping for scaling, recalculate
++ * limits, used for cropping
++ */
++ /* Is this more optimal than just a division? */
++ for (xskip = 8; xskip > 1; xskip--)
++ if (rect.width * xskip <= MT9T031_MAX_WIDTH)
++ break;
++
++ for (yskip = 8; yskip > 1; yskip--)
++ if (rect.height * yskip <= MT9T031_MAX_HEIGHT)
++ break;
++
++ recalculate_limits(icd, xskip, yskip);
++
++ ret = mt9t031_set_params(icd, &rect, xskip, yskip);
++ if (!ret) {
+ mt9t031->xskip = xskip;
+ mt9t031->yskip = yskip;
+ }
+
+- return ret < 0 ? ret : 0;
++ return ret;
+ }
+
+ static int mt9t031_try_fmt(struct soc_camera_device *icd,
+@@ -310,14 +360,14 @@ static int mt9t031_try_fmt(struct soc_camera_device *icd,
+ {
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+
+- if (pix->height < icd->height_min)
+- pix->height = icd->height_min;
+- if (pix->height > icd->height_max)
+- pix->height = icd->height_max;
+- if (pix->width < icd->width_min)
+- pix->width = icd->width_min;
+- if (pix->width > icd->width_max)
+- pix->width = icd->width_max;
++ if (pix->height < MT9T031_MIN_HEIGHT)
++ pix->height = MT9T031_MIN_HEIGHT;
++ if (pix->height > MT9T031_MAX_HEIGHT)
++ pix->height = MT9T031_MAX_HEIGHT;
++ if (pix->width < MT9T031_MIN_WIDTH)
++ pix->width = MT9T031_MIN_WIDTH;
++ if (pix->width > MT9T031_MAX_WIDTH)
++ pix->width = MT9T031_MAX_WIDTH;
+
+ pix->width &= ~0x01; /* has to be even */
+ pix->height &= ~0x01; /* has to be even */
+@@ -390,6 +440,14 @@ static const struct v4l2_queryctrl mt9t031_controls[] = {
+ .step = 1,
+ .default_value = 0,
+ }, {
++ .id = V4L2_CID_HFLIP,
++ .type = V4L2_CTRL_TYPE_BOOLEAN,
++ .name = "Flip Horizontally",
++ .minimum = 0,
++ .maximum = 1,
++ .step = 1,
++ .default_value = 0,
++ }, {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+@@ -431,6 +489,7 @@ static struct soc_camera_ops mt9t031_ops = {
+ .release = mt9t031_release,
+ .start_capture = mt9t031_start_capture,
+ .stop_capture = mt9t031_stop_capture,
++ .set_crop = mt9t031_set_crop,
+ .set_fmt = mt9t031_set_fmt,
+ .try_fmt = mt9t031_try_fmt,
+ .set_bus_param = mt9t031_set_bus_param,
+@@ -513,21 +572,23 @@ static int mt9t031_set_control(struct soc_camera_device *icd, struct v4l2_contro
+ if (data < 0)
+ return -EIO;
+ } else {
+- /* Pack it into 1.125..15 variable step, register values 9..67 */
++ /* Pack it into 1.125..128 variable step, register values 9..0x7860 */
+ /* We assume qctrl->maximum - qctrl->default_value - 1 > 0 */
+ unsigned long range = qctrl->maximum - qctrl->default_value - 1;
++ /* calculated gain: map 65..127 to 9..1024 step 0.125 */
+ unsigned long gain = ((ctrl->value - qctrl->default_value - 1) *
+- 111 + range / 2) / range + 9;
++ 1015 + range / 2) / range + 9;
+
+- if (gain <= 32)
++ if (gain <= 32) /* calculated gain 9..32 -> 9..32 */
+ data = gain;
+- else if (gain <= 64)
++ else if (gain <= 64) /* calculated gain 33..64 -> 0x51..0x60 */
+ data = ((gain - 32) * 16 + 16) / 32 + 80;
+ else
+- data = ((gain - 64) * 7 + 28) / 56 + 96;
++ /* calculated gain 65..1024 -> (1..120) << 8 + 0x60 */
++ data = (((gain - 64 + 7) * 32) & 0xff00) | 0x60;
+
+- dev_dbg(&icd->dev, "Setting gain from %d to %d\n",
+- reg_read(icd, MT9T031_GLOBAL_GAIN), data);
++ dev_dbg(&icd->dev, "Setting gain from 0x%x to 0x%x\n",
++ reg_read(icd, MT9T031_GLOBAL_GAIN), data);
+ data = reg_write(icd, MT9T031_GLOBAL_GAIN, data);
+ if (data < 0)
+ return -EIO;
+diff --git a/drivers/media/video/mt9v022.c b/drivers/media/video/mt9v022.c
+index b04c8cb..4d3b481 100644
+--- a/drivers/media/video/mt9v022.c
++++ b/drivers/media/video/mt9v022.c
+@@ -13,7 +13,6 @@
+ #include <linux/i2c.h>
+ #include <linux/delay.h>
+ #include <linux/log2.h>
+-#include <linux/gpio.h>
+
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-chip-ident.h>
+@@ -89,9 +88,7 @@ struct mt9v022 {
+ struct i2c_client *client;
+ struct soc_camera_device icd;
+ int model; /* V4L2_IDENT_MT9V022* codes from v4l2-chip-ident.h */
+- int switch_gpio;
+ u16 chip_control;
+- unsigned char datawidth;
+ };
+
+ static int reg_read(struct soc_camera_device *icd, const u8 reg)
+@@ -209,66 +206,6 @@ static int mt9v022_stop_capture(struct soc_camera_device *icd)
+ return 0;
+ }
+
+-static int bus_switch_request(struct mt9v022 *mt9v022, struct soc_camera_link *icl)
+-{
+-#ifdef CONFIG_MT9V022_PCA9536_SWITCH
+- int ret;
+- unsigned int gpio = icl->gpio;
+-
+- if (gpio_is_valid(gpio)) {
+- /* We have a data bus switch. */
+- ret = gpio_request(gpio, "mt9v022");
+- if (ret < 0) {
+- dev_err(&mt9v022->client->dev, "Cannot get GPIO %u\n", gpio);
+- return ret;
+- }
+-
+- ret = gpio_direction_output(gpio, 0);
+- if (ret < 0) {
+- dev_err(&mt9v022->client->dev,
+- "Cannot set GPIO %u to output\n", gpio);
+- gpio_free(gpio);
+- return ret;
+- }
+- }
+-
+- mt9v022->switch_gpio = gpio;
+-#else
+- mt9v022->switch_gpio = -EINVAL;
+-#endif
+- return 0;
+-}
+-
+-static void bus_switch_release(struct mt9v022 *mt9v022)
+-{
+-#ifdef CONFIG_MT9V022_PCA9536_SWITCH
+- if (gpio_is_valid(mt9v022->switch_gpio))
+- gpio_free(mt9v022->switch_gpio);
+-#endif
+-}
+-
+-static int bus_switch_act(struct mt9v022 *mt9v022, int go8bit)
+-{
+-#ifdef CONFIG_MT9V022_PCA9536_SWITCH
+- if (!gpio_is_valid(mt9v022->switch_gpio))
+- return -ENODEV;
+-
+- gpio_set_value_cansleep(mt9v022->switch_gpio, go8bit);
+- return 0;
+-#else
+- return -ENODEV;
+-#endif
+-}
+-
+-static int bus_switch_possible(struct mt9v022 *mt9v022)
+-{
+-#ifdef CONFIG_MT9V022_PCA9536_SWITCH
+- return gpio_is_valid(mt9v022->switch_gpio);
+-#else
+- return 0;
+-#endif
+-}
+-
+ static int mt9v022_set_bus_param(struct soc_camera_device *icd,
+ unsigned long flags)
+ {
+@@ -282,19 +219,17 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd,
+ if (!is_power_of_2(width_flag))
+ return -EINVAL;
+
+- if ((mt9v022->datawidth != 10 && (width_flag == SOCAM_DATAWIDTH_10)) ||
+- (mt9v022->datawidth != 9 && (width_flag == SOCAM_DATAWIDTH_9)) ||
+- (mt9v022->datawidth != 8 && (width_flag == SOCAM_DATAWIDTH_8))) {
+- /* Well, we actually only can do 10 or 8 bits... */
+- if (width_flag == SOCAM_DATAWIDTH_9)
+- return -EINVAL;
+-
+- ret = bus_switch_act(mt9v022,
+- width_flag == SOCAM_DATAWIDTH_8);
+- if (ret < 0)
++ if (icl->set_bus_param) {
++ ret = icl->set_bus_param(icl, width_flag);
++ if (ret)
+ return ret;
+-
+- mt9v022->datawidth = width_flag == SOCAM_DATAWIDTH_8 ? 8 : 10;
++ } else {
++ /*
++ * Without board specific bus width settings we only support the
++ * sensors native bus width
++ */
++ if (width_flag != SOCAM_DATAWIDTH_10)
++ return -EINVAL;
+ }
+
+ flags = soc_camera_apply_sensor_flags(icl, flags);
+@@ -328,44 +263,27 @@ static int mt9v022_set_bus_param(struct soc_camera_device *icd,
+ static unsigned long mt9v022_query_bus_param(struct soc_camera_device *icd)
+ {
+ struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
+- unsigned int width_flag = SOCAM_DATAWIDTH_10;
++ struct soc_camera_link *icl = mt9v022->client->dev.platform_data;
++ unsigned int width_flag;
+
+- if (bus_switch_possible(mt9v022))
+- width_flag |= SOCAM_DATAWIDTH_8;
++ if (icl->query_bus_param)
++ width_flag = icl->query_bus_param(icl) &
++ SOCAM_DATAWIDTH_MASK;
++ else
++ width_flag = SOCAM_DATAWIDTH_10;
+
+ return SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING |
+ SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW |
+ SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW |
+- SOCAM_MASTER | SOCAM_SLAVE |
++ SOCAM_DATA_ACTIVE_HIGH | SOCAM_MASTER | SOCAM_SLAVE |
+ width_flag;
+ }
+
+-static int mt9v022_set_fmt(struct soc_camera_device *icd,
+- __u32 pixfmt, struct v4l2_rect *rect)
++static int mt9v022_set_crop(struct soc_camera_device *icd,
++ struct v4l2_rect *rect)
+ {
+- struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
+ int ret;
+
+- /* The caller provides a supported format, as verified per call to
+- * icd->try_fmt(), datawidth is from our supported format list */
+- switch (pixfmt) {
+- case V4L2_PIX_FMT_GREY:
+- case V4L2_PIX_FMT_Y16:
+- if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM)
+- return -EINVAL;
+- break;
+- case V4L2_PIX_FMT_SBGGR8:
+- case V4L2_PIX_FMT_SBGGR16:
+- if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATC)
+- return -EINVAL;
+- break;
+- case 0:
+- /* No format change, only geometry */
+- break;
+- default:
+- return -EINVAL;
+- }
+-
+ /* Like in example app. Contradicts the datasheet though */
+ ret = reg_read(icd, MT9V022_AEC_AGC_ENABLE);
+ if (ret >= 0) {
+@@ -403,6 +321,42 @@ static int mt9v022_set_fmt(struct soc_camera_device *icd,
+ return 0;
+ }
+
++static int mt9v022_set_fmt(struct soc_camera_device *icd,
++ struct v4l2_format *f)
++{
++ struct mt9v022 *mt9v022 = container_of(icd, struct mt9v022, icd);
++ struct v4l2_pix_format *pix = &f->fmt.pix;
++ struct v4l2_rect rect = {
++ .left = icd->x_current,
++ .top = icd->y_current,
++ .width = pix->width,
++ .height = pix->height,
++ };
++
++ /* The caller provides a supported format, as verified per call to
++ * icd->try_fmt(), datawidth is from our supported format list */
++ switch (pix->pixelformat) {
++ case V4L2_PIX_FMT_GREY:
++ case V4L2_PIX_FMT_Y16:
++ if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATM)
++ return -EINVAL;
++ break;
++ case V4L2_PIX_FMT_SBGGR8:
++ case V4L2_PIX_FMT_SBGGR16:
++ if (mt9v022->model != V4L2_IDENT_MT9V022IX7ATC)
++ return -EINVAL;
++ break;
++ case 0:
++ /* No format change, only geometry */
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ /* No support for scaling on this camera, just crop. */
++ return mt9v022_set_crop(icd, &rect);
++}
++
+ static int mt9v022_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+ {
+@@ -544,6 +498,7 @@ static struct soc_camera_ops mt9v022_ops = {
+ .release = mt9v022_release,
+ .start_capture = mt9v022_start_capture,
+ .stop_capture = mt9v022_stop_capture,
++ .set_crop = mt9v022_set_crop,
+ .set_fmt = mt9v022_set_fmt,
+ .try_fmt = mt9v022_try_fmt,
+ .set_bus_param = mt9v022_set_bus_param,
+@@ -699,6 +654,7 @@ static int mt9v022_video_probe(struct soc_camera_device *icd)
+ struct soc_camera_link *icl = mt9v022->client->dev.platform_data;
+ s32 data;
+ int ret;
++ unsigned long flags;
+
+ if (!icd->dev.parent ||
+ to_soc_camera_host(icd->dev.parent)->nr != icd->iface)
+@@ -732,22 +688,36 @@ static int mt9v022_video_probe(struct soc_camera_device *icd)
+ ret = reg_write(icd, MT9V022_PIXEL_OPERATION_MODE, 4 | 0x11);
+ mt9v022->model = V4L2_IDENT_MT9V022IX7ATC;
+ icd->formats = mt9v022_colour_formats;
+- if (gpio_is_valid(icl->gpio))
+- icd->num_formats = ARRAY_SIZE(mt9v022_colour_formats);
+- else
+- icd->num_formats = 1;
+ } else {
+ ret = reg_write(icd, MT9V022_PIXEL_OPERATION_MODE, 0x11);
+ mt9v022->model = V4L2_IDENT_MT9V022IX7ATM;
+ icd->formats = mt9v022_monochrome_formats;
+- if (gpio_is_valid(icl->gpio))
+- icd->num_formats = ARRAY_SIZE(mt9v022_monochrome_formats);
+- else
+- icd->num_formats = 1;
+ }
+
+- if (!ret)
+- ret = soc_camera_video_start(icd);
++ if (ret < 0)
++ goto eisis;
++
++ icd->num_formats = 0;
++
++ /*
++ * This is a 10bit sensor, so by default we only allow 10bit.
++ * The platform may support different bus widths due to
++ * different routing of the data lines.
++ */
++ if (icl->query_bus_param)
++ flags = icl->query_bus_param(icl);
++ else
++ flags = SOCAM_DATAWIDTH_10;
++
++ if (flags & SOCAM_DATAWIDTH_10)
++ icd->num_formats++;
++ else
++ icd->formats++;
++
++ if (flags & SOCAM_DATAWIDTH_8)
++ icd->num_formats++;
++
++ ret = soc_camera_video_start(icd);
+ if (ret < 0)
+ goto eisis;
+
+@@ -812,14 +782,6 @@ static int mt9v022_probe(struct i2c_client *client,
+ icd->height_max = 480;
+ icd->y_skip_top = 1;
+ icd->iface = icl->bus_id;
+- /* Default datawidth - this is the only width this camera (normally)
+- * supports. It is only with extra logic that it can support
+- * other widths. Therefore it seems to be a sensible default. */
+- mt9v022->datawidth = 10;
+-
+- ret = bus_switch_request(mt9v022, icl);
+- if (ret)
+- goto eswinit;
+
+ ret = soc_camera_device_register(icd);
+ if (ret)
+@@ -828,8 +790,6 @@ static int mt9v022_probe(struct i2c_client *client,
+ return 0;
+
+ eisdr:
+- bus_switch_release(mt9v022);
+-eswinit:
+ kfree(mt9v022);
+ return ret;
+ }
+@@ -839,7 +799,6 @@ static int mt9v022_remove(struct i2c_client *client)
+ struct mt9v022 *mt9v022 = i2c_get_clientdata(client);
+
+ soc_camera_device_unregister(&mt9v022->icd);
+- bus_switch_release(mt9v022);
+ kfree(mt9v022);
+
+ return 0;
+diff --git a/drivers/media/video/mx3_camera.c b/drivers/media/video/mx3_camera.c
+new file mode 100644
+index 0000000..70629e1
+--- /dev/null
++++ b/drivers/media/video/mx3_camera.c
+@@ -0,0 +1,1220 @@
++/*
++ * V4L2 Driver for i.MX3x camera host
++ *
++ * Copyright (C) 2008
++ * Guennadi Liakhovetski, DENX Software Engineering, <lg@denx.de>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License version 2 as
++ * published by the Free Software Foundation.
++ */
++
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/version.h>
++#include <linux/videodev2.h>
++#include <linux/platform_device.h>
++#include <linux/clk.h>
++#include <linux/vmalloc.h>
++#include <linux/interrupt.h>
++
++#include <media/v4l2-common.h>
++#include <media/v4l2-dev.h>
++#include <media/videobuf-dma-contig.h>
++#include <media/soc_camera.h>
++
++#include <mach/ipu.h>
++#include <mach/mx3_camera.h>
++
++#define MX3_CAM_DRV_NAME "mx3-camera"
++
++/* CMOS Sensor Interface Registers */
++#define CSI_REG_START 0x60
++
++#define CSI_SENS_CONF (0x60 - CSI_REG_START)
++#define CSI_SENS_FRM_SIZE (0x64 - CSI_REG_START)
++#define CSI_ACT_FRM_SIZE (0x68 - CSI_REG_START)
++#define CSI_OUT_FRM_CTRL (0x6C - CSI_REG_START)
++#define CSI_TST_CTRL (0x70 - CSI_REG_START)
++#define CSI_CCIR_CODE_1 (0x74 - CSI_REG_START)
++#define CSI_CCIR_CODE_2 (0x78 - CSI_REG_START)
++#define CSI_CCIR_CODE_3 (0x7C - CSI_REG_START)
++#define CSI_FLASH_STROBE_1 (0x80 - CSI_REG_START)
++#define CSI_FLASH_STROBE_2 (0x84 - CSI_REG_START)
++
++#define CSI_SENS_CONF_VSYNC_POL_SHIFT 0
++#define CSI_SENS_CONF_HSYNC_POL_SHIFT 1
++#define CSI_SENS_CONF_DATA_POL_SHIFT 2
++#define CSI_SENS_CONF_PIX_CLK_POL_SHIFT 3
++#define CSI_SENS_CONF_SENS_PRTCL_SHIFT 4
++#define CSI_SENS_CONF_SENS_CLKSRC_SHIFT 7
++#define CSI_SENS_CONF_DATA_FMT_SHIFT 8
++#define CSI_SENS_CONF_DATA_WIDTH_SHIFT 10
++#define CSI_SENS_CONF_EXT_VSYNC_SHIFT 15
++#define CSI_SENS_CONF_DIVRATIO_SHIFT 16
++
++#define CSI_SENS_CONF_DATA_FMT_RGB_YUV444 (0UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
++#define CSI_SENS_CONF_DATA_FMT_YUV422 (2UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
++#define CSI_SENS_CONF_DATA_FMT_BAYER (3UL << CSI_SENS_CONF_DATA_FMT_SHIFT)
++
++#define MAX_VIDEO_MEM 16
++
++struct mx3_camera_buffer {
++ /* common v4l buffer stuff -- must be first */
++ struct videobuf_buffer vb;
++ const struct soc_camera_data_format *fmt;
++
++ /* One descriptot per scatterlist (per frame) */
++ struct dma_async_tx_descriptor *txd;
++
++ /* We have to "build" a scatterlist ourselves - one element per frame */
++ struct scatterlist sg;
++};
++
++/**
++ * struct mx3_camera_dev - i.MX3x camera (CSI) object
++ * @dev: camera device, to which the coherent buffer is attached
++ * @icd: currently attached camera sensor
++ * @clk: pointer to clock
++ * @base: remapped register base address
++ * @pdata: platform data
++ * @platform_flags: platform flags
++ * @mclk: master clock frequency in Hz
++ * @capture: list of capture videobuffers
++ * @lock: protects video buffer lists
++ * @active: active video buffer
++ * @idmac_channel: array of pointers to IPU DMAC DMA channels
++ * @soc_host: embedded soc_host object
++ */
++struct mx3_camera_dev {
++ struct device *dev;
++ /*
++ * i.MX3x is only supposed to handle one camera on its Camera Sensor
++ * Interface. If anyone ever builds hardware to enable more than one
++ * camera _simultaneously_, they will have to modify this driver too
++ */
++ struct soc_camera_device *icd;
++ struct clk *clk;
++
++ void __iomem *base;
++
++ struct mx3_camera_pdata *pdata;
++
++ unsigned long platform_flags;
++ unsigned long mclk;
++
++ struct list_head capture;
++ spinlock_t lock; /* Protects video buffer lists */
++ struct mx3_camera_buffer *active;
++
++ /* IDMAC / dmaengine interface */
++ struct idmac_channel *idmac_channel[1]; /* We need one channel */
++
++ struct soc_camera_host soc_host;
++};
++
++struct dma_chan_request {
++ struct mx3_camera_dev *mx3_cam;
++ enum ipu_channel id;
++};
++
++static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt);
++
++static u32 csi_reg_read(struct mx3_camera_dev *mx3, off_t reg)
++{
++ return __raw_readl(mx3->base + reg);
++}
++
++static void csi_reg_write(struct mx3_camera_dev *mx3, u32 value, off_t reg)
++{
++ __raw_writel(value, mx3->base + reg);
++}
++
++/* Called from the IPU IDMAC ISR */
++static void mx3_cam_dma_done(void *arg)
++{
++ struct idmac_tx_desc *desc = to_tx_desc(arg);
++ struct dma_chan *chan = desc->txd.chan;
++ struct idmac_channel *ichannel = to_idmac_chan(chan);
++ struct mx3_camera_dev *mx3_cam = ichannel->client;
++ struct videobuf_buffer *vb;
++
++ dev_dbg(chan->device->dev, "callback cookie %d, active DMA 0x%08x\n",
++ desc->txd.cookie, mx3_cam->active ? sg_dma_address(&mx3_cam->active->sg) : 0);
++
++ spin_lock(&mx3_cam->lock);
++ if (mx3_cam->active) {
++ vb = &mx3_cam->active->vb;
++
++ list_del_init(&vb->queue);
++ vb->state = VIDEOBUF_DONE;
++ do_gettimeofday(&vb->ts);
++ vb->field_count++;
++ wake_up(&vb->done);
++ }
++
++ if (list_empty(&mx3_cam->capture)) {
++ mx3_cam->active = NULL;
++ spin_unlock(&mx3_cam->lock);
++
++ /*
++ * stop capture - without further buffers IPU_CHA_BUF0_RDY will
++ * not get updated
++ */
++ return;
++ }
++
++ mx3_cam->active = list_entry(mx3_cam->capture.next,
++ struct mx3_camera_buffer, vb.queue);
++ mx3_cam->active->vb.state = VIDEOBUF_ACTIVE;
++ spin_unlock(&mx3_cam->lock);
++}
++
++static void free_buffer(struct videobuf_queue *vq, struct mx3_camera_buffer *buf)
++{
++ struct soc_camera_device *icd = vq->priv_data;
++ struct videobuf_buffer *vb = &buf->vb;
++ struct dma_async_tx_descriptor *txd = buf->txd;
++ struct idmac_channel *ichan;
++
++ BUG_ON(in_interrupt());
++
++ dev_dbg(&icd->dev, "%s (vb=0x%p) 0x%08lx %d\n", __func__,
++ vb, vb->baddr, vb->bsize);
++
++ /*
++ * This waits until this buffer is out of danger, i.e., until it is no
++ * longer in STATE_QUEUED or STATE_ACTIVE
++ */
++ videobuf_waiton(vb, 0, 0);
++ if (txd) {
++ ichan = to_idmac_chan(txd->chan);
++ async_tx_ack(txd);
++ }
++ videobuf_dma_contig_free(vq, vb);
++ buf->txd = NULL;
++
++ vb->state = VIDEOBUF_NEEDS_INIT;
++}
++
++/*
++ * Videobuf operations
++ */
++
++/*
++ * Calculate the __buffer__ (not data) size and number of buffers.
++ * Called with .vb_lock held
++ */
++static int mx3_videobuf_setup(struct videobuf_queue *vq, unsigned int *count,
++ unsigned int *size)
++{
++ struct soc_camera_device *icd = vq->priv_data;
++ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
++ struct mx3_camera_dev *mx3_cam = ici->priv;
++ /*
++ * bits-per-pixel (depth) as specified in camera's pixel format does
++ * not necessarily match what the camera interface writes to RAM, but
++ * it should be good enough for now.
++ */
++ unsigned int bpp = DIV_ROUND_UP(icd->current_fmt->depth, 8);
++
++ if (!mx3_cam->idmac_channel[0])
++ return -EINVAL;
++
++ *size = icd->width * icd->height * bpp;
++
++ if (!*count)
++ *count = 32;
++
++ if (*size * *count > MAX_VIDEO_MEM * 1024 * 1024)
++ *count = MAX_VIDEO_MEM * 1024 * 1024 / *size;
++
++ return 0;
++}
++
++/* Called with .vb_lock held */
++static int mx3_videobuf_prepare(struct videobuf_queue *vq,
++ struct videobuf_buffer *vb, enum v4l2_field field)
++{
++ struct soc_camera_device *icd = vq->priv_data;
++ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
++ struct mx3_camera_dev *mx3_cam = ici->priv;
++ struct mx3_camera_buffer *buf =
++ container_of(vb, struct mx3_camera_buffer, vb);
++ /* current_fmt _must_ always be set */
++ size_t new_size = icd->width * icd->height *
++ ((icd->current_fmt->depth + 7) >> 3);
++ int ret;
++
++ /*
++ * I think, in buf_prepare you only have to protect global data,
++ * the actual buffer is yours
++ */
++
++ if (buf->fmt != icd->current_fmt ||
++ vb->width != icd->width ||
++ vb->height != icd->height ||
++ vb->field != field) {
++ buf->fmt = icd->current_fmt;
++ vb->width = icd->width;
++ vb->height = icd->height;
++ vb->field = field;
++ if (vb->state != VIDEOBUF_NEEDS_INIT)
++ free_buffer(vq, buf);
++ }
++
++ if (vb->baddr && vb->bsize < new_size) {
++ /* User provided buffer, but it is too small */
++ ret = -ENOMEM;
++ goto out;
++ }
++
++ if (vb->state == VIDEOBUF_NEEDS_INIT) {
++ struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
++ struct scatterlist *sg = &buf->sg;
++
++ /*
++ * The total size of video-buffers that will be allocated / mapped.
++ * *size that we calculated in videobuf_setup gets assigned to
++ * vb->bsize, and now we use the same calculation to get vb->size.
++ */
++ vb->size = new_size;
++
++ /* This actually (allocates and) maps buffers */
++ ret = videobuf_iolock(vq, vb, NULL);
++ if (ret)
++ goto fail;
++
++ /*
++ * We will have to configure the IDMAC channel. It has two slots
++ * for DMA buffers, we shall enter the first two buffers there,
++ * and then submit new buffers in DMA-ready interrupts
++ */
++ sg_init_table(sg, 1);
++ sg_dma_address(sg) = videobuf_to_dma_contig(vb);
++ sg_dma_len(sg) = vb->size;
++
++ buf->txd = ichan->dma_chan.device->device_prep_slave_sg(
++ &ichan->dma_chan, sg, 1, DMA_FROM_DEVICE,
++ DMA_PREP_INTERRUPT);
++ if (!buf->txd) {
++ ret = -EIO;
++ goto fail;
++ }
++
++ buf->txd->callback_param = buf->txd;
++ buf->txd->callback = mx3_cam_dma_done;
++
++ vb->state = VIDEOBUF_PREPARED;
++ }
++
++ return 0;
++
++fail:
++ free_buffer(vq, buf);
++out:
++ return ret;
++}
++
++static enum pixel_fmt fourcc_to_ipu_pix(__u32 fourcc)
++{
++ /* Add more formats as need arises and test possibilities appear... */
++ switch (fourcc) {
++ case V4L2_PIX_FMT_RGB565:
++ return IPU_PIX_FMT_RGB565;
++ case V4L2_PIX_FMT_RGB24:
++ return IPU_PIX_FMT_RGB24;
++ case V4L2_PIX_FMT_RGB332:
++ return IPU_PIX_FMT_RGB332;
++ case V4L2_PIX_FMT_YUV422P:
++ return IPU_PIX_FMT_YVU422P;
++ default:
++ return IPU_PIX_FMT_GENERIC;
++ }
++}
++
++/* Called with .vb_lock held */
++static void mx3_videobuf_queue(struct videobuf_queue *vq,
++ struct videobuf_buffer *vb)
++{
++ struct soc_camera_device *icd = vq->priv_data;
++ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
++ struct mx3_camera_dev *mx3_cam = ici->priv;
++ struct mx3_camera_buffer *buf =
++ container_of(vb, struct mx3_camera_buffer, vb);
++ struct dma_async_tx_descriptor *txd = buf->txd;
++ struct idmac_channel *ichan = to_idmac_chan(txd->chan);
++ struct idmac_video_param *video = &ichan->params.video;
++ const struct soc_camera_data_format *data_fmt = icd->current_fmt;
++ dma_cookie_t cookie;
++ unsigned long flags;
++
++ /* This is the configuration of one sg-element */
++ video->out_pixel_fmt = fourcc_to_ipu_pix(data_fmt->fourcc);
++ video->out_width = icd->width;
++ video->out_height = icd->height;
++ video->out_stride = icd->width;
++
++#ifdef DEBUG
++ /* helps to see what DMA actually has written */
++ memset((void *)vb->baddr, 0xaa, vb->bsize);
++#endif
++
++ spin_lock_irqsave(&mx3_cam->lock, flags);
++
++ list_add_tail(&vb->queue, &mx3_cam->capture);
++
++ if (!mx3_cam->active) {
++ mx3_cam->active = buf;
++ vb->state = VIDEOBUF_ACTIVE;
++ } else {
++ vb->state = VIDEOBUF_QUEUED;
++ }
++
++ spin_unlock_irqrestore(&mx3_cam->lock, flags);
++
++ cookie = txd->tx_submit(txd);
++ dev_dbg(&icd->dev, "Submitted cookie %d DMA 0x%08x\n", cookie, sg_dma_address(&buf->sg));
++ if (cookie >= 0)
++ return;
++
++ /* Submit error */
++ vb->state = VIDEOBUF_PREPARED;
++
++ spin_lock_irqsave(&mx3_cam->lock, flags);
++
++ list_del_init(&vb->queue);
++
++ if (mx3_cam->active == buf)
++ mx3_cam->active = NULL;
++
++ spin_unlock_irqrestore(&mx3_cam->lock, flags);
++}
++
++/* Called with .vb_lock held */
++static void mx3_videobuf_release(struct videobuf_queue *vq,
++ struct videobuf_buffer *vb)
++{
++ struct soc_camera_device *icd = vq->priv_data;
++ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
++ struct mx3_camera_dev *mx3_cam = ici->priv;
++ struct mx3_camera_buffer *buf =
++ container_of(vb, struct mx3_camera_buffer, vb);
++ unsigned long flags;
++
++ dev_dbg(&icd->dev, "Release%s DMA 0x%08x (state %d), queue %sempty\n",
++ mx3_cam->active == buf ? " active" : "", sg_dma_address(&buf->sg),
++ vb->state, list_empty(&vb->queue) ? "" : "not ");
++ spin_lock_irqsave(&mx3_cam->lock, flags);
++ if ((vb->state == VIDEOBUF_ACTIVE || vb->state == VIDEOBUF_QUEUED) &&
++ !list_empty(&vb->queue)) {
++ vb->state = VIDEOBUF_ERROR;
++
++ list_del_init(&vb->queue);
++ if (mx3_cam->active == buf)
++ mx3_cam->active = NULL;
++ }
++ spin_unlock_irqrestore(&mx3_cam->lock, flags);
++ free_buffer(vq, buf);
++}
++
++static struct videobuf_queue_ops mx3_videobuf_ops = {
++ .buf_setup = mx3_videobuf_setup,
++ .buf_prepare = mx3_videobuf_prepare,
++ .buf_queue = mx3_videobuf_queue,
++ .buf_release = mx3_videobuf_release,
++};
++
++static void mx3_camera_init_videobuf(struct videobuf_queue *q,
++ struct soc_camera_device *icd)
++{
++ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
++ struct mx3_camera_dev *mx3_cam = ici->priv;
++
++ videobuf_queue_dma_contig_init(q, &mx3_videobuf_ops, mx3_cam->dev,
++ &mx3_cam->lock,
++ V4L2_BUF_TYPE_VIDEO_CAPTURE,
++ V4L2_FIELD_NONE,
++ sizeof(struct mx3_camera_buffer), icd);
++}
++
++/* First part of ipu_csi_init_interface() */
++static void mx3_camera_activate(struct mx3_camera_dev *mx3_cam,
++ struct soc_camera_device *icd)
++{
++ u32 conf;
++ long rate;
++
++ /* Set default size: ipu_csi_set_window_size() */
++ csi_reg_write(mx3_cam, (640 - 1) | ((480 - 1) << 16), CSI_ACT_FRM_SIZE);
++ /* ...and position to 0:0: ipu_csi_set_window_pos() */
++ conf = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
++ csi_reg_write(mx3_cam, conf, CSI_OUT_FRM_CTRL);
++
++ /* We use only gated clock synchronisation mode so far */
++ conf = 0 << CSI_SENS_CONF_SENS_PRTCL_SHIFT;
++
++ /* Set generic data, platform-biggest bus-width */
++ conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
++
++ if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15)
++ conf |= 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
++ else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10)
++ conf |= 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
++ else if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8)
++ conf |= 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
++ else/* if (mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4)*/
++ conf |= 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
++
++ if (mx3_cam->platform_flags & MX3_CAMERA_CLK_SRC)
++ conf |= 1 << CSI_SENS_CONF_SENS_CLKSRC_SHIFT;
++ if (mx3_cam->platform_flags & MX3_CAMERA_EXT_VSYNC)
++ conf |= 1 << CSI_SENS_CONF_EXT_VSYNC_SHIFT;
++ if (mx3_cam->platform_flags & MX3_CAMERA_DP)
++ conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
++ if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
++ conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
++ if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
++ conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
++ if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
++ conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
++
++ /* ipu_csi_init_interface() */
++ csi_reg_write(mx3_cam, conf, CSI_SENS_CONF);
++
++ clk_enable(mx3_cam->clk);
++ rate = clk_round_rate(mx3_cam->clk, mx3_cam->mclk);
++ dev_dbg(&icd->dev, "Set SENS_CONF to %x, rate %ld\n", conf, rate);
++ if (rate)
++ clk_set_rate(mx3_cam->clk, rate);
++}
++
++/* Called with .video_lock held */
++static int mx3_camera_add_device(struct soc_camera_device *icd)
++{
++ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
++ struct mx3_camera_dev *mx3_cam = ici->priv;
++ int ret;
++
++ if (mx3_cam->icd) {
++ ret = -EBUSY;
++ goto ebusy;
++ }
++
++ mx3_camera_activate(mx3_cam, icd);
++ ret = icd->ops->init(icd);
++ if (ret < 0) {
++ clk_disable(mx3_cam->clk);
++ goto einit;
++ }
++
++ mx3_cam->icd = icd;
++
++einit:
++ebusy:
++ if (!ret)
++ dev_info(&icd->dev, "MX3 Camera driver attached to camera %d\n",
++ icd->devnum);
++
++ return ret;
++}
++
++/* Called with .video_lock held */
++static void mx3_camera_remove_device(struct soc_camera_device *icd)
++{
++ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
++ struct mx3_camera_dev *mx3_cam = ici->priv;
++ struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
++
++ BUG_ON(icd != mx3_cam->icd);
++
++ if (*ichan) {
++ dma_release_channel(&(*ichan)->dma_chan);
++ *ichan = NULL;
++ }
++
++ icd->ops->release(icd);
++
++ clk_disable(mx3_cam->clk);
++
++ mx3_cam->icd = NULL;
++
++ dev_info(&icd->dev, "MX3 Camera driver detached from camera %d\n",
++ icd->devnum);
++}
++
++static bool channel_change_requested(struct soc_camera_device *icd,
++ struct v4l2_rect *rect)
++{
++ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
++ struct mx3_camera_dev *mx3_cam = ici->priv;
++ struct idmac_channel *ichan = mx3_cam->idmac_channel[0];
++
++ /* Do buffers have to be re-allocated or channel re-configured? */
++ return ichan && rect->width * rect->height > icd->width * icd->height;
++}
++
++static int test_platform_param(struct mx3_camera_dev *mx3_cam,
++ unsigned char buswidth, unsigned long *flags)
++{
++ /*
++ * Platform specified synchronization and pixel clock polarities are
++ * only a recommendation and are only used during probing. MX3x
++ * camera interface only works in master mode, i.e., uses HSYNC and
++ * VSYNC signals from the sensor
++ */
++ *flags = SOCAM_MASTER |
++ SOCAM_HSYNC_ACTIVE_HIGH |
++ SOCAM_HSYNC_ACTIVE_LOW |
++ SOCAM_VSYNC_ACTIVE_HIGH |
++ SOCAM_VSYNC_ACTIVE_LOW |
++ SOCAM_PCLK_SAMPLE_RISING |
++ SOCAM_PCLK_SAMPLE_FALLING |
++ SOCAM_DATA_ACTIVE_HIGH |
++ SOCAM_DATA_ACTIVE_LOW;
++
++ /* If requested data width is supported by the platform, use it or any
++ * possible lower value - i.MX31 is smart enough to schift bits */
++ switch (buswidth) {
++ case 15:
++ if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15))
++ return -EINVAL;
++ *flags |= SOCAM_DATAWIDTH_15 | SOCAM_DATAWIDTH_10 |
++ SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4;
++ break;
++ case 10:
++ if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10))
++ return -EINVAL;
++ *flags |= SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_8 |
++ SOCAM_DATAWIDTH_4;
++ break;
++ case 8:
++ if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8))
++ return -EINVAL;
++ *flags |= SOCAM_DATAWIDTH_8 | SOCAM_DATAWIDTH_4;
++ break;
++ case 4:
++ if (!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4))
++ return -EINVAL;
++ *flags |= SOCAM_DATAWIDTH_4;
++ break;
++ default:
++ dev_info(mx3_cam->dev, "Unsupported bus width %d\n", buswidth);
++ return -EINVAL;
++ }
++
++ return 0;
++}
++
++static int mx3_camera_try_bus_param(struct soc_camera_device *icd,
++ const unsigned int depth)
++{
++ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
++ struct mx3_camera_dev *mx3_cam = ici->priv;
++ unsigned long bus_flags, camera_flags;
++ int ret = test_platform_param(mx3_cam, depth, &bus_flags);
++
++ dev_dbg(&ici->dev, "requested bus width %d bit: %d\n", depth, ret);
++
++ if (ret < 0)
++ return ret;
++
++ camera_flags = icd->ops->query_bus_param(icd);
++
++ ret = soc_camera_bus_param_compatible(camera_flags, bus_flags);
++ if (ret < 0)
++ dev_warn(&icd->dev, "Flags incompatible: camera %lx, host %lx\n",
++ camera_flags, bus_flags);
++
++ return ret;
++}
++
++static bool chan_filter(struct dma_chan *chan, void *arg)
++{
++ struct dma_chan_request *rq = arg;
++ struct mx3_camera_pdata *pdata;
++
++ if (!rq)
++ return false;
++
++ pdata = rq->mx3_cam->dev->platform_data;
++
++ return rq->id == chan->chan_id &&
++ pdata->dma_dev == chan->device->dev;
++}
++
++static const struct soc_camera_data_format mx3_camera_formats[] = {
++ {
++ .name = "Bayer (sRGB) 8 bit",
++ .depth = 8,
++ .fourcc = V4L2_PIX_FMT_SBGGR8,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ }, {
++ .name = "Monochrome 8 bit",
++ .depth = 8,
++ .fourcc = V4L2_PIX_FMT_GREY,
++ .colorspace = V4L2_COLORSPACE_JPEG,
++ },
++};
++
++static bool buswidth_supported(struct soc_camera_host *ici, int depth)
++{
++ struct mx3_camera_dev *mx3_cam = ici->priv;
++
++ switch (depth) {
++ case 4:
++ return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_4);
++ case 8:
++ return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_8);
++ case 10:
++ return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_10);
++ case 15:
++ return !!(mx3_cam->platform_flags & MX3_CAMERA_DATAWIDTH_15);
++ }
++ return false;
++}
++
++static int mx3_camera_get_formats(struct soc_camera_device *icd, int idx,
++ struct soc_camera_format_xlate *xlate)
++{
++ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
++ int formats = 0, buswidth, ret;
++
++ buswidth = icd->formats[idx].depth;
++
++ if (!buswidth_supported(ici, buswidth))
++ return 0;
++
++ ret = mx3_camera_try_bus_param(icd, buswidth);
++ if (ret < 0)
++ return 0;
++
++ switch (icd->formats[idx].fourcc) {
++ case V4L2_PIX_FMT_SGRBG10:
++ formats++;
++ if (xlate) {
++ xlate->host_fmt = &mx3_camera_formats[0];
++ xlate->cam_fmt = icd->formats + idx;
++ xlate->buswidth = buswidth;
++ xlate++;
++ dev_dbg(&ici->dev, "Providing format %s using %s\n",
++ mx3_camera_formats[0].name,
++ icd->formats[idx].name);
++ }
++ goto passthrough;
++ case V4L2_PIX_FMT_Y16:
++ formats++;
++ if (xlate) {
++ xlate->host_fmt = &mx3_camera_formats[1];
++ xlate->cam_fmt = icd->formats + idx;
++ xlate->buswidth = buswidth;
++ xlate++;
++ dev_dbg(&ici->dev, "Providing format %s using %s\n",
++ mx3_camera_formats[0].name,
++ icd->formats[idx].name);
++ }
++ default:
++passthrough:
++ /* Generic pass-through */
++ formats++;
++ if (xlate) {
++ xlate->host_fmt = icd->formats + idx;
++ xlate->cam_fmt = icd->formats + idx;
++ xlate->buswidth = buswidth;
++ xlate++;
++ dev_dbg(&ici->dev,
++ "Providing format %s in pass-through mode\n",
++ icd->formats[idx].name);
++ }
++ }
++
++ return formats;
++}
++
++static void configure_geometry(struct mx3_camera_dev *mx3_cam,
++ struct v4l2_rect *rect)
++{
++ u32 ctrl, width_field, height_field;
++
++ /* Setup frame size - this cannot be changed on-the-fly... */
++ width_field = rect->width - 1;
++ height_field = rect->height - 1;
++ csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_SENS_FRM_SIZE);
++
++ csi_reg_write(mx3_cam, width_field << 16, CSI_FLASH_STROBE_1);
++ csi_reg_write(mx3_cam, (height_field << 16) | 0x22, CSI_FLASH_STROBE_2);
++
++ csi_reg_write(mx3_cam, width_field | (height_field << 16), CSI_ACT_FRM_SIZE);
++
++ /* ...and position */
++ ctrl = csi_reg_read(mx3_cam, CSI_OUT_FRM_CTRL) & 0xffff0000;
++ /* Sensor does the cropping */
++ csi_reg_write(mx3_cam, ctrl | 0 | (0 << 8), CSI_OUT_FRM_CTRL);
++
++ /*
++ * No need to free resources here if we fail, we'll see if we need to
++ * do this next time we are called
++ */
++}
++
++static int acquire_dma_channel(struct mx3_camera_dev *mx3_cam)
++{
++ dma_cap_mask_t mask;
++ struct dma_chan *chan;
++ struct idmac_channel **ichan = &mx3_cam->idmac_channel[0];
++ /* We have to use IDMAC_IC_7 for Bayer / generic data */
++ struct dma_chan_request rq = {.mx3_cam = mx3_cam,
++ .id = IDMAC_IC_7};
++
++ if (*ichan) {
++ struct videobuf_buffer *vb, *_vb;
++ dma_release_channel(&(*ichan)->dma_chan);
++ *ichan = NULL;
++ mx3_cam->active = NULL;
++ list_for_each_entry_safe(vb, _vb, &mx3_cam->capture, queue) {
++ list_del_init(&vb->queue);
++ vb->state = VIDEOBUF_ERROR;
++ wake_up(&vb->done);
++ }
++ }
++
++ dma_cap_zero(mask);
++ dma_cap_set(DMA_SLAVE, mask);
++ dma_cap_set(DMA_PRIVATE, mask);
++ chan = dma_request_channel(mask, chan_filter, &rq);
++ if (!chan)
++ return -EBUSY;
++
++ *ichan = to_idmac_chan(chan);
++ (*ichan)->client = mx3_cam;
++
++ return 0;
++}
++
++static int mx3_camera_set_crop(struct soc_camera_device *icd,
++ struct v4l2_rect *rect)
++{
++ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
++ struct mx3_camera_dev *mx3_cam = ici->priv;
++
++ /*
++ * We now know pixel formats and can decide upon DMA-channel(s)
++ * So far only direct camera-to-memory is supported
++ */
++ if (channel_change_requested(icd, rect)) {
++ int ret = acquire_dma_channel(mx3_cam);
++ if (ret < 0)
++ return ret;
++ }
++
++ configure_geometry(mx3_cam, rect);
++
++ return icd->ops->set_crop(icd, rect);
++}
++
++static int mx3_camera_set_fmt(struct soc_camera_device *icd,
++ struct v4l2_format *f)
++{
++ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
++ struct mx3_camera_dev *mx3_cam = ici->priv;
++ const struct soc_camera_format_xlate *xlate;
++ struct v4l2_pix_format *pix = &f->fmt.pix;
++ struct v4l2_rect rect = {
++ .left = icd->x_current,
++ .top = icd->y_current,
++ .width = pix->width,
++ .height = pix->height,
++ };
++ int ret;
++
++ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
++ if (!xlate) {
++ dev_warn(&ici->dev, "Format %x not found\n", pix->pixelformat);
++ return -EINVAL;
++ }
++
++ ret = acquire_dma_channel(mx3_cam);
++ if (ret < 0)
++ return ret;
++
++ /*
++ * Might have to perform a complete interface initialisation like in
++ * ipu_csi_init_interface() in mxc_v4l2_s_param(). Also consider
++ * mxc_v4l2_s_fmt()
++ */
++
++ configure_geometry(mx3_cam, &rect);
++
++ ret = icd->ops->set_fmt(icd, f);
++ if (!ret) {
++ icd->buswidth = xlate->buswidth;
++ icd->current_fmt = xlate->host_fmt;
++ }
++
++ return ret;
++}
++
++static int mx3_camera_try_fmt(struct soc_camera_device *icd,
++ struct v4l2_format *f)
++{
++ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
++ const struct soc_camera_format_xlate *xlate;
++ struct v4l2_pix_format *pix = &f->fmt.pix;
++ __u32 pixfmt = pix->pixelformat;
++ enum v4l2_field field;
++ int ret;
++
++ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
++ if (pixfmt && !xlate) {
++ dev_warn(&ici->dev, "Format %x not found\n", pixfmt);
++ return -EINVAL;
++ }
++
++ /* limit to MX3 hardware capabilities */
++ if (pix->height > 4096)
++ pix->height = 4096;
++ if (pix->width > 4096)
++ pix->width = 4096;
++
++ pix->bytesperline = pix->width *
++ DIV_ROUND_UP(xlate->host_fmt->depth, 8);
++ pix->sizeimage = pix->height * pix->bytesperline;
++
++ /* camera has to see its format, but the user the original one */
++ pix->pixelformat = xlate->cam_fmt->fourcc;
++ /* limit to sensor capabilities */
++ ret = icd->ops->try_fmt(icd, f);
++ pix->pixelformat = xlate->host_fmt->fourcc;
++
++ field = pix->field;
++
++ if (field == V4L2_FIELD_ANY) {
++ pix->field = V4L2_FIELD_NONE;
++ } else if (field != V4L2_FIELD_NONE) {
++ dev_err(&icd->dev, "Field type %d unsupported.\n", field);
++ return -EINVAL;
++ }
++
++ return ret;
++}
++
++static int mx3_camera_reqbufs(struct soc_camera_file *icf,
++ struct v4l2_requestbuffers *p)
++{
++ return 0;
++}
++
++static unsigned int mx3_camera_poll(struct file *file, poll_table *pt)
++{
++ struct soc_camera_file *icf = file->private_data;
++
++ return videobuf_poll_stream(file, &icf->vb_vidq, pt);
++}
++
++static int mx3_camera_querycap(struct soc_camera_host *ici,
++ struct v4l2_capability *cap)
++{
++ /* cap->name is set by the firendly caller:-> */
++ strlcpy(cap->card, "i.MX3x Camera", sizeof(cap->card));
++ cap->version = KERNEL_VERSION(0, 2, 2);
++ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
++
++ return 0;
++}
++
++static int mx3_camera_set_bus_param(struct soc_camera_device *icd, __u32 pixfmt)
++{
++ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
++ struct mx3_camera_dev *mx3_cam = ici->priv;
++ unsigned long bus_flags, camera_flags, common_flags;
++ u32 dw, sens_conf;
++ int ret = test_platform_param(mx3_cam, icd->buswidth, &bus_flags);
++ const struct soc_camera_format_xlate *xlate;
++
++ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
++ if (!xlate) {
++ dev_warn(&ici->dev, "Format %x not found\n", pixfmt);
++ return -EINVAL;
++ }
++
++ dev_dbg(&ici->dev, "requested bus width %d bit: %d\n",
++ icd->buswidth, ret);
++
++ if (ret < 0)
++ return ret;
++
++ camera_flags = icd->ops->query_bus_param(icd);
++
++ common_flags = soc_camera_bus_param_compatible(camera_flags, bus_flags);
++ if (!common_flags) {
++ dev_dbg(&ici->dev, "no common flags: camera %lx, host %lx\n",
++ camera_flags, bus_flags);
++ return -EINVAL;
++ }
++
++ /* Make choices, based on platform preferences */
++ if ((common_flags & SOCAM_HSYNC_ACTIVE_HIGH) &&
++ (common_flags & SOCAM_HSYNC_ACTIVE_LOW)) {
++ if (mx3_cam->platform_flags & MX3_CAMERA_HSP)
++ common_flags &= ~SOCAM_HSYNC_ACTIVE_HIGH;
++ else
++ common_flags &= ~SOCAM_HSYNC_ACTIVE_LOW;
++ }
++
++ if ((common_flags & SOCAM_VSYNC_ACTIVE_HIGH) &&
++ (common_flags & SOCAM_VSYNC_ACTIVE_LOW)) {
++ if (mx3_cam->platform_flags & MX3_CAMERA_VSP)
++ common_flags &= ~SOCAM_VSYNC_ACTIVE_HIGH;
++ else
++ common_flags &= ~SOCAM_VSYNC_ACTIVE_LOW;
++ }
++
++ if ((common_flags & SOCAM_DATA_ACTIVE_HIGH) &&
++ (common_flags & SOCAM_DATA_ACTIVE_LOW)) {
++ if (mx3_cam->platform_flags & MX3_CAMERA_DP)
++ common_flags &= ~SOCAM_DATA_ACTIVE_HIGH;
++ else
++ common_flags &= ~SOCAM_DATA_ACTIVE_LOW;
++ }
++
++ if ((common_flags & SOCAM_PCLK_SAMPLE_RISING) &&
++ (common_flags & SOCAM_PCLK_SAMPLE_FALLING)) {
++ if (mx3_cam->platform_flags & MX3_CAMERA_PCP)
++ common_flags &= ~SOCAM_PCLK_SAMPLE_RISING;
++ else
++ common_flags &= ~SOCAM_PCLK_SAMPLE_FALLING;
++ }
++
++ /* Make the camera work in widest common mode, we'll take care of
++ * the rest */
++ if (common_flags & SOCAM_DATAWIDTH_15)
++ common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
++ SOCAM_DATAWIDTH_15;
++ else if (common_flags & SOCAM_DATAWIDTH_10)
++ common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
++ SOCAM_DATAWIDTH_10;
++ else if (common_flags & SOCAM_DATAWIDTH_8)
++ common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
++ SOCAM_DATAWIDTH_8;
++ else
++ common_flags = (common_flags & ~SOCAM_DATAWIDTH_MASK) |
++ SOCAM_DATAWIDTH_4;
++
++ ret = icd->ops->set_bus_param(icd, common_flags);
++ if (ret < 0)
++ return ret;
++
++ /*
++ * So far only gated clock mode is supported. Add a line
++ * (3 << CSI_SENS_CONF_SENS_PRTCL_SHIFT) |
++ * below and select the required mode when supporting other
++ * synchronisation protocols.
++ */
++ sens_conf = csi_reg_read(mx3_cam, CSI_SENS_CONF) &
++ ~((1 << CSI_SENS_CONF_VSYNC_POL_SHIFT) |
++ (1 << CSI_SENS_CONF_HSYNC_POL_SHIFT) |
++ (1 << CSI_SENS_CONF_DATA_POL_SHIFT) |
++ (1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT) |
++ (3 << CSI_SENS_CONF_DATA_FMT_SHIFT) |
++ (3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT));
++
++ /* TODO: Support RGB and YUV formats */
++
++ /* This has been set in mx3_camera_activate(), but we clear it above */
++ sens_conf |= CSI_SENS_CONF_DATA_FMT_BAYER;
++
++ if (common_flags & SOCAM_PCLK_SAMPLE_FALLING)
++ sens_conf |= 1 << CSI_SENS_CONF_PIX_CLK_POL_SHIFT;
++ if (common_flags & SOCAM_HSYNC_ACTIVE_LOW)
++ sens_conf |= 1 << CSI_SENS_CONF_HSYNC_POL_SHIFT;
++ if (common_flags & SOCAM_VSYNC_ACTIVE_LOW)
++ sens_conf |= 1 << CSI_SENS_CONF_VSYNC_POL_SHIFT;
++ if (common_flags & SOCAM_DATA_ACTIVE_LOW)
++ sens_conf |= 1 << CSI_SENS_CONF_DATA_POL_SHIFT;
++
++ /* Just do what we're asked to do */
++ switch (xlate->host_fmt->depth) {
++ case 4:
++ dw = 0 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
++ break;
++ case 8:
++ dw = 1 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
++ break;
++ case 10:
++ dw = 2 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
++ break;
++ default:
++ /*
++ * Actually it can only be 15 now, default is just to silence
++ * compiler warnings
++ */
++ case 15:
++ dw = 3 << CSI_SENS_CONF_DATA_WIDTH_SHIFT;
++ }
++
++ csi_reg_write(mx3_cam, sens_conf | dw, CSI_SENS_CONF);
++
++ dev_dbg(&ici->dev, "Set SENS_CONF to %x\n", sens_conf | dw);
++
++ return 0;
++}
++
++static struct soc_camera_host_ops mx3_soc_camera_host_ops = {
++ .owner = THIS_MODULE,
++ .add = mx3_camera_add_device,
++ .remove = mx3_camera_remove_device,
++#ifdef CONFIG_PM
++ .suspend = mx3_camera_suspend,
++ .resume = mx3_camera_resume,
++#endif
++ .set_crop = mx3_camera_set_crop,
++ .set_fmt = mx3_camera_set_fmt,
++ .try_fmt = mx3_camera_try_fmt,
++ .get_formats = mx3_camera_get_formats,
++ .init_videobuf = mx3_camera_init_videobuf,
++ .reqbufs = mx3_camera_reqbufs,
++ .poll = mx3_camera_poll,
++ .querycap = mx3_camera_querycap,
++ .set_bus_param = mx3_camera_set_bus_param,
++};
++
++static int mx3_camera_probe(struct platform_device *pdev)
++{
++ struct mx3_camera_dev *mx3_cam;
++ struct resource *res;
++ void __iomem *base;
++ int err = 0;
++ struct soc_camera_host *soc_host;
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (!res) {
++ err = -ENODEV;
++ goto egetres;
++ }
++
++ mx3_cam = vmalloc(sizeof(*mx3_cam));
++ if (!mx3_cam) {
++ dev_err(&pdev->dev, "Could not allocate mx3 camera object\n");
++ err = -ENOMEM;
++ goto ealloc;
++ }
++ memset(mx3_cam, 0, sizeof(*mx3_cam));
++
++ mx3_cam->clk = clk_get(&pdev->dev, "csi_clk");
++ if (IS_ERR(mx3_cam->clk)) {
++ err = PTR_ERR(mx3_cam->clk);
++ goto eclkget;
++ }
++
++ dev_set_drvdata(&pdev->dev, mx3_cam);
++
++ mx3_cam->pdata = pdev->dev.platform_data;
++ mx3_cam->platform_flags = mx3_cam->pdata->flags;
++ if (!(mx3_cam->platform_flags & (MX3_CAMERA_DATAWIDTH_4 |
++ MX3_CAMERA_DATAWIDTH_8 | MX3_CAMERA_DATAWIDTH_10 |
++ MX3_CAMERA_DATAWIDTH_15))) {
++ /* Platform hasn't set available data widths. This is bad.
++ * Warn and use a default. */
++ dev_warn(&pdev->dev, "WARNING! Platform hasn't set available "
++ "data widths, using default 8 bit\n");
++ mx3_cam->platform_flags |= MX3_CAMERA_DATAWIDTH_8;
++ }
++
++ mx3_cam->mclk = mx3_cam->pdata->mclk_10khz * 10000;
++ if (!mx3_cam->mclk) {
++ dev_warn(&pdev->dev,
++ "mclk_10khz == 0! Please, fix your platform data. "
++ "Using default 20MHz\n");
++ mx3_cam->mclk = 20000000;
++ }
++
++ /* list of video-buffers */
++ INIT_LIST_HEAD(&mx3_cam->capture);
++ spin_lock_init(&mx3_cam->lock);
++
++ base = ioremap(res->start, res->end - res->start + 1);
++ if (!base) {
++ err = -ENOMEM;
++ goto eioremap;
++ }
++
++ mx3_cam->base = base;
++ mx3_cam->dev = &pdev->dev;
++
++ soc_host = &mx3_cam->soc_host;
++ soc_host->drv_name = MX3_CAM_DRV_NAME;
++ soc_host->ops = &mx3_soc_camera_host_ops;
++ soc_host->priv = mx3_cam;
++ soc_host->dev.parent = &pdev->dev;
++ soc_host->nr = pdev->id;
++ err = soc_camera_host_register(soc_host);
++ if (err)
++ goto ecamhostreg;
++
++ /* IDMAC interface */
++ dmaengine_get();
++
++ return 0;
++
++ecamhostreg:
++ iounmap(base);
++eioremap:
++ clk_put(mx3_cam->clk);
++eclkget:
++ vfree(mx3_cam);
++ealloc:
++egetres:
++ return err;
++}
++
++static int __devexit mx3_camera_remove(struct platform_device *pdev)
++{
++ struct mx3_camera_dev *mx3_cam = platform_get_drvdata(pdev);
++
++ clk_put(mx3_cam->clk);
++
++ soc_camera_host_unregister(&mx3_cam->soc_host);
++
++ iounmap(mx3_cam->base);
++
++ /*
++ * The channel has either not been allocated,
++ * or should have been released
++ */
++ if (WARN_ON(mx3_cam->idmac_channel[0]))
++ dma_release_channel(&mx3_cam->idmac_channel[0]->dma_chan);
++
++ vfree(mx3_cam);
++
++ dmaengine_put();
++
++ dev_info(&pdev->dev, "i.MX3x Camera driver unloaded\n");
++
++ return 0;
++}
++
++static struct platform_driver mx3_camera_driver = {
++ .driver = {
++ .name = MX3_CAM_DRV_NAME,
++ },
++ .probe = mx3_camera_probe,
++ .remove = __exit_p(mx3_camera_remove),
++};
++
++
++static int __devinit mx3_camera_init(void)
++{
++ return platform_driver_register(&mx3_camera_driver);
++}
++
++static void __exit mx3_camera_exit(void)
++{
++ platform_driver_unregister(&mx3_camera_driver);
++}
++
++module_init(mx3_camera_init);
++module_exit(mx3_camera_exit);
++
++MODULE_DESCRIPTION("i.MX3x SoC Camera Host driver");
++MODULE_AUTHOR("Guennadi Liakhovetski <lg@denx.de>");
++MODULE_LICENSE("GPL v2");
+diff --git a/drivers/media/video/mxb.c b/drivers/media/video/mxb.c
+index e3cbe14..84aec62 100644
+--- a/drivers/media/video/mxb.c
++++ b/drivers/media/video/mxb.c
+@@ -25,16 +25,20 @@
+
+ #include <media/saa7146_vv.h>
+ #include <media/tuner.h>
+-#include <linux/video_decoder.h>
+ #include <media/v4l2-common.h>
+ #include <media/saa7115.h>
+
+ #include "mxb.h"
+ #include "tea6415c.h"
+ #include "tea6420.h"
+-#include "tda9840.h"
+
+-#define I2C_SAA7111 0x24
++#define I2C_SAA5246A 0x11
++#define I2C_SAA7111A 0x24
++#define I2C_TDA9840 0x42
++#define I2C_TEA6415C 0x43
++#define I2C_TEA6420_1 0x4c
++#define I2C_TEA6420_2 0x4d
++#define I2C_TUNER 0x60
+
+ #define MXB_BOARD_CAN_DO_VBI(dev) (dev->revision != 0)
+
+@@ -79,57 +83,35 @@ static struct {
+ static int video_audio_connect[MXB_INPUTS] =
+ { 0, 1, 3, 3 };
+
+-/* these are the necessary input-output-pins for bringing one audio source
+-(see above) to the CD-output */
+-static struct tea6420_multiplex TEA6420_cd[MXB_AUDIOS+1][2] =
+- {
+- {{1,1,0},{1,1,0}}, /* Tuner */
+- {{5,1,0},{6,1,0}}, /* AUX 1 */
+- {{4,1,0},{6,1,0}}, /* AUX 2 */
+- {{3,1,0},{6,1,0}}, /* AUX 3 */
+- {{1,1,0},{3,1,0}}, /* Radio */
+- {{1,1,0},{2,1,0}}, /* CD-Rom */
+- {{6,1,0},{6,1,0}} /* Mute */
+- };
+-
+-/* these are the necessary input-output-pins for bringing one audio source
+-(see above) to the line-output */
+-static struct tea6420_multiplex TEA6420_line[MXB_AUDIOS+1][2] =
+- {
+- {{2,3,0},{1,2,0}},
+- {{5,3,0},{6,2,0}},
+- {{4,3,0},{6,2,0}},
+- {{3,3,0},{6,2,0}},
+- {{2,3,0},{3,2,0}},
+- {{2,3,0},{2,2,0}},
+- {{6,3,0},{6,2,0}} /* Mute */
+- };
++/* These are the necessary input-output-pins for bringing one audio source
++ (see above) to the CD-output. Note that gain is set to 0 in this table. */
++static struct v4l2_routing TEA6420_cd[MXB_AUDIOS + 1][2] = {
++ { { 1, 1 }, { 1, 1 } }, /* Tuner */
++ { { 5, 1 }, { 6, 1 } }, /* AUX 1 */
++ { { 4, 1 }, { 6, 1 } }, /* AUX 2 */
++ { { 3, 1 }, { 6, 1 } }, /* AUX 3 */
++ { { 1, 1 }, { 3, 1 } }, /* Radio */
++ { { 1, 1 }, { 2, 1 } }, /* CD-Rom */
++ { { 6, 1 }, { 6, 1 } } /* Mute */
++};
++
++/* These are the necessary input-output-pins for bringing one audio source
++ (see above) to the line-output. Note that gain is set to 0 in this table. */
++static struct v4l2_routing TEA6420_line[MXB_AUDIOS + 1][2] = {
++ { { 2, 3 }, { 1, 2 } },
++ { { 5, 3 }, { 6, 2 } },
++ { { 4, 3 }, { 6, 2 } },
++ { { 3, 3 }, { 6, 2 } },
++ { { 2, 3 }, { 3, 2 } },
++ { { 2, 3 }, { 2, 2 } },
++ { { 6, 3 }, { 6, 2 } } /* Mute */
++};
+
+ #define MAXCONTROLS 1
+ static struct v4l2_queryctrl mxb_controls[] = {
+ { V4L2_CID_AUDIO_MUTE, V4L2_CTRL_TYPE_BOOLEAN, "Mute", 0, 1, 1, 0, 0 },
+ };
+
+-static struct saa7146_extension_ioctls ioctls[] = {
+- { VIDIOC_ENUMINPUT, SAA7146_EXCLUSIVE },
+- { VIDIOC_G_INPUT, SAA7146_EXCLUSIVE },
+- { VIDIOC_S_INPUT, SAA7146_EXCLUSIVE },
+- { VIDIOC_QUERYCTRL, SAA7146_BEFORE },
+- { VIDIOC_G_CTRL, SAA7146_BEFORE },
+- { VIDIOC_S_CTRL, SAA7146_BEFORE },
+- { VIDIOC_G_TUNER, SAA7146_EXCLUSIVE },
+- { VIDIOC_S_TUNER, SAA7146_EXCLUSIVE },
+- { VIDIOC_G_FREQUENCY, SAA7146_EXCLUSIVE },
+- { VIDIOC_S_FREQUENCY, SAA7146_EXCLUSIVE },
+- { VIDIOC_G_AUDIO, SAA7146_EXCLUSIVE },
+- { VIDIOC_S_AUDIO, SAA7146_EXCLUSIVE },
+- { VIDIOC_DBG_G_REGISTER, SAA7146_EXCLUSIVE },
+- { VIDIOC_DBG_S_REGISTER, SAA7146_EXCLUSIVE },
+- { MXB_S_AUDIO_CD, SAA7146_EXCLUSIVE }, /* custom control */
+- { MXB_S_AUDIO_LINE, SAA7146_EXCLUSIVE }, /* custom control */
+- { 0, 0 }
+-};
+-
+ struct mxb
+ {
+ struct video_device *video_dev;
+@@ -137,12 +119,12 @@ struct mxb
+
+ struct i2c_adapter i2c_adapter;
+
+- struct i2c_client *saa7111a;
+- struct i2c_client *tda9840;
+- struct i2c_client *tea6415c;
+- struct i2c_client *tuner;
+- struct i2c_client *tea6420_1;
+- struct i2c_client *tea6420_2;
++ struct v4l2_subdev *saa7111a;
++ struct v4l2_subdev *tda9840;
++ struct v4l2_subdev *tea6415c;
++ struct v4l2_subdev *tuner;
++ struct v4l2_subdev *tea6420_1;
++ struct v4l2_subdev *tea6420_2;
+
+ int cur_mode; /* current audio mode (mono, stereo, ...) */
+ int cur_input; /* current input */
+@@ -150,84 +132,51 @@ struct mxb
+ struct v4l2_frequency cur_freq; /* current frequency the tuner is tuned to */
+ };
+
+-static struct saa7146_extension extension;
+-
+-static int mxb_check_clients(struct device *dev, void *data)
+-{
+- struct mxb *mxb = data;
+- struct i2c_client *client = i2c_verify_client(dev);
+-
+- if (!client)
+- return 0;
+-
+- if (I2C_ADDR_TEA6420_1 == client->addr)
+- mxb->tea6420_1 = client;
+- if (I2C_ADDR_TEA6420_2 == client->addr)
+- mxb->tea6420_2 = client;
+- if (I2C_TEA6415C_2 == client->addr)
+- mxb->tea6415c = client;
+- if (I2C_ADDR_TDA9840 == client->addr)
+- mxb->tda9840 = client;
+- if (I2C_SAA7111 == client->addr)
+- mxb->saa7111a = client;
+- if (0x60 == client->addr)
+- mxb->tuner = client;
++#define saa7111a_call(mxb, o, f, args...) \
++ v4l2_subdev_call(mxb->saa7111a, o, f, ##args)
++#define tea6420_1_call(mxb, o, f, args...) \
++ v4l2_subdev_call(mxb->tea6420_1, o, f, ##args)
++#define tea6420_2_call(mxb, o, f, args...) \
++ v4l2_subdev_call(mxb->tea6420_2, o, f, ##args)
++#define tda9840_call(mxb, o, f, args...) \
++ v4l2_subdev_call(mxb->tda9840, o, f, ##args)
++#define tea6415c_call(mxb, o, f, args...) \
++ v4l2_subdev_call(mxb->tea6415c, o, f, ##args)
++#define tuner_call(mxb, o, f, args...) \
++ v4l2_subdev_call(mxb->tuner, o, f, ##args)
++#define call_all(dev, o, f, args...) \
++ v4l2_device_call_until_err(&dev->v4l2_dev, 0, o, f, ##args)
+
+- return 0;
+-}
++static struct saa7146_extension extension;
+
+-static int mxb_probe(struct saa7146_dev* dev)
++static int mxb_probe(struct saa7146_dev *dev)
+ {
+- struct mxb* mxb = NULL;
+- int result;
+-
+- result = request_module("saa7115");
+- if (result < 0) {
+- printk("mxb: saa7111 i2c module not available.\n");
+- return -ENODEV;
+- }
+- result = request_module("tea6420");
+- if (result < 0) {
+- printk("mxb: tea6420 i2c module not available.\n");
+- return -ENODEV;
+- }
+- result = request_module("tea6415c");
+- if (result < 0) {
+- printk("mxb: tea6415c i2c module not available.\n");
+- return -ENODEV;
+- }
+- result = request_module("tda9840");
+- if (result < 0) {
+- printk("mxb: tda9840 i2c module not available.\n");
+- return -ENODEV;
+- }
+- result = request_module("tuner");
+- if (result < 0) {
+- printk("mxb: tuner i2c module not available.\n");
+- return -ENODEV;
+- }
++ struct mxb *mxb = NULL;
+
+ mxb = kzalloc(sizeof(struct mxb), GFP_KERNEL);
+- if( NULL == mxb ) {
++ if (mxb == NULL) {
+ DEB_D(("not enough kernel memory.\n"));
+ return -ENOMEM;
+ }
+
+- mxb->i2c_adapter = (struct i2c_adapter) {
+- .class = I2C_CLASS_TV_ANALOG,
+- };
+-
+ snprintf(mxb->i2c_adapter.name, sizeof(mxb->i2c_adapter.name), "mxb%d", mxb_num);
+
+ saa7146_i2c_adapter_prepare(dev, &mxb->i2c_adapter, SAA7146_I2C_BUS_BIT_RATE_480);
+- if(i2c_add_adapter(&mxb->i2c_adapter) < 0) {
++ if (i2c_add_adapter(&mxb->i2c_adapter) < 0) {
+ DEB_S(("cannot register i2c-device. skipping.\n"));
+ kfree(mxb);
+ return -EFAULT;
+ }
+
+- /* loop through all i2c-devices on the bus and look who is there */
+- device_for_each_child(&mxb->i2c_adapter.dev, mxb, mxb_check_clients);
++ mxb->saa7111a = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "saa7115", "saa7111", I2C_SAA7111A);
++ mxb->tea6420_1 = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tea6420", "tea6420", I2C_TEA6420_1);
++ mxb->tea6420_2 = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tea6420", "tea6420", I2C_TEA6420_2);
++ mxb->tea6415c = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tea6415c", "tea6415c", I2C_TEA6415C);
++ mxb->tda9840 = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tda9840", "tda9840", I2C_TDA9840);
++ mxb->tuner = v4l2_i2c_new_subdev(&mxb->i2c_adapter, "tuner", "tuner", I2C_TUNER);
++ if (v4l2_i2c_new_subdev(&mxb->i2c_adapter, "saa5246a", "saa5246a", I2C_SAA5246A)) {
++ printk(KERN_INFO "mxb: found teletext decoder\n");
++ }
+
+ /* check if all devices are present */
+ if (!mxb->tea6420_1 || !mxb->tea6420_2 || !mxb->tea6415c ||
+@@ -315,47 +264,45 @@ static int mxb_init_done(struct saa7146_dev* dev)
+ struct v4l2_routing route;
+
+ int i = 0, err = 0;
+- struct tea6415c_multiplex vm;
+
+ /* select video mode in saa7111a */
+- mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_S_STD, &std);
++ saa7111a_call(mxb, tuner, s_std, std);
+
+ /* select tuner-output on saa7111a */
+ i = 0;
+ route.input = SAA7115_COMPOSITE0;
+ route.output = SAA7111_FMT_CCIR | SAA7111_VBI_BYPASS;
+- mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_VIDEO_ROUTING, &route);
++ saa7111a_call(mxb, video, s_routing, &route);
+
+ /* select a tuner type */
+ tun_setup.mode_mask = T_ANALOG_TV;
+ tun_setup.addr = ADDR_UNSET;
+ tun_setup.type = TUNER_PHILIPS_PAL;
+- mxb->tuner->driver->command(mxb->tuner, TUNER_SET_TYPE_ADDR, &tun_setup);
++ tuner_call(mxb, tuner, s_type_addr, &tun_setup);
+ /* tune in some frequency on tuner */
+ mxb->cur_freq.tuner = 0;
+ mxb->cur_freq.type = V4L2_TUNER_ANALOG_TV;
+ mxb->cur_freq.frequency = freq;
+- mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_FREQUENCY,
+- &mxb->cur_freq);
++ tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq);
+
+ /* set a default video standard */
+- mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std);
++ tuner_call(mxb, tuner, s_std, std);
+
+ /* mute audio on tea6420s */
+- mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_line[6][0]);
+- mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_line[6][1]);
+- mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH, &TEA6420_cd[6][0]);
+- mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH, &TEA6420_cd[6][1]);
++ tea6420_1_call(mxb, audio, s_routing, &TEA6420_line[6][0]);
++ tea6420_2_call(mxb, audio, s_routing, &TEA6420_line[6][1]);
++ tea6420_1_call(mxb, audio, s_routing, &TEA6420_line[6][0]);
++ tea6420_2_call(mxb, audio, s_routing, &TEA6420_line[6][1]);
+
+- /* switch to tuner-channel on tea6415c*/
+- vm.out = 17;
+- vm.in = 3;
+- mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm);
++ /* switch to tuner-channel on tea6415c */
++ route.input = 3;
++ route.output = 17;
++ tea6415c_call(mxb, video, s_routing, &route);
+
+- /* select tuner-output on multicable on tea6415c*/
+- vm.in = 3;
+- vm.out = 13;
+- mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm);
++ /* select tuner-output on multicable on tea6415c */
++ route.input = 3;
++ route.output = 13;
++ tea6415c_call(mxb, video, s_routing, &route);
+
+ /* the rest for mxb */
+ mxb->cur_input = 0;
+@@ -424,395 +371,414 @@ void mxb_irq_bh(struct saa7146_dev* dev, u32* irq_mask)
+ }
+ */
+
+-static struct saa7146_ext_vv vv_data;
+-
+-/* this function only gets called when the probing was successful */
+-static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
++static int vidioc_queryctrl(struct file *file, void *fh, struct v4l2_queryctrl *qc)
+ {
+- struct mxb *mxb = (struct mxb *)dev->ext_priv;
+-
+- DEB_EE(("dev:%p\n", dev));
+-
+- /* checking for i2c-devices can be omitted here, because we
+- already did this in "mxb_vl42_probe" */
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ int i;
+
+- saa7146_vv_init(dev, &vv_data);
+- if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) {
+- ERR(("cannot register capture v4l2 device. skipping.\n"));
+- return -1;
++ for (i = MAXCONTROLS - 1; i >= 0; i--) {
++ if (mxb_controls[i].id == qc->id) {
++ *qc = mxb_controls[i];
++ DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id));
++ return 0;
++ }
+ }
++ return dev->ext_vv_data->core_ops->vidioc_queryctrl(file, fh, qc);
++}
+
+- /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/
+- if (MXB_BOARD_CAN_DO_VBI(dev)) {
+- if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) {
+- ERR(("cannot register vbi v4l2 device. skipping.\n"));
+- }
++static int vidioc_g_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct mxb *mxb = (struct mxb *)dev->ext_priv;
++ int i;
++
++ for (i = MAXCONTROLS - 1; i >= 0; i--) {
++ if (mxb_controls[i].id == vc->id)
++ break;
+ }
+
+- i2c_use_client(mxb->tea6420_1);
+- i2c_use_client(mxb->tea6420_2);
+- i2c_use_client(mxb->tea6415c);
+- i2c_use_client(mxb->tda9840);
+- i2c_use_client(mxb->saa7111a);
+- i2c_use_client(mxb->tuner);
++ if (i < 0)
++ return dev->ext_vv_data->core_ops->vidioc_g_ctrl(file, fh, vc);
+
+- printk("mxb: found Multimedia eXtension Board #%d.\n", mxb_num);
++ if (vc->id == V4L2_CID_AUDIO_MUTE) {
++ vc->value = mxb->cur_mute;
++ DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value));
++ return 0;
++ }
+
+- mxb_num++;
+- mxb_init_done(dev);
++ DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value));
+ return 0;
+ }
+
+-static int mxb_detach(struct saa7146_dev *dev)
++static int vidioc_s_ctrl(struct file *file, void *fh, struct v4l2_control *vc)
+ {
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
++ int i = 0;
+
+- DEB_EE(("dev:%p\n", dev));
+-
+- i2c_release_client(mxb->tea6420_1);
+- i2c_release_client(mxb->tea6420_2);
+- i2c_release_client(mxb->tea6415c);
+- i2c_release_client(mxb->tda9840);
+- i2c_release_client(mxb->saa7111a);
+- i2c_release_client(mxb->tuner);
+-
+- saa7146_unregister_device(&mxb->video_dev,dev);
+- if (MXB_BOARD_CAN_DO_VBI(dev))
+- saa7146_unregister_device(&mxb->vbi_dev, dev);
+- saa7146_vv_release(dev);
+-
+- mxb_num--;
++ for (i = MAXCONTROLS - 1; i >= 0; i--) {
++ if (mxb_controls[i].id == vc->id)
++ break;
++ }
+
+- i2c_del_adapter(&mxb->i2c_adapter);
+- kfree(mxb);
++ if (i < 0)
++ return dev->ext_vv_data->core_ops->vidioc_s_ctrl(file, fh, vc);
++
++ if (vc->id == V4L2_CID_AUDIO_MUTE) {
++ mxb->cur_mute = vc->value;
++ if (!vc->value) {
++ /* switch the audio-source */
++ tea6420_1_call(mxb, audio, s_routing,
++ &TEA6420_line[video_audio_connect[mxb->cur_input]][0]);
++ tea6420_2_call(mxb, audio, s_routing,
++ &TEA6420_line[video_audio_connect[mxb->cur_input]][1]);
++ } else {
++ tea6420_1_call(mxb, audio, s_routing,
++ &TEA6420_line[6][0]);
++ tea6420_2_call(mxb, audio, s_routing,
++ &TEA6420_line[6][1]);
++ }
++ DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n", vc->value));
++ }
++ return 0;
++}
+
++static int vidioc_enum_input(struct file *file, void *fh, struct v4l2_input *i)
++{
++ DEB_EE(("VIDIOC_ENUMINPUT %d.\n", i->index));
++ if (i->index < 0 || i->index >= MXB_INPUTS)
++ return -EINVAL;
++ memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input));
+ return 0;
+ }
+
+-static long mxb_ioctl(struct saa7146_fh *fh, unsigned int cmd, void *arg)
++static int vidioc_g_input(struct file *file, void *fh, unsigned int *i)
+ {
+- struct saa7146_dev *dev = fh->dev;
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+- struct saa7146_vv *vv = dev->vv_data;
++ *i = mxb->cur_input;
+
+- switch(cmd) {
+- case VIDIOC_ENUMINPUT:
+- {
+- struct v4l2_input *i = arg;
++ DEB_EE(("VIDIOC_G_INPUT %d.\n", *i));
++ return 0;
++}
+
+- DEB_EE(("VIDIOC_ENUMINPUT %d.\n",i->index));
+- if (i->index < 0 || i->index >= MXB_INPUTS)
+- return -EINVAL;
+- memcpy(i, &mxb_inputs[i->index], sizeof(struct v4l2_input));
+- return 0;
+- }
+- /* the saa7146 provides some controls (brightness, contrast, saturation)
+- which gets registered *after* this function. because of this we have
+- to return with a value != 0 even if the function succeded.. */
+- case VIDIOC_QUERYCTRL:
+- {
+- struct v4l2_queryctrl *qc = arg;
+- int i;
+-
+- for (i = MAXCONTROLS - 1; i >= 0; i--) {
+- if (mxb_controls[i].id == qc->id) {
+- *qc = mxb_controls[i];
+- DEB_D(("VIDIOC_QUERYCTRL %d.\n", qc->id));
+- return 0;
+- }
+- }
+- return -EAGAIN;
+- }
+- case VIDIOC_G_CTRL:
+- {
+- struct v4l2_control *vc = arg;
+- int i;
++static int vidioc_s_input(struct file *file, void *fh, unsigned int input)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct mxb *mxb = (struct mxb *)dev->ext_priv;
++ struct v4l2_routing route;
++ int i = 0;
+
+- for (i = MAXCONTROLS - 1; i >= 0; i--) {
+- if (mxb_controls[i].id == vc->id)
+- break;
+- }
++ DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
+
+- if (i < 0)
+- return -EAGAIN;
++ if (input < 0 || input >= MXB_INPUTS)
++ return -EINVAL;
+
+- if (vc->id == V4L2_CID_AUDIO_MUTE) {
+- vc->value = mxb->cur_mute;
+- DEB_D(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value));
+- return 0;
+- }
++ mxb->cur_input = input;
+
+- DEB_EE(("VIDIOC_G_CTRL V4L2_CID_AUDIO_MUTE:%d.\n", vc->value));
+- return 0;
+- }
++ saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source,
++ input_port_selection[input].hps_sync);
+
+- case VIDIOC_S_CTRL:
+- {
+- struct v4l2_control *vc = arg;
+- int i = 0;
++ /* prepare switching of tea6415c and saa7111a;
++ have a look at the 'background'-file for further informations */
++ switch (input) {
++ case TUNER:
++ i = SAA7115_COMPOSITE0;
++ route.input = 3;
++ route.output = 17;
+
+- for (i = MAXCONTROLS - 1; i >= 0; i--) {
+- if (mxb_controls[i].id == vc->id)
+- break;
++ if (tea6415c_call(mxb, video, s_routing, &route)) {
++ printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #1\n");
++ return -EFAULT;
+ }
++ /* connect tuner-output always to multicable */
++ route.input = 3;
++ route.output = 13;
++ break;
++ case AUX3_YC:
++ /* nothing to be done here. aux3_yc is
++ directly connected to the saa711a */
++ i = SAA7115_SVIDEO1;
++ break;
++ case AUX3:
++ /* nothing to be done here. aux3 is
++ directly connected to the saa711a */
++ i = SAA7115_COMPOSITE1;
++ break;
++ case AUX1:
++ i = SAA7115_COMPOSITE0;
++ route.input = 1;
++ route.output = 17;
++ break;
++ }
+
+- if (i < 0)
+- return -EAGAIN;
+-
+- if (vc->id == V4L2_CID_AUDIO_MUTE) {
+- mxb->cur_mute = vc->value;
+- if (!vc->value) {
+- /* switch the audio-source */
+- mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH,
+- &TEA6420_line[video_audio_connect[mxb->cur_input]][0]);
+- mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH,
+- &TEA6420_line[video_audio_connect[mxb->cur_input]][1]);
+- } else {
+- mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH,
+- &TEA6420_line[6][0]);
+- mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH,
+- &TEA6420_line[6][1]);
+- }
+- DEB_EE(("VIDIOC_S_CTRL, V4L2_CID_AUDIO_MUTE: %d.\n", vc->value));
++ /* switch video in tea6415c only if necessary */
++ switch (input) {
++ case TUNER:
++ case AUX1:
++ if (tea6415c_call(mxb, video, s_routing, &route)) {
++ printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #3\n");
++ return -EFAULT;
+ }
+- return 0;
++ break;
++ default:
++ break;
+ }
+- case VIDIOC_G_INPUT:
+- {
+- int *input = (int *)arg;
+- *input = mxb->cur_input;
+
+- DEB_EE(("VIDIOC_G_INPUT %d.\n", *input));
+- return 0;
++ /* switch video in saa7111a */
++ route.input = i;
++ route.output = 0;
++ if (saa7111a_call(mxb, video, s_routing, &route))
++ printk(KERN_ERR "VIDIOC_S_INPUT: could not address saa7111a #1.\n");
++
++ /* switch the audio-source only if necessary */
++ if (0 == mxb->cur_mute) {
++ tea6420_1_call(mxb, audio, s_routing,
++ &TEA6420_line[video_audio_connect[input]][0]);
++ tea6420_2_call(mxb, audio, s_routing,
++ &TEA6420_line[video_audio_connect[input]][1]);
+ }
+- case VIDIOC_S_INPUT:
+- {
+- int input = *(int *)arg;
+- struct tea6415c_multiplex vm;
+- struct v4l2_routing route;
+- int i = 0;
+
+- DEB_EE(("VIDIOC_S_INPUT %d.\n", input));
++ return 0;
++}
+
+- if (input < 0 || input >= MXB_INPUTS)
+- return -EINVAL;
++static int vidioc_g_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+- mxb->cur_input = input;
++ if (t->index) {
++ DEB_D(("VIDIOC_G_TUNER: channel %d does not have a tuner attached.\n", t->index));
++ return -EINVAL;
++ }
+
+- saa7146_set_hps_source_and_sync(dev, input_port_selection[input].hps_source,
+- input_port_selection[input].hps_sync);
++ DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index));
+
+- /* prepare switching of tea6415c and saa7111a;
+- have a look at the 'background'-file for further informations */
+- switch (input) {
+- case TUNER:
+- i = SAA7115_COMPOSITE0;
+- vm.in = 3;
+- vm.out = 17;
++ memset(t, 0, sizeof(*t));
++ strlcpy(t->name, "TV Tuner", sizeof(t->name));
++ t->type = V4L2_TUNER_ANALOG_TV;
++ t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO |
++ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
++ t->audmode = mxb->cur_mode;
++ return call_all(dev, tuner, g_tuner, t);
++}
+
+- if (mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm)) {
+- printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #1\n");
+- return -EFAULT;
+- }
+- /* connect tuner-output always to multicable */
+- vm.in = 3;
+- vm.out = 13;
+- break;
+- case AUX3_YC:
+- /* nothing to be done here. aux3_yc is
+- directly connected to the saa711a */
+- i = SAA7115_SVIDEO1;
+- break;
+- case AUX3:
+- /* nothing to be done here. aux3 is
+- directly connected to the saa711a */
+- i = SAA7115_COMPOSITE1;
+- break;
+- case AUX1:
+- i = SAA7115_COMPOSITE0;
+- vm.in = 1;
+- vm.out = 17;
+- break;
+- }
++static int vidioc_s_tuner(struct file *file, void *fh, struct v4l2_tuner *t)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+- /* switch video in tea6415c only if necessary */
+- switch (input) {
+- case TUNER:
+- case AUX1:
+- if (mxb->tea6415c->driver->command(mxb->tea6415c, TEA6415C_SWITCH, &vm)) {
+- printk(KERN_ERR "VIDIOC_S_INPUT: could not address tea6415c #3\n");
+- return -EFAULT;
+- }
+- break;
+- default:
+- break;
+- }
++ if (t->index) {
++ DEB_D(("VIDIOC_S_TUNER: channel %d does not have a tuner attached.\n", t->index));
++ return -EINVAL;
++ }
+
+- /* switch video in saa7111a */
+- route.input = i;
+- route.output = 0;
+- if (mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_VIDEO_ROUTING, &route))
+- printk("VIDIOC_S_INPUT: could not address saa7111a #1.\n");
+-
+- /* switch the audio-source only if necessary */
+- if( 0 == mxb->cur_mute ) {
+- mxb->tea6420_1->driver->command(mxb->tea6420_1, TEA6420_SWITCH,
+- &TEA6420_line[video_audio_connect[input]][0]);
+- mxb->tea6420_2->driver->command(mxb->tea6420_2, TEA6420_SWITCH,
+- &TEA6420_line[video_audio_connect[input]][1]);
+- }
++ mxb->cur_mode = t->audmode;
++ return call_all(dev, tuner, s_tuner, t);
++}
+
+- return 0;
++static int vidioc_g_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct mxb *mxb = (struct mxb *)dev->ext_priv;
++
++ if (mxb->cur_input) {
++ DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n",
++ mxb->cur_input));
++ return -EINVAL;
+ }
+- case VIDIOC_G_TUNER:
+- {
+- struct v4l2_tuner *t = arg;
+
+- if (t->index) {
+- DEB_D(("VIDIOC_G_TUNER: channel %d does not have a tuner attached.\n", t->index));
+- return -EINVAL;
+- }
++ *f = mxb->cur_freq;
+
+- DEB_EE(("VIDIOC_G_TUNER: %d\n", t->index));
++ DEB_EE(("VIDIOC_G_FREQ: freq:0x%08x.\n", mxb->cur_freq.frequency));
++ return 0;
++}
+
+- memset(t, 0, sizeof(*t));
+- i2c_clients_command(&mxb->i2c_adapter, cmd, arg);
++static int vidioc_s_frequency(struct file *file, void *fh, struct v4l2_frequency *f)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct mxb *mxb = (struct mxb *)dev->ext_priv;
++ struct saa7146_vv *vv = dev->vv_data;
+
+- strlcpy(t->name, "TV Tuner", sizeof(t->name));
+- t->type = V4L2_TUNER_ANALOG_TV;
+- t->capability = V4L2_TUNER_CAP_NORM | V4L2_TUNER_CAP_STEREO | \
+- V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2 | V4L2_TUNER_CAP_SAP;
+- t->audmode = mxb->cur_mode;
+- return 0;
+- }
+- case VIDIOC_S_TUNER:
+- {
+- struct v4l2_tuner *t = arg;
++ if (f->tuner)
++ return -EINVAL;
+
+- if (t->index) {
+- DEB_D(("VIDIOC_S_TUNER: channel %d does not have a tuner attached.\n",t->index));
+- return -EINVAL;
+- }
++ if (V4L2_TUNER_ANALOG_TV != f->type)
++ return -EINVAL;
+
+- mxb->cur_mode = t->audmode;
+- i2c_clients_command(&mxb->i2c_adapter, cmd, arg);
+- return 0;
++ if (mxb->cur_input) {
++ DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n", mxb->cur_input));
++ return -EINVAL;
+ }
+- case VIDIOC_G_FREQUENCY:
+- {
+- struct v4l2_frequency *f = arg;
+
+- if (mxb->cur_input) {
+- DEB_D(("VIDIOC_G_FREQ: channel %d does not have a tuner!\n",
+- mxb->cur_input));
+- return -EINVAL;
+- }
++ mxb->cur_freq = *f;
++ DEB_EE(("VIDIOC_S_FREQUENCY: freq:0x%08x.\n", mxb->cur_freq.frequency));
+
+- *f = mxb->cur_freq;
++ /* tune in desired frequency */
++ tuner_call(mxb, tuner, s_frequency, &mxb->cur_freq);
+
+- DEB_EE(("VIDIOC_G_FREQ: freq:0x%08x.\n", mxb->cur_freq.frequency));
+- return 0;
++ /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */
++ spin_lock(&dev->slock);
++ vv->vbi_fieldcount = 0;
++ spin_unlock(&dev->slock);
++
++ return 0;
++}
++
++static int vidioc_g_audio(struct file *file, void *fh, struct v4l2_audio *a)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct mxb *mxb = (struct mxb *)dev->ext_priv;
++
++ if (a->index < 0 || a->index > MXB_INPUTS) {
++ DEB_D(("VIDIOC_G_AUDIO %d out of range.\n", a->index));
++ return -EINVAL;
+ }
+- case VIDIOC_S_FREQUENCY:
+- {
+- struct v4l2_frequency *f = arg;
+
+- if (f->tuner)
+- return -EINVAL;
++ DEB_EE(("VIDIOC_G_AUDIO %d.\n", a->index));
++ memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio));
++ return 0;
++}
+
+- if (V4L2_TUNER_ANALOG_TV != f->type)
+- return -EINVAL;
++static int vidioc_s_audio(struct file *file, void *fh, struct v4l2_audio *a)
++{
++ DEB_D(("VIDIOC_S_AUDIO %d.\n", a->index));
++ return 0;
++}
+
+- if (mxb->cur_input) {
+- DEB_D(("VIDIOC_S_FREQ: channel %d does not have a tuner!\n", mxb->cur_input));
+- return -EINVAL;
+- }
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++static int vidioc_g_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+
+- mxb->cur_freq = *f;
+- DEB_EE(("VIDIOC_S_FREQUENCY: freq:0x%08x.\n", mxb->cur_freq.frequency));
++ return call_all(dev, core, g_register, reg);
++}
+
+- /* tune in desired frequency */
+- mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_FREQUENCY, &mxb->cur_freq);
++static int vidioc_s_register(struct file *file, void *fh, struct v4l2_dbg_register *reg)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
+
+- /* hack: changing the frequency should invalidate the vbi-counter (=> alevt) */
+- spin_lock(&dev->slock);
+- vv->vbi_fieldcount = 0;
+- spin_unlock(&dev->slock);
++ return call_all(dev, core, s_register, reg);
++}
++#endif
+
+- return 0;
+- }
++static long vidioc_default(struct file *file, void *fh, int cmd, void *arg)
++{
++ struct saa7146_dev *dev = ((struct saa7146_fh *)fh)->dev;
++ struct mxb *mxb = (struct mxb *)dev->ext_priv;
++
++ switch (cmd) {
+ case MXB_S_AUDIO_CD:
+ {
+- int i = *(int*)arg;
++ int i = *(int *)arg;
+
+ if (i < 0 || i >= MXB_AUDIOS) {
+- DEB_D(("illegal argument to MXB_S_AUDIO_CD: i:%d.\n",i));
++ DEB_D(("illegal argument to MXB_S_AUDIO_CD: i:%d.\n", i));
+ return -EINVAL;
+ }
+
+- DEB_EE(("MXB_S_AUDIO_CD: i:%d.\n",i));
++ DEB_EE(("MXB_S_AUDIO_CD: i:%d.\n", i));
+
+- mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_cd[i][0]);
+- mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_cd[i][1]);
++ tea6420_1_call(mxb, audio, s_routing, &TEA6420_cd[i][0]);
++ tea6420_2_call(mxb, audio, s_routing, &TEA6420_cd[i][1]);
+
+ return 0;
+ }
+ case MXB_S_AUDIO_LINE:
+ {
+- int i = *(int*)arg;
++ int i = *(int *)arg;
+
+ if (i < 0 || i >= MXB_AUDIOS) {
+- DEB_D(("illegal argument to MXB_S_AUDIO_LINE: i:%d.\n",i));
++ DEB_D(("illegal argument to MXB_S_AUDIO_LINE: i:%d.\n", i));
+ return -EINVAL;
+ }
+
+- DEB_EE(("MXB_S_AUDIO_LINE: i:%d.\n",i));
+- mxb->tea6420_1->driver->command(mxb->tea6420_1,TEA6420_SWITCH, &TEA6420_line[i][0]);
+- mxb->tea6420_2->driver->command(mxb->tea6420_2,TEA6420_SWITCH, &TEA6420_line[i][1]);
++ DEB_EE(("MXB_S_AUDIO_LINE: i:%d.\n", i));
++ tea6420_1_call(mxb, audio, s_routing, &TEA6420_line[i][0]);
++ tea6420_2_call(mxb, audio, s_routing, &TEA6420_line[i][1]);
+
+ return 0;
+ }
+- case VIDIOC_G_AUDIO:
+- {
+- struct v4l2_audio *a = arg;
++ default:
++/*
++ DEB2(printk("does not handle this ioctl.\n"));
++*/
++ return -ENOIOCTLCMD;
++ }
++ return 0;
++}
+
+- if (a->index < 0 || a->index > MXB_INPUTS) {
+- DEB_D(("VIDIOC_G_AUDIO %d out of range.\n", a->index));
+- return -EINVAL;
+- }
++static struct saa7146_ext_vv vv_data;
+
+- DEB_EE(("VIDIOC_G_AUDIO %d.\n", a->index));
+- memcpy(a, &mxb_audios[video_audio_connect[mxb->cur_input]], sizeof(struct v4l2_audio));
++/* this function only gets called when the probing was successful */
++static int mxb_attach(struct saa7146_dev *dev, struct saa7146_pci_extension_data *info)
++{
++ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+
+- return 0;
+- }
+- case VIDIOC_S_AUDIO:
+- {
+- struct v4l2_audio *a = arg;
++ DEB_EE(("dev:%p\n", dev));
+
+- DEB_D(("VIDIOC_S_AUDIO %d.\n", a->index));
+- return 0;
+- }
++ /* checking for i2c-devices can be omitted here, because we
++ already did this in "mxb_vl42_probe" */
++
++ saa7146_vv_init(dev, &vv_data);
++ vv_data.ops.vidioc_queryctrl = vidioc_queryctrl;
++ vv_data.ops.vidioc_g_ctrl = vidioc_g_ctrl;
++ vv_data.ops.vidioc_s_ctrl = vidioc_s_ctrl;
++ vv_data.ops.vidioc_enum_input = vidioc_enum_input;
++ vv_data.ops.vidioc_g_input = vidioc_g_input;
++ vv_data.ops.vidioc_s_input = vidioc_s_input;
++ vv_data.ops.vidioc_g_tuner = vidioc_g_tuner;
++ vv_data.ops.vidioc_s_tuner = vidioc_s_tuner;
++ vv_data.ops.vidioc_g_frequency = vidioc_g_frequency;
++ vv_data.ops.vidioc_s_frequency = vidioc_s_frequency;
++ vv_data.ops.vidioc_g_audio = vidioc_g_audio;
++ vv_data.ops.vidioc_s_audio = vidioc_s_audio;
+ #ifdef CONFIG_VIDEO_ADV_DEBUG
+- case VIDIOC_DBG_S_REGISTER:
+- case VIDIOC_DBG_G_REGISTER:
+- i2c_clients_command(&mxb->i2c_adapter, cmd, arg);
+- return 0;
++ vv_data.ops.vidioc_g_register = vidioc_g_register;
++ vv_data.ops.vidioc_s_register = vidioc_s_register;
+ #endif
+- default:
+-/*
+- DEB2(printk("does not handle this ioctl.\n"));
+-*/
+- return -ENOIOCTLCMD;
++ vv_data.ops.vidioc_default = vidioc_default;
++ if (saa7146_register_device(&mxb->video_dev, dev, "mxb", VFL_TYPE_GRABBER)) {
++ ERR(("cannot register capture v4l2 device. skipping.\n"));
++ return -1;
+ }
++
++ /* initialization stuff (vbi) (only for revision > 0 and for extensions which want it)*/
++ if (MXB_BOARD_CAN_DO_VBI(dev)) {
++ if (saa7146_register_device(&mxb->vbi_dev, dev, "mxb", VFL_TYPE_VBI)) {
++ ERR(("cannot register vbi v4l2 device. skipping.\n"));
++ }
++ }
++
++ printk("mxb: found Multimedia eXtension Board #%d.\n", mxb_num);
++
++ mxb_num++;
++ mxb_init_done(dev);
++ return 0;
++}
++
++static int mxb_detach(struct saa7146_dev *dev)
++{
++ struct mxb *mxb = (struct mxb *)dev->ext_priv;
++
++ DEB_EE(("dev:%p\n", dev));
++
++ saa7146_unregister_device(&mxb->video_dev,dev);
++ if (MXB_BOARD_CAN_DO_VBI(dev))
++ saa7146_unregister_device(&mxb->vbi_dev, dev);
++ saa7146_vv_release(dev);
++
++ mxb_num--;
++
++ i2c_del_adapter(&mxb->i2c_adapter);
++ kfree(mxb);
++
+ return 0;
+ }
+
+ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standard)
+ {
+ struct mxb *mxb = (struct mxb *)dev->ext_priv;
+- int zero = 0;
+- int one = 1;
+
+ if (V4L2_STD_PAL_I == standard->id) {
+ v4l2_std_id std = V4L2_STD_PAL_I;
+@@ -821,8 +787,8 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa
+ /* set the 7146 gpio register -- I don't know what this does exactly */
+ saa7146_write(dev, GPIO_CTRL, 0x00404050);
+ /* unset the 7111 gpio register -- I don't know what this does exactly */
+- mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_GPIO, &zero);
+- mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std);
++ saa7111a_call(mxb, core, s_gpio, 0);
++ tuner_call(mxb, tuner, s_std, std);
+ } else {
+ v4l2_std_id std = V4L2_STD_PAL_BG;
+
+@@ -830,8 +796,8 @@ static int std_callback(struct saa7146_dev *dev, struct saa7146_standard *standa
+ /* set the 7146 gpio register -- I don't know what this does exactly */
+ saa7146_write(dev, GPIO_CTRL, 0x00404050);
+ /* set the 7111 gpio register -- I don't know what this does exactly */
+- mxb->saa7111a->driver->command(mxb->saa7111a, VIDIOC_INT_S_GPIO, &one);
+- mxb->tuner->driver->command(mxb->tuner, VIDIOC_S_STD, &std);
++ saa7111a_call(mxb, core, s_gpio, 1);
++ tuner_call(mxb, tuner, s_std, std);
+ }
+ return 0;
+ }
+@@ -885,8 +851,6 @@ static struct saa7146_ext_vv vv_data = {
+ .stds = &standard[0],
+ .num_stds = sizeof(standard)/sizeof(struct saa7146_standard),
+ .std_callback = &std_callback,
+- .ioctls = &ioctls[0],
+- .ioctl = mxb_ioctl,
+ };
+
+ static struct saa7146_extension extension = {
+diff --git a/drivers/media/video/omap24xxcam.c b/drivers/media/video/omap24xxcam.c
+index 73eb656..d828597 100644
+--- a/drivers/media/video/omap24xxcam.c
++++ b/drivers/media/video/omap24xxcam.c
+@@ -1285,9 +1285,6 @@ static int vidioc_g_parm(struct file *file, void *fh,
+ struct omap24xxcam_device *cam = ofh->cam;
+ int rval;
+
+- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+-
+ mutex_lock(&cam->mutex);
+ rval = vidioc_int_g_parm(cam->sdev, a);
+ mutex_unlock(&cam->mutex);
+@@ -1303,9 +1300,6 @@ static int vidioc_s_parm(struct file *file, void *fh,
+ struct v4l2_streamparm old_streamparm;
+ int rval;
+
+- if (a->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+-
+ mutex_lock(&cam->mutex);
+ if (cam->streaming) {
+ rval = -EBUSY;
+@@ -1665,7 +1659,6 @@ static int omap24xxcam_device_register(struct v4l2_int_device *s)
+ vfd->parent = cam->dev;
+
+ strlcpy(vfd->name, CAM_NAME, sizeof(vfd->name));
+- vfd->vfl_type = VID_TYPE_CAPTURE | VID_TYPE_CHROMAKEY;
+ vfd->fops = &omap24xxcam_fops;
+ vfd->minor = -1;
+ vfd->ioctl_ops = &omap24xxcam_ioctl_fops;
+diff --git a/drivers/media/video/ov7670.c b/drivers/media/video/ov7670.c
+index 05c14a2..0e2184e 100644
+--- a/drivers/media/video/ov7670.c
++++ b/drivers/media/video/ov7670.c
+@@ -12,18 +12,22 @@
+ */
+ #include <linux/init.h>
+ #include <linux/module.h>
+-#include <linux/slab.h>
++#include <linux/i2c.h>
+ #include <linux/delay.h>
+-#include <linux/videodev.h>
+-#include <media/v4l2-common.h>
++#include <linux/videodev2.h>
++#include <media/v4l2-device.h>
+ #include <media/v4l2-chip-ident.h>
+-#include <linux/i2c.h>
++#include <media/v4l2-i2c-drv.h>
+
+
+ MODULE_AUTHOR("Jonathan Corbet <corbet@lwn.net>");
+ MODULE_DESCRIPTION("A low-level driver for OmniVision ov7670 sensors");
+ MODULE_LICENSE("GPL");
+
++static int debug;
++module_param(debug, bool, 0644);
++MODULE_PARM_DESC(debug, "Debug level (0-1)");
++
+ /*
+ * Basic window sizes. These probably belong somewhere more globally
+ * useful.
+@@ -189,11 +193,16 @@ MODULE_LICENSE("GPL");
+ */
+ struct ov7670_format_struct; /* coming later */
+ struct ov7670_info {
++ struct v4l2_subdev sd;
+ struct ov7670_format_struct *fmt; /* Current format */
+ unsigned char sat; /* Saturation value */
+ int hue; /* Hue value */
+ };
+
++static inline struct ov7670_info *to_state(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct ov7670_info, sd);
++}
+
+
+
+@@ -400,24 +409,27 @@ static struct regval_list ov7670_fmt_raw[] = {
+ * Low-level register I/O.
+ */
+
+-static int ov7670_read(struct i2c_client *c, unsigned char reg,
++static int ov7670_read(struct v4l2_subdev *sd, unsigned char reg,
+ unsigned char *value)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+- ret = i2c_smbus_read_byte_data(c, reg);
++ ret = i2c_smbus_read_byte_data(client, reg);
+ if (ret >= 0) {
+- *value = (unsigned char) ret;
++ *value = (unsigned char)ret;
+ ret = 0;
+ }
+ return ret;
+ }
+
+
+-static int ov7670_write(struct i2c_client *c, unsigned char reg,
++static int ov7670_write(struct v4l2_subdev *sd, unsigned char reg,
+ unsigned char value)
+ {
+- int ret = i2c_smbus_write_byte_data(c, reg, value);
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ int ret = i2c_smbus_write_byte_data(client, reg, value);
++
+ if (reg == REG_COM7 && (value & COM7_RESET))
+ msleep(2); /* Wait for reset to run */
+ return ret;
+@@ -427,10 +439,10 @@ static int ov7670_write(struct i2c_client *c, unsigned char reg,
+ /*
+ * Write a list of register settings; ff/ff stops the process.
+ */
+-static int ov7670_write_array(struct i2c_client *c, struct regval_list *vals)
++static int ov7670_write_array(struct v4l2_subdev *sd, struct regval_list *vals)
+ {
+ while (vals->reg_num != 0xff || vals->value != 0xff) {
+- int ret = ov7670_write(c, vals->reg_num, vals->value);
++ int ret = ov7670_write(sd, vals->reg_num, vals->value);
+ if (ret < 0)
+ return ret;
+ vals++;
+@@ -442,34 +454,35 @@ static int ov7670_write_array(struct i2c_client *c, struct regval_list *vals)
+ /*
+ * Stuff that knows about the sensor.
+ */
+-static void ov7670_reset(struct i2c_client *client)
++static int ov7670_reset(struct v4l2_subdev *sd, u32 val)
+ {
+- ov7670_write(client, REG_COM7, COM7_RESET);
++ ov7670_write(sd, REG_COM7, COM7_RESET);
+ msleep(1);
++ return 0;
+ }
+
+
+-static int ov7670_init(struct i2c_client *client)
++static int ov7670_init(struct v4l2_subdev *sd, u32 val)
+ {
+- return ov7670_write_array(client, ov7670_default_regs);
++ return ov7670_write_array(sd, ov7670_default_regs);
+ }
+
+
+
+-static int ov7670_detect(struct i2c_client *client)
++static int ov7670_detect(struct v4l2_subdev *sd)
+ {
+ unsigned char v;
+ int ret;
+
+- ret = ov7670_init(client);
++ ret = ov7670_init(sd, 0);
+ if (ret < 0)
+ return ret;
+- ret = ov7670_read(client, REG_MIDH, &v);
++ ret = ov7670_read(sd, REG_MIDH, &v);
+ if (ret < 0)
+ return ret;
+ if (v != 0x7f) /* OV manuf. id. */
+ return -ENODEV;
+- ret = ov7670_read(client, REG_MIDL, &v);
++ ret = ov7670_read(sd, REG_MIDL, &v);
+ if (ret < 0)
+ return ret;
+ if (v != 0xa2)
+@@ -477,12 +490,12 @@ static int ov7670_detect(struct i2c_client *client)
+ /*
+ * OK, we know we have an OmniVision chip...but which one?
+ */
+- ret = ov7670_read(client, REG_PID, &v);
++ ret = ov7670_read(sd, REG_PID, &v);
+ if (ret < 0)
+ return ret;
+ if (v != 0x76) /* PID + VER = 0x76 / 0x73 */
+ return -ENODEV;
+- ret = ov7670_read(client, REG_VER, &v);
++ ret = ov7670_read(sd, REG_VER, &v);
+ if (ret < 0)
+ return ret;
+ if (v != 0x73) /* PID + VER = 0x76 / 0x73 */
+@@ -627,7 +640,7 @@ static struct ov7670_win_size {
+ /*
+ * Store a set of start/stop values into the camera.
+ */
+-static int ov7670_set_hw(struct i2c_client *client, int hstart, int hstop,
++static int ov7670_set_hw(struct v4l2_subdev *sd, int hstart, int hstop,
+ int vstart, int vstop)
+ {
+ int ret;
+@@ -637,26 +650,26 @@ static int ov7670_set_hw(struct i2c_client *client, int hstart, int hstop,
+ * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is
+ * a mystery "edge offset" value in the top two bits of href.
+ */
+- ret = ov7670_write(client, REG_HSTART, (hstart >> 3) & 0xff);
+- ret += ov7670_write(client, REG_HSTOP, (hstop >> 3) & 0xff);
+- ret += ov7670_read(client, REG_HREF, &v);
++ ret = ov7670_write(sd, REG_HSTART, (hstart >> 3) & 0xff);
++ ret += ov7670_write(sd, REG_HSTOP, (hstop >> 3) & 0xff);
++ ret += ov7670_read(sd, REG_HREF, &v);
+ v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7);
+ msleep(10);
+- ret += ov7670_write(client, REG_HREF, v);
++ ret += ov7670_write(sd, REG_HREF, v);
+ /*
+ * Vertical: similar arrangement, but only 10 bits.
+ */
+- ret += ov7670_write(client, REG_VSTART, (vstart >> 2) & 0xff);
+- ret += ov7670_write(client, REG_VSTOP, (vstop >> 2) & 0xff);
+- ret += ov7670_read(client, REG_VREF, &v);
++ ret += ov7670_write(sd, REG_VSTART, (vstart >> 2) & 0xff);
++ ret += ov7670_write(sd, REG_VSTOP, (vstop >> 2) & 0xff);
++ ret += ov7670_read(sd, REG_VREF, &v);
+ v = (v & 0xf0) | ((vstop & 0x3) << 2) | (vstart & 0x3);
+ msleep(10);
+- ret += ov7670_write(client, REG_VREF, v);
++ ret += ov7670_write(sd, REG_VREF, v);
+ return ret;
+ }
+
+
+-static int ov7670_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt)
++static int ov7670_enum_fmt(struct v4l2_subdev *sd, struct v4l2_fmtdesc *fmt)
+ {
+ struct ov7670_format_struct *ofmt;
+
+@@ -671,7 +684,8 @@ static int ov7670_enum_fmt(struct i2c_client *c, struct v4l2_fmtdesc *fmt)
+ }
+
+
+-static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt,
++static int ov7670_try_fmt_internal(struct v4l2_subdev *sd,
++ struct v4l2_format *fmt,
+ struct ov7670_format_struct **ret_fmt,
+ struct ov7670_win_size **ret_wsize)
+ {
+@@ -715,18 +729,23 @@ static int ov7670_try_fmt(struct i2c_client *c, struct v4l2_format *fmt,
+ return 0;
+ }
+
++static int ov7670_try_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
++{
++ return ov7670_try_fmt_internal(sd, fmt, NULL, NULL);
++}
++
+ /*
+ * Set a format.
+ */
+-static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt)
++static int ov7670_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *fmt)
+ {
+ int ret;
+ struct ov7670_format_struct *ovfmt;
+ struct ov7670_win_size *wsize;
+- struct ov7670_info *info = i2c_get_clientdata(c);
+- unsigned char com7, clkrc;
++ struct ov7670_info *info = to_state(sd);
++ unsigned char com7, clkrc = 0;
+
+- ret = ov7670_try_fmt(c, fmt, &ovfmt, &wsize);
++ ret = ov7670_try_fmt_internal(sd, fmt, &ovfmt, &wsize);
+ if (ret)
+ return ret;
+ /*
+@@ -735,7 +754,7 @@ static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt)
+ * the colors.
+ */
+ if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565) {
+- ret = ov7670_read(c, REG_CLKRC, &clkrc);
++ ret = ov7670_read(sd, REG_CLKRC, &clkrc);
+ if (ret)
+ return ret;
+ }
+@@ -747,20 +766,20 @@ static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt)
+ */
+ com7 = ovfmt->regs[0].value;
+ com7 |= wsize->com7_bit;
+- ov7670_write(c, REG_COM7, com7);
++ ov7670_write(sd, REG_COM7, com7);
+ /*
+ * Now write the rest of the array. Also store start/stops
+ */
+- ov7670_write_array(c, ovfmt->regs + 1);
+- ov7670_set_hw(c, wsize->hstart, wsize->hstop, wsize->vstart,
++ ov7670_write_array(sd, ovfmt->regs + 1);
++ ov7670_set_hw(sd, wsize->hstart, wsize->hstop, wsize->vstart,
+ wsize->vstop);
+ ret = 0;
+ if (wsize->regs)
+- ret = ov7670_write_array(c, wsize->regs);
++ ret = ov7670_write_array(sd, wsize->regs);
+ info->fmt = ovfmt;
+
+ if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_RGB565 && ret == 0)
+- ret = ov7670_write(c, REG_CLKRC, clkrc);
++ ret = ov7670_write(sd, REG_CLKRC, clkrc);
+ return ret;
+ }
+
+@@ -768,7 +787,7 @@ static int ov7670_s_fmt(struct i2c_client *c, struct v4l2_format *fmt)
+ * Implement G/S_PARM. There is a "high quality" mode we could try
+ * to do someday; for now, we just do the frame rate tweak.
+ */
+-static int ov7670_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
++static int ov7670_g_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+ {
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+ unsigned char clkrc;
+@@ -776,7 +795,7 @@ static int ov7670_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
+
+ if (parms->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+- ret = ov7670_read(c, REG_CLKRC, &clkrc);
++ ret = ov7670_read(sd, REG_CLKRC, &clkrc);
+ if (ret < 0)
+ return ret;
+ memset(cp, 0, sizeof(struct v4l2_captureparm));
+@@ -788,7 +807,7 @@ static int ov7670_g_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
+ return 0;
+ }
+
+-static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
++static int ov7670_s_parm(struct v4l2_subdev *sd, struct v4l2_streamparm *parms)
+ {
+ struct v4l2_captureparm *cp = &parms->parm.capture;
+ struct v4l2_fract *tpf = &cp->timeperframe;
+@@ -802,7 +821,7 @@ static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
+ /*
+ * CLKRC has a reserved bit, so let's preserve it.
+ */
+- ret = ov7670_read(c, REG_CLKRC, &clkrc);
++ ret = ov7670_read(sd, REG_CLKRC, &clkrc);
+ if (ret < 0)
+ return ret;
+ if (tpf->numerator == 0 || tpf->denominator == 0)
+@@ -816,7 +835,7 @@ static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
+ clkrc = (clkrc & 0x80) | div;
+ tpf->numerator = 1;
+ tpf->denominator = OV7670_FRAME_RATE/div;
+- return ov7670_write(c, REG_CLKRC, clkrc);
++ return ov7670_write(sd, REG_CLKRC, clkrc);
+ }
+
+
+@@ -829,7 +848,7 @@ static int ov7670_s_parm(struct i2c_client *c, struct v4l2_streamparm *parms)
+
+
+
+-static int ov7670_store_cmatrix(struct i2c_client *client,
++static int ov7670_store_cmatrix(struct v4l2_subdev *sd,
+ int matrix[CMATRIX_LEN])
+ {
+ int i, ret;
+@@ -839,7 +858,7 @@ static int ov7670_store_cmatrix(struct i2c_client *client,
+ * Weird crap seems to exist in the upper part of
+ * the sign bits register, so let's preserve it.
+ */
+- ret = ov7670_read(client, REG_CMATRIX_SIGN, &signbits);
++ ret = ov7670_read(sd, REG_CMATRIX_SIGN, &signbits);
+ signbits &= 0xc0;
+
+ for (i = 0; i < CMATRIX_LEN; i++) {
+@@ -858,9 +877,9 @@ static int ov7670_store_cmatrix(struct i2c_client *client,
+ else
+ raw = matrix[i] & 0xff;
+ }
+- ret += ov7670_write(client, REG_CMATRIX_BASE + i, raw);
++ ret += ov7670_write(sd, REG_CMATRIX_BASE + i, raw);
+ }
+- ret += ov7670_write(client, REG_CMATRIX_SIGN, signbits);
++ ret += ov7670_write(sd, REG_CMATRIX_SIGN, signbits);
+ return ret;
+ }
+
+@@ -943,29 +962,29 @@ static void ov7670_calc_cmatrix(struct ov7670_info *info,
+
+
+
+-static int ov7670_t_sat(struct i2c_client *client, int value)
++static int ov7670_s_sat(struct v4l2_subdev *sd, int value)
+ {
+- struct ov7670_info *info = i2c_get_clientdata(client);
++ struct ov7670_info *info = to_state(sd);
+ int matrix[CMATRIX_LEN];
+ int ret;
+
+ info->sat = value;
+ ov7670_calc_cmatrix(info, matrix);
+- ret = ov7670_store_cmatrix(client, matrix);
++ ret = ov7670_store_cmatrix(sd, matrix);
+ return ret;
+ }
+
+-static int ov7670_q_sat(struct i2c_client *client, __s32 *value)
++static int ov7670_g_sat(struct v4l2_subdev *sd, __s32 *value)
+ {
+- struct ov7670_info *info = i2c_get_clientdata(client);
++ struct ov7670_info *info = to_state(sd);
+
+ *value = info->sat;
+ return 0;
+ }
+
+-static int ov7670_t_hue(struct i2c_client *client, int value)
++static int ov7670_s_hue(struct v4l2_subdev *sd, int value)
+ {
+- struct ov7670_info *info = i2c_get_clientdata(client);
++ struct ov7670_info *info = to_state(sd);
+ int matrix[CMATRIX_LEN];
+ int ret;
+
+@@ -973,14 +992,14 @@ static int ov7670_t_hue(struct i2c_client *client, int value)
+ return -EINVAL;
+ info->hue = value;
+ ov7670_calc_cmatrix(info, matrix);
+- ret = ov7670_store_cmatrix(client, matrix);
++ ret = ov7670_store_cmatrix(sd, matrix);
+ return ret;
+ }
+
+
+-static int ov7670_q_hue(struct i2c_client *client, __s32 *value)
++static int ov7670_g_hue(struct v4l2_subdev *sd, __s32 *value)
+ {
+- struct ov7670_info *info = i2c_get_clientdata(client);
++ struct ov7670_info *info = to_state(sd);
+
+ *value = info->hue;
+ return 0;
+@@ -994,8 +1013,7 @@ static unsigned char ov7670_sm_to_abs(unsigned char v)
+ {
+ if ((v & 0x80) == 0)
+ return v + 128;
+- else
+- return 128 - (v & 0x7f);
++ return 128 - (v & 0x7f);
+ }
+
+
+@@ -1003,369 +1021,275 @@ static unsigned char ov7670_abs_to_sm(unsigned char v)
+ {
+ if (v > 127)
+ return v & 0x7f;
+- else
+- return (128 - v) | 0x80;
++ return (128 - v) | 0x80;
+ }
+
+-static int ov7670_t_brightness(struct i2c_client *client, int value)
++static int ov7670_s_brightness(struct v4l2_subdev *sd, int value)
+ {
+ unsigned char com8 = 0, v;
+ int ret;
+
+- ov7670_read(client, REG_COM8, &com8);
++ ov7670_read(sd, REG_COM8, &com8);
+ com8 &= ~COM8_AEC;
+- ov7670_write(client, REG_COM8, com8);
++ ov7670_write(sd, REG_COM8, com8);
+ v = ov7670_abs_to_sm(value);
+- ret = ov7670_write(client, REG_BRIGHT, v);
++ ret = ov7670_write(sd, REG_BRIGHT, v);
+ return ret;
+ }
+
+-static int ov7670_q_brightness(struct i2c_client *client, __s32 *value)
++static int ov7670_g_brightness(struct v4l2_subdev *sd, __s32 *value)
+ {
+ unsigned char v = 0;
+- int ret = ov7670_read(client, REG_BRIGHT, &v);
++ int ret = ov7670_read(sd, REG_BRIGHT, &v);
+
+ *value = ov7670_sm_to_abs(v);
+ return ret;
+ }
+
+-static int ov7670_t_contrast(struct i2c_client *client, int value)
++static int ov7670_s_contrast(struct v4l2_subdev *sd, int value)
+ {
+- return ov7670_write(client, REG_CONTRAS, (unsigned char) value);
++ return ov7670_write(sd, REG_CONTRAS, (unsigned char) value);
+ }
+
+-static int ov7670_q_contrast(struct i2c_client *client, __s32 *value)
++static int ov7670_g_contrast(struct v4l2_subdev *sd, __s32 *value)
+ {
+ unsigned char v = 0;
+- int ret = ov7670_read(client, REG_CONTRAS, &v);
++ int ret = ov7670_read(sd, REG_CONTRAS, &v);
+
+ *value = v;
+ return ret;
+ }
+
+-static int ov7670_q_hflip(struct i2c_client *client, __s32 *value)
++static int ov7670_g_hflip(struct v4l2_subdev *sd, __s32 *value)
+ {
+ int ret;
+ unsigned char v = 0;
+
+- ret = ov7670_read(client, REG_MVFP, &v);
++ ret = ov7670_read(sd, REG_MVFP, &v);
+ *value = (v & MVFP_MIRROR) == MVFP_MIRROR;
+ return ret;
+ }
+
+
+-static int ov7670_t_hflip(struct i2c_client *client, int value)
++static int ov7670_s_hflip(struct v4l2_subdev *sd, int value)
+ {
+ unsigned char v = 0;
+ int ret;
+
+- ret = ov7670_read(client, REG_MVFP, &v);
++ ret = ov7670_read(sd, REG_MVFP, &v);
+ if (value)
+ v |= MVFP_MIRROR;
+ else
+ v &= ~MVFP_MIRROR;
+ msleep(10); /* FIXME */
+- ret += ov7670_write(client, REG_MVFP, v);
++ ret += ov7670_write(sd, REG_MVFP, v);
+ return ret;
+ }
+
+
+
+-static int ov7670_q_vflip(struct i2c_client *client, __s32 *value)
++static int ov7670_g_vflip(struct v4l2_subdev *sd, __s32 *value)
+ {
+ int ret;
+ unsigned char v = 0;
+
+- ret = ov7670_read(client, REG_MVFP, &v);
++ ret = ov7670_read(sd, REG_MVFP, &v);
+ *value = (v & MVFP_FLIP) == MVFP_FLIP;
+ return ret;
+ }
+
+
+-static int ov7670_t_vflip(struct i2c_client *client, int value)
++static int ov7670_s_vflip(struct v4l2_subdev *sd, int value)
+ {
+ unsigned char v = 0;
+ int ret;
+
+- ret = ov7670_read(client, REG_MVFP, &v);
++ ret = ov7670_read(sd, REG_MVFP, &v);
+ if (value)
+ v |= MVFP_FLIP;
+ else
+ v &= ~MVFP_FLIP;
+ msleep(10); /* FIXME */
+- ret += ov7670_write(client, REG_MVFP, v);
++ ret += ov7670_write(sd, REG_MVFP, v);
+ return ret;
+ }
+
+-
+-static struct ov7670_control {
+- struct v4l2_queryctrl qc;
+- int (*query)(struct i2c_client *c, __s32 *value);
+- int (*tweak)(struct i2c_client *c, int value);
+-} ov7670_controls[] =
++static int ov7670_queryctrl(struct v4l2_subdev *sd,
++ struct v4l2_queryctrl *qc)
+ {
+- {
+- .qc = {
+- .id = V4L2_CID_BRIGHTNESS,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- .name = "Brightness",
+- .minimum = 0,
+- .maximum = 255,
+- .step = 1,
+- .default_value = 0x80,
+- .flags = V4L2_CTRL_FLAG_SLIDER
+- },
+- .tweak = ov7670_t_brightness,
+- .query = ov7670_q_brightness,
+- },
+- {
+- .qc = {
+- .id = V4L2_CID_CONTRAST,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- .name = "Contrast",
+- .minimum = 0,
+- .maximum = 127,
+- .step = 1,
+- .default_value = 0x40, /* XXX ov7670 spec */
+- .flags = V4L2_CTRL_FLAG_SLIDER
+- },
+- .tweak = ov7670_t_contrast,
+- .query = ov7670_q_contrast,
+- },
+- {
+- .qc = {
+- .id = V4L2_CID_SATURATION,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- .name = "Saturation",
+- .minimum = 0,
+- .maximum = 256,
+- .step = 1,
+- .default_value = 0x80,
+- .flags = V4L2_CTRL_FLAG_SLIDER
+- },
+- .tweak = ov7670_t_sat,
+- .query = ov7670_q_sat,
+- },
+- {
+- .qc = {
+- .id = V4L2_CID_HUE,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- .name = "HUE",
+- .minimum = -180,
+- .maximum = 180,
+- .step = 5,
+- .default_value = 0,
+- .flags = V4L2_CTRL_FLAG_SLIDER
+- },
+- .tweak = ov7670_t_hue,
+- .query = ov7670_q_hue,
+- },
+- {
+- .qc = {
+- .id = V4L2_CID_VFLIP,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- .name = "Vertical flip",
+- .minimum = 0,
+- .maximum = 1,
+- .step = 1,
+- .default_value = 0,
+- },
+- .tweak = ov7670_t_vflip,
+- .query = ov7670_q_vflip,
+- },
+- {
+- .qc = {
+- .id = V4L2_CID_HFLIP,
+- .type = V4L2_CTRL_TYPE_BOOLEAN,
+- .name = "Horizontal mirror",
+- .minimum = 0,
+- .maximum = 1,
+- .step = 1,
+- .default_value = 0,
+- },
+- .tweak = ov7670_t_hflip,
+- .query = ov7670_q_hflip,
+- },
+-};
+-#define N_CONTROLS (ARRAY_SIZE(ov7670_controls))
++ /* Fill in min, max, step and default value for these controls. */
++ switch (qc->id) {
++ case V4L2_CID_BRIGHTNESS:
++ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
++ case V4L2_CID_CONTRAST:
++ return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
++ case V4L2_CID_VFLIP:
++ case V4L2_CID_HFLIP:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
++ case V4L2_CID_SATURATION:
++ return v4l2_ctrl_query_fill(qc, 0, 256, 1, 128);
++ case V4L2_CID_HUE:
++ return v4l2_ctrl_query_fill(qc, -180, 180, 5, 0);
++ }
++ return -EINVAL;
++}
+
+-static struct ov7670_control *ov7670_find_control(__u32 id)
++static int ov7670_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+ {
+- int i;
+-
+- for (i = 0; i < N_CONTROLS; i++)
+- if (ov7670_controls[i].qc.id == id)
+- return ov7670_controls + i;
+- return NULL;
++ switch (ctrl->id) {
++ case V4L2_CID_BRIGHTNESS:
++ return ov7670_g_brightness(sd, &ctrl->value);
++ case V4L2_CID_CONTRAST:
++ return ov7670_g_contrast(sd, &ctrl->value);
++ case V4L2_CID_SATURATION:
++ return ov7670_g_sat(sd, &ctrl->value);
++ case V4L2_CID_HUE:
++ return ov7670_g_hue(sd, &ctrl->value);
++ case V4L2_CID_VFLIP:
++ return ov7670_g_vflip(sd, &ctrl->value);
++ case V4L2_CID_HFLIP:
++ return ov7670_g_hflip(sd, &ctrl->value);
++ }
++ return -EINVAL;
+ }
+
++static int ov7670_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
++{
++ switch (ctrl->id) {
++ case V4L2_CID_BRIGHTNESS:
++ return ov7670_s_brightness(sd, ctrl->value);
++ case V4L2_CID_CONTRAST:
++ return ov7670_s_contrast(sd, ctrl->value);
++ case V4L2_CID_SATURATION:
++ return ov7670_s_sat(sd, ctrl->value);
++ case V4L2_CID_HUE:
++ return ov7670_s_hue(sd, ctrl->value);
++ case V4L2_CID_VFLIP:
++ return ov7670_s_vflip(sd, ctrl->value);
++ case V4L2_CID_HFLIP:
++ return ov7670_s_hflip(sd, ctrl->value);
++ }
++ return -EINVAL;
++}
+
+-static int ov7670_queryctrl(struct i2c_client *client,
+- struct v4l2_queryctrl *qc)
++static int ov7670_g_chip_ident(struct v4l2_subdev *sd,
++ struct v4l2_dbg_chip_ident *chip)
+ {
+- struct ov7670_control *ctrl = ov7670_find_control(qc->id);
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+- if (ctrl == NULL)
+- return -EINVAL;
+- *qc = ctrl->qc;
+- return 0;
++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_OV7670, 0);
+ }
+
+-static int ov7670_g_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++static int ov7670_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+ {
+- struct ov7670_control *octrl = ov7670_find_control(ctrl->id);
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ unsigned char val = 0;
+ int ret;
+
+- if (octrl == NULL)
++ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+- ret = octrl->query(client, &ctrl->value);
+- if (ret >= 0)
+- return 0;
++ if (!capable(CAP_SYS_ADMIN))
++ return -EPERM;
++ ret = ov7670_read(sd, reg->reg & 0xff, &val);
++ reg->val = val;
++ reg->size = 1;
+ return ret;
+ }
+
+-static int ov7670_s_ctrl(struct i2c_client *client, struct v4l2_control *ctrl)
++static int ov7670_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+ {
+- struct ov7670_control *octrl = ov7670_find_control(ctrl->id);
+- int ret;
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+- if (octrl == NULL)
++ if (!v4l2_chip_match_i2c_client(client, &reg->match))
+ return -EINVAL;
+- ret = octrl->tweak(client, ctrl->value);
+- if (ret >= 0)
+- return 0;
+- return ret;
++ if (!capable(CAP_SYS_ADMIN))
++ return -EPERM;
++ ov7670_write(sd, reg->reg & 0xff, reg->val & 0xff);
++ return 0;
+ }
++#endif
++
++/* ----------------------------------------------------------------------- */
++
++static const struct v4l2_subdev_core_ops ov7670_core_ops = {
++ .g_chip_ident = ov7670_g_chip_ident,
++ .g_ctrl = ov7670_g_ctrl,
++ .s_ctrl = ov7670_s_ctrl,
++ .queryctrl = ov7670_queryctrl,
++ .reset = ov7670_reset,
++ .init = ov7670_init,
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++ .g_register = ov7670_g_register,
++ .s_register = ov7670_s_register,
++#endif
++};
+
++static const struct v4l2_subdev_video_ops ov7670_video_ops = {
++ .enum_fmt = ov7670_enum_fmt,
++ .try_fmt = ov7670_try_fmt,
++ .s_fmt = ov7670_s_fmt,
++ .s_parm = ov7670_s_parm,
++ .g_parm = ov7670_g_parm,
++};
+
++static const struct v4l2_subdev_ops ov7670_ops = {
++ .core = &ov7670_core_ops,
++ .video = &ov7670_video_ops,
++};
+
++/* ----------------------------------------------------------------------- */
+
+-
+-
+-/*
+- * Basic i2c stuff.
+- */
+-static struct i2c_driver ov7670_driver;
+-
+-static int ov7670_attach(struct i2c_adapter *adapter)
++static int ov7670_probe(struct i2c_client *client,
++ const struct i2c_device_id *id)
+ {
+- int ret;
+- struct i2c_client *client;
++ struct v4l2_subdev *sd;
+ struct ov7670_info *info;
++ int ret;
+
+- /*
+- * For now: only deal with adapters we recognize.
+- */
+- if (adapter->id != I2C_HW_SMBUS_CAFE)
+- return -ENODEV;
+-
+- client = kzalloc(sizeof (struct i2c_client), GFP_KERNEL);
+- if (! client)
++ info = kzalloc(sizeof(struct ov7670_info), GFP_KERNEL);
++ if (info == NULL)
+ return -ENOMEM;
+- client->adapter = adapter;
+- client->addr = OV7670_I2C_ADDR;
+- client->driver = &ov7670_driver,
+- strcpy(client->name, "OV7670");
+- /*
+- * Set up our info structure.
+- */
+- info = kzalloc(sizeof (struct ov7670_info), GFP_KERNEL);
+- if (! info) {
+- ret = -ENOMEM;
+- goto out_free;
++ sd = &info->sd;
++ v4l2_i2c_subdev_init(sd, client, &ov7670_ops);
++
++ /* Make sure it's an ov7670 */
++ ret = ov7670_detect(sd);
++ if (ret) {
++ v4l_dbg(1, debug, client,
++ "chip found @ 0x%x (%s) is not an ov7670 chip.\n",
++ client->addr << 1, client->adapter->name);
++ kfree(info);
++ return ret;
+ }
++ v4l_info(client, "chip found @ 0x%02x (%s)\n",
++ client->addr << 1, client->adapter->name);
++
+ info->fmt = &ov7670_formats[0];
+ info->sat = 128; /* Review this */
+- i2c_set_clientdata(client, info);
+
+- /*
+- * Make sure it's an ov7670
+- */
+- ret = ov7670_detect(client);
+- if (ret)
+- goto out_free_info;
+- ret = i2c_attach_client(client);
+- if (ret)
+- goto out_free_info;
+ return 0;
+-
+- out_free_info:
+- kfree(info);
+- out_free:
+- kfree(client);
+- return ret;
+ }
+
+
+-static int ov7670_detach(struct i2c_client *client)
++static int ov7670_remove(struct i2c_client *client)
+ {
+- i2c_detach_client(client);
+- kfree(i2c_get_clientdata(client));
+- kfree(client);
+- return 0;
+-}
+-
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+-static int ov7670_command(struct i2c_client *client, unsigned int cmd,
+- void *arg)
+-{
+- switch (cmd) {
+- case VIDIOC_DBG_G_CHIP_IDENT:
+- return v4l2_chip_ident_i2c_client(client, arg, V4L2_IDENT_OV7670, 0);
+-
+- case VIDIOC_INT_RESET:
+- ov7670_reset(client);
+- return 0;
+-
+- case VIDIOC_INT_INIT:
+- return ov7670_init(client);
+-
+- case VIDIOC_ENUM_FMT:
+- return ov7670_enum_fmt(client, (struct v4l2_fmtdesc *) arg);
+- case VIDIOC_TRY_FMT:
+- return ov7670_try_fmt(client, (struct v4l2_format *) arg, NULL, NULL);
+- case VIDIOC_S_FMT:
+- return ov7670_s_fmt(client, (struct v4l2_format *) arg);
+- case VIDIOC_QUERYCTRL:
+- return ov7670_queryctrl(client, (struct v4l2_queryctrl *) arg);
+- case VIDIOC_S_CTRL:
+- return ov7670_s_ctrl(client, (struct v4l2_control *) arg);
+- case VIDIOC_G_CTRL:
+- return ov7670_g_ctrl(client, (struct v4l2_control *) arg);
+- case VIDIOC_S_PARM:
+- return ov7670_s_parm(client, (struct v4l2_streamparm *) arg);
+- case VIDIOC_G_PARM:
+- return ov7670_g_parm(client, (struct v4l2_streamparm *) arg);
+- }
+- return -EINVAL;
++ v4l2_device_unregister_subdev(sd);
++ kfree(to_state(sd));
++ return 0;
+ }
+
+-
+-
+-static struct i2c_driver ov7670_driver = {
+- .driver = {
+- .name = "ov7670",
+- },
+- .id = I2C_DRIVERID_OV7670,
+- .attach_adapter = ov7670_attach,
+- .detach_client = ov7670_detach,
+- .command = ov7670_command,
++static const struct i2c_device_id ov7670_id[] = {
++ { "ov7670", 0 },
++ { }
+ };
++MODULE_DEVICE_TABLE(i2c, ov7670_id);
+
+-
+-/*
+- * Module initialization
+- */
+-static int __init ov7670_mod_init(void)
+-{
+- printk(KERN_NOTICE "OmniVision ov7670 sensor driver, at your service\n");
+- return i2c_add_driver(&ov7670_driver);
+-}
+-
+-static void __exit ov7670_mod_exit(void)
+-{
+- i2c_del_driver(&ov7670_driver);
+-}
+-
+-module_init(ov7670_mod_init);
+-module_exit(ov7670_mod_exit);
++static struct v4l2_i2c_driver_data v4l2_i2c_data = {
++ .name = "ov7670",
++ .probe = ov7670_probe,
++ .remove = ov7670_remove,
++ .id_table = ov7670_id,
++};
+diff --git a/drivers/media/video/ov772x.c b/drivers/media/video/ov772x.c
+index 3c9e0ba..84b0fc1 100644
+--- a/drivers/media/video/ov772x.c
++++ b/drivers/media/video/ov772x.c
+@@ -217,10 +217,11 @@
+ #define OCAP_4x 0x03 /* 4x */
+
+ /* COM3 */
+-#define SWAP_MASK 0x38
++#define SWAP_MASK (SWAP_RGB | SWAP_YUV | SWAP_ML)
++#define IMG_MASK (VFLIP_IMG | HFLIP_IMG)
+
+-#define VFIMG_ON_OFF 0x80 /* Vertical flip image ON/OFF selection */
+-#define HMIMG_ON_OFF 0x40 /* Horizontal mirror image ON/OFF selection */
++#define VFLIP_IMG 0x80 /* Vertical flip image ON/OFF selection */
++#define HFLIP_IMG 0x40 /* Horizontal mirror image ON/OFF selection */
+ #define SWAP_RGB 0x20 /* Swap B/R output sequence in RGB mode */
+ #define SWAP_YUV 0x10 /* Swap Y/UV output sequence in YUV mode */
+ #define SWAP_ML 0x08 /* Swap output MSB/LSB */
+@@ -271,11 +272,13 @@
+ #define SLCT_QVGA 0x40 /* 1 : QVGA */
+ #define ITU656_ON_OFF 0x20 /* ITU656 protocol ON/OFF selection */
+ /* RGB output format control */
++#define FMT_MASK 0x0c /* Mask of color format */
+ #define FMT_GBR422 0x00 /* 00 : GBR 4:2:2 */
+ #define FMT_RGB565 0x04 /* 01 : RGB 565 */
+ #define FMT_RGB555 0x08 /* 10 : RGB 555 */
+ #define FMT_RGB444 0x0c /* 11 : RGB 444 */
+ /* Output format control */
++#define OFMT_MASK 0x03 /* Mask of output format */
+ #define OFMT_YUV 0x00 /* 00 : YUV */
+ #define OFMT_P_BRAW 0x01 /* 01 : Processed Bayer RAW */
+ #define OFMT_RGB 0x02 /* 10 : RGB */
+@@ -299,7 +302,7 @@
+ #define GAIN_2x 0x00 /* 000 : 2x */
+ #define GAIN_4x 0x10 /* 001 : 4x */
+ #define GAIN_8x 0x20 /* 010 : 8x */
+-#define GAIN_16x 0x30 /* 011 : 16x */
++#define GAIN_16x 0x30 /* 011 : 16x */
+ #define GAIN_32x 0x40 /* 100 : 32x */
+ #define GAIN_64x 0x50 /* 101 : 64x */
+ #define GAIN_128x 0x60 /* 110 : 128x */
+@@ -356,13 +359,6 @@
+ #define VOSZ_QVGA 0x78
+
+ /*
+- * bit configure (32 bit)
+- * this is used in struct ov772x_color_format :: option
+- */
+-#define OP_UV 0x00000001
+-#define OP_SWAP_RGB 0x00000002
+-
+-/*
+ * ID
+ */
+ #define OV7720 0x7720
+@@ -380,8 +376,9 @@ struct regval_list {
+ struct ov772x_color_format {
+ char *name;
+ __u32 fourcc;
+- const struct regval_list *regs;
+- unsigned int option;
++ u8 dsp3;
++ u8 com3;
++ u8 com7;
+ };
+
+ struct ov772x_win_size {
+@@ -399,39 +396,13 @@ struct ov772x_priv {
+ const struct ov772x_color_format *fmt;
+ const struct ov772x_win_size *win;
+ int model;
++ unsigned int flag_vflip:1;
++ unsigned int flag_hflip:1;
+ };
+
+ #define ENDMARKER { 0xff, 0xff }
+
+ /*
+- * register setting for color format
+- */
+-static const struct regval_list ov772x_RGB555_regs[] = {
+- { COM3, 0x00 },
+- { COM7, FMT_RGB555 | OFMT_RGB },
+- ENDMARKER,
+-};
+-
+-static const struct regval_list ov772x_RGB565_regs[] = {
+- { COM3, 0x00 },
+- { COM7, FMT_RGB565 | OFMT_RGB },
+- ENDMARKER,
+-};
+-
+-static const struct regval_list ov772x_YYUV_regs[] = {
+- { COM3, SWAP_YUV },
+- { COM7, OFMT_YUV },
+- ENDMARKER,
+-};
+-
+-static const struct regval_list ov772x_UVYY_regs[] = {
+- { COM3, 0x00 },
+- { COM7, OFMT_YUV },
+- ENDMARKER,
+-};
+-
+-
+-/*
+ * register setting for window size
+ */
+ static const struct regval_list ov772x_qvga_regs[] = {
+@@ -500,38 +471,48 @@ static const struct soc_camera_data_format ov772x_fmt_lists[] = {
+ /*
+ * color format list
+ */
+-#define T_YUYV 0
+ static const struct ov772x_color_format ov772x_cfmts[] = {
+- [T_YUYV] = {
++ {
+ SETFOURCC(YUYV),
+- .regs = ov772x_YYUV_regs,
++ .dsp3 = 0x0,
++ .com3 = SWAP_YUV,
++ .com7 = OFMT_YUV,
+ },
+ {
+ SETFOURCC(YVYU),
+- .regs = ov772x_YYUV_regs,
+- .option = OP_UV,
++ .dsp3 = UV_ON,
++ .com3 = SWAP_YUV,
++ .com7 = OFMT_YUV,
+ },
+ {
+ SETFOURCC(UYVY),
+- .regs = ov772x_UVYY_regs,
++ .dsp3 = 0x0,
++ .com3 = 0x0,
++ .com7 = OFMT_YUV,
+ },
+ {
+ SETFOURCC(RGB555),
+- .regs = ov772x_RGB555_regs,
+- .option = OP_SWAP_RGB,
++ .dsp3 = 0x0,
++ .com3 = SWAP_RGB,
++ .com7 = FMT_RGB555 | OFMT_RGB,
+ },
+ {
+ SETFOURCC(RGB555X),
+- .regs = ov772x_RGB555_regs,
++ .dsp3 = 0x0,
++ .com3 = 0x0,
++ .com7 = FMT_RGB555 | OFMT_RGB,
+ },
+ {
+ SETFOURCC(RGB565),
+- .regs = ov772x_RGB565_regs,
+- .option = OP_SWAP_RGB,
++ .dsp3 = 0x0,
++ .com3 = SWAP_RGB,
++ .com7 = FMT_RGB565 | OFMT_RGB,
+ },
+ {
+ SETFOURCC(RGB565X),
+- .regs = ov772x_RGB565_regs,
++ .dsp3 = 0x0,
++ .com3 = 0x0,
++ .com7 = FMT_RGB565 | OFMT_RGB,
+ },
+ };
+
+@@ -562,6 +543,27 @@ static const struct ov772x_win_size ov772x_win_qvga = {
+ .regs = ov772x_qvga_regs,
+ };
+
++static const struct v4l2_queryctrl ov772x_controls[] = {
++ {
++ .id = V4L2_CID_VFLIP,
++ .type = V4L2_CTRL_TYPE_BOOLEAN,
++ .name = "Flip Vertically",
++ .minimum = 0,
++ .maximum = 1,
++ .step = 1,
++ .default_value = 0,
++ },
++ {
++ .id = V4L2_CID_HFLIP,
++ .type = V4L2_CTRL_TYPE_BOOLEAN,
++ .name = "Flip Horizontally",
++ .minimum = 0,
++ .maximum = 1,
++ .step = 1,
++ .default_value = 0,
++ },
++};
++
+
+ /*
+ * general function
+@@ -587,8 +589,11 @@ static int ov772x_mask_set(struct i2c_client *client,
+ u8 set)
+ {
+ s32 val = i2c_smbus_read_byte_data(client, command);
++ if (val < 0)
++ return val;
++
+ val &= ~mask;
+- val |= set;
++ val |= set & mask;
+
+ return i2c_smbus_write_byte_data(client, command, val);
+ }
+@@ -635,74 +640,24 @@ static int ov772x_release(struct soc_camera_device *icd)
+ static int ov772x_start_capture(struct soc_camera_device *icd)
+ {
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+- int ret;
+-
+- if (!priv->win)
+- priv->win = &ov772x_win_vga;
+- if (!priv->fmt)
+- priv->fmt = &ov772x_cfmts[T_YUYV];
+-
+- /*
+- * reset hardware
+- */
+- ov772x_reset(priv->client);
+
+- /*
+- * set color format
+- */
+- ret = ov772x_write_array(priv->client, priv->fmt->regs);
+- if (ret < 0)
+- goto start_end;
+-
+- /*
+- * set size format
+- */
+- ret = ov772x_write_array(priv->client, priv->win->regs);
+- if (ret < 0)
+- goto start_end;
+-
+- /*
+- * set COM7 bit ( QVGA or VGA )
+- */
+- ret = ov772x_mask_set(priv->client,
+- COM7, SLCT_MASK, priv->win->com7_bit);
+- if (ret < 0)
+- goto start_end;
+-
+- /*
+- * set UV setting
+- */
+- if (priv->fmt->option & OP_UV) {
+- ret = ov772x_mask_set(priv->client,
+- DSP_CTRL3, UV_MASK, UV_ON);
+- if (ret < 0)
+- goto start_end;
++ if (!priv->win || !priv->fmt) {
++ dev_err(&icd->dev, "norm or win select error\n");
++ return -EPERM;
+ }
+
+- /*
+- * set SWAP setting
+- */
+- if (priv->fmt->option & OP_SWAP_RGB) {
+- ret = ov772x_mask_set(priv->client,
+- COM3, SWAP_MASK, SWAP_RGB);
+- if (ret < 0)
+- goto start_end;
+- }
++ ov772x_mask_set(priv->client, COM2, SOFT_SLEEP_MODE, 0);
+
+ dev_dbg(&icd->dev,
+ "format %s, win %s\n", priv->fmt->name, priv->win->name);
+
+-start_end:
+- priv->fmt = NULL;
+- priv->win = NULL;
+-
+- return ret;
++ return 0;
+ }
+
+ static int ov772x_stop_capture(struct soc_camera_device *icd)
+ {
+ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+- ov772x_reset(priv->client);
++ ov772x_mask_set(priv->client, COM2, SOFT_SLEEP_MODE, SOFT_SLEEP_MODE);
+ return 0;
+ }
+
+@@ -718,11 +673,54 @@ static unsigned long ov772x_query_bus_param(struct soc_camera_device *icd)
+ struct soc_camera_link *icl = priv->client->dev.platform_data;
+ unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER |
+ SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH |
+- priv->info->buswidth;
++ SOCAM_DATA_ACTIVE_HIGH | priv->info->buswidth;
+
+ return soc_camera_apply_sensor_flags(icl, flags);
+ }
+
++static int ov772x_get_control(struct soc_camera_device *icd,
++ struct v4l2_control *ctrl)
++{
++ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
++
++ switch (ctrl->id) {
++ case V4L2_CID_VFLIP:
++ ctrl->value = priv->flag_vflip;
++ break;
++ case V4L2_CID_HFLIP:
++ ctrl->value = priv->flag_hflip;
++ break;
++ }
++ return 0;
++}
++
++static int ov772x_set_control(struct soc_camera_device *icd,
++ struct v4l2_control *ctrl)
++{
++ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
++ int ret = 0;
++ u8 val;
++
++ switch (ctrl->id) {
++ case V4L2_CID_VFLIP:
++ val = ctrl->value ? VFLIP_IMG : 0x00;
++ priv->flag_vflip = ctrl->value;
++ if (priv->info->flags & OV772X_FLAG_VFLIP)
++ val ^= VFLIP_IMG;
++ ret = ov772x_mask_set(priv->client, COM3, VFLIP_IMG, val);
++ break;
++ case V4L2_CID_HFLIP:
++ val = ctrl->value ? HFLIP_IMG : 0x00;
++ priv->flag_hflip = ctrl->value;
++ if (priv->info->flags & OV772X_FLAG_HFLIP)
++ val ^= HFLIP_IMG;
++ ret = ov772x_mask_set(priv->client, COM3, HFLIP_IMG, val);
++ break;
++ }
++
++ return ret;
++}
++
+ static int ov772x_get_chip_id(struct soc_camera_device *icd,
+ struct v4l2_dbg_chip_ident *id)
+ {
+@@ -787,13 +785,11 @@ ov772x_select_win(u32 width, u32 height)
+ return win;
+ }
+
+-
+-static int ov772x_set_fmt(struct soc_camera_device *icd,
+- __u32 pixfmt,
+- struct v4l2_rect *rect)
++static int ov772x_set_params(struct ov772x_priv *priv, u32 width, u32 height,
++ u32 pixfmt)
+ {
+- struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
+ int ret = -EINVAL;
++ u8 val;
+ int i;
+
+ /*
+@@ -803,19 +799,101 @@ static int ov772x_set_fmt(struct soc_camera_device *icd,
+ for (i = 0; i < ARRAY_SIZE(ov772x_cfmts); i++) {
+ if (pixfmt == ov772x_cfmts[i].fourcc) {
+ priv->fmt = ov772x_cfmts + i;
+- ret = 0;
+ break;
+ }
+ }
++ if (!priv->fmt)
++ goto ov772x_set_fmt_error;
+
+ /*
+ * select win
+ */
+- priv->win = ov772x_select_win(rect->width, rect->height);
++ priv->win = ov772x_select_win(width, height);
++
++ /*
++ * reset hardware
++ */
++ ov772x_reset(priv->client);
++
++ /*
++ * set size format
++ */
++ ret = ov772x_write_array(priv->client, priv->win->regs);
++ if (ret < 0)
++ goto ov772x_set_fmt_error;
++
++ /*
++ * set DSP_CTRL3
++ */
++ val = priv->fmt->dsp3;
++ if (val) {
++ ret = ov772x_mask_set(priv->client,
++ DSP_CTRL3, UV_MASK, val);
++ if (ret < 0)
++ goto ov772x_set_fmt_error;
++ }
++
++ /*
++ * set COM3
++ */
++ val = priv->fmt->com3;
++ if (priv->info->flags & OV772X_FLAG_VFLIP)
++ val |= VFLIP_IMG;
++ if (priv->info->flags & OV772X_FLAG_HFLIP)
++ val |= HFLIP_IMG;
++ if (priv->flag_vflip)
++ val ^= VFLIP_IMG;
++ if (priv->flag_hflip)
++ val ^= HFLIP_IMG;
++
++ ret = ov772x_mask_set(priv->client,
++ COM3, SWAP_MASK | IMG_MASK, val);
++ if (ret < 0)
++ goto ov772x_set_fmt_error;
++
++ /*
++ * set COM7
++ */
++ val = priv->win->com7_bit | priv->fmt->com7;
++ ret = ov772x_mask_set(priv->client,
++ COM7, (SLCT_MASK | FMT_MASK | OFMT_MASK),
++ val);
++ if (ret < 0)
++ goto ov772x_set_fmt_error;
++
++ return ret;
++
++ov772x_set_fmt_error:
++
++ ov772x_reset(priv->client);
++ priv->win = NULL;
++ priv->fmt = NULL;
+
+ return ret;
+ }
+
++static int ov772x_set_crop(struct soc_camera_device *icd,
++ struct v4l2_rect *rect)
++{
++ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
++
++ if (!priv->fmt)
++ return -EINVAL;
++
++ return ov772x_set_params(priv, rect->width, rect->height,
++ priv->fmt->fourcc);
++}
++
++static int ov772x_set_fmt(struct soc_camera_device *icd,
++ struct v4l2_format *f)
++{
++ struct ov772x_priv *priv = container_of(icd, struct ov772x_priv, icd);
++ struct v4l2_pix_format *pix = &f->fmt.pix;
++
++ return ov772x_set_params(priv, pix->width, pix->height,
++ pix->pixelformat);
++}
++
+ static int ov772x_try_fmt(struct soc_camera_device *icd,
+ struct v4l2_format *f)
+ {
+@@ -889,7 +967,6 @@ static int ov772x_video_probe(struct soc_camera_device *icd)
+ i2c_smbus_read_byte_data(priv->client, MIDH),
+ i2c_smbus_read_byte_data(priv->client, MIDL));
+
+-
+ return soc_camera_video_start(icd);
+ }
+
+@@ -906,10 +983,15 @@ static struct soc_camera_ops ov772x_ops = {
+ .release = ov772x_release,
+ .start_capture = ov772x_start_capture,
+ .stop_capture = ov772x_stop_capture,
++ .set_crop = ov772x_set_crop,
+ .set_fmt = ov772x_set_fmt,
+ .try_fmt = ov772x_try_fmt,
+ .set_bus_param = ov772x_set_bus_param,
+ .query_bus_param = ov772x_query_bus_param,
++ .controls = ov772x_controls,
++ .num_controls = ARRAY_SIZE(ov772x_controls),
++ .get_control = ov772x_get_control,
++ .set_control = ov772x_set_control,
+ .get_chip_id = ov772x_get_chip_id,
+ #ifdef CONFIG_VIDEO_ADV_DEBUG
+ .get_register = ov772x_get_register,
+diff --git a/drivers/media/video/ovcamchip/ovcamchip_core.c b/drivers/media/video/ovcamchip/ovcamchip_core.c
+index c841f4e..d573d84 100644
+--- a/drivers/media/video/ovcamchip/ovcamchip_core.c
++++ b/drivers/media/video/ovcamchip/ovcamchip_core.c
+@@ -15,6 +15,9 @@
+ #include <linux/module.h>
+ #include <linux/slab.h>
+ #include <linux/delay.h>
++#include <linux/i2c.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-i2c-drv.h>
+ #include "ovcamchip_priv.h"
+
+ #define DRIVER_VERSION "v2.27 for Linux 2.6"
+@@ -44,6 +47,7 @@ MODULE_AUTHOR(DRIVER_AUTHOR);
+ MODULE_DESCRIPTION(DRIVER_DESC);
+ MODULE_LICENSE("GPL");
+
++
+ /* Registers common to all chips, that are needed for detection */
+ #define GENERIC_REG_ID_HIGH 0x1C /* manufacturer ID MSB */
+ #define GENERIC_REG_ID_LOW 0x1D /* manufacturer ID LSB */
+@@ -61,10 +65,6 @@ static char *chip_names[NUM_CC_TYPES] = {
+ [CC_OV6630AF] = "OV6630AF",
+ };
+
+-/* Forward declarations */
+-static struct i2c_driver driver;
+-static struct i2c_client client_template;
+-
+ /* ----------------------------------------------------------------------- */
+
+ int ov_write_regvals(struct i2c_client *c, struct ovcamchip_regvals *rvals)
+@@ -253,112 +253,36 @@ static int ovcamchip_detect(struct i2c_client *c)
+
+ /* Test for 7xx0 */
+ PDEBUG(3, "Testing for 0V7xx0");
+- c->addr = OV7xx0_SID;
+- if (init_camchip(c) < 0) {
+- /* Test for 6xx0 */
+- PDEBUG(3, "Testing for 0V6xx0");
+- c->addr = OV6xx0_SID;
+- if (init_camchip(c) < 0) {
+- return -ENODEV;
+- } else {
+- if (ov6xx0_detect(c) < 0) {
+- PERROR("Failed to init OV6xx0");
+- return -EIO;
+- }
+- }
+- } else {
++ if (init_camchip(c) < 0)
++ return -ENODEV;
++ /* 7-bit addresses with bit 0 set are for the OV7xx0 */
++ if (c->addr & 1) {
+ if (ov7xx0_detect(c) < 0) {
+ PERROR("Failed to init OV7xx0");
+ return -EIO;
+ }
++ return 0;
++ }
++ /* Test for 6xx0 */
++ PDEBUG(3, "Testing for 0V6xx0");
++ if (ov6xx0_detect(c) < 0) {
++ PERROR("Failed to init OV6xx0");
++ return -EIO;
+ }
+-
+ return 0;
+ }
+
+ /* ----------------------------------------------------------------------- */
+
+-static int ovcamchip_attach(struct i2c_adapter *adap)
++static long ovcamchip_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+ {
+- int rc = 0;
+- struct ovcamchip *ov;
+- struct i2c_client *c;
+-
+- /* I2C is not a PnP bus, so we can never be certain that we're talking
+- * to the right chip. To prevent damage to EEPROMS and such, only
+- * attach to adapters that are known to contain OV camera chips. */
+-
+- switch (adap->id) {
+- case I2C_HW_SMBUS_OV511:
+- case I2C_HW_SMBUS_OV518:
+- case I2C_HW_SMBUS_W9968CF:
+- PDEBUG(1, "Adapter ID 0x%06x accepted", adap->id);
+- break;
+- default:
+- PDEBUG(1, "Adapter ID 0x%06x rejected", adap->id);
+- return -ENODEV;
+- }
+-
+- c = kmalloc(sizeof *c, GFP_KERNEL);
+- if (!c) {
+- rc = -ENOMEM;
+- goto no_client;
+- }
+- memcpy(c, &client_template, sizeof *c);
+- c->adapter = adap;
+- strcpy(c->name, "OV????");
+-
+- ov = kzalloc(sizeof *ov, GFP_KERNEL);
+- if (!ov) {
+- rc = -ENOMEM;
+- goto no_ov;
+- }
+- i2c_set_clientdata(c, ov);
+-
+- rc = ovcamchip_detect(c);
+- if (rc < 0)
+- goto error;
+-
+- strcpy(c->name, chip_names[ov->subtype]);
+-
+- PDEBUG(1, "Camera chip detection complete");
+-
+- i2c_attach_client(c);
+-
+- return rc;
+-error:
+- kfree(ov);
+-no_ov:
+- kfree(c);
+-no_client:
+- PDEBUG(1, "returning %d", rc);
+- return rc;
+-}
+-
+-static int ovcamchip_detach(struct i2c_client *c)
+-{
+- struct ovcamchip *ov = i2c_get_clientdata(c);
+- int rc;
+-
+- rc = ov->sops->free(c);
+- if (rc < 0)
+- return rc;
+-
+- i2c_detach_client(c);
+-
+- kfree(ov);
+- kfree(c);
+- return 0;
+-}
+-
+-static int ovcamchip_command(struct i2c_client *c, unsigned int cmd, void *arg)
+-{
+- struct ovcamchip *ov = i2c_get_clientdata(c);
++ struct ovcamchip *ov = to_ovcamchip(sd);
++ struct i2c_client *c = v4l2_get_subdevdata(sd);
+
+ if (!ov->initialized &&
+ cmd != OVCAMCHIP_CMD_Q_SUBTYPE &&
+ cmd != OVCAMCHIP_CMD_INITIALIZE) {
+- dev_err(&c->dev, "ERROR: Camera chip not initialized yet!\n");
++ v4l2_err(sd, "Camera chip not initialized yet!\n");
+ return -EPERM;
+ }
+
+@@ -379,10 +303,10 @@ static int ovcamchip_command(struct i2c_client *c, unsigned int cmd, void *arg)
+
+ if (ov->mono) {
+ if (ov->subtype != CC_OV7620)
+- dev_warn(&c->dev, "Warning: Monochrome not "
++ v4l2_warn(sd, "Monochrome not "
+ "implemented for this chip\n");
+ else
+- dev_info(&c->dev, "Initializing chip as "
++ v4l2_info(sd, "Initializing chip as "
+ "monochrome\n");
+ }
+
+@@ -400,35 +324,72 @@ static int ovcamchip_command(struct i2c_client *c, unsigned int cmd, void *arg)
+
+ /* ----------------------------------------------------------------------- */
+
+-static struct i2c_driver driver = {
+- .driver = {
+- .name = "ovcamchip",
+- },
+- .id = I2C_DRIVERID_OVCAMCHIP,
+- .attach_adapter = ovcamchip_attach,
+- .detach_client = ovcamchip_detach,
+- .command = ovcamchip_command,
++static const struct v4l2_subdev_core_ops ovcamchip_core_ops = {
++ .ioctl = ovcamchip_ioctl,
+ };
+
+-static struct i2c_client client_template = {
+- .name = "(unset)",
+- .driver = &driver,
++static const struct v4l2_subdev_ops ovcamchip_ops = {
++ .core = &ovcamchip_core_ops,
+ };
+
+-static int __init ovcamchip_init(void)
++static int ovcamchip_probe(struct i2c_client *client,
++ const struct i2c_device_id *id)
+ {
+-#ifdef DEBUG
+- ovcamchip_debug = debug;
+-#endif
++ struct ovcamchip *ov;
++ struct v4l2_subdev *sd;
++ int rc = 0;
++
++ ov = kzalloc(sizeof *ov, GFP_KERNEL);
++ if (!ov) {
++ rc = -ENOMEM;
++ goto no_ov;
++ }
++ sd = &ov->sd;
++ v4l2_i2c_subdev_init(sd, client, &ovcamchip_ops);
++
++ rc = ovcamchip_detect(client);
++ if (rc < 0)
++ goto error;
++
++ v4l_info(client, "%s found @ 0x%02x (%s)\n",
++ chip_names[ov->subtype], client->addr << 1, client->adapter->name);
++
++ PDEBUG(1, "Camera chip detection complete");
+
+- PINFO(DRIVER_VERSION " : " DRIVER_DESC);
+- return i2c_add_driver(&driver);
++ return rc;
++error:
++ kfree(ov);
++no_ov:
++ PDEBUG(1, "returning %d", rc);
++ return rc;
+ }
+
+-static void __exit ovcamchip_exit(void)
++static int ovcamchip_remove(struct i2c_client *client)
+ {
+- i2c_del_driver(&driver);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct ovcamchip *ov = to_ovcamchip(sd);
++ int rc;
++
++ v4l2_device_unregister_subdev(sd);
++ rc = ov->sops->free(client);
++ if (rc < 0)
++ return rc;
++
++ kfree(ov);
++ return 0;
+ }
+
+-module_init(ovcamchip_init);
+-module_exit(ovcamchip_exit);
++/* ----------------------------------------------------------------------- */
++
++static const struct i2c_device_id ovcamchip_id[] = {
++ { "ovcamchip", 0 },
++ { }
++};
++MODULE_DEVICE_TABLE(i2c, ovcamchip_id);
++
++static struct v4l2_i2c_driver_data v4l2_i2c_data = {
++ .name = "ovcamchip",
++ .probe = ovcamchip_probe,
++ .remove = ovcamchip_remove,
++ .id_table = ovcamchip_id,
++};
+diff --git a/drivers/media/video/ovcamchip/ovcamchip_priv.h b/drivers/media/video/ovcamchip/ovcamchip_priv.h
+index a05650f..4f07b78 100644
+--- a/drivers/media/video/ovcamchip/ovcamchip_priv.h
++++ b/drivers/media/video/ovcamchip/ovcamchip_priv.h
+@@ -16,6 +16,7 @@
+ #define __LINUX_OVCAMCHIP_PRIV_H
+
+ #include <linux/i2c.h>
++#include <media/v4l2-subdev.h>
+ #include <media/ovcamchip.h>
+
+ #ifdef DEBUG
+@@ -46,6 +47,7 @@ struct ovcamchip_ops {
+ };
+
+ struct ovcamchip {
++ struct v4l2_subdev sd;
+ struct ovcamchip_ops *sops;
+ void *spriv; /* Private data for OV7x10.c etc... */
+ int subtype; /* = SEN_OV7610 etc... */
+@@ -53,6 +55,11 @@ struct ovcamchip {
+ int initialized; /* OVCAMCHIP_CMD_INITIALIZE was successful */
+ };
+
++static inline struct ovcamchip *to_ovcamchip(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct ovcamchip, sd);
++}
++
+ extern struct ovcamchip_ops ov6x20_ops;
+ extern struct ovcamchip_ops ov6x30_ops;
+ extern struct ovcamchip_ops ov7x10_ops;
+diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig
+index 854c2a8..f9b6001 100644
+--- a/drivers/media/video/pvrusb2/Kconfig
++++ b/drivers/media/video/pvrusb2/Kconfig
+@@ -40,10 +40,10 @@ config VIDEO_PVRUSB2_DVB
+ select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+ select DVB_S5H1409 if !DVB_FE_CUSTOMISE
+ select DVB_S5H1411 if !DVB_FE_CUSTOMISE
+- select DVB_TDA10048 if !DVB_FE_CUSTOMIZE
+- select MEDIA_TUNER_TDA18271 if !DVB_FE_CUSTOMIZE
+- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_TDA8290 if !DVB_FE_CUSTOMIZE
++ select DVB_TDA10048 if !DVB_FE_CUSTOMISE
++ select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
+ ---help---
+
+ This option enables a DVB interface for the pvrusb2 driver.
+diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile
+index 4fda2de..de2fc14 100644
+--- a/drivers/media/video/pvrusb2/Makefile
++++ b/drivers/media/video/pvrusb2/Makefile
+@@ -2,14 +2,15 @@ obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o
+ obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o
+ obj-pvrusb2-dvb-$(CONFIG_VIDEO_PVRUSB2_DVB) := pvrusb2-dvb.o
+
+-pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \
+- pvrusb2-audio.o pvrusb2-i2c-chips-v4l2.o \
++pvrusb2-objs := pvrusb2-i2c-core.o \
++ pvrusb2-audio.o \
+ pvrusb2-encoder.o pvrusb2-video-v4l.o \
+- pvrusb2-eeprom.o pvrusb2-tuner.o \
++ pvrusb2-eeprom.o \
+ pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \
+ pvrusb2-ctrl.o pvrusb2-std.o pvrusb2-devattr.o \
+ pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \
+ pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \
++ pvrusb2-cs53l32a.o \
+ $(obj-pvrusb2-dvb-y) \
+ $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y)
+
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.c b/drivers/media/video/pvrusb2/pvrusb2-audio.c
+index cdedaa5..ccf2a3c 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-audio.c
++++ b/drivers/media/video/pvrusb2/pvrusb2-audio.c
+@@ -26,14 +26,6 @@
+ #include <media/msp3400.h>
+ #include <media/v4l2-common.h>
+
+-struct pvr2_msp3400_handler {
+- struct pvr2_hdw *hdw;
+- struct pvr2_i2c_client *client;
+- struct pvr2_i2c_handler i2c_handler;
+- unsigned long stale_mask;
+-};
+-
+-
+
+ struct routing_scheme {
+ const int *def;
+@@ -63,123 +55,33 @@ static const struct routing_scheme routing_schemes[] = {
+ },
+ };
+
+-/* This function selects the correct audio input source */
+-static void set_stereo(struct pvr2_msp3400_handler *ctxt)
+-{
+- struct pvr2_hdw *hdw = ctxt->hdw;
+- struct v4l2_routing route;
+- const struct routing_scheme *sp;
+- unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
+-
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c msp3400 v4l2 set_stereo");
+-
+- if ((sid < ARRAY_SIZE(routing_schemes)) &&
+- ((sp = routing_schemes + sid) != NULL) &&
+- (hdw->input_val >= 0) &&
+- (hdw->input_val < sp->cnt)) {
+- route.input = sp->def[hdw->input_val];
+- } else {
+- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+- "*** WARNING *** i2c msp3400 v4l2 set_stereo:"
+- " Invalid routing scheme (%u) and/or input (%d)",
+- sid,hdw->input_val);
+- return;
+- }
+- route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
+- pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route);
+-}
+-
+-
+-static int check_stereo(struct pvr2_msp3400_handler *ctxt)
++void pvr2_msp3400_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd)
+ {
+- struct pvr2_hdw *hdw = ctxt->hdw;
+- return hdw->input_dirty;
+-}
+-
+-
+-struct pvr2_msp3400_ops {
+- void (*update)(struct pvr2_msp3400_handler *);
+- int (*check)(struct pvr2_msp3400_handler *);
+-};
+-
+-
+-static const struct pvr2_msp3400_ops msp3400_ops[] = {
+- { .update = set_stereo, .check = check_stereo},
+-};
+-
+-
+-static int msp3400_check(struct pvr2_msp3400_handler *ctxt)
+-{
+- unsigned long msk;
+- unsigned int idx;
+-
+- for (idx = 0; idx < ARRAY_SIZE(msp3400_ops); idx++) {
+- msk = 1 << idx;
+- if (ctxt->stale_mask & msk) continue;
+- if (msp3400_ops[idx].check(ctxt)) {
+- ctxt->stale_mask |= msk;
++ if (hdw->input_dirty || hdw->force_dirty) {
++ struct v4l2_routing route;
++ const struct routing_scheme *sp;
++ unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
++
++ pvr2_trace(PVR2_TRACE_CHIPS, "subdev msp3400 v4l2 set_stereo");
++
++ if ((sid < ARRAY_SIZE(routing_schemes)) &&
++ ((sp = routing_schemes + sid) != NULL) &&
++ (hdw->input_val >= 0) &&
++ (hdw->input_val < sp->cnt)) {
++ route.input = sp->def[hdw->input_val];
++ } else {
++ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
++ "*** WARNING *** subdev msp3400 set_input:"
++ " Invalid routing scheme (%u)"
++ " and/or input (%d)",
++ sid, hdw->input_val);
++ return;
+ }
++ route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
++ sd->ops->audio->s_routing(sd, &route);
+ }
+- return ctxt->stale_mask != 0;
+ }
+
+-
+-static void msp3400_update(struct pvr2_msp3400_handler *ctxt)
+-{
+- unsigned long msk;
+- unsigned int idx;
+-
+- for (idx = 0; idx < ARRAY_SIZE(msp3400_ops); idx++) {
+- msk = 1 << idx;
+- if (!(ctxt->stale_mask & msk)) continue;
+- ctxt->stale_mask &= ~msk;
+- msp3400_ops[idx].update(ctxt);
+- }
+-}
+-
+-
+-static void pvr2_msp3400_detach(struct pvr2_msp3400_handler *ctxt)
+-{
+- ctxt->client->handler = NULL;
+- kfree(ctxt);
+-}
+-
+-
+-static unsigned int pvr2_msp3400_describe(struct pvr2_msp3400_handler *ctxt,
+- char *buf,unsigned int cnt)
+-{
+- return scnprintf(buf,cnt,"handler: pvrusb2-audio v4l2");
+-}
+-
+-
+-static const struct pvr2_i2c_handler_functions msp3400_funcs = {
+- .detach = (void (*)(void *))pvr2_msp3400_detach,
+- .check = (int (*)(void *))msp3400_check,
+- .update = (void (*)(void *))msp3400_update,
+- .describe = (unsigned int (*)(void *,char *,unsigned int))pvr2_msp3400_describe,
+-};
+-
+-
+-int pvr2_i2c_msp3400_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp)
+-{
+- struct pvr2_msp3400_handler *ctxt;
+- if (cp->handler) return 0;
+-
+- ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL);
+- if (!ctxt) return 0;
+-
+- ctxt->i2c_handler.func_data = ctxt;
+- ctxt->i2c_handler.func_table = &msp3400_funcs;
+- ctxt->client = cp;
+- ctxt->hdw = hdw;
+- ctxt->stale_mask = (1 << ARRAY_SIZE(msp3400_ops)) - 1;
+- cp->handler = &ctxt->i2c_handler;
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x msp3400 V4L2 handler set up",
+- cp->client->addr);
+- return !0;
+-}
+-
+-
+ /*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.h b/drivers/media/video/pvrusb2/pvrusb2-audio.h
+index ac54eed..e3e63d7 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-audio.h
++++ b/drivers/media/video/pvrusb2/pvrusb2-audio.h
+@@ -22,10 +22,8 @@
+ #ifndef __PVRUSB2_AUDIO_H
+ #define __PVRUSB2_AUDIO_H
+
+-#include "pvrusb2-i2c-core.h"
+-
+-int pvr2_i2c_msp3400_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
+-
++#include "pvrusb2-hdw-internal.h"
++void pvr2_msp3400_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *);
+ #endif /* __PVRUSB2_AUDIO_H */
+
+ /*
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c b/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c
+new file mode 100644
+index 0000000..b5c3428
+--- /dev/null
++++ b/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.c
+@@ -0,0 +1,95 @@
++/*
++ *
++ *
++ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
++ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
++ *
++ * 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
++ *
++ * 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
++ *
++ */
++
++/*
++
++ This source file is specifically designed to interface with the
++ v4l-dvb cs53l32a module.
++
++*/
++
++#include "pvrusb2-cs53l32a.h"
++
++
++#include "pvrusb2-hdw-internal.h"
++#include "pvrusb2-debug.h"
++#include <linux/videodev2.h>
++#include <media/v4l2-common.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++
++struct routing_scheme {
++ const int *def;
++ unsigned int cnt;
++};
++
++
++static const int routing_scheme1[] = {
++ [PVR2_CVAL_INPUT_TV] = 2, /* 1 or 2 seems to work here */
++ [PVR2_CVAL_INPUT_RADIO] = 2,
++ [PVR2_CVAL_INPUT_COMPOSITE] = 0,
++ [PVR2_CVAL_INPUT_SVIDEO] = 0,
++};
++
++static const struct routing_scheme routing_schemes[] = {
++ [PVR2_ROUTING_SCHEME_ONAIR] = {
++ .def = routing_scheme1,
++ .cnt = ARRAY_SIZE(routing_scheme1),
++ },
++};
++
++
++void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd)
++{
++ if (hdw->input_dirty || hdw->force_dirty) {
++ struct v4l2_routing route;
++ const struct routing_scheme *sp;
++ unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
++ pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)",
++ hdw->input_val);
++ if ((sid < ARRAY_SIZE(routing_schemes)) &&
++ ((sp = routing_schemes + sid) != NULL) &&
++ (hdw->input_val >= 0) &&
++ (hdw->input_val < sp->cnt)) {
++ route.input = sp->def[hdw->input_val];
++ } else {
++ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
++ "*** WARNING *** subdev v4l2 set_input:"
++ " Invalid routing scheme (%u)"
++ " and/or input (%d)",
++ sid, hdw->input_val);
++ return;
++ }
++ route.output = 0;
++ sd->ops->audio->s_routing(sd, &route);
++ }
++}
++
++
++/*
++ Stuff for Emacs to see, in order to encourage consistent editing style:
++ *** Local Variables: ***
++ *** mode: c ***
++ *** fill-column: 70 ***
++ *** tab-width: 8 ***
++ *** c-basic-offset: 8 ***
++ *** End: ***
++ */
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h b/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h
+new file mode 100644
+index 0000000..53ba548
+--- /dev/null
++++ b/drivers/media/video/pvrusb2/pvrusb2-cs53l32a.h
+@@ -0,0 +1,48 @@
++/*
++ *
++ *
++ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
++ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
++ *
++ * 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
++ *
++ * 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
++ *
++ */
++
++#ifndef __PVRUSB2_CS53L32A_H
++#define __PVRUSB2_CS53L32A_H
++
++/*
++
++ This module connects the pvrusb2 driver to the I2C chip level
++ driver which handles device video processing. This interface is
++ used internally by the driver; higher level code should only
++ interact through the interface provided by pvrusb2-hdw.h.
++
++*/
++
++
++#include "pvrusb2-hdw-internal.h"
++void pvr2_cs53l32a_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *);
++
++#endif /* __PVRUSB2_AUDIO_CS53L32A_H */
++
++/*
++ Stuff for Emacs to see, in order to encourage consistent editing style:
++ *** Local Variables: ***
++ *** mode: c ***
++ *** fill-column: 70 ***
++ *** tab-width: 8 ***
++ *** c-basic-offset: 8 ***
++ *** End: ***
++ */
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
+index 895859e..4e017ff 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
++++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
+@@ -28,7 +28,6 @@
+
+ #include "pvrusb2-cx2584x-v4l.h"
+ #include "pvrusb2-video-v4l.h"
+-#include "pvrusb2-i2c-cmd-v4l2.h"
+
+
+ #include "pvrusb2-hdw-internal.h"
+@@ -39,14 +38,6 @@
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+
+-struct pvr2_v4l_cx2584x {
+- struct pvr2_i2c_handler handler;
+- struct pvr2_decoder_ctrl ctrl;
+- struct pvr2_i2c_client *client;
+- struct pvr2_hdw *hdw;
+- unsigned long stale_mask;
+-};
+-
+
+ struct routing_scheme_item {
+ int vid;
+@@ -110,218 +101,44 @@ static const struct routing_scheme routing_schemes[] = {
+ },
+ };
+
+-static void set_input(struct pvr2_v4l_cx2584x *ctxt)
+-{
+- struct pvr2_hdw *hdw = ctxt->hdw;
+- struct v4l2_routing route;
+- enum cx25840_video_input vid_input;
+- enum cx25840_audio_input aud_input;
+- const struct routing_scheme *sp;
+- unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
+-
+- memset(&route,0,sizeof(route));
+-
+- if ((sid < ARRAY_SIZE(routing_schemes)) &&
+- ((sp = routing_schemes + sid) != NULL) &&
+- (hdw->input_val >= 0) &&
+- (hdw->input_val < sp->cnt)) {
+- vid_input = sp->def[hdw->input_val].vid;
+- aud_input = sp->def[hdw->input_val].aud;
+- } else {
+- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+- "*** WARNING *** i2c cx2584x set_input:"
+- " Invalid routing scheme (%u) and/or input (%d)",
+- sid,hdw->input_val);
+- return;
+- }
+-
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_input vid=0x%x aud=0x%x",
+- vid_input,aud_input);
+- route.input = (u32)vid_input;
+- pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route);
+- route.input = (u32)aud_input;
+- pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route);
+-}
+-
+-
+-static int check_input(struct pvr2_v4l_cx2584x *ctxt)
+-{
+- struct pvr2_hdw *hdw = ctxt->hdw;
+- return hdw->input_dirty != 0;
+-}
+-
+-
+-static void set_audio(struct pvr2_v4l_cx2584x *ctxt)
+-{
+- u32 val;
+- struct pvr2_hdw *hdw = ctxt->hdw;
+-
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_audio %d",
+- hdw->srate_val);
+- switch (hdw->srate_val) {
+- default:
+- case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000:
+- val = 48000;
+- break;
+- case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100:
+- val = 44100;
+- break;
+- case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000:
+- val = 32000;
+- break;
+- }
+- pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val);
+-}
+-
+-
+-static int check_audio(struct pvr2_v4l_cx2584x *ctxt)
+-{
+- struct pvr2_hdw *hdw = ctxt->hdw;
+- return hdw->srate_dirty != 0;
+-}
+-
+-
+-struct pvr2_v4l_cx2584x_ops {
+- void (*update)(struct pvr2_v4l_cx2584x *);
+- int (*check)(struct pvr2_v4l_cx2584x *);
+-};
+-
+-
+-static const struct pvr2_v4l_cx2584x_ops decoder_ops[] = {
+- { .update = set_input, .check = check_input},
+- { .update = set_audio, .check = check_audio},
+-};
+-
+-
+-static void decoder_detach(struct pvr2_v4l_cx2584x *ctxt)
++void pvr2_cx25840_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd)
+ {
+- ctxt->client->handler = NULL;
+- pvr2_hdw_set_decoder(ctxt->hdw,NULL);
+- kfree(ctxt);
+-}
+-
+-
+-static int decoder_check(struct pvr2_v4l_cx2584x *ctxt)
+-{
+- unsigned long msk;
+- unsigned int idx;
+-
+- for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) {
+- msk = 1 << idx;
+- if (ctxt->stale_mask & msk) continue;
+- if (decoder_ops[idx].check(ctxt)) {
+- ctxt->stale_mask |= msk;
++ pvr2_trace(PVR2_TRACE_CHIPS, "subdev cx2584x update...");
++ if (hdw->input_dirty || hdw->force_dirty) {
++ struct v4l2_routing route;
++ enum cx25840_video_input vid_input;
++ enum cx25840_audio_input aud_input;
++ const struct routing_scheme *sp;
++ unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
++
++ memset(&route, 0, sizeof(route));
++
++ if ((sid < ARRAY_SIZE(routing_schemes)) &&
++ ((sp = routing_schemes + sid) != NULL) &&
++ (hdw->input_val >= 0) &&
++ (hdw->input_val < sp->cnt)) {
++ vid_input = sp->def[hdw->input_val].vid;
++ aud_input = sp->def[hdw->input_val].aud;
++ } else {
++ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
++ "*** WARNING *** subdev cx2584x set_input:"
++ " Invalid routing scheme (%u)"
++ " and/or input (%d)",
++ sid, hdw->input_val);
++ return;
+ }
+- }
+- return ctxt->stale_mask != 0;
+-}
+-
+
+-static void decoder_update(struct pvr2_v4l_cx2584x *ctxt)
+-{
+- unsigned long msk;
+- unsigned int idx;
+-
+- for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) {
+- msk = 1 << idx;
+- if (!(ctxt->stale_mask & msk)) continue;
+- ctxt->stale_mask &= ~msk;
+- decoder_ops[idx].update(ctxt);
++ pvr2_trace(PVR2_TRACE_CHIPS,
++ "subdev cx2584x set_input vid=0x%x aud=0x%x",
++ vid_input, aud_input);
++ route.input = (u32)vid_input;
++ sd->ops->video->s_routing(sd, &route);
++ route.input = (u32)aud_input;
++ sd->ops->audio->s_routing(sd, &route);
+ }
+ }
+
+
+-static void decoder_enable(struct pvr2_v4l_cx2584x *ctxt,int fl)
+-{
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_enable(%d)",fl);
+- pvr2_v4l2_cmd_stream(ctxt->client,fl);
+-}
+-
+-
+-static int decoder_detect(struct pvr2_i2c_client *cp)
+-{
+- int ret;
+- /* Attempt to query the decoder - let's see if it will answer */
+- struct v4l2_queryctrl qc;
+-
+- memset(&qc,0,sizeof(qc));
+-
+- qc.id = V4L2_CID_BRIGHTNESS;
+-
+- ret = pvr2_i2c_client_cmd(cp,VIDIOC_QUERYCTRL,&qc);
+- return ret == 0; /* Return true if it answered */
+-}
+-
+-
+-static unsigned int decoder_describe(struct pvr2_v4l_cx2584x *ctxt,
+- char *buf,unsigned int cnt)
+-{
+- return scnprintf(buf,cnt,"handler: pvrusb2-cx2584x-v4l");
+-}
+-
+-
+-static void decoder_reset(struct pvr2_v4l_cx2584x *ctxt)
+-{
+- int ret;
+- ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_RESET,NULL);
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_reset (ret=%d)",ret);
+-}
+-
+-
+-static const struct pvr2_i2c_handler_functions hfuncs = {
+- .detach = (void (*)(void *))decoder_detach,
+- .check = (int (*)(void *))decoder_check,
+- .update = (void (*)(void *))decoder_update,
+- .describe = (unsigned int (*)(void *,char *,unsigned int))decoder_describe,
+-};
+-
+-
+-int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw,
+- struct pvr2_i2c_client *cp)
+-{
+- struct pvr2_v4l_cx2584x *ctxt;
+-
+- if (hdw->decoder_ctrl) return 0;
+- if (cp->handler) return 0;
+- if (!decoder_detect(cp)) return 0;
+-
+- ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL);
+- if (!ctxt) return 0;
+-
+- ctxt->handler.func_data = ctxt;
+- ctxt->handler.func_table = &hfuncs;
+- ctxt->ctrl.ctxt = ctxt;
+- ctxt->ctrl.detach = (void (*)(void *))decoder_detach;
+- ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable;
+- ctxt->ctrl.force_reset = (void (*)(void*))decoder_reset;
+- ctxt->client = cp;
+- ctxt->hdw = hdw;
+- ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1;
+- pvr2_hdw_set_decoder(hdw,&ctxt->ctrl);
+- cp->handler = &ctxt->handler;
+- {
+- /*
+- Mike Isely <isely@pobox.com> 19-Nov-2006 - This bit
+- of nuttiness for cx25840 causes that module to
+- correctly set up its video scaling. This is really
+- a problem in the cx25840 module itself, but we work
+- around it here. The problem has not been seen in
+- ivtv because there VBI is supported and set up. We
+- don't do VBI here (at least not yet) and thus we
+- never attempted to even set it up.
+- */
+- struct v4l2_format fmt;
+- memset(&fmt,0,sizeof(fmt));
+- fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
+- pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_FMT,&fmt);
+- }
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x cx2584x V4L2 handler set up",
+- cp->client->addr);
+- return !0;
+-}
+-
+-
+-
+
+ /*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h
+index 66abf77..e35c232 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h
++++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h
+@@ -34,9 +34,9 @@
+
+
+
+-#include "pvrusb2-i2c-core.h"
++#include "pvrusb2-hdw-internal.h"
+
+-int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
++void pvr2_cx25840_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd);
+
+
+ #endif /* __PVRUSB2_CX2584X_V4L_H */
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
+index ca892fb..fbe3856 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
++++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
+@@ -23,7 +23,6 @@
+ #include "pvrusb2-debugifc.h"
+ #include "pvrusb2-hdw.h"
+ #include "pvrusb2-debug.h"
+-#include "pvrusb2-i2c-core.h"
+
+ struct debugifc_mask_item {
+ const char *name;
+@@ -147,10 +146,6 @@ int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt)
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = pvr2_hdw_state_report(hdw,buf,acnt);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+- ccnt = scnprintf(buf,acnt,"Attached I2C modules:\n");
+- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+- ccnt = pvr2_i2c_report(hdw,buf,acnt);
+- bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+ return bcnt;
+ }
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.h b/drivers/media/video/pvrusb2/pvrusb2-debugifc.h
+index e24ff59..2f8d467 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-debugifc.h
++++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.h
+@@ -22,16 +22,16 @@
+
+ struct pvr2_hdw;
+
+-/* Non-intrusively print some useful debugging info from inside the
+- driver. This should work even if the driver appears to be
+- wedged. */
+-int pvr2_debugifc_print_info(struct pvr2_hdw *,
+- char *buf_ptr,unsigned int buf_size);
+-
+ /* Print general status of driver. This will also trigger a probe of
+ the USB link. Unlike print_info(), this one synchronizes with the
+ driver so the information should be self-consistent (but it will
+ hang if the driver is wedged). */
++int pvr2_debugifc_print_info(struct pvr2_hdw *,
++ char *buf_ptr, unsigned int buf_size);
++
++/* Non-intrusively print some useful debugging info from inside the
++ driver. This should work even if the driver appears to be
++ wedged. */
+ int pvr2_debugifc_print_status(struct pvr2_hdw *,
+ char *buf_ptr,unsigned int buf_size);
+
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.c b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
+index cbe2a34..1cb6a26 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-devattr.c
++++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.c
+@@ -46,10 +46,11 @@ pvr2_device_desc structures.
+ /*------------------------------------------------------------------------*/
+ /* Hauppauge PVR-USB2 Model 29xxx */
+
+-static const char *pvr2_client_29xxx[] = {
+- "msp3400",
+- "saa7115",
+- "tuner",
++static const struct pvr2_device_client_desc pvr2_cli_29xxx[] = {
++ { .module_id = PVR2_CLIENT_ID_SAA7115 },
++ { .module_id = PVR2_CLIENT_ID_MSP3400 },
++ { .module_id = PVR2_CLIENT_ID_TUNER },
++ { .module_id = PVR2_CLIENT_ID_DEMOD },
+ };
+
+ static const char *pvr2_fw1_names_29xxx[] = {
+@@ -59,8 +60,8 @@ static const char *pvr2_fw1_names_29xxx[] = {
+ static const struct pvr2_device_desc pvr2_device_29xxx = {
+ .description = "WinTV PVR USB2 Model Category 29xxx",
+ .shortname = "29xxx",
+- .client_modules.lst = pvr2_client_29xxx,
+- .client_modules.cnt = ARRAY_SIZE(pvr2_client_29xxx),
++ .client_table.lst = pvr2_cli_29xxx,
++ .client_table.cnt = ARRAY_SIZE(pvr2_cli_29xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_29xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_29xxx),
+ .flag_has_hauppauge_rom = !0,
+@@ -77,10 +78,11 @@ static const struct pvr2_device_desc pvr2_device_29xxx = {
+ /*------------------------------------------------------------------------*/
+ /* Hauppauge PVR-USB2 Model 24xxx */
+
+-static const char *pvr2_client_24xxx[] = {
+- "cx25840",
+- "tuner",
+- "wm8775",
++static const struct pvr2_device_client_desc pvr2_cli_24xxx[] = {
++ { .module_id = PVR2_CLIENT_ID_CX25840 },
++ { .module_id = PVR2_CLIENT_ID_TUNER },
++ { .module_id = PVR2_CLIENT_ID_WM8775 },
++ { .module_id = PVR2_CLIENT_ID_DEMOD },
+ };
+
+ static const char *pvr2_fw1_names_24xxx[] = {
+@@ -90,8 +92,8 @@ static const char *pvr2_fw1_names_24xxx[] = {
+ static const struct pvr2_device_desc pvr2_device_24xxx = {
+ .description = "WinTV PVR USB2 Model Category 24xxx",
+ .shortname = "24xxx",
+- .client_modules.lst = pvr2_client_24xxx,
+- .client_modules.cnt = ARRAY_SIZE(pvr2_client_24xxx),
++ .client_table.lst = pvr2_cli_24xxx,
++ .client_table.cnt = ARRAY_SIZE(pvr2_cli_24xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_24xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_24xxx),
+ .flag_has_cx25840 = !0,
+@@ -111,16 +113,16 @@ static const struct pvr2_device_desc pvr2_device_24xxx = {
+ /*------------------------------------------------------------------------*/
+ /* GOTVIEW USB2.0 DVD2 */
+
+-static const char *pvr2_client_gotview_2[] = {
+- "cx25840",
+- "tuner",
++static const struct pvr2_device_client_desc pvr2_cli_gotview_2[] = {
++ { .module_id = PVR2_CLIENT_ID_CX25840 },
++ { .module_id = PVR2_CLIENT_ID_TUNER },
+ };
+
+ static const struct pvr2_device_desc pvr2_device_gotview_2 = {
+ .description = "Gotview USB 2.0 DVD 2",
+ .shortname = "gv2",
+- .client_modules.lst = pvr2_client_gotview_2,
+- .client_modules.cnt = ARRAY_SIZE(pvr2_client_gotview_2),
++ .client_table.lst = pvr2_cli_gotview_2,
++ .client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2),
+ .flag_has_cx25840 = !0,
+ .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .flag_has_analogtuner = !0,
+@@ -140,8 +142,8 @@ static const struct pvr2_device_desc pvr2_device_gotview_2 = {
+ static const struct pvr2_device_desc pvr2_device_gotview_2d = {
+ .description = "Gotview USB 2.0 DVD Deluxe",
+ .shortname = "gv2d",
+- .client_modules.lst = pvr2_client_gotview_2,
+- .client_modules.cnt = ARRAY_SIZE(pvr2_client_gotview_2),
++ .client_table.lst = pvr2_cli_gotview_2,
++ .client_table.cnt = ARRAY_SIZE(pvr2_cli_gotview_2),
+ .flag_has_cx25840 = !0,
+ .default_tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .flag_has_analogtuner = !0,
+@@ -181,29 +183,29 @@ static int pvr2_lgh06xf_attach(struct pvr2_dvb_adapter *adap)
+ return 0;
+ }
+
+-static struct pvr2_dvb_props pvr2_onair_creator_fe_props = {
++static const struct pvr2_dvb_props pvr2_onair_creator_fe_props = {
+ .frontend_attach = pvr2_lgdt3303_attach,
+ .tuner_attach = pvr2_lgh06xf_attach,
+ };
+ #endif
+
+-static const char *pvr2_client_onair_creator[] = {
+- "saa7115",
+- "tuner",
+- "cs53l32a",
++static const struct pvr2_device_client_desc pvr2_cli_onair_creator[] = {
++ { .module_id = PVR2_CLIENT_ID_SAA7115 },
++ { .module_id = PVR2_CLIENT_ID_CS53L32A },
++ { .module_id = PVR2_CLIENT_ID_TUNER },
+ };
+
+ static const struct pvr2_device_desc pvr2_device_onair_creator = {
+ .description = "OnAir Creator Hybrid USB tuner",
+ .shortname = "oac",
+- .client_modules.lst = pvr2_client_onair_creator,
+- .client_modules.cnt = ARRAY_SIZE(pvr2_client_onair_creator),
++ .client_table.lst = pvr2_cli_onair_creator,
++ .client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_creator),
+ .default_tuner_type = TUNER_LG_TDVS_H06XF,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .flag_digital_requires_cx23416 = !0,
+- .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
++ .signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR,
+ .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR,
+ .default_std_mask = V4L2_STD_NTSC_M,
+ #ifdef CONFIG_VIDEO_PVRUSB2_DVB
+@@ -241,29 +243,29 @@ static int pvr2_fcv1236d_attach(struct pvr2_dvb_adapter *adap)
+ return 0;
+ }
+
+-static struct pvr2_dvb_props pvr2_onair_usb2_fe_props = {
++static const struct pvr2_dvb_props pvr2_onair_usb2_fe_props = {
+ .frontend_attach = pvr2_lgdt3302_attach,
+ .tuner_attach = pvr2_fcv1236d_attach,
+ };
+ #endif
+
+-static const char *pvr2_client_onair_usb2[] = {
+- "saa7115",
+- "tuner",
+- "cs53l32a",
++static const struct pvr2_device_client_desc pvr2_cli_onair_usb2[] = {
++ { .module_id = PVR2_CLIENT_ID_SAA7115 },
++ { .module_id = PVR2_CLIENT_ID_CS53L32A },
++ { .module_id = PVR2_CLIENT_ID_TUNER },
+ };
+
+ static const struct pvr2_device_desc pvr2_device_onair_usb2 = {
+ .description = "OnAir USB2 Hybrid USB tuner",
+ .shortname = "oa2",
+- .client_modules.lst = pvr2_client_onair_usb2,
+- .client_modules.cnt = ARRAY_SIZE(pvr2_client_onair_usb2),
++ .client_table.lst = pvr2_cli_onair_usb2,
++ .client_table.cnt = ARRAY_SIZE(pvr2_cli_onair_usb2),
+ .default_tuner_type = TUNER_PHILIPS_FCV1236D,
+ .flag_has_analogtuner = !0,
+ .flag_has_composite = !0,
+ .flag_has_svideo = !0,
+ .flag_digital_requires_cx23416 = !0,
+- .signal_routing_scheme = PVR2_ROUTING_SCHEME_HAUPPAUGE,
++ .signal_routing_scheme = PVR2_ROUTING_SCHEME_ONAIR,
+ .digital_control_scheme = PVR2_DIGITAL_SCHEME_ONAIR,
+ .default_std_mask = V4L2_STD_NTSC_M,
+ #ifdef CONFIG_VIDEO_PVRUSB2_DVB
+@@ -314,15 +316,16 @@ static int pvr2_73xxx_tda18271_8295_attach(struct pvr2_dvb_adapter *adap)
+ return 0;
+ }
+
+-static struct pvr2_dvb_props pvr2_73xxx_dvb_props = {
++static const struct pvr2_dvb_props pvr2_73xxx_dvb_props = {
+ .frontend_attach = pvr2_tda10048_attach,
+ .tuner_attach = pvr2_73xxx_tda18271_8295_attach,
+ };
+ #endif
+
+-static const char *pvr2_client_73xxx[] = {
+- "cx25840",
+- "tuner",
++static const struct pvr2_device_client_desc pvr2_cli_73xxx[] = {
++ { .module_id = PVR2_CLIENT_ID_CX25840 },
++ { .module_id = PVR2_CLIENT_ID_TUNER,
++ .i2c_address_list = "\x42"},
+ };
+
+ static const char *pvr2_fw1_names_73xxx[] = {
+@@ -332,8 +335,8 @@ static const char *pvr2_fw1_names_73xxx[] = {
+ static const struct pvr2_device_desc pvr2_device_73xxx = {
+ .description = "WinTV HVR-1900 Model Category 73xxx",
+ .shortname = "73xxx",
+- .client_modules.lst = pvr2_client_73xxx,
+- .client_modules.cnt = ARRAY_SIZE(pvr2_client_73xxx),
++ .client_table.lst = pvr2_cli_73xxx,
++ .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_73xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_73xxx),
+ .flag_has_cx25840 = !0,
+@@ -418,22 +421,17 @@ static int pvr2_tda18271_8295_attach(struct pvr2_dvb_adapter *adap)
+ return 0;
+ }
+
+-static struct pvr2_dvb_props pvr2_750xx_dvb_props = {
++static const struct pvr2_dvb_props pvr2_750xx_dvb_props = {
+ .frontend_attach = pvr2_s5h1409_attach,
+ .tuner_attach = pvr2_tda18271_8295_attach,
+ };
+
+-static struct pvr2_dvb_props pvr2_751xx_dvb_props = {
++static const struct pvr2_dvb_props pvr2_751xx_dvb_props = {
+ .frontend_attach = pvr2_s5h1411_attach,
+ .tuner_attach = pvr2_tda18271_8295_attach,
+ };
+ #endif
+
+-static const char *pvr2_client_75xxx[] = {
+- "cx25840",
+- "tuner",
+-};
+-
+ static const char *pvr2_fw1_names_75xxx[] = {
+ "v4l-pvrusb2-73xxx-01.fw",
+ };
+@@ -441,8 +439,8 @@ static const char *pvr2_fw1_names_75xxx[] = {
+ static const struct pvr2_device_desc pvr2_device_750xx = {
+ .description = "WinTV HVR-1950 Model Category 750xx",
+ .shortname = "750xx",
+- .client_modules.lst = pvr2_client_75xxx,
+- .client_modules.cnt = ARRAY_SIZE(pvr2_client_75xxx),
++ .client_table.lst = pvr2_cli_73xxx,
++ .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_75xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx),
+ .flag_has_cx25840 = !0,
+@@ -463,8 +461,8 @@ static const struct pvr2_device_desc pvr2_device_750xx = {
+ static const struct pvr2_device_desc pvr2_device_751xx = {
+ .description = "WinTV HVR-1950 Model Category 751xx",
+ .shortname = "751xx",
+- .client_modules.lst = pvr2_client_75xxx,
+- .client_modules.cnt = ARRAY_SIZE(pvr2_client_75xxx),
++ .client_table.lst = pvr2_cli_73xxx,
++ .client_table.cnt = ARRAY_SIZE(pvr2_cli_73xxx),
+ .fx2_firmware.lst = pvr2_fw1_names_75xxx,
+ .fx2_firmware.cnt = ARRAY_SIZE(pvr2_fw1_names_75xxx),
+ .flag_has_cx25840 = !0,
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-devattr.h b/drivers/media/video/pvrusb2/pvrusb2-devattr.h
+index cb3a33e..3e55338 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-devattr.h
++++ b/drivers/media/video/pvrusb2/pvrusb2-devattr.h
+@@ -33,6 +33,34 @@
+ */
+
+
++#define PVR2_CLIENT_ID_NULL 0
++#define PVR2_CLIENT_ID_MSP3400 1
++#define PVR2_CLIENT_ID_CX25840 2
++#define PVR2_CLIENT_ID_SAA7115 3
++#define PVR2_CLIENT_ID_TUNER 4
++#define PVR2_CLIENT_ID_CS53L32A 5
++#define PVR2_CLIENT_ID_WM8775 6
++#define PVR2_CLIENT_ID_DEMOD 7
++
++struct pvr2_device_client_desc {
++ /* One ovr PVR2_CLIENT_ID_xxxx */
++ unsigned char module_id;
++
++ /* Null-terminated array of I2C addresses to try in order
++ initialize the module. It's safe to make this null terminated
++ since we're never going to encounter an i2c device with an
++ address of zero. If this is a null pointer or zero-length,
++ then no I2C addresses have been specified, in which case we'll
++ try some compiled in defaults for now. */
++ unsigned char *i2c_address_list;
++};
++
++struct pvr2_device_client_table {
++ const struct pvr2_device_client_desc *lst;
++ unsigned char cnt;
++};
++
++
+ struct pvr2_string_table {
+ const char **lst;
+ unsigned int cnt;
+@@ -40,6 +68,7 @@ struct pvr2_string_table {
+
+ #define PVR2_ROUTING_SCHEME_HAUPPAUGE 0
+ #define PVR2_ROUTING_SCHEME_GOTVIEW 1
++#define PVR2_ROUTING_SCHEME_ONAIR 2
+
+ #define PVR2_DIGITAL_SCHEME_NONE 0
+ #define PVR2_DIGITAL_SCHEME_HAUPPAUGE 1
+@@ -66,6 +95,9 @@ struct pvr2_device_desc {
+ /* List of additional client modules we need to load */
+ struct pvr2_string_table client_modules;
+
++ /* List of defined client modules we need to load */
++ struct pvr2_device_client_table client_table;
++
+ /* List of FX2 firmware file names we should search; if empty then
+ FX2 firmware check / load is skipped and we assume the device
+ was initialized from internal ROM. */
+@@ -73,7 +105,7 @@ struct pvr2_device_desc {
+
+ #ifdef CONFIG_VIDEO_PVRUSB2_DVB
+ /* callback functions to handle attachment of digital tuner & demod */
+- struct pvr2_dvb_props *dvb_props;
++ const struct pvr2_dvb_props *dvb_props;
+
+ #endif
+ /* Initial standard bits to use for this device, if not zero.
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-dvb.c b/drivers/media/video/pvrusb2/pvrusb2-dvb.c
+index 77b3c33..b7f5c49 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-dvb.c
++++ b/drivers/media/video/pvrusb2/pvrusb2-dvb.c
+@@ -321,7 +321,7 @@ static int pvr2_dvb_adapter_exit(struct pvr2_dvb_adapter *adap)
+ static int pvr2_dvb_frontend_init(struct pvr2_dvb_adapter *adap)
+ {
+ struct pvr2_hdw *hdw = adap->channel.hdw;
+- struct pvr2_dvb_props *dvb_props = hdw->hdw_desc->dvb_props;
++ const struct pvr2_dvb_props *dvb_props = hdw->hdw_desc->dvb_props;
+ int ret = 0;
+
+ if (dvb_props == NULL) {
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
+index 273d2a1..54ac534 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c
++++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
+@@ -347,7 +347,7 @@ static int pvr2_encoder_prep_config(struct pvr2_hdw *hdw)
+ int encMisc3Arg = 0;
+
+ #if 0
+- /* This inexplicable bit happens in the Hauppage windows
++ /* This inexplicable bit happens in the Hauppauge windows
+ driver (for both 24xxx and 29xxx devices). However I
+ currently see no difference in behavior with or without
+ this stuff. Leave this here as a note of its existence,
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
+index de7ee72..5d75eb5 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
++++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
+@@ -38,6 +38,7 @@
+ #include <linux/mutex.h>
+ #include "pvrusb2-hdw.h"
+ #include "pvrusb2-io.h"
++#include <media/v4l2-device.h>
+ #include <media/cx2341x.h>
+ #include "pvrusb2-devattr.h"
+
+@@ -57,8 +58,6 @@
+ #define LOCK_TAKE(x) do { mutex_lock(&x##_mutex); x##_held = !0; } while (0)
+ #define LOCK_GIVE(x) do { x##_held = 0; mutex_unlock(&x##_mutex); } while (0)
+
+-struct pvr2_decoder;
+-
+ typedef int (*pvr2_ctlf_is_dirty)(struct pvr2_ctrl *);
+ typedef void (*pvr2_ctlf_clear_dirty)(struct pvr2_ctrl *);
+ typedef int (*pvr2_ctlf_check_value)(struct pvr2_ctrl *,int);
+@@ -139,22 +138,6 @@ struct pvr2_ctrl {
+ };
+
+
+-struct pvr2_decoder_ctrl {
+- void *ctxt;
+- void (*detach)(void *);
+- void (*enable)(void *,int);
+- void (*force_reset)(void *);
+-};
+-
+-#define PVR2_I2C_PEND_DETECT 0x01 /* Need to detect a client type */
+-#define PVR2_I2C_PEND_CLIENT 0x02 /* Client needs a specific update */
+-#define PVR2_I2C_PEND_REFRESH 0x04 /* Client has specific pending bits */
+-#define PVR2_I2C_PEND_STALE 0x08 /* Broadcast pending bits */
+-
+-#define PVR2_I2C_PEND_ALL (PVR2_I2C_PEND_DETECT |\
+- PVR2_I2C_PEND_CLIENT |\
+- PVR2_I2C_PEND_REFRESH |\
+- PVR2_I2C_PEND_STALE)
+
+ /* Disposition of firmware1 loading situation */
+ #define FW1_STATE_UNKNOWN 0
+@@ -179,6 +162,8 @@ struct pvr2_hdw {
+ struct usb_device *usb_dev;
+ struct usb_interface *usb_intf;
+
++ /* Our handle into the v4l2 sub-device architecture */
++ struct v4l2_device v4l2_dev;
+ /* Device description, anything that must adjust behavior based on
+ device specific info will use information held here. */
+ const struct pvr2_device_desc *hdw_desc;
+@@ -186,7 +171,6 @@ struct pvr2_hdw {
+ /* Kernel worker thread handling */
+ struct workqueue_struct *workqueue;
+ struct work_struct workpoll; /* Update driver state */
+- struct work_struct worki2csync; /* Update i2c clients */
+
+ /* Video spigot */
+ struct pvr2_stream *vid_stream;
+@@ -195,20 +179,26 @@ struct pvr2_hdw {
+ struct mutex big_lock_mutex;
+ int big_lock_held; /* For debugging */
+
++ /* This is a simple string which identifies the instance of this
++ driver. It is unique within the set of existing devices, but
++ there is no attempt to keep the name consistent with the same
++ physical device each time. */
+ char name[32];
+
++ /* This is a simple string which identifies the physical device
++ instance itself - if possible. (If not possible, then it is
++ based on the specific driver instance, similar to name above.)
++ The idea here is that userspace might hopefully be able to use
++ this recognize specific tuners. It will encode a serial number,
++ if available. */
++ char identifier[32];
++
+ /* I2C stuff */
+ struct i2c_adapter i2c_adap;
+ struct i2c_algorithm i2c_algo;
+ pvr2_i2c_func i2c_func[PVR2_I2C_FUNC_CNT];
+ int i2c_cx25840_hack_state;
+ int i2c_linked;
+- unsigned int i2c_pend_types; /* Which types of update are needed */
+- unsigned long i2c_pend_mask; /* Change bits we need to scan */
+- unsigned long i2c_stale_mask; /* Pending broadcast change bits */
+- unsigned long i2c_active_mask; /* All change bits currently in use */
+- struct list_head i2c_clients;
+- struct mutex i2c_list_lock;
+
+ /* Frequency table */
+ unsigned int freqTable[FREQTABLE_SIZE];
+@@ -275,6 +265,7 @@ struct pvr2_hdw {
+ wait_queue_head_t state_wait_data;
+
+
++ int force_dirty; /* consider all controls dirty if true */
+ int flag_ok; /* device in known good state */
+ int flag_disconnected; /* flag_ok == 0 due to disconnect */
+ int flag_init_ok; /* true if structure is fully initialized */
+@@ -283,17 +274,13 @@ struct pvr2_hdw {
+ int flag_decoder_missed;/* We've noticed missing decoder */
+ int flag_tripped; /* Indicates overall failure to start */
+
+- struct pvr2_decoder_ctrl *decoder_ctrl;
++ unsigned int decoder_client_id;
+
+ // CPU firmware info (used to help find / save firmware data)
+ char *fw_buffer;
+ unsigned int fw_size;
+ int fw_cpu_flag; /* True if we are dealing with the CPU */
+
+- // True if there is a request to trigger logging of state in each
+- // module.
+- int log_requested;
+-
+ /* Tuner / frequency control stuff */
+ unsigned int tuner_type;
+ int tuner_updated;
+@@ -391,7 +378,8 @@ struct pvr2_hdw {
+
+ /* This function gets the current frequency */
+ unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *);
+-void pvr2_hdw_set_decoder(struct pvr2_hdw *,struct pvr2_decoder_ctrl *);
++
++void pvr2_hdw_status_poll(struct pvr2_hdw *);
+
+ #endif /* __PVRUSB2_HDW_INTERNAL_H */
+
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+index fa304e5..7a65b42 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
++++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+@@ -24,17 +24,22 @@
+ #include <linux/firmware.h>
+ #include <linux/videodev2.h>
+ #include <media/v4l2-common.h>
++#include <media/tuner.h>
+ #include "pvrusb2.h"
+ #include "pvrusb2-std.h"
+ #include "pvrusb2-util.h"
+ #include "pvrusb2-hdw.h"
+ #include "pvrusb2-i2c-core.h"
+-#include "pvrusb2-tuner.h"
+ #include "pvrusb2-eeprom.h"
+ #include "pvrusb2-hdw-internal.h"
+ #include "pvrusb2-encoder.h"
+ #include "pvrusb2-debug.h"
+ #include "pvrusb2-fx2-cmd.h"
++#include "pvrusb2-wm8775.h"
++#include "pvrusb2-video-v4l.h"
++#include "pvrusb2-cx2584x-v4l.h"
++#include "pvrusb2-cs53l32a.h"
++#include "pvrusb2-audio.h"
+
+ #define TV_MIN_FREQ 55250000L
+ #define TV_MAX_FREQ 850000000L
+@@ -104,6 +109,39 @@ MODULE_PARM_DESC(radio_freq, "specify initial radio frequency");
+ /* size of a firmware chunk */
+ #define FIRMWARE_CHUNK_SIZE 0x2000
+
++typedef void (*pvr2_subdev_update_func)(struct pvr2_hdw *,
++ struct v4l2_subdev *);
++
++static const pvr2_subdev_update_func pvr2_module_update_functions[] = {
++ [PVR2_CLIENT_ID_WM8775] = pvr2_wm8775_subdev_update,
++ [PVR2_CLIENT_ID_SAA7115] = pvr2_saa7115_subdev_update,
++ [PVR2_CLIENT_ID_MSP3400] = pvr2_msp3400_subdev_update,
++ [PVR2_CLIENT_ID_CX25840] = pvr2_cx25840_subdev_update,
++ [PVR2_CLIENT_ID_CS53L32A] = pvr2_cs53l32a_subdev_update,
++};
++
++static const char *module_names[] = {
++ [PVR2_CLIENT_ID_MSP3400] = "msp3400",
++ [PVR2_CLIENT_ID_CX25840] = "cx25840",
++ [PVR2_CLIENT_ID_SAA7115] = "saa7115",
++ [PVR2_CLIENT_ID_TUNER] = "tuner",
++ [PVR2_CLIENT_ID_DEMOD] = "tuner",
++ [PVR2_CLIENT_ID_CS53L32A] = "cs53l32a",
++ [PVR2_CLIENT_ID_WM8775] = "wm8775",
++};
++
++
++static const unsigned char *module_i2c_addresses[] = {
++ [PVR2_CLIENT_ID_TUNER] = "\x60\x61\x62\x63",
++ [PVR2_CLIENT_ID_DEMOD] = "\x43",
++ [PVR2_CLIENT_ID_MSP3400] = "\x40",
++ [PVR2_CLIENT_ID_SAA7115] = "\x21",
++ [PVR2_CLIENT_ID_WM8775] = "\x1b",
++ [PVR2_CLIENT_ID_CX25840] = "\x44",
++ [PVR2_CLIENT_ID_CS53L32A] = "\x11",
++};
++
++
+ /* Define the list of additional controls we'll dynamically construct based
+ on query of the cx2341x module. */
+ struct pvr2_mpeg_ids {
+@@ -277,7 +315,6 @@ static int pvr2_hdw_set_input(struct pvr2_hdw *hdw,int v);
+ static void pvr2_hdw_state_sched(struct pvr2_hdw *);
+ static int pvr2_hdw_state_eval(struct pvr2_hdw *);
+ static void pvr2_hdw_set_cur_freq(struct pvr2_hdw *,unsigned long);
+-static void pvr2_hdw_worker_i2c(struct work_struct *work);
+ static void pvr2_hdw_worker_poll(struct work_struct *work);
+ static int pvr2_hdw_wait(struct pvr2_hdw *,int state);
+ static int pvr2_hdw_untrip_unlocked(struct pvr2_hdw *);
+@@ -642,7 +679,7 @@ static int ctrl_freq_max_get(struct pvr2_ctrl *cptr, int *vp)
+ unsigned long fv;
+ struct pvr2_hdw *hdw = cptr->hdw;
+ if (hdw->tuner_signal_stale) {
+- pvr2_i2c_core_status_poll(hdw);
++ pvr2_hdw_status_poll(hdw);
+ }
+ fv = hdw->tuner_signal_info.rangehigh;
+ if (!fv) {
+@@ -664,7 +701,7 @@ static int ctrl_freq_min_get(struct pvr2_ctrl *cptr, int *vp)
+ unsigned long fv;
+ struct pvr2_hdw *hdw = cptr->hdw;
+ if (hdw->tuner_signal_stale) {
+- pvr2_i2c_core_status_poll(hdw);
++ pvr2_hdw_status_poll(hdw);
+ }
+ fv = hdw->tuner_signal_info.rangelow;
+ if (!fv) {
+@@ -858,7 +895,7 @@ static void ctrl_stdcur_clear_dirty(struct pvr2_ctrl *cptr)
+ static int ctrl_signal_get(struct pvr2_ctrl *cptr,int *vp)
+ {
+ struct pvr2_hdw *hdw = cptr->hdw;
+- pvr2_i2c_core_status_poll(hdw);
++ pvr2_hdw_status_poll(hdw);
+ *vp = hdw->tuner_signal_info.signal;
+ return 0;
+ }
+@@ -868,7 +905,7 @@ static int ctrl_audio_modes_present_get(struct pvr2_ctrl *cptr,int *vp)
+ int val = 0;
+ unsigned int subchan;
+ struct pvr2_hdw *hdw = cptr->hdw;
+- pvr2_i2c_core_status_poll(hdw);
++ pvr2_hdw_status_poll(hdw);
+ subchan = hdw->tuner_signal_info.rxsubchans;
+ if (subchan & V4L2_TUNER_SUB_MONO) {
+ val |= (1 << V4L2_TUNER_MODE_MONO);
+@@ -1283,6 +1320,12 @@ const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *hdw)
+ }
+
+
++const char *pvr2_hdw_get_device_identifier(struct pvr2_hdw *hdw)
++{
++ return hdw->identifier;
++}
++
++
+ unsigned long pvr2_hdw_get_cur_freq(struct pvr2_hdw *hdw)
+ {
+ return hdw->freqSelector ? hdw->freqValTelevision : hdw->freqValRadio;
+@@ -1634,33 +1677,27 @@ static const char *pvr2_get_state_name(unsigned int st)
+
+ static int pvr2_decoder_enable(struct pvr2_hdw *hdw,int enablefl)
+ {
+- if (!hdw->decoder_ctrl) {
+- if (!hdw->flag_decoder_missed) {
+- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+- "WARNING: No decoder present");
+- hdw->flag_decoder_missed = !0;
+- trace_stbit("flag_decoder_missed",
+- hdw->flag_decoder_missed);
+- }
+- return -EIO;
++ /* Even though we really only care about the video decoder chip at
++ this point, we'll broadcast stream on/off to all sub-devices
++ anyway, just in case somebody else wants to hear the
++ command... */
++ pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 stream=%s",
++ (enablefl ? "on" : "off"));
++ v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_stream, enablefl);
++ if (hdw->decoder_client_id) {
++ /* We get here if the encoder has been noticed. Otherwise
++ we'll issue a warning to the user (which should
++ normally never happen). */
++ return 0;
+ }
+- hdw->decoder_ctrl->enable(hdw->decoder_ctrl->ctxt,enablefl);
+- return 0;
+-}
+-
+-
+-void pvr2_hdw_set_decoder(struct pvr2_hdw *hdw,struct pvr2_decoder_ctrl *ptr)
+-{
+- if (hdw->decoder_ctrl == ptr) return;
+- hdw->decoder_ctrl = ptr;
+- if (hdw->decoder_ctrl && hdw->flag_decoder_missed) {
+- hdw->flag_decoder_missed = 0;
++ if (!hdw->flag_decoder_missed) {
++ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
++ "WARNING: No decoder present");
++ hdw->flag_decoder_missed = !0;
+ trace_stbit("flag_decoder_missed",
+ hdw->flag_decoder_missed);
+- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+- "Decoder has appeared");
+- pvr2_hdw_state_sched(hdw);
+ }
++ return -EIO;
+ }
+
+
+@@ -1927,6 +1964,166 @@ static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
+ }
+
+
++static unsigned int pvr2_copy_i2c_addr_list(
++ unsigned short *dst, const unsigned char *src,
++ unsigned int dst_max)
++{
++ unsigned int cnt = 0;
++ if (!src) return 0;
++ while (src[cnt] && (cnt + 1) < dst_max) {
++ dst[cnt] = src[cnt];
++ cnt++;
++ }
++ dst[cnt] = I2C_CLIENT_END;
++ return cnt;
++}
++
++
++static int pvr2_hdw_load_subdev(struct pvr2_hdw *hdw,
++ const struct pvr2_device_client_desc *cd)
++{
++ const char *fname;
++ unsigned char mid;
++ struct v4l2_subdev *sd;
++ unsigned int i2ccnt;
++ const unsigned char *p;
++ /* Arbitrary count - max # i2c addresses we will probe */
++ unsigned short i2caddr[25];
++
++ mid = cd->module_id;
++ fname = (mid < ARRAY_SIZE(module_names)) ? module_names[mid] : NULL;
++ if (!fname) {
++ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
++ "Module ID %u for device %s has no name",
++ mid,
++ hdw->hdw_desc->description);
++ return -EINVAL;
++ }
++ pvr2_trace(PVR2_TRACE_INIT,
++ "Module ID %u (%s) for device %s being loaded...",
++ mid, fname,
++ hdw->hdw_desc->description);
++
++ i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, cd->i2c_address_list,
++ ARRAY_SIZE(i2caddr));
++ if (!i2ccnt && ((p = (mid < ARRAY_SIZE(module_i2c_addresses)) ?
++ module_i2c_addresses[mid] : NULL) != NULL)) {
++ /* Second chance: Try default i2c address list */
++ i2ccnt = pvr2_copy_i2c_addr_list(i2caddr, p,
++ ARRAY_SIZE(i2caddr));
++ if (i2ccnt) {
++ pvr2_trace(PVR2_TRACE_INIT,
++ "Module ID %u:"
++ " Using default i2c address list",
++ mid);
++ }
++ }
++
++ if (!i2ccnt) {
++ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
++ "Module ID %u (%s) for device %s:"
++ " No i2c addresses",
++ mid, fname, hdw->hdw_desc->description);
++ return -EINVAL;
++ }
++
++ /* Note how the 2nd and 3rd arguments are the same for both
++ * v4l2_i2c_new_subdev() and v4l2_i2c_new_probed_subdev(). Why?
++ * Well the 2nd argument is the module name to load, while the 3rd
++ * argument is documented in the framework as being the "chipid" -
++ * and every other place where I can find examples of this, the
++ * "chipid" appears to just be the module name again. So here we
++ * just do the same thing. */
++ if (i2ccnt == 1) {
++ pvr2_trace(PVR2_TRACE_INIT,
++ "Module ID %u:"
++ " Setting up with specified i2c address 0x%x",
++ mid, i2caddr[0]);
++ sd = v4l2_i2c_new_subdev(&hdw->i2c_adap,
++ fname, fname,
++ i2caddr[0]);
++ } else {
++ pvr2_trace(PVR2_TRACE_INIT,
++ "Module ID %u:"
++ " Setting up with address probe list",
++ mid);
++ sd = v4l2_i2c_new_probed_subdev(&hdw->i2c_adap,
++ fname, fname,
++ i2caddr);
++ }
++
++ if (!sd) {
++ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
++ "Module ID %u (%s) for device %s failed to load",
++ mid, fname, hdw->hdw_desc->description);
++ return -EIO;
++ }
++
++ /* Tag this sub-device instance with the module ID we know about.
++ In other places we'll use that tag to determine if the instance
++ requires special handling. */
++ sd->grp_id = mid;
++
++ pvr2_trace(PVR2_TRACE_INFO, "Attached sub-driver %s", fname);
++
++
++ /* client-specific setup... */
++ switch (mid) {
++ case PVR2_CLIENT_ID_CX25840:
++ hdw->decoder_client_id = mid;
++ {
++ /*
++ Mike Isely <isely@pobox.com> 19-Nov-2006 - This
++ bit of nuttiness for cx25840 causes that module
++ to correctly set up its video scaling. This is
++ really a problem in the cx25840 module itself,
++ but we work around it here. The problem has not
++ been seen in ivtv because there VBI is supported
++ and set up. We don't do VBI here (at least not
++ yet) and thus we never attempted to even set it
++ up.
++ */
++ struct v4l2_format fmt;
++ pvr2_trace(PVR2_TRACE_INIT,
++ "Module ID %u:"
++ " Executing cx25840 VBI hack",
++ mid);
++ memset(&fmt, 0, sizeof(fmt));
++ fmt.type = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE;
++ v4l2_device_call_all(&hdw->v4l2_dev, mid,
++ video, s_fmt, &fmt);
++ }
++ break;
++ case PVR2_CLIENT_ID_SAA7115:
++ hdw->decoder_client_id = mid;
++ break;
++ default: break;
++ }
++
++ return 0;
++}
++
++
++static void pvr2_hdw_load_modules(struct pvr2_hdw *hdw)
++{
++ unsigned int idx;
++ const struct pvr2_string_table *cm;
++ const struct pvr2_device_client_table *ct;
++ int okFl = !0;
++
++ cm = &hdw->hdw_desc->client_modules;
++ for (idx = 0; idx < cm->cnt; idx++) {
++ request_module(cm->lst[idx]);
++ }
++
++ ct = &hdw->hdw_desc->client_table;
++ for (idx = 0; idx < ct->cnt; idx++) {
++ if (pvr2_hdw_load_subdev(hdw, &ct->lst[idx]) < 0) okFl = 0;
++ }
++ if (!okFl) pvr2_hdw_render_useless(hdw);
++}
++
++
+ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
+ {
+ int ret;
+@@ -1966,9 +2163,7 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
+
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+- for (idx = 0; idx < hdw->hdw_desc->client_modules.cnt; idx++) {
+- request_module(hdw->hdw_desc->client_modules.lst[idx]);
+- }
++ hdw->force_dirty = !0;
+
+ if (!hdw->hdw_desc->flag_no_powerup) {
+ pvr2_hdw_cmd_powerup(hdw);
+@@ -1987,6 +2182,11 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
+ pvr2_i2c_core_init(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
++ pvr2_hdw_load_modules(hdw);
++ if (!pvr2_hdw_dev_ok(hdw)) return;
++
++ v4l2_device_call_all(&hdw->v4l2_dev, 0, core, init, 0);
++
+ for (idx = 0; idx < CTRLDEF_COUNT; idx++) {
+ cptr = hdw->controls + idx;
+ if (cptr->info->skip_init) continue;
+@@ -2024,6 +2224,19 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
+ hdw->std_mask_eeprom = V4L2_STD_ALL;
+ }
+
++ if (hdw->serial_number) {
++ idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
++ "sn-%lu", hdw->serial_number);
++ } else if (hdw->unit_number >= 0) {
++ idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
++ "unit-%c",
++ hdw->unit_number + 'a');
++ } else {
++ idx = scnprintf(hdw->identifier, sizeof(hdw->identifier) - 1,
++ "unit-??");
++ }
++ hdw->identifier[idx] = 0;
++
+ pvr2_hdw_setup_std(hdw);
+
+ if (!get_default_tuner_type(hdw)) {
+@@ -2032,8 +2245,6 @@ static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
+ hdw->tuner_type);
+ }
+
+- pvr2_i2c_core_check_stale(hdw);
+- hdw->tuner_updated = 0;
+
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+@@ -2171,11 +2382,14 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
+ struct pvr2_hdw *hdw = NULL;
+ int valid_std_mask;
+ struct pvr2_ctrl *cptr;
++ struct usb_device *usb_dev;
+ const struct pvr2_device_desc *hdw_desc;
+ __u8 ifnum;
+ struct v4l2_queryctrl qctrl;
+ struct pvr2_ctl_info *ciptr;
+
++ usb_dev = interface_to_usbdev(intf);
++
+ hdw_desc = (const struct pvr2_device_desc *)(devid->driver_info);
+
+ if (hdw_desc == NULL) {
+@@ -2360,6 +2574,11 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
+ hdw->ctl_read_urb = usb_alloc_urb(0,GFP_KERNEL);
+ if (!hdw->ctl_read_urb) goto fail;
+
++ if (v4l2_device_register(&usb_dev->dev, &hdw->v4l2_dev) != 0) {
++ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
++ "Error registering with v4l core, giving up");
++ goto fail;
++ }
+ mutex_lock(&pvr2_unit_mtx); do {
+ for (idx = 0; idx < PVR_NUM; idx++) {
+ if (unit_pointers[idx]) continue;
+@@ -2382,7 +2601,6 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
+
+ hdw->workqueue = create_singlethread_workqueue(hdw->name);
+ INIT_WORK(&hdw->workpoll,pvr2_hdw_worker_poll);
+- INIT_WORK(&hdw->worki2csync,pvr2_hdw_worker_i2c);
+
+ pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s",
+ hdw->unit_number,hdw->name);
+@@ -2391,12 +2609,9 @@ struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
+ hdw->flag_ok = !0;
+
+ hdw->usb_intf = intf;
+- hdw->usb_dev = interface_to_usbdev(intf);
++ hdw->usb_dev = usb_dev;
+
+- scnprintf(hdw->bus_info,sizeof(hdw->bus_info),
+- "usb %s address %d",
+- dev_name(&hdw->usb_dev->dev),
+- hdw->usb_dev->devnum);
++ usb_make_path(hdw->usb_dev, hdw->bus_info, sizeof(hdw->bus_info));
+
+ ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber;
+ usb_set_interface(hdw->usb_dev,ifnum,0);
+@@ -2454,6 +2669,10 @@ static void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw)
+ hdw->ctl_write_buffer = NULL;
+ }
+ hdw->flag_disconnected = !0;
++ /* If we don't do this, then there will be a dangling struct device
++ reference to our disappearing device persisting inside the V4L
++ core... */
++ v4l2_device_disconnect(&hdw->v4l2_dev);
+ hdw->usb_dev = NULL;
+ hdw->usb_intf = NULL;
+ pvr2_hdw_render_useless(hdw);
+@@ -2481,10 +2700,8 @@ void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
+ pvr2_stream_destroy(hdw->vid_stream);
+ hdw->vid_stream = NULL;
+ }
+- if (hdw->decoder_ctrl) {
+- hdw->decoder_ctrl->detach(hdw->decoder_ctrl->ctxt);
+- }
+ pvr2_i2c_core_done(hdw);
++ v4l2_device_unregister(&hdw->v4l2_dev);
+ pvr2_hdw_remove_usb_stuff(hdw);
+ mutex_lock(&pvr2_unit_mtx); do {
+ if ((hdw->unit_number >= 0) &&
+@@ -2678,6 +2895,150 @@ static const char *get_ctrl_typename(enum pvr2_ctl_type tp)
+ }
+
+
++static void pvr2_subdev_set_control(struct pvr2_hdw *hdw, int id,
++ const char *name, int val)
++{
++ struct v4l2_control ctrl;
++ pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 %s=%d", name, val);
++ memset(&ctrl, 0, sizeof(ctrl));
++ ctrl.id = id;
++ ctrl.value = val;
++ v4l2_device_call_all(&hdw->v4l2_dev, 0, core, s_ctrl, &ctrl);
++}
++
++#define PVR2_SUBDEV_SET_CONTROL(hdw, id, lab) \
++ if ((hdw)->lab##_dirty || (hdw)->force_dirty) { \
++ pvr2_subdev_set_control(hdw, id, #lab, (hdw)->lab##_val); \
++ }
++
++/* Execute whatever commands are required to update the state of all the
++ sub-devices so that they match our current control values. */
++static void pvr2_subdev_update(struct pvr2_hdw *hdw)
++{
++ struct v4l2_subdev *sd;
++ unsigned int id;
++ pvr2_subdev_update_func fp;
++
++ pvr2_trace(PVR2_TRACE_CHIPS, "subdev update...");
++
++ if (hdw->tuner_updated || hdw->force_dirty) {
++ struct tuner_setup setup;
++ pvr2_trace(PVR2_TRACE_CHIPS, "subdev tuner set_type(%d)",
++ hdw->tuner_type);
++ if (((int)(hdw->tuner_type)) >= 0) {
++ setup.addr = ADDR_UNSET;
++ setup.type = hdw->tuner_type;
++ setup.mode_mask = T_RADIO | T_ANALOG_TV;
++ v4l2_device_call_all(&hdw->v4l2_dev, 0,
++ tuner, s_type_addr, &setup);
++ }
++ }
++
++ if (hdw->input_dirty || hdw->std_dirty || hdw->force_dirty) {
++ pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_standard");
++ if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
++ v4l2_device_call_all(&hdw->v4l2_dev, 0,
++ tuner, s_radio);
++ } else {
++ v4l2_std_id vs;
++ vs = hdw->std_mask_cur;
++ v4l2_device_call_all(&hdw->v4l2_dev, 0,
++ tuner, s_std, vs);
++ }
++ hdw->tuner_signal_stale = !0;
++ hdw->cropcap_stale = !0;
++ }
++
++ PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_BRIGHTNESS, brightness);
++ PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_CONTRAST, contrast);
++ PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_SATURATION, saturation);
++ PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_HUE, hue);
++ PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_MUTE, mute);
++ PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_VOLUME, volume);
++ PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BALANCE, balance);
++ PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_BASS, bass);
++ PVR2_SUBDEV_SET_CONTROL(hdw, V4L2_CID_AUDIO_TREBLE, treble);
++
++ if (hdw->input_dirty || hdw->audiomode_dirty || hdw->force_dirty) {
++ struct v4l2_tuner vt;
++ memset(&vt, 0, sizeof(vt));
++ vt.audmode = hdw->audiomode_val;
++ v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, s_tuner, &vt);
++ }
++
++ if (hdw->freqDirty || hdw->force_dirty) {
++ unsigned long fv;
++ struct v4l2_frequency freq;
++ fv = pvr2_hdw_get_cur_freq(hdw);
++ pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_freq(%lu)", fv);
++ if (hdw->tuner_signal_stale) pvr2_hdw_status_poll(hdw);
++ memset(&freq, 0, sizeof(freq));
++ if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) {
++ /* ((fv * 1000) / 62500) */
++ freq.frequency = (fv * 2) / 125;
++ } else {
++ freq.frequency = fv / 62500;
++ }
++ /* tuner-core currently doesn't seem to care about this, but
++ let's set it anyway for completeness. */
++ if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
++ freq.type = V4L2_TUNER_RADIO;
++ } else {
++ freq.type = V4L2_TUNER_ANALOG_TV;
++ }
++ freq.tuner = 0;
++ v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner,
++ s_frequency, &freq);
++ }
++
++ if (hdw->res_hor_dirty || hdw->res_ver_dirty || hdw->force_dirty) {
++ struct v4l2_format fmt;
++ memset(&fmt, 0, sizeof(fmt));
++ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ fmt.fmt.pix.width = hdw->res_hor_val;
++ fmt.fmt.pix.height = hdw->res_ver_val;
++ pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_size(%dx%d)",
++ fmt.fmt.pix.width, fmt.fmt.pix.height);
++ v4l2_device_call_all(&hdw->v4l2_dev, 0, video, s_fmt, &fmt);
++ }
++
++ if (hdw->srate_dirty || hdw->force_dirty) {
++ u32 val;
++ pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_audio %d",
++ hdw->srate_val);
++ switch (hdw->srate_val) {
++ default:
++ case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000:
++ val = 48000;
++ break;
++ case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100:
++ val = 44100;
++ break;
++ case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000:
++ val = 32000;
++ break;
++ }
++ v4l2_device_call_all(&hdw->v4l2_dev, 0,
++ audio, s_clock_freq, val);
++ }
++
++ /* Unable to set crop parameters; there is apparently no equivalent
++ for VIDIOC_S_CROP */
++
++ v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) {
++ id = sd->grp_id;
++ if (id >= ARRAY_SIZE(pvr2_module_update_functions)) continue;
++ fp = pvr2_module_update_functions[id];
++ if (!fp) continue;
++ (*fp)(hdw, sd);
++ }
++
++ if (hdw->tuner_signal_stale || hdw->cropcap_stale) {
++ pvr2_hdw_status_poll(hdw);
++ }
++}
++
++
+ /* Figure out if we need to commit control changes. If so, mark internal
+ state flags to indicate this fact and return true. Otherwise do nothing
+ else and return false. */
+@@ -2686,7 +3047,7 @@ static int pvr2_hdw_commit_setup(struct pvr2_hdw *hdw)
+ unsigned int idx;
+ struct pvr2_ctrl *cptr;
+ int value;
+- int commit_flag = 0;
++ int commit_flag = hdw->force_dirty;
+ char buf[100];
+ unsigned int bcnt,ccnt;
+
+@@ -2842,18 +3203,6 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw)
+ cx2341x_ext_ctrls(&hdw->enc_ctl_state, 0, &cs,VIDIOC_S_EXT_CTRLS);
+ }
+
+- /* Scan i2c core at this point - before we clear all the dirty
+- bits. Various parts of the i2c core will notice dirty bits as
+- appropriate and arrange to broadcast or directly send updates to
+- the client drivers in order to keep everything in sync */
+- pvr2_i2c_core_check_stale(hdw);
+-
+- for (idx = 0; idx < hdw->control_cnt; idx++) {
+- cptr = hdw->controls + idx;
+- if (!cptr->info->clear_dirty) continue;
+- cptr->info->clear_dirty(cptr);
+- }
+-
+ if (hdw->active_stream_type != hdw->desired_stream_type) {
+ /* Handle any side effects of stream config here */
+ hdw->active_stream_type = hdw->desired_stream_type;
+@@ -2873,8 +3222,16 @@ static int pvr2_hdw_commit_execute(struct pvr2_hdw *hdw)
+ }
+ }
+
+- /* Now execute i2c core update */
+- pvr2_i2c_core_sync(hdw);
++ /* Check and update state for all sub-devices. */
++ pvr2_subdev_update(hdw);
++
++ hdw->tuner_updated = 0;
++ hdw->force_dirty = 0;
++ for (idx = 0; idx < hdw->control_cnt; idx++) {
++ cptr = hdw->controls + idx;
++ if (!cptr->info->clear_dirty) continue;
++ cptr->info->clear_dirty(cptr);
++ }
+
+ if ((hdw->pathway_state == PVR2_PATHWAY_ANALOG) &&
+ hdw->state_encoder_run) {
+@@ -2904,15 +3261,6 @@ int pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw)
+ }
+
+
+-static void pvr2_hdw_worker_i2c(struct work_struct *work)
+-{
+- struct pvr2_hdw *hdw = container_of(work,struct pvr2_hdw,worki2csync);
+- LOCK_TAKE(hdw->big_lock); do {
+- pvr2_i2c_core_sync(hdw);
+- } while (0); LOCK_GIVE(hdw->big_lock);
+-}
+-
+-
+ static void pvr2_hdw_worker_poll(struct work_struct *work)
+ {
+ int fl = 0;
+@@ -2973,7 +3321,7 @@ int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw)
+ void pvr2_hdw_execute_tuner_poll(struct pvr2_hdw *hdw)
+ {
+ LOCK_TAKE(hdw->big_lock); do {
+- pvr2_i2c_core_status_poll(hdw);
++ pvr2_hdw_status_poll(hdw);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+ }
+
+@@ -2983,7 +3331,7 @@ static int pvr2_hdw_check_cropcap(struct pvr2_hdw *hdw)
+ if (!hdw->cropcap_stale) {
+ return 0;
+ }
+- pvr2_i2c_core_status_poll(hdw);
++ pvr2_hdw_status_poll(hdw);
+ if (hdw->cropcap_stale) {
+ return -EIO;
+ }
+@@ -3010,7 +3358,7 @@ int pvr2_hdw_get_tuner_status(struct pvr2_hdw *hdw,struct v4l2_tuner *vtp)
+ {
+ LOCK_TAKE(hdw->big_lock); do {
+ if (hdw->tuner_signal_stale) {
+- pvr2_i2c_core_status_poll(hdw);
++ pvr2_hdw_status_poll(hdw);
+ }
+ memcpy(vtp,&hdw->tuner_signal_info,sizeof(struct v4l2_tuner));
+ } while (0); LOCK_GIVE(hdw->big_lock);
+@@ -3029,11 +3377,8 @@ void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw)
+ {
+ int nr = pvr2_hdw_get_unit_number(hdw);
+ LOCK_TAKE(hdw->big_lock); do {
+- hdw->log_requested = !0;
+ printk(KERN_INFO "pvrusb2: ================= START STATUS CARD #%d =================\n", nr);
+- pvr2_i2c_core_check_stale(hdw);
+- hdw->log_requested = 0;
+- pvr2_i2c_core_sync(hdw);
++ v4l2_device_call_all(&hdw->v4l2_dev, 0, core, log_status);
+ pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:");
+ cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2");
+ pvr2_hdw_state_log_state(hdw);
+@@ -3716,22 +4061,16 @@ int pvr2_hdw_cmd_powerdown(struct pvr2_hdw *hdw)
+
+ int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw)
+ {
+- if (!hdw->decoder_ctrl) {
+- pvr2_trace(PVR2_TRACE_INIT,
+- "Unable to reset decoder: nothing attached");
+- return -ENOTTY;
+- }
+-
+- if (!hdw->decoder_ctrl->force_reset) {
+- pvr2_trace(PVR2_TRACE_INIT,
+- "Unable to reset decoder: not implemented");
+- return -ENOTTY;
+- }
+-
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Requesting decoder reset");
+- hdw->decoder_ctrl->force_reset(hdw->decoder_ctrl->ctxt);
+- return 0;
++ if (hdw->decoder_client_id) {
++ v4l2_device_call_all(&hdw->v4l2_dev, hdw->decoder_client_id,
++ core, reset, 0);
++ return 0;
++ }
++ pvr2_trace(PVR2_TRACE_INIT,
++ "Unable to reset decoder: nothing attached");
++ return -ENOTTY;
+ }
+
+
+@@ -4476,6 +4815,79 @@ static unsigned int pvr2_hdw_report_unlocked(struct pvr2_hdw *hdw,int which,
+ }
+
+
++/* Generate report containing info about attached sub-devices and attached
++ i2c clients, including an indication of which attached i2c clients are
++ actually sub-devices. */
++static unsigned int pvr2_hdw_report_clients(struct pvr2_hdw *hdw,
++ char *buf, unsigned int acnt)
++{
++ struct v4l2_subdev *sd;
++ unsigned int tcnt = 0;
++ unsigned int ccnt;
++ struct i2c_client *client;
++ struct list_head *item;
++ void *cd;
++ const char *p;
++ unsigned int id;
++
++ ccnt = scnprintf(buf, acnt, "Associated v4l2-subdev drivers:");
++ tcnt += ccnt;
++ v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) {
++ id = sd->grp_id;
++ p = NULL;
++ if (id < ARRAY_SIZE(module_names)) p = module_names[id];
++ if (p) {
++ ccnt = scnprintf(buf + tcnt, acnt - tcnt, " %s", p);
++ tcnt += ccnt;
++ } else {
++ ccnt = scnprintf(buf + tcnt, acnt - tcnt,
++ " (unknown id=%u)", id);
++ tcnt += ccnt;
++ }
++ }
++ ccnt = scnprintf(buf + tcnt, acnt - tcnt, "\n");
++ tcnt += ccnt;
++
++ ccnt = scnprintf(buf + tcnt, acnt - tcnt, "I2C clients:\n");
++ tcnt += ccnt;
++
++ mutex_lock(&hdw->i2c_adap.clist_lock);
++ list_for_each(item, &hdw->i2c_adap.clients) {
++ client = list_entry(item, struct i2c_client, list);
++ ccnt = scnprintf(buf + tcnt, acnt - tcnt,
++ " %s: i2c=%02x", client->name, client->addr);
++ tcnt += ccnt;
++ cd = i2c_get_clientdata(client);
++ v4l2_device_for_each_subdev(sd, &hdw->v4l2_dev) {
++ if (cd == sd) {
++ id = sd->grp_id;
++ p = NULL;
++ if (id < ARRAY_SIZE(module_names)) {
++ p = module_names[id];
++ }
++ if (p) {
++ ccnt = scnprintf(buf + tcnt,
++ acnt - tcnt,
++ " subdev=%s", p);
++ tcnt += ccnt;
++ } else {
++ ccnt = scnprintf(buf + tcnt,
++ acnt - tcnt,
++ " subdev= id %u)",
++ id);
++ tcnt += ccnt;
++ }
++ break;
++ }
++ }
++ ccnt = scnprintf(buf + tcnt, acnt - tcnt, "\n");
++ tcnt += ccnt;
++ }
++ mutex_unlock(&hdw->i2c_adap.clist_lock);
++ return tcnt;
++}
++
++
+ unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw,
+ char *buf,unsigned int acnt)
+ {
+@@ -4490,6 +4902,8 @@ unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw,
+ buf[0] = '\n'; ccnt = 1;
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ }
++ ccnt = pvr2_hdw_report_clients(hdw, buf, acnt);
++ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ LOCK_GIVE(hdw->big_lock);
+ return bcnt;
+ }
+@@ -4497,14 +4911,25 @@ unsigned int pvr2_hdw_state_report(struct pvr2_hdw *hdw,
+
+ static void pvr2_hdw_state_log_state(struct pvr2_hdw *hdw)
+ {
+- char buf[128];
+- unsigned int idx,ccnt;
++ char buf[256];
++ unsigned int idx, ccnt;
++ unsigned int lcnt, ucnt;
+
+ for (idx = 0; ; idx++) {
+ ccnt = pvr2_hdw_report_unlocked(hdw,idx,buf,sizeof(buf));
+ if (!ccnt) break;
+ printk(KERN_INFO "%s %.*s\n",hdw->name,ccnt,buf);
+ }
++ ccnt = pvr2_hdw_report_clients(hdw, buf, sizeof(buf));
++ ucnt = 0;
++ while (ucnt < ccnt) {
++ lcnt = 0;
++ while ((lcnt + ucnt < ccnt) && (buf[lcnt + ucnt] != '\n')) {
++ lcnt++;
++ }
++ printk(KERN_INFO "%s %.*s\n", hdw->name, lcnt, buf + ucnt);
++ ucnt += lcnt + 1;
++ }
+ }
+
+
+@@ -4641,6 +5066,30 @@ int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val)
+ }
+
+
++void pvr2_hdw_status_poll(struct pvr2_hdw *hdw)
++{
++ struct v4l2_tuner *vtp = &hdw->tuner_signal_info;
++ memset(vtp, 0, sizeof(*vtp));
++ hdw->tuner_signal_stale = 0;
++ /* Note: There apparently is no replacement for VIDIOC_CROPCAP
++ using v4l2-subdev - therefore we can't support that AT ALL right
++ now. (Of course, no sub-drivers seem to implement it either.
++ But now it's a a chicken and egg problem...) */
++ v4l2_device_call_all(&hdw->v4l2_dev, 0, tuner, g_tuner,
++ &hdw->tuner_signal_info);
++ pvr2_trace(PVR2_TRACE_CHIPS, "subdev status poll"
++ " type=%u strength=%u audio=0x%x cap=0x%x"
++ " low=%u hi=%u",
++ vtp->type,
++ vtp->signal, vtp->rxsubchans, vtp->capability,
++ vtp->rangelow, vtp->rangehigh);
++
++ /* We have to do this to avoid getting into constant polling if
++ there's nobody to answer a poll of cropcap info. */
++ hdw->cropcap_stale = 0;
++}
++
++
+ unsigned int pvr2_hdw_get_input_available(struct pvr2_hdw *hdw)
+ {
+ return hdw->input_avail_mask;
+@@ -4736,7 +5185,6 @@ int pvr2_hdw_register_access(struct pvr2_hdw *hdw,
+ int setFl, u64 *val_ptr)
+ {
+ #ifdef CONFIG_VIDEO_ADV_DEBUG
+- struct pvr2_i2c_client *cp;
+ struct v4l2_dbg_register req;
+ int stat = 0;
+ int okFl = 0;
+@@ -4746,21 +5194,9 @@ int pvr2_hdw_register_access(struct pvr2_hdw *hdw,
+ req.match = *match;
+ req.reg = reg_id;
+ if (setFl) req.val = *val_ptr;
+- mutex_lock(&hdw->i2c_list_lock); do {
+- list_for_each_entry(cp, &hdw->i2c_clients, list) {
+- if (!v4l2_chip_match_i2c_client(
+- cp->client,
+- &req.match)) {
+- continue;
+- }
+- stat = pvr2_i2c_client_cmd(
+- cp,(setFl ? VIDIOC_DBG_S_REGISTER :
+- VIDIOC_DBG_G_REGISTER),&req);
+- if (!setFl) *val_ptr = req.val;
+- okFl = !0;
+- break;
+- }
+- } while (0); mutex_unlock(&hdw->i2c_list_lock);
++ /* It would be nice to know if a sub-device answered the request */
++ v4l2_device_call_all(&hdw->v4l2_dev, 0, core, g_register, &req);
++ if (!setFl) *val_ptr = req.val;
+ if (okFl) {
+ return stat;
+ }
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
+index 1b4fec3..7b69405 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.h
++++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
+@@ -132,6 +132,9 @@ unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *);
+ /* Retrieve bus location info of device */
+ const char *pvr2_hdw_get_bus_info(struct pvr2_hdw *);
+
++/* Retrieve per-instance string identifier for this specific device */
++const char *pvr2_hdw_get_device_identifier(struct pvr2_hdw *);
++
+ /* Called when hardware has been unplugged */
+ void pvr2_hdw_disconnect(struct pvr2_hdw *);
+
+@@ -236,8 +239,7 @@ void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *,
+ enum pvr2_v4l_type index,int);
+
+ /* Direct read/write access to chip's registers:
+- match_type - how to interpret match_chip (e.g. driver ID, i2c address)
+- match_chip - chip match value (e.g. I2C_DRIVERD_xxxx)
++ match - specify criteria to identify target chip (this is a v4l dbg struct)
+ reg_id - register number to access
+ setFl - true to set the register, false to read it
+ val_ptr - storage location for source / result. */
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c
+deleted file mode 100644
+index 94a4771..0000000
+--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c
++++ /dev/null
+@@ -1,113 +0,0 @@
+-/*
+- *
+- *
+- * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2 of the License
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+- *
+- */
+-
+-#include <linux/kernel.h>
+-#include "pvrusb2-i2c-core.h"
+-#include "pvrusb2-hdw-internal.h"
+-#include "pvrusb2-debug.h"
+-#include "pvrusb2-i2c-cmd-v4l2.h"
+-#include "pvrusb2-audio.h"
+-#include "pvrusb2-tuner.h"
+-#include "pvrusb2-video-v4l.h"
+-#include "pvrusb2-cx2584x-v4l.h"
+-#include "pvrusb2-wm8775.h"
+-
+-#define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__)
+-
+-#define OP_STANDARD 0
+-#define OP_AUDIOMODE 1
+-#define OP_BCSH 2
+-#define OP_VOLUME 3
+-#define OP_FREQ 4
+-#define OP_AUDIORATE 5
+-#define OP_CROP 6
+-#define OP_SIZE 7
+-#define OP_LOG 8
+-
+-static const struct pvr2_i2c_op * const ops[] = {
+- [OP_STANDARD] = &pvr2_i2c_op_v4l2_standard,
+- [OP_AUDIOMODE] = &pvr2_i2c_op_v4l2_audiomode,
+- [OP_BCSH] = &pvr2_i2c_op_v4l2_bcsh,
+- [OP_VOLUME] = &pvr2_i2c_op_v4l2_volume,
+- [OP_FREQ] = &pvr2_i2c_op_v4l2_frequency,
+- [OP_CROP] = &pvr2_i2c_op_v4l2_crop,
+- [OP_SIZE] = &pvr2_i2c_op_v4l2_size,
+- [OP_LOG] = &pvr2_i2c_op_v4l2_log,
+-};
+-
+-void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp)
+-{
+- int id;
+- id = cp->client->driver->id;
+- cp->ctl_mask = ((1 << OP_STANDARD) |
+- (1 << OP_AUDIOMODE) |
+- (1 << OP_BCSH) |
+- (1 << OP_VOLUME) |
+- (1 << OP_FREQ) |
+- (1 << OP_CROP) |
+- (1 << OP_SIZE) |
+- (1 << OP_LOG));
+- cp->status_poll = pvr2_v4l2_cmd_status_poll;
+-
+- if (id == I2C_DRIVERID_MSP3400) {
+- if (pvr2_i2c_msp3400_setup(hdw,cp)) {
+- return;
+- }
+- }
+- if (id == I2C_DRIVERID_TUNER) {
+- if (pvr2_i2c_tuner_setup(hdw,cp)) {
+- return;
+- }
+- }
+- if (id == I2C_DRIVERID_CX25840) {
+- if (pvr2_i2c_cx2584x_v4l_setup(hdw,cp)) {
+- return;
+- }
+- }
+- if (id == I2C_DRIVERID_WM8775) {
+- if (pvr2_i2c_wm8775_setup(hdw,cp)) {
+- return;
+- }
+- }
+- if (id == I2C_DRIVERID_SAA711X) {
+- if (pvr2_i2c_decoder_v4l_setup(hdw,cp)) {
+- return;
+- }
+- }
+-}
+-
+-
+-const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx)
+-{
+- if (idx >= ARRAY_SIZE(ops))
+- return NULL;
+- return ops[idx];
+-}
+-
+-
+-/*
+- Stuff for Emacs to see, in order to encourage consistent editing style:
+- *** Local Variables: ***
+- *** mode: c ***
+- *** fill-column: 75 ***
+- *** tab-width: 8 ***
+- *** c-basic-offset: 8 ***
+- *** End: ***
+- */
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c
+deleted file mode 100644
+index 16bb119..0000000
+--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c
++++ /dev/null
+@@ -1,322 +0,0 @@
+-/*
+- *
+- *
+- * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+- * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+- *
+- * 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
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+- *
+- */
+-
+-#include "pvrusb2-i2c-cmd-v4l2.h"
+-#include "pvrusb2-hdw-internal.h"
+-#include "pvrusb2-debug.h"
+-#include <linux/videodev2.h>
+-#include <media/v4l2-common.h>
+-
+-static void set_standard(struct pvr2_hdw *hdw)
+-{
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_standard");
+-
+- if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
+- pvr2_i2c_core_cmd(hdw,AUDC_SET_RADIO,NULL);
+- } else {
+- v4l2_std_id vs;
+- vs = hdw->std_mask_cur;
+- pvr2_i2c_core_cmd(hdw,VIDIOC_S_STD,&vs);
+- }
+- hdw->tuner_signal_stale = !0;
+- hdw->cropcap_stale = !0;
+-}
+-
+-
+-static int check_standard(struct pvr2_hdw *hdw)
+-{
+- return (hdw->input_dirty != 0) || (hdw->std_dirty != 0);
+-}
+-
+-
+-const struct pvr2_i2c_op pvr2_i2c_op_v4l2_standard = {
+- .check = check_standard,
+- .update = set_standard,
+- .name = "v4l2_standard",
+-};
+-
+-
+-static void set_bcsh(struct pvr2_hdw *hdw)
+-{
+- struct v4l2_control ctrl;
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_bcsh"
+- " b=%d c=%d s=%d h=%d",
+- hdw->brightness_val,hdw->contrast_val,
+- hdw->saturation_val,hdw->hue_val);
+- memset(&ctrl,0,sizeof(ctrl));
+- ctrl.id = V4L2_CID_BRIGHTNESS;
+- ctrl.value = hdw->brightness_val;
+- pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+- ctrl.id = V4L2_CID_CONTRAST;
+- ctrl.value = hdw->contrast_val;
+- pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+- ctrl.id = V4L2_CID_SATURATION;
+- ctrl.value = hdw->saturation_val;
+- pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+- ctrl.id = V4L2_CID_HUE;
+- ctrl.value = hdw->hue_val;
+- pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+-}
+-
+-
+-static int check_bcsh(struct pvr2_hdw *hdw)
+-{
+- return (hdw->brightness_dirty ||
+- hdw->contrast_dirty ||
+- hdw->saturation_dirty ||
+- hdw->hue_dirty);
+-}
+-
+-
+-const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh = {
+- .check = check_bcsh,
+- .update = set_bcsh,
+- .name = "v4l2_bcsh",
+-};
+-
+-
+-static void set_volume(struct pvr2_hdw *hdw)
+-{
+- struct v4l2_control ctrl;
+- pvr2_trace(PVR2_TRACE_CHIPS,
+- "i2c v4l2 set_volume"
+- "(vol=%d bal=%d bas=%d treb=%d mute=%d)",
+- hdw->volume_val,
+- hdw->balance_val,
+- hdw->bass_val,
+- hdw->treble_val,
+- hdw->mute_val);
+- memset(&ctrl,0,sizeof(ctrl));
+- ctrl.id = V4L2_CID_AUDIO_MUTE;
+- ctrl.value = hdw->mute_val ? 1 : 0;
+- pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+- ctrl.id = V4L2_CID_AUDIO_VOLUME;
+- ctrl.value = hdw->volume_val;
+- pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+- ctrl.id = V4L2_CID_AUDIO_BALANCE;
+- ctrl.value = hdw->balance_val;
+- pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+- ctrl.id = V4L2_CID_AUDIO_BASS;
+- ctrl.value = hdw->bass_val;
+- pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+- ctrl.id = V4L2_CID_AUDIO_TREBLE;
+- ctrl.value = hdw->treble_val;
+- pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+-}
+-
+-
+-static int check_volume(struct pvr2_hdw *hdw)
+-{
+- return (hdw->volume_dirty ||
+- hdw->balance_dirty ||
+- hdw->bass_dirty ||
+- hdw->treble_dirty ||
+- hdw->mute_dirty);
+-}
+-
+-
+-const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume = {
+- .check = check_volume,
+- .update = set_volume,
+- .name = "v4l2_volume",
+-};
+-
+-
+-static void set_audiomode(struct pvr2_hdw *hdw)
+-{
+- struct v4l2_tuner vt;
+- memset(&vt,0,sizeof(vt));
+- vt.audmode = hdw->audiomode_val;
+- pvr2_i2c_core_cmd(hdw,VIDIOC_S_TUNER,&vt);
+-}
+-
+-
+-static int check_audiomode(struct pvr2_hdw *hdw)
+-{
+- return (hdw->input_dirty ||
+- hdw->audiomode_dirty);
+-}
+-
+-
+-const struct pvr2_i2c_op pvr2_i2c_op_v4l2_audiomode = {
+- .check = check_audiomode,
+- .update = set_audiomode,
+- .name = "v4l2_audiomode",
+-};
+-
+-
+-static void set_frequency(struct pvr2_hdw *hdw)
+-{
+- unsigned long fv;
+- struct v4l2_frequency freq;
+- fv = pvr2_hdw_get_cur_freq(hdw);
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_freq(%lu)",fv);
+- if (hdw->tuner_signal_stale) {
+- pvr2_i2c_core_status_poll(hdw);
+- }
+- memset(&freq,0,sizeof(freq));
+- if (hdw->tuner_signal_info.capability & V4L2_TUNER_CAP_LOW) {
+- // ((fv * 1000) / 62500)
+- freq.frequency = (fv * 2) / 125;
+- } else {
+- freq.frequency = fv / 62500;
+- }
+- /* tuner-core currently doesn't seem to care about this, but
+- let's set it anyway for completeness. */
+- if (hdw->input_val == PVR2_CVAL_INPUT_RADIO) {
+- freq.type = V4L2_TUNER_RADIO;
+- } else {
+- freq.type = V4L2_TUNER_ANALOG_TV;
+- }
+- freq.tuner = 0;
+- pvr2_i2c_core_cmd(hdw,VIDIOC_S_FREQUENCY,&freq);
+-}
+-
+-
+-static int check_frequency(struct pvr2_hdw *hdw)
+-{
+- return hdw->freqDirty != 0;
+-}
+-
+-
+-const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency = {
+- .check = check_frequency,
+- .update = set_frequency,
+- .name = "v4l2_freq",
+-};
+-
+-
+-static void set_size(struct pvr2_hdw *hdw)
+-{
+- struct v4l2_format fmt;
+-
+- memset(&fmt,0,sizeof(fmt));
+-
+- fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+- fmt.fmt.pix.width = hdw->res_hor_val;
+- fmt.fmt.pix.height = hdw->res_ver_val;
+-
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_size(%dx%d)",
+- fmt.fmt.pix.width,fmt.fmt.pix.height);
+-
+- pvr2_i2c_core_cmd(hdw,VIDIOC_S_FMT,&fmt);
+-}
+-
+-
+-static int check_size(struct pvr2_hdw *hdw)
+-{
+- return (hdw->res_hor_dirty || hdw->res_ver_dirty);
+-}
+-
+-
+-const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size = {
+- .check = check_size,
+- .update = set_size,
+- .name = "v4l2_size",
+-};
+-
+-
+-static void set_crop(struct pvr2_hdw *hdw)
+-{
+- struct v4l2_crop crop;
+-
+- memset(&crop, 0, sizeof crop);
+- crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+- crop.c.left = hdw->cropl_val;
+- crop.c.top = hdw->cropt_val;
+- crop.c.height = hdw->croph_val;
+- crop.c.width = hdw->cropw_val;
+-
+- pvr2_trace(PVR2_TRACE_CHIPS,
+- "i2c v4l2 set_crop crop=%d:%d:%d:%d",
+- crop.c.width, crop.c.height, crop.c.left, crop.c.top);
+-
+- pvr2_i2c_core_cmd(hdw, VIDIOC_S_CROP, &crop);
+-}
+-
+-static int check_crop(struct pvr2_hdw *hdw)
+-{
+- return (hdw->cropl_dirty || hdw->cropt_dirty ||
+- hdw->cropw_dirty || hdw->croph_dirty);
+-}
+-
+-const struct pvr2_i2c_op pvr2_i2c_op_v4l2_crop = {
+- .check = check_crop,
+- .update = set_crop,
+- .name = "v4l2_crop",
+-};
+-
+-
+-static void do_log(struct pvr2_hdw *hdw)
+-{
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 do_log()");
+- pvr2_i2c_core_cmd(hdw,VIDIOC_LOG_STATUS,NULL);
+-
+-}
+-
+-
+-static int check_log(struct pvr2_hdw *hdw)
+-{
+- return hdw->log_requested != 0;
+-}
+-
+-
+-const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log = {
+- .check = check_log,
+- .update = do_log,
+- .name = "v4l2_log",
+-};
+-
+-
+-void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *cp,int fl)
+-{
+- pvr2_i2c_client_cmd(cp,
+- (fl ? VIDIOC_STREAMON : VIDIOC_STREAMOFF),NULL);
+-}
+-
+-
+-void pvr2_v4l2_cmd_status_poll(struct pvr2_i2c_client *cp)
+-{
+- int stat;
+- struct pvr2_hdw *hdw = cp->hdw;
+- if (hdw->cropcap_stale) {
+- hdw->cropcap_info.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+- stat = pvr2_i2c_client_cmd(cp, VIDIOC_CROPCAP,
+- &hdw->cropcap_info);
+- if (stat == 0) {
+- /* Check was successful, so the data is no
+- longer considered stale. */
+- hdw->cropcap_stale = 0;
+- }
+- }
+- pvr2_i2c_client_cmd(cp, VIDIOC_G_TUNER, &hdw->tuner_signal_info);
+-}
+-
+-
+-/*
+- Stuff for Emacs to see, in order to encourage consistent editing style:
+- *** Local Variables: ***
+- *** mode: c ***
+- *** fill-column: 70 ***
+- *** tab-width: 8 ***
+- *** c-basic-offset: 8 ***
+- *** End: ***
+- */
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h
+deleted file mode 100644
+index eb744a2..0000000
+--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h
++++ /dev/null
+@@ -1,50 +0,0 @@
+-/*
+- *
+- *
+- * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+- * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+- *
+- * 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
+- *
+- * 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
+- *
+- */
+-
+-#ifndef __PVRUSB2_CMD_V4L2_H
+-#define __PVRUSB2_CMD_V4L2_H
+-
+-#include "pvrusb2-i2c-core.h"
+-
+-extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_standard;
+-extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_radio;
+-extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh;
+-extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume;
+-extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency;
+-extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_crop;
+-extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size;
+-extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_audiomode;
+-extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log;
+-
+-void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *,int);
+-void pvr2_v4l2_cmd_status_poll(struct pvr2_i2c_client *);
+-
+-#endif /* __PVRUSB2_CMD_V4L2_H */
+-
+-/*
+- Stuff for Emacs to see, in order to encourage consistent editing style:
+- *** Local Variables: ***
+- *** mode: c ***
+- *** fill-column: 70 ***
+- *** tab-width: 8 ***
+- *** c-basic-offset: 8 ***
+- *** End: ***
+- */
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
+index d6a3540..9464862 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
++++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
+@@ -18,6 +18,7 @@
+ *
+ */
+
++#include <linux/i2c.h>
+ #include "pvrusb2-i2c-core.h"
+ #include "pvrusb2-hdw-internal.h"
+ #include "pvrusb2-debug.h"
+@@ -29,8 +30,7 @@
+ /*
+
+ This module attempts to implement a compliant I2C adapter for the pvrusb2
+- device. By doing this we can then make use of existing functionality in
+- V4L (e.g. tuner.c) rather than rolling our own.
++ device.
+
+ */
+
+@@ -42,10 +42,6 @@ static int ir_mode[PVR_NUM] = { [0 ... PVR_NUM-1] = 1 };
+ module_param_array(ir_mode, int, NULL, 0444);
+ MODULE_PARM_DESC(ir_mode,"specify: 0=disable IR reception, 1=normal IR");
+
+-static unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp,
+- unsigned int detail,
+- char *buf,unsigned int maxlen);
+-
+ static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */
+ u8 i2c_addr, /* I2C address we're talking to */
+ u8 *data, /* Data to write */
+@@ -524,414 +520,13 @@ static u32 pvr2_i2c_functionality(struct i2c_adapter *adap)
+ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C;
+ }
+
+-static int pvr2_i2c_core_singleton(struct i2c_client *cp,
+- unsigned int cmd,void *arg)
+-{
+- int stat;
+- if (!cp) return -EINVAL;
+- if (!(cp->driver)) return -EINVAL;
+- if (!(cp->driver->command)) return -EINVAL;
+- if (!try_module_get(cp->driver->driver.owner)) return -EAGAIN;
+- stat = cp->driver->command(cp,cmd,arg);
+- module_put(cp->driver->driver.owner);
+- return stat;
+-}
+-
+-int pvr2_i2c_client_cmd(struct pvr2_i2c_client *cp,unsigned int cmd,void *arg)
+-{
+- int stat;
+- if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) {
+- char buf[100];
+- unsigned int cnt;
+- cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG,
+- buf,sizeof(buf));
+- pvr2_trace(PVR2_TRACE_I2C_CMD,
+- "i2c COMMAND (code=%u 0x%x) to %.*s",
+- cmd,cmd,cnt,buf);
+- }
+- stat = pvr2_i2c_core_singleton(cp->client,cmd,arg);
+- if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) {
+- char buf[100];
+- unsigned int cnt;
+- cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG,
+- buf,sizeof(buf));
+- pvr2_trace(PVR2_TRACE_I2C_CMD,
+- "i2c COMMAND to %.*s (ret=%d)",cnt,buf,stat);
+- }
+- return stat;
+-}
+-
+-int pvr2_i2c_core_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg)
+-{
+- struct pvr2_i2c_client *cp, *ncp;
+- int stat = -EINVAL;
+-
+- if (!hdw) return stat;
+-
+- mutex_lock(&hdw->i2c_list_lock);
+- list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, list) {
+- if (!cp->recv_enable) continue;
+- mutex_unlock(&hdw->i2c_list_lock);
+- stat = pvr2_i2c_client_cmd(cp,cmd,arg);
+- mutex_lock(&hdw->i2c_list_lock);
+- }
+- mutex_unlock(&hdw->i2c_list_lock);
+- return stat;
+-}
+-
+-
+-static int handler_check(struct pvr2_i2c_client *cp)
+-{
+- struct pvr2_i2c_handler *hp = cp->handler;
+- if (!hp) return 0;
+- if (!hp->func_table->check) return 0;
+- return hp->func_table->check(hp->func_data) != 0;
+-}
+-
+-#define BUFSIZE 500
+-
+-
+-void pvr2_i2c_core_status_poll(struct pvr2_hdw *hdw)
+-{
+- struct pvr2_i2c_client *cp;
+- mutex_lock(&hdw->i2c_list_lock); do {
+- struct v4l2_tuner *vtp = &hdw->tuner_signal_info;
+- memset(vtp,0,sizeof(*vtp));
+- list_for_each_entry(cp, &hdw->i2c_clients, list) {
+- if (!cp->detected_flag) continue;
+- if (!cp->status_poll) continue;
+- cp->status_poll(cp);
+- }
+- hdw->tuner_signal_stale = 0;
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c status poll"
+- " type=%u strength=%u audio=0x%x cap=0x%x"
+- " low=%u hi=%u",
+- vtp->type,
+- vtp->signal,vtp->rxsubchans,vtp->capability,
+- vtp->rangelow,vtp->rangehigh);
+- } while (0); mutex_unlock(&hdw->i2c_list_lock);
+-}
+-
+-
+-/* Issue various I2C operations to bring chip-level drivers into sync with
+- state stored in this driver. */
+-void pvr2_i2c_core_sync(struct pvr2_hdw *hdw)
+-{
+- unsigned long msk;
+- unsigned int idx;
+- struct pvr2_i2c_client *cp, *ncp;
+-
+- if (!hdw->i2c_linked) return;
+- if (!(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL)) {
+- return;
+- }
+- mutex_lock(&hdw->i2c_list_lock); do {
+- pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync BEGIN");
+- if (hdw->i2c_pend_types & PVR2_I2C_PEND_DETECT) {
+- /* One or more I2C clients have attached since we
+- last synced. So scan the list and identify the
+- new clients. */
+- char *buf;
+- unsigned int cnt;
+- unsigned long amask = 0;
+- buf = kmalloc(BUFSIZE,GFP_KERNEL);
+- pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_DETECT");
+- hdw->i2c_pend_types &= ~PVR2_I2C_PEND_DETECT;
+- list_for_each_entry(cp, &hdw->i2c_clients, list) {
+- if (!cp->detected_flag) {
+- cp->ctl_mask = 0;
+- pvr2_i2c_probe(hdw,cp);
+- cp->detected_flag = !0;
+- msk = cp->ctl_mask;
+- cnt = 0;
+- if (buf) {
+- cnt = pvr2_i2c_client_describe(
+- cp,
+- PVR2_I2C_DETAIL_ALL,
+- buf,BUFSIZE);
+- }
+- trace_i2c("Probed: %.*s",cnt,buf);
+- if (handler_check(cp)) {
+- hdw->i2c_pend_types |=
+- PVR2_I2C_PEND_CLIENT;
+- }
+- cp->pend_mask = msk;
+- hdw->i2c_pend_mask |= msk;
+- hdw->i2c_pend_types |=
+- PVR2_I2C_PEND_REFRESH;
+- }
+- amask |= cp->ctl_mask;
+- }
+- hdw->i2c_active_mask = amask;
+- if (buf) kfree(buf);
+- }
+- if (hdw->i2c_pend_types & PVR2_I2C_PEND_STALE) {
+- /* Need to do one or more global updates. Arrange
+- for this to happen. */
+- unsigned long m2;
+- pvr2_trace(PVR2_TRACE_I2C_CORE,
+- "i2c: PEND_STALE (0x%lx)",
+- hdw->i2c_stale_mask);
+- hdw->i2c_pend_types &= ~PVR2_I2C_PEND_STALE;
+- list_for_each_entry(cp, &hdw->i2c_clients, list) {
+- m2 = hdw->i2c_stale_mask;
+- m2 &= cp->ctl_mask;
+- m2 &= ~cp->pend_mask;
+- if (m2) {
+- pvr2_trace(PVR2_TRACE_I2C_CORE,
+- "i2c: cp=%p setting 0x%lx",
+- cp,m2);
+- cp->pend_mask |= m2;
+- }
+- }
+- hdw->i2c_pend_mask |= hdw->i2c_stale_mask;
+- hdw->i2c_stale_mask = 0;
+- hdw->i2c_pend_types |= PVR2_I2C_PEND_REFRESH;
+- }
+- if (hdw->i2c_pend_types & PVR2_I2C_PEND_CLIENT) {
+- /* One or more client handlers are asking for an
+- update. Run through the list of known clients
+- and update each one. */
+- pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_CLIENT");
+- hdw->i2c_pend_types &= ~PVR2_I2C_PEND_CLIENT;
+- list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients,
+- list) {
+- if (!cp->handler) continue;
+- if (!cp->handler->func_table->update) continue;
+- pvr2_trace(PVR2_TRACE_I2C_CORE,
+- "i2c: cp=%p update",cp);
+- mutex_unlock(&hdw->i2c_list_lock);
+- cp->handler->func_table->update(
+- cp->handler->func_data);
+- mutex_lock(&hdw->i2c_list_lock);
+- /* If client's update function set some
+- additional pending bits, account for that
+- here. */
+- if (cp->pend_mask & ~hdw->i2c_pend_mask) {
+- hdw->i2c_pend_mask |= cp->pend_mask;
+- hdw->i2c_pend_types |=
+- PVR2_I2C_PEND_REFRESH;
+- }
+- }
+- }
+- if (hdw->i2c_pend_types & PVR2_I2C_PEND_REFRESH) {
+- const struct pvr2_i2c_op *opf;
+- unsigned long pm;
+- /* Some actual updates are pending. Walk through
+- each update type and perform it. */
+- pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_REFRESH"
+- " (0x%lx)",hdw->i2c_pend_mask);
+- hdw->i2c_pend_types &= ~PVR2_I2C_PEND_REFRESH;
+- pm = hdw->i2c_pend_mask;
+- hdw->i2c_pend_mask = 0;
+- for (idx = 0, msk = 1; pm; idx++, msk <<= 1) {
+- if (!(pm & msk)) continue;
+- pm &= ~msk;
+- list_for_each_entry(cp, &hdw->i2c_clients,
+- list) {
+- if (cp->pend_mask & msk) {
+- cp->pend_mask &= ~msk;
+- cp->recv_enable = !0;
+- } else {
+- cp->recv_enable = 0;
+- }
+- }
+- opf = pvr2_i2c_get_op(idx);
+- if (!opf) continue;
+- mutex_unlock(&hdw->i2c_list_lock);
+- opf->update(hdw);
+- mutex_lock(&hdw->i2c_list_lock);
+- }
+- }
+- pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync END");
+- } while (0); mutex_unlock(&hdw->i2c_list_lock);
+-}
+-
+-int pvr2_i2c_core_check_stale(struct pvr2_hdw *hdw)
+-{
+- unsigned long msk,sm,pm;
+- unsigned int idx;
+- const struct pvr2_i2c_op *opf;
+- struct pvr2_i2c_client *cp;
+- unsigned int pt = 0;
+-
+- pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale BEGIN");
+-
+- pm = hdw->i2c_active_mask;
+- sm = 0;
+- for (idx = 0, msk = 1; pm; idx++, msk <<= 1) {
+- if (!(msk & pm)) continue;
+- pm &= ~msk;
+- opf = pvr2_i2c_get_op(idx);
+- if (!opf) continue;
+- if (opf->check(hdw)) {
+- sm |= msk;
+- }
+- }
+- if (sm) pt |= PVR2_I2C_PEND_STALE;
+-
+- list_for_each_entry(cp, &hdw->i2c_clients, list)
+- if (handler_check(cp))
+- pt |= PVR2_I2C_PEND_CLIENT;
+-
+- if (pt) {
+- mutex_lock(&hdw->i2c_list_lock); do {
+- hdw->i2c_pend_types |= pt;
+- hdw->i2c_stale_mask |= sm;
+- hdw->i2c_pend_mask |= hdw->i2c_stale_mask;
+- } while (0); mutex_unlock(&hdw->i2c_list_lock);
+- }
+-
+- pvr2_trace(PVR2_TRACE_I2C_CORE,
+- "i2c: types=0x%x stale=0x%lx pend=0x%lx",
+- hdw->i2c_pend_types,
+- hdw->i2c_stale_mask,
+- hdw->i2c_pend_mask);
+- pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale END");
+-
+- return (hdw->i2c_pend_types & PVR2_I2C_PEND_ALL) != 0;
+-}
+-
+-static unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp,
+- unsigned int detail,
+- char *buf,unsigned int maxlen)
+-{
+- unsigned int ccnt,bcnt;
+- int spcfl = 0;
+- const struct pvr2_i2c_op *opf;
+-
+- ccnt = 0;
+- if (detail & PVR2_I2C_DETAIL_DEBUG) {
+- bcnt = scnprintf(buf,maxlen,
+- "ctxt=%p ctl_mask=0x%lx",
+- cp,cp->ctl_mask);
+- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+- spcfl = !0;
+- }
+- bcnt = scnprintf(buf,maxlen,
+- "%s%s @ 0x%x",
+- (spcfl ? " " : ""),
+- cp->client->name,
+- cp->client->addr);
+- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+- if ((detail & PVR2_I2C_DETAIL_HANDLER) &&
+- cp->handler && cp->handler->func_table->describe) {
+- bcnt = scnprintf(buf,maxlen," (");
+- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+- bcnt = cp->handler->func_table->describe(
+- cp->handler->func_data,buf,maxlen);
+- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+- bcnt = scnprintf(buf,maxlen,")");
+- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+- }
+- if ((detail & PVR2_I2C_DETAIL_CTLMASK) && cp->ctl_mask) {
+- unsigned int idx;
+- unsigned long msk,sm;
+-
+- bcnt = scnprintf(buf,maxlen," [");
+- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+- sm = 0;
+- spcfl = 0;
+- for (idx = 0, msk = 1; msk; idx++, msk <<= 1) {
+- if (!(cp->ctl_mask & msk)) continue;
+- opf = pvr2_i2c_get_op(idx);
+- if (opf) {
+- bcnt = scnprintf(buf,maxlen,"%s%s",
+- spcfl ? " " : "",
+- opf->name);
+- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+- spcfl = !0;
+- } else {
+- sm |= msk;
+- }
+- }
+- if (sm) {
+- bcnt = scnprintf(buf,maxlen,"%s%lx",
+- idx != 0 ? " " : "",sm);
+- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+- }
+- bcnt = scnprintf(buf,maxlen,"]");
+- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+- }
+- return ccnt;
+-}
+-
+-unsigned int pvr2_i2c_report(struct pvr2_hdw *hdw,
+- char *buf,unsigned int maxlen)
+-{
+- unsigned int ccnt,bcnt;
+- struct pvr2_i2c_client *cp;
+- ccnt = 0;
+- mutex_lock(&hdw->i2c_list_lock); do {
+- list_for_each_entry(cp, &hdw->i2c_clients, list) {
+- bcnt = pvr2_i2c_client_describe(
+- cp,
+- (PVR2_I2C_DETAIL_HANDLER|
+- PVR2_I2C_DETAIL_CTLMASK),
+- buf,maxlen);
+- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+- bcnt = scnprintf(buf,maxlen,"\n");
+- ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+- }
+- } while (0); mutex_unlock(&hdw->i2c_list_lock);
+- return ccnt;
+-}
+-
+ static int pvr2_i2c_attach_inform(struct i2c_client *client)
+ {
+- struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data);
+- struct pvr2_i2c_client *cp;
+- int fl = !(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL);
+- cp = kzalloc(sizeof(*cp),GFP_KERNEL);
+- trace_i2c("i2c_attach [client=%s @ 0x%x ctxt=%p]",
+- client->name,
+- client->addr,cp);
+- if (!cp) return -ENOMEM;
+- cp->hdw = hdw;
+- INIT_LIST_HEAD(&cp->list);
+- cp->client = client;
+- mutex_lock(&hdw->i2c_list_lock); do {
+- hdw->cropcap_stale = !0;
+- list_add_tail(&cp->list,&hdw->i2c_clients);
+- hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT;
+- } while (0); mutex_unlock(&hdw->i2c_list_lock);
+- if (fl) queue_work(hdw->workqueue,&hdw->worki2csync);
+ return 0;
+ }
+
+ static int pvr2_i2c_detach_inform(struct i2c_client *client)
+ {
+- struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data);
+- struct pvr2_i2c_client *cp, *ncp;
+- unsigned long amask = 0;
+- int foundfl = 0;
+- mutex_lock(&hdw->i2c_list_lock); do {
+- hdw->cropcap_stale = !0;
+- list_for_each_entry_safe(cp, ncp, &hdw->i2c_clients, list) {
+- if (cp->client == client) {
+- trace_i2c("pvr2_i2c_detach"
+- " [client=%s @ 0x%x ctxt=%p]",
+- client->name,
+- client->addr,cp);
+- if (cp->handler &&
+- cp->handler->func_table->detach) {
+- cp->handler->func_table->detach(
+- cp->handler->func_data);
+- }
+- list_del(&cp->list);
+- kfree(cp);
+- foundfl = !0;
+- continue;
+- }
+- amask |= cp->ctl_mask;
+- }
+- hdw->i2c_active_mask = amask;
+- } while (0); mutex_unlock(&hdw->i2c_list_lock);
+- if (!foundfl) {
+- trace_i2c("pvr2_i2c_detach [client=%s @ 0x%x ctxt=<unknown>]",
+- client->name,
+- client->addr);
+- }
+ return 0;
+ }
+
+@@ -942,7 +537,7 @@ static struct i2c_algorithm pvr2_i2c_algo_template = {
+
+ static struct i2c_adapter pvr2_i2c_adap_template = {
+ .owner = THIS_MODULE,
+- .class = I2C_CLASS_TV_ANALOG,
++ .class = 0,
+ .id = I2C_HW_B_BT848,
+ .client_register = pvr2_i2c_attach_inform,
+ .client_unregister = pvr2_i2c_detach_inform,
+@@ -1009,12 +604,8 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw)
+ hdw->i2c_adap.dev.parent = &hdw->usb_dev->dev;
+ hdw->i2c_adap.algo = &hdw->i2c_algo;
+ hdw->i2c_adap.algo_data = hdw;
+- hdw->i2c_pend_mask = 0;
+- hdw->i2c_stale_mask = 0;
+- hdw->i2c_active_mask = 0;
+- INIT_LIST_HEAD(&hdw->i2c_clients);
+- mutex_init(&hdw->i2c_list_lock);
+ hdw->i2c_linked = !0;
++ i2c_set_adapdata(&hdw->i2c_adap, &hdw->v4l2_dev);
+ i2c_add_adapter(&hdw->i2c_adap);
+ if (hdw->i2c_func[0x18] == i2c_24xxx_ir) {
+ /* Probe for a different type of IR receiver on this
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h
+index 6ef7a1c..6a75769 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h
++++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h
+@@ -20,68 +20,13 @@
+ #ifndef __PVRUSB2_I2C_CORE_H
+ #define __PVRUSB2_I2C_CORE_H
+
+-#include <linux/list.h>
+-#include <linux/i2c.h>
+-
+ struct pvr2_hdw;
+-struct pvr2_i2c_client;
+-struct pvr2_i2c_handler;
+-struct pvr2_i2c_handler_functions;
+-struct pvr2_i2c_op;
+-struct pvr2_i2c_op_functions;
+-
+-struct pvr2_i2c_client {
+- struct i2c_client *client;
+- struct pvr2_i2c_handler *handler;
+- struct list_head list;
+- struct pvr2_hdw *hdw;
+- int detected_flag;
+- int recv_enable;
+- unsigned long pend_mask;
+- unsigned long ctl_mask;
+- void (*status_poll)(struct pvr2_i2c_client *);
+-};
+-
+-struct pvr2_i2c_handler {
+- void *func_data;
+- const struct pvr2_i2c_handler_functions *func_table;
+-};
+-
+-struct pvr2_i2c_handler_functions {
+- void (*detach)(void *);
+- int (*check)(void *);
+- void (*update)(void *);
+- unsigned int (*describe)(void *,char *,unsigned int);
+-};
+-
+-struct pvr2_i2c_op {
+- int (*check)(struct pvr2_hdw *);
+- void (*update)(struct pvr2_hdw *);
+- const char *name;
+-};
+
+ void pvr2_i2c_core_init(struct pvr2_hdw *);
+ void pvr2_i2c_core_done(struct pvr2_hdw *);
+
+-int pvr2_i2c_client_cmd(struct pvr2_i2c_client *,unsigned int cmd,void *arg);
+-int pvr2_i2c_core_cmd(struct pvr2_hdw *,unsigned int cmd,void *arg);
+-
+-int pvr2_i2c_core_check_stale(struct pvr2_hdw *);
+-void pvr2_i2c_core_sync(struct pvr2_hdw *);
+-void pvr2_i2c_core_status_poll(struct pvr2_hdw *);
+-unsigned int pvr2_i2c_report(struct pvr2_hdw *,char *buf,unsigned int maxlen);
+-#define PVR2_I2C_DETAIL_DEBUG 0x0001
+-#define PVR2_I2C_DETAIL_HANDLER 0x0002
+-#define PVR2_I2C_DETAIL_CTLMASK 0x0004
+-#define PVR2_I2C_DETAIL_ALL (\
+- PVR2_I2C_DETAIL_DEBUG |\
+- PVR2_I2C_DETAIL_HANDLER |\
+- PVR2_I2C_DETAIL_CTLMASK)
+-
+-void pvr2_i2c_probe(struct pvr2_hdw *,struct pvr2_i2c_client *);
+-const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx);
+
+-#endif /* __PVRUSB2_I2C_CORE_H */
++#endif /* __PVRUSB2_I2C_ADAPTER_H */
+
+
+ /*
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-main.c b/drivers/media/video/pvrusb2/pvrusb2-main.c
+index 9b3c874..8689ddb 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-main.c
++++ b/drivers/media/video/pvrusb2/pvrusb2-main.c
+@@ -137,10 +137,10 @@ static int __init pvr_init(void)
+ ret = usb_register(&pvr_driver);
+
+ if (ret == 0)
+- printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
++ printk(KERN_INFO "pvrusb2: " DRIVER_VERSION ":"
+ DRIVER_DESC "\n");
+ if (pvrusb2_debug)
+- printk(KERN_INFO KBUILD_MODNAME ": Debug mask is %d (0x%x)\n",
++ printk(KERN_INFO "pvrusb2: Debug mask is %d (0x%x)\n",
+ pvrusb2_debug,pvrusb2_debug);
+
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_init complete");
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
+index e641cd9..e20ba1e 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
++++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
+@@ -627,16 +627,8 @@ static void class_dev_create(struct pvr2_sysfs *sfp,
+ pvr2_sysfs_trace("Creating class_dev id=%p",class_dev);
+
+ class_dev->class = &class_ptr->class;
+- if (pvr2_hdw_get_sn(sfp->channel.hdw)) {
+- dev_set_name(class_dev, "sn-%lu",
+- pvr2_hdw_get_sn(sfp->channel.hdw));
+- } else if (pvr2_hdw_get_unit_number(sfp->channel.hdw) >= 0) {
+- dev_set_name(class_dev, "unit-%c",
+- pvr2_hdw_get_unit_number(sfp->channel.hdw) + 'a');
+- } else {
+- kfree(class_dev);
+- return;
+- }
++ dev_set_name(class_dev, "%s",
++ pvr2_hdw_get_device_identifier(sfp->channel.hdw));
+
+ class_dev->parent = &usb_dev->dev;
+
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-tuner.c b/drivers/media/video/pvrusb2/pvrusb2-tuner.c
+deleted file mode 100644
+index 07775d1..0000000
+--- a/drivers/media/video/pvrusb2/pvrusb2-tuner.c
++++ /dev/null
+@@ -1,120 +0,0 @@
+-/*
+- *
+- *
+- * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+- * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+- *
+- * 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
+- *
+- * This program is distributed in the hope that it will be useful,
+- * but WITHOUT ANY WARRANTY; without even the implied warranty of
+- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+- * GNU General Public License for more details.
+- *
+- * You should have received a copy of the GNU General Public License
+- * along with this program; if not, write to the Free Software
+- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+- *
+- */
+-
+-#include "pvrusb2.h"
+-#include "pvrusb2-util.h"
+-#include "pvrusb2-tuner.h"
+-#include "pvrusb2-hdw-internal.h"
+-#include "pvrusb2-debug.h"
+-#include <linux/videodev2.h>
+-#include <media/tuner.h>
+-#include <media/v4l2-common.h>
+-
+-struct pvr2_tuner_handler {
+- struct pvr2_hdw *hdw;
+- struct pvr2_i2c_client *client;
+- struct pvr2_i2c_handler i2c_handler;
+- int type_update_fl;
+-};
+-
+-
+-static void set_type(struct pvr2_tuner_handler *ctxt)
+-{
+- struct pvr2_hdw *hdw = ctxt->hdw;
+- struct tuner_setup setup;
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c tuner set_type(%d)",hdw->tuner_type);
+- if (((int)(hdw->tuner_type)) < 0) return;
+-
+- setup.addr = ADDR_UNSET;
+- setup.type = hdw->tuner_type;
+- setup.mode_mask = T_RADIO | T_ANALOG_TV;
+- /* We may really want mode_mask to be T_ANALOG_TV for now */
+- pvr2_i2c_client_cmd(ctxt->client,TUNER_SET_TYPE_ADDR,&setup);
+- ctxt->type_update_fl = 0;
+-}
+-
+-
+-static int tuner_check(struct pvr2_tuner_handler *ctxt)
+-{
+- struct pvr2_hdw *hdw = ctxt->hdw;
+- if (hdw->tuner_updated) ctxt->type_update_fl = !0;
+- return ctxt->type_update_fl != 0;
+-}
+-
+-
+-static void tuner_update(struct pvr2_tuner_handler *ctxt)
+-{
+- if (ctxt->type_update_fl) set_type(ctxt);
+-}
+-
+-
+-static void pvr2_tuner_detach(struct pvr2_tuner_handler *ctxt)
+-{
+- ctxt->client->handler = NULL;
+- kfree(ctxt);
+-}
+-
+-
+-static unsigned int pvr2_tuner_describe(struct pvr2_tuner_handler *ctxt,char *buf,unsigned int cnt)
+-{
+- return scnprintf(buf,cnt,"handler: pvrusb2-tuner");
+-}
+-
+-
+-static const struct pvr2_i2c_handler_functions tuner_funcs = {
+- .detach = (void (*)(void *))pvr2_tuner_detach,
+- .check = (int (*)(void *))tuner_check,
+- .update = (void (*)(void *))tuner_update,
+- .describe = (unsigned int (*)(void *,char *,unsigned int))pvr2_tuner_describe,
+-};
+-
+-
+-int pvr2_i2c_tuner_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp)
+-{
+- struct pvr2_tuner_handler *ctxt;
+- if (cp->handler) return 0;
+-
+- ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL);
+- if (!ctxt) return 0;
+-
+- ctxt->i2c_handler.func_data = ctxt;
+- ctxt->i2c_handler.func_table = &tuner_funcs;
+- ctxt->type_update_fl = !0;
+- ctxt->client = cp;
+- ctxt->hdw = hdw;
+- cp->handler = &ctxt->i2c_handler;
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x tuner handler set up",
+- cp->client->addr);
+- return !0;
+-}
+-
+-
+-
+-
+-/*
+- Stuff for Emacs to see, in order to encourage consistent editing style:
+- *** Local Variables: ***
+- *** mode: c ***
+- *** fill-column: 70 ***
+- *** tab-width: 8 ***
+- *** c-basic-offset: 8 ***
+- *** End: ***
+- */
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-tuner.h b/drivers/media/video/pvrusb2/pvrusb2-tuner.h
+deleted file mode 100644
+index ef4afaf..0000000
+--- a/drivers/media/video/pvrusb2/pvrusb2-tuner.h
++++ /dev/null
+@@ -1,37 +0,0 @@
+-/*
+- *
+- *
+- * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+- *
+- * This program is free software; you can redistribute it and/or modify
+- * it under the terms of the GNU General Public License as published by
+- * the Free Software Foundation; either version 2 of the License
+- *
+- * 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
+- *
+- */
+-#ifndef __PVRUSB2_TUNER_H
+-#define __PVRUSB2_TUNER_H
+-
+-#include "pvrusb2-i2c-core.h"
+-
+-int pvr2_i2c_tuner_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
+-
+-#endif /* __PVRUSB2_TUNER_H */
+-
+-/*
+- Stuff for Emacs to see, in order to encourage consistent editing style:
+- *** Local Variables: ***
+- *** mode: c ***
+- *** fill-column: 70 ***
+- *** tab-width: 8 ***
+- *** c-basic-offset: 8 ***
+- *** End: ***
+- */
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+index 878fd52..9e0f2b0 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
++++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+@@ -91,7 +91,7 @@ static struct v4l2_capability pvr_capability ={
+ .card = "Hauppauge WinTV pvr-usb2",
+ .bus_info = "usb",
+ .version = KERNEL_VERSION(0,8,0),
+- .capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE |
++ .capabilities = (V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_TUNER | V4L2_CAP_AUDIO | V4L2_CAP_RADIO |
+ V4L2_CAP_READWRITE),
+ .reserved = {0,0,0,0}
+@@ -952,10 +952,6 @@ static long pvr2_v4l2_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+ {
+
+-/* Temporary hack : use ivtv api until a v4l2 one is available. */
+-#define IVTV_IOC_G_CODEC 0xFFEE7703
+-#define IVTV_IOC_S_CODEC 0xFFEE7704
+- if (cmd == IVTV_IOC_G_CODEC || cmd == IVTV_IOC_S_CODEC) return 0;
+ return video_usercopy(file, cmd, arg, pvr2_v4l2_do_ioctl);
+ }
+
+@@ -1268,8 +1264,9 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
+ dip->minor_type = pvr2_v4l_type_video;
+ nr_ptr = video_nr;
+ if (!dip->stream) {
+- err("Failed to set up pvrusb2 v4l video dev"
+- " due to missing stream instance");
++ pr_err(KBUILD_MODNAME
++ ": Failed to set up pvrusb2 v4l video dev"
++ " due to missing stream instance\n");
+ return;
+ }
+ break;
+@@ -1286,8 +1283,8 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
+ break;
+ default:
+ /* Bail out (this should be impossible) */
+- err("Failed to set up pvrusb2 v4l dev"
+- " due to unrecognized config");
++ pr_err(KBUILD_MODNAME ": Failed to set up pvrusb2 v4l dev"
++ " due to unrecognized config\n");
+ return;
+ }
+
+@@ -1303,7 +1300,8 @@ static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
+ dip->v4l_type, mindevnum) < 0) &&
+ (video_register_device(&dip->devbase,
+ dip->v4l_type, -1) < 0)) {
+- err("Failed to register pvrusb2 v4l device");
++ pr_err(KBUILD_MODNAME
++ ": Failed to register pvrusb2 v4l device\n");
+ }
+
+ printk(KERN_INFO "pvrusb2: registered device %s%u [%s]\n",
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
+index 4059648..b3862f5 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
++++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
+@@ -28,7 +28,7 @@
+ */
+
+ #include "pvrusb2-video-v4l.h"
+-#include "pvrusb2-i2c-cmd-v4l2.h"
++
+
+
+ #include "pvrusb2-hdw-internal.h"
+@@ -39,15 +39,6 @@
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+
+-struct pvr2_v4l_decoder {
+- struct pvr2_i2c_handler handler;
+- struct pvr2_decoder_ctrl ctrl;
+- struct pvr2_i2c_client *client;
+- struct pvr2_hdw *hdw;
+- unsigned long stale_mask;
+-};
+-
+-
+ struct routing_scheme {
+ const int *def;
+ unsigned int cnt;
+@@ -63,190 +54,51 @@ static const int routing_scheme0[] = {
+ [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2,
+ };
+
++static const int routing_scheme1[] = {
++ [PVR2_CVAL_INPUT_TV] = SAA7115_COMPOSITE4,
++ [PVR2_CVAL_INPUT_RADIO] = SAA7115_COMPOSITE5,
++ [PVR2_CVAL_INPUT_COMPOSITE] = SAA7115_COMPOSITE3,
++ [PVR2_CVAL_INPUT_SVIDEO] = SAA7115_SVIDEO2, /* or SVIDEO0, it seems */
++};
++
+ static const struct routing_scheme routing_schemes[] = {
+ [PVR2_ROUTING_SCHEME_HAUPPAUGE] = {
+ .def = routing_scheme0,
+ .cnt = ARRAY_SIZE(routing_scheme0),
+ },
++ [PVR2_ROUTING_SCHEME_ONAIR] = {
++ .def = routing_scheme1,
++ .cnt = ARRAY_SIZE(routing_scheme1),
++ },
+ };
+
+-static void set_input(struct pvr2_v4l_decoder *ctxt)
+-{
+- struct pvr2_hdw *hdw = ctxt->hdw;
+- struct v4l2_routing route;
+- const struct routing_scheme *sp;
+- unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
+-
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_input(%d)",hdw->input_val);
+-
+- if ((sid < ARRAY_SIZE(routing_schemes)) &&
+- ((sp = routing_schemes + sid) != NULL) &&
+- (hdw->input_val >= 0) &&
+- (hdw->input_val < sp->cnt)) {
+- route.input = sp->def[hdw->input_val];
+- } else {
+- pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+- "*** WARNING *** i2c v4l2 set_input:"
+- " Invalid routing scheme (%u) and/or input (%d)",
+- sid,hdw->input_val);
+- return;
+- }
+-
+- route.output = 0;
+- pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route);
+-}
+-
+-
+-static int check_input(struct pvr2_v4l_decoder *ctxt)
+-{
+- struct pvr2_hdw *hdw = ctxt->hdw;
+- return hdw->input_dirty != 0;
+-}
+-
+-
+-static void set_audio(struct pvr2_v4l_decoder *ctxt)
+-{
+- u32 val;
+- struct pvr2_hdw *hdw = ctxt->hdw;
+-
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_audio %d",
+- hdw->srate_val);
+- switch (hdw->srate_val) {
+- default:
+- case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000:
+- val = 48000;
+- break;
+- case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100:
+- val = 44100;
+- break;
+- case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000:
+- val = 32000;
+- break;
+- }
+- pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val);
+-}
+-
+-
+-static int check_audio(struct pvr2_v4l_decoder *ctxt)
+-{
+- struct pvr2_hdw *hdw = ctxt->hdw;
+- return hdw->srate_dirty != 0;
+-}
+-
+-
+-struct pvr2_v4l_decoder_ops {
+- void (*update)(struct pvr2_v4l_decoder *);
+- int (*check)(struct pvr2_v4l_decoder *);
+-};
+-
+-
+-static const struct pvr2_v4l_decoder_ops decoder_ops[] = {
+- { .update = set_input, .check = check_input},
+- { .update = set_audio, .check = check_audio},
+-};
+-
+-
+-static void decoder_detach(struct pvr2_v4l_decoder *ctxt)
++void pvr2_saa7115_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd)
+ {
+- ctxt->client->handler = NULL;
+- pvr2_hdw_set_decoder(ctxt->hdw,NULL);
+- kfree(ctxt);
+-}
+-
+-
+-static int decoder_check(struct pvr2_v4l_decoder *ctxt)
+-{
+- unsigned long msk;
+- unsigned int idx;
+-
+- for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) {
+- msk = 1 << idx;
+- if (ctxt->stale_mask & msk) continue;
+- if (decoder_ops[idx].check(ctxt)) {
+- ctxt->stale_mask |= msk;
++ if (hdw->input_dirty || hdw->force_dirty) {
++ struct v4l2_routing route;
++ const struct routing_scheme *sp;
++ unsigned int sid = hdw->hdw_desc->signal_routing_scheme;
++ pvr2_trace(PVR2_TRACE_CHIPS, "subdev v4l2 set_input(%d)",
++ hdw->input_val);
++ if ((sid < ARRAY_SIZE(routing_schemes)) &&
++ ((sp = routing_schemes + sid) != NULL) &&
++ (hdw->input_val >= 0) &&
++ (hdw->input_val < sp->cnt)) {
++ route.input = sp->def[hdw->input_val];
++ } else {
++ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
++ "*** WARNING *** subdev v4l2 set_input:"
++ " Invalid routing scheme (%u)"
++ " and/or input (%d)",
++ sid, hdw->input_val);
++ return;
+ }
++ route.output = 0;
++ sd->ops->video->s_routing(sd, &route);
+ }
+- return ctxt->stale_mask != 0;
+-}
+-
+-
+-static void decoder_update(struct pvr2_v4l_decoder *ctxt)
+-{
+- unsigned long msk;
+- unsigned int idx;
+-
+- for (idx = 0; idx < ARRAY_SIZE(decoder_ops); idx++) {
+- msk = 1 << idx;
+- if (!(ctxt->stale_mask & msk)) continue;
+- ctxt->stale_mask &= ~msk;
+- decoder_ops[idx].update(ctxt);
+- }
+-}
+-
+-
+-static int decoder_detect(struct pvr2_i2c_client *cp)
+-{
+- /* Attempt to query the decoder - let's see if it will answer */
+- struct v4l2_tuner vt;
+- int ret;
+-
+- memset(&vt,0,sizeof(vt));
+- ret = pvr2_i2c_client_cmd(cp,VIDIOC_G_TUNER,&vt);
+- return ret == 0; /* Return true if it answered */
+-}
+-
+-
+-static void decoder_enable(struct pvr2_v4l_decoder *ctxt,int fl)
+-{
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 decoder_enable(%d)",fl);
+- pvr2_v4l2_cmd_stream(ctxt->client,fl);
+-}
+-
+-
+-static unsigned int decoder_describe(struct pvr2_v4l_decoder *ctxt,char *buf,unsigned int cnt)
+-{
+- return scnprintf(buf,cnt,"handler: pvrusb2-video-v4l");
+-}
+-
+-
+-static const struct pvr2_i2c_handler_functions hfuncs = {
+- .detach = (void (*)(void *))decoder_detach,
+- .check = (int (*)(void *))decoder_check,
+- .update = (void (*)(void *))decoder_update,
+- .describe = (unsigned int (*)(void *,char *,unsigned int))decoder_describe,
+-};
+-
+-
+-int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *hdw,
+- struct pvr2_i2c_client *cp)
+-{
+- struct pvr2_v4l_decoder *ctxt;
+-
+- if (hdw->decoder_ctrl) return 0;
+- if (cp->handler) return 0;
+- if (!decoder_detect(cp)) return 0;
+-
+- ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL);
+- if (!ctxt) return 0;
+-
+- ctxt->handler.func_data = ctxt;
+- ctxt->handler.func_table = &hfuncs;
+- ctxt->ctrl.ctxt = ctxt;
+- ctxt->ctrl.detach = (void (*)(void *))decoder_detach;
+- ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable;
+- ctxt->client = cp;
+- ctxt->hdw = hdw;
+- ctxt->stale_mask = (1 << ARRAY_SIZE(decoder_ops)) - 1;
+- pvr2_hdw_set_decoder(hdw,&ctxt->ctrl);
+- cp->handler = &ctxt->handler;
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x saa711x V4L2 handler set up",
+- cp->client->addr);
+- return !0;
+ }
+
+
+-
+-
+ /*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h
+index 4ff5b89..3b0bd5d 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h
++++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h
+@@ -32,11 +32,8 @@
+ */
+
+
+-
+-#include "pvrusb2-i2c-core.h"
+-
+-int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
+-
++#include "pvrusb2-hdw-internal.h"
++void pvr2_saa7115_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *);
+
+ #endif /* __PVRUSB2_VIDEO_V4L_H */
+
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/drivers/media/video/pvrusb2/pvrusb2-wm8775.c
+index f6fcf0a..1670aa4 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-wm8775.c
++++ b/drivers/media/video/pvrusb2/pvrusb2-wm8775.c
+@@ -27,7 +27,6 @@
+ */
+
+ #include "pvrusb2-wm8775.h"
+-#include "pvrusb2-i2c-cmd-v4l2.h"
+
+
+ #include "pvrusb2-hdw-internal.h"
+@@ -37,128 +36,31 @@
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+
+-struct pvr2_v4l_wm8775 {
+- struct pvr2_i2c_handler handler;
+- struct pvr2_i2c_client *client;
+- struct pvr2_hdw *hdw;
+- unsigned long stale_mask;
+-};
+-
+-
+-static void set_input(struct pvr2_v4l_wm8775 *ctxt)
+-{
+- struct v4l2_routing route;
+- struct pvr2_hdw *hdw = ctxt->hdw;
+-
+- memset(&route,0,sizeof(route));
+-
+- switch(hdw->input_val) {
+- case PVR2_CVAL_INPUT_RADIO:
+- route.input = 1;
+- break;
+- default:
+- /* All other cases just use the second input */
+- route.input = 2;
+- break;
+- }
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c wm8775 set_input(val=%d route=0x%x)",
+- hdw->input_val,route.input);
+-
+- pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route);
+-}
+-
+-static int check_input(struct pvr2_v4l_wm8775 *ctxt)
+-{
+- struct pvr2_hdw *hdw = ctxt->hdw;
+- return hdw->input_dirty != 0;
+-}
+-
+-
+-struct pvr2_v4l_wm8775_ops {
+- void (*update)(struct pvr2_v4l_wm8775 *);
+- int (*check)(struct pvr2_v4l_wm8775 *);
+-};
+-
+-
+-static const struct pvr2_v4l_wm8775_ops wm8775_ops[] = {
+- { .update = set_input, .check = check_input},
+-};
+-
+-
+-static unsigned int wm8775_describe(struct pvr2_v4l_wm8775 *ctxt,
+- char *buf,unsigned int cnt)
+-{
+- return scnprintf(buf,cnt,"handler: pvrusb2-wm8775");
+-}
+-
+-
+-static void wm8775_detach(struct pvr2_v4l_wm8775 *ctxt)
++void pvr2_wm8775_subdev_update(struct pvr2_hdw *hdw, struct v4l2_subdev *sd)
+ {
+- ctxt->client->handler = NULL;
+- kfree(ctxt);
+-}
+-
+-
+-static int wm8775_check(struct pvr2_v4l_wm8775 *ctxt)
+-{
+- unsigned long msk;
+- unsigned int idx;
+-
+- for (idx = 0; idx < ARRAY_SIZE(wm8775_ops); idx++) {
+- msk = 1 << idx;
+- if (ctxt->stale_mask & msk) continue;
+- if (wm8775_ops[idx].check(ctxt)) {
+- ctxt->stale_mask |= msk;
++ if (hdw->input_dirty || hdw->force_dirty) {
++ struct v4l2_routing route;
++
++ memset(&route, 0, sizeof(route));
++
++ switch (hdw->input_val) {
++ case PVR2_CVAL_INPUT_RADIO:
++ route.input = 1;
++ break;
++ default:
++ /* All other cases just use the second input */
++ route.input = 2;
++ break;
+ }
+- }
+- return ctxt->stale_mask != 0;
+-}
+-
+-
+-static void wm8775_update(struct pvr2_v4l_wm8775 *ctxt)
+-{
+- unsigned long msk;
+- unsigned int idx;
++ pvr2_trace(PVR2_TRACE_CHIPS, "subdev wm8775"
++ " set_input(val=%d route=0x%x)",
++ hdw->input_val, route.input);
+
+- for (idx = 0; idx < ARRAY_SIZE(wm8775_ops); idx++) {
+- msk = 1 << idx;
+- if (!(ctxt->stale_mask & msk)) continue;
+- ctxt->stale_mask &= ~msk;
+- wm8775_ops[idx].update(ctxt);
++ sd->ops->audio->s_routing(sd, &route);
+ }
+ }
+
+
+-static const struct pvr2_i2c_handler_functions hfuncs = {
+- .detach = (void (*)(void *))wm8775_detach,
+- .check = (int (*)(void *))wm8775_check,
+- .update = (void (*)(void *))wm8775_update,
+- .describe = (unsigned int (*)(void *,char *,unsigned int))wm8775_describe,
+-};
+-
+-
+-int pvr2_i2c_wm8775_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp)
+-{
+- struct pvr2_v4l_wm8775 *ctxt;
+-
+- if (cp->handler) return 0;
+-
+- ctxt = kzalloc(sizeof(*ctxt),GFP_KERNEL);
+- if (!ctxt) return 0;
+-
+- ctxt->handler.func_data = ctxt;
+- ctxt->handler.func_table = &hfuncs;
+- ctxt->client = cp;
+- ctxt->hdw = hdw;
+- ctxt->stale_mask = (1 << ARRAY_SIZE(wm8775_ops)) - 1;
+- cp->handler = &ctxt->handler;
+- pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x wm8775 V4L2 handler set up",
+- cp->client->addr);
+- return !0;
+-}
+-
+-
+-
+
+ /*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.h b/drivers/media/video/pvrusb2/pvrusb2-wm8775.h
+index 8070909..0577bc7 100644
+--- a/drivers/media/video/pvrusb2/pvrusb2-wm8775.h
++++ b/drivers/media/video/pvrusb2/pvrusb2-wm8775.h
+@@ -34,9 +34,9 @@
+
+
+
+-#include "pvrusb2-i2c-core.h"
++#include "pvrusb2-hdw-internal.h"
+
+-int pvr2_i2c_wm8775_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
++void pvr2_wm8775_subdev_update(struct pvr2_hdw *, struct v4l2_subdev *sd);
+
+
+ #endif /* __PVRUSB2_WM8775_H */
+diff --git a/drivers/media/video/pwc/Kconfig b/drivers/media/video/pwc/Kconfig
+index 7298cf2..8b9f0aa 100644
+--- a/drivers/media/video/pwc/Kconfig
++++ b/drivers/media/video/pwc/Kconfig
+@@ -35,3 +35,13 @@ config USB_PWC_DEBUG
+ Say Y here in order to have the pwc driver generate verbose debugging
+ messages.
+ A special module options 'trace' is used to control the verbosity.
++
++config USB_PWC_INPUT_EVDEV
++ bool "USB Philips Cameras input events device support"
++ default y
++ depends on USB_PWC && INPUT
++ ---help---
++ This option makes USB Philips cameras register the snapshot button as
++ an input device to report button events.
++
++ If you are in doubt, say Y.
+diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
+index 0d81018..7c542ca 100644
+--- a/drivers/media/video/pwc/pwc-if.c
++++ b/drivers/media/video/pwc/pwc-if.c
+@@ -53,6 +53,7 @@
+ - Xavier Roche: QuickCam Pro 4000 ID
+ - Jens Knudsen: QuickCam Zoom ID
+ - J. Debert: QuickCam for Notebooks ID
++ - Pham Thanh Nam: webcam snapshot button as an event input device
+ */
+
+ #include <linux/errno.h>
+@@ -61,6 +62,9 @@
+ #include <linux/module.h>
+ #include <linux/poll.h>
+ #include <linux/slab.h>
++#ifdef CONFIG_USB_PWC_INPUT_EVDEV
++#include <linux/usb/input.h>
++#endif
+ #include <linux/vmalloc.h>
+ #include <asm/io.h>
+
+@@ -586,6 +590,23 @@ static void pwc_frame_dumped(struct pwc_device *pdev)
+ pdev->vframe_count);
+ }
+
++static void pwc_snapshot_button(struct pwc_device *pdev, int down)
++{
++ if (down) {
++ PWC_TRACE("Snapshot button pressed.\n");
++ pdev->snapshot_button_status = 1;
++ } else {
++ PWC_TRACE("Snapshot button released.\n");
++ }
++
++#ifdef CONFIG_USB_PWC_INPUT_EVDEV
++ if (pdev->button_dev) {
++ input_report_key(pdev->button_dev, BTN_0, down);
++ input_sync(pdev->button_dev);
++ }
++#endif
++}
++
+ static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_buf *fbuf)
+ {
+ int awake = 0;
+@@ -603,13 +624,7 @@ static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_
+ pdev->vframes_error++;
+ }
+ if ((ptr[0] ^ pdev->vmirror) & 0x01) {
+- if (ptr[0] & 0x01) {
+- pdev->snapshot_button_status = 1;
+- PWC_TRACE("Snapshot button pressed.\n");
+- }
+- else {
+- PWC_TRACE("Snapshot button released.\n");
+- }
++ pwc_snapshot_button(pdev, ptr[0] & 0x01);
+ }
+ if ((ptr[0] ^ pdev->vmirror) & 0x02) {
+ if (ptr[0] & 0x02)
+@@ -633,12 +648,7 @@ static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_
+ else if (pdev->type == 740 || pdev->type == 720) {
+ unsigned char *ptr = (unsigned char *)fbuf->data;
+ if ((ptr[0] ^ pdev->vmirror) & 0x01) {
+- if (ptr[0] & 0x01) {
+- pdev->snapshot_button_status = 1;
+- PWC_TRACE("Snapshot button pressed.\n");
+- }
+- else
+- PWC_TRACE("Snapshot button released.\n");
++ pwc_snapshot_button(pdev, ptr[0] & 0x01);
+ }
+ pdev->vmirror = ptr[0] & 0x03;
+ }
+@@ -1115,6 +1125,7 @@ static int pwc_video_open(struct file *file)
+ }
+
+ mutex_lock(&pdev->modlock);
++ pwc_construct(pdev); /* set min/max sizes correct */
+ if (!pdev->usb_init) {
+ PWC_DEBUG_OPEN("Doing first time initialization.\n");
+ pdev->usb_init = 1;
+@@ -1139,7 +1150,6 @@ static int pwc_video_open(struct file *file)
+ if (pwc_set_leds(pdev, led_on, led_off) < 0)
+ PWC_DEBUG_OPEN("Failed to set LED on/off time.\n");
+
+- pwc_construct(pdev); /* set min/max sizes correct */
+
+ /* So far, so good. Allocate memory. */
+ i = pwc_allocate_buffers(pdev);
+@@ -1216,6 +1226,15 @@ static void pwc_cleanup(struct pwc_device *pdev)
+ {
+ pwc_remove_sysfs_files(pdev->vdev);
+ video_unregister_device(pdev->vdev);
++
++#ifdef CONFIG_USB_PWC_INPUT_EVDEV
++ if (pdev->button_dev) {
++ input_unregister_device(pdev->button_dev);
++ input_free_device(pdev->button_dev);
++ kfree(pdev->button_dev->phys);
++ pdev->button_dev = NULL;
++ }
++#endif
+ }
+
+ /* Note that all cleanup is done in the reverse order as in _open */
+@@ -1483,6 +1502,9 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
+ int features = 0;
+ int video_nr = -1; /* default: use next available device */
+ char serial_number[30], *name;
++#ifdef CONFIG_USB_PWC_INPUT_EVDEV
++ char *phys = NULL;
++#endif
+
+ vendor_id = le16_to_cpu(udev->descriptor.idVendor);
+ product_id = le16_to_cpu(udev->descriptor.idProduct);
+@@ -1807,6 +1829,35 @@ static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id
+ pwc_set_leds(pdev, 0, 0);
+ pwc_camera_power(pdev, 0);
+
++#ifdef CONFIG_USB_PWC_INPUT_EVDEV
++ /* register webcam snapshot button input device */
++ pdev->button_dev = input_allocate_device();
++ if (!pdev->button_dev) {
++ PWC_ERROR("Err, insufficient memory for webcam snapshot button device.");
++ return -ENOMEM;
++ }
++
++ pdev->button_dev->name = "PWC snapshot button";
++ phys = kasprintf(GFP_KERNEL,"usb-%s-%s", pdev->udev->bus->bus_name, pdev->udev->devpath);
++ if (!phys) {
++ input_free_device(pdev->button_dev);
++ return -ENOMEM;
++ }
++ pdev->button_dev->phys = phys;
++ usb_to_input_id(pdev->udev, &pdev->button_dev->id);
++ pdev->button_dev->dev.parent = &pdev->udev->dev;
++ pdev->button_dev->evbit[0] = BIT_MASK(EV_KEY);
++ pdev->button_dev->keybit[BIT_WORD(BTN_0)] = BIT_MASK(BTN_0);
++
++ rc = input_register_device(pdev->button_dev);
++ if (rc) {
++ input_free_device(pdev->button_dev);
++ kfree(pdev->button_dev->phys);
++ pdev->button_dev = NULL;
++ return rc;
++ }
++#endif
++
+ return 0;
+
+ err_unreg:
+diff --git a/drivers/media/video/pwc/pwc.h b/drivers/media/video/pwc/pwc.h
+index 01411fb..0be6f81 100644
+--- a/drivers/media/video/pwc/pwc.h
++++ b/drivers/media/video/pwc/pwc.h
+@@ -37,6 +37,9 @@
+ #include <linux/videodev.h>
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-ioctl.h>
++#ifdef CONFIG_USB_PWC_INPUT_EVDEV
++#include <linux/input.h>
++#endif
+
+ #include "pwc-uncompress.h"
+ #include <media/pwc-ioctl.h>
+@@ -255,6 +258,9 @@ struct pwc_device
+ int pan_angle; /* in degrees * 100 */
+ int tilt_angle; /* absolute angle; 0,0 is home position */
+ int snapshot_button_status; /* set to 1 when the user push the button, reset to 0 when this value is read */
++#ifdef CONFIG_USB_PWC_INPUT_EVDEV
++ struct input_dev *button_dev; /* webcam snapshot button input */
++#endif
+
+ /*** Misc. data ***/
+ wait_queue_head_t frameq; /* When waiting for a frame to finish... */
+diff --git a/drivers/media/video/pxa_camera.c b/drivers/media/video/pxa_camera.c
+index 07c334f..a30747f 100644
+--- a/drivers/media/video/pxa_camera.c
++++ b/drivers/media/video/pxa_camera.c
+@@ -879,6 +879,7 @@ static int test_platform_param(struct pxa_camera_dev *pcdev,
+ SOCAM_HSYNC_ACTIVE_LOW |
+ SOCAM_VSYNC_ACTIVE_HIGH |
+ SOCAM_VSYNC_ACTIVE_LOW |
++ SOCAM_DATA_ACTIVE_HIGH |
+ SOCAM_PCLK_SAMPLE_RISING |
+ SOCAM_PCLK_SAMPLE_FALLING;
+
+@@ -1150,8 +1151,43 @@ static int pxa_camera_get_formats(struct soc_camera_device *icd, int idx,
+ return formats;
+ }
+
++static int pxa_camera_set_crop(struct soc_camera_device *icd,
++ struct v4l2_rect *rect)
++{
++ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
++ struct pxa_camera_dev *pcdev = ici->priv;
++ struct soc_camera_sense sense = {
++ .master_clock = pcdev->mclk,
++ .pixel_clock_max = pcdev->ciclk / 4,
++ };
++ int ret;
++
++ /* If PCLK is used to latch data from the sensor, check sense */
++ if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
++ icd->sense = &sense;
++
++ ret = icd->ops->set_crop(icd, rect);
++
++ icd->sense = NULL;
++
++ if (ret < 0) {
++ dev_warn(&ici->dev, "Failed to crop to %ux%u@%u:%u\n",
++ rect->width, rect->height, rect->left, rect->top);
++ } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {
++ if (sense.pixel_clock > sense.pixel_clock_max) {
++ dev_err(&ici->dev,
++ "pixel clock %lu set by the camera too high!",
++ sense.pixel_clock);
++ return -EIO;
++ }
++ recalculate_fifo_timeout(pcdev, sense.pixel_clock);
++ }
++
++ return ret;
++}
++
+ static int pxa_camera_set_fmt(struct soc_camera_device *icd,
+- __u32 pixfmt, struct v4l2_rect *rect)
++ struct v4l2_format *f)
+ {
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct pxa_camera_dev *pcdev = ici->priv;
+@@ -1161,35 +1197,30 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd,
+ .master_clock = pcdev->mclk,
+ .pixel_clock_max = pcdev->ciclk / 4,
+ };
++ struct v4l2_pix_format *pix = &f->fmt.pix;
++ struct v4l2_format cam_f = *f;
+ int ret;
+
+- if (pixfmt) {
+- xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+- if (!xlate) {
+- dev_warn(&ici->dev, "Format %x not found\n", pixfmt);
+- return -EINVAL;
+- }
+-
+- cam_fmt = xlate->cam_fmt;
++ xlate = soc_camera_xlate_by_fourcc(icd, pix->pixelformat);
++ if (!xlate) {
++ dev_warn(&ici->dev, "Format %x not found\n", pix->pixelformat);
++ return -EINVAL;
+ }
+
++ cam_fmt = xlate->cam_fmt;
++
+ /* If PCLK is used to latch data from the sensor, check sense */
+ if (pcdev->platform_flags & PXA_CAMERA_PCLK_EN)
+ icd->sense = &sense;
+
+- switch (pixfmt) {
+- case 0: /* Only geometry change */
+- ret = icd->ops->set_fmt(icd, pixfmt, rect);
+- break;
+- default:
+- ret = icd->ops->set_fmt(icd, cam_fmt->fourcc, rect);
+- }
++ cam_f.fmt.pix.pixelformat = cam_fmt->fourcc;
++ ret = icd->ops->set_fmt(icd, &cam_f);
+
+ icd->sense = NULL;
+
+ if (ret < 0) {
+ dev_warn(&ici->dev, "Failed to configure for format %x\n",
+- pixfmt);
++ pix->pixelformat);
+ } else if (sense.flags & SOCAM_SENSE_PCLK_CHANGED) {
+ if (sense.pixel_clock > sense.pixel_clock_max) {
+ dev_err(&ici->dev,
+@@ -1200,7 +1231,7 @@ static int pxa_camera_set_fmt(struct soc_camera_device *icd,
+ recalculate_fifo_timeout(pcdev, sense.pixel_clock);
+ }
+
+- if (pixfmt && !ret) {
++ if (!ret) {
+ icd->buswidth = xlate->buswidth;
+ icd->current_fmt = xlate->host_fmt;
+ }
+@@ -1364,6 +1395,7 @@ static struct soc_camera_host_ops pxa_soc_camera_host_ops = {
+ .remove = pxa_camera_remove_device,
+ .suspend = pxa_camera_suspend,
+ .resume = pxa_camera_resume,
++ .set_crop = pxa_camera_set_crop,
+ .get_formats = pxa_camera_get_formats,
+ .set_fmt = pxa_camera_set_fmt,
+ .try_fmt = pxa_camera_try_fmt,
+diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c
+index 13f85ad..b5be633 100644
+--- a/drivers/media/video/s2255drv.c
++++ b/drivers/media/video/s2255drv.c
+@@ -336,14 +336,19 @@ static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req,
+ u16 index, u16 value, void *buf,
+ s32 buf_len, int bOut);
+
++/* dev_err macro with driver name */
++#define S2255_DRIVER_NAME "s2255"
++#define s2255_dev_err(dev, fmt, arg...) \
++ dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg)
++
+ #define dprintk(level, fmt, arg...) \
+ do { \
+ if (*s2255_debug >= (level)) { \
+- printk(KERN_DEBUG "s2255: " fmt, ##arg); \
++ printk(KERN_DEBUG S2255_DRIVER_NAME \
++ ": " fmt, ##arg); \
+ } \
+ } while (0)
+
+-
+ static struct usb_driver s2255_driver;
+
+
+@@ -528,14 +533,14 @@ static void s2255_fwchunk_complete(struct urb *urb)
+ int len;
+ dprintk(100, "udev %p urb %p", udev, urb);
+ if (urb->status) {
+- dev_err(&udev->dev, "URB failed with status %d", urb->status);
++ dev_err(&udev->dev, "URB failed with status %d\n", urb->status);
+ atomic_set(&data->fw_state, S2255_FW_FAILED);
+ /* wake up anything waiting for the firmware */
+ wake_up(&data->wait_fw);
+ return;
+ }
+ if (data->fw_urb == NULL) {
+- dev_err(&udev->dev, "s2255 disconnected\n");
++ s2255_dev_err(&udev->dev, "disconnected\n");
+ atomic_set(&data->fw_state, S2255_FW_FAILED);
+ /* wake up anything waiting for the firmware */
+ wake_up(&data->wait_fw);
+@@ -841,8 +846,7 @@ static int vidioc_querycap(struct file *file, void *priv,
+ struct s2255_dev *dev = fh->dev;
+ strlcpy(cap->driver, "s2255", sizeof(cap->driver));
+ strlcpy(cap->card, "s2255", sizeof(cap->card));
+- strlcpy(cap->bus_info, dev_name(&dev->udev->dev),
+- sizeof(cap->bus_info));
++ usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info));
+ cap->version = S2255_VERSION;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING;
+ return 0;
+@@ -1278,7 +1282,7 @@ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+ }
+
+ if (!res_get(dev, fh)) {
+- dev_err(&dev->udev->dev, "s2255: stream busy\n");
++ s2255_dev_err(&dev->udev->dev, "stream busy\n");
+ return -EBUSY;
+ }
+
+@@ -1545,7 +1549,8 @@ static int s2255_open(struct file *file)
+
+ switch (atomic_read(&dev->fw_data->fw_state)) {
+ case S2255_FW_FAILED:
+- err("2255 firmware load failed. retrying.\n");
++ s2255_dev_err(&dev->udev->dev,
++ "firmware load failed. retrying.\n");
+ s2255_fwload_start(dev, 1);
+ wait_event_timeout(dev->fw_data->wait_fw,
+ ((atomic_read(&dev->fw_data->fw_state)
+@@ -2173,7 +2178,8 @@ static int s2255_board_init(struct s2255_dev *dev)
+
+ printk(KERN_INFO "2255 usb firmware version %d \n", fw_ver);
+ if (fw_ver < CUR_USB_FWVER)
+- err("usb firmware not up to date %d\n", fw_ver);
++ dev_err(&dev->udev->dev,
++ "usb firmware not up to date %d\n", fw_ver);
+
+ for (j = 0; j < MAX_CHANNELS; j++) {
+ dev->b_acquire[j] = 0;
+@@ -2228,13 +2234,13 @@ static void read_pipe_completion(struct urb *purb)
+ dprintk(100, "read pipe completion %p, status %d\n", purb,
+ purb->status);
+ if (pipe_info == NULL) {
+- err("no context !");
++ dev_err(&purb->dev->dev, "no context!\n");
+ return;
+ }
+
+ dev = pipe_info->dev;
+ if (dev == NULL) {
+- err("no context !");
++ dev_err(&purb->dev->dev, "no context!\n");
+ return;
+ }
+ status = purb->status;
+@@ -2286,7 +2292,7 @@ static int s2255_start_readpipe(struct s2255_dev *dev)
+ pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!pipe_info->stream_urb) {
+ dev_err(&dev->udev->dev,
+- "ReadStream: Unable to alloc URB");
++ "ReadStream: Unable to alloc URB\n");
+ return -ENOMEM;
+ }
+ /* transfer buffer allocated in board_init */
+@@ -2391,7 +2397,7 @@ static void s2255_stop_readpipe(struct s2255_dev *dev)
+ int j;
+
+ if (dev == NULL) {
+- err("s2255: invalid device");
++ s2255_dev_err(&dev->udev->dev, "invalid device\n");
+ return;
+ }
+ dprintk(4, "stop read pipe\n");
+@@ -2453,7 +2459,7 @@ static int s2255_probe(struct usb_interface *interface,
+ /* allocate memory for our device state and initialize it to zero */
+ dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL);
+ if (dev == NULL) {
+- err("s2255: out of memory");
++ s2255_dev_err(&interface->dev, "out of memory\n");
+ goto error;
+ }
+
+@@ -2487,7 +2493,7 @@ static int s2255_probe(struct usb_interface *interface,
+ }
+
+ if (!dev->read_endpoint) {
+- dev_err(&interface->dev, "Could not find bulk-in endpoint");
++ dev_err(&interface->dev, "Could not find bulk-in endpoint\n");
+ goto error;
+ }
+
+@@ -2583,7 +2589,7 @@ static void s2255_disconnect(struct usb_interface *interface)
+ }
+
+ static struct usb_driver s2255_driver = {
+- .name = "s2255",
++ .name = S2255_DRIVER_NAME,
+ .probe = s2255_probe,
+ .disconnect = s2255_disconnect,
+ .id_table = s2255_table,
+@@ -2597,7 +2603,8 @@ static int __init usb_s2255_init(void)
+ result = usb_register(&s2255_driver);
+
+ if (result)
+- err("usb_register failed. Error number %d", result);
++ pr_err(KBUILD_MODNAME
++ ": usb_register failed. Error number %d\n", result);
+
+ dprintk(2, "s2255_init: done\n");
+ return result;
+diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c
+index e637e44..da47b2f 100644
+--- a/drivers/media/video/saa5246a.c
++++ b/drivers/media/video/saa5246a.c
+@@ -46,10 +46,11 @@
+ #include <linux/smp_lock.h>
+ #include <linux/mutex.h>
+ #include <linux/videotext.h>
+-#include <linux/videodev.h>
+-#include <media/v4l2-common.h>
++#include <linux/videodev2.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-chip-ident.h>
+ #include <media/v4l2-ioctl.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <media/v4l2-i2c-drv.h>
+
+ MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>");
+ MODULE_DESCRIPTION("Philips SAA5246A, SAA5281 Teletext decoder driver");
+@@ -388,13 +389,19 @@ MODULE_LICENSE("GPL");
+
+ struct saa5246a_device
+ {
++ struct v4l2_subdev sd;
++ struct video_device *vdev;
+ u8 pgbuf[NUM_DAUS][VTX_VIRTUALSIZE];
+ int is_searching[NUM_DAUS];
+- struct i2c_client *client;
+ unsigned long in_use;
+ struct mutex lock;
+ };
+
++static inline struct saa5246a_device *to_dev(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct saa5246a_device, sd);
++}
++
+ static struct video_device saa_template; /* Declared near bottom */
+
+ /*
+@@ -403,12 +410,13 @@ static struct video_device saa_template; /* Declared near bottom */
+
+ static int i2c_sendbuf(struct saa5246a_device *t, int reg, int count, u8 *data)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(&t->sd);
+ char buf[64];
+
+ buf[0] = reg;
+ memcpy(buf+1, data, count);
+
+- if(i2c_master_send(t->client, buf, count+1)==count+1)
++ if (i2c_master_send(client, buf, count + 1) == count + 1)
+ return 0;
+ return -1;
+ }
+@@ -436,7 +444,9 @@ static int i2c_senddata(struct saa5246a_device *t, ...)
+ */
+ static int i2c_getdata(struct saa5246a_device *t, int count, u8 *buf)
+ {
+- if(i2c_master_recv(t->client, buf, count)!=count)
++ struct i2c_client *client = v4l2_get_subdevdata(&t->sd);
++
++ if (i2c_master_recv(client, buf, count) != count)
+ return -1;
+ return 0;
+ }
+@@ -961,9 +971,6 @@ static int saa5246a_open(struct file *file)
+ {
+ struct saa5246a_device *t = video_drvdata(file);
+
+- if (t->client == NULL)
+- return -ENODEV;
+-
+ if (test_and_set_bit(0, &t->in_use))
+ return -EBUSY;
+
+@@ -1033,18 +1040,29 @@ static struct video_device saa_template =
+ .minor = -1,
+ };
+
+-/* Addresses to scan */
+-static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END };
++static int saa5246a_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA5246A, 0);
++}
++
++static const struct v4l2_subdev_core_ops saa5246a_core_ops = {
++ .g_chip_ident = saa5246a_g_chip_ident,
++};
++
++static const struct v4l2_subdev_ops saa5246a_ops = {
++ .core = &saa5246a_core_ops,
++};
+
+-I2C_CLIENT_INSMOD;
+
+ static int saa5246a_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+ {
+ int pgbuf;
+ int err;
+- struct video_device *vd;
+ struct saa5246a_device *t;
++ struct v4l2_subdev *sd;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+@@ -1053,40 +1071,43 @@ static int saa5246a_probe(struct i2c_client *client,
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
+ if (t == NULL)
+ return -ENOMEM;
++ sd = &t->sd;
++ v4l2_i2c_subdev_init(sd, client, &saa5246a_ops);
+ mutex_init(&t->lock);
+
+ /* Now create a video4linux device */
+- vd = video_device_alloc();
+- if (vd == NULL) {
++ t->vdev = video_device_alloc();
++ if (t->vdev == NULL) {
+ kfree(t);
+ return -ENOMEM;
+ }
+- i2c_set_clientdata(client, vd);
+- memcpy(vd, &saa_template, sizeof(*vd));
++ memcpy(t->vdev, &saa_template, sizeof(*t->vdev));
+
+ for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) {
+ memset(t->pgbuf[pgbuf], ' ', sizeof(t->pgbuf[0]));
+ t->is_searching[pgbuf] = false;
+ }
+- video_set_drvdata(vd, t);
++ video_set_drvdata(t->vdev, t);
+
+ /* Register it */
+- err = video_register_device(vd, VFL_TYPE_VTX, -1);
++ err = video_register_device(t->vdev, VFL_TYPE_VTX, -1);
+ if (err < 0) {
+ kfree(t);
+- video_device_release(vd);
++ video_device_release(t->vdev);
++ t->vdev = NULL;
+ return err;
+ }
+- t->client = client;
+ return 0;
+ }
+
+ static int saa5246a_remove(struct i2c_client *client)
+ {
+- struct video_device *vd = i2c_get_clientdata(client);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct saa5246a_device *t = to_dev(sd);
+
+- video_unregister_device(vd);
+- kfree(video_get_drvdata(vd));
++ video_unregister_device(t->vdev);
++ v4l2_device_unregister_subdev(sd);
++ kfree(t);
+ return 0;
+ }
+
+@@ -1098,7 +1119,6 @@ MODULE_DEVICE_TABLE(i2c, saa5246a_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "saa5246a",
+- .driverid = I2C_DRIVERID_SAA5249,
+ .probe = saa5246a_probe,
+ .remove = saa5246a_remove,
+ .id_table = saa5246a_id,
+diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c
+index e297651..48b27fe 100644
+--- a/drivers/media/video/saa5249.c
++++ b/drivers/media/video/saa5249.c
+@@ -50,15 +50,17 @@
+ #include <linux/mutex.h>
+ #include <linux/delay.h>
+ #include <linux/videotext.h>
+-#include <linux/videodev.h>
+-#include <media/v4l2-common.h>
++#include <linux/videodev2.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-chip-ident.h>
+ #include <media/v4l2-ioctl.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <media/v4l2-i2c-drv.h>
+
+ MODULE_AUTHOR("Michael Geng <linux@MichaelGeng.de>");
+ MODULE_DESCRIPTION("Philips SAA5249 Teletext decoder driver");
+ MODULE_LICENSE("GPL");
+
++
+ #define VTX_VER_MAJ 1
+ #define VTX_VER_MIN 8
+
+@@ -95,17 +97,23 @@ typedef struct {
+
+ struct saa5249_device
+ {
++ struct v4l2_subdev sd;
++ struct video_device *vdev;
+ vdau_t vdau[NUM_DAUS]; /* Data for virtual DAUs (the 5249 only has one */
+ /* real DAU, so we have to simulate some more) */
+ int vtx_use_count;
+ int is_searching[NUM_DAUS];
+ int disp_mode;
+ int virtual_mode;
+- struct i2c_client *client;
+ unsigned long in_use;
+ struct mutex lock;
+ };
+
++static inline struct saa5249_device *to_dev(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct saa5249_device, sd);
++}
++
+
+ #define CCTWR 34 /* IC write/read-address of vtx-chip */
+ #define CCTRD 35
+@@ -147,12 +155,13 @@ static void jdelay(unsigned long delay)
+
+ static int i2c_sendbuf(struct saa5249_device *t, int reg, int count, u8 *data)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(&t->sd);
+ char buf[64];
+
+ buf[0] = reg;
+ memcpy(buf+1, data, count);
+
+- if (i2c_master_send(t->client, buf, count + 1) == count + 1)
++ if (i2c_master_send(client, buf, count + 1) == count + 1)
+ return 0;
+ return -1;
+ }
+@@ -180,7 +189,9 @@ static int i2c_senddata(struct saa5249_device *t, ...)
+
+ static int i2c_getdata(struct saa5249_device *t, int count, u8 *buf)
+ {
+- if(i2c_master_recv(t->client, buf, count)!=count)
++ struct i2c_client *client = v4l2_get_subdevdata(&t->sd);
++
++ if (i2c_master_recv(client, buf, count) != count)
+ return -1;
+ return 0;
+ }
+@@ -497,9 +508,6 @@ static int saa5249_open(struct file *file)
+ struct saa5249_device *t = video_drvdata(file);
+ int pgbuf;
+
+- if (t->client == NULL)
+- return -ENODEV;
+-
+ if (test_and_set_bit(0, &t->in_use))
+ return -EBUSY;
+
+@@ -553,18 +561,28 @@ static struct video_device saa_template =
+ .release = video_device_release,
+ };
+
+-/* Addresses to scan */
+-static unsigned short normal_i2c[] = { 0x22 >> 1, I2C_CLIENT_END };
++static int saa5249_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA5249, 0);
++}
+
+-I2C_CLIENT_INSMOD;
++static const struct v4l2_subdev_core_ops saa5249_core_ops = {
++ .g_chip_ident = saa5249_g_chip_ident,
++};
++
++static const struct v4l2_subdev_ops saa5249_ops = {
++ .core = &saa5249_core_ops,
++};
+
+ static int saa5249_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+ {
+ int pgbuf;
+ int err;
+- struct video_device *vd;
+ struct saa5249_device *t;
++ struct v4l2_subdev *sd;
+
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+@@ -573,16 +591,17 @@ static int saa5249_probe(struct i2c_client *client,
+ t = kzalloc(sizeof(*t), GFP_KERNEL);
+ if (t == NULL)
+ return -ENOMEM;
++ sd = &t->sd;
++ v4l2_i2c_subdev_init(sd, client, &saa5249_ops);
+ mutex_init(&t->lock);
+
+ /* Now create a video4linux device */
+- vd = kmalloc(sizeof(struct video_device), GFP_KERNEL);
+- if (vd == NULL) {
++ t->vdev = video_device_alloc();
++ if (t->vdev == NULL) {
+ kfree(client);
+ return -ENOMEM;
+ }
+- i2c_set_clientdata(client, vd);
+- memcpy(vd, &saa_template, sizeof(*vd));
++ memcpy(t->vdev, &saa_template, sizeof(*t->vdev));
+
+ for (pgbuf = 0; pgbuf < NUM_DAUS; pgbuf++) {
+ memset(t->vdau[pgbuf].pgbuf, ' ', sizeof(t->vdau[0].pgbuf));
+@@ -593,26 +612,27 @@ static int saa5249_probe(struct i2c_client *client,
+ t->vdau[pgbuf].stopped = true;
+ t->is_searching[pgbuf] = false;
+ }
+- video_set_drvdata(vd, t);
++ video_set_drvdata(t->vdev, t);
+
+ /* Register it */
+- err = video_register_device(vd, VFL_TYPE_VTX, -1);
++ err = video_register_device(t->vdev, VFL_TYPE_VTX, -1);
+ if (err < 0) {
+ kfree(t);
+- kfree(vd);
++ video_device_release(t->vdev);
++ t->vdev = NULL;
+ return err;
+ }
+- t->client = client;
+ return 0;
+ }
+
+ static int saa5249_remove(struct i2c_client *client)
+ {
+- struct video_device *vd = i2c_get_clientdata(client);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct saa5249_device *t = to_dev(sd);
+
+- video_unregister_device(vd);
+- kfree(video_get_drvdata(vd));
+- kfree(vd);
++ video_unregister_device(t->vdev);
++ v4l2_device_unregister_subdev(sd);
++ kfree(t);
+ return 0;
+ }
+
+@@ -624,7 +644,6 @@ MODULE_DEVICE_TABLE(i2c, saa5249_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "saa5249",
+- .driverid = I2C_DRIVERID_SAA5249,
+ .probe = saa5249_probe,
+ .remove = saa5249_remove,
+ .id_table = saa5249_id,
+diff --git a/drivers/media/video/saa6588.c b/drivers/media/video/saa6588.c
+index f050242..c25e81a 100644
+--- a/drivers/media/video/saa6588.c
++++ b/drivers/media/video/saa6588.c
+@@ -23,7 +23,7 @@
+ #include <linux/kernel.h>
+ #include <linux/i2c.h>
+ #include <linux/types.h>
+-#include <linux/videodev.h>
++#include <linux/videodev2.h>
+ #include <linux/init.h>
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+@@ -32,15 +32,10 @@
+ #include <asm/uaccess.h>
+
+ #include <media/rds.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/v4l2-i2c-drv.h>
+
+-/* Addresses to scan */
+-static unsigned short normal_i2c[] = {
+- 0x20 >> 1,
+- 0x22 >> 1,
+- I2C_CLIENT_END,
+-};
+-
+-I2C_CLIENT_INSMOD;
+
+ /* insmod options */
+ static unsigned int debug;
+@@ -72,9 +67,8 @@ MODULE_LICENSE("GPL");
+ #define dprintk if (debug) printk
+
+ struct saa6588 {
+- struct i2c_client client;
+- struct work_struct work;
+- struct timer_list timer;
++ struct v4l2_subdev sd;
++ struct delayed_work work;
+ spinlock_t lock;
+ unsigned char *buffer;
+ unsigned int buf_size;
+@@ -86,8 +80,10 @@ struct saa6588 {
+ int data_available_for_read;
+ };
+
+-static struct i2c_driver driver;
+-static struct i2c_client client_template;
++static inline struct saa6588 *to_saa6588(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct saa6588, sd);
++}
+
+ /* ---------------------------------------------------------------------- */
+
+@@ -258,6 +254,7 @@ static void block_to_buf(struct saa6588 *s, unsigned char *blockbuf)
+
+ static void saa6588_i2c_poll(struct saa6588 *s)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(&s->sd);
+ unsigned long flags;
+ unsigned char tmpbuf[6];
+ unsigned char blocknum;
+@@ -265,7 +262,7 @@ static void saa6588_i2c_poll(struct saa6588 *s)
+
+ /* Although we only need 3 bytes, we have to read at least 6.
+ SAA6588 returns garbage otherwise */
+- if (6 != i2c_master_recv(&s->client, &tmpbuf[0], 6)) {
++ if (6 != i2c_master_recv(client, &tmpbuf[0], 6)) {
+ if (debug > 1)
+ dprintk(PREFIX "read error!\n");
+ return;
+@@ -316,23 +313,17 @@ static void saa6588_i2c_poll(struct saa6588 *s)
+ wake_up_interruptible(&s->read_queue);
+ }
+
+-static void saa6588_timer(unsigned long data)
+-{
+- struct saa6588 *s = (struct saa6588 *)data;
+-
+- schedule_work(&s->work);
+-}
+-
+ static void saa6588_work(struct work_struct *work)
+ {
+- struct saa6588 *s = container_of(work, struct saa6588, work);
++ struct saa6588 *s = container_of(work, struct saa6588, work.work);
+
+ saa6588_i2c_poll(s);
+- mod_timer(&s->timer, jiffies + msecs_to_jiffies(20));
++ schedule_delayed_work(&s->work, msecs_to_jiffies(20));
+ }
+
+ static int saa6588_configure(struct saa6588 *s)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(&s->sd);
+ unsigned char buf[3];
+ int rc;
+
+@@ -380,7 +371,8 @@ static int saa6588_configure(struct saa6588 *s)
+ dprintk(PREFIX "writing: 0w=0x%02x 1w=0x%02x 2w=0x%02x\n",
+ buf[0], buf[1], buf[2]);
+
+- if (3 != (rc = i2c_master_send(&s->client, buf, 3)))
++ rc = i2c_master_send(client, buf, 3);
++ if (rc != 3)
+ printk(PREFIX "i2c i/o error: rc == %d (should be 3)\n", rc);
+
+ return 0;
+@@ -388,70 +380,10 @@ static int saa6588_configure(struct saa6588 *s)
+
+ /* ---------------------------------------------------------------------- */
+
+-static int saa6588_attach(struct i2c_adapter *adap, int addr, int kind)
+-{
+- struct saa6588 *s;
+- client_template.adapter = adap;
+- client_template.addr = addr;
+-
+- printk(PREFIX "chip found @ 0x%x\n", addr << 1);
+-
+- if (NULL == (s = kmalloc(sizeof(*s), GFP_KERNEL)))
+- return -ENOMEM;
+-
+- s->buf_size = bufblocks * 3;
+-
+- if (NULL == (s->buffer = kmalloc(s->buf_size, GFP_KERNEL))) {
+- kfree(s);
+- return -ENOMEM;
+- }
+- spin_lock_init(&s->lock);
+- s->client = client_template;
+- s->block_count = 0;
+- s->wr_index = 0;
+- s->rd_index = 0;
+- s->last_blocknum = 0xff;
+- init_waitqueue_head(&s->read_queue);
+- s->data_available_for_read = 0;
+- i2c_set_clientdata(&s->client, s);
+- i2c_attach_client(&s->client);
+-
+- saa6588_configure(s);
+-
+- /* start polling via eventd */
+- INIT_WORK(&s->work, saa6588_work);
+- init_timer(&s->timer);
+- s->timer.function = saa6588_timer;
+- s->timer.data = (unsigned long)s;
+- schedule_work(&s->work);
+- return 0;
+-}
+-
+-static int saa6588_probe(struct i2c_adapter *adap)
+-{
+- if (adap->class & I2C_CLASS_TV_ANALOG)
+- return i2c_probe(adap, &addr_data, saa6588_attach);
+- return 0;
+-}
+-
+-static int saa6588_detach(struct i2c_client *client)
+-{
+- struct saa6588 *s = i2c_get_clientdata(client);
+-
+- del_timer_sync(&s->timer);
+- flush_scheduled_work();
+-
+- i2c_detach_client(client);
+- kfree(s->buffer);
+- kfree(s);
+- return 0;
+-}
+-
+-static int saa6588_command(struct i2c_client *client, unsigned int cmd,
+- void *arg)
++static long saa6588_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+ {
+- struct saa6588 *s = i2c_get_clientdata(client);
+- struct rds_command *a = (struct rds_command *)arg;
++ struct saa6588 *s = to_saa6588(sd);
++ struct rds_command *a = arg;
+
+ switch (cmd) {
+ /* --- open() for /dev/radio --- */
+@@ -479,45 +411,94 @@ static int saa6588_command(struct i2c_client *client, unsigned int cmd,
+
+ default:
+ /* nothing */
+- break;
++ return -ENOIOCTLCMD;
+ }
+ return 0;
+ }
+
++static int saa6588_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA6588, 0);
++}
++
+ /* ----------------------------------------------------------------------- */
+
+-static struct i2c_driver driver = {
+- .driver = {
+- .name = "saa6588",
+- },
+- .id = -1, /* FIXME */
+- .attach_adapter = saa6588_probe,
+- .detach_client = saa6588_detach,
+- .command = saa6588_command,
++static const struct v4l2_subdev_core_ops saa6588_core_ops = {
++ .g_chip_ident = saa6588_g_chip_ident,
++ .ioctl = saa6588_ioctl,
+ };
+
+-static struct i2c_client client_template = {
+- .name = "saa6588",
+- .driver = &driver,
++static const struct v4l2_subdev_ops saa6588_ops = {
++ .core = &saa6588_core_ops,
+ };
+
+-static int __init saa6588_init_module(void)
++/* ---------------------------------------------------------------------- */
++
++static int saa6588_probe(struct i2c_client *client,
++ const struct i2c_device_id *id)
+ {
+- return i2c_add_driver(&driver);
++ struct saa6588 *s;
++ struct v4l2_subdev *sd;
++
++ v4l_info(client, "saa6588 found @ 0x%x (%s)\n",
++ client->addr << 1, client->adapter->name);
++
++ s = kzalloc(sizeof(*s), GFP_KERNEL);
++ if (s == NULL)
++ return -ENOMEM;
++
++ s->buf_size = bufblocks * 3;
++
++ s->buffer = kmalloc(s->buf_size, GFP_KERNEL);
++ if (s->buffer == NULL) {
++ kfree(s);
++ return -ENOMEM;
++ }
++ sd = &s->sd;
++ v4l2_i2c_subdev_init(sd, client, &saa6588_ops);
++ spin_lock_init(&s->lock);
++ s->block_count = 0;
++ s->wr_index = 0;
++ s->rd_index = 0;
++ s->last_blocknum = 0xff;
++ init_waitqueue_head(&s->read_queue);
++ s->data_available_for_read = 0;
++
++ saa6588_configure(s);
++
++ /* start polling via eventd */
++ INIT_DELAYED_WORK(&s->work, saa6588_work);
++ schedule_delayed_work(&s->work, 0);
++ return 0;
+ }
+
+-static void __exit saa6588_cleanup_module(void)
++static int saa6588_remove(struct i2c_client *client)
+ {
+- i2c_del_driver(&driver);
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct saa6588 *s = to_saa6588(sd);
++
++ v4l2_device_unregister_subdev(sd);
++
++ cancel_delayed_work_sync(&s->work);
++
++ kfree(s->buffer);
++ kfree(s);
++ return 0;
+ }
+
+-module_init(saa6588_init_module);
+-module_exit(saa6588_cleanup_module);
++/* ----------------------------------------------------------------------- */
+
+-/*
+- * Overrides for Emacs so that we follow Linus's tabbing style.
+- * ---------------------------------------------------------------------------
+- * Local variables:
+- * c-basic-offset: 8
+- * End:
+- */
++static const struct i2c_device_id saa6588_id[] = {
++ { "saa6588", 0 },
++ { }
++};
++MODULE_DEVICE_TABLE(i2c, saa6588_id);
++
++static struct v4l2_i2c_driver_data v4l2_i2c_data = {
++ .name = "saa6588",
++ .probe = saa6588_probe,
++ .remove = saa6588_remove,
++ .id_table = saa6588_id,
++};
+diff --git a/drivers/media/video/saa7110.c b/drivers/media/video/saa7110.c
+index 3786069..df4e08d 100644
+--- a/drivers/media/video/saa7110.c
++++ b/drivers/media/video/saa7110.c
+@@ -33,15 +33,16 @@
+ #include <linux/wait.h>
+ #include <asm/uaccess.h>
+ #include <linux/i2c.h>
+-#include <linux/videodev.h>
+-#include <linux/video_decoder.h>
+-#include <media/v4l2-common.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <linux/videodev2.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/v4l2-i2c-drv.h>
+
+ MODULE_DESCRIPTION("Philips SAA7110 video decoder driver");
+ MODULE_AUTHOR("Pauline Middelink");
+ MODULE_LICENSE("GPL");
+
++
+ static int debug;
+ module_param(debug, int, 0);
+ MODULE_PARM_DESC(debug, "Debug level (0-1)");
+@@ -52,9 +53,10 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
+ #define SAA7110_NR_REG 0x35
+
+ struct saa7110 {
++ struct v4l2_subdev sd;
+ u8 reg[SAA7110_NR_REG];
+
+- int norm;
++ v4l2_std_id norm;
+ int input;
+ int enable;
+ int bright;
+@@ -65,20 +67,28 @@ struct saa7110 {
+ wait_queue_head_t wq;
+ };
+
++static inline struct saa7110 *to_saa7110(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct saa7110, sd);
++}
++
+ /* ----------------------------------------------------------------------- */
+ /* I2C support functions */
+ /* ----------------------------------------------------------------------- */
+
+-static int saa7110_write(struct i2c_client *client, u8 reg, u8 value)
++static int saa7110_write(struct v4l2_subdev *sd, u8 reg, u8 value)
+ {
+- struct saa7110 *decoder = i2c_get_clientdata(client);
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct saa7110 *decoder = to_saa7110(sd);
+
+ decoder->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+ }
+
+-static int saa7110_write_block(struct i2c_client *client, const u8 *data, unsigned int len)
++static int saa7110_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct saa7110 *decoder = to_saa7110(sd);
+ int ret = -1;
+ u8 reg = *data; /* first register to write to */
+
+@@ -89,15 +99,13 @@ static int saa7110_write_block(struct i2c_client *client, const u8 *data, unsign
+ /* the saa7110 has an autoincrement function, use it if
+ * the adapter understands raw I2C */
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+- struct saa7110 *decoder = i2c_get_clientdata(client);
+-
+ ret = i2c_master_send(client, data, len);
+
+ /* Cache the written data */
+ memcpy(decoder->reg + reg, data + 1, len - 1);
+ } else {
+ for (++data, --len; len; len--) {
+- ret = saa7110_write(client, reg++, *data++);
++ ret = saa7110_write(sd, reg++, *data++);
+ if (ret < 0)
+ break;
+ }
+@@ -106,8 +114,10 @@ static int saa7110_write_block(struct i2c_client *client, const u8 *data, unsign
+ return ret;
+ }
+
+-static inline int saa7110_read(struct i2c_client *client)
++static inline int saa7110_read(struct v4l2_subdev *sd)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
+ return i2c_smbus_read_byte(client);
+ }
+
+@@ -115,11 +125,11 @@ static inline int saa7110_read(struct i2c_client *client)
+ /* SAA7110 functions */
+ /* ----------------------------------------------------------------------- */
+
+-#define FRESP_06H_COMPST 0x03 //0x13
+-#define FRESP_06H_SVIDEO 0x83 //0xC0
++#define FRESP_06H_COMPST 0x03 /*0x13*/
++#define FRESP_06H_SVIDEO 0x83 /*0xC0*/
+
+
+-static int saa7110_selmux(struct i2c_client *client, int chan)
++static int saa7110_selmux(struct v4l2_subdev *sd, int chan)
+ {
+ static const unsigned char modes[9][8] = {
+ /* mode 0 */
+@@ -150,17 +160,17 @@ static int saa7110_selmux(struct i2c_client *client, int chan)
+ {FRESP_06H_SVIDEO, 0x3C, 0x27, 0xC1, 0x23,
+ 0x44, 0x75, 0x21}
+ };
+- struct saa7110 *decoder = i2c_get_clientdata(client);
++ struct saa7110 *decoder = to_saa7110(sd);
+ const unsigned char *ptr = modes[chan];
+
+- saa7110_write(client, 0x06, ptr[0]); /* Luminance control */
+- saa7110_write(client, 0x20, ptr[1]); /* Analog Control #1 */
+- saa7110_write(client, 0x21, ptr[2]); /* Analog Control #2 */
+- saa7110_write(client, 0x22, ptr[3]); /* Mixer Control #1 */
+- saa7110_write(client, 0x2C, ptr[4]); /* Mixer Control #2 */
+- saa7110_write(client, 0x30, ptr[5]); /* ADCs gain control */
+- saa7110_write(client, 0x31, ptr[6]); /* Mixer Control #3 */
+- saa7110_write(client, 0x21, ptr[7]); /* Analog Control #2 */
++ saa7110_write(sd, 0x06, ptr[0]); /* Luminance control */
++ saa7110_write(sd, 0x20, ptr[1]); /* Analog Control #1 */
++ saa7110_write(sd, 0x21, ptr[2]); /* Analog Control #2 */
++ saa7110_write(sd, 0x22, ptr[3]); /* Mixer Control #1 */
++ saa7110_write(sd, 0x2C, ptr[4]); /* Mixer Control #2 */
++ saa7110_write(sd, 0x30, ptr[5]); /* ADCs gain control */
++ saa7110_write(sd, 0x31, ptr[6]); /* Mixer Control #3 */
++ saa7110_write(sd, 0x21, ptr[7]); /* Analog Control #2 */
+ decoder->input = chan;
+
+ return 0;
+@@ -176,246 +186,260 @@ static const unsigned char initseq[1 + SAA7110_NR_REG] = {
+ /* 0x30 */ 0x44, 0x71, 0x02, 0x8C, 0x02
+ };
+
+-static int determine_norm(struct i2c_client *client)
++static v4l2_std_id determine_norm(struct v4l2_subdev *sd)
+ {
+ DEFINE_WAIT(wait);
+- struct saa7110 *decoder = i2c_get_clientdata(client);
++ struct saa7110 *decoder = to_saa7110(sd);
+ int status;
+
+ /* mode changed, start automatic detection */
+- saa7110_write_block(client, initseq, sizeof(initseq));
+- saa7110_selmux(client, decoder->input);
++ saa7110_write_block(sd, initseq, sizeof(initseq));
++ saa7110_selmux(sd, decoder->input);
+ prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE);
+ schedule_timeout(msecs_to_jiffies(250));
+ finish_wait(&decoder->wq, &wait);
+- status = saa7110_read(client);
++ status = saa7110_read(sd);
+ if (status & 0x40) {
+- v4l_dbg(1, debug, client, "status=0x%02x (no signal)\n", status);
+- return decoder->norm; // no change
++ v4l2_dbg(1, debug, sd, "status=0x%02x (no signal)\n", status);
++ return decoder->norm; /* no change*/
+ }
+ if ((status & 3) == 0) {
+- saa7110_write(client, 0x06, 0x83);
++ saa7110_write(sd, 0x06, 0x83);
+ if (status & 0x20) {
+- v4l_dbg(1, debug, client, "status=0x%02x (NTSC/no color)\n", status);
+- //saa7110_write(client,0x2E,0x81);
+- return VIDEO_MODE_NTSC;
++ v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC/no color)\n", status);
++ /*saa7110_write(sd,0x2E,0x81);*/
++ return V4L2_STD_NTSC;
+ }
+- v4l_dbg(1, debug, client, "status=0x%02x (PAL/no color)\n", status);
+- //saa7110_write(client,0x2E,0x9A);
+- return VIDEO_MODE_PAL;
++ v4l2_dbg(1, debug, sd, "status=0x%02x (PAL/no color)\n", status);
++ /*saa7110_write(sd,0x2E,0x9A);*/
++ return V4L2_STD_PAL;
+ }
+- //saa7110_write(client,0x06,0x03);
++ /*saa7110_write(sd,0x06,0x03);*/
+ if (status & 0x20) { /* 60Hz */
+- v4l_dbg(1, debug, client, "status=0x%02x (NTSC)\n", status);
+- saa7110_write(client, 0x0D, 0x86);
+- saa7110_write(client, 0x0F, 0x50);
+- saa7110_write(client, 0x11, 0x2C);
+- //saa7110_write(client,0x2E,0x81);
+- return VIDEO_MODE_NTSC;
++ v4l2_dbg(1, debug, sd, "status=0x%02x (NTSC)\n", status);
++ saa7110_write(sd, 0x0D, 0x86);
++ saa7110_write(sd, 0x0F, 0x50);
++ saa7110_write(sd, 0x11, 0x2C);
++ /*saa7110_write(sd,0x2E,0x81);*/
++ return V4L2_STD_NTSC;
+ }
+
+ /* 50Hz -> PAL/SECAM */
+- saa7110_write(client, 0x0D, 0x86);
+- saa7110_write(client, 0x0F, 0x10);
+- saa7110_write(client, 0x11, 0x59);
+- //saa7110_write(client,0x2E,0x9A);
++ saa7110_write(sd, 0x0D, 0x86);
++ saa7110_write(sd, 0x0F, 0x10);
++ saa7110_write(sd, 0x11, 0x59);
++ /*saa7110_write(sd,0x2E,0x9A);*/
+
+ prepare_to_wait(&decoder->wq, &wait, TASK_UNINTERRUPTIBLE);
+ schedule_timeout(msecs_to_jiffies(250));
+ finish_wait(&decoder->wq, &wait);
+
+- status = saa7110_read(client);
++ status = saa7110_read(sd);
+ if ((status & 0x03) == 0x01) {
+- v4l_dbg(1, debug, client, "status=0x%02x (SECAM)\n", status);
+- saa7110_write(client, 0x0D, 0x87);
+- return VIDEO_MODE_SECAM;
++ v4l2_dbg(1, debug, sd, "status=0x%02x (SECAM)\n", status);
++ saa7110_write(sd, 0x0D, 0x87);
++ return V4L2_STD_SECAM;
+ }
+- v4l_dbg(1, debug, client, "status=0x%02x (PAL)\n", status);
+- return VIDEO_MODE_PAL;
++ v4l2_dbg(1, debug, sd, "status=0x%02x (PAL)\n", status);
++ return V4L2_STD_PAL;
+ }
+
+-static int
+-saa7110_command (struct i2c_client *client,
+- unsigned int cmd,
+- void *arg)
++static int saa7110_g_input_status(struct v4l2_subdev *sd, u32 *pstatus)
+ {
+- struct saa7110 *decoder = i2c_get_clientdata(client);
+- int v;
++ struct saa7110 *decoder = to_saa7110(sd);
++ int res = V4L2_IN_ST_NO_SIGNAL;
++ int status = saa7110_read(sd);
++
++ v4l2_dbg(1, debug, sd, "status=0x%02x norm=%llx\n",
++ status, (unsigned long long)decoder->norm);
++ if (!(status & 0x40))
++ res = 0;
++ if (!(status & 0x03))
++ res |= V4L2_IN_ST_NO_COLOR;
++
++ *pstatus = res;
++ return 0;
++}
+
+- switch (cmd) {
+- case 0:
+- //saa7110_write_block(client, initseq, sizeof(initseq));
+- break;
++static int saa7110_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
++{
++ *(v4l2_std_id *)std = determine_norm(sd);
++ return 0;
++}
+
+- case DECODER_GET_CAPABILITIES:
+- {
+- struct video_decoder_capability *dc = arg;
++static int saa7110_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
++{
++ struct saa7110 *decoder = to_saa7110(sd);
++
++ if (decoder->norm != std) {
++ decoder->norm = std;
++ /*saa7110_write(sd, 0x06, 0x03);*/
++ if (std & V4L2_STD_NTSC) {
++ saa7110_write(sd, 0x0D, 0x86);
++ saa7110_write(sd, 0x0F, 0x50);
++ saa7110_write(sd, 0x11, 0x2C);
++ /*saa7110_write(sd, 0x2E, 0x81);*/
++ v4l2_dbg(1, debug, sd, "switched to NTSC\n");
++ } else if (std & V4L2_STD_PAL) {
++ saa7110_write(sd, 0x0D, 0x86);
++ saa7110_write(sd, 0x0F, 0x10);
++ saa7110_write(sd, 0x11, 0x59);
++ /*saa7110_write(sd, 0x2E, 0x9A);*/
++ v4l2_dbg(1, debug, sd, "switched to PAL\n");
++ } else if (std & V4L2_STD_SECAM) {
++ saa7110_write(sd, 0x0D, 0x87);
++ saa7110_write(sd, 0x0F, 0x10);
++ saa7110_write(sd, 0x11, 0x59);
++ /*saa7110_write(sd, 0x2E, 0x9A);*/
++ v4l2_dbg(1, debug, sd, "switched to SECAM\n");
++ } else {
++ return -EINVAL;
++ }
++ }
++ return 0;
++}
+
+- dc->flags =
+- VIDEO_DECODER_PAL | VIDEO_DECODER_NTSC |
+- VIDEO_DECODER_SECAM | VIDEO_DECODER_AUTO;
+- dc->inputs = SAA7110_MAX_INPUT;
+- dc->outputs = SAA7110_MAX_OUTPUT;
+- break;
++static int saa7110_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
++{
++ struct saa7110 *decoder = to_saa7110(sd);
++
++ if (route->input < 0 || route->input >= SAA7110_MAX_INPUT) {
++ v4l2_dbg(1, debug, sd, "input=%d not available\n", route->input);
++ return -EINVAL;
++ }
++ if (decoder->input != route->input) {
++ saa7110_selmux(sd, route->input);
++ v4l2_dbg(1, debug, sd, "switched to input=%d\n", route->input);
+ }
++ return 0;
++}
+
+- case DECODER_GET_STATUS:
+- {
+- int status;
+- int res = 0;
+-
+- status = saa7110_read(client);
+- v4l_dbg(1, debug, client, "status=0x%02x norm=%d\n",
+- status, decoder->norm);
+- if (!(status & 0x40))
+- res |= DECODER_STATUS_GOOD;
+- if (status & 0x03)
+- res |= DECODER_STATUS_COLOR;
+-
+- switch (decoder->norm) {
+- case VIDEO_MODE_NTSC:
+- res |= DECODER_STATUS_NTSC;
+- break;
+- case VIDEO_MODE_PAL:
+- res |= DECODER_STATUS_PAL;
+- break;
+- case VIDEO_MODE_SECAM:
+- res |= DECODER_STATUS_SECAM;
+- break;
+- }
+- *(int *) arg = res;
+- break;
++static int saa7110_s_stream(struct v4l2_subdev *sd, int enable)
++{
++ struct saa7110 *decoder = to_saa7110(sd);
++
++ if (decoder->enable != enable) {
++ decoder->enable = enable;
++ saa7110_write(sd, 0x0E, enable ? 0x18 : 0x80);
++ v4l2_dbg(1, debug, sd, "YUV %s\n", enable ? "on" : "off");
+ }
++ return 0;
++}
+
+- case DECODER_SET_NORM:
+- v = *(int *) arg;
+- if (decoder->norm != v) {
+- decoder->norm = v;
+- //saa7110_write(client, 0x06, 0x03);
+- switch (v) {
+- case VIDEO_MODE_NTSC:
+- saa7110_write(client, 0x0D, 0x86);
+- saa7110_write(client, 0x0F, 0x50);
+- saa7110_write(client, 0x11, 0x2C);
+- //saa7110_write(client, 0x2E, 0x81);
+- v4l_dbg(1, debug, client, "switched to NTSC\n");
+- break;
+- case VIDEO_MODE_PAL:
+- saa7110_write(client, 0x0D, 0x86);
+- saa7110_write(client, 0x0F, 0x10);
+- saa7110_write(client, 0x11, 0x59);
+- //saa7110_write(client, 0x2E, 0x9A);
+- v4l_dbg(1, debug, client, "switched to PAL\n");
+- break;
+- case VIDEO_MODE_SECAM:
+- saa7110_write(client, 0x0D, 0x87);
+- saa7110_write(client, 0x0F, 0x10);
+- saa7110_write(client, 0x11, 0x59);
+- //saa7110_write(client, 0x2E, 0x9A);
+- v4l_dbg(1, debug, client, "switched to SECAM\n");
+- break;
+- case VIDEO_MODE_AUTO:
+- v4l_dbg(1, debug, client, "switched to AUTO\n");
+- decoder->norm = determine_norm(client);
+- *(int *) arg = decoder->norm;
+- break;
+- default:
+- return -EPERM;
+- }
+- }
+- break;
++static int saa7110_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
++{
++ switch (qc->id) {
++ case V4L2_CID_BRIGHTNESS:
++ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
++ case V4L2_CID_CONTRAST:
++ case V4L2_CID_SATURATION:
++ return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
++ case V4L2_CID_HUE:
++ return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
+
+- case DECODER_SET_INPUT:
+- v = *(int *) arg;
+- if (v < 0 || v >= SAA7110_MAX_INPUT) {
+- v4l_dbg(1, debug, client, "input=%d not available\n", v);
+- return -EINVAL;
+- }
+- if (decoder->input != v) {
+- saa7110_selmux(client, v);
+- v4l_dbg(1, debug, client, "switched to input=%d\n", v);
+- }
+- break;
++static int saa7110_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
++{
++ struct saa7110 *decoder = to_saa7110(sd);
+
+- case DECODER_SET_OUTPUT:
+- v = *(int *) arg;
+- /* not much choice of outputs */
+- if (v != 0)
+- return -EINVAL;
++ switch (ctrl->id) {
++ case V4L2_CID_BRIGHTNESS:
++ ctrl->value = decoder->bright;
+ break;
+-
+- case DECODER_ENABLE_OUTPUT:
+- v = *(int *) arg;
+- if (decoder->enable != v) {
+- decoder->enable = v;
+- saa7110_write(client, 0x0E, v ? 0x18 : 0x80);
+- v4l_dbg(1, debug, client, "YUV %s\n", v ? "on" : "off");
+- }
++ case V4L2_CID_CONTRAST:
++ ctrl->value = decoder->contrast;
++ break;
++ case V4L2_CID_SATURATION:
++ ctrl->value = decoder->sat;
++ break;
++ case V4L2_CID_HUE:
++ ctrl->value = decoder->hue;
+ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
+
+- case DECODER_SET_PICTURE:
+- {
+- struct video_picture *pic = arg;
++static int saa7110_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
++{
++ struct saa7110 *decoder = to_saa7110(sd);
+
+- if (decoder->bright != pic->brightness) {
+- /* We want 0 to 255 we get 0-65535 */
+- decoder->bright = pic->brightness;
+- saa7110_write(client, 0x19, decoder->bright >> 8);
+- }
+- if (decoder->contrast != pic->contrast) {
+- /* We want 0 to 127 we get 0-65535 */
+- decoder->contrast = pic->contrast;
+- saa7110_write(client, 0x13,
+- decoder->contrast >> 9);
++ switch (ctrl->id) {
++ case V4L2_CID_BRIGHTNESS:
++ if (decoder->bright != ctrl->value) {
++ decoder->bright = ctrl->value;
++ saa7110_write(sd, 0x19, decoder->bright);
+ }
+- if (decoder->sat != pic->colour) {
+- /* We want 0 to 127 we get 0-65535 */
+- decoder->sat = pic->colour;
+- saa7110_write(client, 0x12, decoder->sat >> 9);
++ break;
++ case V4L2_CID_CONTRAST:
++ if (decoder->contrast != ctrl->value) {
++ decoder->contrast = ctrl->value;
++ saa7110_write(sd, 0x13, decoder->contrast);
+ }
+- if (decoder->hue != pic->hue) {
+- /* We want -128 to 127 we get 0-65535 */
+- decoder->hue = pic->hue;
+- saa7110_write(client, 0x07,
+- (decoder->hue >> 8) - 128);
++ break;
++ case V4L2_CID_SATURATION:
++ if (decoder->sat != ctrl->value) {
++ decoder->sat = ctrl->value;
++ saa7110_write(sd, 0x12, decoder->sat);
+ }
+ break;
+- }
+-
+- case DECODER_DUMP:
+- if (!debug)
+- break;
+- for (v = 0; v < SAA7110_NR_REG; v += 16) {
+- int j;
+- v4l_dbg(1, debug, client, "%02x:", v);
+- for (j = 0; j < 16 && v + j < SAA7110_NR_REG; j++)
+- printk(KERN_CONT " %02x", decoder->reg[v + j]);
+- printk(KERN_CONT "\n");
++ case V4L2_CID_HUE:
++ if (decoder->hue != ctrl->value) {
++ decoder->hue = ctrl->value;
++ saa7110_write(sd, 0x07, decoder->hue);
+ }
+ break;
+-
+ default:
+- v4l_dbg(1, debug, client, "unknown command %08x\n", cmd);
+ return -EINVAL;
+ }
+ return 0;
+ }
+
++static int saa7110_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7110, 0);
++}
++
+ /* ----------------------------------------------------------------------- */
+
+-/*
+- * Generic i2c probe
+- * concerning the addresses: i2c wants 7 bit (without the r/w bit), so '>>1'
+- */
++static const struct v4l2_subdev_core_ops saa7110_core_ops = {
++ .g_chip_ident = saa7110_g_chip_ident,
++ .g_ctrl = saa7110_g_ctrl,
++ .s_ctrl = saa7110_s_ctrl,
++ .queryctrl = saa7110_queryctrl,
++};
+
+-static unsigned short normal_i2c[] = { 0x9c >> 1, 0x9e >> 1, I2C_CLIENT_END };
++static const struct v4l2_subdev_tuner_ops saa7110_tuner_ops = {
++ .s_std = saa7110_s_std,
++};
+
+-I2C_CLIENT_INSMOD;
++static const struct v4l2_subdev_video_ops saa7110_video_ops = {
++ .s_routing = saa7110_s_routing,
++ .s_stream = saa7110_s_stream,
++ .querystd = saa7110_querystd,
++ .g_input_status = saa7110_g_input_status,
++};
++
++static const struct v4l2_subdev_ops saa7110_ops = {
++ .core = &saa7110_core_ops,
++ .tuner = &saa7110_tuner_ops,
++ .video = &saa7110_video_ops,
++};
++
++/* ----------------------------------------------------------------------- */
+
+ static int saa7110_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+ {
+ struct saa7110 *decoder;
++ struct v4l2_subdev *sd;
+ int rv;
+
+ /* Check if the adapter supports the needed features */
+@@ -429,7 +453,9 @@ static int saa7110_probe(struct i2c_client *client,
+ decoder = kzalloc(sizeof(struct saa7110), GFP_KERNEL);
+ if (!decoder)
+ return -ENOMEM;
+- decoder->norm = VIDEO_MODE_PAL;
++ sd = &decoder->sd;
++ v4l2_i2c_subdev_init(sd, client, &saa7110_ops);
++ decoder->norm = V4L2_STD_PAL;
+ decoder->input = 0;
+ decoder->enable = 1;
+ decoder->bright = 32768;
+@@ -437,30 +463,29 @@ static int saa7110_probe(struct i2c_client *client,
+ decoder->hue = 32768;
+ decoder->sat = 32768;
+ init_waitqueue_head(&decoder->wq);
+- i2c_set_clientdata(client, decoder);
+
+- rv = saa7110_write_block(client, initseq, sizeof(initseq));
++ rv = saa7110_write_block(sd, initseq, sizeof(initseq));
+ if (rv < 0) {
+- v4l_dbg(1, debug, client, "init status %d\n", rv);
++ v4l2_dbg(1, debug, sd, "init status %d\n", rv);
+ } else {
+ int ver, status;
+- saa7110_write(client, 0x21, 0x10);
+- saa7110_write(client, 0x0e, 0x18);
+- saa7110_write(client, 0x0D, 0x04);
+- ver = saa7110_read(client);
+- saa7110_write(client, 0x0D, 0x06);
+- //mdelay(150);
+- status = saa7110_read(client);
+- v4l_dbg(1, debug, client, "version %x, status=0x%02x\n",
++ saa7110_write(sd, 0x21, 0x10);
++ saa7110_write(sd, 0x0e, 0x18);
++ saa7110_write(sd, 0x0D, 0x04);
++ ver = saa7110_read(sd);
++ saa7110_write(sd, 0x0D, 0x06);
++ /*mdelay(150);*/
++ status = saa7110_read(sd);
++ v4l2_dbg(1, debug, sd, "version %x, status=0x%02x\n",
+ ver, status);
+- saa7110_write(client, 0x0D, 0x86);
+- saa7110_write(client, 0x0F, 0x10);
+- saa7110_write(client, 0x11, 0x59);
+- //saa7110_write(client, 0x2E, 0x9A);
++ saa7110_write(sd, 0x0D, 0x86);
++ saa7110_write(sd, 0x0F, 0x10);
++ saa7110_write(sd, 0x11, 0x59);
++ /*saa7110_write(sd, 0x2E, 0x9A);*/
+ }
+
+- //saa7110_selmux(client,0);
+- //determine_norm(client);
++ /*saa7110_selmux(sd,0);*/
++ /*determine_norm(sd);*/
+ /* setup and implicit mode 0 select has been performed */
+
+ return 0;
+@@ -468,7 +493,10 @@ static int saa7110_probe(struct i2c_client *client,
+
+ static int saa7110_remove(struct i2c_client *client)
+ {
+- kfree(i2c_get_clientdata(client));
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++
++ v4l2_device_unregister_subdev(sd);
++ kfree(to_saa7110(sd));
+ return 0;
+ }
+
+@@ -482,8 +510,6 @@ MODULE_DEVICE_TABLE(i2c, saa7110_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "saa7110",
+- .driverid = I2C_DRIVERID_SAA7110,
+- .command = saa7110_command,
+ .probe = saa7110_probe,
+ .remove = saa7110_remove,
+ .id_table = saa7110_id,
+diff --git a/drivers/media/video/saa7111.c b/drivers/media/video/saa7111.c
+deleted file mode 100644
+index a4738a2..0000000
+--- a/drivers/media/video/saa7111.c
++++ /dev/null
+@@ -1,492 +0,0 @@
+-/*
+- * saa7111 - Philips SAA7111A video decoder driver version 0.0.3
+- *
+- * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+- *
+- * Slight changes for video timing and attachment output by
+- * Wolfgang Scherr <scherr@net4you.net>
+- *
+- * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
+- * - moved over to linux>=2.4.x i2c protocol (1/1/2003)
+- *
+- * Changes by Michael Hunold <michael@mihu.de>
+- * - implemented DECODER_SET_GPIO, DECODER_INIT, DECODER_SET_VBI_BYPASS
+- *
+- * 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 <linux/module.h>
+-#include <linux/types.h>
+-#include <linux/ioctl.h>
+-#include <asm/uaccess.h>
+-#include <linux/i2c.h>
+-#include <linux/i2c-id.h>
+-#include <linux/videodev.h>
+-#include <linux/video_decoder.h>
+-#include <media/v4l2-common.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
+-
+-MODULE_DESCRIPTION("Philips SAA7111 video decoder driver");
+-MODULE_AUTHOR("Dave Perks");
+-MODULE_LICENSE("GPL");
+-
+-static int debug;
+-module_param(debug, int, 0644);
+-MODULE_PARM_DESC(debug, "Debug level (0-1)");
+-
+-/* ----------------------------------------------------------------------- */
+-
+-#define SAA7111_NR_REG 0x18
+-
+-struct saa7111 {
+- unsigned char reg[SAA7111_NR_REG];
+-
+- int norm;
+- int input;
+- int enable;
+-};
+-
+-/* ----------------------------------------------------------------------- */
+-
+-static inline int saa7111_write(struct i2c_client *client, u8 reg, u8 value)
+-{
+- struct saa7111 *decoder = i2c_get_clientdata(client);
+-
+- decoder->reg[reg] = value;
+- return i2c_smbus_write_byte_data(client, reg, value);
+-}
+-
+-static inline void saa7111_write_if_changed(struct i2c_client *client, u8 reg, u8 value)
+-{
+- struct saa7111 *decoder = i2c_get_clientdata(client);
+-
+- if (decoder->reg[reg] != value) {
+- decoder->reg[reg] = value;
+- i2c_smbus_write_byte_data(client, reg, value);
+- }
+-}
+-
+-static int saa7111_write_block(struct i2c_client *client, const u8 *data, unsigned int len)
+-{
+- int ret = -1;
+- u8 reg;
+-
+- /* the saa7111 has an autoincrement function, use it if
+- * the adapter understands raw I2C */
+- if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+- /* do raw I2C, not smbus compatible */
+- struct saa7111 *decoder = i2c_get_clientdata(client);
+- u8 block_data[32];
+- int block_len;
+-
+- while (len >= 2) {
+- block_len = 0;
+- block_data[block_len++] = reg = data[0];
+- do {
+- block_data[block_len++] =
+- decoder->reg[reg++] = data[1];
+- len -= 2;
+- data += 2;
+- } while (len >= 2 && data[0] == reg && block_len < 32);
+- ret = i2c_master_send(client, block_data, block_len);
+- if (ret < 0)
+- break;
+- }
+- } else {
+- /* do some slow I2C emulation kind of thing */
+- while (len >= 2) {
+- reg = *data++;
+- ret = saa7111_write(client, reg, *data++);
+- if (ret < 0)
+- break;
+- len -= 2;
+- }
+- }
+-
+- return ret;
+-}
+-
+-static int saa7111_init_decoder(struct i2c_client *client,
+- struct video_decoder_init *init)
+-{
+- return saa7111_write_block(client, init->data, init->len);
+-}
+-
+-static inline int saa7111_read(struct i2c_client *client, u8 reg)
+-{
+- return i2c_smbus_read_byte_data(client, reg);
+-}
+-
+-/* ----------------------------------------------------------------------- */
+-
+-static const unsigned char saa7111_i2c_init[] = {
+- 0x00, 0x00, /* 00 - ID byte */
+- 0x01, 0x00, /* 01 - reserved */
+-
+- /*front end */
+- 0x02, 0xd0, /* 02 - FUSE=3, GUDL=2, MODE=0 */
+- 0x03, 0x23, /* 03 - HLNRS=0, VBSL=1, WPOFF=0,
+- * HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */
+- 0x04, 0x00, /* 04 - GAI1=256 */
+- 0x05, 0x00, /* 05 - GAI2=256 */
+-
+- /* decoder */
+- 0x06, 0xf3, /* 06 - HSB at 13(50Hz) / 17(60Hz)
+- * pixels after end of last line */
+- /*0x07, 0x13, * 07 - HSS at 113(50Hz) / 117(60Hz) pixels
+- * after end of last line */
+- 0x07, 0xe8, /* 07 - HSS seems to be needed to
+- * work with NTSC, too */
+- 0x08, 0xc8, /* 08 - AUFD=1, FSEL=1, EXFIL=0,
+- * VTRC=1, HPLL=0, VNOI=0 */
+- 0x09, 0x01, /* 09 - BYPS=0, PREF=0, BPSS=0,
+- * VBLB=0, UPTCV=0, APER=1 */
+- 0x0a, 0x80, /* 0a - BRIG=128 */
+- 0x0b, 0x47, /* 0b - CONT=1.109 */
+- 0x0c, 0x40, /* 0c - SATN=1.0 */
+- 0x0d, 0x00, /* 0d - HUE=0 */
+- 0x0e, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0,
+- * FCTC=0, CHBW=1 */
+- 0x0f, 0x00, /* 0f - reserved */
+- 0x10, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */
+- 0x11, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1,
+- * OEYC=1, OEHV=1, VIPB=0, COLO=0 */
+- 0x12, 0x00, /* 12 - output control 2 */
+- 0x13, 0x00, /* 13 - output control 3 */
+- 0x14, 0x00, /* 14 - reserved */
+- 0x15, 0x00, /* 15 - VBI */
+- 0x16, 0x00, /* 16 - VBI */
+- 0x17, 0x00, /* 17 - VBI */
+-};
+-
+-static int saa7111_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- struct saa7111 *decoder = i2c_get_clientdata(client);
+-
+- switch (cmd) {
+- case 0:
+- break;
+- case DECODER_INIT:
+- {
+- struct video_decoder_init *init = arg;
+- struct video_decoder_init vdi;
+-
+- if (NULL != init)
+- return saa7111_init_decoder(client, init);
+- vdi.data = saa7111_i2c_init;
+- vdi.len = sizeof(saa7111_i2c_init);
+- return saa7111_init_decoder(client, &vdi);
+- }
+-
+- case DECODER_DUMP:
+- {
+- int i;
+-
+- for (i = 0; i < SAA7111_NR_REG; i += 16) {
+- int j;
+-
+- v4l_info(client, "%03x", i);
+- for (j = 0; j < 16 && i + j < SAA7111_NR_REG; ++j) {
+- printk(KERN_CONT " %02x",
+- saa7111_read(client, i + j));
+- }
+- printk(KERN_CONT "\n");
+- }
+- break;
+- }
+-
+- case DECODER_GET_CAPABILITIES:
+- {
+- struct video_decoder_capability *cap = arg;
+-
+- cap->flags = VIDEO_DECODER_PAL |
+- VIDEO_DECODER_NTSC |
+- VIDEO_DECODER_SECAM |
+- VIDEO_DECODER_AUTO |
+- VIDEO_DECODER_CCIR;
+- cap->inputs = 8;
+- cap->outputs = 1;
+- break;
+- }
+-
+- case DECODER_GET_STATUS:
+- {
+- int *iarg = arg;
+- int status;
+- int res;
+-
+- status = saa7111_read(client, 0x1f);
+- v4l_dbg(1, debug, client, "status: 0x%02x\n", status);
+- res = 0;
+- if ((status & (1 << 6)) == 0) {
+- res |= DECODER_STATUS_GOOD;
+- }
+- switch (decoder->norm) {
+- case VIDEO_MODE_NTSC:
+- res |= DECODER_STATUS_NTSC;
+- break;
+- case VIDEO_MODE_PAL:
+- res |= DECODER_STATUS_PAL;
+- break;
+- case VIDEO_MODE_SECAM:
+- res |= DECODER_STATUS_SECAM;
+- break;
+- default:
+- case VIDEO_MODE_AUTO:
+- if ((status & (1 << 5)) != 0) {
+- res |= DECODER_STATUS_NTSC;
+- } else {
+- res |= DECODER_STATUS_PAL;
+- }
+- break;
+- }
+- if ((status & (1 << 0)) != 0) {
+- res |= DECODER_STATUS_COLOR;
+- }
+- *iarg = res;
+- break;
+- }
+-
+- case DECODER_SET_GPIO:
+- {
+- int *iarg = arg;
+- if (0 != *iarg) {
+- saa7111_write(client, 0x11,
+- (decoder->reg[0x11] | 0x80));
+- } else {
+- saa7111_write(client, 0x11,
+- (decoder->reg[0x11] & 0x7f));
+- }
+- break;
+- }
+-
+- case DECODER_SET_VBI_BYPASS:
+- {
+- int *iarg = arg;
+- if (0 != *iarg) {
+- saa7111_write(client, 0x13,
+- (decoder->reg[0x13] & 0xf0) | 0x0a);
+- } else {
+- saa7111_write(client, 0x13,
+- (decoder->reg[0x13] & 0xf0));
+- }
+- break;
+- }
+-
+- case DECODER_SET_NORM:
+- {
+- int *iarg = arg;
+-
+- switch (*iarg) {
+-
+- case VIDEO_MODE_NTSC:
+- saa7111_write(client, 0x08,
+- (decoder->reg[0x08] & 0x3f) | 0x40);
+- saa7111_write(client, 0x0e,
+- (decoder->reg[0x0e] & 0x8f));
+- break;
+-
+- case VIDEO_MODE_PAL:
+- saa7111_write(client, 0x08,
+- (decoder->reg[0x08] & 0x3f) | 0x00);
+- saa7111_write(client, 0x0e,
+- (decoder->reg[0x0e] & 0x8f));
+- break;
+-
+- case VIDEO_MODE_SECAM:
+- saa7111_write(client, 0x08,
+- (decoder->reg[0x08] & 0x3f) | 0x00);
+- saa7111_write(client, 0x0e,
+- (decoder->reg[0x0e] & 0x8f) | 0x50);
+- break;
+-
+- case VIDEO_MODE_AUTO:
+- saa7111_write(client, 0x08,
+- (decoder->reg[0x08] & 0x3f) | 0x80);
+- saa7111_write(client, 0x0e,
+- (decoder->reg[0x0e] & 0x8f));
+- break;
+-
+- default:
+- return -EINVAL;
+-
+- }
+- decoder->norm = *iarg;
+- break;
+- }
+-
+- case DECODER_SET_INPUT:
+- {
+- int *iarg = arg;
+-
+- if (*iarg < 0 || *iarg > 7) {
+- return -EINVAL;
+- }
+-
+- if (decoder->input != *iarg) {
+- decoder->input = *iarg;
+- /* select mode */
+- saa7111_write(client, 0x02,
+- (decoder->
+- reg[0x02] & 0xf8) | decoder->input);
+- /* bypass chrominance trap for modes 4..7 */
+- saa7111_write(client, 0x09,
+- (decoder->
+- reg[0x09] & 0x7f) | ((decoder->
+- input >
+- 3) ? 0x80 :
+- 0));
+- }
+- break;
+- }
+-
+- case DECODER_SET_OUTPUT:
+- {
+- int *iarg = arg;
+-
+- /* not much choice of outputs */
+- if (*iarg != 0) {
+- return -EINVAL;
+- }
+- break;
+- }
+-
+- case DECODER_ENABLE_OUTPUT:
+- {
+- int *iarg = arg;
+- int enable = (*iarg != 0);
+-
+- if (decoder->enable != enable) {
+- decoder->enable = enable;
+-
+- /* RJ: If output should be disabled (for
+- * playing videos), we also need a open PLL.
+- * The input is set to 0 (where no input
+- * source is connected), although this
+- * is not necessary.
+- *
+- * If output should be enabled, we have to
+- * reverse the above.
+- */
+-
+- if (decoder->enable) {
+- saa7111_write(client, 0x02,
+- (decoder->
+- reg[0x02] & 0xf8) |
+- decoder->input);
+- saa7111_write(client, 0x08,
+- (decoder->reg[0x08] & 0xfb));
+- saa7111_write(client, 0x11,
+- (decoder->
+- reg[0x11] & 0xf3) | 0x0c);
+- } else {
+- saa7111_write(client, 0x02,
+- (decoder->reg[0x02] & 0xf8));
+- saa7111_write(client, 0x08,
+- (decoder->
+- reg[0x08] & 0xfb) | 0x04);
+- saa7111_write(client, 0x11,
+- (decoder->reg[0x11] & 0xf3));
+- }
+- }
+- break;
+- }
+-
+- case DECODER_SET_PICTURE:
+- {
+- struct video_picture *pic = arg;
+-
+- /* We want 0 to 255 we get 0-65535 */
+- saa7111_write_if_changed(client, 0x0a, pic->brightness >> 8);
+- /* We want 0 to 127 we get 0-65535 */
+- saa7111_write(client, 0x0b, pic->contrast >> 9);
+- /* We want 0 to 127 we get 0-65535 */
+- saa7111_write(client, 0x0c, pic->colour >> 9);
+- /* We want -128 to 127 we get 0-65535 */
+- saa7111_write(client, 0x0d, (pic->hue - 32768) >> 8);
+- break;
+- }
+-
+- default:
+- return -EINVAL;
+- }
+-
+- return 0;
+-}
+-
+-/* ----------------------------------------------------------------------- */
+-
+-static unsigned short normal_i2c[] = { 0x48 >> 1, I2C_CLIENT_END };
+-
+-I2C_CLIENT_INSMOD;
+-
+-static int saa7111_probe(struct i2c_client *client,
+- const struct i2c_device_id *id)
+-{
+- int i;
+- struct saa7111 *decoder;
+- struct video_decoder_init vdi;
+-
+- /* Check if the adapter supports the needed features */
+- if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+- return -ENODEV;
+-
+- v4l_info(client, "chip found @ 0x%x (%s)\n",
+- client->addr << 1, client->adapter->name);
+-
+- decoder = kzalloc(sizeof(struct saa7111), GFP_KERNEL);
+- if (decoder == NULL) {
+- kfree(client);
+- return -ENOMEM;
+- }
+- decoder->norm = VIDEO_MODE_NTSC;
+- decoder->input = 0;
+- decoder->enable = 1;
+- i2c_set_clientdata(client, decoder);
+-
+- vdi.data = saa7111_i2c_init;
+- vdi.len = sizeof(saa7111_i2c_init);
+- i = saa7111_init_decoder(client, &vdi);
+- if (i < 0) {
+- v4l_dbg(1, debug, client, "init status %d\n", i);
+- } else {
+- v4l_dbg(1, debug, client, "revision %x\n",
+- saa7111_read(client, 0x00) >> 4);
+- }
+- return 0;
+-}
+-
+-static int saa7111_remove(struct i2c_client *client)
+-{
+- kfree(i2c_get_clientdata(client));
+- return 0;
+-}
+-
+-/* ----------------------------------------------------------------------- */
+-
+-static const struct i2c_device_id saa7111_id[] = {
+- { "saa7111_old", 0 }, /* "saa7111" maps to the saa7115 driver */
+- { }
+-};
+-MODULE_DEVICE_TABLE(i2c, saa7111_id);
+-
+-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+- .name = "saa7111",
+- .driverid = I2C_DRIVERID_SAA7111A,
+- .command = saa7111_command,
+- .probe = saa7111_probe,
+- .remove = saa7111_remove,
+- .id_table = saa7111_id,
+-};
+diff --git a/drivers/media/video/saa7114.c b/drivers/media/video/saa7114.c
+deleted file mode 100644
+index 7ca709f..0000000
+--- a/drivers/media/video/saa7114.c
++++ /dev/null
+@@ -1,1068 +0,0 @@
+-/*
+- * saa7114 - Philips SAA7114H video decoder driver version 0.0.1
+- *
+- * Copyright (C) 2002 Maxim Yevtyushkin <max@linuxmedialabs.com>
+- *
+- * Based on saa7111 driver by Dave Perks
+- *
+- * Copyright (C) 1998 Dave Perks <dperks@ibm.net>
+- *
+- * Slight changes for video timing and attachment output by
+- * Wolfgang Scherr <scherr@net4you.net>
+- *
+- * Changes by Ronald Bultje <rbultje@ronald.bitfreak.net>
+- * - moved over to linux>=2.4.x i2c protocol (1/1/2003)
+- *
+- * 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 <linux/module.h>
+-#include <linux/types.h>
+-#include <linux/ioctl.h>
+-#include <asm/uaccess.h>
+-#include <linux/i2c.h>
+-#include <linux/i2c-id.h>
+-#include <linux/videodev.h>
+-#include <linux/video_decoder.h>
+-#include <media/v4l2-common.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
+-
+-MODULE_DESCRIPTION("Philips SAA7114H video decoder driver");
+-MODULE_AUTHOR("Maxim Yevtyushkin");
+-MODULE_LICENSE("GPL");
+-
+-static int debug;
+-module_param(debug, int, 0);
+-MODULE_PARM_DESC(debug, "Debug level (0-1)");
+-
+-/* ----------------------------------------------------------------------- */
+-
+-struct saa7114 {
+- unsigned char reg[0xf0 * 2];
+-
+- int norm;
+- int input;
+- int enable;
+- int bright;
+- int contrast;
+- int hue;
+- int sat;
+- int playback;
+-};
+-
+-#define I2C_DELAY 10
+-
+-
+-//#define SAA_7114_NTSC_HSYNC_START (-3)
+-//#define SAA_7114_NTSC_HSYNC_STOP (-18)
+-
+-#define SAA_7114_NTSC_HSYNC_START (-17)
+-#define SAA_7114_NTSC_HSYNC_STOP (-32)
+-
+-//#define SAA_7114_NTSC_HOFFSET (5)
+-#define SAA_7114_NTSC_HOFFSET (6)
+-#define SAA_7114_NTSC_VOFFSET (10)
+-#define SAA_7114_NTSC_WIDTH (720)
+-#define SAA_7114_NTSC_HEIGHT (250)
+-
+-#define SAA_7114_SECAM_HSYNC_START (-17)
+-#define SAA_7114_SECAM_HSYNC_STOP (-32)
+-
+-#define SAA_7114_SECAM_HOFFSET (2)
+-#define SAA_7114_SECAM_VOFFSET (10)
+-#define SAA_7114_SECAM_WIDTH (720)
+-#define SAA_7114_SECAM_HEIGHT (300)
+-
+-#define SAA_7114_PAL_HSYNC_START (-17)
+-#define SAA_7114_PAL_HSYNC_STOP (-32)
+-
+-#define SAA_7114_PAL_HOFFSET (2)
+-#define SAA_7114_PAL_VOFFSET (10)
+-#define SAA_7114_PAL_WIDTH (720)
+-#define SAA_7114_PAL_HEIGHT (300)
+-
+-
+-
+-#define SAA_7114_VERTICAL_CHROMA_OFFSET 0 //0x50504040
+-#define SAA_7114_VERTICAL_LUMA_OFFSET 0
+-
+-#define REG_ADDR(x) (((x) << 1) + 1)
+-#define LOBYTE(x) ((unsigned char)((x) & 0xff))
+-#define HIBYTE(x) ((unsigned char)(((x) >> 8) & 0xff))
+-#define LOWORD(x) ((unsigned short int)((x) & 0xffff))
+-#define HIWORD(x) ((unsigned short int)(((x) >> 16) & 0xffff))
+-
+-
+-/* ----------------------------------------------------------------------- */
+-
+-static inline int saa7114_write(struct i2c_client *client, u8 reg, u8 value)
+-{
+- return i2c_smbus_write_byte_data(client, reg, value);
+-}
+-
+-static int saa7114_write_block(struct i2c_client *client, const u8 *data, unsigned int len)
+-{
+- int ret = -1;
+- u8 reg;
+-
+- /* the saa7114 has an autoincrement function, use it if
+- * the adapter understands raw I2C */
+- if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+- /* do raw I2C, not smbus compatible */
+- u8 block_data[32];
+- int block_len;
+-
+- while (len >= 2) {
+- block_len = 0;
+- block_data[block_len++] = reg = data[0];
+- do {
+- block_data[block_len++] = data[1];
+- reg++;
+- len -= 2;
+- data += 2;
+- } while (len >= 2 && data[0] == reg && block_len < 32);
+- ret = i2c_master_send(client, block_data, block_len);
+- if (ret < 0)
+- break;
+- }
+- } else {
+- /* do some slow I2C emulation kind of thing */
+- while (len >= 2) {
+- reg = *data++;
+- ret = saa7114_write(client, reg, *data++);
+- if (ret < 0)
+- break;
+- len -= 2;
+- }
+- }
+-
+- return ret;
+-}
+-
+-static inline int saa7114_read(struct i2c_client *client, u8 reg)
+-{
+- return i2c_smbus_read_byte_data(client, reg);
+-}
+-
+-/* ----------------------------------------------------------------------- */
+-
+-// initially set NTSC, composite
+-
+-
+-static const unsigned char init[] = {
+- 0x00, 0x00, /* 00 - ID byte , chip version,
+- * read only */
+- 0x01, 0x08, /* 01 - X,X,X,X, IDEL3 to IDEL0 -
+- * horizontal increment delay,
+- * recommended position */
+- 0x02, 0x00, /* 02 - FUSE=3, GUDL=2, MODE=0 ;
+- * input control */
+- 0x03, 0x10, /* 03 - HLNRS=0, VBSL=1, WPOFF=0,
+- * HOLDG=0, GAFIX=0, GAI1=256, GAI2=256 */
+- 0x04, 0x90, /* 04 - GAI1=256 */
+- 0x05, 0x90, /* 05 - GAI2=256 */
+- 0x06, SAA_7114_NTSC_HSYNC_START, /* 06 - HSB: hsync start,
+- * depends on the video standard */
+- 0x07, SAA_7114_NTSC_HSYNC_STOP, /* 07 - HSS: hsync stop, depends
+- *on the video standard */
+- 0x08, 0xb8, /* 08 - AUFD=1, FSEL=1, EXFIL=0, VTRC=1,
+- * HPLL: free running in playback, locked
+- * in capture, VNOI=0 */
+- 0x09, 0x80, /* 09 - BYPS=0, PREF=0, BPSS=0, VBLB=0,
+- * UPTCV=0, APER=1; depends from input */
+- 0x0a, 0x80, /* 0a - BRIG=128 */
+- 0x0b, 0x44, /* 0b - CONT=1.109 */
+- 0x0c, 0x40, /* 0c - SATN=1.0 */
+- 0x0d, 0x00, /* 0d - HUE=0 */
+- 0x0e, 0x84, /* 0e - CDTO, CSTD2 to 0, DCVF, FCTC,
+- * CCOMB; depends from video standard */
+- 0x0f, 0x24, /* 0f - ACGC,CGAIN6 to CGAIN0; depends
+- * from video standard */
+- 0x10, 0x03, /* 10 - OFFU1 to 0, OFFV1 to 0, CHBW,
+- * LCBW2 to 0 */
+- 0x11, 0x59, /* 11 - COLO, RTP1, HEDL1 to 0, RTP0,
+- * YDEL2 to 0 */
+- 0x12, 0xc9, /* 12 - RT signal control RTSE13 to 10
+- * and 03 to 00 */
+- 0x13, 0x80, /* 13 - RT/X port output control */
+- 0x14, 0x00, /* 14 - analog, ADC, compatibility control */
+- 0x15, 0x00, /* 15 - VGATE start FID change */
+- 0x16, 0xfe, /* 16 - VGATE stop */
+- 0x17, 0x00, /* 17 - Misc., VGATE MSBs */
+- 0x18, 0x40, /* RAWG */
+- 0x19, 0x80, /* RAWO */
+- 0x1a, 0x00,
+- 0x1b, 0x00,
+- 0x1c, 0x00,
+- 0x1d, 0x00,
+- 0x1e, 0x00,
+- 0x1f, 0x00, /* status byte, read only */
+- 0x20, 0x00, /* video decoder reserved part */
+- 0x21, 0x00,
+- 0x22, 0x00,
+- 0x23, 0x00,
+- 0x24, 0x00,
+- 0x25, 0x00,
+- 0x26, 0x00,
+- 0x27, 0x00,
+- 0x28, 0x00,
+- 0x29, 0x00,
+- 0x2a, 0x00,
+- 0x2b, 0x00,
+- 0x2c, 0x00,
+- 0x2d, 0x00,
+- 0x2e, 0x00,
+- 0x2f, 0x00,
+- 0x30, 0xbc, /* audio clock generator */
+- 0x31, 0xdf,
+- 0x32, 0x02,
+- 0x33, 0x00,
+- 0x34, 0xcd,
+- 0x35, 0xcc,
+- 0x36, 0x3a,
+- 0x37, 0x00,
+- 0x38, 0x03,
+- 0x39, 0x10,
+- 0x3a, 0x00,
+- 0x3b, 0x00,
+- 0x3c, 0x00,
+- 0x3d, 0x00,
+- 0x3e, 0x00,
+- 0x3f, 0x00,
+- 0x40, 0x00, /* VBI data slicer */
+- 0x41, 0xff,
+- 0x42, 0xff,
+- 0x43, 0xff,
+- 0x44, 0xff,
+- 0x45, 0xff,
+- 0x46, 0xff,
+- 0x47, 0xff,
+- 0x48, 0xff,
+- 0x49, 0xff,
+- 0x4a, 0xff,
+- 0x4b, 0xff,
+- 0x4c, 0xff,
+- 0x4d, 0xff,
+- 0x4e, 0xff,
+- 0x4f, 0xff,
+- 0x50, 0xff,
+- 0x51, 0xff,
+- 0x52, 0xff,
+- 0x53, 0xff,
+- 0x54, 0xff,
+- 0x55, 0xff,
+- 0x56, 0xff,
+- 0x57, 0xff,
+- 0x58, 0x40, // framing code
+- 0x59, 0x47, // horizontal offset
+- 0x5a, 0x06, // vertical offset
+- 0x5b, 0x83, // field offset
+- 0x5c, 0x00, // reserved
+- 0x5d, 0x3e, // header and data
+- 0x5e, 0x00, // sliced data
+- 0x5f, 0x00, // reserved
+- 0x60, 0x00, /* video decoder reserved part */
+- 0x61, 0x00,
+- 0x62, 0x00,
+- 0x63, 0x00,
+- 0x64, 0x00,
+- 0x65, 0x00,
+- 0x66, 0x00,
+- 0x67, 0x00,
+- 0x68, 0x00,
+- 0x69, 0x00,
+- 0x6a, 0x00,
+- 0x6b, 0x00,
+- 0x6c, 0x00,
+- 0x6d, 0x00,
+- 0x6e, 0x00,
+- 0x6f, 0x00,
+- 0x70, 0x00, /* video decoder reserved part */
+- 0x71, 0x00,
+- 0x72, 0x00,
+- 0x73, 0x00,
+- 0x74, 0x00,
+- 0x75, 0x00,
+- 0x76, 0x00,
+- 0x77, 0x00,
+- 0x78, 0x00,
+- 0x79, 0x00,
+- 0x7a, 0x00,
+- 0x7b, 0x00,
+- 0x7c, 0x00,
+- 0x7d, 0x00,
+- 0x7e, 0x00,
+- 0x7f, 0x00,
+- 0x80, 0x00, /* X-port, I-port and scaler */
+- 0x81, 0x00,
+- 0x82, 0x00,
+- 0x83, 0x00,
+- 0x84, 0xc5,
+- 0x85, 0x0d, // hsync and vsync ?
+- 0x86, 0x40,
+- 0x87, 0x01,
+- 0x88, 0x00,
+- 0x89, 0x00,
+- 0x8a, 0x00,
+- 0x8b, 0x00,
+- 0x8c, 0x00,
+- 0x8d, 0x00,
+- 0x8e, 0x00,
+- 0x8f, 0x00,
+- 0x90, 0x03, /* Task A definition */
+- 0x91, 0x08,
+- 0x92, 0x00,
+- 0x93, 0x40,
+- 0x94, 0x00, // window settings
+- 0x95, 0x00,
+- 0x96, 0x00,
+- 0x97, 0x00,
+- 0x98, 0x00,
+- 0x99, 0x00,
+- 0x9a, 0x00,
+- 0x9b, 0x00,
+- 0x9c, 0x00,
+- 0x9d, 0x00,
+- 0x9e, 0x00,
+- 0x9f, 0x00,
+- 0xa0, 0x01, /* horizontal integer prescaling ratio */
+- 0xa1, 0x00, /* horizontal prescaler accumulation
+- * sequence length */
+- 0xa2, 0x00, /* UV FIR filter, Y FIR filter, prescaler
+- * DC gain */
+- 0xa3, 0x00,
+- 0xa4, 0x80, // luminance brightness
+- 0xa5, 0x40, // luminance gain
+- 0xa6, 0x40, // chrominance saturation
+- 0xa7, 0x00,
+- 0xa8, 0x00, // horizontal luminance scaling increment
+- 0xa9, 0x04,
+- 0xaa, 0x00, // horizontal luminance phase offset
+- 0xab, 0x00,
+- 0xac, 0x00, // horizontal chrominance scaling increment
+- 0xad, 0x02,
+- 0xae, 0x00, // horizontal chrominance phase offset
+- 0xaf, 0x00,
+- 0xb0, 0x00, // vertical luminance scaling increment
+- 0xb1, 0x04,
+- 0xb2, 0x00, // vertical chrominance scaling increment
+- 0xb3, 0x04,
+- 0xb4, 0x00,
+- 0xb5, 0x00,
+- 0xb6, 0x00,
+- 0xb7, 0x00,
+- 0xb8, 0x00,
+- 0xb9, 0x00,
+- 0xba, 0x00,
+- 0xbb, 0x00,
+- 0xbc, 0x00,
+- 0xbd, 0x00,
+- 0xbe, 0x00,
+- 0xbf, 0x00,
+- 0xc0, 0x02, // Task B definition
+- 0xc1, 0x08,
+- 0xc2, 0x00,
+- 0xc3, 0x40,
+- 0xc4, 0x00, // window settings
+- 0xc5, 0x00,
+- 0xc6, 0x00,
+- 0xc7, 0x00,
+- 0xc8, 0x00,
+- 0xc9, 0x00,
+- 0xca, 0x00,
+- 0xcb, 0x00,
+- 0xcc, 0x00,
+- 0xcd, 0x00,
+- 0xce, 0x00,
+- 0xcf, 0x00,
+- 0xd0, 0x01, // horizontal integer prescaling ratio
+- 0xd1, 0x00, // horizontal prescaler accumulation sequence length
+- 0xd2, 0x00, // UV FIR filter, Y FIR filter, prescaler DC gain
+- 0xd3, 0x00,
+- 0xd4, 0x80, // luminance brightness
+- 0xd5, 0x40, // luminance gain
+- 0xd6, 0x40, // chrominance saturation
+- 0xd7, 0x00,
+- 0xd8, 0x00, // horizontal luminance scaling increment
+- 0xd9, 0x04,
+- 0xda, 0x00, // horizontal luminance phase offset
+- 0xdb, 0x00,
+- 0xdc, 0x00, // horizontal chrominance scaling increment
+- 0xdd, 0x02,
+- 0xde, 0x00, // horizontal chrominance phase offset
+- 0xdf, 0x00,
+- 0xe0, 0x00, // vertical luminance scaling increment
+- 0xe1, 0x04,
+- 0xe2, 0x00, // vertical chrominance scaling increment
+- 0xe3, 0x04,
+- 0xe4, 0x00,
+- 0xe5, 0x00,
+- 0xe6, 0x00,
+- 0xe7, 0x00,
+- 0xe8, 0x00,
+- 0xe9, 0x00,
+- 0xea, 0x00,
+- 0xeb, 0x00,
+- 0xec, 0x00,
+- 0xed, 0x00,
+- 0xee, 0x00,
+- 0xef, 0x00
+-};
+-
+-static int saa7114_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- struct saa7114 *decoder = i2c_get_clientdata(client);
+-
+- switch (cmd) {
+- case 0:
+- //dprintk(1, KERN_INFO "%s: writing init\n", I2C_NAME(client));
+- //saa7114_write_block(client, init, sizeof(init));
+- break;
+-
+- case DECODER_DUMP:
+- {
+- int i;
+-
+- if (!debug)
+- break;
+- v4l_info(client, "decoder dump\n");
+-
+- for (i = 0; i < 32; i += 16) {
+- int j;
+-
+- v4l_info(client, "%03x", i);
+- for (j = 0; j < 16; ++j) {
+- printk(KERN_CONT " %02x",
+- saa7114_read(client, i + j));
+- }
+- printk(KERN_CONT "\n");
+- }
+- break;
+- }
+-
+- case DECODER_GET_CAPABILITIES:
+- {
+- struct video_decoder_capability *cap = arg;
+-
+- v4l_dbg(1, debug, client, "get capabilities\n");
+-
+- cap->flags = VIDEO_DECODER_PAL |
+- VIDEO_DECODER_NTSC |
+- VIDEO_DECODER_AUTO |
+- VIDEO_DECODER_CCIR;
+- cap->inputs = 8;
+- cap->outputs = 1;
+- break;
+- }
+-
+- case DECODER_GET_STATUS:
+- {
+- int *iarg = arg;
+- int status;
+- int res;
+-
+- status = saa7114_read(client, 0x1f);
+-
+- v4l_dbg(1, debug, client, "status: 0x%02x\n", status);
+- res = 0;
+- if ((status & (1 << 6)) == 0) {
+- res |= DECODER_STATUS_GOOD;
+- }
+- switch (decoder->norm) {
+- case VIDEO_MODE_NTSC:
+- res |= DECODER_STATUS_NTSC;
+- break;
+- case VIDEO_MODE_PAL:
+- res |= DECODER_STATUS_PAL;
+- break;
+- case VIDEO_MODE_SECAM:
+- res |= DECODER_STATUS_SECAM;
+- break;
+- default:
+- case VIDEO_MODE_AUTO:
+- if ((status & (1 << 5)) != 0) {
+- res |= DECODER_STATUS_NTSC;
+- } else {
+- res |= DECODER_STATUS_PAL;
+- }
+- break;
+- }
+- if ((status & (1 << 0)) != 0) {
+- res |= DECODER_STATUS_COLOR;
+- }
+- *iarg = res;
+- break;
+- }
+-
+- case DECODER_SET_NORM:
+- {
+- int *iarg = arg;
+-
+- short int hoff = 0, voff = 0, w = 0, h = 0;
+-
+- v4l_dbg(1, debug, client, "set norm\n");
+-
+- switch (*iarg) {
+- case VIDEO_MODE_NTSC:
+- v4l_dbg(1, debug, client, "NTSC\n");
+- decoder->reg[REG_ADDR(0x06)] =
+- SAA_7114_NTSC_HSYNC_START;
+- decoder->reg[REG_ADDR(0x07)] =
+- SAA_7114_NTSC_HSYNC_STOP;
+-
+- decoder->reg[REG_ADDR(0x08)] = decoder->playback ? 0x7c : 0xb8; // PLL free when playback, PLL close when capture
+-
+- decoder->reg[REG_ADDR(0x0e)] = 0x85;
+- decoder->reg[REG_ADDR(0x0f)] = 0x24;
+-
+- hoff = SAA_7114_NTSC_HOFFSET;
+- voff = SAA_7114_NTSC_VOFFSET;
+- w = SAA_7114_NTSC_WIDTH;
+- h = SAA_7114_NTSC_HEIGHT;
+-
+- break;
+-
+- case VIDEO_MODE_PAL:
+- v4l_dbg(1, debug, client, "PAL\n");
+- decoder->reg[REG_ADDR(0x06)] =
+- SAA_7114_PAL_HSYNC_START;
+- decoder->reg[REG_ADDR(0x07)] =
+- SAA_7114_PAL_HSYNC_STOP;
+-
+- decoder->reg[REG_ADDR(0x08)] = decoder->playback ? 0x7c : 0xb8; // PLL free when playback, PLL close when capture
+-
+- decoder->reg[REG_ADDR(0x0e)] = 0x81;
+- decoder->reg[REG_ADDR(0x0f)] = 0x24;
+-
+- hoff = SAA_7114_PAL_HOFFSET;
+- voff = SAA_7114_PAL_VOFFSET;
+- w = SAA_7114_PAL_WIDTH;
+- h = SAA_7114_PAL_HEIGHT;
+-
+- break;
+-
+- default:
+- v4l_dbg(1, debug, client, "Unknown video mode\n");
+- return -EINVAL;
+- }
+-
+-
+- decoder->reg[REG_ADDR(0x94)] = LOBYTE(hoff); // hoffset low
+- decoder->reg[REG_ADDR(0x95)] = HIBYTE(hoff) & 0x0f; // hoffset high
+- decoder->reg[REG_ADDR(0x96)] = LOBYTE(w); // width low
+- decoder->reg[REG_ADDR(0x97)] = HIBYTE(w) & 0x0f; // width high
+- decoder->reg[REG_ADDR(0x98)] = LOBYTE(voff); // voffset low
+- decoder->reg[REG_ADDR(0x99)] = HIBYTE(voff) & 0x0f; // voffset high
+- decoder->reg[REG_ADDR(0x9a)] = LOBYTE(h + 2); // height low
+- decoder->reg[REG_ADDR(0x9b)] = HIBYTE(h + 2) & 0x0f; // height high
+- decoder->reg[REG_ADDR(0x9c)] = LOBYTE(w); // out width low
+- decoder->reg[REG_ADDR(0x9d)] = HIBYTE(w) & 0x0f; // out width high
+- decoder->reg[REG_ADDR(0x9e)] = LOBYTE(h); // out height low
+- decoder->reg[REG_ADDR(0x9f)] = HIBYTE(h) & 0x0f; // out height high
+-
+- decoder->reg[REG_ADDR(0xc4)] = LOBYTE(hoff); // hoffset low
+- decoder->reg[REG_ADDR(0xc5)] = HIBYTE(hoff) & 0x0f; // hoffset high
+- decoder->reg[REG_ADDR(0xc6)] = LOBYTE(w); // width low
+- decoder->reg[REG_ADDR(0xc7)] = HIBYTE(w) & 0x0f; // width high
+- decoder->reg[REG_ADDR(0xc8)] = LOBYTE(voff); // voffset low
+- decoder->reg[REG_ADDR(0xc9)] = HIBYTE(voff) & 0x0f; // voffset high
+- decoder->reg[REG_ADDR(0xca)] = LOBYTE(h + 2); // height low
+- decoder->reg[REG_ADDR(0xcb)] = HIBYTE(h + 2) & 0x0f; // height high
+- decoder->reg[REG_ADDR(0xcc)] = LOBYTE(w); // out width low
+- decoder->reg[REG_ADDR(0xcd)] = HIBYTE(w) & 0x0f; // out width high
+- decoder->reg[REG_ADDR(0xce)] = LOBYTE(h); // out height low
+- decoder->reg[REG_ADDR(0xcf)] = HIBYTE(h) & 0x0f; // out height high
+-
+-
+- saa7114_write(client, 0x80, 0x06); // i-port and scaler back end clock selection, task A&B off
+- saa7114_write(client, 0x88, 0xd8); // sw reset scaler
+- saa7114_write(client, 0x88, 0xf8); // sw reset scaler release
+-
+- saa7114_write_block(client, decoder->reg + (0x06 << 1),
+- 3 << 1);
+- saa7114_write_block(client, decoder->reg + (0x0e << 1),
+- 2 << 1);
+- saa7114_write_block(client, decoder->reg + (0x5a << 1),
+- 2 << 1);
+-
+- saa7114_write_block(client, decoder->reg + (0x94 << 1),
+- (0x9f + 1 - 0x94) << 1);
+- saa7114_write_block(client, decoder->reg + (0xc4 << 1),
+- (0xcf + 1 - 0xc4) << 1);
+-
+- saa7114_write(client, 0x88, 0xd8); // sw reset scaler
+- saa7114_write(client, 0x88, 0xf8); // sw reset scaler release
+- saa7114_write(client, 0x80, 0x36); // i-port and scaler back end clock selection
+-
+- decoder->norm = *iarg;
+- break;
+- }
+-
+- case DECODER_SET_INPUT:
+- {
+- int *iarg = arg;
+-
+- v4l_dbg(1, debug, client, "set input (%d)\n", *iarg);
+- if (*iarg < 0 || *iarg > 7) {
+- return -EINVAL;
+- }
+-
+- if (decoder->input != *iarg) {
+- v4l_dbg(1, debug, client, "now setting %s input\n",
+- *iarg >= 6 ? "S-Video" : "Composite");
+- decoder->input = *iarg;
+-
+- /* select mode */
+- decoder->reg[REG_ADDR(0x02)] =
+- (decoder->
+- reg[REG_ADDR(0x02)] & 0xf0) | (decoder->
+- input <
+- 6 ? 0x0 : 0x9);
+- saa7114_write(client, 0x02,
+- decoder->reg[REG_ADDR(0x02)]);
+-
+- /* bypass chrominance trap for modes 6..9 */
+- decoder->reg[REG_ADDR(0x09)] =
+- (decoder->
+- reg[REG_ADDR(0x09)] & 0x7f) | (decoder->
+- input <
+- 6 ? 0x0 :
+- 0x80);
+- saa7114_write(client, 0x09,
+- decoder->reg[REG_ADDR(0x09)]);
+-
+- decoder->reg[REG_ADDR(0x0e)] =
+- decoder->input <
+- 6 ? decoder->
+- reg[REG_ADDR(0x0e)] | 1 : decoder->
+- reg[REG_ADDR(0x0e)] & ~1;
+- saa7114_write(client, 0x0e,
+- decoder->reg[REG_ADDR(0x0e)]);
+- }
+- break;
+- }
+-
+- case DECODER_SET_OUTPUT:
+- {
+- int *iarg = arg;
+-
+- v4l_dbg(1, debug, client, "set output\n");
+-
+- /* not much choice of outputs */
+- if (*iarg != 0) {
+- return -EINVAL;
+- }
+- break;
+- }
+-
+- case DECODER_ENABLE_OUTPUT:
+- {
+- int *iarg = arg;
+- int enable = (*iarg != 0);
+-
+- v4l_dbg(1, debug, client, "%s output\n",
+- enable ? "enable" : "disable");
+-
+- decoder->playback = !enable;
+-
+- if (decoder->enable != enable) {
+- decoder->enable = enable;
+-
+- /* RJ: If output should be disabled (for
+- * playing videos), we also need a open PLL.
+- * The input is set to 0 (where no input
+- * source is connected), although this
+- * is not necessary.
+- *
+- * If output should be enabled, we have to
+- * reverse the above.
+- */
+-
+- if (decoder->enable) {
+- decoder->reg[REG_ADDR(0x08)] = 0xb8;
+- decoder->reg[REG_ADDR(0x12)] = 0xc9;
+- decoder->reg[REG_ADDR(0x13)] = 0x80;
+- decoder->reg[REG_ADDR(0x87)] = 0x01;
+- } else {
+- decoder->reg[REG_ADDR(0x08)] = 0x7c;
+- decoder->reg[REG_ADDR(0x12)] = 0x00;
+- decoder->reg[REG_ADDR(0x13)] = 0x00;
+- decoder->reg[REG_ADDR(0x87)] = 0x00;
+- }
+-
+- saa7114_write_block(client,
+- decoder->reg + (0x12 << 1),
+- 2 << 1);
+- saa7114_write(client, 0x08,
+- decoder->reg[REG_ADDR(0x08)]);
+- saa7114_write(client, 0x87,
+- decoder->reg[REG_ADDR(0x87)]);
+- saa7114_write(client, 0x88, 0xd8); // sw reset scaler
+- saa7114_write(client, 0x88, 0xf8); // sw reset scaler release
+- saa7114_write(client, 0x80, 0x36);
+-
+- }
+- break;
+- }
+-
+- case DECODER_SET_PICTURE:
+- {
+- struct video_picture *pic = arg;
+-
+- v4l_dbg(1, debug, client,
+- "decoder set picture bright=%d contrast=%d saturation=%d hue=%d\n",
+- pic->brightness, pic->contrast, pic->colour, pic->hue);
+-
+- if (decoder->bright != pic->brightness) {
+- /* We want 0 to 255 we get 0-65535 */
+- decoder->bright = pic->brightness;
+- saa7114_write(client, 0x0a, decoder->bright >> 8);
+- }
+- if (decoder->contrast != pic->contrast) {
+- /* We want 0 to 127 we get 0-65535 */
+- decoder->contrast = pic->contrast;
+- saa7114_write(client, 0x0b,
+- decoder->contrast >> 9);
+- }
+- if (decoder->sat != pic->colour) {
+- /* We want 0 to 127 we get 0-65535 */
+- decoder->sat = pic->colour;
+- saa7114_write(client, 0x0c, decoder->sat >> 9);
+- }
+- if (decoder->hue != pic->hue) {
+- /* We want -128 to 127 we get 0-65535 */
+- decoder->hue = pic->hue;
+- saa7114_write(client, 0x0d,
+- (decoder->hue - 32768) >> 8);
+- }
+- break;
+- }
+-
+- default:
+- return -EINVAL;
+- }
+-
+- return 0;
+-}
+-
+-/* ----------------------------------------------------------------------- */
+-
+-static unsigned short normal_i2c[] = { 0x42 >> 1, 0x40 >> 1, I2C_CLIENT_END };
+-
+-I2C_CLIENT_INSMOD;
+-
+-static int saa7114_probe(struct i2c_client *client,
+- const struct i2c_device_id *id)
+-{
+- int i, err[30];
+- short int hoff = SAA_7114_NTSC_HOFFSET;
+- short int voff = SAA_7114_NTSC_VOFFSET;
+- short int w = SAA_7114_NTSC_WIDTH;
+- short int h = SAA_7114_NTSC_HEIGHT;
+- struct saa7114 *decoder;
+-
+- /* Check if the adapter supports the needed features */
+- if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+- return -ENODEV;
+-
+- v4l_info(client, "chip found @ 0x%x (%s)\n",
+- client->addr << 1, client->adapter->name);
+-
+- decoder = kzalloc(sizeof(struct saa7114), GFP_KERNEL);
+- if (decoder == NULL)
+- return -ENOMEM;
+- decoder->norm = VIDEO_MODE_NTSC;
+- decoder->input = -1;
+- decoder->enable = 1;
+- decoder->bright = 32768;
+- decoder->contrast = 32768;
+- decoder->hue = 32768;
+- decoder->sat = 32768;
+- decoder->playback = 0; // initially capture mode useda
+- i2c_set_clientdata(client, decoder);
+-
+- memcpy(decoder->reg, init, sizeof(init));
+-
+- decoder->reg[REG_ADDR(0x94)] = LOBYTE(hoff); // hoffset low
+- decoder->reg[REG_ADDR(0x95)] = HIBYTE(hoff) & 0x0f; // hoffset high
+- decoder->reg[REG_ADDR(0x96)] = LOBYTE(w); // width low
+- decoder->reg[REG_ADDR(0x97)] = HIBYTE(w) & 0x0f; // width high
+- decoder->reg[REG_ADDR(0x98)] = LOBYTE(voff); // voffset low
+- decoder->reg[REG_ADDR(0x99)] = HIBYTE(voff) & 0x0f; // voffset high
+- decoder->reg[REG_ADDR(0x9a)] = LOBYTE(h + 2); // height low
+- decoder->reg[REG_ADDR(0x9b)] = HIBYTE(h + 2) & 0x0f; // height high
+- decoder->reg[REG_ADDR(0x9c)] = LOBYTE(w); // out width low
+- decoder->reg[REG_ADDR(0x9d)] = HIBYTE(w) & 0x0f; // out width high
+- decoder->reg[REG_ADDR(0x9e)] = LOBYTE(h); // out height low
+- decoder->reg[REG_ADDR(0x9f)] = HIBYTE(h) & 0x0f; // out height high
+-
+- decoder->reg[REG_ADDR(0xc4)] = LOBYTE(hoff); // hoffset low
+- decoder->reg[REG_ADDR(0xc5)] = HIBYTE(hoff) & 0x0f; // hoffset high
+- decoder->reg[REG_ADDR(0xc6)] = LOBYTE(w); // width low
+- decoder->reg[REG_ADDR(0xc7)] = HIBYTE(w) & 0x0f; // width high
+- decoder->reg[REG_ADDR(0xc8)] = LOBYTE(voff); // voffset low
+- decoder->reg[REG_ADDR(0xc9)] = HIBYTE(voff) & 0x0f; // voffset high
+- decoder->reg[REG_ADDR(0xca)] = LOBYTE(h + 2); // height low
+- decoder->reg[REG_ADDR(0xcb)] = HIBYTE(h + 2) & 0x0f; // height high
+- decoder->reg[REG_ADDR(0xcc)] = LOBYTE(w); // out width low
+- decoder->reg[REG_ADDR(0xcd)] = HIBYTE(w) & 0x0f; // out width high
+- decoder->reg[REG_ADDR(0xce)] = LOBYTE(h); // out height low
+- decoder->reg[REG_ADDR(0xcf)] = HIBYTE(h) & 0x0f; // out height high
+-
+- decoder->reg[REG_ADDR(0xb8)] =
+- LOBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
+- decoder->reg[REG_ADDR(0xb9)] =
+- HIBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
+- decoder->reg[REG_ADDR(0xba)] =
+- LOBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
+- decoder->reg[REG_ADDR(0xbb)] =
+- HIBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
+-
+- decoder->reg[REG_ADDR(0xbc)] =
+- LOBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
+- decoder->reg[REG_ADDR(0xbd)] =
+- HIBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
+- decoder->reg[REG_ADDR(0xbe)] =
+- LOBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
+- decoder->reg[REG_ADDR(0xbf)] =
+- HIBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
+-
+- decoder->reg[REG_ADDR(0xe8)] =
+- LOBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
+- decoder->reg[REG_ADDR(0xe9)] =
+- HIBYTE(LOWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
+- decoder->reg[REG_ADDR(0xea)] =
+- LOBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
+- decoder->reg[REG_ADDR(0xeb)] =
+- HIBYTE(HIWORD(SAA_7114_VERTICAL_CHROMA_OFFSET));
+-
+- decoder->reg[REG_ADDR(0xec)] =
+- LOBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
+- decoder->reg[REG_ADDR(0xed)] =
+- HIBYTE(LOWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
+- decoder->reg[REG_ADDR(0xee)] =
+- LOBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
+- decoder->reg[REG_ADDR(0xef)] =
+- HIBYTE(HIWORD(SAA_7114_VERTICAL_LUMA_OFFSET));
+-
+-
+- decoder->reg[REG_ADDR(0x13)] = 0x80; // RTC0 on
+- decoder->reg[REG_ADDR(0x87)] = 0x01; // I-Port
+- decoder->reg[REG_ADDR(0x12)] = 0xc9; // RTS0
+-
+- decoder->reg[REG_ADDR(0x02)] = 0xc0; // set composite1 input, aveasy
+- decoder->reg[REG_ADDR(0x09)] = 0x00; // chrominance trap
+- decoder->reg[REG_ADDR(0x0e)] |= 1; // combfilter on
+-
+-
+- v4l_dbg(1, debug, client, "starting init\n");
+-
+- err[0] =
+- saa7114_write_block(client, decoder->reg + (0x20 << 1),
+- 0x10 << 1);
+- err[1] =
+- saa7114_write_block(client, decoder->reg + (0x30 << 1),
+- 0x10 << 1);
+- err[2] =
+- saa7114_write_block(client, decoder->reg + (0x63 << 1),
+- (0x7f + 1 - 0x63) << 1);
+- err[3] =
+- saa7114_write_block(client, decoder->reg + (0x89 << 1),
+- 6 << 1);
+- err[4] =
+- saa7114_write_block(client, decoder->reg + (0xb8 << 1),
+- 8 << 1);
+- err[5] =
+- saa7114_write_block(client, decoder->reg + (0xe8 << 1),
+- 8 << 1);
+-
+-
+- for (i = 0; i <= 5; i++) {
+- if (err[i] < 0) {
+- v4l_dbg(1, debug, client,
+- "init error %d at stage %d, leaving attach.\n",
+- i, err[i]);
+- kfree(decoder);
+- return -EIO;
+- }
+- }
+-
+- for (i = 6; i < 8; i++) {
+- v4l_dbg(1, debug, client,
+- "reg[0x%02x] = 0x%02x (0x%02x)\n",
+- i, saa7114_read(client, i),
+- decoder->reg[REG_ADDR(i)]);
+- }
+-
+- v4l_dbg(1, debug, client,
+- "performing decoder reset sequence\n");
+-
+- err[6] = saa7114_write(client, 0x80, 0x06); // i-port and scaler backend clock selection, task A&B off
+- err[7] = saa7114_write(client, 0x88, 0xd8); // sw reset scaler
+- err[8] = saa7114_write(client, 0x88, 0xf8); // sw reset scaler release
+-
+- for (i = 6; i <= 8; i++) {
+- if (err[i] < 0) {
+- v4l_dbg(1, debug, client,
+- "init error %d at stage %d, leaving attach.\n",
+- i, err[i]);
+- kfree(decoder);
+- return -EIO;
+- }
+- }
+-
+- v4l_dbg(1, debug, client, "performing the rest of init\n");
+-
+- err[9] = saa7114_write(client, 0x01, decoder->reg[REG_ADDR(0x01)]);
+- err[10] = saa7114_write_block(client, decoder->reg + (0x03 << 1), (0x1e + 1 - 0x03) << 1); // big seq
+- err[11] = saa7114_write_block(client, decoder->reg + (0x40 << 1), (0x5f + 1 - 0x40) << 1); // slicer
+- err[12] = saa7114_write_block(client, decoder->reg + (0x81 << 1), 2 << 1); // ?
+- err[13] = saa7114_write_block(client, decoder->reg + (0x83 << 1), 5 << 1); // ?
+- err[14] = saa7114_write_block(client, decoder->reg + (0x90 << 1), 4 << 1); // Task A
+- err[15] =
+- saa7114_write_block(client, decoder->reg + (0x94 << 1),
+- 12 << 1);
+- err[16] =
+- saa7114_write_block(client, decoder->reg + (0xa0 << 1),
+- 8 << 1);
+- err[17] =
+- saa7114_write_block(client, decoder->reg + (0xa8 << 1),
+- 8 << 1);
+- err[18] =
+- saa7114_write_block(client, decoder->reg + (0xb0 << 1),
+- 8 << 1);
+- err[19] = saa7114_write_block(client, decoder->reg + (0xc0 << 1), 4 << 1); // Task B
+- err[15] =
+- saa7114_write_block(client, decoder->reg + (0xc4 << 1),
+- 12 << 1);
+- err[16] =
+- saa7114_write_block(client, decoder->reg + (0xd0 << 1),
+- 8 << 1);
+- err[17] =
+- saa7114_write_block(client, decoder->reg + (0xd8 << 1),
+- 8 << 1);
+- err[18] =
+- saa7114_write_block(client, decoder->reg + (0xe0 << 1),
+- 8 << 1);
+-
+- for (i = 9; i <= 18; i++) {
+- if (err[i] < 0) {
+- v4l_dbg(1, debug, client,
+- "init error %d at stage %d, leaving attach.\n",
+- i, err[i]);
+- kfree(decoder);
+- return -EIO;
+- }
+- }
+-
+-
+- for (i = 6; i < 8; i++) {
+- v4l_dbg(1, debug, client,
+- "reg[0x%02x] = 0x%02x (0x%02x)\n",
+- i, saa7114_read(client, i),
+- decoder->reg[REG_ADDR(i)]);
+- }
+-
+-
+- for (i = 0x11; i <= 0x13; i++) {
+- v4l_dbg(1, debug, client,
+- "reg[0x%02x] = 0x%02x (0x%02x)\n",
+- i, saa7114_read(client, i),
+- decoder->reg[REG_ADDR(i)]);
+- }
+-
+-
+- v4l_dbg(1, debug, client, "setting video input\n");
+-
+- err[19] =
+- saa7114_write(client, 0x02, decoder->reg[REG_ADDR(0x02)]);
+- err[20] =
+- saa7114_write(client, 0x09, decoder->reg[REG_ADDR(0x09)]);
+- err[21] =
+- saa7114_write(client, 0x0e, decoder->reg[REG_ADDR(0x0e)]);
+-
+- for (i = 19; i <= 21; i++) {
+- if (err[i] < 0) {
+- v4l_dbg(1, debug, client,
+- "init error %d at stage %d, leaving attach.\n",
+- i, err[i]);
+- kfree(decoder);
+- return -EIO;
+- }
+- }
+-
+- v4l_dbg(1, debug, client, "performing decoder reset sequence\n");
+-
+- err[22] = saa7114_write(client, 0x88, 0xd8); // sw reset scaler
+- err[23] = saa7114_write(client, 0x88, 0xf8); // sw reset scaler release
+- err[24] = saa7114_write(client, 0x80, 0x36); // i-port and scaler backend clock selection, task A&B off
+-
+-
+- for (i = 22; i <= 24; i++) {
+- if (err[i] < 0) {
+- v4l_dbg(1, debug, client,
+- "init error %d at stage %d, leaving attach.\n",
+- i, err[i]);
+- kfree(decoder);
+- return -EIO;
+- }
+- }
+-
+- err[25] = saa7114_write(client, 0x06, init[REG_ADDR(0x06)]);
+- err[26] = saa7114_write(client, 0x07, init[REG_ADDR(0x07)]);
+- err[27] = saa7114_write(client, 0x10, init[REG_ADDR(0x10)]);
+-
+- v4l_dbg(1, debug, client, "chip version %x, decoder status 0x%02x\n",
+- saa7114_read(client, 0x00) >> 4,
+- saa7114_read(client, 0x1f));
+- v4l_dbg(1, debug, client,
+- "power save control: 0x%02x, scaler status: 0x%02x\n",
+- saa7114_read(client, 0x88),
+- saa7114_read(client, 0x8f));
+-
+-
+- for (i = 0x94; i < 0x96; i++) {
+- v4l_dbg(1, debug, client,
+- "reg[0x%02x] = 0x%02x (0x%02x)\n",
+- i, saa7114_read(client, i),
+- decoder->reg[REG_ADDR(i)]);
+- }
+-
+- //i = saa7114_write_block(client, init, sizeof(init));
+- return 0;
+-}
+-
+-static int saa7114_remove(struct i2c_client *client)
+-{
+- kfree(i2c_get_clientdata(client));
+- return 0;
+-}
+-
+-/* ----------------------------------------------------------------------- */
+-
+-static const struct i2c_device_id saa7114_id[] = {
+- { "saa7114_old", 0 }, /* "saa7114" maps to the saa7115 driver */
+- { }
+-};
+-MODULE_DEVICE_TABLE(i2c, saa7114_id);
+-
+-static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+- .name = "saa7114",
+- .driverid = I2C_DRIVERID_SAA7114,
+- .command = saa7114_command,
+- .probe = saa7114_probe,
+- .remove = saa7114_remove,
+- .id_table = saa7114_id,
+-};
+diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
+index 46c796c..cebf159 100644
+--- a/drivers/media/video/saa7115.c
++++ b/drivers/media/video/saa7115.c
+@@ -778,7 +778,7 @@ static int saa711x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+ break;
+
+ case V4L2_CID_HUE:
+- if (ctrl->value < -127 || ctrl->value > 127) {
++ if (ctrl->value < -128 || ctrl->value > 127) {
+ v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
+ return -ERANGE;
+ }
+@@ -931,8 +931,8 @@ static void saa711x_set_v4lstd(struct v4l2_subdev *sd, v4l2_std_id std)
+ /* Prevent unnecessary standard changes. During a standard
+ change the I-Port is temporarily disabled. Any devices
+ reading from that port can get confused.
+- Note that VIDIOC_S_STD is also used to switch from
+- radio to TV mode, so if a VIDIOC_S_STD is broadcast to
++ Note that s_std is also used to switch from
++ radio to TV mode, so if a s_std is broadcast to
+ all I2C devices then you do not want to have an unwanted
+ side-effect here. */
+ if (std == state->std)
+@@ -1206,10 +1206,12 @@ static int saa711x_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+ {
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
++ return v4l2_ctrl_query_fill(qc, 0, 255, 1, 128);
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
++ return v4l2_ctrl_query_fill(qc, 0, 127, 1, 64);
+ case V4L2_CID_HUE:
+- return v4l2_ctrl_query_fill_std(qc);
++ return v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
+ default:
+ return -EINVAL;
+ }
+@@ -1308,11 +1310,12 @@ static int saa711x_s_stream(struct v4l2_subdev *sd, int enable)
+ v4l2_dbg(1, debug, sd, "%s output\n",
+ enable ? "enable" : "disable");
+
+- if (state->enable != enable) {
+- state->enable = enable;
+- saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED,
+- state->enable);
+- }
++ if (state->enable == enable)
++ return 0;
++ state->enable = enable;
++ if (!saa711x_has_reg(state->ident, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED))
++ return 0;
++ saa711x_write(sd, R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, state->enable);
+ return 0;
+ }
+
+@@ -1370,6 +1373,47 @@ static int saa711x_g_vbi_data(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_dat
+ }
+ }
+
++static int saa711x_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
++{
++ struct saa711x_state *state = to_state(sd);
++ int reg1e;
++
++ *std = V4L2_STD_ALL;
++ if (state->ident != V4L2_IDENT_SAA7115)
++ return 0;
++ reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
++
++ switch (reg1e & 0x03) {
++ case 1:
++ *std = V4L2_STD_NTSC;
++ break;
++ case 2:
++ *std = V4L2_STD_PAL;
++ break;
++ case 3:
++ *std = V4L2_STD_SECAM;
++ break;
++ default:
++ break;
++ }
++ return 0;
++}
++
++static int saa711x_g_input_status(struct v4l2_subdev *sd, u32 *status)
++{
++ struct saa711x_state *state = to_state(sd);
++ int reg1e = 0x80;
++ int reg1f;
++
++ *status = V4L2_IN_ST_NO_SIGNAL;
++ if (state->ident == V4L2_IDENT_SAA7115)
++ reg1e = saa711x_read(sd, R_1E_STATUS_BYTE_1_VD_DEC);
++ reg1f = saa711x_read(sd, R_1F_STATUS_BYTE_2_VD_DEC);
++ if ((reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80)
++ *status = 0;
++ return 0;
++}
++
+ #ifdef CONFIG_VIDEO_ADV_DEBUG
+ static int saa711x_g_register(struct v4l2_subdev *sd, struct v4l2_dbg_register *reg)
+ {
+@@ -1493,6 +1537,8 @@ static const struct v4l2_subdev_video_ops saa711x_video_ops = {
+ .g_vbi_data = saa711x_g_vbi_data,
+ .decode_vbi_line = saa711x_decode_vbi_line,
+ .s_stream = saa711x_s_stream,
++ .querystd = saa711x_querystd,
++ .g_input_status = saa711x_g_input_status,
+ };
+
+ static const struct v4l2_subdev_ops saa711x_ops = {
+diff --git a/drivers/media/video/saa7127.c b/drivers/media/video/saa7127.c
+index 05221d4..128bb8b 100644
+--- a/drivers/media/video/saa7127.c
++++ b/drivers/media/video/saa7127.c
+@@ -810,7 +810,6 @@ MODULE_DEVICE_TABLE(i2c, saa7127_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "saa7127",
+- .driverid = I2C_DRIVERID_SAA7127,
+ .probe = saa7127_probe,
+ .remove = saa7127_remove,
+ .id_table = saa7127_id,
+diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig
+index fc2164e..0ba6898 100644
+--- a/drivers/media/video/saa7134/Kconfig
++++ b/drivers/media/video/saa7134/Kconfig
+@@ -6,6 +6,7 @@ config VIDEO_SAA7134
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select CRC32
++ select VIDEO_SAA6588 if VIDEO_HELPER_CHIPS_AUTO
+ ---help---
+ This is a video4linux driver for Philips SAA713x based
+ TV cards.
+@@ -35,8 +36,16 @@ config VIDEO_SAA7134_DVB
+ select DVB_TDA10086 if !DVB_FE_CUSTOMISE
+ select DVB_TDA826X if !DVB_FE_CUSTOMISE
+ select DVB_ISL6421 if !DVB_FE_CUSTOMISE
+- select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMIZE
+- select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMIZE
++ select DVB_ISL6405 if !DVB_FE_CUSTOMISE
++ select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_SIMPLE if !MEDIA_TUNER_CUSTOMISE
++ select DVB_ZL10036 if !DVB_FE_CUSTOMISE
++ select DVB_MT312 if !DVB_FE_CUSTOMISE
++ select DVB_LNBP21 if !DVB_FE_CUSTOMISE
++ select DVB_ZL10353 if !DVB_FE_CUSTOMISE
++ select DVB_LGDT3305 if !DVB_FE_CUSTOMISE
++ select MEDIA_TUNER_TDA18271 if !MEDIA_TUNER_CUSTOMISE
++ select MEDIA_TUNER_TDA8290 if !MEDIA_TUNER_CUSTOMISE
+ ---help---
+ This adds support for DVB cards based on the
+ Philips saa7134 chip.
+diff --git a/drivers/media/video/saa7134/saa6752hs.c b/drivers/media/video/saa7134/saa6752hs.c
+index 1fee6e8..dc2213e 100644
+--- a/drivers/media/video/saa7134/saa6752hs.c
++++ b/drivers/media/video/saa7134/saa6752hs.c
+@@ -33,9 +33,10 @@
+ #include <linux/i2c.h>
+ #include <linux/types.h>
+ #include <linux/videodev2.h>
++#include <media/v4l2-device.h>
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-chip-ident.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <media/v4l2-i2c-drv.h>
+ #include <linux/init.h>
+ #include <linux/crc32.h>
+
+@@ -44,10 +45,6 @@
+ #define MPEG_TOTAL_TARGET_BITRATE_MAX 27000
+ #define MPEG_PID_MAX ((1 << 14) - 1)
+
+-/* Addresses to scan */
+-static unsigned short normal_i2c[] = {0x20, I2C_CLIENT_END};
+-
+-I2C_CLIENT_INSMOD;
+
+ MODULE_DESCRIPTION("device driver for saa6752hs MPEG2 encoder");
+ MODULE_AUTHOR("Andrew de Quincey");
+@@ -95,6 +92,7 @@ static const struct v4l2_format v4l2_format_table[] =
+ };
+
+ struct saa6752hs_state {
++ struct v4l2_subdev sd;
+ int chip;
+ u32 revision;
+ int has_ac3;
+@@ -115,6 +113,11 @@ enum saa6752hs_command {
+ SAA6752HS_COMMAND_MAX
+ };
+
++static inline struct saa6752hs_state *to_state(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct saa6752hs_state, sd);
++}
++
+ /* ---------------------------------------------------------------------- */
+
+ static u8 PAT[] = {
+@@ -360,185 +363,191 @@ static int saa6752hs_set_bitrate(struct i2c_client *client,
+ return 0;
+ }
+
+-static void saa6752hs_set_subsampling(struct i2c_client *client,
+- struct v4l2_format *f)
+-{
+- struct saa6752hs_state *h = i2c_get_clientdata(client);
+- int dist_352, dist_480, dist_720;
+-
+- /*
+- FIXME: translate and round width/height into EMPRESS
+- subsample type:
+
+- type | PAL | NTSC
+- ---------------------------
+- SIF | 352x288 | 352x240
+- 1/2 D1 | 352x576 | 352x480
+- 2/3 D1 | 480x576 | 480x480
+- D1 | 720x576 | 720x480
+- */
+-
+- dist_352 = abs(f->fmt.pix.width - 352);
+- dist_480 = abs(f->fmt.pix.width - 480);
+- dist_720 = abs(f->fmt.pix.width - 720);
+- if (dist_720 < dist_480) {
+- f->fmt.pix.width = 720;
+- f->fmt.pix.height = 576;
+- h->video_format = SAA6752HS_VF_D1;
+- }
+- else if (dist_480 < dist_352) {
+- f->fmt.pix.width = 480;
+- f->fmt.pix.height = 576;
+- h->video_format = SAA6752HS_VF_2_3_D1;
+- }
+- else {
+- f->fmt.pix.width = 352;
+- if (abs(f->fmt.pix.height - 576) <
+- abs(f->fmt.pix.height - 288)) {
+- f->fmt.pix.height = 576;
+- h->video_format = SAA6752HS_VF_1_2_D1;
+- }
+- else {
+- f->fmt.pix.height = 288;
+- h->video_format = SAA6752HS_VF_SIF;
+- }
++static int get_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params,
++ struct v4l2_ext_control *ctrl)
++{
++ switch (ctrl->id) {
++ case V4L2_CID_MPEG_STREAM_TYPE:
++ ctrl->value = V4L2_MPEG_STREAM_TYPE_MPEG2_TS;
++ break;
++ case V4L2_CID_MPEG_STREAM_PID_PMT:
++ ctrl->value = params->ts_pid_pmt;
++ break;
++ case V4L2_CID_MPEG_STREAM_PID_AUDIO:
++ ctrl->value = params->ts_pid_audio;
++ break;
++ case V4L2_CID_MPEG_STREAM_PID_VIDEO:
++ ctrl->value = params->ts_pid_video;
++ break;
++ case V4L2_CID_MPEG_STREAM_PID_PCR:
++ ctrl->value = params->ts_pid_pcr;
++ break;
++ case V4L2_CID_MPEG_AUDIO_ENCODING:
++ ctrl->value = params->au_encoding;
++ break;
++ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
++ ctrl->value = params->au_l2_bitrate;
++ break;
++ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
++ if (!has_ac3)
++ return -EINVAL;
++ ctrl->value = params->au_ac3_bitrate;
++ break;
++ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
++ ctrl->value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
++ break;
++ case V4L2_CID_MPEG_VIDEO_ENCODING:
++ ctrl->value = V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
++ break;
++ case V4L2_CID_MPEG_VIDEO_ASPECT:
++ ctrl->value = params->vi_aspect;
++ break;
++ case V4L2_CID_MPEG_VIDEO_BITRATE:
++ ctrl->value = params->vi_bitrate * 1000;
++ break;
++ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
++ ctrl->value = params->vi_bitrate_peak * 1000;
++ break;
++ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
++ ctrl->value = params->vi_bitrate_mode;
++ break;
++ default:
++ return -EINVAL;
+ }
++ return 0;
+ }
+
+-
+ static int handle_ctrl(int has_ac3, struct saa6752hs_mpeg_params *params,
+- struct v4l2_ext_control *ctrl, unsigned int cmd)
++ struct v4l2_ext_control *ctrl, int set)
+ {
+ int old = 0, new;
+- int set = (cmd == VIDIOC_S_EXT_CTRLS);
+
+ new = ctrl->value;
+ switch (ctrl->id) {
+- case V4L2_CID_MPEG_STREAM_TYPE:
+- old = V4L2_MPEG_STREAM_TYPE_MPEG2_TS;
+- if (set && new != old)
+- return -ERANGE;
+- new = old;
+- break;
+- case V4L2_CID_MPEG_STREAM_PID_PMT:
+- old = params->ts_pid_pmt;
+- if (set && new > MPEG_PID_MAX)
+- return -ERANGE;
+- if (new > MPEG_PID_MAX)
+- new = MPEG_PID_MAX;
+- params->ts_pid_pmt = new;
+- break;
+- case V4L2_CID_MPEG_STREAM_PID_AUDIO:
+- old = params->ts_pid_audio;
+- if (set && new > MPEG_PID_MAX)
+- return -ERANGE;
+- if (new > MPEG_PID_MAX)
+- new = MPEG_PID_MAX;
+- params->ts_pid_audio = new;
+- break;
+- case V4L2_CID_MPEG_STREAM_PID_VIDEO:
+- old = params->ts_pid_video;
+- if (set && new > MPEG_PID_MAX)
+- return -ERANGE;
+- if (new > MPEG_PID_MAX)
+- new = MPEG_PID_MAX;
+- params->ts_pid_video = new;
+- break;
+- case V4L2_CID_MPEG_STREAM_PID_PCR:
+- old = params->ts_pid_pcr;
+- if (set && new > MPEG_PID_MAX)
+- return -ERANGE;
+- if (new > MPEG_PID_MAX)
+- new = MPEG_PID_MAX;
+- params->ts_pid_pcr = new;
+- break;
+- case V4L2_CID_MPEG_AUDIO_ENCODING:
+- old = params->au_encoding;
+- if (set && new != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 &&
+- (!has_ac3 || new != V4L2_MPEG_AUDIO_ENCODING_AC3))
+- return -ERANGE;
+- new = old;
+- break;
+- case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+- old = params->au_l2_bitrate;
+- if (set && new != V4L2_MPEG_AUDIO_L2_BITRATE_256K &&
+- new != V4L2_MPEG_AUDIO_L2_BITRATE_384K)
+- return -ERANGE;
+- if (new <= V4L2_MPEG_AUDIO_L2_BITRATE_256K)
+- new = V4L2_MPEG_AUDIO_L2_BITRATE_256K;
+- else
+- new = V4L2_MPEG_AUDIO_L2_BITRATE_384K;
+- params->au_l2_bitrate = new;
+- break;
+- case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+- if (!has_ac3)
+- return -EINVAL;
+- old = params->au_ac3_bitrate;
+- if (set && new != V4L2_MPEG_AUDIO_AC3_BITRATE_256K &&
+- new != V4L2_MPEG_AUDIO_AC3_BITRATE_384K)
+- return -ERANGE;
+- if (new <= V4L2_MPEG_AUDIO_AC3_BITRATE_256K)
+- new = V4L2_MPEG_AUDIO_AC3_BITRATE_256K;
+- else
+- new = V4L2_MPEG_AUDIO_AC3_BITRATE_384K;
+- params->au_ac3_bitrate = new;
+- break;
+- case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+- old = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
+- if (set && new != old)
+- return -ERANGE;
+- new = old;
+- break;
+- case V4L2_CID_MPEG_VIDEO_ENCODING:
+- old = V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
+- if (set && new != old)
+- return -ERANGE;
+- new = old;
+- break;
+- case V4L2_CID_MPEG_VIDEO_ASPECT:
+- old = params->vi_aspect;
+- if (set && new != V4L2_MPEG_VIDEO_ASPECT_16x9 &&
+- new != V4L2_MPEG_VIDEO_ASPECT_4x3)
+- return -ERANGE;
+- if (new != V4L2_MPEG_VIDEO_ASPECT_16x9)
+- new = V4L2_MPEG_VIDEO_ASPECT_4x3;
+- params->vi_aspect = new;
+- break;
+- case V4L2_CID_MPEG_VIDEO_BITRATE:
+- old = params->vi_bitrate * 1000;
+- new = 1000 * (new / 1000);
+- if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
+- return -ERANGE;
+- if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
+- new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000;
+- params->vi_bitrate = new / 1000;
+- break;
+- case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+- old = params->vi_bitrate_peak * 1000;
+- new = 1000 * (new / 1000);
+- if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
+- return -ERANGE;
+- if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
+- new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000;
+- params->vi_bitrate_peak = new / 1000;
+- break;
+- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+- old = params->vi_bitrate_mode;
+- params->vi_bitrate_mode = new;
+- break;
+- default:
++ case V4L2_CID_MPEG_STREAM_TYPE:
++ old = V4L2_MPEG_STREAM_TYPE_MPEG2_TS;
++ if (set && new != old)
++ return -ERANGE;
++ new = old;
++ break;
++ case V4L2_CID_MPEG_STREAM_PID_PMT:
++ old = params->ts_pid_pmt;
++ if (set && new > MPEG_PID_MAX)
++ return -ERANGE;
++ if (new > MPEG_PID_MAX)
++ new = MPEG_PID_MAX;
++ params->ts_pid_pmt = new;
++ break;
++ case V4L2_CID_MPEG_STREAM_PID_AUDIO:
++ old = params->ts_pid_audio;
++ if (set && new > MPEG_PID_MAX)
++ return -ERANGE;
++ if (new > MPEG_PID_MAX)
++ new = MPEG_PID_MAX;
++ params->ts_pid_audio = new;
++ break;
++ case V4L2_CID_MPEG_STREAM_PID_VIDEO:
++ old = params->ts_pid_video;
++ if (set && new > MPEG_PID_MAX)
++ return -ERANGE;
++ if (new > MPEG_PID_MAX)
++ new = MPEG_PID_MAX;
++ params->ts_pid_video = new;
++ break;
++ case V4L2_CID_MPEG_STREAM_PID_PCR:
++ old = params->ts_pid_pcr;
++ if (set && new > MPEG_PID_MAX)
++ return -ERANGE;
++ if (new > MPEG_PID_MAX)
++ new = MPEG_PID_MAX;
++ params->ts_pid_pcr = new;
++ break;
++ case V4L2_CID_MPEG_AUDIO_ENCODING:
++ old = params->au_encoding;
++ if (set && new != V4L2_MPEG_AUDIO_ENCODING_LAYER_2 &&
++ (!has_ac3 || new != V4L2_MPEG_AUDIO_ENCODING_AC3))
++ return -ERANGE;
++ new = old;
++ break;
++ case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
++ old = params->au_l2_bitrate;
++ if (set && new != V4L2_MPEG_AUDIO_L2_BITRATE_256K &&
++ new != V4L2_MPEG_AUDIO_L2_BITRATE_384K)
++ return -ERANGE;
++ if (new <= V4L2_MPEG_AUDIO_L2_BITRATE_256K)
++ new = V4L2_MPEG_AUDIO_L2_BITRATE_256K;
++ else
++ new = V4L2_MPEG_AUDIO_L2_BITRATE_384K;
++ params->au_l2_bitrate = new;
++ break;
++ case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
++ if (!has_ac3)
+ return -EINVAL;
++ old = params->au_ac3_bitrate;
++ if (set && new != V4L2_MPEG_AUDIO_AC3_BITRATE_256K &&
++ new != V4L2_MPEG_AUDIO_AC3_BITRATE_384K)
++ return -ERANGE;
++ if (new <= V4L2_MPEG_AUDIO_AC3_BITRATE_256K)
++ new = V4L2_MPEG_AUDIO_AC3_BITRATE_256K;
++ else
++ new = V4L2_MPEG_AUDIO_AC3_BITRATE_384K;
++ params->au_ac3_bitrate = new;
++ break;
++ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
++ old = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000;
++ if (set && new != old)
++ return -ERANGE;
++ new = old;
++ break;
++ case V4L2_CID_MPEG_VIDEO_ENCODING:
++ old = V4L2_MPEG_VIDEO_ENCODING_MPEG_2;
++ if (set && new != old)
++ return -ERANGE;
++ new = old;
++ break;
++ case V4L2_CID_MPEG_VIDEO_ASPECT:
++ old = params->vi_aspect;
++ if (set && new != V4L2_MPEG_VIDEO_ASPECT_16x9 &&
++ new != V4L2_MPEG_VIDEO_ASPECT_4x3)
++ return -ERANGE;
++ if (new != V4L2_MPEG_VIDEO_ASPECT_16x9)
++ new = V4L2_MPEG_VIDEO_ASPECT_4x3;
++ params->vi_aspect = new;
++ break;
++ case V4L2_CID_MPEG_VIDEO_BITRATE:
++ old = params->vi_bitrate * 1000;
++ new = 1000 * (new / 1000);
++ if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
++ return -ERANGE;
++ if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
++ new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000;
++ params->vi_bitrate = new / 1000;
++ break;
++ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
++ old = params->vi_bitrate_peak * 1000;
++ new = 1000 * (new / 1000);
++ if (set && new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
++ return -ERANGE;
++ if (new > MPEG_VIDEO_TARGET_BITRATE_MAX * 1000)
++ new = MPEG_VIDEO_TARGET_BITRATE_MAX * 1000;
++ params->vi_bitrate_peak = new / 1000;
++ break;
++ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
++ old = params->vi_bitrate_mode;
++ params->vi_bitrate_mode = new;
++ break;
++ default:
++ return -EINVAL;
+ }
+- if (cmd == VIDIOC_G_EXT_CTRLS)
+- ctrl->value = old;
+- else
+- ctrl->value = new;
++ ctrl->value = new;
+ return 0;
+ }
+
+-static int saa6752hs_qctrl(struct saa6752hs_state *h,
+- struct v4l2_queryctrl *qctrl)
++
++static int saa6752hs_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qctrl)
+ {
++ struct saa6752hs_state *h = to_state(sd);
+ struct saa6752hs_mpeg_params *params = &h->params;
+ int err;
+
+@@ -583,7 +592,7 @@ static int saa6752hs_qctrl(struct saa6752hs_state *h,
+ V4L2_MPEG_VIDEO_ASPECT_4x3);
+
+ case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+- err = v4l2_ctrl_query_fill_std(qctrl);
++ err = v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000);
+ if (err == 0 &&
+ params->vi_bitrate_mode ==
+ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR)
+@@ -597,12 +606,20 @@ static int saa6752hs_qctrl(struct saa6752hs_state *h,
+ V4L2_MPEG_STREAM_TYPE_MPEG2_TS);
+
+ case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
++ return v4l2_ctrl_query_fill(qctrl,
++ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
++ V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1,
++ V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+ case V4L2_CID_MPEG_VIDEO_BITRATE:
++ return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000);
+ case V4L2_CID_MPEG_STREAM_PID_PMT:
++ return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 16);
+ case V4L2_CID_MPEG_STREAM_PID_AUDIO:
++ return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 260);
+ case V4L2_CID_MPEG_STREAM_PID_VIDEO:
++ return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 256);
+ case V4L2_CID_MPEG_STREAM_PID_PCR:
+- return v4l2_ctrl_query_fill_std(qctrl);
++ return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 259);
+
+ default:
+ break;
+@@ -610,8 +627,7 @@ static int saa6752hs_qctrl(struct saa6752hs_state *h,
+ return -EINVAL;
+ }
+
+-static int saa6752hs_qmenu(struct saa6752hs_state *h,
+- struct v4l2_querymenu *qmenu)
++static int saa6752hs_querymenu(struct v4l2_subdev *sd, struct v4l2_querymenu *qmenu)
+ {
+ static const u32 mpeg_audio_encoding[] = {
+ V4L2_MPEG_AUDIO_ENCODING_LAYER_2,
+@@ -632,11 +648,12 @@ static int saa6752hs_qmenu(struct saa6752hs_state *h,
+ V4L2_MPEG_AUDIO_AC3_BITRATE_384K,
+ V4L2_CTRL_MENU_IDS_END
+ };
++ struct saa6752hs_state *h = to_state(sd);
+ struct v4l2_queryctrl qctrl;
+ int err;
+
+ qctrl.id = qmenu->id;
+- err = saa6752hs_qctrl(h, &qctrl);
++ err = saa6752hs_queryctrl(sd, &qctrl);
+ if (err)
+ return err;
+ switch (qmenu->id) {
+@@ -656,17 +673,16 @@ static int saa6752hs_qmenu(struct saa6752hs_state *h,
+ return v4l2_ctrl_query_menu(qmenu, &qctrl, NULL);
+ }
+
+-static int saa6752hs_init(struct i2c_client *client, u32 leading_null_bytes)
++static int saa6752hs_init(struct v4l2_subdev *sd, u32 leading_null_bytes)
+ {
+ unsigned char buf[9], buf2[4];
+- struct saa6752hs_state *h;
++ struct saa6752hs_state *h = to_state(sd);
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ unsigned size;
+ u32 crc;
+ unsigned char localPAT[256];
+ unsigned char localPMT[256];
+
+- h = i2c_get_clientdata(client);
+-
+ /* Set video format - must be done first as it resets other settings */
+ set_reg8(client, 0x41, h->video_format);
+
+@@ -762,7 +778,7 @@ static int saa6752hs_init(struct i2c_client *client, u32 leading_null_bytes)
+ buf[3] = 0x82;
+ buf[4] = 0xB0;
+ buf[5] = buf2[0];
+- switch(h->params.vi_aspect) {
++ switch (h->params.vi_aspect) {
+ case V4L2_MPEG_VIDEO_ASPECT_16x9:
+ buf[6] = buf2[1] | 0x40;
+ break;
+@@ -770,7 +786,6 @@ static int saa6752hs_init(struct i2c_client *client, u32 leading_null_bytes)
+ default:
+ buf[6] = buf2[1] & 0xBF;
+ break;
+- break;
+ }
+ buf[7] = buf2[2];
+ buf[8] = buf2[3];
+@@ -779,81 +794,162 @@ static int saa6752hs_init(struct i2c_client *client, u32 leading_null_bytes)
+ return 0;
+ }
+
+-static int
+-saa6752hs_command(struct i2c_client *client, unsigned int cmd, void *arg)
++static int saa6752hs_do_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls, int set)
+ {
+- struct saa6752hs_state *h = i2c_get_clientdata(client);
+- struct v4l2_ext_controls *ctrls = arg;
++ struct saa6752hs_state *h = to_state(sd);
+ struct saa6752hs_mpeg_params params;
+- int err = 0;
+ int i;
+
+- switch (cmd) {
+- case VIDIOC_INT_INIT:
+- /* apply settings and start encoder */
+- saa6752hs_init(client, *(u32 *)arg);
+- break;
+- case VIDIOC_S_EXT_CTRLS:
+- if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+- return -EINVAL;
+- /* fall through */
+- case VIDIOC_TRY_EXT_CTRLS:
+- case VIDIOC_G_EXT_CTRLS:
+- if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+- return -EINVAL;
+- params = h->params;
+- for (i = 0; i < ctrls->count; i++) {
+- err = handle_ctrl(h->has_ac3, &params, ctrls->controls + i, cmd);
+- if (err) {
+- ctrls->error_idx = i;
+- return err;
+- }
++ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
++ return -EINVAL;
++
++ params = h->params;
++ for (i = 0; i < ctrls->count; i++) {
++ int err = handle_ctrl(h->has_ac3, &params, ctrls->controls + i, set);
++
++ if (err) {
++ ctrls->error_idx = i;
++ return err;
+ }
+- h->params = params;
+- break;
+- case VIDIOC_QUERYCTRL:
+- return saa6752hs_qctrl(h, arg);
+- case VIDIOC_QUERYMENU:
+- return saa6752hs_qmenu(h, arg);
+- case VIDIOC_G_FMT:
+- {
+- struct v4l2_format *f = arg;
+-
+- if (h->video_format == SAA6752HS_VF_UNKNOWN)
+- h->video_format = SAA6752HS_VF_D1;
+- f->fmt.pix.width =
+- v4l2_format_table[h->video_format].fmt.pix.width;
+- f->fmt.pix.height =
+- v4l2_format_table[h->video_format].fmt.pix.height;
+- break ;
+ }
+- case VIDIOC_S_FMT:
+- {
+- struct v4l2_format *f = arg;
++ if (set)
++ h->params = params;
++ return 0;
++}
+
+- saa6752hs_set_subsampling(client, f);
+- break;
++static int saa6752hs_s_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls)
++{
++ return saa6752hs_do_ext_ctrls(sd, ctrls, 1);
++}
++
++static int saa6752hs_try_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls)
++{
++ return saa6752hs_do_ext_ctrls(sd, ctrls, 0);
++}
++
++static int saa6752hs_g_ext_ctrls(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls)
++{
++ struct saa6752hs_state *h = to_state(sd);
++ int i;
++
++ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
++ return -EINVAL;
++
++ for (i = 0; i < ctrls->count; i++) {
++ int err = get_ctrl(h->has_ac3, &h->params, ctrls->controls + i);
++
++ if (err) {
++ ctrls->error_idx = i;
++ return err;
++ }
+ }
+- case VIDIOC_S_STD:
+- h->standard = *((v4l2_std_id *) arg);
+- break;
++ return 0;
++}
+
+- case VIDIOC_DBG_G_CHIP_IDENT:
+- return v4l2_chip_ident_i2c_client(client,
+- arg, h->chip, h->revision);
++static int saa6752hs_g_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
++{
++ struct saa6752hs_state *h = to_state(sd);
+
+- default:
+- /* nothing */
+- break;
++ if (h->video_format == SAA6752HS_VF_UNKNOWN)
++ h->video_format = SAA6752HS_VF_D1;
++ f->fmt.pix.width =
++ v4l2_format_table[h->video_format].fmt.pix.width;
++ f->fmt.pix.height =
++ v4l2_format_table[h->video_format].fmt.pix.height;
++ return 0;
++}
++
++static int saa6752hs_s_fmt(struct v4l2_subdev *sd, struct v4l2_format *f)
++{
++ struct saa6752hs_state *h = to_state(sd);
++ int dist_352, dist_480, dist_720;
++
++ /*
++ FIXME: translate and round width/height into EMPRESS
++ subsample type:
++
++ type | PAL | NTSC
++ ---------------------------
++ SIF | 352x288 | 352x240
++ 1/2 D1 | 352x576 | 352x480
++ 2/3 D1 | 480x576 | 480x480
++ D1 | 720x576 | 720x480
++ */
++
++ dist_352 = abs(f->fmt.pix.width - 352);
++ dist_480 = abs(f->fmt.pix.width - 480);
++ dist_720 = abs(f->fmt.pix.width - 720);
++ if (dist_720 < dist_480) {
++ f->fmt.pix.width = 720;
++ f->fmt.pix.height = 576;
++ h->video_format = SAA6752HS_VF_D1;
++ } else if (dist_480 < dist_352) {
++ f->fmt.pix.width = 480;
++ f->fmt.pix.height = 576;
++ h->video_format = SAA6752HS_VF_2_3_D1;
++ } else {
++ f->fmt.pix.width = 352;
++ if (abs(f->fmt.pix.height - 576) <
++ abs(f->fmt.pix.height - 288)) {
++ f->fmt.pix.height = 576;
++ h->video_format = SAA6752HS_VF_1_2_D1;
++ } else {
++ f->fmt.pix.height = 288;
++ h->video_format = SAA6752HS_VF_SIF;
++ }
+ }
++ return 0;
++}
++
++static int saa6752hs_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
++{
++ struct saa6752hs_state *h = to_state(sd);
++
++ h->standard = std;
++ return 0;
++}
+
+- return err;
++static int saa6752hs_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct saa6752hs_state *h = to_state(sd);
++
++ return v4l2_chip_ident_i2c_client(client,
++ chip, h->chip, h->revision);
+ }
+
++/* ----------------------------------------------------------------------- */
++
++static const struct v4l2_subdev_core_ops saa6752hs_core_ops = {
++ .g_chip_ident = saa6752hs_g_chip_ident,
++ .init = saa6752hs_init,
++ .queryctrl = saa6752hs_queryctrl,
++ .querymenu = saa6752hs_querymenu,
++ .g_ext_ctrls = saa6752hs_g_ext_ctrls,
++ .s_ext_ctrls = saa6752hs_s_ext_ctrls,
++ .try_ext_ctrls = saa6752hs_try_ext_ctrls,
++};
++
++static const struct v4l2_subdev_tuner_ops saa6752hs_tuner_ops = {
++ .s_std = saa6752hs_s_std,
++};
++
++static const struct v4l2_subdev_video_ops saa6752hs_video_ops = {
++ .s_fmt = saa6752hs_s_fmt,
++ .g_fmt = saa6752hs_g_fmt,
++};
++
++static const struct v4l2_subdev_ops saa6752hs_ops = {
++ .core = &saa6752hs_core_ops,
++ .tuner = &saa6752hs_tuner_ops,
++ .video = &saa6752hs_video_ops,
++};
++
+ static int saa6752hs_probe(struct i2c_client *client,
+- const struct i2c_device_id *id)
++ const struct i2c_device_id *id)
+ {
+ struct saa6752hs_state *h = kzalloc(sizeof(*h), GFP_KERNEL);
++ struct v4l2_subdev *sd;
+ u8 addr = 0x13;
+ u8 data[12];
+
+@@ -861,6 +957,8 @@ static int saa6752hs_probe(struct i2c_client *client,
+ client->addr << 1, client->adapter->name);
+ if (h == NULL)
+ return -ENOMEM;
++ sd = &h->sd;
++ v4l2_i2c_subdev_init(sd, client, &saa6752hs_ops);
+
+ i2c_master_send(client, &addr, 1);
+ i2c_master_recv(client, data, sizeof(data));
+@@ -874,14 +972,15 @@ static int saa6752hs_probe(struct i2c_client *client,
+ }
+ h->params = param_defaults;
+ h->standard = 0; /* Assume 625 input lines */
+-
+- i2c_set_clientdata(client, h);
+ return 0;
+ }
+
+ static int saa6752hs_remove(struct i2c_client *client)
+ {
+- kfree(i2c_get_clientdata(client));
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++
++ v4l2_device_unregister_subdev(sd);
++ kfree(to_state(sd));
+ return 0;
+ }
+
+@@ -893,8 +992,6 @@ MODULE_DEVICE_TABLE(i2c, saa6752hs_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "saa6752hs",
+- .driverid = I2C_DRIVERID_SAA6752HS,
+- .command = saa6752hs_command,
+ .probe = saa6752hs_probe,
+ .remove = saa6752hs_remove,
+ .id_table = saa6752hs_id,
+diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
+index e2febcd..a790a72 100644
+--- a/drivers/media/video/saa7134/saa7134-cards.c
++++ b/drivers/media/video/saa7134/saa7134-cards.c
+@@ -31,6 +31,7 @@
+ #include <media/v4l2-common.h>
+ #include <media/tveeprom.h>
+ #include "tea5767.h"
++#include "tda18271.h"
+
+ /* commly used strings */
+ static char name_mute[] = "mute";
+@@ -272,6 +273,7 @@ struct saa7134_board saa7134_boards[] = {
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
++ .empress_addr = 0x20,
+
+ .inputs = {{
+ .name = name_comp1,
+@@ -408,6 +410,7 @@ struct saa7134_board saa7134_boards[] = {
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
++ .empress_addr = 0x20,
+ .tda9887_conf = TDA9887_PRESENT,
+ .gpiomask = 0x820000,
+ .inputs = {{
+@@ -818,6 +821,7 @@ struct saa7134_board saa7134_boards[] = {
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
++ .empress_addr = 0x20,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 4,
+@@ -977,6 +981,7 @@ struct saa7134_board saa7134_boards[] = {
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
++ .empress_addr = 0x20,
+ .inputs = {{
+ .name = name_comp1,
+ .vmux = 1,
+@@ -1699,6 +1704,7 @@ struct saa7134_board saa7134_boards[] = {
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
++ .rds_addr = 0x10,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+@@ -2364,6 +2370,7 @@ struct saa7134_board saa7134_boards[] = {
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
++ .empress_addr = 0x21,
+ .inputs = {{
+ .name = "Composite 0",
+ .vmux = 0,
+@@ -3291,6 +3298,68 @@ struct saa7134_board saa7134_boards[] = {
+ .gpio = 0x0200100,
+ },
+ },
++ [SAA7134_BOARD_HAUPPAUGE_HVR1120] = {
++ .name = "Hauppauge WinTV-HVR1120 ATSC/QAM-Hybrid",
++ .audio_clock = 0x00187de7,
++ .tuner_type = TUNER_PHILIPS_TDA8290,
++ .radio_type = UNSET,
++ .tuner_addr = ADDR_UNSET,
++ .radio_addr = ADDR_UNSET,
++ .tuner_config = 3,
++ .mpeg = SAA7134_MPEG_DVB,
++ .ts_type = SAA7134_MPEG_TS_SERIAL,
++ .gpiomask = 0x0800100, /* GPIO 21 is an INPUT */
++ .inputs = {{
++ .name = name_tv,
++ .vmux = 1,
++ .amux = TV,
++ .tv = 1,
++ .gpio = 0x0000100,
++ }, {
++ .name = name_comp1,
++ .vmux = 3,
++ .amux = LINE1,
++ }, {
++ .name = name_svideo,
++ .vmux = 8,
++ .amux = LINE1,
++ } },
++ .radio = {
++ .name = name_radio,
++ .amux = TV,
++ .gpio = 0x0800100, /* GPIO 23 HI for FM */
++ },
++ },
++ [SAA7134_BOARD_HAUPPAUGE_HVR1110R3] = {
++ .name = "Hauppauge WinTV-HVR1110r3",
++ .audio_clock = 0x00187de7,
++ .tuner_type = TUNER_PHILIPS_TDA8290,
++ .radio_type = UNSET,
++ .tuner_addr = ADDR_UNSET,
++ .radio_addr = ADDR_UNSET,
++ .tuner_config = 3,
++ .gpiomask = 0x0800100, /* GPIO 21 is an INPUT */
++ .inputs = {{
++ .name = name_tv,
++ .vmux = 1,
++ .amux = TV,
++ .tv = 1,
++ .gpio = 0x0000100,
++ }, {
++ .name = name_comp1,
++ .vmux = 3,
++ .amux = LINE1,
++ }, {
++ .name = name_svideo,
++ .vmux = 8,
++ .amux = LINE1,
++ } },
++ .radio = {
++ .name = name_radio,
++ .amux = TV,
++ .gpio = 0x0800100, /* GPIO 23 HI for FM */
++ },
++ },
+ [SAA7134_BOARD_CINERGY_HT_PCMCIA] = {
+ .name = "Terratec Cinergy HT PCMCIA",
+ .audio_clock = 0x00187de7,
+@@ -4070,6 +4139,7 @@ struct saa7134_board saa7134_boards[] = {
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
++ .empress_addr = 0x20,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = { {
+ .name = name_tv,
+@@ -4106,6 +4176,7 @@ struct saa7134_board saa7134_boards[] = {
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
++ .empress_addr = 0x20,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = { {
+ .name = name_tv,
+@@ -4143,6 +4214,7 @@ struct saa7134_board saa7134_boards[] = {
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
++ .empress_addr = 0x20,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = { {
+ .name = name_tv,
+@@ -4323,13 +4395,13 @@ struct saa7134_board saa7134_boards[] = {
+ .amux = TV,
+ .tv = 1,
+ }, {
+- .name = name_comp,
+- .vmux = 0,
++ .name = name_comp1,
++ .vmux = 3,
+ .amux = LINE1,
+ }, {
+ .name = name_svideo,
+ .vmux = 8,
+- .amux = LINE1,
++ .amux = LINE2,
+ } },
+ .radio = {
+ .name = name_radio,
+@@ -4421,8 +4493,7 @@ struct saa7134_board saa7134_boards[] = {
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+- /* no DVB support for now */
+- /* .mpeg = SAA7134_MPEG_DVB, */
++ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = { {
+ .name = name_comp,
+ .vmux = 1,
+@@ -4441,8 +4512,7 @@ struct saa7134_board saa7134_boards[] = {
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+- /* no DVB support for now */
+- /* .mpeg = SAA7134_MPEG_DVB, */
++ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = { {
+ .name = name_comp,
+ .vmux = 1,
+@@ -4611,7 +4681,7 @@ struct saa7134_board saa7134_boards[] = {
+ .tuner_type = TUNER_YMEC_TVF_5533MF,
+ .radio_type = TUNER_TEA5767,
+ .tuner_addr = ADDR_UNSET,
+- .radio_addr = ADDR_UNSET,
++ .radio_addr = 0x60,
+ .gpiomask = 0x80000700,
+ .inputs = { {
+ .name = name_tv,
+@@ -5405,6 +5475,36 @@ struct pci_device_id saa7134_pci_tbl[] = {
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
++ .subvendor = 0x0070,
++ .subdevice = 0x6706,
++ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120,
++ },{
++ .vendor = PCI_VENDOR_ID_PHILIPS,
++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
++ .subvendor = 0x0070,
++ .subdevice = 0x6707,
++ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3,
++ },{
++ .vendor = PCI_VENDOR_ID_PHILIPS,
++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
++ .subvendor = 0x0070,
++ .subdevice = 0x6708,
++ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1120,
++ },{
++ .vendor = PCI_VENDOR_ID_PHILIPS,
++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
++ .subvendor = 0x0070,
++ .subdevice = 0x6709,
++ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3,
++ },{
++ .vendor = PCI_VENDOR_ID_PHILIPS,
++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
++ .subvendor = 0x0070,
++ .subdevice = 0x670a,
++ .driver_data = SAA7134_BOARD_HAUPPAUGE_HVR1110R3,
++ },{
++ .vendor = PCI_VENDOR_ID_PHILIPS,
++ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x153b,
+ .subdevice = 0x1172,
+ .driver_data = SAA7134_BOARD_CINERGY_HT_PCMCIA,
+@@ -5821,8 +5921,8 @@ static int saa7134_xc2028_callback(struct saa7134_dev *dev,
+ }
+
+
+-static int saa7134_tda8290_callback(struct saa7134_dev *dev,
+- int command, int arg)
++static int saa7134_tda8290_827x_callback(struct saa7134_dev *dev,
++ int command, int arg)
+ {
+ u8 sync_control;
+
+@@ -5848,6 +5948,65 @@ static int saa7134_tda8290_callback(struct saa7134_dev *dev,
+ return 0;
+ }
+
++static inline int saa7134_tda18271_hvr11x0_toggle_agc(struct saa7134_dev *dev,
++ enum tda18271_mode mode)
++{
++ /* toggle AGC switch through GPIO 26 */
++ switch (mode) {
++ case TDA18271_ANALOG:
++ saa7134_set_gpio(dev, 26, 0);
++ break;
++ case TDA18271_DIGITAL:
++ saa7134_set_gpio(dev, 26, 1);
++ break;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
++
++static int saa7134_tda8290_18271_callback(struct saa7134_dev *dev,
++ int command, int arg)
++{
++ int ret = 0;
++
++ switch (command) {
++ case TDA18271_CALLBACK_CMD_AGC_ENABLE: /* 0 */
++ switch (dev->board) {
++ case SAA7134_BOARD_HAUPPAUGE_HVR1120:
++ case SAA7134_BOARD_HAUPPAUGE_HVR1110R3:
++ ret = saa7134_tda18271_hvr11x0_toggle_agc(dev, arg);
++ break;
++ default:
++ break;
++ }
++ break;
++ default:
++ ret = -EINVAL;
++ break;
++ }
++ return ret;
++}
++
++static int saa7134_tda8290_callback(struct saa7134_dev *dev,
++ int command, int arg)
++{
++ int ret;
++
++ switch (dev->board) {
++ case SAA7134_BOARD_HAUPPAUGE_HVR1120:
++ case SAA7134_BOARD_HAUPPAUGE_HVR1110R3:
++ /* tda8290 + tda18271 */
++ ret = saa7134_tda8290_18271_callback(dev, command, arg);
++ break;
++ default:
++ /* tda8290 + tda827x */
++ ret = saa7134_tda8290_827x_callback(dev, command, arg);
++ break;
++ }
++ return ret;
++}
++
+ int saa7134_tuner_callback(void *priv, int component, int command, int arg)
+ {
+ struct saa7134_dev *dev = priv;
+@@ -5878,11 +6037,16 @@ static void hauppauge_eeprom(struct saa7134_dev *dev, u8 *eeprom_data)
+ switch (tv.model) {
+ case 67019: /* WinTV-HVR1110 (Retail, IR Blaster, hybrid, FM, SVid/Comp, 3.5mm audio in) */
+ case 67109: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */
++ case 67201: /* WinTV-HVR1120 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */
++ case 67301: /* WinTV-HVR1000 (Retail, IR Receive, analog, no FM, SVid/Comp, 3.5mm audio in) */
++ case 67209: /* WinTV-HVR1110 (Retail, IR Receive, hybrid, FM, SVid/Comp, 3.5mm audio in) */
+ case 67559: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */
+ case 67569: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM) */
+ case 67579: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM) */
+ case 67589: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */
+ case 67599: /* WinTV-HVR1110 (OEM, no IR, hybrid, no FM, SVid/Comp, RCA aud) */
++ case 67651: /* WinTV-HVR1120 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */
++ case 67659: /* WinTV-HVR1110 (OEM, no IR, hybrid, FM, SVid/Comp, RCA aud) */
+ break;
+ default:
+ printk(KERN_WARNING "%s: warning: "
+@@ -6019,6 +6183,11 @@ int saa7134_board_init1(struct saa7134_dev *dev)
+ msleep(10);
+ break;
+ case SAA7134_BOARD_AVERMEDIA_CARDBUS_506:
++ saa7134_set_gpio(dev, 23, 0);
++ msleep(10);
++ saa7134_set_gpio(dev, 23, 1);
++ dev->has_remote = SAA7134_REMOTE_I2C;
++ break;
+ case SAA7134_BOARD_AVERMEDIA_M103:
+ saa7134_set_gpio(dev, 23, 0);
+ msleep(10);
+@@ -6054,6 +6223,16 @@ int saa7134_board_init1(struct saa7134_dev *dev)
+
+ saa_writeb (SAA7134_PRODUCTION_TEST_MODE, 0x00);
+ break;
++ case SAA7134_BOARD_HAUPPAUGE_HVR1120:
++ case SAA7134_BOARD_HAUPPAUGE_HVR1110R3:
++ /* GPIO 26 high for digital, low for analog */
++ saa7134_set_gpio(dev, 26, 0);
++ msleep(1);
++
++ saa7134_set_gpio(dev, 22, 0);
++ msleep(10);
++ saa7134_set_gpio(dev, 22, 1);
++ break;
+ /* i2c remotes */
+ case SAA7134_BOARD_PINNACLE_PCTV_110i:
+ case SAA7134_BOARD_PINNACLE_PCTV_310i:
+@@ -6079,15 +6258,15 @@ int saa7134_board_init1(struct saa7134_dev *dev)
+ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x8c040007, 0x8c040007);
+ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x0c0007cd, 0x0c0007cd);
+ break;
+- case SAA7134_BOARD_AVERMEDIA_A700_PRO:
+ case SAA7134_BOARD_AVERMEDIA_A700_HYBRID:
+- /* write windows gpio values */
+- saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x80040100, 0x80040100);
+- saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x80040100, 0x00040100);
+ printk("%s: %s: hybrid analog/dvb card\n"
+- "%s: Sorry, only analog s-video and composite input "
++ "%s: Sorry, of the analog inputs, only analog s-video and composite "
+ "are supported for now.\n",
+ dev->name, card(dev).name, dev->name);
++ case SAA7134_BOARD_AVERMEDIA_A700_PRO:
++ /* write windows gpio values */
++ saa_andorl(SAA7134_GPIO_GPMODE0 >> 2, 0x80040100, 0x80040100);
++ saa_andorl(SAA7134_GPIO_GPSTATUS0 >> 2, 0x80040100, 0x00040100);
+ break;
+ }
+ return 0;
+@@ -6109,7 +6288,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev)
+
+ tun_setup.mode_mask = T_RADIO;
+
+- saa7134_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup);
++ saa_call_all(dev, tuner, s_type_addr, &tun_setup);
+ mode_mask &= ~T_RADIO;
+ }
+
+@@ -6121,7 +6300,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev)
+
+ tun_setup.mode_mask = mode_mask;
+
+- saa7134_i2c_call_clients(dev, TUNER_SET_TYPE_ADDR, &tun_setup);
++ saa_call_all(dev, tuner, s_type_addr, &tun_setup);
+ }
+
+ if (dev->tda9887_conf) {
+@@ -6130,8 +6309,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev)
+ tda9887_cfg.tuner = TUNER_TDA9887;
+ tda9887_cfg.priv = &dev->tda9887_conf;
+
+- saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG,
+- &tda9887_cfg);
++ saa_call_all(dev, tuner, s_config, &tda9887_cfg);
+ }
+
+ if (dev->tuner_type == TUNER_XC2028) {
+@@ -6158,7 +6336,7 @@ static void saa7134_tuner_setup(struct saa7134_dev *dev)
+ xc2028_cfg.tuner = TUNER_XC2028;
+ xc2028_cfg.priv = &ctl;
+
+- saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &xc2028_cfg);
++ saa_call_all(dev, tuner, s_config, &xc2028_cfg);
+ }
+ }
+
+@@ -6168,9 +6346,20 @@ int saa7134_board_init2(struct saa7134_dev *dev)
+ unsigned char buf;
+ int board;
+
++ /* Put here the code that enables the chips that are needed
++ for analog mode and doesn't depend on the tuner attachment.
++ It is also a good idea to get tuner type from eeprom, etc before
++ initializing tuner, since we can avoid loading tuner driver
++ on devices that has TUNER_ABSENT
++ */
+ switch (dev->board) {
+ case SAA7134_BOARD_BMK_MPEX_NOTUNER:
+ case SAA7134_BOARD_BMK_MPEX_TUNER:
++ /* Checks if the device has a tuner at 0x60 addr
++ If the device doesn't have a tuner, TUNER_ABSENT
++ will be used at tuner_type, avoiding loading tuner
++ without needing it
++ */
+ dev->i2c_client.addr = 0x60;
+ board = (i2c_master_recv(&dev->i2c_client, &buf, 0) < 0)
+ ? SAA7134_BOARD_BMK_MPEX_NOTUNER
+@@ -6188,11 +6377,15 @@ int saa7134_board_init2(struct saa7134_dev *dev)
+ u8 subaddr;
+ u8 data[3];
+ int ret, tuner_t;
+-
+ struct i2c_msg msg[] = {{.addr=0x50, .flags=0, .buf=&subaddr, .len = 1},
+ {.addr=0x50, .flags=I2C_M_RD, .buf=data, .len = 3}};
++
+ subaddr= 0x14;
+ tuner_t = 0;
++
++ /* Retrieve device data from eeprom, checking for the
++ proper tuner_type.
++ */
+ ret = i2c_transfer(&dev->i2c_adap, msg, 2);
+ if (ret != 2) {
+ printk(KERN_ERR "EEPROM read failure\n");
+@@ -6248,12 +6441,14 @@ int saa7134_board_init2(struct saa7134_dev *dev)
+ dev->name, saa7134_boards[dev->board].name);
+ break;
+ }
++ /* break intentionally omitted */
+ case SAA7134_BOARD_VIDEOMATE_DVBT_300:
+ case SAA7134_BOARD_ASUS_EUROPA2_HYBRID:
+ {
+
+- /* The Philips EUROPA based hybrid boards have the tuner connected through
+- * the channel decoder. We have to make it transparent to find it
++ /* The Philips EUROPA based hybrid boards have the tuner
++ connected through the channel decoder. We have to make it
++ transparent to find it
+ */
+ u8 data[] = { 0x07, 0x02};
+ struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+@@ -6274,21 +6469,15 @@ int saa7134_board_init2(struct saa7134_dev *dev)
+ if (dev->board == SAA7134_BOARD_PHILIPS_TIGER_S) {
+ dev->tuner_type = TUNER_PHILIPS_TDA8290;
+
+- saa7134_tuner_setup(dev);
+-
+ data[2] = 0x68;
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+-
+- /* Tuner setup is handled before I2C transfer.
+- Due to that, there's no need to do it later
+- */
+- return 0;
++ break;
+ }
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ break;
+ }
+- case SAA7134_BOARD_ASUSTeK_TVFM7135:
+- /* The card below is detected as card=53, but is different */
++ case SAA7134_BOARD_ASUSTeK_TVFM7135:
++ /* The card below is detected as card=53, but is different */
+ if (dev->autodetected && (dev->eedata[0x27] == 0x03)) {
+ dev->board = SAA7134_BOARD_ASUSTeK_P7131_ANALOG;
+ printk(KERN_INFO "%s: P7131 analog only, using "
+@@ -6296,6 +6485,10 @@ int saa7134_board_init2(struct saa7134_dev *dev)
+ dev->name, saa7134_boards[dev->board].name);
+ }
+ break;
++ case SAA7134_BOARD_HAUPPAUGE_HVR1120:
++ case SAA7134_BOARD_HAUPPAUGE_HVR1110R3:
++ hauppauge_eeprom(dev, dev->eedata+0x80);
++ break;
+ case SAA7134_BOARD_HAUPPAUGE_HVR1110:
+ hauppauge_eeprom(dev, dev->eedata+0x80);
+ /* break intentionally omitted */
+@@ -6351,22 +6544,6 @@ int saa7134_board_init2(struct saa7134_dev *dev)
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ break;
+ }
+- case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI:
+- case SAA7134_BOARD_KWORLD_ATSC110:
+- {
+- /* enable tuner */
+- int i;
+- static const u8 buffer [] = { 0x10, 0x12, 0x13, 0x04, 0x16,
+- 0x00, 0x14, 0x04, 0x17, 0x00 };
+- dev->i2c_client.addr = 0x0a;
+- for (i = 0; i < 5; i++)
+- if (2 != i2c_master_send(&dev->i2c_client,
+- &buffer[i*2], 2))
+- printk(KERN_WARNING
+- "%s: Unable to enable tuner(%i).\n",
+- dev->name, i);
+- break;
+- }
+ case SAA7134_BOARD_VIDEOMATE_DVBT_200:
+ case SAA7134_BOARD_VIDEOMATE_DVBT_200A:
+ /* The T200 and the T200A share the same pci id. Consequently,
+@@ -6375,9 +6552,9 @@ int saa7134_board_init2(struct saa7134_dev *dev)
+
+ /* Don't do this if the board was specifically selected with an
+ * insmod option or if we have the default configuration T200*/
+- if(!dev->autodetected || (dev->eedata[0x41] == 0xd0))
++ if (!dev->autodetected || (dev->eedata[0x41] == 0xd0))
+ break;
+- if(dev->eedata[0x41] == 0x02) {
++ if (dev->eedata[0x41] == 0x02) {
+ /* Reconfigure board as T200A */
+ dev->board = SAA7134_BOARD_VIDEOMATE_DVBT_200A;
+ dev->tuner_type = saa7134_boards[dev->board].tuner_type;
+@@ -6390,6 +6567,58 @@ int saa7134_board_init2(struct saa7134_dev *dev)
+ break;
+ }
+ break;
++ case SAA7134_BOARD_ADS_INSTANT_HDTV_PCI:
++ case SAA7134_BOARD_KWORLD_ATSC110:
++ {
++ struct i2c_msg msg = { .addr = 0x0a, .flags = 0 };
++ int i;
++ static u8 buffer[][2] = {
++ { 0x10, 0x12 },
++ { 0x13, 0x04 },
++ { 0x16, 0x00 },
++ { 0x14, 0x04 },
++ { 0x17, 0x00 },
++ };
++
++ for (i = 0; i < ARRAY_SIZE(buffer); i++) {
++ msg.buf = &buffer[i][0];
++ msg.len = ARRAY_SIZE(buffer[0]);
++ if (i2c_transfer(&dev->i2c_adap, &msg, 1) != 1)
++ printk(KERN_WARNING
++ "%s: Unable to enable tuner(%i).\n",
++ dev->name, i);
++ }
++ break;
++ }
++ } /* switch() */
++
++ /* initialize tuner */
++ if (TUNER_ABSENT != dev->tuner_type) {
++ int has_demod = (dev->tda9887_conf & TDA9887_PRESENT);
++
++ /* Note: radio tuner address is always filled in,
++ so we do not need to probe for a radio tuner device. */
++ if (dev->radio_type != UNSET)
++ v4l2_i2c_new_subdev(&dev->i2c_adap,
++ "tuner", "tuner", dev->radio_addr);
++ if (has_demod)
++ v4l2_i2c_new_probed_subdev(&dev->i2c_adap, "tuner",
++ "tuner", v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
++ if (dev->tuner_addr == ADDR_UNSET) {
++ enum v4l2_i2c_tuner_type type =
++ has_demod ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
++
++ v4l2_i2c_new_probed_subdev(&dev->i2c_adap, "tuner",
++ "tuner", v4l2_i2c_tuner_addrs(type));
++ } else {
++ v4l2_i2c_new_subdev(&dev->i2c_adap,
++ "tuner", "tuner", dev->tuner_addr);
++ }
++ }
++
++ saa7134_tuner_setup(dev);
++
++ switch (dev->board) {
+ case SAA7134_BOARD_BEHOLD_COLUMBUS_TVFM:
+ {
+ struct v4l2_priv_tun_config tea5767_cfg;
+@@ -6401,12 +6630,10 @@ int saa7134_board_init2(struct saa7134_dev *dev)
+ ctl.xtal_freq = TEA5767_HIGH_LO_13MHz;
+ tea5767_cfg.tuner = TUNER_TEA5767;
+ tea5767_cfg.priv = &ctl;
+- saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &tea5767_cfg);
++ saa_call_all(dev, tuner, s_config, &tea5767_cfg);
+ break;
+ }
+ } /* switch() */
+
+- saa7134_tuner_setup(dev);
+-
+ return 0;
+ }
+diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
+index 99221d7..dafa0d8 100644
+--- a/drivers/media/video/saa7134/saa7134-core.c
++++ b/drivers/media/video/saa7134/saa7134-core.c
+@@ -54,13 +54,9 @@ static unsigned int gpio_tracking;
+ module_param(gpio_tracking, int, 0644);
+ MODULE_PARM_DESC(gpio_tracking,"enable debug messages [gpio]");
+
+-static unsigned int alsa;
++static unsigned int alsa = 1;
+ module_param(alsa, int, 0644);
+-MODULE_PARM_DESC(alsa,"enable ALSA DMA sound [dmasound]");
+-
+-static unsigned int oss;
+-module_param(oss, int, 0644);
+-MODULE_PARM_DESC(oss,"enable OSS DMA sound [dmasound]");
++MODULE_PARM_DESC(alsa,"enable/disable ALSA DMA sound [dmasound]");
+
+ static unsigned int latency = UNSET;
+ module_param(latency, int, 0444);
+@@ -90,8 +86,10 @@ MODULE_PARM_DESC(radio_nr, "radio device number");
+ MODULE_PARM_DESC(tuner, "tuner type");
+ MODULE_PARM_DESC(card, "card type");
+
+-static DEFINE_MUTEX(devlist_lock);
++DEFINE_MUTEX(saa7134_devlist_lock);
++EXPORT_SYMBOL(saa7134_devlist_lock);
+ LIST_HEAD(saa7134_devlist);
++EXPORT_SYMBOL(saa7134_devlist);
+ static LIST_HEAD(mops_list);
+ static unsigned int saa7134_devcount;
+
+@@ -156,10 +154,10 @@ static void request_module_async(struct work_struct *work){
+ request_module("saa7134-empress");
+ if (card_is_dvb(dev))
+ request_module("saa7134-dvb");
+- if (alsa)
+- request_module("saa7134-alsa");
+- if (oss)
+- request_module("saa7134-oss");
++ if (alsa) {
++ if (dev->pci->device != PCI_DEVICE_ID_PHILIPS_SAA7130)
++ request_module("saa7134-alsa");
++ }
+ }
+
+ static void request_submodules(struct saa7134_dev *dev)
+@@ -778,7 +776,7 @@ static struct video_device *vdev_init(struct saa7134_dev *dev,
+ return NULL;
+ *vfd = *template;
+ vfd->minor = -1;
+- vfd->parent = &dev->pci->dev;
++ vfd->v4l2_dev = &dev->v4l2_dev;
+ vfd->release = video_device_release;
+ vfd->debug = video_debug;
+ snprintf(vfd->name, sizeof(vfd->name), "%s %s (%s)",
+@@ -851,6 +849,10 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
+ if (NULL == dev)
+ return -ENOMEM;
+
++ err = v4l2_device_register(&pci_dev->dev, &dev->v4l2_dev);
++ if (err)
++ goto fail0;
++
+ /* pci init */
+ dev->pci = pci_dev;
+ if (pci_enable_device(pci_dev)) {
+@@ -927,6 +929,8 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
+ dev->autodetected = card[dev->nr] != dev->board;
+ dev->tuner_type = saa7134_boards[dev->board].tuner_type;
+ dev->tuner_addr = saa7134_boards[dev->board].tuner_addr;
++ dev->radio_type = saa7134_boards[dev->board].radio_type;
++ dev->radio_addr = saa7134_boards[dev->board].radio_addr;
+ dev->tda9887_conf = saa7134_boards[dev->board].tda9887_conf;
+ if (UNSET != tuner[dev->nr])
+ dev->tuner_type = tuner[dev->nr];
+@@ -971,23 +975,48 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
+ /* wait a bit, register i2c bus */
+ msleep(100);
+ saa7134_i2c_register(dev);
+-
+- /* initialize hardware #2 */
+- if (TUNER_ABSENT != dev->tuner_type)
+- request_module("tuner");
+ saa7134_board_init2(dev);
+
+ saa7134_hwinit2(dev);
+
+ /* load i2c helpers */
+ if (card_is_empress(dev)) {
+- request_module("saa6752hs");
++ struct v4l2_subdev *sd =
++ v4l2_i2c_new_subdev(&dev->i2c_adap,
++ "saa6752hs", "saa6752hs",
++ saa7134_boards[dev->board].empress_addr);
++
++ if (sd)
++ sd->grp_id = GRP_EMPRESS;
++ }
++
++ if (saa7134_boards[dev->board].rds_addr) {
++ unsigned short addrs[2] = { 0, I2C_CLIENT_END };
++ struct v4l2_subdev *sd;
++
++ addrs[0] = saa7134_boards[dev->board].rds_addr;
++ sd = v4l2_i2c_new_probed_subdev(&dev->i2c_adap, "saa6588",
++ "saa6588", addrs);
++ if (sd)
++ printk(KERN_INFO "%s: found RDS decoder\n", dev->name);
+ }
+
+ request_submodules(dev);
+
+ v4l2_prio_init(&dev->prio);
+
++ mutex_lock(&saa7134_devlist_lock);
++ list_for_each_entry(mops, &mops_list, next)
++ mpeg_ops_attach(mops, dev);
++ list_add_tail(&dev->devlist, &saa7134_devlist);
++ mutex_unlock(&saa7134_devlist_lock);
++
++ /* check for signal */
++ saa7134_irq_video_signalchange(dev);
++
++ if (TUNER_ABSENT != dev->tuner_type)
++ saa_call_all(dev, core, s_standby, 0);
++
+ /* register v4l devices */
+ if (saa7134_no_overlay > 0)
+ printk(KERN_INFO "%s: Overlay support disabled.\n", dev->name);
+@@ -1023,24 +1052,10 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
+ }
+
+ /* everything worked */
+- pci_set_drvdata(pci_dev,dev);
+ saa7134_devcount++;
+
+- mutex_lock(&devlist_lock);
+- list_for_each_entry(mops, &mops_list, next)
+- mpeg_ops_attach(mops, dev);
+- list_add_tail(&dev->devlist,&saa7134_devlist);
+- mutex_unlock(&devlist_lock);
+-
+- /* check for signal */
+- saa7134_irq_video_signalchange(dev);
+-
+- if (saa7134_dmasound_init && !dev->dmasound.priv_data) {
++ if (saa7134_dmasound_init && !dev->dmasound.priv_data)
+ saa7134_dmasound_init(dev);
+- }
+-
+- if (TUNER_ABSENT != dev->tuner_type)
+- saa7134_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL);
+
+ return 0;
+
+@@ -1055,13 +1070,16 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
+ release_mem_region(pci_resource_start(pci_dev,0),
+ pci_resource_len(pci_dev,0));
+ fail1:
++ v4l2_device_unregister(&dev->v4l2_dev);
++ fail0:
+ kfree(dev);
+ return err;
+ }
+
+ static void __devexit saa7134_finidev(struct pci_dev *pci_dev)
+ {
+- struct saa7134_dev *dev = pci_get_drvdata(pci_dev);
++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
++ struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev);
+ struct saa7134_mpeg_ops *mops;
+
+ /* Release DMA sound modules if present */
+@@ -1088,11 +1106,11 @@ static void __devexit saa7134_finidev(struct pci_dev *pci_dev)
+ saa7134_hwfini(dev);
+
+ /* unregister */
+- mutex_lock(&devlist_lock);
++ mutex_lock(&saa7134_devlist_lock);
+ list_del(&dev->devlist);
+ list_for_each_entry(mops, &mops_list, next)
+ mpeg_ops_detach(mops, dev);
+- mutex_unlock(&devlist_lock);
++ mutex_unlock(&saa7134_devlist_lock);
+ saa7134_devcount--;
+
+ saa7134_i2c_unregister(dev);
+@@ -1113,7 +1131,8 @@ static void __devexit saa7134_finidev(struct pci_dev *pci_dev)
+ release_mem_region(pci_resource_start(pci_dev,0),
+ pci_resource_len(pci_dev,0));
+
+- pci_set_drvdata(pci_dev, NULL);
++
++ v4l2_device_unregister(&dev->v4l2_dev);
+
+ /* free memory */
+ kfree(dev);
+@@ -1148,8 +1167,8 @@ static int saa7134_buffer_requeue(struct saa7134_dev *dev,
+
+ static int saa7134_suspend(struct pci_dev *pci_dev , pm_message_t state)
+ {
+-
+- struct saa7134_dev *dev = pci_get_drvdata(pci_dev);
++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
++ struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev);
+
+ /* disable overlay - apps should enable it explicitly on resume*/
+ dev->ovenable = 0;
+@@ -1185,7 +1204,8 @@ static int saa7134_suspend(struct pci_dev *pci_dev , pm_message_t state)
+
+ static int saa7134_resume(struct pci_dev *pci_dev)
+ {
+- struct saa7134_dev *dev = pci_get_drvdata(pci_dev);
++ struct v4l2_device *v4l2_dev = pci_get_drvdata(pci_dev);
++ struct saa7134_dev *dev = container_of(v4l2_dev, struct saa7134_dev, v4l2_dev);
+ unsigned long flags;
+
+ pci_set_power_state(pci_dev, PCI_D0);
+@@ -1247,11 +1267,11 @@ int saa7134_ts_register(struct saa7134_mpeg_ops *ops)
+ {
+ struct saa7134_dev *dev;
+
+- mutex_lock(&devlist_lock);
++ mutex_lock(&saa7134_devlist_lock);
+ list_for_each_entry(dev, &saa7134_devlist, devlist)
+ mpeg_ops_attach(ops, dev);
+ list_add_tail(&ops->next,&mops_list);
+- mutex_unlock(&devlist_lock);
++ mutex_unlock(&saa7134_devlist_lock);
+ return 0;
+ }
+
+@@ -1259,11 +1279,11 @@ void saa7134_ts_unregister(struct saa7134_mpeg_ops *ops)
+ {
+ struct saa7134_dev *dev;
+
+- mutex_lock(&devlist_lock);
++ mutex_lock(&saa7134_devlist_lock);
+ list_del(&ops->next);
+ list_for_each_entry(dev, &saa7134_devlist, devlist)
+ mpeg_ops_detach(ops, dev);
+- mutex_unlock(&devlist_lock);
++ mutex_unlock(&saa7134_devlist_lock);
+ }
+
+ EXPORT_SYMBOL(saa7134_ts_register);
+@@ -1307,8 +1327,6 @@ module_exit(saa7134_fini);
+ /* ----------------------------------------------------------- */
+
+ EXPORT_SYMBOL(saa7134_set_gpio);
+-EXPORT_SYMBOL(saa7134_i2c_call_clients);
+-EXPORT_SYMBOL(saa7134_devlist);
+ EXPORT_SYMBOL(saa7134_boards);
+
+ /* ----------------- for the DMA sound modules --------------- */
+diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
+index b5370b3..4eff1ca 100644
+--- a/drivers/media/video/saa7134/saa7134-dvb.c
++++ b/drivers/media/video/saa7134/saa7134-dvb.c
+@@ -48,9 +48,15 @@
+ #include "isl6405.h"
+ #include "lnbp21.h"
+ #include "tuner-simple.h"
++#include "tda18271.h"
++#include "lgdt3305.h"
++#include "tda8290.h"
+
+ #include "zl10353.h"
+
++#include "zl10036.h"
++#include "mt312.h"
++
+ MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
+ MODULE_LICENSE("GPL");
+
+@@ -189,7 +195,7 @@ static int mt352_pinnacle_tuner_set_params(struct dvb_frontend* fe,
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+- saa7134_i2c_call_clients(dev,VIDIOC_S_FREQUENCY,&f);
++ saa_call_all(dev, tuner, s_frequency, &f);
+ msg.buf = on;
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+@@ -950,6 +956,45 @@ static struct nxt200x_config kworldatsc110 = {
+ .demod_address = 0x0a,
+ };
+
++/* ------------------------------------------------------------------ */
++
++static struct mt312_config avertv_a700_mt312 = {
++ .demod_address = 0x0e,
++ .voltage_inverted = 1,
++};
++
++static struct zl10036_config avertv_a700_tuner = {
++ .tuner_address = 0x60,
++};
++
++static struct lgdt3305_config hcw_lgdt3305_config = {
++ .i2c_addr = 0x0e,
++ .mpeg_mode = LGDT3305_MPEG_SERIAL,
++ .tpclk_edge = LGDT3305_TPCLK_RISING_EDGE,
++ .tpvalid_polarity = LGDT3305_TP_VALID_HIGH,
++ .deny_i2c_rptr = 1,
++ .spectral_inversion = 1,
++ .qam_if_khz = 4000,
++ .vsb_if_khz = 3250,
++};
++
++static struct tda18271_std_map hauppauge_tda18271_std_map = {
++ .atsc_6 = { .if_freq = 3250, .agc_mode = 3, .std = 4,
++ .if_lvl = 1, .rfagc_top = 0x58, },
++ .qam_6 = { .if_freq = 4000, .agc_mode = 3, .std = 5,
++ .if_lvl = 1, .rfagc_top = 0x58, },
++};
++
++static struct tda18271_config hcw_tda18271_config = {
++ .std_map = &hauppauge_tda18271_std_map,
++ .gate = TDA18271_GATE_ANALOG,
++ .config = 3,
++};
++
++static struct tda829x_config tda829x_no_probe = {
++ .probe_tuner = TDA829X_DONT_PROBE,
++};
++
+ /* ==================================================================
+ * Core code
+ */
+@@ -1076,6 +1121,19 @@ static int dvb_init(struct saa7134_dev *dev)
+ &tda827x_cfg_1) < 0)
+ goto dettach_frontend;
+ break;
++ case SAA7134_BOARD_HAUPPAUGE_HVR1120:
++ fe0->dvb.frontend = dvb_attach(lgdt3305_attach,
++ &hcw_lgdt3305_config,
++ &dev->i2c_adap);
++ if (fe0->dvb.frontend) {
++ dvb_attach(tda829x_attach, fe0->dvb.frontend,
++ &dev->i2c_adap, 0x4b,
++ &tda829x_no_probe);
++ dvb_attach(tda18271_attach, fe0->dvb.frontend,
++ 0x60, &dev->i2c_adap,
++ &hcw_tda18271_config);
++ }
++ break;
+ case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
+ if (configure_tda827x_fe(dev, &asus_p7131_dual_config,
+ &tda827x_cfg_0) < 0)
+@@ -1376,6 +1434,19 @@ static int dvb_init(struct saa7134_dev *dev)
+ TUNER_PHILIPS_FMD1216ME_MK3);
+ }
+ break;
++ case SAA7134_BOARD_AVERMEDIA_A700_PRO:
++ case SAA7134_BOARD_AVERMEDIA_A700_HYBRID:
++ /* Zarlink ZL10313 */
++ fe0->dvb.frontend = dvb_attach(mt312_attach,
++ &avertv_a700_mt312, &dev->i2c_adap);
++ if (fe0->dvb.frontend) {
++ if (dvb_attach(zl10036_attach, fe0->dvb.frontend,
++ &avertv_a700_tuner, &dev->i2c_adap) == NULL) {
++ wprintk("%s: No zl10036 found!\n",
++ __func__);
++ }
++ }
++ break;
+ default:
+ wprintk("Huh? unknown DVB card?\n");
+ break;
+@@ -1449,7 +1520,7 @@ static int dvb_fini(struct saa7134_dev *dev)
+ tda9887_cfg.priv = &on;
+
+ /* otherwise we don't detect the tuner on next insmod */
+- saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG, &tda9887_cfg);
++ saa_call_all(dev, tuner, s_config, &tda9887_cfg);
+ } else if (dev->board == SAA7134_BOARD_MEDION_MD8800_QUADRO) {
+ if ((dev->eedata[2] == 0x07) && use_frontend) {
+ /* turn off the 2nd lnb supply */
+diff --git a/drivers/media/video/saa7134/saa7134-empress.c b/drivers/media/video/saa7134/saa7134-empress.c
+index c9d8beb..9db3472 100644
+--- a/drivers/media/video/saa7134/saa7134-empress.c
++++ b/drivers/media/video/saa7134/saa7134-empress.c
+@@ -76,7 +76,7 @@ static int ts_init_encoder(struct saa7134_dev* dev)
+ break;
+ }
+ ts_reset_encoder(dev);
+- saa7134_i2c_call_clients(dev, VIDIOC_INT_INIT, &leading_null_bytes);
++ saa_call_all(dev, core, init, leading_null_bytes);
+ dev->empress_started = 1;
+ return 0;
+ }
+@@ -234,7 +234,7 @@ static int empress_g_fmt_vid_cap(struct file *file, void *priv,
+ {
+ struct saa7134_dev *dev = file->private_data;
+
+- saa7134_i2c_call_clients(dev, VIDIOC_G_FMT, f);
++ saa_call_all(dev, video, g_fmt, f);
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets;
+@@ -247,7 +247,7 @@ static int empress_s_fmt_vid_cap(struct file *file, void *priv,
+ {
+ struct saa7134_dev *dev = file->private_data;
+
+- saa7134_i2c_call_clients(dev, VIDIOC_S_FMT, f);
++ saa_call_all(dev, video, s_fmt, f);
+
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_MPEG;
+ f->fmt.pix.sizeimage = TS_PACKET_SIZE * dev->ts.nr_packets;
+@@ -317,7 +317,7 @@ static int empress_s_ext_ctrls(struct file *file, void *priv,
+ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+
+- err = saa7134_i2c_call_saa6752(dev, VIDIOC_S_EXT_CTRLS, ctrls);
++ err = saa_call_empress(dev, core, s_ext_ctrls, ctrls);
+ ts_init_encoder(dev);
+
+ return err;
+@@ -330,7 +330,7 @@ static int empress_g_ext_ctrls(struct file *file, void *priv,
+
+ if (ctrls->ctrl_class != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+- return saa7134_i2c_call_saa6752(dev, VIDIOC_G_EXT_CTRLS, ctrls);
++ return saa_call_empress(dev, core, g_ext_ctrls, ctrls);
+ }
+
+ static int empress_g_ctrl(struct file *file, void *priv,
+@@ -352,6 +352,7 @@ static int empress_s_ctrl(struct file *file, void *priv,
+ static int empress_queryctrl(struct file *file, void *priv,
+ struct v4l2_queryctrl *c)
+ {
++ /* Must be sorted from low to high control ID! */
+ static const u32 user_ctrls[] = {
+ V4L2_CID_USER_CLASS,
+ V4L2_CID_BRIGHTNESS,
+@@ -364,6 +365,7 @@ static int empress_queryctrl(struct file *file, void *priv,
+ 0
+ };
+
++ /* Must be sorted from low to high control ID! */
+ static const u32 mpeg_ctrls[] = {
+ V4L2_CID_MPEG_CLASS,
+ V4L2_CID_MPEG_STREAM_TYPE,
+@@ -388,10 +390,10 @@ static int empress_queryctrl(struct file *file, void *priv,
+ if (c->id == 0)
+ return -EINVAL;
+ if (c->id == V4L2_CID_USER_CLASS || c->id == V4L2_CID_MPEG_CLASS)
+- return v4l2_ctrl_query_fill_std(c);
++ return v4l2_ctrl_query_fill(c, 0, 0, 0, 0);
+ if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG)
+ return saa7134_queryctrl(file, priv, c);
+- return saa7134_i2c_call_saa6752(dev, VIDIOC_QUERYCTRL, c);
++ return saa_call_empress(dev, core, queryctrl, c);
+ }
+
+ static int empress_querymenu(struct file *file, void *priv,
+@@ -401,7 +403,7 @@ static int empress_querymenu(struct file *file, void *priv,
+
+ if (V4L2_CTRL_ID2CLASS(c->id) != V4L2_CTRL_CLASS_MPEG)
+ return -EINVAL;
+- return saa7134_i2c_call_saa6752(dev, VIDIOC_QUERYMENU, c);
++ return saa_call_empress(dev, core, querymenu, c);
+ }
+
+ static int empress_g_chip_ident(struct file *file, void *fh,
+@@ -411,14 +413,11 @@ static int empress_g_chip_ident(struct file *file, void *fh,
+
+ chip->ident = V4L2_IDENT_NONE;
+ chip->revision = 0;
+- if (dev->mpeg_i2c_client == NULL)
+- return -EINVAL;
+ if (chip->match.type == V4L2_CHIP_MATCH_I2C_DRIVER &&
+ !strcmp(chip->match.name, "saa6752hs"))
+- return saa7134_i2c_call_saa6752(dev, VIDIOC_DBG_G_CHIP_IDENT, chip);
+- if (chip->match.type == V4L2_CHIP_MATCH_I2C_ADDR &&
+- chip->match.addr == dev->mpeg_i2c_client->addr)
+- return saa7134_i2c_call_saa6752(dev, VIDIOC_DBG_G_CHIP_IDENT, chip);
++ return saa_call_empress(dev, core, g_chip_ident, chip);
++ if (chip->match.type == V4L2_CHIP_MATCH_I2C_ADDR)
++ return saa_call_empress(dev, core, g_chip_ident, chip);
+ return -EINVAL;
+ }
+
+diff --git a/drivers/media/video/saa7134/saa7134-i2c.c b/drivers/media/video/saa7134/saa7134-i2c.c
+index 20c1b33..f3e285a 100644
+--- a/drivers/media/video/saa7134/saa7134-i2c.c
++++ b/drivers/media/video/saa7134/saa7134-i2c.c
+@@ -255,7 +255,7 @@ static int saa7134_i2c_xfer(struct i2c_adapter *i2c_adap,
+ addr = msgs[i].addr << 1;
+ if (msgs[i].flags & I2C_M_RD)
+ addr |= 1;
+- if (i > 0 && msgs[i].flags & I2C_M_RD) {
++ if (i > 0 && msgs[i].flags & I2C_M_RD && msgs[i].addr != 0x40) {
+ /* workaround for a saa7134 i2c bug
+ * needed to talk to the mt352 demux
+ * thanks to pinnacle for the hint */
+@@ -327,8 +327,6 @@ static int attach_inform(struct i2c_client *client)
+
+ d1printk( "%s i2c attach [addr=0x%x,client=%s]\n",
+ client->driver->driver.name, client->addr, client->name);
+- if (client->addr == 0x20 && client->driver && client->driver->command)
+- dev->mpeg_i2c_client = client;
+
+ /* Am I an i2c remote control? */
+
+@@ -357,7 +355,6 @@ static struct i2c_algorithm saa7134_algo = {
+
+ static struct i2c_adapter saa7134_adap_template = {
+ .owner = THIS_MODULE,
+- .class = I2C_CLASS_TV_ANALOG,
+ .name = "saa7134",
+ .id = I2C_HW_SAA7134,
+ .algo = &saa7134_algo,
+@@ -421,29 +418,13 @@ static void do_i2c_scan(char *name, struct i2c_client *c)
+ }
+ }
+
+-void saa7134_i2c_call_clients(struct saa7134_dev *dev,
+- unsigned int cmd, void *arg)
+-{
+- BUG_ON(NULL == dev->i2c_adap.algo_data);
+- i2c_clients_command(&dev->i2c_adap, cmd, arg);
+-}
+-
+-int saa7134_i2c_call_saa6752(struct saa7134_dev *dev,
+- unsigned int cmd, void *arg)
+-{
+- if (dev->mpeg_i2c_client == NULL)
+- return -EINVAL;
+- return dev->mpeg_i2c_client->driver->command(dev->mpeg_i2c_client,
+- cmd, arg);
+-}
+-EXPORT_SYMBOL_GPL(saa7134_i2c_call_saa6752);
+-
+ int saa7134_i2c_register(struct saa7134_dev *dev)
+ {
+ dev->i2c_adap = saa7134_adap_template;
+ dev->i2c_adap.dev.parent = &dev->pci->dev;
+ strcpy(dev->i2c_adap.name,dev->name);
+ dev->i2c_adap.algo_data = dev;
++ i2c_set_adapdata(&dev->i2c_adap, &dev->v4l2_dev);
+ i2c_add_adapter(&dev->i2c_adap);
+
+ dev->i2c_client = saa7134_client_template;
+diff --git a/drivers/media/video/saa7134/saa7134-ts.c b/drivers/media/video/saa7134/saa7134-ts.c
+index ef55a59..cc8b923 100644
+--- a/drivers/media/video/saa7134/saa7134-ts.c
++++ b/drivers/media/video/saa7134/saa7134-ts.c
+@@ -79,8 +79,19 @@ static int buffer_activate(struct saa7134_dev *dev,
+ saa_writeb(SAA7134_TS_SERIAL1, 0x00);
+
+ /* Start TS stream */
+- saa_writeb(SAA7134_TS_SERIAL0, 0x40);
+- saa_writeb(SAA7134_TS_PARALLEL, 0xEC);
++ switch (saa7134_boards[dev->board].ts_type) {
++ case SAA7134_MPEG_TS_PARALLEL:
++ saa_writeb(SAA7134_TS_SERIAL0, 0x40);
++ saa_writeb(SAA7134_TS_PARALLEL, 0xec);
++ break;
++ case SAA7134_MPEG_TS_SERIAL:
++ saa_writeb(SAA7134_TS_SERIAL0, 0xd8);
++ saa_writeb(SAA7134_TS_PARALLEL, 0x6c);
++ saa_writeb(SAA7134_TS_PARALLEL_SERIAL, 0xbc);
++ saa_writeb(SAA7134_TS_SERIAL1, 0x02);
++ break;
++ }
++
+ dev->ts_state = SAA7134_TS_STARTED;
+ }
+
+diff --git a/drivers/media/video/saa7134/saa7134-video.c b/drivers/media/video/saa7134/saa7134-video.c
+index a1f7e35..404f70e 100644
+--- a/drivers/media/video/saa7134/saa7134-video.c
++++ b/drivers/media/video/saa7134/saa7134-video.c
+@@ -30,11 +30,7 @@
+ #include "saa7134-reg.h"
+ #include "saa7134.h"
+ #include <media/v4l2-common.h>
+-
+-#ifdef CONFIG_VIDEO_V4L1_COMPAT
+-/* Include V4L1 specific functions. Should be removed soon */
+-#include <linux/videodev.h>
+-#endif
++#include <media/rds.h>
+
+ /* ------------------------------------------------------------------ */
+
+@@ -452,6 +448,7 @@ static const struct v4l2_queryctrl video_ctrls[] = {
+ .name = "y offset odd field",
+ .minimum = 0,
+ .maximum = 128,
++ .step = 1,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+@@ -459,6 +456,7 @@ static const struct v4l2_queryctrl video_ctrls[] = {
+ .name = "y offset even field",
+ .minimum = 0,
+ .maximum = 128,
++ .step = 1,
+ .default_value = 0,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+@@ -627,10 +625,10 @@ void saa7134_set_tvnorm_hw(struct saa7134_dev *dev)
+ saa7134_set_decoder(dev);
+
+ if (card_in(dev, dev->ctl_input).tv)
+- saa7134_i2c_call_clients(dev, VIDIOC_S_STD, &dev->tvnorm->id);
++ saa_call_all(dev, tuner, s_std, dev->tvnorm->id);
+ /* Set the correct norm for the saa6752hs. This function
+ does nothing if there is no saa6752hs. */
+- saa7134_i2c_call_saa6752(dev, VIDIOC_S_STD, &dev->tvnorm->id);
++ saa_call_empress(dev, tuner, s_std, dev->tvnorm->id);
+ }
+
+ static void set_h_prescale(struct saa7134_dev *dev, int task, int prescale)
+@@ -1266,8 +1264,7 @@ int saa7134_s_ctrl_internal(struct saa7134_dev *dev, struct saa7134_fh *fh, str
+ else
+ dev->tda9887_conf &= ~TDA9887_AUTOMUTE;
+
+- saa7134_i2c_call_clients(dev, TUNER_SET_CONFIG,
+- &tda9887_cfg);
++ saa_call_all(dev, tuner, s_config, &tda9887_cfg);
+ }
+ break;
+ }
+@@ -1334,7 +1331,7 @@ static int video_open(struct file *file)
+ enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ int radio = 0;
+
+- lock_kernel();
++ mutex_lock(&saa7134_devlist_lock);
+ list_for_each_entry(dev, &saa7134_devlist, devlist) {
+ if (dev->video_dev && (dev->video_dev->minor == minor))
+ goto found;
+@@ -1347,19 +1344,20 @@ static int video_open(struct file *file)
+ goto found;
+ }
+ }
+- unlock_kernel();
++ mutex_unlock(&saa7134_devlist_lock);
+ return -ENODEV;
+- found:
++
++found:
++ mutex_unlock(&saa7134_devlist_lock);
+
+ dprintk("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) {
+- unlock_kernel();
++ if (NULL == fh)
+ return -ENOMEM;
+- }
++
+ file->private_data = fh;
+ fh->dev = dev;
+ fh->radio = radio;
+@@ -1387,12 +1385,11 @@ static int video_open(struct file *file)
+ if (fh->radio) {
+ /* switch to radio mode */
+ saa7134_tvaudio_setinput(dev,&card(dev).radio);
+- saa7134_i2c_call_clients(dev,AUDC_SET_RADIO, NULL);
++ saa_call_all(dev, tuner, s_radio);
+ } else {
+ /* switch to video/vbi mode */
+ video_mux(dev,dev->ctl_input);
+ }
+- unlock_kernel();
+ return 0;
+ }
+
+@@ -1466,6 +1463,7 @@ static int video_release(struct file *file)
+ {
+ struct saa7134_fh *fh = file->private_data;
+ struct saa7134_dev *dev = fh->dev;
++ struct rds_command cmd;
+ unsigned long flags;
+
+ /* turn off overlay */
+@@ -1498,7 +1496,9 @@ static int video_release(struct file *file)
+ saa_andorb(SAA7134_OFMT_DATA_A, 0x1f, 0);
+ saa_andorb(SAA7134_OFMT_DATA_B, 0x1f, 0);
+
+- saa7134_i2c_call_clients(dev, TUNER_SET_STANDBY, NULL);
++ saa_call_all(dev, core, s_standby, 0);
++ if (fh->radio)
++ saa_call_all(dev, core, ioctl, RDS_CMD_CLOSE, &cmd);
+
+ /* free stuff */
+ videobuf_mmap_free(&fh->cap);
+@@ -1519,6 +1519,37 @@ static int video_mmap(struct file *file, struct vm_area_struct * vma)
+ return videobuf_mmap_mapper(saa7134_queue(fh), vma);
+ }
+
++static ssize_t radio_read(struct file *file, char __user *data,
++ size_t count, loff_t *ppos)
++{
++ struct saa7134_fh *fh = file->private_data;
++ struct saa7134_dev *dev = fh->dev;
++ struct rds_command cmd;
++
++ cmd.block_count = count/3;
++ cmd.buffer = data;
++ cmd.instance = file;
++ cmd.result = -ENODEV;
++
++ saa_call_all(dev, core, ioctl, RDS_CMD_READ, &cmd);
++
++ return cmd.result;
++}
++
++static unsigned int radio_poll(struct file *file, poll_table *wait)
++{
++ struct saa7134_fh *fh = file->private_data;
++ struct saa7134_dev *dev = fh->dev;
++ struct rds_command cmd;
++
++ cmd.instance = file;
++ cmd.event_list = wait;
++ cmd.result = -ENODEV;
++ saa_call_all(dev, core, ioctl, RDS_CMD_POLL, &cmd);
++
++ return cmd.result;
++}
++
+ /* ------------------------------------------------------------------ */
+
+ static int saa7134_try_get_set_fmt_vbi_cap(struct file *file, void *priv,
+@@ -2041,7 +2072,7 @@ static int saa7134_s_frequency(struct file *file, void *priv,
+ mutex_lock(&dev->lock);
+ dev->ctl_freq = f->frequency;
+
+- saa7134_i2c_call_clients(dev, VIDIOC_S_FREQUENCY, f);
++ saa_call_all(dev, tuner, s_frequency, f);
+
+ saa7134_tvaudio_do_scan(dev);
+ mutex_unlock(&dev->lock);
+@@ -2299,7 +2330,7 @@ static int radio_g_tuner(struct file *file, void *priv,
+ strcpy(t->name, "Radio");
+ t->type = V4L2_TUNER_RADIO;
+
+- saa7134_i2c_call_clients(dev, VIDIOC_G_TUNER, t);
++ saa_call_all(dev, tuner, g_tuner, t);
+ if (dev->input->amux == TV) {
+ t->signal = 0xf800 - ((saa_readb(0x581) & 0x1f) << 11);
+ t->rxsubchans = (saa_readb(0x529) & 0x08) ?
+@@ -2316,7 +2347,7 @@ static int radio_s_tuner(struct file *file, void *priv,
+ if (0 != t->index)
+ return -EINVAL;
+
+- saa7134_i2c_call_clients(dev, VIDIOC_S_TUNER, t);
++ saa_call_all(dev, tuner, s_tuner, t);
+ return 0;
+ }
+
+@@ -2443,8 +2474,10 @@ static const struct v4l2_ioctl_ops video_ioctl_ops = {
+ static const struct v4l2_file_operations radio_fops = {
+ .owner = THIS_MODULE,
+ .open = video_open,
++ .read = radio_read,
+ .release = video_release,
+ .ioctl = video_ioctl2,
++ .poll = radio_poll,
+ };
+
+ static const struct v4l2_ioctl_ops radio_ioctl_ops = {
+diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
+index 14ee265..a2dd326 100644
+--- a/drivers/media/video/saa7134/saa7134.h
++++ b/drivers/media/video/saa7134/saa7134.h
+@@ -35,6 +35,7 @@
+
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-ioctl.h>
++#include <media/v4l2-device.h>
+ #include <media/tuner.h>
+ #include <media/ir-common.h>
+ #include <media/ir-kbd-i2c.h>
+@@ -277,6 +278,8 @@ struct saa7134_format {
+ #define SAA7134_BOARD_ASUSTeK_TIGER 152
+ #define SAA7134_BOARD_KWORLD_PLUS_TV_ANALOG 153
+ #define SAA7134_BOARD_AVERMEDIA_GO_007_FM_PLUS 154
++#define SAA7134_BOARD_HAUPPAUGE_HVR1120 155
++#define SAA7134_BOARD_HAUPPAUGE_HVR1110R3 156
+
+ #define SAA7134_MAXBOARDS 32
+ #define SAA7134_INPUT_MAX 8
+@@ -309,6 +312,11 @@ enum saa7134_mpeg_type {
+ SAA7134_MPEG_DVB,
+ };
+
++enum saa7134_mpeg_ts_type {
++ SAA7134_MPEG_TS_PARALLEL = 0,
++ SAA7134_MPEG_TS_SERIAL,
++};
++
+ struct saa7134_board {
+ char *name;
+ unsigned int audio_clock;
+@@ -324,6 +332,8 @@ struct saa7134_board {
+ unsigned int radio_type;
+ unsigned char tuner_addr;
+ unsigned char radio_addr;
++ unsigned char empress_addr;
++ unsigned char rds_addr;
+
+ unsigned int tda9887_conf;
+ unsigned int tuner_config;
+@@ -331,6 +341,7 @@ struct saa7134_board {
+ /* peripheral I/O */
+ enum saa7134_video_out video_out;
+ enum saa7134_mpeg_type mpeg;
++ enum saa7134_mpeg_ts_type ts_type;
+ unsigned int vid_port_opts;
+ };
+
+@@ -445,7 +456,6 @@ struct saa7134_dmasound {
+ unsigned int bufsize;
+ struct saa7134_pgtable pt;
+ struct videobuf_dmabuf dma;
+- wait_queue_head_t wq;
+ unsigned int dma_blk;
+ unsigned int read_offset;
+ unsigned int read_count;
+@@ -482,6 +492,7 @@ struct saa7134_dev {
+ struct mutex lock;
+ spinlock_t slock;
+ struct v4l2_prio_state prio;
++ struct v4l2_device v4l2_dev;
+ /* workstruct for loading modules */
+ struct work_struct request_module_wk;
+
+@@ -572,7 +583,6 @@ struct saa7134_dev {
+ enum saa7134_ts_status ts_state;
+ unsigned int buff_cnt;
+ struct saa7134_mpeg_ops *mops;
+- struct i2c_client *mpeg_i2c_client;
+
+ /* SAA7134_MPEG_EMPRESS only */
+ struct video_device *empress_dev;
+@@ -588,6 +598,7 @@ struct saa7134_dev {
+ int (*original_set_voltage)(struct dvb_frontend *fe, fe_sec_voltage_t voltage);
+ int (*original_set_high_voltage)(struct dvb_frontend *fe, long arg);
+ #endif
++ void (*gate_ctrl)(struct saa7134_dev *dev, int open);
+ };
+
+ /* ----------------------------------------------------------- */
+@@ -616,10 +627,31 @@ struct saa7134_dev {
+ V4L2_STD_NTSC | V4L2_STD_PAL_M | \
+ V4L2_STD_PAL_60)
+
++#define GRP_EMPRESS (1)
++#define saa_call_all(dev, o, f, args...) do { \
++ if (dev->gate_ctrl) \
++ dev->gate_ctrl(dev, 1); \
++ v4l2_device_call_all(&(dev)->v4l2_dev, 0, o, f , ##args); \
++ if (dev->gate_ctrl) \
++ dev->gate_ctrl(dev, 0); \
++} while (0)
++
++#define saa_call_empress(dev, o, f, args...) ({ \
++ long _rc; \
++ if (dev->gate_ctrl) \
++ dev->gate_ctrl(dev, 1); \
++ _rc = v4l2_device_call_until_err(&(dev)->v4l2_dev, \
++ GRP_EMPRESS, o, f , ##args); \
++ if (dev->gate_ctrl) \
++ dev->gate_ctrl(dev, 0); \
++ _rc; \
++})
++
+ /* ----------------------------------------------------------- */
+ /* saa7134-core.c */
+
+ extern struct list_head saa7134_devlist;
++extern struct mutex saa7134_devlist_lock;
+ extern int saa7134_no_overlay;
+
+ void saa7134_track_gpio(struct saa7134_dev *dev, char *msg);
+@@ -668,10 +700,6 @@ int saa7134_tuner_callback(void *priv, int component, int command, int arg);
+
+ int saa7134_i2c_register(struct saa7134_dev *dev);
+ int saa7134_i2c_unregister(struct saa7134_dev *dev);
+-void saa7134_i2c_call_clients(struct saa7134_dev *dev,
+- unsigned int cmd, void *arg);
+-int saa7134_i2c_call_saa6752(struct saa7134_dev *dev,
+- unsigned int cmd, void *arg);
+
+
+ /* ----------------------------------------------------------- */
+diff --git a/drivers/media/video/saa7146.h b/drivers/media/video/saa7146.h
+index 2830b5e..9fadb33 100644
+--- a/drivers/media/video/saa7146.h
++++ b/drivers/media/video/saa7146.h
+@@ -25,8 +25,6 @@
+ #include <linux/types.h>
+ #include <linux/wait.h>
+
+-#include <linux/videodev.h>
+-
+ #ifndef O_NONCAP
+ #define O_NONCAP O_TRUNC
+ #endif
+diff --git a/drivers/media/video/saa717x.c b/drivers/media/video/saa717x.c
+index 88c5e94..25bf230 100644
+--- a/drivers/media/video/saa717x.c
++++ b/drivers/media/video/saa717x.c
+@@ -931,7 +931,7 @@ static int saa717x_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+ break;
+
+ case V4L2_CID_HUE:
+- if (ctrl->value < -127 || ctrl->value > 127) {
++ if (ctrl->value < -128 || ctrl->value > 127) {
+ v4l2_err(sd, "invalid hue setting %d\n", ctrl->value);
+ return -ERANGE;
+ }
+@@ -1380,11 +1380,6 @@ static int saa717x_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+ return 0;
+ }
+
+-static int saa717x_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+-}
+-
+ /* ----------------------------------------------------------------------- */
+
+ static const struct v4l2_subdev_core_ops saa717x_core_ops = {
+@@ -1528,10 +1523,7 @@ MODULE_DEVICE_TABLE(i2c, saa717x_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "saa717x",
+- .driverid = I2C_DRIVERID_SAA717X,
+- .command = saa717x_command,
+ .probe = saa717x_probe,
+ .remove = saa717x_remove,
+- .legacy_class = I2C_CLASS_TV_ANALOG | I2C_CLASS_TV_DIGITAL,
+ .id_table = saa717x_id,
+ };
+diff --git a/drivers/media/video/saa7185.c b/drivers/media/video/saa7185.c
+index 6debb65..75747b1 100644
+--- a/drivers/media/video/saa7185.c
++++ b/drivers/media/video/saa7185.c
+@@ -30,52 +30,58 @@
+ #include <asm/uaccess.h>
+ #include <linux/i2c.h>
+ #include <linux/i2c-id.h>
+-#include <linux/videodev.h>
+-#include <linux/video_encoder.h>
+-#include <media/v4l2-common.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <linux/videodev2.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/v4l2-i2c-drv.h>
+
+ MODULE_DESCRIPTION("Philips SAA7185 video encoder driver");
+ MODULE_AUTHOR("Dave Perks");
+ MODULE_LICENSE("GPL");
+
+-
+ static int debug;
+ module_param(debug, int, 0);
+ MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
++
+ /* ----------------------------------------------------------------------- */
+
+ struct saa7185 {
++ struct v4l2_subdev sd;
+ unsigned char reg[128];
+
+- int norm;
+- int enable;
+- int bright;
+- int contrast;
+- int hue;
+- int sat;
++ v4l2_std_id norm;
+ };
+
++static inline struct saa7185 *to_saa7185(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct saa7185, sd);
++}
++
+ /* ----------------------------------------------------------------------- */
+
+-static inline int saa7185_read(struct i2c_client *client)
++static inline int saa7185_read(struct v4l2_subdev *sd)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
+ return i2c_smbus_read_byte(client);
+ }
+
+-static int saa7185_write(struct i2c_client *client, u8 reg, u8 value)
++static int saa7185_write(struct v4l2_subdev *sd, u8 reg, u8 value)
+ {
+- struct saa7185 *encoder = i2c_get_clientdata(client);
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct saa7185 *encoder = to_saa7185(sd);
+
+- v4l_dbg(1, debug, client, "%02x set to %02x\n", reg, value);
++ v4l2_dbg(1, debug, sd, "%02x set to %02x\n", reg, value);
+ encoder->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+ }
+
+-static int saa7185_write_block(struct i2c_client *client,
++static int saa7185_write_block(struct v4l2_subdev *sd,
+ const u8 *data, unsigned int len)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct saa7185 *encoder = to_saa7185(sd);
+ int ret = -1;
+ u8 reg;
+
+@@ -83,7 +89,6 @@ static int saa7185_write_block(struct i2c_client *client,
+ * the adapter understands raw I2C */
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ /* do raw I2C, not smbus compatible */
+- struct saa7185 *encoder = i2c_get_clientdata(client);
+ u8 block_data[32];
+ int block_len;
+
+@@ -104,7 +109,7 @@ static int saa7185_write_block(struct i2c_client *client,
+ /* do some slow I2C emulation kind of thing */
+ while (len >= 2) {
+ reg = *data++;
+- ret = saa7185_write(client, reg, *data++);
++ ret = saa7185_write(sd, reg, *data++);
+ if (ret < 0)
+ break;
+ len -= 2;
+@@ -213,133 +218,106 @@ static const unsigned char init_ntsc[] = {
+ 0x66, 0x21, /* FSC3 */
+ };
+
+-static int saa7185_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- struct saa7185 *encoder = i2c_get_clientdata(client);
+-
+- switch (cmd) {
+- case 0:
+- saa7185_write_block(client, init_common,
+- sizeof(init_common));
+- switch (encoder->norm) {
+-
+- case VIDEO_MODE_NTSC:
+- saa7185_write_block(client, init_ntsc,
+- sizeof(init_ntsc));
+- break;
+-
+- case VIDEO_MODE_PAL:
+- saa7185_write_block(client, init_pal,
+- sizeof(init_pal));
+- break;
+- }
+- break;
+
+- case ENCODER_GET_CAPABILITIES:
+- {
+- struct video_encoder_capability *cap = arg;
++static int saa7185_init(struct v4l2_subdev *sd, u32 val)
++{
++ struct saa7185 *encoder = to_saa7185(sd);
+
+- cap->flags =
+- VIDEO_ENCODER_PAL | VIDEO_ENCODER_NTSC |
+- VIDEO_ENCODER_SECAM | VIDEO_ENCODER_CCIR;
+- cap->inputs = 1;
+- cap->outputs = 1;
+- break;
+- }
++ saa7185_write_block(sd, init_common, sizeof(init_common));
++ if (encoder->norm & V4L2_STD_NTSC)
++ saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc));
++ else
++ saa7185_write_block(sd, init_pal, sizeof(init_pal));
++ return 0;
++}
+
+- case ENCODER_SET_NORM:
+- {
+- int *iarg = arg;
++static int saa7185_s_std_output(struct v4l2_subdev *sd, v4l2_std_id std)
++{
++ struct saa7185 *encoder = to_saa7185(sd);
+
+- //saa7185_write_block(client, init_common, sizeof(init_common));
++ if (std & V4L2_STD_NTSC)
++ saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc));
++ else if (std & V4L2_STD_PAL)
++ saa7185_write_block(sd, init_pal, sizeof(init_pal));
++ else
++ return -EINVAL;
++ encoder->norm = std;
++ return 0;
++}
+
+- switch (*iarg) {
+- case VIDEO_MODE_NTSC:
+- saa7185_write_block(client, init_ntsc,
+- sizeof(init_ntsc));
+- break;
++static int saa7185_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
++{
++ struct saa7185 *encoder = to_saa7185(sd);
+
+- case VIDEO_MODE_PAL:
+- saa7185_write_block(client, init_pal,
+- sizeof(init_pal));
+- break;
++ /* RJ: route->input = 0: input is from SA7111
++ route->input = 1: input is from ZR36060 */
+
+- case VIDEO_MODE_SECAM:
+- default:
+- return -EINVAL;
+- }
+- encoder->norm = *iarg;
+- break;
+- }
+-
+- case ENCODER_SET_INPUT:
+- {
+- int *iarg = arg;
+-
+- /* RJ: *iarg = 0: input is from SA7111
+- *iarg = 1: input is from ZR36060 */
+-
+- switch (*iarg) {
+- case 0:
+- /* Switch RTCE to 1 */
+- saa7185_write(client, 0x61,
+- (encoder->reg[0x61] & 0xf7) | 0x08);
+- saa7185_write(client, 0x6e, 0x01);
+- break;
+-
+- case 1:
+- /* Switch RTCE to 0 */
+- saa7185_write(client, 0x61,
+- (encoder->reg[0x61] & 0xf7) | 0x00);
+- /* SW: a slight sync problem... */
+- saa7185_write(client, 0x6e, 0x00);
+- break;
+-
+- default:
+- return -EINVAL;
+- }
++ switch (route->input) {
++ case 0:
++ /* turn off colorbar */
++ saa7185_write(sd, 0x3a, 0x0f);
++ /* Switch RTCE to 1 */
++ saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x08);
++ saa7185_write(sd, 0x6e, 0x01);
+ break;
+- }
+
+- case ENCODER_SET_OUTPUT:
+- {
+- int *iarg = arg;
+-
+- /* not much choice of outputs */
+- if (*iarg != 0)
+- return -EINVAL;
++ case 1:
++ /* turn off colorbar */
++ saa7185_write(sd, 0x3a, 0x0f);
++ /* Switch RTCE to 0 */
++ saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x00);
++ /* SW: a slight sync problem... */
++ saa7185_write(sd, 0x6e, 0x00);
+ break;
+- }
+-
+- case ENCODER_ENABLE_OUTPUT:
+- {
+- int *iarg = arg;
+
+- encoder->enable = !!*iarg;
+- saa7185_write(client, 0x61,
+- (encoder->reg[0x61] & 0xbf) |
+- (encoder->enable ? 0x00 : 0x40));
++ case 2:
++ /* turn on colorbar */
++ saa7185_write(sd, 0x3a, 0x8f);
++ /* Switch RTCE to 0 */
++ saa7185_write(sd, 0x61, (encoder->reg[0x61] & 0xf7) | 0x08);
++ /* SW: a slight sync problem... */
++ saa7185_write(sd, 0x6e, 0x01);
+ break;
+- }
+
+ default:
+ return -EINVAL;
+ }
+-
+ return 0;
+ }
+
++static int saa7185_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7185, 0);
++}
++
+ /* ----------------------------------------------------------------------- */
+
+-static unsigned short normal_i2c[] = { 0x88 >> 1, I2C_CLIENT_END };
++static const struct v4l2_subdev_core_ops saa7185_core_ops = {
++ .g_chip_ident = saa7185_g_chip_ident,
++ .init = saa7185_init,
++};
+
+-I2C_CLIENT_INSMOD;
++static const struct v4l2_subdev_video_ops saa7185_video_ops = {
++ .s_std_output = saa7185_s_std_output,
++ .s_routing = saa7185_s_routing,
++};
++
++static const struct v4l2_subdev_ops saa7185_ops = {
++ .core = &saa7185_core_ops,
++ .video = &saa7185_video_ops,
++};
++
++
++/* ----------------------------------------------------------------------- */
+
+ static int saa7185_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+ {
+ int i;
+ struct saa7185 *encoder;
++ struct v4l2_subdev *sd;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+@@ -351,28 +329,29 @@ static int saa7185_probe(struct i2c_client *client,
+ encoder = kzalloc(sizeof(struct saa7185), GFP_KERNEL);
+ if (encoder == NULL)
+ return -ENOMEM;
+- encoder->norm = VIDEO_MODE_NTSC;
+- encoder->enable = 1;
+- i2c_set_clientdata(client, encoder);
++ encoder->norm = V4L2_STD_NTSC;
++ sd = &encoder->sd;
++ v4l2_i2c_subdev_init(sd, client, &saa7185_ops);
+
+- i = saa7185_write_block(client, init_common, sizeof(init_common));
++ i = saa7185_write_block(sd, init_common, sizeof(init_common));
+ if (i >= 0)
+- i = saa7185_write_block(client, init_ntsc, sizeof(init_ntsc));
++ i = saa7185_write_block(sd, init_ntsc, sizeof(init_ntsc));
+ if (i < 0)
+- v4l_dbg(1, debug, client, "init error %d\n", i);
++ v4l2_dbg(1, debug, sd, "init error %d\n", i);
+ else
+- v4l_dbg(1, debug, client, "revision 0x%x\n",
+- saa7185_read(client) >> 5);
++ v4l2_dbg(1, debug, sd, "revision 0x%x\n",
++ saa7185_read(sd) >> 5);
+ return 0;
+ }
+
+ static int saa7185_remove(struct i2c_client *client)
+ {
+- struct saa7185 *encoder = i2c_get_clientdata(client);
+-
+- saa7185_write(client, 0x61, (encoder->reg[0x61]) | 0x40); /* SW: output off is active */
+- //saa7185_write(client, 0x3a, (encoder->reg[0x3a]) | 0x80); /* SW: color bar */
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++ struct saa7185 *encoder = to_saa7185(sd);
+
++ v4l2_device_unregister_subdev(sd);
++ /* SW: output off is active */
++ saa7185_write(sd, 0x61, (encoder->reg[0x61]) | 0x40);
+ kfree(encoder);
+ return 0;
+ }
+@@ -387,8 +366,6 @@ MODULE_DEVICE_TABLE(i2c, saa7185_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "saa7185",
+- .driverid = I2C_DRIVERID_SAA7185B,
+- .command = saa7185_command,
+ .probe = saa7185_probe,
+ .remove = saa7185_remove,
+ .id_table = saa7185_id,
+diff --git a/drivers/media/video/saa7191.c b/drivers/media/video/saa7191.c
+index b4018cc..3f523ae 100644
+--- a/drivers/media/video/saa7191.c
++++ b/drivers/media/video/saa7191.c
+@@ -19,9 +19,11 @@
+ #include <linux/mm.h>
+ #include <linux/slab.h>
+
+-#include <linux/videodev.h>
+-#include <linux/video_decoder.h>
++#include <linux/videodev2.h>
+ #include <linux/i2c.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/v4l2-i2c-drv.h>
+
+ #include "saa7191.h"
+
+@@ -32,6 +34,7 @@ MODULE_VERSION(SAA7191_MODULE_VERSION);
+ MODULE_AUTHOR("Mikael Nousiainen <tmnousia@cc.hut.fi>");
+ MODULE_LICENSE("GPL");
+
++
+ // #define SAA7191_DEBUG
+
+ #ifdef SAA7191_DEBUG
+@@ -44,17 +47,20 @@ MODULE_LICENSE("GPL");
+ #define SAA7191_SYNC_DELAY 100 /* milliseconds */
+
+ struct saa7191 {
+- struct i2c_client *client;
++ struct v4l2_subdev sd;
+
+ /* the register values are stored here as the actual
+ * I2C-registers are write-only */
+ u8 reg[25];
+
+ int input;
+- int norm;
++ v4l2_std_id norm;
+ };
+
+-static struct i2c_driver i2c_driver_saa7191;
++static inline struct saa7191 *to_saa7191(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct saa7191, sd);
++}
+
+ static const u8 initseq[] = {
+ 0, /* Subaddress */
+@@ -100,15 +106,14 @@ static const u8 initseq[] = {
+
+ /* SAA7191 register handling */
+
+-static u8 saa7191_read_reg(struct i2c_client *client,
+- u8 reg)
++static u8 saa7191_read_reg(struct v4l2_subdev *sd, u8 reg)
+ {
+- return ((struct saa7191 *)i2c_get_clientdata(client))->reg[reg];
++ return to_saa7191(sd)->reg[reg];
+ }
+
+-static int saa7191_read_status(struct i2c_client *client,
+- u8 *value)
++static int saa7191_read_status(struct v4l2_subdev *sd, u8 *value)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ int ret;
+
+ ret = i2c_master_recv(client, value, 1);
+@@ -121,21 +126,23 @@ static int saa7191_read_status(struct i2c_client *client,
+ }
+
+
+-static int saa7191_write_reg(struct i2c_client *client, u8 reg,
+- u8 value)
++static int saa7191_write_reg(struct v4l2_subdev *sd, u8 reg, u8 value)
+ {
+- ((struct saa7191 *)i2c_get_clientdata(client))->reg[reg] = value;
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
++ to_saa7191(sd)->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+ }
+
+ /* the first byte of data must be the first subaddress number (register) */
+-static int saa7191_write_block(struct i2c_client *client,
++static int saa7191_write_block(struct v4l2_subdev *sd,
+ u8 length, const u8 *data)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ struct saa7191 *decoder = to_saa7191(sd);
+ int i;
+ int ret;
+
+- struct saa7191 *decoder = (struct saa7191 *)i2c_get_clientdata(client);
+ for (i = 0; i < (length - 1); i++) {
+ decoder->reg[data[0] + i] = data[i + 1];
+ }
+@@ -152,14 +159,15 @@ static int saa7191_write_block(struct i2c_client *client,
+
+ /* Helper functions */
+
+-static int saa7191_set_input(struct i2c_client *client, int input)
++static int saa7191_s_routing(struct v4l2_subdev *sd,
++ const struct v4l2_routing *route)
+ {
+- struct saa7191 *decoder = i2c_get_clientdata(client);
+- u8 luma = saa7191_read_reg(client, SAA7191_REG_LUMA);
+- u8 iock = saa7191_read_reg(client, SAA7191_REG_IOCK);
++ struct saa7191 *decoder = to_saa7191(sd);
++ u8 luma = saa7191_read_reg(sd, SAA7191_REG_LUMA);
++ u8 iock = saa7191_read_reg(sd, SAA7191_REG_IOCK);
+ int err;
+
+- switch (input) {
++ switch (route->input) {
+ case SAA7191_INPUT_COMPOSITE: /* Set Composite input */
+ iock &= ~(SAA7191_IOCK_CHRS | SAA7191_IOCK_GPSW1
+ | SAA7191_IOCK_GPSW2);
+@@ -175,54 +183,50 @@ static int saa7191_set_input(struct i2c_client *client, int input)
+ return -EINVAL;
+ }
+
+- err = saa7191_write_reg(client, SAA7191_REG_LUMA, luma);
++ err = saa7191_write_reg(sd, SAA7191_REG_LUMA, luma);
+ if (err)
+ return -EIO;
+- err = saa7191_write_reg(client, SAA7191_REG_IOCK, iock);
++ err = saa7191_write_reg(sd, SAA7191_REG_IOCK, iock);
+ if (err)
+ return -EIO;
+
+- decoder->input = input;
++ decoder->input = route->input;
+
+ return 0;
+ }
+
+-static int saa7191_set_norm(struct i2c_client *client, int norm)
++static int saa7191_s_std(struct v4l2_subdev *sd, v4l2_std_id norm)
+ {
+- struct saa7191 *decoder = i2c_get_clientdata(client);
+- u8 stdc = saa7191_read_reg(client, SAA7191_REG_STDC);
+- u8 ctl3 = saa7191_read_reg(client, SAA7191_REG_CTL3);
+- u8 chcv = saa7191_read_reg(client, SAA7191_REG_CHCV);
++ struct saa7191 *decoder = to_saa7191(sd);
++ u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC);
++ u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3);
++ u8 chcv = saa7191_read_reg(sd, SAA7191_REG_CHCV);
+ int err;
+
+- switch(norm) {
+- case SAA7191_NORM_PAL:
++ if (norm & V4L2_STD_PAL) {
+ stdc &= ~SAA7191_STDC_SECS;
+ ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL);
+ chcv = SAA7191_CHCV_PAL;
+- break;
+- case SAA7191_NORM_NTSC:
++ } else if (norm & V4L2_STD_NTSC) {
+ stdc &= ~SAA7191_STDC_SECS;
+ ctl3 &= ~SAA7191_CTL3_AUFD;
+ ctl3 |= SAA7191_CTL3_FSEL;
+ chcv = SAA7191_CHCV_NTSC;
+- break;
+- case SAA7191_NORM_SECAM:
++ } else if (norm & V4L2_STD_SECAM) {
+ stdc |= SAA7191_STDC_SECS;
+ ctl3 &= ~(SAA7191_CTL3_AUFD | SAA7191_CTL3_FSEL);
+ chcv = SAA7191_CHCV_PAL;
+- break;
+- default:
++ } else {
+ return -EINVAL;
+ }
+
+- err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3);
++ err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3);
+ if (err)
+ return -EIO;
+- err = saa7191_write_reg(client, SAA7191_REG_STDC, stdc);
++ err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc);
+ if (err)
+ return -EIO;
+- err = saa7191_write_reg(client, SAA7191_REG_CHCV, chcv);
++ err = saa7191_write_reg(sd, SAA7191_REG_CHCV, chcv);
+ if (err)
+ return -EIO;
+
+@@ -230,19 +234,19 @@ static int saa7191_set_norm(struct i2c_client *client, int norm)
+
+ dprintk("ctl3: %02x stdc: %02x chcv: %02x\n", ctl3,
+ stdc, chcv);
+- dprintk("norm: %d\n", norm);
++ dprintk("norm: %llx\n", norm);
+
+ return 0;
+ }
+
+-static int saa7191_wait_for_signal(struct i2c_client *client, u8 *status)
++static int saa7191_wait_for_signal(struct v4l2_subdev *sd, u8 *status)
+ {
+ int i = 0;
+
+ dprintk("Checking for signal...\n");
+
+ for (i = 0; i < SAA7191_SYNC_COUNT; i++) {
+- if (saa7191_read_status(client, status))
++ if (saa7191_read_status(sd, status))
+ return -EIO;
+
+ if (((*status) & SAA7191_STATUS_HLCK) == 0) {
+@@ -258,31 +262,34 @@ static int saa7191_wait_for_signal(struct i2c_client *client, u8 *status)
+ return -EBUSY;
+ }
+
+-static int saa7191_autodetect_norm_extended(struct i2c_client *client)
++static int saa7191_querystd(struct v4l2_subdev *sd, v4l2_std_id *norm)
+ {
+- u8 stdc = saa7191_read_reg(client, SAA7191_REG_STDC);
+- u8 ctl3 = saa7191_read_reg(client, SAA7191_REG_CTL3);
++ struct saa7191 *decoder = to_saa7191(sd);
++ u8 stdc = saa7191_read_reg(sd, SAA7191_REG_STDC);
++ u8 ctl3 = saa7191_read_reg(sd, SAA7191_REG_CTL3);
+ u8 status;
++ v4l2_std_id old_norm = decoder->norm;
+ int err = 0;
+
+ dprintk("SAA7191 extended signal auto-detection...\n");
+
++ *norm = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM;
+ stdc &= ~SAA7191_STDC_SECS;
+ ctl3 &= ~(SAA7191_CTL3_FSEL);
+
+- err = saa7191_write_reg(client, SAA7191_REG_STDC, stdc);
++ err = saa7191_write_reg(sd, SAA7191_REG_STDC, stdc);
+ if (err) {
+ err = -EIO;
+ goto out;
+ }
+- err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3);
++ err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3);
+ if (err) {
+ err = -EIO;
+ goto out;
+ }
+
+ ctl3 |= SAA7191_CTL3_AUFD;
+- err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3);
++ err = saa7191_write_reg(sd, SAA7191_REG_CTL3, ctl3);
+ if (err) {
+ err = -EIO;
+ goto out;
+@@ -290,53 +297,54 @@ static int saa7191_autodetect_norm_extended(struct i2c_client *client)
+
+ msleep(SAA7191_SYNC_DELAY);
+
+- err = saa7191_wait_for_signal(client, &status);
++ err = saa7191_wait_for_signal(sd, &status);
+ if (err)
+ goto out;
+
+ if (status & SAA7191_STATUS_FIDT) {
+ /* 60Hz signal -> NTSC */
+ dprintk("60Hz signal: NTSC\n");
+- return saa7191_set_norm(client, SAA7191_NORM_NTSC);
++ *norm = V4L2_STD_NTSC;
++ return 0;
+ }
+
+ /* 50Hz signal */
+ dprintk("50Hz signal: Trying PAL...\n");
+
+ /* try PAL first */
+- err = saa7191_set_norm(client, SAA7191_NORM_PAL);
++ err = saa7191_s_std(sd, V4L2_STD_PAL);
+ if (err)
+ goto out;
+
+ msleep(SAA7191_SYNC_DELAY);
+
+- err = saa7191_wait_for_signal(client, &status);
++ err = saa7191_wait_for_signal(sd, &status);
+ if (err)
+ goto out;
+
+ /* not 50Hz ? */
+ if (status & SAA7191_STATUS_FIDT) {
+ dprintk("No 50Hz signal\n");
+- err = -EAGAIN;
+- goto out;
++ saa7191_s_std(sd, old_norm);
++ return -EAGAIN;
+ }
+
+ if (status & SAA7191_STATUS_CODE) {
+ dprintk("PAL\n");
+- return 0;
++ *norm = V4L2_STD_PAL;
++ return saa7191_s_std(sd, old_norm);
+ }
+
+ dprintk("No color detected with PAL - Trying SECAM...\n");
+
+ /* no color detected ? -> try SECAM */
+- err = saa7191_set_norm(client,
+- SAA7191_NORM_SECAM);
++ err = saa7191_s_std(sd, V4L2_STD_SECAM);
+ if (err)
+ goto out;
+
+ msleep(SAA7191_SYNC_DELAY);
+
+- err = saa7191_wait_for_signal(client, &status);
++ err = saa7191_wait_for_signal(sd, &status);
+ if (err)
+ goto out;
+
+@@ -350,32 +358,17 @@ static int saa7191_autodetect_norm_extended(struct i2c_client *client)
+ if (status & SAA7191_STATUS_CODE) {
+ /* Color detected -> SECAM */
+ dprintk("SECAM\n");
+- return 0;
++ *norm = V4L2_STD_SECAM;
++ return saa7191_s_std(sd, old_norm);
+ }
+
+ dprintk("No color detected with SECAM - Going back to PAL.\n");
+
+- /* still no color detected ?
+- * -> set norm back to PAL */
+- err = saa7191_set_norm(client,
+- SAA7191_NORM_PAL);
+- if (err)
+- goto out;
+-
+ out:
+- ctl3 = saa7191_read_reg(client, SAA7191_REG_CTL3);
+- if (ctl3 & SAA7191_CTL3_AUFD) {
+- ctl3 &= ~(SAA7191_CTL3_AUFD);
+- err = saa7191_write_reg(client, SAA7191_REG_CTL3, ctl3);
+- if (err) {
+- err = -EIO;
+- }
+- }
+-
+- return err;
++ return saa7191_s_std(sd, old_norm);
+ }
+
+-static int saa7191_autodetect_norm(struct i2c_client *client)
++static int saa7191_autodetect_norm(struct v4l2_subdev *sd)
+ {
+ u8 status;
+
+@@ -383,7 +376,7 @@ static int saa7191_autodetect_norm(struct i2c_client *client)
+
+ dprintk("Reading status...\n");
+
+- if (saa7191_read_status(client, &status))
++ if (saa7191_read_status(sd, &status))
+ return -EIO;
+
+ dprintk("Checking for signal...\n");
+@@ -399,26 +392,25 @@ static int saa7191_autodetect_norm(struct i2c_client *client)
+ if (status & SAA7191_STATUS_FIDT) {
+ /* 60hz signal -> NTSC */
+ dprintk("NTSC\n");
+- return saa7191_set_norm(client, SAA7191_NORM_NTSC);
++ return saa7191_s_std(sd, V4L2_STD_NTSC);
+ } else {
+ /* 50hz signal -> PAL */
+ dprintk("PAL\n");
+- return saa7191_set_norm(client, SAA7191_NORM_PAL);
++ return saa7191_s_std(sd, V4L2_STD_PAL);
+ }
+ }
+
+-static int saa7191_get_control(struct i2c_client *client,
+- struct saa7191_control *ctrl)
++static int saa7191_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+ {
+ u8 reg;
+ int ret = 0;
+
+- switch (ctrl->type) {
++ switch (ctrl->id) {
+ case SAA7191_CONTROL_BANDPASS:
+ case SAA7191_CONTROL_BANDPASS_WEIGHT:
+ case SAA7191_CONTROL_CORING:
+- reg = saa7191_read_reg(client, SAA7191_REG_LUMA);
+- switch (ctrl->type) {
++ reg = saa7191_read_reg(sd, SAA7191_REG_LUMA);
++ switch (ctrl->id) {
+ case SAA7191_CONTROL_BANDPASS:
+ ctrl->value = ((s32)reg & SAA7191_LUMA_BPSS_MASK)
+ >> SAA7191_LUMA_BPSS_SHIFT;
+@@ -435,15 +427,15 @@ static int saa7191_get_control(struct i2c_client *client,
+ break;
+ case SAA7191_CONTROL_FORCE_COLOUR:
+ case SAA7191_CONTROL_CHROMA_GAIN:
+- reg = saa7191_read_reg(client, SAA7191_REG_GAIN);
+- if (ctrl->type == SAA7191_CONTROL_FORCE_COLOUR)
++ reg = saa7191_read_reg(sd, SAA7191_REG_GAIN);
++ if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR)
+ ctrl->value = ((s32)reg & SAA7191_GAIN_COLO) ? 1 : 0;
+ else
+ ctrl->value = ((s32)reg & SAA7191_GAIN_LFIS_MASK)
+ >> SAA7191_GAIN_LFIS_SHIFT;
+ break;
+- case SAA7191_CONTROL_HUE:
+- reg = saa7191_read_reg(client, SAA7191_REG_HUEC);
++ case V4L2_CID_HUE:
++ reg = saa7191_read_reg(sd, SAA7191_REG_HUEC);
+ if (reg < 0x80)
+ reg += 0x80;
+ else
+@@ -451,18 +443,18 @@ static int saa7191_get_control(struct i2c_client *client,
+ ctrl->value = (s32)reg;
+ break;
+ case SAA7191_CONTROL_VTRC:
+- reg = saa7191_read_reg(client, SAA7191_REG_STDC);
++ reg = saa7191_read_reg(sd, SAA7191_REG_STDC);
+ ctrl->value = ((s32)reg & SAA7191_STDC_VTRC) ? 1 : 0;
+ break;
+ case SAA7191_CONTROL_LUMA_DELAY:
+- reg = saa7191_read_reg(client, SAA7191_REG_CTL3);
++ reg = saa7191_read_reg(sd, SAA7191_REG_CTL3);
+ ctrl->value = ((s32)reg & SAA7191_CTL3_YDEL_MASK)
+ >> SAA7191_CTL3_YDEL_SHIFT;
+ if (ctrl->value >= 4)
+ ctrl->value -= 8;
+ break;
+ case SAA7191_CONTROL_VNR:
+- reg = saa7191_read_reg(client, SAA7191_REG_CTL4);
++ reg = saa7191_read_reg(sd, SAA7191_REG_CTL4);
+ ctrl->value = ((s32)reg & SAA7191_CTL4_VNOI_MASK)
+ >> SAA7191_CTL4_VNOI_SHIFT;
+ break;
+@@ -473,18 +465,17 @@ static int saa7191_get_control(struct i2c_client *client,
+ return ret;
+ }
+
+-static int saa7191_set_control(struct i2c_client *client,
+- struct saa7191_control *ctrl)
++static int saa7191_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+ {
+ u8 reg;
+ int ret = 0;
+
+- switch (ctrl->type) {
++ switch (ctrl->id) {
+ case SAA7191_CONTROL_BANDPASS:
+ case SAA7191_CONTROL_BANDPASS_WEIGHT:
+ case SAA7191_CONTROL_CORING:
+- reg = saa7191_read_reg(client, SAA7191_REG_LUMA);
+- switch (ctrl->type) {
++ reg = saa7191_read_reg(sd, SAA7191_REG_LUMA);
++ switch (ctrl->id) {
+ case SAA7191_CONTROL_BANDPASS:
+ reg &= ~SAA7191_LUMA_BPSS_MASK;
+ reg |= (ctrl->value << SAA7191_LUMA_BPSS_SHIFT)
+@@ -501,12 +492,12 @@ static int saa7191_set_control(struct i2c_client *client,
+ & SAA7191_LUMA_CORI_MASK;
+ break;
+ }
+- ret = saa7191_write_reg(client, SAA7191_REG_LUMA, reg);
++ ret = saa7191_write_reg(sd, SAA7191_REG_LUMA, reg);
+ break;
+ case SAA7191_CONTROL_FORCE_COLOUR:
+ case SAA7191_CONTROL_CHROMA_GAIN:
+- reg = saa7191_read_reg(client, SAA7191_REG_GAIN);
+- if (ctrl->type == SAA7191_CONTROL_FORCE_COLOUR) {
++ reg = saa7191_read_reg(sd, SAA7191_REG_GAIN);
++ if (ctrl->id == SAA7191_CONTROL_FORCE_COLOUR) {
+ if (ctrl->value)
+ reg |= SAA7191_GAIN_COLO;
+ else
+@@ -516,41 +507,41 @@ static int saa7191_set_control(struct i2c_client *client,
+ reg |= (ctrl->value << SAA7191_GAIN_LFIS_SHIFT)
+ & SAA7191_GAIN_LFIS_MASK;
+ }
+- ret = saa7191_write_reg(client, SAA7191_REG_GAIN, reg);
++ ret = saa7191_write_reg(sd, SAA7191_REG_GAIN, reg);
+ break;
+- case SAA7191_CONTROL_HUE:
++ case V4L2_CID_HUE:
+ reg = ctrl->value & 0xff;
+ if (reg < 0x80)
+ reg += 0x80;
+ else
+ reg -= 0x80;
+- ret = saa7191_write_reg(client, SAA7191_REG_HUEC, reg);
++ ret = saa7191_write_reg(sd, SAA7191_REG_HUEC, reg);
+ break;
+ case SAA7191_CONTROL_VTRC:
+- reg = saa7191_read_reg(client, SAA7191_REG_STDC);
++ reg = saa7191_read_reg(sd, SAA7191_REG_STDC);
+ if (ctrl->value)
+ reg |= SAA7191_STDC_VTRC;
+ else
+ reg &= ~SAA7191_STDC_VTRC;
+- ret = saa7191_write_reg(client, SAA7191_REG_STDC, reg);
++ ret = saa7191_write_reg(sd, SAA7191_REG_STDC, reg);
+ break;
+ case SAA7191_CONTROL_LUMA_DELAY: {
+ s32 value = ctrl->value;
+ if (value < 0)
+ value += 8;
+- reg = saa7191_read_reg(client, SAA7191_REG_CTL3);
++ reg = saa7191_read_reg(sd, SAA7191_REG_CTL3);
+ reg &= ~SAA7191_CTL3_YDEL_MASK;
+ reg |= (value << SAA7191_CTL3_YDEL_SHIFT)
+ & SAA7191_CTL3_YDEL_MASK;
+- ret = saa7191_write_reg(client, SAA7191_REG_CTL3, reg);
++ ret = saa7191_write_reg(sd, SAA7191_REG_CTL3, reg);
+ break;
+ }
+ case SAA7191_CONTROL_VNR:
+- reg = saa7191_read_reg(client, SAA7191_REG_CTL4);
++ reg = saa7191_read_reg(sd, SAA7191_REG_CTL4);
+ reg &= ~SAA7191_CTL4_VNOI_MASK;
+ reg |= (ctrl->value << SAA7191_CTL4_VNOI_SHIFT)
+ & SAA7191_CTL4_VNOI_MASK;
+- ret = saa7191_write_reg(client, SAA7191_REG_CTL4, reg);
++ ret = saa7191_write_reg(sd, SAA7191_REG_CTL4, reg);
+ break;
+ default:
+ ret = -EINVAL;
+@@ -561,247 +552,108 @@ static int saa7191_set_control(struct i2c_client *client,
+
+ /* I2C-interface */
+
+-static int saa7191_attach(struct i2c_adapter *adap, int addr, int kind)
++static int saa7191_g_input_status(struct v4l2_subdev *sd, u32 *status)
+ {
+- int err = 0;
+- struct saa7191 *decoder;
+- struct i2c_client *client;
+-
+- printk(KERN_INFO "Philips SAA7191 driver version %s\n",
+- SAA7191_MODULE_VERSION);
+-
+- client = kzalloc(sizeof(*client), GFP_KERNEL);
+- if (!client)
+- return -ENOMEM;
+- decoder = kzalloc(sizeof(*decoder), GFP_KERNEL);
+- if (!decoder) {
+- err = -ENOMEM;
+- goto out_free_client;
+- }
+-
+- client->addr = addr;
+- client->adapter = adap;
+- client->driver = &i2c_driver_saa7191;
+- client->flags = 0;
+- strcpy(client->name, "saa7191 client");
+- i2c_set_clientdata(client, decoder);
+-
+- decoder->client = client;
+-
+- err = i2c_attach_client(client);
+- if (err)
+- goto out_free_decoder;
+-
+- err = saa7191_write_block(client, sizeof(initseq), initseq);
+- if (err) {
+- printk(KERN_ERR "SAA7191 initialization failed\n");
+- goto out_detach_client;
+- }
+-
+- printk(KERN_INFO "SAA7191 initialized\n");
+-
+- decoder->input = SAA7191_INPUT_COMPOSITE;
+- decoder->norm = SAA7191_NORM_PAL;
+-
+- err = saa7191_autodetect_norm(client);
+- if (err && (err != -EBUSY)) {
+- printk(KERN_ERR "SAA7191: Signal auto-detection failed\n");
+- }
++ u8 status_reg;
++ int res = V4L2_IN_ST_NO_SIGNAL;
+
++ if (saa7191_read_status(sd, &status_reg))
++ return -EIO;
++ if ((status_reg & SAA7191_STATUS_HLCK) == 0)
++ res = 0;
++ if (!(status_reg & SAA7191_STATUS_CODE))
++ res |= V4L2_IN_ST_NO_COLOR;
++ *status = res;
+ return 0;
+-
+-out_detach_client:
+- i2c_detach_client(client);
+-out_free_decoder:
+- kfree(decoder);
+-out_free_client:
+- kfree(client);
+- return err;
+ }
+
+-static int saa7191_probe(struct i2c_adapter *adap)
+-{
+- /* Always connected to VINO */
+- if (adap->id == I2C_HW_SGI_VINO)
+- return saa7191_attach(adap, SAA7191_ADDR, 0);
+- /* Feel free to add probe here :-) */
+- return -ENODEV;
+-}
+
+-static int saa7191_detach(struct i2c_client *client)
++static int saa7191_g_chip_ident(struct v4l2_subdev *sd,
++ struct v4l2_dbg_chip_ident *chip)
+ {
+- struct saa7191 *decoder = i2c_get_clientdata(client);
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+- i2c_detach_client(client);
+- kfree(decoder);
+- kfree(client);
+- return 0;
++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_SAA7191, 0);
+ }
+
+-static int saa7191_command(struct i2c_client *client, unsigned int cmd,
+- void *arg)
+-{
+- struct saa7191 *decoder = i2c_get_clientdata(client);
++/* ----------------------------------------------------------------------- */
+
+- switch (cmd) {
+- case DECODER_GET_CAPABILITIES: {
+- struct video_decoder_capability *cap = arg;
++static const struct v4l2_subdev_core_ops saa7191_core_ops = {
++ .g_chip_ident = saa7191_g_chip_ident,
++ .g_ctrl = saa7191_g_ctrl,
++ .s_ctrl = saa7191_s_ctrl,
++};
+
+- cap->flags = VIDEO_DECODER_PAL | VIDEO_DECODER_NTSC |
+- VIDEO_DECODER_SECAM | VIDEO_DECODER_AUTO;
+- cap->inputs = (client->adapter->id == I2C_HW_SGI_VINO) ? 2 : 1;
+- cap->outputs = 1;
+- break;
+- }
+- case DECODER_GET_STATUS: {
+- int *iarg = arg;
+- u8 status;
+- int res = 0;
++static const struct v4l2_subdev_tuner_ops saa7191_tuner_ops = {
++ .s_std = saa7191_s_std,
++};
+
+- if (saa7191_read_status(client, &status)) {
+- return -EIO;
+- }
+- if ((status & SAA7191_STATUS_HLCK) == 0)
+- res |= DECODER_STATUS_GOOD;
+- if (status & SAA7191_STATUS_CODE)
+- res |= DECODER_STATUS_COLOR;
+- switch (decoder->norm) {
+- case SAA7191_NORM_NTSC:
+- res |= DECODER_STATUS_NTSC;
+- break;
+- case SAA7191_NORM_PAL:
+- res |= DECODER_STATUS_PAL;
+- break;
+- case SAA7191_NORM_SECAM:
+- res |= DECODER_STATUS_SECAM;
+- break;
+- case SAA7191_NORM_AUTO:
+- default:
+- if (status & SAA7191_STATUS_FIDT)
+- res |= DECODER_STATUS_NTSC;
+- else
+- res |= DECODER_STATUS_PAL;
+- break;
+- }
+- *iarg = res;
+- break;
+- }
+- case DECODER_SET_NORM: {
+- int *iarg = arg;
+-
+- switch (*iarg) {
+- case VIDEO_MODE_AUTO:
+- return saa7191_autodetect_norm(client);
+- case VIDEO_MODE_PAL:
+- return saa7191_set_norm(client, SAA7191_NORM_PAL);
+- case VIDEO_MODE_NTSC:
+- return saa7191_set_norm(client, SAA7191_NORM_NTSC);
+- case VIDEO_MODE_SECAM:
+- return saa7191_set_norm(client, SAA7191_NORM_SECAM);
+- default:
+- return -EINVAL;
+- }
+- break;
+- }
+- case DECODER_SET_INPUT: {
+- int *iarg = arg;
+-
+- switch (client->adapter->id) {
+- case I2C_HW_SGI_VINO:
+- return saa7191_set_input(client, *iarg);
+- default:
+- if (*iarg != 0)
+- return -EINVAL;
+- }
+- break;
+- }
+- case DECODER_SET_OUTPUT: {
+- int *iarg = arg;
++static const struct v4l2_subdev_video_ops saa7191_video_ops = {
++ .s_routing = saa7191_s_routing,
++ .querystd = saa7191_querystd,
++ .g_input_status = saa7191_g_input_status,
++};
+
+- /* not much choice of outputs */
+- if (*iarg != 0)
+- return -EINVAL;
+- break;
+- }
+- case DECODER_ENABLE_OUTPUT: {
+- /* Always enabled */
+- break;
+- }
+- case DECODER_SET_PICTURE: {
+- struct video_picture *pic = arg;
+- unsigned val;
+- int err;
++static const struct v4l2_subdev_ops saa7191_ops = {
++ .core = &saa7191_core_ops,
++ .video = &saa7191_video_ops,
++ .tuner = &saa7191_tuner_ops,
++};
+
+- val = (pic->hue >> 8) - 0x80;
++static int saa7191_probe(struct i2c_client *client,
++ const struct i2c_device_id *id)
++{
++ int err = 0;
++ struct saa7191 *decoder;
++ struct v4l2_subdev *sd;
+
+- err = saa7191_write_reg(client, SAA7191_REG_HUEC, val);
+- if (err)
+- return -EIO;
++ v4l_info(client, "chip found @ 0x%x (%s)\n",
++ client->addr << 1, client->adapter->name);
+
+- break;
+- }
+- case DECODER_SAA7191_GET_STATUS: {
+- struct saa7191_status *status = arg;
+- u8 status_reg;
++ decoder = kzalloc(sizeof(*decoder), GFP_KERNEL);
++ if (!decoder)
++ return -ENOMEM;
+
+- if (saa7191_read_status(client, &status_reg))
+- return -EIO;
++ sd = &decoder->sd;
++ v4l2_i2c_subdev_init(sd, client, &saa7191_ops);
+
+- status->signal = ((status_reg & SAA7191_STATUS_HLCK) == 0)
+- ? 1 : 0;
+- status->signal_60hz = (status_reg & SAA7191_STATUS_FIDT)
+- ? 1 : 0;
+- status->color = (status_reg & SAA7191_STATUS_CODE) ? 1 : 0;
++ err = saa7191_write_block(sd, sizeof(initseq), initseq);
++ if (err) {
++ printk(KERN_ERR "SAA7191 initialization failed\n");
++ kfree(decoder);
++ return err;
++ }
+
+- status->input = decoder->input;
+- status->norm = decoder->norm;
++ printk(KERN_INFO "SAA7191 initialized\n");
+
+- break;
+- }
+- case DECODER_SAA7191_SET_NORM: {
+- int *norm = arg;
+-
+- switch (*norm) {
+- case SAA7191_NORM_AUTO:
+- return saa7191_autodetect_norm(client);
+- case SAA7191_NORM_AUTO_EXT:
+- return saa7191_autodetect_norm_extended(client);
+- default:
+- return saa7191_set_norm(client, *norm);
+- }
+- }
+- case DECODER_SAA7191_GET_CONTROL: {
+- return saa7191_get_control(client, arg);
+- }
+- case DECODER_SAA7191_SET_CONTROL: {
+- return saa7191_set_control(client, arg);
+- }
+- default:
+- return -EINVAL;
+- }
++ decoder->input = SAA7191_INPUT_COMPOSITE;
++ decoder->norm = V4L2_STD_PAL;
++
++ err = saa7191_autodetect_norm(sd);
++ if (err && (err != -EBUSY))
++ printk(KERN_ERR "SAA7191: Signal auto-detection failed\n");
+
+ return 0;
+ }
+
+-static struct i2c_driver i2c_driver_saa7191 = {
+- .driver = {
+- .name = "saa7191",
+- },
+- .id = I2C_DRIVERID_SAA7191,
+- .attach_adapter = saa7191_probe,
+- .detach_client = saa7191_detach,
+- .command = saa7191_command
+-};
+-
+-static int saa7191_init(void)
++static int saa7191_remove(struct i2c_client *client)
+ {
+- return i2c_add_driver(&i2c_driver_saa7191);
+-}
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
+
+-static void saa7191_exit(void)
+-{
+- i2c_del_driver(&i2c_driver_saa7191);
++ v4l2_device_unregister_subdev(sd);
++ kfree(to_saa7191(sd));
++ return 0;
+ }
+
+-module_init(saa7191_init);
+-module_exit(saa7191_exit);
++static const struct i2c_device_id saa7191_id[] = {
++ { "saa7191", 0 },
++ { }
++};
++MODULE_DEVICE_TABLE(i2c, saa7191_id);
++
++static struct v4l2_i2c_driver_data v4l2_i2c_data = {
++ .name = "saa7191",
++ .probe = saa7191_probe,
++ .remove = saa7191_remove,
++ .id_table = saa7191_id,
++};
+diff --git a/drivers/media/video/saa7191.h b/drivers/media/video/saa7191.h
+index a2310da..803c74d 100644
+--- a/drivers/media/video/saa7191.h
++++ b/drivers/media/video/saa7191.h
+@@ -176,11 +176,9 @@
+ #define SAA7191_INPUT_COMPOSITE 0
+ #define SAA7191_INPUT_SVIDEO 1
+
+-#define SAA7191_NORM_AUTO 0
+ #define SAA7191_NORM_PAL 1
+ #define SAA7191_NORM_NTSC 2
+ #define SAA7191_NORM_SECAM 3
+-#define SAA7191_NORM_AUTO_EXT 4 /* extended auto-detection */
+
+ struct saa7191_status {
+ /* 0=no signal, 1=signal detected */
+@@ -232,24 +230,16 @@ struct saa7191_status {
+ #define SAA7191_VNR_MAX 0x03
+ #define SAA7191_VNR_DEFAULT 0x00
+
+-#define SAA7191_CONTROL_BANDPASS 0
+-#define SAA7191_CONTROL_BANDPASS_WEIGHT 1
+-#define SAA7191_CONTROL_CORING 2
+-#define SAA7191_CONTROL_FORCE_COLOUR 3 /* boolean */
+-#define SAA7191_CONTROL_CHROMA_GAIN 4
+-#define SAA7191_CONTROL_HUE 5
+-#define SAA7191_CONTROL_VTRC 6 /* boolean */
+-#define SAA7191_CONTROL_LUMA_DELAY 7
+-#define SAA7191_CONTROL_VNR 8
+-
+-struct saa7191_control {
+- u8 type;
+- s32 value;
+-};
++#define SAA7191_CONTROL_BANDPASS (V4L2_CID_PRIVATE_BASE + 0)
++#define SAA7191_CONTROL_BANDPASS_WEIGHT (V4L2_CID_PRIVATE_BASE + 1)
++#define SAA7191_CONTROL_CORING (V4L2_CID_PRIVATE_BASE + 2)
++#define SAA7191_CONTROL_FORCE_COLOUR (V4L2_CID_PRIVATE_BASE + 3)
++#define SAA7191_CONTROL_CHROMA_GAIN (V4L2_CID_PRIVATE_BASE + 4)
++#define SAA7191_CONTROL_VTRC (V4L2_CID_PRIVATE_BASE + 5)
++#define SAA7191_CONTROL_LUMA_DELAY (V4L2_CID_PRIVATE_BASE + 6)
++#define SAA7191_CONTROL_VNR (V4L2_CID_PRIVATE_BASE + 7)
+
+ #define DECODER_SAA7191_GET_STATUS _IOR('d', 195, struct saa7191_status)
+ #define DECODER_SAA7191_SET_NORM _IOW('d', 196, int)
+-#define DECODER_SAA7191_GET_CONTROL _IOR('d', 197, struct saa7191_control)
+-#define DECODER_SAA7191_SET_CONTROL _IOW('d', 198, struct saa7191_control)
+
+ #endif
+diff --git a/drivers/media/video/sh_mobile_ceu_camera.c b/drivers/media/video/sh_mobile_ceu_camera.c
+index ddcb81d..b5e37a5 100644
+--- a/drivers/media/video/sh_mobile_ceu_camera.c
++++ b/drivers/media/video/sh_mobile_ceu_camera.c
+@@ -94,13 +94,37 @@ struct sh_mobile_ceu_dev {
+ spinlock_t lock;
+ struct list_head capture;
+ struct videobuf_buffer *active;
+- int is_interlace;
++ int is_interlaced;
+
+ struct sh_mobile_ceu_info *pdata;
+
+ const struct soc_camera_data_format *camera_fmt;
+ };
+
++static unsigned long make_bus_param(struct sh_mobile_ceu_dev *pcdev)
++{
++ unsigned long flags;
++
++ flags = SOCAM_MASTER |
++ SOCAM_PCLK_SAMPLE_RISING |
++ SOCAM_HSYNC_ACTIVE_HIGH |
++ SOCAM_HSYNC_ACTIVE_LOW |
++ SOCAM_VSYNC_ACTIVE_HIGH |
++ SOCAM_VSYNC_ACTIVE_LOW |
++ SOCAM_DATA_ACTIVE_HIGH;
++
++ if (pcdev->pdata->flags & SH_CEU_FLAG_USE_8BIT_BUS)
++ flags |= SOCAM_DATAWIDTH_8;
++
++ if (pcdev->pdata->flags & SH_CEU_FLAG_USE_16BIT_BUS)
++ flags |= SOCAM_DATAWIDTH_16;
++
++ if (flags & SOCAM_DATAWIDTH_MASK)
++ return flags;
++
++ return 0;
++}
++
+ static void ceu_write(struct sh_mobile_ceu_dev *priv,
+ unsigned long reg_offs, u32 data)
+ {
+@@ -150,6 +174,7 @@ static void free_buffer(struct videobuf_queue *vq,
+ if (in_interrupt())
+ BUG();
+
++ videobuf_waiton(&buf->vb, 0, 0);
+ videobuf_dma_contig_free(vq, &buf->vb);
+ dev_dbg(&icd->dev, "%s freed\n", __func__);
+ buf->vb.state = VIDEOBUF_NEEDS_INIT;
+@@ -181,7 +206,7 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
+
+ phys_addr_top = videobuf_to_dma_contig(pcdev->active);
+ ceu_write(pcdev, CDAYR, phys_addr_top);
+- if (pcdev->is_interlace) {
++ if (pcdev->is_interlaced) {
+ phys_addr_bottom = phys_addr_top + icd->width;
+ ceu_write(pcdev, CDBYR, phys_addr_bottom);
+ }
+@@ -193,7 +218,7 @@ static void sh_mobile_ceu_capture(struct sh_mobile_ceu_dev *pcdev)
+ case V4L2_PIX_FMT_NV61:
+ phys_addr_top += icd->width * icd->height;
+ ceu_write(pcdev, CDACR, phys_addr_top);
+- if (pcdev->is_interlace) {
++ if (pcdev->is_interlaced) {
+ phys_addr_bottom = phys_addr_top + icd->width;
+ ceu_write(pcdev, CDBCR, phys_addr_bottom);
+ }
+@@ -396,7 +421,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
+
+ camera_flags = icd->ops->query_bus_param(icd);
+ common_flags = soc_camera_bus_param_compatible(camera_flags,
+- pcdev->pdata->flags);
++ make_bus_param(pcdev));
+ if (!common_flags)
+ return -EINVAL;
+
+@@ -457,7 +482,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
+ ceu_write(pcdev, CAMCR, value);
+
+ ceu_write(pcdev, CAPCR, 0x00300000);
+- ceu_write(pcdev, CAIFR, (pcdev->is_interlace) ? 0x101 : 0);
++ ceu_write(pcdev, CAIFR, pcdev->is_interlaced ? 0x101 : 0);
+
+ mdelay(1);
+
+@@ -473,7 +498,7 @@ static int sh_mobile_ceu_set_bus_param(struct soc_camera_device *icd,
+ }
+
+ height = icd->height;
+- if (pcdev->is_interlace) {
++ if (pcdev->is_interlaced) {
+ height /= 2;
+ cdwdr_width *= 2;
+ }
+@@ -517,7 +542,7 @@ static int sh_mobile_ceu_try_bus_param(struct soc_camera_device *icd)
+
+ camera_flags = icd->ops->query_bus_param(icd);
+ common_flags = soc_camera_bus_param_compatible(camera_flags,
+- pcdev->pdata->flags);
++ make_bus_param(pcdev));
+ if (!common_flags)
+ return -EINVAL;
+
+@@ -562,11 +587,29 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx,
+ if (ret < 0)
+ return 0;
+
++ /* Beginning of a pass */
++ if (!idx)
++ icd->host_priv = NULL;
++
+ switch (icd->formats[idx].fourcc) {
+ case V4L2_PIX_FMT_UYVY:
+ case V4L2_PIX_FMT_VYUY:
+ case V4L2_PIX_FMT_YUYV:
+ case V4L2_PIX_FMT_YVYU:
++ if (icd->host_priv)
++ goto add_single_format;
++
++ /*
++ * Our case is simple so far: for any of the above four camera
++ * formats we add all our four synthesized NV* formats, so,
++ * just marking the device with a single flag suffices. If
++ * the format generation rules are more complex, you would have
++ * to actually hang your already added / counted formats onto
++ * the host_priv pointer and check whether the format you're
++ * going to add now is already there.
++ */
++ icd->host_priv = (void *)sh_mobile_ceu_formats;
++
+ n = ARRAY_SIZE(sh_mobile_ceu_formats);
+ formats += n;
+ for (k = 0; xlate && k < n; k++) {
+@@ -579,6 +622,7 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx,
+ icd->formats[idx].name);
+ }
+ default:
++add_single_format:
+ /* Generic pass-through */
+ formats++;
+ if (xlate) {
+@@ -595,24 +639,30 @@ static int sh_mobile_ceu_get_formats(struct soc_camera_device *icd, int idx,
+ return formats;
+ }
+
++static int sh_mobile_ceu_set_crop(struct soc_camera_device *icd,
++ struct v4l2_rect *rect)
++{
++ return icd->ops->set_crop(icd, rect);
++}
++
+ static int sh_mobile_ceu_set_fmt(struct soc_camera_device *icd,
+- __u32 pixfmt, struct v4l2_rect *rect)
++ struct v4l2_format *f)
+ {
+ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+ struct sh_mobile_ceu_dev *pcdev = ici->priv;
++ __u32 pixfmt = f->fmt.pix.pixelformat;
+ const struct soc_camera_format_xlate *xlate;
++ struct v4l2_format cam_f = *f;
+ int ret;
+
+- if (!pixfmt)
+- return icd->ops->set_fmt(icd, pixfmt, rect);
+-
+ xlate = soc_camera_xlate_by_fourcc(icd, pixfmt);
+ if (!xlate) {
+ dev_warn(&ici->dev, "Format %x not found\n", pixfmt);
+ return -EINVAL;
+ }
+
+- ret = icd->ops->set_fmt(icd, xlate->cam_fmt->fourcc, rect);
++ cam_f.fmt.pix.pixelformat = xlate->cam_fmt->fourcc;
++ ret = icd->ops->set_fmt(icd, &cam_f);
+
+ if (!ret) {
+ icd->buswidth = xlate->buswidth;
+@@ -662,13 +712,13 @@ static int sh_mobile_ceu_try_fmt(struct soc_camera_device *icd,
+
+ switch (f->fmt.pix.field) {
+ case V4L2_FIELD_INTERLACED:
+- pcdev->is_interlace = 1;
++ pcdev->is_interlaced = 1;
+ break;
+ case V4L2_FIELD_ANY:
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ /* fall-through */
+ case V4L2_FIELD_NONE:
+- pcdev->is_interlace = 0;
++ pcdev->is_interlaced = 0;
+ break;
+ default:
+ ret = -EINVAL;
+@@ -734,7 +784,8 @@ static void sh_mobile_ceu_init_videobuf(struct videobuf_queue *q,
+ &sh_mobile_ceu_videobuf_ops,
+ &ici->dev, &pcdev->lock,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE,
+- V4L2_FIELD_ANY,
++ pcdev->is_interlaced ?
++ V4L2_FIELD_INTERLACED : V4L2_FIELD_NONE,
+ sizeof(struct sh_mobile_ceu_buffer),
+ icd);
+ }
+@@ -744,6 +795,7 @@ static struct soc_camera_host_ops sh_mobile_ceu_host_ops = {
+ .add = sh_mobile_ceu_add_device,
+ .remove = sh_mobile_ceu_remove_device,
+ .get_formats = sh_mobile_ceu_get_formats,
++ .set_crop = sh_mobile_ceu_set_crop,
+ .set_fmt = sh_mobile_ceu_set_fmt,
+ .try_fmt = sh_mobile_ceu_try_fmt,
+ .reqbufs = sh_mobile_ceu_reqbufs,
+diff --git a/drivers/media/video/sn9c102/sn9c102_devtable.h b/drivers/media/video/sn9c102/sn9c102_devtable.h
+index 8cb3457..38a7160 100644
+--- a/drivers/media/video/sn9c102/sn9c102_devtable.h
++++ b/drivers/media/video/sn9c102/sn9c102_devtable.h
+@@ -96,9 +96,7 @@ static const struct usb_device_id sn9c102_id_table[] = {
+ #if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE
+ { SN9C102_USB_DEVICE(0x045e, 0x00f5, BRIDGE_SN9C105), },
+ { SN9C102_USB_DEVICE(0x045e, 0x00f7, BRIDGE_SN9C105), },
+-#endif
+ { SN9C102_USB_DEVICE(0x0471, 0x0327, BRIDGE_SN9C105), },
+-#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE
+ { SN9C102_USB_DEVICE(0x0471, 0x0328, BRIDGE_SN9C105), },
+ #endif
+ { SN9C102_USB_DEVICE(0x0c45, 0x60c0, BRIDGE_SN9C105), },
+@@ -123,7 +121,9 @@ static const struct usb_device_id sn9c102_id_table[] = {
+ { SN9C102_USB_DEVICE(0x0c45, 0x613a, BRIDGE_SN9C120), },
+ #endif
+ { SN9C102_USB_DEVICE(0x0c45, 0x613b, BRIDGE_SN9C120), },
++#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE
+ { SN9C102_USB_DEVICE(0x0c45, 0x613c, BRIDGE_SN9C120), },
++#endif
+ { SN9C102_USB_DEVICE(0x0c45, 0x613e, BRIDGE_SN9C120), },
+ { }
+ };
+diff --git a/drivers/media/video/soc_camera.c b/drivers/media/video/soc_camera.c
+index fcb05f0..6d8bfd4 100644
+--- a/drivers/media/video/soc_camera.c
++++ b/drivers/media/video/soc_camera.c
+@@ -30,6 +30,10 @@
+ #include <media/videobuf-core.h>
+ #include <media/soc_camera.h>
+
++/* Default to VGA resolution */
++#define DEFAULT_WIDTH 640
++#define DEFAULT_HEIGHT 480
++
+ static LIST_HEAD(hosts);
+ static LIST_HEAD(devices);
+ static DEFINE_MUTEX(list_lock);
+@@ -256,6 +260,46 @@ static void soc_camera_free_user_formats(struct soc_camera_device *icd)
+ vfree(icd->user_formats);
+ }
+
++/* Called with .vb_lock held */
++static int soc_camera_set_fmt(struct soc_camera_file *icf,
++ struct v4l2_format *f)
++{
++ struct soc_camera_device *icd = icf->icd;
++ struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
++ struct v4l2_pix_format *pix = &f->fmt.pix;
++ int ret;
++
++ /* We always call try_fmt() before set_fmt() or set_crop() */
++ ret = ici->ops->try_fmt(icd, f);
++ if (ret < 0)
++ return ret;
++
++ ret = ici->ops->set_fmt(icd, f);
++ if (ret < 0) {
++ return ret;
++ } else if (!icd->current_fmt ||
++ icd->current_fmt->fourcc != pix->pixelformat) {
++ dev_err(&ici->dev,
++ "Host driver hasn't set up current format correctly!\n");
++ return -EINVAL;
++ }
++
++ icd->width = pix->width;
++ icd->height = pix->height;
++ icf->vb_vidq.field =
++ icd->field = pix->field;
++
++ if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
++ dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n",
++ f->type);
++
++ dev_dbg(&icd->dev, "set width: %d height: %d\n",
++ icd->width, icd->height);
++
++ /* set physical bus parameters */
++ return ici->ops->set_bus_param(icd, pix->pixelformat);
++}
++
+ static int soc_camera_open(struct file *file)
+ {
+ struct video_device *vdev;
+@@ -297,14 +341,28 @@ static int soc_camera_open(struct file *file)
+
+ /* Now we really have to activate the camera */
+ if (icd->use_count == 1) {
+- ret = soc_camera_init_user_formats(icd);
+- if (ret < 0)
+- goto eiufmt;
++ /* Restore parameters before the last close() per V4L2 API */
++ struct v4l2_format f = {
++ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
++ .fmt.pix = {
++ .width = icd->width,
++ .height = icd->height,
++ .field = icd->field,
++ .pixelformat = icd->current_fmt->fourcc,
++ .colorspace = icd->current_fmt->colorspace,
++ },
++ };
++
+ ret = ici->ops->add(icd);
+ if (ret < 0) {
+ dev_err(&icd->dev, "Couldn't activate the camera: %d\n", ret);
+ goto eiciadd;
+ }
++
++ /* Try to configure with default parameters */
++ ret = soc_camera_set_fmt(icf, &f);
++ if (ret < 0)
++ goto esfmt;
+ }
+
+ mutex_unlock(&icd->video_lock);
+@@ -316,10 +374,13 @@ static int soc_camera_open(struct file *file)
+
+ return 0;
+
+- /* First two errors are entered with the .video_lock held */
++ /*
++ * First three errors are entered with the .video_lock held
++ * and use_count == 1
++ */
++esfmt:
++ ici->ops->remove(icd);
+ eiciadd:
+- soc_camera_free_user_formats(icd);
+-eiufmt:
+ icd->use_count--;
+ mutex_unlock(&icd->video_lock);
+ module_put(ici->ops->owner);
+@@ -339,10 +400,9 @@ static int soc_camera_close(struct file *file)
+
+ mutex_lock(&icd->video_lock);
+ icd->use_count--;
+- if (!icd->use_count) {
++ if (!icd->use_count)
+ ici->ops->remove(icd);
+- soc_camera_free_user_formats(icd);
+- }
++
+ mutex_unlock(&icd->video_lock);
+
+ module_put(icd->ops->owner);
+@@ -415,18 +475,10 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
+ {
+ struct soc_camera_file *icf = file->private_data;
+ struct soc_camera_device *icd = icf->icd;
+- struct soc_camera_host *ici = to_soc_camera_host(icd->dev.parent);
+- struct v4l2_pix_format *pix = &f->fmt.pix;
+- __u32 pixfmt = pix->pixelformat;
+ int ret;
+- struct v4l2_rect rect;
+
+ WARN_ON(priv != file->private_data);
+
+- ret = soc_camera_try_fmt_vid_cap(file, priv, f);
+- if (ret < 0)
+- return ret;
+-
+ mutex_lock(&icf->vb_vidq.vb_lock);
+
+ if (videobuf_queue_is_busy(&icf->vb_vidq)) {
+@@ -435,33 +487,7 @@ static int soc_camera_s_fmt_vid_cap(struct file *file, void *priv,
+ goto unlock;
+ }
+
+- rect.left = icd->x_current;
+- rect.top = icd->y_current;
+- rect.width = pix->width;
+- rect.height = pix->height;
+- ret = ici->ops->set_fmt(icd, pix->pixelformat, &rect);
+- if (ret < 0) {
+- goto unlock;
+- } else if (!icd->current_fmt ||
+- icd->current_fmt->fourcc != pixfmt) {
+- dev_err(&ici->dev,
+- "Host driver hasn't set up current format correctly!\n");
+- ret = -EINVAL;
+- goto unlock;
+- }
+-
+- icd->width = rect.width;
+- icd->height = rect.height;
+- icf->vb_vidq.field = pix->field;
+- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- dev_warn(&icd->dev, "Attention! Wrong buf-type %d\n",
+- f->type);
+-
+- dev_dbg(&icd->dev, "set width: %d height: %d\n",
+- icd->width, icd->height);
+-
+- /* set physical bus parameters */
+- ret = ici->ops->set_bus_param(icd, pixfmt);
++ ret = soc_camera_set_fmt(icf, f);
+
+ unlock:
+ mutex_unlock(&icf->vb_vidq.vb_lock);
+@@ -648,8 +674,8 @@ static int soc_camera_cropcap(struct file *file, void *fh,
+ a->bounds.height = icd->height_max;
+ a->defrect.left = icd->x_min;
+ a->defrect.top = icd->y_min;
+- a->defrect.width = 640;
+- a->defrect.height = 480;
++ a->defrect.width = DEFAULT_WIDTH;
++ a->defrect.height = DEFAULT_HEIGHT;
+ a->pixelaspect.numerator = 1;
+ a->pixelaspect.denominator = 1;
+
+@@ -685,7 +711,7 @@ static int soc_camera_s_crop(struct file *file, void *fh,
+ /* Cropping is allowed during a running capture, guard consistency */
+ mutex_lock(&icf->vb_vidq.vb_lock);
+
+- ret = ici->ops->set_fmt(icd, 0, &a->c);
++ ret = ici->ops->set_crop(icd, &a->c);
+ if (!ret) {
+ icd->width = a->c.width;
+ icd->height = a->c.height;
+@@ -844,9 +870,18 @@ static int soc_camera_probe(struct device *dev)
+ qctrl = soc_camera_find_qctrl(icd->ops, V4L2_CID_EXPOSURE);
+ icd->exposure = qctrl ? qctrl->default_value :
+ (unsigned short)~0;
++
++ ret = soc_camera_init_user_formats(icd);
++ if (ret < 0)
++ goto eiufmt;
++
++ icd->height = DEFAULT_HEIGHT;
++ icd->width = DEFAULT_WIDTH;
++ icd->field = V4L2_FIELD_ANY;
+ }
+- ici->ops->remove(icd);
+
++eiufmt:
++ ici->ops->remove(icd);
+ eiadd:
+ mutex_unlock(&icd->video_lock);
+ module_put(ici->ops->owner);
+@@ -865,6 +900,8 @@ static int soc_camera_remove(struct device *dev)
+ if (icd->ops->remove)
+ icd->ops->remove(icd);
+
++ soc_camera_free_user_formats(icd);
++
+ return 0;
+ }
+
+@@ -918,6 +955,7 @@ int soc_camera_host_register(struct soc_camera_host *ici)
+ if (!ici || !ici->ops ||
+ !ici->ops->try_fmt ||
+ !ici->ops->set_fmt ||
++ !ici->ops->set_crop ||
+ !ici->ops->set_bus_param ||
+ !ici->ops->querycap ||
+ !ici->ops->init_videobuf ||
+@@ -998,6 +1036,7 @@ int soc_camera_device_register(struct soc_camera_device *icd)
+ !icd->ops->release ||
+ !icd->ops->start_capture ||
+ !icd->ops->stop_capture ||
++ !icd->ops->set_crop ||
+ !icd->ops->set_fmt ||
+ !icd->ops->try_fmt ||
+ !icd->ops->query_bus_param ||
+diff --git a/drivers/media/video/soc_camera_platform.c b/drivers/media/video/soc_camera_platform.c
+index 013ab06..c486763 100644
+--- a/drivers/media/video/soc_camera_platform.c
++++ b/drivers/media/video/soc_camera_platform.c
+@@ -79,8 +79,14 @@ soc_camera_platform_query_bus_param(struct soc_camera_device *icd)
+ return p->bus_param;
+ }
+
++static int soc_camera_platform_set_crop(struct soc_camera_device *icd,
++ struct v4l2_rect *rect)
++{
++ return 0;
++}
++
+ static int soc_camera_platform_set_fmt(struct soc_camera_device *icd,
+- __u32 pixfmt, struct v4l2_rect *rect)
++ struct v4l2_format *f)
+ {
+ return 0;
+ }
+@@ -125,6 +131,7 @@ static struct soc_camera_ops soc_camera_platform_ops = {
+ .release = soc_camera_platform_release,
+ .start_capture = soc_camera_platform_start_capture,
+ .stop_capture = soc_camera_platform_stop_capture,
++ .set_crop = soc_camera_platform_set_crop,
+ .set_fmt = soc_camera_platform_set_fmt,
+ .try_fmt = soc_camera_platform_try_fmt,
+ .set_bus_param = soc_camera_platform_set_bus_param,
+diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c
+index 26378cf..1a6d39c 100644
+--- a/drivers/media/video/stk-webcam.c
++++ b/drivers/media/video/stk-webcam.c
+@@ -933,8 +933,6 @@ static int stk_vidioc_s_ctrl(struct file *filp,
+ static int stk_vidioc_enum_fmt_vid_cap(struct file *filp,
+ void *priv, struct v4l2_fmtdesc *fmtd)
+ {
+- fmtd->flags = 0;
+-
+ switch (fmtd->index) {
+ case 0:
+ fmtd->pixelformat = V4L2_PIX_FMT_RGB565;
+@@ -992,7 +990,6 @@ static int stk_vidioc_g_fmt_vid_cap(struct file *filp,
+ pix_format->height = stk_sizes[i].h;
+ pix_format->field = V4L2_FIELD_NONE;
+ pix_format->colorspace = V4L2_COLORSPACE_SRGB;
+- pix_format->priv = 0;
+ pix_format->pixelformat = dev->vsettings.palette;
+ if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8)
+ pix_format->bytesperline = pix_format->width;
+@@ -1115,8 +1112,6 @@ static int stk_vidioc_reqbufs(struct file *filp,
+
+ if (dev == NULL)
+ return -ENODEV;
+- if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+ if (rb->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+ if (is_streaming(dev)
+@@ -1139,16 +1134,10 @@ static int stk_vidioc_reqbufs(struct file *filp,
+ static int stk_vidioc_querybuf(struct file *filp,
+ void *priv, struct v4l2_buffer *buf)
+ {
+- int index;
+ struct stk_camera *dev = priv;
+ struct stk_sio_buffer *sbuf;
+
+- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+-
+- index = buf->index;
+-
+- if (index < 0 || index >= dev->n_sbufs)
++ if (buf->index < 0 || buf->index >= dev->n_sbufs)
+ return -EINVAL;
+ sbuf = dev->sio_bufs + buf->index;
+ *buf = sbuf->v4lbuf;
+@@ -1161,8 +1150,6 @@ static int stk_vidioc_qbuf(struct file *filp,
+ struct stk_camera *dev = priv;
+ struct stk_sio_buffer *sbuf;
+ unsigned long flags;
+- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+
+ if (buf->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+@@ -1189,8 +1176,7 @@ static int stk_vidioc_dqbuf(struct file *filp,
+ unsigned long flags;
+ int ret;
+
+- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE
+- || !is_streaming(dev))
++ if (!is_streaming(dev))
+ return -EINVAL;
+
+ if (filp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full))
+@@ -1249,16 +1235,10 @@ static int stk_vidioc_streamoff(struct file *filp,
+ static int stk_vidioc_g_parm(struct file *filp,
+ void *priv, struct v4l2_streamparm *sp)
+ {
+- if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+-
+- sp->parm.capture.capability = 0;
+- sp->parm.capture.capturemode = 0;
+ /*FIXME This is not correct */
+ sp->parm.capture.timeperframe.numerator = 1;
+ sp->parm.capture.timeperframe.denominator = 30;
+ sp->parm.capture.readbuffers = 2;
+- sp->parm.capture.extendedmode = 0;
+ return 0;
+ }
+
+diff --git a/drivers/media/video/tcm825x.c b/drivers/media/video/tcm825x.c
+index 29991d1..b30c492 100644
+--- a/drivers/media/video/tcm825x.c
++++ b/drivers/media/video/tcm825x.c
+@@ -50,7 +50,7 @@ struct tcm825x_sensor {
+ };
+
+ /* list of image formats supported by TCM825X sensor */
+-const static struct v4l2_fmtdesc tcm825x_formats[] = {
++static const struct v4l2_fmtdesc tcm825x_formats[] = {
+ {
+ .description = "YUYV (YUV 4:2:2), packed",
+ .pixelformat = V4L2_PIX_FMT_UYVY,
+@@ -76,15 +76,15 @@ const static struct v4l2_fmtdesc tcm825x_formats[] = {
+ * TCM825X register configuration for all combinations of pixel format and
+ * image size
+ */
+-const static struct tcm825x_reg subqcif = { 0x20, TCM825X_PICSIZ };
+-const static struct tcm825x_reg qcif = { 0x18, TCM825X_PICSIZ };
+-const static struct tcm825x_reg cif = { 0x14, TCM825X_PICSIZ };
+-const static struct tcm825x_reg qqvga = { 0x0c, TCM825X_PICSIZ };
+-const static struct tcm825x_reg qvga = { 0x04, TCM825X_PICSIZ };
+-const static struct tcm825x_reg vga = { 0x00, TCM825X_PICSIZ };
++static const struct tcm825x_reg subqcif = { 0x20, TCM825X_PICSIZ };
++static const struct tcm825x_reg qcif = { 0x18, TCM825X_PICSIZ };
++static const struct tcm825x_reg cif = { 0x14, TCM825X_PICSIZ };
++static const struct tcm825x_reg qqvga = { 0x0c, TCM825X_PICSIZ };
++static const struct tcm825x_reg qvga = { 0x04, TCM825X_PICSIZ };
++static const struct tcm825x_reg vga = { 0x00, TCM825X_PICSIZ };
+
+-const static struct tcm825x_reg yuv422 = { 0x00, TCM825X_PICFMT };
+-const static struct tcm825x_reg rgb565 = { 0x02, TCM825X_PICFMT };
++static const struct tcm825x_reg yuv422 = { 0x00, TCM825X_PICFMT };
++static const struct tcm825x_reg rgb565 = { 0x02, TCM825X_PICFMT };
+
+ /* Our own specific controls */
+ #define V4L2_CID_ALC V4L2_CID_PRIVATE_BASE
+@@ -248,10 +248,10 @@ static struct vcontrol {
+ };
+
+
+-const static struct tcm825x_reg *tcm825x_siz_reg[NUM_IMAGE_SIZES] =
++static const struct tcm825x_reg *tcm825x_siz_reg[NUM_IMAGE_SIZES] =
+ { &subqcif, &qqvga, &qcif, &qvga, &cif, &vga };
+
+-const static struct tcm825x_reg *tcm825x_fmt_reg[NUM_PIXEL_FORMATS] =
++static const struct tcm825x_reg *tcm825x_fmt_reg[NUM_PIXEL_FORMATS] =
+ { &yuv422, &rgb565 };
+
+ /*
+diff --git a/drivers/media/video/tcm825x.h b/drivers/media/video/tcm825x.h
+index 770ebac..5b7e696 100644
+--- a/drivers/media/video/tcm825x.h
++++ b/drivers/media/video/tcm825x.h
+@@ -188,7 +188,7 @@ struct tcm825x_platform_data {
+ /* Array of image sizes supported by TCM825X. These must be ordered from
+ * smallest image size to largest.
+ */
+-const static struct capture_size tcm825x_sizes[] = {
++static const struct capture_size tcm825x_sizes[] = {
+ { 128, 96 }, /* subQCIF */
+ { 160, 120 }, /* QQVGA */
+ { 176, 144 }, /* QCIF */
+diff --git a/drivers/media/video/tda7432.c b/drivers/media/video/tda7432.c
+index 0c02058..005f8a4 100644
+--- a/drivers/media/video/tda7432.c
++++ b/drivers/media/video/tda7432.c
+@@ -50,7 +50,7 @@
+ #include <media/v4l2-device.h>
+ #include <media/v4l2-ioctl.h>
+ #include <media/i2c-addr.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <media/v4l2-i2c-drv.h>
+
+ #ifndef VIDEO_AUDIO_BALANCE
+ # define VIDEO_AUDIO_BALANCE 32
+@@ -69,13 +69,6 @@ MODULE_PARM_DESC(maxvol,"Set maximium volume to +20db (0), default is 0db(1)");
+ module_param(maxvol, int, S_IRUGO | S_IWUSR);
+
+
+-/* Address to scan (I2C address of this chip) */
+-static unsigned short normal_i2c[] = {
+- I2C_ADDR_TDA7432 >> 1,
+- I2C_CLIENT_END,
+-};
+-
+-I2C_CLIENT_INSMOD;
+
+ /* Structure of address and subaddresses for the tda7432 */
+
+@@ -421,21 +414,18 @@ static int tda7432_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+ static int tda7432_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+ {
+ switch (qc->id) {
+- case V4L2_CID_AUDIO_MUTE:
+ case V4L2_CID_AUDIO_VOLUME:
++ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880);
++ case V4L2_CID_AUDIO_MUTE:
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ case V4L2_CID_AUDIO_BALANCE:
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+- return v4l2_ctrl_query_fill_std(qc);
++ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
+ }
+ return -EINVAL;
+ }
+
+-static int tda7432_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+-}
+-
+ /* ----------------------------------------------------------------------- */
+
+ static const struct v4l2_subdev_core_ops tda7432_core_ops = {
+@@ -498,8 +488,6 @@ MODULE_DEVICE_TABLE(i2c, tda7432_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tda7432",
+- .driverid = I2C_DRIVERID_TDA7432,
+- .command = tda7432_command,
+ .probe = tda7432_probe,
+ .remove = tda7432_remove,
+ .id_table = tda7432_id,
+diff --git a/drivers/media/video/tda9840.c b/drivers/media/video/tda9840.c
+index 6afb705..fe11580 100644
+--- a/drivers/media/video/tda9840.c
++++ b/drivers/media/video/tda9840.c
+@@ -30,8 +30,8 @@
+ #include <linux/ioctl.h>
+ #include <linux/i2c.h>
+ #include <media/v4l2-device.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
+-#include "tda9840.h"
++#include <media/v4l2-chip-ident.h>
++#include <media/v4l2-i2c-drv.h>
+
+ MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+ MODULE_DESCRIPTION("tda9840 driver");
+@@ -56,11 +56,6 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
+ #define TDA9840_SET_BOTH_R 0x16
+ #define TDA9840_SET_EXTERNAL 0x7a
+
+-/* addresses to scan, found only at 0x42 (7-Bit) */
+-static unsigned short normal_i2c[] = { I2C_ADDR_TDA9840, I2C_CLIENT_END };
+-
+-/* magic definition of all other variables and things */
+-I2C_CLIENT_INSMOD;
+
+ static void tda9840_write(struct v4l2_subdev *sd, u8 reg, u8 val)
+ {
+@@ -137,60 +132,17 @@ static int tda9840_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *t)
+ return 0;
+ }
+
+-static long tda9840_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg)
++static int tda9840_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+ {
+- int byte;
+-
+- switch (cmd) {
+- case TDA9840_LEVEL_ADJUST:
+- byte = *(int *)arg;
+- v4l2_dbg(1, debug, sd, "TDA9840_LEVEL_ADJUST: %d\n", byte);
+-
+- /* check for correct range */
+- if (byte > 25 || byte < -20)
+- return -EINVAL;
+-
+- /* calculate actual value to set, see specs, page 18 */
+- byte /= 5;
+- if (0 < byte)
+- byte += 0x8;
+- else
+- byte = -byte;
+- tda9840_write(sd, LEVEL_ADJUST, byte);
+- break;
+-
+- case TDA9840_STEREO_ADJUST:
+- byte = *(int *)arg;
+- v4l2_dbg(1, debug, sd, "TDA9840_STEREO_ADJUST: %d\n", byte);
+-
+- /* check for correct range */
+- if (byte > 25 || byte < -24)
+- return -EINVAL;
+-
+- /* calculate actual value to set */
+- byte /= 5;
+- if (0 < byte)
+- byte += 0x20;
+- else
+- byte = -byte;
+-
+- tda9840_write(sd, STEREO_ADJUST, byte);
+- break;
+- default:
+- return -ENOIOCTLCMD;
+- }
+- return 0;
+-}
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+-static int tda9840_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TDA9840, 0);
+ }
+
+ /* ----------------------------------------------------------------------- */
+
+ static const struct v4l2_subdev_core_ops tda9840_core_ops = {
+- .ioctl = tda9840_ioctl,
++ .g_chip_ident = tda9840_g_chip_ident,
+ };
+
+ static const struct v4l2_subdev_tuner_ops tda9840_tuner_ops = {
+@@ -209,8 +161,6 @@ static int tda9840_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+ {
+ struct v4l2_subdev *sd;
+- int result;
+- int byte;
+
+ /* let's see whether this adapter can support what we need */
+ if (!i2c_check_functionality(client->adapter,
+@@ -227,15 +177,9 @@ static int tda9840_probe(struct i2c_client *client,
+ v4l2_i2c_subdev_init(sd, client, &tda9840_ops);
+
+ /* set initial values for level & stereo - adjustment, mode */
+- byte = 0;
+- result = tda9840_ioctl(sd, TDA9840_LEVEL_ADJUST, &byte);
+- result |= tda9840_ioctl(sd, TDA9840_STEREO_ADJUST, &byte);
++ tda9840_write(sd, LEVEL_ADJUST, 0);
++ tda9840_write(sd, STEREO_ADJUST, 0);
+ tda9840_write(sd, SWITCH, TDA9840_SET_STEREO);
+- if (result) {
+- v4l2_dbg(1, debug, sd, "could not initialize tda9840\n");
+- kfree(sd);
+- return -ENODEV;
+- }
+ return 0;
+ }
+
+@@ -248,12 +192,7 @@ static int tda9840_remove(struct i2c_client *client)
+ return 0;
+ }
+
+-static int tda9840_legacy_probe(struct i2c_adapter *adapter)
+-{
+- /* Let's see whether this is a known adapter we can attach to.
+- Prevents conflicts with tvaudio.c. */
+- return adapter->id == I2C_HW_SAA7146;
+-}
++
+ static const struct i2c_device_id tda9840_id[] = {
+ { "tda9840", 0 },
+ { }
+@@ -262,10 +201,7 @@ MODULE_DEVICE_TABLE(i2c, tda9840_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tda9840",
+- .driverid = I2C_DRIVERID_TDA9840,
+- .command = tda9840_command,
+ .probe = tda9840_probe,
+ .remove = tda9840_remove,
+- .legacy_probe = tda9840_legacy_probe,
+ .id_table = tda9840_id,
+ };
+diff --git a/drivers/media/video/tda9840.h b/drivers/media/video/tda9840.h
+deleted file mode 100644
+index dc12ae7..0000000
+--- a/drivers/media/video/tda9840.h
++++ /dev/null
+@@ -1,14 +0,0 @@
+-#ifndef __INCLUDED_TDA9840__
+-#define __INCLUDED_TDA9840__
+-
+-#define I2C_ADDR_TDA9840 0x42
+-
+-/* values may range between +2.5 and -2.0;
+- the value has to be multiplied with 10 */
+-#define TDA9840_LEVEL_ADJUST _IOW('v',3,int)
+-
+-/* values may range between +2.5 and -2.4;
+- the value has to be multiplied with 10 */
+-#define TDA9840_STEREO_ADJUST _IOW('v',4,int)
+-
+-#endif
+diff --git a/drivers/media/video/tda9875.c b/drivers/media/video/tda9875.c
+index 00c6cbe..24e2b7d 100644
+--- a/drivers/media/video/tda9875.c
++++ b/drivers/media/video/tda9875.c
+@@ -28,20 +28,13 @@
+ #include <linux/i2c.h>
+ #include <linux/videodev2.h>
+ #include <media/v4l2-device.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <media/v4l2-i2c-drv.h>
+ #include <media/i2c-addr.h>
+
+ static int debug; /* insmod parameter */
+ module_param(debug, int, S_IRUGO | S_IWUSR);
+ MODULE_LICENSE("GPL");
+
+-/* Addresses to scan */
+-static unsigned short normal_i2c[] = {
+- I2C_ADDR_TDA9875 >> 1,
+- I2C_CLIENT_END
+-};
+-
+-I2C_CLIENT_INSMOD;
+
+ /* This is a superset of the TDA9875 */
+ struct tda9875 {
+@@ -313,18 +306,14 @@ static int tda9875_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+ {
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_VOLUME:
++ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880);
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+- return v4l2_ctrl_query_fill_std(qc);
++ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
+ }
+ return -EINVAL;
+ }
+
+-static int tda9875_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+-}
+-
+ /* ----------------------------------------------------------------------- */
+
+ static const struct v4l2_subdev_core_ops tda9875_core_ops = {
+@@ -401,8 +390,6 @@ MODULE_DEVICE_TABLE(i2c, tda9875_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tda9875",
+- .driverid = I2C_DRIVERID_TDA9875,
+- .command = tda9875_command,
+ .probe = tda9875_probe,
+ .remove = tda9875_remove,
+ .id_table = tda9875_id,
+diff --git a/drivers/media/video/tea6415c.c b/drivers/media/video/tea6415c.c
+index 7519fd1..d61c56f 100644
+--- a/drivers/media/video/tea6415c.c
++++ b/drivers/media/video/tea6415c.c
+@@ -32,7 +32,8 @@
+ #include <linux/ioctl.h>
+ #include <linux/i2c.h>
+ #include <media/v4l2-device.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/v4l2-i2c-drv.h>
+ #include "tea6415c.h"
+
+ MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+@@ -44,25 +45,22 @@ module_param(debug, int, 0644);
+
+ MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+-/* addresses to scan, found only at 0x03 and/or 0x43 (7-bit) */
+-static unsigned short normal_i2c[] = { I2C_TEA6415C_1, I2C_TEA6415C_2, I2C_CLIENT_END };
+
+-/* magic definition of all other variables and things */
+-I2C_CLIENT_INSMOD;
+-
+-/* makes a connection between the input-pin 'i' and the output-pin 'o'
+- for the tea6415c-client 'client' */
+-static int switch_matrix(struct i2c_client *client, int i, int o)
++/* makes a connection between the input-pin 'i' and the output-pin 'o' */
++static int tea6415c_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ u8 byte = 0;
++ u32 i = route->input;
++ u32 o = route->output;
+ int ret;
+
+- v4l_dbg(1, debug, client, "i=%d, o=%d\n", i, o);
++ v4l2_dbg(1, debug, sd, "i=%d, o=%d\n", i, o);
+
+ /* check if the pins are valid */
+ if (0 == ((1 == i || 3 == i || 5 == i || 6 == i || 8 == i || 10 == i || 20 == i || 11 == i)
+ && (18 == o || 17 == o || 16 == o || 15 == o || 14 == o || 13 == o)))
+- return -1;
++ return -EINVAL;
+
+ /* to understand this, have a look at the tea6415c-specs (p.5) */
+ switch (o) {
+@@ -115,37 +113,33 @@ static int switch_matrix(struct i2c_client *client, int i, int o)
+
+ ret = i2c_smbus_write_byte(client, byte);
+ if (ret) {
+- v4l_dbg(1, debug, client,
++ v4l2_dbg(1, debug, sd,
+ "i2c_smbus_write_byte() failed, ret:%d\n", ret);
+ return -EIO;
+ }
+ return ret;
+ }
+
+-static long tea6415c_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg)
++static int tea6415c_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+ {
+- if (cmd == TEA6415C_SWITCH) {
+- struct i2c_client *client = v4l2_get_subdevdata(sd);
+- struct tea6415c_multiplex *v = (struct tea6415c_multiplex *)arg;
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+- return switch_matrix(client, v->in, v->out);
+- }
+- return -ENOIOCTLCMD;
+-}
+-
+-static int tea6415c_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6415C, 0);
+ }
+
+ /* ----------------------------------------------------------------------- */
+
+ static const struct v4l2_subdev_core_ops tea6415c_core_ops = {
+- .ioctl = tea6415c_ioctl,
++ .g_chip_ident = tea6415c_g_chip_ident,
++};
++
++static const struct v4l2_subdev_video_ops tea6415c_video_ops = {
++ .s_routing = tea6415c_s_routing,
+ };
+
+ static const struct v4l2_subdev_ops tea6415c_ops = {
+ .core = &tea6415c_core_ops,
++ .video = &tea6415c_video_ops,
+ };
+
+ /* this function is called by i2c_probe */
+@@ -176,12 +170,6 @@ static int tea6415c_remove(struct i2c_client *client)
+ return 0;
+ }
+
+-static int tea6415c_legacy_probe(struct i2c_adapter *adapter)
+-{
+- /* Let's see whether this is a known adapter we can attach to.
+- Prevents conflicts with tvaudio.c. */
+- return adapter->id == I2C_HW_SAA7146;
+-}
+
+ static const struct i2c_device_id tea6415c_id[] = {
+ { "tea6415c", 0 },
+@@ -191,10 +179,7 @@ MODULE_DEVICE_TABLE(i2c, tea6415c_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tea6415c",
+- .driverid = I2C_DRIVERID_TEA6415C,
+- .command = tea6415c_command,
+ .probe = tea6415c_probe,
+ .remove = tea6415c_remove,
+- .legacy_probe = tea6415c_legacy_probe,
+ .id_table = tea6415c_id,
+ };
+diff --git a/drivers/media/video/tea6415c.h b/drivers/media/video/tea6415c.h
+index f84ed80..3a47d69 100644
+--- a/drivers/media/video/tea6415c.h
++++ b/drivers/media/video/tea6415c.h
+@@ -1,10 +1,6 @@
+ #ifndef __INCLUDED_TEA6415C__
+ #define __INCLUDED_TEA6415C__
+
+-/* possible i2c-addresses */
+-#define I2C_TEA6415C_1 0x03
+-#define I2C_TEA6415C_2 0x43
+-
+ /* the tea6415c's design is quite brain-dead. although there are
+ 8 inputs and 6 outputs, these aren't enumerated in any way. because
+ I don't want to say "connect input pin 20 to output pin 17", I define
+@@ -28,12 +24,4 @@
+ #define TEA6415C_INPUT7 1
+ #define TEA6415C_INPUT8 11
+
+-struct tea6415c_multiplex
+-{
+- int in; /* input-pin */
+- int out; /* output-pin */
+-};
+-
+-#define TEA6415C_SWITCH _IOW('v',1,struct tea6415c_multiplex)
+-
+ #endif
+diff --git a/drivers/media/video/tea6420.c b/drivers/media/video/tea6420.c
+index 081e74f..3492223 100644
+--- a/drivers/media/video/tea6420.c
++++ b/drivers/media/video/tea6420.c
+@@ -32,7 +32,8 @@
+ #include <linux/ioctl.h>
+ #include <linux/i2c.h>
+ #include <media/v4l2-device.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/v4l2-i2c-drv.h>
+ #include "tea6420.h"
+
+ MODULE_AUTHOR("Michael Hunold <michael@mihu.de>");
+@@ -44,24 +45,23 @@ module_param(debug, int, 0644);
+
+ MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
+-/* addresses to scan, found only at 0x4c and/or 0x4d (7-Bit) */
+-static unsigned short normal_i2c[] = { I2C_ADDR_TEA6420_1, I2C_ADDR_TEA6420_2, I2C_CLIENT_END };
+-
+-/* magic definition of all other variables and things */
+-I2C_CLIENT_INSMOD;
+
+ /* make a connection between the input 'i' and the output 'o'
+- with gain 'g' for the tea6420-client 'client' (note: i = 6 means 'mute') */
+-static int tea6420_switch(struct i2c_client *client, int i, int o, int g)
++ with gain 'g' (note: i = 6 means 'mute') */
++static int tea6420_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++ int i = route->input;
++ int o = route->output & 0xf;
++ int g = (route->output >> 4) & 0xf;
+ u8 byte;
+ int ret;
+
+- v4l_dbg(1, debug, client, "i=%d, o=%d, g=%d\n", i, o, g);
++ v4l2_dbg(1, debug, sd, "i=%d, o=%d, g=%d\n", i, o, g);
+
+ /* check if the parameters are valid */
+ if (i < 1 || i > 6 || o < 1 || o > 4 || g < 0 || g > 6 || g % 2 != 0)
+- return -1;
++ return -EINVAL;
+
+ byte = ((o - 1) << 5);
+ byte |= (i - 1);
+@@ -83,37 +83,33 @@ static int tea6420_switch(struct i2c_client *client, int i, int o, int g)
+
+ ret = i2c_smbus_write_byte(client, byte);
+ if (ret) {
+- v4l_dbg(1, debug, client,
++ v4l2_dbg(1, debug, sd,
+ "i2c_smbus_write_byte() failed, ret:%d\n", ret);
+ return -EIO;
+ }
+ return 0;
+ }
+
+-static long tea6420_ioctl(struct v4l2_subdev *sd, unsigned cmd, void *arg)
++static int tea6420_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+ {
+- if (cmd == TEA6420_SWITCH) {
+- struct i2c_client *client = v4l2_get_subdevdata(sd);
+- struct tea6420_multiplex *a = (struct tea6420_multiplex *)arg;
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+- return tea6420_switch(client, a->in, a->out, a->gain);
+- }
+- return -ENOIOCTLCMD;
+-}
+-
+-static int tea6420_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
++ return v4l2_chip_ident_i2c_client(client, chip, V4L2_IDENT_TEA6420, 0);
+ }
+
+ /* ----------------------------------------------------------------------- */
+
+ static const struct v4l2_subdev_core_ops tea6420_core_ops = {
+- .ioctl = tea6420_ioctl,
++ .g_chip_ident = tea6420_g_chip_ident,
++};
++
++static const struct v4l2_subdev_audio_ops tea6420_audio_ops = {
++ .s_routing = tea6420_s_routing,
+ };
+
+ static const struct v4l2_subdev_ops tea6420_ops = {
+ .core = &tea6420_core_ops,
++ .audio = &tea6420_audio_ops,
+ };
+
+ /* this function is called by i2c_probe */
+@@ -130,20 +126,24 @@ static int tea6420_probe(struct i2c_client *client,
+ v4l_info(client, "chip found @ 0x%x (%s)\n",
+ client->addr << 1, client->adapter->name);
+
++ sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
++ if (sd == NULL)
++ return -ENOMEM;
++ v4l2_i2c_subdev_init(sd, client, &tea6420_ops);
++
+ /* set initial values: set "mute"-input to all outputs at gain 0 */
+ err = 0;
+ for (i = 1; i < 5; i++) {
+- err += tea6420_switch(client, 6, i, 0);
++ struct v4l2_routing route;
++
++ route.input = 6;
++ route.output = i;
++ err += tea6420_s_routing(sd, &route);
+ }
+ if (err) {
+ v4l_dbg(1, debug, client, "could not initialize tea6420\n");
+ return -ENODEV;
+ }
+-
+- sd = kmalloc(sizeof(struct v4l2_subdev), GFP_KERNEL);
+- if (sd == NULL)
+- return -ENOMEM;
+- v4l2_i2c_subdev_init(sd, client, &tea6420_ops);
+ return 0;
+ }
+
+@@ -156,12 +156,6 @@ static int tea6420_remove(struct i2c_client *client)
+ return 0;
+ }
+
+-static int tea6420_legacy_probe(struct i2c_adapter *adapter)
+-{
+- /* Let's see whether this is a known adapter we can attach to.
+- Prevents conflicts with tvaudio.c. */
+- return adapter->id == I2C_HW_SAA7146;
+-}
+
+ static const struct i2c_device_id tea6420_id[] = {
+ { "tea6420", 0 },
+@@ -171,10 +165,7 @@ MODULE_DEVICE_TABLE(i2c, tea6420_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tea6420",
+- .driverid = I2C_DRIVERID_TEA6420,
+- .command = tea6420_command,
+ .probe = tea6420_probe,
+ .remove = tea6420_remove,
+- .legacy_probe = tea6420_legacy_probe,
+ .id_table = tea6420_id,
+ };
+diff --git a/drivers/media/video/tea6420.h b/drivers/media/video/tea6420.h
+index 5ef7c18..4aa3edb 100644
+--- a/drivers/media/video/tea6420.h
++++ b/drivers/media/video/tea6420.h
+@@ -1,17 +1,24 @@
+ #ifndef __INCLUDED_TEA6420__
+ #define __INCLUDED_TEA6420__
+
+-/* possible addresses */
+-#define I2C_ADDR_TEA6420_1 0x4c
+-#define I2C_ADDR_TEA6420_2 0x4d
++/* input pins */
++#define TEA6420_OUTPUT1 1
++#define TEA6420_OUTPUT2 2
++#define TEA6420_OUTPUT3 3
++#define TEA6420_OUTPUT4 4
+
+-struct tea6420_multiplex
+-{
+- int in; /* input of audio switch */
+- int out; /* output of audio switch */
+- int gain; /* gain of connection */
+-};
++/* output pins */
++#define TEA6420_INPUT1 1
++#define TEA6420_INPUT2 2
++#define TEA6420_INPUT3 3
++#define TEA6420_INPUT4 4
++#define TEA6420_INPUT5 5
++#define TEA6420_INPUT6 6
+
+-#define TEA6420_SWITCH _IOW('v',1,struct tea6420_multiplex)
++/* gain on the output pins, ORed with the output pin */
++#define TEA6420_GAIN0 0x00
++#define TEA6420_GAIN2 0x20
++#define TEA6420_GAIN4 0x40
++#define TEA6420_GAIN6 0x60
+
+ #endif
+diff --git a/drivers/media/video/tlv320aic23b.c b/drivers/media/video/tlv320aic23b.c
+index 5c95ecd..07789c6 100644
+--- a/drivers/media/video/tlv320aic23b.c
++++ b/drivers/media/video/tlv320aic23b.c
+@@ -31,15 +31,12 @@
+ #include <linux/i2c-id.h>
+ #include <linux/videodev2.h>
+ #include <media/v4l2-device.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <media/v4l2-i2c-drv.h>
+
+ MODULE_DESCRIPTION("tlv320aic23b driver");
+ MODULE_AUTHOR("Scott Alfter, Ulf Eklund, Hans Verkuil");
+ MODULE_LICENSE("GPL");
+
+-static unsigned short normal_i2c[] = { 0x34 >> 1, I2C_CLIENT_END };
+-
+-I2C_CLIENT_INSMOD;
+
+ /* ----------------------------------------------------------------------- */
+
+@@ -121,11 +118,6 @@ static int tlv320aic23b_log_status(struct v4l2_subdev *sd)
+ return 0;
+ }
+
+-static int tlv320aic23b_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+-}
+-
+ /* ----------------------------------------------------------------------- */
+
+ static const struct v4l2_subdev_core_ops tlv320aic23b_core_ops = {
+@@ -208,8 +200,6 @@ MODULE_DEVICE_TABLE(i2c, tlv320aic23b_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tlv320aic23b",
+- .driverid = I2C_DRIVERID_TLV320AIC23B,
+- .command = tlv320aic23b_command,
+ .probe = tlv320aic23b_probe,
+ .remove = tlv320aic23b_remove,
+ .id_table = tlv320aic23b_id,
+diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
+index 30640fb..72d4103 100644
+--- a/drivers/media/video/tuner-core.c
++++ b/drivers/media/video/tuner-core.c
+@@ -364,7 +364,8 @@ static void set_type(struct i2c_client *c, unsigned int type,
+ }
+
+ t->type = type;
+- t->config = new_config;
++ /* prevent invalid config values */
++ t->config = ((new_config >= 0) && (new_config < 256)) ? new_config : 0;
+ if (tuner_callback != NULL) {
+ tuner_dbg("defining GPIO callback\n");
+ t->fe.callback = tuner_callback;
+@@ -452,7 +453,8 @@ static void set_type(struct i2c_client *c, unsigned int type,
+ struct dvb_tuner_ops *xc_tuner_ops;
+
+ xc5000_cfg.i2c_address = t->i2c->addr;
+- xc5000_cfg.if_khz = 5380;
++ /* if_khz will be set when the digital dvb_attach() occurs */
++ xc5000_cfg.if_khz = 0;
+ if (!dvb_attach(xc5000_attach,
+ &t->fe, t->i2c->adapter, &xc5000_cfg))
+ goto attach_failed;
+@@ -776,8 +778,7 @@ static int tuner_s_radio(struct v4l2_subdev *sd)
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+- if (set_mode(client, t, V4L2_TUNER_RADIO, "AUDC_SET_RADIO")
+- == -EINVAL)
++ if (set_mode(client, t, V4L2_TUNER_RADIO, "s_radio") == -EINVAL)
+ return 0;
+ if (t->radio_freq)
+ set_freq(client, t->radio_freq);
+@@ -791,7 +792,7 @@ static int tuner_s_standby(struct v4l2_subdev *sd, u32 standby)
+
+ tuner_dbg("Putting tuner to sleep\n");
+
+- if (check_mode(t, "TUNER_SET_STANDBY") == -EINVAL)
++ if (check_mode(t, "s_standby") == -EINVAL)
+ return 0;
+ t->mode = T_STANDBY;
+ if (analog_ops->standby)
+@@ -799,132 +800,6 @@ static int tuner_s_standby(struct v4l2_subdev *sd, u32 standby)
+ return 0;
+ }
+
+-#ifdef CONFIG_VIDEO_ALLOW_V4L1
+-static long tuner_ioctl(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
+-{
+- struct tuner *t = to_tuner(sd);
+- struct i2c_client *client = v4l2_get_subdevdata(sd);
+- struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+- struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+-
+- switch (cmd) {
+- case VIDIOCSAUDIO:
+- if (check_mode(t, "VIDIOCSAUDIO") == -EINVAL)
+- return 0;
+- if (check_v4l2(t) == -EINVAL)
+- return 0;
+-
+- /* Should be implemented, since bttv calls it */
+- tuner_dbg("VIDIOCSAUDIO not implemented.\n");
+- break;
+- case VIDIOCSCHAN:
+- {
+- static const v4l2_std_id map[] = {
+- [VIDEO_MODE_PAL] = V4L2_STD_PAL,
+- [VIDEO_MODE_NTSC] = V4L2_STD_NTSC_M,
+- [VIDEO_MODE_SECAM] = V4L2_STD_SECAM,
+- [4 /* bttv */ ] = V4L2_STD_PAL_M,
+- [5 /* bttv */ ] = V4L2_STD_PAL_N,
+- [6 /* bttv */ ] = V4L2_STD_NTSC_M_JP,
+- };
+- struct video_channel *vc = arg;
+-
+- if (check_v4l2(t) == -EINVAL)
+- return 0;
+-
+- if (set_mode(client,t,V4L2_TUNER_ANALOG_TV, "VIDIOCSCHAN")==-EINVAL)
+- return 0;
+-
+- if (vc->norm < ARRAY_SIZE(map))
+- t->std = map[vc->norm];
+- tuner_fixup_std(t);
+- if (t->tv_freq)
+- set_tv_freq(client, t->tv_freq);
+- return 0;
+- }
+- case VIDIOCSFREQ:
+- {
+- unsigned long *v = arg;
+-
+- if (check_mode(t, "VIDIOCSFREQ") == -EINVAL)
+- return 0;
+- if (check_v4l2(t) == -EINVAL)
+- return 0;
+-
+- set_freq(client, *v);
+- return 0;
+- }
+- case VIDIOCGTUNER:
+- {
+- struct video_tuner *vt = arg;
+-
+- if (check_mode(t, "VIDIOCGTUNER") == -EINVAL)
+- return 0;
+- if (check_v4l2(t) == -EINVAL)
+- return 0;
+-
+- if (V4L2_TUNER_RADIO == t->mode) {
+- if (fe_tuner_ops->get_status) {
+- u32 tuner_status;
+-
+- fe_tuner_ops->get_status(&t->fe, &tuner_status);
+- if (tuner_status & TUNER_STATUS_STEREO)
+- vt->flags |= VIDEO_TUNER_STEREO_ON;
+- else
+- vt->flags &= ~VIDEO_TUNER_STEREO_ON;
+- } else {
+- if (analog_ops->is_stereo) {
+- if (analog_ops->is_stereo(&t->fe))
+- vt->flags |=
+- VIDEO_TUNER_STEREO_ON;
+- else
+- vt->flags &=
+- ~VIDEO_TUNER_STEREO_ON;
+- }
+- }
+- if (analog_ops->has_signal)
+- vt->signal =
+- analog_ops->has_signal(&t->fe);
+-
+- vt->flags |= VIDEO_TUNER_LOW; /* Allow freqs at 62.5 Hz */
+-
+- vt->rangelow = radio_range[0] * 16000;
+- vt->rangehigh = radio_range[1] * 16000;
+-
+- } else {
+- vt->rangelow = tv_range[0] * 16;
+- vt->rangehigh = tv_range[1] * 16;
+- }
+-
+- return 0;
+- }
+- case VIDIOCGAUDIO:
+- {
+- struct video_audio *va = arg;
+-
+- if (check_mode(t, "VIDIOCGAUDIO") == -EINVAL)
+- return 0;
+- if (check_v4l2(t) == -EINVAL)
+- return 0;
+-
+- if (V4L2_TUNER_RADIO == t->mode) {
+- if (fe_tuner_ops->get_status) {
+- u32 tuner_status;
+-
+- fe_tuner_ops->get_status(&t->fe, &tuner_status);
+- va->mode = (tuner_status & TUNER_STATUS_STEREO)
+- ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
+- } else if (analog_ops->is_stereo)
+- va->mode = analog_ops->is_stereo(&t->fe)
+- ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
+- }
+- return 0;
+- }
+- }
+- return -ENOIOCTLCMD;
+-}
+-#endif
+-
+ static int tuner_s_config(struct v4l2_subdev *sd, const struct v4l2_priv_tun_config *cfg)
+ {
+ struct tuner *t = to_tuner(sd);
+@@ -950,8 +825,7 @@ static int tuner_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+- if (set_mode(client, t, V4L2_TUNER_ANALOG_TV, "VIDIOC_S_STD")
+- == -EINVAL)
++ if (set_mode(client, t, V4L2_TUNER_ANALOG_TV, "s_std") == -EINVAL)
+ return 0;
+
+ switch_v4l2();
+@@ -968,8 +842,7 @@ static int tuner_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+- if (set_mode(client, t, f->type, "VIDIOC_S_FREQUENCY")
+- == -EINVAL)
++ if (set_mode(client, t, f->type, "s_frequency") == -EINVAL)
+ return 0;
+ switch_v4l2();
+ set_freq(client, f->frequency);
+@@ -982,7 +855,7 @@ static int tuner_g_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *f)
+ struct tuner *t = to_tuner(sd);
+ struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+
+- if (check_mode(t, "VIDIOC_G_FREQUENCY") == -EINVAL)
++ if (check_mode(t, "g_frequency") == -EINVAL)
+ return 0;
+ switch_v4l2();
+ f->type = t->mode;
+@@ -1006,7 +879,7 @@ static int tuner_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+ struct analog_demod_ops *analog_ops = &t->fe.ops.analog_ops;
+ struct dvb_tuner_ops *fe_tuner_ops = &t->fe.ops.tuner_ops;
+
+- if (check_mode(t, "VIDIOC_G_TUNER") == -EINVAL)
++ if (check_mode(t, "g_tuner") == -EINVAL)
+ return 0;
+ switch_v4l2();
+
+@@ -1055,7 +928,7 @@ static int tuner_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+ struct tuner *t = to_tuner(sd);
+ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+- if (check_mode(t, "VIDIOC_S_TUNER") == -EINVAL)
++ if (check_mode(t, "s_tuner") == -EINVAL)
+ return 0;
+
+ switch_v4l2();
+@@ -1112,9 +985,6 @@ static int tuner_resume(struct i2c_client *c)
+ static const struct v4l2_subdev_core_ops tuner_core_ops = {
+ .log_status = tuner_log_status,
+ .s_standby = tuner_s_standby,
+-#ifdef CONFIG_VIDEO_ALLOW_V4L1
+- .ioctl = tuner_ioctl,
+-#endif
+ };
+
+ static const struct v4l2_subdev_tuner_ops tuner_tuner_ops = {
+diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c
+index 076ed5b..226bf35 100644
+--- a/drivers/media/video/tvaudio.c
++++ b/drivers/media/video/tvaudio.c
+@@ -26,7 +26,7 @@
+ #include <linux/delay.h>
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+-#include <linux/videodev.h>
++#include <linux/videodev2.h>
+ #include <linux/i2c.h>
+ #include <linux/init.h>
+ #include <linux/kthread.h>
+@@ -1047,6 +1047,116 @@ static int tda9874a_initialize(struct CHIPSTATE *chip)
+ return 0;
+ }
+
++/* ---------------------------------------------------------------------- */
++/* audio chip description - defines+functions for tda9875 */
++/* The TDA9875 is made by Philips Semiconductor
++ * http://www.semiconductors.philips.com
++ * TDA9875: I2C-bus controlled DSP audio processor, FM demodulator
++ *
++ */
++
++/* subaddresses for TDA9875 */
++#define TDA9875_MUT 0x12 /*General mute (value --> 0b11001100*/
++#define TDA9875_CFG 0x01 /* Config register (value --> 0b00000000 */
++#define TDA9875_DACOS 0x13 /*DAC i/o select (ADC) 0b0000100*/
++#define TDA9875_LOSR 0x16 /*Line output select regirter 0b0100 0001*/
++
++#define TDA9875_CH1V 0x0c /*Channel 1 volume (mute)*/
++#define TDA9875_CH2V 0x0d /*Channel 2 volume (mute)*/
++#define TDA9875_SC1 0x14 /*SCART 1 in (mono)*/
++#define TDA9875_SC2 0x15 /*SCART 2 in (mono)*/
++
++#define TDA9875_ADCIS 0x17 /*ADC input select (mono) 0b0110 000*/
++#define TDA9875_AER 0x19 /*Audio effect (AVL+Pseudo) 0b0000 0110*/
++#define TDA9875_MCS 0x18 /*Main channel select (DAC) 0b0000100*/
++#define TDA9875_MVL 0x1a /* Main volume gauche */
++#define TDA9875_MVR 0x1b /* Main volume droite */
++#define TDA9875_MBA 0x1d /* Main Basse */
++#define TDA9875_MTR 0x1e /* Main treble */
++#define TDA9875_ACS 0x1f /* Auxilary channel select (FM) 0b0000000*/
++#define TDA9875_AVL 0x20 /* Auxilary volume gauche */
++#define TDA9875_AVR 0x21 /* Auxilary volume droite */
++#define TDA9875_ABA 0x22 /* Auxilary Basse */
++#define TDA9875_ATR 0x23 /* Auxilary treble */
++
++#define TDA9875_MSR 0x02 /* Monitor select register */
++#define TDA9875_C1MSB 0x03 /* Carrier 1 (FM) frequency register MSB */
++#define TDA9875_C1MIB 0x04 /* Carrier 1 (FM) frequency register (16-8]b */
++#define TDA9875_C1LSB 0x05 /* Carrier 1 (FM) frequency register LSB */
++#define TDA9875_C2MSB 0x06 /* Carrier 2 (nicam) frequency register MSB */
++#define TDA9875_C2MIB 0x07 /* Carrier 2 (nicam) frequency register (16-8]b */
++#define TDA9875_C2LSB 0x08 /* Carrier 2 (nicam) frequency register LSB */
++#define TDA9875_DCR 0x09 /* Demodulateur configuration regirter*/
++#define TDA9875_DEEM 0x0a /* FM de-emphasis regirter*/
++#define TDA9875_FMAT 0x0b /* FM Matrix regirter*/
++
++/* values */
++#define TDA9875_MUTE_ON 0xff /* general mute */
++#define TDA9875_MUTE_OFF 0xcc /* general no mute */
++
++static int tda9875_initialize(struct CHIPSTATE *chip)
++{
++ chip_write(chip, TDA9875_CFG, 0xd0); /*reg de config 0 (reset)*/
++ chip_write(chip, TDA9875_MSR, 0x03); /* Monitor 0b00000XXX*/
++ chip_write(chip, TDA9875_C1MSB, 0x00); /*Car1(FM) MSB XMHz*/
++ chip_write(chip, TDA9875_C1MIB, 0x00); /*Car1(FM) MIB XMHz*/
++ chip_write(chip, TDA9875_C1LSB, 0x00); /*Car1(FM) LSB XMHz*/
++ chip_write(chip, TDA9875_C2MSB, 0x00); /*Car2(NICAM) MSB XMHz*/
++ chip_write(chip, TDA9875_C2MIB, 0x00); /*Car2(NICAM) MIB XMHz*/
++ chip_write(chip, TDA9875_C2LSB, 0x00); /*Car2(NICAM) LSB XMHz*/
++ chip_write(chip, TDA9875_DCR, 0x00); /*Demod config 0x00*/
++ chip_write(chip, TDA9875_DEEM, 0x44); /*DE-Emph 0b0100 0100*/
++ chip_write(chip, TDA9875_FMAT, 0x00); /*FM Matrix reg 0x00*/
++ chip_write(chip, TDA9875_SC1, 0x00); /* SCART 1 (SC1)*/
++ chip_write(chip, TDA9875_SC2, 0x01); /* SCART 2 (sc2)*/
++
++ chip_write(chip, TDA9875_CH1V, 0x10); /* Channel volume 1 mute*/
++ chip_write(chip, TDA9875_CH2V, 0x10); /* Channel volume 2 mute */
++ chip_write(chip, TDA9875_DACOS, 0x02); /* sig DAC i/o(in:nicam)*/
++ chip_write(chip, TDA9875_ADCIS, 0x6f); /* sig ADC input(in:mono)*/
++ chip_write(chip, TDA9875_LOSR, 0x00); /* line out (in:mono)*/
++ chip_write(chip, TDA9875_AER, 0x00); /*06 Effect (AVL+PSEUDO) */
++ chip_write(chip, TDA9875_MCS, 0x44); /* Main ch select (DAC) */
++ chip_write(chip, TDA9875_MVL, 0x03); /* Vol Main left 10dB */
++ chip_write(chip, TDA9875_MVR, 0x03); /* Vol Main right 10dB*/
++ chip_write(chip, TDA9875_MBA, 0x00); /* Main Bass Main 0dB*/
++ chip_write(chip, TDA9875_MTR, 0x00); /* Main Treble Main 0dB*/
++ chip_write(chip, TDA9875_ACS, 0x44); /* Aux chan select (dac)*/
++ chip_write(chip, TDA9875_AVL, 0x00); /* Vol Aux left 0dB*/
++ chip_write(chip, TDA9875_AVR, 0x00); /* Vol Aux right 0dB*/
++ chip_write(chip, TDA9875_ABA, 0x00); /* Aux Bass Main 0dB*/
++ chip_write(chip, TDA9875_ATR, 0x00); /* Aux Aigus Main 0dB*/
++
++ chip_write(chip, TDA9875_MUT, 0xcc); /* General mute */
++ return 0;
++}
++
++static int tda9875_volume(int val) { return (unsigned char)(val / 602 - 84); }
++static int tda9875_bass(int val) { return (unsigned char)(max(-12, val / 2115 - 15)); }
++static int tda9875_treble(int val) { return (unsigned char)(val / 2622 - 12); }
++
++/* ----------------------------------------------------------------------- */
++
++
++/* *********************** *
++ * i2c interface functions *
++ * *********************** */
++
++static int tda9875_checkit(struct CHIPSTATE *chip)
++{
++ struct v4l2_subdev *sd = &chip->sd;
++ int dic, rev;
++
++ dic = chip_read2(chip, 254);
++ rev = chip_read2(chip, 255);
++
++ if (dic == 0 || dic == 2) { /* tda9875 and tda9875A */
++ v4l2_info(sd, "found tda9875%s rev. %d.\n",
++ dic == 0 ? "" : "A", rev);
++ return 1;
++ }
++ return 0;
++}
+
+ /* ---------------------------------------------------------------------- */
+ /* audio chip descriptions - defines+functions for tea6420 */
+@@ -1280,6 +1390,7 @@ static int tda9850 = 1;
+ static int tda9855 = 1;
+ static int tda9873 = 1;
+ static int tda9874a = 1;
++static int tda9875 = 1;
+ static int tea6300; /* default 0 - address clash with msp34xx */
+ static int tea6320; /* default 0 - address clash with msp34xx */
+ static int tea6420 = 1;
+@@ -1292,6 +1403,7 @@ module_param(tda9850, int, 0444);
+ module_param(tda9855, int, 0444);
+ module_param(tda9873, int, 0444);
+ module_param(tda9874a, int, 0444);
++module_param(tda9875, int, 0444);
+ module_param(tea6300, int, 0444);
+ module_param(tea6320, int, 0444);
+ module_param(tea6420, int, 0444);
+@@ -1349,6 +1461,26 @@ static struct CHIPDESC chiplist[] = {
+ .setmode = tda9874a_setmode,
+ },
+ {
++ .name = "tda9875",
++ .insmodopt = &tda9875,
++ .addr_lo = I2C_ADDR_TDA9875 >> 1,
++ .addr_hi = I2C_ADDR_TDA9875 >> 1,
++ .flags = CHIP_HAS_VOLUME | CHIP_HAS_BASSTREBLE,
++
++ /* callbacks */
++ .initialize = tda9875_initialize,
++ .checkit = tda9875_checkit,
++ .volfunc = tda9875_volume,
++ .bassfunc = tda9875_bass,
++ .treblefunc = tda9875_treble,
++ .leftreg = TDA9875_MVL,
++ .rightreg = TDA9875_MVR,
++ .bassreg = TDA9875_MBA,
++ .treblereg = TDA9875_MTR,
++ .leftinit = 58880,
++ .rightinit = 58880,
++ },
++ {
+ .name = "tda9850",
+ .insmodopt = &tda9850,
+ .addr_lo = I2C_ADDR_TDA985x_L >> 1,
+@@ -1511,6 +1643,8 @@ static int tvaudio_g_ctrl(struct v4l2_subdev *sd,
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
++ if (!(desc->flags & CHIP_HAS_INPUTSEL))
++ break;
+ ctrl->value=chip->muted;
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+@@ -1552,6 +1686,9 @@ static int tvaudio_s_ctrl(struct v4l2_subdev *sd,
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
++ if (!(desc->flags & CHIP_HAS_INPUTSEL))
++ break;
++
+ if (ctrl->value < 0 || ctrl->value >= 2)
+ return -ERANGE;
+ chip->muted = ctrl->value;
+@@ -1636,21 +1773,26 @@ static int tvaudio_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+
+ switch (qc->id) {
+ case V4L2_CID_AUDIO_MUTE:
++ if (desc->flags & CHIP_HAS_INPUTSEL)
++ return v4l2_ctrl_query_fill(qc, 0, 1, 1, 0);
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
++ if (desc->flags & CHIP_HAS_VOLUME)
++ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 58880);
++ break;
+ case V4L2_CID_AUDIO_BALANCE:
+- if (!(desc->flags & CHIP_HAS_VOLUME))
+- return -EINVAL;
++ if (desc->flags & CHIP_HAS_VOLUME)
++ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
+ break;
+ case V4L2_CID_AUDIO_BASS:
+ case V4L2_CID_AUDIO_TREBLE:
+- if (!(desc->flags & CHIP_HAS_BASSTREBLE))
+- return -EINVAL;
++ if (desc->flags & CHIP_HAS_BASSTREBLE)
++ return v4l2_ctrl_query_fill(qc, 0, 65535, 65535 / 100, 32768);
+ break;
+ default:
+- return -EINVAL;
++ break;
+ }
+- return v4l2_ctrl_query_fill_std(qc);
++ return -EINVAL;
+ }
+
+ static int tvaudio_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *rt)
+@@ -1658,7 +1800,9 @@ static int tvaudio_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *
+ struct CHIPSTATE *chip = to_state(sd);
+ struct CHIPDESC *desc = chip->desc;
+
+- if (!(desc->flags & CHIP_HAS_INPUTSEL) || rt->input >= 4)
++ if (!(desc->flags & CHIP_HAS_INPUTSEL))
++ return 0;
++ if (rt->input >= 4)
+ return -EINVAL;
+ /* There are four inputs: tuner, radio, extern and intern. */
+ chip->input = rt->input;
+@@ -1675,8 +1819,11 @@ static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+ struct CHIPDESC *desc = chip->desc;
+ int mode = 0;
+
++ if (!desc->setmode)
++ return 0;
+ if (chip->radio)
+ return 0;
++
+ switch (vt->audmode) {
+ case V4L2_TUNER_MODE_MONO:
+ case V4L2_TUNER_MODE_STEREO:
+@@ -1692,7 +1839,7 @@ static int tvaudio_s_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+ }
+ chip->audmode = vt->audmode;
+
+- if (desc->setmode && mode) {
++ if (mode) {
+ chip->watch_stereo = 0;
+ /* del_timer(&chip->wt); */
+ chip->mode = mode;
+@@ -1707,15 +1854,17 @@ static int tvaudio_g_tuner(struct v4l2_subdev *sd, struct v4l2_tuner *vt)
+ struct CHIPDESC *desc = chip->desc;
+ int mode = V4L2_TUNER_MODE_MONO;
+
++ if (!desc->getmode)
++ return 0;
+ if (chip->radio)
+ return 0;
++
+ vt->audmode = chip->audmode;
+ vt->rxsubchans = 0;
+ vt->capability = V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 | V4L2_TUNER_CAP_LANG2;
+
+- if (desc->getmode)
+- mode = desc->getmode(chip);
++ mode = desc->getmode(chip);
+
+ if (mode & V4L2_TUNER_MODE_MONO)
+ vt->rxsubchans |= V4L2_TUNER_SUB_MONO;
+@@ -1901,6 +2050,7 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *
+ }
+
+ chip->thread = NULL;
++ init_timer(&chip->wt);
+ if (desc->flags & CHIP_NEED_CHECKMODE) {
+ if (!desc->getmode || !desc->setmode) {
+ /* This shouldn't be happen. Warn user, but keep working
+@@ -1910,7 +2060,6 @@ static int tvaudio_probe(struct i2c_client *client, const struct i2c_device_id *
+ return 0;
+ }
+ /* start async thread */
+- init_timer(&chip->wt);
+ chip->wt.function = chip_thread_wake;
+ chip->wt.data = (unsigned long)chip;
+ chip->thread = kthread_run(chip_thread, chip, client->name);
+diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c
+index 78277ab..e24a38c 100644
+--- a/drivers/media/video/tveeprom.c
++++ b/drivers/media/video/tveeprom.c
+@@ -261,7 +261,12 @@ hauppauge_tuner[] =
+ { TUNER_ABSENT, "MaxLinear MXL5005_v2"},
+ { TUNER_PHILIPS_TDA8290, "Philips 18271_8295"},
+ /* 150-159 */
+- { TUNER_ABSENT, "Xceive XC5000"},
++ { TUNER_XC5000, "Xceive XC5000"},
++ { TUNER_ABSENT, "Xceive XC3028L"},
++ { TUNER_ABSENT, "NXP 18271C2_716x"},
++ { TUNER_ABSENT, "Xceive XC4000"},
++ { TUNER_ABSENT, "Dibcom 7070"},
++ { TUNER_PHILIPS_TDA8290, "NXP 18271C2"},
+ };
+
+ /* Use V4L2_IDENT_AMBIGUOUS for those audio 'chips' that are
+diff --git a/drivers/media/video/tvp514x.c b/drivers/media/video/tvp514x.c
+index 8e23aa5..4262e60 100644
+--- a/drivers/media/video/tvp514x.c
++++ b/drivers/media/video/tvp514x.c
+@@ -86,9 +86,12 @@ struct tvp514x_std_info {
+ struct v4l2_standard standard;
+ };
+
++static struct tvp514x_reg tvp514x_reg_list_default[0x40];
+ /**
+- * struct tvp514x_decoded - TVP5146/47 decoder object
++ * struct tvp514x_decoder - TVP5146/47 decoder object
+ * @v4l2_int_device: Slave handle
++ * @tvp514x_slave: Slave pointer which is used by @v4l2_int_device
++ * @tvp514x_regs: copy of hw's regs with preset values.
+ * @pdata: Board specific
+ * @client: I2C client data
+ * @id: Entry from I2C table
+@@ -103,7 +106,9 @@ struct tvp514x_std_info {
+ * @route: input and output routing at chip level
+ */
+ struct tvp514x_decoder {
+- struct v4l2_int_device *v4l2_int_device;
++ struct v4l2_int_device v4l2_int_device;
++ struct v4l2_int_slave tvp514x_slave;
++ struct tvp514x_reg tvp514x_regs[ARRAY_SIZE(tvp514x_reg_list_default)];
+ const struct tvp514x_platform_data *pdata;
+ struct i2c_client *client;
+
+@@ -124,7 +129,7 @@ struct tvp514x_decoder {
+ };
+
+ /* TVP514x default register values */
+-static struct tvp514x_reg tvp514x_reg_list[] = {
++static struct tvp514x_reg tvp514x_reg_list_default[] = {
+ {TOK_WRITE, REG_INPUT_SEL, 0x05}, /* Composite selected */
+ {TOK_WRITE, REG_AFE_GAIN_CTRL, 0x0F},
+ {TOK_WRITE, REG_VIDEO_STD, 0x00}, /* Auto mode */
+@@ -422,7 +427,7 @@ static int tvp514x_configure(struct tvp514x_decoder *decoder)
+
+ /* common register initialization */
+ err =
+- tvp514x_write_regs(decoder->client, tvp514x_reg_list);
++ tvp514x_write_regs(decoder->client, decoder->tvp514x_regs);
+ if (err)
+ return err;
+
+@@ -580,7 +585,8 @@ static int ioctl_s_std(struct v4l2_int_device *s, v4l2_std_id *std_id)
+ return err;
+
+ decoder->current_std = i;
+- tvp514x_reg_list[REG_VIDEO_STD].val = decoder->std_list[i].video_std;
++ decoder->tvp514x_regs[REG_VIDEO_STD].val =
++ decoder->std_list[i].video_std;
+
+ v4l_dbg(1, debug, decoder->client, "Standard set to: %s",
+ decoder->std_list[i].standard.name);
+@@ -625,8 +631,8 @@ static int ioctl_s_routing(struct v4l2_int_device *s,
+ if (err)
+ return err;
+
+- tvp514x_reg_list[REG_INPUT_SEL].val = input_sel;
+- tvp514x_reg_list[REG_OUTPUT_FORMATTER1].val = output_sel;
++ decoder->tvp514x_regs[REG_INPUT_SEL].val = input_sel;
++ decoder->tvp514x_regs[REG_OUTPUT_FORMATTER1].val = output_sel;
+
+ /* Clear status */
+ msleep(LOCK_RETRY_DELAY);
+@@ -686,7 +692,7 @@ static int ioctl_s_routing(struct v4l2_int_device *s,
+ break; /* Input detected */
+ }
+
+- if ((current_std == STD_INVALID) || (try_count < 0))
++ if ((current_std == STD_INVALID) || (try_count <= 0))
+ return -EINVAL;
+
+ decoder->current_std = current_std;
+@@ -719,10 +725,9 @@ ioctl_queryctrl(struct v4l2_int_device *s, struct v4l2_queryctrl *qctrl)
+
+ switch (qctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+- /* Brightness supported is same as standard one (0-255),
+- * so make use of standard API provided.
++ /* Brightness supported is (0-255),
+ */
+- err = v4l2_ctrl_query_fill_std(qctrl);
++ err = v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128);
+ break;
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+@@ -779,16 +784,16 @@ ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl)
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+- ctrl->value = tvp514x_reg_list[REG_BRIGHTNESS].val;
++ ctrl->value = decoder->tvp514x_regs[REG_BRIGHTNESS].val;
+ break;
+ case V4L2_CID_CONTRAST:
+- ctrl->value = tvp514x_reg_list[REG_CONTRAST].val;
++ ctrl->value = decoder->tvp514x_regs[REG_CONTRAST].val;
+ break;
+ case V4L2_CID_SATURATION:
+- ctrl->value = tvp514x_reg_list[REG_SATURATION].val;
++ ctrl->value = decoder->tvp514x_regs[REG_SATURATION].val;
+ break;
+ case V4L2_CID_HUE:
+- ctrl->value = tvp514x_reg_list[REG_HUE].val;
++ ctrl->value = decoder->tvp514x_regs[REG_HUE].val;
+ if (ctrl->value == 0x7F)
+ ctrl->value = 180;
+ else if (ctrl->value == 0x80)
+@@ -798,7 +803,7 @@ ioctl_g_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl)
+
+ break;
+ case V4L2_CID_AUTOGAIN:
+- ctrl->value = tvp514x_reg_list[REG_AFE_GAIN_CTRL].val;
++ ctrl->value = decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val;
+ if ((ctrl->value & 0x3) == 3)
+ ctrl->value = 1;
+ else
+@@ -848,7 +853,7 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl)
+ value);
+ if (err)
+ return err;
+- tvp514x_reg_list[REG_BRIGHTNESS].val = value;
++ decoder->tvp514x_regs[REG_BRIGHTNESS].val = value;
+ break;
+ case V4L2_CID_CONTRAST:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+@@ -861,7 +866,7 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl)
+ value);
+ if (err)
+ return err;
+- tvp514x_reg_list[REG_CONTRAST].val = value;
++ decoder->tvp514x_regs[REG_CONTRAST].val = value;
+ break;
+ case V4L2_CID_SATURATION:
+ if (ctrl->value < 0 || ctrl->value > 255) {
+@@ -874,7 +879,7 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl)
+ value);
+ if (err)
+ return err;
+- tvp514x_reg_list[REG_SATURATION].val = value;
++ decoder->tvp514x_regs[REG_SATURATION].val = value;
+ break;
+ case V4L2_CID_HUE:
+ if (value == 180)
+@@ -893,7 +898,7 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl)
+ value);
+ if (err)
+ return err;
+- tvp514x_reg_list[REG_HUE].val = value;
++ decoder->tvp514x_regs[REG_HUE].val = value;
+ break;
+ case V4L2_CID_AUTOGAIN:
+ if (value == 1)
+@@ -910,7 +915,7 @@ ioctl_s_ctrl(struct v4l2_int_device *s, struct v4l2_control *ctrl)
+ value);
+ if (err)
+ return err;
+- tvp514x_reg_list[REG_AFE_GAIN_CTRL].val = value;
++ decoder->tvp514x_regs[REG_AFE_GAIN_CTRL].val = value;
+ break;
+ default:
+ v4l_err(decoder->client,
+@@ -1275,7 +1280,7 @@ static int ioctl_init(struct v4l2_int_device *s)
+ struct tvp514x_decoder *decoder = s->priv;
+
+ /* Set default standard to auto */
+- tvp514x_reg_list[REG_VIDEO_STD].val =
++ decoder->tvp514x_regs[REG_VIDEO_STD].val =
+ VIDEO_STD_AUTO_SWITCH_BIT;
+
+ return tvp514x_configure(decoder);
+@@ -1344,11 +1349,6 @@ static struct v4l2_int_ioctl_desc tvp514x_ioctl_desc[] = {
+ (v4l2_int_ioctl_func *) ioctl_s_routing},
+ };
+
+-static struct v4l2_int_slave tvp514x_slave = {
+- .ioctls = tvp514x_ioctl_desc,
+- .num_ioctls = ARRAY_SIZE(tvp514x_ioctl_desc),
+-};
+-
+ static struct tvp514x_decoder tvp514x_dev = {
+ .state = STATE_NOT_DETECTED,
+
+@@ -1369,17 +1369,15 @@ static struct tvp514x_decoder tvp514x_dev = {
+ .current_std = STD_NTSC_MJ,
+ .std_list = tvp514x_std_list,
+ .num_stds = ARRAY_SIZE(tvp514x_std_list),
+-
+-};
+-
+-static struct v4l2_int_device tvp514x_int_device = {
+- .module = THIS_MODULE,
+- .name = TVP514X_MODULE_NAME,
+- .priv = &tvp514x_dev,
+- .type = v4l2_int_type_slave,
+- .u = {
+- .slave = &tvp514x_slave,
+- },
++ .v4l2_int_device = {
++ .module = THIS_MODULE,
++ .name = TVP514X_MODULE_NAME,
++ .type = v4l2_int_type_slave,
++ },
++ .tvp514x_slave = {
++ .ioctls = tvp514x_ioctl_desc,
++ .num_ioctls = ARRAY_SIZE(tvp514x_ioctl_desc),
++ },
+ };
+
+ /**
+@@ -1392,26 +1390,37 @@ static struct v4l2_int_device tvp514x_int_device = {
+ static int
+ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id)
+ {
+- struct tvp514x_decoder *decoder = &tvp514x_dev;
++ struct tvp514x_decoder *decoder;
+ int err;
+
+ /* Check if the adapter supports the needed features */
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EIO;
+
+- decoder->pdata = client->dev.platform_data;
+- if (!decoder->pdata) {
++ decoder = kzalloc(sizeof(*decoder), GFP_KERNEL);
++ if (!decoder)
++ return -ENOMEM;
++
++ if (!client->dev.platform_data) {
+ v4l_err(client, "No platform data!!\n");
+- return -ENODEV;
++ err = -ENODEV;
++ goto out_free;
+ }
++
++ *decoder = tvp514x_dev;
++ decoder->v4l2_int_device.priv = decoder;
++ decoder->pdata = client->dev.platform_data;
++ decoder->v4l2_int_device.u.slave = &decoder->tvp514x_slave;
++ memcpy(decoder->tvp514x_regs, tvp514x_reg_list_default,
++ sizeof(tvp514x_reg_list_default));
+ /*
+ * Fetch platform specific data, and configure the
+ * tvp514x_reg_list[] accordingly. Since this is one
+ * time configuration, no need to preserve.
+ */
+- tvp514x_reg_list[REG_OUTPUT_FORMATTER2].val |=
++ decoder->tvp514x_regs[REG_OUTPUT_FORMATTER2].val |=
+ (decoder->pdata->clk_polarity << 1);
+- tvp514x_reg_list[REG_SYNC_CONTROL].val |=
++ decoder->tvp514x_regs[REG_SYNC_CONTROL].val |=
+ ((decoder->pdata->hs_polarity << 2) |
+ (decoder->pdata->vs_polarity << 3));
+ /*
+@@ -1419,23 +1428,27 @@ tvp514x_probe(struct i2c_client *client, const struct i2c_device_id *id)
+ */
+ decoder->id = (struct i2c_device_id *)id;
+ /* Attach to Master */
+- strcpy(tvp514x_int_device.u.slave->attach_to, decoder->pdata->master);
+- decoder->v4l2_int_device = &tvp514x_int_device;
++ strcpy(decoder->v4l2_int_device.u.slave->attach_to,
++ decoder->pdata->master);
+ decoder->client = client;
+ i2c_set_clientdata(client, decoder);
+
+ /* Register with V4L2 layer as slave device */
+- err = v4l2_int_device_register(decoder->v4l2_int_device);
++ err = v4l2_int_device_register(&decoder->v4l2_int_device);
+ if (err) {
+ i2c_set_clientdata(client, NULL);
+ v4l_err(client,
+ "Unable to register to v4l2. Err[%d]\n", err);
++ goto out_free;
+
+ } else
+ v4l_info(client, "Registered to v4l2 master %s!!\n",
+ decoder->pdata->master);
+-
+ return 0;
++
++out_free:
++ kfree(decoder);
++ return err;
+ }
+
+ /**
+@@ -1452,9 +1465,9 @@ static int __exit tvp514x_remove(struct i2c_client *client)
+ if (!client->adapter)
+ return -ENODEV; /* our client isn't attached */
+
+- v4l2_int_device_unregister(decoder->v4l2_int_device);
++ v4l2_int_device_unregister(&decoder->v4l2_int_device);
+ i2c_set_clientdata(client, NULL);
+-
++ kfree(decoder);
+ return 0;
+ }
+ /*
+diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c
+index 2cd64ef..3a5a95f 100644
+--- a/drivers/media/video/tvp5150.c
++++ b/drivers/media/video/tvp5150.c
+@@ -8,7 +8,6 @@
+ #include <linux/i2c.h>
+ #include <linux/videodev2.h>
+ #include <linux/delay.h>
+-#include <linux/video_decoder.h>
+ #include <media/v4l2-device.h>
+ #include <media/tvp5150.h>
+ #include <media/v4l2-i2c-drv-legacy.h>
+@@ -632,7 +631,7 @@ static int tvp5150_g_sliced_vbi_cap(struct v4l2_subdev *sd,
+ const struct i2c_vbi_ram_value *regs = vbi_ram_default;
+ int line;
+
+- v4l2_dbg(1, debug, sd, "VIDIOC_G_SLICED_VBI_CAP\n");
++ v4l2_dbg(1, debug, sd, "g_sliced_vbi_cap\n");
+ memset(cap, 0, sizeof *cap);
+
+ while (regs->reg != (u16)-1 ) {
+@@ -831,7 +830,7 @@ static int tvp5150_reset(struct v4l2_subdev *sd, u32 val)
+
+ static int tvp5150_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+ {
+- v4l2_dbg(1, debug, sd, "VIDIOC_G_CTRL called\n");
++ v4l2_dbg(1, debug, sd, "g_ctrl called\n");
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+@@ -861,7 +860,7 @@ static int tvp5150_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
+ if (ctrl->value < tvp5150_qctrl[i].minimum ||
+ ctrl->value > tvp5150_qctrl[i].maximum)
+ return -ERANGE;
+- v4l2_dbg(1, debug, sd, "VIDIOC_S_CTRL: id=%d, value=%d\n",
++ v4l2_dbg(1, debug, sd, "s_ctrl: id=%d, value=%d\n",
+ ctrl->id, ctrl->value);
+ break;
+ }
+@@ -1015,7 +1014,7 @@ static int tvp5150_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
+ {
+ int i;
+
+- v4l2_dbg(1, debug, sd, "VIDIOC_QUERYCTRL called\n");
++ v4l2_dbg(1, debug, sd, "queryctrl called\n");
+
+ for (i = 0; i < ARRAY_SIZE(tvp5150_qctrl); i++)
+ if (qc->id && qc->id == tvp5150_qctrl[i].id) {
+@@ -1126,7 +1125,6 @@ MODULE_DEVICE_TABLE(i2c, tvp5150_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "tvp5150",
+- .driverid = I2C_DRIVERID_TVP5150,
+ .command = tvp5150_command,
+ .probe = tvp5150_probe,
+ .remove = tvp5150_remove,
+diff --git a/drivers/media/video/tw9910.c b/drivers/media/video/tw9910.c
+index 52c0357..a399476 100644
+--- a/drivers/media/video/tw9910.c
++++ b/drivers/media/video/tw9910.c
+@@ -460,9 +460,11 @@ static int tw9910_mask_set(struct i2c_client *client, u8 command,
+ u8 mask, u8 set)
+ {
+ s32 val = i2c_smbus_read_byte_data(client, command);
++ if (val < 0)
++ return val;
+
+ val &= ~mask;
+- val |= set;
++ val |= set & mask;
+
+ return i2c_smbus_write_byte_data(client, command, val);
+ }
+@@ -639,8 +641,8 @@ static int tw9910_set_register(struct soc_camera_device *icd,
+ }
+ #endif
+
+-static int tw9910_set_fmt(struct soc_camera_device *icd, __u32 pixfmt,
+- struct v4l2_rect *rect)
++static int tw9910_set_crop(struct soc_camera_device *icd,
++ struct v4l2_rect *rect)
+ {
+ struct tw9910_priv *priv = container_of(icd, struct tw9910_priv, icd);
+ int ret = -EINVAL;
+@@ -731,8 +733,33 @@ tw9910_set_fmt_error:
+ return ret;
+ }
+
++static int tw9910_set_fmt(struct soc_camera_device *icd,
++ struct v4l2_format *f)
++{
++ struct v4l2_pix_format *pix = &f->fmt.pix;
++ struct v4l2_rect rect = {
++ .left = icd->x_current,
++ .top = icd->y_current,
++ .width = pix->width,
++ .height = pix->height,
++ };
++ int i;
++
++ /*
++ * check color format
++ */
++ for (i = 0; i < ARRAY_SIZE(tw9910_color_fmt); i++)
++ if (pix->pixelformat == tw9910_color_fmt[i].fourcc)
++ break;
++
++ if (i == ARRAY_SIZE(tw9910_color_fmt))
++ return -EINVAL;
++
++ return tw9910_set_crop(icd, &rect);
++}
++
+ static int tw9910_try_fmt(struct soc_camera_device *icd,
+- struct v4l2_format *f)
++ struct v4l2_format *f)
+ {
+ struct v4l2_pix_format *pix = &f->fmt.pix;
+ const struct tw9910_scale_ctrl *scale;
+@@ -820,6 +847,7 @@ static struct soc_camera_ops tw9910_ops = {
+ .release = tw9910_release,
+ .start_capture = tw9910_start_capture,
+ .stop_capture = tw9910_stop_capture,
++ .set_crop = tw9910_set_crop,
+ .set_fmt = tw9910_set_fmt,
+ .try_fmt = tw9910_try_fmt,
+ .set_bus_param = tw9910_set_bus_param,
+diff --git a/drivers/media/video/upd64031a.c b/drivers/media/video/upd64031a.c
+index f4522bb..c0ac651 100644
+--- a/drivers/media/video/upd64031a.c
++++ b/drivers/media/video/upd64031a.c
+@@ -187,11 +187,6 @@ static int upd64031a_s_register(struct v4l2_subdev *sd, struct v4l2_dbg_register
+ }
+ #endif
+
+-static int upd64031a_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+-}
+-
+ /* ----------------------------------------------------------------------- */
+
+ static const struct v4l2_subdev_core_ops upd64031a_core_ops = {
+@@ -267,8 +262,6 @@ MODULE_DEVICE_TABLE(i2c, upd64031a_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "upd64031a",
+- .driverid = I2C_DRIVERID_UPD64031A,
+- .command = upd64031a_command,
+ .probe = upd64031a_probe,
+ .remove = upd64031a_remove,
+ .id_table = upd64031a_id,
+diff --git a/drivers/media/video/upd64083.c b/drivers/media/video/upd64083.c
+index a5fb74b..410c915 100644
+--- a/drivers/media/video/upd64083.c
++++ b/drivers/media/video/upd64083.c
+@@ -164,11 +164,6 @@ static int upd64083_log_status(struct v4l2_subdev *sd)
+ return 0;
+ }
+
+-static int upd64083_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+-}
+-
+ /* ----------------------------------------------------------------------- */
+
+ static const struct v4l2_subdev_core_ops upd64083_core_ops = {
+@@ -239,8 +234,6 @@ MODULE_DEVICE_TABLE(i2c, upd64083_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "upd64083",
+- .driverid = I2C_DRIVERID_UPD64083,
+- .command = upd64083_command,
+ .probe = upd64083_probe,
+ .remove = upd64083_remove,
+ .id_table = upd64083_id,
+diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c
+index 2f11063..8d73979 100644
+--- a/drivers/media/video/usbvideo/vicam.c
++++ b/drivers/media/video/usbvideo/vicam.c
+@@ -191,7 +191,7 @@ initialize_camera(struct vicam_camera *cam)
+ {
+ int err;
+ const struct ihex_binrec *rec;
+- const struct firmware *fw;
++ const struct firmware *uninitialized_var(fw);
+
+ err = request_ihex_firmware(&fw, "vicam/firmware.fw", &cam->udev->dev);
+ if (err) {
+diff --git a/drivers/media/video/usbvision/usbvision-core.c b/drivers/media/video/usbvision/usbvision-core.c
+index 9e4f506..a0feb1c 100644
+--- a/drivers/media/video/usbvision/usbvision-core.c
++++ b/drivers/media/video/usbvision/usbvision-core.c
+@@ -36,7 +36,6 @@
+ #include <linux/spinlock.h>
+ #include <asm/io.h>
+ #include <linux/videodev2.h>
+-#include <linux/video_decoder.h>
+ #include <linux/i2c.h>
+
+ #include <media/saa7115.h>
+@@ -381,8 +380,9 @@ int usbvision_scratch_alloc(struct usb_usbvision *usbvision)
+ usbvision->scratch = vmalloc_32(scratch_buf_size);
+ scratch_reset(usbvision);
+ if(usbvision->scratch == NULL) {
+- err("%s: unable to allocate %d bytes for scratch",
+- __func__, scratch_buf_size);
++ dev_err(&usbvision->dev->dev,
++ "%s: unable to allocate %d bytes for scratch\n",
++ __func__, scratch_buf_size);
+ return -ENOMEM;
+ }
+ return 0;
+@@ -491,8 +491,9 @@ int usbvision_decompress_alloc(struct usb_usbvision *usbvision)
+ int IFB_size = MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * 3 / 2;
+ usbvision->IntraFrameBuffer = vmalloc_32(IFB_size);
+ if (usbvision->IntraFrameBuffer == NULL) {
+- err("%s: unable to allocate %d for compr. frame buffer",
+- __func__, IFB_size);
++ dev_err(&usbvision->dev->dev,
++ "%s: unable to allocate %d for compr. frame buffer\n",
++ __func__, IFB_size);
+ return -ENOMEM;
+ }
+ return 0;
+@@ -1514,8 +1515,9 @@ static void usbvision_isocIrq(struct urb *urb)
+ errCode = usb_submit_urb (urb, GFP_ATOMIC);
+
+ if(errCode) {
+- err("%s: usb_submit_urb failed: error %d",
+- __func__, errCode);
++ dev_err(&usbvision->dev->dev,
++ "%s: usb_submit_urb failed: error %d\n",
++ __func__, errCode);
+ }
+
+ return;
+@@ -1546,7 +1548,8 @@ int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg)
+ 0, (__u16) reg, buffer, 1, HZ);
+
+ if (errCode < 0) {
+- err("%s: failed: error %d", __func__, errCode);
++ dev_err(&usbvision->dev->dev,
++ "%s: failed: error %d\n", __func__, errCode);
+ return errCode;
+ }
+ return buffer[0];
+@@ -1574,7 +1577,8 @@ int usbvision_write_reg(struct usb_usbvision *usbvision, unsigned char reg,
+ USB_RECIP_ENDPOINT, 0, (__u16) reg, &value, 1, HZ);
+
+ if (errCode < 0) {
+- err("%s: failed: error %d", __func__, errCode);
++ dev_err(&usbvision->dev->dev,
++ "%s: failed: error %d\n", __func__, errCode);
+ }
+ return errCode;
+ }
+@@ -1850,7 +1854,8 @@ int usbvision_set_output(struct usb_usbvision *usbvision, int width,
+ 0, (__u16) USBVISION_LXSIZE_O, value, 4, HZ);
+
+ if (errCode < 0) {
+- err("%s failed: error %d", __func__, errCode);
++ dev_err(&usbvision->dev->dev,
++ "%s failed: error %d\n", __func__, errCode);
+ return errCode;
+ }
+ usbvision->curwidth = usbvision->stretch_width * UsbWidth;
+@@ -2236,7 +2241,7 @@ static int usbvision_set_dram_settings(struct usb_usbvision *usbvision)
+ (__u16) USBVISION_DRM_PRM1, value, 8, HZ);
+
+ if (rc < 0) {
+- err("%sERROR=%d", __func__, rc);
++ dev_err(&usbvision->dev->dev, "%sERROR=%d\n", __func__, rc);
+ return rc;
+ }
+
+@@ -2432,8 +2437,9 @@ int usbvision_set_alternate(struct usb_usbvision *dev)
+ PDEBUG(DBG_FUNC,"setting alternate %d with wMaxPacketSize=%u", dev->ifaceAlt,dev->isocPacketSize);
+ errCode = usb_set_interface(dev->dev, dev->iface, dev->ifaceAlt);
+ if (errCode < 0) {
+- err ("cannot change alternate number to %d (error=%i)",
+- dev->ifaceAlt, errCode);
++ dev_err(&dev->dev->dev,
++ "cannot change alternate number to %d (error=%i)\n",
++ dev->ifaceAlt, errCode);
+ return errCode;
+ }
+ }
+@@ -2484,7 +2490,8 @@ int usbvision_init_isoc(struct usb_usbvision *usbvision)
+
+ urb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL);
+ if (urb == NULL) {
+- err("%s: usb_alloc_urb() failed", __func__);
++ dev_err(&usbvision->dev->dev,
++ "%s: usb_alloc_urb() failed\n", __func__);
+ return -ENOMEM;
+ }
+ usbvision->sbuf[bufIdx].urb = urb;
+@@ -2496,7 +2503,7 @@ int usbvision_init_isoc(struct usb_usbvision *usbvision)
+ urb->dev = dev;
+ urb->context = usbvision;
+ urb->pipe = usb_rcvisocpipe(dev, usbvision->video_endp);
+- urb->transfer_flags = URB_ISO_ASAP;
++ urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP;
+ urb->interval = 1;
+ urb->transfer_buffer = usbvision->sbuf[bufIdx].data;
+ urb->complete = usbvision_isocIrq;
+@@ -2516,8 +2523,9 @@ int usbvision_init_isoc(struct usb_usbvision *usbvision)
+ errCode = usb_submit_urb(usbvision->sbuf[bufIdx].urb,
+ GFP_KERNEL);
+ if (errCode) {
+- err("%s: usb_submit_urb(%d) failed: error %d",
+- __func__, bufIdx, errCode);
++ dev_err(&usbvision->dev->dev,
++ "%s: usb_submit_urb(%d) failed: error %d\n",
++ __func__, bufIdx, errCode);
+ }
+ }
+
+@@ -2566,8 +2574,9 @@ void usbvision_stop_isoc(struct usb_usbvision *usbvision)
+ errCode = usb_set_interface(usbvision->dev, usbvision->iface,
+ usbvision->ifaceAlt);
+ if (errCode < 0) {
+- err("%s: usb_set_interface() failed: error %d",
+- __func__, errCode);
++ dev_err(&usbvision->dev->dev,
++ "%s: usb_set_interface() failed: error %d\n",
++ __func__, errCode);
+ usbvision->last_error = errCode;
+ }
+ regValue = (16-usbvision_read_reg(usbvision, USBVISION_ALTER_REG)) & 0x0F;
+@@ -2623,7 +2632,7 @@ int usbvision_muxsel(struct usb_usbvision *usbvision, int channel)
+ }
+ route.input = mode[channel];
+ route.output = 0;
+- call_i2c_clients(usbvision, VIDIOC_INT_S_VIDEO_ROUTING,&route);
++ call_all(usbvision, video, s_routing, &route);
+ usbvision_set_audio(usbvision, audio[channel]);
+ return 0;
+ }
+diff --git a/drivers/media/video/usbvision/usbvision-i2c.c b/drivers/media/video/usbvision/usbvision-i2c.c
+index 6b66ae4..dd2f8f2 100644
+--- a/drivers/media/video/usbvision/usbvision-i2c.c
++++ b/drivers/media/video/usbvision/usbvision-i2c.c
+@@ -119,7 +119,8 @@ static inline int usb_find_address(struct i2c_adapter *i2c_adap,
+ /* try extended address code... */
+ ret = try_write_address(i2c_adap, addr, retries);
+ if (ret != 1) {
+- err("died at extended address code, while writing");
++ dev_err(&i2c_adap->dev,
++ "died at extended address code, while writing\n");
+ return -EREMOTEIO;
+ }
+ add[0] = addr;
+@@ -128,7 +129,8 @@ static inline int usb_find_address(struct i2c_adapter *i2c_adap,
+ addr |= 0x01;
+ ret = try_read_address(i2c_adap, addr, retries);
+ if (ret != 1) {
+- err("died at extended address code, while reading");
++ dev_err(&i2c_adap->dev,
++ "died at extended address code, while reading\n");
+ return -EREMOTEIO;
+ }
+ }
+@@ -200,72 +202,78 @@ static struct i2c_algorithm usbvision_algo = {
+ };
+
+
+-/*
+- * registering functions to load algorithms at runtime
+- */
+-static int usbvision_i2c_usb_add_bus(struct i2c_adapter *adap)
+-{
+- PDEBUG(DBG_I2C, "I2C debugging is enabled [i2c]");
+- PDEBUG(DBG_I2C, "ALGO debugging is enabled [i2c]");
+-
+- /* register new adapter to i2c module... */
+-
+- adap->algo = &usbvision_algo;
+-
+- adap->timeout = 100; /* default values, should */
+- adap->retries = 3; /* be replaced by defines */
+-
+- i2c_add_adapter(adap);
+-
+- PDEBUG(DBG_I2C,"i2c bus for %s registered", adap->name);
+-
+- return 0;
+-}
+-
+ /* ----------------------------------------------------------------------- */
+ /* usbvision specific I2C functions */
+ /* ----------------------------------------------------------------------- */
+ static struct i2c_adapter i2c_adap_template;
+-static struct i2c_client i2c_client_template;
+
+ int usbvision_i2c_register(struct usb_usbvision *usbvision)
+ {
++ static unsigned short saa711x_addrs[] = {
++ 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */
++ 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */
++ I2C_CLIENT_END };
++
+ memcpy(&usbvision->i2c_adap, &i2c_adap_template,
+ sizeof(struct i2c_adapter));
+- memcpy(&usbvision->i2c_client, &i2c_client_template,
+- sizeof(struct i2c_client));
+
+ sprintf(usbvision->i2c_adap.name + strlen(usbvision->i2c_adap.name),
+ " #%d", usbvision->vdev->num);
+ PDEBUG(DBG_I2C,"Adaptername: %s", usbvision->i2c_adap.name);
+ usbvision->i2c_adap.dev.parent = &usbvision->dev->dev;
+
+- i2c_set_adapdata(&usbvision->i2c_adap, usbvision);
+- i2c_set_clientdata(&usbvision->i2c_client, usbvision);
+-
+- usbvision->i2c_client.adapter = &usbvision->i2c_adap;
++ i2c_set_adapdata(&usbvision->i2c_adap, &usbvision->v4l2_dev);
+
+ if (usbvision_write_reg(usbvision, USBVISION_SER_MODE, USBVISION_IIC_LRNACK) < 0) {
+ printk(KERN_ERR "usbvision_register: can't write reg\n");
+ return -EBUSY;
+ }
+
+-#ifdef CONFIG_MODULES
++ PDEBUG(DBG_I2C, "I2C debugging is enabled [i2c]");
++ PDEBUG(DBG_I2C, "ALGO debugging is enabled [i2c]");
++
++ /* register new adapter to i2c module... */
++
++ usbvision->i2c_adap.algo = &usbvision_algo;
++
++ usbvision->i2c_adap.timeout = 100; /* default values, should */
++ usbvision->i2c_adap.retries = 3; /* be replaced by defines */
++
++ i2c_add_adapter(&usbvision->i2c_adap);
++
++ PDEBUG(DBG_I2C, "i2c bus for %s registered", usbvision->i2c_adap.name);
++
+ /* Request the load of the i2c modules we need */
+ switch (usbvision_device_data[usbvision->DevModel].Codec) {
+ case CODEC_SAA7113:
+- request_module("saa7115");
+- break;
+ case CODEC_SAA7111:
+- request_module("saa7115");
++ v4l2_i2c_new_probed_subdev(&usbvision->i2c_adap, "saa7115",
++ "saa7115_auto", saa711x_addrs);
+ break;
+ }
+ if (usbvision_device_data[usbvision->DevModel].Tuner == 1) {
+- request_module("tuner");
++ struct v4l2_subdev *sd;
++ enum v4l2_i2c_tuner_type type;
++ struct tuner_setup tun_setup;
++
++ sd = v4l2_i2c_new_probed_subdev(&usbvision->i2c_adap, "tuner",
++ "tuner", v4l2_i2c_tuner_addrs(ADDRS_DEMOD));
++ /* depending on whether we found a demod or not, select
++ the tuner type. */
++ type = sd ? ADDRS_TV_WITH_DEMOD : ADDRS_TV;
++
++ sd = v4l2_i2c_new_probed_subdev(&usbvision->i2c_adap, "tuner",
++ "tuner", v4l2_i2c_tuner_addrs(type));
++
++ if (usbvision->tuner_type != -1) {
++ tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
++ tun_setup.type = usbvision->tuner_type;
++ tun_setup.addr = v4l2_i2c_subdev_addr(sd);
++ call_all(usbvision, tuner, s_type_addr, &tun_setup);
++ }
+ }
+-#endif
+
+- return usbvision_i2c_usb_add_bus(&usbvision->i2c_adap);
++ return 0;
+ }
+
+ int usbvision_i2c_unregister(struct usb_usbvision *usbvision)
+@@ -278,67 +286,6 @@ int usbvision_i2c_unregister(struct usb_usbvision *usbvision)
+ return 0;
+ }
+
+-void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd,
+- void *arg)
+-{
+- i2c_clients_command(&usbvision->i2c_adap, cmd, arg);
+-}
+-
+-static int attach_inform(struct i2c_client *client)
+-{
+- struct usb_usbvision *usbvision;
+-
+- usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter);
+-
+- switch (client->addr << 1) {
+- case 0x42 << 1:
+- case 0x43 << 1:
+- case 0x4a << 1:
+- case 0x4b << 1:
+- PDEBUG(DBG_I2C,"attach_inform: tda9887 detected.");
+- break;
+- case 0x42:
+- PDEBUG(DBG_I2C,"attach_inform: saa7114 detected.");
+- break;
+- case 0x4a:
+- PDEBUG(DBG_I2C,"attach_inform: saa7113 detected.");
+- break;
+- case 0x48:
+- PDEBUG(DBG_I2C,"attach_inform: saa7111 detected.");
+- break;
+- case 0xa0:
+- PDEBUG(DBG_I2C,"attach_inform: eeprom detected.");
+- break;
+-
+- default:
+- {
+- struct tuner_setup tun_setup;
+-
+- PDEBUG(DBG_I2C,"attach inform: detected I2C address %x", client->addr << 1);
+- usbvision->tuner_addr = client->addr;
+-
+- if ((usbvision->have_tuner) && (usbvision->tuner_type != -1)) {
+- tun_setup.mode_mask = T_ANALOG_TV | T_RADIO;
+- tun_setup.type = usbvision->tuner_type;
+- tun_setup.addr = usbvision->tuner_addr;
+- call_i2c_clients(usbvision, TUNER_SET_TYPE_ADDR, &tun_setup);
+- }
+- }
+- break;
+- }
+- return 0;
+-}
+-
+-static int detach_inform(struct i2c_client *client)
+-{
+- struct usb_usbvision *usbvision;
+-
+- usbvision = (struct usb_usbvision *)i2c_get_adapdata(client->adapter);
+-
+- PDEBUG(DBG_I2C,"usbvision[%d] detaches %s", usbvision->nr, client->name);
+- return 0;
+-}
+-
+ static int
+ usbvision_i2c_read_max4(struct usb_usbvision *usbvision, unsigned char addr,
+ char *buf, short len)
+@@ -511,14 +458,6 @@ static int usbvision_i2c_read(struct usb_usbvision *usbvision, unsigned char add
+ static struct i2c_adapter i2c_adap_template = {
+ .owner = THIS_MODULE,
+ .name = "usbvision",
+- .id = I2C_HW_B_BT848, /* FIXME */
+- .client_register = attach_inform,
+- .client_unregister = detach_inform,
+- .class = I2C_CLASS_TV_ANALOG,
+-};
+-
+-static struct i2c_client i2c_client_template = {
+- .name = "usbvision internal",
+ };
+
+ /*
+diff --git a/drivers/media/video/usbvision/usbvision-video.c b/drivers/media/video/usbvision/usbvision-video.c
+index 2622de0..fa62a2f 100644
+--- a/drivers/media/video/usbvision/usbvision-video.c
++++ b/drivers/media/video/usbvision/usbvision-video.c
+@@ -59,7 +59,6 @@
+ #include <linux/spinlock.h>
+ #include <asm/io.h>
+ #include <linux/videodev2.h>
+-#include <linux/video_decoder.h>
+ #include <linux/i2c.h>
+
+ #include <media/saa7115.h>
+@@ -212,7 +211,7 @@ static ssize_t show_hue(struct device *cd,
+ ctrl.id = V4L2_CID_HUE;
+ ctrl.value = 0;
+ if(usbvision->user)
+- call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl);
++ call_all(usbvision, core, g_ctrl, &ctrl);
+ return sprintf(buf, "%d\n", ctrl.value);
+ }
+ static DEVICE_ATTR(hue, S_IRUGO, show_hue, NULL);
+@@ -227,7 +226,7 @@ static ssize_t show_contrast(struct device *cd,
+ ctrl.id = V4L2_CID_CONTRAST;
+ ctrl.value = 0;
+ if(usbvision->user)
+- call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl);
++ call_all(usbvision, core, g_ctrl, &ctrl);
+ return sprintf(buf, "%d\n", ctrl.value);
+ }
+ static DEVICE_ATTR(contrast, S_IRUGO, show_contrast, NULL);
+@@ -242,7 +241,7 @@ static ssize_t show_brightness(struct device *cd,
+ ctrl.id = V4L2_CID_BRIGHTNESS;
+ ctrl.value = 0;
+ if(usbvision->user)
+- call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl);
++ call_all(usbvision, core, g_ctrl, &ctrl);
+ return sprintf(buf, "%d\n", ctrl.value);
+ }
+ static DEVICE_ATTR(brightness, S_IRUGO, show_brightness, NULL);
+@@ -257,7 +256,7 @@ static ssize_t show_saturation(struct device *cd,
+ ctrl.id = V4L2_CID_SATURATION;
+ ctrl.value = 0;
+ if(usbvision->user)
+- call_i2c_clients(usbvision, VIDIOC_G_CTRL, &ctrl);
++ call_all(usbvision, core, g_ctrl, &ctrl);
+ return sprintf(buf, "%d\n", ctrl.value);
+ }
+ static DEVICE_ATTR(saturation, S_IRUGO, show_saturation, NULL);
+@@ -329,7 +328,7 @@ static void usbvision_create_sysfs(struct video_device *vdev)
+ return;
+ } while (0);
+
+- err("%s error: %d\n", __func__, res);
++ dev_err(&vdev->dev, "%s error: %d\n", __func__, res);
+ }
+
+ static void usbvision_remove_sysfs(struct video_device *vdev)
+@@ -487,8 +486,9 @@ static int vidioc_g_register (struct file *file, void *priv,
+ /* NT100x has a 8-bit register space */
+ errCode = usbvision_read_reg(usbvision, reg->reg&0xff);
+ if (errCode < 0) {
+- err("%s: VIDIOC_DBG_G_REGISTER failed: error %d",
+- __func__, errCode);
++ dev_err(&usbvision->vdev->dev,
++ "%s: VIDIOC_DBG_G_REGISTER failed: error %d\n",
++ __func__, errCode);
+ return errCode;
+ }
+ reg->val = errCode;
+@@ -507,8 +507,9 @@ static int vidioc_s_register (struct file *file, void *priv,
+ /* NT100x has a 8-bit register space */
+ errCode = usbvision_write_reg(usbvision, reg->reg&0xff, reg->val);
+ if (errCode < 0) {
+- err("%s: VIDIOC_DBG_S_REGISTER failed: error %d",
+- __func__, errCode);
++ dev_err(&usbvision->vdev->dev,
++ "%s: VIDIOC_DBG_S_REGISTER failed: error %d\n",
++ __func__, errCode);
+ return errCode;
+ }
+ return 0;
+@@ -524,8 +525,7 @@ static int vidioc_querycap (struct file *file, void *priv,
+ strlcpy(vc->card,
+ usbvision_device_data[usbvision->DevModel].ModelString,
+ sizeof(vc->card));
+- strlcpy(vc->bus_info, dev_name(&usbvision->dev->dev),
+- sizeof(vc->bus_info));
++ usb_make_path(usbvision->dev, vc->bus_info, sizeof(vc->bus_info));
+ vc->version = USBVISION_DRIVER_VERSION;
+ vc->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_AUDIO |
+@@ -621,8 +621,7 @@ static int vidioc_s_std (struct file *file, void *priv, v4l2_std_id *id)
+ usbvision->tvnormId=*id;
+
+ mutex_lock(&usbvision->lock);
+- call_i2c_clients(usbvision, VIDIOC_S_STD,
+- &usbvision->tvnormId);
++ call_all(usbvision, tuner, s_std, usbvision->tvnormId);
+ mutex_unlock(&usbvision->lock);
+ /* propagate the change to the decoder */
+ usbvision_muxsel(usbvision, usbvision->ctl_input);
+@@ -644,7 +643,7 @@ static int vidioc_g_tuner (struct file *file, void *priv,
+ strcpy(vt->name, "Television");
+ }
+ /* Let clients fill in the remainder of this struct */
+- call_i2c_clients(usbvision,VIDIOC_G_TUNER,vt);
++ call_all(usbvision, tuner, g_tuner, vt);
+
+ return 0;
+ }
+@@ -658,7 +657,7 @@ static int vidioc_s_tuner (struct file *file, void *priv,
+ if (!usbvision->have_tuner || vt->index)
+ return -EINVAL;
+ /* let clients handle this */
+- call_i2c_clients(usbvision,VIDIOC_S_TUNER,vt);
++ call_all(usbvision, tuner, s_tuner, vt);
+
+ return 0;
+ }
+@@ -689,7 +688,7 @@ static int vidioc_s_frequency (struct file *file, void *priv,
+ return -EINVAL;
+
+ usbvision->freq = freq->frequency;
+- call_i2c_clients(usbvision, VIDIOC_S_FREQUENCY, freq);
++ call_all(usbvision, tuner, s_frequency, freq);
+
+ return 0;
+ }
+@@ -698,7 +697,6 @@ static int vidioc_g_audio (struct file *file, void *priv, struct v4l2_audio *a)
+ {
+ struct usb_usbvision *usbvision = video_drvdata(file);
+
+- memset(a,0,sizeof(*a));
+ if(usbvision->radio) {
+ strcpy(a->name,"Radio");
+ } else {
+@@ -722,12 +720,8 @@ static int vidioc_queryctrl (struct file *file, void *priv,
+ struct v4l2_queryctrl *ctrl)
+ {
+ struct usb_usbvision *usbvision = video_drvdata(file);
+- int id=ctrl->id;
+
+- memset(ctrl,0,sizeof(*ctrl));
+- ctrl->id=id;
+-
+- call_i2c_clients(usbvision, VIDIOC_QUERYCTRL, ctrl);
++ call_all(usbvision, core, queryctrl, ctrl);
+
+ if (!ctrl->type)
+ return -EINVAL;
+@@ -739,7 +733,7 @@ static int vidioc_g_ctrl (struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+ struct usb_usbvision *usbvision = video_drvdata(file);
+- call_i2c_clients(usbvision, VIDIOC_G_CTRL, ctrl);
++ call_all(usbvision, core, g_ctrl, ctrl);
+
+ return 0;
+ }
+@@ -748,7 +742,7 @@ static int vidioc_s_ctrl (struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
+ struct usb_usbvision *usbvision = video_drvdata(file);
+- call_i2c_clients(usbvision, VIDIOC_S_CTRL, ctrl);
++ call_all(usbvision, core, s_ctrl, ctrl);
+
+ return 0;
+ }
+@@ -763,8 +757,7 @@ static int vidioc_reqbufs (struct file *file,
+
+ /* Check input validity:
+ the user must do a VIDEO CAPTURE and MMAP method. */
+- if((vr->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) ||
+- (vr->memory != V4L2_MEMORY_MMAP))
++ if (vr->memory != V4L2_MEMORY_MMAP)
+ return -EINVAL;
+
+ if(usbvision->streaming == Stream_On) {
+@@ -789,9 +782,6 @@ static int vidioc_querybuf (struct file *file,
+
+ /* FIXME : must control
+ that buffers are mapped (VIDIOC_REQBUFS has been called) */
+- if(vb->type != V4L2_CAP_VIDEO_CAPTURE) {
+- return -EINVAL;
+- }
+ if(vb->index>=usbvision->num_frames) {
+ return -EINVAL;
+ }
+@@ -825,9 +815,6 @@ static int vidioc_qbuf (struct file *file, void *priv, struct v4l2_buffer *vb)
+ unsigned long lock_flags;
+
+ /* FIXME : works only on VIDEO_CAPTURE MODE, MMAP. */
+- if(vb->type != V4L2_CAP_VIDEO_CAPTURE) {
+- return -EINVAL;
+- }
+ if(vb->index>=usbvision->num_frames) {
+ return -EINVAL;
+ }
+@@ -862,9 +849,6 @@ static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *vb)
+ struct usbvision_frame *f;
+ unsigned long lock_flags;
+
+- if (vb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+-
+ if (list_empty(&(usbvision->outqueue))) {
+ if (usbvision->streaming == Stream_Idle)
+ return -EINVAL;
+@@ -899,10 +883,9 @@ static int vidioc_dqbuf (struct file *file, void *priv, struct v4l2_buffer *vb)
+ static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i)
+ {
+ struct usb_usbvision *usbvision = video_drvdata(file);
+- int b=V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ usbvision->streaming = Stream_On;
+- call_i2c_clients(usbvision,VIDIOC_STREAMON , &b);
++ call_all(usbvision, video, s_stream, 1);
+
+ return 0;
+ }
+@@ -911,7 +894,6 @@ static int vidioc_streamoff(struct file *file,
+ void *priv, enum v4l2_buf_type type)
+ {
+ struct usb_usbvision *usbvision = video_drvdata(file);
+- int b=V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+@@ -919,7 +901,7 @@ static int vidioc_streamoff(struct file *file,
+ if(usbvision->streaming == Stream_On) {
+ usbvision_stream_interrupt(usbvision);
+ /* Stop all video streamings */
+- call_i2c_clients(usbvision,VIDIOC_STREAMOFF , &b);
++ call_all(usbvision, video, s_stream, 0);
+ }
+ usbvision_empty_framequeues(usbvision);
+
+@@ -932,11 +914,8 @@ static int vidioc_enum_fmt_vid_cap (struct file *file, void *priv,
+ if(vfd->index>=USBVISION_SUPPORTED_PALETTES-1) {
+ return -EINVAL;
+ }
+- vfd->flags = 0;
+- vfd->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ strcpy(vfd->description,usbvision_v4l2_format[vfd->index].desc);
+ vfd->pixelformat = usbvision_v4l2_format[vfd->index].format;
+- memset(vfd->reserved, 0, sizeof(vfd->reserved));
+ return 0;
+ }
+
+@@ -1042,7 +1021,7 @@ static ssize_t usbvision_v4l2_read(struct file *file, char __user *buf,
+ if(usbvision->streaming != Stream_On) {
+ /* no stream is running, make it running ! */
+ usbvision->streaming = Stream_On;
+- call_i2c_clients(usbvision,VIDIOC_STREAMON , NULL);
++ call_all(usbvision, video, s_stream, 1);
+ }
+
+ /* Then, enqueue as many frames as possible
+@@ -1189,7 +1168,9 @@ static int usbvision_radio_open(struct file *file)
+ mutex_lock(&usbvision->lock);
+
+ if (usbvision->user) {
+- err("%s: Someone tried to open an already opened USBVision Radio!", __func__);
++ dev_err(&usbvision->rdev->dev,
++ "%s: Someone tried to open an already opened USBVision Radio!\n",
++ __func__);
+ errCode = -EBUSY;
+ }
+ else {
+@@ -1211,7 +1192,7 @@ static int usbvision_radio_open(struct file *file)
+
+ // If so far no errors then we shall start the radio
+ usbvision->radio = 1;
+- call_i2c_clients(usbvision,AUDC_SET_RADIO,&usbvision->tuner_type);
++ call_all(usbvision, tuner, s_radio);
+ usbvision_set_audio(usbvision, USBVISION_AUDIO_RADIO);
+ usbvision->user++;
+ }
+@@ -1413,7 +1394,8 @@ static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision,
+ struct video_device *vdev;
+
+ if (usb_dev == NULL) {
+- err("%s: usbvision->dev is not set", __func__);
++ dev_err(&usbvision->dev->dev,
++ "%s: usbvision->dev is not set\n", __func__);
+ return NULL;
+ }
+
+@@ -1423,7 +1405,7 @@ static struct video_device *usbvision_vdev_init(struct usb_usbvision *usbvision,
+ }
+ *vdev = *vdev_template;
+ // vdev->minor = -1;
+- vdev->parent = &usb_dev->dev;
++ vdev->v4l2_dev = &usbvision->v4l2_dev;
+ snprintf(vdev->name, sizeof(vdev->name), "%s", name);
+ video_set_drvdata(vdev, usbvision);
+ return vdev;
+@@ -1524,7 +1506,9 @@ static int __devinit usbvision_register_video(struct usb_usbvision *usbvision)
+ return 0;
+
+ err_exit:
+- err("USBVision[%d]: video_register_device() failed", usbvision->nr);
++ dev_err(&usbvision->dev->dev,
++ "USBVision[%d]: video_register_device() failed\n",
++ usbvision->nr);
+ usbvision_unregister_video(usbvision);
+ return -1;
+ }
+@@ -1542,33 +1526,30 @@ static struct usb_usbvision *usbvision_alloc(struct usb_device *dev)
+ {
+ struct usb_usbvision *usbvision;
+
+- if ((usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL)) ==
+- NULL) {
+- goto err_exit;
+- }
++ usbvision = kzalloc(sizeof(struct usb_usbvision), GFP_KERNEL);
++ if (usbvision == NULL)
++ return NULL;
+
+ usbvision->dev = dev;
++ if (v4l2_device_register(&dev->dev, &usbvision->v4l2_dev))
++ goto err_free;
+
+ mutex_init(&usbvision->lock); /* available */
+
+ // prepare control urb for control messages during interrupts
+ usbvision->ctrlUrb = usb_alloc_urb(USBVISION_URB_FRAMES, GFP_KERNEL);
+- if (usbvision->ctrlUrb == NULL) {
+- goto err_exit;
+- }
++ if (usbvision->ctrlUrb == NULL)
++ goto err_unreg;
+ init_waitqueue_head(&usbvision->ctrlUrb_wq);
+
+ usbvision_init_powerOffTimer(usbvision);
+
+ return usbvision;
+
+-err_exit:
+- if (usbvision && usbvision->ctrlUrb) {
+- usb_free_urb(usbvision->ctrlUrb);
+- }
+- if (usbvision) {
+- kfree(usbvision);
+- }
++err_unreg:
++ v4l2_device_unregister(&usbvision->v4l2_dev);
++err_free:
++ kfree(usbvision);
+ return NULL;
+ }
+
+@@ -1598,6 +1579,7 @@ static void usbvision_release(struct usb_usbvision *usbvision)
+ usb_free_urb(usbvision->ctrlUrb);
+ }
+
++ v4l2_device_unregister(&usbvision->v4l2_dev);
+ kfree(usbvision);
+
+ PDEBUG(DBG_PROBE, "success");
+@@ -1675,20 +1657,20 @@ static int __devinit usbvision_probe(struct usb_interface *intf,
+ }
+ endpoint = &interface->endpoint[1].desc;
+ if (!usb_endpoint_xfer_isoc(endpoint)) {
+- err("%s: interface %d. has non-ISO endpoint!",
++ dev_err(&intf->dev, "%s: interface %d. has non-ISO endpoint!\n",
+ __func__, ifnum);
+- err("%s: Endpoint attributes %d",
++ dev_err(&intf->dev, "%s: Endpoint attributes %d",
+ __func__, endpoint->bmAttributes);
+ return -ENODEV;
+ }
+ if (usb_endpoint_dir_out(endpoint)) {
+- err("%s: interface %d. has ISO OUT endpoint!",
++ dev_err(&intf->dev, "%s: interface %d. has ISO OUT endpoint!\n",
+ __func__, ifnum);
+ return -ENODEV;
+ }
+
+ if ((usbvision = usbvision_alloc(dev)) == NULL) {
+- err("%s: couldn't allocate USBVision struct", __func__);
++ dev_err(&intf->dev, "%s: couldn't allocate USBVision struct\n", __func__);
+ return -ENOMEM;
+ }
+
+@@ -1711,7 +1693,7 @@ static int __devinit usbvision_probe(struct usb_interface *intf,
+ usbvision->alt_max_pkt_size = kmalloc(32*
+ usbvision->num_alt,GFP_KERNEL);
+ if (usbvision->alt_max_pkt_size == NULL) {
+- err("usbvision: out of memory!\n");
++ dev_err(&intf->dev, "usbvision: out of memory!\n");
+ mutex_unlock(&usbvision->lock);
+ return -ENOMEM;
+ }
+@@ -1733,8 +1715,6 @@ static int __devinit usbvision_probe(struct usb_interface *intf,
+ usbvision->tuner_type = usbvision_device_data[model].TunerType;
+ }
+
+- usbvision->tuner_addr = ADDR_UNSET;
+-
+ usbvision->DevModel = model;
+ usbvision->remove_pending = 0;
+ usbvision->iface = ifnum;
+@@ -1772,7 +1752,8 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf)
+ PDEBUG(DBG_PROBE, "");
+
+ if (usbvision == NULL) {
+- err("%s: usb_get_intfdata() failed", __func__);
++ dev_err(&usbvision->dev->dev,
++ "%s: usb_get_intfdata() failed\n", __func__);
+ return;
+ }
+ usb_set_intfdata (intf, NULL);
+@@ -1782,6 +1763,8 @@ static void __devexit usbvision_disconnect(struct usb_interface *intf)
+ // At this time we ask to cancel outstanding URBs
+ usbvision_stop_isoc(usbvision);
+
++ v4l2_device_disconnect(&usbvision->v4l2_dev);
++
+ if (usbvision->power) {
+ usbvision_i2c_unregister(usbvision);
+ usbvision_power_off(usbvision);
+diff --git a/drivers/media/video/usbvision/usbvision.h b/drivers/media/video/usbvision/usbvision.h
+index 20d7ec6..f8d7458 100644
+--- a/drivers/media/video/usbvision/usbvision.h
++++ b/drivers/media/video/usbvision/usbvision.h
+@@ -6,7 +6,7 @@
+ * Dwaine Garden <dwainegarden@rogers.com>
+ *
+ *
+- * Report problems to v4l MailingList : http://www.redhat.com/mailman/listinfo/video4linux-list
++ * Report problems to v4l MailingList: linux-media@vger.kernel.org
+ *
+ * This module is part of usbvision driver project.
+ * Updates to driver completed by Dwaine P. Garden
+@@ -35,7 +35,7 @@
+ #include <linux/usb.h>
+ #include <linux/i2c.h>
+ #include <linux/mutex.h>
+-#include <media/v4l2-common.h>
++#include <media/v4l2-device.h>
+ #include <media/tuner.h>
+ #include <linux/videodev2.h>
+
+@@ -357,13 +357,13 @@ extern struct usbvision_device_data_st usbvision_device_data[];
+ extern struct usb_device_id usbvision_table[];
+
+ struct usb_usbvision {
++ struct v4l2_device v4l2_dev;
+ struct video_device *vdev; /* Video Device */
+ struct video_device *rdev; /* Radio Device */
+ struct video_device *vbi; /* VBI Device */
+
+ /* i2c Declaration Section*/
+ struct i2c_adapter i2c_adap;
+- struct i2c_client i2c_client;
+
+ struct urb *ctrlUrb;
+ unsigned char ctrlUrbBuffer[8];
+@@ -374,7 +374,6 @@ struct usb_usbvision {
+ /* configuration part */
+ int have_tuner;
+ int tuner_type;
+- int tuner_addr;
+ int bridgeType; // NT1003, NT1004, NT1005
+ int radio;
+ int video_inputs; // # of inputs
+@@ -464,6 +463,8 @@ struct usb_usbvision {
+ int ComprBlockTypes[4];
+ };
+
++#define call_all(usbvision, o, f, args...) \
++ v4l2_device_call_all(&usbvision->v4l2_dev, 0, o, f, ##args)
+
+ /* --------------------------------------------------------------- */
+ /* defined in usbvision-i2c.c */
+@@ -475,7 +476,6 @@ struct usb_usbvision {
+ /* ----------------------------------------------------------------------- */
+ int usbvision_i2c_register(struct usb_usbvision *usbvision);
+ int usbvision_i2c_unregister(struct usb_usbvision *usbvision);
+-void call_i2c_clients(struct usb_usbvision *usbvision, unsigned int cmd,void *arg);
+
+ /* defined in usbvision-core.c */
+ int usbvision_read_reg(struct usb_usbvision *usbvision, unsigned char reg);
+diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c
+index d2576f6..0d7e38d 100644
+--- a/drivers/media/video/uvc/uvc_ctrl.c
++++ b/drivers/media/video/uvc/uvc_ctrl.c
+@@ -786,7 +786,7 @@ int uvc_query_v4l2_ctrl(struct uvc_video_device *video,
+ memset(v4l2_ctrl, 0, sizeof *v4l2_ctrl);
+ v4l2_ctrl->id = mapping->id;
+ v4l2_ctrl->type = mapping->v4l2_type;
+- strncpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name);
++ strlcpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name);
+ v4l2_ctrl->flags = 0;
+
+ if (!(ctrl->info->flags & UVC_CONTROL_SET_CUR))
+diff --git a/drivers/media/video/uvc/uvc_driver.c b/drivers/media/video/uvc/uvc_driver.c
+index b128732..399412d 100644
+--- a/drivers/media/video/uvc/uvc_driver.c
++++ b/drivers/media/video/uvc/uvc_driver.c
+@@ -314,7 +314,7 @@ static int uvc_parse_format(struct uvc_device *dev,
+ fmtdesc = uvc_format_by_guid(&buffer[5]);
+
+ if (fmtdesc != NULL) {
+- strncpy(format->name, fmtdesc->name,
++ strlcpy(format->name, fmtdesc->name,
+ sizeof format->name);
+ format->fcc = fmtdesc->fcc;
+ } else {
+@@ -345,7 +345,7 @@ static int uvc_parse_format(struct uvc_device *dev,
+ return -EINVAL;
+ }
+
+- strncpy(format->name, "MJPEG", sizeof format->name);
++ strlcpy(format->name, "MJPEG", sizeof format->name);
+ format->fcc = V4L2_PIX_FMT_MJPEG;
+ format->flags = UVC_FMT_FLAG_COMPRESSED;
+ format->bpp = 0;
+@@ -363,13 +363,13 @@ static int uvc_parse_format(struct uvc_device *dev,
+
+ switch (buffer[8] & 0x7f) {
+ case 0:
+- strncpy(format->name, "SD-DV", sizeof format->name);
++ strlcpy(format->name, "SD-DV", sizeof format->name);
+ break;
+ case 1:
+- strncpy(format->name, "SDL-DV", sizeof format->name);
++ strlcpy(format->name, "SDL-DV", sizeof format->name);
+ break;
+ case 2:
+- strncpy(format->name, "HD-DV", sizeof format->name);
++ strlcpy(format->name, "HD-DV", sizeof format->name);
+ break;
+ default:
+ uvc_trace(UVC_TRACE_DESCR, "device %d videostreaming"
+@@ -379,7 +379,7 @@ static int uvc_parse_format(struct uvc_device *dev,
+ return -EINVAL;
+ }
+
+- strncat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz",
++ strlcat(format->name, buffer[8] & (1 << 7) ? " 60Hz" : " 50Hz",
+ sizeof format->name);
+
+ format->fcc = V4L2_PIX_FMT_DV;
+@@ -1526,7 +1526,7 @@ static int uvc_register_video(struct uvc_device *dev)
+ vdev->minor = -1;
+ vdev->fops = &uvc_fops;
+ vdev->release = video_device_release;
+- strncpy(vdev->name, dev->name, sizeof vdev->name);
++ strlcpy(vdev->name, dev->name, sizeof vdev->name);
+
+ /* Set the driver data before calling video_register_device, otherwise
+ * uvc_v4l2_open might race us.
+@@ -1621,7 +1621,7 @@ static int uvc_probe(struct usb_interface *intf,
+ dev->quirks = id->driver_info | uvc_quirks_param;
+
+ if (udev->product != NULL)
+- strncpy(dev->name, udev->product, sizeof dev->name);
++ strlcpy(dev->name, udev->product, sizeof dev->name);
+ else
+ snprintf(dev->name, sizeof dev->name,
+ "UVC Camera (%04x:%04x)",
+@@ -1833,6 +1833,15 @@ static struct usb_device_id uvc_ids[] = {
+ .bInterfaceClass = USB_CLASS_VENDOR_SPEC,
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0 },
++ /* Alcor Micro AU3820 (Future Boy PC USB Webcam) */
++ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
++ | USB_DEVICE_ID_MATCH_INT_INFO,
++ .idVendor = 0x058f,
++ .idProduct = 0x3820,
++ .bInterfaceClass = USB_CLASS_VIDEO,
++ .bInterfaceSubClass = 1,
++ .bInterfaceProtocol = 0,
++ .driver_info = UVC_QUIRK_PROBE_MINMAX },
+ /* Apple Built-In iSight */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+@@ -1852,6 +1861,15 @@ static struct usb_device_id uvc_ids[] = {
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
++ /* ViMicro */
++ { .match_flags = USB_DEVICE_ID_MATCH_VENDOR
++ | USB_DEVICE_ID_MATCH_INT_INFO,
++ .idVendor = 0x0ac8,
++ .idProduct = 0x0000,
++ .bInterfaceClass = USB_CLASS_VIDEO,
++ .bInterfaceSubClass = 1,
++ .bInterfaceProtocol = 0,
++ .driver_info = UVC_QUIRK_FIX_BANDWIDTH },
+ /* MT6227 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+@@ -1879,7 +1897,7 @@ static struct usb_device_id uvc_ids[] = {
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+- /* Asus F9SG */
++ /* Syntek (Asus F9SG) */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+ .idVendor = 0x174f,
+@@ -1897,6 +1915,15 @@ static struct usb_device_id uvc_ids[] = {
+ .bInterfaceSubClass = 1,
+ .bInterfaceProtocol = 0,
+ .driver_info = UVC_QUIRK_STREAM_NO_FID },
++ /* Syntek (JAOtech Smart Terminal) */
++ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
++ | USB_DEVICE_ID_MATCH_INT_INFO,
++ .idVendor = 0x174f,
++ .idProduct = 0x8a34,
++ .bInterfaceClass = USB_CLASS_VIDEO,
++ .bInterfaceSubClass = 1,
++ .bInterfaceProtocol = 0,
++ .driver_info = UVC_QUIRK_STREAM_NO_FID },
+ /* Lenovo Thinkpad SL500 */
+ { .match_flags = USB_DEVICE_ID_MATCH_DEVICE
+ | USB_DEVICE_ID_MATCH_INT_INFO,
+diff --git a/drivers/media/video/uvc/uvc_status.c b/drivers/media/video/uvc/uvc_status.c
+index c705f24..21d8712 100644
+--- a/drivers/media/video/uvc/uvc_status.c
++++ b/drivers/media/video/uvc/uvc_status.c
+@@ -24,26 +24,19 @@
+ #ifdef CONFIG_USB_VIDEO_CLASS_INPUT_EVDEV
+ static int uvc_input_init(struct uvc_device *dev)
+ {
+- struct usb_device *udev = dev->udev;
+ struct input_dev *input;
+- char *phys = NULL;
+ int ret;
+
+ input = input_allocate_device();
+ if (input == NULL)
+ return -ENOMEM;
+
+- phys = kmalloc(6 + strlen(udev->bus->bus_name) + strlen(udev->devpath),
+- GFP_KERNEL);
+- if (phys == NULL) {
+- ret = -ENOMEM;
+- goto error;
+- }
+- sprintf(phys, "usb-%s-%s", udev->bus->bus_name, udev->devpath);
++ usb_make_path(dev->udev, dev->input_phys, sizeof(dev->input_phys));
++ strlcat(dev->input_phys, "/button", sizeof(dev->input_phys));
+
+ input->name = dev->name;
+- input->phys = phys;
+- usb_to_input_id(udev, &input->id);
++ input->phys = dev->input_phys;
++ usb_to_input_id(dev->udev, &input->id);
+ input->dev.parent = &dev->intf->dev;
+
+ __set_bit(EV_KEY, input->evbit);
+@@ -57,7 +50,6 @@ static int uvc_input_init(struct uvc_device *dev)
+
+ error:
+ input_free_device(input);
+- kfree(phys);
+ return ret;
+ }
+
+diff --git a/drivers/media/video/uvc/uvc_v4l2.c b/drivers/media/video/uvc/uvc_v4l2.c
+index d681519..2a80caa 100644
+--- a/drivers/media/video/uvc/uvc_v4l2.c
++++ b/drivers/media/video/uvc/uvc_v4l2.c
+@@ -55,7 +55,7 @@ static int uvc_v4l2_query_menu(struct uvc_video_device *video,
+ return -EINVAL;
+
+ menu_info = &mapping->menu_info[query_menu->index];
+- strncpy(query_menu->name, menu_info->name, 32);
++ strlcpy(query_menu->name, menu_info->name, sizeof query_menu->name);
+ return 0;
+ }
+
+@@ -486,10 +486,10 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+ struct v4l2_capability *cap = arg;
+
+ memset(cap, 0, sizeof *cap);
+- strncpy(cap->driver, "uvcvideo", sizeof cap->driver);
+- strncpy(cap->card, vdev->name, 32);
+- strncpy(cap->bus_info, video->dev->udev->bus->bus_name,
+- sizeof cap->bus_info);
++ strlcpy(cap->driver, "uvcvideo", sizeof cap->driver);
++ strlcpy(cap->card, vdev->name, sizeof cap->card);
++ usb_make_path(video->dev->udev,
++ cap->bus_info, sizeof(cap->bus_info));
+ cap->version = DRIVER_VERSION_NUMBER;
+ if (video->streaming->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE
+@@ -620,7 +620,7 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+
+ memset(input, 0, sizeof *input);
+ input->index = index;
+- strncpy(input->name, iterm->name, sizeof input->name);
++ strlcpy(input->name, iterm->name, sizeof input->name);
+ if (UVC_ENTITY_TYPE(iterm) == ITT_CAMERA)
+ input->type = V4L2_INPUT_TYPE_CAMERA;
+ break;
+@@ -673,16 +673,22 @@ static long uvc_v4l2_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+ {
+ struct v4l2_fmtdesc *fmt = arg;
+ struct uvc_format *format;
++ enum v4l2_buf_type type = fmt->type;
++ __u32 index = fmt->index;
+
+ if (fmt->type != video->streaming->type ||
+ fmt->index >= video->streaming->nformats)
+ return -EINVAL;
+
++ memset(fmt, 0, sizeof(*fmt));
++ fmt->index = index;
++ fmt->type = type;
++
+ format = &video->streaming->format[fmt->index];
+ fmt->flags = 0;
+ if (format->flags & UVC_FMT_FLAG_COMPRESSED)
+ fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
+- strncpy(fmt->description, format->name,
++ strlcpy(fmt->description, format->name,
+ sizeof fmt->description);
+ fmt->description[sizeof fmt->description - 1] = 0;
+ fmt->pixelformat = format->fcc;
+diff --git a/drivers/media/video/uvc/uvc_video.c b/drivers/media/video/uvc/uvc_video.c
+index 9bc4705..a95e173 100644
+--- a/drivers/media/video/uvc/uvc_video.c
++++ b/drivers/media/video/uvc/uvc_video.c
+@@ -61,7 +61,7 @@ int uvc_query_ctrl(struct uvc_device *dev, __u8 query, __u8 unit,
+ return 0;
+ }
+
+-static void uvc_fixup_buffer_size(struct uvc_video_device *video,
++static void uvc_fixup_video_ctrl(struct uvc_video_device *video,
+ struct uvc_streaming_control *ctrl)
+ {
+ struct uvc_format *format;
+@@ -84,6 +84,31 @@ static void uvc_fixup_buffer_size(struct uvc_video_device *video,
+ video->dev->uvc_version < 0x0110))
+ ctrl->dwMaxVideoFrameSize =
+ frame->dwMaxVideoFrameBufferSize;
++
++ if (video->dev->quirks & UVC_QUIRK_FIX_BANDWIDTH &&
++ video->streaming->intf->num_altsetting > 1) {
++ u32 interval;
++ u32 bandwidth;
++
++ interval = (ctrl->dwFrameInterval > 100000)
++ ? ctrl->dwFrameInterval
++ : frame->dwFrameInterval[0];
++
++ /* Compute a bandwidth estimation by multiplying the frame
++ * size by the number of video frames per second, divide the
++ * result by the number of USB frames (or micro-frames for
++ * high-speed devices) per second and add the UVC header size
++ * (assumed to be 12 bytes long).
++ */
++ bandwidth = frame->wWidth * frame->wHeight / 8 * format->bpp;
++ bandwidth *= 10000000 / interval + 1;
++ bandwidth /= 1000;
++ if (video->dev->udev->speed == USB_SPEED_HIGH)
++ bandwidth /= 8;
++ bandwidth += 12;
++
++ ctrl->dwMaxPayloadTransferSize = bandwidth;
++ }
+ }
+
+ static int uvc_get_video_ctrl(struct uvc_video_device *video,
+@@ -158,10 +183,11 @@ static int uvc_get_video_ctrl(struct uvc_video_device *video,
+ ctrl->bMaxVersion = 0;
+ }
+
+- /* Some broken devices return a null or wrong dwMaxVideoFrameSize.
+- * Try to get the value from the format and frame descriptors.
++ /* Some broken devices return null or wrong dwMaxVideoFrameSize and
++ * dwMaxPayloadTransferSize fields. Try to get the value from the
++ * format and frame descriptors.
+ */
+- uvc_fixup_buffer_size(video, ctrl);
++ uvc_fixup_video_ctrl(video, ctrl);
+ ret = 0;
+
+ out:
+@@ -540,6 +566,9 @@ static void uvc_video_decode_bulk(struct urb *urb,
+ u8 *mem;
+ int len, ret;
+
++ if (urb->actual_length == 0)
++ return;
++
+ mem = urb->transfer_buffer;
+ len = urb->actual_length;
+ video->bulk.payload_size += len;
+@@ -699,27 +728,47 @@ static void uvc_free_urb_buffers(struct uvc_video_device *video)
+ * already allocated when resuming from suspend, in which case it will
+ * return without touching the buffers.
+ *
+- * Return 0 on success or -ENOMEM when out of memory.
++ * Limit the buffer size to UVC_MAX_PACKETS bulk/isochronous packets. If the
++ * system is too low on memory try successively smaller numbers of packets
++ * until allocation succeeds.
++ *
++ * Return the number of allocated packets on success or 0 when out of memory.
+ */
+ static int uvc_alloc_urb_buffers(struct uvc_video_device *video,
+- unsigned int size)
++ unsigned int size, unsigned int psize, gfp_t gfp_flags)
+ {
++ unsigned int npackets;
+ unsigned int i;
+
+ /* Buffers are already allocated, bail out. */
+ if (video->urb_size)
+ return 0;
+
+- for (i = 0; i < UVC_URBS; ++i) {
+- video->urb_buffer[i] = usb_buffer_alloc(video->dev->udev,
+- size, GFP_KERNEL, &video->urb_dma[i]);
+- if (video->urb_buffer[i] == NULL) {
+- uvc_free_urb_buffers(video);
+- return -ENOMEM;
++ /* Compute the number of packets. Bulk endpoints might transfer UVC
++ * payloads accross multiple URBs.
++ */
++ npackets = DIV_ROUND_UP(size, psize);
++ if (npackets > UVC_MAX_PACKETS)
++ npackets = UVC_MAX_PACKETS;
++
++ /* Retry allocations until one succeed. */
++ for (; npackets > 1; npackets /= 2) {
++ for (i = 0; i < UVC_URBS; ++i) {
++ video->urb_buffer[i] = usb_buffer_alloc(
++ video->dev->udev, psize * npackets,
++ gfp_flags | __GFP_NOWARN, &video->urb_dma[i]);
++ if (!video->urb_buffer[i]) {
++ uvc_free_urb_buffers(video);
++ break;
++ }
++ }
++
++ if (i == UVC_URBS) {
++ video->urb_size = psize * npackets;
++ return npackets;
+ }
+ }
+
+- video->urb_size = size;
+ return 0;
+ }
+
+@@ -753,29 +802,19 @@ static int uvc_init_video_isoc(struct uvc_video_device *video,
+ {
+ struct urb *urb;
+ unsigned int npackets, i, j;
+- __u16 psize;
+- __u32 size;
++ u16 psize;
++ u32 size;
+
+- /* Compute the number of isochronous packets to allocate by dividing
+- * the maximum video frame size by the packet size. Limit the result
+- * to UVC_MAX_ISO_PACKETS.
+- */
+ psize = le16_to_cpu(ep->desc.wMaxPacketSize);
+ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3));
+-
+ size = video->streaming->ctrl.dwMaxVideoFrameSize;
+- if (size > UVC_MAX_FRAME_SIZE)
+- return -EINVAL;
+
+- npackets = DIV_ROUND_UP(size, psize);
+- if (npackets > UVC_MAX_ISO_PACKETS)
+- npackets = UVC_MAX_ISO_PACKETS;
++ npackets = uvc_alloc_urb_buffers(video, size, psize, gfp_flags);
++ if (npackets == 0)
++ return -ENOMEM;
+
+ size = npackets * psize;
+
+- if (uvc_alloc_urb_buffers(video, size) < 0)
+- return -ENOMEM;
+-
+ for (i = 0; i < UVC_URBS; ++i) {
+ urb = usb_alloc_urb(npackets, gfp_flags);
+ if (urb == NULL) {
+@@ -814,25 +853,20 @@ static int uvc_init_video_bulk(struct uvc_video_device *video,
+ struct usb_host_endpoint *ep, gfp_t gfp_flags)
+ {
+ struct urb *urb;
+- unsigned int pipe, i;
+- __u16 psize;
+- __u32 size;
+-
+- /* Compute the bulk URB size. Some devices set the maximum payload
+- * size to a value too high for memory-constrained devices. We must
+- * then transfer the payload accross multiple URBs. To be consistant
+- * with isochronous mode, allocate maximum UVC_MAX_ISO_PACKETS per bulk
+- * URB.
+- */
++ unsigned int npackets, pipe, i;
++ u16 psize;
++ u32 size;
++
+ psize = le16_to_cpu(ep->desc.wMaxPacketSize) & 0x07ff;
+ size = video->streaming->ctrl.dwMaxPayloadTransferSize;
+ video->bulk.max_payload_size = size;
+- if (size > psize * UVC_MAX_ISO_PACKETS)
+- size = psize * UVC_MAX_ISO_PACKETS;
+
+- if (uvc_alloc_urb_buffers(video, size) < 0)
++ npackets = uvc_alloc_urb_buffers(video, size, psize, gfp_flags);
++ if (npackets == 0)
+ return -ENOMEM;
+
++ size = npackets * psize;
++
+ if (usb_endpoint_dir_in(&ep->desc))
+ pipe = usb_rcvbulkpipe(video->dev->udev,
+ ep->desc.bEndpointAddress);
+@@ -1021,11 +1055,20 @@ int uvc_video_init(struct uvc_video_device *video)
+ */
+ usb_set_interface(video->dev->udev, video->streaming->intfnum, 0);
+
+- /* Some webcams don't suport GET_DEF requests on the probe control. We
+- * fall back to GET_CUR if GET_DEF fails.
++ /* Set the streaming probe control with default streaming parameters
++ * retrieved from the device. Webcams that don't suport GET_DEF
++ * requests on the probe control will just keep their current streaming
++ * parameters.
++ */
++ if (uvc_get_video_ctrl(video, probe, 1, GET_DEF) == 0)
++ uvc_set_video_ctrl(video, probe, 1);
++
++ /* Initialize the streaming parameters with the probe control current
++ * value. This makes sure SET_CUR requests on the streaming commit
++ * control will always use values retrieved from a successful GET_CUR
++ * request on the probe control, as required by the UVC specification.
+ */
+- if ((ret = uvc_get_video_ctrl(video, probe, 1, GET_DEF)) < 0 &&
+- (ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0)
++ if ((ret = uvc_get_video_ctrl(video, probe, 1, GET_CUR)) < 0)
+ return ret;
+
+ /* Check if the default format descriptor exists. Use the first
+diff --git a/drivers/media/video/uvc/uvcvideo.h b/drivers/media/video/uvc/uvcvideo.h
+index 027947e..e5014e6 100644
+--- a/drivers/media/video/uvc/uvcvideo.h
++++ b/drivers/media/video/uvc/uvcvideo.h
+@@ -296,10 +296,8 @@ struct uvc_xu_control {
+
+ /* Number of isochronous URBs. */
+ #define UVC_URBS 5
+-/* Maximum number of packets per isochronous URB. */
+-#define UVC_MAX_ISO_PACKETS 40
+-/* Maximum frame size in bytes, for sanity checking. */
+-#define UVC_MAX_FRAME_SIZE (16*1024*1024)
++/* Maximum number of packets per URB. */
++#define UVC_MAX_PACKETS 32
+ /* Maximum number of video buffers. */
+ #define UVC_MAX_VIDEO_BUFFERS 32
+ /* Maximum status buffer size in bytes of interrupt URB. */
+@@ -316,6 +314,7 @@ struct uvc_xu_control {
+ #define UVC_QUIRK_STREAM_NO_FID 0x00000010
+ #define UVC_QUIRK_IGNORE_SELECTOR_UNIT 0x00000020
+ #define UVC_QUIRK_PRUNE_CONTROLS 0x00000040
++#define UVC_QUIRK_FIX_BANDWIDTH 0x00000080
+
+ /* Format flags */
+ #define UVC_FMT_FLAG_COMPRESSED 0x00000001
+@@ -649,6 +648,7 @@ struct uvc_device {
+ struct urb *int_urb;
+ __u8 *status;
+ struct input_dev *input;
++ char input_phys[64];
+
+ /* Video Streaming interfaces */
+ struct list_head streaming;
+diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
+index b8f2be8..1da8cb8 100644
+--- a/drivers/media/video/v4l2-common.c
++++ b/drivers/media/video/v4l2-common.c
+@@ -334,6 +334,12 @@ const char **v4l2_ctrl_get_menu(u32 id)
+ "Aperture Priority Mode",
+ NULL
+ };
++ static const char *colorfx[] = {
++ "None",
++ "Black & White",
++ "Sepia",
++ NULL
++ };
+
+ switch (id) {
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+@@ -370,6 +376,8 @@ const char **v4l2_ctrl_get_menu(u32 id)
+ return camera_power_line_frequency;
+ case V4L2_CID_EXPOSURE_AUTO:
+ return camera_exposure_auto;
++ case V4L2_CID_COLORFX:
++ return colorfx;
+ default:
+ return NULL;
+ }
+@@ -382,16 +390,16 @@ const char *v4l2_ctrl_get_name(u32 id)
+ switch (id) {
+ /* USER controls */
+ case V4L2_CID_USER_CLASS: return "User Controls";
++ case V4L2_CID_BRIGHTNESS: return "Brightness";
++ case V4L2_CID_CONTRAST: return "Contrast";
++ case V4L2_CID_SATURATION: return "Saturation";
++ case V4L2_CID_HUE: return "Hue";
+ case V4L2_CID_AUDIO_VOLUME: return "Volume";
+- case V4L2_CID_AUDIO_MUTE: return "Mute";
+ case V4L2_CID_AUDIO_BALANCE: return "Balance";
+ case V4L2_CID_AUDIO_BASS: return "Bass";
+ case V4L2_CID_AUDIO_TREBLE: return "Treble";
++ case V4L2_CID_AUDIO_MUTE: return "Mute";
+ case V4L2_CID_AUDIO_LOUDNESS: return "Loudness";
+- case V4L2_CID_BRIGHTNESS: return "Brightness";
+- case V4L2_CID_CONTRAST: return "Contrast";
+- case V4L2_CID_SATURATION: return "Saturation";
+- case V4L2_CID_HUE: return "Hue";
+ case V4L2_CID_BLACK_LEVEL: return "Black Level";
+ case V4L2_CID_AUTO_WHITE_BALANCE: return "White Balance, Automatic";
+ case V4L2_CID_DO_WHITE_BALANCE: return "Do White Balance";
+@@ -412,6 +420,7 @@ const char *v4l2_ctrl_get_name(u32 id)
+ case V4L2_CID_BACKLIGHT_COMPENSATION: return "Backlight Compensation";
+ case V4L2_CID_CHROMA_AGC: return "Chroma AGC";
+ case V4L2_CID_COLOR_KILLER: return "Color Killer";
++ case V4L2_CID_COLORFX: return "Color Effects";
+
+ /* MPEG controls */
+ case V4L2_CID_MPEG_CLASS: return "MPEG Encoder Controls";
+@@ -490,16 +499,25 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste
+ case V4L2_CID_HFLIP:
+ case V4L2_CID_VFLIP:
+ case V4L2_CID_HUE_AUTO:
++ case V4L2_CID_CHROMA_AGC:
++ case V4L2_CID_COLOR_KILLER:
+ case V4L2_CID_MPEG_AUDIO_MUTE:
+ case V4L2_CID_MPEG_VIDEO_MUTE:
+ case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+ case V4L2_CID_MPEG_VIDEO_PULLDOWN:
+ case V4L2_CID_EXPOSURE_AUTO_PRIORITY:
++ case V4L2_CID_FOCUS_AUTO:
+ case V4L2_CID_PRIVACY:
+ qctrl->type = V4L2_CTRL_TYPE_BOOLEAN;
+ min = 0;
+ max = step = 1;
+ break;
++ case V4L2_CID_PAN_RESET:
++ case V4L2_CID_TILT_RESET:
++ qctrl->type = V4L2_CTRL_TYPE_BUTTON;
++ qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
++ min = max = step = def = 0;
++ break;
+ case V4L2_CID_POWER_LINE_FREQUENCY:
+ case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+ case V4L2_CID_MPEG_AUDIO_ENCODING:
+@@ -517,6 +535,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste
+ case V4L2_CID_MPEG_STREAM_TYPE:
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ case V4L2_CID_EXPOSURE_AUTO:
++ case V4L2_CID_COLORFX:
+ qctrl->type = V4L2_CTRL_TYPE_MENU;
+ step = 1;
+ break;
+@@ -547,161 +566,29 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste
+ case V4L2_CID_CONTRAST:
+ case V4L2_CID_SATURATION:
+ case V4L2_CID_HUE:
++ case V4L2_CID_RED_BALANCE:
++ case V4L2_CID_BLUE_BALANCE:
++ case V4L2_CID_GAMMA:
++ case V4L2_CID_SHARPNESS:
+ qctrl->flags |= V4L2_CTRL_FLAG_SLIDER;
+ break;
++ case V4L2_CID_PAN_RELATIVE:
++ case V4L2_CID_TILT_RELATIVE:
++ case V4L2_CID_FOCUS_RELATIVE:
++ case V4L2_CID_ZOOM_RELATIVE:
++ qctrl->flags |= V4L2_CTRL_FLAG_WRITE_ONLY;
++ break;
+ }
+ qctrl->minimum = min;
+ qctrl->maximum = max;
+ qctrl->step = step;
+ qctrl->default_value = def;
+ qctrl->reserved[0] = qctrl->reserved[1] = 0;
+- snprintf(qctrl->name, sizeof(qctrl->name), name);
++ strlcpy(qctrl->name, name, sizeof(qctrl->name));
+ return 0;
+ }
+ EXPORT_SYMBOL(v4l2_ctrl_query_fill);
+
+-/* Fill in a struct v4l2_queryctrl with standard values based on
+- the control ID. */
+-int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl)
+-{
+- switch (qctrl->id) {
+- /* USER controls */
+- case V4L2_CID_USER_CLASS:
+- case V4L2_CID_MPEG_CLASS:
+- return v4l2_ctrl_query_fill(qctrl, 0, 0, 0, 0);
+- case V4L2_CID_AUDIO_VOLUME:
+- return v4l2_ctrl_query_fill(qctrl, 0, 65535, 65535 / 100, 58880);
+- case V4L2_CID_AUDIO_MUTE:
+- case V4L2_CID_AUDIO_LOUDNESS:
+- return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0);
+- case V4L2_CID_AUDIO_BALANCE:
+- case V4L2_CID_AUDIO_BASS:
+- case V4L2_CID_AUDIO_TREBLE:
+- return v4l2_ctrl_query_fill(qctrl, 0, 65535, 65535 / 100, 32768);
+- case V4L2_CID_BRIGHTNESS:
+- return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 128);
+- case V4L2_CID_CONTRAST:
+- case V4L2_CID_SATURATION:
+- return v4l2_ctrl_query_fill(qctrl, 0, 127, 1, 64);
+- case V4L2_CID_HUE:
+- return v4l2_ctrl_query_fill(qctrl, -128, 127, 1, 0);
+-
+- /* MPEG controls */
+- case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
+- return v4l2_ctrl_query_fill(qctrl,
+- V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100,
+- V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000, 1,
+- V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000);
+- case V4L2_CID_MPEG_AUDIO_ENCODING:
+- return v4l2_ctrl_query_fill(qctrl,
+- V4L2_MPEG_AUDIO_ENCODING_LAYER_1,
+- V4L2_MPEG_AUDIO_ENCODING_AC3, 1,
+- V4L2_MPEG_AUDIO_ENCODING_LAYER_2);
+- case V4L2_CID_MPEG_AUDIO_L1_BITRATE:
+- return v4l2_ctrl_query_fill(qctrl,
+- V4L2_MPEG_AUDIO_L1_BITRATE_32K,
+- V4L2_MPEG_AUDIO_L1_BITRATE_448K, 1,
+- V4L2_MPEG_AUDIO_L1_BITRATE_256K);
+- case V4L2_CID_MPEG_AUDIO_L2_BITRATE:
+- return v4l2_ctrl_query_fill(qctrl,
+- V4L2_MPEG_AUDIO_L2_BITRATE_32K,
+- V4L2_MPEG_AUDIO_L2_BITRATE_384K, 1,
+- V4L2_MPEG_AUDIO_L2_BITRATE_224K);
+- case V4L2_CID_MPEG_AUDIO_L3_BITRATE:
+- return v4l2_ctrl_query_fill(qctrl,
+- V4L2_MPEG_AUDIO_L3_BITRATE_32K,
+- V4L2_MPEG_AUDIO_L3_BITRATE_320K, 1,
+- V4L2_MPEG_AUDIO_L3_BITRATE_192K);
+- case V4L2_CID_MPEG_AUDIO_AAC_BITRATE:
+- return v4l2_ctrl_query_fill(qctrl, 0, 6400, 1, 3200000);
+- case V4L2_CID_MPEG_AUDIO_AC3_BITRATE:
+- return v4l2_ctrl_query_fill(qctrl,
+- V4L2_MPEG_AUDIO_AC3_BITRATE_32K,
+- V4L2_MPEG_AUDIO_AC3_BITRATE_640K, 1,
+- V4L2_MPEG_AUDIO_AC3_BITRATE_384K);
+- case V4L2_CID_MPEG_AUDIO_MODE:
+- return v4l2_ctrl_query_fill(qctrl,
+- V4L2_MPEG_AUDIO_MODE_STEREO,
+- V4L2_MPEG_AUDIO_MODE_MONO, 1,
+- V4L2_MPEG_AUDIO_MODE_STEREO);
+- case V4L2_CID_MPEG_AUDIO_MODE_EXTENSION:
+- return v4l2_ctrl_query_fill(qctrl,
+- V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4,
+- V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_16, 1,
+- V4L2_MPEG_AUDIO_MODE_EXTENSION_BOUND_4);
+- case V4L2_CID_MPEG_AUDIO_EMPHASIS:
+- return v4l2_ctrl_query_fill(qctrl,
+- V4L2_MPEG_AUDIO_EMPHASIS_NONE,
+- V4L2_MPEG_AUDIO_EMPHASIS_CCITT_J17, 1,
+- V4L2_MPEG_AUDIO_EMPHASIS_NONE);
+- case V4L2_CID_MPEG_AUDIO_CRC:
+- return v4l2_ctrl_query_fill(qctrl,
+- V4L2_MPEG_AUDIO_CRC_NONE,
+- V4L2_MPEG_AUDIO_CRC_CRC16, 1,
+- V4L2_MPEG_AUDIO_CRC_NONE);
+- case V4L2_CID_MPEG_AUDIO_MUTE:
+- return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0);
+- case V4L2_CID_MPEG_VIDEO_ENCODING:
+- return v4l2_ctrl_query_fill(qctrl,
+- V4L2_MPEG_VIDEO_ENCODING_MPEG_1,
+- V4L2_MPEG_VIDEO_ENCODING_MPEG_4_AVC, 1,
+- V4L2_MPEG_VIDEO_ENCODING_MPEG_2);
+- case V4L2_CID_MPEG_VIDEO_ASPECT:
+- return v4l2_ctrl_query_fill(qctrl,
+- V4L2_MPEG_VIDEO_ASPECT_1x1,
+- V4L2_MPEG_VIDEO_ASPECT_221x100, 1,
+- V4L2_MPEG_VIDEO_ASPECT_4x3);
+- case V4L2_CID_MPEG_VIDEO_B_FRAMES:
+- return v4l2_ctrl_query_fill(qctrl, 0, 33, 1, 2);
+- case V4L2_CID_MPEG_VIDEO_GOP_SIZE:
+- return v4l2_ctrl_query_fill(qctrl, 1, 34, 1, 12);
+- case V4L2_CID_MPEG_VIDEO_GOP_CLOSURE:
+- return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 1);
+- case V4L2_CID_MPEG_VIDEO_PULLDOWN:
+- return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0);
+- case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
+- return v4l2_ctrl_query_fill(qctrl,
+- V4L2_MPEG_VIDEO_BITRATE_MODE_VBR,
+- V4L2_MPEG_VIDEO_BITRATE_MODE_CBR, 1,
+- V4L2_MPEG_VIDEO_BITRATE_MODE_VBR);
+- case V4L2_CID_MPEG_VIDEO_BITRATE:
+- return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 6000000);
+- case V4L2_CID_MPEG_VIDEO_BITRATE_PEAK:
+- return v4l2_ctrl_query_fill(qctrl, 0, 27000000, 1, 8000000);
+- case V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION:
+- return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0);
+- case V4L2_CID_MPEG_VIDEO_MUTE:
+- return v4l2_ctrl_query_fill(qctrl, 0, 1, 1, 0);
+- case V4L2_CID_MPEG_VIDEO_MUTE_YUV: /* Init YUV (really YCbCr) to black */
+- return v4l2_ctrl_query_fill(qctrl, 0, 0xffffff, 1, 0x008080);
+- case V4L2_CID_MPEG_STREAM_TYPE:
+- return v4l2_ctrl_query_fill(qctrl,
+- V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
+- V4L2_MPEG_STREAM_TYPE_MPEG2_SVCD, 1,
+- V4L2_MPEG_STREAM_TYPE_MPEG2_PS);
+- case V4L2_CID_MPEG_STREAM_PID_PMT:
+- return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 16);
+- case V4L2_CID_MPEG_STREAM_PID_AUDIO:
+- return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 260);
+- case V4L2_CID_MPEG_STREAM_PID_VIDEO:
+- return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 256);
+- case V4L2_CID_MPEG_STREAM_PID_PCR:
+- return v4l2_ctrl_query_fill(qctrl, 0, (1 << 14) - 1, 1, 259);
+- case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO:
+- return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0);
+- case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO:
+- return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0);
+- case V4L2_CID_MPEG_STREAM_VBI_FMT:
+- return v4l2_ctrl_query_fill(qctrl,
+- V4L2_MPEG_STREAM_VBI_FMT_NONE,
+- V4L2_MPEG_STREAM_VBI_FMT_IVTV, 1,
+- V4L2_MPEG_STREAM_VBI_FMT_NONE);
+- default:
+- return -EINVAL;
+- }
+-}
+-EXPORT_SYMBOL(v4l2_ctrl_query_fill_std);
+-
+ /* Fill in a struct v4l2_querymenu based on the struct v4l2_queryctrl and
+ the menu. The qctrl pointer may be NULL, in which case it is ignored.
+ If menu_items is NULL, then the menu items are retrieved using
+@@ -720,7 +607,7 @@ int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu, struct v4l2_queryctrl *qc
+ for (i = 0; i < qmenu->index && menu_items[i]; i++) ;
+ if (menu_items[i] == NULL || menu_items[i][0] == '\0')
+ return -EINVAL;
+- snprintf(qmenu->name, sizeof(qmenu->name), menu_items[qmenu->index]);
++ strlcpy(qmenu->name, menu_items[qmenu->index], sizeof(qmenu->name));
+ return 0;
+ }
+ EXPORT_SYMBOL(v4l2_ctrl_query_menu);
+@@ -737,8 +624,8 @@ int v4l2_ctrl_query_menu_valid_items(struct v4l2_querymenu *qmenu, const u32 *id
+ return -EINVAL;
+ while (*ids != V4L2_CTRL_MENU_IDS_END) {
+ if (*ids++ == qmenu->index) {
+- snprintf(qmenu->name, sizeof(qmenu->name),
+- menu_items[qmenu->index]);
++ strlcpy(qmenu->name, menu_items[qmenu->index],
++ sizeof(qmenu->name));
+ return 0;
+ }
+ }
+@@ -749,7 +636,7 @@ EXPORT_SYMBOL(v4l2_ctrl_query_menu_valid_items);
+ /* ctrl_classes points to an array of u32 pointers, the last element is
+ a NULL pointer. Each u32 array is a 0-terminated array of control IDs.
+ Each array must be sorted low to high and belong to the same control
+- class. The array of u32 pointer must also be sorted, from low class IDs
++ class. The array of u32 pointers must also be sorted, from low class IDs
+ to high class IDs.
+
+ This function returns the first ID that follows after the given ID.
+@@ -910,10 +797,10 @@ struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter,
+ struct i2c_board_info info;
+
+ BUG_ON(!dev);
+-#ifdef MODULE
++
+ if (module_name)
+ request_module(module_name);
+-#endif
++
+ /* Setup the i2c board info with the device type and
+ the device address. */
+ memset(&info, 0, sizeof(info));
+@@ -927,11 +814,11 @@ struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter,
+ We need better support from the kernel so that we
+ can easily wait for the load to finish. */
+ if (client == NULL || client->driver == NULL)
+- return NULL;
++ goto error;
+
+ /* Lock the module so we can safely get the v4l2_subdev pointer */
+ if (!try_module_get(client->driver->driver.owner))
+- return NULL;
++ goto error;
+ sd = i2c_get_clientdata(client);
+
+ /* Register with the v4l2_device which increases the module's
+@@ -940,8 +827,13 @@ struct v4l2_subdev *v4l2_i2c_new_subdev(struct i2c_adapter *adapter,
+ sd = NULL;
+ /* Decrease the module use count to match the first try_module_get. */
+ module_put(client->driver->driver.owner);
+- return sd;
+
++error:
++ /* If we have a client but no subdev, then something went wrong and
++ we must unregister the client. */
++ if (client && sd == NULL)
++ i2c_unregister_device(client);
++ return sd;
+ }
+ EXPORT_SYMBOL_GPL(v4l2_i2c_new_subdev);
+
+@@ -958,10 +850,10 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter,
+ struct i2c_board_info info;
+
+ BUG_ON(!dev);
+-#ifdef MODULE
++
+ if (module_name)
+ request_module(module_name);
+-#endif
++
+ /* Setup the i2c board info with the device type and
+ the device address. */
+ memset(&info, 0, sizeof(info));
+@@ -974,11 +866,11 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter,
+ We need better support from the kernel so that we
+ can easily wait for the load to finish. */
+ if (client == NULL || client->driver == NULL)
+- return NULL;
++ goto error;
+
+ /* Lock the module so we can safely get the v4l2_subdev pointer */
+ if (!try_module_get(client->driver->driver.owner))
+- return NULL;
++ goto error;
+ sd = i2c_get_clientdata(client);
+
+ /* Register with the v4l2_device which increases the module's
+@@ -987,8 +879,59 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter,
+ sd = NULL;
+ /* Decrease the module use count to match the first try_module_get. */
+ module_put(client->driver->driver.owner);
++
++error:
++ /* If we have a client but no subdev, then something went wrong and
++ we must unregister the client. */
++ if (client && sd == NULL)
++ i2c_unregister_device(client);
+ return sd;
+ }
+ EXPORT_SYMBOL_GPL(v4l2_i2c_new_probed_subdev);
+
++/* Return i2c client address of v4l2_subdev. */
++unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd)
++{
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
++ return client ? client->addr : I2C_CLIENT_END;
++}
++EXPORT_SYMBOL_GPL(v4l2_i2c_subdev_addr);
++
++/* Return a list of I2C tuner addresses to probe. Use only if the tuner
++ addresses are unknown. */
++const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type)
++{
++ static const unsigned short radio_addrs[] = {
++#if defined(CONFIG_MEDIA_TUNER_TEA5761) || defined(CONFIG_MEDIA_TUNER_TEA5761_MODULE)
++ 0x10,
++#endif
++ 0x60,
++ I2C_CLIENT_END
++ };
++ static const unsigned short demod_addrs[] = {
++ 0x42, 0x43, 0x4a, 0x4b,
++ I2C_CLIENT_END
++ };
++ static const unsigned short tv_addrs[] = {
++ 0x42, 0x43, 0x4a, 0x4b, /* tda8290 */
++ 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
++ 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
++ I2C_CLIENT_END
++ };
++
++ switch (type) {
++ case ADDRS_RADIO:
++ return radio_addrs;
++ case ADDRS_DEMOD:
++ return demod_addrs;
++ case ADDRS_TV:
++ return tv_addrs;
++ case ADDRS_TV_WITH_DEMOD:
++ return tv_addrs + 4;
++ }
++ return NULL;
++}
++EXPORT_SYMBOL_GPL(v4l2_i2c_tuner_addrs);
++
+ #endif
+diff --git a/drivers/media/video/v4l2-compat-ioctl32.c b/drivers/media/video/v4l2-compat-ioctl32.c
+index 110376b..0056b11 100644
+--- a/drivers/media/video/v4l2-compat-ioctl32.c
++++ b/drivers/media/video/v4l2-compat-ioctl32.c
+@@ -1047,7 +1047,6 @@ long v4l2_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
+ case VIDIOC_DBG_S_REGISTER:
+ case VIDIOC_DBG_G_REGISTER:
+ case VIDIOC_DBG_G_CHIP_IDENT:
+- case VIDIOC_G_CHIP_IDENT_OLD:
+ case VIDIOC_S_HW_FREQ_SEEK:
+ ret = do_video_ioctl(file, cmd, arg);
+ break;
+diff --git a/drivers/media/video/v4l2-dev.c b/drivers/media/video/v4l2-dev.c
+index 13f87c2..91228b3 100644
+--- a/drivers/media/video/v4l2-dev.c
++++ b/drivers/media/video/v4l2-dev.c
+@@ -198,6 +198,23 @@ static long v4l2_unlocked_ioctl(struct file *filp,
+ return vdev->fops->unlocked_ioctl(filp, cmd, arg);
+ }
+
++#ifdef CONFIG_MMU
++#define v4l2_get_unmapped_area NULL
++#else
++static unsigned long v4l2_get_unmapped_area(struct file *filp,
++ unsigned long addr, unsigned long len, unsigned long pgoff,
++ unsigned long flags)
++{
++ struct video_device *vdev = video_devdata(filp);
++
++ if (!vdev->fops->get_unmapped_area)
++ return -ENOSYS;
++ if (video_is_unregistered(vdev))
++ return -ENODEV;
++ return vdev->fops->get_unmapped_area(filp, addr, len, pgoff, flags);
++}
++#endif
++
+ static int v4l2_mmap(struct file *filp, struct vm_area_struct *vm)
+ {
+ struct video_device *vdev = video_devdata(filp);
+@@ -250,6 +267,7 @@ static const struct file_operations v4l2_unlocked_fops = {
+ .read = v4l2_read,
+ .write = v4l2_write,
+ .open = v4l2_open,
++ .get_unmapped_area = v4l2_get_unmapped_area,
+ .mmap = v4l2_mmap,
+ .unlocked_ioctl = v4l2_unlocked_ioctl,
+ #ifdef CONFIG_COMPAT
+@@ -265,6 +283,7 @@ static const struct file_operations v4l2_fops = {
+ .read = v4l2_read,
+ .write = v4l2_write,
+ .open = v4l2_open,
++ .get_unmapped_area = v4l2_get_unmapped_area,
+ .mmap = v4l2_mmap,
+ .ioctl = v4l2_ioctl,
+ #ifdef CONFIG_COMPAT
+@@ -288,37 +307,38 @@ static const struct file_operations v4l2_fops = {
+ */
+ static int get_index(struct video_device *vdev, int num)
+ {
+- u32 used = 0;
+- const int max_index = sizeof(used) * 8 - 1;
++ /* This can be static since this function is called with the global
++ videodev_lock held. */
++ static DECLARE_BITMAP(used, VIDEO_NUM_DEVICES);
+ int i;
+
+- /* Currently a single v4l driver instance cannot create more than
+- 32 devices.
+- Increase to u64 or an array of u32 if more are needed. */
+- if (num > max_index) {
++ if (num >= VIDEO_NUM_DEVICES) {
+ printk(KERN_ERR "videodev: %s num is too large\n", __func__);
+ return -EINVAL;
+ }
+
+- /* Some drivers do not set the parent. In that case always return 0. */
++ /* Some drivers do not set the parent. In that case always return
++ num or 0. */
+ if (vdev->parent == NULL)
+- return 0;
++ return num >= 0 ? num : 0;
++
++ bitmap_zero(used, VIDEO_NUM_DEVICES);
+
+ for (i = 0; i < VIDEO_NUM_DEVICES; i++) {
+ if (video_device[i] != NULL &&
+ video_device[i]->parent == vdev->parent) {
+- used |= 1 << video_device[i]->index;
++ set_bit(video_device[i]->index, used);
+ }
+ }
+
+ if (num >= 0) {
+- if (used & (1 << num))
++ if (test_bit(num, used))
+ return -ENFILE;
+ return num;
+ }
+
+- i = ffz(used);
+- return i > max_index ? -ENFILE : i;
++ i = find_first_zero_bit(used, VIDEO_NUM_DEVICES);
++ return i == VIDEO_NUM_DEVICES ? -ENFILE : i;
+ }
+
+ int video_register_device(struct video_device *vdev, int type, int nr)
+@@ -365,12 +385,11 @@ int video_register_device_index(struct video_device *vdev, int type, int nr,
+
+ /* A minor value of -1 marks this video device as never
+ having been registered */
+- if (vdev)
+- vdev->minor = -1;
++ vdev->minor = -1;
+
+ /* the release callback MUST be present */
+- WARN_ON(!vdev || !vdev->release);
+- if (!vdev || !vdev->release)
++ WARN_ON(!vdev->release);
++ if (!vdev->release)
+ return -EINVAL;
+
+ /* Part 1: check device type */
+@@ -395,7 +414,7 @@ int video_register_device_index(struct video_device *vdev, int type, int nr,
+
+ vdev->vfl_type = type;
+ vdev->cdev = NULL;
+- if (vdev->v4l2_dev)
++ if (vdev->v4l2_dev && vdev->v4l2_dev->dev)
+ vdev->parent = vdev->v4l2_dev->dev;
+
+ /* Part 2: find a free minor, kernel number and device index. */
+@@ -582,6 +601,7 @@ module_exit(videodev_exit)
+ MODULE_AUTHOR("Alan Cox, Mauro Carvalho Chehab <mchehab@infradead.org>");
+ MODULE_DESCRIPTION("Device registrar for Video4Linux drivers v2");
+ MODULE_LICENSE("GPL");
++MODULE_ALIAS_CHARDEV_MAJOR(VIDEO_MAJOR);
+
+
+ /*
+diff --git a/drivers/media/video/v4l2-device.c b/drivers/media/video/v4l2-device.c
+index cf9d4c7..94aa485 100644
+--- a/drivers/media/video/v4l2-device.c
++++ b/drivers/media/video/v4l2-device.c
+@@ -26,48 +26,66 @@
+
+ int v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev)
+ {
+- if (dev == NULL || v4l2_dev == NULL)
++ if (v4l2_dev == NULL)
+ return -EINVAL;
+- /* Warn if we apparently re-register a device */
+- WARN_ON(dev_get_drvdata(dev) != NULL);
++
+ INIT_LIST_HEAD(&v4l2_dev->subdevs);
+ spin_lock_init(&v4l2_dev->lock);
+ v4l2_dev->dev = dev;
+- snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
+- dev->driver->name, dev->bus_id);
++ if (dev == NULL) {
++ /* If dev == NULL, then name must be filled in by the caller */
++ WARN_ON(!v4l2_dev->name[0]);
++ return 0;
++ }
++
++ /* Set name to driver name + device name if it is empty. */
++ if (!v4l2_dev->name[0])
++ snprintf(v4l2_dev->name, sizeof(v4l2_dev->name), "%s %s",
++ dev->driver->name, dev_name(dev));
++ if (dev_get_drvdata(dev))
++ v4l2_warn(v4l2_dev, "Non-NULL drvdata on register\n");
+ dev_set_drvdata(dev, v4l2_dev);
+ return 0;
+ }
+ EXPORT_SYMBOL_GPL(v4l2_device_register);
+
++void v4l2_device_disconnect(struct v4l2_device *v4l2_dev)
++{
++ if (v4l2_dev->dev) {
++ dev_set_drvdata(v4l2_dev->dev, NULL);
++ v4l2_dev->dev = NULL;
++ }
++}
++EXPORT_SYMBOL_GPL(v4l2_device_disconnect);
++
+ void v4l2_device_unregister(struct v4l2_device *v4l2_dev)
+ {
+ struct v4l2_subdev *sd, *next;
+
+- if (v4l2_dev == NULL || v4l2_dev->dev == NULL)
++ if (v4l2_dev == NULL)
+ return;
+- dev_set_drvdata(v4l2_dev->dev, NULL);
+- /* unregister subdevs */
++ v4l2_device_disconnect(v4l2_dev);
++
++ /* Unregister subdevs */
+ list_for_each_entry_safe(sd, next, &v4l2_dev->subdevs, list)
+ v4l2_device_unregister_subdev(sd);
+-
+- v4l2_dev->dev = NULL;
+ }
+ EXPORT_SYMBOL_GPL(v4l2_device_unregister);
+
+-int v4l2_device_register_subdev(struct v4l2_device *dev, struct v4l2_subdev *sd)
++int v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
++ struct v4l2_subdev *sd)
+ {
+ /* Check for valid input */
+- if (dev == NULL || sd == NULL || !sd->name[0])
++ if (v4l2_dev == NULL || sd == NULL || !sd->name[0])
+ return -EINVAL;
+ /* Warn if we apparently re-register a subdev */
+- WARN_ON(sd->dev != NULL);
++ WARN_ON(sd->v4l2_dev != NULL);
+ if (!try_module_get(sd->owner))
+ return -ENODEV;
+- sd->dev = dev;
+- spin_lock(&dev->lock);
+- list_add_tail(&sd->list, &dev->subdevs);
+- spin_unlock(&dev->lock);
++ sd->v4l2_dev = v4l2_dev;
++ spin_lock(&v4l2_dev->lock);
++ list_add_tail(&sd->list, &v4l2_dev->subdevs);
++ spin_unlock(&v4l2_dev->lock);
+ return 0;
+ }
+ EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);
+@@ -75,12 +93,12 @@ EXPORT_SYMBOL_GPL(v4l2_device_register_subdev);
+ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd)
+ {
+ /* return if it isn't registered */
+- if (sd == NULL || sd->dev == NULL)
++ if (sd == NULL || sd->v4l2_dev == NULL)
+ return;
+- spin_lock(&sd->dev->lock);
++ spin_lock(&sd->v4l2_dev->lock);
+ list_del(&sd->list);
+- spin_unlock(&sd->dev->lock);
+- sd->dev = NULL;
++ spin_unlock(&sd->v4l2_dev->lock);
++ sd->v4l2_dev = NULL;
+ module_put(sd->owner);
+ }
+ EXPORT_SYMBOL_GPL(v4l2_device_unregister_subdev);
+diff --git a/drivers/media/video/v4l2-ioctl.c b/drivers/media/video/v4l2-ioctl.c
+index 52d687b..f41c6f5 100644
+--- a/drivers/media/video/v4l2-ioctl.c
++++ b/drivers/media/video/v4l2-ioctl.c
+@@ -17,6 +17,7 @@
+ #include <linux/kernel.h>
+
+ #define __OLD_VIDIOC_ /* To allow fixing old calls */
++#include <linux/videodev.h>
+ #include <linux/videodev2.h>
+
+ #ifdef CONFIG_VIDEO_V4L1
+@@ -24,7 +25,7 @@
+ #endif
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-ioctl.h>
+-#include <linux/video_decoder.h>
++#include <media/v4l2-chip-ident.h>
+
+ #define dbgarg(cmd, fmt, arg...) \
+ do { \
+@@ -100,25 +101,27 @@ const char *v4l2_norm_to_name(v4l2_std_id id)
+ }
+ EXPORT_SYMBOL(v4l2_norm_to_name);
+
++/* Returns frame period for the given standard */
++void v4l2_video_std_frame_period(int id, struct v4l2_fract *frameperiod)
++{
++ if (id & V4L2_STD_525_60) {
++ frameperiod->numerator = 1001;
++ frameperiod->denominator = 30000;
++ } else {
++ frameperiod->numerator = 1;
++ frameperiod->denominator = 25;
++ }
++}
++EXPORT_SYMBOL(v4l2_video_std_frame_period);
++
+ /* Fill in the fields of a v4l2_standard structure according to the
+ 'id' and 'transmission' parameters. Returns negative on error. */
+ int v4l2_video_std_construct(struct v4l2_standard *vs,
+ int id, const char *name)
+ {
+- u32 index = vs->index;
+-
+- memset(vs, 0, sizeof(struct v4l2_standard));
+- vs->index = index;
+- vs->id = id;
+- if (id & V4L2_STD_525_60) {
+- vs->frameperiod.numerator = 1001;
+- vs->frameperiod.denominator = 30000;
+- vs->framelines = 525;
+- } else {
+- vs->frameperiod.numerator = 1;
+- vs->frameperiod.denominator = 25;
+- vs->framelines = 625;
+- }
++ vs->id = id;
++ v4l2_video_std_frame_period(id, &vs->frameperiod);
++ vs->framelines = (id & V4L2_STD_525_60) ? 525 : 625;
+ strlcpy(vs->name, name, sizeof(vs->name));
+ return 0;
+ }
+@@ -273,19 +276,6 @@ static const char *v4l2_ioctls[] = {
+ #define V4L2_IOCTLS ARRAY_SIZE(v4l2_ioctls)
+
+ static const char *v4l2_int_ioctls[] = {
+-#ifdef CONFIG_VIDEO_V4L1_COMPAT
+- [_IOC_NR(DECODER_GET_CAPABILITIES)] = "DECODER_GET_CAPABILITIES",
+- [_IOC_NR(DECODER_GET_STATUS)] = "DECODER_GET_STATUS",
+- [_IOC_NR(DECODER_SET_NORM)] = "DECODER_SET_NORM",
+- [_IOC_NR(DECODER_SET_INPUT)] = "DECODER_SET_INPUT",
+- [_IOC_NR(DECODER_SET_OUTPUT)] = "DECODER_SET_OUTPUT",
+- [_IOC_NR(DECODER_ENABLE_OUTPUT)] = "DECODER_ENABLE_OUTPUT",
+- [_IOC_NR(DECODER_SET_PICTURE)] = "DECODER_SET_PICTURE",
+- [_IOC_NR(DECODER_SET_GPIO)] = "DECODER_SET_GPIO",
+- [_IOC_NR(DECODER_INIT)] = "DECODER_INIT",
+- [_IOC_NR(DECODER_SET_VBI_BYPASS)] = "DECODER_SET_VBI_BYPASS",
+- [_IOC_NR(DECODER_DUMP)] = "DECODER_DUMP",
+-#endif
+ [_IOC_NR(AUDC_SET_RADIO)] = "AUDC_SET_RADIO",
+
+ [_IOC_NR(TUNER_SET_TYPE_ADDR)] = "TUNER_SET_TYPE_ADDR",
+@@ -654,8 +644,6 @@ static long __video_do_ioctl(struct file *file,
+ if (cmd == VIDIOCGMBUF) {
+ struct video_mbuf *p = arg;
+
+- memset(p, 0, sizeof(*p));
+-
+ if (!ops->vidiocgmbuf)
+ return ret;
+ ret = ops->vidiocgmbuf(file, fh, p);
+@@ -682,7 +670,6 @@ static long __video_do_ioctl(struct file *file,
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = (struct v4l2_capability *)arg;
+- memset(cap, 0, sizeof(*cap));
+
+ if (!ops->vidioc_querycap)
+ break;
+@@ -725,16 +712,8 @@ static long __video_do_ioctl(struct file *file,
+ case VIDIOC_ENUM_FMT:
+ {
+ struct v4l2_fmtdesc *f = arg;
+- enum v4l2_buf_type type;
+- unsigned int index;
+-
+- index = f->index;
+- type = f->type;
+- memset(f, 0, sizeof(*f));
+- f->index = index;
+- f->type = type;
+
+- switch (type) {
++ switch (f->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ if (ops->vidioc_enum_fmt_vid_cap)
+ ret = ops->vidioc_enum_fmt_vid_cap(file, fh, f);
+@@ -771,8 +750,6 @@ static long __video_do_ioctl(struct file *file,
+ {
+ struct v4l2_format *f = (struct v4l2_format *)arg;
+
+- memset(f->fmt.raw_data, 0, sizeof(f->fmt.raw_data));
+-
+ /* FIXME: Should be one dump per type */
+ dbgarg(cmd, "type=%s\n", prt_names(f->type, v4l2_type_names));
+
+@@ -1088,7 +1065,6 @@ static long __video_do_ioctl(struct file *file,
+ return -EINVAL;
+
+ v4l2_video_std_construct(p, curr_id, descr);
+- p->index = index;
+
+ dbgarg(cmd, "index=%d, id=0x%Lx, name=%s, fps=%d/%d, "
+ "framelines=%d\n", p->index,
+@@ -1153,12 +1129,9 @@ static long __video_do_ioctl(struct file *file,
+ case VIDIOC_ENUMINPUT:
+ {
+ struct v4l2_input *p = arg;
+- int i = p->index;
+
+ if (!ops->vidioc_enum_input)
+ break;
+- memset(p, 0, sizeof(*p));
+- p->index = i;
+
+ ret = ops->vidioc_enum_input(file, fh, p);
+ if (!ret)
+@@ -1197,12 +1170,9 @@ static long __video_do_ioctl(struct file *file,
+ case VIDIOC_ENUMOUTPUT:
+ {
+ struct v4l2_output *p = arg;
+- int i = p->index;
+
+ if (!ops->vidioc_enum_output)
+ break;
+- memset(p, 0, sizeof(*p));
+- p->index = i;
+
+ ret = ops->vidioc_enum_output(file, fh, p);
+ if (!ret)
+@@ -1378,13 +1348,10 @@ static long __video_do_ioctl(struct file *file,
+ case VIDIOC_G_AUDIO:
+ {
+ struct v4l2_audio *p = arg;
+- __u32 index = p->index;
+
+ if (!ops->vidioc_g_audio)
+ break;
+
+- memset(p, 0, sizeof(*p));
+- p->index = index;
+ ret = ops->vidioc_g_audio(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "index=%d, name=%s, capability=0x%x, "
+@@ -1426,7 +1393,7 @@ static long __video_do_ioctl(struct file *file,
+
+ if (!ops->vidioc_g_audout)
+ break;
+- dbgarg(cmd, "Enum for index=%d\n", p->index);
++
+ ret = ops->vidioc_g_audout(file, fh, p);
+ if (!ret)
+ dbgarg2("index=%d, name=%s, capability=%d, "
+@@ -1479,15 +1446,10 @@ static long __video_do_ioctl(struct file *file,
+ case VIDIOC_G_CROP:
+ {
+ struct v4l2_crop *p = arg;
+- __u32 type;
+
+ if (!ops->vidioc_g_crop)
+ break;
+
+- type = p->type;
+- memset(p, 0, sizeof(*p));
+- p->type = type;
+-
+ dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
+ ret = ops->vidioc_g_crop(file, fh, p);
+ if (!ret)
+@@ -1508,16 +1470,11 @@ static long __video_do_ioctl(struct file *file,
+ case VIDIOC_CROPCAP:
+ {
+ struct v4l2_cropcap *p = arg;
+- __u32 type;
+
+ /*FIXME: Should also show v4l2_fract pixelaspect */
+ if (!ops->vidioc_cropcap)
+ break;
+
+- type = p->type;
+- memset(p, 0, sizeof(*p));
+- p->type = type;
+-
+ dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
+ ret = ops->vidioc_cropcap(file, fh, p);
+ if (!ret) {
+@@ -1533,8 +1490,6 @@ static long __video_do_ioctl(struct file *file,
+ if (!ops->vidioc_g_jpegcomp)
+ break;
+
+- memset(p, 0, sizeof(*p));
+-
+ ret = ops->vidioc_g_jpegcomp(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "quality=%d, APPn=%d, "
+@@ -1575,7 +1530,6 @@ static long __video_do_ioctl(struct file *file,
+
+ if (!ops->vidioc_encoder_cmd)
+ break;
+- memset(&p->raw, 0, sizeof(p->raw));
+ ret = ops->vidioc_encoder_cmd(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
+@@ -1587,7 +1541,6 @@ static long __video_do_ioctl(struct file *file,
+
+ if (!ops->vidioc_try_encoder_cmd)
+ break;
+- memset(&p->raw, 0, sizeof(p->raw));
+ ret = ops->vidioc_try_encoder_cmd(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "cmd=%d, flags=%x\n", p->cmd, p->flags);
+@@ -1596,23 +1549,18 @@ static long __video_do_ioctl(struct file *file,
+ case VIDIOC_G_PARM:
+ {
+ struct v4l2_streamparm *p = arg;
+- __u32 type = p->type;
+-
+- memset(p, 0, sizeof(*p));
+- p->type = type;
+
+ if (ops->vidioc_g_parm) {
++ ret = check_fmt(ops, p->type);
++ if (ret)
++ break;
+ ret = ops->vidioc_g_parm(file, fh, p);
+ } else {
+- struct v4l2_standard s;
+-
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+- v4l2_video_std_construct(&s, vfd->current_norm,
+- v4l2_norm_to_name(vfd->current_norm));
+-
+- p->parm.capture.timeperframe = s.frameperiod;
++ v4l2_video_std_frame_period(vfd->current_norm,
++ &p->parm.capture.timeperframe);
+ ret = 0;
+ }
+
+@@ -1625,6 +1573,10 @@ static long __video_do_ioctl(struct file *file,
+
+ if (!ops->vidioc_s_parm)
+ break;
++ ret = check_fmt(ops, p->type);
++ if (ret)
++ break;
++
+ dbgarg(cmd, "type=%d\n", p->type);
+ ret = ops->vidioc_s_parm(file, fh, p);
+ break;
+@@ -1632,14 +1584,10 @@ static long __video_do_ioctl(struct file *file,
+ case VIDIOC_G_TUNER:
+ {
+ struct v4l2_tuner *p = arg;
+- __u32 index = p->index;
+
+ if (!ops->vidioc_g_tuner)
+ break;
+
+- memset(p, 0, sizeof(*p));
+- p->index = index;
+-
+ ret = ops->vidioc_g_tuner(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "index=%d, name=%s, type=%d, "
+@@ -1676,8 +1624,6 @@ static long __video_do_ioctl(struct file *file,
+ if (!ops->vidioc_g_frequency)
+ break;
+
+- memset(p->reserved, 0, sizeof(p->reserved));
+-
+ ret = ops->vidioc_g_frequency(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "tuner=%d, type=%d, frequency=%d\n",
+@@ -1698,12 +1644,13 @@ static long __video_do_ioctl(struct file *file,
+ case VIDIOC_G_SLICED_VBI_CAP:
+ {
+ struct v4l2_sliced_vbi_cap *p = arg;
+- __u32 type = p->type;
+
+ if (!ops->vidioc_g_sliced_vbi_cap)
+ break;
+- memset(p, 0, sizeof(*p));
+- p->type = type;
++
++ /* Clear up to type, everything after type is zerod already */
++ memset(p, 0, offsetof(struct v4l2_sliced_vbi_cap, type));
++
+ dbgarg(cmd, "type=%s\n", prt_names(p->type, v4l2_type_names));
+ ret = ops->vidioc_g_sliced_vbi_cap(file, fh, p);
+ if (!ret)
+@@ -1745,16 +1692,13 @@ static long __video_do_ioctl(struct file *file,
+
+ if (!ops->vidioc_g_chip_ident)
+ break;
++ p->ident = V4L2_IDENT_NONE;
++ p->revision = 0;
+ ret = ops->vidioc_g_chip_ident(file, fh, p);
+ if (!ret)
+ dbgarg(cmd, "chip_ident=%u, revision=0x%x\n", p->ident, p->revision);
+ break;
+ }
+- case VIDIOC_G_CHIP_IDENT_OLD:
+- printk(KERN_ERR "VIDIOC_G_CHIP_IDENT has been deprecated and will disappear in 2.6.30.\n");
+- printk(KERN_ERR "It is a debugging ioctl and must not be used in applications!\n");
+- return -EINVAL;
+-
+ case VIDIOC_S_HW_FREQ_SEEK:
+ {
+ struct v4l2_hw_freq_seek *p = arg;
+@@ -1774,8 +1718,6 @@ static long __video_do_ioctl(struct file *file,
+ if (!ops->vidioc_enum_framesizes)
+ break;
+
+- memset(p, 0, sizeof(*p));
+-
+ ret = ops->vidioc_enum_framesizes(file, fh, p);
+ dbgarg(cmd,
+ "index=%d, pixelformat=%d, type=%d ",
+@@ -1807,8 +1749,6 @@ static long __video_do_ioctl(struct file *file,
+ if (!ops->vidioc_enum_frameintervals)
+ break;
+
+- memset(p, 0, sizeof(*p));
+-
+ ret = ops->vidioc_enum_frameintervals(file, fh, p);
+ dbgarg(cmd,
+ "index=%d, pixelformat=%d, width=%d, height=%d, type=%d ",
+@@ -1857,6 +1797,45 @@ static long __video_do_ioctl(struct file *file,
+ return ret;
+ }
+
++/* In some cases, only a few fields are used as input, i.e. when the app sets
++ * "index" and then the driver fills in the rest of the structure for the thing
++ * with that index. We only need to copy up the first non-input field. */
++static unsigned long cmd_input_size(unsigned int cmd)
++{
++ /* Size of structure up to and including 'field' */
++#define CMDINSIZE(cmd, type, field) \
++ case VIDIOC_##cmd: \
++ return offsetof(struct v4l2_##type, field) + \
++ sizeof(((struct v4l2_##type *)0)->field);
++
++ switch (cmd) {
++ CMDINSIZE(ENUM_FMT, fmtdesc, type);
++ CMDINSIZE(G_FMT, format, type);
++ CMDINSIZE(QUERYBUF, buffer, type);
++ CMDINSIZE(G_PARM, streamparm, type);
++ CMDINSIZE(ENUMSTD, standard, index);
++ CMDINSIZE(ENUMINPUT, input, index);
++ CMDINSIZE(G_CTRL, control, id);
++ CMDINSIZE(G_TUNER, tuner, index);
++ CMDINSIZE(QUERYCTRL, queryctrl, id);
++ CMDINSIZE(QUERYMENU, querymenu, index);
++ CMDINSIZE(ENUMOUTPUT, output, index);
++ CMDINSIZE(G_MODULATOR, modulator, index);
++ CMDINSIZE(G_FREQUENCY, frequency, tuner);
++ CMDINSIZE(CROPCAP, cropcap, type);
++ CMDINSIZE(G_CROP, crop, type);
++ CMDINSIZE(ENUMAUDIO, audio, index);
++ CMDINSIZE(ENUMAUDOUT, audioout, index);
++ CMDINSIZE(ENCODER_CMD, encoder_cmd, flags);
++ CMDINSIZE(TRY_ENCODER_CMD, encoder_cmd, flags);
++ CMDINSIZE(G_SLICED_VBI_CAP, sliced_vbi_cap, type);
++ CMDINSIZE(ENUM_FRAMESIZES, frmsizeenum, pixel_format);
++ CMDINSIZE(ENUM_FRAMEINTERVALS, frmivalenum, height);
++ default:
++ return _IOC_SIZE(cmd);
++ }
++}
++
+ long video_ioctl2(struct file *file,
+ unsigned int cmd, unsigned long arg)
+ {
+@@ -1875,13 +1854,7 @@ long video_ioctl2(struct file *file,
+ cmd == VIDIOC_TRY_EXT_CTRLS);
+
+ /* Copy arguments into temp kernel buffer */
+- switch (_IOC_DIR(cmd)) {
+- case _IOC_NONE:
+- parg = NULL;
+- break;
+- case _IOC_READ:
+- case _IOC_WRITE:
+- case (_IOC_WRITE | _IOC_READ):
++ if (_IOC_DIR(cmd) != _IOC_NONE) {
+ if (_IOC_SIZE(cmd) <= sizeof(sbuf)) {
+ parg = sbuf;
+ } else {
+@@ -1893,10 +1866,19 @@ long video_ioctl2(struct file *file,
+ }
+
+ err = -EFAULT;
+- if (_IOC_DIR(cmd) & _IOC_WRITE)
+- if (copy_from_user(parg, (void __user *)arg, _IOC_SIZE(cmd)))
++ if (_IOC_DIR(cmd) & _IOC_WRITE) {
++ unsigned long n = cmd_input_size(cmd);
++
++ if (copy_from_user(parg, (void __user *)arg, n))
+ goto out;
+- break;
++
++ /* zero out anything we don't copy from userspace */
++ if (n < _IOC_SIZE(cmd))
++ memset((u8 *)parg + n, 0, _IOC_SIZE(cmd) - n);
++ } else {
++ /* read-only ioctl */
++ memset(parg, 0, _IOC_SIZE(cmd));
++ }
+ }
+
+ if (is_ext_ctrl) {
+diff --git a/drivers/media/video/v4l2-subdev.c b/drivers/media/video/v4l2-subdev.c
+index 2120880..dc88167 100644
+--- a/drivers/media/video/v4l2-subdev.c
++++ b/drivers/media/video/v4l2-subdev.c
+@@ -33,6 +33,12 @@ int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg)
+ return v4l2_subdev_call(sd, core, g_ctrl, arg);
+ case VIDIOC_S_CTRL:
+ return v4l2_subdev_call(sd, core, s_ctrl, arg);
++ case VIDIOC_G_EXT_CTRLS:
++ return v4l2_subdev_call(sd, core, g_ext_ctrls, arg);
++ case VIDIOC_S_EXT_CTRLS:
++ return v4l2_subdev_call(sd, core, s_ext_ctrls, arg);
++ case VIDIOC_TRY_EXT_CTRLS:
++ return v4l2_subdev_call(sd, core, try_ext_ctrls, arg);
+ case VIDIOC_QUERYMENU:
+ return v4l2_subdev_call(sd, core, querymenu, arg);
+ case VIDIOC_LOG_STATUS:
+@@ -92,16 +98,28 @@ int v4l2_subdev_command(struct v4l2_subdev *sd, unsigned cmd, void *arg)
+ return v4l2_subdev_call(sd, video, g_vbi_data, arg);
+ case VIDIOC_G_SLICED_VBI_CAP:
+ return v4l2_subdev_call(sd, video, g_sliced_vbi_cap, arg);
++ case VIDIOC_ENUM_FMT:
++ return v4l2_subdev_call(sd, video, enum_fmt, arg);
++ case VIDIOC_TRY_FMT:
++ return v4l2_subdev_call(sd, video, try_fmt, arg);
+ case VIDIOC_S_FMT:
+ return v4l2_subdev_call(sd, video, s_fmt, arg);
+ case VIDIOC_G_FMT:
+ return v4l2_subdev_call(sd, video, g_fmt, arg);
+ case VIDIOC_INT_S_STD_OUTPUT:
+ return v4l2_subdev_call(sd, video, s_std_output, *(v4l2_std_id *)arg);
++ case VIDIOC_QUERYSTD:
++ return v4l2_subdev_call(sd, video, querystd, arg);
++ case VIDIOC_INT_G_INPUT_STATUS:
++ return v4l2_subdev_call(sd, video, g_input_status, arg);
+ case VIDIOC_STREAMON:
+ return v4l2_subdev_call(sd, video, s_stream, 1);
+ case VIDIOC_STREAMOFF:
+ return v4l2_subdev_call(sd, video, s_stream, 0);
++ case VIDIOC_S_PARM:
++ return v4l2_subdev_call(sd, video, s_parm, arg);
++ case VIDIOC_G_PARM:
++ return v4l2_subdev_call(sd, video, g_parm, arg);
+
+ default:
+ return v4l2_subdev_call(sd, core, ioctl, cmd, arg);
+diff --git a/drivers/media/video/videobuf-dma-contig.c b/drivers/media/video/videobuf-dma-contig.c
+index 31944b1..6109fb5 100644
+--- a/drivers/media/video/videobuf-dma-contig.c
++++ b/drivers/media/video/videobuf-dma-contig.c
+@@ -400,7 +400,7 @@ void videobuf_dma_contig_free(struct videobuf_queue *q,
+ So, it should free memory only if the memory were allocated for
+ read() operation.
+ */
+- if ((buf->memory != V4L2_MEMORY_USERPTR) || !buf->baddr)
++ if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr)
+ return;
+
+ if (!mem)
+diff --git a/drivers/media/video/videobuf-vmalloc.c b/drivers/media/video/videobuf-vmalloc.c
+index be65a2f..30ae30f 100644
+--- a/drivers/media/video/videobuf-vmalloc.c
++++ b/drivers/media/video/videobuf-vmalloc.c
+@@ -425,7 +425,7 @@ void videobuf_vmalloc_free (struct videobuf_buffer *buf)
+ So, it should free memory only if the memory were allocated for
+ read() operation.
+ */
+- if ((buf->memory != V4L2_MEMORY_USERPTR) || (buf->baddr == 0))
++ if ((buf->memory != V4L2_MEMORY_USERPTR) || buf->baddr)
+ return;
+
+ if (!mem)
+diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c
+index 88bf845..8da4dd1 100644
+--- a/drivers/media/video/vino.c
++++ b/drivers/media/video/vino.c
+@@ -8,6 +8,12 @@
+ *
+ * Based on the previous version of the driver for 2.4 kernels by:
+ * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
++ *
++ * v4l2_device/v4l2_subdev conversion by:
++ * Copyright (C) 2009 Hans Verkuil <hverkuil@xs4all.nl>
++ *
++ * Note: this conversion is untested! Please contact the linux-media
++ * mailinglist if you can test this, together with the test results.
+ */
+
+ /*
+@@ -33,12 +39,10 @@
+ #include <linux/kmod.h>
+
+ #include <linux/i2c.h>
+-#include <linux/i2c-algo-sgi.h>
+
+ #include <linux/videodev2.h>
+-#include <media/v4l2-common.h>
++#include <media/v4l2-device.h>
+ #include <media/v4l2-ioctl.h>
+-#include <linux/video_decoder.h>
+ #include <linux/mutex.h>
+
+ #include <asm/paccess.h>
+@@ -139,13 +143,23 @@ MODULE_LICENSE("GPL");
+ #define VINO_DATA_NORM_PAL 1
+ #define VINO_DATA_NORM_SECAM 2
+ #define VINO_DATA_NORM_D1 3
+-/* The following are special entries that can be used to
+- * autodetect the norm. */
+-#define VINO_DATA_NORM_AUTO 0xfe
+-#define VINO_DATA_NORM_AUTO_EXT 0xff
+
+ #define VINO_DATA_NORM_COUNT 4
+
++/* I2C controller flags */
++#define SGI_I2C_FORCE_IDLE (0 << 0)
++#define SGI_I2C_NOT_IDLE (1 << 0)
++#define SGI_I2C_WRITE (0 << 1)
++#define SGI_I2C_READ (1 << 1)
++#define SGI_I2C_RELEASE_BUS (0 << 2)
++#define SGI_I2C_HOLD_BUS (1 << 2)
++#define SGI_I2C_XFER_DONE (0 << 4)
++#define SGI_I2C_XFER_BUSY (1 << 4)
++#define SGI_I2C_ACK (0 << 5)
++#define SGI_I2C_NACK (1 << 5)
++#define SGI_I2C_BUS_OK (0 << 7)
++#define SGI_I2C_BUS_ERR (1 << 7)
++
+ /* Internal data structure definitions */
+
+ struct vino_input {
+@@ -289,22 +303,20 @@ struct vino_channel_settings {
+ struct vino_interrupt_data int_data;
+
+ /* V4L support */
+- struct video_device *v4l_device;
+-};
+-
+-struct vino_client {
+- /* the channel which owns this client:
+- * VINO_NO_CHANNEL, VINO_CHANNEL_A or VINO_CHANNEL_B */
+- unsigned int owner;
+- struct i2c_client *driver;
++ struct video_device *vdev;
+ };
+
+ struct vino_settings {
++ struct v4l2_device v4l2_dev;
+ struct vino_channel_settings a;
+ struct vino_channel_settings b;
+
+- struct vino_client decoder;
+- struct vino_client camera;
++ /* the channel which owns this client:
++ * VINO_NO_CHANNEL, VINO_CHANNEL_A or VINO_CHANNEL_B */
++ unsigned int decoder_owner;
++ struct v4l2_subdev *decoder;
++ unsigned int camera_owner;
++ struct v4l2_subdev *camera;
+
+ /* a lock for vino register access */
+ spinlock_t vino_lock;
+@@ -344,11 +356,16 @@ static struct sgi_vino *vino;
+
+ static struct vino_settings *vino_drvdata;
+
++#define camera_call(o, f, args...) \
++ v4l2_subdev_call(vino_drvdata->camera, o, f, ##args)
++#define decoder_call(o, f, args...) \
++ v4l2_subdev_call(vino_drvdata->decoder, o, f, ##args)
++
+ static const char *vino_driver_name = "vino";
+ static const char *vino_driver_description = "SGI VINO";
+ static const char *vino_bus_name = "GIO64 bus";
+-static const char *vino_v4l_device_name_a = "SGI VINO Channel A";
+-static const char *vino_v4l_device_name_b = "SGI VINO Channel B";
++static const char *vino_vdev_name_a = "SGI VINO Channel A";
++static const char *vino_vdev_name_b = "SGI VINO Channel B";
+
+ static void vino_capture_tasklet(unsigned long channel);
+
+@@ -360,11 +377,11 @@ static const struct vino_input vino_inputs[] = {
+ .name = "Composite",
+ .std = V4L2_STD_NTSC | V4L2_STD_PAL
+ | V4L2_STD_SECAM,
+- },{
++ }, {
+ .name = "S-Video",
+ .std = V4L2_STD_NTSC | V4L2_STD_PAL
+ | V4L2_STD_SECAM,
+- },{
++ }, {
+ .name = "D1/IndyCam",
+ .std = V4L2_STD_NTSC,
+ }
+@@ -376,17 +393,17 @@ static const struct vino_data_format vino_data_formats[] = {
+ .bpp = 1,
+ .pixelformat = V4L2_PIX_FMT_GREY,
+ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+- },{
++ }, {
+ .description = "8-bit dithered RGB 3-3-2",
+ .bpp = 1,
+ .pixelformat = V4L2_PIX_FMT_RGB332,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+- },{
++ }, {
+ .description = "32-bit RGB",
+ .bpp = 4,
+ .pixelformat = V4L2_PIX_FMT_RGB32,
+ .colorspace = V4L2_COLORSPACE_SRGB,
+- },{
++ }, {
+ .description = "YUV 4:2:2",
+ .bpp = 2,
+ .pixelformat = V4L2_PIX_FMT_YUYV, // XXX: swapped?
+@@ -417,7 +434,7 @@ static const struct vino_data_norm vino_data_norms[] = {
+ + VINO_NTSC_HEIGHT / 2 - 1,
+ .right = VINO_NTSC_WIDTH,
+ },
+- },{
++ }, {
+ .description = "PAL",
+ .std = V4L2_STD_PAL,
+ .fps_min = 5,
+@@ -439,7 +456,7 @@ static const struct vino_data_norm vino_data_norms[] = {
+ + VINO_PAL_HEIGHT / 2 - 1,
+ .right = VINO_PAL_WIDTH,
+ },
+- },{
++ }, {
+ .description = "SECAM",
+ .std = V4L2_STD_SECAM,
+ .fps_min = 5,
+@@ -461,7 +478,7 @@ static const struct vino_data_norm vino_data_norms[] = {
+ + VINO_PAL_HEIGHT / 2 - 1,
+ .right = VINO_PAL_WIDTH,
+ },
+- },{
++ }, {
+ .description = "NTSC/D1",
+ .std = V4L2_STD_NTSC,
+ .fps_min = 6,
+@@ -497,9 +514,7 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = {
+ .maximum = 1,
+ .step = 1,
+ .default_value = INDYCAM_AGC_DEFAULT,
+- .flags = 0,
+- .reserved = { INDYCAM_CONTROL_AGC, 0 },
+- },{
++ }, {
+ .id = V4L2_CID_AUTO_WHITE_BALANCE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Automatic White Balance",
+@@ -507,9 +522,7 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = {
+ .maximum = 1,
+ .step = 1,
+ .default_value = INDYCAM_AWB_DEFAULT,
+- .flags = 0,
+- .reserved = { INDYCAM_CONTROL_AWB, 0 },
+- },{
++ }, {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gain",
+@@ -517,29 +530,23 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = {
+ .maximum = INDYCAM_GAIN_MAX,
+ .step = 1,
+ .default_value = INDYCAM_GAIN_DEFAULT,
+- .flags = 0,
+- .reserved = { INDYCAM_CONTROL_GAIN, 0 },
+- },{
+- .id = V4L2_CID_PRIVATE_BASE,
++ }, {
++ .id = INDYCAM_CONTROL_RED_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Saturation",
+ .minimum = INDYCAM_RED_SATURATION_MIN,
+ .maximum = INDYCAM_RED_SATURATION_MAX,
+ .step = 1,
+ .default_value = INDYCAM_RED_SATURATION_DEFAULT,
+- .flags = 0,
+- .reserved = { INDYCAM_CONTROL_RED_SATURATION, 0 },
+- },{
+- .id = V4L2_CID_PRIVATE_BASE + 1,
++ }, {
++ .id = INDYCAM_CONTROL_BLUE_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Saturation",
+ .minimum = INDYCAM_BLUE_SATURATION_MIN,
+ .maximum = INDYCAM_BLUE_SATURATION_MAX,
+ .step = 1,
+ .default_value = INDYCAM_BLUE_SATURATION_DEFAULT,
+- .flags = 0,
+- .reserved = { INDYCAM_CONTROL_BLUE_SATURATION, 0 },
+- },{
++ }, {
+ .id = V4L2_CID_RED_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Red Balance",
+@@ -547,9 +554,7 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = {
+ .maximum = INDYCAM_RED_BALANCE_MAX,
+ .step = 1,
+ .default_value = INDYCAM_RED_BALANCE_DEFAULT,
+- .flags = 0,
+- .reserved = { INDYCAM_CONTROL_RED_BALANCE, 0 },
+- },{
++ }, {
+ .id = V4L2_CID_BLUE_BALANCE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Blue Balance",
+@@ -557,9 +562,7 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = {
+ .maximum = INDYCAM_BLUE_BALANCE_MAX,
+ .step = 1,
+ .default_value = INDYCAM_BLUE_BALANCE_DEFAULT,
+- .flags = 0,
+- .reserved = { INDYCAM_CONTROL_BLUE_BALANCE, 0 },
+- },{
++ }, {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Shutter Control",
+@@ -567,9 +570,7 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = {
+ .maximum = INDYCAM_SHUTTER_MAX,
+ .step = 1,
+ .default_value = INDYCAM_SHUTTER_DEFAULT,
+- .flags = 0,
+- .reserved = { INDYCAM_CONTROL_SHUTTER, 0 },
+- },{
++ }, {
+ .id = V4L2_CID_GAMMA,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Gamma",
+@@ -577,8 +578,6 @@ struct v4l2_queryctrl vino_indycam_v4l2_controls[] = {
+ .maximum = INDYCAM_GAMMA_MAX,
+ .step = 1,
+ .default_value = INDYCAM_GAMMA_DEFAULT,
+- .flags = 0,
+- .reserved = { INDYCAM_CONTROL_GAMMA, 0 },
+ }
+ };
+
+@@ -593,209 +592,73 @@ struct v4l2_queryctrl vino_saa7191_v4l2_controls[] = {
+ .maximum = SAA7191_HUE_MAX,
+ .step = 1,
+ .default_value = SAA7191_HUE_DEFAULT,
+- .flags = 0,
+- .reserved = { SAA7191_CONTROL_HUE, 0 },
+- },{
+- .id = V4L2_CID_PRIVATE_BASE,
++ }, {
++ .id = SAA7191_CONTROL_BANDPASS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Luminance Bandpass",
+ .minimum = SAA7191_BANDPASS_MIN,
+ .maximum = SAA7191_BANDPASS_MAX,
+ .step = 1,
+ .default_value = SAA7191_BANDPASS_DEFAULT,
+- .flags = 0,
+- .reserved = { SAA7191_CONTROL_BANDPASS, 0 },
+- },{
+- .id = V4L2_CID_PRIVATE_BASE + 1,
++ }, {
++ .id = SAA7191_CONTROL_BANDPASS_WEIGHT,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Luminance Bandpass Weight",
+ .minimum = SAA7191_BANDPASS_WEIGHT_MIN,
+ .maximum = SAA7191_BANDPASS_WEIGHT_MAX,
+ .step = 1,
+ .default_value = SAA7191_BANDPASS_WEIGHT_DEFAULT,
+- .flags = 0,
+- .reserved = { SAA7191_CONTROL_BANDPASS_WEIGHT, 0 },
+- },{
+- .id = V4L2_CID_PRIVATE_BASE + 2,
++ }, {
++ .id = SAA7191_CONTROL_CORING,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "HF Luminance Coring",
+ .minimum = SAA7191_CORING_MIN,
+ .maximum = SAA7191_CORING_MAX,
+ .step = 1,
+ .default_value = SAA7191_CORING_DEFAULT,
+- .flags = 0,
+- .reserved = { SAA7191_CONTROL_CORING, 0 },
+- },{
+- .id = V4L2_CID_PRIVATE_BASE + 3,
++ }, {
++ .id = SAA7191_CONTROL_FORCE_COLOUR,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Force Colour",
+ .minimum = SAA7191_FORCE_COLOUR_MIN,
+ .maximum = SAA7191_FORCE_COLOUR_MAX,
+ .step = 1,
+ .default_value = SAA7191_FORCE_COLOUR_DEFAULT,
+- .flags = 0,
+- .reserved = { SAA7191_CONTROL_FORCE_COLOUR, 0 },
+- },{
+- .id = V4L2_CID_PRIVATE_BASE + 4,
++ }, {
++ .id = SAA7191_CONTROL_CHROMA_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Chrominance Gain Control",
+ .minimum = SAA7191_CHROMA_GAIN_MIN,
+ .maximum = SAA7191_CHROMA_GAIN_MAX,
+ .step = 1,
+ .default_value = SAA7191_CHROMA_GAIN_DEFAULT,
+- .flags = 0,
+- .reserved = { SAA7191_CONTROL_CHROMA_GAIN, 0 },
+- },{
+- .id = V4L2_CID_PRIVATE_BASE + 5,
++ }, {
++ .id = SAA7191_CONTROL_VTRC,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "VTR Time Constant",
+ .minimum = SAA7191_VTRC_MIN,
+ .maximum = SAA7191_VTRC_MAX,
+ .step = 1,
+ .default_value = SAA7191_VTRC_DEFAULT,
+- .flags = 0,
+- .reserved = { SAA7191_CONTROL_VTRC, 0 },
+- },{
+- .id = V4L2_CID_PRIVATE_BASE + 6,
++ }, {
++ .id = SAA7191_CONTROL_LUMA_DELAY,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Luminance Delay Compensation",
+ .minimum = SAA7191_LUMA_DELAY_MIN,
+ .maximum = SAA7191_LUMA_DELAY_MAX,
+ .step = 1,
+ .default_value = SAA7191_LUMA_DELAY_DEFAULT,
+- .flags = 0,
+- .reserved = { SAA7191_CONTROL_LUMA_DELAY, 0 },
+- },{
+- .id = V4L2_CID_PRIVATE_BASE + 7,
++ }, {
++ .id = SAA7191_CONTROL_VNR,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Vertical Noise Reduction",
+ .minimum = SAA7191_VNR_MIN,
+ .maximum = SAA7191_VNR_MAX,
+ .step = 1,
+ .default_value = SAA7191_VNR_DEFAULT,
+- .flags = 0,
+- .reserved = { SAA7191_CONTROL_VNR, 0 },
+ }
+ };
+
+-/* VINO I2C bus functions */
+-
+-unsigned i2c_vino_getctrl(void *data)
+-{
+- return vino->i2c_control;
+-}
+-
+-void i2c_vino_setctrl(void *data, unsigned val)
+-{
+- vino->i2c_control = val;
+-}
+-
+-unsigned i2c_vino_rdata(void *data)
+-{
+- return vino->i2c_data;
+-}
+-
+-void i2c_vino_wdata(void *data, unsigned val)
+-{
+- vino->i2c_data = val;
+-}
+-
+-static struct i2c_algo_sgi_data i2c_sgi_vino_data =
+-{
+- .getctrl = &i2c_vino_getctrl,
+- .setctrl = &i2c_vino_setctrl,
+- .rdata = &i2c_vino_rdata,
+- .wdata = &i2c_vino_wdata,
+- .xfer_timeout = 200,
+- .ack_timeout = 1000,
+-};
+-
+-/*
+- * There are two possible clients on VINO I2C bus, so we limit usage only
+- * to them.
+- */
+-static int i2c_vino_client_reg(struct i2c_client *client)
+-{
+- unsigned long flags;
+- int ret = 0;
+-
+- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+- switch (client->driver->id) {
+- case I2C_DRIVERID_SAA7191:
+- if (vino_drvdata->decoder.driver)
+- ret = -EBUSY;
+- else
+- vino_drvdata->decoder.driver = client;
+- break;
+- case I2C_DRIVERID_INDYCAM:
+- if (vino_drvdata->camera.driver)
+- ret = -EBUSY;
+- else
+- vino_drvdata->camera.driver = client;
+- break;
+- default:
+- ret = -ENODEV;
+- }
+- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+-
+- return ret;
+-}
+-
+-static int i2c_vino_client_unreg(struct i2c_client *client)
+-{
+- unsigned long flags;
+- int ret = 0;
+-
+- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+- if (client == vino_drvdata->decoder.driver) {
+- if (vino_drvdata->decoder.owner != VINO_NO_CHANNEL)
+- ret = -EBUSY;
+- else
+- vino_drvdata->decoder.driver = NULL;
+- } else if (client == vino_drvdata->camera.driver) {
+- if (vino_drvdata->camera.owner != VINO_NO_CHANNEL)
+- ret = -EBUSY;
+- else
+- vino_drvdata->camera.driver = NULL;
+- }
+- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+-
+- return ret;
+-}
+-
+-static struct i2c_adapter vino_i2c_adapter =
+-{
+- .name = "VINO I2C bus",
+- .id = I2C_HW_SGI_VINO,
+- .algo_data = &i2c_sgi_vino_data,
+- .client_register = &i2c_vino_client_reg,
+- .client_unregister = &i2c_vino_client_unreg,
+-};
+-
+-static int vino_i2c_add_bus(void)
+-{
+- return i2c_sgi_add_bus(&vino_i2c_adapter);
+-}
+-
+-static int vino_i2c_del_bus(void)
+-{
+- return i2c_del_adapter(&vino_i2c_adapter);
+-}
+-
+-static int i2c_camera_command(unsigned int cmd, void *arg)
+-{
+- return vino_drvdata->camera.driver->
+- driver->command(vino_drvdata->camera.driver,
+- cmd, arg);
+-}
+-
+-static int i2c_decoder_command(unsigned int cmd, void *arg)
+-{
+- return vino_drvdata->decoder.driver->
+- driver->command(vino_drvdata->decoder.driver,
+- cmd, arg);
+-}
+-
+ /* VINO framebuffer/DMA descriptor management */
+
+ static void vino_free_buffer_with_count(struct vino_framebuffer *fb,
+@@ -1741,6 +1604,184 @@ static inline void vino_set_default_framerate(struct
+ vino_set_framerate(vcs, vino_data_norms[vcs->data_norm].fps_max);
+ }
+
++/* VINO I2C bus functions */
++
++struct i2c_algo_sgi_data {
++ void *data; /* private data for lowlevel routines */
++ unsigned (*getctrl)(void *data);
++ void (*setctrl)(void *data, unsigned val);
++ unsigned (*rdata)(void *data);
++ void (*wdata)(void *data, unsigned val);
++
++ int xfer_timeout;
++ int ack_timeout;
++};
++
++static int wait_xfer_done(struct i2c_algo_sgi_data *adap)
++{
++ int i;
++
++ for (i = 0; i < adap->xfer_timeout; i++) {
++ if ((adap->getctrl(adap->data) & SGI_I2C_XFER_BUSY) == 0)
++ return 0;
++ udelay(1);
++ }
++
++ return -ETIMEDOUT;
++}
++
++static int wait_ack(struct i2c_algo_sgi_data *adap)
++{
++ int i;
++
++ if (wait_xfer_done(adap))
++ return -ETIMEDOUT;
++ for (i = 0; i < adap->ack_timeout; i++) {
++ if ((adap->getctrl(adap->data) & SGI_I2C_NACK) == 0)
++ return 0;
++ udelay(1);
++ }
++
++ return -ETIMEDOUT;
++}
++
++static int force_idle(struct i2c_algo_sgi_data *adap)
++{
++ int i;
++
++ adap->setctrl(adap->data, SGI_I2C_FORCE_IDLE);
++ for (i = 0; i < adap->xfer_timeout; i++) {
++ if ((adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE) == 0)
++ goto out;
++ udelay(1);
++ }
++ return -ETIMEDOUT;
++out:
++ if (adap->getctrl(adap->data) & SGI_I2C_BUS_ERR)
++ return -EIO;
++ return 0;
++}
++
++static int do_address(struct i2c_algo_sgi_data *adap, unsigned int addr,
++ int rd)
++{
++ if (rd)
++ adap->setctrl(adap->data, SGI_I2C_NOT_IDLE);
++ /* Check if bus is idle, eventually force it to do so */
++ if (adap->getctrl(adap->data) & SGI_I2C_NOT_IDLE)
++ if (force_idle(adap))
++ return -EIO;
++ /* Write out the i2c chip address and specify operation */
++ adap->setctrl(adap->data,
++ SGI_I2C_HOLD_BUS | SGI_I2C_WRITE | SGI_I2C_NOT_IDLE);
++ if (rd)
++ addr |= 1;
++ adap->wdata(adap->data, addr);
++ if (wait_ack(adap))
++ return -EIO;
++ return 0;
++}
++
++static int i2c_read(struct i2c_algo_sgi_data *adap, unsigned char *buf,
++ unsigned int len)
++{
++ int i;
++
++ adap->setctrl(adap->data,
++ SGI_I2C_HOLD_BUS | SGI_I2C_READ | SGI_I2C_NOT_IDLE);
++ for (i = 0; i < len; i++) {
++ if (wait_xfer_done(adap))
++ return -EIO;
++ buf[i] = adap->rdata(adap->data);
++ }
++ adap->setctrl(adap->data, SGI_I2C_RELEASE_BUS | SGI_I2C_FORCE_IDLE);
++
++ return 0;
++
++}
++
++static int i2c_write(struct i2c_algo_sgi_data *adap, unsigned char *buf,
++ unsigned int len)
++{
++ int i;
++
++ /* We are already in write state */
++ for (i = 0; i < len; i++) {
++ adap->wdata(adap->data, buf[i]);
++ if (wait_ack(adap))
++ return -EIO;
++ }
++ return 0;
++}
++
++static int sgi_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs,
++ int num)
++{
++ struct i2c_algo_sgi_data *adap = i2c_adap->algo_data;
++ struct i2c_msg *p;
++ int i, err = 0;
++
++ for (i = 0; !err && i < num; i++) {
++ p = &msgs[i];
++ err = do_address(adap, p->addr, p->flags & I2C_M_RD);
++ if (err || !p->len)
++ continue;
++ if (p->flags & I2C_M_RD)
++ err = i2c_read(adap, p->buf, p->len);
++ else
++ err = i2c_write(adap, p->buf, p->len);
++ }
++
++ return (err < 0) ? err : i;
++}
++
++static u32 sgi_func(struct i2c_adapter *adap)
++{
++ return I2C_FUNC_SMBUS_EMUL;
++}
++
++static const struct i2c_algorithm sgi_algo = {
++ .master_xfer = sgi_xfer,
++ .functionality = sgi_func,
++};
++
++static unsigned i2c_vino_getctrl(void *data)
++{
++ return vino->i2c_control;
++}
++
++static void i2c_vino_setctrl(void *data, unsigned val)
++{
++ vino->i2c_control = val;
++}
++
++static unsigned i2c_vino_rdata(void *data)
++{
++ return vino->i2c_data;
++}
++
++static void i2c_vino_wdata(void *data, unsigned val)
++{
++ vino->i2c_data = val;
++}
++
++static struct i2c_algo_sgi_data i2c_sgi_vino_data = {
++ .getctrl = &i2c_vino_getctrl,
++ .setctrl = &i2c_vino_setctrl,
++ .rdata = &i2c_vino_rdata,
++ .wdata = &i2c_vino_wdata,
++ .xfer_timeout = 200,
++ .ack_timeout = 1000,
++};
++
++static struct i2c_adapter vino_i2c_adapter = {
++ .name = "VINO I2C bus",
++ .id = I2C_HW_SGI_VINO,
++ .algo = &sgi_algo,
++ .algo_data = &i2c_sgi_vino_data,
++ .owner = THIS_MODULE,
++};
++
+ /*
+ * Prepare VINO for DMA transfer...
+ * (execute only with vino_lock and input_lock locked)
+@@ -2490,86 +2531,15 @@ static int vino_get_saa7191_input(int input)
+ }
+ }
+
+-static int vino_get_saa7191_norm(unsigned int data_norm)
+-{
+- switch (data_norm) {
+- case VINO_DATA_NORM_AUTO:
+- return SAA7191_NORM_AUTO;
+- case VINO_DATA_NORM_AUTO_EXT:
+- return SAA7191_NORM_AUTO_EXT;
+- case VINO_DATA_NORM_PAL:
+- return SAA7191_NORM_PAL;
+- case VINO_DATA_NORM_NTSC:
+- return SAA7191_NORM_NTSC;
+- case VINO_DATA_NORM_SECAM:
+- return SAA7191_NORM_SECAM;
+- default:
+- printk(KERN_ERR "VINO: vino_get_saa7191_norm(): "
+- "invalid norm!\n");
+- return -1;
+- }
+-}
+-
+-static int vino_get_from_saa7191_norm(int saa7191_norm)
+-{
+- switch (saa7191_norm) {
+- case SAA7191_NORM_PAL:
+- return VINO_DATA_NORM_PAL;
+- case SAA7191_NORM_NTSC:
+- return VINO_DATA_NORM_NTSC;
+- case SAA7191_NORM_SECAM:
+- return VINO_DATA_NORM_SECAM;
+- default:
+- printk(KERN_ERR "VINO: vino_get_from_saa7191_norm(): "
+- "invalid norm!\n");
+- return VINO_DATA_NORM_NONE;
+- }
+-}
+-
+-static int vino_saa7191_set_norm(unsigned int *data_norm)
+-{
+- int saa7191_norm, new_data_norm;
+- int err = 0;
+-
+- saa7191_norm = vino_get_saa7191_norm(*data_norm);
+-
+- err = i2c_decoder_command(DECODER_SAA7191_SET_NORM,
+- &saa7191_norm);
+- if (err)
+- goto out;
+-
+- if ((*data_norm == VINO_DATA_NORM_AUTO)
+- || (*data_norm == VINO_DATA_NORM_AUTO_EXT)) {
+- struct saa7191_status status;
+-
+- err = i2c_decoder_command(DECODER_SAA7191_GET_STATUS,
+- &status);
+- if (err)
+- goto out;
+-
+- new_data_norm =
+- vino_get_from_saa7191_norm(status.norm);
+- if (new_data_norm == VINO_DATA_NORM_NONE) {
+- err = -EINVAL;
+- goto out;
+- }
+-
+- *data_norm = (unsigned int)new_data_norm;
+- }
+-
+-out:
+- return err;
+-}
+-
+ /* execute with input_lock locked */
+ static int vino_is_input_owner(struct vino_channel_settings *vcs)
+ {
+ switch(vcs->input) {
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO:
+- return (vino_drvdata->decoder.owner == vcs->channel);
++ return vino_drvdata->decoder_owner == vcs->channel;
+ case VINO_INPUT_D1:
+- return (vino_drvdata->camera.owner == vcs->channel);
++ return vino_drvdata->camera_owner == vcs->channel;
+ default:
+ return 0;
+ }
+@@ -2585,23 +2555,22 @@ static int vino_acquire_input(struct vino_channel_settings *vcs)
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ /* First try D1 and then SAA7191 */
+- if (vino_drvdata->camera.driver
+- && (vino_drvdata->camera.owner == VINO_NO_CHANNEL)) {
+- i2c_use_client(vino_drvdata->camera.driver);
+- vino_drvdata->camera.owner = vcs->channel;
++ if (vino_drvdata->camera
++ && (vino_drvdata->camera_owner == VINO_NO_CHANNEL)) {
++ vino_drvdata->camera_owner = vcs->channel;
+ vcs->input = VINO_INPUT_D1;
+ vcs->data_norm = VINO_DATA_NORM_D1;
+- } else if (vino_drvdata->decoder.driver
+- && (vino_drvdata->decoder.owner == VINO_NO_CHANNEL)) {
+- int input, data_norm;
+- int saa7191_input;
++ } else if (vino_drvdata->decoder
++ && (vino_drvdata->decoder_owner == VINO_NO_CHANNEL)) {
++ int input;
++ int data_norm;
++ v4l2_std_id norm;
++ struct v4l2_routing route = { 0, 0 };
+
+- i2c_use_client(vino_drvdata->decoder.driver);
+ input = VINO_INPUT_COMPOSITE;
+
+- saa7191_input = vino_get_saa7191_input(input);
+- ret = i2c_decoder_command(DECODER_SET_INPUT,
+- &saa7191_input);
++ route.input = vino_get_saa7191_input(input);
++ ret = decoder_call(video, s_routing, &route);
+ if (ret) {
+ ret = -EINVAL;
+ goto out;
+@@ -2612,12 +2581,15 @@ static int vino_acquire_input(struct vino_channel_settings *vcs)
+ /* Don't hold spinlocks while auto-detecting norm
+ * as it may take a while... */
+
+- data_norm = VINO_DATA_NORM_AUTO_EXT;
+-
+- ret = vino_saa7191_set_norm(&data_norm);
+- if ((ret == -EBUSY) || (ret == -EAGAIN)) {
+- data_norm = VINO_DATA_NORM_PAL;
+- ret = vino_saa7191_set_norm(&data_norm);
++ ret = decoder_call(video, querystd, &norm);
++ if (!ret) {
++ for (data_norm = 0; data_norm < 3; data_norm++) {
++ if (vino_data_norms[data_norm].std & norm)
++ break;
++ }
++ if (data_norm == 3)
++ data_norm = VINO_DATA_NORM_PAL;
++ ret = decoder_call(tuner, s_std, norm);
+ }
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+@@ -2627,7 +2599,7 @@ static int vino_acquire_input(struct vino_channel_settings *vcs)
+ goto out;
+ }
+
+- vino_drvdata->decoder.owner = vcs->channel;
++ vino_drvdata->decoder_owner = vcs->channel;
+
+ vcs->input = input;
+ vcs->data_norm = data_norm;
+@@ -2672,25 +2644,24 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input)
+ switch (input) {
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO:
+- if (!vino_drvdata->decoder.driver) {
++ if (!vino_drvdata->decoder) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+- if (vino_drvdata->decoder.owner == VINO_NO_CHANNEL) {
+- i2c_use_client(vino_drvdata->decoder.driver);
+- vino_drvdata->decoder.owner = vcs->channel;
++ if (vino_drvdata->decoder_owner == VINO_NO_CHANNEL) {
++ vino_drvdata->decoder_owner = vcs->channel;
+ }
+
+- if (vino_drvdata->decoder.owner == vcs->channel) {
++ if (vino_drvdata->decoder_owner == vcs->channel) {
+ int data_norm;
+- int saa7191_input;
++ v4l2_std_id norm;
++ struct v4l2_routing route = { 0, 0 };
+
+- saa7191_input = vino_get_saa7191_input(input);
+- ret = i2c_decoder_command(DECODER_SET_INPUT,
+- &saa7191_input);
++ route.input = vino_get_saa7191_input(input);
++ ret = decoder_call(video, s_routing, &route);
+ if (ret) {
+- vino_drvdata->decoder.owner = VINO_NO_CHANNEL;
++ vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
+ ret = -EINVAL;
+ goto out;
+ }
+@@ -2700,18 +2671,21 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input)
+ /* Don't hold spinlocks while auto-detecting norm
+ * as it may take a while... */
+
+- data_norm = VINO_DATA_NORM_AUTO_EXT;
+-
+- ret = vino_saa7191_set_norm(&data_norm);
+- if ((ret == -EBUSY) || (ret == -EAGAIN)) {
+- data_norm = VINO_DATA_NORM_PAL;
+- ret = vino_saa7191_set_norm(&data_norm);
++ ret = decoder_call(video, querystd, &norm);
++ if (!ret) {
++ for (data_norm = 0; data_norm < 3; data_norm++) {
++ if (vino_data_norms[data_norm].std & norm)
++ break;
++ }
++ if (data_norm == 3)
++ data_norm = VINO_DATA_NORM_PAL;
++ ret = decoder_call(tuner, s_std, norm);
+ }
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+ if (ret) {
+- vino_drvdata->decoder.owner = VINO_NO_CHANNEL;
++ vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
+ ret = -EINVAL;
+ goto out;
+ }
+@@ -2728,37 +2702,31 @@ static int vino_set_input(struct vino_channel_settings *vcs, int input)
+ vcs->data_norm = vcs2->data_norm;
+ }
+
+- if (vino_drvdata->camera.owner == vcs->channel) {
++ if (vino_drvdata->camera_owner == vcs->channel) {
+ /* Transfer the ownership or release the input */
+ if (vcs2->input == VINO_INPUT_D1) {
+- vino_drvdata->camera.owner = vcs2->channel;
++ vino_drvdata->camera_owner = vcs2->channel;
+ } else {
+- i2c_release_client(vino_drvdata->
+- camera.driver);
+- vino_drvdata->camera.owner = VINO_NO_CHANNEL;
++ vino_drvdata->camera_owner = VINO_NO_CHANNEL;
+ }
+ }
+ break;
+ case VINO_INPUT_D1:
+- if (!vino_drvdata->camera.driver) {
++ if (!vino_drvdata->camera) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+- if (vino_drvdata->camera.owner == VINO_NO_CHANNEL) {
+- i2c_use_client(vino_drvdata->camera.driver);
+- vino_drvdata->camera.owner = vcs->channel;
+- }
++ if (vino_drvdata->camera_owner == VINO_NO_CHANNEL)
++ vino_drvdata->camera_owner = vcs->channel;
+
+- if (vino_drvdata->decoder.owner == vcs->channel) {
++ if (vino_drvdata->decoder_owner == vcs->channel) {
+ /* Transfer the ownership or release the input */
+ if ((vcs2->input == VINO_INPUT_COMPOSITE) ||
+ (vcs2->input == VINO_INPUT_SVIDEO)) {
+- vino_drvdata->decoder.owner = vcs2->channel;
++ vino_drvdata->decoder_owner = vcs2->channel;
+ } else {
+- i2c_release_client(vino_drvdata->
+- decoder.driver);
+- vino_drvdata->decoder.owner = VINO_NO_CHANNEL;
++ vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
+ }
+ }
+
+@@ -2795,20 +2763,18 @@ static void vino_release_input(struct vino_channel_settings *vcs)
+ /* Release ownership of the channel
+ * and if the other channel takes input from
+ * the same source, transfer the ownership */
+- if (vino_drvdata->camera.owner == vcs->channel) {
++ if (vino_drvdata->camera_owner == vcs->channel) {
+ if (vcs2->input == VINO_INPUT_D1) {
+- vino_drvdata->camera.owner = vcs2->channel;
++ vino_drvdata->camera_owner = vcs2->channel;
+ } else {
+- i2c_release_client(vino_drvdata->camera.driver);
+- vino_drvdata->camera.owner = VINO_NO_CHANNEL;
++ vino_drvdata->camera_owner = VINO_NO_CHANNEL;
+ }
+- } else if (vino_drvdata->decoder.owner == vcs->channel) {
++ } else if (vino_drvdata->decoder_owner == vcs->channel) {
+ if ((vcs2->input == VINO_INPUT_COMPOSITE) ||
+ (vcs2->input == VINO_INPUT_SVIDEO)) {
+- vino_drvdata->decoder.owner = vcs2->channel;
++ vino_drvdata->decoder_owner = vcs2->channel;
+ } else {
+- i2c_release_client(vino_drvdata->decoder.driver);
+- vino_drvdata->decoder.owner = VINO_NO_CHANNEL;
++ vino_drvdata->decoder_owner = VINO_NO_CHANNEL;
+ }
+ }
+ vcs->input = VINO_INPUT_NONE;
+@@ -2829,18 +2795,16 @@ static int vino_set_data_norm(struct vino_channel_settings *vcs,
+ switch (vcs->input) {
+ case VINO_INPUT_D1:
+ /* only one "norm" supported */
+- if ((data_norm != VINO_DATA_NORM_D1)
+- && (data_norm != VINO_DATA_NORM_AUTO)
+- && (data_norm != VINO_DATA_NORM_AUTO_EXT))
++ if (data_norm != VINO_DATA_NORM_D1)
+ return -EINVAL;
+ break;
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO: {
++ v4l2_std_id norm;
++
+ if ((data_norm != VINO_DATA_NORM_PAL)
+ && (data_norm != VINO_DATA_NORM_NTSC)
+- && (data_norm != VINO_DATA_NORM_SECAM)
+- && (data_norm != VINO_DATA_NORM_AUTO)
+- && (data_norm != VINO_DATA_NORM_AUTO_EXT))
++ && (data_norm != VINO_DATA_NORM_SECAM))
+ return -EINVAL;
+
+ spin_unlock_irqrestore(&vino_drvdata->input_lock, *flags);
+@@ -2848,7 +2812,8 @@ static int vino_set_data_norm(struct vino_channel_settings *vcs,
+ /* Don't hold spinlocks while setting norm
+ * as it may take a while... */
+
+- err = vino_saa7191_set_norm(&data_norm);
++ norm = vino_data_norms[data_norm].std;
++ err = decoder_call(tuner, s_std, norm);
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, *flags);
+
+@@ -2884,41 +2849,13 @@ static int vino_find_data_format(__u32 pixelformat)
+ return VINO_DATA_FMT_NONE;
+ }
+
+-static int vino_enum_data_norm(struct vino_channel_settings *vcs, __u32 index)
+-{
+- int data_norm = VINO_DATA_NORM_NONE;
+- unsigned long flags;
+-
+- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+- switch(vcs->input) {
+- case VINO_INPUT_COMPOSITE:
+- case VINO_INPUT_SVIDEO:
+- if (index == 0) {
+- data_norm = VINO_DATA_NORM_PAL;
+- } else if (index == 1) {
+- data_norm = VINO_DATA_NORM_NTSC;
+- } else if (index == 2) {
+- data_norm = VINO_DATA_NORM_SECAM;
+- }
+- break;
+- case VINO_INPUT_D1:
+- if (index == 0) {
+- data_norm = VINO_DATA_NORM_D1;
+- }
+- break;
+- }
+- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+-
+- return data_norm;
+-}
+-
+-static int vino_enum_input(struct vino_channel_settings *vcs, __u32 index)
++static int vino_int_enum_input(struct vino_channel_settings *vcs, __u32 index)
+ {
+ int input = VINO_INPUT_NONE;
+ unsigned long flags;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+- if (vino_drvdata->decoder.driver && vino_drvdata->camera.driver) {
++ if (vino_drvdata->decoder && vino_drvdata->camera) {
+ switch (index) {
+ case 0:
+ input = VINO_INPUT_COMPOSITE;
+@@ -2930,7 +2867,7 @@ static int vino_enum_input(struct vino_channel_settings *vcs, __u32 index)
+ input = VINO_INPUT_D1;
+ break;
+ }
+- } else if (vino_drvdata->decoder.driver) {
++ } else if (vino_drvdata->decoder) {
+ switch (index) {
+ case 0:
+ input = VINO_INPUT_COMPOSITE;
+@@ -2939,7 +2876,7 @@ static int vino_enum_input(struct vino_channel_settings *vcs, __u32 index)
+ input = VINO_INPUT_SVIDEO;
+ break;
+ }
+- } else if (vino_drvdata->camera.driver) {
++ } else if (vino_drvdata->camera) {
+ switch (index) {
+ case 0:
+ input = VINO_INPUT_D1;
+@@ -2957,7 +2894,7 @@ static __u32 vino_find_input_index(struct vino_channel_settings *vcs)
+ __u32 index = 0;
+ // FIXME: detect when no inputs available
+
+- if (vino_drvdata->decoder.driver && vino_drvdata->camera.driver) {
++ if (vino_drvdata->decoder && vino_drvdata->camera) {
+ switch (vcs->input) {
+ case VINO_INPUT_COMPOSITE:
+ index = 0;
+@@ -2969,7 +2906,7 @@ static __u32 vino_find_input_index(struct vino_channel_settings *vcs)
+ index = 2;
+ break;
+ }
+- } else if (vino_drvdata->decoder.driver) {
++ } else if (vino_drvdata->decoder) {
+ switch (vcs->input) {
+ case VINO_INPUT_COMPOSITE:
+ index = 0;
+@@ -2978,7 +2915,7 @@ static __u32 vino_find_input_index(struct vino_channel_settings *vcs)
+ index = 1;
+ break;
+ }
+- } else if (vino_drvdata->camera.driver) {
++ } else if (vino_drvdata->camera) {
+ switch (vcs->input) {
+ case VINO_INPUT_D1:
+ index = 0;
+@@ -2991,7 +2928,8 @@ static __u32 vino_find_input_index(struct vino_channel_settings *vcs)
+
+ /* V4L2 ioctls */
+
+-static void vino_v4l2_querycap(struct v4l2_capability *cap)
++static int vino_querycap(struct file *file, void *__fh,
++ struct v4l2_capability *cap)
+ {
+ memset(cap, 0, sizeof(struct v4l2_capability));
+
+@@ -3003,16 +2941,18 @@ static void vino_v4l2_querycap(struct v4l2_capability *cap)
+ V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING;
+ // V4L2_CAP_OVERLAY, V4L2_CAP_READWRITE
++ return 0;
+ }
+
+-static int vino_v4l2_enuminput(struct vino_channel_settings *vcs,
++static int vino_enum_input(struct file *file, void *__fh,
+ struct v4l2_input *i)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ __u32 index = i->index;
+ int input;
+ dprintk("requested index = %d\n", index);
+
+- input = vino_enum_input(vcs, index);
++ input = vino_int_enum_input(vcs, index);
+ if (input == VINO_INPUT_NONE)
+ return -EINVAL;
+
+@@ -3023,20 +2963,15 @@ static int vino_v4l2_enuminput(struct vino_channel_settings *vcs,
+ i->std = vino_inputs[input].std;
+ strcpy(i->name, vino_inputs[input].name);
+
+- if ((input == VINO_INPUT_COMPOSITE)
+- || (input == VINO_INPUT_SVIDEO)) {
+- struct saa7191_status status;
+- i2c_decoder_command(DECODER_SAA7191_GET_STATUS, &status);
+- i->status |= status.signal ? 0 : V4L2_IN_ST_NO_SIGNAL;
+- i->status |= status.color ? 0 : V4L2_IN_ST_NO_COLOR;
+- }
+-
++ if (input == VINO_INPUT_COMPOSITE || input == VINO_INPUT_SVIDEO)
++ decoder_call(video, g_input_status, &i->status);
+ return 0;
+ }
+
+-static int vino_v4l2_g_input(struct vino_channel_settings *vcs,
++static int vino_g_input(struct file *file, void *__fh,
+ unsigned int *i)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ __u32 index;
+ int input;
+ unsigned long flags;
+@@ -3057,52 +2992,24 @@ static int vino_v4l2_g_input(struct vino_channel_settings *vcs,
+ return 0;
+ }
+
+-static int vino_v4l2_s_input(struct vino_channel_settings *vcs,
+- unsigned int *i)
++static int vino_s_input(struct file *file, void *__fh,
++ unsigned int i)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ int input;
+- dprintk("requested input = %d\n", *i);
++ dprintk("requested input = %d\n", i);
+
+- input = vino_enum_input(vcs, *i);
++ input = vino_int_enum_input(vcs, i);
+ if (input == VINO_INPUT_NONE)
+ return -EINVAL;
+
+ return vino_set_input(vcs, input);
+ }
+
+-static int vino_v4l2_enumstd(struct vino_channel_settings *vcs,
+- struct v4l2_standard *s)
+-{
+- int index = s->index;
+- int data_norm;
+-
+- data_norm = vino_enum_data_norm(vcs, index);
+- dprintk("standard index = %d\n", index);
+-
+- if (data_norm == VINO_DATA_NORM_NONE)
+- return -EINVAL;
+-
+- dprintk("standard name = %s\n",
+- vino_data_norms[data_norm].description);
+-
+- memset(s, 0, sizeof(struct v4l2_standard));
+- s->index = index;
+-
+- s->id = vino_data_norms[data_norm].std;
+- s->frameperiod.numerator = 1;
+- s->frameperiod.denominator =
+- vino_data_norms[data_norm].fps_max;
+- s->framelines =
+- vino_data_norms[data_norm].framelines;
+- strcpy(s->name,
+- vino_data_norms[data_norm].description);
+-
+- return 0;
+-}
+-
+-static int vino_v4l2_querystd(struct vino_channel_settings *vcs,
++static int vino_querystd(struct file *file, void *__fh,
+ v4l2_std_id *std)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ int err = 0;
+
+@@ -3114,19 +3021,7 @@ static int vino_v4l2_querystd(struct vino_channel_settings *vcs,
+ break;
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO: {
+- struct saa7191_status status;
+-
+- i2c_decoder_command(DECODER_SAA7191_GET_STATUS, &status);
+-
+- if (status.signal) {
+- if (status.signal_60hz) {
+- *std = V4L2_STD_NTSC;
+- } else {
+- *std = V4L2_STD_PAL | V4L2_STD_SECAM;
+- }
+- } else {
+- *std = vino_inputs[vcs->input].std;
+- }
++ decoder_call(video, querystd, std);
+ break;
+ }
+ default:
+@@ -3138,9 +3033,10 @@ static int vino_v4l2_querystd(struct vino_channel_settings *vcs,
+ return err;
+ }
+
+-static int vino_v4l2_g_std(struct vino_channel_settings *vcs,
++static int vino_g_std(struct file *file, void *__fh,
+ v4l2_std_id *std)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+
+ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+@@ -3153,9 +3049,10 @@ static int vino_v4l2_g_std(struct vino_channel_settings *vcs,
+ return 0;
+ }
+
+-static int vino_v4l2_s_std(struct vino_channel_settings *vcs,
++static int vino_s_std(struct file *file, void *__fh,
+ v4l2_std_id *std)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ int ret = 0;
+
+@@ -3176,12 +3073,7 @@ static int vino_v4l2_s_std(struct vino_channel_settings *vcs,
+ if (vcs->input == VINO_INPUT_D1)
+ goto out;
+
+- if (((*std) & V4L2_STD_PAL)
+- && ((*std) & V4L2_STD_NTSC)
+- && ((*std) & V4L2_STD_SECAM)) {
+- ret = vino_set_data_norm(vcs, VINO_DATA_NORM_AUTO_EXT,
+- &flags);
+- } else if ((*std) & V4L2_STD_PAL) {
++ if ((*std) & V4L2_STD_PAL) {
+ ret = vino_set_data_norm(vcs, VINO_DATA_NORM_PAL,
+ &flags);
+ } else if ((*std) & V4L2_STD_NTSC) {
+@@ -3207,185 +3099,144 @@ out:
+ return ret;
+ }
+
+-static int vino_v4l2_enum_fmt(struct vino_channel_settings *vcs,
++static int vino_enum_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_fmtdesc *fd)
+ {
+- enum v4l2_buf_type type = fd->type;
+- int index = fd->index;
+- dprintk("format index = %d\n", index);
++ dprintk("format index = %d\n", fd->index);
+
+- switch (fd->type) {
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+- if ((fd->index < 0) ||
+- (fd->index >= VINO_DATA_FMT_COUNT))
+- return -EINVAL;
+- dprintk("format name = %s\n",
+- vino_data_formats[index].description);
+-
+- memset(fd, 0, sizeof(struct v4l2_fmtdesc));
+- fd->index = index;
+- fd->type = type;
+- fd->pixelformat = vino_data_formats[index].pixelformat;
+- strcpy(fd->description, vino_data_formats[index].description);
+- break;
+- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+- default:
++ if (fd->index >= VINO_DATA_FMT_COUNT)
+ return -EINVAL;
+- }
++ dprintk("format name = %s\n", vino_data_formats[fd->index].description);
+
++ fd->pixelformat = vino_data_formats[fd->index].pixelformat;
++ strcpy(fd->description, vino_data_formats[fd->index].description);
+ return 0;
+ }
+
+-static int vino_v4l2_try_fmt(struct vino_channel_settings *vcs,
++static int vino_try_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_format *f)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ struct vino_channel_settings tempvcs;
+ unsigned long flags;
++ struct v4l2_pix_format *pf = &f->fmt.pix;
+
+- switch (f->type) {
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+- struct v4l2_pix_format *pf = &f->fmt.pix;
+-
+- dprintk("requested: w = %d, h = %d\n",
+- pf->width, pf->height);
+-
+- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+- memcpy(&tempvcs, vcs, sizeof(struct vino_channel_settings));
+- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
++ dprintk("requested: w = %d, h = %d\n",
++ pf->width, pf->height);
+
+- tempvcs.data_format = vino_find_data_format(pf->pixelformat);
+- if (tempvcs.data_format == VINO_DATA_FMT_NONE) {
+- tempvcs.data_format = VINO_DATA_FMT_GREY;
+- pf->pixelformat =
+- vino_data_formats[tempvcs.data_format].
+- pixelformat;
+- }
++ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
++ memcpy(&tempvcs, vcs, sizeof(struct vino_channel_settings));
++ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+- /* data format must be set before clipping/scaling */
+- vino_set_scaling(&tempvcs, pf->width, pf->height);
++ tempvcs.data_format = vino_find_data_format(pf->pixelformat);
++ if (tempvcs.data_format == VINO_DATA_FMT_NONE) {
++ tempvcs.data_format = VINO_DATA_FMT_GREY;
++ pf->pixelformat =
++ vino_data_formats[tempvcs.data_format].
++ pixelformat;
++ }
+
+- dprintk("data format = %s\n",
+- vino_data_formats[tempvcs.data_format].description);
++ /* data format must be set before clipping/scaling */
++ vino_set_scaling(&tempvcs, pf->width, pf->height);
+
+- pf->width = (tempvcs.clipping.right - tempvcs.clipping.left) /
+- tempvcs.decimation;
+- pf->height = (tempvcs.clipping.bottom - tempvcs.clipping.top) /
+- tempvcs.decimation;
++ dprintk("data format = %s\n",
++ vino_data_formats[tempvcs.data_format].description);
+
+- pf->field = V4L2_FIELD_INTERLACED;
+- pf->bytesperline = tempvcs.line_size;
+- pf->sizeimage = tempvcs.line_size *
+- (tempvcs.clipping.bottom - tempvcs.clipping.top) /
+- tempvcs.decimation;
+- pf->colorspace =
+- vino_data_formats[tempvcs.data_format].colorspace;
++ pf->width = (tempvcs.clipping.right - tempvcs.clipping.left) /
++ tempvcs.decimation;
++ pf->height = (tempvcs.clipping.bottom - tempvcs.clipping.top) /
++ tempvcs.decimation;
+
+- pf->priv = 0;
+- break;
+- }
+- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+- default:
+- return -EINVAL;
+- }
++ pf->field = V4L2_FIELD_INTERLACED;
++ pf->bytesperline = tempvcs.line_size;
++ pf->sizeimage = tempvcs.line_size *
++ (tempvcs.clipping.bottom - tempvcs.clipping.top) /
++ tempvcs.decimation;
++ pf->colorspace =
++ vino_data_formats[tempvcs.data_format].colorspace;
+
++ pf->priv = 0;
+ return 0;
+ }
+
+-static int vino_v4l2_g_fmt(struct vino_channel_settings *vcs,
++static int vino_g_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_format *f)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
++ struct v4l2_pix_format *pf = &f->fmt.pix;
+
+- switch (f->type) {
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+- struct v4l2_pix_format *pf = &f->fmt.pix;
+-
+- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+-
+- pf->width = (vcs->clipping.right - vcs->clipping.left) /
+- vcs->decimation;
+- pf->height = (vcs->clipping.bottom - vcs->clipping.top) /
+- vcs->decimation;
+- pf->pixelformat =
+- vino_data_formats[vcs->data_format].pixelformat;
++ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+- pf->field = V4L2_FIELD_INTERLACED;
+- pf->bytesperline = vcs->line_size;
+- pf->sizeimage = vcs->line_size *
+- (vcs->clipping.bottom - vcs->clipping.top) /
+- vcs->decimation;
+- pf->colorspace =
+- vino_data_formats[vcs->data_format].colorspace;
++ pf->width = (vcs->clipping.right - vcs->clipping.left) /
++ vcs->decimation;
++ pf->height = (vcs->clipping.bottom - vcs->clipping.top) /
++ vcs->decimation;
++ pf->pixelformat =
++ vino_data_formats[vcs->data_format].pixelformat;
+
+- pf->priv = 0;
++ pf->field = V4L2_FIELD_INTERLACED;
++ pf->bytesperline = vcs->line_size;
++ pf->sizeimage = vcs->line_size *
++ (vcs->clipping.bottom - vcs->clipping.top) /
++ vcs->decimation;
++ pf->colorspace =
++ vino_data_formats[vcs->data_format].colorspace;
+
+- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+- break;
+- }
+- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+- default:
+- return -EINVAL;
+- }
++ pf->priv = 0;
+
++ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+ return 0;
+ }
+
+-static int vino_v4l2_s_fmt(struct vino_channel_settings *vcs,
++static int vino_s_fmt_vid_cap(struct file *file, void *__fh,
+ struct v4l2_format *f)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ int data_format;
+ unsigned long flags;
++ struct v4l2_pix_format *pf = &f->fmt.pix;
+
+- switch (f->type) {
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+- struct v4l2_pix_format *pf = &f->fmt.pix;
+-
+- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
++ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+- data_format = vino_find_data_format(pf->pixelformat);
++ data_format = vino_find_data_format(pf->pixelformat);
+
+- if (data_format == VINO_DATA_FMT_NONE) {
+- vcs->data_format = VINO_DATA_FMT_GREY;
+- pf->pixelformat =
+- vino_data_formats[vcs->data_format].
+- pixelformat;
+- } else {
+- vcs->data_format = data_format;
+- }
++ if (data_format == VINO_DATA_FMT_NONE) {
++ vcs->data_format = VINO_DATA_FMT_GREY;
++ pf->pixelformat =
++ vino_data_formats[vcs->data_format].
++ pixelformat;
++ } else {
++ vcs->data_format = data_format;
++ }
+
+- /* data format must be set before clipping/scaling */
+- vino_set_scaling(vcs, pf->width, pf->height);
++ /* data format must be set before clipping/scaling */
++ vino_set_scaling(vcs, pf->width, pf->height);
+
+- dprintk("data format = %s\n",
+- vino_data_formats[vcs->data_format].description);
++ dprintk("data format = %s\n",
++ vino_data_formats[vcs->data_format].description);
+
+- pf->width = vcs->clipping.right - vcs->clipping.left;
+- pf->height = vcs->clipping.bottom - vcs->clipping.top;
++ pf->width = vcs->clipping.right - vcs->clipping.left;
++ pf->height = vcs->clipping.bottom - vcs->clipping.top;
+
+- pf->field = V4L2_FIELD_INTERLACED;
+- pf->bytesperline = vcs->line_size;
+- pf->sizeimage = vcs->line_size *
+- (vcs->clipping.bottom - vcs->clipping.top) /
+- vcs->decimation;
+- pf->colorspace =
+- vino_data_formats[vcs->data_format].colorspace;
++ pf->field = V4L2_FIELD_INTERLACED;
++ pf->bytesperline = vcs->line_size;
++ pf->sizeimage = vcs->line_size *
++ (vcs->clipping.bottom - vcs->clipping.top) /
++ vcs->decimation;
++ pf->colorspace =
++ vino_data_formats[vcs->data_format].colorspace;
+
+- pf->priv = 0;
+-
+- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+- break;
+- }
+- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+- default:
+- return -EINVAL;
+- }
++ pf->priv = 0;
+
++ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+ return 0;
+ }
+
+-static int vino_v4l2_cropcap(struct vino_channel_settings *vcs,
++static int vino_cropcap(struct file *file, void *__fh,
+ struct v4l2_cropcap *ccap)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ const struct vino_data_norm *norm;
+ unsigned long flags;
+
+@@ -3415,9 +3266,10 @@ static int vino_v4l2_cropcap(struct vino_channel_settings *vcs,
+ return 0;
+ }
+
+-static int vino_v4l2_g_crop(struct vino_channel_settings *vcs,
++static int vino_g_crop(struct file *file, void *__fh,
+ struct v4l2_crop *c)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+
+ switch (c->type) {
+@@ -3439,9 +3291,10 @@ static int vino_v4l2_g_crop(struct vino_channel_settings *vcs,
+ return 0;
+ }
+
+-static int vino_v4l2_s_crop(struct vino_channel_settings *vcs,
++static int vino_s_crop(struct file *file, void *__fh,
+ struct v4l2_crop *c)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+
+ switch (c->type) {
+@@ -3461,108 +3314,83 @@ static int vino_v4l2_s_crop(struct vino_channel_settings *vcs,
+ return 0;
+ }
+
+-static int vino_v4l2_g_parm(struct vino_channel_settings *vcs,
++static int vino_g_parm(struct file *file, void *__fh,
+ struct v4l2_streamparm *sp)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
++ struct v4l2_captureparm *cp = &sp->parm.capture;
+
+- switch (sp->type) {
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+- struct v4l2_captureparm *cp = &sp->parm.capture;
+- memset(cp, 0, sizeof(struct v4l2_captureparm));
++ cp->capability = V4L2_CAP_TIMEPERFRAME;
++ cp->timeperframe.numerator = 1;
+
+- cp->capability = V4L2_CAP_TIMEPERFRAME;
+- cp->timeperframe.numerator = 1;
++ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
++ cp->timeperframe.denominator = vcs->fps;
+
+- cp->timeperframe.denominator = vcs->fps;
++ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+
+- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
+-
+- // TODO: cp->readbuffers = xxx;
+- break;
+- }
+- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+- default:
+- return -EINVAL;
+- }
++ /* TODO: cp->readbuffers = xxx; */
+
+ return 0;
+ }
+
+-static int vino_v4l2_s_parm(struct vino_channel_settings *vcs,
++static int vino_s_parm(struct file *file, void *__fh,
+ struct v4l2_streamparm *sp)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
++ struct v4l2_captureparm *cp = &sp->parm.capture;
+
+- switch (sp->type) {
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+- struct v4l2_captureparm *cp = &sp->parm.capture;
+-
+- spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+-
+- if ((cp->timeperframe.numerator == 0) ||
+- (cp->timeperframe.denominator == 0)) {
+- /* reset framerate */
+- vino_set_default_framerate(vcs);
+- } else {
+- vino_set_framerate(vcs, cp->timeperframe.denominator /
+- cp->timeperframe.numerator);
+- }
+-
+- spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
++ spin_lock_irqsave(&vino_drvdata->input_lock, flags);
+
+- // TODO: set buffers according to cp->readbuffers
+- break;
+- }
+- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+- default:
+- return -EINVAL;
++ if ((cp->timeperframe.numerator == 0) ||
++ (cp->timeperframe.denominator == 0)) {
++ /* reset framerate */
++ vino_set_default_framerate(vcs);
++ } else {
++ vino_set_framerate(vcs, cp->timeperframe.denominator /
++ cp->timeperframe.numerator);
+ }
+
++ spin_unlock_irqrestore(&vino_drvdata->input_lock, flags);
++
+ return 0;
+ }
+
+-static int vino_v4l2_reqbufs(struct vino_channel_settings *vcs,
++static int vino_reqbufs(struct file *file, void *__fh,
+ struct v4l2_requestbuffers *rb)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
++
+ if (vcs->reading)
+ return -EBUSY;
+
+- switch (rb->type) {
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+- // TODO: check queue type
+- if (rb->memory != V4L2_MEMORY_MMAP) {
+- dprintk("type not mmap\n");
+- return -EINVAL;
+- }
++ /* TODO: check queue type */
++ if (rb->memory != V4L2_MEMORY_MMAP) {
++ dprintk("type not mmap\n");
++ return -EINVAL;
++ }
+
+- dprintk("count = %d\n", rb->count);
+- if (rb->count > 0) {
+- if (vino_is_capturing(vcs)) {
+- dprintk("busy, capturing\n");
+- return -EBUSY;
+- }
++ dprintk("count = %d\n", rb->count);
++ if (rb->count > 0) {
++ if (vino_is_capturing(vcs)) {
++ dprintk("busy, capturing\n");
++ return -EBUSY;
++ }
+
+- if (vino_queue_has_mapped_buffers(&vcs->fb_queue)) {
+- dprintk("busy, buffers still mapped\n");
+- return -EBUSY;
+- } else {
+- vcs->streaming = 0;
+- vino_queue_free(&vcs->fb_queue);
+- vino_queue_init(&vcs->fb_queue, &rb->count);
+- }
++ if (vino_queue_has_mapped_buffers(&vcs->fb_queue)) {
++ dprintk("busy, buffers still mapped\n");
++ return -EBUSY;
+ } else {
+ vcs->streaming = 0;
+- vino_capture_stop(vcs);
+ vino_queue_free(&vcs->fb_queue);
++ vino_queue_init(&vcs->fb_queue, &rb->count);
+ }
+- break;
+- }
+- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+- default:
+- return -EINVAL;
++ } else {
++ vcs->streaming = 0;
++ vino_capture_stop(vcs);
++ vino_queue_free(&vcs->fb_queue);
+ }
+
+ return 0;
+@@ -3606,156 +3434,135 @@ static void vino_v4l2_get_buffer_status(struct vino_channel_settings *vcs,
+ fb->id, fb->size, fb->data_size, fb->offset);
+ }
+
+-static int vino_v4l2_querybuf(struct vino_channel_settings *vcs,
++static int vino_querybuf(struct file *file, void *__fh,
+ struct v4l2_buffer *b)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
++ struct vino_framebuffer *fb;
++
+ if (vcs->reading)
+ return -EBUSY;
+
+- switch (b->type) {
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+- struct vino_framebuffer *fb;
+-
+- // TODO: check queue type
+- if (b->index >= vino_queue_get_length(&vcs->fb_queue)) {
+- dprintk("invalid index = %d\n",
+- b->index);
+- return -EINVAL;
+- }
+-
+- fb = vino_queue_get_buffer(&vcs->fb_queue,
+- b->index);
+- if (fb == NULL) {
+- dprintk("vino_queue_get_buffer() failed");
+- return -EINVAL;
+- }
+-
+- vino_v4l2_get_buffer_status(vcs, fb, b);
+- break;
++ /* TODO: check queue type */
++ if (b->index >= vino_queue_get_length(&vcs->fb_queue)) {
++ dprintk("invalid index = %d\n",
++ b->index);
++ return -EINVAL;
+ }
+- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+- default:
++
++ fb = vino_queue_get_buffer(&vcs->fb_queue,
++ b->index);
++ if (fb == NULL) {
++ dprintk("vino_queue_get_buffer() failed");
+ return -EINVAL;
+ }
+
++ vino_v4l2_get_buffer_status(vcs, fb, b);
++
+ return 0;
+ }
+
+-static int vino_v4l2_qbuf(struct vino_channel_settings *vcs,
++static int vino_qbuf(struct file *file, void *__fh,
+ struct v4l2_buffer *b)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
++ struct vino_framebuffer *fb;
++ int ret;
++
+ if (vcs->reading)
+ return -EBUSY;
+
+- switch (b->type) {
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+- struct vino_framebuffer *fb;
+- int ret;
+-
+- // TODO: check queue type
+- if (b->memory != V4L2_MEMORY_MMAP) {
+- dprintk("type not mmap\n");
+- return -EINVAL;
+- }
++ /* TODO: check queue type */
++ if (b->memory != V4L2_MEMORY_MMAP) {
++ dprintk("type not mmap\n");
++ return -EINVAL;
++ }
+
+- fb = vino_capture_enqueue(vcs, b->index);
+- if (fb == NULL)
+- return -EINVAL;
++ fb = vino_capture_enqueue(vcs, b->index);
++ if (fb == NULL)
++ return -EINVAL;
+
+- vino_v4l2_get_buffer_status(vcs, fb, b);
++ vino_v4l2_get_buffer_status(vcs, fb, b);
+
+- if (vcs->streaming) {
+- ret = vino_capture_next(vcs, 1);
+- if (ret)
+- return ret;
+- }
+- break;
+- }
+- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+- default:
+- return -EINVAL;
++ if (vcs->streaming) {
++ ret = vino_capture_next(vcs, 1);
++ if (ret)
++ return ret;
+ }
+
+ return 0;
+ }
+
+-static int vino_v4l2_dqbuf(struct vino_channel_settings *vcs,
+- struct v4l2_buffer *b,
+- unsigned int nonblocking)
++static int vino_dqbuf(struct file *file, void *__fh,
++ struct v4l2_buffer *b)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
++ unsigned int nonblocking = file->f_flags & O_NONBLOCK;
++ struct vino_framebuffer *fb;
++ unsigned int incoming, outgoing;
++ int err;
++
+ if (vcs->reading)
+ return -EBUSY;
+
+- switch (b->type) {
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+- struct vino_framebuffer *fb;
+- unsigned int incoming, outgoing;
+- int err;
++ /* TODO: check queue type */
+
+- // TODO: check queue type
++ err = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
++ if (err) {
++ dprintk("vino_queue_get_incoming() failed\n");
++ return -EINVAL;
++ }
++ err = vino_queue_get_outgoing(&vcs->fb_queue, &outgoing);
++ if (err) {
++ dprintk("vino_queue_get_outgoing() failed\n");
++ return -EINVAL;
++ }
+
+- err = vino_queue_get_incoming(&vcs->fb_queue, &incoming);
+- if (err) {
+- dprintk("vino_queue_get_incoming() failed\n");
++ dprintk("incoming = %d, outgoing = %d\n", incoming, outgoing);
++
++ if (outgoing == 0) {
++ if (incoming == 0) {
++ dprintk("no incoming or outgoing buffers\n");
+ return -EINVAL;
+ }
+- err = vino_queue_get_outgoing(&vcs->fb_queue, &outgoing);
+- if (err) {
+- dprintk("vino_queue_get_outgoing() failed\n");
+- return -EINVAL;
++ if (nonblocking) {
++ dprintk("non-blocking I/O was selected and "
++ "there are no buffers to dequeue\n");
++ return -EAGAIN;
+ }
+
+- dprintk("incoming = %d, outgoing = %d\n", incoming, outgoing);
+-
+- if (outgoing == 0) {
+- if (incoming == 0) {
+- dprintk("no incoming or outgoing buffers\n");
+- return -EINVAL;
+- }
+- if (nonblocking) {
+- dprintk("non-blocking I/O was selected and "
+- "there are no buffers to dequeue\n");
+- return -EAGAIN;
+- }
+-
++ err = vino_wait_for_frame(vcs);
++ if (err) {
+ err = vino_wait_for_frame(vcs);
+ if (err) {
+- err = vino_wait_for_frame(vcs);
+- if (err) {
+- /* interrupted or
+- * no frames captured because
+- * of frame skipping */
+- // vino_capture_failed(vcs);
+- return -EIO;
+- }
++ /* interrupted or no frames captured because of
++ * frame skipping */
++ /* vino_capture_failed(vcs); */
++ return -EIO;
+ }
+ }
++ }
+
+- fb = vino_queue_remove(&vcs->fb_queue, &b->index);
+- if (fb == NULL) {
+- dprintk("vino_queue_remove() failed\n");
+- return -EINVAL;
+- }
+-
+- err = vino_check_buffer(vcs, fb);
++ fb = vino_queue_remove(&vcs->fb_queue, &b->index);
++ if (fb == NULL) {
++ dprintk("vino_queue_remove() failed\n");
++ return -EINVAL;
++ }
+
+- vino_v4l2_get_buffer_status(vcs, fb, b);
++ err = vino_check_buffer(vcs, fb);
+
+- if (err)
+- return -EIO;
++ vino_v4l2_get_buffer_status(vcs, fb, b);
+
+- break;
+- }
+- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+- default:
+- return -EINVAL;
+- }
++ if (err)
++ return -EIO;
+
+ return 0;
+ }
+
+-static int vino_v4l2_streamon(struct vino_channel_settings *vcs)
++static int vino_streamon(struct file *file, void *__fh,
++ enum v4l2_buf_type i)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned int incoming;
+ int ret;
+ if (vcs->reading)
+@@ -3792,8 +3599,10 @@ static int vino_v4l2_streamon(struct vino_channel_settings *vcs)
+ return 0;
+ }
+
+-static int vino_v4l2_streamoff(struct vino_channel_settings *vcs)
++static int vino_streamoff(struct file *file, void *__fh,
++ enum v4l2_buf_type i)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ if (vcs->reading)
+ return -EBUSY;
+
+@@ -3806,9 +3615,10 @@ static int vino_v4l2_streamoff(struct vino_channel_settings *vcs)
+ return 0;
+ }
+
+-static int vino_v4l2_queryctrl(struct vino_channel_settings *vcs,
++static int vino_queryctrl(struct file *file, void *__fh,
+ struct v4l2_queryctrl *queryctrl)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ int i;
+ int err = 0;
+@@ -3855,9 +3665,10 @@ static int vino_v4l2_queryctrl(struct vino_channel_settings *vcs,
+ return err;
+ }
+
+-static int vino_v4l2_g_ctrl(struct vino_channel_settings *vcs,
++static int vino_g_ctrl(struct file *file, void *__fh,
+ struct v4l2_control *control)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ int i;
+ int err = 0;
+@@ -3866,56 +3677,38 @@ static int vino_v4l2_g_ctrl(struct vino_channel_settings *vcs,
+
+ switch (vcs->input) {
+ case VINO_INPUT_D1: {
+- struct indycam_control indycam_ctrl;
+-
++ err = -EINVAL;
+ for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
+- if (vino_indycam_v4l2_controls[i].id ==
+- control->id) {
+- goto found1;
++ if (vino_indycam_v4l2_controls[i].id == control->id) {
++ err = 0;
++ break;
+ }
+ }
+
+- err = -EINVAL;
+- goto out;
+-
+-found1:
+- indycam_ctrl.type = vino_indycam_v4l2_controls[i].reserved[0];
+-
+- err = i2c_camera_command(DECODER_INDYCAM_GET_CONTROL,
+- &indycam_ctrl);
+- if (err) {
+- err = -EINVAL;
++ if (err)
+ goto out;
+- }
+
+- control->value = indycam_ctrl.value;
++ err = camera_call(core, g_ctrl, control);
++ if (err)
++ err = -EINVAL;
+ break;
+ }
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO: {
+- struct saa7191_control saa7191_ctrl;
+-
++ err = -EINVAL;
+ for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
+- if (vino_saa7191_v4l2_controls[i].id ==
+- control->id) {
+- goto found2;
++ if (vino_saa7191_v4l2_controls[i].id == control->id) {
++ err = 0;
++ break;
+ }
+ }
+
+- err = -EINVAL;
+- goto out;
+-
+-found2:
+- saa7191_ctrl.type = vino_saa7191_v4l2_controls[i].reserved[0];
+-
+- err = i2c_decoder_command(DECODER_SAA7191_GET_CONTROL,
+- &saa7191_ctrl);
+- if (err) {
+- err = -EINVAL;
++ if (err)
+ goto out;
+- }
+
+- control->value = saa7191_ctrl.value;
++ err = decoder_call(core, g_ctrl, control);
++ if (err)
++ err = -EINVAL;
+ break;
+ }
+ default:
+@@ -3928,9 +3721,10 @@ out:
+ return err;
+ }
+
+-static int vino_v4l2_s_ctrl(struct vino_channel_settings *vcs,
++static int vino_s_ctrl(struct file *file, void *__fh,
+ struct v4l2_control *control)
+ {
++ struct vino_channel_settings *vcs = video_drvdata(file);
+ unsigned long flags;
+ int i;
+ int err = 0;
+@@ -3944,65 +3738,43 @@ static int vino_v4l2_s_ctrl(struct vino_channel_settings *vcs,
+
+ switch (vcs->input) {
+ case VINO_INPUT_D1: {
+- struct indycam_control indycam_ctrl;
+-
++ err = -EINVAL;
+ for (i = 0; i < VINO_INDYCAM_V4L2_CONTROL_COUNT; i++) {
+- if (vino_indycam_v4l2_controls[i].id ==
+- control->id) {
+- if ((control->value >=
+- vino_indycam_v4l2_controls[i].minimum)
+- && (control->value <=
+- vino_indycam_v4l2_controls[i].
+- maximum)) {
+- goto found1;
+- } else {
+- err = -ERANGE;
+- goto out;
+- }
++ if (vino_indycam_v4l2_controls[i].id == control->id) {
++ err = 0;
++ break;
+ }
+ }
+-
+- err = -EINVAL;
+- goto out;
+-
+-found1:
+- indycam_ctrl.type = vino_indycam_v4l2_controls[i].reserved[0];
+- indycam_ctrl.value = control->value;
+-
+- err = i2c_camera_command(DECODER_INDYCAM_SET_CONTROL,
+- &indycam_ctrl);
++ if (err)
++ goto out;
++ if (control->value < vino_indycam_v4l2_controls[i].minimum ||
++ control->value > vino_indycam_v4l2_controls[i].maximum) {
++ err = -ERANGE;
++ goto out;
++ }
++ err = camera_call(core, s_ctrl, control);
+ if (err)
+ err = -EINVAL;
+ break;
+ }
+ case VINO_INPUT_COMPOSITE:
+ case VINO_INPUT_SVIDEO: {
+- struct saa7191_control saa7191_ctrl;
+-
++ err = -EINVAL;
+ for (i = 0; i < VINO_SAA7191_V4L2_CONTROL_COUNT; i++) {
+- if (vino_saa7191_v4l2_controls[i].id ==
+- control->id) {
+- if ((control->value >=
+- vino_saa7191_v4l2_controls[i].minimum)
+- && (control->value <=
+- vino_saa7191_v4l2_controls[i].
+- maximum)) {
+- goto found2;
+- } else {
+- err = -ERANGE;
+- goto out;
+- }
++ if (vino_saa7191_v4l2_controls[i].id == control->id) {
++ err = 0;
++ break;
+ }
+ }
+- err = -EINVAL;
+- goto out;
+-
+-found2:
+- saa7191_ctrl.type = vino_saa7191_v4l2_controls[i].reserved[0];
+- saa7191_ctrl.value = control->value;
++ if (err)
++ goto out;
++ if (control->value < vino_saa7191_v4l2_controls[i].minimum ||
++ control->value > vino_saa7191_v4l2_controls[i].maximum) {
++ err = -ERANGE;
++ goto out;
++ }
+
+- err = i2c_decoder_command(DECODER_SAA7191_SET_CONTROL,
+- &saa7191_ctrl);
++ err = decoder_call(core, s_ctrl, control);
+ if (err)
+ err = -EINVAL;
+ break;
+@@ -4233,116 +4005,9 @@ over:
+ ret = POLLIN | POLLRDNORM;
+
+ error:
+-
+ return ret;
+ }
+
+-static long vino_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+-{
+- struct vino_channel_settings *vcs = video_drvdata(file);
+-
+-#ifdef VINO_DEBUG
+- switch (_IOC_TYPE(cmd)) {
+- case 'v':
+- dprintk("ioctl(): V4L1 unsupported (0x%08x)\n", cmd);
+- break;
+- case 'V':
+- dprintk("ioctl(): V4L2 %s (0x%08x)\n",
+- v4l2_ioctl_names[_IOC_NR(cmd)], cmd);
+- break;
+- default:
+- dprintk("ioctl(): unsupported command 0x%08x\n", cmd);
+- }
+-#endif
+-
+- switch (cmd) {
+- /* V4L2 interface */
+- case VIDIOC_QUERYCAP: {
+- vino_v4l2_querycap(arg);
+- break;
+- }
+- case VIDIOC_ENUMINPUT: {
+- return vino_v4l2_enuminput(vcs, arg);
+- }
+- case VIDIOC_G_INPUT: {
+- return vino_v4l2_g_input(vcs, arg);
+- }
+- case VIDIOC_S_INPUT: {
+- return vino_v4l2_s_input(vcs, arg);
+- }
+- case VIDIOC_ENUMSTD: {
+- return vino_v4l2_enumstd(vcs, arg);
+- }
+- case VIDIOC_QUERYSTD: {
+- return vino_v4l2_querystd(vcs, arg);
+- }
+- case VIDIOC_G_STD: {
+- return vino_v4l2_g_std(vcs, arg);
+- }
+- case VIDIOC_S_STD: {
+- return vino_v4l2_s_std(vcs, arg);
+- }
+- case VIDIOC_ENUM_FMT: {
+- return vino_v4l2_enum_fmt(vcs, arg);
+- }
+- case VIDIOC_TRY_FMT: {
+- return vino_v4l2_try_fmt(vcs, arg);
+- }
+- case VIDIOC_G_FMT: {
+- return vino_v4l2_g_fmt(vcs, arg);
+- }
+- case VIDIOC_S_FMT: {
+- return vino_v4l2_s_fmt(vcs, arg);
+- }
+- case VIDIOC_CROPCAP: {
+- return vino_v4l2_cropcap(vcs, arg);
+- }
+- case VIDIOC_G_CROP: {
+- return vino_v4l2_g_crop(vcs, arg);
+- }
+- case VIDIOC_S_CROP: {
+- return vino_v4l2_s_crop(vcs, arg);
+- }
+- case VIDIOC_G_PARM: {
+- return vino_v4l2_g_parm(vcs, arg);
+- }
+- case VIDIOC_S_PARM: {
+- return vino_v4l2_s_parm(vcs, arg);
+- }
+- case VIDIOC_REQBUFS: {
+- return vino_v4l2_reqbufs(vcs, arg);
+- }
+- case VIDIOC_QUERYBUF: {
+- return vino_v4l2_querybuf(vcs, arg);
+- }
+- case VIDIOC_QBUF: {
+- return vino_v4l2_qbuf(vcs, arg);
+- }
+- case VIDIOC_DQBUF: {
+- return vino_v4l2_dqbuf(vcs, arg, file->f_flags & O_NONBLOCK);
+- }
+- case VIDIOC_STREAMON: {
+- return vino_v4l2_streamon(vcs);
+- }
+- case VIDIOC_STREAMOFF: {
+- return vino_v4l2_streamoff(vcs);
+- }
+- case VIDIOC_QUERYCTRL: {
+- return vino_v4l2_queryctrl(vcs, arg);
+- }
+- case VIDIOC_G_CTRL: {
+- return vino_v4l2_g_ctrl(vcs, arg);
+- }
+- case VIDIOC_S_CTRL: {
+- return vino_v4l2_s_ctrl(vcs, arg);
+- }
+- default:
+- return -ENOIOCTLCMD;
+- }
+-
+- return 0;
+-}
+-
+ static long vino_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+ {
+@@ -4352,7 +4017,7 @@ static long vino_ioctl(struct file *file,
+ if (mutex_lock_interruptible(&vcs->mutex))
+ return -EINTR;
+
+- ret = video_usercopy(file, cmd, arg, vino_do_ioctl);
++ ret = video_ioctl2(file, cmd, arg);
+
+ mutex_unlock(&vcs->mutex);
+
+@@ -4364,45 +4029,75 @@ static long vino_ioctl(struct file *file,
+ /* __initdata */
+ static int vino_init_stage;
+
++const struct v4l2_ioctl_ops vino_ioctl_ops = {
++ .vidioc_enum_fmt_vid_cap = vino_enum_fmt_vid_cap,
++ .vidioc_g_fmt_vid_cap = vino_g_fmt_vid_cap,
++ .vidioc_s_fmt_vid_cap = vino_s_fmt_vid_cap,
++ .vidioc_try_fmt_vid_cap = vino_try_fmt_vid_cap,
++ .vidioc_querycap = vino_querycap,
++ .vidioc_enum_input = vino_enum_input,
++ .vidioc_g_input = vino_g_input,
++ .vidioc_s_input = vino_s_input,
++ .vidioc_g_std = vino_g_std,
++ .vidioc_s_std = vino_s_std,
++ .vidioc_querystd = vino_querystd,
++ .vidioc_cropcap = vino_cropcap,
++ .vidioc_s_crop = vino_s_crop,
++ .vidioc_g_crop = vino_g_crop,
++ .vidioc_s_parm = vino_s_parm,
++ .vidioc_g_parm = vino_g_parm,
++ .vidioc_reqbufs = vino_reqbufs,
++ .vidioc_querybuf = vino_querybuf,
++ .vidioc_qbuf = vino_qbuf,
++ .vidioc_dqbuf = vino_dqbuf,
++ .vidioc_streamon = vino_streamon,
++ .vidioc_streamoff = vino_streamoff,
++ .vidioc_queryctrl = vino_queryctrl,
++ .vidioc_g_ctrl = vino_g_ctrl,
++ .vidioc_s_ctrl = vino_s_ctrl,
++};
++
+ static const struct v4l2_file_operations vino_fops = {
+ .owner = THIS_MODULE,
+ .open = vino_open,
+ .release = vino_close,
+- .ioctl = vino_ioctl,
++ .unlocked_ioctl = vino_ioctl,
+ .mmap = vino_mmap,
+ .poll = vino_poll,
+ };
+
+-static struct video_device v4l_device_template = {
++static struct video_device vdev_template = {
+ .name = "NOT SET",
+ .fops = &vino_fops,
++ .ioctl_ops = &vino_ioctl_ops,
++ .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
+ .minor = -1,
+ };
+
+ static void vino_module_cleanup(int stage)
+ {
+ switch(stage) {
++ case 11:
++ video_unregister_device(vino_drvdata->b.vdev);
++ vino_drvdata->b.vdev = NULL;
+ case 10:
+- video_unregister_device(vino_drvdata->b.v4l_device);
+- vino_drvdata->b.v4l_device = NULL;
++ video_unregister_device(vino_drvdata->a.vdev);
++ vino_drvdata->a.vdev = NULL;
+ case 9:
+- video_unregister_device(vino_drvdata->a.v4l_device);
+- vino_drvdata->a.v4l_device = NULL;
++ i2c_del_adapter(&vino_i2c_adapter);
+ case 8:
+- vino_i2c_del_bus();
+- case 7:
+ free_irq(SGI_VINO_IRQ, NULL);
++ case 7:
++ if (vino_drvdata->b.vdev) {
++ video_device_release(vino_drvdata->b.vdev);
++ vino_drvdata->b.vdev = NULL;
++ }
+ case 6:
+- if (vino_drvdata->b.v4l_device) {
+- video_device_release(vino_drvdata->b.v4l_device);
+- vino_drvdata->b.v4l_device = NULL;
++ if (vino_drvdata->a.vdev) {
++ video_device_release(vino_drvdata->a.vdev);
++ vino_drvdata->a.vdev = NULL;
+ }
+ case 5:
+- if (vino_drvdata->a.v4l_device) {
+- video_device_release(vino_drvdata->a.v4l_device);
+- vino_drvdata->a.v4l_device = NULL;
+- }
+- case 4:
+ /* all entries in dma_cpu dummy table have the same address */
+ dma_unmap_single(NULL,
+ vino_drvdata->dummy_desc_table.dma_cpu[0],
+@@ -4412,8 +4107,10 @@ static void vino_module_cleanup(int stage)
+ (void *)vino_drvdata->
+ dummy_desc_table.dma_cpu,
+ vino_drvdata->dummy_desc_table.dma);
+- case 3:
++ case 4:
+ free_page(vino_drvdata->dummy_page);
++ case 3:
++ v4l2_device_unregister(&vino_drvdata->v4l2_dev);
+ case 2:
+ kfree(vino_drvdata);
+ case 1:
+@@ -4468,6 +4165,7 @@ static int vino_probe(void)
+ static int vino_init(void)
+ {
+ dma_addr_t dma_dummy_address;
++ int err;
+ int i;
+
+ vino_drvdata = kzalloc(sizeof(struct vino_settings), GFP_KERNEL);
+@@ -4476,6 +4174,12 @@ static int vino_init(void)
+ return -ENOMEM;
+ }
+ vino_init_stage++;
++ strlcpy(vino_drvdata->v4l2_dev.name, "vino",
++ sizeof(vino_drvdata->v4l2_dev.name));
++ err = v4l2_device_register(NULL, &vino_drvdata->v4l2_dev);
++ if (err)
++ return err;
++ vino_init_stage++;
+
+ /* create a dummy dma descriptor */
+ vino_drvdata->dummy_page = get_zeroed_page(GFP_KERNEL | GFP_DMA);
+@@ -4542,25 +4246,27 @@ static int vino_init_channel_settings(struct vino_channel_settings *vcs,
+ spin_lock_init(&vcs->fb_queue.queue_lock);
+ init_waitqueue_head(&vcs->fb_queue.frame_wait_queue);
+
+- vcs->v4l_device = video_device_alloc();
+- if (!vcs->v4l_device) {
++ vcs->vdev = video_device_alloc();
++ if (!vcs->vdev) {
+ vino_module_cleanup(vino_init_stage);
+ return -ENOMEM;
+ }
+ vino_init_stage++;
+
+- memcpy(vcs->v4l_device, &v4l_device_template,
++ memcpy(vcs->vdev, &vdev_template,
+ sizeof(struct video_device));
+- strcpy(vcs->v4l_device->name, name);
+- vcs->v4l_device->release = video_device_release;
++ strcpy(vcs->vdev->name, name);
++ vcs->vdev->release = video_device_release;
++ vcs->vdev->v4l2_dev = &vino_drvdata->v4l2_dev;
+
+- video_set_drvdata(vcs->v4l_device, vcs);
++ video_set_drvdata(vcs->vdev, vcs);
+
+ return 0;
+ }
+
+ static int __init vino_module_init(void)
+ {
++ unsigned short addr[] = { 0, I2C_CLIENT_END };
+ int ret;
+
+ printk(KERN_INFO "SGI VINO driver version %s\n",
+@@ -4580,12 +4286,12 @@ static int __init vino_module_init(void)
+ spin_lock_init(&vino_drvdata->input_lock);
+
+ ret = vino_init_channel_settings(&vino_drvdata->a, VINO_CHANNEL_A,
+- vino_v4l_device_name_a);
++ vino_vdev_name_a);
+ if (ret)
+ return ret;
+
+ ret = vino_init_channel_settings(&vino_drvdata->b, VINO_CHANNEL_B,
+- vino_v4l_device_name_b);
++ vino_vdev_name_b);
+ if (ret)
+ return ret;
+
+@@ -4601,15 +4307,16 @@ static int __init vino_module_init(void)
+ }
+ vino_init_stage++;
+
+- ret = vino_i2c_add_bus();
++ ret = i2c_add_adapter(&vino_i2c_adapter);
+ if (ret) {
+ printk(KERN_ERR "VINO I2C bus registration failed\n");
+ vino_module_cleanup(vino_init_stage);
+ return ret;
+ }
++ i2c_set_adapdata(&vino_i2c_adapter, &vino_drvdata->v4l2_dev);
+ vino_init_stage++;
+
+- ret = video_register_device(vino_drvdata->a.v4l_device,
++ ret = video_register_device(vino_drvdata->a.vdev,
+ VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ printk(KERN_ERR "VINO channel A Video4Linux-device "
+@@ -4619,7 +4326,7 @@ static int __init vino_module_init(void)
+ }
+ vino_init_stage++;
+
+- ret = video_register_device(vino_drvdata->b.v4l_device,
++ ret = video_register_device(vino_drvdata->b.vdev,
+ VFL_TYPE_GRABBER, -1);
+ if (ret < 0) {
+ printk(KERN_ERR "VINO channel B Video4Linux-device "
+@@ -4629,10 +4336,12 @@ static int __init vino_module_init(void)
+ }
+ vino_init_stage++;
+
+-#ifdef MODULE
+- request_module("saa7191");
+- request_module("indycam");
+-#endif
++ addr[0] = 0x45;
++ vino_drvdata->decoder = v4l2_i2c_new_probed_subdev(&vino_i2c_adapter,
++ "saa7191", "saa7191", addr);
++ addr[0] = 0x2b;
++ vino_drvdata->camera = v4l2_i2c_new_probed_subdev(&vino_i2c_adapter,
++ "indycam", "indycam", addr);
+
+ dprintk("init complete!\n");
+
+diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
+index 81d5aa5..fbfefae 100644
+--- a/drivers/media/video/vivi.c
++++ b/drivers/media/video/vivi.c
+@@ -28,17 +28,14 @@
+ #include <linux/mutex.h>
+ #include <linux/videodev2.h>
+ #include <linux/dma-mapping.h>
+-#ifdef CONFIG_VIDEO_V4L1_COMPAT
+-/* Include V4L1 specific functions. Should be removed soon */
+-#include <linux/videodev.h>
+-#endif
+ #include <linux/interrupt.h>
+-#include <media/videobuf-vmalloc.h>
+-#include <media/v4l2-common.h>
+-#include <media/v4l2-ioctl.h>
+ #include <linux/kthread.h>
+ #include <linux/highmem.h>
+ #include <linux/freezer.h>
++#include <media/videobuf-vmalloc.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-ioctl.h>
++#include "font.h"
+
+ #define VIVI_MODULE_NAME "vivi"
+
+@@ -47,18 +44,32 @@
+ #define WAKE_DENOMINATOR 1001
+ #define BUFFER_TIMEOUT msecs_to_jiffies(500) /* 0.5 seconds */
+
+-#include "font.h"
+-
+ #define VIVI_MAJOR_VERSION 0
+-#define VIVI_MINOR_VERSION 5
++#define VIVI_MINOR_VERSION 6
+ #define VIVI_RELEASE 0
+ #define VIVI_VERSION \
+ KERNEL_VERSION(VIVI_MAJOR_VERSION, VIVI_MINOR_VERSION, VIVI_RELEASE)
+
+-/* Declare static vars that will be used as parameters */
+-static unsigned int vid_limit = 16; /* Video memory limit, in Mb */
+-static int video_nr = -1; /* /dev/videoN, -1 for autodetect */
+-static int n_devs = 1; /* Number of virtual devices */
++MODULE_DESCRIPTION("Video Technology Magazine Virtual Video Capture Board");
++MODULE_AUTHOR("Mauro Carvalho Chehab, Ted Walther and John Sokol");
++MODULE_LICENSE("Dual BSD/GPL");
++
++static unsigned video_nr = -1;
++module_param(video_nr, uint, 0644);
++MODULE_PARM_DESC(video_nr, "videoX start number, -1 is autodetect");
++
++static unsigned n_devs = 1;
++module_param(n_devs, uint, 0644);
++MODULE_PARM_DESC(n_devs, "number of video devices to create");
++
++static unsigned debug;
++module_param(debug, uint, 0644);
++MODULE_PARM_DESC(debug, "activates debug info");
++
++static unsigned int vid_limit = 16;
++module_param(vid_limit, uint, 0644);
++MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
++
+
+ /* supported controls */
+ static struct v4l2_queryctrl vivi_qctrl[] = {
+@@ -69,7 +80,7 @@ static struct v4l2_queryctrl vivi_qctrl[] = {
+ .maximum = 65535,
+ .step = 65535/100,
+ .default_value = 65535,
+- .flags = 0,
++ .flags = V4L2_CTRL_FLAG_SLIDER,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }, {
+ .id = V4L2_CID_BRIGHTNESS,
+@@ -79,7 +90,7 @@ static struct v4l2_queryctrl vivi_qctrl[] = {
+ .maximum = 255,
+ .step = 1,
+ .default_value = 127,
+- .flags = 0,
++ .flags = V4L2_CTRL_FLAG_SLIDER,
+ }, {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+@@ -88,7 +99,7 @@ static struct v4l2_queryctrl vivi_qctrl[] = {
+ .maximum = 255,
+ .step = 0x1,
+ .default_value = 0x10,
+- .flags = 0,
++ .flags = V4L2_CTRL_FLAG_SLIDER,
+ }, {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+@@ -97,7 +108,7 @@ static struct v4l2_queryctrl vivi_qctrl[] = {
+ .maximum = 255,
+ .step = 0x1,
+ .default_value = 127,
+- .flags = 0,
++ .flags = V4L2_CTRL_FLAG_SLIDER,
+ }, {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+@@ -106,17 +117,12 @@ static struct v4l2_queryctrl vivi_qctrl[] = {
+ .maximum = 127,
+ .step = 0x1,
+ .default_value = 0,
+- .flags = 0,
++ .flags = V4L2_CTRL_FLAG_SLIDER,
+ }
+ };
+
+-static int qctl_regs[ARRAY_SIZE(vivi_qctrl)];
+-
+-#define dprintk(dev, level, fmt, arg...) \
+- do { \
+- if (dev->vfd->debug >= (level)) \
+- printk(KERN_DEBUG "vivi: " fmt , ## arg); \
+- } while (0)
++#define dprintk(dev, level, fmt, arg...) \
++ v4l2_dbg(level, debug, &dev->v4l2_dev, fmt, ## arg)
+
+ /* ------------------------------------------------------------------
+ Basic structures
+@@ -206,6 +212,7 @@ static LIST_HEAD(vivi_devlist);
+
+ struct vivi_dev {
+ struct list_head vivi_devlist;
++ struct v4l2_device v4l2_dev;
+
+ spinlock_t slock;
+ struct mutex mutex;
+@@ -223,6 +230,12 @@ struct vivi_dev {
+ char timestr[13];
+
+ int mv_count; /* Controls bars movement */
++
++ /* Input Number */
++ int input;
++
++ /* Control 'registers' */
++ int qctl_regs[ARRAY_SIZE(vivi_qctrl)];
+ };
+
+ struct vivi_fh {
+@@ -235,6 +248,7 @@ struct vivi_fh {
+
+ enum v4l2_buf_type type;
+ unsigned char bars[8][3];
++ int input; /* Input Number on bars */
+ };
+
+ /* ------------------------------------------------------------------
+@@ -254,18 +268,72 @@ enum colors {
+ BLACK,
+ };
+
+-static u8 bars[8][3] = {
+ /* R G B */
+- {204, 204, 204}, /* white */
+- {208, 208, 0}, /* ambar */
+- { 0, 206, 206}, /* cyan */
+- { 0, 239, 0}, /* green */
+- {239, 0, 239}, /* magenta */
+- {205, 0, 0}, /* red */
+- { 0, 0, 255}, /* blue */
+- { 0, 0, 0}, /* black */
++#define COLOR_WHITE {204, 204, 204}
++#define COLOR_AMBAR {208, 208, 0}
++#define COLOR_CIAN { 0, 206, 206}
++#define COLOR_GREEN { 0, 239, 0}
++#define COLOR_MAGENTA {239, 0, 239}
++#define COLOR_RED {205, 0, 0}
++#define COLOR_BLUE { 0, 0, 255}
++#define COLOR_BLACK { 0, 0, 0}
++
++struct bar_std {
++ u8 bar[8][3];
+ };
+
++/* Maximum number of bars are 10 - otherwise, the input print code
++ should be modified */
++static struct bar_std bars[] = {
++ { /* Standard ITU-R color bar sequence */
++ {
++ COLOR_WHITE,
++ COLOR_AMBAR,
++ COLOR_CIAN,
++ COLOR_GREEN,
++ COLOR_MAGENTA,
++ COLOR_RED,
++ COLOR_BLUE,
++ COLOR_BLACK,
++ }
++ }, {
++ {
++ COLOR_WHITE,
++ COLOR_AMBAR,
++ COLOR_BLACK,
++ COLOR_WHITE,
++ COLOR_AMBAR,
++ COLOR_BLACK,
++ COLOR_WHITE,
++ COLOR_AMBAR,
++ }
++ }, {
++ {
++ COLOR_WHITE,
++ COLOR_CIAN,
++ COLOR_BLACK,
++ COLOR_WHITE,
++ COLOR_CIAN,
++ COLOR_BLACK,
++ COLOR_WHITE,
++ COLOR_CIAN,
++ }
++ }, {
++ {
++ COLOR_WHITE,
++ COLOR_GREEN,
++ COLOR_BLACK,
++ COLOR_WHITE,
++ COLOR_GREEN,
++ COLOR_BLACK,
++ COLOR_WHITE,
++ COLOR_GREEN,
++ }
++ },
++};
++
++#define NUM_INPUTS ARRAY_SIZE(bars)
++
+ #define TO_Y(r, g, b) \
+ (((16829 * r + 33039 * g + 6416 * b + 32768) >> 16) + 16)
+ /* RGB to V(Cr) Color transform */
+@@ -275,9 +343,10 @@ static u8 bars[8][3] = {
+ #define TO_U(r, g, b) \
+ (((-9714 * r - 19070 * g + 28784 * b + 32768) >> 16) + 128)
+
+-#define TSTAMP_MIN_Y 24
+-#define TSTAMP_MAX_Y TSTAMP_MIN_Y+15
+-#define TSTAMP_MIN_X 64
++#define TSTAMP_MIN_Y 24
++#define TSTAMP_MAX_Y (TSTAMP_MIN_Y + 15)
++#define TSTAMP_INPUT_X 10
++#define TSTAMP_MIN_X (54 + TSTAMP_INPUT_X)
+
+ static void gen_twopix(struct vivi_fh *fh, unsigned char *buf, int colorpos)
+ {
+@@ -392,9 +461,29 @@ static void gen_line(struct vivi_fh *fh, char *basep, int inipos, int wmax,
+ pos += 4; /* only 16 bpp supported for now */
+ }
+
+- /* Checks if it is possible to show timestamp */
++ /* Prints input entry number */
++
++ /* Checks if it is possible to input number */
+ if (TSTAMP_MAX_Y >= hmax)
+ goto end;
++
++ if (TSTAMP_INPUT_X + strlen(timestr) >= wmax)
++ goto end;
++
++ if (line >= TSTAMP_MIN_Y && line <= TSTAMP_MAX_Y) {
++ chr = rom8x16_bits[fh->input * 16 + line - TSTAMP_MIN_Y];
++ pos = TSTAMP_INPUT_X;
++ for (i = 0; i < 7; i++) {
++ /* Draw white font on black background */
++ if (chr & 1 << (7 - i))
++ gen_twopix(fh, basep + pos, WHITE);
++ else
++ gen_twopix(fh, basep + pos, BLACK);
++ pos += 2;
++ }
++ }
++
++ /* Checks if it is possible to show timestamp */
+ if (TSTAMP_MIN_X + strlen(timestr) >= wmax)
+ goto end;
+
+@@ -577,7 +666,7 @@ static int vivi_start_thread(struct vivi_fh *fh)
+ dma_q->kthread = kthread_run(vivi_thread, fh, "vivi");
+
+ if (IS_ERR(dma_q->kthread)) {
+- printk(KERN_ERR "vivi: kernel_thread() failed\n");
++ v4l2_err(&dev->v4l2_dev, "kernel_thread() failed\n");
+ return PTR_ERR(dma_q->kthread);
+ }
+ /* Wakes thread */
+@@ -720,8 +809,12 @@ static struct videobuf_queue_ops vivi_video_qops = {
+ static int vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+ {
++ struct vivi_fh *fh = priv;
++ struct vivi_dev *dev = fh->dev;
++
+ strcpy(cap->driver, "vivi");
+ strcpy(cap->card, "vivi");
++ strlcpy(cap->bus_info, dev->v4l2_dev.name, sizeof(cap->bus_info));
+ cap->version = VIVI_VERSION;
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING |
+@@ -807,38 +900,19 @@ static int vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ return 0;
+ }
+
+-/*FIXME: This seems to be generic enough to be at videodev2 */
+-static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+- struct v4l2_format *f)
++/* precalculate color bar values to speed up rendering */
++static void precalculate_bars(struct vivi_fh *fh)
+ {
+- struct vivi_fh *fh = priv;
+- struct videobuf_queue *q = &fh->vb_vidq;
++ struct vivi_dev *dev = fh->dev;
+ unsigned char r, g, b;
+ int k, is_yuv;
+
+- int ret = vidioc_try_fmt_vid_cap(file, fh, f);
+- if (ret < 0)
+- return (ret);
+-
+- mutex_lock(&q->vb_lock);
+-
+- if (videobuf_queue_is_busy(&fh->vb_vidq)) {
+- dprintk(fh->dev, 1, "%s queue busy\n", __func__);
+- ret = -EBUSY;
+- goto out;
+- }
+-
+- fh->fmt = get_format(f);
+- fh->width = f->fmt.pix.width;
+- fh->height = f->fmt.pix.height;
+- fh->vb_vidq.field = f->fmt.pix.field;
+- fh->type = f->type;
++ fh->input = dev->input;
+
+- /* precalculate color bar values to speed up rendering */
+ for (k = 0; k < 8; k++) {
+- r = bars[k][0];
+- g = bars[k][1];
+- b = bars[k][2];
++ r = bars[fh->input].bar[k][0];
++ g = bars[fh->input].bar[k][1];
++ b = bars[fh->input].bar[k][2];
+ is_yuv = 0;
+
+ switch (fh->fmt->fourcc) {
+@@ -871,11 +945,40 @@ static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ }
+ }
+
++}
++
++/*FIXME: This seems to be generic enough to be at videodev2 */
++static int vidioc_s_fmt_vid_cap(struct file *file, void *priv,
++ struct v4l2_format *f)
++{
++ struct vivi_fh *fh = priv;
++ struct videobuf_queue *q = &fh->vb_vidq;
++
++ int ret = vidioc_try_fmt_vid_cap(file, fh, f);
++ if (ret < 0)
++ return ret;
++
++ mutex_lock(&q->vb_lock);
++
++ if (videobuf_queue_is_busy(&fh->vb_vidq)) {
++ dprintk(fh->dev, 1, "%s queue busy\n", __func__);
++ ret = -EBUSY;
++ goto out;
++ }
++
++ fh->fmt = get_format(f);
++ fh->width = f->fmt.pix.width;
++ fh->height = f->fmt.pix.height;
++ fh->vb_vidq.field = f->fmt.pix.field;
++ fh->type = f->type;
++
++ precalculate_bars(fh);
++
+ ret = 0;
+ out:
+ mutex_unlock(&q->vb_lock);
+
+- return (ret);
++ return ret;
+ }
+
+ static int vidioc_reqbufs(struct file *file, void *priv,
+@@ -950,27 +1053,36 @@ static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i)
+ static int vidioc_enum_input(struct file *file, void *priv,
+ struct v4l2_input *inp)
+ {
+- if (inp->index != 0)
++ if (inp->index >= NUM_INPUTS)
+ return -EINVAL;
+
+ inp->type = V4L2_INPUT_TYPE_CAMERA;
+ inp->std = V4L2_STD_525_60;
+- strcpy(inp->name, "Camera");
++ sprintf(inp->name, "Camera %u", inp->index);
+
+ return (0);
+ }
+
+ static int vidioc_g_input(struct file *file, void *priv, unsigned int *i)
+ {
+- *i = 0;
++ struct vivi_fh *fh = priv;
++ struct vivi_dev *dev = fh->dev;
++
++ *i = dev->input;
+
+ return (0);
+ }
+ static int vidioc_s_input(struct file *file, void *priv, unsigned int i)
+ {
+- if (i > 0)
++ struct vivi_fh *fh = priv;
++ struct vivi_dev *dev = fh->dev;
++
++ if (i >= NUM_INPUTS)
+ return -EINVAL;
+
++ dev->input = i;
++ precalculate_bars(fh);
++
+ return (0);
+ }
+
+@@ -993,12 +1105,14 @@ static int vidioc_queryctrl(struct file *file, void *priv,
+ static int vidioc_g_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
++ struct vivi_fh *fh = priv;
++ struct vivi_dev *dev = fh->dev;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++)
+ if (ctrl->id == vivi_qctrl[i].id) {
+- ctrl->value = qctl_regs[i];
+- return (0);
++ ctrl->value = dev->qctl_regs[i];
++ return 0;
+ }
+
+ return -EINVAL;
+@@ -1006,16 +1120,18 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
+ static int vidioc_s_ctrl(struct file *file, void *priv,
+ struct v4l2_control *ctrl)
+ {
++ struct vivi_fh *fh = priv;
++ struct vivi_dev *dev = fh->dev;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++)
+ if (ctrl->id == vivi_qctrl[i].id) {
+- if (ctrl->value < vivi_qctrl[i].minimum
+- || ctrl->value > vivi_qctrl[i].maximum) {
+- return (-ERANGE);
+- }
+- qctl_regs[i] = ctrl->value;
+- return (0);
++ if (ctrl->value < vivi_qctrl[i].minimum ||
++ ctrl->value > vivi_qctrl[i].maximum) {
++ return -ERANGE;
++ }
++ dev->qctl_regs[i] = ctrl->value;
++ return 0;
+ }
+ return -EINVAL;
+ }
+@@ -1026,32 +1142,20 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
+
+ static int vivi_open(struct file *file)
+ {
+- int minor = video_devdata(file)->minor;
+- struct vivi_dev *dev;
++ struct vivi_dev *dev = video_drvdata(file);
+ struct vivi_fh *fh = NULL;
+- int i;
+ int retval = 0;
+
+- printk(KERN_DEBUG "vivi: open called (minor=%d)\n", minor);
+-
+- lock_kernel();
+- list_for_each_entry(dev, &vivi_devlist, vivi_devlist)
+- if (dev->vfd->minor == minor)
+- goto found;
+- unlock_kernel();
+- return -ENODEV;
+-
+-found:
+ mutex_lock(&dev->mutex);
+ dev->users++;
+
+ if (dev->users > 1) {
+ dev->users--;
+- retval = -EBUSY;
+- goto unlock;
++ mutex_unlock(&dev->mutex);
++ return -EBUSY;
+ }
+
+- dprintk(dev, 1, "open minor=%d type=%s users=%d\n", minor,
++ dprintk(dev, 1, "open /dev/video%d type=%s users=%d\n", dev->vfd->num,
+ v4l2_type_names[V4L2_BUF_TYPE_VIDEO_CAPTURE], dev->users);
+
+ /* allocate + initialize per filehandle data */
+@@ -1059,14 +1163,11 @@ found:
+ if (NULL == fh) {
+ dev->users--;
+ retval = -ENOMEM;
+- goto unlock;
+ }
+-unlock:
+ mutex_unlock(&dev->mutex);
+- if (retval) {
+- unlock_kernel();
++
++ if (retval)
+ return retval;
+- }
+
+ file->private_data = fh;
+ fh->dev = dev;
+@@ -1076,10 +1177,6 @@ unlock:
+ fh->width = 640;
+ fh->height = 480;
+
+- /* Put all controls at a sane state */
+- for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++)
+- qctl_regs[i] = vivi_qctrl[i].default_value;
+-
+ /* Resets frame counters */
+ dev->h = 0;
+ dev->m = 0;
+@@ -1095,7 +1192,6 @@ unlock:
+ sizeof(struct vivi_buffer), fh);
+
+ vivi_start_thread(fh);
+- unlock_kernel();
+
+ return 0;
+ }
+@@ -1151,32 +1247,6 @@ static int vivi_close(struct file *file)
+ return 0;
+ }
+
+-static int vivi_release(void)
+-{
+- struct vivi_dev *dev;
+- struct list_head *list;
+-
+- while (!list_empty(&vivi_devlist)) {
+- list = vivi_devlist.next;
+- list_del(list);
+- dev = list_entry(list, struct vivi_dev, vivi_devlist);
+-
+- if (-1 != dev->vfd->minor) {
+- printk(KERN_INFO "%s: unregistering /dev/video%d\n",
+- VIVI_MODULE_NAME, dev->vfd->num);
+- video_unregister_device(dev->vfd);
+- } else {
+- printk(KERN_INFO "%s: releasing /dev/video%d\n",
+- VIVI_MODULE_NAME, dev->vfd->num);
+- video_device_release(dev->vfd);
+- }
+-
+- kfree(dev);
+- }
+-
+- return 0;
+-}
+-
+ static int vivi_mmap(struct file *file, struct vm_area_struct *vma)
+ {
+ struct vivi_fh *fh = file->private_data;
+@@ -1239,87 +1309,130 @@ static struct video_device vivi_template = {
+ .tvnorms = V4L2_STD_525_60,
+ .current_norm = V4L2_STD_NTSC_M,
+ };
++
+ /* -----------------------------------------------------------------
+ Initialization and module stuff
+ ------------------------------------------------------------------*/
+
+-/* This routine allocates from 1 to n_devs virtual drivers.
++static int vivi_release(void)
++{
++ struct vivi_dev *dev;
++ struct list_head *list;
+
+- The real maximum number of virtual drivers will depend on how many drivers
+- will succeed. This is limited to the maximum number of devices that
+- videodev supports. Since there are 64 minors for video grabbers, this is
+- currently the theoretical maximum limit. However, a further limit does
+- exist at videodev that forbids any driver to register more than 32 video
+- grabbers.
+- */
+-static int __init vivi_init(void)
++ while (!list_empty(&vivi_devlist)) {
++ list = vivi_devlist.next;
++ list_del(list);
++ dev = list_entry(list, struct vivi_dev, vivi_devlist);
++
++ v4l2_info(&dev->v4l2_dev, "unregistering /dev/video%d\n",
++ dev->vfd->num);
++ video_unregister_device(dev->vfd);
++ v4l2_device_unregister(&dev->v4l2_dev);
++ kfree(dev);
++ }
++
++ return 0;
++}
++
++static int __init vivi_create_instance(int inst)
+ {
+- int ret = -ENOMEM, i;
+ struct vivi_dev *dev;
+ struct video_device *vfd;
++ int ret, i;
+
+- if (n_devs <= 0)
+- n_devs = 1;
++ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
++ if (!dev)
++ return -ENOMEM;
+
+- for (i = 0; i < n_devs; i++) {
+- dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+- if (!dev)
+- break;
++ snprintf(dev->v4l2_dev.name, sizeof(dev->v4l2_dev.name),
++ "%s-%03d", VIVI_MODULE_NAME, inst);
++ ret = v4l2_device_register(NULL, &dev->v4l2_dev);
++ if (ret)
++ goto free_dev;
+
+- /* init video dma queues */
+- INIT_LIST_HEAD(&dev->vidq.active);
+- init_waitqueue_head(&dev->vidq.wq);
++ /* init video dma queues */
++ INIT_LIST_HEAD(&dev->vidq.active);
++ init_waitqueue_head(&dev->vidq.wq);
+
+- /* initialize locks */
+- spin_lock_init(&dev->slock);
+- mutex_init(&dev->mutex);
++ /* initialize locks */
++ spin_lock_init(&dev->slock);
++ mutex_init(&dev->mutex);
+
+- vfd = video_device_alloc();
+- if (!vfd) {
+- kfree(dev);
+- break;
+- }
++ ret = -ENOMEM;
++ vfd = video_device_alloc();
++ if (!vfd)
++ goto unreg_dev;
+
+- *vfd = vivi_template;
++ *vfd = vivi_template;
+
+- ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
+- if (ret < 0) {
+- video_device_release(vfd);
+- kfree(dev);
++ ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr);
++ if (ret < 0)
++ goto rel_vdev;
+
+- /* If some registers succeeded, keep driver */
+- if (i)
+- ret = 0;
++ video_set_drvdata(vfd, dev);
+
+- break;
+- }
++ /* Set all controls to their default value. */
++ for (i = 0; i < ARRAY_SIZE(vivi_qctrl); i++)
++ dev->qctl_regs[i] = vivi_qctrl[i].default_value;
++
++ /* Now that everything is fine, let's add it to device list */
++ list_add_tail(&dev->vivi_devlist, &vivi_devlist);
++
++ snprintf(vfd->name, sizeof(vfd->name), "%s (%i)",
++ vivi_template.name, vfd->num);
++
++ if (video_nr >= 0)
++ video_nr++;
+
+- /* Now that everything is fine, let's add it to device list */
+- list_add_tail(&dev->vivi_devlist, &vivi_devlist);
++ dev->vfd = vfd;
++ v4l2_info(&dev->v4l2_dev, "V4L2 device registered as /dev/video%d\n",
++ vfd->num);
++ return 0;
++
++rel_vdev:
++ video_device_release(vfd);
++unreg_dev:
++ v4l2_device_unregister(&dev->v4l2_dev);
++free_dev:
++ kfree(dev);
++ return ret;
++}
++
++/* This routine allocates from 1 to n_devs virtual drivers.
+
+- snprintf(vfd->name, sizeof(vfd->name), "%s (%i)",
+- vivi_template.name, vfd->minor);
++ The real maximum number of virtual drivers will depend on how many drivers
++ will succeed. This is limited to the maximum number of devices that
++ videodev supports, which is equal to VIDEO_NUM_DEVICES.
++ */
++static int __init vivi_init(void)
++{
++ int ret = 0, i;
+
+- if (video_nr >= 0)
+- video_nr++;
++ if (n_devs <= 0)
++ n_devs = 1;
+
+- dev->vfd = vfd;
+- printk(KERN_INFO "%s: V4L2 device registered as /dev/video%d\n",
+- VIVI_MODULE_NAME, vfd->num);
++ for (i = 0; i < n_devs; i++) {
++ ret = vivi_create_instance(i);
++ if (ret) {
++ /* If some instantiations succeeded, keep driver */
++ if (i)
++ ret = 0;
++ break;
++ }
+ }
+
+ if (ret < 0) {
+- vivi_release();
+ printk(KERN_INFO "Error %d while loading vivi driver\n", ret);
+- } else {
+- printk(KERN_INFO "Video Technology Magazine Virtual Video "
++ return ret;
++ }
++
++ printk(KERN_INFO "Video Technology Magazine Virtual Video "
+ "Capture Board ver %u.%u.%u successfully loaded.\n",
+ (VIVI_VERSION >> 16) & 0xFF, (VIVI_VERSION >> 8) & 0xFF,
+ VIVI_VERSION & 0xFF);
+
+- /* n_devs will reflect the actual number of allocated devices */
+- n_devs = i;
+- }
++ /* n_devs will reflect the actual number of allocated devices */
++ n_devs = i;
+
+ return ret;
+ }
+@@ -1331,19 +1444,3 @@ static void __exit vivi_exit(void)
+
+ module_init(vivi_init);
+ module_exit(vivi_exit);
+-
+-MODULE_DESCRIPTION("Video Technology Magazine Virtual Video Capture Board");
+-MODULE_AUTHOR("Mauro Carvalho Chehab, Ted Walther and John Sokol");
+-MODULE_LICENSE("Dual BSD/GPL");
+-
+-module_param(video_nr, uint, 0444);
+-MODULE_PARM_DESC(video_nr, "video iminor start number");
+-
+-module_param(n_devs, uint, 0444);
+-MODULE_PARM_DESC(n_devs, "number of video devices to create");
+-
+-module_param_named(debug, vivi_template.debug, int, 0444);
+-MODULE_PARM_DESC(debug, "activates debug info");
+-
+-module_param(vid_limit, int, 0644);
+-MODULE_PARM_DESC(vid_limit, "capture memory limit in megabytes");
+diff --git a/drivers/media/video/vp27smpx.c b/drivers/media/video/vp27smpx.c
+index 5d73f66..42e23a4 100644
+--- a/drivers/media/video/vp27smpx.c
++++ b/drivers/media/video/vp27smpx.c
+@@ -129,11 +129,6 @@ static int vp27smpx_log_status(struct v4l2_subdev *sd)
+ return 0;
+ }
+
+-static int vp27smpx_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+-}
+-
+ /* ----------------------------------------------------------------------- */
+
+ static const struct v4l2_subdev_core_ops vp27smpx_core_ops = {
+@@ -206,8 +201,6 @@ MODULE_DEVICE_TABLE(i2c, vp27smpx_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "vp27smpx",
+- .driverid = I2C_DRIVERID_VP27SMPX,
+- .command = vp27smpx_command,
+ .probe = vp27smpx_probe,
+ .remove = vp27smpx_remove,
+ .id_table = vp27smpx_id,
+diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c
+index 67aa0db..2fa7e8b 100644
+--- a/drivers/media/video/vpx3220.c
++++ b/drivers/media/video/vpx3220.c
+@@ -24,10 +24,10 @@
+ #include <linux/types.h>
+ #include <asm/uaccess.h>
+ #include <linux/i2c.h>
+-#include <media/v4l2-common.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
+-#include <linux/videodev.h>
+-#include <linux/video_decoder.h>
++#include <linux/videodev2.h>
++#include <media/v4l2-device.h>
++#include <media/v4l2-chip-ident.h>
++#include <media/v4l2-i2c-drv.h>
+
+ MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver");
+ MODULE_AUTHOR("Laurent Pinchart");
+@@ -37,14 +37,17 @@ static int debug;
+ module_param(debug, int, 0);
+ MODULE_PARM_DESC(debug, "Debug level (0-1)");
+
++
+ #define VPX_TIMEOUT_COUNT 10
+
+ /* ----------------------------------------------------------------------- */
+
+ struct vpx3220 {
++ struct v4l2_subdev sd;
+ unsigned char reg[255];
+
+- int norm;
++ v4l2_std_id norm;
++ int ident;
+ int input;
+ int enable;
+ int bright;
+@@ -53,30 +56,38 @@ struct vpx3220 {
+ int sat;
+ };
+
++static inline struct vpx3220 *to_vpx3220(struct v4l2_subdev *sd)
++{
++ return container_of(sd, struct vpx3220, sd);
++}
++
+ static char *inputs[] = { "internal", "composite", "svideo" };
+
+ /* ----------------------------------------------------------------------- */
+
+-static inline int vpx3220_write(struct i2c_client *client, u8 reg, u8 value)
++static inline int vpx3220_write(struct v4l2_subdev *sd, u8 reg, u8 value)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ struct vpx3220 *decoder = i2c_get_clientdata(client);
+
+ decoder->reg[reg] = value;
+ return i2c_smbus_write_byte_data(client, reg, value);
+ }
+
+-static inline int vpx3220_read(struct i2c_client *client, u8 reg)
++static inline int vpx3220_read(struct v4l2_subdev *sd, u8 reg)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
+ return i2c_smbus_read_byte_data(client, reg);
+ }
+
+-static int vpx3220_fp_status(struct i2c_client *client)
++static int vpx3220_fp_status(struct v4l2_subdev *sd)
+ {
+ unsigned char status;
+ unsigned int i;
+
+ for (i = 0; i < VPX_TIMEOUT_COUNT; i++) {
+- status = vpx3220_read(client, 0x29);
++ status = vpx3220_read(sd, 0x29);
+
+ if (!(status & 4))
+ return 0;
+@@ -90,57 +101,60 @@ static int vpx3220_fp_status(struct i2c_client *client)
+ return -1;
+ }
+
+-static int vpx3220_fp_write(struct i2c_client *client, u8 fpaddr, u16 data)
++static int vpx3220_fp_write(struct v4l2_subdev *sd, u8 fpaddr, u16 data)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
++
+ /* Write the 16-bit address to the FPWR register */
+ if (i2c_smbus_write_word_data(client, 0x27, swab16(fpaddr)) == -1) {
+- v4l_dbg(1, debug, client, "%s: failed\n", __func__);
++ v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
+ return -1;
+ }
+
+- if (vpx3220_fp_status(client) < 0)
++ if (vpx3220_fp_status(sd) < 0)
+ return -1;
+
+ /* Write the 16-bit data to the FPDAT register */
+ if (i2c_smbus_write_word_data(client, 0x28, swab16(data)) == -1) {
+- v4l_dbg(1, debug, client, "%s: failed\n", __func__);
++ v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
+ return -1;
+ }
+
+ return 0;
+ }
+
+-static u16 vpx3220_fp_read(struct i2c_client *client, u16 fpaddr)
++static u16 vpx3220_fp_read(struct v4l2_subdev *sd, u16 fpaddr)
+ {
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+ s16 data;
+
+ /* Write the 16-bit address to the FPRD register */
+ if (i2c_smbus_write_word_data(client, 0x26, swab16(fpaddr)) == -1) {
+- v4l_dbg(1, debug, client, "%s: failed\n", __func__);
++ v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
+ return -1;
+ }
+
+- if (vpx3220_fp_status(client) < 0)
++ if (vpx3220_fp_status(sd) < 0)
+ return -1;
+
+ /* Read the 16-bit data from the FPDAT register */
+ data = i2c_smbus_read_word_data(client, 0x28);
+ if (data == -1) {
+- v4l_dbg(1, debug, client, "%s: failed\n", __func__);
++ v4l2_dbg(1, debug, sd, "%s: failed\n", __func__);
+ return -1;
+ }
+
+ return swab16(data);
+ }
+
+-static int vpx3220_write_block(struct i2c_client *client, const u8 *data, unsigned int len)
++static int vpx3220_write_block(struct v4l2_subdev *sd, const u8 *data, unsigned int len)
+ {
+ u8 reg;
+ int ret = -1;
+
+ while (len >= 2) {
+ reg = *data++;
+- ret = vpx3220_write(client, reg, *data++);
++ ret = vpx3220_write(sd, reg, *data++);
+ if (ret < 0)
+ break;
+ len -= 2;
+@@ -149,7 +163,7 @@ static int vpx3220_write_block(struct i2c_client *client, const u8 *data, unsign
+ return ret;
+ }
+
+-static int vpx3220_write_fp_block(struct i2c_client *client,
++static int vpx3220_write_fp_block(struct v4l2_subdev *sd,
+ const u16 *data, unsigned int len)
+ {
+ u8 reg;
+@@ -157,7 +171,7 @@ static int vpx3220_write_fp_block(struct i2c_client *client,
+
+ while (len > 1) {
+ reg = *data++;
+- ret |= vpx3220_fp_write(client, reg, *data++);
++ ret |= vpx3220_fp_write(sd, reg, *data++);
+ len -= 2;
+ }
+
+@@ -259,276 +273,277 @@ static const unsigned short init_fp[] = {
+ 0x4b, 0x298, /* PLL gain */
+ };
+
+-static void vpx3220_dump_i2c(struct i2c_client *client)
+-{
+- int len = sizeof(init_common);
+- const unsigned char *data = init_common;
+
+- while (len > 1) {
+- v4l_dbg(1, debug, client, "i2c reg 0x%02x data 0x%02x\n",
+- *data, vpx3220_read(client, *data));
+- data += 2;
+- len -= 2;
+- }
++static int vpx3220_init(struct v4l2_subdev *sd, u32 val)
++{
++ struct vpx3220 *decoder = to_vpx3220(sd);
++
++ vpx3220_write_block(sd, init_common, sizeof(init_common));
++ vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1);
++ if (decoder->norm & V4L2_STD_NTSC)
++ vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1);
++ else if (decoder->norm & V4L2_STD_PAL)
++ vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
++ else if (decoder->norm & V4L2_STD_SECAM)
++ vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1);
++ else
++ vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
++ return 0;
+ }
+
+-static int vpx3220_command(struct i2c_client *client, unsigned cmd, void *arg)
++static int vpx3220_status(struct v4l2_subdev *sd, u32 *pstatus, v4l2_std_id *pstd)
+ {
+- struct vpx3220 *decoder = i2c_get_clientdata(client);
++ int res = V4L2_IN_ST_NO_SIGNAL, status;
++ v4l2_std_id std = 0;
+
+- switch (cmd) {
+- case 0:
+- {
+- vpx3220_write_block(client, init_common,
+- sizeof(init_common));
+- vpx3220_write_fp_block(client, init_fp,
+- sizeof(init_fp) >> 1);
+- switch (decoder->norm) {
+- case VIDEO_MODE_NTSC:
+- vpx3220_write_fp_block(client, init_ntsc,
+- sizeof(init_ntsc) >> 1);
+- break;
++ status = vpx3220_fp_read(sd, 0x0f3);
++
++ v4l2_dbg(1, debug, sd, "status: 0x%04x\n", status);
++
++ if (status < 0)
++ return status;
+
+- case VIDEO_MODE_PAL:
+- vpx3220_write_fp_block(client, init_pal,
+- sizeof(init_pal) >> 1);
++ if ((status & 0x20) == 0) {
++ res = 0;
++
++ switch (status & 0x18) {
++ case 0x00:
++ case 0x10:
++ case 0x14:
++ case 0x18:
++ std = V4L2_STD_PAL;
+ break;
+- case VIDEO_MODE_SECAM:
+- vpx3220_write_fp_block(client, init_secam,
+- sizeof(init_secam) >> 1);
++
++ case 0x08:
++ std = V4L2_STD_SECAM;
+ break;
+- default:
+- vpx3220_write_fp_block(client, init_pal,
+- sizeof(init_pal) >> 1);
++
++ case 0x04:
++ case 0x0c:
++ case 0x1c:
++ std = V4L2_STD_NTSC;
+ break;
+ }
+- break;
+- }
+-
+- case DECODER_DUMP:
+- {
+- vpx3220_dump_i2c(client);
+- break;
+ }
++ if (pstd)
++ *pstd = std;
++ if (pstatus)
++ *pstatus = status;
++ return 0;
++}
+
+- case DECODER_GET_CAPABILITIES:
+- {
+- struct video_decoder_capability *cap = arg;
++static int vpx3220_querystd(struct v4l2_subdev *sd, v4l2_std_id *std)
++{
++ v4l2_dbg(1, debug, sd, "querystd\n");
++ return vpx3220_status(sd, NULL, std);
++}
+
+- v4l_dbg(1, debug, client, "DECODER_GET_CAPABILITIES\n");
++static int vpx3220_g_input_status(struct v4l2_subdev *sd, u32 *status)
++{
++ v4l2_dbg(1, debug, sd, "g_input_status\n");
++ return vpx3220_status(sd, status, NULL);
++}
+
+- cap->flags = VIDEO_DECODER_PAL |
+- VIDEO_DECODER_NTSC |
+- VIDEO_DECODER_SECAM |
+- VIDEO_DECODER_AUTO |
+- VIDEO_DECODER_CCIR;
+- cap->inputs = 3;
+- cap->outputs = 1;
+- break;
++static int vpx3220_s_std(struct v4l2_subdev *sd, v4l2_std_id std)
++{
++ struct vpx3220 *decoder = to_vpx3220(sd);
++ int temp_input;
++
++ /* Here we back up the input selection because it gets
++ overwritten when we fill the registers with the
++ choosen video norm */
++ temp_input = vpx3220_fp_read(sd, 0xf2);
++
++ v4l2_dbg(1, debug, sd, "s_std %llx\n", (unsigned long long)std);
++ if (std & V4L2_STD_NTSC) {
++ vpx3220_write_fp_block(sd, init_ntsc, sizeof(init_ntsc) >> 1);
++ v4l2_dbg(1, debug, sd, "norm switched to NTSC\n");
++ } else if (std & V4L2_STD_PAL) {
++ vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
++ v4l2_dbg(1, debug, sd, "norm switched to PAL\n");
++ } else if (std & V4L2_STD_SECAM) {
++ vpx3220_write_fp_block(sd, init_secam, sizeof(init_secam) >> 1);
++ v4l2_dbg(1, debug, sd, "norm switched to SECAM\n");
++ } else {
++ return -EINVAL;
+ }
+
+- case DECODER_GET_STATUS:
+- {
+- int res = 0, status;
++ decoder->norm = std;
+
+- v4l_dbg(1, debug, client, "DECODER_GET_STATUS\n");
+-
+- status = vpx3220_fp_read(client, 0x0f3);
+-
+- v4l_dbg(1, debug, client, "status: 0x%04x\n", status);
+-
+- if (status < 0)
+- return status;
++ /* And here we set the backed up video input again */
++ vpx3220_fp_write(sd, 0xf2, temp_input | 0x0010);
++ udelay(10);
++ return 0;
++}
+
+- if ((status & 0x20) == 0) {
+- res |= DECODER_STATUS_GOOD | DECODER_STATUS_COLOR;
++static int vpx3220_s_routing(struct v4l2_subdev *sd, const struct v4l2_routing *route)
++{
++ int data;
+
+- switch (status & 0x18) {
+- case 0x00:
+- case 0x10:
+- case 0x14:
+- case 0x18:
+- res |= DECODER_STATUS_PAL;
+- break;
++ /* RJ: route->input = 0: ST8 (PCTV) input
++ route->input = 1: COMPOSITE input
++ route->input = 2: SVHS input */
+
+- case 0x08:
+- res |= DECODER_STATUS_SECAM;
+- break;
++ const int input[3][2] = {
++ {0x0c, 0},
++ {0x0d, 0},
++ {0x0e, 1}
++ };
+
+- case 0x04:
+- case 0x0c:
+- case 0x1c:
+- res |= DECODER_STATUS_NTSC;
+- break;
+- }
+- }
++ if (route->input < 0 || route->input > 2)
++ return -EINVAL;
+
+- *(int *) arg = res;
+- break;
+- }
++ v4l2_dbg(1, debug, sd, "input switched to %s\n", inputs[route->input]);
+
+- case DECODER_SET_NORM:
+- {
+- int *iarg = arg, data;
+- int temp_input;
+-
+- /* Here we back up the input selection because it gets
+- overwritten when we fill the registers with the
+- choosen video norm */
+- temp_input = vpx3220_fp_read(client, 0xf2);
+-
+- v4l_dbg(1, debug, client, "DECODER_SET_NORM %d\n", *iarg);
+- switch (*iarg) {
+- case VIDEO_MODE_NTSC:
+- vpx3220_write_fp_block(client, init_ntsc,
+- sizeof(init_ntsc) >> 1);
+- v4l_dbg(1, debug, client, "norm switched to NTSC\n");
+- break;
++ vpx3220_write(sd, 0x33, input[route->input][0]);
+
+- case VIDEO_MODE_PAL:
+- vpx3220_write_fp_block(client, init_pal,
+- sizeof(init_pal) >> 1);
+- v4l_dbg(1, debug, client, "norm switched to PAL\n");
+- break;
++ data = vpx3220_fp_read(sd, 0xf2) & ~(0x0020);
++ if (data < 0)
++ return data;
++ /* 0x0010 is required to latch the setting */
++ vpx3220_fp_write(sd, 0xf2,
++ data | (input[route->input][1] << 5) | 0x0010);
+
+- case VIDEO_MODE_SECAM:
+- vpx3220_write_fp_block(client, init_secam,
+- sizeof(init_secam) >> 1);
+- v4l_dbg(1, debug, client, "norm switched to SECAM\n");
+- break;
++ udelay(10);
++ return 0;
++}
+
+- case VIDEO_MODE_AUTO:
+- /* FIXME This is only preliminary support */
+- data = vpx3220_fp_read(client, 0xf2) & 0x20;
+- vpx3220_fp_write(client, 0xf2, 0x00c0 | data);
+- v4l_dbg(1, debug, client, "norm switched to AUTO\n");
+- break;
++static int vpx3220_s_stream(struct v4l2_subdev *sd, int enable)
++{
++ v4l2_dbg(1, debug, sd, "s_stream %s\n", enable ? "on" : "off");
+
+- default:
+- return -EINVAL;
+- }
+- decoder->norm = *iarg;
++ vpx3220_write(sd, 0xf2, (enable ? 0x1b : 0x00));
++ return 0;
++}
+
+- /* And here we set the backed up video input again */
+- vpx3220_fp_write(client, 0xf2, temp_input | 0x0010);
+- udelay(10);
++static int vpx3220_queryctrl(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc)
++{
++ switch (qc->id) {
++ case V4L2_CID_BRIGHTNESS:
++ v4l2_ctrl_query_fill(qc, -128, 127, 1, 0);
+ break;
+- }
+-
+- case DECODER_SET_INPUT:
+- {
+- int *iarg = arg, data;
+-
+- /* RJ: *iarg = 0: ST8 (PCTV) input
+- *iarg = 1: COMPOSITE input
+- *iarg = 2: SVHS input */
+-
+- const int input[3][2] = {
+- {0x0c, 0},
+- {0x0d, 0},
+- {0x0e, 1}
+- };
+
+- if (*iarg < 0 || *iarg > 2)
+- return -EINVAL;
+-
+- v4l_dbg(1, debug, client, "input switched to %s\n", inputs[*iarg]);
+-
+- vpx3220_write(client, 0x33, input[*iarg][0]);
+-
+- data = vpx3220_fp_read(client, 0xf2) & ~(0x0020);
+- if (data < 0)
+- return data;
+- /* 0x0010 is required to latch the setting */
+- vpx3220_fp_write(client, 0xf2,
+- data | (input[*iarg][1] << 5) | 0x0010);
+-
+- udelay(10);
++ case V4L2_CID_CONTRAST:
++ v4l2_ctrl_query_fill(qc, 0, 63, 1, 32);
+ break;
+- }
+
+- case DECODER_SET_OUTPUT:
+- {
+- int *iarg = arg;
++ case V4L2_CID_SATURATION:
++ v4l2_ctrl_query_fill(qc, 0, 4095, 1, 2048);
++ break;
+
+- /* not much choice of outputs */
+- if (*iarg != 0) {
+- return -EINVAL;
+- }
++ case V4L2_CID_HUE:
++ v4l2_ctrl_query_fill(qc, -512, 511, 1, 0);
+ break;
+- }
+
+- case DECODER_ENABLE_OUTPUT:
+- {
+- int *iarg = arg;
++ default:
++ return -EINVAL;
++ }
++ return 0;
++}
+
+- v4l_dbg(1, debug, client, "DECODER_ENABLE_OUTPUT %d\n", *iarg);
++static int vpx3220_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
++{
++ struct vpx3220 *decoder = to_vpx3220(sd);
+
+- vpx3220_write(client, 0xf2, (*iarg ? 0x1b : 0x00));
++ switch (ctrl->id) {
++ case V4L2_CID_BRIGHTNESS:
++ ctrl->value = decoder->bright;
++ break;
++ case V4L2_CID_CONTRAST:
++ ctrl->value = decoder->contrast;
+ break;
++ case V4L2_CID_SATURATION:
++ ctrl->value = decoder->sat;
++ break;
++ case V4L2_CID_HUE:
++ ctrl->value = decoder->hue;
++ break;
++ default:
++ return -EINVAL;
+ }
++ return 0;
++}
+
+- case DECODER_SET_PICTURE:
+- {
+- struct video_picture *pic = arg;
++static int vpx3220_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl)
++{
++ struct vpx3220 *decoder = to_vpx3220(sd);
+
+- if (decoder->bright != pic->brightness) {
+- /* We want -128 to 128 we get 0-65535 */
+- decoder->bright = pic->brightness;
+- vpx3220_write(client, 0xe6,
+- (decoder->bright - 32768) >> 8);
++ switch (ctrl->id) {
++ case V4L2_CID_BRIGHTNESS:
++ if (decoder->bright != ctrl->value) {
++ decoder->bright = ctrl->value;
++ vpx3220_write(sd, 0xe6, decoder->bright);
+ }
+- if (decoder->contrast != pic->contrast) {
+- /* We want 0 to 64 we get 0-65535 */
++ break;
++ case V4L2_CID_CONTRAST:
++ if (decoder->contrast != ctrl->value) {
+ /* Bit 7 and 8 is for noise shaping */
+- decoder->contrast = pic->contrast;
+- vpx3220_write(client, 0xe7,
+- (decoder->contrast >> 10) + 192);
++ decoder->contrast = ctrl->value;
++ vpx3220_write(sd, 0xe7, decoder->contrast + 192);
+ }
+- if (decoder->sat != pic->colour) {
+- /* We want 0 to 4096 we get 0-65535 */
+- decoder->sat = pic->colour;
+- vpx3220_fp_write(client, 0xa0,
+- decoder->sat >> 4);
++ break;
++ case V4L2_CID_SATURATION:
++ if (decoder->sat != ctrl->value) {
++ decoder->sat = ctrl->value;
++ vpx3220_fp_write(sd, 0xa0, decoder->sat);
+ }
+- if (decoder->hue != pic->hue) {
+- /* We want -512 to 512 we get 0-65535 */
+- decoder->hue = pic->hue;
+- vpx3220_fp_write(client, 0x1c,
+- ((decoder->hue - 32768) >> 6) & 0xFFF);
++ break;
++ case V4L2_CID_HUE:
++ if (decoder->hue != ctrl->value) {
++ decoder->hue = ctrl->value;
++ vpx3220_fp_write(sd, 0x1c, decoder->hue);
+ }
+ break;
+- }
+-
+ default:
+ return -EINVAL;
+ }
+-
+ return 0;
+ }
+
+-static int vpx3220_init_client(struct i2c_client *client)
++static int vpx3220_g_chip_ident(struct v4l2_subdev *sd, struct v4l2_dbg_chip_ident *chip)
+ {
+- vpx3220_write_block(client, init_common, sizeof(init_common));
+- vpx3220_write_fp_block(client, init_fp, sizeof(init_fp) >> 1);
+- /* Default to PAL */
+- vpx3220_write_fp_block(client, init_pal, sizeof(init_pal) >> 1);
++ struct vpx3220 *decoder = to_vpx3220(sd);
++ struct i2c_client *client = v4l2_get_subdevdata(sd);
+
+- return 0;
++ return v4l2_chip_ident_i2c_client(client, chip, decoder->ident, 0);
+ }
+
++/* ----------------------------------------------------------------------- */
++
++static const struct v4l2_subdev_core_ops vpx3220_core_ops = {
++ .g_chip_ident = vpx3220_g_chip_ident,
++ .init = vpx3220_init,
++ .g_ctrl = vpx3220_g_ctrl,
++ .s_ctrl = vpx3220_s_ctrl,
++ .queryctrl = vpx3220_queryctrl,
++};
++
++static const struct v4l2_subdev_tuner_ops vpx3220_tuner_ops = {
++ .s_std = vpx3220_s_std,
++};
++
++static const struct v4l2_subdev_video_ops vpx3220_video_ops = {
++ .s_routing = vpx3220_s_routing,
++ .s_stream = vpx3220_s_stream,
++ .querystd = vpx3220_querystd,
++ .g_input_status = vpx3220_g_input_status,
++};
++
++static const struct v4l2_subdev_ops vpx3220_ops = {
++ .core = &vpx3220_core_ops,
++ .tuner = &vpx3220_tuner_ops,
++ .video = &vpx3220_video_ops,
++};
++
+ /* -----------------------------------------------------------------------
+ * Client management code
+ */
+
+-static unsigned short normal_i2c[] = { 0x86 >> 1, 0x8e >> 1, I2C_CLIENT_END };
+-
+-I2C_CLIENT_INSMOD;
+-
+ static int vpx3220_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+ {
+ struct vpx3220 *decoder;
++ struct v4l2_subdev *sd;
+ const char *name = NULL;
+ u8 ver;
+ u16 pn;
+@@ -541,18 +556,20 @@ static int vpx3220_probe(struct i2c_client *client,
+ decoder = kzalloc(sizeof(struct vpx3220), GFP_KERNEL);
+ if (decoder == NULL)
+ return -ENOMEM;
+- decoder->norm = VIDEO_MODE_PAL;
++ sd = &decoder->sd;
++ v4l2_i2c_subdev_init(sd, client, &vpx3220_ops);
++ decoder->norm = V4L2_STD_PAL;
+ decoder->input = 0;
+ decoder->enable = 1;
+ decoder->bright = 32768;
+ decoder->contrast = 32768;
+ decoder->hue = 32768;
+ decoder->sat = 32768;
+- i2c_set_clientdata(client, decoder);
+
+ ver = i2c_smbus_read_byte_data(client, 0x00);
+ pn = (i2c_smbus_read_byte_data(client, 0x02) << 8) +
+ i2c_smbus_read_byte_data(client, 0x01);
++ decoder->ident = V4L2_IDENT_VPX3220A;
+ if (ver == 0xec) {
+ switch (pn) {
+ case 0x4680:
+@@ -560,26 +577,34 @@ static int vpx3220_probe(struct i2c_client *client,
+ break;
+ case 0x4260:
+ name = "vpx3216b";
++ decoder->ident = V4L2_IDENT_VPX3216B;
+ break;
+ case 0x4280:
+ name = "vpx3214c";
++ decoder->ident = V4L2_IDENT_VPX3214C;
+ break;
+ }
+ }
+ if (name)
+- v4l_info(client, "%s found @ 0x%x (%s)\n", name,
++ v4l2_info(sd, "%s found @ 0x%x (%s)\n", name,
+ client->addr << 1, client->adapter->name);
+ else
+- v4l_info(client, "chip (%02x:%04x) found @ 0x%x (%s)\n",
++ v4l2_info(sd, "chip (%02x:%04x) found @ 0x%x (%s)\n",
+ ver, pn, client->addr << 1, client->adapter->name);
+
+- vpx3220_init_client(client);
++ vpx3220_write_block(sd, init_common, sizeof(init_common));
++ vpx3220_write_fp_block(sd, init_fp, sizeof(init_fp) >> 1);
++ /* Default to PAL */
++ vpx3220_write_fp_block(sd, init_pal, sizeof(init_pal) >> 1);
+ return 0;
+ }
+
+ static int vpx3220_remove(struct i2c_client *client)
+ {
+- kfree(i2c_get_clientdata(client));
++ struct v4l2_subdev *sd = i2c_get_clientdata(client);
++
++ v4l2_device_unregister_subdev(sd);
++ kfree(to_vpx3220(sd));
+ return 0;
+ }
+
+@@ -593,8 +618,6 @@ MODULE_DEVICE_TABLE(i2c, vpx3220_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "vpx3220",
+- .driverid = I2C_DRIVERID_VPX3220,
+- .command = vpx3220_command,
+ .probe = vpx3220_probe,
+ .remove = vpx3220_remove,
+ .id_table = vpx3220_id,
+diff --git a/drivers/media/video/w9966.c b/drivers/media/video/w9966.c
+index 038ff32..dcade61 100644
+--- a/drivers/media/video/w9966.c
++++ b/drivers/media/video/w9966.c
+@@ -57,7 +57,7 @@
+ #include <linux/module.h>
+ #include <linux/init.h>
+ #include <linux/delay.h>
+-#include <linux/videodev2.h>
++#include <linux/videodev.h>
+ #include <media/v4l2-common.h>
+ #include <media/v4l2-ioctl.h>
+ #include <linux/parport.h>
+diff --git a/drivers/media/video/w9968cf.c b/drivers/media/video/w9968cf.c
+index 105a832..3b08bc4 100644
+--- a/drivers/media/video/w9968cf.c
++++ b/drivers/media/video/w9968cf.c
+@@ -42,6 +42,7 @@
+ #include <asm/page.h>
+ #include <asm/uaccess.h>
+ #include <linux/page-flags.h>
++#include <linux/videodev.h>
+ #include <media/v4l2-ioctl.h>
+
+ #include "w9968cf.h"
+@@ -68,7 +69,6 @@ MODULE_VERSION(W9968CF_MODULE_VERSION);
+ MODULE_LICENSE(W9968CF_MODULE_LICENSE);
+ MODULE_SUPPORTED_DEVICE("Video");
+
+-static int ovmod_load = W9968CF_OVMOD_LOAD;
+ static unsigned short simcams = W9968CF_SIMCAMS;
+ static short video_nr[]={[0 ... W9968CF_MAX_DEVICES-1] = -1}; /*-1=first free*/
+ static unsigned int packet_size[] = {[0 ... W9968CF_MAX_DEVICES-1] =
+@@ -111,9 +111,6 @@ static int specific_debug = W9968CF_SPECIFIC_DEBUG;
+
+ static unsigned int param_nv[24]; /* number of values per parameter */
+
+-#ifdef CONFIG_MODULES
+-module_param(ovmod_load, bool, 0644);
+-#endif
+ module_param(simcams, ushort, 0644);
+ module_param_array(video_nr, short, &param_nv[0], 0444);
+ module_param_array(packet_size, uint, &param_nv[1], 0444);
+@@ -144,18 +141,6 @@ module_param(debug, ushort, 0644);
+ module_param(specific_debug, bool, 0644);
+ #endif
+
+-#ifdef CONFIG_MODULES
+-MODULE_PARM_DESC(ovmod_load,
+- "\n<0|1> Automatic 'ovcamchip' module loading."
+- "\n0 disabled, 1 enabled."
+- "\nIf enabled,'insmod' searches for the required 'ovcamchip'"
+- "\nmodule in the system, according to its configuration, and"
+- "\nattempts to load that module automatically. This action is"
+- "\nperformed once as soon as the 'w9968cf' module is loaded"
+- "\ninto memory."
+- "\nDefault value is "__MODULE_STRING(W9968CF_OVMOD_LOAD)"."
+- "\n");
+-#endif
+ MODULE_PARM_DESC(simcams,
+ "\n<n> Number of cameras allowed to stream simultaneously."
+ "\nn may vary from 0 to "
+@@ -443,8 +428,6 @@ static int w9968cf_i2c_smbus_xfer(struct i2c_adapter*, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size, union i2c_smbus_data*);
+ static u32 w9968cf_i2c_func(struct i2c_adapter*);
+-static int w9968cf_i2c_attach_inform(struct i2c_client*);
+-static int w9968cf_i2c_detach_inform(struct i2c_client*);
+
+ /* Memory management */
+ static void* rvmalloc(unsigned long size);
+@@ -1443,19 +1426,11 @@ w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
+ unsigned short flags, char read_write, u8 command,
+ int size, union i2c_smbus_data *data)
+ {
+- struct w9968cf_device* cam = i2c_get_adapdata(adapter);
++ struct v4l2_device *v4l2_dev = i2c_get_adapdata(adapter);
++ struct w9968cf_device *cam = to_cam(v4l2_dev);
+ u8 i;
+ int err = 0;
+
+- switch (addr) {
+- case OV6xx0_SID:
+- case OV7xx0_SID:
+- break;
+- default:
+- DBG(4, "Rejected slave ID 0x%04X", addr)
+- return -EINVAL;
+- }
+-
+ if (size == I2C_SMBUS_BYTE) {
+ /* Why addr <<= 1? See OVXXX0_SID defines in ovcamchip.h */
+ addr <<= 1;
+@@ -1463,8 +1438,17 @@ w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
+ if (read_write == I2C_SMBUS_WRITE)
+ err = w9968cf_i2c_adap_write_byte(cam, addr, command);
+ else if (read_write == I2C_SMBUS_READ)
+- err = w9968cf_i2c_adap_read_byte(cam,addr,&data->byte);
+-
++ for (i = 1; i <= W9968CF_I2C_RW_RETRIES; i++) {
++ err = w9968cf_i2c_adap_read_byte(cam, addr,
++ &data->byte);
++ if (err) {
++ if (w9968cf_smbus_refresh_bus(cam)) {
++ err = -EIO;
++ break;
++ }
++ } else
++ break;
++ }
+ } else if (size == I2C_SMBUS_BYTE_DATA) {
+ addr <<= 1;
+
+@@ -1491,7 +1475,6 @@ w9968cf_i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr,
+ DBG(4, "Unsupported I2C transfer mode (%d)", size)
+ return -EINVAL;
+ }
+-
+ return err;
+ }
+
+@@ -1504,44 +1487,6 @@ static u32 w9968cf_i2c_func(struct i2c_adapter* adap)
+ }
+
+
+-static int w9968cf_i2c_attach_inform(struct i2c_client* client)
+-{
+- struct w9968cf_device* cam = i2c_get_adapdata(client->adapter);
+- int id = client->driver->id, err = 0;
+-
+- if (id == I2C_DRIVERID_OVCAMCHIP) {
+- cam->sensor_client = client;
+- err = w9968cf_sensor_init(cam);
+- if (err) {
+- cam->sensor_client = NULL;
+- return err;
+- }
+- } else {
+- DBG(4, "Rejected client [%s] with driver [%s]",
+- client->name, client->driver->driver.name)
+- return -EINVAL;
+- }
+-
+- DBG(5, "I2C attach client [%s] with driver [%s]",
+- client->name, client->driver->driver.name)
+-
+- return 0;
+-}
+-
+-
+-static int w9968cf_i2c_detach_inform(struct i2c_client* client)
+-{
+- struct w9968cf_device* cam = i2c_get_adapdata(client->adapter);
+-
+- if (cam->sensor_client == client)
+- cam->sensor_client = NULL;
+-
+- DBG(5, "I2C detach client [%s]", client->name)
+-
+- return 0;
+-}
+-
+-
+ static int w9968cf_i2c_init(struct w9968cf_device* cam)
+ {
+ int err = 0;
+@@ -1554,15 +1499,13 @@ static int w9968cf_i2c_init(struct w9968cf_device* cam)
+ static struct i2c_adapter adap = {
+ .id = I2C_HW_SMBUS_W9968CF,
+ .owner = THIS_MODULE,
+- .client_register = w9968cf_i2c_attach_inform,
+- .client_unregister = w9968cf_i2c_detach_inform,
+ .algo = &algo,
+ };
+
+ memcpy(&cam->i2c_adapter, &adap, sizeof(struct i2c_adapter));
+ strcpy(cam->i2c_adapter.name, "w9968cf");
+ cam->i2c_adapter.dev.parent = &cam->usbdev->dev;
+- i2c_set_adapdata(&cam->i2c_adapter, cam);
++ i2c_set_adapdata(&cam->i2c_adapter, &cam->v4l2_dev);
+
+ DBG(6, "Registering I2C adapter with kernel...")
+
+@@ -2165,13 +2108,9 @@ w9968cf_sensor_get_control(struct w9968cf_device* cam, int cid, int* val)
+ static int
+ w9968cf_sensor_cmd(struct w9968cf_device* cam, unsigned int cmd, void* arg)
+ {
+- struct i2c_client* c = cam->sensor_client;
+- int rc = 0;
+-
+- if (!c || !c->driver || !c->driver->command)
+- return -EINVAL;
++ int rc;
+
+- rc = c->driver->command(c, cmd, arg);
++ rc = v4l2_subdev_call(cam->sensor_sd, core, ioctl, cmd, arg);
+ /* The I2C driver returns -EPERM on non-supported controls */
+ return (rc < 0 && rc != -EPERM) ? rc : 0;
+ }
+@@ -2346,7 +2285,7 @@ static int w9968cf_sensor_init(struct w9968cf_device* cam)
+ goto error;
+
+ /* NOTE: Make sure width and height are a multiple of 16 */
+- switch (cam->sensor_client->addr) {
++ switch (v4l2_i2c_subdev_addr(cam->sensor_sd)) {
+ case OV6xx0_SID:
+ cam->maxwidth = 352;
+ cam->maxheight = 288;
+@@ -2651,6 +2590,7 @@ static void w9968cf_release_resources(struct w9968cf_device* cam)
+ w9968cf_deallocate_memory(cam);
+ kfree(cam->control_buffer);
+ kfree(cam->data_buffer);
++ v4l2_device_unregister(&cam->v4l2_dev);
+
+ mutex_unlock(&w9968cf_devlist_mutex);
+ }
+@@ -3480,6 +3420,11 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
+ struct list_head* ptr;
+ u8 sc = 0; /* number of simultaneous cameras */
+ static unsigned short dev_nr; /* 0 - we are handling device number n */
++ static unsigned short addrs[] = {
++ OV7xx0_SID,
++ OV6xx0_SID,
++ I2C_CLIENT_END
++ };
+
+ if (le16_to_cpu(udev->descriptor.idVendor) == winbond_id_table[0].idVendor &&
+ le16_to_cpu(udev->descriptor.idProduct) == winbond_id_table[0].idProduct)
+@@ -3495,12 +3440,14 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
+ if (!cam)
+ return -ENOMEM;
+
++ err = v4l2_device_register(&udev->dev, &cam->v4l2_dev);
++ if (err)
++ goto fail0;
++
+ mutex_init(&cam->dev_mutex);
+ mutex_lock(&cam->dev_mutex);
+
+ cam->usbdev = udev;
+- /* NOTE: a local copy is used to avoid possible race conditions */
+- memcpy(&cam->dev, &udev->dev, sizeof(struct device));
+
+ DBG(2, "%s detected", symbolic(camlist, mod_id))
+
+@@ -3549,7 +3496,7 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
+ cam->v4ldev->minor = video_nr[dev_nr];
+ cam->v4ldev->release = video_device_release;
+ video_set_drvdata(cam->v4ldev, cam);
+- cam->v4ldev->parent = &cam->dev;
++ cam->v4ldev->v4l2_dev = &cam->v4l2_dev;
+
+ err = video_register_device(cam->v4ldev, VFL_TYPE_GRABBER,
+ video_nr[dev_nr]);
+@@ -3576,9 +3523,13 @@ w9968cf_usb_probe(struct usb_interface* intf, const struct usb_device_id* id)
+ w9968cf_turn_on_led(cam);
+
+ w9968cf_i2c_init(cam);
++ cam->sensor_sd = v4l2_i2c_new_probed_subdev(&cam->i2c_adapter,
++ "ovcamchip", "ovcamchip", addrs);
+
+ usb_set_intfdata(intf, cam);
+ mutex_unlock(&cam->dev_mutex);
++
++ err = w9968cf_sensor_init(cam);
+ return 0;
+
+ fail: /* Free unused memory */
+@@ -3587,6 +3538,8 @@ fail: /* Free unused memory */
+ if (cam->v4ldev)
+ video_device_release(cam->v4ldev);
+ mutex_unlock(&cam->dev_mutex);
++ v4l2_device_unregister(&cam->v4l2_dev);
++fail0:
+ kfree(cam);
+ return err;
+ }
+@@ -3597,15 +3550,16 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf)
+ struct w9968cf_device* cam =
+ (struct w9968cf_device*)usb_get_intfdata(intf);
+
+- down_write(&w9968cf_disconnect);
+-
+ if (cam) {
++ down_write(&w9968cf_disconnect);
+ /* Prevent concurrent accesses to data */
+ mutex_lock(&cam->dev_mutex);
+
+ cam->disconnected = 1;
+
+- DBG(2, "Disconnecting %s...", symbolic(camlist, cam->id))
++ DBG(2, "Disconnecting %s...", symbolic(camlist, cam->id));
++
++ v4l2_device_disconnect(&cam->v4l2_dev);
+
+ wake_up_interruptible_all(&cam->open);
+
+@@ -3621,12 +3575,12 @@ static void w9968cf_usb_disconnect(struct usb_interface* intf)
+ w9968cf_release_resources(cam);
+
+ mutex_unlock(&cam->dev_mutex);
++ up_write(&w9968cf_disconnect);
+
+- if (!cam->users)
++ if (!cam->users) {
+ kfree(cam);
++ }
+ }
+-
+- up_write(&w9968cf_disconnect);
+ }
+
+
+@@ -3650,9 +3604,6 @@ static int __init w9968cf_module_init(void)
+ KDBG(2, W9968CF_MODULE_NAME" "W9968CF_MODULE_VERSION)
+ KDBG(3, W9968CF_MODULE_AUTHOR)
+
+- if (ovmod_load)
+- request_module("ovcamchip");
+-
+ if ((err = usb_register(&w9968cf_usb_driver)))
+ return err;
+
+diff --git a/drivers/media/video/w9968cf.h b/drivers/media/video/w9968cf.h
+index 30032e1..fdfc6a4 100644
+--- a/drivers/media/video/w9968cf.h
++++ b/drivers/media/video/w9968cf.h
+@@ -33,6 +33,7 @@
+ #include <linux/rwsem.h>
+ #include <linux/mutex.h>
+
++#include <media/v4l2-device.h>
+ #include <media/ovcamchip.h>
+
+ #include "w9968cf_vpp.h"
+@@ -42,7 +43,6 @@
+ * Default values *
+ ****************************************************************************/
+
+-#define W9968CF_OVMOD_LOAD 1 /* automatic 'ovcamchip' module loading */
+ #define W9968CF_VPPMOD_LOAD 1 /* automatic 'w9968cf-vpp' module loading */
+
+ /* Comment/uncomment the following line to enable/disable debugging messages */
+@@ -195,10 +195,9 @@ enum w9968cf_vpp_flag {
+
+ /* Main device driver structure */
+ struct w9968cf_device {
+- struct device dev; /* device structure */
+-
+ enum w9968cf_model_id id; /* private device identifier */
+
++ struct v4l2_device v4l2_dev;
+ struct video_device* v4ldev; /* -> V4L structure */
+ struct list_head v4llist; /* entry of the list of V4L cameras */
+
+@@ -265,7 +264,7 @@ struct w9968cf_device {
+
+ /* I2C interface to kernel */
+ struct i2c_adapter i2c_adapter;
+- struct i2c_client* sensor_client;
++ struct v4l2_subdev *sensor_sd;
+
+ /* Locks */
+ struct mutex dev_mutex, /* for probe, disconnect,open and close */
+@@ -277,6 +276,11 @@ struct w9968cf_device {
+ char command[16]; /* name of the program holding the device */
+ };
+
++static inline struct w9968cf_device *to_cam(struct v4l2_device *v4l2_dev)
++{
++ return container_of(v4l2_dev, struct w9968cf_device, v4l2_dev);
++}
++
+
+ /****************************************************************************
+ * Macros for debugging *
+@@ -291,14 +295,14 @@ struct w9968cf_device {
+ if ( ((specific_debug) && (debug == (level))) || \
+ ((!specific_debug) && (debug >= (level))) ) { \
+ if ((level) == 1) \
+- dev_err(&cam->dev, fmt "\n", ## args); \
++ v4l2_err(&cam->v4l2_dev, fmt "\n", ## args); \
+ else if ((level) == 2 || (level) == 3) \
+- dev_info(&cam->dev, fmt "\n", ## args); \
++ v4l2_info(&cam->v4l2_dev, fmt "\n", ## args); \
+ else if ((level) == 4) \
+- dev_warn(&cam->dev, fmt "\n", ## args); \
++ v4l2_warn(&cam->v4l2_dev, fmt "\n", ## args); \
+ else if ((level) >= 5) \
+- dev_info(&cam->dev, "[%s:%d] " fmt "\n", \
+- __func__, __LINE__ , ## args); \
++ v4l2_info(&cam->v4l2_dev, "[%s:%d] " fmt "\n", \
++ __func__, __LINE__ , ## args); \
+ } \
+ }
+ /* For generic kernel (not device specific) messages */
+@@ -321,7 +325,7 @@ struct w9968cf_device {
+
+ #undef PDBG
+ #define PDBG(fmt, args...) \
+-dev_info(&cam->dev, "[%s:%d] " fmt "\n", __func__, __LINE__ , ## args);
++v4l2_info(&cam->v4l2_dev, "[%s:%d] " fmt "\n", __func__, __LINE__ , ## args);
+
+ #undef PDBGG
+ #define PDBGG(fmt, args...) do {;} while(0); /* nothing: it's a placeholder */
+diff --git a/drivers/media/video/wm8739.c b/drivers/media/video/wm8739.c
+index f2864d5..b572ce2 100644
+--- a/drivers/media/video/wm8739.c
++++ b/drivers/media/video/wm8739.c
+@@ -252,11 +252,6 @@ static int wm8739_log_status(struct v4l2_subdev *sd)
+ return 0;
+ }
+
+-static int wm8739_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+-}
+-
+ /* ----------------------------------------------------------------------- */
+
+ static const struct v4l2_subdev_core_ops wm8739_core_ops = {
+@@ -343,8 +338,6 @@ MODULE_DEVICE_TABLE(i2c, wm8739_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "wm8739",
+- .driverid = I2C_DRIVERID_WM8739,
+- .command = wm8739_command,
+ .probe = wm8739_probe,
+ .remove = wm8739_remove,
+ .id_table = wm8739_id,
+diff --git a/drivers/media/video/wm8775.c b/drivers/media/video/wm8775.c
+index 53fcd42..eddf11a 100644
+--- a/drivers/media/video/wm8775.c
++++ b/drivers/media/video/wm8775.c
+@@ -34,15 +34,12 @@
+ #include <linux/videodev2.h>
+ #include <media/v4l2-device.h>
+ #include <media/v4l2-chip-ident.h>
+-#include <media/v4l2-i2c-drv-legacy.h>
++#include <media/v4l2-i2c-drv.h>
+
+ MODULE_DESCRIPTION("wm8775 driver");
+ MODULE_AUTHOR("Ulf Eklund, Hans Verkuil");
+ MODULE_LICENSE("GPL");
+
+-static unsigned short normal_i2c[] = { 0x36 >> 1, I2C_CLIENT_END };
+-
+-I2C_CLIENT_INSMOD;
+
+
+ /* ----------------------------------------------------------------------- */
+@@ -161,11 +158,6 @@ static int wm8775_s_frequency(struct v4l2_subdev *sd, struct v4l2_frequency *fre
+ return 0;
+ }
+
+-static int wm8775_command(struct i2c_client *client, unsigned cmd, void *arg)
+-{
+- return v4l2_subdev_command(i2c_get_clientdata(client), cmd, arg);
+-}
+-
+ /* ----------------------------------------------------------------------- */
+
+ static const struct v4l2_subdev_core_ops wm8775_core_ops = {
+@@ -268,8 +260,6 @@ MODULE_DEVICE_TABLE(i2c, wm8775_id);
+
+ static struct v4l2_i2c_driver_data v4l2_i2c_data = {
+ .name = "wm8775",
+- .driverid = I2C_DRIVERID_WM8775,
+- .command = wm8775_command,
+ .probe = wm8775_probe,
+ .remove = wm8775_remove,
+ .id_table = wm8775_id,
+diff --git a/drivers/media/video/zc0301/zc0301_sensor.h b/drivers/media/video/zc0301/zc0301_sensor.h
+index b0cd49c..3a408de 100644
+--- a/drivers/media/video/zc0301/zc0301_sensor.h
++++ b/drivers/media/video/zc0301/zc0301_sensor.h
+@@ -58,12 +58,20 @@ zc0301_attach_sensor(struct zc0301_device* cam, struct zc0301_sensor* sensor);
+ .idProduct = (prod), \
+ .bInterfaceClass = (intclass)
+
++#if !defined CONFIG_USB_GSPCA && !defined CONFIG_USB_GSPCA_MODULE
+ #define ZC0301_ID_TABLE \
+ static const struct usb_device_id zc0301_id_table[] = { \
+ { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \
+ { ZC0301_USB_DEVICE(0x0ac8, 0x303b, 0xff), }, /* PB-0330 */ \
+ { } \
+ };
++#else
++#define ZC0301_ID_TABLE \
++static const struct usb_device_id zc0301_id_table[] = { \
++ { ZC0301_USB_DEVICE(0x046d, 0x08ae, 0xff), }, /* PAS202 */ \
++ { } \
++};
++#endif
+
+ /*****************************************************************************/
+
+diff --git a/drivers/media/video/zoran/Kconfig b/drivers/media/video/zoran/Kconfig
+index 8666e19..fd4120e 100644
+--- a/drivers/media/video/zoran/Kconfig
++++ b/drivers/media/video/zoran/Kconfig
+@@ -1,6 +1,6 @@
+ config VIDEO_ZORAN
+ tristate "Zoran ZR36057/36067 Video For Linux"
+- depends on PCI && I2C_ALGOBIT && VIDEO_V4L1 && VIRT_TO_BUS
++ depends on PCI && I2C_ALGOBIT && VIDEO_V4L2 && VIRT_TO_BUS
+ help
+ Say Y for support for MJPEG capture cards based on the Zoran
+ 36057/36067 PCI controller chipset. This includes the Iomega
+@@ -32,7 +32,7 @@ config VIDEO_ZORAN_ZR36060
+ config VIDEO_ZORAN_BUZ
+ tristate "Iomega Buz support"
+ depends on VIDEO_ZORAN_ZR36060
+- select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO
++ select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_SAA7185 if VIDEO_HELPER_CHIPS_AUTO
+ help
+ Support for the Iomega Buz MJPEG capture/playback card.
+@@ -58,7 +58,7 @@ config VIDEO_ZORAN_LML33
+ config VIDEO_ZORAN_LML33R10
+ tristate "Linux Media Labs LML33R10 support"
+ depends on VIDEO_ZORAN_ZR36060
+- select VIDEO_SAA7114 if VIDEO_HELPER_CHIPS_AUTO
++ select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_ADV7170 if VIDEO_HELPER_CHIPS_AUTO
+ help
+ support for the Linux Media Labs LML33R10 MJPEG capture/playback
+@@ -66,7 +66,7 @@ config VIDEO_ZORAN_LML33R10
+
+ config VIDEO_ZORAN_AVS6EYES
+ tristate "AverMedia 6 Eyes support (EXPERIMENTAL)"
+- depends on VIDEO_ZORAN_ZR36060 && EXPERIMENTAL && VIDEO_V4L1
++ depends on VIDEO_ZORAN_ZR36060 && EXPERIMENTAL
+ select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_BT866 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_KS0127 if VIDEO_HELPER_CHIPS_AUTO
+diff --git a/drivers/media/video/zoran/videocodec.h b/drivers/media/video/zoran/videocodec.h
+index 97a3bbe..5c27b25 100644
+--- a/drivers/media/video/zoran/videocodec.h
++++ b/drivers/media/video/zoran/videocodec.h
+@@ -97,7 +97,7 @@
+ available) - it returns 0 if the mode is possible
+ set_size -> this fn-ref. sets the norm and image size for
+ compression/decompression (returns 0 on success)
+- the norm param is defined in videodev.h (VIDEO_MODE_*)
++ the norm param is defined in videodev2.h (V4L2_STD_*)
+
+ additional setup may be available, too - but the codec should work with
+ some default values even without this
+@@ -144,9 +144,8 @@ M zr36055[1] 0001 0000c001 00000000 (zr36050[1])
+ #ifndef __LINUX_VIDEOCODEC_H
+ #define __LINUX_VIDEOCODEC_H
+
+-#include <linux/videodev.h>
++#include <linux/videodev2.h>
+
+-//should be in videodev.h ??? (VID_DO_....)
+ #define CODEC_DO_COMPRESSION 0
+ #define CODEC_DO_EXPANSION 1
+
+@@ -237,10 +236,6 @@ struct vfe_settings {
+ __u32 width, height; /* Area to capture */
+ __u16 decimation; /* Decimation divider */
+ __u16 flags; /* Flags for capture */
+-/* flags are the same as in struct video_capture - see videodev.h:
+-#define VIDEO_CAPTURE_ODD 0
+-#define VIDEO_CAPTURE_EVEN 1
+-*/
+ __u16 quality; /* quality of the video */
+ };
+
+diff --git a/drivers/media/video/zoran/zoran.h b/drivers/media/video/zoran/zoran.h
+index e873a91..afecf32 100644
+--- a/drivers/media/video/zoran/zoran.h
++++ b/drivers/media/video/zoran/zoran.h
+@@ -31,6 +31,8 @@
+ #ifndef _BUZ_H_
+ #define _BUZ_H_
+
++#include <media/v4l2-device.h>
++
+ struct zoran_requestbuffers {
+ unsigned long count; /* Number of buffers for MJPEG grabbing */
+ unsigned long size; /* Size PER BUFFER in bytes */
+@@ -170,7 +172,7 @@ Private IOCTL to set up for displaying MJPEG
+ #endif
+ #define V4L_MASK_FRAME (V4L_MAX_FRAME - 1)
+
+-#define MAX_KMALLOC_MEM (128*1024)
++#define MAX_FRAME (BUZ_MAX_FRAME > VIDEO_MAX_FRAME ? BUZ_MAX_FRAME : VIDEO_MAX_FRAME)
+
+ #include "zr36057.h"
+
+@@ -240,9 +242,6 @@ enum gpcs_type {
+
+ struct zoran_format {
+ char *name;
+-#ifdef CONFIG_VIDEO_V4L1_COMPAT
+- int palette;
+-#endif
+ __u32 fourcc;
+ int colorspace;
+ int depth;
+@@ -283,21 +282,21 @@ struct zoran_mapping {
+ int count;
+ };
+
+-struct zoran_jpg_buffer {
+- struct zoran_mapping *map;
+- __le32 *frag_tab; /* addresses of frag table */
+- u32 frag_tab_bus; /* same value cached to save time in ISR */
+- enum zoran_buffer_state state; /* non-zero if corresponding buffer is in use in grab queue */
+- struct zoran_sync bs; /* DONE: info to return to application */
+-};
+-
+-struct zoran_v4l_buffer {
++struct zoran_buffer {
+ struct zoran_mapping *map;
+- char *fbuffer; /* virtual address of frame buffer */
+- unsigned long fbuffer_phys; /* physical address of frame buffer */
+- unsigned long fbuffer_bus; /* bus address of frame buffer */
+- enum zoran_buffer_state state; /* state: unused/pending/done */
+- struct zoran_sync bs; /* DONE: info to return to application */
++ enum zoran_buffer_state state; /* state: unused/pending/dma/done */
++ struct zoran_sync bs; /* DONE: info to return to application */
++ union {
++ struct {
++ __le32 *frag_tab; /* addresses of frag table */
++ u32 frag_tab_bus; /* same value cached to save time in ISR */
++ } jpg;
++ struct {
++ char *fbuffer; /* virtual address of frame buffer */
++ unsigned long fbuffer_phys;/* physical address of frame buffer */
++ unsigned long fbuffer_bus;/* bus address of frame buffer */
++ } v4l;
++ };
+ };
+
+ enum zoran_lock_activity {
+@@ -307,21 +306,13 @@ enum zoran_lock_activity {
+ };
+
+ /* buffer collections */
+-struct zoran_jpg_struct {
++struct zoran_buffer_col {
+ enum zoran_lock_activity active; /* feature currently in use? */
+- struct zoran_jpg_buffer buffer[BUZ_MAX_FRAME]; /* buffers */
+- int num_buffers, buffer_size;
++ unsigned int num_buffers, buffer_size;
++ struct zoran_buffer buffer[MAX_FRAME]; /* buffers */
+ u8 allocated; /* Flag if buffers are allocated */
+- u8 ready_to_be_freed; /* hack - see zoran_driver.c */
+ u8 need_contiguous; /* Flag if contiguous buffers are needed */
+-};
+-
+-struct zoran_v4l_struct {
+- enum zoran_lock_activity active; /* feature currently in use? */
+- struct zoran_v4l_buffer buffer[VIDEO_MAX_FRAME]; /* buffers */
+- int num_buffers, buffer_size;
+- u8 allocated; /* Flag if buffers are allocated */
+- u8 ready_to_be_freed; /* hack - see zoran_driver.c */
++ /* only applies to jpg buffers, raw buffers are always contiguous */
+ };
+
+ struct zoran;
+@@ -330,23 +321,27 @@ struct zoran;
+ struct zoran_fh {
+ struct zoran *zr;
+
+- enum zoran_map_mode map_mode; /* Flag which bufferset will map by next mmap() */
++ enum zoran_map_mode map_mode; /* Flag which bufferset will map by next mmap() */
+
+ struct zoran_overlay_settings overlay_settings;
+- u32 *overlay_mask; /* overlay mask */
+- enum zoran_lock_activity overlay_active; /* feature currently in use? */
++ u32 *overlay_mask; /* overlay mask */
++ enum zoran_lock_activity overlay_active;/* feature currently in use? */
+
+- struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */
+- struct zoran_v4l_struct v4l_buffers; /* V4L buffers' info */
++ struct zoran_buffer_col buffers; /* buffers' info */
+
++ struct zoran_v4l_settings v4l_settings; /* structure with a lot of things to play with */
+ struct zoran_jpg_settings jpg_settings; /* structure with a lot of things to play with */
+- struct zoran_jpg_struct jpg_buffers; /* MJPEG buffers' info */
+ };
+
+ struct card_info {
+ enum card_type type;
+ char name[32];
+- u16 i2c_decoder, i2c_encoder; /* I2C types */
++ const char *i2c_decoder; /* i2c decoder device */
++ const char *mod_decoder; /* i2c decoder module */
++ const unsigned short *addrs_decoder;
++ const char *i2c_encoder; /* i2c encoder device */
++ const char *mod_encoder; /* i2c encoder module */
++ const unsigned short *addrs_encoder;
+ u16 video_vfe, video_codec; /* videocodec types */
+ u16 audio_chip; /* audio type */
+
+@@ -356,7 +351,7 @@ struct card_info {
+ char name[32];
+ } input[BUZ_MAX_INPUT];
+
+- int norms;
++ v4l2_std_id norms;
+ struct tvnorm *tvn[3]; /* supported TV norms */
+
+ u32 jpeg_int; /* JPEG interrupt */
+@@ -377,14 +372,15 @@ struct card_info {
+ };
+
+ struct zoran {
++ struct v4l2_device v4l2_dev;
+ struct video_device *video_dev;
+
+ struct i2c_adapter i2c_adapter; /* */
+ struct i2c_algo_bit_data i2c_algo; /* */
+ u32 i2cbr;
+
+- struct i2c_client *decoder; /* video decoder i2c client */
+- struct i2c_client *encoder; /* video encoder i2c client */
++ struct v4l2_subdev *decoder; /* video decoder sub-device */
++ struct v4l2_subdev *encoder; /* video encoder sub-device */
+
+ struct videocodec *codec; /* video codec */
+ struct videocodec *vfe; /* video front end */
+@@ -405,9 +401,15 @@ struct zoran {
+ spinlock_t spinlock; /* Spinlock */
+
+ /* Video for Linux parameters */
+- int input, norm; /* card's norm and input - norm=VIDEO_MODE_* */
+- int hue, saturation, contrast, brightness; /* Current picture params */
+- struct video_buffer buffer; /* Current buffer params */
++ int input; /* card's norm and input - norm=VIDEO_MODE_* */
++ v4l2_std_id norm;
++
++ /* Current buffer params */
++ void *vbuf_base;
++ int vbuf_height, vbuf_width;
++ int vbuf_depth;
++ int vbuf_bytesperline;
++
+ struct zoran_overlay_settings overlay_settings;
+ u32 *overlay_mask; /* overlay mask */
+ enum zoran_lock_activity overlay_active; /* feature currently in use? */
+@@ -427,7 +429,7 @@ struct zoran {
+ unsigned long v4l_pend_tail;
+ unsigned long v4l_sync_tail;
+ int v4l_pend[V4L_MAX_FRAME];
+- struct zoran_v4l_struct v4l_buffers; /* V4L buffers' info */
++ struct zoran_buffer_col v4l_buffers; /* V4L buffers' info */
+
+ /* Buz MJPEG parameters */
+ enum zoran_codec_mode codec_mode; /* status of codec */
+@@ -454,7 +456,7 @@ struct zoran {
+ int jpg_pend[BUZ_MAX_FRAME];
+
+ /* array indexed by frame number */
+- struct zoran_jpg_struct jpg_buffers; /* MJPEG buffers' info */
++ struct zoran_buffer_col jpg_buffers; /* MJPEG buffers' info */
+
+ /* Additional stuff for testing */
+ #ifdef CONFIG_PROC_FS
+@@ -488,6 +490,11 @@ struct zoran {
+ wait_queue_head_t test_q;
+ };
+
++static inline struct zoran *to_zoran(struct v4l2_device *v4l2_dev)
++{
++ return container_of(v4l2_dev, struct zoran, v4l2_dev);
++}
++
+ /* There was something called _ALPHA_BUZ that used the PCI address instead of
+ * the kernel iomapped address for btread/btwrite. */
+ #define btwrite(dat,adr) writel((dat), zr->zr36057_mem+(adr))
+diff --git a/drivers/media/video/zoran/zoran_card.c b/drivers/media/video/zoran/zoran_card.c
+index 5d2f090..f91bba4 100644
+--- a/drivers/media/video/zoran/zoran_card.c
++++ b/drivers/media/video/zoran/zoran_card.c
+@@ -38,8 +38,7 @@
+ #include <linux/proc_fs.h>
+ #include <linux/i2c.h>
+ #include <linux/i2c-algo-bit.h>
+-#include <linux/videodev.h>
+-#include <media/v4l2-common.h>
++#include <linux/videodev2.h>
+ #include <linux/spinlock.h>
+ #include <linux/sem.h>
+ #include <linux/kmod.h>
+@@ -47,11 +46,10 @@
+
+ #include <linux/pci.h>
+ #include <linux/interrupt.h>
+-#include <linux/video_decoder.h>
+-#include <linux/video_encoder.h>
+ #include <linux/mutex.h>
+-
+-#include <asm/io.h>
++#include <linux/io.h>
++#include <media/v4l2-common.h>
++#include <media/bt819.h>
+
+ #include "videocodec.h"
+ #include "zoran.h"
+@@ -108,25 +106,8 @@ static int video_nr[BUZ_MAX] = { [0 ... (BUZ_MAX-1)] = -1 };
+ module_param_array(video_nr, int, NULL, 0444);
+ MODULE_PARM_DESC(video_nr, "Video device number (-1=Auto)");
+
+-/*
+- Number and size of grab buffers for Video 4 Linux
+- The vast majority of applications should not need more than 2,
+- the very popular BTTV driver actually does ONLY have 2.
+- Time sensitive applications might need more, the maximum
+- is VIDEO_MAX_FRAME (defined in <linux/videodev.h>).
+-
+- The size is set so that the maximum possible request
+- can be satisfied. Decrease it, if bigphys_area alloc'd
+- memory is low. If you don't have the bigphys_area patch,
+- set it to 128 KB. Will you allow only to grab small
+- images with V4L, but that's better than nothing.
+-
+- v4l_bufsize has to be given in KB !
+-
+-*/
+-
+-int v4l_nbufs = 2;
+-int v4l_bufsize = 128; /* Everybody should be able to work with this setting */
++int v4l_nbufs = 4;
++int v4l_bufsize = 864; /* Everybody should be able to work with this setting */
+ module_param(v4l_nbufs, int, 0644);
+ MODULE_PARM_DESC(v4l_nbufs, "Maximum number of V4L buffers to use");
+ module_param(v4l_bufsize, int, 0644);
+@@ -273,7 +254,7 @@ zr36016_write (struct videocodec *codec,
+ static void
+ dc10_init (struct zoran *zr)
+ {
+- dprintk(3, KERN_DEBUG "%s: dc10_init()\n", ZR_DEVNAME(zr));
++ dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+ /* Pixel clock selection */
+ GPIO(zr, 4, 0);
+@@ -285,13 +266,13 @@ dc10_init (struct zoran *zr)
+ static void
+ dc10plus_init (struct zoran *zr)
+ {
+- dprintk(3, KERN_DEBUG "%s: dc10plus_init()\n", ZR_DEVNAME(zr));
++ dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+ }
+
+ static void
+ buz_init (struct zoran *zr)
+ {
+- dprintk(3, KERN_DEBUG "%s: buz_init()\n", ZR_DEVNAME(zr));
++ dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+ /* some stuff from Iomega */
+ pci_write_config_dword(zr->pci_dev, 0xfc, 0x90680f15);
+@@ -302,7 +283,7 @@ buz_init (struct zoran *zr)
+ static void
+ lml33_init (struct zoran *zr)
+ {
+- dprintk(3, KERN_DEBUG "%s: lml33_init()\n", ZR_DEVNAME(zr));
++ dprintk(3, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+ GPIO(zr, 2, 1); // Set Composite input/output
+ }
+@@ -332,50 +313,6 @@ avs6eyes_init (struct zoran *zr)
+ }
+
+ static char *
+-i2cid_to_modulename (u16 i2c_id)
+-{
+- char *name = NULL;
+-
+- switch (i2c_id) {
+- case I2C_DRIVERID_SAA7110:
+- name = "saa7110";
+- break;
+- case I2C_DRIVERID_SAA7111A:
+- name = "saa7111";
+- break;
+- case I2C_DRIVERID_SAA7114:
+- name = "saa7114";
+- break;
+- case I2C_DRIVERID_SAA7185B:
+- name = "saa7185";
+- break;
+- case I2C_DRIVERID_ADV7170:
+- name = "adv7170";
+- break;
+- case I2C_DRIVERID_ADV7175:
+- name = "adv7175";
+- break;
+- case I2C_DRIVERID_BT819:
+- name = "bt819";
+- break;
+- case I2C_DRIVERID_BT856:
+- name = "bt856";
+- break;
+- case I2C_DRIVERID_BT866:
+- name = "bt866";
+- break;
+- case I2C_DRIVERID_VPX3220:
+- name = "vpx3220";
+- break;
+- case I2C_DRIVERID_KS0127:
+- name = "ks0127";
+- break;
+- }
+-
+- return name;
+-}
+-
+-static char *
+ codecid_to_modulename (u16 codecid)
+ {
+ char *name = NULL;
+@@ -425,11 +362,24 @@ static struct tvnorm f60ccir601_lm33r10 = { 858, 720, 56+54, 788, 525, 480, 16 }
+ static struct tvnorm f50ccir601_avs6eyes = { 864, 720, 74, 804, 625, 576, 18 };
+ static struct tvnorm f60ccir601_avs6eyes = { 858, 720, 56, 788, 525, 480, 16 };
+
++static const unsigned short vpx3220_addrs[] = { 0x43, 0x47, I2C_CLIENT_END };
++static const unsigned short saa7110_addrs[] = { 0x4e, 0x4f, I2C_CLIENT_END };
++static const unsigned short saa7111_addrs[] = { 0x25, 0x24, I2C_CLIENT_END };
++static const unsigned short saa7114_addrs[] = { 0x21, 0x20, I2C_CLIENT_END };
++static const unsigned short adv717x_addrs[] = { 0x6a, 0x6b, 0x2a, 0x2b, I2C_CLIENT_END };
++static const unsigned short ks0127_addrs[] = { 0x6c, 0x6d, I2C_CLIENT_END };
++static const unsigned short saa7185_addrs[] = { 0x44, I2C_CLIENT_END };
++static const unsigned short bt819_addrs[] = { 0x45, I2C_CLIENT_END };
++static const unsigned short bt856_addrs[] = { 0x44, I2C_CLIENT_END };
++static const unsigned short bt866_addrs[] = { 0x44, I2C_CLIENT_END };
++
+ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ {
+ .type = DC10_old,
+ .name = "DC10(old)",
+- .i2c_decoder = I2C_DRIVERID_VPX3220,
++ .i2c_decoder = "vpx3220a",
++ .mod_decoder = "vpx3220",
++ .addrs_decoder = vpx3220_addrs,
+ .video_codec = CODEC_TYPE_ZR36050,
+ .video_vfe = CODEC_TYPE_ZR36016,
+
+@@ -439,7 +389,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ { 2, "S-Video" },
+ { 0, "Internal/comp" }
+ },
+- .norms = 3,
++ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+ .tvn = {
+ &f50sqpixel_dc10,
+ &f60sqpixel_dc10,
+@@ -457,8 +407,12 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ }, {
+ .type = DC10_new,
+ .name = "DC10(new)",
+- .i2c_decoder = I2C_DRIVERID_SAA7110,
+- .i2c_encoder = I2C_DRIVERID_ADV7175,
++ .i2c_decoder = "saa7110",
++ .mod_decoder = "saa7110",
++ .addrs_decoder = saa7110_addrs,
++ .i2c_encoder = "adv7175",
++ .mod_encoder = "adv7175",
++ .addrs_encoder = adv717x_addrs,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 3,
+@@ -467,7 +421,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ { 7, "S-Video" },
+ { 5, "Internal/comp" }
+ },
+- .norms = 3,
++ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+ .tvn = {
+ &f50sqpixel,
+ &f60sqpixel,
+@@ -484,8 +438,12 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ }, {
+ .type = DC10plus,
+ .name = "DC10plus",
+- .i2c_decoder = I2C_DRIVERID_SAA7110,
+- .i2c_encoder = I2C_DRIVERID_ADV7175,
++ .i2c_decoder = "saa7110",
++ .mod_decoder = "saa7110",
++ .addrs_decoder = saa7110_addrs,
++ .i2c_encoder = "adv7175",
++ .mod_encoder = "adv7175",
++ .addrs_encoder = adv717x_addrs,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 3,
+@@ -494,7 +452,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ { 7, "S-Video" },
+ { 5, "Internal/comp" }
+ },
+- .norms = 3,
++ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+ .tvn = {
+ &f50sqpixel,
+ &f60sqpixel,
+@@ -512,8 +470,12 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ }, {
+ .type = DC30,
+ .name = "DC30",
+- .i2c_decoder = I2C_DRIVERID_VPX3220,
+- .i2c_encoder = I2C_DRIVERID_ADV7175,
++ .i2c_decoder = "vpx3220a",
++ .mod_decoder = "vpx3220",
++ .addrs_decoder = vpx3220_addrs,
++ .i2c_encoder = "adv7175",
++ .mod_encoder = "adv7175",
++ .addrs_encoder = adv717x_addrs,
+ .video_codec = CODEC_TYPE_ZR36050,
+ .video_vfe = CODEC_TYPE_ZR36016,
+
+@@ -523,7 +485,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ { 2, "S-Video" },
+ { 0, "Internal/comp" }
+ },
+- .norms = 3,
++ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+ .tvn = {
+ &f50sqpixel_dc10,
+ &f60sqpixel_dc10,
+@@ -541,8 +503,12 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ }, {
+ .type = DC30plus,
+ .name = "DC30plus",
+- .i2c_decoder = I2C_DRIVERID_VPX3220,
+- .i2c_encoder = I2C_DRIVERID_ADV7175,
++ .i2c_decoder = "vpx3220a",
++ .mod_decoder = "vpx3220",
++ .addrs_decoder = vpx3220_addrs,
++ .i2c_encoder = "adv7175",
++ .mod_encoder = "adv7175",
++ .addrs_encoder = adv717x_addrs,
+ .video_codec = CODEC_TYPE_ZR36050,
+ .video_vfe = CODEC_TYPE_ZR36016,
+
+@@ -552,7 +518,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ { 2, "S-Video" },
+ { 0, "Internal/comp" }
+ },
+- .norms = 3,
++ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+ .tvn = {
+ &f50sqpixel_dc10,
+ &f60sqpixel_dc10,
+@@ -570,8 +536,12 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ }, {
+ .type = LML33,
+ .name = "LML33",
+- .i2c_decoder = I2C_DRIVERID_BT819,
+- .i2c_encoder = I2C_DRIVERID_BT856,
++ .i2c_decoder = "bt819a",
++ .mod_decoder = "bt819",
++ .addrs_decoder = bt819_addrs,
++ .i2c_encoder = "bt856",
++ .mod_encoder = "bt856",
++ .addrs_encoder = bt856_addrs,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 2,
+@@ -579,7 +549,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ { 0, "Composite" },
+ { 7, "S-Video" }
+ },
+- .norms = 2,
++ .norms = V4L2_STD_NTSC|V4L2_STD_PAL,
+ .tvn = {
+ &f50ccir601_lml33,
+ &f60ccir601_lml33,
+@@ -597,8 +567,12 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ }, {
+ .type = LML33R10,
+ .name = "LML33R10",
+- .i2c_decoder = I2C_DRIVERID_SAA7114,
+- .i2c_encoder = I2C_DRIVERID_ADV7170,
++ .i2c_decoder = "saa7114",
++ .mod_decoder = "saa7115",
++ .addrs_decoder = saa7114_addrs,
++ .i2c_encoder = "adv7170",
++ .mod_encoder = "adv7170",
++ .addrs_encoder = adv717x_addrs,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 2,
+@@ -606,7 +580,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ { 0, "Composite" },
+ { 7, "S-Video" }
+ },
+- .norms = 2,
++ .norms = V4L2_STD_NTSC|V4L2_STD_PAL,
+ .tvn = {
+ &f50ccir601_lm33r10,
+ &f60ccir601_lm33r10,
+@@ -624,8 +598,12 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ }, {
+ .type = BUZ,
+ .name = "Buz",
+- .i2c_decoder = I2C_DRIVERID_SAA7111A,
+- .i2c_encoder = I2C_DRIVERID_SAA7185B,
++ .i2c_decoder = "saa7111",
++ .mod_decoder = "saa7115",
++ .addrs_decoder = saa7111_addrs,
++ .i2c_encoder = "saa7185",
++ .mod_encoder = "saa7185",
++ .addrs_encoder = saa7185_addrs,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 2,
+@@ -633,7 +611,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ { 3, "Composite" },
+ { 7, "S-Video" }
+ },
+- .norms = 3,
++ .norms = V4L2_STD_NTSC|V4L2_STD_PAL|V4L2_STD_SECAM,
+ .tvn = {
+ &f50ccir601,
+ &f60ccir601,
+@@ -653,8 +631,12 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ .name = "6-Eyes",
+ /* AverMedia chose not to brand the 6-Eyes. Thus it
+ can't be autodetected, and requires card=x. */
+- .i2c_decoder = I2C_DRIVERID_KS0127,
+- .i2c_encoder = I2C_DRIVERID_BT866,
++ .i2c_decoder = "ks0127",
++ .mod_decoder = "ks0127",
++ .addrs_decoder = ks0127_addrs,
++ .i2c_encoder = "bt866",
++ .mod_encoder = "bt866",
++ .addrs_encoder = bt866_addrs,
+ .video_codec = CODEC_TYPE_ZR36060,
+
+ .inputs = 10,
+@@ -670,7 +652,7 @@ static struct card_info zoran_cards[NUM_CARDS] __devinitdata = {
+ {10, "S-Video 3" },
+ {15, "YCbCr" }
+ },
+- .norms = 2,
++ .norms = V4L2_STD_NTSC|V4L2_STD_PAL,
+ .tvn = {
+ &f50ccir601_avs6eyes,
+ &f60ccir601_avs6eyes,
+@@ -735,69 +717,6 @@ zoran_i2c_setscl (void *data,
+ btwrite(zr->i2cbr, ZR36057_I2CBR);
+ }
+
+-static int
+-zoran_i2c_client_register (struct i2c_client *client)
+-{
+- struct zoran *zr = (struct zoran *) i2c_get_adapdata(client->adapter);
+- int res = 0;
+-
+- dprintk(2,
+- KERN_DEBUG "%s: i2c_client_register() - driver id = %d\n",
+- ZR_DEVNAME(zr), client->driver->id);
+-
+- mutex_lock(&zr->resource_lock);
+-
+- if (zr->user > 0) {
+- /* we're already busy, so we keep a reference to
+- * them... Could do a lot of stuff here, but this
+- * is easiest. (Did I ever mention I'm a lazy ass?)
+- */
+- res = -EBUSY;
+- goto clientreg_unlock_and_return;
+- }
+-
+- if (client->driver->id == zr->card.i2c_decoder)
+- zr->decoder = client;
+- else if (client->driver->id == zr->card.i2c_encoder)
+- zr->encoder = client;
+- else {
+- res = -ENODEV;
+- goto clientreg_unlock_and_return;
+- }
+-
+-clientreg_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
+-
+- return res;
+-}
+-
+-static int
+-zoran_i2c_client_unregister (struct i2c_client *client)
+-{
+- struct zoran *zr = (struct zoran *) i2c_get_adapdata(client->adapter);
+- int res = 0;
+-
+- dprintk(2, KERN_DEBUG "%s: i2c_client_unregister()\n", ZR_DEVNAME(zr));
+-
+- mutex_lock(&zr->resource_lock);
+-
+- if (zr->user > 0) {
+- res = -EBUSY;
+- goto clientunreg_unlock_and_return;
+- }
+-
+- /* try to locate it */
+- if (client == zr->encoder) {
+- zr->encoder = NULL;
+- } else if (client == zr->decoder) {
+- zr->decoder = NULL;
+- snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%d]", zr->id);
+- }
+-clientunreg_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
+- return res;
+-}
+-
+ static const struct i2c_algo_bit_data zoran_i2c_bit_data_template = {
+ .setsda = zoran_i2c_setsda,
+ .setscl = zoran_i2c_setscl,
+@@ -813,13 +732,10 @@ zoran_register_i2c (struct zoran *zr)
+ memcpy(&zr->i2c_algo, &zoran_i2c_bit_data_template,
+ sizeof(struct i2c_algo_bit_data));
+ zr->i2c_algo.data = zr;
+- zr->i2c_adapter.class = I2C_CLASS_TV_ANALOG;
+ zr->i2c_adapter.id = I2C_HW_B_ZR36067;
+- zr->i2c_adapter.client_register = zoran_i2c_client_register;
+- zr->i2c_adapter.client_unregister = zoran_i2c_client_unregister;
+ strlcpy(zr->i2c_adapter.name, ZR_DEVNAME(zr),
+ sizeof(zr->i2c_adapter.name));
+- i2c_set_adapdata(&zr->i2c_adapter, zr);
++ i2c_set_adapdata(&zr->i2c_adapter, &zr->v4l2_dev);
+ zr->i2c_adapter.algo_data = &zr->i2c_algo;
+ zr->i2c_adapter.dev.parent = &zr->pci_dev->dev;
+ return i2c_bit_add_bus(&zr->i2c_adapter);
+@@ -835,19 +751,20 @@ zoran_unregister_i2c (struct zoran *zr)
+
+ int
+ zoran_check_jpg_settings (struct zoran *zr,
+- struct zoran_jpg_settings *settings)
++ struct zoran_jpg_settings *settings,
++ int try)
+ {
+ int err = 0, err0 = 0;
+
+ dprintk(4,
+ KERN_DEBUG
+- "%s: check_jpg_settings() - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n",
+- ZR_DEVNAME(zr), settings->decimation, settings->HorDcm,
++ "%s: %s - dec: %d, Hdcm: %d, Vdcm: %d, Tdcm: %d\n",
++ ZR_DEVNAME(zr), __func__, settings->decimation, settings->HorDcm,
+ settings->VerDcm, settings->TmpDcm);
+ dprintk(4,
+ KERN_DEBUG
+- "%s: check_jpg_settings() - x: %d, y: %d, w: %d, y: %d\n",
+- ZR_DEVNAME(zr), settings->img_x, settings->img_y,
++ "%s: %s - x: %d, y: %d, w: %d, y: %d\n",
++ ZR_DEVNAME(zr), __func__, settings->img_x, settings->img_y,
+ settings->img_width, settings->img_height);
+ /* Check decimation, set default values for decimation = 1, 2, 4 */
+ switch (settings->decimation) {
+@@ -879,8 +796,8 @@ zoran_check_jpg_settings (struct zoran *zr,
+ if (zr->card.type == DC10_new) {
+ dprintk(1,
+ KERN_DEBUG
+- "%s: check_jpg_settings() - HDec by 4 is not supported on the DC10\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - HDec by 4 is not supported on the DC10\n",
++ ZR_DEVNAME(zr), __func__);
+ err0++;
+ break;
+ }
+@@ -900,50 +817,73 @@ zoran_check_jpg_settings (struct zoran *zr,
+ /* We have to check the data the user has set */
+
+ if (settings->HorDcm != 1 && settings->HorDcm != 2 &&
+- (zr->card.type == DC10_new || settings->HorDcm != 4))
++ (zr->card.type == DC10_new || settings->HorDcm != 4)) {
++ settings->HorDcm = clamp(settings->HorDcm, 1, 2);
+ err0++;
+- if (settings->VerDcm != 1 && settings->VerDcm != 2)
++ }
++ if (settings->VerDcm != 1 && settings->VerDcm != 2) {
++ settings->VerDcm = clamp(settings->VerDcm, 1, 2);
+ err0++;
+- if (settings->TmpDcm != 1 && settings->TmpDcm != 2)
++ }
++ if (settings->TmpDcm != 1 && settings->TmpDcm != 2) {
++ settings->TmpDcm = clamp(settings->TmpDcm, 1, 2);
+ err0++;
++ }
+ if (settings->field_per_buff != 1 &&
+- settings->field_per_buff != 2)
++ settings->field_per_buff != 2) {
++ settings->field_per_buff = clamp(settings->field_per_buff, 1, 2);
+ err0++;
+- if (settings->img_x < 0)
++ }
++ if (settings->img_x < 0) {
++ settings->img_x = 0;
+ err0++;
+- if (settings->img_y < 0)
++ }
++ if (settings->img_y < 0) {
++ settings->img_y = 0;
+ err0++;
+- if (settings->img_width < 0)
++ }
++ if (settings->img_width < 0 || settings->img_width > BUZ_MAX_WIDTH) {
++ settings->img_width = clamp(settings->img_width, 0, (int)BUZ_MAX_WIDTH);
+ err0++;
+- if (settings->img_height < 0)
++ }
++ if (settings->img_height < 0 || settings->img_height > BUZ_MAX_HEIGHT / 2) {
++ settings->img_height = clamp(settings->img_height, 0, BUZ_MAX_HEIGHT / 2);
+ err0++;
+- if (settings->img_x + settings->img_width > BUZ_MAX_WIDTH)
++ }
++ if (settings->img_x + settings->img_width > BUZ_MAX_WIDTH) {
++ settings->img_x = BUZ_MAX_WIDTH - settings->img_width;
+ err0++;
+- if (settings->img_y + settings->img_height >
+- BUZ_MAX_HEIGHT / 2)
++ }
++ if (settings->img_y + settings->img_height > BUZ_MAX_HEIGHT / 2) {
++ settings->img_y = BUZ_MAX_HEIGHT / 2 - settings->img_height;
++ err0++;
++ }
++ if (settings->img_width % (16 * settings->HorDcm) != 0) {
++ settings->img_width -= settings->img_width % (16 * settings->HorDcm);
++ if (settings->img_width == 0)
++ settings->img_width = 16 * settings->HorDcm;
++ err0++;
++ }
++ if (settings->img_height % (8 * settings->VerDcm) != 0) {
++ settings->img_height -= settings->img_height % (8 * settings->VerDcm);
++ if (settings->img_height == 0)
++ settings->img_height = 8 * settings->VerDcm;
+ err0++;
+- if (settings->HorDcm && settings->VerDcm) {
+- if (settings->img_width %
+- (16 * settings->HorDcm) != 0)
+- err0++;
+- if (settings->img_height %
+- (8 * settings->VerDcm) != 0)
+- err0++;
+ }
+
+- if (err0) {
++ if (!try && err0) {
+ dprintk(1,
+ KERN_ERR
+- "%s: check_jpg_settings() - error in params for decimation = 0\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - error in params for decimation = 0\n",
++ ZR_DEVNAME(zr), __func__);
+ err++;
+ }
+ break;
+ default:
+ dprintk(1,
+ KERN_ERR
+- "%s: check_jpg_settings() - decimation = %d, must be 0, 1, 2 or 4\n",
+- ZR_DEVNAME(zr), settings->decimation);
++ "%s: %s - decimation = %d, must be 0, 1, 2 or 4\n",
++ ZR_DEVNAME(zr), __func__, settings->decimation);
+ err++;
+ break;
+ }
+@@ -1021,12 +961,10 @@ zoran_open_init_params (struct zoran *zr)
+ sizeof(zr->jpg_settings.jpg_comp.COM_data));
+ zr->jpg_settings.jpg_comp.jpeg_markers =
+ JPEG_MARKER_DHT | JPEG_MARKER_DQT;
+- i = zoran_check_jpg_settings(zr, &zr->jpg_settings);
++ i = zoran_check_jpg_settings(zr, &zr->jpg_settings, 0);
+ if (i)
+- dprintk(1,
+- KERN_ERR
+- "%s: zoran_open_init_params() internal error\n",
+- ZR_DEVNAME(zr));
++ dprintk(1, KERN_ERR "%s: %s internal error\n",
++ ZR_DEVNAME(zr), __func__);
+
+ clear_interrupt_counters(zr);
+ zr->testing = 0;
+@@ -1062,13 +1000,11 @@ static int __devinit
+ zr36057_init (struct zoran *zr)
+ {
+ int j, err;
+- int two = 2;
+- int zero = 0;
+
+ dprintk(1,
+ KERN_INFO
+- "%s: zr36057_init() - initializing card[%d], zr=%p\n",
+- ZR_DEVNAME(zr), zr->id, zr);
++ "%s: %s - initializing card[%d], zr=%p\n",
++ ZR_DEVNAME(zr), __func__, zr->id, zr);
+
+ /* default setup of all parameters which will persist between opens */
+ zr->user = 0;
+@@ -1079,24 +1015,32 @@ zr36057_init (struct zoran *zr)
+ zr->jpg_buffers.allocated = 0;
+ zr->v4l_buffers.allocated = 0;
+
+- zr->buffer.base = (void *) vidmem;
+- zr->buffer.width = 0;
+- zr->buffer.height = 0;
+- zr->buffer.depth = 0;
+- zr->buffer.bytesperline = 0;
++ zr->vbuf_base = (void *) vidmem;
++ zr->vbuf_width = 0;
++ zr->vbuf_height = 0;
++ zr->vbuf_depth = 0;
++ zr->vbuf_bytesperline = 0;
+
+ /* Avoid nonsense settings from user for default input/norm */
+- if (default_norm < VIDEO_MODE_PAL &&
+- default_norm > VIDEO_MODE_SECAM)
+- default_norm = VIDEO_MODE_PAL;
+- zr->norm = default_norm;
+- if (!(zr->timing = zr->card.tvn[zr->norm])) {
++ if (default_norm < 0 && default_norm > 2)
++ default_norm = 0;
++ if (default_norm == 0) {
++ zr->norm = V4L2_STD_PAL;
++ zr->timing = zr->card.tvn[0];
++ } else if (default_norm == 1) {
++ zr->norm = V4L2_STD_NTSC;
++ zr->timing = zr->card.tvn[1];
++ } else {
++ zr->norm = V4L2_STD_SECAM;
++ zr->timing = zr->card.tvn[2];
++ }
++ if (zr->timing == NULL) {
+ dprintk(1,
+ KERN_WARNING
+- "%s: zr36057_init() - default TV standard not supported by hardware. PAL will be used.\n",
+- ZR_DEVNAME(zr));
+- zr->norm = VIDEO_MODE_PAL;
+- zr->timing = zr->card.tvn[zr->norm];
++ "%s: %s - default TV standard not supported by hardware. PAL will be used.\n",
++ ZR_DEVNAME(zr), __func__);
++ zr->norm = V4L2_STD_PAL;
++ zr->timing = zr->card.tvn[0];
+ }
+
+ if (default_input > zr->card.inputs-1) {
+@@ -1108,12 +1052,6 @@ zr36057_init (struct zoran *zr)
+ }
+ zr->input = default_input;
+
+- /* Should the following be reset at every open ? */
+- zr->hue = 32768;
+- zr->contrast = 32768;
+- zr->saturation = 32768;
+- zr->brightness = 32768;
+-
+ /* default setup (will be repeated at every open) */
+ zoran_open_init_params(zr);
+
+@@ -1124,8 +1062,8 @@ zr36057_init (struct zoran *zr)
+ if (!zr->stat_com || !zr->video_dev) {
+ dprintk(1,
+ KERN_ERR
+- "%s: zr36057_init() - kmalloc (STAT_COM) failed\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - kmalloc (STAT_COM) failed\n",
++ ZR_DEVNAME(zr), __func__);
+ err = -ENOMEM;
+ goto exit_free;
+ }
+@@ -1137,6 +1075,7 @@ zr36057_init (struct zoran *zr)
+ * Now add the template and register the device unit.
+ */
+ memcpy(zr->video_dev, &zoran_template, sizeof(zoran_template));
++ zr->video_dev->parent = &zr->pci_dev->dev;
+ strcpy(zr->video_dev->name, ZR_DEVNAME(zr));
+ err = video_register_device(zr->video_dev, VFL_TYPE_GRABBER, video_nr[zr->id]);
+ if (err < 0)
+@@ -1148,8 +1087,10 @@ zr36057_init (struct zoran *zr)
+ detect_guest_activity(zr);
+ test_interrupts(zr);
+ if (!pass_through) {
+- decoder_command(zr, DECODER_ENABLE_OUTPUT, &zero);
+- encoder_command(zr, ENCODER_SET_INPUT, &two);
++ struct v4l2_routing route = { 2, 0 };
++
++ decoder_call(zr, video, s_stream, 0);
++ encoder_call(zr, video, s_routing, &route);
+ }
+
+ zr->zoran_proc = NULL;
+@@ -1164,7 +1105,8 @@ exit_free:
+
+ static void __devexit zoran_remove(struct pci_dev *pdev)
+ {
+- struct zoran *zr = pci_get_drvdata(pdev);
++ struct v4l2_device *v4l2_dev = dev_get_drvdata(&pdev->dev);
++ struct zoran *zr = to_zoran(v4l2_dev);
+
+ if (!zr->initialized)
+ goto exit_free;
+@@ -1197,7 +1139,7 @@ static void __devexit zoran_remove(struct pci_dev *pdev)
+ pci_disable_device(zr->pci_dev);
+ video_unregister_device(zr->video_dev);
+ exit_free:
+- pci_set_drvdata(pdev, NULL);
++ v4l2_device_unregister(&zr->v4l2_dev);
+ kfree(zr);
+ }
+
+@@ -1215,10 +1157,8 @@ zoran_setup_videocodec (struct zoran *zr,
+
+ m = kmalloc(sizeof(struct videocodec_master), GFP_KERNEL);
+ if (!m) {
+- dprintk(1,
+- KERN_ERR
+- "%s: zoran_setup_videocodec() - no memory\n",
+- ZR_DEVNAME(zr));
++ dprintk(1, KERN_ERR "%s: %s - no memory\n",
++ ZR_DEVNAME(zr), __func__);
+ return m;
+ }
+
+@@ -1256,6 +1196,18 @@ zoran_setup_videocodec (struct zoran *zr,
+ return m;
+ }
+
++static void zoran_subdev_notify(struct v4l2_subdev *sd, unsigned int cmd, void *arg)
++{
++ struct zoran *zr = to_zoran(sd->v4l2_dev);
++
++ /* Bt819 needs to reset its FIFO buffer using #FRST pin and
++ LML33 card uses GPIO(7) for that. */
++ if (cmd == BT819_FIFO_RESET_LOW)
++ GPIO(zr, 7, 0);
++ else if (cmd == BT819_FIFO_RESET_HIGH)
++ GPIO(zr, 7, 1);
++}
++
+ /*
+ * Scan for a Buz card (actually for the PCI controller ZR36057),
+ * request the irq and map the io memory
+@@ -1269,34 +1221,33 @@ static int __devinit zoran_probe(struct pci_dev *pdev,
+ struct videocodec_master *master_vfe = NULL;
+ struct videocodec_master *master_codec = NULL;
+ int card_num;
+- char *i2c_enc_name, *i2c_dec_name, *codec_name, *vfe_name;
++ char *codec_name, *vfe_name;
+ unsigned int nr;
+
+
+ nr = zoran_num++;
+ if (nr >= BUZ_MAX) {
+- dprintk(1,
+- KERN_ERR
+- "%s: driver limited to %d card(s) maximum\n",
++ dprintk(1, KERN_ERR "%s: driver limited to %d card(s) maximum\n",
+ ZORAN_NAME, BUZ_MAX);
+ return -ENOENT;
+ }
+
+ zr = kzalloc(sizeof(struct zoran), GFP_KERNEL);
+ if (!zr) {
+- dprintk(1,
+- KERN_ERR
+- "%s: find_zr36057() - kzalloc failed\n",
+- ZORAN_NAME);
++ dprintk(1, KERN_ERR "%s: %s - kzalloc failed\n",
++ ZORAN_NAME, __func__);
+ return -ENOMEM;
+ }
++ zr->v4l2_dev.notify = zoran_subdev_notify;
++ if (v4l2_device_register(&pdev->dev, &zr->v4l2_dev))
++ goto zr_free_mem;
+ zr->pci_dev = pdev;
+ zr->id = nr;
+ snprintf(ZR_DEVNAME(zr), sizeof(ZR_DEVNAME(zr)), "MJPEG[%u]", zr->id);
+ spin_lock_init(&zr->spinlock);
+ mutex_init(&zr->resource_lock);
+ if (pci_enable_device(pdev))
+- goto zr_free_mem;
++ goto zr_unreg;
+ pci_read_config_byte(zr->pci_dev, PCI_CLASS_REVISION, &zr->revision);
+
+ dprintk(1,
+@@ -1323,7 +1274,7 @@ static int __devinit zoran_probe(struct pci_dev *pdev,
+ KERN_ERR
+ "%s: It is not possible to auto-detect ZR36057 based cards\n",
+ ZR_DEVNAME(zr));
+- goto zr_free_mem;
++ goto zr_unreg;
+ }
+
+ card_num = ent->driver_data;
+@@ -1332,7 +1283,7 @@ static int __devinit zoran_probe(struct pci_dev *pdev,
+ KERN_ERR
+ "%s: Unknown card, try specifying card=X module parameter\n",
+ ZR_DEVNAME(zr));
+- goto zr_free_mem;
++ goto zr_unreg;
+ }
+ dprintk(3,
+ KERN_DEBUG
+@@ -1345,7 +1296,7 @@ static int __devinit zoran_probe(struct pci_dev *pdev,
+ KERN_ERR
+ "%s: User specified card type %d out of range (0 .. %d)\n",
+ ZR_DEVNAME(zr), card_num, NUM_CARDS - 1);
+- goto zr_free_mem;
++ goto zr_unreg;
+ }
+ }
+
+@@ -1360,11 +1311,9 @@ static int __devinit zoran_probe(struct pci_dev *pdev,
+
+ zr->zr36057_mem = pci_ioremap_bar(zr->pci_dev, 0);
+ if (!zr->zr36057_mem) {
+- dprintk(1,
+- KERN_ERR
+- "%s: %s() - ioremap failed\n",
++ dprintk(1, KERN_ERR "%s: %s() - ioremap failed\n",
+ ZR_DEVNAME(zr), __func__);
+- goto zr_free_mem;
++ goto zr_unreg;
+ }
+
+ result = request_irq(zr->pci_dev->irq, zoran_irq,
+@@ -1373,18 +1322,18 @@ static int __devinit zoran_probe(struct pci_dev *pdev,
+ if (result == -EINVAL) {
+ dprintk(1,
+ KERN_ERR
+- "%s: find_zr36057() - bad irq number or handler\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - bad irq number or handler\n",
++ ZR_DEVNAME(zr), __func__);
+ } else if (result == -EBUSY) {
+ dprintk(1,
+ KERN_ERR
+- "%s: find_zr36057() - IRQ %d busy, change your PnP config in BIOS\n",
+- ZR_DEVNAME(zr), zr->pci_dev->irq);
++ "%s: %s - IRQ %d busy, change your PnP config in BIOS\n",
++ ZR_DEVNAME(zr), __func__, zr->pci_dev->irq);
+ } else {
+ dprintk(1,
+ KERN_ERR
+- "%s: find_zr36057() - can't assign irq, error code %d\n",
+- ZR_DEVNAME(zr), result);
++ "%s: %s - can't assign irq, error code %d\n",
++ ZR_DEVNAME(zr), __func__, result);
+ }
+ goto zr_unmap;
+ }
+@@ -1394,9 +1343,7 @@ static int __devinit zoran_probe(struct pci_dev *pdev,
+ &latency);
+ need_latency = zr->revision > 1 ? 32 : 48;
+ if (latency != need_latency) {
+- dprintk(2,
+- KERN_INFO
+- "%s: Changing PCI latency from %d to %d\n",
++ dprintk(2, KERN_INFO "%s: Changing PCI latency from %d to %d\n",
+ ZR_DEVNAME(zr), latency, need_latency);
+ pci_write_config_byte(zr->pci_dev, PCI_LATENCY_TIMER,
+ need_latency);
+@@ -1407,54 +1354,20 @@ static int __devinit zoran_probe(struct pci_dev *pdev,
+ dprintk(2, KERN_INFO "%s: Initializing i2c bus...\n",
+ ZR_DEVNAME(zr));
+
+- /* i2c decoder */
+- if (decoder[zr->id] != -1) {
+- i2c_dec_name = i2cid_to_modulename(decoder[zr->id]);
+- zr->card.i2c_decoder = decoder[zr->id];
+- } else if (zr->card.i2c_decoder != 0) {
+- i2c_dec_name = i2cid_to_modulename(zr->card.i2c_decoder);
+- } else {
+- i2c_dec_name = NULL;
+- }
+-
+- if (i2c_dec_name) {
+- result = request_module(i2c_dec_name);
+- if (result < 0) {
+- dprintk(1,
+- KERN_ERR
+- "%s: failed to load module %s: %d\n",
+- ZR_DEVNAME(zr), i2c_dec_name, result);
+- }
+- }
+-
+- /* i2c encoder */
+- if (encoder[zr->id] != -1) {
+- i2c_enc_name = i2cid_to_modulename(encoder[zr->id]);
+- zr->card.i2c_encoder = encoder[zr->id];
+- } else if (zr->card.i2c_encoder != 0) {
+- i2c_enc_name = i2cid_to_modulename(zr->card.i2c_encoder);
+- } else {
+- i2c_enc_name = NULL;
+- }
+-
+- if (i2c_enc_name) {
+- result = request_module(i2c_enc_name);
+- if (result < 0) {
+- dprintk(1,
+- KERN_ERR
+- "%s: failed to load module %s: %d\n",
+- ZR_DEVNAME(zr), i2c_enc_name, result);
+- }
+- }
+-
+ if (zoran_register_i2c(zr) < 0) {
+- dprintk(1,
+- KERN_ERR
+- "%s: find_zr36057() - can't initialize i2c bus\n",
+- ZR_DEVNAME(zr));
++ dprintk(1, KERN_ERR "%s: %s - can't initialize i2c bus\n",
++ ZR_DEVNAME(zr), __func__);
+ goto zr_free_irq;
+ }
+
++ zr->decoder = v4l2_i2c_new_probed_subdev(&zr->i2c_adapter,
++ zr->card.mod_decoder, zr->card.i2c_decoder, zr->card.addrs_decoder);
++
++ if (zr->card.mod_encoder)
++ zr->encoder = v4l2_i2c_new_probed_subdev(&zr->i2c_adapter,
++ zr->card.mod_encoder, zr->card.i2c_encoder,
++ zr->card.addrs_encoder);
++
+ dprintk(2,
+ KERN_INFO "%s: Initializing videocodec bus...\n",
+ ZR_DEVNAME(zr));
+@@ -1495,17 +1408,13 @@ static int __devinit zoran_probe(struct pci_dev *pdev,
+ goto zr_unreg_i2c;
+ zr->codec = videocodec_attach(master_codec);
+ if (!zr->codec) {
+- dprintk(1,
+- KERN_ERR
+- "%s: find_zr36057() - no codec found\n",
+- ZR_DEVNAME(zr));
++ dprintk(1, KERN_ERR "%s: %s - no codec found\n",
++ ZR_DEVNAME(zr), __func__);
+ goto zr_free_codec;
+ }
+ if (zr->codec->type != zr->card.video_codec) {
+- dprintk(1,
+- KERN_ERR
+- "%s: find_zr36057() - wrong codec\n",
+- ZR_DEVNAME(zr));
++ dprintk(1, KERN_ERR "%s: %s - wrong codec\n",
++ ZR_DEVNAME(zr), __func__);
+ goto zr_detach_codec;
+ }
+ }
+@@ -1515,17 +1424,13 @@ static int __devinit zoran_probe(struct pci_dev *pdev,
+ goto zr_detach_codec;
+ zr->vfe = videocodec_attach(master_vfe);
+ if (!zr->vfe) {
+- dprintk(1,
+- KERN_ERR
+- "%s: find_zr36057() - no VFE found\n",
+- ZR_DEVNAME(zr));
++ dprintk(1, KERN_ERR "%s: %s - no VFE found\n",
++ ZR_DEVNAME(zr), __func__);
+ goto zr_free_vfe;
+ }
+ if (zr->vfe->type != zr->card.video_vfe) {
+- dprintk(1,
+- KERN_ERR
+- "%s: find_zr36057() = wrong VFE\n",
+- ZR_DEVNAME(zr));
++ dprintk(1, KERN_ERR "%s: %s = wrong VFE\n",
++ ZR_DEVNAME(zr), __func__);
+ goto zr_detach_vfe;
+ }
+ }
+@@ -1533,8 +1438,7 @@ static int __devinit zoran_probe(struct pci_dev *pdev,
+ /* take care of Natoma chipset and a revision 1 zr36057 */
+ if ((pci_pci_problems & PCIPCI_NATOMA) && zr->revision <= 1) {
+ zr->jpg_buffers.need_contiguous = 1;
+- dprintk(1,
+- KERN_INFO
++ dprintk(1, KERN_INFO
+ "%s: ZR36057/Natoma bug, max. buffer size is 128K\n",
+ ZR_DEVNAME(zr));
+ }
+@@ -1544,8 +1448,6 @@ static int __devinit zoran_probe(struct pci_dev *pdev,
+
+ zoran_proc_init(zr);
+
+- pci_set_drvdata(pdev, zr);
+-
+ return 0;
+
+ zr_detach_vfe:
+@@ -1563,6 +1465,8 @@ zr_free_irq:
+ free_irq(zr->pci_dev->irq, zr);
+ zr_unmap:
+ iounmap(zr->zr36057_mem);
++zr_unreg:
++ v4l2_device_unregister(&zr->v4l2_dev);
+ zr_free_mem:
+ kfree(zr);
+
+@@ -1613,9 +1517,6 @@ static int __init zoran_init(void)
+ ZORAN_NAME, vidmem);
+ }
+
+- /* random nonsense */
+- dprintk(6, KERN_DEBUG "Jotti is een held!\n");
+-
+ /* some mainboards might not do PCI-PCI data transfer well */
+ if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL|PCIPCI_ALIMAGIK)) {
+ dprintk(1,
+diff --git a/drivers/media/video/zoran/zoran_card.h b/drivers/media/video/zoran/zoran_card.h
+index 4507bdc..4936fea 100644
+--- a/drivers/media/video/zoran/zoran_card.h
++++ b/drivers/media/video/zoran/zoran_card.h
+@@ -44,7 +44,8 @@ extern int zr36067_debug;
+ extern struct video_device zoran_template;
+
+ extern int zoran_check_jpg_settings(struct zoran *zr,
+- struct zoran_jpg_settings *settings);
++ struct zoran_jpg_settings *settings,
++ int try);
+ extern void zoran_open_init_params(struct zoran *zr);
+ extern void zoran_vdev_release(struct video_device *vdev);
+
+diff --git a/drivers/media/video/zoran/zoran_device.c b/drivers/media/video/zoran/zoran_device.c
+index 5d948ff..e0223de 100644
+--- a/drivers/media/video/zoran/zoran_device.c
++++ b/drivers/media/video/zoran/zoran_device.c
+@@ -36,13 +36,12 @@
+ #include <linux/proc_fs.h>
+ #include <linux/i2c.h>
+ #include <linux/i2c-algo-bit.h>
+-#include <linux/videodev.h>
++#include <linux/videodev2.h>
++#include <media/v4l2-common.h>
+ #include <linux/spinlock.h>
+ #include <linux/sem.h>
+
+ #include <linux/pci.h>
+-#include <linux/video_decoder.h>
+-#include <linux/video_encoder.h>
+ #include <linux/delay.h>
+ #include <linux/wait.h>
+
+@@ -312,9 +311,9 @@ zr36057_adjust_vfe (struct zoran *zr,
+ case BUZ_MODE_MOTION_COMPRESS:
+ case BUZ_MODE_IDLE:
+ default:
+- if (zr->norm == VIDEO_MODE_NTSC ||
++ if ((zr->norm & V4L2_STD_NTSC) ||
+ (zr->card.type == LML33R10 &&
+- zr->norm == VIDEO_MODE_PAL))
++ (zr->norm & V4L2_STD_PAL)))
+ btand(~ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
+ else
+ btor(ZR36057_VFESPFR_ExtFl, ZR36057_VFESPFR);
+@@ -355,14 +354,6 @@ zr36057_set_vfe (struct zoran *zr,
+ dprintk(2, KERN_INFO "%s: set_vfe() - width = %d, height = %d\n",
+ ZR_DEVNAME(zr), video_width, video_height);
+
+- if (zr->norm != VIDEO_MODE_PAL &&
+- zr->norm != VIDEO_MODE_NTSC &&
+- zr->norm != VIDEO_MODE_SECAM) {
+- dprintk(1,
+- KERN_ERR "%s: set_vfe() - norm = %d not valid\n",
+- ZR_DEVNAME(zr), zr->norm);
+- return;
+- }
+ if (video_width < BUZ_MIN_WIDTH ||
+ video_height < BUZ_MIN_HEIGHT ||
+ video_width > Wa || video_height > Ha) {
+@@ -426,7 +417,7 @@ zr36057_set_vfe (struct zoran *zr,
+ * we get the correct colors when uncompressing to the screen */
+ //reg |= ZR36057_VFESPFR_VCLKPol; /**/
+ /* RJ: Don't know if that is needed for NTSC also */
+- if (zr->norm != VIDEO_MODE_NTSC)
++ if (!(zr->norm & V4L2_STD_NTSC))
+ reg |= ZR36057_VFESPFR_ExtFl; // NEEDED!!!!!!! Wolfgang
+ reg |= ZR36057_VFESPFR_TopField;
+ if (HorDcm >= 48) {
+@@ -497,11 +488,11 @@ zr36057_overlay (struct zoran *zr,
+ * All error messages are internal driver checking only! */
+
+ /* video display top and bottom registers */
+- reg = (long) zr->buffer.base +
++ reg = (long) zr->vbuf_base +
+ zr->overlay_settings.x *
+ ((zr->overlay_settings.format->depth + 7) / 8) +
+ zr->overlay_settings.y *
+- zr->buffer.bytesperline;
++ zr->vbuf_bytesperline;
+ btwrite(reg, ZR36057_VDTR);
+ if (reg & 3)
+ dprintk(1,
+@@ -509,15 +500,15 @@ zr36057_overlay (struct zoran *zr,
+ "%s: zr36057_overlay() - video_address not aligned\n",
+ ZR_DEVNAME(zr));
+ if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2)
+- reg += zr->buffer.bytesperline;
++ reg += zr->vbuf_bytesperline;
+ btwrite(reg, ZR36057_VDBR);
+
+ /* video stride, status, and frame grab register */
+- reg = zr->buffer.bytesperline -
++ reg = zr->vbuf_bytesperline -
+ zr->overlay_settings.width *
+ ((zr->overlay_settings.format->depth + 7) / 8);
+ if (zr->overlay_settings.height > BUZ_MAX_HEIGHT / 2)
+- reg += zr->buffer.bytesperline;
++ reg += zr->vbuf_bytesperline;
+ if (reg & 3)
+ dprintk(1,
+ KERN_ERR
+@@ -544,12 +535,8 @@ zr36057_overlay (struct zoran *zr,
+ * and the maximum window size is BUZ_MAX_WIDTH * BUZ_MAX_HEIGHT pixels.
+ */
+
+-void
+-write_overlay_mask (struct file *file,
+- struct video_clip *vp,
+- int count)
++void write_overlay_mask(struct zoran_fh *fh, struct v4l2_clip *vp, int count)
+ {
+- struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ unsigned mask_line_size = (BUZ_MAX_WIDTH + 31) / 32;
+ u32 *mask;
+@@ -563,10 +550,10 @@ write_overlay_mask (struct file *file,
+
+ for (i = 0; i < count; ++i) {
+ /* pick up local copy of clip */
+- x = vp[i].x;
+- y = vp[i].y;
+- width = vp[i].width;
+- height = vp[i].height;
++ x = vp[i].c.left;
++ y = vp[i].c.top;
++ width = vp[i].c.width;
++ height = vp[i].c.height;
+
+ /* trim clips that extend beyond the window */
+ if (x < 0) {
+@@ -981,11 +968,10 @@ void
+ zr36057_enable_jpg (struct zoran *zr,
+ enum zoran_codec_mode mode)
+ {
+- static int zero;
+- static int one = 1;
+ struct vfe_settings cap;
+ int field_size =
+ zr->jpg_buffers.buffer_size / zr->jpg_settings.field_per_buff;
++ struct v4l2_routing route = { 0, 0 };
+
+ zr->codec_mode = mode;
+
+@@ -1007,8 +993,9 @@ zr36057_enable_jpg (struct zoran *zr,
+ * the video bus direction set to input.
+ */
+ set_videobus_dir(zr, 0);
+- decoder_command(zr, DECODER_ENABLE_OUTPUT, &one);
+- encoder_command(zr, ENCODER_SET_INPUT, &zero);
++ decoder_call(zr, video, s_stream, 1);
++ route.input = 0;
++ encoder_call(zr, video, s_routing, &route);
+
+ /* Take the JPEG codec and the VFE out of sleep */
+ jpeg_codec_sleep(zr, 0);
+@@ -1054,9 +1041,10 @@ zr36057_enable_jpg (struct zoran *zr,
+ /* In motion decompression mode, the decoder output must be disabled, and
+ * the video bus direction set to output.
+ */
+- decoder_command(zr, DECODER_ENABLE_OUTPUT, &zero);
++ decoder_call(zr, video, s_stream, 0);
+ set_videobus_dir(zr, 1);
+- encoder_command(zr, ENCODER_SET_INPUT, &one);
++ route.input = 1;
++ encoder_call(zr, video, s_routing, &route);
+
+ /* Take the JPEG codec and the VFE out of sleep */
+ jpeg_codec_sleep(zr, 0);
+@@ -1100,8 +1088,9 @@ zr36057_enable_jpg (struct zoran *zr,
+ jpeg_codec_sleep(zr, 1);
+ zr36057_adjust_vfe(zr, mode);
+
+- decoder_command(zr, DECODER_ENABLE_OUTPUT, &one);
+- encoder_command(zr, ENCODER_SET_INPUT, &zero);
++ decoder_call(zr, video, s_stream, 1);
++ route.input = 0;
++ encoder_call(zr, video, s_routing, &route);
+
+ dprintk(2, KERN_INFO "%s: enable_jpg(IDLE)\n", ZR_DEVNAME(zr));
+ break;
+@@ -1132,7 +1121,7 @@ zoran_feed_stat_com (struct zoran *zr)
+ if (!(zr->stat_com[i] & cpu_to_le32(1)))
+ break;
+ zr->stat_com[i] =
+- cpu_to_le32(zr->jpg_buffers.buffer[frame].frag_tab_bus);
++ cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus);
+ } else {
+ /* fill 2 stat_com entries */
+ i = ((zr->jpg_dma_head -
+@@ -1140,9 +1129,9 @@ zoran_feed_stat_com (struct zoran *zr)
+ if (!(zr->stat_com[i] & cpu_to_le32(1)))
+ break;
+ zr->stat_com[i] =
+- cpu_to_le32(zr->jpg_buffers.buffer[frame].frag_tab_bus);
++ cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus);
+ zr->stat_com[i + 1] =
+- cpu_to_le32(zr->jpg_buffers.buffer[frame].frag_tab_bus);
++ cpu_to_le32(zr->jpg_buffers.buffer[frame].jpg.frag_tab_bus);
+ }
+ zr->jpg_buffers.buffer[frame].state = BUZ_STATE_DMA;
+ zr->jpg_dma_head++;
+@@ -1162,7 +1151,7 @@ zoran_reap_stat_com (struct zoran *zr)
+ u32 stat_com;
+ unsigned int seq;
+ unsigned int dif;
+- struct zoran_jpg_buffer *buffer;
++ struct zoran_buffer *buffer;
+ int frame;
+
+ /* In motion decompress we don't have a hardware frame counter,
+@@ -1208,22 +1197,52 @@ zoran_reap_stat_com (struct zoran *zr)
+ }
+ }
+
++static void zoran_restart(struct zoran *zr)
++{
++ /* Now the stat_comm buffer is ready for restart */
++ int status = 0, mode;
++
++ if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
++ decoder_call(zr, video, g_input_status, &status);
++ mode = CODEC_DO_COMPRESSION;
++ } else {
++ status = V4L2_IN_ST_NO_SIGNAL;
++ mode = CODEC_DO_EXPANSION;
++ }
++ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
++ !(status & V4L2_IN_ST_NO_SIGNAL)) {
++ /********** RESTART code *************/
++ jpeg_codec_reset(zr);
++ zr->codec->set_mode(zr->codec, mode);
++ zr36057_set_jpg(zr, zr->codec_mode);
++ jpeg_start(zr);
++
++ if (zr->num_errors <= 8)
++ dprintk(2, KERN_INFO "%s: Restart\n",
++ ZR_DEVNAME(zr));
++
++ zr->JPEG_missed = 0;
++ zr->JPEG_error = 2;
++ /********** End RESTART code ***********/
++ }
++}
++
+ static void
+ error_handler (struct zoran *zr,
+ u32 astat,
+ u32 stat)
+ {
++ int i, j;
++
+ /* This is JPEG error handling part */
+- if ((zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) &&
+- (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS)) {
+- //dprintk(1, KERN_ERR "%s: Internal error: error handling request in mode %d\n", ZR_DEVNAME(zr), zr->codec_mode);
++ if (zr->codec_mode != BUZ_MODE_MOTION_COMPRESS &&
++ zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS) {
+ return;
+ }
+
+ if ((stat & 1) == 0 &&
+ zr->codec_mode == BUZ_MODE_MOTION_COMPRESS &&
+- zr->jpg_dma_tail - zr->jpg_que_tail >=
+- zr->jpg_buffers.num_buffers) {
++ zr->jpg_dma_tail - zr->jpg_que_tail >= zr->jpg_buffers.num_buffers) {
+ /* No free buffers... */
+ zoran_reap_stat_com(zr);
+ zoran_feed_stat_com(zr);
+@@ -1232,142 +1251,95 @@ error_handler (struct zoran *zr,
+ return;
+ }
+
+- if (zr->JPEG_error != 1) {
+- /*
+- * First entry: error just happened during normal operation
+- *
+- * In BUZ_MODE_MOTION_COMPRESS:
+- *
+- * Possible glitch in TV signal. In this case we should
+- * stop the codec and wait for good quality signal before
+- * restarting it to avoid further problems
+- *
+- * In BUZ_MODE_MOTION_DECOMPRESS:
+- *
+- * Bad JPEG frame: we have to mark it as processed (codec crashed
+- * and was not able to do it itself), and to remove it from queue.
+- */
+- btand(~ZR36057_JMC_Go_en, ZR36057_JMC);
+- udelay(1);
+- stat = stat | (post_office_read(zr, 7, 0) & 3) << 8;
+- btwrite(0, ZR36057_JPC);
+- btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR);
+- jpeg_codec_reset(zr);
+- jpeg_codec_sleep(zr, 1);
+- zr->JPEG_error = 1;
+- zr->num_errors++;
+-
+- /* Report error */
+- if (zr36067_debug > 1 && zr->num_errors <= 8) {
+- long frame;
+- frame =
+- zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME];
+- printk(KERN_ERR
+- "%s: JPEG error stat=0x%08x(0x%08x) queue_state=%ld/%ld/%ld/%ld seq=%ld frame=%ld. Codec stopped. ",
+- ZR_DEVNAME(zr), stat, zr->last_isr,
+- zr->jpg_que_tail, zr->jpg_dma_tail,
+- zr->jpg_dma_head, zr->jpg_que_head,
+- zr->jpg_seq_num, frame);
+- printk("stat_com frames:");
+- {
+- int i, j;
+- for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
+- for (i = 0;
+- i < zr->jpg_buffers.num_buffers;
+- i++) {
+- if (le32_to_cpu(zr->stat_com[j]) ==
+- zr->jpg_buffers.
+- buffer[i].
+- frag_tab_bus) {
+- printk("% d->%d",
+- j, i);
+- }
+- }
+- }
+- printk("\n");
+- }
+- }
+- /* Find an entry in stat_com and rotate contents */
+- {
+- int i;
+-
+- if (zr->jpg_settings.TmpDcm == 1)
+- i = (zr->jpg_dma_tail -
+- zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
+- else
+- i = ((zr->jpg_dma_tail -
+- zr->jpg_err_shift) & 1) * 2;
+- if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) {
+- /* Mimic zr36067 operation */
+- zr->stat_com[i] |= cpu_to_le32(1);
+- if (zr->jpg_settings.TmpDcm != 1)
+- zr->stat_com[i + 1] |= cpu_to_le32(1);
+- /* Refill */
+- zoran_reap_stat_com(zr);
+- zoran_feed_stat_com(zr);
+- wake_up_interruptible(&zr->jpg_capq);
+- /* Find an entry in stat_com again after refill */
+- if (zr->jpg_settings.TmpDcm == 1)
+- i = (zr->jpg_dma_tail -
+- zr->jpg_err_shift) &
+- BUZ_MASK_STAT_COM;
+- else
+- i = ((zr->jpg_dma_tail -
+- zr->jpg_err_shift) & 1) * 2;
+- }
+- if (i) {
+- /* Rotate stat_comm entries to make current entry first */
+- int j;
+- __le32 bus_addr[BUZ_NUM_STAT_COM];
+-
+- /* Here we are copying the stat_com array, which
+- * is already in little endian format, so
+- * no endian conversions here
+- */
+- memcpy(bus_addr, zr->stat_com,
+- sizeof(bus_addr));
+- for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
+- zr->stat_com[j] =
+- bus_addr[(i + j) &
+- BUZ_MASK_STAT_COM];
++ if (zr->JPEG_error == 1) {
++ zoran_restart(zr);
++ return;
++ }
+
+- }
+- zr->jpg_err_shift += i;
+- zr->jpg_err_shift &= BUZ_MASK_STAT_COM;
++ /*
++ * First entry: error just happened during normal operation
++ *
++ * In BUZ_MODE_MOTION_COMPRESS:
++ *
++ * Possible glitch in TV signal. In this case we should
++ * stop the codec and wait for good quality signal before
++ * restarting it to avoid further problems
++ *
++ * In BUZ_MODE_MOTION_DECOMPRESS:
++ *
++ * Bad JPEG frame: we have to mark it as processed (codec crashed
++ * and was not able to do it itself), and to remove it from queue.
++ */
++ btand(~ZR36057_JMC_Go_en, ZR36057_JMC);
++ udelay(1);
++ stat = stat | (post_office_read(zr, 7, 0) & 3) << 8;
++ btwrite(0, ZR36057_JPC);
++ btor(ZR36057_MCTCR_CFlush, ZR36057_MCTCR);
++ jpeg_codec_reset(zr);
++ jpeg_codec_sleep(zr, 1);
++ zr->JPEG_error = 1;
++ zr->num_errors++;
++
++ /* Report error */
++ if (zr36067_debug > 1 && zr->num_errors <= 8) {
++ long frame;
++
++ frame = zr->jpg_pend[zr->jpg_dma_tail & BUZ_MASK_FRAME];
++ printk(KERN_ERR
++ "%s: JPEG error stat=0x%08x(0x%08x) queue_state=%ld/%ld/%ld/%ld seq=%ld frame=%ld. Codec stopped. ",
++ ZR_DEVNAME(zr), stat, zr->last_isr,
++ zr->jpg_que_tail, zr->jpg_dma_tail,
++ zr->jpg_dma_head, zr->jpg_que_head,
++ zr->jpg_seq_num, frame);
++ printk(KERN_INFO "stat_com frames:");
++ for (j = 0; j < BUZ_NUM_STAT_COM; j++) {
++ for (i = 0; i < zr->jpg_buffers.num_buffers; i++) {
++ if (le32_to_cpu(zr->stat_com[j]) == zr->jpg_buffers.buffer[i].jpg.frag_tab_bus)
++ printk(KERN_CONT "% d->%d", j, i);
+ }
+- if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)
+- zr->jpg_err_seq = zr->jpg_seq_num; /* + 1; */
+ }
++ printk(KERN_CONT "\n");
+ }
++ /* Find an entry in stat_com and rotate contents */
++ if (zr->jpg_settings.TmpDcm == 1)
++ i = (zr->jpg_dma_tail - zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
++ else
++ i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2;
++ if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) {
++ /* Mimic zr36067 operation */
++ zr->stat_com[i] |= cpu_to_le32(1);
++ if (zr->jpg_settings.TmpDcm != 1)
++ zr->stat_com[i + 1] |= cpu_to_le32(1);
++ /* Refill */
++ zoran_reap_stat_com(zr);
++ zoran_feed_stat_com(zr);
++ wake_up_interruptible(&zr->jpg_capq);
++ /* Find an entry in stat_com again after refill */
++ if (zr->jpg_settings.TmpDcm == 1)
++ i = (zr->jpg_dma_tail - zr->jpg_err_shift) & BUZ_MASK_STAT_COM;
++ else
++ i = ((zr->jpg_dma_tail - zr->jpg_err_shift) & 1) * 2;
++ }
++ if (i) {
++ /* Rotate stat_comm entries to make current entry first */
++ int j;
++ __le32 bus_addr[BUZ_NUM_STAT_COM];
++
++ /* Here we are copying the stat_com array, which
++ * is already in little endian format, so
++ * no endian conversions here
++ */
++ memcpy(bus_addr, zr->stat_com, sizeof(bus_addr));
+
+- /* Now the stat_comm buffer is ready for restart */
+- do {
+- int status, mode;
+-
+- if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
+- decoder_command(zr, DECODER_GET_STATUS, &status);
+- mode = CODEC_DO_COMPRESSION;
+- } else {
+- status = 0;
+- mode = CODEC_DO_EXPANSION;
+- }
+- if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
+- (status & DECODER_STATUS_GOOD)) {
+- /********** RESTART code *************/
+- jpeg_codec_reset(zr);
+- zr->codec->set_mode(zr->codec, mode);
+- zr36057_set_jpg(zr, zr->codec_mode);
+- jpeg_start(zr);
+-
+- if (zr->num_errors <= 8)
+- dprintk(2, KERN_INFO "%s: Restart\n",
+- ZR_DEVNAME(zr));
++ for (j = 0; j < BUZ_NUM_STAT_COM; j++)
++ zr->stat_com[j] = bus_addr[(i + j) & BUZ_MASK_STAT_COM];
+
+- zr->JPEG_missed = 0;
+- zr->JPEG_error = 2;
+- /********** End RESTART code ***********/
+- }
+- } while (0);
++ zr->jpg_err_shift += i;
++ zr->jpg_err_shift &= BUZ_MASK_STAT_COM;
++ }
++ if (zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)
++ zr->jpg_err_seq = zr->jpg_seq_num; /* + 1; */
++ zoran_restart(zr);
+ }
+
+ irqreturn_t
+@@ -1425,10 +1397,8 @@ zoran_irq (int irq,
+ * We simply ignore them */
+
+ if (zr->v4l_memgrab_active) {
+-
+ /* A lot more checks should be here ... */
+- if ((btread(ZR36057_VSSFGR) &
+- ZR36057_VSSFGR_SnapShot) == 0)
++ if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_SnapShot) == 0)
+ dprintk(1,
+ KERN_WARNING
+ "%s: BuzIRQ with SnapShot off ???\n",
+@@ -1436,10 +1406,7 @@ zoran_irq (int irq,
+
+ if (zr->v4l_grab_frame != NO_GRAB_ACTIVE) {
+ /* There is a grab on a frame going on, check if it has finished */
+-
+- if ((btread(ZR36057_VSSFGR) &
+- ZR36057_VSSFGR_FrameGrab) ==
+- 0) {
++ if ((btread(ZR36057_VSSFGR) & ZR36057_VSSFGR_FrameGrab) == 0) {
+ /* it is finished, notify the user */
+
+ zr->v4l_buffers.buffer[zr->v4l_grab_frame].state = BUZ_STATE_DONE;
+@@ -1457,9 +1424,7 @@ zoran_irq (int irq,
+
+ if (zr->v4l_grab_frame == NO_GRAB_ACTIVE &&
+ zr->v4l_pend_tail != zr->v4l_pend_head) {
+-
+- int frame = zr->v4l_pend[zr->v4l_pend_tail &
+- V4L_MASK_FRAME];
++ int frame = zr->v4l_pend[zr->v4l_pend_tail & V4L_MASK_FRAME];
+ u32 reg;
+
+ zr->v4l_grab_frame = frame;
+@@ -1468,27 +1433,17 @@ zoran_irq (int irq,
+
+ /* Buffer address */
+
+- reg =
+- zr->v4l_buffers.buffer[frame].
+- fbuffer_bus;
++ reg = zr->v4l_buffers.buffer[frame].v4l.fbuffer_bus;
+ btwrite(reg, ZR36057_VDTR);
+- if (zr->v4l_settings.height >
+- BUZ_MAX_HEIGHT / 2)
+- reg +=
+- zr->v4l_settings.
+- bytesperline;
++ if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2)
++ reg += zr->v4l_settings.bytesperline;
+ btwrite(reg, ZR36057_VDBR);
+
+ /* video stride, status, and frame grab register */
+ reg = 0;
+- if (zr->v4l_settings.height >
+- BUZ_MAX_HEIGHT / 2)
+- reg +=
+- zr->v4l_settings.
+- bytesperline;
+- reg =
+- (reg <<
+- ZR36057_VSSFGR_DispStride);
++ if (zr->v4l_settings.height > BUZ_MAX_HEIGHT / 2)
++ reg += zr->v4l_settings.bytesperline;
++ reg = (reg << ZR36057_VSSFGR_DispStride);
+ reg |= ZR36057_VSSFGR_VidOvf;
+ reg |= ZR36057_VSSFGR_SnapShot;
+ reg |= ZR36057_VSSFGR_FrameGrab;
+@@ -1506,77 +1461,66 @@ zoran_irq (int irq,
+ #if (IRQ_MASK & ZR36057_ISR_CodRepIRQ)
+ if (astat & ZR36057_ISR_CodRepIRQ) {
+ zr->intr_counter_CodRepIRQ++;
+- IDEBUG(printk
+- (KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n",
++ IDEBUG(printk(KERN_DEBUG "%s: ZR36057_ISR_CodRepIRQ\n",
+ ZR_DEVNAME(zr)));
+ btand(~ZR36057_ICR_CodRepIRQ, ZR36057_ICR);
+ }
+ #endif /* (IRQ_MASK & ZR36057_ISR_CodRepIRQ) */
+
+ #if (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ)
+- if (astat & ZR36057_ISR_JPEGRepIRQ) {
+-
+- if (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
+- zr->codec_mode == BUZ_MODE_MOTION_COMPRESS) {
+- if (zr36067_debug > 1 &&
+- (!zr->frame_num || zr->JPEG_error)) {
+- printk(KERN_INFO
+- "%s: first frame ready: state=0x%08x odd_even=%d field_per_buff=%d delay=%d\n",
+- ZR_DEVNAME(zr), stat,
+- zr->jpg_settings.odd_even,
+- zr->jpg_settings.
+- field_per_buff,
+- zr->JPEG_missed);
+- {
+- char sc[] = "0000";
+- char sv[5];
+- int i;
+- strcpy(sv, sc);
+- for (i = 0; i < 4; i++) {
+- if (le32_to_cpu(zr->stat_com[i]) & 1)
+- sv[i] = '1';
+- }
+- sv[4] = 0;
+- printk(KERN_INFO
+- "%s: stat_com=%s queue_state=%ld/%ld/%ld/%ld\n",
+- ZR_DEVNAME(zr), sv,
+- zr->jpg_que_tail,
+- zr->jpg_dma_tail,
+- zr->jpg_dma_head,
+- zr->jpg_que_head);
+- }
+- } else {
+- if (zr->JPEG_missed > zr->JPEG_max_missed) // Get statistics
+- zr->JPEG_max_missed =
+- zr->JPEG_missed;
+- if (zr->JPEG_missed <
+- zr->JPEG_min_missed)
+- zr->JPEG_min_missed =
+- zr->JPEG_missed;
++ if ((astat & ZR36057_ISR_JPEGRepIRQ) &&
++ (zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS ||
++ zr->codec_mode == BUZ_MODE_MOTION_COMPRESS)) {
++ if (zr36067_debug > 1 && (!zr->frame_num || zr->JPEG_error)) {
++ char sc[] = "0000";
++ char sv[5];
++ int i;
++
++ printk(KERN_INFO
++ "%s: first frame ready: state=0x%08x odd_even=%d field_per_buff=%d delay=%d\n",
++ ZR_DEVNAME(zr), stat,
++ zr->jpg_settings.odd_even,
++ zr->jpg_settings.field_per_buff,
++ zr->JPEG_missed);
++
++ strcpy(sv, sc);
++ for (i = 0; i < 4; i++) {
++ if (le32_to_cpu(zr->stat_com[i]) & 1)
++ sv[i] = '1';
+ }
++ sv[4] = 0;
++ printk(KERN_INFO
++ "%s: stat_com=%s queue_state=%ld/%ld/%ld/%ld\n",
++ ZR_DEVNAME(zr), sv,
++ zr->jpg_que_tail,
++ zr->jpg_dma_tail,
++ zr->jpg_dma_head,
++ zr->jpg_que_head);
++ } else {
++ /* Get statistics */
++ if (zr->JPEG_missed > zr->JPEG_max_missed)
++ zr->JPEG_max_missed = zr->JPEG_missed;
++ if (zr->JPEG_missed < zr->JPEG_min_missed)
++ zr->JPEG_min_missed = zr->JPEG_missed;
++ }
+
+- if (zr36067_debug > 2 && zr->frame_num < 6) {
+- int i;
+- printk("%s: seq=%ld stat_com:",
+- ZR_DEVNAME(zr), zr->jpg_seq_num);
+- for (i = 0; i < 4; i++) {
+- printk(" %08x",
+- le32_to_cpu(zr->stat_com[i]));
+- }
+- printk("\n");
++ if (zr36067_debug > 2 && zr->frame_num < 6) {
++ int i;
++
++ printk(KERN_INFO "%s: seq=%ld stat_com:",
++ ZR_DEVNAME(zr), zr->jpg_seq_num);
++ for (i = 0; i < 4; i++) {
++ printk(KERN_CONT " %08x",
++ le32_to_cpu(zr->stat_com[i]));
+ }
+- zr->frame_num++;
+- zr->JPEG_missed = 0;
+- zr->JPEG_error = 0;
+- zoran_reap_stat_com(zr);
+- zoran_feed_stat_com(zr);
+- wake_up_interruptible(&zr->jpg_capq);
+- } /*else {
+- dprintk(1,
+- KERN_ERR
+- "%s: JPEG interrupt while not in motion (de)compress mode!\n",
+- ZR_DEVNAME(zr));
+- }*/
++ printk(KERN_CONT "\n");
++ }
++ zr->frame_num++;
++ zr->JPEG_missed = 0;
++ zr->JPEG_error = 0;
++ zoran_reap_stat_com(zr);
++ zoran_feed_stat_com(zr);
++ wake_up_interruptible(&zr->jpg_capq);
+ }
+ #endif /* (IRQ_MASK & ZR36057_ISR_JPEGRepIRQ) */
+
+@@ -1585,8 +1529,7 @@ zoran_irq (int irq,
+ zr->JPEG_missed > 25 ||
+ zr->JPEG_error == 1 ||
+ ((zr->codec_mode == BUZ_MODE_MOTION_DECOMPRESS) &&
+- (zr->frame_num & (zr->JPEG_missed >
+- zr->jpg_settings.field_per_buff)))) {
++ (zr->frame_num & (zr->JPEG_missed > zr->jpg_settings.field_per_buff)))) {
+ error_handler(zr, astat, stat);
+ }
+
+@@ -1628,7 +1571,7 @@ zoran_set_pci_master (struct zoran *zr,
+ void
+ zoran_init_hardware (struct zoran *zr)
+ {
+- int j, zero = 0;
++ struct v4l2_routing route = { 0, 0 };
+
+ /* Enable bus-mastering */
+ zoran_set_pci_master(zr, 1);
+@@ -1638,15 +1581,16 @@ zoran_init_hardware (struct zoran *zr)
+ zr->card.init(zr);
+ }
+
+- j = zr->card.input[zr->input].muxsel;
++ route.input = zr->card.input[zr->input].muxsel;
+
+- decoder_command(zr, 0, NULL);
+- decoder_command(zr, DECODER_SET_NORM, &zr->norm);
+- decoder_command(zr, DECODER_SET_INPUT, &j);
++ decoder_call(zr, core, init, 0);
++ decoder_call(zr, tuner, s_std, zr->norm);
++ decoder_call(zr, video, s_routing, &route);
+
+- encoder_command(zr, 0, NULL);
+- encoder_command(zr, ENCODER_SET_NORM, &zr->norm);
+- encoder_command(zr, ENCODER_SET_INPUT, &zero);
++ encoder_call(zr, core, init, 0);
++ encoder_call(zr, video, s_std_output, zr->norm);
++ route.input = 0;
++ encoder_call(zr, video, s_routing, &route);
+
+ /* toggle JPEG codec sleep to sync PLL */
+ jpeg_codec_sleep(zr, 1);
+@@ -1706,42 +1650,3 @@ zr36057_init_vfe (struct zoran *zr)
+ reg |= ZR36057_VDCR_Triton;
+ btwrite(reg, ZR36057_VDCR);
+ }
+-
+-/*
+- * Interface to decoder and encoder chips using i2c bus
+- */
+-
+-int
+-decoder_command (struct zoran *zr,
+- int cmd,
+- void *data)
+-{
+- if (zr->decoder == NULL)
+- return -EIO;
+-
+- if (zr->card.type == LML33 &&
+- (cmd == DECODER_SET_NORM || cmd == DECODER_SET_INPUT)) {
+- int res;
+-
+- // Bt819 needs to reset its FIFO buffer using #FRST pin and
+- // LML33 card uses GPIO(7) for that.
+- GPIO(zr, 7, 0);
+- res = zr->decoder->driver->command(zr->decoder, cmd, data);
+- // Pull #FRST high.
+- GPIO(zr, 7, 1);
+- return res;
+- } else
+- return zr->decoder->driver->command(zr->decoder, cmd,
+- data);
+-}
+-
+-int
+-encoder_command (struct zoran *zr,
+- int cmd,
+- void *data)
+-{
+- if (zr->encoder == NULL)
+- return -1;
+-
+- return zr->encoder->driver->command(zr->encoder, cmd, data);
+-}
+diff --git a/drivers/media/video/zoran/zoran_device.h b/drivers/media/video/zoran/zoran_device.h
+index 74c6c8e..07f2c23 100644
+--- a/drivers/media/video/zoran/zoran_device.h
++++ b/drivers/media/video/zoran/zoran_device.h
+@@ -54,8 +54,8 @@ extern int jpeg_codec_reset(struct zoran *zr);
+ /* zr360x7 access to raw capture */
+ extern void zr36057_overlay(struct zoran *zr,
+ int on);
+-extern void write_overlay_mask(struct file *file,
+- struct video_clip *vp,
++extern void write_overlay_mask(struct zoran_fh *fh,
++ struct v4l2_clip *vp,
+ int count);
+ extern void zr36057_set_memgrab(struct zoran *zr,
+ int mode);
+@@ -87,11 +87,9 @@ extern int jpg_bufsize;
+ extern int pass_through;
+
+ /* i2c */
+-extern int decoder_command(struct zoran *zr,
+- int cmd,
+- void *data);
+-extern int encoder_command(struct zoran *zr,
+- int cmd,
+- void *data);
++#define decoder_call(zr, o, f, args...) \
++ v4l2_subdev_call(zr->decoder, o, f, ##args)
++#define encoder_call(zr, o, f, args...) \
++ v4l2_subdev_call(zr->encoder, o, f, ##args)
+
+ #endif /* __ZORAN_DEVICE_H__ */
+diff --git a/drivers/media/video/zoran/zoran_driver.c b/drivers/media/video/zoran/zoran_driver.c
+index 120ef23..f16e57c 100644
+--- a/drivers/media/video/zoran/zoran_driver.c
++++ b/drivers/media/video/zoran/zoran_driver.c
+@@ -58,16 +58,6 @@
+ #include <linux/i2c-algo-bit.h>
+
+ #include <linux/spinlock.h>
+-#define MAP_NR(x) virt_to_page(x)
+-#define ZORAN_VID_TYPE ( \
+- VID_TYPE_CAPTURE | \
+- VID_TYPE_OVERLAY | \
+- VID_TYPE_CLIPPING | \
+- VID_TYPE_FRAMERAM | \
+- VID_TYPE_SCALES | \
+- VID_TYPE_MJPEG_DECODER | \
+- VID_TYPE_MJPEG_ENCODER \
+- )
+
+ #include <linux/videodev.h>
+ #include <media/v4l2-common.h>
+@@ -79,36 +69,17 @@
+ #include <asm/uaccess.h>
+ #include <linux/proc_fs.h>
+
+-#include <linux/video_decoder.h>
+-#include <linux/video_encoder.h>
+ #include <linux/mutex.h>
+ #include "zoran.h"
+ #include "zoran_device.h"
+ #include "zoran_card.h"
+
+- /* we declare some card type definitions here, they mean
+- * the same as the v4l1 ZORAN_VID_TYPE above, except it's v4l2 */
+-#define ZORAN_V4L2_VID_FLAGS ( \
+- V4L2_CAP_STREAMING |\
+- V4L2_CAP_VIDEO_CAPTURE |\
+- V4L2_CAP_VIDEO_OUTPUT |\
+- V4L2_CAP_VIDEO_OVERLAY \
+- )
+-
+-
+-#if defined(CONFIG_VIDEO_V4L1_COMPAT)
+-#define ZFMT(pal, fcc, cs) \
+- .palette = (pal), .fourcc = (fcc), .colorspace = (cs)
+-#else
+-#define ZFMT(pal, fcc, cs) \
+- .fourcc = (fcc), .colorspace = (cs)
+-#endif
+
+ const struct zoran_format zoran_formats[] = {
+ {
+ .name = "15-bit RGB LE",
+- ZFMT(VIDEO_PALETTE_RGB555,
+- V4L2_PIX_FMT_RGB555, V4L2_COLORSPACE_SRGB),
++ .fourcc = V4L2_PIX_FMT_RGB555,
++ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 15,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+@@ -116,16 +87,16 @@ const struct zoran_format zoran_formats[] = {
+ ZR36057_VFESPFR_LittleEndian,
+ }, {
+ .name = "15-bit RGB BE",
+- ZFMT(-1,
+- V4L2_PIX_FMT_RGB555X, V4L2_COLORSPACE_SRGB),
++ .fourcc = V4L2_PIX_FMT_RGB555X,
++ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 15,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB555|ZR36057_VFESPFR_ErrDif,
+ }, {
+ .name = "16-bit RGB LE",
+- ZFMT(VIDEO_PALETTE_RGB565,
+- V4L2_PIX_FMT_RGB565, V4L2_COLORSPACE_SRGB),
++ .fourcc = V4L2_PIX_FMT_RGB565,
++ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 16,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+@@ -133,56 +104,56 @@ const struct zoran_format zoran_formats[] = {
+ ZR36057_VFESPFR_LittleEndian,
+ }, {
+ .name = "16-bit RGB BE",
+- ZFMT(-1,
+- V4L2_PIX_FMT_RGB565X, V4L2_COLORSPACE_SRGB),
++ .fourcc = V4L2_PIX_FMT_RGB565X,
++ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 16,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB565|ZR36057_VFESPFR_ErrDif,
+ }, {
+ .name = "24-bit RGB",
+- ZFMT(VIDEO_PALETTE_RGB24,
+- V4L2_PIX_FMT_BGR24, V4L2_COLORSPACE_SRGB),
++ .fourcc = V4L2_PIX_FMT_BGR24,
++ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 24,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_Pack24,
+ }, {
+ .name = "32-bit RGB LE",
+- ZFMT(VIDEO_PALETTE_RGB32,
+- V4L2_PIX_FMT_BGR32, V4L2_COLORSPACE_SRGB),
++ .fourcc = V4L2_PIX_FMT_BGR32,
++ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 32,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB888|ZR36057_VFESPFR_LittleEndian,
+ }, {
+ .name = "32-bit RGB BE",
+- ZFMT(-1,
+- V4L2_PIX_FMT_RGB32, V4L2_COLORSPACE_SRGB),
++ .fourcc = V4L2_PIX_FMT_RGB32,
++ .colorspace = V4L2_COLORSPACE_SRGB,
+ .depth = 32,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_RGB888,
+ }, {
+ .name = "4:2:2, packed, YUYV",
+- ZFMT(VIDEO_PALETTE_YUV422,
+- V4L2_PIX_FMT_YUYV, V4L2_COLORSPACE_SMPTE170M),
++ .fourcc = V4L2_PIX_FMT_YUYV,
++ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .depth = 16,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_YUV422,
+ }, {
+ .name = "4:2:2, packed, UYVY",
+- ZFMT(VIDEO_PALETTE_UYVY,
+- V4L2_PIX_FMT_UYVY, V4L2_COLORSPACE_SMPTE170M),
++ .fourcc = V4L2_PIX_FMT_UYVY,
++ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .depth = 16,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_OVERLAY,
+ .vfespfr = ZR36057_VFESPFR_YUV422|ZR36057_VFESPFR_LittleEndian,
+ }, {
+ .name = "Hardware-encoded Motion-JPEG",
+- ZFMT(-1,
+- V4L2_PIX_FMT_MJPEG, V4L2_COLORSPACE_SMPTE170M),
++ .fourcc = V4L2_PIX_FMT_MJPEG,
++ .colorspace = V4L2_COLORSPACE_SMPTE170M,
+ .depth = 0,
+ .flags = ZORAN_FORMAT_CAPTURE |
+ ZORAN_FORMAT_PLAYBACK |
+@@ -191,13 +162,6 @@ const struct zoran_format zoran_formats[] = {
+ };
+ #define NUM_FORMATS ARRAY_SIZE(zoran_formats)
+
+-// RJ: Test only - want to test BUZ_USE_HIMEM even when CONFIG_BIGPHYS_AREA is defined
+-
+-
+-static int lock_norm; /* 0 = default 1 = Don't change TV standard (norm) */
+-module_param(lock_norm, int, 0644);
+-MODULE_PARM_DESC(lock_norm, "Prevent norm changes (1 = ignore, >1 = fail)");
+-
+ /* small helper function for calculating buffersizes for v4l2
+ * we calculate the nearest higher power-of-two, which
+ * will be the recommended buffersize */
+@@ -222,221 +186,106 @@ zoran_v4l2_calc_bufsize (struct zoran_jpg_settings *settings)
+ }
+
+ /* forward references */
+-static void v4l_fbuffer_free(struct file *file);
+-static void jpg_fbuffer_free(struct file *file);
++static void v4l_fbuffer_free(struct zoran_fh *fh);
++static void jpg_fbuffer_free(struct zoran_fh *fh);
++
++/* Set mapping mode */
++static void map_mode_raw(struct zoran_fh *fh)
++{
++ fh->map_mode = ZORAN_MAP_MODE_RAW;
++ fh->buffers.buffer_size = v4l_bufsize;
++ fh->buffers.num_buffers = v4l_nbufs;
++}
++static void map_mode_jpg(struct zoran_fh *fh, int play)
++{
++ fh->map_mode = play ? ZORAN_MAP_MODE_JPG_PLAY : ZORAN_MAP_MODE_JPG_REC;
++ fh->buffers.buffer_size = jpg_bufsize;
++ fh->buffers.num_buffers = jpg_nbufs;
++}
++static inline const char *mode_name(enum zoran_map_mode mode)
++{
++ return mode == ZORAN_MAP_MODE_RAW ? "V4L" : "JPG";
++}
+
+ /*
+ * Allocate the V4L grab buffers
+ *
+ * These have to be pysically contiguous.
+- * If v4l_bufsize <= MAX_KMALLOC_MEM we use kmalloc
+- * else we try to allocate them with bigphysarea_alloc_pages
+- * if the bigphysarea patch is present in the kernel,
+- * else we try to use high memory (if the user has bootet
+- * Linux with the necessary memory left over).
+ */
+
+-static unsigned long
+-get_high_mem (unsigned long size)
++static int v4l_fbuffer_alloc(struct zoran_fh *fh)
+ {
+-/*
+- * Check if there is usable memory at the end of Linux memory
+- * of at least size. Return the physical address of this memory,
+- * return 0 on failure.
+- *
+- * The idea is from Alexandro Rubini's book "Linux device drivers".
+- * The driver from him which is downloadable from O'Reilly's
+- * web site misses the "virt_to_phys(high_memory)" part
+- * (and therefore doesn't work at all - at least with 2.2.x kernels).
+- *
+- * It should be unnecessary to mention that THIS IS DANGEROUS,
+- * if more than one driver at a time has the idea to use this memory!!!!
+- */
+-
+- volatile unsigned char __iomem *mem;
+- unsigned char c;
+- unsigned long hi_mem_ph;
+- unsigned long i;
+-
+- /* Map the high memory to user space */
+-
+- hi_mem_ph = virt_to_phys(high_memory);
+-
+- mem = ioremap(hi_mem_ph, size);
+- if (!mem) {
+- dprintk(1,
+- KERN_ERR "%s: get_high_mem() - ioremap failed\n",
+- ZORAN_NAME);
+- return 0;
+- }
+-
+- for (i = 0; i < size; i++) {
+- /* Check if it is memory */
+- c = i & 0xff;
+- writeb(c, mem + i);
+- if (readb(mem + i) != c)
+- break;
+- c = 255 - c;
+- writeb(c, mem + i);
+- if (readb(mem + i) != c)
+- break;
+- writeb(0, mem + i); /* zero out memory */
+-
+- /* give the kernel air to breath */
+- if ((i & 0x3ffff) == 0x3ffff)
+- schedule();
+- }
+-
+- iounmap(mem);
+-
+- if (i != size) {
+- dprintk(1,
+- KERN_ERR
+- "%s: get_high_mem() - requested %lu, avail %lu\n",
+- ZORAN_NAME, size, i);
+- return 0;
+- }
+-
+- return hi_mem_ph;
+-}
+-
+-static int
+-v4l_fbuffer_alloc (struct file *file)
+-{
+- struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ int i, off;
+ unsigned char *mem;
+- unsigned long pmem = 0;
+
+- /* we might have old buffers lying around... */
+- if (fh->v4l_buffers.ready_to_be_freed) {
+- v4l_fbuffer_free(file);
+- }
+-
+- for (i = 0; i < fh->v4l_buffers.num_buffers; i++) {
+- if (fh->v4l_buffers.buffer[i].fbuffer)
++ for (i = 0; i < fh->buffers.num_buffers; i++) {
++ if (fh->buffers.buffer[i].v4l.fbuffer)
+ dprintk(2,
+ KERN_WARNING
+- "%s: v4l_fbuffer_alloc() - buffer %d allready allocated!?\n",
+- ZR_DEVNAME(zr), i);
++ "%s: %s - buffer %d already allocated!?\n",
++ ZR_DEVNAME(zr), __func__, i);
+
+ //udelay(20);
+- if (fh->v4l_buffers.buffer_size <= MAX_KMALLOC_MEM) {
+- /* Use kmalloc */
+-
+- mem = kmalloc(fh->v4l_buffers.buffer_size, GFP_KERNEL);
+- if (!mem) {
+- dprintk(1,
+- KERN_ERR
+- "%s: v4l_fbuffer_alloc() - kmalloc for V4L buf %d failed\n",
+- ZR_DEVNAME(zr), i);
+- v4l_fbuffer_free(file);
+- return -ENOBUFS;
+- }
+- fh->v4l_buffers.buffer[i].fbuffer = mem;
+- fh->v4l_buffers.buffer[i].fbuffer_phys =
+- virt_to_phys(mem);
+- fh->v4l_buffers.buffer[i].fbuffer_bus =
+- virt_to_bus(mem);
+- for (off = 0; off < fh->v4l_buffers.buffer_size;
+- off += PAGE_SIZE)
+- SetPageReserved(MAP_NR(mem + off));
+- dprintk(4,
+- KERN_INFO
+- "%s: v4l_fbuffer_alloc() - V4L frame %d mem 0x%lx (bus: 0x%lx)\n",
+- ZR_DEVNAME(zr), i, (unsigned long) mem,
+- virt_to_bus(mem));
+- } else {
+-
+- /* Use high memory which has been left at boot time */
+-
+- /* Ok., Ok. this is an evil hack - we make
+- * the assumption that physical addresses are
+- * the same as bus addresses (true at least
+- * for Intel processors). The whole method of
+- * obtaining and using this memory is not very
+- * nice - but I hope it saves some poor users
+- * from kernel hacking, which might have even
+- * more evil results */
+-
+- if (i == 0) {
+- int size =
+- fh->v4l_buffers.num_buffers *
+- fh->v4l_buffers.buffer_size;
+-
+- pmem = get_high_mem(size);
+- if (pmem == 0) {
+- dprintk(1,
+- KERN_ERR
+- "%s: v4l_fbuffer_alloc() - get_high_mem (size = %d KB) for V4L bufs failed\n",
+- ZR_DEVNAME(zr), size >> 10);
+- return -ENOBUFS;
+- }
+- fh->v4l_buffers.buffer[0].fbuffer = NULL;
+- fh->v4l_buffers.buffer[0].fbuffer_phys = pmem;
+- fh->v4l_buffers.buffer[0].fbuffer_bus = pmem;
+- dprintk(4,
+- KERN_INFO
+- "%s: v4l_fbuffer_alloc() - using %d KB high memory\n",
+- ZR_DEVNAME(zr), size >> 10);
+- } else {
+- fh->v4l_buffers.buffer[i].fbuffer = NULL;
+- fh->v4l_buffers.buffer[i].fbuffer_phys =
+- pmem + i * fh->v4l_buffers.buffer_size;
+- fh->v4l_buffers.buffer[i].fbuffer_bus =
+- pmem + i * fh->v4l_buffers.buffer_size;
+- }
++ mem = kmalloc(fh->buffers.buffer_size,
++ GFP_KERNEL | __GFP_NOWARN);
++ if (!mem) {
++ dprintk(1,
++ KERN_ERR
++ "%s: %s - kmalloc for V4L buf %d failed\n",
++ ZR_DEVNAME(zr), __func__, i);
++ v4l_fbuffer_free(fh);
++ return -ENOBUFS;
+ }
++ fh->buffers.buffer[i].v4l.fbuffer = mem;
++ fh->buffers.buffer[i].v4l.fbuffer_phys = virt_to_phys(mem);
++ fh->buffers.buffer[i].v4l.fbuffer_bus = virt_to_bus(mem);
++ for (off = 0; off < fh->buffers.buffer_size;
++ off += PAGE_SIZE)
++ SetPageReserved(virt_to_page(mem + off));
++ dprintk(4,
++ KERN_INFO
++ "%s: %s - V4L frame %d mem 0x%lx (bus: 0x%llx)\n",
++ ZR_DEVNAME(zr), __func__, i, (unsigned long) mem,
++ (unsigned long long)virt_to_bus(mem));
+ }
+
+- fh->v4l_buffers.allocated = 1;
++ fh->buffers.allocated = 1;
+
+ return 0;
+ }
+
+ /* free the V4L grab buffers */
+-static void
+-v4l_fbuffer_free (struct file *file)
++static void v4l_fbuffer_free(struct zoran_fh *fh)
+ {
+- struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ int i, off;
+ unsigned char *mem;
+
+- dprintk(4, KERN_INFO "%s: v4l_fbuffer_free()\n", ZR_DEVNAME(zr));
++ dprintk(4, KERN_INFO "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+- for (i = 0; i < fh->v4l_buffers.num_buffers; i++) {
+- if (!fh->v4l_buffers.buffer[i].fbuffer)
++ for (i = 0; i < fh->buffers.num_buffers; i++) {
++ if (!fh->buffers.buffer[i].v4l.fbuffer)
+ continue;
+
+- if (fh->v4l_buffers.buffer_size <= MAX_KMALLOC_MEM) {
+- mem = fh->v4l_buffers.buffer[i].fbuffer;
+- for (off = 0; off < fh->v4l_buffers.buffer_size;
+- off += PAGE_SIZE)
+- ClearPageReserved(MAP_NR(mem + off));
+- kfree((void *) fh->v4l_buffers.buffer[i].fbuffer);
+- }
+- fh->v4l_buffers.buffer[i].fbuffer = NULL;
++ mem = fh->buffers.buffer[i].v4l.fbuffer;
++ for (off = 0; off < fh->buffers.buffer_size;
++ off += PAGE_SIZE)
++ ClearPageReserved(virt_to_page(mem + off));
++ kfree(fh->buffers.buffer[i].v4l.fbuffer);
++ fh->buffers.buffer[i].v4l.fbuffer = NULL;
+ }
+
+- fh->v4l_buffers.allocated = 0;
+- fh->v4l_buffers.ready_to_be_freed = 0;
++ fh->buffers.allocated = 0;
+ }
+
+ /*
+ * Allocate the MJPEG grab buffers.
+ *
+- * If the requested buffer size is smaller than MAX_KMALLOC_MEM,
+- * kmalloc is used to request a physically contiguous area,
+- * else we allocate the memory in framgents with get_zeroed_page.
+- *
+ * If a Natoma chipset is present and this is a revision 1 zr36057,
+ * each MJPEG buffer needs to be physically contiguous.
+ * (RJ: This statement is from Dave Perks' original driver,
+ * I could never check it because I have a zr36067)
+- * The driver cares about this because it reduces the buffer
+- * size to MAX_KMALLOC_MEM in that case (which forces contiguous allocation).
+ *
+ * RJ: The contents grab buffers needs never be accessed in the driver.
+ * Therefore there is no need to allocate them with vmalloc in order
+@@ -458,162 +307,128 @@ v4l_fbuffer_free (struct file *file)
+ * and fragment buffers are not little-endian.
+ */
+
+-static int
+-jpg_fbuffer_alloc (struct file *file)
++static int jpg_fbuffer_alloc(struct zoran_fh *fh)
+ {
+- struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ int i, j, off;
+- unsigned long mem;
+-
+- /* we might have old buffers lying around */
+- if (fh->jpg_buffers.ready_to_be_freed) {
+- jpg_fbuffer_free(file);
+- }
++ u8 *mem;
+
+- for (i = 0; i < fh->jpg_buffers.num_buffers; i++) {
+- if (fh->jpg_buffers.buffer[i].frag_tab)
++ for (i = 0; i < fh->buffers.num_buffers; i++) {
++ if (fh->buffers.buffer[i].jpg.frag_tab)
+ dprintk(2,
+ KERN_WARNING
+- "%s: jpg_fbuffer_alloc() - buffer %d allready allocated!?\n",
+- ZR_DEVNAME(zr), i);
++ "%s: %s - buffer %d already allocated!?\n",
++ ZR_DEVNAME(zr), __func__, i);
+
+ /* Allocate fragment table for this buffer */
+
+- mem = get_zeroed_page(GFP_KERNEL);
++ mem = (void *)get_zeroed_page(GFP_KERNEL);
+ if (mem == 0) {
+ dprintk(1,
+ KERN_ERR
+- "%s: jpg_fbuffer_alloc() - get_zeroed_page (frag_tab) failed for buffer %d\n",
+- ZR_DEVNAME(zr), i);
+- jpg_fbuffer_free(file);
++ "%s: %s - get_zeroed_page (frag_tab) failed for buffer %d\n",
++ ZR_DEVNAME(zr), __func__, i);
++ jpg_fbuffer_free(fh);
+ return -ENOBUFS;
+ }
+- fh->jpg_buffers.buffer[i].frag_tab = (__le32 *) mem;
+- fh->jpg_buffers.buffer[i].frag_tab_bus =
+- virt_to_bus((void *) mem);
+-
+- //if (alloc_contig) {
+- if (fh->jpg_buffers.need_contiguous) {
+- mem =
+- (unsigned long) kmalloc(fh->jpg_buffers.
+- buffer_size,
+- GFP_KERNEL);
+- if (mem == 0) {
++ fh->buffers.buffer[i].jpg.frag_tab = (__le32 *)mem;
++ fh->buffers.buffer[i].jpg.frag_tab_bus = virt_to_bus(mem);
++
++ if (fh->buffers.need_contiguous) {
++ mem = kmalloc(fh->buffers.buffer_size, GFP_KERNEL);
++ if (mem == NULL) {
+ dprintk(1,
+ KERN_ERR
+- "%s: jpg_fbuffer_alloc() - kmalloc failed for buffer %d\n",
+- ZR_DEVNAME(zr), i);
+- jpg_fbuffer_free(file);
++ "%s: %s - kmalloc failed for buffer %d\n",
++ ZR_DEVNAME(zr), __func__, i);
++ jpg_fbuffer_free(fh);
+ return -ENOBUFS;
+ }
+- fh->jpg_buffers.buffer[i].frag_tab[0] =
+- cpu_to_le32(virt_to_bus((void *) mem));
+- fh->jpg_buffers.buffer[i].frag_tab[1] =
+- cpu_to_le32(((fh->jpg_buffers.buffer_size / 4) << 1) | 1);
+- for (off = 0; off < fh->jpg_buffers.buffer_size;
+- off += PAGE_SIZE)
+- SetPageReserved(MAP_NR(mem + off));
++ fh->buffers.buffer[i].jpg.frag_tab[0] =
++ cpu_to_le32(virt_to_bus(mem));
++ fh->buffers.buffer[i].jpg.frag_tab[1] =
++ cpu_to_le32((fh->buffers.buffer_size >> 1) | 1);
++ for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE)
++ SetPageReserved(virt_to_page(mem + off));
+ } else {
+- /* jpg_bufsize is allreay page aligned */
+- for (j = 0;
+- j < fh->jpg_buffers.buffer_size / PAGE_SIZE;
+- j++) {
+- mem = get_zeroed_page(GFP_KERNEL);
+- if (mem == 0) {
++ /* jpg_bufsize is already page aligned */
++ for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) {
++ mem = (void *)get_zeroed_page(GFP_KERNEL);
++ if (mem == NULL) {
+ dprintk(1,
+ KERN_ERR
+- "%s: jpg_fbuffer_alloc() - get_zeroed_page failed for buffer %d\n",
+- ZR_DEVNAME(zr), i);
+- jpg_fbuffer_free(file);
++ "%s: %s - get_zeroed_page failed for buffer %d\n",
++ ZR_DEVNAME(zr), __func__, i);
++ jpg_fbuffer_free(fh);
+ return -ENOBUFS;
+ }
+
+- fh->jpg_buffers.buffer[i].frag_tab[2 * j] =
+- cpu_to_le32(virt_to_bus((void *) mem));
+- fh->jpg_buffers.buffer[i].frag_tab[2 * j +
+- 1] =
+- cpu_to_le32((PAGE_SIZE / 4) << 1);
+- SetPageReserved(MAP_NR(mem));
++ fh->buffers.buffer[i].jpg.frag_tab[2 * j] =
++ cpu_to_le32(virt_to_bus(mem));
++ fh->buffers.buffer[i].jpg.frag_tab[2 * j + 1] =
++ cpu_to_le32((PAGE_SIZE >> 2) << 1);
++ SetPageReserved(virt_to_page(mem));
+ }
+
+- fh->jpg_buffers.buffer[i].frag_tab[2 * j - 1] |= cpu_to_le32(1);
++ fh->buffers.buffer[i].jpg.frag_tab[2 * j - 1] |= cpu_to_le32(1);
+ }
+ }
+
+ dprintk(4,
+- KERN_DEBUG "%s: jpg_fbuffer_alloc() - %d KB allocated\n",
+- ZR_DEVNAME(zr),
+- (fh->jpg_buffers.num_buffers *
+- fh->jpg_buffers.buffer_size) >> 10);
++ KERN_DEBUG "%s: %s - %d KB allocated\n",
++ ZR_DEVNAME(zr), __func__,
++ (fh->buffers.num_buffers * fh->buffers.buffer_size) >> 10);
+
+- fh->jpg_buffers.allocated = 1;
++ fh->buffers.allocated = 1;
+
+ return 0;
+ }
+
+ /* free the MJPEG grab buffers */
+-static void
+-jpg_fbuffer_free (struct file *file)
++static void jpg_fbuffer_free(struct zoran_fh *fh)
+ {
+- struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ int i, j, off;
+ unsigned char *mem;
++ __le32 frag_tab;
++ struct zoran_buffer *buffer;
+
+- dprintk(4, KERN_DEBUG "%s: jpg_fbuffer_free()\n", ZR_DEVNAME(zr));
++ dprintk(4, KERN_DEBUG "%s: %s\n", ZR_DEVNAME(zr), __func__);
+
+- for (i = 0; i < fh->jpg_buffers.num_buffers; i++) {
+- if (!fh->jpg_buffers.buffer[i].frag_tab)
++ for (i = 0, buffer = &fh->buffers.buffer[0];
++ i < fh->buffers.num_buffers; i++, buffer++) {
++ if (!buffer->jpg.frag_tab)
+ continue;
+
+- //if (alloc_contig) {
+- if (fh->jpg_buffers.need_contiguous) {
+- if (fh->jpg_buffers.buffer[i].frag_tab[0]) {
+- mem = (unsigned char *) bus_to_virt(le32_to_cpu(
+- fh->jpg_buffers.buffer[i].frag_tab[0]));
+- for (off = 0;
+- off < fh->jpg_buffers.buffer_size;
+- off += PAGE_SIZE)
+- ClearPageReserved(MAP_NR
+- (mem + off));
++ if (fh->buffers.need_contiguous) {
++ frag_tab = buffer->jpg.frag_tab[0];
++
++ if (frag_tab) {
++ mem = bus_to_virt(le32_to_cpu(frag_tab));
++ for (off = 0; off < fh->buffers.buffer_size; off += PAGE_SIZE)
++ ClearPageReserved(virt_to_page(mem + off));
+ kfree(mem);
+- fh->jpg_buffers.buffer[i].frag_tab[0] = 0;
+- fh->jpg_buffers.buffer[i].frag_tab[1] = 0;
++ buffer->jpg.frag_tab[0] = 0;
++ buffer->jpg.frag_tab[1] = 0;
+ }
+ } else {
+- for (j = 0;
+- j < fh->jpg_buffers.buffer_size / PAGE_SIZE;
+- j++) {
+- if (!fh->jpg_buffers.buffer[i].
+- frag_tab[2 * j])
++ for (j = 0; j < fh->buffers.buffer_size / PAGE_SIZE; j++) {
++ frag_tab = buffer->jpg.frag_tab[2 * j];
++
++ if (!frag_tab)
+ break;
+- ClearPageReserved(MAP_NR
+- (bus_to_virt
+- (le32_to_cpu
+- (fh->jpg_buffers.
+- buffer[i].frag_tab[2 *
+- j]))));
+- free_page((unsigned long)
+- bus_to_virt
+- (le32_to_cpu
+- (fh->jpg_buffers.
+- buffer[i].
+- frag_tab[2 * j])));
+- fh->jpg_buffers.buffer[i].frag_tab[2 * j] =
+- 0;
+- fh->jpg_buffers.buffer[i].frag_tab[2 * j +
+- 1] = 0;
++ ClearPageReserved(virt_to_page(bus_to_virt(le32_to_cpu(frag_tab))));
++ free_page((unsigned long)bus_to_virt(le32_to_cpu(frag_tab)));
++ buffer->jpg.frag_tab[2 * j] = 0;
++ buffer->jpg.frag_tab[2 * j + 1] = 0;
+ }
+ }
+
+- free_page((unsigned long) fh->jpg_buffers.buffer[i].
+- frag_tab);
+- fh->jpg_buffers.buffer[i].frag_tab = NULL;
++ free_page((unsigned long)buffer->jpg.frag_tab);
++ buffer->jpg.frag_tab = NULL;
+ }
+
+- fh->jpg_buffers.allocated = 0;
+- fh->jpg_buffers.ready_to_be_freed = 0;
++ fh->buffers.allocated = 0;
+ }
+
+ /*
+@@ -621,12 +436,11 @@ jpg_fbuffer_free (struct file *file)
+ */
+
+ static int
+-zoran_v4l_set_format (struct file *file,
++zoran_v4l_set_format (struct zoran_fh *fh,
+ int width,
+ int height,
+ const struct zoran_format *format)
+ {
+- struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ int bpp;
+
+@@ -636,19 +450,19 @@ zoran_v4l_set_format (struct file *file,
+ height > BUZ_MAX_HEIGHT || width > BUZ_MAX_WIDTH) {
+ dprintk(1,
+ KERN_ERR
+- "%s: v4l_set_format() - wrong frame size (%dx%d)\n",
+- ZR_DEVNAME(zr), width, height);
++ "%s: %s - wrong frame size (%dx%d)\n",
++ ZR_DEVNAME(zr), __func__, width, height);
+ return -EINVAL;
+ }
+
+ bpp = (format->depth + 7) / 8;
+
+ /* Check against available buffer size */
+- if (height * width * bpp > fh->v4l_buffers.buffer_size) {
++ if (height * width * bpp > fh->buffers.buffer_size) {
+ dprintk(1,
+ KERN_ERR
+- "%s: v4l_set_format() - video buffer size (%d kB) is too small\n",
+- ZR_DEVNAME(zr), fh->v4l_buffers.buffer_size >> 10);
++ "%s: %s - video buffer size (%d kB) is too small\n",
++ ZR_DEVNAME(zr), __func__, fh->buffers.buffer_size >> 10);
+ return -EINVAL;
+ }
+
+@@ -657,8 +471,8 @@ zoran_v4l_set_format (struct file *file,
+ if ((bpp == 2 && (width & 1)) || (bpp == 3 && (width & 3))) {
+ dprintk(1,
+ KERN_ERR
+- "%s: v4l_set_format() - wrong frame alingment\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - wrong frame alignment\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+@@ -670,43 +484,40 @@ zoran_v4l_set_format (struct file *file,
+ return 0;
+ }
+
+-static int
+-zoran_v4l_queue_frame (struct file *file,
+- int num)
++static int zoran_v4l_queue_frame(struct zoran_fh *fh, int num)
+ {
+- struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ unsigned long flags;
+ int res = 0;
+
+- if (!fh->v4l_buffers.allocated) {
++ if (!fh->buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+- "%s: v4l_queue_frame() - buffers not yet allocated\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - buffers not yet allocated\n",
++ ZR_DEVNAME(zr), __func__);
+ res = -ENOMEM;
+ }
+
+ /* No grabbing outside the buffer range! */
+- if (num >= fh->v4l_buffers.num_buffers || num < 0) {
++ if (num >= fh->buffers.num_buffers || num < 0) {
+ dprintk(1,
+ KERN_ERR
+- "%s: v4l_queue_frame() - buffer %d is out of range\n",
+- ZR_DEVNAME(zr), num);
++ "%s: %s - buffer %d is out of range\n",
++ ZR_DEVNAME(zr), __func__, num);
+ res = -EINVAL;
+ }
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+
+- if (fh->v4l_buffers.active == ZORAN_FREE) {
++ if (fh->buffers.active == ZORAN_FREE) {
+ if (zr->v4l_buffers.active == ZORAN_FREE) {
+- zr->v4l_buffers = fh->v4l_buffers;
+- fh->v4l_buffers.active = ZORAN_ACTIVE;
++ zr->v4l_buffers = fh->buffers;
++ fh->buffers.active = ZORAN_ACTIVE;
+ } else {
+ dprintk(1,
+ KERN_ERR
+- "%s: v4l_queue_frame() - another session is already capturing\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - another session is already capturing\n",
++ ZR_DEVNAME(zr), __func__);
+ res = -EBUSY;
+ }
+ }
+@@ -717,7 +528,7 @@ zoran_v4l_queue_frame (struct file *file,
+ default:
+ case BUZ_STATE_PEND:
+ if (zr->v4l_buffers.active == ZORAN_FREE) {
+- fh->v4l_buffers.active = ZORAN_FREE;
++ fh->buffers.active = ZORAN_FREE;
+ zr->v4l_buffers.allocated = 0;
+ }
+ res = -EBUSY; /* what are you doing? */
+@@ -725,19 +536,17 @@ zoran_v4l_queue_frame (struct file *file,
+ case BUZ_STATE_DONE:
+ dprintk(2,
+ KERN_WARNING
+- "%s: v4l_queue_frame() - queueing buffer %d in state DONE!?\n",
+- ZR_DEVNAME(zr), num);
++ "%s: %s - queueing buffer %d in state DONE!?\n",
++ ZR_DEVNAME(zr), __func__, num);
+ case BUZ_STATE_USER:
+ /* since there is at least one unused buffer there's room for at least
+ * one more pend[] entry */
+- zr->v4l_pend[zr->v4l_pend_head++ &
+- V4L_MASK_FRAME] = num;
++ zr->v4l_pend[zr->v4l_pend_head++ & V4L_MASK_FRAME] = num;
+ zr->v4l_buffers.buffer[num].state = BUZ_STATE_PEND;
+ zr->v4l_buffers.buffer[num].bs.length =
+ fh->v4l_settings.bytesperline *
+ zr->v4l_settings.height;
+- fh->v4l_buffers.buffer[num] =
+- zr->v4l_buffers.buffer[num];
++ fh->buffers.buffer[num] = zr->v4l_buffers.buffer[num];
+ break;
+ }
+ }
+@@ -745,65 +554,7 @@ zoran_v4l_queue_frame (struct file *file,
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ if (!res && zr->v4l_buffers.active == ZORAN_FREE)
+- zr->v4l_buffers.active = fh->v4l_buffers.active;
+-
+- return res;
+-}
+-
+-static int
+-v4l_grab (struct file *file,
+- struct video_mmap *mp)
+-{
+- struct zoran_fh *fh = file->private_data;
+- struct zoran *zr = fh->zr;
+- int res = 0, i;
+-
+- for (i = 0; i < NUM_FORMATS; i++) {
+- if (zoran_formats[i].palette == mp->format &&
+- zoran_formats[i].flags & ZORAN_FORMAT_CAPTURE &&
+- !(zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED))
+- break;
+- }
+- if (i == NUM_FORMATS || zoran_formats[i].depth == 0) {
+- dprintk(1,
+- KERN_ERR
+- "%s: v4l_grab() - wrong bytes-per-pixel format\n",
+- ZR_DEVNAME(zr));
+- return -EINVAL;
+- }
+-
+- /*
+- * To minimize the time spent in the IRQ routine, we avoid setting up
+- * the video front end there.
+- * If this grab has different parameters from a running streaming capture
+- * we stop the streaming capture and start it over again.
+- */
+- if (zr->v4l_memgrab_active &&
+- (zr->v4l_settings.width != mp->width ||
+- zr->v4l_settings.height != mp->height ||
+- zr->v4l_settings.format->palette != mp->format)) {
+- res = wait_grab_pending(zr);
+- if (res)
+- return res;
+- }
+- if ((res = zoran_v4l_set_format(file,
+- mp->width,
+- mp->height,
+- &zoran_formats[i])))
+- return res;
+- zr->v4l_settings = fh->v4l_settings;
+-
+- /* queue the frame in the pending queue */
+- if ((res = zoran_v4l_queue_frame(file, mp->frame))) {
+- fh->v4l_buffers.active = ZORAN_FREE;
+- return res;
+- }
+-
+- /* put the 36057 into frame grabbing mode */
+- if (!res && !zr->v4l_memgrab_active)
+- zr36057_set_memgrab(zr, 1);
+-
+- //dprintk(4, KERN_INFO "%s: Frame grab 3...\n", ZR_DEVNAME(zr));
++ zr->v4l_buffers.active = fh->buffers.active;
+
+ return res;
+ }
+@@ -812,27 +563,24 @@ v4l_grab (struct file *file,
+ * Sync on a V4L buffer
+ */
+
+-static int
+-v4l_sync (struct file *file,
+- int frame)
++static int v4l_sync(struct zoran_fh *fh, int frame)
+ {
+- struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ unsigned long flags;
+
+- if (fh->v4l_buffers.active == ZORAN_FREE) {
++ if (fh->buffers.active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+- "%s: v4l_sync() - no grab active for this session\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - no grab active for this session\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ /* check passed-in frame number */
+- if (frame >= fh->v4l_buffers.num_buffers || frame < 0) {
++ if (frame >= fh->buffers.num_buffers || frame < 0) {
+ dprintk(1,
+- KERN_ERR "%s: v4l_sync() - frame %d is invalid\n",
+- ZR_DEVNAME(zr), frame);
++ KERN_ERR "%s: %s - frame %d is invalid\n",
++ ZR_DEVNAME(zr), __func__, frame);
+ return -EINVAL;
+ }
+
+@@ -840,15 +588,14 @@ v4l_sync (struct file *file,
+ if (zr->v4l_buffers.buffer[frame].state == BUZ_STATE_USER) {
+ dprintk(1,
+ KERN_ERR
+- "%s: v4l_sync() - attempt to sync on a buffer which was not queued?\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - attempt to sync on a buffer which was not queued?\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EPROTO;
+ }
+
+ /* wait on this buffer to get ready */
+ if (!wait_event_interruptible_timeout(zr->v4l_capq,
+- (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND),
+- 10*HZ))
++ (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_PEND), 10*HZ))
+ return -ETIME;
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+@@ -856,11 +603,11 @@ v4l_sync (struct file *file,
+ /* buffer should now be in BUZ_STATE_DONE */
+ if (zr->v4l_buffers.buffer[frame].state != BUZ_STATE_DONE)
+ dprintk(2,
+- KERN_ERR "%s: v4l_sync() - internal state error\n",
+- ZR_DEVNAME(zr));
++ KERN_ERR "%s: %s - internal state error\n",
++ ZR_DEVNAME(zr), __func__);
+
+ zr->v4l_buffers.buffer[frame].state = BUZ_STATE_USER;
+- fh->v4l_buffers.buffer[frame] = zr->v4l_buffers.buffer[frame];
++ fh->buffers.buffer[frame] = zr->v4l_buffers.buffer[frame];
+
+ spin_lock_irqsave(&zr->spinlock, flags);
+
+@@ -868,8 +615,7 @@ v4l_sync (struct file *file,
+ if (zr->v4l_pend_tail == zr->v4l_pend_head) {
+ zr36057_set_memgrab(zr, 0);
+ if (zr->v4l_buffers.active == ZORAN_ACTIVE) {
+- fh->v4l_buffers.active = zr->v4l_buffers.active =
+- ZORAN_FREE;
++ fh->buffers.active = zr->v4l_buffers.active = ZORAN_FREE;
+ zr->v4l_buffers.allocated = 0;
+ }
+ }
+@@ -883,31 +629,28 @@ v4l_sync (struct file *file,
+ * Queue a MJPEG buffer for capture/playback
+ */
+
+-static int
+-zoran_jpg_queue_frame (struct file *file,
+- int num,
+- enum zoran_codec_mode mode)
++static int zoran_jpg_queue_frame(struct zoran_fh *fh, int num,
++ enum zoran_codec_mode mode)
+ {
+- struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ unsigned long flags;
+ int res = 0;
+
+ /* Check if buffers are allocated */
+- if (!fh->jpg_buffers.allocated) {
++ if (!fh->buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+- "%s: jpg_queue_frame() - buffers not yet allocated\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - buffers not yet allocated\n",
++ ZR_DEVNAME(zr), __func__);
+ return -ENOMEM;
+ }
+
+ /* No grabbing outside the buffer range! */
+- if (num >= fh->jpg_buffers.num_buffers || num < 0) {
++ if (num >= fh->buffers.num_buffers || num < 0) {
+ dprintk(1,
+ KERN_ERR
+- "%s: jpg_queue_frame() - buffer %d out of range\n",
+- ZR_DEVNAME(zr), num);
++ "%s: %s - buffer %d out of range\n",
++ ZR_DEVNAME(zr), __func__, num);
+ return -EINVAL;
+ }
+
+@@ -918,20 +661,20 @@ zoran_jpg_queue_frame (struct file *file,
+ /* wrong codec mode active - invalid */
+ dprintk(1,
+ KERN_ERR
+- "%s: jpg_queue_frame() - codec in wrong mode\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - codec in wrong mode\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+- if (fh->jpg_buffers.active == ZORAN_FREE) {
++ if (fh->buffers.active == ZORAN_FREE) {
+ if (zr->jpg_buffers.active == ZORAN_FREE) {
+- zr->jpg_buffers = fh->jpg_buffers;
+- fh->jpg_buffers.active = ZORAN_ACTIVE;
++ zr->jpg_buffers = fh->buffers;
++ fh->buffers.active = ZORAN_ACTIVE;
+ } else {
+ dprintk(1,
+ KERN_ERR
+- "%s: jpg_queue_frame() - another session is already capturing\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - another session is already capturing\n",
++ ZR_DEVNAME(zr), __func__);
+ res = -EBUSY;
+ }
+ }
+@@ -948,23 +691,21 @@ zoran_jpg_queue_frame (struct file *file,
+ case BUZ_STATE_DONE:
+ dprintk(2,
+ KERN_WARNING
+- "%s: jpg_queue_frame() - queing frame in BUZ_STATE_DONE state!?\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - queing frame in BUZ_STATE_DONE state!?\n",
++ ZR_DEVNAME(zr), __func__);
+ case BUZ_STATE_USER:
+ /* since there is at least one unused buffer there's room for at
+ *least one more pend[] entry */
+- zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] =
+- num;
++ zr->jpg_pend[zr->jpg_que_head++ & BUZ_MASK_FRAME] = num;
+ zr->jpg_buffers.buffer[num].state = BUZ_STATE_PEND;
+- fh->jpg_buffers.buffer[num] =
+- zr->jpg_buffers.buffer[num];
++ fh->buffers.buffer[num] = zr->jpg_buffers.buffer[num];
+ zoran_feed_stat_com(zr);
+ break;
+ default:
+ case BUZ_STATE_DMA:
+ case BUZ_STATE_PEND:
+ if (zr->jpg_buffers.active == ZORAN_FREE) {
+- fh->jpg_buffers.active = ZORAN_FREE;
++ fh->buffers.active = ZORAN_FREE;
+ zr->jpg_buffers.allocated = 0;
+ }
+ res = -EBUSY; /* what are you doing? */
+@@ -974,47 +715,41 @@ zoran_jpg_queue_frame (struct file *file,
+
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+- if (!res && zr->jpg_buffers.active == ZORAN_FREE) {
+- zr->jpg_buffers.active = fh->jpg_buffers.active;
+- }
++ if (!res && zr->jpg_buffers.active == ZORAN_FREE)
++ zr->jpg_buffers.active = fh->buffers.active;
+
+ return res;
+ }
+
+-static int
+-jpg_qbuf (struct file *file,
+- int frame,
+- enum zoran_codec_mode mode)
++static int jpg_qbuf(struct zoran_fh *fh, int frame, enum zoran_codec_mode mode)
+ {
+- struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ int res = 0;
+
+ /* Does the user want to stop streaming? */
+ if (frame < 0) {
+ if (zr->codec_mode == mode) {
+- if (fh->jpg_buffers.active == ZORAN_FREE) {
++ if (fh->buffers.active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+- "%s: jpg_qbuf(-1) - session not active\n",
+- ZR_DEVNAME(zr));
++ "%s: %s(-1) - session not active\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+- fh->jpg_buffers.active = zr->jpg_buffers.active =
+- ZORAN_FREE;
++ fh->buffers.active = zr->jpg_buffers.active = ZORAN_FREE;
+ zr->jpg_buffers.allocated = 0;
+ zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+ return 0;
+ } else {
+ dprintk(1,
+ KERN_ERR
+- "%s: jpg_qbuf() - stop streaming but not in streaming mode\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - stop streaming but not in streaming mode\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ }
+
+- if ((res = zoran_jpg_queue_frame(file, frame, mode)))
++ if ((res = zoran_jpg_queue_frame(fh, frame, mode)))
+ return res;
+
+ /* Start the jpeg codec when the first frame is queued */
+@@ -1028,28 +763,25 @@ jpg_qbuf (struct file *file,
+ * Sync on a MJPEG buffer
+ */
+
+-static int
+-jpg_sync (struct file *file,
+- struct zoran_sync *bs)
++static int jpg_sync(struct zoran_fh *fh, struct zoran_sync *bs)
+ {
+- struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+ unsigned long flags;
+ int frame;
+
+- if (fh->jpg_buffers.active == ZORAN_FREE) {
++ if (fh->buffers.active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+- "%s: jpg_sync() - capture is not currently active\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - capture is not currently active\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ if (zr->codec_mode != BUZ_MODE_MOTION_DECOMPRESS &&
+ zr->codec_mode != BUZ_MODE_MOTION_COMPRESS) {
+ dprintk(1,
+ KERN_ERR
+- "%s: jpg_sync() - codec not in streaming mode\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - codec not in streaming mode\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ if (!wait_event_interruptible_timeout(zr->jpg_capq,
+@@ -1064,8 +796,8 @@ jpg_sync (struct file *file,
+ sizeof(isr), &isr);
+ dprintk(1,
+ KERN_ERR
+- "%s: jpg_sync() - timeout: codec isr=0x%02x\n",
+- ZR_DEVNAME(zr), isr);
++ "%s: %s - timeout: codec isr=0x%02x\n",
++ ZR_DEVNAME(zr), __func__, isr);
+
+ return -ETIME;
+
+@@ -1083,28 +815,26 @@ jpg_sync (struct file *file,
+ /* buffer should now be in BUZ_STATE_DONE */
+ if (zr->jpg_buffers.buffer[frame].state != BUZ_STATE_DONE)
+ dprintk(2,
+- KERN_ERR "%s: jpg_sync() - internal state error\n",
+- ZR_DEVNAME(zr));
++ KERN_ERR "%s: %s - internal state error\n",
++ ZR_DEVNAME(zr), __func__);
+
+ *bs = zr->jpg_buffers.buffer[frame].bs;
+ bs->frame = frame;
+ zr->jpg_buffers.buffer[frame].state = BUZ_STATE_USER;
+- fh->jpg_buffers.buffer[frame] = zr->jpg_buffers.buffer[frame];
++ fh->buffers.buffer[frame] = zr->jpg_buffers.buffer[frame];
+
+ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+ return 0;
+ }
+
+-static void
+-zoran_open_init_session (struct file *file)
++static void zoran_open_init_session(struct zoran_fh *fh)
+ {
+ int i;
+- struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+
+ /* Per default, map the V4L Buffers */
+- fh->map_mode = ZORAN_MAP_MODE_RAW;
++ map_mode_raw(fh);
+
+ /* take over the card's current settings */
+ fh->overlay_settings = zr->overlay_settings;
+@@ -1114,40 +844,21 @@ zoran_open_init_session (struct file *file)
+
+ /* v4l settings */
+ fh->v4l_settings = zr->v4l_settings;
+-
+- /* v4l_buffers */
+- memset(&fh->v4l_buffers, 0, sizeof(struct zoran_v4l_struct));
+- for (i = 0; i < VIDEO_MAX_FRAME; i++) {
+- fh->v4l_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
+- fh->v4l_buffers.buffer[i].bs.frame = i;
+- }
+- fh->v4l_buffers.allocated = 0;
+- fh->v4l_buffers.ready_to_be_freed = 0;
+- fh->v4l_buffers.active = ZORAN_FREE;
+- fh->v4l_buffers.buffer_size = v4l_bufsize;
+- fh->v4l_buffers.num_buffers = v4l_nbufs;
+-
+ /* jpg settings */
+ fh->jpg_settings = zr->jpg_settings;
+
+- /* jpg_buffers */
+- memset(&fh->jpg_buffers, 0, sizeof(struct zoran_jpg_struct));
+- for (i = 0; i < BUZ_MAX_FRAME; i++) {
+- fh->jpg_buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
+- fh->jpg_buffers.buffer[i].bs.frame = i;
++ /* buffers */
++ memset(&fh->buffers, 0, sizeof(fh->buffers));
++ for (i = 0; i < MAX_FRAME; i++) {
++ fh->buffers.buffer[i].state = BUZ_STATE_USER; /* nothing going on */
++ fh->buffers.buffer[i].bs.frame = i;
+ }
+- fh->jpg_buffers.need_contiguous = zr->jpg_buffers.need_contiguous;
+- fh->jpg_buffers.allocated = 0;
+- fh->jpg_buffers.ready_to_be_freed = 0;
+- fh->jpg_buffers.active = ZORAN_FREE;
+- fh->jpg_buffers.buffer_size = jpg_bufsize;
+- fh->jpg_buffers.num_buffers = jpg_nbufs;
++ fh->buffers.allocated = 0;
++ fh->buffers.active = ZORAN_FREE;
+ }
+
+-static void
+-zoran_close_end_session (struct file *file)
++static void zoran_close_end_session(struct zoran_fh *fh)
+ {
+- struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+
+ /* overlay */
+@@ -1159,36 +870,32 @@ zoran_close_end_session (struct file *file)
+ zr->overlay_mask = NULL;
+ }
+
+- /* v4l capture */
+- if (fh->v4l_buffers.active != ZORAN_FREE) {
+- unsigned long flags;
++ if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
++ /* v4l capture */
++ if (fh->buffers.active != ZORAN_FREE) {
++ unsigned long flags;
+
+- spin_lock_irqsave(&zr->spinlock, flags);
+- zr36057_set_memgrab(zr, 0);
+- zr->v4l_buffers.allocated = 0;
+- zr->v4l_buffers.active = fh->v4l_buffers.active =
+- ZORAN_FREE;
+- spin_unlock_irqrestore(&zr->spinlock, flags);
+- }
+-
+- /* v4l buffers */
+- if (fh->v4l_buffers.allocated ||
+- fh->v4l_buffers.ready_to_be_freed) {
+- v4l_fbuffer_free(file);
+- }
++ spin_lock_irqsave(&zr->spinlock, flags);
++ zr36057_set_memgrab(zr, 0);
++ zr->v4l_buffers.allocated = 0;
++ zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE;
++ spin_unlock_irqrestore(&zr->spinlock, flags);
++ }
+
+- /* jpg capture */
+- if (fh->jpg_buffers.active != ZORAN_FREE) {
+- zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
+- zr->jpg_buffers.allocated = 0;
+- zr->jpg_buffers.active = fh->jpg_buffers.active =
+- ZORAN_FREE;
+- }
++ /* v4l buffers */
++ if (fh->buffers.allocated)
++ v4l_fbuffer_free(fh);
++ } else {
++ /* jpg capture */
++ if (fh->buffers.active != ZORAN_FREE) {
++ zr36057_enable_jpg(zr, BUZ_MODE_IDLE);
++ zr->jpg_buffers.allocated = 0;
++ zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE;
++ }
+
+- /* jpg buffers */
+- if (fh->jpg_buffers.allocated ||
+- fh->jpg_buffers.ready_to_be_freed) {
+- jpg_fbuffer_free(file);
++ /* jpg buffers */
++ if (fh->buffers.allocated)
++ jpg_fbuffer_free(fh);
+ }
+ }
+
+@@ -1202,15 +909,11 @@ static int zoran_open(struct file *file)
+ struct zoran_fh *fh;
+ int res, first_open = 0;
+
+- dprintk(2, KERN_INFO "%s: zoran_open(%s, pid=[%d]), users(-)=%d\n",
+- ZR_DEVNAME(zr), current->comm, task_pid_nr(current), zr->user + 1);
++ dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(-)=%d\n",
++ ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user + 1);
+
+ lock_kernel();
+
+- /* see fs/device.c - the kernel already locks during open(),
+- * so locking ourselves only causes deadlocks */
+- /*mutex_lock(&zr->resource_lock);*/
+-
+ if (zr->user >= 2048) {
+ dprintk(1, KERN_ERR "%s: too many users (%d) on device\n",
+ ZR_DEVNAME(zr), zr->user);
+@@ -1218,41 +921,15 @@ static int zoran_open(struct file *file)
+ goto fail_unlock;
+ }
+
+- if (!zr->decoder) {
+- dprintk(1,
+- KERN_ERR "%s: no TV decoder loaded for device!\n",
+- ZR_DEVNAME(zr));
+- res = -EIO;
+- goto fail_unlock;
+- }
+-
+- if (!try_module_get(zr->decoder->driver->driver.owner)) {
+- dprintk(1,
+- KERN_ERR
+- "%s: failed to grab ownership of video decoder\n",
+- ZR_DEVNAME(zr));
+- res = -EIO;
+- goto fail_unlock;
+- }
+- if (zr->encoder &&
+- !try_module_get(zr->encoder->driver->driver.owner)) {
+- dprintk(1,
+- KERN_ERR
+- "%s: failed to grab ownership of video encoder\n",
+- ZR_DEVNAME(zr));
+- res = -EIO;
+- goto fail_decoder;
+- }
+-
+ /* now, create the open()-specific file_ops struct */
+ fh = kzalloc(sizeof(struct zoran_fh), GFP_KERNEL);
+ if (!fh) {
+ dprintk(1,
+ KERN_ERR
+- "%s: zoran_open() - allocation of zoran_fh failed\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - allocation of zoran_fh failed\n",
++ ZR_DEVNAME(zr), __func__);
+ res = -ENOMEM;
+- goto fail_encoder;
++ goto fail_unlock;
+ }
+ /* used to be BUZ_MAX_WIDTH/HEIGHT, but that gives overflows
+ * on norm-change! */
+@@ -1261,8 +938,8 @@ static int zoran_open(struct file *file)
+ if (!fh->overlay_mask) {
+ dprintk(1,
+ KERN_ERR
+- "%s: zoran_open() - allocation of overlay_mask failed\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - allocation of overlay_mask failed\n",
++ ZR_DEVNAME(zr), __func__);
+ res = -ENOMEM;
+ goto fail_fh;
+ }
+@@ -1284,18 +961,13 @@ static int zoran_open(struct file *file)
+ /* set file_ops stuff */
+ file->private_data = fh;
+ fh->zr = zr;
+- zoran_open_init_session(file);
++ zoran_open_init_session(fh);
+ unlock_kernel();
+
+ return 0;
+
+ fail_fh:
+ kfree(fh);
+-fail_encoder:
+- if (zr->encoder)
+- module_put(zr->encoder->driver->driver.owner);
+-fail_decoder:
+- module_put(zr->decoder->driver->driver.owner);
+ fail_unlock:
+ unlock_kernel();
+
+@@ -1311,14 +983,14 @@ zoran_close(struct file *file)
+ struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+
+- dprintk(2, KERN_INFO "%s: zoran_close(%s, pid=[%d]), users(+)=%d\n",
+- ZR_DEVNAME(zr), current->comm, task_pid_nr(current), zr->user - 1);
++ dprintk(2, KERN_INFO "%s: %s(%s, pid=[%d]), users(+)=%d\n",
++ ZR_DEVNAME(zr), __func__, current->comm, task_pid_nr(current), zr->user - 1);
+
+ /* kernel locks (fs/device.c), so don't do that ourselves
+ * (prevents deadlocks) */
+ /*mutex_lock(&zr->resource_lock);*/
+
+- zoran_close_end_session(file);
++ zoran_close_end_session(fh);
+
+ if (zr->user-- == 1) { /* Last process */
+ /* Clean up JPEG process */
+@@ -1346,9 +1018,10 @@ zoran_close(struct file *file)
+ zoran_set_pci_master(zr, 0);
+
+ if (!pass_through) { /* Switch to color bar */
+- int zero = 0, two = 2;
+- decoder_command(zr, DECODER_ENABLE_OUTPUT, &zero);
+- encoder_command(zr, ENCODER_SET_INPUT, &two);
++ struct v4l2_routing route = { 2, 0 };
++
++ decoder_call(zr, video, s_stream, 0);
++ encoder_call(zr, video, s_routing, &route);
+ }
+ }
+
+@@ -1356,14 +1029,7 @@ zoran_close(struct file *file)
+ kfree(fh->overlay_mask);
+ kfree(fh);
+
+- /* release locks on the i2c modules */
+- module_put(zr->decoder->driver->driver.owner);
+- if (zr->encoder)
+- module_put(zr->encoder->driver->driver.owner);
+-
+- /*mutex_unlock(&zr->resource_lock);*/
+-
+- dprintk(4, KERN_INFO "%s: zoran_close() done\n", ZR_DEVNAME(zr));
++ dprintk(4, KERN_INFO "%s: %s done\n", ZR_DEVNAME(zr), __func__);
+
+ return 0;
+ }
+@@ -1391,15 +1057,13 @@ zoran_write (struct file *file,
+ return -EINVAL;
+ }
+
+-static int
+-setup_fbuffer (struct file *file,
++static int setup_fbuffer(struct zoran_fh *fh,
+ void *base,
+ const struct zoran_format *fmt,
+ int width,
+ int height,
+ int bytesperline)
+ {
+- struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+
+ /* (Ronald) v4l/v4l2 guidelines */
+@@ -1427,8 +1091,8 @@ setup_fbuffer (struct file *file,
+ * friendly and silently do as if nothing went wrong */
+ dprintk(3,
+ KERN_ERR
+- "%s: setup_fbuffer() - forced overlay turnoff because framebuffer changed\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - forced overlay turnoff because framebuffer changed\n",
++ ZR_DEVNAME(zr), __func__);
+ zr36057_overlay(zr, 0);
+ }
+ #endif
+@@ -1436,31 +1100,31 @@ setup_fbuffer (struct file *file,
+ if (!(fmt->flags & ZORAN_FORMAT_OVERLAY)) {
+ dprintk(1,
+ KERN_ERR
+- "%s: setup_fbuffer() - no valid overlay format given\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - no valid overlay format given\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ if (height <= 0 || width <= 0 || bytesperline <= 0) {
+ dprintk(1,
+ KERN_ERR
+- "%s: setup_fbuffer() - invalid height/width/bpl value (%d|%d|%d)\n",
+- ZR_DEVNAME(zr), width, height, bytesperline);
++ "%s: %s - invalid height/width/bpl value (%d|%d|%d)\n",
++ ZR_DEVNAME(zr), __func__, width, height, bytesperline);
+ return -EINVAL;
+ }
+ if (bytesperline & 3) {
+ dprintk(1,
+ KERN_ERR
+- "%s: setup_fbuffer() - bytesperline (%d) must be 4-byte aligned\n",
+- ZR_DEVNAME(zr), bytesperline);
++ "%s: %s - bytesperline (%d) must be 4-byte aligned\n",
++ ZR_DEVNAME(zr), __func__, bytesperline);
+ return -EINVAL;
+ }
+
+- zr->buffer.base = (void *) ((unsigned long) base & ~3);
+- zr->buffer.height = height;
+- zr->buffer.width = width;
+- zr->buffer.depth = fmt->depth;
++ zr->vbuf_base = (void *) ((unsigned long) base & ~3);
++ zr->vbuf_height = height;
++ zr->vbuf_width = width;
++ zr->vbuf_depth = fmt->depth;
+ zr->overlay_settings.format = fmt;
+- zr->buffer.bytesperline = bytesperline;
++ zr->vbuf_bytesperline = bytesperline;
+
+ /* The user should set new window parameters */
+ zr->overlay_settings.is_set = 0;
+@@ -1469,35 +1133,27 @@ setup_fbuffer (struct file *file,
+ }
+
+
+-static int
+-setup_window (struct file *file,
+- int x,
+- int y,
+- int width,
+- int height,
+- struct video_clip __user *clips,
+- int clipcount,
+- void __user *bitmap)
++static int setup_window(struct zoran_fh *fh, int x, int y, int width, int height,
++ struct v4l2_clip __user *clips, int clipcount, void __user *bitmap)
+ {
+- struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+- struct video_clip *vcp = NULL;
++ struct v4l2_clip *vcp = NULL;
+ int on, end;
+
+
+- if (!zr->buffer.base) {
++ if (!zr->vbuf_base) {
+ dprintk(1,
+ KERN_ERR
+- "%s: setup_window() - frame buffer has to be set first\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - frame buffer has to be set first\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ if (!fh->overlay_settings.format) {
+ dprintk(1,
+ KERN_ERR
+- "%s: setup_window() - no overlay format set\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - no overlay format set\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+@@ -1505,13 +1161,13 @@ setup_window (struct file *file,
+ * The video front end needs 4-byte alinged line sizes, we correct that
+ * silently here if necessary
+ */
+- if (zr->buffer.depth == 15 || zr->buffer.depth == 16) {
++ if (zr->vbuf_depth == 15 || zr->vbuf_depth == 16) {
+ end = (x + width) & ~1; /* round down */
+ x = (x + 1) & ~1; /* round up */
+ width = end - x;
+ }
+
+- if (zr->buffer.depth == 24) {
++ if (zr->vbuf_depth == 24) {
+ end = (x + width) & ~3; /* round down */
+ x = (x + 3) & ~3; /* round up */
+ width = end - x;
+@@ -1527,8 +1183,8 @@ setup_window (struct file *file,
+ width > BUZ_MAX_WIDTH || height > BUZ_MAX_HEIGHT) {
+ dprintk(1,
+ KERN_ERR
+- "%s: setup_window() - width = %d or height = %d invalid\n",
+- ZR_DEVNAME(zr), width, height);
++ "%s: %s - width = %d or height = %d invalid\n",
++ ZR_DEVNAME(zr), __func__, width, height);
+ return -EINVAL;
+ }
+
+@@ -1566,20 +1222,20 @@ setup_window (struct file *file,
+ }
+ } else if (clipcount > 0) {
+ /* write our own bitmap from the clips */
+- vcp = vmalloc(sizeof(struct video_clip) * (clipcount + 4));
++ vcp = vmalloc(sizeof(struct v4l2_clip) * (clipcount + 4));
+ if (vcp == NULL) {
+ dprintk(1,
+ KERN_ERR
+- "%s: setup_window() - Alloc of clip mask failed\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - Alloc of clip mask failed\n",
++ ZR_DEVNAME(zr), __func__);
+ return -ENOMEM;
+ }
+ if (copy_from_user
+- (vcp, clips, sizeof(struct video_clip) * clipcount)) {
++ (vcp, clips, sizeof(struct v4l2_clip) * clipcount)) {
+ vfree(vcp);
+ return -EFAULT;
+ }
+- write_overlay_mask(file, vcp, clipcount);
++ write_overlay_mask(fh, vcp, clipcount);
+ vfree(vcp);
+ }
+
+@@ -1595,11 +1251,8 @@ setup_window (struct file *file,
+ return wait_grab_pending(zr);
+ }
+
+-static int
+-setup_overlay (struct file *file,
+- int on)
++static int setup_overlay(struct zoran_fh *fh, int on)
+ {
+- struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
+
+ /* If there is nothing to do, return immediatly */
+@@ -1612,16 +1265,16 @@ setup_overlay (struct file *file,
+ fh->overlay_active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+- "%s: setup_overlay() - overlay is already active for another session\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - overlay is already active for another session\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EBUSY;
+ }
+ if (!on && zr->overlay_active != ZORAN_FREE &&
+ fh->overlay_active == ZORAN_FREE) {
+ dprintk(1,
+ KERN_ERR
+- "%s: setup_overlay() - you cannot cancel someone else's session\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - you cannot cancel someone else's session\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EPERM;
+ }
+
+@@ -1634,18 +1287,18 @@ setup_overlay (struct file *file,
+ zr36057_overlay(zr, 0);
+ zr->overlay_mask = NULL;
+ } else {
+- if (!zr->buffer.base || !fh->overlay_settings.is_set) {
++ if (!zr->vbuf_base || !fh->overlay_settings.is_set) {
+ dprintk(1,
+ KERN_ERR
+- "%s: setup_overlay() - buffer or window not set\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - buffer or window not set\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ if (!fh->overlay_settings.format) {
+ dprintk(1,
+ KERN_ERR
+- "%s: setup_overlay() - no overlay format set\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - no overlay format set\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+ zr->overlay_active = fh->overlay_active = ZORAN_LOCKED;
+@@ -1662,41 +1315,47 @@ setup_overlay (struct file *file,
+ return wait_grab_pending(zr);
+ }
+
+- /* get the status of a buffer in the clients buffer queue */
+-static int
+-zoran_v4l2_buffer_status (struct file *file,
+- struct v4l2_buffer *buf,
+- int num)
++/* get the status of a buffer in the clients buffer queue */
++static int zoran_v4l2_buffer_status(struct zoran_fh *fh,
++ struct v4l2_buffer *buf, int num)
+ {
+- struct zoran_fh *fh = file->private_data;
+ struct zoran *zr = fh->zr;
++ unsigned long flags;
+
+ buf->flags = V4L2_BUF_FLAG_MAPPED;
+
+ switch (fh->map_mode) {
+ case ZORAN_MAP_MODE_RAW:
+-
+ /* check range */
+- if (num < 0 || num >= fh->v4l_buffers.num_buffers ||
+- !fh->v4l_buffers.allocated) {
++ if (num < 0 || num >= fh->buffers.num_buffers ||
++ !fh->buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+- "%s: v4l2_buffer_status() - wrong number or buffers not allocated\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - wrong number or buffers not allocated\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
++ spin_lock_irqsave(&zr->spinlock, flags);
++ dprintk(3,
++ KERN_DEBUG
++ "%s: %s() - raw active=%c, buffer %d: state=%c, map=%c\n",
++ ZR_DEVNAME(zr), __func__,
++ "FAL"[fh->buffers.active], num,
++ "UPMD"[zr->v4l_buffers.buffer[num].state],
++ fh->buffers.buffer[num].map ? 'Y' : 'N');
++ spin_unlock_irqrestore(&zr->spinlock, flags);
++
+ buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+- buf->length = fh->v4l_buffers.buffer_size;
++ buf->length = fh->buffers.buffer_size;
+
+ /* get buffer */
+- buf->bytesused = fh->v4l_buffers.buffer[num].bs.length;
+- if (fh->v4l_buffers.buffer[num].state == BUZ_STATE_DONE ||
+- fh->v4l_buffers.buffer[num].state == BUZ_STATE_USER) {
+- buf->sequence = fh->v4l_buffers.buffer[num].bs.seq;
++ buf->bytesused = fh->buffers.buffer[num].bs.length;
++ if (fh->buffers.buffer[num].state == BUZ_STATE_DONE ||
++ fh->buffers.buffer[num].state == BUZ_STATE_USER) {
++ buf->sequence = fh->buffers.buffer[num].bs.seq;
+ buf->flags |= V4L2_BUF_FLAG_DONE;
+- buf->timestamp =
+- fh->v4l_buffers.buffer[num].bs.timestamp;
++ buf->timestamp = fh->buffers.buffer[num].bs.timestamp;
+ } else {
+ buf->flags |= V4L2_BUF_FLAG_QUEUED;
+ }
+@@ -1712,28 +1371,26 @@ zoran_v4l2_buffer_status (struct file *file,
+ case ZORAN_MAP_MODE_JPG_PLAY:
+
+ /* check range */
+- if (num < 0 || num >= fh->jpg_buffers.num_buffers ||
+- !fh->jpg_buffers.allocated) {
++ if (num < 0 || num >= fh->buffers.num_buffers ||
++ !fh->buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+- "%s: v4l2_buffer_status() - wrong number or buffers not allocated\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - wrong number or buffers not allocated\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+ buf->type = (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ?
+ V4L2_BUF_TYPE_VIDEO_CAPTURE :
+ V4L2_BUF_TYPE_VIDEO_OUTPUT;
+- buf->length = fh->jpg_buffers.buffer_size;
++ buf->length = fh->buffers.buffer_size;
+
+ /* these variables are only written after frame has been captured */
+- if (fh->jpg_buffers.buffer[num].state == BUZ_STATE_DONE ||
+- fh->jpg_buffers.buffer[num].state == BUZ_STATE_USER) {
+- buf->sequence = fh->jpg_buffers.buffer[num].bs.seq;
+- buf->timestamp =
+- fh->jpg_buffers.buffer[num].bs.timestamp;
+- buf->bytesused =
+- fh->jpg_buffers.buffer[num].bs.length;
++ if (fh->buffers.buffer[num].state == BUZ_STATE_DONE ||
++ fh->buffers.buffer[num].state == BUZ_STATE_USER) {
++ buf->sequence = fh->buffers.buffer[num].bs.seq;
++ buf->timestamp = fh->buffers.buffer[num].bs.timestamp;
++ buf->bytesused = fh->buffers.buffer[num].bs.length;
+ buf->flags |= V4L2_BUF_FLAG_DONE;
+ } else {
+ buf->flags |= V4L2_BUF_FLAG_QUEUED;
+@@ -1741,14 +1398,11 @@ zoran_v4l2_buffer_status (struct file *file,
+
+ /* which fields are these? */
+ if (fh->jpg_settings.TmpDcm != 1)
+- buf->field =
+- fh->jpg_settings.
+- odd_even ? V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
++ buf->field = fh->jpg_settings.odd_even ?
++ V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM;
+ else
+- buf->field =
+- fh->jpg_settings.
+- odd_even ? V4L2_FIELD_SEQ_TB :
+- V4L2_FIELD_SEQ_BT;
++ buf->field = fh->jpg_settings.odd_even ?
++ V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT;
+
+ break;
+
+@@ -1756,8 +1410,8 @@ zoran_v4l2_buffer_status (struct file *file,
+
+ dprintk(5,
+ KERN_ERR
+- "%s: v4l2_buffer_status() - invalid buffer type|map_mode (%d|%d)\n",
+- ZR_DEVNAME(zr), buf->type, fh->map_mode);
++ "%s: %s - invalid buffer type|map_mode (%d|%d)\n",
++ ZR_DEVNAME(zr), __func__, buf->type, fh->map_mode);
+ return -EINVAL;
+ }
+
+@@ -1770,81 +1424,55 @@ zoran_v4l2_buffer_status (struct file *file,
+
+ static int
+ zoran_set_norm (struct zoran *zr,
+- int norm) /* VIDEO_MODE_* */
++ v4l2_std_id norm)
+ {
+- int norm_encoder, on;
++ int on;
+
+ if (zr->v4l_buffers.active != ZORAN_FREE ||
+ zr->jpg_buffers.active != ZORAN_FREE) {
+ dprintk(1,
+ KERN_WARNING
+- "%s: set_norm() called while in playback/capture mode\n",
+- ZR_DEVNAME(zr));
++ "%s: %s called while in playback/capture mode\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EBUSY;
+ }
+
+- if (lock_norm && norm != zr->norm) {
+- if (lock_norm > 1) {
+- dprintk(1,
+- KERN_WARNING
+- "%s: set_norm() - TV standard is locked, can not switch norm\n",
+- ZR_DEVNAME(zr));
+- return -EPERM;
+- } else {
+- dprintk(1,
+- KERN_WARNING
+- "%s: set_norm() - TV standard is locked, norm was not changed\n",
+- ZR_DEVNAME(zr));
+- norm = zr->norm;
+- }
+- }
+-
+- if (norm != VIDEO_MODE_AUTO &&
+- (norm < 0 || norm >= zr->card.norms ||
+- !zr->card.tvn[norm])) {
++ if (!(norm & zr->card.norms)) {
+ dprintk(1,
+- KERN_ERR "%s: set_norm() - unsupported norm %d\n",
+- ZR_DEVNAME(zr), norm);
++ KERN_ERR "%s: %s - unsupported norm %llx\n",
++ ZR_DEVNAME(zr), __func__, norm);
+ return -EINVAL;
+ }
+
+- if (norm == VIDEO_MODE_AUTO) {
+- int status;
++ if (norm == V4L2_STD_ALL) {
++ int status = 0;
++ v4l2_std_id std = 0;
+
+- /* if we have autodetect, ... */
+- struct video_decoder_capability caps;
+- decoder_command(zr, DECODER_GET_CAPABILITIES, &caps);
+- if (!(caps.flags & VIDEO_DECODER_AUTO)) {
+- dprintk(1, KERN_ERR "%s: norm=auto unsupported\n",
+- ZR_DEVNAME(zr));
+- return -EINVAL;
+- }
+-
+- decoder_command(zr, DECODER_SET_NORM, &norm);
++ decoder_call(zr, video, querystd, &std);
++ decoder_call(zr, tuner, s_std, std);
+
+ /* let changes come into effect */
+ ssleep(2);
+
+- decoder_command(zr, DECODER_GET_STATUS, &status);
+- if (!(status & DECODER_STATUS_GOOD)) {
++ decoder_call(zr, video, g_input_status, &status);
++ if (status & V4L2_IN_ST_NO_SIGNAL) {
+ dprintk(1,
+ KERN_ERR
+- "%s: set_norm() - no norm detected\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - no norm detected\n",
++ ZR_DEVNAME(zr), __func__);
+ /* reset norm */
+- decoder_command(zr, DECODER_SET_NORM, &zr->norm);
++ decoder_call(zr, tuner, s_std, zr->norm);
+ return -EIO;
+ }
+
+- if (status & DECODER_STATUS_NTSC)
+- norm = VIDEO_MODE_NTSC;
+- else if (status & DECODER_STATUS_SECAM)
+- norm = VIDEO_MODE_SECAM;
+- else
+- norm = VIDEO_MODE_PAL;
++ norm = std;
+ }
+- zr->timing = zr->card.tvn[norm];
+- norm_encoder = norm;
++ if (norm & V4L2_STD_SECAM)
++ zr->timing = zr->card.tvn[2];
++ else if (norm & V4L2_STD_NTSC)
++ zr->timing = zr->card.tvn[1];
++ else
++ zr->timing = zr->card.tvn[0];
+
+ /* We switch overlay off and on since a change in the
+ * norm needs different VFE settings */
+@@ -1852,8 +1480,8 @@ zoran_set_norm (struct zoran *zr,
+ if (on)
+ zr36057_overlay(zr, 0);
+
+- decoder_command(zr, DECODER_SET_NORM, &norm);
+- encoder_command(zr, ENCODER_SET_NORM, &norm_encoder);
++ decoder_call(zr, tuner, s_std, norm);
++ encoder_call(zr, video, s_std_output, norm);
+
+ if (on)
+ zr36057_overlay(zr, 1);
+@@ -1868,7 +1496,7 @@ static int
+ zoran_set_input (struct zoran *zr,
+ int input)
+ {
+- int realinput;
++ struct v4l2_routing route = { 0, 0 };
+
+ if (input == zr->input) {
+ return 0;
+@@ -1878,23 +1506,23 @@ zoran_set_input (struct zoran *zr,
+ zr->jpg_buffers.active != ZORAN_FREE) {
+ dprintk(1,
+ KERN_WARNING
+- "%s: set_input() called while in playback/capture mode\n",
+- ZR_DEVNAME(zr));
++ "%s: %s called while in playback/capture mode\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EBUSY;
+ }
+
+ if (input < 0 || input >= zr->card.inputs) {
+ dprintk(1,
+ KERN_ERR
+- "%s: set_input() - unnsupported input %d\n",
+- ZR_DEVNAME(zr), input);
++ "%s: %s - unnsupported input %d\n",
++ ZR_DEVNAME(zr), __func__, input);
+ return -EINVAL;
+ }
+
+- realinput = zr->card.input[input].muxsel;
++ route.input = zr->card.input[input].muxsel;
+ zr->input = input;
+
+- decoder_command(zr, DECODER_SET_INPUT, &realinput);
++ decoder_call(zr, video, s_routing, &route);
+
+ return 0;
+ }
+@@ -1903,410 +1531,14 @@ zoran_set_input (struct zoran *zr,
+ * ioctl routine
+ */
+
+-static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg)
++#ifdef CONFIG_VIDEO_V4L1_COMPAT
++static long zoran_default(struct file *file, void *__fh, int cmd, void *arg)
+ {
+- struct zoran_fh *fh = file->private_data;
++ struct zoran_fh *fh = __fh;
+ struct zoran *zr = fh->zr;
+- /* CAREFUL: used in multiple places here */
+ struct zoran_jpg_settings settings;
+
+- /* we might have older buffers lying around... We don't want
+- * to wait, but we do want to try cleaning them up ASAP. So
+- * we try to obtain the lock and free them. If that fails, we
+- * don't do anything and wait for the next turn. In the end,
+- * zoran_close() or a new allocation will still free them...
+- * This is just a 'the sooner the better' extra 'feature'
+- *
+- * We don't free the buffers right on munmap() because that
+- * causes oopses (kfree() inside munmap() oopses for no
+- * apparent reason - it's also not reproduceable in any way,
+- * but moving the free code outside the munmap() handler fixes
+- * all this... If someone knows why, please explain me (Ronald)
+- */
+- if (mutex_trylock(&zr->resource_lock)) {
+- /* we obtained it! Let's try to free some things */
+- if (fh->jpg_buffers.ready_to_be_freed)
+- jpg_fbuffer_free(file);
+- if (fh->v4l_buffers.ready_to_be_freed)
+- v4l_fbuffer_free(file);
+-
+- mutex_unlock(&zr->resource_lock);
+- }
+-
+ switch (cmd) {
+-
+- case VIDIOCGCAP:
+- {
+- struct video_capability *vcap = arg;
+-
+- dprintk(3, KERN_DEBUG "%s: VIDIOCGCAP\n", ZR_DEVNAME(zr));
+-
+- memset(vcap, 0, sizeof(struct video_capability));
+- strncpy(vcap->name, ZR_DEVNAME(zr), sizeof(vcap->name)-1);
+- vcap->type = ZORAN_VID_TYPE;
+-
+- vcap->channels = zr->card.inputs;
+- vcap->audios = 0;
+- mutex_lock(&zr->resource_lock);
+- vcap->maxwidth = BUZ_MAX_WIDTH;
+- vcap->maxheight = BUZ_MAX_HEIGHT;
+- vcap->minwidth = BUZ_MIN_WIDTH;
+- vcap->minheight = BUZ_MIN_HEIGHT;
+- mutex_unlock(&zr->resource_lock);
+-
+- return 0;
+- }
+- break;
+-
+- case VIDIOCGCHAN:
+- {
+- struct video_channel *vchan = arg;
+- int channel = vchan->channel;
+-
+- dprintk(3, KERN_DEBUG "%s: VIDIOCGCHAN - channel=%d\n",
+- ZR_DEVNAME(zr), vchan->channel);
+-
+- memset(vchan, 0, sizeof(struct video_channel));
+- if (channel > zr->card.inputs || channel < 0) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOCGCHAN on not existing channel %d\n",
+- ZR_DEVNAME(zr), channel);
+- return -EINVAL;
+- }
+-
+- strcpy(vchan->name, zr->card.input[channel].name);
+-
+- vchan->tuners = 0;
+- vchan->flags = 0;
+- vchan->type = VIDEO_TYPE_CAMERA;
+- mutex_lock(&zr->resource_lock);
+- vchan->norm = zr->norm;
+- mutex_unlock(&zr->resource_lock);
+- vchan->channel = channel;
+-
+- return 0;
+- }
+- break;
+-
+- /* RJ: the documentation at http://roadrunner.swansea.linux.org.uk/v4lapi.shtml says:
+- *
+- * * "The VIDIOCSCHAN ioctl takes an integer argument and switches the capture to this input."
+- * * ^^^^^^^
+- * * The famos BTTV driver has it implemented with a struct video_channel argument
+- * * and we follow it for compatibility reasons
+- * *
+- * * BTW: this is the only way the user can set the norm!
+- */
+-
+- case VIDIOCSCHAN:
+- {
+- struct video_channel *vchan = arg;
+- int res;
+-
+- dprintk(3,
+- KERN_DEBUG
+- "%s: VIDIOCSCHAN - channel=%d, norm=%d\n",
+- ZR_DEVNAME(zr), vchan->channel, vchan->norm);
+-
+- mutex_lock(&zr->resource_lock);
+- if ((res = zoran_set_input(zr, vchan->channel)))
+- goto schan_unlock_and_return;
+- if ((res = zoran_set_norm(zr, vchan->norm)))
+- goto schan_unlock_and_return;
+-
+- /* Make sure the changes come into effect */
+- res = wait_grab_pending(zr);
+- schan_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
+- return res;
+- }
+- break;
+-
+- case VIDIOCGPICT:
+- {
+- struct video_picture *vpict = arg;
+-
+- dprintk(3, KERN_DEBUG "%s: VIDIOCGPICT\n", ZR_DEVNAME(zr));
+-
+- memset(vpict, 0, sizeof(struct video_picture));
+- mutex_lock(&zr->resource_lock);
+- vpict->hue = zr->hue;
+- vpict->brightness = zr->brightness;
+- vpict->contrast = zr->contrast;
+- vpict->colour = zr->saturation;
+- if (fh->overlay_settings.format) {
+- vpict->depth = fh->overlay_settings.format->depth;
+- vpict->palette = fh->overlay_settings.format->palette;
+- } else {
+- vpict->depth = 0;
+- }
+- mutex_unlock(&zr->resource_lock);
+-
+- return 0;
+- }
+- break;
+-
+- case VIDIOCSPICT:
+- {
+- struct video_picture *vpict = arg;
+- int i;
+-
+- dprintk(3,
+- KERN_DEBUG
+- "%s: VIDIOCSPICT - bri=%d, hue=%d, col=%d, con=%d, dep=%d, pal=%d\n",
+- ZR_DEVNAME(zr), vpict->brightness, vpict->hue,
+- vpict->colour, vpict->contrast, vpict->depth,
+- vpict->palette);
+-
+- for (i = 0; i < NUM_FORMATS; i++) {
+- const struct zoran_format *fmt = &zoran_formats[i];
+-
+- if (fmt->palette != -1 &&
+- fmt->flags & ZORAN_FORMAT_OVERLAY &&
+- fmt->palette == vpict->palette &&
+- fmt->depth == vpict->depth)
+- break;
+- }
+- if (i == NUM_FORMATS) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOCSPICT - Invalid palette %d\n",
+- ZR_DEVNAME(zr), vpict->palette);
+- return -EINVAL;
+- }
+-
+- mutex_lock(&zr->resource_lock);
+-
+- decoder_command(zr, DECODER_SET_PICTURE, vpict);
+-
+- zr->hue = vpict->hue;
+- zr->contrast = vpict->contrast;
+- zr->saturation = vpict->colour;
+- zr->brightness = vpict->brightness;
+-
+- fh->overlay_settings.format = &zoran_formats[i];
+-
+- mutex_unlock(&zr->resource_lock);
+-
+- return 0;
+- }
+- break;
+-
+- case VIDIOCCAPTURE:
+- {
+- int *on = arg, res;
+-
+- dprintk(3, KERN_DEBUG "%s: VIDIOCCAPTURE - on=%d\n",
+- ZR_DEVNAME(zr), *on);
+-
+- mutex_lock(&zr->resource_lock);
+- res = setup_overlay(file, *on);
+- mutex_unlock(&zr->resource_lock);
+-
+- return res;
+- }
+- break;
+-
+- case VIDIOCGWIN:
+- {
+- struct video_window *vwin = arg;
+-
+- dprintk(3, KERN_DEBUG "%s: VIDIOCGWIN\n", ZR_DEVNAME(zr));
+-
+- memset(vwin, 0, sizeof(struct video_window));
+- mutex_lock(&zr->resource_lock);
+- vwin->x = fh->overlay_settings.x;
+- vwin->y = fh->overlay_settings.y;
+- vwin->width = fh->overlay_settings.width;
+- vwin->height = fh->overlay_settings.height;
+- mutex_unlock(&zr->resource_lock);
+- vwin->clipcount = 0;
+- return 0;
+- }
+- break;
+-
+- case VIDIOCSWIN:
+- {
+- struct video_window *vwin = arg;
+- int res;
+-
+- dprintk(3,
+- KERN_DEBUG
+- "%s: VIDIOCSWIN - x=%d, y=%d, w=%d, h=%d, clipcount=%d\n",
+- ZR_DEVNAME(zr), vwin->x, vwin->y, vwin->width,
+- vwin->height, vwin->clipcount);
+-
+- mutex_lock(&zr->resource_lock);
+- res =
+- setup_window(file, vwin->x, vwin->y, vwin->width,
+- vwin->height, vwin->clips,
+- vwin->clipcount, NULL);
+- mutex_unlock(&zr->resource_lock);
+-
+- return res;
+- }
+- break;
+-
+- case VIDIOCGFBUF:
+- {
+- struct video_buffer *vbuf = arg;
+-
+- dprintk(3, KERN_DEBUG "%s: VIDIOCGFBUF\n", ZR_DEVNAME(zr));
+-
+- mutex_lock(&zr->resource_lock);
+- *vbuf = zr->buffer;
+- mutex_unlock(&zr->resource_lock);
+- return 0;
+- }
+- break;
+-
+- case VIDIOCSFBUF:
+- {
+- struct video_buffer *vbuf = arg;
+- int i, res = 0;
+-
+- dprintk(3,
+- KERN_DEBUG
+- "%s: VIDIOCSFBUF - base=%p, w=%d, h=%d, depth=%d, bpl=%d\n",
+- ZR_DEVNAME(zr), vbuf->base, vbuf->width,
+- vbuf->height, vbuf->depth, vbuf->bytesperline);
+-
+- for (i = 0; i < NUM_FORMATS; i++)
+- if (zoran_formats[i].depth == vbuf->depth)
+- break;
+- if (i == NUM_FORMATS) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOCSFBUF - invalid fbuf depth %d\n",
+- ZR_DEVNAME(zr), vbuf->depth);
+- return -EINVAL;
+- }
+-
+- mutex_lock(&zr->resource_lock);
+- res =
+- setup_fbuffer(file, vbuf->base, &zoran_formats[i],
+- vbuf->width, vbuf->height,
+- vbuf->bytesperline);
+- mutex_unlock(&zr->resource_lock);
+-
+- return res;
+- }
+- break;
+-
+- case VIDIOCSYNC:
+- {
+- int *frame = arg, res;
+-
+- dprintk(3, KERN_DEBUG "%s: VIDIOCSYNC - frame=%d\n",
+- ZR_DEVNAME(zr), *frame);
+-
+- mutex_lock(&zr->resource_lock);
+- res = v4l_sync(file, *frame);
+- mutex_unlock(&zr->resource_lock);
+- if (!res)
+- zr->v4l_sync_tail++;
+- return res;
+- }
+- break;
+-
+- case VIDIOCMCAPTURE:
+- {
+- struct video_mmap *vmap = arg;
+- int res;
+-
+- dprintk(3,
+- KERN_DEBUG
+- "%s: VIDIOCMCAPTURE - frame=%d, geom=%dx%d, fmt=%d\n",
+- ZR_DEVNAME(zr), vmap->frame, vmap->width, vmap->height,
+- vmap->format);
+-
+- mutex_lock(&zr->resource_lock);
+- res = v4l_grab(file, vmap);
+- mutex_unlock(&zr->resource_lock);
+- return res;
+- }
+- break;
+-
+- case VIDIOCGMBUF:
+- {
+- struct video_mbuf *vmbuf = arg;
+- int i, res = 0;
+-
+- dprintk(3, KERN_DEBUG "%s: VIDIOCGMBUF\n", ZR_DEVNAME(zr));
+-
+- vmbuf->size =
+- fh->v4l_buffers.num_buffers *
+- fh->v4l_buffers.buffer_size;
+- vmbuf->frames = fh->v4l_buffers.num_buffers;
+- for (i = 0; i < vmbuf->frames; i++) {
+- vmbuf->offsets[i] =
+- i * fh->v4l_buffers.buffer_size;
+- }
+-
+- mutex_lock(&zr->resource_lock);
+-
+- if (fh->jpg_buffers.allocated || fh->v4l_buffers.allocated) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOCGMBUF - buffers already allocated\n",
+- ZR_DEVNAME(zr));
+- res = -EINVAL;
+- goto v4l1reqbuf_unlock_and_return;
+- }
+-
+- if (v4l_fbuffer_alloc(file)) {
+- res = -ENOMEM;
+- goto v4l1reqbuf_unlock_and_return;
+- }
+-
+- /* The next mmap will map the V4L buffers */
+- fh->map_mode = ZORAN_MAP_MODE_RAW;
+- v4l1reqbuf_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
+-
+- return res;
+- }
+- break;
+-
+- case VIDIOCGUNIT:
+- {
+- struct video_unit *vunit = arg;
+-
+- dprintk(3, KERN_DEBUG "%s: VIDIOCGUNIT\n", ZR_DEVNAME(zr));
+-
+- vunit->video = zr->video_dev->minor;
+- vunit->vbi = VIDEO_NO_UNIT;
+- vunit->radio = VIDEO_NO_UNIT;
+- vunit->audio = VIDEO_NO_UNIT;
+- vunit->teletext = VIDEO_NO_UNIT;
+-
+- return 0;
+- }
+- break;
+-
+- /*
+- * RJ: In principal we could support subcaptures for V4L grabbing.
+- * Not even the famous BTTV driver has them, however.
+- * If there should be a strong demand, one could consider
+- * to implement them.
+- */
+- case VIDIOCGCAPTURE:
+- {
+- dprintk(3, KERN_ERR "%s: VIDIOCGCAPTURE not supported\n",
+- ZR_DEVNAME(zr));
+- return -EINVAL;
+- }
+- break;
+-
+- case VIDIOCSCAPTURE:
+- {
+- dprintk(3, KERN_ERR "%s: VIDIOCSCAPTURE not supported\n",
+- ZR_DEVNAME(zr));
+- return -EINVAL;
+- }
+- break;
+-
+ case BUZIOC_G_PARAMS:
+ {
+ struct zoran_params *bparams = arg;
+@@ -2319,7 +1551,13 @@ static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+
+ mutex_lock(&zr->resource_lock);
+
+- bparams->norm = zr->norm;
++ if (zr->norm & V4L2_STD_NTSC)
++ bparams->norm = VIDEO_MODE_NTSC;
++ else if (zr->norm & V4L2_STD_PAL)
++ bparams->norm = VIDEO_MODE_PAL;
++ else
++ bparams->norm = VIDEO_MODE_SECAM;
++
+ bparams->input = zr->input;
+
+ bparams->decimation = fh->jpg_settings.decimation;
+@@ -2352,7 +1590,6 @@ static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+
+ return 0;
+ }
+- break;
+
+ case BUZIOC_S_PARAMS:
+ {
+@@ -2395,18 +1632,17 @@ static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+
+ /* Check the params first before overwriting our
+ * nternal values */
+- if (zoran_check_jpg_settings(zr, &settings)) {
++ if (zoran_check_jpg_settings(zr, &settings, 0)) {
+ res = -EINVAL;
+ goto sparams_unlock_and_return;
+ }
+
+ fh->jpg_settings = settings;
+- sparams_unlock_and_return:
++sparams_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+- break;
+
+ case BUZIOC_REQBUFS:
+ {
+@@ -2430,38 +1666,34 @@ static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+ * tables to a Maximum of 2 MB */
+ if (breq->size > jpg_bufsize)
+ breq->size = jpg_bufsize;
+- if (fh->jpg_buffers.need_contiguous &&
+- breq->size > MAX_KMALLOC_MEM)
+- breq->size = MAX_KMALLOC_MEM;
+
+ mutex_lock(&zr->resource_lock);
+
+- if (fh->jpg_buffers.allocated || fh->v4l_buffers.allocated) {
++ if (fh->buffers.allocated) {
+ dprintk(1,
+ KERN_ERR
+- "%s: BUZIOC_REQBUFS - buffers allready allocated\n",
++ "%s: BUZIOC_REQBUFS - buffers already allocated\n",
+ ZR_DEVNAME(zr));
+ res = -EBUSY;
+ goto jpgreqbuf_unlock_and_return;
+ }
+
+- fh->jpg_buffers.num_buffers = breq->count;
+- fh->jpg_buffers.buffer_size = breq->size;
++ /* The next mmap will map the MJPEG buffers - could
++ * also be *_PLAY, but it doesn't matter here */
++ map_mode_jpg(fh, 0);
++ fh->buffers.num_buffers = breq->count;
++ fh->buffers.buffer_size = breq->size;
+
+- if (jpg_fbuffer_alloc(file)) {
++ if (jpg_fbuffer_alloc(fh)) {
+ res = -ENOMEM;
+ goto jpgreqbuf_unlock_and_return;
+ }
+
+- /* The next mmap will map the MJPEG buffers - could
+- * also be *_PLAY, but it doesn't matter here */
+- fh->map_mode = ZORAN_MAP_MODE_JPG_REC;
+- jpgreqbuf_unlock_and_return:
++jpgreqbuf_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+- break;
+
+ case BUZIOC_QBUF_CAPT:
+ {
+@@ -2471,12 +1703,11 @@ static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+ ZR_DEVNAME(zr), *frame);
+
+ mutex_lock(&zr->resource_lock);
+- res = jpg_qbuf(file, *frame, BUZ_MODE_MOTION_COMPRESS);
++ res = jpg_qbuf(fh, *frame, BUZ_MODE_MOTION_COMPRESS);
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+- break;
+
+ case BUZIOC_QBUF_PLAY:
+ {
+@@ -2486,12 +1717,11 @@ static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+ ZR_DEVNAME(zr), *frame);
+
+ mutex_lock(&zr->resource_lock);
+- res = jpg_qbuf(file, *frame, BUZ_MODE_MOTION_DECOMPRESS);
++ res = jpg_qbuf(fh, *frame, BUZ_MODE_MOTION_DECOMPRESS);
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+- break;
+
+ case BUZIOC_SYNC:
+ {
+@@ -2501,17 +1731,26 @@ static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+ dprintk(3, KERN_DEBUG "%s: BUZIOC_SYNC\n", ZR_DEVNAME(zr));
+
+ mutex_lock(&zr->resource_lock);
+- res = jpg_sync(file, bsync);
++
++ if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
++ dprintk(2, KERN_WARNING
++ "%s: %s - not in jpg capture mode\n",
++ ZR_DEVNAME(zr), __func__);
++ res = -EINVAL;
++ } else {
++ res = jpg_sync(fh, bsync);
++ }
+ mutex_unlock(&zr->resource_lock);
+
+ return res;
+ }
+- break;
+
+ case BUZIOC_G_STATUS:
+ {
+ struct zoran_status *bstat = arg;
+- int norm, input, status, res = 0;
++ struct v4l2_routing route = { 0, 0 };
++ int status = 0, res = 0;
++ v4l2_std_id norm;
+
+ dprintk(3, KERN_DEBUG "%s: BUZIOC_G_STATUS\n", ZR_DEVNAME(zr));
+
+@@ -2523,8 +1762,7 @@ static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+ return -EINVAL;
+ }
+
+- input = zr->card.input[bstat->input].muxsel;
+- norm = VIDEO_MODE_AUTO;
++ route.input = zr->card.input[bstat->input].muxsel;
+
+ mutex_lock(&zr->resource_lock);
+
+@@ -2537,1629 +1775,1262 @@ static long zoran_do_ioctl(struct file *file, unsigned int cmd, void *arg)
+ goto gstat_unlock_and_return;
+ }
+
+- decoder_command(zr, DECODER_SET_INPUT, &input);
+- decoder_command(zr, DECODER_SET_NORM, &norm);
++ decoder_call(zr, video, s_routing, &route);
+
+ /* sleep 1 second */
+ ssleep(1);
+
+ /* Get status of video decoder */
+- decoder_command(zr, DECODER_GET_STATUS, &status);
++ decoder_call(zr, video, querystd, &norm);
++ decoder_call(zr, video, g_input_status, &status);
+
+ /* restore previous input and norm */
+- input = zr->card.input[zr->input].muxsel;
+- decoder_command(zr, DECODER_SET_INPUT, &input);
+- decoder_command(zr, DECODER_SET_NORM, &zr->norm);
+- gstat_unlock_and_return:
++ route.input = zr->card.input[zr->input].muxsel;
++ decoder_call(zr, video, s_routing, &route);
++gstat_unlock_and_return:
+ mutex_unlock(&zr->resource_lock);
+
+ if (!res) {
+ bstat->signal =
+- (status & DECODER_STATUS_GOOD) ? 1 : 0;
+- if (status & DECODER_STATUS_NTSC)
++ (status & V4L2_IN_ST_NO_SIGNAL) ? 0 : 1;
++ if (norm & V4L2_STD_NTSC)
+ bstat->norm = VIDEO_MODE_NTSC;
+- else if (status & DECODER_STATUS_SECAM)
++ else if (norm & V4L2_STD_SECAM)
+ bstat->norm = VIDEO_MODE_SECAM;
+ else
+ bstat->norm = VIDEO_MODE_PAL;
+
+ bstat->color =
+- (status & DECODER_STATUS_COLOR) ? 1 : 0;
++ (status & V4L2_IN_ST_NO_COLOR) ? 0 : 1;
+ }
+
+ return res;
+ }
+- break;
+-
+- /* The new video4linux2 capture interface - much nicer than video4linux1, since
+- * it allows for integrating the JPEG capturing calls inside standard v4l2
+- */
+-
+- case VIDIOC_QUERYCAP:
+- {
+- struct v4l2_capability *cap = arg;
+
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_QUERYCAP\n", ZR_DEVNAME(zr));
+-
+- memset(cap, 0, sizeof(*cap));
+- strncpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)-1);
+- strncpy(cap->driver, "zoran", sizeof(cap->driver)-1);
+- snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s",
+- pci_name(zr->pci_dev));
+- cap->version =
+- KERNEL_VERSION(MAJOR_VERSION, MINOR_VERSION,
+- RELEASE_VERSION);
+- cap->capabilities = ZORAN_V4L2_VID_FLAGS;
+-
+- return 0;
++ default:
++ return -EINVAL;
+ }
+- break;
++}
+
+- case VIDIOC_ENUM_FMT:
+- {
+- struct v4l2_fmtdesc *fmt = arg;
+- int index = fmt->index, num = -1, i, flag = 0, type =
+- fmt->type;
++static int zoran_vidiocgmbuf(struct file *file, void *__fh, struct video_mbuf *vmbuf)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ int i, res = 0;
+
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_ENUM_FMT - index=%d\n",
+- ZR_DEVNAME(zr), fmt->index);
+
+- switch (fmt->type) {
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+- flag = ZORAN_FORMAT_CAPTURE;
+- break;
+- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+- flag = ZORAN_FORMAT_PLAYBACK;
+- break;
+- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+- flag = ZORAN_FORMAT_OVERLAY;
+- break;
+- default:
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_ENUM_FMT - unknown type %d\n",
+- ZR_DEVNAME(zr), fmt->type);
+- return -EINVAL;
+- }
++ mutex_lock(&zr->resource_lock);
+
+- for (i = 0; i < NUM_FORMATS; i++) {
+- if (zoran_formats[i].flags & flag)
+- num++;
+- if (num == fmt->index)
+- break;
+- }
+- if (fmt->index < 0 /* late, but not too late */ ||
+- i == NUM_FORMATS)
+- return -EINVAL;
++ if (fh->buffers.allocated) {
++ dprintk(1,
++ KERN_ERR
++ "%s: VIDIOCGMBUF - buffers already allocated\n",
++ ZR_DEVNAME(zr));
++ res = -EINVAL;
++ goto v4l1reqbuf_unlock_and_return;
++ }
+
+- memset(fmt, 0, sizeof(*fmt));
+- fmt->index = index;
+- fmt->type = type;
+- strncpy(fmt->description, zoran_formats[i].name, sizeof(fmt->description)-1);
+- fmt->pixelformat = zoran_formats[i].fourcc;
+- if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED)
+- fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
++ /* The next mmap will map the V4L buffers */
++ map_mode_raw(fh);
+
+- return 0;
++ if (v4l_fbuffer_alloc(fh)) {
++ res = -ENOMEM;
++ goto v4l1reqbuf_unlock_and_return;
+ }
+- break;
+-
+- case VIDIOC_G_FMT:
+- {
+- struct v4l2_format *fmt = arg;
+- int type = fmt->type;
+
+- dprintk(5, KERN_DEBUG "%s: VIDIOC_G_FMT\n", ZR_DEVNAME(zr));
++ vmbuf->size = fh->buffers.num_buffers * fh->buffers.buffer_size;
++ vmbuf->frames = fh->buffers.num_buffers;
++ for (i = 0; i < vmbuf->frames; i++)
++ vmbuf->offsets[i] = i * fh->buffers.buffer_size;
+
+- memset(fmt, 0, sizeof(*fmt));
+- fmt->type = type;
++v4l1reqbuf_unlock_and_return:
++ mutex_unlock(&zr->resource_lock);
+
+- switch (fmt->type) {
+- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
++ return res;
++}
++#endif
+
+- mutex_lock(&zr->resource_lock);
++static int zoran_querycap(struct file *file, void *__fh, struct v4l2_capability *cap)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
+
+- fmt->fmt.win.w.left = fh->overlay_settings.x;
+- fmt->fmt.win.w.top = fh->overlay_settings.y;
+- fmt->fmt.win.w.width = fh->overlay_settings.width;
+- fmt->fmt.win.w.height =
+- fh->overlay_settings.height;
+- if (fh->overlay_settings.width * 2 >
+- BUZ_MAX_HEIGHT)
+- fmt->fmt.win.field = V4L2_FIELD_INTERLACED;
+- else
+- fmt->fmt.win.field = V4L2_FIELD_TOP;
++ memset(cap, 0, sizeof(*cap));
++ strncpy(cap->card, ZR_DEVNAME(zr), sizeof(cap->card)-1);
++ strncpy(cap->driver, "zoran", sizeof(cap->driver)-1);
++ snprintf(cap->bus_info, sizeof(cap->bus_info), "PCI:%s",
++ pci_name(zr->pci_dev));
++ cap->version = KERNEL_VERSION(MAJOR_VERSION, MINOR_VERSION,
++ RELEASE_VERSION);
++ cap->capabilities = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_CAPTURE |
++ V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OVERLAY;
++ return 0;
++}
+
+- mutex_unlock(&zr->resource_lock);
++static int zoran_enum_fmt(struct zoran *zr, struct v4l2_fmtdesc *fmt, int flag)
++{
++ int num = -1, i;
+
++ for (i = 0; i < NUM_FORMATS; i++) {
++ if (zoran_formats[i].flags & flag)
++ num++;
++ if (num == fmt->index)
+ break;
++ }
++ if (fmt->index < 0 /* late, but not too late */ || i == NUM_FORMATS)
++ return -EINVAL;
+
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+-
+- mutex_lock(&zr->resource_lock);
+-
+- if (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
+- fh->map_mode == ZORAN_MAP_MODE_RAW) {
+-
+- fmt->fmt.pix.width =
+- fh->v4l_settings.width;
+- fmt->fmt.pix.height =
+- fh->v4l_settings.height;
+- fmt->fmt.pix.sizeimage =
+- fh->v4l_settings.bytesperline *
+- fh->v4l_settings.height;
+- fmt->fmt.pix.pixelformat =
+- fh->v4l_settings.format->fourcc;
+- fmt->fmt.pix.colorspace =
+- fh->v4l_settings.format->colorspace;
+- fmt->fmt.pix.bytesperline =
+- fh->v4l_settings.bytesperline;
+- if (BUZ_MAX_HEIGHT <
+- (fh->v4l_settings.height * 2))
+- fmt->fmt.pix.field =
+- V4L2_FIELD_INTERLACED;
+- else
+- fmt->fmt.pix.field =
+- V4L2_FIELD_TOP;
+-
+- } else {
+-
+- fmt->fmt.pix.width =
+- fh->jpg_settings.img_width /
+- fh->jpg_settings.HorDcm;
+- fmt->fmt.pix.height =
+- fh->jpg_settings.img_height /
+- (fh->jpg_settings.VerDcm *
+- fh->jpg_settings.TmpDcm);
+- fmt->fmt.pix.sizeimage =
+- zoran_v4l2_calc_bufsize(&fh->
+- jpg_settings);
+- fmt->fmt.pix.pixelformat =
+- V4L2_PIX_FMT_MJPEG;
+- if (fh->jpg_settings.TmpDcm == 1)
+- fmt->fmt.pix.field =
+- (fh->jpg_settings.
+- odd_even ? V4L2_FIELD_SEQ_BT :
+- V4L2_FIELD_SEQ_BT);
+- else
+- fmt->fmt.pix.field =
+- (fh->jpg_settings.
+- odd_even ? V4L2_FIELD_TOP :
+- V4L2_FIELD_BOTTOM);
+-
+- fmt->fmt.pix.bytesperline = 0;
+- fmt->fmt.pix.colorspace =
+- V4L2_COLORSPACE_SMPTE170M;
+- }
+-
+- mutex_unlock(&zr->resource_lock);
++ strncpy(fmt->description, zoran_formats[i].name, sizeof(fmt->description)-1);
++ fmt->pixelformat = zoran_formats[i].fourcc;
++ if (zoran_formats[i].flags & ZORAN_FORMAT_COMPRESSED)
++ fmt->flags |= V4L2_FMT_FLAG_COMPRESSED;
++ return 0;
++}
+
+- break;
++static int zoran_enum_fmt_vid_cap(struct file *file, void *__fh,
++ struct v4l2_fmtdesc *f)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
+
+- default:
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_G_FMT - unsupported type %d\n",
+- ZR_DEVNAME(zr), fmt->type);
+- return -EINVAL;
+- }
+- return 0;
+- }
+- break;
++ return zoran_enum_fmt(zr, f, ZORAN_FORMAT_CAPTURE);
++}
+
+- case VIDIOC_S_FMT:
+- {
+- struct v4l2_format *fmt = arg;
+- int i, res = 0;
+- __le32 printformat;
+-
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_S_FMT - type=%d, ",
+- ZR_DEVNAME(zr), fmt->type);
+-
+- switch (fmt->type) {
+- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+-
+- dprintk(3, "x=%d, y=%d, w=%d, h=%d, cnt=%d, map=0x%p\n",
+- fmt->fmt.win.w.left, fmt->fmt.win.w.top,
+- fmt->fmt.win.w.width,
+- fmt->fmt.win.w.height,
+- fmt->fmt.win.clipcount,
+- fmt->fmt.win.bitmap);
+- mutex_lock(&zr->resource_lock);
+- res =
+- setup_window(file, fmt->fmt.win.w.left,
+- fmt->fmt.win.w.top,
+- fmt->fmt.win.w.width,
+- fmt->fmt.win.w.height,
+- (struct video_clip __user *)
+- fmt->fmt.win.clips,
+- fmt->fmt.win.clipcount,
+- fmt->fmt.win.bitmap);
+- mutex_unlock(&zr->resource_lock);
+- return res;
+- break;
++static int zoran_enum_fmt_vid_out(struct file *file, void *__fh,
++ struct v4l2_fmtdesc *f)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
+
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+-
+- printformat =
+- __cpu_to_le32(fmt->fmt.pix.pixelformat);
+- dprintk(3, "size=%dx%d, fmt=0x%x (%4.4s)\n",
+- fmt->fmt.pix.width, fmt->fmt.pix.height,
+- fmt->fmt.pix.pixelformat,
+- (char *) &printformat);
+-
+- /* we can be requested to do JPEG/raw playback/capture */
+- if (!
+- (fmt->type == V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+- (fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+- fmt->fmt.pix.pixelformat ==
+- V4L2_PIX_FMT_MJPEG))) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_S_FMT - unknown type %d/0x%x(%4.4s) combination\n",
+- ZR_DEVNAME(zr), fmt->type,
+- fmt->fmt.pix.pixelformat,
+- (char *) &printformat);
+- return -EINVAL;
+- }
++ return zoran_enum_fmt(zr, f, ZORAN_FORMAT_PLAYBACK);
++}
+
+- if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) {
+- mutex_lock(&zr->resource_lock);
++static int zoran_enum_fmt_vid_overlay(struct file *file, void *__fh,
++ struct v4l2_fmtdesc *f)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
+
+- settings = fh->jpg_settings;
++ return zoran_enum_fmt(zr, f, ZORAN_FORMAT_OVERLAY);
++}
+
+- if (fh->v4l_buffers.allocated ||
+- fh->jpg_buffers.allocated) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_S_FMT - cannot change capture mode\n",
+- ZR_DEVNAME(zr));
+- res = -EBUSY;
+- goto sfmtjpg_unlock_and_return;
+- }
++static int zoran_g_fmt_vid_out(struct file *file, void *__fh,
++ struct v4l2_format *fmt)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
+
+- /* we actually need to set 'real' parameters now */
+- if ((fmt->fmt.pix.height * 2) >
+- BUZ_MAX_HEIGHT)
+- settings.TmpDcm = 1;
+- else
+- settings.TmpDcm = 2;
+- settings.decimation = 0;
+- if (fmt->fmt.pix.height <=
+- fh->jpg_settings.img_height / 2)
+- settings.VerDcm = 2;
+- else
+- settings.VerDcm = 1;
+- if (fmt->fmt.pix.width <=
+- fh->jpg_settings.img_width / 4)
+- settings.HorDcm = 4;
+- else if (fmt->fmt.pix.width <=
+- fh->jpg_settings.img_width / 2)
+- settings.HorDcm = 2;
+- else
+- settings.HorDcm = 1;
+- if (settings.TmpDcm == 1)
+- settings.field_per_buff = 2;
+- else
+- settings.field_per_buff = 1;
+-
+- /* check */
+- if ((res =
+- zoran_check_jpg_settings(zr,
+- &settings)))
+- goto sfmtjpg_unlock_and_return;
+-
+- /* it's ok, so set them */
+- fh->jpg_settings = settings;
+-
+- /* tell the user what we actually did */
+- fmt->fmt.pix.width =
+- settings.img_width / settings.HorDcm;
+- fmt->fmt.pix.height =
+- settings.img_height * 2 /
+- (settings.TmpDcm * settings.VerDcm);
+- if (settings.TmpDcm == 1)
+- fmt->fmt.pix.field =
+- (fh->jpg_settings.
+- odd_even ? V4L2_FIELD_SEQ_TB :
+- V4L2_FIELD_SEQ_BT);
+- else
+- fmt->fmt.pix.field =
+- (fh->jpg_settings.
+- odd_even ? V4L2_FIELD_TOP :
+- V4L2_FIELD_BOTTOM);
+- fh->jpg_buffers.buffer_size =
+- zoran_v4l2_calc_bufsize(&fh->
+- jpg_settings);
+- fmt->fmt.pix.bytesperline = 0;
+- fmt->fmt.pix.sizeimage =
+- fh->jpg_buffers.buffer_size;
+- fmt->fmt.pix.colorspace =
+- V4L2_COLORSPACE_SMPTE170M;
+-
+- /* we hereby abuse this variable to show that
+- * we're gonna do mjpeg capture */
+- fh->map_mode =
+- (fmt->type ==
+- V4L2_BUF_TYPE_VIDEO_CAPTURE) ?
+- ZORAN_MAP_MODE_JPG_REC :
+- ZORAN_MAP_MODE_JPG_PLAY;
+- sfmtjpg_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
+- } else {
+- for (i = 0; i < NUM_FORMATS; i++)
+- if (fmt->fmt.pix.pixelformat ==
+- zoran_formats[i].fourcc)
+- break;
+- if (i == NUM_FORMATS) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_S_FMT - unknown/unsupported format 0x%x (%4.4s)\n",
+- ZR_DEVNAME(zr),
+- fmt->fmt.pix.pixelformat,
+- (char *) &printformat);
+- return -EINVAL;
+- }
+- mutex_lock(&zr->resource_lock);
+- if (fh->jpg_buffers.allocated ||
+- (fh->v4l_buffers.allocated &&
+- fh->v4l_buffers.active !=
+- ZORAN_FREE)) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_S_FMT - cannot change capture mode\n",
+- ZR_DEVNAME(zr));
+- res = -EBUSY;
+- goto sfmtv4l_unlock_and_return;
+- }
+- if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT)
+- fmt->fmt.pix.height =
+- BUZ_MAX_HEIGHT;
+- if (fmt->fmt.pix.width > BUZ_MAX_WIDTH)
+- fmt->fmt.pix.width = BUZ_MAX_WIDTH;
+-
+- if ((res =
+- zoran_v4l_set_format(file,
+- fmt->fmt.pix.
+- width,
+- fmt->fmt.pix.
+- height,
+- &zoran_formats
+- [i])))
+- goto sfmtv4l_unlock_and_return;
+-
+- /* tell the user the
+- * results/missing stuff */
+- fmt->fmt.pix.bytesperline =
+- fh->v4l_settings.bytesperline;
+- fmt->fmt.pix.sizeimage =
+- fh->v4l_settings.height *
+- fh->v4l_settings.bytesperline;
+- fmt->fmt.pix.colorspace =
+- fh->v4l_settings.format->colorspace;
+- if (BUZ_MAX_HEIGHT <
+- (fh->v4l_settings.height * 2))
+- fmt->fmt.pix.field =
+- V4L2_FIELD_INTERLACED;
+- else
+- fmt->fmt.pix.field =
+- V4L2_FIELD_TOP;
+-
+- fh->map_mode = ZORAN_MAP_MODE_RAW;
+- sfmtv4l_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
+- }
++ mutex_lock(&zr->resource_lock);
+
+- break;
++ fmt->fmt.pix.width = fh->jpg_settings.img_width / fh->jpg_settings.HorDcm;
++ fmt->fmt.pix.height = fh->jpg_settings.img_height * 2 /
++ (fh->jpg_settings.VerDcm * fh->jpg_settings.TmpDcm);
++ fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&fh->jpg_settings);
++ fmt->fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
++ if (fh->jpg_settings.TmpDcm == 1)
++ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
++ V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT);
++ else
++ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
++ V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
++ fmt->fmt.pix.bytesperline = 0;
++ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+- default:
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_S_FMT - unsupported type %d\n",
+- ZR_DEVNAME(zr), fmt->type);
+- return -EINVAL;
+- }
++ mutex_unlock(&zr->resource_lock);
++ return 0;
++}
+
+- return res;
+- }
+- break;
++static int zoran_g_fmt_vid_cap(struct file *file, void *__fh,
++ struct v4l2_format *fmt)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
+
+- case VIDIOC_G_FBUF:
+- {
+- struct v4l2_framebuffer *fb = arg;
++ if (fh->map_mode != ZORAN_MAP_MODE_RAW)
++ return zoran_g_fmt_vid_out(file, fh, fmt);
+
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_G_FBUF\n", ZR_DEVNAME(zr));
++ mutex_lock(&zr->resource_lock);
++ fmt->fmt.pix.width = fh->v4l_settings.width;
++ fmt->fmt.pix.height = fh->v4l_settings.height;
++ fmt->fmt.pix.sizeimage = fh->v4l_settings.bytesperline *
++ fh->v4l_settings.height;
++ fmt->fmt.pix.pixelformat = fh->v4l_settings.format->fourcc;
++ fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace;
++ fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline;
++ if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2))
++ fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
++ else
++ fmt->fmt.pix.field = V4L2_FIELD_TOP;
++ mutex_unlock(&zr->resource_lock);
++ return 0;
++}
+
+- memset(fb, 0, sizeof(*fb));
+- mutex_lock(&zr->resource_lock);
+- fb->base = zr->buffer.base;
+- fb->fmt.width = zr->buffer.width;
+- fb->fmt.height = zr->buffer.height;
+- if (zr->overlay_settings.format) {
+- fb->fmt.pixelformat =
+- fh->overlay_settings.format->fourcc;
+- }
+- fb->fmt.bytesperline = zr->buffer.bytesperline;
+- mutex_unlock(&zr->resource_lock);
+- fb->fmt.colorspace = V4L2_COLORSPACE_SRGB;
+- fb->fmt.field = V4L2_FIELD_INTERLACED;
+- fb->flags = V4L2_FBUF_FLAG_OVERLAY;
+- fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
++static int zoran_g_fmt_vid_overlay(struct file *file, void *__fh,
++ struct v4l2_format *fmt)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
+
+- return 0;
+- }
+- break;
++ mutex_lock(&zr->resource_lock);
+
+- case VIDIOC_S_FBUF:
+- {
+- int i, res = 0;
+- struct v4l2_framebuffer *fb = arg;
+- __le32 printformat = __cpu_to_le32(fb->fmt.pixelformat);
++ fmt->fmt.win.w.left = fh->overlay_settings.x;
++ fmt->fmt.win.w.top = fh->overlay_settings.y;
++ fmt->fmt.win.w.width = fh->overlay_settings.width;
++ fmt->fmt.win.w.height = fh->overlay_settings.height;
++ if (fh->overlay_settings.width * 2 > BUZ_MAX_HEIGHT)
++ fmt->fmt.win.field = V4L2_FIELD_INTERLACED;
++ else
++ fmt->fmt.win.field = V4L2_FIELD_TOP;
+
+- dprintk(3,
+- KERN_DEBUG
+- "%s: VIDIOC_S_FBUF - base=0x%p, size=%dx%d, bpl=%d, fmt=0x%x (%4.4s)\n",
+- ZR_DEVNAME(zr), fb->base, fb->fmt.width, fb->fmt.height,
+- fb->fmt.bytesperline, fb->fmt.pixelformat,
+- (char *) &printformat);
++ mutex_unlock(&zr->resource_lock);
++ return 0;
++}
+
+- for (i = 0; i < NUM_FORMATS; i++)
+- if (zoran_formats[i].fourcc == fb->fmt.pixelformat)
+- break;
+- if (i == NUM_FORMATS) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_S_FBUF - format=0x%x (%4.4s) not allowed\n",
+- ZR_DEVNAME(zr), fb->fmt.pixelformat,
+- (char *) &printformat);
+- return -EINVAL;
+- }
++static int zoran_try_fmt_vid_overlay(struct file *file, void *__fh,
++ struct v4l2_format *fmt)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
+
+- mutex_lock(&zr->resource_lock);
+- res =
+- setup_fbuffer(file, fb->base, &zoran_formats[i],
+- fb->fmt.width, fb->fmt.height,
+- fb->fmt.bytesperline);
+- mutex_unlock(&zr->resource_lock);
++ mutex_lock(&zr->resource_lock);
+
+- return res;
+- }
+- break;
++ if (fmt->fmt.win.w.width > BUZ_MAX_WIDTH)
++ fmt->fmt.win.w.width = BUZ_MAX_WIDTH;
++ if (fmt->fmt.win.w.width < BUZ_MIN_WIDTH)
++ fmt->fmt.win.w.width = BUZ_MIN_WIDTH;
++ if (fmt->fmt.win.w.height > BUZ_MAX_HEIGHT)
++ fmt->fmt.win.w.height = BUZ_MAX_HEIGHT;
++ if (fmt->fmt.win.w.height < BUZ_MIN_HEIGHT)
++ fmt->fmt.win.w.height = BUZ_MIN_HEIGHT;
+
+- case VIDIOC_OVERLAY:
+- {
+- int *on = arg, res;
++ mutex_unlock(&zr->resource_lock);
++ return 0;
++}
+
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_PREVIEW - on=%d\n",
+- ZR_DEVNAME(zr), *on);
++static int zoran_try_fmt_vid_out(struct file *file, void *__fh,
++ struct v4l2_format *fmt)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ struct zoran_jpg_settings settings;
++ int res = 0;
+
+- mutex_lock(&zr->resource_lock);
+- res = setup_overlay(file, *on);
+- mutex_unlock(&zr->resource_lock);
++ if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
++ return -EINVAL;
+
+- return res;
+- }
+- break;
++ mutex_lock(&zr->resource_lock);
++ settings = fh->jpg_settings;
+
+- case VIDIOC_REQBUFS:
+- {
+- struct v4l2_requestbuffers *req = arg;
+- int res = 0;
++ /* we actually need to set 'real' parameters now */
++ if ((fmt->fmt.pix.height * 2) > BUZ_MAX_HEIGHT)
++ settings.TmpDcm = 1;
++ else
++ settings.TmpDcm = 2;
++ settings.decimation = 0;
++ if (fmt->fmt.pix.height <= fh->jpg_settings.img_height / 2)
++ settings.VerDcm = 2;
++ else
++ settings.VerDcm = 1;
++ if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 4)
++ settings.HorDcm = 4;
++ else if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 2)
++ settings.HorDcm = 2;
++ else
++ settings.HorDcm = 1;
++ if (settings.TmpDcm == 1)
++ settings.field_per_buff = 2;
++ else
++ settings.field_per_buff = 1;
+
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_REQBUFS - type=%d\n",
+- ZR_DEVNAME(zr), req->type);
++ if (settings.HorDcm > 1) {
++ settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
++ settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
++ } else {
++ settings.img_x = 0;
++ settings.img_width = BUZ_MAX_WIDTH;
++ }
++
++ /* check */
++ res = zoran_check_jpg_settings(zr, &settings, 1);
++ if (res)
++ goto tryfmt_unlock_and_return;
++
++ /* tell the user what we actually did */
++ fmt->fmt.pix.width = settings.img_width / settings.HorDcm;
++ fmt->fmt.pix.height = settings.img_height * 2 /
++ (settings.TmpDcm * settings.VerDcm);
++ if (settings.TmpDcm == 1)
++ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
++ V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT);
++ else
++ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
++ V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
+
+- if (req->memory != V4L2_MEMORY_MMAP) {
+- dprintk(1,
+- KERN_ERR
+- "%s: only MEMORY_MMAP capture is supported, not %d\n",
+- ZR_DEVNAME(zr), req->memory);
+- return -EINVAL;
+- }
++ fmt->fmt.pix.sizeimage = zoran_v4l2_calc_bufsize(&settings);
++ fmt->fmt.pix.bytesperline = 0;
++ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
++tryfmt_unlock_and_return:
++ mutex_unlock(&zr->resource_lock);
++ return res;
++}
+
+- mutex_lock(&zr->resource_lock);
++static int zoran_try_fmt_vid_cap(struct file *file, void *__fh,
++ struct v4l2_format *fmt)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ int bpp;
++ int i;
+
+- if (fh->v4l_buffers.allocated || fh->jpg_buffers.allocated) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_REQBUFS - buffers allready allocated\n",
+- ZR_DEVNAME(zr));
+- res = -EBUSY;
+- goto v4l2reqbuf_unlock_and_return;
+- }
++ if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)
++ return zoran_try_fmt_vid_out(file, fh, fmt);
+
+- if (fh->map_mode == ZORAN_MAP_MODE_RAW &&
+- req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
++ mutex_lock(&zr->resource_lock);
+
+- /* control user input */
+- if (req->count < 2)
+- req->count = 2;
+- if (req->count > v4l_nbufs)
+- req->count = v4l_nbufs;
+- fh->v4l_buffers.num_buffers = req->count;
++ for (i = 0; i < NUM_FORMATS; i++)
++ if (zoran_formats[i].fourcc == fmt->fmt.pix.pixelformat)
++ break;
+
+- if (v4l_fbuffer_alloc(file)) {
+- res = -ENOMEM;
+- goto v4l2reqbuf_unlock_and_return;
+- }
++ if (i == NUM_FORMATS) {
++ mutex_unlock(&zr->resource_lock);
++ return -EINVAL;
++ }
+
+- /* The next mmap will map the V4L buffers */
+- fh->map_mode = ZORAN_MAP_MODE_RAW;
++ bpp = (zoran_formats[i].depth + 7) / 8;
++ fmt->fmt.pix.width &= ~((bpp == 2) ? 1 : 3);
++ if (fmt->fmt.pix.width > BUZ_MAX_WIDTH)
++ fmt->fmt.pix.width = BUZ_MAX_WIDTH;
++ if (fmt->fmt.pix.width < BUZ_MIN_WIDTH)
++ fmt->fmt.pix.width = BUZ_MIN_WIDTH;
++ if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT)
++ fmt->fmt.pix.height = BUZ_MAX_HEIGHT;
++ if (fmt->fmt.pix.height < BUZ_MIN_HEIGHT)
++ fmt->fmt.pix.height = BUZ_MIN_HEIGHT;
++ mutex_unlock(&zr->resource_lock);
+
+- } else if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC ||
+- fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) {
++ return 0;
++}
+
+- /* we need to calculate size ourselves now */
+- if (req->count < 4)
+- req->count = 4;
+- if (req->count > jpg_nbufs)
+- req->count = jpg_nbufs;
+- fh->jpg_buffers.num_buffers = req->count;
+- fh->jpg_buffers.buffer_size =
+- zoran_v4l2_calc_bufsize(&fh->jpg_settings);
++static int zoran_s_fmt_vid_overlay(struct file *file, void *__fh,
++ struct v4l2_format *fmt)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ int res;
++
++ dprintk(3, "x=%d, y=%d, w=%d, h=%d, cnt=%d, map=0x%p\n",
++ fmt->fmt.win.w.left, fmt->fmt.win.w.top,
++ fmt->fmt.win.w.width,
++ fmt->fmt.win.w.height,
++ fmt->fmt.win.clipcount,
++ fmt->fmt.win.bitmap);
++ mutex_lock(&zr->resource_lock);
++ res = setup_window(fh, fmt->fmt.win.w.left, fmt->fmt.win.w.top,
++ fmt->fmt.win.w.width, fmt->fmt.win.w.height,
++ (struct v4l2_clip __user *)fmt->fmt.win.clips,
++ fmt->fmt.win.clipcount, fmt->fmt.win.bitmap);
++ mutex_unlock(&zr->resource_lock);
++ return res;
++}
+
+- if (jpg_fbuffer_alloc(file)) {
+- res = -ENOMEM;
+- goto v4l2reqbuf_unlock_and_return;
+- }
++static int zoran_s_fmt_vid_out(struct file *file, void *__fh,
++ struct v4l2_format *fmt)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ __le32 printformat = __cpu_to_le32(fmt->fmt.pix.pixelformat);
++ struct zoran_jpg_settings settings;
++ int res = 0;
+
+- /* The next mmap will map the MJPEG buffers */
+- if (req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- fh->map_mode = ZORAN_MAP_MODE_JPG_REC;
+- else
+- fh->map_mode = ZORAN_MAP_MODE_JPG_PLAY;
++ dprintk(3, "size=%dx%d, fmt=0x%x (%4.4s)\n",
++ fmt->fmt.pix.width, fmt->fmt.pix.height,
++ fmt->fmt.pix.pixelformat,
++ (char *) &printformat);
++ if (fmt->fmt.pix.pixelformat != V4L2_PIX_FMT_MJPEG)
++ return -EINVAL;
+
+- } else {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_REQBUFS - unknown type %d\n",
+- ZR_DEVNAME(zr), req->type);
+- res = -EINVAL;
+- goto v4l2reqbuf_unlock_and_return;
+- }
+- v4l2reqbuf_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
++ mutex_lock(&zr->resource_lock);
+
+- return 0;
++ if (fh->buffers.allocated) {
++ dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n",
++ ZR_DEVNAME(zr));
++ res = -EBUSY;
++ goto sfmtjpg_unlock_and_return;
+ }
+- break;
+
+- case VIDIOC_QUERYBUF:
+- {
+- struct v4l2_buffer *buf = arg;
+- __u32 type = buf->type;
+- int index = buf->index, res;
++ settings = fh->jpg_settings;
+
+- dprintk(3,
+- KERN_DEBUG
+- "%s: VIDIOC_QUERYBUF - index=%d, type=%d\n",
+- ZR_DEVNAME(zr), buf->index, buf->type);
+-
+- memset(buf, 0, sizeof(*buf));
+- buf->type = type;
+- buf->index = index;
+-
+- mutex_lock(&zr->resource_lock);
+- res = zoran_v4l2_buffer_status(file, buf, buf->index);
+- mutex_unlock(&zr->resource_lock);
++ /* we actually need to set 'real' parameters now */
++ if (fmt->fmt.pix.height * 2 > BUZ_MAX_HEIGHT)
++ settings.TmpDcm = 1;
++ else
++ settings.TmpDcm = 2;
++ settings.decimation = 0;
++ if (fmt->fmt.pix.height <= fh->jpg_settings.img_height / 2)
++ settings.VerDcm = 2;
++ else
++ settings.VerDcm = 1;
++ if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 4)
++ settings.HorDcm = 4;
++ else if (fmt->fmt.pix.width <= fh->jpg_settings.img_width / 2)
++ settings.HorDcm = 2;
++ else
++ settings.HorDcm = 1;
++ if (settings.TmpDcm == 1)
++ settings.field_per_buff = 2;
++ else
++ settings.field_per_buff = 1;
+
+- return res;
++ if (settings.HorDcm > 1) {
++ settings.img_x = (BUZ_MAX_WIDTH == 720) ? 8 : 0;
++ settings.img_width = (BUZ_MAX_WIDTH == 720) ? 704 : BUZ_MAX_WIDTH;
++ } else {
++ settings.img_x = 0;
++ settings.img_width = BUZ_MAX_WIDTH;
+ }
+- break;
+
+- case VIDIOC_QBUF:
+- {
+- struct v4l2_buffer *buf = arg;
+- int res = 0, codec_mode, buf_type;
++ /* check */
++ res = zoran_check_jpg_settings(zr, &settings, 0);
++ if (res)
++ goto sfmtjpg_unlock_and_return;
+
+- dprintk(3,
+- KERN_DEBUG "%s: VIDIOC_QBUF - type=%d, index=%d\n",
+- ZR_DEVNAME(zr), buf->type, buf->index);
++ /* it's ok, so set them */
++ fh->jpg_settings = settings;
+
+- mutex_lock(&zr->resource_lock);
++ map_mode_jpg(fh, fmt->type == V4L2_BUF_TYPE_VIDEO_OUTPUT);
++ fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings);
+
+- switch (fh->map_mode) {
+- case ZORAN_MAP_MODE_RAW:
+- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+- ZR_DEVNAME(zr), buf->type, fh->map_mode);
+- res = -EINVAL;
+- goto qbuf_unlock_and_return;
+- }
++ /* tell the user what we actually did */
++ fmt->fmt.pix.width = settings.img_width / settings.HorDcm;
++ fmt->fmt.pix.height = settings.img_height * 2 /
++ (settings.TmpDcm * settings.VerDcm);
++ if (settings.TmpDcm == 1)
++ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
++ V4L2_FIELD_SEQ_TB : V4L2_FIELD_SEQ_BT);
++ else
++ fmt->fmt.pix.field = (fh->jpg_settings.odd_even ?
++ V4L2_FIELD_TOP : V4L2_FIELD_BOTTOM);
++ fmt->fmt.pix.bytesperline = 0;
++ fmt->fmt.pix.sizeimage = fh->buffers.buffer_size;
++ fmt->fmt.pix.colorspace = V4L2_COLORSPACE_SMPTE170M;
+
+- res = zoran_v4l_queue_frame(file, buf->index);
+- if (res)
+- goto qbuf_unlock_and_return;
+- if (!zr->v4l_memgrab_active &&
+- fh->v4l_buffers.active == ZORAN_LOCKED)
+- zr36057_set_memgrab(zr, 1);
+- break;
++sfmtjpg_unlock_and_return:
++ mutex_unlock(&zr->resource_lock);
++ return res;
++}
+
+- case ZORAN_MAP_MODE_JPG_REC:
+- case ZORAN_MAP_MODE_JPG_PLAY:
+- if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) {
+- buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+- codec_mode = BUZ_MODE_MOTION_DECOMPRESS;
+- } else {
+- buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+- codec_mode = BUZ_MODE_MOTION_COMPRESS;
+- }
++static int zoran_s_fmt_vid_cap(struct file *file, void *__fh,
++ struct v4l2_format *fmt)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ int i;
++ int res = 0;
+
+- if (buf->type != buf_type) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+- ZR_DEVNAME(zr), buf->type, fh->map_mode);
+- res = -EINVAL;
+- goto qbuf_unlock_and_return;
+- }
++ if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG)
++ return zoran_s_fmt_vid_out(file, fh, fmt);
+
+- res =
+- zoran_jpg_queue_frame(file, buf->index,
+- codec_mode);
+- if (res != 0)
+- goto qbuf_unlock_and_return;
+- if (zr->codec_mode == BUZ_MODE_IDLE &&
+- fh->jpg_buffers.active == ZORAN_LOCKED) {
+- zr36057_enable_jpg(zr, codec_mode);
+- }
++ for (i = 0; i < NUM_FORMATS; i++)
++ if (fmt->fmt.pix.pixelformat == zoran_formats[i].fourcc)
+ break;
+-
+- default:
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_QBUF - unsupported type %d\n",
+- ZR_DEVNAME(zr), buf->type);
+- res = -EINVAL;
+- goto qbuf_unlock_and_return;
+- }
+- qbuf_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
+-
+- return res;
++ if (i == NUM_FORMATS) {
++ dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - unknown/unsupported format 0x%x\n",
++ ZR_DEVNAME(zr), fmt->fmt.pix.pixelformat);
++ return -EINVAL;
+ }
+- break;
+
+- case VIDIOC_DQBUF:
+- {
+- struct v4l2_buffer *buf = arg;
+- int res = 0, buf_type, num = -1; /* compiler borks here (?) */
+-
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_DQBUF - type=%d\n",
+- ZR_DEVNAME(zr), buf->type);
+-
+- mutex_lock(&zr->resource_lock);
++ mutex_lock(&zr->resource_lock);
+
+- switch (fh->map_mode) {
+- case ZORAN_MAP_MODE_RAW:
+- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+- ZR_DEVNAME(zr), buf->type, fh->map_mode);
+- res = -EINVAL;
+- goto dqbuf_unlock_and_return;
+- }
++ if ((fh->map_mode != ZORAN_MAP_MODE_RAW && fh->buffers.allocated) ||
++ fh->buffers.active != ZORAN_FREE) {
++ dprintk(1, KERN_ERR "%s: VIDIOC_S_FMT - cannot change capture mode\n",
++ ZR_DEVNAME(zr));
++ res = -EBUSY;
++ goto sfmtv4l_unlock_and_return;
++ }
++ if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT)
++ fmt->fmt.pix.height = BUZ_MAX_HEIGHT;
++ if (fmt->fmt.pix.width > BUZ_MAX_WIDTH)
++ fmt->fmt.pix.width = BUZ_MAX_WIDTH;
++
++ map_mode_raw(fh);
++
++ res = zoran_v4l_set_format(fh, fmt->fmt.pix.width, fmt->fmt.pix.height,
++ &zoran_formats[i]);
++ if (res)
++ goto sfmtv4l_unlock_and_return;
++
++ /* tell the user the results/missing stuff */
++ fmt->fmt.pix.bytesperline = fh->v4l_settings.bytesperline;
++ fmt->fmt.pix.sizeimage = fh->v4l_settings.height * fh->v4l_settings.bytesperline;
++ fmt->fmt.pix.colorspace = fh->v4l_settings.format->colorspace;
++ if (BUZ_MAX_HEIGHT < (fh->v4l_settings.height * 2))
++ fmt->fmt.pix.field = V4L2_FIELD_INTERLACED;
++ else
++ fmt->fmt.pix.field = V4L2_FIELD_TOP;
+
+- num = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME];
+- if (file->f_flags & O_NONBLOCK &&
+- zr->v4l_buffers.buffer[num].state !=
+- BUZ_STATE_DONE) {
+- res = -EAGAIN;
+- goto dqbuf_unlock_and_return;
+- }
+- res = v4l_sync(file, num);
+- if (res)
+- goto dqbuf_unlock_and_return;
+- else
+- zr->v4l_sync_tail++;
+- res = zoran_v4l2_buffer_status(file, buf, num);
+- break;
++sfmtv4l_unlock_and_return:
++ mutex_unlock(&zr->resource_lock);
++ return res;
++}
+
+- case ZORAN_MAP_MODE_JPG_REC:
+- case ZORAN_MAP_MODE_JPG_PLAY:
+- {
+- struct zoran_sync bs;
++static int zoran_g_fbuf(struct file *file, void *__fh,
++ struct v4l2_framebuffer *fb)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
+
+- if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY)
+- buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
+- else
+- buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ memset(fb, 0, sizeof(*fb));
++ mutex_lock(&zr->resource_lock);
++ fb->base = zr->vbuf_base;
++ fb->fmt.width = zr->vbuf_width;
++ fb->fmt.height = zr->vbuf_height;
++ if (zr->overlay_settings.format)
++ fb->fmt.pixelformat = fh->overlay_settings.format->fourcc;
++ fb->fmt.bytesperline = zr->vbuf_bytesperline;
++ mutex_unlock(&zr->resource_lock);
++ fb->fmt.colorspace = V4L2_COLORSPACE_SRGB;
++ fb->fmt.field = V4L2_FIELD_INTERLACED;
++ fb->flags = V4L2_FBUF_FLAG_OVERLAY;
++ fb->capability = V4L2_FBUF_CAP_LIST_CLIPPING;
+
+- if (buf->type != buf_type) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
+- ZR_DEVNAME(zr), buf->type, fh->map_mode);
+- res = -EINVAL;
+- goto dqbuf_unlock_and_return;
+- }
++ return 0;
++}
+
+- num =
+- zr->jpg_pend[zr->
+- jpg_que_tail & BUZ_MASK_FRAME];
++static int zoran_s_fbuf(struct file *file, void *__fh,
++ struct v4l2_framebuffer *fb)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ int i, res = 0;
++ __le32 printformat = __cpu_to_le32(fb->fmt.pixelformat);
+
+- if (file->f_flags & O_NONBLOCK &&
+- zr->jpg_buffers.buffer[num].state !=
+- BUZ_STATE_DONE) {
+- res = -EAGAIN;
+- goto dqbuf_unlock_and_return;
+- }
+- res = jpg_sync(file, &bs);
+- if (res)
+- goto dqbuf_unlock_and_return;
+- res =
+- zoran_v4l2_buffer_status(file, buf, bs.frame);
++ for (i = 0; i < NUM_FORMATS; i++)
++ if (zoran_formats[i].fourcc == fb->fmt.pixelformat)
+ break;
+- }
+-
+- default:
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_DQBUF - unsupported type %d\n",
+- ZR_DEVNAME(zr), buf->type);
+- res = -EINVAL;
+- goto dqbuf_unlock_and_return;
+- }
+- dqbuf_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
+-
+- return res;
++ if (i == NUM_FORMATS) {
++ dprintk(1, KERN_ERR "%s: VIDIOC_S_FBUF - format=0x%x (%4.4s) not allowed\n",
++ ZR_DEVNAME(zr), fb->fmt.pixelformat,
++ (char *)&printformat);
++ return -EINVAL;
+ }
+- break;
+
+- case VIDIOC_STREAMON:
+- {
+- int res = 0;
++ mutex_lock(&zr->resource_lock);
++ res = setup_fbuffer(fh, fb->base, &zoran_formats[i], fb->fmt.width,
++ fb->fmt.height, fb->fmt.bytesperline);
++ mutex_unlock(&zr->resource_lock);
+
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_STREAMON\n", ZR_DEVNAME(zr));
++ return res;
++}
+
+- mutex_lock(&zr->resource_lock);
++static int zoran_overlay(struct file *file, void *__fh, unsigned int on)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ int res;
+
+- switch (fh->map_mode) {
+- case ZORAN_MAP_MODE_RAW: /* raw capture */
+- if (zr->v4l_buffers.active != ZORAN_ACTIVE ||
+- fh->v4l_buffers.active != ZORAN_ACTIVE) {
+- res = -EBUSY;
+- goto strmon_unlock_and_return;
+- }
++ mutex_lock(&zr->resource_lock);
++ res = setup_overlay(fh, on);
++ mutex_unlock(&zr->resource_lock);
+
+- zr->v4l_buffers.active = fh->v4l_buffers.active =
+- ZORAN_LOCKED;
+- zr->v4l_settings = fh->v4l_settings;
++ return res;
++}
+
+- zr->v4l_sync_tail = zr->v4l_pend_tail;
+- if (!zr->v4l_memgrab_active &&
+- zr->v4l_pend_head != zr->v4l_pend_tail) {
+- zr36057_set_memgrab(zr, 1);
+- }
+- break;
++static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type);
+
+- case ZORAN_MAP_MODE_JPG_REC:
+- case ZORAN_MAP_MODE_JPG_PLAY:
+- /* what is the codec mode right now? */
+- if (zr->jpg_buffers.active != ZORAN_ACTIVE ||
+- fh->jpg_buffers.active != ZORAN_ACTIVE) {
+- res = -EBUSY;
+- goto strmon_unlock_and_return;
+- }
++static int zoran_reqbufs(struct file *file, void *__fh, struct v4l2_requestbuffers *req)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ int res = 0;
+
+- zr->jpg_buffers.active = fh->jpg_buffers.active =
+- ZORAN_LOCKED;
++ if (req->memory != V4L2_MEMORY_MMAP) {
++ dprintk(2,
++ KERN_ERR
++ "%s: only MEMORY_MMAP capture is supported, not %d\n",
++ ZR_DEVNAME(zr), req->memory);
++ return -EINVAL;
++ }
+
+- if (zr->jpg_que_head != zr->jpg_que_tail) {
+- /* Start the jpeg codec when the first frame is queued */
+- jpeg_start(zr);
+- }
++ if (req->count == 0)
++ return zoran_streamoff(file, fh, req->type);
+
+- break;
+- default:
+- dprintk(1,
++ mutex_lock(&zr->resource_lock);
++ if (fh->buffers.allocated) {
++ dprintk(2,
+ KERN_ERR
+- "%s: VIDIOC_STREAMON - invalid map mode %d\n",
+- ZR_DEVNAME(zr), fh->map_mode);
+- res = -EINVAL;
+- goto strmon_unlock_and_return;
+- }
+- strmon_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
+-
+- return res;
++ "%s: VIDIOC_REQBUFS - buffers already allocated\n",
++ ZR_DEVNAME(zr));
++ res = -EBUSY;
++ goto v4l2reqbuf_unlock_and_return;
+ }
+- break;
+
+- case VIDIOC_STREAMOFF:
+- {
+- int i, res = 0;
++ if (fh->map_mode == ZORAN_MAP_MODE_RAW &&
++ req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
++ /* control user input */
++ if (req->count < 2)
++ req->count = 2;
++ if (req->count > v4l_nbufs)
++ req->count = v4l_nbufs;
+
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_STREAMOFF\n", ZR_DEVNAME(zr));
++ /* The next mmap will map the V4L buffers */
++ map_mode_raw(fh);
++ fh->buffers.num_buffers = req->count;
+
+- mutex_lock(&zr->resource_lock);
++ if (v4l_fbuffer_alloc(fh)) {
++ res = -ENOMEM;
++ goto v4l2reqbuf_unlock_and_return;
++ }
++ } else if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC ||
++ fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) {
++ /* we need to calculate size ourselves now */
++ if (req->count < 4)
++ req->count = 4;
++ if (req->count > jpg_nbufs)
++ req->count = jpg_nbufs;
+
+- switch (fh->map_mode) {
+- case ZORAN_MAP_MODE_RAW: /* raw capture */
+- if (fh->v4l_buffers.active == ZORAN_FREE &&
+- zr->v4l_buffers.active != ZORAN_FREE) {
+- res = -EPERM; /* stay off other's settings! */
+- goto strmoff_unlock_and_return;
+- }
+- if (zr->v4l_buffers.active == ZORAN_FREE)
+- goto strmoff_unlock_and_return;
++ /* The next mmap will map the MJPEG buffers */
++ map_mode_jpg(fh, req->type == V4L2_BUF_TYPE_VIDEO_OUTPUT);
++ fh->buffers.num_buffers = req->count;
++ fh->buffers.buffer_size = zoran_v4l2_calc_bufsize(&fh->jpg_settings);
+
+- /* unload capture */
+- if (zr->v4l_memgrab_active) {
+- unsigned long flags;
++ if (jpg_fbuffer_alloc(fh)) {
++ res = -ENOMEM;
++ goto v4l2reqbuf_unlock_and_return;
++ }
++ } else {
++ dprintk(1,
++ KERN_ERR
++ "%s: VIDIOC_REQBUFS - unknown type %d\n",
++ ZR_DEVNAME(zr), req->type);
++ res = -EINVAL;
++ goto v4l2reqbuf_unlock_and_return;
++ }
++v4l2reqbuf_unlock_and_return:
++ mutex_unlock(&zr->resource_lock);
+
+- spin_lock_irqsave(&zr->spinlock, flags);
+- zr36057_set_memgrab(zr, 0);
+- spin_unlock_irqrestore(&zr->spinlock, flags);
+- }
++ return res;
++}
+
+- for (i = 0; i < fh->v4l_buffers.num_buffers; i++)
+- zr->v4l_buffers.buffer[i].state =
+- BUZ_STATE_USER;
+- fh->v4l_buffers = zr->v4l_buffers;
++static int zoran_querybuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ int res;
+
+- zr->v4l_buffers.active = fh->v4l_buffers.active =
+- ZORAN_FREE;
++ mutex_lock(&zr->resource_lock);
++ res = zoran_v4l2_buffer_status(fh, buf, buf->index);
++ mutex_unlock(&zr->resource_lock);
+
+- zr->v4l_grab_seq = 0;
+- zr->v4l_pend_head = zr->v4l_pend_tail = 0;
+- zr->v4l_sync_tail = 0;
++ return res;
++}
+
+- break;
++static int zoran_qbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ int res = 0, codec_mode, buf_type;
+
+- case ZORAN_MAP_MODE_JPG_REC:
+- case ZORAN_MAP_MODE_JPG_PLAY:
+- if (fh->jpg_buffers.active == ZORAN_FREE &&
+- zr->jpg_buffers.active != ZORAN_FREE) {
+- res = -EPERM; /* stay off other's settings! */
+- goto strmoff_unlock_and_return;
+- }
+- if (zr->jpg_buffers.active == ZORAN_FREE)
+- goto strmoff_unlock_and_return;
+-
+- res =
+- jpg_qbuf(file, -1,
+- (fh->map_mode ==
+- ZORAN_MAP_MODE_JPG_REC) ?
+- BUZ_MODE_MOTION_COMPRESS :
+- BUZ_MODE_MOTION_DECOMPRESS);
+- if (res)
+- goto strmoff_unlock_and_return;
+- break;
+- default:
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_STREAMOFF - invalid map mode %d\n",
+- ZR_DEVNAME(zr), fh->map_mode);
++ mutex_lock(&zr->resource_lock);
++
++ switch (fh->map_mode) {
++ case ZORAN_MAP_MODE_RAW:
++ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
++ dprintk(1, KERN_ERR
++ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
++ ZR_DEVNAME(zr), buf->type, fh->map_mode);
+ res = -EINVAL;
+- goto strmoff_unlock_and_return;
++ goto qbuf_unlock_and_return;
+ }
+- strmoff_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
+
+- return res;
+- }
++ res = zoran_v4l_queue_frame(fh, buf->index);
++ if (res)
++ goto qbuf_unlock_and_return;
++ if (!zr->v4l_memgrab_active && fh->buffers.active == ZORAN_LOCKED)
++ zr36057_set_memgrab(zr, 1);
+ break;
+
+- case VIDIOC_QUERYCTRL:
+- {
+- struct v4l2_queryctrl *ctrl = arg;
+-
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_QUERYCTRL - id=%d\n",
+- ZR_DEVNAME(zr), ctrl->id);
+-
+- /* we only support hue/saturation/contrast/brightness */
+- if (ctrl->id < V4L2_CID_BRIGHTNESS ||
+- ctrl->id > V4L2_CID_HUE)
+- return -EINVAL;
+- else {
+- int id = ctrl->id;
+- memset(ctrl, 0, sizeof(*ctrl));
+- ctrl->id = id;
++ case ZORAN_MAP_MODE_JPG_REC:
++ case ZORAN_MAP_MODE_JPG_PLAY:
++ if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY) {
++ buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
++ codec_mode = BUZ_MODE_MOTION_DECOMPRESS;
++ } else {
++ buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
++ codec_mode = BUZ_MODE_MOTION_COMPRESS;
+ }
+
+- switch (ctrl->id) {
+- case V4L2_CID_BRIGHTNESS:
+- strncpy(ctrl->name, "Brightness", sizeof(ctrl->name)-1);
+- break;
+- case V4L2_CID_CONTRAST:
+- strncpy(ctrl->name, "Contrast", sizeof(ctrl->name)-1);
+- break;
+- case V4L2_CID_SATURATION:
+- strncpy(ctrl->name, "Saturation", sizeof(ctrl->name)-1);
+- break;
+- case V4L2_CID_HUE:
+- strncpy(ctrl->name, "Hue", sizeof(ctrl->name)-1);
+- break;
++ if (buf->type != buf_type) {
++ dprintk(1, KERN_ERR
++ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
++ ZR_DEVNAME(zr), buf->type, fh->map_mode);
++ res = -EINVAL;
++ goto qbuf_unlock_and_return;
+ }
+
+- ctrl->minimum = 0;
+- ctrl->maximum = 65535;
+- ctrl->step = 1;
+- ctrl->default_value = 32768;
+- ctrl->type = V4L2_CTRL_TYPE_INTEGER;
++ res = zoran_jpg_queue_frame(fh, buf->index, codec_mode);
++ if (res != 0)
++ goto qbuf_unlock_and_return;
++ if (zr->codec_mode == BUZ_MODE_IDLE &&
++ fh->buffers.active == ZORAN_LOCKED)
++ zr36057_enable_jpg(zr, codec_mode);
+
+- return 0;
+- }
+ break;
+
+- case VIDIOC_G_CTRL:
+- {
+- struct v4l2_control *ctrl = arg;
+-
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_G_CTRL - id=%d\n",
+- ZR_DEVNAME(zr), ctrl->id);
+-
+- /* we only support hue/saturation/contrast/brightness */
+- if (ctrl->id < V4L2_CID_BRIGHTNESS ||
+- ctrl->id > V4L2_CID_HUE)
+- return -EINVAL;
+-
+- mutex_lock(&zr->resource_lock);
+- switch (ctrl->id) {
+- case V4L2_CID_BRIGHTNESS:
+- ctrl->value = zr->brightness;
+- break;
+- case V4L2_CID_CONTRAST:
+- ctrl->value = zr->contrast;
+- break;
+- case V4L2_CID_SATURATION:
+- ctrl->value = zr->saturation;
+- break;
+- case V4L2_CID_HUE:
+- ctrl->value = zr->hue;
+- break;
+- }
+- mutex_unlock(&zr->resource_lock);
+-
+- return 0;
+- }
++ default:
++ dprintk(1, KERN_ERR
++ "%s: VIDIOC_QBUF - unsupported type %d\n",
++ ZR_DEVNAME(zr), buf->type);
++ res = -EINVAL;
+ break;
++ }
++qbuf_unlock_and_return:
++ mutex_unlock(&zr->resource_lock);
+
+- case VIDIOC_S_CTRL:
+- {
+- struct v4l2_control *ctrl = arg;
+- struct video_picture pict;
++ return res;
++}
+
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_S_CTRL - id=%d\n",
+- ZR_DEVNAME(zr), ctrl->id);
++static int zoran_dqbuf(struct file *file, void *__fh, struct v4l2_buffer *buf)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ int res = 0, buf_type, num = -1; /* compiler borks here (?) */
+
+- /* we only support hue/saturation/contrast/brightness */
+- if (ctrl->id < V4L2_CID_BRIGHTNESS ||
+- ctrl->id > V4L2_CID_HUE)
+- return -EINVAL;
++ mutex_lock(&zr->resource_lock);
+
+- if (ctrl->value < 0 || ctrl->value > 65535) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_S_CTRL - invalid value %d for id=%d\n",
+- ZR_DEVNAME(zr), ctrl->value, ctrl->id);
+- return -EINVAL;
++ switch (fh->map_mode) {
++ case ZORAN_MAP_MODE_RAW:
++ if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
++ dprintk(1, KERN_ERR
++ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
++ ZR_DEVNAME(zr), buf->type, fh->map_mode);
++ res = -EINVAL;
++ goto dqbuf_unlock_and_return;
+ }
+
+- mutex_lock(&zr->resource_lock);
+- switch (ctrl->id) {
+- case V4L2_CID_BRIGHTNESS:
+- zr->brightness = ctrl->value;
+- break;
+- case V4L2_CID_CONTRAST:
+- zr->contrast = ctrl->value;
+- break;
+- case V4L2_CID_SATURATION:
+- zr->saturation = ctrl->value;
+- break;
+- case V4L2_CID_HUE:
+- zr->hue = ctrl->value;
+- break;
++ num = zr->v4l_pend[zr->v4l_sync_tail & V4L_MASK_FRAME];
++ if (file->f_flags & O_NONBLOCK &&
++ zr->v4l_buffers.buffer[num].state != BUZ_STATE_DONE) {
++ res = -EAGAIN;
++ goto dqbuf_unlock_and_return;
+ }
+- pict.brightness = zr->brightness;
+- pict.contrast = zr->contrast;
+- pict.colour = zr->saturation;
+- pict.hue = zr->hue;
+-
+- decoder_command(zr, DECODER_SET_PICTURE, &pict);
+-
+- mutex_unlock(&zr->resource_lock);
+-
+- return 0;
+- }
++ res = v4l_sync(fh, num);
++ if (res)
++ goto dqbuf_unlock_and_return;
++ zr->v4l_sync_tail++;
++ res = zoran_v4l2_buffer_status(fh, buf, num);
+ break;
+
+- case VIDIOC_ENUMSTD:
++ case ZORAN_MAP_MODE_JPG_REC:
++ case ZORAN_MAP_MODE_JPG_PLAY:
+ {
+- struct v4l2_standard *std = arg;
++ struct zoran_sync bs;
+
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_ENUMSTD - index=%d\n",
+- ZR_DEVNAME(zr), std->index);
++ if (fh->map_mode == ZORAN_MAP_MODE_JPG_PLAY)
++ buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
++ else
++ buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+- if (std->index < 0 || std->index >= (zr->card.norms + 1))
+- return -EINVAL;
+- else {
+- int id = std->index;
+- memset(std, 0, sizeof(*std));
+- std->index = id;
++ if (buf->type != buf_type) {
++ dprintk(1, KERN_ERR
++ "%s: VIDIOC_QBUF - invalid buf->type=%d for map_mode=%d\n",
++ ZR_DEVNAME(zr), buf->type, fh->map_mode);
++ res = -EINVAL;
++ goto dqbuf_unlock_and_return;
+ }
+
+- if (std->index == zr->card.norms) {
+- /* if we have autodetect, ... */
+- struct video_decoder_capability caps;
+- decoder_command(zr, DECODER_GET_CAPABILITIES,
+- &caps);
+- if (caps.flags & VIDEO_DECODER_AUTO) {
+- std->id = V4L2_STD_ALL;
+- strncpy(std->name, "Autodetect", sizeof(std->name)-1);
+- return 0;
+- } else
+- return -EINVAL;
+- }
+- switch (std->index) {
+- case 0:
+- std->id = V4L2_STD_PAL;
+- strncpy(std->name, "PAL", sizeof(std->name)-1);
+- std->frameperiod.numerator = 1;
+- std->frameperiod.denominator = 25;
+- std->framelines = zr->card.tvn[0]->Ht;
+- break;
+- case 1:
+- std->id = V4L2_STD_NTSC;
+- strncpy(std->name, "NTSC", sizeof(std->name)-1);
+- std->frameperiod.numerator = 1001;
+- std->frameperiod.denominator = 30000;
+- std->framelines = zr->card.tvn[1]->Ht;
+- break;
+- case 2:
+- std->id = V4L2_STD_SECAM;
+- strncpy(std->name, "SECAM", sizeof(std->name)-1);
+- std->frameperiod.numerator = 1;
+- std->frameperiod.denominator = 25;
+- std->framelines = zr->card.tvn[2]->Ht;
+- break;
+- }
++ num = zr->jpg_pend[zr->jpg_que_tail & BUZ_MASK_FRAME];
+
+- return 0;
++ if (file->f_flags & O_NONBLOCK &&
++ zr->jpg_buffers.buffer[num].state != BUZ_STATE_DONE) {
++ res = -EAGAIN;
++ goto dqbuf_unlock_and_return;
++ }
++ res = jpg_sync(fh, &bs);
++ if (res)
++ goto dqbuf_unlock_and_return;
++ res = zoran_v4l2_buffer_status(fh, buf, bs.frame);
++ break;
+ }
++
++ default:
++ dprintk(1, KERN_ERR
++ "%s: VIDIOC_DQBUF - unsupported type %d\n",
++ ZR_DEVNAME(zr), buf->type);
++ res = -EINVAL;
+ break;
++ }
++dqbuf_unlock_and_return:
++ mutex_unlock(&zr->resource_lock);
+
+- case VIDIOC_G_STD:
+- {
+- v4l2_std_id *std = arg;
+- int norm;
++ return res;
++}
+
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_G_STD\n", ZR_DEVNAME(zr));
++static int zoran_streamon(struct file *file, void *__fh, enum v4l2_buf_type type)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ int res = 0;
+
+- mutex_lock(&zr->resource_lock);
+- norm = zr->norm;
+- mutex_unlock(&zr->resource_lock);
++ mutex_lock(&zr->resource_lock);
+
+- switch (norm) {
+- case VIDEO_MODE_PAL:
+- *std = V4L2_STD_PAL;
+- break;
+- case VIDEO_MODE_NTSC:
+- *std = V4L2_STD_NTSC;
+- break;
+- case VIDEO_MODE_SECAM:
+- *std = V4L2_STD_SECAM;
+- break;
++ switch (fh->map_mode) {
++ case ZORAN_MAP_MODE_RAW: /* raw capture */
++ if (zr->v4l_buffers.active != ZORAN_ACTIVE ||
++ fh->buffers.active != ZORAN_ACTIVE) {
++ res = -EBUSY;
++ goto strmon_unlock_and_return;
+ }
+
+- return 0;
+- }
++ zr->v4l_buffers.active = fh->buffers.active = ZORAN_LOCKED;
++ zr->v4l_settings = fh->v4l_settings;
++
++ zr->v4l_sync_tail = zr->v4l_pend_tail;
++ if (!zr->v4l_memgrab_active &&
++ zr->v4l_pend_head != zr->v4l_pend_tail) {
++ zr36057_set_memgrab(zr, 1);
++ }
+ break;
+
+- case VIDIOC_S_STD:
+- {
+- int norm = -1, res = 0;
+- v4l2_std_id *std = arg;
+-
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_S_STD - norm=0x%llx\n",
+- ZR_DEVNAME(zr), (unsigned long long)*std);
+-
+- if ((*std & V4L2_STD_PAL) && !(*std & ~V4L2_STD_PAL))
+- norm = VIDEO_MODE_PAL;
+- else if ((*std & V4L2_STD_NTSC) && !(*std & ~V4L2_STD_NTSC))
+- norm = VIDEO_MODE_NTSC;
+- else if ((*std & V4L2_STD_SECAM) && !(*std & ~V4L2_STD_SECAM))
+- norm = VIDEO_MODE_SECAM;
+- else if (*std == V4L2_STD_ALL)
+- norm = VIDEO_MODE_AUTO;
+- else {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_S_STD - invalid norm 0x%llx\n",
+- ZR_DEVNAME(zr), (unsigned long long)*std);
+- return -EINVAL;
++ case ZORAN_MAP_MODE_JPG_REC:
++ case ZORAN_MAP_MODE_JPG_PLAY:
++ /* what is the codec mode right now? */
++ if (zr->jpg_buffers.active != ZORAN_ACTIVE ||
++ fh->buffers.active != ZORAN_ACTIVE) {
++ res = -EBUSY;
++ goto strmon_unlock_and_return;
+ }
+
+- mutex_lock(&zr->resource_lock);
+- if ((res = zoran_set_norm(zr, norm)))
+- goto sstd_unlock_and_return;
++ zr->jpg_buffers.active = fh->buffers.active = ZORAN_LOCKED;
+
+- res = wait_grab_pending(zr);
+- sstd_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
+- return res;
+- }
++ if (zr->jpg_que_head != zr->jpg_que_tail) {
++ /* Start the jpeg codec when the first frame is queued */
++ jpeg_start(zr);
++ }
+ break;
+
+- case VIDIOC_ENUMINPUT:
+- {
+- struct v4l2_input *inp = arg;
+- int status;
+-
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_ENUMINPUT - index=%d\n",
+- ZR_DEVNAME(zr), inp->index);
++ default:
++ dprintk(1,
++ KERN_ERR
++ "%s: VIDIOC_STREAMON - invalid map mode %d\n",
++ ZR_DEVNAME(zr), fh->map_mode);
++ res = -EINVAL;
++ break;
++ }
++strmon_unlock_and_return:
++ mutex_unlock(&zr->resource_lock);
+
+- if (inp->index < 0 || inp->index >= zr->card.inputs)
+- return -EINVAL;
+- else {
+- int id = inp->index;
+- memset(inp, 0, sizeof(*inp));
+- inp->index = id;
+- }
++ return res;
++}
+
+- strncpy(inp->name, zr->card.input[inp->index].name,
+- sizeof(inp->name) - 1);
+- inp->type = V4L2_INPUT_TYPE_CAMERA;
+- inp->std = V4L2_STD_ALL;
++static int zoran_streamoff(struct file *file, void *__fh, enum v4l2_buf_type type)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ int i, res = 0;
++ unsigned long flags;
+
+- /* Get status of video decoder */
+- mutex_lock(&zr->resource_lock);
+- decoder_command(zr, DECODER_GET_STATUS, &status);
+- mutex_unlock(&zr->resource_lock);
++ mutex_lock(&zr->resource_lock);
+
+- if (!(status & DECODER_STATUS_GOOD)) {
+- inp->status |= V4L2_IN_ST_NO_POWER;
+- inp->status |= V4L2_IN_ST_NO_SIGNAL;
++ switch (fh->map_mode) {
++ case ZORAN_MAP_MODE_RAW: /* raw capture */
++ if (fh->buffers.active == ZORAN_FREE &&
++ zr->v4l_buffers.active != ZORAN_FREE) {
++ res = -EPERM; /* stay off other's settings! */
++ goto strmoff_unlock_and_return;
+ }
+- if (!(status & DECODER_STATUS_COLOR))
+- inp->status |= V4L2_IN_ST_NO_COLOR;
++ if (zr->v4l_buffers.active == ZORAN_FREE)
++ goto strmoff_unlock_and_return;
+
+- return 0;
+- }
+- break;
++ spin_lock_irqsave(&zr->spinlock, flags);
++ /* unload capture */
++ if (zr->v4l_memgrab_active) {
+
+- case VIDIOC_G_INPUT:
+- {
+- int *input = arg;
++ zr36057_set_memgrab(zr, 0);
++ }
+
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_G_INPUT\n", ZR_DEVNAME(zr));
++ for (i = 0; i < fh->buffers.num_buffers; i++)
++ zr->v4l_buffers.buffer[i].state = BUZ_STATE_USER;
++ fh->buffers = zr->v4l_buffers;
+
+- mutex_lock(&zr->resource_lock);
+- *input = zr->input;
+- mutex_unlock(&zr->resource_lock);
++ zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE;
+
+- return 0;
+- }
+- break;
++ zr->v4l_grab_seq = 0;
++ zr->v4l_pend_head = zr->v4l_pend_tail = 0;
++ zr->v4l_sync_tail = 0;
+
+- case VIDIOC_S_INPUT:
+- {
+- int *input = arg, res = 0;
++ spin_unlock_irqrestore(&zr->spinlock, flags);
+
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_S_INPUT - input=%d\n",
+- ZR_DEVNAME(zr), *input);
++ break;
+
+- mutex_lock(&zr->resource_lock);
+- if ((res = zoran_set_input(zr, *input)))
+- goto sinput_unlock_and_return;
++ case ZORAN_MAP_MODE_JPG_REC:
++ case ZORAN_MAP_MODE_JPG_PLAY:
++ if (fh->buffers.active == ZORAN_FREE &&
++ zr->jpg_buffers.active != ZORAN_FREE) {
++ res = -EPERM; /* stay off other's settings! */
++ goto strmoff_unlock_and_return;
++ }
++ if (zr->jpg_buffers.active == ZORAN_FREE)
++ goto strmoff_unlock_and_return;
+
+- /* Make sure the changes come into effect */
+- res = wait_grab_pending(zr);
+- sinput_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
+- return res;
+- }
++ res = jpg_qbuf(fh, -1,
++ (fh->map_mode == ZORAN_MAP_MODE_JPG_REC) ?
++ BUZ_MODE_MOTION_COMPRESS :
++ BUZ_MODE_MOTION_DECOMPRESS);
++ if (res)
++ goto strmoff_unlock_and_return;
++ break;
++ default:
++ dprintk(1, KERN_ERR
++ "%s: VIDIOC_STREAMOFF - invalid map mode %d\n",
++ ZR_DEVNAME(zr), fh->map_mode);
++ res = -EINVAL;
+ break;
++ }
++strmoff_unlock_and_return:
++ mutex_unlock(&zr->resource_lock);
+
+- case VIDIOC_ENUMOUTPUT:
+- {
+- struct v4l2_output *outp = arg;
++ return res;
++}
+
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_ENUMOUTPUT - index=%d\n",
+- ZR_DEVNAME(zr), outp->index);
++static int zoran_queryctrl(struct file *file, void *__fh,
++ struct v4l2_queryctrl *ctrl)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
+
+- if (outp->index != 0)
+- return -EINVAL;
++ /* we only support hue/saturation/contrast/brightness */
++ if (ctrl->id < V4L2_CID_BRIGHTNESS ||
++ ctrl->id > V4L2_CID_HUE)
++ return -EINVAL;
+
+- memset(outp, 0, sizeof(*outp));
+- outp->index = 0;
+- outp->type = V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY;
+- strncpy(outp->name, "Autodetect", sizeof(outp->name)-1);
++ decoder_call(zr, core, queryctrl, ctrl);
+
+- return 0;
+- }
+- break;
++ return 0;
++}
+
+- case VIDIOC_G_OUTPUT:
+- {
+- int *output = arg;
++static int zoran_g_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
+
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_G_OUTPUT\n", ZR_DEVNAME(zr));
++ /* we only support hue/saturation/contrast/brightness */
++ if (ctrl->id < V4L2_CID_BRIGHTNESS ||
++ ctrl->id > V4L2_CID_HUE)
++ return -EINVAL;
+
+- *output = 0;
++ mutex_lock(&zr->resource_lock);
++ decoder_call(zr, core, g_ctrl, ctrl);
++ mutex_unlock(&zr->resource_lock);
+
+- return 0;
+- }
+- break;
++ return 0;
++}
+
+- case VIDIOC_S_OUTPUT:
+- {
+- int *output = arg;
++static int zoran_s_ctrl(struct file *file, void *__fh, struct v4l2_control *ctrl)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
+
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_S_OUTPUT - output=%d\n",
+- ZR_DEVNAME(zr), *output);
++ /* we only support hue/saturation/contrast/brightness */
++ if (ctrl->id < V4L2_CID_BRIGHTNESS ||
++ ctrl->id > V4L2_CID_HUE)
++ return -EINVAL;
+
+- if (*output != 0)
+- return -EINVAL;
++ mutex_lock(&zr->resource_lock);
++ decoder_call(zr, core, s_ctrl, ctrl);
++ mutex_unlock(&zr->resource_lock);
+
+- return 0;
+- }
+- break;
++ return 0;
++}
+
+- /* cropping (sub-frame capture) */
+- case VIDIOC_CROPCAP:
+- {
+- struct v4l2_cropcap *cropcap = arg;
+- int type = cropcap->type, res = 0;
++static int zoran_g_std(struct file *file, void *__fh, v4l2_std_id *std)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
+
+- dprintk(3, KERN_ERR "%s: VIDIOC_CROPCAP - type=%d\n",
+- ZR_DEVNAME(zr), cropcap->type);
++ mutex_lock(&zr->resource_lock);
++ *std = zr->norm;
++ mutex_unlock(&zr->resource_lock);
++ return 0;
++}
+
+- memset(cropcap, 0, sizeof(*cropcap));
+- cropcap->type = type;
++static int zoran_s_std(struct file *file, void *__fh, v4l2_std_id *std)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ int res = 0;
+
+- mutex_lock(&zr->resource_lock);
++ mutex_lock(&zr->resource_lock);
++ res = zoran_set_norm(zr, *std);
++ if (res)
++ goto sstd_unlock_and_return;
+
+- if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+- (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+- fh->map_mode == ZORAN_MAP_MODE_RAW)) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_CROPCAP - subcapture only supported for compressed capture\n",
+- ZR_DEVNAME(zr));
+- res = -EINVAL;
+- goto cropcap_unlock_and_return;
+- }
++ res = wait_grab_pending(zr);
++sstd_unlock_and_return:
++ mutex_unlock(&zr->resource_lock);
++ return res;
++}
+
+- cropcap->bounds.top = cropcap->bounds.left = 0;
+- cropcap->bounds.width = BUZ_MAX_WIDTH;
+- cropcap->bounds.height = BUZ_MAX_HEIGHT;
+- cropcap->defrect.top = cropcap->defrect.left = 0;
+- cropcap->defrect.width = BUZ_MIN_WIDTH;
+- cropcap->defrect.height = BUZ_MIN_HEIGHT;
+- cropcap_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
+- return res;
+- }
+- break;
++static int zoran_enum_input(struct file *file, void *__fh,
++ struct v4l2_input *inp)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
+
+- case VIDIOC_G_CROP:
+- {
+- struct v4l2_crop *crop = arg;
+- int type = crop->type, res = 0;
++ if (inp->index < 0 || inp->index >= zr->card.inputs)
++ return -EINVAL;
++ else {
++ int id = inp->index;
++ memset(inp, 0, sizeof(*inp));
++ inp->index = id;
++ }
+
+- dprintk(3, KERN_ERR "%s: VIDIOC_G_CROP - type=%d\n",
+- ZR_DEVNAME(zr), crop->type);
++ strncpy(inp->name, zr->card.input[inp->index].name,
++ sizeof(inp->name) - 1);
++ inp->type = V4L2_INPUT_TYPE_CAMERA;
++ inp->std = V4L2_STD_ALL;
+
+- memset(crop, 0, sizeof(*crop));
+- crop->type = type;
++ /* Get status of video decoder */
++ mutex_lock(&zr->resource_lock);
++ decoder_call(zr, video, g_input_status, &inp->status);
++ mutex_unlock(&zr->resource_lock);
++ return 0;
++}
+
+- mutex_lock(&zr->resource_lock);
++static int zoran_g_input(struct file *file, void *__fh, unsigned int *input)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
+
+- if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+- (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+- fh->map_mode == ZORAN_MAP_MODE_RAW)) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n",
+- ZR_DEVNAME(zr));
+- res = -EINVAL;
+- goto gcrop_unlock_and_return;
+- }
++ mutex_lock(&zr->resource_lock);
++ *input = zr->input;
++ mutex_unlock(&zr->resource_lock);
+
+- crop->c.top = fh->jpg_settings.img_y;
+- crop->c.left = fh->jpg_settings.img_x;
+- crop->c.width = fh->jpg_settings.img_width;
+- crop->c.height = fh->jpg_settings.img_height;
++ return 0;
++}
+
+- gcrop_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
++static int zoran_s_input(struct file *file, void *__fh, unsigned int input)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ int res;
+
+- return res;
+- }
+- break;
++ mutex_lock(&zr->resource_lock);
++ res = zoran_set_input(zr, input);
++ if (res)
++ goto sinput_unlock_and_return;
+
+- case VIDIOC_S_CROP:
+- {
+- struct v4l2_crop *crop = arg;
+- int res = 0;
++ /* Make sure the changes come into effect */
++ res = wait_grab_pending(zr);
++sinput_unlock_and_return:
++ mutex_unlock(&zr->resource_lock);
++ return res;
++}
+
+- settings = fh->jpg_settings;
++static int zoran_enum_output(struct file *file, void *__fh,
++ struct v4l2_output *outp)
++{
++ if (outp->index != 0)
++ return -EINVAL;
+
+- dprintk(3,
+- KERN_ERR
+- "%s: VIDIOC_S_CROP - type=%d, x=%d,y=%d,w=%d,h=%d\n",
+- ZR_DEVNAME(zr), crop->type, crop->c.left, crop->c.top,
+- crop->c.width, crop->c.height);
++ memset(outp, 0, sizeof(*outp));
++ outp->index = 0;
++ outp->type = V4L2_OUTPUT_TYPE_ANALOGVGAOVERLAY;
++ strncpy(outp->name, "Autodetect", sizeof(outp->name)-1);
+
+- mutex_lock(&zr->resource_lock);
++ return 0;
++}
+
+- if (fh->jpg_buffers.allocated || fh->v4l_buffers.allocated) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_S_CROP - cannot change settings while active\n",
+- ZR_DEVNAME(zr));
+- res = -EBUSY;
+- goto scrop_unlock_and_return;
+- }
++static int zoran_g_output(struct file *file, void *__fh, unsigned int *output)
++{
++ *output = 0;
+
+- if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
+- (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
+- fh->map_mode == ZORAN_MAP_MODE_RAW)) {
+- dprintk(1,
+- KERN_ERR
+- "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n",
+- ZR_DEVNAME(zr));
+- res = -EINVAL;
+- goto scrop_unlock_and_return;
+- }
++ return 0;
++}
+
+- /* move into a form that we understand */
+- settings.img_x = crop->c.left;
+- settings.img_y = crop->c.top;
+- settings.img_width = crop->c.width;
+- settings.img_height = crop->c.height;
++static int zoran_s_output(struct file *file, void *__fh, unsigned int output)
++{
++ if (output != 0)
++ return -EINVAL;
+
+- /* check validity */
+- if ((res = zoran_check_jpg_settings(zr, &settings)))
+- goto scrop_unlock_and_return;
++ return 0;
++}
+
+- /* accept */
+- fh->jpg_settings = settings;
++/* cropping (sub-frame capture) */
++static int zoran_cropcap(struct file *file, void *__fh,
++ struct v4l2_cropcap *cropcap)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ int type = cropcap->type, res = 0;
+
+- scrop_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
+- return res;
+- }
+- break;
++ memset(cropcap, 0, sizeof(*cropcap));
++ cropcap->type = type;
+
+- case VIDIOC_G_JPEGCOMP:
+- {
+- struct v4l2_jpegcompression *params = arg;
++ mutex_lock(&zr->resource_lock);
+
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_G_JPEGCOMP\n",
++ if (cropcap->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
++ (cropcap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
++ fh->map_mode == ZORAN_MAP_MODE_RAW)) {
++ dprintk(1, KERN_ERR
++ "%s: VIDIOC_CROPCAP - subcapture only supported for compressed capture\n",
+ ZR_DEVNAME(zr));
++ res = -EINVAL;
++ goto cropcap_unlock_and_return;
++ }
+
+- memset(params, 0, sizeof(*params));
++ cropcap->bounds.top = cropcap->bounds.left = 0;
++ cropcap->bounds.width = BUZ_MAX_WIDTH;
++ cropcap->bounds.height = BUZ_MAX_HEIGHT;
++ cropcap->defrect.top = cropcap->defrect.left = 0;
++ cropcap->defrect.width = BUZ_MIN_WIDTH;
++ cropcap->defrect.height = BUZ_MIN_HEIGHT;
++cropcap_unlock_and_return:
++ mutex_unlock(&zr->resource_lock);
++ return res;
++}
+
+- mutex_lock(&zr->resource_lock);
++static int zoran_g_crop(struct file *file, void *__fh, struct v4l2_crop *crop)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ int type = crop->type, res = 0;
+
+- params->quality = fh->jpg_settings.jpg_comp.quality;
+- params->APPn = fh->jpg_settings.jpg_comp.APPn;
+- memcpy(params->APP_data,
+- fh->jpg_settings.jpg_comp.APP_data,
+- fh->jpg_settings.jpg_comp.APP_len);
+- params->APP_len = fh->jpg_settings.jpg_comp.APP_len;
+- memcpy(params->COM_data,
+- fh->jpg_settings.jpg_comp.COM_data,
+- fh->jpg_settings.jpg_comp.COM_len);
+- params->COM_len = fh->jpg_settings.jpg_comp.COM_len;
+- params->jpeg_markers =
+- fh->jpg_settings.jpg_comp.jpeg_markers;
++ memset(crop, 0, sizeof(*crop));
++ crop->type = type;
+
+- mutex_unlock(&zr->resource_lock);
++ mutex_lock(&zr->resource_lock);
+
+- return 0;
++ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
++ (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
++ fh->map_mode == ZORAN_MAP_MODE_RAW)) {
++ dprintk(1,
++ KERN_ERR
++ "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n",
++ ZR_DEVNAME(zr));
++ res = -EINVAL;
++ goto gcrop_unlock_and_return;
+ }
+- break;
+-
+- case VIDIOC_S_JPEGCOMP:
+- {
+- struct v4l2_jpegcompression *params = arg;
+- int res = 0;
+
+- settings = fh->jpg_settings;
++ crop->c.top = fh->jpg_settings.img_y;
++ crop->c.left = fh->jpg_settings.img_x;
++ crop->c.width = fh->jpg_settings.img_width;
++ crop->c.height = fh->jpg_settings.img_height;
+
+- dprintk(3,
+- KERN_DEBUG
+- "%s: VIDIOC_S_JPEGCOMP - quality=%d, APPN=%d, APP_len=%d, COM_len=%d\n",
+- ZR_DEVNAME(zr), params->quality, params->APPn,
+- params->APP_len, params->COM_len);
++gcrop_unlock_and_return:
++ mutex_unlock(&zr->resource_lock);
+
+- settings.jpg_comp = *params;
++ return res;
++}
+
+- mutex_lock(&zr->resource_lock);
++static int zoran_s_crop(struct file *file, void *__fh, struct v4l2_crop *crop)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ int res = 0;
++ struct zoran_jpg_settings settings;
+
+- if (fh->v4l_buffers.active != ZORAN_FREE ||
+- fh->jpg_buffers.active != ZORAN_FREE) {
+- dprintk(1,
+- KERN_WARNING
+- "%s: VIDIOC_S_JPEGCOMP called while in playback/capture mode\n",
+- ZR_DEVNAME(zr));
+- res = -EBUSY;
+- goto sjpegc_unlock_and_return;
+- }
++ settings = fh->jpg_settings;
+
+- if ((res = zoran_check_jpg_settings(zr, &settings)))
+- goto sjpegc_unlock_and_return;
+- if (!fh->jpg_buffers.allocated)
+- fh->jpg_buffers.buffer_size =
+- zoran_v4l2_calc_bufsize(&fh->jpg_settings);
+- fh->jpg_settings.jpg_comp = *params = settings.jpg_comp;
+- sjpegc_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
++ mutex_lock(&zr->resource_lock);
+
+- return 0;
++ if (fh->buffers.allocated) {
++ dprintk(1, KERN_ERR
++ "%s: VIDIOC_S_CROP - cannot change settings while active\n",
++ ZR_DEVNAME(zr));
++ res = -EBUSY;
++ goto scrop_unlock_and_return;
+ }
+- break;
+
+- case VIDIOC_QUERYSTD: /* why is this useful? */
+- {
+- v4l2_std_id *std = arg;
+-
+- dprintk(3,
+- KERN_DEBUG "%s: VIDIOC_QUERY_STD - std=0x%llx\n",
+- ZR_DEVNAME(zr), (unsigned long long)*std);
+-
+- if (*std == V4L2_STD_ALL || *std == V4L2_STD_NTSC ||
+- *std == V4L2_STD_PAL || (*std == V4L2_STD_SECAM &&
+- zr->card.norms == 3)) {
+- return 0;
+- }
+-
+- return -EINVAL;
++ if (crop->type != V4L2_BUF_TYPE_VIDEO_OUTPUT &&
++ (crop->type != V4L2_BUF_TYPE_VIDEO_CAPTURE ||
++ fh->map_mode == ZORAN_MAP_MODE_RAW)) {
++ dprintk(1, KERN_ERR
++ "%s: VIDIOC_G_CROP - subcapture only supported for compressed capture\n",
++ ZR_DEVNAME(zr));
++ res = -EINVAL;
++ goto scrop_unlock_and_return;
+ }
+- break;
+
+- case VIDIOC_TRY_FMT:
+- {
+- struct v4l2_format *fmt = arg;
+- int res = 0;
++ /* move into a form that we understand */
++ settings.img_x = crop->c.left;
++ settings.img_y = crop->c.top;
++ settings.img_width = crop->c.width;
++ settings.img_height = crop->c.height;
+
+- dprintk(3, KERN_DEBUG "%s: VIDIOC_TRY_FMT - type=%d\n",
+- ZR_DEVNAME(zr), fmt->type);
++ /* check validity */
++ res = zoran_check_jpg_settings(zr, &settings, 0);
++ if (res)
++ goto scrop_unlock_and_return;
+
+- switch (fmt->type) {
+- case V4L2_BUF_TYPE_VIDEO_OVERLAY:
+- mutex_lock(&zr->resource_lock);
++ /* accept */
++ fh->jpg_settings = settings;
+
+- if (fmt->fmt.win.w.width > BUZ_MAX_WIDTH)
+- fmt->fmt.win.w.width = BUZ_MAX_WIDTH;
+- if (fmt->fmt.win.w.width < BUZ_MIN_WIDTH)
+- fmt->fmt.win.w.width = BUZ_MIN_WIDTH;
+- if (fmt->fmt.win.w.height > BUZ_MAX_HEIGHT)
+- fmt->fmt.win.w.height = BUZ_MAX_HEIGHT;
+- if (fmt->fmt.win.w.height < BUZ_MIN_HEIGHT)
+- fmt->fmt.win.w.height = BUZ_MIN_HEIGHT;
++scrop_unlock_and_return:
++ mutex_unlock(&zr->resource_lock);
++ return res;
++}
+
+- mutex_unlock(&zr->resource_lock);
+- break;
++static int zoran_g_jpegcomp(struct file *file, void *__fh,
++ struct v4l2_jpegcompression *params)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ memset(params, 0, sizeof(*params));
+
+- case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+- case V4L2_BUF_TYPE_VIDEO_OUTPUT:
+- if (fmt->fmt.pix.bytesperline > 0)
+- return -EINVAL;
++ mutex_lock(&zr->resource_lock);
+
+- mutex_lock(&zr->resource_lock);
+-
+- if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_MJPEG) {
+- settings = fh->jpg_settings;
+-
+- /* we actually need to set 'real' parameters now */
+- if ((fmt->fmt.pix.height * 2) >
+- BUZ_MAX_HEIGHT)
+- settings.TmpDcm = 1;
+- else
+- settings.TmpDcm = 2;
+- settings.decimation = 0;
+- if (fmt->fmt.pix.height <=
+- fh->jpg_settings.img_height / 2)
+- settings.VerDcm = 2;
+- else
+- settings.VerDcm = 1;
+- if (fmt->fmt.pix.width <=
+- fh->jpg_settings.img_width / 4)
+- settings.HorDcm = 4;
+- else if (fmt->fmt.pix.width <=
+- fh->jpg_settings.img_width / 2)
+- settings.HorDcm = 2;
+- else
+- settings.HorDcm = 1;
+- if (settings.TmpDcm == 1)
+- settings.field_per_buff = 2;
+- else
+- settings.field_per_buff = 1;
+-
+- /* check */
+- if ((res =
+- zoran_check_jpg_settings(zr,
+- &settings)))
+- goto tryfmt_unlock_and_return;
+-
+- /* tell the user what we actually did */
+- fmt->fmt.pix.width =
+- settings.img_width / settings.HorDcm;
+- fmt->fmt.pix.height =
+- settings.img_height * 2 /
+- (settings.TmpDcm * settings.VerDcm);
+- if (settings.TmpDcm == 1)
+- fmt->fmt.pix.field =
+- (fh->jpg_settings.
+- odd_even ? V4L2_FIELD_SEQ_TB :
+- V4L2_FIELD_SEQ_BT);
+- else
+- fmt->fmt.pix.field =
+- (fh->jpg_settings.
+- odd_even ? V4L2_FIELD_TOP :
+- V4L2_FIELD_BOTTOM);
+-
+- fmt->fmt.pix.sizeimage =
+- zoran_v4l2_calc_bufsize(&settings);
+- } else if (fmt->type ==
+- V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+- int i;
+-
+- for (i = 0; i < NUM_FORMATS; i++)
+- if (zoran_formats[i].fourcc ==
+- fmt->fmt.pix.pixelformat)
+- break;
+- if (i == NUM_FORMATS) {
+- res = -EINVAL;
+- goto tryfmt_unlock_and_return;
+- }
++ params->quality = fh->jpg_settings.jpg_comp.quality;
++ params->APPn = fh->jpg_settings.jpg_comp.APPn;
++ memcpy(params->APP_data,
++ fh->jpg_settings.jpg_comp.APP_data,
++ fh->jpg_settings.jpg_comp.APP_len);
++ params->APP_len = fh->jpg_settings.jpg_comp.APP_len;
++ memcpy(params->COM_data,
++ fh->jpg_settings.jpg_comp.COM_data,
++ fh->jpg_settings.jpg_comp.COM_len);
++ params->COM_len = fh->jpg_settings.jpg_comp.COM_len;
++ params->jpeg_markers =
++ fh->jpg_settings.jpg_comp.jpeg_markers;
+
+- if (fmt->fmt.pix.width > BUZ_MAX_WIDTH)
+- fmt->fmt.pix.width = BUZ_MAX_WIDTH;
+- if (fmt->fmt.pix.width < BUZ_MIN_WIDTH)
+- fmt->fmt.pix.width = BUZ_MIN_WIDTH;
+- if (fmt->fmt.pix.height > BUZ_MAX_HEIGHT)
+- fmt->fmt.pix.height =
+- BUZ_MAX_HEIGHT;
+- if (fmt->fmt.pix.height < BUZ_MIN_HEIGHT)
+- fmt->fmt.pix.height =
+- BUZ_MIN_HEIGHT;
+- } else {
+- res = -EINVAL;
+- goto tryfmt_unlock_and_return;
+- }
+- tryfmt_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
++ mutex_unlock(&zr->resource_lock);
+
+- return res;
+- break;
++ return 0;
++}
+
+- default:
+- return -EINVAL;
+- }
++static int zoran_s_jpegcomp(struct file *file, void *__fh,
++ struct v4l2_jpegcompression *params)
++{
++ struct zoran_fh *fh = __fh;
++ struct zoran *zr = fh->zr;
++ int res = 0;
++ struct zoran_jpg_settings settings;
+
+- return 0;
+- }
+- break;
++ settings = fh->jpg_settings;
+
+- default:
+- dprintk(1, KERN_DEBUG "%s: UNKNOWN ioctl cmd: 0x%x\n",
+- ZR_DEVNAME(zr), cmd);
+- return -ENOIOCTLCMD;
+- break;
++ settings.jpg_comp = *params;
++
++ mutex_lock(&zr->resource_lock);
+
++ if (fh->buffers.active != ZORAN_FREE) {
++ dprintk(1, KERN_WARNING
++ "%s: VIDIOC_S_JPEGCOMP called while in playback/capture mode\n",
++ ZR_DEVNAME(zr));
++ res = -EBUSY;
++ goto sjpegc_unlock_and_return;
+ }
+- return 0;
+-}
+
++ res = zoran_check_jpg_settings(zr, &settings, 0);
++ if (res)
++ goto sjpegc_unlock_and_return;
++ if (!fh->buffers.allocated)
++ fh->buffers.buffer_size =
++ zoran_v4l2_calc_bufsize(&fh->jpg_settings);
++ fh->jpg_settings.jpg_comp = *params = settings.jpg_comp;
++sjpegc_unlock_and_return:
++ mutex_unlock(&zr->resource_lock);
+
+-static long
+-zoran_ioctl(struct file *file,
+- unsigned int cmd,
+- unsigned long arg)
+-{
+- return video_usercopy(file, cmd, arg, zoran_do_ioctl);
++ return res;
+ }
+
+ static unsigned int
+@@ -4191,11 +3062,11 @@ zoran_poll (struct file *file,
+ KERN_DEBUG
+ "%s: %s() raw - active=%c, sync_tail=%lu/%c, pend_tail=%lu, pend_head=%lu\n",
+ ZR_DEVNAME(zr), __func__,
+- "FAL"[fh->v4l_buffers.active], zr->v4l_sync_tail,
++ "FAL"[fh->buffers.active], zr->v4l_sync_tail,
+ "UPMD"[zr->v4l_buffers.buffer[frame].state],
+ zr->v4l_pend_tail, zr->v4l_pend_head);
+ /* Process is the one capturing? */
+- if (fh->v4l_buffers.active != ZORAN_FREE &&
++ if (fh->buffers.active != ZORAN_FREE &&
+ /* Buffer ready to DQBUF? */
+ zr->v4l_buffers.buffer[frame].state == BUZ_STATE_DONE)
+ res = POLLIN | POLLRDNORM;
+@@ -4213,10 +3084,10 @@ zoran_poll (struct file *file,
+ KERN_DEBUG
+ "%s: %s() jpg - active=%c, que_tail=%lu/%c, que_head=%lu, dma=%lu/%lu\n",
+ ZR_DEVNAME(zr), __func__,
+- "FAL"[fh->jpg_buffers.active], zr->jpg_que_tail,
++ "FAL"[fh->buffers.active], zr->jpg_que_tail,
+ "UPMD"[zr->jpg_buffers.buffer[frame].state],
+ zr->jpg_que_head, zr->jpg_dma_tail, zr->jpg_dma_head);
+- if (fh->jpg_buffers.active != ZORAN_FREE &&
++ if (fh->buffers.active != ZORAN_FREE &&
+ zr->jpg_buffers.buffer[frame].state == BUZ_STATE_DONE) {
+ if (fh->map_mode == ZORAN_MAP_MODE_JPG_REC)
+ res = POLLIN | POLLRDNORM;
+@@ -4230,8 +3101,8 @@ zoran_poll (struct file *file,
+ default:
+ dprintk(1,
+ KERN_ERR
+- "%s: zoran_poll() - internal error, unknown map_mode=%d\n",
+- ZR_DEVNAME(zr), fh->map_mode);
++ "%s: %s - internal error, unknown map_mode=%d\n",
++ ZR_DEVNAME(zr), __func__, fh->map_mode);
+ res = POLLNVAL;
+ }
+
+@@ -4265,98 +3136,53 @@ static void
+ zoran_vm_close (struct vm_area_struct *vma)
+ {
+ struct zoran_mapping *map = vma->vm_private_data;
+- struct file *file = map->file;
+- struct zoran_fh *fh = file->private_data;
++ struct zoran_fh *fh = map->file->private_data;
+ struct zoran *zr = fh->zr;
+ int i;
+
+- map->count--;
+- if (map->count == 0) {
+- switch (fh->map_mode) {
+- case ZORAN_MAP_MODE_JPG_REC:
+- case ZORAN_MAP_MODE_JPG_PLAY:
+-
+- dprintk(3, KERN_INFO "%s: munmap(MJPEG)\n",
+- ZR_DEVNAME(zr));
++ if (--map->count > 0)
++ return;
+
+- for (i = 0; i < fh->jpg_buffers.num_buffers; i++) {
+- if (fh->jpg_buffers.buffer[i].map == map) {
+- fh->jpg_buffers.buffer[i].map =
+- NULL;
+- }
+- }
+- kfree(map);
+-
+- for (i = 0; i < fh->jpg_buffers.num_buffers; i++)
+- if (fh->jpg_buffers.buffer[i].map)
+- break;
+- if (i == fh->jpg_buffers.num_buffers) {
+- mutex_lock(&zr->resource_lock);
+-
+- if (fh->jpg_buffers.active != ZORAN_FREE) {
+- jpg_qbuf(file, -1, zr->codec_mode);
+- zr->jpg_buffers.allocated = 0;
+- zr->jpg_buffers.active =
+- fh->jpg_buffers.active =
+- ZORAN_FREE;
+- }
+- //jpg_fbuffer_free(file);
+- fh->jpg_buffers.allocated = 0;
+- fh->jpg_buffers.ready_to_be_freed = 1;
+-
+- mutex_unlock(&zr->resource_lock);
+- }
++ dprintk(3, KERN_INFO "%s: %s - munmap(%s)\n", ZR_DEVNAME(zr),
++ __func__, mode_name(fh->map_mode));
+
+- break;
+-
+- case ZORAN_MAP_MODE_RAW:
+-
+- dprintk(3, KERN_INFO "%s: munmap(V4L)\n",
+- ZR_DEVNAME(zr));
+-
+- for (i = 0; i < fh->v4l_buffers.num_buffers; i++) {
+- if (fh->v4l_buffers.buffer[i].map == map) {
+- /* unqueue/unmap */
+- fh->v4l_buffers.buffer[i].map =
+- NULL;
+- }
+- }
+- kfree(map);
++ for (i = 0; i < fh->buffers.num_buffers; i++) {
++ if (fh->buffers.buffer[i].map == map)
++ fh->buffers.buffer[i].map = NULL;
++ }
++ kfree(map);
+
+- for (i = 0; i < fh->v4l_buffers.num_buffers; i++)
+- if (fh->v4l_buffers.buffer[i].map)
+- break;
+- if (i == fh->v4l_buffers.num_buffers) {
+- mutex_lock(&zr->resource_lock);
+-
+- if (fh->v4l_buffers.active != ZORAN_FREE) {
+- unsigned long flags;
+-
+- spin_lock_irqsave(&zr->spinlock, flags);
+- zr36057_set_memgrab(zr, 0);
+- zr->v4l_buffers.allocated = 0;
+- zr->v4l_buffers.active =
+- fh->v4l_buffers.active =
+- ZORAN_FREE;
+- spin_unlock_irqrestore(&zr->spinlock, flags);
+- }
+- //v4l_fbuffer_free(file);
+- fh->v4l_buffers.allocated = 0;
+- fh->v4l_buffers.ready_to_be_freed = 1;
++ /* Any buffers still mapped? */
++ for (i = 0; i < fh->buffers.num_buffers; i++)
++ if (fh->buffers.buffer[i].map)
++ return;
+
+- mutex_unlock(&zr->resource_lock);
+- }
++ dprintk(3, KERN_INFO "%s: %s - free %s buffers\n", ZR_DEVNAME(zr),
++ __func__, mode_name(fh->map_mode));
+
+- break;
++ mutex_lock(&zr->resource_lock);
+
+- default:
+- printk(KERN_ERR
+- "%s: munmap() - internal error - unknown map mode %d\n",
+- ZR_DEVNAME(zr), fh->map_mode);
+- break;
++ if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
++ if (fh->buffers.active != ZORAN_FREE) {
++ unsigned long flags;
+
++ spin_lock_irqsave(&zr->spinlock, flags);
++ zr36057_set_memgrab(zr, 0);
++ zr->v4l_buffers.allocated = 0;
++ zr->v4l_buffers.active = fh->buffers.active = ZORAN_FREE;
++ spin_unlock_irqrestore(&zr->spinlock, flags);
+ }
++ v4l_fbuffer_free(fh);
++ } else {
++ if (fh->buffers.active != ZORAN_FREE) {
++ jpg_qbuf(fh, -1, zr->codec_mode);
++ zr->jpg_buffers.allocated = 0;
++ zr->jpg_buffers.active = fh->buffers.active = ZORAN_FREE;
++ }
++ jpg_fbuffer_free(fh);
+ }
++
++ mutex_unlock(&zr->resource_lock);
+ }
+
+ static struct vm_operations_struct zoran_vm_ops = {
+@@ -4379,90 +3205,106 @@ zoran_mmap (struct file *file,
+ int res = 0;
+
+ dprintk(3,
+- KERN_INFO "%s: mmap(%s) of 0x%08lx-0x%08lx (size=%lu)\n",
+- ZR_DEVNAME(zr),
+- fh->map_mode == ZORAN_MAP_MODE_RAW ? "V4L" : "MJPEG",
+- vma->vm_start, vma->vm_end, size);
++ KERN_INFO "%s: %s(%s) of 0x%08lx-0x%08lx (size=%lu)\n",
++ ZR_DEVNAME(zr), __func__,
++ mode_name(fh->map_mode), vma->vm_start, vma->vm_end, size);
+
+ if (!(vma->vm_flags & VM_SHARED) || !(vma->vm_flags & VM_READ) ||
+ !(vma->vm_flags & VM_WRITE)) {
+ dprintk(1,
+ KERN_ERR
+- "%s: mmap() - no MAP_SHARED/PROT_{READ,WRITE} given\n",
+- ZR_DEVNAME(zr));
++ "%s: %s - no MAP_SHARED/PROT_{READ,WRITE} given\n",
++ ZR_DEVNAME(zr), __func__);
+ return -EINVAL;
+ }
+
+- switch (fh->map_mode) {
++ mutex_lock(&zr->resource_lock);
+
+- case ZORAN_MAP_MODE_JPG_REC:
+- case ZORAN_MAP_MODE_JPG_PLAY:
++ if (!fh->buffers.allocated) {
++ dprintk(1,
++ KERN_ERR
++ "%s: %s(%s) - buffers not yet allocated\n",
++ ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode));
++ res = -ENOMEM;
++ goto mmap_unlock_and_return;
++ }
+
+- /* lock */
+- mutex_lock(&zr->resource_lock);
++ first = offset / fh->buffers.buffer_size;
++ last = first - 1 + size / fh->buffers.buffer_size;
++ if (offset % fh->buffers.buffer_size != 0 ||
++ size % fh->buffers.buffer_size != 0 || first < 0 ||
++ last < 0 || first >= fh->buffers.num_buffers ||
++ last >= fh->buffers.buffer_size) {
++ dprintk(1,
++ KERN_ERR
++ "%s: %s(%s) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n",
++ ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), offset, size,
++ fh->buffers.buffer_size,
++ fh->buffers.num_buffers);
++ res = -EINVAL;
++ goto mmap_unlock_and_return;
++ }
+
+- /* Map the MJPEG buffers */
+- if (!fh->jpg_buffers.allocated) {
++ /* Check if any buffers are already mapped */
++ for (i = first; i <= last; i++) {
++ if (fh->buffers.buffer[i].map) {
+ dprintk(1,
+ KERN_ERR
+- "%s: zoran_mmap(MJPEG) - buffers not yet allocated\n",
+- ZR_DEVNAME(zr));
+- res = -ENOMEM;
+- goto jpg_mmap_unlock_and_return;
++ "%s: %s(%s) - buffer %d already mapped\n",
++ ZR_DEVNAME(zr), __func__, mode_name(fh->map_mode), i);
++ res = -EBUSY;
++ goto mmap_unlock_and_return;
+ }
++ }
+
+- first = offset / fh->jpg_buffers.buffer_size;
+- last = first - 1 + size / fh->jpg_buffers.buffer_size;
+- if (offset % fh->jpg_buffers.buffer_size != 0 ||
+- size % fh->jpg_buffers.buffer_size != 0 || first < 0 ||
+- last < 0 || first >= fh->jpg_buffers.num_buffers ||
+- last >= fh->jpg_buffers.num_buffers) {
+- dprintk(1,
+- KERN_ERR
+- "%s: mmap(MJPEG) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n",
+- ZR_DEVNAME(zr), offset, size,
+- fh->jpg_buffers.buffer_size,
+- fh->jpg_buffers.num_buffers);
+- res = -EINVAL;
+- goto jpg_mmap_unlock_and_return;
+- }
++ /* map these buffers */
++ map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL);
++ if (!map) {
++ res = -ENOMEM;
++ goto mmap_unlock_and_return;
++ }
++ map->file = file;
++ map->count = 1;
++
++ vma->vm_ops = &zoran_vm_ops;
++ vma->vm_flags |= VM_DONTEXPAND;
++ vma->vm_private_data = map;
++
++ if (fh->map_mode == ZORAN_MAP_MODE_RAW) {
+ for (i = first; i <= last; i++) {
+- if (fh->jpg_buffers.buffer[i].map) {
++ todo = size;
++ if (todo > fh->buffers.buffer_size)
++ todo = fh->buffers.buffer_size;
++ page = fh->buffers.buffer[i].v4l.fbuffer_phys;
++ if (remap_pfn_range(vma, start, page >> PAGE_SHIFT,
++ todo, PAGE_SHARED)) {
+ dprintk(1,
+ KERN_ERR
+- "%s: mmap(MJPEG) - buffer %d already mapped\n",
+- ZR_DEVNAME(zr), i);
+- res = -EBUSY;
+- goto jpg_mmap_unlock_and_return;
++ "%s: %s(V4L) - remap_pfn_range failed\n",
++ ZR_DEVNAME(zr), __func__);
++ res = -EAGAIN;
++ goto mmap_unlock_and_return;
+ }
++ size -= todo;
++ start += todo;
++ fh->buffers.buffer[i].map = map;
++ if (size == 0)
++ break;
+ }
+-
+- /* map these buffers (v4l_buffers[i]) */
+- map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL);
+- if (!map) {
+- res = -ENOMEM;
+- goto jpg_mmap_unlock_and_return;
+- }
+- map->file = file;
+- map->count = 1;
+-
+- vma->vm_ops = &zoran_vm_ops;
+- vma->vm_flags |= VM_DONTEXPAND;
+- vma->vm_private_data = map;
+-
++ } else {
+ for (i = first; i <= last; i++) {
+ for (j = 0;
+- j < fh->jpg_buffers.buffer_size / PAGE_SIZE;
++ j < fh->buffers.buffer_size / PAGE_SIZE;
+ j++) {
+ fraglen =
+- (le32_to_cpu(fh->jpg_buffers.buffer[i].
++ (le32_to_cpu(fh->buffers.buffer[i].jpg.
+ frag_tab[2 * j + 1]) & ~1) << 1;
+ todo = size;
+ if (todo > fraglen)
+ todo = fraglen;
+ pos =
+- le32_to_cpu(fh->jpg_buffers.
+- buffer[i].frag_tab[2 * j]);
++ le32_to_cpu(fh->buffers.
++ buffer[i].jpg.frag_tab[2 * j]);
+ /* should just be pos on i386 */
+ page = virt_to_phys(bus_to_virt(pos))
+ >> PAGE_SHIFT;
+@@ -4470,123 +3312,82 @@ zoran_mmap (struct file *file,
+ todo, PAGE_SHARED)) {
+ dprintk(1,
+ KERN_ERR
+- "%s: zoran_mmap(V4L) - remap_pfn_range failed\n",
+- ZR_DEVNAME(zr));
++ "%s: %s(V4L) - remap_pfn_range failed\n",
++ ZR_DEVNAME(zr), __func__);
+ res = -EAGAIN;
+- goto jpg_mmap_unlock_and_return;
++ goto mmap_unlock_and_return;
+ }
+ size -= todo;
+ start += todo;
+ if (size == 0)
+ break;
+- if (le32_to_cpu(fh->jpg_buffers.buffer[i].
++ if (le32_to_cpu(fh->buffers.buffer[i].jpg.
+ frag_tab[2 * j + 1]) & 1)
+ break; /* was last fragment */
+ }
+- fh->jpg_buffers.buffer[i].map = map;
++ fh->buffers.buffer[i].map = map;
+ if (size == 0)
+ break;
+
+ }
+- jpg_mmap_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
+-
+- break;
+-
+- case ZORAN_MAP_MODE_RAW:
+-
+- mutex_lock(&zr->resource_lock);
+-
+- /* Map the V4L buffers */
+- if (!fh->v4l_buffers.allocated) {
+- dprintk(1,
+- KERN_ERR
+- "%s: zoran_mmap(V4L) - buffers not yet allocated\n",
+- ZR_DEVNAME(zr));
+- res = -ENOMEM;
+- goto v4l_mmap_unlock_and_return;
+- }
+-
+- first = offset / fh->v4l_buffers.buffer_size;
+- last = first - 1 + size / fh->v4l_buffers.buffer_size;
+- if (offset % fh->v4l_buffers.buffer_size != 0 ||
+- size % fh->v4l_buffers.buffer_size != 0 || first < 0 ||
+- last < 0 || first >= fh->v4l_buffers.num_buffers ||
+- last >= fh->v4l_buffers.buffer_size) {
+- dprintk(1,
+- KERN_ERR
+- "%s: mmap(V4L) - offset=%lu or size=%lu invalid for bufsize=%d and numbufs=%d\n",
+- ZR_DEVNAME(zr), offset, size,
+- fh->v4l_buffers.buffer_size,
+- fh->v4l_buffers.num_buffers);
+- res = -EINVAL;
+- goto v4l_mmap_unlock_and_return;
+- }
+- for (i = first; i <= last; i++) {
+- if (fh->v4l_buffers.buffer[i].map) {
+- dprintk(1,
+- KERN_ERR
+- "%s: mmap(V4L) - buffer %d already mapped\n",
+- ZR_DEVNAME(zr), i);
+- res = -EBUSY;
+- goto v4l_mmap_unlock_and_return;
+- }
+- }
+-
+- /* map these buffers (v4l_buffers[i]) */
+- map = kmalloc(sizeof(struct zoran_mapping), GFP_KERNEL);
+- if (!map) {
+- res = -ENOMEM;
+- goto v4l_mmap_unlock_and_return;
+- }
+- map->file = file;
+- map->count = 1;
+-
+- vma->vm_ops = &zoran_vm_ops;
+- vma->vm_flags |= VM_DONTEXPAND;
+- vma->vm_private_data = map;
+-
+- for (i = first; i <= last; i++) {
+- todo = size;
+- if (todo > fh->v4l_buffers.buffer_size)
+- todo = fh->v4l_buffers.buffer_size;
+- page = fh->v4l_buffers.buffer[i].fbuffer_phys;
+- if (remap_pfn_range(vma, start, page >> PAGE_SHIFT,
+- todo, PAGE_SHARED)) {
+- dprintk(1,
+- KERN_ERR
+- "%s: zoran_mmap(V4L)i - remap_pfn_range failed\n",
+- ZR_DEVNAME(zr));
+- res = -EAGAIN;
+- goto v4l_mmap_unlock_and_return;
+- }
+- size -= todo;
+- start += todo;
+- fh->v4l_buffers.buffer[i].map = map;
+- if (size == 0)
+- break;
+- }
+- v4l_mmap_unlock_and_return:
+- mutex_unlock(&zr->resource_lock);
+-
+- break;
+-
+- default:
+- dprintk(1,
+- KERN_ERR
+- "%s: zoran_mmap() - internal error - unknown map mode %d\n",
+- ZR_DEVNAME(zr), fh->map_mode);
+- break;
+ }
+
++mmap_unlock_and_return:
++ mutex_unlock(&zr->resource_lock);
++
+ return 0;
+ }
+
++static const struct v4l2_ioctl_ops zoran_ioctl_ops = {
++ .vidioc_querycap = zoran_querycap,
++ .vidioc_cropcap = zoran_cropcap,
++ .vidioc_s_crop = zoran_s_crop,
++ .vidioc_g_crop = zoran_g_crop,
++ .vidioc_enum_input = zoran_enum_input,
++ .vidioc_g_input = zoran_g_input,
++ .vidioc_s_input = zoran_s_input,
++ .vidioc_enum_output = zoran_enum_output,
++ .vidioc_g_output = zoran_g_output,
++ .vidioc_s_output = zoran_s_output,
++ .vidioc_g_fbuf = zoran_g_fbuf,
++ .vidioc_s_fbuf = zoran_s_fbuf,
++ .vidioc_g_std = zoran_g_std,
++ .vidioc_s_std = zoran_s_std,
++ .vidioc_g_jpegcomp = zoran_g_jpegcomp,
++ .vidioc_s_jpegcomp = zoran_s_jpegcomp,
++ .vidioc_overlay = zoran_overlay,
++ .vidioc_reqbufs = zoran_reqbufs,
++ .vidioc_querybuf = zoran_querybuf,
++ .vidioc_qbuf = zoran_qbuf,
++ .vidioc_dqbuf = zoran_dqbuf,
++ .vidioc_streamon = zoran_streamon,
++ .vidioc_streamoff = zoran_streamoff,
++ .vidioc_enum_fmt_vid_cap = zoran_enum_fmt_vid_cap,
++ .vidioc_enum_fmt_vid_out = zoran_enum_fmt_vid_out,
++ .vidioc_enum_fmt_vid_overlay = zoran_enum_fmt_vid_overlay,
++ .vidioc_g_fmt_vid_cap = zoran_g_fmt_vid_cap,
++ .vidioc_g_fmt_vid_out = zoran_g_fmt_vid_out,
++ .vidioc_g_fmt_vid_overlay = zoran_g_fmt_vid_overlay,
++ .vidioc_s_fmt_vid_cap = zoran_s_fmt_vid_cap,
++ .vidioc_s_fmt_vid_out = zoran_s_fmt_vid_out,
++ .vidioc_s_fmt_vid_overlay = zoran_s_fmt_vid_overlay,
++ .vidioc_try_fmt_vid_cap = zoran_try_fmt_vid_cap,
++ .vidioc_try_fmt_vid_out = zoran_try_fmt_vid_out,
++ .vidioc_try_fmt_vid_overlay = zoran_try_fmt_vid_overlay,
++ .vidioc_queryctrl = zoran_queryctrl,
++ .vidioc_s_ctrl = zoran_s_ctrl,
++ .vidioc_g_ctrl = zoran_g_ctrl,
++#ifdef CONFIG_VIDEO_V4L1_COMPAT
++ .vidioc_default = zoran_default,
++ .vidiocgmbuf = zoran_vidiocgmbuf,
++#endif
++};
++
+ static const struct v4l2_file_operations zoran_fops = {
+ .owner = THIS_MODULE,
+ .open = zoran_open,
+ .release = zoran_close,
+- .ioctl = zoran_ioctl,
++ .ioctl = video_ioctl2,
+ .read = zoran_read,
+ .write = zoran_write,
+ .mmap = zoran_mmap,
+@@ -4596,7 +3397,9 @@ static const struct v4l2_file_operations zoran_fops = {
+ struct video_device zoran_template __devinitdata = {
+ .name = ZORAN_NAME,
+ .fops = &zoran_fops,
++ .ioctl_ops = &zoran_ioctl_ops,
+ .release = &zoran_vdev_release,
++ .tvnorms = V4L2_STD_NTSC | V4L2_STD_PAL | V4L2_STD_SECAM,
+ .minor = -1
+ };
+
+diff --git a/drivers/media/video/zoran/zoran_procfs.c b/drivers/media/video/zoran/zoran_procfs.c
+index 870bc5a..f1423b7 100644
+--- a/drivers/media/video/zoran/zoran_procfs.c
++++ b/drivers/media/video/zoran/zoran_procfs.c
+@@ -36,7 +36,7 @@
+ #include <linux/pci.h>
+ #include <linux/i2c.h>
+ #include <linux/i2c-algo-bit.h>
+-#include <linux/videodev.h>
++#include <linux/videodev2.h>
+ #include <linux/spinlock.h>
+ #include <linux/sem.h>
+ #include <linux/seq_file.h>
+diff --git a/drivers/media/video/zoran/zr36016.c b/drivers/media/video/zoran/zr36016.c
+index 00d132b..21c088e 100644
+--- a/drivers/media/video/zoran/zr36016.c
++++ b/drivers/media/video/zoran/zr36016.c
+@@ -34,15 +34,10 @@
+ #include <linux/types.h>
+ #include <linux/wait.h>
+
+-/* includes for structures and defines regarding video
+- #include<linux/videodev.h> */
+-
+ /* I/O commands, error codes */
+ #include <asm/io.h>
+-//#include<errno.h>
+
+ /* v4l API */
+-#include <linux/videodev.h>
+
+ /* headerfile of this module */
+ #include"zr36016.h"
+diff --git a/drivers/media/video/zoran/zr36050.c b/drivers/media/video/zoran/zr36050.c
+index cf8b271..639dd87 100644
+--- a/drivers/media/video/zoran/zr36050.c
++++ b/drivers/media/video/zoran/zr36050.c
+@@ -34,12 +34,8 @@
+ #include <linux/types.h>
+ #include <linux/wait.h>
+
+-/* includes for structures and defines regarding video
+- #include<linux/videodev.h> */
+-
+ /* I/O commands, error codes */
+ #include <asm/io.h>
+-//#include<errno.h>
+
+ /* headerfile of this module */
+ #include "zr36050.h"
+diff --git a/drivers/media/video/zoran/zr36060.c b/drivers/media/video/zoran/zr36060.c
+index 8e74054..008746f 100644
+--- a/drivers/media/video/zoran/zr36060.c
++++ b/drivers/media/video/zoran/zr36060.c
+@@ -34,12 +34,8 @@
+ #include <linux/types.h>
+ #include <linux/wait.h>
+
+-/* includes for structures and defines regarding video
+- #include<linux/videodev.h> */
+-
+ /* I/O commands, error codes */
+ #include <asm/io.h>
+-//#include<errno.h>
+
+ /* headerfile of this module */
+ #include "zr36060.h"
+diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c
+index 9302356..221409f 100644
+--- a/drivers/media/video/zr364xx.c
++++ b/drivers/media/video/zr364xx.c
+@@ -96,6 +96,7 @@ static struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x06d6, 0x003b), .driver_info = METHOD0 },
+ {USB_DEVICE(0x0a17, 0x004e), .driver_info = METHOD2 },
+ {USB_DEVICE(0x041e, 0x405d), .driver_info = METHOD2 },
++ {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD2 },
+ {} /* Terminating entry */
+ };
+
+@@ -425,7 +426,6 @@ static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t cnt,
+ static int zr364xx_vidioc_querycap(struct file *file, void *priv,
+ struct v4l2_capability *cap)
+ {
+- memset(cap, 0, sizeof(*cap));
+ strcpy(cap->driver, DRIVER_DESC);
+ cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE;
+ return 0;
+@@ -436,8 +436,6 @@ static int zr364xx_vidioc_enum_input(struct file *file, void *priv,
+ {
+ if (i->index != 0)
+ return -EINVAL;
+- memset(i, 0, sizeof(*i));
+- i->index = 0;
+ strcpy(i->name, DRIVER_DESC " Camera");
+ i->type = V4L2_INPUT_TYPE_CAMERA;
+ return 0;
+@@ -529,11 +527,6 @@ static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file,
+ {
+ if (f->index > 0)
+ return -EINVAL;
+- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+- memset(f, 0, sizeof(*f));
+- f->index = 0;
+- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->flags = V4L2_FMT_FLAG_COMPRESSED;
+ strcpy(f->description, "JPEG");
+ f->pixelformat = V4L2_PIX_FMT_JPEG;
+@@ -550,8 +543,6 @@ static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv,
+ return -ENODEV;
+ cam = video_get_drvdata(vdev);
+
+- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+ if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG)
+ return -EINVAL;
+ if (f->fmt.pix.field != V4L2_FIELD_ANY &&
+@@ -577,10 +568,6 @@ static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv,
+ return -ENODEV;
+ cam = video_get_drvdata(vdev);
+
+- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+- memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format));
+- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ f->fmt.pix.pixelformat = V4L2_PIX_FMT_JPEG;
+ f->fmt.pix.field = V4L2_FIELD_NONE;
+ f->fmt.pix.width = cam->width;
+@@ -602,8 +589,6 @@ static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv,
+ return -ENODEV;
+ cam = video_get_drvdata(vdev);
+
+- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+- return -EINVAL;
+ if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG)
+ return -EINVAL;
+ if (f->fmt.pix.field != V4L2_FIELD_ANY &&
+diff --git a/include/linux/Kbuild b/include/linux/Kbuild
+index 106c3ba..5bd3efd 100644
+--- a/include/linux/Kbuild
++++ b/include/linux/Kbuild
+@@ -157,8 +157,6 @@ header-y += ultrasound.h
+ header-y += un.h
+ header-y += utime.h
+ header-y += veth.h
+-header-y += video_decoder.h
+-header-y += video_encoder.h
+ header-y += videotext.h
+ header-y += x25.h
+
+diff --git a/include/linux/i2c-id.h b/include/linux/i2c-id.h
+index 1ffc23b..f27604a 100644
+--- a/include/linux/i2c-id.h
++++ b/include/linux/i2c-id.h
+@@ -71,6 +71,7 @@
+ #define I2C_DRIVERID_VP27SMPX 93 /* Panasonic VP27s tuner internal MPX */
+ #define I2C_DRIVERID_M52790 95 /* Mitsubishi M52790SP/FP AV switch */
+ #define I2C_DRIVERID_CS5345 96 /* cs5345 audio processor */
++#define I2C_DRIVERID_AU8522 97 /* Auvitek au8522 */
+
+ #define I2C_DRIVERID_OV7670 1048 /* Omnivision 7670 camera */
+
+@@ -87,6 +88,7 @@
+ #define I2C_HW_B_CX2341X 0x010020 /* Conexant CX2341X MPEG encoder cards */
+ #define I2C_HW_B_CX23885 0x010022 /* conexant 23885 based tv cards (bus1) */
+ #define I2C_HW_B_AU0828 0x010023 /* auvitek au0828 usb bridge */
++#define I2C_HW_B_HDPVR 0x010025 /* Hauppauge HD PVR */
+
+ /* --- SGI adapters */
+ #define I2C_HW_SGI_VINO 0x160000
+diff --git a/include/linux/ivtv.h b/include/linux/ivtv.h
+index f272028..062d20f 100644
+--- a/include/linux/ivtv.h
++++ b/include/linux/ivtv.h
+@@ -60,10 +60,10 @@ struct ivtv_dma_frame {
+
+ #define IVTV_IOC_DMA_FRAME _IOW ('V', BASE_VIDIOC_PRIVATE+0, struct ivtv_dma_frame)
+
+-/* These are the VBI types as they appear in the embedded VBI private packets. */
+-#define IVTV_SLICED_TYPE_TELETEXT_B (1)
+-#define IVTV_SLICED_TYPE_CAPTION_525 (4)
+-#define IVTV_SLICED_TYPE_WSS_625 (5)
+-#define IVTV_SLICED_TYPE_VPS (7)
++/* Deprecated defines: applications should use the defines from videodev2.h */
++#define IVTV_SLICED_TYPE_TELETEXT_B V4L2_MPEG_VBI_IVTV_TELETEXT_B
++#define IVTV_SLICED_TYPE_CAPTION_525 V4L2_MPEG_VBI_IVTV_CAPTION_525
++#define IVTV_SLICED_TYPE_WSS_625 V4L2_MPEG_VBI_IVTV_WSS_625
++#define IVTV_SLICED_TYPE_VPS V4L2_MPEG_VBI_IVTV_VPS
+
+ #endif /* _LINUX_IVTV_H */
+diff --git a/include/linux/video_decoder.h b/include/linux/video_decoder.h
+deleted file mode 100644
+index e26c0c8..0000000
+--- a/include/linux/video_decoder.h
++++ /dev/null
+@@ -1,48 +0,0 @@
+-#ifndef _LINUX_VIDEO_DECODER_H
+-#define _LINUX_VIDEO_DECODER_H
+-
+-#include <linux/types.h>
+-
+-#define HAVE_VIDEO_DECODER 1
+-
+-struct video_decoder_capability { /* this name is too long */
+- __u32 flags;
+-#define VIDEO_DECODER_PAL 1 /* can decode PAL signal */
+-#define VIDEO_DECODER_NTSC 2 /* can decode NTSC */
+-#define VIDEO_DECODER_SECAM 4 /* can decode SECAM */
+-#define VIDEO_DECODER_AUTO 8 /* can autosense norm */
+-#define VIDEO_DECODER_CCIR 16 /* CCIR-601 pixel rate (720 pixels per line) instead of square pixel rate */
+- int inputs; /* number of inputs */
+- int outputs; /* number of outputs */
+-};
+-
+-/*
+-DECODER_GET_STATUS returns the following flags. The only one you need is
+-DECODER_STATUS_GOOD, the others are just nice things to know.
+-*/
+-#define DECODER_STATUS_GOOD 1 /* receiving acceptable input */
+-#define DECODER_STATUS_COLOR 2 /* receiving color information */
+-#define DECODER_STATUS_PAL 4 /* auto detected */
+-#define DECODER_STATUS_NTSC 8 /* auto detected */
+-#define DECODER_STATUS_SECAM 16 /* auto detected */
+-
+-struct video_decoder_init {
+- unsigned char len;
+- const unsigned char *data;
+-};
+-
+-#define DECODER_GET_CAPABILITIES _IOR('d', 1, struct video_decoder_capability)
+-#define DECODER_GET_STATUS _IOR('d', 2, int)
+-#define DECODER_SET_NORM _IOW('d', 3, int)
+-#define DECODER_SET_INPUT _IOW('d', 4, int) /* 0 <= input < #inputs */
+-#define DECODER_SET_OUTPUT _IOW('d', 5, int) /* 0 <= output < #outputs */
+-#define DECODER_ENABLE_OUTPUT _IOW('d', 6, int) /* boolean output enable control */
+-#define DECODER_SET_PICTURE _IOW('d', 7, struct video_picture)
+-#define DECODER_SET_GPIO _IOW('d', 8, int) /* switch general purpose pin */
+-#define DECODER_INIT _IOW('d', 9, struct video_decoder_init) /* init internal registers at once */
+-#define DECODER_SET_VBI_BYPASS _IOW('d', 10, int) /* switch vbi bypass */
+-
+-#define DECODER_DUMP _IO('d', 192) /* debug hook */
+-
+-
+-#endif
+diff --git a/include/linux/video_encoder.h b/include/linux/video_encoder.h
+deleted file mode 100644
+index b7b6423..0000000
+--- a/include/linux/video_encoder.h
++++ /dev/null
+@@ -1,23 +0,0 @@
+-#ifndef _LINUX_VIDEO_ENCODER_H
+-#define _LINUX_VIDEO_ENCODER_H
+-
+-#include <linux/types.h>
+-
+-struct video_encoder_capability { /* this name is too long */
+- __u32 flags;
+-#define VIDEO_ENCODER_PAL 1 /* can encode PAL signal */
+-#define VIDEO_ENCODER_NTSC 2 /* can encode NTSC */
+-#define VIDEO_ENCODER_SECAM 4 /* can encode SECAM */
+-#define VIDEO_ENCODER_CCIR 16 /* CCIR-601 pixel rate (720 pixels per line) instead of square pixel rate */
+- int inputs; /* number of inputs */
+- int outputs; /* number of outputs */
+-};
+-
+-#define ENCODER_GET_CAPABILITIES _IOR('e', 1, struct video_encoder_capability)
+-#define ENCODER_SET_NORM _IOW('e', 2, int)
+-#define ENCODER_SET_INPUT _IOW('e', 3, int) /* 0 <= input < #inputs */
+-#define ENCODER_SET_OUTPUT _IOW('e', 4, int) /* 0 <= output < #outputs */
+-#define ENCODER_ENABLE_OUTPUT _IOW('e', 5, int) /* boolean output enable control */
+-
+-
+-#endif
+diff --git a/include/linux/videodev.h b/include/linux/videodev.h
+index 837f392..b19eab1 100644
+--- a/include/linux/videodev.h
++++ b/include/linux/videodev.h
+@@ -16,6 +16,23 @@
+ #include <linux/ioctl.h>
+ #include <linux/videodev2.h>
+
++#if defined(__MIN_V4L1) && defined (__KERNEL__)
++
++/*
++ * Used by those V4L2 core functions that need a minimum V4L1 support,
++ * in order to allow V4L1 Compatibilty code compilation.
++ */
++
++struct video_mbuf
++{
++ int size; /* Total memory to map */
++ int frames; /* Frames */
++ int offsets[VIDEO_MAX_FRAME];
++};
++
++#define VIDIOCGMBUF _IOR('v',20, struct video_mbuf) /* Memory map buffer info */
++
++#else
+ #if defined(CONFIG_VIDEO_V4L1_COMPAT) || !defined (__KERNEL__)
+
+ #define VID_TYPE_CAPTURE 1 /* Can capture */
+@@ -312,6 +329,7 @@ struct video_code
+ #define VID_PLAY_END_MARK 14
+
+ #endif /* CONFIG_VIDEO_V4L1_COMPAT */
++#endif /* __MIN_V4L1 */
+
+ #endif /* __LINUX_VIDEODEV_H */
+
+diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
+index 5571dbe..139d234 100644
+--- a/include/linux/videodev2.h
++++ b/include/linux/videodev2.h
+@@ -344,6 +344,8 @@ struct v4l2_pix_format {
+ #define V4L2_PIX_FMT_SPCA508 v4l2_fourcc('S', '5', '0', '8') /* YUVY per line */
+ #define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */
+ #define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */
++#define V4L2_PIX_FMT_MR97310A v4l2_fourcc('M', '3', '1', '0') /* compressed BGGR bayer */
++#define V4L2_PIX_FMT_SQ905C v4l2_fourcc('9', '0', '5', 'C') /* compressed RGGB bayer */
+ #define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */
+ #define V4L2_PIX_FMT_YVYU v4l2_fourcc('Y', 'V', 'Y', 'U') /* 16 YVU 4:2:2 */
+
+@@ -829,6 +831,7 @@ struct v4l2_querymenu {
+ #define V4L2_CTRL_FLAG_UPDATE 0x0008
+ #define V4L2_CTRL_FLAG_INACTIVE 0x0010
+ #define V4L2_CTRL_FLAG_SLIDER 0x0020
++#define V4L2_CTRL_FLAG_WRITE_ONLY 0x0040
+
+ /* Query flag, to be ORed with the control ID */
+ #define V4L2_CTRL_FLAG_NEXT_CTRL 0x80000000
+@@ -879,8 +882,15 @@ enum v4l2_power_line_frequency {
+ #define V4L2_CID_BACKLIGHT_COMPENSATION (V4L2_CID_BASE+28)
+ #define V4L2_CID_CHROMA_AGC (V4L2_CID_BASE+29)
+ #define V4L2_CID_COLOR_KILLER (V4L2_CID_BASE+30)
++#define V4L2_CID_COLORFX (V4L2_CID_BASE+31)
++enum v4l2_colorfx {
++ V4L2_COLORFX_NONE = 0,
++ V4L2_COLORFX_BW = 1,
++ V4L2_COLORFX_SEPIA = 2,
++};
++
+ /* last CID + 1 */
+-#define V4L2_CID_LASTP1 (V4L2_CID_BASE+31)
++#define V4L2_CID_LASTP1 (V4L2_CID_BASE+32)
+
+ /* MPEG-class control IDs defined by V4L2 */
+ #define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900)
+@@ -1339,6 +1349,53 @@ struct v4l2_sliced_vbi_data {
+ };
+
+ /*
++ * Sliced VBI data inserted into MPEG Streams
++ */
++
++/*
++ * V4L2_MPEG_STREAM_VBI_FMT_IVTV:
++ *
++ * Structure of payload contained in an MPEG 2 Private Stream 1 PES Packet in an
++ * MPEG-2 Program Pack that contains V4L2_MPEG_STREAM_VBI_FMT_IVTV Sliced VBI
++ * data
++ *
++ * Note, the MPEG-2 Program Pack and Private Stream 1 PES packet header
++ * definitions are not included here. See the MPEG-2 specifications for details
++ * on these headers.
++ */
++
++/* Line type IDs */
++#define V4L2_MPEG_VBI_IVTV_TELETEXT_B (1)
++#define V4L2_MPEG_VBI_IVTV_CAPTION_525 (4)
++#define V4L2_MPEG_VBI_IVTV_WSS_625 (5)
++#define V4L2_MPEG_VBI_IVTV_VPS (7)
++
++struct v4l2_mpeg_vbi_itv0_line {
++ __u8 id; /* One of V4L2_MPEG_VBI_IVTV_* above */
++ __u8 data[42]; /* Sliced VBI data for the line */
++} __attribute__ ((packed));
++
++struct v4l2_mpeg_vbi_itv0 {
++ __le32 linemask[2]; /* Bitmasks of VBI service lines present */
++ struct v4l2_mpeg_vbi_itv0_line line[35];
++} __attribute__ ((packed));
++
++struct v4l2_mpeg_vbi_ITV0 {
++ struct v4l2_mpeg_vbi_itv0_line line[36];
++} __attribute__ ((packed));
++
++#define V4L2_MPEG_VBI_IVTV_MAGIC0 "itv0"
++#define V4L2_MPEG_VBI_IVTV_MAGIC1 "ITV0"
++
++struct v4l2_mpeg_vbi_fmt_ivtv {
++ __u8 magic[4];
++ union {
++ struct v4l2_mpeg_vbi_itv0 itv0;
++ struct v4l2_mpeg_vbi_ITV0 ITV0;
++ };
++} __attribute__ ((packed));
++
++/*
+ * A G G R E G A T E S T R U C T U R E S
+ */
+
+@@ -1403,14 +1460,6 @@ struct v4l2_dbg_chip_ident {
+ __u32 revision; /* chip revision, chip specific */
+ } __attribute__ ((packed));
+
+-/* VIDIOC_G_CHIP_IDENT_OLD: Deprecated, do not use */
+-struct v4l2_chip_ident_old {
+- __u32 match_type; /* Match type */
+- __u32 match_chip; /* Match this chip, meaning determined by match_type */
+- __u32 ident; /* chip identifier as specified in <media/v4l2-chip-ident.h> */
+- __u32 revision; /* chip revision, chip specific */
+-};
+-
+ /*
+ * I O C T L C O D E S F O R V I D E O D E V I C E S
+ *
+@@ -1488,8 +1537,6 @@ struct v4l2_chip_ident_old {
+ /* Experimental, meant for debugging, testing and internal use.
+ Never use this ioctl in applications! */
+ #define VIDIOC_DBG_G_CHIP_IDENT _IOWR('V', 81, struct v4l2_dbg_chip_ident)
+-/* This is deprecated and will go away in 2.6.30 */
+-#define VIDIOC_G_CHIP_IDENT_OLD _IOWR('V', 81, struct v4l2_chip_ident_old)
+ #endif
+
+ #define VIDIOC_S_HW_FREQ_SEEK _IOW('V', 82, struct v4l2_hw_freq_seek)
+diff --git a/include/media/bt819.h b/include/media/bt819.h
+new file mode 100644
+index 0000000..38f666b
+--- /dev/null
++++ b/include/media/bt819.h
+@@ -0,0 +1,33 @@
++/*
++ bt819.h - bt819 notifications
++
++ Copyright (C) 2009 Hans Verkuil (hverkuil@xs4all.nl)
++
++ 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 _BT819_H_
++#define _BT819_H_
++
++#include <linux/ioctl.h>
++
++/* v4l2_device notifications. */
++
++/* Needed to reset the FIFO buffer when changing the input
++ or the video standard. */
++#define BT819_FIFO_RESET_LOW _IO('b', 0)
++#define BT819_FIFO_RESET_HIGH _IO('b', 1)
++
++#endif
+diff --git a/include/media/cx2341x.h b/include/media/cx2341x.h
+index 9ec4d58..9ebe855 100644
+--- a/include/media/cx2341x.h
++++ b/include/media/cx2341x.h
+@@ -1,5 +1,5 @@
+ /*
+- cx23415/6 header containing common defines.
++ cx23415/6/8 header containing common defines.
+
+ 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
+@@ -28,6 +28,7 @@ enum cx2341x_port {
+ enum cx2341x_cap {
+ CX2341X_CAP_HAS_SLICED_VBI = 1 << 0,
+ CX2341X_CAP_HAS_TS = 1 << 1,
++ CX2341X_CAP_HAS_AC3 = 1 << 2,
+ };
+
+ struct cx2341x_mpeg_params {
+@@ -47,11 +48,12 @@ struct cx2341x_mpeg_params {
+ enum v4l2_mpeg_audio_sampling_freq audio_sampling_freq;
+ enum v4l2_mpeg_audio_encoding audio_encoding;
+ enum v4l2_mpeg_audio_l2_bitrate audio_l2_bitrate;
++ enum v4l2_mpeg_audio_ac3_bitrate audio_ac3_bitrate;
+ enum v4l2_mpeg_audio_mode audio_mode;
+ enum v4l2_mpeg_audio_mode_extension audio_mode_extension;
+ enum v4l2_mpeg_audio_emphasis audio_emphasis;
+ enum v4l2_mpeg_audio_crc audio_crc;
+- u16 audio_properties;
++ u32 audio_properties;
+ u16 audio_mute;
+
+ /* video */
+diff --git a/include/media/cx25840.h b/include/media/cx25840.h
+index db431d5..2c3fbaa 100644
+--- a/include/media/cx25840.h
++++ b/include/media/cx25840.h
+@@ -21,6 +21,18 @@
+ #ifndef _CX25840_H_
+ #define _CX25840_H_
+
++/* Note that the cx25840 driver requires that the bridge driver calls the
++ v4l2_subdev's init operation in order to load the driver's firmware.
++ Without this the audio standard detection will fail and you will
++ only get mono.
++
++ Since loading the firmware is often problematic when the driver is
++ compiled into the kernel I recommend postponing calling this function
++ until the first open of the video device. Another reason for
++ postponing it is that loading this firmware takes a long time (seconds)
++ due to the slow i2c bus speed. So it will speed up the boot process if
++ you can avoid loading the fw as long as the video device isn't used. */
++
+ enum cx25840_video_input {
+ /* Composite video inputs In1-In8 */
+ CX25840_COMPOSITE1 = 1,
+diff --git a/include/media/ir-common.h b/include/media/ir-common.h
+index 5bf2ea0..7b5b91f 100644
+--- a/include/media/ir-common.h
++++ b/include/media/ir-common.h
+@@ -111,6 +111,7 @@ extern IR_KEYTAB_TYPE ir_codes_empty[IR_KEYTAB_SIZE];
+ extern IR_KEYTAB_TYPE ir_codes_avermedia[IR_KEYTAB_SIZE];
+ extern IR_KEYTAB_TYPE ir_codes_avermedia_dvbt[IR_KEYTAB_SIZE];
+ extern IR_KEYTAB_TYPE ir_codes_avermedia_m135a[IR_KEYTAB_SIZE];
++extern IR_KEYTAB_TYPE ir_codes_avermedia_cardbus[IR_KEYTAB_SIZE];
+ extern IR_KEYTAB_TYPE ir_codes_apac_viewcomp[IR_KEYTAB_SIZE];
+ extern IR_KEYTAB_TYPE ir_codes_pixelview[IR_KEYTAB_SIZE];
+ extern IR_KEYTAB_TYPE ir_codes_pixelview_new[IR_KEYTAB_SIZE];
+@@ -159,6 +160,8 @@ extern IR_KEYTAB_TYPE ir_codes_real_audio_220_32_keys[IR_KEYTAB_SIZE];
+ extern IR_KEYTAB_TYPE ir_codes_msi_tvanywhere_plus[IR_KEYTAB_SIZE];
+ extern IR_KEYTAB_TYPE ir_codes_ati_tv_wonder_hd_600[IR_KEYTAB_SIZE];
+ extern IR_KEYTAB_TYPE ir_codes_kworld_plus_tv_analog[IR_KEYTAB_SIZE];
++extern IR_KEYTAB_TYPE ir_codes_kaiomy[IR_KEYTAB_SIZE];
++extern IR_KEYTAB_TYPE ir_codes_dm1105_nec[IR_KEYTAB_SIZE];
+ #endif
+
+ /*
+diff --git a/include/media/ir-kbd-i2c.h b/include/media/ir-kbd-i2c.h
+index 00fa57e..07963d7 100644
+--- a/include/media/ir-kbd-i2c.h
++++ b/include/media/ir-kbd-i2c.h
+@@ -14,8 +14,7 @@ struct IR_i2c {
+ /* Used to avoid fast repeating */
+ unsigned char old;
+
+- struct work_struct work;
+- struct timer_list timer;
++ struct delayed_work work;
+ char phys[32];
+ int (*get_key)(struct IR_i2c*, u32*, u32*);
+ };
+diff --git a/include/media/ov772x.h b/include/media/ov772x.h
+index e391d55..57db48d 100644
+--- a/include/media/ov772x.h
++++ b/include/media/ov772x.h
+@@ -13,8 +13,13 @@
+
+ #include <media/soc_camera.h>
+
++/* for flags */
++#define OV772X_FLAG_VFLIP 0x00000001 /* Vertical flip image */
++#define OV772X_FLAG_HFLIP 0x00000002 /* Horizontal flip image */
++
+ struct ov772x_camera_info {
+ unsigned long buswidth;
++ unsigned long flags;
+ struct soc_camera_link link;
+ };
+
+diff --git a/include/media/saa7146.h b/include/media/saa7146.h
+index c5a6e22..fff4235 100644
+--- a/include/media/saa7146.h
++++ b/include/media/saa7146.h
+@@ -13,6 +13,7 @@
+ #include <linux/stringify.h>
+ #include <linux/mutex.h>
+ #include <linux/scatterlist.h>
++#include <media/v4l2-device.h>
+
+ #include <linux/vmalloc.h> /* for vmalloc() */
+ #include <linux/mm.h> /* for vmalloc_to_page() */
+@@ -110,6 +111,8 @@ struct saa7146_dev
+
+ struct list_head item;
+
++ struct v4l2_device v4l2_dev;
++
+ /* different device locks */
+ spinlock_t slock;
+ struct mutex lock;
+@@ -145,6 +148,11 @@ struct saa7146_dev
+ struct saa7146_dma d_rps1;
+ };
+
++static inline struct saa7146_dev *to_saa7146_dev(struct v4l2_device *v4l2_dev)
++{
++ return container_of(v4l2_dev, struct saa7146_dev, v4l2_dev);
++}
++
+ /* from saa7146_i2c.c */
+ int saa7146_i2c_adapter_prepare(struct saa7146_dev *dev, struct i2c_adapter *i2c_adapter, u32 bitrate);
+
+diff --git a/include/media/saa7146_vv.h b/include/media/saa7146_vv.h
+index c8d0b23..eed5fcc 100644
+--- a/include/media/saa7146_vv.h
++++ b/include/media/saa7146_vv.h
+@@ -150,16 +150,6 @@ struct saa7146_vv
+ unsigned int resources; /* resource management for device */
+ };
+
+-#define SAA7146_EXCLUSIVE 0x1
+-#define SAA7146_BEFORE 0x2
+-#define SAA7146_AFTER 0x4
+-
+-struct saa7146_extension_ioctls
+-{
+- unsigned int cmd;
+- int flags;
+-};
+-
+ /* flags */
+ #define SAA7146_USE_PORT_B_FOR_VBI 0x2 /* use input port b for vbi hardware bug workaround */
+
+@@ -176,8 +166,10 @@ struct saa7146_ext_vv
+ int num_stds;
+ int (*std_callback)(struct saa7146_dev*, struct saa7146_standard *);
+
+- struct saa7146_extension_ioctls *ioctls;
+- long (*ioctl)(struct saa7146_fh *, unsigned int cmd, void *arg);
++ /* the extension can override this */
++ struct v4l2_ioctl_ops ops;
++ /* pointer to the saa7146 core ops */
++ const struct v4l2_ioctl_ops *core_ops;
+
+ struct v4l2_file_operations vbi_fops;
+ };
+@@ -213,6 +205,7 @@ void saa7146_set_hps_source_and_sync(struct saa7146_dev *saa, int source, int sy
+ void saa7146_set_gpio(struct saa7146_dev *saa, u8 pin, u8 data);
+
+ /* from saa7146_video.c */
++extern const struct v4l2_ioctl_ops saa7146_video_ioctl_ops;
+ extern struct saa7146_use_ops saa7146_video_uops;
+ int saa7146_start_preview(struct saa7146_fh *fh);
+ int saa7146_stop_preview(struct saa7146_fh *fh);
+diff --git a/include/media/sh_mobile_ceu.h b/include/media/sh_mobile_ceu.h
+index b5dbefe..0f3524c 100644
+--- a/include/media/sh_mobile_ceu.h
++++ b/include/media/sh_mobile_ceu.h
+@@ -1,10 +1,11 @@
+ #ifndef __ASM_SH_MOBILE_CEU_H__
+ #define __ASM_SH_MOBILE_CEU_H__
+
+-#include <media/soc_camera.h>
++#define SH_CEU_FLAG_USE_8BIT_BUS (1 << 0) /* use 8bit bus width */
++#define SH_CEU_FLAG_USE_16BIT_BUS (1 << 1) /* use 16bit bus width */
+
+ struct sh_mobile_ceu_info {
+- unsigned long flags; /* SOCAM_... */
++ unsigned long flags;
+ };
+
+ #endif /* __ASM_SH_MOBILE_CEU_H__ */
+diff --git a/include/media/soc_camera.h b/include/media/soc_camera.h
+index 7440d92..3701368 100644
+--- a/include/media/soc_camera.h
++++ b/include/media/soc_camera.h
+@@ -45,6 +45,7 @@ struct soc_camera_device {
+ int num_formats;
+ struct soc_camera_format_xlate *user_formats;
+ int num_user_formats;
++ enum v4l2_field field; /* Preserve field over close() */
+ struct module *owner;
+ void *host_priv; /* Per-device host private data */
+ /* soc_camera.c private count. Only accessed with .video_lock held */
+@@ -74,7 +75,8 @@ struct soc_camera_host_ops {
+ int (*resume)(struct soc_camera_device *);
+ int (*get_formats)(struct soc_camera_device *, int,
+ struct soc_camera_format_xlate *);
+- int (*set_fmt)(struct soc_camera_device *, __u32, struct v4l2_rect *);
++ int (*set_crop)(struct soc_camera_device *, struct v4l2_rect *);
++ int (*set_fmt)(struct soc_camera_device *, struct v4l2_format *);
+ int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *);
+ void (*init_videobuf)(struct videobuf_queue *,
+ struct soc_camera_device *);
+@@ -93,13 +95,18 @@ struct soc_camera_host_ops {
+ struct soc_camera_link {
+ /* Camera bus id, used to match a camera and a bus */
+ int bus_id;
+- /* GPIO number to switch between 8 and 10 bit modes */
+- unsigned int gpio;
+ /* Per camera SOCAM_SENSOR_* bus flags */
+ unsigned long flags;
+ /* Optional callbacks to power on or off and reset the sensor */
+ int (*power)(struct device *, int);
+ int (*reset)(struct device *);
++ /*
++ * some platforms may support different data widths than the sensors
++ * native ones due to different data line routing. Let the board code
++ * overwrite the width flags.
++ */
++ int (*set_bus_param)(struct soc_camera_link *, unsigned long flags);
++ unsigned long (*query_bus_param)(struct soc_camera_link *);
+ };
+
+ static inline struct soc_camera_device *to_soc_camera_dev(struct device *dev)
+@@ -159,7 +166,8 @@ struct soc_camera_ops {
+ int (*release)(struct soc_camera_device *);
+ int (*start_capture)(struct soc_camera_device *);
+ int (*stop_capture)(struct soc_camera_device *);
+- int (*set_fmt)(struct soc_camera_device *, __u32, struct v4l2_rect *);
++ int (*set_crop)(struct soc_camera_device *, struct v4l2_rect *);
++ int (*set_fmt)(struct soc_camera_device *, struct v4l2_format *);
+ int (*try_fmt)(struct soc_camera_device *, struct v4l2_format *);
+ unsigned long (*query_bus_param)(struct soc_camera_device *);
+ int (*set_bus_param)(struct soc_camera_device *, unsigned long);
+@@ -239,15 +247,19 @@ static inline struct v4l2_queryctrl const *soc_camera_find_qctrl(
+ static inline unsigned long soc_camera_bus_param_compatible(
+ unsigned long camera_flags, unsigned long bus_flags)
+ {
+- unsigned long common_flags, hsync, vsync, pclk;
++ unsigned long common_flags, hsync, vsync, pclk, data, buswidth, mode;
+
+ common_flags = camera_flags & bus_flags;
+
+ hsync = common_flags & (SOCAM_HSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_LOW);
+ vsync = common_flags & (SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_VSYNC_ACTIVE_LOW);
+ pclk = common_flags & (SOCAM_PCLK_SAMPLE_RISING | SOCAM_PCLK_SAMPLE_FALLING);
++ data = common_flags & (SOCAM_DATA_ACTIVE_HIGH | SOCAM_DATA_ACTIVE_LOW);
++ mode = common_flags & (SOCAM_MASTER | SOCAM_SLAVE);
++ buswidth = common_flags & SOCAM_DATAWIDTH_MASK;
+
+- return (!hsync || !vsync || !pclk) ? 0 : common_flags;
++ return (!hsync || !vsync || !pclk || !data || !mode || !buswidth) ? 0 :
++ common_flags;
+ }
+
+ extern unsigned long soc_camera_apply_sensor_flags(struct soc_camera_link *icl,
+diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h
+index 9aaf652..1be461a 100644
+--- a/include/media/v4l2-chip-ident.h
++++ b/include/media/v4l2-chip-ident.h
+@@ -37,10 +37,8 @@ enum {
+ /* module saa7110: just ident 100 */
+ V4L2_IDENT_SAA7110 = 100,
+
+- /* module saa7111: just ident 101 */
++ /* module saa7115: reserved range 101-149 */
+ V4L2_IDENT_SAA7111 = 101,
+-
+- /* module saa7115: reserved range 102-149 */
+ V4L2_IDENT_SAA7113 = 103,
+ V4L2_IDENT_SAA7114 = 104,
+ V4L2_IDENT_SAA7115 = 105,
+@@ -63,44 +61,96 @@ enum {
+ V4L2_IDENT_OV7720 = 251,
+ V4L2_IDENT_OV7725 = 252,
+
+- /* Conexant MPEG encoder/decoders: reserved range 410-420 */
++ /* module saa7146: reserved range 300-309 */
++ V4L2_IDENT_SAA7146 = 300,
++
++ /* Conexant MPEG encoder/decoders: reserved range 400-420 */
++ V4L2_IDENT_CX23418_843 = 403, /* Integrated A/V Decoder on the '418 */
+ V4L2_IDENT_CX23415 = 415,
+ V4L2_IDENT_CX23416 = 416,
+ V4L2_IDENT_CX23418 = 418,
+
++ /* module au0828 */
++ V4L2_IDENT_AU0828 = 828,
++
++ /* module indycam: just ident 2000 */
++ V4L2_IDENT_INDYCAM = 2000,
++
++ /* module bt819: reserved range 810-819 */
++ V4L2_IDENT_BT815A = 815,
++ V4L2_IDENT_BT817A = 817,
++ V4L2_IDENT_BT819A = 819,
++
++ /* module bt856: just ident 856 */
++ V4L2_IDENT_BT856 = 856,
++
++ /* module bt866: just ident 866 */
++ V4L2_IDENT_BT866 = 866,
++
++ /* module ks0127: reserved range 1120-1129 */
++ V4L2_IDENT_KS0122S = 1122,
++ V4L2_IDENT_KS0127 = 1127,
++ V4L2_IDENT_KS0127B = 1128,
++
+ /* module vp27smpx: just ident 2700 */
+ V4L2_IDENT_VP27SMPX = 2700,
+
++ /* module vpx3220: reserved range: 3210-3229 */
++ V4L2_IDENT_VPX3214C = 3214,
++ V4L2_IDENT_VPX3216B = 3216,
++ V4L2_IDENT_VPX3220A = 3220,
++
+ /* module tvp5150 */
+ V4L2_IDENT_TVP5150 = 5150,
+
++ /* module saa5246a: just ident 5246 */
++ V4L2_IDENT_SAA5246A = 5246,
++
++ /* module saa5249: just ident 5249 */
++ V4L2_IDENT_SAA5249 = 5249,
++
+ /* module cs5345: just ident 5345 */
+ V4L2_IDENT_CS5345 = 5345,
+
++ /* module tea6415c: just ident 6415 */
++ V4L2_IDENT_TEA6415C = 6415,
++
++ /* module tea6420: just ident 6420 */
++ V4L2_IDENT_TEA6420 = 6420,
++
++ /* module saa6588: just ident 6588 */
++ V4L2_IDENT_SAA6588 = 6588,
++
+ /* module saa6752hs: reserved range 6750-6759 */
+ V4L2_IDENT_SAA6752HS = 6752,
+ V4L2_IDENT_SAA6752HS_AC3 = 6753,
+
++ /* module adv7170: just ident 7170 */
++ V4L2_IDENT_ADV7170 = 7170,
++
++ /* module adv7175: just ident 7175 */
++ V4L2_IDENT_ADV7175 = 7175,
++
++ /* module saa7185: just ident 7185 */
++ V4L2_IDENT_SAA7185 = 7185,
++
++ /* module saa7191: just ident 7191 */
++ V4L2_IDENT_SAA7191 = 7191,
++
+ /* module wm8739: just ident 8739 */
+ V4L2_IDENT_WM8739 = 8739,
+
+ /* module wm8775: just ident 8775 */
+ V4L2_IDENT_WM8775 = 8775,
+
+- /* module tw9910: just ident 9910 */
+- V4L2_IDENT_TW9910 = 9910,
+-
+- /* module cs53132a: just ident 53132 */
+- V4L2_IDENT_CS53l32A = 53132,
+-
+- /* module upd64031a: just ident 64031 */
+- V4L2_IDENT_UPD64031A = 64031,
++ /* module tda9840: just ident 9840 */
++ V4L2_IDENT_TDA9840 = 9840,
+
+- /* module upd64083: just ident 64083 */
+- V4L2_IDENT_UPD64083 = 64083,
++ /* module cafe_ccic, just ident 8801 */
++ V4L2_IDENT_CAFE = 8801,
+
+- /* module m52790: just ident 52790 */
+- V4L2_IDENT_M52790 = 52790,
++ /* module tw9910: just ident 9910 */
++ V4L2_IDENT_TW9910 = 9910,
+
+ /* module msp3400: reserved range 34000-34999 and 44000-44999 */
+ V4L2_IDENT_MSPX4XX = 34000, /* generic MSPX4XX identifier, only
+@@ -178,6 +228,18 @@ enum {
+ V4L2_IDENT_MT9V022IX7ATC = 45010, /* No way to detect "normal" I77ATx */
+ V4L2_IDENT_MT9V022IX7ATM = 45015, /* and "lead free" IA7ATx chips */
+ V4L2_IDENT_MT9T031 = 45020,
++
++ /* module cs53132a: just ident 53132 */
++ V4L2_IDENT_CS53l32A = 53132,
++
++ /* module upd64031a: just ident 64031 */
++ V4L2_IDENT_UPD64031A = 64031,
++
++ /* module upd64083: just ident 64083 */
++ V4L2_IDENT_UPD64083 = 64083,
++
++ /* module m52790: just ident 52790 */
++ V4L2_IDENT_M52790 = 52790,
+ };
+
+ #endif
+diff --git a/include/media/v4l2-common.h b/include/media/v4l2-common.h
+index 95e74f1..3a69056 100644
+--- a/include/media/v4l2-common.h
++++ b/include/media/v4l2-common.h
+@@ -102,11 +102,15 @@ int v4l2_ctrl_check(struct v4l2_ext_control *ctrl, struct v4l2_queryctrl *qctrl,
+ const char *v4l2_ctrl_get_name(u32 id);
+ const char **v4l2_ctrl_get_menu(u32 id);
+ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 step, s32 def);
+-int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl);
+ int v4l2_ctrl_query_menu(struct v4l2_querymenu *qmenu,
+ struct v4l2_queryctrl *qctrl, const char **menu_items);
+ #define V4L2_CTRL_MENU_IDS_END (0xffffffff)
+ int v4l2_ctrl_query_menu_valid_items(struct v4l2_querymenu *qmenu, const u32 *ids);
++
++/* Note: ctrl_classes points to an array of u32 pointers. Each u32 array is a
++ 0-terminated array of control IDs. Each array must be sorted low to high
++ and belong to the same control class. The array of u32 pointers must also
++ be sorted, from low class IDs to high class IDs. */
+ u32 v4l2_ctrl_next(const u32 * const *ctrl_classes, u32 id);
+
+ /* ------------------------------------------------------------------------- */
+@@ -149,6 +153,21 @@ struct v4l2_subdev *v4l2_i2c_new_probed_subdev(struct i2c_adapter *adapter,
+ /* Initialize an v4l2_subdev with data from an i2c_client struct */
+ void v4l2_i2c_subdev_init(struct v4l2_subdev *sd, struct i2c_client *client,
+ const struct v4l2_subdev_ops *ops);
++/* Return i2c client address of v4l2_subdev. */
++unsigned short v4l2_i2c_subdev_addr(struct v4l2_subdev *sd);
++
++enum v4l2_i2c_tuner_type {
++ ADDRS_RADIO, /* Radio tuner addresses */
++ ADDRS_DEMOD, /* Demod tuner addresses */
++ ADDRS_TV, /* TV tuner addresses */
++ /* TV tuner addresses if demod is present, this excludes
++ addresses used by the demodulator from the list of
++ candidates. */
++ ADDRS_TV_WITH_DEMOD,
++};
++/* Return a list of I2C tuner addresses to probe. Use only if the tuner
++ addresses are unknown. */
++const unsigned short *v4l2_i2c_tuner_addrs(enum v4l2_i2c_tuner_type type);
+
+ /* ------------------------------------------------------------------------- */
+
+@@ -284,4 +303,7 @@ struct v4l2_crystal_freq {
+ a v4l2_gpio struct if a direction is also needed. */
+ #define VIDIOC_INT_S_GPIO _IOW('d', 117, u32)
+
++/* Get input status. Same as the status field in the v4l2_input struct. */
++#define VIDIOC_INT_G_INPUT_STATUS _IOR('d', 118, u32)
++
+ #endif /* V4L2_COMMON_H_ */
+diff --git a/include/media/v4l2-dev.h b/include/media/v4l2-dev.h
+index e36faab..2058dd4 100644
+--- a/include/media/v4l2-dev.h
++++ b/include/media/v4l2-dev.h
+@@ -40,6 +40,8 @@ struct v4l2_file_operations {
+ unsigned int (*poll) (struct file *, struct poll_table_struct *);
+ long (*ioctl) (struct file *, unsigned int, unsigned long);
+ long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
++ unsigned long (*get_unmapped_area) (struct file *, unsigned long,
++ unsigned long, unsigned long, unsigned long);
+ int (*mmap) (struct file *, struct vm_area_struct *);
+ int (*open) (struct file *);
+ int (*release) (struct file *);
+diff --git a/include/media/v4l2-device.h b/include/media/v4l2-device.h
+index 55e41af..0dd3e8e 100644
+--- a/include/media/v4l2-device.h
++++ b/include/media/v4l2-device.h
+@@ -33,7 +33,9 @@
+ #define V4L2_DEVICE_NAME_SIZE (BUS_ID_SIZE + 16)
+
+ struct v4l2_device {
+- /* dev->driver_data points to this struct */
++ /* dev->driver_data points to this struct.
++ Note: dev might be NULL if there is no parent device
++ as is the case with e.g. ISA devices. */
+ struct device *dev;
+ /* used to keep track of the registered subdevs */
+ struct list_head subdevs;
+@@ -42,33 +44,43 @@ struct v4l2_device {
+ spinlock_t lock;
+ /* unique device name, by default the driver name + bus ID */
+ char name[V4L2_DEVICE_NAME_SIZE];
++ /* notify callback called by some sub-devices. */
++ void (*notify)(struct v4l2_subdev *sd,
++ unsigned int notification, void *arg);
+ };
+
+-/* Initialize v4l2_dev and make dev->driver_data point to v4l2_dev */
++/* Initialize v4l2_dev and make dev->driver_data point to v4l2_dev.
++ dev may be NULL in rare cases (ISA devices). In that case you
++ must fill in the v4l2_dev->name field before calling this function. */
+ int __must_check v4l2_device_register(struct device *dev, struct v4l2_device *v4l2_dev);
+-/* Set v4l2_dev->dev->driver_data to NULL and unregister all sub-devices */
++/* Set v4l2_dev->dev to NULL. Call when the USB parent disconnects.
++ Since the parent disappears this ensures that v4l2_dev doesn't have an
++ invalid parent pointer. */
++void v4l2_device_disconnect(struct v4l2_device *v4l2_dev);
++/* Unregister all sub-devices and any other resources related to v4l2_dev. */
+ void v4l2_device_unregister(struct v4l2_device *v4l2_dev);
+
+ /* Register a subdev with a v4l2 device. While registered the subdev module
+ is marked as in-use. An error is returned if the module is no longer
+ loaded when you attempt to register it. */
+-int __must_check v4l2_device_register_subdev(struct v4l2_device *dev, struct v4l2_subdev *sd);
++int __must_check v4l2_device_register_subdev(struct v4l2_device *v4l2_dev,
++ struct v4l2_subdev *sd);
+ /* Unregister a subdev with a v4l2 device. Can also be called if the subdev
+ wasn't registered. In that case it will do nothing. */
+ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd);
+
+ /* Iterate over all subdevs. */
+-#define v4l2_device_for_each_subdev(sd, dev) \
+- list_for_each_entry(sd, &(dev)->subdevs, list)
++#define v4l2_device_for_each_subdev(sd, v4l2_dev) \
++ list_for_each_entry(sd, &(v4l2_dev)->subdevs, list)
+
+ /* Call the specified callback for all subdevs matching the condition.
+ Ignore any errors. Note that you cannot add or delete a subdev
+ while walking the subdevs list. */
+-#define __v4l2_device_call_subdevs(dev, cond, o, f, args...) \
++#define __v4l2_device_call_subdevs(v4l2_dev, cond, o, f, args...) \
+ do { \
+ struct v4l2_subdev *sd; \
+ \
+- list_for_each_entry(sd, &(dev)->subdevs, list) \
++ list_for_each_entry(sd, &(v4l2_dev)->subdevs, list) \
+ if ((cond) && sd->ops->o && sd->ops->o->f) \
+ sd->ops->o->f(sd , ##args); \
+ } while (0)
+@@ -77,12 +89,12 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd);
+ If the callback returns an error other than 0 or -ENOIOCTLCMD, then
+ return with that error code. Note that you cannot add or delete a
+ subdev while walking the subdevs list. */
+-#define __v4l2_device_call_subdevs_until_err(dev, cond, o, f, args...) \
++#define __v4l2_device_call_subdevs_until_err(v4l2_dev, cond, o, f, args...) \
+ ({ \
+ struct v4l2_subdev *sd; \
+ long err = 0; \
+ \
+- list_for_each_entry(sd, &(dev)->subdevs, list) { \
++ list_for_each_entry(sd, &(v4l2_dev)->subdevs, list) { \
+ if ((cond) && sd->ops->o && sd->ops->o->f) \
+ err = sd->ops->o->f(sd , ##args); \
+ if (err && err != -ENOIOCTLCMD) \
+@@ -94,16 +106,16 @@ void v4l2_device_unregister_subdev(struct v4l2_subdev *sd);
+ /* Call the specified callback for all subdevs matching grp_id (if 0, then
+ match them all). Ignore any errors. Note that you cannot add or delete
+ a subdev while walking the subdevs list. */
+-#define v4l2_device_call_all(dev, grpid, o, f, args...) \
+- __v4l2_device_call_subdevs(dev, \
++#define v4l2_device_call_all(v4l2_dev, grpid, o, f, args...) \
++ __v4l2_device_call_subdevs(v4l2_dev, \
+ !(grpid) || sd->grp_id == (grpid), o, f , ##args)
+
+ /* Call the specified callback for all subdevs matching grp_id (if 0, then
+ match them all). If the callback returns an error other than 0 or
+ -ENOIOCTLCMD, then return with that error code. Note that you cannot
+ add or delete a subdev while walking the subdevs list. */
+-#define v4l2_device_call_until_err(dev, grpid, o, f, args...) \
+- __v4l2_device_call_subdevs_until_err(dev, \
++#define v4l2_device_call_until_err(v4l2_dev, grpid, o, f, args...) \
++ __v4l2_device_call_subdevs_until_err(v4l2_dev, \
+ !(grpid) || sd->grp_id == (grpid), o, f , ##args)
+
+ #endif
+diff --git a/include/media/v4l2-ioctl.h b/include/media/v4l2-ioctl.h
+index b01c044..7a4529d 100644
+--- a/include/media/v4l2-ioctl.h
++++ b/include/media/v4l2-ioctl.h
+@@ -15,6 +15,7 @@
+ #include <linux/mutex.h>
+ #include <linux/compiler.h> /* need __user */
+ #ifdef CONFIG_VIDEO_V4L1_COMPAT
++#define __MIN_V4L1
+ #include <linux/videodev.h>
+ #else
+ #include <linux/videodev2.h>
+@@ -267,6 +268,7 @@ struct v4l2_ioctl_ops {
+
+ /* Video standard functions */
+ extern const char *v4l2_norm_to_name(v4l2_std_id id);
++extern void v4l2_video_std_frame_period(int id, struct v4l2_fract *frameperiod);
+ extern int v4l2_video_std_construct(struct v4l2_standard *vs,
+ int id, const char *name);
+ /* Prints the ioctl in a human-readable format */
+diff --git a/include/media/v4l2-subdev.h b/include/media/v4l2-subdev.h
+index 37b09e5..1d181b4 100644
+--- a/include/media/v4l2-subdev.h
++++ b/include/media/v4l2-subdev.h
+@@ -78,6 +78,9 @@ struct v4l2_subdev_core_ops {
+ int (*queryctrl)(struct v4l2_subdev *sd, struct v4l2_queryctrl *qc);
+ int (*g_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
+ int (*s_ctrl)(struct v4l2_subdev *sd, struct v4l2_control *ctrl);
++ int (*g_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);
++ int (*s_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);
++ int (*try_ext_ctrls)(struct v4l2_subdev *sd, struct v4l2_ext_controls *ctrls);
+ int (*querymenu)(struct v4l2_subdev *sd, struct v4l2_querymenu *qm);
+ long (*ioctl)(struct v4l2_subdev *sd, unsigned int cmd, void *arg);
+ #ifdef CONFIG_VIDEO_ADV_DEBUG
+@@ -112,9 +115,17 @@ struct v4l2_subdev_video_ops {
+ int (*g_vbi_data)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_data *vbi_data);
+ int (*g_sliced_vbi_cap)(struct v4l2_subdev *sd, struct v4l2_sliced_vbi_cap *cap);
+ int (*s_std_output)(struct v4l2_subdev *sd, v4l2_std_id std);
++ int (*querystd)(struct v4l2_subdev *sd, v4l2_std_id *std);
++ int (*g_input_status)(struct v4l2_subdev *sd, u32 *status);
+ int (*s_stream)(struct v4l2_subdev *sd, int enable);
+- int (*s_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt);
++ int (*enum_fmt)(struct v4l2_subdev *sd, struct v4l2_fmtdesc *fmtdesc);
+ int (*g_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt);
++ int (*try_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt);
++ int (*s_fmt)(struct v4l2_subdev *sd, struct v4l2_format *fmt);
++ int (*g_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);
++ int (*s_parm)(struct v4l2_subdev *sd, struct v4l2_streamparm *param);
++ int (*enum_framesizes)(struct v4l2_subdev *sd, struct v4l2_frmsizeenum *fsize);
++ int (*enum_frameintervals)(struct v4l2_subdev *sd, struct v4l2_frmivalenum *fival);
+ };
+
+ struct v4l2_subdev_ops {
+@@ -132,7 +143,7 @@ struct v4l2_subdev_ops {
+ struct v4l2_subdev {
+ struct list_head list;
+ struct module *owner;
+- struct v4l2_device *dev;
++ struct v4l2_device *v4l2_dev;
+ const struct v4l2_subdev_ops *ops;
+ /* name must be unique */
+ char name[V4L2_SUBDEV_NAME_SIZE];
+@@ -171,7 +182,7 @@ static inline void v4l2_subdev_init(struct v4l2_subdev *sd,
+ /* ops->core MUST be set */
+ BUG_ON(!ops || !ops->core);
+ sd->ops = ops;
+- sd->dev = NULL;
++ sd->v4l2_dev = NULL;
+ sd->name[0] = '\0';
+ sd->grp_id = 0;
+ sd->priv = NULL;
+@@ -186,4 +197,9 @@ static inline void v4l2_subdev_init(struct v4l2_subdev *sd,
+ (!(sd) ? -ENODEV : (((sd) && (sd)->ops->o && (sd)->ops->o->f) ? \
+ (sd)->ops->o->f((sd) , ##args) : -ENOIOCTLCMD))
+
++/* Send a notification to v4l2_device. */
++#define v4l2_subdev_notify(sd, notification, arg) \
++ ((!(sd) || !(sd)->v4l2_dev || !(sd)->v4l2_dev->notify) ? -ENODEV : \
++ (sd)->v4l2_dev->notify((sd), (notification), (arg)))
++
+ #endif
+diff --git a/include/media/videobuf-core.h b/include/media/videobuf-core.h
+index 874f134..1c5946c 100644
+--- a/include/media/videobuf-core.h
++++ b/include/media/videobuf-core.h
+@@ -18,6 +18,7 @@
+
+ #include <linux/poll.h>
+ #ifdef CONFIG_VIDEO_V4L1_COMPAT
++#define __MIN_V4L1
+ #include <linux/videodev.h>
+ #endif
+ #include <linux/videodev2.h>
+diff --git a/include/sound/tea575x-tuner.h b/include/sound/tea575x-tuner.h
+index 426899e..5718a02 100644
+--- a/include/sound/tea575x-tuner.h
++++ b/include/sound/tea575x-tuner.h
+@@ -22,8 +22,9 @@
+ *
+ */
+
+-#include <linux/videodev.h>
++#include <linux/videodev2.h>
+ #include <media/v4l2-dev.h>
++#include <media/v4l2-ioctl.h>
+
+ struct snd_tea575x;
+
+@@ -35,11 +36,10 @@ struct snd_tea575x_ops {
+
+ struct snd_tea575x {
+ struct snd_card *card;
+- struct video_device vd; /* video device */
+- struct v4l2_file_operations fops;
++ struct video_device *vd; /* video device */
+ int dev_nr; /* requested device number + 1 */
+- int vd_registered; /* video device is registered */
+ int tea5759; /* 5759 chip is present */
++ int mute; /* Device is muted? */
+ unsigned int freq_fixup; /* crystal onboard */
+ unsigned int val; /* hw value */
+ unsigned long freq; /* frequency */
+diff --git a/sound/i2c/other/tea575x-tuner.c b/sound/i2c/other/tea575x-tuner.c
+index 9d98a66..d31c373 100644
+--- a/sound/i2c/other/tea575x-tuner.c
++++ b/sound/i2c/other/tea575x-tuner.c
+@@ -24,6 +24,7 @@
+ #include <linux/delay.h>
+ #include <linux/interrupt.h>
+ #include <linux/init.h>
++#include <linux/version.h>
+ #include <sound/core.h>
+ #include <sound/tea575x-tuner.h>
+
+@@ -31,6 +32,13 @@ MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
+ MODULE_DESCRIPTION("Routines for control of TEA5757/5759 Philips AM/FM radio tuner chips");
+ MODULE_LICENSE("GPL");
+
++static int radio_nr = -1;
++module_param(radio_nr, int, 0);
++
++#define RADIO_VERSION KERNEL_VERSION(0, 0, 2)
++#define FREQ_LO (87 * 16000)
++#define FREQ_HI (108 * 16000)
++
+ /*
+ * definitions
+ */
+@@ -53,6 +61,17 @@ MODULE_LICENSE("GPL");
+ #define TEA575X_BIT_DUMMY (1<<15) /* buffer */
+ #define TEA575X_BIT_FREQ_MASK 0x7fff
+
++static struct v4l2_queryctrl radio_qctrl[] = {
++ {
++ .id = V4L2_CID_AUDIO_MUTE,
++ .name = "Mute",
++ .minimum = 0,
++ .maximum = 1,
++ .default_value = 1,
++ .type = V4L2_CTRL_TYPE_BOOLEAN,
++ }
++};
++
+ /*
+ * lowlevel part
+ */
+@@ -84,94 +103,146 @@ static void snd_tea575x_set_freq(struct snd_tea575x *tea)
+ * Linux Video interface
+ */
+
+-static long snd_tea575x_ioctl(struct file *file,
+- unsigned int cmd, unsigned long data)
++static int vidioc_querycap(struct file *file, void *priv,
++ struct v4l2_capability *v)
+ {
+ struct snd_tea575x *tea = video_drvdata(file);
+- void __user *arg = (void __user *)data;
+-
+- switch(cmd) {
+- case VIDIOCGCAP:
+- {
+- struct video_capability v;
+- v.type = VID_TYPE_TUNER;
+- v.channels = 1;
+- v.audios = 1;
+- /* No we don't do pictures */
+- v.maxwidth = 0;
+- v.maxheight = 0;
+- v.minwidth = 0;
+- v.minheight = 0;
+- strcpy(v.name, tea->tea5759 ? "TEA5759" : "TEA5757");
+- if (copy_to_user(arg,&v,sizeof(v)))
+- return -EFAULT;
+- return 0;
+- }
+- case VIDIOCGTUNER:
+- {
+- struct video_tuner v;
+- if (copy_from_user(&v, arg,sizeof(v))!=0)
+- return -EFAULT;
+- if (v.tuner) /* Only 1 tuner */
+- return -EINVAL;
+- v.rangelow = (87*16000);
+- v.rangehigh = (108*16000);
+- v.flags = VIDEO_TUNER_LOW;
+- v.mode = VIDEO_MODE_AUTO;
+- strcpy(v.name, "FM");
+- v.signal = 0xFFFF;
+- if (copy_to_user(arg, &v, sizeof(v)))
+- return -EFAULT;
+- return 0;
+- }
+- case VIDIOCSTUNER:
+- {
+- struct video_tuner v;
+- if(copy_from_user(&v, arg, sizeof(v)))
+- return -EFAULT;
+- if(v.tuner!=0)
+- return -EINVAL;
+- /* Only 1 tuner so no setting needed ! */
++
++ strcpy(v->card, tea->tea5759 ? "TEA5759" : "TEA5757");
++ strlcpy(v->driver, "tea575x-tuner", sizeof(v->driver));
++ strlcpy(v->card, "Maestro Radio", sizeof(v->card));
++ sprintf(v->bus_info, "PCI");
++ v->version = RADIO_VERSION;
++ v->capabilities = V4L2_CAP_TUNER;
++ return 0;
++}
++
++static int vidioc_g_tuner(struct file *file, void *priv,
++ struct v4l2_tuner *v)
++{
++ if (v->index > 0)
++ return -EINVAL;
++
++ strcpy(v->name, "FM");
++ v->type = V4L2_TUNER_RADIO;
++ v->rangelow = FREQ_LO;
++ v->rangehigh = FREQ_HI;
++ v->rxsubchans = V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
++ v->capability = V4L2_TUNER_CAP_LOW;
++ v->audmode = V4L2_TUNER_MODE_MONO;
++ v->signal = 0xffff;
++ return 0;
++}
++
++static int vidioc_s_tuner(struct file *file, void *priv,
++ struct v4l2_tuner *v)
++{
++ if (v->index > 0)
++ return -EINVAL;
++ return 0;
++}
++
++static int vidioc_g_frequency(struct file *file, void *priv,
++ struct v4l2_frequency *f)
++{
++ struct snd_tea575x *tea = video_drvdata(file);
++
++ f->type = V4L2_TUNER_RADIO;
++ f->frequency = tea->freq;
++ return 0;
++}
++
++static int vidioc_s_frequency(struct file *file, void *priv,
++ struct v4l2_frequency *f)
++{
++ struct snd_tea575x *tea = video_drvdata(file);
++
++ if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
++ return -EINVAL;
++
++ tea->freq = f->frequency;
++
++ snd_tea575x_set_freq(tea);
++
++ return 0;
++}
++
++static int vidioc_g_audio(struct file *file, void *priv,
++ struct v4l2_audio *a)
++{
++ if (a->index > 1)
++ return -EINVAL;
++
++ strcpy(a->name, "Radio");
++ a->capability = V4L2_AUDCAP_STEREO;
++ return 0;
++}
++
++static int vidioc_s_audio(struct file *file, void *priv,
++ struct v4l2_audio *a)
++{
++ if (a->index != 0)
++ return -EINVAL;
++ return 0;
++}
++
++static int vidioc_queryctrl(struct file *file, void *priv,
++ struct v4l2_queryctrl *qc)
++{
++ int i;
++
++ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
++ if (qc->id && qc->id == radio_qctrl[i].id) {
++ memcpy(qc, &(radio_qctrl[i]),
++ sizeof(*qc));
+ return 0;
+ }
+- case VIDIOCGFREQ:
+- if(copy_to_user(arg, &tea->freq, sizeof(tea->freq)))
+- return -EFAULT;
+- return 0;
+- case VIDIOCSFREQ:
+- if(copy_from_user(&tea->freq, arg, sizeof(tea->freq)))
+- return -EFAULT;
+- snd_tea575x_set_freq(tea);
+- return 0;
+- case VIDIOCGAUDIO:
+- {
+- struct video_audio v;
+- memset(&v, 0, sizeof(v));
+- strcpy(v.name, "Radio");
+- if(copy_to_user(arg,&v, sizeof(v)))
+- return -EFAULT;
++ }
++ return -EINVAL;
++}
++
++static int vidioc_g_ctrl(struct file *file, void *priv,
++ struct v4l2_control *ctrl)
++{
++ struct snd_tea575x *tea = video_drvdata(file);
++
++ switch (ctrl->id) {
++ case V4L2_CID_AUDIO_MUTE:
++ if (tea->ops->mute) {
++ ctrl->value = tea->mute;
+ return 0;
+ }
+- case VIDIOCSAUDIO:
+- {
+- struct video_audio v;
+- if(copy_from_user(&v, arg, sizeof(v)))
+- return -EFAULT;
+- if (tea->ops->mute)
+- tea->ops->mute(tea,
+- (v.flags &
+- VIDEO_AUDIO_MUTE) ? 1 : 0);
+- if(v.audio)
+- return -EINVAL;
++ }
++ return -EINVAL;
++}
++
++static int vidioc_s_ctrl(struct file *file, void *priv,
++ struct v4l2_control *ctrl)
++{
++ struct snd_tea575x *tea = video_drvdata(file);
++
++ switch (ctrl->id) {
++ case V4L2_CID_AUDIO_MUTE:
++ if (tea->ops->mute) {
++ tea->ops->mute(tea, ctrl->value);
++ tea->mute = 1;
+ return 0;
+ }
+- default:
+- return -ENOIOCTLCMD;
+ }
++ return -EINVAL;
++}
++
++static int vidioc_g_input(struct file *filp, void *priv, unsigned int *i)
++{
++ *i = 0;
++ return 0;
+ }
+
+-static void snd_tea575x_release(struct video_device *vfd)
++static int vidioc_s_input(struct file *filp, void *priv, unsigned int i)
+ {
++ if (i != 0)
++ return -EINVAL;
++ return 0;
+ }
+
+ static int snd_tea575x_exclusive_open(struct file *file)
+@@ -189,50 +260,91 @@ static int snd_tea575x_exclusive_release(struct file *file)
+ return 0;
+ }
+
++static const struct v4l2_file_operations tea575x_fops = {
++ .owner = THIS_MODULE,
++ .open = snd_tea575x_exclusive_open,
++ .release = snd_tea575x_exclusive_release,
++ .ioctl = video_ioctl2,
++};
++
++static const struct v4l2_ioctl_ops tea575x_ioctl_ops = {
++ .vidioc_querycap = vidioc_querycap,
++ .vidioc_g_tuner = vidioc_g_tuner,
++ .vidioc_s_tuner = vidioc_s_tuner,
++ .vidioc_g_audio = vidioc_g_audio,
++ .vidioc_s_audio = vidioc_s_audio,
++ .vidioc_g_input = vidioc_g_input,
++ .vidioc_s_input = vidioc_s_input,
++ .vidioc_g_frequency = vidioc_g_frequency,
++ .vidioc_s_frequency = vidioc_s_frequency,
++ .vidioc_queryctrl = vidioc_queryctrl,
++ .vidioc_g_ctrl = vidioc_g_ctrl,
++ .vidioc_s_ctrl = vidioc_s_ctrl,
++};
++
++static struct video_device tea575x_radio = {
++ .name = "tea575x-tuner",
++ .fops = &tea575x_fops,
++ .ioctl_ops = &tea575x_ioctl_ops,
++ .release = video_device_release,
++};
++
+ /*
+ * initialize all the tea575x chips
+ */
+ void snd_tea575x_init(struct snd_tea575x *tea)
+ {
++ int retval;
+ unsigned int val;
++ struct video_device *tea575x_radio_inst;
+
+ val = tea->ops->read(tea);
+ if (val == 0x1ffffff || val == 0) {
+- snd_printk(KERN_ERR "Cannot find TEA575x chip\n");
++ snd_printk(KERN_ERR
++ "tea575x-tuner: Cannot find TEA575x chip\n");
+ return;
+ }
+
+- memset(&tea->vd, 0, sizeof(tea->vd));
+- strcpy(tea->vd.name, tea->tea5759 ? "TEA5759 radio" : "TEA5757 radio");
+- tea->vd.release = snd_tea575x_release;
+- video_set_drvdata(&tea->vd, tea);
+- tea->vd.fops = &tea->fops;
+ tea->in_use = 0;
+- tea->fops.owner = tea->card->module;
+- tea->fops.open = snd_tea575x_exclusive_open;
+- tea->fops.release = snd_tea575x_exclusive_release;
+- tea->fops.ioctl = snd_tea575x_ioctl;
+- if (video_register_device(&tea->vd, VFL_TYPE_RADIO, tea->dev_nr - 1) < 0) {
+- snd_printk(KERN_ERR "unable to register tea575x tuner\n");
++ tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
++ tea->freq = 90500 * 16; /* 90.5Mhz default */
++
++ tea575x_radio_inst = video_device_alloc();
++ if (tea575x_radio_inst == NULL) {
++ printk(KERN_ERR "tea575x-tuner: not enough memory\n");
+ return;
+ }
+- tea->vd_registered = 1;
+
+- tea->val = TEA575X_BIT_BAND_FM | TEA575X_BIT_SEARCH_10_40;
+- tea->freq = 90500 * 16; /* 90.5Mhz default */
++ memcpy(tea575x_radio_inst, &tea575x_radio, sizeof(tea575x_radio));
++
++ strcpy(tea575x_radio.name, tea->tea5759 ?
++ "TEA5759 radio" : "TEA5757 radio");
++
++ video_set_drvdata(tea575x_radio_inst, tea);
++
++ retval = video_register_device(tea575x_radio_inst,
++ VFL_TYPE_RADIO, radio_nr);
++ if (retval) {
++ printk(KERN_ERR "tea575x-tuner: can't register video device!\n");
++ kfree(tea575x_radio_inst);
++ return;
++ }
+
+ snd_tea575x_set_freq(tea);
+
+ /* mute on init */
+- if (tea->ops->mute)
++ if (tea->ops->mute) {
+ tea->ops->mute(tea, 1);
++ tea->mute = 1;
++ }
++ tea->vd = tea575x_radio_inst;
+ }
+
+ void snd_tea575x_exit(struct snd_tea575x *tea)
+ {
+- if (tea->vd_registered) {
+- video_unregister_device(&tea->vd);
+- tea->vd_registered = 0;
++ if (tea->vd) {
++ video_unregister_device(tea->vd);
++ tea->vd = NULL;
+ }
+ }
+
+diff --git a/sound/pci/Kconfig b/sound/pci/Kconfig
+index 82b9bdd..6cc18bf 100644
+--- a/sound/pci/Kconfig
++++ b/sound/pci/Kconfig
+@@ -487,7 +487,7 @@ config SND_FM801
+ config SND_FM801_TEA575X_BOOL
+ bool "ForteMedia FM801 + TEA5757 tuner"
+ depends on SND_FM801
+- depends on VIDEO_V4L1=y || VIDEO_V4L1=SND_FM801
++ depends on VIDEO_V4L2=y || VIDEO_V4L2=SND_FM801
+ help
+ Say Y here to include support for soundcards based on the ForteMedia
+ FM801 chip with a TEA5757 tuner connected to GPIO1-3 pins (Media
+--- linux-2.6.30.noarch/drivers/media/video/hdpvr/hdpvr-i2c.c~ 2009-07-09 21:56:58.000000000 -0400
++++ linux-2.6.30.noarch/drivers/media/video/hdpvr/hdpvr-i2c.c 2009-07-09 21:57:34.000000000 -0400
+@@ -64,7 +64,7 @@ static int hdpvr_i2c_write(struct hdpvr_
+
+ ret = usb_control_msg(dev->udev,
+ usb_rcvctrlpipe(dev->udev, 0),
+- REQTYPE_I2C_WRITE_STAT, CTRL_READ_REQUEST,
++ REQTYPE_I2C_WRITE_STATT, CTRL_READ_REQUEST,
+ 0, 0, buf, 2, 1000);
+
+ if (ret == 2)
diff --git a/linux-2.6-v4l-dvb-rebase-gspca-to-latest.patch b/linux-2.6-v4l-dvb-rebase-gspca-to-latest.patch
new file mode 100644
index 0000000..6b325b5
--- /dev/null
+++ b/linux-2.6-v4l-dvb-rebase-gspca-to-latest.patch
@@ -0,0 +1,23900 @@
+diff --git a/Documentation/video4linux/gspca.txt b/Documentation/video4linux/gspca.txt
+index 90e85a8..939dda9 100644
+--- a/Documentation/video4linux/gspca.txt
++++ b/Documentation/video4linux/gspca.txt
+@@ -6,11 +6,13 @@ The modules are:
+
+ xxxx vend:prod
+ ----
+-spca501 0000:0000 MystFromOri Unknow Camera
++spca501 0000:0000 MystFromOri Unknown Camera
++spca508 0130:0130 Clone Digital Webcam 11043
+ m5602 0402:5602 ALi Video Camera Controller
+ spca501 040a:0002 Kodak DVC-325
+ spca500 040a:0300 Kodak EZ200
+ zc3xx 041e:041e Creative WebCam Live!
++ov519 041e:4003 Video Blaster WebCam Go Plus
+ spca500 041e:400a Creative PC-CAM 300
+ sunplus 041e:400b Creative PC-CAM 600
+ sunplus 041e:4012 PC-Cam350
+@@ -40,6 +42,7 @@ ov519 041e:4064 Creative Live! VISTA VF0420
+ ov519 041e:4067 Creative Live! Cam Video IM (VF0350)
+ ov519 041e:4068 Creative Live! VISTA VF0470
+ spca561 0458:7004 Genius VideoCAM Express V2
++sn9c2028 0458:7005 Genius Smart 300, version 2
+ sunplus 0458:7006 Genius Dsc 1.3 Smart
+ zc3xx 0458:7007 Genius VideoCam V2
+ zc3xx 0458:700c Genius VideoCam V3
+@@ -69,12 +72,12 @@ zc3xx 046d:08a3 Logitech QC Chat
+ zc3xx 046d:08a6 Logitech QCim
+ zc3xx 046d:08a7 Logitech QuickCam Image
+ zc3xx 046d:08a9 Logitech Notebook Deluxe
+-zc3xx 046d:08aa Labtec Webcam Notebook
++zc3xx 046d:08aa Labtec Webcam Notebook
+ zc3xx 046d:08ac Logitech QuickCam Cool
+ zc3xx 046d:08ad Logitech QCCommunicate STX
+ zc3xx 046d:08ae Logitech QuickCam for Notebooks
+ zc3xx 046d:08af Logitech QuickCam Cool
+-zc3xx 046d:08b9 Logitech QC IM ???
++zc3xx 046d:08b9 Logitech QuickCam Express
+ zc3xx 046d:08d7 Logitech QCam STX
+ zc3xx 046d:08d9 Logitech QuickCam IM/Connect
+ zc3xx 046d:08d8 Logitech Notebook Deluxe
+@@ -83,7 +86,7 @@ zc3xx 046d:08dd Logitech QuickCam for Notebooks
+ spca500 046d:0900 Logitech Inc. ClickSmart 310
+ spca500 046d:0901 Logitech Inc. ClickSmart 510
+ sunplus 046d:0905 Logitech ClickSmart 820
+-tv8532 046d:0920 QC Express
++tv8532 046d:0920 Logitech QuickCam Express
+ tv8532 046d:0921 Labtec Webcam
+ spca561 046d:0928 Logitech QC Express Etch2
+ spca561 046d:0929 Labtec Webcam Elch2
+@@ -92,7 +95,7 @@ spca561 046d:092b Labtec Webcam Plus
+ spca561 046d:092c Logitech QC chat Elch2
+ spca561 046d:092d Logitech QC Elch2
+ spca561 046d:092e Logitech QC Elch2
+-spca561 046d:092f Logitech QuickCam Express Plus
++spca561 046d:092f Logitech QuickCam Express Plus
+ sunplus 046d:0960 Logitech ClickSmart 420
+ sunplus 0471:0322 Philips DMVC1300K
+ zc3xx 0471:0325 Philips SPC 200 NC
+@@ -107,6 +110,7 @@ sunplus 04a5:3003 Benq DC 1300
+ sunplus 04a5:3008 Benq DC 1500
+ sunplus 04a5:300a Benq DC 3410
+ spca500 04a5:300c Benq DC 1016
++benq 04a5:3035 Benq DC E300
+ finepix 04cb:0104 Fujifilm FinePix 4800
+ finepix 04cb:0109 Fujifilm FinePix A202
+ finepix 04cb:010b Fujifilm FinePix A203
+@@ -140,6 +144,7 @@ sunplus 04fc:5360 Sunplus Generic
+ spca500 04fc:7333 PalmPixDC85
+ sunplus 04fc:ffff Pure DigitalDakota
+ spca501 0506:00df 3Com HomeConnect Lite
++sunplus 052b:1507 Megapixel 5 Pretec DC-1007
+ sunplus 052b:1513 Megapix V4
+ sunplus 052b:1803 MegaImage VI
+ tv8532 0545:808b Veo Stingray
+@@ -149,6 +154,7 @@ sunplus 0546:3191 Polaroid Ion 80
+ sunplus 0546:3273 Polaroid PDC2030
+ ov519 054c:0154 Sonny toy4
+ ov519 054c:0155 Sonny toy5
++cpia1 0553:0002 CPIA CPiA (version1) based cameras
+ zc3xx 055f:c005 Mustek Wcam300A
+ spca500 055f:c200 Mustek Gsmart 300
+ sunplus 055f:c211 Kowa Bs888e Microcamera
+@@ -167,10 +173,14 @@ sunplus 055f:c650 Mustek MDC5500Z
+ zc3xx 055f:d003 Mustek WCam300A
+ zc3xx 055f:d004 Mustek WCam300 AN
+ conex 0572:0041 Creative Notebook cx11646
++ov519 05a9:0511 Video Blaster WebCam 3/WebCam Plus, D-Link USB Digital Video Camera
++ov519 05a9:0518 Creative WebCam
+ ov519 05a9:0519 OV519 Microphone
+ ov519 05a9:0530 OmniVision
++ov519 05a9:2800 OmniVision SuperCAM
+ ov519 05a9:4519 Webcam Classic
+ ov519 05a9:8519 OmniVision
++ov519 05a9:a511 D-Link USB Digital Video Camera
+ ov519 05a9:a518 D-Link DSB-C310 Webcam
+ sunplus 05da:1018 Digital Dream Enigma 1.3
+ stk014 05e1:0893 Syntek DV4000
+@@ -182,13 +192,11 @@ spca500 06bd:0404 Agfa CL20
+ spca500 06be:0800 Optimedia
+ sunplus 06d6:0031 Trust 610 LCD PowerC@m Zoom
+ spca506 06e1:a190 ADS Instant VCD
+-ov534 06f8:3002 Hercules Blog Webcam
+-ov534 06f8:3003 Hercules Dualpix HD Weblog
++ov534_9 06f8:3003 Hercules Dualpix HD Weblog
+ sonixj 06f8:3004 Hercules Classic Silver
+ sonixj 06f8:3008 Hercules Deluxe Optical Glass
+-pac7311 06f8:3009 Hercules Classic Link
++pac7302 06f8:3009 Hercules Classic Link
+ spca508 0733:0110 ViewQuest VQ110
+-spca508 0130:0130 Clone Digital Webcam 11043
+ spca501 0733:0401 Intel Create and Share
+ spca501 0733:0402 ViewQuest M318B
+ spca505 0733:0430 Intel PC Camera Pro
+@@ -199,10 +207,13 @@ sunplus 0733:2221 Mercury Digital Pro 3.1p
+ sunplus 0733:3261 Concord 3045 spca536a
+ sunplus 0733:3281 Cyberpix S550V
+ spca506 0734:043b 3DeMon USB Capture aka
++cpia1 0813:0001 QX3 camera
++ov519 0813:0002 Dual Mode USB Camera Plus
+ spca500 084d:0003 D-Link DSC-350
+ spca500 08ca:0103 Aiptek PocketDV
+ sunplus 08ca:0104 Aiptek PocketDVII 1.3
+ sunplus 08ca:0106 Aiptek Pocket DV3100+
++mr97310a 08ca:0110 Trust Spyc@m 100
+ mr97310a 08ca:0111 Aiptek PenCam VGA+
+ sunplus 08ca:2008 Aiptek Mini PenCam 2 M
+ sunplus 08ca:2010 Aiptek PocketCam 3M
+@@ -218,12 +229,13 @@ sunplus 08ca:2050 Medion MD 41437
+ sunplus 08ca:2060 Aiptek PocketDV5300
+ tv8532 0923:010f ICM532 cams
+ mars 093a:050f Mars-Semi Pc-Camera
+-mr97310a 093a:010f Sakar Digital no. 77379
++mr97310a 093a:010e All four known CIF cams with this ID
++mr97310a 093a:010f All four known VGA cams with this ID
+ pac207 093a:2460 Qtec Webcam 100
+ pac207 093a:2461 HP Webcam
+ pac207 093a:2463 Philips SPC 220 NC
+ pac207 093a:2464 Labtec Webcam 1200
+-pac207 093a:2468 PAC207
++pac207 093a:2468 Webcam WB-1400T
+ pac207 093a:2470 Genius GF112
+ pac207 093a:2471 Genius VideoCam ge111
+ pac207 093a:2472 Genius VideoCam ge110
+@@ -231,18 +243,19 @@ pac207 093a:2474 Genius iLook 111
+ pac207 093a:2476 Genius e-Messenger 112
+ pac7311 093a:2600 PAC7311 Typhoon
+ pac7311 093a:2601 Philips SPC 610 NC
+-pac7311 093a:2603 PAC7312
++pac7311 093a:2603 Philips SPC 500 NC
+ pac7311 093a:2608 Trust WB-3300p
+ pac7311 093a:260e Gigaware VGA PC Camera, Trust WB-3350p, SIGMA cam 2350
+ pac7311 093a:260f SnakeCam
+-pac7311 093a:2620 Apollo AC-905
+-pac7311 093a:2621 PAC731x
+-pac7311 093a:2622 Genius Eye 312
+-pac7311 093a:2624 PAC7302
+-pac7311 093a:2626 Labtec 2200
+-pac7311 093a:2629 Genious iSlim 300
+-pac7311 093a:262a Webcam 300k
+-pac7311 093a:262c Philips SPC 230 NC
++pac7302 093a:2620 Apollo AC-905
++pac7302 093a:2621 PAC731x
++pac7302 093a:2622 Genius Eye 312
++pac7302 093a:2624 PAC7302
++pac7302 093a:2626 Labtec 2200
++pac7302 093a:2628 Genius iLook 300
++pac7302 093a:2629 Genious iSlim 300
++pac7302 093a:262a Webcam 300k
++pac7302 093a:262c Philips SPC 230 NC
+ jeilinj 0979:0280 Sakar 57379
+ zc3xx 0ac8:0302 Z-star Vimicro zc0302
+ vc032x 0ac8:0321 Vimicro generic vc0321
+@@ -251,12 +264,13 @@ vc032x 0ac8:0328 A4Tech PK-130MG
+ zc3xx 0ac8:301b Z-Star zc301b
+ zc3xx 0ac8:303b Vimicro 0x303b
+ zc3xx 0ac8:305b Z-star Vimicro zc0305b
+-zc3xx 0ac8:307b Ldlc VC302+Ov7620
++zc3xx 0ac8:307b PC Camera (ZS0211)
+ vc032x 0ac8:c001 Sony embedded vimicro
+ vc032x 0ac8:c002 Sony embedded vimicro
+ vc032x 0ac8:c301 Samsung Q1 Ultra Premium
+ spca508 0af9:0010 Hama USB Sightcam 100
+ spca508 0af9:0011 Hama USB Sightcam 100
++ov519 0b62:0059 iBOT2 Webcam
+ sonixb 0c45:6001 Genius VideoCAM NB
+ sonixb 0c45:6005 Microdia Sweex Mini Webcam
+ sonixb 0c45:6007 Sonix sn9c101 + Tas5110D
+@@ -293,6 +307,7 @@ sonixj 0c45:613b Surfer SN-206
+ sonixj 0c45:613c Sonix Pccam168
+ sonixj 0c45:6143 Sonix Pccam168
+ sonixj 0c45:6148 Digitus DA-70811/ZSMC USB PC Camera ZS211/Microdia
++sonixj 0c45:614a Frontech E-Ccam (JIL-2225)
+ sn9c20x 0c45:6240 PC Camera (SN9C201 + MT9M001)
+ sn9c20x 0c45:6242 PC Camera (SN9C201 + MT9M111)
+ sn9c20x 0c45:6248 PC Camera (SN9C201 + OV9655)
+@@ -315,9 +330,15 @@ sn9c20x 0c45:62b0 PC Camera (SN9C202 + MT9V011/MT9V111/MT9V112)
+ sn9c20x 0c45:62b3 PC Camera (SN9C202 + OV9655)
+ sn9c20x 0c45:62bb PC Camera (SN9C202 + OV7660)
+ sn9c20x 0c45:62bc PC Camera (SN9C202 + HV7131R)
++sn9c2028 0c45:8001 Wild Planet Digital Spy Camera
++sn9c2028 0c45:8003 Sakar #11199, #6637x, #67480 keychain cams
++sn9c2028 0c45:8008 Mini-Shotz ms-350
++sn9c2028 0c45:800a Vivitar Vivicam 3350B
+ sunplus 0d64:0303 Sunplus FashionCam DXG
++ov519 0e96:c001 TRUST 380 USB2 SPACEC@M
+ etoms 102c:6151 Qcam Sangha CIF
+ etoms 102c:6251 Qcam xxxxxx VGA
++ov519 1046:9967 W9967CF/W9968CF WebCam IC, Video Blaster WebCam Go
+ zc3xx 10fd:0128 Typhoon Webshot II USB 300k 0x0128
+ spca561 10fd:7e50 FlyCam Usb 100
+ zc3xx 10fd:8050 Typhoon Webshot II USB 300k
+@@ -330,7 +351,13 @@ spca501 1776:501c Arowana 300K CMOS Camera
+ t613 17a1:0128 TASCORP JPEG Webcam, NGS Cyclops
+ vc032x 17ef:4802 Lenovo Vc0323+MI1310_SOC
+ pac207 2001:f115 D-Link DSB-C120
++sq905c 2770:9050 Disney pix micro (CIF)
++sq905c 2770:9052 Disney pix micro 2 (VGA)
++sq905c 2770:905c All 11 known cameras with this ID
++sq905 2770:9120 All 24 known cameras with this ID
++sq905c 2770:913d All 4 known cameras with this ID
+ spca500 2899:012c Toptro Industrial
++ov519 8020:ef04 ov519
+ spca508 8086:0110 Intel Easy PC Camera
+ spca500 8086:0630 Intel Pocket PC Camera
+ spca506 99fa:8988 Grandtec V.cap
+diff --git a/drivers/media/video/gspca/Kconfig b/drivers/media/video/gspca/Kconfig
+index fe2e490..e0060c1 100644
+--- a/drivers/media/video/gspca/Kconfig
++++ b/drivers/media/video/gspca/Kconfig
+@@ -21,6 +21,15 @@ source "drivers/media/video/gspca/m5602/Kconfig"
+ source "drivers/media/video/gspca/stv06xx/Kconfig"
+ source "drivers/media/video/gspca/gl860/Kconfig"
+
++config USB_GSPCA_BENQ
++ tristate "Benq USB Camera Driver"
++ depends on VIDEO_V4L2 && USB_GSPCA
++ help
++ Say Y here if you want support for the Benq DC E300 camera.
++
++ To compile this driver as a module, choose M here: the
++ module will be called gspca_benq.
++
+ config USB_GSPCA_CONEX
+ tristate "Conexant Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+@@ -30,6 +39,17 @@ config USB_GSPCA_CONEX
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_conex.
+
++config USB_GSPCA_CPIA1
++ tristate "cpia CPiA (version 1) Camera Driver"
++ depends on VIDEO_V4L2 && USB_GSPCA
++ help
++ Say Y here if you want support for USB cameras based on the cpia
++ CPiA chip. Note that you need atleast version 0.6.4 of libv4l for
++ applications to understand the videoformat generated by this driver.
++
++ To compile this driver as a module, choose M here: the
++ module will be called gspca_cpia1.
++
+ config USB_GSPCA_ETOMS
+ tristate "Etoms USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+@@ -76,24 +96,35 @@ config USB_GSPCA_MR97310A
+ module will be called gspca_mr97310a.
+
+ config USB_GSPCA_OV519
+- tristate "OV519 USB Camera Driver"
++ tristate "OV51x / OVFX2 / W996xCF USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+- Say Y here if you want support for cameras based on the OV519 chip.
++ Say Y here if you want support for cameras based on one of these:
++ OV511(+), OV518(+), OV519, OVFX2, W9967CF, W9968CF
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_ov519.
+
+ config USB_GSPCA_OV534
+- tristate "OV534 USB Camera Driver"
++ tristate "OV534 OV772x USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+ help
+- Say Y here if you want support for cameras based on the OV534 chip.
+- (e.g. Sony Playstation EYE)
++ Say Y here if you want support for cameras based on the OV534 chip
++ and sensor OV772x (e.g. Sony Playstation EYE)
+
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_ov534.
+
++config USB_GSPCA_OV534_9
++ tristate "OV534 OV965x USB Camera Driver"
++ depends on VIDEO_V4L2 && USB_GSPCA
++ help
++ Say Y here if you want support for cameras based on the OV534 chip
++ and sensor OV965x (e.g. Hercules Dualpix)
++
++ To compile this driver as a module, choose M here: the
++ module will be called gspca_ov534_9.
++
+ config USB_GSPCA_PAC207
+ tristate "Pixart PAC207 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+@@ -103,6 +134,15 @@ config USB_GSPCA_PAC207
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_pac207.
+
++config USB_GSPCA_PAC7302
++ tristate "Pixart PAC7302 USB Camera Driver"
++ depends on VIDEO_V4L2 && USB_GSPCA
++ help
++ Say Y here if you want support for cameras based on the PAC7302 chip.
++
++ To compile this driver as a module, choose M here: the
++ module will be called gspca_pac7302.
++
+ config USB_GSPCA_PAC7311
+ tristate "Pixart PAC7311 USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+@@ -112,6 +152,16 @@ config USB_GSPCA_PAC7311
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_pac7311.
+
++config USB_GSPCA_SN9C2028
++ tristate "SONIX Dual-Mode USB Camera Driver"
++ depends on VIDEO_V4L2 && USB_GSPCA
++ help
++ Say Y here if you want streaming support for Sonix SN9C2028 cameras.
++ These are supported as stillcams in libgphoto2/camlibs/sonix.
++
++ To compile this driver as a module, choose M here: the
++ module will be called gspca_sn9c2028.
++
+ config USB_GSPCA_SN9C20X
+ tristate "SN9C20X USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+@@ -229,6 +279,15 @@ config USB_GSPCA_STK014
+ To compile this driver as a module, choose M here: the
+ module will be called gspca_stk014.
+
++config USB_GSPCA_STV0680
++ tristate "STV0680 USB Camera Driver"
++ depends on VIDEO_V4L2 && USB_GSPCA
++ help
++ Say Y here if you want support for cameras based on the STV0680 chip.
++
++ To compile this driver as a module, choose M here: the
++ module will be called gspca_stv0680.
++
+ config USB_GSPCA_SUNPLUS
+ tristate "SUNPLUS USB Camera Driver"
+ depends on VIDEO_V4L2 && USB_GSPCA
+diff --git a/drivers/media/video/gspca/Makefile b/drivers/media/video/gspca/Makefile
+index b742081..6e4cf1c 100644
+--- a/drivers/media/video/gspca/Makefile
++++ b/drivers/media/video/gspca/Makefile
+@@ -1,5 +1,7 @@
+ obj-$(CONFIG_USB_GSPCA) += gspca_main.o
++obj-$(CONFIG_USB_GSPCA_BENQ) += gspca_benq.o
+ obj-$(CONFIG_USB_GSPCA_CONEX) += gspca_conex.o
++obj-$(CONFIG_USB_GSPCA_CPIA1) += gspca_cpia1.o
+ obj-$(CONFIG_USB_GSPCA_ETOMS) += gspca_etoms.o
+ obj-$(CONFIG_USB_GSPCA_FINEPIX) += gspca_finepix.o
+ obj-$(CONFIG_USB_GSPCA_JEILINJ) += gspca_jeilinj.o
+@@ -7,8 +9,11 @@ obj-$(CONFIG_USB_GSPCA_MARS) += gspca_mars.o
+ obj-$(CONFIG_USB_GSPCA_MR97310A) += gspca_mr97310a.o
+ obj-$(CONFIG_USB_GSPCA_OV519) += gspca_ov519.o
+ obj-$(CONFIG_USB_GSPCA_OV534) += gspca_ov534.o
++obj-$(CONFIG_USB_GSPCA_OV534_9) += gspca_ov534_9.o
+ obj-$(CONFIG_USB_GSPCA_PAC207) += gspca_pac207.o
++obj-$(CONFIG_USB_GSPCA_PAC7302) += gspca_pac7302.o
+ obj-$(CONFIG_USB_GSPCA_PAC7311) += gspca_pac7311.o
++obj-$(CONFIG_USB_GSPCA_SN9C2028) += gspca_sn9c2028.o
+ obj-$(CONFIG_USB_GSPCA_SN9C20X) += gspca_sn9c20x.o
+ obj-$(CONFIG_USB_GSPCA_SONIXB) += gspca_sonixb.o
+ obj-$(CONFIG_USB_GSPCA_SONIXJ) += gspca_sonixj.o
+@@ -22,13 +27,16 @@ obj-$(CONFIG_USB_GSPCA_SQ905) += gspca_sq905.o
+ obj-$(CONFIG_USB_GSPCA_SQ905C) += gspca_sq905c.o
+ obj-$(CONFIG_USB_GSPCA_SUNPLUS) += gspca_sunplus.o
+ obj-$(CONFIG_USB_GSPCA_STK014) += gspca_stk014.o
++obj-$(CONFIG_USB_GSPCA_STV0680) += gspca_stv0680.o
+ obj-$(CONFIG_USB_GSPCA_T613) += gspca_t613.o
+ obj-$(CONFIG_USB_GSPCA_TV8532) += gspca_tv8532.o
+ obj-$(CONFIG_USB_GSPCA_VC032X) += gspca_vc032x.o
+ obj-$(CONFIG_USB_GSPCA_ZC3XX) += gspca_zc3xx.o
+
+ gspca_main-objs := gspca.o
++gspca_benq-objs := benq.o
+ gspca_conex-objs := conex.o
++gspca_cpia1-objs := cpia1.o
+ gspca_etoms-objs := etoms.o
+ gspca_finepix-objs := finepix.o
+ gspca_jeilinj-objs := jeilinj.o
+@@ -36,8 +44,11 @@ gspca_mars-objs := mars.o
+ gspca_mr97310a-objs := mr97310a.o
+ gspca_ov519-objs := ov519.o
+ gspca_ov534-objs := ov534.o
++gspca_ov534_9-objs := ov534_9.o
+ gspca_pac207-objs := pac207.o
++gspca_pac7302-objs := pac7302.o
+ gspca_pac7311-objs := pac7311.o
++gspca_sn9c2028-objs := sn9c2028.o
+ gspca_sn9c20x-objs := sn9c20x.o
+ gspca_sonixb-objs := sonixb.o
+ gspca_sonixj-objs := sonixj.o
+@@ -50,6 +61,7 @@ gspca_spca561-objs := spca561.o
+ gspca_sq905-objs := sq905.o
+ gspca_sq905c-objs := sq905c.o
+ gspca_stk014-objs := stk014.o
++gspca_stv0680-objs := stv0680.o
+ gspca_sunplus-objs := sunplus.o
+ gspca_t613-objs := t613.o
+ gspca_tv8532-objs := tv8532.o
+diff --git a/drivers/media/video/gspca/benq.c b/drivers/media/video/gspca/benq.c
+new file mode 100644
+index 0000000..43ac4af
+--- /dev/null
++++ b/drivers/media/video/gspca/benq.c
+@@ -0,0 +1,322 @@
++/*
++ * Benq DC E300 subdriver
++ *
++ * Copyright (C) 2009 Jean-Francois Moine (http://moinejf.free.fr)
++ *
++ * 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
++ * 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
++ */
++
++#define MODULE_NAME "benq"
++
++#include "gspca.h"
++
++MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
++MODULE_DESCRIPTION("Benq DC E300 USB Camera Driver");
++MODULE_LICENSE("GPL");
++
++/* specific webcam descriptor */
++struct sd {
++ struct gspca_dev gspca_dev; /* !! must be the first item */
++};
++
++/* V4L2 controls supported by the driver */
++static const struct ctrl sd_ctrls[] = {
++};
++
++static const struct v4l2_pix_format vga_mode[] = {
++ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
++ .bytesperline = 320,
++ .sizeimage = 320 * 240 * 3 / 8 + 590,
++ .colorspace = V4L2_COLORSPACE_JPEG},
++};
++
++static void sd_isoc_irq(struct urb *urb);
++
++/* -- write a register -- */
++static void reg_w(struct gspca_dev *gspca_dev,
++ u16 value, u16 index)
++{
++ struct usb_device *dev = gspca_dev->dev;
++ int ret;
++
++ if (gspca_dev->usb_err < 0)
++ return;
++ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
++ 0x02,
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ value,
++ index,
++ NULL,
++ 0,
++ 500);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "reg_w err %d", ret);
++ gspca_dev->usb_err = ret;
++ }
++}
++
++/* this function is called at probe time */
++static int sd_config(struct gspca_dev *gspca_dev,
++ const struct usb_device_id *id)
++{
++ gspca_dev->cam.cam_mode = vga_mode;
++ gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode);
++ gspca_dev->cam.no_urb_create = 1;
++ gspca_dev->cam.reverse_alts = 1;
++ return 0;
++}
++
++/* this function is called at probe and resume time */
++static int sd_init(struct gspca_dev *gspca_dev)
++{
++ return 0;
++}
++
++static int sd_isoc_init(struct gspca_dev *gspca_dev)
++{
++ int ret;
++
++ ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface,
++ gspca_dev->nbalt - 1);
++ if (ret < 0) {
++ err("usb_set_interface failed");
++ return ret;
++ }
++/* reg_w(gspca_dev, 0x0003, 0x0002); */
++ return 0;
++}
++
++/* -- start the camera -- */
++static int sd_start(struct gspca_dev *gspca_dev)
++{
++ struct urb *urb;
++ int i, n;
++
++ /* create 4 URBs - 2 on endpoint 0x83 and 2 on 0x082 */
++#if MAX_NURBS < 4
++#error "Not enough URBs in the gspca table"
++#endif
++#define SD_PKT_SZ 64
++#define SD_NPKT 32
++ for (n = 0; n < 4; n++) {
++ urb = usb_alloc_urb(SD_NPKT, GFP_KERNEL);
++ if (!urb) {
++ err("usb_alloc_urb failed");
++ return -ENOMEM;
++ }
++ gspca_dev->urb[n] = urb;
++ urb->transfer_buffer = usb_buffer_alloc(gspca_dev->dev,
++ SD_PKT_SZ * SD_NPKT,
++ GFP_KERNEL,
++ &urb->transfer_dma);
++
++ if (urb->transfer_buffer == NULL) {
++ err("usb_buffer_alloc failed");
++ return -ENOMEM;
++ }
++ urb->dev = gspca_dev->dev;
++ urb->context = gspca_dev;
++ urb->transfer_buffer_length = SD_PKT_SZ * SD_NPKT;
++ urb->pipe = usb_rcvisocpipe(gspca_dev->dev,
++ n & 1 ? 0x82 : 0x83);
++ urb->transfer_flags = URB_ISO_ASAP
++ | URB_NO_TRANSFER_DMA_MAP;
++ urb->interval = 1;
++ urb->complete = sd_isoc_irq;
++ urb->number_of_packets = SD_NPKT;
++ for (i = 0; i < SD_NPKT; i++) {
++ urb->iso_frame_desc[i].length = SD_PKT_SZ;
++ urb->iso_frame_desc[i].offset = SD_PKT_SZ * i;
++ }
++ }
++
++ return gspca_dev->usb_err;
++}
++
++static void sd_stopN(struct gspca_dev *gspca_dev)
++{
++ reg_w(gspca_dev, 0x003c, 0x0003);
++ reg_w(gspca_dev, 0x003c, 0x0004);
++ reg_w(gspca_dev, 0x003c, 0x0005);
++ reg_w(gspca_dev, 0x003c, 0x0006);
++ reg_w(gspca_dev, 0x003c, 0x0007);
++ usb_set_interface(gspca_dev->dev, gspca_dev->iface, gspca_dev->nbalt - 1);
++}
++
++static void sd_pkt_scan(struct gspca_dev *gspca_dev,
++ u8 *data, /* isoc packet */
++ int len) /* iso packet length */
++{
++ /* unused */
++}
++
++/* reception of an URB */
++static void sd_isoc_irq(struct urb *urb)
++{
++ struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
++ struct urb *urb0;
++ u8 *data;
++ int i, st;
++
++ PDEBUG(D_PACK, "sd isoc irq");
++ if (!gspca_dev->streaming)
++ return;
++ if (urb->status != 0) {
++ if (urb->status == -ESHUTDOWN)
++ return; /* disconnection */
++#ifdef CONFIG_PM
++ if (gspca_dev->frozen)
++ return;
++#endif
++ PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
++ return;
++ }
++
++ /* if this is a control URN (ep 0x83), wait */
++ if (urb == gspca_dev->urb[0] || urb == gspca_dev->urb[2])
++ return;
++
++ /* scan both received URBs */
++ if (urb == gspca_dev->urb[1])
++ urb0 = gspca_dev->urb[0];
++ else
++ urb0 = gspca_dev->urb[2];
++ for (i = 0; i < urb->number_of_packets; i++) {
++
++ /* check the packet status and length */
++ if (urb0->iso_frame_desc[i].actual_length != SD_PKT_SZ
++ || urb->iso_frame_desc[i].actual_length != SD_PKT_SZ) {
++ PDEBUG(D_ERR, "ISOC bad lengths %d / %d",
++ urb0->iso_frame_desc[i].actual_length,
++ urb->iso_frame_desc[i].actual_length);
++ gspca_dev->last_packet_type = DISCARD_PACKET;
++ continue;
++ }
++ st = urb0->iso_frame_desc[i].status;
++ if (st == 0)
++ st = urb->iso_frame_desc[i].status;
++ if (st) {
++ PDEBUG(D_ERR,
++ "ISOC data error: [%d] status=%d",
++ i, st);
++ gspca_dev->last_packet_type = DISCARD_PACKET;
++ continue;
++ }
++
++ /*
++ * The images are received in URBs of different endpoints
++ * (0x83 and 0x82).
++ * Image pieces in URBs of ep 0x83 are continuated in URBs of
++ * ep 0x82 of the same index.
++ * The packets in the URBs of endpoint 0x83 start with:
++ * - 80 ba/bb 00 00 = start of image followed by 'ff d8'
++ * - 04 ba/bb oo oo = image piece
++ * where 'oo oo' is the image offset
++ (not cheked)
++ * - (other -> bad frame)
++ * The images are JPEG encoded with full header and
++ * normal ff escape.
++ * The end of image ('ff d9') may occur in any URB.
++ * (not cheked)
++ */
++ data = (u8 *) urb0->transfer_buffer
++ + urb0->iso_frame_desc[i].offset;
++ if (data[0] == 0x80 && (data[1] & 0xfe) == 0xba) {
++
++ /* new image */
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ NULL, 0);
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
++ data + 4, SD_PKT_SZ - 4);
++ } else if (data[0] == 0x04 && (data[1] & 0xfe) == 0xba) {
++ gspca_frame_add(gspca_dev, INTER_PACKET,
++ data + 4, SD_PKT_SZ - 4);
++ } else {
++ gspca_dev->last_packet_type = DISCARD_PACKET;
++ continue;
++ }
++ data = (u8 *) urb->transfer_buffer
++ + urb->iso_frame_desc[i].offset;
++ gspca_frame_add(gspca_dev, INTER_PACKET,
++ data, SD_PKT_SZ);
++ }
++
++ /* resubmit the URBs */
++ st = usb_submit_urb(urb0, GFP_ATOMIC);
++ if (st < 0)
++ PDEBUG(D_ERR|D_PACK, "usb_submit_urb(0) ret %d", st);
++ st = usb_submit_urb(urb, GFP_ATOMIC);
++ if (st < 0)
++ PDEBUG(D_ERR|D_PACK, "usb_submit_urb() ret %d", st);
++}
++
++/* sub-driver description */
++static const struct sd_desc sd_desc = {
++ .name = MODULE_NAME,
++ .ctrls = sd_ctrls,
++ .nctrls = ARRAY_SIZE(sd_ctrls),
++ .config = sd_config,
++ .init = sd_init,
++ .isoc_init = sd_isoc_init,
++ .start = sd_start,
++ .stopN = sd_stopN,
++ .pkt_scan = sd_pkt_scan,
++};
++
++/* -- module initialisation -- */
++static const __devinitdata struct usb_device_id device_table[] = {
++ {USB_DEVICE(0x04a5, 0x3035)},
++ {}
++};
++MODULE_DEVICE_TABLE(usb, device_table);
++
++/* -- device connect -- */
++static int sd_probe(struct usb_interface *intf,
++ const struct usb_device_id *id)
++{
++ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
++ THIS_MODULE);
++}
++
++static struct usb_driver sd_driver = {
++ .name = MODULE_NAME,
++ .id_table = device_table,
++ .probe = sd_probe,
++ .disconnect = gspca_disconnect,
++#ifdef CONFIG_PM
++ .suspend = gspca_suspend,
++ .resume = gspca_resume,
++#endif
++};
++
++/* -- module insert / remove -- */
++static int __init sd_mod_init(void)
++{
++ int ret;
++
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
++ info("registered");
++ return 0;
++}
++static void __exit sd_mod_exit(void)
++{
++ usb_deregister(&sd_driver);
++ info("deregistered");
++}
++
++module_init(sd_mod_init);
++module_exit(sd_mod_exit);
+diff --git a/drivers/media/video/gspca/conex.c b/drivers/media/video/gspca/conex.c
+index eca0035..19fe6b2 100644
+--- a/drivers/media/video/gspca/conex.c
++++ b/drivers/media/video/gspca/conex.c
+@@ -52,7 +52,7 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+@@ -888,8 +888,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+- __u8 *data, /* isoc packet */
++ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -897,16 +896,15 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ if (data[0] == 0xff && data[1] == 0xd8) {
+
+ /* start of frame */
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+- data, 0);
++ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+
+ /* put the JPEG header in the new frame */
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+- sd->jpeg_hdr, JPEG_HDR_SZ);
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
++ sd->jpeg_hdr, JPEG_HDR_SZ);
+ data += 2;
+ len -= 2;
+ }
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+
+ static void setbrightness(struct gspca_dev*gspca_dev)
+@@ -1034,7 +1032,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+ }
+
+ /* sub-driver description */
+-static struct sd_desc sd_desc = {
++static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+@@ -1048,14 +1046,14 @@ static struct sd_desc sd_desc = {
+ };
+
+ /* -- module initialisation -- */
+-static __devinitdata struct usb_device_id device_table[] = {
++static const struct usb_device_id device_table[] __devinitconst = {
+ {USB_DEVICE(0x0572, 0x0041)},
+ {}
+ };
+ MODULE_DEVICE_TABLE(usb, device_table);
+
+ /* -- device connect -- */
+-static int sd_probe(struct usb_interface *intf,
++static int __devinit sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+ {
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+diff --git a/drivers/media/video/gspca/cpia1.c b/drivers/media/video/gspca/cpia1.c
+new file mode 100644
+index 0000000..7afdc58
+--- /dev/null
++++ b/drivers/media/video/gspca/cpia1.c
+@@ -0,0 +1,2022 @@
++/*
++ * cpia CPiA (1) gspca driver
++ *
++ * Copyright (C) 2010 Hans de Goede <hdgoede@redhat.com>
++ *
++ * This module is adapted from the in kernel v4l1 cpia driver which is :
++ *
++ * (C) Copyright 1999-2000 Peter Pregler
++ * (C) Copyright 1999-2000 Scott J. Bertin
++ * (C) Copyright 1999-2000 Johannes Erdfelt <johannes@erdfelt.com>
++ * (C) Copyright 2000 STMicroelectronics
++ *
++ * 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
++ *
++ */
++
++#define MODULE_NAME "cpia1"
++
++#include "gspca.h"
++
++MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>");
++MODULE_DESCRIPTION("Vision CPiA");
++MODULE_LICENSE("GPL");
++
++/* constant value's */
++#define MAGIC_0 0x19
++#define MAGIC_1 0x68
++#define DATA_IN 0xC0
++#define DATA_OUT 0x40
++#define VIDEOSIZE_QCIF 0 /* 176x144 */
++#define VIDEOSIZE_CIF 1 /* 352x288 */
++#define SUBSAMPLE_420 0
++#define SUBSAMPLE_422 1
++#define YUVORDER_YUYV 0
++#define YUVORDER_UYVY 1
++#define NOT_COMPRESSED 0
++#define COMPRESSED 1
++#define NO_DECIMATION 0
++#define DECIMATION_ENAB 1
++#define EOI 0xff /* End Of Image */
++#define EOL 0xfd /* End Of Line */
++#define FRAME_HEADER_SIZE 64
++
++/* Image grab modes */
++#define CPIA_GRAB_SINGLE 0
++#define CPIA_GRAB_CONTINEOUS 1
++
++/* Compression parameters */
++#define CPIA_COMPRESSION_NONE 0
++#define CPIA_COMPRESSION_AUTO 1
++#define CPIA_COMPRESSION_MANUAL 2
++#define CPIA_COMPRESSION_TARGET_QUALITY 0
++#define CPIA_COMPRESSION_TARGET_FRAMERATE 1
++
++/* Return offsets for GetCameraState */
++#define SYSTEMSTATE 0
++#define GRABSTATE 1
++#define STREAMSTATE 2
++#define FATALERROR 3
++#define CMDERROR 4
++#define DEBUGFLAGS 5
++#define VPSTATUS 6
++#define ERRORCODE 7
++
++/* SystemState */
++#define UNINITIALISED_STATE 0
++#define PASS_THROUGH_STATE 1
++#define LO_POWER_STATE 2
++#define HI_POWER_STATE 3
++#define WARM_BOOT_STATE 4
++
++/* GrabState */
++#define GRAB_IDLE 0
++#define GRAB_ACTIVE 1
++#define GRAB_DONE 2
++
++/* StreamState */
++#define STREAM_NOT_READY 0
++#define STREAM_READY 1
++#define STREAM_OPEN 2
++#define STREAM_PAUSED 3
++#define STREAM_FINISHED 4
++
++/* Fatal Error, CmdError, and DebugFlags */
++#define CPIA_FLAG 1
++#define SYSTEM_FLAG 2
++#define INT_CTRL_FLAG 4
++#define PROCESS_FLAG 8
++#define COM_FLAG 16
++#define VP_CTRL_FLAG 32
++#define CAPTURE_FLAG 64
++#define DEBUG_FLAG 128
++
++/* VPStatus */
++#define VP_STATE_OK 0x00
++
++#define VP_STATE_FAILED_VIDEOINIT 0x01
++#define VP_STATE_FAILED_AECACBINIT 0x02
++#define VP_STATE_AEC_MAX 0x04
++#define VP_STATE_ACB_BMAX 0x08
++
++#define VP_STATE_ACB_RMIN 0x10
++#define VP_STATE_ACB_GMIN 0x20
++#define VP_STATE_ACB_RMAX 0x40
++#define VP_STATE_ACB_GMAX 0x80
++
++/* default (minimum) compensation values */
++#define COMP_RED 220
++#define COMP_GREEN1 214
++#define COMP_GREEN2 COMP_GREEN1
++#define COMP_BLUE 230
++
++/* exposure status */
++#define EXPOSURE_VERY_LIGHT 0
++#define EXPOSURE_LIGHT 1
++#define EXPOSURE_NORMAL 2
++#define EXPOSURE_DARK 3
++#define EXPOSURE_VERY_DARK 4
++
++#define CPIA_MODULE_CPIA (0 << 5)
++#define CPIA_MODULE_SYSTEM (1 << 5)
++#define CPIA_MODULE_VP_CTRL (5 << 5)
++#define CPIA_MODULE_CAPTURE (6 << 5)
++#define CPIA_MODULE_DEBUG (7 << 5)
++
++#define INPUT (DATA_IN << 8)
++#define OUTPUT (DATA_OUT << 8)
++
++#define CPIA_COMMAND_GetCPIAVersion (INPUT | CPIA_MODULE_CPIA | 1)
++#define CPIA_COMMAND_GetPnPID (INPUT | CPIA_MODULE_CPIA | 2)
++#define CPIA_COMMAND_GetCameraStatus (INPUT | CPIA_MODULE_CPIA | 3)
++#define CPIA_COMMAND_GotoHiPower (OUTPUT | CPIA_MODULE_CPIA | 4)
++#define CPIA_COMMAND_GotoLoPower (OUTPUT | CPIA_MODULE_CPIA | 5)
++#define CPIA_COMMAND_GotoSuspend (OUTPUT | CPIA_MODULE_CPIA | 7)
++#define CPIA_COMMAND_GotoPassThrough (OUTPUT | CPIA_MODULE_CPIA | 8)
++#define CPIA_COMMAND_ModifyCameraStatus (OUTPUT | CPIA_MODULE_CPIA | 10)
++
++#define CPIA_COMMAND_ReadVCRegs (INPUT | CPIA_MODULE_SYSTEM | 1)
++#define CPIA_COMMAND_WriteVCReg (OUTPUT | CPIA_MODULE_SYSTEM | 2)
++#define CPIA_COMMAND_ReadMCPorts (INPUT | CPIA_MODULE_SYSTEM | 3)
++#define CPIA_COMMAND_WriteMCPort (OUTPUT | CPIA_MODULE_SYSTEM | 4)
++#define CPIA_COMMAND_SetBaudRate (OUTPUT | CPIA_MODULE_SYSTEM | 5)
++#define CPIA_COMMAND_SetECPTiming (OUTPUT | CPIA_MODULE_SYSTEM | 6)
++#define CPIA_COMMAND_ReadIDATA (INPUT | CPIA_MODULE_SYSTEM | 7)
++#define CPIA_COMMAND_WriteIDATA (OUTPUT | CPIA_MODULE_SYSTEM | 8)
++#define CPIA_COMMAND_GenericCall (OUTPUT | CPIA_MODULE_SYSTEM | 9)
++#define CPIA_COMMAND_I2CStart (OUTPUT | CPIA_MODULE_SYSTEM | 10)
++#define CPIA_COMMAND_I2CStop (OUTPUT | CPIA_MODULE_SYSTEM | 11)
++#define CPIA_COMMAND_I2CWrite (OUTPUT | CPIA_MODULE_SYSTEM | 12)
++#define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13)
++
++#define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1)
++#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2)
++#define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3)
++#define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4)
++#define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6)
++#define CPIA_COMMAND_SetSensorFPS (OUTPUT | CPIA_MODULE_VP_CTRL | 7)
++#define CPIA_COMMAND_SetVPDefaults (OUTPUT | CPIA_MODULE_VP_CTRL | 8)
++#define CPIA_COMMAND_SetApcor (OUTPUT | CPIA_MODULE_VP_CTRL | 9)
++#define CPIA_COMMAND_SetFlickerCtrl (OUTPUT | CPIA_MODULE_VP_CTRL | 10)
++#define CPIA_COMMAND_SetVLOffset (OUTPUT | CPIA_MODULE_VP_CTRL | 11)
++#define CPIA_COMMAND_GetColourParams (INPUT | CPIA_MODULE_VP_CTRL | 16)
++#define CPIA_COMMAND_GetColourBalance (INPUT | CPIA_MODULE_VP_CTRL | 17)
++#define CPIA_COMMAND_GetExposure (INPUT | CPIA_MODULE_VP_CTRL | 18)
++#define CPIA_COMMAND_SetSensorMatrix (OUTPUT | CPIA_MODULE_VP_CTRL | 19)
++#define CPIA_COMMAND_ColourBars (OUTPUT | CPIA_MODULE_VP_CTRL | 25)
++#define CPIA_COMMAND_ReadVPRegs (INPUT | CPIA_MODULE_VP_CTRL | 30)
++#define CPIA_COMMAND_WriteVPReg (OUTPUT | CPIA_MODULE_VP_CTRL | 31)
++
++#define CPIA_COMMAND_GrabFrame (OUTPUT | CPIA_MODULE_CAPTURE | 1)
++#define CPIA_COMMAND_UploadFrame (OUTPUT | CPIA_MODULE_CAPTURE | 2)
++#define CPIA_COMMAND_SetGrabMode (OUTPUT | CPIA_MODULE_CAPTURE | 3)
++#define CPIA_COMMAND_InitStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 4)
++#define CPIA_COMMAND_FiniStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 5)
++#define CPIA_COMMAND_StartStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 6)
++#define CPIA_COMMAND_EndStreamCap (OUTPUT | CPIA_MODULE_CAPTURE | 7)
++#define CPIA_COMMAND_SetFormat (OUTPUT | CPIA_MODULE_CAPTURE | 8)
++#define CPIA_COMMAND_SetROI (OUTPUT | CPIA_MODULE_CAPTURE | 9)
++#define CPIA_COMMAND_SetCompression (OUTPUT | CPIA_MODULE_CAPTURE | 10)
++#define CPIA_COMMAND_SetCompressionTarget (OUTPUT | CPIA_MODULE_CAPTURE | 11)
++#define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12)
++#define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13)
++#define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14)
++#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15)
++
++#define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1)
++#define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4)
++#define CPIA_COMMAND_SetDramPage (OUTPUT | CPIA_MODULE_DEBUG | 5)
++#define CPIA_COMMAND_StartDramUpload (OUTPUT | CPIA_MODULE_DEBUG | 6)
++#define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8)
++#define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9)
++#define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10)
++#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11)
++
++#define ROUND_UP_EXP_FOR_FLICKER 15
++
++/* Constants for automatic frame rate adjustment */
++#define MAX_EXP 302
++#define MAX_EXP_102 255
++#define LOW_EXP 140
++#define VERY_LOW_EXP 70
++#define TC 94
++#define EXP_ACC_DARK 50
++#define EXP_ACC_LIGHT 90
++#define HIGH_COMP_102 160
++#define MAX_COMP 239
++#define DARK_TIME 3
++#define LIGHT_TIME 3
++
++#define FIRMWARE_VERSION(x, y) (sd->params.version.firmwareVersion == (x) && \
++ sd->params.version.firmwareRevision == (y))
++
++/* Developer's Guide Table 5 p 3-34
++ * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/
++static u8 flicker_jumps[2][2][4] =
++{ { { 76, 38, 19, 9 }, { 92, 46, 23, 11 } },
++ { { 64, 32, 16, 8 }, { 76, 38, 19, 9} }
++};
++
++struct cam_params {
++ struct {
++ u8 firmwareVersion;
++ u8 firmwareRevision;
++ u8 vcVersion;
++ u8 vcRevision;
++ } version;
++ struct {
++ u16 vendor;
++ u16 product;
++ u16 deviceRevision;
++ } pnpID;
++ struct {
++ u8 vpVersion;
++ u8 vpRevision;
++ u16 cameraHeadID;
++ } vpVersion;
++ struct {
++ u8 systemState;
++ u8 grabState;
++ u8 streamState;
++ u8 fatalError;
++ u8 cmdError;
++ u8 debugFlags;
++ u8 vpStatus;
++ u8 errorCode;
++ } status;
++ struct {
++ u8 brightness;
++ u8 contrast;
++ u8 saturation;
++ } colourParams;
++ struct {
++ u8 gainMode;
++ u8 expMode;
++ u8 compMode;
++ u8 centreWeight;
++ u8 gain;
++ u8 fineExp;
++ u8 coarseExpLo;
++ u8 coarseExpHi;
++ u8 redComp;
++ u8 green1Comp;
++ u8 green2Comp;
++ u8 blueComp;
++ } exposure;
++ struct {
++ u8 balanceMode;
++ u8 redGain;
++ u8 greenGain;
++ u8 blueGain;
++ } colourBalance;
++ struct {
++ u8 divisor;
++ u8 baserate;
++ } sensorFps;
++ struct {
++ u8 gain1;
++ u8 gain2;
++ u8 gain4;
++ u8 gain8;
++ } apcor;
++ struct {
++ u8 disabled;
++ u8 flickerMode;
++ u8 coarseJump;
++ u8 allowableOverExposure;
++ } flickerControl;
++ struct {
++ u8 gain1;
++ u8 gain2;
++ u8 gain4;
++ u8 gain8;
++ } vlOffset;
++ struct {
++ u8 mode;
++ u8 decimation;
++ } compression;
++ struct {
++ u8 frTargeting;
++ u8 targetFR;
++ u8 targetQ;
++ } compressionTarget;
++ struct {
++ u8 yThreshold;
++ u8 uvThreshold;
++ } yuvThreshold;
++ struct {
++ u8 hysteresis;
++ u8 threshMax;
++ u8 smallStep;
++ u8 largeStep;
++ u8 decimationHysteresis;
++ u8 frDiffStepThresh;
++ u8 qDiffStepThresh;
++ u8 decimationThreshMod;
++ } compressionParams;
++ struct {
++ u8 videoSize; /* CIF/QCIF */
++ u8 subSample;
++ u8 yuvOrder;
++ } format;
++ struct { /* Intel QX3 specific data */
++ u8 qx3_detected; /* a QX3 is present */
++ u8 toplight; /* top light lit , R/W */
++ u8 bottomlight; /* bottom light lit, R/W */
++ u8 button; /* snapshot button pressed (R/O) */
++ u8 cradled; /* microscope is in cradle (R/O) */
++ } qx3;
++ struct {
++ u8 colStart; /* skip first 8*colStart pixels */
++ u8 colEnd; /* finish at 8*colEnd pixels */
++ u8 rowStart; /* skip first 4*rowStart lines */
++ u8 rowEnd; /* finish at 4*rowEnd lines */
++ } roi;
++ u8 ecpTiming;
++ u8 streamStartLine;
++};
++
++/* specific webcam descriptor */
++struct sd {
++ struct gspca_dev gspca_dev; /* !! must be the first item */
++ struct cam_params params; /* camera settings */
++
++ atomic_t cam_exposure;
++ atomic_t fps;
++ int exposure_count;
++ u8 exposure_status;
++ u8 mainsFreq; /* 0 = 50hz, 1 = 60hz */
++ u8 first_frame;
++ u8 freq;
++};
++
++/* V4L2 controls supported by the driver */
++static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val);
++
++static struct ctrl sd_ctrls[] = {
++ {
++ {
++ .id = V4L2_CID_BRIGHTNESS,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Brightness",
++ .minimum = 0,
++ .maximum = 100,
++ .step = 1,
++#define BRIGHTNESS_DEF 50
++ .default_value = BRIGHTNESS_DEF,
++ .flags = 0,
++ },
++ .set = sd_setbrightness,
++ .get = sd_getbrightness,
++ },
++ {
++ {
++ .id = V4L2_CID_CONTRAST,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Contrast",
++ .minimum = 0,
++ .maximum = 96,
++ .step = 8,
++#define CONTRAST_DEF 48
++ .default_value = CONTRAST_DEF,
++ },
++ .set = sd_setcontrast,
++ .get = sd_getcontrast,
++ },
++ {
++ {
++ .id = V4L2_CID_SATURATION,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Saturation",
++ .minimum = 0,
++ .maximum = 100,
++ .step = 1,
++#define SATURATION_DEF 50
++ .default_value = SATURATION_DEF,
++ },
++ .set = sd_setsaturation,
++ .get = sd_getsaturation,
++ },
++ {
++ {
++ .id = V4L2_CID_POWER_LINE_FREQUENCY,
++ .type = V4L2_CTRL_TYPE_MENU,
++ .name = "Light frequency filter",
++ .minimum = 0,
++ .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
++ .step = 1,
++#define FREQ_DEF 1
++ .default_value = FREQ_DEF,
++ },
++ .set = sd_setfreq,
++ .get = sd_getfreq,
++ },
++ {
++ {
++#define V4L2_CID_COMP_TARGET V4L2_CID_PRIVATE_BASE
++ .id = V4L2_CID_COMP_TARGET,
++ .type = V4L2_CTRL_TYPE_MENU,
++ .name = "Compression Target",
++ .minimum = 0,
++ .maximum = 1,
++ .step = 1,
++#define COMP_TARGET_DEF CPIA_COMPRESSION_TARGET_QUALITY
++ .default_value = COMP_TARGET_DEF,
++ },
++ .set = sd_setcomptarget,
++ .get = sd_getcomptarget,
++ },
++};
++
++static const struct v4l2_pix_format mode[] = {
++ {160, 120, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
++ /* The sizeimage is trial and error, as with low framerates
++ the camera will pad out usb frames, making the image
++ data larger then strictly necessary */
++ .bytesperline = 160,
++ .sizeimage = 65536,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 3},
++ {176, 144, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
++ .bytesperline = 172,
++ .sizeimage = 65536,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 2},
++ {320, 240, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
++ .bytesperline = 320,
++ .sizeimage = 262144,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 1},
++ {352, 288, V4L2_PIX_FMT_CPIA1, V4L2_FIELD_NONE,
++ .bytesperline = 352,
++ .sizeimage = 262144,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 0},
++};
++
++/**********************************************************************
++ *
++ * General functions
++ *
++ **********************************************************************/
++
++static int cpia_usb_transferCmd(struct gspca_dev *gspca_dev, u8 *command)
++{
++ u8 requesttype;
++ unsigned int pipe;
++ int ret, databytes = command[6] | (command[7] << 8);
++ /* Sometimes we see spurious EPIPE errors */
++ int retries = 3;
++
++ if (command[0] == DATA_IN) {
++ pipe = usb_rcvctrlpipe(gspca_dev->dev, 0);
++ requesttype = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
++ } else if (command[0] == DATA_OUT) {
++ pipe = usb_sndctrlpipe(gspca_dev->dev, 0);
++ requesttype = USB_TYPE_VENDOR | USB_RECIP_DEVICE;
++ } else {
++ PDEBUG(D_ERR, "Unexpected first byte of command: %x",
++ command[0]);
++ return -EINVAL;
++ }
++
++retry:
++ ret = usb_control_msg(gspca_dev->dev, pipe,
++ command[1],
++ requesttype,
++ command[2] | (command[3] << 8),
++ command[4] | (command[5] << 8),
++ gspca_dev->usb_buf, databytes, 1000);
++
++ if (ret < 0)
++ PDEBUG(D_ERR, "usb_control_msg %02x, error %d", command[1],
++ ret);
++
++ if (ret == -EPIPE && retries > 0) {
++ retries--;
++ goto retry;
++ }
++
++ return (ret < 0) ? ret : 0;
++}
++
++/* send an arbitrary command to the camera */
++static int do_command(struct gspca_dev *gspca_dev, u16 command,
++ u8 a, u8 b, u8 c, u8 d)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int ret, datasize;
++ u8 cmd[8];
++
++ switch (command) {
++ case CPIA_COMMAND_GetCPIAVersion:
++ case CPIA_COMMAND_GetPnPID:
++ case CPIA_COMMAND_GetCameraStatus:
++ case CPIA_COMMAND_GetVPVersion:
++ case CPIA_COMMAND_GetColourParams:
++ case CPIA_COMMAND_GetColourBalance:
++ case CPIA_COMMAND_GetExposure:
++ datasize = 8;
++ break;
++ case CPIA_COMMAND_ReadMCPorts:
++ case CPIA_COMMAND_ReadVCRegs:
++ datasize = 4;
++ break;
++ default:
++ datasize = 0;
++ break;
++ }
++
++ cmd[0] = command >> 8;
++ cmd[1] = command & 0xff;
++ cmd[2] = a;
++ cmd[3] = b;
++ cmd[4] = c;
++ cmd[5] = d;
++ cmd[6] = datasize;
++ cmd[7] = 0;
++
++ ret = cpia_usb_transferCmd(gspca_dev, cmd);
++ if (ret)
++ return ret;
++
++ switch (command) {
++ case CPIA_COMMAND_GetCPIAVersion:
++ sd->params.version.firmwareVersion = gspca_dev->usb_buf[0];
++ sd->params.version.firmwareRevision = gspca_dev->usb_buf[1];
++ sd->params.version.vcVersion = gspca_dev->usb_buf[2];
++ sd->params.version.vcRevision = gspca_dev->usb_buf[3];
++ break;
++ case CPIA_COMMAND_GetPnPID:
++ sd->params.pnpID.vendor =
++ gspca_dev->usb_buf[0] | (gspca_dev->usb_buf[1] << 8);
++ sd->params.pnpID.product =
++ gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8);
++ sd->params.pnpID.deviceRevision =
++ gspca_dev->usb_buf[4] | (gspca_dev->usb_buf[5] << 8);
++ break;
++ case CPIA_COMMAND_GetCameraStatus:
++ sd->params.status.systemState = gspca_dev->usb_buf[0];
++ sd->params.status.grabState = gspca_dev->usb_buf[1];
++ sd->params.status.streamState = gspca_dev->usb_buf[2];
++ sd->params.status.fatalError = gspca_dev->usb_buf[3];
++ sd->params.status.cmdError = gspca_dev->usb_buf[4];
++ sd->params.status.debugFlags = gspca_dev->usb_buf[5];
++ sd->params.status.vpStatus = gspca_dev->usb_buf[6];
++ sd->params.status.errorCode = gspca_dev->usb_buf[7];
++ break;
++ case CPIA_COMMAND_GetVPVersion:
++ sd->params.vpVersion.vpVersion = gspca_dev->usb_buf[0];
++ sd->params.vpVersion.vpRevision = gspca_dev->usb_buf[1];
++ sd->params.vpVersion.cameraHeadID =
++ gspca_dev->usb_buf[2] | (gspca_dev->usb_buf[3] << 8);
++ break;
++ case CPIA_COMMAND_GetColourParams:
++ sd->params.colourParams.brightness = gspca_dev->usb_buf[0];
++ sd->params.colourParams.contrast = gspca_dev->usb_buf[1];
++ sd->params.colourParams.saturation = gspca_dev->usb_buf[2];
++ break;
++ case CPIA_COMMAND_GetColourBalance:
++ sd->params.colourBalance.redGain = gspca_dev->usb_buf[0];
++ sd->params.colourBalance.greenGain = gspca_dev->usb_buf[1];
++ sd->params.colourBalance.blueGain = gspca_dev->usb_buf[2];
++ break;
++ case CPIA_COMMAND_GetExposure:
++ sd->params.exposure.gain = gspca_dev->usb_buf[0];
++ sd->params.exposure.fineExp = gspca_dev->usb_buf[1];
++ sd->params.exposure.coarseExpLo = gspca_dev->usb_buf[2];
++ sd->params.exposure.coarseExpHi = gspca_dev->usb_buf[3];
++ sd->params.exposure.redComp = gspca_dev->usb_buf[4];
++ sd->params.exposure.green1Comp = gspca_dev->usb_buf[5];
++ sd->params.exposure.green2Comp = gspca_dev->usb_buf[6];
++ sd->params.exposure.blueComp = gspca_dev->usb_buf[7];
++ break;
++
++ case CPIA_COMMAND_ReadMCPorts:
++ if (!sd->params.qx3.qx3_detected)
++ break;
++ /* test button press */
++ sd->params.qx3.button = ((gspca_dev->usb_buf[1] & 0x02) == 0);
++ if (sd->params.qx3.button) {
++ /* button pressed - unlock the latch */
++ do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
++ 3, 0xDF, 0xDF, 0);
++ do_command(gspca_dev, CPIA_COMMAND_WriteMCPort,
++ 3, 0xFF, 0xFF, 0);
++ }
++
++ /* test whether microscope is cradled */
++ sd->params.qx3.cradled = ((gspca_dev->usb_buf[2] & 0x40) == 0);
++ break;
++ }
++
++ return 0;
++}
++
++/* send a command to the camera with an additional data transaction */
++static int do_command_extended(struct gspca_dev *gspca_dev, u16 command,
++ u8 a, u8 b, u8 c, u8 d,
++ u8 e, u8 f, u8 g, u8 h,
++ u8 i, u8 j, u8 k, u8 l)
++{
++ u8 cmd[8];
++
++ cmd[0] = command >> 8;
++ cmd[1] = command & 0xff;
++ cmd[2] = a;
++ cmd[3] = b;
++ cmd[4] = c;
++ cmd[5] = d;
++ cmd[6] = 8;
++ cmd[7] = 0;
++ gspca_dev->usb_buf[0] = e;
++ gspca_dev->usb_buf[1] = f;
++ gspca_dev->usb_buf[2] = g;
++ gspca_dev->usb_buf[3] = h;
++ gspca_dev->usb_buf[4] = i;
++ gspca_dev->usb_buf[5] = j;
++ gspca_dev->usb_buf[6] = k;
++ gspca_dev->usb_buf[7] = l;
++
++ return cpia_usb_transferCmd(gspca_dev, cmd);
++}
++
++/* find_over_exposure
++ * Finds a suitable value of OverExposure for use with SetFlickerCtrl
++ * Some calculation is required because this value changes with the brightness
++ * set with SetColourParameters
++ *
++ * Parameters: Brightness - last brightness value set with SetColourParameters
++ *
++ * Returns: OverExposure value to use with SetFlickerCtrl
++ */
++#define FLICKER_MAX_EXPOSURE 250
++#define FLICKER_ALLOWABLE_OVER_EXPOSURE 146
++#define FLICKER_BRIGHTNESS_CONSTANT 59
++static int find_over_exposure(int brightness)
++{
++ int MaxAllowableOverExposure, OverExposure;
++
++ MaxAllowableOverExposure = FLICKER_MAX_EXPOSURE - brightness -
++ FLICKER_BRIGHTNESS_CONSTANT;
++
++ if (MaxAllowableOverExposure < FLICKER_ALLOWABLE_OVER_EXPOSURE)
++ OverExposure = MaxAllowableOverExposure;
++ else
++ OverExposure = FLICKER_ALLOWABLE_OVER_EXPOSURE;
++
++ return OverExposure;
++}
++#undef FLICKER_MAX_EXPOSURE
++#undef FLICKER_ALLOWABLE_OVER_EXPOSURE
++#undef FLICKER_BRIGHTNESS_CONSTANT
++
++/* initialise cam_data structure */
++static void reset_camera_params(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ struct cam_params *params = &sd->params;
++
++ /* The following parameter values are the defaults from
++ * "Software Developer's Guide for CPiA Cameras". Any changes
++ * to the defaults are noted in comments. */
++ params->colourParams.brightness = BRIGHTNESS_DEF;
++ params->colourParams.contrast = CONTRAST_DEF;
++ params->colourParams.saturation = SATURATION_DEF;
++ params->exposure.gainMode = 4;
++ params->exposure.expMode = 2; /* AEC */
++ params->exposure.compMode = 1;
++ params->exposure.centreWeight = 1;
++ params->exposure.gain = 0;
++ params->exposure.fineExp = 0;
++ params->exposure.coarseExpLo = 185;
++ params->exposure.coarseExpHi = 0;
++ params->exposure.redComp = COMP_RED;
++ params->exposure.green1Comp = COMP_GREEN1;
++ params->exposure.green2Comp = COMP_GREEN2;
++ params->exposure.blueComp = COMP_BLUE;
++ params->colourBalance.balanceMode = 2; /* ACB */
++ params->colourBalance.redGain = 32;
++ params->colourBalance.greenGain = 6;
++ params->colourBalance.blueGain = 92;
++ params->apcor.gain1 = 0x18;
++ params->apcor.gain2 = 0x16;
++ params->apcor.gain4 = 0x24;
++ params->apcor.gain8 = 0x34;
++ params->flickerControl.flickerMode = 0;
++ params->flickerControl.disabled = 1;
++
++ params->flickerControl.coarseJump =
++ flicker_jumps[sd->mainsFreq]
++ [params->sensorFps.baserate]
++ [params->sensorFps.divisor];
++ params->flickerControl.allowableOverExposure =
++ find_over_exposure(params->colourParams.brightness);
++ params->vlOffset.gain1 = 20;
++ params->vlOffset.gain2 = 24;
++ params->vlOffset.gain4 = 26;
++ params->vlOffset.gain8 = 26;
++ params->compressionParams.hysteresis = 3;
++ params->compressionParams.threshMax = 11;
++ params->compressionParams.smallStep = 1;
++ params->compressionParams.largeStep = 3;
++ params->compressionParams.decimationHysteresis = 2;
++ params->compressionParams.frDiffStepThresh = 5;
++ params->compressionParams.qDiffStepThresh = 3;
++ params->compressionParams.decimationThreshMod = 2;
++ /* End of default values from Software Developer's Guide */
++
++ /* Set Sensor FPS to 15fps. This seems better than 30fps
++ * for indoor lighting. */
++ params->sensorFps.divisor = 1;
++ params->sensorFps.baserate = 1;
++
++ params->yuvThreshold.yThreshold = 6; /* From windows driver */
++ params->yuvThreshold.uvThreshold = 6; /* From windows driver */
++
++ params->format.subSample = SUBSAMPLE_420;
++ params->format.yuvOrder = YUVORDER_YUYV;
++
++ params->compression.mode = CPIA_COMPRESSION_AUTO;
++ params->compression.decimation = NO_DECIMATION;
++
++ params->compressionTarget.frTargeting = COMP_TARGET_DEF;
++ params->compressionTarget.targetFR = 15; /* From windows driver */
++ params->compressionTarget.targetQ = 5; /* From windows driver */
++
++ params->qx3.qx3_detected = 0;
++ params->qx3.toplight = 0;
++ params->qx3.bottomlight = 0;
++ params->qx3.button = 0;
++ params->qx3.cradled = 0;
++}
++
++static void printstatus(struct cam_params *params)
++{
++ PDEBUG(D_PROBE, "status: %02x %02x %02x %02x %02x %02x %02x %02x",
++ params->status.systemState, params->status.grabState,
++ params->status.streamState, params->status.fatalError,
++ params->status.cmdError, params->status.debugFlags,
++ params->status.vpStatus, params->status.errorCode);
++}
++
++static int goto_low_power(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int ret;
++
++ ret = do_command(gspca_dev, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0);
++ if (ret)
++ return ret;
++
++ do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
++ if (ret)
++ return ret;
++
++ if (sd->params.status.systemState != LO_POWER_STATE) {
++ if (sd->params.status.systemState != WARM_BOOT_STATE) {
++ PDEBUG(D_ERR,
++ "unexpected state after lo power cmd: %02x",
++ sd->params.status.systemState);
++ printstatus(&sd->params);
++ }
++ return -EIO;
++ }
++
++ PDEBUG(D_CONF, "camera now in LOW power state");
++ return 0;
++}
++
++static int goto_high_power(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int ret;
++
++ ret = do_command(gspca_dev, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0);
++ if (ret)
++ return ret;
++
++ msleep_interruptible(40); /* windows driver does it too */
++
++ if (signal_pending(current))
++ return -EINTR;
++
++ do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
++ if (ret)
++ return ret;
++
++ if (sd->params.status.systemState != HI_POWER_STATE) {
++ PDEBUG(D_ERR, "unexpected state after hi power cmd: %02x",
++ sd->params.status.systemState);
++ printstatus(&sd->params);
++ return -EIO;
++ }
++
++ PDEBUG(D_CONF, "camera now in HIGH power state");
++ return 0;
++}
++
++static int get_version_information(struct gspca_dev *gspca_dev)
++{
++ int ret;
++
++ /* GetCPIAVersion */
++ ret = do_command(gspca_dev, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0);
++ if (ret)
++ return ret;
++
++ /* GetPnPID */
++ return do_command(gspca_dev, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0);
++}
++
++static int save_camera_state(struct gspca_dev *gspca_dev)
++{
++ int ret;
++
++ ret = do_command(gspca_dev, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0);
++ if (ret)
++ return ret;
++
++ return do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
++}
++
++int command_setformat(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int ret;
++
++ ret = do_command(gspca_dev, CPIA_COMMAND_SetFormat,
++ sd->params.format.videoSize,
++ sd->params.format.subSample,
++ sd->params.format.yuvOrder, 0);
++ if (ret)
++ return ret;
++
++ return do_command(gspca_dev, CPIA_COMMAND_SetROI,
++ sd->params.roi.colStart, sd->params.roi.colEnd,
++ sd->params.roi.rowStart, sd->params.roi.rowEnd);
++}
++
++int command_setcolourparams(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ return do_command(gspca_dev, CPIA_COMMAND_SetColourParams,
++ sd->params.colourParams.brightness,
++ sd->params.colourParams.contrast,
++ sd->params.colourParams.saturation, 0);
++}
++
++int command_setapcor(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ return do_command(gspca_dev, CPIA_COMMAND_SetApcor,
++ sd->params.apcor.gain1,
++ sd->params.apcor.gain2,
++ sd->params.apcor.gain4,
++ sd->params.apcor.gain8);
++}
++
++int command_setvloffset(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ return do_command(gspca_dev, CPIA_COMMAND_SetVLOffset,
++ sd->params.vlOffset.gain1,
++ sd->params.vlOffset.gain2,
++ sd->params.vlOffset.gain4,
++ sd->params.vlOffset.gain8);
++}
++
++int command_setexposure(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int ret;
++
++ ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure,
++ sd->params.exposure.gainMode,
++ 1,
++ sd->params.exposure.compMode,
++ sd->params.exposure.centreWeight,
++ sd->params.exposure.gain,
++ sd->params.exposure.fineExp,
++ sd->params.exposure.coarseExpLo,
++ sd->params.exposure.coarseExpHi,
++ sd->params.exposure.redComp,
++ sd->params.exposure.green1Comp,
++ sd->params.exposure.green2Comp,
++ sd->params.exposure.blueComp);
++ if (ret)
++ return ret;
++
++ if (sd->params.exposure.expMode != 1) {
++ ret = do_command_extended(gspca_dev, CPIA_COMMAND_SetExposure,
++ 0,
++ sd->params.exposure.expMode,
++ 0, 0,
++ sd->params.exposure.gain,
++ sd->params.exposure.fineExp,
++ sd->params.exposure.coarseExpLo,
++ sd->params.exposure.coarseExpHi,
++ 0, 0, 0, 0);
++ }
++
++ return ret;
++}
++
++int command_setcolourbalance(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (sd->params.colourBalance.balanceMode == 1) {
++ int ret;
++
++ ret = do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
++ 1,
++ sd->params.colourBalance.redGain,
++ sd->params.colourBalance.greenGain,
++ sd->params.colourBalance.blueGain);
++ if (ret)
++ return ret;
++
++ return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
++ 3, 0, 0, 0);
++ }
++ if (sd->params.colourBalance.balanceMode == 2) {
++ return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
++ 2, 0, 0, 0);
++ }
++ if (sd->params.colourBalance.balanceMode == 3) {
++ return do_command(gspca_dev, CPIA_COMMAND_SetColourBalance,
++ 3, 0, 0, 0);
++ }
++
++ return -EINVAL;
++}
++
++int command_setcompressiontarget(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ return do_command(gspca_dev, CPIA_COMMAND_SetCompressionTarget,
++ sd->params.compressionTarget.frTargeting,
++ sd->params.compressionTarget.targetFR,
++ sd->params.compressionTarget.targetQ, 0);
++}
++
++int command_setyuvtresh(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ return do_command(gspca_dev, CPIA_COMMAND_SetYUVThresh,
++ sd->params.yuvThreshold.yThreshold,
++ sd->params.yuvThreshold.uvThreshold, 0, 0);
++}
++
++int command_setcompressionparams(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ return do_command_extended(gspca_dev,
++ CPIA_COMMAND_SetCompressionParams,
++ 0, 0, 0, 0,
++ sd->params.compressionParams.hysteresis,
++ sd->params.compressionParams.threshMax,
++ sd->params.compressionParams.smallStep,
++ sd->params.compressionParams.largeStep,
++ sd->params.compressionParams.decimationHysteresis,
++ sd->params.compressionParams.frDiffStepThresh,
++ sd->params.compressionParams.qDiffStepThresh,
++ sd->params.compressionParams.decimationThreshMod);
++}
++
++int command_setcompression(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ return do_command(gspca_dev, CPIA_COMMAND_SetCompression,
++ sd->params.compression.mode,
++ sd->params.compression.decimation, 0, 0);
++}
++
++int command_setsensorfps(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ return do_command(gspca_dev, CPIA_COMMAND_SetSensorFPS,
++ sd->params.sensorFps.divisor,
++ sd->params.sensorFps.baserate, 0, 0);
++}
++
++int command_setflickerctrl(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ return do_command(gspca_dev, CPIA_COMMAND_SetFlickerCtrl,
++ sd->params.flickerControl.flickerMode,
++ sd->params.flickerControl.coarseJump,
++ sd->params.flickerControl.allowableOverExposure,
++ 0);
++}
++
++int command_setecptiming(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ return do_command(gspca_dev, CPIA_COMMAND_SetECPTiming,
++ sd->params.ecpTiming, 0, 0, 0);
++}
++
++int command_pause(struct gspca_dev *gspca_dev)
++{
++ return do_command(gspca_dev, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0);
++}
++
++int command_resume(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ return do_command(gspca_dev, CPIA_COMMAND_InitStreamCap,
++ 0, sd->params.streamStartLine, 0, 0);
++}
++
++int command_setlights(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int ret, p1, p2;
++
++ if (!sd->params.qx3.qx3_detected)
++ return 0;
++
++ p1 = (sd->params.qx3.bottomlight == 0) << 1;
++ p2 = (sd->params.qx3.toplight == 0) << 3;
++
++ ret = do_command(gspca_dev, CPIA_COMMAND_WriteVCReg,
++ 0x90, 0x8F, 0x50, 0);
++ if (ret)
++ return ret;
++
++ return do_command(gspca_dev, CPIA_COMMAND_WriteMCPort, 2, 0,
++ p1 | p2 | 0xE0, 0);
++}
++
++static int set_flicker(struct gspca_dev *gspca_dev, int on, int apply)
++{
++ /* Everything in here is from the Windows driver */
++/* define for compgain calculation */
++#if 0 /* keep */
++#define COMPGAIN(base, curexp, newexp) \
++ (u8) ((((float) base - 128.0) * ((float) curexp / (float) newexp)) + 128.5)
++#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
++ (u16)((float)curexp * (float)(u8)(curcomp + 128) / \
++ (float)(u8)(basecomp - 128))
++#else
++ /* equivalent functions without floating point math */
++#define COMPGAIN(base, curexp, newexp) \
++ (u8)(128 + (((u32)(2*(base-128)*curexp + newexp)) / (2 * newexp)))
++#define EXP_FROM_COMP(basecomp, curcomp, curexp) \
++ (u16)(((u32)(curexp * (u8)(curcomp + 128)) / (u8)(basecomp - 128)))
++#endif
++
++ struct sd *sd = (struct sd *) gspca_dev;
++ int currentexp = sd->params.exposure.coarseExpLo +
++ sd->params.exposure.coarseExpHi * 256;
++ int ret, startexp;
++
++ if (on) {
++ int cj = sd->params.flickerControl.coarseJump;
++ sd->params.flickerControl.flickerMode = 1;
++ sd->params.flickerControl.disabled = 0;
++ if (sd->params.exposure.expMode != 2) {
++ sd->params.exposure.expMode = 2;
++ sd->exposure_status = EXPOSURE_NORMAL;
++ }
++ currentexp = currentexp << sd->params.exposure.gain;
++ sd->params.exposure.gain = 0;
++ /* round down current exposure to nearest value */
++ startexp = (currentexp + ROUND_UP_EXP_FOR_FLICKER) / cj;
++ if (startexp < 1)
++ startexp = 1;
++ startexp = (startexp * cj) - 1;
++ if (FIRMWARE_VERSION(1, 2))
++ while (startexp > MAX_EXP_102)
++ startexp -= cj;
++ else
++ while (startexp > MAX_EXP)
++ startexp -= cj;
++ sd->params.exposure.coarseExpLo = startexp & 0xff;
++ sd->params.exposure.coarseExpHi = startexp >> 8;
++ if (currentexp > startexp) {
++ if (currentexp > (2 * startexp))
++ currentexp = 2 * startexp;
++ sd->params.exposure.redComp =
++ COMPGAIN(COMP_RED, currentexp, startexp);
++ sd->params.exposure.green1Comp =
++ COMPGAIN(COMP_GREEN1, currentexp, startexp);
++ sd->params.exposure.green2Comp =
++ COMPGAIN(COMP_GREEN2, currentexp, startexp);
++ sd->params.exposure.blueComp =
++ COMPGAIN(COMP_BLUE, currentexp, startexp);
++ } else {
++ sd->params.exposure.redComp = COMP_RED;
++ sd->params.exposure.green1Comp = COMP_GREEN1;
++ sd->params.exposure.green2Comp = COMP_GREEN2;
++ sd->params.exposure.blueComp = COMP_BLUE;
++ }
++ if (FIRMWARE_VERSION(1, 2))
++ sd->params.exposure.compMode = 0;
++ else
++ sd->params.exposure.compMode = 1;
++
++ sd->params.apcor.gain1 = 0x18;
++ sd->params.apcor.gain2 = 0x18;
++ sd->params.apcor.gain4 = 0x16;
++ sd->params.apcor.gain8 = 0x14;
++ } else {
++ sd->params.flickerControl.flickerMode = 0;
++ sd->params.flickerControl.disabled = 1;
++ /* Average equivalent coarse for each comp channel */
++ startexp = EXP_FROM_COMP(COMP_RED,
++ sd->params.exposure.redComp, currentexp);
++ startexp += EXP_FROM_COMP(COMP_GREEN1,
++ sd->params.exposure.green1Comp, currentexp);
++ startexp += EXP_FROM_COMP(COMP_GREEN2,
++ sd->params.exposure.green2Comp, currentexp);
++ startexp += EXP_FROM_COMP(COMP_BLUE,
++ sd->params.exposure.blueComp, currentexp);
++ startexp = startexp >> 2;
++ while (startexp > MAX_EXP && sd->params.exposure.gain <
++ sd->params.exposure.gainMode - 1) {
++ startexp = startexp >> 1;
++ ++sd->params.exposure.gain;
++ }
++ if (FIRMWARE_VERSION(1, 2) && startexp > MAX_EXP_102)
++ startexp = MAX_EXP_102;
++ if (startexp > MAX_EXP)
++ startexp = MAX_EXP;
++ sd->params.exposure.coarseExpLo = startexp & 0xff;
++ sd->params.exposure.coarseExpHi = startexp >> 8;
++ sd->params.exposure.redComp = COMP_RED;
++ sd->params.exposure.green1Comp = COMP_GREEN1;
++ sd->params.exposure.green2Comp = COMP_GREEN2;
++ sd->params.exposure.blueComp = COMP_BLUE;
++ sd->params.exposure.compMode = 1;
++ sd->params.apcor.gain1 = 0x18;
++ sd->params.apcor.gain2 = 0x16;
++ sd->params.apcor.gain4 = 0x24;
++ sd->params.apcor.gain8 = 0x34;
++ }
++ sd->params.vlOffset.gain1 = 20;
++ sd->params.vlOffset.gain2 = 24;
++ sd->params.vlOffset.gain4 = 26;
++ sd->params.vlOffset.gain8 = 26;
++
++ if (apply) {
++ ret = command_setexposure(gspca_dev);
++ if (ret)
++ return ret;
++
++ ret = command_setapcor(gspca_dev);
++ if (ret)
++ return ret;
++
++ ret = command_setvloffset(gspca_dev);
++ if (ret)
++ return ret;
++
++ ret = command_setflickerctrl(gspca_dev);
++ if (ret)
++ return ret;
++ }
++
++ return 0;
++#undef EXP_FROM_COMP
++#undef COMPGAIN
++}
++
++/* monitor the exposure and adjust the sensor frame rate if needed */
++static void monitor_exposure(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ u8 exp_acc, bcomp, gain, coarseL, cmd[8];
++ int ret, light_exp, dark_exp, very_dark_exp;
++ int old_exposure, new_exposure, framerate;
++ int setfps = 0, setexp = 0, setflicker = 0;
++
++ /* get necessary stats and register settings from camera */
++ /* do_command can't handle this, so do it ourselves */
++ cmd[0] = CPIA_COMMAND_ReadVPRegs >> 8;
++ cmd[1] = CPIA_COMMAND_ReadVPRegs & 0xff;
++ cmd[2] = 30;
++ cmd[3] = 4;
++ cmd[4] = 9;
++ cmd[5] = 8;
++ cmd[6] = 8;
++ cmd[7] = 0;
++ ret = cpia_usb_transferCmd(gspca_dev, cmd);
++ if (ret) {
++ PDEBUG(D_ERR, "ReadVPRegs(30,4,9,8) - failed: %d", ret);
++ return;
++ }
++ exp_acc = gspca_dev->usb_buf[0];
++ bcomp = gspca_dev->usb_buf[1];
++ gain = gspca_dev->usb_buf[2];
++ coarseL = gspca_dev->usb_buf[3];
++
++ light_exp = sd->params.colourParams.brightness +
++ TC - 50 + EXP_ACC_LIGHT;
++ if (light_exp > 255)
++ light_exp = 255;
++ dark_exp = sd->params.colourParams.brightness +
++ TC - 50 - EXP_ACC_DARK;
++ if (dark_exp < 0)
++ dark_exp = 0;
++ very_dark_exp = dark_exp / 2;
++
++ old_exposure = sd->params.exposure.coarseExpHi * 256 +
++ sd->params.exposure.coarseExpLo;
++
++ if (!sd->params.flickerControl.disabled) {
++ /* Flicker control on */
++ int max_comp = FIRMWARE_VERSION(1, 2) ? MAX_COMP :
++ HIGH_COMP_102;
++ bcomp += 128; /* decode */
++ if (bcomp >= max_comp && exp_acc < dark_exp) {
++ /* dark */
++ if (exp_acc < very_dark_exp) {
++ /* very dark */
++ if (sd->exposure_status == EXPOSURE_VERY_DARK)
++ ++sd->exposure_count;
++ else {
++ sd->exposure_status =
++ EXPOSURE_VERY_DARK;
++ sd->exposure_count = 1;
++ }
++ } else {
++ /* just dark */
++ if (sd->exposure_status == EXPOSURE_DARK)
++ ++sd->exposure_count;
++ else {
++ sd->exposure_status = EXPOSURE_DARK;
++ sd->exposure_count = 1;
++ }
++ }
++ } else if (old_exposure <= LOW_EXP || exp_acc > light_exp) {
++ /* light */
++ if (old_exposure <= VERY_LOW_EXP) {
++ /* very light */
++ if (sd->exposure_status == EXPOSURE_VERY_LIGHT)
++ ++sd->exposure_count;
++ else {
++ sd->exposure_status =
++ EXPOSURE_VERY_LIGHT;
++ sd->exposure_count = 1;
++ }
++ } else {
++ /* just light */
++ if (sd->exposure_status == EXPOSURE_LIGHT)
++ ++sd->exposure_count;
++ else {
++ sd->exposure_status = EXPOSURE_LIGHT;
++ sd->exposure_count = 1;
++ }
++ }
++ } else {
++ /* not dark or light */
++ sd->exposure_status = EXPOSURE_NORMAL;
++ }
++ } else {
++ /* Flicker control off */
++ if (old_exposure >= MAX_EXP && exp_acc < dark_exp) {
++ /* dark */
++ if (exp_acc < very_dark_exp) {
++ /* very dark */
++ if (sd->exposure_status == EXPOSURE_VERY_DARK)
++ ++sd->exposure_count;
++ else {
++ sd->exposure_status =
++ EXPOSURE_VERY_DARK;
++ sd->exposure_count = 1;
++ }
++ } else {
++ /* just dark */
++ if (sd->exposure_status == EXPOSURE_DARK)
++ ++sd->exposure_count;
++ else {
++ sd->exposure_status = EXPOSURE_DARK;
++ sd->exposure_count = 1;
++ }
++ }
++ } else if (old_exposure <= LOW_EXP || exp_acc > light_exp) {
++ /* light */
++ if (old_exposure <= VERY_LOW_EXP) {
++ /* very light */
++ if (sd->exposure_status == EXPOSURE_VERY_LIGHT)
++ ++sd->exposure_count;
++ else {
++ sd->exposure_status =
++ EXPOSURE_VERY_LIGHT;
++ sd->exposure_count = 1;
++ }
++ } else {
++ /* just light */
++ if (sd->exposure_status == EXPOSURE_LIGHT)
++ ++sd->exposure_count;
++ else {
++ sd->exposure_status = EXPOSURE_LIGHT;
++ sd->exposure_count = 1;
++ }
++ }
++ } else {
++ /* not dark or light */
++ sd->exposure_status = EXPOSURE_NORMAL;
++ }
++ }
++
++ framerate = atomic_read(&sd->fps);
++ if (framerate > 30 || framerate < 1)
++ framerate = 1;
++
++ if (!sd->params.flickerControl.disabled) {
++ /* Flicker control on */
++ if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
++ sd->exposure_status == EXPOSURE_DARK) &&
++ sd->exposure_count >= DARK_TIME * framerate &&
++ sd->params.sensorFps.divisor < 3) {
++
++ /* dark for too long */
++ ++sd->params.sensorFps.divisor;
++ setfps = 1;
++
++ sd->params.flickerControl.coarseJump =
++ flicker_jumps[sd->mainsFreq]
++ [sd->params.sensorFps.baserate]
++ [sd->params.sensorFps.divisor];
++ setflicker = 1;
++
++ new_exposure = sd->params.flickerControl.coarseJump-1;
++ while (new_exposure < old_exposure / 2)
++ new_exposure +=
++ sd->params.flickerControl.coarseJump;
++ sd->params.exposure.coarseExpLo = new_exposure & 0xff;
++ sd->params.exposure.coarseExpHi = new_exposure >> 8;
++ setexp = 1;
++ sd->exposure_status = EXPOSURE_NORMAL;
++ PDEBUG(D_CONF, "Automatically decreasing sensor_fps");
++
++ } else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT ||
++ sd->exposure_status == EXPOSURE_LIGHT) &&
++ sd->exposure_count >= LIGHT_TIME * framerate &&
++ sd->params.sensorFps.divisor > 0) {
++
++ /* light for too long */
++ int max_exp = FIRMWARE_VERSION(1, 2) ? MAX_EXP_102 :
++ MAX_EXP;
++ --sd->params.sensorFps.divisor;
++ setfps = 1;
++
++ sd->params.flickerControl.coarseJump =
++ flicker_jumps[sd->mainsFreq]
++ [sd->params.sensorFps.baserate]
++ [sd->params.sensorFps.divisor];
++ setflicker = 1;
++
++ new_exposure = sd->params.flickerControl.coarseJump-1;
++ while (new_exposure < 2 * old_exposure &&
++ new_exposure +
++ sd->params.flickerControl.coarseJump < max_exp)
++ new_exposure +=
++ sd->params.flickerControl.coarseJump;
++ sd->params.exposure.coarseExpLo = new_exposure & 0xff;
++ sd->params.exposure.coarseExpHi = new_exposure >> 8;
++ setexp = 1;
++ sd->exposure_status = EXPOSURE_NORMAL;
++ PDEBUG(D_CONF, "Automatically increasing sensor_fps");
++ }
++ } else {
++ /* Flicker control off */
++ if ((sd->exposure_status == EXPOSURE_VERY_DARK ||
++ sd->exposure_status == EXPOSURE_DARK) &&
++ sd->exposure_count >= DARK_TIME * framerate &&
++ sd->params.sensorFps.divisor < 3) {
++
++ /* dark for too long */
++ ++sd->params.sensorFps.divisor;
++ setfps = 1;
++
++ if (sd->params.exposure.gain > 0) {
++ --sd->params.exposure.gain;
++ setexp = 1;
++ }
++ sd->exposure_status = EXPOSURE_NORMAL;
++ PDEBUG(D_CONF, "Automatically decreasing sensor_fps");
++
++ } else if ((sd->exposure_status == EXPOSURE_VERY_LIGHT ||
++ sd->exposure_status == EXPOSURE_LIGHT) &&
++ sd->exposure_count >= LIGHT_TIME * framerate &&
++ sd->params.sensorFps.divisor > 0) {
++
++ /* light for too long */
++ --sd->params.sensorFps.divisor;
++ setfps = 1;
++
++ if (sd->params.exposure.gain <
++ sd->params.exposure.gainMode - 1) {
++ ++sd->params.exposure.gain;
++ setexp = 1;
++ }
++ sd->exposure_status = EXPOSURE_NORMAL;
++ PDEBUG(D_CONF, "Automatically increasing sensor_fps");
++ }
++ }
++
++ if (setexp)
++ command_setexposure(gspca_dev);
++
++ if (setfps)
++ command_setsensorfps(gspca_dev);
++
++ if (setflicker)
++ command_setflickerctrl(gspca_dev);
++}
++
++/*-----------------------------------------------------------------*/
++/* if flicker is switched off, this function switches it back on.It checks,
++ however, that conditions are suitable before restarting it.
++ This should only be called for firmware version 1.2.
++
++ It also adjust the colour balance when an exposure step is detected - as
++ long as flicker is running
++*/
++static void restart_flicker(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int cam_exposure, old_exp;
++
++ if (!FIRMWARE_VERSION(1, 2))
++ return;
++
++ cam_exposure = atomic_read(&sd->cam_exposure);
++
++ if (sd->params.flickerControl.flickerMode == 0 ||
++ cam_exposure == 0)
++ return;
++
++ old_exp = sd->params.exposure.coarseExpLo +
++ sd->params.exposure.coarseExpHi*256;
++ /*
++ see how far away camera exposure is from a valid
++ flicker exposure value
++ */
++ cam_exposure %= sd->params.flickerControl.coarseJump;
++ if (!sd->params.flickerControl.disabled &&
++ cam_exposure <= sd->params.flickerControl.coarseJump - 3) {
++ /* Flicker control auto-disabled */
++ sd->params.flickerControl.disabled = 1;
++ }
++
++ if (sd->params.flickerControl.disabled &&
++ old_exp > sd->params.flickerControl.coarseJump +
++ ROUND_UP_EXP_FOR_FLICKER) {
++ /* exposure is now high enough to switch
++ flicker control back on */
++ set_flicker(gspca_dev, 1, 1);
++ }
++}
++
++/* this function is called at probe time */
++static int sd_config(struct gspca_dev *gspca_dev,
++ const struct usb_device_id *id)
++{
++ struct cam *cam;
++
++ reset_camera_params(gspca_dev);
++
++ PDEBUG(D_PROBE, "cpia CPiA camera detected (vid/pid 0x%04X:0x%04X)",
++ id->idVendor, id->idProduct);
++
++ cam = &gspca_dev->cam;
++ cam->cam_mode = mode;
++ cam->nmodes = ARRAY_SIZE(mode);
++
++ sd_setfreq(gspca_dev, FREQ_DEF);
++
++ return 0;
++}
++
++/* -- start the camera -- */
++static int sd_start(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int priv, ret;
++
++ /* Start the camera in low power mode */
++ if (goto_low_power(gspca_dev)) {
++ if (sd->params.status.systemState != WARM_BOOT_STATE) {
++ PDEBUG(D_ERR, "unexpected systemstate: %02x",
++ sd->params.status.systemState);
++ printstatus(&sd->params);
++ return -ENODEV;
++ }
++
++ /* FIXME: this is just dirty trial and error */
++ ret = goto_high_power(gspca_dev);
++ if (ret)
++ return ret;
++
++ ret = do_command(gspca_dev, CPIA_COMMAND_DiscardFrame,
++ 0, 0, 0, 0);
++ if (ret)
++ return ret;
++
++ ret = goto_low_power(gspca_dev);
++ if (ret)
++ return ret;
++ }
++
++ /* procedure described in developer's guide p3-28 */
++
++ /* Check the firmware version. */
++ sd->params.version.firmwareVersion = 0;
++ get_version_information(gspca_dev);
++ if (sd->params.version.firmwareVersion != 1) {
++ PDEBUG(D_ERR, "only firmware version 1 is supported (got: %d)",
++ sd->params.version.firmwareVersion);
++ return -ENODEV;
++ }
++
++ /* A bug in firmware 1-02 limits gainMode to 2 */
++ if (sd->params.version.firmwareRevision <= 2 &&
++ sd->params.exposure.gainMode > 2) {
++ sd->params.exposure.gainMode = 2;
++ }
++
++ /* set QX3 detected flag */
++ sd->params.qx3.qx3_detected = (sd->params.pnpID.vendor == 0x0813 &&
++ sd->params.pnpID.product == 0x0001);
++
++ /* The fatal error checking should be done after
++ * the camera powers up (developer's guide p 3-38) */
++
++ /* Set streamState before transition to high power to avoid bug
++ * in firmware 1-02 */
++ ret = do_command(gspca_dev, CPIA_COMMAND_ModifyCameraStatus,
++ STREAMSTATE, 0, STREAM_NOT_READY, 0);
++ if (ret)
++ return ret;
++
++ /* GotoHiPower */
++ ret = goto_high_power(gspca_dev);
++ if (ret)
++ return ret;
++
++ /* Check the camera status */
++ ret = do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
++ if (ret)
++ return ret;
++
++ if (sd->params.status.fatalError) {
++ PDEBUG(D_ERR, "fatal_error: %04x, vp_status: %04x",
++ sd->params.status.fatalError,
++ sd->params.status.vpStatus);
++ return -EIO;
++ }
++
++ /* VPVersion can't be retrieved before the camera is in HiPower,
++ * so get it here instead of in get_version_information. */
++ ret = do_command(gspca_dev, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0);
++ if (ret)
++ return ret;
++
++ /* Determine video mode settings */
++ sd->params.streamStartLine = 120;
++
++ priv = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
++ if (priv & 0x01) { /* crop */
++ sd->params.roi.colStart = 2;
++ sd->params.roi.rowStart = 6;
++ } else {
++ sd->params.roi.colStart = 0;
++ sd->params.roi.rowStart = 0;
++ }
++
++ if (priv & 0x02) { /* quarter */
++ sd->params.format.videoSize = VIDEOSIZE_QCIF;
++ sd->params.roi.colStart /= 2;
++ sd->params.roi.rowStart /= 2;
++ sd->params.streamStartLine /= 2;
++ } else
++ sd->params.format.videoSize = VIDEOSIZE_CIF;
++
++ sd->params.roi.colEnd = sd->params.roi.colStart +
++ (gspca_dev->width >> 3);
++ sd->params.roi.rowEnd = sd->params.roi.rowStart +
++ (gspca_dev->height >> 2);
++
++ /* And now set the camera to a known state */
++ ret = do_command(gspca_dev, CPIA_COMMAND_SetGrabMode,
++ CPIA_GRAB_CONTINEOUS, 0, 0, 0);
++ if (ret)
++ return ret;
++ /* We start with compression disabled, as we need one uncompressed
++ frame to handle later compressed frames */
++ ret = do_command(gspca_dev, CPIA_COMMAND_SetCompression,
++ CPIA_COMPRESSION_NONE,
++ NO_DECIMATION, 0, 0);
++ if (ret)
++ return ret;
++ ret = command_setcompressiontarget(gspca_dev);
++ if (ret)
++ return ret;
++ ret = command_setcolourparams(gspca_dev);
++ if (ret)
++ return ret;
++ ret = command_setformat(gspca_dev);
++ if (ret)
++ return ret;
++ ret = command_setyuvtresh(gspca_dev);
++ if (ret)
++ return ret;
++ ret = command_setecptiming(gspca_dev);
++ if (ret)
++ return ret;
++ ret = command_setcompressionparams(gspca_dev);
++ if (ret)
++ return ret;
++ ret = command_setexposure(gspca_dev);
++ if (ret)
++ return ret;
++ ret = command_setcolourbalance(gspca_dev);
++ if (ret)
++ return ret;
++ ret = command_setsensorfps(gspca_dev);
++ if (ret)
++ return ret;
++ ret = command_setapcor(gspca_dev);
++ if (ret)
++ return ret;
++ ret = command_setflickerctrl(gspca_dev);
++ if (ret)
++ return ret;
++ ret = command_setvloffset(gspca_dev);
++ if (ret)
++ return ret;
++
++ /* Start stream */
++ ret = command_resume(gspca_dev);
++ if (ret)
++ return ret;
++
++ /* Wait 6 frames before turning compression on for the sensor to get
++ all settings and AEC/ACB to settle */
++ sd->first_frame = 6;
++ sd->exposure_status = EXPOSURE_NORMAL;
++ sd->exposure_count = 0;
++ atomic_set(&sd->cam_exposure, 0);
++ atomic_set(&sd->fps, 0);
++
++ return 0;
++}
++
++static void sd_stopN(struct gspca_dev *gspca_dev)
++{
++ command_pause(gspca_dev);
++
++ /* save camera state for later open (developers guide ch 3.5.3) */
++ save_camera_state(gspca_dev);
++
++ /* GotoLoPower */
++ goto_low_power(gspca_dev);
++
++ /* Update the camera status */
++ do_command(gspca_dev, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0);
++}
++
++/* this function is called at probe and resume time */
++static int sd_init(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int ret;
++
++ /* Start / Stop the camera to make sure we are talking to
++ a supported camera, and to get some information from it
++ to print. */
++ ret = sd_start(gspca_dev);
++ if (ret)
++ return ret;
++
++ sd_stopN(gspca_dev);
++
++ PDEBUG(D_PROBE, "CPIA Version: %d.%02d (%d.%d)",
++ sd->params.version.firmwareVersion,
++ sd->params.version.firmwareRevision,
++ sd->params.version.vcVersion,
++ sd->params.version.vcRevision);
++ PDEBUG(D_PROBE, "CPIA PnP-ID: %04x:%04x:%04x",
++ sd->params.pnpID.vendor, sd->params.pnpID.product,
++ sd->params.pnpID.deviceRevision);
++ PDEBUG(D_PROBE, "VP-Version: %d.%d %04x",
++ sd->params.vpVersion.vpVersion,
++ sd->params.vpVersion.vpRevision,
++ sd->params.vpVersion.cameraHeadID);
++
++ return 0;
++}
++
++static void sd_pkt_scan(struct gspca_dev *gspca_dev,
++ u8 *data,
++ int len)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ /* Check for SOF */
++ if (len >= 64 &&
++ data[0] == MAGIC_0 && data[1] == MAGIC_1 &&
++ data[16] == sd->params.format.videoSize &&
++ data[17] == sd->params.format.subSample &&
++ data[18] == sd->params.format.yuvOrder &&
++ data[24] == sd->params.roi.colStart &&
++ data[25] == sd->params.roi.colEnd &&
++ data[26] == sd->params.roi.rowStart &&
++ data[27] == sd->params.roi.rowEnd) {
++ struct gspca_frame *frame = gspca_get_i_frame(gspca_dev);
++
++ atomic_set(&sd->cam_exposure, data[39] * 2);
++ atomic_set(&sd->fps, data[41]);
++
++ if (frame == NULL) {
++ gspca_dev->last_packet_type = DISCARD_PACKET;
++ return;
++ }
++
++ /* Check for proper EOF for last frame */
++ if ((frame->data_end - frame->data) > 4 &&
++ frame->data_end[-4] == 0xff &&
++ frame->data_end[-3] == 0xff &&
++ frame->data_end[-2] == 0xff &&
++ frame->data_end[-1] == 0xff)
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ NULL, 0);
++
++ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
++ return;
++ }
++
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
++}
++
++static void sd_dq_callback(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ /* Set the normal compression settings once we have captured a
++ few uncompressed frames (and AEC has hopefully settled) */
++ if (sd->first_frame) {
++ sd->first_frame--;
++ if (sd->first_frame == 0)
++ command_setcompression(gspca_dev);
++ }
++
++ /* Switch flicker control back on if it got turned off */
++ restart_flicker(gspca_dev);
++
++ /* If AEC is enabled, monitor the exposure and
++ adjust the sensor frame rate if needed */
++ if (sd->params.exposure.expMode == 2)
++ monitor_exposure(gspca_dev);
++
++ /* Update our knowledge of the camera state */
++ do_command(gspca_dev, CPIA_COMMAND_GetExposure, 0, 0, 0, 0);
++ if (sd->params.qx3.qx3_detected)
++ do_command(gspca_dev, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0);
++}
++
++static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int ret;
++
++ sd->params.colourParams.brightness = val;
++ sd->params.flickerControl.allowableOverExposure =
++ find_over_exposure(sd->params.colourParams.brightness);
++ if (gspca_dev->streaming) {
++ ret = command_setcolourparams(gspca_dev);
++ if (ret)
++ return ret;
++ return command_setflickerctrl(gspca_dev);
++ }
++ return 0;
++}
++
++static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->params.colourParams.brightness;
++ return 0;
++}
++
++static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->params.colourParams.contrast = val;
++ if (gspca_dev->streaming)
++ return command_setcolourparams(gspca_dev);
++
++ return 0;
++}
++
++static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->params.colourParams.contrast;
++ return 0;
++}
++
++static int sd_setsaturation(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->params.colourParams.saturation = val;
++ if (gspca_dev->streaming)
++ return command_setcolourparams(gspca_dev);
++
++ return 0;
++}
++
++static int sd_getsaturation(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->params.colourParams.saturation;
++ return 0;
++}
++
++static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int on;
++
++ switch (val) {
++ case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
++ on = 0;
++ break;
++ case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
++ on = 1;
++ sd->mainsFreq = 0;
++ break;
++ case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
++ on = 1;
++ sd->mainsFreq = 1;
++ break;
++ default:
++ return -EINVAL;
++ }
++
++ sd->freq = val;
++ sd->params.flickerControl.coarseJump =
++ flicker_jumps[sd->mainsFreq]
++ [sd->params.sensorFps.baserate]
++ [sd->params.sensorFps.divisor];
++
++ return set_flicker(gspca_dev, on, gspca_dev->streaming);
++}
++
++static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->freq;
++ return 0;
++}
++
++static int sd_setcomptarget(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->params.compressionTarget.frTargeting = val;
++ if (gspca_dev->streaming)
++ return command_setcompressiontarget(gspca_dev);
++
++ return 0;
++}
++
++static int sd_getcomptarget(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->params.compressionTarget.frTargeting;
++ return 0;
++}
++
++static int sd_querymenu(struct gspca_dev *gspca_dev,
++ struct v4l2_querymenu *menu)
++{
++ switch (menu->id) {
++ case V4L2_CID_POWER_LINE_FREQUENCY:
++ switch (menu->index) {
++ case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
++ strcpy((char *) menu->name, "NoFliker");
++ return 0;
++ case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
++ strcpy((char *) menu->name, "50 Hz");
++ return 0;
++ case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
++ strcpy((char *) menu->name, "60 Hz");
++ return 0;
++ }
++ break;
++ case V4L2_CID_COMP_TARGET:
++ switch (menu->index) {
++ case CPIA_COMPRESSION_TARGET_QUALITY:
++ strcpy((char *) menu->name, "Quality");
++ return 0;
++ case CPIA_COMPRESSION_TARGET_FRAMERATE:
++ strcpy((char *) menu->name, "Framerate");
++ return 0;
++ }
++ break;
++ }
++ return -EINVAL;
++}
++
++/* sub-driver description */
++static const struct sd_desc sd_desc = {
++ .name = MODULE_NAME,
++ .ctrls = sd_ctrls,
++ .nctrls = ARRAY_SIZE(sd_ctrls),
++ .config = sd_config,
++ .init = sd_init,
++ .start = sd_start,
++ .stopN = sd_stopN,
++ .dq_callback = sd_dq_callback,
++ .pkt_scan = sd_pkt_scan,
++ .querymenu = sd_querymenu,
++};
++
++/* -- module initialisation -- */
++static const __devinitdata struct usb_device_id device_table[] = {
++ {USB_DEVICE(0x0553, 0x0002)},
++ {USB_DEVICE(0x0813, 0x0001)},
++ {}
++};
++MODULE_DEVICE_TABLE(usb, device_table);
++
++/* -- device connect -- */
++static int sd_probe(struct usb_interface *intf,
++ const struct usb_device_id *id)
++{
++ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
++ THIS_MODULE);
++}
++
++static struct usb_driver sd_driver = {
++ .name = MODULE_NAME,
++ .id_table = device_table,
++ .probe = sd_probe,
++ .disconnect = gspca_disconnect,
++#ifdef CONFIG_PM
++ .suspend = gspca_suspend,
++ .resume = gspca_resume,
++#endif
++};
++
++/* -- module insert / remove -- */
++static int __init sd_mod_init(void)
++{
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
++ PDEBUG(D_PROBE, "registered");
++ return 0;
++}
++static void __exit sd_mod_exit(void)
++{
++ usb_deregister(&sd_driver);
++ PDEBUG(D_PROBE, "deregistered");
++}
++
++module_init(sd_mod_init);
++module_exit(sd_mod_exit);
+diff --git a/drivers/media/video/gspca/etoms.c b/drivers/media/video/gspca/etoms.c
+index c1461e6..ecd4d74 100644
+--- a/drivers/media/video/gspca/etoms.c
++++ b/drivers/media/video/gspca/etoms.c
+@@ -52,7 +52,7 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+@@ -752,8 +752,7 @@ static void do_autogain(struct gspca_dev *gspca_dev)
+ #undef LIMIT
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+- __u8 *data, /* isoc packet */
++ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ int seqframe;
+@@ -767,14 +766,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ data[2], data[3], data[4], data[5]);
+ data += 30;
+ /* don't change datalength as the chips provided it */
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+- data, 0);
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
++ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ return;
+ }
+ if (len) {
+ data += 8;
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ } else { /* Drop Packet */
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ }
+@@ -853,7 +851,7 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+ }
+
+ /* sub-driver description */
+-static struct sd_desc sd_desc = {
++static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+@@ -866,7 +864,7 @@ static struct sd_desc sd_desc = {
+ };
+
+ /* -- module initialisation -- */
+-static __devinitdata struct usb_device_id device_table[] = {
++static const struct usb_device_id device_table[] __devinitconst = {
+ {USB_DEVICE(0x102c, 0x6151), .driver_info = SENSOR_PAS106},
+ #if !defined CONFIG_USB_ET61X251 && !defined CONFIG_USB_ET61X251_MODULE
+ {USB_DEVICE(0x102c, 0x6251), .driver_info = SENSOR_TAS5130CXX},
+@@ -877,7 +875,7 @@ static __devinitdata struct usb_device_id device_table[] = {
+ MODULE_DEVICE_TABLE(usb, device_table);
+
+ /* -- device connect -- */
+-static int sd_probe(struct usb_interface *intf,
++static int __devinit sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+ {
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+diff --git a/drivers/media/video/gspca/finepix.c b/drivers/media/video/gspca/finepix.c
+index 480ec5c..5d90e74 100644
+--- a/drivers/media/video/gspca/finepix.c
++++ b/drivers/media/video/gspca/finepix.c
+@@ -82,7 +82,6 @@ static void dostream(struct work_struct *work)
+ struct gspca_dev *gspca_dev = &dev->gspca_dev;
+ struct urb *urb = gspca_dev->urb[0];
+ u8 *data = urb->transfer_buffer;
+- struct gspca_frame *frame;
+ int ret = 0;
+ int len;
+
+@@ -118,10 +117,6 @@ again:
+ }
+ if (!gspca_dev->present || !gspca_dev->streaming)
+ goto out;
+- frame = gspca_get_i_frame(&dev->gspca_dev);
+- if (frame == NULL)
+- gspca_dev->last_packet_type = DISCARD_PACKET;
+-
+ if (len < FPIX_MAX_TRANSFER ||
+ (data[len - 2] == 0xff &&
+ data[len - 1] == 0xd9)) {
+@@ -132,21 +127,17 @@ again:
+ * but there's nothing we can do. We also end
+ * here if the the jpeg ends right at the end
+ * of the frame. */
+- if (frame)
+- frame = gspca_frame_add(gspca_dev,
+- LAST_PACKET,
+- frame,
+- data, len);
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ data, len);
+ break;
+ }
+
+ /* got a partial image */
+- if (frame)
+- gspca_frame_add(gspca_dev,
+- gspca_dev->last_packet_type
+- == LAST_PACKET
+- ? FIRST_PACKET : INTER_PACKET,
+- frame, data, len);
++ gspca_frame_add(gspca_dev,
++ gspca_dev->last_packet_type
++ == LAST_PACKET
++ ? FIRST_PACKET : INTER_PACKET,
++ data, len);
+ }
+
+ /* We must wait before trying reading the next
+diff --git a/drivers/media/video/gspca/gl860/gl860-mi1320.c b/drivers/media/video/gspca/gl860/gl860-mi1320.c
+index 39f6261..c276a7d 100644
+--- a/drivers/media/video/gspca/gl860/gl860-mi1320.c
++++ b/drivers/media/video/gspca/gl860/gl860-mi1320.c
+@@ -1,6 +1,5 @@
+-/* @file gl860-mi1320.c
+- * @author Olivier LORIN from my logs
+- * @date 2009-08-27
++/* Subdriver for the GL860 chip with the MI1320 sensor
++ * Author Olivier LORIN from own logs
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+@@ -127,49 +126,49 @@ static u8 dat_wbalBL[] =
+
+ static u8 dat_hvflip1[] = {0xf0, 0x00, 0xf1, 0x00};
+
+-static u8 s000[] =
++static u8 dat_common00[] =
+ "\x00\x01\x07\x6a\x06\x63\x0d\x6a" "\xc0\x00\x10\x10\xc1\x03\xc2\x42"
+ "\xd8\x04\x58\x00\x04\x02";
+-static u8 s001[] =
++static u8 dat_common01[] =
+ "\x0d\x00\xf1\x0b\x0d\x00\xf1\x08" "\x35\x00\xf1\x22\x68\x00\xf1\x5d"
+ "\xf0\x00\xf1\x01\x06\x70\xf1\x0e" "\xf0\x00\xf1\x02\xdd\x18\xf1\xe0";
+-static u8 s002[] =
++static u8 dat_common02[] =
+ "\x05\x01\xf1\x84\x06\x00\xf1\x44" "\x07\x00\xf1\xbe\x08\x00\xf1\x1e"
+ "\x20\x01\xf1\x03\x21\x84\xf1\x00" "\x22\x0d\xf1\x0f\x24\x80\xf1\x00"
+ "\x34\x18\xf1\x2d\x35\x00\xf1\x22" "\x43\x83\xf1\x83\x59\x00\xf1\xff";
+-static u8 s003[] =
++static u8 dat_common03[] =
+ "\xf0\x00\xf1\x02\x39\x06\xf1\x8c" "\x3a\x06\xf1\x8c\x3b\x03\xf1\xda"
+ "\x3c\x05\xf1\x30\x57\x01\xf1\x0c" "\x58\x01\xf1\x42\x59\x01\xf1\x0c"
+ "\x5a\x01\xf1\x42\x5c\x13\xf1\x0e" "\x5d\x17\xf1\x12\x64\x1e\xf1\x1c";
+-static u8 s004[] =
++static u8 dat_common04[] =
+ "\xf0\x00\xf1\x02\x24\x5f\xf1\x20" "\x28\xea\xf1\x02\x5f\x41\xf1\x43";
+-static u8 s005[] =
++static u8 dat_common05[] =
+ "\x02\x00\xf1\xee\x03\x29\xf1\x1a" "\x04\x02\xf1\xa4\x09\x00\xf1\x68"
+ "\x0a\x00\xf1\x2a\x0b\x00\xf1\x04" "\x0c\x00\xf1\x93\x0d\x00\xf1\x82"
+ "\x0e\x00\xf1\x40\x0f\x00\xf1\x5f" "\x10\x00\xf1\x4e\x11\x00\xf1\x5b";
+-static u8 s006[] =
++static u8 dat_common06[] =
+ "\x15\x00\xf1\xc9\x16\x00\xf1\x5e" "\x17\x00\xf1\x9d\x18\x00\xf1\x06"
+ "\x19\x00\xf1\x89\x1a\x00\xf1\x12" "\x1b\x00\xf1\xa1\x1c\x00\xf1\xe4"
+ "\x1d\x00\xf1\x7a\x1e\x00\xf1\x64" "\xf6\x00\xf1\x5f";
+-static u8 s007[] =
++static u8 dat_common07[] =
+ "\xf0\x00\xf1\x01\x53\x09\xf1\x03" "\x54\x3d\xf1\x1c\x55\x99\xf1\x72"
+ "\x56\xc1\xf1\xb1\x57\xd8\xf1\xce" "\x58\xe0\xf1\x00\xdc\x0a\xf1\x03"
+ "\xdd\x45\xf1\x20\xde\xae\xf1\x82" "\xdf\xdc\xf1\xc9\xe0\xf6\xf1\xea"
+ "\xe1\xff\xf1\x00";
+-static u8 s008[] =
++static u8 dat_common08[] =
+ "\xf0\x00\xf1\x01\x80\x00\xf1\x06" "\x81\xf6\xf1\x08\x82\xfb\xf1\xf7"
+ "\x83\x00\xf1\xfe\xb6\x07\xf1\x03" "\xb7\x18\xf1\x0c\x84\xfb\xf1\x06"
+ "\x85\xfb\xf1\xf9\x86\x00\xf1\xff" "\xb8\x07\xf1\x04\xb9\x16\xf1\x0a";
+-static u8 s009[] =
++static u8 dat_common09[] =
+ "\x87\xfa\xf1\x05\x88\xfc\xf1\xf9" "\x89\x00\xf1\xff\xba\x06\xf1\x03"
+ "\xbb\x17\xf1\x09\x8a\xe8\xf1\x14" "\x8b\xf7\xf1\xf0\x8c\xfd\xf1\xfa"
+ "\x8d\x00\xf1\x00\xbc\x05\xf1\x01" "\xbd\x0c\xf1\x08\xbe\x00\xf1\x14";
+-static u8 s010[] =
++static u8 dat_common10[] =
+ "\x8e\xea\xf1\x13\x8f\xf7\xf1\xf2" "\x90\xfd\xf1\xfa\x91\x00\xf1\x00"
+ "\xbf\x05\xf1\x01\xc0\x0a\xf1\x08" "\xc1\x00\xf1\x0c\x92\xed\xf1\x0f"
+ "\x93\xf9\xf1\xf4\x94\xfe\xf1\xfb" "\x95\x00\xf1\x00\xc2\x04\xf1\x01"
+ "\xc3\x0a\xf1\x07\xc4\x00\xf1\x10";
+-static u8 s011[] =
++static u8 dat_common11[] =
+ "\xf0\x00\xf1\x01\x05\x00\xf1\x06" "\x25\x00\xf1\x55\x34\x10\xf1\x10"
+ "\x35\xf0\xf1\x10\x3a\x02\xf1\x03" "\x3b\x04\xf1\x2a\x9b\x43\xf1\x00"
+ "\xa4\x03\xf1\xc0\xa7\x02\xf1\x81";
+@@ -222,26 +221,26 @@ void mi1320_init_settings(struct gspca_dev *gspca_dev)
+
+ static void common(struct gspca_dev *gspca_dev)
+ {
+- s32 n; /* reserved for FETCH macros */
++ s32 n; /* reserved for FETCH functions */
+
+- ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, s000);
++ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 22, dat_common00);
+ ctrl_out(gspca_dev, 0x40, 1, 0x0041, 0x0000, 0, NULL);
+- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, s001);
++ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 32, dat_common01);
+ n = fetch_validx(gspca_dev, tbl_common, ARRAY_SIZE(tbl_common));
+- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s002);
+- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s003);
+- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, s004);
+- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s005);
+- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, s006);
++ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common02);
++ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common03);
++ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 16, dat_common04);
++ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common05);
++ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 44, dat_common06);
+ keep_on_fetching_validx(gspca_dev, tbl_common,
+ ARRAY_SIZE(tbl_common), n);
+- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, s007);
+- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s008);
+- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, s009);
+- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, s010);
++ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 52, dat_common07);
++ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common08);
++ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 48, dat_common09);
++ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 56, dat_common10);
+ keep_on_fetching_validx(gspca_dev, tbl_common,
+ ARRAY_SIZE(tbl_common), n);
+- ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, s011);
++ ctrl_out(gspca_dev, 0x40, 3, 0xba00, 0x0200, 40, dat_common11);
+ keep_on_fetching_validx(gspca_dev, tbl_common,
+ ARRAY_SIZE(tbl_common), n);
+ }
+@@ -346,7 +345,7 @@ static int mi1320_configure_alt(struct gspca_dev *gspca_dev)
+ return 0;
+ }
+
+-int mi1320_camera_settings(struct gspca_dev *gspca_dev)
++static int mi1320_camera_settings(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+diff --git a/drivers/media/video/gspca/gl860/gl860-mi2020.c b/drivers/media/video/gspca/gl860/gl860-mi2020.c
+index ffb09fe..7c31b4f 100644
+--- a/drivers/media/video/gspca/gl860/gl860-mi2020.c
++++ b/drivers/media/video/gspca/gl860/gl860-mi2020.c
+@@ -1,7 +1,6 @@
+-/* @file gl860-mi2020.c
+- * @author Olivier LORIN, from Ice/Soro2005's logs(A), Fret_saw/Hulkie's
++/* Subdriver for the GL860 chip with the MI2020 sensor
++ * Author Olivier LORIN, from Ice/Soro2005's logs(A), Fret_saw/Hulkie's
+ * logs(B) and Tricid"s logs(C). With the help of Kytrix/BUGabundo/Blazercist.
+- * @date 2009-08-27
+ *
+ * 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
+@@ -41,7 +40,7 @@ static u8 dat_freq1[] = { 0x8c, 0xa4, 0x04 };
+ static u8 dat_multi5[] = { 0x8c, 0xa1, 0x03 };
+ static u8 dat_multi6[] = { 0x90, 0x00, 0x05 };
+
+-static struct validx tbl_common_a[] = {
++static struct validx tbl_common1[] = {
+ {0x0000, 0x0000},
+ {1, 0xffff}, /* msleep(35); */
+ {0x006a, 0x0007}, {0x0063, 0x0006}, {0x006a, 0x000d}, {0x0000, 0x00c0},
+@@ -49,7 +48,7 @@ static struct validx tbl_common_a[] = {
+ {0x0000, 0x0058}, {0x0002, 0x0004}, {0x0041, 0x0000},
+ };
+
+-static struct validx tbl_common_b[] = {
++static struct validx tbl_common2[] = {
+ {0x006a, 0x0007},
+ {35, 0xffff},
+ {0x00ef, 0x0006},
+@@ -60,7 +59,7 @@ static struct validx tbl_common_b[] = {
+ {0x0004, 0x00d8}, {0x0000, 0x0058}, {0x0041, 0x0000},
+ };
+
+-static struct idxdata tbl_common_c[] = {
++static struct idxdata tbl_common3[] = {
+ {0x32, "\x02\x00\x08"}, {0x33, "\xf4\x03\x1d"},
+ {6, "\xff\xff\xff"}, /* 12 */
+ {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"},
+@@ -109,7 +108,7 @@ static struct idxdata tbl_common_c[] = {
+ {0x33, "\x8c\xa2\x03"}, {0x33, "\x90\x00\xbb"},
+ };
+
+-static struct idxdata tbl_common_d[] = {
++static struct idxdata tbl_common4[] = {
+ {0x33, "\x8c\x22\x2e"}, {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\xa4\x08"},
+ {0x33, "\x90\x00\x1f"}, {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x21"},
+ {0x33, "\x8c\xa4\x0a"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\xa4\x0b"},
+@@ -118,7 +117,7 @@ static struct idxdata tbl_common_d[] = {
+ {0x33, "\x90\x00\xa0"}, {0x33, "\x8c\x24\x17"}, {0x33, "\x90\x00\xc0"},
+ };
+
+-static struct idxdata tbl_common_e[] = {
++static struct idxdata tbl_common5[] = {
+ {0x33, "\x8c\xa4\x04"}, {0x33, "\x90\x00\x80"}, {0x33, "\x8c\xa7\x9d"},
+ {0x33, "\x90\x00\x00"}, {0x33, "\x8c\xa7\x9e"}, {0x33, "\x90\x00\x00"},
+ {0x33, "\x8c\xa2\x0c"}, {0x33, "\x90\x00\x17"}, {0x33, "\x8c\xa2\x15"},
+@@ -180,7 +179,7 @@ static struct validx tbl_init_at_startup[] = {
+ {53, 0xffff},
+ };
+
+-static struct idxdata tbl_init_post_alt_low_a[] = {
++static struct idxdata tbl_init_post_alt_low1[] = {
+ {0x33, "\x8c\x27\x15"}, {0x33, "\x90\x00\x25"}, {0x33, "\x8c\x22\x2e"},
+ {0x33, "\x90\x00\x81"}, {0x33, "\x8c\xa4\x08"}, {0x33, "\x90\x00\x17"},
+ {0x33, "\x8c\xa4\x09"}, {0x33, "\x90\x00\x1a"}, {0x33, "\x8c\xa4\x0a"},
+@@ -189,7 +188,7 @@ static struct idxdata tbl_init_post_alt_low_a[] = {
+ {0x33, "\x90\x00\x9b"},
+ };
+
+-static struct idxdata tbl_init_post_alt_low_b[] = {
++static struct idxdata tbl_init_post_alt_low2[] = {
+ {0x33, "\x8c\x27\x03"}, {0x33, "\x90\x03\x24"}, {0x33, "\x8c\x27\x05"},
+ {0x33, "\x90\x02\x58"}, {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
+ {2, "\xff\xff\xff"},
+@@ -197,7 +196,7 @@ static struct idxdata tbl_init_post_alt_low_b[] = {
+ {2, "\xff\xff\xff"},
+ };
+
+-static struct idxdata tbl_init_post_alt_low_c[] = {
++static struct idxdata tbl_init_post_alt_low3[] = {
+ {0x34, "\x1e\x8f\x09"}, {0x34, "\x1c\x01\x28"}, {0x34, "\x1e\x8f\x09"},
+ {2, "\xff\xff\xff"},
+ {0x34, "\x1e\x8f\x09"}, {0x32, "\x14\x06\xe6"}, {0x33, "\x8c\xa1\x20"},
+@@ -221,7 +220,7 @@ static struct idxdata tbl_init_post_alt_low_c[] = {
+ {1, "\xff\xff\xff"},
+ };
+
+-static struct idxdata tbl_init_post_alt_low_d[] = {
++static struct idxdata tbl_init_post_alt_low4[] = {
+ {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"},
+ {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"},
+ {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"},
+@@ -267,7 +266,7 @@ static struct idxdata tbl_init_post_alt_low_d[] = {
+ {0x32, "\x6c\x14\x08"},
+ };
+
+-static struct idxdata tbl_init_post_alt_big_a[] = {
++static struct idxdata tbl_init_post_alt_big1[] = {
+ {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x05"},
+ {2, "\xff\xff\xff"},
+ {0x33, "\x8c\xa1\x03"}, {0x33, "\x90\x00\x06"},
+@@ -288,7 +287,7 @@ static struct idxdata tbl_init_post_alt_big_a[] = {
+ {0x34, "\x04\x00\x2a"}, {0x33, "\x8c\xa7\x02"}, {0x33, "\x90\x00\x01"},
+ };
+
+-static struct idxdata tbl_init_post_alt_big_b[] = {
++static struct idxdata tbl_init_post_alt_big2[] = {
+ {0x32, "\x10\x01\xf8"}, {0x34, "\xce\x01\xa8"}, {0x34, "\xd0\x66\x33"},
+ {0x34, "\xd2\x31\x9a"}, {0x34, "\xd4\x94\x63"}, {0x34, "\xd6\x4b\x25"},
+ {0x34, "\xd8\x26\x70"}, {0x34, "\xda\x72\x4c"}, {0x34, "\xdc\xff\x04"},
+@@ -317,7 +316,7 @@ static struct idxdata tbl_init_post_alt_big_b[] = {
+ {0x32, "\x10\x01\xfc"}, {0x33, "\x8c\xa1\x18"}, {0x33, "\x90\x00\x3c"},
+ };
+
+-static struct idxdata tbl_init_post_alt_big_c[] = {
++static struct idxdata tbl_init_post_alt_big3[] = {
+ {0x33, "\x8c\xa1\x02"},
+ {0x33, "\x90\x00\x1f"},
+ {0x33, "\x8c\xa1\x02"},
+@@ -388,14 +387,14 @@ static void common(struct gspca_dev *gspca_dev)
+ s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+
+ if (_MI2020b_) {
+- fetch_validx(gspca_dev, tbl_common_a, ARRAY_SIZE(tbl_common_a));
++ fetch_validx(gspca_dev, tbl_common1, ARRAY_SIZE(tbl_common1));
+ } else {
+ if (_MI2020_)
+ ctrl_out(gspca_dev, 0x40, 1, 0x0008, 0x0004, 0, NULL);
+ else
+ ctrl_out(gspca_dev, 0x40, 1, 0x0002, 0x0004, 0, NULL);
+ msleep(35);
+- fetch_validx(gspca_dev, tbl_common_b, ARRAY_SIZE(tbl_common_b));
++ fetch_validx(gspca_dev, tbl_common2, ARRAY_SIZE(tbl_common2));
+ }
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x01");
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x86\x25\x00");
+@@ -403,13 +402,13 @@ static void common(struct gspca_dev *gspca_dev)
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0030, 3, "\x1a\x0a\xcc");
+ if (reso == IMAGE_1600)
+ msleep(2); /* 1600 */
+- fetch_idxdata(gspca_dev, tbl_common_c, ARRAY_SIZE(tbl_common_c));
++ fetch_idxdata(gspca_dev, tbl_common3, ARRAY_SIZE(tbl_common3));
+
+ if (_MI2020b_ || _MI2020_)
+- fetch_idxdata(gspca_dev, tbl_common_d,
+- ARRAY_SIZE(tbl_common_d));
++ fetch_idxdata(gspca_dev, tbl_common4,
++ ARRAY_SIZE(tbl_common4));
+
+- fetch_idxdata(gspca_dev, tbl_common_e, ARRAY_SIZE(tbl_common_e));
++ fetch_idxdata(gspca_dev, tbl_common5, ARRAY_SIZE(tbl_common5));
+ if (_MI2020b_ || _MI2020_) {
+ /* Different from fret */
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, "\x90\x00\x78");
+@@ -525,15 +524,15 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev)
+ 12, dat_800);
+
+ if (_MI2020c_)
+- fetch_idxdata(gspca_dev, tbl_init_post_alt_low_a,
+- ARRAY_SIZE(tbl_init_post_alt_low_a));
++ fetch_idxdata(gspca_dev, tbl_init_post_alt_low1,
++ ARRAY_SIZE(tbl_init_post_alt_low1));
+
+ if (reso == IMAGE_800)
+- fetch_idxdata(gspca_dev, tbl_init_post_alt_low_b,
+- ARRAY_SIZE(tbl_init_post_alt_low_b));
++ fetch_idxdata(gspca_dev, tbl_init_post_alt_low2,
++ ARRAY_SIZE(tbl_init_post_alt_low2));
+
+- fetch_idxdata(gspca_dev, tbl_init_post_alt_low_c,
+- ARRAY_SIZE(tbl_init_post_alt_low_c));
++ fetch_idxdata(gspca_dev, tbl_init_post_alt_low3,
++ ARRAY_SIZE(tbl_init_post_alt_low3));
+
+ if (_MI2020b_) {
+ ctrl_out(gspca_dev, 0x40, 1, 0x0001, 0x0010, 0, NULL);
+@@ -574,8 +573,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev)
+ msleep(5);/* " */
+
+ if (_MI2020c_) {
+- fetch_idxdata(gspca_dev, tbl_init_post_alt_low_d,
+- ARRAY_SIZE(tbl_init_post_alt_low_d));
++ fetch_idxdata(gspca_dev, tbl_init_post_alt_low4,
++ ARRAY_SIZE(tbl_init_post_alt_low4));
+ } else {
+ ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, &c);
+ msleep(14); /* 0xd8 */
+@@ -644,8 +643,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev)
+ 3, "\x90\x04\xb0");
+ }
+
+- fetch_idxdata(gspca_dev, tbl_init_post_alt_big_a,
+- ARRAY_SIZE(tbl_init_post_alt_big_a));
++ fetch_idxdata(gspca_dev, tbl_init_post_alt_big1,
++ ARRAY_SIZE(tbl_init_post_alt_big1));
+
+ if (reso == IMAGE_1600)
+ msleep(13); /* 1600 */
+@@ -708,8 +707,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev)
+ msleep(14);
+
+ if (_MI2020c_)
+- fetch_idxdata(gspca_dev, tbl_init_post_alt_big_b,
+- ARRAY_SIZE(tbl_init_post_alt_big_b));
++ fetch_idxdata(gspca_dev, tbl_init_post_alt_big2,
++ ARRAY_SIZE(tbl_init_post_alt_big2));
+
+ /* flip/mirror */
+ ctrl_out(gspca_dev, 0x40, 3, 0x7a00, 0x0033, 3, dat_hvflip1);
+@@ -738,8 +737,8 @@ static int mi2020_init_post_alt(struct gspca_dev *gspca_dev)
+ sd->nbIm = 0;
+
+ if (_MI2020c_)
+- fetch_idxdata(gspca_dev, tbl_init_post_alt_big_c,
+- ARRAY_SIZE(tbl_init_post_alt_big_c));
++ fetch_idxdata(gspca_dev, tbl_init_post_alt_big3,
++ ARRAY_SIZE(tbl_init_post_alt_big3));
+ }
+
+ sd->vold.mirror = mirror;
+@@ -770,7 +769,7 @@ static int mi2020_configure_alt(struct gspca_dev *gspca_dev)
+ return 0;
+ }
+
+-int mi2020_camera_settings(struct gspca_dev *gspca_dev)
++static int mi2020_camera_settings(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+diff --git a/drivers/media/video/gspca/gl860/gl860-ov2640.c b/drivers/media/video/gspca/gl860/gl860-ov2640.c
+index 14b9c37..768cac5 100644
+--- a/drivers/media/video/gspca/gl860/gl860-ov2640.c
++++ b/drivers/media/video/gspca/gl860/gl860-ov2640.c
+@@ -1,6 +1,5 @@
+-/* @file gl860-ov2640.c
+- * @author Olivier LORIN, from Malmostoso's logs
+- * @date 2009-08-27
++/* Subdriver for the GL860 chip with the OV2640 sensor
++ * Author Olivier LORIN, from Malmostoso's logs
+ *
+ * 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
+@@ -21,8 +20,12 @@
+ #include "gl860.h"
+
+ static u8 dat_init1[] = "\x00\x41\x07\x6a\x06\x61\x0d\x6a" "\x10\x10\xc1\x01";
+-static u8 dat_init2[] = {0x61}; /* expected */
+-static u8 dat_init3[] = {0x51}; /* expected */
++
++static u8 c61[] = {0x61}; /* expected */
++static u8 c51[] = {0x51}; /* expected */
++static u8 c50[] = {0x50}; /* expected */
++static u8 c28[] = {0x28}; /* expected */
++static u8 ca8[] = {0xa8}; /* expected */
+
+ static u8 dat_post[] =
+ "\x00\x41\x07\x6a\x06\xef\x0d\x6a" "\x10\x10\xc1\x01";
+@@ -32,10 +35,6 @@ static u8 dat_800[] = "\xd0\x01\xd1\x10\xd2\x58\xd3\x02\xd4\x18\xd5\x21";
+ static u8 dat_1280[] = "\xd0\x01\xd1\x18\xd2\xc0\xd3\x02\xd4\x28\xd5\x01";
+ static u8 dat_1600[] = "\xd0\x01\xd1\x20\xd2\xb0\xd3\x02\xd4\x30\xd5\x41";
+
+-static u8 c50[] = {0x50}; /* expected */
+-static u8 c28[] = {0x28}; /* expected */
+-static u8 ca8[] = {0xa8}; /* expected */
+-
+ static struct validx tbl_init_at_startup[] = {
+ {0x0000, 0x0000}, {0x0010, 0x0010}, {0x0008, 0x00c0}, {0x0001, 0x00c1},
+ {0x0001, 0x00c2}, {0x0020, 0x0006}, {0x006a, 0x000d},
+@@ -92,7 +91,7 @@ static struct validx tbl_common[] = {
+ {0x6000, 0x0010},
+ };
+
+-static struct validx tbl_sensor_settings_common_a[] = {
++static struct validx tbl_sensor_settings_common1[] = {
+ {0x0041, 0x0000}, {0x006a, 0x0007}, {0x00ef, 0x0006}, {0x006a, 0x000d},
+ {0x0000, 0x00c0}, {0x0010, 0x0010}, {0x0001, 0x00c1}, {0x0041, 0x00c2},
+ {0x0004, 0x00d8}, {0x0012, 0x0004}, {0x0000, 0x0058}, {0x0041, 0x0000},
+@@ -104,40 +103,10 @@ static struct validx tbl_sensor_settings_common_a[] = {
+ {0x0040, 0x0000},
+ };
+
+-static struct validx tbl_sensor_settings_common_b[] = {
++static struct validx tbl_sensor_settings_common2[] = {
+ {0x6001, 0x00ff}, {0x6038, 0x000c},
+ {10, 0xffff},
+ {0x6000, 0x0011},
+- /* backlight=31/64 */
+- {0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025},
+- /* bright=0/256 */
+- {0x6000, 0x00ff}, {0x6009, 0x007c}, {0x6000, 0x007d},
+- /* wbal=64/128 */
+- {0x6000, 0x00ff}, {0x6003, 0x007c}, {0x6040, 0x007d},
+- /* cntr=0/256 */
+- {0x6000, 0x00ff}, {0x6007, 0x007c}, {0x6000, 0x007d},
+- /* sat=128/256 */
+- {0x6000, 0x00ff}, {0x6001, 0x007c}, {0x6080, 0x007d},
+- /* sharpness=0/32 */
+- {0x6000, 0x00ff}, {0x6001, 0x0092}, {0x60c0, 0x0093},
+- /* hue=0/256 */
+- {0x6000, 0x00ff}, {0x6002, 0x007c}, {0x6000, 0x007d},
+- /* gam=32/64 */
+- {0x6000, 0x00ff}, {0x6008, 0x007c}, {0x6020, 0x007d},
+- /* image right up */
+- {0xffff, 0xffff},
+- {15, 0xffff},
+- {0x6001, 0x00ff}, {0x6000, 0x8004},
+- {0xffff, 0xffff},
+- {0x60a8, 0x0004},
+- {15, 0xffff},
+- {0x6001, 0x00ff}, {0x6000, 0x8004},
+- {0xffff, 0xffff},
+- {0x60f8, 0x0004},
+- /* image right up */
+- {0xffff, 0xffff},
+- /* backlight=31/64 */
+- {0x6001, 0x00ff}, {0x603e, 0x0024}, {0x6034, 0x0025},
+ };
+
+ static struct validx tbl_640[] = {
+@@ -166,7 +135,7 @@ static struct validx tbl_800[] = {
+ {0x60ff, 0x00dd}, {0x6020, 0x008c}, {0x6001, 0x00ff}, {0x6044, 0x0018},
+ };
+
+-static struct validx tbl_big_a[] = {
++static struct validx tbl_big1[] = {
+ {0x0002, 0x00c1}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0},
+ {0x6001, 0x00ff}, {0x6000, 0x0012}, {0x6000, 0x0000}, {0x6000, 0x0045},
+ {0x6000, 0x0010}, {0x6000, 0x0011}, {0x6011, 0x0017}, {0x6075, 0x0018},
+@@ -176,14 +145,14 @@ static struct validx tbl_big_a[] = {
+ {0x60c8, 0x00c0}, {0x6096, 0x00c1}, {0x6000, 0x008c},
+ };
+
+-static struct validx tbl_big_b[] = {
++static struct validx tbl_big2[] = {
+ {0x603d, 0x0086}, {0x6000, 0x0050}, {0x6090, 0x0051}, {0x602c, 0x0052},
+ {0x6000, 0x0053}, {0x6000, 0x0054}, {0x6088, 0x0055}, {0x6000, 0x0057},
+ {0x6040, 0x005a}, {0x60f0, 0x005b}, {0x6001, 0x005c}, {0x6082, 0x00d3},
+ {0x6000, 0x008e},
+ };
+
+-static struct validx tbl_big_c[] = {
++static struct validx tbl_big3[] = {
+ {0x6004, 0x00da}, {0x6000, 0x00e0}, {0x6067, 0x00e1}, {0x60ff, 0x00dd},
+ {0x6001, 0x00ff}, {0x6000, 0x00ff}, {0x60f1, 0x00dd}, {0x6004, 0x00e0},
+ {0x6001, 0x00ff}, {0x6000, 0x0011}, {0x6000, 0x00ff}, {0x6010, 0x00c7},
+@@ -223,17 +192,19 @@ void ov2640_init_settings(struct gspca_dev *gspca_dev)
+ sd->vcur.hue = 0;
+ sd->vcur.saturation = 128;
+ sd->vcur.whitebal = 64;
++ sd->vcur.mirror = 0;
++ sd->vcur.flip = 0;
+
+ sd->vmax.backlight = 64;
+ sd->vmax.brightness = 255;
+ sd->vmax.sharpness = 31;
+ sd->vmax.contrast = 255;
+ sd->vmax.gamma = 64;
+- sd->vmax.hue = 255 + 1;
++ sd->vmax.hue = 254 + 2;
+ sd->vmax.saturation = 255;
+ sd->vmax.whitebal = 128;
+- sd->vmax.mirror = 0;
+- sd->vmax.flip = 0;
++ sd->vmax.mirror = 1;
++ sd->vmax.flip = 1;
+ sd->vmax.AC50Hz = 0;
+
+ sd->dev_camera_settings = ov2640_camera_settings;
+@@ -259,11 +230,11 @@ static int ov2640_init_at_startup(struct gspca_dev *gspca_dev)
+
+ common(gspca_dev);
+
+- ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, dat_init2);
++ ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0006, 1, c61);
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x00ef, 0x0006, 0, NULL);
+
+- ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, dat_init3);
++ ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c51);
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x0051, 0x0000, 0, NULL);
+ /* ctrl_out(gspca_dev, 0x40, 11, 0x0000, 0x0000, 0, NULL); */
+@@ -275,6 +246,8 @@ static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
++ sd->mirrorMask = 0;
++
+ sd->vold.backlight = -1;
+ sd->vold.brightness = -1;
+ sd->vold.sharpness = -1;
+@@ -283,6 +256,8 @@ static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev)
+ sd->vold.gamma = -1;
+ sd->vold.hue = -1;
+ sd->vold.whitebal = -1;
++ sd->vold.mirror = -1;
++ sd->vold.flip = -1;
+
+ ov2640_init_post_alt(gspca_dev);
+
+@@ -292,16 +267,16 @@ static int ov2640_init_pre_alt(struct gspca_dev *gspca_dev)
+ static int ov2640_init_post_alt(struct gspca_dev *gspca_dev)
+ {
+ s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+- s32 n; /* reserved for FETCH macros */
++ s32 n; /* reserved for FETCH functions */
+
+ ctrl_out(gspca_dev, 0x40, 5, 0x0001, 0x0000, 0, NULL);
+
+- n = fetch_validx(gspca_dev, tbl_sensor_settings_common_a,
+- ARRAY_SIZE(tbl_sensor_settings_common_a));
++ n = fetch_validx(gspca_dev, tbl_sensor_settings_common1,
++ ARRAY_SIZE(tbl_sensor_settings_common1));
+ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 12, dat_post);
+ common(gspca_dev);
+- keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_a,
+- ARRAY_SIZE(tbl_sensor_settings_common_a), n);
++ keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common1,
++ ARRAY_SIZE(tbl_sensor_settings_common1), n);
+
+ switch (reso) {
+ case IMAGE_640:
+@@ -316,18 +291,18 @@ static int ov2640_init_post_alt(struct gspca_dev *gspca_dev)
+
+ case IMAGE_1600:
+ case IMAGE_1280:
+- n = fetch_validx(gspca_dev, tbl_big_a, ARRAY_SIZE(tbl_big_a));
++ n = fetch_validx(gspca_dev, tbl_big1, ARRAY_SIZE(tbl_big1));
+
+ if (reso == IMAGE_1280) {
+- n = fetch_validx(gspca_dev, tbl_big_b,
+- ARRAY_SIZE(tbl_big_b));
++ n = fetch_validx(gspca_dev, tbl_big2,
++ ARRAY_SIZE(tbl_big2));
+ } else {
+ ctrl_out(gspca_dev, 0x40, 1, 0x601d, 0x0086, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00d7, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6082, 0x00d3, 0, NULL);
+ }
+
+- n = fetch_validx(gspca_dev, tbl_big_c, ARRAY_SIZE(tbl_big_c));
++ n = fetch_validx(gspca_dev, tbl_big3, ARRAY_SIZE(tbl_big3));
+
+ if (reso == IMAGE_1280) {
+ ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
+@@ -343,20 +318,8 @@ static int ov2640_init_post_alt(struct gspca_dev *gspca_dev)
+ break;
+ }
+
+- n = fetch_validx(gspca_dev, tbl_sensor_settings_common_b,
+- ARRAY_SIZE(tbl_sensor_settings_common_b));
+- ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50);
+- keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
+- ARRAY_SIZE(tbl_sensor_settings_common_b), n);
+- ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28);
+- keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
+- ARRAY_SIZE(tbl_sensor_settings_common_b), n);
+- ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8);
+- keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
+- ARRAY_SIZE(tbl_sensor_settings_common_b), n);
+- ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50);
+- keep_on_fetching_validx(gspca_dev, tbl_sensor_settings_common_b,
+- ARRAY_SIZE(tbl_sensor_settings_common_b), n);
++ n = fetch_validx(gspca_dev, tbl_sensor_settings_common2,
++ ARRAY_SIZE(tbl_sensor_settings_common2));
+
+ ov2640_camera_settings(gspca_dev);
+
+@@ -393,18 +356,20 @@ static int ov2640_camera_settings(struct gspca_dev *gspca_dev)
+ s32 sat = sd->vcur.saturation;
+ s32 hue = sd->vcur.hue;
+ s32 wbal = sd->vcur.whitebal;
++ s32 mirror = (((sd->vcur.mirror > 0) ^ sd->mirrorMask) == 0);
++ s32 flip = (((sd->vcur.flip > 0) ^ sd->mirrorMask) == 0);
+
+ if (backlight != sd->vold.backlight) {
++ /* No sd->vold.backlight=backlight; (to be done again later) */
+ if (backlight < 0 || backlight > sd->vmax.backlight)
+ backlight = 0;
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff,
+ 0, NULL);
+- ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight , 0x0024,
++ ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight , 0x0024,
+ 0, NULL);
+- ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025,
++ ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025,
+ 0, NULL);
+- /* No sd->vold.backlight=backlight; (to be done again later) */
+ }
+
+ if (bright != sd->vold.brightness) {
+@@ -466,7 +431,7 @@ static int ov2640_camera_settings(struct gspca_dev *gspca_dev)
+ ctrl_out(gspca_dev, 0x40, 1, 0x6002 , 0x007c, 0, NULL);
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000 + hue * (hue < 255), 0x007d,
+ 0, NULL);
+- if (hue >= sd->vmax.hue)
++ if (hue >= 255)
+ sd->swapRB = 1;
+ else
+ sd->swapRB = 0;
+@@ -482,14 +447,33 @@ static int ov2640_camera_settings(struct gspca_dev *gspca_dev)
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000 + gam, 0x007d, 0, NULL);
+ }
+
++ if (mirror != sd->vold.mirror || flip != sd->vold.flip) {
++ sd->vold.mirror = mirror;
++ sd->vold.flip = flip;
++
++ mirror = 0x80 * mirror;
++ ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
++ ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL);
++ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, c28);
++ ctrl_out(gspca_dev, 0x40, 1, 0x6028 + mirror, 0x0004, 0, NULL);
++
++ flip = 0x50 * flip + mirror;
++ ctrl_out(gspca_dev, 0x40, 1, 0x6001, 0x00ff, 0, NULL);
++ ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x8004, 0, NULL);
++ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x8004, 1, ca8);
++ ctrl_out(gspca_dev, 0x40, 1, 0x6028 + flip, 0x0004, 0, NULL);
++
++ ctrl_in(gspca_dev, 0xc0, 2, 0x0000, 0x0000, 1, c50);
++ }
++
+ if (backlight != sd->vold.backlight) {
+ sd->vold.backlight = backlight;
+
+ ctrl_out(gspca_dev, 0x40, 1, 0x6001 , 0x00ff,
+ 0, NULL);
+- ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight , 0x0024,
++ ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight , 0x0024,
+ 0, NULL);
+- ctrl_out(gspca_dev, 0x40, 1, 0x601f + backlight - 10, 0x0025,
++ ctrl_out(gspca_dev, 0x40, 1, 0x601e + backlight - 10, 0x0025,
+ 0, NULL);
+ }
+
+diff --git a/drivers/media/video/gspca/gl860/gl860-ov9655.c b/drivers/media/video/gspca/gl860/gl860-ov9655.c
+index eda3346..d412694 100644
+--- a/drivers/media/video/gspca/gl860/gl860-ov9655.c
++++ b/drivers/media/video/gspca/gl860/gl860-ov9655.c
+@@ -1,7 +1,6 @@
+-/* @file gl860-ov9655.c
+- * @author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt
++/* Subdriver for the GL860 chip with the OV9655 sensor
++ * Author Olivier LORIN, from logs done by Simon (Sur3) and Almighurt
+ * on dsd's weblog
+- * @date 2009-08-27
+ *
+ * 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
+@@ -104,14 +103,14 @@ static u8 *tbl_800[] = {
+ };
+
+ static u8 c04[] = {0x04};
+-static u8 dat_post_1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02";
+-static u8 dat_post_2[] = "\x10\x10\xc1\x02";
+-static u8 dat_post_3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04";
+-static u8 dat_post_4[] = "\x10\x02\xc1\x06";
+-static u8 dat_post_5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08";
+-static u8 dat_post_6[] = "\x10\x10\xc1\x05";
+-static u8 dat_post_7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08";
+-static u8 dat_post_8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09";
++static u8 dat_post1[] = "\x04\x00\x10\x20\xa1\x00\x00\x02";
++static u8 dat_post2[] = "\x10\x10\xc1\x02";
++static u8 dat_post3[] = "\x04\x00\x10\x7c\xa1\x00\x00\x04";
++static u8 dat_post4[] = "\x10\x02\xc1\x06";
++static u8 dat_post5[] = "\x04\x00\x10\x7b\xa1\x00\x00\x08";
++static u8 dat_post6[] = "\x10\x10\xc1\x05";
++static u8 dat_post7[] = "\x04\x00\x10\x7c\xa1\x00\x00\x08";
++static u8 dat_post8[] = "\x04\x00\x10\x7c\xa1\x00\x00\x09";
+
+ static struct validx tbl_init_post_alt[] = {
+ {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x6032, 0x00ff}, {0x603c, 0x00ff},
+@@ -212,7 +211,7 @@ static int ov9655_init_pre_alt(struct gspca_dev *gspca_dev)
+ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev)
+ {
+ s32 reso = gspca_dev->cam.cam_mode[(s32) gspca_dev->curr_mode].priv;
+- s32 n; /* reserved for FETCH macros */
++ s32 n; /* reserved for FETCH functions */
+ s32 i;
+ u8 **tbl;
+
+@@ -243,7 +242,7 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev)
+ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+ keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+ ARRAY_SIZE(tbl_init_post_alt), n);
+- ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1);
++ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1);
+ keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+ ARRAY_SIZE(tbl_init_post_alt), n);
+
+@@ -259,7 +258,7 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev)
+ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x801e, 1, c04);
+ keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+ ARRAY_SIZE(tbl_init_post_alt), n);
+- ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1);
++ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1);
+ keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+ ARRAY_SIZE(tbl_init_post_alt), n);
+
+@@ -270,18 +269,18 @@ static int ov9655_init_post_alt(struct gspca_dev *gspca_dev)
+ keep_on_fetching_validx(gspca_dev, tbl_init_post_alt,
+ ARRAY_SIZE(tbl_init_post_alt), n);
+
+- ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_1);
++ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post1);
+
+- ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_2);
+- ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_3);
++ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post2);
++ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post3);
+
+- ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_4);
+- ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_5);
++ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post4);
++ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post5);
+
+- ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post_6);
+- ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_7);
++ ctrl_out(gspca_dev, 0x40, 3, 0x0000, 0x0200, 4, dat_post6);
++ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post7);
+
+- ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post_8);
++ ctrl_out(gspca_dev, 0x40, 3, 0x6000, 0x0200, 8, dat_post8);
+
+ ov9655_camera_settings(gspca_dev);
+
+diff --git a/drivers/media/video/gspca/gl860/gl860.c b/drivers/media/video/gspca/gl860/gl860.c
+index 6ef59ac..38bf924 100644
+--- a/drivers/media/video/gspca/gl860/gl860.c
++++ b/drivers/media/video/gspca/gl860/gl860.c
+@@ -1,9 +1,7 @@
+-/* @file gl860.c
+- * @date 2009-08-27
++/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip
++ * Subdriver core
+ *
+- * Genesys Logic webcam with gl860 subdrivers
+- *
+- * Driver by Olivier Lorin <o.lorin@laposte.net>
++ * 2009/09/24 Olivier Lorin <o.lorin@laposte.net>
+ * GSPCA by Jean-Francois Moine <http://moinejf.free.fr>
+ * Thanks BUGabundo and Malmostoso for your amazing help!
+ *
+@@ -23,8 +21,8 @@
+ #include "gspca.h"
+ #include "gl860.h"
+
+-MODULE_AUTHOR("Olivier Lorin <lorin@laposte.net>");
+-MODULE_DESCRIPTION("GSPCA/Genesys Logic GL860 USB Camera Driver");
++MODULE_AUTHOR("Olivier Lorin <o.lorin@laposte.net>");
++MODULE_DESCRIPTION("Genesys Logic USB PC Camera Driver");
+ MODULE_LICENSE("GPL");
+
+ /*======================== static function declarations ====================*/
+@@ -38,11 +36,11 @@ static int sd_isoc_init(struct gspca_dev *gspca_dev);
+ static int sd_start(struct gspca_dev *gspca_dev);
+ static void sd_stop0(struct gspca_dev *gspca_dev);
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, u8 *data, s32 len);
++ u8 *data, int len);
+ static void sd_callback(struct gspca_dev *gspca_dev);
+
+ static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
+- s32 vendor_id, s32 product_id);
++ u16 vendor_id, u16 product_id);
+
+ /*============================ driver options ==============================*/
+
+@@ -53,7 +51,7 @@ MODULE_PARM_DESC(AC50Hz, " Does AC power frequency is 50Hz? (0/1)");
+ static char sensor[7];
+ module_param_string(sensor, sensor, sizeof(sensor), 0644);
+ MODULE_PARM_DESC(sensor,
+- " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640'/'')");
++ " Driver sensor ('MI1320'/'MI2020'/'OV9655'/'OV2640')");
+
+ /*============================ webcam controls =============================*/
+
+@@ -156,14 +154,14 @@ static int gl860_build_control_table(struct gspca_dev *gspca_dev)
+ SET_MY_CTRL(V4L2_CID_VFLIP,
+ V4L2_CTRL_TYPE_BOOLEAN, "Flip", flip)
+ SET_MY_CTRL(V4L2_CID_POWER_LINE_FREQUENCY,
+- V4L2_CTRL_TYPE_BOOLEAN, "50Hz", AC50Hz)
++ V4L2_CTRL_TYPE_BOOLEAN, "AC power 50Hz", AC50Hz)
+
+ return nCtrls;
+ }
+
+ /*==================== sud-driver structure initialisation =================*/
+
+-static struct sd_desc sd_desc_mi1320 = {
++static const struct sd_desc sd_desc_mi1320 = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls_mi1320,
+ .nctrls = GL860_NCTRLS,
+@@ -176,7 +174,7 @@ static struct sd_desc sd_desc_mi1320 = {
+ .dq_callback = sd_callback,
+ };
+
+-static struct sd_desc sd_desc_mi2020 = {
++static const struct sd_desc sd_desc_mi2020 = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls_mi2020,
+ .nctrls = GL860_NCTRLS,
+@@ -189,7 +187,7 @@ static struct sd_desc sd_desc_mi2020 = {
+ .dq_callback = sd_callback,
+ };
+
+-static struct sd_desc sd_desc_mi2020b = {
++static const struct sd_desc sd_desc_mi2020b = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls_mi2020b,
+ .nctrls = GL860_NCTRLS,
+@@ -202,7 +200,7 @@ static struct sd_desc sd_desc_mi2020b = {
+ .dq_callback = sd_callback,
+ };
+
+-static struct sd_desc sd_desc_ov2640 = {
++static const struct sd_desc sd_desc_ov2640 = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls_ov2640,
+ .nctrls = GL860_NCTRLS,
+@@ -215,7 +213,7 @@ static struct sd_desc sd_desc_ov2640 = {
+ .dq_callback = sd_callback,
+ };
+
+-static struct sd_desc sd_desc_ov9655 = {
++static const struct sd_desc sd_desc_ov9655 = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls_ov9655,
+ .nctrls = GL860_NCTRLS,
+@@ -328,11 +326,11 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+- s32 vendor_id, product_id;
++ u16 vendor_id, product_id;
+
+ /* Get USB VendorID and ProductID */
+- vendor_id = le16_to_cpu(id->idVendor);
+- product_id = le16_to_cpu(id->idProduct);
++ vendor_id = id->idVendor;
++ product_id = id->idProduct;
+
+ sd->nbRightUp = 1;
+ sd->nbIm = -1;
+@@ -435,7 +433,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
+
+ /* This function is called when an image is being received */
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, u8 *data, s32 len)
++ u8 *data, int len)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ static s32 nSkipped;
+@@ -447,11 +445,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ /* Test only against 0202h, so endianess does not matter */
+ switch (*(s16 *) data) {
+ case 0x0202: /* End of frame, start a new one */
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0);
++ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ nSkipped = 0;
+ if (sd->nbIm >= 0 && sd->nbIm < 10)
+ sd->nbIm++;
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ break;
+
+ default:
+@@ -466,7 +464,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ nSkipped = nToSkip + 1;
+ }
+ gspca_frame_add(gspca_dev,
+- INTER_PACKET, frame, data, len);
++ INTER_PACKET, data, len);
+ }
+ break;
+ }
+@@ -675,7 +673,7 @@ void fetch_idxdata(struct gspca_dev *gspca_dev, struct idxdata *tbl, int len)
+ }
+
+ static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
+- s32 vendor_id, s32 product_id)
++ u16 vendor_id, u16 product_id)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 probe, nb26, nb96, nOV, ntry;
+@@ -702,6 +700,7 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
+ ctrl_out(gspca_dev, 0x40, 1, 0x006a, 0x000d, 0, NULL);
+ msleep(56);
+
++ PDEBUG(D_PROBE, "probing for sensor MI2020 or OVXXXX");
+ nOV = 0;
+ for (ntry = 0; ntry < 4; ntry++) {
+ ctrl_out(gspca_dev, 0x40, 1, 0x0040, 0x0000, 0, NULL);
+@@ -711,14 +710,14 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
+ ctrl_out(gspca_dev, 0x40, 1, 0x7a00, 0x8030, 0, NULL);
+ msleep(10);
+ ctrl_in(gspca_dev, 0xc0, 2, 0x7a00, 0x8030, 1, &probe);
+- PDEBUG(D_PROBE, "1st probe=%02x", probe);
++ PDEBUG(D_PROBE, "probe=0x%02x", probe);
+ if (probe == 0xff)
+ nOV++;
+ }
+
+ if (nOV) {
+- PDEBUG(D_PROBE, "0xff -> sensor OVXXXX");
+- PDEBUG(D_PROBE, "Probing for sensor OV2640 or OV9655");
++ PDEBUG(D_PROBE, "0xff -> OVXXXX");
++ PDEBUG(D_PROBE, "probing for sensor OV2640 or OV9655");
+
+ nb26 = nb96 = 0;
+ for (ntry = 0; ntry < 4; ntry++) {
+@@ -728,40 +727,38 @@ static int gl860_guess_sensor(struct gspca_dev *gspca_dev,
+ ctrl_out(gspca_dev, 0x40, 1, 0x6000, 0x800a,
+ 0, NULL);
+ msleep(10);
++
+ /* Wait for 26(OV2640) or 96(OV9655) */
+ ctrl_in(gspca_dev, 0xc0, 2, 0x6000, 0x800a,
+ 1, &probe);
+
+- PDEBUG(D_PROBE, "2nd probe=%02x", probe);
+- if (probe == 0x00)
+- nb26++;
+ if (probe == 0x26 || probe == 0x40) {
++ PDEBUG(D_PROBE,
++ "probe=0x%02x -> OV2640",
++ probe);
+ sd->sensor = ID_OV2640;
+ nb26 += 4;
+ break;
+ }
+ if (probe == 0x96 || probe == 0x55) {
++ PDEBUG(D_PROBE,
++ "probe=0x%02x -> OV9655",
++ probe);
+ sd->sensor = ID_OV9655;
+ nb96 += 4;
+ break;
+ }
++ PDEBUG(D_PROBE, "probe=0x%02x", probe);
++ if (probe == 0x00)
++ nb26++;
+ if (probe == 0xff)
+ nb96++;
+ msleep(3);
+ }
+- if (nb26 < 4 && nb96 < 4) {
+- PDEBUG(D_PROBE, "No relevant answer ");
+- PDEBUG(D_PROBE, "* 1.3Mpixels -> use OV9655");
+- PDEBUG(D_PROBE, "* 2.0Mpixels -> use OV2640");
+- PDEBUG(D_PROBE,
+- "To force a sensor, add that line to "
+- "/etc/modprobe.d/options.conf:");
+- PDEBUG(D_PROBE, "options gspca_gl860 "
+- "sensor=\"OV2640\" or \"OV9655\"");
++ if (nb26 < 4 && nb96 < 4)
+ return -1;
+- }
+- } else { /* probe = 0 */
+- PDEBUG(D_PROBE, "No 0xff -> sensor MI2020");
++ } else {
++ PDEBUG(D_PROBE, "Not any 0xff -> MI2020");
+ sd->sensor = ID_MI2020;
+ }
+ }
+diff --git a/drivers/media/video/gspca/gl860/gl860.h b/drivers/media/video/gspca/gl860/gl860.h
+index cef4e24..305061f 100644
+--- a/drivers/media/video/gspca/gl860/gl860.h
++++ b/drivers/media/video/gspca/gl860/gl860.h
+@@ -1,6 +1,7 @@
+-/* @file gl860.h
+- * @author Olivier LORIN, tiré du pilote Syntek par Nicolas VIVIEN
+- * @date 2009-08-27
++/* GSPCA subdrivers for Genesys Logic webcams with the GL860 chip
++ * Subdriver declarations
++ *
++ * 2009/10/14 Olivier LORIN <o.lorin@laposte.net>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+diff --git a/drivers/media/video/gspca/gspca.c b/drivers/media/video/gspca/gspca.c
+index 23d3fb7..2ed4823 100644
+--- a/drivers/media/video/gspca/gspca.c
++++ b/drivers/media/video/gspca/gspca.c
+@@ -47,7 +47,7 @@ MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>");
+ MODULE_DESCRIPTION("GSPCA USB Camera Driver");
+ MODULE_LICENSE("GPL");
+
+-#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 7, 0)
++#define DRIVER_VERSION_NUMBER KERNEL_VERSION(2, 8, 0)
+
+ #ifdef GSPCA_DEBUG
+ int gspca_debug = D_ERR | D_PROBE;
+@@ -74,7 +74,7 @@ static void PDEBUG_MODE(char *txt, __u32 pixfmt, int w, int h)
+ #define PDEBUG_MODE(txt, pixfmt, w, h)
+ #endif
+
+-/* specific memory types - !! should different from V4L2_MEMORY_xxx */
++/* specific memory types - !! should be different from V4L2_MEMORY_xxx */
+ #define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */
+ #define GSPCA_MEMORY_READ 7
+
+@@ -108,11 +108,8 @@ static const struct vm_operations_struct gspca_vm_ops = {
+ struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev)
+ {
+ struct gspca_frame *frame;
+- int i;
+
+- i = gspca_dev->fr_i;
+- i = gspca_dev->fr_queue[i];
+- frame = &gspca_dev->frame[i];
++ frame = gspca_dev->cur_frame;
+ if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)
+ != V4L2_BUF_FLAG_QUEUED)
+ return NULL;
+@@ -126,7 +123,6 @@ EXPORT_SYMBOL(gspca_get_i_frame);
+ static void fill_frame(struct gspca_dev *gspca_dev,
+ struct urb *urb)
+ {
+- struct gspca_frame *frame;
+ u8 *data; /* address of data in the iso message */
+ int i, len, st;
+ cam_pkt_op pkt_scan;
+@@ -135,21 +131,16 @@ static void fill_frame(struct gspca_dev *gspca_dev,
+ if (urb->status == -ESHUTDOWN)
+ return; /* disconnection */
+ #ifdef CONFIG_PM
+- if (!gspca_dev->frozen)
++ if (gspca_dev->frozen)
++ return;
+ #endif
+- PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+- return;
++ PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
++ urb->status = 0;
++ goto resubmit;
+ }
+ pkt_scan = gspca_dev->sd_desc->pkt_scan;
+ for (i = 0; i < urb->number_of_packets; i++) {
+
+- /* check the availability of the frame buffer */
+- frame = gspca_get_i_frame(gspca_dev);
+- if (!frame) {
+- gspca_dev->last_packet_type = DISCARD_PACKET;
+- break;
+- }
+-
+ /* check the packet status and length */
+ len = urb->iso_frame_desc[i].actual_length;
+ if (len == 0) {
+@@ -171,9 +162,10 @@ static void fill_frame(struct gspca_dev *gspca_dev,
+ i, urb->iso_frame_desc[i].offset, len);
+ data = (u8 *) urb->transfer_buffer
+ + urb->iso_frame_desc[i].offset;
+- pkt_scan(gspca_dev, frame, data, len);
++ pkt_scan(gspca_dev, data, len);
+ }
+
++resubmit:
+ /* resubmit the URB */
+ st = usb_submit_urb(urb, GFP_ATOMIC);
+ if (st < 0)
+@@ -201,7 +193,6 @@ static void isoc_irq(struct urb *urb)
+ static void bulk_irq(struct urb *urb)
+ {
+ struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
+- struct gspca_frame *frame;
+ int st;
+
+ PDEBUG(D_PACK, "bulk irq");
+@@ -212,29 +203,22 @@ static void bulk_irq(struct urb *urb)
+ break;
+ case -ESHUTDOWN:
+ return; /* disconnection */
+- case -ECONNRESET:
+- urb->status = 0;
+- break;
+ default:
+ #ifdef CONFIG_PM
+- if (!gspca_dev->frozen)
++ if (gspca_dev->frozen)
++ return;
+ #endif
+- PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
+- return;
++ PDEBUG(D_ERR|D_PACK, "urb status: %d", urb->status);
++ urb->status = 0;
++ goto resubmit;
+ }
+
+- /* check the availability of the frame buffer */
+- frame = gspca_get_i_frame(gspca_dev);
+- if (!frame) {
+- gspca_dev->last_packet_type = DISCARD_PACKET;
+- } else {
+- PDEBUG(D_PACK, "packet l:%d", urb->actual_length);
+- gspca_dev->sd_desc->pkt_scan(gspca_dev,
+- frame,
+- urb->transfer_buffer,
+- urb->actual_length);
+- }
++ PDEBUG(D_PACK, "packet l:%d", urb->actual_length);
++ gspca_dev->sd_desc->pkt_scan(gspca_dev,
++ urb->transfer_buffer,
++ urb->actual_length);
+
++resubmit:
+ /* resubmit the URB */
+ if (gspca_dev->cam.bulk_nurbs != 0) {
+ st = usb_submit_urb(urb, GFP_ATOMIC);
+@@ -255,24 +239,27 @@ static void bulk_irq(struct urb *urb)
+ * DISCARD_PACKET invalidates the whole frame.
+ * On LAST_PACKET, a new frame is returned.
+ */
+-struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
+- enum gspca_packet_type packet_type,
+- struct gspca_frame *frame,
+- const __u8 *data,
+- int len)
++void gspca_frame_add(struct gspca_dev *gspca_dev,
++ enum gspca_packet_type packet_type,
++ const u8 *data,
++ int len)
+ {
++ struct gspca_frame *frame;
+ int i, j;
+
+ PDEBUG(D_PACK, "add t:%d l:%d", packet_type, len);
+
++ /* check the availability of the frame buffer */
++ frame = gspca_dev->cur_frame;
++ if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)
++ != V4L2_BUF_FLAG_QUEUED) {
++ gspca_dev->last_packet_type = DISCARD_PACKET;
++ return;
++ }
++
+ /* when start of a new frame, if the current frame buffer
+ * is not queued, discard the whole frame */
+ if (packet_type == FIRST_PACKET) {
+- if ((frame->v4l2_buf.flags & BUF_ALL_FLAGS)
+- != V4L2_BUF_FLAG_QUEUED) {
+- gspca_dev->last_packet_type = DISCARD_PACKET;
+- return frame;
+- }
+ frame->data_end = frame->data;
+ jiffies_to_timeval(get_jiffies_64(),
+ &frame->v4l2_buf.timestamp);
+@@ -280,7 +267,7 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
+ } else if (gspca_dev->last_packet_type == DISCARD_PACKET) {
+ if (packet_type == LAST_PACKET)
+ gspca_dev->last_packet_type = packet_type;
+- return frame;
++ return;
+ }
+
+ /* append the packet to the frame buffer */
+@@ -312,9 +299,8 @@ struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
+ i,
+ gspca_dev->fr_o);
+ j = gspca_dev->fr_queue[i];
+- frame = &gspca_dev->frame[j];
++ gspca_dev->cur_frame = &gspca_dev->frame[j];
+ }
+- return frame;
+ }
+ EXPORT_SYMBOL(gspca_frame_add);
+
+@@ -331,7 +317,7 @@ static int gspca_is_compressed(__u32 format)
+ return 0;
+ }
+
+-static void *rvmalloc(unsigned long size)
++static void *rvmalloc(long size)
+ {
+ void *mem;
+ unsigned long adr;
+@@ -339,7 +325,7 @@ static void *rvmalloc(unsigned long size)
+ mem = vmalloc_32(size);
+ if (mem != NULL) {
+ adr = (unsigned long) mem;
+- while ((long) size > 0) {
++ while (size > 0) {
+ SetPageReserved(vmalloc_to_page((void *) adr));
+ adr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+@@ -395,6 +381,7 @@ static int frame_alloc(struct gspca_dev *gspca_dev,
+ frame->v4l2_buf.m.offset = i * frsz;
+ }
+ gspca_dev->fr_i = gspca_dev->fr_o = gspca_dev->fr_q = 0;
++ gspca_dev->cur_frame = &gspca_dev->frame[0];
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+ gspca_dev->sequence = 0;
+ return 0;
+@@ -475,10 +462,18 @@ static struct usb_host_endpoint *get_ep(struct gspca_dev *gspca_dev)
+ xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK
+ : USB_ENDPOINT_XFER_ISOC;
+ i = gspca_dev->alt; /* previous alt setting */
+- while (--i >= 0) {
+- ep = alt_xfer(&intf->altsetting[i], xfer);
+- if (ep)
+- break;
++ if (gspca_dev->cam.reverse_alts) {
++ while (++i < gspca_dev->nbalt) {
++ ep = alt_xfer(&intf->altsetting[i], xfer);
++ if (ep)
++ break;
++ }
++ } else {
++ while (--i >= 0) {
++ ep = alt_xfer(&intf->altsetting[i], xfer);
++ if (ep)
++ break;
++ }
+ }
+ if (ep == NULL) {
+ err("no transfer endpoint found");
+@@ -536,26 +531,22 @@ static int create_urbs(struct gspca_dev *gspca_dev,
+ nurbs = 1;
+ }
+
+- gspca_dev->nurbs = nurbs;
+ for (n = 0; n < nurbs; n++) {
+ urb = usb_alloc_urb(npkt, GFP_KERNEL);
+ if (!urb) {
+ err("usb_alloc_urb failed");
+- destroy_urbs(gspca_dev);
+ return -ENOMEM;
+ }
++ gspca_dev->urb[n] = urb;
+ urb->transfer_buffer = usb_buffer_alloc(gspca_dev->dev,
+ bsize,
+ GFP_KERNEL,
+ &urb->transfer_dma);
+
+ if (urb->transfer_buffer == NULL) {
+- usb_free_urb(urb);
+- err("usb_buffer_urb failed");
+- destroy_urbs(gspca_dev);
++ err("usb_buffer_alloc failed");
+ return -ENOMEM;
+ }
+- gspca_dev->urb[n] = urb;
+ urb->dev = gspca_dev->dev;
+ urb->context = gspca_dev;
+ urb->transfer_buffer_length = bsize;
+@@ -587,6 +578,7 @@ static int create_urbs(struct gspca_dev *gspca_dev,
+ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
+ {
+ struct usb_host_endpoint *ep;
++ struct urb *urb;
+ int n, ret;
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+@@ -597,9 +589,15 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
+ goto out;
+ }
+
++ gspca_dev->usb_err = 0;
++
+ /* set the higher alternate setting and
+ * loop until urb submit succeeds */
+- gspca_dev->alt = gspca_dev->nbalt;
++ if (gspca_dev->cam.reverse_alts)
++ gspca_dev->alt = 0;
++ else
++ gspca_dev->alt = gspca_dev->nbalt;
++
+ if (gspca_dev->sd_desc->isoc_init) {
+ ret = gspca_dev->sd_desc->isoc_init(gspca_dev);
+ if (ret < 0)
+@@ -611,10 +609,15 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
+ goto out;
+ }
+ for (;;) {
+- PDEBUG(D_STREAM, "init transfer alt %d", gspca_dev->alt);
+- ret = create_urbs(gspca_dev, ep);
+- if (ret < 0)
+- goto out;
++ if (!gspca_dev->cam.no_urb_create) {
++ PDEBUG(D_STREAM, "init transfer alt %d",
++ gspca_dev->alt);
++ ret = create_urbs(gspca_dev, ep);
++ if (ret < 0) {
++ destroy_urbs(gspca_dev);
++ goto out;
++ }
++ }
+
+ /* clear the bulk endpoint */
+ if (gspca_dev->cam.bulk)
+@@ -634,22 +637,29 @@ static int gspca_init_transfer(struct gspca_dev *gspca_dev)
+ break;
+
+ /* submit the URBs */
+- for (n = 0; n < gspca_dev->nurbs; n++) {
+- ret = usb_submit_urb(gspca_dev->urb[n], GFP_KERNEL);
++ for (n = 0; n < MAX_NURBS; n++) {
++ urb = gspca_dev->urb[n];
++ if (urb == NULL)
++ break;
++ ret = usb_submit_urb(urb, GFP_KERNEL);
+ if (ret < 0)
+ break;
+ }
+ if (ret >= 0)
+ break;
+- PDEBUG(D_ERR|D_STREAM,
+- "usb_submit_urb alt %d err %d", gspca_dev->alt, ret);
+ gspca_dev->streaming = 0;
+ destroy_urbs(gspca_dev);
+- if (ret != -ENOSPC)
++ if (ret != -ENOSPC) {
++ PDEBUG(D_ERR|D_STREAM,
++ "usb_submit_urb alt %d err %d",
++ gspca_dev->alt, ret);
+ goto out;
++ }
+
+ /* the bandwidth is not wide enough
+ * negociate or try a lower alternate setting */
++ PDEBUG(D_ERR|D_STREAM,
++ "bandwidth not wide enough - trying again");
+ msleep(20); /* wait for kill complete */
+ if (gspca_dev->sd_desc->isoc_nego) {
+ ret = gspca_dev->sd_desc->isoc_nego(gspca_dev);
+@@ -761,6 +771,7 @@ static int vidioc_g_register(struct file *file, void *priv,
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
++ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ ret = gspca_dev->sd_desc->get_register(gspca_dev, reg);
+ else
+@@ -784,6 +795,7 @@ static int vidioc_s_register(struct file *file, void *priv,
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
++ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ ret = gspca_dev->sd_desc->set_register(gspca_dev, reg);
+ else
+@@ -805,6 +817,7 @@ static int vidioc_g_chip_ident(struct file *file, void *priv,
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
++ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ ret = gspca_dev->sd_desc->get_chip_ident(gspca_dev, chip);
+ else
+@@ -976,12 +989,41 @@ static int vidioc_enum_framesizes(struct file *file, void *priv,
+ return -EINVAL;
+ }
+
++static int vidioc_enum_frameintervals(struct file *filp, void *priv,
++ struct v4l2_frmivalenum *fival)
++{
++ struct gspca_dev *gspca_dev = priv;
++ int mode = wxh_to_mode(gspca_dev, fival->width, fival->height);
++ __u32 i;
++
++ if (gspca_dev->cam.mode_framerates == NULL ||
++ gspca_dev->cam.mode_framerates[mode].nrates == 0)
++ return -EINVAL;
++
++ if (fival->pixel_format !=
++ gspca_dev->cam.cam_mode[mode].pixelformat)
++ return -EINVAL;
++
++ for (i = 0; i < gspca_dev->cam.mode_framerates[mode].nrates; i++) {
++ if (fival->index == i) {
++ fival->type = V4L2_FRMSIZE_TYPE_DISCRETE;
++ fival->discrete.numerator = 1;
++ fival->discrete.denominator =
++ gspca_dev->cam.mode_framerates[mode].rates[i];
++ return 0;
++ }
++ }
++
++ return -EINVAL;
++}
++
+ static void gspca_release(struct video_device *vfd)
+ {
+ struct gspca_dev *gspca_dev = container_of(vfd, struct gspca_dev, vdev);
+
+ PDEBUG(D_STREAM, "device released");
+
++
+ kfree(gspca_dev->usb_buf);
+ kfree(gspca_dev);
+ }
+@@ -991,7 +1033,7 @@ static int dev_open(struct file *file)
+ struct gspca_dev *gspca_dev;
+ int ret;
+
+- PDEBUG(D_STREAM, "%s open", current->comm);
++ PDEBUG(D_STREAM, "[%s] open", current->comm);
+ gspca_dev = (struct gspca_dev *) video_devdata(file);
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+@@ -1037,7 +1079,7 @@ static int dev_close(struct file *file)
+ {
+ struct gspca_dev *gspca_dev = file->private_data;
+
+- PDEBUG(D_STREAM, "%s close", current->comm);
++ PDEBUG(D_STREAM, "[%s] close", current->comm);
+ if (mutex_lock_interruptible(&gspca_dev->queue_lock))
+ return -ERESTARTSYS;
+ gspca_dev->users--;
+@@ -1046,6 +1088,7 @@ static int dev_close(struct file *file)
+ if (gspca_dev->capt_file == file) {
+ if (gspca_dev->streaming) {
+ mutex_lock(&gspca_dev->usb_lock);
++ gspca_dev->usb_err = 0;
+ gspca_stream_off(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
+@@ -1136,12 +1179,17 @@ static int vidioc_queryctrl(struct file *file, void *priv,
+ continue;
+ ctrls = &gspca_dev->sd_desc->ctrls[i];
+ }
++ if (ctrls == NULL)
++ return -EINVAL;
+ } else {
+ ctrls = get_ctrl(gspca_dev, id);
++ if (ctrls == NULL)
++ return -EINVAL;
++ i = ctrls - gspca_dev->sd_desc->ctrls;
+ }
+- if (ctrls == NULL)
+- return -EINVAL;
+ memcpy(q_ctrl, ctrls, sizeof *q_ctrl);
++ if (gspca_dev->ctrl_inac & (1 << i))
++ q_ctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
+ return 0;
+ }
+
+@@ -1162,6 +1210,7 @@ static int vidioc_s_ctrl(struct file *file, void *priv,
+ PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value);
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
++ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ ret = ctrls->set(gspca_dev, ctrl->value);
+ else
+@@ -1183,6 +1232,7 @@ static int vidioc_g_ctrl(struct file *file, void *priv,
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
++ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ ret = ctrls->get(gspca_dev, &ctrl->value);
+ else
+@@ -1297,6 +1347,7 @@ static int vidioc_reqbufs(struct file *file, void *priv,
+ /* stop streaming */
+ if (gspca_dev->streaming) {
+ mutex_lock(&gspca_dev->usb_lock);
++ gspca_dev->usb_err = 0;
+ gspca_stream_off(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+ }
+@@ -1388,6 +1439,7 @@ static int vidioc_streamoff(struct file *file, void *priv,
+ ret = -ERESTARTSYS;
+ goto out;
+ }
++ gspca_dev->usb_err = 0;
+ gspca_stream_off(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+
+@@ -1413,6 +1465,7 @@ static int vidioc_g_jpegcomp(struct file *file, void *priv,
+ return -EINVAL;
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
++ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ ret = gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp);
+ else
+@@ -1431,6 +1484,7 @@ static int vidioc_s_jpegcomp(struct file *file, void *priv,
+ return -EINVAL;
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
++ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ ret = gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp);
+ else
+@@ -1451,6 +1505,7 @@ static int vidioc_g_parm(struct file *filp, void *priv,
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
++ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ ret = gspca_dev->sd_desc->get_streamparm(gspca_dev,
+ parm);
+@@ -1480,6 +1535,7 @@ static int vidioc_s_parm(struct file *filp, void *priv,
+
+ if (mutex_lock_interruptible(&gspca_dev->usb_lock))
+ return -ERESTARTSYS;
++ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ ret = gspca_dev->sd_desc->set_streamparm(gspca_dev,
+ parm);
+@@ -1651,6 +1707,7 @@ static int frame_wait(struct gspca_dev *gspca_dev,
+
+ if (gspca_dev->sd_desc->dq_callback) {
+ mutex_lock(&gspca_dev->usb_lock);
++ gspca_dev->usb_err = 0;
+ if (gspca_dev->present)
+ gspca_dev->sd_desc->dq_callback(gspca_dev);
+ mutex_unlock(&gspca_dev->usb_lock);
+@@ -1762,6 +1819,8 @@ static int vidioc_qbuf(struct file *file, void *priv,
+ /* put the buffer in the 'queued' queue */
+ i = gspca_dev->fr_q;
+ gspca_dev->fr_queue[i] = index;
++ if (gspca_dev->fr_i == i)
++ gspca_dev->cur_frame = frame;
+ gspca_dev->fr_q = (i + 1) % gspca_dev->nframes;
+ PDEBUG(D_FRAM, "qbuf q:%d i:%d o:%d",
+ gspca_dev->fr_q,
+@@ -1963,6 +2022,7 @@ static const struct v4l2_ioctl_ops dev_ioctl_ops = {
+ .vidioc_g_parm = vidioc_g_parm,
+ .vidioc_s_parm = vidioc_s_parm,
+ .vidioc_enum_framesizes = vidioc_enum_framesizes,
++ .vidioc_enum_frameintervals = vidioc_enum_frameintervals,
+ #ifdef CONFIG_VIDEO_ADV_DEBUG
+ .vidioc_g_register = vidioc_g_register,
+ .vidioc_s_register = vidioc_s_register,
+@@ -1978,7 +2038,6 @@ static struct video_device gspca_template = {
+ .fops = &dev_fops,
+ .ioctl_ops = &dev_ioctl_ops,
+ .release = gspca_release,
+- .minor = -1,
+ };
+
+ /*
+@@ -2001,10 +2060,15 @@ int gspca_dev_probe(struct usb_interface *intf,
+ PDEBUG(D_PROBE, "probing %04x:%04x", id->idVendor, id->idProduct);
+
+ /* we don't handle multi-config cameras */
+- if (dev->descriptor.bNumConfigurations != 1)
++ if (dev->descriptor.bNumConfigurations != 1) {
++ PDEBUG(D_ERR, "Too many config");
+ return -ENODEV;
++ }
++
++ /* the USB video interface must be the first one */
+ interface = &intf->cur_altsetting->desc;
+- if (interface->bInterfaceNumber > 0)
++ if (dev->config->desc.bNumInterfaces != 1 &&
++ interface->bInterfaceNumber != 0)
+ return -ENODEV;
+
+ /* create the device */
+@@ -2035,9 +2099,6 @@ int gspca_dev_probe(struct usb_interface *intf,
+ ret = sd_desc->init(gspca_dev);
+ if (ret < 0)
+ goto out;
+- ret = gspca_set_alt0(gspca_dev);
+- if (ret < 0)
+- goto out;
+ gspca_set_default_mode(gspca_dev);
+
+ mutex_init(&gspca_dev->usb_lock);
+diff --git a/drivers/media/video/gspca/gspca.h b/drivers/media/video/gspca/gspca.h
+index 70b1fd8..53c034e 100644
+--- a/drivers/media/video/gspca/gspca.h
++++ b/drivers/media/video/gspca/gspca.h
+@@ -45,19 +45,30 @@ extern int gspca_debug;
+ /* image transfers */
+ #define MAX_NURBS 4 /* max number of URBs */
+
++
++/* used to list framerates supported by a camera mode (resolution) */
++struct framerates {
++ const u8 *rates;
++ int nrates;
++};
++
+ /* device information - set at probe time */
+ struct cam {
+- int bulk_size; /* buffer size when image transfer by bulk */
+ const struct v4l2_pix_format *cam_mode; /* size nmodes */
+- char nmodes;
+- __u8 bulk_nurbs; /* number of URBs in bulk mode
++ const struct framerates *mode_framerates; /* must have size nmode,
++ * just like cam_mode */
++ u32 bulk_size; /* buffer size when image transfer by bulk */
++ u32 input_flags; /* value for ENUM_INPUT status flags */
++ u8 nmodes; /* size of cam_mode */
++ u8 no_urb_create; /* don't create transfer URBs */
++ u8 bulk_nurbs; /* number of URBs in bulk mode
+ * - cannot be > MAX_NURBS
+ * - when 0 and bulk_size != 0 means
+ * 1 URB and submit done by subdriver */
+ u8 bulk; /* image transfer by 0:isoc / 1:bulk */
+ u8 npkt; /* number of packets in an ISOC message
+ * 0 is the default value: 32 packets */
+- u32 input_flags; /* value for ENUM_INPUT status flags */
++ u8 reverse_alts; /* Alt settings are in high to low order */
+ };
+
+ struct gspca_dev;
+@@ -78,8 +89,7 @@ typedef int (*cam_streamparm_op) (struct gspca_dev *,
+ typedef int (*cam_qmnu_op) (struct gspca_dev *,
+ struct v4l2_querymenu *);
+ typedef void (*cam_pkt_op) (struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame,
+- __u8 *data,
++ u8 *data,
+ int len);
+
+ struct ctrl {
+@@ -142,6 +152,7 @@ struct gspca_dev {
+ struct cam cam; /* device information */
+ const struct sd_desc *sd_desc; /* subdriver description */
+ unsigned ctrl_dis; /* disabled controls (bit map) */
++ unsigned ctrl_inac; /* inactive controls (bit map) */
+
+ #define USB_BUF_SZ 64
+ __u8 *usb_buf; /* buffer for USB exchanges */
+@@ -149,6 +160,7 @@ struct gspca_dev {
+
+ __u8 *frbuf; /* buffer for nframes */
+ struct gspca_frame frame[GSPCA_MAX_FRAMES];
++ struct gspca_frame *cur_frame; /* frame beeing filled */
+ __u32 frsz; /* frame size */
+ char nframes; /* number of frames */
+ char fr_i; /* frame being filled */
+@@ -169,13 +181,13 @@ struct gspca_dev {
+ struct mutex usb_lock; /* usb exchange protection */
+ struct mutex read_lock; /* read protection */
+ struct mutex queue_lock; /* ISOC queue protection */
++ int usb_err; /* USB error - protected by usb_lock */
+ #ifdef CONFIG_PM
+ char frozen; /* suspend - resume */
+ #endif
+ char users; /* number of opens */
+ char present; /* device connected */
+ char nbufread; /* number of buffers for read() */
+- char nurbs; /* number of allocated URBs */
+ char memory; /* memory type (V4L2_MEMORY_xxx) */
+ __u8 iface; /* USB interface number */
+ __u8 alt; /* USB alternate setting */
+@@ -189,11 +201,10 @@ int gspca_dev_probe(struct usb_interface *intf,
+ int dev_size,
+ struct module *module);
+ void gspca_disconnect(struct usb_interface *intf);
+-struct gspca_frame *gspca_frame_add(struct gspca_dev *gspca_dev,
+- enum gspca_packet_type packet_type,
+- struct gspca_frame *frame,
+- const __u8 *data,
+- int len);
++void gspca_frame_add(struct gspca_dev *gspca_dev,
++ enum gspca_packet_type packet_type,
++ const u8 *data,
++ int len);
+ struct gspca_frame *gspca_get_i_frame(struct gspca_dev *gspca_dev);
+ #ifdef CONFIG_PM
+ int gspca_suspend(struct usb_interface *intf, pm_message_t message);
+diff --git a/drivers/media/video/gspca/jeilinj.c b/drivers/media/video/gspca/jeilinj.c
+index a11c97e..2019b04 100644
+--- a/drivers/media/video/gspca/jeilinj.c
++++ b/drivers/media/video/gspca/jeilinj.c
+@@ -181,11 +181,9 @@ static void jlj_dostream(struct work_struct *work)
+ {
+ struct sd *dev = container_of(work, struct sd, work_struct);
+ struct gspca_dev *gspca_dev = &dev->gspca_dev;
+- struct gspca_frame *frame;
+ int blocks_left; /* 0x200-sized blocks remaining in current frame. */
+ int size_in_blocks;
+ int act_len;
+- int discarding = 0; /* true if we failed to get space for frame. */
+ int packet_type;
+ int ret;
+ u8 *buffer;
+@@ -196,15 +194,6 @@ static void jlj_dostream(struct work_struct *work)
+ goto quit_stream;
+ }
+ while (gspca_dev->present && gspca_dev->streaming) {
+- if (!gspca_dev->present)
+- goto quit_stream;
+- /* Start a new frame, and add the JPEG header, first thing */
+- frame = gspca_get_i_frame(gspca_dev);
+- if (frame && !discarding)
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+- dev->jpeg_hdr, JPEG_HDR_SZ);
+- else
+- discarding = 1;
+ /*
+ * Now request data block 0. Line 0 reports the size
+ * to download, in blocks of size 0x200, and also tells the
+@@ -222,14 +211,15 @@ static void jlj_dostream(struct work_struct *work)
+ size_in_blocks = buffer[0x0a];
+ blocks_left = buffer[0x0a] - 1;
+ PDEBUG(D_STREAM, "blocks_left = 0x%x", blocks_left);
+- packet_type = INTER_PACKET;
+- if (frame && !discarding)
+- /* Toss line 0 of data block 0, keep the rest. */
+- gspca_frame_add(gspca_dev, packet_type,
+- frame, buffer + FRAME_HEADER_LEN,
++
++ /* Start a new frame, and add the JPEG header, first thing */
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
++ dev->jpeg_hdr, JPEG_HDR_SZ);
++ /* Toss line 0 of data block 0, keep the rest. */
++ gspca_frame_add(gspca_dev, INTER_PACKET,
++ buffer + FRAME_HEADER_LEN,
+ JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN);
+- else
+- discarding = 1;
++
+ while (blocks_left > 0) {
+ if (!gspca_dev->present)
+ goto quit_stream;
+@@ -246,12 +236,8 @@ static void jlj_dostream(struct work_struct *work)
+ packet_type = LAST_PACKET;
+ else
+ packet_type = INTER_PACKET;
+- if (frame && !discarding)
+- gspca_frame_add(gspca_dev, packet_type,
+- frame, buffer,
+- JEILINJ_MAX_TRANSFER);
+- else
+- discarding = 1;
++ gspca_frame_add(gspca_dev, packet_type,
++ buffer, JEILINJ_MAX_TRANSFER);
+ }
+ }
+ quit_stream:
+diff --git a/drivers/media/video/gspca/m5602/m5602_core.c b/drivers/media/video/gspca/m5602/m5602_core.c
+index 7f1e541..4294c75 100644
+--- a/drivers/media/video/gspca/m5602/m5602_core.c
++++ b/drivers/media/video/gspca/m5602/m5602_core.c
+@@ -81,7 +81,7 @@ int m5602_write_bridge(struct sd *sd, const u8 address, const u8 i2c_data)
+ return (err < 0) ? err : 0;
+ }
+
+-int m5602_wait_for_i2c(struct sd *sd)
++static int m5602_wait_for_i2c(struct sd *sd)
+ {
+ int err;
+ u8 data;
+@@ -274,8 +274,7 @@ static int m5602_start_transfer(struct gspca_dev *gspca_dev)
+ }
+
+ static void m5602_urb_complete(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame,
+- __u8 *data, int len)
++ u8 *data, int len)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+@@ -295,19 +294,27 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev,
+ len -= 6;
+
+ /* Complete the last frame (if any) */
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET,
+- frame, data, 0);
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ NULL, 0);
+ sd->frame_count++;
+
+ /* Create a new frame */
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+
+ PDEBUG(D_FRAM, "Starting new frame %d",
+ sd->frame_count);
+
+ } else {
+- int cur_frame_len = frame->data_end - frame->data;
++ struct gspca_frame *frame;
++ int cur_frame_len;
+
++ frame = gspca_get_i_frame(gspca_dev);
++ if (frame == NULL) {
++ gspca_dev->last_packet_type = DISCARD_PACKET;
++ return;
++ }
++
++ cur_frame_len = frame->data_end - frame->data;
+ /* Remove urb header */
+ data += 4;
+ len -= 4;
+@@ -316,12 +323,12 @@ static void m5602_urb_complete(struct gspca_dev *gspca_dev,
+ PDEBUG(D_FRAM, "Continuing frame %d copying %d bytes",
+ sd->frame_count, len);
+
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
++ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, len);
+ } else if (frame->v4l2_buf.length - cur_frame_len > 0) {
+ /* Add the remaining data up to frame size */
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data,
+- frame->v4l2_buf.length - cur_frame_len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data,
++ frame->v4l2_buf.length - cur_frame_len);
+ }
+ }
+ }
+@@ -381,7 +388,7 @@ static int m5602_probe(struct usb_interface *intf,
+ THIS_MODULE);
+ }
+
+-void m5602_disconnect(struct usb_interface *intf)
++static void m5602_disconnect(struct usb_interface *intf)
+ {
+ struct gspca_dev *gspca_dev = usb_get_intfdata(intf);
+ struct sd *sd = (struct sd *) gspca_dev;
+diff --git a/drivers/media/video/gspca/m5602/m5602_mt9m111.c b/drivers/media/video/gspca/m5602/m5602_mt9m111.c
+index 8d071df..c0722fa 100644
+--- a/drivers/media/video/gspca/m5602/m5602_mt9m111.c
++++ b/drivers/media/video/gspca/m5602/m5602_mt9m111.c
+@@ -48,7 +48,7 @@ static struct v4l2_pix_format mt9m111_modes[] = {
+ }
+ };
+
+-const static struct ctrl mt9m111_ctrls[] = {
++static const struct ctrl mt9m111_ctrls[] = {
+ #define VFLIP_IDX 0
+ {
+ {
+@@ -171,7 +171,7 @@ int mt9m111_probe(struct sd *sd)
+ return -ENODEV;
+ }
+
+- info("Probing for a mt9m111 sensor");
++ PDEBUG(D_PROBE, "Probing for a mt9m111 sensor");
+
+ /* Do the preinit */
+ for (i = 0; i < ARRAY_SIZE(preinit_mt9m111); i++) {
+diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.c b/drivers/media/video/gspca/m5602/m5602_ov7660.c
+index 2a28b74..62c1cbf 100644
+--- a/drivers/media/video/gspca/m5602/m5602_ov7660.c
++++ b/drivers/media/video/gspca/m5602/m5602_ov7660.c
+@@ -33,7 +33,7 @@ static int ov7660_set_hflip(struct gspca_dev *gspca_dev, __s32 val);
+ static int ov7660_get_vflip(struct gspca_dev *gspca_dev, __s32 *val);
+ static int ov7660_set_vflip(struct gspca_dev *gspca_dev, __s32 val);
+
+-const static struct ctrl ov7660_ctrls[] = {
++static const struct ctrl ov7660_ctrls[] = {
+ #define GAIN_IDX 1
+ {
+ {
+diff --git a/drivers/media/video/gspca/m5602/m5602_ov7660.h b/drivers/media/video/gspca/m5602/m5602_ov7660.h
+index f5588eb..4d9dcf2 100644
+--- a/drivers/media/video/gspca/m5602/m5602_ov7660.h
++++ b/drivers/media/video/gspca/m5602/m5602_ov7660.h
+@@ -94,7 +94,7 @@ int ov7660_start(struct sd *sd);
+ int ov7660_stop(struct sd *sd);
+ void ov7660_disconnect(struct sd *sd);
+
+-const static struct m5602_sensor ov7660 = {
++static const struct m5602_sensor ov7660 = {
+ .name = "ov7660",
+ .i2c_slave_id = 0x42,
+ .i2c_regW = 1,
+diff --git a/drivers/media/video/gspca/m5602/m5602_ov9650.c b/drivers/media/video/gspca/m5602/m5602_ov9650.c
+index c2739d6..069ba00 100644
+--- a/drivers/media/video/gspca/m5602/m5602_ov9650.c
++++ b/drivers/media/video/gspca/m5602/m5602_ov9650.c
+@@ -307,7 +307,7 @@ int ov9650_probe(struct sd *sd)
+ return -ENODEV;
+ }
+
+- info("Probing for an ov9650 sensor");
++ PDEBUG(D_PROBE, "Probing for an ov9650 sensor");
+
+ /* Run the pre-init before probing the sensor */
+ for (i = 0; i < ARRAY_SIZE(preinit_ov9650) && !err; i++) {
+@@ -439,7 +439,7 @@ int ov9650_start(struct sd *sd)
+ err = m5602_write_bridge(sd, res_init_ov9650[i][1],
+ res_init_ov9650[i][2]);
+ else if (res_init_ov9650[i][0] == SENSOR) {
+- u8 data = res_init_ov9650[i][2];
++ data = res_init_ov9650[i][2];
+ err = m5602_write_sensor(sd,
+ res_init_ov9650[i][1], &data, 1);
+ }
+diff --git a/drivers/media/video/gspca/m5602/m5602_po1030.c b/drivers/media/video/gspca/m5602/m5602_po1030.c
+index 8d74d80..925b87d 100644
+--- a/drivers/media/video/gspca/m5602/m5602_po1030.c
++++ b/drivers/media/video/gspca/m5602/m5602_po1030.c
+@@ -205,7 +205,7 @@ int po1030_probe(struct sd *sd)
+ return -ENODEV;
+ }
+
+- info("Probing for a po1030 sensor");
++ PDEBUG(D_PROBE, "Probing for a po1030 sensor");
+
+ /* Run the pre-init to actually probe the unit */
+ for (i = 0; i < ARRAY_SIZE(preinit_po1030); i++) {
+diff --git a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
+index a27afeb..da0a38c 100644
+--- a/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
++++ b/drivers/media/video/gspca/m5602/m5602_s5k4aa.c
+@@ -48,6 +48,12 @@ static
+ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xa 2528")
+ }
+ }, {
++ .ident = "Fujitsu-Siemens Amilo Xi 2428",
++ .matches = {
++ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
++ DMI_MATCH(DMI_PRODUCT_NAME, "AMILO Xi 2428")
++ }
++ }, {
+ .ident = "Fujitsu-Siemens Amilo Xi 2528",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"),
+@@ -242,7 +248,7 @@ int s5k4aa_probe(struct sd *sd)
+ return -ENODEV;
+ }
+
+- info("Probing for a s5k4aa sensor");
++ PDEBUG(D_PROBE, "Probing for a s5k4aa sensor");
+
+ /* Preinit the sensor */
+ for (i = 0; i < ARRAY_SIZE(preinit_s5k4aa) && !err; i++) {
+@@ -525,7 +531,10 @@ static int s5k4aa_set_vflip(struct gspca_dev *gspca_dev, __s32 val)
+ err = m5602_read_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ if (err < 0)
+ return err;
+- data = (data & 0xfe) | !val;
++ if (val)
++ data &= 0xfe;
++ else
++ data |= 0x01;
+ err = m5602_write_sensor(sd, S5K4AA_ROWSTART_LO, &data, 1);
+ return err;
+ }
+@@ -570,7 +579,10 @@ static int s5k4aa_set_hflip(struct gspca_dev *gspca_dev, __s32 val)
+ err = m5602_read_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ if (err < 0)
+ return err;
+- data = (data & 0xfe) | !val;
++ if (val)
++ data &= 0xfe;
++ else
++ data |= 0x01;
+ err = m5602_write_sensor(sd, S5K4AA_COLSTART_LO, &data, 1);
+ return err;
+ }
+diff --git a/drivers/media/video/gspca/m5602/m5602_s5k83a.c b/drivers/media/video/gspca/m5602/m5602_s5k83a.c
+index 6b89f33..fbd9154 100644
+--- a/drivers/media/video/gspca/m5602/m5602_s5k83a.c
++++ b/drivers/media/video/gspca/m5602/m5602_s5k83a.c
+@@ -143,7 +143,7 @@ int s5k83a_probe(struct sd *sd)
+ return -ENODEV;
+ }
+
+- info("Probing for a s5k83a sensor");
++ PDEBUG(D_PROBE, "Probing for a s5k83a sensor");
+
+ /* Preinit the sensor */
+ for (i = 0; i < ARRAY_SIZE(preinit_s5k83a) && !err; i++) {
+diff --git a/drivers/media/video/gspca/mars.c b/drivers/media/video/gspca/mars.c
+index de769ca..3d9229e 100644
+--- a/drivers/media/video/gspca/mars.c
++++ b/drivers/media/video/gspca/mars.c
+@@ -54,7 +54,7 @@ static int sd_getgamma(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+@@ -325,8 +325,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+- __u8 *data, /* isoc packet */
++ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -348,11 +347,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ || data[5 + p] == 0x67) {
+ PDEBUG(D_PACK, "sof offset: %d len: %d",
+ p, len);
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET,
+- frame, data, p);
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ data, p);
+
+ /* put the JPEG header */
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ data += p + 16;
+ len -= p + 16;
+@@ -360,7 +359,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ }
+ }
+ }
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+diff --git a/drivers/media/video/gspca/mr97310a.c b/drivers/media/video/gspca/mr97310a.c
+index f8328b9..959ea2a 100644
+--- a/drivers/media/video/gspca/mr97310a.c
++++ b/drivers/media/video/gspca/mr97310a.c
+@@ -1,23 +1,30 @@
+ /*
+ * Mars MR97310A library
+ *
++ * The original mr97310a driver, which supported the Aiptek Pencam VGA+, is
+ * Copyright (C) 2009 Kyle Guinn <elyk03@gmail.com>
+ *
+ * Support for the MR97310A cameras in addition to the Aiptek Pencam VGA+
+ * and for the routines for detecting and classifying these various cameras,
++ * is Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
+ *
++ * Support for the control settings for the CIF cameras is
++ * Copyright (C) 2009 Hans de Goede <hdgoede@redhat.com> and
++ * Thomas Kaiser <thomas@kaiser-linux.li>
++ *
++ * Support for the control settings for the VGA cameras is
+ * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
+ *
+- * Acknowledgements:
++ * Several previously unsupported cameras are owned and have been tested by
++ * Hans de Goede <hdgoede@redhat.com> and
++ * Thomas Kaiser <thomas@kaiser-linux.li> and
++ * Theodore Kilgore <kilgota@auburn.edu> and
++ * Edmond Rodriguez <erodrig_97@yahoo.com> and
++ * Aurelien Jacobs <aurel@gnuage.org>
+ *
+ * The MR97311A support in gspca/mars.c has been helpful in understanding some
+ * of the registers in these cameras.
+ *
+- * Hans de Goede <hdgoede@redhat.com> and
+- * Thomas Kaiser <thomas@kaiser-linux.li>
+- * have assisted with their experience. Each of them has also helped by
+- * testing a previously unsupported camera.
+- *
+ * 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
+@@ -40,11 +47,9 @@
+ #define CAM_TYPE_CIF 0
+ #define CAM_TYPE_VGA 1
+
+-#define MR97310A_BRIGHTNESS_MIN -254
+-#define MR97310A_BRIGHTNESS_MAX 255
+ #define MR97310A_BRIGHTNESS_DEFAULT 0
+
+-#define MR97310A_EXPOSURE_MIN 300
++#define MR97310A_EXPOSURE_MIN 0
+ #define MR97310A_EXPOSURE_MAX 4095
+ #define MR97310A_EXPOSURE_DEFAULT 1000
+
+@@ -52,13 +57,17 @@
+ #define MR97310A_GAIN_MAX 31
+ #define MR97310A_GAIN_DEFAULT 25
+
++#define MR97310A_MIN_CLOCKDIV_MIN 3
++#define MR97310A_MIN_CLOCKDIV_MAX 8
++#define MR97310A_MIN_CLOCKDIV_DEFAULT 3
++
+ MODULE_AUTHOR("Kyle Guinn <elyk03@gmail.com>,"
+ "Theodore Kilgore <kilgota@auburn.edu>");
+ MODULE_DESCRIPTION("GSPCA/Mars-Semi MR97310A USB Camera Driver");
+ MODULE_LICENSE("GPL");
+
+ /* global parameters */
+-int force_sensor_type = -1;
++static int force_sensor_type = -1;
+ module_param(force_sensor_type, int, 0644);
+ MODULE_PARM_DESC(force_sensor_type, "Force sensor type (-1 (auto), 0 or 1)");
+
+@@ -69,10 +78,12 @@ struct sd {
+ u8 cam_type; /* 0 is CIF and 1 is VGA */
+ u8 sensor_type; /* We use 0 and 1 here, too. */
+ u8 do_lcd_stop;
++ u8 adj_colors;
+
+ int brightness;
+ u16 exposure;
+ u8 gain;
++ u8 min_clockdiv;
+ };
+
+ struct sensor_w_data {
+@@ -82,26 +93,31 @@ struct sensor_w_data {
+ int len;
+ };
+
++static void sd_stopN(struct gspca_dev *gspca_dev);
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val);
+ static void setbrightness(struct gspca_dev *gspca_dev);
+ static void setexposure(struct gspca_dev *gspca_dev);
+ static void setgain(struct gspca_dev *gspca_dev);
+
+ /* V4L2 controls supported by the driver */
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
++/* Separate brightness control description for Argus QuickClix as it has
++ different limits from the other mr97310a cameras */
+ {
+-#define BRIGHTNESS_IDX 0
++#define NORM_BRIGHTNESS_IDX 0
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+- .minimum = MR97310A_BRIGHTNESS_MIN,
+- .maximum = MR97310A_BRIGHTNESS_MAX,
++ .minimum = -254,
++ .maximum = 255,
+ .step = 1,
+ .default_value = MR97310A_BRIGHTNESS_DEFAULT,
+ .flags = 0,
+@@ -110,7 +126,22 @@ static struct ctrl sd_ctrls[] = {
+ .get = sd_getbrightness,
+ },
+ {
+-#define EXPOSURE_IDX 1
++#define ARGUS_QC_BRIGHTNESS_IDX 1
++ {
++ .id = V4L2_CID_BRIGHTNESS,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Brightness",
++ .minimum = 0,
++ .maximum = 15,
++ .step = 1,
++ .default_value = MR97310A_BRIGHTNESS_DEFAULT,
++ .flags = 0,
++ },
++ .set = sd_setbrightness,
++ .get = sd_getbrightness,
++ },
++ {
++#define EXPOSURE_IDX 2
+ {
+ .id = V4L2_CID_EXPOSURE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+@@ -125,7 +156,7 @@ static struct ctrl sd_ctrls[] = {
+ .get = sd_getexposure,
+ },
+ {
+-#define GAIN_IDX 2
++#define GAIN_IDX 3
+ {
+ .id = V4L2_CID_GAIN,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+@@ -139,6 +170,21 @@ static struct ctrl sd_ctrls[] = {
+ .set = sd_setgain,
+ .get = sd_getgain,
+ },
++ {
++#define MIN_CLOCKDIV_IDX 4
++ {
++ .id = V4L2_CID_PRIVATE_BASE,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Minimum Clock Divider",
++ .minimum = MR97310A_MIN_CLOCKDIV_MIN,
++ .maximum = MR97310A_MIN_CLOCKDIV_MAX,
++ .step = 1,
++ .default_value = MR97310A_MIN_CLOCKDIV_DEFAULT,
++ .flags = 0,
++ },
++ .set = sd_setmin_clockdiv,
++ .get = sd_getmin_clockdiv,
++ },
+ };
+
+ static const struct v4l2_pix_format vga_mode[] = {
+@@ -230,12 +276,17 @@ static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data)
+ int rc;
+
+ buf = data;
+- rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1);
++ if (sd->cam_type == CAM_TYPE_CIF) {
++ rc = sensor_write_reg(gspca_dev, reg, 0x01, &buf, 1);
++ confirm_reg = sd->sensor_type ? 0x13 : 0x11;
++ } else {
++ rc = sensor_write_reg(gspca_dev, reg, 0x00, &buf, 1);
++ confirm_reg = 0x11;
++ }
+ if (rc < 0)
+ return rc;
+
+ buf = 0x01;
+- confirm_reg = sd->sensor_type ? 0x13 : 0x11;
+ rc = sensor_write_reg(gspca_dev, confirm_reg, 0x00, &buf, 1);
+ if (rc < 0)
+ return rc;
+@@ -243,18 +294,26 @@ static int sensor_write1(struct gspca_dev *gspca_dev, u8 reg, u8 data)
+ return 0;
+ }
+
+-static int cam_get_response16(struct gspca_dev *gspca_dev)
++static int cam_get_response16(struct gspca_dev *gspca_dev, u8 reg, int verbose)
+ {
+- __u8 *data = gspca_dev->usb_buf;
+ int err_code;
+
+- data[0] = 0x21;
++ gspca_dev->usb_buf[0] = reg;
+ err_code = mr_write(gspca_dev, 1);
+ if (err_code < 0)
+ return err_code;
+
+ err_code = mr_read(gspca_dev, 16);
+- return err_code;
++ if (err_code < 0)
++ return err_code;
++
++ if (verbose)
++ PDEBUG(D_PROBE, "Register: %02x reads %02x%02x%02x", reg,
++ gspca_dev->usb_buf[0],
++ gspca_dev->usb_buf[1],
++ gspca_dev->usb_buf[2]);
++
++ return 0;
+ }
+
+ static int zero_the_pointer(struct gspca_dev *gspca_dev)
+@@ -264,18 +323,17 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
+ u8 status = 0;
+ int tries = 0;
+
+- err_code = cam_get_response16(gspca_dev);
++ err_code = cam_get_response16(gspca_dev, 0x21, 0);
+ if (err_code < 0)
+ return err_code;
+
+- err_code = mr_write(gspca_dev, 1);
+ data[0] = 0x19;
+ data[1] = 0x51;
+ err_code = mr_write(gspca_dev, 2);
+ if (err_code < 0)
+ return err_code;
+
+- err_code = cam_get_response16(gspca_dev);
++ err_code = cam_get_response16(gspca_dev, 0x21, 0);
+ if (err_code < 0)
+ return err_code;
+
+@@ -285,7 +343,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
+ if (err_code < 0)
+ return err_code;
+
+- err_code = cam_get_response16(gspca_dev);
++ err_code = cam_get_response16(gspca_dev, 0x21, 0);
+ if (err_code < 0)
+ return err_code;
+
+@@ -295,7 +353,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
+ if (err_code < 0)
+ return err_code;
+
+- err_code = cam_get_response16(gspca_dev);
++ err_code = cam_get_response16(gspca_dev, 0x21, 0);
+ if (err_code < 0)
+ return err_code;
+
+@@ -306,7 +364,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
+ return err_code;
+
+ while (status != 0x0a && tries < 256) {
+- err_code = cam_get_response16(gspca_dev);
++ err_code = cam_get_response16(gspca_dev, 0x21, 0);
+ status = data[0];
+ tries++;
+ if (err_code < 0)
+@@ -323,7 +381,7 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
+ if (err_code < 0)
+ return err_code;
+
+- err_code = cam_get_response16(gspca_dev);
++ err_code = cam_get_response16(gspca_dev, 0x21, 0);
+ status = data[0];
+ tries++;
+ if (err_code < 0)
+@@ -342,89 +400,200 @@ static int zero_the_pointer(struct gspca_dev *gspca_dev)
+ return 0;
+ }
+
+-static u8 get_sensor_id(struct gspca_dev *gspca_dev)
++static int stream_start(struct gspca_dev *gspca_dev)
+ {
+- int err_code;
+-
+- gspca_dev->usb_buf[0] = 0x1e;
+- err_code = mr_write(gspca_dev, 1);
+- if (err_code < 0)
+- return err_code;
++ gspca_dev->usb_buf[0] = 0x01;
++ gspca_dev->usb_buf[1] = 0x01;
++ return mr_write(gspca_dev, 2);
++}
+
+- err_code = mr_read(gspca_dev, 16);
+- if (err_code < 0)
+- return err_code;
++static void stream_stop(struct gspca_dev *gspca_dev)
++{
++ gspca_dev->usb_buf[0] = 0x01;
++ gspca_dev->usb_buf[1] = 0x00;
++ if (mr_write(gspca_dev, 2) < 0)
++ PDEBUG(D_ERR, "Stream Stop failed");
++}
+
+- PDEBUG(D_PROBE, "Byte zero reported is %01x", gspca_dev->usb_buf[0]);
++static void lcd_stop(struct gspca_dev *gspca_dev)
++{
++ gspca_dev->usb_buf[0] = 0x19;
++ gspca_dev->usb_buf[1] = 0x54;
++ if (mr_write(gspca_dev, 2) < 0)
++ PDEBUG(D_ERR, "LCD Stop failed");
++}
+
+- return gspca_dev->usb_buf[0];
++static int isoc_enable(struct gspca_dev *gspca_dev)
++{
++ gspca_dev->usb_buf[0] = 0x00;
++ gspca_dev->usb_buf[1] = 0x4d; /* ISOC transfering enable... */
++ return mr_write(gspca_dev, 2);
+ }
+
+-/* this function is called at probe time */
++/* This function is called at probe time */
+ static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+- __u8 *data = gspca_dev->usb_buf;
+ int err_code;
+
+ cam = &gspca_dev->cam;
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
++ sd->do_lcd_stop = 0;
++
++ /* Several of the supported CIF cameras share the same USB ID but
++ * require different initializations and different control settings.
++ * The same is true of the VGA cameras. Therefore, we are forced
++ * to start the initialization process in order to determine which
++ * camera is present. Some of the supported cameras require the
++ * memory pointer to be set to 0 as the very first item of business
++ * or else they will not stream. So we do that immediately.
++ */
++ err_code = zero_the_pointer(gspca_dev);
++ if (err_code < 0)
++ return err_code;
+
+- if (id->idProduct == 0x010e) {
+- sd->cam_type = CAM_TYPE_CIF;
+- cam->nmodes--;
++ err_code = stream_start(gspca_dev);
++ if (err_code < 0)
++ return err_code;
+
+- data[0] = 0x01;
+- data[1] = 0x01;
+- err_code = mr_write(gspca_dev, 2);
+- if (err_code < 0)
+- return err_code;
++ /* Now, the query for sensor type. */
++ err_code = cam_get_response16(gspca_dev, 0x07, 1);
++ if (err_code < 0)
++ return err_code;
+
+- msleep(200);
+- data[0] = get_sensor_id(gspca_dev);
++ if (id->idProduct == 0x0110 || id->idProduct == 0x010e) {
++ sd->cam_type = CAM_TYPE_CIF;
++ cam->nmodes--;
+ /*
+- * Known CIF cameras. If you have another to report, please do
++ * All but one of the known CIF cameras share the same USB ID,
++ * but two different init routines are in use, and the control
++ * settings are different, too. We need to detect which camera
++ * of the two known varieties is connected!
++ *
++ * A list of known CIF cameras follows. They all report either
++ * 0200 for type 0 or 0300 for type 1.
++ * If you have another to report, please do
++ *
++ * Name sd->sensor_type reported by
+ *
+- * Name byte just read sd->sensor_type
+- * reported by
+- * Sakar Spy-shot 0x28 T. Kilgore 0
+- * Innovage 0xf5 (unstable) T. Kilgore 0
+- * Vivitar Mini 0x53 H. De Goede 0
+- * Vivitar Mini 0x04 / 0x24 E. Rodriguez 0
+- * Vivitar Mini 0x08 T. Kilgore 1
+- * Elta-Media 8212dc 0x23 T. Kaiser 1
+- * Philips dig. keych. 0x37 T. Kilgore 1
++ * Sakar Spy-shot 0 T. Kilgore
++ * Innovage 0 T. Kilgore
++ * Vivitar Mini 0 H. De Goede
++ * Vivitar Mini 0 E. Rodriguez
++ * Vivitar Mini 1 T. Kilgore
++ * Elta-Media 8212dc 1 T. Kaiser
++ * Philips dig. keych. 1 T. Kilgore
++ * Trust Spyc@m 100 1 A. Jacobs
+ */
+- if ((data[0] & 0x78) == 8 ||
+- ((data[0] & 0x2) == 0x2 && data[0] != 0x53))
+- sd->sensor_type = 1;
+- else
++ switch (gspca_dev->usb_buf[0]) {
++ case 2:
+ sd->sensor_type = 0;
+-
++ break;
++ case 3:
++ sd->sensor_type = 1;
++ break;
++ default:
++ PDEBUG(D_ERR, "Unknown CIF Sensor id : %02x",
++ gspca_dev->usb_buf[1]);
++ return -ENODEV;
++ }
+ PDEBUG(D_PROBE, "MR97310A CIF camera detected, sensor: %d",
+ sd->sensor_type);
++ } else {
++ sd->cam_type = CAM_TYPE_VGA;
++
++ /*
++ * Here is a table of the responses to the query for sensor
++ * type, from the known MR97310A VGA cameras.
++ *
++ * Name gspca_dev->usb_buf[] sd->sensor_type
++ * sd->do_lcd_stop
++ * Aiptek Pencam VGA+ 0300 0 1
++ * ION digital 0350 0 1
++ * Argus DC-1620 0450 1 0
++ * Argus QuickClix 0420 1 1
++ *
++ * Based upon these results, we assume default settings
++ * and then correct as necessary, as follows.
++ *
++ */
+
+- if (force_sensor_type != -1) {
+- sd->sensor_type = !! force_sensor_type;
+- PDEBUG(D_PROBE, "Forcing sensor type to: %d",
+- sd->sensor_type);
++ sd->sensor_type = 1;
++ sd->do_lcd_stop = 0;
++ sd->adj_colors = 0;
++ if ((gspca_dev->usb_buf[0] != 0x03) &&
++ (gspca_dev->usb_buf[0] != 0x04)) {
++ PDEBUG(D_ERR, "Unknown VGA Sensor id Byte 0: %02x",
++ gspca_dev->usb_buf[1]);
++ PDEBUG(D_ERR, "Defaults assumed, may not work");
++ PDEBUG(D_ERR, "Please report this");
+ }
++ /* Sakar Digital color needs to be adjusted. */
++ if ((gspca_dev->usb_buf[0] == 0x03) &&
++ (gspca_dev->usb_buf[1] == 0x50))
++ sd->adj_colors = 1;
++ if (gspca_dev->usb_buf[0] == 0x04) {
++ sd->do_lcd_stop = 1;
++ switch (gspca_dev->usb_buf[1]) {
++ case 0x50:
++ sd->sensor_type = 0;
++ PDEBUG(D_PROBE, "sensor_type corrected to 0");
++ break;
++ case 0x20:
++ /* Nothing to do here. */
++ break;
++ default:
++ PDEBUG(D_ERR,
++ "Unknown VGA Sensor id Byte 1: %02x",
++ gspca_dev->usb_buf[1]);
++ PDEBUG(D_ERR,
++ "Defaults assumed, may not work");
++ PDEBUG(D_ERR, "Please report this");
++ }
++ }
++ PDEBUG(D_PROBE, "MR97310A VGA camera detected, sensor: %d",
++ sd->sensor_type);
++ }
++ /* Stop streaming as we've started it only to probe the sensor type. */
++ sd_stopN(gspca_dev);
++
++ if (force_sensor_type != -1) {
++ sd->sensor_type = !!force_sensor_type;
++ PDEBUG(D_PROBE, "Forcing sensor type to: %d",
++ sd->sensor_type);
++ }
+
++ /* Setup controls depending on camera type */
++ if (sd->cam_type == CAM_TYPE_CIF) {
++ /* No brightness for sensor_type 0 */
+ if (sd->sensor_type == 0)
+- gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX);
++ gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
++ (1 << ARGUS_QC_BRIGHTNESS_IDX);
++ else
++ gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX) |
++ (1 << MIN_CLOCKDIV_IDX);
+ } else {
+- sd->cam_type = CAM_TYPE_VGA;
+- PDEBUG(D_PROBE, "MR97310A VGA camera detected");
+- gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX) |
+- (1 << EXPOSURE_IDX) | (1 << GAIN_IDX);
++ /* All controls need to be disabled if VGA sensor_type is 0 */
++ if (sd->sensor_type == 0)
++ gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX) |
++ (1 << ARGUS_QC_BRIGHTNESS_IDX) |
++ (1 << EXPOSURE_IDX) |
++ (1 << GAIN_IDX) |
++ (1 << MIN_CLOCKDIV_IDX);
++ else if (sd->do_lcd_stop)
++ /* Argus QuickClix has different brightness limits */
++ gspca_dev->ctrl_dis = (1 << NORM_BRIGHTNESS_IDX);
++ else
++ gspca_dev->ctrl_dis = (1 << ARGUS_QC_BRIGHTNESS_IDX);
+ }
+
+ sd->brightness = MR97310A_BRIGHTNESS_DEFAULT;
+ sd->exposure = MR97310A_EXPOSURE_DEFAULT;
+ sd->gain = MR97310A_GAIN_DEFAULT;
++ sd->min_clockdiv = MR97310A_MIN_CLOCKDIV_DEFAULT;
+
+ return 0;
+ }
+@@ -455,11 +624,6 @@ static int start_cif_cam(struct gspca_dev *gspca_dev)
+ };
+
+ /* Note: Some of the above descriptions guessed from MR97113A driver */
+- data[0] = 0x01;
+- data[1] = 0x01;
+- err_code = mr_write(gspca_dev, 2);
+- if (err_code < 0)
+- return err_code;
+
+ memcpy(data, startup_string, 11);
+ if (sd->sensor_type)
+@@ -530,25 +694,16 @@ static int start_cif_cam(struct gspca_dev *gspca_dev)
+ {0x13, 0x00, {0x01}, 1},
+ {0, 0, {0}, 0}
+ };
++ /* Without this command the cam won't work with USB-UHCI */
++ gspca_dev->usb_buf[0] = 0x0a;
++ gspca_dev->usb_buf[1] = 0x00;
++ err_code = mr_write(gspca_dev, 2);
++ if (err_code < 0)
++ return err_code;
+ err_code = sensor_write_regs(gspca_dev, cif_sensor1_init_data,
+ ARRAY_SIZE(cif_sensor1_init_data));
+ }
+- if (err_code < 0)
+- return err_code;
+-
+- setbrightness(gspca_dev);
+- setexposure(gspca_dev);
+- setgain(gspca_dev);
+-
+- msleep(200);
+-
+- data[0] = 0x00;
+- data[1] = 0x4d; /* ISOC transfering enable... */
+- err_code = mr_write(gspca_dev, 2);
+- if (err_code < 0)
+- return err_code;
+-
+- return 0;
++ return err_code;
+ }
+
+ static int start_vga_cam(struct gspca_dev *gspca_dev)
+@@ -558,84 +713,8 @@ static int start_vga_cam(struct gspca_dev *gspca_dev)
+ int err_code;
+ const __u8 startup_string[] = {0x00, 0x0d, 0x01, 0x00, 0x00, 0x2b,
+ 0x00, 0x00, 0x00, 0x50, 0xc0};
+-
+ /* What some of these mean is explained in start_cif_cam(), above */
+- sd->sof_read = 0;
+-
+- /*
+- * We have to know which camera we have, because the register writes
+- * depend upon the camera. This test, run before we actually enter
+- * the initialization routine, distinguishes most of the cameras, If
+- * needed, another routine is done later, too.
+- */
+- memset(data, 0, 16);
+- data[0] = 0x20;
+- err_code = mr_write(gspca_dev, 1);
+- if (err_code < 0)
+- return err_code;
+-
+- err_code = mr_read(gspca_dev, 16);
+- if (err_code < 0)
+- return err_code;
+-
+- PDEBUG(D_PROBE, "Byte reported is %02x", data[0]);
+-
+- msleep(200);
+- /*
+- * Known VGA cameras. If you have another to report, please do
+- *
+- * Name byte just read sd->sensor_type
+- * sd->do_lcd_stop
+- * Aiptek Pencam VGA+ 0x31 0 1
+- * ION digital 0x31 0 1
+- * Argus DC-1620 0x30 1 0
+- * Argus QuickClix 0x30 1 1 (not caught here)
+- */
+- sd->sensor_type = data[0] & 1;
+- sd->do_lcd_stop = (~data[0]) & 1;
+-
+-
+-
+- /* Streaming setup begins here. */
+-
+-
+- data[0] = 0x01;
+- data[1] = 0x01;
+- err_code = mr_write(gspca_dev, 2);
+- if (err_code < 0)
+- return err_code;
+-
+- /*
+- * A second test can now resolve any remaining ambiguity in the
+- * identification of the camera type,
+- */
+- if (!sd->sensor_type) {
+- data[0] = get_sensor_id(gspca_dev);
+- if (data[0] == 0x7f) {
+- sd->sensor_type = 1;
+- PDEBUG(D_PROBE, "sensor_type corrected to 1");
+- }
+- msleep(200);
+- }
+
+- if (force_sensor_type != -1) {
+- sd->sensor_type = !! force_sensor_type;
+- PDEBUG(D_PROBE, "Forcing sensor type to: %d",
+- sd->sensor_type);
+- }
+-
+- /*
+- * Known VGA cameras.
+- * This test is only run if the previous test returned 0x30, but
+- * here is the information for all others, too, just for reference.
+- *
+- * Name byte just read sd->sensor_type
+- *
+- * Aiptek Pencam VGA+ 0xfb (this test not run) 1
+- * ION digital 0xbd (this test not run) 1
+- * Argus DC-1620 0xe5 (no change) 0
+- * Argus QuickClix 0x7f (reclassified) 1
+- */
+ memcpy(data, startup_string, 11);
+ if (!sd->sensor_type) {
+ data[5] = 0x00;
+@@ -689,29 +768,44 @@ static int start_vga_cam(struct gspca_dev *gspca_dev)
+ err_code = sensor_write_regs(gspca_dev, vga_sensor0_init_data,
+ ARRAY_SIZE(vga_sensor0_init_data));
+ } else { /* sd->sensor_type = 1 */
+- const struct sensor_w_data vga_sensor1_init_data[] = {
++ const struct sensor_w_data color_adj[] = {
++ {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00,
++ /* adjusted blue, green, red gain correct
++ too much blue from the Sakar Digital */
++ 0x05, 0x01, 0x04}, 8}
++ };
++
++ const struct sensor_w_data color_no_adj[] = {
+ {0x02, 0x00, {0x06, 0x59, 0x0c, 0x16, 0x00,
+- 0x07, 0x00, 0x01}, 8},
++ /* default blue, green, red gain settings */
++ 0x07, 0x00, 0x01}, 8}
++ };
++
++ const struct sensor_w_data vga_sensor1_init_data[] = {
+ {0x11, 0x04, {0x01}, 1},
+- /*{0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01, */
+- {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01,
++ {0x0a, 0x00, {0x00, 0x01, 0x00, 0x00, 0x01,
++ /* These settings may be better for some cameras */
++ /* {0x0a, 0x00, {0x01, 0x06, 0x00, 0x00, 0x01, */
+ 0x00, 0x0a}, 7},
+ {0x11, 0x04, {0x01}, 1},
+ {0x12, 0x00, {0x00, 0x63, 0x00, 0x70, 0x00, 0x00}, 6},
+ {0x11, 0x04, {0x01}, 1},
+ {0, 0, {0}, 0}
+ };
++
++ if (sd->adj_colors)
++ err_code = sensor_write_regs(gspca_dev, color_adj,
++ ARRAY_SIZE(color_adj));
++ else
++ err_code = sensor_write_regs(gspca_dev, color_no_adj,
++ ARRAY_SIZE(color_no_adj));
++
++ if (err_code < 0)
++ return err_code;
++
+ err_code = sensor_write_regs(gspca_dev, vga_sensor1_init_data,
+ ARRAY_SIZE(vga_sensor1_init_data));
+ }
+- if (err_code < 0)
+- return err_code;
+-
+- msleep(200);
+- data[0] = 0x00;
+- data[1] = 0x4d; /* ISOC transfering enable... */
+- err_code = mr_write(gspca_dev, 2);
+-
+ return err_code;
+ }
+
+@@ -719,97 +813,120 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ int err_code;
+- struct cam *cam;
+
+- cam = &gspca_dev->cam;
+ sd->sof_read = 0;
+- /*
+- * Some of the supported cameras require the memory pointer to be
+- * set to 0, or else they will not stream.
+- */
+- zero_the_pointer(gspca_dev);
+- msleep(200);
++
++ /* Some of the VGA cameras require the memory pointer
++ * to be set to 0 again. We have been forced to start the
++ * stream in sd_config() to detect the hardware, and closed it.
++ * Thus, we need here to do a completely fresh and clean start. */
++ err_code = zero_the_pointer(gspca_dev);
++ if (err_code < 0)
++ return err_code;
++
++ err_code = stream_start(gspca_dev);
++ if (err_code < 0)
++ return err_code;
++
+ if (sd->cam_type == CAM_TYPE_CIF) {
+ err_code = start_cif_cam(gspca_dev);
+ } else {
+ err_code = start_vga_cam(gspca_dev);
+ }
+- return err_code;
++ if (err_code < 0)
++ return err_code;
++
++ setbrightness(gspca_dev);
++ setexposure(gspca_dev);
++ setgain(gspca_dev);
++
++ return isoc_enable(gspca_dev);
+ }
+
+ static void sd_stopN(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- int result;
+-
+- gspca_dev->usb_buf[0] = 1;
+- gspca_dev->usb_buf[1] = 0;
+- result = mr_write(gspca_dev, 2);
+- if (result < 0)
+- PDEBUG(D_ERR, "Camera Stop failed");
+
++ stream_stop(gspca_dev);
+ /* Not all the cams need this, but even if not, probably a good idea */
+ zero_the_pointer(gspca_dev);
+- if (sd->do_lcd_stop) {
+- gspca_dev->usb_buf[0] = 0x19;
+- gspca_dev->usb_buf[1] = 0x54;
+- result = mr_write(gspca_dev, 2);
+- if (result < 0)
+- PDEBUG(D_ERR, "Camera Stop failed");
+- }
++ if (sd->do_lcd_stop)
++ lcd_stop(gspca_dev);
+ }
+
+ static void setbrightness(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 val;
+-
+- if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS_IDX))
++ u8 sign_reg = 7; /* This reg and the next one used on CIF cams. */
++ u8 value_reg = 8; /* VGA cams seem to use regs 0x0b and 0x0c */
++ const u8 quick_clix_table[] =
++ /* 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 */
++ { 0, 4, 8, 12, 1, 2, 3, 5, 6, 9, 7, 10, 13, 11, 14, 15};
++ /*
++ * This control is disabled for CIF type 1 and VGA type 0 cameras.
++ * It does not quite act linearly for the Argus QuickClix camera,
++ * but it does control brightness. The values are 0 - 15 only, and
++ * the table above makes them act consecutively.
++ */
++ if ((gspca_dev->ctrl_dis & (1 << NORM_BRIGHTNESS_IDX)) &&
++ (gspca_dev->ctrl_dis & (1 << ARGUS_QC_BRIGHTNESS_IDX)))
+ return;
+
+- /* Note register 7 is also seen as 0x8x or 0xCx in dumps */
++ if (sd->cam_type == CAM_TYPE_VGA) {
++ sign_reg += 4;
++ value_reg += 4;
++ }
++
++ /* Note register 7 is also seen as 0x8x or 0xCx in some dumps */
+ if (sd->brightness > 0) {
+- sensor_write1(gspca_dev, 7, 0x00);
++ sensor_write1(gspca_dev, sign_reg, 0x00);
+ val = sd->brightness;
+ } else {
+- sensor_write1(gspca_dev, 7, 0x01);
+- val = 257 - sd->brightness;
++ sensor_write1(gspca_dev, sign_reg, 0x01);
++ val = (257 - sd->brightness);
+ }
+- sensor_write1(gspca_dev, 8, val);
++ /* Use lookup table for funky Argus QuickClix brightness */
++ if (sd->do_lcd_stop)
++ val = quick_clix_table[val];
++
++ sensor_write1(gspca_dev, value_reg, val);
+ }
+
+ static void setexposure(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- u8 val;
++ int exposure;
++ u8 buf[2];
+
+ if (gspca_dev->ctrl_dis & (1 << EXPOSURE_IDX))
+ return;
+
+- if (sd->sensor_type) {
+- val = sd->exposure >> 4;
+- sensor_write1(gspca_dev, 3, val);
+- val = sd->exposure & 0xf;
+- sensor_write1(gspca_dev, 4, val);
++ if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) {
++ /* This cam does not like exposure settings < 300,
++ so scale 0 - 4095 to 300 - 4095 */
++ exposure = (sd->exposure * 9267) / 10000 + 300;
++ sensor_write1(gspca_dev, 3, exposure >> 4);
++ sensor_write1(gspca_dev, 4, exposure & 0x0f);
+ } else {
+- u8 clockdiv;
+- int exposure;
+-
+ /* We have both a clock divider and an exposure register.
+ We first calculate the clock divider, as that determines
+- the maximum exposure and then we calculayte the exposure
++ the maximum exposure and then we calculate the exposure
+ register setting (which goes from 0 - 511).
+
+ Note our 0 - 4095 exposure is mapped to 0 - 511
+ milliseconds exposure time */
+- clockdiv = (60 * sd->exposure + 7999) / 8000;
++ u8 clockdiv = (60 * sd->exposure + 7999) / 8000;
+
+ /* Limit framerate to not exceed usb bandwidth */
+- if (clockdiv < 3 && gspca_dev->width >= 320)
+- clockdiv = 3;
++ if (clockdiv < sd->min_clockdiv && gspca_dev->width >= 320)
++ clockdiv = sd->min_clockdiv;
+ else if (clockdiv < 2)
+ clockdiv = 2;
+
++ if (sd->cam_type == CAM_TYPE_VGA && clockdiv < 4)
++ clockdiv = 4;
++
+ /* Frame exposure time in ms = 1000 * clockdiv / 60 ->
+ exposure = (sd->exposure / 8) * 511 / (1000 * clockdiv / 60) */
+ exposure = (60 * 511 * sd->exposure) / (8000 * clockdiv);
+@@ -819,9 +936,10 @@ static void setexposure(struct gspca_dev *gspca_dev)
+ /* exposure register value is reversed! */
+ exposure = 511 - exposure;
+
++ buf[0] = exposure & 0xff;
++ buf[1] = exposure >> 8;
++ sensor_write_reg(gspca_dev, 0x0e, 0, buf, 2);
+ sensor_write1(gspca_dev, 0x02, clockdiv);
+- sensor_write1(gspca_dev, 0x0e, exposure & 0xff);
+- sensor_write1(gspca_dev, 0x0f, exposure >> 8);
+ }
+ }
+
+@@ -832,7 +950,7 @@ static void setgain(struct gspca_dev *gspca_dev)
+ if (gspca_dev->ctrl_dis & (1 << GAIN_IDX))
+ return;
+
+- if (sd->sensor_type) {
++ if (sd->cam_type == CAM_TYPE_CIF && sd->sensor_type == 1) {
+ sensor_write1(gspca_dev, 0x0e, sd->gain);
+ } else {
+ sensor_write1(gspca_dev, 0x10, sd->gain);
+@@ -893,17 +1011,35 @@ static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+ return 0;
+ }
+
++static int sd_setmin_clockdiv(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->min_clockdiv = val;
++ if (gspca_dev->streaming)
++ setexposure(gspca_dev);
++ return 0;
++}
++
++static int sd_getmin_clockdiv(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->min_clockdiv;
++ return 0;
++}
++
+ /* Include pac common sof detection functions */
+ #include "pac_common.h"
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+- __u8 *data, /* isoc packet */
+- int len) /* iso packet length */
++ u8 *data, /* isoc packet */
++ int len) /* iso packet length */
+ {
++ struct sd *sd = (struct sd *) gspca_dev;
+ unsigned char *sof;
+
+- sof = pac_find_sof(gspca_dev, data, len);
++ sof = pac_find_sof(&sd->sof_read, data, len);
+ if (sof) {
+ int n;
+
+@@ -913,15 +1049,15 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ n -= sizeof pac_sof_marker;
+ else
+ n = 0;
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
++ gspca_frame_add(gspca_dev, LAST_PACKET,
+ data, n);
+ /* Start next frame. */
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ pac_sof_marker, sizeof pac_sof_marker);
+ len -= sof - data;
+ data = sof;
+ }
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+
+ /* sub-driver description */
+@@ -938,6 +1074,7 @@ static const struct sd_desc sd_desc = {
+
+ /* -- module initialisation -- */
+ static const __devinitdata struct usb_device_id device_table[] = {
++ {USB_DEVICE(0x08ca, 0x0110)}, /* Trust Spyc@m 100 */
+ {USB_DEVICE(0x08ca, 0x0111)}, /* Aiptek Pencam VGA+ */
+ {USB_DEVICE(0x093a, 0x010f)}, /* All other known MR97310A VGA cams */
+ {USB_DEVICE(0x093a, 0x010e)}, /* All known MR97310A CIF cams */
+diff --git a/drivers/media/video/gspca/ov519.c b/drivers/media/video/gspca/ov519.c
+index e165578..d8fe289 100644
+--- a/drivers/media/video/gspca/ov519.c
++++ b/drivers/media/video/gspca/ov519.c
+@@ -2,14 +2,19 @@
+ * OV519 driver
+ *
+ * Copyright (C) 2008 Jean-Francois Moine (http://moinejf.free.fr)
++ * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This module is adapted from the ov51x-jpeg package, which itself
+ * was adapted from the ov511 driver.
+ *
+ * Original copyright for the ov511 driver is:
+ *
+- * Copyright (c) 1999-2004 Mark W. McClelland
++ * Copyright (c) 1999-2006 Mark W. McClelland
+ * Support for OV519, OV8610 Copyright (c) 2003 Joerg Heckenbach
++ * Many improvements by Bret Wallach <bwallac1@san.rr.com>
++ * Color fixes by by Orion Sky Lawlor <olawlor@acm.org> (2/26/2000)
++ * OV7620 fixes by Charl P. Botha <cpbotha@ieee.org>
++ * Changes by Claudio Matsuoka <claudio@conectiva.com>
+ *
+ * ov51x-jpeg original copyright is:
+ *
+@@ -58,6 +63,8 @@ struct sd {
+ #define BRIDGE_OV518 2
+ #define BRIDGE_OV518PLUS 3
+ #define BRIDGE_OV519 4
++#define BRIDGE_OVFX2 5
++#define BRIDGE_W9968CF 6
+ #define BRIDGE_MASK 7
+
+ char invert_led;
+@@ -73,6 +80,10 @@ struct sd {
+ __u8 vflip;
+ __u8 autobrightness;
+ __u8 freq;
++ __u8 quality;
++#define QUALITY_MIN 50
++#define QUALITY_MAX 70
++#define QUALITY_DEF 50
+
+ __u8 stopped; /* Streaming is temporarily paused */
+
+@@ -81,17 +92,33 @@ struct sd {
+
+ char sensor; /* Type of image sensor chip (SEN_*) */
+ #define SEN_UNKNOWN 0
+-#define SEN_OV6620 1
+-#define SEN_OV6630 2
+-#define SEN_OV66308AF 3
+-#define SEN_OV7610 4
+-#define SEN_OV7620 5
+-#define SEN_OV7640 6
+-#define SEN_OV7670 7
+-#define SEN_OV76BE 8
+-#define SEN_OV8610 9
++#define SEN_OV2610 1
++#define SEN_OV3610 2
++#define SEN_OV6620 3
++#define SEN_OV6630 4
++#define SEN_OV66308AF 5
++#define SEN_OV7610 6
++#define SEN_OV7620 7
++#define SEN_OV7620AE 8
++#define SEN_OV7640 9
++#define SEN_OV7648 10
++#define SEN_OV7670 11
++#define SEN_OV76BE 12
++#define SEN_OV8610 13
++
++ u8 sensor_addr;
++ int sensor_width;
++ int sensor_height;
++ int sensor_reg_cache[256];
++
++ u8 *jpeg_hdr;
+ };
+
++/* Note this is a bit of a hack, but the w9968cf driver needs the code for all
++ the ov sensors which is already present here. When we have the time we
++ really should move the sensor drivers to v4l2 sub drivers. */
++#include "w996Xcf.c"
++
+ /* V4L2 controls supported by the driver */
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+@@ -114,6 +141,7 @@ static void setautobrightness(struct sd *sd);
+ static void setfreq(struct sd *sd);
+
+ static const struct ctrl sd_ctrls[] = {
++#define BRIGHTNESS_IDX 0
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+@@ -128,6 +156,7 @@ static const struct ctrl sd_ctrls[] = {
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
++#define CONTRAST_IDX 1
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+@@ -142,6 +171,7 @@ static const struct ctrl sd_ctrls[] = {
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
++#define COLOR_IDX 2
+ {
+ {
+ .id = V4L2_CID_SATURATION,
+@@ -345,6 +375,75 @@ static const struct v4l2_pix_format ov511_sif_mode[] = {
+ .priv = 0},
+ };
+
++static const struct v4l2_pix_format ovfx2_vga_mode[] = {
++ {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
++ .bytesperline = 320,
++ .sizeimage = 320 * 240,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 1},
++ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
++ .bytesperline = 640,
++ .sizeimage = 640 * 480,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 0},
++};
++static const struct v4l2_pix_format ovfx2_cif_mode[] = {
++ {160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
++ .bytesperline = 160,
++ .sizeimage = 160 * 120,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 3},
++ {176, 144, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
++ .bytesperline = 176,
++ .sizeimage = 176 * 144,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 1},
++ {320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
++ .bytesperline = 320,
++ .sizeimage = 320 * 240,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 2},
++ {352, 288, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
++ .bytesperline = 352,
++ .sizeimage = 352 * 288,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 0},
++};
++static const struct v4l2_pix_format ovfx2_ov2610_mode[] = {
++ {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
++ .bytesperline = 1600,
++ .sizeimage = 1600 * 1200,
++ .colorspace = V4L2_COLORSPACE_SRGB},
++};
++static const struct v4l2_pix_format ovfx2_ov3610_mode[] = {
++ {640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
++ .bytesperline = 640,
++ .sizeimage = 640 * 480,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 1},
++ {800, 600, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
++ .bytesperline = 800,
++ .sizeimage = 800 * 600,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 1},
++ {1024, 768, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
++ .bytesperline = 1024,
++ .sizeimage = 1024 * 768,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 1},
++ {1600, 1200, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
++ .bytesperline = 1600,
++ .sizeimage = 1600 * 1200,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 0},
++ {2048, 1536, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
++ .bytesperline = 2048,
++ .sizeimage = 2048 * 1536,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 0},
++};
++
++
+ /* Registers common to OV511 / OV518 */
+ #define R51x_FIFO_PSIZE 0x30 /* 2 bytes wide w/ OV518(+) */
+ #define R51x_SYS_RESET 0x50
+@@ -406,6 +505,30 @@ static const struct v4l2_pix_format ov511_sif_mode[] = {
+
+ #define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */
+
++/*
++ * The FX2 chip does not give us a zero length read at end of frame.
++ * It does, however, give a short read at the end of a frame, if
++ * neccessary, rather than run two frames together.
++ *
++ * By choosing the right bulk transfer size, we are guaranteed to always
++ * get a short read for the last read of each frame. Frame sizes are
++ * always a composite number (width * height, or a multiple) so if we
++ * choose a prime number, we are guaranteed that the last read of a
++ * frame will be short.
++ *
++ * But it isn't that easy: the 2.6 kernel requires a multiple of 4KB,
++ * otherwise EOVERFLOW "babbling" errors occur. I have not been able
++ * to figure out why. [PMiller]
++ *
++ * The constant (13 * 4096) is the largest "prime enough" number less than 64KB.
++ *
++ * It isn't enough to know the number of bytes per frame, in case we
++ * have data dropouts or buffer overruns (even though the FX2 double
++ * buffers, there are some pretty strict real time constraints for
++ * isochronous transfer for larger frame sizes).
++ */
++#define OVFX2_BULK_SIZE (13 * 4096)
++
+ /* I2C registers */
+ #define R51x_I2C_W_SID 0x41
+ #define R51x_I2C_SADDR_3 0x42
+@@ -413,9 +536,11 @@ static const struct v4l2_pix_format ov511_sif_mode[] = {
+ #define R51x_I2C_R_SID 0x44
+ #define R51x_I2C_DATA 0x45
+ #define R518_I2C_CTL 0x47 /* OV518(+) only */
++#define OVFX2_I2C_ADDR 0x00
+
+ /* I2C ADDRESSES */
+ #define OV7xx0_SID 0x42
++#define OV_HIRES_SID 0x60 /* OV9xxx / OV2xxx / OV3xxx */
+ #define OV8xx0_SID 0xa0
+ #define OV6xx0_SID 0xc0
+
+@@ -508,6 +633,696 @@ struct ov_i2c_regvals {
+ __u8 val;
+ };
+
++/* Settings for OV2610 camera chip */
++static const struct ov_i2c_regvals norm_2610[] =
++{
++ { 0x12, 0x80 }, /* reset */
++};
++
++static const struct ov_i2c_regvals norm_3620b[] =
++{
++ /*
++ * From the datasheet: "Note that after writing to register COMH
++ * (0x12) to change the sensor mode, registers related to the
++ * sensor’s cropping window will be reset back to their default
++ * values."
++ *
++ * "wait 4096 external clock ... to make sure the sensor is
++ * stable and ready to access registers" i.e. 160us at 24MHz
++ */
++
++ { 0x12, 0x80 }, /* COMH reset */
++ { 0x12, 0x00 }, /* QXGA, master */
++
++ /*
++ * 11 CLKRC "Clock Rate Control"
++ * [7] internal frequency doublers: on
++ * [6] video port mode: master
++ * [5:0] clock divider: 1
++ */
++ { 0x11, 0x80 },
++
++ /*
++ * 13 COMI "Common Control I"
++ * = 192 (0xC0) 11000000
++ * COMI[7] "AEC speed selection"
++ * = 1 (0x01) 1....... "Faster AEC correction"
++ * COMI[6] "AEC speed step selection"
++ * = 1 (0x01) .1...... "Big steps, fast"
++ * COMI[5] "Banding filter on off"
++ * = 0 (0x00) ..0..... "Off"
++ * COMI[4] "Banding filter option"
++ * = 0 (0x00) ...0.... "Main clock is 48 MHz and
++ * the PLL is ON"
++ * COMI[3] "Reserved"
++ * = 0 (0x00) ....0...
++ * COMI[2] "AGC auto manual control selection"
++ * = 0 (0x00) .....0.. "Manual"
++ * COMI[1] "AWB auto manual control selection"
++ * = 0 (0x00) ......0. "Manual"
++ * COMI[0] "Exposure control"
++ * = 0 (0x00) .......0 "Manual"
++ */
++ { 0x13, 0xC0 },
++
++ /*
++ * 09 COMC "Common Control C"
++ * = 8 (0x08) 00001000
++ * COMC[7:5] "Reserved"
++ * = 0 (0x00) 000.....
++ * COMC[4] "Sleep Mode Enable"
++ * = 0 (0x00) ...0.... "Normal mode"
++ * COMC[3:2] "Sensor sampling reset timing selection"
++ * = 2 (0x02) ....10.. "Longer reset time"
++ * COMC[1:0] "Output drive current select"
++ * = 0 (0x00) ......00 "Weakest"
++ */
++ { 0x09, 0x08 },
++
++ /*
++ * 0C COMD "Common Control D"
++ * = 8 (0x08) 00001000
++ * COMD[7] "Reserved"
++ * = 0 (0x00) 0.......
++ * COMD[6] "Swap MSB and LSB at the output port"
++ * = 0 (0x00) .0...... "False"
++ * COMD[5:3] "Reserved"
++ * = 1 (0x01) ..001...
++ * COMD[2] "Output Average On Off"
++ * = 0 (0x00) .....0.. "Output Normal"
++ * COMD[1] "Sensor precharge voltage selection"
++ * = 0 (0x00) ......0. "Selects internal
++ * reference precharge
++ * voltage"
++ * COMD[0] "Snapshot option"
++ * = 0 (0x00) .......0 "Enable live video output
++ * after snapshot sequence"
++ */
++ { 0x0c, 0x08 },
++
++ /*
++ * 0D COME "Common Control E"
++ * = 161 (0xA1) 10100001
++ * COME[7] "Output average option"
++ * = 1 (0x01) 1....... "Output average of 4 pixels"
++ * COME[6] "Anti-blooming control"
++ * = 0 (0x00) .0...... "Off"
++ * COME[5:3] "Reserved"
++ * = 4 (0x04) ..100...
++ * COME[2] "Clock output power down pin status"
++ * = 0 (0x00) .....0.. "Tri-state data output pin
++ * on power down"
++ * COME[1] "Data output pin status selection at power down"
++ * = 0 (0x00) ......0. "Tri-state VSYNC, PCLK,
++ * HREF, and CHSYNC pins on
++ * power down"
++ * COME[0] "Auto zero circuit select"
++ * = 1 (0x01) .......1 "On"
++ */
++ { 0x0d, 0xA1 },
++
++ /*
++ * 0E COMF "Common Control F"
++ * = 112 (0x70) 01110000
++ * COMF[7] "System clock selection"
++ * = 0 (0x00) 0....... "Use 24 MHz system clock"
++ * COMF[6:4] "Reserved"
++ * = 7 (0x07) .111....
++ * COMF[3] "Manual auto negative offset canceling selection"
++ * = 0 (0x00) ....0... "Auto detect negative
++ * offset and cancel it"
++ * COMF[2:0] "Reserved"
++ * = 0 (0x00) .....000
++ */
++ { 0x0e, 0x70 },
++
++ /*
++ * 0F COMG "Common Control G"
++ * = 66 (0x42) 01000010
++ * COMG[7] "Optical black output selection"
++ * = 0 (0x00) 0....... "Disable"
++ * COMG[6] "Black level calibrate selection"
++ * = 1 (0x01) .1...... "Use optical black pixels
++ * to calibrate"
++ * COMG[5:4] "Reserved"
++ * = 0 (0x00) ..00....
++ * COMG[3] "Channel offset adjustment"
++ * = 0 (0x00) ....0... "Disable offset adjustment"
++ * COMG[2] "ADC black level calibration option"
++ * = 0 (0x00) .....0.. "Use B/G line and G/R
++ * line to calibrate each
++ * channel's black level"
++ * COMG[1] "Reserved"
++ * = 1 (0x01) ......1.
++ * COMG[0] "ADC black level calibration enable"
++ * = 0 (0x00) .......0 "Disable"
++ */
++ { 0x0f, 0x42 },
++
++ /*
++ * 14 COMJ "Common Control J"
++ * = 198 (0xC6) 11000110
++ * COMJ[7:6] "AGC gain ceiling"
++ * = 3 (0x03) 11...... "8x"
++ * COMJ[5:4] "Reserved"
++ * = 0 (0x00) ..00....
++ * COMJ[3] "Auto banding filter"
++ * = 0 (0x00) ....0... "Banding filter is always
++ * on off depending on
++ * COMI[5] setting"
++ * COMJ[2] "VSYNC drop option"
++ * = 1 (0x01) .....1.. "SYNC is dropped if frame
++ * data is dropped"
++ * COMJ[1] "Frame data drop"
++ * = 1 (0x01) ......1. "Drop frame data if
++ * exposure is not within
++ * tolerance. In AEC mode,
++ * data is normally dropped
++ * when data is out of
++ * range."
++ * COMJ[0] "Reserved"
++ * = 0 (0x00) .......0
++ */
++ { 0x14, 0xC6 },
++
++ /*
++ * 15 COMK "Common Control K"
++ * = 2 (0x02) 00000010
++ * COMK[7] "CHSYNC pin output swap"
++ * = 0 (0x00) 0....... "CHSYNC"
++ * COMK[6] "HREF pin output swap"
++ * = 0 (0x00) .0...... "HREF"
++ * COMK[5] "PCLK output selection"
++ * = 0 (0x00) ..0..... "PCLK always output"
++ * COMK[4] "PCLK edge selection"
++ * = 0 (0x00) ...0.... "Data valid on falling edge"
++ * COMK[3] "HREF output polarity"
++ * = 0 (0x00) ....0... "positive"
++ * COMK[2] "Reserved"
++ * = 0 (0x00) .....0..
++ * COMK[1] "VSYNC polarity"
++ * = 1 (0x01) ......1. "negative"
++ * COMK[0] "HSYNC polarity"
++ * = 0 (0x00) .......0 "positive"
++ */
++ { 0x15, 0x02 },
++
++ /*
++ * 33 CHLF "Current Control"
++ * = 9 (0x09) 00001001
++ * CHLF[7:6] "Sensor current control"
++ * = 0 (0x00) 00......
++ * CHLF[5] "Sensor current range control"
++ * = 0 (0x00) ..0..... "normal range"
++ * CHLF[4] "Sensor current"
++ * = 0 (0x00) ...0.... "normal current"
++ * CHLF[3] "Sensor buffer current control"
++ * = 1 (0x01) ....1... "half current"
++ * CHLF[2] "Column buffer current control"
++ * = 0 (0x00) .....0.. "normal current"
++ * CHLF[1] "Analog DSP current control"
++ * = 0 (0x00) ......0. "normal current"
++ * CHLF[1] "ADC current control"
++ * = 0 (0x00) ......0. "normal current"
++ */
++ { 0x33, 0x09 },
++
++ /*
++ * 34 VBLM "Blooming Control"
++ * = 80 (0x50) 01010000
++ * VBLM[7] "Hard soft reset switch"
++ * = 0 (0x00) 0....... "Hard reset"
++ * VBLM[6:4] "Blooming voltage selection"
++ * = 5 (0x05) .101....
++ * VBLM[3:0] "Sensor current control"
++ * = 0 (0x00) ....0000
++ */
++ { 0x34, 0x50 },
++
++ /*
++ * 36 VCHG "Sensor Precharge Voltage Control"
++ * = 0 (0x00) 00000000
++ * VCHG[7] "Reserved"
++ * = 0 (0x00) 0.......
++ * VCHG[6:4] "Sensor precharge voltage control"
++ * = 0 (0x00) .000....
++ * VCHG[3:0] "Sensor array common reference"
++ * = 0 (0x00) ....0000
++ */
++ { 0x36, 0x00 },
++
++ /*
++ * 37 ADC "ADC Reference Control"
++ * = 4 (0x04) 00000100
++ * ADC[7:4] "Reserved"
++ * = 0 (0x00) 0000....
++ * ADC[3] "ADC input signal range"
++ * = 0 (0x00) ....0... "Input signal 1.0x"
++ * ADC[2:0] "ADC range control"
++ * = 4 (0x04) .....100
++ */
++ { 0x37, 0x04 },
++
++ /*
++ * 38 ACOM "Analog Common Ground"
++ * = 82 (0x52) 01010010
++ * ACOM[7] "Analog gain control"
++ * = 0 (0x00) 0....... "Gain 1x"
++ * ACOM[6] "Analog black level calibration"
++ * = 1 (0x01) .1...... "On"
++ * ACOM[5:0] "Reserved"
++ * = 18 (0x12) ..010010
++ */
++ { 0x38, 0x52 },
++
++ /*
++ * 3A FREFA "Internal Reference Adjustment"
++ * = 0 (0x00) 00000000
++ * FREFA[7:0] "Range"
++ * = 0 (0x00) 00000000
++ */
++ { 0x3a, 0x00 },
++
++ /*
++ * 3C FVOPT "Internal Reference Adjustment"
++ * = 31 (0x1F) 00011111
++ * FVOPT[7:0] "Range"
++ * = 31 (0x1F) 00011111
++ */
++ { 0x3c, 0x1F },
++
++ /*
++ * 44 Undocumented = 0 (0x00) 00000000
++ * 44[7:0] "It's a secret"
++ * = 0 (0x00) 00000000
++ */
++ { 0x44, 0x00 },
++
++ /*
++ * 40 Undocumented = 0 (0x00) 00000000
++ * 40[7:0] "It's a secret"
++ * = 0 (0x00) 00000000
++ */
++ { 0x40, 0x00 },
++
++ /*
++ * 41 Undocumented = 0 (0x00) 00000000
++ * 41[7:0] "It's a secret"
++ * = 0 (0x00) 00000000
++ */
++ { 0x41, 0x00 },
++
++ /*
++ * 42 Undocumented = 0 (0x00) 00000000
++ * 42[7:0] "It's a secret"
++ * = 0 (0x00) 00000000
++ */
++ { 0x42, 0x00 },
++
++ /*
++ * 43 Undocumented = 0 (0x00) 00000000
++ * 43[7:0] "It's a secret"
++ * = 0 (0x00) 00000000
++ */
++ { 0x43, 0x00 },
++
++ /*
++ * 45 Undocumented = 128 (0x80) 10000000
++ * 45[7:0] "It's a secret"
++ * = 128 (0x80) 10000000
++ */
++ { 0x45, 0x80 },
++
++ /*
++ * 48 Undocumented = 192 (0xC0) 11000000
++ * 48[7:0] "It's a secret"
++ * = 192 (0xC0) 11000000
++ */
++ { 0x48, 0xC0 },
++
++ /*
++ * 49 Undocumented = 25 (0x19) 00011001
++ * 49[7:0] "It's a secret"
++ * = 25 (0x19) 00011001
++ */
++ { 0x49, 0x19 },
++
++ /*
++ * 4B Undocumented = 128 (0x80) 10000000
++ * 4B[7:0] "It's a secret"
++ * = 128 (0x80) 10000000
++ */
++ { 0x4B, 0x80 },
++
++ /*
++ * 4D Undocumented = 196 (0xC4) 11000100
++ * 4D[7:0] "It's a secret"
++ * = 196 (0xC4) 11000100
++ */
++ { 0x4D, 0xC4 },
++
++ /*
++ * 35 VREF "Reference Voltage Control"
++ * = 76 (0x4C) 01001100
++ * VREF[7:5] "Column high reference control"
++ * = 2 (0x02) 010..... "higher voltage"
++ * VREF[4:2] "Column low reference control"
++ * = 3 (0x03) ...011.. "Highest voltage"
++ * VREF[1:0] "Reserved"
++ * = 0 (0x00) ......00
++ */
++ { 0x35, 0x4C },
++
++ /*
++ * 3D Undocumented = 0 (0x00) 00000000
++ * 3D[7:0] "It's a secret"
++ * = 0 (0x00) 00000000
++ */
++ { 0x3D, 0x00 },
++
++ /*
++ * 3E Undocumented = 0 (0x00) 00000000
++ * 3E[7:0] "It's a secret"
++ * = 0 (0x00) 00000000
++ */
++ { 0x3E, 0x00 },
++
++ /*
++ * 3B FREFB "Internal Reference Adjustment"
++ * = 24 (0x18) 00011000
++ * FREFB[7:0] "Range"
++ * = 24 (0x18) 00011000
++ */
++ { 0x3b, 0x18 },
++
++ /*
++ * 33 CHLF "Current Control"
++ * = 25 (0x19) 00011001
++ * CHLF[7:6] "Sensor current control"
++ * = 0 (0x00) 00......
++ * CHLF[5] "Sensor current range control"
++ * = 0 (0x00) ..0..... "normal range"
++ * CHLF[4] "Sensor current"
++ * = 1 (0x01) ...1.... "double current"
++ * CHLF[3] "Sensor buffer current control"
++ * = 1 (0x01) ....1... "half current"
++ * CHLF[2] "Column buffer current control"
++ * = 0 (0x00) .....0.. "normal current"
++ * CHLF[1] "Analog DSP current control"
++ * = 0 (0x00) ......0. "normal current"
++ * CHLF[1] "ADC current control"
++ * = 0 (0x00) ......0. "normal current"
++ */
++ { 0x33, 0x19 },
++
++ /*
++ * 34 VBLM "Blooming Control"
++ * = 90 (0x5A) 01011010
++ * VBLM[7] "Hard soft reset switch"
++ * = 0 (0x00) 0....... "Hard reset"
++ * VBLM[6:4] "Blooming voltage selection"
++ * = 5 (0x05) .101....
++ * VBLM[3:0] "Sensor current control"
++ * = 10 (0x0A) ....1010
++ */
++ { 0x34, 0x5A },
++
++ /*
++ * 3B FREFB "Internal Reference Adjustment"
++ * = 0 (0x00) 00000000
++ * FREFB[7:0] "Range"
++ * = 0 (0x00) 00000000
++ */
++ { 0x3b, 0x00 },
++
++ /*
++ * 33 CHLF "Current Control"
++ * = 9 (0x09) 00001001
++ * CHLF[7:6] "Sensor current control"
++ * = 0 (0x00) 00......
++ * CHLF[5] "Sensor current range control"
++ * = 0 (0x00) ..0..... "normal range"
++ * CHLF[4] "Sensor current"
++ * = 0 (0x00) ...0.... "normal current"
++ * CHLF[3] "Sensor buffer current control"
++ * = 1 (0x01) ....1... "half current"
++ * CHLF[2] "Column buffer current control"
++ * = 0 (0x00) .....0.. "normal current"
++ * CHLF[1] "Analog DSP current control"
++ * = 0 (0x00) ......0. "normal current"
++ * CHLF[1] "ADC current control"
++ * = 0 (0x00) ......0. "normal current"
++ */
++ { 0x33, 0x09 },
++
++ /*
++ * 34 VBLM "Blooming Control"
++ * = 80 (0x50) 01010000
++ * VBLM[7] "Hard soft reset switch"
++ * = 0 (0x00) 0....... "Hard reset"
++ * VBLM[6:4] "Blooming voltage selection"
++ * = 5 (0x05) .101....
++ * VBLM[3:0] "Sensor current control"
++ * = 0 (0x00) ....0000
++ */
++ { 0x34, 0x50 },
++
++ /*
++ * 12 COMH "Common Control H"
++ * = 64 (0x40) 01000000
++ * COMH[7] "SRST"
++ * = 0 (0x00) 0....... "No-op"
++ * COMH[6:4] "Resolution selection"
++ * = 4 (0x04) .100.... "XGA"
++ * COMH[3] "Master slave selection"
++ * = 0 (0x00) ....0... "Master mode"
++ * COMH[2] "Internal B/R channel option"
++ * = 0 (0x00) .....0.. "B/R use same channel"
++ * COMH[1] "Color bar test pattern"
++ * = 0 (0x00) ......0. "Off"
++ * COMH[0] "Reserved"
++ * = 0 (0x00) .......0
++ */
++ { 0x12, 0x40 },
++
++ /*
++ * 17 HREFST "Horizontal window start"
++ * = 31 (0x1F) 00011111
++ * HREFST[7:0] "Horizontal window start, 8 MSBs"
++ * = 31 (0x1F) 00011111
++ */
++ { 0x17, 0x1F },
++
++ /*
++ * 18 HREFEND "Horizontal window end"
++ * = 95 (0x5F) 01011111
++ * HREFEND[7:0] "Horizontal Window End, 8 MSBs"
++ * = 95 (0x5F) 01011111
++ */
++ { 0x18, 0x5F },
++
++ /*
++ * 19 VSTRT "Vertical window start"
++ * = 0 (0x00) 00000000
++ * VSTRT[7:0] "Vertical Window Start, 8 MSBs"
++ * = 0 (0x00) 00000000
++ */
++ { 0x19, 0x00 },
++
++ /*
++ * 1A VEND "Vertical window end"
++ * = 96 (0x60) 01100000
++ * VEND[7:0] "Vertical Window End, 8 MSBs"
++ * = 96 (0x60) 01100000
++ */
++ { 0x1a, 0x60 },
++
++ /*
++ * 32 COMM "Common Control M"
++ * = 18 (0x12) 00010010
++ * COMM[7:6] "Pixel clock divide option"
++ * = 0 (0x00) 00...... "/1"
++ * COMM[5:3] "Horizontal window end position, 3 LSBs"
++ * = 2 (0x02) ..010...
++ * COMM[2:0] "Horizontal window start position, 3 LSBs"
++ * = 2 (0x02) .....010
++ */
++ { 0x32, 0x12 },
++
++ /*
++ * 03 COMA "Common Control A"
++ * = 74 (0x4A) 01001010
++ * COMA[7:4] "AWB Update Threshold"
++ * = 4 (0x04) 0100....
++ * COMA[3:2] "Vertical window end line control 2 LSBs"
++ * = 2 (0x02) ....10..
++ * COMA[1:0] "Vertical window start line control 2 LSBs"
++ * = 2 (0x02) ......10
++ */
++ { 0x03, 0x4A },
++
++ /*
++ * 11 CLKRC "Clock Rate Control"
++ * = 128 (0x80) 10000000
++ * CLKRC[7] "Internal frequency doublers on off seclection"
++ * = 1 (0x01) 1....... "On"
++ * CLKRC[6] "Digital video master slave selection"
++ * = 0 (0x00) .0...... "Master mode, sensor
++ * provides PCLK"
++ * CLKRC[5:0] "Clock divider { CLK = PCLK/(1+CLKRC[5:0]) }"
++ * = 0 (0x00) ..000000
++ */
++ { 0x11, 0x80 },
++
++ /*
++ * 12 COMH "Common Control H"
++ * = 0 (0x00) 00000000
++ * COMH[7] "SRST"
++ * = 0 (0x00) 0....... "No-op"
++ * COMH[6:4] "Resolution selection"
++ * = 0 (0x00) .000.... "QXGA"
++ * COMH[3] "Master slave selection"
++ * = 0 (0x00) ....0... "Master mode"
++ * COMH[2] "Internal B/R channel option"
++ * = 0 (0x00) .....0.. "B/R use same channel"
++ * COMH[1] "Color bar test pattern"
++ * = 0 (0x00) ......0. "Off"
++ * COMH[0] "Reserved"
++ * = 0 (0x00) .......0
++ */
++ { 0x12, 0x00 },
++
++ /*
++ * 12 COMH "Common Control H"
++ * = 64 (0x40) 01000000
++ * COMH[7] "SRST"
++ * = 0 (0x00) 0....... "No-op"
++ * COMH[6:4] "Resolution selection"
++ * = 4 (0x04) .100.... "XGA"
++ * COMH[3] "Master slave selection"
++ * = 0 (0x00) ....0... "Master mode"
++ * COMH[2] "Internal B/R channel option"
++ * = 0 (0x00) .....0.. "B/R use same channel"
++ * COMH[1] "Color bar test pattern"
++ * = 0 (0x00) ......0. "Off"
++ * COMH[0] "Reserved"
++ * = 0 (0x00) .......0
++ */
++ { 0x12, 0x40 },
++
++ /*
++ * 17 HREFST "Horizontal window start"
++ * = 31 (0x1F) 00011111
++ * HREFST[7:0] "Horizontal window start, 8 MSBs"
++ * = 31 (0x1F) 00011111
++ */
++ { 0x17, 0x1F },
++
++ /*
++ * 18 HREFEND "Horizontal window end"
++ * = 95 (0x5F) 01011111
++ * HREFEND[7:0] "Horizontal Window End, 8 MSBs"
++ * = 95 (0x5F) 01011111
++ */
++ { 0x18, 0x5F },
++
++ /*
++ * 19 VSTRT "Vertical window start"
++ * = 0 (0x00) 00000000
++ * VSTRT[7:0] "Vertical Window Start, 8 MSBs"
++ * = 0 (0x00) 00000000
++ */
++ { 0x19, 0x00 },
++
++ /*
++ * 1A VEND "Vertical window end"
++ * = 96 (0x60) 01100000
++ * VEND[7:0] "Vertical Window End, 8 MSBs"
++ * = 96 (0x60) 01100000
++ */
++ { 0x1a, 0x60 },
++
++ /*
++ * 32 COMM "Common Control M"
++ * = 18 (0x12) 00010010
++ * COMM[7:6] "Pixel clock divide option"
++ * = 0 (0x00) 00...... "/1"
++ * COMM[5:3] "Horizontal window end position, 3 LSBs"
++ * = 2 (0x02) ..010...
++ * COMM[2:0] "Horizontal window start position, 3 LSBs"
++ * = 2 (0x02) .....010
++ */
++ { 0x32, 0x12 },
++
++ /*
++ * 03 COMA "Common Control A"
++ * = 74 (0x4A) 01001010
++ * COMA[7:4] "AWB Update Threshold"
++ * = 4 (0x04) 0100....
++ * COMA[3:2] "Vertical window end line control 2 LSBs"
++ * = 2 (0x02) ....10..
++ * COMA[1:0] "Vertical window start line control 2 LSBs"
++ * = 2 (0x02) ......10
++ */
++ { 0x03, 0x4A },
++
++ /*
++ * 02 RED "Red Gain Control"
++ * = 175 (0xAF) 10101111
++ * RED[7] "Action"
++ * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))"
++ * RED[6:0] "Value"
++ * = 47 (0x2F) .0101111
++ */
++ { 0x02, 0xAF },
++
++ /*
++ * 2D ADDVSL "VSYNC Pulse Width"
++ * = 210 (0xD2) 11010010
++ * ADDVSL[7:0] "VSYNC pulse width, LSB"
++ * = 210 (0xD2) 11010010
++ */
++ { 0x2d, 0xD2 },
++
++ /*
++ * 00 GAIN = 24 (0x18) 00011000
++ * GAIN[7:6] "Reserved"
++ * = 0 (0x00) 00......
++ * GAIN[5] "Double"
++ * = 0 (0x00) ..0..... "False"
++ * GAIN[4] "Double"
++ * = 1 (0x01) ...1.... "True"
++ * GAIN[3:0] "Range"
++ * = 8 (0x08) ....1000
++ */
++ { 0x00, 0x18 },
++
++ /*
++ * 01 BLUE "Blue Gain Control"
++ * = 240 (0xF0) 11110000
++ * BLUE[7] "Action"
++ * = 1 (0x01) 1....... "gain = 1/(1+bitrev([6:0]))"
++ * BLUE[6:0] "Value"
++ * = 112 (0x70) .1110000
++ */
++ { 0x01, 0xF0 },
++
++ /*
++ * 10 AEC "Automatic Exposure Control"
++ * = 10 (0x0A) 00001010
++ * AEC[7:0] "Automatic Exposure Control, 8 MSBs"
++ * = 10 (0x0A) 00001010
++ */
++ { 0x10, 0x0A },
++
++ { 0xE1, 0x67 },
++ { 0xE3, 0x03 },
++ { 0xE4, 0x26 },
++ { 0xE5, 0x3E },
++ { 0xF8, 0x01 },
++ { 0xFF, 0x01 },
++};
++
+ static const struct ov_i2c_regvals norm_6x20[] = {
+ { 0x12, 0x80 }, /* reset */
+ { 0x11, 0x01 },
+@@ -678,6 +1493,7 @@ static const struct ov_i2c_regvals norm_7610[] = {
+ };
+
+ static const struct ov_i2c_regvals norm_7620[] = {
++ { 0x12, 0x80 }, /* reset */
+ { 0x00, 0x00 }, /* gain */
+ { 0x01, 0x80 }, /* blue gain */
+ { 0x02, 0x80 }, /* red gain */
+@@ -1042,10 +1858,28 @@ static unsigned char ov7670_abs_to_sm(unsigned char v)
+ }
+
+ /* Write a OV519 register */
+-static int reg_w(struct sd *sd, __u16 index, __u8 value)
++static int reg_w(struct sd *sd, __u16 index, __u16 value)
+ {
+- int ret;
+- int req = (sd->bridge <= BRIDGE_OV511PLUS) ? 2 : 1;
++ int ret, req = 0;
++
++ switch (sd->bridge) {
++ case BRIDGE_OV511:
++ case BRIDGE_OV511PLUS:
++ req = 2;
++ break;
++ case BRIDGE_OVFX2:
++ req = 0x0a;
++ /* fall through */
++ case BRIDGE_W9968CF:
++ ret = usb_control_msg(sd->gspca_dev.dev,
++ usb_sndctrlpipe(sd->gspca_dev.dev, 0),
++ req,
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ value, index, NULL, 0, 500);
++ goto leave;
++ default:
++ req = 1;
++ }
+
+ sd->gspca_dev.usb_buf[0] = value;
+ ret = usb_control_msg(sd->gspca_dev.dev,
+@@ -1054,17 +1888,35 @@ static int reg_w(struct sd *sd, __u16 index, __u8 value)
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index,
+ sd->gspca_dev.usb_buf, 1, 500);
+- if (ret < 0)
+- PDEBUG(D_ERR, "Write reg [%02x] %02x failed", index, value);
+- return ret;
++leave:
++ if (ret < 0) {
++ PDEBUG(D_ERR, "Write reg 0x%04x -> [0x%02x] failed",
++ value, index);
++ return ret;
++ }
++
++ PDEBUG(D_USBO, "Write reg 0x%04x -> [0x%02x]", value, index);
++ return 0;
+ }
+
+-/* Read from a OV519 register */
++/* Read from a OV519 register, note not valid for the w9968cf!! */
+ /* returns: negative is error, pos or zero is data */
+ static int reg_r(struct sd *sd, __u16 index)
+ {
+ int ret;
+- int req = (sd->bridge <= BRIDGE_OV511PLUS) ? 3 : 1;
++ int req;
++
++ switch (sd->bridge) {
++ case BRIDGE_OV511:
++ case BRIDGE_OV511PLUS:
++ req = 3;
++ break;
++ case BRIDGE_OVFX2:
++ req = 0x0b;
++ break;
++ default:
++ req = 1;
++ }
+
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
+@@ -1072,10 +1924,12 @@ static int reg_r(struct sd *sd, __u16 index)
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, sd->gspca_dev.usb_buf, 1, 500);
+
+- if (ret >= 0)
++ if (ret >= 0) {
+ ret = sd->gspca_dev.usb_buf[0];
+- else
++ PDEBUG(D_USBI, "Read reg [0x%02X] -> 0x%04X", index, ret);
++ } else
+ PDEBUG(D_ERR, "Read reg [0x%02x] failed", index);
++
+ return ret;
+ }
+
+@@ -1095,6 +1949,7 @@ static int reg_r8(struct sd *sd,
+ ret = sd->gspca_dev.usb_buf[0];
+ else
+ PDEBUG(D_ERR, "Read reg 8 [0x%02x] failed", index);
++
+ return ret;
+ }
+
+@@ -1132,7 +1987,7 @@ static int ov518_reg_w32(struct sd *sd, __u16 index, u32 value, int n)
+ {
+ int ret;
+
+- *((u32 *)sd->gspca_dev.usb_buf) = __cpu_to_le32(value);
++ *((__le32 *) sd->gspca_dev.usb_buf) = __cpu_to_le32(value);
+
+ ret = usb_control_msg(sd->gspca_dev.dev,
+ usb_sndctrlpipe(sd->gspca_dev.dev, 0),
+@@ -1140,9 +1995,12 @@ static int ov518_reg_w32(struct sd *sd, __u16 index, u32 value, int n)
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index,
+ sd->gspca_dev.usb_buf, n, 500);
+- if (ret < 0)
++ if (ret < 0) {
+ PDEBUG(D_ERR, "Write reg32 [%02x] %08x failed", index, value);
+- return ret;
++ return ret;
++ }
++
++ return 0;
+ }
+
+ static int ov511_i2c_w(struct sd *sd, __u8 reg, __u8 value)
+@@ -1168,15 +2026,19 @@ static int ov511_i2c_w(struct sd *sd, __u8 reg, __u8 value)
+ if (rc < 0)
+ return rc;
+
+- do
++ do {
+ rc = reg_r(sd, R511_I2C_CTL);
+- while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
++ } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
+
+ if (rc < 0)
+ return rc;
+
+ if ((rc & 2) == 0) /* Ack? */
+ break;
++#if 0
++ /* I2C abort */
++ reg_w(sd, R511_I2C_CTL, 0x10);
++#endif
+ if (--retries < 0) {
+ PDEBUG(D_USBO, "i2c write retries exhausted");
+ return -1;
+@@ -1202,9 +2064,9 @@ static int ov511_i2c_r(struct sd *sd, __u8 reg)
+ if (rc < 0)
+ return rc;
+
+- do
++ do {
+ rc = reg_r(sd, R511_I2C_CTL);
+- while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
++ } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
+
+ if (rc < 0)
+ return rc;
+@@ -1228,9 +2090,9 @@ static int ov511_i2c_r(struct sd *sd, __u8 reg)
+ if (rc < 0)
+ return rc;
+
+- do
++ do {
+ rc = reg_r(sd, R511_I2C_CTL);
+- while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
++ } while (rc > 0 && ((rc & 1) == 0)); /* Retry until idle */
+
+ if (rc < 0)
+ return rc;
+@@ -1324,32 +2186,110 @@ static int ov518_i2c_r(struct sd *sd, __u8 reg)
+ return value;
+ }
+
++static int ovfx2_i2c_w(struct sd *sd, __u8 reg, __u8 value)
++{
++ int ret;
++
++ ret = usb_control_msg(sd->gspca_dev.dev,
++ usb_sndctrlpipe(sd->gspca_dev.dev, 0),
++ 0x02,
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ (__u16)value, (__u16)reg, NULL, 0, 500);
++
++ if (ret < 0) {
++ PDEBUG(D_ERR, "i2c 0x%02x -> [0x%02x] failed", value, reg);
++ return ret;
++ }
++
++ PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg);
++ return 0;
++}
++
++static int ovfx2_i2c_r(struct sd *sd, __u8 reg)
++{
++ int ret;
++
++ ret = usb_control_msg(sd->gspca_dev.dev,
++ usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
++ 0x03,
++ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ 0, (__u16)reg, sd->gspca_dev.usb_buf, 1, 500);
++
++ if (ret >= 0) {
++ ret = sd->gspca_dev.usb_buf[0];
++ PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, ret);
++ } else
++ PDEBUG(D_ERR, "i2c read [0x%02x] failed", reg);
++
++ return ret;
++}
++
+ static int i2c_w(struct sd *sd, __u8 reg, __u8 value)
+ {
++ int ret = -1;
++
++ if (sd->sensor_reg_cache[reg] == value)
++ return 0;
++
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+- return ov511_i2c_w(sd, reg, value);
++ ret = ov511_i2c_w(sd, reg, value);
++ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ case BRIDGE_OV519:
+- return ov518_i2c_w(sd, reg, value);
++ ret = ov518_i2c_w(sd, reg, value);
++ break;
++ case BRIDGE_OVFX2:
++ ret = ovfx2_i2c_w(sd, reg, value);
++ break;
++ case BRIDGE_W9968CF:
++ ret = w9968cf_i2c_w(sd, reg, value);
++ break;
++ }
++
++ if (ret >= 0) {
++ /* Up on sensor reset empty the register cache */
++ if (reg == 0x12 && (value & 0x80))
++ memset(sd->sensor_reg_cache, -1,
++ sizeof(sd->sensor_reg_cache));
++ else
++ sd->sensor_reg_cache[reg] = value;
+ }
+- return -1; /* Should never happen */
++
++ return ret;
+ }
+
+ static int i2c_r(struct sd *sd, __u8 reg)
+ {
++ int ret = -1;
++
++ if (sd->sensor_reg_cache[reg] != -1)
++ return sd->sensor_reg_cache[reg];
++
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+- return ov511_i2c_r(sd, reg);
++ ret = ov511_i2c_r(sd, reg);
++ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+ case BRIDGE_OV519:
+- return ov518_i2c_r(sd, reg);
++ ret = ov518_i2c_r(sd, reg);
++ break;
++ case BRIDGE_OVFX2:
++ ret = ovfx2_i2c_r(sd, reg);
++ break;
++ case BRIDGE_W9968CF:
++ ret = w9968cf_i2c_r(sd, reg);
++ break;
+ }
+- return -1; /* Should never happen */
++
++ if (ret >= 0)
++ sd->sensor_reg_cache[reg] = ret;
++
++ return ret;
+ }
+
+ /* Writes bits at positions specified by mask to an I2C reg. Bits that are in
+@@ -1389,6 +2329,10 @@ static inline int ov51x_stop(struct sd *sd)
+ return reg_w_mask(sd, R51x_SYS_RESET, 0x3a, 0x3a);
+ case BRIDGE_OV519:
+ return reg_w(sd, OV519_SYS_RESET1, 0x0f);
++ case BRIDGE_OVFX2:
++ return reg_w_mask(sd, 0x0f, 0x00, 0x02);
++ case BRIDGE_W9968CF:
++ return reg_w(sd, 0x3c, 0x0a05); /* stop USB transfer */
+ }
+
+ return 0;
+@@ -1418,18 +2362,27 @@ static inline int ov51x_restart(struct sd *sd)
+ return reg_w(sd, R51x_SYS_RESET, 0x00);
+ case BRIDGE_OV519:
+ return reg_w(sd, OV519_SYS_RESET1, 0x00);
++ case BRIDGE_OVFX2:
++ return reg_w_mask(sd, 0x0f, 0x02, 0x02);
++ case BRIDGE_W9968CF:
++ return reg_w(sd, 0x3c, 0x8a05); /* USB FIFO enable */
+ }
+
+ return 0;
+ }
+
++static int ov51x_set_slave_ids(struct sd *sd, __u8 slave);
++
+ /* This does an initial reset of an OmniVision sensor and ensures that I2C
+ * is synchronized. Returns <0 on failure.
+ */
+-static int init_ov_sensor(struct sd *sd)
++static int init_ov_sensor(struct sd *sd, __u8 slave)
+ {
+ int i;
+
++ if (ov51x_set_slave_ids(sd, slave) < 0)
++ return -EIO;
++
+ /* Reset the sensor */
+ if (i2c_w(sd, 0x12, 0x80) < 0)
+ return -EIO;
+@@ -1466,6 +2419,14 @@ static int ov51x_set_slave_ids(struct sd *sd,
+ {
+ int rc;
+
++ switch (sd->bridge) {
++ case BRIDGE_OVFX2:
++ return reg_w(sd, OVFX2_I2C_ADDR, slave);
++ case BRIDGE_W9968CF:
++ sd->sensor_addr = slave;
++ return 0;
++ }
++
+ rc = reg_w(sd, R51x_I2C_W_SID, slave);
+ if (rc < 0)
+ return rc;
+@@ -1508,6 +2469,39 @@ static int write_i2c_regvals(struct sd *sd,
+ *
+ ***************************************************************************/
+
++/* This initializes the OV2x10 / OV3610 / OV3620 */
++static int ov_hires_configure(struct sd *sd)
++{
++ int high, low;
++
++ if (sd->bridge != BRIDGE_OVFX2) {
++ PDEBUG(D_ERR, "error hires sensors only supported with ovfx2");
++ return -1;
++ }
++
++ PDEBUG(D_PROBE, "starting ov hires configuration");
++
++ /* Detect sensor (sub)type */
++ high = i2c_r(sd, 0x0a);
++ low = i2c_r(sd, 0x0b);
++ /* info("%x, %x", high, low); */
++ if (high == 0x96 && low == 0x40) {
++ PDEBUG(D_PROBE, "Sensor is an OV2610");
++ sd->sensor = SEN_OV2610;
++ } else if (high == 0x36 && (low & 0x0f) == 0x00) {
++ PDEBUG(D_PROBE, "Sensor is an OV3610");
++ sd->sensor = SEN_OV3610;
++ } else {
++ PDEBUG(D_ERR, "Error unknown sensor type: 0x%02x%02x",
++ high, low);
++ return -1;
++ }
++
++ /* Set sensor-specific vars */
++ return 0;
++}
++
++
+ /* This initializes the OV8110, OV8610 sensor. The OV8110 uses
+ * the same register settings as the OV8610, since they are very similar.
+ */
+@@ -1569,7 +2563,7 @@ static int ov7xx0_configure(struct sd *sd)
+ /* I don't know what's different about the 76BE yet. */
+ if (i2c_r(sd, 0x15) & 1) {
+ PDEBUG(D_PROBE, "Sensor is an OV7620AE");
+- sd->sensor = SEN_OV7620;
++ sd->sensor = SEN_OV7620AE;
+ } else {
+ PDEBUG(D_PROBE, "Sensor is an OV76BE");
+ sd->sensor = SEN_OV76BE;
+@@ -1603,7 +2597,7 @@ static int ov7xx0_configure(struct sd *sd)
+ break;
+ case 0x48:
+ PDEBUG(D_PROBE, "Sensor is an OV7648");
+- sd->sensor = SEN_OV7640; /* FIXME */
++ sd->sensor = SEN_OV7648;
+ break;
+ default:
+ PDEBUG(D_PROBE, "Unknown sensor: 0x76%x", low);
+@@ -1966,12 +2960,29 @@ static int ov519_configure(struct sd *sd)
+ return write_regvals(sd, init_519, ARRAY_SIZE(init_519));
+ }
+
++static int ovfx2_configure(struct sd *sd)
++{
++ static const struct ov_regvals init_fx2[] = {
++ { 0x00, 0x60 },
++ { 0x02, 0x01 },
++ { 0x0f, 0x1d },
++ { 0xe9, 0x82 },
++ { 0xea, 0xc7 },
++ { 0xeb, 0x10 },
++ { 0xec, 0xf6 },
++ };
++
++ sd->stopped = 1;
++
++ return write_regvals(sd, init_fx2, ARRAY_SIZE(init_fx2));
++}
++
+ /* this function is called at probe time */
+ static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- struct cam *cam;
++ struct cam *cam = &gspca_dev->cam;
+ int ret = 0;
+
+ sd->bridge = id->driver_info & BRIDGE_MASK;
+@@ -1989,6 +3000,16 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ case BRIDGE_OV519:
+ ret = ov519_configure(sd);
+ break;
++ case BRIDGE_OVFX2:
++ ret = ovfx2_configure(sd);
++ cam->bulk_size = OVFX2_BULK_SIZE;
++ cam->bulk_nurbs = MAX_NURBS;
++ cam->bulk = 1;
++ break;
++ case BRIDGE_W9968CF:
++ ret = w9968cf_configure(sd);
++ cam->reverse_alts = 1;
++ break;
+ }
+
+ if (ret)
+@@ -1996,49 +3017,39 @@ static int sd_config(struct gspca_dev *gspca_dev,
+
+ ov51x_led_control(sd, 0); /* turn LED off */
+
+- /* Test for 76xx */
+- if (ov51x_set_slave_ids(sd, OV7xx0_SID) < 0)
+- goto error;
+-
+ /* The OV519 must be more aggressive about sensor detection since
+ * I2C write will never fail if the sensor is not present. We have
+ * to try to initialize the sensor to detect its presence */
+- if (init_ov_sensor(sd) >= 0) {
++
++ /* Test for 76xx */
++ if (init_ov_sensor(sd, OV7xx0_SID) >= 0) {
+ if (ov7xx0_configure(sd) < 0) {
+ PDEBUG(D_ERR, "Failed to configure OV7xx0");
+ goto error;
+ }
+- } else {
+-
+- /* Test for 6xx0 */
+- if (ov51x_set_slave_ids(sd, OV6xx0_SID) < 0)
++ /* Test for 6xx0 */
++ } else if (init_ov_sensor(sd, OV6xx0_SID) >= 0) {
++ if (ov6xx0_configure(sd) < 0) {
++ PDEBUG(D_ERR, "Failed to configure OV6xx0");
++ goto error;
++ }
++ /* Test for 8xx0 */
++ } else if (init_ov_sensor(sd, OV8xx0_SID) >= 0) {
++ if (ov8xx0_configure(sd) < 0) {
++ PDEBUG(D_ERR, "Failed to configure OV8xx0");
++ goto error;
++ }
++ /* Test for 3xxx / 2xxx */
++ } else if (init_ov_sensor(sd, OV_HIRES_SID) >= 0) {
++ if (ov_hires_configure(sd) < 0) {
++ PDEBUG(D_ERR, "Failed to configure high res OV");
+ goto error;
+-
+- if (init_ov_sensor(sd) >= 0) {
+- if (ov6xx0_configure(sd) < 0) {
+- PDEBUG(D_ERR, "Failed to configure OV6xx0");
+- goto error;
+- }
+- } else {
+-
+- /* Test for 8xx0 */
+- if (ov51x_set_slave_ids(sd, OV8xx0_SID) < 0)
+- goto error;
+-
+- if (init_ov_sensor(sd) < 0) {
+- PDEBUG(D_ERR,
+- "Can't determine sensor slave IDs");
+- goto error;
+- }
+- if (ov8xx0_configure(sd) < 0) {
+- PDEBUG(D_ERR,
+- "Failed to configure OV8xx0 sensor");
+- goto error;
+- }
+ }
++ } else {
++ PDEBUG(D_ERR, "Can't determine sensor slave IDs");
++ goto error;
+ }
+
+- cam = &gspca_dev->cam;
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+@@ -2069,6 +3080,31 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ cam->nmodes = ARRAY_SIZE(ov519_sif_mode);
+ }
+ break;
++ case BRIDGE_OVFX2:
++ if (sd->sensor == SEN_OV2610) {
++ cam->cam_mode = ovfx2_ov2610_mode;
++ cam->nmodes = ARRAY_SIZE(ovfx2_ov2610_mode);
++ } else if (sd->sensor == SEN_OV3610) {
++ cam->cam_mode = ovfx2_ov3610_mode;
++ cam->nmodes = ARRAY_SIZE(ovfx2_ov3610_mode);
++ } else if (!sd->sif) {
++ cam->cam_mode = ov519_vga_mode;
++ cam->nmodes = ARRAY_SIZE(ov519_vga_mode);
++ } else {
++ cam->cam_mode = ov519_sif_mode;
++ cam->nmodes = ARRAY_SIZE(ov519_sif_mode);
++ }
++ break;
++ case BRIDGE_W9968CF:
++ cam->cam_mode = w9968cf_vga_mode;
++ cam->nmodes = ARRAY_SIZE(w9968cf_vga_mode);
++ if (sd->sif)
++ cam->nmodes--;
++
++ /* w9968cf needs initialisation once the sensor is known */
++ if (w9968cf_init(sd) < 0)
++ goto error;
++ break;
+ }
+ sd->brightness = BRIGHTNESS_DEF;
+ if (sd->sensor == SEN_OV6630 || sd->sensor == SEN_OV66308AF)
+@@ -2087,11 +3123,19 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ gspca_dev->ctrl_dis = (1 << HFLIP_IDX) | (1 << VFLIP_IDX) |
+ (1 << OV7670_FREQ_IDX);
+ }
+- if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670)
++ sd->quality = QUALITY_DEF;
++ if (sd->sensor == SEN_OV7640 ||
++ sd->sensor == SEN_OV7648)
++ gspca_dev->ctrl_dis |= (1 << AUTOBRIGHT_IDX) |
++ (1 << CONTRAST_IDX);
++ if (sd->sensor == SEN_OV7670)
+ gspca_dev->ctrl_dis |= 1 << AUTOBRIGHT_IDX;
+ /* OV8610 Frequency filter control should work but needs testing */
+ if (sd->sensor == SEN_OV8610)
+ gspca_dev->ctrl_dis |= 1 << FREQ_IDX;
++ /* No controls for the OV2610/OV3610 */
++ if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610)
++ gspca_dev->ctrl_dis |= 0xFF;
+
+ return 0;
+ error:
+@@ -2106,6 +3150,20 @@ static int sd_init(struct gspca_dev *gspca_dev)
+
+ /* initialize the sensor */
+ switch (sd->sensor) {
++ case SEN_OV2610:
++ if (write_i2c_regvals(sd, norm_2610, ARRAY_SIZE(norm_2610)))
++ return -EIO;
++ /* Enable autogain, autoexpo, awb, bandfilter */
++ if (i2c_w_mask(sd, 0x13, 0x27, 0x27) < 0)
++ return -EIO;
++ break;
++ case SEN_OV3610:
++ if (write_i2c_regvals(sd, norm_3620b, ARRAY_SIZE(norm_3620b)))
++ return -EIO;
++ /* Enable autogain, autoexpo, awb, bandfilter */
++ if (i2c_w_mask(sd, 0x13, 0x27, 0x27) < 0)
++ return -EIO;
++ break;
+ case SEN_OV6620:
+ if (write_i2c_regvals(sd, norm_6x20, ARRAY_SIZE(norm_6x20)))
+ return -EIO;
+@@ -2124,10 +3182,12 @@ static int sd_init(struct gspca_dev *gspca_dev)
+ return -EIO;
+ break;
+ case SEN_OV7620:
++ case SEN_OV7620AE:
+ if (write_i2c_regvals(sd, norm_7620, ARRAY_SIZE(norm_7620)))
+ return -EIO;
+ break;
+ case SEN_OV7640:
++ case SEN_OV7648:
+ if (write_i2c_regvals(sd, norm_7640, ARRAY_SIZE(norm_7640)))
+ return -EIO;
+ break;
+@@ -2201,7 +3261,9 @@ static int ov511_mode_init_regs(struct sd *sd)
+ /* Note once the FIXME's in mode_init_ov_sensor_regs() are fixed
+ for more sensors we need to do this for them too */
+ case SEN_OV7620:
++ case SEN_OV7620AE:
+ case SEN_OV7640:
++ case SEN_OV7648:
+ case SEN_OV76BE:
+ if (sd->gspca_dev.width == 320)
+ interlaced = 1;
+@@ -2332,7 +3394,7 @@ static int ov518_mode_init_regs(struct sd *sd)
+
+ if (sd->bridge == BRIDGE_OV518PLUS) {
+ switch (sd->sensor) {
+- case SEN_OV7620:
++ case SEN_OV7620AE:
+ if (sd->gspca_dev.width == 320) {
+ reg_w(sd, 0x20, 0x00);
+ reg_w(sd, 0x21, 0x19);
+@@ -2341,6 +3403,10 @@ static int ov518_mode_init_regs(struct sd *sd)
+ reg_w(sd, 0x21, 0x1f);
+ }
+ break;
++ case SEN_OV7620:
++ reg_w(sd, 0x20, 0x00);
++ reg_w(sd, 0x21, 0x19);
++ break;
+ default:
+ reg_w(sd, 0x21, 0x19);
+ }
+@@ -2443,7 +3509,8 @@ static int ov519_mode_init_regs(struct sd *sd)
+ if (write_regvals(sd, mode_init_519,
+ ARRAY_SIZE(mode_init_519)))
+ return -EIO;
+- if (sd->sensor == SEN_OV7640) {
++ if (sd->sensor == SEN_OV7640 ||
++ sd->sensor == SEN_OV7648) {
+ /* Select 8-bit input mode */
+ reg_w_mask(sd, OV519_R20_DFR, 0x10, 0x10);
+ }
+@@ -2458,6 +3525,9 @@ static int ov519_mode_init_regs(struct sd *sd)
+ if (sd->sensor == SEN_OV7670 &&
+ sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv)
+ reg_w(sd, OV519_R12_X_OFFSETL, 0x04);
++ else if (sd->sensor == SEN_OV7648 &&
++ sd->gspca_dev.cam.cam_mode[sd->gspca_dev.curr_mode].priv)
++ reg_w(sd, OV519_R12_X_OFFSETL, 0x01);
+ else
+ reg_w(sd, OV519_R12_X_OFFSETL, 0x00);
+ reg_w(sd, OV519_R13_X_OFFSETH, 0x00);
+@@ -2475,6 +3545,7 @@ static int ov519_mode_init_regs(struct sd *sd)
+ sd->clockdiv = 0;
+ switch (sd->sensor) {
+ case SEN_OV7640:
++ case SEN_OV7648:
+ switch (sd->frame_rate) {
+ default:
+ /* case 30: */
+@@ -2548,21 +3619,78 @@ static int ov519_mode_init_regs(struct sd *sd)
+ static int mode_init_ov_sensor_regs(struct sd *sd)
+ {
+ struct gspca_dev *gspca_dev;
+- int qvga;
++ int qvga, xstart, xend, ystart, yend;
++ __u8 v;
+
+ gspca_dev = &sd->gspca_dev;
+ qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 1;
+
+ /******** Mode (VGA/QVGA) and sensor specific regs ********/
+ switch (sd->sensor) {
++ case SEN_OV2610:
++ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
++ i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
++ i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a);
++ i2c_w(sd, 0x25, qvga ? 0x30 : 0x60);
++ i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
++ i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0);
++ i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
++ return 0;
++ case SEN_OV3610:
++ if (qvga) {
++ xstart = (1040 - gspca_dev->width) / 2 + (0x1f << 4);
++ ystart = (776 - gspca_dev->height) / 2;
++ } else {
++ xstart = (2076 - gspca_dev->width) / 2 + (0x10 << 4);
++ ystart = (1544 - gspca_dev->height) / 2;
++ }
++ xend = xstart + gspca_dev->width;
++ yend = ystart + gspca_dev->height;
++ /* Writing to the COMH register resets the other windowing regs
++ to their default values, so we must do this first. */
++ i2c_w_mask(sd, 0x12, qvga ? 0x40 : 0x00, 0xf0);
++ i2c_w_mask(sd, 0x32,
++ (((xend >> 1) & 7) << 3) | ((xstart >> 1) & 7),
++ 0x3f);
++ i2c_w_mask(sd, 0x03,
++ (((yend >> 1) & 3) << 2) | ((ystart >> 1) & 3),
++ 0x0f);
++ i2c_w(sd, 0x17, xstart >> 4);
++ i2c_w(sd, 0x18, xend >> 4);
++ i2c_w(sd, 0x19, ystart >> 3);
++ i2c_w(sd, 0x1a, yend >> 3);
++ return 0;
+ case SEN_OV8610:
+ /* For OV8610 qvga means qsvga */
+ i2c_w_mask(sd, OV7610_REG_COM_C, qvga ? (1 << 5) : 0, 1 << 5);
++#if 0
++ /* FIXME: Does this improve the image quality or frame rate? */
++ i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
++ i2c_w(sd, 0x24, 0x10);
++ i2c_w(sd, 0x25, qvga ? 0x40 : 0x8a);
++ i2c_w(sd, 0x2f, qvga ? 0x30 : 0xb0);
++ i2c_w(sd, 0x35, qvga ? 0x1c : 0x9c);
++#endif
++ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
++ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
++ i2c_w_mask(sd, 0x2d, 0x00, 0x40); /* from windrv 090403 */
++ i2c_w_mask(sd, 0x28, 0x20, 0x20); /* progressive mode on */
+ break;
+ case SEN_OV7610:
+ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
++#if 0
++ /* FIXME: Does this improve the image quality or frame rate? */
++ i2c_w_mask(sd, 0x28, qvga?0x00:0x20, 0x20);
++ i2c_w(sd, 0x24, 0x10);
++ i2c_w(sd, 0x25, qvga?0x40:0x8a);
++ i2c_w(sd, 0x2f, qvga?0x30:0xb0);
++#endif
++ i2c_w(sd, 0x35, qvga?0x1e:0x9e);
++ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
++ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+ break;
+ case SEN_OV7620:
++ case SEN_OV7620AE:
+ case SEN_OV76BE:
+ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
+@@ -2571,15 +3699,23 @@ static int mode_init_ov_sensor_regs(struct sd *sd)
+ i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
+ i2c_w_mask(sd, 0x67, qvga ? 0xb0 : 0x90, 0xf0);
+ i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
++ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
++ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
++ if (sd->sensor == SEN_OV76BE)
++ i2c_w(sd, 0x35, qvga ? 0x1e : 0x9e);
+ break;
+ case SEN_OV7640:
++ case SEN_OV7648:
+ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
+ i2c_w_mask(sd, 0x28, qvga ? 0x00 : 0x20, 0x20);
+-/* i2c_w(sd, 0x24, qvga ? 0x20 : 0x3a); */
+-/* i2c_w(sd, 0x25, qvga ? 0x30 : 0x60); */
+-/* i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40); */
+-/* i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0); */
+-/* i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20); */
++ /* Setting this undocumented bit in qvga mode removes a very
++ annoying vertical shaking of the image */
++ i2c_w_mask(sd, 0x2d, qvga ? 0x40 : 0x00, 0x40);
++ /* Unknown */
++ i2c_w_mask(sd, 0x67, qvga ? 0xf0 : 0x90, 0xf0);
++ /* Allow higher automatic gain (to allow higher framerates) */
++ i2c_w_mask(sd, 0x74, qvga ? 0x20 : 0x00, 0x20);
++ i2c_w_mask(sd, 0x12, 0x04, 0x04); /* AWB: 1 */
+ break;
+ case SEN_OV7670:
+ /* set COM7_FMT_VGA or COM7_FMT_QVGA
+@@ -2588,55 +3724,56 @@ static int mode_init_ov_sensor_regs(struct sd *sd)
+ i2c_w_mask(sd, OV7670_REG_COM7,
+ qvga ? OV7670_COM7_FMT_QVGA : OV7670_COM7_FMT_VGA,
+ OV7670_COM7_FMT_MASK);
++ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
++ i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_AWB,
++ OV7670_COM8_AWB);
++ if (qvga) { /* QVGA from ov7670.c by
++ * Jonathan Corbet */
++ xstart = 164;
++ xend = 28;
++ ystart = 14;
++ yend = 494;
++ } else { /* VGA */
++ xstart = 158;
++ xend = 14;
++ ystart = 10;
++ yend = 490;
++ }
++ /* OV7670 hardware window registers are split across
++ * multiple locations */
++ i2c_w(sd, OV7670_REG_HSTART, xstart >> 3);
++ i2c_w(sd, OV7670_REG_HSTOP, xend >> 3);
++ v = i2c_r(sd, OV7670_REG_HREF);
++ v = (v & 0xc0) | ((xend & 0x7) << 3) | (xstart & 0x07);
++ msleep(10); /* need to sleep between read and write to
++ * same reg! */
++ i2c_w(sd, OV7670_REG_HREF, v);
++
++ i2c_w(sd, OV7670_REG_VSTART, ystart >> 2);
++ i2c_w(sd, OV7670_REG_VSTOP, yend >> 2);
++ v = i2c_r(sd, OV7670_REG_VREF);
++ v = (v & 0xc0) | ((yend & 0x3) << 2) | (ystart & 0x03);
++ msleep(10); /* need to sleep between read and write to
++ * same reg! */
++ i2c_w(sd, OV7670_REG_VREF, v);
+ break;
+ case SEN_OV6620:
++ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
++ i2c_w_mask(sd, 0x13, 0x00, 0x20); /* Select 16 bit data bus */
++ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
++ break;
+ case SEN_OV6630:
+ case SEN_OV66308AF:
+ i2c_w_mask(sd, 0x14, qvga ? 0x20 : 0x00, 0x20);
++ i2c_w_mask(sd, 0x12, 0x04, 0x06); /* AWB: 1 Test pattern: 0 */
+ break;
+ default:
+ return -EINVAL;
+ }
+
+- /******** Palette-specific regs ********/
+-
+- /* The OV518 needs special treatment. Although both the OV518
+- * and the OV6630 support a 16-bit video bus, only the 8 bit Y
+- * bus is actually used. The UV bus is tied to ground.
+- * Therefore, the OV6630 needs to be in 8-bit multiplexed
+- * output mode */
+-
+- /* OV7640 is 8-bit only */
+-
+- if (sd->sensor != SEN_OV6630 && sd->sensor != SEN_OV66308AF &&
+- sd->sensor != SEN_OV7640)
+- i2c_w_mask(sd, 0x13, 0x00, 0x20);
+-
+ /******** Clock programming ********/
+ i2c_w(sd, 0x11, sd->clockdiv);
+
+- /******** Special Features ********/
+-/* no evidence this is possible with OV7670, either */
+- /* Test Pattern */
+- if (sd->sensor != SEN_OV7640 && sd->sensor != SEN_OV7670)
+- i2c_w_mask(sd, 0x12, 0x00, 0x02);
+-
+- /* Enable auto white balance */
+- if (sd->sensor == SEN_OV7670)
+- i2c_w_mask(sd, OV7670_REG_COM8, OV7670_COM8_AWB,
+- OV7670_COM8_AWB);
+- else
+- i2c_w_mask(sd, 0x12, 0x04, 0x04);
+-
+- /* This will go away as soon as ov51x_mode_init_sensor_regs() */
+- /* is fully tested. */
+- /* 7620/6620/6630? don't have register 0x35, so play it safe */
+- if (sd->sensor == SEN_OV7610 || sd->sensor == SEN_OV76BE) {
+- if (!qvga)
+- i2c_w(sd, 0x35, 0x9e);
+- else
+- i2c_w(sd, 0x35, 0x1e);
+- }
+ return 0;
+ }
+
+@@ -2659,8 +3796,12 @@ static int set_ov_sensor_window(struct sd *sd)
+ struct gspca_dev *gspca_dev;
+ int qvga, crop;
+ int hwsbase, hwebase, vwsbase, vwebase, hwscale, vwscale;
+- int ret, hstart, hstop, vstop, vstart;
+- __u8 v;
++ int ret;
++
++ /* mode setup is fully handled in mode_init_ov_sensor_regs for these */
++ if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610 ||
++ sd->sensor == SEN_OV7670)
++ return mode_init_ov_sensor_regs(sd);
+
+ gspca_dev = &sd->gspca_dev;
+ qvga = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv & 1;
+@@ -2699,20 +3840,17 @@ static int set_ov_sensor_window(struct sd *sd)
+ }
+ break;
+ case SEN_OV7620:
++ case SEN_OV7620AE:
+ hwsbase = 0x2f; /* From 7620.SET (spec is wrong) */
+ hwebase = 0x2f;
+ vwsbase = vwebase = 0x05;
+ break;
+ case SEN_OV7640:
++ case SEN_OV7648:
+ hwsbase = 0x1a;
+ hwebase = 0x1a;
+ vwsbase = vwebase = 0x03;
+ break;
+- case SEN_OV7670:
+- /*handling of OV7670 hardware sensor start and stop values
+- * is very odd, compared to the other OV sensors */
+- vwsbase = vwebase = hwebase = hwsbase = 0x00;
+- break;
+ default:
+ return -EINVAL;
+ }
+@@ -2753,58 +3891,11 @@ static int set_ov_sensor_window(struct sd *sd)
+ if (ret < 0)
+ return ret;
+
+- if (sd->sensor == SEN_OV8610) {
+- i2c_w_mask(sd, 0x2d, 0x05, 0x40);
+- /* old 0x95, new 0x05 from windrv 090403 */
+- /* bits 5-7: reserved */
+- i2c_w_mask(sd, 0x28, 0x20, 0x20);
+- /* bit 5: progressive mode on */
+- }
+-
+- /* The below is wrong for OV7670s because their window registers
+- * only store the high bits in 0x17 to 0x1a */
++ i2c_w(sd, 0x17, hwsbase);
++ i2c_w(sd, 0x18, hwebase + (sd->sensor_width >> hwscale));
++ i2c_w(sd, 0x19, vwsbase);
++ i2c_w(sd, 0x1a, vwebase + (sd->sensor_height >> vwscale));
+
+- /* SRH Use sd->max values instead of requested win values */
+- /* SCS Since we're sticking with only the max hardware widths
+- * for a given mode */
+- /* I can hard code this for OV7670s */
+- /* Yes, these numbers do look odd, but they're tested and work! */
+- if (sd->sensor == SEN_OV7670) {
+- if (qvga) { /* QVGA from ov7670.c by
+- * Jonathan Corbet */
+- hstart = 164;
+- hstop = 28;
+- vstart = 14;
+- vstop = 494;
+- } else { /* VGA */
+- hstart = 158;
+- hstop = 14;
+- vstart = 10;
+- vstop = 490;
+- }
+- /* OV7670 hardware window registers are split across
+- * multiple locations */
+- i2c_w(sd, OV7670_REG_HSTART, hstart >> 3);
+- i2c_w(sd, OV7670_REG_HSTOP, hstop >> 3);
+- v = i2c_r(sd, OV7670_REG_HREF);
+- v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x07);
+- msleep(10); /* need to sleep between read and write to
+- * same reg! */
+- i2c_w(sd, OV7670_REG_HREF, v);
+-
+- i2c_w(sd, OV7670_REG_VSTART, vstart >> 2);
+- i2c_w(sd, OV7670_REG_VSTOP, vstop >> 2);
+- v = i2c_r(sd, OV7670_REG_VREF);
+- v = (v & 0xc0) | ((vstop & 0x3) << 2) | (vstart & 0x03);
+- msleep(10); /* need to sleep between read and write to
+- * same reg! */
+- i2c_w(sd, OV7670_REG_VREF, v);
+- } else {
+- i2c_w(sd, 0x17, hwsbase);
+- i2c_w(sd, 0x18, hwebase + (sd->gspca_dev.width >> hwscale));
+- i2c_w(sd, 0x19, vwsbase);
+- i2c_w(sd, 0x1a, vwebase + (sd->gspca_dev.height >> vwscale));
+- }
+ return 0;
+ }
+
+@@ -2814,6 +3905,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ struct sd *sd = (struct sd *) gspca_dev;
+ int ret = 0;
+
++ /* Default for most bridges, allow bridge_mode_init_regs to override */
++ sd->sensor_width = sd->gspca_dev.width;
++ sd->sensor_height = sd->gspca_dev.height;
++
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+@@ -2826,6 +3921,10 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ case BRIDGE_OV519:
+ ret = ov519_mode_init_regs(sd);
+ break;
++ /* case BRIDGE_OVFX2: nothing to do */
++ case BRIDGE_W9968CF:
++ ret = w9968cf_mode_init_regs(sd);
++ break;
+ }
+ if (ret < 0)
+ goto out;
+@@ -2859,10 +3958,17 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
+ ov51x_led_control(sd, 0);
+ }
+
++static void sd_stop0(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (sd->bridge == BRIDGE_W9968CF)
++ w9968cf_stop0(sd);
++}
++
+ static void ov511_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+- __u8 *in, /* isoc packet */
+- int len) /* iso packet length */
++ u8 *in, /* isoc packet */
++ int len) /* iso packet length */
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+@@ -2893,11 +3999,11 @@ static void ov511_pkt_scan(struct gspca_dev *gspca_dev,
+ return;
+ }
+ /* Add 11 byte footer to frame, might be usefull */
+- gspca_frame_add(gspca_dev, LAST_PACKET, frame, in, 11);
++ gspca_frame_add(gspca_dev, LAST_PACKET, in, 11);
+ return;
+ } else {
+ /* Frame start */
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame, in, 0);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, in, 0);
+ sd->packet_nr = 0;
+ }
+ }
+@@ -2906,12 +4012,11 @@ static void ov511_pkt_scan(struct gspca_dev *gspca_dev,
+ len--;
+
+ /* intermediate packet */
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, in, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, in, len);
+ }
+
+ static void ov518_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+- __u8 *data, /* isoc packet */
++ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -2919,8 +4024,8 @@ static void ov518_pkt_scan(struct gspca_dev *gspca_dev,
+ /* A false positive here is likely, until OVT gives me
+ * the definitive SOF/EOF format */
+ if ((!(data[0] | data[1] | data[2] | data[3] | data[5])) && data[6]) {
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame, data, 0);
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, 0);
++ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ sd->packet_nr = 0;
+ }
+
+@@ -2944,12 +4049,11 @@ static void ov518_pkt_scan(struct gspca_dev *gspca_dev,
+ }
+
+ /* intermediate packet */
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+
+ static void ov519_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+- __u8 *data, /* isoc packet */
++ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ /* Header of ov519 is 16 bytes:
+@@ -2972,7 +4076,7 @@ static void ov519_pkt_scan(struct gspca_dev *gspca_dev,
+ len -= HDRSZ;
+ #undef HDRSZ
+ if (data[0] == 0xff || data[1] == 0xd8)
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ data, len);
+ else
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+@@ -2980,20 +4084,31 @@ static void ov519_pkt_scan(struct gspca_dev *gspca_dev,
+ case 0x51: /* end of frame */
+ if (data[9] != 0)
+ gspca_dev->last_packet_type = DISCARD_PACKET;
+- gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+- data, 0);
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ NULL, 0);
+ return;
+ }
+ }
+
+ /* intermediate packet */
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+- data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
++}
++
++static void ovfx2_pkt_scan(struct gspca_dev *gspca_dev,
++ u8 *data, /* isoc packet */
++ int len) /* iso packet length */
++{
++ /* A short read signals EOF */
++ if (len < OVFX2_BULK_SIZE) {
++ gspca_frame_add(gspca_dev, LAST_PACKET, data, len);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
++ return;
++ }
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+- __u8 *data, /* isoc packet */
++ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -3001,14 +4116,20 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ switch (sd->bridge) {
+ case BRIDGE_OV511:
+ case BRIDGE_OV511PLUS:
+- ov511_pkt_scan(gspca_dev, frame, data, len);
++ ov511_pkt_scan(gspca_dev, data, len);
+ break;
+ case BRIDGE_OV518:
+ case BRIDGE_OV518PLUS:
+- ov518_pkt_scan(gspca_dev, frame, data, len);
++ ov518_pkt_scan(gspca_dev, data, len);
+ break;
+ case BRIDGE_OV519:
+- ov519_pkt_scan(gspca_dev, frame, data, len);
++ ov519_pkt_scan(gspca_dev, data, len);
++ break;
++ case BRIDGE_OVFX2:
++ ovfx2_pkt_scan(gspca_dev, data, len);
++ break;
++ case BRIDGE_W9968CF:
++ w9968cf_pkt_scan(gspca_dev, data, len);
+ break;
+ }
+ }
+@@ -3029,9 +4150,11 @@ static void setbrightness(struct gspca_dev *gspca_dev)
+ case SEN_OV6630:
+ case SEN_OV66308AF:
+ case SEN_OV7640:
++ case SEN_OV7648:
+ i2c_w(sd, OV7610_REG_BRT, val);
+ break;
+ case SEN_OV7620:
++ case SEN_OV7620AE:
+ /* 7620 doesn't like manual changes when in auto mode */
+ if (!sd->autobrightness)
+ i2c_w(sd, OV7610_REG_BRT, val);
+@@ -3068,7 +4191,8 @@ static void setcontrast(struct gspca_dev *gspca_dev)
+ i2c_w(sd, 0x64, ctab[val >> 5]);
+ break;
+ }
+- case SEN_OV7620: {
++ case SEN_OV7620:
++ case SEN_OV7620AE: {
+ static const __u8 ctab[] = {
+ 0x01, 0x05, 0x09, 0x11, 0x15, 0x35, 0x37, 0x57,
+ 0x5b, 0xa5, 0xa7, 0xc7, 0xc9, 0xcf, 0xef, 0xff
+@@ -3078,10 +4202,6 @@ static void setcontrast(struct gspca_dev *gspca_dev)
+ i2c_w(sd, 0x64, ctab[val >> 4]);
+ break;
+ }
+- case SEN_OV7640:
+- /* Use gain control instead. */
+- i2c_w(sd, OV7610_REG_GAIN, val >> 2);
+- break;
+ case SEN_OV7670:
+ /* check that this isn't just the same as ov7610 */
+ i2c_w(sd, OV7670_REG_CONTRAS, val >> 1);
+@@ -3105,6 +4225,7 @@ static void setcolors(struct gspca_dev *gspca_dev)
+ i2c_w(sd, OV7610_REG_SAT, val);
+ break;
+ case SEN_OV7620:
++ case SEN_OV7620AE:
+ /* Use UV gamma control instead. Bits 0 & 7 are reserved. */
+ /* rc = ov_i2c_write(sd->dev, 0x62, (val >> 9) & 0x7e);
+ if (rc < 0)
+@@ -3112,6 +4233,7 @@ static void setcolors(struct gspca_dev *gspca_dev)
+ i2c_w(sd, OV7610_REG_SAT, val);
+ break;
+ case SEN_OV7640:
++ case SEN_OV7648:
+ i2c_w(sd, OV7610_REG_SAT, val & 0xf0);
+ break;
+ case SEN_OV7670:
+@@ -3124,7 +4246,9 @@ static void setcolors(struct gspca_dev *gspca_dev)
+
+ static void setautobrightness(struct sd *sd)
+ {
+- if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7670)
++ if (sd->sensor == SEN_OV7640 || sd->sensor == SEN_OV7648 ||
++ sd->sensor == SEN_OV7670 ||
++ sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610)
+ return;
+
+ i2c_w_mask(sd, 0x2d, sd->autobrightness ? 0x10 : 0x00, 0x10);
+@@ -3132,6 +4256,9 @@ static void setautobrightness(struct sd *sd)
+
+ static void setfreq(struct sd *sd)
+ {
++ if (sd->sensor == SEN_OV2610 || sd->sensor == SEN_OV3610)
++ return;
++
+ if (sd->sensor == SEN_OV7670) {
+ switch (sd->freq) {
+ case 0: /* Banding filter disabled */
+@@ -3301,8 +4428,12 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ sd->freq = val;
+- if (gspca_dev->streaming)
++ if (gspca_dev->streaming) {
+ setfreq(sd);
++ /* Ugly but necessary */
++ if (sd->bridge == BRIDGE_W9968CF)
++ w9968cf_set_crop_window(sd);
++ }
+ return 0;
+ }
+
+@@ -3343,6 +4474,45 @@ static int sd_querymenu(struct gspca_dev *gspca_dev,
+ return -EINVAL;
+ }
+
++static int sd_get_jcomp(struct gspca_dev *gspca_dev,
++ struct v4l2_jpegcompression *jcomp)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (sd->bridge != BRIDGE_W9968CF)
++ return -EINVAL;
++
++ memset(jcomp, 0, sizeof *jcomp);
++ jcomp->quality = sd->quality;
++ jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT |
++ V4L2_JPEG_MARKER_DRI;
++ return 0;
++}
++
++static int sd_set_jcomp(struct gspca_dev *gspca_dev,
++ struct v4l2_jpegcompression *jcomp)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (sd->bridge != BRIDGE_W9968CF)
++ return -EINVAL;
++
++ if (gspca_dev->streaming)
++ return -EBUSY;
++
++ if (jcomp->quality < QUALITY_MIN)
++ sd->quality = QUALITY_MIN;
++ else if (jcomp->quality > QUALITY_MAX)
++ sd->quality = QUALITY_MAX;
++ else
++ sd->quality = jcomp->quality;
++
++ /* Return resulting jcomp params to app */
++ sd_get_jcomp(gspca_dev, jcomp);
++
++ return 0;
++}
++
+ /* sub-driver description */
+ static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+@@ -3352,12 +4522,16 @@ static const struct sd_desc sd_desc = {
+ .init = sd_init,
+ .start = sd_start,
+ .stopN = sd_stopN,
++ .stop0 = sd_stop0,
+ .pkt_scan = sd_pkt_scan,
+ .querymenu = sd_querymenu,
++ .get_jcomp = sd_get_jcomp,
++ .set_jcomp = sd_set_jcomp,
+ };
+
+ /* -- module initialisation -- */
+ static const __devinitdata struct usb_device_id device_table[] = {
++ {USB_DEVICE(0x041e, 0x4003), .driver_info = BRIDGE_W9968CF },
+ {USB_DEVICE(0x041e, 0x4052), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x041e, 0x405f), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x041e, 0x4060), .driver_info = BRIDGE_OV519 },
+@@ -3369,16 +4543,22 @@ static const __devinitdata struct usb_device_id device_table[] = {
+ .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+ {USB_DEVICE(0x045e, 0x028c), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x054c, 0x0154), .driver_info = BRIDGE_OV519 },
+- {USB_DEVICE(0x054c, 0x0155), .driver_info = BRIDGE_OV519 },
++ {USB_DEVICE(0x054c, 0x0155),
++ .driver_info = BRIDGE_OV519 | BRIDGE_INVERT_LED },
+ {USB_DEVICE(0x05a9, 0x0511), .driver_info = BRIDGE_OV511 },
+ {USB_DEVICE(0x05a9, 0x0518), .driver_info = BRIDGE_OV518 },
+ {USB_DEVICE(0x05a9, 0x0519), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x05a9, 0x0530), .driver_info = BRIDGE_OV519 },
++ {USB_DEVICE(0x05a9, 0x2800), .driver_info = BRIDGE_OVFX2 },
+ {USB_DEVICE(0x05a9, 0x4519), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x05a9, 0x8519), .driver_info = BRIDGE_OV519 },
+ {USB_DEVICE(0x05a9, 0xa511), .driver_info = BRIDGE_OV511PLUS },
+ {USB_DEVICE(0x05a9, 0xa518), .driver_info = BRIDGE_OV518PLUS },
+ {USB_DEVICE(0x0813, 0x0002), .driver_info = BRIDGE_OV511PLUS },
++ {USB_DEVICE(0x0b62, 0x0059), .driver_info = BRIDGE_OVFX2 },
++ {USB_DEVICE(0x0e96, 0xc001), .driver_info = BRIDGE_OVFX2 },
++ {USB_DEVICE(0x1046, 0x9967), .driver_info = BRIDGE_W9968CF },
++ {USB_DEVICE(0x8020, 0xEF04), .driver_info = BRIDGE_OVFX2 },
+ {}
+ };
+
+diff --git a/drivers/media/video/gspca/ov534.c b/drivers/media/video/gspca/ov534.c
+index 4b528b3..957e05e 100644
+--- a/drivers/media/video/gspca/ov534.c
++++ b/drivers/media/video/gspca/ov534.c
+@@ -1,5 +1,6 @@
+ /*
+- * ov534 gspca driver
++ * ov534-ov772x gspca driver
++ *
+ * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it>
+ * Copyright (C) 2008 Jim Paris <jim@jtan.com>
+ * Copyright (C) 2009 Jean-Francois Moine http://moinejf.free.fr
+@@ -8,6 +9,10 @@
+ * USB protocol reverse engineered by Jim Paris <jim@jtan.com>
+ * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/
+ *
++ * PS3 Eye camera enhanced by Richard Kaswy http://kaswy.free.fr
++ * PS3 Eye camera, brightness, contrast, hue, AWB control added
++ * by Max Thrun <bear24rw@gmail.com>
++ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+@@ -51,16 +56,225 @@ struct sd {
+ u16 last_fid;
+ u8 frame_rate;
+
+- u8 sensor;
+-#define SENSOR_OV772X 0
+-#define SENSOR_OV965X 1
++ u8 brightness;
++ u8 contrast;
++ u8 gain;
++ u8 exposure;
++ u8 redblc;
++ u8 blueblc;
++ u8 hue;
++ u8 autogain;
++ u8 awb;
++ s8 sharpness;
++ u8 hflip;
++ u8 vflip;
++
+ };
+
+ /* V4L2 controls supported by the driver */
+-static struct ctrl sd_ctrls[] = {
++static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setredblc(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getredblc(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setblueblc(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getblueblc(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
++
++static const struct ctrl sd_ctrls[] = {
++ { /* 0 */
++ {
++ .id = V4L2_CID_BRIGHTNESS,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Brightness",
++ .minimum = 0,
++ .maximum = 255,
++ .step = 1,
++#define BRIGHTNESS_DEF 20
++ .default_value = BRIGHTNESS_DEF,
++ },
++ .set = sd_setbrightness,
++ .get = sd_getbrightness,
++ },
++ { /* 1 */
++ {
++ .id = V4L2_CID_CONTRAST,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Contrast",
++ .minimum = 0,
++ .maximum = 255,
++ .step = 1,
++#define CONTRAST_DEF 37
++ .default_value = CONTRAST_DEF,
++ },
++ .set = sd_setcontrast,
++ .get = sd_getcontrast,
++ },
++ { /* 2 */
++ {
++ .id = V4L2_CID_GAIN,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Main Gain",
++ .minimum = 0,
++ .maximum = 63,
++ .step = 1,
++#define GAIN_DEF 20
++ .default_value = GAIN_DEF,
++ },
++ .set = sd_setgain,
++ .get = sd_getgain,
++ },
++ { /* 3 */
++ {
++ .id = V4L2_CID_EXPOSURE,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Exposure",
++ .minimum = 0,
++ .maximum = 255,
++ .step = 1,
++#define EXPO_DEF 120
++ .default_value = EXPO_DEF,
++ },
++ .set = sd_setexposure,
++ .get = sd_getexposure,
++ },
++ { /* 4 */
++ {
++ .id = V4L2_CID_RED_BALANCE,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Red Balance",
++ .minimum = 0,
++ .maximum = 255,
++ .step = 1,
++#define RED_BALANCE_DEF 128
++ .default_value = RED_BALANCE_DEF,
++ },
++ .set = sd_setredblc,
++ .get = sd_getredblc,
++ },
++ { /* 5 */
++ {
++ .id = V4L2_CID_BLUE_BALANCE,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Blue Balance",
++ .minimum = 0,
++ .maximum = 255,
++ .step = 1,
++#define BLUE_BALANCE_DEF 128
++ .default_value = BLUE_BALANCE_DEF,
++ },
++ .set = sd_setblueblc,
++ .get = sd_getblueblc,
++ },
++ { /* 6 */
++ {
++ .id = V4L2_CID_HUE,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Hue",
++ .minimum = 0,
++ .maximum = 255,
++ .step = 1,
++#define HUE_DEF 143
++ .default_value = HUE_DEF,
++ },
++ .set = sd_sethue,
++ .get = sd_gethue,
++ },
++ { /* 7 */
++ {
++ .id = V4L2_CID_AUTOGAIN,
++ .type = V4L2_CTRL_TYPE_BOOLEAN,
++ .name = "Autogain",
++ .minimum = 0,
++ .maximum = 1,
++ .step = 1,
++#define AUTOGAIN_DEF 0
++ .default_value = AUTOGAIN_DEF,
++ },
++ .set = sd_setautogain,
++ .get = sd_getautogain,
++ },
++#define AWB_IDX 8
++ { /* 8 */
++ {
++ .id = V4L2_CID_AUTO_WHITE_BALANCE,
++ .type = V4L2_CTRL_TYPE_BOOLEAN,
++ .name = "Auto White Balance",
++ .minimum = 0,
++ .maximum = 1,
++ .step = 1,
++#define AWB_DEF 0
++ .default_value = AWB_DEF,
++ },
++ .set = sd_setawb,
++ .get = sd_getawb,
++ },
++ { /* 9 */
++ {
++ .id = V4L2_CID_SHARPNESS,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Sharpness",
++ .minimum = 0,
++ .maximum = 63,
++ .step = 1,
++#define SHARPNESS_DEF 0
++ .default_value = SHARPNESS_DEF,
++ },
++ .set = sd_setsharpness,
++ .get = sd_getsharpness,
++ },
++ { /* 10 */
++ {
++ .id = V4L2_CID_HFLIP,
++ .type = V4L2_CTRL_TYPE_BOOLEAN,
++ .name = "HFlip",
++ .minimum = 0,
++ .maximum = 1,
++ .step = 1,
++#define HFLIP_DEF 0
++ .default_value = HFLIP_DEF,
++ },
++ .set = sd_sethflip,
++ .get = sd_gethflip,
++ },
++ { /* 11 */
++ {
++ .id = V4L2_CID_VFLIP,
++ .type = V4L2_CTRL_TYPE_BOOLEAN,
++ .name = "VFlip",
++ .minimum = 0,
++ .maximum = 1,
++ .step = 1,
++#define VFLIP_DEF 0
++ .default_value = VFLIP_DEF,
++ },
++ .set = sd_setvflip,
++ .get = sd_getvflip,
++ },
+ };
+
+-static const struct v4l2_pix_format vga_yuyv_mode[] = {
++static const struct v4l2_pix_format ov772x_mode[] = {
++ {320, 240, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
++ .bytesperline = 320 * 2,
++ .sizeimage = 320 * 240 * 2,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 1},
+ {640, 480, V4L2_PIX_FMT_YUYV, V4L2_FIELD_NONE,
+ .bytesperline = 640 * 2,
+ .sizeimage = 640 * 480 * 2,
+@@ -68,20 +282,21 @@ static const struct v4l2_pix_format vga_yuyv_mode[] = {
+ .priv = 0},
+ };
+
+-static const struct v4l2_pix_format vga_jpeg_mode[] = {
+- {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+- .bytesperline = 320,
+- .sizeimage = 320 * 240 * 3 / 8 + 590,
+- .colorspace = V4L2_COLORSPACE_JPEG,
+- .priv = 1},
+- {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+- .bytesperline = 640,
+- .sizeimage = 640 * 480 * 3 / 8 + 590,
+- .colorspace = V4L2_COLORSPACE_JPEG,
+- .priv = 0},
++static const u8 qvga_rates[] = {125, 100, 75, 60, 50, 40, 30};
++static const u8 vga_rates[] = {60, 50, 40, 30, 15};
++
++static const struct framerates ov772x_framerates[] = {
++ { /* 320x240 */
++ .rates = qvga_rates,
++ .nrates = ARRAY_SIZE(qvga_rates),
++ },
++ { /* 640x480 */
++ .rates = vga_rates,
++ .nrates = ARRAY_SIZE(vga_rates),
++ },
+ };
+
+-static const u8 bridge_init_ov722x[][2] = {
++static const u8 bridge_init[][2] = {
+ { 0xc2, 0x0c },
+ { 0x88, 0xf8 },
+ { 0xc3, 0x69 },
+@@ -122,6 +337,7 @@ static const u8 bridge_init_ov722x[][2] = {
+ { 0x1d, 0x40 },
+ { 0x1d, 0x02 }, /* payload size 0x0200 * 4 = 2048 bytes */
+ { 0x1d, 0x00 }, /* payload size */
++
+ { 0x1d, 0x02 }, /* frame size 0x025800 * 4 = 614400 */
+ { 0x1d, 0x58 }, /* frame size */
+ { 0x1d, 0x00 }, /* frame size */
+@@ -138,10 +354,20 @@ static const u8 bridge_init_ov722x[][2] = {
+ { 0xc1, 0x3c },
+ { 0xc2, 0x0c },
+ };
+-
+-static const u8 sensor_init_ov722x[][2] = {
++static const u8 sensor_init[][2] = {
+ { 0x12, 0x80 },
+ { 0x11, 0x01 },
++/*fixme: better have a delay?*/
++ { 0x11, 0x01 },
++ { 0x11, 0x01 },
++ { 0x11, 0x01 },
++ { 0x11, 0x01 },
++ { 0x11, 0x01 },
++ { 0x11, 0x01 },
++ { 0x11, 0x01 },
++ { 0x11, 0x01 },
++ { 0x11, 0x01 },
++ { 0x11, 0x01 },
+
+ { 0x3d, 0x03 },
+ { 0x17, 0x26 },
+@@ -154,10 +380,10 @@ static const u8 sensor_init_ov722x[][2] = {
+ { 0x65, 0x20 },
+ { 0x11, 0x01 },
+ { 0x42, 0x7f },
+- { 0x63, 0xe0 },
++ { 0x63, 0xaa }, /* AWB - was e0 */
+ { 0x64, 0xff },
+ { 0x66, 0x00 },
+- { 0x13, 0xf0 },
++ { 0x13, 0xf0 }, /* com8 */
+ { 0x0d, 0x41 },
+ { 0x0f, 0xc5 },
+ { 0x14, 0x11 },
+@@ -170,7 +396,7 @@ static const u8 sensor_init_ov722x[][2] = {
+ { 0x2a, 0x00 },
+ { 0x2b, 0x00 },
+ { 0x6b, 0xaa },
+- { 0x13, 0xff },
++ { 0x13, 0xff }, /* AWB */
+
+ { 0x90, 0x05 },
+ { 0x91, 0x01 },
+@@ -218,409 +444,52 @@ static const u8 sensor_init_ov722x[][2] = {
+ { 0x14, 0x41 },
+ { 0x0e, 0xcd },
+ { 0xac, 0xbf },
+- { 0x8e, 0x00 },
++ { 0x8e, 0x00 }, /* De-noise threshold */
+ { 0x0c, 0xd0 }
+ };
+-
+-static const u8 bridge_init_ov965x[][2] = {
+- {0x88, 0xf8},
+- {0x89, 0xff},
+- {0x76, 0x03},
+- {0x92, 0x03},
+- {0x95, 0x10},
+- {0xe2, 0x00},
+- {0xe7, 0x3e},
+- {0x8d, 0x1c},
+- {0x8e, 0x00},
+- {0x8f, 0x00},
+- {0x1f, 0x00},
+- {0xc3, 0xf9},
+- {0x89, 0xff},
+- {0x88, 0xf8},
+- {0x76, 0x03},
+- {0x92, 0x01},
+- {0x93, 0x18},
+- {0x1c, 0x0a},
+- {0x1d, 0x48},
++static const u8 bridge_start_vga[][2] = {
++ {0x1c, 0x00},
++ {0x1d, 0x40},
++ {0x1d, 0x02},
++ {0x1d, 0x00},
++ {0x1d, 0x02},
++ {0x1d, 0x58},
++ {0x1d, 0x00},
+ {0xc0, 0x50},
+ {0xc1, 0x3c},
+- {0x34, 0x05},
+- {0xc2, 0x0c},
+- {0xc3, 0xf9},
+- {0x34, 0x05},
+- {0xe7, 0x2e},
+- {0x31, 0xf9},
+- {0x35, 0x02},
+- {0xd9, 0x10},
+- {0x25, 0x42},
+- {0x94, 0x11},
+-};
+-
+-static const u8 sensor_init_ov965x[][2] = {
+- {0x12, 0x80}, /* com7 - SSCB reset */
+- {0x00, 0x00}, /* gain */
+- {0x01, 0x80}, /* blue */
+- {0x02, 0x80}, /* red */
+- {0x03, 0x1b}, /* vref */
+- {0x04, 0x03}, /* com1 - exposure low bits */
+- {0x0b, 0x57}, /* ver */
+- {0x0e, 0x61}, /* com5 */
+- {0x0f, 0x42}, /* com6 */
+- {0x11, 0x00}, /* clkrc */
+- {0x12, 0x02}, /* com7 - 15fps VGA YUYV */
+- {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
+- {0x14, 0x28}, /* com9 */
+- {0x16, 0x24}, /* reg16 */
+- {0x17, 0x1d}, /* hstart*/
+- {0x18, 0xbd}, /* hstop */
+- {0x19, 0x01}, /* vstrt */
+- {0x1a, 0x81}, /* vstop*/
+- {0x1e, 0x04}, /* mvfp */
+- {0x24, 0x3c}, /* aew */
+- {0x25, 0x36}, /* aeb */
+- {0x26, 0x71}, /* vpt */
+- {0x27, 0x08}, /* bbias */
+- {0x28, 0x08}, /* gbbias */
+- {0x29, 0x15}, /* gr com */
+- {0x2a, 0x00}, /* exhch */
+- {0x2b, 0x00}, /* exhcl */
+- {0x2c, 0x08}, /* rbias */
+- {0x32, 0xff}, /* href */
+- {0x33, 0x00}, /* chlf */
+- {0x34, 0x3f}, /* aref1 */
+- {0x35, 0x00}, /* aref2 */
+- {0x36, 0xf8}, /* aref3 */
+- {0x38, 0x72}, /* adc2 */
+- {0x39, 0x57}, /* aref4 */
+- {0x3a, 0x80}, /* tslb - yuyv */
+- {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
+- {0x3d, 0x99}, /* com13 */
+- {0x3f, 0xc1}, /* edge */
+- {0x40, 0xc0}, /* com15 */
+- {0x41, 0x40}, /* com16 */
+- {0x42, 0xc0}, /* com17 */
+- {0x43, 0x0a}, /* rsvd */
+- {0x44, 0xf0},
+- {0x45, 0x46},
+- {0x46, 0x62},
+- {0x47, 0x2a},
+- {0x48, 0x3c},
+- {0x4a, 0xfc},
+- {0x4b, 0xfc},
+- {0x4c, 0x7f},
+- {0x4d, 0x7f},
+- {0x4e, 0x7f},
+- {0x4f, 0x98}, /* matrix */
+- {0x50, 0x98},
+- {0x51, 0x00},
+- {0x52, 0x28},
+- {0x53, 0x70},
+- {0x54, 0x98},
+- {0x58, 0x1a}, /* matrix coef sign */
+- {0x59, 0x85}, /* AWB control */
+- {0x5a, 0xa9},
+- {0x5b, 0x64},
+- {0x5c, 0x84},
+- {0x5d, 0x53},
+- {0x5e, 0x0e},
+- {0x5f, 0xf0}, /* AWB blue limit */
+- {0x60, 0xf0}, /* AWB red limit */
+- {0x61, 0xf0}, /* AWB green limit */
+- {0x62, 0x00}, /* lcc1 */
+- {0x63, 0x00}, /* lcc2 */
+- {0x64, 0x02}, /* lcc3 */
+- {0x65, 0x16}, /* lcc4 */
+- {0x66, 0x01}, /* lcc5 */
+- {0x69, 0x02}, /* hv */
+- {0x6b, 0x5a}, /* dbvl */
+- {0x6c, 0x04},
+- {0x6d, 0x55},
+- {0x6e, 0x00},
+- {0x6f, 0x9d},
+- {0x70, 0x21}, /* dnsth */
+- {0x71, 0x78},
+- {0x72, 0x00}, /* poidx */
+- {0x73, 0x01}, /* pckdv */
+- {0x74, 0x3a}, /* xindx */
+- {0x75, 0x35}, /* yindx */
+- {0x76, 0x01},
+- {0x77, 0x02},
+- {0x7a, 0x12}, /* gamma curve */
+- {0x7b, 0x08},
+- {0x7c, 0x16},
+- {0x7d, 0x30},
+- {0x7e, 0x5e},
+- {0x7f, 0x72},
+- {0x80, 0x82},
+- {0x81, 0x8e},
+- {0x82, 0x9a},
+- {0x83, 0xa4},
+- {0x84, 0xac},
+- {0x85, 0xb8},
+- {0x86, 0xc3},
+- {0x87, 0xd6},
+- {0x88, 0xe6},
+- {0x89, 0xf2},
+- {0x8a, 0x03},
+- {0x8c, 0x89}, /* com19 */
+- {0x14, 0x28}, /* com9 */
+- {0x90, 0x7d},
+- {0x91, 0x7b},
+- {0x9d, 0x03}, /* lcc6 */
+- {0x9e, 0x04}, /* lcc7 */
+- {0x9f, 0x7a},
+- {0xa0, 0x79},
+- {0xa1, 0x40}, /* aechm */
+- {0xa4, 0x50}, /* com21 */
+- {0xa5, 0x68}, /* com26 */
+- {0xa6, 0x4a}, /* AWB green */
+- {0xa8, 0xc1}, /* refa8 */
+- {0xa9, 0xef}, /* refa9 */
+- {0xaa, 0x92},
+- {0xab, 0x04},
+- {0xac, 0x80}, /* black level control */
+- {0xad, 0x80},
+- {0xae, 0x80},
+- {0xaf, 0x80},
+- {0xb2, 0xf2},
+- {0xb3, 0x20},
+- {0xb4, 0x20}, /* ctrlb4 */
+- {0xb5, 0x00},
+- {0xb6, 0xaf},
+- {0xbb, 0xae},
+- {0xbc, 0x7f}, /* ADC channel offsets */
+- {0xdb, 0x7f},
+- {0xbe, 0x7f},
+- {0xbf, 0x7f},
+- {0xc0, 0xe2},
+- {0xc1, 0xc0},
+- {0xc2, 0x01},
+- {0xc3, 0x4e},
+- {0xc6, 0x85},
+- {0xc7, 0x80}, /* com24 */
+- {0xc9, 0xe0},
+- {0xca, 0xe8},
+- {0xcb, 0xf0},
+- {0xcc, 0xd8},
+- {0xcd, 0xf1},
+- {0x4f, 0x98},
+- {0x50, 0x98},
+- {0x51, 0x00},
+- {0x52, 0x28},
+- {0x53, 0x70},
+- {0x54, 0x98},
+- {0x58, 0x1a},
+- {0xff, 0x41}, /* read 41, write ff 00 */
+- {0x41, 0x40}, /* com16 */
+- {0xc5, 0x03}, /* 60 Hz banding filter */
+- {0x6a, 0x02}, /* 50 Hz banding filter */
+-
+- {0x12, 0x62}, /* com7 - 30fps VGA YUV */
+- {0x36, 0xfa}, /* aref3 */
+- {0x69, 0x0a}, /* hv */
+- {0x8c, 0x89}, /* com22 */
+- {0x14, 0x28}, /* com9 */
+- {0x3e, 0x0c},
+- {0x41, 0x40}, /* com16 */
+- {0x72, 0x00},
+- {0x73, 0x00},
+- {0x74, 0x3a},
+- {0x75, 0x35},
+- {0x76, 0x01},
+- {0xc7, 0x80},
+- {0x03, 0x12}, /* vref */
+- {0x17, 0x16}, /* hstart */
+- {0x18, 0x02}, /* hstop */
+- {0x19, 0x01}, /* vstrt */
+- {0x1a, 0x3d}, /* vstop */
+- {0x32, 0xff}, /* href */
+- {0xc0, 0xaa},
+ };
+-
+-static const u8 bridge_init_ov965x_2[][2] = {
+- {0x94, 0xaa},
+- {0xf1, 0x60},
+- {0xe5, 0x04},
+- {0xc0, 0x50},
+- {0xc1, 0x3c},
+- {0x8c, 0x00},
+- {0x8d, 0x1c},
+- {0x34, 0x05},
+-
+- {0xc2, 0x0c},
+- {0xc3, 0xf9},
+- {0xda, 0x01},
+- {0x50, 0x00},
+- {0x51, 0xa0},
+- {0x52, 0x3c},
+- {0x53, 0x00},
+- {0x54, 0x00},
+- {0x55, 0x00}, /* brightness */
+- {0x57, 0x00}, /* contrast 2 */
+- {0x5c, 0x00},
+- {0x5a, 0xa0},
+- {0x5b, 0x78},
+- {0x35, 0x02},
+- {0xd9, 0x10},
+- {0x94, 0x11},
++static const u8 sensor_start_vga[][2] = {
++ {0x12, 0x00},
++ {0x17, 0x26},
++ {0x18, 0xa0},
++ {0x19, 0x07},
++ {0x1a, 0xf0},
++ {0x29, 0xa0},
++ {0x2c, 0xf0},
++ {0x65, 0x20},
+ };
+-
+-static const u8 sensor_init_ov965x_2[][2] = {
+- {0x3b, 0xc4},
+- {0x1e, 0x04}, /* mvfp */
+- {0x13, 0xe0}, /* com8 */
+- {0x00, 0x00}, /* gain */
+- {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
+- {0x11, 0x03}, /* clkrc */
+- {0x6b, 0x5a}, /* dblv */
+- {0x6a, 0x05},
+- {0xc5, 0x07},
+- {0xa2, 0x4b},
+- {0xa3, 0x3e},
+- {0x2d, 0x00},
+- {0xff, 0x42}, /* read 42, write ff 00 */
+- {0x42, 0xc0},
+- {0x2d, 0x00},
+- {0xff, 0x42}, /* read 42, write ff 00 */
+- {0x42, 0xc1},
+- {0x3f, 0x01},
+- {0xff, 0x42}, /* read 42, write ff 00 */
+- {0x42, 0xc1},
+- {0x4f, 0x98},
+- {0x50, 0x98},
+- {0x51, 0x00},
+- {0x52, 0x28},
+- {0x53, 0x70},
+- {0x54, 0x98},
+- {0x58, 0x1a},
+- {0xff, 0x41}, /* read 41, write ff 00 */
+- {0x41, 0x40}, /* com16 */
+- {0x56, 0x40},
+- {0x55, 0x8f},
+- {0x10, 0x25}, /* aech - exposure high bits */
+- {0xff, 0x13}, /* read 13, write ff 00 */
+- {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
++static const u8 bridge_start_qvga[][2] = {
++ {0x1c, 0x00},
++ {0x1d, 0x40},
++ {0x1d, 0x02},
++ {0x1d, 0x00},
++ {0x1d, 0x01},
++ {0x1d, 0x4b},
++ {0x1d, 0x00},
++ {0xc0, 0x28},
++ {0xc1, 0x1e},
+ };
+-
+-static const u8 sensor_start_ov965x[][2] = {
+- {0x12, 0x62}, /* com7 - 30fps VGA YUV */
+- {0x36, 0xfa}, /* aref3 */
+- {0x69, 0x0a}, /* hv */
+- {0x8c, 0x89}, /* com22 */
+- {0x14, 0x28}, /* com9 */
+- {0x3e, 0x0c}, /* com14 */
+- {0x41, 0x40}, /* com16 */
+- {0x72, 0x00},
+- {0x73, 0x00},
+- {0x74, 0x3a},
+- {0x75, 0x35},
+- {0x76, 0x01},
+- {0xc7, 0x80}, /* com24 */
+- {0x03, 0x12}, /* vref */
+- {0x17, 0x16}, /* hstart */
+- {0x18, 0x02}, /* hstop */
+- {0x19, 0x01}, /* vstrt */
+- {0x1a, 0x3d}, /* vstop */
+- {0x32, 0xff}, /* href */
+- {0xc0, 0xaa},
+- {}
++static const u8 sensor_start_qvga[][2] = {
++ {0x12, 0x40},
++ {0x17, 0x3f},
++ {0x18, 0x50},
++ {0x19, 0x03},
++ {0x1a, 0x78},
++ {0x29, 0x50},
++ {0x2c, 0x78},
++ {0x65, 0x2f},
+ };
+
+-static const u8 bridge_start_ov965x[][2] = {
+- {0x94, 0xaa},
+- {0xf1, 0x60},
+- {0xe5, 0x04},
+- {0xc0, 0x50},
+- {0xc1, 0x3c},
+- {0x8c, 0x00},
+- {0x8d, 0x1c},
+- {0x34, 0x05},
+- {}
+-};
+-
+-static const u8 bridge_start_ov965x_vga[][2] = {
+- {0xc2, 0x0c},
+- {0xc3, 0xf9},
+- {0xda, 0x01},
+- {0x50, 0x00},
+- {0x51, 0xa0},
+- {0x52, 0x3c},
+- {0x53, 0x00},
+- {0x54, 0x00},
+- {0x55, 0x00},
+- {0x57, 0x00},
+- {0x5c, 0x00},
+- {0x5a, 0xa0},
+- {0x5b, 0x78},
+- {0x35, 0x02},
+- {0xd9, 0x10},
+- {0x94, 0x11},
+- {}
+-};
+-
+-static const u8 bridge_start_ov965x_cif[][2] = {
+- {0xc2, 0x4c},
+- {0xc3, 0xf9},
+- {0xda, 0x00},
+- {0x50, 0x00},
+- {0x51, 0xa0},
+- {0x52, 0x78},
+- {0x53, 0x00},
+- {0x54, 0x00},
+- {0x55, 0x00},
+- {0x57, 0x00},
+- {0x5c, 0x00},
+- {0x5a, 0x50},
+- {0x5b, 0x3c},
+- {0x35, 0x02},
+- {0xd9, 0x10},
+- {0x94, 0x11},
+- {}
+-};
+-
+-static const u8 sensor_start_ov965x_vga[][2] = {
+- {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
+- {0x1e, 0x04}, /* mvfp */
+- {0x13, 0xe0}, /* com8 */
+- {0x00, 0x00},
+- {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
+- {0x11, 0x03}, /* clkrc */
+- {0x6b, 0x5a}, /* dblv */
+- {0x6a, 0x05}, /* 50 Hz banding filter */
+- {0xc5, 0x07}, /* 60 Hz banding filter */
+- {0xa2, 0x4b}, /* bd50 */
+- {0xa3, 0x3e}, /* bd60 */
+-
+- {0x2d, 0x00}, /* advfl */
+- {}
+-};
+-
+-static const u8 sensor_start_ov965x_cif[][2] = {
+- {0x3b, 0xe4}, /* com11 - night mode 1/4 frame rate */
+- {0x1e, 0x04}, /* mvfp */
+- {0x13, 0xe0}, /* com8 */
+- {0x00, 0x00},
+- {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
+- {0x11, 0x01}, /* clkrc */
+- {0x6b, 0x5a}, /* dblv */
+- {0x6a, 0x02}, /* 50 Hz banding filter */
+- {0xc5, 0x03}, /* 60 Hz banding filter */
+- {0xa2, 0x96}, /* bd50 */
+- {0xa3, 0x7d}, /* bd60 */
+-
+- {0xff, 0x13}, /* read 13, write ff 00 */
+- {0x13, 0xe7},
+- {0x3a, 0x80}, /* tslb - yuyv */
+- {}
+-};
+-
+-static const u8 sensor_start_ov965x_2[][2] = {
+- {0xff, 0x42}, /* read 42, write ff 00 */
+- {0x42, 0xc1}, /* com17 - 50 Hz filter */
+- {}
+-};
+-
+-
+ static void ov534_reg_write(struct gspca_dev *gspca_dev, u16 reg, u8 val)
+ {
+ struct usb_device *udev = gspca_dev->dev;
+@@ -753,39 +622,184 @@ static void sccb_w_array(struct gspca_dev *gspca_dev,
+ }
+ }
+
+-/* set framerate */
+-static void ov534_set_frame_rate(struct gspca_dev *gspca_dev)
++/* ov772x specific controls */
++static void set_frame_rate(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- int fr = sd->frame_rate;
++ int i;
++ struct rate_s {
++ u8 fps;
++ u8 r11;
++ u8 r0d;
++ u8 re5;
++ };
++ const struct rate_s *r;
++ static const struct rate_s rate_0[] = { /* 640x480 */
++ {60, 0x01, 0xc1, 0x04},
++ {50, 0x01, 0x41, 0x02},
++ {40, 0x02, 0xc1, 0x04},
++ {30, 0x04, 0x81, 0x02},
++ {15, 0x03, 0x41, 0x04},
++ };
++ static const struct rate_s rate_1[] = { /* 320x240 */
++ {125, 0x02, 0x81, 0x02},
++ {100, 0x02, 0xc1, 0x04},
++ {75, 0x03, 0xc1, 0x04},
++ {60, 0x04, 0xc1, 0x04},
++ {50, 0x02, 0x41, 0x04},
++ {40, 0x03, 0x41, 0x04},
++ {30, 0x04, 0x41, 0x04},
++ };
++
++ if (gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv == 0) {
++ r = rate_0;
++ i = ARRAY_SIZE(rate_0);
++ } else {
++ r = rate_1;
++ i = ARRAY_SIZE(rate_1);
++ }
++ while (--i > 0) {
++ if (sd->frame_rate >= r->fps)
++ break;
++ r++;
++ }
+
+- switch (fr) {
+- case 50:
+- sccb_reg_write(gspca_dev, 0x11, 0x01);
+- sccb_reg_write(gspca_dev, 0x0d, 0x41);
+- ov534_reg_write(gspca_dev, 0xe5, 0x02);
++ sccb_reg_write(gspca_dev, 0x11, r->r11);
++ sccb_reg_write(gspca_dev, 0x0d, r->r0d);
++ ov534_reg_write(gspca_dev, 0xe5, r->re5);
++
++ PDEBUG(D_PROBE, "frame_rate: %d", r->fps);
++}
++
++static void setbrightness(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sccb_reg_write(gspca_dev, 0x9B, sd->brightness);
++}
++
++static void setcontrast(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sccb_reg_write(gspca_dev, 0x9C, sd->contrast);
++}
++
++static void setgain(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ u8 val;
++
++ val = sd->gain;
++ switch (val & 0x30) {
++ case 0x00:
++ val &= 0x0f;
+ break;
+- case 40:
+- sccb_reg_write(gspca_dev, 0x11, 0x02);
+- sccb_reg_write(gspca_dev, 0x0d, 0xc1);
+- ov534_reg_write(gspca_dev, 0xe5, 0x04);
++ case 0x10:
++ val &= 0x0f;
++ val |= 0x30;
+ break;
+-/* case 30: */
+- default:
+- fr = 30;
+- sccb_reg_write(gspca_dev, 0x11, 0x04);
+- sccb_reg_write(gspca_dev, 0x0d, 0x81);
+- ov534_reg_write(gspca_dev, 0xe5, 0x02);
++ case 0x20:
++ val &= 0x0f;
++ val |= 0x70;
+ break;
+- case 15:
+- sccb_reg_write(gspca_dev, 0x11, 0x03);
+- sccb_reg_write(gspca_dev, 0x0d, 0x41);
+- ov534_reg_write(gspca_dev, 0xe5, 0x04);
++ default:
++/* case 0x30: */
++ val &= 0x0f;
++ val |= 0xf0;
+ break;
+ }
++ sccb_reg_write(gspca_dev, 0x00, val);
++}
++
++static void setexposure(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ u8 val;
++
++ val = sd->exposure;
++ sccb_reg_write(gspca_dev, 0x08, val >> 7);
++ sccb_reg_write(gspca_dev, 0x10, val << 1);
++}
++
++static void setredblc(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sccb_reg_write(gspca_dev, 0x43, sd->redblc);
++}
++
++static void setblueblc(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sccb_reg_write(gspca_dev, 0x42, sd->blueblc);
++}
++
++static void sethue(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sccb_reg_write(gspca_dev, 0x01, sd->hue);
++}
++
++static void setautogain(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (sd->autogain) {
++ sccb_reg_write(gspca_dev, 0x13, 0xf7); /* AGC,AEC,AWB ON */
++ sccb_reg_write(gspca_dev, 0x64,
++ sccb_reg_read(gspca_dev, 0x64) | 0x03);
++ } else {
++ sccb_reg_write(gspca_dev, 0x13, 0xf0); /* AGC,AEC,AWB OFF */
++ sccb_reg_write(gspca_dev, 0x64,
++ sccb_reg_read(gspca_dev, 0x64) & 0xfc);
++ }
++}
++
++static void setawb(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (sd->awb)
++ sccb_reg_write(gspca_dev, 0x63, 0xe0); /* AWB on */
++ else
++ sccb_reg_write(gspca_dev, 0x63, 0xaa); /* AWB off */
++}
++
++static void setsharpness(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ u8 val;
++
++ val = sd->sharpness;
++ sccb_reg_write(gspca_dev, 0x91, val); /* vga noise */
++ sccb_reg_write(gspca_dev, 0x8e, val); /* qvga noise */
++}
+
+- sd->frame_rate = fr;
+- PDEBUG(D_PROBE, "frame_rate: %d", fr);
++static void sethflip(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (sd->hflip == 0)
++ sccb_reg_write(gspca_dev, 0x0c,
++ sccb_reg_read(gspca_dev, 0x0c) | 0x40);
++ else
++ sccb_reg_write(gspca_dev, 0x0c,
++ sccb_reg_read(gspca_dev, 0x0c) & 0xbf);
++}
++
++static void setvflip(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (sd->vflip == 0)
++ sccb_reg_write(gspca_dev, 0x0c,
++ sccb_reg_read(gspca_dev, 0x0c) | 0x80);
++ else
++ sccb_reg_write(gspca_dev, 0x0c,
++ sccb_reg_read(gspca_dev, 0x0c) & 0x7f);
+ }
+
+ /* this function is called at probe time */
+@@ -795,21 +809,42 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
+- sd->sensor = id->driver_info;
+-
+ cam = &gspca_dev->cam;
+
+- if (sd->sensor == SENSOR_OV772X) {
+- cam->cam_mode = vga_yuyv_mode;
+- cam->nmodes = ARRAY_SIZE(vga_yuyv_mode);
+-
+- cam->bulk = 1;
+- cam->bulk_size = 16384;
+- cam->bulk_nurbs = 2;
+- } else { /* ov965x */
+- cam->cam_mode = vga_jpeg_mode;
+- cam->nmodes = ARRAY_SIZE(vga_jpeg_mode);
+- }
++ cam->cam_mode = ov772x_mode;
++ cam->nmodes = ARRAY_SIZE(ov772x_mode);
++ cam->mode_framerates = ov772x_framerates;
++
++ cam->bulk = 1;
++ cam->bulk_size = 16384;
++ cam->bulk_nurbs = 2;
++
++ sd->frame_rate = 30;
++
++ sd->brightness = BRIGHTNESS_DEF;
++ sd->contrast = CONTRAST_DEF;
++ sd->gain = GAIN_DEF;
++ sd->exposure = EXPO_DEF;
++ sd->redblc = RED_BALANCE_DEF;
++ sd->blueblc = BLUE_BALANCE_DEF;
++ sd->hue = HUE_DEF;
++#if AUTOGAIN_DEF != 0
++ sd->autogain = AUTOGAIN_DEF;
++#else
++ gspca_dev->ctrl_inac |= (1 << AWB_IDX);
++#endif
++#if AWB_DEF != 0
++ sd->awb = AWB_DEF
++#endif
++#if SHARPNESS_DEF != 0
++ sd->sharpness = SHARPNESS_DEF;
++#endif
++#if HFLIP_DEF != 0
++ sd->hflip = HFLIP_DEF;
++#endif
++#if VFLIP_DEF != 0
++ sd->vflip = VFLIP_DEF;
++#endif
+
+ return 0;
+ }
+@@ -817,12 +852,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ /* this function is called at probe and resume time */
+ static int sd_init(struct gspca_dev *gspca_dev)
+ {
+- struct sd *sd = (struct sd *) gspca_dev;
+ u16 sensor_id;
+- static const u8 sensor_addr[2] = {
+- 0x42, /* 0 SENSOR_OV772X */
+- 0x60, /* 1 SENSOR_OV965X */
+- };
+
+ /* reset bridge */
+ ov534_reg_write(gspca_dev, 0xe7, 0x3a);
+@@ -830,8 +860,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
+ msleep(100);
+
+ /* initialize the sensor address */
+- ov534_reg_write(gspca_dev, OV534_REG_ADDRESS,
+- sensor_addr[sd->sensor]);
++ ov534_reg_write(gspca_dev, OV534_REG_ADDRESS, 0x42);
+
+ /* reset sensor */
+ sccb_reg_write(gspca_dev, 0x12, 0x80);
+@@ -845,90 +874,58 @@ static int sd_init(struct gspca_dev *gspca_dev)
+ PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id);
+
+ /* initialize */
+- switch (sd->sensor) {
+- case SENSOR_OV772X:
+- reg_w_array(gspca_dev, bridge_init_ov722x,
+- ARRAY_SIZE(bridge_init_ov722x));
+- ov534_set_led(gspca_dev, 1);
+- sccb_w_array(gspca_dev, sensor_init_ov722x,
+- ARRAY_SIZE(sensor_init_ov722x));
+- ov534_reg_write(gspca_dev, 0xe0, 0x09);
+- ov534_set_led(gspca_dev, 0);
+- ov534_set_frame_rate(gspca_dev);
+- break;
+- default:
+-/* case SENSOR_OV965X: */
+- reg_w_array(gspca_dev, bridge_init_ov965x,
+- ARRAY_SIZE(bridge_init_ov965x));
+- sccb_w_array(gspca_dev, sensor_init_ov965x,
+- ARRAY_SIZE(sensor_init_ov965x));
+- reg_w_array(gspca_dev, bridge_init_ov965x_2,
+- ARRAY_SIZE(bridge_init_ov965x_2));
+- sccb_w_array(gspca_dev, sensor_init_ov965x_2,
+- ARRAY_SIZE(sensor_init_ov965x_2));
+- ov534_reg_write(gspca_dev, 0xe0, 0x00);
+- ov534_reg_write(gspca_dev, 0xe0, 0x01);
+- ov534_set_led(gspca_dev, 0);
+- ov534_reg_write(gspca_dev, 0xe0, 0x00);
+- }
++ reg_w_array(gspca_dev, bridge_init,
++ ARRAY_SIZE(bridge_init));
++ ov534_set_led(gspca_dev, 1);
++ sccb_w_array(gspca_dev, sensor_init,
++ ARRAY_SIZE(sensor_init));
++ ov534_reg_write(gspca_dev, 0xe0, 0x09);
++ ov534_set_led(gspca_dev, 0);
++ set_frame_rate(gspca_dev);
+
+ return 0;
+ }
+
+ static int sd_start(struct gspca_dev *gspca_dev)
+ {
+- struct sd *sd = (struct sd *) gspca_dev;
+ int mode;
+
+- switch (sd->sensor) {
+- case SENSOR_OV772X:
+- ov534_set_led(gspca_dev, 1);
+- ov534_reg_write(gspca_dev, 0xe0, 0x00);
+- break;
+- default:
+-/* case SENSOR_OV965X: */
+-
+- sccb_w_array(gspca_dev, sensor_start_ov965x,
+- ARRAY_SIZE(sensor_start_ov965x));
+- reg_w_array(gspca_dev, bridge_start_ov965x,
+- ARRAY_SIZE(bridge_start_ov965x));
+- mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+- if (mode != 0) { /* 320x240 */
+- reg_w_array(gspca_dev, bridge_start_ov965x_cif,
+- ARRAY_SIZE(bridge_start_ov965x_cif));
+- sccb_w_array(gspca_dev, sensor_start_ov965x_cif,
+- ARRAY_SIZE(sensor_start_ov965x_cif));
+- } else { /* 640x480 */
+- reg_w_array(gspca_dev, bridge_start_ov965x_vga,
+- ARRAY_SIZE(bridge_start_ov965x_vga));
+- sccb_w_array(gspca_dev, sensor_start_ov965x_vga,
+- ARRAY_SIZE(sensor_start_ov965x_vga));
+- }
+- sccb_w_array(gspca_dev, sensor_start_ov965x_2,
+- ARRAY_SIZE(sensor_start_ov965x_2));
+- ov534_reg_write(gspca_dev, 0xe0, 0x00);
+- ov534_reg_write(gspca_dev, 0xe0, 0x00);
+- ov534_set_led(gspca_dev, 1);
++ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
++ if (mode != 0) { /* 320x240 */
++ reg_w_array(gspca_dev, bridge_start_qvga,
++ ARRAY_SIZE(bridge_start_qvga));
++ sccb_w_array(gspca_dev, sensor_start_qvga,
++ ARRAY_SIZE(sensor_start_qvga));
++ } else { /* 640x480 */
++ reg_w_array(gspca_dev, bridge_start_vga,
++ ARRAY_SIZE(bridge_start_vga));
++ sccb_w_array(gspca_dev, sensor_start_vga,
++ ARRAY_SIZE(sensor_start_vga));
+ }
++ set_frame_rate(gspca_dev);
++
++ setautogain(gspca_dev);
++ setawb(gspca_dev);
++ setgain(gspca_dev);
++ setredblc(gspca_dev);
++ setblueblc(gspca_dev);
++ sethue(gspca_dev);
++ setexposure(gspca_dev);
++ setbrightness(gspca_dev);
++ setcontrast(gspca_dev);
++ setsharpness(gspca_dev);
++ setvflip(gspca_dev);
++ sethflip(gspca_dev);
++
++ ov534_set_led(gspca_dev, 1);
++ ov534_reg_write(gspca_dev, 0xe0, 0x00);
+ return 0;
+ }
+
+ static void sd_stopN(struct gspca_dev *gspca_dev)
+ {
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- switch (sd->sensor) {
+- case SENSOR_OV772X:
+- ov534_reg_write(gspca_dev, 0xe0, 0x09);
+- ov534_set_led(gspca_dev, 0);
+- break;
+- default:
+-/* case SENSOR_OV965X: */
+- ov534_reg_write(gspca_dev, 0xe0, 0x01);
+- ov534_set_led(gspca_dev, 0);
+- ov534_reg_write(gspca_dev, 0xe0, 0x00);
+- break;
+- }
++ ov534_reg_write(gspca_dev, 0xe0, 0x09);
++ ov534_set_led(gspca_dev, 0);
+ }
+
+ /* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
+@@ -941,18 +938,16 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
+ #define UVC_STREAM_EOF (1 << 1)
+ #define UVC_STREAM_FID (1 << 0)
+
+-static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame,
+- __u8 *data, int len)
++static void sd_pkt_scan(struct gspca_dev *gspca_dev,
++ u8 *data, int len)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u32 this_pts;
+ u16 this_fid;
+ int remaining_len = len;
+- int payload_len;
+
+- payload_len = gspca_dev->cam.bulk ? 2048 : 2040;
+ do {
+- len = min(remaining_len, payload_len);
++ len = min(remaining_len, 2048);
+
+ /* Payloads are prefixed with a UVC-style header. We
+ consider a frame to start when the FID toggles, or the PTS
+@@ -983,32 +978,40 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev, struct gspca_frame *frame,
+ /* If PTS or FID has changed, start a new frame. */
+ if (this_pts != sd->last_pts || this_fid != sd->last_fid) {
+ if (gspca_dev->last_packet_type == INTER_PACKET)
+- frame = gspca_frame_add(gspca_dev,
+- LAST_PACKET, frame,
+- NULL, 0);
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ NULL, 0);
+ sd->last_pts = this_pts;
+ sd->last_fid = this_fid;
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ data + 12, len - 12);
+ /* If this packet is marked as EOF, end the frame */
+ } else if (data[1] & UVC_STREAM_EOF) {
++ struct gspca_frame *frame;
++
+ sd->last_pts = 0;
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+- data + 12, len - 12);
++ frame = gspca_get_i_frame(gspca_dev);
++ if (frame == NULL)
++ goto discard;
++ if (frame->data_end - frame->data + (len - 12) !=
++ gspca_dev->width * gspca_dev->height * 2) {
++ PDEBUG(D_PACK, "wrong sized frame");
++ goto discard;
++ }
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ data + 12, len - 12);
+ } else {
+
+ /* Add the data from this payload */
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+- data + 12, len - 12);
++ gspca_frame_add(gspca_dev, INTER_PACKET,
++ data + 12, len - 12);
+ }
+
+-
+ /* Done this payload */
+ goto scan_next;
+
+ discard:
+ /* Discard data until a new frame starts. */
+- gspca_frame_add(gspca_dev, DISCARD_PACKET, frame, NULL, 0);
++ gspca_dev->last_packet_type = DISCARD_PACKET;
+
+ scan_next:
+ remaining_len -= len;
+@@ -1016,6 +1019,232 @@ scan_next:
+ } while (remaining_len > 0);
+ }
+
++/* controls */
++static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->gain = val;
++ if (gspca_dev->streaming)
++ setgain(gspca_dev);
++ return 0;
++}
++
++static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->gain;
++ return 0;
++}
++
++static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->exposure = val;
++ if (gspca_dev->streaming)
++ setexposure(gspca_dev);
++ return 0;
++}
++
++static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->exposure;
++ return 0;
++}
++
++static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->brightness = val;
++ if (gspca_dev->streaming)
++ setbrightness(gspca_dev);
++ return 0;
++}
++
++static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->brightness;
++ return 0;
++}
++
++static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->contrast = val;
++ if (gspca_dev->streaming)
++ setcontrast(gspca_dev);
++ return 0;
++}
++
++static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->contrast;
++ return 0;
++}
++
++static int sd_setredblc(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->redblc = val;
++ if (gspca_dev->streaming)
++ setredblc(gspca_dev);
++ return 0;
++}
++
++static int sd_getredblc(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->redblc;
++ return 0;
++}
++
++static int sd_setblueblc(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->blueblc = val;
++ if (gspca_dev->streaming)
++ setblueblc(gspca_dev);
++ return 0;
++}
++
++static int sd_getblueblc(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->blueblc;
++ return 0;
++}
++
++static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->hue = val;
++ if (gspca_dev->streaming)
++ sethue(gspca_dev);
++ return 0;
++}
++
++static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->hue;
++ return 0;
++}
++
++static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->autogain = val;
++
++ if (gspca_dev->streaming) {
++
++ /* the auto white balance control works only
++ * when auto gain is set */
++ if (val)
++ gspca_dev->ctrl_inac &= ~(1 << AWB_IDX);
++ else
++ gspca_dev->ctrl_inac |= (1 << AWB_IDX);
++ setautogain(gspca_dev);
++ }
++ return 0;
++}
++
++static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->autogain;
++ return 0;
++}
++
++static int sd_setawb(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->awb = val;
++ if (gspca_dev->streaming)
++ setawb(gspca_dev);
++ return 0;
++}
++
++static int sd_getawb(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->awb;
++ return 0;
++}
++
++static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->sharpness = val;
++ if (gspca_dev->streaming)
++ setsharpness(gspca_dev);
++ return 0;
++}
++
++static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->sharpness;
++ return 0;
++}
++
++static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->hflip = val;
++ if (gspca_dev->streaming)
++ sethflip(gspca_dev);
++ return 0;
++}
++
++static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->hflip;
++ return 0;
++}
++
++static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->vflip = val;
++ if (gspca_dev->streaming)
++ setvflip(gspca_dev);
++ return 0;
++}
++
++static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->vflip;
++ return 0;
++}
++
+ /* get stream parameters (framerate) */
+ static int sd_get_streamparm(struct gspca_dev *gspca_dev,
+ struct v4l2_streamparm *parm)
+@@ -1047,7 +1276,8 @@ static int sd_set_streamparm(struct gspca_dev *gspca_dev,
+
+ /* Set requested framerate */
+ sd->frame_rate = tpf->denominator / tpf->numerator;
+- ov534_set_frame_rate(gspca_dev);
++ if (gspca_dev->streaming)
++ set_frame_rate(gspca_dev);
+
+ /* Return the actual framerate */
+ tpf->numerator = 1;
+@@ -1072,8 +1302,7 @@ static const struct sd_desc sd_desc = {
+
+ /* -- module initialisation -- */
+ static const __devinitdata struct usb_device_id device_table[] = {
+- {USB_DEVICE(0x06f8, 0x3003), .driver_info = SENSOR_OV965X},
+- {USB_DEVICE(0x1415, 0x2000), .driver_info = SENSOR_OV772X},
++ {USB_DEVICE(0x1415, 0x2000)},
+ {}
+ };
+
+@@ -1083,7 +1312,7 @@ MODULE_DEVICE_TABLE(usb, device_table);
+ static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id)
+ {
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+- THIS_MODULE);
++ THIS_MODULE);
+ }
+
+ static struct usb_driver sd_driver = {
+@@ -1101,6 +1330,7 @@ static struct usb_driver sd_driver = {
+ static int __init sd_mod_init(void)
+ {
+ int ret;
++
+ ret = usb_register(&sd_driver);
+ if (ret < 0)
+ return ret;
+diff --git a/drivers/media/video/gspca/ov534_9.c b/drivers/media/video/gspca/ov534_9.c
+new file mode 100644
+index 0000000..bbe5a03
+--- /dev/null
++++ b/drivers/media/video/gspca/ov534_9.c
+@@ -0,0 +1,1477 @@
++/*
++ * ov534-ov965x gspca driver
++ *
++ * Copyright (C) 2009-2010 Jean-Francois Moine http://moinejf.free.fr
++ * Copyright (C) 2008 Antonio Ospite <ospite@studenti.unina.it>
++ * Copyright (C) 2008 Jim Paris <jim@jtan.com>
++ *
++ * Based on a prototype written by Mark Ferrell <majortrips@gmail.com>
++ * USB protocol reverse engineered by Jim Paris <jim@jtan.com>
++ * https://jim.sh/svn/jim/devl/playstation/ps3/eye/test/
++ *
++ * 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
++ * 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
++ */
++
++#define MODULE_NAME "ov534_9"
++
++#include "gspca.h"
++
++#define OV534_REG_ADDRESS 0xf1 /* sensor address */
++#define OV534_REG_SUBADDR 0xf2
++#define OV534_REG_WRITE 0xf3
++#define OV534_REG_READ 0xf4
++#define OV534_REG_OPERATION 0xf5
++#define OV534_REG_STATUS 0xf6
++
++#define OV534_OP_WRITE_3 0x37
++#define OV534_OP_WRITE_2 0x33
++#define OV534_OP_READ_2 0xf9
++
++#define CTRL_TIMEOUT 500
++
++MODULE_AUTHOR("Jean-Francois Moine <moinejf@free.fr>");
++MODULE_DESCRIPTION("GSPCA/OV534_9 USB Camera Driver");
++MODULE_LICENSE("GPL");
++
++/* specific webcam descriptor */
++struct sd {
++ struct gspca_dev gspca_dev; /* !! must be the first item */
++ __u32 last_pts;
++ u8 last_fid;
++
++ u8 brightness;
++ u8 contrast;
++ u8 autogain;
++ u8 exposure;
++ s8 sharpness;
++ u8 satur;
++ u8 freq;
++};
++
++/* V4L2 controls supported by the driver */
++static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setsatur(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getsatur(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
++
++static const struct ctrl sd_ctrls[] = {
++ { /* 0 */
++ {
++ .id = V4L2_CID_BRIGHTNESS,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Brightness",
++ .minimum = 0,
++ .maximum = 15,
++ .step = 1,
++#define BRIGHTNESS_DEF 7
++ .default_value = BRIGHTNESS_DEF,
++ },
++ .set = sd_setbrightness,
++ .get = sd_getbrightness,
++ },
++ { /* 1 */
++ {
++ .id = V4L2_CID_CONTRAST,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Contrast",
++ .minimum = 0,
++ .maximum = 15,
++ .step = 1,
++#define CONTRAST_DEF 3
++ .default_value = CONTRAST_DEF,
++ },
++ .set = sd_setcontrast,
++ .get = sd_getcontrast,
++ },
++ { /* 2 */
++ {
++ .id = V4L2_CID_AUTOGAIN,
++ .type = V4L2_CTRL_TYPE_BOOLEAN,
++ .name = "Autogain",
++ .minimum = 0,
++ .maximum = 1,
++ .step = 1,
++#define AUTOGAIN_DEF 1
++ .default_value = AUTOGAIN_DEF,
++ },
++ .set = sd_setautogain,
++ .get = sd_getautogain,
++ },
++#define EXPO_IDX 3
++ { /* 3 */
++ {
++ .id = V4L2_CID_EXPOSURE,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Exposure",
++ .minimum = 0,
++ .maximum = 3,
++ .step = 1,
++#define EXPO_DEF 0
++ .default_value = EXPO_DEF,
++ },
++ .set = sd_setexposure,
++ .get = sd_getexposure,
++ },
++ { /* 4 */
++ {
++ .id = V4L2_CID_SHARPNESS,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Sharpness",
++ .minimum = -1, /* -1 = auto */
++ .maximum = 4,
++ .step = 1,
++#define SHARPNESS_DEF -1
++ .default_value = SHARPNESS_DEF,
++ },
++ .set = sd_setsharpness,
++ .get = sd_getsharpness,
++ },
++ { /* 5 */
++ {
++ .id = V4L2_CID_SATURATION,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Saturation",
++ .minimum = 0,
++ .maximum = 4,
++ .step = 1,
++#define SATUR_DEF 2
++ .default_value = SATUR_DEF,
++ },
++ .set = sd_setsatur,
++ .get = sd_getsatur,
++ },
++ {
++ {
++ .id = V4L2_CID_POWER_LINE_FREQUENCY,
++ .type = V4L2_CTRL_TYPE_MENU,
++ .name = "Light frequency filter",
++ .minimum = 0,
++ .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
++ .step = 1,
++#define FREQ_DEF 0
++ .default_value = FREQ_DEF,
++ },
++ .set = sd_setfreq,
++ .get = sd_getfreq,
++ },
++};
++
++static const struct v4l2_pix_format ov965x_mode[] = {
++#define QVGA_MODE 0
++ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
++ .bytesperline = 320,
++ .sizeimage = 320 * 240 * 3 / 8 + 590,
++ .colorspace = V4L2_COLORSPACE_JPEG},
++#define VGA_MODE 1
++ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
++ .bytesperline = 640,
++ .sizeimage = 640 * 480 * 3 / 8 + 590,
++ .colorspace = V4L2_COLORSPACE_JPEG},
++#define SVGA_MODE 2
++ {800, 600, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
++ .bytesperline = 800,
++ .sizeimage = 800 * 600 * 3 / 8 + 590,
++ .colorspace = V4L2_COLORSPACE_JPEG},
++#define XGA_MODE 3
++ {1024, 768, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
++ .bytesperline = 1024,
++ .sizeimage = 1024 * 768 * 3 / 8 + 590,
++ .colorspace = V4L2_COLORSPACE_JPEG},
++#define SXGA_MODE 4
++ {1280, 1024, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
++ .bytesperline = 1280,
++ .sizeimage = 1280 * 1024 * 3 / 8 + 590,
++ .colorspace = V4L2_COLORSPACE_JPEG},
++};
++
++static const u8 bridge_init[][2] = {
++ {0x88, 0xf8},
++ {0x89, 0xff},
++ {0x76, 0x03},
++ {0x92, 0x03},
++ {0x95, 0x10},
++ {0xe2, 0x00},
++ {0xe7, 0x3e},
++ {0x8d, 0x1c},
++ {0x8e, 0x00},
++ {0x8f, 0x00},
++ {0x1f, 0x00},
++ {0xc3, 0xf9},
++ {0x89, 0xff},
++ {0x88, 0xf8},
++ {0x76, 0x03},
++ {0x92, 0x01},
++ {0x93, 0x18},
++ {0x1c, 0x0a},
++ {0x1d, 0x48},
++ {0xc0, 0x50},
++ {0xc1, 0x3c},
++ {0x34, 0x05},
++ {0xc2, 0x0c},
++ {0xc3, 0xf9},
++ {0x34, 0x05},
++ {0xe7, 0x2e},
++ {0x31, 0xf9},
++ {0x35, 0x02},
++ {0xd9, 0x10},
++ {0x25, 0x42},
++ {0x94, 0x11},
++};
++
++static const u8 sensor_init[][2] = {
++ {0x12, 0x80}, /* com7 - SSCB reset */
++ {0x00, 0x00}, /* gain */
++ {0x01, 0x80}, /* blue */
++ {0x02, 0x80}, /* red */
++ {0x03, 0x1b}, /* vref */
++ {0x04, 0x03}, /* com1 - exposure low bits */
++ {0x0b, 0x57}, /* ver */
++ {0x0e, 0x61}, /* com5 */
++ {0x0f, 0x42}, /* com6 */
++ {0x11, 0x00}, /* clkrc */
++ {0x12, 0x02}, /* com7 - 15fps VGA YUYV */
++ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
++ {0x14, 0x28}, /* com9 */
++ {0x16, 0x24}, /* reg16 */
++ {0x17, 0x1d}, /* hstart*/
++ {0x18, 0xbd}, /* hstop */
++ {0x19, 0x01}, /* vstrt */
++ {0x1a, 0x81}, /* vstop*/
++ {0x1e, 0x04}, /* mvfp */
++ {0x24, 0x3c}, /* aew */
++ {0x25, 0x36}, /* aeb */
++ {0x26, 0x71}, /* vpt */
++ {0x27, 0x08}, /* bbias */
++ {0x28, 0x08}, /* gbbias */
++ {0x29, 0x15}, /* gr com */
++ {0x2a, 0x00}, /* exhch */
++ {0x2b, 0x00}, /* exhcl */
++ {0x2c, 0x08}, /* rbias */
++ {0x32, 0xff}, /* href */
++ {0x33, 0x00}, /* chlf */
++ {0x34, 0x3f}, /* aref1 */
++ {0x35, 0x00}, /* aref2 */
++ {0x36, 0xf8}, /* aref3 */
++ {0x38, 0x72}, /* adc2 */
++ {0x39, 0x57}, /* aref4 */
++ {0x3a, 0x80}, /* tslb - yuyv */
++ {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
++ {0x3d, 0x99}, /* com13 */
++ {0x3f, 0xc1}, /* edge */
++ {0x40, 0xc0}, /* com15 */
++ {0x41, 0x40}, /* com16 */
++ {0x42, 0xc0}, /* com17 */
++ {0x43, 0x0a}, /* rsvd */
++ {0x44, 0xf0},
++ {0x45, 0x46},
++ {0x46, 0x62},
++ {0x47, 0x2a},
++ {0x48, 0x3c},
++ {0x4a, 0xfc},
++ {0x4b, 0xfc},
++ {0x4c, 0x7f},
++ {0x4d, 0x7f},
++ {0x4e, 0x7f},
++ {0x4f, 0x98}, /* matrix */
++ {0x50, 0x98},
++ {0x51, 0x00},
++ {0x52, 0x28},
++ {0x53, 0x70},
++ {0x54, 0x98},
++ {0x58, 0x1a}, /* matrix coef sign */
++ {0x59, 0x85}, /* AWB control */
++ {0x5a, 0xa9},
++ {0x5b, 0x64},
++ {0x5c, 0x84},
++ {0x5d, 0x53},
++ {0x5e, 0x0e},
++ {0x5f, 0xf0}, /* AWB blue limit */
++ {0x60, 0xf0}, /* AWB red limit */
++ {0x61, 0xf0}, /* AWB green limit */
++ {0x62, 0x00}, /* lcc1 */
++ {0x63, 0x00}, /* lcc2 */
++ {0x64, 0x02}, /* lcc3 */
++ {0x65, 0x16}, /* lcc4 */
++ {0x66, 0x01}, /* lcc5 */
++ {0x69, 0x02}, /* hv */
++ {0x6b, 0x5a}, /* dbvl */
++ {0x6c, 0x04},
++ {0x6d, 0x55},
++ {0x6e, 0x00},
++ {0x6f, 0x9d},
++ {0x70, 0x21}, /* dnsth */
++ {0x71, 0x78},
++ {0x72, 0x00}, /* poidx */
++ {0x73, 0x01}, /* pckdv */
++ {0x74, 0x3a}, /* xindx */
++ {0x75, 0x35}, /* yindx */
++ {0x76, 0x01},
++ {0x77, 0x02},
++ {0x7a, 0x12}, /* gamma curve */
++ {0x7b, 0x08},
++ {0x7c, 0x16},
++ {0x7d, 0x30},
++ {0x7e, 0x5e},
++ {0x7f, 0x72},
++ {0x80, 0x82},
++ {0x81, 0x8e},
++ {0x82, 0x9a},
++ {0x83, 0xa4},
++ {0x84, 0xac},
++ {0x85, 0xb8},
++ {0x86, 0xc3},
++ {0x87, 0xd6},
++ {0x88, 0xe6},
++ {0x89, 0xf2},
++ {0x8a, 0x03},
++ {0x8c, 0x89}, /* com19 */
++ {0x14, 0x28}, /* com9 */
++ {0x90, 0x7d},
++ {0x91, 0x7b},
++ {0x9d, 0x03}, /* lcc6 */
++ {0x9e, 0x04}, /* lcc7 */
++ {0x9f, 0x7a},
++ {0xa0, 0x79},
++ {0xa1, 0x40}, /* aechm */
++ {0xa4, 0x50}, /* com21 */
++ {0xa5, 0x68}, /* com26 */
++ {0xa6, 0x4a}, /* AWB green */
++ {0xa8, 0xc1}, /* refa8 */
++ {0xa9, 0xef}, /* refa9 */
++ {0xaa, 0x92},
++ {0xab, 0x04},
++ {0xac, 0x80}, /* black level control */
++ {0xad, 0x80},
++ {0xae, 0x80},
++ {0xaf, 0x80},
++ {0xb2, 0xf2},
++ {0xb3, 0x20},
++ {0xb4, 0x20}, /* ctrlb4 */
++ {0xb5, 0x00},
++ {0xb6, 0xaf},
++ {0xbb, 0xae},
++ {0xbc, 0x7f}, /* ADC channel offsets */
++ {0xdb, 0x7f},
++ {0xbe, 0x7f},
++ {0xbf, 0x7f},
++ {0xc0, 0xe2},
++ {0xc1, 0xc0},
++ {0xc2, 0x01},
++ {0xc3, 0x4e},
++ {0xc6, 0x85},
++ {0xc7, 0x80}, /* com24 */
++ {0xc9, 0xe0},
++ {0xca, 0xe8},
++ {0xcb, 0xf0},
++ {0xcc, 0xd8},
++ {0xcd, 0xf1},
++ {0x4f, 0x98}, /* matrix */
++ {0x50, 0x98},
++ {0x51, 0x00},
++ {0x52, 0x28},
++ {0x53, 0x70},
++ {0x54, 0x98},
++ {0x58, 0x1a},
++ {0xff, 0x41}, /* read 41, write ff 00 */
++ {0x41, 0x40}, /* com16 */
++
++ {0xc5, 0x03}, /* 60 Hz banding filter */
++ {0x6a, 0x02}, /* 50 Hz banding filter */
++
++ {0x12, 0x62}, /* com7 - 30fps VGA YUV */
++ {0x36, 0xfa}, /* aref3 */
++ {0x69, 0x0a}, /* hv */
++ {0x8c, 0x89}, /* com22 */
++ {0x14, 0x28}, /* com9 */
++ {0x3e, 0x0c},
++ {0x41, 0x40}, /* com16 */
++ {0x72, 0x00},
++ {0x73, 0x00},
++ {0x74, 0x3a},
++ {0x75, 0x35},
++ {0x76, 0x01},
++ {0xc7, 0x80},
++ {0x03, 0x12}, /* vref */
++ {0x17, 0x16}, /* hstart */
++ {0x18, 0x02}, /* hstop */
++ {0x19, 0x01}, /* vstrt */
++ {0x1a, 0x3d}, /* vstop */
++ {0x32, 0xff}, /* href */
++ {0xc0, 0xaa},
++};
++
++static const u8 bridge_init_2[][2] = {
++ {0x94, 0xaa},
++ {0xf1, 0x60},
++ {0xe5, 0x04},
++ {0xc0, 0x50},
++ {0xc1, 0x3c},
++ {0x8c, 0x00},
++ {0x8d, 0x1c},
++ {0x34, 0x05},
++
++ {0xc2, 0x0c},
++ {0xc3, 0xf9},
++ {0xda, 0x01},
++ {0x50, 0x00},
++ {0x51, 0xa0},
++ {0x52, 0x3c},
++ {0x53, 0x00},
++ {0x54, 0x00},
++ {0x55, 0x00},
++ {0x57, 0x00},
++ {0x5c, 0x00},
++ {0x5a, 0xa0},
++ {0x5b, 0x78},
++ {0x35, 0x02},
++ {0xd9, 0x10},
++ {0x94, 0x11},
++};
++
++static const u8 sensor_init_2[][2] = {
++ {0x3b, 0xc4},
++ {0x1e, 0x04}, /* mvfp */
++ {0x13, 0xe0}, /* com8 */
++ {0x00, 0x00}, /* gain */
++ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
++ {0x11, 0x03}, /* clkrc */
++ {0x6b, 0x5a}, /* dblv */
++ {0x6a, 0x05},
++ {0xc5, 0x07},
++ {0xa2, 0x4b},
++ {0xa3, 0x3e},
++ {0x2d, 0x00},
++ {0xff, 0x42}, /* read 42, write ff 00 */
++ {0x42, 0xc0}, /* com17 */
++ {0x2d, 0x00},
++ {0xff, 0x42}, /* read 42, write ff 00 */
++ {0x42, 0xc1}, /* com17 */
++/* sharpness */
++ {0x3f, 0x01},
++ {0xff, 0x42}, /* read 42, write ff 00 */
++ {0x42, 0xc1}, /* com17 */
++/* saturation */
++ {0x4f, 0x98}, /* matrix */
++ {0x50, 0x98},
++ {0x51, 0x00},
++ {0x52, 0x28},
++ {0x53, 0x70},
++ {0x54, 0x98},
++ {0x58, 0x1a},
++ {0xff, 0x41}, /* read 41, write ff 00 */
++ {0x41, 0x40}, /* com16 */
++/* contrast */
++ {0x56, 0x40},
++/* brightness */
++ {0x55, 0x8f},
++/* expo */
++ {0x10, 0x25}, /* aech - exposure high bits */
++ {0xff, 0x13}, /* read 13, write ff 00 */
++ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
++};
++
++static const u8 sensor_start_1_vga[][2] = { /* same for qvga */
++ {0x12, 0x62}, /* com7 - 30fps VGA YUV */
++ {0x36, 0xfa}, /* aref3 */
++ {0x69, 0x0a}, /* hv */
++ {0x8c, 0x89}, /* com22 */
++ {0x14, 0x28}, /* com9 */
++ {0x3e, 0x0c}, /* com14 */
++ {0x41, 0x40}, /* com16 */
++ {0x72, 0x00},
++ {0x73, 0x00},
++ {0x74, 0x3a},
++ {0x75, 0x35},
++ {0x76, 0x01},
++ {0xc7, 0x80}, /* com24 */
++ {0x03, 0x12}, /* vref */
++ {0x17, 0x16}, /* hstart */
++ {0x18, 0x02}, /* hstop */
++ {0x19, 0x01}, /* vstrt */
++ {0x1a, 0x3d}, /* vstop */
++ {0x32, 0xff}, /* href */
++ {0xc0, 0xaa},
++};
++
++static const u8 sensor_start_1_svga[][2] = {
++ {0x12, 0x02}, /* com7 - YUYV - VGA 15 full resolution */
++ {0x36, 0xf8}, /* aref3 */
++ {0x69, 0x02}, /* hv */
++ {0x8c, 0x0d}, /* com22 */
++ {0x3e, 0x0c}, /* com14 */
++ {0x41, 0x40}, /* com16 */
++ {0x72, 0x00},
++ {0x73, 0x01},
++ {0x74, 0x3a},
++ {0x75, 0x35},
++ {0x76, 0x01},
++ {0xc7, 0x80}, /* com24 */
++ {0x03, 0x1b}, /* vref */
++ {0x17, 0x1d}, /* hstart */
++ {0x18, 0xbd}, /* hstop */
++ {0x19, 0x01}, /* vstrt */
++ {0x1a, 0x81}, /* vstop */
++ {0x32, 0xff}, /* href */
++ {0xc0, 0xe2},
++};
++
++static const u8 sensor_start_1_xga[][2] = {
++ {0x12, 0x02}, /* com7 */
++ {0x36, 0xf8}, /* aref3 */
++ {0x69, 0x02}, /* hv */
++ {0x8c, 0x89}, /* com22 */
++ {0x14, 0x28}, /* com9 */
++ {0x3e, 0x0c}, /* com14 */
++ {0x41, 0x40}, /* com16 */
++ {0x72, 0x00},
++ {0x73, 0x01},
++ {0x74, 0x3a},
++ {0x75, 0x35},
++ {0x76, 0x01},
++ {0xc7, 0x80}, /* com24 */
++ {0x03, 0x1b}, /* vref */
++ {0x17, 0x1d}, /* hstart */
++ {0x18, 0xbd}, /* hstop */
++ {0x19, 0x01}, /* vstrt */
++ {0x1a, 0x81}, /* vstop */
++ {0x32, 0xff}, /* href */
++ {0xc0, 0xe2},
++};
++
++static const u8 sensor_start_1_sxga[][2] = {
++ {0x12, 0x02}, /* com7 */
++ {0x36, 0xf8}, /* aref3 */
++ {0x69, 0x02}, /* hv */
++ {0x8c, 0x89}, /* com22 */
++ {0x14, 0x28}, /* com9 */
++ {0x3e, 0x0c}, /* com14 */
++ {0x41, 0x40}, /* com16 */
++ {0x72, 0x00},
++ {0x73, 0x01},
++ {0x74, 0x3a},
++ {0x75, 0x35},
++ {0x76, 0x01},
++ {0xc7, 0x80}, /* com24 */
++ {0x03, 0x1b}, /* vref */
++ {0x17, 0x1d}, /* hstart */
++ {0x18, 0x02}, /* hstop */
++ {0x19, 0x01}, /* vstrt */
++ {0x1a, 0x81}, /* vstop */
++ {0x32, 0xff}, /* href */
++ {0xc0, 0xe2},
++};
++
++static const u8 bridge_start_qvga[][2] = {
++ {0x94, 0xaa},
++ {0xf1, 0x60},
++ {0xe5, 0x04},
++ {0xc0, 0x50},
++ {0xc1, 0x3c},
++ {0x8c, 0x00},
++ {0x8d, 0x1c},
++ {0x34, 0x05},
++
++ {0xc2, 0x4c},
++ {0xc3, 0xf9},
++ {0xda, 0x00},
++ {0x50, 0x00},
++ {0x51, 0xa0},
++ {0x52, 0x78},
++ {0x53, 0x00},
++ {0x54, 0x00},
++ {0x55, 0x00},
++ {0x57, 0x00},
++ {0x5c, 0x00},
++ {0x5a, 0x50},
++ {0x5b, 0x3c},
++ {0x35, 0x02},
++ {0xd9, 0x10},
++ {0x94, 0x11},
++};
++
++static const u8 bridge_start_vga[][2] = {
++ {0x94, 0xaa},
++ {0xf1, 0x60},
++ {0xe5, 0x04},
++ {0xc0, 0x50},
++ {0xc1, 0x3c},
++ {0x8c, 0x00},
++ {0x8d, 0x1c},
++ {0x34, 0x05},
++ {0xc2, 0x0c},
++ {0xc3, 0xf9},
++ {0xda, 0x01},
++ {0x50, 0x00},
++ {0x51, 0xa0},
++ {0x52, 0x3c},
++ {0x53, 0x00},
++ {0x54, 0x00},
++ {0x55, 0x00},
++ {0x57, 0x00},
++ {0x5c, 0x00},
++ {0x5a, 0xa0},
++ {0x5b, 0x78},
++ {0x35, 0x02},
++ {0xd9, 0x10},
++ {0x94, 0x11},
++};
++
++static const u8 bridge_start_svga[][2] = {
++ {0x94, 0xaa},
++ {0xf1, 0x60},
++ {0xe5, 0x04},
++ {0xc0, 0xa0},
++ {0xc1, 0x80},
++ {0x8c, 0x00},
++ {0x8d, 0x1c},
++ {0x34, 0x05},
++ {0xc2, 0x4c},
++ {0xc3, 0xf9},
++ {0x50, 0x00},
++ {0x51, 0x40},
++ {0x52, 0x00},
++ {0x53, 0x00},
++ {0x54, 0x00},
++ {0x55, 0x88},
++ {0x57, 0x00},
++ {0x5c, 0x00},
++ {0x5a, 0xc8},
++ {0x5b, 0x96},
++ {0x35, 0x02},
++ {0xd9, 0x10},
++ {0xda, 0x00},
++ {0x94, 0x11},
++};
++
++static const u8 bridge_start_xga[][2] = {
++ {0x94, 0xaa},
++ {0xf1, 0x60},
++ {0xe5, 0x04},
++ {0xc0, 0xa0},
++ {0xc1, 0x80},
++ {0x8c, 0x00},
++ {0x8d, 0x1c},
++ {0x34, 0x05},
++ {0xc2, 0x4c},
++ {0xc3, 0xf9},
++ {0x50, 0x00},
++ {0x51, 0x40},
++ {0x52, 0x00},
++ {0x53, 0x00},
++ {0x54, 0x00},
++ {0x55, 0x88},
++ {0x57, 0x00},
++ {0x5c, 0x01},
++ {0x5a, 0x00},
++ {0x5b, 0xc0},
++ {0x35, 0x02},
++ {0xd9, 0x10},
++ {0xda, 0x01},
++ {0x94, 0x11},
++};
++
++static const u8 bridge_start_sxga[][2] = {
++ {0x94, 0xaa},
++ {0xf1, 0x60},
++ {0xe5, 0x04},
++ {0xc0, 0xa0},
++ {0xc1, 0x80},
++ {0x8c, 0x00},
++ {0x8d, 0x1c},
++ {0x34, 0x05},
++ {0xc2, 0x0c},
++ {0xc3, 0xf9},
++ {0xda, 0x00},
++ {0x35, 0x02},
++ {0xd9, 0x10},
++ {0x94, 0x11},
++};
++
++static const u8 sensor_start_2_qvga[][2] = {
++ {0x3b, 0xe4}, /* com11 - night mode 1/4 frame rate */
++ {0x1e, 0x04}, /* mvfp */
++ {0x13, 0xe0}, /* com8 */
++ {0x00, 0x00},
++ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
++ {0x11, 0x01}, /* clkrc */
++ {0x6b, 0x5a}, /* dblv */
++ {0x6a, 0x02}, /* 50 Hz banding filter */
++ {0xc5, 0x03}, /* 60 Hz banding filter */
++ {0xa2, 0x96}, /* bd50 */
++ {0xa3, 0x7d}, /* bd60 */
++
++ {0xff, 0x13}, /* read 13, write ff 00 */
++ {0x13, 0xe7},
++ {0x3a, 0x80}, /* tslb - yuyv */
++};
++
++static const u8 sensor_start_2_vga[][2] = {
++ {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
++ {0x1e, 0x04}, /* mvfp */
++ {0x13, 0xe0}, /* com8 */
++ {0x00, 0x00},
++ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
++ {0x11, 0x03}, /* clkrc */
++ {0x6b, 0x5a}, /* dblv */
++ {0x6a, 0x05}, /* 50 Hz banding filter */
++ {0xc5, 0x07}, /* 60 Hz banding filter */
++ {0xa2, 0x4b}, /* bd50 */
++ {0xa3, 0x3e}, /* bd60 */
++
++ {0x2d, 0x00}, /* advfl */
++};
++
++static const u8 sensor_start_2_svga[][2] = { /* same for xga */
++ {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
++ {0x1e, 0x04}, /* mvfp */
++ {0x13, 0xe0}, /* com8 */
++ {0x00, 0x00},
++ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
++ {0x11, 0x01}, /* clkrc */
++ {0x6b, 0x5a}, /* dblv */
++ {0x6a, 0x0c}, /* 50 Hz banding filter */
++ {0xc5, 0x0f}, /* 60 Hz banding filter */
++ {0xa2, 0x4e}, /* bd50 */
++ {0xa3, 0x41}, /* bd60 */
++};
++
++static const u8 sensor_start_2_sxga[][2] = {
++ {0x13, 0xe0}, /* com8 */
++ {0x00, 0x00},
++ {0x13, 0xe7}, /* com8 - everything (AGC, AWB and AEC) */
++ {0x3b, 0xc4}, /* com11 - night mode 1/4 frame rate */
++ {0x1e, 0x04}, /* mvfp */
++ {0x11, 0x01}, /* clkrc */
++ {0x6b, 0x5a}, /* dblv */
++ {0x6a, 0x0c}, /* 50 Hz banding filter */
++ {0xc5, 0x0f}, /* 60 Hz banding filter */
++ {0xa2, 0x4e}, /* bd50 */
++ {0xa3, 0x41}, /* bd60 */
++};
++
++static void reg_w_i(struct gspca_dev *gspca_dev, u16 reg, u8 val)
++{
++ struct usb_device *udev = gspca_dev->dev;
++ int ret;
++
++ if (gspca_dev->usb_err < 0)
++ return;
++ gspca_dev->usb_buf[0] = val;
++ ret = usb_control_msg(udev,
++ usb_sndctrlpipe(udev, 0),
++ 0x01,
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "reg_w failed %d", ret);
++ gspca_dev->usb_err = ret;
++ }
++}
++
++static void reg_w(struct gspca_dev *gspca_dev, u16 reg, u8 val)
++{
++ PDEBUG(D_USBO, "reg_w [%04x] = %02x", reg, val);
++ reg_w_i(gspca_dev, reg, val);
++}
++
++static u8 reg_r(struct gspca_dev *gspca_dev, u16 reg)
++{
++ struct usb_device *udev = gspca_dev->dev;
++ int ret;
++
++ if (gspca_dev->usb_err < 0)
++ return 0;
++ ret = usb_control_msg(udev,
++ usb_rcvctrlpipe(udev, 0),
++ 0x01,
++ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ 0x00, reg, gspca_dev->usb_buf, 1, CTRL_TIMEOUT);
++ PDEBUG(D_USBI, "reg_r [%04x] -> %02x", reg, gspca_dev->usb_buf[0]);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "reg_r err %d", ret);
++ gspca_dev->usb_err = ret;
++ }
++ return gspca_dev->usb_buf[0];
++}
++
++static int sccb_check_status(struct gspca_dev *gspca_dev)
++{
++ u8 data;
++ int i;
++
++ for (i = 0; i < 5; i++) {
++ data = reg_r(gspca_dev, OV534_REG_STATUS);
++
++ switch (data) {
++ case 0x00:
++ return 1;
++ case 0x04:
++ return 0;
++ case 0x03:
++ break;
++ default:
++ PDEBUG(D_USBI|D_USBO,
++ "sccb status 0x%02x, attempt %d/5",
++ data, i + 1);
++ }
++ }
++ return 0;
++}
++
++static void sccb_write(struct gspca_dev *gspca_dev, u8 reg, u8 val)
++{
++ PDEBUG(D_USBO, "sccb_write [%02x] = %02x", reg, val);
++ reg_w_i(gspca_dev, OV534_REG_SUBADDR, reg);
++ reg_w_i(gspca_dev, OV534_REG_WRITE, val);
++ reg_w_i(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_3);
++
++ if (!sccb_check_status(gspca_dev))
++ PDEBUG(D_ERR, "sccb_write failed");
++}
++
++static u8 sccb_read(struct gspca_dev *gspca_dev, u16 reg)
++{
++ reg_w(gspca_dev, OV534_REG_SUBADDR, reg);
++ reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_WRITE_2);
++ if (!sccb_check_status(gspca_dev))
++ PDEBUG(D_ERR, "sccb_read failed 1");
++
++ reg_w(gspca_dev, OV534_REG_OPERATION, OV534_OP_READ_2);
++ if (!sccb_check_status(gspca_dev))
++ PDEBUG(D_ERR, "sccb_read failed 2");
++
++ return reg_r(gspca_dev, OV534_REG_READ);
++}
++
++/* output a bridge sequence (reg - val) */
++static void reg_w_array(struct gspca_dev *gspca_dev,
++ const u8 (*data)[2], int len)
++{
++ while (--len >= 0) {
++ reg_w(gspca_dev, (*data)[0], (*data)[1]);
++ data++;
++ }
++}
++
++/* output a sensor sequence (reg - val) */
++static void sccb_w_array(struct gspca_dev *gspca_dev,
++ const u8 (*data)[2], int len)
++{
++ while (--len >= 0) {
++ if ((*data)[0] != 0xff) {
++ sccb_write(gspca_dev, (*data)[0], (*data)[1]);
++ } else {
++ sccb_read(gspca_dev, (*data)[1]);
++ sccb_write(gspca_dev, 0xff, 0x00);
++ }
++ data++;
++ }
++}
++
++/* Two bits control LED: 0x21 bit 7 and 0x23 bit 7.
++ * (direction and output)? */
++static void set_led(struct gspca_dev *gspca_dev, int status)
++{
++ u8 data;
++
++ PDEBUG(D_CONF, "led status: %d", status);
++
++ data = reg_r(gspca_dev, 0x21);
++ data |= 0x80;
++ reg_w(gspca_dev, 0x21, data);
++
++ data = reg_r(gspca_dev, 0x23);
++ if (status)
++ data |= 0x80;
++ else
++ data &= ~0x80;
++
++ reg_w(gspca_dev, 0x23, data);
++
++ if (!status) {
++ data = reg_r(gspca_dev, 0x21);
++ data &= ~0x80;
++ reg_w(gspca_dev, 0x21, data);
++ }
++}
++
++static void setbrightness(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ u8 val;
++
++ val = sd->brightness;
++ if (val < 8)
++ val = 15 - val; /* f .. 8 */
++ else
++ val = val - 8; /* 0 .. 7 */
++ sccb_write(gspca_dev, 0x55, /* brtn - brightness adjustment */
++ 0x0f | (val << 4));
++}
++
++static void setcontrast(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sccb_write(gspca_dev, 0x56, /* cnst1 - contrast 1 ctrl coeff */
++ sd->contrast << 4);
++}
++
++static void setautogain(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ u8 val;
++
++/*fixme: should adjust agc/awb/aec by different controls */
++ val = sd->autogain;
++ val = sccb_read(gspca_dev, 0x13); /* com8 */
++ sccb_write(gspca_dev, 0xff, 0x00);
++ if (sd->autogain)
++ val |= 0x05; /* agc & aec */
++ else
++ val &= 0xfa;
++ sccb_write(gspca_dev, 0x13, val);
++}
++
++static void setexposure(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ u8 val;
++ static const u8 expo[4] = {0x00, 0x25, 0x38, 0x5e};
++
++ sccb_write(gspca_dev, 0x10, /* aec[9:2] */
++ expo[sd->exposure]);
++
++ val = sccb_read(gspca_dev, 0x13); /* com8 */
++ sccb_write(gspca_dev, 0xff, 0x00);
++ sccb_write(gspca_dev, 0x13, val);
++
++ val = sccb_read(gspca_dev, 0xa1); /* aech */
++ sccb_write(gspca_dev, 0xff, 0x00);
++ sccb_write(gspca_dev, 0xa1, val & 0xe0); /* aec[15:10] = 0 */
++}
++
++static void setsharpness(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ s8 val;
++
++ val = sd->sharpness;
++ if (val < 0) { /* auto */
++ val = sccb_read(gspca_dev, 0x42); /* com17 */
++ sccb_write(gspca_dev, 0xff, 0x00);
++ sccb_write(gspca_dev, 0x42, val | 0x40);
++ /* Edge enhancement strength auto adjust */
++ return;
++ }
++ if (val != 0)
++ val = 1 << (val - 1);
++ sccb_write(gspca_dev, 0x3f, /* edge - edge enhance. factor */
++ val);
++ val = sccb_read(gspca_dev, 0x42); /* com17 */
++ sccb_write(gspca_dev, 0xff, 0x00);
++ sccb_write(gspca_dev, 0x42, val & 0xbf);
++}
++
++static void setsatur(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ u8 val1, val2, val3;
++ static const u8 matrix[5][2] = {
++ {0x14, 0x38},
++ {0x1e, 0x54},
++ {0x28, 0x70},
++ {0x32, 0x8c},
++ {0x48, 0x90}
++ };
++
++ val1 = matrix[sd->satur][0];
++ val2 = matrix[sd->satur][1];
++ val3 = val1 + val2;
++ sccb_write(gspca_dev, 0x4f, val3); /* matrix coeff */
++ sccb_write(gspca_dev, 0x50, val3);
++ sccb_write(gspca_dev, 0x51, 0x00);
++ sccb_write(gspca_dev, 0x52, val1);
++ sccb_write(gspca_dev, 0x53, val2);
++ sccb_write(gspca_dev, 0x54, val3);
++ sccb_write(gspca_dev, 0x58, 0x1a); /* mtxs - coeff signs */
++
++ val1 = sccb_read(gspca_dev, 0x41); /* com16 */
++ sccb_write(gspca_dev, 0xff, 0x00);
++ sccb_write(gspca_dev, 0x41, val1);
++}
++
++static void setfreq(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ u8 val;
++
++ val = sccb_read(gspca_dev, 0x13); /* com8 */
++ sccb_write(gspca_dev, 0xff, 0x00);
++ if (sd->freq == 0) {
++ sccb_write(gspca_dev, 0x13, val & 0xdf);
++ return;
++ }
++ sccb_write(gspca_dev, 0x13, val | 0x20);
++
++ val = sccb_read(gspca_dev, 0x42); /* com17 */
++ sccb_write(gspca_dev, 0xff, 0x00);
++ if (sd->freq == 1)
++ val |= 0x01;
++ else
++ val &= 0xfe;
++ sccb_write(gspca_dev, 0x42, val);
++}
++
++/* this function is called at probe time */
++static int sd_config(struct gspca_dev *gspca_dev,
++ const struct usb_device_id *id)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ struct cam *cam;
++
++ cam = &gspca_dev->cam;
++
++ cam->cam_mode = ov965x_mode;
++ cam->nmodes = ARRAY_SIZE(ov965x_mode);
++
++ sd->brightness = BRIGHTNESS_DEF;
++ sd->contrast = CONTRAST_DEF;
++#if AUTOGAIN_DEF != 0
++ sd->autogain = AUTOGAIN_DEF;
++ gspca_dev->ctrl_inac |= (1 << EXPO_IDX);
++#endif
++#if EXPO_DEF != 0
++ sd->exposure = EXPO_DEF;
++#endif
++#if SHARPNESS_DEF != 0
++ sd->sharpness = SHARPNESS_DEF;
++#endif
++ sd->satur = SATUR_DEF;
++ sd->freq = FREQ_DEF;
++
++ return 0;
++}
++
++/* this function is called at probe and resume time */
++static int sd_init(struct gspca_dev *gspca_dev)
++{
++ u16 sensor_id;
++
++ /* reset bridge */
++ reg_w(gspca_dev, 0xe7, 0x3a);
++ reg_w(gspca_dev, 0xe0, 0x08);
++ msleep(100);
++
++ /* initialize the sensor address */
++ reg_w(gspca_dev, OV534_REG_ADDRESS, 0x60);
++
++ /* reset sensor */
++ sccb_write(gspca_dev, 0x12, 0x80);
++ msleep(10);
++
++ /* probe the sensor */
++ sccb_read(gspca_dev, 0x0a);
++ sensor_id = sccb_read(gspca_dev, 0x0a) << 8;
++ sccb_read(gspca_dev, 0x0b);
++ sensor_id |= sccb_read(gspca_dev, 0x0b);
++ PDEBUG(D_PROBE, "Sensor ID: %04x", sensor_id);
++
++ /* initialize */
++ reg_w_array(gspca_dev, bridge_init,
++ ARRAY_SIZE(bridge_init));
++ sccb_w_array(gspca_dev, sensor_init,
++ ARRAY_SIZE(sensor_init));
++ reg_w_array(gspca_dev, bridge_init_2,
++ ARRAY_SIZE(bridge_init_2));
++ sccb_w_array(gspca_dev, sensor_init_2,
++ ARRAY_SIZE(sensor_init_2));
++ reg_w(gspca_dev, 0xe0, 0x00);
++ reg_w(gspca_dev, 0xe0, 0x01);
++ set_led(gspca_dev, 0);
++ reg_w(gspca_dev, 0xe0, 0x00);
++
++ return gspca_dev->usb_err;
++}
++
++static int sd_start(struct gspca_dev *gspca_dev)
++{
++ switch (gspca_dev->curr_mode) {
++ case QVGA_MODE: /* 320x240 */
++ sccb_w_array(gspca_dev, sensor_start_1_vga,
++ ARRAY_SIZE(sensor_start_1_vga));
++ reg_w_array(gspca_dev, bridge_start_qvga,
++ ARRAY_SIZE(bridge_start_qvga));
++ sccb_w_array(gspca_dev, sensor_start_2_qvga,
++ ARRAY_SIZE(sensor_start_2_qvga));
++ break;
++ case VGA_MODE: /* 640x480 */
++ sccb_w_array(gspca_dev, sensor_start_1_vga,
++ ARRAY_SIZE(sensor_start_1_vga));
++ reg_w_array(gspca_dev, bridge_start_vga,
++ ARRAY_SIZE(bridge_start_vga));
++ sccb_w_array(gspca_dev, sensor_start_2_vga,
++ ARRAY_SIZE(sensor_start_2_vga));
++ break;
++ case SVGA_MODE: /* 800x600 */
++ sccb_w_array(gspca_dev, sensor_start_1_svga,
++ ARRAY_SIZE(sensor_start_1_svga));
++ reg_w_array(gspca_dev, bridge_start_svga,
++ ARRAY_SIZE(bridge_start_svga));
++ sccb_w_array(gspca_dev, sensor_start_2_svga,
++ ARRAY_SIZE(sensor_start_2_svga));
++ break;
++ case XGA_MODE: /* 1024x768 */
++ sccb_w_array(gspca_dev, sensor_start_1_xga,
++ ARRAY_SIZE(sensor_start_1_xga));
++ reg_w_array(gspca_dev, bridge_start_xga,
++ ARRAY_SIZE(bridge_start_xga));
++ sccb_w_array(gspca_dev, sensor_start_2_svga,
++ ARRAY_SIZE(sensor_start_2_svga));
++ break;
++ default:
++/* case SXGA_MODE: * 1280x1024 */
++ sccb_w_array(gspca_dev, sensor_start_1_sxga,
++ ARRAY_SIZE(sensor_start_1_sxga));
++ reg_w_array(gspca_dev, bridge_start_sxga,
++ ARRAY_SIZE(bridge_start_sxga));
++ sccb_w_array(gspca_dev, sensor_start_2_sxga,
++ ARRAY_SIZE(sensor_start_2_sxga));
++ break;
++ }
++ setfreq(gspca_dev);
++ setautogain(gspca_dev);
++ setbrightness(gspca_dev);
++ setcontrast(gspca_dev);
++ setexposure(gspca_dev);
++ setsharpness(gspca_dev);
++ setsatur(gspca_dev);
++
++ reg_w(gspca_dev, 0xe0, 0x00);
++ reg_w(gspca_dev, 0xe0, 0x00);
++ set_led(gspca_dev, 1);
++ return gspca_dev->usb_err;
++}
++
++static void sd_stopN(struct gspca_dev *gspca_dev)
++{
++ reg_w(gspca_dev, 0xe0, 0x01);
++ set_led(gspca_dev, 0);
++ reg_w(gspca_dev, 0xe0, 0x00);
++}
++
++/* Values for bmHeaderInfo (Video and Still Image Payload Headers, 2.4.3.3) */
++#define UVC_STREAM_EOH (1 << 7)
++#define UVC_STREAM_ERR (1 << 6)
++#define UVC_STREAM_STI (1 << 5)
++#define UVC_STREAM_RES (1 << 4)
++#define UVC_STREAM_SCR (1 << 3)
++#define UVC_STREAM_PTS (1 << 2)
++#define UVC_STREAM_EOF (1 << 1)
++#define UVC_STREAM_FID (1 << 0)
++
++static void sd_pkt_scan(struct gspca_dev *gspca_dev,
++ u8 *data, int len)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ __u32 this_pts;
++ u8 this_fid;
++ int remaining_len = len;
++
++ do {
++ len = min(remaining_len, 2040);
++
++ /* Payloads are prefixed with a UVC-style header. We
++ consider a frame to start when the FID toggles, or the PTS
++ changes. A frame ends when EOF is set, and we've received
++ the correct number of bytes. */
++
++ /* Verify UVC header. Header length is always 12 */
++ if (data[0] != 12 || len < 12) {
++ PDEBUG(D_PACK, "bad header");
++ goto discard;
++ }
++
++ /* Check errors */
++ if (data[1] & UVC_STREAM_ERR) {
++ PDEBUG(D_PACK, "payload error");
++ goto discard;
++ }
++
++ /* Extract PTS and FID */
++ if (!(data[1] & UVC_STREAM_PTS)) {
++ PDEBUG(D_PACK, "PTS not present");
++ goto discard;
++ }
++ this_pts = (data[5] << 24) | (data[4] << 16)
++ | (data[3] << 8) | data[2];
++ this_fid = data[1] & UVC_STREAM_FID;
++
++ /* If PTS or FID has changed, start a new frame. */
++ if (this_pts != sd->last_pts || this_fid != sd->last_fid) {
++ if (gspca_dev->last_packet_type == INTER_PACKET)
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ NULL, 0);
++ sd->last_pts = this_pts;
++ sd->last_fid = this_fid;
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
++ data + 12, len - 12);
++ /* If this packet is marked as EOF, end the frame */
++ } else if (data[1] & UVC_STREAM_EOF) {
++ sd->last_pts = 0;
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ data + 12, len - 12);
++ } else {
++
++ /* Add the data from this payload */
++ gspca_frame_add(gspca_dev, INTER_PACKET,
++ data + 12, len - 12);
++ }
++
++ /* Done this payload */
++ goto scan_next;
++
++discard:
++ /* Discard data until a new frame starts. */
++ gspca_dev->last_packet_type = DISCARD_PACKET;
++
++scan_next:
++ remaining_len -= len;
++ data += len;
++ } while (remaining_len > 0);
++}
++
++/* controls */
++static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->brightness = val;
++ if (gspca_dev->streaming)
++ setbrightness(gspca_dev);
++ return gspca_dev->usb_err;
++}
++
++static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->brightness;
++ return 0;
++}
++
++static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->contrast = val;
++ if (gspca_dev->streaming)
++ setcontrast(gspca_dev);
++ return gspca_dev->usb_err;
++}
++
++static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->contrast;
++ return 0;
++}
++
++static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->autogain = val;
++
++ if (gspca_dev->streaming) {
++ if (val)
++ gspca_dev->ctrl_inac |= (1 << EXPO_IDX);
++ else
++ gspca_dev->ctrl_inac &= ~(1 << EXPO_IDX);
++ setautogain(gspca_dev);
++ }
++ return gspca_dev->usb_err;
++}
++
++static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->autogain;
++ return 0;
++}
++
++static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->exposure = val;
++ if (gspca_dev->streaming)
++ setexposure(gspca_dev);
++ return gspca_dev->usb_err;
++}
++
++static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->exposure;
++ return 0;
++}
++
++static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->sharpness = val;
++ if (gspca_dev->streaming)
++ setsharpness(gspca_dev);
++ return gspca_dev->usb_err;
++}
++
++static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->sharpness;
++ return 0;
++}
++
++static int sd_setsatur(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->satur = val;
++ if (gspca_dev->streaming)
++ setsatur(gspca_dev);
++ return gspca_dev->usb_err;
++}
++
++static int sd_getsatur(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->satur;
++ return 0;
++}
++static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->freq = val;
++ if (gspca_dev->streaming)
++ setfreq(gspca_dev);
++ return gspca_dev->usb_err;
++}
++
++static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->freq;
++ return 0;
++}
++
++static int sd_querymenu(struct gspca_dev *gspca_dev,
++ struct v4l2_querymenu *menu)
++{
++ switch (menu->id) {
++ case V4L2_CID_POWER_LINE_FREQUENCY:
++ switch (menu->index) {
++ case 0: /* V4L2_CID_POWER_LINE_FREQUENCY_DISABLED */
++ strcpy((char *) menu->name, "NoFliker");
++ return 0;
++ case 1: /* V4L2_CID_POWER_LINE_FREQUENCY_50HZ */
++ strcpy((char *) menu->name, "50 Hz");
++ return 0;
++ case 2: /* V4L2_CID_POWER_LINE_FREQUENCY_60HZ */
++ strcpy((char *) menu->name, "60 Hz");
++ return 0;
++ }
++ break;
++ }
++ return -EINVAL;
++}
++
++/* sub-driver description */
++static const struct sd_desc sd_desc = {
++ .name = MODULE_NAME,
++ .ctrls = sd_ctrls,
++ .nctrls = ARRAY_SIZE(sd_ctrls),
++ .config = sd_config,
++ .init = sd_init,
++ .start = sd_start,
++ .stopN = sd_stopN,
++ .pkt_scan = sd_pkt_scan,
++ .querymenu = sd_querymenu,
++};
++
++/* -- module initialisation -- */
++static const __devinitdata struct usb_device_id device_table[] = {
++ {USB_DEVICE(0x06f8, 0x3003)},
++ {}
++};
++
++MODULE_DEVICE_TABLE(usb, device_table);
++
++/* -- device connect -- */
++static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id)
++{
++ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
++ THIS_MODULE);
++}
++
++static struct usb_driver sd_driver = {
++ .name = MODULE_NAME,
++ .id_table = device_table,
++ .probe = sd_probe,
++ .disconnect = gspca_disconnect,
++#ifdef CONFIG_PM
++ .suspend = gspca_suspend,
++ .resume = gspca_resume,
++#endif
++};
++
++/* -- module insert / remove -- */
++static int __init sd_mod_init(void)
++{
++ int ret;
++
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
++ PDEBUG(D_PROBE, "registered");
++ return 0;
++}
++
++static void __exit sd_mod_exit(void)
++{
++ usb_deregister(&sd_driver);
++ PDEBUG(D_PROBE, "deregistered");
++}
++
++module_init(sd_mod_init);
++module_exit(sd_mod_exit);
+diff --git a/drivers/media/video/gspca/pac207.c b/drivers/media/video/gspca/pac207.c
+index 9665943..5ad4c73 100644
+--- a/drivers/media/video/gspca/pac207.c
++++ b/drivers/media/video/gspca/pac207.c
+@@ -77,7 +77,7 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
+ #define SD_BRIGHTNESS 0
+ {
+ {
+@@ -337,14 +337,13 @@ static void pac207_do_auto_gain(struct gspca_dev *gspca_dev)
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame,
+- __u8 *data,
++ u8 *data,
+ int len)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ unsigned char *sof;
+
+- sof = pac_find_sof(gspca_dev, data, len);
++ sof = pac_find_sof(&sd->sof_read, data, len);
+ if (sof) {
+ int n;
+
+@@ -354,10 +353,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ n -= sizeof pac_sof_marker;
+ else
+ n = 0;
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+- data, n);
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ data, n);
+ sd->header_read = 0;
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame, NULL, 0);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, NULL, 0);
+ len -= sof - data;
+ data = sof;
+ }
+@@ -381,7 +380,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ sd->header_read = 11;
+ }
+
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+
+ static void setbrightness(struct gspca_dev *gspca_dev)
+diff --git a/drivers/media/video/gspca/pac7302.c b/drivers/media/video/gspca/pac7302.c
+new file mode 100644
+index 0000000..42ea1b4
+--- /dev/null
++++ b/drivers/media/video/gspca/pac7302.c
+@@ -0,0 +1,1237 @@
++/*
++ * Pixart PAC7302 library
++ * Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
++ *
++ * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
++ *
++ * Separated from Pixart PAC7311 library by Mrton Nmeth <nm127@freemail.hu>
++ *
++ * 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
++ * 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
++ */
++
++/* Some documentation about various registers as determined by trial and error.
++ When the register addresses differ between the 7202 and the 7311 the 2
++ different addresses are written as 7302addr/7311addr, when one of the 2
++ addresses is a - sign that register description is not valid for the
++ matching IC.
++
++ Register page 1:
++
++ Address Description
++ -/0x08 Unknown compressor related, must always be 8 except when not
++ in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 !
++ -/0x1b Auto white balance related, bit 0 is AWB enable (inverted)
++ bits 345 seem to toggle per color gains on/off (inverted)
++ 0x78 Global control, bit 6 controls the LED (inverted)
++ -/0x80 JPEG compression ratio ? Best not touched
++
++ Register page 3/4:
++
++ Address Description
++ 0x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on
++ the 7302, so one of 3, 6, 9, ..., except when between 6 and 12?
++ -/0x0f Master gain 1-245, low value = high gain
++ 0x10/- Master gain 0-31
++ -/0x10 Another gain 0-15, limited influence (1-2x gain I guess)
++ 0x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused
++ -/0x27 Seems to toggle various gains on / off, Setting bit 7 seems to
++ completely disable the analog amplification block. Set to 0x68
++ for max gain, 0x14 for minimal gain.
++
++ The registers are accessed in the following functions:
++
++ Page | Register | Function
++ -----+------------+---------------------------------------------------
++ 0 | 0x0f..0x20 | setcolors()
++ 0 | 0xa2..0xab | setbrightcont()
++ 0 | 0xc5 | setredbalance()
++ 0 | 0xc6 | setwhitebalance()
++ 0 | 0xc7 | setbluebalance()
++ 0 | 0xdc | setbrightcont(), setcolors()
++ 3 | 0x02 | setexposure()
++ 3 | 0x10 | setgain()
++ 3 | 0x11 | setcolors(), setgain(), setexposure(), sethvflip()
++ 3 | 0x21 | sethvflip()
++*/
++
++#define MODULE_NAME "pac7302"
++
++#include <media/v4l2-chip-ident.h>
++#include "gspca.h"
++
++MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
++MODULE_DESCRIPTION("Pixart PAC7302");
++MODULE_LICENSE("GPL");
++
++/* specific webcam descriptor for pac7302 */
++struct sd {
++ struct gspca_dev gspca_dev; /* !! must be the first item */
++
++ unsigned char brightness;
++ unsigned char contrast;
++ unsigned char colors;
++ unsigned char white_balance;
++ unsigned char red_balance;
++ unsigned char blue_balance;
++ unsigned char gain;
++ unsigned char exposure;
++ unsigned char autogain;
++ __u8 hflip;
++ __u8 vflip;
++ u8 flags;
++#define FL_HFLIP 0x01 /* mirrored by default */
++#define FL_VFLIP 0x02 /* vertical flipped by default */
++
++ u8 sof_read;
++ u8 autogain_ignore_frames;
++
++ atomic_t avg_lum;
++};
++
++/* V4L2 controls supported by the driver */
++static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
++
++static const struct ctrl sd_ctrls[] = {
++/* This control is pac7302 only */
++ {
++ {
++ .id = V4L2_CID_BRIGHTNESS,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Brightness",
++ .minimum = 0,
++#define BRIGHTNESS_MAX 0x20
++ .maximum = BRIGHTNESS_MAX,
++ .step = 1,
++#define BRIGHTNESS_DEF 0x10
++ .default_value = BRIGHTNESS_DEF,
++ },
++ .set = sd_setbrightness,
++ .get = sd_getbrightness,
++ },
++/* This control is for both the 7302 and the 7311 */
++ {
++ {
++ .id = V4L2_CID_CONTRAST,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Contrast",
++ .minimum = 0,
++#define CONTRAST_MAX 255
++ .maximum = CONTRAST_MAX,
++ .step = 1,
++#define CONTRAST_DEF 127
++ .default_value = CONTRAST_DEF,
++ },
++ .set = sd_setcontrast,
++ .get = sd_getcontrast,
++ },
++/* This control is pac7302 only */
++ {
++ {
++ .id = V4L2_CID_SATURATION,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Saturation",
++ .minimum = 0,
++#define COLOR_MAX 255
++ .maximum = COLOR_MAX,
++ .step = 1,
++#define COLOR_DEF 127
++ .default_value = COLOR_DEF,
++ },
++ .set = sd_setcolors,
++ .get = sd_getcolors,
++ },
++ {
++ {
++ .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "White Balance",
++ .minimum = 0,
++ .maximum = 255,
++ .step = 1,
++#define WHITEBALANCE_DEF 4
++ .default_value = WHITEBALANCE_DEF,
++ },
++ .set = sd_setwhitebalance,
++ .get = sd_getwhitebalance,
++ },
++ {
++ {
++ .id = V4L2_CID_RED_BALANCE,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Red",
++ .minimum = 0,
++ .maximum = 3,
++ .step = 1,
++#define REDBALANCE_DEF 1
++ .default_value = REDBALANCE_DEF,
++ },
++ .set = sd_setredbalance,
++ .get = sd_getredbalance,
++ },
++ {
++ {
++ .id = V4L2_CID_BLUE_BALANCE,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Blue",
++ .minimum = 0,
++ .maximum = 3,
++ .step = 1,
++#define BLUEBALANCE_DEF 1
++ .default_value = BLUEBALANCE_DEF,
++ },
++ .set = sd_setbluebalance,
++ .get = sd_getbluebalance,
++ },
++/* All controls below are for both the 7302 and the 7311 */
++ {
++ {
++ .id = V4L2_CID_GAIN,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Gain",
++ .minimum = 0,
++#define GAIN_MAX 255
++ .maximum = GAIN_MAX,
++ .step = 1,
++#define GAIN_DEF 127
++#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */
++ .default_value = GAIN_DEF,
++ },
++ .set = sd_setgain,
++ .get = sd_getgain,
++ },
++ {
++ {
++ .id = V4L2_CID_EXPOSURE,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Exposure",
++ .minimum = 0,
++#define EXPOSURE_MAX 255
++ .maximum = EXPOSURE_MAX,
++ .step = 1,
++#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */
++#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */
++ .default_value = EXPOSURE_DEF,
++ },
++ .set = sd_setexposure,
++ .get = sd_getexposure,
++ },
++ {
++ {
++ .id = V4L2_CID_AUTOGAIN,
++ .type = V4L2_CTRL_TYPE_BOOLEAN,
++ .name = "Auto Gain",
++ .minimum = 0,
++ .maximum = 1,
++ .step = 1,
++#define AUTOGAIN_DEF 1
++ .default_value = AUTOGAIN_DEF,
++ },
++ .set = sd_setautogain,
++ .get = sd_getautogain,
++ },
++ {
++ {
++ .id = V4L2_CID_HFLIP,
++ .type = V4L2_CTRL_TYPE_BOOLEAN,
++ .name = "Mirror",
++ .minimum = 0,
++ .maximum = 1,
++ .step = 1,
++#define HFLIP_DEF 0
++ .default_value = HFLIP_DEF,
++ },
++ .set = sd_sethflip,
++ .get = sd_gethflip,
++ },
++ {
++ {
++ .id = V4L2_CID_VFLIP,
++ .type = V4L2_CTRL_TYPE_BOOLEAN,
++ .name = "Vflip",
++ .minimum = 0,
++ .maximum = 1,
++ .step = 1,
++#define VFLIP_DEF 0
++ .default_value = VFLIP_DEF,
++ },
++ .set = sd_setvflip,
++ .get = sd_getvflip,
++ },
++};
++
++static const struct v4l2_pix_format vga_mode[] = {
++ {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
++ .bytesperline = 640,
++ .sizeimage = 640 * 480 * 3 / 8 + 590,
++ .colorspace = V4L2_COLORSPACE_JPEG,
++ .priv = 0},
++};
++
++#define LOAD_PAGE3 255
++#define END_OF_SEQUENCE 0
++
++/* pac 7302 */
++static const __u8 init_7302[] = {
++/* index,value */
++ 0xff, 0x01, /* page 1 */
++ 0x78, 0x00, /* deactivate */
++ 0xff, 0x01,
++ 0x78, 0x40, /* led off */
++};
++static const __u8 start_7302[] = {
++/* index, len, [value]* */
++ 0xff, 1, 0x00, /* page 0 */
++ 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80,
++ 0x00, 0x00, 0x00, 0x00,
++ 0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00,
++ 0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7,
++ 0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11,
++ 0x26, 2, 0xaa, 0xaa,
++ 0x2e, 1, 0x31,
++ 0x38, 1, 0x01,
++ 0x3a, 3, 0x14, 0xff, 0x5a,
++ 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11,
++ 0x00, 0x54, 0x11,
++ 0x55, 1, 0x00,
++ 0x62, 4, 0x10, 0x1e, 0x1e, 0x18,
++ 0x6b, 1, 0x00,
++ 0x6e, 3, 0x08, 0x06, 0x00,
++ 0x72, 3, 0x00, 0xff, 0x00,
++ 0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c,
++ 0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50,
++ 0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00,
++ 0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9,
++ 0xd2, 0xeb,
++ 0xaf, 1, 0x02,
++ 0xb5, 2, 0x08, 0x08,
++ 0xb8, 2, 0x08, 0x88,
++ 0xc4, 4, 0xae, 0x01, 0x04, 0x01,
++ 0xcc, 1, 0x00,
++ 0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9,
++ 0xc1, 0xd7, 0xec,
++ 0xdc, 1, 0x01,
++ 0xff, 1, 0x01, /* page 1 */
++ 0x12, 3, 0x02, 0x00, 0x01,
++ 0x3e, 2, 0x00, 0x00,
++ 0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2,
++ 0x7c, 1, 0x00,
++ 0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20,
++ 0x02, 0x00,
++ 0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04,
++ 0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
++ 0x07, 0x00, 0x01, 0x07, 0x04, 0x01,
++ 0xd8, 1, 0x01,
++ 0xdb, 2, 0x00, 0x01,
++ 0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00,
++ 0xe6, 4, 0x00, 0x00, 0x00, 0x01,
++ 0xeb, 1, 0x00,
++ 0xff, 1, 0x02, /* page 2 */
++ 0x22, 1, 0x00,
++ 0xff, 1, 0x03, /* page 3 */
++ 0, LOAD_PAGE3, /* load the page 3 */
++ 0x11, 1, 0x01,
++ 0xff, 1, 0x02, /* page 2 */
++ 0x13, 1, 0x00,
++ 0x22, 4, 0x1f, 0xa4, 0xf0, 0x96,
++ 0x27, 2, 0x14, 0x0c,
++ 0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22,
++ 0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44,
++ 0x6e, 1, 0x08,
++ 0xff, 1, 0x01, /* page 1 */
++ 0x78, 1, 0x00,
++ 0, END_OF_SEQUENCE /* end of sequence */
++};
++
++#define SKIP 0xaa
++/* page 3 - the value SKIP says skip the index - see reg_w_page() */
++static const __u8 page3_7302[] = {
++ 0x90, 0x40, 0x03, 0x50, 0xc2, 0x01, 0x14, 0x16,
++ 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00,
++ 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00,
++ 0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21,
++ 0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54,
++ 0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00,
++ 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00,
++ SKIP, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00,
++ 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8,
++ 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
++ 0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00,
++ 0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00,
++ 0x00
++};
++
++static void reg_w_buf(struct gspca_dev *gspca_dev,
++ __u8 index,
++ const char *buffer, int len)
++{
++ int ret;
++
++ if (gspca_dev->usb_err < 0)
++ return;
++ memcpy(gspca_dev->usb_buf, buffer, len);
++ ret = usb_control_msg(gspca_dev->dev,
++ usb_sndctrlpipe(gspca_dev->dev, 0),
++ 1, /* request */
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ 0, /* value */
++ index, gspca_dev->usb_buf, len,
++ 500);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "reg_w_buf(): "
++ "Failed to write registers to index 0x%x, error %i",
++ index, ret);
++ gspca_dev->usb_err = ret;
++ }
++}
++
++#if 0 /* not used */
++static __u8 reg_r(struct gspca_dev *gspca_dev,
++ __u8 index)
++{
++ int ret;
++
++ ret = usb_control_msg(gspca_dev->dev,
++ usb_rcvctrlpipe(gspca_dev->dev, 0),
++ 0, /* request */
++ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ 0, /* value */
++ index, gspca_dev->usb_buf, 1,
++ 500);
++ if (ret < 0)
++ PDEBUG(D_ERR, "reg_r(): "
++ "Failed to read register from index 0x%x, error %i",
++ index, ret);
++
++ return gspca_dev->usb_buf[0];
++}
++#endif
++
++static void reg_w(struct gspca_dev *gspca_dev,
++ __u8 index,
++ __u8 value)
++{
++ int ret;
++
++ if (gspca_dev->usb_err < 0)
++ return;
++ gspca_dev->usb_buf[0] = value;
++ ret = usb_control_msg(gspca_dev->dev,
++ usb_sndctrlpipe(gspca_dev->dev, 0),
++ 0, /* request */
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ 0, index, gspca_dev->usb_buf, 1,
++ 500);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "reg_w(): "
++ "Failed to write register to index 0x%x, value 0x%x, error %i",
++ index, value, ret);
++ gspca_dev->usb_err = ret;
++ }
++}
++
++static void reg_w_seq(struct gspca_dev *gspca_dev,
++ const __u8 *seq, int len)
++{
++ while (--len >= 0) {
++ reg_w(gspca_dev, seq[0], seq[1]);
++ seq += 2;
++ }
++}
++
++/* load the beginning of a page */
++static void reg_w_page(struct gspca_dev *gspca_dev,
++ const __u8 *page, int len)
++{
++ int index;
++ int ret = 0;
++
++ if (gspca_dev->usb_err < 0)
++ return;
++ for (index = 0; index < len; index++) {
++ if (page[index] == SKIP) /* skip this index */
++ continue;
++ gspca_dev->usb_buf[0] = page[index];
++ ret = usb_control_msg(gspca_dev->dev,
++ usb_sndctrlpipe(gspca_dev->dev, 0),
++ 0, /* request */
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ 0, index, gspca_dev->usb_buf, 1,
++ 500);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "reg_w_page(): "
++ "Failed to write register to index 0x%x, "
++ "value 0x%x, error %i",
++ index, page[index], ret);
++ gspca_dev->usb_err = ret;
++ break;
++ }
++ }
++}
++
++/* output a variable sequence */
++static void reg_w_var(struct gspca_dev *gspca_dev,
++ const __u8 *seq,
++ const __u8 *page3, unsigned int page3_len)
++{
++ int index, len;
++
++ for (;;) {
++ index = *seq++;
++ len = *seq++;
++ switch (len) {
++ case END_OF_SEQUENCE:
++ return;
++ case LOAD_PAGE3:
++ reg_w_page(gspca_dev, page3, page3_len);
++ break;
++ default:
++ if (len > USB_BUF_SZ) {
++ PDEBUG(D_ERR|D_STREAM,
++ "Incorrect variable sequence");
++ return;
++ }
++ while (len > 0) {
++ if (len < 8) {
++ reg_w_buf(gspca_dev,
++ index, seq, len);
++ seq += len;
++ break;
++ }
++ reg_w_buf(gspca_dev, index, seq, 8);
++ seq += 8;
++ index += 8;
++ len -= 8;
++ }
++ }
++ }
++ /* not reached */
++}
++
++/* this function is called at probe time for pac7302 */
++static int sd_config(struct gspca_dev *gspca_dev,
++ const struct usb_device_id *id)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ struct cam *cam;
++
++ cam = &gspca_dev->cam;
++
++ PDEBUG(D_CONF, "Find Sensor PAC7302");
++ cam->cam_mode = vga_mode; /* only 640x480 */
++ cam->nmodes = ARRAY_SIZE(vga_mode);
++
++ sd->brightness = BRIGHTNESS_DEF;
++ sd->contrast = CONTRAST_DEF;
++ sd->colors = COLOR_DEF;
++ sd->white_balance = WHITEBALANCE_DEF;
++ sd->red_balance = REDBALANCE_DEF;
++ sd->blue_balance = BLUEBALANCE_DEF;
++ sd->gain = GAIN_DEF;
++ sd->exposure = EXPOSURE_DEF;
++ sd->autogain = AUTOGAIN_DEF;
++ sd->hflip = HFLIP_DEF;
++ sd->vflip = VFLIP_DEF;
++ sd->flags = id->driver_info;
++ return 0;
++}
++
++/* This function is used by pac7302 only */
++static void setbrightcont(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int i, v;
++ static const __u8 max[10] =
++ {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb,
++ 0xd4, 0xec};
++ static const __u8 delta[10] =
++ {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17,
++ 0x11, 0x0b};
++
++ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
++ for (i = 0; i < 10; i++) {
++ v = max[i];
++ v += (sd->brightness - BRIGHTNESS_MAX)
++ * 150 / BRIGHTNESS_MAX; /* 200 ? */
++ v -= delta[i] * sd->contrast / CONTRAST_MAX;
++ if (v < 0)
++ v = 0;
++ else if (v > 0xff)
++ v = 0xff;
++ reg_w(gspca_dev, 0xa2 + i, v);
++ }
++ reg_w(gspca_dev, 0xdc, 0x01);
++}
++
++/* This function is used by pac7302 only */
++static void setcolors(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int i, v;
++ static const int a[9] =
++ {217, -212, 0, -101, 170, -67, -38, -315, 355};
++ static const int b[9] =
++ {19, 106, 0, 19, 106, 1, 19, 106, 1};
++
++ reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
++ reg_w(gspca_dev, 0x11, 0x01);
++ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
++ for (i = 0; i < 9; i++) {
++ v = a[i] * sd->colors / COLOR_MAX + b[i];
++ reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07);
++ reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
++ }
++ reg_w(gspca_dev, 0xdc, 0x01);
++ PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors);
++}
++
++static void setwhitebalance(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
++ reg_w(gspca_dev, 0xc6, sd->white_balance);
++
++ reg_w(gspca_dev, 0xdc, 0x01);
++ PDEBUG(D_CONF|D_STREAM, "white_balance: %i", sd->white_balance);
++}
++
++static void setredbalance(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
++ reg_w(gspca_dev, 0xc5, sd->red_balance);
++
++ reg_w(gspca_dev, 0xdc, 0x01);
++ PDEBUG(D_CONF|D_STREAM, "red_balance: %i", sd->red_balance);
++}
++
++static void setbluebalance(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
++ reg_w(gspca_dev, 0xc7, sd->blue_balance);
++
++ reg_w(gspca_dev, 0xdc, 0x01);
++ PDEBUG(D_CONF|D_STREAM, "blue_balance: %i", sd->blue_balance);
++}
++
++static void setgain(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
++ reg_w(gspca_dev, 0x10, sd->gain >> 3);
++
++ /* load registers to sensor (Bit 0, auto clear) */
++ reg_w(gspca_dev, 0x11, 0x01);
++}
++
++static void setexposure(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ __u8 reg;
++
++ /* register 2 of frame 3/4 contains the clock divider configuring the
++ no fps according to the formula: 60 / reg. sd->exposure is the
++ desired exposure time in ms. */
++ reg = 120 * sd->exposure / 1000;
++ if (reg < 2)
++ reg = 2;
++ else if (reg > 63)
++ reg = 63;
++
++ /* On the pac7302 reg2 MUST be a multiple of 3, so round it to
++ the nearest multiple of 3, except when between 6 and 12? */
++ if (reg < 6 || reg > 12)
++ reg = ((reg + 1) / 3) * 3;
++ reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
++ reg_w(gspca_dev, 0x02, reg);
++
++ /* load registers to sensor (Bit 0, auto clear) */
++ reg_w(gspca_dev, 0x11, 0x01);
++}
++
++static void sethvflip(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ u8 data, hflip, vflip;
++
++ hflip = sd->hflip;
++ if (sd->flags & FL_HFLIP)
++ hflip = !hflip;
++ vflip = sd->vflip;
++ if (sd->flags & FL_VFLIP)
++ vflip = !vflip;
++
++ reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
++ data = (hflip ? 0x08 : 0x00) | (vflip ? 0x04 : 0x00);
++ reg_w(gspca_dev, 0x21, data);
++
++ /* load registers to sensor (Bit 0, auto clear) */
++ reg_w(gspca_dev, 0x11, 0x01);
++}
++
++/* this function is called at probe and resume time for pac7302 */
++static int sd_init(struct gspca_dev *gspca_dev)
++{
++ reg_w_seq(gspca_dev, init_7302, sizeof(init_7302)/2);
++ return gspca_dev->usb_err;
++}
++
++static int sd_start(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->sof_read = 0;
++
++ reg_w_var(gspca_dev, start_7302,
++ page3_7302, sizeof(page3_7302));
++ setbrightcont(gspca_dev);
++ setcolors(gspca_dev);
++ setwhitebalance(gspca_dev);
++ setredbalance(gspca_dev);
++ setbluebalance(gspca_dev);
++ setgain(gspca_dev);
++ setexposure(gspca_dev);
++ sethvflip(gspca_dev);
++
++ /* only resolution 640x480 is supported for pac7302 */
++
++ sd->sof_read = 0;
++ sd->autogain_ignore_frames = 0;
++ atomic_set(&sd->avg_lum, -1);
++
++ /* start stream */
++ reg_w(gspca_dev, 0xff, 0x01);
++ reg_w(gspca_dev, 0x78, 0x01);
++
++ return gspca_dev->usb_err;
++}
++
++static void sd_stopN(struct gspca_dev *gspca_dev)
++{
++
++ /* stop stream */
++ reg_w(gspca_dev, 0xff, 0x01);
++ reg_w(gspca_dev, 0x78, 0x00);
++}
++
++/* called on streamoff with alt 0 and on disconnect for pac7302 */
++static void sd_stop0(struct gspca_dev *gspca_dev)
++{
++ if (!gspca_dev->present)
++ return;
++ reg_w(gspca_dev, 0xff, 0x01);
++ reg_w(gspca_dev, 0x78, 0x40);
++}
++
++/* Include pac common sof detection functions */
++#include "pac_common.h"
++
++static void do_autogain(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int avg_lum = atomic_read(&sd->avg_lum);
++ int desired_lum, deadzone;
++
++ if (avg_lum == -1)
++ return;
++
++ desired_lum = 270 + sd->brightness * 4;
++ /* Hack hack, with the 7202 the first exposure step is
++ pretty large, so if we're about to make the first
++ exposure increase make the deadzone large to avoid
++ oscilating */
++ if (desired_lum > avg_lum && sd->gain == GAIN_DEF &&
++ sd->exposure > EXPOSURE_DEF &&
++ sd->exposure < 42)
++ deadzone = 90;
++ else
++ deadzone = 30;
++
++ if (sd->autogain_ignore_frames > 0)
++ sd->autogain_ignore_frames--;
++ else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum,
++ deadzone, GAIN_KNEE, EXPOSURE_KNEE))
++ sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
++}
++
++/* JPEG header, part 1 */
++static const unsigned char pac_jpeg_header1[] = {
++ 0xff, 0xd8, /* SOI: Start of Image */
++
++ 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */
++ 0x00, 0x11, /* length = 17 bytes (including this length field) */
++ 0x08 /* Precision: 8 */
++ /* 2 bytes is placed here: number of image lines */
++ /* 2 bytes is placed here: samples per line */
++};
++
++/* JPEG header, continued */
++static const unsigned char pac_jpeg_header2[] = {
++ 0x03, /* Number of image components: 3 */
++ 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */
++ 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */
++ 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */
++
++ 0xff, 0xda, /* SOS: Start Of Scan */
++ 0x00, 0x0c, /* length = 12 bytes (including this length field) */
++ 0x03, /* number of components: 3 */
++ 0x01, 0x00, /* selector 1, table 0x00 */
++ 0x02, 0x11, /* selector 2, table 0x11 */
++ 0x03, 0x11, /* selector 3, table 0x11 */
++ 0x00, 0x3f, /* Spectral selection: 0 .. 63 */
++ 0x00 /* Successive approximation: 0 */
++};
++
++static void pac_start_frame(struct gspca_dev *gspca_dev,
++ struct gspca_frame *frame,
++ __u16 lines, __u16 samples_per_line)
++{
++ unsigned char tmpbuf[4];
++
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
++ pac_jpeg_header1, sizeof(pac_jpeg_header1));
++
++ tmpbuf[0] = lines >> 8;
++ tmpbuf[1] = lines & 0xff;
++ tmpbuf[2] = samples_per_line >> 8;
++ tmpbuf[3] = samples_per_line & 0xff;
++
++ gspca_frame_add(gspca_dev, INTER_PACKET,
++ tmpbuf, sizeof(tmpbuf));
++ gspca_frame_add(gspca_dev, INTER_PACKET,
++ pac_jpeg_header2, sizeof(pac_jpeg_header2));
++}
++
++/* this function is run at interrupt level */
++static void sd_pkt_scan(struct gspca_dev *gspca_dev,
++ u8 *data, /* isoc packet */
++ int len) /* iso packet length */
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ struct gspca_frame *frame;
++ unsigned char *sof;
++
++ sof = pac_find_sof(&sd->sof_read, data, len);
++ if (sof) {
++ int n, lum_offset, footer_length;
++
++ frame = gspca_get_i_frame(gspca_dev);
++ if (frame == NULL) {
++ gspca_dev->last_packet_type = DISCARD_PACKET;
++ return;
++ }
++
++ /* 6 bytes after the FF D9 EOF marker a number of lumination
++ bytes are send corresponding to different parts of the
++ image, the 14th and 15th byte after the EOF seem to
++ correspond to the center of the image */
++ lum_offset = 61 + sizeof pac_sof_marker;
++ footer_length = 74;
++
++ /* Finish decoding current frame */
++ n = (sof - data) - (footer_length + sizeof pac_sof_marker);
++ if (n < 0) {
++ frame->data_end += n;
++ n = 0;
++ }
++ gspca_frame_add(gspca_dev, INTER_PACKET,
++ data, n);
++ if (gspca_dev->last_packet_type != DISCARD_PACKET &&
++ frame->data_end[-2] == 0xff &&
++ frame->data_end[-1] == 0xd9)
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ NULL, 0);
++
++ n = sof - data;
++ len -= n;
++ data = sof;
++
++ /* Get average lumination */
++ if (gspca_dev->last_packet_type == LAST_PACKET &&
++ n >= lum_offset)
++ atomic_set(&sd->avg_lum, data[-lum_offset] +
++ data[-lum_offset + 1]);
++ else
++ atomic_set(&sd->avg_lum, -1);
++
++ /* Start the new frame with the jpeg header */
++ /* The PAC7302 has the image rotated 90 degrees */
++ pac_start_frame(gspca_dev, frame,
++ gspca_dev->width, gspca_dev->height);
++ }
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
++}
++
++static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->brightness = val;
++ if (gspca_dev->streaming)
++ setbrightcont(gspca_dev);
++ return gspca_dev->usb_err;
++}
++
++static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->brightness;
++ return 0;
++}
++
++static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->contrast = val;
++ if (gspca_dev->streaming) {
++ setbrightcont(gspca_dev);
++ }
++ return gspca_dev->usb_err;
++}
++
++static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->contrast;
++ return 0;
++}
++
++static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->colors = val;
++ if (gspca_dev->streaming)
++ setcolors(gspca_dev);
++ return gspca_dev->usb_err;
++}
++
++static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->colors;
++ return 0;
++}
++
++static int sd_setwhitebalance(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->white_balance = val;
++ if (gspca_dev->streaming)
++ setwhitebalance(gspca_dev);
++ return gspca_dev->usb_err;
++}
++
++static int sd_getwhitebalance(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->white_balance;
++ return 0;
++}
++
++static int sd_setredbalance(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->red_balance = val;
++ if (gspca_dev->streaming)
++ setredbalance(gspca_dev);
++ return gspca_dev->usb_err;
++}
++
++static int sd_getredbalance(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->red_balance;
++ return 0;
++}
++
++static int sd_setbluebalance(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->blue_balance = val;
++ if (gspca_dev->streaming)
++ setbluebalance(gspca_dev);
++ return gspca_dev->usb_err;
++}
++
++static int sd_getbluebalance(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->blue_balance;
++ return 0;
++}
++
++static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->gain = val;
++ if (gspca_dev->streaming)
++ setgain(gspca_dev);
++ return gspca_dev->usb_err;
++}
++
++static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->gain;
++ return 0;
++}
++
++static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->exposure = val;
++ if (gspca_dev->streaming)
++ setexposure(gspca_dev);
++ return gspca_dev->usb_err;
++}
++
++static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->exposure;
++ return 0;
++}
++
++static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->autogain = val;
++ /* when switching to autogain set defaults to make sure
++ we are on a valid point of the autogain gain /
++ exposure knee graph, and give this change time to
++ take effect before doing autogain. */
++ if (sd->autogain) {
++ sd->exposure = EXPOSURE_DEF;
++ sd->gain = GAIN_DEF;
++ if (gspca_dev->streaming) {
++ sd->autogain_ignore_frames =
++ PAC_AUTOGAIN_IGNORE_FRAMES;
++ setexposure(gspca_dev);
++ setgain(gspca_dev);
++ }
++ }
++
++ return gspca_dev->usb_err;
++}
++
++static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->autogain;
++ return 0;
++}
++
++static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->hflip = val;
++ if (gspca_dev->streaming)
++ sethvflip(gspca_dev);
++ return gspca_dev->usb_err;
++}
++
++static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->hflip;
++ return 0;
++}
++
++static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->vflip = val;
++ if (gspca_dev->streaming)
++ sethvflip(gspca_dev);
++ return gspca_dev->usb_err;
++}
++
++static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->vflip;
++ return 0;
++}
++
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++static int sd_dbg_s_register(struct gspca_dev *gspca_dev,
++ struct v4l2_dbg_register *reg)
++{
++ __u8 index;
++ __u8 value;
++
++ /* reg->reg: bit0..15: reserved for register index (wIndex is 16bit
++ long on the USB bus)
++ */
++ if (reg->match.type == V4L2_CHIP_MATCH_HOST &&
++ reg->match.addr == 0 &&
++ (reg->reg < 0x000000ff) &&
++ (reg->val <= 0x000000ff)
++ ) {
++ /* Currently writing to page 0 is only supported. */
++ /* reg_w() only supports 8bit index */
++ index = reg->reg & 0x000000ff;
++ value = reg->val & 0x000000ff;
++
++ /* Note that there shall be no access to other page
++ by any other function between the page swith and
++ the actual register write */
++ reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
++ reg_w(gspca_dev, index, value);
++
++ reg_w(gspca_dev, 0xdc, 0x01);
++ }
++ return gspca_dev->usb_err;
++}
++
++static int sd_chip_ident(struct gspca_dev *gspca_dev,
++ struct v4l2_dbg_chip_ident *chip)
++{
++ int ret = -EINVAL;
++
++ if (chip->match.type == V4L2_CHIP_MATCH_HOST &&
++ chip->match.addr == 0) {
++ chip->revision = 0;
++ chip->ident = V4L2_IDENT_UNKNOWN;
++ ret = 0;
++ }
++ return ret;
++}
++#endif
++
++/* sub-driver description for pac7302 */
++static const struct sd_desc sd_desc = {
++ .name = MODULE_NAME,
++ .ctrls = sd_ctrls,
++ .nctrls = ARRAY_SIZE(sd_ctrls),
++ .config = sd_config,
++ .init = sd_init,
++ .start = sd_start,
++ .stopN = sd_stopN,
++ .stop0 = sd_stop0,
++ .pkt_scan = sd_pkt_scan,
++ .dq_callback = do_autogain,
++#ifdef CONFIG_VIDEO_ADV_DEBUG
++ .set_register = sd_dbg_s_register,
++ .get_chip_ident = sd_chip_ident,
++#endif
++};
++
++/* -- module initialisation -- */
++static const struct usb_device_id device_table[] __devinitconst = {
++ {USB_DEVICE(0x06f8, 0x3009)},
++ {USB_DEVICE(0x093a, 0x2620)},
++ {USB_DEVICE(0x093a, 0x2621)},
++ {USB_DEVICE(0x093a, 0x2622), .driver_info = FL_VFLIP},
++ {USB_DEVICE(0x093a, 0x2624), .driver_info = FL_VFLIP},
++ {USB_DEVICE(0x093a, 0x2626)},
++ {USB_DEVICE(0x093a, 0x2628)},
++ {USB_DEVICE(0x093a, 0x2629), .driver_info = FL_VFLIP},
++ {USB_DEVICE(0x093a, 0x262a)},
++ {USB_DEVICE(0x093a, 0x262c)},
++ {}
++};
++MODULE_DEVICE_TABLE(usb, device_table);
++
++/* -- device connect -- */
++static int __devinit sd_probe(struct usb_interface *intf,
++ const struct usb_device_id *id)
++{
++ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
++ THIS_MODULE);
++}
++
++static struct usb_driver sd_driver = {
++ .name = MODULE_NAME,
++ .id_table = device_table,
++ .probe = sd_probe,
++ .disconnect = gspca_disconnect,
++#ifdef CONFIG_PM
++ .suspend = gspca_suspend,
++ .resume = gspca_resume,
++#endif
++};
++
++/* -- module insert / remove -- */
++static int __init sd_mod_init(void)
++{
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
++ PDEBUG(D_PROBE, "registered");
++ return 0;
++}
++static void __exit sd_mod_exit(void)
++{
++ usb_deregister(&sd_driver);
++ PDEBUG(D_PROBE, "deregistered");
++}
++
++module_init(sd_mod_init);
++module_exit(sd_mod_exit);
+diff --git a/drivers/media/video/gspca/pac7311.c b/drivers/media/video/gspca/pac7311.c
+index 0527144..101a058 100644
+--- a/drivers/media/video/gspca/pac7311.c
++++ b/drivers/media/video/gspca/pac7311.c
+@@ -57,23 +57,17 @@ MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
+ MODULE_DESCRIPTION("Pixart PAC7311");
+ MODULE_LICENSE("GPL");
+
+-/* specific webcam descriptor */
++/* specific webcam descriptor for pac7311 */
+ struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+- unsigned char brightness;
+ unsigned char contrast;
+- unsigned char colors;
+ unsigned char gain;
+ unsigned char exposure;
+ unsigned char autogain;
+ __u8 hflip;
+ __u8 vflip;
+
+- __u8 sensor;
+-#define SENSOR_PAC7302 0
+-#define SENSOR_PAC7311 1
+-
+ u8 sof_read;
+ u8 autogain_ignore_frames;
+
+@@ -81,12 +75,8 @@ struct sd {
+ };
+
+ /* V4L2 controls supported by the driver */
+-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
+@@ -98,24 +88,7 @@ static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
+
+-static struct ctrl sd_ctrls[] = {
+-/* This control is pac7302 only */
+-#define BRIGHTNESS_IDX 0
+- {
+- {
+- .id = V4L2_CID_BRIGHTNESS,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- .name = "Brightness",
+- .minimum = 0,
+-#define BRIGHTNESS_MAX 0x20
+- .maximum = BRIGHTNESS_MAX,
+- .step = 1,
+-#define BRIGHTNESS_DEF 0x10
+- .default_value = BRIGHTNESS_DEF,
+- },
+- .set = sd_setbrightness,
+- .get = sd_getbrightness,
+- },
++static const struct ctrl sd_ctrls[] = {
+ /* This control is for both the 7302 and the 7311 */
+ {
+ {
+@@ -132,23 +105,6 @@ static struct ctrl sd_ctrls[] = {
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+-/* This control is pac7302 only */
+-#define SATURATION_IDX 2
+- {
+- {
+- .id = V4L2_CID_SATURATION,
+- .type = V4L2_CTRL_TYPE_INTEGER,
+- .name = "Saturation",
+- .minimum = 0,
+-#define COLOR_MAX 255
+- .maximum = COLOR_MAX,
+- .step = 1,
+-#define COLOR_DEF 127
+- .default_value = COLOR_DEF,
+- },
+- .set = sd_setcolors,
+- .get = sd_getcolors,
+- },
+ /* All controls below are for both the 7302 and the 7311 */
+ {
+ {
+@@ -244,101 +200,8 @@ static const struct v4l2_pix_format vga_mode[] = {
+ .priv = 0},
+ };
+
+-/* pac 7302 */
+-static const __u8 init_7302[] = {
+-/* index,value */
+- 0xff, 0x01, /* page 1 */
+- 0x78, 0x00, /* deactivate */
+- 0xff, 0x01,
+- 0x78, 0x40, /* led off */
+-};
+-static const __u8 start_7302[] = {
+-/* index, len, [value]* */
+- 0xff, 1, 0x00, /* page 0 */
+- 0x00, 12, 0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80,
+- 0x00, 0x00, 0x00, 0x00,
+- 0x0d, 24, 0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00,
+- 0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7,
+- 0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11,
+- 0x26, 2, 0xaa, 0xaa,
+- 0x2e, 1, 0x31,
+- 0x38, 1, 0x01,
+- 0x3a, 3, 0x14, 0xff, 0x5a,
+- 0x43, 11, 0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11,
+- 0x00, 0x54, 0x11,
+- 0x55, 1, 0x00,
+- 0x62, 4, 0x10, 0x1e, 0x1e, 0x18,
+- 0x6b, 1, 0x00,
+- 0x6e, 3, 0x08, 0x06, 0x00,
+- 0x72, 3, 0x00, 0xff, 0x00,
+- 0x7d, 23, 0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c,
+- 0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50,
+- 0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00,
+- 0xa2, 10, 0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9,
+- 0xd2, 0xeb,
+- 0xaf, 1, 0x02,
+- 0xb5, 2, 0x08, 0x08,
+- 0xb8, 2, 0x08, 0x88,
+- 0xc4, 4, 0xae, 0x01, 0x04, 0x01,
+- 0xcc, 1, 0x00,
+- 0xd1, 11, 0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9,
+- 0xc1, 0xd7, 0xec,
+- 0xdc, 1, 0x01,
+- 0xff, 1, 0x01, /* page 1 */
+- 0x12, 3, 0x02, 0x00, 0x01,
+- 0x3e, 2, 0x00, 0x00,
+- 0x76, 5, 0x01, 0x20, 0x40, 0x00, 0xf2,
+- 0x7c, 1, 0x00,
+- 0x7f, 10, 0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20,
+- 0x02, 0x00,
+- 0x96, 5, 0x01, 0x10, 0x04, 0x01, 0x04,
+- 0xc8, 14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
+- 0x07, 0x00, 0x01, 0x07, 0x04, 0x01,
+- 0xd8, 1, 0x01,
+- 0xdb, 2, 0x00, 0x01,
+- 0xde, 7, 0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00,
+- 0xe6, 4, 0x00, 0x00, 0x00, 0x01,
+- 0xeb, 1, 0x00,
+- 0xff, 1, 0x02, /* page 2 */
+- 0x22, 1, 0x00,
+- 0xff, 1, 0x03, /* page 3 */
+- 0x00, 255, /* load the page 3 */
+- 0x11, 1, 0x01,
+- 0xff, 1, 0x02, /* page 2 */
+- 0x13, 1, 0x00,
+- 0x22, 4, 0x1f, 0xa4, 0xf0, 0x96,
+- 0x27, 2, 0x14, 0x0c,
+- 0x2a, 5, 0xc8, 0x00, 0x18, 0x12, 0x22,
+- 0x64, 8, 0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44,
+- 0x6e, 1, 0x08,
+- 0xff, 1, 0x01, /* page 1 */
+- 0x78, 1, 0x00,
+- 0, 0 /* end of sequence */
+-};
+-
+-/* page 3 - the value 0xaa says skip the index - see reg_w_page() */
+-static const __u8 page3_7302[] = {
+- 0x90, 0x40, 0x03, 0x50, 0xc2, 0x01, 0x14, 0x16,
+- 0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00,
+- 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+- 0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00,
+- 0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21,
+- 0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54,
+- 0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00,
+- 0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+- 0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00,
+- 0x00, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00,
+- 0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+- 0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00,
+- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+- 0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8,
+- 0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
+- 0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00,
+- 0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00,
+- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+- 0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00,
+- 0x00
+-};
++#define LOAD_PAGE4 254
++#define END_OF_SEQUENCE 0
+
+ /* pac 7311 */
+ static const __u8 init_7311[] = {
+@@ -378,18 +241,19 @@ static const __u8 start_7311[] = {
+ 0xf0, 13, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x20, 0x00,
+ 0x3f, 0x00, 0x0a, 0x01, 0x00,
+ 0xff, 1, 0x04, /* page 4 */
+- 0x00, 254, /* load the page 4 */
++ 0, LOAD_PAGE4, /* load the page 4 */
+ 0x11, 1, 0x01,
+- 0, 0 /* end of sequence */
++ 0, END_OF_SEQUENCE /* end of sequence */
+ };
+
+-/* page 4 - the value 0xaa says skip the index - see reg_w_page() */
++#define SKIP 0xaa
++/* page 4 - the value SKIP says skip the index - see reg_w_page() */
+ static const __u8 page4_7311[] = {
+- 0xaa, 0xaa, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f,
+- 0x09, 0x00, 0xaa, 0xaa, 0x07, 0x00, 0x00, 0x62,
+- 0x08, 0xaa, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
+- 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, 0xaa,
+- 0xaa, 0x00, 0x08, 0xaa, 0x03, 0xaa, 0x00, 0x68,
++ SKIP, SKIP, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f,
++ 0x09, 0x00, SKIP, SKIP, 0x07, 0x00, 0x00, 0x62,
++ 0x08, SKIP, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, SKIP,
++ SKIP, 0x00, 0x08, SKIP, 0x03, SKIP, 0x00, 0x68,
+ 0xca, 0x10, 0x06, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x28, 0x04, 0x11, 0x00, 0x00
+ };
+@@ -398,28 +262,69 @@ static void reg_w_buf(struct gspca_dev *gspca_dev,
+ __u8 index,
+ const char *buffer, int len)
+ {
++ int ret;
++
++ if (gspca_dev->usb_err < 0)
++ return;
+ memcpy(gspca_dev->usb_buf, buffer, len);
+- usb_control_msg(gspca_dev->dev,
++ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 1, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, /* value */
+ index, gspca_dev->usb_buf, len,
+ 500);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "reg_w_buf(): "
++ "Failed to write registers to index 0x%x, error %i",
++ index, ret);
++ gspca_dev->usb_err = ret;
++ }
+ }
+
++#if 0 /* not used */
++static __u8 reg_r(struct gspca_dev *gspca_dev,
++ __u8 index)
++{
++ int ret;
++
++ ret = usb_control_msg(gspca_dev->dev,
++ usb_rcvctrlpipe(gspca_dev->dev, 0),
++ 0, /* request */
++ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ 0, /* value */
++ index, gspca_dev->usb_buf, 1,
++ 500);
++ if (ret < 0)
++ PDEBUG(D_ERR, "reg_r(): "
++ "Failed to read register from index 0x%x, error %i",
++ index, ret);
++
++ return gspca_dev->usb_buf[0];
++}
++#endif
+
+ static void reg_w(struct gspca_dev *gspca_dev,
+ __u8 index,
+ __u8 value)
+ {
++ int ret;
++
++ if (gspca_dev->usb_err < 0)
++ return;
+ gspca_dev->usb_buf[0] = value;
+- usb_control_msg(gspca_dev->dev,
++ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, gspca_dev->usb_buf, 1,
+ 500);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "reg_w(): "
++ "Failed to write register to index 0x%x, value 0x%x, error %i",
++ index, value, ret);
++ gspca_dev->usb_err = ret;
++ }
+ }
+
+ static void reg_w_seq(struct gspca_dev *gspca_dev,
+@@ -436,23 +341,35 @@ static void reg_w_page(struct gspca_dev *gspca_dev,
+ const __u8 *page, int len)
+ {
+ int index;
++ int ret = 0;
+
++ if (gspca_dev->usb_err < 0)
++ return;
+ for (index = 0; index < len; index++) {
+- if (page[index] == 0xaa) /* skip this index */
++ if (page[index] == SKIP) /* skip this index */
+ continue;
+ gspca_dev->usb_buf[0] = page[index];
+- usb_control_msg(gspca_dev->dev,
++ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ 0, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 0, index, gspca_dev->usb_buf, 1,
+ 500);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "reg_w_page(): "
++ "Failed to write register to index 0x%x, "
++ "value 0x%x, error %i",
++ index, page[index], ret);
++ gspca_dev->usb_err = ret;
++ break;
++ }
+ }
+ }
+
+ /* output a variable sequence */
+ static void reg_w_var(struct gspca_dev *gspca_dev,
+- const __u8 *seq)
++ const __u8 *seq,
++ const __u8 *page4, unsigned int page4_len)
+ {
+ int index, len;
+
+@@ -460,23 +377,21 @@ static void reg_w_var(struct gspca_dev *gspca_dev,
+ index = *seq++;
+ len = *seq++;
+ switch (len) {
+- case 0:
++ case END_OF_SEQUENCE:
+ return;
+- case 254:
+- reg_w_page(gspca_dev, page4_7311, sizeof page4_7311);
+- break;
+- case 255:
+- reg_w_page(gspca_dev, page3_7302, sizeof page3_7302);
++ case LOAD_PAGE4:
++ reg_w_page(gspca_dev, page4, page4_len);
+ break;
+ default:
+- if (len > 64) {
++ if (len > USB_BUF_SZ) {
+ PDEBUG(D_ERR|D_STREAM,
+ "Incorrect variable sequence");
+ return;
+ }
+ while (len > 0) {
+ if (len < 8) {
+- reg_w_buf(gspca_dev, index, seq, len);
++ reg_w_buf(gspca_dev,
++ index, seq, len);
+ seq += len;
+ break;
+ }
+@@ -490,7 +405,7 @@ static void reg_w_var(struct gspca_dev *gspca_dev,
+ /* not reached */
+ }
+
+-/* this function is called at probe time */
++/* this function is called at probe time for pac7311 */
+ static int sd_config(struct gspca_dev *gspca_dev,
+ const struct usb_device_id *id)
+ {
+@@ -499,22 +414,11 @@ static int sd_config(struct gspca_dev *gspca_dev,
+
+ cam = &gspca_dev->cam;
+
+- sd->sensor = id->driver_info;
+- if (sd->sensor == SENSOR_PAC7302) {
+- PDEBUG(D_CONF, "Find Sensor PAC7302");
+- cam->cam_mode = &vga_mode[2]; /* only 640x480 */
+- cam->nmodes = 1;
+- } else {
+- PDEBUG(D_CONF, "Find Sensor PAC7311");
+- cam->cam_mode = vga_mode;
+- cam->nmodes = ARRAY_SIZE(vga_mode);
+- gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX)
+- | (1 << SATURATION_IDX);
+- }
++ PDEBUG(D_CONF, "Find Sensor PAC7311");
++ cam->cam_mode = vga_mode;
++ cam->nmodes = ARRAY_SIZE(vga_mode);
+
+- sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+- sd->colors = COLOR_DEF;
+ sd->gain = GAIN_DEF;
+ sd->exposure = EXPOSURE_DEF;
+ sd->autogain = AUTOGAIN_DEF;
+@@ -523,33 +427,6 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ return 0;
+ }
+
+-/* This function is used by pac7302 only */
+-static void setbrightcont(struct gspca_dev *gspca_dev)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+- int i, v;
+- static const __u8 max[10] =
+- {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb,
+- 0xd4, 0xec};
+- static const __u8 delta[10] =
+- {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17,
+- 0x11, 0x0b};
+-
+- reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+- for (i = 0; i < 10; i++) {
+- v = max[i];
+- v += (sd->brightness - BRIGHTNESS_MAX)
+- * 150 / BRIGHTNESS_MAX; /* 200 ? */
+- v -= delta[i] * sd->contrast / CONTRAST_MAX;
+- if (v < 0)
+- v = 0;
+- else if (v > 0xff)
+- v = 0xff;
+- reg_w(gspca_dev, 0xa2 + i, v);
+- }
+- reg_w(gspca_dev, 0xdc, 0x01);
+-}
+-
+ /* This function is used by pac7311 only */
+ static void setcontrast(struct gspca_dev *gspca_dev)
+ {
+@@ -561,46 +438,19 @@ static void setcontrast(struct gspca_dev *gspca_dev)
+ reg_w(gspca_dev, 0x11, 0x01);
+ }
+
+-/* This function is used by pac7302 only */
+-static void setcolors(struct gspca_dev *gspca_dev)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+- int i, v;
+- static const int a[9] =
+- {217, -212, 0, -101, 170, -67, -38, -315, 355};
+- static const int b[9] =
+- {19, 106, 0, 19, 106, 1, 19, 106, 1};
+-
+- reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+- reg_w(gspca_dev, 0x11, 0x01);
+- reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+- reg_w(gspca_dev, 0xff, 0x00); /* page 0 */
+- for (i = 0; i < 9; i++) {
+- v = a[i] * sd->colors / COLOR_MAX + b[i];
+- reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07);
+- reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
+- }
+- reg_w(gspca_dev, 0xdc, 0x01);
+- PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors);
+-}
+-
+ static void setgain(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
++ int gain = GAIN_MAX - sd->gain;
++
++ if (gain < 1)
++ gain = 1;
++ else if (gain > 245)
++ gain = 245;
++ reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
++ reg_w(gspca_dev, 0x0e, 0x00);
++ reg_w(gspca_dev, 0x0f, gain);
+
+- if (sd->sensor == SENSOR_PAC7302) {
+- reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+- reg_w(gspca_dev, 0x10, sd->gain >> 3);
+- } else {
+- int gain = GAIN_MAX - sd->gain;
+- if (gain < 1)
+- gain = 1;
+- else if (gain > 245)
+- gain = 245;
+- reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
+- reg_w(gspca_dev, 0x0e, 0x00);
+- reg_w(gspca_dev, 0x0f, gain);
+- }
+ /* load registers to sensor (Bit 0, auto clear) */
+ reg_w(gspca_dev, 0x11, 0x01);
+ }
+@@ -619,25 +469,19 @@ static void setexposure(struct gspca_dev *gspca_dev)
+ else if (reg > 63)
+ reg = 63;
+
+- if (sd->sensor == SENSOR_PAC7302) {
+- /* On the pac7302 reg2 MUST be a multiple of 3, so round it to
+- the nearest multiple of 3, except when between 6 and 12? */
+- if (reg < 6 || reg > 12)
+- reg = ((reg + 1) / 3) * 3;
+- reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+- reg_w(gspca_dev, 0x02, reg);
++ reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
++ reg_w(gspca_dev, 0x02, reg);
++
++ /* Page 1 register 8 must always be 0x08 except when not in
++ 640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */
++ reg_w(gspca_dev, 0xff, 0x01);
++ if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv &&
++ reg <= 3) {
++ reg_w(gspca_dev, 0x08, 0x09);
+ } else {
+- reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
+- reg_w(gspca_dev, 0x02, reg);
+- /* Page 1 register 8 must always be 0x08 except when not in
+- 640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */
+- reg_w(gspca_dev, 0xff, 0x01);
+- if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv &&
+- reg <= 3)
+- reg_w(gspca_dev, 0x08, 0x09);
+- else
+- reg_w(gspca_dev, 0x08, 0x08);
++ reg_w(gspca_dev, 0x08, 0x08);
+ }
++
+ /* load registers to sensor (Bit 0, auto clear) */
+ reg_w(gspca_dev, 0x11, 0x01);
+ }
+@@ -647,31 +491,19 @@ static void sethvflip(struct gspca_dev *gspca_dev)
+ struct sd *sd = (struct sd *) gspca_dev;
+ __u8 data;
+
+- if (sd->sensor == SENSOR_PAC7302) {
+- reg_w(gspca_dev, 0xff, 0x03); /* page 3 */
+- data = (sd->hflip ? 0x08 : 0x00)
+- | (sd->vflip ? 0x04 : 0x00);
+- } else {
+- reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
+- data = (sd->hflip ? 0x04 : 0x00)
+- | (sd->vflip ? 0x08 : 0x00);
+- }
++ reg_w(gspca_dev, 0xff, 0x04); /* page 4 */
++ data = (sd->hflip ? 0x04 : 0x00) | (sd->vflip ? 0x08 : 0x00);
+ reg_w(gspca_dev, 0x21, data);
++
+ /* load registers to sensor (Bit 0, auto clear) */
+ reg_w(gspca_dev, 0x11, 0x01);
+ }
+
+-/* this function is called at probe and resume time */
++/* this function is called at probe and resume time for pac7311 */
+ static int sd_init(struct gspca_dev *gspca_dev)
+ {
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- if (sd->sensor == SENSOR_PAC7302)
+- reg_w_seq(gspca_dev, init_7302, sizeof init_7302);
+- else
+- reg_w_seq(gspca_dev, init_7311, sizeof init_7311);
+-
+- return 0;
++ reg_w_seq(gspca_dev, init_7311, sizeof(init_7311)/2);
++ return gspca_dev->usb_err;
+ }
+
+ static int sd_start(struct gspca_dev *gspca_dev)
+@@ -680,14 +512,9 @@ static int sd_start(struct gspca_dev *gspca_dev)
+
+ sd->sof_read = 0;
+
+- if (sd->sensor == SENSOR_PAC7302) {
+- reg_w_var(gspca_dev, start_7302);
+- setbrightcont(gspca_dev);
+- setcolors(gspca_dev);
+- } else {
+- reg_w_var(gspca_dev, start_7311);
+- setcontrast(gspca_dev);
+- }
++ reg_w_var(gspca_dev, start_7311,
++ page4_7311, sizeof(page4_7311));
++ setcontrast(gspca_dev);
+ setgain(gspca_dev);
+ setexposure(gspca_dev);
+ sethvflip(gspca_dev);
+@@ -705,8 +532,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ reg_w(gspca_dev, 0x87, 0x11);
+ break;
+ case 0: /* 640x480 */
+- if (sd->sensor == SENSOR_PAC7302)
+- break;
+ reg_w(gspca_dev, 0xff, 0x01);
+ reg_w(gspca_dev, 0x17, 0x00);
+ reg_w(gspca_dev, 0x87, 0x12);
+@@ -719,23 +544,13 @@ static int sd_start(struct gspca_dev *gspca_dev)
+
+ /* start stream */
+ reg_w(gspca_dev, 0xff, 0x01);
+- if (sd->sensor == SENSOR_PAC7302)
+- reg_w(gspca_dev, 0x78, 0x01);
+- else
+- reg_w(gspca_dev, 0x78, 0x05);
+- return 0;
++ reg_w(gspca_dev, 0x78, 0x05);
++
++ return gspca_dev->usb_err;
+ }
+
+ static void sd_stopN(struct gspca_dev *gspca_dev)
+ {
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- if (sd->sensor == SENSOR_PAC7302) {
+- reg_w(gspca_dev, 0xff, 0x01);
+- reg_w(gspca_dev, 0x78, 0x00);
+- reg_w(gspca_dev, 0x78, 0x00);
+- return;
+- }
+ reg_w(gspca_dev, 0xff, 0x04);
+ reg_w(gspca_dev, 0x27, 0x80);
+ reg_w(gspca_dev, 0x28, 0xca);
+@@ -748,17 +563,9 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
+ reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
+ }
+
+-/* called on streamoff with alt 0 and on disconnect */
++/* called on streamoff with alt 0 and on disconnect for 7311 */
+ static void sd_stop0(struct gspca_dev *gspca_dev)
+ {
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- if (!gspca_dev->present)
+- return;
+- if (sd->sensor == SENSOR_PAC7302) {
+- reg_w(gspca_dev, 0xff, 0x01);
+- reg_w(gspca_dev, 0x78, 0x40);
+- }
+ }
+
+ /* Include pac common sof detection functions */
+@@ -773,22 +580,8 @@ static void do_autogain(struct gspca_dev *gspca_dev)
+ if (avg_lum == -1)
+ return;
+
+- if (sd->sensor == SENSOR_PAC7302) {
+- desired_lum = 270 + sd->brightness * 4;
+- /* Hack hack, with the 7202 the first exposure step is
+- pretty large, so if we're about to make the first
+- exposure increase make the deadzone large to avoid
+- oscilating */
+- if (desired_lum > avg_lum && sd->gain == GAIN_DEF &&
+- sd->exposure > EXPOSURE_DEF &&
+- sd->exposure < 42)
+- deadzone = 90;
+- else
+- deadzone = 30;
+- } else {
+- desired_lum = 200;
+- deadzone = 20;
+- }
++ desired_lum = 200;
++ deadzone = 20;
+
+ if (sd->autogain_ignore_frames > 0)
+ sd->autogain_ignore_frames--;
+@@ -797,53 +590,92 @@ static void do_autogain(struct gspca_dev *gspca_dev)
+ sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
+ }
+
+-static const unsigned char pac7311_jpeg_header1[] = {
+- 0xff, 0xd8, 0xff, 0xc0, 0x00, 0x11, 0x08
++/* JPEG header, part 1 */
++static const unsigned char pac_jpeg_header1[] = {
++ 0xff, 0xd8, /* SOI: Start of Image */
++
++ 0xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */
++ 0x00, 0x11, /* length = 17 bytes (including this length field) */
++ 0x08 /* Precision: 8 */
++ /* 2 bytes is placed here: number of image lines */
++ /* 2 bytes is placed here: samples per line */
+ };
+
+-static const unsigned char pac7311_jpeg_header2[] = {
+- 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda,
+- 0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
++/* JPEG header, continued */
++static const unsigned char pac_jpeg_header2[] = {
++ 0x03, /* Number of image components: 3 */
++ 0x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */
++ 0x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */
++ 0x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */
++
++ 0xff, 0xda, /* SOS: Start Of Scan */
++ 0x00, 0x0c, /* length = 12 bytes (including this length field) */
++ 0x03, /* number of components: 3 */
++ 0x01, 0x00, /* selector 1, table 0x00 */
++ 0x02, 0x11, /* selector 2, table 0x11 */
++ 0x03, 0x11, /* selector 3, table 0x11 */
++ 0x00, 0x3f, /* Spectral selection: 0 .. 63 */
++ 0x00 /* Successive approximation: 0 */
+ };
+
++static void pac_start_frame(struct gspca_dev *gspca_dev,
++ struct gspca_frame *frame,
++ __u16 lines, __u16 samples_per_line)
++{
++ unsigned char tmpbuf[4];
++
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
++ pac_jpeg_header1, sizeof(pac_jpeg_header1));
++
++ tmpbuf[0] = lines >> 8;
++ tmpbuf[1] = lines & 0xff;
++ tmpbuf[2] = samples_per_line >> 8;
++ tmpbuf[3] = samples_per_line & 0xff;
++
++ gspca_frame_add(gspca_dev, INTER_PACKET,
++ tmpbuf, sizeof(tmpbuf));
++ gspca_frame_add(gspca_dev, INTER_PACKET,
++ pac_jpeg_header2, sizeof(pac_jpeg_header2));
++}
++
+ /* this function is run at interrupt level */
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+- __u8 *data, /* isoc packet */
++ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ unsigned char *sof;
++ struct gspca_frame *frame;
+
+- sof = pac_find_sof(gspca_dev, data, len);
++ sof = pac_find_sof(&sd->sof_read, data, len);
+ if (sof) {
+- unsigned char tmpbuf[4];
+ int n, lum_offset, footer_length;
+
+- if (sd->sensor == SENSOR_PAC7302) {
+- /* 6 bytes after the FF D9 EOF marker a number of lumination
+- bytes are send corresponding to different parts of the
+- image, the 14th and 15th byte after the EOF seem to
+- correspond to the center of the image */
+- lum_offset = 61 + sizeof pac_sof_marker;
+- footer_length = 74;
+- } else {
+- lum_offset = 24 + sizeof pac_sof_marker;
+- footer_length = 26;
++ frame = gspca_get_i_frame(gspca_dev);
++ if (frame == NULL) {
++ gspca_dev->last_packet_type = DISCARD_PACKET;
++ return;
+ }
+
++ /* 6 bytes after the FF D9 EOF marker a number of lumination
++ bytes are send corresponding to different parts of the
++ image, the 14th and 15th byte after the EOF seem to
++ correspond to the center of the image */
++ lum_offset = 24 + sizeof pac_sof_marker;
++ footer_length = 26;
++
+ /* Finish decoding current frame */
+ n = (sof - data) - (footer_length + sizeof pac_sof_marker);
+ if (n < 0) {
+ frame->data_end += n;
+ n = 0;
+ }
+- frame = gspca_frame_add(gspca_dev, INTER_PACKET, frame,
++ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, n);
+ if (gspca_dev->last_packet_type != DISCARD_PACKET &&
+ frame->data_end[-2] == 0xff &&
+ frame->data_end[-1] == 0xd9)
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
++ gspca_frame_add(gspca_dev, LAST_PACKET,
+ NULL, 0);
+
+ n = sof - data;
+@@ -859,43 +691,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ atomic_set(&sd->avg_lum, -1);
+
+ /* Start the new frame with the jpeg header */
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+- pac7311_jpeg_header1, sizeof(pac7311_jpeg_header1));
+- if (sd->sensor == SENSOR_PAC7302) {
+- /* The PAC7302 has the image rotated 90 degrees */
+- tmpbuf[0] = gspca_dev->width >> 8;
+- tmpbuf[1] = gspca_dev->width & 0xff;
+- tmpbuf[2] = gspca_dev->height >> 8;
+- tmpbuf[3] = gspca_dev->height & 0xff;
+- } else {
+- tmpbuf[0] = gspca_dev->height >> 8;
+- tmpbuf[1] = gspca_dev->height & 0xff;
+- tmpbuf[2] = gspca_dev->width >> 8;
+- tmpbuf[3] = gspca_dev->width & 0xff;
+- }
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, tmpbuf, 4);
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+- pac7311_jpeg_header2, sizeof(pac7311_jpeg_header2));
++ pac_start_frame(gspca_dev, frame,
++ gspca_dev->height, gspca_dev->width);
+ }
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
+-}
+-
+-static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- sd->brightness = val;
+- if (gspca_dev->streaming)
+- setbrightcont(gspca_dev);
+- return 0;
+-}
+-
+-static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- *val = sd->brightness;
+- return 0;
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+
+ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+@@ -904,12 +703,9 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+
+ sd->contrast = val;
+ if (gspca_dev->streaming) {
+- if (sd->sensor == SENSOR_PAC7302)
+- setbrightcont(gspca_dev);
+- else
+- setcontrast(gspca_dev);
++ setcontrast(gspca_dev);
+ }
+- return 0;
++ return gspca_dev->usb_err;
+ }
+
+ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+@@ -920,24 +716,6 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+ return 0;
+ }
+
+-static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- sd->colors = val;
+- if (gspca_dev->streaming)
+- setcolors(gspca_dev);
+- return 0;
+-}
+-
+-static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+-{
+- struct sd *sd = (struct sd *) gspca_dev;
+-
+- *val = sd->colors;
+- return 0;
+-}
+-
+ static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -945,7 +723,7 @@ static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
+ sd->gain = val;
+ if (gspca_dev->streaming)
+ setgain(gspca_dev);
+- return 0;
++ return gspca_dev->usb_err;
+ }
+
+ static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+@@ -963,7 +741,7 @@ static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
+ sd->exposure = val;
+ if (gspca_dev->streaming)
+ setexposure(gspca_dev);
+- return 0;
++ return gspca_dev->usb_err;
+ }
+
+ static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
+@@ -994,7 +772,7 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
+ }
+ }
+
+- return 0;
++ return gspca_dev->usb_err;
+ }
+
+ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+@@ -1012,7 +790,7 @@ static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
+ sd->hflip = val;
+ if (gspca_dev->streaming)
+ sethvflip(gspca_dev);
+- return 0;
++ return gspca_dev->usb_err;
+ }
+
+ static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
+@@ -1030,7 +808,7 @@ static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
+ sd->vflip = val;
+ if (gspca_dev->streaming)
+ sethvflip(gspca_dev);
+- return 0;
++ return gspca_dev->usb_err;
+ }
+
+ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
+@@ -1041,8 +819,8 @@ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
+ return 0;
+ }
+
+-/* sub-driver description */
+-static struct sd_desc sd_desc = {
++/* sub-driver description for pac7311 */
++static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+@@ -1056,28 +834,19 @@ static struct sd_desc sd_desc = {
+ };
+
+ /* -- module initialisation -- */
+-static __devinitdata struct usb_device_id device_table[] = {
+- {USB_DEVICE(0x06f8, 0x3009), .driver_info = SENSOR_PAC7302},
+- {USB_DEVICE(0x093a, 0x2600), .driver_info = SENSOR_PAC7311},
+- {USB_DEVICE(0x093a, 0x2601), .driver_info = SENSOR_PAC7311},
+- {USB_DEVICE(0x093a, 0x2603), .driver_info = SENSOR_PAC7311},
+- {USB_DEVICE(0x093a, 0x2608), .driver_info = SENSOR_PAC7311},
+- {USB_DEVICE(0x093a, 0x260e), .driver_info = SENSOR_PAC7311},
+- {USB_DEVICE(0x093a, 0x260f), .driver_info = SENSOR_PAC7311},
+- {USB_DEVICE(0x093a, 0x2620), .driver_info = SENSOR_PAC7302},
+- {USB_DEVICE(0x093a, 0x2621), .driver_info = SENSOR_PAC7302},
+- {USB_DEVICE(0x093a, 0x2622), .driver_info = SENSOR_PAC7302},
+- {USB_DEVICE(0x093a, 0x2624), .driver_info = SENSOR_PAC7302},
+- {USB_DEVICE(0x093a, 0x2626), .driver_info = SENSOR_PAC7302},
+- {USB_DEVICE(0x093a, 0x2629), .driver_info = SENSOR_PAC7302},
+- {USB_DEVICE(0x093a, 0x262a), .driver_info = SENSOR_PAC7302},
+- {USB_DEVICE(0x093a, 0x262c), .driver_info = SENSOR_PAC7302},
++static const struct usb_device_id device_table[] __devinitconst = {
++ {USB_DEVICE(0x093a, 0x2600)},
++ {USB_DEVICE(0x093a, 0x2601)},
++ {USB_DEVICE(0x093a, 0x2603)},
++ {USB_DEVICE(0x093a, 0x2608)},
++ {USB_DEVICE(0x093a, 0x260e)},
++ {USB_DEVICE(0x093a, 0x260f)},
+ {}
+ };
+ MODULE_DEVICE_TABLE(usb, device_table);
+
+ /* -- device connect -- */
+-static int sd_probe(struct usb_interface *intf,
++static int __devinit sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+ {
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+diff --git a/drivers/media/video/gspca/pac_common.h b/drivers/media/video/gspca/pac_common.h
+index 34d4b14..20f67d9 100644
+--- a/drivers/media/video/gspca/pac_common.h
++++ b/drivers/media/video/gspca/pac_common.h
+@@ -33,26 +33,101 @@
+ static const unsigned char pac_sof_marker[5] =
+ { 0xff, 0xff, 0x00, 0xff, 0x96 };
+
+-static unsigned char *pac_find_sof(struct gspca_dev *gspca_dev,
++/*
++ The following state machine finds the SOF marker sequence
++ 0xff, 0xff, 0x00, 0xff, 0x96 in a byte stream.
++
++ +----------+
++ | 0: START |<---------------\
++ +----------+<-\ |
++ | \---/otherwise |
++ v 0xff |
++ +----------+ otherwise |
++ | 1 |--------------->*
++ | | ^
++ +----------+ |
++ | |
++ v 0xff |
++ +----------+<-\0xff |
++ /->| |--/ |
++ | | 2 |--------------->*
++ | | | otherwise ^
++ | +----------+ |
++ | | |
++ | v 0x00 |
++ | +----------+ |
++ | | 3 | |
++ | | |--------------->*
++ | +----------+ otherwise ^
++ | | |
++ 0xff | v 0xff |
++ | +----------+ |
++ \--| 4 | |
++ | |----------------/
++ +----------+ otherwise
++ |
++ v 0x96
++ +----------+
++ | FOUND |
++ +----------+
++*/
++
++static unsigned char *pac_find_sof(u8 *sof_read,
+ unsigned char *m, int len)
+ {
+- struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+
+ /* Search for the SOF marker (fixed part) in the header */
+ for (i = 0; i < len; i++) {
+- if (m[i] == pac_sof_marker[sd->sof_read]) {
+- sd->sof_read++;
+- if (sd->sof_read == sizeof(pac_sof_marker)) {
++ switch (*sof_read) {
++ case 0:
++ if (m[i] == 0xff)
++ *sof_read = 1;
++ break;
++ case 1:
++ if (m[i] == 0xff)
++ *sof_read = 2;
++ else
++ *sof_read = 0;
++ break;
++ case 2:
++ switch (m[i]) {
++ case 0x00:
++ *sof_read = 3;
++ break;
++ case 0xff:
++ /* stay in this state */
++ break;
++ default:
++ *sof_read = 0;
++ }
++ break;
++ case 3:
++ if (m[i] == 0xff)
++ *sof_read = 4;
++ else
++ *sof_read = 0;
++ break;
++ case 4:
++ switch (m[i]) {
++ case 0x96:
++ /* Pattern found */
+ PDEBUG(D_FRAM,
+ "SOF found, bytes to analyze: %u."
+ " Frame starts at byte #%u",
+ len, i + 1);
+- sd->sof_read = 0;
++ *sof_read = 0;
+ return m + i + 1;
++ break;
++ case 0xff:
++ *sof_read = 2;
++ break;
++ default:
++ *sof_read = 0;
+ }
+- } else {
+- sd->sof_read = 0;
++ break;
++ default:
++ *sof_read = 0;
+ }
+ }
+
+diff --git a/drivers/media/video/gspca/sn9c2028.c b/drivers/media/video/gspca/sn9c2028.c
+new file mode 100644
+index 0000000..dda5fd4
+--- /dev/null
++++ b/drivers/media/video/gspca/sn9c2028.c
+@@ -0,0 +1,757 @@
++/*
++ * SN9C2028 library
++ *
++ * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn.edu>
++ *
++ * 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
++ * 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
++ */
++
++#define MODULE_NAME "sn9c2028"
++
++#include "gspca.h"
++
++MODULE_AUTHOR("Theodore Kilgore");
++MODULE_DESCRIPTION("Sonix SN9C2028 USB Camera Driver");
++MODULE_LICENSE("GPL");
++
++/* specific webcam descriptor */
++struct sd {
++ struct gspca_dev gspca_dev; /* !! must be the first item */
++ u8 sof_read;
++ u16 model;
++};
++
++struct init_command {
++ unsigned char instruction[6];
++ unsigned char to_read; /* length to read. 0 means no reply requested */
++};
++
++/* V4L2 controls supported by the driver */
++static struct ctrl sd_ctrls[] = {
++};
++
++/* How to change the resolution of any of the VGA cams is unknown */
++static const struct v4l2_pix_format vga_mode[] = {
++ {640, 480, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE,
++ .bytesperline = 640,
++ .sizeimage = 640 * 480 * 3 / 4,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 0},
++};
++
++/* No way to change the resolution of the CIF cams is known */
++static const struct v4l2_pix_format cif_mode[] = {
++ {352, 288, V4L2_PIX_FMT_SN9C2028, V4L2_FIELD_NONE,
++ .bytesperline = 352,
++ .sizeimage = 352 * 288 * 3 / 4,
++ .colorspace = V4L2_COLORSPACE_SRGB,
++ .priv = 0},
++};
++
++/* the bytes to write are in gspca_dev->usb_buf */
++static int sn9c2028_command(struct gspca_dev *gspca_dev, u8 *command)
++{
++ int rc;
++
++ PDEBUG(D_USBO, "sending command %02x%02x%02x%02x%02x%02x", command[0],
++ command[1], command[2], command[3], command[4], command[5]);
++
++ memcpy(gspca_dev->usb_buf, command, 6);
++ rc = usb_control_msg(gspca_dev->dev,
++ usb_sndctrlpipe(gspca_dev->dev, 0),
++ USB_REQ_GET_CONFIGURATION,
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
++ 2, 0, gspca_dev->usb_buf, 6, 500);
++ if (rc < 0) {
++ PDEBUG(D_ERR, "command write [%02x] error %d",
++ gspca_dev->usb_buf[0], rc);
++ return rc;
++ }
++
++ return 0;
++}
++
++static int sn9c2028_read1(struct gspca_dev *gspca_dev)
++{
++ int rc;
++
++ rc = usb_control_msg(gspca_dev->dev,
++ usb_rcvctrlpipe(gspca_dev->dev, 0),
++ USB_REQ_GET_STATUS,
++ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
++ 1, 0, gspca_dev->usb_buf, 1, 500);
++ if (rc != 1) {
++ PDEBUG(D_ERR, "read1 error %d", rc);
++ return (rc < 0) ? rc : -EIO;
++ }
++ PDEBUG(D_USBI, "read1 response %02x", gspca_dev->usb_buf[0]);
++ return gspca_dev->usb_buf[0];
++}
++
++static int sn9c2028_read4(struct gspca_dev *gspca_dev, u8 *reading)
++{
++ int rc;
++ rc = usb_control_msg(gspca_dev->dev,
++ usb_rcvctrlpipe(gspca_dev->dev, 0),
++ USB_REQ_GET_STATUS,
++ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE,
++ 4, 0, gspca_dev->usb_buf, 4, 500);
++ if (rc != 4) {
++ PDEBUG(D_ERR, "read4 error %d", rc);
++ return (rc < 0) ? rc : -EIO;
++ }
++ memcpy(reading, gspca_dev->usb_buf, 4);
++ PDEBUG(D_USBI, "read4 response %02x%02x%02x%02x", reading[0],
++ reading[1], reading[2], reading[3]);
++ return rc;
++}
++
++static int sn9c2028_long_command(struct gspca_dev *gspca_dev, u8 *command)
++{
++ int i, status;
++ __u8 reading[4];
++
++ status = sn9c2028_command(gspca_dev, command);
++ if (status < 0)
++ return status;
++
++ status = -1;
++ for (i = 0; i < 256 && status < 2; i++)
++ status = sn9c2028_read1(gspca_dev);
++ if (status != 2) {
++ PDEBUG(D_ERR, "long command status read error %d", status);
++ return (status < 0) ? status : -EIO;
++ }
++
++ memset(reading, 0, 4);
++ status = sn9c2028_read4(gspca_dev, reading);
++ if (status < 0)
++ return status;
++
++ /* in general, the first byte of the response is the first byte of
++ * the command, or'ed with 8 */
++ status = sn9c2028_read1(gspca_dev);
++ if (status < 0)
++ return status;
++
++ return 0;
++}
++
++static int sn9c2028_short_command(struct gspca_dev *gspca_dev, u8 *command)
++{
++ int err_code;
++
++ err_code = sn9c2028_command(gspca_dev, command);
++ if (err_code < 0)
++ return err_code;
++
++ err_code = sn9c2028_read1(gspca_dev);
++ if (err_code < 0)
++ return err_code;
++
++ return 0;
++}
++
++/* this function is called at probe time */
++static int sd_config(struct gspca_dev *gspca_dev,
++ const struct usb_device_id *id)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ struct cam *cam = &gspca_dev->cam;
++
++ PDEBUG(D_PROBE, "SN9C2028 camera detected (vid/pid 0x%04X:0x%04X)",
++ id->idVendor, id->idProduct);
++
++ sd->model = id->idProduct;
++
++ switch (sd->model) {
++ case 0x7005:
++ PDEBUG(D_PROBE, "Genius Smart 300 camera");
++ break;
++ case 0x8000:
++ PDEBUG(D_PROBE, "DC31VC");
++ break;
++ case 0x8001:
++ PDEBUG(D_PROBE, "Spy camera");
++ break;
++ case 0x8003:
++ PDEBUG(D_PROBE, "CIF camera");
++ break;
++ case 0x8008:
++ PDEBUG(D_PROBE, "Mini-Shotz ms-350 camera");
++ break;
++ case 0x800a:
++ PDEBUG(D_PROBE, "Vivitar 3350b type camera");
++ cam->input_flags = V4L2_IN_ST_VFLIP | V4L2_IN_ST_HFLIP;
++ break;
++ }
++
++ switch (sd->model) {
++ case 0x8000:
++ case 0x8001:
++ case 0x8003:
++ cam->cam_mode = cif_mode;
++ cam->nmodes = ARRAY_SIZE(cif_mode);
++ break;
++ default:
++ cam->cam_mode = vga_mode;
++ cam->nmodes = ARRAY_SIZE(vga_mode);
++ }
++ return 0;
++}
++
++/* this function is called at probe and resume time */
++static int sd_init(struct gspca_dev *gspca_dev)
++{
++ int status = -1;
++
++ sn9c2028_read1(gspca_dev);
++ sn9c2028_read1(gspca_dev);
++ status = sn9c2028_read1(gspca_dev);
++
++ return (status < 0) ? status : 0;
++}
++
++static int run_start_commands(struct gspca_dev *gspca_dev,
++ struct init_command *cam_commands, int n)
++{
++ int i, err_code = -1;
++
++ for (i = 0; i < n; i++) {
++ switch (cam_commands[i].to_read) {
++ case 4:
++ err_code = sn9c2028_long_command(gspca_dev,
++ cam_commands[i].instruction);
++ break;
++ case 1:
++ err_code = sn9c2028_short_command(gspca_dev,
++ cam_commands[i].instruction);
++ break;
++ case 0:
++ err_code = sn9c2028_command(gspca_dev,
++ cam_commands[i].instruction);
++ break;
++ }
++ if (err_code < 0)
++ return err_code;
++ }
++ return 0;
++}
++
++static int start_spy_cam(struct gspca_dev *gspca_dev)
++{
++ struct init_command spy_start_commands[] = {
++ {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x22, 0x01, 0x04, 0x00, 0x00}, 4},
++ {{0x13, 0x23, 0x01, 0x03, 0x00, 0x00}, 4},
++ {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, /* width 352 */
++ {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, /* height 288 */
++ /* {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4}, */
++ {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4},
++ {{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, /* red gain ?*/
++ /* {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4}, */
++ {{0x13, 0x29, 0x01, 0x00, 0x00, 0x00}, 4},
++ /* {{0x13, 0x29, 0x01, 0x0c, 0x00, 0x00}, 4}, */
++ {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
++ /* {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4}, */
++ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
++ {{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4},
++ /* {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4}, */
++ {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4},
++ {{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4},
++ {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
++ {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x02, 0x06, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x03, 0x13, 0x00, 0x00, 0x00}, 4}, /*don't mess with*/
++ /*{{0x11, 0x04, 0x06, 0x00, 0x00, 0x00}, 4}, observed */
++ {{0x11, 0x04, 0x00, 0x00, 0x00, 0x00}, 4}, /* brighter */
++ /*{{0x11, 0x05, 0x65, 0x00, 0x00, 0x00}, 4}, observed */
++ {{0x11, 0x05, 0x00, 0x00, 0x00, 0x00}, 4}, /* brighter */
++ {{0x11, 0x06, 0xb1, 0x00, 0x00, 0x00}, 4}, /* observed */
++ {{0x11, 0x07, 0x00, 0x00, 0x00, 0x00}, 4},
++ /*{{0x11, 0x08, 0x06, 0x00, 0x00, 0x00}, 4}, observed */
++ {{0x11, 0x08, 0x0b, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x09, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x0a, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x0b, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x0c, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x0d, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x0e, 0x04, 0x00, 0x00, 0x00}, 4},
++ /* {{0x11, 0x0f, 0x00, 0x00, 0x00, 0x00}, 4}, */
++ /* brightness or gain. 0 is default. 4 is good
++ * indoors at night with incandescent lighting */
++ {{0x11, 0x0f, 0x04, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x10, 0x06, 0x00, 0x00, 0x00}, 4}, /*hstart or hoffs*/
++ {{0x11, 0x11, 0x06, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x14, 0x02, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x13, 0x01, 0x00, 0x00, 0x00}, 4},
++ /* {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1}, observed */
++ {{0x1b, 0x02, 0x11, 0x00, 0x00, 0x00}, 1}, /* brighter */
++ /* {{0x1b, 0x13, 0x01, 0x00, 0x00, 0x00}, 1}, observed */
++ {{0x1b, 0x13, 0x11, 0x00, 0x00, 0x00}, 1},
++ {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1}, /* compresses */
++ /* Camera should start to capture now. */
++ };
++
++ return run_start_commands(gspca_dev, spy_start_commands,
++ ARRAY_SIZE(spy_start_commands));
++}
++
++static int start_cif_cam(struct gspca_dev *gspca_dev)
++{
++ struct init_command cif_start_commands[] = {
++ {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
++ /* The entire sequence below seems redundant */
++ /* {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x22, 0x01, 0x06, 0x00, 0x00}, 4},
++ {{0x13, 0x23, 0x01, 0x02, 0x00, 0x00}, 4},
++ {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4}, width?
++ {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4}, height?
++ {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, subsample?
++ {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x29, 0x01, 0x20, 0x00, 0x00}, 4},
++ {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
++ {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
++ {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
++ {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
++ {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
++ {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},*/
++ {{0x1b, 0x21, 0x00, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x17, 0x00, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x19, 0x00, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x03, 0x5a, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x04, 0x27, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x05, 0x01, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x12, 0x14, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x13, 0x00, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x14, 0x00, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x15, 0x00, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x16, 0x00, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x77, 0xa2, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x06, 0x0f, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x07, 0x14, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x08, 0x0f, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x09, 0x10, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x0e, 0x00, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x0f, 0x00, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x12, 0x07, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x10, 0x1f, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1},
++ {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 1}, /* width/8 */
++ {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 1}, /* height/8 */
++ /* {{0x13, 0x27, 0x01, 0x68, 0x00, 0x00}, 4}, subsample?
++ * {{0x13, 0x28, 0x01, 0x1e, 0x00, 0x00}, 4}, does nothing
++ * {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4}, */
++ /* {{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4},
++ * causes subsampling
++ * but not a change in the resolution setting! */
++ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
++ {{0x13, 0x2d, 0x01, 0x01, 0x00, 0x00}, 4},
++ {{0x13, 0x2e, 0x01, 0x08, 0x00, 0x00}, 4},
++ {{0x13, 0x2f, 0x01, 0x06, 0x00, 0x00}, 4},
++ {{0x13, 0x28, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x1b, 0x04, 0x6d, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x05, 0x03, 0x00, 0x00, 0x00}, 1},
++ {{0x20, 0x36, 0x06, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x0e, 0x01, 0x00, 0x00, 0x00}, 1},
++ {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x1b, 0x0f, 0x00, 0x00, 0x00, 0x00}, 1},
++ {{0x20, 0x36, 0x05, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x10, 0x0f, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x02, 0x06, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1},
++ {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1},/* use compression */
++ /* Camera should start to capture now. */
++ };
++
++ return run_start_commands(gspca_dev, cif_start_commands,
++ ARRAY_SIZE(cif_start_commands));
++}
++
++static int start_ms350_cam(struct gspca_dev *gspca_dev)
++{
++ struct init_command ms350_start_commands[] = {
++ {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x22, 0x01, 0x04, 0x00, 0x00}, 4},
++ {{0x13, 0x23, 0x01, 0x03, 0x00, 0x00}, 4},
++ {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4},
++ {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4},
++ {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4},
++ {{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4},
++ {{0x13, 0x29, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
++ {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
++ {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
++ {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
++ {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
++ {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x00, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x01, 0x70, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x02, 0x05, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x03, 0x5d, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x04, 0x07, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x05, 0x25, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x06, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x07, 0x09, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x08, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x09, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x0a, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x0b, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x0c, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x0d, 0x0c, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x0e, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x0f, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x11, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x13, 0x63, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x15, 0x70, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x18, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x11, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, /* width */
++ {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, /* height */
++ {{0x13, 0x28, 0x01, 0x09, 0x00, 0x00}, 4}, /* vstart? */
++ {{0x13, 0x27, 0x01, 0x28, 0x00, 0x00}, 4},
++ {{0x13, 0x29, 0x01, 0x40, 0x00, 0x00}, 4}, /* hstart? */
++ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
++ {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
++ {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
++ {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
++ {{0x1b, 0x02, 0x05, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 1},
++ {{0x20, 0x18, 0x00, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x02, 0x0a, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x11, 0x01, 0x00, 0x00, 0x00}, 0},
++ /* Camera should start to capture now. */
++ };
++
++ return run_start_commands(gspca_dev, ms350_start_commands,
++ ARRAY_SIZE(ms350_start_commands));
++}
++
++static int start_genius_cam(struct gspca_dev *gspca_dev)
++{
++ struct init_command genius_start_commands[] = {
++ {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x16, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x10, 0x00, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x25, 0x01, 0x16, 0x00, 0x00}, 4},
++ {{0x13, 0x26, 0x01, 0x12, 0x00, 0x00}, 4},
++ /* "preliminary" width and height settings */
++ {{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4},
++ {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
++ {{0x13, 0x29, 0x01, 0x22, 0x00, 0x00}, 4},
++ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
++ {{0x13, 0x2d, 0x01, 0x02, 0x00, 0x00}, 4},
++ {{0x13, 0x2e, 0x01, 0x09, 0x00, 0x00}, 4},
++ {{0x13, 0x2f, 0x01, 0x07, 0x00, 0x00}, 4},
++ {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x11, 0x64, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x13, 0x91, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x15, 0x20, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x16, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x17, 0x60, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x21, 0x2d, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x23, 0x03, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x25, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x26, 0x02, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x27, 0x88, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x30, 0x38, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x31, 0x2a, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x32, 0x2a, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x33, 0x2a, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x34, 0x02, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x5b, 0x0a, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4}, /* real width */
++ {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4}, /* real height */
++ {{0x13, 0x28, 0x01, 0x0e, 0x00, 0x00}, 4},
++ {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
++ {{0x13, 0x29, 0x01, 0x62, 0x00, 0x00}, 4},
++ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
++ {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
++ {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
++ {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
++ {{0x11, 0x20, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x21, 0x2a, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x22, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x23, 0x28, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x10, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x11, 0x04, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x12, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x13, 0x03, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x14, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x15, 0xe0, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x16, 0x02, 0x00, 0x00, 0x00}, 4},
++ {{0x11, 0x17, 0x80, 0x00, 0x00, 0x00}, 4},
++ {{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1},
++ {{0x1c, 0x20, 0x00, 0x2a, 0x00, 0x00}, 1},
++ {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 0}
++ /* Camera should start to capture now. */
++ };
++
++ return run_start_commands(gspca_dev, genius_start_commands,
++ ARRAY_SIZE(genius_start_commands));
++}
++
++static int start_vivitar_cam(struct gspca_dev *gspca_dev)
++{
++ struct init_command vivitar_start_commands[] = {
++ {{0x0c, 0x01, 0x00, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x20, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x21, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x22, 0x01, 0x01, 0x00, 0x00}, 4},
++ {{0x13, 0x23, 0x01, 0x01, 0x00, 0x00}, 4},
++ {{0x13, 0x24, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4},
++ {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4},
++ {{0x13, 0x27, 0x01, 0x20, 0x00, 0x00}, 4},
++ {{0x13, 0x28, 0x01, 0x0a, 0x00, 0x00}, 4},
++ /*
++ * Above is changed from OEM 0x0b. Fixes Bayer tiling.
++ * Presumably gives a vertical shift of one row.
++ */
++ {{0x13, 0x29, 0x01, 0x20, 0x00, 0x00}, 4},
++ /* Above seems to do horizontal shift. */
++ {{0x13, 0x2a, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x2b, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x2c, 0x01, 0x02, 0x00, 0x00}, 4},
++ {{0x13, 0x2d, 0x01, 0x03, 0x00, 0x00}, 4},
++ {{0x13, 0x2e, 0x01, 0x0f, 0x00, 0x00}, 4},
++ {{0x13, 0x2f, 0x01, 0x0c, 0x00, 0x00}, 4},
++ /* Above three commands seem to relate to brightness. */
++ {{0x12, 0x34, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x13, 0x34, 0x01, 0xa1, 0x00, 0x00}, 4},
++ {{0x13, 0x35, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x1b, 0x12, 0x80, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x01, 0x77, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x02, 0x3a, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x12, 0x78, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x13, 0x00, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x14, 0x80, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x15, 0x34, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x1b, 0x04, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x20, 0x44, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x23, 0xee, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x26, 0xa0, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x27, 0x9a, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x28, 0xa0, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x29, 0x30, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x2a, 0x80, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x2b, 0x00, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x2f, 0x3d, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x30, 0x24, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x32, 0x86, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x60, 0xa9, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x61, 0x42, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x65, 0x00, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x69, 0x38, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x6f, 0x88, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x70, 0x0b, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x71, 0x00, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x74, 0x21, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x75, 0x86, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x76, 0x00, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x7d, 0xf3, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x17, 0x1c, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x18, 0xc0, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x19, 0x05, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x1a, 0xf6, 0x00, 0x00, 0x00}, 1},
++ /* {{0x13, 0x25, 0x01, 0x28, 0x00, 0x00}, 4},
++ {{0x13, 0x26, 0x01, 0x1e, 0x00, 0x00}, 4},
++ {{0x13, 0x28, 0x01, 0x0b, 0x00, 0x00}, 4}, */
++ {{0x20, 0x36, 0x06, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x10, 0x26, 0x00, 0x00, 0x00}, 1},
++ {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x1b, 0x76, 0x03, 0x00, 0x00, 0x00}, 1},
++ {{0x20, 0x36, 0x05, 0x00, 0x00, 0x00}, 1},
++ {{0x1b, 0x00, 0x3f, 0x00, 0x00, 0x00}, 1},
++ /* Above is brightness; OEM driver setting is 0x10 */
++ {{0x12, 0x27, 0x01, 0x00, 0x00, 0x00}, 4},
++ {{0x20, 0x29, 0x30, 0x00, 0x00, 0x00}, 1},
++ {{0x20, 0x34, 0xa1, 0x00, 0x00, 0x00}, 1}
++ };
++
++ return run_start_commands(gspca_dev, vivitar_start_commands,
++ ARRAY_SIZE(vivitar_start_commands));
++}
++
++static int sd_start(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int err_code;
++
++ sd->sof_read = 0;
++
++ switch (sd->model) {
++ case 0x7005:
++ err_code = start_genius_cam(gspca_dev);
++ break;
++ case 0x8001:
++ err_code = start_spy_cam(gspca_dev);
++ break;
++ case 0x8003:
++ err_code = start_cif_cam(gspca_dev);
++ break;
++ case 0x8008:
++ err_code = start_ms350_cam(gspca_dev);
++ break;
++ case 0x800a:
++ err_code = start_vivitar_cam(gspca_dev);
++ break;
++ default:
++ PDEBUG(D_ERR, "Starting unknown camera, please report this");
++ return -ENXIO;
++ }
++
++ return err_code;
++}
++
++static void sd_stopN(struct gspca_dev *gspca_dev)
++{
++ int result;
++ __u8 data[6];
++
++ result = sn9c2028_read1(gspca_dev);
++ if (result < 0)
++ PDEBUG(D_ERR, "Camera Stop read failed");
++
++ memset(data, 0, 6);
++ data[0] = 0x14;
++ result = sn9c2028_command(gspca_dev, data);
++ if (result < 0)
++ PDEBUG(D_ERR, "Camera Stop command failed");
++}
++
++/* Include sn9c2028 sof detection functions */
++#include "sn9c2028.h"
++
++static void sd_pkt_scan(struct gspca_dev *gspca_dev,
++ __u8 *data, /* isoc packet */
++ int len) /* iso packet length */
++{
++ unsigned char *sof;
++
++ sof = sn9c2028_find_sof(gspca_dev, data, len);
++ if (sof) {
++ int n;
++
++ /* finish decoding current frame */
++ n = sof - data;
++ if (n > sizeof sn9c2028_sof_marker)
++ n -= sizeof sn9c2028_sof_marker;
++ else
++ n = 0;
++ gspca_frame_add(gspca_dev, LAST_PACKET, data, n);
++ /* Start next frame. */
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
++ sn9c2028_sof_marker, sizeof sn9c2028_sof_marker);
++ len -= sof - data;
++ data = sof;
++ }
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
++}
++
++/* sub-driver description */
++static const struct sd_desc sd_desc = {
++ .name = MODULE_NAME,
++ .ctrls = sd_ctrls,
++ .nctrls = ARRAY_SIZE(sd_ctrls),
++ .config = sd_config,
++ .init = sd_init,
++ .start = sd_start,
++ .stopN = sd_stopN,
++ .pkt_scan = sd_pkt_scan,
++};
++
++/* -- module initialisation -- */
++static const __devinitdata struct usb_device_id device_table[] = {
++ {USB_DEVICE(0x0458, 0x7005)}, /* Genius Smart 300, version 2 */
++ /* The Genius Smart is untested. I can't find an owner ! */
++ /* {USB_DEVICE(0x0c45, 0x8000)}, DC31VC, Don't know this camera */
++ {USB_DEVICE(0x0c45, 0x8001)}, /* Wild Planet digital spy cam */
++ {USB_DEVICE(0x0c45, 0x8003)}, /* Several small CIF cameras */
++ /* {USB_DEVICE(0x0c45, 0x8006)}, Unknown VGA camera */
++ {USB_DEVICE(0x0c45, 0x8008)}, /* Mini-Shotz ms-350 */
++ {USB_DEVICE(0x0c45, 0x800a)}, /* Vivicam 3350B */
++ {}
++};
++MODULE_DEVICE_TABLE(usb, device_table);
++
++/* -- device connect -- */
++static int sd_probe(struct usb_interface *intf,
++ const struct usb_device_id *id)
++{
++ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
++ THIS_MODULE);
++}
++
++static struct usb_driver sd_driver = {
++ .name = MODULE_NAME,
++ .id_table = device_table,
++ .probe = sd_probe,
++ .disconnect = gspca_disconnect,
++#ifdef CONFIG_PM
++ .suspend = gspca_suspend,
++ .resume = gspca_resume,
++#endif
++};
++
++/* -- module insert / remove -- */
++static int __init sd_mod_init(void)
++{
++ int ret;
++
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
++ PDEBUG(D_PROBE, "registered");
++ return 0;
++}
++
++static void __exit sd_mod_exit(void)
++{
++ usb_deregister(&sd_driver);
++ PDEBUG(D_PROBE, "deregistered");
++}
++
++module_init(sd_mod_init);
++module_exit(sd_mod_exit);
+diff --git a/drivers/media/video/gspca/sn9c2028.h b/drivers/media/video/gspca/sn9c2028.h
+new file mode 100644
+index 0000000..8fd1d3e
+--- /dev/null
++++ b/drivers/media/video/gspca/sn9c2028.h
+@@ -0,0 +1,51 @@
++/*
++ * SN9C2028 common functions
++ *
++ * Copyright (C) 2009 Theodore Kilgore <kilgota@auburn,edu>
++ *
++ * Based closely upon the file gspca/pac_common.h
++ *
++ * 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
++ *
++ */
++
++static const unsigned char sn9c2028_sof_marker[5] =
++ { 0xff, 0xff, 0x00, 0xc4, 0xc4 };
++
++static unsigned char *sn9c2028_find_sof(struct gspca_dev *gspca_dev,
++ unsigned char *m, int len)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ int i;
++
++ /* Search for the SOF marker (fixed part) in the header */
++ for (i = 0; i < len; i++) {
++ if (m[i] == sn9c2028_sof_marker[sd->sof_read]) {
++ sd->sof_read++;
++ if (sd->sof_read == sizeof(sn9c2028_sof_marker)) {
++ PDEBUG(D_FRAM,
++ "SOF found, bytes to analyze: %u."
++ " Frame starts at byte #%u",
++ len, i + 1);
++ sd->sof_read = 0;
++ return m + i + 1;
++ }
++ } else {
++ sd->sof_read = 0;
++ }
++ }
++
++ return NULL;
++}
+diff --git a/drivers/media/video/gspca/sn9c20x.c b/drivers/media/video/gspca/sn9c20x.c
+index e0a3b75..4a1bc08 100644
+--- a/drivers/media/video/gspca/sn9c20x.c
++++ b/drivers/media/video/gspca/sn9c20x.c
+@@ -129,7 +129,7 @@ static int sd_getexposure(struct gspca_dev *gspca_dev, s32 *val);
+ static int sd_setautoexposure(struct gspca_dev *gspca_dev, s32 val);
+ static int sd_getautoexposure(struct gspca_dev *gspca_dev, s32 *val);
+
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
+ {
+ #define BRIGHTNESS_IDX 0
+ {
+@@ -1158,7 +1158,7 @@ static int i2c_w2(struct gspca_dev *gspca_dev, u8 reg, u16 val)
+ return i2c_w(gspca_dev, row);
+ }
+
+-int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val)
++static int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 row[8];
+@@ -1183,7 +1183,7 @@ int i2c_r1(struct gspca_dev *gspca_dev, u8 reg, u8 *val)
+ return 0;
+ }
+
+-int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val)
++static int i2c_r2(struct gspca_dev *gspca_dev, u8 reg, u16 *val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 row[8];
+@@ -1476,8 +1476,9 @@ static int sn9c20x_input_init(struct gspca_dev *gspca_dev)
+ if (input_register_device(sd->input_dev))
+ return -EINVAL;
+
+- sd->input_task = kthread_run(input_kthread, gspca_dev, "sn9c20x/%d",
+- gspca_dev->vdev.minor);
++ sd->input_task = kthread_run(input_kthread, gspca_dev, "sn9c20x/%s-%s",
++ gspca_dev->dev->bus->bus_name,
++ gspca_dev->dev->devpath);
+
+ if (IS_ERR(sd->input_task))
+ return -EINVAL;
+@@ -1505,36 +1506,36 @@ static int set_cmatrix(struct gspca_dev *gspca_dev)
+ struct sd *sd = (struct sd *) gspca_dev;
+ s32 hue_coord, hue_index = 180 + sd->hue;
+ u8 cmatrix[21];
+- memset(cmatrix, 0, 21);
+
++ memset(cmatrix, 0, sizeof cmatrix);
+ cmatrix[2] = (sd->contrast * 0x25 / 0x100) + 0x26;
+ cmatrix[0] = 0x13 + (cmatrix[2] - 0x26) * 0x13 / 0x25;
+ cmatrix[4] = 0x07 + (cmatrix[2] - 0x26) * 0x07 / 0x25;
+ cmatrix[18] = sd->brightness - 0x80;
+
+ hue_coord = (hsv_red_x[hue_index] * sd->saturation) >> 8;
+- cmatrix[6] = (unsigned char)(hue_coord & 0xff);
+- cmatrix[7] = (unsigned char)((hue_coord >> 8) & 0x0f);
++ cmatrix[6] = hue_coord;
++ cmatrix[7] = (hue_coord >> 8) & 0x0f;
+
+ hue_coord = (hsv_red_y[hue_index] * sd->saturation) >> 8;
+- cmatrix[8] = (unsigned char)(hue_coord & 0xff);
+- cmatrix[9] = (unsigned char)((hue_coord >> 8) & 0x0f);
++ cmatrix[8] = hue_coord;
++ cmatrix[9] = (hue_coord >> 8) & 0x0f;
+
+ hue_coord = (hsv_green_x[hue_index] * sd->saturation) >> 8;
+- cmatrix[10] = (unsigned char)(hue_coord & 0xff);
+- cmatrix[11] = (unsigned char)((hue_coord >> 8) & 0x0f);
++ cmatrix[10] = hue_coord;
++ cmatrix[11] = (hue_coord >> 8) & 0x0f;
+
+ hue_coord = (hsv_green_y[hue_index] * sd->saturation) >> 8;
+- cmatrix[12] = (unsigned char)(hue_coord & 0xff);
+- cmatrix[13] = (unsigned char)((hue_coord >> 8) & 0x0f);
++ cmatrix[12] = hue_coord;
++ cmatrix[13] = (hue_coord >> 8) & 0x0f;
+
+ hue_coord = (hsv_blue_x[hue_index] * sd->saturation) >> 8;
+- cmatrix[14] = (unsigned char)(hue_coord & 0xff);
+- cmatrix[15] = (unsigned char)((hue_coord >> 8) & 0x0f);
++ cmatrix[14] = hue_coord;
++ cmatrix[15] = (hue_coord >> 8) & 0x0f;
+
+ hue_coord = (hsv_blue_y[hue_index] * sd->saturation) >> 8;
+- cmatrix[16] = (unsigned char)(hue_coord & 0xff);
+- cmatrix[17] = (unsigned char)((hue_coord >> 8) & 0x0f);
++ cmatrix[16] = hue_coord;
++ cmatrix[17] = (hue_coord >> 8) & 0x0f;
+
+ return reg_w(gspca_dev, 0x10e1, cmatrix, 21);
+ }
+@@ -2014,6 +2015,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ default:
+ cam->cam_mode = vga_mode;
+ cam->nmodes = ARRAY_SIZE(vga_mode);
++ break;
+ }
+
+ sd->old_step = 0;
+@@ -2174,8 +2176,7 @@ static void configure_sensor_output(struct gspca_dev *gspca_dev, int mode)
+ }
+
+ #define HW_WIN(mode, hstart, vstart) \
+-((const u8 []){hstart & 0xff, hstart >> 8, \
+-vstart & 0xff, vstart >> 8, \
++((const u8 []){hstart, 0, vstart, 0, \
+ (mode & MODE_SXGA ? 1280 >> 4 : 640 >> 4), \
+ (mode & MODE_SXGA ? 1024 >> 3 : 480 >> 3)})
+
+@@ -2319,7 +2320,7 @@ static void do_autogain(struct gspca_dev *gspca_dev, u16 avg_lum)
+ }
+ }
+ if (avg_lum > MAX_AVG_LUM) {
+- if (sd->gain >= 1) {
++ if (sd->gain > 0) {
+ sd->gain--;
+ set_gain(gspca_dev);
+ }
+@@ -2342,13 +2343,12 @@ static void sd_dqcallback(struct gspca_dev *gspca_dev)
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ int avg_lum;
+- static unsigned char frame_header[] =
++ static u8 frame_header[] =
+ {0xff, 0xff, 0x00, 0xc4, 0xc4, 0x96};
+ if (len == 64 && memcmp(data, frame_header, 6) == 0) {
+ avg_lum = ((data[35] >> 2) & 3) |
+@@ -2378,22 +2378,22 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ avg_lum >>= 9;
+ atomic_set(&sd->avg_lum, avg_lum);
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+- frame, data, len);
++ data, len);
+ return;
+ }
+ if (gspca_dev->last_packet_type == LAST_PACKET) {
+ if (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv
+ & MODE_JPEG) {
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
++ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, len);
+ } else {
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ data, len);
+ }
+ } else {
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+ }
+
+diff --git a/drivers/media/video/gspca/sonixb.c b/drivers/media/video/gspca/sonixb.c
+index cf3af8d..01cbcb3 100644
+--- a/drivers/media/video/gspca/sonixb.c
++++ b/drivers/media/video/gspca/sonixb.c
+@@ -73,8 +73,9 @@ struct sd {
+ #define SENSOR_OV7630 2
+ #define SENSOR_PAS106 3
+ #define SENSOR_PAS202 4
+-#define SENSOR_TAS5110 5
+-#define SENSOR_TAS5130CXX 6
++#define SENSOR_TAS5110C 5
++#define SENSOR_TAS5110D 6
++#define SENSOR_TAS5130CXX 7
+ __u8 reg11;
+ };
+
+@@ -145,7 +146,7 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
+ #define BRIGHTNESS_IDX 0
+ {
+ {
+@@ -460,13 +461,21 @@ static const __u8 pas202_sensor_init[][8] = {
+ {0xa0, 0x40, 0x11, 0x01, 0x31, 0x00, 0x63, 0x16},
+ };
+
+-static const __u8 initTas5110[] = {
++static const __u8 initTas5110c[] = {
+ 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+ 0x00, 0x01, 0x00, 0x45, 0x09, 0x0a,
+ 0x16, 0x12, 0x60, 0x86, 0x2b,
+ 0x14, 0x0a, 0x02, 0x02, 0x09, 0x07
+ };
++/* Same as above, except a different hstart */
++static const __u8 initTas5110d[] = {
++ 0x44, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x11, 0x00, 0x00, 0x00,
++ 0x00, 0x00,
++ 0x00, 0x01, 0x00, 0x41, 0x09, 0x0a,
++ 0x16, 0x12, 0x60, 0x86, 0x2b,
++ 0x14, 0x0a, 0x02, 0x02, 0x09, 0x07
++};
+ static const __u8 tas5110_sensor_init[][8] = {
+ {0x30, 0x11, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x10},
+ {0x30, 0x11, 0x02, 0x20, 0xa9, 0x00, 0x00, 0x10},
+@@ -497,7 +506,9 @@ SENS(initPas106, NULL, pas106_sensor_init, NULL, NULL, F_SIF, NO_EXPO|NO_FREQ,
+ 0),
+ SENS(initPas202, initPas202, pas202_sensor_init, NULL, NULL, 0,
+ NO_EXPO|NO_FREQ, 0),
+-SENS(initTas5110, NULL, tas5110_sensor_init, NULL, NULL, F_GAIN|F_SIF,
++SENS(initTas5110c, NULL, tas5110_sensor_init, NULL, NULL, F_GAIN|F_SIF,
++ NO_BRIGHTNESS|NO_FREQ, 0),
++SENS(initTas5110d, NULL, tas5110_sensor_init, NULL, NULL, F_GAIN|F_SIF,
+ NO_BRIGHTNESS|NO_FREQ, 0),
+ SENS(initTas5130, NULL, tas5130_sensor_init, NULL, NULL, 0, NO_EXPO|NO_FREQ,
+ 0),
+@@ -652,7 +663,8 @@ static void setsensorgain(struct gspca_dev *gspca_dev)
+
+ switch (sd->sensor) {
+
+- case SENSOR_TAS5110: {
++ case SENSOR_TAS5110C:
++ case SENSOR_TAS5110D: {
+ __u8 i2c[] =
+ {0x30, 0x11, 0x02, 0x20, 0x70, 0x00, 0x00, 0x10};
+
+@@ -704,7 +716,8 @@ static void setexposure(struct gspca_dev *gspca_dev)
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ switch (sd->sensor) {
+- case SENSOR_TAS5110: {
++ case SENSOR_TAS5110C:
++ case SENSOR_TAS5110D: {
+ __u8 reg;
+
+ /* register 19's high nibble contains the sn9c10x clock divider
+@@ -995,8 +1008,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+- unsigned char *data, /* isoc packet */
++ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ int i;
+@@ -1054,12 +1066,12 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ pkt_type = DISCARD_PACKET;
+ }
+
+- frame = gspca_frame_add(gspca_dev, pkt_type,
+- frame, data, 0);
++ gspca_frame_add(gspca_dev, pkt_type,
++ NULL, 0);
+ data += i + fr_h_sz;
+ len -= i + fr_h_sz;
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+- frame, data, len);
++ data, len);
+ return;
+ }
+ }
+@@ -1068,15 +1080,21 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ if (cam->cam_mode[gspca_dev->curr_mode].priv & MODE_RAW) {
+ /* In raw mode we sometimes get some garbage after the frame
+ ignore this */
+- int used = frame->data_end - frame->data;
++ struct gspca_frame *frame;
++ int used;
+ int size = cam->cam_mode[gspca_dev->curr_mode].sizeimage;
+
++ frame = gspca_get_i_frame(gspca_dev);
++ if (frame == NULL) {
++ gspca_dev->last_packet_type = DISCARD_PACKET;
++ return;
++ }
++ used = frame->data_end - frame->data;
+ if (used + len > size)
+ len = size - used;
+ }
+
+- gspca_frame_add(gspca_dev, INTER_PACKET,
+- frame, data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+@@ -1221,11 +1239,11 @@ static const struct sd_desc sd_desc = {
+ .driver_info = (SENSOR_ ## sensor << 8) | BRIDGE_ ## bridge
+
+
+-static __devinitdata struct usb_device_id device_table[] = {
+- {USB_DEVICE(0x0c45, 0x6001), SB(TAS5110, 102)}, /* TAS5110C1B */
+- {USB_DEVICE(0x0c45, 0x6005), SB(TAS5110, 101)}, /* TAS5110C1B */
++static const struct usb_device_id device_table[] __devinitconst = {
++ {USB_DEVICE(0x0c45, 0x6001), SB(TAS5110C, 102)}, /* TAS5110C1B */
++ {USB_DEVICE(0x0c45, 0x6005), SB(TAS5110C, 101)}, /* TAS5110C1B */
+ #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
+- {USB_DEVICE(0x0c45, 0x6007), SB(TAS5110, 101)}, /* TAS5110D */
++ {USB_DEVICE(0x0c45, 0x6007), SB(TAS5110D, 101)}, /* TAS5110D */
+ {USB_DEVICE(0x0c45, 0x6009), SB(PAS106, 101)},
+ {USB_DEVICE(0x0c45, 0x600d), SB(PAS106, 101)},
+ #endif
+@@ -1252,7 +1270,7 @@ static __devinitdata struct usb_device_id device_table[] = {
+ MODULE_DEVICE_TABLE(usb, device_table);
+
+ /* -- device connect -- */
+-static int sd_probe(struct usb_interface *intf,
++static int __devinit sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+ {
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+diff --git a/drivers/media/video/gspca/sonixj.c b/drivers/media/video/gspca/sonixj.c
+index 33f4d0a..b2b390e 100644
+--- a/drivers/media/video/gspca/sonixj.c
++++ b/drivers/media/video/gspca/sonixj.c
+@@ -1,8 +1,8 @@
+ /*
+- * Sonix sn9c102p sn9c105 sn9c120 (jpeg) library
+- * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr
++ * Sonix sn9c102p sn9c105 sn9c120 (jpeg) subdriver
+ *
+- * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
++ * Copyright (C) 2009 Jean-Francois Moine <http://moinejf.free.fr>
++ * Copyright (C) 2005 Michel Xhaard mxhaard@magic.fr
+ *
+ * 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
+@@ -45,6 +45,7 @@ struct sd {
+ u8 red;
+ u8 gamma;
+ u8 vflip; /* ov7630/ov7648 only */
++ u8 sharpness;
+ u8 infrared; /* mt9v111 only */
+ u8 freq; /* ov76xx only */
+ u8 quality; /* image quality */
+@@ -64,16 +65,18 @@ struct sd {
+ #define BRIDGE_SN9C110 2
+ #define BRIDGE_SN9C120 3
+ u8 sensor; /* Type of image sensor chip */
+-#define SENSOR_HV7131R 0
+-#define SENSOR_MI0360 1
+-#define SENSOR_MO4000 2
+-#define SENSOR_MT9V111 3
+-#define SENSOR_OM6802 4
+-#define SENSOR_OV7630 5
+-#define SENSOR_OV7648 6
+-#define SENSOR_OV7660 7
+-#define SENSOR_SP80708 8
+- u8 i2c_base;
++#define SENSOR_ADCM1700 0
++#define SENSOR_HV7131R 1
++#define SENSOR_MI0360 2
++#define SENSOR_MO4000 3
++#define SENSOR_MT9V111 4
++#define SENSOR_OM6802 5
++#define SENSOR_OV7630 6
++#define SENSOR_OV7648 7
++#define SENSOR_OV7660 8
++#define SENSOR_PO1030 9
++#define SENSOR_SP80708 10
++ u8 i2c_addr;
+
+ u8 *jpeg_hdr;
+ };
+@@ -95,12 +98,14 @@ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setinfrared(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getinfrared(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
+ #define BRIGHTNESS_IDX 0
+ {
+ {
+@@ -224,8 +229,23 @@ static struct ctrl sd_ctrls[] = {
+ .set = sd_setvflip,
+ .get = sd_getvflip,
+ },
++#define SHARPNESS_IDX 8
++ {
++ {
++ .id = V4L2_CID_SHARPNESS,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Sharpness",
++ .minimum = 0,
++ .maximum = 255,
++ .step = 1,
++#define SHARPNESS_DEF 90
++ .default_value = SHARPNESS_DEF,
++ },
++ .set = sd_setsharpness,
++ .get = sd_getsharpness,
++ },
+ /* mt9v111 only */
+-#define INFRARED_IDX 8
++#define INFRARED_IDX 9
+ {
+ {
+ .id = V4L2_CID_INFRARED,
+@@ -241,7 +261,7 @@ static struct ctrl sd_ctrls[] = {
+ .get = sd_getinfrared,
+ },
+ /* ov7630/ov7648/ov7660 only */
+-#define FREQ_IDX 9
++#define FREQ_IDX 10
+ {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+@@ -250,7 +270,7 @@ static struct ctrl sd_ctrls[] = {
+ .minimum = 0,
+ .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
+ .step = 1,
+-#define FREQ_DEF 2
++#define FREQ_DEF 1
+ .default_value = FREQ_DEF,
+ },
+ .set = sd_setfreq,
+@@ -260,26 +280,41 @@ static struct ctrl sd_ctrls[] = {
+
+ /* table of the disabled controls */
+ static __u32 ctrl_dis[] = {
++ (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX) |
++ (1 << AUTOGAIN_IDX), /* SENSOR_ADCM1700 0 */
+ (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX),
+- /* SENSOR_HV7131R 0 */
++ /* SENSOR_HV7131R 1 */
+ (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX),
+- /* SENSOR_MI0360 1 */
++ /* SENSOR_MI0360 2 */
+ (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX),
+- /* SENSOR_MO4000 2 */
++ /* SENSOR_MO4000 3 */
++#if 1
+ (1 << VFLIP_IDX) | (1 << FREQ_IDX),
+- /* SENSOR_MT9V111 3 */
++#else
++ (1 << AUTOGAIN_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX),
++#endif
++ /* SENSOR_MT9V111 4 */
+ (1 << INFRARED_IDX) | (1 << VFLIP_IDX) | (1 << FREQ_IDX),
+- /* SENSOR_OM6802 4 */
++ /* SENSOR_OM6802 5 */
+ (1 << INFRARED_IDX),
+- /* SENSOR_OV7630 5 */
++ /* SENSOR_OV7630 6 */
+ (1 << INFRARED_IDX),
+- /* SENSOR_OV7648 6 */
++ /* SENSOR_OV7648 7 */
+ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX),
+- /* SENSOR_OV7660 7 */
++ /* SENSOR_OV7660 8 */
+ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) |
+- (1 << FREQ_IDX), /* SENSOR_SP80708 8 */
++ (1 << FREQ_IDX), /* SENSOR_PO1030 9 */
++ (1 << AUTOGAIN_IDX) | (1 << INFRARED_IDX) | (1 << VFLIP_IDX) |
++ (1 << FREQ_IDX), /* SENSOR_SP80708 10 */
+ };
+
++static const struct v4l2_pix_format cif_mode[] = {
++ {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
++ .bytesperline = 352,
++ .sizeimage = 352 * 288 * 4 / 8 + 590,
++ .colorspace = V4L2_COLORSPACE_JPEG,
++ .priv = 0},
++};
+ static const struct v4l2_pix_format vga_mode[] = {
+ {160, 120, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 160,
+@@ -299,12 +334,23 @@ static const struct v4l2_pix_format vga_mode[] = {
+ .priv = 0},
+ };
+
++static const u8 sn_adcm1700[0x1c] = {
++/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
++ 0x00, 0x43, 0x60, 0x00, 0x1a, 0x00, 0x00, 0x00,
++/* reg8 reg9 rega regb regc regd rege regf */
++ 0x80, 0x51, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
++ 0x03, 0x00, 0x05, 0x01, 0x05, 0x16, 0x12, 0x42,
++/* reg18 reg19 reg1a reg1b */
++ 0x06, 0x00, 0x00, 0x00
++};
++
+ /*Data from sn9c102p+hv7131r */
+ static const u8 sn_hv7131[0x1c] = {
+ /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x03, 0x64, 0x00, 0x1a, 0x20, 0x20, 0x20,
+ /* reg8 reg9 rega regb regc regd rege regf */
+- 0xa1, 0x11, 0x02, 0x09, 0x00, 0x00, 0x00, 0x10,
++ 0x81, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x01, 0x03, 0x28, 0x1e, 0x41,
+ /* reg18 reg19 reg1a reg1b */
+@@ -315,7 +361,7 @@ static const u8 sn_mi0360[0x1c] = {
+ /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x61, 0x44, 0x00, 0x1a, 0x20, 0x20, 0x20,
+ /* reg8 reg9 rega regb regc regd rege regf */
+- 0xb1, 0x5d, 0x07, 0x00, 0x00, 0x00, 0x00, 0x10,
++ 0x81, 0x5d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x02, 0x0a, 0x28, 0x1e, 0x61,
+ /* reg18 reg19 reg1a reg1b */
+@@ -337,7 +383,7 @@ static const u8 sn_mt9v111[0x1c] = {
+ /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x61, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
+ /* reg8 reg9 rega regb regc regd rege regf */
+- 0x81, 0x5c, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x81, 0x5c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x02, 0x1c, 0x28, 0x1e, 0x40,
+ /* reg18 reg19 reg1a reg1b */
+@@ -346,7 +392,7 @@ static const u8 sn_mt9v111[0x1c] = {
+
+ static const u8 sn_om6802[0x1c] = {
+ /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+- 0x00, 0x23, 0x72, 0x00, 0x1a, 0x34, 0x27, 0x20,
++ 0x00, 0x23, 0x72, 0x00, 0x1a, 0x20, 0x20, 0x19,
+ /* reg8 reg9 rega regb regc regd rege regf */
+ 0x80, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+@@ -359,7 +405,7 @@ static const u8 sn_ov7630[0x1c] = {
+ /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x21, 0x40, 0x00, 0x1a, 0x20, 0x1f, 0x20,
+ /* reg8 reg9 rega regb regc regd rege regf */
+- 0xa1, 0x21, 0x76, 0x21, 0x00, 0x00, 0x00, 0x10,
++ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x04, 0x01, 0x0a, 0x28, 0x1e, 0xc2,
+ /* reg18 reg19 reg1a reg1b */
+@@ -370,7 +416,7 @@ static const u8 sn_ov7648[0x1c] = {
+ /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x63, 0x40, 0x00, 0x1a, 0x20, 0x20, 0x20,
+ /* reg8 reg9 rega regb regc regd rege regf */
+- 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10,
++ 0x81, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x01, 0x00, 0x28, 0x1e, 0x00,
+ /* reg18 reg19 reg1a reg1b */
+@@ -388,11 +434,22 @@ static const u8 sn_ov7660[0x1c] = {
+ 0x07, 0x00, 0x00, 0x00
+ };
+
++static const u8 sn_po1030[0x1c] = {
++/* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
++ 0x00, 0x21, 0x62, 0x00, 0x1a, 0x20, 0x20, 0x20,
++/* reg8 reg9 rega regb regc regd rege regf */
++ 0x81, 0x6e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++/* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
++ 0x03, 0x00, 0x00, 0x06, 0x06, 0x28, 0x1e, 0x00,
++/* reg18 reg19 reg1a reg1b */
++ 0x07, 0x00, 0x00, 0x00
++};
++
+ static const u8 sn_sp80708[0x1c] = {
+ /* reg0 reg1 reg2 reg3 reg4 reg5 reg6 reg7 */
+ 0x00, 0x63, 0x60, 0x00, 0x1a, 0x20, 0x20, 0x20,
+ /* reg8 reg9 rega regb regc regd rege regf */
+- 0x81, 0x18, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x81, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* reg10 reg11 reg12 reg13 reg14 reg15 reg16 reg17 */
+ 0x03, 0x00, 0x00, 0x03, 0x04, 0x28, 0x1e, 0x00,
+ /* reg18 reg19 reg1a reg1b */
+@@ -401,6 +458,7 @@ static const u8 sn_sp80708[0x1c] = {
+
+ /* sequence specific to the sensors - !! index = SENSOR_xxx */
+ static const u8 *sn_tb[] = {
++ sn_adcm1700,
+ sn_hv7131,
+ sn_mi0360,
+ sn_mo4000,
+@@ -409,6 +467,7 @@ static const u8 *sn_tb[] = {
+ sn_ov7630,
+ sn_ov7648,
+ sn_ov7660,
++ sn_po1030,
+ sn_sp80708
+ };
+
+@@ -417,6 +476,11 @@ static const u8 gamma_def[17] = {
+ 0x00, 0x2d, 0x46, 0x5a, 0x6c, 0x7c, 0x8b, 0x99,
+ 0xa6, 0xb2, 0xbf, 0xca, 0xd5, 0xe0, 0xeb, 0xf5, 0xff
+ };
++/* gamma for sensor ADCM1700 */
++static const u8 gamma_spec_0[17] = {
++ 0x0f, 0x39, 0x5a, 0x74, 0x86, 0x95, 0xa6, 0xb4,
++ 0xbd, 0xc4, 0xcc, 0xd4, 0xd5, 0xde, 0xe4, 0xed, 0xf5
++};
+ /* gamma for sensors HV7131R and MT9V111 */
+ static const u8 gamma_spec_1[17] = {
+ 0x08, 0x3a, 0x52, 0x65, 0x75, 0x83, 0x91, 0x9d,
+@@ -435,6 +499,42 @@ static const u8 reg84[] = {
+ 0x3e, 0x00, 0xcd, 0x0f, 0xf7, 0x0f, /* VR VG VB */
+ 0x00, 0x00, 0x00 /* YUV offsets */
+ };
++static const u8 adcm1700_sensor_init[][8] = {
++ {0xa0, 0x51, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xb0, 0x51, 0x04, 0x08, 0x00, 0x00, 0x00, 0x10}, /* reset */
++ {0xdd, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++ {0xb0, 0x51, 0x04, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xdd, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++ {0xb0, 0x51, 0x0c, 0xe0, 0x2e, 0x00, 0x00, 0x10},
++ {0xb0, 0x51, 0x10, 0x02, 0x02, 0x00, 0x00, 0x10},
++ {0xb0, 0x51, 0x14, 0x0e, 0x0e, 0x00, 0x00, 0x10},
++ {0xb0, 0x51, 0x1c, 0x00, 0x80, 0x00, 0x00, 0x10},
++ {0xb0, 0x51, 0x20, 0x01, 0x00, 0x00, 0x00, 0x10},
++ {0xdd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++ {0xb0, 0x51, 0x04, 0x04, 0x00, 0x00, 0x00, 0x10},
++ {0xdd, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
++ {0xb0, 0x51, 0x04, 0x01, 0x00, 0x00, 0x00, 0x10},
++ {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
++ {0xb0, 0x51, 0x14, 0x01, 0x00, 0x00, 0x00, 0x10},
++ {0xb0, 0x51, 0x32, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {}
++};
++static const u8 adcm1700_sensor_param1[][8] = {
++ {0xb0, 0x51, 0x26, 0xf9, 0x01, 0x00, 0x00, 0x10}, /* exposure? */
++ {0xd0, 0x51, 0x1e, 0x8e, 0x8e, 0x8e, 0x8e, 0x10},
++
++ {0xa0, 0x51, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x10},
++ {0xb0, 0x51, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10},
++ {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
++ {0xb0, 0x51, 0x32, 0x00, 0x72, 0x00, 0x00, 0x10},
++ {0xd0, 0x51, 0x1e, 0xbe, 0xd7, 0xe8, 0xbe, 0x10}, /* exposure? */
++
++ {0xa0, 0x51, 0xfe, 0x01, 0x00, 0x00, 0x00, 0x10},
++ {0xb0, 0x51, 0x00, 0x02, 0x00, 0x00, 0x00, 0x10},
++ {0xa0, 0x51, 0xfe, 0x10, 0x00, 0x00, 0x00, 0x10},
++ {0xb0, 0x51, 0x32, 0x00, 0xa2, 0x00, 0x00, 0x10},
++ {}
++};
+ static const u8 hv7131r_sensor_init[][8] = {
+ {0xc1, 0x11, 0x01, 0x08, 0x01, 0x00, 0x00, 0x10},
+ {0xb1, 0x11, 0x34, 0x17, 0x7f, 0x00, 0x00, 0x10},
+@@ -455,7 +555,7 @@ static const u8 hv7131r_sensor_init[][8] = {
+
+ {0xa1, 0x11, 0x01, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
+- {0xa1, 0x11, 0x21, 0xD0, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x23, 0x09, 0x00, 0x00, 0x00, 0x10},
+
+@@ -464,6 +564,8 @@ static const u8 hv7131r_sensor_init[][8] = {
+ {0xa1, 0x11, 0x21, 0xd0, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x11, 0x23, 0x10, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10},
++ /* set sensor clock */
+ {}
+ };
+ static const u8 mi0360_sensor_init[][8] = {
+@@ -545,7 +647,7 @@ static const u8 mo4000_sensor_init[][8] = {
+ };
+ static const u8 mt9v111_sensor_init[][8] = {
+ {0xb1, 0x5c, 0x0d, 0x00, 0x01, 0x00, 0x00, 0x10}, /* reset? */
+- /* delay 20 ms */
++ {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+ {0xb1, 0x5c, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x5c, 0x01, 0x00, 0x01, 0x00, 0x00, 0x10}, /* IFP select */
+ {0xb1, 0x5c, 0x08, 0x04, 0x80, 0x00, 0x00, 0x10}, /* output fmt ctrl */
+@@ -572,10 +674,16 @@ static const u8 mt9v111_sensor_init[][8] = {
+ {0xb1, 0x5c, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x10}, /* digital zoom */
+ {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10}, /* read mode */
+ {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
+- /*******/
++ {}
++};
++static const u8 mt9v111_sensor_param1[][8] = {
+ {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xb1, 0x5c, 0x20, 0x00, 0x00, 0x00, 0x00, 0x10},
++#if 1
+ {0xb1, 0x5c, 0x09, 0x01, 0x2c, 0x00, 0x00, 0x10},
++#else
++ {0xb1, 0x5c, 0x09, 0x00, 0x64, 0x00, 0x00, 0x10}, /* shutter width */
++#endif
+ {0xd1, 0x5c, 0x2b, 0x00, 0x33, 0x00, 0xa0, 0x10}, /* green1 gain */
+ {0xd1, 0x5c, 0x2d, 0x00, 0xa0, 0x00, 0x33, 0x10}, /* red gain */
+ /*******/
+@@ -585,14 +693,25 @@ static const u8 mt9v111_sensor_init[][8] = {
+ {0xb1, 0x5c, 0x35, 0x01, 0xc0, 0x00, 0x00, 0x10}, /* global gain */
+ {}
+ };
+-static const u8 om6802_sensor_init[][8] = {
++static const u8 om6802_init0[2][8] = {
++/*fixme: variable*/
++#if 0
+ {0xa0, 0x34, 0x90, 0x05, 0x00, 0x00, 0x00, 0x10},
+ {0xa0, 0x34, 0x49, 0x85, 0x00, 0x00, 0x00, 0x10},
+- {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10},
++#else
++ {0xa0, 0x34, 0x29, 0x0e, 0x00, 0x00, 0x00, 0x10},
++ {0xa0, 0x34, 0x23, 0xb0, 0x00, 0x00, 0x00, 0x10},
++#endif
++};
++static const u8 om6802_sensor_init[][8] = {
++ {0xa0, 0x34, 0xdf, 0x6d, 0x00, 0x00, 0x00, 0x10},
++ /* factory mode */
+ {0xa0, 0x34, 0xdd, 0x18, 0x00, 0x00, 0x00, 0x10},
++ /* output raw RGB */
++ {0xa0, 0x34, 0x5a, 0xc0, 0x00, 0x00, 0x00, 0x10},
+ /* {0xa0, 0x34, 0xfb, 0x11, 0x00, 0x00, 0x00, 0x10}, */
+ {0xa0, 0x34, 0xf0, 0x04, 0x00, 0x00, 0x00, 0x10},
+- /* white balance & auto-exposure */
++ /* auto-exposure speed (0) / white balance mode (auto RGB) */
+ /* {0xa0, 0x34, 0xf1, 0x02, 0x00, 0x00, 0x00, 0x10},
+ * set color mode */
+ /* {0xa0, 0x34, 0xfe, 0x5b, 0x00, 0x00, 0x00, 0x10},
+@@ -606,26 +725,29 @@ static const u8 om6802_sensor_init[][8] = {
+ /* {0xa0, 0x34, 0xe8, 0x31, 0x00, 0x00, 0x00, 0x10},
+ * preset gamma */
+ {0xa0, 0x34, 0xe9, 0x0f, 0x00, 0x00, 0x00, 0x10},
+- /* luminance mode (0x4f = AE) */
++ /* luminance mode (0x4f -> AutoExpo on) */
+ {0xa0, 0x34, 0xe4, 0xff, 0x00, 0x00, 0x00, 0x10},
+ /* preset shutter */
+ /* {0xa0, 0x34, 0xef, 0x00, 0x00, 0x00, 0x00, 0x10},
+ * auto frame rate */
+ /* {0xa0, 0x34, 0xfb, 0xee, 0x00, 0x00, 0x00, 0x10}, */
+-
+-/* {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10}, */
+-/* {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10}, */
+-/* {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10}, */
+-/* {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10}, */
++ {0xa0, 0x34, 0x5d, 0x80, 0x00, 0x00, 0x00, 0x10},
++ {}
++};
++static const u8 om6802_sensor_param1[][8] = {
++ {0xa0, 0x34, 0x71, 0x84, 0x00, 0x00, 0x00, 0x10},
++ {0xa0, 0x34, 0x72, 0x05, 0x00, 0x00, 0x00, 0x10},
++ {0xa0, 0x34, 0x68, 0x80, 0x00, 0x00, 0x00, 0x10},
++ {0xa0, 0x34, 0x69, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {}
+ };
+ static const u8 ov7630_sensor_init[][8] = {
+ {0xa1, 0x21, 0x76, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10},
+-/* win: delay 20ms */
++ {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+ {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x12, 0xc8, 0x00, 0x00, 0x00, 0x10},
+-/* win: delay 20ms */
++ {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+ {0xa1, 0x21, 0x12, 0x48, 0x00, 0x00, 0x00, 0x10},
+ /* win: i2c_r from 00 to 80 */
+ {0xd1, 0x21, 0x03, 0x80, 0x10, 0x20, 0x80, 0x10},
+@@ -677,6 +799,7 @@ static const u8 ov7630_sensor_init[][8] = {
+ static const u8 ov7648_sensor_init[][8] = {
+ {0xa1, 0x21, 0x76, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset */
++ {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+ {0xa1, 0x21, 0x12, 0x00, 0x00, 0x00, 0x00, 0x10},
+ {0xd1, 0x21, 0x03, 0xa4, 0x30, 0x88, 0x00, 0x10},
+ {0xb1, 0x21, 0x11, 0x80, 0x08, 0x00, 0x00, 0x10},
+@@ -701,7 +824,9 @@ static const u8 ov7648_sensor_init[][8] = {
+ /* {0xd1, 0x21, 0x25, 0x80, 0x32, 0xfe, 0xa0, 0x10}, jfm done */
+ /* {0xd1, 0x21, 0x29, 0x00, 0x91, 0x00, 0x88, 0x10}, jfm done */
+ /* {0xb1, 0x21, 0x2d, 0x85, 0x00, 0x00, 0x00, 0x10}, set by setfreq */
+-/*...*/
++ {}
++};
++static const u8 ov7648_sensor_param1[][8] = {
+ /* {0xa1, 0x21, 0x12, 0x08, 0x00, 0x00, 0x00, 0x10}, jfm done */
+ /* {0xa1, 0x21, 0x75, 0x06, 0x00, 0x00, 0x00, 0x10}, * COMN
+ * set by setvflip */
+@@ -723,7 +848,7 @@ static const u8 ov7648_sensor_init[][8] = {
+
+ static const u8 ov7660_sensor_init[][8] = {
+ {0xa1, 0x21, 0x12, 0x80, 0x00, 0x00, 0x00, 0x10}, /* reset SCCB */
+-/* (delay 20ms) */
++ {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
+ {0xa1, 0x21, 0x12, 0x05, 0x00, 0x00, 0x00, 0x10},
+ /* Outformat = rawRGB */
+ {0xa1, 0x21, 0x13, 0xb8, 0x00, 0x00, 0x00, 0x10}, /* init COM8 */
+@@ -733,7 +858,11 @@ static const u8 ov7660_sensor_init[][8] = {
+ /* COM 1 BAVE GEAVE AECHH */
+ {0xb1, 0x21, 0x08, 0x83, 0x01, 0x00, 0x00, 0x10}, /* RAVE COM2 */
+ {0xd1, 0x21, 0x0c, 0x00, 0x08, 0x04, 0x4f, 0x10}, /* COM 3 4 5 6 */
++#if 0
++ {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xf8, 0x10},
++#else
+ {0xd1, 0x21, 0x10, 0x7f, 0x40, 0x05, 0xff, 0x10},
++#endif
+ /* AECH CLKRC COM7 COM8 */
+ {0xc1, 0x21, 0x14, 0x2c, 0x00, 0x02, 0x00, 0x10}, /* COM9 COM10 */
+ {0xd1, 0x21, 0x17, 0x10, 0x60, 0x02, 0x7b, 0x10},
+@@ -783,8 +912,11 @@ static const u8 ov7660_sensor_init[][8] = {
+ {0xc1, 0x21, 0x88, 0xaf, 0xc7, 0xdf, 0x00, 0x10}, /* gamma curve */
+ {0xc1, 0x21, 0x8b, 0x99, 0x99, 0xcf, 0x00, 0x10}, /* reserved */
+ {0xb1, 0x21, 0x92, 0x00, 0x00, 0x00, 0x00, 0x10}, /* DM_LNL/H */
++/* not in all ms-win traces*/
+ {0xa1, 0x21, 0xa1, 0x00, 0x00, 0x00, 0x00, 0x10},
+-/****** (some exchanges in the win trace) ******/
++ {}
++};
++static const u8 ov7660_sensor_param1[][8] = {
+ {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10}, /* MVFP */
+ /* bits[3..0]reserved */
+ {0xa1, 0x21, 0x1e, 0x01, 0x00, 0x00, 0x00, 0x10},
+@@ -797,6 +929,7 @@ static const u8 ov7660_sensor_init[][8] = {
+ {0xa1, 0x21, 0x00, 0x1f, 0x00, 0x00, 0x00, 0x10}, /* GAIN */
+ /* {0xb1, 0x21, 0x01, 0x78, 0x78, 0x00, 0x00, 0x10}, * BLUE */
+ /****** (some exchanges in the win trace) ******/
++/*fixme:param2*/
+ {0xa1, 0x21, 0x93, 0x00, 0x00, 0x00, 0x00, 0x10},/* dummy line hight */
+ {0xa1, 0x21, 0x92, 0x25, 0x00, 0x00, 0x00, 0x10}, /* dummy line low */
+ {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10}, /* EXHCH */
+@@ -804,6 +937,7 @@ static const u8 ov7660_sensor_init[][8] = {
+ /* {0xa1, 0x21, 0x02, 0x90, 0x00, 0x00, 0x00, 0x10}, * RED */
+ /****** (some exchanges in the win trace) ******/
+ /******!! startsensor KO if changed !!****/
++/*fixme: param3*/
+ {0xa1, 0x21, 0x93, 0x01, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x92, 0xff, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x21, 0x2a, 0x00, 0x00, 0x00, 0x00, 0x10},
+@@ -811,6 +945,91 @@ static const u8 ov7660_sensor_init[][8] = {
+ {}
+ };
+
++static const u8 po1030_sensor_init[][8] = {
++/* the sensor registers are described in m5602/m5602_po1030.h */
++ {0xa1, 0x6e, 0x3f, 0x20, 0x00, 0x00, 0x00, 0x10}, /* sensor reset */
++ {0xdd, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* delay 20ms */
++ {0xa1, 0x6e, 0x3f, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x6e, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x6e, 0x04, 0x02, 0xb1, 0x02, 0x39, 0x10},
++ {0xd1, 0x6e, 0x08, 0x00, 0x01, 0x00, 0x00, 0x10},
++ {0xd1, 0x6e, 0x0c, 0x02, 0x7f, 0x01, 0xe0, 0x10},
++ {0xd1, 0x6e, 0x12, 0x03, 0x02, 0x00, 0x03, 0x10},
++ {0xd1, 0x6e, 0x16, 0x85, 0x40, 0x4a, 0x40, 0x10}, /* r/g1/b/g2 gains */
++ {0xc1, 0x6e, 0x1a, 0x00, 0x80, 0x00, 0x00, 0x10},
++ {0xd1, 0x6e, 0x1d, 0x08, 0x03, 0x00, 0x00, 0x10},
++ {0xd1, 0x6e, 0x23, 0x00, 0xb0, 0x00, 0x94, 0x10},
++ {0xd1, 0x6e, 0x27, 0x58, 0x00, 0x00, 0x00, 0x10},
++ {0xb1, 0x6e, 0x2b, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x6e, 0x2d, 0x14, 0x35, 0x61, 0x84, 0x10}, /* gamma corr */
++ {0xd1, 0x6e, 0x31, 0xa2, 0xbd, 0xd8, 0xff, 0x10},
++ {0xd1, 0x6e, 0x35, 0x06, 0x1e, 0x12, 0x02, 0x10}, /* color matrix */
++ {0xd1, 0x6e, 0x39, 0xaa, 0x53, 0x37, 0xd5, 0x10},
++ {0xa1, 0x6e, 0x3d, 0xf2, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x6e, 0x3e, 0x00, 0x00, 0x80, 0x03, 0x10},
++ {0xd1, 0x6e, 0x42, 0x03, 0x00, 0x00, 0x00, 0x10},
++ {0xc1, 0x6e, 0x46, 0x00, 0x80, 0x80, 0x00, 0x10},
++ {0xd1, 0x6e, 0x4b, 0x02, 0xef, 0x08, 0xcd, 0x10},
++ {0xd1, 0x6e, 0x4f, 0x00, 0xd0, 0x00, 0xa0, 0x10},
++ {0xd1, 0x6e, 0x53, 0x01, 0xaa, 0x01, 0x40, 0x10},
++ {0xd1, 0x6e, 0x5a, 0x50, 0x04, 0x30, 0x03, 0x10}, /* raw rgb bayer */
++ {0xa1, 0x6e, 0x5e, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x6e, 0x5f, 0x10, 0x40, 0xff, 0x00, 0x10},
++
++ {0xd1, 0x6e, 0x63, 0x40, 0x40, 0x00, 0x00, 0x10},
++ {0xd1, 0x6e, 0x67, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x6e, 0x6b, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xd1, 0x6e, 0x6f, 0x00, 0x00, 0x00, 0x00, 0x10},
++ {0xc1, 0x6e, 0x73, 0x10, 0x80, 0xeb, 0x00, 0x10},
++ {}
++};
++static const u8 po1030_sensor_param1[][8] = {
++#if 1
++/* from ms-win traces - these values change with auto gain/expo/wb.. */
++ {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10},
++#if 1
++/* mean values */
++ {0xc1, 0x6e, 0x1a, 0x02, 0xd4, 0xa4, 0x00, 0x10}, /* integlines */
++ {0xa1, 0x6e, 0x15, 0x04, 0x00, 0x00, 0x00, 0x10}, /* global gain */
++ {0xc1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10}, /* r/g1/b gains */
++
++ {0xa1, 0x6e, 0x1d, 0x08, 0x00, 0x00, 0x00, 0x10}, /* control1 */
++ {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10}, /* frameheight */
++ {0xa1, 0x6e, 0x07, 0xd5, 0x00, 0x00, 0x00, 0x10},
++/* {0xc1, 0x6e, 0x16, 0x49, 0x40, 0x45, 0x00, 0x10}, */
++#else
++/* 1st values */
++ {0xc1, 0x6e, 0x1a, 0x02, 0x3a, 0x00, 0x00, 0x10},
++ {0xa1, 0x6e, 0x15, 0x01, 0x00, 0x00, 0x00, 0x10},
++ {0xc1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10},
++
++ {0xa1, 0x6e, 0x1d, 0x08, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x6e, 0x07, 0x44, 0x00, 0x00, 0x00, 0x10},
++ {0xc1, 0x6e, 0x16, 0x49, 0x40, 0x45, 0x00, 0x10},
++#endif
++#else
++ {0xa1, 0x6e, 0x1e, 0x03, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x6e, 0x15, 0x01, 0x00, 0x00, 0x00, 0x10},
++ {0xc1, 0x6e, 0x16, 0x40, 0x40, 0x40, 0x00, 0x10},
++ {0xa1, 0x6e, 0x1d, 0x08, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x6e, 0x07, 0xb5, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x6e, 0x18, 0x6B, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x6e, 0x1d, 0x08, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x6e, 0x06, 0x02, 0x00, 0x00, 0x00, 0x10},
++ {0xa1, 0x6e, 0x07, 0xb8, 0x00, 0x00, 0x00, 0x10},
++
++ {0xc1, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00},
++ {0xa1, 0x6e, 0x06, 0x03, 0x00, 0x00, 0x00, 0x10}, /* Bright... */
++ {0xa1, 0x6e, 0x07, 0x66, 0x00, 0x00, 0x00, 0x10}, /* B.. */
++ {0xc1, 0x6e, 0x1a, 0x03, 0x65, 0x90, 0x00, 0x10}, /* Bright/Witen....*/
++/* {0xc1, 0x6e, 0x16, 0x45, 0x40, 0x60, 0x00, 0x10}, * Bright/Witene */
++#endif
++ {}
++};
++
+ static const u8 sp80708_sensor_init[][8] = {
+ {0xa1, 0x18, 0x06, 0xf9, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x09, 0x1f, 0x00, 0x00, 0x00, 0x10},
+@@ -883,7 +1102,9 @@ static const u8 sp80708_sensor_init[][8] = {
+ {0xa1, 0x18, 0x67, 0x24, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x68, 0x08, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x2f, 0xc9, 0x00, 0x00, 0x00, 0x10},
+- /********/
++ {}
++};
++static const u8 sp80708_sensor_param1[][8] = {
+ {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x0c, 0x04, 0x00, 0x00, 0x00, 0x10},
+ {0xa1, 0x18, 0x03, 0x01, 0x00, 0x00, 0x00, 0x10},
+@@ -894,6 +1115,20 @@ static const u8 sp80708_sensor_init[][8] = {
+ {}
+ };
+
++static const u8 (*sensor_init[11])[8] = {
++ adcm1700_sensor_init, /* ADCM1700 0 */
++ hv7131r_sensor_init, /* HV7131R 1 */
++ mi0360_sensor_init, /* MI0360 2 */
++ mo4000_sensor_init, /* MO4000 3 */
++ mt9v111_sensor_init, /* MT9V111 4 */
++ om6802_sensor_init, /* OM6802 5 */
++ ov7630_sensor_init, /* OV7630 6 */
++ ov7648_sensor_init, /* OV7648 7 */
++ ov7660_sensor_init, /* OV7660 8 */
++ po1030_sensor_init, /* PO1030 9 */
++ sp80708_sensor_init, /* SP80708 10 */
++};
++
+ /* read <len> bytes to gspca_dev->usb_buf */
+ static void reg_r(struct gspca_dev *gspca_dev,
+ u16 value, int len)
+@@ -958,8 +1193,16 @@ static void i2c_w1(struct gspca_dev *gspca_dev, u8 reg, u8 val)
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ PDEBUG(D_USBO, "i2c_w2 [%02x] = %02x", reg, val);
+- gspca_dev->usb_buf[0] = 0x81 | (2 << 4); /* = a1 */
+- gspca_dev->usb_buf[1] = sd->i2c_base;
++ switch (sd->sensor) {
++ case SENSOR_ADCM1700:
++ case SENSOR_OM6802: /* i2c command = a0 (100 kHz) */
++ gspca_dev->usb_buf[0] = 0x80 | (2 << 4);
++ break;
++ default: /* i2c command = a1 (400 kHz) */
++ gspca_dev->usb_buf[0] = 0x81 | (2 << 4);
++ break;
++ }
++ gspca_dev->usb_buf[1] = sd->i2c_addr;
+ gspca_dev->usb_buf[2] = reg;
+ gspca_dev->usb_buf[3] = val;
+ gspca_dev->usb_buf[4] = 0;
+@@ -991,14 +1234,22 @@ static void i2c_w8(struct gspca_dev *gspca_dev,
+ msleep(2);
+ }
+
+-/* read 5 bytes in gspca_dev->usb_buf */
+-static void i2c_r5(struct gspca_dev *gspca_dev, u8 reg)
++/* sensor read 'len' (1..5) bytes in gspca_dev->usb_buf */
++static void i2c_r(struct gspca_dev *gspca_dev, u8 reg, int len)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 mode[8];
+
+- mode[0] = 0x81 | 0x10;
+- mode[1] = sd->i2c_base;
++ switch (sd->sensor) {
++ case SENSOR_ADCM1700:
++ case SENSOR_OM6802: /* i2c command = 90 (100 kHz) */
++ mode[0] = 0x80 | 0x10;
++ break;
++ default: /* i2c command = 91 (400 kHz) */
++ mode[0] = 0x81 | 0x10;
++ break;
++ }
++ mode[1] = sd->i2c_addr;
+ mode[2] = reg;
+ mode[3] = 0;
+ mode[4] = 0;
+@@ -1007,33 +1258,43 @@ static void i2c_r5(struct gspca_dev *gspca_dev, u8 reg)
+ mode[7] = 0x10;
+ i2c_w8(gspca_dev, mode);
+ msleep(2);
+- mode[0] = 0x81 | (5 << 4) | 0x02;
++ mode[0] = (mode[0] & 0x81) | (len << 4) | 0x02;
+ mode[2] = 0;
+ i2c_w8(gspca_dev, mode);
+ msleep(2);
+ reg_r(gspca_dev, 0x0a, 5);
+ }
+
+-static int hv7131r_probe(struct gspca_dev *gspca_dev)
++static void i2c_w_seq(struct gspca_dev *gspca_dev,
++ const u8 (*data)[8])
++{
++ while ((*data)[0] != 0) {
++ if ((*data)[0] != 0xdd)
++ i2c_w8(gspca_dev, *data);
++ else
++ msleep((*data)[1]);
++ data++;
++ }
++}
++
++static void hv7131r_probe(struct gspca_dev *gspca_dev)
+ {
+ i2c_w1(gspca_dev, 0x02, 0); /* sensor wakeup */
+ msleep(10);
+ reg_w1(gspca_dev, 0x02, 0x66); /* Gpio on */
+ msleep(10);
+- i2c_r5(gspca_dev, 0); /* read sensor id */
++ i2c_r(gspca_dev, 0, 5); /* read sensor id */
+ if (gspca_dev->usb_buf[0] == 0x02
+ && gspca_dev->usb_buf[1] == 0x09
+ && gspca_dev->usb_buf[2] == 0x01
+ && gspca_dev->usb_buf[3] == 0x00
+ && gspca_dev->usb_buf[4] == 0x00) {
+- PDEBUG(D_PROBE, "Find Sensor sn9c102P HV7131R");
+- return 0;
++ PDEBUG(D_PROBE, "Sensor sn9c102P HV7131R found");
++ return;
+ }
+- PDEBUG(D_PROBE, "Find Sensor 0x%02x 0x%02x 0x%02x",
++ PDEBUG(D_PROBE, "Sensor 0x%02x 0x%02x 0x%02x - sn9c102P not found",
+ gspca_dev->usb_buf[0], gspca_dev->usb_buf[1],
+ gspca_dev->usb_buf[2]);
+- PDEBUG(D_PROBE, "Sensor sn9c102P Not found");
+- return -ENODEV;
+ }
+
+ static void mi0360_probe(struct gspca_dev *gspca_dev)
+@@ -1075,7 +1336,6 @@ static void mi0360_probe(struct gspca_dev *gspca_dev)
+ case 0x823a:
+ PDEBUG(D_PROBE, "Sensor mt9v111");
+ sd->sensor = SENSOR_MT9V111;
+- sd->i2c_base = 0x5c;
+ break;
+ case 0x8243:
+ PDEBUG(D_PROBE, "Sensor mi0360");
+@@ -1086,26 +1346,70 @@ static void mi0360_probe(struct gspca_dev *gspca_dev)
+ }
+ }
+
+-static int configure_gpio(struct gspca_dev *gspca_dev,
++static void ov7648_probe(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ /* check ov76xx */
++ reg_w1(gspca_dev, 0x17, 0x62);
++ reg_w1(gspca_dev, 0x01, 0x08);
++ sd->i2c_addr = 0x21;
++ i2c_r(gspca_dev, 0x0a, 2);
++ if (gspca_dev->usb_buf[3] == 0x76) { /* ov76xx */
++ PDEBUG(D_PROBE, "Sensor ov%02x%02x",
++ gspca_dev->usb_buf[3], gspca_dev->usb_buf[4]);
++ return;
++ }
++
++ /* reset */
++ reg_w1(gspca_dev, 0x01, 0x29);
++ reg_w1(gspca_dev, 0x17, 0x42);
++
++ /* check po1030 */
++ reg_w1(gspca_dev, 0x17, 0x62);
++ reg_w1(gspca_dev, 0x01, 0x08);
++ sd->i2c_addr = 0x6e;
++ i2c_r(gspca_dev, 0x00, 2);
++ if (gspca_dev->usb_buf[3] == 0x10 /* po1030 */
++ && gspca_dev->usb_buf[4] == 0x30) {
++ PDEBUG(D_PROBE, "Sensor po1030");
++ sd->sensor = SENSOR_PO1030;
++ return;
++ }
++
++ PDEBUG(D_PROBE, "Unknown sensor %02x%02x",
++ gspca_dev->usb_buf[3], gspca_dev->usb_buf[4]);
++}
++
++static void bridge_init(struct gspca_dev *gspca_dev,
+ const u8 *sn9c1xx)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ const u8 *reg9a;
+ static const u8 reg9a_def[] =
++#if 1
+ {0x00, 0x40, 0x20, 0x00, 0x00, 0x00};
++#else
++ {0x00, 0x40, 0x20, 0x10, 0x00, 0x04};
++#endif
+ static const u8 reg9a_spec[] =
+ {0x00, 0x40, 0x38, 0x30, 0x00, 0x20};
+ static const u8 regd4[] = {0x60, 0x00, 0x00};
+
+ reg_w1(gspca_dev, 0xf1, 0x00);
++#if 1
+ reg_w1(gspca_dev, 0x01, sn9c1xx[1]);
++#else
++ reg_w1(gspca_dev, 0x01, 0x00); /*jfm: in some win traces*/
++#endif
+
+ /* configure gpio */
+ reg_w(gspca_dev, 0x01, &sn9c1xx[1], 2);
+ reg_w(gspca_dev, 0x08, &sn9c1xx[8], 2);
+- reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5); /* jfm len was 3 */
++ reg_w(gspca_dev, 0x17, &sn9c1xx[0x17], 5);
+ switch (sd->sensor) {
+ case SENSOR_OV7660:
++ case SENSOR_PO1030:
+ case SENSOR_SP80708:
+ reg9a = reg9a_spec;
+ break;
+@@ -1115,11 +1419,17 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
+ }
+ reg_w(gspca_dev, 0x9a, reg9a, 6);
+
+- reg_w(gspca_dev, 0xd4, regd4, sizeof regd4); /*fixme:jfm was 60 only*/
++ reg_w(gspca_dev, 0xd4, regd4, sizeof regd4);
+
+ reg_w(gspca_dev, 0x03, &sn9c1xx[3], 0x0f);
+
+ switch (sd->sensor) {
++ case SENSOR_ADCM1700:
++ reg_w1(gspca_dev, 0x01, 0x43);
++ reg_w1(gspca_dev, 0x17, 0x62);
++ reg_w1(gspca_dev, 0x01, 0x42);
++ reg_w1(gspca_dev, 0x01, 0x42);
++ break;
+ case SENSOR_MT9V111:
+ reg_w1(gspca_dev, 0x01, 0x61);
+ reg_w1(gspca_dev, 0x17, 0x61);
+@@ -1127,10 +1437,22 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
+ reg_w1(gspca_dev, 0x01, 0x40);
+ break;
+ case SENSOR_OM6802:
+- reg_w1(gspca_dev, 0x02, 0x71);
+- reg_w1(gspca_dev, 0x01, 0x42);
++ msleep(10);
++ reg_w1(gspca_dev, 0x02, 0x73);
++ reg_w1(gspca_dev, 0x17, 0x60);
++ reg_w1(gspca_dev, 0x01, 0x22);
++ msleep(100);
++ reg_w1(gspca_dev, 0x01, 0x62);
++ reg_w1(gspca_dev, 0x17, 0x64);
+ reg_w1(gspca_dev, 0x17, 0x64);
+ reg_w1(gspca_dev, 0x01, 0x42);
++ msleep(10);
++ reg_w1(gspca_dev, 0x01, 0x42);
++ i2c_w8(gspca_dev, om6802_init0[0]);
++ i2c_w8(gspca_dev, om6802_init0[1]);
++ msleep(15);
++ reg_w1(gspca_dev, 0x02, 0x71);
++ msleep(150);
+ break;
+ case SENSOR_OV7630:
+ reg_w1(gspca_dev, 0x01, 0x61);
+@@ -1144,7 +1466,22 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
+ reg_w1(gspca_dev, 0x01, 0x62);
+ reg_w1(gspca_dev, 0x01, 0x42);
+ break;
++ case SENSOR_PO1030:
++ reg_w1(gspca_dev, 0x01, 0x61);
++ reg_w1(gspca_dev, 0x17, 0x20);
++ reg_w1(gspca_dev, 0x01, 0x60);
++ reg_w1(gspca_dev, 0x01, 0x40);
++ break;
+ case SENSOR_OV7660:
++#if 1
++ /* fall thru */
++#else
++ reg_w1(gspca_dev, 0x01, 0x61);
++ reg_w1(gspca_dev, 0x17, 0x20);
++ reg_w1(gspca_dev, 0x01, 0x60);
++ reg_w1(gspca_dev, 0x01, 0x40);
++ break;
++#endif
+ case SENSOR_SP80708:
+ reg_w1(gspca_dev, 0x01, 0x63);
+ reg_w1(gspca_dev, 0x17, 0x20);
+@@ -1153,143 +1490,18 @@ static int configure_gpio(struct gspca_dev *gspca_dev,
+ msleep(100);
+ reg_w1(gspca_dev, 0x02, 0x62);
+ break;
++ default:
+ /* case SENSOR_HV7131R: */
+ /* case SENSOR_MI0360: */
+ /* case SENSOR_MO4000: */
+- default:
+ reg_w1(gspca_dev, 0x01, 0x43);
+ reg_w1(gspca_dev, 0x17, 0x61);
+ reg_w1(gspca_dev, 0x01, 0x42);
+- if (sd->sensor == SENSOR_HV7131R) {
+- if (hv7131r_probe(gspca_dev) < 0)
+- return -ENODEV;
+- }
++ if (sd->sensor == SENSOR_HV7131R
++ && sd->bridge == BRIDGE_SN9C102P)
++ hv7131r_probe(gspca_dev);
+ break;
+ }
+- return 0;
+-}
+-
+-static void hv7131R_InitSensor(struct gspca_dev *gspca_dev)
+-{
+- int i = 0;
+- static const u8 SetSensorClk[] = /* 0x08 Mclk */
+- { 0xa1, 0x11, 0x01, 0x18, 0x00, 0x00, 0x00, 0x10 };
+-
+- while (hv7131r_sensor_init[i][0]) {
+- i2c_w8(gspca_dev, hv7131r_sensor_init[i]);
+- i++;
+- }
+- i2c_w8(gspca_dev, SetSensorClk);
+-}
+-
+-static void mi0360_InitSensor(struct gspca_dev *gspca_dev)
+-{
+- int i = 0;
+-
+- while (mi0360_sensor_init[i][0]) {
+- i2c_w8(gspca_dev, mi0360_sensor_init[i]);
+- i++;
+- }
+-}
+-
+-static void mo4000_InitSensor(struct gspca_dev *gspca_dev)
+-{
+- int i = 0;
+-
+- while (mo4000_sensor_init[i][0]) {
+- i2c_w8(gspca_dev, mo4000_sensor_init[i]);
+- i++;
+- }
+-}
+-
+-static void mt9v111_InitSensor(struct gspca_dev *gspca_dev)
+-{
+- int i = 0;
+-
+- i2c_w8(gspca_dev, mt9v111_sensor_init[i]);
+- i++;
+- msleep(20);
+- while (mt9v111_sensor_init[i][0]) {
+- i2c_w8(gspca_dev, mt9v111_sensor_init[i]);
+- i++;
+- }
+-}
+-
+-static void om6802_InitSensor(struct gspca_dev *gspca_dev)
+-{
+- int i = 0;
+-
+- while (om6802_sensor_init[i][0]) {
+- i2c_w8(gspca_dev, om6802_sensor_init[i]);
+- i++;
+- }
+-}
+-
+-static void ov7630_InitSensor(struct gspca_dev *gspca_dev)
+-{
+- int i = 0;
+-
+- i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 76 01 */
+- i++;
+- i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 c8 (RGB+SRST) */
+- i++;
+- msleep(20);
+- i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 48 */
+- i++;
+- i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 c8 */
+- i++;
+- msleep(20);
+- i2c_w8(gspca_dev, ov7630_sensor_init[i]); /* 12 48 */
+- i++;
+-/*jfm:win i2c_r from 00 to 80*/
+-
+- while (ov7630_sensor_init[i][0]) {
+- i2c_w8(gspca_dev, ov7630_sensor_init[i]);
+- i++;
+- }
+-}
+-
+-static void ov7648_InitSensor(struct gspca_dev *gspca_dev)
+-{
+- int i = 0;
+-
+- i2c_w8(gspca_dev, ov7648_sensor_init[i]);
+- i++;
+-/* win: dble reset */
+- i2c_w8(gspca_dev, ov7648_sensor_init[i]); /* reset */
+- i++;
+- msleep(20);
+-/* win: i2c reg read 00..7f */
+- while (ov7648_sensor_init[i][0]) {
+- i2c_w8(gspca_dev, ov7648_sensor_init[i]);
+- i++;
+- }
+-}
+-
+-static void ov7660_InitSensor(struct gspca_dev *gspca_dev)
+-{
+- int i = 0;
+-
+- i2c_w8(gspca_dev, ov7660_sensor_init[i]); /* reset SCCB */
+- i++;
+- msleep(20);
+- while (ov7660_sensor_init[i][0]) {
+- i2c_w8(gspca_dev, ov7660_sensor_init[i]);
+- i++;
+- }
+-}
+-
+-static void sp80708_InitSensor(struct gspca_dev *gspca_dev)
+-{
+- int i = 0;
+-
+- i2c_w8(gspca_dev, sp80708_sensor_init[i]); /* reset SCCB */
+- i++;
+- msleep(20);
+- while (sp80708_sensor_init[i][0]) {
+- i2c_w8(gspca_dev, sp80708_sensor_init[i]);
+- i++;
+- }
+ }
+
+ /* this function is called at probe time */
+@@ -1299,15 +1511,19 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+
++ sd->bridge = id->driver_info >> 16;
++ sd->sensor = id->driver_info;
++
+ cam = &gspca_dev->cam;
+- cam->cam_mode = vga_mode;
+- cam->nmodes = ARRAY_SIZE(vga_mode);
++ if (sd->sensor == SENSOR_ADCM1700) {
++ cam->cam_mode = cif_mode;
++ cam->nmodes = ARRAY_SIZE(cif_mode);
++ } else {
++ cam->cam_mode = vga_mode;
++ cam->nmodes = ARRAY_SIZE(vga_mode);
++ }
+ cam->npkt = 24; /* 24 packets per ISOC message */
+
+- sd->bridge = id->driver_info >> 16;
+- sd->sensor = id->driver_info >> 8;
+- sd->i2c_base = id->driver_info;
+-
+ sd->brightness = BRIGHTNESS_DEF;
+ sd->contrast = CONTRAST_DEF;
+ sd->colors = COLOR_DEF;
+@@ -1317,12 +1533,19 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ sd->autogain = AUTOGAIN_DEF;
+ sd->ag_cnt = -1;
+ sd->vflip = VFLIP_DEF;
++ switch (sd->sensor) {
++ case SENSOR_OM6802:
++ sd->sharpness = 0x10;
++ break;
++ default:
++ sd->sharpness = SHARPNESS_DEF;
++ break;
++ }
+ sd->infrared = INFRARED_DEF;
+ sd->freq = FREQ_DEF;
+ sd->quality = QUALITY_DEF;
+ sd->jpegqual = 80;
+
+- gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
+ return 0;
+ }
+
+@@ -1330,6 +1553,7 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ static int sd_init(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
++ const u8 *sn9c1xx;
+ u8 regGpio[] = { 0x29, 0x74 };
+ u8 regF1;
+
+@@ -1356,8 +1580,14 @@ static int sd_init(struct gspca_dev *gspca_dev)
+ case BRIDGE_SN9C120:
+ if (regF1 != 0x12)
+ return -ENODEV;
+- if (sd->sensor == SENSOR_MI0360)
++ switch (sd->sensor) {
++ case SENSOR_MI0360:
+ mi0360_probe(gspca_dev);
++ break;
++ case SENSOR_OV7648:
++ ov7648_probe(gspca_dev);
++ break;
++ }
+ regGpio[1] = 0x70;
+ reg_w(gspca_dev, 0x01, regGpio, 2);
+ break;
+@@ -1372,6 +1602,12 @@ static int sd_init(struct gspca_dev *gspca_dev)
+
+ reg_w1(gspca_dev, 0xf1, 0x01);
+
++ /* set the i2c address */
++ sn9c1xx = sn_tb[sd->sensor];
++ sd->i2c_addr = sn9c1xx[9];
++
++ gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
++
+ return 0;
+ }
+
+@@ -1383,7 +1619,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev,
+ switch (sd->sensor) {
+ case SENSOR_HV7131R: {
+ u8 Expodoit[] =
+- { 0xc1, 0x11, 0x25, 0x07, 0x27, 0xc0, 0x00, 0x16 };
++ { 0xc1, 0x11, 0x25, 0x00, 0x00, 0x00, 0x00, 0x16 };
+
+ Expodoit[3] = expo >> 16;
+ Expodoit[4] = expo >> 8;
+@@ -1393,7 +1629,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev,
+ }
+ case SENSOR_MI0360: {
+ u8 expoMi[] = /* exposure 0x0635 -> 4 fp/s 0x10 */
+- { 0xb1, 0x5d, 0x09, 0x06, 0x35, 0x00, 0x00, 0x16 };
++ { 0xb1, 0x5d, 0x09, 0x00, 0x00, 0x00, 0x00, 0x16 };
+ static const u8 doit[] = /* update sensor */
+ { 0xb1, 0x5d, 0x07, 0x00, 0x03, 0x00, 0x00, 0x10 };
+ static const u8 sensorgo[] = /* sensor on */
+@@ -1412,9 +1648,9 @@ static u32 setexposure(struct gspca_dev *gspca_dev,
+ }
+ case SENSOR_MO4000: {
+ u8 expoMof[] =
+- { 0xa1, 0x21, 0x0f, 0x20, 0x00, 0x00, 0x00, 0x10 };
++ { 0xa1, 0x21, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x10 };
+ u8 expoMo10[] =
+- { 0xa1, 0x21, 0x10, 0x20, 0x00, 0x00, 0x00, 0x10 };
++ { 0xa1, 0x21, 0x10, 0x00, 0x00, 0x00, 0x00, 0x10 };
+ static const u8 gainMo[] =
+ { 0xa1, 0x21, 0x00, 0x10, 0x00, 0x00, 0x00, 0x1d };
+
+@@ -1457,7 +1693,7 @@ static u32 setexposure(struct gspca_dev *gspca_dev,
+ expo = 0x0001;
+ gainOm[3] = expo >> 2;
+ i2c_w8(gspca_dev, gainOm);
+- reg_w1(gspca_dev, 0x96, (expo >> 5) & 0x1f);
++ reg_w1(gspca_dev, 0x96, expo >> 5);
+ PDEBUG(D_FRAM, "set exposure %d", gainOm[3]);
+ break;
+ }
+@@ -1473,6 +1709,10 @@ static void setbrightness(struct gspca_dev *gspca_dev)
+
+ k2 = ((int) sd->brightness - 0x8000) >> 10;
+ switch (sd->sensor) {
++ case SENSOR_ADCM1700:
++ if (k2 > 0x1f)
++ k2 = 0; /* only positive Y offset */
++ break;
+ case SENSOR_HV7131R:
+ expo = sd->brightness << 4;
+ if (expo > 0x002dc6c0)
+@@ -1489,7 +1729,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
+ case SENSOR_MT9V111:
+ expo = sd->brightness >> 8;
+ sd->exposure = setexposure(gspca_dev, expo);
+- break;
++ return; /* don't set the Y offset */
+ case SENSOR_OM6802:
+ expo = sd->brightness >> 6;
+ sd->exposure = setexposure(gspca_dev, expo);
+@@ -1497,8 +1737,7 @@ static void setbrightness(struct gspca_dev *gspca_dev)
+ break;
+ }
+
+- if (sd->sensor != SENSOR_MT9V111)
+- reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */
++ reg_w1(gspca_dev, 0x96, k2); /* color matrix Y offset */
+ }
+
+ static void setcontrast(struct gspca_dev *gspca_dev)
+@@ -1526,6 +1765,7 @@ static void setcolors(struct gspca_dev *gspca_dev)
+ -24, -38, 64, /* UR UG UB */
+ 62, -51, -9 /* VR VG VB */
+ };
++
+ for (i = 0; i < 6; i++) {
+ v = uv[i] * sd->colors / COLOR_DEF;
+ reg8a[i * 2] = v;
+@@ -1555,6 +1795,9 @@ static void setgamma(struct gspca_dev *gspca_dev)
+ };
+
+ switch (sd->sensor) {
++ case SENSOR_ADCM1700:
++ gamma_base = gamma_spec_0;
++ break;
+ case SENSOR_HV7131R:
+ case SENSOR_MT9V111:
+ gamma_base = gamma_spec_1;
+@@ -1605,6 +1848,8 @@ static void setvflip(struct sd *sd)
+ {
+ u8 comn;
+
++ if (sd->gspca_dev.ctrl_dis & (1 << VFLIP_IDX))
++ return;
+ if (sd->sensor == SENSOR_OV7630) {
+ comn = 0x02;
+ if (!sd->vflip)
+@@ -1617,14 +1862,25 @@ static void setvflip(struct sd *sd)
+ i2c_w1(&sd->gspca_dev, 0x75, comn);
+ }
+
++static void setsharpness(struct sd *sd)
++{
++ reg_w1(&sd->gspca_dev, 0x99, sd->sharpness);
++}
++
+ static void setinfrared(struct sd *sd)
+ {
+ if (sd->gspca_dev.ctrl_dis & (1 << INFRARED_IDX))
+ return;
+ /*fixme: different sequence for StarCam Clip and StarCam 370i */
++#if 1
+ /* Clip */
+ i2c_w1(&sd->gspca_dev, 0x02, /* gpio */
+ sd->infrared ? 0x66 : 0x64);
++#else
++/* 370i */
++ i2c_w1(&sd->gspca_dev, 0x02, /* gpio */
++ sd->infrared ? 0x55 : 0x54);
++#endif
+ }
+
+ static void setfreq(struct gspca_dev *gspca_dev)
+@@ -1726,11 +1982,14 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+- u8 reg1, reg17;
++ u8 reg1, reg2, reg17;
+ const u8 *sn9c1xx;
++ const u8 (*init)[8];
+ int mode;
+ static const u8 C0[] = { 0x2d, 0x2d, 0x3a, 0x05, 0x04, 0x3f };
+ static const u8 CA[] = { 0x28, 0xd8, 0x14, 0xec };
++ static const u8 CA_adcm1700[] =
++ { 0x14, 0xec, 0x0a, 0xf6 };
+ static const u8 CE[] = { 0x32, 0xdd, 0x2d, 0xdd }; /* MI0360 */
+ static const u8 CE_ov76xx[] =
+ { 0x32, 0xdd, 0x32, 0xdd };
+@@ -1743,25 +2002,57 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ 0x21); /* JPEG 422 */
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+
+- sn9c1xx = sn_tb[(int) sd->sensor];
+- configure_gpio(gspca_dev, sn9c1xx);
++ /* initialize the bridge */
++ sn9c1xx = sn_tb[sd->sensor];
++ bridge_init(gspca_dev, sn9c1xx);
++
++ /* initialize the sensor */
++ i2c_w_seq(gspca_dev, sensor_init[sd->sensor]);
++
++ switch (sd->sensor) {
++ case SENSOR_ADCM1700:
++ reg2 = 0x60;
++ break;
++ case SENSOR_OM6802:
++ reg2 = 0x71;
++ break;
++ case SENSOR_SP80708:
++ reg2 = 0x62;
++ break;
++ default:
++ reg2 = 0x40;
++ break;
++ }
++ reg_w1(gspca_dev, 0x02, reg2);
++ reg_w1(gspca_dev, 0x02, reg2);
+
+ reg_w1(gspca_dev, 0x15, sn9c1xx[0x15]);
+ reg_w1(gspca_dev, 0x16, sn9c1xx[0x16]);
+ reg_w1(gspca_dev, 0x12, sn9c1xx[0x12]);
+ reg_w1(gspca_dev, 0x13, sn9c1xx[0x13]);
+ reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]);
+- reg_w1(gspca_dev, 0xd2, 0x6a); /* DC29 */
+- reg_w1(gspca_dev, 0xd3, 0x50);
++ if (sd->sensor == SENSOR_ADCM1700) {
++ reg_w1(gspca_dev, 0xd2, 0x3a); /* AE_H_SIZE = 116 */
++ reg_w1(gspca_dev, 0xd3, 0x30); /* AE_V_SIZE = 96 */
++ } else {
++ reg_w1(gspca_dev, 0xd2, 0x6a); /* AE_H_SIZE = 212 */
++ reg_w1(gspca_dev, 0xd3, 0x50); /* AE_V_SIZE = 160 */
++ }
+ reg_w1(gspca_dev, 0xc6, 0x00);
+ reg_w1(gspca_dev, 0xc7, 0x00);
+- reg_w1(gspca_dev, 0xc8, 0x50);
+- reg_w1(gspca_dev, 0xc9, 0x3c);
++ if (sd->sensor == SENSOR_ADCM1700) {
++ reg_w1(gspca_dev, 0xc8, 0x2c); /* AW_H_STOP = 352 */
++ reg_w1(gspca_dev, 0xc9, 0x24); /* AW_V_STOP = 288 */
++ } else {
++ reg_w1(gspca_dev, 0xc8, 0x50); /* AW_H_STOP = 640 */
++ reg_w1(gspca_dev, 0xc9, 0x3c); /* AW_V_STOP = 480 */
++ }
+ reg_w1(gspca_dev, 0x18, sn9c1xx[0x18]);
+ switch (sd->sensor) {
+ case SENSOR_MT9V111:
+ reg17 = 0xe0;
+ break;
++ case SENSOR_ADCM1700:
+ case SENSOR_OV7630:
+ reg17 = 0xe2;
+ break;
+@@ -1771,56 +2062,66 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ case SENSOR_OV7660:
+ reg17 = 0xa0;
+ break;
++ case SENSOR_PO1030:
++#if 1
++ reg17 = 0xa0;
++#else
++ reg17 = 0xae;
++#endif
++ break;
+ default:
+ reg17 = 0x60;
+ break;
+ }
+ reg_w1(gspca_dev, 0x17, reg17);
+-/* set reg1 was here */
+- reg_w1(gspca_dev, 0x05, sn9c1xx[5]); /* red */
+- reg_w1(gspca_dev, 0x07, sn9c1xx[7]); /* green */
+- reg_w1(gspca_dev, 0x06, sn9c1xx[6]); /* blue */
++
++ reg_w1(gspca_dev, 0x05, 0x00); /* red */
++ reg_w1(gspca_dev, 0x07, 0x00); /* green */
++ reg_w1(gspca_dev, 0x06, 0x00); /* blue */
+ reg_w1(gspca_dev, 0x14, sn9c1xx[0x14]);
+
+ setgamma(gspca_dev);
+
++/*fixme: 8 times with all zeroes and 1 or 2 times with normal values */
+ for (i = 0; i < 8; i++)
+ reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
+ switch (sd->sensor) {
++ case SENSOR_ADCM1700:
++ case SENSOR_OV7660:
++ case SENSOR_SP80708:
++ reg_w1(gspca_dev, 0x9a, 0x05);
++ break;
+ case SENSOR_MT9V111:
+ reg_w1(gspca_dev, 0x9a, 0x07);
+- reg_w1(gspca_dev, 0x99, 0x59);
+ break;
+ case SENSOR_OV7648:
+ reg_w1(gspca_dev, 0x9a, 0x0a);
+- reg_w1(gspca_dev, 0x99, 0x60);
+- break;
+- case SENSOR_OV7660:
+- case SENSOR_SP80708:
+- reg_w1(gspca_dev, 0x9a, 0x05);
+- reg_w1(gspca_dev, 0x99, 0x59);
+ break;
+ default:
+ reg_w1(gspca_dev, 0x9a, 0x08);
+- reg_w1(gspca_dev, 0x99, 0x59);
+ break;
+ }
++ setsharpness(sd);
+
+- mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
++ reg_w(gspca_dev, 0x84, reg84, sizeof reg84);
++ reg_w1(gspca_dev, 0x05, 0x20); /* red */
++ reg_w1(gspca_dev, 0x07, 0x20); /* green */
++ reg_w1(gspca_dev, 0x06, 0x20); /* blue */
++
++ init = NULL;
++ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ if (mode)
+ reg1 = 0x46; /* 320x240: clk 48Mhz, video trf enable */
+ else
+ reg1 = 0x06; /* 640x480: clk 24Mhz, video trf enable */
+ reg17 = 0x61; /* 0x:20: enable sensor clock */
+ switch (sd->sensor) {
+- case SENSOR_HV7131R:
+- hv7131R_InitSensor(gspca_dev);
+- break;
+- case SENSOR_MI0360:
+- mi0360_InitSensor(gspca_dev);
++ case SENSOR_ADCM1700:
++ init = adcm1700_sensor_param1;
++ reg1 = 0x46;
++ reg17 = 0xe2;
+ break;
+ case SENSOR_MO4000:
+- mo4000_InitSensor(gspca_dev);
+ if (mode) {
+ /* reg1 = 0x46; * 320 clk 48Mhz 60fp/s */
+ reg1 = 0x06; /* clk 24Mz */
+@@ -1830,7 +2131,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ }
+ break;
+ case SENSOR_MT9V111:
+- mt9v111_InitSensor(gspca_dev);
++ init = mt9v111_sensor_param1;
+ if (mode) {
+ reg1 = 0x04; /* 320 clk 48Mhz */
+ } else {
+@@ -1839,22 +2140,21 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ }
+ break;
+ case SENSOR_OM6802:
+- om6802_InitSensor(gspca_dev);
++ init = om6802_sensor_param1;
+ reg17 = 0x64; /* 640 MCKSIZE */
+ break;
+ case SENSOR_OV7630:
+- ov7630_InitSensor(gspca_dev);
+ setvflip(sd);
+ reg17 = 0xe2;
+ reg1 = 0x44;
+ break;
+ case SENSOR_OV7648:
+- ov7648_InitSensor(gspca_dev);
++ init = ov7648_sensor_param1;
+ reg17 = 0x21;
+ /* reg1 = 0x42; * 42 - 46? */
+ break;
+ case SENSOR_OV7660:
+- ov7660_InitSensor(gspca_dev);
++ init = ov7660_sensor_param1;
+ if (sd->bridge == BRIDGE_SN9C120) {
+ if (mode) { /* 320x240 - 160x120 */
+ reg17 = 0xa2;
+@@ -1866,9 +2166,14 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ * inverse power down */
+ }
+ break;
++ case SENSOR_PO1030:
++ init = po1030_sensor_param1;
++ reg17 = 0xa2;
++ reg1 = 0x44;
++ break;
+ default:
+ /* case SENSOR_SP80708: */
+- sp80708_InitSensor(gspca_dev);
++ init = sp80708_sensor_param1;
+ if (mode) {
+ /*?? reg1 = 0x04; * 320 clk 48Mhz */
+ } else {
+@@ -1877,9 +2182,20 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ }
+ break;
+ }
++
++ /* more sensor initialization - param1 */
++ if (init != NULL) {
++ i2c_w_seq(gspca_dev, init);
++/* init = NULL; */
++ }
++
+ reg_w(gspca_dev, 0xc0, C0, 6);
+- reg_w(gspca_dev, 0xca, CA, 4);
++ if (sd->sensor == SENSOR_ADCM1700)
++ reg_w(gspca_dev, 0xca, CA_adcm1700, 4);
++ else
++ reg_w(gspca_dev, 0xca, CA, 4);
+ switch (sd->sensor) {
++ case SENSOR_ADCM1700:
+ case SENSOR_OV7630:
+ case SENSOR_OV7648:
+ case SENSOR_OV7660:
+@@ -1937,14 +2253,11 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
+ /* fall thru */
+ case SENSOR_MT9V111:
+ case SENSOR_OV7630:
++ case SENSOR_PO1030:
+ data = 0x29;
+ break;
+- default:
+-/* case SENSOR_MO4000: */
+-/* case SENSOR_OV7660: */
+- break;
+ }
+- sn9c1xx = sn_tb[(int) sd->sensor];
++ sn9c1xx = sn_tb[sd->sensor];
+ reg_w1(gspca_dev, 0x01, sn9c1xx[1]);
+ reg_w1(gspca_dev, 0x17, sn9c1xx[0x17]);
+ reg_w1(gspca_dev, 0x01, sn9c1xx[1]);
+@@ -1987,11 +2300,19 @@ static void do_autogain(struct gspca_dev *gspca_dev)
+ sd->exposure = setexposure(gspca_dev,
+ (unsigned int) (expotimes << 8));
+ break;
++ case SENSOR_OM6802:
++ expotimes = sd->exposure;
++ expotimes += (luma_mean - delta) >> 2;
++ if (expotimes < 0)
++ expotimes = 0;
++ sd->exposure = setexposure(gspca_dev,
++ (unsigned int) expotimes);
++ setredblue(gspca_dev);
++ break;
+ default:
+ /* case SENSOR_MO4000: */
+ /* case SENSOR_MI0360: */
+ /* case SENSOR_MT9V111: */
+-/* case SENSOR_OM6802: */
+ expotimes = sd->exposure;
+ expotimes += (luma_mean - delta) >> 6;
+ if (expotimes < 0)
+@@ -2007,7 +2328,6 @@ static void do_autogain(struct gspca_dev *gspca_dev)
+ /* scan the URB packets */
+ /* This function is run at interrupt level. */
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+@@ -2019,7 +2339,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+
+ /* end of frame */
+ gspca_frame_add(gspca_dev, LAST_PACKET,
+- frame, data, sof + 2);
++ data, sof + 2);
+ if (sd->ag_cnt < 0)
+ return;
+ /* w1 w2 w3 */
+@@ -2042,10 +2362,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ if (gspca_dev->last_packet_type == LAST_PACKET) {
+
+ /* put the JPEG 422 header */
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ }
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+@@ -2174,6 +2494,24 @@ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
+ return 0;
+ }
+
++static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->sharpness = val;
++ if (gspca_dev->streaming)
++ setsharpness(sd);
++ return 0;
++}
++
++static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->sharpness;
++ return 0;
++}
++
+ static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -2295,69 +2633,75 @@ static const struct sd_desc sd_desc = {
+ };
+
+ /* -- module initialisation -- */
+-#define BSI(bridge, sensor, i2c_addr) \
++#define BS(bridge, sensor) \
+ .driver_info = (BRIDGE_ ## bridge << 16) \
+- | (SENSOR_ ## sensor << 8) \
+- | (i2c_addr)
++ | SENSOR_ ## sensor
+ static const __devinitdata struct usb_device_id device_table[] = {
+ #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
+- {USB_DEVICE(0x0458, 0x7025), BSI(SN9C120, MI0360, 0x5d)},
+- {USB_DEVICE(0x0458, 0x702e), BSI(SN9C120, OV7660, 0x21)},
++ {USB_DEVICE(0x0458, 0x7025), BS(SN9C120, MI0360)},
++ {USB_DEVICE(0x0458, 0x702e), BS(SN9C120, OV7660)},
++#endif
++ {USB_DEVICE(0x045e, 0x00f5), BS(SN9C105, OV7660)},
++ {USB_DEVICE(0x045e, 0x00f7), BS(SN9C105, OV7660)},
++ {USB_DEVICE(0x0471, 0x0327), BS(SN9C105, MI0360)},
++ {USB_DEVICE(0x0471, 0x0328), BS(SN9C105, MI0360)},
++ {USB_DEVICE(0x0471, 0x0330), BS(SN9C105, MI0360)},
++ {USB_DEVICE(0x06f8, 0x3004), BS(SN9C105, OV7660)},
++ {USB_DEVICE(0x06f8, 0x3008), BS(SN9C105, OV7660)},
++/* {USB_DEVICE(0x0c45, 0x603a), BS(SN9C102P, OV7648)}, */
++#if 1
++ {USB_DEVICE(0x0c45, 0x6040), BS(SN9C102P, HV7131R)},
++#else
++/* snpstd3.inf */
++ {USB_DEVICE(0x0c45, 0x6040), BS(SN9C102P, MI0360)},
+ #endif
+- {USB_DEVICE(0x045e, 0x00f5), BSI(SN9C105, OV7660, 0x21)},
+- {USB_DEVICE(0x045e, 0x00f7), BSI(SN9C105, OV7660, 0x21)},
+- {USB_DEVICE(0x0471, 0x0327), BSI(SN9C105, MI0360, 0x5d)},
+- {USB_DEVICE(0x0471, 0x0328), BSI(SN9C105, MI0360, 0x5d)},
+- {USB_DEVICE(0x0471, 0x0330), BSI(SN9C105, MI0360, 0x5d)},
+- {USB_DEVICE(0x06f8, 0x3004), BSI(SN9C105, OV7660, 0x21)},
+- {USB_DEVICE(0x06f8, 0x3008), BSI(SN9C105, OV7660, 0x21)},
+- {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, HV7131R, 0x11)},
+-/* bw600.inf:
+- {USB_DEVICE(0x0c45, 0x6040), BSI(SN9C102P, MI0360, 0x5d)}, */
+-/* {USB_DEVICE(0x0c45, 0x603a), BSI(SN9C102P, OV7648, 0x??)}, */
+-/* {USB_DEVICE(0x0c45, 0x607a), BSI(SN9C102P, OV7648, 0x??)}, */
+- {USB_DEVICE(0x0c45, 0x607c), BSI(SN9C102P, HV7131R, 0x11)},
+-/* {USB_DEVICE(0x0c45, 0x607e), BSI(SN9C102P, OV7630, 0x??)}, */
+- {USB_DEVICE(0x0c45, 0x60c0), BSI(SN9C105, MI0360, 0x5d)},
+-/* {USB_DEVICE(0x0c45, 0x60c8), BSI(SN9C105, OM6802, 0x??)}, */
+-/* {USB_DEVICE(0x0c45, 0x60cc), BSI(SN9C105, HV7131GP, 0x??)}, */
+- {USB_DEVICE(0x0c45, 0x60ec), BSI(SN9C105, MO4000, 0x21)},
+-/* {USB_DEVICE(0x0c45, 0x60ef), BSI(SN9C105, ICM105C, 0x??)}, */
+-/* {USB_DEVICE(0x0c45, 0x60fa), BSI(SN9C105, OV7648, 0x??)}, */
+- {USB_DEVICE(0x0c45, 0x60fb), BSI(SN9C105, OV7660, 0x21)},
++/* {USB_DEVICE(0x0c45, 0x607a), BS(SN9C102P, OV7648)}, */
++/* {USB_DEVICE(0x0c45, 0x607b), BS(SN9C102P, OV7660)}, */
++ {USB_DEVICE(0x0c45, 0x607c), BS(SN9C102P, HV7131R)},
++/* {USB_DEVICE(0x0c45, 0x607e), BS(SN9C102P, OV7630)}, */
++ {USB_DEVICE(0x0c45, 0x60c0), BS(SN9C105, MI0360)},
++/* {USB_DEVICE(0x0c45, 0x60c2), BS(SN9C105, P1030xC)}, */
++/* {USB_DEVICE(0x0c45, 0x60c8), BS(SN9C105, OM6802)}, */
++/* {USB_DEVICE(0x0c45, 0x60cc), BS(SN9C105, HV7131GP)}, */
++ {USB_DEVICE(0x0c45, 0x60ec), BS(SN9C105, MO4000)},
++/* {USB_DEVICE(0x0c45, 0x60ef), BS(SN9C105, ICM105C)}, */
++/* {USB_DEVICE(0x0c45, 0x60fa), BS(SN9C105, OV7648)}, */
++ {USB_DEVICE(0x0c45, 0x60fb), BS(SN9C105, OV7660)},
+ #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
+- {USB_DEVICE(0x0c45, 0x60fc), BSI(SN9C105, HV7131R, 0x11)},
+- {USB_DEVICE(0x0c45, 0x60fe), BSI(SN9C105, OV7630, 0x21)},
++ {USB_DEVICE(0x0c45, 0x60fc), BS(SN9C105, HV7131R)},
++ {USB_DEVICE(0x0c45, 0x60fe), BS(SN9C105, OV7630)},
+ #endif
+- {USB_DEVICE(0x0c45, 0x6100), BSI(SN9C120, MI0360, 0x5d)}, /*sn9c128*/
+-/* {USB_DEVICE(0x0c45, 0x6102), BSI(SN9C120, PO2030N, ??)}, */
+-/* {USB_DEVICE(0x0c45, 0x6108), BSI(SN9C120, OM6802, 0x21)}, */
+- {USB_DEVICE(0x0c45, 0x610a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c128*/
+- {USB_DEVICE(0x0c45, 0x610b), BSI(SN9C120, OV7660, 0x21)}, /*sn9c128*/
+- {USB_DEVICE(0x0c45, 0x610c), BSI(SN9C120, HV7131R, 0x11)}, /*sn9c128*/
+- {USB_DEVICE(0x0c45, 0x610e), BSI(SN9C120, OV7630, 0x21)}, /*sn9c128*/
+-/* {USB_DEVICE(0x0c45, 0x6122), BSI(SN9C110, ICM105C, 0x??)}, */
+-/* {USB_DEVICE(0x0c45, 0x6123), BSI(SN9C110, SanyoCCD, 0x??)}, */
+- {USB_DEVICE(0x0c45, 0x6128), BSI(SN9C110, OM6802, 0x21)}, /*sn9c325?*/
++ {USB_DEVICE(0x0c45, 0x6100), BS(SN9C120, MI0360)}, /*sn9c128*/
++/* {USB_DEVICE(0x0c45, 0x6102), BS(SN9C120, P1030xC)}, */
++/* {USB_DEVICE(0x0c45, 0x6108), BS(SN9C120, OM6802)}, */
++ {USB_DEVICE(0x0c45, 0x610a), BS(SN9C120, OV7648)}, /*sn9c128*/
++ {USB_DEVICE(0x0c45, 0x610b), BS(SN9C120, OV7660)}, /*sn9c128*/
++ {USB_DEVICE(0x0c45, 0x610c), BS(SN9C120, HV7131R)}, /*sn9c128*/
++ {USB_DEVICE(0x0c45, 0x610e), BS(SN9C120, OV7630)}, /*sn9c128*/
++/* {USB_DEVICE(0x0c45, 0x610f), BS(SN9C120, S5K53BEB)}, */
++/* {USB_DEVICE(0x0c45, 0x6122), BS(SN9C110, ICM105C)}, */
++/* {USB_DEVICE(0x0c45, 0x6123), BS(SN9C110, SanyoCCD)}, */
++ {USB_DEVICE(0x0c45, 0x6128), BS(SN9C120, OM6802)}, /*sn9c325?*/
+ /*bw600.inf:*/
+- {USB_DEVICE(0x0c45, 0x612a), BSI(SN9C120, OV7648, 0x21)}, /*sn9c110?*/
+- {USB_DEVICE(0x0c45, 0x612c), BSI(SN9C110, MO4000, 0x21)},
+- {USB_DEVICE(0x0c45, 0x612e), BSI(SN9C110, OV7630, 0x21)},
+-/* {USB_DEVICE(0x0c45, 0x612f), BSI(SN9C110, ICM105C, 0x??)}, */
++ {USB_DEVICE(0x0c45, 0x612a), BS(SN9C120, OV7648)}, /*sn9c325?*/
++ {USB_DEVICE(0x0c45, 0x612c), BS(SN9C110, MO4000)},
++ {USB_DEVICE(0x0c45, 0x612e), BS(SN9C110, OV7630)},
++/* {USB_DEVICE(0x0c45, 0x612f), BS(SN9C110, ICM105C)}, */
+ #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
+- {USB_DEVICE(0x0c45, 0x6130), BSI(SN9C120, MI0360, 0x5d)},
++ {USB_DEVICE(0x0c45, 0x6130), BS(SN9C120, MI0360)},
+ #endif
+-/* {USB_DEVICE(0x0c45, 0x6132), BSI(SN9C120, OV7670, 0x21)}, */
+- {USB_DEVICE(0x0c45, 0x6138), BSI(SN9C120, MO4000, 0x21)},
+- {USB_DEVICE(0x0c45, 0x613a), BSI(SN9C120, OV7648, 0x21)},
++/* {USB_DEVICE(0x0c45, 0x6132), BS(SN9C120, OV7670)}, */
++ {USB_DEVICE(0x0c45, 0x6138), BS(SN9C120, MO4000)},
++ {USB_DEVICE(0x0c45, 0x613a), BS(SN9C120, OV7648)},
+ #if !defined CONFIG_USB_SN9C102 && !defined CONFIG_USB_SN9C102_MODULE
+- {USB_DEVICE(0x0c45, 0x613b), BSI(SN9C120, OV7660, 0x21)},
++ {USB_DEVICE(0x0c45, 0x613b), BS(SN9C120, OV7660)},
+ #endif
+- {USB_DEVICE(0x0c45, 0x613c), BSI(SN9C120, HV7131R, 0x11)},
+- {USB_DEVICE(0x0c45, 0x613e), BSI(SN9C120, OV7630, 0x21)},
+-/* {USB_DEVICE(0x0c45, 0x6142), BSI(SN9C120, PO2030N, ??)}, *sn9c120b*/
+- {USB_DEVICE(0x0c45, 0x6143), BSI(SN9C120, SP80708, 0x18)}, /*sn9c120b*/
+- {USB_DEVICE(0x0c45, 0x6148), BSI(SN9C120, OM6802, 0x21)}, /*sn9c120b*/
++ {USB_DEVICE(0x0c45, 0x613c), BS(SN9C120, HV7131R)},
++ {USB_DEVICE(0x0c45, 0x613e), BS(SN9C120, OV7630)},
++/* {USB_DEVICE(0x0c45, 0x6142), BS(SN9C120, PO2030N)}, *sn9c120b*/
++ {USB_DEVICE(0x0c45, 0x6143), BS(SN9C120, SP80708)}, /*sn9c120b*/
++ {USB_DEVICE(0x0c45, 0x6148), BS(SN9C120, OM6802)}, /*sn9c120b*/
++ {USB_DEVICE(0x0c45, 0x614a), BS(SN9C120, ADCM1700)}, /*sn9c120b*/
+ {}
+ };
+ MODULE_DEVICE_TABLE(usb, device_table);
+diff --git a/drivers/media/video/gspca/spca500.c b/drivers/media/video/gspca/spca500.c
+index fab7ef8..e2dcc0d 100644
+--- a/drivers/media/video/gspca/spca500.c
++++ b/drivers/media/video/gspca/spca500.c
+@@ -68,7 +68,7 @@ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+@@ -899,8 +899,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+- __u8 *data, /* isoc packet */
++ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -913,11 +912,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ /* gspca_dev->last_packet_type = DISCARD_PACKET; */
+ return;
+ }
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
++ gspca_frame_add(gspca_dev, LAST_PACKET,
+ ffd9, 2);
+
+ /* put the JPEG header in the new frame */
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+
+ data += SPCA500_OFFSET_DATA;
+@@ -931,7 +930,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ i = 0;
+ do {
+ if (data[i] == 0xff) {
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
++ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, i + 1);
+ len -= i;
+ data += i;
+@@ -940,7 +939,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ }
+ i++;
+ } while (i < len);
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+
+ static void setbrightness(struct gspca_dev *gspca_dev)
+@@ -1048,7 +1047,7 @@ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+ }
+
+ /* sub-driver description */
+-static struct sd_desc sd_desc = {
++static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+diff --git a/drivers/media/video/gspca/spca501.c b/drivers/media/video/gspca/spca501.c
+index b74a342..e68598b 100644
+--- a/drivers/media/video/gspca/spca501.c
++++ b/drivers/media/video/gspca/spca501.c
+@@ -59,7 +59,7 @@ static int sd_getblue_balance(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setred_balance(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getred_balance(struct gspca_dev *gspca_dev, __s32 *val);
+
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
+ #define MY_BRIGHTNESS 0
+ {
+ {
+@@ -2032,20 +2032,15 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+- __u8 *data, /* isoc packet */
++ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ switch (data[0]) {
+ case 0: /* start of frame */
+- frame = gspca_frame_add(gspca_dev,
+- LAST_PACKET,
+- frame,
+- data, 0);
++ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ data += SPCA501_OFFSET_DATA;
+ len -= SPCA501_OFFSET_DATA;
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+- data, len);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ return;
+ case 0xff: /* drop */
+ /* gspca_dev->last_packet_type = DISCARD_PACKET; */
+@@ -2053,8 +2048,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ }
+ data++;
+ len--;
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+- data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+diff --git a/drivers/media/video/gspca/spca505.c b/drivers/media/video/gspca/spca505.c
+index ea8c9fe..c576eed 100644
+--- a/drivers/media/video/gspca/spca505.c
++++ b/drivers/media/video/gspca/spca505.c
+@@ -42,7 +42,7 @@ struct sd {
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+@@ -739,26 +739,22 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ switch (data[0]) {
+ case 0: /* start of frame */
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+- data, 0);
++ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ data += SPCA50X_OFFSET_DATA;
+ len -= SPCA50X_OFFSET_DATA;
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+- data, len);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ break;
+ case 0xff: /* drop */
+ break;
+ default:
+ data += 1;
+ len -= 1;
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+- data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ break;
+ }
+ }
+diff --git a/drivers/media/video/gspca/spca506.c b/drivers/media/video/gspca/spca506.c
+index a199298..89fec4c 100644
+--- a/drivers/media/video/gspca/spca506.c
++++ b/drivers/media/video/gspca/spca506.c
+@@ -51,7 +51,7 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_sethue(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val);
+
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
+ #define SD_BRIGHTNESS 0
+ {
+ {
+@@ -543,18 +543,15 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+- __u8 *data, /* isoc packet */
++ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ switch (data[0]) {
+ case 0: /* start of frame */
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+- data, 0);
++ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ data += SPCA50X_OFFSET_DATA;
+ len -= SPCA50X_OFFSET_DATA;
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+- data, len);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ break;
+ case 0xff: /* drop */
+ /* gspca_dev->last_packet_type = DISCARD_PACKET; */
+@@ -562,8 +559,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ default:
+ data += 1;
+ len -= 1;
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+- data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ break;
+ }
+ }
+@@ -677,7 +673,7 @@ static int sd_gethue(struct gspca_dev *gspca_dev, __s32 *val)
+ }
+
+ /* sub-driver description */
+-static struct sd_desc sd_desc = {
++static const struct sd_desc sd_desc = {
+ .name = MODULE_NAME,
+ .ctrls = sd_ctrls,
+ .nctrls = ARRAY_SIZE(sd_ctrls),
+@@ -689,7 +685,7 @@ static struct sd_desc sd_desc = {
+ };
+
+ /* -- module initialisation -- */
+-static __devinitdata struct usb_device_id device_table[] = {
++static const struct usb_device_id device_table[] __devinitconst = {
+ {USB_DEVICE(0x06e1, 0xa190)},
+ /*fixme: may be IntelPCCameraPro BRIDGE_SPCA505
+ {USB_DEVICE(0x0733, 0x0430)}, */
+@@ -700,7 +696,7 @@ static __devinitdata struct usb_device_id device_table[] = {
+ MODULE_DEVICE_TABLE(usb, device_table);
+
+ /* -- device connect -- */
+-static int sd_probe(struct usb_interface *intf,
++static int __devinit sd_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+ {
+ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
+diff --git a/drivers/media/video/gspca/spca508.c b/drivers/media/video/gspca/spca508.c
+index 9696c4c..15b2eef 100644
+--- a/drivers/media/video/gspca/spca508.c
++++ b/drivers/media/video/gspca/spca508.c
+@@ -45,7 +45,7 @@ struct sd {
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+@@ -1447,26 +1447,22 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ switch (data[0]) {
+ case 0: /* start of frame */
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+- data, 0);
++ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ data += SPCA508_OFFSET_DATA;
+ len -= SPCA508_OFFSET_DATA;
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+- data, len);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ break;
+ case 0xff: /* drop */
+ break;
+ default:
+ data += 1;
+ len -= 1;
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
+- data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ break;
+ }
+ }
+diff --git a/drivers/media/video/gspca/spca561.c b/drivers/media/video/gspca/spca561.c
+index 27e82b3..dc7f2b0 100644
+--- a/drivers/media/video/gspca/spca561.c
++++ b/drivers/media/video/gspca/spca561.c
+@@ -779,8 +779,7 @@ static void do_autogain(struct gspca_dev *gspca_dev)
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+- __u8 *data, /* isoc packet */
++ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -788,12 +787,10 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ len--;
+ switch (*data++) { /* sequence number */
+ case 0: /* start of frame */
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+- data, 0);
++ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ if (data[1] & 0x10) {
+ /* compressed bayer */
+- gspca_frame_add(gspca_dev, FIRST_PACKET,
+- frame, data, len);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ } else {
+ /* raw bayer (with a header, which we skip) */
+ if (sd->chip_revision == Rev012A) {
+@@ -803,14 +800,13 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ data += 16;
+ len -= 16;
+ }
+- gspca_frame_add(gspca_dev, FIRST_PACKET,
+- frame, data, len);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ }
+ return;
+ case 0xff: /* drop (empty mpackets) */
+ return;
+ }
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+
+ /* rev 72a only */
+@@ -926,7 +922,7 @@ static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
+ }
+
+ /* control tables */
+-static struct ctrl sd_ctrls_12a[] = {
++static const struct ctrl sd_ctrls_12a[] = {
+ {
+ {
+ .id = V4L2_CID_HUE,
+@@ -968,7 +964,7 @@ static struct ctrl sd_ctrls_12a[] = {
+ },
+ };
+
+-static struct ctrl sd_ctrls_72a[] = {
++static const struct ctrl sd_ctrls_72a[] = {
+ {
+ {
+ .id = V4L2_CID_HUE,
+diff --git a/drivers/media/video/gspca/sq905.c b/drivers/media/video/gspca/sq905.c
+index 715a68f..1fcaca6 100644
+--- a/drivers/media/video/gspca/sq905.c
++++ b/drivers/media/video/gspca/sq905.c
+@@ -168,18 +168,22 @@ static int sq905_ack_frame(struct gspca_dev *gspca_dev)
+ * request and read a block of data - see warning on sq905_command.
+ */
+ static int
+-sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size)
++sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock)
+ {
+ int ret;
+ int act_len;
+
+ gspca_dev->usb_buf[0] = '\0';
++ if (need_lock)
++ mutex_lock(&gspca_dev->usb_lock);
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ USB_REQ_SYNCH_FRAME, /* request */
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ SQ905_BULK_READ, size, gspca_dev->usb_buf,
+ 1, SQ905_CMD_TIMEOUT);
++ if (need_lock)
++ mutex_unlock(&gspca_dev->usb_lock);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)", __func__, ret);
+ return ret;
+@@ -210,11 +214,9 @@ static void sq905_dostream(struct work_struct *work)
+ {
+ struct sd *dev = container_of(work, struct sd, work_struct);
+ struct gspca_dev *gspca_dev = &dev->gspca_dev;
+- struct gspca_frame *frame;
+ int bytes_left; /* bytes remaining in current frame. */
+ int data_len; /* size to use for the next read. */
+ int header_read; /* true if we have already read the frame header. */
+- int discarding; /* true if we failed to get space for frame. */
+ int packet_type;
+ int frame_sz;
+ int ret;
+@@ -222,7 +224,6 @@ static void sq905_dostream(struct work_struct *work)
+ u8 *buffer;
+
+ buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL | GFP_DMA);
+- mutex_lock(&gspca_dev->usb_lock);
+ if (!buffer) {
+ PDEBUG(D_ERR, "Couldn't allocate USB buffer");
+ goto quit_stream;
+@@ -232,28 +233,22 @@ static void sq905_dostream(struct work_struct *work)
+ + FRAME_HEADER_LEN;
+
+ while (gspca_dev->present && gspca_dev->streaming) {
+- /* Need a short delay to ensure streaming flag was set by
+- * gspca and to make sure gspca can grab the mutex. */
+- mutex_unlock(&gspca_dev->usb_lock);
+- msleep(1);
+-
+ /* request some data and then read it until we have
+ * a complete frame. */
+ bytes_left = frame_sz;
+ header_read = 0;
+- discarding = 0;
+
+- while (bytes_left > 0) {
++ /* Note we do not check for gspca_dev->streaming here, as
++ we must finish reading an entire frame, otherwise the
++ next time we stream we start reading in the middle of a
++ frame. */
++ while (bytes_left > 0 && gspca_dev->present) {
+ data_len = bytes_left > SQ905_MAX_TRANSFER ?
+ SQ905_MAX_TRANSFER : bytes_left;
+- mutex_lock(&gspca_dev->usb_lock);
+- if (!gspca_dev->present)
+- goto quit_stream;
+- ret = sq905_read_data(gspca_dev, buffer, data_len);
++ ret = sq905_read_data(gspca_dev, buffer, data_len, 1);
+ if (ret < 0)
+ goto quit_stream;
+- mutex_unlock(&gspca_dev->usb_lock);
+- PDEBUG(D_STREAM,
++ PDEBUG(D_PACK,
+ "Got %d bytes out of %d for frame",
+ data_len, bytes_left);
+ bytes_left -= data_len;
+@@ -270,34 +265,30 @@ static void sq905_dostream(struct work_struct *work)
+ } else {
+ packet_type = INTER_PACKET;
+ }
+- frame = gspca_get_i_frame(gspca_dev);
+- if (frame && !discarding) {
+- frame = gspca_frame_add(gspca_dev, packet_type,
+- frame, data, data_len);
+- /* If entire frame fits in one packet we still
+- need to add a LAST_PACKET */
+- if (packet_type == FIRST_PACKET &&
+- bytes_left == 0)
+- frame = gspca_frame_add(gspca_dev,
+- LAST_PACKET,
+- frame, data, 0);
+- } else {
+- discarding = 1;
+- }
++ gspca_frame_add(gspca_dev, packet_type,
++ data, data_len);
++ /* If entire frame fits in one packet we still
++ need to add a LAST_PACKET */
++ if (packet_type == FIRST_PACKET &&
++ bytes_left == 0)
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ NULL, 0);
++ }
++ if (gspca_dev->present) {
++ /* acknowledge the frame */
++ mutex_lock(&gspca_dev->usb_lock);
++ ret = sq905_ack_frame(gspca_dev);
++ mutex_unlock(&gspca_dev->usb_lock);
++ if (ret < 0)
++ goto quit_stream;
+ }
+- /* acknowledge the frame */
+- mutex_lock(&gspca_dev->usb_lock);
+- if (!gspca_dev->present)
+- goto quit_stream;
+- ret = sq905_ack_frame(gspca_dev);
+- if (ret < 0)
+- goto quit_stream;
+ }
+ quit_stream:
+- /* the usb_lock is already acquired */
+- if (gspca_dev->present)
++ if (gspca_dev->present) {
++ mutex_lock(&gspca_dev->usb_lock);
+ sq905_command(gspca_dev, SQ905_CLEAR);
+- mutex_unlock(&gspca_dev->usb_lock);
++ mutex_unlock(&gspca_dev->usb_lock);
++ }
+ kfree(buffer);
+ }
+
+@@ -346,7 +337,7 @@ static int sd_init(struct gspca_dev *gspca_dev)
+ ret = sq905_command(gspca_dev, SQ905_ID);
+ if (ret < 0)
+ return ret;
+- ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4);
++ ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4, 0);
+ if (ret < 0)
+ return ret;
+ /* usb_buf is allocated with kmalloc so is aligned.
+diff --git a/drivers/media/video/gspca/sq905c.c b/drivers/media/video/gspca/sq905c.c
+index 9168925..e646620 100644
+--- a/drivers/media/video/gspca/sq905c.c
++++ b/drivers/media/video/gspca/sq905c.c
+@@ -47,6 +47,7 @@ MODULE_LICENSE("GPL");
+
+ /* Commands. These go in the "value" slot. */
+ #define SQ905C_CLEAR 0xa0 /* clear everything */
++#define SQ905C_GET_ID 0x14f4 /* Read version number */
+ #define SQ905C_CAPTURE_LOW 0xa040 /* Starts capture at 160x120 */
+ #define SQ905C_CAPTURE_MED 0x1440 /* Starts capture at 320x240 */
+ #define SQ905C_CAPTURE_HI 0x2840 /* Starts capture at 320x240 */
+@@ -101,6 +102,26 @@ static int sq905c_command(struct gspca_dev *gspca_dev, u16 command, u16 index)
+ return 0;
+ }
+
++static int sq905c_read(struct gspca_dev *gspca_dev, u16 command, u16 index,
++ int size)
++{
++ int ret;
++
++ ret = usb_control_msg(gspca_dev->dev,
++ usb_rcvctrlpipe(gspca_dev->dev, 0),
++ USB_REQ_SYNCH_FRAME, /* request */
++ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ command, index, gspca_dev->usb_buf, size,
++ SQ905C_CMD_TIMEOUT);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "%s: usb_control_msg failed (%d)",
++ __func__, ret);
++ return ret;
++ }
++
++ return 0;
++}
++
+ /* This function is called as a workqueue function and runs whenever the camera
+ * is streaming data. Because it is a workqueue function it is allowed to sleep
+ * so we can use synchronous USB calls. To avoid possible collisions with other
+@@ -115,11 +136,9 @@ static void sq905c_dostream(struct work_struct *work)
+ {
+ struct sd *dev = container_of(work, struct sd, work_struct);
+ struct gspca_dev *gspca_dev = &dev->gspca_dev;
+- struct gspca_frame *frame;
+ int bytes_left; /* bytes remaining in current frame. */
+ int data_len; /* size to use for the next read. */
+ int act_len;
+- int discarding = 0; /* true if we failed to get space for frame. */
+ int packet_type;
+ int ret;
+ u8 *buffer;
+@@ -131,8 +150,6 @@ static void sq905c_dostream(struct work_struct *work)
+ }
+
+ while (gspca_dev->present && gspca_dev->streaming) {
+- if (!gspca_dev->present)
+- goto quit_stream;
+ /* Request the header, which tells the size to download */
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x81),
+@@ -149,17 +166,11 @@ static void sq905c_dostream(struct work_struct *work)
+ PDEBUG(D_STREAM, "bytes_left = 0x%x", bytes_left);
+ /* We keep the header. It has other information, too. */
+ packet_type = FIRST_PACKET;
+- frame = gspca_get_i_frame(gspca_dev);
+- if (frame && !discarding) {
+- gspca_frame_add(gspca_dev, packet_type,
+- frame, buffer, FRAME_HEADER_LEN);
+- } else
+- discarding = 1;
+- while (bytes_left > 0) {
++ gspca_frame_add(gspca_dev, packet_type,
++ buffer, FRAME_HEADER_LEN);
++ while (bytes_left > 0 && gspca_dev->present) {
+ data_len = bytes_left > SQ905C_MAX_TRANSFER ?
+ SQ905C_MAX_TRANSFER : bytes_left;
+- if (!gspca_dev->present)
+- goto quit_stream;
+ ret = usb_bulk_msg(gspca_dev->dev,
+ usb_rcvbulkpipe(gspca_dev->dev, 0x81),
+ buffer, data_len, &act_len,
+@@ -174,19 +185,16 @@ static void sq905c_dostream(struct work_struct *work)
+ packet_type = LAST_PACKET;
+ else
+ packet_type = INTER_PACKET;
+- frame = gspca_get_i_frame(gspca_dev);
+- if (frame && !discarding)
+- gspca_frame_add(gspca_dev, packet_type,
+- frame, buffer, data_len);
+- else
+- discarding = 1;
++ gspca_frame_add(gspca_dev, packet_type,
++ buffer, data_len);
+ }
+ }
+ quit_stream:
+- mutex_lock(&gspca_dev->usb_lock);
+- if (gspca_dev->present)
++ if (gspca_dev->present) {
++ mutex_lock(&gspca_dev->usb_lock);
+ sq905c_command(gspca_dev, SQ905C_CLEAR, 0);
+- mutex_unlock(&gspca_dev->usb_lock);
++ mutex_unlock(&gspca_dev->usb_lock);
++ }
+ kfree(buffer);
+ }
+
+@@ -196,13 +204,34 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ {
+ struct cam *cam = &gspca_dev->cam;
+ struct sd *dev = (struct sd *) gspca_dev;
++ int ret;
+
+ PDEBUG(D_PROBE,
+ "SQ9050 camera detected"
+ " (vid/pid 0x%04X:0x%04X)", id->idVendor, id->idProduct);
++
++ ret = sq905c_command(gspca_dev, SQ905C_GET_ID, 0);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "Get version command failed");
++ return ret;
++ }
++
++ ret = sq905c_read(gspca_dev, 0xf5, 0, 20);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "Reading version command failed");
++ return ret;
++ }
++ /* Note we leave out the usb id and the manufacturing date */
++ PDEBUG(D_PROBE,
++ "SQ9050 ID string: %02x - %02x %02x %02x %02x %02x %02x",
++ gspca_dev->usb_buf[3],
++ gspca_dev->usb_buf[14], gspca_dev->usb_buf[15],
++ gspca_dev->usb_buf[16], gspca_dev->usb_buf[17],
++ gspca_dev->usb_buf[18], gspca_dev->usb_buf[19]);
++
+ cam->cam_mode = sq905c_mode;
+ cam->nmodes = 2;
+- if (id->idProduct == 0x9050)
++ if (gspca_dev->usb_buf[15] == 0)
+ cam->nmodes = 1;
+ /* We don't use the buffer gspca allocates so make it small. */
+ cam->bulk_size = 32;
+@@ -271,6 +300,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x2770, 0x905c)},
+ {USB_DEVICE(0x2770, 0x9050)},
++ {USB_DEVICE(0x2770, 0x9052)},
+ {USB_DEVICE(0x2770, 0x913d)},
+ {}
+ };
+diff --git a/drivers/media/video/gspca/stk014.c b/drivers/media/video/gspca/stk014.c
+index 4762896..0fb5342 100644
+--- a/drivers/media/video/gspca/stk014.c
++++ b/drivers/media/video/gspca/stk014.c
+@@ -53,7 +53,7 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+@@ -126,12 +126,14 @@ static const struct v4l2_pix_format vga_mode[] = {
+ };
+
+ /* -- read a register -- */
+-static int reg_r(struct gspca_dev *gspca_dev,
++static u8 reg_r(struct gspca_dev *gspca_dev,
+ __u16 index)
+ {
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
++ if (gspca_dev->usb_err < 0)
++ return 0;
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
+ 0x00,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+@@ -141,18 +143,21 @@ static int reg_r(struct gspca_dev *gspca_dev,
+ 500);
+ if (ret < 0) {
+ PDEBUG(D_ERR, "reg_r err %d", ret);
+- return ret;
++ gspca_dev->usb_err = ret;
++ return 0;
+ }
+ return gspca_dev->usb_buf[0];
+ }
+
+ /* -- write a register -- */
+-static int reg_w(struct gspca_dev *gspca_dev,
++static void reg_w(struct gspca_dev *gspca_dev,
+ __u16 index, __u16 value)
+ {
+ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
++ if (gspca_dev->usb_err < 0)
++ return;
+ ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ 0x01,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+@@ -161,13 +166,14 @@ static int reg_w(struct gspca_dev *gspca_dev,
+ NULL,
+ 0,
+ 500);
+- if (ret < 0)
++ if (ret < 0) {
+ PDEBUG(D_ERR, "reg_w err %d", ret);
+- return ret;
++ gspca_dev->usb_err = ret;
++ }
+ }
+
+ /* -- get a bulk value (4 bytes) -- */
+-static int rcv_val(struct gspca_dev *gspca_dev,
++static void rcv_val(struct gspca_dev *gspca_dev,
+ int ads)
+ {
+ struct usb_device *dev = gspca_dev->dev;
+@@ -182,17 +188,22 @@ static int rcv_val(struct gspca_dev *gspca_dev,
+ reg_w(gspca_dev, 0x63a, 0);
+ reg_w(gspca_dev, 0x63b, 0);
+ reg_w(gspca_dev, 0x630, 5);
++ if (gspca_dev->usb_err < 0)
++ return;
+ ret = usb_bulk_msg(dev,
+ usb_rcvbulkpipe(dev, 0x05),
+ gspca_dev->usb_buf,
+ 4, /* length */
+ &alen,
+ 500); /* timeout in milliseconds */
+- return ret;
++ if (ret < 0) {
++ PDEBUG(D_ERR, "rcv_val err %d", ret);
++ gspca_dev->usb_err = ret;
++ }
+ }
+
+ /* -- send a bulk value -- */
+-static int snd_val(struct gspca_dev *gspca_dev,
++static void snd_val(struct gspca_dev *gspca_dev,
+ int ads,
+ unsigned int val)
+ {
+@@ -201,16 +212,9 @@ static int snd_val(struct gspca_dev *gspca_dev,
+ __u8 seq = 0;
+
+ if (ads == 0x003f08) {
+- ret = reg_r(gspca_dev, 0x0704);
+- if (ret < 0)
+- goto ko;
+- ret = reg_r(gspca_dev, 0x0705);
+- if (ret < 0)
+- goto ko;
+- seq = ret; /* keep the sequence number */
+- ret = reg_r(gspca_dev, 0x0650);
+- if (ret < 0)
+- goto ko;
++ reg_r(gspca_dev, 0x0704);
++ seq = reg_r(gspca_dev, 0x0705);
++ reg_r(gspca_dev, 0x0650);
+ reg_w(gspca_dev, 0x654, seq);
+ } else {
+ reg_w(gspca_dev, 0x654, (ads >> 16) & 0xff);
+@@ -223,6 +227,8 @@ static int snd_val(struct gspca_dev *gspca_dev,
+ reg_w(gspca_dev, 0x65a, 0);
+ reg_w(gspca_dev, 0x65b, 0);
+ reg_w(gspca_dev, 0x650, 5);
++ if (gspca_dev->usb_err < 0)
++ return;
+ gspca_dev->usb_buf[0] = val >> 24;
+ gspca_dev->usb_buf[1] = val >> 16;
+ gspca_dev->usb_buf[2] = val >> 8;
+@@ -233,24 +239,23 @@ static int snd_val(struct gspca_dev *gspca_dev,
+ 4,
+ &alen,
+ 500); /* timeout in milliseconds */
+- if (ret < 0)
+- goto ko;
+- if (ads == 0x003f08) {
+- seq += 4;
+- seq &= 0x3f;
+- reg_w(gspca_dev, 0x705, seq);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "snd_val err %d", ret);
++ gspca_dev->usb_err = ret;
++ } else {
++ if (ads == 0x003f08) {
++ seq += 4;
++ seq &= 0x3f;
++ reg_w(gspca_dev, 0x705, seq);
++ }
+ }
+- return ret;
+-ko:
+- PDEBUG(D_ERR, "snd_val err %d", ret);
+- return ret;
+ }
+
+ /* set a camera parameter */
+-static int set_par(struct gspca_dev *gspca_dev,
++static void set_par(struct gspca_dev *gspca_dev,
+ int parval)
+ {
+- return snd_val(gspca_dev, 0x003f08, parval);
++ snd_val(gspca_dev, 0x003f08, parval);
+ }
+
+ static void setbrightness(struct gspca_dev *gspca_dev)
+@@ -311,18 +316,18 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ /* this function is called at probe and resume time */
+ static int sd_init(struct gspca_dev *gspca_dev)
+ {
+- int ret;
++ u8 ret;
+
+ /* check if the device responds */
+ usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
+ ret = reg_r(gspca_dev, 0x0740);
+- if (ret < 0)
+- return ret;
+- if (ret != 0xff) {
+- PDEBUG(D_ERR|D_STREAM, "init reg: 0x%02x", ret);
+- return -1;
++ if (gspca_dev->usb_err >= 0) {
++ if (ret != 0xff) {
++ PDEBUG(D_ERR|D_STREAM, "init reg: 0x%02x", ret);
++ gspca_dev->usb_err = -EIO;
++ }
+ }
+- return 0;
++ return gspca_dev->usb_err;
+ }
+
+ /* -- start the camera -- */
+@@ -357,15 +362,12 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ if (ret < 0) {
+ PDEBUG(D_ERR|D_STREAM, "set intf %d %d failed",
+ gspca_dev->iface, gspca_dev->alt);
++ gspca_dev->usb_err = ret;
+ goto out;
+ }
+- ret = reg_r(gspca_dev, 0x0630);
+- if (ret < 0)
+- goto out;
++ reg_r(gspca_dev, 0x0630);
+ rcv_val(gspca_dev, 0x000020); /* << (value ff ff ff ff) */
+- ret = reg_r(gspca_dev, 0x0650);
+- if (ret < 0)
+- goto out;
++ reg_r(gspca_dev, 0x0650);
+ snd_val(gspca_dev, 0x000020, 0xffffffff);
+ reg_w(gspca_dev, 0x0620, 0);
+ reg_w(gspca_dev, 0x0630, 0);
+@@ -384,11 +386,11 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ /* start the video flow */
+ set_par(gspca_dev, 0x01000000);
+ set_par(gspca_dev, 0x01000000);
+- PDEBUG(D_STREAM, "camera started alt: 0x%02x", gspca_dev->alt);
+- return 0;
++ if (gspca_dev->usb_err >= 0)
++ PDEBUG(D_STREAM, "camera started alt: 0x%02x",
++ gspca_dev->alt);
+ out:
+- PDEBUG(D_ERR|D_STREAM, "camera start err %d", ret);
+- return ret;
++ return gspca_dev->usb_err;
+ }
+
+ static void sd_stopN(struct gspca_dev *gspca_dev)
+@@ -418,8 +420,7 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+- __u8 *data, /* isoc packet */
++ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -435,11 +436,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ * (without ending - ff d9)
+ */
+ if (data[0] == 0xff && data[1] == 0xfe) {
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+- ffd9, 2);
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ ffd9, 2);
+
+ /* put the JPEG 411 header */
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+
+ /* beginning of the frame */
+@@ -447,7 +448,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ data += STKHDRSZ;
+ len -= STKHDRSZ;
+ }
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+@@ -457,7 +458,7 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
+- return 0;
++ return gspca_dev->usb_err;
+ }
+
+ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+@@ -475,7 +476,7 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+ sd->contrast = val;
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
+- return 0;
++ return gspca_dev->usb_err;
+ }
+
+ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+@@ -493,7 +494,7 @@ static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+ sd->colors = val;
+ if (gspca_dev->streaming)
+ setcolors(gspca_dev);
+- return 0;
++ return gspca_dev->usb_err;
+ }
+
+ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+@@ -511,7 +512,7 @@ static int sd_setfreq(struct gspca_dev *gspca_dev, __s32 val)
+ sd->lightfreq = val;
+ if (gspca_dev->streaming)
+ setfreq(gspca_dev);
+- return 0;
++ return gspca_dev->usb_err;
+ }
+
+ static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val)
+@@ -553,7 +554,7 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ sd->quality = jcomp->quality;
+ if (gspca_dev->streaming)
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+- return 0;
++ return gspca_dev->usb_err;
+ }
+
+ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+diff --git a/drivers/media/video/gspca/stv0680.c b/drivers/media/video/gspca/stv0680.c
+new file mode 100644
+index 0000000..c3743c9
+--- /dev/null
++++ b/drivers/media/video/gspca/stv0680.c
+@@ -0,0 +1,394 @@
++/*
++ * STV0680 USB Camera Driver
++ *
++ * Copyright (C) 2009 Hans de Goede <hdgoede@redhat.com>
++ *
++ * This module is adapted from the in kernel v4l1 stv680 driver:
++ *
++ * STV0680 USB Camera Driver, by Kevin Sisson (kjsisson@bellsouth.net)
++ *
++ * Thanks to STMicroelectronics for information on the usb commands, and
++ * to Steve Miller at STM for his help and encouragement while I was
++ * writing this driver.
++ *
++ * 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
++ *
++ */
++
++#define MODULE_NAME "stv0680"
++
++#include "gspca.h"
++
++MODULE_AUTHOR("Hans de Goede <hdgoede@redhat.com>");
++MODULE_DESCRIPTION("STV0680 USB Camera Driver");
++MODULE_LICENSE("GPL");
++
++/* specific webcam descriptor */
++struct sd {
++ struct gspca_dev gspca_dev; /* !! must be the first item */
++ struct v4l2_pix_format mode;
++ u8 orig_mode;
++ u8 video_mode;
++ u8 current_mode;
++};
++
++/* V4L2 controls supported by the driver */
++static const struct ctrl sd_ctrls[] = {
++};
++
++static int stv_sndctrl(struct gspca_dev *gspca_dev, int set, u8 req, u16 val,
++ int size)
++{
++ int ret = -1;
++ u8 req_type = 0;
++ unsigned int pipe = 0;
++
++ switch (set) {
++ case 0: /* 0xc1 */
++ req_type = USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
++ pipe = usb_rcvctrlpipe(gspca_dev->dev, 0);
++ break;
++ case 1: /* 0x41 */
++ req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT;
++ pipe = usb_sndctrlpipe(gspca_dev->dev, 0);
++ break;
++ case 2: /* 0x80 */
++ req_type = USB_DIR_IN | USB_RECIP_DEVICE;
++ pipe = usb_rcvctrlpipe(gspca_dev->dev, 0);
++ break;
++ case 3: /* 0x40 */
++ req_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
++ pipe = usb_sndctrlpipe(gspca_dev->dev, 0);
++ break;
++ }
++
++ ret = usb_control_msg(gspca_dev->dev, pipe,
++ req, req_type,
++ val, 0, gspca_dev->usb_buf, size, 500);
++
++ if ((ret < 0) && (req != 0x0a))
++ PDEBUG(D_ERR,
++ "usb_control_msg error %i, request = 0x%x, error = %i",
++ set, req, ret);
++
++ return ret;
++}
++
++static int stv0680_handle_error(struct gspca_dev *gspca_dev, int ret)
++{
++ stv_sndctrl(gspca_dev, 0, 0x80, 0, 0x02); /* Get Last Error */
++ PDEBUG(D_ERR, "last error: %i, command = 0x%x",
++ gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
++ return ret;
++}
++
++static int stv0680_get_video_mode(struct gspca_dev *gspca_dev)
++{
++ /* Note not sure if this init of usb_buf is really necessary */
++ memset(gspca_dev->usb_buf, 0, 8);
++ gspca_dev->usb_buf[0] = 0x0f;
++
++ if (stv_sndctrl(gspca_dev, 0, 0x87, 0, 0x08) != 0x08) {
++ PDEBUG(D_ERR, "Get_Camera_Mode failed");
++ return stv0680_handle_error(gspca_dev, -EIO);
++ }
++
++ return gspca_dev->usb_buf[0]; /* 01 = VGA, 03 = QVGA, 00 = CIF */
++}
++
++static int stv0680_set_video_mode(struct gspca_dev *gspca_dev, u8 mode)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (sd->current_mode == mode)
++ return 0;
++
++ memset(gspca_dev->usb_buf, 0, 8);
++ gspca_dev->usb_buf[0] = mode;
++
++ if (stv_sndctrl(gspca_dev, 3, 0x07, 0x0100, 0x08) != 0x08) {
++ PDEBUG(D_ERR, "Set_Camera_Mode failed");
++ return stv0680_handle_error(gspca_dev, -EIO);
++ }
++
++ /* Verify we got what we've asked for */
++ if (stv0680_get_video_mode(gspca_dev) != mode) {
++ PDEBUG(D_ERR, "Error setting camera video mode!");
++ return -EIO;
++ }
++
++ sd->current_mode = mode;
++
++ return 0;
++}
++
++/* this function is called at probe time */
++static int sd_config(struct gspca_dev *gspca_dev,
++ const struct usb_device_id *id)
++{
++ int ret;
++ struct sd *sd = (struct sd *) gspca_dev;
++ struct cam *cam = &gspca_dev->cam;
++
++ /* Give the camera some time to settle, otherwise initalization will
++ fail on hotplug, and yes it really needs a full second. */
++ msleep(1000);
++
++ /* ping camera to be sure STV0680 is present */
++ if (stv_sndctrl(gspca_dev, 0, 0x88, 0x5678, 0x02) != 0x02 ||
++ gspca_dev->usb_buf[0] != 0x56 || gspca_dev->usb_buf[1] != 0x78) {
++ PDEBUG(D_ERR, "STV(e): camera ping failed!!");
++ return stv0680_handle_error(gspca_dev, -ENODEV);
++ }
++
++ /* get camera descriptor */
++ if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x09) != 0x09)
++ return stv0680_handle_error(gspca_dev, -ENODEV);
++
++ if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0200, 0x22) != 0x22 ||
++ gspca_dev->usb_buf[7] != 0xa0 || gspca_dev->usb_buf[8] != 0x23) {
++ PDEBUG(D_ERR, "Could not get descriptor 0200.");
++ return stv0680_handle_error(gspca_dev, -ENODEV);
++ }
++ if (stv_sndctrl(gspca_dev, 0, 0x8a, 0, 0x02) != 0x02)
++ return stv0680_handle_error(gspca_dev, -ENODEV);
++ if (stv_sndctrl(gspca_dev, 0, 0x8b, 0, 0x24) != 0x24)
++ return stv0680_handle_error(gspca_dev, -ENODEV);
++ if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10)
++ return stv0680_handle_error(gspca_dev, -ENODEV);
++
++ if (!(gspca_dev->usb_buf[7] & 0x09)) {
++ PDEBUG(D_ERR, "Camera supports neither CIF nor QVGA mode");
++ return -ENODEV;
++ }
++ if (gspca_dev->usb_buf[7] & 0x01)
++ PDEBUG(D_PROBE, "Camera supports CIF mode");
++ if (gspca_dev->usb_buf[7] & 0x02)
++ PDEBUG(D_PROBE, "Camera supports VGA mode");
++ if (gspca_dev->usb_buf[7] & 0x04)
++ PDEBUG(D_PROBE, "Camera supports QCIF mode");
++ if (gspca_dev->usb_buf[7] & 0x08)
++ PDEBUG(D_PROBE, "Camera supports QVGA mode");
++
++ if (gspca_dev->usb_buf[7] & 0x01)
++ sd->video_mode = 0x00; /* CIF */
++ else
++ sd->video_mode = 0x03; /* QVGA */
++
++ /* FW rev, ASIC rev, sensor ID */
++ PDEBUG(D_PROBE, "Firmware rev is %i.%i",
++ gspca_dev->usb_buf[0], gspca_dev->usb_buf[1]);
++ PDEBUG(D_PROBE, "ASIC rev is %i.%i",
++ gspca_dev->usb_buf[2], gspca_dev->usb_buf[3]);
++ PDEBUG(D_PROBE, "Sensor ID is %i",
++ (gspca_dev->usb_buf[4]*16) + (gspca_dev->usb_buf[5]>>4));
++
++#if 0 /* The v4l1 driver used to this but I don't think it is necessary */
++ ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 1);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "Set alt 1 failed (%d)", ret);
++ return ret;
++ }
++
++ if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10)
++ return stv0680_handle_error(gspca_dev, -EIO);
++ if (stv_sndctrl(gspca_dev, 0, 0x8d, 0, 0x08) != 0x08)
++ return stv0680_handle_error(gspca_dev, -EIO);
++ PDEBUG(D_PROBE, "Camera has %i pictures.", gspca_dev->usb_buf[3]);
++#endif
++
++ ret = stv0680_get_video_mode(gspca_dev);
++ if (ret < 0)
++ return ret;
++ sd->current_mode = sd->orig_mode = ret;
++
++ ret = stv0680_set_video_mode(gspca_dev, sd->video_mode);
++ if (ret < 0)
++ return ret;
++
++ /* Get mode details */
++ if (stv_sndctrl(gspca_dev, 0, 0x8f, 0, 0x10) != 0x10)
++ return stv0680_handle_error(gspca_dev, -EIO);
++
++ cam->bulk = 1;
++ cam->bulk_nurbs = 1; /* The cam cannot handle more */
++ cam->bulk_size = (gspca_dev->usb_buf[0] << 24) |
++ (gspca_dev->usb_buf[1] << 16) |
++ (gspca_dev->usb_buf[2] << 8) |
++ (gspca_dev->usb_buf[3]);
++ sd->mode.width = (gspca_dev->usb_buf[4] << 8) |
++ (gspca_dev->usb_buf[5]); /* 322, 356, 644 */
++ sd->mode.height = (gspca_dev->usb_buf[6] << 8) |
++ (gspca_dev->usb_buf[7]); /* 242, 292, 484 */
++ sd->mode.pixelformat = V4L2_PIX_FMT_STV0680;
++ sd->mode.field = V4L2_FIELD_NONE;
++ sd->mode.bytesperline = sd->mode.width;
++ sd->mode.sizeimage = cam->bulk_size;
++ sd->mode.colorspace = V4L2_COLORSPACE_SRGB;
++
++ /* origGain = gspca_dev->usb_buf[12]; */
++
++ cam->cam_mode = &sd->mode;
++ cam->nmodes = 1;
++
++#if 0 /* The v4l1 driver used to this but I don't think it is necessary */
++ ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "Set alt 0 failed (%d)", ret);
++ return ret;
++ }
++#endif
++
++ ret = stv0680_set_video_mode(gspca_dev, sd->orig_mode);
++ if (ret < 0)
++ return ret;
++
++ if (stv_sndctrl(gspca_dev, 2, 0x06, 0x0100, 0x12) != 0x12 ||
++ gspca_dev->usb_buf[8] != 0x53 || gspca_dev->usb_buf[9] != 0x05) {
++ PDEBUG(D_ERR, "Could not get descriptor 0100.");
++ return stv0680_handle_error(gspca_dev, -EIO);
++ }
++
++ return 0;
++}
++
++/* this function is called at probe and resume time */
++static int sd_init(struct gspca_dev *gspca_dev)
++{
++ return 0;
++}
++
++/* -- start the camera -- */
++static int sd_start(struct gspca_dev *gspca_dev)
++{
++ int ret;
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ ret = stv0680_set_video_mode(gspca_dev, sd->video_mode);
++ if (ret < 0)
++ return ret;
++
++ if (stv_sndctrl(gspca_dev, 0, 0x85, 0, 0x10) != 0x10)
++ return stv0680_handle_error(gspca_dev, -EIO);
++
++ /* Start stream at:
++ 0x0000 = CIF (352x288)
++ 0x0100 = VGA (640x480)
++ 0x0300 = QVGA (320x240) */
++ if (stv_sndctrl(gspca_dev, 1, 0x09, sd->video_mode << 8, 0x0) != 0x0)
++ return stv0680_handle_error(gspca_dev, -EIO);
++
++ return 0;
++}
++
++static void sd_stopN(struct gspca_dev *gspca_dev)
++{
++ /* This is a high priority command; it stops all lower order cmds */
++ if (stv_sndctrl(gspca_dev, 1, 0x04, 0x0000, 0x0) != 0x0)
++ stv0680_handle_error(gspca_dev, -EIO);
++}
++
++static void sd_stop0(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (!sd->gspca_dev.present)
++ return;
++
++ stv0680_set_video_mode(gspca_dev, sd->orig_mode);
++}
++
++static void sd_pkt_scan(struct gspca_dev *gspca_dev,
++ u8 *data,
++ int len)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ /* Every now and then the camera sends a 16 byte packet, no idea
++ what it contains, but it is not image data, when this
++ happens the frame received before this packet is corrupt,
++ so discard it. */
++ if (len != sd->mode.sizeimage) {
++ gspca_dev->last_packet_type = DISCARD_PACKET;
++ return;
++ }
++
++ /* Finish the previous frame, we do this upon reception of the next
++ packet, even though it is already complete so that the strange 16
++ byte packets send after a corrupt frame can discard it. */
++ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
++
++ /* Store the just received frame */
++ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
++}
++
++/* sub-driver description */
++static const struct sd_desc sd_desc = {
++ .name = MODULE_NAME,
++ .ctrls = sd_ctrls,
++ .nctrls = ARRAY_SIZE(sd_ctrls),
++ .config = sd_config,
++ .init = sd_init,
++ .start = sd_start,
++ .stopN = sd_stopN,
++ .stop0 = sd_stop0,
++ .pkt_scan = sd_pkt_scan,
++};
++
++/* -- module initialisation -- */
++static const __devinitdata struct usb_device_id device_table[] = {
++ {USB_DEVICE(0x0553, 0x0202)},
++ {USB_DEVICE(0x041e, 0x4007)},
++ {}
++};
++MODULE_DEVICE_TABLE(usb, device_table);
++
++/* -- device connect -- */
++static int sd_probe(struct usb_interface *intf,
++ const struct usb_device_id *id)
++{
++ return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
++ THIS_MODULE);
++}
++
++static struct usb_driver sd_driver = {
++ .name = MODULE_NAME,
++ .id_table = device_table,
++ .probe = sd_probe,
++ .disconnect = gspca_disconnect,
++#ifdef CONFIG_PM
++ .suspend = gspca_suspend,
++ .resume = gspca_resume,
++#endif
++};
++
++/* -- module insert / remove -- */
++static int __init sd_mod_init(void)
++{
++ int ret;
++ ret = usb_register(&sd_driver);
++ if (ret < 0)
++ return ret;
++ PDEBUG(D_PROBE, "registered");
++ return 0;
++}
++static void __exit sd_mod_exit(void)
++{
++ usb_deregister(&sd_driver);
++ PDEBUG(D_PROBE, "deregistered");
++}
++
++module_init(sd_mod_init);
++module_exit(sd_mod_exit);
+diff --git a/drivers/media/video/gspca/stv06xx/stv06xx.c b/drivers/media/video/gspca/stv06xx/stv06xx.c
+index bfae63f..de823ed 100644
+--- a/drivers/media/video/gspca/stv06xx/stv06xx.c
++++ b/drivers/media/video/gspca/stv06xx/stv06xx.c
+@@ -219,6 +219,7 @@ static void stv06xx_dump_bridge(struct sd *sd)
+ info("Read 0x%x from address 0x%x", data, i);
+ }
+
++ info("Testing stv06xx bridge registers for writability");
+ for (i = 0x1400; i < 0x160f; i++) {
+ stv06xx_read_bridge(sd, i, &data);
+ buf = data;
+@@ -229,7 +230,7 @@ static void stv06xx_dump_bridge(struct sd *sd)
+ info("Register 0x%x is read/write", i);
+ else if (data != buf)
+ info("Register 0x%x is read/write,"
+- "but only partially", i);
++ " but only partially", i);
+ else
+ info("Register 0x%x is read-only", i);
+
+@@ -312,8 +313,7 @@ out:
+ * The 0005 and 0100 chunks seem to appear only in compressed stream.
+ */
+ static void stv06xx_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+- __u8 *data, /* isoc packet */
++ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -366,7 +366,7 @@ frame_data:
+ sd->to_skip -= skip;
+ }
+
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
++ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, chunk_len);
+ break;
+
+@@ -378,7 +378,7 @@ frame_data:
+
+ /* Create a new frame, chunk length should be zero */
+ gspca_frame_add(gspca_dev, FIRST_PACKET,
+- frame, data, 0);
++ NULL, 0);
+
+ if (sd->bridge == BRIDGE_ST6422)
+ sd->to_skip = gspca_dev->width * 4;
+@@ -394,8 +394,8 @@ frame_data:
+ PDEBUG(D_PACK, "End of frame detected");
+
+ /* Complete the last frame (if any) */
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET,
+- frame, data, 0);
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ NULL, 0);
+
+ if (chunk_len)
+ PDEBUG(D_ERR, "Chunk length is "
+diff --git a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
+index 487d405..96c6192 100644
+--- a/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
++++ b/drivers/media/video/gspca/stv06xx/stv06xx_vv6410.h
+@@ -228,6 +228,7 @@ static const struct stv_init stv_bridge_init[] = {
+ /* This reg is written twice. Some kind of reset? */
+ {NULL, 0x1620, 0x80},
+ {NULL, 0x1620, 0x00},
++ {NULL, 0x1443, 0x00},
+ {NULL, 0x1423, 0x04},
+ {x1500, 0x1500, ARRAY_SIZE(x1500)},
+ {x1536, 0x1536, ARRAY_SIZE(x1536)},
+diff --git a/drivers/media/video/gspca/sunplus.c b/drivers/media/video/gspca/sunplus.c
+index 28b4625..952d7e5 100644
+--- a/drivers/media/video/gspca/sunplus.c
++++ b/drivers/media/video/gspca/sunplus.c
+@@ -67,7 +67,7 @@ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
+
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+@@ -267,6 +267,8 @@ static const struct cmd spca504A_clicksmart420_open_data[] = {
+ {0x06, 0x0000, 0x0000},
+ {0x00, 0x0004, 0x2880},
+ {0x00, 0x0001, 0x2881},
++#if 0
++/*jfm: overloaded by setup_qtable()*/
+ /* look like setting a qTable */
+ {0x00, 0x0006, 0x2800},
+ {0x00, 0x0004, 0x2801},
+@@ -403,6 +405,7 @@ static const struct cmd spca504A_clicksmart420_open_data[] = {
+ {0x00, 0x0028, 0x287d},
+ {0x00, 0x0028, 0x287e},
+ {0x00, 0x0028, 0x287f},
++#endif
+
+ {0xa0, 0x0000, 0x0503},
+ };
+@@ -460,13 +463,17 @@ static void reg_r(struct gspca_dev *gspca_dev,
+ u16 index,
+ u16 len)
+ {
++ int ret;
++
+ #ifdef GSPCA_DEBUG
+ if (len > USB_BUF_SZ) {
+ err("reg_r: buffer overflow");
+ return;
+ }
+ #endif
+- usb_control_msg(gspca_dev->dev,
++ if (gspca_dev->usb_err < 0)
++ return;
++ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ req,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+@@ -474,6 +481,10 @@ static void reg_r(struct gspca_dev *gspca_dev,
+ index,
+ len ? gspca_dev->usb_buf : NULL, len,
+ 500);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "reg_r err %d", ret);
++ gspca_dev->usb_err = ret;
++ }
+ }
+
+ /* write one byte */
+@@ -483,40 +494,55 @@ static void reg_w_1(struct gspca_dev *gspca_dev,
+ u16 index,
+ u16 byte)
+ {
++ int ret;
++
++ if (gspca_dev->usb_err < 0)
++ return;
+ gspca_dev->usb_buf[0] = byte;
+- usb_control_msg(gspca_dev->dev,
++ ret = usb_control_msg(gspca_dev->dev,
+ usb_sndctrlpipe(gspca_dev->dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index,
+ gspca_dev->usb_buf, 1,
+ 500);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "reg_w_1 err %d", ret);
++ gspca_dev->usb_err = ret;
++ }
+ }
+
+ /* write req / index / value */
+-static int reg_w_riv(struct usb_device *dev,
++static void reg_w_riv(struct gspca_dev *gspca_dev,
+ u8 req, u16 index, u16 value)
+ {
++ struct usb_device *dev = gspca_dev->dev;
+ int ret;
+
++ if (gspca_dev->usb_err < 0)
++ return;
+ ret = usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
+ req,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ value, index, NULL, 0, 500);
+- PDEBUG(D_USBO, "reg write: 0x%02x,0x%02x:0x%02x, %d",
+- req, index, value, ret);
+- if (ret < 0)
+- PDEBUG(D_ERR, "reg write: error %d", ret);
+- return ret;
++ if (ret < 0) {
++ PDEBUG(D_ERR, "reg_w_riv err %d", ret);
++ gspca_dev->usb_err = ret;
++ return;
++ }
++ PDEBUG(D_USBO, "reg_w_riv: 0x%02x,0x%04x:0x%04x",
++ req, index, value);
+ }
+
+ /* read 1 byte */
+-static int reg_r_1(struct gspca_dev *gspca_dev,
++static u8 reg_r_1(struct gspca_dev *gspca_dev,
+ u16 value) /* wValue */
+ {
+ int ret;
+
++ if (gspca_dev->usb_err < 0)
++ return 0;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+ 0x20, /* request */
+@@ -527,19 +553,22 @@ static int reg_r_1(struct gspca_dev *gspca_dev,
+ 500); /* timeout */
+ if (ret < 0) {
+ PDEBUG(D_ERR, "reg_r_1 err %d", ret);
++ gspca_dev->usb_err = ret;
+ return 0;
+ }
+ return gspca_dev->usb_buf[0];
+ }
+
+-/* read 1 or 2 bytes - returns < 0 if error */
+-static int reg_r_12(struct gspca_dev *gspca_dev,
++/* read 1 or 2 bytes */
++static u16 reg_r_12(struct gspca_dev *gspca_dev,
+ u8 req, /* bRequest */
+ u16 index, /* wIndex */
+ u16 length) /* wLength (1 or 2 only) */
+ {
+ int ret;
+
++ if (gspca_dev->usb_err < 0)
++ return 0;
+ gspca_dev->usb_buf[1] = 0;
+ ret = usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+@@ -550,62 +579,44 @@ static int reg_r_12(struct gspca_dev *gspca_dev,
+ gspca_dev->usb_buf, length,
+ 500);
+ if (ret < 0) {
+- PDEBUG(D_ERR, "reg_read err %d", ret);
+- return -1;
++ PDEBUG(D_ERR, "reg_r_12 err %d", ret);
++ gspca_dev->usb_err = ret;
++ return 0;
+ }
+ return (gspca_dev->usb_buf[1] << 8) + gspca_dev->usb_buf[0];
+ }
+
+-static int write_vector(struct gspca_dev *gspca_dev,
++static void write_vector(struct gspca_dev *gspca_dev,
+ const struct cmd *data, int ncmds)
+ {
+- struct usb_device *dev = gspca_dev->dev;
+- int ret;
+-
+ while (--ncmds >= 0) {
+- ret = reg_w_riv(dev, data->req, data->idx, data->val);
+- if (ret < 0) {
+- PDEBUG(D_ERR,
+- "Register write failed for 0x%02x, 0x%04x, 0x%04x",
+- data->req, data->val, data->idx);
+- return ret;
+- }
++ reg_w_riv(gspca_dev, data->req, data->idx, data->val);
+ data++;
+ }
+- return 0;
+ }
+
+-static int spca50x_setup_qtable(struct gspca_dev *gspca_dev,
+- const u8 qtable[2][64])
++static void setup_qtable(struct gspca_dev *gspca_dev,
++ const u8 qtable[2][64])
+ {
+- struct usb_device *dev = gspca_dev->dev;
+- int i, err;
++ int i;
+
+ /* loop over y components */
+- for (i = 0; i < 64; i++) {
+- err = reg_w_riv(dev, 0x00, 0x2800 + i, qtable[0][i]);
+- if (err < 0)
+- return err;
+- }
++ for (i = 0; i < 64; i++)
++ reg_w_riv(gspca_dev, 0x00, 0x2800 + i, qtable[0][i]);
+
+ /* loop over c components */
+- for (i = 0; i < 64; i++) {
+- err = reg_w_riv(dev, 0x00, 0x2840 + i, qtable[1][i]);
+- if (err < 0)
+- return err;
+- }
+- return 0;
++ for (i = 0; i < 64; i++)
++ reg_w_riv(gspca_dev, 0x00, 0x2840 + i, qtable[1][i]);
+ }
+
+ static void spca504_acknowledged_command(struct gspca_dev *gspca_dev,
+ u8 req, u16 idx, u16 val)
+ {
+- struct usb_device *dev = gspca_dev->dev;
+- int notdone;
++ u16 notdone;
+
+- reg_w_riv(dev, req, idx, val);
++ reg_w_riv(gspca_dev, req, idx, val);
+ notdone = reg_r_12(gspca_dev, 0x01, 0x0001, 1);
+- reg_w_riv(dev, req, idx, val);
++ reg_w_riv(gspca_dev, req, idx, val);
+
+ PDEBUG(D_FRAM, "before wait 0x%04x", notdone);
+
+@@ -614,25 +625,38 @@ static void spca504_acknowledged_command(struct gspca_dev *gspca_dev,
+ PDEBUG(D_FRAM, "after wait 0x%04x", notdone);
+ }
+
++static void spca504_read_info(struct gspca_dev *gspca_dev)
++{
++ int i;
++ u8 info[6];
++
++ for (i = 0; i < 6; i++)
++ info[i] = reg_r_1(gspca_dev, i);
++ PDEBUG(D_STREAM,
++ "Read info: %d %d %d %d %d %d."
++ " Should be 1,0,2,2,0,0",
++ info[0], info[1], info[2],
++ info[3], info[4], info[5]);
++}
++
+ static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev,
+ u8 req,
+- u16 idx, u16 val, u8 stat, u8 count)
++ u16 idx, u16 val, u16 endcode, u8 count)
+ {
+- struct usb_device *dev = gspca_dev->dev;
+- int status;
+- u8 endcode;
++ u16 status;
+
+- reg_w_riv(dev, req, idx, val);
++ reg_w_riv(gspca_dev, req, idx, val);
+ status = reg_r_12(gspca_dev, 0x01, 0x0001, 1);
+- endcode = stat;
+- PDEBUG(D_FRAM, "Status 0x%x Need 0x%04x", status, stat);
++ if (gspca_dev->usb_err < 0)
++ return;
++ PDEBUG(D_FRAM, "Status 0x%04x Need 0x%04x", status, endcode);
+ if (!count)
+ return;
+ count = 200;
+ while (--count > 0) {
+ msleep(10);
+- /* gsmart mini2 write a each wait setting 1 ms is enought */
+-/* reg_w_riv(dev, req, idx, val); */
++ /* gsmart mini2 write a each wait setting 1 ms is enough */
++/* reg_w_riv(gspca_dev, req, idx, val); */
+ status = reg_r_12(gspca_dev, 0x01, 0x0001, 1);
+ if (status == endcode) {
+ PDEBUG(D_FRAM, "status 0x%04x after wait %d",
+@@ -642,7 +666,7 @@ static void spca504A_acknowledged_command(struct gspca_dev *gspca_dev,
+ }
+ }
+
+-static int spca504B_PollingDataReady(struct gspca_dev *gspca_dev)
++static void spca504B_PollingDataReady(struct gspca_dev *gspca_dev)
+ {
+ int count = 10;
+
+@@ -652,7 +676,6 @@ static int spca504B_PollingDataReady(struct gspca_dev *gspca_dev)
+ break;
+ msleep(10);
+ }
+- return gspca_dev->usb_buf[0];
+ }
+
+ static void spca504B_WaitCmdStatus(struct gspca_dev *gspca_dev)
+@@ -686,28 +709,26 @@ static void spca50x_GetFirmware(struct gspca_dev *gspca_dev)
+ static void spca504B_SetSizeType(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- struct usb_device *dev = gspca_dev->dev;
+ u8 Size;
+- int rc;
+
+ Size = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ switch (sd->bridge) {
+ case BRIDGE_SPCA533:
+- reg_w_riv(dev, 0x31, 0, 0);
++ reg_w_riv(gspca_dev, 0x31, 0, 0);
+ spca504B_WaitCmdStatus(gspca_dev);
+- rc = spca504B_PollingDataReady(gspca_dev);
++ spca504B_PollingDataReady(gspca_dev);
+ spca50x_GetFirmware(gspca_dev);
+ reg_w_1(gspca_dev, 0x24, 0, 8, 2); /* type */
+ reg_r(gspca_dev, 0x24, 8, 1);
+
+ reg_w_1(gspca_dev, 0x25, 0, 4, Size);
+ reg_r(gspca_dev, 0x25, 4, 1); /* size */
+- rc = spca504B_PollingDataReady(gspca_dev);
++ spca504B_PollingDataReady(gspca_dev);
+
+ /* Init the cam width height with some values get on init ? */
+- reg_w_riv(dev, 0x31, 0x04, 0);
++ reg_w_riv(gspca_dev, 0x31, 0x04, 0);
+ spca504B_WaitCmdStatus(gspca_dev);
+- rc = spca504B_PollingDataReady(gspca_dev);
++ spca504B_PollingDataReady(gspca_dev);
+ break;
+ default:
+ /* case BRIDGE_SPCA504B: */
+@@ -716,7 +737,7 @@ static void spca504B_SetSizeType(struct gspca_dev *gspca_dev)
+ reg_r(gspca_dev, 0x25, 4, 1); /* size */
+ reg_w_1(gspca_dev, 0x27, 0, 0, 6);
+ reg_r(gspca_dev, 0x27, 0, 1); /* type */
+- rc = spca504B_PollingDataReady(gspca_dev);
++ spca504B_PollingDataReady(gspca_dev);
+ break;
+ case BRIDGE_SPCA504:
+ Size += 3;
+@@ -733,8 +754,8 @@ static void spca504B_SetSizeType(struct gspca_dev *gspca_dev)
+ break;
+ case BRIDGE_SPCA504C:
+ /* capture mode */
+- reg_w_riv(dev, 0xa0, (0x0500 | (Size & 0x0f)), 0x00);
+- reg_w_riv(dev, 0x20, 0x01, 0x0500 | (Size & 0x0f));
++ reg_w_riv(gspca_dev, 0xa0, (0x0500 | (Size & 0x0f)), 0x00);
++ reg_w_riv(gspca_dev, 0x20, 0x01, 0x0500 | (Size & 0x0f));
+ break;
+ }
+ }
+@@ -762,37 +783,33 @@ static void spca504B_setQtable(struct gspca_dev *gspca_dev)
+ static void setbrightness(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- struct usb_device *dev = gspca_dev->dev;
+ u16 reg;
+
+ reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f0 : 0x21a7;
+- reg_w_riv(dev, 0x00, reg, sd->brightness);
++ reg_w_riv(gspca_dev, 0x00, reg, sd->brightness);
+ }
+
+ static void setcontrast(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- struct usb_device *dev = gspca_dev->dev;
+ u16 reg;
+
+ reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f1 : 0x21a8;
+- reg_w_riv(dev, 0x00, reg, sd->contrast);
++ reg_w_riv(gspca_dev, 0x00, reg, sd->contrast);
+ }
+
+ static void setcolors(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- struct usb_device *dev = gspca_dev->dev;
+ u16 reg;
+
+ reg = sd->bridge == BRIDGE_SPCA536 ? 0x20f6 : 0x21ae;
+- reg_w_riv(dev, 0x00, reg, sd->colors);
++ reg_w_riv(gspca_dev, 0x00, reg, sd->colors);
+ }
+
+ static void init_ctl_reg(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- struct usb_device *dev = gspca_dev->dev;
+ int pollreg = 1;
+
+ setbrightness(gspca_dev);
+@@ -807,14 +824,14 @@ static void init_ctl_reg(struct gspca_dev *gspca_dev)
+ default:
+ /* case BRIDGE_SPCA533: */
+ /* case BRIDGE_SPCA504B: */
+- reg_w_riv(dev, 0, 0x21ad, 0x00); /* hue */
+- reg_w_riv(dev, 0, 0x21ac, 0x01); /* sat/hue */
+- reg_w_riv(dev, 0, 0x21a3, 0x00); /* gamma */
++ reg_w_riv(gspca_dev, 0, 0x21ad, 0x00); /* hue */
++ reg_w_riv(gspca_dev, 0, 0x21ac, 0x01); /* sat/hue */
++ reg_w_riv(gspca_dev, 0, 0x21a3, 0x00); /* gamma */
+ break;
+ case BRIDGE_SPCA536:
+- reg_w_riv(dev, 0, 0x20f5, 0x40);
+- reg_w_riv(dev, 0, 0x20f4, 0x01);
+- reg_w_riv(dev, 0, 0x2089, 0x00);
++ reg_w_riv(gspca_dev, 0, 0x20f5, 0x40);
++ reg_w_riv(gspca_dev, 0, 0x20f4, 0x01);
++ reg_w_riv(gspca_dev, 0, 0x2089, 0x00);
+ break;
+ }
+ if (pollreg)
+@@ -881,18 +898,15 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ static int sd_init(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- struct usb_device *dev = gspca_dev->dev;
+- int i, err_code;
+- u8 info[6];
+
+ switch (sd->bridge) {
+ case BRIDGE_SPCA504B:
+- reg_w_riv(dev, 0x1d, 0x00, 0);
+- reg_w_riv(dev, 0, 0x2306, 0x01);
+- reg_w_riv(dev, 0, 0x0d04, 0x00);
+- reg_w_riv(dev, 0, 0x2000, 0x00);
+- reg_w_riv(dev, 0, 0x2301, 0x13);
+- reg_w_riv(dev, 0, 0x2306, 0x00);
++ reg_w_riv(gspca_dev, 0x1d, 0x00, 0);
++ reg_w_riv(gspca_dev, 0x00, 0x2306, 0x01);
++ reg_w_riv(gspca_dev, 0x00, 0x0d04, 0x00);
++ reg_w_riv(gspca_dev, 0x00, 0x2000, 0x00);
++ reg_w_riv(gspca_dev, 0x00, 0x2301, 0x13);
++ reg_w_riv(gspca_dev, 0x00, 0x2306, 0x00);
+ /* fall thru */
+ case BRIDGE_SPCA533:
+ spca504B_PollingDataReady(gspca_dev);
+@@ -904,13 +918,13 @@ static int sd_init(struct gspca_dev *gspca_dev)
+ reg_w_1(gspca_dev, 0x24, 0, 0, 0);
+ reg_r(gspca_dev, 0x24, 0, 1);
+ spca504B_PollingDataReady(gspca_dev);
+- reg_w_riv(dev, 0x34, 0, 0);
++ reg_w_riv(gspca_dev, 0x34, 0, 0);
+ spca504B_WaitCmdStatus(gspca_dev);
+ break;
+ case BRIDGE_SPCA504C: /* pccam600 */
+ PDEBUG(D_STREAM, "Opening SPCA504 (PC-CAM 600)");
+- reg_w_riv(dev, 0xe0, 0x0000, 0x0000);
+- reg_w_riv(dev, 0xe0, 0x0000, 0x0001); /* reset */
++ reg_w_riv(gspca_dev, 0xe0, 0x0000, 0x0000);
++ reg_w_riv(gspca_dev, 0xe0, 0x0000, 0x0001); /* reset */
+ spca504_wait_status(gspca_dev);
+ if (sd->subtype == LogitechClickSmart420)
+ write_vector(gspca_dev,
+@@ -919,26 +933,14 @@ static int sd_init(struct gspca_dev *gspca_dev)
+ else
+ write_vector(gspca_dev, spca504_pccam600_open_data,
+ ARRAY_SIZE(spca504_pccam600_open_data));
+- err_code = spca50x_setup_qtable(gspca_dev,
+- qtable_creative_pccam);
+- if (err_code < 0) {
+- PDEBUG(D_ERR|D_STREAM, "spca50x_setup_qtable failed");
+- return err_code;
+- }
++ setup_qtable(gspca_dev, qtable_creative_pccam);
+ break;
+ default:
+ /* case BRIDGE_SPCA504: */
+ PDEBUG(D_STREAM, "Opening SPCA504");
+ if (sd->subtype == AiptekMiniPenCam13) {
+- /*****************************/
+- for (i = 0; i < 6; i++)
+- info[i] = reg_r_1(gspca_dev, i);
+- PDEBUG(D_STREAM,
+- "Read info: %d %d %d %d %d %d."
+- " Should be 1,0,2,2,0,0",
+- info[0], info[1], info[2],
+- info[3], info[4], info[5]);
+- /* spca504a aiptek */
++ spca504_read_info(gspca_dev);
++
+ /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */
+ spca504A_acknowledged_command(gspca_dev, 0x24,
+ 8, 3, 0x9e, 1);
+@@ -958,32 +960,25 @@ static int sd_init(struct gspca_dev *gspca_dev)
+ 6, 0, 0x86, 1); */
+ /* spca504A_acknowledged_command (gspca_dev, 0x24,
+ 0, 0, 0x9D, 1); */
+- reg_w_riv(dev, 0x00, 0x270c, 0x05); /* L92 sno1t.txt */
+- reg_w_riv(dev, 0x00, 0x2310, 0x05);
++ reg_w_riv(gspca_dev, 0x00, 0x270c, 0x05);
++ /* L92 sno1t.txt */
++ reg_w_riv(gspca_dev, 0x00, 0x2310, 0x05);
+ spca504A_acknowledged_command(gspca_dev, 0x01,
+ 0x0f, 0, 0xff, 0);
+ }
+ /* setup qtable */
+- reg_w_riv(dev, 0, 0x2000, 0);
+- reg_w_riv(dev, 0, 0x2883, 1);
+- err_code = spca50x_setup_qtable(gspca_dev,
+- qtable_spca504_default);
+- if (err_code < 0) {
+- PDEBUG(D_ERR, "spca50x_setup_qtable failed");
+- return err_code;
+- }
++ reg_w_riv(gspca_dev, 0, 0x2000, 0);
++ reg_w_riv(gspca_dev, 0, 0x2883, 1);
++ setup_qtable(gspca_dev, qtable_spca504_default);
+ break;
+ }
+- return 0;
++ return gspca_dev->usb_err;
+ }
+
+ static int sd_start(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- struct usb_device *dev = gspca_dev->dev;
+ int enable;
+- int i;
+- u8 info[6];
+
+ /* create the JPEG header */
+ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
+@@ -1005,13 +1000,13 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ case MegapixV4:
+ case LogitechClickSmart820:
+ case MegaImageVI:
+- reg_w_riv(dev, 0xf0, 0, 0);
++ reg_w_riv(gspca_dev, 0xf0, 0, 0);
+ spca504B_WaitCmdStatus(gspca_dev);
+ reg_r(gspca_dev, 0xf0, 4, 0);
+ spca504B_WaitCmdStatus(gspca_dev);
+ break;
+ default:
+- reg_w_riv(dev, 0x31, 0x04, 0);
++ reg_w_riv(gspca_dev, 0x31, 0x04, 0x00);
+ spca504B_WaitCmdStatus(gspca_dev);
+ spca504B_PollingDataReady(gspca_dev);
+ break;
+@@ -1019,14 +1014,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ break;
+ case BRIDGE_SPCA504:
+ if (sd->subtype == AiptekMiniPenCam13) {
+- for (i = 0; i < 6; i++)
+- info[i] = reg_r_1(gspca_dev, i);
+- PDEBUG(D_STREAM,
+- "Read info: %d %d %d %d %d %d."
+- " Should be 1,0,2,2,0,0",
+- info[0], info[1], info[2],
+- info[3], info[4], info[5]);
+- /* spca504a aiptek */
++ spca504_read_info(gspca_dev);
++
+ /* Set AE AWB Banding Type 3-> 50Hz 2-> 60Hz */
+ spca504A_acknowledged_command(gspca_dev, 0x24,
+ 8, 3, 0x9e, 1);
+@@ -1037,19 +1026,14 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ 0, 0, 0x9d, 1);
+ } else {
+ spca504_acknowledged_command(gspca_dev, 0x24, 8, 3);
+- for (i = 0; i < 6; i++)
+- info[i] = reg_r_1(gspca_dev, i);
+- PDEBUG(D_STREAM,
+- "Read info: %d %d %d %d %d %d."
+- " Should be 1,0,2,2,0,0",
+- info[0], info[1], info[2],
+- info[3], info[4], info[5]);
++ spca504_read_info(gspca_dev);
+ spca504_acknowledged_command(gspca_dev, 0x24, 8, 3);
+ spca504_acknowledged_command(gspca_dev, 0x24, 0, 0);
+ }
+ spca504B_SetSizeType(gspca_dev);
+- reg_w_riv(dev, 0x00, 0x270c, 0x05); /* L92 sno1t.txt */
+- reg_w_riv(dev, 0x00, 0x2310, 0x05);
++ reg_w_riv(gspca_dev, 0x00, 0x270c, 0x05);
++ /* L92 sno1t.txt */
++ reg_w_riv(gspca_dev, 0x00, 0x2310, 0x05);
+ break;
+ case BRIDGE_SPCA504C:
+ if (sd->subtype == LogitechClickSmart420) {
+@@ -1061,36 +1045,37 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ ARRAY_SIZE(spca504_pccam600_init_data));
+ }
+ enable = (sd->autogain ? 0x04 : 0x01);
+- reg_w_riv(dev, 0x0c, 0x0000, enable); /* auto exposure */
+- reg_w_riv(dev, 0xb0, 0x0000, enable); /* auto whiteness */
++ reg_w_riv(gspca_dev, 0x0c, 0x0000, enable);
++ /* auto exposure */
++ reg_w_riv(gspca_dev, 0xb0, 0x0000, enable);
++ /* auto whiteness */
+
+ /* set default exposure compensation and whiteness balance */
+- reg_w_riv(dev, 0x30, 0x0001, 800); /* ~ 20 fps */
+- reg_w_riv(dev, 0x30, 0x0002, 1600);
++ reg_w_riv(gspca_dev, 0x30, 0x0001, 800); /* ~ 20 fps */
++ reg_w_riv(gspca_dev, 0x30, 0x0002, 1600);
+ spca504B_SetSizeType(gspca_dev);
+ break;
+ }
+ init_ctl_reg(gspca_dev);
+- return 0;
++ return gspca_dev->usb_err;
+ }
+
+ static void sd_stopN(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- struct usb_device *dev = gspca_dev->dev;
+
+ switch (sd->bridge) {
+ default:
+ /* case BRIDGE_SPCA533: */
+ /* case BRIDGE_SPCA536: */
+ /* case BRIDGE_SPCA504B: */
+- reg_w_riv(dev, 0x31, 0, 0);
++ reg_w_riv(gspca_dev, 0x31, 0, 0);
+ spca504B_WaitCmdStatus(gspca_dev);
+ spca504B_PollingDataReady(gspca_dev);
+ break;
+ case BRIDGE_SPCA504:
+ case BRIDGE_SPCA504C:
+- reg_w_riv(dev, 0x00, 0x2000, 0x0000);
++ reg_w_riv(gspca_dev, 0x00, 0x2000, 0x0000);
+
+ if (sd->subtype == AiptekMiniPenCam13) {
+ /* spca504a aiptek */
+@@ -1102,7 +1087,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
+ 0x0f, 0x00, 0xff, 1);
+ } else {
+ spca504_acknowledged_command(gspca_dev, 0x24, 0, 0);
+- reg_w_riv(dev, 0x01, 0x000f, 0x0000);
++ reg_w_riv(gspca_dev, 0x01, 0x000f, 0x0000);
+ }
+ break;
+ }
+@@ -1116,7 +1101,6 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+@@ -1186,11 +1170,11 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ break;
+ }
+ if (sof) { /* start of frame */
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+- ffd9, 2);
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ ffd9, 2);
+
+ /* put the JPEG header in the new frame */
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+ }
+
+@@ -1198,7 +1182,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ i = 0;
+ do {
+ if (data[i] == 0xff) {
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame,
++ gspca_frame_add(gspca_dev, INTER_PACKET,
+ data, i + 1);
+ len -= i;
+ data += i;
+@@ -1207,7 +1191,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ }
+ i++;
+ } while (i < len);
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+@@ -1217,7 +1201,7 @@ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+ sd->brightness = val;
+ if (gspca_dev->streaming)
+ setbrightness(gspca_dev);
+- return 0;
++ return gspca_dev->usb_err;
+ }
+
+ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
+@@ -1235,7 +1219,7 @@ static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
+ sd->contrast = val;
+ if (gspca_dev->streaming)
+ setcontrast(gspca_dev);
+- return 0;
++ return gspca_dev->usb_err;
+ }
+
+ static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
+@@ -1253,7 +1237,7 @@ static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
+ sd->colors = val;
+ if (gspca_dev->streaming)
+ setcolors(gspca_dev);
+- return 0;
++ return gspca_dev->usb_err;
+ }
+
+ static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
+@@ -1293,7 +1277,7 @@ static int sd_set_jcomp(struct gspca_dev *gspca_dev,
+ sd->quality = jcomp->quality;
+ if (gspca_dev->streaming)
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+- return 0;
++ return gspca_dev->usb_err;
+ }
+
+ static int sd_get_jcomp(struct gspca_dev *gspca_dev,
+@@ -1346,6 +1330,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x04fc, 0x5330), BS(SPCA533, 0)},
+ {USB_DEVICE(0x04fc, 0x5360), BS(SPCA536, 0)},
+ {USB_DEVICE(0x04fc, 0xffff), BS(SPCA504B, 0)},
++ {USB_DEVICE(0x052b, 0x1507), BS(SPCA533, MegapixV4)},
+ {USB_DEVICE(0x052b, 0x1513), BS(SPCA533, MegapixV4)},
+ {USB_DEVICE(0x052b, 0x1803), BS(SPCA533, MegaImageVI)},
+ {USB_DEVICE(0x0546, 0x3155), BS(SPCA533, 0)},
+diff --git a/drivers/media/video/gspca/t613.c b/drivers/media/video/gspca/t613.c
+index 1d321c3..d0c208c 100644
+--- a/drivers/media/video/gspca/t613.c
++++ b/drivers/media/video/gspca/t613.c
+@@ -78,7 +78,7 @@ static int sd_geteffect(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_querymenu(struct gspca_dev *gspca_dev,
+ struct v4l2_querymenu *menu);
+
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+@@ -938,7 +938,6 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+@@ -956,9 +955,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ /* extra bytes....., could be processed too but would be
+ * a waste of time, right now leave the application and
+ * libjpeg do it for ourserlves.. */
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
++ gspca_frame_add(gspca_dev, LAST_PACKET,
+ ffd9, 2);
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame, data, len);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ return;
+ }
+
+@@ -967,7 +966,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ * other's do not include it... */
+ len -= 2;
+ }
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+diff --git a/drivers/media/video/gspca/tv8532.c b/drivers/media/video/gspca/tv8532.c
+index 4b44dde..c7b6eb1 100644
+--- a/drivers/media/video/gspca/tv8532.c
++++ b/drivers/media/video/gspca/tv8532.c
+@@ -39,7 +39,7 @@ struct sd {
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
+
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+@@ -398,8 +398,7 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+- __u8 *data, /* isoc packet */
++ u8 *data, /* isoc packet */
+ int len) /* iso packet length */
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -424,9 +423,9 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ * - 4 bytes
+ */
+ gspca_frame_add(gspca_dev, packet_type0,
+- frame, data + 2, gspca_dev->width);
++ data + 2, gspca_dev->width);
+ gspca_frame_add(gspca_dev, packet_type1,
+- frame, data + gspca_dev->width + 5, gspca_dev->width);
++ data + gspca_dev->width + 5, gspca_dev->width);
+ }
+
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+diff --git a/drivers/media/video/gspca/vc032x.c b/drivers/media/video/gspca/vc032x.c
+index 589042f..c63c4b2 100644
+--- a/drivers/media/video/gspca/vc032x.c
++++ b/drivers/media/video/gspca/vc032x.c
+@@ -32,10 +32,13 @@ MODULE_LICENSE("GPL");
+ struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
++ u8 brightness;
++ u8 contrast;
++ u8 colors;
+ u8 hflip;
+ u8 vflip;
+ u8 lightfreq;
+- u8 sharpness;
++ s8 sharpness;
+
+ u8 image_offset;
+
+@@ -52,6 +55,7 @@ struct sd {
+ #define SENSOR_OV7670 6
+ #define SENSOR_PO1200 7
+ #define SENSOR_PO3130NC 8
++#define SENSOR_POxxxx 9
+ u8 flags;
+ #define FL_SAMSUNG 0x01 /* SamsungQ1 (2 sensors) */
+ #define FL_HFLIP 0x02 /* mirrored by default */
+@@ -59,6 +63,12 @@ struct sd {
+ };
+
+ /* V4L2 controls supported by the driver */
++static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
++static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
++static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
+@@ -68,9 +78,54 @@ static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
++#define BRIGHTNESS_IDX 0
++ {
++ {
++ .id = V4L2_CID_BRIGHTNESS,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Brightness",
++ .minimum = 0,
++ .maximum = 255,
++ .step = 1,
++#define BRIGHTNESS_DEF 128
++ .default_value = BRIGHTNESS_DEF,
++ },
++ .set = sd_setbrightness,
++ .get = sd_getbrightness,
++ },
++#define CONTRAST_IDX 1
++ {
++ {
++ .id = V4L2_CID_CONTRAST,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Contrast",
++ .minimum = 0,
++ .maximum = 255,
++ .step = 1,
++#define CONTRAST_DEF 127
++ .default_value = CONTRAST_DEF,
++ },
++ .set = sd_setcontrast,
++ .get = sd_getcontrast,
++ },
++#define COLORS_IDX 2
++ {
++ {
++ .id = V4L2_CID_SATURATION,
++ .type = V4L2_CTRL_TYPE_INTEGER,
++ .name = "Saturation",
++ .minimum = 1,
++ .maximum = 127,
++ .step = 1,
++#define COLOR_DEF 63
++ .default_value = COLOR_DEF,
++ },
++ .set = sd_setcolors,
++ .get = sd_getcolors,
++ },
+ /* next 2 controls work with some sensors only */
+-#define HFLIP_IDX 0
++#define HFLIP_IDX 3
+ {
+ {
+ .id = V4L2_CID_HFLIP,
+@@ -85,7 +140,7 @@ static struct ctrl sd_ctrls[] = {
+ .set = sd_sethflip,
+ .get = sd_gethflip,
+ },
+-#define VFLIP_IDX 1
++#define VFLIP_IDX 4
+ {
+ {
+ .id = V4L2_CID_VFLIP,
+@@ -100,7 +155,7 @@ static struct ctrl sd_ctrls[] = {
+ .set = sd_setvflip,
+ .get = sd_getvflip,
+ },
+-#define LIGHTFREQ_IDX 2
++#define LIGHTFREQ_IDX 5
+ {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+@@ -115,17 +170,16 @@ static struct ctrl sd_ctrls[] = {
+ .set = sd_setfreq,
+ .get = sd_getfreq,
+ },
+-/* po1200 only */
+-#define SHARPNESS_IDX 3
++#define SHARPNESS_IDX 6
+ {
+ {
+ .id = V4L2_CID_SHARPNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Sharpness",
+- .minimum = 0,
++ .minimum = -1,
+ .maximum = 2,
+ .step = 1,
+-#define SHARPNESS_DEF 1
++#define SHARPNESS_DEF -1
+ .default_value = SHARPNESS_DEF,
+ },
+ .set = sd_setsharpness,
+@@ -133,6 +187,42 @@ static struct ctrl sd_ctrls[] = {
+ },
+ };
+
++/* table of the disabled controls */
++static u32 ctrl_dis[] = {
++/* SENSOR_HV7131R 0 */
++ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
++ | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
++ | (1 << SHARPNESS_IDX),
++/* SENSOR_MI0360 1 */
++ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
++ | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
++ | (1 << SHARPNESS_IDX),
++/* SENSOR_MI1310_SOC 2 */
++ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
++ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
++/* SENSOR_MI1320 3 */
++ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
++ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
++/* SENSOR_MI1320_SOC 4 */
++ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
++ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
++/* SENSOR_OV7660 5 */
++ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
++ | (1 << LIGHTFREQ_IDX) | (1 << SHARPNESS_IDX),
++/* SENSOR_OV7670 6 */
++ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
++ | (1 << SHARPNESS_IDX),
++/* SENSOR_PO1200 7 */
++ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
++ | (1 << LIGHTFREQ_IDX),
++/* SENSOR_PO3130NC 8 */
++ (1 << BRIGHTNESS_IDX) | (1 << CONTRAST_IDX) | (1 << COLORS_IDX)
++ | (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX)
++ | (1 << SHARPNESS_IDX),
++/* SENSOR_POxxxx 9 */
++ (1 << HFLIP_IDX) | (1 << VFLIP_IDX) | (1 << LIGHTFREQ_IDX),
++};
++
+ static const struct v4l2_pix_format vc0321_mode[] = {
+ {320, 240, V4L2_PIX_FMT_YVYU, V4L2_FIELD_NONE,
+ .bytesperline = 320,
+@@ -215,7 +305,7 @@ static const u8 mi0360_initVGA_JPG[][4] = {
+ {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+- {0xb3, 0x35, 0xdd, 0xcc},
++ {0xb3, 0x35, 0xdd, 0xcc}, /* i2c add: 5d */
+ {0xb3, 0x34, 0x02, 0xcc},
+ {0xb3, 0x00, 0x25, 0xcc},
+ {0xbc, 0x00, 0x71, 0xcc},
+@@ -435,7 +525,7 @@ static const u8 mi1310_socinitVGA_JPG[][4] = {
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+- {0xb3, 0x35, 0xdd, 0xcc},
++ {0xb3, 0x35, 0xdd, 0xcc}, /* i2c add: 5d */
+ {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+@@ -860,7 +950,8 @@ static const u8 mi1320_initVGA_data[][4] = {
+ {0xb0, 0x16, 0x03, 0xcc}, {0xb3, 0x05, 0x00, 0xcc},
+ {0xb3, 0x06, 0x00, 0xcc}, {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc}, {0xb3, 0x34, 0x02, 0xcc},
+- {0xb3, 0x35, 0xc8, 0xcc}, {0xb3, 0x02, 0x00, 0xcc},
++ {0xb3, 0x35, 0xc8, 0xcc}, /* i2c add: 48 */
++ {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc}, {0xb3, 0x04, 0x05, 0xcc},
+ {0xb3, 0x20, 0x00, 0xcc}, {0xb3, 0x21, 0x00, 0xcc},
+ {0xb3, 0x22, 0x03, 0xcc}, {0xb3, 0x23, 0xc0, 0xcc},
+@@ -901,7 +992,8 @@ static const u8 mi1320_initVGA_data[][4] = {
+ {0xc3, 0x01, 0x03, 0xbb}, {0xc4, 0x00, 0x04, 0xbb},
+ {0xf0, 0x00, 0x00, 0xbb}, {0x05, 0x01, 0x13, 0xbb},
+ {0x06, 0x00, 0x11, 0xbb}, {0x07, 0x00, 0x85, 0xbb},
+- {0x08, 0x00, 0x27, 0xbb}, {0x20, 0x01, 0x03, 0xbb},
++ {0x08, 0x00, 0x27, 0xbb},
++ {0x20, 0x01, 0x00, 0xbb}, /* h/v flips - was 03 */
+ {0x21, 0x80, 0x00, 0xbb}, {0x22, 0x0d, 0x0f, 0xbb},
+ {0x24, 0x80, 0x00, 0xbb}, {0x59, 0x00, 0xff, 0xbb},
+ {0xf0, 0x00, 0x02, 0xbb}, {0x39, 0x03, 0x0d, 0xbb},
+@@ -1012,7 +1104,7 @@ static const u8 mi1320_soc_InitVGA[][4] = {
+ {0xb3, 0x08, 0x01, 0xcc},
+ {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x34, 0x02, 0xcc},
+- {0xb3, 0x35, 0xc8, 0xcc},
++ {0xb3, 0x35, 0xc8, 0xcc}, /* i2c add: 48 */
+ {0xb3, 0x02, 0x00, 0xcc},
+ {0xb3, 0x03, 0x0a, 0xcc},
+ {0xb3, 0x04, 0x05, 0xcc},
+@@ -1359,7 +1451,8 @@ static const u8 po3130_initVGA_data[][4] = {
+ {0xb3, 0x23, 0xe8, 0xcc}, {0xb8, 0x08, 0xe8, 0xcc},
+ {0xb3, 0x14, 0x00, 0xcc}, {0xb3, 0x15, 0x00, 0xcc},
+ {0xb3, 0x16, 0x02, 0xcc}, {0xb3, 0x17, 0x7f, 0xcc},
+- {0xb3, 0x34, 0x01, 0xcc}, {0xb3, 0x35, 0xf6, 0xcc},
++ {0xb3, 0x34, 0x01, 0xcc},
++ {0xb3, 0x35, 0xf6, 0xcc}, /* i2c add: 76 */
+ {0xb3, 0x00, 0x27, 0xcc}, {0xbc, 0x00, 0x71, 0xcc},
+ {0xb8, 0x00, 0x21, 0xcc}, {0xb8, 0x27, 0x20, 0xcc},
+ {0xb8, 0x01, 0x79, 0xcc}, {0xb8, 0x81, 0x09, 0xcc},
+@@ -1561,7 +1654,7 @@ static const u8 hv7131r_initVGA_data[][4] = {
+ {0xb3, 0x16, 0x02, 0xcc},
+ {0xb3, 0x17, 0x7f, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+- {0xb3, 0x35, 0x91, 0xcc},
++ {0xb3, 0x35, 0x91, 0xcc}, /* i2c add: 11 */
+ {0xb3, 0x00, 0x27, 0xcc},
+ {0xbc, 0x00, 0x73, 0xcc},
+ {0xb8, 0x00, 0x23, 0xcc},
+@@ -1747,7 +1840,8 @@ static const u8 ov7660_initVGA_data[][4] = {
+ {0xb3, 0x23, 0xe0, 0xcc}, {0xb3, 0x1d, 0x01, 0xcc},
+ {0xb3, 0x1f, 0x02, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+- {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x00, 0x26, 0xcc},
++ {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */
++ {0xb3, 0x00, 0x26, 0xcc},
+ {0xb8, 0x00, 0x33, 0xcc}, /* 13 */
+ {0xb8, 0x01, 0x7d, 0xcc},
+ {0xbc, 0x00, 0x73, 0xcc}, {0xb8, 0x81, 0x09, 0xcc},
+@@ -1883,7 +1977,8 @@ static const u8 ov7670_initVGA_JPG[][4] = {
+ {0x00, 0x00, 0x10, 0xdd},
+ {0xb0, 0x04, 0x02, 0xcc}, {0x00, 0x00, 0x10, 0xdd},
+ {0xb3, 0x00, 0x66, 0xcc}, {0xb3, 0x00, 0x67, 0xcc},
+- {0xb3, 0x35, 0xa1, 0xcc}, {0xb3, 0x34, 0x01, 0xcc},
++ {0xb3, 0x35, 0xa1, 0xcc}, /* i2c add: 21 */
++ {0xb3, 0x34, 0x01, 0xcc},
+ {0xb3, 0x05, 0x01, 0xcc}, {0xb3, 0x06, 0x01, 0xcc},
+ {0xb3, 0x08, 0x01, 0xcc}, {0xb3, 0x09, 0x0c, 0xcc},
+ {0xb3, 0x02, 0x02, 0xcc}, {0xb3, 0x03, 0x1f, 0xcc},
+@@ -2181,7 +2276,7 @@ static const u8 po1200_initVGA_data[][4] = {
+ {0xb0, 0x54, 0x13, 0xcc},
+ {0xb3, 0x00, 0x67, 0xcc},
+ {0xb3, 0x34, 0x01, 0xcc},
+- {0xb3, 0x35, 0xdc, 0xcc},
++ {0xb3, 0x35, 0xdc, 0xcc}, /* i2c add: 5c */
+ {0x00, 0x03, 0x00, 0xaa},
+ {0x00, 0x12, 0x05, 0xaa},
+ {0x00, 0x13, 0x02, 0xaa},
+@@ -2408,6 +2503,259 @@ static const u8 po1200_initVGA_data[][4] = {
+ {0x00, 0xb6, 0x39, 0xaa},
+ {0x00, 0xb7, 0x24, 0xaa},
+ /*write 89 0400 1415*/
++ {}
++};
++
++static const u8 poxxxx_init_common[][4] = {
++ {0xb3, 0x00, 0x04, 0xcc},
++ {0x00, 0x00, 0x10, 0xdd},
++ {0xb3, 0x00, 0x64, 0xcc},
++ {0x00, 0x00, 0x10, 0xdd},
++ {0xb3, 0x00, 0x65, 0xcc},
++ {0x00, 0x00, 0x10, 0xdd},
++ {0xb3, 0x00, 0x67, 0xcc},
++ {0xb0, 0x03, 0x09, 0xcc},
++ {0xb3, 0x05, 0x00, 0xcc},
++ {0xb3, 0x06, 0x00, 0xcc},
++ {0xb3, 0x5c, 0x01, 0xcc},
++ {0xb3, 0x08, 0x01, 0xcc},
++ {0xb3, 0x09, 0x0c, 0xcc},
++ {0xb3, 0x34, 0x01, 0xcc},
++ {0xb3, 0x35, 0xf6, 0xcc}, /* i2c add: 76 */
++ {0xb3, 0x02, 0xb0, 0xcc},
++ {0xb3, 0x03, 0x18, 0xcc},
++ {0xb3, 0x04, 0x15, 0xcc},
++ {0xb3, 0x20, 0x00, 0xcc},
++ {0xb3, 0x21, 0x00, 0xcc},
++ {0xb3, 0x22, 0x04, 0xcc},
++ {0xb3, 0x23, 0x00, 0xcc},
++ {0xb3, 0x14, 0x00, 0xcc},
++ {0xb3, 0x15, 0x00, 0xcc},
++ {0xb3, 0x16, 0x04, 0xcc},
++ {0xb3, 0x17, 0xff, 0xcc},
++ {0xb3, 0x2c, 0x03, 0xcc},
++ {0xb3, 0x2d, 0x56, 0xcc},
++ {0xb3, 0x2e, 0x02, 0xcc},
++ {0xb3, 0x2f, 0x0a, 0xcc},
++ {0xb3, 0x40, 0x00, 0xcc},
++ {0xb3, 0x41, 0x34, 0xcc},
++ {0xb3, 0x42, 0x01, 0xcc},
++ {0xb3, 0x43, 0xe0, 0xcc},
++ {0xbc, 0x00, 0x71, 0xcc},
++ {0xbc, 0x01, 0x01, 0xcc},
++ {0xb3, 0x01, 0x41, 0xcc},
++ {0xb3, 0x4d, 0x00, 0xcc},
++ {0x00, 0x0b, 0x2a, 0xaa},
++ {0x00, 0x0e, 0x03, 0xaa},
++ {0x00, 0x0f, 0xea, 0xaa},
++ {0x00, 0x12, 0x08, 0xaa},
++ {0x00, 0x1e, 0x06, 0xaa},
++ {0x00, 0x21, 0x00, 0xaa},
++ {0x00, 0x31, 0x1f, 0xaa},
++ {0x00, 0x33, 0x38, 0xaa},
++ {0x00, 0x36, 0xc0, 0xaa},
++ {0x00, 0x37, 0xc8, 0xaa},
++ {0x00, 0x3b, 0x36, 0xaa},
++ {0x00, 0x4b, 0xfe, 0xaa},
++ {0x00, 0x4d, 0x2e, 0xaa},
++ {0x00, 0x51, 0x1c, 0xaa},
++ {0x00, 0x52, 0x01, 0xaa},
++ {0x00, 0x55, 0x0a, 0xaa},
++ {0x00, 0x56, 0x0a, 0xaa},
++ {0x00, 0x57, 0x07, 0xaa},
++ {0x00, 0x58, 0x07, 0xaa},
++ {0x00, 0x59, 0x04, 0xaa},
++ {0x00, 0x70, 0x68, 0xaa},
++ {0x00, 0x71, 0x04, 0xaa},
++ {0x00, 0x72, 0x10, 0xaa},
++ {0x00, 0x80, 0x71, 0xaa},
++ {0x00, 0x81, 0x08, 0xaa},
++ {0x00, 0x82, 0x00, 0xaa},
++ {0x00, 0x83, 0x55, 0xaa},
++ {0x00, 0x84, 0x06, 0xaa},
++ {0x00, 0x85, 0x06, 0xaa},
++ {0x00, 0x8b, 0x25, 0xaa},
++ {0x00, 0x8c, 0x00, 0xaa},
++ {0x00, 0x8d, 0x86, 0xaa},
++ {0x00, 0x8e, 0x82, 0xaa},
++ {0x00, 0x8f, 0x2d, 0xaa},
++ {0x00, 0x90, 0x8b, 0xaa},
++ {0x00, 0x91, 0x81, 0xaa},
++ {0x00, 0x92, 0x81, 0xaa},
++ {0x00, 0x93, 0x23, 0xaa},
++ {0x00, 0xa3, 0x2a, 0xaa},
++ {0x00, 0xa4, 0x03, 0xaa},
++ {0x00, 0xa5, 0xea, 0xaa},
++ {0x00, 0xb0, 0x68, 0xaa},
++ {0x00, 0xbc, 0x04, 0xaa},
++ {0x00, 0xbe, 0x3b, 0xaa},
++ {0x00, 0x4e, 0x40, 0xaa},
++ {0x00, 0x06, 0x04, 0xaa},
++ {0x00, 0x07, 0x03, 0xaa},
++ {0x00, 0xcd, 0x18, 0xaa},
++ {0x00, 0x28, 0x03, 0xaa},
++ {0x00, 0x29, 0xef, 0xaa},
++/* reinit on alt 2 (qvga) or alt7 (vga) */
++ {0xb3, 0x05, 0x00, 0xcc},
++ {0xb3, 0x06, 0x00, 0xcc},
++ {0xb8, 0x00, 0x01, 0xcc},
++
++ {0x00, 0x1d, 0x85, 0xaa},
++ {0x00, 0x1e, 0xc6, 0xaa},
++ {0x00, 0x00, 0x40, 0xdd},
++ {0x00, 0x1d, 0x05, 0xaa},
++
++ {0x00, 0xd6, 0x22, 0xaa}, /* gamma 0 */
++ {0x00, 0x73, 0x00, 0xaa},
++ {0x00, 0x74, 0x0a, 0xaa},
++ {0x00, 0x75, 0x16, 0xaa},
++ {0x00, 0x76, 0x25, 0xaa},
++ {0x00, 0x77, 0x34, 0xaa},
++ {0x00, 0x78, 0x49, 0xaa},
++ {0x00, 0x79, 0x5a, 0xaa},
++ {0x00, 0x7a, 0x7f, 0xaa},
++ {0x00, 0x7b, 0x9b, 0xaa},
++ {0x00, 0x7c, 0xba, 0xaa},
++ {0x00, 0x7d, 0xd4, 0xaa},
++ {0x00, 0x7e, 0xea, 0xaa},
++
++ {0x00, 0xd6, 0x62, 0xaa}, /* gamma 1 */
++ {0x00, 0x73, 0x00, 0xaa},
++ {0x00, 0x74, 0x0a, 0xaa},
++ {0x00, 0x75, 0x16, 0xaa},
++ {0x00, 0x76, 0x25, 0xaa},
++ {0x00, 0x77, 0x34, 0xaa},
++ {0x00, 0x78, 0x49, 0xaa},
++ {0x00, 0x79, 0x5a, 0xaa},
++ {0x00, 0x7a, 0x7f, 0xaa},
++ {0x00, 0x7b, 0x9b, 0xaa},
++ {0x00, 0x7c, 0xba, 0xaa},
++ {0x00, 0x7d, 0xd4, 0xaa},
++ {0x00, 0x7e, 0xea, 0xaa},
++
++ {0x00, 0xd6, 0xa2, 0xaa}, /* gamma 2 */
++ {0x00, 0x73, 0x00, 0xaa},
++ {0x00, 0x74, 0x0a, 0xaa},
++ {0x00, 0x75, 0x16, 0xaa},
++ {0x00, 0x76, 0x25, 0xaa},
++ {0x00, 0x77, 0x34, 0xaa},
++ {0x00, 0x78, 0x49, 0xaa},
++ {0x00, 0x79, 0x5a, 0xaa},
++ {0x00, 0x7a, 0x7f, 0xaa},
++ {0x00, 0x7b, 0x9b, 0xaa},
++ {0x00, 0x7c, 0xba, 0xaa},
++ {0x00, 0x7d, 0xd4, 0xaa},
++ {0x00, 0x7e, 0xea, 0xaa},
++
++ {0x00, 0xaa, 0xff, 0xaa}, /* back light comp */
++ {0x00, 0xc4, 0x03, 0xaa},
++ {0x00, 0xc5, 0x19, 0xaa},
++ {0x00, 0xc6, 0x03, 0xaa},
++ {0x00, 0xc7, 0x91, 0xaa},
++ {0x00, 0xc8, 0x01, 0xaa},
++ {0x00, 0xc9, 0xdd, 0xaa},
++ {0x00, 0xca, 0x02, 0xaa},
++ {0x00, 0xcb, 0x37, 0xaa},
++
++/* read d1 */
++ {0x00, 0xd1, 0x3c, 0xaa},
++ {0x00, 0xb8, 0x28, 0xaa},
++ {0x00, 0xb9, 0x1e, 0xaa},
++ {0x00, 0xb6, 0x14, 0xaa},
++ {0x00, 0xb7, 0x0f, 0xaa},
++ {0x00, 0x5c, 0x10, 0xaa},
++ {0x00, 0x5d, 0x18, 0xaa},
++ {0x00, 0x5e, 0x24, 0xaa},
++ {0x00, 0x5f, 0x24, 0xaa},
++ {0x00, 0x86, 0x1a, 0xaa},
++ {0x00, 0x60, 0x00, 0xaa},
++ {0x00, 0x61, 0x1b, 0xaa},
++ {0x00, 0x62, 0x30, 0xaa},
++ {0x00, 0x63, 0x40, 0xaa},
++ {0x00, 0x87, 0x1a, 0xaa},
++ {0x00, 0x64, 0x00, 0xaa},
++ {0x00, 0x65, 0x08, 0xaa},
++ {0x00, 0x66, 0x10, 0xaa},
++ {0x00, 0x67, 0x20, 0xaa},
++ {0x00, 0x88, 0x10, 0xaa},
++ {0x00, 0x68, 0x00, 0xaa},
++ {0x00, 0x69, 0x08, 0xaa},
++ {0x00, 0x6a, 0x0f, 0xaa},
++ {0x00, 0x6b, 0x0f, 0xaa},
++ {0x00, 0x89, 0x07, 0xaa},
++ {0x00, 0xd5, 0x4c, 0xaa},
++ {0x00, 0x0a, 0x00, 0xaa},
++ {0x00, 0x0b, 0x2a, 0xaa},
++ {0x00, 0x0e, 0x03, 0xaa},
++ {0x00, 0x0f, 0xea, 0xaa},
++ {0x00, 0xa2, 0x00, 0xaa},
++ {0x00, 0xa3, 0x2a, 0xaa},
++ {0x00, 0xa4, 0x03, 0xaa},
++ {0x00, 0xa5, 0xea, 0xaa},
++ {}
++};
++static const u8 poxxxx_initVGA[][4] = {
++ {0x00, 0x20, 0x11, 0xaa},
++ {0x00, 0x33, 0x38, 0xaa},
++ {0x00, 0xbb, 0x0d, 0xaa},
++ {0xb3, 0x22, 0x01, 0xcc},
++ {0xb3, 0x23, 0xe0, 0xcc},
++ {0xb3, 0x16, 0x02, 0xcc},
++ {0xb3, 0x17, 0x7f, 0xcc},
++ {0xb3, 0x02, 0xb0, 0xcc},
++ {0xb3, 0x06, 0x00, 0xcc},
++ {0xb3, 0x5c, 0x01, 0xcc},
++ {0x00, 0x04, 0x06, 0xaa},
++ {0x00, 0x05, 0x3f, 0xaa},
++ {0x00, 0x04, 0x00, 0xdd}, /* delay 1s */
++ {}
++};
++static const u8 poxxxx_initQVGA[][4] = {
++ {0x00, 0x20, 0x33, 0xaa},
++ {0x00, 0x33, 0x38, 0xaa},
++ {0x00, 0xbb, 0x0d, 0xaa},
++ {0xb3, 0x22, 0x00, 0xcc},
++ {0xb3, 0x23, 0xf0, 0xcc},
++ {0xb3, 0x16, 0x01, 0xcc},
++ {0xb3, 0x17, 0x3f, 0xcc},
++ {0xb3, 0x02, 0xb0, 0xcc},
++ {0xb3, 0x06, 0x01, 0xcc},
++ {0xb3, 0x5c, 0x00, 0xcc},
++ {0x00, 0x04, 0x06, 0xaa},
++ {0x00, 0x05, 0x3f, 0xaa},
++ {0x00, 0x04, 0x00, 0xdd}, /* delay 1s */
++ {}
++};
++static const u8 poxxxx_init_end_1[][4] = {
++ {0x00, 0x47, 0x25, 0xaa},
++ {0x00, 0x48, 0x80, 0xaa},
++ {0x00, 0x49, 0x1f, 0xaa},
++ {0x00, 0x4a, 0x40, 0xaa},
++ {0x00, 0x44, 0x40, 0xaa},
++ {0x00, 0xab, 0x4a, 0xaa},
++ {0x00, 0xb1, 0x00, 0xaa},
++ {0x00, 0xb2, 0x04, 0xaa},
++ {0x00, 0xb3, 0x08, 0xaa},
++ {0x00, 0xb4, 0x0b, 0xaa},
++ {0x00, 0xb5, 0x0d, 0xaa},
++ {0x00, 0x59, 0x7e, 0xaa}, /* sharpness */
++ {0x00, 0x16, 0x00, 0xaa}, /* white balance */
++ {0x00, 0x18, 0x00, 0xaa},
++#if 0
++/* read d1 */
++ {0x00, 0xd1, 0x3c, 0xaa},
++ {0x00, 0x94, 0x46, 0xaa}, /* colors */
++ {0x00, 0x95, 0x51, 0xaa},
++ {0x00, 0x98, 0x88, 0xaa}, /* contrast */
++ {0x00, 0x99, 0x93, 0xaa}, /* brightness */
++#endif
++ {}
++};
++static const u8 poxxxx_init_end_2[][4] = {
++ {0x00, 0x1d, 0x85, 0xaa},
++ {0x00, 0x1e, 0x06, 0xaa},
++ {0x00, 0x1d, 0x05, 0xaa},
++ {}
+ };
+
+ struct sensor_info {
+@@ -2420,33 +2768,89 @@ struct sensor_info {
+ u8 op;
+ };
+
+-static const struct sensor_info sensor_info_data[] = {
+-/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */
++/* probe values */
++static const struct sensor_info vc0321_probe_data[] = {
++/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */
++/* 0 OV9640 */
+ {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
++/* 1 ICM108T (may respond on IdAdd == 0x83 - tested in vc032x_probe_sensor) */
+ {-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01},
+-/* (tested in vc032x_probe_sensor) */
+-/* {-1, 0x80 | 0x20, 0x83, 0x0000, 0x24, 0x25, 0x01}, */
+- {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01},
++/* 2 PO2130 (may detect PO3130NC - tested in vc032x_probe_sensor)*/
++ {-1, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01},
++/* 3 MI1310 */
++ {-1, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01},
++/* 4 MI360 - tested in vc032x_probe_sensor */
++/* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */
++/* 5 7131R */
++ {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01},
++/* 6 OV7649 */
++ {-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05},
++/* 7 PAS302BCW */
++ {-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05},
++/* 8 OV7660 */
++ {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05},
++/* 9 PO3130NC - (tested in vc032x_probe_sensor) */
++/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, */
++/* 10 PO1030KC */
++ {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
++/* 11 MI1310_SOC */
+ {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01},
+-/* (tested in vc032x_probe_sensor) */
++/* 12 OV9650 */
++ {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
++/* 13 S5K532 */
++ {-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01},
++/* 14 MI360_SOC - ??? */
++/* 15 PO1200N */
++ {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01},
++/* 16 PO3030K */
++ {-1, 0x80 | 0x18, 0x00, 0x0000, 0x24, 0x25, 0x01},
++/* 17 PO2030 */
++ {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
++/* ?? */
++ {-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01},
++ {SENSOR_MI1320, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x65, 0x01},
++};
++static const struct sensor_info vc0323_probe_data[] = {
++/* sensorId, I2cAdd, IdAdd, VpId, m1, m2, op */
++/* 0 OV9640 */
++ {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
++/* 1 ICM108T (may respond on IdAdd == 0x83 - tested in vc032x_probe_sensor) */
++ {-1, 0x80 | 0x20, 0x82, 0x0000, 0x24, 0x25, 0x01},
++/* 2 PO2130 (may detect PO3130NC - tested in vc032x_probe_sensor)*/
++ {-1, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01},
++/* 3 MI1310 */
++ {-1, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01},
++/* 4 MI360 - tested in vc032x_probe_sensor */
+ /* {SENSOR_MI0360, 0x80 | 0x5d, 0x00, 0x8243, 0x24, 0x25, 0x01}, */
++/* 5 7131R */
+ {SENSOR_HV7131R, 0x80 | 0x11, 0x00, 0x0209, 0x24, 0x25, 0x01},
++/* 6 OV7649 */
+ {-1, 0x80 | 0x21, 0x0a, 0x0000, 0x21, 0x20, 0x05},
++/* 7 PAS302BCW */
+ {-1, 0x80 | 0x40, 0x00, 0x0000, 0x20, 0x22, 0x05},
++/* 8 OV7660 */
+ {SENSOR_OV7660, 0x80 | 0x21, 0x0a, 0x7660, 0x26, 0x26, 0x05},
+-/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x0000, 0x24, 0x25, 0x01}, */
++/* 9 PO3130NC - (tested in vc032x_probe_sensor) */
++/* {SENSOR_PO3130NC, 0x80 | 0x76, 0x00, 0x3130, 0x24, 0x25, 0x01}, */
++/* 10 PO1030KC */
+ {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
+-/* {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x0000, 0x24, 0x25, 0x01}, */
+-/* {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05}, */
++/* 11 MI1310_SOC */
++ {SENSOR_MI1310_SOC, 0x80 | 0x5d, 0x00, 0x143a, 0x24, 0x25, 0x01},
++/* 12 OV9650 */
++ {-1, 0x80 | 0x30, 0x0a, 0x0000, 0x25, 0x24, 0x05},
++/* 13 S5K532 */
+ {-1, 0x80 | 0x11, 0x39, 0x0000, 0x24, 0x25, 0x01},
++/* 14 MI360_SOC - ??? */
++/* 15 PO1200N */
+ {SENSOR_PO1200, 0x80 | 0x5c, 0x00, 0x1200, 0x67, 0x67, 0x01},
++/* 16 ?? */
+ {-1, 0x80 | 0x2d, 0x00, 0x0000, 0x65, 0x67, 0x01},
++/* 17 PO2030 */
+ {-1, 0x80 | 0x6e, 0x00, 0x0000, 0x24, 0x25, 0x01},
++/* ?? */
+ {-1, 0x80 | 0x56, 0x01, 0x0000, 0x64, 0x67, 0x01},
+ {SENSOR_MI1320_SOC, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x67, 0x01},
+-/*fixme: previously detected?*/
+- {SENSOR_MI1320, 0x80 | 0x48, 0x00, 0x148c, 0x64, 0x65, 0x01},
+-/*fixme: not in the ms-win probe - may be found before?*/
++/*fixme: not in the ms-win probe - may be found before? */
+ {SENSOR_OV7670, 0x80 | 0x21, 0x0a, 0x7673, 0x66, 0x67, 0x05},
+ };
+
+@@ -2520,20 +2924,31 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct usb_device *dev = gspca_dev->dev;
+- int i;
++ int i, n;
+ u16 value;
+ const struct sensor_info *ptsensor_info;
+
+ /*fixme: should also check the other sensor (back mi1320_soc, front mc501cb)*/
+ if (sd->flags & FL_SAMSUNG) {
+ reg_w(dev, 0xa0, 0x01, 0xb301);
++#if 1
+ reg_w(dev, 0x89, 0xf0ff, 0xffff); /* select the back sensor */
++#else
++ reg_w(dev, 0x89, 0xf3ff, 0xffff); /* select the front sensor */
++#endif
+ }
+
+ reg_r(gspca_dev, 0xa1, 0xbfcf, 1);
+- PDEBUG(D_PROBE, "check sensor header %02x", gspca_dev->usb_buf[0]);
+- for (i = 0; i < ARRAY_SIZE(sensor_info_data); i++) {
+- ptsensor_info = &sensor_info_data[i];
++ PDEBUG(D_PROBE, "vc032%d check sensor header %02x",
++ sd->bridge == BRIDGE_VC0321 ? 1 : 3, gspca_dev->usb_buf[0]);
++ if (sd->bridge == BRIDGE_VC0321) {
++ ptsensor_info = vc0321_probe_data;
++ n = ARRAY_SIZE(vc0321_probe_data);
++ } else {
++ ptsensor_info = vc0323_probe_data;
++ n = ARRAY_SIZE(vc0323_probe_data);
++ }
++ for (i = 0; i < n; i++) {
+ reg_w(dev, 0xa0, 0x02, 0xb334);
+ reg_w(dev, 0xa0, ptsensor_info->m1, 0xb300);
+ reg_w(dev, 0xa0, ptsensor_info->m2, 0xb300);
+@@ -2551,13 +2966,15 @@ static int vc032x_probe_sensor(struct gspca_dev *gspca_dev)
+ return ptsensor_info->sensorId;
+
+ switch (value) {
++ case 0x3130:
++ return SENSOR_PO3130NC;
+ case 0x7673:
+ return SENSOR_OV7670;
+ case 0x8243:
+ return SENSOR_MI0360;
+ }
+-/*fixme: should return here*/
+ }
++ ptsensor_info++;
+ }
+ return -1;
+ }
+@@ -2619,7 +3036,7 @@ static void usb_exchange(struct gspca_dev *gspca_dev,
+ i2c_write(gspca_dev, data[i][0], &data[i][1], 2);
+ break;
+ case 0xdd:
+- msleep(data[i][2] + 10);
++ msleep(data[i][1] * 256 + data[i][2] + 10);
+ break;
+ }
+ i++;
+@@ -2646,12 +3063,20 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ 64, /* OV7670 6 */
+ 128, /* PO1200 7 */
+ 128, /* PO3130NC 8 */
++ 128, /* POxxxx 9 */
+ };
+
+ cam = &gspca_dev->cam;
+ sd->bridge = id->driver_info >> 8;
+ sd->flags = id->driver_info & 0xff;
+- sensor = vc032x_probe_sensor(gspca_dev);
++#if 0
++ vc0321_reset(gspca_dev);
++#endif
++ if (id->idVendor == 0x046d &&
++ (id->idProduct == 0x0892 || id->idProduct == 0x0896))
++ sensor = SENSOR_POxxxx;
++ else
++ sensor = vc032x_probe_sensor(gspca_dev);
+ switch (sensor) {
+ case -1:
+ PDEBUG(D_PROBE, "Unknown sensor...");
+@@ -2684,6 +3109,9 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ case SENSOR_PO3130NC:
+ PDEBUG(D_PROBE, "Find Sensor PO3130NC");
+ break;
++ case SENSOR_POxxxx:
++ PDEBUG(D_PROBE, "Sensor POxxxx");
++ break;
+ }
+ sd->sensor = sensor;
+
+@@ -2712,28 +3140,19 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ }
+ cam->npkt = npkt[sd->sensor];
+
++ sd->brightness = BRIGHTNESS_DEF;
++ sd->contrast = CONTRAST_DEF;
++ sd->colors = COLOR_DEF;
+ sd->hflip = HFLIP_DEF;
+ sd->vflip = VFLIP_DEF;
+- if (sd->sensor == SENSOR_OV7670)
+- sd->flags |= FL_HFLIP | FL_VFLIP;
+ sd->lightfreq = FREQ_DEF;
+- if (sd->sensor != SENSOR_OV7670)
+- gspca_dev->ctrl_dis = (1 << LIGHTFREQ_IDX);
+- switch (sd->sensor) {
+- case SENSOR_MI1310_SOC:
+- case SENSOR_MI1320_SOC:
+- case SENSOR_OV7660:
+- case SENSOR_OV7670:
+- case SENSOR_PO1200:
+- break;
+- default:
+- gspca_dev->ctrl_dis = (1 << HFLIP_IDX)
+- | (1 << VFLIP_IDX);
+- break;
+- }
+-
+ sd->sharpness = SHARPNESS_DEF;
+
++ gspca_dev->ctrl_dis = ctrl_dis[sd->sensor];
++
++ if (sd->sensor == SENSOR_OV7670)
++ sd->flags |= FL_HFLIP | FL_VFLIP;
++
+ if (sd->bridge == BRIDGE_VC0321) {
+ reg_r(gspca_dev, 0x8a, 0, 3);
+ reg_w(dev, 0x87, 0x00, 0x0f0f);
+@@ -2747,10 +3166,55 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ /* this function is called at probe and resume time */
+ static int sd_init(struct gspca_dev *gspca_dev)
+ {
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (sd->sensor == SENSOR_POxxxx) {
++ reg_r(gspca_dev, 0xa1, 0xb300, 1);
++ if (gspca_dev->usb_buf[0] != 0) {
++ reg_w(gspca_dev->dev, 0xa0, 0x26, 0xb300);
++ reg_w(gspca_dev->dev, 0xa0, 0x04, 0xb300);
++ reg_w(gspca_dev->dev, 0xa0, 0x00, 0xb300);
++ }
++ }
+ return 0;
+ }
+
+-/* some sensors only */
++static void setbrightness(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ u8 data;
++
++ if (gspca_dev->ctrl_dis & (1 << BRIGHTNESS_IDX))
++ return;
++ data = sd->brightness;
++ if (data >= 0x80)
++ data &= 0x7f;
++ else
++ data = 0xff ^ data;
++ i2c_write(gspca_dev, 0x98, &data, 1);
++}
++
++static void setcontrast(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (gspca_dev->ctrl_dis & (1 << CONTRAST_IDX))
++ return;
++ i2c_write(gspca_dev, 0x99, &sd->contrast, 1);
++}
++
++static void setcolors(struct gspca_dev *gspca_dev)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++ u8 data;
++
++ if (gspca_dev->ctrl_dis & (1 << COLORS_IDX))
++ return;
++ data = sd->colors - (sd->colors >> 3) - 1;
++ i2c_write(gspca_dev, 0x94, &data, 1);
++ i2c_write(gspca_dev, 0x95, &sd->colors, 1);
++}
++
+ static void sethvflip(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+@@ -2764,6 +3228,7 @@ static void sethvflip(struct gspca_dev *gspca_dev)
+ vflip = !vflip;
+ switch (sd->sensor) {
+ case SENSOR_MI1310_SOC:
++ case SENSOR_MI1320:
+ case SENSOR_MI1320_SOC:
+ data[0] = data[1] = 0; /* select page 0 */
+ i2c_write(gspca_dev, 0xf0, data, 2);
+@@ -2801,18 +3266,29 @@ static void setlightfreq(struct gspca_dev *gspca_dev)
+ usb_exchange(gspca_dev, ov7660_freq_tb[sd->lightfreq]);
+ }
+
+-/* po1200 only */
+ static void setsharpness(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ u8 data;
+
+- if (sd->sensor != SENSOR_PO1200)
+- return;
+- data = 0;
+- i2c_write(gspca_dev, 0x03, &data, 1);
+- data = 0xb5 + sd->sharpness * 3;
+- i2c_write(gspca_dev, 0x61, &data, 1);
++ switch (sd->sensor) {
++ case SENSOR_PO1200:
++ data = 0;
++ i2c_write(gspca_dev, 0x03, &data, 1);
++ if (sd->sharpness < 0)
++ data = 0x6a;
++ else
++ data = 0xb5 + sd->sharpness * 3;
++ i2c_write(gspca_dev, 0x61, &data, 1);
++ break;
++ case SENSOR_POxxxx:
++ if (sd->sharpness < 0)
++ data = 0x7e; /* def = max */
++ else
++ data = 0x60 + sd->sharpness * 0x0f;
++ i2c_write(gspca_dev, 0x59, &data, 1);
++ break;
++ }
+ }
+
+ static int sd_start(struct gspca_dev *gspca_dev)
+@@ -2922,12 +3398,27 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ usb_exchange(gspca_dev, init);
+ init = po3130_rundata;
+ break;
+- default:
+-/* case SENSOR_PO1200: */
++ case SENSOR_PO1200:
+ GammaT = po1200_gamma;
+ MatrixT = po1200_matrix;
+ init = po1200_initVGA_data;
+ break;
++ default:
++/* case SENSOR_POxxxx: */
++ usb_exchange(gspca_dev, poxxxx_init_common);
++ if (mode)
++ init = poxxxx_initQVGA;
++ else
++ init = poxxxx_initVGA;
++ usb_exchange(gspca_dev, init);
++ reg_r(gspca_dev, 0x8c, 0x0000, 3);
++ reg_w(gspca_dev->dev, 0xa0,
++ gspca_dev->usb_buf[2] & 1 ? 0 : 1,
++ 0xb35c);
++ msleep(300);
++/*fixme: i2c read 04 and 05*/
++ init = poxxxx_init_end_1;
++ break;
+ }
+ usb_exchange(gspca_dev, init);
+ if (GammaT && MatrixT) {
+@@ -2936,7 +3427,6 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ put_tab_to_reg(gspca_dev, GammaT, 17, 0xb86c);
+ put_tab_to_reg(gspca_dev, MatrixT, 9, 0xb82c);
+
+- /* set the led on 0x0892 0x0896 */
+ switch (sd->sensor) {
+ case SENSOR_PO1200:
+ case SENSOR_HV7131R:
+@@ -2945,16 +3435,22 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ case SENSOR_MI1310_SOC:
+ reg_w(gspca_dev->dev, 0x89, 0x058c, 0x0000);
+ break;
+- default:
+- if (!(sd->flags & FL_SAMSUNG))
+- reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff);
+- break;
+ }
+ msleep(100);
+ setsharpness(gspca_dev);
+ sethvflip(gspca_dev);
+ setlightfreq(gspca_dev);
+ }
++ if (sd->sensor == SENSOR_POxxxx) {
++ setcolors(gspca_dev);
++ setbrightness(gspca_dev);
++ setcontrast(gspca_dev);
++
++ /* led on */
++ msleep(80);
++ reg_w(gspca_dev->dev, 0x89, 0xffff, 0xfdff);
++ usb_exchange(gspca_dev, poxxxx_init_end_2);
++ }
+ return 0;
+ }
+
+@@ -2963,10 +3459,17 @@ static void sd_stopN(struct gspca_dev *gspca_dev)
+ struct usb_device *dev = gspca_dev->dev;
+ struct sd *sd = (struct sd *) gspca_dev;
+
+- if (sd->sensor == SENSOR_MI1310_SOC)
++ switch (sd->sensor) {
++ case SENSOR_MI1310_SOC:
+ reg_w(dev, 0x89, 0x058c, 0x00ff);
+- else if (!(sd->flags & FL_SAMSUNG))
+- reg_w(dev, 0x89, 0xffff, 0xffff);
++ break;
++ case SENSOR_POxxxx:
++ return;
++ default:
++ if (!(sd->flags & FL_SAMSUNG))
++ reg_w(dev, 0x89, 0xffff, 0xffff);
++ break;
++ }
+ reg_w(dev, 0xa0, 0x01, 0xb301);
+ reg_w(dev, 0xa0, 0x09, 0xb003);
+ }
+@@ -2984,10 +3487,15 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
+ reg_w(dev, 0x89, 0x058c, 0x00ff);
+ else if (!(sd->flags & FL_SAMSUNG))
+ reg_w(dev, 0x89, 0xffff, 0xffff);
++
++ if (sd->sensor == SENSOR_POxxxx) {
++ reg_w(dev, 0xa0, 0x26, 0xb300);
++ reg_w(dev, 0xa0, 0x04, 0xb300);
++ reg_w(dev, 0xa0, 0x00, 0xb300);
++ }
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame, /* target */
+ u8 *data, /* isoc packet */
+ int len) /* iso pkt length */
+ {
+@@ -2996,21 +3504,83 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ if (data[0] == 0xff && data[1] == 0xd8) {
+ PDEBUG(D_PACK,
+ "vc032x header packet found len %d", len);
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+- data, 0);
++ gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
+ data += sd->image_offset;
+ len -= sd->image_offset;
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
+- data, len);
++ gspca_frame_add(gspca_dev, FIRST_PACKET, data, len);
+ return;
+ }
+
+ /* The vc0321 sends some additional data after sending the complete
+ * frame, we ignore this. */
+- if (sd->bridge == BRIDGE_VC0321
+- && len > frame->v4l2_buf.length - (frame->data_end - frame->data))
+- len = frame->v4l2_buf.length - (frame->data_end - frame->data);
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
++ if (sd->bridge == BRIDGE_VC0321) {
++ struct gspca_frame *frame;
++ int l;
++
++ frame = gspca_get_i_frame(gspca_dev);
++ if (frame == NULL) {
++ gspca_dev->last_packet_type = DISCARD_PACKET;
++ return;
++ }
++ l = frame->data_end - frame->data;
++ if (len > frame->v4l2_buf.length - l)
++ len = frame->v4l2_buf.length - l;
++ }
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
++}
++
++static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->brightness = val;
++ if (gspca_dev->streaming)
++ setbrightness(gspca_dev);
++ return 0;
++}
++
++static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->brightness;
++ return 0;
++}
++
++static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->contrast = val;
++ if (gspca_dev->streaming)
++ setcontrast(gspca_dev);
++ return 0;
++}
++
++static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->contrast;
++ return 0;
++}
++
++static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ sd->colors = val;
++ if (gspca_dev->streaming)
++ setcolors(gspca_dev);
++ return 0;
++}
++
++static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ *val = sd->colors;
++ return 0;
+ }
+
+ static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
+@@ -3092,6 +3662,8 @@ static int sd_querymenu(struct gspca_dev *gspca_dev,
+
+ switch (menu->id) {
+ case V4L2_CID_POWER_LINE_FREQUENCY:
++ if (menu->index >= ARRAY_SIZE(freq_nm))
++ break;
+ strcpy((char *) menu->name, freq_nm[menu->index]);
+ return 0;
+ }
+diff --git a/drivers/media/video/gspca/w996Xcf.c b/drivers/media/video/gspca/w996Xcf.c
+new file mode 100644
+index 0000000..2fffe20
+--- /dev/null
++++ b/drivers/media/video/gspca/w996Xcf.c
+@@ -0,0 +1,609 @@
++/**
++ *
++ * GSPCA sub driver for W996[78]CF JPEG USB Dual Mode Camera Chip.
++ *
++ * Copyright (C) 2009 Hans de Goede <hdegoede@redhat.com>
++ *
++ * This module is adapted from the in kernel v4l1 w9968cf driver:
++ *
++ * Copyright (C) 2002-2004 by Luca Risolia <luca.risolia@studio.unibo.it>
++ *
++ * 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
++ * 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
++ *
++ */
++
++/* Note this is not a stand alone driver, it gets included in ov519.c, this
++ is a bit of a hack, but it needs the driver code for a lot of different
++ ov sensors which is already present in ov519.c (the old v4l1 driver used
++ the ovchipcam framework). When we have the time we really should move
++ the sensor drivers to v4l2 sub drivers, and properly split of this
++ driver from ov519.c */
++
++/* The CONEX_CAM define for jpeg.h needs renaming, now its used here too */
++#define CONEX_CAM
++#include "jpeg.h"
++
++#define W9968CF_I2C_BUS_DELAY 4 /* delay in us for I2C bit r/w operations */
++
++#define Y_QUANTABLE (sd->jpeg_hdr + JPEG_QT0_OFFSET)
++#define UV_QUANTABLE (sd->jpeg_hdr + JPEG_QT1_OFFSET)
++
++static const struct v4l2_pix_format w9968cf_vga_mode[] = {
++ {160, 120, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
++ .bytesperline = 160 * 2,
++ .sizeimage = 160 * 120 * 2,
++ .colorspace = V4L2_COLORSPACE_JPEG},
++ {176, 144, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE,
++ .bytesperline = 176 * 2,
++ .sizeimage = 176 * 144 * 2,
++ .colorspace = V4L2_COLORSPACE_JPEG},
++ {320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
++ .bytesperline = 320 * 2,
++ .sizeimage = 320 * 240 * 2,
++ .colorspace = V4L2_COLORSPACE_JPEG},
++ {352, 288, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
++ .bytesperline = 352 * 2,
++ .sizeimage = 352 * 288 * 2,
++ .colorspace = V4L2_COLORSPACE_JPEG},
++ {640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
++ .bytesperline = 640 * 2,
++ .sizeimage = 640 * 480 * 2,
++ .colorspace = V4L2_COLORSPACE_JPEG},
++};
++
++static int reg_w(struct sd *sd, __u16 index, __u16 value);
++
++/*--------------------------------------------------------------------------
++ Write 64-bit data to the fast serial bus registers.
++ Return 0 on success, -1 otherwise.
++ --------------------------------------------------------------------------*/
++static int w9968cf_write_fsb(struct sd *sd, u16* data)
++{
++ struct usb_device* udev = sd->gspca_dev.dev;
++ u16 value;
++ int ret;
++
++ value = *data++;
++ memcpy(sd->gspca_dev.usb_buf, data, 6);
++
++ ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), 0,
++ USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE,
++ value, 0x06, sd->gspca_dev.usb_buf, 6, 500);
++ if (ret < 0) {
++ PDEBUG(D_ERR, "Write FSB registers failed (%d)", ret);
++ return ret;
++ }
++
++ return 0;
++}
++
++/*--------------------------------------------------------------------------
++ Write data to the serial bus control register.
++ Return 0 on success, a negative number otherwise.
++ --------------------------------------------------------------------------*/
++static int w9968cf_write_sb(struct sd *sd, u16 value)
++{
++ int ret;
++
++ /* We don't use reg_w here, as that would cause all writes when
++ bitbanging i2c to be logged, making the logs impossible to read */
++ ret = usb_control_msg(sd->gspca_dev.dev,
++ usb_sndctrlpipe(sd->gspca_dev.dev, 0),
++ 0,
++ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ value, 0x01, NULL, 0, 500);
++
++ udelay(W9968CF_I2C_BUS_DELAY);
++
++ if (ret < 0) {
++ PDEBUG(D_ERR, "Write SB reg [01] %04x failed", value);
++ return ret;
++ }
++
++ return 0;
++}
++
++/*--------------------------------------------------------------------------
++ Read data from the serial bus control register.
++ Return 0 on success, a negative number otherwise.
++ --------------------------------------------------------------------------*/
++static int w9968cf_read_sb(struct sd *sd)
++{
++ int ret;
++
++ /* We don't use reg_r here, as the w9968cf is special and has 16
++ bit registers instead of 8 bit */
++ ret = usb_control_msg(sd->gspca_dev.dev,
++ usb_rcvctrlpipe(sd->gspca_dev.dev, 0),
++ 1,
++ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
++ 0, 0x01, sd->gspca_dev.usb_buf, 2, 500);
++ if (ret >= 0)
++ ret = sd->gspca_dev.usb_buf[0] |
++ (sd->gspca_dev.usb_buf[1] << 8);
++ else
++ PDEBUG(D_ERR, "Read SB reg [01] failed");
++
++ udelay(W9968CF_I2C_BUS_DELAY);
++
++ return ret;
++}
++
++/*--------------------------------------------------------------------------
++ Upload quantization tables for the JPEG compression.
++ This function is called by w9968cf_start_transfer().
++ Return 0 on success, a negative number otherwise.
++ --------------------------------------------------------------------------*/
++static int w9968cf_upload_quantizationtables(struct sd *sd)
++{
++ u16 a, b;
++ int ret = 0, i, j;
++
++ ret += reg_w(sd, 0x39, 0x0010); /* JPEG clock enable */
++
++ for (i = 0, j = 0; i < 32; i++, j += 2) {
++ a = Y_QUANTABLE[j] | ((unsigned)(Y_QUANTABLE[j+1]) << 8);
++ b = UV_QUANTABLE[j] | ((unsigned)(UV_QUANTABLE[j+1]) << 8);
++ ret += reg_w(sd, 0x40+i, a);
++ ret += reg_w(sd, 0x60+i, b);
++ }
++ ret += reg_w(sd, 0x39, 0x0012); /* JPEG encoder enable */
++
++ return ret;
++}
++
++/****************************************************************************
++ * Low-level I2C I/O functions. *
++ * The adapter supports the following I2C transfer functions: *
++ * i2c_adap_fastwrite_byte_data() (at 400 kHz bit frequency only) *
++ * i2c_adap_read_byte_data() *
++ * i2c_adap_read_byte() *
++ ****************************************************************************/
++
++static int w9968cf_smbus_start(struct sd *sd)
++{
++ int ret = 0;
++
++ ret += w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */
++ ret += w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */
++
++ return ret;
++}
++
++static int w9968cf_smbus_stop(struct sd *sd)
++{
++ int ret = 0;
++
++ ret += w9968cf_write_sb(sd, 0x0010); /* SDE=1, SDA=0, SCL=0 */
++ ret += w9968cf_write_sb(sd, 0x0011); /* SDE=1, SDA=0, SCL=1 */
++ ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */
++
++ return ret;
++}
++
++static int w9968cf_smbus_write_byte(struct sd *sd, u8 v)
++{
++ u8 bit;
++ int ret = 0, sda;
++
++ for (bit = 0 ; bit < 8 ; bit++) {
++ sda = (v & 0x80) ? 2 : 0;
++ v <<= 1;
++ /* SDE=1, SDA=sda, SCL=0 */
++ ret += w9968cf_write_sb(sd, 0x10 | sda);
++ /* SDE=1, SDA=sda, SCL=1 */
++ ret += w9968cf_write_sb(sd, 0x11 | sda);
++ /* SDE=1, SDA=sda, SCL=0 */
++ ret += w9968cf_write_sb(sd, 0x10 | sda);
++ }
++
++ return ret;
++}
++
++static int w9968cf_smbus_read_byte(struct sd *sd, u8* v)
++{
++ u8 bit;
++ int ret = 0;
++
++ /* No need to ensure SDA is high as we are always called after
++ read_ack which ends with SDA high */
++ *v = 0;
++ for (bit = 0 ; bit < 8 ; bit++) {
++ *v <<= 1;
++ /* SDE=1, SDA=1, SCL=1 */
++ ret += w9968cf_write_sb(sd, 0x0013);
++ *v |= (w9968cf_read_sb(sd) & 0x0008) ? 1 : 0;
++ /* SDE=1, SDA=1, SCL=0 */
++ ret += w9968cf_write_sb(sd, 0x0012);
++ }
++
++ return ret;
++}
++
++static int w9968cf_smbus_write_nack(struct sd *sd)
++{
++ int ret = 0;
++
++ /* No need to ensure SDA is high as we are always called after
++ read_byte which ends with SDA high */
++ ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */
++ ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */
++
++ return ret;
++}
++
++static int w9968cf_smbus_read_ack(struct sd *sd)
++{
++ int ret = 0, sda;
++
++ /* Ensure SDA is high before raising clock to avoid a spurious stop */
++ ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */
++ ret += w9968cf_write_sb(sd, 0x0013); /* SDE=1, SDA=1, SCL=1 */
++ sda = w9968cf_read_sb(sd);
++ ret += w9968cf_write_sb(sd, 0x0012); /* SDE=1, SDA=1, SCL=0 */
++ if (sda < 0)
++ ret += sda;
++ else if (sda & 0x08) {
++ PDEBUG(D_USBI, "Did not receive i2c ACK");
++ ret += -1;
++ }
++
++ return ret;
++}
++
++/* SMBus protocol: S Addr Wr [A] Subaddr [A] Value [A] P */
++static int w9968cf_i2c_w(struct sd *sd, u8 reg, u8 value)
++{
++ u16* data = (u16 *)sd->gspca_dev.usb_buf;
++ int ret = 0;
++
++ data[0] = 0x082f | ((sd->sensor_addr & 0x80) ? 0x1500 : 0x0);
++ data[0] |= (sd->sensor_addr & 0x40) ? 0x4000 : 0x0;
++ data[1] = 0x2082 | ((sd->sensor_addr & 0x40) ? 0x0005 : 0x0);
++ data[1] |= (sd->sensor_addr & 0x20) ? 0x0150 : 0x0;
++ data[1] |= (sd->sensor_addr & 0x10) ? 0x5400 : 0x0;
++ data[2] = 0x8208 | ((sd->sensor_addr & 0x08) ? 0x0015 : 0x0);
++ data[2] |= (sd->sensor_addr & 0x04) ? 0x0540 : 0x0;
++ data[2] |= (sd->sensor_addr & 0x02) ? 0x5000 : 0x0;
++ data[3] = 0x1d20 | ((sd->sensor_addr & 0x02) ? 0x0001 : 0x0);
++ data[3] |= (sd->sensor_addr & 0x01) ? 0x0054 : 0x0;
++
++ ret += w9968cf_write_fsb(sd, data);
++
++ data[0] = 0x8208 | ((reg & 0x80) ? 0x0015 : 0x0);
++ data[0] |= (reg & 0x40) ? 0x0540 : 0x0;
++ data[0] |= (reg & 0x20) ? 0x5000 : 0x0;
++ data[1] = 0x0820 | ((reg & 0x20) ? 0x0001 : 0x0);
++ data[1] |= (reg & 0x10) ? 0x0054 : 0x0;
++ data[1] |= (reg & 0x08) ? 0x1500 : 0x0;
++ data[1] |= (reg & 0x04) ? 0x4000 : 0x0;
++ data[2] = 0x2082 | ((reg & 0x04) ? 0x0005 : 0x0);
++ data[2] |= (reg & 0x02) ? 0x0150 : 0x0;
++ data[2] |= (reg & 0x01) ? 0x5400 : 0x0;
++ data[3] = 0x001d;
++
++ ret += w9968cf_write_fsb(sd, data);
++
++ data[0] = 0x8208 | ((value & 0x80) ? 0x0015 : 0x0);
++ data[0] |= (value & 0x40) ? 0x0540 : 0x0;
++ data[0] |= (value & 0x20) ? 0x5000 : 0x0;
++ data[1] = 0x0820 | ((value & 0x20) ? 0x0001 : 0x0);
++ data[1] |= (value & 0x10) ? 0x0054 : 0x0;
++ data[1] |= (value & 0x08) ? 0x1500 : 0x0;
++ data[1] |= (value & 0x04) ? 0x4000 : 0x0;
++ data[2] = 0x2082 | ((value & 0x04) ? 0x0005 : 0x0);
++ data[2] |= (value & 0x02) ? 0x0150 : 0x0;
++ data[2] |= (value & 0x01) ? 0x5400 : 0x0;
++ data[3] = 0xfe1d;
++
++ ret += w9968cf_write_fsb(sd, data);
++
++ if (!ret)
++ PDEBUG(D_USBO, "i2c 0x%02x -> [0x%02x]", value, reg);
++ else
++ PDEBUG(D_ERR, "i2c 0x%02x -> [0x%02x] failed", value, reg);
++
++ return ret;
++}
++
++/* SMBus protocol: S Addr Wr [A] Subaddr [A] P S Addr+1 Rd [A] [Value] NA P */
++static int w9968cf_i2c_r(struct sd *sd, u8 reg)
++{
++ int ret = 0;
++ u8 value;
++
++ /* Fast serial bus data control disable */
++ ret += w9968cf_write_sb(sd, 0x0013); /* don't change ! */
++
++ ret += w9968cf_smbus_start(sd);
++ ret += w9968cf_smbus_write_byte(sd, sd->sensor_addr);
++ ret += w9968cf_smbus_read_ack(sd);
++ ret += w9968cf_smbus_write_byte(sd, reg);
++ ret += w9968cf_smbus_read_ack(sd);
++ ret += w9968cf_smbus_stop(sd);
++ ret += w9968cf_smbus_start(sd);
++ ret += w9968cf_smbus_write_byte(sd, sd->sensor_addr + 1);
++ ret += w9968cf_smbus_read_ack(sd);
++ ret += w9968cf_smbus_read_byte(sd, &value);
++ /* signal we don't want to read anymore, the v4l1 driver used to
++ send an ack here which is very wrong! (and then fixed
++ the issues this gave by retrying reads) */
++ ret += w9968cf_smbus_write_nack(sd);
++ ret += w9968cf_smbus_stop(sd);
++
++ /* Fast serial bus data control re-enable */
++ ret += w9968cf_write_sb(sd, 0x0030);
++
++ if (!ret) {
++ ret = value;
++ PDEBUG(D_USBI, "i2c [0x%02X] -> 0x%02X", reg, value);
++ } else
++ PDEBUG(D_ERR, "i2c read [0x%02x] failed", reg);
++
++ return ret;
++}
++
++
++/*--------------------------------------------------------------------------
++ Turn on the LED on some webcams. A beep should be heard too.
++ Return 0 on success, a negative number otherwise.
++ --------------------------------------------------------------------------*/
++static int w9968cf_configure(struct sd *sd)
++{
++ int ret = 0;
++
++ ret += reg_w(sd, 0x00, 0xff00); /* power-down */
++ ret += reg_w(sd, 0x00, 0xbf17); /* reset everything */
++ ret += reg_w(sd, 0x00, 0xbf10); /* normal operation */
++ ret += reg_w(sd, 0x01, 0x0010); /* serial bus, SDS high */
++ ret += reg_w(sd, 0x01, 0x0000); /* serial bus, SDS low */
++ ret += reg_w(sd, 0x01, 0x0010); /* ..high 'beep-beep' */
++ ret += reg_w(sd, 0x01, 0x0030); /* Set sda scl to FSB mode */
++
++ if (ret)
++ PDEBUG(D_ERR, "Couldn't turn on the LED");
++
++ sd->stopped = 1;
++
++ return ret;
++}
++
++static int w9968cf_init(struct sd *sd)
++{
++ int ret = 0;
++ unsigned long hw_bufsize = sd->sif ? (352 * 288 * 2) : (640 * 480 * 2),
++ y0 = 0x0000,
++ u0 = y0 + hw_bufsize/2,
++ v0 = u0 + hw_bufsize/4,
++ y1 = v0 + hw_bufsize/4,
++ u1 = y1 + hw_bufsize/2,
++ v1 = u1 + hw_bufsize/4;
++
++ ret += reg_w(sd, 0x00, 0xff00); /* power off */
++ ret += reg_w(sd, 0x00, 0xbf10); /* power on */
++
++ ret += reg_w(sd, 0x03, 0x405d); /* DRAM timings */
++ ret += reg_w(sd, 0x04, 0x0030); /* SDRAM timings */
++
++ ret += reg_w(sd, 0x20, y0 & 0xffff); /* Y buf.0, low */
++ ret += reg_w(sd, 0x21, y0 >> 16); /* Y buf.0, high */
++ ret += reg_w(sd, 0x24, u0 & 0xffff); /* U buf.0, low */
++ ret += reg_w(sd, 0x25, u0 >> 16); /* U buf.0, high */
++ ret += reg_w(sd, 0x28, v0 & 0xffff); /* V buf.0, low */
++ ret += reg_w(sd, 0x29, v0 >> 16); /* V buf.0, high */
++
++ ret += reg_w(sd, 0x22, y1 & 0xffff); /* Y buf.1, low */
++ ret += reg_w(sd, 0x23, y1 >> 16); /* Y buf.1, high */
++ ret += reg_w(sd, 0x26, u1 & 0xffff); /* U buf.1, low */
++ ret += reg_w(sd, 0x27, u1 >> 16); /* U buf.1, high */
++ ret += reg_w(sd, 0x2a, v1 & 0xffff); /* V buf.1, low */
++ ret += reg_w(sd, 0x2b, v1 >> 16); /* V buf.1, high */
++
++ ret += reg_w(sd, 0x32, y1 & 0xffff); /* JPEG buf 0 low */
++ ret += reg_w(sd, 0x33, y1 >> 16); /* JPEG buf 0 high */
++
++ ret += reg_w(sd, 0x34, y1 & 0xffff); /* JPEG buf 1 low */
++ ret += reg_w(sd, 0x35, y1 >> 16); /* JPEG bug 1 high */
++
++ ret += reg_w(sd, 0x36, 0x0000);/* JPEG restart interval */
++ ret += reg_w(sd, 0x37, 0x0804);/*JPEG VLE FIFO threshold*/
++ ret += reg_w(sd, 0x38, 0x0000);/* disable hw up-scaling */
++ ret += reg_w(sd, 0x3f, 0x0000); /* JPEG/MCTL test data */
++
++ return ret;
++}
++
++static int w9968cf_set_crop_window(struct sd *sd)
++{
++ int ret = 0, start_cropx, start_cropy, x, y, fw, fh, cw, ch,
++ max_width, max_height;
++
++ if (sd->sif) {
++ max_width = 352;
++ max_height = 288;
++ } else {
++ max_width = 640;
++ max_height = 480;
++ }
++
++ if (sd->sensor == SEN_OV7620) {
++ /* Sigh, this is dependend on the clock / framerate changes
++ made by the frequency control, sick. */
++ if (sd->freq == 1) {
++ start_cropx = 277;
++ start_cropy = 37;
++ } else {
++ start_cropx = 105;
++ start_cropy = 37;
++ }
++ } else {
++ start_cropx = 320;
++ start_cropy = 35;
++ }
++
++ /* Work around to avoid FP arithmetics */
++ #define SC(x) ((x) << 10)
++
++ /* Scaling factors */
++ fw = SC(sd->gspca_dev.width) / max_width;
++ fh = SC(sd->gspca_dev.height) / max_height;
++
++ cw = (fw >= fh) ? max_width : SC(sd->gspca_dev.width)/fh;
++ ch = (fw >= fh) ? SC(sd->gspca_dev.height)/fw : max_height;
++
++ sd->sensor_width = max_width;
++ sd->sensor_height = max_height;
++
++ x = (max_width - cw) / 2;
++ y = (max_height - ch) / 2;
++
++ ret += reg_w(sd, 0x10, start_cropx + x);
++ ret += reg_w(sd, 0x11, start_cropy + y);
++ ret += reg_w(sd, 0x12, start_cropx + x + cw);
++ ret += reg_w(sd, 0x13, start_cropy + y + ch);
++
++ return ret;
++}
++
++static int w9968cf_mode_init_regs(struct sd *sd)
++{
++ int ret = 0, val, vs_polarity, hs_polarity;
++
++ ret += w9968cf_set_crop_window(sd);
++
++ ret += reg_w(sd, 0x14, sd->gspca_dev.width);
++ ret += reg_w(sd, 0x15, sd->gspca_dev.height);
++
++ /* JPEG width & height */
++ ret += reg_w(sd, 0x30, sd->gspca_dev.width);
++ ret += reg_w(sd, 0x31, sd->gspca_dev.height);
++
++ /* Y & UV frame buffer strides (in WORD) */
++ if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
++ V4L2_PIX_FMT_JPEG) {
++ ret += reg_w(sd, 0x2c, sd->gspca_dev.width/2);
++ ret += reg_w(sd, 0x2d, sd->gspca_dev.width/4);
++ } else
++ ret += reg_w(sd, 0x2c, sd->gspca_dev.width);
++
++ ret += reg_w(sd, 0x00, 0xbf17); /* reset everything */
++ ret += reg_w(sd, 0x00, 0xbf10); /* normal operation */
++
++ /* Transfer size in WORDS (for UYVY format only) */
++ val = sd->gspca_dev.width * sd->gspca_dev.height;
++ ret += reg_w(sd, 0x3d, val & 0xffff); /* low bits */
++ ret += reg_w(sd, 0x3e, val >> 16); /* high bits */
++
++ if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
++ V4L2_PIX_FMT_JPEG) {
++ /* We may get called multiple times (usb isoc bw negotiat.) */
++ if (!sd->jpeg_hdr)
++ sd->jpeg_hdr = kmalloc(JPEG_HDR_SZ, GFP_KERNEL);
++ if (!sd->jpeg_hdr)
++ return -ENOMEM;
++
++ jpeg_define(sd->jpeg_hdr, sd->gspca_dev.height,
++ sd->gspca_dev.width, 0x22); /* JPEG 420 */
++ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
++ ret += w9968cf_upload_quantizationtables(sd);
++ }
++
++ /* Video Capture Control Register */
++ if (sd->sensor == SEN_OV7620) {
++ /* Seems to work around a bug in the image sensor */
++ vs_polarity = 1;
++ hs_polarity = 1;
++ } else {
++ vs_polarity = 1;
++ hs_polarity = 0;
++ }
++
++ val = (vs_polarity << 12) | (hs_polarity << 11);
++
++ /* NOTE: We may not have enough memory to do double buffering while
++ doing compression (amount of memory differs per model cam).
++ So we use the second image buffer also as jpeg stream buffer
++ (see w9968cf_init), and disable double buffering. */
++ if (w9968cf_vga_mode[sd->gspca_dev.curr_mode].pixelformat ==
++ V4L2_PIX_FMT_JPEG) {
++ /* val |= 0x0002; YUV422P */
++ val |= 0x0003; /* YUV420P */
++ } else
++ val |= 0x0080; /* Enable HW double buffering */
++
++ /* val |= 0x0020; enable clamping */
++ /* val |= 0x0008; enable (1-2-1) filter */
++ /* val |= 0x000c; enable (2-3-6-3-2) filter */
++
++ val |= 0x8000; /* capt. enable */
++
++ ret += reg_w(sd, 0x16, val);
++
++ sd->gspca_dev.empty_packet = 0;
++
++ return ret;
++}
++
++static void w9968cf_stop0(struct sd *sd)
++{
++ if (sd->gspca_dev.present) {
++ reg_w(sd, 0x39, 0x0000); /* disable JPEG encoder */
++ reg_w(sd, 0x16, 0x0000); /* stop video capture */
++ }
++
++ kfree(sd->jpeg_hdr);
++ sd->jpeg_hdr = NULL;
++}
++
++/* The w9968cf docs say that a 0 sized packet means EOF (and also SOF
++ for the next frame). This seems to simply not be true when operating
++ in JPEG mode, in this case there may be empty packets within the
++ frame. So in JPEG mode use the JPEG SOI marker to detect SOF.
++
++ Note to make things even more interesting the w9968cf sends *PLANAR* jpeg,
++ to be precise it sends: SOI, SOF, DRI, SOS, Y-data, SOS, U-data, SOS,
++ V-data, EOI. */
++static void w9968cf_pkt_scan(struct gspca_dev *gspca_dev,
++ u8 *data, /* isoc packet */
++ int len) /* iso packet length */
++{
++ struct sd *sd = (struct sd *) gspca_dev;
++
++ if (w9968cf_vga_mode[gspca_dev->curr_mode].pixelformat ==
++ V4L2_PIX_FMT_JPEG) {
++ if (len >= 2 &&
++ data[0] == 0xff &&
++ data[1] == 0xd8) {
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ NULL, 0);
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
++ sd->jpeg_hdr, JPEG_HDR_SZ);
++ /* Strip the ff d8, our own header (which adds
++ huffman and quantization tables) already has this */
++ len -= 2;
++ data += 2;
++ }
++ } else {
++ /* In UYVY mode an empty packet signals EOF */
++ if (gspca_dev->empty_packet) {
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ NULL, 0);
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
++ NULL, 0);
++ gspca_dev->empty_packet = 0;
++ }
++ }
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
++}
+diff --git a/drivers/media/video/gspca/zc3xx.c b/drivers/media/video/gspca/zc3xx.c
+index cdf3357..7795a99 100644
+--- a/drivers/media/video/gspca/zc3xx.c
++++ b/drivers/media/video/gspca/zc3xx.c
+@@ -1,9 +1,8 @@
+ /*
+- * Z-Star/Vimicro zc301/zc302p/vc30x library
+- * Copyright (C) 2004 2005 2006 Michel Xhaard
+- * mxhaard@magic.fr
++ * Z-Star/Vimicro zc301/zc302p/vc30x library
+ *
+- * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
++ * Copyright (C) 2009-2010 Jean-Francois Moine <http://moinejf.free.fr>
++ * Copyright (C) 2004 2005 2006 Michel Xhaard mxhaard@magic.fr
+ *
+ * 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
+@@ -25,7 +24,7 @@
+ #include "gspca.h"
+ #include "jpeg.h"
+
+-MODULE_AUTHOR("Michel Xhaard <mxhaard@users.sourceforge.net>, "
++MODULE_AUTHOR("Jean-Francois Moine <http://moinejf.free.fr>, "
+ "Serge A. Suchkov <Serge.A.S@tochka.ru>");
+ MODULE_DESCRIPTION("GSPCA ZC03xx/VC3xx USB Camera Driver");
+ MODULE_LICENSE("GPL");
+@@ -39,18 +38,18 @@ static int force_sensor = -1;
+ struct sd {
+ struct gspca_dev gspca_dev; /* !! must be the first item */
+
+- __u8 brightness;
+- __u8 contrast;
+- __u8 gamma;
+- __u8 autogain;
+- __u8 lightfreq;
+- __u8 sharpness;
++ u8 brightness;
++ u8 contrast;
++ u8 gamma;
++ u8 autogain;
++ u8 lightfreq;
++ u8 sharpness;
+ u8 quality; /* image quality */
+ #define QUALITY_MIN 40
+ #define QUALITY_MAX 60
+ #define QUALITY_DEF 50
+
+- signed char sensor; /* Type of image sensor chip */
++ u8 sensor; /* Type of image sensor chip */
+ /* !! values used in different tables */
+ #define SENSOR_ADCM2700 0
+ #define SENSOR_CS2102 1
+@@ -61,17 +60,18 @@ struct sd {
+ #define SENSOR_HV7131C 6
+ #define SENSOR_ICM105A 7
+ #define SENSOR_MC501CB 8
+-#define SENSOR_OV7620 9
+-/*#define SENSOR_OV7648 9 - same values */
+-#define SENSOR_OV7630C 10
+-#define SENSOR_PAS106 11
+-#define SENSOR_PAS202B 12
+-#define SENSOR_PB0330 13
+-#define SENSOR_PO2030 14
+-#define SENSOR_TAS5130CK 15
+-#define SENSOR_TAS5130CXX 16
+-#define SENSOR_TAS5130C_VF0250 17
+-#define SENSOR_MAX 18
++#define SENSOR_MI0360SOC 9
++#define SENSOR_OV7620 10
++/*#define SENSOR_OV7648 10 - same values */
++#define SENSOR_OV7630C 11
++#define SENSOR_PAS106 12
++#define SENSOR_PAS202B 13
++#define SENSOR_PB0330 14 /* (MI0360) */
++#define SENSOR_PO2030 15
++#define SENSOR_TAS5130CK 16
++#define SENSOR_TAS5130CXX 17
++#define SENSOR_TAS5130C_VF0250 18
++#define SENSOR_MAX 19
+ unsigned short chip_revision;
+
+ u8 *jpeg_hdr;
+@@ -91,9 +91,8 @@ static int sd_getfreq(struct gspca_dev *gspca_dev, __s32 *val);
+ static int sd_setsharpness(struct gspca_dev *gspca_dev, __s32 val);
+ static int sd_getsharpness(struct gspca_dev *gspca_dev, __s32 *val);
+
+-static struct ctrl sd_ctrls[] = {
++static const struct ctrl sd_ctrls[] = {
+ #define BRIGHTNESS_IDX 0
+-#define SD_BRIGHTNESS 0
+ {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+@@ -102,26 +101,26 @@ static struct ctrl sd_ctrls[] = {
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+- .default_value = 128,
++#define BRIGHTNESS_DEF 128
++ .default_value = BRIGHTNESS_DEF,
+ },
+ .set = sd_setbrightness,
+ .get = sd_getbrightness,
+ },
+-#define SD_CONTRAST 1
+ {
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+- .maximum = 256,
++ .maximum = 255,
+ .step = 1,
+- .default_value = 128,
++#define CONTRAST_DEF 128
++ .default_value = CONTRAST_DEF,
+ },
+ .set = sd_setcontrast,
+ .get = sd_getcontrast,
+ },
+-#define SD_GAMMA 2
+ {
+ {
+ .id = V4L2_CID_GAMMA,
+@@ -135,7 +134,6 @@ static struct ctrl sd_ctrls[] = {
+ .set = sd_setgamma,
+ .get = sd_getgamma,
+ },
+-#define SD_AUTOGAIN 3
+ {
+ {
+ .id = V4L2_CID_AUTOGAIN,
+@@ -144,13 +142,13 @@ static struct ctrl sd_ctrls[] = {
+ .minimum = 0,
+ .maximum = 1,
+ .step = 1,
+- .default_value = 1,
++#define AUTOGAIN_DEF 1
++ .default_value = AUTOGAIN_DEF,
+ },
+ .set = sd_setautogain,
+ .get = sd_getautogain,
+ },
+ #define LIGHTFREQ_IDX 4
+-#define SD_FREQ 4
+ {
+ {
+ .id = V4L2_CID_POWER_LINE_FREQUENCY,
+@@ -159,12 +157,12 @@ static struct ctrl sd_ctrls[] = {
+ .minimum = 0,
+ .maximum = 2, /* 0: 0, 1: 50Hz, 2:60Hz */
+ .step = 1,
+- .default_value = 1,
++#define FREQ_DEF 0
++ .default_value = FREQ_DEF,
+ },
+ .set = sd_setfreq,
+ .get = sd_getfreq,
+ },
+-#define SD_SHARPNESS 5
+ {
+ {
+ .id = V4L2_CID_SHARPNESS,
+@@ -173,7 +171,8 @@ static struct ctrl sd_ctrls[] = {
+ .minimum = 0,
+ .maximum = 3,
+ .step = 1,
+- .default_value = 2,
++#define SHARPNESS_DEF 2
++ .default_value = SHARPNESS_DEF,
+ },
+ .set = sd_setsharpness,
+ .get = sd_getsharpness,
+@@ -193,6 +192,19 @@ static const struct v4l2_pix_format vga_mode[] = {
+ .priv = 0},
+ };
+
++static const struct v4l2_pix_format broken_vga_mode[] = {
++ {320, 232, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
++ .bytesperline = 320,
++ .sizeimage = 320 * 232 * 4 / 8 + 590,
++ .colorspace = V4L2_COLORSPACE_JPEG,
++ .priv = 1},
++ {640, 472, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
++ .bytesperline = 640,
++ .sizeimage = 640 * 472 * 3 / 8 + 590,
++ .colorspace = V4L2_COLORSPACE_JPEG,
++ .priv = 0},
++};
++
+ static const struct v4l2_pix_format sif_mode[] = {
+ {176, 144, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
+ .bytesperline = 176,
+@@ -208,15 +220,19 @@ static const struct v4l2_pix_format sif_mode[] = {
+
+ /* usb exchanges */
+ struct usb_action {
+- __u8 req;
+- __u8 val;
+- __u16 idx;
++ u8 req;
++ u8 val;
++ u16 idx;
+ };
+
+ static const struct usb_action adcm2700_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */
++#if 1 /*jfm*/
+ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
++#else
++ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,03,cc */
++#endif
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0a,cc */
+ {0xa0, 0xd3, ZC3XX_R08B_I2CDEVICEADDR}, /* 00,8b,d3,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+@@ -420,9 +436,7 @@ static const struct usb_action adcm2700_NoFliker[] = {
+ {0xaa, 0xfe, 0x0010}, /* 00,fe,10,aa */
+ {}
+ };
+-static const struct usb_action cs2102_Initial[] = {
+- {0xa1, 0x01, 0x0008},
+- {0xa1, 0x01, 0x0008},
++static const struct usb_action cs2102_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
+@@ -471,88 +485,10 @@ static const struct usb_action cs2102_Initial[] = {
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x68, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x00, 0x01ad},
+- {0xa1, 0x01, 0x0002},
+- {0xa1, 0x01, 0x0008},
+- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+- {0xa1, 0x01, 0x01c8},
+- {0xa1, 0x01, 0x01c9},
+- {0xa1, 0x01, 0x01ca},
+- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+- {0xa0, 0x24, ZC3XX_R120_GAMMA00}, /* gamma 5 */
+- {0xa0, 0x44, ZC3XX_R121_GAMMA01},
+- {0xa0, 0x64, ZC3XX_R122_GAMMA02},
+- {0xa0, 0x84, ZC3XX_R123_GAMMA03},
+- {0xa0, 0x9d, ZC3XX_R124_GAMMA04},
+- {0xa0, 0xb2, ZC3XX_R125_GAMMA05},
+- {0xa0, 0xc4, ZC3XX_R126_GAMMA06},
+- {0xa0, 0xd3, ZC3XX_R127_GAMMA07},
+- {0xa0, 0xe0, ZC3XX_R128_GAMMA08},
+- {0xa0, 0xeb, ZC3XX_R129_GAMMA09},
+- {0xa0, 0xf4, ZC3XX_R12A_GAMMA0A},
+- {0xa0, 0xfb, ZC3XX_R12B_GAMMA0B},
+- {0xa0, 0xff, ZC3XX_R12C_GAMMA0C},
+- {0xa0, 0xff, ZC3XX_R12D_GAMMA0D},
+- {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
+- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+- {0xa0, 0x18, ZC3XX_R130_GAMMA10},
+- {0xa0, 0x20, ZC3XX_R131_GAMMA11},
+- {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+- {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+- {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+- {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+- {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+- {0xa0, 0x0e, ZC3XX_R137_GAMMA17},
+- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+- {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+- {0xa0, 0x00, ZC3XX_R13C_GAMMA1C},
+- {0xa0, 0x00, ZC3XX_R13D_GAMMA1D},
+- {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
+- {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
+- {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
+- {0xa0, 0xf4, ZC3XX_R10B_RGB01},
+- {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+- {0xa0, 0xf4, ZC3XX_R10D_RGB10},
+- {0xa0, 0x58, ZC3XX_R10E_RGB11},
+- {0xa0, 0xf4, ZC3XX_R10F_RGB12},
+- {0xa0, 0xf4, ZC3XX_R110_RGB20},
+- {0xa0, 0xf4, ZC3XX_R111_RGB21},
+- {0xa0, 0x58, ZC3XX_R112_RGB22},
+- {0xa1, 0x01, 0x0180},
+- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+- {0xaa, 0x23, 0x0001},
+- {0xaa, 0x24, 0x0055},
+- {0xaa, 0x25, 0x00cc},
+- {0xaa, 0x21, 0x003f},
+- {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
+- {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID},
+- {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW},
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+- {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID},
+- {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW},
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
+- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+- {0xa0, 0x39, ZC3XX_R01D_HSYNC_0},
+- {0xa0, 0x70, ZC3XX_R01E_HSYNC_1},
+- {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2},
+- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa1, 0x01, 0x0180},
+- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa0, 0x40, ZC3XX_R116_RGAIN},
+- {0xa0, 0x40, ZC3XX_R117_GGAIN},
+- {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {}
+ };
+
+-static const struct usb_action cs2102_InitialScale[] = {
+- {0xa1, 0x01, 0x0008},
+- {0xa1, 0x01, 0x0008},
++static const struct usb_action cs2102_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
+@@ -601,57 +537,75 @@ static const struct usb_action cs2102_InitialScale[] = {
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x68, ZC3XX_R18D_YTARGET},
+ {0xa0, 0x00, 0x01ad},
+- {0xa1, 0x01, 0x0002},
+- {0xa1, 0x01, 0x0008},
+- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+- {0xa1, 0x01, 0x01c8},
+- {0xa1, 0x01, 0x01c9},
+- {0xa1, 0x01, 0x01ca},
+- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+- {0xa0, 0x24, ZC3XX_R120_GAMMA00}, /* gamma 5 */
+- {0xa0, 0x44, ZC3XX_R121_GAMMA01},
+- {0xa0, 0x64, ZC3XX_R122_GAMMA02},
+- {0xa0, 0x84, ZC3XX_R123_GAMMA03},
+- {0xa0, 0x9d, ZC3XX_R124_GAMMA04},
+- {0xa0, 0xb2, ZC3XX_R125_GAMMA05},
+- {0xa0, 0xc4, ZC3XX_R126_GAMMA06},
+- {0xa0, 0xd3, ZC3XX_R127_GAMMA07},
+- {0xa0, 0xe0, ZC3XX_R128_GAMMA08},
+- {0xa0, 0xeb, ZC3XX_R129_GAMMA09},
+- {0xa0, 0xf4, ZC3XX_R12A_GAMMA0A},
+- {0xa0, 0xfb, ZC3XX_R12B_GAMMA0B},
+- {0xa0, 0xff, ZC3XX_R12C_GAMMA0C},
+- {0xa0, 0xff, ZC3XX_R12D_GAMMA0D},
+- {0xa0, 0xff, ZC3XX_R12E_GAMMA0E},
+- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+- {0xa0, 0x18, ZC3XX_R130_GAMMA10},
+- {0xa0, 0x20, ZC3XX_R131_GAMMA11},
+- {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+- {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+- {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+- {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+- {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+- {0xa0, 0x0e, ZC3XX_R137_GAMMA17},
+- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+- {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+- {0xa0, 0x00, ZC3XX_R13C_GAMMA1C},
+- {0xa0, 0x00, ZC3XX_R13D_GAMMA1D},
+- {0xa0, 0x00, ZC3XX_R13E_GAMMA1E},
+- {0xa0, 0x01, ZC3XX_R13F_GAMMA1F},
+- {0xa0, 0x58, ZC3XX_R10A_RGB00}, /* matrix */
+- {0xa0, 0xf4, ZC3XX_R10B_RGB01},
+- {0xa0, 0xf4, ZC3XX_R10C_RGB02},
+- {0xa0, 0xf4, ZC3XX_R10D_RGB10},
+- {0xa0, 0x58, ZC3XX_R10E_RGB11},
+- {0xa0, 0xf4, ZC3XX_R10F_RGB12},
+- {0xa0, 0xf4, ZC3XX_R110_RGB20},
+- {0xa0, 0xf4, ZC3XX_R111_RGB21},
+- {0xa0, 0x58, ZC3XX_R112_RGB22},
+- {0xa1, 0x01, 0x0180},
+- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
++ {}
++};
++static const struct usb_action cs2102_50HZScale[] = {
++ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
++ {0xaa, 0x23, 0x0001},
++ {0xaa, 0x24, 0x005f},
++ {0xaa, 0x25, 0x0090},
++ {0xaa, 0x21, 0x00dd},
++ {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
++ {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0x20, ZC3XX_R192_EXPOSURELIMITLOW},
++ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
++ {0xa0, 0x3a, ZC3XX_R196_ANTIFLICKERMID},
++ {0xa0, 0x98, ZC3XX_R197_ANTIFLICKERLOW},
++ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
++ {0xa0, 0xdd, ZC3XX_R01D_HSYNC_0},
++ {0xa0, 0xe4, ZC3XX_R01E_HSYNC_1},
++ {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
++ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
++ {}
++};
++static const struct usb_action cs2102_50HZ[] = {
++ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
++ {0xaa, 0x23, 0x0000},
++ {0xaa, 0x24, 0x00af},
++ {0xaa, 0x25, 0x00c8},
++ {0xaa, 0x21, 0x0068},
++ {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH},
++ {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0x90, ZC3XX_R192_EXPOSURELIMITLOW},
++ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
++ {0xa0, 0x1d, ZC3XX_R196_ANTIFLICKERMID},
++ {0xa0, 0x4c, ZC3XX_R197_ANTIFLICKERLOW},
++ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
++ {0xa0, 0x68, ZC3XX_R01D_HSYNC_0},
++ {0xa0, 0xe3, ZC3XX_R01E_HSYNC_1},
++ {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
++ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
++ {}
++};
++static const struct usb_action cs2102_60HZScale[] = {
++ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
++ {0xaa, 0x23, 0x0001},
++ {0xaa, 0x24, 0x0055},
++ {0xaa, 0x25, 0x00cc},
++ {0xaa, 0x21, 0x003f},
++ {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
++ {0xa0, 0xab, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0x98, ZC3XX_R192_EXPOSURELIMITLOW},
++ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
++ {0xa0, 0x30, ZC3XX_R196_ANTIFLICKERMID},
++ {0xa0, 0xd4, ZC3XX_R197_ANTIFLICKERLOW},
++ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
++ {0xa0, 0x39, ZC3XX_R01D_HSYNC_0},
++ {0xa0, 0x70, ZC3XX_R01E_HSYNC_1},
++ {0xa0, 0xb0, ZC3XX_R01F_HSYNC_2},
++ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
++ {}
++};
++static const struct usb_action cs2102_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+ {0xaa, 0x23, 0x0000},
+ {0xaa, 0x24, 0x00aa},
+@@ -671,167 +625,55 @@ static const struct usb_action cs2102_InitialScale[] = {
+ {0xa0, 0xa5, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf0, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa1, 0x01, 0x0180},
+- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa0, 0x40, ZC3XX_R116_RGAIN},
+- {0xa0, 0x40, ZC3XX_R117_GGAIN},
+- {0xa0, 0x40, ZC3XX_R118_BGAIN},
+- {}
+-};
+-static const struct usb_action cs2102_50HZ[] = {
+- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+- {0xaa, 0x0f, 0x008c}, /* 00,0f,8c,aa */
+- {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
+- {0xaa, 0x04, 0x00ac}, /* 00,04,ac,aa */
+- {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
+- {0xaa, 0x11, 0x00ac}, /* 00,11,ac,aa */
+- {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
+- {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
+- {0xaa, 0x1d, 0x00ac}, /* 00,1d,ac,aa */
+- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
+- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+- {0xa0, 0x42, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,42,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+- {0xa0, 0x8c, ZC3XX_R01D_HSYNC_0}, /* 00,1d,8c,cc */
+- {0xa0, 0xb0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,b0,cc */
+- {0xa0, 0xd0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,d0,cc */
+- {}
+-};
+-static const struct usb_action cs2102_50HZScale[] = {
+- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+- {0xaa, 0x0f, 0x0093}, /* 00,0f,93,aa */
+- {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
+- {0xaa, 0x04, 0x00a1}, /* 00,04,a1,aa */
+- {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
+- {0xaa, 0x11, 0x00a1}, /* 00,11,a1,aa */
+- {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
+- {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
+- {0xaa, 0x1d, 0x00a1}, /* 00,1d,a1,aa */
+- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
+- {0xa0, 0xf7, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f7,cc */
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+- {0xa0, 0x83, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,83,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+- {0xa0, 0x93, ZC3XX_R01D_HSYNC_0}, /* 00,1d,93,cc */
+- {0xa0, 0xb0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,b0,cc */
+- {0xa0, 0xd0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,d0,cc */
+- {}
+-};
+-static const struct usb_action cs2102_60HZ[] = {
+- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+- {0xaa, 0x0f, 0x005d}, /* 00,0f,5d,aa */
+- {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
+- {0xaa, 0x04, 0x00aa}, /* 00,04,aa,aa */
+- {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
+- {0xaa, 0x11, 0x00aa}, /* 00,11,aa,aa */
+- {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
+- {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
+- {0xaa, 0x1d, 0x00aa}, /* 00,1d,aa,aa */
+- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
+- {0xa0, 0xe4, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e4,cc */
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+- {0xa0, 0x3a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3a,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+- {0xa0, 0x5d, ZC3XX_R01D_HSYNC_0}, /* 00,1d,5d,cc */
+- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
+- {0xa0, 0xd0, 0x00c8}, /* 00,c8,d0,cc */
+ {}
+ };
+-static const struct usb_action cs2102_60HZScale[] = {
+- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+- {0xaa, 0x0f, 0x00b7}, /* 00,0f,b7,aa */
+- {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
+- {0xaa, 0x04, 0x00be}, /* 00,04,be,aa */
+- {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
+- {0xaa, 0x11, 0x00be}, /* 00,11,be,aa */
+- {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
+- {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
+- {0xaa, 0x1d, 0x00be}, /* 00,1d,be,aa */
+- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
+- {0xa0, 0xfc, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,fc,cc */
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+- {0xa0, 0x69, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,69,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+- {0xa0, 0x10, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,10,cc */
+- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+- {0xa0, 0xb7, ZC3XX_R01D_HSYNC_0}, /* 00,1d,b7,cc */
+- {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
+- {0xa0, 0xe8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e8,cc */
++static const struct usb_action cs2102_NoFlikerScale[] = {
++ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
++ {0xaa, 0x23, 0x0001},
++ {0xaa, 0x24, 0x005f},
++ {0xaa, 0x25, 0x0000},
++ {0xaa, 0x21, 0x0001},
++ {0xa0, 0x02, ZC3XX_R190_EXPOSURELIMITHIGH},
++ {0xa0, 0xbf, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
++ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
++ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
++ {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW},
++ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
++ {0xa0, 0x01, ZC3XX_R01D_HSYNC_0},
++ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
++ {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2},
++ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+ };
+ static const struct usb_action cs2102_NoFliker[] = {
+- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+- {0xaa, 0x0f, 0x0059}, /* 00,0f,59,aa */
+- {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
+- {0xaa, 0x04, 0x0080}, /* 00,04,80,aa */
+- {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
+- {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */
+- {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
+- {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
+- {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */
+- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
+- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+- {0xa0, 0x59, ZC3XX_R01D_HSYNC_0}, /* 00,1d,59,cc */
+- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
+- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
+- {}
+-};
+-static const struct usb_action cs2102_NoFlikerScale[] = {
+- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+- {0xaa, 0x0f, 0x0059}, /* 00,0f,59,aa */
+- {0xaa, 0x03, 0x0005}, /* 00,03,05,aa */
+- {0xaa, 0x04, 0x0080}, /* 00,04,80,aa */
+- {0xaa, 0x10, 0x0005}, /* 00,10,05,aa */
+- {0xaa, 0x11, 0x0080}, /* 00,11,80,aa */
+- {0xaa, 0x1b, 0x0000}, /* 00,1b,00,aa */
+- {0xaa, 0x1c, 0x0005}, /* 00,1c,05,aa */
+- {0xaa, 0x1d, 0x0080}, /* 00,1d,80,aa */
+- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x3f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,3f,cc */
+- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+- {0xa0, 0x59, ZC3XX_R01D_HSYNC_0}, /* 00,1d,59,cc */
+- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
+- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
++ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
++ {0xaa, 0x23, 0x0000},
++ {0xaa, 0x24, 0x00af},
++ {0xaa, 0x25, 0x0080},
++ {0xaa, 0x21, 0x0001},
++ {0xa0, 0x01, ZC3XX_R190_EXPOSURELIMITHIGH},
++ {0xa0, 0x5f, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0x80, ZC3XX_R192_EXPOSURELIMITLOW},
++ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
++ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
++ {0xa0, 0x80, ZC3XX_R197_ANTIFLICKERLOW},
++ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
++ {0xa0, 0x01, ZC3XX_R01D_HSYNC_0},
++ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
++ {0xa0, 0xa0, ZC3XX_R01F_HSYNC_2},
++ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {}
+ };
+
+ /* CS2102_KOCOM */
+-static const struct usb_action cs2102K_Initial[] = {
++static const struct usb_action cs2102K_InitialScale[] = {
+ {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x08, ZC3XX_R010_CMOSSENSORSELECT},
+@@ -1090,7 +932,7 @@ static const struct usb_action cs2102K_Initial[] = {
+ {}
+ };
+
+-static const struct usb_action cs2102K_InitialScale[] = {
++static const struct usb_action cs2102K_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+@@ -1668,7 +1510,7 @@ static const struct usb_action gc0305_NoFliker[] = {
+ {}
+ };
+
+-static const struct usb_action hdcs2020xb_Initial[] = {
++static const struct usb_action hdcs2020b_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x11, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* qtable 0x05 */
+@@ -1800,7 +1642,7 @@ static const struct usb_action hdcs2020xb_Initial[] = {
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {}
+ };
+-static const struct usb_action hdcs2020xb_InitialScale[] = {
++static const struct usb_action hdcs2020b_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+@@ -1992,7 +1834,7 @@ static const struct usb_action hdcs2020b_NoFliker[] = {
+ {}
+ };
+
+-static const struct usb_action hv7131bxx_Initial[] = { /* 320x240 */
++static const struct usb_action hv7131b_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
+@@ -2039,7 +1881,7 @@ static const struct usb_action hv7131bxx_Initial[] = { /* 320x240 */
+ {}
+ };
+
+-static const struct usb_action hv7131bxx_InitialScale[] = { /* 640x480*/
++static const struct usb_action hv7131b_Initial[] = { /* 640x480*/
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x00, ZC3XX_R010_CMOSSENSORSELECT},
+@@ -2236,7 +2078,7 @@ static const struct usb_action hv7131b_NoFlikerScale[] = { /* 320x240 */
+ {}
+ };
+
+-static const struct usb_action hv7131cxx_Initial[] = {
++static const struct usb_action hv7131r_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT},
+@@ -2330,7 +2172,7 @@ static const struct usb_action hv7131cxx_Initial[] = {
+ {}
+ };
+
+-static const struct usb_action hv7131cxx_InitialScale[] = {
++static const struct usb_action hv7131r_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* diff */
+@@ -2432,7 +2274,7 @@ static const struct usb_action hv7131cxx_InitialScale[] = {
+ {}
+ };
+
+-static const struct usb_action icm105axx_Initial[] = {
++static const struct usb_action icm105a_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+@@ -2609,7 +2451,7 @@ static const struct usb_action icm105axx_Initial[] = {
+ {}
+ };
+
+-static const struct usb_action icm105axx_InitialScale[] = {
++static const struct usb_action icm105a_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+@@ -2788,7 +2630,7 @@ static const struct usb_action icm105axx_InitialScale[] = {
+ {0xa0, 0x40, ZC3XX_R118_BGAIN},
+ {}
+ };
+-static const struct usb_action icm105a_50HZ[] = {
++static const struct usb_action icm105a_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+ {0xaa, 0x0c, 0x0020}, /* 00,0c,20,aa */
+@@ -2819,7 +2661,7 @@ static const struct usb_action icm105a_50HZ[] = {
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {}
+ };
+-static const struct usb_action icm105a_50HZScale[] = {
++static const struct usb_action icm105a_50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+ {0xaa, 0x0c, 0x008c}, /* 00,0c,8c,aa */
+@@ -2852,7 +2694,7 @@ static const struct usb_action icm105a_50HZScale[] = {
+ {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */
+ {}
+ };
+-static const struct usb_action icm105a_60HZ[] = {
++static const struct usb_action icm105a_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+ {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
+@@ -2883,7 +2725,7 @@ static const struct usb_action icm105a_60HZ[] = {
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {}
+ };
+-static const struct usb_action icm105a_60HZScale[] = {
++static const struct usb_action icm105a_60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+ {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */
+@@ -2916,7 +2758,7 @@ static const struct usb_action icm105a_60HZScale[] = {
+ {0xa0, 0xc0, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,c0,cc */
+ {}
+ };
+-static const struct usb_action icm105a_NoFliker[] = {
++static const struct usb_action icm105a_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+ {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
+@@ -2947,7 +2789,7 @@ static const struct usb_action icm105a_NoFliker[] = {
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {}
+ };
+-static const struct usb_action icm105a_NoFlikerScale[] = {
++static const struct usb_action icm105a_NoFliker[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xaa, 0x0d, 0x0003}, /* 00,0d,03,aa */
+ {0xaa, 0x0c, 0x0004}, /* 00,0c,04,aa */
+@@ -2981,7 +2823,7 @@ static const struct usb_action icm105a_NoFlikerScale[] = {
+ {}
+ };
+
+-static const struct usb_action MC501CB_InitialScale[] = {
++static const struct usb_action mc501cb_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+@@ -3101,7 +2943,7 @@ static const struct usb_action MC501CB_InitialScale[] = {
+ {}
+ };
+
+-static const struct usb_action MC501CB_Initial[] = { /* 320x240 */
++static const struct usb_action mc501cb_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+@@ -3220,7 +3062,7 @@ static const struct usb_action MC501CB_Initial[] = { /* 320x240 */
+ {}
+ };
+
+-static const struct usb_action MC501CB_50HZ[] = {
++static const struct usb_action mc501cb_50HZScale[] = {
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x001d}, /* 00,36,1D,aa */
+@@ -3237,7 +3079,7 @@ static const struct usb_action MC501CB_50HZ[] = {
+ {}
+ };
+
+-static const struct usb_action MC501CB_50HZScale[] = {
++static const struct usb_action mc501cb_50HZ[] = {
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x003a}, /* 00,36,3A,aa */
+@@ -3254,7 +3096,7 @@ static const struct usb_action MC501CB_50HZScale[] = {
+ {}
+ };
+
+-static const struct usb_action MC501CB_60HZ[] = {
++static const struct usb_action mc501cb_60HZScale[] = {
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */
+@@ -3271,7 +3113,7 @@ static const struct usb_action MC501CB_60HZ[] = {
+ {}
+ };
+
+-static const struct usb_action MC501CB_60HZScale[] = {
++static const struct usb_action mc501cb_60HZ[] = {
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */
+@@ -3288,7 +3130,7 @@ static const struct usb_action MC501CB_60HZScale[] = {
+ {}
+ };
+
+-static const struct usb_action MC501CB_NoFliker[] = {
++static const struct usb_action mc501cb_NoFlikerScale[] = {
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x0018}, /* 00,36,18,aa */
+@@ -3305,7 +3147,7 @@ static const struct usb_action MC501CB_NoFliker[] = {
+ {}
+ };
+
+-static const struct usb_action MC501CB_NoFlikerScale[] = {
++static const struct usb_action mc501cb_NoFliker[] = {
+ {0xaa, 0x03, 0x0003}, /* 00,03,03,aa */
+ {0xaa, 0x10, 0x00fc}, /* 00,10,fc,aa */
+ {0xaa, 0x36, 0x0030}, /* 00,36,30,aa */
+@@ -3317,11 +3159,16 @@ static const struct usb_action MC501CB_NoFlikerScale[] = {
+ {}
+ };
+
+-/* from zs211.inf - HKR,%OV7620%,Initial - 640x480 */
+-static const struct usb_action OV7620_mode0[] = {
++/* from zs211.inf */
++static const struct usb_action ov7620_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT}, /* 00,02,40,cc */
++#if 1 /*jfm*/
+ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
++#else
++ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
++ /* mx change? */
++#endif
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x06, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,06,cc */
+ {0xa0, 0x02, ZC3XX_R083_RGAINADDR}, /* 00,83,02,cc */
+@@ -3387,9 +3234,7 @@ static const struct usb_action OV7620_mode0[] = {
+ {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */
+ {}
+ };
+-
+-/* from zs211.inf - HKR,%OV7620%,InitialScale - 320x240 */
+-static const struct usb_action OV7620_mode1[] = {
++static const struct usb_action ov7620_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT}, /* 00,02,50,cc */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
+@@ -3460,9 +3305,7 @@ static const struct usb_action OV7620_mode1[] = {
+ {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN}, /* 01,a8,50,cc */
+ {}
+ };
+-
+-/* from zs211.inf - HKR,%OV7620%\AE,50HZ */
+-static const struct usb_action OV7620_50HZ[] = {
++static const struct usb_action ov7620_50HZ[] = {
+ {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
+ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
+ {0xaa, 0x2b, 0x0096}, /* 00,2b,96,aa */
+@@ -3480,9 +3323,7 @@ static const struct usb_action OV7620_50HZ[] = {
+ if mode0 (640x480) */
+ {}
+ };
+-
+-/* from zs211.inf - HKR,%OV7620%\AE,60HZ */
+-static const struct usb_action OV7620_60HZ[] = {
++static const struct usb_action ov7620_60HZ[] = {
+ {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
+ /* (bug in zs211.inf) */
+ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
+@@ -3504,9 +3345,7 @@ static const struct usb_action OV7620_60HZ[] = {
+ {0xa1, 0x01, 0x0037}, */
+ {}
+ };
+-
+-/* from zs211.inf - HKR,%OV7620%\AE,NoFliker */
+-static const struct usb_action OV7620_NoFliker[] = {
++static const struct usb_action ov7620_NoFliker[] = {
+ {0xaa, 0x13, 0x00a3}, /* 00,13,a3,aa */
+ /* (bug in zs211.inf) */
+ {0xdd, 0x00, 0x0100}, /* 00,01,00,dd */
+@@ -3527,7 +3366,7 @@ static const struct usb_action OV7620_NoFliker[] = {
+ {}
+ };
+
+-static const struct usb_action ov7630c_Initial[] = {
++static const struct usb_action ov7630c_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+@@ -3684,7 +3523,7 @@ static const struct usb_action ov7630c_Initial[] = {
+ {}
+ };
+
+-static const struct usb_action ov7630c_InitialScale[] = {
++static const struct usb_action ov7630c_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+@@ -3855,7 +3694,7 @@ static const struct usb_action pas106b_Initial_com[] = {
+ {}
+ };
+
+-static const struct usb_action pas106b_Initial[] = { /* 176x144 */
++static const struct usb_action pas106b_InitialScale[] = { /* 176x144 */
+ /* JPEG control */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ /* Sream and Sensor specific */
+@@ -3973,7 +3812,7 @@ static const struct usb_action pas106b_Initial[] = { /* 176x144 */
+ {}
+ };
+
+-static const struct usb_action pas106b_InitialScale[] = { /* 352x288 */
++static const struct usb_action pas106b_Initial[] = { /* 352x288 */
+ /* JPEG control */
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ /* Sream and Sensor specific */
+@@ -4145,10 +3984,10 @@ static const struct usb_action pas106b_NoFliker[] = {
+ {}
+ };
+
+-/* from usbvm31b.inf */
++/* from lvWIMv.inf 046d:08a2/:08aa 2007/06/03 */
+ static const struct usb_action pas202b_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+- {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
++ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */
+ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 00,02,00,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+@@ -4173,7 +4012,7 @@ static const struct usb_action pas202b_Initial[] = { /* 640x480 */
+ {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */
+ {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */
+ {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */
+- {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */
++ {0xaa, 0x0c, 0x0006},
+ {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */
+@@ -4192,13 +4031,13 @@ static const struct usb_action pas202b_Initial[] = { /* 640x480 */
+ };
+ static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+- {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING}, /* 00,08,00,cc */
++ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0e, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,0e,cc */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH}, /* 00,03,02,cc */
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW}, /* 00,04,80,cc */
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH}, /* 00,05,01,cc */
+- {0xa0, 0xd0, ZC3XX_R006_FRAMEHEIGHTLOW}, /* 00,06,d0,cc */
++ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING}, /* 00,01,01,cc */
+ {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,03,cc */
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, /* 00,12,01,cc */
+@@ -4208,7 +4047,7 @@ static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x08, ZC3XX_R11A_FIRSTYLOW}, /* 01,1a,08,cc */
+ {0xa0, 0x02, ZC3XX_R11C_FIRSTXLOW}, /* 01,1c,02,cc */
+ {0xa0, 0x01, ZC3XX_R09B_WINHEIGHTHIGH}, /* 00,9b,01,cc */
+- {0xa0, 0xd8, ZC3XX_R09C_WINHEIGHTLOW}, /* 00,9c,d8,cc */
++ {0xa0, 0xe8, ZC3XX_R09C_WINHEIGHTLOW},
+ {0xa0, 0x02, ZC3XX_R09D_WINWIDTHHIGH}, /* 00,9d,02,cc */
+ {0xa0, 0x88, ZC3XX_R09E_WINWIDTHLOW}, /* 00,9e,88,cc */
+ {0xaa, 0x02, 0x0002}, /* 00,02,02,aa */
+@@ -4217,7 +4056,7 @@ static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */
+ {0xaa, 0x09, 0x0006}, /* 00,09,06,aa */
+ {0xaa, 0x0a, 0x0001}, /* 00,0a,01,aa */
+ {0xaa, 0x0b, 0x0001}, /* 00,0b,01,aa */
+- {0xaa, 0x0c, 0x0008}, /* 00,0c,08,aa */
++ {0xaa, 0x0c, 0x0006},
+ {0xaa, 0x0d, 0x0000}, /* 00,0d,00,aa */
+ {0xaa, 0x10, 0x0000}, /* 00,10,00,aa */
+ {0xaa, 0x12, 0x0005}, /* 00,12,05,aa */
+@@ -4232,6 +4071,8 @@ static const struct usb_action pas202b_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE}, /* 02,50,08,cc */
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS}, /* 03,01,08,cc */
+ {0xa0, 0x70, ZC3XX_R18D_YTARGET}, /* 01,8d,70,cc */
++ {0xa0, 0xff, ZC3XX_R097_WINYSTARTHIGH},
++ {0xa0, 0xfe, ZC3XX_R098_WINYSTARTLOW},
+ {}
+ };
+ static const struct usb_action pas202b_50HZ[] = {
+@@ -4239,22 +4080,22 @@ static const struct usb_action pas202b_50HZ[] = {
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+- {0xaa, 0x21, 0x0068}, /* 00,21,68,aa */
++ {0xaa, 0x21, 0x001b},
+ {0xaa, 0x03, 0x0044}, /* 00,03,44,aa */
+- {0xaa, 0x04, 0x0009}, /* 00,04,09,aa */
+- {0xaa, 0x05, 0x0028}, /* 00,05,28,aa */
++ {0xaa, 0x04, 0x0008},
++ {0xaa, 0x05, 0x001b},
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
++ {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+- {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,d2,cc */
++ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0x1b, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x4d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,4d,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
++ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x44, ZC3XX_R01D_HSYNC_0}, /* 00,1d,44,cc */
+ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
+ {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */
+@@ -4267,23 +4108,23 @@ static const struct usb_action pas202b_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+- {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+- {0xaa, 0x21, 0x006c}, /* 00,21,6c,aa */
++ {0xaa, 0x20, 0x0004},
++ {0xaa, 0x21, 0x003d},
+ {0xaa, 0x03, 0x0041}, /* 00,03,41,aa */
+- {0xaa, 0x04, 0x0009}, /* 00,04,09,aa */
+- {0xaa, 0x05, 0x002c}, /* 00,05,2c,aa */
++ {0xaa, 0x04, 0x0010},
++ {0xaa, 0x05, 0x003d},
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
++ {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */
+- {0xa0, 0xbe, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,be,cc */
++ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0x3d, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x9b, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,9b,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
++ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */
+ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
+ {0xa0, 0xad, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ad,cc */
+@@ -4303,16 +4144,16 @@ static const struct usb_action pas202b_60HZ[] = {
+ {0xaa, 0x05, 0x0000}, /* 00,05,00,aa */
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
++ {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+- {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,c0,cc */
++ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x40, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,40,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
++ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x45, ZC3XX_R01D_HSYNC_0}, /* 00,1d,45,cc */
+ {0xa0, 0x8e, ZC3XX_R01E_HSYNC_1}, /* 00,1e,8e,cc */
+ {0xa0, 0xc1, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c1,cc */
+@@ -4325,23 +4166,23 @@ static const struct usb_action pas202b_60HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+- {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+- {0xaa, 0x21, 0x0004}, /* 00,21,04,aa */
++ {0xaa, 0x20, 0x0004},
++ {0xaa, 0x21, 0x0008},
+ {0xaa, 0x03, 0x0042}, /* 00,03,42,aa */
+- {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+- {0xaa, 0x05, 0x0004}, /* 00,05,04,aa */
++ {0xaa, 0x04, 0x0010},
++ {0xaa, 0x05, 0x0008},
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,14,cc */
++ {0xa0, 0x1c, ZC3XX_R1A9_DIGITALLIMITDIFF},
+ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,24,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */
+- {0xa0, 0x9f, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,9f,cc */
++ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0x08, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,81,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
++ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x1b, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x42, ZC3XX_R01D_HSYNC_0}, /* 00,1d,42,cc */
+ {0xa0, 0x6f, ZC3XX_R01E_HSYNC_1}, /* 00,1e,6f,cc */
+ {0xa0, 0xaf, ZC3XX_R01F_HSYNC_2}, /* 00,1f,af,cc */
+@@ -4355,22 +4196,22 @@ static const struct usb_action pas202b_NoFliker[] = {
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+ {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+- {0xaa, 0x21, 0x0020}, /* 00,21,20,aa */
++ {0xaa, 0x21, 0x0006},
+ {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */
+ {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+- {0xaa, 0x05, 0x0020}, /* 00,05,20,aa */
++ {0xaa, 0x05, 0x0006},
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
++ {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0x06, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+- {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */
++ {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
++ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+@@ -4383,23 +4224,23 @@ static const struct usb_action pas202b_NoFlikerScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+ {0xa0, 0x20, ZC3XX_R087_EXPTIMEMID}, /* 00,87,20,cc */
+ {0xa0, 0x21, ZC3XX_R088_EXPTIMELOW}, /* 00,88,21,cc */
+- {0xaa, 0x20, 0x0002}, /* 00,20,02,aa */
+- {0xaa, 0x21, 0x0010}, /* 00,21,10,aa */
++ {0xaa, 0x20, 0x0004},
++ {0xaa, 0x21, 0x000c},
+ {0xaa, 0x03, 0x0040}, /* 00,03,40,aa */
+- {0xaa, 0x04, 0x0008}, /* 00,04,08,aa */
+- {0xaa, 0x05, 0x0010}, /* 00,05,10,aa */
++ {0xaa, 0x04, 0x0010},
++ {0xaa, 0x05, 0x000c},
+ {0xaa, 0x0e, 0x0001}, /* 00,0e,01,aa */
+ {0xaa, 0x0f, 0x0000}, /* 00,0f,00,aa */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x0f, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,0f,cc */
+- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
++ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0x0c, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x02, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,02,cc */
+ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
++ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0x40, ZC3XX_R01D_HSYNC_0}, /* 00,1d,40,cc */
+ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1}, /* 00,1e,60,cc */
+ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
+@@ -4409,170 +4250,80 @@ static const struct usb_action pas202b_NoFlikerScale[] = {
+ {}
+ };
+
+-static const struct usb_action pb03303x_Initial[] = {
++/* mi0360soc and pb0330 from vm30x.inf for 0ac8:301b and 0ac8:303b 07/02/13 */
++static const struct usb_action mi0360soc_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
++ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, /* 8b -> dc */
++ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
++ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, /*jfm: was 03*/
++/* {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
++ {0xdd, 0x00, 0x0200},
+ {0xaa, 0x01, 0x0001},
+ {0xaa, 0x06, 0x0000},
+ {0xaa, 0x08, 0x0483},
+ {0xaa, 0x01, 0x0004},
+ {0xaa, 0x08, 0x0006},
+ {0xaa, 0x02, 0x0011},
+- {0xaa, 0x03, 0x01e7},
+- {0xaa, 0x04, 0x0287},
++ {0xaa, 0x03, 0x01e5}, /*jfm: was 01e7*/
++ {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/
+ {0xaa, 0x07, 0x3002},
+- {0xaa, 0x20, 0x1100},
+- {0xaa, 0x35, 0x0050},
++ {0xaa, 0x20, 0x5100}, /*jfm: was 1100*/
++ {0xaa, 0x35, 0x507f}, /*jfm: was 0050*/
+ {0xaa, 0x30, 0x0005},
+ {0xaa, 0x31, 0x0000},
+ {0xaa, 0x58, 0x0078},
+ {0xaa, 0x62, 0x0411},
+ {0xaa, 0x2b, 0x0028},
+- {0xaa, 0x2c, 0x0030},
+- {0xaa, 0x2d, 0x0030},
+- {0xaa, 0x2e, 0x0028},
++ {0xaa, 0x2c, 0x007f}, /*jfm: was 0030*/
++ {0xaa, 0x2d, 0x007f}, /*jfm: was 0030*/
++ {0xaa, 0x2e, 0x007f}, /*jfm: was 0030*/
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
++ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /*jfm: was 37*/
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+- {0xa0, 0x00, 0x01ad},
++ {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+- {0xa0, 0x78, ZC3XX_R18D_YTARGET},
++ {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, /* jfm: was 78 */
+ {0xa0, 0x61, ZC3XX_R116_RGAIN},
+ {0xa0, 0x65, ZC3XX_R118_BGAIN},
+-
+- {0xa1, 0x01, 0x0002},
+- {0xa0, 0x09, 0x01ad},
+- {0xa0, 0x15, 0x01ae},
+- {0xa0, 0x0d, 0x003a},
+- {0xa0, 0x02, 0x003b},
+- {0xa0, 0x00, 0x0038},
+- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+- {0xa0, 0x50, ZC3XX_R10E_RGB11},
+- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+- {0xa0, 0xf8, ZC3XX_R110_RGB20},
+- {0xa0, 0xf8, ZC3XX_R111_RGB21},
+- {0xa0, 0x50, ZC3XX_R112_RGB22},
+-
+- {0xa1, 0x01, 0x0008},
+- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00},
+- {0xa1, 0x01, 0x01c8},
+- {0xa1, 0x01, 0x01c9},
+- {0xa1, 0x01, 0x01ca},
+- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+- {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */
+- {0xa0, 0x38, ZC3XX_R121_GAMMA01},
+- {0xa0, 0x59, ZC3XX_R122_GAMMA02},
+- {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+- {0xa0, 0x92, ZC3XX_R124_GAMMA04},
+- {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
+- {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
+- {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
+- {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
+- {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
+- {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
+- {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
+- {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
+- {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
+- {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
+- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+- {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+- {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+- {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+- {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+- {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+- {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+- {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+- {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+- {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+- {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+- {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+- {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+- {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+- {0xa0, 0x50, ZC3XX_R10E_RGB11},
+- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+- {0xa0, 0xf8, ZC3XX_R110_RGB20},
+- {0xa0, 0xf8, ZC3XX_R111_RGB21},
+- {0xa0, 0x50, ZC3XX_R112_RGB22},
+-
+- {0xa1, 0x01, 0x0180},
+- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+- {0xaa, 0x05, 0x0009},
+- {0xaa, 0x09, 0x0134},
+- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+- {0xa0, 0xec, ZC3XX_R192_EXPOSURELIMITLOW},
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+- {0xa0, 0x9c, ZC3XX_R197_ANTIFLICKERLOW},
+- {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+- {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+- {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+- {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+- {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa0, 0x09, 0x01ad},
+- {0xa0, 0x15, 0x01ae},
+- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa1, 0x01, 0x0180},
+- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+ };
+-
+-static const struct usb_action pb03303x_InitialScale[] = {
++static const struct usb_action mi0360soc_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
++ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+ {0xa0, 0xe0, ZC3XX_R006_FRAMEHEIGHTLOW},
+- {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR}, /* 8b -> dc */
++ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+- {0xa0, 0x03, ZC3XX_R012_VIDEOCONTROLFUNC},
+- {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
++ {0xa0, 0x07, ZC3XX_R012_VIDEOCONTROLFUNC}, /*jfm: was 03*/
++/* {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC}, */
+ {0xa0, 0x00, ZC3XX_R098_WINYSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
+ {0xa0, 0xdc, ZC3XX_R08B_I2CDEVICEADDR},
++ {0xdd, 0x00, 0x0200},
+ {0xaa, 0x01, 0x0001},
+ {0xaa, 0x06, 0x0000},
+ {0xaa, 0x08, 0x0483},
+@@ -4582,111 +4333,111 @@ static const struct usb_action pb03303x_InitialScale[] = {
+ {0xaa, 0x03, 0x01e7},
+ {0xaa, 0x04, 0x0287},
+ {0xaa, 0x07, 0x3002},
+- {0xaa, 0x20, 0x1100},
+- {0xaa, 0x35, 0x0050},
++ {0xaa, 0x20, 0x5100}, /*jfm: was 1100*/
++ {0xaa, 0x35, 0x007f}, /*jfm: was 0050*/
+ {0xaa, 0x30, 0x0005},
+ {0xaa, 0x31, 0x0000},
+ {0xaa, 0x58, 0x0078},
+ {0xaa, 0x62, 0x0411},
+- {0xaa, 0x2b, 0x0028},
+- {0xaa, 0x2c, 0x0030},
+- {0xaa, 0x2d, 0x0030},
+- {0xaa, 0x2e, 0x0028},
++ {0xaa, 0x2b, 0x007f}, /*jfm: was 28*/
++ {0xaa, 0x2c, 0x007f}, /*jfm: was 30*/
++ {0xaa, 0x2d, 0x007f}, /*jfm: was 30*/
++ {0xaa, 0x2e, 0x007f}, /*jfm: was 28*/
+ {0xa0, 0x10, ZC3XX_R087_EXPTIMEMID},
+- {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
++ {0xa0, 0xb7, ZC3XX_R101_SENSORCORRECTION}, /*jfm: was 37*/
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+- {0xa0, 0x00, 0x01ad},
++ {0xa0, 0x09, 0x01ad}, /*jfm: was 00*/
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+- {0xa0, 0x78, ZC3XX_R18D_YTARGET},
++ {0xa0, 0x6c, ZC3XX_R18D_YTARGET}, /*jfm: was 78*/
+ {0xa0, 0x61, ZC3XX_R116_RGAIN},
+ {0xa0, 0x65, ZC3XX_R118_BGAIN},
+-
+- {0xa1, 0x01, 0x0002},
+-
+- {0xa0, 0x09, 0x01ad},
+- {0xa0, 0x15, 0x01ae},
+-
+- {0xa0, 0x0d, 0x003a},
+- {0xa0, 0x02, 0x003b},
+- {0xa0, 0x00, 0x0038},
+- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+- {0xa0, 0x50, ZC3XX_R10E_RGB11},
+- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+- {0xa0, 0xf8, ZC3XX_R110_RGB20},
+- {0xa0, 0xf8, ZC3XX_R111_RGB21},
+- {0xa0, 0x50, ZC3XX_R112_RGB22},
+-
+- {0xa1, 0x01, 0x0008},
+- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+- {0xa1, 0x01, 0x01c8},
+- {0xa1, 0x01, 0x01c9},
+- {0xa1, 0x01, 0x01ca},
+- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+-
+- {0xa0, 0x13, ZC3XX_R120_GAMMA00}, /* gamma 4 */
+- {0xa0, 0x38, ZC3XX_R121_GAMMA01},
+- {0xa0, 0x59, ZC3XX_R122_GAMMA02},
+- {0xa0, 0x79, ZC3XX_R123_GAMMA03},
+- {0xa0, 0x92, ZC3XX_R124_GAMMA04},
+- {0xa0, 0xa7, ZC3XX_R125_GAMMA05},
+- {0xa0, 0xb9, ZC3XX_R126_GAMMA06},
+- {0xa0, 0xc8, ZC3XX_R127_GAMMA07},
+- {0xa0, 0xd4, ZC3XX_R128_GAMMA08},
+- {0xa0, 0xdf, ZC3XX_R129_GAMMA09},
+- {0xa0, 0xe7, ZC3XX_R12A_GAMMA0A},
+- {0xa0, 0xee, ZC3XX_R12B_GAMMA0B},
+- {0xa0, 0xf4, ZC3XX_R12C_GAMMA0C},
+- {0xa0, 0xf9, ZC3XX_R12D_GAMMA0D},
+- {0xa0, 0xfc, ZC3XX_R12E_GAMMA0E},
+- {0xa0, 0xff, ZC3XX_R12F_GAMMA0F},
+- {0xa0, 0x26, ZC3XX_R130_GAMMA10},
+- {0xa0, 0x22, ZC3XX_R131_GAMMA11},
+- {0xa0, 0x20, ZC3XX_R132_GAMMA12},
+- {0xa0, 0x1c, ZC3XX_R133_GAMMA13},
+- {0xa0, 0x16, ZC3XX_R134_GAMMA14},
+- {0xa0, 0x13, ZC3XX_R135_GAMMA15},
+- {0xa0, 0x10, ZC3XX_R136_GAMMA16},
+- {0xa0, 0x0d, ZC3XX_R137_GAMMA17},
+- {0xa0, 0x0b, ZC3XX_R138_GAMMA18},
+- {0xa0, 0x09, ZC3XX_R139_GAMMA19},
+- {0xa0, 0x07, ZC3XX_R13A_GAMMA1A},
+- {0xa0, 0x06, ZC3XX_R13B_GAMMA1B},
+- {0xa0, 0x05, ZC3XX_R13C_GAMMA1C},
+- {0xa0, 0x04, ZC3XX_R13D_GAMMA1D},
+- {0xa0, 0x03, ZC3XX_R13E_GAMMA1E},
+- {0xa0, 0x02, ZC3XX_R13F_GAMMA1F},
+- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+- {0xa0, 0x50, ZC3XX_R10E_RGB11},
+- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+- {0xa0, 0xf8, ZC3XX_R110_RGB20},
+- {0xa0, 0xf8, ZC3XX_R111_RGB21},
+- {0xa0, 0x50, ZC3XX_R112_RGB22},
+-
+- {0xa1, 0x01, 0x0180},
++ {}
++};
++static const struct usb_action mi360soc_AE50HZ[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
++ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
++ {0xbb, 0x00, 0x0562},
++ {0xbb, 0x01, 0x09aa},
++ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
++ {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0x9b, ZC3XX_R192_EXPOSURELIMITLOW},
++ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
++ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
++ {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
++ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
++ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},
++ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
++ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
++ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
++ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
++ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
++ {}
++};
++static const struct usb_action mi360soc_AE50HZScale[] = {
++ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
++ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
++ {0xbb, 0x00, 0x0509},
++ {0xbb, 0x01, 0x0934},
++ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
++ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0xd2, ZC3XX_R192_EXPOSURELIMITLOW},
++ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
++ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
++ {0xa0, 0x9a, ZC3XX_R197_ANTIFLICKERLOW},
++ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
++ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
++ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
++ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
++ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
++ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
++ {}
++};
++static const struct usb_action mi360soc_AE60HZ[] = {
+ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+- {0xaa, 0x05, 0x0009},
+- {0xaa, 0x09, 0x0134},
++ {0xbb, 0x00, 0x053d},
++ {0xbb, 0x01, 0x096e},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+- {0xa0, 0xec, ZC3XX_R192_EXPOSURELIMITLOW},
++ {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+- {0xa0, 0x9c, ZC3XX_R197_ANTIFLICKERLOW},
++ {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW},
++ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
++ {0xa0, 0x62, ZC3XX_R01D_HSYNC_0},
++ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
++ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
++ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
++ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
++ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
++ {}
++};
++static const struct usb_action mi360soc_AE60HZScale[] = {
++ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
++ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
++ {0xbb, 0x00, 0x0509},
++ {0xbb, 0x01, 0x0983},
++ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
++ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0x8f, ZC3XX_R192_EXPOSURELIMITLOW},
++ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
++ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
++ {0xa0, 0x81, ZC3XX_R197_ANTIFLICKERLOW},
+ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
+ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+@@ -4696,20 +4447,60 @@ static const struct usb_action pb03303x_InitialScale[] = {
+ {0xa0, 0xf9, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa0, 0x09, 0x01ad},
+- {0xa0, 0x15, 0x01ae},
+- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa1, 0x01, 0x0180},
++ {}
++};
++static const struct usb_action mi360soc_AENoFliker[] = {
++ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
++ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
++ {0xbb, 0x00, 0x0509},
++ {0xbb, 0x01, 0x0960},
++ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
++ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
++ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
++ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
++ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
++ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
++ {0xa0, 0x09, ZC3XX_R01D_HSYNC_0},
++ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
++ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
++ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
++ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+ {}
+ };
+-static const struct usb_action pb0330xx_Initial[] = {
+- {0xa1, 0x01, 0x0008},
+- {0xa1, 0x01, 0x0008},
++static const struct usb_action mi360soc_AENoFlikerScale[] = {
++ {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
++ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
++ {0xbb, 0x00, 0x0534},
++ {0xbb, 0x02, 0x0960},
++ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
++ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
++ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
++ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
++ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
++ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x1c, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
++ {0xa0, 0x34, ZC3XX_R01D_HSYNC_0},
++ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
++ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
++ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
++ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
++ {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
++ {}
++};
++
++static const struct usb_action pb0330_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+- {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
++ {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+@@ -4721,11 +4512,12 @@ static const struct usb_action pb0330xx_Initial[] = {
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
++ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x0006},
+ {0xaa, 0x02, 0x0011},
+- {0xaa, 0x03, 0x01e7},
+- {0xaa, 0x04, 0x0287},
++ {0xaa, 0x03, 0x01e5}, /*jfm: was 1e7*/
++ {0xaa, 0x04, 0x0285}, /*jfm: was 0287*/
+ {0xaa, 0x06, 0x0003},
+ {0xaa, 0x07, 0x3002},
+ {0xaa, 0x20, 0x1100},
+@@ -4743,88 +4535,21 @@ static const struct usb_action pb0330xx_Initial[] = {
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+- {0xa0, 0x00, 0x01ad},
++ {0xa0, 0x09, 0x01ad}, /*jfm: was 00 */
++ {0xa0, 0x15, 0x01ae},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+- {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
+- {0xa1, 0x01, 0x0002},
+- {0xa0, 0x09, 0x01ad},
+- {0xa0, 0x15, 0x01ae},
+- {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
+- {0xa1, 0x01, 0x0091},
+- {0xa1, 0x01, 0x0095},
+- {0xa1, 0x01, 0x0096},
+- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+- {0xa0, 0x50, ZC3XX_R10E_RGB11},
+- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+- {0xa0, 0xf8, ZC3XX_R110_RGB20},
+- {0xa0, 0xf8, ZC3XX_R111_RGB21},
+- {0xa0, 0x50, ZC3XX_R112_RGB22},
+- {0xa1, 0x01, 0x0008},
+- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+- {0xa1, 0x01, 0x01c8},
+- {0xa1, 0x01, 0x01c9},
+- {0xa1, 0x01, 0x01ca},
+- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+-
+- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+- {0xa0, 0x50, ZC3XX_R10E_RGB11},
+- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+- {0xa0, 0xf8, ZC3XX_R110_RGB20},
+- {0xa0, 0xf8, ZC3XX_R111_RGB21},
+- {0xa0, 0x50, ZC3XX_R112_RGB22},
+- {0xa1, 0x01, 0x0180},
+- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+- {0xaa, 0x05, 0x0066},
+- {0xaa, 0x09, 0x02b2},
+- {0xaa, 0x10, 0x0002},
+-
+- {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+- {0xa0, 0x8c, ZC3XX_R192_EXPOSURELIMITLOW},
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+- {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW},
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
+- {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+- {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+- {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1},
+- {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
+- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+- {0xa0, 0x09, 0x01ad},
+- {0xa0, 0x15, 0x01ae},
+- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa1, 0x01, 0x0180},
+- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa1, 0x01, 0x0008},
+- {0xa1, 0x01, 0x0007},
+-/* {0xa0, 0x30, 0x0007}, */
+-/* {0xa0, 0x00, 0x0007}, */
++ {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/
+ {}
+ };
+-
+-static const struct usb_action pb0330xx_InitialScale[] = {
+- {0xa1, 0x01, 0x0008},
+- {0xa1, 0x01, 0x0008},
++static const struct usb_action pb0330_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* 00 */
+ {0xa0, 0x0a, ZC3XX_R010_CMOSSENSORSELECT},
+- {0xa0, 0x00, ZC3XX_R002_CLOCKSELECT}, /* 10 */
++ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+@@ -4836,6 +4561,7 @@ static const struct usb_action pb0330xx_InitialScale[] = {
+ {0xa0, 0x00, ZC3XX_R09A_WINXSTARTLOW},
+ {0xa0, 0x00, ZC3XX_R11A_FIRSTYLOW},
+ {0xa0, 0x00, ZC3XX_R11C_FIRSTXLOW},
++ {0xdd, 0x00, 0x0200},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xaa, 0x01, 0x0006},
+ {0xaa, 0x02, 0x0011},
+@@ -4858,53 +4584,43 @@ static const struct usb_action pb0330xx_InitialScale[] = {
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+- {0xa0, 0x00, 0x01ad},
++ {0xa0, 0x09, 0x01ad},
++ {0xa0, 0x15, 0x01ae},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+ {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
+- {0xa0, 0x6c, ZC3XX_R18D_YTARGET},
+- {0xa1, 0x01, 0x0002},
+- {0xa0, 0x09, 0x01ad},
+- {0xa0, 0x15, 0x01ae},
+- {0xa0, 0x00, ZC3XX_R092_I2CADDRESSSELECT},
+- {0xa0, 0x02, ZC3XX_R090_I2CCOMMAND},
+- {0xa1, 0x01, 0x0091},
+- {0xa1, 0x01, 0x0095},
+- {0xa1, 0x01, 0x0096},
+- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+- {0xa0, 0x50, ZC3XX_R10E_RGB11},
+- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+- {0xa0, 0xf8, ZC3XX_R110_RGB20},
+- {0xa0, 0xf8, ZC3XX_R111_RGB21},
+- {0xa0, 0x50, ZC3XX_R112_RGB22},
+- {0xa1, 0x01, 0x0008},
+- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+- {0xa1, 0x01, 0x01c8},
+- {0xa1, 0x01, 0x01c9},
+- {0xa1, 0x01, 0x01ca},
+- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+-
+- {0xa0, 0x50, ZC3XX_R10A_RGB00}, /* matrix */
+- {0xa0, 0xf8, ZC3XX_R10B_RGB01},
+- {0xa0, 0xf8, ZC3XX_R10C_RGB02},
+- {0xa0, 0xf8, ZC3XX_R10D_RGB10},
+- {0xa0, 0x50, ZC3XX_R10E_RGB11},
+- {0xa0, 0xf8, ZC3XX_R10F_RGB12},
+- {0xa0, 0xf8, ZC3XX_R110_RGB20},
+- {0xa0, 0xf8, ZC3XX_R111_RGB21},
+- {0xa0, 0x50, ZC3XX_R112_RGB22},
+- {0xa1, 0x01, 0x0180},
+- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
++ {0xa0, 0x78, ZC3XX_R18D_YTARGET}, /*jfm: was 6c*/
++ {}
++};
++static const struct usb_action pb0330_50HZ[] = {
++ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
++ {0xbb, 0x00, 0x055c},
++ {0xbb, 0x01, 0x09aa},
++ {0xbb, 0x00, 0x1001},
++ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
++ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
++ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0xc4, ZC3XX_R192_EXPOSURELIMITLOW},
++ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
++ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
++ {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
++ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
++ {0xa0, 0x5c, ZC3XX_R01D_HSYNC_0},
++ {0xa0, 0x90, ZC3XX_R01E_HSYNC_1},
++ {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2},
++ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
++ {}
++};
++static const struct usb_action pb0330_50HZScale[] = {
+ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+- {0xaa, 0x05, 0x0066},
+- {0xaa, 0x09, 0x02b2},
+- {0xaa, 0x10, 0x0002},
++ {0xbb, 0x00, 0x0566},
++ {0xbb, 0x02, 0x09b2},
++ {0xbb, 0x00, 0x1002},
+ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
+@@ -4912,129 +4628,107 @@ static const struct usb_action pb0330xx_InitialScale[] = {
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+ {0xa0, 0x8a, ZC3XX_R197_ANTIFLICKERLOW},
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
+- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
++ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd7, ZC3XX_R01D_HSYNC_0},
+ {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1},
+ {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+- {0xa0, 0x09, 0x01ad},
+- {0xa0, 0x15, 0x01ae},
+- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa1, 0x01, 0x0180},
+- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa1, 0x01, 0x0008},
+- {0xa1, 0x01, 0x0007},
+-/* {0xa0, 0x30, 0x0007}, */
+-/* {0xa0, 0x00, 0x0007}, */
+- {}
+-};
+-static const struct usb_action pb0330_50HZ[] = {
+- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+- {0xa0, 0xee, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,ee,cc */
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+- {0xa0, 0x46, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,46,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+- {0xa0, 0x68, ZC3XX_R01D_HSYNC_0}, /* 00,1d,68,cc */
+- {0xa0, 0x90, ZC3XX_R01E_HSYNC_1}, /* 00,1e,90,cc */
+- {0xa0, 0xc8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,c8,cc */
+- {}
+-};
+-static const struct usb_action pb0330_50HZScale[] = {
+- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+- {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,a0,cc */
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+- {0xa0, 0x7a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7a,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+- {0xa0, 0xe5, ZC3XX_R01D_HSYNC_0}, /* 00,1d,e5,cc */
+- {0xa0, 0xf0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f0,cc */
+- {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */
+ {}
+ };
+ static const struct usb_action pb0330_60HZ[] = {
+- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+- {0xa0, 0xdd, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,dd,cc */
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+- {0xa0, 0x3d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3d,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+- {0xa0, 0x43, ZC3XX_R01D_HSYNC_0}, /* 00,1d,43,cc */
+- {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */
+- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
++ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
++ {0xbb, 0x00, 0x0535},
++ {0xbb, 0x01, 0x0974},
++ {0xbb, 0x00, 0x1001},
++ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
++ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
++ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW},
++ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
++ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
++ {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW},
++ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
++ {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
++ {0xa0, 0x50, ZC3XX_R01E_HSYNC_1},
++ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
++ {0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
+ {}
+ };
+ static const struct usb_action pb0330_60HZScale[] = {
+- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+- {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,a0,cc */
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+- {0xa0, 0x7a, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7a,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
+- {0xa0, 0x41, ZC3XX_R01D_HSYNC_0}, /* 00,1d,41,cc */
+- {0xa0, 0x50, ZC3XX_R01E_HSYNC_1}, /* 00,1e,50,cc */
+- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
++ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
++ {0xbb, 0x00, 0x0535},
++ {0xbb, 0x02, 0x096c},
++ {0xbb, 0x00, 0x1002},
++ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
++ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
++ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0xc0, ZC3XX_R192_EXPOSURELIMITLOW},
++ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
++ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
++ {0xa0, 0x7c, ZC3XX_R197_ANTIFLICKERLOW},
++ {0xa0, 0x0e, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x1a, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x14, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x66, ZC3XX_R1AA_DIGITALGAINSTEP},
++ {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
++ {0xa0, 0x50, ZC3XX_R01E_HSYNC_1},
++ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
++ {0xa0, 0xd0, ZC3XX_R020_HSYNC_3},
+ {}
+ };
+ static const struct usb_action pb0330_NoFliker[] = {
+- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+- {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, /* 00,1d,09,cc */
+- {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */
+- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
++ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
++ {0xbb, 0x00, 0x0509},
++ {0xbb, 0x02, 0x0940},
++ {0xbb, 0x00, 0x1002},
++ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
++ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
++ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
++ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
++ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
++ {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
++ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
++ {0xa0, 0x09, ZC3XX_R01D_HSYNC_0},
++ {0xa0, 0x40, ZC3XX_R01E_HSYNC_1},
++ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
++ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {}
+ };
+ static const struct usb_action pb0330_NoFlikerScale[] = {
+- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS}, /* 00,19,00,cc */
+- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,07,cc */
+- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+- {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+- {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+- {0xa0, 0x09, ZC3XX_R01D_HSYNC_0}, /* 00,1d,09,cc */
+- {0xa0, 0x40, ZC3XX_R01E_HSYNC_1}, /* 00,1e,40,cc */
+- {0xa0, 0x90, ZC3XX_R01F_HSYNC_2}, /* 00,1f,90,cc */
++ {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
++ {0xbb, 0x00, 0x0535},
++ {0xbb, 0x01, 0x0980},
++ {0xbb, 0x00, 0x1001},
++ {0xa0, 0x60, ZC3XX_R11D_GLOBALGAIN},
++ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
++ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW},
++ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
++ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
++ {0xa0, 0x01, ZC3XX_R197_ANTIFLICKERLOW},
++ {0xa0, 0x10, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP},
++ {0xa0, 0x35, ZC3XX_R01D_HSYNC_0},
++ {0xa0, 0x60, ZC3XX_R01E_HSYNC_1},
++ {0xa0, 0x90, ZC3XX_R01F_HSYNC_2},
++ {0xa0, 0xe0, ZC3XX_R020_HSYNC_3},
+ {}
+ };
+
+-/* from oem9.inf - HKR,%PO2030%,Initial - 640x480 - (close to CS2102) */
+-static const struct usb_action PO2030_mode0[] = {
++/* from oem9.inf */
++static const struct usb_action po2030_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x04, ZC3XX_R002_CLOCKSELECT}, /* 00,02,04,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+@@ -5110,8 +4804,8 @@ static const struct usb_action PO2030_mode0[] = {
+ {}
+ };
+
+-/* from oem9.inf - HKR,%PO2030%,InitialScale - 320x240 */
+-static const struct usb_action PO2030_mode1[] = {
++/* from oem9.inf */
++static const struct usb_action po2030_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc */
+ {0xa0, 0x10, ZC3XX_R002_CLOCKSELECT}, /* 00,02,10,cc */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc */
+@@ -5187,7 +4881,7 @@ static const struct usb_action PO2030_mode1[] = {
+ {}
+ };
+
+-static const struct usb_action PO2030_50HZ[] = {
++static const struct usb_action po2030_50HZ[] = {
+ {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
+ {0xaa, 0x1a, 0x0001}, /* 00,1a,01,aa */
+ {0xaa, 0x1b, 0x000a}, /* 00,1b,0a,aa */
+@@ -5209,7 +4903,7 @@ static const struct usb_action PO2030_50HZ[] = {
+ {}
+ };
+
+-static const struct usb_action PO2030_60HZ[] = {
++static const struct usb_action po2030_60HZ[] = {
+ {0xaa, 0x8d, 0x0008}, /* 00,8d,08,aa */
+ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
+ {0xaa, 0x1b, 0x00de}, /* 00,1b,de,aa */
+@@ -5232,7 +4926,7 @@ static const struct usb_action PO2030_60HZ[] = {
+ {}
+ };
+
+-static const struct usb_action PO2030_NoFliker[] = {
++static const struct usb_action po2030_NoFliker[] = {
+ {0xa0, 0x02, ZC3XX_R180_AUTOCORRECTENABLE}, /* 01,80,02,cc */
+ {0xaa, 0x8d, 0x000d}, /* 00,8d,0d,aa */
+ {0xaa, 0x1a, 0x0000}, /* 00,1a,00,aa */
+@@ -5244,7 +4938,7 @@ static const struct usb_action PO2030_NoFliker[] = {
+ };
+
+ /* TEST */
+-static const struct usb_action tas5130CK_Initial[] = {
++static const struct usb_action tas5130cK_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x01, 0x003b},
+ {0xa0, 0x0e, 0x003a},
+@@ -5447,7 +5141,7 @@ static const struct usb_action tas5130CK_Initial[] = {
+ {}
+ };
+
+-static const struct usb_action tas5130CK_InitialScale[] = {
++static const struct usb_action tas5130cK_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x01, 0x003b},
+ {0xa0, 0x0e, 0x003a},
+@@ -5655,7 +5349,7 @@ static const struct usb_action tas5130CK_InitialScale[] = {
+ {}
+ };
+
+-static const struct usb_action tas5130cxx_Initial[] = {
++static const struct usb_action tas5130cxx_InitialScale[] = { /* 320x240 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x50, ZC3XX_R002_CLOCKSELECT},
+ {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+@@ -5665,9 +5359,6 @@ static const struct usb_action tas5130cxx_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+- {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN},
+- {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
+-
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+@@ -5684,82 +5375,27 @@ static const struct usb_action tas5130cxx_Initial[] = {
+ {0xa0, 0xf7, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+- {0xa0, 0x68, ZC3XX_R18D_YTARGET},
+- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
++ {0xa0, 0x70, ZC3XX_R18D_YTARGET},
++ {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+- {0xa1, 0x01, 0x0002},
+- {0xa1, 0x01, 0x0008},
+- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING}, /* clock ? */
+- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+- {0xa1, 0x01, 0x01c8},
+- {0xa1, 0x01, 0x01c9},
+- {0xa1, 0x01, 0x01ca},
+- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+-
+- {0xa0, 0x68, ZC3XX_R10A_RGB00}, /* matrix */
+- {0xa0, 0xec, ZC3XX_R10B_RGB01},
+- {0xa0, 0xec, ZC3XX_R10C_RGB02},
+- {0xa0, 0xec, ZC3XX_R10D_RGB10},
+- {0xa0, 0x68, ZC3XX_R10E_RGB11},
+- {0xa0, 0xec, ZC3XX_R10F_RGB12},
+- {0xa0, 0xec, ZC3XX_R110_RGB20},
+- {0xa0, 0xec, ZC3XX_R111_RGB21},
+- {0xa0, 0x68, ZC3XX_R112_RGB22},
+-
+- {0xa1, 0x01, 0x018d},
+- {0xa0, 0x90, ZC3XX_R18D_YTARGET}, /* 90 */
+- {0xa1, 0x01, 0x0180},
+- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+-
+- {0xaa, 0xa3, 0x0001},
+- {0xaa, 0xa4, 0x0077},
+- {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH},
+- {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW},
+-
+- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 00 */
+- {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 03 */
+- {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* e8 */
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 0 */
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 0 */
+- {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 7d */
+-
+- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+- {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 08 */
+- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 24 */
+- {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0},
+- {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1},
+- {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2},
+- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+- {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH},
+- {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW},
+- {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN}, /* 50 */
+- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa1, 0x01, 0x0180},
+- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
++ {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN},
++ {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
+ {}
+ };
+-static const struct usb_action tas5130cxx_InitialScale[] = {
+-/*?? {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, */
++static const struct usb_action tas5130cxx_Initial[] = { /* 640x480 */
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL},
+ {0xa0, 0x40, ZC3XX_R002_CLOCKSELECT},
+-
+- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+- {0xa1, 0x01, 0x0008},
+-
++ {0xa0, 0x00, ZC3XX_R008_CLOCKSETTING},
+ {0xa0, 0x02, ZC3XX_R010_CMOSSENSORSELECT},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x00, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x01, ZC3XX_R012_VIDEOCONTROLFUNC},
+ {0xa0, 0x01, ZC3XX_R001_SYSTEMOPERATING},
+ {0xa0, 0x05, ZC3XX_R012_VIDEOCONTROLFUNC},
+- {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN},
+- {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
+ {0xa0, 0x02, ZC3XX_R003_FRAMEWIDTHHIGH},
+ {0xa0, 0x80, ZC3XX_R004_FRAMEWIDTHLOW},
+ {0xa0, 0x01, ZC3XX_R005_FRAMEHEIGHTHIGH},
+@@ -5775,63 +5411,15 @@ static const struct usb_action tas5130cxx_InitialScale[] = {
+ {0xa0, 0x37, ZC3XX_R101_SENSORCORRECTION},
+ {0xa0, 0x0d, ZC3XX_R100_OPERATIONMODE},
+ {0xa0, 0x06, ZC3XX_R189_AWBSTATUS},
+- {0xa0, 0x68, ZC3XX_R18D_YTARGET},
+- {0xa0, 0x60, ZC3XX_R1A8_DIGITALGAIN},
++ {0xa0, 0x70, ZC3XX_R18D_YTARGET},
++ {0xa0, 0x50, ZC3XX_R1A8_DIGITALGAIN},
+ {0xa0, 0x00, 0x01ad},
+ {0xa0, 0x03, ZC3XX_R1C5_SHARPNESSMODE},
+ {0xa0, 0x13, ZC3XX_R1CB_SHARPNESS05},
+ {0xa0, 0x08, ZC3XX_R250_DEADPIXELSMODE},
+ {0xa0, 0x08, ZC3XX_R301_EEPROMACCESS},
+- {0xa1, 0x01, 0x0002},
+- {0xa1, 0x01, 0x0008},
+-
+- {0xa0, 0x03, ZC3XX_R008_CLOCKSETTING},
+- {0xa1, 0x01, 0x0008}, /* clock ? */
+- {0xa0, 0x08, ZC3XX_R1C6_SHARPNESS00}, /* sharpness+ */
+- {0xa1, 0x01, 0x01c8},
+- {0xa1, 0x01, 0x01c9},
+- {0xa1, 0x01, 0x01ca},
+- {0xa0, 0x0f, ZC3XX_R1CB_SHARPNESS05}, /* sharpness- */
+-
+- {0xa0, 0x68, ZC3XX_R10A_RGB00}, /* matrix */
+- {0xa0, 0xec, ZC3XX_R10B_RGB01},
+- {0xa0, 0xec, ZC3XX_R10C_RGB02},
+- {0xa0, 0xec, ZC3XX_R10D_RGB10},
+- {0xa0, 0x68, ZC3XX_R10E_RGB11},
+- {0xa0, 0xec, ZC3XX_R10F_RGB12},
+- {0xa0, 0xec, ZC3XX_R110_RGB20},
+- {0xa0, 0xec, ZC3XX_R111_RGB21},
+- {0xa0, 0x68, ZC3XX_R112_RGB22},
+-
+- {0xa1, 0x01, 0x018d},
+- {0xa0, 0x90, ZC3XX_R18D_YTARGET},
+- {0xa1, 0x01, 0x0180},
+- {0xa0, 0x00, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa0, 0x00, ZC3XX_R019_AUTOADJUSTFPS},
+- {0xaa, 0xa3, 0x0001},
+- {0xaa, 0xa4, 0x0063},
+- {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH},
+- {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW},
+- {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH},
+- {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID},
+- {0xa0, 0x38, ZC3XX_R192_EXPOSURELIMITLOW},
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
+- {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW},
+- {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
+- {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+- {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
+- {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+- {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0},
+- {0xa0, 0xda, ZC3XX_R01E_HSYNC_1},
+- {0xa0, 0xea, ZC3XX_R01F_HSYNC_2},
+- {0xa0, 0xff, ZC3XX_R020_HSYNC_3},
+- {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH},
+- {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW},
+- {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+- {0xa0, 0x40, ZC3XX_R180_AUTOCORRECTENABLE},
+- {0xa1, 0x01, 0x0180},
+- {0xa0, 0x42, ZC3XX_R180_AUTOCORRECTENABLE},
++ {0xa0, 0x07, ZC3XX_R0A5_EXPOSUREGAIN},
++ {0xa0, 0x02, ZC3XX_R0A6_EXPOSUREBLACKLVL},
+ {}
+ };
+ static const struct usb_action tas5130cxx_50HZ[] = {
+@@ -5841,20 +5429,22 @@ static const struct usb_action tas5130cxx_50HZ[] = {
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+ {0xa0, 0x63, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,63,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x02, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,02,cc */
+- {0xa0, 0x38, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,38,cc */
++ {0xa0, 0x04, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0xfe, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x47, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,47,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
++ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xd3, ZC3XX_R01D_HSYNC_0}, /* 00,1d,d3,cc */
+ {0xa0, 0xda, ZC3XX_R01E_HSYNC_1}, /* 00,1e,da,cc */
+ {0xa0, 0xea, ZC3XX_R01F_HSYNC_2}, /* 00,1f,ea,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
++ {0xa0, 0x4c, ZC3XX_R0A0_MAXXLOW},
++ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {}
+ };
+ static const struct usb_action tas5130cxx_50HZScale[] = {
+@@ -5864,20 +5454,22 @@ static const struct usb_action tas5130cxx_50HZScale[] = {
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+ {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */
+- {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e8,cc */
++ {0xa0, 0x07, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0xd0, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */
+- {0xa0, 0x14, ZC3XX_R18C_AEFREEZE}, /* 01,8c,14,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
++ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xf0, ZC3XX_R01D_HSYNC_0}, /* 00,1d,f0,cc */
+ {0xa0, 0xf4, ZC3XX_R01E_HSYNC_1}, /* 00,1e,f4,cc */
+ {0xa0, 0xf8, ZC3XX_R01F_HSYNC_2}, /* 00,1f,f8,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
++ {0xa0, 0xc0, ZC3XX_R0A0_MAXXLOW},
++ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {}
+ };
+ static const struct usb_action tas5130cxx_60HZ[] = {
+@@ -5887,20 +5479,22 @@ static const struct usb_action tas5130cxx_60HZ[] = {
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+ {0xa0, 0x36, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,36,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,01,cc */
+- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
++ {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0x54, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x3e, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,3e,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
++ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xca, ZC3XX_R01D_HSYNC_0}, /* 00,1d,ca,cc */
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
+ {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
++ {0xa0, 0x28, ZC3XX_R0A0_MAXXLOW},
++ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {}
+ };
+ static const struct usb_action tas5130cxx_60HZScale[] = {
+@@ -5910,20 +5504,22 @@ static const struct usb_action tas5130cxx_60HZScale[] = {
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+ {0xa0, 0x77, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,77,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */
+- {0xa0, 0xe8, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,e8,cc */
++ {0xa0, 0x09, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0x47, ZC3XX_R192_EXPOSURELIMITLOW},
+ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+ {0xa0, 0x7d, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,7d,cc */
+- {0xa0, 0x14, ZC3XX_R18C_AEFREEZE}, /* 01,8c,14,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
+- {0xa0, 0x0c, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,0c,cc */
+- {0xa0, 0x26, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,26,cc */
++ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
++ {0xa0, 0x08, ZC3XX_R1A9_DIGITALLIMITDIFF},
++ {0xa0, 0x24, ZC3XX_R1AA_DIGITALGAINSTEP},
+ {0xa0, 0xc8, ZC3XX_R01D_HSYNC_0}, /* 00,1d,c8,cc */
+ {0xa0, 0xd0, ZC3XX_R01E_HSYNC_1}, /* 00,1e,d0,cc */
+ {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x03, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,03,cc */
++ {0xa0, 0x20, ZC3XX_R0A0_MAXXLOW},
++ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {}
+ };
+ static const struct usb_action tas5130cxx_NoFliker[] = {
+@@ -5933,13 +5529,13 @@ static const struct usb_action tas5130cxx_NoFliker[] = {
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+ {0xa0, 0x40, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,40,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x01, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,01,cc */
+- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
++ {0xa0, 0x05, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0xa0, ZC3XX_R192_EXPOSURELIMITLOW},
++ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
++ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
++ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
++ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */
+@@ -5947,6 +5543,8 @@ static const struct usb_action tas5130cxx_NoFliker[] = {
+ {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */
++ {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW},
++ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {}
+ };
+
+@@ -5957,13 +5555,13 @@ static const struct usb_action tas5130cxx_NoFlikerScale[] = {
+ {0xa0, 0x01, ZC3XX_R0A3_EXPOSURETIMEHIGH}, /* 00,a3,01,cc */
+ {0xa0, 0x90, ZC3XX_R0A4_EXPOSURETIMELOW}, /* 00,a4,90,cc */
+ {0xa0, 0x00, ZC3XX_R190_EXPOSURELIMITHIGH}, /* 01,90,00,cc */
+- {0xa0, 0x03, ZC3XX_R191_EXPOSURELIMITMID}, /* 01,91,03,cc */
+- {0xa0, 0xf0, ZC3XX_R192_EXPOSURELIMITLOW}, /* 01,92,f0,cc */
+- {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH}, /* 01,95,00,cc */
+- {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID}, /* 01,96,00,cc */
+- {0xa0, 0x10, ZC3XX_R197_ANTIFLICKERLOW}, /* 01,97,10,cc */
+- {0xa0, 0x10, ZC3XX_R18C_AEFREEZE}, /* 01,8c,10,cc */
+- {0xa0, 0x20, ZC3XX_R18F_AEUNFREEZE}, /* 01,8f,20,cc */
++ {0xa0, 0x0a, ZC3XX_R191_EXPOSURELIMITMID},
++ {0xa0, 0x00, ZC3XX_R192_EXPOSURELIMITLOW},
++ {0xa0, 0x00, ZC3XX_R195_ANTIFLICKERHIGH},
++ {0xa0, 0x00, ZC3XX_R196_ANTIFLICKERMID},
++ {0xa0, 0x04, ZC3XX_R197_ANTIFLICKERLOW},
++ {0xa0, 0x0c, ZC3XX_R18C_AEFREEZE},
++ {0xa0, 0x18, ZC3XX_R18F_AEUNFREEZE},
+ {0xa0, 0x00, ZC3XX_R1A9_DIGITALLIMITDIFF}, /* 01,a9,00,cc */
+ {0xa0, 0x00, ZC3XX_R1AA_DIGITALGAINSTEP}, /* 01,aa,00,cc */
+ {0xa0, 0xbc, ZC3XX_R01D_HSYNC_0}, /* 00,1d,bc,cc */
+@@ -5971,10 +5569,12 @@ static const struct usb_action tas5130cxx_NoFlikerScale[] = {
+ {0xa0, 0xe0, ZC3XX_R01F_HSYNC_2}, /* 00,1f,e0,cc */
+ {0xa0, 0xff, ZC3XX_R020_HSYNC_3}, /* 00,20,ff,cc */
+ {0xa0, 0x02, ZC3XX_R09F_MAXXHIGH}, /* 00,9f,02,cc */
++ {0xa0, 0xf0, ZC3XX_R0A0_MAXXLOW},
++ {0xa0, 0x50, ZC3XX_R11D_GLOBALGAIN},
+ {}
+ };
+
+-static const struct usb_action tas5130c_vf0250_Initial[] = {
++static const struct usb_action tas5130c_vf0250_InitialScale[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc, */
+ {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, /* 00,08,02,cc, */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc, */
+@@ -6041,7 +5641,7 @@ static const struct usb_action tas5130c_vf0250_Initial[] = {
+ {}
+ };
+
+-static const struct usb_action tas5130c_vf0250_InitialScale[] = {
++static const struct usb_action tas5130c_vf0250_Initial[] = {
+ {0xa0, 0x01, ZC3XX_R000_SYSTEMCONTROL}, /* 00,00,01,cc, */
+ {0xa0, 0x02, ZC3XX_R008_CLOCKSETTING}, /* 00,08,02,cc, */
+ {0xa0, 0x01, ZC3XX_R010_CMOSSENSORSELECT}, /* 00,10,01,cc, */
+@@ -6106,8 +5706,7 @@ static const struct usb_action tas5130c_vf0250_InitialScale[] = {
+ {0xa0, 0x65, ZC3XX_R118_BGAIN}, /* 01,18,65,cc */
+ {}
+ };
+-/* "50HZ" light frequency banding filter */
+-static const struct usb_action tas5130c_vf0250_50HZ[] = {
++static const struct usb_action tas5130c_vf0250_50HZScale[] = {
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0001}, /* 00,83,01,aa */
+ {0xaa, 0x84, 0x00aa}, /* 00,84,aa,aa */
+@@ -6131,8 +5730,7 @@ static const struct usb_action tas5130c_vf0250_50HZ[] = {
+ {}
+ };
+
+-/* "50HZScale" light frequency banding filter */
+-static const struct usb_action tas5130c_vf0250_50HZScale[] = {
++static const struct usb_action tas5130c_vf0250_50HZ[] = {
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0003}, /* 00,83,03,aa */
+ {0xaa, 0x84, 0x0054}, /* 00,84,54,aa */
+@@ -6156,8 +5754,7 @@ static const struct usb_action tas5130c_vf0250_50HZScale[] = {
+ {}
+ };
+
+-/* "60HZ" light frequency banding filter */
+-static const struct usb_action tas5130c_vf0250_60HZ[] = {
++static const struct usb_action tas5130c_vf0250_60HZScale[] = {
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0001}, /* 00,83,01,aa */
+ {0xaa, 0x84, 0x0062}, /* 00,84,62,aa */
+@@ -6181,8 +5778,7 @@ static const struct usb_action tas5130c_vf0250_60HZ[] = {
+ {}
+ };
+
+-/* "60HZScale" light frequency banding ilter */
+-static const struct usb_action tas5130c_vf0250_60HZScale[] = {
++static const struct usb_action tas5130c_vf0250_60HZ[] = {
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0002}, /* 00,83,02,aa */
+ {0xaa, 0x84, 0x00c4}, /* 00,84,c4,aa */
+@@ -6206,8 +5802,7 @@ static const struct usb_action tas5130c_vf0250_60HZScale[] = {
+ {}
+ };
+
+-/* "NoFliker" light frequency banding flter */
+-static const struct usb_action tas5130c_vf0250_NoFliker[] = {
++static const struct usb_action tas5130c_vf0250_NoFlikerScale[] = {
+ {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
+@@ -6229,8 +5824,7 @@ static const struct usb_action tas5130c_vf0250_NoFliker[] = {
+ {}
+ };
+
+-/* "NoFlikerScale" light frequency banding filter */
+-static const struct usb_action tas5130c_vf0250_NoFlikerScale[] = {
++static const struct usb_action tas5130c_vf0250_NoFliker[] = {
+ {0xa0, 0x0c, ZC3XX_R100_OPERATIONMODE}, /* 01,00,0c,cc, */
+ {0xaa, 0x82, 0x0000}, /* 00,82,00,aa */
+ {0xaa, 0x83, 0x0000}, /* 00,83,00,aa */
+@@ -6253,7 +5847,7 @@ static const struct usb_action tas5130c_vf0250_NoFlikerScale[] = {
+ };
+
+ static u8 reg_r_i(struct gspca_dev *gspca_dev,
+- __u16 index)
++ u16 index)
+ {
+ usb_control_msg(gspca_dev->dev,
+ usb_rcvctrlpipe(gspca_dev->dev, 0),
+@@ -6266,7 +5860,7 @@ static u8 reg_r_i(struct gspca_dev *gspca_dev,
+ }
+
+ static u8 reg_r(struct gspca_dev *gspca_dev,
+- __u16 index)
++ u16 index)
+ {
+ u8 ret;
+
+@@ -6276,8 +5870,8 @@ static u8 reg_r(struct gspca_dev *gspca_dev,
+ }
+
+ static void reg_w_i(struct usb_device *dev,
+- __u8 value,
+- __u16 index)
++ u8 value,
++ u16 index)
+ {
+ usb_control_msg(dev,
+ usb_sndctrlpipe(dev, 0),
+@@ -6288,23 +5882,25 @@ static void reg_w_i(struct usb_device *dev,
+ }
+
+ static void reg_w(struct usb_device *dev,
+- __u8 value,
+- __u16 index)
++ u8 value,
++ u16 index)
+ {
+ PDEBUG(D_USBO, "reg w [%04x] = %02x", index, value);
+ reg_w_i(dev, value, index);
+ }
+
+-static __u16 i2c_read(struct gspca_dev *gspca_dev,
+- __u8 reg)
++static u16 i2c_read(struct gspca_dev *gspca_dev,
++ u8 reg)
+ {
+- __u8 retbyte;
+- __u16 retval;
++ u8 retbyte;
++ u16 retval;
+
+ reg_w_i(gspca_dev->dev, reg, 0x0092);
+ reg_w_i(gspca_dev->dev, 0x02, 0x0090); /* <- read command */
+- msleep(25);
++ msleep(20);
+ retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
++ if (retbyte != 0x00)
++ err("i2c_r status error %02x", retbyte);
+ retval = reg_r_i(gspca_dev, 0x0095); /* read Lowbyte */
+ retval |= reg_r_i(gspca_dev, 0x0096) << 8; /* read Hightbyte */
+ PDEBUG(D_USBI, "i2c r [%02x] -> %04x (%02x)",
+@@ -6312,19 +5908,21 @@ static __u16 i2c_read(struct gspca_dev *gspca_dev,
+ return retval;
+ }
+
+-static __u8 i2c_write(struct gspca_dev *gspca_dev,
+- __u8 reg,
+- __u8 valL,
+- __u8 valH)
++static u8 i2c_write(struct gspca_dev *gspca_dev,
++ u8 reg,
++ u8 valL,
++ u8 valH)
+ {
+- __u8 retbyte;
++ u8 retbyte;
+
+ reg_w_i(gspca_dev->dev, reg, 0x92);
+ reg_w_i(gspca_dev->dev, valL, 0x93);
+ reg_w_i(gspca_dev->dev, valH, 0x94);
+ reg_w_i(gspca_dev->dev, 0x01, 0x90); /* <- write command */
+- msleep(15);
++ msleep(1);
+ retbyte = reg_r_i(gspca_dev, 0x0091); /* read status */
++ if (retbyte != 0x00)
++ err("i2c_w status error %02x", retbyte);
+ PDEBUG(D_USBO, "i2c w [%02x] = %02x%02x (%02x)",
+ reg, valH, valL, retbyte);
+ return retbyte;
+@@ -6359,7 +5957,7 @@ static void usb_exchange(struct gspca_dev *gspca_dev,
+ break;
+ }
+ action++;
+-/* msleep(1); */
++ msleep(1);
+ }
+ }
+
+@@ -6367,24 +5965,26 @@ static void setmatrix(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ int i;
+- const __u8 *matrix;
++ const u8 *matrix;
+ static const u8 adcm2700_matrix[9] =
+ /* {0x66, 0xed, 0xed, 0xed, 0x66, 0xed, 0xed, 0xed, 0x66}; */
+ /*ms-win*/
+ {0x74, 0xed, 0xed, 0xed, 0x74, 0xed, 0xed, 0xed, 0x74};
+- static const __u8 gc0305_matrix[9] =
++ static const u8 gc0305_matrix[9] =
+ {0x50, 0xf8, 0xf8, 0xf8, 0x50, 0xf8, 0xf8, 0xf8, 0x50};
+- static const __u8 ov7620_matrix[9] =
++ static const u8 ov7620_matrix[9] =
+ {0x58, 0xf4, 0xf4, 0xf4, 0x58, 0xf4, 0xf4, 0xf4, 0x58};
+- static const __u8 pas202b_matrix[9] =
++ static const u8 pas202b_matrix[9] =
+ {0x4c, 0xf5, 0xff, 0xf9, 0x51, 0xf5, 0xfb, 0xed, 0x5f};
+- static const __u8 po2030_matrix[9] =
++ static const u8 po2030_matrix[9] =
+ {0x60, 0xf0, 0xf0, 0xf0, 0x60, 0xf0, 0xf0, 0xf0, 0x60};
+- static const __u8 vf0250_matrix[9] =
++ static const u8 tas5130c_matrix[9] =
++ {0x68, 0xec, 0xec, 0xec, 0x68, 0xec, 0xec, 0xec, 0x68};
++ static const u8 vf0250_matrix[9] =
+ {0x7b, 0xea, 0xea, 0xea, 0x7b, 0xea, 0xea, 0xea, 0x7b};
+- static const __u8 *matrix_tb[SENSOR_MAX] = {
++ static const u8 *matrix_tb[SENSOR_MAX] = {
+ adcm2700_matrix, /* SENSOR_ADCM2700 0 */
+- NULL, /* SENSOR_CS2102 1 */
++ ov7620_matrix, /* SENSOR_CS2102 1 */
+ NULL, /* SENSOR_CS2102K 2 */
+ gc0305_matrix, /* SENSOR_GC0305 3 */
+ NULL, /* SENSOR_HDCS2020b 4 */
+@@ -6392,15 +5992,16 @@ static void setmatrix(struct gspca_dev *gspca_dev)
+ NULL, /* SENSOR_HV7131C 6 */
+ NULL, /* SENSOR_ICM105A 7 */
+ NULL, /* SENSOR_MC501CB 8 */
+- ov7620_matrix, /* SENSOR_OV7620 9 */
+- NULL, /* SENSOR_OV7630C 10 */
+- NULL, /* SENSOR_PAS106 11 */
+- pas202b_matrix, /* SENSOR_PAS202B 12 */
+- NULL, /* SENSOR_PB0330 13 */
+- po2030_matrix, /* SENSOR_PO2030 14 */
+- NULL, /* SENSOR_TAS5130CK 15 */
+- NULL, /* SENSOR_TAS5130CXX 16 */
+- vf0250_matrix, /* SENSOR_TAS5130C_VF0250 17 */
++ gc0305_matrix, /* SENSOR_MI0360SOC 9 */
++ ov7620_matrix, /* SENSOR_OV7620 10 */
++ NULL, /* SENSOR_OV7630C 11 */
++ NULL, /* SENSOR_PAS106 12 */
++ pas202b_matrix, /* SENSOR_PAS202B 13 */
++ gc0305_matrix, /* SENSOR_PB0330 14 */
++ po2030_matrix, /* SENSOR_PO2030 15 */
++ NULL, /* SENSOR_TAS5130CK 16 */
++ tas5130c_matrix, /* SENSOR_TAS5130CXX 17 */
++ vf0250_matrix, /* SENSOR_TAS5130C_VF0250 18 */
+ };
+
+ matrix = matrix_tb[sd->sensor];
+@@ -6413,11 +6014,12 @@ static void setmatrix(struct gspca_dev *gspca_dev)
+ static void setbrightness(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- __u8 brightness;
++ u8 brightness;
+
+ switch (sd->sensor) {
+ case SENSOR_GC0305:
+ case SENSOR_OV7620:
++ case SENSOR_PAS202B:
+ case SENSOR_PO2030:
+ return;
+ }
+@@ -6441,7 +6043,7 @@ static void setsharpness(struct gspca_dev *gspca_dev)
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct usb_device *dev = gspca_dev->dev;
+ int sharpness;
+- static const __u8 sharpness_tb[][2] = {
++ static const u8 sharpness_tb[][2] = {
+ {0x02, 0x03},
+ {0x04, 0x07},
+ {0x08, 0x0f},
+@@ -6460,118 +6062,69 @@ static void setcontrast(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct usb_device *dev = gspca_dev->dev;
+- const __u8 *Tgamma, *Tgradient;
+- int g, i, k;
+- static const __u8 kgamma_tb[16] = /* delta for contrast */
++ const u8 *Tgamma;
++ int g, i, k, adj, gp;
++ u8 gr[16];
++ static const u8 delta_tb[16] = /* delta for contrast */
+ {0x15, 0x0d, 0x0a, 0x09, 0x08, 0x08, 0x08, 0x08,
+ 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08, 0x08};
+- static const __u8 kgrad_tb[16] =
+- {0x1b, 0x06, 0x03, 0x02, 0x00, 0x00, 0x00, 0x00,
+- 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x06, 0x04};
+- static const __u8 Tgamma_1[16] =
++ static const u8 gamma_tb[6][16] = {
+ {0x00, 0x00, 0x03, 0x0d, 0x1b, 0x2e, 0x45, 0x5f,
+- 0x79, 0x93, 0xab, 0xc1, 0xd4, 0xe5, 0xf3, 0xff};
+- static const __u8 Tgradient_1[16] =
+- {0x00, 0x01, 0x05, 0x0b, 0x10, 0x15, 0x18, 0x1a,
+- 0x1a, 0x18, 0x16, 0x14, 0x12, 0x0f, 0x0d, 0x06};
+- static const __u8 Tgamma_2[16] =
++ 0x79, 0x93, 0xab, 0xc1, 0xd4, 0xe5, 0xf3, 0xff},
+ {0x01, 0x0c, 0x1f, 0x3a, 0x53, 0x6d, 0x85, 0x9c,
+- 0xb0, 0xc2, 0xd1, 0xde, 0xe9, 0xf2, 0xf9, 0xff};
+- static const __u8 Tgradient_2[16] =
+- {0x05, 0x0f, 0x16, 0x1a, 0x19, 0x19, 0x17, 0x15,
+- 0x12, 0x10, 0x0e, 0x0b, 0x09, 0x08, 0x06, 0x03};
+- static const __u8 Tgamma_3[16] =
++ 0xb0, 0xc2, 0xd1, 0xde, 0xe9, 0xf2, 0xf9, 0xff},
+ {0x04, 0x16, 0x30, 0x4e, 0x68, 0x81, 0x98, 0xac,
+- 0xbe, 0xcd, 0xda, 0xe4, 0xed, 0xf5, 0xfb, 0xff};
+- static const __u8 Tgradient_3[16] =
+- {0x0c, 0x16, 0x1b, 0x1c, 0x19, 0x18, 0x15, 0x12,
+- 0x10, 0x0d, 0x0b, 0x09, 0x08, 0x06, 0x05, 0x03};
+- static const __u8 Tgamma_4[16] =
++ 0xbe, 0xcd, 0xda, 0xe4, 0xed, 0xf5, 0xfb, 0xff},
+ {0x13, 0x38, 0x59, 0x79, 0x92, 0xa7, 0xb9, 0xc8,
+- 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff};
+- static const __u8 Tgradient_4[16] =
+- {0x26, 0x22, 0x20, 0x1c, 0x16, 0x13, 0x10, 0x0d,
+- 0x0b, 0x09, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02};
+- static const __u8 Tgamma_5[16] =
++ 0xd4, 0xdf, 0xe7, 0xee, 0xf4, 0xf9, 0xfc, 0xff},
+ {0x20, 0x4b, 0x6e, 0x8d, 0xa3, 0xb5, 0xc5, 0xd2,
+- 0xdc, 0xe5, 0xec, 0xf2, 0xf6, 0xfa, 0xfd, 0xff};
+- static const __u8 Tgradient_5[16] =
+- {0x37, 0x26, 0x20, 0x1a, 0x14, 0x10, 0x0e, 0x0b,
+- 0x09, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x02};
+- static const __u8 Tgamma_6[16] = /* ?? was gamma 5 */
++ 0xdc, 0xe5, 0xec, 0xf2, 0xf6, 0xfa, 0xfd, 0xff},
+ {0x24, 0x44, 0x64, 0x84, 0x9d, 0xb2, 0xc4, 0xd3,
+- 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff};
+- static const __u8 Tgradient_6[16] =
+- {0x18, 0x20, 0x20, 0x1c, 0x16, 0x13, 0x10, 0x0e,
+- 0x0b, 0x09, 0x07, 0x00, 0x00, 0x00, 0x00, 0x01};
+- static const __u8 *gamma_tb[] = {
+- NULL, Tgamma_1, Tgamma_2,
+- Tgamma_3, Tgamma_4, Tgamma_5, Tgamma_6
++ 0xe0, 0xeb, 0xf4, 0xff, 0xff, 0xff, 0xff, 0xff},
+ };
+- static const __u8 *gradient_tb[] = {
+- NULL, Tgradient_1, Tgradient_2,
+- Tgradient_3, Tgradient_4, Tgradient_5, Tgradient_6
+- };
+-#ifdef GSPCA_DEBUG
+- __u8 v[16];
+-#endif
+
+- Tgamma = gamma_tb[sd->gamma];
+- Tgradient = gradient_tb[sd->gamma];
++ Tgamma = gamma_tb[sd->gamma - 1];
+
+- k = (sd->contrast - 128) /* -128 / 128 */
+- * Tgamma[0];
+- PDEBUG(D_CONF, "gamma:%d contrast:%d gamma coeff: %d/128",
+- sd->gamma, sd->contrast, k);
++ k = ((int) sd->contrast - 128); /* -128 / 128 */
++ adj = 0;
++ gp = 0;
+ for (i = 0; i < 16; i++) {
+- g = Tgamma[i] + kgamma_tb[i] * k / 128;
++ g = Tgamma[i] - delta_tb[i] * k / 128 - adj / 2;
+ if (g > 0xff)
+ g = 0xff;
+ else if (g <= 0)
+ g = 1;
+ reg_w(dev, g, 0x0120 + i); /* gamma */
+-#ifdef GSPCA_DEBUG
+- if (gspca_debug & D_CONF)
+- v[i] = g;
+-#endif
+- }
+- PDEBUG(D_CONF, "tb: %02x %02x %02x %02x %02x %02x %02x %02x",
+- v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
+- PDEBUG(D_CONF, " %02x %02x %02x %02x %02x %02x %02x %02x",
+- v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]);
+- for (i = 0; i < 16; i++) {
+- g = Tgradient[i] - kgrad_tb[i] * k / 128;
+- if (g > 0xff)
+- g = 0xff;
+- else if (g <= 0) {
+- if (i != 15)
+- g = 0;
++ if (k > 0)
++ adj--;
++ else
++ adj++;
++
++ if (i != 0) {
++ if (gp == 0)
++ gr[i - 1] = 0;
+ else
+- g = 1;
++ gr[i - 1] = g - gp;
+ }
+- reg_w(dev, g, 0x0130 + i); /* gradient */
+-#ifdef GSPCA_DEBUG
+- if (gspca_debug & D_CONF)
+- v[i] = g;
+-#endif
++ gp = g;
+ }
+- PDEBUG(D_CONF, " %02x %02x %02x %02x %02x %02x %02x %02x",
+- v[0], v[1], v[2], v[3], v[4], v[5], v[6], v[7]);
+- PDEBUG(D_CONF, " %02x %02x %02x %02x %02x %02x %02x %02x",
+- v[8], v[9], v[10], v[11], v[12], v[13], v[14], v[15]);
++ gr[15] = gr[14] / 2;
++ for (i = 0; i < 16; i++)
++ reg_w(dev, gr[i], 0x0130 + i); /* gradient */
+ }
+
+ static void setquality(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct usb_device *dev = gspca_dev->dev;
+- __u8 frxt;
++ u8 frxt;
+
+ switch (sd->sensor) {
+ case SENSOR_ADCM2700:
+ case SENSOR_GC0305:
+ case SENSOR_HV7131B:
+ case SENSOR_OV7620:
++ case SENSOR_PAS202B:
+ case SENSOR_PO2030:
+ return;
+ }
+@@ -6625,9 +6178,9 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
+ hdcs2020b_50HZ, hdcs2020b_50HZ,
+ hdcs2020b_60HZ, hdcs2020b_60HZ},
+ /* SENSOR_HV7131B 5 */
+- {hv7131b_NoFlikerScale, hv7131b_NoFliker,
+- hv7131b_50HZScale, hv7131b_50HZ,
+- hv7131b_60HZScale, hv7131b_60HZ},
++ {hv7131b_NoFliker, hv7131b_NoFlikerScale,
++ hv7131b_50HZ, hv7131b_50HZScale,
++ hv7131b_60HZ, hv7131b_60HZScale},
+ /* SENSOR_HV7131C 6 */
+ {NULL, NULL,
+ NULL, NULL,
+@@ -6637,42 +6190,46 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
+ icm105a_50HZ, icm105a_50HZScale,
+ icm105a_60HZ, icm105a_60HZScale},
+ /* SENSOR_MC501CB 8 */
+- {MC501CB_NoFliker, MC501CB_NoFlikerScale,
+- MC501CB_50HZ, MC501CB_50HZScale,
+- MC501CB_60HZ, MC501CB_60HZScale},
+-/* SENSOR_OV7620 9 */
+- {OV7620_NoFliker, OV7620_NoFliker,
+- OV7620_50HZ, OV7620_50HZ,
+- OV7620_60HZ, OV7620_60HZ},
+-/* SENSOR_OV7630C 10 */
++ {mc501cb_NoFliker, mc501cb_NoFlikerScale,
++ mc501cb_50HZ, mc501cb_50HZScale,
++ mc501cb_60HZ, mc501cb_60HZScale},
++/* SENSOR_MI0360SOC 9 */
++ {mi360soc_AENoFliker, mi360soc_AENoFlikerScale,
++ mi360soc_AE50HZ, mi360soc_AE50HZScale,
++ mi360soc_AE60HZ, mi360soc_AE60HZScale},
++/* SENSOR_OV7620 10 */
++ {ov7620_NoFliker, ov7620_NoFliker,
++ ov7620_50HZ, ov7620_50HZ,
++ ov7620_60HZ, ov7620_60HZ},
++/* SENSOR_OV7630C 11 */
+ {NULL, NULL,
+ NULL, NULL,
+ NULL, NULL},
+-/* SENSOR_PAS106 11 */
++/* SENSOR_PAS106 12 */
+ {pas106b_NoFliker, pas106b_NoFliker,
+ pas106b_50HZ, pas106b_50HZ,
+ pas106b_60HZ, pas106b_60HZ},
+-/* SENSOR_PAS202B 12 */
++/* SENSOR_PAS202B 13 */
+ {pas202b_NoFlikerScale, pas202b_NoFliker,
+ pas202b_50HZScale, pas202b_50HZ,
+ pas202b_60HZScale, pas202b_60HZ},
+-/* SENSOR_PB0330 13 */
+- {pb0330_NoFliker, pb0330_NoFlikerScale,
+- pb0330_50HZ, pb0330_50HZScale,
+- pb0330_60HZ, pb0330_60HZScale},
+-/* SENSOR_PO2030 14 */
+- {PO2030_NoFliker, PO2030_NoFliker,
+- PO2030_50HZ, PO2030_50HZ,
+- PO2030_60HZ, PO2030_60HZ},
+-/* SENSOR_TAS5130CK 15 */
++/* SENSOR_PB0330 14 */
++ {pb0330_NoFlikerScale, pb0330_NoFliker,
++ pb0330_50HZScale, pb0330_50HZ,
++ pb0330_60HZScale, pb0330_60HZ},
++/* SENSOR_PO2030 15 */
++ {po2030_NoFliker, po2030_NoFliker,
++ po2030_50HZ, po2030_50HZ,
++ po2030_60HZ, po2030_60HZ},
++/* SENSOR_TAS5130CK 16 */
+ {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale,
+ tas5130cxx_50HZ, tas5130cxx_50HZScale,
+ tas5130cxx_60HZ, tas5130cxx_60HZScale},
+-/* SENSOR_TAS5130CXX 16 */
++/* SENSOR_TAS5130CXX 17 */
+ {tas5130cxx_NoFliker, tas5130cxx_NoFlikerScale,
+ tas5130cxx_50HZ, tas5130cxx_50HZScale,
+ tas5130cxx_60HZ, tas5130cxx_60HZScale},
+-/* SENSOR_TAS5130C_VF0250 17 */
++/* SENSOR_TAS5130C_VF0250 18 */
+ {tas5130c_vf0250_NoFliker, tas5130c_vf0250_NoFlikerScale,
+ tas5130c_vf0250_50HZ, tas5130c_vf0250_50HZScale,
+ tas5130c_vf0250_60HZ, tas5130c_vf0250_60HZScale},
+@@ -6680,9 +6237,9 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
+
+ i = sd->lightfreq * 2;
+ mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+- if (!mode)
+- i++; /* 640x480 */
+- zc3_freq = freq_tb[(int) sd->sensor][i];
++ if (mode)
++ i++; /* 320x240 */
++ zc3_freq = freq_tb[sd->sensor][i];
+ if (zc3_freq != NULL) {
+ usb_exchange(gspca_dev, zc3_freq);
+ switch (sd->sensor) {
+@@ -6700,6 +6257,9 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
+ reg_w(gspca_dev->dev, 0x44, 0x0002);
+ }
+ break;
++ case SENSOR_PAS202B:
++ reg_w(gspca_dev->dev, 0x00, 0x01a7);
++ break;
+ }
+ }
+ return 0;
+@@ -6708,7 +6268,7 @@ static int setlightfreq(struct gspca_dev *gspca_dev)
+ static void setautogain(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+- __u8 autoval;
++ u8 autoval;
+
+ if (sd->autogain)
+ autoval = 0x42;
+@@ -6729,12 +6289,19 @@ static void send_unknown(struct usb_device *dev, int sensor)
+ case SENSOR_ADCM2700:
+ case SENSOR_GC0305:
+ case SENSOR_OV7620:
++ case SENSOR_MI0360SOC:
+ case SENSOR_PB0330:
+ case SENSOR_PO2030:
+ reg_w(dev, 0x0d, 0x003a);
+ reg_w(dev, 0x02, 0x003b);
+ reg_w(dev, 0x00, 0x0038);
+ break;
++ case SENSOR_PAS202B:
++ reg_w(dev, 0x03, 0x003b);
++ reg_w(dev, 0x0c, 0x003a);
++ reg_w(dev, 0x0b, 0x0039);
++ reg_w(dev, 0x0b, 0x0038);
++ break;
+ }
+ }
+
+@@ -6751,7 +6318,7 @@ static void start_2wr_probe(struct usb_device *dev, int sensor)
+
+ static int sif_probe(struct gspca_dev *gspca_dev)
+ {
+- __u16 checkword;
++ u16 checkword;
+
+ start_2wr_probe(gspca_dev->dev, 0x0f); /* PAS106 */
+ reg_w(gspca_dev->dev, 0x08, 0x008d);
+@@ -6794,6 +6361,7 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev)
+ }
+
+ start_2wr_probe(dev, 0x08); /* HDCS2020 */
++ i2c_write(gspca_dev, 0x1c, 0x00, 0x00);
+ i2c_write(gspca_dev, 0x15, 0xaa, 0x00);
+ retword = i2c_read(gspca_dev, 0x15);
+ if (retword != 0)
+@@ -6820,16 +6388,18 @@ static int vga_2wr_probe(struct gspca_dev *gspca_dev)
+ start_2wr_probe(dev, 0x0e); /* PAS202BCB */
+ reg_w(dev, 0x08, 0x008d);
+ i2c_write(gspca_dev, 0x03, 0xaa, 0x00);
+- msleep(500);
++ msleep(50);
+ retword = i2c_read(gspca_dev, 0x03);
+- if (retword != 0)
++ if (retword != 0) {
++ send_unknown(dev, SENSOR_PAS202B);
+ return 0x0e; /* PAS202BCB */
++ }
+
+- start_2wr_probe(dev, 0x02); /* ?? */
++ start_2wr_probe(dev, 0x02); /* TAS5130C */
+ i2c_write(gspca_dev, 0x01, 0xaa, 0x00);
+ retword = i2c_read(gspca_dev, 0x01);
+ if (retword != 0)
+- return 0x02; /* ?? */
++ return 0x02; /* TAS5130C */
+ ov_check:
+ reg_r(gspca_dev, 0x0010); /* ?? */
+ reg_r(gspca_dev, 0x0010);
+@@ -6859,15 +6429,17 @@ ov_check:
+ }
+
+ struct sensor_by_chipset_revision {
+- __u16 revision;
+- __u8 internal_sensor_id;
++ u16 revision;
++ u8 internal_sensor_id;
+ };
+ static const struct sensor_by_chipset_revision chipset_revision_sensor[] = {
+- {0xc001, 0x13}, /* MI0360 */
++ {0xc000, 0x12}, /* TAS5130C */
++ {0xc001, 0x13}, /* MI0360SOC */
+ {0xe001, 0x13},
+ {0x8001, 0x13},
+ {0x8000, 0x14}, /* CS2102K */
+ {0x8400, 0x15}, /* TAS5130K */
++ {0xe400, 0x15},
+ };
+
+ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+@@ -6875,7 +6447,7 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct usb_device *dev = gspca_dev->dev;
+ int i;
+- __u8 retbyte;
++ u8 retbyte;
+ u16 retword;
+
+ /*fixme: lack of 8b=b3 (11,12)-> 10, 8b=e0 (14,15,16)-> 12 found in gspcav1*/
+@@ -6905,6 +6477,8 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+ reg_r(gspca_dev, 0x0010);
+ /* value 0x4001 is meaningless */
+ if (retword != 0x4001) {
++ if ((retword & 0xff00) == 0x6400)
++ return 0x02; /* TAS5130C */
+ for (i = 0; i < ARRAY_SIZE(chipset_revision_sensor); i++) {
+ if (chipset_revision_sensor[i].revision == retword) {
+ sd->chip_revision = retword;
+@@ -6915,7 +6489,7 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+ }
+ }
+
+- reg_w(dev, 0x01, 0x0000); /* check ?? */
++ reg_w(dev, 0x01, 0x0000); /* check PB0330 */
+ reg_w(dev, 0x01, 0x0001);
+ reg_w(dev, 0xdd, 0x008b);
+ reg_w(dev, 0x0a, 0x0010);
+@@ -6924,7 +6498,7 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+ retword = i2c_read(gspca_dev, 0x00);
+ if (retword != 0) {
+ PDEBUG(D_PROBE, "probe 3wr vga type 0a ?");
+- return 0x0a; /* ?? */
++ return 0x0a; /* PB0330 */
+ }
+
+ reg_w(dev, 0x01, 0x0000);
+@@ -6963,7 +6537,6 @@ static int vga_3wr_probe(struct gspca_dev *gspca_dev)
+ reg_w(dev, 0x01, 0x0001);
+ reg_w(dev, 0xee, 0x008b);
+ reg_w(dev, 0x03, 0x0012);
+-/* msleep(150); */
+ reg_w(dev, 0x01, 0x0012);
+ reg_w(dev, 0x05, 0x0012);
+ retword = i2c_read(gspca_dev, 0x00) << 8; /* ID 0 */
+@@ -7022,10 +6595,9 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct cam *cam;
+ int sensor;
+- int vga = 1; /* 1: vga, 0: sif */
+- static const __u8 gamma[SENSOR_MAX] = {
++ static const u8 gamma[SENSOR_MAX] = {
+ 4, /* SENSOR_ADCM2700 0 */
+- 5, /* SENSOR_CS2102 1 */
++ 4, /* SENSOR_CS2102 1 */
+ 5, /* SENSOR_CS2102K 2 */
+ 4, /* SENSOR_GC0305 3 */
+ 4, /* SENSOR_HDCS2020b 4 */
+@@ -7033,19 +6605,41 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ 4, /* SENSOR_HV7131C 6 */
+ 4, /* SENSOR_ICM105A 7 */
+ 4, /* SENSOR_MC501CB 8 */
+- 3, /* SENSOR_OV7620 9 */
+- 4, /* SENSOR_OV7630C 10 */
+- 4, /* SENSOR_PAS106 11 */
+- 4, /* SENSOR_PAS202B 12 */
+- 4, /* SENSOR_PB0330 13 */
+- 4, /* SENSOR_PO2030 14 */
+- 4, /* SENSOR_TAS5130CK 15 */
+- 4, /* SENSOR_TAS5130CXX 16 */
+- 3, /* SENSOR_TAS5130C_VF0250 17 */
++ 4, /* SENSOR_MI0360SOC 9 */
++ 3, /* SENSOR_OV7620 10 */
++ 4, /* SENSOR_OV7630C 11 */
++ 4, /* SENSOR_PAS106 12 */
++ 4, /* SENSOR_PAS202B 13 */
++ 4, /* SENSOR_PB0330 14 */
++ 4, /* SENSOR_PO2030 15 */
++ 4, /* SENSOR_TAS5130CK 16 */
++ 3, /* SENSOR_TAS5130CXX 17 */
++ 3, /* SENSOR_TAS5130C_VF0250 18 */
++ };
++ static const u8 mode_tb[SENSOR_MAX] = {
++ 2, /* SENSOR_ADCM2700 0 */
++ 1, /* SENSOR_CS2102 1 */
++ 1, /* SENSOR_CS2102K 2 */
++ 1, /* SENSOR_GC0305 3 */
++ 1, /* SENSOR_HDCS2020b 4 */
++ 1, /* SENSOR_HV7131B 5 */
++ 1, /* SENSOR_HV7131C 6 */
++ 1, /* SENSOR_ICM105A 7 */
++ 2, /* SENSOR_MC501CB 8 */
++ 1, /* SENSOR_MI0360SOC 9 */
++ 2, /* SENSOR_OV7620 10 */
++ 1, /* SENSOR_OV7630C 11 */
++ 0, /* SENSOR_PAS106 12 */
++ 1, /* SENSOR_PAS202B 13 */
++ 1, /* SENSOR_PB0330 14 */
++ 1, /* SENSOR_PO2030 15 */
++ 1, /* SENSOR_TAS5130CK 16 */
++ 1, /* SENSOR_TAS5130CXX 17 */
++ 1, /* SENSOR_TAS5130C_VF0250 18 */
+ };
+
+ /* define some sensors from the vendor/product */
+- sd->sharpness = 2;
++ sd->sharpness = SHARPNESS_DEF;
+ sd->sensor = id->driver_info;
+ sensor = zcxx_probeSensor(gspca_dev);
+ if (sensor >= 0)
+@@ -7065,13 +6659,30 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ break;
+ default:
+ PDEBUG(D_PROBE,
+- "Sensor UNKNOW_0 force Tas5130");
++ "Sensor UNKNOWN_0 force Tas5130");
+ sd->sensor = SENSOR_TAS5130CXX;
+ }
+ break;
+ case 0:
+- PDEBUG(D_PROBE, "Find Sensor HV7131B");
+- sd->sensor = SENSOR_HV7131B;
++ /* check the sensor type */
++ sensor = i2c_read(gspca_dev, 0x00);
++ PDEBUG(D_PROBE, "Sensor hv7131 type %d", sensor);
++ switch (sensor) {
++ case 0: /* hv7131b */
++ case 1: /* hv7131e */
++ PDEBUG(D_PROBE, "Find Sensor HV7131B");
++ sd->sensor = SENSOR_HV7131B;
++ break;
++ default:
++/* case 2: * hv7131r */
++ PDEBUG(D_PROBE, "Find Sensor HV7131R(c)");
++ sd->sensor = SENSOR_HV7131C;
++ break;
++ }
++ break;
++ case 0x02:
++ PDEBUG(D_PROBE, "Sensor TAS5130C");
++ sd->sensor = SENSOR_TAS5130CXX;
+ break;
+ case 0x04:
+ PDEBUG(D_PROBE, "Find Sensor CS2102");
+@@ -7094,16 +6705,15 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ case 0x0e:
+ PDEBUG(D_PROBE, "Find Sensor PAS202B");
+ sd->sensor = SENSOR_PAS202B;
+- sd->sharpness = 1;
++/* sd->sharpness = 1; */
+ break;
+ case 0x0f:
+ PDEBUG(D_PROBE, "Find Sensor PAS106");
+ sd->sensor = SENSOR_PAS106;
+- vga = 0; /* SIF */
+ break;
+ case 0x10:
+ case 0x12:
+- PDEBUG(D_PROBE, "Find Sensor TAS5130");
++ PDEBUG(D_PROBE, "Find Sensor TAS5130C");
+ sd->sensor = SENSOR_TAS5130CXX;
+ break;
+ case 0x11:
+@@ -7112,9 +6722,9 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ break;
+ case 0x13:
+ PDEBUG(D_PROBE,
+- "Find Sensor MI0360. Chip revision %x",
++ "Find Sensor MI0360SOC. Chip revision %x",
+ sd->chip_revision);
+- sd->sensor = SENSOR_PB0330;
++ sd->sensor = SENSOR_MI0360SOC;
+ break;
+ case 0x14:
+ PDEBUG(D_PROBE,
+@@ -7165,31 +6775,42 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ if (sensor < 0x20) {
+ if (sensor == -1 || sensor == 0x10 || sensor == 0x12)
+ reg_w(gspca_dev->dev, 0x02, 0x0010);
++#if 0
+ else
+ reg_w(gspca_dev->dev, sensor & 0x0f, 0x0010);
++#endif
+ reg_r(gspca_dev, 0x0010);
+ }
+
+ cam = &gspca_dev->cam;
+ /*fixme:test*/
+ gspca_dev->nbalt--;
+- if (vga) {
+- cam->cam_mode = vga_mode;
+- cam->nmodes = ARRAY_SIZE(vga_mode);
+- } else {
++ switch (mode_tb[sd->sensor]) {
++ case 0:
+ cam->cam_mode = sif_mode;
+ cam->nmodes = ARRAY_SIZE(sif_mode);
++ break;
++ case 1:
++ cam->cam_mode = vga_mode;
++ cam->nmodes = ARRAY_SIZE(vga_mode);
++ break;
++ default:
++/* case 2: */
++ cam->cam_mode = broken_vga_mode;
++ cam->nmodes = ARRAY_SIZE(broken_vga_mode);
++ break;
+ }
+- sd->brightness = sd_ctrls[SD_BRIGHTNESS].qctrl.default_value;
+- sd->contrast = sd_ctrls[SD_CONTRAST].qctrl.default_value;
+- sd->gamma = gamma[(int) sd->sensor];
+- sd->autogain = sd_ctrls[SD_AUTOGAIN].qctrl.default_value;
+- sd->lightfreq = sd_ctrls[SD_FREQ].qctrl.default_value;
++ sd->brightness = BRIGHTNESS_DEF;
++ sd->contrast = CONTRAST_DEF;
++ sd->gamma = gamma[sd->sensor];
++ sd->autogain = AUTOGAIN_DEF;
++ sd->lightfreq = FREQ_DEF;
+ sd->quality = QUALITY_DEF;
+
+ switch (sd->sensor) {
+ case SENSOR_GC0305:
+ case SENSOR_OV7620:
++ case SENSOR_PAS202B:
+ case SENSOR_PO2030:
+ gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX);
+ break;
+@@ -7200,14 +6821,13 @@ static int sd_config(struct gspca_dev *gspca_dev,
+ break;
+ }
+
+- /* switch the led off */
+- reg_w(gspca_dev->dev, 0x01, 0x0000);
+ return 0;
+ }
+
+ /* this function is called at probe and resume time */
+ static int sd_init(struct gspca_dev *gspca_dev)
+ {
++ /* switch off the led */
+ reg_w(gspca_dev->dev, 0x01, 0x0000);
+ return 0;
+ }
+@@ -7216,29 +6836,28 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+ struct usb_device *dev = gspca_dev->dev;
+- const struct usb_action *zc3_init;
+ int mode;
+ static const struct usb_action *init_tb[SENSOR_MAX][2] = {
+ {adcm2700_Initial, adcm2700_InitialScale}, /* 0 */
+- {cs2102_InitialScale, cs2102_Initial}, /* 1 */
+- {cs2102K_InitialScale, cs2102K_Initial}, /* 2 */
++ {cs2102_Initial, cs2102_InitialScale}, /* 1 */
++ {cs2102K_Initial, cs2102K_InitialScale}, /* 2 */
+ {gc0305_Initial, gc0305_InitialScale}, /* 3 */
+- {hdcs2020xb_InitialScale, hdcs2020xb_Initial}, /* 4 */
+- {hv7131bxx_InitialScale, hv7131bxx_Initial}, /* 5 */
+- {hv7131cxx_InitialScale, hv7131cxx_Initial}, /* 6 */
+- {icm105axx_InitialScale, icm105axx_Initial}, /* 7 */
+- {MC501CB_InitialScale, MC501CB_Initial}, /* 8 */
+- {OV7620_mode0, OV7620_mode1}, /* 9 */
+- {ov7630c_InitialScale, ov7630c_Initial}, /* 10 */
+- {pas106b_InitialScale, pas106b_Initial}, /* 11 */
+- {pas202b_Initial, pas202b_InitialScale}, /* 12 */
+- {pb0330xx_InitialScale, pb0330xx_Initial}, /* 13 */
+-/* or {pb03303x_InitialScale, pb03303x_Initial}, */
+- {PO2030_mode0, PO2030_mode1}, /* 14 */
+- {tas5130CK_InitialScale, tas5130CK_Initial}, /* 15 */
+- {tas5130cxx_InitialScale, tas5130cxx_Initial}, /* 16 */
+- {tas5130c_vf0250_InitialScale, tas5130c_vf0250_Initial},
+- /* 17 */
++ {hdcs2020b_Initial, hdcs2020b_InitialScale}, /* 4 */
++ {hv7131b_Initial, hv7131b_InitialScale}, /* 5 */
++ {hv7131r_Initial, hv7131r_InitialScale}, /* 6 */
++ {icm105a_Initial, icm105a_InitialScale}, /* 7 */
++ {mc501cb_Initial, mc501cb_InitialScale}, /* 8 */
++ {mi0360soc_Initial, mi0360soc_InitialScale}, /* 9 */
++ {ov7620_Initial, ov7620_InitialScale}, /* 10 */
++ {ov7630c_Initial, ov7630c_InitialScale}, /* 11 */
++ {pas106b_Initial, pas106b_InitialScale}, /* 12 */
++ {pas202b_Initial, pas202b_InitialScale}, /* 13 */
++ {pb0330_Initial, pb0330_InitialScale}, /* 14 */
++ {po2030_Initial, po2030_InitialScale}, /* 15 */
++ {tas5130cK_Initial, tas5130cK_InitialScale}, /* 16 */
++ {tas5130cxx_Initial, tas5130cxx_InitialScale}, /* 17 */
++ {tas5130c_vf0250_Initial, tas5130c_vf0250_InitialScale},
++ /* 18 */
+ };
+
+ /* create the JPEG header */
+@@ -7249,8 +6868,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ 0x21); /* JPEG 422 */
+ jpeg_set_qual(sd->jpeg_hdr, sd->quality);
+
+- mode = gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv;
+- zc3_init = init_tb[(int) sd->sensor][mode];
++ mode = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].priv;
+ switch (sd->sensor) {
+ case SENSOR_HV7131C:
+ zcxx_probeSensor(gspca_dev);
+@@ -7258,36 +6876,31 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ case SENSOR_PAS106:
+ usb_exchange(gspca_dev, pas106b_Initial_com);
+ break;
+- case SENSOR_PB0330:
+- if (mode) {
+- if (sd->chip_revision == 0xc001
+- || sd->chip_revision == 0xe001
+- || sd->chip_revision == 0x8001)
+- zc3_init = pb03303x_Initial;
+- } else {
+- if (sd->chip_revision == 0xc001
+- || sd->chip_revision == 0xe001
+- || sd->chip_revision == 0x8001)
+- zc3_init = pb03303x_InitialScale;
+- }
+- break;
+ }
+- usb_exchange(gspca_dev, zc3_init);
++ usb_exchange(gspca_dev, init_tb[sd->sensor][mode]);
+
+ switch (sd->sensor) {
+ case SENSOR_ADCM2700:
+ case SENSOR_GC0305:
+ case SENSOR_OV7620:
+ case SENSOR_PO2030:
++ case SENSOR_TAS5130CXX:
+ case SENSOR_TAS5130C_VF0250:
+ /* msleep(100); * ?? */
+ reg_r(gspca_dev, 0x0002); /* --> 0x40 */
+ reg_w(dev, 0x09, 0x01ad); /* (from win traces) */
+ reg_w(dev, 0x15, 0x01ae);
++ if (sd->sensor == SENSOR_TAS5130CXX)
++ break;
+ reg_w(dev, 0x0d, 0x003a);
+ reg_w(dev, 0x02, 0x003b);
+ reg_w(dev, 0x00, 0x0038);
+ break;
++ case SENSOR_PAS202B:
++ reg_w(dev, 0x03, 0x003b);
++ reg_w(dev, 0x0c, 0x003a);
++ reg_w(dev, 0x0b, 0x0039);
++ break;
+ }
+
+ setmatrix(gspca_dev);
+@@ -7300,6 +6913,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ break;
+ case SENSOR_PAS202B:
+ case SENSOR_GC0305:
++ case SENSOR_TAS5130CXX:
+ reg_r(gspca_dev, 0x0008);
+ /* fall thru */
+ case SENSOR_PO2030:
+@@ -7310,10 +6924,8 @@ static int sd_start(struct gspca_dev *gspca_dev)
+
+ /* set the gamma tables when not set */
+ switch (sd->sensor) {
+- case SENSOR_CS2102: /* gamma set in xxx_Initial */
+- case SENSOR_CS2102K:
++ case SENSOR_CS2102K: /* gamma set in xxx_Initial */
+ case SENSOR_HDCS2020b:
+- case SENSOR_PB0330: /* pb with chip_revision - see above */
+ case SENSOR_OV7630C:
+ case SENSOR_TAS5130CK:
+ break;
+@@ -7343,6 +6955,7 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ reg_w(dev, 0x40, 0x0117);
+ break;
+ case SENSOR_GC0305:
++ case SENSOR_TAS5130CXX:
+ reg_w(dev, 0x09, 0x01ad); /* (from win traces) */
+ reg_w(dev, 0x15, 0x01ae);
+ /* fall thru */
+@@ -7365,14 +6978,14 @@ static int sd_start(struct gspca_dev *gspca_dev)
+ setautogain(gspca_dev);
+ switch (sd->sensor) {
+ case SENSOR_PO2030:
+- msleep(500);
+- reg_r(gspca_dev, 0x0008);
+- reg_r(gspca_dev, 0x0007);
+- /*fall thru*/
+- case SENSOR_PAS202B:
++ msleep(50);
+ reg_w(dev, 0x00, 0x0007); /* (from win traces) */
+ reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING);
+ break;
++ case SENSOR_PAS202B:
++ reg_w(dev, 0x32, 0x0007); /* (from win traces) */
++ reg_w(dev, 0x02, ZC3XX_R008_CLOCKSETTING);
++ break;
+ }
+ return 0;
+ }
+@@ -7389,17 +7002,16 @@ static void sd_stop0(struct gspca_dev *gspca_dev)
+ }
+
+ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+- struct gspca_frame *frame,
+- __u8 *data,
++ u8 *data,
+ int len)
+ {
+ struct sd *sd = (struct sd *) gspca_dev;
+
+ if (data[0] == 0xff && data[1] == 0xd8) { /* start of frame */
+- frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
+- data, 0);
++ gspca_frame_add(gspca_dev, LAST_PACKET,
++ NULL, 0);
+ /* put the JPEG header in the new frame */
+- gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
++ gspca_frame_add(gspca_dev, FIRST_PACKET,
+ sd->jpeg_hdr, JPEG_HDR_SZ);
+
+ /* remove the webcam's header:
+@@ -7411,7 +7023,7 @@ static void sd_pkt_scan(struct gspca_dev *gspca_dev,
+ data += 18;
+ len -= 18;
+ }
+- gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
++ gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
+ }
+
+ static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
+@@ -7636,7 +7248,7 @@ static const __devinitdata struct usb_device_id device_table[] = {
+ {USB_DEVICE(0x0ac8, 0x0302), .driver_info = SENSOR_PAS106},
+ {USB_DEVICE(0x0ac8, 0x301b)},
+ {USB_DEVICE(0x0ac8, 0x303b)},
+- {USB_DEVICE(0x0ac8, 0x305b), .driver_info = SENSOR_TAS5130C_VF0250},
++ {USB_DEVICE(0x0ac8, 0x305b)},
+ {USB_DEVICE(0x0ac8, 0x307b)},
+ {USB_DEVICE(0x10fd, 0x0128)},
+ {USB_DEVICE(0x10fd, 0x804d)},
+diff --git a/include/linux/videodev2.h b/include/linux/videodev2.h
+index b59e78c..535ea7b 100644
+--- a/include/linux/videodev2.h
++++ b/include/linux/videodev2.h
+@@ -294,6 +294,7 @@ struct v4l2_pix_format {
+
+ /* Grey formats */
+ #define V4L2_PIX_FMT_GREY v4l2_fourcc('G', 'R', 'E', 'Y') /* 8 Greyscale */
++#define V4L2_PIX_FMT_Y10 v4l2_fourcc('Y', '1', '0', ' ') /* 10 Greyscale */
+ #define V4L2_PIX_FMT_Y16 v4l2_fourcc('Y', '1', '6', ' ') /* 16 Greyscale */
+
+ /* Palette formats */
+@@ -329,7 +330,11 @@ struct v4l2_pix_format {
+ #define V4L2_PIX_FMT_SBGGR8 v4l2_fourcc('B', 'A', '8', '1') /* 8 BGBG.. GRGR.. */
+ #define V4L2_PIX_FMT_SGBRG8 v4l2_fourcc('G', 'B', 'R', 'G') /* 8 GBGB.. RGRG.. */
+ #define V4L2_PIX_FMT_SGRBG8 v4l2_fourcc('G', 'R', 'B', 'G') /* 8 GRGR.. BGBG.. */
+-#define V4L2_PIX_FMT_SGRBG10 v4l2_fourcc('B', 'A', '1', '0') /* 10bit raw bayer */
++#define V4L2_PIX_FMT_SRGGB8 v4l2_fourcc('R', 'G', 'G', 'B') /* 8 RGRG.. GBGB.. */
++#define V4L2_PIX_FMT_SBGGR10 v4l2_fourcc('B', 'G', '1', '0') /* 10 BGBG.. GRGR.. */
++#define V4L2_PIX_FMT_SGBRG10 v4l2_fourcc('G', 'B', '1', '0') /* 10 GBGB.. RGRG.. */
++#define V4L2_PIX_FMT_SGRBG10 v4l2_fourcc('B', 'A', '1', '0') /* 10 GRGR.. BGBG.. */
++#define V4L2_PIX_FMT_SRGGB10 v4l2_fourcc('R', 'G', '1', '0') /* 10 RGRG.. GBGB.. */
+ /* 10bit raw bayer DPCM compressed to 8 bits */
+ #define V4L2_PIX_FMT_SGRBG10DPCM8 v4l2_fourcc('B', 'D', '1', '0')
+ /*
+@@ -345,6 +350,7 @@ struct v4l2_pix_format {
+ #define V4L2_PIX_FMT_MPEG v4l2_fourcc('M', 'P', 'E', 'G') /* MPEG-1/2/4 */
+
+ /* Vendor-specific formats */
++#define V4L2_PIX_FMT_CPIA1 v4l2_fourcc('C', 'P', 'I', 'A') /* cpia1 YUV */
+ #define V4L2_PIX_FMT_WNVA v4l2_fourcc('W', 'N', 'V', 'A') /* Winnov hw compress */
+ #define V4L2_PIX_FMT_SN9C10X v4l2_fourcc('S', '9', '1', '0') /* SN9C10x compression */
+ #define V4L2_PIX_FMT_SN9C20X_I420 v4l2_fourcc('S', '9', '2', '0') /* SN9C20x YUV 4:2:0 */
+@@ -357,10 +363,13 @@ struct v4l2_pix_format {
+ #define V4L2_PIX_FMT_SPCA561 v4l2_fourcc('S', '5', '6', '1') /* compressed GBRG bayer */
+ #define V4L2_PIX_FMT_PAC207 v4l2_fourcc('P', '2', '0', '7') /* compressed BGGR bayer */
+ #define V4L2_PIX_FMT_MR97310A v4l2_fourcc('M', '3', '1', '0') /* compressed BGGR bayer */
++#define V4L2_PIX_FMT_SN9C2028 v4l2_fourcc('S', 'O', 'N', 'X') /* compressed GBRG bayer */
+ #define V4L2_PIX_FMT_SQ905C v4l2_fourcc('9', '0', '5', 'C') /* compressed RGGB bayer */
+ #define V4L2_PIX_FMT_PJPG v4l2_fourcc('P', 'J', 'P', 'G') /* Pixart 73xx JPEG */
+ #define V4L2_PIX_FMT_OV511 v4l2_fourcc('O', '5', '1', '1') /* ov511 JPEG */
+ #define V4L2_PIX_FMT_OV518 v4l2_fourcc('O', '5', '1', '8') /* ov518 JPEG */
++#define V4L2_PIX_FMT_STV0680 v4l2_fourcc('S', '6', '8', '0') /* stv0680 bayer */
++#define V4L2_PIX_FMT_TM6000 v4l2_fourcc('T', 'M', '6', '0') /* tm5600/tm60x0 */
+
+ /*
+ * F O R M A T E N U M E R A T I O N
+@@ -912,8 +921,10 @@ enum v4l2_colorfx {
+ #define V4L2_CID_AUTOBRIGHTNESS (V4L2_CID_BASE+32)
+ #define V4L2_CID_BAND_STOP_FILTER (V4L2_CID_BASE+33)
+
++#define V4L2_CID_ROTATE (V4L2_CID_BASE+34)
++#define V4L2_CID_BG_COLOR (V4L2_CID_BASE+35)
+ /* last CID + 1 */
+-#define V4L2_CID_LASTP1 (V4L2_CID_BASE+34)
++#define V4L2_CID_LASTP1 (V4L2_CID_BASE+36)
+
+ /* MPEG-class control IDs defined by V4L2 */
+ #define V4L2_CID_MPEG_BASE (V4L2_CTRL_CLASS_MPEG | 0x900)
diff --git a/linux-2.6-v4l-dvb-update.patch b/linux-2.6-v4l-dvb-update.patch
new file mode 100644
index 0000000..814bff1
--- /dev/null
+++ b/linux-2.6-v4l-dvb-update.patch
@@ -0,0 +1,366 @@
+Mauro Carvalho Chehab (1):
+ Merge branch 'next' of ../devel into Fedora
+
+Uri Shkolnik (1):
+ V4L/DVB (11241): Siano: SDIO interface driver - remove two redundant lines
+
+diff --git a/linux/drivers/media/dvb/siano/smssdio.c b/linux/drivers/media/dvb/siano/smssdio.c
+new file mode 100644
+index 0000000..4f8fa59
+--- /dev/null
++++ b/linux/drivers/media/dvb/siano/smssdio.c
+@@ -0,0 +1,354 @@
++/*
++ * smssdio.c - Siano 1xxx SDIO interface driver
++ *
++ * Copyright 2008 Pierre Ossman
++ *
++ * Based on code by Siano Mobile Silicon, Inc.,
++ * Copyright (C) 2006-2008, Uri Shkolnik
++ *
++ * 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 hardware is a bit odd in that all transfers should be done
++ * to/from the SMSSDIO_DATA register, yet the "increase address" bit
++ * always needs to be set.
++ *
++ * Also, buffers from the card are always aligned to 128 byte
++ * boundaries.
++ */
++
++/*
++ * General cleanup notes:
++ *
++ * - only typedefs should be name *_t
++ *
++ * - use ERR_PTR and friends for smscore_register_device()
++ *
++ * - smscore_getbuffer should zero fields
++ *
++ * Fix stop command
++ */
++
++#include <linux/moduleparam.h>
++#include <linux/firmware.h>
++#include <linux/delay.h>
++#include <linux/mmc/card.h>
++#include <linux/mmc/sdio_func.h>
++#include <linux/mmc/sdio_ids.h>
++
++#include "smscoreapi.h"
++#include "sms-cards.h"
++
++/* Registers */
++
++#define SMSSDIO_DATA 0x00
++#define SMSSDIO_INT 0x04
++
++static const struct sdio_device_id smssdio_ids[] = {
++ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_STELLAR),
++ .driver_data = SMS1XXX_BOARD_SIANO_STELLAR},
++ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_A0),
++ .driver_data = SMS1XXX_BOARD_SIANO_NOVA_A},
++ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_NOVA_B0),
++ .driver_data = SMS1XXX_BOARD_SIANO_NOVA_B},
++ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VEGA_A0),
++ .driver_data = SMS1XXX_BOARD_SIANO_VEGA},
++ {SDIO_DEVICE(SDIO_VENDOR_ID_SIANO, SDIO_DEVICE_ID_SIANO_VENICE),
++ .driver_data = SMS1XXX_BOARD_SIANO_VEGA},
++ { /* end: all zeroes */ },
++};
++
++MODULE_DEVICE_TABLE(sdio, smssdio_ids);
++
++struct smssdio_device {
++ struct sdio_func *func;
++
++ struct smscore_device_t *coredev;
++
++ struct smscore_buffer_t *split_cb;
++};
++
++/*******************************************************************/
++/* Siano core callbacks */
++/*******************************************************************/
++
++static int smssdio_sendrequest(void *context, void *buffer, size_t size)
++{
++ int ret;
++ struct smssdio_device *smsdev;
++
++ smsdev = context;
++
++ sdio_claim_host(smsdev->func);
++
++ while (size >= smsdev->func->cur_blksize) {
++ ret = sdio_write_blocks(smsdev->func, SMSSDIO_DATA, buffer, 1);
++ if (ret)
++ goto out;
++
++ buffer += smsdev->func->cur_blksize;
++ size -= smsdev->func->cur_blksize;
++ }
++
++ if (size) {
++ ret = sdio_write_bytes(smsdev->func, SMSSDIO_DATA,
++ buffer, size);
++ }
++
++out:
++ sdio_release_host(smsdev->func);
++
++ return ret;
++}
++
++/*******************************************************************/
++/* SDIO callbacks */
++/*******************************************************************/
++
++static void smssdio_interrupt(struct sdio_func *func)
++{
++ int ret, isr;
++
++ struct smssdio_device *smsdev;
++ struct smscore_buffer_t *cb;
++ struct SmsMsgHdr_ST *hdr;
++ size_t size;
++
++ smsdev = sdio_get_drvdata(func);
++
++ /*
++ * The interrupt register has no defined meaning. It is just
++ * a way of turning of the level triggered interrupt.
++ */
++ isr = sdio_readb(func, SMSSDIO_INT, &ret);
++ if (ret) {
++ dev_err(&smsdev->func->dev,
++ "Unable to read interrupt register!\n");
++ return;
++ }
++
++ if (smsdev->split_cb == NULL) {
++ cb = smscore_getbuffer(smsdev->coredev);
++ if (!cb) {
++ dev_err(&smsdev->func->dev,
++ "Unable to allocate data buffer!\n");
++ return;
++ }
++
++ ret = sdio_read_blocks(smsdev->func, cb->p, SMSSDIO_DATA, 1);
++ if (ret) {
++ dev_err(&smsdev->func->dev,
++ "Error %d reading initial block!\n", ret);
++ return;
++ }
++
++ hdr = cb->p;
++
++ if (hdr->msgFlags & MSG_HDR_FLAG_SPLIT_MSG) {
++ smsdev->split_cb = cb;
++ return;
++ }
++
++ size = hdr->msgLength - smsdev->func->cur_blksize;
++ } else {
++ cb = smsdev->split_cb;
++ hdr = cb->p;
++
++ size = hdr->msgLength - sizeof(struct SmsMsgHdr_ST);
++
++ smsdev->split_cb = NULL;
++ }
++
++ if (hdr->msgLength > smsdev->func->cur_blksize) {
++ void *buffer;
++
++ size = ALIGN(size, 128);
++ buffer = cb->p + hdr->msgLength;
++
++ BUG_ON(smsdev->func->cur_blksize != 128);
++
++ /*
++ * First attempt to transfer all of it in one go...
++ */
++ ret = sdio_read_blocks(smsdev->func, buffer,
++ SMSSDIO_DATA, size / 128);
++ if (ret && ret != -EINVAL) {
++ smscore_putbuffer(smsdev->coredev, cb);
++ dev_err(&smsdev->func->dev,
++ "Error %d reading data from card!\n", ret);
++ return;
++ }
++
++ /*
++ * ..then fall back to one block at a time if that is
++ * not possible...
++ *
++ * (we have to do this manually because of the
++ * problem with the "increase address" bit)
++ */
++ if (ret == -EINVAL) {
++ while (size) {
++ ret = sdio_read_blocks(smsdev->func,
++ buffer, SMSSDIO_DATA, 1);
++ if (ret) {
++ smscore_putbuffer(smsdev->coredev, cb);
++ dev_err(&smsdev->func->dev,
++ "Error %d reading "
++ "data from card!\n", ret);
++ return;
++ }
++
++ buffer += smsdev->func->cur_blksize;
++ if (size > smsdev->func->cur_blksize)
++ size -= smsdev->func->cur_blksize;
++ else
++ size = 0;
++ }
++ }
++ }
++
++ cb->size = hdr->msgLength;
++ cb->offset = 0;
++
++ smscore_onresponse(smsdev->coredev, cb);
++}
++
++static int smssdio_probe(struct sdio_func *func,
++ const struct sdio_device_id *id)
++{
++ int ret;
++
++ int board_id;
++ struct smssdio_device *smsdev;
++ struct smsdevice_params_t params;
++
++ board_id = id->driver_data;
++
++ smsdev = kzalloc(sizeof(struct smssdio_device), GFP_KERNEL);
++ if (!smsdev)
++ return -ENOMEM;
++
++ smsdev->func = func;
++
++ memset(&params, 0, sizeof(struct smsdevice_params_t));
++
++ params.device = &func->dev;
++ params.buffer_size = 0x5000; /* ?? */
++ params.num_buffers = 22; /* ?? */
++ params.context = smsdev;
++
++ snprintf(params.devpath, sizeof(params.devpath),
++ "sdio\\%s", sdio_func_id(func));
++
++ params.sendrequest_handler = smssdio_sendrequest;
++
++ params.device_type = sms_get_board(board_id)->type;
++
++ if (params.device_type != SMS_STELLAR)
++ params.flags |= SMS_DEVICE_FAMILY2;
++ else {
++ /*
++ * FIXME: Stellar needs special handling...
++ */
++ ret = -ENODEV;
++ goto free;
++ }
++
++ ret = smscore_register_device(&params, &smsdev->coredev);
++ if (ret < 0)
++ goto free;
++
++ smscore_set_board_id(smsdev->coredev, board_id);
++
++ sdio_claim_host(func);
++
++ ret = sdio_enable_func(func);
++ if (ret)
++ goto release;
++
++ ret = sdio_set_block_size(func, 128);
++ if (ret)
++ goto disable;
++
++ ret = sdio_claim_irq(func, smssdio_interrupt);
++ if (ret)
++ goto disable;
++
++ sdio_set_drvdata(func, smsdev);
++
++ sdio_release_host(func);
++
++ ret = smscore_start_device(smsdev->coredev);
++ if (ret < 0)
++ goto reclaim;
++
++ return 0;
++
++reclaim:
++ sdio_claim_host(func);
++ sdio_release_irq(func);
++disable:
++ sdio_disable_func(func);
++release:
++ sdio_release_host(func);
++ smscore_unregister_device(smsdev->coredev);
++free:
++ kfree(smsdev);
++
++ return ret;
++}
++
++static void smssdio_remove(struct sdio_func *func)
++{
++ struct smssdio_device *smsdev;
++
++ smsdev = sdio_get_drvdata(func);
++
++ /* FIXME: racy! */
++ if (smsdev->split_cb)
++ smscore_putbuffer(smsdev->coredev, smsdev->split_cb);
++
++ smscore_unregister_device(smsdev->coredev);
++
++ sdio_claim_host(func);
++ sdio_release_irq(func);
++ sdio_disable_func(func);
++ sdio_release_host(func);
++
++ kfree(smsdev);
++}
++
++static struct sdio_driver smssdio_driver = {
++ .name = "smssdio",
++ .id_table = smssdio_ids,
++ .probe = smssdio_probe,
++ .remove = smssdio_remove,
++};
++
++/*******************************************************************/
++/* Module functions */
++/*******************************************************************/
++
++int smssdio_register(void)
++{
++ int ret = 0;
++
++ printk(KERN_INFO "smssdio: Siano SMS1xxx SDIO driver\n");
++ printk(KERN_INFO "smssdio: Copyright Pierre Ossman\n");
++
++ ret = sdio_register_driver(&smssdio_driver);
++
++ return ret;
++}
++
++void smssdio_unregister(void)
++{
++ sdio_unregister_driver(&smssdio_driver);
++}
++
++MODULE_DESCRIPTION("Siano SMS1xxx SDIO driver");
++MODULE_AUTHOR("Pierre Ossman");
++MODULE_LICENSE("GPL");
diff --git a/linux-2.6-vio-modalias.patch b/linux-2.6-vio-modalias.patch
new file mode 100644
index 0000000..057eac7
--- /dev/null
+++ b/linux-2.6-vio-modalias.patch
@@ -0,0 +1,32 @@
+diff --git a/arch/powerpc/kernel/vio.c b/arch/powerpc/kernel/vio.c
+index f988672..12a0851 100644
+--- a/arch/powerpc/kernel/vio.c
++++ b/arch/powerpc/kernel/vio.c
+@@ -294,9 +294,27 @@ static ssize_t devspec_show(struct device *dev,
+ return sprintf(buf, "%s\n", of_node ? of_node->full_name : "none");
+ }
+
++static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
++ char *buf)
++{
++ const struct vio_dev *vio_dev = to_vio_dev(dev);
++ struct device_node *dn;
++ const char *cp;
++
++ dn = dev->archdata.of_node;
++ if (!dn)
++ return -ENODEV;
++ cp = of_get_property(dn, "compatible", NULL);
++ if (!cp)
++ return -ENODEV;
++
++ return sprintf(buf, "vio:T%sS%s\n", vio_dev->type, cp);
++}
++
+ static struct device_attribute vio_dev_attrs[] = {
+ __ATTR_RO(name),
+ __ATTR_RO(devspec),
++ __ATTR_RO(modalias),
+ __ATTR_NULL
+ };
+
diff --git a/linux-2.6-x86-64-fbdev-primary.patch b/linux-2.6-x86-64-fbdev-primary.patch
new file mode 100644
index 0000000..b35096f
--- /dev/null
+++ b/linux-2.6-x86-64-fbdev-primary.patch
@@ -0,0 +1,49 @@
+From cdd54d73203838f249291988d5f79e40fee00a05 Mon Sep 17 00:00:00 2001
+From: Dave Airlie <airlied@redhat.com>
+Date: Thu, 7 Jan 2010 16:59:06 +1000
+Subject: [PATCH] x86: allow fbdev primary video code on 64-bit.
+
+For some reason the 64-bit tree was doing this differently and
+I can't see why it would need to.
+
+This correct behaviour when you have two GPUs plugged in and
+32-bit put the console in one place and 64-bit in another.
+
+Signed-off-by: Dave Airlie <airlied@redhat.com>
+---
+ arch/x86/Makefile | 2 --
+ arch/x86/include/asm/fb.h | 4 ----
+ 2 files changed, 0 insertions(+), 6 deletions(-)
+
+diff --git a/arch/x86/Makefile b/arch/x86/Makefile
+index 78b32be..0a43dc5 100644
+--- a/arch/x86/Makefile
++++ b/arch/x86/Makefile
+@@ -135,9 +135,7 @@ drivers-$(CONFIG_OPROFILE) += arch/x86/oprofile/
+ # suspend and hibernation support
+ drivers-$(CONFIG_PM) += arch/x86/power/
+
+-ifeq ($(CONFIG_X86_32),y)
+ drivers-$(CONFIG_FB) += arch/x86/video/
+-endif
+
+ ####
+ # boot loader support. Several targets are kept for legacy purposes
+diff --git a/arch/x86/include/asm/fb.h b/arch/x86/include/asm/fb.h
+index 5301846..2519d06 100644
+--- a/arch/x86/include/asm/fb.h
++++ b/arch/x86/include/asm/fb.h
+@@ -12,10 +12,6 @@ static inline void fb_pgprotect(struct file *file, struct vm_area_struct *vma,
+ pgprot_val(vma->vm_page_prot) |= _PAGE_PCD;
+ }
+
+-#ifdef CONFIG_X86_32
+ extern int fb_is_primary_device(struct fb_info *info);
+-#else
+-static inline int fb_is_primary_device(struct fb_info *info) { return 0; }
+-#endif
+
+ #endif /* _ASM_X86_FB_H */
+--
+1.6.5.2
+
diff --git a/linux-2.6.29-sparc-IOC_TYPECHECK.patch b/linux-2.6.29-sparc-IOC_TYPECHECK.patch
new file mode 100644
index 0000000..d73c30a
--- /dev/null
+++ b/linux-2.6.29-sparc-IOC_TYPECHECK.patch
@@ -0,0 +1,21 @@
+diff -up vanilla-2.6.29-rc7-git2/arch/sparc/include/asm/ioctl.h.BAD vanilla-2.6.29-rc7-git2/arch/sparc/include/asm/ioctl.h
+--- vanilla-2.6.29-rc7-git2/arch/sparc/include/asm/ioctl.h.BAD 2009-03-09 17:01:32.000000000 -0400
++++ vanilla-2.6.29-rc7-git2/arch/sparc/include/asm/ioctl.h 2009-03-09 16:52:27.000000000 -0400
+@@ -41,6 +41,17 @@
+ ((nr) << _IOC_NRSHIFT) | \
+ ((size) << _IOC_SIZESHIFT))
+
++#ifdef __KERNEL__
++/* provoke compile error for invalid uses of size argument */
++extern unsigned int __invalid_size_argument_for_IOC;
++#define _IOC_TYPECHECK(t) \
++ ((sizeof(t) == sizeof(t[1]) && \
++ sizeof(t) < (1 << _IOC_SIZEBITS)) ? \
++ sizeof(t) : __invalid_size_argument_for_IOC)
++#else
++#define _IOC_TYPECHECK(t) (sizeof(t))
++#endif
++
+ #define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
+ #define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
+ #define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
diff --git a/linux-2.6.30-hush-rom-warning.patch b/linux-2.6.30-hush-rom-warning.patch
new file mode 100644
index 0000000..a4a0809
--- /dev/null
+++ b/linux-2.6.30-hush-rom-warning.patch
@@ -0,0 +1,27 @@
+diff -up linux-2.6.30.noarch/drivers/pci/setup-res.c.jx linux-2.6.30.noarch/drivers/pci/setup-res.c
+--- linux-2.6.30.noarch/drivers/pci/setup-res.c.jx 2009-07-27 17:56:13.000000000 -0400
++++ linux-2.6.30.noarch/drivers/pci/setup-res.c 2009-07-27 17:58:25.000000000 -0400
+@@ -101,6 +101,7 @@ int pci_claim_resource(struct pci_dev *d
+ struct resource *res = &dev->resource[resource];
+ struct resource *root;
+ int err;
++ const char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge";
+
+ root = pci_find_parent_resource(dev, res);
+
+@@ -108,8 +109,13 @@ int pci_claim_resource(struct pci_dev *d
+ if (root != NULL)
+ err = request_resource(root, res);
+
+- if (err) {
+- const char *dtype = resource < PCI_BRIDGE_RESOURCES ? "device" : "bridge";
++ if (err && resource == 6) {
++ dev_info(&dev->dev, "BAR %d: %s of %s %pR\n",
++ resource,
++ root ? "address space collision on" :
++ "no parent found for",
++ dtype, res);
++ } else if (err) {
+ dev_err(&dev->dev, "BAR %d: %s of %s %pR\n",
+ resource,
+ root ? "address space collision on" :
diff --git a/linux-2.6.30-no-pcspkr-modalias.patch b/linux-2.6.30-no-pcspkr-modalias.patch
new file mode 100644
index 0000000..c703b88
--- /dev/null
+++ b/linux-2.6.30-no-pcspkr-modalias.patch
@@ -0,0 +1,11 @@
+diff -up linux-2.6.30.noarch/drivers/input/misc/pcspkr.c.jx linux-2.6.30.noarch/drivers/input/misc/pcspkr.c
+--- linux-2.6.30.noarch/drivers/input/misc/pcspkr.c.jx 2009-07-28 16:54:44.000000000 -0400
++++ linux-2.6.30.noarch/drivers/input/misc/pcspkr.c 2009-07-28 16:59:36.000000000 -0400
+@@ -23,7 +23,6 @@
+ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+ MODULE_DESCRIPTION("PC Speaker beeper driver");
+ MODULE_LICENSE("GPL");
+-MODULE_ALIAS("platform:pcspkr");
+
+ #if defined(CONFIG_MIPS) || defined(CONFIG_X86)
+ /* Use the global PIT lock ! */
diff --git a/lirc-2.6.32.patch b/lirc-2.6.32.patch
new file mode 100644
index 0000000..f64e9d5
--- /dev/null
+++ b/lirc-2.6.32.patch
@@ -0,0 +1,17736 @@
+ drivers/input/Kconfig | 30 +-
+ drivers/input/Makefile | 3 +
+ drivers/input/input-polldev.c | 21 +-
+ drivers/input/lirc/Kconfig | 116 ++
+ drivers/input/lirc/Makefile | 21 +
+ drivers/input/lirc/lirc_bt829.c | 383 +++++
+ drivers/input/lirc/lirc_dev.c | 836 +++++++++++
+ drivers/input/lirc/lirc_dev.h | 194 +++
+ drivers/input/lirc/lirc_ene0100.c | 646 +++++++++
+ drivers/input/lirc/lirc_ene0100.h | 169 +++
+ drivers/input/lirc/lirc_i2c.c | 536 +++++++
+ drivers/input/lirc/lirc_igorplugusb.c | 555 ++++++++
+ drivers/input/lirc/lirc_imon.c | 1053 ++++++++++++++
+ drivers/input/lirc/lirc_it87.c | 1021 +++++++++++++
+ drivers/input/lirc/lirc_it87.h | 116 ++
+ drivers/input/lirc/lirc_ite8709.c | 540 +++++++
+ drivers/input/lirc/lirc_mceusb.c | 1385 ++++++++++++++++++
+ drivers/input/lirc/lirc_parallel.c | 709 +++++++++
+ drivers/input/lirc/lirc_parallel.h | 26 +
+ drivers/input/lirc/lirc_sasem.c | 931 ++++++++++++
+ drivers/input/lirc/lirc_serial.c | 1317 +++++++++++++++++
+ drivers/input/lirc/lirc_sir.c | 1283 +++++++++++++++++
+ drivers/input/lirc/lirc_streamzap.c | 794 +++++++++++
+ drivers/input/lirc/lirc_ttusbir.c | 397 ++++++
+ drivers/input/lirc/lirc_zilog.c | 1388 ++++++++++++++++++
+ drivers/input/misc/Kconfig | 11 +
+ drivers/input/misc/Makefile | 1 +
+ drivers/input/misc/imon.c | 2523 +++++++++++++++++++++++++++++++++
+ drivers/input/sparse-keymap.c | 250 ++++
+ include/linux/input/sparse-keymap.h | 62 +
+ include/linux/lirc.h | 94 ++
+ 31 files changed, 17398 insertions(+), 13 deletions(-)
+
+diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
+index cd50c00..198e48f 100644
+--- a/drivers/input/Kconfig
++++ b/drivers/input/Kconfig
+@@ -8,7 +8,7 @@ menu "Input device support"
+ config INPUT
+ tristate "Generic input layer (needed for keyboard, mouse, ...)" if EMBEDDED
+ default y
+- ---help---
++ help
+ Say Y here if you have any input device (mouse, keyboard, tablet,
+ joystick, steering wheel ...) connected to your system and want
+ it to be available to applications. This includes standard PS/2
+@@ -27,8 +27,7 @@ if INPUT
+
+ config INPUT_FF_MEMLESS
+ tristate "Support for memoryless force-feedback devices"
+- default n
+- ---help---
++ help
+ Say Y here if you have memoryless force-feedback input device
+ such as Logitech WingMan Force 3D, ThrustMaster FireStorm Dual
+ Power 2, or similar. You will also need to enable hardware-specific
+@@ -52,12 +51,25 @@ config INPUT_POLLDEV
+ To compile this driver as a module, choose M here: the
+ module will be called input-polldev.
+
++config INPUT_SPARSEKMAP
++ tristate "Sparse keymap support library"
++ help
++ Say Y here if you are using a driver for an input
++ device that uses sparse keymap. This option is only
++ useful for out-of-tree drivers since in-tree drivers
++ select it automatically.
++
++ If unsure, say N.
++
++ To compile this driver as a module, choose M here: the
++ module will be called sparse-keymap.
++
+ comment "Userland interfaces"
+
+ config INPUT_MOUSEDEV
+ tristate "Mouse interface" if EMBEDDED
+ default y
+- ---help---
++ help
+ Say Y here if you want your mouse to be accessible as char devices
+ 13:32+ - /dev/input/mouseX and 13:63 - /dev/input/mice as an
+ emulated IntelliMouse Explorer PS/2 mouse. That way, all user space
+@@ -73,7 +85,7 @@ config INPUT_MOUSEDEV_PSAUX
+ bool "Provide legacy /dev/psaux device"
+ default y
+ depends on INPUT_MOUSEDEV
+- ---help---
++ help
+ Say Y here if you want your mouse also be accessible as char device
+ 10:1 - /dev/psaux. The data available through /dev/psaux is exactly
+ the same as the data from /dev/input/mice.
+@@ -103,7 +115,7 @@ config INPUT_MOUSEDEV_SCREEN_Y
+
+ config INPUT_JOYDEV
+ tristate "Joystick interface"
+- ---help---
++ help
+ Say Y here if you want your joystick or gamepad to be
+ accessible as char device 13:0+ - /dev/input/jsX device.
+
+@@ -125,7 +137,7 @@ config INPUT_EVDEV
+
+ config INPUT_EVBUG
+ tristate "Event debugging"
+- ---help---
++ help
+ Say Y here if you have a problem with the input subsystem and
+ want all events (keypresses, mouse movements), to be output to
+ the system log. While this is useful for debugging, it's also
+@@ -140,7 +152,7 @@ config INPUT_EVBUG
+ config INPUT_APMPOWER
+ tristate "Input Power Event -> APM Bridge" if EMBEDDED
+ depends on INPUT && APM_EMULATION
+- ---help---
++ help
+ Say Y here if you want suspend key events to trigger a user
+ requested suspend through APM. This is useful on embedded
+ systems where such behaviour is desired without userspace
+@@ -170,6 +182,8 @@ source "drivers/input/tablet/Kconfig"
+
+ source "drivers/input/touchscreen/Kconfig"
+
++source "drivers/input/lirc/Kconfig"
++
+ source "drivers/input/misc/Kconfig"
+
+ endif
+diff --git a/drivers/input/Makefile b/drivers/input/Makefile
+index 4c9c745..cb119e7 100644
+--- a/drivers/input/Makefile
++++ b/drivers/input/Makefile
+@@ -9,6 +9,7 @@ input-core-objs := input.o input-compat.o ff-core.o
+
+ obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
+ obj-$(CONFIG_INPUT_POLLDEV) += input-polldev.o
++obj-$(CONFIG_INPUT_SPARSEKMAP) += sparse-keymap.o
+
+ obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
+ obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
+@@ -25,3 +26,5 @@ obj-$(CONFIG_INPUT_MISC) += misc/
+ obj-$(CONFIG_INPUT_APMPOWER) += apm-power.o
+
+ obj-$(CONFIG_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o
++
++obj-$(CONFIG_INPUT_LIRC) += lirc/
+diff --git a/drivers/input/input-polldev.c b/drivers/input/input-polldev.c
+index 0d3ce7a..eddeba6 100644
+--- a/drivers/input/input-polldev.c
++++ b/drivers/input/input-polldev.c
+@@ -126,7 +126,7 @@ EXPORT_SYMBOL(input_allocate_polled_device);
+ * @dev: device to free
+ *
+ * The function frees memory allocated for polling device and drops
+- * reference to the associated input device (if present).
++ * reference to the associated input device.
+ */
+ void input_free_polled_device(struct input_polled_dev *dev)
+ {
+@@ -150,6 +150,7 @@ EXPORT_SYMBOL(input_free_polled_device);
+ int input_register_polled_device(struct input_polled_dev *dev)
+ {
+ struct input_dev *input = dev->input;
++ int error;
+
+ input_set_drvdata(input, dev);
+ INIT_DELAYED_WORK(&dev->work, input_polled_device_work);
+@@ -158,7 +159,20 @@ int input_register_polled_device(struct input_polled_dev *dev)
+ input->open = input_open_polled_device;
+ input->close = input_close_polled_device;
+
+- return input_register_device(input);
++ error = input_register_device(input);
++ if (error)
++ return error;
++
++ /*
++ * Take extra reference to the underlying input device so
++ * that it survives call to input_unregister_polled_device()
++ * and is deleted only after input_free_polled_device()
++ * has been invoked. This is needed to ease task of freeing
++ * sparse keymaps.
++ */
++ input_get_device(input);
++
++ return 0;
+ }
+ EXPORT_SYMBOL(input_register_polled_device);
+
+@@ -169,13 +183,10 @@ EXPORT_SYMBOL(input_register_polled_device);
+ * The function unregisters previously registered polled input
+ * device from input layer. Polling is stopped and device is
+ * ready to be freed with call to input_free_polled_device().
+- * Callers should not attempt to access dev->input pointer
+- * after calling this function.
+ */
+ void input_unregister_polled_device(struct input_polled_dev *dev)
+ {
+ input_unregister_device(dev->input);
+- dev->input = NULL;
+ }
+ EXPORT_SYMBOL(input_unregister_polled_device);
+
+diff --git a/drivers/input/lirc/Kconfig b/drivers/input/lirc/Kconfig
+new file mode 100644
+index 0000000..86fc063
+--- /dev/null
++++ b/drivers/input/lirc/Kconfig
+@@ -0,0 +1,116 @@
++#
++# LIRC driver(s) configuration
++#
++menuconfig INPUT_LIRC
++ tristate "Linux Infrared Remote Control IR receiver/transmitter drivers"
++ help
++ Say Y here, and all supported Linux Infrared Remote Control IR and
++ RF receiver and transmitter drivers will be displayed. When paired
++ with a remote control and the lirc daemon, the receiver drivers
++ allow control of your Linux system via remote control.
++
++if INPUT_LIRC
++
++config LIRC_BT829
++ tristate "BT829 based hardware"
++ depends on INPUT_LIRC
++ help
++ Driver for the IR interface on BT829-based hardware
++
++config LIRC_ENE0100
++ tristate "ENE KB3924/ENE0100 CIR Port Reciever"
++ depends on INPUT_LIRC
++ help
++ This is a driver for CIR port handled by ENE KB3924 embedded
++ controller found on some notebooks.
++ It appears on PNP list as ENE0100.
++
++config LIRC_I2C
++ tristate "I2C Based IR Receivers"
++ depends on INPUT_LIRC
++ help
++ Driver for I2C-based IR receivers, such as those commonly
++ found onboard Hauppauge PVR-150/250/350 video capture cards
++
++config LIRC_IGORPLUGUSB
++ tristate "Igor Cesko's USB IR Receiver"
++ depends on INPUT_LIRC && USB
++ help
++ Driver for Igor Cesko's USB IR Receiver
++
++config LIRC_IMON
++ tristate "Legacy SoundGraph iMON Receiver and Display"
++ depends on INPUT_LIRC
++ help
++ Driver for the original SoundGraph iMON IR Receiver and Display
++
++ Current generation iMON devices use the input layer imon driver.
++
++config LIRC_IT87
++ tristate "ITE IT87XX CIR Port Receiver"
++ depends on INPUT_LIRC
++ help
++ Driver for the ITE IT87xx IR Receiver
++
++config LIRC_ITE8709
++ tristate "ITE8709 CIR Port Receiver"
++ depends on INPUT_LIRC && PNP
++ help
++ Driver for the ITE8709 IR Receiver
++
++config LIRC_MCEUSB
++ tristate "Windows Media Center Ed. USB IR Transceiver"
++ depends on INPUT_LIRC && USB
++ help
++ Driver for Windows Media Center Ed. USB IR Transceivers
++
++config LIRC_PARALLEL
++ tristate "Homebrew Parallel Port Receiver"
++ depends on INPUT_LIRC && !SMP
++ help
++ Driver for Homebrew Parallel Port Receivers
++
++config LIRC_SASEM
++ tristate "Sasem USB IR Remote"
++ depends on INPUT_LIRC
++ help
++ Driver for the Sasem OnAir Remocon-V or Dign HV5 HTPC IR/VFD Module
++
++config LIRC_SERIAL
++ tristate "Homebrew Serial Port Receiver"
++ depends on INPUT_LIRC
++ help
++ Driver for Homebrew Serial Port Receivers
++
++config LIRC_SERIAL_TRANSMITTER
++ bool "Serial Port Transmitter"
++ default y
++ depends on LIRC_SERIAL
++ help
++ Serial Port Transmitter support
++
++config LIRC_SIR
++ tristate "Built-in SIR IrDA port"
++ depends on INPUT_LIRC
++ help
++ Driver for the SIR IrDA port
++
++config LIRC_STREAMZAP
++ tristate "Streamzap PC Receiver"
++ depends on INPUT_LIRC
++ help
++ Driver for the Streamzap PC Receiver
++
++config LIRC_TTUSBIR
++ tristate "Technotrend USB IR Receiver"
++ depends on INPUT_LIRC && USB
++ help
++ Driver for the Technotrend USB IR Receiver
++
++config LIRC_ZILOG
++ tristate "Zilog/Hauppauge IR Transmitter"
++ depends on INPUT_LIRC
++ help
++ Driver for the Zilog/Hauppauge IR Transmitter, found on
++ PVR-150/500, HVR-1200/1250/1700/1800, HD-PVR and other cards
++endif
+diff --git a/drivers/input/lirc/Makefile b/drivers/input/lirc/Makefile
+new file mode 100644
+index 0000000..9122e87
+--- /dev/null
++++ b/drivers/input/lirc/Makefile
+@@ -0,0 +1,21 @@
++# Makefile for the lirc drivers.
++#
++
++# Each configuration option enables a list of files.
++
++obj-$(CONFIG_INPUT_LIRC) += lirc_dev.o
++obj-$(CONFIG_LIRC_BT829) += lirc_bt829.o
++obj-$(CONFIG_LIRC_ENE0100) += lirc_ene0100.o
++obj-$(CONFIG_LIRC_I2C) += lirc_i2c.o
++obj-$(CONFIG_LIRC_IGORPLUGUSB) += lirc_igorplugusb.o
++obj-$(CONFIG_LIRC_IMON) += lirc_imon.o
++obj-$(CONFIG_LIRC_IT87) += lirc_it87.o
++obj-$(CONFIG_LIRC_ITE8709) += lirc_ite8709.o
++obj-$(CONFIG_LIRC_MCEUSB) += lirc_mceusb.o
++obj-$(CONFIG_LIRC_PARALLEL) += lirc_parallel.o
++obj-$(CONFIG_LIRC_SASEM) += lirc_sasem.o
++obj-$(CONFIG_LIRC_SERIAL) += lirc_serial.o
++obj-$(CONFIG_LIRC_SIR) += lirc_sir.o
++obj-$(CONFIG_LIRC_STREAMZAP) += lirc_streamzap.o
++obj-$(CONFIG_LIRC_TTUSBIR) += lirc_ttusbir.o
++obj-$(CONFIG_LIRC_ZILOG) += lirc_zilog.o
+diff --git a/drivers/input/lirc/lirc_bt829.c b/drivers/input/lirc/lirc_bt829.c
+new file mode 100644
+index 0000000..0485884
+--- /dev/null
++++ b/drivers/input/lirc/lirc_bt829.c
+@@ -0,0 +1,383 @@
++/*
++ * Remote control driver for the TV-card based on bt829
++ *
++ * by Leonid Froenchenko <lfroen@galileo.co.il>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++*/
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/threads.h>
++#include <linux/sched.h>
++#include <linux/ioport.h>
++#include <linux/pci.h>
++#include <linux/delay.h>
++
++#include "lirc_dev.h"
++
++static int poll_main(void);
++static int atir_init_start(void);
++
++static void write_index(unsigned char index, unsigned int value);
++static unsigned int read_index(unsigned char index);
++
++static void do_i2c_start(void);
++static void do_i2c_stop(void);
++
++static void seems_wr_byte(unsigned char al);
++static unsigned char seems_rd_byte(void);
++
++static unsigned int read_index(unsigned char al);
++static void write_index(unsigned char ah, unsigned int edx);
++
++static void cycle_delay(int cycle);
++
++static void do_set_bits(unsigned char bl);
++static unsigned char do_get_bits(void);
++
++#define DATA_PCI_OFF 0x7FFC00
++#define WAIT_CYCLE 20
++
++#define DRIVER_NAME "lirc_bt829"
++
++static int debug;
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG DRIVER_NAME ": "fmt, ## args); \
++ } while (0)
++
++static int atir_minor;
++static unsigned long pci_addr_phys;
++static unsigned char *pci_addr_lin;
++
++static struct lirc_driver atir_driver;
++
++static struct pci_dev *do_pci_probe(void)
++{
++ struct pci_dev *my_dev;
++ my_dev = pci_get_device(PCI_VENDOR_ID_ATI,
++ PCI_DEVICE_ID_ATI_264VT, NULL);
++ if (my_dev) {
++ printk(KERN_ERR DRIVER_NAME ": Using device: %s\n",
++ pci_name(my_dev));
++ pci_addr_phys = 0;
++ if (my_dev->resource[0].flags & IORESOURCE_MEM) {
++ pci_addr_phys = my_dev->resource[0].start;
++ printk(KERN_INFO DRIVER_NAME ": memory at 0x%08X \n",
++ (unsigned int)pci_addr_phys);
++ }
++ if (pci_addr_phys == 0) {
++ printk(KERN_ERR DRIVER_NAME ": no memory resource ?\n");
++ return NULL;
++ }
++ } else {
++ printk(KERN_ERR DRIVER_NAME ": pci_probe failed\n");
++ return NULL;
++ }
++ return my_dev;
++}
++
++static int atir_add_to_buf(void *data, struct lirc_buffer *buf)
++{
++ unsigned char key;
++ int status;
++ status = poll_main();
++ key = (status >> 8) & 0xFF;
++ if (status & 0xFF) {
++ dprintk("reading key %02X\n", key);
++ lirc_buffer_write(buf, &key);
++ return 0;
++ }
++ return -ENODATA;
++}
++
++static int atir_set_use_inc(void *data)
++{
++ dprintk("driver is opened\n");
++ return 0;
++}
++
++static void atir_set_use_dec(void *data)
++{
++ dprintk("driver is closed\n");
++}
++
++int init_module(void)
++{
++ struct pci_dev *pdev;
++
++ pdev = do_pci_probe();
++ if (pdev == NULL)
++ return 1;
++
++ if (!atir_init_start())
++ return 1;
++
++ strcpy(atir_driver.name, "ATIR");
++ atir_driver.minor = -1;
++ atir_driver.code_length = 8;
++ atir_driver.sample_rate = 10;
++ atir_driver.data = 0;
++ atir_driver.add_to_buf = atir_add_to_buf;
++ atir_driver.set_use_inc = atir_set_use_inc;
++ atir_driver.set_use_dec = atir_set_use_dec;
++ atir_driver.dev = &pdev->dev;
++ atir_driver.owner = THIS_MODULE;
++
++ atir_minor = lirc_register_driver(&atir_driver);
++ if (atir_minor < 0) {
++ printk(KERN_ERR DRIVER_NAME ": failed to register driver!\n");
++ return atir_minor;
++ }
++ dprintk("driver is registered on minor %d\n", atir_minor);
++
++ return 0;
++}
++
++
++void cleanup_module(void)
++{
++ lirc_unregister_driver(atir_minor);
++}
++
++
++static int atir_init_start(void)
++{
++ pci_addr_lin = ioremap(pci_addr_phys + DATA_PCI_OFF, 0x400);
++ if (pci_addr_lin == 0) {
++ printk(KERN_INFO DRIVER_NAME ": pci mem must be mapped\n");
++ return 0;
++ }
++ return 1;
++}
++
++static void cycle_delay(int cycle)
++{
++ udelay(WAIT_CYCLE*cycle);
++}
++
++
++static int poll_main()
++{
++ unsigned char status_high, status_low;
++
++ do_i2c_start();
++
++ seems_wr_byte(0xAA);
++ seems_wr_byte(0x01);
++
++ do_i2c_start();
++
++ seems_wr_byte(0xAB);
++
++ status_low = seems_rd_byte();
++ status_high = seems_rd_byte();
++
++ do_i2c_stop();
++
++ return (status_high << 8) | status_low;
++}
++
++static void do_i2c_start(void)
++{
++ do_set_bits(3);
++ cycle_delay(4);
++
++ do_set_bits(1);
++ cycle_delay(7);
++
++ do_set_bits(0);
++ cycle_delay(2);
++}
++
++static void do_i2c_stop(void)
++{
++ unsigned char bits;
++ bits = do_get_bits() & 0xFD;
++ do_set_bits(bits);
++ cycle_delay(1);
++
++ bits |= 1;
++ do_set_bits(bits);
++ cycle_delay(2);
++
++ bits |= 2;
++ do_set_bits(bits);
++ bits = 3;
++ do_set_bits(bits);
++ cycle_delay(2);
++}
++
++static void seems_wr_byte(unsigned char value)
++{
++ int i;
++ unsigned char reg;
++
++ reg = do_get_bits();
++ for (i = 0; i < 8; i++) {
++ if (value & 0x80)
++ reg |= 0x02;
++ else
++ reg &= 0xFD;
++
++ do_set_bits(reg);
++ cycle_delay(1);
++
++ reg |= 1;
++ do_set_bits(reg);
++ cycle_delay(1);
++
++ reg &= 0xFE;
++ do_set_bits(reg);
++ cycle_delay(1);
++ value <<= 1;
++ }
++ cycle_delay(2);
++
++ reg |= 2;
++ do_set_bits(reg);
++
++ reg |= 1;
++ do_set_bits(reg);
++
++ cycle_delay(1);
++ do_get_bits();
++
++ reg &= 0xFE;
++ do_set_bits(reg);
++ cycle_delay(3);
++}
++
++static unsigned char seems_rd_byte(void)
++{
++ int i;
++ int rd_byte;
++ unsigned char bits_2, bits_1;
++
++ bits_1 = do_get_bits() | 2;
++ do_set_bits(bits_1);
++
++ rd_byte = 0;
++ for (i = 0; i < 8; i++) {
++ bits_1 &= 0xFE;
++ do_set_bits(bits_1);
++ cycle_delay(2);
++
++ bits_1 |= 1;
++ do_set_bits(bits_1);
++ cycle_delay(1);
++
++ bits_2 = do_get_bits();
++ if (bits_2 & 2)
++ rd_byte |= 1;
++
++ rd_byte <<= 1;
++ }
++
++ bits_1 = 0;
++ if (bits_2 == 0)
++ bits_1 |= 2;
++
++ do_set_bits(bits_1);
++ cycle_delay(2);
++
++ bits_1 |= 1;
++ do_set_bits(bits_1);
++ cycle_delay(3);
++
++ bits_1 &= 0xFE;
++ do_set_bits(bits_1);
++ cycle_delay(2);
++
++ rd_byte >>= 1;
++ rd_byte &= 0xFF;
++ return rd_byte;
++}
++
++static void do_set_bits(unsigned char new_bits)
++{
++ int reg_val;
++ reg_val = read_index(0x34);
++ if (new_bits & 2) {
++ reg_val &= 0xFFFFFFDF;
++ reg_val |= 1;
++ } else {
++ reg_val &= 0xFFFFFFFE;
++ reg_val |= 0x20;
++ }
++ reg_val |= 0x10;
++ write_index(0x34, reg_val);
++
++ reg_val = read_index(0x31);
++ if (new_bits & 1)
++ reg_val |= 0x1000000;
++ else
++ reg_val &= 0xFEFFFFFF;
++
++ reg_val |= 0x8000000;
++ write_index(0x31, reg_val);
++}
++
++static unsigned char do_get_bits(void)
++{
++ unsigned char bits;
++ int reg_val;
++
++ reg_val = read_index(0x34);
++ reg_val |= 0x10;
++ reg_val &= 0xFFFFFFDF;
++ write_index(0x34, reg_val);
++
++ reg_val = read_index(0x34);
++ bits = 0;
++ if (reg_val & 8)
++ bits |= 2;
++ else
++ bits &= 0xFD;
++
++ reg_val = read_index(0x31);
++ if (reg_val & 0x1000000)
++ bits |= 1;
++ else
++ bits &= 0xFE;
++
++ return bits;
++}
++
++static unsigned int read_index(unsigned char index)
++{
++ unsigned char *addr;
++ unsigned int value;
++ /* addr = pci_addr_lin + DATA_PCI_OFF + ((index & 0xFF) << 2); */
++ addr = pci_addr_lin + ((index & 0xFF) << 2);
++ value = readl(addr);
++ return value;
++}
++
++static void write_index(unsigned char index, unsigned int reg_val)
++{
++ unsigned char *addr;
++ addr = pci_addr_lin + ((index & 0xFF) << 2);
++ writel(reg_val, addr);
++}
++
++MODULE_AUTHOR("Froenchenko Leonid");
++MODULE_DESCRIPTION("IR remote driver for bt829 based TV cards");
++MODULE_LICENSE("GPL");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Debug enabled or not");
+diff --git a/drivers/input/lirc/lirc_dev.c b/drivers/input/lirc/lirc_dev.c
+new file mode 100644
+index 0000000..f7d6fdd
+--- /dev/null
++++ b/drivers/input/lirc/lirc_dev.c
+@@ -0,0 +1,836 @@
++/*
++ * LIRC base driver
++ *
++ * by Artur Lipowski <alipowski@interia.pl>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/ioctl.h>
++#include <linux/fs.h>
++#include <linux/poll.h>
++#include <linux/completion.h>
++#include <linux/errno.h>
++#include <linux/mutex.h>
++#include <linux/wait.h>
++#include <linux/unistd.h>
++#include <linux/kthread.h>
++#include <linux/bitops.h>
++#include <linux/device.h>
++#include <linux/cdev.h>
++#include <linux/smp_lock.h>
++#ifdef CONFIG_COMPAT
++#include <linux/compat.h>
++#endif
++
++#include <linux/lirc.h>
++#include "lirc_dev.h"
++
++static int debug;
++
++#define IRCTL_DEV_NAME "BaseRemoteCtl"
++#define NOPLUG -1
++#define LOGHEAD "lirc_dev (%s[%d]): "
++
++static dev_t lirc_base_dev;
++
++struct irctl {
++ struct lirc_driver d;
++ int attached;
++ int open;
++
++ struct mutex irctl_lock;
++ struct lirc_buffer *buf;
++ unsigned int chunk_size;
++
++ struct task_struct *task;
++ long jiffies_to_wait;
++
++ struct cdev cdev;
++};
++
++static DEFINE_MUTEX(lirc_dev_lock);
++
++static struct irctl *irctls[MAX_IRCTL_DEVICES];
++
++/* Only used for sysfs but defined to void otherwise */
++static struct class *lirc_class;
++
++/* helper function
++ * initializes the irctl structure
++ */
++static void init_irctl(struct irctl *ir)
++{
++ dev_dbg(ir->d.dev, LOGHEAD "initializing irctl\n",
++ ir->d.name, ir->d.minor);
++ mutex_init(&ir->irctl_lock);
++ ir->d.minor = NOPLUG;
++}
++
++static void cleanup(struct irctl *ir)
++{
++ dev_dbg(ir->d.dev, LOGHEAD "cleaning up\n", ir->d.name, ir->d.minor);
++
++ device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor));
++
++ if (ir->buf != ir->d.rbuf) {
++ lirc_buffer_free(ir->buf);
++ kfree(ir->buf);
++ }
++ ir->buf = NULL;
++}
++
++/* helper function
++ * reads key codes from driver and puts them into buffer
++ * returns 0 on success
++ */
++static int add_to_buf(struct irctl *ir)
++{
++ if (ir->d.add_to_buf) {
++ int res = -ENODATA;
++ int got_data = 0;
++
++ /*
++ * service the device as long as it is returning
++ * data and we have space
++ */
++get_data:
++ res = ir->d.add_to_buf(ir->d.data, ir->buf);
++ if (res == 0) {
++ got_data++;
++ goto get_data;
++ }
++
++ if (res == -ENODEV)
++ kthread_stop(ir->task);
++
++ return got_data ? 0 : res;
++ }
++
++ return 0;
++}
++
++/* main function of the polling thread
++ */
++static int lirc_thread(void *irctl)
++{
++ struct irctl *ir = irctl;
++
++ dev_dbg(ir->d.dev, LOGHEAD "poll thread started\n",
++ ir->d.name, ir->d.minor);
++
++ do {
++ if (ir->open) {
++ if (ir->jiffies_to_wait) {
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(ir->jiffies_to_wait);
++ }
++ if (kthread_should_stop())
++ break;
++ if (!add_to_buf(ir))
++ wake_up_interruptible(&ir->buf->wait_poll);
++ } else {
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule();
++ }
++ } while (!kthread_should_stop());
++
++ dev_dbg(ir->d.dev, LOGHEAD "poll thread ended\n",
++ ir->d.name, ir->d.minor);
++
++ return 0;
++}
++
++
++static struct file_operations fops = {
++ .owner = THIS_MODULE,
++ .read = lirc_dev_fop_read,
++ .write = lirc_dev_fop_write,
++ .poll = lirc_dev_fop_poll,
++ .ioctl = lirc_dev_fop_ioctl,
++#ifdef CONFIG_COMPAT
++ .compat_ioctl = lirc_dev_fop_compat_ioctl,
++#endif
++ .open = lirc_dev_fop_open,
++ .release = lirc_dev_fop_close,
++};
++
++static int lirc_cdev_add(struct irctl *ir)
++{
++ int retval;
++ struct lirc_driver *d = &ir->d;
++
++ if (d->fops) {
++ cdev_init(&ir->cdev, d->fops);
++ ir->cdev.owner = d->owner;
++ } else {
++ cdev_init(&ir->cdev, &fops);
++ ir->cdev.owner = THIS_MODULE;
++ }
++ kobject_set_name(&ir->cdev.kobj, "lirc%d", d->minor);
++
++ retval = cdev_add(&ir->cdev, MKDEV(MAJOR(lirc_base_dev), d->minor), 1);
++ if (retval)
++ kobject_put(&ir->cdev.kobj);
++
++ return retval;
++}
++
++int lirc_register_driver(struct lirc_driver *d)
++{
++ struct irctl *ir;
++ int minor;
++ int bytes_in_key;
++ unsigned int chunk_size;
++ unsigned int buffer_size;
++ int err;
++
++ if (!d) {
++ printk(KERN_ERR "lirc_dev: lirc_register_driver: "
++ "driver pointer must be not NULL!\n");
++ err = -EBADRQC;
++ goto out;
++ }
++
++ if (MAX_IRCTL_DEVICES <= d->minor) {
++ dev_err(d->dev, "lirc_dev: lirc_register_driver: "
++ "\"minor\" must be between 0 and %d (%d)!\n",
++ MAX_IRCTL_DEVICES-1, d->minor);
++ err = -EBADRQC;
++ goto out;
++ }
++
++ if (1 > d->code_length || (BUFLEN * 8) < d->code_length) {
++ dev_err(d->dev, "lirc_dev: lirc_register_driver: "
++ "code length in bits for minor (%d) "
++ "must be less than %d!\n",
++ d->minor, BUFLEN * 8);
++ err = -EBADRQC;
++ goto out;
++ }
++
++ dev_dbg(d->dev, "lirc_dev: lirc_register_driver: sample_rate: %d\n",
++ d->sample_rate);
++ if (d->sample_rate) {
++ if (2 > d->sample_rate || HZ < d->sample_rate) {
++ dev_err(d->dev, "lirc_dev: lirc_register_driver: "
++ "sample_rate must be between 2 and %d!\n", HZ);
++ err = -EBADRQC;
++ goto out;
++ }
++ if (!d->add_to_buf) {
++ dev_err(d->dev, "lirc_dev: lirc_register_driver: "
++ "add_to_buf cannot be NULL when "
++ "sample_rate is set\n");
++ err = -EBADRQC;
++ goto out;
++ }
++ } else if (!(d->fops && d->fops->read) && !d->rbuf) {
++ dev_err(d->dev, "lirc_dev: lirc_register_driver: "
++ "fops->read and rbuf cannot all be NULL!\n");
++ err = -EBADRQC;
++ goto out;
++ } else if (!d->rbuf) {
++ if (!(d->fops && d->fops->read && d->fops->poll &&
++ d->fops->ioctl)) {
++ dev_err(d->dev, "lirc_dev: lirc_register_driver: "
++ "neither read, poll nor ioctl can be NULL!\n");
++ err = -EBADRQC;
++ goto out;
++ }
++ }
++
++ mutex_lock(&lirc_dev_lock);
++
++ minor = d->minor;
++
++ if (minor < 0) {
++ /* find first free slot for driver */
++ for (minor = 0; minor < MAX_IRCTL_DEVICES; minor++)
++ if (!irctls[minor])
++ break;
++ if (MAX_IRCTL_DEVICES == minor) {
++ dev_err(d->dev, "lirc_dev: lirc_register_driver: "
++ "no free slots for drivers!\n");
++ err = -ENOMEM;
++ goto out_lock;
++ }
++ } else if (irctls[minor]) {
++ dev_err(d->dev, "lirc_dev: lirc_register_driver: "
++ "minor (%d) just registered!\n", minor);
++ err = -EBUSY;
++ goto out_lock;
++ }
++
++ ir = kzalloc(sizeof(struct irctl), GFP_KERNEL);
++ if (!ir) {
++ err = -ENOMEM;
++ goto out_lock;
++ }
++ init_irctl(ir);
++ irctls[minor] = ir;
++ d->minor = minor;
++
++ if (d->sample_rate) {
++ ir->jiffies_to_wait = HZ / d->sample_rate;
++ } else {
++ /* it means - wait for external event in task queue */
++ ir->jiffies_to_wait = 0;
++ }
++
++ /* some safety check 8-) */
++ d->name[sizeof(d->name)-1] = '\0';
++
++ bytes_in_key = BITS_TO_LONGS(d->code_length) +
++ (d->code_length % 8 ? 1 : 0);
++ buffer_size = d->buffer_size ? d->buffer_size : BUFLEN / bytes_in_key;
++ chunk_size = d->chunk_size ? d->chunk_size : bytes_in_key;
++
++ if (d->rbuf) {
++ ir->buf = d->rbuf;
++ } else {
++ ir->buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
++ if (!ir->buf) {
++ err = -ENOMEM;
++ goto out_lock;
++ }
++ err = lirc_buffer_init(ir->buf, chunk_size, buffer_size);
++ if (err) {
++ kfree(ir->buf);
++ goto out_lock;
++ }
++ }
++ ir->chunk_size = ir->buf->chunk_size;
++
++ if (d->features == 0)
++ d->features = LIRC_CAN_REC_LIRCCODE;
++
++ ir->d = *d;
++ ir->d.minor = minor;
++
++ device_create(lirc_class, ir->d.dev,
++ MKDEV(MAJOR(lirc_base_dev), ir->d.minor), NULL,
++ "lirc%u", ir->d.minor);
++
++ if (d->sample_rate) {
++ /* try to fire up polling thread */
++ ir->task = kthread_run(lirc_thread, (void *)ir, "lirc_dev");
++ if (IS_ERR(ir->task)) {
++ dev_err(d->dev, "lirc_dev: lirc_register_driver: "
++ "cannot run poll thread for minor = %d\n",
++ d->minor);
++ err = -ECHILD;
++ goto out_sysfs;
++ }
++ }
++
++ err = lirc_cdev_add(ir);
++ if (err)
++ goto out_sysfs;
++
++ ir->attached = 1;
++ mutex_unlock(&lirc_dev_lock);
++
++ dev_info(ir->d.dev, "lirc_dev: driver %s registered at minor = %d\n",
++ ir->d.name, ir->d.minor);
++ return minor;
++
++out_sysfs:
++ device_destroy(lirc_class, MKDEV(MAJOR(lirc_base_dev), ir->d.minor));
++out_lock:
++ mutex_unlock(&lirc_dev_lock);
++out:
++ return err;
++}
++EXPORT_SYMBOL(lirc_register_driver);
++
++int lirc_unregister_driver(int minor)
++{
++ struct irctl *ir;
++
++ if (minor < 0 || minor >= MAX_IRCTL_DEVICES) {
++ printk(KERN_ERR "lirc_dev: lirc_unregister_driver: "
++ "\"minor (%d)\" must be between 0 and %d!\n",
++ minor, MAX_IRCTL_DEVICES-1);
++ return -EBADRQC;
++ }
++
++ ir = irctls[minor];
++
++ mutex_lock(&lirc_dev_lock);
++
++ if (ir->d.minor != minor) {
++ printk(KERN_ERR "lirc_dev: lirc_unregister_driver: "
++ "minor (%d) device not registered!", minor);
++ mutex_unlock(&lirc_dev_lock);
++ return -ENOENT;
++ }
++
++ /* end up polling thread */
++ if (ir->task)
++ kthread_stop(ir->task);
++
++ dev_dbg(ir->d.dev, "lirc_dev: driver %s unregistered from minor = %d\n",
++ ir->d.name, ir->d.minor);
++
++ ir->attached = 0;
++ if (ir->open) {
++ dev_dbg(ir->d.dev, LOGHEAD "releasing opened driver\n",
++ ir->d.name, ir->d.minor);
++ wake_up_interruptible(&ir->buf->wait_poll);
++ mutex_lock(&ir->irctl_lock);
++ ir->d.set_use_dec(ir->d.data);
++ module_put(ir->d.owner);
++ mutex_unlock(&ir->irctl_lock);
++ cdev_del(&ir->cdev);
++ } else {
++ cleanup(ir);
++ cdev_del(&ir->cdev);
++ kfree(ir);
++ irctls[minor] = NULL;
++ }
++
++ mutex_unlock(&lirc_dev_lock);
++
++ return 0;
++}
++EXPORT_SYMBOL(lirc_unregister_driver);
++
++int lirc_dev_fop_open(struct inode *inode, struct file *file)
++{
++ struct irctl *ir;
++ int retval = 0;
++
++ if (iminor(inode) >= MAX_IRCTL_DEVICES) {
++ printk(KERN_WARNING "lirc_dev [%d]: open result = -ENODEV\n",
++ iminor(inode));
++ return -ENODEV;
++ }
++
++ if (mutex_lock_interruptible(&lirc_dev_lock))
++ return -ERESTARTSYS;
++
++ ir = irctls[iminor(inode)];
++ if (!ir) {
++ retval = -ENODEV;
++ goto error;
++ }
++
++ dev_dbg(ir->d.dev, LOGHEAD "open called\n", ir->d.name, ir->d.minor);
++
++ if (ir->d.minor == NOPLUG) {
++ retval = -ENODEV;
++ goto error;
++ }
++
++ if (ir->open) {
++ retval = -EBUSY;
++ goto error;
++ }
++
++ if (try_module_get(ir->d.owner)) {
++ ++ir->open;
++ retval = ir->d.set_use_inc(ir->d.data);
++
++ if (retval) {
++ module_put(ir->d.owner);
++ --ir->open;
++ } else {
++ lirc_buffer_clear(ir->buf);
++ }
++ if (ir->task)
++ wake_up_process(ir->task);
++ }
++
++error:
++ if (ir)
++ dev_dbg(ir->d.dev, LOGHEAD "open result = %d\n",
++ ir->d.name, ir->d.minor, retval);
++
++ mutex_unlock(&lirc_dev_lock);
++
++ return retval;
++}
++EXPORT_SYMBOL(lirc_dev_fop_open);
++
++int lirc_dev_fop_close(struct inode *inode, struct file *file)
++{
++ struct irctl *ir = irctls[iminor(inode)];
++
++ dev_dbg(ir->d.dev, LOGHEAD "close called\n", ir->d.name, ir->d.minor);
++
++ WARN_ON(mutex_lock_killable(&lirc_dev_lock));
++
++ --ir->open;
++ if (ir->attached) {
++ ir->d.set_use_dec(ir->d.data);
++ module_put(ir->d.owner);
++ } else {
++ cleanup(ir);
++ irctls[ir->d.minor] = NULL;
++ kfree(ir);
++ }
++
++ mutex_unlock(&lirc_dev_lock);
++
++ return 0;
++}
++EXPORT_SYMBOL(lirc_dev_fop_close);
++
++unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait)
++{
++ struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)];
++ unsigned int ret;
++
++ dev_dbg(ir->d.dev, LOGHEAD "poll called\n", ir->d.name, ir->d.minor);
++
++ if (!ir->attached) {
++ mutex_unlock(&ir->irctl_lock);
++ return POLLERR;
++ }
++
++ poll_wait(file, &ir->buf->wait_poll, wait);
++
++ if (ir->buf)
++ if (lirc_buffer_empty(ir->buf))
++ ret = 0;
++ else
++ ret = POLLIN | POLLRDNORM;
++ else
++ ret = POLLERR;
++
++ dev_dbg(ir->d.dev, LOGHEAD "poll result = %d\n",
++ ir->d.name, ir->d.minor, ret);
++
++ return ret;
++}
++EXPORT_SYMBOL(lirc_dev_fop_poll);
++
++int lirc_dev_fop_ioctl(struct inode *inode, struct file *file,
++ unsigned int cmd, unsigned long arg)
++{
++ unsigned long mode;
++ int result = 0;
++ struct irctl *ir = irctls[iminor(inode)];
++
++ dev_dbg(ir->d.dev, LOGHEAD "ioctl called (0x%x)\n",
++ ir->d.name, ir->d.minor, cmd);
++
++ if (ir->d.minor == NOPLUG || !ir->attached) {
++ dev_dbg(ir->d.dev, LOGHEAD "ioctl result = -ENODEV\n",
++ ir->d.name, ir->d.minor);
++ return -ENODEV;
++ }
++
++ switch (cmd) {
++ case LIRC_GET_FEATURES:
++ result = put_user(ir->d.features, (unsigned long *)arg);
++ break;
++ case LIRC_GET_REC_MODE:
++ if (!(ir->d.features & LIRC_CAN_REC_MASK))
++ return -ENOSYS;
++
++ result = put_user(LIRC_REC2MODE
++ (ir->d.features & LIRC_CAN_REC_MASK),
++ (unsigned long *)arg);
++ break;
++ case LIRC_SET_REC_MODE:
++ if (!(ir->d.features & LIRC_CAN_REC_MASK))
++ return -ENOSYS;
++
++ result = get_user(mode, (unsigned long *)arg);
++ if (!result && !(LIRC_MODE2REC(mode) & ir->d.features))
++ result = -EINVAL;
++ /*
++ * FIXME: We should actually set the mode somehow but
++ * for now, lirc_serial doesn't support mode changing either
++ */
++ break;
++ case LIRC_GET_LENGTH:
++ result = put_user(ir->d.code_length, (unsigned long *)arg);
++ break;
++ default:
++ result = -EINVAL;
++ }
++
++ dev_dbg(ir->d.dev, LOGHEAD "ioctl result = %d\n",
++ ir->d.name, ir->d.minor, result);
++
++ return result;
++}
++EXPORT_SYMBOL(lirc_dev_fop_ioctl);
++
++#ifdef CONFIG_COMPAT
++#define LIRC_GET_FEATURES_COMPAT32 _IOR('i', 0x00000000, __u32)
++
++#define LIRC_GET_SEND_MODE_COMPAT32 _IOR('i', 0x00000001, __u32)
++#define LIRC_GET_REC_MODE_COMPAT32 _IOR('i', 0x00000002, __u32)
++
++#define LIRC_GET_LENGTH_COMPAT32 _IOR('i', 0x0000000f, __u32)
++
++#define LIRC_SET_SEND_MODE_COMPAT32 _IOW('i', 0x00000011, __u32)
++#define LIRC_SET_REC_MODE_COMPAT32 _IOW('i', 0x00000012, __u32)
++
++long lirc_dev_fop_compat_ioctl(struct file *file,
++ unsigned int cmd32,
++ unsigned long arg)
++{
++ mm_segment_t old_fs;
++ int ret;
++ unsigned long val;
++ unsigned int cmd;
++
++ switch (cmd32) {
++ case LIRC_GET_FEATURES_COMPAT32:
++ case LIRC_GET_SEND_MODE_COMPAT32:
++ case LIRC_GET_REC_MODE_COMPAT32:
++ case LIRC_GET_LENGTH_COMPAT32:
++ case LIRC_SET_SEND_MODE_COMPAT32:
++ case LIRC_SET_REC_MODE_COMPAT32:
++ /*
++ * These commands expect (unsigned long *) arg
++ * but the 32-bit app supplied (__u32 *).
++ * Conversion is required.
++ */
++ if (get_user(val, (__u32 *)compat_ptr(arg)))
++ return -EFAULT;
++ lock_kernel();
++ /*
++ * tell lirc_dev_fop_ioctl that it's safe to use the pointer
++ * to val which is in kernel address space and not in
++ * user address space.
++ */
++ old_fs = get_fs();
++ set_fs(KERNEL_DS);
++
++ cmd = _IOC(_IOC_DIR(cmd32), _IOC_TYPE(cmd32), _IOC_NR(cmd32),
++ (_IOC_TYPECHECK(unsigned long)));
++ ret = lirc_dev_fop_ioctl(file->f_path.dentry->d_inode, file,
++ cmd, (unsigned long)(&val));
++
++ set_fs(old_fs);
++ unlock_kernel();
++ switch (cmd) {
++ case LIRC_GET_FEATURES:
++ case LIRC_GET_SEND_MODE:
++ case LIRC_GET_REC_MODE:
++ case LIRC_GET_LENGTH:
++ if (!ret && put_user(val, (__u32 *)compat_ptr(arg)))
++ return -EFAULT;
++ break;
++ }
++ return ret;
++
++ case LIRC_GET_SEND_CARRIER:
++ case LIRC_GET_REC_CARRIER:
++ case LIRC_GET_SEND_DUTY_CYCLE:
++ case LIRC_GET_REC_DUTY_CYCLE:
++ case LIRC_GET_REC_RESOLUTION:
++ case LIRC_SET_SEND_CARRIER:
++ case LIRC_SET_REC_CARRIER:
++ case LIRC_SET_SEND_DUTY_CYCLE:
++ case LIRC_SET_REC_DUTY_CYCLE:
++ case LIRC_SET_TRANSMITTER_MASK:
++ case LIRC_SET_REC_DUTY_CYCLE_RANGE:
++ case LIRC_SET_REC_CARRIER_RANGE:
++ /*
++ * These commands expect (unsigned int *)arg
++ * so no problems here. Just handle the locking.
++ */
++ lock_kernel();
++ cmd = cmd32;
++ ret = lirc_dev_fop_ioctl(file->f_path.dentry->d_inode,
++ file, cmd, arg);
++ unlock_kernel();
++ return ret;
++ default:
++ /* unknown */
++ printk(KERN_ERR "lirc_dev: %s(%s:%d): Unknown cmd %08x\n",
++ __func__, current->comm, current->pid, cmd32);
++ return -ENOIOCTLCMD;
++ }
++}
++EXPORT_SYMBOL(lirc_dev_fop_compat_ioctl);
++#endif
++
++
++ssize_t lirc_dev_fop_read(struct file *file,
++ char *buffer,
++ size_t length,
++ loff_t *ppos)
++{
++ struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)];
++ unsigned char buf[ir->chunk_size];
++ int ret = 0, written = 0;
++ DECLARE_WAITQUEUE(wait, current);
++
++ dev_dbg(ir->d.dev, LOGHEAD "read called\n", ir->d.name, ir->d.minor);
++
++ if (mutex_lock_interruptible(&ir->irctl_lock))
++ return -ERESTARTSYS;
++ if (!ir->attached) {
++ mutex_unlock(&ir->irctl_lock);
++ return -ENODEV;
++ }
++
++ if (length % ir->chunk_size) {
++ dev_dbg(ir->d.dev, LOGHEAD "read result = -EINVAL\n",
++ ir->d.name, ir->d.minor);
++ mutex_unlock(&ir->irctl_lock);
++ return -EINVAL;
++ }
++
++ /*
++ * we add ourselves to the task queue before buffer check
++ * to avoid losing scan code (in case when queue is awaken somewhere
++ * between while condition checking and scheduling)
++ */
++ add_wait_queue(&ir->buf->wait_poll, &wait);
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ /*
++ * while we didn't provide 'length' bytes, device is opened in blocking
++ * mode and 'copy_to_user' is happy, wait for data.
++ */
++ while (written < length && ret == 0) {
++ if (lirc_buffer_empty(ir->buf)) {
++ /* According to the read(2) man page, 'written' can be
++ * returned as less than 'length', instead of blocking
++ * again, returning -EWOULDBLOCK, or returning
++ * -ERESTARTSYS */
++ if (written)
++ break;
++ if (file->f_flags & O_NONBLOCK) {
++ ret = -EWOULDBLOCK;
++ break;
++ }
++ if (signal_pending(current)) {
++ ret = -ERESTARTSYS;
++ break;
++ }
++
++ mutex_unlock(&ir->irctl_lock);
++ schedule();
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ if (mutex_lock_interruptible(&ir->irctl_lock)) {
++ ret = -ERESTARTSYS;
++ break;
++ }
++
++ if (!ir->attached) {
++ ret = -ENODEV;
++ break;
++ }
++ } else {
++ lirc_buffer_read(ir->buf, buf);
++ ret = copy_to_user((void *)buffer+written, buf,
++ ir->buf->chunk_size);
++ written += ir->buf->chunk_size;
++ }
++ }
++
++ remove_wait_queue(&ir->buf->wait_poll, &wait);
++ set_current_state(TASK_RUNNING);
++ mutex_unlock(&ir->irctl_lock);
++
++ dev_dbg(ir->d.dev, LOGHEAD "read result = %s (%d)\n",
++ ir->d.name, ir->d.minor, ret ? "-EFAULT" : "OK", ret);
++
++ return ret ? ret : written;
++}
++EXPORT_SYMBOL(lirc_dev_fop_read);
++
++void *lirc_get_pdata(struct file *file)
++{
++ void *data = NULL;
++
++ if (file && file->f_dentry && file->f_dentry->d_inode &&
++ file->f_dentry->d_inode->i_rdev) {
++ struct irctl *ir;
++ ir = irctls[iminor(file->f_dentry->d_inode)];
++ data = ir->d.data;
++ }
++
++ return data;
++}
++EXPORT_SYMBOL(lirc_get_pdata);
++
++
++ssize_t lirc_dev_fop_write(struct file *file, const char *buffer,
++ size_t length, loff_t *ppos)
++{
++ struct irctl *ir = irctls[iminor(file->f_dentry->d_inode)];
++
++ dev_dbg(ir->d.dev, LOGHEAD "write called\n", ir->d.name, ir->d.minor);
++
++ if (!ir->attached)
++ return -ENODEV;
++
++ return -EINVAL;
++}
++EXPORT_SYMBOL(lirc_dev_fop_write);
++
++
++static int __init lirc_dev_init(void)
++{
++ int retval;
++
++ lirc_class = class_create(THIS_MODULE, "lirc");
++ if (IS_ERR(lirc_class)) {
++ retval = PTR_ERR(lirc_class);
++ printk(KERN_ERR "lirc_dev: class_create failed\n");
++ goto error;
++ }
++
++ retval = alloc_chrdev_region(&lirc_base_dev, 0, MAX_IRCTL_DEVICES,
++ IRCTL_DEV_NAME);
++ if (retval) {
++ class_destroy(lirc_class);
++ printk(KERN_ERR "lirc_dev: alloc_chrdev_region failed\n");
++ goto error;
++ }
++
++
++ printk(KERN_INFO "lirc_dev: IR Remote Control driver registered, "
++ "major %d \n", MAJOR(lirc_base_dev));
++
++error:
++ return retval;
++}
++
++
++
++static void __exit lirc_dev_exit(void)
++{
++ class_destroy(lirc_class);
++ unregister_chrdev_region(lirc_base_dev, MAX_IRCTL_DEVICES);
++ printk(KERN_INFO "lirc_dev: module unloaded\n");
++}
++
++module_init(lirc_dev_init);
++module_exit(lirc_dev_exit);
++
++MODULE_DESCRIPTION("LIRC base driver module");
++MODULE_AUTHOR("Artur Lipowski");
++MODULE_LICENSE("GPL");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
+diff --git a/drivers/input/lirc/lirc_dev.h b/drivers/input/lirc/lirc_dev.h
+new file mode 100644
+index 0000000..06a1a47
+--- /dev/null
++++ b/drivers/input/lirc/lirc_dev.h
+@@ -0,0 +1,194 @@
++/*
++ * LIRC base driver
++ *
++ * by Artur Lipowski <alipowski@interia.pl>
++ * This code is licensed under GNU GPL
++ *
++ */
++
++#ifndef _LINUX_LIRC_DEV_H
++#define _LINUX_LIRC_DEV_H
++
++#define MAX_IRCTL_DEVICES 4
++#define BUFLEN 16
++
++#define mod(n, div) ((n) % (div))
++
++#include <linux/slab.h>
++#include <linux/fs.h>
++#include <linux/ioctl.h>
++#include <linux/poll.h>
++#include <linux/kfifo.h>
++
++struct lirc_buffer {
++ wait_queue_head_t wait_poll;
++ spinlock_t buffer_lock;
++ unsigned int chunk_size;
++ unsigned int size; /* in chunks */
++ /* Using chunks instead of bytes pretends to simplify boundary checking
++ * And should allow for some performance fine tunning later */
++ struct kfifo *fifo;
++};
++
++static inline void lirc_buffer_clear(struct lirc_buffer *buf)
++{
++ if (buf->fifo)
++ kfifo_reset(buf->fifo);
++ else
++ WARN(1, "calling %s on an uninitialized lirc_buffer\n",
++ __func__);
++}
++
++static inline int lirc_buffer_init(struct lirc_buffer *buf,
++ unsigned int chunk_size,
++ unsigned int size)
++{
++ init_waitqueue_head(&buf->wait_poll);
++ spin_lock_init(&buf->buffer_lock);
++ buf->chunk_size = chunk_size;
++ buf->size = size;
++ buf->fifo = kfifo_alloc(size * chunk_size, GFP_KERNEL, &buf->buffer_lock);
++ if (!buf->fifo)
++ return -ENOMEM;
++ return 0;
++}
++
++static inline void lirc_buffer_free(struct lirc_buffer *buf)
++{
++ if (buf->fifo)
++ kfifo_free(buf->fifo);
++ else
++ WARN(1, "calling %s on an uninitialized lirc_buffer\n",
++ __func__);
++}
++
++static inline int lirc_buffer_full(struct lirc_buffer *buf)
++{
++ return kfifo_len(buf->fifo) == buf->size * buf->chunk_size;
++}
++
++static inline int lirc_buffer_empty(struct lirc_buffer *buf)
++{
++ return !kfifo_len(buf->fifo);
++}
++
++static inline int lirc_buffer_available(struct lirc_buffer *buf)
++{
++ return buf->size - (kfifo_len(buf->fifo) / buf->chunk_size);
++}
++
++static inline void lirc_buffer_read(struct lirc_buffer *buf,
++ unsigned char *dest)
++{
++ if (kfifo_len(buf->fifo) >= buf->chunk_size)
++ kfifo_get(buf->fifo, dest, buf->chunk_size);
++}
++
++static inline void lirc_buffer_write(struct lirc_buffer *buf,
++ unsigned char *orig)
++{
++ kfifo_put(buf->fifo, orig, buf->chunk_size);
++}
++
++struct lirc_driver {
++ char name[40];
++ int minor;
++ unsigned long code_length;
++ unsigned int buffer_size; /* in chunks holding one code each */
++ int sample_rate;
++ unsigned long features;
++
++ unsigned int chunk_size;
++
++ void *data;
++ int (*add_to_buf) (void *data, struct lirc_buffer *buf);
++ struct lirc_buffer *rbuf;
++ int (*set_use_inc) (void *data);
++ void (*set_use_dec) (void *data);
++ struct file_operations *fops;
++ struct device *dev;
++ struct module *owner;
++};
++
++/* name:
++ * this string will be used for logs
++ *
++ * minor:
++ * indicates minor device (/dev/lirc) number for registered driver
++ * if caller fills it with negative value, then the first free minor
++ * number will be used (if available)
++ *
++ * code_length:
++ * length of the remote control key code expressed in bits
++ *
++ * sample_rate:
++ *
++ * data:
++ * it may point to any driver data and this pointer will be passed to
++ * all callback functions
++ *
++ * add_to_buf:
++ * add_to_buf will be called after specified period of the time or
++ * triggered by the external event, this behavior depends on value of
++ * the sample_rate this function will be called in user context. This
++ * routine should return 0 if data was added to the buffer and
++ * -ENODATA if none was available. This should add some number of bits
++ * evenly divisible by code_length to the buffer
++ *
++ * rbuf:
++ * if not NULL, it will be used as a read buffer, you will have to
++ * write to the buffer by other means, like irq's (see also
++ * lirc_serial.c).
++ *
++ * set_use_inc:
++ * set_use_inc will be called after device is opened
++ *
++ * set_use_dec:
++ * set_use_dec will be called after device is closed
++ *
++ * fops:
++ * file_operations for drivers which don't fit the current driver model.
++ *
++ * Some ioctl's can be directly handled by lirc_dev if the driver's
++ * ioctl function is NULL or if it returns -ENOIOCTLCMD (see also
++ * lirc_serial.c).
++ *
++ * owner:
++ * the module owning this struct
++ *
++ */
++
++
++/* following functions can be called ONLY from user context
++ *
++ * returns negative value on error or minor number
++ * of the registered device if success
++ * contents of the structure pointed by p is copied
++ */
++extern int lirc_register_driver(struct lirc_driver *d);
++
++/* returns negative value on error or 0 if success
++*/
++extern int lirc_unregister_driver(int minor);
++
++/* Returns the private data stored in the lirc_driver
++ * associated with the given device file pointer.
++ */
++void *lirc_get_pdata(struct file *file);
++
++/* default file operations
++ * used by drivers if they override only some operations
++ */
++int lirc_dev_fop_open(struct inode *inode, struct file *file);
++int lirc_dev_fop_close(struct inode *inode, struct file *file);
++unsigned int lirc_dev_fop_poll(struct file *file, poll_table *wait);
++int lirc_dev_fop_ioctl(struct inode *inode, struct file *file,
++ unsigned int cmd, unsigned long arg);
++ssize_t lirc_dev_fop_read(struct file *file, char *buffer, size_t length,
++ loff_t *ppos);
++ssize_t lirc_dev_fop_write(struct file *file, const char *buffer, size_t length,
++ loff_t *ppos);
++long lirc_dev_fop_compat_ioctl(struct file *file, unsigned int cmd32,
++ unsigned long arg);
++
++#endif
+diff --git a/drivers/input/lirc/lirc_ene0100.c b/drivers/input/lirc/lirc_ene0100.c
+new file mode 100644
+index 0000000..a152c52
+--- /dev/null
++++ b/drivers/input/lirc/lirc_ene0100.c
+@@ -0,0 +1,646 @@
++/*
++ * driver for ENE KB3926 B/C/D CIR (also known as ENE0100)
++ *
++ * Copyright (C) 2009 Maxim Levitsky <maximlevitsky@gmail.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
++ * USA
++ */
++
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/pnp.h>
++#include <linux/io.h>
++#include <linux/interrupt.h>
++#include <linux/sched.h>
++#include "lirc_ene0100.h"
++
++static int sample_period = 75;
++static int enable_idle = 1;
++static int enable_learning;
++
++static void ene_set_idle(struct ene_device *dev, int idle);
++static void ene_set_inputs(struct ene_device *dev, int enable);
++
++/* read a hardware register */
++static u8 ene_hw_read_reg(struct ene_device *dev, u16 reg)
++{
++ outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
++ outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
++ return inb(dev->hw_io + ENE_IO);
++}
++
++/* write a hardware register */
++static void ene_hw_write_reg(struct ene_device *dev, u16 reg, u8 value)
++{
++ outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
++ outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
++ outb(value, dev->hw_io + ENE_IO);
++}
++
++/* change specific bits in hardware register */
++static void ene_hw_write_reg_mask(struct ene_device *dev,
++ u16 reg, u8 value, u8 mask)
++{
++ u8 regvalue;
++
++ outb(reg >> 8, dev->hw_io + ENE_ADDR_HI);
++ outb(reg & 0xFF, dev->hw_io + ENE_ADDR_LO);
++
++ regvalue = inb(dev->hw_io + ENE_IO) & ~mask;
++ regvalue |= (value & mask);
++ outb(regvalue, dev->hw_io + ENE_IO);
++}
++
++/* read irq status and ack it */
++static int ene_hw_irq_status(struct ene_device *dev, int *buffer_pointer)
++{
++ u8 irq_status;
++ u8 fw_flags1, fw_flags2;
++
++ fw_flags2 = ene_hw_read_reg(dev, ENE_FW2);
++
++ if (buffer_pointer)
++ *buffer_pointer = 4 * (fw_flags2 & ENE_FW2_BUF_HIGH);
++
++ if (dev->hw_revision < ENE_HW_C) {
++ irq_status = ene_hw_read_reg(dev, ENEB_IRQ_STATUS);
++
++ if (!(irq_status & ENEB_IRQ_STATUS_IR))
++ return 0;
++ ene_hw_write_reg(dev, ENEB_IRQ_STATUS,
++ irq_status & ~ENEB_IRQ_STATUS_IR);
++
++ /* rev B support only recieving */
++ return ENE_IRQ_RX;
++ }
++
++ irq_status = ene_hw_read_reg(dev, ENEC_IRQ);
++
++ if (!(irq_status & ENEC_IRQ_STATUS))
++ return 0;
++
++ /* original driver does that twice - a workaround ? */
++ ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
++ ene_hw_write_reg(dev, ENEC_IRQ, irq_status & ~ENEC_IRQ_STATUS);
++
++ /* clear unknown flag in F8F9 */
++ if (fw_flags2 & ENE_FW2_IRQ_CLR)
++ ene_hw_write_reg(dev, ENE_FW2, fw_flags2 & ~ENE_FW2_IRQ_CLR);
++
++ /* check if this is a TX interrupt */
++ fw_flags1 = ene_hw_read_reg(dev, ENE_FW1);
++
++ if (fw_flags1 & ENE_FW1_TXIRQ) {
++ ene_hw_write_reg(dev, ENE_FW1, fw_flags1 & ~ENE_FW1_TXIRQ);
++ return ENE_IRQ_TX;
++ } else
++ return ENE_IRQ_RX;
++}
++
++static int ene_hw_detect(struct ene_device *dev)
++{
++ u8 chip_major, chip_minor;
++ u8 hw_revision, old_ver;
++ u8 tmp;
++ u8 fw_capabilities;
++
++ tmp = ene_hw_read_reg(dev, ENE_HW_UNK);
++ ene_hw_write_reg(dev, ENE_HW_UNK, tmp & ~ENE_HW_UNK_CLR);
++
++ chip_major = ene_hw_read_reg(dev, ENE_HW_VER_MAJOR);
++ chip_minor = ene_hw_read_reg(dev, ENE_HW_VER_MINOR);
++
++ ene_hw_write_reg(dev, ENE_HW_UNK, tmp);
++ hw_revision = ene_hw_read_reg(dev, ENE_HW_VERSION);
++ old_ver = ene_hw_read_reg(dev, ENE_HW_VER_OLD);
++
++ if (hw_revision == 0xFF) {
++
++ ene_printk(KERN_WARNING, "device seems to be disabled\n");
++ ene_printk(KERN_WARNING,
++ "send a mail to lirc-list@lists.sourceforge.net\n");
++ ene_printk(KERN_WARNING, "please attach output of acpidump\n");
++
++ return -ENODEV;
++ }
++
++ if (chip_major == 0x33) {
++ ene_printk(KERN_WARNING, "chips 0x33xx aren't supported yet\n");
++ return -ENODEV;
++ }
++
++ if (chip_major == 0x39 && chip_minor == 0x26 && hw_revision == 0xC0) {
++ dev->hw_revision = ENE_HW_C;
++ ene_printk(KERN_WARNING,
++ "KB3926C detected, driver support is not complete!\n");
++
++ } else if (old_ver == 0x24 && hw_revision == 0xC0) {
++ dev->hw_revision = ENE_HW_B;
++ ene_printk(KERN_NOTICE, "KB3926B detected\n");
++ } else {
++ dev->hw_revision = ENE_HW_D;
++ ene_printk(KERN_WARNING,
++ "unknown ENE chip detected, assuming KB3926D\n");
++ ene_printk(KERN_WARNING, "driver support incomplete");
++
++ }
++
++ ene_printk(KERN_DEBUG, "chip is 0x%02x%02x - 0x%02x, 0x%02x\n",
++ chip_major, chip_minor, old_ver, hw_revision);
++
++
++ /* detect features hardware supports */
++
++ if (dev->hw_revision < ENE_HW_C)
++ return 0;
++
++ fw_capabilities = ene_hw_read_reg(dev, ENE_FW2);
++
++ dev->hw_gpio40_learning = fw_capabilities & ENE_FW2_GP40_AS_LEARN;
++ dev->hw_learning_and_tx_capable = fw_capabilities & ENE_FW2_LEARNING;
++
++ dev->hw_fan_as_normal_input = dev->hw_learning_and_tx_capable &&
++ fw_capabilities & ENE_FW2_FAN_AS_NRML_IN;
++
++ ene_printk(KERN_NOTICE, "hardware features:\n");
++ ene_printk(KERN_NOTICE,
++ "learning and tx %s, gpio40_learn %s, fan_in %s\n",
++ dev->hw_learning_and_tx_capable ? "on" : "off",
++ dev->hw_gpio40_learning ? "on" : "off",
++ dev->hw_fan_as_normal_input ? "on" : "off");
++
++ if (!dev->hw_learning_and_tx_capable && enable_learning)
++ enable_learning = 0;
++
++ if (dev->hw_learning_and_tx_capable) {
++ ene_printk(KERN_WARNING,
++ "Device supports transmitting, but the driver doesn't\n");
++ ene_printk(KERN_WARNING,
++ "due to lack of hardware to test against.\n");
++ ene_printk(KERN_WARNING,
++ "Send a mail to: lirc-list@lists.sourceforge.net\n");
++ }
++ return 0;
++}
++
++/* hardware initialization */
++static int ene_hw_init(void *data)
++{
++ u8 reg_value;
++ struct ene_device *dev = (struct ene_device *)data;
++ dev->in_use = 1;
++
++ if (dev->hw_revision < ENE_HW_C) {
++ ene_hw_write_reg(dev, ENEB_IRQ, dev->irq << 1);
++ ene_hw_write_reg(dev, ENEB_IRQ_UNK1, 0x01);
++ } else {
++ reg_value = ene_hw_read_reg(dev, ENEC_IRQ) & 0xF0;
++ reg_value |= ENEC_IRQ_UNK_EN;
++ reg_value &= ~ENEC_IRQ_STATUS;
++ reg_value |= (dev->irq & ENEC_IRQ_MASK);
++ ene_hw_write_reg(dev, ENEC_IRQ, reg_value);
++ ene_hw_write_reg(dev, ENE_TX_UNK1, 0x63);
++ }
++
++ ene_hw_write_reg(dev, ENE_CIR_CONF2, 0x00);
++ ene_set_inputs(dev, enable_learning);
++
++ /* set sampling period */
++ ene_hw_write_reg(dev, ENE_CIR_SAMPLE_PERIOD, sample_period);
++
++ /* ack any pending irqs - just in case */
++ ene_hw_irq_status(dev, NULL);
++
++ /* enter idle mode */
++ ene_set_idle(dev, 1);
++
++ /* enable firmware bits */
++ ene_hw_write_reg_mask(dev, ENE_FW1,
++ ENE_FW1_ENABLE | ENE_FW1_IRQ,
++ ENE_FW1_ENABLE | ENE_FW1_IRQ);
++ /* clear stats */
++ dev->sample = 0;
++ return 0;
++}
++
++/* this enables gpio40 signal, used if connected to wide band input*/
++static void ene_enable_gpio40(struct ene_device *dev, int enable)
++{
++ ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, enable ?
++ 0 : ENE_CIR_CONF2_GPIO40DIS,
++ ENE_CIR_CONF2_GPIO40DIS);
++}
++
++/* this enables the classic sampler */
++static void ene_enable_normal_recieve(struct ene_device *dev, int enable)
++{
++ ene_hw_write_reg(dev, ENE_CIR_CONF1, enable ? ENE_CIR_CONF1_ADC_ON : 0);
++}
++
++/* this enables recieve via fan input */
++static void ene_enable_fan_recieve(struct ene_device *dev, int enable)
++{
++ if (!enable)
++ ene_hw_write_reg(dev, ENE_FAN_AS_IN1, 0);
++ else {
++ ene_hw_write_reg(dev, ENE_FAN_AS_IN1, ENE_FAN_AS_IN1_EN);
++ ene_hw_write_reg(dev, ENE_FAN_AS_IN2, ENE_FAN_AS_IN2_EN);
++ }
++ dev->fan_input_inuse = enable;
++}
++
++/* determine which input to use*/
++static void ene_set_inputs(struct ene_device *dev, int learning_enable)
++{
++ ene_enable_normal_recieve(dev, 1);
++
++ /* old hardware doesn't support learning mode for sure */
++ if (dev->hw_revision <= ENE_HW_B)
++ return;
++
++ /* reciever not learning capable, still set gpio40 correctly */
++ if (!dev->hw_learning_and_tx_capable) {
++ ene_enable_gpio40(dev, !dev->hw_gpio40_learning);
++ return;
++ }
++
++ /* enable learning mode */
++ if (learning_enable) {
++ ene_enable_gpio40(dev, dev->hw_gpio40_learning);
++
++ /* fan input is not used for learning */
++ if (dev->hw_fan_as_normal_input)
++ ene_enable_fan_recieve(dev, 0);
++
++ /* disable learning mode */
++ } else {
++ if (dev->hw_fan_as_normal_input) {
++ ene_enable_fan_recieve(dev, 1);
++ ene_enable_normal_recieve(dev, 0);
++ } else
++ ene_enable_gpio40(dev, !dev->hw_gpio40_learning);
++ }
++
++ /* set few additional settings for this mode */
++ ene_hw_write_reg_mask(dev, ENE_CIR_CONF1, learning_enable ?
++ ENE_CIR_CONF1_LEARN1 : 0, ENE_CIR_CONF1_LEARN1);
++
++ ene_hw_write_reg_mask(dev, ENE_CIR_CONF2, learning_enable ?
++ ENE_CIR_CONF2_LEARN2 : 0, ENE_CIR_CONF2_LEARN2);
++}
++
++/* deinitialization */
++static void ene_hw_deinit(void *data)
++{
++ struct ene_device *dev = (struct ene_device *)data;
++
++ /* disable samplers */
++ ene_enable_normal_recieve(dev, 0);
++
++ if (dev->hw_fan_as_normal_input)
++ ene_enable_fan_recieve(dev, 0);
++
++ /* disable hardware IRQ and firmware flag */
++ ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_ENABLE | ENE_FW1_IRQ);
++
++ ene_set_idle(dev, 1);
++ dev->in_use = 0;
++}
++
++/* sends current sample to userspace */
++static void send_sample(struct ene_device *dev)
++{
++ int value = abs(dev->sample) & PULSE_MASK;
++
++ if (dev->sample > 0)
++ value |= PULSE_BIT;
++
++ if (!lirc_buffer_full(dev->lirc_driver->rbuf)) {
++ lirc_buffer_write(dev->lirc_driver->rbuf, (void *)&value);
++ wake_up(&dev->lirc_driver->rbuf->wait_poll);
++ }
++ dev->sample = 0;
++}
++
++/* this updates current sample */
++static void update_sample(struct ene_device *dev, int sample)
++{
++ if (!dev->sample)
++ dev->sample = sample;
++ else if (same_sign(dev->sample, sample))
++ dev->sample += sample;
++ else {
++ send_sample(dev);
++ dev->sample = sample;
++ }
++}
++
++/* enable or disable idle mode */
++static void ene_set_idle(struct ene_device *dev, int idle)
++{
++ struct timeval now;
++ int disable = idle && enable_idle && (dev->hw_revision < ENE_HW_C);
++
++ ene_hw_write_reg_mask(dev, ENE_CIR_SAMPLE_PERIOD,
++ disable ? 0 : ENE_CIR_SAMPLE_OVERFLOW,
++ ENE_CIR_SAMPLE_OVERFLOW);
++ dev->idle = idle;
++
++ /* remember when we have entered the idle mode */
++ if (idle) {
++ do_gettimeofday(&dev->gap_start);
++ return;
++ }
++
++ /* send the gap between keypresses now */
++ do_gettimeofday(&now);
++
++ if (now.tv_sec - dev->gap_start.tv_sec > 16)
++ dev->sample = space(PULSE_MASK);
++ else
++ dev->sample = dev->sample +
++ space(1000000ull * (now.tv_sec - dev->gap_start.tv_sec))
++ + space(now.tv_usec - dev->gap_start.tv_usec);
++
++ if (abs(dev->sample) > PULSE_MASK)
++ dev->sample = space(PULSE_MASK);
++ send_sample(dev);
++}
++
++/* interrupt handler */
++static irqreturn_t ene_hw_irq(int irq, void *data)
++{
++ u16 hw_value;
++ int i, hw_sample;
++ int space;
++ int buffer_pointer;
++ int irq_status;
++
++ struct ene_device *dev = (struct ene_device *)data;
++ irq_status = ene_hw_irq_status(dev, &buffer_pointer);
++
++ if (!irq_status)
++ return IRQ_NONE;
++
++ /* TODO: only RX for now */
++ if (irq_status == ENE_IRQ_TX)
++ return IRQ_HANDLED;
++
++ for (i = 0; i < ENE_SAMPLES_SIZE; i++) {
++
++ hw_value = ene_hw_read_reg(dev,
++ ENE_SAMPLE_BUFFER + buffer_pointer + i);
++
++ if (dev->fan_input_inuse) {
++ /* read high part of the sample */
++ hw_value |= ene_hw_read_reg(dev,
++ ENE_SAMPLE_BUFFER_FAN + buffer_pointer + i) << 8;
++
++ /* test for _space_ bit */
++ space = !(hw_value & ENE_FAN_SMPL_PULS_MSK);
++
++ /* clear space bit, and other unused bits */
++ hw_value &= ENE_FAN_VALUE_MASK;
++ hw_sample = hw_value * ENE_SAMPLE_PERIOD_FAN;
++
++ } else {
++ space = hw_value & ENE_SAMPLE_SPC_MASK;
++ hw_value &= ENE_SAMPLE_VALUE_MASK;
++ hw_sample = hw_value * sample_period;
++ }
++
++ /* no more data */
++ if (!(hw_value))
++ break;
++
++ if (space)
++ hw_sample *= -1;
++
++ /* overflow sample recieved, handle it */
++
++ if (!dev->fan_input_inuse && hw_value == ENE_SAMPLE_OVERFLOW) {
++
++ if (dev->idle)
++ continue;
++
++ if (dev->sample > 0 || abs(dev->sample) <= ENE_MAXGAP)
++ update_sample(dev, hw_sample);
++ else
++ ene_set_idle(dev, 1);
++
++ continue;
++ }
++
++ /* normal first sample recieved */
++ if (!dev->fan_input_inuse && dev->idle) {
++ ene_set_idle(dev, 0);
++
++ /* discard first recieved value, its random
++ since its the time signal was off before
++ first pulse if idle mode is enabled, HW
++ does that for us */
++
++ if (!enable_idle)
++ continue;
++ }
++ update_sample(dev, hw_sample);
++ send_sample(dev);
++ }
++ return IRQ_HANDLED;
++}
++
++static int ene_probe(struct pnp_dev *pnp_dev,
++ const struct pnp_device_id *dev_id)
++{
++ struct ene_device *dev;
++ struct lirc_driver *lirc_driver;
++ int error = -ENOMEM;
++
++ dev = kzalloc(sizeof(struct ene_device), GFP_KERNEL);
++
++ if (!dev)
++ goto err1;
++
++ dev->pnp_dev = pnp_dev;
++ pnp_set_drvdata(pnp_dev, dev);
++
++
++ /* prepare lirc interface */
++ error = -ENOMEM;
++ lirc_driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
++
++ if (!lirc_driver)
++ goto err2;
++
++ dev->lirc_driver = lirc_driver;
++
++ strcpy(lirc_driver->name, ENE_DRIVER_NAME);
++ lirc_driver->minor = -1;
++ lirc_driver->code_length = sizeof(int) * 8;
++ lirc_driver->features = LIRC_CAN_REC_MODE2;
++ lirc_driver->data = dev;
++ lirc_driver->set_use_inc = ene_hw_init;
++ lirc_driver->set_use_dec = ene_hw_deinit;
++ lirc_driver->dev = &pnp_dev->dev;
++ lirc_driver->owner = THIS_MODULE;
++
++ lirc_driver->rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
++
++ if (!lirc_driver->rbuf)
++ goto err3;
++
++ if (lirc_buffer_init(lirc_driver->rbuf, sizeof(int), sizeof(int) * 256))
++ goto err4;
++
++ error = -ENODEV;
++ if (lirc_register_driver(lirc_driver))
++ goto err5;
++
++ /* validate resources */
++ if (!pnp_port_valid(pnp_dev, 0) ||
++ pnp_port_len(pnp_dev, 0) < ENE_MAX_IO)
++ goto err6;
++
++ if (!pnp_irq_valid(pnp_dev, 0))
++ goto err6;
++
++ dev->hw_io = pnp_port_start(pnp_dev, 0);
++ dev->irq = pnp_irq(pnp_dev, 0);
++
++ /* claim the resources */
++ error = -EBUSY;
++ if (!request_region(dev->hw_io, ENE_MAX_IO, ENE_DRIVER_NAME))
++ goto err6;
++
++ if (request_irq(dev->irq, ene_hw_irq,
++ IRQF_SHARED, ENE_DRIVER_NAME, (void *)dev))
++ goto err7;
++
++ /* detect hardware version and features */
++ error = ene_hw_detect(dev);
++ if (error)
++ goto err8;
++
++ ene_printk(KERN_NOTICE, "driver has been succesfully loaded\n");
++ return 0;
++
++err8:
++ free_irq(dev->irq, dev);
++err7:
++ release_region(dev->hw_io, ENE_MAX_IO);
++err6:
++ lirc_unregister_driver(lirc_driver->minor);
++err5:
++ lirc_buffer_free(lirc_driver->rbuf);
++err4:
++ kfree(lirc_driver->rbuf);
++err3:
++ kfree(lirc_driver);
++err2:
++ kfree(dev);
++err1:
++ return error;
++}
++
++static void ene_remove(struct pnp_dev *pnp_dev)
++{
++ struct ene_device *dev = pnp_get_drvdata(pnp_dev);
++ ene_hw_deinit(dev);
++ free_irq(dev->irq, dev);
++ release_region(dev->hw_io, ENE_MAX_IO);
++ lirc_unregister_driver(dev->lirc_driver->minor);
++ lirc_buffer_free(dev->lirc_driver->rbuf);
++ kfree(dev->lirc_driver);
++ kfree(dev);
++}
++
++#ifdef CONFIG_PM
++
++/* TODO: make 'wake on IR' configurable and add .shutdown */
++/* currently impossible due to lack of kernel support */
++
++static int ene_suspend(struct pnp_dev *pnp_dev, pm_message_t state)
++{
++ struct ene_device *dev = pnp_get_drvdata(pnp_dev);
++ ene_hw_write_reg_mask(dev, ENE_FW1, ENE_FW1_WAKE, ENE_FW1_WAKE);
++ return 0;
++}
++
++static int ene_resume(struct pnp_dev *pnp_dev)
++{
++ struct ene_device *dev = pnp_get_drvdata(pnp_dev);
++ if (dev->in_use)
++ ene_hw_init(dev);
++
++ ene_hw_write_reg_mask(dev, ENE_FW1, 0, ENE_FW1_WAKE);
++ return 0;
++}
++
++#endif
++
++static const struct pnp_device_id ene_ids[] = {
++ {.id = "ENE0100",},
++ {},
++};
++
++static struct pnp_driver ene_driver = {
++ .name = ENE_DRIVER_NAME,
++ .id_table = ene_ids,
++ .flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
++
++ .probe = ene_probe,
++ .remove = __devexit_p(ene_remove),
++
++#ifdef CONFIG_PM
++ .suspend = ene_suspend,
++ .resume = ene_resume,
++#endif
++};
++
++static int __init ene_init(void)
++{
++ if (sample_period < 5) {
++ ene_printk(KERN_ERR, "sample period must be at\n");
++ ene_printk(KERN_ERR, "least 5 us, (at least 30 recommended)\n");
++ return -EINVAL;
++ }
++ return pnp_register_driver(&ene_driver);
++}
++
++static void ene_exit(void)
++{
++ pnp_unregister_driver(&ene_driver);
++}
++
++module_param(sample_period, int, S_IRUGO);
++MODULE_PARM_DESC(sample_period, "Hardware sample period (75 us default)");
++
++module_param(enable_idle, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(enable_idle,
++ "Enables turning off signal sampling after long inactivity time; "
++ "if disabled might help detecting input signal (default: enabled)");
++
++module_param(enable_learning, bool, S_IRUGO);
++MODULE_PARM_DESC(enable_learning, "Use wide band (learning) reciever");
++
++MODULE_DEVICE_TABLE(pnp, ene_ids);
++MODULE_DESCRIPTION
++ ("LIRC driver for KB3926B/KB3926C/KB3926D (aka ENE0100) CIR port");
++MODULE_AUTHOR("Maxim Levitsky");
++MODULE_LICENSE("GPL");
++
++module_init(ene_init);
++module_exit(ene_exit);
+diff --git a/drivers/input/lirc/lirc_ene0100.h b/drivers/input/lirc/lirc_ene0100.h
+new file mode 100644
+index 0000000..953e7e4
+--- /dev/null
++++ b/drivers/input/lirc/lirc_ene0100.h
+@@ -0,0 +1,169 @@
++/*
++ * driver for ENE KB3926 B/C/D CIR (also known as ENE0100)
++ *
++ * Copyright (C) 2009 Maxim Levitsky <maximlevitsky@gmail.com>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
++ * USA
++ */
++
++#include <linux/lirc.h>
++#include "lirc_dev.h"
++
++/* hardware address */
++#define ENE_STATUS 0 /* hardware status - unused */
++#define ENE_ADDR_HI 1 /* hi byte of register address */
++#define ENE_ADDR_LO 2 /* low byte of register address */
++#define ENE_IO 3 /* read/write window */
++#define ENE_MAX_IO 4
++
++/* 8 bytes of samples, divided in 2 halfs*/
++#define ENE_SAMPLE_BUFFER 0xF8F0 /* regular sample buffer */
++#define ENE_SAMPLE_SPC_MASK (1 << 7) /* sample is space */
++#define ENE_SAMPLE_VALUE_MASK 0x7F
++#define ENE_SAMPLE_OVERFLOW 0x7F
++#define ENE_SAMPLES_SIZE 4
++
++/* fan input sample buffer */
++#define ENE_SAMPLE_BUFFER_FAN 0xF8FB /* this buffer holds high byte of */
++ /* each sample of normal buffer */
++
++#define ENE_FAN_SMPL_PULS_MSK 0x8000 /* this bit of combined sample */
++ /* if set, says that sample is pulse */
++#define ENE_FAN_VALUE_MASK 0x0FFF /* mask for valid bits of the value */
++
++/* first firmware register */
++#define ENE_FW1 0xF8F8
++#define ENE_FW1_ENABLE (1 << 0) /* enable fw processing */
++#define ENE_FW1_TXIRQ (1 << 1) /* TX interrupt pending */
++#define ENE_FW1_WAKE (1 << 6) /* enable wake from S3 */
++#define ENE_FW1_IRQ (1 << 7) /* enable interrupt */
++
++/* second firmware register */
++#define ENE_FW2 0xF8F9
++#define ENE_FW2_BUF_HIGH (1 << 0) /* which half of the buffer to read */
++#define ENE_FW2_IRQ_CLR (1 << 2) /* clear this on IRQ */
++#define ENE_FW2_GP40_AS_LEARN (1 << 4) /* normal input is used as */
++ /* learning input */
++#define ENE_FW2_FAN_AS_NRML_IN (1 << 6) /* fan is used as normal input */
++#define ENE_FW2_LEARNING (1 << 7) /* hardware supports learning and TX */
++
++/* fan as input settings - only if learning capable */
++#define ENE_FAN_AS_IN1 0xFE30 /* fan init reg 1 */
++#define ENE_FAN_AS_IN1_EN 0xCD
++#define ENE_FAN_AS_IN2 0xFE31 /* fan init reg 2 */
++#define ENE_FAN_AS_IN2_EN 0x03
++#define ENE_SAMPLE_PERIOD_FAN 61 /* fan input has fixed sample period */
++
++/* IRQ registers block (for revision B) */
++#define ENEB_IRQ 0xFD09 /* IRQ number */
++#define ENEB_IRQ_UNK1 0xFD17 /* unknown setting = 1 */
++#define ENEB_IRQ_STATUS 0xFD80 /* irq status */
++#define ENEB_IRQ_STATUS_IR (1 << 5) /* IR irq */
++
++/* IRQ registers block (for revision C,D) */
++#define ENEC_IRQ 0xFE9B /* new irq settings register */
++#define ENEC_IRQ_MASK 0x0F /* irq number mask */
++#define ENEC_IRQ_UNK_EN (1 << 4) /* always enabled */
++#define ENEC_IRQ_STATUS (1 << 5) /* irq status and ACK */
++
++/* CIR block settings */
++#define ENE_CIR_CONF1 0xFEC0
++#define ENE_CIR_CONF1_ADC_ON 0x7 /* reciever on gpio40 enabled */
++#define ENE_CIR_CONF1_LEARN1 (1 << 3) /* enabled on learning mode */
++#define ENE_CIR_CONF1_TX_ON 0x30 /* enabled on transmit */
++#define ENE_CIR_CONF1_TX_CARR (1 << 7) /* send TX carrier or not */
++
++#define ENE_CIR_CONF2 0xFEC1 /* unknown setting = 0 */
++#define ENE_CIR_CONF2_LEARN2 (1 << 4) /* set on enable learning */
++#define ENE_CIR_CONF2_GPIO40DIS (1 << 5) /* disable normal input via gpio40 */
++
++#define ENE_CIR_SAMPLE_PERIOD 0xFEC8 /* sample period in us */
++#define ENE_CIR_SAMPLE_OVERFLOW (1 << 7) /* interrupt on overflows if set */
++
++
++/* transmitter - not implemented yet */
++/* KB3926C and higher */
++/* transmission is very similiar to recieving, a byte is written to */
++/* ENE_TX_INPUT, in same manner as it is read from sample buffer */
++/* sample period is fixed*/
++
++
++/* transmitter ports */
++#define ENE_TX_PORT1 0xFC01 /* this enables one or both */
++#define ENE_TX_PORT1_EN (1 << 5) /* TX ports */
++#define ENE_TX_PORT2 0xFC08
++#define ENE_TX_PORT2_EN (1 << 1)
++
++#define ENE_TX_INPUT 0xFEC9 /* next byte to transmit */
++#define ENE_TX_SPC_MASK (1 << 7) /* Transmitted sample is space */
++#define ENE_TX_UNK1 0xFECB /* set to 0x63 */
++#define ENE_TX_SMPL_PERIOD 50 /* transmit sample period */
++
++
++#define ENE_TX_CARRIER 0xFECE /* TX carrier * 2 (khz) */
++#define ENE_TX_CARRIER_UNKBIT 0x80 /* This bit set on transmit */
++#define ENE_TX_CARRIER_LOW 0xFECF /* TX carrier / 2 */
++
++/* Hardware versions */
++#define ENE_HW_VERSION 0xFF00 /* hardware revision */
++#define ENE_HW_UNK 0xFF1D
++#define ENE_HW_UNK_CLR (1 << 2)
++#define ENE_HW_VER_MAJOR 0xFF1E /* chip version */
++#define ENE_HW_VER_MINOR 0xFF1F
++#define ENE_HW_VER_OLD 0xFD00
++
++#define same_sign(a, b) ((((a) > 0) && (b) > 0) || ((a) < 0 && (b) < 0))
++
++#define ENE_DRIVER_NAME "enecir"
++#define ENE_MAXGAP 250000 /* this is amount of time we wait
++ before turning the sampler, chosen
++ arbitry */
++
++#define space(len) (-(len)) /* add a space */
++
++/* software defines */
++#define ENE_IRQ_RX 1
++#define ENE_IRQ_TX 2
++
++#define ENE_HW_B 1 /* 3926B */
++#define ENE_HW_C 2 /* 3926C */
++#define ENE_HW_D 3 /* 3926D */
++
++#define ene_printk(level, text, ...) \
++ printk(level ENE_DRIVER_NAME ": " text, ## __VA_ARGS__)
++
++struct ene_device {
++ struct pnp_dev *pnp_dev;
++ struct lirc_driver *lirc_driver;
++
++ /* hw settings */
++ unsigned long hw_io;
++ int irq;
++
++ int hw_revision; /* hardware revision */
++ int hw_learning_and_tx_capable; /* learning capable */
++ int hw_gpio40_learning; /* gpio40 is learning */
++ int hw_fan_as_normal_input; /* fan input is used as regular input */
++
++ /* device data */
++ int idle;
++ int fan_input_inuse;
++
++ int sample;
++ int in_use;
++
++ struct timeval gap_start;
++};
+diff --git a/drivers/input/lirc/lirc_i2c.c b/drivers/input/lirc/lirc_i2c.c
+new file mode 100644
+index 0000000..f3f8c2e
+--- /dev/null
++++ b/drivers/input/lirc/lirc_i2c.c
+@@ -0,0 +1,536 @@
++/*
++ * lirc_i2c.c
++ *
++ * i2c IR driver for the onboard IR port on many TV tuner cards, including:
++ * -Flavors of the Hauppauge PVR-150/250/350
++ * -Hauppauge HVR-1300
++ * -PixelView (BT878P+W/FM)
++ * -KNC ONE TV Station/Anubis Typhoon TView Tuner
++ * -Asus TV-Box and Creative/VisionTek BreakOut-Box
++ * -Leadtek Winfast PVR2000
++ *
++ * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
++ * modified for PixelView (BT878P+W/FM) by
++ * Michal Kochanowicz <mkochano@pld.org.pl>
++ * Christoph Bartelmus <lirc@bartelmus.de>
++ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
++ * Ulrich Mueller <ulrich.mueller42@web.de>
++ * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by
++ * Stefan Jahn <stefan@lkcc.org>
++ * modified for inclusion into kernel sources by
++ * Jerome Brock <jbrock@users.sourceforge.net>
++ * modified for Leadtek Winfast PVR2000 by
++ * Thomas Reitmayr (treitmayr@yahoo.com)
++ * modified for Hauppauge HVR-1300 by
++ * Jan Frey (jfrey@gmx.de)
++ *
++ * parts are cut&pasted from the old lirc_haup.c driver
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/kmod.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/string.h>
++#include <linux/timer.h>
++#include <linux/delay.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/i2c.h>
++#include <linux/i2c-algo-bit.h>
++
++#include "lirc_dev.h"
++
++struct IR {
++ struct lirc_driver l;
++ struct i2c_client c;
++ int nextkey;
++ unsigned char b[3];
++ unsigned char bits;
++ unsigned char flag;
++};
++
++#define DEVICE_NAME "lirc_i2c"
++
++/* module parameters */
++static int debug; /* debug output */
++static int minor = -1; /* minor number */
++
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG DEVICE_NAME ": " fmt, \
++ ## args); \
++ } while (0)
++
++static int reverse(int data, int bits)
++{
++ int i;
++ int c;
++
++ for (c = 0, i = 0; i < bits; i++)
++ c |= ((data & (1<<i)) ? 1 : 0) << (bits-1-i);
++
++ return c;
++}
++
++static int add_to_buf_adap(void *data, struct lirc_buffer *buf)
++{
++ struct IR *ir = data;
++ unsigned char keybuf[4];
++
++ keybuf[0] = 0x00;
++ i2c_master_send(&ir->c, keybuf, 1);
++ /* poll IR chip */
++ if (i2c_master_recv(&ir->c, keybuf, sizeof(keybuf)) != sizeof(keybuf)) {
++ dprintk("read error\n");
++ return -EIO;
++ }
++
++ dprintk("key (0x%02x%02x%02x%02x)\n",
++ keybuf[0], keybuf[1], keybuf[2], keybuf[3]);
++
++ /* key pressed ? */
++ if (keybuf[2] == 0xff)
++ return -ENODATA;
++
++ /* remove repeat bit */
++ keybuf[2] &= 0x7f;
++ keybuf[3] |= 0x80;
++
++ lirc_buffer_write(buf, keybuf);
++ return 0;
++}
++
++static int add_to_buf_pcf8574(void *data, struct lirc_buffer *buf)
++{
++ struct IR *ir = data;
++ int rc;
++ unsigned char all, mask;
++ unsigned char key;
++
++ /* compute all valid bits (key code + pressed/release flag) */
++ all = ir->bits | ir->flag;
++
++ /* save IR writable mask bits */
++ mask = i2c_smbus_read_byte(&ir->c) & ~all;
++
++ /* send bit mask */
++ rc = i2c_smbus_write_byte(&ir->c, (0xff & all) | mask);
++
++ /* receive scan code */
++ rc = i2c_smbus_read_byte(&ir->c);
++
++ if (rc == -1) {
++ dprintk("%s read error\n", ir->c.name);
++ return -EIO;
++ }
++
++ /* drop duplicate polls */
++ if (ir->b[0] == (rc & all))
++ return -ENODATA;
++
++ ir->b[0] = rc & all;
++
++ dprintk("%s key 0x%02X %s\n", ir->c.name, rc & ir->bits,
++ (rc & ir->flag) ? "released" : "pressed");
++
++ /* ignore released buttons */
++ if (rc & ir->flag)
++ return -ENODATA;
++
++ /* set valid key code */
++ key = rc & ir->bits;
++ lirc_buffer_write(buf, &key);
++ return 0;
++}
++
++/* common for Hauppauge IR receivers */
++static int add_to_buf_haup_common(void *data, struct lirc_buffer *buf,
++ unsigned char *keybuf, int size, int offset)
++{
++ struct IR *ir = data;
++ __u16 code;
++ unsigned char codes[2];
++ int ret;
++
++ /* poll IR chip */
++ ret = i2c_master_recv(&ir->c, keybuf, size);
++ if (ret == size) {
++ ir->b[0] = keybuf[offset];
++ ir->b[1] = keybuf[offset+1];
++ ir->b[2] = keybuf[offset+2];
++ if (ir->b[0] != 0x00 && ir->b[1] != 0x00)
++ dprintk("key (0x%02x/0x%02x)\n", ir->b[0], ir->b[1]);
++ } else {
++ dprintk("read error (ret=%d)\n", ret);
++ /* keep last successful read buffer */
++ }
++
++ /* key pressed ? */
++ if ((ir->b[0] & 0x80) == 0)
++ return -ENODATA;
++
++ /* look what we have */
++ code = (((__u16)ir->b[0]&0x7f)<<6) | (ir->b[1]>>2);
++
++ codes[0] = (code >> 8) & 0xff;
++ codes[1] = code & 0xff;
++
++ /* return it */
++ dprintk("sending code 0x%02x%02x to lirc\n", codes[0], codes[1]);
++ lirc_buffer_write(buf, codes);
++ return 0;
++}
++
++/* specific for the Hauppauge PVR150 IR receiver */
++static int add_to_buf_haup_pvr150(void *data, struct lirc_buffer *buf)
++{
++ unsigned char keybuf[6];
++ /* fetch 6 bytes, first relevant is at offset 3 */
++ return add_to_buf_haup_common(data, buf, keybuf, 6, 3);
++}
++
++/* used for all Hauppauge IR receivers but the PVR150 */
++static int add_to_buf_haup(void *data, struct lirc_buffer *buf)
++{
++ unsigned char keybuf[3];
++ /* fetch 3 bytes, first relevant is at offset 0 */
++ return add_to_buf_haup_common(data, buf, keybuf, 3, 0);
++}
++
++
++static int add_to_buf_pvr2000(void *data, struct lirc_buffer *buf)
++{
++ struct IR *ir = data;
++ unsigned char key;
++ s32 flags;
++ s32 code;
++
++ /* poll IR chip */
++ flags = i2c_smbus_read_byte_data(&ir->c, 0x10);
++ if (-1 == flags) {
++ dprintk("read error\n");
++ return -ENODATA;
++ }
++ /* key pressed ? */
++ if (0 == (flags & 0x80))
++ return -ENODATA;
++
++ /* read actual key code */
++ code = i2c_smbus_read_byte_data(&ir->c, 0x00);
++ if (-1 == code) {
++ dprintk("read error\n");
++ return -ENODATA;
++ }
++
++ key = code & 0xFF;
++
++ dprintk("IR Key/Flags: (0x%02x/0x%02x)\n", key, flags & 0xFF);
++
++ /* return it */
++ lirc_buffer_write(buf, &key);
++ return 0;
++}
++
++static int add_to_buf_pixelview(void *data, struct lirc_buffer *buf)
++{
++ struct IR *ir = data;
++ unsigned char key;
++
++ /* poll IR chip */
++ if (1 != i2c_master_recv(&ir->c, &key, 1)) {
++ dprintk("read error\n");
++ return -1;
++ }
++ dprintk("key %02x\n", key);
++
++ /* return it */
++ lirc_buffer_write(buf, &key);
++ return 0;
++}
++
++static int add_to_buf_pv951(void *data, struct lirc_buffer *buf)
++{
++ struct IR *ir = data;
++ unsigned char key;
++ unsigned char codes[4];
++
++ /* poll IR chip */
++ if (1 != i2c_master_recv(&ir->c, &key, 1)) {
++ dprintk("read error\n");
++ return -ENODATA;
++ }
++ /* ignore 0xaa */
++ if (key == 0xaa)
++ return -ENODATA;
++ dprintk("key %02x\n", key);
++
++ codes[0] = 0x61;
++ codes[1] = 0xD6;
++ codes[2] = reverse(key, 8);
++ codes[3] = (~codes[2])&0xff;
++
++ lirc_buffer_write(buf, codes);
++ return 0;
++}
++
++static int add_to_buf_knc1(void *data, struct lirc_buffer *buf)
++{
++ static unsigned char last_key = 0xFF;
++ struct IR *ir = data;
++ unsigned char key;
++
++ /* poll IR chip */
++ if (1 != i2c_master_recv(&ir->c, &key, 1)) {
++ dprintk("read error\n");
++ return -ENODATA;
++ }
++
++ /*
++ * it seems that 0xFE indicates that a button is still held
++ * down, while 0xFF indicates that no button is held
++ * down. 0xFE sequences are sometimes interrupted by 0xFF
++ */
++
++ dprintk("key %02x\n", key);
++
++ if (key == 0xFF)
++ return -ENODATA;
++
++ if (key == 0xFE)
++ key = last_key;
++
++ last_key = key;
++ lirc_buffer_write(buf, &key);
++
++ return 0;
++}
++
++static int set_use_inc(void *data)
++{
++ struct IR *ir = data;
++
++ dprintk("%s called\n", __func__);
++
++ /* lock bttv in memory while /dev/lirc is in use */
++ i2c_use_client(&ir->c);
++
++ return 0;
++}
++
++static void set_use_dec(void *data)
++{
++ struct IR *ir = data;
++
++ dprintk("%s called\n", __func__);
++
++ i2c_release_client(&ir->c);
++}
++
++static struct lirc_driver lirc_template = {
++ .name = "lirc_i2c",
++ .set_use_inc = set_use_inc,
++ .set_use_dec = set_use_dec,
++ .dev = NULL,
++ .owner = THIS_MODULE,
++};
++
++static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id);
++static int ir_remove(struct i2c_client *client);
++static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg);
++
++static const struct i2c_device_id ir_receiver_id[] = {
++ /* Generic entry for any IR receiver */
++ { "ir_video", 0 },
++ /* IR device specific entries could be added here */
++ { }
++};
++
++static struct i2c_driver driver = {
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "i2c ir driver",
++ },
++ .probe = ir_probe,
++ .remove = ir_remove,
++ .id_table = ir_receiver_id,
++ .command = ir_command,
++};
++
++static void pcf_probe(struct i2c_client *client, struct IR *ir)
++{
++ int ret1, ret2, ret3, ret4;
++
++ ret1 = i2c_smbus_write_byte(client, 0xff);
++ ret2 = i2c_smbus_read_byte(client);
++ ret3 = i2c_smbus_write_byte(client, 0x00);
++ ret4 = i2c_smbus_read_byte(client);
++
++ /* in the Asus TV-Box: bit 1-0 */
++ if (((ret2 & 0x03) == 0x03) && ((ret4 & 0x03) == 0x00)) {
++ ir->bits = (unsigned char) ~0x07;
++ ir->flag = 0x04;
++ /* in the Creative/VisionTek BreakOut-Box: bit 7-6 */
++ } else if (((ret2 & 0xc0) == 0xc0) && ((ret4 & 0xc0) == 0x00)) {
++ ir->bits = (unsigned char) ~0xe0;
++ ir->flag = 0x20;
++ }
++
++ return;
++}
++
++static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
++{
++ struct IR *ir;
++ struct i2c_adapter *adap = client->adapter;
++ unsigned short addr = client->addr;
++ int retval;
++
++ ir = kzalloc(sizeof(struct IR), GFP_KERNEL);
++ if (!ir)
++ return -ENOMEM;
++ memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver));
++ memcpy(&ir->c, client, sizeof(struct i2c_client));
++
++ i2c_set_clientdata(client, ir);
++ ir->l.data = ir;
++ ir->l.minor = minor;
++ ir->l.sample_rate = 10;
++ ir->l.dev = &ir->c.dev;
++ ir->nextkey = -1;
++
++ switch (addr) {
++ case 0x64:
++ strlcpy(ir->c.name, "Pixelview IR", I2C_NAME_SIZE);
++ ir->l.code_length = 8;
++ ir->l.add_to_buf = add_to_buf_pixelview;
++ break;
++ case 0x4b:
++ strlcpy(ir->c.name, "PV951 IR", I2C_NAME_SIZE);
++ ir->l.code_length = 32;
++ ir->l.add_to_buf = add_to_buf_pv951;
++ break;
++ case 0x71:
++ if (adap->id == I2C_HW_B_CX2388x)
++ strlcpy(ir->c.name, "Hauppauge HVR1300", I2C_NAME_SIZE);
++ else /* bt8xx or cx2341x */
++ /*
++ * The PVR150 IR receiver uses the same protocol as
++ * other Hauppauge cards, but the data flow is
++ * different, so we need to deal with it by its own.
++ */
++ strlcpy(ir->c.name, "Hauppauge PVR150", I2C_NAME_SIZE);
++ ir->l.code_length = 13;
++ ir->l.add_to_buf = add_to_buf_haup_pvr150;
++ break;
++ case 0x6b:
++ strlcpy(ir->c.name, "Adaptec IR", I2C_NAME_SIZE);
++ ir->l.code_length = 32;
++ ir->l.add_to_buf = add_to_buf_adap;
++ break;
++ case 0x18:
++ case 0x1a:
++ if (adap->id == I2C_HW_B_CX2388x) {
++ strlcpy(ir->c.name, "Leadtek IR", I2C_NAME_SIZE);
++ ir->l.code_length = 8;
++ ir->l.add_to_buf = add_to_buf_pvr2000;
++ } else { /* bt8xx or cx2341x */
++ strlcpy(ir->c.name, "Hauppauge IR", I2C_NAME_SIZE);
++ ir->l.code_length = 13;
++ ir->l.add_to_buf = add_to_buf_haup;
++ }
++ break;
++ case 0x30:
++ strlcpy(ir->c.name, "KNC ONE IR", I2C_NAME_SIZE);
++ ir->l.code_length = 8;
++ ir->l.add_to_buf = add_to_buf_knc1;
++ break;
++ case 0x21:
++ case 0x23:
++ pcf_probe(client, ir);
++ strlcpy(ir->c.name, "TV-Box IR", I2C_NAME_SIZE);
++ ir->l.code_length = 8;
++ ir->l.add_to_buf = add_to_buf_pcf8574;
++ break;
++ default:
++ /* shouldn't happen */
++ printk("lirc_i2c: Huh? unknown i2c address (0x%02x)?\n", addr);
++ kfree(ir);
++ return -EINVAL;
++ }
++ printk(KERN_INFO "lirc_i2c: chip 0x%x found @ 0x%02x (%s)\n",
++ adap->id, addr, ir->c.name);
++
++ retval = lirc_register_driver(&ir->l);
++
++ if (retval < 0) {
++ printk(KERN_ERR "lirc_i2c: failed to register driver!\n");
++ kfree(ir);
++ return retval;
++ }
++
++ ir->l.minor = retval;
++
++ return 0;
++}
++
++static int ir_remove(struct i2c_client *client)
++{
++ struct IR *ir = i2c_get_clientdata(client);
++
++ /* unregister device */
++ lirc_unregister_driver(ir->l.minor);
++
++ /* free memory */
++ kfree(ir);
++ return 0;
++}
++
++static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg)
++{
++ /* nothing */
++ return 0;
++}
++
++static int __init lirc_i2c_init(void)
++{
++ i2c_add_driver(&driver);
++ return 0;
++}
++
++static void __exit lirc_i2c_exit(void)
++{
++ i2c_del_driver(&driver);
++}
++
++MODULE_DESCRIPTION("Infrared receiver driver for Hauppauge and "
++ "Pixelview cards (i2c stack)");
++MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, "
++ "Ulrich Mueller, Stefan Jahn, Jerome Brock");
++MODULE_LICENSE("GPL");
++
++module_param(minor, int, S_IRUGO);
++MODULE_PARM_DESC(minor, "Preferred minor device number");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
++
++module_init(lirc_i2c_init);
++module_exit(lirc_i2c_exit);
+diff --git a/drivers/input/lirc/lirc_igorplugusb.c b/drivers/input/lirc/lirc_igorplugusb.c
+new file mode 100644
+index 0000000..d1c02c2
+--- /dev/null
++++ b/drivers/input/lirc/lirc_igorplugusb.c
+@@ -0,0 +1,555 @@
++/*
++ * lirc_igorplugusb - USB remote support for LIRC
++ *
++ * Supports the standard homebrew IgorPlugUSB receiver with Igor's firmware.
++ * See http://www.cesko.host.sk/IgorPlugUSB/IgorPlug-USB%20(AVR)_eng.htm
++ *
++ * The device can only record bursts of up to 36 pulses/spaces.
++ * Works fine with RC5. Longer commands lead to device buffer overrun.
++ * (Maybe a better firmware or a microcontroller with more ram can help?)
++ *
++ * Version 0.1 [beta status]
++ *
++ * Copyright (C) 2004 Jan M. Hochstein
++ * <hochstein@algo.informatik.tu-darmstadt.de>
++ *
++ * This driver was derived from:
++ * Paul Miller <pmiller9@users.sourceforge.net>
++ * "lirc_atiusb" module
++ * Vladimir Dergachev <volodya@minspring.com>'s 2002
++ * "USB ATI Remote support" (input device)
++ * Adrian Dewhurst <sailor-lk@sailorfrag.net>'s 2002
++ * "USB StreamZap remote driver" (LIRC)
++ * Artur Lipowski <alipowski@kki.net.pl>'s 2002
++ * "lirc_dev" and "lirc_gpio" LIRC modules
++ */
++
++/*
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/kmod.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/fs.h>
++#include <linux/usb.h>
++#include <linux/time.h>
++
++#include <linux/lirc.h>
++#include "lirc_dev.h"
++
++
++/* module identification */
++#define DRIVER_VERSION "0.1"
++#define DRIVER_AUTHOR \
++ "Jan M. Hochstein <hochstein@algo.informatik.tu-darmstadt.de>"
++#define DRIVER_DESC "USB remote driver for LIRC"
++#define DRIVER_NAME "lirc_igorplugusb"
++
++/* debugging support */
++#ifdef CONFIG_USB_DEBUG
++static int debug = 1;
++#else
++static int debug;
++#endif
++
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG fmt, ## args); \
++ } while (0)
++
++/* One mode2 pulse/space has 4 bytes. */
++#define CODE_LENGTH sizeof(int)
++
++/* Igor's firmware cannot record bursts longer than 36. */
++#define DEVICE_BUFLEN 36
++
++/*
++ * Header at the beginning of the device's buffer:
++ * unsigned char data_length
++ * unsigned char data_start (!=0 means ring-buffer overrun)
++ * unsigned char counter (incremented by each burst)
++ */
++#define DEVICE_HEADERLEN 3
++
++/* This is for the gap */
++#define ADDITIONAL_LIRC_BYTES 2
++
++/* times to poll per second */
++#define SAMPLE_RATE 100
++static int sample_rate = SAMPLE_RATE;
++
++
++/**** Igor's USB Request Codes */
++
++#define SET_INFRABUFFER_EMPTY 1
++/**
++ * Params: none
++ * Answer: empty
++ */
++
++#define GET_INFRACODE 2
++/**
++ * Params:
++ * wValue: offset to begin reading infra buffer
++ *
++ * Answer: infra data
++ */
++
++#define SET_DATAPORT_DIRECTION 3
++/**
++ * Params:
++ * wValue: (byte) 1 bit for each data port pin (0=in, 1=out)
++ *
++ * Answer: empty
++ */
++
++#define GET_DATAPORT_DIRECTION 4
++/**
++ * Params: none
++ *
++ * Answer: (byte) 1 bit for each data port pin (0=in, 1=out)
++ */
++
++#define SET_OUT_DATAPORT 5
++/**
++ * Params:
++ * wValue: byte to write to output data port
++ *
++ * Answer: empty
++ */
++
++#define GET_OUT_DATAPORT 6
++/**
++ * Params: none
++ *
++ * Answer: least significant 3 bits read from output data port
++ */
++
++#define GET_IN_DATAPORT 7
++/**
++ * Params: none
++ *
++ * Answer: least significant 3 bits read from input data port
++ */
++
++#define READ_EEPROM 8
++/**
++ * Params:
++ * wValue: offset to begin reading EEPROM
++ *
++ * Answer: EEPROM bytes
++ */
++
++#define WRITE_EEPROM 9
++/**
++ * Params:
++ * wValue: offset to EEPROM byte
++ * wIndex: byte to write
++ *
++ * Answer: empty
++ */
++
++#define SEND_RS232 10
++/**
++ * Params:
++ * wValue: byte to send
++ *
++ * Answer: empty
++ */
++
++#define RECV_RS232 11
++/**
++ * Params: none
++ *
++ * Answer: byte received
++ */
++
++#define SET_RS232_BAUD 12
++/**
++ * Params:
++ * wValue: byte to write to UART bit rate register (UBRR)
++ *
++ * Answer: empty
++ */
++
++#define GET_RS232_BAUD 13
++/**
++ * Params: none
++ *
++ * Answer: byte read from UART bit rate register (UBRR)
++ */
++
++
++/* data structure for each usb remote */
++struct igorplug {
++
++ /* usb */
++ struct usb_device *usbdev;
++ struct urb *urb_in;
++ int devnum;
++
++ unsigned char *buf_in;
++ unsigned int len_in;
++ int in_space;
++ struct timeval last_time;
++
++ dma_addr_t dma_in;
++
++ /* lirc */
++ struct lirc_driver *d;
++
++ /* handle sending (init strings) */
++ int send_flags;
++ wait_queue_head_t wait_out;
++};
++
++static int unregister_from_lirc(struct igorplug *ir)
++{
++ struct lirc_driver *d = ir->d;
++ int devnum;
++
++ if (!ir->d)
++ return -EINVAL;
++
++ devnum = ir->devnum;
++ dprintk(DRIVER_NAME "[%d]: unregister from lirc called\n", devnum);
++
++ lirc_unregister_driver(d->minor);
++
++ printk(DRIVER_NAME "[%d]: usb remote disconnected\n", devnum);
++
++ kfree(d);
++ ir->d = NULL;
++ kfree(ir);
++ return 0;
++}
++
++static int set_use_inc(void *data)
++{
++ struct igorplug *ir = data;
++
++ if (!ir) {
++ printk(DRIVER_NAME "[?]: set_use_inc called with no context\n");
++ return -EIO;
++ }
++ dprintk(DRIVER_NAME "[%d]: set use inc\n", ir->devnum);
++
++ if (!ir->usbdev)
++ return -ENODEV;
++
++ return 0;
++}
++
++static void set_use_dec(void *data)
++{
++ struct igorplug *ir = data;
++
++ if (!ir) {
++ printk(DRIVER_NAME "[?]: set_use_dec called with no context\n");
++ return;
++ }
++ dprintk(DRIVER_NAME "[%d]: set use dec\n", ir->devnum);
++}
++
++
++/**
++ * Called in user context.
++ * return 0 if data was added to the buffer and
++ * -ENODATA if none was available. This should add some number of bits
++ * evenly divisible by code_length to the buffer
++ */
++static int usb_remote_poll(void *data, struct lirc_buffer *buf)
++{
++ int ret;
++ struct igorplug *ir = (struct igorplug *)data;
++
++ if (!ir->usbdev) /* Has the device been removed? */
++ return -ENODEV;
++
++ memset(ir->buf_in, 0, ir->len_in);
++
++ ret = usb_control_msg(
++ ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
++ GET_INFRACODE, USB_TYPE_VENDOR|USB_DIR_IN,
++ 0/* offset */, /*unused*/0,
++ ir->buf_in, ir->len_in,
++ /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
++ if (ret > 0) {
++ int i = DEVICE_HEADERLEN;
++ int code, timediff;
++ struct timeval now;
++
++ if (ret <= 1) /* ACK packet has 1 byte --> ignore */
++ return -ENODATA;
++
++ dprintk(DRIVER_NAME ": Got %d bytes. Header: %02x %02x %02x\n",
++ ret, ir->buf_in[0], ir->buf_in[1], ir->buf_in[2]);
++
++ if (ir->buf_in[2] != 0) {
++ printk(DRIVER_NAME "[%d]: Device buffer overrun.\n",
++ ir->devnum);
++ /* start at earliest byte */
++ i = DEVICE_HEADERLEN + ir->buf_in[2];
++ /* where are we now? space, gap or pulse? */
++ }
++
++ do_gettimeofday(&now);
++ timediff = now.tv_sec - ir->last_time.tv_sec;
++ if (timediff + 1 > PULSE_MASK / 1000000)
++ timediff = PULSE_MASK;
++ else {
++ timediff *= 1000000;
++ timediff += now.tv_usec - ir->last_time.tv_usec;
++ }
++ ir->last_time.tv_sec = now.tv_sec;
++ ir->last_time.tv_usec = now.tv_usec;
++
++ /* create leading gap */
++ code = timediff;
++ lirc_buffer_write(buf, (unsigned char *)&code);
++ ir->in_space = 1; /* next comes a pulse */
++
++ /* MODE2: pulse/space (PULSE_BIT) in 1us units */
++
++ while (i < ret) {
++ /* 1 Igor-tick = 85.333333 us */
++ code = (unsigned int)ir->buf_in[i] * 85
++ + (unsigned int)ir->buf_in[i] / 3;
++ if (ir->in_space)
++ code |= PULSE_BIT;
++ lirc_buffer_write(buf, (unsigned char *)&code);
++ /* 1 chunk = CODE_LENGTH bytes */
++ ir->in_space ^= 1;
++ ++i;
++ }
++
++ ret = usb_control_msg(
++ ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
++ SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN,
++ /*unused*/0, /*unused*/0,
++ /*dummy*/ir->buf_in, /*dummy*/ir->len_in,
++ /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
++ if (ret < 0)
++ printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: "
++ "error %d\n", ir->devnum, ret);
++ return 0;
++ } else if (ret < 0)
++ printk(DRIVER_NAME "[%d]: GET_INFRACODE: error %d\n",
++ ir->devnum, ret);
++
++ return -ENODATA;
++}
++
++
++
++static int usb_remote_probe(struct usb_interface *intf,
++ const struct usb_device_id *id)
++{
++ struct usb_device *dev = NULL;
++ struct usb_host_interface *idesc = NULL;
++ struct usb_host_endpoint *ep_ctl2;
++ struct igorplug *ir = NULL;
++ struct lirc_driver *driver = NULL;
++ int devnum, pipe, maxp;
++ int minor = 0;
++ char buf[63], name[128] = "";
++ int mem_failure = 0;
++ int ret;
++
++ dprintk(DRIVER_NAME ": usb probe called.\n");
++
++ dev = interface_to_usbdev(intf);
++
++ idesc = intf->cur_altsetting;
++
++ if (idesc->desc.bNumEndpoints != 1)
++ return -ENODEV;
++ ep_ctl2 = idesc->endpoint;
++ if (((ep_ctl2->desc.bEndpointAddress & USB_ENDPOINT_DIR_MASK)
++ != USB_DIR_IN)
++ || (ep_ctl2->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
++ != USB_ENDPOINT_XFER_CONTROL)
++ return -ENODEV;
++ pipe = usb_rcvctrlpipe(dev, ep_ctl2->desc.bEndpointAddress);
++ devnum = dev->devnum;
++ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
++
++ dprintk(DRIVER_NAME "[%d]: bytes_in_key=%lu maxp=%d\n",
++ devnum, CODE_LENGTH, maxp);
++
++
++ mem_failure = 0;
++ ir = kzalloc(sizeof(struct igorplug), GFP_KERNEL);
++ if (!ir) {
++ mem_failure = 1;
++ goto mem_failure_switch;
++ }
++ driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
++ if (!driver) {
++ mem_failure = 2;
++ goto mem_failure_switch;
++ }
++
++ ir->buf_in = usb_buffer_alloc(dev,
++ DEVICE_BUFLEN+DEVICE_HEADERLEN,
++ GFP_ATOMIC, &ir->dma_in);
++ if (!ir->buf_in) {
++ mem_failure = 3;
++ goto mem_failure_switch;
++ }
++
++ strcpy(driver->name, DRIVER_NAME " ");
++ driver->minor = -1;
++ driver->code_length = CODE_LENGTH * 8; /* in bits */
++ driver->features = LIRC_CAN_REC_MODE2;
++ driver->data = ir;
++ driver->chunk_size = CODE_LENGTH;
++ driver->buffer_size = DEVICE_BUFLEN + ADDITIONAL_LIRC_BYTES;
++ driver->set_use_inc = &set_use_inc;
++ driver->set_use_dec = &set_use_dec;
++ driver->sample_rate = sample_rate; /* per second */
++ driver->add_to_buf = &usb_remote_poll;
++ driver->dev = &intf->dev;
++ driver->owner = THIS_MODULE;
++
++ init_waitqueue_head(&ir->wait_out);
++
++ minor = lirc_register_driver(driver);
++ if (minor < 0)
++ mem_failure = 9;
++
++mem_failure_switch:
++
++ switch (mem_failure) {
++ case 9:
++ usb_buffer_free(dev, DEVICE_BUFLEN+DEVICE_HEADERLEN,
++ ir->buf_in, ir->dma_in);
++ case 3:
++ kfree(driver);
++ case 2:
++ kfree(ir);
++ case 1:
++ printk(DRIVER_NAME "[%d]: out of memory (code=%d)\n",
++ devnum, mem_failure);
++ return -ENOMEM;
++ }
++
++ driver->minor = minor;
++ ir->d = driver;
++ ir->devnum = devnum;
++ ir->usbdev = dev;
++ ir->len_in = DEVICE_BUFLEN+DEVICE_HEADERLEN;
++ ir->in_space = 1; /* First mode2 event is a space. */
++ do_gettimeofday(&ir->last_time);
++
++ if (dev->descriptor.iManufacturer
++ && usb_string(dev, dev->descriptor.iManufacturer,
++ buf, sizeof(buf)) > 0)
++ strlcpy(name, buf, sizeof(name));
++ if (dev->descriptor.iProduct
++ && usb_string(dev, dev->descriptor.iProduct, buf, sizeof(buf)) > 0)
++ snprintf(name + strlen(name), sizeof(name) - strlen(name),
++ " %s", buf);
++ printk(DRIVER_NAME "[%d]: %s on usb%d:%d\n", devnum, name,
++ dev->bus->busnum, devnum);
++
++ /* clear device buffer */
++ ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
++ SET_INFRABUFFER_EMPTY, USB_TYPE_VENDOR|USB_DIR_IN,
++ /*unused*/0, /*unused*/0,
++ /*dummy*/ir->buf_in, /*dummy*/ir->len_in,
++ /*timeout*/HZ * USB_CTRL_GET_TIMEOUT);
++ if (ret < 0)
++ printk(DRIVER_NAME "[%d]: SET_INFRABUFFER_EMPTY: error %d\n",
++ devnum, ret);
++
++ usb_set_intfdata(intf, ir);
++ return 0;
++}
++
++
++static void usb_remote_disconnect(struct usb_interface *intf)
++{
++ struct usb_device *dev = interface_to_usbdev(intf);
++ struct igorplug *ir = usb_get_intfdata(intf);
++ usb_set_intfdata(intf, NULL);
++
++ if (!ir || !ir->d)
++ return;
++
++ ir->usbdev = NULL;
++ wake_up_all(&ir->wait_out);
++
++ usb_buffer_free(dev, ir->len_in, ir->buf_in, ir->dma_in);
++
++ unregister_from_lirc(ir);
++}
++
++static struct usb_device_id usb_remote_id_table[] = {
++ /* Igor Plug USB (Atmel's Manufact. ID) */
++ { USB_DEVICE(0x03eb, 0x0002) },
++
++ /* Terminating entry */
++ { }
++};
++
++static struct usb_driver usb_remote_driver = {
++ .name = DRIVER_NAME,
++ .probe = usb_remote_probe,
++ .disconnect = usb_remote_disconnect,
++ .id_table = usb_remote_id_table
++};
++
++static int __init usb_remote_init(void)
++{
++ int i;
++
++ printk(KERN_INFO "\n"
++ DRIVER_NAME ": " DRIVER_DESC " v" DRIVER_VERSION "\n");
++ printk(DRIVER_NAME ": " DRIVER_AUTHOR "\n");
++ dprintk(DRIVER_NAME ": debug mode enabled\n");
++
++ i = usb_register(&usb_remote_driver);
++ if (i < 0) {
++ printk(DRIVER_NAME ": usb register failed, result = %d\n", i);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++static void __exit usb_remote_exit(void)
++{
++ usb_deregister(&usb_remote_driver);
++}
++
++module_init(usb_remote_init);
++module_exit(usb_remote_exit);
++
++#include <linux/vermagic.h>
++MODULE_INFO(vermagic, VERMAGIC_STRING);
++
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_AUTHOR(DRIVER_AUTHOR);
++MODULE_LICENSE("GPL");
++MODULE_DEVICE_TABLE(usb, usb_remote_id_table);
++
++module_param(sample_rate, int, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(sample_rate, "Sampling rate in Hz (default: 100)");
++
+diff --git a/drivers/input/lirc/lirc_imon.c b/drivers/input/lirc/lirc_imon.c
+new file mode 100644
+index 0000000..af5eec8
+--- /dev/null
++++ b/drivers/input/lirc/lirc_imon.c
+@@ -0,0 +1,1053 @@
++/*
++ * lirc_imon.c: LIRC/VFD/LCD driver for SoundGraph iMON IR/VFD/LCD
++ * including the iMON PAD model
++ *
++ * Copyright(C) 2004 Venky Raju(dev@venky.ws)
++ * Copyright(C) 2009 Jarod Wilson <jarod@wilsonet.com>
++ *
++ * lirc_imon 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 <linux/errno.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/usb.h>
++
++#include <linux/lirc.h>
++#include "lirc_dev.h"
++
++
++#define MOD_AUTHOR "Venky Raju <dev@venky.ws>"
++#define MOD_DESC "Driver for SoundGraph iMON MultiMedia IR/Display"
++#define MOD_NAME "lirc_imon"
++#define MOD_VERSION "0.8"
++
++#define DISPLAY_MINOR_BASE 144
++#define DEVICE_NAME "lcd%d"
++
++#define BUF_CHUNK_SIZE 4
++#define BUF_SIZE 128
++
++#define BIT_DURATION 250 /* each bit received is 250us */
++
++/*** P R O T O T Y P E S ***/
++
++/* USB Callback prototypes */
++static int imon_probe(struct usb_interface *interface,
++ const struct usb_device_id *id);
++static void imon_disconnect(struct usb_interface *interface);
++static void usb_rx_callback(struct urb *urb);
++static void usb_tx_callback(struct urb *urb);
++
++/* suspend/resume support */
++static int imon_resume(struct usb_interface *intf);
++static int imon_suspend(struct usb_interface *intf, pm_message_t message);
++
++/* Display file_operations function prototypes */
++static int display_open(struct inode *inode, struct file *file);
++static int display_close(struct inode *inode, struct file *file);
++
++/* VFD write operation */
++static ssize_t vfd_write(struct file *file, const char *buf,
++ size_t n_bytes, loff_t *pos);
++
++/* LIRC driver function prototypes */
++static int ir_open(void *data);
++static void ir_close(void *data);
++
++/* Driver init/exit prototypes */
++static int __init imon_init(void);
++static void __exit imon_exit(void);
++
++/*** G L O B A L S ***/
++
++struct imon_context {
++ struct usb_device *usbdev;
++ /* Newer devices have two interfaces */
++ int display; /* not all controllers do */
++ int display_isopen; /* display port has been opened */
++ int ir_isopen; /* IR port open */
++ int dev_present; /* USB device presence */
++ struct mutex ctx_lock; /* to lock this object */
++ wait_queue_head_t remove_ok; /* For unexpected USB disconnects */
++
++ int vfd_proto_6p; /* some VFD require a 6th packet */
++
++ struct lirc_driver *driver;
++ struct usb_endpoint_descriptor *rx_endpoint;
++ struct usb_endpoint_descriptor *tx_endpoint;
++ struct urb *rx_urb;
++ struct urb *tx_urb;
++ unsigned char usb_rx_buf[8];
++ unsigned char usb_tx_buf[8];
++
++ struct rx_data {
++ int count; /* length of 0 or 1 sequence */
++ int prev_bit; /* logic level of sequence */
++ int initial_space; /* initial space flag */
++ } rx;
++
++ struct tx_t {
++ unsigned char data_buf[35]; /* user data buffer */
++ struct completion finished; /* wait for write to finish */
++ atomic_t busy; /* write in progress */
++ int status; /* status of tx completion */
++ } tx;
++};
++
++static struct file_operations display_fops = {
++ .owner = THIS_MODULE,
++ .open = &display_open,
++ .write = &vfd_write,
++ .release = &display_close
++};
++
++/*
++ * USB Device ID for iMON USB Control Boards
++ *
++ * The Windows drivers contain 6 different inf files, more or less one for
++ * each new device until the 0x0034-0x0046 devices, which all use the same
++ * driver. Some of the devices in the 34-46 range haven't been definitively
++ * identified yet. Early devices have either a TriGem Computer, Inc. or a
++ * Samsung vendor ID (0x0aa8 and 0x04e8 respectively), while all later
++ * devices use the SoundGraph vendor ID (0x15c2).
++ */
++static struct usb_device_id imon_usb_id_table[] = {
++ /* TriGem iMON (IR only) -- TG_iMON.inf */
++ { USB_DEVICE(0x0aa8, 0x8001) },
++
++ /* SoundGraph iMON (IR only) -- sg_imon.inf */
++ { USB_DEVICE(0x04e8, 0xff30) },
++
++ /* SoundGraph iMON VFD (IR & VFD) -- iMON_VFD.inf */
++ { USB_DEVICE(0x0aa8, 0xffda) },
++
++ /* SoundGraph iMON SS (IR & VFD) -- iMON_SS.inf */
++ { USB_DEVICE(0x15c2, 0xffda) },
++
++ {}
++};
++
++/* Some iMON VFD models requires a 6th packet for VFD writes */
++static struct usb_device_id vfd_proto_6p_list[] = {
++ { USB_DEVICE(0x15c2, 0xffda) },
++ {}
++};
++
++/* Some iMON devices have no lcd/vfd, don't set one up */
++static struct usb_device_id ir_only_list[] = {
++ { USB_DEVICE(0x0aa8, 0x8001) },
++ { USB_DEVICE(0x04e8, 0xff30) },
++ {}
++};
++
++/* USB Device data */
++static struct usb_driver imon_driver = {
++ .name = MOD_NAME,
++ .probe = imon_probe,
++ .disconnect = imon_disconnect,
++ .suspend = imon_suspend,
++ .resume = imon_resume,
++ .id_table = imon_usb_id_table,
++};
++
++static struct usb_class_driver imon_class = {
++ .name = DEVICE_NAME,
++ .fops = &display_fops,
++ .minor_base = DISPLAY_MINOR_BASE,
++};
++
++/* to prevent races between open() and disconnect(), probing, etc */
++static DEFINE_MUTEX(driver_lock);
++
++static int debug;
++
++/*** M O D U L E C O D E ***/
++
++MODULE_AUTHOR(MOD_AUTHOR);
++MODULE_DESCRIPTION(MOD_DESC);
++MODULE_VERSION(MOD_VERSION);
++MODULE_LICENSE("GPL");
++MODULE_DEVICE_TABLE(usb, imon_usb_id_table);
++module_param(debug, int, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)");
++
++static void free_imon_context(struct imon_context *context)
++{
++ struct device *dev = context->driver->dev;
++ usb_free_urb(context->tx_urb);
++ usb_free_urb(context->rx_urb);
++ lirc_buffer_free(context->driver->rbuf);
++ kfree(context->driver->rbuf);
++ kfree(context->driver);
++ kfree(context);
++
++ dev_dbg(dev, "%s: iMON context freed\n", __func__);
++}
++
++static void deregister_from_lirc(struct imon_context *context)
++{
++ int retval;
++ int minor = context->driver->minor;
++
++ retval = lirc_unregister_driver(minor);
++ if (retval)
++ err("%s: unable to deregister from lirc(%d)",
++ __func__, retval);
++ else
++ printk(KERN_INFO MOD_NAME ": Deregistered iMON driver "
++ "(minor:%d)\n", minor);
++
++}
++
++/**
++ * Called when the Display device (e.g. /dev/lcd0)
++ * is opened by the application.
++ */
++static int display_open(struct inode *inode, struct file *file)
++{
++ struct usb_interface *interface;
++ struct imon_context *context = NULL;
++ int subminor;
++ int retval = 0;
++
++ /* prevent races with disconnect */
++ mutex_lock(&driver_lock);
++
++ subminor = iminor(inode);
++ interface = usb_find_interface(&imon_driver, subminor);
++ if (!interface) {
++ err("%s: could not find interface for minor %d",
++ __func__, subminor);
++ retval = -ENODEV;
++ goto exit;
++ }
++ context = usb_get_intfdata(interface);
++
++ if (!context) {
++ err("%s: no context found for minor %d",
++ __func__, subminor);
++ retval = -ENODEV;
++ goto exit;
++ }
++
++ mutex_lock(&context->ctx_lock);
++
++ if (!context->display) {
++ err("%s: display not supported by device", __func__);
++ retval = -ENODEV;
++ } else if (context->display_isopen) {
++ err("%s: display port is already open", __func__);
++ retval = -EBUSY;
++ } else {
++ context->display_isopen = 1;
++ file->private_data = context;
++ dev_info(context->driver->dev, "display port opened\n");
++ }
++
++ mutex_unlock(&context->ctx_lock);
++
++exit:
++ mutex_unlock(&driver_lock);
++ return retval;
++}
++
++/**
++ * Called when the display device (e.g. /dev/lcd0)
++ * is closed by the application.
++ */
++static int display_close(struct inode *inode, struct file *file)
++{
++ struct imon_context *context = NULL;
++ int retval = 0;
++
++ context = (struct imon_context *)file->private_data;
++
++ if (!context) {
++ err("%s: no context for device", __func__);
++ return -ENODEV;
++ }
++
++ mutex_lock(&context->ctx_lock);
++
++ if (!context->display) {
++ err("%s: display not supported by device", __func__);
++ retval = -ENODEV;
++ } else if (!context->display_isopen) {
++ err("%s: display is not open", __func__);
++ retval = -EIO;
++ } else {
++ context->display_isopen = 0;
++ dev_info(context->driver->dev, "display port closed\n");
++ if (!context->dev_present && !context->ir_isopen) {
++ /*
++ * Device disconnected before close and IR port is not
++ * open. If IR port is open, context will be deleted by
++ * ir_close.
++ */
++ mutex_unlock(&context->ctx_lock);
++ free_imon_context(context);
++ return retval;
++ }
++ }
++
++ mutex_unlock(&context->ctx_lock);
++ return retval;
++}
++
++/**
++ * Sends a packet to the device -- this function must be called
++ * with context->ctx_lock held.
++ */
++static int send_packet(struct imon_context *context)
++{
++ unsigned int pipe;
++ int interval = 0;
++ int retval = 0;
++ struct usb_ctrlrequest *control_req = NULL;
++
++ /* Check if we need to use control or interrupt urb */
++ pipe = usb_sndintpipe(context->usbdev,
++ context->tx_endpoint->bEndpointAddress);
++ interval = context->tx_endpoint->bInterval;
++
++ usb_fill_int_urb(context->tx_urb, context->usbdev, pipe,
++ context->usb_tx_buf,
++ sizeof(context->usb_tx_buf),
++ usb_tx_callback, context, interval);
++
++ context->tx_urb->actual_length = 0;
++
++ init_completion(&context->tx.finished);
++ atomic_set(&(context->tx.busy), 1);
++
++ retval = usb_submit_urb(context->tx_urb, GFP_KERNEL);
++ if (retval) {
++ atomic_set(&(context->tx.busy), 0);
++ err("%s: error submitting urb(%d)", __func__, retval);
++ } else {
++ /* Wait for transmission to complete (or abort) */
++ mutex_unlock(&context->ctx_lock);
++ retval = wait_for_completion_interruptible(
++ &context->tx.finished);
++ if (retval)
++ err("%s: task interrupted", __func__);
++ mutex_lock(&context->ctx_lock);
++
++ retval = context->tx.status;
++ if (retval)
++ err("%s: packet tx failed (%d)", __func__, retval);
++ }
++
++ kfree(control_req);
++
++ return retval;
++}
++
++/**
++ * Writes data to the VFD. The iMON VFD is 2x16 characters
++ * and requires data in 5 consecutive USB interrupt packets,
++ * each packet but the last carrying 7 bytes.
++ *
++ * I don't know if the VFD board supports features such as
++ * scrolling, clearing rows, blanking, etc. so at
++ * the caller must provide a full screen of data. If fewer
++ * than 32 bytes are provided spaces will be appended to
++ * generate a full screen.
++ */
++static ssize_t vfd_write(struct file *file, const char *buf,
++ size_t n_bytes, loff_t *pos)
++{
++ int i;
++ int offset;
++ int seq;
++ int retval = 0;
++ struct imon_context *context;
++ const unsigned char vfd_packet6[] = {
++ 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF };
++
++ context = (struct imon_context *)file->private_data;
++ if (!context) {
++ err("%s: no context for device", __func__);
++ return -ENODEV;
++ }
++
++ mutex_lock(&context->ctx_lock);
++
++ if (!context->dev_present) {
++ err("%s: no iMON device present", __func__);
++ retval = -ENODEV;
++ goto exit;
++ }
++
++ if (n_bytes <= 0 || n_bytes > 32) {
++ err("%s: invalid payload size", __func__);
++ retval = -EINVAL;
++ goto exit;
++ }
++
++ if (copy_from_user(context->tx.data_buf, buf, n_bytes)) {
++ retval = -EFAULT;
++ goto exit;
++ }
++
++ /* Pad with spaces */
++ for (i = n_bytes; i < 32; ++i)
++ context->tx.data_buf[i] = ' ';
++
++ for (i = 32; i < 35; ++i)
++ context->tx.data_buf[i] = 0xFF;
++
++ offset = 0;
++ seq = 0;
++
++ do {
++ memcpy(context->usb_tx_buf, context->tx.data_buf + offset, 7);
++ context->usb_tx_buf[7] = (unsigned char) seq;
++
++ retval = send_packet(context);
++ if (retval) {
++ err("%s: send packet failed for packet #%d",
++ __func__, seq/2);
++ goto exit;
++ } else {
++ seq += 2;
++ offset += 7;
++ }
++
++ } while (offset < 35);
++
++ if (context->vfd_proto_6p) {
++ /* Send packet #6 */
++ memcpy(context->usb_tx_buf, &vfd_packet6, sizeof(vfd_packet6));
++ context->usb_tx_buf[7] = (unsigned char) seq;
++ retval = send_packet(context);
++ if (retval)
++ err("%s: send packet failed for packet #%d",
++ __func__, seq/2);
++ }
++
++exit:
++ mutex_unlock(&context->ctx_lock);
++
++ return (!retval) ? n_bytes : retval;
++}
++
++/**
++ * Callback function for USB core API: transmit data
++ */
++static void usb_tx_callback(struct urb *urb)
++{
++ struct imon_context *context;
++
++ if (!urb)
++ return;
++ context = (struct imon_context *)urb->context;
++ if (!context)
++ return;
++
++ context->tx.status = urb->status;
++
++ /* notify waiters that write has finished */
++ atomic_set(&context->tx.busy, 0);
++ complete(&context->tx.finished);
++
++ return;
++}
++
++/**
++ * Called by lirc_dev when the application opens /dev/lirc
++ */
++static int ir_open(void *data)
++{
++ int retval = 0;
++ struct imon_context *context;
++
++ /* prevent races with disconnect */
++ mutex_lock(&driver_lock);
++
++ context = (struct imon_context *)data;
++
++ /* initial IR protocol decode variables */
++ context->rx.count = 0;
++ context->rx.initial_space = 1;
++ context->rx.prev_bit = 0;
++
++ context->ir_isopen = 1;
++ dev_info(context->driver->dev, "IR port opened\n");
++
++ mutex_unlock(&driver_lock);
++ return retval;
++}
++
++/**
++ * Called by lirc_dev when the application closes /dev/lirc
++ */
++static void ir_close(void *data)
++{
++ struct imon_context *context;
++
++ context = (struct imon_context *)data;
++ if (!context) {
++ err("%s: no context for device", __func__);
++ return;
++ }
++
++ mutex_lock(&context->ctx_lock);
++
++ context->ir_isopen = 0;
++ dev_info(context->driver->dev, "IR port closed\n");
++
++ if (!context->dev_present) {
++ /*
++ * Device disconnected while IR port was still open. Driver
++ * was not deregistered at disconnect time, so do it now.
++ */
++ deregister_from_lirc(context);
++
++ if (!context->display_isopen) {
++ mutex_unlock(&context->ctx_lock);
++ free_imon_context(context);
++ return;
++ }
++ /*
++ * If display port is open, context will be deleted by
++ * display_close
++ */
++ }
++
++ mutex_unlock(&context->ctx_lock);
++ return;
++}
++
++/**
++ * Convert bit count to time duration (in us) and submit
++ * the value to lirc_dev.
++ */
++static void submit_data(struct imon_context *context)
++{
++ unsigned char buf[4];
++ int value = context->rx.count;
++ int i;
++
++ dev_dbg(context->driver->dev, "submitting data to LIRC\n");
++
++ value *= BIT_DURATION;
++ value &= PULSE_MASK;
++ if (context->rx.prev_bit)
++ value |= PULSE_BIT;
++
++ for (i = 0; i < 4; ++i)
++ buf[i] = value>>(i*8);
++
++ lirc_buffer_write(context->driver->rbuf, buf);
++ wake_up(&context->driver->rbuf->wait_poll);
++ return;
++}
++
++static inline int tv2int(const struct timeval *a, const struct timeval *b)
++{
++ int usecs = 0;
++ int sec = 0;
++
++ if (b->tv_usec > a->tv_usec) {
++ usecs = 1000000;
++ sec--;
++ }
++
++ usecs += a->tv_usec - b->tv_usec;
++
++ sec += a->tv_sec - b->tv_sec;
++ sec *= 1000;
++ usecs /= 1000;
++ sec += usecs;
++
++ if (sec < 0)
++ sec = 1000;
++
++ return sec;
++}
++
++/**
++ * Process the incoming packet
++ */
++static void imon_incoming_packet(struct imon_context *context,
++ struct urb *urb, int intf)
++{
++ int len = urb->actual_length;
++ unsigned char *buf = urb->transfer_buffer;
++ struct device *dev = context->driver->dev;
++ int octet, bit;
++ unsigned char mask;
++ int i, chunk_num;
++
++ /*
++ * just bail out if no listening IR client
++ */
++ if (!context->ir_isopen)
++ return;
++
++ if (len != 8) {
++ dev_warn(dev, "imon %s: invalid incoming packet "
++ "size (len = %d, intf%d)\n", __func__, len, intf);
++ return;
++ }
++
++ if (debug) {
++ printk(KERN_INFO "raw packet: ");
++ for (i = 0; i < len; ++i)
++ printk("%02x ", buf[i]);
++ printk("\n");
++ }
++
++ /*
++ * Translate received data to pulse and space lengths.
++ * Received data is active low, i.e. pulses are 0 and
++ * spaces are 1.
++ *
++ * My original algorithm was essentially similar to
++ * Changwoo Ryu's with the exception that he switched
++ * the incoming bits to active high and also fed an
++ * initial space to LIRC at the start of a new sequence
++ * if the previous bit was a pulse.
++ *
++ * I've decided to adopt his algorithm.
++ */
++
++ if (buf[7] == 1 && context->rx.initial_space) {
++ /* LIRC requires a leading space */
++ context->rx.prev_bit = 0;
++ context->rx.count = 4;
++ submit_data(context);
++ context->rx.count = 0;
++ }
++
++ for (octet = 0; octet < 5; ++octet) {
++ mask = 0x80;
++ for (bit = 0; bit < 8; ++bit) {
++ int curr_bit = !(buf[octet] & mask);
++ if (curr_bit != context->rx.prev_bit) {
++ if (context->rx.count) {
++ submit_data(context);
++ context->rx.count = 0;
++ }
++ context->rx.prev_bit = curr_bit;
++ }
++ ++context->rx.count;
++ mask >>= 1;
++ }
++ }
++
++ if (chunk_num == 10) {
++ if (context->rx.count) {
++ submit_data(context);
++ context->rx.count = 0;
++ }
++ context->rx.initial_space = context->rx.prev_bit;
++ }
++}
++
++/**
++ * Callback function for USB core API: receive data
++ */
++static void usb_rx_callback(struct urb *urb)
++{
++ struct imon_context *context;
++ unsigned char *buf;
++ int len;
++ int intfnum = 0;
++
++ if (!urb)
++ return;
++
++ context = (struct imon_context *)urb->context;
++ if (!context)
++ return;
++
++ buf = urb->transfer_buffer;
++ len = urb->actual_length;
++
++ switch (urb->status) {
++ case -ENOENT: /* usbcore unlink successful! */
++ return;
++
++ case 0:
++ imon_incoming_packet(context, urb, intfnum);
++ break;
++
++ default:
++ dev_warn(context->driver->dev, "imon %s: status(%d): ignored\n",
++ __func__, urb->status);
++ break;
++ }
++
++ usb_submit_urb(context->rx_urb, GFP_ATOMIC);
++
++ return;
++}
++
++/**
++ * Callback function for USB core API: Probe
++ */
++static int imon_probe(struct usb_interface *interface,
++ const struct usb_device_id *id)
++{
++ struct usb_device *usbdev = NULL;
++ struct usb_host_interface *iface_desc = NULL;
++ struct usb_endpoint_descriptor *rx_endpoint = NULL;
++ struct usb_endpoint_descriptor *tx_endpoint = NULL;
++ struct urb *rx_urb = NULL;
++ struct urb *tx_urb = NULL;
++ struct lirc_driver *driver = NULL;
++ struct lirc_buffer *rbuf = NULL;
++ struct device *dev = &interface->dev;
++ int ifnum;
++ int lirc_minor = 0;
++ int num_endpts;
++ int retval = 0;
++ int display_ep_found = 0;
++ int ir_ep_found = 0;
++ int alloc_status = 0;
++ int vfd_proto_6p = 0;
++ int code_length;
++ struct imon_context *context = NULL;
++ int i;
++ u16 vendor, product;
++
++ context = kzalloc(sizeof(struct imon_context), GFP_KERNEL);
++ if (!context) {
++ err("%s: kzalloc failed for context", __func__);
++ alloc_status = 1;
++ goto alloc_status_switch;
++ }
++
++ /*
++ * Try to auto-detect the type of display if the user hasn't set
++ * it by hand via the display_type modparam. Default is VFD.
++ */
++ if (usb_match_id(interface, ir_only_list))
++ context->display = 0;
++ else
++ context->display = 1;
++
++ code_length = BUF_CHUNK_SIZE * 8;
++
++ usbdev = usb_get_dev(interface_to_usbdev(interface));
++ iface_desc = interface->cur_altsetting;
++ num_endpts = iface_desc->desc.bNumEndpoints;
++ ifnum = iface_desc->desc.bInterfaceNumber;
++ vendor = le16_to_cpu(usbdev->descriptor.idVendor);
++ product = le16_to_cpu(usbdev->descriptor.idProduct);
++
++ dev_dbg(dev, "%s: found iMON device (%04x:%04x, intf%d)\n",
++ __func__, vendor, product, ifnum);
++
++ /* prevent races probing devices w/multiple interfaces */
++ mutex_lock(&driver_lock);
++
++ /*
++ * Scan the endpoint list and set:
++ * first input endpoint = IR endpoint
++ * first output endpoint = display endpoint
++ */
++ for (i = 0; i < num_endpts && !(ir_ep_found && display_ep_found); ++i) {
++ struct usb_endpoint_descriptor *ep;
++ int ep_dir;
++ int ep_type;
++ ep = &iface_desc->endpoint[i].desc;
++ ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
++ ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
++
++ if (!ir_ep_found &&
++ ep_dir == USB_DIR_IN &&
++ ep_type == USB_ENDPOINT_XFER_INT) {
++
++ rx_endpoint = ep;
++ ir_ep_found = 1;
++ dev_dbg(dev, "%s: found IR endpoint\n", __func__);
++
++ } else if (!display_ep_found && ep_dir == USB_DIR_OUT &&
++ ep_type == USB_ENDPOINT_XFER_INT) {
++ tx_endpoint = ep;
++ display_ep_found = 1;
++ dev_dbg(dev, "%s: found display endpoint\n", __func__);
++ }
++ }
++
++ /*
++ * Some iMON receivers have no display. Unfortunately, it seems
++ * that SoundGraph recycles device IDs between devices both with
++ * and without... :\
++ */
++ if (context->display == 0) {
++ display_ep_found = 0;
++ dev_dbg(dev, "%s: device has no display\n", __func__);
++ }
++
++ /* Input endpoint is mandatory */
++ if (!ir_ep_found) {
++ err("%s: no valid input (IR) endpoint found.", __func__);
++ retval = -ENODEV;
++ alloc_status = 2;
++ goto alloc_status_switch;
++ }
++
++ /* Determine if display requires 6 packets */
++ if (display_ep_found) {
++ if (usb_match_id(interface, vfd_proto_6p_list))
++ vfd_proto_6p = 1;
++
++ dev_dbg(dev, "%s: vfd_proto_6p: %d\n",
++ __func__, vfd_proto_6p);
++ }
++
++ driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
++ if (!driver) {
++ err("%s: kzalloc failed for lirc_driver", __func__);
++ alloc_status = 2;
++ goto alloc_status_switch;
++ }
++ rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
++ if (!rbuf) {
++ err("%s: kmalloc failed for lirc_buffer", __func__);
++ alloc_status = 3;
++ goto alloc_status_switch;
++ }
++ if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
++ err("%s: lirc_buffer_init failed", __func__);
++ alloc_status = 4;
++ goto alloc_status_switch;
++ }
++ rx_urb = usb_alloc_urb(0, GFP_KERNEL);
++ if (!rx_urb) {
++ err("%s: usb_alloc_urb failed for IR urb", __func__);
++ alloc_status = 5;
++ goto alloc_status_switch;
++ }
++ tx_urb = usb_alloc_urb(0, GFP_KERNEL);
++ if (!tx_urb) {
++ err("%s: usb_alloc_urb failed for display urb",
++ __func__);
++ alloc_status = 6;
++ goto alloc_status_switch;
++ }
++
++ mutex_init(&context->ctx_lock);
++ context->vfd_proto_6p = vfd_proto_6p;
++
++ strcpy(driver->name, MOD_NAME);
++ driver->minor = -1;
++ driver->code_length = sizeof(int) * 8;
++ driver->sample_rate = 0;
++ driver->features = LIRC_CAN_REC_MODE2;
++ driver->data = context;
++ driver->rbuf = rbuf;
++ driver->set_use_inc = ir_open;
++ driver->set_use_dec = ir_close;
++ driver->dev = &interface->dev;
++ driver->owner = THIS_MODULE;
++
++ mutex_lock(&context->ctx_lock);
++
++ context->driver = driver;
++ /* start out in keyboard mode */
++
++ lirc_minor = lirc_register_driver(driver);
++ if (lirc_minor < 0) {
++ err("%s: lirc_register_driver failed", __func__);
++ alloc_status = 7;
++ goto alloc_status_switch;
++ } else
++ dev_info(dev, "Registered iMON driver "
++ "(lirc minor: %d)\n", lirc_minor);
++
++ /* Needed while unregistering! */
++ driver->minor = lirc_minor;
++
++ context->usbdev = usbdev;
++ context->dev_present = 1;
++ context->rx_endpoint = rx_endpoint;
++ context->rx_urb = rx_urb;
++
++ /*
++ * tx is used to send characters to lcd/vfd, associate RF
++ * remotes, set IR protocol, and maybe more...
++ */
++ context->tx_endpoint = tx_endpoint;
++ context->tx_urb = tx_urb;
++
++ if (display_ep_found)
++ context->display = 1;
++
++ usb_fill_int_urb(context->rx_urb, context->usbdev,
++ usb_rcvintpipe(context->usbdev,
++ context->rx_endpoint->bEndpointAddress),
++ context->usb_rx_buf, sizeof(context->usb_rx_buf),
++ usb_rx_callback, context,
++ context->rx_endpoint->bInterval);
++
++ retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
++
++ if (retval) {
++ err("%s: usb_submit_urb failed for intf0 (%d)",
++ __func__, retval);
++ mutex_unlock(&context->ctx_lock);
++ goto exit;
++ }
++
++ usb_set_intfdata(interface, context);
++
++ if (context->display && ifnum == 0) {
++ dev_dbg(dev, "%s: Registering iMON display with sysfs\n",
++ __func__);
++
++ if (usb_register_dev(interface, &imon_class)) {
++ /* Not a fatal error, so ignore */
++ dev_info(dev, "%s: could not get a minor number for "
++ "display\n", __func__);
++ }
++ }
++
++ dev_info(dev, "iMON device (%04x:%04x, intf%d) on "
++ "usb<%d:%d> initialized\n", vendor, product, ifnum,
++ usbdev->bus->busnum, usbdev->devnum);
++
++alloc_status_switch:
++ mutex_unlock(&context->ctx_lock);
++
++ switch (alloc_status) {
++ case 7:
++ usb_free_urb(tx_urb);
++ case 6:
++ usb_free_urb(rx_urb);
++ case 5:
++ if (rbuf)
++ lirc_buffer_free(rbuf);
++ case 4:
++ kfree(rbuf);
++ case 3:
++ kfree(driver);
++ case 2:
++ kfree(context);
++ context = NULL;
++ case 1:
++ if (retval != -ENODEV)
++ retval = -ENOMEM;
++ break;
++ case 0:
++ retval = 0;
++ }
++
++exit:
++ mutex_unlock(&driver_lock);
++
++ return retval;
++}
++
++/**
++ * Callback function for USB core API: disconnect
++ */
++static void imon_disconnect(struct usb_interface *interface)
++{
++ struct imon_context *context;
++ int ifnum;
++
++ /* prevent races with ir_open()/display_open() */
++ mutex_lock(&driver_lock);
++
++ context = usb_get_intfdata(interface);
++ ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
++
++ mutex_lock(&context->ctx_lock);
++
++ usb_set_intfdata(interface, NULL);
++
++ /* Abort ongoing write */
++ if (atomic_read(&context->tx.busy)) {
++ usb_kill_urb(context->tx_urb);
++ complete_all(&context->tx.finished);
++ }
++
++ context->dev_present = 0;
++ usb_kill_urb(context->rx_urb);
++ if (context->display)
++ usb_deregister_dev(interface, &imon_class);
++
++ if (!context->ir_isopen && !context->dev_present) {
++ deregister_from_lirc(context);
++ mutex_unlock(&context->ctx_lock);
++ if (!context->display_isopen)
++ free_imon_context(context);
++ } else
++ mutex_unlock(&context->ctx_lock);
++
++ mutex_unlock(&driver_lock);
++
++ printk(KERN_INFO "%s: iMON device (intf%d) disconnected\n",
++ __func__, ifnum);
++}
++
++static int imon_suspend(struct usb_interface *intf, pm_message_t message)
++{
++ struct imon_context *context = usb_get_intfdata(intf);
++
++ usb_kill_urb(context->rx_urb);
++
++ return 0;
++}
++
++static int imon_resume(struct usb_interface *intf)
++{
++ int rc = 0;
++ struct imon_context *context = usb_get_intfdata(intf);
++
++ usb_fill_int_urb(context->rx_urb, context->usbdev,
++ usb_rcvintpipe(context->usbdev,
++ context->rx_endpoint->bEndpointAddress),
++ context->usb_rx_buf, sizeof(context->usb_rx_buf),
++ usb_rx_callback, context,
++ context->rx_endpoint->bInterval);
++
++ rc = usb_submit_urb(context->rx_urb, GFP_ATOMIC);
++
++ return rc;
++}
++
++static int __init imon_init(void)
++{
++ int rc;
++
++ printk(KERN_INFO MOD_NAME ": " MOD_DESC ", v" MOD_VERSION "\n");
++
++ rc = usb_register(&imon_driver);
++ if (rc) {
++ err("%s: usb register failed(%d)", __func__, rc);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++static void __exit imon_exit(void)
++{
++ usb_deregister(&imon_driver);
++ printk(KERN_INFO MOD_NAME ": module removed. Goodbye!\n");
++}
++
++module_init(imon_init);
++module_exit(imon_exit);
+diff --git a/drivers/input/lirc/lirc_it87.c b/drivers/input/lirc/lirc_it87.c
+new file mode 100644
+index 0000000..a899d00
+--- /dev/null
++++ b/drivers/input/lirc/lirc_it87.c
+@@ -0,0 +1,1021 @@
++/*
++ * LIRC driver for ITE IT8712/IT8705 CIR port
++ *
++ * Copyright (C) 2001 Hans-Gunter Lutke Uphues <hg_lu@web.de>
++ *
++ * 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
++ *
++ * ITE IT8705 and IT8712(not tested) and IT8720 CIR-port support for lirc based
++ * via cut and paste from lirc_sir.c (C) 2000 Milan Pikula
++ *
++ * Attention: Sendmode only tested with debugging logs
++ *
++ * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> :
++ * reimplemented read function
++ * 2005/06/05 Andrew Calkin implemented support for Asus Digimatrix,
++ * based on work of the following member of the Outertrack Digimatrix
++ * Forum: Art103 <r_tay@hotmail.com>
++ * 2009/12/24 James Edwards <jimbo-lirc@edwardsclan.net> implemeted support
++ * for ITE8704/ITE8718, on my machine, the DSDT reports 8704, but the
++ * chip identifies as 18.
++ */
++
++#include <linux/module.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/fs.h>
++#include <linux/interrupt.h>
++#include <linux/ioport.h>
++#include <linux/kernel.h>
++#include <linux/time.h>
++#include <linux/string.h>
++#include <linux/types.h>
++#include <linux/wait.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++#include <linux/poll.h>
++#include <asm/system.h>
++#include <linux/io.h>
++#include <linux/irq.h>
++#include <linux/fcntl.h>
++
++#include <linux/timer.h>
++#include <linux/pnp.h>
++
++#include <linux/lirc.h>
++#include "lirc_dev.h"
++
++#include "lirc_it87.h"
++
++#ifdef LIRC_IT87_DIGIMATRIX
++static int digimatrix = 1;
++static int it87_freq = 36; /* kHz */
++static int irq = 9;
++#else
++static int digimatrix;
++static int it87_freq = 38; /* kHz */
++static int irq = IT87_CIR_DEFAULT_IRQ;
++#endif
++
++static unsigned long it87_bits_in_byte_out;
++static unsigned long it87_send_counter;
++static unsigned char it87_RXEN_mask = IT87_CIR_RCR_RXEN;
++
++#define RBUF_LEN 1024
++#define WBUF_LEN 1024
++
++#define LIRC_DRIVER_NAME "lirc_it87"
++
++/* timeout for sequences in jiffies (=5/100s) */
++/* must be longer than TIME_CONST */
++#define IT87_TIMEOUT (HZ*5/100)
++
++/* module parameters */
++static int debug;
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
++ fmt, ## args); \
++ } while (0)
++
++static int io = IT87_CIR_DEFAULT_IOBASE;
++/* receiver demodulator default: off */
++static int it87_enable_demodulator;
++
++static int timer_enabled;
++static DEFINE_SPINLOCK(timer_lock);
++static struct timer_list timerlist;
++/* time of last signal change detected */
++static struct timeval last_tv = {0, 0};
++/* time of last UART data ready interrupt */
++static struct timeval last_intr_tv = {0, 0};
++static int last_value;
++
++static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
++
++static DEFINE_SPINLOCK(hardware_lock);
++static DEFINE_SPINLOCK(dev_lock);
++
++static int rx_buf[RBUF_LEN];
++unsigned int rx_tail, rx_head;
++static int tx_buf[WBUF_LEN];
++
++static struct pnp_driver it87_pnp_driver;
++
++/* SECTION: Prototypes */
++
++/* Communication with user-space */
++static int lirc_open(struct inode *inode, struct file *file);
++static int lirc_close(struct inode *inode, struct file *file);
++static unsigned int lirc_poll(struct file *file, poll_table *wait);
++static ssize_t lirc_read(struct file *file, char *buf,
++ size_t count, loff_t *ppos);
++static ssize_t lirc_write(struct file *file, const char *buf,
++ size_t n, loff_t *pos);
++static int lirc_ioctl(struct inode *node, struct file *filep,
++ unsigned int cmd, unsigned long arg);
++static void add_read_queue(int flag, unsigned long val);
++static int init_chrdev(void);
++static void drop_chrdev(void);
++/* Hardware */
++static irqreturn_t it87_interrupt(int irq, void *dev_id);
++static void send_space(unsigned long len);
++static void send_pulse(unsigned long len);
++static void init_send(void);
++static void terminate_send(unsigned long len);
++static int init_hardware(void);
++static void drop_hardware(void);
++/* Initialisation */
++static int init_port(void);
++static void drop_port(void);
++
++
++/* SECTION: Communication with user-space */
++
++static int lirc_open(struct inode *inode, struct file *file)
++{
++ spin_lock(&dev_lock);
++ if (module_refcount(THIS_MODULE)) {
++ spin_unlock(&dev_lock);
++ return -EBUSY;
++ }
++ spin_unlock(&dev_lock);
++ return 0;
++}
++
++
++static int lirc_close(struct inode *inode, struct file *file)
++{
++ return 0;
++}
++
++
++static unsigned int lirc_poll(struct file *file, poll_table *wait)
++{
++ poll_wait(file, &lirc_read_queue, wait);
++ if (rx_head != rx_tail)
++ return POLLIN | POLLRDNORM;
++ return 0;
++}
++
++
++static ssize_t lirc_read(struct file *file, char *buf,
++ size_t count, loff_t *ppos)
++{
++ int n = 0;
++ int retval = 0;
++
++ while (n < count) {
++ if (file->f_flags & O_NONBLOCK && rx_head == rx_tail) {
++ retval = -EAGAIN;
++ break;
++ }
++ retval = wait_event_interruptible(lirc_read_queue,
++ rx_head != rx_tail);
++ if (retval)
++ break;
++
++ if (copy_to_user((void *) buf + n, (void *) (rx_buf + rx_head),
++ sizeof(int))) {
++ retval = -EFAULT;
++ break;
++ }
++ rx_head = (rx_head + 1) & (RBUF_LEN - 1);
++ n += sizeof(int);
++ }
++ if (n)
++ return n;
++ return retval;
++}
++
++
++static ssize_t lirc_write(struct file *file, const char *buf,
++ size_t n, loff_t *pos)
++{
++ int i = 0;
++
++ if (n % sizeof(int) || (n / sizeof(int)) > WBUF_LEN)
++ return -EINVAL;
++ if (copy_from_user(tx_buf, buf, n))
++ return -EFAULT;
++ n /= sizeof(int);
++ init_send();
++ while (1) {
++ if (i >= n)
++ break;
++ if (tx_buf[i])
++ send_pulse(tx_buf[i]);
++ i++;
++ if (i >= n)
++ break;
++ if (tx_buf[i])
++ send_space(tx_buf[i]);
++ i++;
++ }
++ terminate_send(tx_buf[i - 1]);
++ return n;
++}
++
++
++static int lirc_ioctl(struct inode *node, struct file *filep,
++ unsigned int cmd, unsigned long arg)
++{
++ int retval = 0;
++ unsigned long value = 0;
++ unsigned int ivalue;
++ unsigned long hw_flags;
++
++ if (cmd == LIRC_GET_FEATURES)
++ value = LIRC_CAN_SEND_PULSE |
++ LIRC_CAN_SET_SEND_CARRIER |
++ LIRC_CAN_REC_MODE2;
++ else if (cmd == LIRC_GET_SEND_MODE)
++ value = LIRC_MODE_PULSE;
++ else if (cmd == LIRC_GET_REC_MODE)
++ value = LIRC_MODE_MODE2;
++
++ switch (cmd) {
++ case LIRC_GET_FEATURES:
++ case LIRC_GET_SEND_MODE:
++ case LIRC_GET_REC_MODE:
++ retval = put_user(value, (unsigned long *) arg);
++ break;
++
++ case LIRC_SET_SEND_MODE:
++ case LIRC_SET_REC_MODE:
++ retval = get_user(value, (unsigned long *) arg);
++ break;
++
++ case LIRC_SET_SEND_CARRIER:
++ retval = get_user(ivalue, (unsigned int *) arg);
++ if (retval)
++ return retval;
++ ivalue /= 1000;
++ if (ivalue > IT87_CIR_FREQ_MAX ||
++ ivalue < IT87_CIR_FREQ_MIN)
++ return -EINVAL;
++
++ it87_freq = ivalue;
++
++ spin_lock_irqsave(&hardware_lock, hw_flags);
++ outb(((inb(io + IT87_CIR_TCR2) & IT87_CIR_TCR2_TXMPW) |
++ (it87_freq - IT87_CIR_FREQ_MIN) << 3),
++ io + IT87_CIR_TCR2);
++ spin_unlock_irqrestore(&hardware_lock, hw_flags);
++ dprintk("demodulation frequency: %d kHz\n", it87_freq);
++
++ break;
++
++ default:
++ retval = -EINVAL;
++ }
++
++ if (retval)
++ return retval;
++
++ if (cmd == LIRC_SET_REC_MODE) {
++ if (value != LIRC_MODE_MODE2)
++ retval = -ENOSYS;
++ } else if (cmd == LIRC_SET_SEND_MODE) {
++ if (value != LIRC_MODE_PULSE)
++ retval = -ENOSYS;
++ }
++ return retval;
++}
++
++static void add_read_queue(int flag, unsigned long val)
++{
++ unsigned int new_rx_tail;
++ int newval;
++
++ dprintk("add flag %d with val %lu\n", flag, val);
++
++ newval = val & PULSE_MASK;
++
++ /*
++ * statistically, pulses are ~TIME_CONST/2 too long. we could
++ * maybe make this more exact, but this is good enough
++ */
++ if (flag) {
++ /* pulse */
++ if (newval > TIME_CONST / 2)
++ newval -= TIME_CONST / 2;
++ else /* should not ever happen */
++ newval = 1;
++ newval |= PULSE_BIT;
++ } else
++ newval += TIME_CONST / 2;
++ new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1);
++ if (new_rx_tail == rx_head) {
++ dprintk("Buffer overrun.\n");
++ return;
++ }
++ rx_buf[rx_tail] = newval;
++ rx_tail = new_rx_tail;
++ wake_up_interruptible(&lirc_read_queue);
++}
++
++
++static struct file_operations lirc_fops = {
++ .owner = THIS_MODULE,
++ .read = lirc_read,
++ .write = lirc_write,
++ .poll = lirc_poll,
++ .ioctl = lirc_ioctl,
++ .open = lirc_open,
++ .release = lirc_close,
++};
++
++static int set_use_inc(void *data)
++{
++ return 0;
++}
++
++static void set_use_dec(void *data)
++{
++}
++
++static struct lirc_driver driver = {
++ .name = LIRC_DRIVER_NAME,
++ .minor = -1,
++ .code_length = 1,
++ .sample_rate = 0,
++ .data = NULL,
++ .add_to_buf = NULL,
++ .set_use_inc = set_use_inc,
++ .set_use_dec = set_use_dec,
++ .fops = &lirc_fops,
++ .dev = NULL,
++ .owner = THIS_MODULE,
++};
++
++
++#ifdef MODULE
++static int init_chrdev(void)
++{
++ driver.minor = lirc_register_driver(&driver);
++
++ if (driver.minor < 0) {
++ printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n");
++ return -EIO;
++ }
++ return 0;
++}
++
++
++static void drop_chrdev(void)
++{
++ lirc_unregister_driver(driver.minor);
++}
++#endif
++
++
++/* SECTION: Hardware */
++static long delta(struct timeval *tv1, struct timeval *tv2)
++{
++ unsigned long deltv;
++
++ deltv = tv2->tv_sec - tv1->tv_sec;
++ if (deltv > 15)
++ deltv = 0xFFFFFF;
++ else
++ deltv = deltv*1000000 + tv2->tv_usec - tv1->tv_usec;
++ return deltv;
++}
++
++static void it87_timeout(unsigned long data)
++{
++ unsigned long flags;
++
++ /* avoid interference with interrupt */
++ spin_lock_irqsave(&timer_lock, flags);
++
++ if (digimatrix) {
++ /* We have timed out. Disable the RX mechanism. */
++
++ outb((inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN) |
++ IT87_CIR_RCR_RXACT, io + IT87_CIR_RCR);
++ if (it87_RXEN_mask)
++ outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN,
++ io + IT87_CIR_RCR);
++ dprintk(" TIMEOUT\n");
++ timer_enabled = 0;
++
++ /* fifo clear */
++ outb(inb(io + IT87_CIR_TCR1) | IT87_CIR_TCR1_FIFOCLR,
++ io+IT87_CIR_TCR1);
++
++ } else {
++ /*
++ * if last received signal was a pulse, but receiving stopped
++ * within the 9 bit frame, we need to finish this pulse and
++ * simulate a signal change to from pulse to space. Otherwise
++ * upper layers will receive two sequences next time.
++ */
++
++ if (last_value) {
++ unsigned long pulse_end;
++
++ /* determine 'virtual' pulse end: */
++ pulse_end = delta(&last_tv, &last_intr_tv);
++ dprintk("timeout add %d for %lu usec\n",
++ last_value, pulse_end);
++ add_read_queue(last_value, pulse_end);
++ last_value = 0;
++ last_tv = last_intr_tv;
++ }
++ }
++ spin_unlock_irqrestore(&timer_lock, flags);
++}
++
++static irqreturn_t it87_interrupt(int irq, void *dev_id)
++{
++ unsigned char data;
++ struct timeval curr_tv;
++ static unsigned long deltv;
++ unsigned long deltintrtv;
++ unsigned long flags, hw_flags;
++ int iir, lsr;
++ int fifo = 0;
++ static char lastbit;
++ char bit;
++
++ /* Bit duration in microseconds */
++ const unsigned long bit_duration = 1000000ul /
++ (115200 / IT87_CIR_BAUDRATE_DIVISOR);
++
++
++ iir = inb(io + IT87_CIR_IIR);
++
++ switch (iir & IT87_CIR_IIR_IID) {
++ case 0x4:
++ case 0x6:
++ lsr = inb(io + IT87_CIR_RSR) & (IT87_CIR_RSR_RXFTO |
++ IT87_CIR_RSR_RXFBC);
++ fifo = lsr & IT87_CIR_RSR_RXFBC;
++ dprintk("iir: 0x%x fifo: 0x%x\n", iir, lsr);
++
++ /* avoid interference with timer */
++ spin_lock_irqsave(&timer_lock, flags);
++ spin_lock_irqsave(&hardware_lock, hw_flags);
++ if (digimatrix) {
++ static unsigned long acc_pulse;
++ static unsigned long acc_space;
++
++ do {
++ data = inb(io + IT87_CIR_DR);
++ data = ~data;
++ fifo--;
++ if (data != 0x00) {
++ if (timer_enabled)
++ del_timer(&timerlist);
++ /*
++ * start timer for end of
++ * sequence detection
++ */
++ timerlist.expires = jiffies +
++ IT87_TIMEOUT;
++ add_timer(&timerlist);
++ timer_enabled = 1;
++ }
++ /* Loop through */
++ for (bit = 0; bit < 8; ++bit) {
++ if ((data >> bit) & 1) {
++ ++acc_pulse;
++ if (lastbit == 0) {
++ add_read_queue(0,
++ acc_space *
++ bit_duration);
++ acc_space = 0;
++ }
++ } else {
++ ++acc_space;
++ if (lastbit == 1) {
++ add_read_queue(1,
++ acc_pulse *
++ bit_duration);
++ acc_pulse = 0;
++ }
++ }
++ lastbit = (data >> bit) & 1;
++ }
++
++ } while (fifo != 0);
++ } else { /* Normal Operation */
++ do {
++ del_timer(&timerlist);
++ data = inb(io + IT87_CIR_DR);
++
++ dprintk("data=%02x\n", data);
++ do_gettimeofday(&curr_tv);
++ deltv = delta(&last_tv, &curr_tv);
++ deltintrtv = delta(&last_intr_tv, &curr_tv);
++
++ dprintk("t %lu , d %d\n",
++ deltintrtv, (int)data);
++
++ /*
++ * if nothing came in last 2 cycles,
++ * it was gap
++ */
++ if (deltintrtv > TIME_CONST * 2) {
++ if (last_value) {
++ dprintk("GAP\n");
++
++ /* simulate signal change */
++ add_read_queue(last_value,
++ deltv -
++ deltintrtv);
++ last_value = 0;
++ last_tv.tv_sec =
++ last_intr_tv.tv_sec;
++ last_tv.tv_usec =
++ last_intr_tv.tv_usec;
++ deltv = deltintrtv;
++ }
++ }
++ data = 1;
++ if (data ^ last_value) {
++ /*
++ * deltintrtv > 2*TIME_CONST,
++ * remember ? the other case is
++ * timeout
++ */
++ add_read_queue(last_value,
++ deltv-TIME_CONST);
++ last_value = data;
++ last_tv = curr_tv;
++ if (last_tv.tv_usec >= TIME_CONST)
++ last_tv.tv_usec -= TIME_CONST;
++ else {
++ last_tv.tv_sec--;
++ last_tv.tv_usec += 1000000 -
++ TIME_CONST;
++ }
++ }
++ last_intr_tv = curr_tv;
++ if (data) {
++ /*
++ * start timer for end of
++ * sequence detection
++ */
++ timerlist.expires =
++ jiffies + IT87_TIMEOUT;
++ add_timer(&timerlist);
++ }
++ outb((inb(io + IT87_CIR_RCR) &
++ ~IT87_CIR_RCR_RXEN) |
++ IT87_CIR_RCR_RXACT,
++ io + IT87_CIR_RCR);
++ if (it87_RXEN_mask)
++ outb(inb(io + IT87_CIR_RCR) |
++ IT87_CIR_RCR_RXEN,
++ io + IT87_CIR_RCR);
++ fifo--;
++ } while (fifo != 0);
++ }
++ spin_unlock_irqrestore(&hardware_lock, hw_flags);
++ spin_unlock_irqrestore(&timer_lock, flags);
++
++ return IRQ_RETVAL(IRQ_HANDLED);
++
++ default:
++ /* not our irq */
++ dprintk("unknown IRQ (shouldn't happen) !!\n");
++ return IRQ_RETVAL(IRQ_NONE);
++ }
++}
++
++
++static void send_it87(unsigned long len, unsigned long stime,
++ unsigned char send_byte, unsigned int count_bits)
++{
++ long count = len / stime;
++ long time_left = 0;
++ static unsigned char byte_out;
++ unsigned long hw_flags;
++
++ dprintk("%s: len=%ld, sb=%d\n", __func__, len, send_byte);
++
++ time_left = (long)len - (long)count * (long)stime;
++ count += ((2 * time_left) / stime);
++ while (count) {
++ long i = 0;
++ for (i = 0; i < count_bits; i++) {
++ byte_out = (byte_out << 1) | (send_byte & 1);
++ it87_bits_in_byte_out++;
++ }
++ if (it87_bits_in_byte_out == 8) {
++ dprintk("out=0x%x, tsr_txfbc: 0x%x\n",
++ byte_out,
++ inb(io + IT87_CIR_TSR) &
++ IT87_CIR_TSR_TXFBC);
++
++ while ((inb(io + IT87_CIR_TSR) &
++ IT87_CIR_TSR_TXFBC) >= IT87_CIR_FIFO_SIZE)
++ ;
++
++ spin_lock_irqsave(&hardware_lock, hw_flags);
++ outb(byte_out, io + IT87_CIR_DR);
++ spin_unlock_irqrestore(&hardware_lock, hw_flags);
++
++ it87_bits_in_byte_out = 0;
++ it87_send_counter++;
++ byte_out = 0;
++ }
++ count--;
++ }
++}
++
++
++/*TODO: maybe exchange space and pulse because it8705 only modulates 0-bits */
++
++static void send_space(unsigned long len)
++{
++ send_it87(len, TIME_CONST, IT87_CIR_SPACE, IT87_CIR_BAUDRATE_DIVISOR);
++}
++
++static void send_pulse(unsigned long len)
++{
++ send_it87(len, TIME_CONST, IT87_CIR_PULSE, IT87_CIR_BAUDRATE_DIVISOR);
++}
++
++
++static void init_send()
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&hardware_lock, flags);
++ /* RXEN=0: receiver disable */
++ it87_RXEN_mask = 0;
++ outb(inb(io + IT87_CIR_RCR) & ~IT87_CIR_RCR_RXEN,
++ io + IT87_CIR_RCR);
++ spin_unlock_irqrestore(&hardware_lock, flags);
++ it87_bits_in_byte_out = 0;
++ it87_send_counter = 0;
++}
++
++
++static void terminate_send(unsigned long len)
++{
++ unsigned long flags;
++ unsigned long last = 0;
++
++ last = it87_send_counter;
++ /* make sure all necessary data has been sent */
++ while (last == it87_send_counter)
++ send_space(len);
++ /* wait until all data sent */
++ while ((inb(io + IT87_CIR_TSR) & IT87_CIR_TSR_TXFBC) != 0)
++ ;
++ /* then re-enable receiver */
++ spin_lock_irqsave(&hardware_lock, flags);
++ it87_RXEN_mask = IT87_CIR_RCR_RXEN;
++ outb(inb(io + IT87_CIR_RCR) | IT87_CIR_RCR_RXEN,
++ io + IT87_CIR_RCR);
++ spin_unlock_irqrestore(&hardware_lock, flags);
++}
++
++
++static int init_hardware(void)
++{
++ unsigned long flags;
++ unsigned char it87_rcr = 0;
++
++ spin_lock_irqsave(&hardware_lock, flags);
++ /* init cir-port */
++ /* enable r/w-access to Baudrate-Register */
++ outb(IT87_CIR_IER_BR, io + IT87_CIR_IER);
++ outb(IT87_CIR_BAUDRATE_DIVISOR % 0x100, io+IT87_CIR_BDLR);
++ outb(IT87_CIR_BAUDRATE_DIVISOR / 0x100, io+IT87_CIR_BDHR);
++ /* Baudrate Register off, define IRQs: Input only */
++ if (digimatrix) {
++ outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RFOIE, io + IT87_CIR_IER);
++ /* RX: HCFS=0, RXDCR = 001b (33,75..38,25 kHz), RXEN=1 */
++ } else {
++ outb(IT87_CIR_IER_IEC | IT87_CIR_IER_RDAIE, io + IT87_CIR_IER);
++ /* RX: HCFS=0, RXDCR = 001b (35,6..40,3 kHz), RXEN=1 */
++ }
++ it87_rcr = (IT87_CIR_RCR_RXEN & it87_RXEN_mask) | 0x1;
++ if (it87_enable_demodulator)
++ it87_rcr |= IT87_CIR_RCR_RXEND;
++ outb(it87_rcr, io + IT87_CIR_RCR);
++ if (digimatrix) {
++ /* Set FIFO depth to 1 byte, and disable TX */
++ outb(inb(io + IT87_CIR_TCR1) | 0x00,
++ io + IT87_CIR_TCR1);
++
++ /*
++ * TX: it87_freq (36kHz), 'reserved' sensitivity
++ * setting (0x00)
++ */
++ outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x00,
++ io + IT87_CIR_TCR2);
++ } else {
++ /* TX: 38kHz, 13,3us (pulse-width) */
++ outb(((it87_freq - IT87_CIR_FREQ_MIN) << 3) | 0x06,
++ io + IT87_CIR_TCR2);
++ }
++ spin_unlock_irqrestore(&hardware_lock, flags);
++ return 0;
++}
++
++
++static void drop_hardware(void)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&hardware_lock, flags);
++ disable_irq(irq);
++ /* receiver disable */
++ it87_RXEN_mask = 0;
++ outb(0x1, io + IT87_CIR_RCR);
++ /* turn off irqs */
++ outb(0, io + IT87_CIR_IER);
++ /* fifo clear */
++ outb(IT87_CIR_TCR1_FIFOCLR, io+IT87_CIR_TCR1);
++ /* reset */
++ outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER);
++ enable_irq(irq);
++ spin_unlock_irqrestore(&hardware_lock, flags);
++}
++
++
++static unsigned char it87_read(unsigned char port)
++{
++ outb(port, IT87_ADRPORT);
++ return inb(IT87_DATAPORT);
++}
++
++
++static void it87_write(unsigned char port, unsigned char data)
++{
++ outb(port, IT87_ADRPORT);
++ outb(data, IT87_DATAPORT);
++}
++
++
++/* SECTION: Initialisation */
++
++static int init_port(void)
++{
++ unsigned long hw_flags;
++ int retval = 0;
++
++ unsigned char init_bytes[4] = IT87_INIT;
++ unsigned char it87_chipid = 0;
++ unsigned char ldn = 0;
++ unsigned int it87_io = 0;
++ unsigned int it87_irq = 0;
++
++ /* Enter MB PnP Mode */
++ outb(init_bytes[0], IT87_ADRPORT);
++ outb(init_bytes[1], IT87_ADRPORT);
++ outb(init_bytes[2], IT87_ADRPORT);
++ outb(init_bytes[3], IT87_ADRPORT);
++
++ /* 8712 or 8705 ? */
++ it87_chipid = it87_read(IT87_CHIP_ID1);
++ if (it87_chipid != 0x87) {
++ retval = -ENXIO;
++ return retval;
++ }
++ it87_chipid = it87_read(IT87_CHIP_ID2);
++ if ((it87_chipid != 0x05) &&
++ (it87_chipid != 0x12) &&
++ (it87_chipid != 0x18) &&
++ (it87_chipid != 0x20)) {
++ printk(KERN_INFO LIRC_DRIVER_NAME
++ ": no IT8704/05/12/18/20 found (claimed IT87%02x), "
++ "exiting..\n", it87_chipid);
++ retval = -ENXIO;
++ return retval;
++ }
++ printk(KERN_INFO LIRC_DRIVER_NAME
++ ": found IT87%02x.\n",
++ it87_chipid);
++
++ /* get I/O-Port and IRQ */
++ if (it87_chipid == 0x12 || it87_chipid == 0x18)
++ ldn = IT8712_CIR_LDN;
++ else
++ ldn = IT8705_CIR_LDN;
++ it87_write(IT87_LDN, ldn);
++
++ it87_io = it87_read(IT87_CIR_BASE_MSB) * 256 +
++ it87_read(IT87_CIR_BASE_LSB);
++ if (it87_io == 0) {
++ if (io == 0)
++ io = IT87_CIR_DEFAULT_IOBASE;
++ printk(KERN_INFO LIRC_DRIVER_NAME
++ ": set default io 0x%x\n",
++ io);
++ it87_write(IT87_CIR_BASE_MSB, io / 0x100);
++ it87_write(IT87_CIR_BASE_LSB, io % 0x100);
++ } else
++ io = it87_io;
++
++ it87_irq = it87_read(IT87_CIR_IRQ);
++ if (digimatrix || it87_irq == 0) {
++ if (irq == 0)
++ irq = IT87_CIR_DEFAULT_IRQ;
++ printk(KERN_INFO LIRC_DRIVER_NAME
++ ": set default irq 0x%x\n",
++ irq);
++ it87_write(IT87_CIR_IRQ, irq);
++ } else
++ irq = it87_irq;
++
++ spin_lock_irqsave(&hardware_lock, hw_flags);
++ /* reset */
++ outb(IT87_CIR_IER_RESET, io+IT87_CIR_IER);
++ /* fifo clear */
++ outb(IT87_CIR_TCR1_FIFOCLR |
++ /* IT87_CIR_TCR1_ILE | */
++ IT87_CIR_TCR1_TXRLE |
++ IT87_CIR_TCR1_TXENDF, io+IT87_CIR_TCR1);
++ spin_unlock_irqrestore(&hardware_lock, hw_flags);
++
++ /* get I/O port access and IRQ line */
++ if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) {
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": i/o port 0x%.4x already in use.\n", io);
++ /* Leaving MB PnP Mode */
++ it87_write(IT87_CFGCTRL, 0x2);
++ return -EBUSY;
++ }
++
++ /* activate CIR-Device */
++ it87_write(IT87_CIR_ACT, 0x1);
++
++ /* Leaving MB PnP Mode */
++ it87_write(IT87_CFGCTRL, 0x2);
++
++ retval = request_irq(irq, it87_interrupt, 0 /*IRQF_DISABLED*/,
++ LIRC_DRIVER_NAME, NULL);
++ if (retval < 0) {
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": IRQ %d already in use.\n",
++ irq);
++ release_region(io, 8);
++ return retval;
++ }
++
++ printk(KERN_INFO LIRC_DRIVER_NAME
++ ": I/O port 0x%.4x, IRQ %d.\n", io, irq);
++
++ init_timer(&timerlist);
++ timerlist.function = it87_timeout;
++ timerlist.data = 0xabadcafe;
++
++ return 0;
++}
++
++
++static void drop_port(void)
++{
++#if 0
++ unsigned char init_bytes[4] = IT87_INIT;
++
++ /* Enter MB PnP Mode */
++ outb(init_bytes[0], IT87_ADRPORT);
++ outb(init_bytes[1], IT87_ADRPORT);
++ outb(init_bytes[2], IT87_ADRPORT);
++ outb(init_bytes[3], IT87_ADRPORT);
++
++ /* deactivate CIR-Device */
++ it87_write(IT87_CIR_ACT, 0x0);
++
++ /* Leaving MB PnP Mode */
++ it87_write(IT87_CFGCTRL, 0x2);
++#endif
++
++ del_timer_sync(&timerlist);
++ free_irq(irq, NULL);
++ release_region(io, 8);
++}
++
++
++static int init_lirc_it87(void)
++{
++ int retval;
++
++ init_waitqueue_head(&lirc_read_queue);
++ retval = init_port();
++ if (retval < 0)
++ return retval;
++ init_hardware();
++ printk(KERN_INFO LIRC_DRIVER_NAME ": Installed.\n");
++ return 0;
++}
++
++static int it87_probe(struct pnp_dev *pnp_dev,
++ const struct pnp_device_id *dev_id)
++{
++ int retval;
++
++ driver.dev = &pnp_dev->dev;
++
++ retval = init_chrdev();
++ if (retval < 0)
++ return retval;
++
++ retval = init_lirc_it87();
++ if (retval)
++ goto init_lirc_it87_failed;
++
++ return 0;
++
++init_lirc_it87_failed:
++ drop_chrdev();
++
++ return retval;
++}
++
++static int __init lirc_it87_init(void)
++{
++ return pnp_register_driver(&it87_pnp_driver);
++}
++
++
++static void __exit lirc_it87_exit(void)
++{
++ drop_hardware();
++ drop_chrdev();
++ drop_port();
++ pnp_unregister_driver(&it87_pnp_driver);
++ printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
++}
++
++/* SECTION: PNP for ITE8704/18 */
++
++static const struct pnp_device_id pnp_dev_table[] = {
++ {"ITE8704", 0},
++ {}
++};
++
++MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
++
++static struct pnp_driver it87_pnp_driver = {
++ .name = LIRC_DRIVER_NAME,
++ .id_table = pnp_dev_table,
++ .probe = it87_probe,
++};
++
++module_init(lirc_it87_init);
++module_exit(lirc_it87_exit);
++
++MODULE_DESCRIPTION("LIRC driver for ITE IT8704/05/12/18/20 CIR port");
++MODULE_AUTHOR("Hans-Gunter Lutke Uphues");
++MODULE_LICENSE("GPL");
++
++module_param(io, int, S_IRUGO);
++MODULE_PARM_DESC(io, "I/O base address (default: 0x310)");
++
++module_param(irq, int, S_IRUGO);
++#ifdef LIRC_IT87_DIGIMATRIX
++MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 9)");
++#else
++MODULE_PARM_DESC(irq, "Interrupt (1,3-12) (default: 7)");
++#endif
++
++module_param(it87_enable_demodulator, bool, S_IRUGO);
++MODULE_PARM_DESC(it87_enable_demodulator,
++ "Receiver demodulator enable/disable (1/0), default: 0");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
++
++module_param(digimatrix, bool, S_IRUGO | S_IWUSR);
++#ifdef LIRC_IT87_DIGIMATRIX
++MODULE_PARM_DESC(digimatrix,
++ "Asus Digimatrix it87 compat. enable/disable (1/0), default: 1");
++#else
++MODULE_PARM_DESC(digimatrix,
++ "Asus Digimatrix it87 compat. enable/disable (1/0), default: 0");
++#endif
++
++
++module_param(it87_freq, int, S_IRUGO);
++#ifdef LIRC_IT87_DIGIMATRIX
++MODULE_PARM_DESC(it87_freq,
++ "Carrier demodulator frequency (kHz), (default: 36)");
++#else
++MODULE_PARM_DESC(it87_freq,
++ "Carrier demodulator frequency (kHz), (default: 38)");
++#endif
+diff --git a/drivers/input/lirc/lirc_it87.h b/drivers/input/lirc/lirc_it87.h
+new file mode 100644
+index 0000000..cf021c8
+--- /dev/null
++++ b/drivers/input/lirc/lirc_it87.h
+@@ -0,0 +1,116 @@
++/* lirc_it87.h */
++/* SECTION: Definitions */
++
++/********************************* ITE IT87xx ************************/
++
++/* based on the following documentation from ITE:
++ a) IT8712F Preliminary CIR Programming Guide V0.1
++ b) IT8705F Simple LPC I/O Preliminary Specification V0.3
++ c) IT8712F EC-LPC I/O Preliminary Specification V0.5
++*/
++
++/* IT8712/05 Ports: */
++#define IT87_ADRPORT 0x2e
++#define IT87_DATAPORT 0x2f
++#define IT87_INIT {0x87, 0x01, 0x55, 0x55}
++
++/* alternate Ports: */
++/*
++#define IT87_ADRPORT 0x4e
++#define IT87_DATAPORT 0x4f
++#define IT87_INIT {0x87, 0x01, 0x55, 0xaa}
++ */
++
++/* IT8712/05 Registers */
++#define IT87_CFGCTRL 0x2
++#define IT87_LDN 0x7
++#define IT87_CHIP_ID1 0x20
++#define IT87_CHIP_ID2 0x21
++#define IT87_CFG_VERSION 0x22
++#define IT87_SWSUSPEND 0x23
++
++#define IT8712_CIR_LDN 0xa
++#define IT8705_CIR_LDN 0x7
++
++/* CIR Configuration Registers: */
++#define IT87_CIR_ACT 0x30
++#define IT87_CIR_BASE_MSB 0x60
++#define IT87_CIR_BASE_LSB 0x61
++#define IT87_CIR_IRQ 0x70
++#define IT87_CIR_CONFIG 0xf0
++
++/* List of IT87_CIR registers: offset to BaseAddr */
++#define IT87_CIR_DR 0
++#define IT87_CIR_IER 1
++#define IT87_CIR_RCR 2
++#define IT87_CIR_TCR1 3
++#define IT87_CIR_TCR2 4
++#define IT87_CIR_TSR 5
++#define IT87_CIR_RSR 6
++#define IT87_CIR_BDLR 5
++#define IT87_CIR_BDHR 6
++#define IT87_CIR_IIR 7
++
++/* Bit Definition */
++/* IER: */
++#define IT87_CIR_IER_TM_EN 0x80
++#define IT87_CIR_IER_RESEVED 0x40
++#define IT87_CIR_IER_RESET 0x20
++#define IT87_CIR_IER_BR 0x10
++#define IT87_CIR_IER_IEC 0x8
++#define IT87_CIR_IER_RFOIE 0x4
++#define IT87_CIR_IER_RDAIE 0x2
++#define IT87_CIR_IER_TLDLIE 0x1
++
++/* RCR: */
++#define IT87_CIR_RCR_RDWOS 0x80
++#define IT87_CIR_RCR_HCFS 0x40
++#define IT87_CIR_RCR_RXEN 0x20
++#define IT87_CIR_RCR_RXEND 0x10
++#define IT87_CIR_RCR_RXACT 0x8
++#define IT87_CIR_RCR_RXDCR 0x7
++
++/* TCR1: */
++#define IT87_CIR_TCR1_FIFOCLR 0x80
++#define IT87_CIR_TCR1_ILE 0x40
++#define IT87_CIR_TCR1_FIFOTL 0x30
++#define IT87_CIR_TCR1_TXRLE 0x8
++#define IT87_CIR_TCR1_TXENDF 0x4
++#define IT87_CIR_TCR1_TXMPM 0x3
++
++/* TCR2: */
++#define IT87_CIR_TCR2_CFQ 0xf8
++#define IT87_CIR_TCR2_TXMPW 0x7
++
++/* TSR: */
++#define IT87_CIR_TSR_RESERVED 0xc0
++#define IT87_CIR_TSR_TXFBC 0x3f
++
++/* RSR: */
++#define IT87_CIR_RSR_RXFTO 0x80
++#define IT87_CIR_RSR_RESERVED 0x40
++#define IT87_CIR_RSR_RXFBC 0x3f
++
++/* IIR: */
++#define IT87_CIR_IIR_RESERVED 0xf8
++#define IT87_CIR_IIR_IID 0x6
++#define IT87_CIR_IIR_IIP 0x1
++
++/* TM: */
++#define IT87_CIR_TM_IL_SEL 0x80
++#define IT87_CIR_TM_RESERVED 0x40
++#define IT87_CIR_TM_TM_REG 0x3f
++
++#define IT87_CIR_FIFO_SIZE 32
++
++/* Baudratedivisor for IT87: power of 2: only 1,2,4 or 8) */
++#define IT87_CIR_BAUDRATE_DIVISOR 0x1
++#define IT87_CIR_DEFAULT_IOBASE 0x310
++#define IT87_CIR_DEFAULT_IRQ 0x7
++#define IT87_CIR_SPACE 0x00
++#define IT87_CIR_PULSE 0xff
++#define IT87_CIR_FREQ_MIN 27
++#define IT87_CIR_FREQ_MAX 58
++#define TIME_CONST (IT87_CIR_BAUDRATE_DIVISOR * 8000000ul / 115200ul)
++
++/********************************* ITE IT87xx ************************/
+diff --git a/drivers/input/lirc/lirc_ite8709.c b/drivers/input/lirc/lirc_ite8709.c
+new file mode 100644
+index 0000000..4c3d3ad
+--- /dev/null
++++ b/drivers/input/lirc/lirc_ite8709.c
+@@ -0,0 +1,540 @@
++/*
++ * LIRC driver for ITE8709 CIR port
++ *
++ * Copyright (C) 2008 Grégory Lardière <spmf2004-lirc@yahoo.fr>
++ *
++ * This program is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU General Public License as
++ * published by the Free Software Foundation; either version 2 of the
++ * License, or (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
++ * USA
++ */
++
++#include <linux/module.h>
++#include <linux/interrupt.h>
++#include <linux/sched.h>
++#include <linux/delay.h>
++#include <linux/pnp.h>
++#include <linux/io.h>
++
++#include <linux/lirc.h>
++#include "lirc_dev.h"
++
++#define LIRC_DRIVER_NAME "lirc_ite8709"
++
++#define BUF_CHUNK_SIZE sizeof(int)
++#define BUF_SIZE (128*BUF_CHUNK_SIZE)
++
++/*
++ * The ITE8709 device seems to be the combination of IT8512 superIO chip and
++ * a specific firmware running on the IT8512's embedded micro-controller.
++ * In addition of the embedded micro-controller, the IT8512 chip contains a
++ * CIR module and several other modules. A few modules are directly accessible
++ * by the host CPU, but most of them are only accessible by the
++ * micro-controller. The CIR module is only accessible by the micro-controller.
++ * The battery-backed SRAM module is accessible by the host CPU and the
++ * micro-controller. So one of the MC's firmware role is to act as a bridge
++ * between the host CPU and the CIR module. The firmware implements a kind of
++ * communication protocol using the SRAM module as a shared memory. The IT8512
++ * specification is publicly available on ITE's web site, but the communication
++ * protocol is not, so it was reverse-engineered.
++ */
++
++/* ITE8709 Registers addresses and values (reverse-engineered) */
++#define ITE8709_MODE 0x1a
++#define ITE8709_REG_ADR 0x1b
++#define ITE8709_REG_VAL 0x1c
++#define ITE8709_IIR 0x1e /* Interrupt identification register */
++#define ITE8709_RFSR 0x1f /* Receiver FIFO status register */
++#define ITE8709_FIFO_START 0x20
++
++#define ITE8709_MODE_READY 0X00
++#define ITE8709_MODE_WRITE 0X01
++#define ITE8709_MODE_READ 0X02
++#define ITE8709_IIR_RDAI 0x02 /* Receiver data available interrupt */
++#define ITE8709_IIR_RFOI 0x04 /* Receiver FIFO overrun interrupt */
++#define ITE8709_RFSR_MASK 0x3f /* FIFO byte count mask */
++
++/*
++ * IT8512 CIR-module registers addresses and values
++ * (from IT8512 E/F specification v0.4.1)
++ */
++#define IT8512_REG_MSTCR 0x01 /* Master control register */
++#define IT8512_REG_IER 0x02 /* Interrupt enable register */
++#define IT8512_REG_CFR 0x04 /* Carrier frequency register */
++#define IT8512_REG_RCR 0x05 /* Receive control register */
++#define IT8512_REG_BDLR 0x08 /* Baud rate divisor low byte register */
++#define IT8512_REG_BDHR 0x09 /* Baud rate divisor high byte register */
++
++#define IT8512_MSTCR_RESET 0x01 /* Reset registers to default value */
++#define IT8512_MSTCR_FIFOCLR 0x02 /* Clear FIFO */
++#define IT8512_MSTCR_FIFOTL_7 0x04 /* FIFO threshold level : 7 */
++#define IT8512_MSTCR_FIFOTL_25 0x0c /* FIFO threshold level : 25 */
++#define IT8512_IER_RDAIE 0x02 /* Enable data interrupt request */
++#define IT8512_IER_RFOIE 0x04 /* Enable FIFO overrun interrupt req */
++#define IT8512_IER_IEC 0x80 /* Enable interrupt request */
++#define IT8512_CFR_CF_36KHZ 0x09 /* Carrier freq : low speed, 36kHz */
++#define IT8512_RCR_RXDCR_1 0x01 /* Demodulation carrier range : 1 */
++#define IT8512_RCR_RXACT 0x08 /* Receiver active */
++#define IT8512_RCR_RXEN 0x80 /* Receiver enable */
++#define IT8512_BDR_6 6 /* Baud rate divisor : 6 */
++
++/* Actual values used by this driver */
++#define CFG_FIFOTL IT8512_MSTCR_FIFOTL_25
++#define CFG_CR_FREQ IT8512_CFR_CF_36KHZ
++#define CFG_DCR IT8512_RCR_RXDCR_1
++#define CFG_BDR IT8512_BDR_6
++#define CFG_TIMEOUT 100000 /* Rearm interrupt when a space is > 100 ms */
++
++static int debug;
++
++struct ite8709_device {
++ int use_count;
++ int io;
++ int irq;
++ spinlock_t hardware_lock;
++ unsigned long long acc_pulse;
++ unsigned long long acc_space;
++ char lastbit;
++ struct timeval last_tv;
++ struct lirc_driver driver;
++ struct tasklet_struct tasklet;
++ char force_rearm;
++ char rearmed;
++ char device_busy;
++};
++
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
++ fmt, ## args); \
++ } while (0)
++
++
++static unsigned char ite8709_read(struct ite8709_device *dev,
++ unsigned char port)
++{
++ outb(port, dev->io);
++ return inb(dev->io+1);
++}
++
++static void ite8709_write(struct ite8709_device *dev, unsigned char port,
++ unsigned char data)
++{
++ outb(port, dev->io);
++ outb(data, dev->io+1);
++}
++
++static void ite8709_wait_device(struct ite8709_device *dev)
++{
++ int i = 0;
++ /*
++ * loop until device tells it's ready to continue
++ * iterations count is usually ~750 but can sometimes achieve 13000
++ */
++ for (i = 0; i < 15000; i++) {
++ udelay(2);
++ if (ite8709_read(dev, ITE8709_MODE) == ITE8709_MODE_READY)
++ break;
++ }
++}
++
++static void ite8709_write_register(struct ite8709_device *dev,
++ unsigned char reg_adr, unsigned char reg_value)
++{
++ ite8709_wait_device(dev);
++
++ ite8709_write(dev, ITE8709_REG_VAL, reg_value);
++ ite8709_write(dev, ITE8709_REG_ADR, reg_adr);
++ ite8709_write(dev, ITE8709_MODE, ITE8709_MODE_WRITE);
++}
++
++static void ite8709_init_hardware(struct ite8709_device *dev)
++{
++ spin_lock_irq(&dev->hardware_lock);
++ dev->device_busy = 1;
++ spin_unlock_irq(&dev->hardware_lock);
++
++ ite8709_write_register(dev, IT8512_REG_BDHR, (CFG_BDR >> 8) & 0xff);
++ ite8709_write_register(dev, IT8512_REG_BDLR, CFG_BDR & 0xff);
++ ite8709_write_register(dev, IT8512_REG_CFR, CFG_CR_FREQ);
++ ite8709_write_register(dev, IT8512_REG_IER,
++ IT8512_IER_IEC | IT8512_IER_RFOIE | IT8512_IER_RDAIE);
++ ite8709_write_register(dev, IT8512_REG_RCR, CFG_DCR);
++ ite8709_write_register(dev, IT8512_REG_MSTCR,
++ CFG_FIFOTL | IT8512_MSTCR_FIFOCLR);
++ ite8709_write_register(dev, IT8512_REG_RCR,
++ IT8512_RCR_RXEN | IT8512_RCR_RXACT | CFG_DCR);
++
++ spin_lock_irq(&dev->hardware_lock);
++ dev->device_busy = 0;
++ spin_unlock_irq(&dev->hardware_lock);
++
++ tasklet_enable(&dev->tasklet);
++}
++
++static void ite8709_drop_hardware(struct ite8709_device *dev)
++{
++ tasklet_disable(&dev->tasklet);
++
++ spin_lock_irq(&dev->hardware_lock);
++ dev->device_busy = 1;
++ spin_unlock_irq(&dev->hardware_lock);
++
++ ite8709_write_register(dev, IT8512_REG_RCR, 0);
++ ite8709_write_register(dev, IT8512_REG_MSTCR,
++ IT8512_MSTCR_RESET | IT8512_MSTCR_FIFOCLR);
++
++ spin_lock_irq(&dev->hardware_lock);
++ dev->device_busy = 0;
++ spin_unlock_irq(&dev->hardware_lock);
++}
++
++static int ite8709_set_use_inc(void *data)
++{
++ struct ite8709_device *dev;
++ dev = data;
++ if (dev->use_count == 0)
++ ite8709_init_hardware(dev);
++ dev->use_count++;
++ return 0;
++}
++
++static void ite8709_set_use_dec(void *data)
++{
++ struct ite8709_device *dev;
++ dev = data;
++ dev->use_count--;
++ if (dev->use_count == 0)
++ ite8709_drop_hardware(dev);
++}
++
++static void ite8709_add_read_queue(struct ite8709_device *dev, int flag,
++ unsigned long long val)
++{
++ int value;
++
++ dprintk("add a %llu usec %s\n", val, flag ? "pulse" : "space");
++
++ value = (val > PULSE_MASK) ? PULSE_MASK : val;
++ if (flag)
++ value |= PULSE_BIT;
++
++ if (!lirc_buffer_full(dev->driver.rbuf)) {
++ lirc_buffer_write(dev->driver.rbuf, (void *) &value);
++ wake_up(&dev->driver.rbuf->wait_poll);
++ }
++}
++
++static irqreturn_t ite8709_interrupt(int irq, void *dev_id)
++{
++ unsigned char data;
++ int iir, rfsr, i;
++ int fifo = 0;
++ char bit;
++ struct timeval curr_tv;
++
++ /* Bit duration in microseconds */
++ const unsigned long bit_duration = 1000000ul / (115200 / CFG_BDR);
++
++ struct ite8709_device *dev;
++ dev = dev_id;
++
++ /*
++ * If device is busy, we simply discard data because we are in one of
++ * these two cases : shutting down or rearming the device, so this
++ * doesn't really matter and this avoids waiting too long in IRQ ctx
++ */
++ spin_lock(&dev->hardware_lock);
++ if (dev->device_busy) {
++ spin_unlock(&dev->hardware_lock);
++ return IRQ_RETVAL(IRQ_HANDLED);
++ }
++
++ iir = ite8709_read(dev, ITE8709_IIR);
++
++ switch (iir) {
++ case ITE8709_IIR_RFOI:
++ dprintk("fifo overrun, scheduling forced rearm just in case\n");
++ dev->force_rearm = 1;
++ tasklet_schedule(&dev->tasklet);
++ spin_unlock(&dev->hardware_lock);
++ return IRQ_RETVAL(IRQ_HANDLED);
++
++ case ITE8709_IIR_RDAI:
++ rfsr = ite8709_read(dev, ITE8709_RFSR);
++ fifo = rfsr & ITE8709_RFSR_MASK;
++ if (fifo > 32)
++ fifo = 32;
++ dprintk("iir: 0x%x rfsr: 0x%x fifo: %d\n", iir, rfsr, fifo);
++
++ if (dev->rearmed) {
++ do_gettimeofday(&curr_tv);
++ dev->acc_space += 1000000ull
++ * (curr_tv.tv_sec - dev->last_tv.tv_sec)
++ + (curr_tv.tv_usec - dev->last_tv.tv_usec);
++ dev->rearmed = 0;
++ }
++ for (i = 0; i < fifo; i++) {
++ data = ite8709_read(dev, i+ITE8709_FIFO_START);
++ data = ~data;
++ /* Loop through */
++ for (bit = 0; bit < 8; ++bit) {
++ if ((data >> bit) & 1) {
++ dev->acc_pulse += bit_duration;
++ if (dev->lastbit == 0) {
++ ite8709_add_read_queue(dev, 0,
++ dev->acc_space);
++ dev->acc_space = 0;
++ }
++ } else {
++ dev->acc_space += bit_duration;
++ if (dev->lastbit == 1) {
++ ite8709_add_read_queue(dev, 1,
++ dev->acc_pulse);
++ dev->acc_pulse = 0;
++ }
++ }
++ dev->lastbit = (data >> bit) & 1;
++ }
++ }
++ ite8709_write(dev, ITE8709_RFSR, 0);
++
++ if (dev->acc_space > CFG_TIMEOUT) {
++ dprintk("scheduling rearm IRQ\n");
++ do_gettimeofday(&dev->last_tv);
++ dev->force_rearm = 0;
++ tasklet_schedule(&dev->tasklet);
++ }
++
++ spin_unlock(&dev->hardware_lock);
++ return IRQ_RETVAL(IRQ_HANDLED);
++
++ default:
++ /* not our irq */
++ dprintk("unknown IRQ (shouldn't happen) !!\n");
++ spin_unlock(&dev->hardware_lock);
++ return IRQ_RETVAL(IRQ_NONE);
++ }
++}
++
++static void ite8709_rearm_irq(unsigned long data)
++{
++ struct ite8709_device *dev;
++ unsigned long flags;
++ dev = (struct ite8709_device *) data;
++
++ spin_lock_irqsave(&dev->hardware_lock, flags);
++ dev->device_busy = 1;
++ spin_unlock_irqrestore(&dev->hardware_lock, flags);
++
++ if (dev->force_rearm || dev->acc_space > CFG_TIMEOUT) {
++ dprintk("rearming IRQ\n");
++ ite8709_write_register(dev, IT8512_REG_RCR,
++ IT8512_RCR_RXACT | CFG_DCR);
++ ite8709_write_register(dev, IT8512_REG_MSTCR,
++ CFG_FIFOTL | IT8512_MSTCR_FIFOCLR);
++ ite8709_write_register(dev, IT8512_REG_RCR,
++ IT8512_RCR_RXEN | IT8512_RCR_RXACT | CFG_DCR);
++ if (!dev->force_rearm)
++ dev->rearmed = 1;
++ dev->force_rearm = 0;
++ }
++
++ spin_lock_irqsave(&dev->hardware_lock, flags);
++ dev->device_busy = 0;
++ spin_unlock_irqrestore(&dev->hardware_lock, flags);
++}
++
++static int ite8709_cleanup(struct ite8709_device *dev, int stage, int errno,
++ char *msg)
++{
++ if (msg != NULL)
++ printk(KERN_ERR LIRC_DRIVER_NAME ": %s\n", msg);
++
++ switch (stage) {
++ case 6:
++ if (dev->use_count > 0)
++ ite8709_drop_hardware(dev);
++ case 5:
++ free_irq(dev->irq, dev);
++ case 4:
++ release_region(dev->io, 2);
++ case 3:
++ lirc_unregister_driver(dev->driver.minor);
++ case 2:
++ lirc_buffer_free(dev->driver.rbuf);
++ kfree(dev->driver.rbuf);
++ case 1:
++ kfree(dev);
++ case 0:
++ ;
++ }
++
++ return errno;
++}
++
++static int __devinit ite8709_pnp_probe(struct pnp_dev *dev,
++ const struct pnp_device_id *dev_id)
++{
++ struct lirc_driver *driver;
++ struct ite8709_device *ite8709_dev;
++ int ret;
++
++ /* Check resources validity */
++ if (!pnp_irq_valid(dev, 0))
++ return ite8709_cleanup(NULL, 0, -ENODEV, "invalid IRQ");
++ if (!pnp_port_valid(dev, 2))
++ return ite8709_cleanup(NULL, 0, -ENODEV, "invalid IO port");
++
++ /* Allocate memory for device struct */
++ ite8709_dev = kzalloc(sizeof(struct ite8709_device), GFP_KERNEL);
++ if (ite8709_dev == NULL)
++ return ite8709_cleanup(NULL, 0, -ENOMEM, "kzalloc failed");
++ pnp_set_drvdata(dev, ite8709_dev);
++
++ /* Initialize device struct */
++ ite8709_dev->use_count = 0;
++ ite8709_dev->irq = pnp_irq(dev, 0);
++ ite8709_dev->io = pnp_port_start(dev, 2);
++ ite8709_dev->hardware_lock =
++ __SPIN_LOCK_UNLOCKED(ite8709_dev->hardware_lock);
++ ite8709_dev->acc_pulse = 0;
++ ite8709_dev->acc_space = 0;
++ ite8709_dev->lastbit = 0;
++ do_gettimeofday(&ite8709_dev->last_tv);
++ tasklet_init(&ite8709_dev->tasklet, ite8709_rearm_irq,
++ (long) ite8709_dev);
++ ite8709_dev->force_rearm = 0;
++ ite8709_dev->rearmed = 0;
++ ite8709_dev->device_busy = 0;
++
++ /* Initialize driver struct */
++ driver = &ite8709_dev->driver;
++ strcpy(driver->name, LIRC_DRIVER_NAME);
++ driver->minor = -1;
++ driver->code_length = sizeof(int) * 8;
++ driver->sample_rate = 0;
++ driver->features = LIRC_CAN_REC_MODE2;
++ driver->data = ite8709_dev;
++ driver->add_to_buf = NULL;
++ driver->set_use_inc = ite8709_set_use_inc;
++ driver->set_use_dec = ite8709_set_use_dec;
++ driver->dev = &dev->dev;
++ driver->owner = THIS_MODULE;
++
++ /* Initialize LIRC buffer */
++ driver->rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
++ if (!driver->rbuf)
++ return ite8709_cleanup(ite8709_dev, 1, -ENOMEM,
++ "can't allocate lirc_buffer");
++ if (lirc_buffer_init(driver->rbuf, BUF_CHUNK_SIZE, BUF_SIZE))
++ return ite8709_cleanup(ite8709_dev, 1, -ENOMEM,
++ "lirc_buffer_init() failed");
++
++ /* Register LIRC driver */
++ ret = lirc_register_driver(driver);
++ if (ret < 0)
++ return ite8709_cleanup(ite8709_dev, 2, ret,
++ "lirc_register_driver() failed");
++
++ /* Reserve I/O port access */
++ if (!request_region(ite8709_dev->io, 2, LIRC_DRIVER_NAME))
++ return ite8709_cleanup(ite8709_dev, 3, -EBUSY,
++ "i/o port already in use");
++
++ /* Reserve IRQ line */
++ ret = request_irq(ite8709_dev->irq, ite8709_interrupt, 0,
++ LIRC_DRIVER_NAME, ite8709_dev);
++ if (ret < 0)
++ return ite8709_cleanup(ite8709_dev, 4, ret,
++ "IRQ already in use");
++
++ /* Initialize hardware */
++ ite8709_drop_hardware(ite8709_dev); /* Shutdown hw until first use */
++
++ printk(KERN_INFO LIRC_DRIVER_NAME ": device found : irq=%d io=0x%x\n",
++ ite8709_dev->irq, ite8709_dev->io);
++
++ return 0;
++}
++
++static void __devexit ite8709_pnp_remove(struct pnp_dev *dev)
++{
++ struct ite8709_device *ite8709_dev;
++ ite8709_dev = pnp_get_drvdata(dev);
++
++ ite8709_cleanup(ite8709_dev, 6, 0, NULL);
++
++ printk(KERN_INFO LIRC_DRIVER_NAME ": device removed\n");
++}
++
++#ifdef CONFIG_PM
++static int ite8709_pnp_suspend(struct pnp_dev *dev, pm_message_t state)
++{
++ struct ite8709_device *ite8709_dev;
++ ite8709_dev = pnp_get_drvdata(dev);
++
++ if (ite8709_dev->use_count > 0)
++ ite8709_drop_hardware(ite8709_dev);
++
++ return 0;
++}
++
++static int ite8709_pnp_resume(struct pnp_dev *dev)
++{
++ struct ite8709_device *ite8709_dev;
++ ite8709_dev = pnp_get_drvdata(dev);
++
++ if (ite8709_dev->use_count > 0)
++ ite8709_init_hardware(ite8709_dev);
++
++ return 0;
++}
++#else
++#define ite8709_pnp_suspend NULL
++#define ite8709_pnp_resume NULL
++#endif
++
++static const struct pnp_device_id pnp_dev_table[] = {
++ {"ITE8709", 0},
++ {}
++};
++
++MODULE_DEVICE_TABLE(pnp, pnp_dev_table);
++
++static struct pnp_driver ite8709_pnp_driver = {
++ .name = LIRC_DRIVER_NAME,
++ .probe = ite8709_pnp_probe,
++ .remove = __devexit_p(ite8709_pnp_remove),
++ .suspend = ite8709_pnp_suspend,
++ .resume = ite8709_pnp_resume,
++ .id_table = pnp_dev_table,
++};
++
++int init_module(void)
++{
++ return pnp_register_driver(&ite8709_pnp_driver);
++}
++
++void cleanup_module(void)
++{
++ pnp_unregister_driver(&ite8709_pnp_driver);
++}
++
++MODULE_DESCRIPTION("LIRC driver for ITE8709 CIR port");
++MODULE_AUTHOR("Grégory Lardière");
++MODULE_LICENSE("GPL");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
+diff --git a/drivers/input/lirc/lirc_mceusb.c b/drivers/input/lirc/lirc_mceusb.c
+new file mode 100644
+index 0000000..c0869d8
+--- /dev/null
++++ b/drivers/input/lirc/lirc_mceusb.c
+@@ -0,0 +1,1385 @@
++/*
++ * LIRC driver for Windows Media Center Edition USB Infrared Transceivers
++ *
++ * (C) by Martin A. Blatter <martin_a_blatter@yahoo.com>
++ *
++ * Transmitter support and reception code cleanup.
++ * (C) by Daniel Melander <lirc@rajidae.se>
++ *
++ * Original lirc_mceusb driver for 1st-gen device:
++ * Copyright (c) 2003-2004 Dan Conti <dconti@acm.wwu.edu>
++ *
++ * Original lirc_mceusb driver deprecated in favor of this driver, which
++ * supports the 1st-gen device now too. Transmit and receive support for
++ * the 1st-gen device added June-September 2009,
++ * by Jarod Wilson <jarod@wilsonet.com> and Patrick Calhoun <phineas@ou.edu>
++ *
++ * Derived from ATI USB driver by Paul Miller and the original
++ * MCE USB driver by Dan Conti (and now including chunks of the latter
++ * relevant to the 1st-gen device initialization)
++ *
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/kmod.h>
++#include <linux/smp_lock.h>
++#include <linux/completion.h>
++#include <linux/uaccess.h>
++#include <linux/usb.h>
++#include <linux/wait.h>
++#include <linux/time.h>
++
++#include <linux/lirc.h>
++#include "lirc_dev.h"
++
++#define DRIVER_VERSION "1.90"
++#define DRIVER_AUTHOR "Daniel Melander <lirc@rajidae.se>, " \
++ "Martin Blatter <martin_a_blatter@yahoo.com>, " \
++ "Dan Conti <dconti@acm.wwu.edu>"
++#define DRIVER_DESC "Windows Media Center Edition USB IR Transceiver " \
++ "driver for LIRC"
++#define DRIVER_NAME "lirc_mceusb"
++
++#define USB_BUFLEN 32 /* USB reception buffer length */
++#define LIRCBUF_SIZE 256 /* LIRC work buffer length */
++
++/* MCE constants */
++#define MCE_CMDBUF_SIZE 384 /* MCE Command buffer length */
++#define MCE_TIME_UNIT 50 /* Approx 50us resolution */
++#define MCE_CODE_LENGTH 5 /* Normal length of packet (with header) */
++#define MCE_PACKET_SIZE 4 /* Normal length of packet (without header) */
++#define MCE_PACKET_HEADER 0x84 /* Actual header format is 0x80 + num_bytes */
++#define MCE_CONTROL_HEADER 0x9F /* MCE status header */
++#define MCE_TX_HEADER_LENGTH 3 /* # of bytes in the initializing tx header */
++#define MCE_MAX_CHANNELS 2 /* Two transmitters, hardware dependent? */
++#define MCE_DEFAULT_TX_MASK 0x03 /* Val opts: TX1=0x01, TX2=0x02, ALL=0x03 */
++#define MCE_PULSE_BIT 0x80 /* Pulse bit, MSB set == PULSE else SPACE */
++#define MCE_PULSE_MASK 0x7F /* Pulse mask */
++#define MCE_MAX_PULSE_LENGTH 0x7F /* Longest transmittable pulse symbol */
++#define MCE_PACKET_LENGTH_MASK 0x7F /* Pulse mask */
++
++
++/* module parameters */
++#ifdef CONFIG_USB_DEBUG
++static int debug = 1;
++#else
++static int debug;
++#endif
++
++/* general constants */
++#define SEND_FLAG_IN_PROGRESS 1
++#define SEND_FLAG_COMPLETE 2
++#define RECV_FLAG_IN_PROGRESS 3
++#define RECV_FLAG_COMPLETE 4
++
++#define MCEUSB_INBOUND 1
++#define MCEUSB_OUTBOUND 2
++
++#define VENDOR_PHILIPS 0x0471
++#define VENDOR_SMK 0x0609
++#define VENDOR_TATUNG 0x1460
++#define VENDOR_GATEWAY 0x107b
++#define VENDOR_SHUTTLE 0x1308
++#define VENDOR_SHUTTLE2 0x051c
++#define VENDOR_MITSUMI 0x03ee
++#define VENDOR_TOPSEED 0x1784
++#define VENDOR_RICAVISION 0x179d
++#define VENDOR_ITRON 0x195d
++#define VENDOR_FIC 0x1509
++#define VENDOR_LG 0x043e
++#define VENDOR_MICROSOFT 0x045e
++#define VENDOR_FORMOSA 0x147a
++#define VENDOR_FINTEK 0x1934
++#define VENDOR_PINNACLE 0x2304
++#define VENDOR_ECS 0x1019
++#define VENDOR_WISTRON 0x0fb8
++#define VENDOR_COMPRO 0x185b
++#define VENDOR_NORTHSTAR 0x04eb
++#define VENDOR_REALTEK 0x0bda
++#define VENDOR_TIVO 0x105a
++
++static struct usb_device_id mceusb_dev_table[] = {
++ /* Original Microsoft MCE IR Transceiver (often HP-branded) */
++ { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) },
++ /* Philips Infrared Transceiver - Sahara branded */
++ { USB_DEVICE(VENDOR_PHILIPS, 0x0608) },
++ /* Philips Infrared Transceiver - HP branded */
++ { USB_DEVICE(VENDOR_PHILIPS, 0x060c) },
++ /* Philips SRM5100 */
++ { USB_DEVICE(VENDOR_PHILIPS, 0x060d) },
++ /* Philips Infrared Transceiver - Omaura */
++ { USB_DEVICE(VENDOR_PHILIPS, 0x060f) },
++ /* Philips Infrared Transceiver - Spinel plus */
++ { USB_DEVICE(VENDOR_PHILIPS, 0x0613) },
++ /* Philips eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_PHILIPS, 0x0815) },
++ /* Realtek MCE IR Receiver */
++ { USB_DEVICE(VENDOR_REALTEK, 0x0161) },
++ /* SMK/Toshiba G83C0004D410 */
++ { USB_DEVICE(VENDOR_SMK, 0x031d) },
++ /* SMK eHome Infrared Transceiver (Sony VAIO) */
++ { USB_DEVICE(VENDOR_SMK, 0x0322) },
++ /* bundled with Hauppauge PVR-150 */
++ { USB_DEVICE(VENDOR_SMK, 0x0334) },
++ /* SMK eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_SMK, 0x0338) },
++ /* Tatung eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_TATUNG, 0x9150) },
++ /* Shuttle eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_SHUTTLE, 0xc001) },
++ /* Shuttle eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_SHUTTLE2, 0xc001) },
++ /* Gateway eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_GATEWAY, 0x3009) },
++ /* Mitsumi */
++ { USB_DEVICE(VENDOR_MITSUMI, 0x2501) },
++ /* Topseed eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
++ /* Topseed HP eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0006) },
++ /* Topseed eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
++ /* Topseed eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
++ /* Topseed eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_TOPSEED, 0x000a) },
++ /* Topseed eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0011) },
++ /* Ricavision internal Infrared Transceiver */
++ { USB_DEVICE(VENDOR_RICAVISION, 0x0010) },
++ /* Itron ione Libra Q-11 */
++ { USB_DEVICE(VENDOR_ITRON, 0x7002) },
++ /* FIC eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_FIC, 0x9242) },
++ /* LG eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_LG, 0x9803) },
++ /* Microsoft MCE Infrared Transceiver */
++ { USB_DEVICE(VENDOR_MICROSOFT, 0x00a0) },
++ /* Formosa eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_FORMOSA, 0xe015) },
++ /* Formosa21 / eHome Infrared Receiver */
++ { USB_DEVICE(VENDOR_FORMOSA, 0xe016) },
++ /* Formosa aim / Trust MCE Infrared Receiver */
++ { USB_DEVICE(VENDOR_FORMOSA, 0xe017) },
++ /* Formosa Industrial Computing / Beanbag Emulation Device */
++ { USB_DEVICE(VENDOR_FORMOSA, 0xe018) },
++ /* Formosa21 / eHome Infrared Receiver */
++ { USB_DEVICE(VENDOR_FORMOSA, 0xe03a) },
++ /* Formosa Industrial Computing AIM IR605/A */
++ { USB_DEVICE(VENDOR_FORMOSA, 0xe03c) },
++ /* Fintek eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_FINTEK, 0x0602) },
++ /* Fintek eHome Infrared Transceiver (in the AOpen MP45) */
++ { USB_DEVICE(VENDOR_FINTEK, 0x0702) },
++ /* Pinnacle Remote Kit */
++ { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
++ /* Elitegroup Computer Systems IR */
++ { USB_DEVICE(VENDOR_ECS, 0x0f38) },
++ /* Wistron Corp. eHome Infrared Receiver */
++ { USB_DEVICE(VENDOR_WISTRON, 0x0002) },
++ /* Compro K100 */
++ { USB_DEVICE(VENDOR_COMPRO, 0x3020) },
++ /* Compro K100 v2 */
++ { USB_DEVICE(VENDOR_COMPRO, 0x3082) },
++ /* Northstar Systems, Inc. eHome Infrared Transceiver */
++ { USB_DEVICE(VENDOR_NORTHSTAR, 0xe004) },
++ /* TiVo PC IR Receiver */
++ { USB_DEVICE(VENDOR_TIVO, 0x2000) },
++ /* Terminating entry */
++ { }
++};
++
++static struct usb_device_id gen3_list[] = {
++ { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
++ {}
++};
++
++static struct usb_device_id pinnacle_list[] = {
++ { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
++ {}
++};
++
++static struct usb_device_id microsoft_gen1_list[] = {
++ { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) },
++ {}
++};
++
++static struct usb_device_id transmitter_mask_list[] = {
++ { USB_DEVICE(VENDOR_MICROSOFT, 0x006d) },
++ { USB_DEVICE(VENDOR_PHILIPS, 0x060c) },
++ { USB_DEVICE(VENDOR_SMK, 0x031d) },
++ { USB_DEVICE(VENDOR_SMK, 0x0322) },
++ { USB_DEVICE(VENDOR_SMK, 0x0334) },
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0001) },
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0006) },
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0007) },
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0008) },
++ { USB_DEVICE(VENDOR_TOPSEED, 0x000a) },
++ { USB_DEVICE(VENDOR_TOPSEED, 0x0011) },
++ { USB_DEVICE(VENDOR_PINNACLE, 0x0225) },
++ {}
++};
++
++/* data structure for each usb transceiver */
++struct mceusb_dev {
++
++ /* usb */
++ struct usb_device *usbdev;
++ struct urb *urb_in;
++ int devnum;
++ struct usb_endpoint_descriptor *usb_ep_in;
++ struct usb_endpoint_descriptor *usb_ep_out;
++
++ /* buffers and dma */
++ unsigned char *buf_in;
++ unsigned int len_in;
++ dma_addr_t dma_in;
++ dma_addr_t dma_out;
++ unsigned int overflow_len;
++
++ /* lirc */
++ struct lirc_driver *d;
++ int lircdata;
++ unsigned char is_pulse;
++ struct {
++ u32 connected:1;
++ u32 gen3:1;
++ u32 transmitter_mask_inverted:1;
++ u32 microsoft_gen1:1;
++ u32 reserved:28;
++ } flags;
++
++ unsigned char transmitter_mask;
++ unsigned int carrier_freq;
++
++ /* handle sending (init strings) */
++ int send_flags;
++ wait_queue_head_t wait_out;
++
++ struct mutex dev_lock;
++};
++
++/*
++ * MCE Device Command Strings
++ * Device command responses vary from device to device...
++ * - DEVICE_RESET resets the hardware to its default state
++ * - GET_REVISION fetches the hardware/software revision, common
++ * replies are ff 0b 45 ff 1b 08 and ff 0b 50 ff 1b 42
++ * - GET_CARRIER_FREQ gets the carrier mode and frequency of the
++ * device, with replies in the form of 9f 06 MM FF, where MM is 0-3,
++ * meaning clk of 10000000, 2500000, 625000 or 156250, and FF is
++ * ((clk / frequency) - 1)
++ * - GET_RX_TIMEOUT fetches the receiver timeout in units of 50us,
++ * response in the form of 9f 0c msb lsb
++ * - GET_TX_BITMASK fetches the transmitter bitmask, replies in
++ * the form of 9f 08 bm, where bm is the bitmask
++ * - GET_RX_SENSOR fetches the RX sensor setting -- long-range
++ * general use one or short-range learning one, in the form of
++ * 9f 14 ss, where ss is either 01 for long-range or 02 for short
++ * - SET_CARRIER_FREQ sets a new carrier mode and frequency
++ * - SET_TX_BITMASK sets the transmitter bitmask
++ * - SET_RX_TIMEOUT sets the receiver timeout
++ * - SET_RX_SENSOR sets which receiver sensor to use
++ */
++static char DEVICE_RESET[] = {0x00, 0xff, 0xaa};
++static char GET_REVISION[] = {0xff, 0x0b};
++static char GET_UNKNOWN[] = {0xff, 0x18};
++static char GET_CARRIER_FREQ[] = {0x9f, 0x07};
++static char GET_RX_TIMEOUT[] = {0x9f, 0x0d};
++static char GET_TX_BITMASK[] = {0x9f, 0x13};
++static char GET_RX_SENSOR[] = {0x9f, 0x15};
++/* sub in desired values in lower byte or bytes for full command */
++//static char SET_CARRIER_FREQ[] = {0x9f, 0x06, 0x00, 0x00};
++//static char SET_TX_BITMASK[] = {0x9f, 0x08, 0x00};
++//static char SET_RX_TIMEOUT[] = {0x9f, 0x0c, 0x00, 0x00};
++//static char SET_RX_SENSOR[] = {0x9f, 0x14, 0x00};
++
++static void mceusb_dev_printdata(struct mceusb_dev *ir, char *buf,
++ int len, bool out)
++{
++ char codes[USB_BUFLEN * 3 + 1];
++ char inout[9];
++ int i;
++ u8 cmd, subcmd, data1, data2;
++ struct device *dev = ir->d->dev;
++
++ if (len <= 0)
++ return;
++
++ if (ir->flags.microsoft_gen1 && len <= 2)
++ return;
++
++ for (i = 0; i < len && i < USB_BUFLEN; i++)
++ snprintf(codes + i * 3, 4, "%02x ", buf[i] & 0xFF);
++
++ dev_info(dev, "%sbound data: %s (length=%d)\n",
++ (out ? "out" : " in"), codes, len);
++
++ if (out)
++ strcpy(inout, "Request\0");
++ else
++ strcpy(inout, "Got\0");
++
++ cmd = buf[0] & 0xff;
++ subcmd = buf[1] & 0xff;
++ data1 = buf[2] & 0xff;
++ data2 = buf[3] & 0xff;
++
++ switch (cmd) {
++ case 0x00:
++ if (subcmd == 0xff && data1 == 0xaa)
++ dev_info(dev, "Device reset requested\n");
++ else
++ dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
++ cmd, subcmd);
++ break;
++ case 0xff:
++ switch (subcmd) {
++ case 0x0b:
++ if (len == 2)
++ dev_info(dev, "Get hw/sw rev?\n");
++ else
++ dev_info(dev, "hw/sw rev 0x%02x 0x%02x "
++ "0x%02x 0x%02x\n", data1, data2,
++ buf[4], buf[5]);
++ break;
++ case 0xaa:
++ dev_info(dev, "Device reset requested\n");
++ break;
++ case 0xfe:
++ dev_info(dev, "Previous command not supported\n");
++ break;
++ case 0x18:
++ case 0x1b:
++ default:
++ dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
++ cmd, subcmd);
++ break;
++ }
++ break;
++ case 0x9f:
++ switch (subcmd) {
++ case 0x03:
++ dev_info(dev, "Ping\n");
++ break;
++ case 0x04:
++ dev_info(dev, "Resp to 9f 05 of 0x%02x 0x%02x\n",
++ data1, data2);
++ break;
++ case 0x06:
++ dev_info(dev, "%s carrier mode and freq of 0x%02x 0x%02x\n",
++ inout, data1, data2);
++ break;
++ case 0x07:
++ dev_info(dev, "Get carrier mode and freq\n");
++ break;
++ case 0x08:
++ dev_info(dev, "%s transmit blaster mask of 0x%02x\n",
++ inout, data1);
++ break;
++ case 0x0c:
++ /* value is in units of 50us, so x*50/100 or x/2 ms */
++ dev_info(dev, "%s receive timeout of %d ms\n",
++ inout, ((data1 << 8) | data2) / 2);
++ break;
++ case 0x0d:
++ dev_info(dev, "Get receive timeout\n");
++ break;
++ case 0x13:
++ dev_info(dev, "Get transmit blaster mask\n");
++ break;
++ case 0x14:
++ dev_info(dev, "%s %s-range receive sensor in use\n",
++ inout, data1 == 0x02 ? "short" : "long");
++ break;
++ case 0x15:
++ if (len == 2)
++ dev_info(dev, "Get receive sensor\n");
++ else
++ dev_info(dev, "Received pulse count is %d\n",
++ ((data1 << 8) | data2));
++ break;
++ case 0xfe:
++ dev_info(dev, "Error! Hardware is likely wedged...\n");
++ break;
++ case 0x05:
++ case 0x09:
++ case 0x0f:
++ default:
++ dev_info(dev, "Unknown command 0x%02x 0x%02x\n",
++ cmd, subcmd);
++ break;
++ }
++ break;
++ default:
++ break;
++ }
++}
++
++static void usb_async_callback(struct urb *urb, struct pt_regs *regs)
++{
++ struct mceusb_dev *ir;
++ int len;
++
++ if (!urb)
++ return;
++
++ ir = urb->context;
++ if (ir) {
++ len = urb->actual_length;
++
++ dev_dbg(ir->d->dev, "callback called (status=%d len=%d)\n",
++ urb->status, len);
++
++ if (debug)
++ mceusb_dev_printdata(ir, urb->transfer_buffer, len, true);
++ }
++
++}
++
++/* request incoming or send outgoing usb packet - used to initialize remote */
++static void mce_request_packet(struct mceusb_dev *ir,
++ struct usb_endpoint_descriptor *ep,
++ unsigned char *data, int size, int urb_type)
++{
++ int res;
++ struct urb *async_urb;
++ unsigned char *async_buf;
++
++ if (urb_type == MCEUSB_OUTBOUND) {
++ async_urb = usb_alloc_urb(0, GFP_KERNEL);
++ if (unlikely(!async_urb)) {
++ dev_err(ir->d->dev, "Error, couldn't allocate urb!\n");
++ return;
++ }
++
++ async_buf = kzalloc(size, GFP_KERNEL);
++ if (!async_buf) {
++ dev_err(ir->d->dev, "Error, couldn't allocate buf!\n");
++ usb_free_urb(async_urb);
++ return;
++ }
++
++ /* outbound data */
++ usb_fill_int_urb(async_urb, ir->usbdev,
++ usb_sndintpipe(ir->usbdev, ep->bEndpointAddress),
++ async_buf, size, (usb_complete_t) usb_async_callback,
++ ir, ep->bInterval);
++ memcpy(async_buf, data, size);
++
++ } else if (urb_type == MCEUSB_INBOUND) {
++ /* standard request */
++ async_urb = ir->urb_in;
++ ir->send_flags = RECV_FLAG_IN_PROGRESS;
++
++ } else {
++ dev_err(ir->d->dev, "Error! Unknown urb type %d\n", urb_type);
++ return;
++ }
++
++ dev_dbg(ir->d->dev, "receive request called (size=%#x)\n", size);
++
++ async_urb->transfer_buffer_length = size;
++ async_urb->dev = ir->usbdev;
++
++ res = usb_submit_urb(async_urb, GFP_ATOMIC);
++ if (res) {
++ dev_dbg(ir->d->dev, "receive request FAILED! (res=%d)\n", res);
++ return;
++ }
++ dev_dbg(ir->d->dev, "receive request complete (res=%d)\n", res);
++}
++
++static void mce_async_out(struct mceusb_dev *ir, unsigned char *data, int size)
++{
++ mce_request_packet(ir, ir->usb_ep_out, data, size, MCEUSB_OUTBOUND);
++}
++
++static void mce_sync_in(struct mceusb_dev *ir, unsigned char *data, int size)
++{
++ mce_request_packet(ir, ir->usb_ep_in, data, size, MCEUSB_INBOUND);
++}
++
++static int unregister_from_lirc(struct mceusb_dev *ir)
++{
++ struct lirc_driver *d = ir->d;
++ int devnum;
++ int rtn;
++
++ devnum = ir->devnum;
++ dev_dbg(ir->d->dev, "unregister from lirc called\n");
++
++ rtn = lirc_unregister_driver(d->minor);
++ if (rtn > 0) {
++ dev_info(ir->d->dev, "error in lirc_unregister minor: %d\n"
++ "Trying again...\n", d->minor);
++ if (rtn == -EBUSY) {
++ dev_info(ir->d->dev, "device is opened, will "
++ "unregister on close\n");
++ return -EAGAIN;
++ }
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(HZ);
++
++ rtn = lirc_unregister_driver(d->minor);
++ if (rtn > 0)
++ dev_info(ir->d->dev, "lirc_unregister failed\n");
++ }
++
++ if (rtn) {
++ dev_info(ir->d->dev, "didn't free resources\n");
++ return -EAGAIN;
++ }
++
++ dev_info(ir->d->dev, "usb remote disconnected\n");
++
++ lirc_buffer_free(d->rbuf);
++ kfree(d->rbuf);
++ kfree(d);
++ kfree(ir);
++ return 0;
++}
++
++static int mceusb_ir_open(void *data)
++{
++ struct mceusb_dev *ir = data;
++
++ if (!ir) {
++ printk(KERN_WARNING DRIVER_NAME
++ "[?]: %s called with no context\n", __func__);
++ return -EIO;
++ }
++
++ dev_dbg(ir->d->dev, "mceusb IR device opened\n");
++
++ if (!ir->flags.connected) {
++ if (!ir->usbdev)
++ return -ENOENT;
++ ir->flags.connected = 1;
++ }
++
++ return 0;
++}
++
++static void mceusb_ir_close(void *data)
++{
++ struct mceusb_dev *ir = data;
++
++ if (!ir) {
++ printk(KERN_WARNING DRIVER_NAME
++ "[?]: %s called with no context\n", __func__);
++ return;
++ }
++
++ dev_dbg(ir->d->dev, "mceusb IR device closed\n");
++
++ if (ir->flags.connected) {
++ mutex_lock(&ir->dev_lock);
++ ir->flags.connected = 0;
++ mutex_unlock(&ir->dev_lock);
++ }
++}
++
++static void send_packet_to_lirc(struct mceusb_dev *ir)
++{
++ if (ir->lircdata) {
++ lirc_buffer_write(ir->d->rbuf,
++ (unsigned char *) &ir->lircdata);
++ wake_up(&ir->d->rbuf->wait_poll);
++ ir->lircdata = 0;
++ }
++}
++
++static void mceusb_process_ir_data(struct mceusb_dev *ir, int buf_len)
++{
++ int i, j;
++ int packet_len = 0;
++ int start_index = 0;
++
++ /* skip meaningless 0xb1 0x60 header bytes on orig receiver */
++ if (ir->flags.microsoft_gen1)
++ start_index = 2;
++
++ /* this should only trigger w/the 1st-gen mce receiver */
++ for (i = start_index; i < (start_index + ir->overflow_len) &&
++ i < buf_len; i++) {
++ /* rising/falling flank */
++ if (ir->is_pulse != (ir->buf_in[i] & MCE_PULSE_BIT)) {
++ send_packet_to_lirc(ir);
++ ir->is_pulse = ir->buf_in[i] & MCE_PULSE_BIT;
++ }
++
++ /* accumulate mce pulse/space values */
++ ir->lircdata += (ir->buf_in[i] & MCE_PULSE_MASK) *
++ MCE_TIME_UNIT;
++ ir->lircdata |= (ir->is_pulse ? PULSE_BIT : 0);
++ }
++ start_index += ir->overflow_len;
++ ir->overflow_len = 0;
++
++ for (i = start_index; i < buf_len; i++) {
++ /* decode mce packets of the form (84),AA,BB,CC,DD */
++
++ /* data headers */
++ if (ir->buf_in[i] >= 0x80 && ir->buf_in[i] <= 0x9e) {
++ /* decode packet data */
++ packet_len = ir->buf_in[i] & MCE_PACKET_LENGTH_MASK;
++ ir->overflow_len = i + 1 + packet_len - buf_len;
++ for (j = 1; j <= packet_len && (i + j < buf_len); j++) {
++ /* rising/falling flank */
++ if (ir->is_pulse !=
++ (ir->buf_in[i + j] & MCE_PULSE_BIT)) {
++ send_packet_to_lirc(ir);
++ ir->is_pulse =
++ ir->buf_in[i + j] &
++ MCE_PULSE_BIT;
++ }
++
++ /* accumulate mce pulse/space values */
++ ir->lircdata +=
++ (ir->buf_in[i + j] & MCE_PULSE_MASK) *
++ MCE_TIME_UNIT;
++ ir->lircdata |= (ir->is_pulse ? PULSE_BIT : 0);
++ }
++
++ i += packet_len;
++
++ /* status header (0x9F) */
++ } else if (ir->buf_in[i] == MCE_CONTROL_HEADER) {
++ /*
++ * A transmission containing one or more consecutive ir
++ * commands always ends with a GAP of 100ms followed by
++ * the sequence 0x9F 0x01 0x01 0x9F 0x15 0x00 0x00 0x80
++ */
++
++#if 0
++ Uncomment this if the last 100ms "infinity"-space should be transmitted
++ to lirc directly instead of at the beginning of the next transmission.
++ Changes pulse/space order.
++
++ if (++i < buf_len && ir->buf_in[i] == 0x01)
++ send_packet_to_lirc(ir);
++
++#endif
++
++ /* end decode loop */
++ dev_dbg(ir->d->dev, "[%d] %s: found control header\n",
++ ir->devnum, __func__);
++ ir->overflow_len = 0;
++ break;
++ } else {
++ dev_dbg(ir->d->dev, "[%d] %s: stray packet?\n",
++ ir->devnum, __func__);
++ ir->overflow_len = 0;
++ }
++ }
++
++ return;
++}
++
++static void mceusb_dev_recv(struct urb *urb, struct pt_regs *regs)
++{
++ struct mceusb_dev *ir;
++ int buf_len;
++
++ if (!urb)
++ return;
++
++ ir = urb->context;
++ if (!ir) {
++ usb_unlink_urb(urb);
++ return;
++ }
++
++ buf_len = urb->actual_length;
++
++ if (debug)
++ mceusb_dev_printdata(ir, urb->transfer_buffer, buf_len, false);
++
++ if (ir->send_flags == RECV_FLAG_IN_PROGRESS) {
++ ir->send_flags = SEND_FLAG_COMPLETE;
++ dev_dbg(ir->d->dev, "setup answer received %d bytes\n",
++ buf_len);
++ }
++
++ switch (urb->status) {
++ /* success */
++ case 0:
++ mceusb_process_ir_data(ir, buf_len);
++ break;
++
++ case -ECONNRESET:
++ case -ENOENT:
++ case -ESHUTDOWN:
++ usb_unlink_urb(urb);
++ return;
++
++ case -EPIPE:
++ default:
++ break;
++ }
++
++ usb_submit_urb(urb, GFP_ATOMIC);
++}
++
++
++static ssize_t mceusb_transmit_ir(struct file *file, const char *buf,
++ size_t n, loff_t *ppos)
++{
++ int i, count = 0, cmdcount = 0;
++ struct mceusb_dev *ir = NULL;
++ int wbuf[LIRCBUF_SIZE]; /* Workbuffer with values from lirc */
++ unsigned char cmdbuf[MCE_CMDBUF_SIZE]; /* MCE command buffer */
++ unsigned long signal_duration = 0; /* Singnal length in us */
++ struct timeval start_time, end_time;
++
++ do_gettimeofday(&start_time);
++
++ /* Retrieve lirc_driver data for the device */
++ ir = lirc_get_pdata(file);
++ if (!ir || !ir->usb_ep_out)
++ return -EFAULT;
++
++ if (n % sizeof(int))
++ return -EINVAL;
++ count = n / sizeof(int);
++
++ /* Check if command is within limits */
++ if (count > LIRCBUF_SIZE || count%2 == 0)
++ return -EINVAL;
++ if (copy_from_user(wbuf, buf, n))
++ return -EFAULT;
++
++ /* MCE tx init header */
++ cmdbuf[cmdcount++] = MCE_CONTROL_HEADER;
++ cmdbuf[cmdcount++] = 0x08;
++ cmdbuf[cmdcount++] = ir->transmitter_mask;
++
++ /* Generate mce packet data */
++ for (i = 0; (i < count) && (cmdcount < MCE_CMDBUF_SIZE); i++) {
++ signal_duration += wbuf[i];
++ wbuf[i] = wbuf[i] / MCE_TIME_UNIT;
++
++ do { /* loop to support long pulses/spaces > 127*50us=6.35ms */
++
++ /* Insert mce packet header every 4th entry */
++ if ((cmdcount < MCE_CMDBUF_SIZE) &&
++ (cmdcount - MCE_TX_HEADER_LENGTH) %
++ MCE_CODE_LENGTH == 0)
++ cmdbuf[cmdcount++] = MCE_PACKET_HEADER;
++
++ /* Insert mce packet data */
++ if (cmdcount < MCE_CMDBUF_SIZE)
++ cmdbuf[cmdcount++] =
++ (wbuf[i] < MCE_PULSE_BIT ?
++ wbuf[i] : MCE_MAX_PULSE_LENGTH) |
++ (i & 1 ? 0x00 : MCE_PULSE_BIT);
++ else
++ return -EINVAL;
++ } while ((wbuf[i] > MCE_MAX_PULSE_LENGTH) &&
++ (wbuf[i] -= MCE_MAX_PULSE_LENGTH));
++ }
++
++ /* Fix packet length in last header */
++ cmdbuf[cmdcount - (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH] =
++ 0x80 + (cmdcount - MCE_TX_HEADER_LENGTH) % MCE_CODE_LENGTH - 1;
++
++ /* Check if we have room for the empty packet at the end */
++ if (cmdcount >= MCE_CMDBUF_SIZE)
++ return -EINVAL;
++
++ /* All mce commands end with an empty packet (0x80) */
++ cmdbuf[cmdcount++] = 0x80;
++
++ /* Transmit the command to the mce device */
++ mce_async_out(ir, cmdbuf, cmdcount);
++
++ /*
++ * The lircd gap calculation expects the write function to
++ * wait the time it takes for the ircommand to be sent before
++ * it returns.
++ */
++ do_gettimeofday(&end_time);
++ signal_duration -= (end_time.tv_usec - start_time.tv_usec) +
++ (end_time.tv_sec - start_time.tv_sec) * 1000000;
++
++ /* delay with the closest number of ticks */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(usecs_to_jiffies(signal_duration));
++
++ return n;
++}
++
++static void set_transmitter_mask(struct mceusb_dev *ir, unsigned int mask)
++{
++ if (ir->flags.transmitter_mask_inverted)
++ ir->transmitter_mask = (mask != 0x03 ? mask ^ 0x03 : mask) << 1;
++ else
++ ir->transmitter_mask = mask;
++}
++
++
++/* Sets the send carrier frequency */
++static int set_send_carrier(struct mceusb_dev *ir, int carrier)
++{
++ int clk = 10000000;
++ int prescaler = 0, divisor = 0;
++ unsigned char cmdbuf[] = { 0x9F, 0x06, 0x01, 0x80 };
++
++ /* Carrier is changed */
++ if (ir->carrier_freq != carrier) {
++
++ if (carrier <= 0) {
++ ir->carrier_freq = carrier;
++ dev_dbg(ir->d->dev, "SET_CARRIER disabling carrier "
++ "modulation\n");
++ mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
++ return carrier;
++ }
++
++ for (prescaler = 0; prescaler < 4; ++prescaler) {
++ divisor = (clk >> (2 * prescaler)) / carrier;
++ if (divisor <= 0xFF) {
++ ir->carrier_freq = carrier;
++ cmdbuf[2] = prescaler;
++ cmdbuf[3] = divisor;
++ dev_dbg(ir->d->dev, "SET_CARRIER requesting "
++ "%d Hz\n", carrier);
++
++ /* Transmit new carrier to mce device */
++ mce_async_out(ir, cmdbuf, sizeof(cmdbuf));
++ return carrier;
++ }
++ }
++
++ return -EINVAL;
++
++ }
++
++ return carrier;
++}
++
++
++static int mceusb_lirc_ioctl(struct inode *node, struct file *filep,
++ unsigned int cmd, unsigned long arg)
++{
++ int result;
++ unsigned int ivalue;
++ unsigned long lvalue;
++ struct mceusb_dev *ir = NULL;
++
++ /* Retrieve lirc_driver data for the device */
++ ir = lirc_get_pdata(filep);
++ if (!ir || !ir->usb_ep_out)
++ return -EFAULT;
++
++
++ switch (cmd) {
++ case LIRC_SET_TRANSMITTER_MASK:
++
++ result = get_user(ivalue, (unsigned int *) arg);
++ if (result)
++ return result;
++ switch (ivalue) {
++ case 0x01: /* Transmitter 1 => 0x04 */
++ case 0x02: /* Transmitter 2 => 0x02 */
++ case 0x03: /* Transmitter 1 & 2 => 0x06 */
++ set_transmitter_mask(ir, ivalue);
++ break;
++
++ default: /* Unsupported transmitter mask */
++ return MCE_MAX_CHANNELS;
++ }
++
++ dev_dbg(ir->d->dev, ": SET_TRANSMITTERS mask=%d\n", ivalue);
++ break;
++
++ case LIRC_GET_SEND_MODE:
++
++ result = put_user(LIRC_SEND2MODE(LIRC_CAN_SEND_PULSE &
++ LIRC_CAN_SEND_MASK),
++ (unsigned long *) arg);
++
++ if (result)
++ return result;
++ break;
++
++ case LIRC_SET_SEND_MODE:
++
++ result = get_user(lvalue, (unsigned long *) arg);
++
++ if (result)
++ return result;
++ if (lvalue != (LIRC_MODE_PULSE&LIRC_CAN_SEND_MASK))
++ return -EINVAL;
++ break;
++
++ case LIRC_SET_SEND_CARRIER:
++
++ result = get_user(ivalue, (unsigned int *) arg);
++ if (result)
++ return result;
++
++ set_send_carrier(ir, ivalue);
++ break;
++
++ default:
++ return lirc_dev_fop_ioctl(node, filep, cmd, arg);
++ }
++
++ return 0;
++}
++
++static struct file_operations lirc_fops = {
++ .owner = THIS_MODULE,
++ .write = mceusb_transmit_ir,
++ .ioctl = mceusb_lirc_ioctl,
++ .read = lirc_dev_fop_read,
++ .poll = lirc_dev_fop_poll,
++ .open = lirc_dev_fop_open,
++ .release = lirc_dev_fop_close,
++};
++
++static int mceusb_gen1_init(struct mceusb_dev *ir)
++{
++ int i, ret;
++ char junk[64], data[8];
++ int partial = 0;
++
++ /*
++ * Clear off the first few messages. These look like calibration
++ * or test data, I can't really tell. This also flushes in case
++ * we have random ir data queued up.
++ */
++ for (i = 0; i < 40; i++)
++ usb_bulk_msg(ir->usbdev,
++ usb_rcvbulkpipe(ir->usbdev,
++ ir->usb_ep_in->bEndpointAddress),
++ junk, 64, &partial, HZ * 10);
++
++ ir->is_pulse = 1;
++
++ memset(data, 0, 8);
++
++ /* Get Status */
++ ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
++ USB_REQ_GET_STATUS, USB_DIR_IN,
++ 0, 0, data, 2, HZ * 3);
++
++ /* ret = usb_get_status( ir->usbdev, 0, 0, data ); */
++ dev_dbg(ir->d->dev, "%s - ret = %d status = 0x%x 0x%x\n", __func__,
++ ret, data[0], data[1]);
++
++ /*
++ * This is a strange one. They issue a set address to the device
++ * on the receive control pipe and expect a certain value pair back
++ */
++ memset(data, 0, 8);
++
++ ret = usb_control_msg(ir->usbdev, usb_rcvctrlpipe(ir->usbdev, 0),
++ USB_REQ_SET_ADDRESS, USB_TYPE_VENDOR, 0, 0,
++ data, 2, HZ * 3);
++ dev_dbg(ir->d->dev, "%s - ret = %d, devnum = %d\n",
++ __func__, ret, ir->usbdev->devnum);
++ dev_dbg(ir->d->dev, "%s - data[0] = %d, data[1] = %d\n",
++ __func__, data[0], data[1]);
++
++ /* set feature: bit rate 38400 bps */
++ ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
++ USB_REQ_SET_FEATURE, USB_TYPE_VENDOR,
++ 0xc04e, 0x0000, NULL, 0, HZ * 3);
++
++ dev_dbg(ir->d->dev, "%s - ret = %d\n", __func__, ret);
++
++ /* bRequest 4: set char length to 8 bits */
++ ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
++ 4, USB_TYPE_VENDOR,
++ 0x0808, 0x0000, NULL, 0, HZ * 3);
++ dev_dbg(ir->d->dev, "%s - retB = %d\n", __func__, ret);
++
++ /* bRequest 2: set handshaking to use DTR/DSR */
++ ret = usb_control_msg(ir->usbdev, usb_sndctrlpipe(ir->usbdev, 0),
++ 2, USB_TYPE_VENDOR,
++ 0x0000, 0x0100, NULL, 0, HZ * 3);
++ dev_dbg(ir->d->dev, "%s - retC = %d\n", __func__, ret);
++
++ return ret;
++
++};
++
++static int mceusb_dev_probe(struct usb_interface *intf,
++ const struct usb_device_id *id)
++{
++ struct usb_device *dev = interface_to_usbdev(intf);
++ struct usb_host_interface *idesc;
++ struct usb_endpoint_descriptor *ep = NULL;
++ struct usb_endpoint_descriptor *ep_in = NULL;
++ struct usb_endpoint_descriptor *ep_out = NULL;
++ struct usb_host_config *config;
++ struct mceusb_dev *ir = NULL;
++ struct lirc_driver *driver = NULL;
++ struct lirc_buffer *rbuf = NULL;
++ int devnum, pipe, maxp;
++ int minor = 0;
++ int i;
++ char buf[63], name[128] = "";
++ int mem_failure = 0;
++ bool is_gen3;
++ bool is_microsoft_gen1;
++ bool is_pinnacle;
++
++ dev_dbg(&intf->dev, ": %s called\n", __func__);
++
++ usb_reset_device(dev);
++
++ config = dev->actconfig;
++
++ idesc = intf->cur_altsetting;
++
++ is_gen3 = usb_match_id(intf, gen3_list) ? 1 : 0;
++
++ is_microsoft_gen1 = usb_match_id(intf, microsoft_gen1_list) ? 1 : 0;
++
++ is_pinnacle = usb_match_id(intf, pinnacle_list) ? 1 : 0;
++
++ /* step through the endpoints to find first bulk in and out endpoint */
++ for (i = 0; i < idesc->desc.bNumEndpoints; ++i) {
++ ep = &idesc->endpoint[i].desc;
++
++ if ((ep_in == NULL)
++ && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
++ == USB_DIR_IN)
++ && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
++ == USB_ENDPOINT_XFER_BULK)
++ || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
++ == USB_ENDPOINT_XFER_INT))) {
++
++ dev_dbg(&intf->dev, ": acceptable inbound endpoint "
++ "found\n");
++ ep_in = ep;
++ ep_in->bmAttributes = USB_ENDPOINT_XFER_INT;
++ if (!is_pinnacle)
++ /*
++ * Ideally, we'd use what the device offers up,
++ * but that leads to non-functioning first and
++ * second-gen devices, and many devices have an
++ * invalid bInterval of 0. Pinnacle devices
++ * don't work witha bInterval of 1 though.
++ */
++ ep_in->bInterval = 1;
++ }
++
++ if ((ep_out == NULL)
++ && ((ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
++ == USB_DIR_OUT)
++ && (((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
++ == USB_ENDPOINT_XFER_BULK)
++ || ((ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
++ == USB_ENDPOINT_XFER_INT))) {
++
++ dev_dbg(&intf->dev, ": acceptable outbound endpoint "
++ "found\n");
++ ep_out = ep;
++ ep_out->bmAttributes = USB_ENDPOINT_XFER_INT;
++ if (!is_pinnacle)
++ /*
++ * Ideally, we'd use what the device offers up,
++ * but that leads to non-functioning first and
++ * second-gen devices, and many devices have an
++ * invalid bInterval of 0. Pinnacle devices
++ * don't work witha bInterval of 1 though.
++ */
++ ep_out->bInterval = 1;
++ }
++ }
++ if (ep_in == NULL) {
++ dev_dbg(&intf->dev, ": inbound and/or endpoint not found\n");
++ return -ENODEV;
++ }
++
++ devnum = dev->devnum;
++ pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress);
++ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
++
++ mem_failure = 0;
++ ir = kzalloc(sizeof(struct mceusb_dev), GFP_KERNEL);
++ if (!ir)
++ goto mem_alloc_fail;
++
++ driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
++ if (!driver)
++ goto mem_alloc_fail;
++
++ rbuf = kzalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
++ if (!rbuf)
++ goto mem_alloc_fail;
++
++ if (lirc_buffer_init(rbuf, sizeof(int), LIRCBUF_SIZE))
++ goto mem_alloc_fail;
++
++ ir->buf_in = usb_buffer_alloc(dev, maxp, GFP_ATOMIC, &ir->dma_in);
++ if (!ir->buf_in)
++ goto buf_in_alloc_fail;
++
++ ir->urb_in = usb_alloc_urb(0, GFP_KERNEL);
++ if (!ir->urb_in)
++ goto urb_in_alloc_fail;
++
++ strcpy(driver->name, DRIVER_NAME);
++ driver->minor = -1;
++ driver->features = LIRC_CAN_SEND_PULSE |
++ LIRC_CAN_SET_TRANSMITTER_MASK |
++ LIRC_CAN_REC_MODE2 |
++ LIRC_CAN_SET_SEND_CARRIER;
++ driver->data = ir;
++ driver->rbuf = rbuf;
++ driver->set_use_inc = &mceusb_ir_open;
++ driver->set_use_dec = &mceusb_ir_close;
++ driver->code_length = sizeof(int) * 8;
++ driver->fops = &lirc_fops;
++ driver->dev = &intf->dev;
++ driver->owner = THIS_MODULE;
++
++ mutex_init(&ir->dev_lock);
++ init_waitqueue_head(&ir->wait_out);
++
++ minor = lirc_register_driver(driver);
++ if (minor < 0)
++ goto lirc_register_fail;
++
++ driver->minor = minor;
++ ir->d = driver;
++ ir->devnum = devnum;
++ ir->usbdev = dev;
++ ir->len_in = maxp;
++ ir->overflow_len = 0;
++ ir->flags.connected = 0;
++ ir->flags.gen3 = is_gen3;
++ ir->flags.microsoft_gen1 = is_microsoft_gen1;
++ ir->flags.transmitter_mask_inverted =
++ usb_match_id(intf, transmitter_mask_list) ? 0 : 1;
++
++ ir->lircdata = PULSE_MASK;
++ ir->is_pulse = 0;
++
++ /* ir->flags.transmitter_mask_inverted must be set */
++ set_transmitter_mask(ir, MCE_DEFAULT_TX_MASK);
++ /* Saving usb interface data for use by the transmitter routine */
++ ir->usb_ep_in = ep_in;
++ ir->usb_ep_out = ep_out;
++
++ if (dev->descriptor.iManufacturer
++ && usb_string(dev, dev->descriptor.iManufacturer,
++ buf, sizeof(buf)) > 0)
++ strlcpy(name, buf, sizeof(name));
++ if (dev->descriptor.iProduct
++ && usb_string(dev, dev->descriptor.iProduct,
++ buf, sizeof(buf)) > 0)
++ snprintf(name + strlen(name), sizeof(name) - strlen(name),
++ " %s", buf);
++
++ /* inbound data */
++ usb_fill_int_urb(ir->urb_in, dev, pipe, ir->buf_in,
++ maxp, (usb_complete_t) mceusb_dev_recv, ir, ep_in->bInterval);
++ ir->urb_in->transfer_dma = ir->dma_in;
++ ir->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
++
++ if (is_pinnacle) {
++ int usbret;
++
++ /*
++ * I have no idea why but this reset seems to be crucial to
++ * getting the device to do outbound IO correctly - without
++ * this the device seems to hang, ignoring all input - although
++ * IR signals are correctly sent from the device, no input is
++ * interpreted by the device and the host never does the
++ * completion routine
++ */
++ usbret = usb_reset_configuration(dev);
++ dev_info(ir->d->dev, "usb reset config ret %x\n", usbret);
++ }
++
++ /* initialize device */
++ if (ir->flags.gen3) {
++ mce_sync_in(ir, NULL, maxp);
++
++ /* device reset */
++ mce_async_out(ir, DEVICE_RESET, sizeof(DEVICE_RESET));
++ mce_sync_in(ir, NULL, maxp);
++
++ /* get the carrier and frequency */
++ mce_async_out(ir, GET_CARRIER_FREQ, sizeof(GET_CARRIER_FREQ));
++ mce_sync_in(ir, NULL, maxp);
++
++ /* get the transmitter bitmask */
++ mce_async_out(ir, GET_TX_BITMASK, sizeof(GET_TX_BITMASK));
++ mce_sync_in(ir, NULL, maxp);
++
++ /* get receiver timeout value */
++ mce_async_out(ir, GET_RX_TIMEOUT, sizeof(GET_RX_TIMEOUT));
++ mce_sync_in(ir, NULL, maxp);
++
++ /* get receiver sensor setting */
++ mce_async_out(ir, GET_RX_SENSOR, sizeof(GET_RX_SENSOR));
++ mce_sync_in(ir, NULL, maxp);
++
++ } else if (ir->flags.microsoft_gen1) {
++ /* original ms mce device requires some additional setup */
++ mceusb_gen1_init(ir);
++
++ } else {
++ mce_sync_in(ir, NULL, maxp);
++ mce_sync_in(ir, NULL, maxp);
++
++ /* device reset */
++ mce_async_out(ir, DEVICE_RESET, sizeof(DEVICE_RESET));
++ mce_sync_in(ir, NULL, maxp);
++
++ /* get hw/sw revision? */
++ mce_async_out(ir, GET_REVISION, sizeof(GET_REVISION));
++ mce_sync_in(ir, NULL, maxp);
++
++ /* unknown what this actually returns... */
++ mce_async_out(ir, GET_UNKNOWN, sizeof(GET_UNKNOWN));
++ mce_sync_in(ir, NULL, maxp);
++ }
++
++ /*
++ * if we don't issue the correct number of receives (mce_sync_in())
++ * for each outbound, then the first few ir pulses will be interpreted
++ * by the usb_async_callback routine - we should ensure we have the
++ * right amount OR less - as the mceusb_dev_recv routine will handle
++ * the control packets OK - they start with 0x9f - but the async
++ * callback doesn't handle ir pulse packets
++ */
++ mce_sync_in(ir, NULL, maxp);
++
++ usb_set_intfdata(intf, ir);
++
++ dev_info(ir->d->dev, "Registered %s on usb%d:%d\n", name,
++ dev->bus->busnum, devnum);
++
++ return 0;
++
++ /* Error-handling path */
++lirc_register_fail:
++ usb_free_urb(ir->urb_in);
++urb_in_alloc_fail:
++ usb_buffer_free(dev, maxp, ir->buf_in, ir->dma_in);
++buf_in_alloc_fail:
++ lirc_buffer_free(rbuf);
++mem_alloc_fail:
++ kfree(rbuf);
++ kfree(driver);
++ kfree(ir);
++ dev_info(&intf->dev, "out of memory (code=%d)\n", mem_failure);
++
++ return -ENOMEM;
++}
++
++
++static void mceusb_dev_disconnect(struct usb_interface *intf)
++{
++ struct usb_device *dev = interface_to_usbdev(intf);
++ struct mceusb_dev *ir = usb_get_intfdata(intf);
++
++ usb_set_intfdata(intf, NULL);
++
++ if (!ir || !ir->d)
++ return;
++
++ ir->usbdev = NULL;
++ wake_up_all(&ir->wait_out);
++
++ mutex_lock(&ir->dev_lock);
++ usb_kill_urb(ir->urb_in);
++ usb_free_urb(ir->urb_in);
++ usb_buffer_free(dev, ir->len_in, ir->buf_in, ir->dma_in);
++ mutex_unlock(&ir->dev_lock);
++
++ unregister_from_lirc(ir);
++}
++
++static int mceusb_dev_suspend(struct usb_interface *intf, pm_message_t message)
++{
++ struct mceusb_dev *ir = usb_get_intfdata(intf);
++ dev_info(ir->d->dev, "suspend\n");
++ usb_kill_urb(ir->urb_in);
++ return 0;
++}
++
++static int mceusb_dev_resume(struct usb_interface *intf)
++{
++ struct mceusb_dev *ir = usb_get_intfdata(intf);
++ dev_info(ir->d->dev, "resume\n");
++ if (usb_submit_urb(ir->urb_in, GFP_ATOMIC))
++ return -EIO;
++ return 0;
++}
++
++static struct usb_driver mceusb_dev_driver = {
++ .name = DRIVER_NAME,
++ .probe = mceusb_dev_probe,
++ .disconnect = mceusb_dev_disconnect,
++ .suspend = mceusb_dev_suspend,
++ .resume = mceusb_dev_resume,
++ .reset_resume = mceusb_dev_resume,
++ .id_table = mceusb_dev_table
++};
++
++static int __init mceusb_dev_init(void)
++{
++ int i;
++
++ printk(KERN_INFO DRIVER_NAME ": " DRIVER_DESC " " DRIVER_VERSION "\n");
++ printk(KERN_INFO DRIVER_NAME ": " DRIVER_AUTHOR "\n");
++ if (debug)
++ printk(KERN_DEBUG DRIVER_NAME ": debug mode enabled\n");
++
++ i = usb_register(&mceusb_dev_driver);
++ if (i < 0) {
++ printk(KERN_ERR DRIVER_NAME
++ ": usb register failed, result = %d\n", i);
++ return -ENODEV;
++ }
++
++ return 0;
++}
++
++static void __exit mceusb_dev_exit(void)
++{
++ usb_deregister(&mceusb_dev_driver);
++}
++
++module_init(mceusb_dev_init);
++module_exit(mceusb_dev_exit);
++
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_AUTHOR(DRIVER_AUTHOR);
++MODULE_LICENSE("GPL");
++MODULE_DEVICE_TABLE(usb, mceusb_dev_table);
++/* this was originally lirc_mceusb2, lirc_mceusb and lirc_mceusb2 merged now */
++MODULE_ALIAS("lirc_mceusb2");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Debug enabled or not");
+diff --git a/drivers/input/lirc/lirc_parallel.c b/drivers/input/lirc/lirc_parallel.c
+new file mode 100644
+index 0000000..bb57b3e
+--- /dev/null
++++ b/drivers/input/lirc/lirc_parallel.c
+@@ -0,0 +1,709 @@
++/*
++ * lirc_parallel.c
++ *
++ * lirc_parallel - device driver for infra-red signal receiving and
++ * transmitting unit built by the author
++ *
++ * Copyright (C) 1998 Christoph Bartelmus <lirc@bartelmus.de>
++ *
++ * 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
++ *
++ */
++
++/*** Includes ***/
++
++#ifdef CONFIG_SMP
++#error "--- Sorry, this driver is not SMP safe. ---"
++#endif
++
++#include <linux/module.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/fs.h>
++#include <linux/kernel.h>
++#include <linux/ioport.h>
++#include <linux/time.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++
++#include <linux/io.h>
++#include <linux/signal.h>
++#include <linux/irq.h>
++#include <linux/uaccess.h>
++#include <asm/div64.h>
++
++#include <linux/poll.h>
++#include <linux/parport.h>
++
++#include <linux/lirc.h>
++#include "lirc_dev.h"
++
++#include "lirc_parallel.h"
++
++#define LIRC_DRIVER_NAME "lirc_parallel"
++
++#ifndef LIRC_IRQ
++#define LIRC_IRQ 7
++#endif
++#ifndef LIRC_PORT
++#define LIRC_PORT 0x378
++#endif
++#ifndef LIRC_TIMER
++#define LIRC_TIMER 65536
++#endif
++
++/*** Global Variables ***/
++
++static int debug;
++static int check_pselecd;
++
++unsigned int irq = LIRC_IRQ;
++unsigned int io = LIRC_PORT;
++#ifdef LIRC_TIMER
++unsigned int timer;
++unsigned int default_timer = LIRC_TIMER;
++#endif
++
++#define WBUF_SIZE (256)
++#define RBUF_SIZE (256) /* this must be a power of 2 larger than 1 */
++
++static int wbuf[WBUF_SIZE];
++static int rbuf[RBUF_SIZE];
++
++DECLARE_WAIT_QUEUE_HEAD(lirc_wait);
++
++unsigned int rptr;
++unsigned int wptr;
++unsigned int lost_irqs;
++int is_open;
++
++struct parport *pport;
++struct pardevice *ppdevice;
++int is_claimed;
++
++unsigned int tx_mask = 1;
++
++/*** Internal Functions ***/
++
++static unsigned int in(int offset)
++{
++ switch (offset) {
++ case LIRC_LP_BASE:
++ return parport_read_data(pport);
++ case LIRC_LP_STATUS:
++ return parport_read_status(pport);
++ case LIRC_LP_CONTROL:
++ return parport_read_control(pport);
++ }
++ return 0; /* make compiler happy */
++}
++
++static void out(int offset, int value)
++{
++ switch (offset) {
++ case LIRC_LP_BASE:
++ parport_write_data(pport, value);
++ break;
++ case LIRC_LP_CONTROL:
++ parport_write_control(pport, value);
++ break;
++ case LIRC_LP_STATUS:
++ printk(KERN_INFO "%s: attempt to write to status register\n",
++ LIRC_DRIVER_NAME);
++ break;
++ }
++}
++
++static unsigned int lirc_get_timer(void)
++{
++ return in(LIRC_PORT_TIMER) & LIRC_PORT_TIMER_BIT;
++}
++
++static unsigned int lirc_get_signal(void)
++{
++ return in(LIRC_PORT_SIGNAL) & LIRC_PORT_SIGNAL_BIT;
++}
++
++static void lirc_on(void)
++{
++ out(LIRC_PORT_DATA, tx_mask);
++}
++
++static void lirc_off(void)
++{
++ out(LIRC_PORT_DATA, 0);
++}
++
++static unsigned int init_lirc_timer(void)
++{
++ struct timeval tv, now;
++ unsigned int level, newlevel, timeelapsed, newtimer;
++ int count = 0;
++
++ do_gettimeofday(&tv);
++ tv.tv_sec++; /* wait max. 1 sec. */
++ level = lirc_get_timer();
++ do {
++ newlevel = lirc_get_timer();
++ if (level == 0 && newlevel != 0)
++ count++;
++ level = newlevel;
++ do_gettimeofday(&now);
++ } while (count < 1000 && (now.tv_sec < tv.tv_sec
++ || (now.tv_sec == tv.tv_sec
++ && now.tv_usec < tv.tv_usec)));
++
++ timeelapsed = ((now.tv_sec + 1 - tv.tv_sec)*1000000
++ + (now.tv_usec - tv.tv_usec));
++ if (count >= 1000 && timeelapsed > 0) {
++ if (default_timer == 0) {
++ /* autodetect timer */
++ newtimer = (1000000*count)/timeelapsed;
++ printk(KERN_INFO "%s: %u Hz timer detected\n",
++ LIRC_DRIVER_NAME, newtimer);
++ return newtimer;
++ } else {
++ newtimer = (1000000*count)/timeelapsed;
++ if (abs(newtimer - default_timer) > default_timer/10) {
++ /* bad timer */
++ printk(KERN_NOTICE "%s: bad timer: %u Hz\n",
++ LIRC_DRIVER_NAME, newtimer);
++ printk(KERN_NOTICE "%s: using default timer: "
++ "%u Hz\n",
++ LIRC_DRIVER_NAME, default_timer);
++ return default_timer;
++ } else {
++ printk(KERN_INFO "%s: %u Hz timer detected\n",
++ LIRC_DRIVER_NAME, newtimer);
++ return newtimer; /* use detected value */
++ }
++ }
++ } else {
++ printk(KERN_NOTICE "%s: no timer detected\n", LIRC_DRIVER_NAME);
++ return 0;
++ }
++}
++
++static int lirc_claim(void)
++{
++ if (parport_claim(ppdevice) != 0) {
++ printk(KERN_WARNING "%s: could not claim port\n",
++ LIRC_DRIVER_NAME);
++ printk(KERN_WARNING "%s: waiting for port becoming available"
++ "\n", LIRC_DRIVER_NAME);
++ if (parport_claim_or_block(ppdevice) < 0) {
++ printk(KERN_NOTICE "%s: could not claim port, giving"
++ " up\n", LIRC_DRIVER_NAME);
++ return 0;
++ }
++ }
++ out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
++ is_claimed = 1;
++ return 1;
++}
++
++/*** interrupt handler ***/
++
++static void rbuf_write(int signal)
++{
++ unsigned int nwptr;
++
++ nwptr = (wptr + 1) & (RBUF_SIZE - 1);
++ if (nwptr == rptr) {
++ /* no new signals will be accepted */
++ lost_irqs++;
++ printk(KERN_NOTICE "%s: buffer overrun\n", LIRC_DRIVER_NAME);
++ return;
++ }
++ rbuf[wptr] = signal;
++ wptr = nwptr;
++}
++
++static void irq_handler(void *blah)
++{
++ struct timeval tv;
++ static struct timeval lasttv;
++ static int init;
++ long signal;
++ int data;
++ unsigned int level, newlevel;
++ unsigned int timeout;
++
++ if (!module_refcount(THIS_MODULE))
++ return;
++
++ if (!is_claimed)
++ return;
++
++#if 0
++ /* disable interrupt */
++ disable_irq(irq);
++ out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ) & (~LP_PINTEN));
++#endif
++ if (check_pselecd && (in(1) & LP_PSELECD))
++ return;
++
++#ifdef LIRC_TIMER
++ if (init) {
++ do_gettimeofday(&tv);
++
++ signal = tv.tv_sec - lasttv.tv_sec;
++ if (signal > 15)
++ /* really long time */
++ data = PULSE_MASK;
++ else
++ data = (int) (signal*1000000 +
++ tv.tv_usec - lasttv.tv_usec +
++ LIRC_SFH506_DELAY);
++
++ rbuf_write(data); /* space */
++ } else {
++ if (timer == 0) {
++ /*
++ * wake up; we'll lose this signal, but it will be
++ * garbage if the device is turned on anyway
++ */
++ timer = init_lirc_timer();
++ /* enable_irq(irq); */
++ return;
++ }
++ init = 1;
++ }
++
++ timeout = timer/10; /* timeout after 1/10 sec. */
++ signal = 1;
++ level = lirc_get_timer();
++ do {
++ newlevel = lirc_get_timer();
++ if (level == 0 && newlevel != 0)
++ signal++;
++ level = newlevel;
++
++ /* giving up */
++ if (signal > timeout
++ || (check_pselecd && (in(1) & LP_PSELECD))) {
++ signal = 0;
++ printk(KERN_NOTICE "%s: timeout\n", LIRC_DRIVER_NAME);
++ break;
++ }
++ } while (lirc_get_signal());
++
++ if (signal != 0) {
++ /* ajust value to usecs */
++ unsigned long long helper;
++
++ helper = ((unsigned long long) signal)*1000000;
++ do_div(helper, timer);
++ signal = (long) helper;
++
++ if (signal > LIRC_SFH506_DELAY)
++ data = signal - LIRC_SFH506_DELAY;
++ else
++ data = 1;
++ rbuf_write(PULSE_BIT|data); /* pulse */
++ }
++ do_gettimeofday(&lasttv);
++#else
++ /* add your code here */
++#endif
++
++ wake_up_interruptible(&lirc_wait);
++
++ /* enable interrupt */
++ /*
++ enable_irq(irq);
++ out(LIRC_PORT_IRQ, in(LIRC_PORT_IRQ)|LP_PINTEN);
++ */
++}
++
++/*** file operations ***/
++
++static loff_t lirc_lseek(struct file *filep, loff_t offset, int orig)
++{
++ return -ESPIPE;
++}
++
++static ssize_t lirc_read(struct file *filep, char *buf, size_t n, loff_t *ppos)
++{
++ int result = 0;
++ int count = 0;
++ DECLARE_WAITQUEUE(wait, current);
++
++ if (n % sizeof(int))
++ return -EINVAL;
++
++ add_wait_queue(&lirc_wait, &wait);
++ set_current_state(TASK_INTERRUPTIBLE);
++ while (count < n) {
++ if (rptr != wptr) {
++ if (copy_to_user(buf+count, (char *) &rbuf[rptr],
++ sizeof(int))) {
++ result = -EFAULT;
++ break;
++ }
++ rptr = (rptr + 1) & (RBUF_SIZE - 1);
++ count += sizeof(int);
++ } else {
++ if (filep->f_flags & O_NONBLOCK) {
++ result = -EAGAIN;
++ break;
++ }
++ if (signal_pending(current)) {
++ result = -ERESTARTSYS;
++ break;
++ }
++ schedule();
++ set_current_state(TASK_INTERRUPTIBLE);
++ }
++ }
++ remove_wait_queue(&lirc_wait, &wait);
++ set_current_state(TASK_RUNNING);
++ return count ? count : result;
++}
++
++static ssize_t lirc_write(struct file *filep, const char *buf, size_t n,
++ loff_t *ppos)
++{
++ int count;
++ unsigned int i;
++ unsigned int level, newlevel;
++ unsigned long flags;
++ int counttimer;
++
++ if (!is_claimed)
++ return -EBUSY;
++
++ if (n % sizeof(int))
++ return -EINVAL;
++
++ count = n / sizeof(int);
++
++ if (count > WBUF_SIZE || count % 2 == 0)
++ return -EINVAL;
++
++ if (copy_from_user(wbuf, buf, n))
++ return -EFAULT;
++
++#ifdef LIRC_TIMER
++ if (timer == 0) {
++ /* try again if device is ready */
++ timer = init_lirc_timer();
++ if (timer == 0)
++ return -EIO;
++ }
++
++ /* adjust values from usecs */
++ for (i = 0; i < count; i++) {
++ unsigned long long helper;
++
++ helper = ((unsigned long long) wbuf[i])*timer;
++ do_div(helper, 1000000);
++ wbuf[i] = (int) helper;
++ }
++
++ local_irq_save(flags);
++ i = 0;
++ while (i < count) {
++ level = lirc_get_timer();
++ counttimer = 0;
++ lirc_on();
++ do {
++ newlevel = lirc_get_timer();
++ if (level == 0 && newlevel != 0)
++ counttimer++;
++ level = newlevel;
++ if (check_pselecd && (in(1) & LP_PSELECD)) {
++ lirc_off();
++ local_irq_restore(flags);
++ return -EIO;
++ }
++ } while (counttimer < wbuf[i]);
++ i++;
++
++ lirc_off();
++ if (i == count)
++ break;
++ counttimer = 0;
++ do {
++ newlevel = lirc_get_timer();
++ if (level == 0 && newlevel != 0)
++ counttimer++;
++ level = newlevel;
++ if (check_pselecd && (in(1) & LP_PSELECD)) {
++ local_irq_restore(flags);
++ return -EIO;
++ }
++ } while (counttimer < wbuf[i]);
++ i++;
++ }
++ local_irq_restore(flags);
++#else
++ /* place code that handles write without external timer here */
++#endif
++ return n;
++}
++
++static unsigned int lirc_poll(struct file *file, poll_table *wait)
++{
++ poll_wait(file, &lirc_wait, wait);
++ if (rptr != wptr)
++ return POLLIN | POLLRDNORM;
++ return 0;
++}
++
++static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd,
++ unsigned long arg)
++{
++ int result;
++ unsigned long features = LIRC_CAN_SET_TRANSMITTER_MASK |
++ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
++ unsigned long mode;
++ unsigned int ivalue;
++
++ switch (cmd) {
++ case LIRC_GET_FEATURES:
++ result = put_user(features, (unsigned long *) arg);
++ if (result)
++ return result;
++ break;
++ case LIRC_GET_SEND_MODE:
++ result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg);
++ if (result)
++ return result;
++ break;
++ case LIRC_GET_REC_MODE:
++ result = put_user(LIRC_MODE_MODE2, (unsigned long *) arg);
++ if (result)
++ return result;
++ break;
++ case LIRC_SET_SEND_MODE:
++ result = get_user(mode, (unsigned long *) arg);
++ if (result)
++ return result;
++ if (mode != LIRC_MODE_PULSE)
++ return -EINVAL;
++ break;
++ case LIRC_SET_REC_MODE:
++ result = get_user(mode, (unsigned long *) arg);
++ if (result)
++ return result;
++ if (mode != LIRC_MODE_MODE2)
++ return -ENOSYS;
++ break;
++ case LIRC_SET_TRANSMITTER_MASK:
++ result = get_user(ivalue, (unsigned int *) arg);
++ if (result)
++ return result;
++ if ((ivalue & LIRC_PARALLEL_TRANSMITTER_MASK) != ivalue)
++ return LIRC_PARALLEL_MAX_TRANSMITTERS;
++ tx_mask = ivalue;
++ break;
++ default:
++ return -ENOIOCTLCMD;
++ }
++ return 0;
++}
++
++static int lirc_open(struct inode *node, struct file *filep)
++{
++ if (module_refcount(THIS_MODULE) || !lirc_claim())
++ return -EBUSY;
++
++ parport_enable_irq(pport);
++
++ /* init read ptr */
++ rptr = 0;
++ wptr = 0;
++ lost_irqs = 0;
++
++ is_open = 1;
++ return 0;
++}
++
++static int lirc_close(struct inode *node, struct file *filep)
++{
++ if (is_claimed) {
++ is_claimed = 0;
++ parport_release(ppdevice);
++ }
++ is_open = 0;
++ return 0;
++}
++
++static struct file_operations lirc_fops = {
++ .owner = THIS_MODULE,
++ .llseek = lirc_lseek,
++ .read = lirc_read,
++ .write = lirc_write,
++ .poll = lirc_poll,
++ .ioctl = lirc_ioctl,
++ .open = lirc_open,
++ .release = lirc_close
++};
++
++static int set_use_inc(void *data)
++{
++ return 0;
++}
++
++static void set_use_dec(void *data)
++{
++}
++
++static struct lirc_driver driver = {
++ .name = LIRC_DRIVER_NAME,
++ .minor = -1,
++ .code_length = 1,
++ .sample_rate = 0,
++ .data = NULL,
++ .add_to_buf = NULL,
++ .set_use_inc = set_use_inc,
++ .set_use_dec = set_use_dec,
++ .fops = &lirc_fops,
++ .dev = NULL,
++ .owner = THIS_MODULE,
++};
++
++static int pf(void *handle);
++static void kf(void *handle);
++
++static struct timer_list poll_timer;
++static void poll_state(unsigned long ignored);
++
++static void poll_state(unsigned long ignored)
++{
++ printk(KERN_NOTICE "%s: time\n",
++ LIRC_DRIVER_NAME);
++ del_timer(&poll_timer);
++ if (is_claimed)
++ return;
++ kf(NULL);
++ if (!is_claimed) {
++ printk(KERN_NOTICE "%s: could not claim port, giving up\n",
++ LIRC_DRIVER_NAME);
++ init_timer(&poll_timer);
++ poll_timer.expires = jiffies + HZ;
++ poll_timer.data = (unsigned long)current;
++ poll_timer.function = poll_state;
++ add_timer(&poll_timer);
++ }
++}
++
++static int pf(void *handle)
++{
++ parport_disable_irq(pport);
++ is_claimed = 0;
++ return 0;
++}
++
++static void kf(void *handle)
++{
++ if (!is_open)
++ return;
++ if (!lirc_claim())
++ return;
++ parport_enable_irq(pport);
++ lirc_off();
++ /* this is a bit annoying when you actually print...*/
++ /*
++ printk(KERN_INFO "%s: reclaimed port\n", LIRC_DRIVER_NAME);
++ */
++}
++
++/*** module initialization and cleanup ***/
++
++static int __init lirc_parallel_init(void)
++{
++ pport = parport_find_base(io);
++ if (pport == NULL) {
++ printk(KERN_NOTICE "%s: no port at %x found\n",
++ LIRC_DRIVER_NAME, io);
++ return -ENXIO;
++ }
++ ppdevice = parport_register_device(pport, LIRC_DRIVER_NAME,
++ pf, kf, irq_handler, 0, NULL);
++ parport_put_port(pport);
++ if (ppdevice == NULL) {
++ printk(KERN_NOTICE "%s: parport_register_device() failed\n",
++ LIRC_DRIVER_NAME);
++ return -ENXIO;
++ }
++ if (parport_claim(ppdevice) != 0)
++ goto skip_init;
++ is_claimed = 1;
++ out(LIRC_LP_CONTROL, LP_PSELECP|LP_PINITP);
++
++#ifdef LIRC_TIMER
++ if (debug)
++ out(LIRC_PORT_DATA, tx_mask);
++
++ timer = init_lirc_timer();
++
++#if 0 /* continue even if device is offline */
++ if (timer == 0) {
++ is_claimed = 0;
++ parport_release(pport);
++ parport_unregister_device(ppdevice);
++ return -EIO;
++ }
++
++#endif
++ if (debug)
++ out(LIRC_PORT_DATA, 0);
++#endif
++
++ is_claimed = 0;
++ parport_release(ppdevice);
++ skip_init:
++ driver.minor = lirc_register_driver(&driver);
++ if (driver.minor < 0) {
++ printk(KERN_NOTICE "%s: register_chrdev() failed\n",
++ LIRC_DRIVER_NAME);
++ parport_unregister_device(ppdevice);
++ return -EIO;
++ }
++ printk(KERN_INFO "%s: installed using port 0x%04x irq %d\n",
++ LIRC_DRIVER_NAME, io, irq);
++ return 0;
++}
++
++static void __exit lirc_parallel_exit(void)
++{
++ parport_unregister_device(ppdevice);
++ lirc_unregister_driver(driver.minor);
++}
++
++module_init(lirc_parallel_init);
++module_exit(lirc_parallel_exit);
++
++MODULE_DESCRIPTION("Infrared receiver driver for parallel ports.");
++MODULE_AUTHOR("Christoph Bartelmus");
++MODULE_LICENSE("GPL");
++
++module_param(io, int, S_IRUGO);
++MODULE_PARM_DESC(io, "I/O address base (0x3bc, 0x378 or 0x278)");
++
++module_param(irq, int, S_IRUGO);
++MODULE_PARM_DESC(irq, "Interrupt (7 or 5)");
++
++module_param(tx_mask, int, S_IRUGO);
++MODULE_PARM_DESC(tx_maxk, "Transmitter mask (default: 0x01)");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
++
++module_param(check_pselecd, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Check for printer (default: 0)");
+diff --git a/drivers/input/lirc/lirc_parallel.h b/drivers/input/lirc/lirc_parallel.h
+new file mode 100644
+index 0000000..4bed6af
+--- /dev/null
++++ b/drivers/input/lirc/lirc_parallel.h
+@@ -0,0 +1,26 @@
++/* lirc_parallel.h */
++
++#ifndef _LIRC_PARALLEL_H
++#define _LIRC_PARALLEL_H
++
++#include <linux/lp.h>
++
++#define LIRC_PORT_LEN 3
++
++#define LIRC_LP_BASE 0
++#define LIRC_LP_STATUS 1
++#define LIRC_LP_CONTROL 2
++
++#define LIRC_PORT_DATA LIRC_LP_BASE /* base */
++#define LIRC_PORT_TIMER LIRC_LP_STATUS /* status port */
++#define LIRC_PORT_TIMER_BIT LP_PBUSY /* busy signal */
++#define LIRC_PORT_SIGNAL LIRC_LP_STATUS /* status port */
++#define LIRC_PORT_SIGNAL_BIT LP_PACK /* ack signal */
++#define LIRC_PORT_IRQ LIRC_LP_CONTROL /* control port */
++
++#define LIRC_SFH506_DELAY 0 /* delay t_phl in usecs */
++
++#define LIRC_PARALLEL_MAX_TRANSMITTERS 8
++#define LIRC_PARALLEL_TRANSMITTER_MASK ((1<<LIRC_PARALLEL_MAX_TRANSMITTERS) - 1)
++
++#endif
+diff --git a/drivers/input/lirc/lirc_sasem.c b/drivers/input/lirc/lirc_sasem.c
+new file mode 100644
+index 0000000..4015684
+--- /dev/null
++++ b/drivers/input/lirc/lirc_sasem.c
+@@ -0,0 +1,931 @@
++/*
++ * lirc_sasem.c - USB remote support for LIRC
++ * Version 0.5
++ *
++ * Copyright (C) 2004-2005 Oliver Stabel <oliver.stabel@gmx.de>
++ * Tim Davies <tim@opensystems.net.au>
++ *
++ * This driver was derived from:
++ * Venky Raju <dev@venky.ws>
++ * "lirc_imon - "LIRC/VFD driver for Ahanix/Soundgraph IMON IR/VFD"
++ * Paul Miller <pmiller9@users.sourceforge.net>'s 2003-2004
++ * "lirc_atiusb - USB remote support for LIRC"
++ * Culver Consulting Services <henry@culcon.com>'s 2003
++ * "Sasem OnAir VFD/IR USB driver"
++ *
++ *
++ * NOTE - The LCDproc iMon driver should work with this module. More info at
++ * http://www.frogstorm.info/sasem
++ */
++
++/*
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/usb.h>
++
++#include <linux/lirc.h>
++#include "lirc_dev.h"
++
++
++#define MOD_AUTHOR "Oliver Stabel <oliver.stabel@gmx.de>, " \
++ "Tim Davies <tim@opensystems.net.au>"
++#define MOD_DESC "USB Driver for Sasem Remote Controller V1.1"
++#define MOD_NAME "lirc_sasem"
++#define MOD_VERSION "0.5"
++
++#define VFD_MINOR_BASE 144 /* Same as LCD */
++#define DEVICE_NAME "lcd%d"
++
++#define BUF_CHUNK_SIZE 8
++#define BUF_SIZE 128
++
++#define IOCTL_LCD_CONTRAST 1
++
++/*** P R O T O T Y P E S ***/
++
++/* USB Callback prototypes */
++static int sasem_probe(struct usb_interface *interface,
++ const struct usb_device_id *id);
++static void sasem_disconnect(struct usb_interface *interface);
++static void usb_rx_callback(struct urb *urb);
++static void usb_tx_callback(struct urb *urb);
++
++/* VFD file_operations function prototypes */
++static int vfd_open(struct inode *inode, struct file *file);
++static int vfd_ioctl(struct inode *inode, struct file *file,
++ unsigned cmd, unsigned long arg);
++static int vfd_close(struct inode *inode, struct file *file);
++static ssize_t vfd_write(struct file *file, const char *buf,
++ size_t n_bytes, loff_t *pos);
++
++/* LIRC driver function prototypes */
++static int ir_open(void *data);
++static void ir_close(void *data);
++
++/* Driver init/exit prototypes */
++static int __init sasem_init(void);
++static void __exit sasem_exit(void);
++
++/*** G L O B A L S ***/
++
++struct sasem_context {
++
++ struct usb_device *dev;
++ int vfd_isopen; /* VFD port has been opened */
++ unsigned int vfd_contrast; /* VFD contrast */
++ int ir_isopen; /* IR port has been opened */
++ int dev_present; /* USB device presence */
++ struct mutex ctx_lock; /* to lock this object */
++ wait_queue_head_t remove_ok; /* For unexpected USB disconnects */
++
++ struct lirc_driver *driver;
++ struct usb_endpoint_descriptor *rx_endpoint;
++ struct usb_endpoint_descriptor *tx_endpoint;
++ struct urb *rx_urb;
++ struct urb *tx_urb;
++ unsigned char usb_rx_buf[8];
++ unsigned char usb_tx_buf[8];
++
++ struct tx_t {
++ unsigned char data_buf[32]; /* user data buffer */
++ struct completion finished; /* wait for write to finish */
++ atomic_t busy; /* write in progress */
++ int status; /* status of tx completion */
++ } tx;
++
++ /* for dealing with repeat codes (wish there was a toggle bit!) */
++ struct timeval presstime;
++ char lastcode[8];
++ int codesaved;
++};
++
++/* VFD file operations */
++static struct file_operations vfd_fops = {
++ .owner = THIS_MODULE,
++ .open = &vfd_open,
++ .write = &vfd_write,
++ .ioctl = &vfd_ioctl,
++ .release = &vfd_close,
++};
++
++/* USB Device ID for Sasem USB Control Board */
++static struct usb_device_id sasem_usb_id_table[] = {
++ /* Sasem USB Control Board */
++ { USB_DEVICE(0x11ba, 0x0101) },
++ /* Terminating entry */
++ {}
++};
++
++/* USB Device data */
++static struct usb_driver sasem_driver = {
++ .name = MOD_NAME,
++ .probe = sasem_probe,
++ .disconnect = sasem_disconnect,
++ .id_table = sasem_usb_id_table,
++};
++
++static struct usb_class_driver sasem_class = {
++ .name = DEVICE_NAME,
++ .fops = &vfd_fops,
++ .minor_base = VFD_MINOR_BASE,
++};
++
++/* to prevent races between open() and disconnect() */
++static DEFINE_MUTEX(disconnect_lock);
++
++static int debug;
++
++
++/*** M O D U L E C O D E ***/
++
++MODULE_AUTHOR(MOD_AUTHOR);
++MODULE_DESCRIPTION(MOD_DESC);
++MODULE_LICENSE("GPL");
++module_param(debug, int, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes (default: no)");
++
++static void delete_context(struct sasem_context *context)
++{
++ usb_free_urb(context->tx_urb); /* VFD */
++ usb_free_urb(context->rx_urb); /* IR */
++ lirc_buffer_free(context->driver->rbuf);
++ kfree(context->driver->rbuf);
++ kfree(context->driver);
++ kfree(context);
++
++ if (debug)
++ printk(KERN_INFO "%s: context deleted\n", __func__);
++}
++
++static void deregister_from_lirc(struct sasem_context *context)
++{
++ int retval;
++ int minor = context->driver->minor;
++
++ retval = lirc_unregister_driver(minor);
++ if (retval)
++ err("%s: unable to deregister from lirc (%d)",
++ __func__, retval);
++ else
++ printk(KERN_INFO "Deregistered Sasem driver (minor:%d)\n",
++ minor);
++
++}
++
++/**
++ * Called when the VFD device (e.g. /dev/usb/lcd)
++ * is opened by the application.
++ */
++static int vfd_open(struct inode *inode, struct file *file)
++{
++ struct usb_interface *interface;
++ struct sasem_context *context = NULL;
++ int subminor;
++ int retval = 0;
++
++ /* prevent races with disconnect */
++ mutex_lock(&disconnect_lock);
++
++ subminor = iminor(inode);
++ interface = usb_find_interface(&sasem_driver, subminor);
++ if (!interface) {
++ err("%s: could not find interface for minor %d",
++ __func__, subminor);
++ retval = -ENODEV;
++ goto exit;
++ }
++ context = usb_get_intfdata(interface);
++
++ if (!context) {
++ err("%s: no context found for minor %d",
++ __func__, subminor);
++ retval = -ENODEV;
++ goto exit;
++ }
++
++ mutex_lock(&context->ctx_lock);
++
++ if (context->vfd_isopen) {
++ err("%s: VFD port is already open", __func__);
++ retval = -EBUSY;
++ } else {
++ context->vfd_isopen = 1;
++ file->private_data = context;
++ printk(KERN_INFO "VFD port opened\n");
++ }
++
++ mutex_unlock(&context->ctx_lock);
++
++exit:
++ mutex_unlock(&disconnect_lock);
++ return retval;
++}
++
++/**
++ * Called when the VFD device (e.g. /dev/usb/lcd)
++ * is closed by the application.
++ */
++static int vfd_ioctl(struct inode *inode, struct file *file,
++ unsigned cmd, unsigned long arg)
++{
++ struct sasem_context *context = NULL;
++
++ context = (struct sasem_context *) file->private_data;
++
++ if (!context) {
++ err("%s: no context for device", __func__);
++ return -ENODEV;
++ }
++
++ mutex_lock(&context->ctx_lock);
++
++ switch (cmd) {
++ case IOCTL_LCD_CONTRAST:
++ if (arg > 1000)
++ arg = 1000;
++ context->vfd_contrast = (unsigned int)arg;
++ break;
++ default:
++ printk(KERN_INFO "Unknown IOCTL command\n");
++ mutex_unlock(&context->ctx_lock);
++ return -ENOIOCTLCMD; /* not supported */
++ }
++
++ mutex_unlock(&context->ctx_lock);
++ return 0;
++}
++
++/**
++ * Called when the VFD device (e.g. /dev/usb/lcd)
++ * is closed by the application.
++ */
++static int vfd_close(struct inode *inode, struct file *file)
++{
++ struct sasem_context *context = NULL;
++ int retval = 0;
++
++ context = (struct sasem_context *) file->private_data;
++
++ if (!context) {
++ err("%s: no context for device", __func__);
++ return -ENODEV;
++ }
++
++ mutex_lock(&context->ctx_lock);
++
++ if (!context->vfd_isopen) {
++ err("%s: VFD is not open", __func__);
++ retval = -EIO;
++ } else {
++ context->vfd_isopen = 0;
++ printk(KERN_INFO "VFD port closed\n");
++ if (!context->dev_present && !context->ir_isopen) {
++
++ /* Device disconnected before close and IR port is
++ * not open. If IR port is open, context will be
++ * deleted by ir_close. */
++ mutex_unlock(&context->ctx_lock);
++ delete_context(context);
++ return retval;
++ }
++ }
++
++ mutex_unlock(&context->ctx_lock);
++ return retval;
++}
++
++/**
++ * Sends a packet to the VFD.
++ */
++static int send_packet(struct sasem_context *context)
++{
++ unsigned int pipe;
++ int interval = 0;
++ int retval = 0;
++
++ pipe = usb_sndintpipe(context->dev,
++ context->tx_endpoint->bEndpointAddress);
++ interval = context->tx_endpoint->bInterval;
++
++ usb_fill_int_urb(context->tx_urb, context->dev, pipe,
++ context->usb_tx_buf, sizeof(context->usb_tx_buf),
++ usb_tx_callback, context, interval);
++
++ context->tx_urb->actual_length = 0;
++
++ init_completion(&context->tx.finished);
++ atomic_set(&(context->tx.busy), 1);
++
++ retval = usb_submit_urb(context->tx_urb, GFP_KERNEL);
++ if (retval) {
++ atomic_set(&(context->tx.busy), 0);
++ err("%s: error submitting urb (%d)", __func__, retval);
++ } else {
++ /* Wait for transmission to complete (or abort) */
++ mutex_unlock(&context->ctx_lock);
++ wait_for_completion(&context->tx.finished);
++ mutex_lock(&context->ctx_lock);
++
++ retval = context->tx.status;
++ if (retval)
++ err("%s: packet tx failed (%d)", __func__, retval);
++ }
++
++ return retval;
++}
++
++/**
++ * Writes data to the VFD. The Sasem VFD is 2x16 characters
++ * and requires data in 9 consecutive USB interrupt packets,
++ * each packet carrying 8 bytes.
++ */
++static ssize_t vfd_write(struct file *file, const char *buf,
++ size_t n_bytes, loff_t *pos)
++{
++ int i;
++ int retval = 0;
++ struct sasem_context *context;
++
++ context = (struct sasem_context *) file->private_data;
++ if (!context) {
++ err("%s: no context for device", __func__);
++ return -ENODEV;
++ }
++
++ mutex_lock(&context->ctx_lock);
++
++ if (!context->dev_present) {
++ err("%s: no Sasem device present", __func__);
++ retval = -ENODEV;
++ goto exit;
++ }
++
++ if (n_bytes <= 0 || n_bytes > 32) {
++ err("%s: invalid payload size", __func__);
++ retval = -EINVAL;
++ goto exit;
++ }
++
++ retval = copy_from_user(context->tx.data_buf, buf, n_bytes);
++ if (retval < 0)
++ goto exit;
++
++ /* Pad with spaces */
++ for (i = n_bytes; i < 32; ++i)
++ context->tx.data_buf[i] = ' ';
++
++ /* Nine 8 byte packets to be sent */
++ /* NOTE: "\x07\x01\0\0\0\0\0\0" or "\x0c\0\0\0\0\0\0\0"
++ * will clear the VFD */
++ for (i = 0; i < 9; i++) {
++ switch (i) {
++ case 0:
++ memcpy(context->usb_tx_buf, "\x07\0\0\0\0\0\0\0", 8);
++ context->usb_tx_buf[1] = (context->vfd_contrast) ?
++ (0x2B - (context->vfd_contrast - 1) / 250)
++ : 0x2B;
++ break;
++ case 1:
++ memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
++ break;
++ case 2:
++ memcpy(context->usb_tx_buf, "\x0b\x01\0\0\0\0\0\0", 8);
++ break;
++ case 3:
++ memcpy(context->usb_tx_buf, context->tx.data_buf, 8);
++ break;
++ case 4:
++ memcpy(context->usb_tx_buf,
++ context->tx.data_buf + 8, 8);
++ break;
++ case 5:
++ memcpy(context->usb_tx_buf, "\x09\x01\0\0\0\0\0\0", 8);
++ break;
++ case 6:
++ memcpy(context->usb_tx_buf, "\x0b\x02\0\0\0\0\0\0", 8);
++ break;
++ case 7:
++ memcpy(context->usb_tx_buf,
++ context->tx.data_buf + 16, 8);
++ break;
++ case 8:
++ memcpy(context->usb_tx_buf,
++ context->tx.data_buf + 24, 8);
++ break;
++ }
++ retval = send_packet(context);
++ if (retval) {
++
++ err("%s: send packet failed for packet #%d",
++ __func__, i);
++ goto exit;
++ }
++ }
++exit:
++
++ mutex_unlock(&context->ctx_lock);
++
++ return (!retval) ? n_bytes : retval;
++}
++
++/**
++ * Callback function for USB core API: transmit data
++ */
++static void usb_tx_callback(struct urb *urb)
++{
++ struct sasem_context *context;
++
++ if (!urb)
++ return;
++ context = (struct sasem_context *) urb->context;
++ if (!context)
++ return;
++
++ context->tx.status = urb->status;
++
++ /* notify waiters that write has finished */
++ atomic_set(&context->tx.busy, 0);
++ complete(&context->tx.finished);
++
++ return;
++}
++
++/**
++ * Called by lirc_dev when the application opens /dev/lirc
++ */
++static int ir_open(void *data)
++{
++ int retval = 0;
++ struct sasem_context *context;
++
++ /* prevent races with disconnect */
++ mutex_lock(&disconnect_lock);
++
++ context = (struct sasem_context *) data;
++
++ mutex_lock(&context->ctx_lock);
++
++ if (context->ir_isopen) {
++ err("%s: IR port is already open", __func__);
++ retval = -EBUSY;
++ goto exit;
++ }
++
++ usb_fill_int_urb(context->rx_urb, context->dev,
++ usb_rcvintpipe(context->dev,
++ context->rx_endpoint->bEndpointAddress),
++ context->usb_rx_buf, sizeof(context->usb_rx_buf),
++ usb_rx_callback, context, context->rx_endpoint->bInterval);
++
++ retval = usb_submit_urb(context->rx_urb, GFP_KERNEL);
++
++ if (retval)
++ err("%s: usb_submit_urb failed for ir_open (%d)",
++ __func__, retval);
++ else {
++ context->ir_isopen = 1;
++ printk(KERN_INFO "IR port opened\n");
++ }
++
++exit:
++ mutex_unlock(&context->ctx_lock);
++
++ mutex_unlock(&disconnect_lock);
++ return 0;
++}
++
++/**
++ * Called by lirc_dev when the application closes /dev/lirc
++ */
++static void ir_close(void *data)
++{
++ struct sasem_context *context;
++
++ context = (struct sasem_context *)data;
++ if (!context) {
++ err("%s: no context for device", __func__);
++ return;
++ }
++
++ mutex_lock(&context->ctx_lock);
++
++ usb_kill_urb(context->rx_urb);
++ context->ir_isopen = 0;
++ printk(KERN_INFO "IR port closed\n");
++
++ if (!context->dev_present) {
++
++ /*
++ * Device disconnected while IR port was
++ * still open. Driver was not deregistered
++ * at disconnect time, so do it now.
++ */
++ deregister_from_lirc(context);
++
++ if (!context->vfd_isopen) {
++
++ mutex_unlock(&context->ctx_lock);
++ delete_context(context);
++ return;
++ }
++ /* If VFD port is open, context will be deleted by vfd_close */
++ }
++
++ mutex_unlock(&context->ctx_lock);
++ return;
++}
++
++/**
++ * Process the incoming packet
++ */
++static void incoming_packet(struct sasem_context *context,
++ struct urb *urb)
++{
++ int len = urb->actual_length;
++ unsigned char *buf = urb->transfer_buffer;
++ long ms;
++ struct timeval tv;
++
++ if (len != 8) {
++ printk(KERN_WARNING "%s: invalid incoming packet size (%d)\n",
++ __func__, len);
++ return;
++ }
++
++#ifdef DEBUG
++ int i;
++ for (i = 0; i < 8; ++i)
++ printk(KERN_INFO "%02x ", buf[i]);
++ printk(KERN_INFO "\n");
++#endif
++
++ /*
++ * Lirc could deal with the repeat code, but we really need to block it
++ * if it arrives too late. Otherwise we could repeat the wrong code.
++ */
++
++ /* get the time since the last button press */
++ do_gettimeofday(&tv);
++ ms = (tv.tv_sec - context->presstime.tv_sec) * 1000 +
++ (tv.tv_usec - context->presstime.tv_usec) / 1000;
++
++ if (memcmp(buf, "\x08\0\0\0\0\0\0\0", 8) == 0) {
++ /*
++ * the repeat code is being sent, so we copy
++ * the old code to LIRC
++ */
++
++ /*
++ * NOTE: Only if the last code was less than 250ms ago
++ * - no one should be able to push another (undetected) button
++ * in that time and then get a false repeat of the previous
++ * press but it is long enough for a genuine repeat
++ */
++ if ((ms < 250) && (context->codesaved != 0)) {
++ memcpy(buf, &context->lastcode, 8);
++ context->presstime.tv_sec = tv.tv_sec;
++ context->presstime.tv_usec = tv.tv_usec;
++ }
++ } else {
++ /* save the current valid code for repeats */
++ memcpy(&context->lastcode, buf, 8);
++ /*
++ * set flag to signal a valid code was save;
++ * just for safety reasons
++ */
++ context->codesaved = 1;
++ context->presstime.tv_sec = tv.tv_sec;
++ context->presstime.tv_usec = tv.tv_usec;
++ }
++
++ lirc_buffer_write(context->driver->rbuf, buf);
++ wake_up(&context->driver->rbuf->wait_poll);
++}
++
++/**
++ * Callback function for USB core API: receive data
++ */
++static void usb_rx_callback(struct urb *urb)
++{
++ struct sasem_context *context;
++
++ if (!urb)
++ return;
++ context = (struct sasem_context *) urb->context;
++ if (!context)
++ return;
++
++ switch (urb->status) {
++
++ case -ENOENT: /* usbcore unlink successful! */
++ return;
++
++ case 0:
++ if (context->ir_isopen)
++ incoming_packet(context, urb);
++ break;
++
++ default:
++ printk(KERN_WARNING "%s: status (%d): ignored",
++ __func__, urb->status);
++ break;
++ }
++
++ usb_submit_urb(context->rx_urb, GFP_ATOMIC);
++ return;
++}
++
++
++
++/**
++ * Callback function for USB core API: Probe
++ */
++static int sasem_probe(struct usb_interface *interface,
++ const struct usb_device_id *id)
++{
++ struct usb_device *dev = NULL;
++ struct usb_host_interface *iface_desc = NULL;
++ struct usb_endpoint_descriptor *rx_endpoint = NULL;
++ struct usb_endpoint_descriptor *tx_endpoint = NULL;
++ struct urb *rx_urb = NULL;
++ struct urb *tx_urb = NULL;
++ struct lirc_driver *driver = NULL;
++ struct lirc_buffer *rbuf = NULL;
++ int lirc_minor = 0;
++ int num_endpoints;
++ int retval = 0;
++ int vfd_ep_found;
++ int ir_ep_found;
++ int alloc_status;
++ struct sasem_context *context = NULL;
++ int i;
++
++ printk(KERN_INFO "%s: found Sasem device\n", __func__);
++
++
++ dev = usb_get_dev(interface_to_usbdev(interface));
++ iface_desc = interface->cur_altsetting;
++ num_endpoints = iface_desc->desc.bNumEndpoints;
++
++ /*
++ * Scan the endpoint list and set:
++ * first input endpoint = IR endpoint
++ * first output endpoint = VFD endpoint
++ */
++
++ ir_ep_found = 0;
++ vfd_ep_found = 0;
++
++ for (i = 0; i < num_endpoints && !(ir_ep_found && vfd_ep_found); ++i) {
++
++ struct usb_endpoint_descriptor *ep;
++ int ep_dir;
++ int ep_type;
++ ep = &iface_desc->endpoint [i].desc;
++ ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
++ ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
++
++ if (!ir_ep_found &&
++ ep_dir == USB_DIR_IN &&
++ ep_type == USB_ENDPOINT_XFER_INT) {
++
++ rx_endpoint = ep;
++ ir_ep_found = 1;
++ if (debug)
++ printk(KERN_INFO "%s: found IR endpoint\n",
++ __func__);
++
++ } else if (!vfd_ep_found &&
++ ep_dir == USB_DIR_OUT &&
++ ep_type == USB_ENDPOINT_XFER_INT) {
++
++ tx_endpoint = ep;
++ vfd_ep_found = 1;
++ if (debug)
++ printk(KERN_INFO "%s: found VFD endpoint\n",
++ __func__);
++ }
++ }
++
++ /* Input endpoint is mandatory */
++ if (!ir_ep_found) {
++
++ err("%s: no valid input (IR) endpoint found.", __func__);
++ retval = -ENODEV;
++ goto exit;
++ }
++
++ if (!vfd_ep_found)
++ printk(KERN_INFO "%s: no valid output (VFD) endpoint found.\n",
++ __func__);
++
++
++ /* Allocate memory */
++ alloc_status = 0;
++
++ context = kzalloc(sizeof(struct sasem_context), GFP_KERNEL);
++ if (!context) {
++ err("%s: kzalloc failed for context", __func__);
++ alloc_status = 1;
++ goto alloc_status_switch;
++ }
++ driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
++ if (!driver) {
++ err("%s: kzalloc failed for lirc_driver", __func__);
++ alloc_status = 2;
++ goto alloc_status_switch;
++ }
++ rbuf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
++ if (!rbuf) {
++ err("%s: kmalloc failed for lirc_buffer", __func__);
++ alloc_status = 3;
++ goto alloc_status_switch;
++ }
++ if (lirc_buffer_init(rbuf, BUF_CHUNK_SIZE, BUF_SIZE)) {
++ err("%s: lirc_buffer_init failed", __func__);
++ alloc_status = 4;
++ goto alloc_status_switch;
++ }
++ rx_urb = usb_alloc_urb(0, GFP_KERNEL);
++ if (!rx_urb) {
++ err("%s: usb_alloc_urb failed for IR urb", __func__);
++ alloc_status = 5;
++ goto alloc_status_switch;
++ }
++ if (vfd_ep_found) {
++ tx_urb = usb_alloc_urb(0, GFP_KERNEL);
++ if (!tx_urb) {
++ err("%s: usb_alloc_urb failed for VFD urb",
++ __func__);
++ alloc_status = 6;
++ goto alloc_status_switch;
++ }
++ }
++
++ mutex_init(&context->ctx_lock);
++
++ strcpy(driver->name, MOD_NAME);
++ driver->minor = -1;
++ driver->code_length = 64;
++ driver->sample_rate = 0;
++ driver->features = LIRC_CAN_REC_LIRCCODE;
++ driver->data = context;
++ driver->rbuf = rbuf;
++ driver->set_use_inc = ir_open;
++ driver->set_use_dec = ir_close;
++ driver->dev = &interface->dev;
++ driver->owner = THIS_MODULE;
++
++ mutex_lock(&context->ctx_lock);
++
++ lirc_minor = lirc_register_driver(driver);
++ if (lirc_minor < 0) {
++ err("%s: lirc_register_driver failed", __func__);
++ alloc_status = 7;
++ mutex_unlock(&context->ctx_lock);
++ } else
++ printk(KERN_INFO "%s: Registered Sasem driver (minor:%d)\n",
++ __func__, lirc_minor);
++
++alloc_status_switch:
++
++ switch (alloc_status) {
++
++ case 7:
++ if (vfd_ep_found)
++ usb_free_urb(tx_urb);
++ case 6:
++ usb_free_urb(rx_urb);
++ case 5:
++ lirc_buffer_free(rbuf);
++ case 4:
++ kfree(rbuf);
++ case 3:
++ kfree(driver);
++ case 2:
++ kfree(context);
++ context = NULL;
++ case 1:
++ retval = -ENOMEM;
++ goto exit;
++ }
++
++ /* Needed while unregistering! */
++ driver->minor = lirc_minor;
++
++ context->dev = dev;
++ context->dev_present = 1;
++ context->rx_endpoint = rx_endpoint;
++ context->rx_urb = rx_urb;
++ if (vfd_ep_found) {
++ context->tx_endpoint = tx_endpoint;
++ context->tx_urb = tx_urb;
++ context->vfd_contrast = 1000; /* range 0 - 1000 */
++ }
++ context->driver = driver;
++
++ usb_set_intfdata(interface, context);
++
++ if (vfd_ep_found) {
++
++ if (debug)
++ printk(KERN_INFO "Registering VFD with sysfs\n");
++ if (usb_register_dev(interface, &sasem_class))
++ /* Not a fatal error, so ignore */
++ printk(KERN_INFO "%s: could not get a minor number "
++ "for VFD\n", __func__);
++ }
++
++ printk(KERN_INFO "%s: Sasem device on usb<%d:%d> initialized\n",
++ __func__, dev->bus->busnum, dev->devnum);
++
++ mutex_unlock(&context->ctx_lock);
++exit:
++ return retval;
++}
++
++/**
++ * Callback function for USB core API: disonnect
++ */
++static void sasem_disconnect(struct usb_interface *interface)
++{
++ struct sasem_context *context;
++
++ /* prevent races with ir_open()/vfd_open() */
++ mutex_lock(&disconnect_lock);
++
++ context = usb_get_intfdata(interface);
++ mutex_lock(&context->ctx_lock);
++
++ printk(KERN_INFO "%s: Sasem device disconnected\n", __func__);
++
++ usb_set_intfdata(interface, NULL);
++ context->dev_present = 0;
++
++ /* Stop reception */
++ usb_kill_urb(context->rx_urb);
++
++ /* Abort ongoing write */
++ if (atomic_read(&context->tx.busy)) {
++
++ usb_kill_urb(context->tx_urb);
++ wait_for_completion(&context->tx.finished);
++ }
++
++ /* De-register from lirc_dev if IR port is not open */
++ if (!context->ir_isopen)
++ deregister_from_lirc(context);
++
++ usb_deregister_dev(interface, &sasem_class);
++
++ mutex_unlock(&context->ctx_lock);
++
++ if (!context->ir_isopen && !context->vfd_isopen)
++ delete_context(context);
++
++ mutex_unlock(&disconnect_lock);
++}
++
++static int __init sasem_init(void)
++{
++ int rc;
++
++ printk(KERN_INFO MOD_DESC ", v" MOD_VERSION "\n");
++ printk(KERN_INFO MOD_AUTHOR "\n");
++
++ rc = usb_register(&sasem_driver);
++ if (rc < 0) {
++ err("%s: usb register failed (%d)", __func__, rc);
++ return -ENODEV;
++ }
++ return 0;
++}
++
++static void __exit sasem_exit(void)
++{
++ usb_deregister(&sasem_driver);
++ printk(KERN_INFO "module removed. Goodbye!\n");
++}
++
++
++module_init(sasem_init);
++module_exit(sasem_exit);
+diff --git a/drivers/input/lirc/lirc_serial.c b/drivers/input/lirc/lirc_serial.c
+new file mode 100644
+index 0000000..f4fcc37
+--- /dev/null
++++ b/drivers/input/lirc/lirc_serial.c
+@@ -0,0 +1,1317 @@
++/*
++ * lirc_serial.c
++ *
++ * lirc_serial - Device driver that records pulse- and pause-lengths
++ * (space-lengths) between DDCD event on a serial port.
++ *
++ * Copyright (C) 1996,97 Ralph Metzler <rjkm@thp.uni-koeln.de>
++ * Copyright (C) 1998 Trent Piepho <xyzzy@u.washington.edu>
++ * Copyright (C) 1998 Ben Pfaff <blp@gnu.org>
++ * Copyright (C) 1999 Christoph Bartelmus <lirc@bartelmus.de>
++ * Copyright (C) 2007 Andrei Tanas <andrei@tanas.ca> (suspend/resume support)
++ * 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
++ *
++ */
++
++/*
++ * Steve's changes to improve transmission fidelity:
++ * - for systems with the rdtsc instruction and the clock counter, a
++ * send_pule that times the pulses directly using the counter.
++ * This means that the LIRC_SERIAL_TRANSMITTER_LATENCY fudge is
++ * not needed. Measurement shows very stable waveform, even where
++ * PCI activity slows the access to the UART, which trips up other
++ * versions.
++ * - For other system, non-integer-microsecond pulse/space lengths,
++ * done using fixed point binary. So, much more accurate carrier
++ * frequency.
++ * - fine tuned transmitter latency, taking advantage of fractional
++ * microseconds in previous change
++ * - Fixed bug in the way transmitter latency was accounted for by
++ * tuning the pulse lengths down - the send_pulse routine ignored
++ * this overhead as it timed the overall pulse length - so the
++ * pulse frequency was right but overall pulse length was too
++ * long. Fixed by accounting for latency on each pulse/space
++ * iteration.
++ *
++ * Steve Davies <steve@daviesfam.org> July 2001
++ */
++
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/sched.h>
++#include <linux/fs.h>
++#include <linux/interrupt.h>
++#include <linux/ioport.h>
++#include <linux/kernel.h>
++#include <linux/serial_reg.h>
++#include <linux/time.h>
++#include <linux/string.h>
++#include <linux/types.h>
++#include <linux/wait.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++#include <linux/poll.h>
++#include <linux/platform_device.h>
++
++#include <asm/system.h>
++#include <linux/io.h>
++#include <linux/irq.h>
++#include <linux/fcntl.h>
++#include <linux/spinlock.h>
++
++#ifdef CONFIG_LIRC_SERIAL_NSLU2
++#include <asm/hardware.h>
++#endif
++/* From Intel IXP42X Developer's Manual (#252480-005): */
++/* ftp://download.intel.com/design/network/manuals/25248005.pdf */
++#define UART_IE_IXP42X_UUE 0x40 /* IXP42X UART Unit enable */
++#define UART_IE_IXP42X_RTOIE 0x10 /* IXP42X Receiver Data Timeout int.enable */
++
++#include <linux/lirc.h>
++#include "lirc_dev.h"
++
++#define LIRC_DRIVER_NAME "lirc_serial"
++
++struct lirc_serial {
++ int signal_pin;
++ int signal_pin_change;
++ u8 on;
++ u8 off;
++ long (*send_pulse)(unsigned long length);
++ void (*send_space)(long length);
++ int features;
++ spinlock_t lock;
++};
++
++#define LIRC_HOMEBREW 0
++#define LIRC_IRDEO 1
++#define LIRC_IRDEO_REMOTE 2
++#define LIRC_ANIMAX 3
++#define LIRC_IGOR 4
++#define LIRC_NSLU2 5
++
++/*** module parameters ***/
++static int type;
++static int io;
++static int irq;
++static int iommap;
++static int ioshift;
++static int softcarrier = 1;
++static int share_irq;
++static int debug;
++static int sense = -1; /* -1 = auto, 0 = active high, 1 = active low */
++static int txsense; /* 0 = active high, 1 = active low */
++
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
++ fmt, ## args); \
++ } while (0)
++
++/* forward declarations */
++static long send_pulse_irdeo(unsigned long length);
++static long send_pulse_homebrew(unsigned long length);
++static void send_space_irdeo(long length);
++static void send_space_homebrew(long length);
++
++static struct lirc_serial hardware[] = {
++ [LIRC_HOMEBREW] = {
++ .signal_pin = UART_MSR_DCD,
++ .signal_pin_change = UART_MSR_DDCD,
++ .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
++ .off = (UART_MCR_RTS | UART_MCR_OUT2),
++ .send_pulse = send_pulse_homebrew,
++ .send_space = send_space_homebrew,
++#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
++ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
++ LIRC_CAN_SET_SEND_CARRIER |
++ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
++#else
++ .features = LIRC_CAN_REC_MODE2
++#endif
++ },
++
++ [LIRC_IRDEO] = {
++ .signal_pin = UART_MSR_DSR,
++ .signal_pin_change = UART_MSR_DDSR,
++ .on = UART_MCR_OUT2,
++ .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
++ .send_pulse = send_pulse_irdeo,
++ .send_space = send_space_irdeo,
++ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
++ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
++ },
++
++ [LIRC_IRDEO_REMOTE] = {
++ .signal_pin = UART_MSR_DSR,
++ .signal_pin_change = UART_MSR_DDSR,
++ .on = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
++ .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
++ .send_pulse = send_pulse_irdeo,
++ .send_space = send_space_irdeo,
++ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
++ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
++ },
++
++ [LIRC_ANIMAX] = {
++ .signal_pin = UART_MSR_DCD,
++ .signal_pin_change = UART_MSR_DDCD,
++ .on = 0,
++ .off = (UART_MCR_RTS | UART_MCR_DTR | UART_MCR_OUT2),
++ .send_pulse = NULL,
++ .send_space = NULL,
++ .features = LIRC_CAN_REC_MODE2
++ },
++
++ [LIRC_IGOR] = {
++ .signal_pin = UART_MSR_DSR,
++ .signal_pin_change = UART_MSR_DDSR,
++ .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
++ .off = (UART_MCR_RTS | UART_MCR_OUT2),
++ .send_pulse = send_pulse_homebrew,
++ .send_space = send_space_homebrew,
++#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
++ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
++ LIRC_CAN_SET_SEND_CARRIER |
++ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
++#else
++ .features = LIRC_CAN_REC_MODE2
++#endif
++ },
++
++#ifdef CONFIG_LIRC_SERIAL_NSLU2
++ /*
++ * Modified Linksys Network Storage Link USB 2.0 (NSLU2):
++ * We receive on CTS of the 2nd serial port (R142,LHS), we
++ * transmit with a IR diode between GPIO[1] (green status LED),
++ * and ground (Matthias Goebl <matthias.goebl@goebl.net>).
++ * See also http://www.nslu2-linux.org for this device
++ */
++ [LIRC_NSLU2] = {
++ .signal_pin = UART_MSR_CTS,
++ .signal_pin_change = UART_MSR_DCTS,
++ .on = (UART_MCR_RTS | UART_MCR_OUT2 | UART_MCR_DTR),
++ .off = (UART_MCR_RTS | UART_MCR_OUT2),
++ .send_pulse = send_pulse_homebrew,
++ .send_space = send_space_homebrew,
++#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
++ .features = (LIRC_CAN_SET_SEND_DUTY_CYCLE |
++ LIRC_CAN_SET_SEND_CARRIER |
++ LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2)
++#else
++ .features = LIRC_CAN_REC_MODE2
++#endif
++ },
++#endif
++
++};
++
++#define RS_ISR_PASS_LIMIT 256
++
++/*
++ * A long pulse code from a remote might take up to 300 bytes. The
++ * daemon should read the bytes as soon as they are generated, so take
++ * the number of keys you think you can push before the daemon runs
++ * and multiply by 300. The driver will warn you if you overrun this
++ * buffer. If you have a slow computer or non-busmastering IDE disks,
++ * maybe you will need to increase this.
++ */
++
++/* This MUST be a power of two! It has to be larger than 1 as well. */
++
++#define RBUF_LEN 256
++#define WBUF_LEN 256
++
++static struct timeval lasttv = {0, 0};
++
++static struct lirc_buffer rbuf;
++
++static int wbuf[WBUF_LEN];
++
++static unsigned int freq = 38000;
++static unsigned int duty_cycle = 50;
++
++/* Initialized in init_timing_params() */
++static unsigned long period;
++static unsigned long pulse_width;
++static unsigned long space_width;
++
++#if defined(__i386__)
++/*
++ * From:
++ * Linux I/O port programming mini-HOWTO
++ * Author: Riku Saikkonen <Riku.Saikkonen@hut.fi>
++ * v, 28 December 1997
++ *
++ * [...]
++ * Actually, a port I/O instruction on most ports in the 0-0x3ff range
++ * takes almost exactly 1 microsecond, so if you're, for example, using
++ * the parallel port directly, just do additional inb()s from that port
++ * to delay.
++ * [...]
++ */
++/* transmitter latency 1.5625us 0x1.90 - this figure arrived at from
++ * comment above plus trimming to match actual measured frequency.
++ * This will be sensitive to cpu speed, though hopefully most of the 1.5us
++ * is spent in the uart access. Still - for reference test machine was a
++ * 1.13GHz Athlon system - Steve
++ */
++
++/*
++ * changed from 400 to 450 as this works better on slower machines;
++ * faster machines will use the rdtsc code anyway
++ */
++#define LIRC_SERIAL_TRANSMITTER_LATENCY 450
++
++#else
++
++/* does anybody have information on other platforms ? */
++/* 256 = 1<<8 */
++#define LIRC_SERIAL_TRANSMITTER_LATENCY 256
++
++#endif /* __i386__ */
++/*
++ * FIXME: should we be using hrtimers instead of this
++ * LIRC_SERIAL_TRANSMITTER_LATENCY nonsense?
++ */
++
++/* fetch serial input packet (1 byte) from register offset */
++static u8 sinp(int offset)
++{
++ if (iommap != 0)
++ /* the register is memory-mapped */
++ offset <<= ioshift;
++
++ return inb(io + offset);
++}
++
++/* write serial output packet (1 byte) of value to register offset */
++static void soutp(int offset, u8 value)
++{
++ if (iommap != 0)
++ /* the register is memory-mapped */
++ offset <<= ioshift;
++
++ outb(value, io + offset);
++}
++
++static void on(void)
++{
++#ifdef CONFIG_LIRC_SERIAL_NSLU2
++ /*
++ * On NSLU2, we put the transmit diode between the output of the green
++ * status LED and ground
++ */
++ if (type == LIRC_NSLU2) {
++ gpio_line_set(NSLU2_LED_GRN, IXP4XX_GPIO_LOW);
++ return;
++ }
++#endif
++ if (txsense)
++ soutp(UART_MCR, hardware[type].off);
++ else
++ soutp(UART_MCR, hardware[type].on);
++}
++
++static void off(void)
++{
++#ifdef CONFIG_LIRC_SERIAL_NSLU2
++ if (type == LIRC_NSLU2) {
++ gpio_line_set(NSLU2_LED_GRN, IXP4XX_GPIO_HIGH);
++ return;
++ }
++#endif
++ if (txsense)
++ soutp(UART_MCR, hardware[type].on);
++ else
++ soutp(UART_MCR, hardware[type].off);
++}
++
++#ifndef MAX_UDELAY_MS
++#define MAX_UDELAY_US 5000
++#else
++#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
++#endif
++
++static void safe_udelay(unsigned long usecs)
++{
++ while (usecs > MAX_UDELAY_US) {
++ udelay(MAX_UDELAY_US);
++ usecs -= MAX_UDELAY_US;
++ }
++ udelay(usecs);
++}
++
++#ifdef USE_RDTSC
++/*
++ * This is an overflow/precision juggle, complicated in that we can't
++ * do long long divide in the kernel
++ */
++
++/*
++ * When we use the rdtsc instruction to measure clocks, we keep the
++ * pulse and space widths as clock cycles. As this is CPU speed
++ * dependent, the widths must be calculated in init_port and ioctl
++ * time
++ */
++
++/* So send_pulse can quickly convert microseconds to clocks */
++static unsigned long conv_us_to_clocks;
++
++static int init_timing_params(unsigned int new_duty_cycle,
++ unsigned int new_freq)
++{
++ unsigned long long loops_per_sec, work;
++
++ duty_cycle = new_duty_cycle;
++ freq = new_freq;
++
++ loops_per_sec = current_cpu_data.loops_per_jiffy;
++ loops_per_sec *= HZ;
++
++ /* How many clocks in a microsecond?, avoiding long long divide */
++ work = loops_per_sec;
++ work *= 4295; /* 4295 = 2^32 / 1e6 */
++ conv_us_to_clocks = (work >> 32);
++
++ /*
++ * Carrier period in clocks, approach good up to 32GHz clock,
++ * gets carrier frequency within 8Hz
++ */
++ period = loops_per_sec >> 3;
++ period /= (freq >> 3);
++
++ /* Derive pulse and space from the period */
++ pulse_width = period * duty_cycle / 100;
++ space_width = period - pulse_width;
++ dprintk("in init_timing_params, freq=%d, duty_cycle=%d, "
++ "clk/jiffy=%ld, pulse=%ld, space=%ld, "
++ "conv_us_to_clocks=%ld\n",
++ freq, duty_cycle, current_cpu_data.loops_per_jiffy,
++ pulse_width, space_width, conv_us_to_clocks);
++ return 0;
++}
++#else /* ! USE_RDTSC */
++static int init_timing_params(unsigned int new_duty_cycle,
++ unsigned int new_freq)
++{
++/*
++ * period, pulse/space width are kept with 8 binary places -
++ * IE multiplied by 256.
++ */
++ if (256 * 1000000L / new_freq * new_duty_cycle / 100 <=
++ LIRC_SERIAL_TRANSMITTER_LATENCY)
++ return -EINVAL;
++ if (256 * 1000000L / new_freq * (100 - new_duty_cycle) / 100 <=
++ LIRC_SERIAL_TRANSMITTER_LATENCY)
++ return -EINVAL;
++ duty_cycle = new_duty_cycle;
++ freq = new_freq;
++ period = 256 * 1000000L / freq;
++ pulse_width = period * duty_cycle / 100;
++ space_width = period - pulse_width;
++ dprintk("in init_timing_params, freq=%d pulse=%ld, "
++ "space=%ld\n", freq, pulse_width, space_width);
++ return 0;
++}
++#endif /* USE_RDTSC */
++
++
++/* return value: space length delta */
++
++static long send_pulse_irdeo(unsigned long length)
++{
++ long rawbits, ret;
++ int i;
++ unsigned char output;
++ unsigned char chunk, shifted;
++
++ /* how many bits have to be sent ? */
++ rawbits = length * 1152 / 10000;
++ if (duty_cycle > 50)
++ chunk = 3;
++ else
++ chunk = 1;
++ for (i = 0, output = 0x7f; rawbits > 0; rawbits -= 3) {
++ shifted = chunk << (i * 3);
++ shifted >>= 1;
++ output &= (~shifted);
++ i++;
++ if (i == 3) {
++ soutp(UART_TX, output);
++ while (!(sinp(UART_LSR) & UART_LSR_THRE))
++ ;
++ output = 0x7f;
++ i = 0;
++ }
++ }
++ if (i != 0) {
++ soutp(UART_TX, output);
++ while (!(sinp(UART_LSR) & UART_LSR_TEMT))
++ ;
++ }
++
++ if (i == 0)
++ ret = (-rawbits) * 10000 / 1152;
++ else
++ ret = (3 - i) * 3 * 10000 / 1152 + (-rawbits) * 10000 / 1152;
++
++ return ret;
++}
++
++#ifdef USE_RDTSC
++/* Version that uses Pentium rdtsc instruction to measure clocks */
++
++/*
++ * This version does sub-microsecond timing using rdtsc instruction,
++ * and does away with the fudged LIRC_SERIAL_TRANSMITTER_LATENCY
++ * Implicitly i586 architecture... - Steve
++ */
++
++static long send_pulse_homebrew_softcarrier(unsigned long length)
++{
++ int flag;
++ unsigned long target, start, now;
++
++ /* Get going quick as we can */
++ rdtscl(start);
++ on();
++ /* Convert length from microseconds to clocks */
++ length *= conv_us_to_clocks;
++ /* And loop till time is up - flipping at right intervals */
++ now = start;
++ target = pulse_width;
++ flag = 1;
++ /*
++ * FIXME: This looks like a hard busy wait, without even an occasional,
++ * polite, cpu_relax() call. There's got to be a better way?
++ *
++ * The i2c code has the result of a lot of bit-banging work, I wonder if
++ * there's something there which could be helpful here.
++ */
++ while ((now - start) < length) {
++ /* Delay till flip time */
++ do {
++ rdtscl(now);
++ } while ((now - start) < target);
++
++ /* flip */
++ if (flag) {
++ rdtscl(now);
++ off();
++ target += space_width;
++ } else {
++ rdtscl(now); on();
++ target += pulse_width;
++ }
++ flag = !flag;
++ }
++ rdtscl(now);
++ return ((now - start) - length) / conv_us_to_clocks;
++}
++#else /* ! USE_RDTSC */
++/* Version using udelay() */
++
++/*
++ * here we use fixed point arithmetic, with 8
++ * fractional bits. that gets us within 0.1% or so of the right average
++ * frequency, albeit with some jitter in pulse length - Steve
++ */
++
++/* To match 8 fractional bits used for pulse/space length */
++
++static long send_pulse_homebrew_softcarrier(unsigned long length)
++{
++ int flag;
++ unsigned long actual, target, d;
++ length <<= 8;
++
++ actual = 0; target = 0; flag = 0;
++ while (actual < length) {
++ if (flag) {
++ off();
++ target += space_width;
++ } else {
++ on();
++ target += pulse_width;
++ }
++ d = (target - actual -
++ LIRC_SERIAL_TRANSMITTER_LATENCY + 128) >> 8;
++ /*
++ * Note - we've checked in ioctl that the pulse/space
++ * widths are big enough so that d is > 0
++ */
++ udelay(d);
++ actual += (d << 8) + LIRC_SERIAL_TRANSMITTER_LATENCY;
++ flag = !flag;
++ }
++ return (actual-length) >> 8;
++}
++#endif /* USE_RDTSC */
++
++static long send_pulse_homebrew(unsigned long length)
++{
++ if (length <= 0)
++ return 0;
++
++ if (softcarrier)
++ return send_pulse_homebrew_softcarrier(length);
++ else {
++ on();
++ safe_udelay(length);
++ return 0;
++ }
++}
++
++static void send_space_irdeo(long length)
++{
++ if (length <= 0)
++ return;
++
++ safe_udelay(length);
++}
++
++static void send_space_homebrew(long length)
++{
++ off();
++ if (length <= 0)
++ return;
++ safe_udelay(length);
++}
++
++static void rbwrite(int l)
++{
++ if (lirc_buffer_full(&rbuf)) {
++ /* no new signals will be accepted */
++ dprintk("Buffer overrun\n");
++ return;
++ }
++ lirc_buffer_write(&rbuf, (void *)&l);
++}
++
++static void frbwrite(int l)
++{
++ /* simple noise filter */
++ static int pulse, space;
++ static unsigned int ptr;
++
++ if (ptr > 0 && (l & PULSE_BIT)) {
++ pulse += l & PULSE_MASK;
++ if (pulse > 250) {
++ rbwrite(space);
++ rbwrite(pulse | PULSE_BIT);
++ ptr = 0;
++ pulse = 0;
++ }
++ return;
++ }
++ if (!(l & PULSE_BIT)) {
++ if (ptr == 0) {
++ if (l > 20000) {
++ space = l;
++ ptr++;
++ return;
++ }
++ } else {
++ if (l > 20000) {
++ space += pulse;
++ if (space > PULSE_MASK)
++ space = PULSE_MASK;
++ space += l;
++ if (space > PULSE_MASK)
++ space = PULSE_MASK;
++ pulse = 0;
++ return;
++ }
++ rbwrite(space);
++ rbwrite(pulse | PULSE_BIT);
++ ptr = 0;
++ pulse = 0;
++ }
++ }
++ rbwrite(l);
++}
++
++static irqreturn_t irq_handler(int i, void *blah)
++{
++ struct timeval tv;
++ int counter, dcd;
++ u8 status;
++ long deltv;
++ int data;
++ static int last_dcd = -1;
++
++ if ((sinp(UART_IIR) & UART_IIR_NO_INT)) {
++ /* not our interrupt */
++ return IRQ_NONE;
++ }
++
++ counter = 0;
++ do {
++ counter++;
++ status = sinp(UART_MSR);
++ if (counter > RS_ISR_PASS_LIMIT) {
++ printk(KERN_WARNING LIRC_DRIVER_NAME ": AIEEEE: "
++ "We're caught!\n");
++ break;
++ }
++ if ((status & hardware[type].signal_pin_change)
++ && sense != -1) {
++ /* get current time */
++ do_gettimeofday(&tv);
++
++ /* New mode, written by Trent Piepho
++ <xyzzy@u.washington.edu>. */
++
++ /*
++ * The old format was not very portable.
++ * We now use an int to pass pulses
++ * and spaces to user space.
++ *
++ * If PULSE_BIT is set a pulse has been
++ * received, otherwise a space has been
++ * received. The driver needs to know if your
++ * receiver is active high or active low, or
++ * the space/pulse sense could be
++ * inverted. The bits denoted by PULSE_MASK are
++ * the length in microseconds. Lengths greater
++ * than or equal to 16 seconds are clamped to
++ * PULSE_MASK. All other bits are unused.
++ * This is a much simpler interface for user
++ * programs, as well as eliminating "out of
++ * phase" errors with space/pulse
++ * autodetection.
++ */
++
++ /* calc time since last interrupt in microseconds */
++ dcd = (status & hardware[type].signal_pin) ? 1 : 0;
++
++ if (dcd == last_dcd) {
++ printk(KERN_WARNING LIRC_DRIVER_NAME
++ ": ignoring spike: %d %d %lx %lx %lx %lx\n",
++ dcd, sense,
++ tv.tv_sec, lasttv.tv_sec,
++ tv.tv_usec, lasttv.tv_usec);
++ continue;
++ }
++
++ deltv = tv.tv_sec-lasttv.tv_sec;
++ if (tv.tv_sec < lasttv.tv_sec ||
++ (tv.tv_sec == lasttv.tv_sec &&
++ tv.tv_usec < lasttv.tv_usec)) {
++ printk(KERN_WARNING LIRC_DRIVER_NAME
++ ": AIEEEE: your clock just jumped "
++ "backwards\n");
++ printk(KERN_WARNING LIRC_DRIVER_NAME
++ ": %d %d %lx %lx %lx %lx\n",
++ dcd, sense,
++ tv.tv_sec, lasttv.tv_sec,
++ tv.tv_usec, lasttv.tv_usec);
++ data = PULSE_MASK;
++ } else if (deltv > 15) {
++ data = PULSE_MASK; /* really long time */
++ if (!(dcd^sense)) {
++ /* sanity check */
++ printk(KERN_WARNING LIRC_DRIVER_NAME
++ ": AIEEEE: "
++ "%d %d %lx %lx %lx %lx\n",
++ dcd, sense,
++ tv.tv_sec, lasttv.tv_sec,
++ tv.tv_usec, lasttv.tv_usec);
++ /*
++ * detecting pulse while this
++ * MUST be a space!
++ */
++ sense = sense ? 0 : 1;
++ }
++ } else
++ data = (int) (deltv*1000000 +
++ tv.tv_usec -
++ lasttv.tv_usec);
++ frbwrite(dcd^sense ? data : (data|PULSE_BIT));
++ lasttv = tv;
++ last_dcd = dcd;
++ wake_up_interruptible(&rbuf.wait_poll);
++ }
++ } while (!(sinp(UART_IIR) & UART_IIR_NO_INT)); /* still pending ? */
++ return IRQ_HANDLED;
++}
++
++
++static int hardware_init_port(void)
++{
++ u8 scratch, scratch2, scratch3;
++
++ /*
++ * This is a simple port existence test, borrowed from the autoconfig
++ * function in drivers/serial/8250.c
++ */
++ scratch = sinp(UART_IER);
++ soutp(UART_IER, 0);
++#ifdef __i386__
++ outb(0xff, 0x080);
++#endif
++ scratch2 = sinp(UART_IER) & 0x0f;
++ soutp(UART_IER, 0x0f);
++#ifdef __i386__
++ outb(0x00, 0x080);
++#endif
++ scratch3 = sinp(UART_IER) & 0x0f;
++ soutp(UART_IER, scratch);
++ if (scratch2 != 0 || scratch3 != 0x0f) {
++ /* we fail, there's nothing here */
++ printk(KERN_ERR LIRC_DRIVER_NAME ": port existence test "
++ "failed, cannot continue\n");
++ return -EINVAL;
++ }
++
++
++
++ /* Set DLAB 0. */
++ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
++
++ /* First of all, disable all interrupts */
++ soutp(UART_IER, sinp(UART_IER) &
++ (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
++
++ /* Clear registers. */
++ sinp(UART_LSR);
++ sinp(UART_RX);
++ sinp(UART_IIR);
++ sinp(UART_MSR);
++
++#ifdef CONFIG_LIRC_SERIAL_NSLU2
++ if (type == LIRC_NSLU2) {
++ /* Setup NSLU2 UART */
++
++ /* Enable UART */
++ soutp(UART_IER, sinp(UART_IER) | UART_IE_IXP42X_UUE);
++ /* Disable Receiver data Time out interrupt */
++ soutp(UART_IER, sinp(UART_IER) & ~UART_IE_IXP42X_RTOIE);
++ /* set out2 = interrupt unmask; off() doesn't set MCR
++ on NSLU2 */
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
++ }
++#endif
++
++ /* Set line for power source */
++ off();
++
++ /* Clear registers again to be sure. */
++ sinp(UART_LSR);
++ sinp(UART_RX);
++ sinp(UART_IIR);
++ sinp(UART_MSR);
++
++ switch (type) {
++ case LIRC_IRDEO:
++ case LIRC_IRDEO_REMOTE:
++ /* setup port to 7N1 @ 115200 Baud */
++ /* 7N1+start = 9 bits at 115200 ~ 3 bits at 38kHz */
++
++ /* Set DLAB 1. */
++ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
++ /* Set divisor to 1 => 115200 Baud */
++ soutp(UART_DLM, 0);
++ soutp(UART_DLL, 1);
++ /* Set DLAB 0 + 7N1 */
++ soutp(UART_LCR, UART_LCR_WLEN7);
++ /* THR interrupt already disabled at this point */
++ break;
++ default:
++ break;
++ }
++
++ return 0;
++}
++
++static int init_port(void)
++{
++ int i, nlow, nhigh;
++
++ /* Reserve io region. */
++ /*
++ * Future MMAP-Developers: Attention!
++ * For memory mapped I/O you *might* need to use ioremap() first,
++ * for the NSLU2 it's done in boot code.
++ */
++ if (((iommap != 0)
++ && (request_mem_region(iommap, 8 << ioshift,
++ LIRC_DRIVER_NAME) == NULL))
++ || ((iommap == 0)
++ && (request_region(io, 8, LIRC_DRIVER_NAME) == NULL))) {
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": port %04x already in use\n", io);
++ printk(KERN_WARNING LIRC_DRIVER_NAME
++ ": use 'setserial /dev/ttySX uart none'\n");
++ printk(KERN_WARNING LIRC_DRIVER_NAME
++ ": or compile the serial port driver as module and\n");
++ printk(KERN_WARNING LIRC_DRIVER_NAME
++ ": make sure this module is loaded first\n");
++ return -EBUSY;
++ }
++
++ if (hardware_init_port() < 0)
++ return -EINVAL;
++
++ /* Initialize pulse/space widths */
++ init_timing_params(duty_cycle, freq);
++
++ /* If pin is high, then this must be an active low receiver. */
++ if (sense == -1) {
++ /* wait 1/2 sec for the power supply */
++ msleep(500);
++
++ /*
++ * probe 9 times every 0.04s, collect "votes" for
++ * active high/low
++ */
++ nlow = 0;
++ nhigh = 0;
++ for (i = 0; i < 9; i++) {
++ if (sinp(UART_MSR) & hardware[type].signal_pin)
++ nlow++;
++ else
++ nhigh++;
++ msleep(40);
++ }
++ sense = (nlow >= nhigh ? 1 : 0);
++ printk(KERN_INFO LIRC_DRIVER_NAME ": auto-detected active "
++ "%s receiver\n", sense ? "low" : "high");
++ } else
++ printk(KERN_INFO LIRC_DRIVER_NAME ": Manually using active "
++ "%s receiver\n", sense ? "low" : "high");
++
++ return 0;
++}
++
++static int set_use_inc(void *data)
++{
++ int result;
++ unsigned long flags;
++
++ /* initialize timestamp */
++ do_gettimeofday(&lasttv);
++
++ result = request_irq(irq, irq_handler,
++ IRQF_DISABLED | (share_irq ? IRQF_SHARED : 0),
++ LIRC_DRIVER_NAME, (void *)&hardware);
++
++ switch (result) {
++ case -EBUSY:
++ printk(KERN_ERR LIRC_DRIVER_NAME ": IRQ %d busy\n", irq);
++ return -EBUSY;
++ case -EINVAL:
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": Bad irq number or handler\n");
++ return -EINVAL;
++ default:
++ dprintk("Interrupt %d, port %04x obtained\n", irq, io);
++ break;
++ };
++
++ spin_lock_irqsave(&hardware[type].lock, flags);
++
++ /* Set DLAB 0. */
++ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
++
++ soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
++
++ spin_unlock_irqrestore(&hardware[type].lock, flags);
++
++ return 0;
++}
++
++static void set_use_dec(void *data)
++{ unsigned long flags;
++
++ spin_lock_irqsave(&hardware[type].lock, flags);
++
++ /* Set DLAB 0. */
++ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
++
++ /* First of all, disable all interrupts */
++ soutp(UART_IER, sinp(UART_IER) &
++ (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
++ spin_unlock_irqrestore(&hardware[type].lock, flags);
++
++ free_irq(irq, (void *)&hardware);
++
++ dprintk("freed IRQ %d\n", irq);
++}
++
++static ssize_t lirc_write(struct file *file, const char *buf,
++ size_t n, loff_t *ppos)
++{
++ int i, count;
++ unsigned long flags;
++ long delta = 0;
++
++ if (!(hardware[type].features&LIRC_CAN_SEND_PULSE))
++ return -EBADF;
++
++ if (n % sizeof(int))
++ return -EINVAL;
++ count = n / sizeof(int);
++ if (count > WBUF_LEN || count % 2 == 0)
++ return -EINVAL;
++ if (copy_from_user(wbuf, buf, n))
++ return -EFAULT;
++ spin_lock_irqsave(&hardware[type].lock, flags);
++ if (type == LIRC_IRDEO) {
++ /* DTR, RTS down */
++ on();
++ }
++ for (i = 0; i < count; i++) {
++ if (i%2)
++ hardware[type].send_space(wbuf[i]-delta);
++ else
++ delta = hardware[type].send_pulse(wbuf[i]);
++ }
++ off();
++ spin_unlock_irqrestore(&hardware[type].lock, flags);
++ return n;
++}
++
++static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd,
++ unsigned long arg)
++{
++ int result;
++ unsigned long value;
++ unsigned int ivalue;
++
++ switch (cmd) {
++ case LIRC_GET_SEND_MODE:
++ if (!(hardware[type].features&LIRC_CAN_SEND_MASK))
++ return -ENOIOCTLCMD;
++
++ result = put_user(LIRC_SEND2MODE
++ (hardware[type].features&LIRC_CAN_SEND_MASK),
++ (unsigned long *) arg);
++ if (result)
++ return result;
++ break;
++
++ case LIRC_SET_SEND_MODE:
++ if (!(hardware[type].features&LIRC_CAN_SEND_MASK))
++ return -ENOIOCTLCMD;
++
++ result = get_user(value, (unsigned long *) arg);
++ if (result)
++ return result;
++ /* only LIRC_MODE_PULSE supported */
++ if (value != LIRC_MODE_PULSE)
++ return -ENOSYS;
++ break;
++
++ case LIRC_GET_LENGTH:
++ return -ENOSYS;
++ break;
++
++ case LIRC_SET_SEND_DUTY_CYCLE:
++ dprintk("SET_SEND_DUTY_CYCLE\n");
++ if (!(hardware[type].features&LIRC_CAN_SET_SEND_DUTY_CYCLE))
++ return -ENOIOCTLCMD;
++
++ result = get_user(ivalue, (unsigned int *) arg);
++ if (result)
++ return result;
++ if (ivalue <= 0 || ivalue > 100)
++ return -EINVAL;
++ return init_timing_params(ivalue, freq);
++ break;
++
++ case LIRC_SET_SEND_CARRIER:
++ dprintk("SET_SEND_CARRIER\n");
++ if (!(hardware[type].features&LIRC_CAN_SET_SEND_CARRIER))
++ return -ENOIOCTLCMD;
++
++ result = get_user(ivalue, (unsigned int *) arg);
++ if (result)
++ return result;
++ if (ivalue > 500000 || ivalue < 20000)
++ return -EINVAL;
++ return init_timing_params(duty_cycle, ivalue);
++ break;
++
++ default:
++ return lirc_dev_fop_ioctl(node, filep, cmd, arg);
++ }
++ return 0;
++}
++
++static struct file_operations lirc_fops = {
++ .owner = THIS_MODULE,
++ .write = lirc_write,
++ .ioctl = lirc_ioctl,
++ .read = lirc_dev_fop_read,
++ .poll = lirc_dev_fop_poll,
++ .open = lirc_dev_fop_open,
++ .release = lirc_dev_fop_close,
++};
++
++static struct lirc_driver driver = {
++ .name = LIRC_DRIVER_NAME,
++ .minor = -1,
++ .code_length = 1,
++ .sample_rate = 0,
++ .data = NULL,
++ .add_to_buf = NULL,
++ .rbuf = &rbuf,
++ .set_use_inc = set_use_inc,
++ .set_use_dec = set_use_dec,
++ .fops = &lirc_fops,
++ .dev = NULL,
++ .owner = THIS_MODULE,
++};
++
++static struct platform_device *lirc_serial_dev;
++
++static int __devinit lirc_serial_probe(struct platform_device *dev)
++{
++ return 0;
++}
++
++static int __devexit lirc_serial_remove(struct platform_device *dev)
++{
++ return 0;
++}
++
++static int lirc_serial_suspend(struct platform_device *dev,
++ pm_message_t state)
++{
++ /* Set DLAB 0. */
++ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
++
++ /* Disable all interrupts */
++ soutp(UART_IER, sinp(UART_IER) &
++ (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
++
++ /* Clear registers. */
++ sinp(UART_LSR);
++ sinp(UART_RX);
++ sinp(UART_IIR);
++ sinp(UART_MSR);
++
++ return 0;
++}
++
++/* twisty maze... need a forward-declaration here... */
++static void lirc_serial_exit(void);
++
++static int lirc_serial_resume(struct platform_device *dev)
++{
++ unsigned long flags;
++
++ if (hardware_init_port() < 0) {
++ lirc_serial_exit();
++ return -EINVAL;
++ }
++
++ spin_lock_irqsave(&hardware[type].lock, flags);
++ /* Enable Interrupt */
++ do_gettimeofday(&lasttv);
++ soutp(UART_IER, sinp(UART_IER)|UART_IER_MSI);
++ off();
++
++ lirc_buffer_clear(&rbuf);
++
++ spin_unlock_irqrestore(&hardware[type].lock, flags);
++
++ return 0;
++}
++
++static struct platform_driver lirc_serial_driver = {
++ .probe = lirc_serial_probe,
++ .remove = __devexit_p(lirc_serial_remove),
++ .suspend = lirc_serial_suspend,
++ .resume = lirc_serial_resume,
++ .driver = {
++ .name = "lirc_serial",
++ .owner = THIS_MODULE,
++ },
++};
++
++static int __init lirc_serial_init(void)
++{
++ int result;
++
++ /* Init read buffer. */
++ result = lirc_buffer_init(&rbuf, sizeof(int), RBUF_LEN);
++ if (result < 0)
++ return -ENOMEM;
++
++ result = platform_driver_register(&lirc_serial_driver);
++ if (result) {
++ printk("lirc register returned %d\n", result);
++ goto exit_buffer_free;
++ }
++
++ lirc_serial_dev = platform_device_alloc("lirc_serial", 0);
++ if (!lirc_serial_dev) {
++ result = -ENOMEM;
++ goto exit_driver_unregister;
++ }
++
++ result = platform_device_add(lirc_serial_dev);
++ if (result)
++ goto exit_device_put;
++
++ return 0;
++
++exit_device_put:
++ platform_device_put(lirc_serial_dev);
++exit_driver_unregister:
++ platform_driver_unregister(&lirc_serial_driver);
++exit_buffer_free:
++ lirc_buffer_free(&rbuf);
++ return result;
++}
++
++static void lirc_serial_exit(void)
++{
++ platform_device_unregister(lirc_serial_dev);
++ platform_driver_unregister(&lirc_serial_driver);
++ lirc_buffer_free(&rbuf);
++}
++
++static int __init lirc_serial_init_module(void)
++{
++ int result;
++
++ result = lirc_serial_init();
++ if (result)
++ return result;
++
++ switch (type) {
++ case LIRC_HOMEBREW:
++ case LIRC_IRDEO:
++ case LIRC_IRDEO_REMOTE:
++ case LIRC_ANIMAX:
++ case LIRC_IGOR:
++ /* if nothing specified, use ttyS0/com1 and irq 4 */
++ io = io ? io : 0x3f8;
++ irq = irq ? irq : 4;
++ break;
++#ifdef CONFIG_LIRC_SERIAL_NSLU2
++ case LIRC_NSLU2:
++ io = io ? io : IRQ_IXP4XX_UART2;
++ irq = irq ? irq : (IXP4XX_UART2_BASE_VIRT + REG_OFFSET);
++ iommap = iommap ? iommap : IXP4XX_UART2_BASE_PHYS;
++ ioshift = ioshift ? ioshift : 2;
++ break;
++#endif
++ default:
++ result = -EINVAL;
++ goto exit_serial_exit;
++ }
++ if (!softcarrier) {
++ switch (type) {
++ case LIRC_HOMEBREW:
++ case LIRC_IGOR:
++#ifdef CONFIG_LIRC_SERIAL_NSLU2
++ case LIRC_NSLU2:
++#endif
++ hardware[type].features &=
++ ~(LIRC_CAN_SET_SEND_DUTY_CYCLE|
++ LIRC_CAN_SET_SEND_CARRIER);
++ break;
++ }
++ }
++
++ result = init_port();
++ if (result < 0)
++ goto exit_serial_exit;
++ driver.features = hardware[type].features;
++ driver.dev = &lirc_serial_dev->dev;
++ driver.minor = lirc_register_driver(&driver);
++ if (driver.minor < 0) {
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": register_chrdev failed!\n");
++ result = -EIO;
++ goto exit_release;
++ }
++ return 0;
++exit_release:
++ release_region(io, 8);
++exit_serial_exit:
++ lirc_serial_exit();
++ return result;
++}
++
++static void __exit lirc_serial_exit_module(void)
++{
++ lirc_serial_exit();
++ if (iommap != 0)
++ release_mem_region(iommap, 8 << ioshift);
++ else
++ release_region(io, 8);
++ lirc_unregister_driver(driver.minor);
++ dprintk("cleaned up module\n");
++}
++
++
++module_init(lirc_serial_init_module);
++module_exit(lirc_serial_exit_module);
++
++MODULE_DESCRIPTION("Infra-red receiver driver for serial ports.");
++MODULE_AUTHOR("Ralph Metzler, Trent Piepho, Ben Pfaff, "
++ "Christoph Bartelmus, Andrei Tanas");
++MODULE_LICENSE("GPL");
++
++module_param(type, int, S_IRUGO);
++MODULE_PARM_DESC(type, "Hardware type (0 = home-brew, 1 = IRdeo,"
++ " 2 = IRdeo Remote, 3 = AnimaX, 4 = IgorPlug,"
++ " 5 = NSLU2 RX:CTS2/TX:GreenLED)");
++
++module_param(io, int, S_IRUGO);
++MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
++
++/* some architectures (e.g. intel xscale) have memory mapped registers */
++module_param(iommap, bool, S_IRUGO);
++MODULE_PARM_DESC(iommap, "physical base for memory mapped I/O"
++ " (0 = no memory mapped io)");
++
++/*
++ * some architectures (e.g. intel xscale) align the 8bit serial registers
++ * on 32bit word boundaries.
++ * See linux-kernel/serial/8250.c serial_in()/out()
++ */
++module_param(ioshift, int, S_IRUGO);
++MODULE_PARM_DESC(ioshift, "shift I/O register offset (0 = no shift)");
++
++module_param(irq, int, S_IRUGO);
++MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
++
++module_param(share_irq, bool, S_IRUGO);
++MODULE_PARM_DESC(share_irq, "Share interrupts (0 = off, 1 = on)");
++
++module_param(sense, bool, S_IRUGO);
++MODULE_PARM_DESC(sense, "Override autodetection of IR receiver circuit"
++ " (0 = active high, 1 = active low )");
++
++#ifdef CONFIG_LIRC_SERIAL_TRANSMITTER
++module_param(txsense, bool, S_IRUGO);
++MODULE_PARM_DESC(txsense, "Sense of transmitter circuit"
++ " (0 = active high, 1 = active low )");
++#endif
++
++module_param(softcarrier, bool, S_IRUGO);
++MODULE_PARM_DESC(softcarrier, "Software carrier (0 = off, 1 = on, default on)");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
+diff --git a/drivers/input/lirc/lirc_sir.c b/drivers/input/lirc/lirc_sir.c
+new file mode 100644
+index 0000000..4a471d6
+--- /dev/null
++++ b/drivers/input/lirc/lirc_sir.c
+@@ -0,0 +1,1283 @@
++/*
++ * LIRC SIR driver, (C) 2000 Milan Pikula <www@fornax.sk>
++ *
++ * lirc_sir - Device driver for use with SIR (serial infra red)
++ * mode of IrDA on many notebooks.
++ *
++ * 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
++ *
++ *
++ * 2000/09/16 Frank Przybylski <mail@frankprzybylski.de> :
++ * added timeout and relaxed pulse detection, removed gap bug
++ *
++ * 2000/12/15 Christoph Bartelmus <lirc@bartelmus.de> :
++ * added support for Tekram Irmate 210 (sending does not work yet,
++ * kind of disappointing that nobody was able to implement that
++ * before),
++ * major clean-up
++ *
++ * 2001/02/27 Christoph Bartelmus <lirc@bartelmus.de> :
++ * added support for StrongARM SA1100 embedded microprocessor
++ * parts cut'n'pasted from sa1100_ir.c (C) 2000 Russell King
++ */
++
++#include <linux/module.h>
++#include <linux/sched.h>
++#include <linux/errno.h>
++#include <linux/signal.h>
++#include <linux/fs.h>
++#include <linux/interrupt.h>
++#include <linux/ioport.h>
++#include <linux/kernel.h>
++#include <linux/serial_reg.h>
++#include <linux/time.h>
++#include <linux/string.h>
++#include <linux/types.h>
++#include <linux/wait.h>
++#include <linux/mm.h>
++#include <linux/delay.h>
++#include <linux/poll.h>
++#include <asm/system.h>
++#include <linux/io.h>
++#include <asm/irq.h>
++#include <linux/fcntl.h>
++#ifdef LIRC_ON_SA1100
++#include <asm/hardware.h>
++#ifdef CONFIG_SA1100_COLLIE
++#include <asm/arch/tc35143.h>
++#include <asm/ucb1200.h>
++#endif
++#endif
++
++#include <linux/timer.h>
++
++#include <linux/lirc.h>
++#include "lirc_dev.h"
++
++/* SECTION: Definitions */
++
++/*** Tekram dongle ***/
++#ifdef LIRC_SIR_TEKRAM
++/* stolen from kernel source */
++/* definitions for Tekram dongle */
++#define TEKRAM_115200 0x00
++#define TEKRAM_57600 0x01
++#define TEKRAM_38400 0x02
++#define TEKRAM_19200 0x03
++#define TEKRAM_9600 0x04
++#define TEKRAM_2400 0x08
++
++#define TEKRAM_PW 0x10 /* Pulse select bit */
++
++/* 10bit * 1s/115200bit in milliseconds = 87ms*/
++#define TIME_CONST (10000000ul/115200ul)
++
++#endif
++
++#ifdef LIRC_SIR_ACTISYS_ACT200L
++static void init_act200(void);
++#elif defined(LIRC_SIR_ACTISYS_ACT220L)
++static void init_act220(void);
++#endif
++
++/*** SA1100 ***/
++#ifdef LIRC_ON_SA1100
++struct sa1100_ser2_registers {
++ /* HSSP control register */
++ unsigned char hscr0;
++ /* UART registers */
++ unsigned char utcr0;
++ unsigned char utcr1;
++ unsigned char utcr2;
++ unsigned char utcr3;
++ unsigned char utcr4;
++ unsigned char utdr;
++ unsigned char utsr0;
++ unsigned char utsr1;
++} sr;
++
++static int irq = IRQ_Ser2ICP;
++
++#define LIRC_ON_SA1100_TRANSMITTER_LATENCY 0
++
++/* pulse/space ratio of 50/50 */
++static unsigned long pulse_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);
++/* 1000000/freq-pulse_width */
++static unsigned long space_width = (13-LIRC_ON_SA1100_TRANSMITTER_LATENCY);
++static unsigned int freq = 38000; /* modulation frequency */
++static unsigned int duty_cycle = 50; /* duty cycle of 50% */
++
++#endif
++
++#define RBUF_LEN 1024
++#define WBUF_LEN 1024
++
++#define LIRC_DRIVER_NAME "lirc_sir"
++
++#define PULSE '['
++
++#ifndef LIRC_SIR_TEKRAM
++/* 9bit * 1s/115200bit in milli seconds = 78.125ms*/
++#define TIME_CONST (9000000ul/115200ul)
++#endif
++
++
++/* timeout for sequences in jiffies (=5/100s), must be longer than TIME_CONST */
++#define SIR_TIMEOUT (HZ*5/100)
++
++#ifndef LIRC_ON_SA1100
++#ifndef LIRC_IRQ
++#define LIRC_IRQ 4
++#endif
++#ifndef LIRC_PORT
++/* for external dongles, default to com1 */
++#if defined(LIRC_SIR_ACTISYS_ACT200L) || \
++ defined(LIRC_SIR_ACTISYS_ACT220L) || \
++ defined(LIRC_SIR_TEKRAM)
++#define LIRC_PORT 0x3f8
++#else
++/* onboard sir ports are typically com3 */
++#define LIRC_PORT 0x3e8
++#endif
++#endif
++
++static int io = LIRC_PORT;
++static int irq = LIRC_IRQ;
++static int threshold = 3;
++#endif
++
++static DEFINE_SPINLOCK(timer_lock);
++static struct timer_list timerlist;
++/* time of last signal change detected */
++static struct timeval last_tv = {0, 0};
++/* time of last UART data ready interrupt */
++static struct timeval last_intr_tv = {0, 0};
++static int last_value;
++
++static DECLARE_WAIT_QUEUE_HEAD(lirc_read_queue);
++
++static DEFINE_SPINLOCK(hardware_lock);
++
++static int rx_buf[RBUF_LEN];
++static unsigned int rx_tail, rx_head;
++static int tx_buf[WBUF_LEN];
++
++static int debug;
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG LIRC_DRIVER_NAME ": " \
++ fmt, ## args); \
++ } while (0)
++
++/* SECTION: Prototypes */
++
++/* Communication with user-space */
++static unsigned int lirc_poll(struct file *file, poll_table *wait);
++static ssize_t lirc_read(struct file *file, char *buf, size_t count,
++ loff_t *ppos);
++static ssize_t lirc_write(struct file *file, const char *buf, size_t n,
++ loff_t *pos);
++static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd,
++ unsigned long arg);
++static void add_read_queue(int flag, unsigned long val);
++static int init_chrdev(void);
++static void drop_chrdev(void);
++/* Hardware */
++static irqreturn_t sir_interrupt(int irq, void *dev_id);
++static void send_space(unsigned long len);
++static void send_pulse(unsigned long len);
++static int init_hardware(void);
++static void drop_hardware(void);
++/* Initialisation */
++static int init_port(void);
++static void drop_port(void);
++
++#ifdef LIRC_ON_SA1100
++static void on(void)
++{
++ PPSR |= PPC_TXD2;
++}
++
++static void off(void)
++{
++ PPSR &= ~PPC_TXD2;
++}
++#else
++static inline unsigned int sinp(int offset)
++{
++ return inb(io + offset);
++}
++
++static inline void soutp(int offset, int value)
++{
++ outb(value, io + offset);
++}
++#endif
++
++#ifndef MAX_UDELAY_MS
++#define MAX_UDELAY_US 5000
++#else
++#define MAX_UDELAY_US (MAX_UDELAY_MS*1000)
++#endif
++
++static void safe_udelay(unsigned long usecs)
++{
++ while (usecs > MAX_UDELAY_US) {
++ udelay(MAX_UDELAY_US);
++ usecs -= MAX_UDELAY_US;
++ }
++ udelay(usecs);
++}
++
++/* SECTION: Communication with user-space */
++
++static unsigned int lirc_poll(struct file *file, poll_table *wait)
++{
++ poll_wait(file, &lirc_read_queue, wait);
++ if (rx_head != rx_tail)
++ return POLLIN | POLLRDNORM;
++ return 0;
++}
++
++static ssize_t lirc_read(struct file *file, char *buf, size_t count,
++ loff_t *ppos)
++{
++ int n = 0;
++ int retval = 0;
++ DECLARE_WAITQUEUE(wait, current);
++
++ if (count % sizeof(int))
++ return -EINVAL;
++
++ add_wait_queue(&lirc_read_queue, &wait);
++ set_current_state(TASK_INTERRUPTIBLE);
++ while (n < count) {
++ if (rx_head != rx_tail) {
++ if (copy_to_user((void *) buf + n,
++ (void *) (rx_buf + rx_head),
++ sizeof(int))) {
++ retval = -EFAULT;
++ break;
++ }
++ rx_head = (rx_head + 1) & (RBUF_LEN - 1);
++ n += sizeof(int);
++ } else {
++ if (file->f_flags & O_NONBLOCK) {
++ retval = -EAGAIN;
++ break;
++ }
++ if (signal_pending(current)) {
++ retval = -ERESTARTSYS;
++ break;
++ }
++ schedule();
++ set_current_state(TASK_INTERRUPTIBLE);
++ }
++ }
++ remove_wait_queue(&lirc_read_queue, &wait);
++ set_current_state(TASK_RUNNING);
++ return n ? n : retval;
++}
++static ssize_t lirc_write(struct file *file, const char *buf, size_t n,
++ loff_t *pos)
++{
++ unsigned long flags;
++ int i;
++
++ if (n % sizeof(int) || (n / sizeof(int)) > WBUF_LEN)
++ return -EINVAL;
++ if (copy_from_user(tx_buf, buf, n))
++ return -EFAULT;
++ i = 0;
++ n /= sizeof(int);
++#ifdef LIRC_ON_SA1100
++ /* disable receiver */
++ Ser2UTCR3 = 0;
++#endif
++ local_irq_save(flags);
++ while (1) {
++ if (i >= n)
++ break;
++ if (tx_buf[i])
++ send_pulse(tx_buf[i]);
++ i++;
++ if (i >= n)
++ break;
++ if (tx_buf[i])
++ send_space(tx_buf[i]);
++ i++;
++ }
++ local_irq_restore(flags);
++#ifdef LIRC_ON_SA1100
++ off();
++ udelay(1000); /* wait 1ms for IR diode to recover */
++ Ser2UTCR3 = 0;
++ /* clear status register to prevent unwanted interrupts */
++ Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
++ /* enable receiver */
++ Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE;
++#endif
++ return n;
++}
++
++static int lirc_ioctl(struct inode *node, struct file *filep, unsigned int cmd,
++ unsigned long arg)
++{
++ int retval = 0;
++ unsigned long value = 0;
++#ifdef LIRC_ON_SA1100
++ unsigned int ivalue;
++
++ if (cmd == LIRC_GET_FEATURES)
++ value = LIRC_CAN_SEND_PULSE |
++ LIRC_CAN_SET_SEND_DUTY_CYCLE |
++ LIRC_CAN_SET_SEND_CARRIER |
++ LIRC_CAN_REC_MODE2;
++ else if (cmd == LIRC_GET_SEND_MODE)
++ value = LIRC_MODE_PULSE;
++ else if (cmd == LIRC_GET_REC_MODE)
++ value = LIRC_MODE_MODE2;
++#else
++ if (cmd == LIRC_GET_FEATURES)
++ value = LIRC_CAN_SEND_PULSE | LIRC_CAN_REC_MODE2;
++ else if (cmd == LIRC_GET_SEND_MODE)
++ value = LIRC_MODE_PULSE;
++ else if (cmd == LIRC_GET_REC_MODE)
++ value = LIRC_MODE_MODE2;
++#endif
++
++ switch (cmd) {
++ case LIRC_GET_FEATURES:
++ case LIRC_GET_SEND_MODE:
++ case LIRC_GET_REC_MODE:
++ retval = put_user(value, (unsigned long *) arg);
++ break;
++
++ case LIRC_SET_SEND_MODE:
++ case LIRC_SET_REC_MODE:
++ retval = get_user(value, (unsigned long *) arg);
++ break;
++#ifdef LIRC_ON_SA1100
++ case LIRC_SET_SEND_DUTY_CYCLE:
++ retval = get_user(ivalue, (unsigned int *) arg);
++ if (retval)
++ return retval;
++ if (ivalue <= 0 || ivalue > 100)
++ return -EINVAL;
++ /* (ivalue/100)*(1000000/freq) */
++ duty_cycle = ivalue;
++ pulse_width = (unsigned long) duty_cycle*10000/freq;
++ space_width = (unsigned long) 1000000L/freq-pulse_width;
++ if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
++ pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
++ if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
++ space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
++ break;
++ case LIRC_SET_SEND_CARRIER:
++ retval = get_user(ivalue, (unsigned int *) arg);
++ if (retval)
++ return retval;
++ if (ivalue > 500000 || ivalue < 20000)
++ return -EINVAL;
++ freq = ivalue;
++ pulse_width = (unsigned long) duty_cycle*10000/freq;
++ space_width = (unsigned long) 1000000L/freq-pulse_width;
++ if (pulse_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
++ pulse_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
++ if (space_width >= LIRC_ON_SA1100_TRANSMITTER_LATENCY)
++ space_width -= LIRC_ON_SA1100_TRANSMITTER_LATENCY;
++ break;
++#endif
++ default:
++ retval = -ENOIOCTLCMD;
++
++ }
++
++ if (retval)
++ return retval;
++ if (cmd == LIRC_SET_REC_MODE) {
++ if (value != LIRC_MODE_MODE2)
++ retval = -ENOSYS;
++ } else if (cmd == LIRC_SET_SEND_MODE) {
++ if (value != LIRC_MODE_PULSE)
++ retval = -ENOSYS;
++ }
++
++ return retval;
++}
++
++static void add_read_queue(int flag, unsigned long val)
++{
++ unsigned int new_rx_tail;
++ int newval;
++
++ dprintk("add flag %d with val %lu\n", flag, val);
++
++ newval = val & PULSE_MASK;
++
++ /*
++ * statistically, pulses are ~TIME_CONST/2 too long. we could
++ * maybe make this more exact, but this is good enough
++ */
++ if (flag) {
++ /* pulse */
++ if (newval > TIME_CONST/2)
++ newval -= TIME_CONST/2;
++ else /* should not ever happen */
++ newval = 1;
++ newval |= PULSE_BIT;
++ } else {
++ newval += TIME_CONST/2;
++ }
++ new_rx_tail = (rx_tail + 1) & (RBUF_LEN - 1);
++ if (new_rx_tail == rx_head) {
++ dprintk("Buffer overrun.\n");
++ return;
++ }
++ rx_buf[rx_tail] = newval;
++ rx_tail = new_rx_tail;
++ wake_up_interruptible(&lirc_read_queue);
++}
++
++static struct file_operations lirc_fops = {
++ .owner = THIS_MODULE,
++ .read = lirc_read,
++ .write = lirc_write,
++ .poll = lirc_poll,
++ .ioctl = lirc_ioctl,
++ .open = lirc_dev_fop_open,
++ .release = lirc_dev_fop_close,
++};
++
++static int set_use_inc(void *data)
++{
++ return 0;
++}
++
++static void set_use_dec(void *data)
++{
++}
++
++static struct lirc_driver driver = {
++ .name = LIRC_DRIVER_NAME,
++ .minor = -1,
++ .code_length = 1,
++ .sample_rate = 0,
++ .data = NULL,
++ .add_to_buf = NULL,
++ .set_use_inc = set_use_inc,
++ .set_use_dec = set_use_dec,
++ .fops = &lirc_fops,
++ .dev = NULL,
++ .owner = THIS_MODULE,
++};
++
++
++static int init_chrdev(void)
++{
++ driver.minor = lirc_register_driver(&driver);
++ if (driver.minor < 0) {
++ printk(KERN_ERR LIRC_DRIVER_NAME ": init_chrdev() failed.\n");
++ return -EIO;
++ }
++ return 0;
++}
++
++static void drop_chrdev(void)
++{
++ lirc_unregister_driver(driver.minor);
++}
++
++/* SECTION: Hardware */
++static long delta(struct timeval *tv1, struct timeval *tv2)
++{
++ unsigned long deltv;
++
++ deltv = tv2->tv_sec - tv1->tv_sec;
++ if (deltv > 15)
++ deltv = 0xFFFFFF;
++ else
++ deltv = deltv*1000000 +
++ tv2->tv_usec -
++ tv1->tv_usec;
++ return deltv;
++}
++
++static void sir_timeout(unsigned long data)
++{
++ /*
++ * if last received signal was a pulse, but receiving stopped
++ * within the 9 bit frame, we need to finish this pulse and
++ * simulate a signal change to from pulse to space. Otherwise
++ * upper layers will receive two sequences next time.
++ */
++
++ unsigned long flags;
++ unsigned long pulse_end;
++
++ /* avoid interference with interrupt */
++ spin_lock_irqsave(&timer_lock, flags);
++ if (last_value) {
++#ifndef LIRC_ON_SA1100
++ /* clear unread bits in UART and restart */
++ outb(UART_FCR_CLEAR_RCVR, io + UART_FCR);
++#endif
++ /* determine 'virtual' pulse end: */
++ pulse_end = delta(&last_tv, &last_intr_tv);
++ dprintk("timeout add %d for %lu usec\n", last_value, pulse_end);
++ add_read_queue(last_value, pulse_end);
++ last_value = 0;
++ last_tv = last_intr_tv;
++ }
++ spin_unlock_irqrestore(&timer_lock, flags);
++}
++
++static irqreturn_t sir_interrupt(int irq, void *dev_id)
++{
++ unsigned char data;
++ struct timeval curr_tv;
++ static unsigned long deltv;
++#ifdef LIRC_ON_SA1100
++ int status;
++ static int n;
++
++ status = Ser2UTSR0;
++ /*
++ * Deal with any receive errors first. The bytes in error may be
++ * the only bytes in the receive FIFO, so we do this first.
++ */
++ while (status & UTSR0_EIF) {
++ int bstat;
++
++ if (debug) {
++ dprintk("EIF\n");
++ bstat = Ser2UTSR1;
++
++ if (bstat & UTSR1_FRE)
++ dprintk("frame error\n");
++ if (bstat & UTSR1_ROR)
++ dprintk("receive fifo overrun\n");
++ if (bstat & UTSR1_PRE)
++ dprintk("parity error\n");
++ }
++
++ bstat = Ser2UTDR;
++ n++;
++ status = Ser2UTSR0;
++ }
++
++ if (status & (UTSR0_RFS | UTSR0_RID)) {
++ do_gettimeofday(&curr_tv);
++ deltv = delta(&last_tv, &curr_tv);
++ do {
++ data = Ser2UTDR;
++ dprintk("%d data: %u\n", n, (unsigned int) data);
++ n++;
++ } while (status & UTSR0_RID && /* do not empty fifo in order to
++ * get UTSR0_RID in any case */
++ Ser2UTSR1 & UTSR1_RNE); /* data ready */
++
++ if (status&UTSR0_RID) {
++ add_read_queue(0 , deltv - n * TIME_CONST); /*space*/
++ add_read_queue(1, n * TIME_CONST); /*pulse*/
++ n = 0;
++ last_tv = curr_tv;
++ }
++ }
++
++ if (status & UTSR0_TFS)
++ printk(KERN_ERR "transmit fifo not full, shouldn't happen\n");
++
++ /* We must clear certain bits. */
++ status &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
++ if (status)
++ Ser2UTSR0 = status;
++#else
++ unsigned long deltintrtv;
++ unsigned long flags;
++ int iir, lsr;
++
++ while ((iir = inb(io + UART_IIR) & UART_IIR_ID)) {
++ switch (iir&UART_IIR_ID) { /* FIXME toto treba preriedit */
++ case UART_IIR_MSI:
++ (void) inb(io + UART_MSR);
++ break;
++ case UART_IIR_RLSI:
++ (void) inb(io + UART_LSR);
++ break;
++ case UART_IIR_THRI:
++#if 0
++ if (lsr & UART_LSR_THRE) /* FIFO is empty */
++ outb(data, io + UART_TX)
++#endif
++ break;
++ case UART_IIR_RDI:
++ /* avoid interference with timer */
++ spin_lock_irqsave(&timer_lock, flags);
++ do {
++ del_timer(&timerlist);
++ data = inb(io + UART_RX);
++ do_gettimeofday(&curr_tv);
++ deltv = delta(&last_tv, &curr_tv);
++ deltintrtv = delta(&last_intr_tv, &curr_tv);
++ dprintk("t %lu, d %d\n", deltintrtv, (int)data);
++ /*
++ * if nothing came in last X cycles,
++ * it was gap
++ */
++ if (deltintrtv > TIME_CONST * threshold) {
++ if (last_value) {
++ dprintk("GAP\n");
++ /* simulate signal change */
++ add_read_queue(last_value,
++ deltv -
++ deltintrtv);
++ last_value = 0;
++ last_tv.tv_sec =
++ last_intr_tv.tv_sec;
++ last_tv.tv_usec =
++ last_intr_tv.tv_usec;
++ deltv = deltintrtv;
++ }
++ }
++ data = 1;
++ if (data ^ last_value) {
++ /*
++ * deltintrtv > 2*TIME_CONST, remember?
++ * the other case is timeout
++ */
++ add_read_queue(last_value,
++ deltv-TIME_CONST);
++ last_value = data;
++ last_tv = curr_tv;
++ if (last_tv.tv_usec >= TIME_CONST) {
++ last_tv.tv_usec -= TIME_CONST;
++ } else {
++ last_tv.tv_sec--;
++ last_tv.tv_usec += 1000000 -
++ TIME_CONST;
++ }
++ }
++ last_intr_tv = curr_tv;
++ if (data) {
++ /*
++ * start timer for end of
++ * sequence detection
++ */
++ timerlist.expires = jiffies +
++ SIR_TIMEOUT;
++ add_timer(&timerlist);
++ }
++
++ lsr = inb(io + UART_LSR);
++ } while (lsr & UART_LSR_DR); /* data ready */
++ spin_unlock_irqrestore(&timer_lock, flags);
++ break;
++ default:
++ break;
++ }
++ }
++#endif
++ return IRQ_RETVAL(IRQ_HANDLED);
++}
++
++#ifdef LIRC_ON_SA1100
++static void send_pulse(unsigned long length)
++{
++ unsigned long k, delay;
++ int flag;
++
++ if (length == 0)
++ return;
++ /*
++ * this won't give us the carrier frequency we really want
++ * due to integer arithmetic, but we can accept this inaccuracy
++ */
++
++ for (k = flag = 0; k < length; k += delay, flag = !flag) {
++ if (flag) {
++ off();
++ delay = space_width;
++ } else {
++ on();
++ delay = pulse_width;
++ }
++ safe_udelay(delay);
++ }
++ off();
++}
++
++static void send_space(unsigned long length)
++{
++ if (length == 0)
++ return;
++ off();
++ safe_udelay(length);
++}
++#else
++static void send_space(unsigned long len)
++{
++ safe_udelay(len);
++}
++
++static void send_pulse(unsigned long len)
++{
++ long bytes_out = len / TIME_CONST;
++ long time_left;
++
++ time_left = (long)len - (long)bytes_out * (long)TIME_CONST;
++ if (bytes_out == 0) {
++ bytes_out++;
++ time_left = 0;
++ }
++ while (bytes_out--) {
++ outb(PULSE, io + UART_TX);
++ /* FIXME treba seriozne cakanie z char/serial.c */
++ while (!(inb(io + UART_LSR) & UART_LSR_THRE))
++ ;
++ }
++#if 0
++ if (time_left > 0)
++ safe_udelay(time_left);
++#endif
++}
++#endif
++
++#ifdef CONFIG_SA1100_COLLIE
++static int sa1100_irda_set_power_collie(int state)
++{
++ if (state) {
++ /*
++ * 0 - off
++ * 1 - short range, lowest power
++ * 2 - medium range, medium power
++ * 3 - maximum range, high power
++ */
++ ucb1200_set_io_direction(TC35143_GPIO_IR_ON,
++ TC35143_IODIR_OUTPUT);
++ ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_LOW);
++ udelay(100);
++ } else {
++ /* OFF */
++ ucb1200_set_io_direction(TC35143_GPIO_IR_ON,
++ TC35143_IODIR_OUTPUT);
++ ucb1200_set_io(TC35143_GPIO_IR_ON, TC35143_IODAT_HIGH);
++ }
++ return 0;
++}
++#endif
++
++static int init_hardware(void)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&hardware_lock, flags);
++ /* reset UART */
++#ifdef LIRC_ON_SA1100
++#ifdef CONFIG_SA1100_BITSY
++ if (machine_is_bitsy()) {
++ printk(KERN_INFO "Power on IR module\n");
++ set_bitsy_egpio(EGPIO_BITSY_IR_ON);
++ }
++#endif
++#ifdef CONFIG_SA1100_COLLIE
++ sa1100_irda_set_power_collie(3); /* power on */
++#endif
++ sr.hscr0 = Ser2HSCR0;
++
++ sr.utcr0 = Ser2UTCR0;
++ sr.utcr1 = Ser2UTCR1;
++ sr.utcr2 = Ser2UTCR2;
++ sr.utcr3 = Ser2UTCR3;
++ sr.utcr4 = Ser2UTCR4;
++
++ sr.utdr = Ser2UTDR;
++ sr.utsr0 = Ser2UTSR0;
++ sr.utsr1 = Ser2UTSR1;
++
++ /* configure GPIO */
++ /* output */
++ PPDR |= PPC_TXD2;
++ PSDR |= PPC_TXD2;
++ /* set output to 0 */
++ off();
++
++ /* Enable HP-SIR modulation, and ensure that the port is disabled. */
++ Ser2UTCR3 = 0;
++ Ser2HSCR0 = sr.hscr0 & (~HSCR0_HSSP);
++
++ /* clear status register to prevent unwanted interrupts */
++ Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
++
++ /* 7N1 */
++ Ser2UTCR0 = UTCR0_1StpBit|UTCR0_7BitData;
++ /* 115200 */
++ Ser2UTCR1 = 0;
++ Ser2UTCR2 = 1;
++ /* use HPSIR, 1.6 usec pulses */
++ Ser2UTCR4 = UTCR4_HPSIR|UTCR4_Z1_6us;
++
++ /* enable receiver, receive fifo interrupt */
++ Ser2UTCR3 = UTCR3_RXE|UTCR3_RIE;
++
++ /* clear status register to prevent unwanted interrupts */
++ Ser2UTSR0 &= (UTSR0_RID | UTSR0_RBB | UTSR0_REB);
++
++#elif defined(LIRC_SIR_TEKRAM)
++ /* disable FIFO */
++ soutp(UART_FCR,
++ UART_FCR_CLEAR_RCVR|
++ UART_FCR_CLEAR_XMIT|
++ UART_FCR_TRIGGER_1);
++
++ /* Set DLAB 0. */
++ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
++
++ /* First of all, disable all interrupts */
++ soutp(UART_IER, sinp(UART_IER) &
++ (~(UART_IER_MSI|UART_IER_RLSI|UART_IER_THRI|UART_IER_RDI)));
++
++ /* Set DLAB 1. */
++ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
++
++ /* Set divisor to 12 => 9600 Baud */
++ soutp(UART_DLM, 0);
++ soutp(UART_DLL, 12);
++
++ /* Set DLAB 0. */
++ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
++
++ /* power supply */
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
++ safe_udelay(50*1000);
++
++ /* -DTR low -> reset PIC */
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
++ udelay(1*1000);
++
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
++ udelay(100);
++
++
++ /* -RTS low -> send control byte */
++ soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
++ udelay(7);
++ soutp(UART_TX, TEKRAM_115200|TEKRAM_PW);
++
++ /* one byte takes ~1042 usec to transmit at 9600,8N1 */
++ udelay(1500);
++
++ /* back to normal operation */
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
++ udelay(50);
++
++ udelay(1500);
++
++ /* read previous control byte */
++ printk(KERN_INFO LIRC_DRIVER_NAME
++ ": 0x%02x\n", sinp(UART_RX));
++
++ /* Set DLAB 1. */
++ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
++
++ /* Set divisor to 1 => 115200 Baud */
++ soutp(UART_DLM, 0);
++ soutp(UART_DLL, 1);
++
++ /* Set DLAB 0, 8 Bit */
++ soutp(UART_LCR, UART_LCR_WLEN8);
++ /* enable interrupts */
++ soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
++#else
++ outb(0, io + UART_MCR);
++ outb(0, io + UART_IER);
++ /* init UART */
++ /* set DLAB, speed = 115200 */
++ outb(UART_LCR_DLAB | UART_LCR_WLEN7, io + UART_LCR);
++ outb(1, io + UART_DLL); outb(0, io + UART_DLM);
++ /* 7N1+start = 9 bits at 115200 ~ 3 bits at 44000 */
++ outb(UART_LCR_WLEN7, io + UART_LCR);
++ /* FIFO operation */
++ outb(UART_FCR_ENABLE_FIFO, io + UART_FCR);
++ /* interrupts */
++ /* outb(UART_IER_RLSI|UART_IER_RDI|UART_IER_THRI, io + UART_IER); */
++ outb(UART_IER_RDI, io + UART_IER);
++ /* turn on UART */
++ outb(UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2, io + UART_MCR);
++#ifdef LIRC_SIR_ACTISYS_ACT200L
++ init_act200();
++#elif defined(LIRC_SIR_ACTISYS_ACT220L)
++ init_act220();
++#endif
++#endif
++ spin_unlock_irqrestore(&hardware_lock, flags);
++ return 0;
++}
++
++static void drop_hardware(void)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&hardware_lock, flags);
++
++#ifdef LIRC_ON_SA1100
++ Ser2UTCR3 = 0;
++
++ Ser2UTCR0 = sr.utcr0;
++ Ser2UTCR1 = sr.utcr1;
++ Ser2UTCR2 = sr.utcr2;
++ Ser2UTCR4 = sr.utcr4;
++ Ser2UTCR3 = sr.utcr3;
++
++ Ser2HSCR0 = sr.hscr0;
++#ifdef CONFIG_SA1100_BITSY
++ if (machine_is_bitsy())
++ clr_bitsy_egpio(EGPIO_BITSY_IR_ON);
++#endif
++#ifdef CONFIG_SA1100_COLLIE
++ sa1100_irda_set_power_collie(0); /* power off */
++#endif
++#else
++ /* turn off interrupts */
++ outb(0, io + UART_IER);
++#endif
++ spin_unlock_irqrestore(&hardware_lock, flags);
++}
++
++/* SECTION: Initialisation */
++
++static int init_port(void)
++{
++ int retval;
++
++ /* get I/O port access and IRQ line */
++#ifndef LIRC_ON_SA1100
++ if (request_region(io, 8, LIRC_DRIVER_NAME) == NULL) {
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": i/o port 0x%.4x already in use.\n", io);
++ return -EBUSY;
++ }
++#endif
++ retval = request_irq(irq, sir_interrupt, IRQF_DISABLED,
++ LIRC_DRIVER_NAME, NULL);
++ if (retval < 0) {
++# ifndef LIRC_ON_SA1100
++ release_region(io, 8);
++# endif
++ printk(KERN_ERR LIRC_DRIVER_NAME
++ ": IRQ %d already in use.\n",
++ irq);
++ return retval;
++ }
++#ifndef LIRC_ON_SA1100
++ printk(KERN_INFO LIRC_DRIVER_NAME
++ ": I/O port 0x%.4x, IRQ %d.\n",
++ io, irq);
++#endif
++
++ init_timer(&timerlist);
++ timerlist.function = sir_timeout;
++ timerlist.data = 0xabadcafe;
++
++ return 0;
++}
++
++static void drop_port(void)
++{
++ free_irq(irq, NULL);
++ del_timer_sync(&timerlist);
++#ifndef LIRC_ON_SA1100
++ release_region(io, 8);
++#endif
++}
++
++#ifdef LIRC_SIR_ACTISYS_ACT200L
++/* Crystal/Cirrus CS8130 IR transceiver, used in Actisys Act200L dongle */
++/* some code borrowed from Linux IRDA driver */
++
++/* Register 0: Control register #1 */
++#define ACT200L_REG0 0x00
++#define ACT200L_TXEN 0x01 /* Enable transmitter */
++#define ACT200L_RXEN 0x02 /* Enable receiver */
++#define ACT200L_ECHO 0x08 /* Echo control chars */
++
++/* Register 1: Control register #2 */
++#define ACT200L_REG1 0x10
++#define ACT200L_LODB 0x01 /* Load new baud rate count value */
++#define ACT200L_WIDE 0x04 /* Expand the maximum allowable pulse */
++
++/* Register 3: Transmit mode register #2 */
++#define ACT200L_REG3 0x30
++#define ACT200L_B0 0x01 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P) */
++#define ACT200L_B1 0x02 /* DataBits, 0=6, 1=7, 2=8, 3=9(8P) */
++#define ACT200L_CHSY 0x04 /* StartBit Synced 0=bittime, 1=startbit */
++
++/* Register 4: Output Power register */
++#define ACT200L_REG4 0x40
++#define ACT200L_OP0 0x01 /* Enable LED1C output */
++#define ACT200L_OP1 0x02 /* Enable LED2C output */
++#define ACT200L_BLKR 0x04
++
++/* Register 5: Receive Mode register */
++#define ACT200L_REG5 0x50
++#define ACT200L_RWIDL 0x01 /* fixed 1.6us pulse mode */
++ /*.. other various IRDA bit modes, and TV remote modes..*/
++
++/* Register 6: Receive Sensitivity register #1 */
++#define ACT200L_REG6 0x60
++#define ACT200L_RS0 0x01 /* receive threshold bit 0 */
++#define ACT200L_RS1 0x02 /* receive threshold bit 1 */
++
++/* Register 7: Receive Sensitivity register #2 */
++#define ACT200L_REG7 0x70
++#define ACT200L_ENPOS 0x04 /* Ignore the falling edge */
++
++/* Register 8,9: Baud Rate Divider register #1,#2 */
++#define ACT200L_REG8 0x80
++#define ACT200L_REG9 0x90
++
++#define ACT200L_2400 0x5f
++#define ACT200L_9600 0x17
++#define ACT200L_19200 0x0b
++#define ACT200L_38400 0x05
++#define ACT200L_57600 0x03
++#define ACT200L_115200 0x01
++
++/* Register 13: Control register #3 */
++#define ACT200L_REG13 0xd0
++#define ACT200L_SHDW 0x01 /* Enable access to shadow registers */
++
++/* Register 15: Status register */
++#define ACT200L_REG15 0xf0
++
++/* Register 21: Control register #4 */
++#define ACT200L_REG21 0x50
++#define ACT200L_EXCK 0x02 /* Disable clock output driver */
++#define ACT200L_OSCL 0x04 /* oscillator in low power, medium accuracy mode */
++
++static void init_act200(void)
++{
++ int i;
++ __u8 control[] = {
++ ACT200L_REG15,
++ ACT200L_REG13 | ACT200L_SHDW,
++ ACT200L_REG21 | ACT200L_EXCK | ACT200L_OSCL,
++ ACT200L_REG13,
++ ACT200L_REG7 | ACT200L_ENPOS,
++ ACT200L_REG6 | ACT200L_RS0 | ACT200L_RS1,
++ ACT200L_REG5 | ACT200L_RWIDL,
++ ACT200L_REG4 | ACT200L_OP0 | ACT200L_OP1 | ACT200L_BLKR,
++ ACT200L_REG3 | ACT200L_B0,
++ ACT200L_REG0 | ACT200L_TXEN | ACT200L_RXEN,
++ ACT200L_REG8 | (ACT200L_115200 & 0x0f),
++ ACT200L_REG9 | ((ACT200L_115200 >> 4) & 0x0f),
++ ACT200L_REG1 | ACT200L_LODB | ACT200L_WIDE
++ };
++
++ /* Set DLAB 1. */
++ soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN8);
++
++ /* Set divisor to 12 => 9600 Baud */
++ soutp(UART_DLM, 0);
++ soutp(UART_DLL, 12);
++
++ /* Set DLAB 0. */
++ soutp(UART_LCR, UART_LCR_WLEN8);
++ /* Set divisor to 12 => 9600 Baud */
++
++ /* power supply */
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
++ for (i = 0; i < 50; i++)
++ safe_udelay(1000);
++
++ /* Reset the dongle : set RTS low for 25 ms */
++ soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
++ for (i = 0; i < 25; i++)
++ udelay(1000);
++
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
++ udelay(100);
++
++ /* Clear DTR and set RTS to enter command mode */
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
++ udelay(7);
++
++ /* send out the control register settings for 115K 7N1 SIR operation */
++ for (i = 0; i < sizeof(control); i++) {
++ soutp(UART_TX, control[i]);
++ /* one byte takes ~1042 usec to transmit at 9600,8N1 */
++ udelay(1500);
++ }
++
++ /* back to normal operation */
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
++ udelay(50);
++
++ udelay(1500);
++ soutp(UART_LCR, sinp(UART_LCR) | UART_LCR_DLAB);
++
++ /* Set DLAB 1. */
++ soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
++
++ /* Set divisor to 1 => 115200 Baud */
++ soutp(UART_DLM, 0);
++ soutp(UART_DLL, 1);
++
++ /* Set DLAB 0. */
++ soutp(UART_LCR, sinp(UART_LCR) & (~UART_LCR_DLAB));
++
++ /* Set DLAB 0, 7 Bit */
++ soutp(UART_LCR, UART_LCR_WLEN7);
++
++ /* enable interrupts */
++ soutp(UART_IER, sinp(UART_IER)|UART_IER_RDI);
++}
++#endif
++
++#ifdef LIRC_SIR_ACTISYS_ACT220L
++/*
++ * Derived from linux IrDA driver (net/irda/actisys.c)
++ * Drop me a mail for any kind of comment: maxx@spaceboyz.net
++ */
++
++void init_act220(void)
++{
++ int i;
++
++ /* DLAB 1 */
++ soutp(UART_LCR, UART_LCR_DLAB|UART_LCR_WLEN7);
++
++ /* 9600 baud */
++ soutp(UART_DLM, 0);
++ soutp(UART_DLL, 12);
++
++ /* DLAB 0 */
++ soutp(UART_LCR, UART_LCR_WLEN7);
++
++ /* reset the dongle, set DTR low for 10us */
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_OUT2);
++ udelay(10);
++
++ /* back to normal (still 9600) */
++ soutp(UART_MCR, UART_MCR_DTR|UART_MCR_RTS|UART_MCR_OUT2);
++
++ /*
++ * send RTS pulses until we reach 115200
++ * i hope this is really the same for act220l/act220l+
++ */
++ for (i = 0; i < 3; i++) {
++ udelay(10);
++ /* set RTS low for 10 us */
++ soutp(UART_MCR, UART_MCR_DTR|UART_MCR_OUT2);
++ udelay(10);
++ /* set RTS high for 10 us */
++ soutp(UART_MCR, UART_MCR_RTS|UART_MCR_DTR|UART_MCR_OUT2);
++ }
++
++ /* back to normal operation */
++ udelay(1500); /* better safe than sorry ;) */
++
++ /* Set DLAB 1. */
++ soutp(UART_LCR, UART_LCR_DLAB | UART_LCR_WLEN7);
++
++ /* Set divisor to 1 => 115200 Baud */
++ soutp(UART_DLM, 0);
++ soutp(UART_DLL, 1);
++
++ /* Set DLAB 0, 7 Bit */
++ /* The dongle doesn't seem to have any problems with operation at 7N1 */
++ soutp(UART_LCR, UART_LCR_WLEN7);
++
++ /* enable interrupts */
++ soutp(UART_IER, UART_IER_RDI);
++}
++#endif
++
++static int init_lirc_sir(void)
++{
++ int retval;
++
++ init_waitqueue_head(&lirc_read_queue);
++ retval = init_port();
++ if (retval < 0)
++ return retval;
++ init_hardware();
++ printk(KERN_INFO LIRC_DRIVER_NAME
++ ": Installed.\n");
++ return 0;
++}
++
++
++static int __init lirc_sir_init(void)
++{
++ int retval;
++
++ retval = init_chrdev();
++ if (retval < 0)
++ return retval;
++ retval = init_lirc_sir();
++ if (retval) {
++ drop_chrdev();
++ return retval;
++ }
++ return 0;
++}
++
++static void __exit lirc_sir_exit(void)
++{
++ drop_hardware();
++ drop_chrdev();
++ drop_port();
++ printk(KERN_INFO LIRC_DRIVER_NAME ": Uninstalled.\n");
++}
++
++module_init(lirc_sir_init);
++module_exit(lirc_sir_exit);
++
++#ifdef LIRC_SIR_TEKRAM
++MODULE_DESCRIPTION("Infrared receiver driver for Tekram Irmate 210");
++MODULE_AUTHOR("Christoph Bartelmus");
++#elif defined(LIRC_ON_SA1100)
++MODULE_DESCRIPTION("LIRC driver for StrongARM SA1100 embedded microprocessor");
++MODULE_AUTHOR("Christoph Bartelmus");
++#elif defined(LIRC_SIR_ACTISYS_ACT200L)
++MODULE_DESCRIPTION("LIRC driver for Actisys Act200L");
++MODULE_AUTHOR("Karl Bongers");
++#elif defined(LIRC_SIR_ACTISYS_ACT220L)
++MODULE_DESCRIPTION("LIRC driver for Actisys Act220L(+)");
++MODULE_AUTHOR("Jan Roemisch");
++#else
++MODULE_DESCRIPTION("Infrared receiver driver for SIR type serial ports");
++MODULE_AUTHOR("Milan Pikula");
++#endif
++MODULE_LICENSE("GPL");
++
++#ifdef LIRC_ON_SA1100
++module_param(irq, int, S_IRUGO);
++MODULE_PARM_DESC(irq, "Interrupt (16)");
++#else
++module_param(io, int, S_IRUGO);
++MODULE_PARM_DESC(io, "I/O address base (0x3f8 or 0x2f8)");
++
++module_param(irq, int, S_IRUGO);
++MODULE_PARM_DESC(irq, "Interrupt (4 or 3)");
++
++module_param(threshold, int, S_IRUGO);
++MODULE_PARM_DESC(threshold, "space detection threshold (3)");
++#endif
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
+diff --git a/drivers/input/lirc/lirc_streamzap.c b/drivers/input/lirc/lirc_streamzap.c
+new file mode 100644
+index 0000000..f4374e8
+--- /dev/null
++++ b/drivers/input/lirc/lirc_streamzap.c
+@@ -0,0 +1,794 @@
++/*
++ * Streamzap Remote Control driver
++ *
++ * Copyright (c) 2005 Christoph Bartelmus <lirc@bartelmus.de>
++ *
++ * This driver was based on the work of Greg Wickham and Adrian
++ * Dewhurst. It was substantially rewritten to support correct signal
++ * gaps and now maintains a delay buffer, which is used to present
++ * consistent timing behaviour to user space applications. Without the
++ * delay buffer an ugly hack would be required in lircd, which can
++ * cause sluggish signal decoding in certain situations.
++ *
++ * This driver is based on the USB skeleton driver packaged with the
++ * kernel; copyright (C) 2001-2003 Greg Kroah-Hartman (greg@kroah.com)
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/kernel.h>
++#include <linux/errno.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/module.h>
++#include <linux/smp_lock.h>
++#include <linux/completion.h>
++#include <linux/uaccess.h>
++#include <linux/usb.h>
++
++#include <linux/lirc.h>
++#include "lirc_dev.h"
++
++#define DRIVER_VERSION "1.28"
++#define DRIVER_NAME "lirc_streamzap"
++#define DRIVER_DESC "Streamzap Remote Control driver"
++
++static int debug;
++
++#define USB_STREAMZAP_VENDOR_ID 0x0e9c
++#define USB_STREAMZAP_PRODUCT_ID 0x0000
++
++/* Use our own dbg macro */
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG DRIVER_NAME "[%d]: " \
++ fmt "\n", ## args); \
++ } while (0)
++
++/* table of devices that work with this driver */
++static struct usb_device_id streamzap_table[] = {
++ /* Streamzap Remote Control */
++ { USB_DEVICE(USB_STREAMZAP_VENDOR_ID, USB_STREAMZAP_PRODUCT_ID) },
++ /* Terminating entry */
++ { }
++};
++
++MODULE_DEVICE_TABLE(usb, streamzap_table);
++
++#define STREAMZAP_PULSE_MASK 0xf0
++#define STREAMZAP_SPACE_MASK 0x0f
++#define STREAMZAP_RESOLUTION 256
++
++/* number of samples buffered */
++#define STREAMZAP_BUF_LEN 128
++
++enum StreamzapDecoderState {
++ PulseSpace,
++ FullPulse,
++ FullSpace,
++ IgnorePulse
++};
++
++/* Structure to hold all of our device specific stuff
++ *
++ * some remarks regarding locking:
++ * theoretically this struct can be accessed from three threads:
++ *
++ * - from lirc_dev through set_use_inc/set_use_dec
++ *
++ * - from the USB layer throuh probe/disconnect/irq
++ *
++ * Careful placement of lirc_register_driver/lirc_unregister_driver
++ * calls will prevent conflicts. lirc_dev makes sure that
++ * set_use_inc/set_use_dec are not being executed and will not be
++ * called after lirc_unregister_driver returns.
++ *
++ * - by the timer callback
++ *
++ * The timer is only running when the device is connected and the
++ * LIRC device is open. Making sure the timer is deleted by
++ * set_use_dec will make conflicts impossible.
++ */
++struct usb_streamzap {
++
++ /* usb */
++ /* save off the usb device pointer */
++ struct usb_device *udev;
++ /* the interface for this device */
++ struct usb_interface *interface;
++
++ /* buffer & dma */
++ unsigned char *buf_in;
++ dma_addr_t dma_in;
++ unsigned int buf_in_len;
++
++ struct usb_endpoint_descriptor *endpoint;
++
++ /* IRQ */
++ struct urb *urb_in;
++
++ /* lirc */
++ struct lirc_driver *driver;
++ struct lirc_buffer *delay_buf;
++
++ /* timer used to support delay buffering */
++ struct timer_list delay_timer;
++ int timer_running;
++ spinlock_t timer_lock;
++
++ /* tracks whether we are currently receiving some signal */
++ int idle;
++ /* sum of signal lengths received since signal start */
++ unsigned long sum;
++ /* start time of signal; necessary for gap tracking */
++ struct timeval signal_last;
++ struct timeval signal_start;
++ enum StreamzapDecoderState decoder_state;
++ struct timer_list flush_timer;
++ int flush;
++ int in_use;
++};
++
++
++/* local function prototypes */
++static int streamzap_probe(struct usb_interface *interface,
++ const struct usb_device_id *id);
++static void streamzap_disconnect(struct usb_interface *interface);
++static void usb_streamzap_irq(struct urb *urb);
++static int streamzap_use_inc(void *data);
++static void streamzap_use_dec(void *data);
++static int streamzap_ioctl(struct inode *node, struct file *filep,
++ unsigned int cmd, unsigned long arg);
++static int streamzap_suspend(struct usb_interface *intf, pm_message_t message);
++static int streamzap_resume(struct usb_interface *intf);
++
++/* usb specific object needed to register this driver with the usb subsystem */
++
++static struct usb_driver streamzap_driver = {
++ .name = DRIVER_NAME,
++ .probe = streamzap_probe,
++ .disconnect = streamzap_disconnect,
++ .suspend = streamzap_suspend,
++ .resume = streamzap_resume,
++ .id_table = streamzap_table,
++};
++
++static void stop_timer(struct usb_streamzap *sz)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&sz->timer_lock, flags);
++ if (sz->timer_running) {
++ sz->timer_running = 0;
++ spin_unlock_irqrestore(&sz->timer_lock, flags);
++ del_timer_sync(&sz->delay_timer);
++ } else {
++ spin_unlock_irqrestore(&sz->timer_lock, flags);
++ }
++}
++
++static void flush_timeout(unsigned long arg)
++{
++ struct usb_streamzap *sz = (struct usb_streamzap *) arg;
++
++ /* finally start accepting data */
++ sz->flush = 0;
++}
++static void delay_timeout(unsigned long arg)
++{
++ unsigned long flags;
++ /* deliver data every 10 ms */
++ static unsigned long timer_inc =
++ (10000/(1000000/HZ)) == 0 ? 1 : (10000/(1000000/HZ));
++ struct usb_streamzap *sz = (struct usb_streamzap *) arg;
++ int data;
++
++ spin_lock_irqsave(&sz->timer_lock, flags);
++
++ if (!lirc_buffer_empty(sz->delay_buf) &&
++ !lirc_buffer_full(sz->driver->rbuf)) {
++ lirc_buffer_read(sz->delay_buf, (unsigned char *) &data);
++ lirc_buffer_write(sz->driver->rbuf, (unsigned char *) &data);
++ }
++ if (!lirc_buffer_empty(sz->delay_buf)) {
++ while (lirc_buffer_available(sz->delay_buf) <
++ STREAMZAP_BUF_LEN / 2 &&
++ !lirc_buffer_full(sz->driver->rbuf)) {
++ lirc_buffer_read(sz->delay_buf,
++ (unsigned char *) &data);
++ lirc_buffer_write(sz->driver->rbuf,
++ (unsigned char *) &data);
++ }
++ if (sz->timer_running) {
++ sz->delay_timer.expires = jiffies + timer_inc;
++ add_timer(&sz->delay_timer);
++ }
++ } else {
++ sz->timer_running = 0;
++ }
++
++ if (!lirc_buffer_empty(sz->driver->rbuf))
++ wake_up(&sz->driver->rbuf->wait_poll);
++
++ spin_unlock_irqrestore(&sz->timer_lock, flags);
++}
++
++static void flush_delay_buffer(struct usb_streamzap *sz)
++{
++ int data;
++ int empty = 1;
++
++ while (!lirc_buffer_empty(sz->delay_buf)) {
++ empty = 0;
++ lirc_buffer_read(sz->delay_buf, (unsigned char *) &data);
++ if (!lirc_buffer_full(sz->driver->rbuf)) {
++ lirc_buffer_write(sz->driver->rbuf,
++ (unsigned char *) &data);
++ } else {
++ dprintk("buffer overflow", sz->driver->minor);
++ }
++ }
++ if (!empty)
++ wake_up(&sz->driver->rbuf->wait_poll);
++}
++
++static void push(struct usb_streamzap *sz, unsigned char *data)
++{
++ unsigned long flags;
++
++ spin_lock_irqsave(&sz->timer_lock, flags);
++ if (lirc_buffer_full(sz->delay_buf)) {
++ int read_data;
++
++ lirc_buffer_read(sz->delay_buf,
++ (unsigned char *) &read_data);
++ if (!lirc_buffer_full(sz->driver->rbuf)) {
++ lirc_buffer_write(sz->driver->rbuf,
++ (unsigned char *) &read_data);
++ } else {
++ dprintk("buffer overflow", sz->driver->minor);
++ }
++ }
++
++ lirc_buffer_write(sz->delay_buf, data);
++
++ if (!sz->timer_running) {
++ sz->delay_timer.expires = jiffies + HZ/10;
++ add_timer(&sz->delay_timer);
++ sz->timer_running = 1;
++ }
++
++ spin_unlock_irqrestore(&sz->timer_lock, flags);
++}
++
++static void push_full_pulse(struct usb_streamzap *sz,
++ unsigned char value)
++{
++ int pulse;
++
++ if (sz->idle) {
++ long deltv;
++ int tmp;
++
++ sz->signal_last = sz->signal_start;
++ do_gettimeofday(&sz->signal_start);
++
++ deltv = sz->signal_start.tv_sec-sz->signal_last.tv_sec;
++ if (deltv > 15) {
++ tmp = PULSE_MASK; /* really long time */
++ } else {
++ tmp = (int) (deltv*1000000+
++ sz->signal_start.tv_usec -
++ sz->signal_last.tv_usec);
++ tmp -= sz->sum;
++ }
++ dprintk("ls %u", sz->driver->minor, tmp);
++ push(sz, (char *)&tmp);
++
++ sz->idle = 0;
++ sz->sum = 0;
++ }
++
++ pulse = ((int) value) * STREAMZAP_RESOLUTION;
++ pulse += STREAMZAP_RESOLUTION / 2;
++ sz->sum += pulse;
++ pulse |= PULSE_BIT;
++
++ dprintk("p %u", sz->driver->minor, pulse & PULSE_MASK);
++ push(sz, (char *)&pulse);
++}
++
++static void push_half_pulse(struct usb_streamzap *sz,
++ unsigned char value)
++{
++ push_full_pulse(sz, (value & STREAMZAP_PULSE_MASK)>>4);
++}
++
++static void push_full_space(struct usb_streamzap *sz,
++ unsigned char value)
++{
++ int space;
++
++ space = ((int) value)*STREAMZAP_RESOLUTION;
++ space += STREAMZAP_RESOLUTION/2;
++ sz->sum += space;
++ dprintk("s %u", sz->driver->minor, space);
++ push(sz, (char *)&space);
++}
++
++static void push_half_space(struct usb_streamzap *sz,
++ unsigned char value)
++{
++ push_full_space(sz, value & STREAMZAP_SPACE_MASK);
++}
++
++/**
++ * usb_streamzap_irq - IRQ handler
++ *
++ * This procedure is invoked on reception of data from
++ * the usb remote.
++ */
++static void usb_streamzap_irq(struct urb *urb)
++{
++ struct usb_streamzap *sz;
++ int len;
++ unsigned int i = 0;
++
++ if (!urb)
++ return;
++
++ sz = urb->context;
++ len = urb->actual_length;
++
++ switch (urb->status) {
++ case -ECONNRESET:
++ case -ENOENT:
++ case -ESHUTDOWN:
++ /*
++ * this urb is terminated, clean up.
++ * sz might already be invalid at this point
++ */
++ dprintk("urb status: %d", -1, urb->status);
++ return;
++ default:
++ break;
++ }
++
++ dprintk("received %d", sz->driver->minor, urb->actual_length);
++ if (!sz->flush) {
++ for (i = 0; i < urb->actual_length; i++) {
++ dprintk("%d: %x", sz->driver->minor,
++ i, (unsigned char) sz->buf_in[i]);
++ switch (sz->decoder_state) {
++ case PulseSpace:
++ if ((sz->buf_in[i]&STREAMZAP_PULSE_MASK) ==
++ STREAMZAP_PULSE_MASK) {
++ sz->decoder_state = FullPulse;
++ continue;
++ } else if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK)
++ == STREAMZAP_SPACE_MASK) {
++ push_half_pulse(sz, sz->buf_in[i]);
++ sz->decoder_state = FullSpace;
++ continue;
++ } else {
++ push_half_pulse(sz, sz->buf_in[i]);
++ push_half_space(sz, sz->buf_in[i]);
++ }
++ break;
++ case FullPulse:
++ push_full_pulse(sz, sz->buf_in[i]);
++ sz->decoder_state = IgnorePulse;
++ break;
++ case FullSpace:
++ if (sz->buf_in[i] == 0xff) {
++ sz->idle = 1;
++ stop_timer(sz);
++ flush_delay_buffer(sz);
++ } else
++ push_full_space(sz, sz->buf_in[i]);
++ sz->decoder_state = PulseSpace;
++ break;
++ case IgnorePulse:
++ if ((sz->buf_in[i]&STREAMZAP_SPACE_MASK) ==
++ STREAMZAP_SPACE_MASK) {
++ sz->decoder_state = FullSpace;
++ continue;
++ }
++ push_half_space(sz, sz->buf_in[i]);
++ sz->decoder_state = PulseSpace;
++ break;
++ }
++ }
++ }
++
++ usb_submit_urb(urb, GFP_ATOMIC);
++
++ return;
++}
++
++static struct file_operations streamzap_fops = {
++ .owner = THIS_MODULE,
++ .ioctl = streamzap_ioctl,
++ .read = lirc_dev_fop_read,
++ .write = lirc_dev_fop_write,
++ .poll = lirc_dev_fop_poll,
++ .open = lirc_dev_fop_open,
++ .release = lirc_dev_fop_close,
++};
++
++
++/**
++ * streamzap_probe
++ *
++ * Called by usb-core to associated with a candidate device
++ * On any failure the return value is the ERROR
++ * On success return 0
++ */
++static int streamzap_probe(struct usb_interface *interface,
++ const struct usb_device_id *id)
++{
++ struct usb_device *udev = interface_to_usbdev(interface);
++ struct usb_host_interface *iface_host;
++ struct usb_streamzap *sz;
++ struct lirc_driver *driver;
++ struct lirc_buffer *lirc_buf;
++ struct lirc_buffer *delay_buf;
++ char buf[63], name[128] = "";
++ int retval = -ENOMEM;
++ int minor = 0;
++
++ /* Allocate space for device driver specific data */
++ sz = kzalloc(sizeof(struct usb_streamzap), GFP_KERNEL);
++ if (sz == NULL)
++ return -ENOMEM;
++
++ sz->udev = udev;
++ sz->interface = interface;
++
++ /* Check to ensure endpoint information matches requirements */
++ iface_host = interface->cur_altsetting;
++
++ if (iface_host->desc.bNumEndpoints != 1) {
++ err("%s: Unexpected desc.bNumEndpoints (%d)", __func__,
++ iface_host->desc.bNumEndpoints);
++ retval = -ENODEV;
++ goto free_sz;
++ }
++
++ sz->endpoint = &(iface_host->endpoint[0].desc);
++ if ((sz->endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
++ != USB_DIR_IN) {
++ err("%s: endpoint doesn't match input device 02%02x",
++ __func__, sz->endpoint->bEndpointAddress);
++ retval = -ENODEV;
++ goto free_sz;
++ }
++
++ if ((sz->endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
++ != USB_ENDPOINT_XFER_INT) {
++ err("%s: endpoint attributes don't match xfer 02%02x",
++ __func__, sz->endpoint->bmAttributes);
++ retval = -ENODEV;
++ goto free_sz;
++ }
++
++ if (sz->endpoint->wMaxPacketSize == 0) {
++ err("%s: endpoint message size==0? ", __func__);
++ retval = -ENODEV;
++ goto free_sz;
++ }
++
++ /* Allocate the USB buffer and IRQ URB */
++
++ sz->buf_in_len = sz->endpoint->wMaxPacketSize;
++ sz->buf_in = usb_buffer_alloc(sz->udev, sz->buf_in_len,
++ GFP_ATOMIC, &sz->dma_in);
++ if (sz->buf_in == NULL)
++ goto free_sz;
++
++ sz->urb_in = usb_alloc_urb(0, GFP_KERNEL);
++ if (sz->urb_in == NULL)
++ goto free_sz;
++
++ /* Connect this device to the LIRC sub-system */
++ driver = kzalloc(sizeof(struct lirc_driver), GFP_KERNEL);
++ if (!driver)
++ goto free_sz;
++
++ lirc_buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
++ if (!lirc_buf)
++ goto free_driver;
++ if (lirc_buffer_init(lirc_buf, sizeof(int), STREAMZAP_BUF_LEN))
++ goto kfree_lirc_buf;
++
++ delay_buf = kmalloc(sizeof(struct lirc_buffer), GFP_KERNEL);
++ if (!delay_buf)
++ goto free_lirc_buf;
++ if (lirc_buffer_init(delay_buf, sizeof(int), STREAMZAP_BUF_LEN))
++ goto kfree_delay_buf;
++
++ sz->driver = driver;
++ strcpy(sz->driver->name, DRIVER_NAME);
++ sz->driver->minor = -1;
++ sz->driver->sample_rate = 0;
++ sz->driver->code_length = sizeof(int) * 8;
++ sz->driver->features = LIRC_CAN_REC_MODE2 | LIRC_CAN_GET_REC_RESOLUTION;
++ sz->driver->data = sz;
++ sz->driver->rbuf = lirc_buf;
++ sz->delay_buf = delay_buf;
++ sz->driver->set_use_inc = &streamzap_use_inc;
++ sz->driver->set_use_dec = &streamzap_use_dec;
++ sz->driver->fops = &streamzap_fops;
++ sz->driver->dev = &interface->dev;
++ sz->driver->owner = THIS_MODULE;
++
++ sz->idle = 1;
++ sz->decoder_state = PulseSpace;
++ init_timer(&sz->delay_timer);
++ sz->delay_timer.function = delay_timeout;
++ sz->delay_timer.data = (unsigned long) sz;
++ sz->timer_running = 0;
++ spin_lock_init(&sz->timer_lock);
++
++ init_timer(&sz->flush_timer);
++ sz->flush_timer.function = flush_timeout;
++ sz->flush_timer.data = (unsigned long) sz;
++ /* Complete final initialisations */
++
++ usb_fill_int_urb(sz->urb_in, udev,
++ usb_rcvintpipe(udev, sz->endpoint->bEndpointAddress),
++ sz->buf_in, sz->buf_in_len, usb_streamzap_irq, sz,
++ sz->endpoint->bInterval);
++ sz->urb_in->transfer_dma = sz->dma_in;
++ sz->urb_in->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
++
++ if (udev->descriptor.iManufacturer
++ && usb_string(udev, udev->descriptor.iManufacturer,
++ buf, sizeof(buf)) > 0)
++ strlcpy(name, buf, sizeof(name));
++
++ if (udev->descriptor.iProduct
++ && usb_string(udev, udev->descriptor.iProduct,
++ buf, sizeof(buf)) > 0)
++ snprintf(name + strlen(name), sizeof(name) - strlen(name),
++ " %s", buf);
++
++ minor = lirc_register_driver(driver);
++
++ if (minor < 0)
++ goto free_delay_buf;
++
++ sz->driver->minor = minor;
++
++ usb_set_intfdata(interface, sz);
++
++ printk(KERN_INFO DRIVER_NAME "[%d]: %s on usb%d:%d attached\n",
++ sz->driver->minor, name,
++ udev->bus->busnum, sz->udev->devnum);
++
++ return 0;
++
++free_delay_buf:
++ lirc_buffer_free(sz->delay_buf);
++kfree_delay_buf:
++ kfree(delay_buf);
++free_lirc_buf:
++ lirc_buffer_free(sz->driver->rbuf);
++kfree_lirc_buf:
++ kfree(lirc_buf);
++free_driver:
++ kfree(driver);
++free_sz:
++ if (retval == -ENOMEM)
++ err("Out of memory");
++
++ if (sz) {
++ usb_free_urb(sz->urb_in);
++ usb_buffer_free(udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
++ kfree(sz);
++ }
++
++ return retval;
++}
++
++static int streamzap_use_inc(void *data)
++{
++ struct usb_streamzap *sz = data;
++
++ if (!sz) {
++ dprintk("%s called with no context", -1, __func__);
++ return -EINVAL;
++ }
++ dprintk("set use inc", sz->driver->minor);
++
++ lirc_buffer_clear(sz->driver->rbuf);
++ lirc_buffer_clear(sz->delay_buf);
++
++ sz->flush_timer.expires = jiffies + HZ;
++ sz->flush = 1;
++ add_timer(&sz->flush_timer);
++
++ sz->urb_in->dev = sz->udev;
++ if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
++ dprintk("open result = -EIO error submitting urb",
++ sz->driver->minor);
++ return -EIO;
++ }
++ sz->in_use++;
++
++ return 0;
++}
++
++static void streamzap_use_dec(void *data)
++{
++ struct usb_streamzap *sz = data;
++
++ if (!sz) {
++ dprintk("%s called with no context", -1, __func__);
++ return;
++ }
++ dprintk("set use dec", sz->driver->minor);
++
++ if (sz->flush) {
++ sz->flush = 0;
++ del_timer_sync(&sz->flush_timer);
++ }
++
++ usb_kill_urb(sz->urb_in);
++
++ stop_timer(sz);
++
++ sz->in_use--;
++}
++
++static int streamzap_ioctl(struct inode *node, struct file *filep,
++ unsigned int cmd, unsigned long arg)
++{
++ int result;
++
++ switch (cmd) {
++ case LIRC_GET_REC_RESOLUTION:
++ result = put_user(STREAMZAP_RESOLUTION, (unsigned int *) arg);
++ if (result)
++ return result;
++ break;
++ default:
++ return lirc_dev_fop_ioctl(node, filep, cmd, arg);
++ }
++ return 0;
++}
++
++/**
++ * streamzap_disconnect
++ *
++ * Called by the usb core when the device is removed from the system.
++ *
++ * This routine guarantees that the driver will not submit any more urbs
++ * by clearing dev->udev. It is also supposed to terminate any currently
++ * active urbs. Unfortunately, usb_bulk_msg(), used in streamzap_read(),
++ * does not provide any way to do this.
++ */
++static void streamzap_disconnect(struct usb_interface *interface)
++{
++ struct usb_streamzap *sz;
++ int errnum;
++ int minor;
++
++ sz = usb_get_intfdata(interface);
++
++ /* unregister from the LIRC sub-system */
++
++ errnum = lirc_unregister_driver(sz->driver->minor);
++ if (errnum != 0)
++ dprintk("error in lirc_unregister: (returned %d)",
++ sz->driver->minor, errnum);
++
++ lirc_buffer_free(sz->delay_buf);
++ lirc_buffer_free(sz->driver->rbuf);
++
++ /* unregister from the USB sub-system */
++
++ usb_free_urb(sz->urb_in);
++
++ usb_buffer_free(sz->udev, sz->buf_in_len, sz->buf_in, sz->dma_in);
++
++ minor = sz->driver->minor;
++ kfree(sz->driver->rbuf);
++ kfree(sz->driver);
++ kfree(sz->delay_buf);
++ kfree(sz);
++
++ printk(KERN_INFO DRIVER_NAME "[%d]: disconnected\n", minor);
++}
++
++static int streamzap_suspend(struct usb_interface *intf, pm_message_t message)
++{
++ struct usb_streamzap *sz = usb_get_intfdata(intf);
++
++ printk(KERN_INFO DRIVER_NAME "[%d]: suspend\n", sz->driver->minor);
++ if (sz->in_use) {
++ if (sz->flush) {
++ sz->flush = 0;
++ del_timer_sync(&sz->flush_timer);
++ }
++
++ stop_timer(sz);
++
++ usb_kill_urb(sz->urb_in);
++ }
++ return 0;
++}
++
++static int streamzap_resume(struct usb_interface *intf)
++{
++ struct usb_streamzap *sz = usb_get_intfdata(intf);
++
++ lirc_buffer_clear(sz->driver->rbuf);
++ lirc_buffer_clear(sz->delay_buf);
++
++ if (sz->in_use) {
++ sz->flush_timer.expires = jiffies + HZ;
++ sz->flush = 1;
++ add_timer(&sz->flush_timer);
++
++ sz->urb_in->dev = sz->udev;
++ if (usb_submit_urb(sz->urb_in, GFP_ATOMIC)) {
++ dprintk("open result = -EIO error submitting urb",
++ sz->driver->minor);
++ return -EIO;
++ }
++ }
++ return 0;
++}
++
++/**
++ * usb_streamzap_init
++ */
++static int __init usb_streamzap_init(void)
++{
++ int result;
++
++ /* register this driver with the USB subsystem */
++ result = usb_register(&streamzap_driver);
++
++ if (result) {
++ err("usb_register failed. Error number %d",
++ result);
++ return result;
++ }
++
++ printk(KERN_INFO DRIVER_NAME " " DRIVER_VERSION " registered\n");
++ return 0;
++}
++
++/**
++ * usb_streamzap_exit
++ */
++static void __exit usb_streamzap_exit(void)
++{
++ usb_deregister(&streamzap_driver);
++}
++
++
++module_init(usb_streamzap_init);
++module_exit(usb_streamzap_exit);
++
++MODULE_AUTHOR("Christoph Bartelmus, Greg Wickham, Adrian Dewhurst");
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_LICENSE("GPL");
++
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
+diff --git a/drivers/input/lirc/lirc_ttusbir.c b/drivers/input/lirc/lirc_ttusbir.c
+new file mode 100644
+index 0000000..b0a4e8b
+--- /dev/null
++++ b/drivers/input/lirc/lirc_ttusbir.c
+@@ -0,0 +1,397 @@
++/*
++ * lirc_ttusbir.c
++ *
++ * lirc_ttusbir - LIRC device driver for the TechnoTrend USB IR Receiver
++ *
++ * Copyright (C) 2007 Stefan Macher <st_maker-lirc@yahoo.de>
++ *
++ * This LIRC driver provides access to the TechnoTrend USB IR Receiver.
++ * The receiver delivers the IR signal as raw sampled true/false data in
++ * isochronous USB packets each of size 128 byte.
++ * Currently the driver reduces the sampling rate by factor of 8 as this
++ * is still more than enough to decode RC-5 - others should be analyzed.
++ * But the driver does not rely on RC-5 it should be able to decode every
++ * IR signal that is not too fast.
++ */
++
++/*
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ */
++
++#include <linux/version.h>
++#include <linux/kernel.h>
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/usb.h>
++
++#include <linux/lirc.h>
++#include "lirc_dev.h"
++
++MODULE_DESCRIPTION("TechnoTrend USB IR device driver for LIRC");
++MODULE_AUTHOR("Stefan Macher (st_maker-lirc@yahoo.de)");
++MODULE_LICENSE("GPL");
++
++/* #define DEBUG */
++#ifdef DEBUG
++#define DPRINTK printk
++#else
++#define DPRINTK(_x_, a...)
++#endif
++
++/* function declarations */
++static int probe(struct usb_interface *intf, const struct usb_device_id *id);
++static void disconnect(struct usb_interface *intf);
++static void urb_complete(struct urb *urb);
++static int set_use_inc(void *data);
++static void set_use_dec(void *data);
++
++static int num_urbs = 2;
++module_param(num_urbs, int, S_IRUGO);
++MODULE_PARM_DESC(num_urbs,
++ "Number of URBs in queue. Try to increase to 4 in case "
++ "of problems (default: 2; minimum: 2)");
++
++/* table of devices that work with this driver */
++static struct usb_device_id device_id_table[] = {
++ /* TechnoTrend USB IR Receiver */
++ { USB_DEVICE(0x0B48, 0x2003) },
++ /* Terminating entry */
++ { }
++};
++MODULE_DEVICE_TABLE(usb, device_id_table);
++
++/* USB driver definition */
++static struct usb_driver usb_driver = {
++ .name = "TTUSBIR",
++ .id_table = &(device_id_table[0]),
++ .probe = probe,
++ .disconnect = disconnect,
++};
++
++/* USB device definition */
++struct ttusbir_device {
++ struct usb_driver *usb_driver;
++ struct usb_device *udev;
++ struct usb_interface *interf;
++ struct usb_class_driver class_driver;
++ unsigned int ifnum; /* Interface number to use */
++ unsigned int alt_setting; /* alternate setting to use */
++ unsigned int endpoint; /* Endpoint to use */
++ struct urb **urb; /* num_urb URB pointers*/
++ char **buffer; /* 128 byte buffer for each URB */
++ struct lirc_buffer rbuf; /* Buffer towards LIRC */
++ struct lirc_driver driver;
++ int minor;
++ int last_pulse; /* remembers if last received byte was pulse or space */
++ int last_num; /* remembers how many last bytes appeared */
++ int opened;
++};
++
++/*** LIRC specific functions ***/
++static int set_use_inc(void *data)
++{
++ int i, retval;
++ struct ttusbir_device *ttusbir = data;
++
++ DPRINTK("Sending first URBs\n");
++ /* @TODO Do I need to check if I am already opened */
++ ttusbir->opened = 1;
++
++ for (i = 0; i < num_urbs; i++) {
++ retval = usb_submit_urb(ttusbir->urb[i], GFP_KERNEL);
++ if (retval) {
++ err("%s: usb_submit_urb failed on urb %d",
++ __func__, i);
++ return retval;
++ }
++ }
++ return 0;
++}
++
++static void set_use_dec(void *data)
++{
++ struct ttusbir_device *ttusbir = data;
++
++ DPRINTK("Device closed\n");
++
++ ttusbir->opened = 0;
++}
++
++/*** USB specific functions ***/
++
++/*
++ * This mapping table is used to do a very simple filtering of the
++ * input signal.
++ * For a value with at least 4 bits set it returns 0xFF otherwise
++ * 0x00. For faster IR signals this can not be used. But for RC-5 we
++ * still have about 14 samples per pulse/space, i.e. we sample with 14
++ * times higher frequency than the signal frequency
++ */
++const unsigned char map_table[] =
++{
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++ 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF,
++ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++ 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
++ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
++};
++
++static void urb_complete(struct urb *urb)
++{
++ struct ttusbir_device *ttusbir;
++ unsigned char *buf;
++ int i;
++ int l;
++
++ ttusbir = urb->context;
++
++ if (!ttusbir->opened)
++ return;
++
++ buf = (unsigned char *)urb->transfer_buffer;
++
++ for (i = 0; i < 128; i++) {
++ /* Here we do the filtering and some kind of down sampling */
++ buf[i] = ~map_table[buf[i]];
++ if (ttusbir->last_pulse == buf[i]) {
++ if (ttusbir->last_num < PULSE_MASK/63)
++ ttusbir->last_num++;
++ /*
++ * else we are in a idle period and do not need to
++ * increment any longer
++ */
++ } else {
++ l = ttusbir->last_num * 62; /* about 62 = us/byte */
++ if (ttusbir->last_pulse) /* pulse or space? */
++ l |= PULSE_BIT;
++ if (!lirc_buffer_full(&ttusbir->rbuf)) {
++ lirc_buffer_write(&ttusbir->rbuf, (void *)&l);
++ wake_up_interruptible(&ttusbir->rbuf.wait_poll);
++ }
++ ttusbir->last_num = 0;
++ ttusbir->last_pulse = buf[i];
++ }
++ }
++ usb_submit_urb(urb, GFP_ATOMIC); /* keep data rolling :-) */
++}
++
++/*
++ * Called whenever the USB subsystem thinks we could be the right driver
++ * to handle this device
++ */
++static int probe(struct usb_interface *intf, const struct usb_device_id *id)
++{
++ int alt_set, endp;
++ int found = 0;
++ int i, j;
++ int struct_size;
++ struct usb_host_interface *host_interf;
++ struct usb_interface_descriptor *interf_desc;
++ struct usb_host_endpoint *host_endpoint;
++ struct ttusbir_device *ttusbir;
++
++ DPRINTK("Module ttusbir probe\n");
++
++ /* To reduce memory fragmentation we use only one allocation */
++ struct_size = sizeof(struct ttusbir_device) +
++ (sizeof(struct urb *) * num_urbs) +
++ (sizeof(char *) * num_urbs) +
++ (num_urbs * 128);
++ ttusbir = kzalloc(struct_size, GFP_KERNEL);
++ if (!ttusbir)
++ return -ENOMEM;
++
++ ttusbir->urb = (struct urb **)((char *)ttusbir +
++ sizeof(struct ttusbir_device));
++ ttusbir->buffer = (char **)((char *)ttusbir->urb +
++ (sizeof(struct urb *) * num_urbs));
++ for (i = 0; i < num_urbs; i++)
++ ttusbir->buffer[i] = (char *)ttusbir->buffer +
++ (sizeof(char *)*num_urbs) + (i * 128);
++
++ ttusbir->usb_driver = &usb_driver;
++ ttusbir->alt_setting = -1;
++ /* @TODO check if error can be returned */
++ ttusbir->udev = usb_get_dev(interface_to_usbdev(intf));
++ ttusbir->interf = intf;
++ ttusbir->last_pulse = 0x00;
++ ttusbir->last_num = 0;
++
++ /*
++ * Now look for interface setting we can handle
++ * We are searching for the alt setting where end point
++ * 0x82 has max packet size 16
++ */
++ for (alt_set = 0; alt_set < intf->num_altsetting && !found; alt_set++) {
++ host_interf = &intf->altsetting[alt_set];
++ interf_desc = &host_interf->desc;
++ for (endp = 0; endp < interf_desc->bNumEndpoints; endp++) {
++ host_endpoint = &host_interf->endpoint[endp];
++ if ((host_endpoint->desc.bEndpointAddress == 0x82) &&
++ (host_endpoint->desc.wMaxPacketSize == 0x10)) {
++ ttusbir->alt_setting = alt_set;
++ ttusbir->endpoint = endp;
++ found = 1;
++ break;
++ }
++ }
++ }
++ if (ttusbir->alt_setting != -1)
++ DPRINTK("alt setting: %d\n", ttusbir->alt_setting);
++ else {
++ err("Could not find alternate setting\n");
++ kfree(ttusbir);
++ return -EINVAL;
++ }
++
++ /* OK lets setup this interface setting */
++ usb_set_interface(ttusbir->udev, 0, ttusbir->alt_setting);
++
++ /* Store device info in interface structure */
++ usb_set_intfdata(intf, ttusbir);
++
++ /* Register as a LIRC driver */
++ if (lirc_buffer_init(&ttusbir->rbuf, sizeof(int), 256) < 0) {
++ err("Could not get memory for LIRC data buffer\n");
++ usb_set_intfdata(intf, NULL);
++ kfree(ttusbir);
++ return -ENOMEM;
++ }
++ strcpy(ttusbir->driver.name, "TTUSBIR");
++ ttusbir->driver.minor = -1;
++ ttusbir->driver.code_length = 1;
++ ttusbir->driver.sample_rate = 0;
++ ttusbir->driver.data = ttusbir;
++ ttusbir->driver.add_to_buf = NULL;
++ ttusbir->driver.rbuf = &ttusbir->rbuf;
++ ttusbir->driver.set_use_inc = set_use_inc;
++ ttusbir->driver.set_use_dec = set_use_dec;
++ ttusbir->driver.dev = &intf->dev;
++ ttusbir->driver.owner = THIS_MODULE;
++ ttusbir->driver.features = LIRC_CAN_REC_MODE2;
++ ttusbir->minor = lirc_register_driver(&ttusbir->driver);
++ if (ttusbir->minor < 0) {
++ err("Error registering as LIRC driver\n");
++ usb_set_intfdata(intf, NULL);
++ lirc_buffer_free(&ttusbir->rbuf);
++ kfree(ttusbir);
++ return -EIO;
++ }
++
++ /* Allocate and setup the URB that we will use to talk to the device */
++ for (i = 0; i < num_urbs; i++) {
++ ttusbir->urb[i] = usb_alloc_urb(8, GFP_KERNEL);
++ if (!ttusbir->urb[i]) {
++ err("Could not allocate memory for the URB\n");
++ for (j = i - 1; j >= 0; j--)
++ kfree(ttusbir->urb[j]);
++ lirc_buffer_free(&ttusbir->rbuf);
++ lirc_unregister_driver(ttusbir->minor);
++ kfree(ttusbir);
++ usb_set_intfdata(intf, NULL);
++ return -ENOMEM;
++ }
++ ttusbir->urb[i]->dev = ttusbir->udev;
++ ttusbir->urb[i]->context = ttusbir;
++ ttusbir->urb[i]->pipe = usb_rcvisocpipe(ttusbir->udev,
++ ttusbir->endpoint);
++ ttusbir->urb[i]->interval = 1;
++ ttusbir->urb[i]->transfer_flags = URB_ISO_ASAP;
++ ttusbir->urb[i]->transfer_buffer = &ttusbir->buffer[i][0];
++ ttusbir->urb[i]->complete = urb_complete;
++ ttusbir->urb[i]->number_of_packets = 8;
++ ttusbir->urb[i]->transfer_buffer_length = 128;
++ for (j = 0; j < 8; j++) {
++ ttusbir->urb[i]->iso_frame_desc[j].offset = j*16;
++ ttusbir->urb[i]->iso_frame_desc[j].length = 16;
++ }
++ }
++ return 0;
++}
++
++/**
++ * Called when the driver is unloaded or the device is unplugged
++ */
++static void disconnect(struct usb_interface *intf)
++{
++ int i;
++ struct ttusbir_device *ttusbir;
++
++ DPRINTK("Module ttusbir disconnect\n");
++
++ ttusbir = (struct ttusbir_device *) usb_get_intfdata(intf);
++ usb_set_intfdata(intf, NULL);
++ lirc_unregister_driver(ttusbir->minor);
++ DPRINTK("unregistered\n");
++
++ for (i = 0; i < num_urbs; i++) {
++ usb_kill_urb(ttusbir->urb[i]);
++ usb_free_urb(ttusbir->urb[i]);
++ }
++ DPRINTK("URBs killed\n");
++ lirc_buffer_free(&ttusbir->rbuf);
++ kfree(ttusbir);
++}
++
++static int ttusbir_init_module(void)
++{
++ int result;
++
++ DPRINTK(KERN_DEBUG "Module ttusbir init\n");
++
++ /* register this driver with the USB subsystem */
++ result = usb_register(&usb_driver);
++ if (result)
++ err("usb_register failed. Error number %d", result);
++ return result;
++}
++
++static void ttusbir_exit_module(void)
++{
++ printk(KERN_DEBUG "Module ttusbir exit\n");
++ usb_deregister(&usb_driver);
++}
++
++module_init(ttusbir_init_module);
++module_exit(ttusbir_exit_module);
+diff --git a/drivers/input/lirc/lirc_zilog.c b/drivers/input/lirc/lirc_zilog.c
+new file mode 100644
+index 0000000..9f73430
+--- /dev/null
++++ b/drivers/input/lirc/lirc_zilog.c
+@@ -0,0 +1,1388 @@
++/*
++ * i2c IR lirc driver for devices with zilog IR processors
++ *
++ * Copyright (c) 2000 Gerd Knorr <kraxel@goldbach.in-berlin.de>
++ * modified for PixelView (BT878P+W/FM) by
++ * Michal Kochanowicz <mkochano@pld.org.pl>
++ * Christoph Bartelmus <lirc@bartelmus.de>
++ * modified for KNC ONE TV Station/Anubis Typhoon TView Tuner by
++ * Ulrich Mueller <ulrich.mueller42@web.de>
++ * modified for Asus TV-Box and Creative/VisionTek BreakOut-Box by
++ * Stefan Jahn <stefan@lkcc.org>
++ * modified for inclusion into kernel sources by
++ * Jerome Brock <jbrock@users.sourceforge.net>
++ * modified for Leadtek Winfast PVR2000 by
++ * Thomas Reitmayr (treitmayr@yahoo.com)
++ * modified for Hauppauge PVR-150 IR TX device by
++ * Mark Weaver <mark@npsl.co.uk>
++ * changed name from lirc_pvr150 to lirc_zilog, works on more than pvr-150
++ * Jarod Wilson <jarod@redhat.com>
++ *
++ * parts are cut&pasted from the lirc_i2c.c driver
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
++ *
++ */
++
++
++#include <linux/version.h>
++#include <linux/module.h>
++#include <linux/kmod.h>
++#include <linux/kernel.h>
++#include <linux/sched.h>
++#include <linux/fs.h>
++#include <linux/poll.h>
++#include <linux/string.h>
++#include <linux/timer.h>
++#include <linux/delay.h>
++#include <linux/completion.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/i2c.h>
++#include <linux/firmware.h>
++#include <linux/vmalloc.h>
++
++#include <linux/mutex.h>
++#include <linux/kthread.h>
++
++#include "lirc_dev.h"
++#include <linux/lirc.h>
++
++struct IR {
++ struct lirc_driver l;
++
++ /* Device info */
++ struct mutex ir_lock;
++ int open;
++
++ /* RX device */
++ struct i2c_client c_rx;
++ int have_rx;
++
++ /* RX device buffer & lock */
++ struct lirc_buffer buf;
++ struct mutex buf_lock;
++
++ /* RX polling thread data */
++ struct completion *t_notify;
++ struct completion *t_notify2;
++ int shutdown;
++ struct task_struct *task;
++
++ /* RX read data */
++ unsigned char b[3];
++
++ /* TX device */
++ struct i2c_client c_tx;
++ int need_boot;
++ int have_tx;
++};
++
++/* Minor -> data mapping */
++static struct IR *ir_devices[MAX_IRCTL_DEVICES];
++
++/* Block size for IR transmitter */
++#define TX_BLOCK_SIZE 99
++
++/* Hauppauge IR transmitter data */
++struct tx_data_struct {
++ /* Boot block */
++ unsigned char *boot_data;
++
++ /* Start of binary data block */
++ unsigned char *datap;
++
++ /* End of binary data block */
++ unsigned char *endp;
++
++ /* Number of installed codesets */
++ unsigned int num_code_sets;
++
++ /* Pointers to codesets */
++ unsigned char **code_sets;
++
++ /* Global fixed data template */
++ int fixed[TX_BLOCK_SIZE];
++};
++
++static struct tx_data_struct *tx_data;
++static struct mutex tx_data_lock;
++
++#define zilog_notify(s, args...) printk(KERN_NOTICE KBUILD_MODNAME ": " s, \
++ ## args)
++#define zilog_error(s, args...) printk(KERN_ERR KBUILD_MODNAME ": " s, ## args)
++
++#define ZILOG_HAUPPAUGE_IR_RX_NAME "Zilog/Hauppauge IR RX"
++#define ZILOG_HAUPPAUGE_IR_TX_NAME "Zilog/Hauppauge IR TX"
++
++/* module parameters */
++static int debug; /* debug output */
++static int disable_rx; /* disable RX device */
++static int disable_tx; /* disable TX device */
++static int minor = -1; /* minor number */
++
++#define dprintk(fmt, args...) \
++ do { \
++ if (debug) \
++ printk(KERN_DEBUG KBUILD_MODNAME ": " fmt, \
++ ## args); \
++ } while (0)
++
++static int add_to_buf(struct IR *ir)
++{
++ __u16 code;
++ unsigned char codes[2];
++ unsigned char keybuf[6];
++ int got_data = 0;
++ int ret;
++ int failures = 0;
++ unsigned char sendbuf[1] = { 0 };
++
++ if (lirc_buffer_full(&ir->buf)) {
++ dprintk("buffer overflow\n");
++ return -EOVERFLOW;
++ }
++
++ /*
++ * service the device as long as it is returning
++ * data and we have space
++ */
++ do {
++ /*
++ * Lock i2c bus for the duration. RX/TX chips interfere so
++ * this is worth it
++ */
++ mutex_lock(&ir->ir_lock);
++
++ /*
++ * Send random "poll command" (?) Windows driver does this
++ * and it is a good point to detect chip failure.
++ */
++ ret = i2c_master_send(&ir->c_rx, sendbuf, 1);
++ if (ret != 1) {
++ zilog_error("i2c_master_send failed with %d\n", ret);
++ if (failures >= 3) {
++ mutex_unlock(&ir->ir_lock);
++ zilog_error("unable to read from the IR chip "
++ "after 3 resets, giving up\n");
++ return ret;
++ }
++
++ /* Looks like the chip crashed, reset it */
++ zilog_error("polling the IR receiver chip failed, "
++ "trying reset\n");
++
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ schedule_timeout((100 * HZ + 999) / 1000);
++ ir->need_boot = 1;
++
++ ++failures;
++ mutex_unlock(&ir->ir_lock);
++ continue;
++ }
++
++ ret = i2c_master_recv(&ir->c_rx, keybuf, sizeof(keybuf));
++ mutex_unlock(&ir->ir_lock);
++ if (ret != sizeof(keybuf)) {
++ zilog_error("i2c_master_recv failed with %d -- "
++ "keeping last read buffer\n", ret);
++ } else {
++ ir->b[0] = keybuf[3];
++ ir->b[1] = keybuf[4];
++ ir->b[2] = keybuf[5];
++ dprintk("key (0x%02x/0x%02x)\n", ir->b[0], ir->b[1]);
++ }
++
++ /* key pressed ? */
++#ifdef I2C_HW_B_HDPVR
++ if (ir->c_rx.adapter->id == I2C_HW_B_HDPVR) {
++ if (got_data && (keybuf[0] == 0x80))
++ return 0;
++ else if (got_data && (keybuf[0] == 0x00))
++ return -ENODATA;
++ } else if ((ir->b[0] & 0x80) == 0)
++#else
++ if ((ir->b[0] & 0x80) == 0)
++#endif
++ return got_data ? 0 : -ENODATA;
++
++ /* look what we have */
++ code = (((__u16)ir->b[0] & 0x7f) << 6) | (ir->b[1] >> 2);
++
++ codes[0] = (code >> 8) & 0xff;
++ codes[1] = code & 0xff;
++
++ /* return it */
++ lirc_buffer_write(&ir->buf, codes);
++ ++got_data;
++ } while (!lirc_buffer_full(&ir->buf));
++
++ return 0;
++}
++
++/*
++ * Main function of the polling thread -- from lirc_dev.
++ * We don't fit the LIRC model at all anymore. This is horrible, but
++ * basically we have a single RX/TX device with a nasty failure mode
++ * that needs to be accounted for across the pair. lirc lets us provide
++ * fops, but prevents us from using the internal polling, etc. if we do
++ * so. Hence the replication. Might be neater to extend the LIRC model
++ * to account for this but I'd think it's a very special case of seriously
++ * messed up hardware.
++ */
++static int lirc_thread(void *arg)
++{
++ struct IR *ir = arg;
++
++ if (ir->t_notify != NULL)
++ complete(ir->t_notify);
++
++ dprintk("poll thread started\n");
++
++ do {
++ if (ir->open) {
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ /*
++ * This is ~113*2 + 24 + jitter (2*repeat gap +
++ * code length). We use this interval as the chip
++ * resets every time you poll it (bad!). This is
++ * therefore just sufficient to catch all of the
++ * button presses. It makes the remote much more
++ * responsive. You can see the difference by
++ * running irw and holding down a button. With
++ * 100ms, the old polling interval, you'll notice
++ * breaks in the repeat sequence corresponding to
++ * lost keypresses.
++ */
++ schedule_timeout((260 * HZ) / 1000);
++ if (ir->shutdown)
++ break;
++ if (!add_to_buf(ir))
++ wake_up_interruptible(&ir->buf.wait_poll);
++ } else {
++ /* if device not opened so we can sleep half a second */
++ set_current_state(TASK_INTERRUPTIBLE);
++ schedule_timeout(HZ/2);
++ }
++ } while (!ir->shutdown);
++
++ if (ir->t_notify2 != NULL)
++ wait_for_completion(ir->t_notify2);
++
++ ir->task = NULL;
++ if (ir->t_notify != NULL)
++ complete(ir->t_notify);
++
++ dprintk("poll thread ended\n");
++ return 0;
++}
++
++static int set_use_inc(void *data)
++{
++ struct IR *ir = data;
++
++ if (ir->l.owner == NULL || try_module_get(ir->l.owner) == 0)
++ return -ENODEV;
++
++ /* lock bttv in memory while /dev/lirc is in use */
++ /*
++ * this is completely broken code. lirc_unregister_driver()
++ * must be possible even when the device is open
++ */
++ if (ir->c_rx.addr)
++ i2c_use_client(&ir->c_rx);
++ if (ir->c_tx.addr)
++ i2c_use_client(&ir->c_tx);
++
++ return 0;
++}
++
++static void set_use_dec(void *data)
++{
++ struct IR *ir = data;
++
++ if (ir->c_rx.addr)
++ i2c_release_client(&ir->c_rx);
++ if (ir->c_tx.addr)
++ i2c_release_client(&ir->c_tx);
++ if (ir->l.owner != NULL)
++ module_put(ir->l.owner);
++}
++
++/* safe read of a uint32 (always network byte order) */
++static int read_uint32(unsigned char **data,
++ unsigned char *endp, unsigned int *val)
++{
++ if (*data + 4 > endp)
++ return 0;
++ *val = ((*data)[0] << 24) | ((*data)[1] << 16) |
++ ((*data)[2] << 8) | (*data)[3];
++ *data += 4;
++ return 1;
++}
++
++/* safe read of a uint8 */
++static int read_uint8(unsigned char **data,
++ unsigned char *endp, unsigned char *val)
++{
++ if (*data + 1 > endp)
++ return 0;
++ *val = *((*data)++);
++ return 1;
++}
++
++/* safe skipping of N bytes */
++static int skip(unsigned char **data,
++ unsigned char *endp, unsigned int distance)
++{
++ if (*data + distance > endp)
++ return 0;
++ *data += distance;
++ return 1;
++}
++
++/* decompress key data into the given buffer */
++static int get_key_data(unsigned char *buf,
++ unsigned int codeset, unsigned int key)
++{
++ unsigned char *data, *endp, *diffs, *key_block;
++ unsigned char keys, ndiffs, id;
++ unsigned int base, lim, pos, i;
++
++ /* Binary search for the codeset */
++ for (base = 0, lim = tx_data->num_code_sets; lim; lim >>= 1) {
++ pos = base + (lim >> 1);
++ data = tx_data->code_sets[pos];
++
++ if (!read_uint32(&data, tx_data->endp, &i))
++ goto corrupt;
++
++ if (i == codeset)
++ break;
++ else if (codeset > i) {
++ base = pos + 1;
++ --lim;
++ }
++ }
++ /* Not found? */
++ if (!lim)
++ return -EPROTO;
++
++ /* Set end of data block */
++ endp = pos < tx_data->num_code_sets - 1 ?
++ tx_data->code_sets[pos + 1] : tx_data->endp;
++
++ /* Read the block header */
++ if (!read_uint8(&data, endp, &keys) ||
++ !read_uint8(&data, endp, &ndiffs) ||
++ ndiffs > TX_BLOCK_SIZE || keys == 0)
++ goto corrupt;
++
++ /* Save diffs & skip */
++ diffs = data;
++ if (!skip(&data, endp, ndiffs))
++ goto corrupt;
++
++ /* Read the id of the first key */
++ if (!read_uint8(&data, endp, &id))
++ goto corrupt;
++
++ /* Unpack the first key's data */
++ for (i = 0; i < TX_BLOCK_SIZE; ++i) {
++ if (tx_data->fixed[i] == -1) {
++ if (!read_uint8(&data, endp, &buf[i]))
++ goto corrupt;
++ } else {
++ buf[i] = (unsigned char)tx_data->fixed[i];
++ }
++ }
++
++ /* Early out key found/not found */
++ if (key == id)
++ return 0;
++ if (keys == 1)
++ return -EPROTO;
++
++ /* Sanity check */
++ key_block = data;
++ if (!skip(&data, endp, (keys - 1) * (ndiffs + 1)))
++ goto corrupt;
++
++ /* Binary search for the key */
++ for (base = 0, lim = keys - 1; lim; lim >>= 1) {
++ /* Seek to block */
++ unsigned char *key_data;
++ pos = base + (lim >> 1);
++ key_data = key_block + (ndiffs + 1) * pos;
++
++ if (*key_data == key) {
++ /* skip key id */
++ ++key_data;
++
++ /* found, so unpack the diffs */
++ for (i = 0; i < ndiffs; ++i) {
++ unsigned char val;
++ if (!read_uint8(&key_data, endp, &val) ||
++ diffs[i] >= TX_BLOCK_SIZE)
++ goto corrupt;
++ buf[diffs[i]] = val;
++ }
++
++ return 0;
++ } else if (key > *key_data) {
++ base = pos + 1;
++ --lim;
++ }
++ }
++ /* Key not found */
++ return -EPROTO;
++
++corrupt:
++ zilog_error("firmware is corrupt\n");
++ return -EFAULT;
++}
++
++/* send a block of data to the IR TX device */
++static int send_data_block(struct IR *ir, unsigned char *data_block)
++{
++ int i, j, ret;
++ unsigned char buf[5];
++
++ for (i = 0; i < TX_BLOCK_SIZE;) {
++ int tosend = TX_BLOCK_SIZE - i;
++ if (tosend > 4)
++ tosend = 4;
++ buf[0] = (unsigned char)(i + 1);
++ for (j = 0; j < tosend; ++j)
++ buf[1 + j] = data_block[i + j];
++ dprintk("%02x %02x %02x %02x %02x",
++ buf[0], buf[1], buf[2], buf[3], buf[4]);
++ ret = i2c_master_send(&ir->c_tx, buf, tosend + 1);
++ if (ret != tosend + 1) {
++ zilog_error("i2c_master_send failed with %d\n", ret);
++ return ret < 0 ? ret : -EFAULT;
++ }
++ i += tosend;
++ }
++ return 0;
++}
++
++/* send boot data to the IR TX device */
++static int send_boot_data(struct IR *ir)
++{
++ int ret;
++ unsigned char buf[4];
++
++ /* send the boot block */
++ ret = send_data_block(ir, tx_data->boot_data);
++ if (ret != 0)
++ return ret;
++
++ /* kick it off? */
++ buf[0] = 0x00;
++ buf[1] = 0x20;
++ ret = i2c_master_send(&ir->c_tx, buf, 2);
++ if (ret != 2) {
++ zilog_error("i2c_master_send failed with %d\n", ret);
++ return ret < 0 ? ret : -EFAULT;
++ }
++ ret = i2c_master_send(&ir->c_tx, buf, 1);
++ if (ret != 1) {
++ zilog_error("i2c_master_send failed with %d\n", ret);
++ return ret < 0 ? ret : -EFAULT;
++ }
++
++ /* Here comes the firmware version... (hopefully) */
++ ret = i2c_master_recv(&ir->c_tx, buf, 4);
++ if (ret != 4) {
++ zilog_error("i2c_master_recv failed with %d\n", ret);
++ return 0;
++ }
++ if (buf[0] != 0x80) {
++ zilog_error("unexpected IR TX response: %02x\n", buf[0]);
++ return 0;
++ }
++ zilog_notify("Zilog/Hauppauge IR blaster firmware version "
++ "%d.%d.%d loaded\n", buf[1], buf[2], buf[3]);
++
++ return 0;
++}
++
++/* unload "firmware", lock held */
++static void fw_unload_locked(void)
++{
++ if (tx_data) {
++ if (tx_data->code_sets)
++ vfree(tx_data->code_sets);
++
++ if (tx_data->datap)
++ vfree(tx_data->datap);
++
++ vfree(tx_data);
++ tx_data = NULL;
++ dprintk("successfully unloaded IR blaster firmware\n");
++ }
++}
++
++/* unload "firmware" for the IR TX device */
++static void fw_unload(void)
++{
++ mutex_lock(&tx_data_lock);
++ fw_unload_locked();
++ mutex_unlock(&tx_data_lock);
++}
++
++/* load "firmware" for the IR TX device */
++static int fw_load(struct IR *ir)
++{
++ int ret;
++ unsigned int i;
++ unsigned char *data, version, num_global_fixed;
++ const struct firmware *fw_entry;
++
++ /* Already loaded? */
++ mutex_lock(&tx_data_lock);
++ if (tx_data) {
++ ret = 0;
++ goto out;
++ }
++
++ /* Request codeset data file */
++ ret = request_firmware(&fw_entry, "haup-ir-blaster.bin", &ir->c_tx.dev);
++ if (ret != 0) {
++ zilog_error("firmware haup-ir-blaster.bin not available "
++ "(%d)\n", ret);
++ ret = ret < 0 ? ret : -EFAULT;
++ goto out;
++ }
++ dprintk("firmware of size %zu loaded\n", fw_entry->size);
++
++ /* Parse the file */
++ tx_data = vmalloc(sizeof(*tx_data));
++ if (tx_data == NULL) {
++ zilog_error("out of memory\n");
++ release_firmware(fw_entry);
++ ret = -ENOMEM;
++ goto out;
++ }
++ tx_data->code_sets = NULL;
++
++ /* Copy the data so hotplug doesn't get confused and timeout */
++ tx_data->datap = vmalloc(fw_entry->size);
++ if (tx_data->datap == NULL) {
++ zilog_error("out of memory\n");
++ release_firmware(fw_entry);
++ vfree(tx_data);
++ ret = -ENOMEM;
++ goto out;
++ }
++ memcpy(tx_data->datap, fw_entry->data, fw_entry->size);
++ tx_data->endp = tx_data->datap + fw_entry->size;
++ release_firmware(fw_entry); fw_entry = NULL;
++
++ /* Check version */
++ data = tx_data->datap;
++ if (!read_uint8(&data, tx_data->endp, &version))
++ goto corrupt;
++ if (version != 1) {
++ zilog_error("unsupported code set file version (%u, expected"
++ "1) -- please upgrade to a newer driver",
++ version);
++ fw_unload_locked();
++ ret = -EFAULT;
++ goto out;
++ }
++
++ /* Save boot block for later */
++ tx_data->boot_data = data;
++ if (!skip(&data, tx_data->endp, TX_BLOCK_SIZE))
++ goto corrupt;
++
++ if (!read_uint32(&data, tx_data->endp,
++ &tx_data->num_code_sets))
++ goto corrupt;
++
++ dprintk("%u IR blaster codesets loaded\n", tx_data->num_code_sets);
++
++ tx_data->code_sets = vmalloc(
++ tx_data->num_code_sets * sizeof(char *));
++ if (tx_data->code_sets == NULL) {
++ fw_unload_locked();
++ ret = -ENOMEM;
++ goto out;
++ }
++
++ for (i = 0; i < TX_BLOCK_SIZE; ++i)
++ tx_data->fixed[i] = -1;
++
++ /* Read global fixed data template */
++ if (!read_uint8(&data, tx_data->endp, &num_global_fixed) ||
++ num_global_fixed > TX_BLOCK_SIZE)
++ goto corrupt;
++ for (i = 0; i < num_global_fixed; ++i) {
++ unsigned char pos, val;
++ if (!read_uint8(&data, tx_data->endp, &pos) ||
++ !read_uint8(&data, tx_data->endp, &val) ||
++ pos >= TX_BLOCK_SIZE)
++ goto corrupt;
++ tx_data->fixed[pos] = (int)val;
++ }
++
++ /* Filch out the position of each code set */
++ for (i = 0; i < tx_data->num_code_sets; ++i) {
++ unsigned int id;
++ unsigned char keys;
++ unsigned char ndiffs;
++
++ /* Save the codeset position */
++ tx_data->code_sets[i] = data;
++
++ /* Read header */
++ if (!read_uint32(&data, tx_data->endp, &id) ||
++ !read_uint8(&data, tx_data->endp, &keys) ||
++ !read_uint8(&data, tx_data->endp, &ndiffs) ||
++ ndiffs > TX_BLOCK_SIZE || keys == 0)
++ goto corrupt;
++
++ /* skip diff positions */
++ if (!skip(&data, tx_data->endp, ndiffs))
++ goto corrupt;
++
++ /*
++ * After the diffs we have the first key id + data -
++ * global fixed
++ */
++ if (!skip(&data, tx_data->endp,
++ 1 + TX_BLOCK_SIZE - num_global_fixed))
++ goto corrupt;
++
++ /* Then we have keys-1 blocks of key id+diffs */
++ if (!skip(&data, tx_data->endp,
++ (ndiffs + 1) * (keys - 1)))
++ goto corrupt;
++ }
++ ret = 0;
++ goto out;
++
++corrupt:
++ zilog_error("firmware is corrupt\n");
++ fw_unload_locked();
++ ret = -EFAULT;
++
++out:
++ mutex_unlock(&tx_data_lock);
++ return ret;
++}
++
++/* initialise the IR TX device */
++static int tx_init(struct IR *ir)
++{
++ int ret;
++
++ /* Load 'firmware' */
++ ret = fw_load(ir);
++ if (ret != 0)
++ return ret;
++
++ /* Send boot block */
++ ret = send_boot_data(ir);
++ if (ret != 0)
++ return ret;
++ ir->need_boot = 0;
++
++ /* Looks good */
++ return 0;
++}
++
++/* do nothing stub to make LIRC happy */
++static loff_t lseek(struct file *filep, loff_t offset, int orig)
++{
++ return -ESPIPE;
++}
++
++/* copied from lirc_dev */
++static ssize_t read(struct file *filep, char *outbuf, size_t n, loff_t *ppos)
++{
++ struct IR *ir = (struct IR *)filep->private_data;
++ unsigned char buf[ir->buf.chunk_size];
++ int ret = 0, written = 0;
++ DECLARE_WAITQUEUE(wait, current);
++
++ dprintk("read called\n");
++ if (ir->c_rx.addr == 0)
++ return -ENODEV;
++
++ if (mutex_lock_interruptible(&ir->buf_lock))
++ return -ERESTARTSYS;
++
++ if (n % ir->buf.chunk_size) {
++ dprintk("read result = -EINVAL\n");
++ mutex_unlock(&ir->buf_lock);
++ return -EINVAL;
++ }
++
++ /*
++ * we add ourselves to the task queue before buffer check
++ * to avoid losing scan code (in case when queue is awaken somewhere
++ * between while condition checking and scheduling)
++ */
++ add_wait_queue(&ir->buf.wait_poll, &wait);
++ set_current_state(TASK_INTERRUPTIBLE);
++
++ /*
++ * while we didn't provide 'length' bytes, device is opened in blocking
++ * mode and 'copy_to_user' is happy, wait for data.
++ */
++ while (written < n && ret == 0) {
++ if (lirc_buffer_empty(&ir->buf)) {
++ /*
++ * According to the read(2) man page, 'written' can be
++ * returned as less than 'n', instead of blocking
++ * again, returning -EWOULDBLOCK, or returning
++ * -ERESTARTSYS
++ */
++ if (written)
++ break;
++ if (filep->f_flags & O_NONBLOCK) {
++ ret = -EWOULDBLOCK;
++ break;
++ }
++ if (signal_pending(current)) {
++ ret = -ERESTARTSYS;
++ break;
++ }
++ schedule();
++ set_current_state(TASK_INTERRUPTIBLE);
++ } else {
++ lirc_buffer_read(&ir->buf, buf);
++ ret = copy_to_user((void *)outbuf+written, buf,
++ ir->buf.chunk_size);
++ written += ir->buf.chunk_size;
++ }
++ }
++
++ remove_wait_queue(&ir->buf.wait_poll, &wait);
++ set_current_state(TASK_RUNNING);
++ mutex_unlock(&ir->buf_lock);
++
++ dprintk("read result = %s (%d)\n",
++ ret ? "-EFAULT" : "OK", ret);
++
++ return ret ? ret : written;
++}
++
++/* send a keypress to the IR TX device */
++static int send_code(struct IR *ir, unsigned int code, unsigned int key)
++{
++ unsigned char data_block[TX_BLOCK_SIZE];
++ unsigned char buf[2];
++ int i, ret;
++
++ /* Get data for the codeset/key */
++ ret = get_key_data(data_block, code, key);
++
++ if (ret == -EPROTO) {
++ zilog_error("failed to get data for code %u, key %u -- check "
++ "lircd.conf entries\n", code, key);
++ return ret;
++ } else if (ret != 0)
++ return ret;
++
++ /* Send the data block */
++ ret = send_data_block(ir, data_block);
++ if (ret != 0)
++ return ret;
++
++ /* Send data block length? */
++ buf[0] = 0x00;
++ buf[1] = 0x40;
++ ret = i2c_master_send(&ir->c_tx, buf, 2);
++ if (ret != 2) {
++ zilog_error("i2c_master_send failed with %d\n", ret);
++ return ret < 0 ? ret : -EFAULT;
++ }
++ ret = i2c_master_send(&ir->c_tx, buf, 1);
++ if (ret != 1) {
++ zilog_error("i2c_master_send failed with %d\n", ret);
++ return ret < 0 ? ret : -EFAULT;
++ }
++
++ /* Send finished download? */
++ ret = i2c_master_recv(&ir->c_tx, buf, 1);
++ if (ret != 1) {
++ zilog_error("i2c_master_recv failed with %d\n", ret);
++ return ret < 0 ? ret : -EFAULT;
++ }
++ if (buf[0] != 0xA0) {
++ zilog_error("unexpected IR TX response #1: %02x\n",
++ buf[0]);
++ return -EFAULT;
++ }
++
++ /* Send prepare command? */
++ buf[0] = 0x00;
++ buf[1] = 0x80;
++ ret = i2c_master_send(&ir->c_tx, buf, 2);
++ if (ret != 2) {
++ zilog_error("i2c_master_send failed with %d\n", ret);
++ return ret < 0 ? ret : -EFAULT;
++ }
++
++#ifdef I2C_HW_B_HDPVR
++ /*
++ * The sleep bits aren't necessary on the HD PVR, and in fact, the
++ * last i2c_master_recv always fails with a -5, so for now, we're
++ * going to skip this whole mess and say we're done on the HD PVR
++ */
++ if (ir->c_rx.adapter->id == I2C_HW_B_HDPVR)
++ goto done;
++#endif
++
++ /*
++ * This bit NAKs until the device is ready, so we retry it
++ * sleeping a bit each time. This seems to be what the windows
++ * driver does, approximately.
++ * Try for up to 1s.
++ */
++ for (i = 0; i < 20; ++i) {
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ schedule_timeout((50 * HZ + 999) / 1000);
++ ret = i2c_master_send(&ir->c_tx, buf, 1);
++ if (ret == 1)
++ break;
++ dprintk("NAK expected: i2c_master_send "
++ "failed with %d (try %d)\n", ret, i+1);
++ }
++ if (ret != 1) {
++ zilog_error("IR TX chip never got ready: last i2c_master_send "
++ "failed with %d\n", ret);
++ return ret < 0 ? ret : -EFAULT;
++ }
++
++ /* Seems to be an 'ok' response */
++ i = i2c_master_recv(&ir->c_tx, buf, 1);
++ if (i != 1) {
++ zilog_error("i2c_master_recv failed with %d\n", ret);
++ return -EFAULT;
++ }
++ if (buf[0] != 0x80) {
++ zilog_error("unexpected IR TX response #2: %02x\n", buf[0]);
++ return -EFAULT;
++ }
++
++done:
++ /* Oh good, it worked */
++ dprintk("sent code %u, key %u\n", code, key);
++ return 0;
++}
++
++/*
++ * Write a code to the device. We take in a 32-bit number (an int) and then
++ * decode this to a codeset/key index. The key data is then decompressed and
++ * sent to the device. We have a spin lock as per i2c documentation to prevent
++ * multiple concurrent sends which would probably cause the device to explode.
++ */
++static ssize_t write(struct file *filep, const char *buf, size_t n,
++ loff_t *ppos)
++{
++ struct IR *ir = (struct IR *)filep->private_data;
++ size_t i;
++ int failures = 0;
++
++ if (ir->c_tx.addr == 0)
++ return -ENODEV;
++
++ /* Validate user parameters */
++ if (n % sizeof(int))
++ return -EINVAL;
++
++ /* Lock i2c bus for the duration */
++ mutex_lock(&ir->ir_lock);
++
++ /* Send each keypress */
++ for (i = 0; i < n;) {
++ int ret = 0;
++ int command;
++
++ if (copy_from_user(&command, buf + i, sizeof(command))) {
++ mutex_unlock(&ir->ir_lock);
++ return -EFAULT;
++ }
++
++ /* Send boot data first if required */
++ if (ir->need_boot == 1) {
++ ret = send_boot_data(ir);
++ if (ret == 0)
++ ir->need_boot = 0;
++ }
++
++ /* Send the code */
++ if (ret == 0) {
++ ret = send_code(ir, (unsigned)command >> 16,
++ (unsigned)command & 0xFFFF);
++ if (ret == -EPROTO) {
++ mutex_unlock(&ir->ir_lock);
++ return ret;
++ }
++ }
++
++ /*
++ * Hmm, a failure. If we've had a few then give up, otherwise
++ * try a reset
++ */
++ if (ret != 0) {
++ /* Looks like the chip crashed, reset it */
++ zilog_error("sending to the IR transmitter chip "
++ "failed, trying reset\n");
++
++ if (failures >= 3) {
++ zilog_error("unable to send to the IR chip "
++ "after 3 resets, giving up\n");
++ mutex_unlock(&ir->ir_lock);
++ return ret;
++ }
++ set_current_state(TASK_UNINTERRUPTIBLE);
++ schedule_timeout((100 * HZ + 999) / 1000);
++ ir->need_boot = 1;
++ ++failures;
++ } else
++ i += sizeof(int);
++ }
++
++ /* Release i2c bus */
++ mutex_unlock(&ir->ir_lock);
++
++ /* All looks good */
++ return n;
++}
++
++/* copied from lirc_dev */
++static unsigned int poll(struct file *filep, poll_table *wait)
++{
++ struct IR *ir = (struct IR *)filep->private_data;
++ unsigned int ret;
++
++ dprintk("poll called\n");
++ if (ir->c_rx.addr == 0)
++ return -ENODEV;
++
++ mutex_lock(&ir->buf_lock);
++
++ poll_wait(filep, &ir->buf.wait_poll, wait);
++
++ dprintk("poll result = %s\n",
++ lirc_buffer_empty(&ir->buf) ? "0" : "POLLIN|POLLRDNORM");
++
++ ret = lirc_buffer_empty(&ir->buf) ? 0 : (POLLIN|POLLRDNORM);
++
++ mutex_unlock(&ir->buf_lock);
++ return ret;
++}
++
++static int ioctl(struct inode *node, struct file *filep, unsigned int cmd,
++ unsigned long arg)
++{
++ struct IR *ir = (struct IR *)filep->private_data;
++ int result;
++ unsigned long mode, features = 0;
++
++ if (ir->c_rx.addr != 0)
++ features |= LIRC_CAN_REC_LIRCCODE;
++ if (ir->c_tx.addr != 0)
++ features |= LIRC_CAN_SEND_PULSE;
++
++ switch (cmd) {
++ case LIRC_GET_LENGTH:
++ result = put_user((unsigned long)13,
++ (unsigned long *)arg);
++ break;
++ case LIRC_GET_FEATURES:
++ result = put_user(features, (unsigned long *) arg);
++ break;
++ case LIRC_GET_REC_MODE:
++ if (!(features&LIRC_CAN_REC_MASK))
++ return -ENOSYS;
++
++ result = put_user(LIRC_REC2MODE
++ (features&LIRC_CAN_REC_MASK),
++ (unsigned long *)arg);
++ break;
++ case LIRC_SET_REC_MODE:
++ if (!(features&LIRC_CAN_REC_MASK))
++ return -ENOSYS;
++
++ result = get_user(mode, (unsigned long *)arg);
++ if (!result && !(LIRC_MODE2REC(mode) & features))
++ result = -EINVAL;
++ break;
++ case LIRC_GET_SEND_MODE:
++ if (!(features&LIRC_CAN_SEND_MASK))
++ return -ENOSYS;
++
++ result = put_user(LIRC_MODE_PULSE, (unsigned long *) arg);
++ break;
++ case LIRC_SET_SEND_MODE:
++ if (!(features&LIRC_CAN_SEND_MASK))
++ return -ENOSYS;
++
++ result = get_user(mode, (unsigned long *) arg);
++ if (!result && mode != LIRC_MODE_PULSE)
++ return -EINVAL;
++ break;
++ default:
++ return -EINVAL;
++ }
++ return result;
++}
++
++/*
++ * Open the IR device. Get hold of our IR structure and
++ * stash it in private_data for the file
++ */
++static int open(struct inode *node, struct file *filep)
++{
++ struct IR *ir;
++ int ret;
++
++ /* find our IR struct */
++ unsigned minor = MINOR(node->i_rdev);
++ if (minor >= MAX_IRCTL_DEVICES) {
++ dprintk("minor %d: open result = -ENODEV\n",
++ minor);
++ return -ENODEV;
++ }
++ ir = ir_devices[minor];
++
++ /* increment in use count */
++ mutex_lock(&ir->ir_lock);
++ ++ir->open;
++ ret = set_use_inc(ir);
++ if (ret != 0) {
++ --ir->open;
++ mutex_unlock(&ir->ir_lock);
++ return ret;
++ }
++ mutex_unlock(&ir->ir_lock);
++
++ /* stash our IR struct */
++ filep->private_data = ir;
++
++ return 0;
++}
++
++/* Close the IR device */
++static int close(struct inode *node, struct file *filep)
++{
++ /* find our IR struct */
++ struct IR *ir = (struct IR *)filep->private_data;
++ if (ir == NULL) {
++ zilog_error("close: no private_data attached to the file!\n");
++ return -ENODEV;
++ }
++
++ /* decrement in use count */
++ mutex_lock(&ir->ir_lock);
++ --ir->open;
++ set_use_dec(ir);
++ mutex_unlock(&ir->ir_lock);
++
++ return 0;
++}
++
++static struct lirc_driver lirc_template = {
++ .name = "lirc_zilog",
++ .set_use_inc = set_use_inc,
++ .set_use_dec = set_use_dec,
++ .owner = THIS_MODULE
++};
++
++static int ir_remove(struct i2c_client *client);
++static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id);
++static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg);
++
++static const struct i2c_device_id ir_transceiver_id[] = {
++ /* Generic entry for any IR transceiver */
++ { "ir_video", 0 },
++ /* IR device specific entries should be added here */
++ { "ir_tx_z8f0811_haup", 0 },
++ { "ir_rx_z8f0811_haup", 0 },
++ { }
++};
++
++static struct i2c_driver driver = {
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = "Zilog/Hauppauge i2c IR",
++ },
++ .probe = ir_probe,
++ .remove = ir_remove,
++ .command = ir_command,
++ .id_table = ir_transceiver_id,
++};
++
++static struct file_operations lirc_fops = {
++ .owner = THIS_MODULE,
++ .llseek = lseek,
++ .read = read,
++ .write = write,
++ .poll = poll,
++ .ioctl = ioctl,
++ .open = open,
++ .release = close
++};
++
++static int ir_remove(struct i2c_client *client)
++{
++ struct IR *ir = i2c_get_clientdata(client);
++
++ mutex_lock(&ir->ir_lock);
++
++ if (ir->have_rx || ir->have_tx) {
++ DECLARE_COMPLETION(tn);
++ DECLARE_COMPLETION(tn2);
++
++ /* end up polling thread */
++ if (ir->task && !IS_ERR(ir->task)) {
++ ir->t_notify = &tn;
++ ir->t_notify2 = &tn2;
++ ir->shutdown = 1;
++ wake_up_process(ir->task);
++ complete(&tn2);
++ wait_for_completion(&tn);
++ ir->t_notify = NULL;
++ ir->t_notify2 = NULL;
++ }
++
++ } else {
++ mutex_unlock(&ir->ir_lock);
++ zilog_error("%s: detached from something we didn't "
++ "attach to\n", __func__);
++ return -ENODEV;
++ }
++
++ /* unregister lirc driver */
++ if (ir->l.minor >= 0 && ir->l.minor < MAX_IRCTL_DEVICES) {
++ lirc_unregister_driver(ir->l.minor);
++ ir_devices[ir->l.minor] = NULL;
++ }
++
++ /* free memory */
++ lirc_buffer_free(&ir->buf);
++ mutex_unlock(&ir->ir_lock);
++ kfree(ir);
++
++ return 0;
++}
++
++static int ir_probe(struct i2c_client *client, const struct i2c_device_id *id)
++{
++ struct IR *ir = NULL;
++ struct i2c_adapter *adap = client->adapter;
++ char buf;
++ int ret;
++ int have_rx = 0, have_tx = 0;
++
++ dprintk("%s: adapter id=0x%x, client addr=0x%02x\n",
++ __func__, adap->id, client->addr);
++
++ /*
++ * The external IR receiver is at i2c address 0x71.
++ * The IR transmitter is at 0x70.
++ */
++ client->addr = 0x70;
++
++ if (!disable_tx) {
++ if (i2c_master_recv(client, &buf, 1) == 1)
++ have_tx = 1;
++ dprintk("probe 0x70 @ %s: %s\n",
++ adap->name, have_tx ? "success" : "failed");
++ }
++
++ if (!disable_rx) {
++ client->addr = 0x71;
++ if (i2c_master_recv(client, &buf, 1) == 1)
++ have_rx = 1;
++ dprintk("probe 0x71 @ %s: %s\n",
++ adap->name, have_rx ? "success" : "failed");
++ }
++
++ if (!(have_rx || have_tx)) {
++ zilog_error("%s: no devices found\n", adap->name);
++ goto out_nodev;
++ }
++
++ printk(KERN_INFO "lirc_zilog: chip found with %s\n",
++ have_rx && have_tx ? "RX and TX" :
++ have_rx ? "RX only" : "TX only");
++
++ ir = kzalloc(sizeof(struct IR), GFP_KERNEL);
++
++ if (!ir)
++ goto out_nomem;
++
++ ret = lirc_buffer_init(&ir->buf, 2, BUFLEN / 2);
++ if (ret)
++ goto out_nomem;
++
++ mutex_init(&ir->ir_lock);
++ mutex_init(&ir->buf_lock);
++ ir->need_boot = 1;
++
++ memcpy(&ir->l, &lirc_template, sizeof(struct lirc_driver));
++ ir->l.minor = -1;
++
++ /* I2C attach to device */
++ i2c_set_clientdata(client, ir);
++
++ /* initialise RX device */
++ if (have_rx) {
++ DECLARE_COMPLETION(tn);
++ memcpy(&ir->c_rx, client, sizeof(struct i2c_client));
++
++ ir->c_rx.addr = 0x71;
++ strlcpy(ir->c_rx.name, ZILOG_HAUPPAUGE_IR_RX_NAME,
++ I2C_NAME_SIZE);
++
++ /* try to fire up polling thread */
++ ir->t_notify = &tn;
++ ir->task = kthread_run(lirc_thread, ir, "lirc_zilog");
++ if (IS_ERR(ir->task)) {
++ ret = PTR_ERR(ir->task);
++ zilog_error("lirc_register_driver: cannot run "
++ "poll thread %d\n", ret);
++ goto err;
++ }
++ wait_for_completion(&tn);
++ ir->t_notify = NULL;
++ ir->have_rx = 1;
++ }
++
++ /* initialise TX device */
++ if (have_tx) {
++ memcpy(&ir->c_tx, client, sizeof(struct i2c_client));
++ ir->c_tx.addr = 0x70;
++ strlcpy(ir->c_tx.name, ZILOG_HAUPPAUGE_IR_TX_NAME,
++ I2C_NAME_SIZE);
++ ir->have_tx = 1;
++ }
++
++ /* set lirc_dev stuff */
++ ir->l.code_length = 13;
++ ir->l.rbuf = &ir->buf;
++ ir->l.fops = &lirc_fops;
++ ir->l.data = ir;
++ ir->l.minor = minor;
++ ir->l.dev = &adap->dev;
++ ir->l.sample_rate = 0;
++
++ /* register with lirc */
++ ir->l.minor = lirc_register_driver(&ir->l);
++ if (ir->l.minor < 0 || ir->l.minor >= MAX_IRCTL_DEVICES) {
++ zilog_error("ir_attach: \"minor\" must be between 0 and %d "
++ "(%d)!\n", MAX_IRCTL_DEVICES-1, ir->l.minor);
++ ret = -EBADRQC;
++ goto err;
++ }
++
++ /* store this for getting back in open() later on */
++ ir_devices[ir->l.minor] = ir;
++
++ /*
++ * if we have the tx device, load the 'firmware'. We do this
++ * after registering with lirc as otherwise hotplug seems to take
++ * 10s to create the lirc device.
++ */
++ if (have_tx) {
++ /* Special TX init */
++ ret = tx_init(ir);
++ if (ret != 0)
++ goto err;
++ }
++
++ return 0;
++
++err:
++ /* undo everything, hopefully... */
++ if (ir->c_rx.addr)
++ ir_remove(&ir->c_rx);
++ if (ir->c_tx.addr)
++ ir_remove(&ir->c_tx);
++ return ret;
++
++out_nodev:
++ zilog_error("no device found\n");
++ return -ENODEV;
++
++out_nomem:
++ zilog_error("memory allocation failure\n");
++ kfree(ir);
++ return -ENOMEM;
++}
++
++static int ir_command(struct i2c_client *client, unsigned int cmd, void *arg)
++{
++ /* nothing */
++ return 0;
++}
++
++static int __init zilog_init(void)
++{
++ int ret;
++
++ zilog_notify("Zilog/Hauppauge IR driver initializing\n");
++
++ mutex_init(&tx_data_lock);
++
++ request_module("firmware_class");
++
++ ret = i2c_add_driver(&driver);
++ if (ret)
++ zilog_error("initialization failed\n");
++ else
++ zilog_notify("initialization complete\n");
++
++ return ret;
++}
++
++static void __exit zilog_exit(void)
++{
++ i2c_del_driver(&driver);
++ /* if loaded */
++ fw_unload();
++ zilog_notify("Zilog/Hauppauge IR driver unloaded\n");
++}
++
++module_init(zilog_init);
++module_exit(zilog_exit);
++
++MODULE_DESCRIPTION("Zilog/Hauppauge infrared transmitter driver (i2c stack)");
++MODULE_AUTHOR("Gerd Knorr, Michal Kochanowicz, Christoph Bartelmus, "
++ "Ulrich Mueller, Stefan Jahn, Jerome Brock, Mark Weaver");
++MODULE_LICENSE("GPL");
++/* for compat with old name, which isn't all that accurate anymore */
++MODULE_ALIAS("lirc_pvr150");
++
++module_param(minor, int, 0444);
++MODULE_PARM_DESC(minor, "Preferred minor device number");
++
++module_param(debug, bool, 0644);
++MODULE_PARM_DESC(debug, "Enable debugging messages");
++
++module_param(disable_rx, bool, 0644);
++MODULE_PARM_DESC(disable_rx, "Disable the IR receiver device");
++
++module_param(disable_tx, bool, 0644);
++MODULE_PARM_DESC(disable_tx, "Disable the IR transmitter device");
+diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
+index a9bb254..2c06984 100644
+--- a/drivers/input/misc/Kconfig
++++ b/drivers/input/misc/Kconfig
+@@ -317,4 +317,15 @@ config INPUT_PCAP
+ To compile this driver as a module, choose M here: the
+ module will be called pcap_keys.
+
++config INPUT_IMON
++ tristate "SoundGraph iMON Receiver and Display"
++ depends on USB_ARCH_HAS_HCD
++ select USB
++ help
++ Say Y here if you want to use a SoundGraph iMON (aka Antec Veris)
++ IR Receiver and/or LCD/VFD/VGA display.
++
++ To compile this driver as a module, choose M here: the
++ module will be called imon.
++
+ endif
+diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
+index a8b8485..79358ff 100644
+--- a/drivers/input/misc/Makefile
++++ b/drivers/input/misc/Makefile
+@@ -13,6 +13,7 @@ obj-$(CONFIG_INPUT_CM109) += cm109.o
+ obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o
+ obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o
+ obj-$(CONFIG_HP_SDC_RTC) += hp_sdc_rtc.o
++obj-$(CONFIG_INPUT_IMON) += imon.o
+ obj-$(CONFIG_INPUT_IXP4XX_BEEPER) += ixp4xx-beeper.o
+ obj-$(CONFIG_INPUT_KEYSPAN_REMOTE) += keyspan_remote.o
+ obj-$(CONFIG_INPUT_M68K_BEEP) += m68kspkr.o
+diff --git a/drivers/input/misc/imon.c b/drivers/input/misc/imon.c
+new file mode 100644
+index 0000000..58a2130
+--- /dev/null
++++ b/drivers/input/misc/imon.c
+@@ -0,0 +1,2523 @@
++/*
++ * imon.c: input and display driver for SoundGraph iMON IR/VFD/LCD
++ *
++ * Copyright(C) 2009 Jarod Wilson <jarod@wilsonet.com>
++ * Portions based on the original lirc_imon driver,
++ * Copyright(C) 2004 Venky Raju(dev@venky.ws)
++ *
++ * imon 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 <linux/errno.h>
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/module.h>
++#include <linux/slab.h>
++#include <linux/uaccess.h>
++#include <linux/input.h>
++#include <linux/input/sparse-keymap.h>
++#include <linux/usb.h>
++#include <linux/usb/input.h>
++#include <linux/time.h>
++#include <linux/timer.h>
++
++#define MOD_AUTHOR "Jarod Wilson <jarod@wilsonet.com>"
++#define MOD_DESC "Driver for SoundGraph iMON MultiMedia IR/Display"
++#define MOD_NAME "imon"
++#define MOD_VERSION "0.8"
++
++#define DISPLAY_MINOR_BASE 144
++#define DEVICE_NAME "lcd%d"
++
++#define BUF_CHUNK_SIZE 8
++#define BUF_SIZE 128
++
++#define BIT_DURATION 250 /* each bit received is 250us */
++
++#define IMON_CLOCK_ENABLE_PACKETS 2
++#define IMON_KEY_RELEASE_OFFSET 1000
++
++/*** P R O T O T Y P E S ***/
++
++/* USB Callback prototypes */
++static int imon_probe(struct usb_interface *interface,
++ const struct usb_device_id *id);
++static void imon_disconnect(struct usb_interface *interface);
++static void usb_rx_callback_intf0(struct urb *urb);
++static void usb_rx_callback_intf1(struct urb *urb);
++static void usb_tx_callback(struct urb *urb);
++
++/* suspend/resume support */
++static int imon_resume(struct usb_interface *intf);
++static int imon_suspend(struct usb_interface *intf, pm_message_t message);
++
++/* Display file_operations function prototypes */
++static int display_open(struct inode *inode, struct file *file);
++static int display_close(struct inode *inode, struct file *file);
++
++/* VFD write operation */
++static ssize_t vfd_write(struct file *file, const char *buf,
++ size_t n_bytes, loff_t *pos);
++
++/* LCD file_operations override function prototypes */
++static ssize_t lcd_write(struct file *file, const char *buf,
++ size_t n_bytes, loff_t *pos);
++
++/*** G L O B A L S ***/
++
++struct imon_context {
++ struct device *dev;
++ struct usb_device *usbdev_intf0;
++ /* Newer devices have two interfaces */
++ struct usb_device *usbdev_intf1;
++ bool display_supported; /* not all controllers do */
++ bool display_isopen; /* display port has been opened */
++ bool ir_isassociating; /* IR port open for association */
++ bool dev_present_intf0; /* USB device presence, interface 0 */
++ bool dev_present_intf1; /* USB device presence, interface 1 */
++ struct mutex lock; /* to lock this object */
++ wait_queue_head_t remove_ok; /* For unexpected USB disconnects */
++
++ struct usb_endpoint_descriptor *rx_endpoint_intf0;
++ struct usb_endpoint_descriptor *rx_endpoint_intf1;
++ struct usb_endpoint_descriptor *tx_endpoint;
++ struct urb *rx_urb_intf0;
++ struct urb *rx_urb_intf1;
++ struct urb *tx_urb;
++ bool tx_control;
++ unsigned char usb_rx_buf[8];
++ unsigned char usb_tx_buf[8];
++
++ struct tx_t {
++ unsigned char data_buf[35]; /* user data buffer */
++ struct completion finished; /* wait for write to finish */
++ bool busy; /* write in progress */
++ int status; /* status of tx completion */
++ } tx;
++
++ u16 vendor; /* usb vendor ID */
++ u16 product; /* usb product ID */
++ int ir_protocol; /* iMON or MCE (RC6) IR protocol? */
++ struct input_dev *idev; /* input device for remote */
++ struct input_dev *touch; /* input device for touchscreen */
++ int ki; /* current input keycode key index */
++ u16 kc; /* current input keycode */
++ u16 last_keycode; /* last reported input keycode */
++ u8 mce_toggle_bit; /* last mce toggle bit */
++ int display_type; /* store the display type */
++ bool pad_mouse; /* toggle kbd(0)/mouse(1) mode */
++ int touch_x; /* x coordinate on touchscreen */
++ int touch_y; /* y coordinate on touchscreen */
++ char name_idev[128]; /* input device name */
++ char phys_idev[64]; /* input device phys path */
++ struct timer_list itimer; /* input device timer, need for rc6 */
++ char name_touch[128]; /* touch screen name */
++ char phys_touch[64]; /* touch screen phys path */
++ struct timer_list ttimer; /* touch screen timer */
++};
++
++#define TOUCH_TIMEOUT (HZ/30)
++#define MCE_TIMEOUT_MS 200
++
++/* vfd character device file operations */
++static const struct file_operations vfd_fops = {
++ .owner = THIS_MODULE,
++ .open = &display_open,
++ .write = &vfd_write,
++ .release = &display_close
++};
++
++/* lcd character device file operations */
++static const struct file_operations lcd_fops = {
++ .owner = THIS_MODULE,
++ .open = &display_open,
++ .write = &lcd_write,
++ .release = &display_close
++};
++
++enum {
++ IMON_DISPLAY_TYPE_AUTO = 0,
++ IMON_DISPLAY_TYPE_VFD = 1,
++ IMON_DISPLAY_TYPE_LCD = 2,
++ IMON_DISPLAY_TYPE_VGA = 3,
++ IMON_DISPLAY_TYPE_NONE = 4,
++};
++
++enum {
++ IMON_IR_PROTOCOL_IMON = 0,
++ IMON_IR_PROTOCOL_MCE = 1,
++ IMON_IR_PROTOCOL_IMON_NOPAD = 2,
++};
++
++enum {
++ IMON_BUTTON_IMON = 0,
++ IMON_BUTTON_MCE = 1,
++ IMON_BUTTON_PANEL = 2,
++};
++
++/*
++ * USB Device ID for iMON USB Control Boards
++ *
++ * The Windows drivers contain 6 different inf files, more or less one for
++ * each new device until the 0x0034-0x0046 devices, which all use the same
++ * driver. Some of the devices in the 34-46 range haven't been definitively
++ * identified yet. Early devices have either a TriGem Computer, Inc. or a
++ * Samsung vendor ID (0x0aa8 and 0x04e8 respectively), while all later
++ * devices use the SoundGraph vendor ID (0x15c2). This driver only supports
++ * the ffdc and later devices, which do onboard decoding.
++ */
++static struct usb_device_id imon_usb_id_table[] = {
++ /*
++ * Several devices with this same device ID, all use iMON_PAD.inf
++ * SoundGraph iMON PAD (IR & VFD)
++ * SoundGraph iMON PAD (IR & LCD)
++ * SoundGraph iMON Knob (IR only)
++ */
++ { USB_DEVICE(0x15c2, 0xffdc) },
++
++ /*
++ * Newer devices, all driven by the latest iMON Windows driver, full
++ * list of device IDs extracted via 'strings Setup/data1.hdr |grep 15c2'
++ * Need user input to fill in details on unknown devices.
++ */
++ /* SoundGraph iMON OEM Touch LCD (IR & 7" VGA LCD) */
++ { USB_DEVICE(0x15c2, 0x0034) },
++ /* SoundGraph iMON OEM Touch LCD (IR & 4.3" VGA LCD) */
++ { USB_DEVICE(0x15c2, 0x0035) },
++ /* SoundGraph iMON OEM VFD (IR & VFD) */
++ { USB_DEVICE(0x15c2, 0x0036) },
++ /* device specifics unknown */
++ { USB_DEVICE(0x15c2, 0x0037) },
++ /* SoundGraph iMON OEM LCD (IR & LCD) */
++ { USB_DEVICE(0x15c2, 0x0038) },
++ /* SoundGraph iMON UltraBay (IR & LCD) */
++ { USB_DEVICE(0x15c2, 0x0039) },
++ /* device specifics unknown */
++ { USB_DEVICE(0x15c2, 0x003a) },
++ /* device specifics unknown */
++ { USB_DEVICE(0x15c2, 0x003b) },
++ /* SoundGraph iMON OEM Inside (IR only) */
++ { USB_DEVICE(0x15c2, 0x003c) },
++ /* device specifics unknown */
++ { USB_DEVICE(0x15c2, 0x003d) },
++ /* device specifics unknown */
++ { USB_DEVICE(0x15c2, 0x003e) },
++ /* device specifics unknown */
++ { USB_DEVICE(0x15c2, 0x003f) },
++ /* device specifics unknown */
++ { USB_DEVICE(0x15c2, 0x0040) },
++ /* SoundGraph iMON MINI (IR only) */
++ { USB_DEVICE(0x15c2, 0x0041) },
++ /* Antec Veris Multimedia Station EZ External (IR only) */
++ { USB_DEVICE(0x15c2, 0x0042) },
++ /* Antec Veris Multimedia Station Basic Internal (IR only) */
++ { USB_DEVICE(0x15c2, 0x0043) },
++ /* Antec Veris Multimedia Station Elite (IR & VFD) */
++ { USB_DEVICE(0x15c2, 0x0044) },
++ /* Antec Veris Multimedia Station Premiere (IR & LCD) */
++ { USB_DEVICE(0x15c2, 0x0045) },
++ /* device specifics unknown */
++ { USB_DEVICE(0x15c2, 0x0046) },
++ {}
++};
++
++/* iMON LCD models use a different write op */
++static struct usb_device_id lcd_device_list[] = {
++ { USB_DEVICE(0x15c2, 0xffdc) },
++ { USB_DEVICE(0x15c2, 0x0038) },
++ { USB_DEVICE(0x15c2, 0x0039) },
++ { USB_DEVICE(0x15c2, 0x0045) },
++ {}
++};
++
++/* Some iMON devices have no lcd/vfd, don't set one up */
++static struct usb_device_id ir_only_list[] = {
++ /* the first imon lcd and the knob share this device id. :\ */
++ /*{ USB_DEVICE(0x15c2, 0xffdc) },*/
++ { USB_DEVICE(0x15c2, 0x003c) },
++ { USB_DEVICE(0x15c2, 0x0041) },
++ { USB_DEVICE(0x15c2, 0x0042) },
++ { USB_DEVICE(0x15c2, 0x0043) },
++ {}
++};
++
++/* iMON devices with VGA touchscreens */
++static struct usb_device_id imon_touchscreen_list[] = {
++ { USB_DEVICE(0x15c2, 0x0034) },
++ { USB_DEVICE(0x15c2, 0x0035) },
++ {}
++};
++
++/* USB Device data */
++static struct usb_driver imon_driver = {
++ .name = MOD_NAME,
++ .probe = imon_probe,
++ .disconnect = imon_disconnect,
++ .suspend = imon_suspend,
++ .resume = imon_resume,
++ .id_table = imon_usb_id_table,
++};
++
++static struct usb_class_driver imon_vfd_class = {
++ .name = DEVICE_NAME,
++ .fops = &vfd_fops,
++ .minor_base = DISPLAY_MINOR_BASE,
++};
++
++static struct usb_class_driver imon_lcd_class = {
++ .name = DEVICE_NAME,
++ .fops = &lcd_fops,
++ .minor_base = DISPLAY_MINOR_BASE,
++};
++
++/*
++ * standard imon remote key table, which isn't really entirely
++ * "standard", as different receivers decode the same key on the
++ * same remote to different hex codes... ugh.
++ */
++static const struct key_entry imon_remote_key_table[] = {
++ /* keys sorted mostly by frequency of use to optimize lookups */
++ { KE_KEY, 0x2a8195b7, { KEY_REWIND } },
++ { KE_KEY, 0x298315b7, { KEY_REWIND } },
++ { KE_KEY, 0x2b8115b7, { KEY_FASTFORWARD } },
++ { KE_KEY, 0x2b8315b7, { KEY_FASTFORWARD } },
++ { KE_KEY, 0x2b9115b7, { KEY_PREVIOUS } },
++ { KE_KEY, 0x298195b7, { KEY_NEXT } },
++
++ { KE_KEY, 0x2a8115b7, { KEY_PLAY } },
++ { KE_KEY, 0x2a8315b7, { KEY_PLAY } },
++ { KE_KEY, 0x2a9115b7, { KEY_PAUSE } },
++ { KE_KEY, 0x2b9715b7, { KEY_STOP } },
++ { KE_KEY, 0x298115b7, { KEY_RECORD } },
++
++ { KE_KEY, 0x01008000, { KEY_UP } },
++ { KE_KEY, 0x01007f00, { KEY_DOWN } },
++ { KE_KEY, 0x01000080, { KEY_LEFT } },
++ { KE_KEY, 0x0100007f, { KEY_RIGHT } },
++
++ { KE_KEY, 0x2aa515b7, { KEY_UP } },
++ { KE_KEY, 0x289515b7, { KEY_DOWN } },
++ { KE_KEY, 0x29a515b7, { KEY_LEFT } },
++ { KE_KEY, 0x2ba515b7, { KEY_RIGHT } },
++
++ { KE_KEY, 0x0200002c, { KEY_SPACE } }, /* Select/Space */
++ { KE_KEY, 0x2a9315b7, { KEY_SPACE } }, /* Select/Space */
++ { KE_KEY, 0x02000028, { KEY_ENTER } },
++ { KE_KEY, 0x28a195b7, { KEY_ENTER } },
++ { KE_KEY, 0x288195b7, { KEY_EXIT } },
++ { KE_KEY, 0x02000029, { KEY_ESC } },
++ { KE_KEY, 0x2bb715b7, { KEY_ESC } },
++ { KE_KEY, 0x0200002a, { KEY_BACKSPACE } },
++ { KE_KEY, 0x28a115b7, { KEY_BACKSPACE } },
++
++ { KE_KEY, 0x2b9595b7, { KEY_MUTE } },
++ { KE_KEY, 0x28a395b7, { KEY_VOLUMEUP } },
++ { KE_KEY, 0x28a595b7, { KEY_VOLUMEDOWN } },
++ { KE_KEY, 0x289395b7, { KEY_CHANNELUP } },
++ { KE_KEY, 0x288795b7, { KEY_CHANNELDOWN } },
++
++ { KE_KEY, 0x0200001e, { KEY_NUMERIC_1 } },
++ { KE_KEY, 0x0200001f, { KEY_NUMERIC_2 } },
++ { KE_KEY, 0x02000020, { KEY_NUMERIC_3 } },
++ { KE_KEY, 0x02000021, { KEY_NUMERIC_4 } },
++ { KE_KEY, 0x02000022, { KEY_NUMERIC_5 } },
++ { KE_KEY, 0x02000023, { KEY_NUMERIC_6 } },
++ { KE_KEY, 0x02000024, { KEY_NUMERIC_7 } },
++ { KE_KEY, 0x02000025, { KEY_NUMERIC_8 } },
++ { KE_KEY, 0x02000026, { KEY_NUMERIC_9 } },
++ { KE_KEY, 0x02000027, { KEY_NUMERIC_0 } },
++
++ { KE_KEY, 0x28b595b7, { KEY_NUMERIC_1 } },
++ { KE_KEY, 0x2bb195b7, { KEY_NUMERIC_2 } },
++ { KE_KEY, 0x28b195b7, { KEY_NUMERIC_3 } },
++ { KE_KEY, 0x2a8595b7, { KEY_NUMERIC_4 } },
++ { KE_KEY, 0x299595b7, { KEY_NUMERIC_5 } },
++ { KE_KEY, 0x2aa595b7, { KEY_NUMERIC_6 } },
++ { KE_KEY, 0x2b9395b7, { KEY_NUMERIC_7 } },
++ { KE_KEY, 0x2a8515b7, { KEY_NUMERIC_8 } },
++ { KE_KEY, 0x2aa115b7, { KEY_NUMERIC_9 } },
++ { KE_KEY, 0x2ba595b7, { KEY_NUMERIC_0 } },
++
++ { KE_KEY, 0x02200025, { KEY_NUMERIC_STAR } },
++ { KE_KEY, 0x28b515b7, { KEY_NUMERIC_STAR } },
++ { KE_KEY, 0x02200020, { KEY_NUMERIC_POUND } },
++ { KE_KEY, 0x29a115b7, { KEY_NUMERIC_POUND } },
++
++ { KE_KEY, 0x2b8515b7, { KEY_VIDEO } },
++ { KE_KEY, 0x299195b7, { KEY_AUDIO } },
++ { KE_KEY, 0x2ba115b7, { KEY_CAMERA } },
++ { KE_KEY, 0x28a515b7, { KEY_TV } },
++ { KE_KEY, 0x29a395b7, { KEY_DVD } },
++ { KE_KEY, 0x29a295b7, { KEY_DVD } },
++
++ /* the Menu key between DVD and Subtitle on the RM-200... */
++ { KE_KEY, 0x2ba385b7, { KEY_MENU } },
++ { KE_KEY, 0x2ba395b7, { KEY_MENU } },
++
++ { KE_KEY, 0x288515b7, { KEY_BOOKMARKS } },
++ { KE_KEY, 0x2ab715b7, { KEY_MEDIA } }, /* Thumbnail */
++ { KE_KEY, 0x298595b7, { KEY_SUBTITLE } },
++ { KE_KEY, 0x2b8595b7, { KEY_LANGUAGE } },
++
++ { KE_KEY, 0x29a595b7, { KEY_ZOOM } },
++ { KE_KEY, 0x2aa395b7, { KEY_SCREEN } }, /* FullScreen */
++
++ { KE_KEY, 0x299115b7, { KEY_KEYBOARD } },
++ { KE_KEY, 0x299135b7, { KEY_KEYBOARD } },
++
++ { KE_KEY, 0x01010000, { BTN_LEFT } },
++ { KE_KEY, 0x01020000, { BTN_RIGHT } },
++ { KE_KEY, 0x01010080, { BTN_LEFT } },
++ { KE_KEY, 0x01020080, { BTN_RIGHT } },
++ { KE_KEY, 0x688301b7, { BTN_LEFT } },
++ { KE_KEY, 0x688481b7, { BTN_RIGHT } },
++
++ { KE_KEY, 0x2a9395b7, { KEY_CYCLEWINDOWS } }, /* TaskSwitcher */
++ { KE_KEY, 0x2b8395b7, { KEY_TIME } }, /* Timer */
++
++ { KE_KEY, 0x289115b7, { KEY_POWER } },
++ { KE_KEY, 0x29b195b7, { KEY_EJECTCD } }, /* the one next to play */
++ { KE_KEY, 0x299395b7, { KEY_EJECTCLOSECD } }, /* eject (by TaskSw) */
++
++ { KE_KEY, 0x02800000, { KEY_CONTEXT_MENU } }, /* Left Menu */
++ { KE_KEY, 0x2b8195b7, { KEY_CONTEXT_MENU } }, /* Left Menu*/
++ { KE_KEY, 0x02000065, { KEY_COMPOSE } }, /* RightMenu */
++ { KE_KEY, 0x28b715b7, { KEY_COMPOSE } }, /* RightMenu */
++ { KE_KEY, 0x2ab195b7, { KEY_PROG1 } }, /* Go or MultiMon */
++ { KE_KEY, 0x29b715b7, { KEY_DASHBOARD } }, /* AppLauncher */
++ { KE_END, 0 }
++};
++
++/* mce-mode imon mce remote key table */
++static const struct key_entry imon_mce_key_table[] = {
++ /* keys sorted mostly by frequency of use to optimize lookups */
++ { KE_KEY, 0x800ff415, { KEY_REWIND } },
++ { KE_KEY, 0x800ff414, { KEY_FASTFORWARD } },
++ { KE_KEY, 0x800ff41b, { KEY_PREVIOUS } },
++ { KE_KEY, 0x800ff41a, { KEY_NEXT } },
++
++ { KE_KEY, 0x800ff416, { KEY_PLAY } },
++ { KE_KEY, 0x800ff418, { KEY_PAUSE } },
++ { KE_KEY, 0x800ff419, { KEY_STOP } },
++ { KE_KEY, 0x800ff417, { KEY_RECORD } },
++
++ { KE_KEY, 0x02000052, { KEY_UP } },
++ { KE_KEY, 0x02000051, { KEY_DOWN } },
++ { KE_KEY, 0x02000050, { KEY_LEFT } },
++ { KE_KEY, 0x0200004f, { KEY_RIGHT } },
++
++ { KE_KEY, 0x800ff41e, { KEY_UP } },
++ { KE_KEY, 0x800ff41f, { KEY_DOWN } },
++ { KE_KEY, 0x800ff420, { KEY_LEFT } },
++ { KE_KEY, 0x800ff421, { KEY_RIGHT } },
++
++ /* 0x800ff40b also KEY_NUMERIC_POUND on some receivers */
++ { KE_KEY, 0x800ff40b, { KEY_ENTER } },
++ { KE_KEY, 0x02000028, { KEY_ENTER } },
++/* the OK and Enter buttons decode to the same value on some remotes
++ { KE_KEY, 0x02000028, { KEY_OK } }, */
++ { KE_KEY, 0x800ff422, { KEY_OK } },
++ { KE_KEY, 0x0200002a, { KEY_EXIT } },
++ { KE_KEY, 0x800ff423, { KEY_EXIT } },
++ { KE_KEY, 0x02000029, { KEY_DELETE } },
++ /* 0x800ff40a also KEY_NUMERIC_STAR on some receivers */
++ { KE_KEY, 0x800ff40a, { KEY_DELETE } },
++
++ { KE_KEY, 0x800ff40e, { KEY_MUTE } },
++ { KE_KEY, 0x800ff410, { KEY_VOLUMEUP } },
++ { KE_KEY, 0x800ff411, { KEY_VOLUMEDOWN } },
++ { KE_KEY, 0x800ff412, { KEY_CHANNELUP } },
++ { KE_KEY, 0x800ff413, { KEY_CHANNELDOWN } },
++
++ { KE_KEY, 0x0200001e, { KEY_NUMERIC_1 } },
++ { KE_KEY, 0x0200001f, { KEY_NUMERIC_2 } },
++ { KE_KEY, 0x02000020, { KEY_NUMERIC_3 } },
++ { KE_KEY, 0x02000021, { KEY_NUMERIC_4 } },
++ { KE_KEY, 0x02000022, { KEY_NUMERIC_5 } },
++ { KE_KEY, 0x02000023, { KEY_NUMERIC_6 } },
++ { KE_KEY, 0x02000024, { KEY_NUMERIC_7 } },
++ { KE_KEY, 0x02000025, { KEY_NUMERIC_8 } },
++ { KE_KEY, 0x02000026, { KEY_NUMERIC_9 } },
++ { KE_KEY, 0x02000027, { KEY_NUMERIC_0 } },
++
++ { KE_KEY, 0x800ff401, { KEY_NUMERIC_1 } },
++ { KE_KEY, 0x800ff402, { KEY_NUMERIC_2 } },
++ { KE_KEY, 0x800ff403, { KEY_NUMERIC_3 } },
++ { KE_KEY, 0x800ff404, { KEY_NUMERIC_4 } },
++ { KE_KEY, 0x800ff405, { KEY_NUMERIC_5 } },
++ { KE_KEY, 0x800ff406, { KEY_NUMERIC_6 } },
++ { KE_KEY, 0x800ff407, { KEY_NUMERIC_7 } },
++ { KE_KEY, 0x800ff408, { KEY_NUMERIC_8 } },
++ { KE_KEY, 0x800ff409, { KEY_NUMERIC_9 } },
++ { KE_KEY, 0x800ff400, { KEY_NUMERIC_0 } },
++
++ { KE_KEY, 0x02200025, { KEY_NUMERIC_STAR } },
++ { KE_KEY, 0x02200020, { KEY_NUMERIC_POUND } },
++ /* 0x800ff41d also KEY_BLUE on some receivers */
++ { KE_KEY, 0x800ff41d, { KEY_NUMERIC_STAR } },
++ /* 0x800ff41c also KEY_PREVIOUS on some receivers */
++ { KE_KEY, 0x800ff41c, { KEY_NUMERIC_POUND } },
++
++ { KE_KEY, 0x800ff446, { KEY_TV } },
++ { KE_KEY, 0x800ff447, { KEY_AUDIO } }, /* My Music */
++ { KE_KEY, 0x800ff448, { KEY_PVR } }, /* RecordedTV */
++ { KE_KEY, 0x800ff449, { KEY_CAMERA } },
++ { KE_KEY, 0x800ff44a, { KEY_VIDEO } },
++ /* 0x800ff424 also KEY_MENU on some receivers */
++ { KE_KEY, 0x800ff424, { KEY_DVD } },
++ /* 0x800ff425 also KEY_GREEN on some receivers */
++ { KE_KEY, 0x800ff425, { KEY_TUNER } }, /* LiveTV */
++ { KE_KEY, 0x800ff450, { KEY_RADIO } },
++
++ { KE_KEY, 0x800ff44c, { KEY_LANGUAGE } },
++ { KE_KEY, 0x800ff427, { KEY_ZOOM } }, /* Aspect */
++
++ { KE_KEY, 0x800ff45b, { KEY_RED } },
++ { KE_KEY, 0x800ff45c, { KEY_GREEN } },
++ { KE_KEY, 0x800ff45d, { KEY_YELLOW } },
++ { KE_KEY, 0x800ff45e, { KEY_BLUE } },
++
++ { KE_KEY, 0x800ff466, { KEY_RED } },
++ /* { KE_KEY, 0x800ff425, { KEY_GREEN } }, */
++ { KE_KEY, 0x800ff468, { KEY_YELLOW } },
++ /* { KE_KEY, 0x800ff41d, { KEY_BLUE } }, */
++
++ { KE_KEY, 0x800ff40f, { KEY_INFO } },
++ { KE_KEY, 0x800ff426, { KEY_EPG } }, /* Guide */
++ { KE_KEY, 0x800ff45a, { KEY_SUBTITLE } }, /* Caption/Teletext */
++ { KE_KEY, 0x800ff44d, { KEY_TITLE } },
++
++ { KE_KEY, 0x800ff40c, { KEY_POWER } },
++ { KE_KEY, 0x800ff40d, { KEY_PROG1 } }, /* Windows MCE button */
++ { KE_END, 0 }
++
++};
++
++/* imon receiver front panel/knob key table */
++static const struct {
++ u64 hw_code;
++ u16 keycode;
++} imon_panel_key_table[] = {
++ { 0x000000000f00ffee, KEY_PROG1 }, /* Go */
++ { 0x000000001f00ffee, KEY_AUDIO },
++ { 0x000000002000ffee, KEY_VIDEO },
++ { 0x000000002100ffee, KEY_CAMERA },
++ { 0x000000002700ffee, KEY_DVD },
++ { 0x000000002300ffee, KEY_TV },
++ { 0x000000000500ffee, KEY_PREVIOUS },
++ { 0x000000000700ffee, KEY_REWIND },
++ { 0x000000000400ffee, KEY_STOP },
++ { 0x000000003c00ffee, KEY_PLAYPAUSE },
++ { 0x000000000800ffee, KEY_FASTFORWARD },
++ { 0x000000000600ffee, KEY_NEXT },
++ { 0x000000010000ffee, KEY_RIGHT },
++ { 0x000001000000ffee, KEY_LEFT },
++ { 0x000000003d00ffee, KEY_SELECT },
++ { 0x000100000000ffee, KEY_VOLUMEUP },
++ { 0x010000000000ffee, KEY_VOLUMEDOWN },
++ { 0x000000000100ffee, KEY_MUTE },
++ /* iMON Knob values */
++ { 0x000100ffffffffee, KEY_VOLUMEUP },
++ { 0x010000ffffffffee, KEY_VOLUMEDOWN },
++ { 0x000008ffffffffee, KEY_MUTE },
++};
++
++/* to prevent races between open() and disconnect(), probing, etc */
++static DEFINE_MUTEX(driver_lock);
++
++/* Module bookkeeping bits */
++MODULE_AUTHOR(MOD_AUTHOR);
++MODULE_DESCRIPTION(MOD_DESC);
++MODULE_VERSION(MOD_VERSION);
++MODULE_LICENSE("GPL");
++MODULE_DEVICE_TABLE(usb, imon_usb_id_table);
++
++static bool debug;
++module_param(debug, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(debug, "Debug messages: 0=no, 1=yes(default: no)");
++
++/* lcd, vfd, vga or none? should be auto-detected, but can be overridden... */
++static int display_type;
++module_param(display_type, int, S_IRUGO);
++MODULE_PARM_DESC(display_type, "Type of attached display. 0=autodetect, "
++ "1=vfd, 2=lcd, 3=vga, 4=none (default: autodetect)");
++
++/* IR protocol: native iMON, Windows MCE (RC-6), or iMON w/o PAD stabilize */
++static int ir_protocol;
++module_param(ir_protocol, int, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(ir_protocol, "Which IR protocol to use. 0=native iMON, "
++ "1=Windows Media Center Ed. (RC-6), 2=iMON w/o PAD stabilize "
++ "(default: native iMON)");
++
++/*
++ * In certain use cases, mouse mode isn't really helpful, and could actually
++ * cause confusion, so allow disabling it when the IR device is open.
++ */
++static bool nomouse;
++module_param(nomouse, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(nomouse, "Disable mouse input device mode when IR device is "
++ "open. 0=don't disable, 1=disable. (default: don't disable)");
++
++/* threshold at which a pad push registers as an arrow key in kbd mode */
++static int pad_thresh;
++module_param(pad_thresh, int, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(pad_thresh, "Threshold at which a pad push registers as an "
++ "arrow key in kbd mode (default: 28)");
++
++
++static void free_imon_context(struct imon_context *ictx)
++{
++ struct device *dev = ictx->dev;
++
++ usb_free_urb(ictx->tx_urb);
++ usb_free_urb(ictx->rx_urb_intf0);
++ usb_free_urb(ictx->rx_urb_intf1);
++ kfree(ictx);
++
++ dev_dbg(dev, "%s: iMON context freed\n", __func__);
++}
++
++/**
++ * Called when the Display device (e.g. /dev/lcd0)
++ * is opened by the application.
++ */
++static int display_open(struct inode *inode, struct file *file)
++{
++ struct usb_interface *interface;
++ struct imon_context *ictx = NULL;
++ int subminor;
++ int retval = 0;
++
++ /* prevent races with disconnect */
++ mutex_lock(&driver_lock);
++
++ subminor = iminor(inode);
++ interface = usb_find_interface(&imon_driver, subminor);
++ if (!interface) {
++ err("%s: could not find interface for minor %d",
++ __func__, subminor);
++ retval = -ENODEV;
++ goto exit;
++ }
++ ictx = usb_get_intfdata(interface);
++
++ if (!ictx) {
++ err("%s: no context found for minor %d", __func__, subminor);
++ retval = -ENODEV;
++ goto exit;
++ }
++
++ mutex_lock(&ictx->lock);
++
++ if (!ictx->display_supported) {
++ err("%s: display not supported by device", __func__);
++ retval = -ENODEV;
++ } else if (ictx->display_isopen) {
++ err("%s: display port is already open", __func__);
++ retval = -EBUSY;
++ } else {
++ ictx->display_isopen = 1;
++ file->private_data = ictx;
++ dev_dbg(ictx->dev, "display port opened\n");
++ }
++
++ mutex_unlock(&ictx->lock);
++
++exit:
++ mutex_unlock(&driver_lock);
++ return retval;
++}
++
++/**
++ * Called when the display device (e.g. /dev/lcd0)
++ * is closed by the application.
++ */
++static int display_close(struct inode *inode, struct file *file)
++{
++ struct imon_context *ictx = NULL;
++ int retval = 0;
++
++ ictx = (struct imon_context *)file->private_data;
++
++ if (!ictx) {
++ err("%s: no context for device", __func__);
++ return -ENODEV;
++ }
++
++ mutex_lock(&ictx->lock);
++
++ if (!ictx->display_supported) {
++ err("%s: display not supported by device", __func__);
++ retval = -ENODEV;
++ } else if (!ictx->display_isopen) {
++ err("%s: display is not open", __func__);
++ retval = -EIO;
++ } else {
++ ictx->display_isopen = 0;
++ dev_dbg(ictx->dev, "display port closed\n");
++ if (!ictx->dev_present_intf0) {
++ /*
++ * Device disconnected before close and IR port is not
++ * open. If IR port is open, context will be deleted by
++ * ir_close.
++ */
++ mutex_unlock(&ictx->lock);
++ free_imon_context(ictx);
++ return retval;
++ }
++ }
++
++ mutex_unlock(&ictx->lock);
++ return retval;
++}
++
++/**
++ * Sends a packet to the device -- this function must be called
++ * with ictx->lock held.
++ */
++static int send_packet(struct imon_context *ictx)
++{
++ unsigned int pipe;
++ int interval = 0;
++ int retval = 0;
++ struct usb_ctrlrequest *control_req = NULL;
++
++ /* Check if we need to use control or interrupt urb */
++ if (!ictx->tx_control) {
++ pipe = usb_sndintpipe(ictx->usbdev_intf0,
++ ictx->tx_endpoint->bEndpointAddress);
++ interval = ictx->tx_endpoint->bInterval;
++
++ usb_fill_int_urb(ictx->tx_urb, ictx->usbdev_intf0, pipe,
++ ictx->usb_tx_buf,
++ sizeof(ictx->usb_tx_buf),
++ usb_tx_callback, ictx, interval);
++
++ ictx->tx_urb->actual_length = 0;
++ } else {
++ /* fill request into kmalloc'ed space: */
++ control_req = kmalloc(sizeof(struct usb_ctrlrequest),
++ GFP_KERNEL);
++ if (control_req == NULL)
++ return -ENOMEM;
++
++ /* setup packet is '21 09 0200 0001 0008' */
++ control_req->bRequestType = 0x21;
++ control_req->bRequest = 0x09;
++ control_req->wValue = cpu_to_le16(0x0200);
++ control_req->wIndex = cpu_to_le16(0x0001);
++ control_req->wLength = cpu_to_le16(0x0008);
++
++ /* control pipe is endpoint 0x00 */
++ pipe = usb_sndctrlpipe(ictx->usbdev_intf0, 0);
++
++ /* build the control urb */
++ usb_fill_control_urb(ictx->tx_urb, ictx->usbdev_intf0,
++ pipe, (unsigned char *)control_req,
++ ictx->usb_tx_buf,
++ sizeof(ictx->usb_tx_buf),
++ usb_tx_callback, ictx);
++ ictx->tx_urb->actual_length = 0;
++ }
++
++ init_completion(&ictx->tx.finished);
++ ictx->tx.busy = 1;
++ smp_rmb(); /* ensure later readers know we're busy */
++
++ retval = usb_submit_urb(ictx->tx_urb, GFP_KERNEL);
++ if (retval) {
++ ictx->tx.busy = 0;
++ smp_rmb(); /* ensure later readers know we're not busy */
++ err("%s: error submitting urb(%d)", __func__, retval);
++ } else {
++ /* Wait for transmission to complete (or abort) */
++ mutex_unlock(&ictx->lock);
++ retval = wait_for_completion_interruptible(
++ &ictx->tx.finished);
++ if (retval)
++ err("%s: task interrupted", __func__);
++ mutex_lock(&ictx->lock);
++
++ retval = ictx->tx.status;
++ if (retval)
++ err("%s: packet tx failed (%d)", __func__, retval);
++ }
++
++ kfree(control_req);
++
++ return retval;
++}
++
++/**
++ * Sends an associate packet to the iMON 2.4G.
++ *
++ * This might not be such a good idea, since it has an id collision with
++ * some versions of the "IR & VFD" combo. The only way to determine if it
++ * is an RF version is to look at the product description string. (Which
++ * we currently do not fetch).
++ */
++static int send_associate_24g(struct imon_context *ictx)
++{
++ int retval;
++ const unsigned char packet[8] = { 0x01, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x20 };
++
++ if (!ictx) {
++ err("%s: no context for device", __func__);
++ return -ENODEV;
++ }
++
++ if (!ictx->dev_present_intf0) {
++ err("%s: no iMON device present", __func__);
++ return -ENODEV;
++ }
++
++ memcpy(ictx->usb_tx_buf, packet, sizeof(packet));
++ retval = send_packet(ictx);
++
++ return retval;
++}
++
++/**
++ * Sends packets to setup and show clock on iMON display
++ *
++ * Arguments: year - last 2 digits of year, month - 1..12,
++ * day - 1..31, dow - day of the week (0-Sun...6-Sat),
++ * hour - 0..23, minute - 0..59, second - 0..59
++ */
++static int send_set_imon_clock(struct imon_context *ictx,
++ unsigned int year, unsigned int month,
++ unsigned int day, unsigned int dow,
++ unsigned int hour, unsigned int minute,
++ unsigned int second)
++{
++ unsigned char clock_enable_pkt[IMON_CLOCK_ENABLE_PACKETS][8];
++ int retval = 0;
++ int i;
++
++ if (!ictx) {
++ err("%s: no context for device", __func__);
++ return -ENODEV;
++ }
++
++ switch (ictx->display_type) {
++ case IMON_DISPLAY_TYPE_LCD:
++ clock_enable_pkt[0][0] = 0x80;
++ clock_enable_pkt[0][1] = year;
++ clock_enable_pkt[0][2] = month-1;
++ clock_enable_pkt[0][3] = day;
++ clock_enable_pkt[0][4] = hour;
++ clock_enable_pkt[0][5] = minute;
++ clock_enable_pkt[0][6] = second;
++
++ clock_enable_pkt[1][0] = 0x80;
++ clock_enable_pkt[1][1] = 0;
++ clock_enable_pkt[1][2] = 0;
++ clock_enable_pkt[1][3] = 0;
++ clock_enable_pkt[1][4] = 0;
++ clock_enable_pkt[1][5] = 0;
++ clock_enable_pkt[1][6] = 0;
++
++ if (ictx->product == 0xffdc) {
++ clock_enable_pkt[0][7] = 0x50;
++ clock_enable_pkt[1][7] = 0x51;
++ } else {
++ clock_enable_pkt[0][7] = 0x88;
++ clock_enable_pkt[1][7] = 0x8a;
++ }
++
++ break;
++
++ case IMON_DISPLAY_TYPE_VFD:
++ clock_enable_pkt[0][0] = year;
++ clock_enable_pkt[0][1] = month-1;
++ clock_enable_pkt[0][2] = day;
++ clock_enable_pkt[0][3] = dow;
++ clock_enable_pkt[0][4] = hour;
++ clock_enable_pkt[0][5] = minute;
++ clock_enable_pkt[0][6] = second;
++ clock_enable_pkt[0][7] = 0x40;
++
++ clock_enable_pkt[1][0] = 0;
++ clock_enable_pkt[1][1] = 0;
++ clock_enable_pkt[1][2] = 1;
++ clock_enable_pkt[1][3] = 0;
++ clock_enable_pkt[1][4] = 0;
++ clock_enable_pkt[1][5] = 0;
++ clock_enable_pkt[1][6] = 0;
++ clock_enable_pkt[1][7] = 0x42;
++
++ break;
++
++ default:
++ return -ENODEV;
++ }
++
++ for (i = 0; i < IMON_CLOCK_ENABLE_PACKETS; i++) {
++ memcpy(ictx->usb_tx_buf, clock_enable_pkt[i], 8);
++ retval = send_packet(ictx);
++ if (retval) {
++ err("%s: send_packet failed for packet %d",
++ __func__, i);
++ break;
++ }
++ }
++
++ return retval;
++}
++
++/**
++ * These are the sysfs functions to handle the association on the iMON 2.4G LT.
++ */
++static ssize_t show_associate_remote(struct device *d,
++ struct device_attribute *attr,
++ char *buf)
++{
++ struct imon_context *ictx = dev_get_drvdata(d);
++
++ if (!ictx)
++ return -ENODEV;
++
++ mutex_lock(&ictx->lock);
++ if (ictx->ir_isassociating)
++ strcpy(buf, "associating\n");
++ else
++ strcpy(buf, "closed\n");
++
++ dev_info(d, "Visit http://www.lirc.org/html/imon-24g.html for "
++ "instructions on how to associate your iMON 2.4G DT/LT "
++ "remote\n");
++ mutex_unlock(&ictx->lock);
++ return strlen(buf);
++}
++
++static ssize_t store_associate_remote(struct device *d,
++ struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct imon_context *ictx;
++
++ ictx = dev_get_drvdata(d);
++
++ if (!ictx)
++ return -ENODEV;
++
++ mutex_lock(&ictx->lock);
++ ictx->ir_isassociating = 1;
++ send_associate_24g(ictx);
++ mutex_unlock(&ictx->lock);
++
++ return count;
++}
++
++/**
++ * sysfs functions to control internal imon clock
++ */
++static ssize_t show_imon_clock(struct device *d,
++ struct device_attribute *attr, char *buf)
++{
++ struct imon_context *ictx = dev_get_drvdata(d);
++ size_t len;
++
++ if (!ictx)
++ return -ENODEV;
++
++ mutex_lock(&ictx->lock);
++
++ if (!ictx->display_supported) {
++ len = snprintf(buf, PAGE_SIZE, "Not supported.");
++ } else {
++ len = snprintf(buf, PAGE_SIZE,
++ "To set the clock on your iMON display:\n"
++ "# date \"+%%y %%m %%d %%w %%H %%M %%S\" > imon_clock\n"
++ "%s", ictx->display_isopen ?
++ "\nNOTE: imon device must be closed\n" : "");
++ }
++
++ mutex_unlock(&ictx->lock);
++
++ return len;
++}
++
++static ssize_t store_imon_clock(struct device *d,
++ struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct imon_context *ictx = dev_get_drvdata(d);
++ ssize_t retval;
++ unsigned int year, month, day, dow, hour, minute, second;
++
++ if (!ictx)
++ return -ENODEV;
++
++ mutex_lock(&ictx->lock);
++
++ if (!ictx->display_supported) {
++ retval = -ENODEV;
++ goto exit;
++ } else if (ictx->display_isopen) {
++ retval = -EBUSY;
++ goto exit;
++ }
++
++ if (sscanf(buf, "%u %u %u %u %u %u %u", &year, &month, &day, &dow,
++ &hour, &minute, &second) != 7) {
++ retval = -EINVAL;
++ goto exit;
++ }
++
++ if ((month < 1 || month > 12) ||
++ (day < 1 || day > 31) || (dow > 6) ||
++ (hour > 23) || (minute > 59) || (second > 59)) {
++ retval = -EINVAL;
++ goto exit;
++ }
++
++ retval = send_set_imon_clock(ictx, year, month, day, dow,
++ hour, minute, second);
++ if (retval)
++ goto exit;
++
++ retval = count;
++exit:
++ mutex_unlock(&ictx->lock);
++
++ return retval;
++}
++
++
++static DEVICE_ATTR(imon_clock, S_IWUSR | S_IRUGO, show_imon_clock,
++ store_imon_clock);
++
++static DEVICE_ATTR(associate_remote, S_IWUSR | S_IRUGO, show_associate_remote,
++ store_associate_remote);
++
++static struct attribute *imon_display_sysfs_entries[] = {
++ &dev_attr_imon_clock.attr,
++ NULL
++};
++
++static struct attribute_group imon_display_attribute_group = {
++ .attrs = imon_display_sysfs_entries
++};
++
++static struct attribute *imon_rf_sysfs_entries[] = {
++ &dev_attr_associate_remote.attr,
++ NULL
++};
++
++static struct attribute_group imon_rf_attribute_group = {
++ .attrs = imon_rf_sysfs_entries
++};
++
++/**
++ * Writes data to the VFD. The iMON VFD is 2x16 characters
++ * and requires data in 5 consecutive USB interrupt packets,
++ * each packet but the last carrying 7 bytes.
++ *
++ * I don't know if the VFD board supports features such as
++ * scrolling, clearing rows, blanking, etc. so at
++ * the caller must provide a full screen of data. If fewer
++ * than 32 bytes are provided spaces will be appended to
++ * generate a full screen.
++ */
++static ssize_t vfd_write(struct file *file, const char *buf,
++ size_t n_bytes, loff_t *pos)
++{
++ int i;
++ int offset;
++ int seq;
++ int retval = 0;
++ struct imon_context *ictx;
++ const unsigned char vfd_packet6[] = {
++ 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF };
++
++ ictx = (struct imon_context *)file->private_data;
++ if (!ictx) {
++ err("%s: no context for device", __func__);
++ return -ENODEV;
++ }
++
++ mutex_lock(&ictx->lock);
++
++ if (!ictx->dev_present_intf0) {
++ err("%s: no iMON device present", __func__);
++ retval = -ENODEV;
++ goto exit;
++ }
++
++ if (n_bytes <= 0 || n_bytes > 32) {
++ err("%s: invalid payload size", __func__);
++ retval = -EINVAL;
++ goto exit;
++ }
++
++ if (copy_from_user(ictx->tx.data_buf, buf, n_bytes)) {
++ retval = -EFAULT;
++ goto exit;
++ }
++
++ /* Pad with spaces */
++ for (i = n_bytes; i < 32; ++i)
++ ictx->tx.data_buf[i] = ' ';
++
++ for (i = 32; i < 35; ++i)
++ ictx->tx.data_buf[i] = 0xFF;
++
++ offset = 0;
++ seq = 0;
++
++ do {
++ memcpy(ictx->usb_tx_buf, ictx->tx.data_buf + offset, 7);
++ ictx->usb_tx_buf[7] = (unsigned char) seq;
++
++ retval = send_packet(ictx);
++ if (retval) {
++ err("%s: send packet failed for packet #%d",
++ __func__, seq/2);
++ goto exit;
++ } else {
++ seq += 2;
++ offset += 7;
++ }
++
++ } while (offset < 35);
++
++ /* Send packet #6 */
++ memcpy(ictx->usb_tx_buf, &vfd_packet6, sizeof(vfd_packet6));
++ ictx->usb_tx_buf[7] = (unsigned char) seq;
++ retval = send_packet(ictx);
++ if (retval)
++ err("%s: send packet failed for packet #%d",
++ __func__, seq / 2);
++
++exit:
++ mutex_unlock(&ictx->lock);
++
++ return (!retval) ? n_bytes : retval;
++}
++
++/**
++ * Writes data to the LCD. The iMON OEM LCD screen expects 8-byte
++ * packets. We accept data as 16 hexadecimal digits, followed by a
++ * newline (to make it easy to drive the device from a command-line
++ * -- even though the actual binary data is a bit complicated).
++ *
++ * The device itself is not a "traditional" text-mode display. It's
++ * actually a 16x96 pixel bitmap display. That means if you want to
++ * display text, you've got to have your own "font" and translate the
++ * text into bitmaps for display. This is really flexible (you can
++ * display whatever diacritics you need, and so on), but it's also
++ * a lot more complicated than most LCDs...
++ */
++static ssize_t lcd_write(struct file *file, const char *buf,
++ size_t n_bytes, loff_t *pos)
++{
++ int retval = 0;
++ struct imon_context *ictx;
++
++ ictx = (struct imon_context *)file->private_data;
++ if (!ictx) {
++ err("%s: no context for device", __func__);
++ return -ENODEV;
++ }
++
++ mutex_lock(&ictx->lock);
++
++ if (!ictx->display_supported) {
++ err("%s: no iMON display present", __func__);
++ retval = -ENODEV;
++ goto exit;
++ }
++
++ if (n_bytes != 8) {
++ err("%s: invalid payload size: %d (expecting 8)",
++ __func__, (int) n_bytes);
++ retval = -EINVAL;
++ goto exit;
++ }
++
++ if (copy_from_user(ictx->usb_tx_buf, buf, 8)) {
++ retval = -EFAULT;
++ goto exit;
++ }
++
++ retval = send_packet(ictx);
++ if (retval) {
++ err("%s: send packet failed!", __func__);
++ goto exit;
++ } else {
++ dev_dbg(ictx->dev, "%s: write %d bytes to LCD\n",
++ __func__, (int) n_bytes);
++ }
++exit:
++ mutex_unlock(&ictx->lock);
++ return (!retval) ? n_bytes : retval;
++}
++
++/**
++ * Callback function for USB core API: transmit data
++ */
++static void usb_tx_callback(struct urb *urb)
++{
++ struct imon_context *ictx;
++
++ if (!urb)
++ return;
++ ictx = (struct imon_context *)urb->context;
++ if (!ictx)
++ return;
++
++ ictx->tx.status = urb->status;
++
++ /* notify waiters that write has finished */
++ ictx->tx.busy = 0;
++ smp_rmb(); /* ensure later readers know we're not busy */
++ complete(&ictx->tx.finished);
++}
++
++/**
++ * mce/rc6 keypresses have no distinct release code, use timer
++ */
++static void imon_mce_timeout(unsigned long data)
++{
++ struct imon_context *ictx = (struct imon_context *)data;
++
++ input_report_key(ictx->idev, ictx->last_keycode, 0);
++ input_sync(ictx->idev);
++}
++
++/**
++ * report touchscreen input
++ */
++static void imon_touch_display_timeout(unsigned long data)
++{
++ struct imon_context *ictx = (struct imon_context *)data;
++
++ if (!ictx->display_type == IMON_DISPLAY_TYPE_VGA)
++ return;
++
++ input_report_abs(ictx->touch, ABS_X, ictx->touch_x);
++ input_report_abs(ictx->touch, ABS_Y, ictx->touch_y);
++ input_report_key(ictx->touch, BTN_TOUCH, 0x00);
++ input_sync(ictx->touch);
++}
++
++/**
++ * iMON IR receivers support two different signal sets -- those used by
++ * the iMON remotes, and those used by the Windows MCE remotes (which is
++ * really just RC-6), but only one or the other at a time, as the signals
++ * are decoded onboard the receiver.
++ */
++static void imon_set_ir_protocol(struct imon_context *ictx)
++{
++ int retval;
++ struct device *dev = ictx->dev;
++ unsigned char ir_proto_packet[] = {
++ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86 };
++
++ switch (ir_protocol) {
++ case IMON_IR_PROTOCOL_MCE:
++ dev_dbg(dev, "Configuring IR receiver for MCE protocol\n");
++ ir_proto_packet[0] = 0x01;
++ ictx->ir_protocol = IMON_IR_PROTOCOL_MCE;
++ ictx->pad_mouse = 0;
++ init_timer(&ictx->itimer);
++ ictx->itimer.data = (unsigned long)ictx;
++ ictx->itimer.function = imon_mce_timeout;
++ break;
++ case IMON_IR_PROTOCOL_IMON:
++ dev_dbg(dev, "Configuring IR receiver for iMON protocol\n");
++ /* ir_proto_packet[0] = 0x00; // already the default */
++ ictx->ir_protocol = IMON_IR_PROTOCOL_IMON;
++ ictx->pad_mouse = 1;
++ break;
++ case IMON_IR_PROTOCOL_IMON_NOPAD:
++ dev_dbg(dev, "Configuring IR receiver for iMON protocol "
++ "without PAD stabilize function enabled\n");
++ /* ir_proto_packet[0] = 0x00; // already the default */
++ ictx->ir_protocol = IMON_IR_PROTOCOL_IMON_NOPAD;
++ ictx->pad_mouse = 0;
++ break;
++ default:
++ dev_info(dev, "%s: unknown IR protocol specified, will "
++ "just default to iMON protocol\n", __func__);
++ ictx->ir_protocol = IMON_IR_PROTOCOL_IMON;
++ ictx->pad_mouse = 1;
++ break;
++ }
++
++ memcpy(ictx->usb_tx_buf, &ir_proto_packet, sizeof(ir_proto_packet));
++
++ retval = send_packet(ictx);
++ if (retval) {
++ dev_info(dev, "%s: failed to set IR protocol, falling back "
++ "to standard iMON protocol mode\n", __func__);
++ ir_protocol = IMON_IR_PROTOCOL_IMON;
++ ictx->ir_protocol = IMON_IR_PROTOCOL_IMON;
++ }
++}
++
++static inline int tv2int(const struct timeval *a, const struct timeval *b)
++{
++ int usecs = 0;
++ int sec = 0;
++
++ if (b->tv_usec > a->tv_usec) {
++ usecs = 1000000;
++ sec--;
++ }
++
++ usecs += a->tv_usec - b->tv_usec;
++
++ sec += a->tv_sec - b->tv_sec;
++ sec *= 1000;
++ usecs /= 1000;
++ sec += usecs;
++
++ if (sec < 0)
++ sec = 1000;
++
++ return sec;
++}
++
++/**
++ * The directional pad behaves a bit differently, depending on whether this is
++ * one of the older ffdc devices or a newer device. Newer devices appear to
++ * have a higher resolution matrix for more precise mouse movement, but it
++ * makes things overly sensitive in keyboard mode, so we do some interesting
++ * contortions to make it less touchy. Older devices run through the same
++ * routine with shorter timeout and a smaller threshold.
++ */
++static int stabilize(int a, int b, u16 timeout, u16 threshold)
++{
++ struct timeval ct;
++ static struct timeval prev_time = {0, 0};
++ static struct timeval hit_time = {0, 0};
++ static int x, y, prev_result, hits;
++ int result = 0;
++ int msec, msec_hit;
++
++ do_gettimeofday(&ct);
++ msec = tv2int(&ct, &prev_time);
++ msec_hit = tv2int(&ct, &hit_time);
++
++ if (msec > 100) {
++ x = 0;
++ y = 0;
++ hits = 0;
++ }
++
++ x += a;
++ y += b;
++
++ prev_time = ct;
++
++ if (abs(x) > threshold || abs(y) > threshold) {
++ if (abs(y) > abs(x))
++ result = (y > 0) ? 0x7F : 0x80;
++ else
++ result = (x > 0) ? 0x7F00 : 0x8000;
++
++ x = 0;
++ y = 0;
++
++ if (result == prev_result) {
++ hits++;
++
++ if (hits > 3) {
++ switch (result) {
++ case 0x7F:
++ y = 17 * threshold / 30;
++ break;
++ case 0x80:
++ y -= 17 * threshold / 30;
++ break;
++ case 0x7F00:
++ x = 17 * threshold / 30;
++ break;
++ case 0x8000:
++ x -= 17 * threshold / 30;
++ break;
++ }
++ }
++
++ if (hits == 2 && msec_hit < timeout) {
++ result = 0;
++ hits = 1;
++ }
++ } else {
++ prev_result = result;
++ hits = 1;
++ hit_time = ct;
++ }
++ }
++
++ return result;
++}
++
++static int imon_remote_key_lookup(u32 hw_code)
++{
++ int i;
++ u32 code = be32_to_cpu(hw_code);
++
++ /* Look for the initial press of a button */
++ for (i = 0; i < ARRAY_SIZE(imon_remote_key_table); i++)
++ if (imon_remote_key_table[i].code == code)
++ return i;
++
++ /* Look for the release of a button, return index + offset */
++ for (i = 0; i < ARRAY_SIZE(imon_remote_key_table); i++)
++ if ((imon_remote_key_table[i].code | 0x4000) == code)
++ return i + IMON_KEY_RELEASE_OFFSET;
++
++ return -1;
++}
++
++static int imon_mce_key_lookup(u32 hw_code)
++{
++ int i;
++ u32 code = be32_to_cpu(hw_code);
++
++#define MCE_KEY_MASK 0x7000
++#define MCE_TOGGLE_BIT 0x8000
++
++ /*
++ * On some receivers, mce keys decode to 0x8000f04xx and 0x8000f84xx
++ * (the toggle bit flipping between alternating key presses), while
++ * on other receivers, we see 0x8000f74xx and 0x8000ff4xx. To keep
++ * the table trim, we always or in the bits to look up 0x8000ff4xx,
++ * but we can't or them into all codes, as some keys are decoded in
++ * a different way w/o the same use of the toggle bit...
++ */
++ if ((code >> 24) & 0x80)
++ code = code | MCE_KEY_MASK | MCE_TOGGLE_BIT;
++
++ for (i = 0; i < ARRAY_SIZE(imon_mce_key_table); i++)
++ if (imon_mce_key_table[i].code == code)
++ return i;
++
++ return -1;
++}
++
++static int imon_panel_key_lookup(u64 hw_code)
++{
++ int i;
++ u64 code = be64_to_cpu(hw_code);
++
++ for (i = 0; i < ARRAY_SIZE(imon_panel_key_table); i++)
++ if (imon_panel_key_table[i].hw_code == (code | 0xffee))
++ return i;
++
++ return -1;
++}
++
++static bool imon_mouse_event(struct imon_context *ictx,
++ unsigned char *buf, int len)
++{
++ char rel_x = 0x00, rel_y = 0x00;
++ u8 right_shift = 1;
++ bool mouse_input = 1;
++ int dir = 0;
++
++ /* newer iMON device PAD or mouse button */
++ if (ictx->product != 0xffdc && (buf[0] & 0x01) && len == 5) {
++ rel_x = buf[2];
++ rel_y = buf[3];
++ right_shift = 1;
++ /* 0xffdc iMON PAD or mouse button input */
++ } else if (ictx->product == 0xffdc && (buf[0] & 0x40) &&
++ !((buf[1] & 0x01) || ((buf[1] >> 2) & 0x01))) {
++ rel_x = (buf[1] & 0x08) | (buf[1] & 0x10) >> 2 |
++ (buf[1] & 0x20) >> 4 | (buf[1] & 0x40) >> 6;
++ if (buf[0] & 0x02)
++ rel_x |= ~0x0f;
++ rel_x = rel_x + rel_x / 2;
++ rel_y = (buf[2] & 0x08) | (buf[2] & 0x10) >> 2 |
++ (buf[2] & 0x20) >> 4 | (buf[2] & 0x40) >> 6;
++ if (buf[0] & 0x01)
++ rel_y |= ~0x0f;
++ rel_y = rel_y + rel_y / 2;
++ right_shift = 2;
++ /* some ffdc devices decode mouse buttons differently... */
++ } else if (ictx->product == 0xffdc && (buf[0] == 0x68)) {
++ right_shift = 2;
++ /* ch+/- buttons, which we use for an emulated scroll wheel */
++ } else if (ictx->kc == KEY_CHANNELUP && (buf[2] & 0x40) != 0x40) {
++ dir = 1;
++ } else if (ictx->kc == KEY_CHANNELDOWN && (buf[2] & 0x40) != 0x40) {
++ dir = -1;
++ } else
++ mouse_input = 0;
++
++ if (mouse_input) {
++ dev_dbg(ictx->dev, "sending mouse data via input subsystem\n");
++
++ if (dir) {
++ input_report_rel(ictx->idev, REL_WHEEL, dir);
++ } else if (rel_x || rel_y) {
++ input_report_rel(ictx->idev, REL_X, rel_x);
++ input_report_rel(ictx->idev, REL_Y, rel_y);
++ } else {
++ input_report_key(ictx->idev, BTN_LEFT, buf[1] & 0x1);
++ input_report_key(ictx->idev, BTN_RIGHT,
++ buf[1] >> right_shift & 0x1);
++ }
++ input_sync(ictx->idev);
++ ictx->last_keycode = ictx->kc;
++ }
++
++ return mouse_input;
++}
++
++static void imon_touch_event(struct imon_context *ictx, unsigned char *buf)
++{
++ mod_timer(&ictx->ttimer, jiffies + TOUCH_TIMEOUT);
++ ictx->touch_x = (buf[0] << 4) | (buf[1] >> 4);
++ ictx->touch_y = 0xfff - ((buf[2] << 4) | (buf[1] & 0xf));
++ input_report_abs(ictx->touch, ABS_X, ictx->touch_x);
++ input_report_abs(ictx->touch, ABS_Y, ictx->touch_y);
++ input_report_key(ictx->touch, BTN_TOUCH, 0x01);
++ input_sync(ictx->touch);
++}
++
++static void imon_pad_to_keys(struct imon_context *ictx, unsigned char *buf)
++{
++ int ki = 1;
++ int dir = 0;
++ int offset = IMON_KEY_RELEASE_OFFSET;
++ char rel_x = 0x00, rel_y = 0x00;
++ u16 timeout, threshold;
++ u64 temp_key;
++ u32 remote_key;
++
++ /*
++ * The imon directional pad functions more like a touchpad. Bytes 3 & 4
++ * contain a position coordinate (x,y), with each component ranging
++ * from -14 to 14. We want to down-sample this to only 4 discrete values
++ * for up/down/left/right arrow keys. Also, when you get too close to
++ * diagonals, it has a tendancy to jump back and forth, so lets try to
++ * ignore when they get too close.
++ */
++ if (ictx->product != 0xffdc) {
++ /* first, pad to 8 bytes so it conforms with everything else */
++ buf[5] = buf[6] = buf[7] = 0;
++ timeout = 500; /* in msecs */
++ /* (2*threshold) x (2*threshold) square */
++ threshold = pad_thresh ? pad_thresh : 28;
++ rel_x = buf[2];
++ rel_y = buf[3];
++
++ if (ictx->ir_protocol == IMON_IR_PROTOCOL_IMON) {
++ if ((buf[1] == 0) && ((rel_x != 0) || (rel_y != 0))) {
++ dir = stabilize((int)rel_x, (int)rel_y,
++ timeout, threshold);
++ if (!dir) {
++ ictx->kc = KEY_UNKNOWN;
++ return;
++ }
++ buf[2] = dir & 0xFF;
++ buf[3] = (dir >> 8) & 0xFF;
++ memcpy(&temp_key, buf, sizeof(temp_key));
++ remote_key = (u32) (le64_to_cpu(temp_key)
++ & 0xffffffff);
++ ki = imon_remote_key_lookup(remote_key);
++ ictx->kc =
++ imon_remote_key_table[ki % offset].keycode;
++ }
++ } else {
++ if (abs(rel_y) > abs(rel_x)) {
++ buf[2] = (rel_y > 0) ? 0x7F : 0x80;
++ buf[3] = 0;
++ ictx->kc = (rel_y > 0) ? KEY_DOWN : KEY_UP;
++ } else {
++ buf[2] = 0;
++ buf[3] = (rel_x > 0) ? 0x7F : 0x80;
++ ictx->kc = (rel_x > 0) ? KEY_RIGHT : KEY_LEFT;
++ }
++ }
++
++ /*
++ * Handle on-board decoded pad events for e.g. older VFD/iMON-Pad
++ * device (15c2:ffdc). The remote generates various codes from
++ * 0x68nnnnB7 to 0x6AnnnnB7, the left mouse button generates
++ * 0x688301b7 and the right one 0x688481b7. All other keys generate
++ * 0x2nnnnnnn. Position coordinate is encoded in buf[1] and buf[2] with
++ * reversed endianess. Extract direction from buffer, rotate endianess,
++ * adjust sign and feed the values into stabilize(). The resulting codes
++ * will be 0x01008000, 0x01007F00, which match the newer devices.
++ */
++ } else {
++ timeout = 10; /* in msecs */
++ /* (2*threshold) x (2*threshold) square */
++ threshold = pad_thresh ? pad_thresh : 15;
++
++ /* buf[1] is x */
++ rel_x = (buf[1] & 0x08) | (buf[1] & 0x10) >> 2 |
++ (buf[1] & 0x20) >> 4 | (buf[1] & 0x40) >> 6;
++ if (buf[0] & 0x02)
++ rel_x |= ~0x10+1;
++ /* buf[2] is y */
++ rel_y = (buf[2] & 0x08) | (buf[2] & 0x10) >> 2 |
++ (buf[2] & 0x20) >> 4 | (buf[2] & 0x40) >> 6;
++ if (buf[0] & 0x01)
++ rel_y |= ~0x10+1;
++
++ buf[0] = 0x01;
++ buf[1] = buf[4] = buf[5] = buf[6] = buf[7] = 0;
++
++ if (ictx->ir_protocol == IMON_IR_PROTOCOL_IMON) {
++ dir = stabilize((int)rel_x, (int)rel_y,
++ timeout, threshold);
++ if (!dir) {
++ ictx->kc = KEY_UNKNOWN;
++ return;
++ }
++ buf[2] = dir & 0xFF;
++ buf[3] = (dir >> 8) & 0xFF;
++ memcpy(&temp_key, buf, sizeof(temp_key));
++ remote_key = (u32) (le64_to_cpu(temp_key) & 0xffffffff);
++ ki = imon_remote_key_lookup(remote_key);
++ ictx->kc = imon_remote_key_table[ki % offset].keycode;
++ } else {
++ if (abs(rel_y) > abs(rel_x)) {
++ buf[2] = (rel_y > 0) ? 0x7F : 0x80;
++ buf[3] = 0;
++ ictx->kc = (rel_y > 0) ? KEY_DOWN : KEY_UP;
++ } else {
++ buf[2] = 0;
++ buf[3] = (rel_x > 0) ? 0x7F : 0x80;
++ ictx->kc = (rel_x > 0) ? KEY_RIGHT : KEY_LEFT;
++ }
++ }
++ }
++
++ ictx->ki = ki;
++}
++
++static int imon_parse_press_type(struct imon_context *ictx,
++ unsigned char *buf, u8 ksrc)
++{
++ int press_type = 0;
++
++ /* key release of 0x02XXXXXX key */
++ if (ictx->ki == -1 && buf[0] == 0x02 && buf[3] == 0x00)
++ ictx->kc = ictx->last_keycode;
++
++ /* mouse button release on (some) 0xffdc devices */
++ else if (ictx->ki == -1 && buf[0] == 0x68 && buf[1] == 0x82 &&
++ buf[2] == 0x81 && buf[3] == 0xb7)
++ ictx->kc = ictx->last_keycode;
++
++ /* mouse button release on (some other) 0xffdc devices */
++ else if (ictx->ki == -1 && buf[0] == 0x01 && buf[1] == 0x00 &&
++ buf[2] == 0x81 && buf[3] == 0xb7)
++ ictx->kc = ictx->last_keycode;
++
++ /* mce-specific button handling */
++ else if (ksrc == IMON_BUTTON_MCE) {
++ /* initial press */
++ if (ictx->kc != ictx->last_keycode
++ || buf[2] != ictx->mce_toggle_bit) {
++ ictx->last_keycode = ictx->kc;
++ ictx->mce_toggle_bit = buf[2];
++ press_type = 1;
++ mod_timer(&ictx->itimer,
++ jiffies + msecs_to_jiffies(MCE_TIMEOUT_MS));
++ /* repeat */
++ } else {
++ press_type = 2;
++ mod_timer(&ictx->itimer,
++ jiffies + msecs_to_jiffies(MCE_TIMEOUT_MS));
++ }
++
++ /* incoherent or irrelevant data */
++ } else if (ictx->ki == -1)
++ press_type = -EINVAL;
++
++ /* key release of 0xXXXXXXb7 key */
++ else if (ictx->ki >= IMON_KEY_RELEASE_OFFSET)
++ press_type = 0;
++
++ /* this is a button press */
++ else
++ press_type = 1;
++
++ return press_type;
++}
++
++/**
++ * Process the incoming packet
++ */
++static void imon_incoming_packet(struct imon_context *ictx,
++ struct urb *urb, int intf)
++{
++ int len = urb->actual_length;
++ unsigned char *buf = urb->transfer_buffer;
++ struct device *dev = ictx->dev;
++ u16 kc;
++ bool norelease = 0;
++ int i, ki;
++ int offset = IMON_KEY_RELEASE_OFFSET;
++ u64 temp_key;
++ u64 panel_key = 0;
++ u32 remote_key = 0;
++ struct input_dev *idev = NULL;
++ int press_type = 0;
++ int msec;
++ struct timeval t;
++ static struct timeval prev_time = { 0, 0 };
++ u8 ksrc = IMON_BUTTON_IMON;
++
++ idev = ictx->idev;
++
++ /* filter out junk data on the older 0xffdc imon devices */
++ if ((buf[0] == 0xff) && (buf[7] == 0xff))
++ return;
++
++ /* Figure out what key was pressed */
++ memcpy(&temp_key, buf, sizeof(temp_key));
++ if (len == 8 && buf[7] == 0xee) {
++ ksrc = IMON_BUTTON_PANEL;
++ panel_key = le64_to_cpu(temp_key);
++ ki = imon_panel_key_lookup(panel_key);
++ if (ki < 0)
++ kc = KEY_UNKNOWN;
++ else
++ kc = imon_panel_key_table[ki].keycode;
++ } else {
++ remote_key = (u32) (le64_to_cpu(temp_key) & 0xffffffff);
++ if (ictx->ir_protocol == IMON_IR_PROTOCOL_MCE) {
++ if (buf[0] == 0x80)
++ ksrc = IMON_BUTTON_MCE;
++ ki = imon_mce_key_lookup(remote_key);
++ if (ki < 0)
++ kc = KEY_UNKNOWN;
++ else
++ kc = imon_mce_key_table[ki].keycode;
++ } else {
++ ki = imon_remote_key_lookup(remote_key);
++ if (ki < 0)
++ kc = KEY_UNKNOWN;
++ else
++ kc = imon_remote_key_table[ki % offset].keycode;
++ }
++ }
++
++ /* keyboard/mouse mode toggle button */
++ if (kc == KEY_KEYBOARD && ki < offset) {
++ ictx->last_keycode = kc;
++ if (!nomouse) {
++ ictx->pad_mouse = ~(ictx->pad_mouse) & 0x1;
++ dev_dbg(dev, "toggling to %s mode\n",
++ ictx->pad_mouse ? "mouse" : "keyboard");
++ return;
++ } else {
++ ictx->pad_mouse = 0;
++ dev_dbg(dev, "mouse mode disabled, passing key value\n");
++ }
++ }
++
++ ictx->ki = ki;
++ ictx->kc = kc;
++
++ /* send touchscreen events through input subsystem if touchpad data */
++ if (ictx->display_type == IMON_DISPLAY_TYPE_VGA && len == 8 &&
++ buf[7] == 0x86) {
++ imon_touch_event(ictx, buf);
++
++ /* look for mouse events with pad in mouse mode */
++ } else if (ictx->pad_mouse) {
++ if (imon_mouse_event(ictx, buf, len))
++ return;
++ }
++
++ /* Now for some special handling to convert pad input to arrow keys */
++ if (((len == 5) && (buf[0] == 0x01) && (buf[4] == 0x00)) ||
++ ((len == 8) && (buf[0] & 0x40) &&
++ !(buf[1] & 0x1 || buf[1] >> 2 & 0x1))) {
++ len = 8;
++ imon_pad_to_keys(ictx, buf);
++ norelease = 1;
++ }
++
++ if (debug) {
++ printk(KERN_INFO "intf%d decoded packet: ", intf);
++ for (i = 0; i < len; ++i)
++ printk("%02x ", buf[i]);
++ printk("\n");
++ }
++
++ press_type = imon_parse_press_type(ictx, buf, ksrc);
++ if (press_type < 0)
++ goto not_input_data;
++
++ if (ictx->kc == KEY_UNKNOWN)
++ goto unknown_key;
++
++ /* KEY_MUTE repeats from MCE and knob need to be suppressed */
++ if ((ictx->kc == KEY_MUTE && ictx->kc == ictx->last_keycode)
++ && (buf[7] == 0xee || ksrc == IMON_BUTTON_MCE)) {
++ do_gettimeofday(&t);
++ msec = tv2int(&t, &prev_time);
++ prev_time = t;
++ if (msec < 200)
++ return;
++ }
++
++ input_report_key(idev, ictx->kc, press_type);
++ input_sync(idev);
++
++ /* panel keys and some remote keys don't generate a release */
++ if (panel_key || norelease) {
++ input_report_key(idev, ictx->kc, 0);
++ input_sync(idev);
++ }
++
++ ictx->last_keycode = ictx->kc;
++
++ return;
++
++unknown_key:
++ dev_info(dev, "%s: unknown keypress, code 0x%llx\n", __func__,
++ (panel_key ? be64_to_cpu(panel_key) :
++ be32_to_cpu(remote_key)));
++ return;
++
++not_input_data:
++ if (len != 8) {
++ dev_warn(dev, "imon %s: invalid incoming packet "
++ "size (len = %d, intf%d)\n", __func__, len, intf);
++ return;
++ }
++
++ /* iMON 2.4G associate frame */
++ if (buf[0] == 0x00 &&
++ buf[2] == 0xFF && /* REFID */
++ buf[3] == 0xFF &&
++ buf[4] == 0xFF &&
++ buf[5] == 0xFF && /* iMON 2.4G */
++ ((buf[6] == 0x4E && buf[7] == 0xDF) || /* LT */
++ (buf[6] == 0x5E && buf[7] == 0xDF))) { /* DT */
++ dev_warn(dev, "%s: remote associated refid=%02X\n",
++ __func__, buf[1]);
++ ictx->ir_isassociating = 0;
++ }
++}
++
++/**
++ * Callback function for USB core API: receive data
++ */
++static void usb_rx_callback_intf0(struct urb *urb)
++{
++ struct imon_context *ictx;
++ int intfnum = 0;
++
++ if (!urb)
++ return;
++
++ ictx = (struct imon_context *)urb->context;
++ if (!ictx)
++ return;
++
++ switch (urb->status) {
++ case -ENOENT: /* usbcore unlink successful! */
++ return;
++
++ case -ESHUTDOWN: /* transport endpoint was shut down */
++ break;
++
++ case 0:
++ imon_incoming_packet(ictx, urb, intfnum);
++ break;
++
++ default:
++ dev_warn(ictx->dev, "imon %s: status(%d): ignored\n",
++ __func__, urb->status);
++ break;
++ }
++
++ usb_submit_urb(ictx->rx_urb_intf0, GFP_ATOMIC);
++}
++
++static void usb_rx_callback_intf1(struct urb *urb)
++{
++ struct imon_context *ictx;
++ int intfnum = 1;
++
++ if (!urb)
++ return;
++
++ ictx = (struct imon_context *)urb->context;
++ if (!ictx)
++ return;
++
++ switch (urb->status) {
++ case -ENOENT: /* usbcore unlink successful! */
++ return;
++
++ case -ESHUTDOWN: /* transport endpoint was shut down */
++ break;
++
++ case 0:
++ imon_incoming_packet(ictx, urb, intfnum);
++ break;
++
++ default:
++ dev_warn(ictx->dev, "imon %s: status(%d): ignored\n",
++ __func__, urb->status);
++ break;
++ }
++
++ usb_submit_urb(ictx->rx_urb_intf1, GFP_ATOMIC);
++}
++
++static struct input_dev *imon_init_idev(struct imon_context *ictx)
++{
++ struct input_dev *idev;
++ int ret, i;
++
++ idev = input_allocate_device();
++ if (!idev) {
++ dev_err(ictx->dev, "remote input dev allocation failed\n");
++ goto idev_alloc_failed;
++ }
++
++ snprintf(ictx->name_idev, sizeof(ictx->name_idev),
++ "iMON Remote (%04x:%04x)", ictx->vendor, ictx->product);
++ idev->name = ictx->name_idev;
++
++ usb_make_path(ictx->usbdev_intf0, ictx->phys_idev,
++ sizeof(ictx->phys_idev));
++ strlcat(ictx->phys_idev, "/input0", sizeof(ictx->phys_idev));
++ idev->phys = ictx->phys_idev;
++
++ idev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REL);
++
++ idev->keybit[BIT_WORD(BTN_MOUSE)] =
++ BIT_MASK(BTN_LEFT) | BIT_MASK(BTN_RIGHT);
++ idev->relbit[0] = BIT_MASK(REL_X) | BIT_MASK(REL_Y) |
++ BIT_MASK(REL_WHEEL);
++
++ input_set_drvdata(idev, ictx);
++
++ if (ir_protocol == IMON_IR_PROTOCOL_MCE)
++ ret = sparse_keymap_setup(idev, imon_mce_key_table, NULL);
++ else
++ ret = sparse_keymap_setup(idev, imon_remote_key_table, NULL);
++ if (ret)
++ goto keymap_failed;
++
++ /* can't use sparse keymap atm, 64-bit keycodes */
++ for (i = 0; i < ARRAY_SIZE(imon_panel_key_table); i++) {
++ u16 kc = imon_panel_key_table[i].keycode;
++ __set_bit(kc, idev->keybit);
++ }
++
++ usb_to_input_id(ictx->usbdev_intf0, &idev->id);
++ idev->dev.parent = ictx->dev;
++ ret = input_register_device(idev);
++ if (ret < 0) {
++ dev_err(ictx->dev, "remote input dev register failed\n");
++ goto idev_register_failed;
++ }
++
++ return idev;
++
++idev_register_failed:
++ sparse_keymap_free(idev);
++keymap_failed:
++ input_free_device(idev);
++idev_alloc_failed:
++
++ return NULL;
++}
++
++static struct input_dev *imon_init_touch(struct imon_context *ictx)
++{
++ struct input_dev *touch;
++ int ret;
++
++ touch = input_allocate_device();
++ if (!touch) {
++ dev_err(ictx->dev, "touchscreen input dev allocation failed\n");
++ goto touch_alloc_failed;
++ }
++
++ snprintf(ictx->name_touch, sizeof(ictx->name_touch),
++ "iMON USB Touchscreen (%04x:%04x)",
++ ictx->vendor, ictx->product);
++ touch->name = ictx->name_touch;
++
++ usb_make_path(ictx->usbdev_intf1, ictx->phys_touch,
++ sizeof(ictx->phys_touch));
++ strlcat(ictx->phys_touch, "/input1", sizeof(ictx->phys_touch));
++ touch->phys = ictx->phys_touch;
++
++ touch->evbit[0] =
++ BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
++ touch->keybit[BIT_WORD(BTN_TOUCH)] =
++ BIT_MASK(BTN_TOUCH);
++ input_set_abs_params(touch, ABS_X,
++ 0x00, 0xfff, 0, 0);
++ input_set_abs_params(touch, ABS_Y,
++ 0x00, 0xfff, 0, 0);
++
++ input_set_drvdata(touch, ictx);
++
++ usb_to_input_id(ictx->usbdev_intf1, &touch->id);
++ touch->dev.parent = ictx->dev;
++ ret = input_register_device(touch);
++ if (ret < 0) {
++ dev_info(ictx->dev, "touchscreen input dev register failed\n");
++ goto touch_register_failed;
++ }
++
++ return touch;
++
++touch_register_failed:
++ input_free_device(ictx->touch);
++ mutex_unlock(&ictx->lock);
++
++touch_alloc_failed:
++ return NULL;
++}
++
++static bool imon_find_endpoints(struct imon_context *ictx,
++ struct usb_host_interface *iface_desc)
++{
++ struct usb_endpoint_descriptor *ep;
++ struct usb_endpoint_descriptor *rx_endpoint = NULL;
++ struct usb_endpoint_descriptor *tx_endpoint = NULL;
++ int ifnum = iface_desc->desc.bInterfaceNumber;
++ int num_endpts = iface_desc->desc.bNumEndpoints;
++ int i, ep_dir, ep_type;
++ bool ir_ep_found = 0;
++ bool display_ep_found = 0;
++ bool tx_control = 0;
++
++ /*
++ * Scan the endpoint list and set:
++ * first input endpoint = IR endpoint
++ * first output endpoint = display endpoint
++ */
++ for (i = 0; i < num_endpts && !(ir_ep_found && display_ep_found); ++i) {
++ ep = &iface_desc->endpoint[i].desc;
++ ep_dir = ep->bEndpointAddress & USB_ENDPOINT_DIR_MASK;
++ ep_type = ep->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
++
++ if (!ir_ep_found && ep_dir == USB_DIR_IN &&
++ ep_type == USB_ENDPOINT_XFER_INT) {
++
++ rx_endpoint = ep;
++ ir_ep_found = 1;
++ dev_dbg(ictx->dev, "%s: found IR endpoint\n", __func__);
++
++ } else if (!display_ep_found && ep_dir == USB_DIR_OUT &&
++ ep_type == USB_ENDPOINT_XFER_INT) {
++ tx_endpoint = ep;
++ display_ep_found = 1;
++ dev_dbg(ictx->dev, "%s: found display endpoint\n", __func__);
++ }
++ }
++
++ if (ifnum == 0) {
++ ictx->rx_endpoint_intf0 = rx_endpoint;
++ /*
++ * tx is used to send characters to lcd/vfd, associate RF
++ * remotes, set IR protocol, and maybe more...
++ */
++ ictx->tx_endpoint = tx_endpoint;
++ } else {
++ ictx->rx_endpoint_intf1 = rx_endpoint;
++ }
++
++ /*
++ * If we didn't find a display endpoint, this is probably one of the
++ * newer iMON devices that use control urb instead of interrupt
++ */
++ if (!display_ep_found) {
++ tx_control = 1;
++ display_ep_found = 1;
++ dev_dbg(ictx->dev, "%s: device uses control endpoint, not "
++ "interface OUT endpoint\n", __func__);
++ }
++
++ /*
++ * Some iMON receivers have no display. Unfortunately, it seems
++ * that SoundGraph recycles device IDs between devices both with
++ * and without... :\
++ */
++ if (ictx->display_type == IMON_DISPLAY_TYPE_NONE) {
++ display_ep_found = 0;
++ dev_dbg(ictx->dev, "%s: device has no display\n", __func__);
++ }
++
++ /*
++ * iMON Touch devices have a VGA touchscreen, but no "display", as
++ * that refers to e.g. /dev/lcd0 (a character device LCD or VFD).
++ */
++ if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) {
++ display_ep_found = 0;
++ dev_dbg(ictx->dev, "%s: iMON Touch device found\n", __func__);
++ }
++
++ /* Input endpoint is mandatory */
++ if (!ir_ep_found)
++ err("%s: no valid input (IR) endpoint found.", __func__);
++
++ ictx->tx_control = tx_control;
++
++ if (display_ep_found)
++ ictx->display_supported = 1;
++
++ return ir_ep_found;
++
++}
++
++static struct imon_context *imon_init_intf0(struct usb_interface *intf)
++{
++ struct imon_context *ictx;
++ struct urb *rx_urb;
++ struct urb *tx_urb;
++ struct device *dev = &intf->dev;
++ struct usb_host_interface *iface_desc;
++ int ret;
++
++ ictx = kzalloc(sizeof(struct imon_context), GFP_KERNEL);
++ if (!ictx) {
++ dev_err(dev, "%s: kzalloc failed for context", __func__);
++ goto exit;
++ }
++ rx_urb = usb_alloc_urb(0, GFP_KERNEL);
++ if (!rx_urb) {
++ dev_err(dev, "%s: usb_alloc_urb failed for IR urb", __func__);
++ goto rx_urb_alloc_failed;
++ }
++ tx_urb = usb_alloc_urb(0, GFP_KERNEL);
++ if (!tx_urb) {
++ dev_err(dev, "%s: usb_alloc_urb failed for display urb",
++ __func__);
++ goto tx_urb_alloc_failed;
++ }
++
++ mutex_init(&ictx->lock);
++
++ mutex_lock(&ictx->lock);
++
++ ictx->dev = dev;
++ ictx->usbdev_intf0 = usb_get_dev(interface_to_usbdev(intf));
++ ictx->dev_present_intf0 = 1;
++ ictx->rx_urb_intf0 = rx_urb;
++ ictx->tx_urb = tx_urb;
++
++ ictx->vendor = le16_to_cpu(ictx->usbdev_intf0->descriptor.idVendor);
++ ictx->product = le16_to_cpu(ictx->usbdev_intf0->descriptor.idProduct);
++
++ iface_desc = intf->cur_altsetting;
++ if (!imon_find_endpoints(ictx, iface_desc))
++ goto find_endpoint_failed;
++
++ ictx->idev = imon_init_idev(ictx);
++ if (!ictx->idev) {
++ dev_err(dev, "%s: input device setup failed\n", __func__);
++ goto idev_setup_failed;
++ }
++
++ usb_fill_int_urb(ictx->rx_urb_intf0, ictx->usbdev_intf0,
++ usb_rcvintpipe(ictx->usbdev_intf0,
++ ictx->rx_endpoint_intf0->bEndpointAddress),
++ ictx->usb_rx_buf, sizeof(ictx->usb_rx_buf),
++ usb_rx_callback_intf0, ictx,
++ ictx->rx_endpoint_intf0->bInterval);
++
++ ret = usb_submit_urb(ictx->rx_urb_intf0, GFP_KERNEL);
++ if (ret) {
++ err("%s: usb_submit_urb failed for intf0 (%d)",
++ __func__, ret);
++ goto urb_submit_failed;
++ }
++
++ return ictx;
++
++urb_submit_failed:
++ sparse_keymap_free(ictx->idev);
++ input_unregister_device(ictx->idev);
++ input_free_device(ictx->idev);
++idev_setup_failed:
++find_endpoint_failed:
++ mutex_unlock(&ictx->lock);
++ usb_free_urb(tx_urb);
++tx_urb_alloc_failed:
++ usb_free_urb(rx_urb);
++rx_urb_alloc_failed:
++ kfree(ictx);
++exit:
++ dev_err(dev, "unable to initialize intf0, err %d\n", ret);
++
++ return NULL;
++}
++
++static struct imon_context *imon_init_intf1(struct usb_interface *intf,
++ struct imon_context *ictx)
++{
++ struct urb *rx_urb;
++ struct usb_host_interface *iface_desc;
++ int ret;
++
++ rx_urb = usb_alloc_urb(0, GFP_KERNEL);
++ if (!rx_urb) {
++ err("%s: usb_alloc_urb failed for IR urb", __func__);
++ ret = -ENOMEM;
++ goto rx_urb_alloc_failed;
++ }
++
++ mutex_lock(&ictx->lock);
++
++ if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) {
++ init_timer(&ictx->ttimer);
++ ictx->ttimer.data = (unsigned long)ictx;
++ ictx->ttimer.function = imon_touch_display_timeout;
++ }
++
++ ictx->usbdev_intf1 = usb_get_dev(interface_to_usbdev(intf));
++ ictx->dev_present_intf1 = 1;
++ ictx->rx_urb_intf1 = rx_urb;
++
++ iface_desc = intf->cur_altsetting;
++ if (!imon_find_endpoints(ictx, iface_desc))
++ goto find_endpoint_failed;
++
++ if (ictx->display_type == IMON_DISPLAY_TYPE_VGA) {
++ ictx->touch = imon_init_touch(ictx);
++ if (!ictx->touch)
++ goto touch_setup_failed;
++ } else
++ ictx->touch = NULL;
++
++ usb_fill_int_urb(ictx->rx_urb_intf1, ictx->usbdev_intf1,
++ usb_rcvintpipe(ictx->usbdev_intf1,
++ ictx->rx_endpoint_intf1->bEndpointAddress),
++ ictx->usb_rx_buf, sizeof(ictx->usb_rx_buf),
++ usb_rx_callback_intf1, ictx,
++ ictx->rx_endpoint_intf1->bInterval);
++
++ ret = usb_submit_urb(ictx->rx_urb_intf1, GFP_KERNEL);
++
++ if (ret) {
++ err("%s: usb_submit_urb failed for intf1 (%d)",
++ __func__, ret);
++ goto urb_submit_failed;
++ }
++
++ return ictx;
++
++urb_submit_failed:
++ if (ictx->touch) {
++ input_unregister_device(ictx->touch);
++ input_free_device(ictx->touch);
++ }
++touch_setup_failed:
++find_endpoint_failed:
++ mutex_unlock(&ictx->lock);
++ usb_free_urb(rx_urb);
++rx_urb_alloc_failed:
++ dev_err(ictx->dev, "unable to initialize intf0, err %d\n", ret);
++
++ return NULL;
++}
++
++static void imon_set_display_type(struct imon_context *ictx,
++ struct usb_interface *intf)
++{
++ int configured_display_type = IMON_DISPLAY_TYPE_VFD;
++
++ /*
++ * Try to auto-detect the type of display if the user hasn't set
++ * it by hand via the display_type modparam. Default is VFD.
++ */
++ if (display_type == IMON_DISPLAY_TYPE_AUTO) {
++ if (usb_match_id(intf, lcd_device_list))
++ configured_display_type = IMON_DISPLAY_TYPE_LCD;
++ else if (usb_match_id(intf, imon_touchscreen_list))
++ configured_display_type = IMON_DISPLAY_TYPE_VGA;
++ else if (usb_match_id(intf, ir_only_list))
++ configured_display_type = IMON_DISPLAY_TYPE_NONE;
++ else
++ configured_display_type = IMON_DISPLAY_TYPE_VFD;
++ } else {
++ configured_display_type = display_type;
++ dev_dbg(ictx->dev, "%s: overriding display type to %d via "
++ "modparam\n", __func__, display_type);
++ }
++
++ ictx->display_type = configured_display_type;
++}
++
++static void imon_init_display(struct imon_context *ictx,
++ struct usb_interface *intf)
++{
++ int ret;
++ const unsigned char fp_packet[] = { 0x40, 0x00, 0x00, 0x00,
++ 0x00, 0x00, 0x00, 0x88 };
++
++ dev_dbg(ictx->dev, "Registering iMON display with sysfs\n");
++
++ /* set up sysfs entry for built-in clock */
++ ret = sysfs_create_group(&intf->dev.kobj,
++ &imon_display_attribute_group);
++ if (ret)
++ dev_err(ictx->dev, "Could not create display sysfs "
++ "entries(%d)", ret);
++
++ if (ictx->display_type == IMON_DISPLAY_TYPE_LCD)
++ ret = usb_register_dev(intf, &imon_lcd_class);
++ else
++ ret = usb_register_dev(intf, &imon_vfd_class);
++ if (ret)
++ /* Not a fatal error, so ignore */
++ dev_info(ictx->dev, "could not get a minor number for "
++ "display\n");
++
++ /* Enable front-panel buttons and/or knobs */
++ memcpy(ictx->usb_tx_buf, &fp_packet, sizeof(fp_packet));
++ ret = send_packet(ictx);
++ /* Not fatal, but warn about it */
++ if (ret)
++ dev_info(ictx->dev, "failed to enable front-panel "
++ "buttons and/or knobs\n");
++}
++
++/**
++ * Callback function for USB core API: Probe
++ */
++static int __devinit imon_probe(struct usb_interface *interface,
++ const struct usb_device_id *id)
++{
++ struct usb_device *usbdev = NULL;
++ struct usb_host_interface *iface_desc = NULL;
++ struct usb_interface *first_if;
++ struct device *dev = &interface->dev;
++ int ifnum, code_length, sysfs_err;
++ int ret = 0;
++ struct imon_context *ictx = NULL;
++ struct imon_context *first_if_ctx = NULL;
++ u16 vendor, product;
++
++ code_length = BUF_CHUNK_SIZE * 8;
++
++ usbdev = usb_get_dev(interface_to_usbdev(interface));
++ iface_desc = interface->cur_altsetting;
++ ifnum = iface_desc->desc.bInterfaceNumber;
++ vendor = le16_to_cpu(usbdev->descriptor.idVendor);
++ product = le16_to_cpu(usbdev->descriptor.idProduct);
++
++ dev_dbg(dev, "%s: found iMON device (%04x:%04x, intf%d)\n",
++ __func__, vendor, product, ifnum);
++
++ /* prevent races probing devices w/multiple interfaces */
++ mutex_lock(&driver_lock);
++
++ first_if = usb_ifnum_to_if(usbdev, 0);
++ first_if_ctx = (struct imon_context *)usb_get_intfdata(first_if);
++
++ if (ifnum == 0) {
++ ictx = imon_init_intf0(interface);
++ if (!ictx) {
++ err("%s: failed to initialize context!\n", __func__);
++ ret = -ENODEV;
++ goto fail;
++ }
++
++ imon_set_display_type(ictx, interface);
++
++ if (ictx->display_supported)
++ imon_init_display(ictx, interface);
++
++ if (product == 0xffdc) {
++ /* RF products *also* use 0xffdc... sigh... */
++ sysfs_err = sysfs_create_group(&interface->dev.kobj,
++ &imon_rf_attribute_group);
++ if (sysfs_err)
++ err("%s: Could not create RF sysfs entries(%d)",
++ __func__, sysfs_err);
++ }
++
++ } else {
++ /* this is the secondary interface on the device */
++ ictx = imon_init_intf1(interface, first_if_ctx);
++ if (!ictx) {
++ err("%s: failed to attach to context!\n", __func__);
++ ret = -ENODEV;
++ goto fail;
++ }
++
++ }
++
++ usb_set_intfdata(interface, ictx);
++
++ /* set IR protocol/remote type */
++ imon_set_ir_protocol(ictx);
++
++ dev_info(dev, "iMON device (%04x:%04x, intf%d) on "
++ "usb<%d:%d> initialized\n", vendor, product, ifnum,
++ usbdev->bus->busnum, usbdev->devnum);
++
++ mutex_unlock(&ictx->lock);
++ mutex_unlock(&driver_lock);
++
++ return 0;
++
++fail:
++ mutex_unlock(&driver_lock);
++ dev_err(dev, "unable to register, err %d\n", ret);
++
++ return ret;
++}
++
++/**
++ * Callback function for USB core API: disconnect
++ */
++static void __devexit imon_disconnect(struct usb_interface *interface)
++{
++ struct imon_context *ictx;
++ struct device *dev;
++ int ifnum;
++
++ /* prevent races with multi-interface device probing and display_open */
++ mutex_lock(&driver_lock);
++
++ ictx = usb_get_intfdata(interface);
++ dev = ictx->dev;
++ ifnum = interface->cur_altsetting->desc.bInterfaceNumber;
++
++ mutex_lock(&ictx->lock);
++
++ /*
++ * sysfs_remove_group is safe to call even if sysfs_create_group
++ * hasn't been called
++ */
++ sysfs_remove_group(&interface->dev.kobj,
++ &imon_display_attribute_group);
++ sysfs_remove_group(&interface->dev.kobj,
++ &imon_rf_attribute_group);
++
++ usb_set_intfdata(interface, NULL);
++
++ /* Abort ongoing write */
++ if (ictx->tx.busy) {
++ usb_kill_urb(ictx->tx_urb);
++ complete_all(&ictx->tx.finished);
++ }
++
++ if (ifnum == 0) {
++ ictx->dev_present_intf0 = 0;
++ usb_kill_urb(ictx->rx_urb_intf0);
++ sparse_keymap_free(ictx->idev);
++ input_unregister_device(ictx->idev);
++ if (ictx->display_supported) {
++ if (ictx->display_type == IMON_DISPLAY_TYPE_LCD)
++ usb_deregister_dev(interface, &imon_lcd_class);
++ else
++ usb_deregister_dev(interface, &imon_vfd_class);
++ }
++ } else {
++ ictx->dev_present_intf1 = 0;
++ usb_kill_urb(ictx->rx_urb_intf1);
++ if (ictx->display_type == IMON_DISPLAY_TYPE_VGA)
++ input_unregister_device(ictx->touch);
++ }
++
++ if (!ictx->dev_present_intf0 && !ictx->dev_present_intf1) {
++ if (ictx->display_type == IMON_DISPLAY_TYPE_VGA)
++ del_timer_sync(&ictx->ttimer);
++ mutex_unlock(&ictx->lock);
++ if (!ictx->display_isopen)
++ free_imon_context(ictx);
++ } else {
++ if (ictx->ir_protocol == IMON_IR_PROTOCOL_MCE)
++ del_timer_sync(&ictx->itimer);
++ mutex_unlock(&ictx->lock);
++ }
++
++ mutex_unlock(&driver_lock);
++
++ dev_dbg(dev, "%s: iMON device (intf%d) disconnected\n",
++ __func__, ifnum);
++}
++
++static int imon_suspend(struct usb_interface *intf, pm_message_t message)
++{
++ struct imon_context *ictx = usb_get_intfdata(intf);
++ int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
++
++ if (ifnum == 0)
++ usb_kill_urb(ictx->rx_urb_intf0);
++ else
++ usb_kill_urb(ictx->rx_urb_intf1);
++
++ return 0;
++}
++
++static int imon_resume(struct usb_interface *intf)
++{
++ int rc = 0;
++ struct imon_context *ictx = usb_get_intfdata(intf);
++ int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
++
++ if (ifnum == 0) {
++ usb_fill_int_urb(ictx->rx_urb_intf0, ictx->usbdev_intf0,
++ usb_rcvintpipe(ictx->usbdev_intf0,
++ ictx->rx_endpoint_intf0->bEndpointAddress),
++ ictx->usb_rx_buf, sizeof(ictx->usb_rx_buf),
++ usb_rx_callback_intf0, ictx,
++ ictx->rx_endpoint_intf0->bInterval);
++
++ rc = usb_submit_urb(ictx->rx_urb_intf0, GFP_ATOMIC);
++
++ } else {
++ usb_fill_int_urb(ictx->rx_urb_intf1, ictx->usbdev_intf1,
++ usb_rcvintpipe(ictx->usbdev_intf1,
++ ictx->rx_endpoint_intf1->bEndpointAddress),
++ ictx->usb_rx_buf, sizeof(ictx->usb_rx_buf),
++ usb_rx_callback_intf1, ictx,
++ ictx->rx_endpoint_intf1->bInterval);
++
++ rc = usb_submit_urb(ictx->rx_urb_intf1, GFP_ATOMIC);
++ }
++
++ return rc;
++}
++
++static int __init imon_init(void)
++{
++ int rc;
++
++ rc = usb_register(&imon_driver);
++ if (rc) {
++ err("%s: usb register failed(%d)", __func__, rc);
++ rc = -ENODEV;
++ }
++
++ return rc;
++}
++
++static void __exit imon_exit(void)
++{
++ usb_deregister(&imon_driver);
++}
++
++module_init(imon_init);
++module_exit(imon_exit);
+diff --git a/drivers/input/sparse-keymap.c b/drivers/input/sparse-keymap.c
+new file mode 100644
+index 0000000..fbd3987
+--- /dev/null
++++ b/drivers/input/sparse-keymap.c
+@@ -0,0 +1,250 @@
++/*
++ * Generic support for sparse keymaps
++ *
++ * Copyright (c) 2009 Dmitry Torokhov
++ *
++ * Derived from wistron button driver:
++ * Copyright (C) 2005 Miloslav Trmac <mitr@volny.cz>
++ * Copyright (C) 2005 Bernhard Rosenkraenzer <bero@arklinux.org>
++ * Copyright (C) 2005 Dmitry Torokhov <dtor@mail.ru>
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 as published by
++ * the Free Software Foundation.
++ */
++
++#include <linux/input.h>
++#include <linux/input/sparse-keymap.h>
++
++MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
++MODULE_DESCRIPTION("Generic support for sparse keymaps");
++MODULE_LICENSE("GPL v2");
++MODULE_VERSION("0.1");
++
++/**
++ * sparse_keymap_entry_from_scancode - perform sparse keymap lookup
++ * @dev: Input device using sparse keymap
++ * @code: Scan code
++ *
++ * This function is used to perform &struct key_entry lookup in an
++ * input device using sparse keymap.
++ */
++struct key_entry *sparse_keymap_entry_from_scancode(struct input_dev *dev,
++ unsigned int code)
++{
++ struct key_entry *key;
++
++ for (key = dev->keycode; key->type != KE_END; key++)
++ if (code == key->code)
++ return key;
++
++ return NULL;
++}
++EXPORT_SYMBOL(sparse_keymap_entry_from_scancode);
++
++/**
++ * sparse_keymap_entry_from_keycode - perform sparse keymap lookup
++ * @dev: Input device using sparse keymap
++ * @keycode: Key code
++ *
++ * This function is used to perform &struct key_entry lookup in an
++ * input device using sparse keymap.
++ */
++struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev,
++ unsigned int keycode)
++{
++ struct key_entry *key;
++
++ for (key = dev->keycode; key->type != KE_END; key++)
++ if (key->type == KE_KEY && keycode == key->keycode)
++ return key;
++
++ return NULL;
++}
++EXPORT_SYMBOL(sparse_keymap_entry_from_keycode);
++
++static int sparse_keymap_getkeycode(struct input_dev *dev,
++ int scancode, int *keycode)
++{
++ const struct key_entry *key =
++ sparse_keymap_entry_from_scancode(dev, scancode);
++
++ if (key && key->type == KE_KEY) {
++ *keycode = key->keycode;
++ return 0;
++ }
++
++ return -EINVAL;
++}
++
++static int sparse_keymap_setkeycode(struct input_dev *dev,
++ int scancode, int keycode)
++{
++ struct key_entry *key;
++ int old_keycode;
++
++ if (keycode < 0 || keycode > KEY_MAX)
++ return -EINVAL;
++
++ key = sparse_keymap_entry_from_scancode(dev, scancode);
++ if (key && key->type == KE_KEY) {
++ old_keycode = key->keycode;
++ key->keycode = keycode;
++ set_bit(keycode, dev->keybit);
++ if (!sparse_keymap_entry_from_keycode(dev, old_keycode))
++ clear_bit(old_keycode, dev->keybit);
++ return 0;
++ }
++
++ return -EINVAL;
++}
++
++/**
++ * sparse_keymap_setup - set up sparse keymap for an input device
++ * @dev: Input device
++ * @keymap: Keymap in form of array of &key_entry structures ending
++ * with %KE_END type entry
++ * @setup: Function that can be used to adjust keymap entries
++ * depending on device's deeds, may be %NULL
++ *
++ * The function calculates size and allocates copy of the original
++ * keymap after which sets up input device event bits appropriately.
++ * Before destroying input device allocated keymap should be freed
++ * with a call to sparse_keymap_free().
++ */
++int sparse_keymap_setup(struct input_dev *dev,
++ const struct key_entry *keymap,
++ int (*setup)(struct input_dev *, struct key_entry *))
++{
++ size_t map_size = 1; /* to account for the last KE_END entry */
++ const struct key_entry *e;
++ struct key_entry *map, *entry;
++ int i;
++ int error;
++
++ for (e = keymap; e->type != KE_END; e++)
++ map_size++;
++
++ map = kcalloc(map_size, sizeof (struct key_entry), GFP_KERNEL);
++ if (!map)
++ return -ENOMEM;
++
++ memcpy(map, keymap, map_size * sizeof (struct key_entry));
++
++ for (i = 0; i < map_size; i++) {
++ entry = &map[i];
++
++ if (setup) {
++ error = setup(dev, entry);
++ if (error)
++ goto err_out;
++ }
++
++ switch (entry->type) {
++ case KE_KEY:
++ __set_bit(EV_KEY, dev->evbit);
++ __set_bit(entry->keycode, dev->keybit);
++ break;
++
++ case KE_SW:
++ __set_bit(EV_SW, dev->evbit);
++ __set_bit(entry->sw.code, dev->swbit);
++ break;
++ }
++ }
++
++ dev->keycode = map;
++ dev->keycodemax = map_size;
++ dev->getkeycode = sparse_keymap_getkeycode;
++ dev->setkeycode = sparse_keymap_setkeycode;
++
++ return 0;
++
++ err_out:
++ kfree(keymap);
++ return error;
++
++}
++EXPORT_SYMBOL(sparse_keymap_setup);
++
++/**
++ * sparse_keymap_free - free memory allocated for sparse keymap
++ * @dev: Input device using sparse keymap
++ *
++ * This function is used to free memory allocated by sparse keymap
++ * in an input device that was set up by sparse_keymap_setup().
++ */
++void sparse_keymap_free(struct input_dev *dev)
++{
++ kfree(dev->keycode);
++ dev->keycode = NULL;
++ dev->keycodemax = 0;
++ dev->getkeycode = NULL;
++ dev->setkeycode = NULL;
++}
++EXPORT_SYMBOL(sparse_keymap_free);
++
++/**
++ * sparse_keymap_report_entry - report event corresponding to given key entry
++ * @dev: Input device for which event should be reported
++ * @ke: key entry describing event
++ * @value: Value that should be reported (ignored by %KE_SW entries)
++ * @autorelease: Signals whether release event should be emitted for %KE_KEY
++ * entries right after reporting press event, ignored by all other
++ * entries
++ *
++ * This function is used to report input event described by given
++ * &struct key_entry.
++ */
++void sparse_keymap_report_entry(struct input_dev *dev, const struct key_entry *ke,
++ unsigned int value, bool autorelease)
++{
++ switch (ke->type) {
++ case KE_KEY:
++ input_report_key(dev, ke->keycode, value);
++ input_sync(dev);
++ if (value && autorelease) {
++ input_report_key(dev, ke->keycode, 0);
++ input_sync(dev);
++ }
++ break;
++
++ case KE_SW:
++ value = ke->sw.value;
++ /* fall through */
++
++ case KE_VSW:
++ input_report_switch(dev, ke->sw.code, value);
++ break;
++ }
++}
++EXPORT_SYMBOL(sparse_keymap_report_entry);
++
++/**
++ * sparse_keymap_report_event - report event corresponding to given scancode
++ * @dev: Input device using sparse keymap
++ * @code: Scan code
++ * @value: Value that should be reported (ignored by %KE_SW entries)
++ * @autorelease: Signals whether release event should be emitted for %KE_KEY
++ * entries right after reporting press event, ignored by all other
++ * entries
++ *
++ * This function is used to perform lookup in an input device using sparse
++ * keymap and report corresponding event. Returns %true if lookup was
++ * successful and %false otherwise.
++ */
++bool sparse_keymap_report_event(struct input_dev *dev, unsigned int code,
++ unsigned int value, bool autorelease)
++{
++ const struct key_entry *ke =
++ sparse_keymap_entry_from_scancode(dev, code);
++
++ if (ke) {
++ sparse_keymap_report_entry(dev, ke, value, autorelease);
++ return true;
++ }
++
++ return false;
++}
++EXPORT_SYMBOL(sparse_keymap_report_event);
++
+diff --git a/include/linux/input/sparse-keymap.h b/include/linux/input/sparse-keymap.h
+new file mode 100644
+index 0000000..52db620
+--- /dev/null
++++ b/include/linux/input/sparse-keymap.h
+@@ -0,0 +1,62 @@
++#ifndef _SPARSE_KEYMAP_H
++#define _SPARSE_KEYMAP_H
++
++/*
++ * Copyright (c) 2009 Dmitry Torokhov
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License version 2 as published by
++ * the Free Software Foundation.
++ */
++
++#define KE_END 0 /* Indicates end of keymap */
++#define KE_KEY 1 /* Ordinary key/button */
++#define KE_SW 2 /* Switch (predetermined value) */
++#define KE_VSW 3 /* Switch (value supplied at runtime) */
++#define KE_IGNORE 4 /* Known entry that should be ignored */
++#define KE_LAST KE_IGNORE
++
++/**
++ * struct key_entry - keymap entry for use in sparse keymap
++ * @type: Type of the key entry (KE_KEY, KE_SW, KE_VSW, KE_END);
++ * drivers are allowed to extend the list with their own
++ * private definitions.
++ * @code: Device-specific data identifying the button/switch
++ * @keycode: KEY_* code assigned to a key/button
++ * @sw.code: SW_* code assigned to a switch
++ * @sw.value: Value that should be sent in an input even when KE_SW
++ * switch is toggled. KE_VSW switches ignore this field and
++ * expect driver to supply value for the event.
++ *
++ * This structure defines an entry in a sparse keymap used by some
++ * input devices for which traditional table-based approach is not
++ * suitable.
++ */
++struct key_entry {
++ int type; /* See KE_* above */
++ u32 code;
++ union {
++ u16 keycode; /* For KE_KEY */
++ struct { /* For KE_SW, KE_VSW */
++ u8 code;
++ u8 value; /* For KE_SW, ignored by KE_VSW */
++ } sw;
++ };
++};
++
++struct key_entry *sparse_keymap_entry_from_scancode(struct input_dev *dev,
++ unsigned int code);
++struct key_entry *sparse_keymap_entry_from_keycode(struct input_dev *dev,
++ unsigned int code);
++int sparse_keymap_setup(struct input_dev *dev,
++ const struct key_entry *keymap,
++ int (*setup)(struct input_dev *, struct key_entry *));
++void sparse_keymap_free(struct input_dev *dev);
++
++void sparse_keymap_report_entry(struct input_dev *dev, const struct key_entry *ke,
++ unsigned int value, bool autorelease);
++
++bool sparse_keymap_report_event(struct input_dev *dev, unsigned int code,
++ unsigned int value, bool autorelease);
++
++#endif /* _SPARSE_KEYMAP_H */
+diff --git a/include/linux/lirc.h b/include/linux/lirc.h
+new file mode 100644
+index 0000000..917ffca
+--- /dev/null
++++ b/include/linux/lirc.h
+@@ -0,0 +1,94 @@
++/*
++ * lirc.h - linux infrared remote control header file
++ * last modified 2007/09/27
++ */
++
++#ifndef _LINUX_LIRC_H
++#define _LINUX_LIRC_H
++
++#include <linux/types.h>
++#include <linux/ioctl.h>
++
++#define PULSE_BIT 0x01000000
++#define PULSE_MASK 0x00FFFFFF
++
++/*** lirc compatible hardware features ***/
++
++#define LIRC_MODE2SEND(x) (x)
++#define LIRC_SEND2MODE(x) (x)
++#define LIRC_MODE2REC(x) ((x) << 16)
++#define LIRC_REC2MODE(x) ((x) >> 16)
++
++#define LIRC_MODE_RAW 0x00000001
++#define LIRC_MODE_PULSE 0x00000002
++#define LIRC_MODE_MODE2 0x00000004
++#define LIRC_MODE_LIRCCODE 0x00000010
++
++
++#define LIRC_CAN_SEND_RAW LIRC_MODE2SEND(LIRC_MODE_RAW)
++#define LIRC_CAN_SEND_PULSE LIRC_MODE2SEND(LIRC_MODE_PULSE)
++#define LIRC_CAN_SEND_MODE2 LIRC_MODE2SEND(LIRC_MODE_MODE2)
++#define LIRC_CAN_SEND_LIRCCODE LIRC_MODE2SEND(LIRC_MODE_LIRCCODE)
++
++#define LIRC_CAN_SEND_MASK 0x0000003f
++
++#define LIRC_CAN_SET_SEND_CARRIER 0x00000100
++#define LIRC_CAN_SET_SEND_DUTY_CYCLE 0x00000200
++#define LIRC_CAN_SET_TRANSMITTER_MASK 0x00000400
++
++#define LIRC_CAN_REC_RAW LIRC_MODE2REC(LIRC_MODE_RAW)
++#define LIRC_CAN_REC_PULSE LIRC_MODE2REC(LIRC_MODE_PULSE)
++#define LIRC_CAN_REC_MODE2 LIRC_MODE2REC(LIRC_MODE_MODE2)
++#define LIRC_CAN_REC_LIRCCODE LIRC_MODE2REC(LIRC_MODE_LIRCCODE)
++
++#define LIRC_CAN_REC_MASK LIRC_MODE2REC(LIRC_CAN_SEND_MASK)
++
++#define LIRC_CAN_SET_REC_CARRIER (LIRC_CAN_SET_SEND_CARRIER << 16)
++#define LIRC_CAN_SET_REC_DUTY_CYCLE (LIRC_CAN_SET_SEND_DUTY_CYCLE << 16)
++
++#define LIRC_CAN_SET_REC_DUTY_CYCLE_RANGE 0x40000000
++#define LIRC_CAN_SET_REC_CARRIER_RANGE 0x80000000
++#define LIRC_CAN_GET_REC_RESOLUTION 0x20000000
++
++#define LIRC_CAN_SEND(x) ((x)&LIRC_CAN_SEND_MASK)
++#define LIRC_CAN_REC(x) ((x)&LIRC_CAN_REC_MASK)
++
++#define LIRC_CAN_NOTIFY_DECODE 0x01000000
++
++/*** IOCTL commands for lirc driver ***/
++
++#define LIRC_GET_FEATURES _IOR('i', 0x00000000, unsigned long)
++
++#define LIRC_GET_SEND_MODE _IOR('i', 0x00000001, unsigned long)
++#define LIRC_GET_REC_MODE _IOR('i', 0x00000002, unsigned long)
++#define LIRC_GET_SEND_CARRIER _IOR('i', 0x00000003, unsigned int)
++#define LIRC_GET_REC_CARRIER _IOR('i', 0x00000004, unsigned int)
++#define LIRC_GET_SEND_DUTY_CYCLE _IOR('i', 0x00000005, unsigned int)
++#define LIRC_GET_REC_DUTY_CYCLE _IOR('i', 0x00000006, unsigned int)
++#define LIRC_GET_REC_RESOLUTION _IOR('i', 0x00000007, unsigned int)
++
++/* code length in bits, currently only for LIRC_MODE_LIRCCODE */
++#define LIRC_GET_LENGTH _IOR('i', 0x0000000f, unsigned long)
++
++#define LIRC_SET_SEND_MODE _IOW('i', 0x00000011, unsigned long)
++#define LIRC_SET_REC_MODE _IOW('i', 0x00000012, unsigned long)
++/* Note: these can reset the according pulse_width */
++#define LIRC_SET_SEND_CARRIER _IOW('i', 0x00000013, unsigned int)
++#define LIRC_SET_REC_CARRIER _IOW('i', 0x00000014, unsigned int)
++#define LIRC_SET_SEND_DUTY_CYCLE _IOW('i', 0x00000015, unsigned int)
++#define LIRC_SET_REC_DUTY_CYCLE _IOW('i', 0x00000016, unsigned int)
++#define LIRC_SET_TRANSMITTER_MASK _IOW('i', 0x00000017, unsigned int)
++
++/*
++ * to set a range use
++ * LIRC_SET_REC_DUTY_CYCLE_RANGE/LIRC_SET_REC_CARRIER_RANGE with the
++ * lower bound first and later
++ * LIRC_SET_REC_DUTY_CYCLE/LIRC_SET_REC_CARRIER with the upper bound
++ */
++
++#define LIRC_SET_REC_DUTY_CYCLE_RANGE _IOW('i', 0x0000001e, unsigned int)
++#define LIRC_SET_REC_CARRIER_RANGE _IOW('i', 0x0000001f, unsigned int)
++
++#define LIRC_NOTIFY_DECODE _IO('i', 0x00000020)
++
++#endif
diff --git a/mac80211-do-not-wipe-out-old-supported-rates.patch b/mac80211-do-not-wipe-out-old-supported-rates.patch
new file mode 100644
index 0000000..0cdff31
--- /dev/null
+++ b/mac80211-do-not-wipe-out-old-supported-rates.patch
@@ -0,0 +1,68 @@
+From: Stanislaw Gruszka <sgruszka@redhat.com>
+To: kernel@lists.fedoraproject.org, "John W. Linville" <linville@redhat.com>
+Subject: [PATCH 3/4 2.6.32.y] mac80211: do not wip out old supported rates
+Date: Fri, 11 Jun 2010 17:03:15 +0200
+
+Use old supported rates, if some buggy AP do not provide
+supported rates information element in managment frame.
+
+Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
+---
+ net/mac80211/scan.c | 21 +++++++++++----------
+ 1 files changed, 11 insertions(+), 10 deletions(-)
+
+diff --git a/net/mac80211/scan.c b/net/mac80211/scan.c
+index fd6411d..169111a 100644
+--- a/net/mac80211/scan.c
++++ b/net/mac80211/scan.c
+@@ -62,7 +62,7 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
+ bool beacon)
+ {
+ struct ieee80211_bss *bss;
+- int clen;
++ int clen, srlen;
+ s32 signal = 0;
+
+ if (local->hw.flags & IEEE80211_HW_SIGNAL_DBM)
+@@ -94,23 +94,24 @@ ieee80211_bss_info_update(struct ieee80211_local *local,
+ if (bss->dtim_period == 0)
+ bss->dtim_period = 1;
+
+- bss->supp_rates_len = 0;
++ /* replace old supported rates if we get new values */
++ srlen = 0;
+ if (elems->supp_rates) {
+- clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
++ clen = IEEE80211_MAX_SUPP_RATES;
+ if (clen > elems->supp_rates_len)
+ clen = elems->supp_rates_len;
+- memcpy(&bss->supp_rates[bss->supp_rates_len], elems->supp_rates,
+- clen);
+- bss->supp_rates_len += clen;
++ memcpy(bss->supp_rates, elems->supp_rates, clen);
++ srlen += clen;
+ }
+ if (elems->ext_supp_rates) {
+- clen = IEEE80211_MAX_SUPP_RATES - bss->supp_rates_len;
++ clen = IEEE80211_MAX_SUPP_RATES - srlen;
+ if (clen > elems->ext_supp_rates_len)
+ clen = elems->ext_supp_rates_len;
+- memcpy(&bss->supp_rates[bss->supp_rates_len],
+- elems->ext_supp_rates, clen);
+- bss->supp_rates_len += clen;
++ memcpy(bss->supp_rates + srlen, elems->ext_supp_rates, clen);
++ srlen += clen;
+ }
++ if (srlen)
++ bss->supp_rates_len = srlen;
+
+ bss->wmm_used = elems->wmm_param || elems->wmm_info;
+
+--
+1.6.2.5
+
+_______________________________________________
+kernel mailing list
+kernel@lists.fedoraproject.org
+https://admin.fedoraproject.org/mailman/listinfo/kernel
+
diff --git a/mac80211-explicitly-disable-enable-QoS.patch b/mac80211-explicitly-disable-enable-QoS.patch
new file mode 100644
index 0000000..2d54540
--- /dev/null
+++ b/mac80211-explicitly-disable-enable-QoS.patch
@@ -0,0 +1,358 @@
+From: Stanislaw Gruszka <sgruszka@redhat.com>
+To: kernel@lists.fedoraproject.org, "John W. Linville" <linville@redhat.com>
+Subject: [PATCH 2/4 2.6.32.y] iwlwifi: manage QoS by mac stack
+Date: Fri, 11 Jun 2010 17:03:14 +0200
+
+We activate/deactivate QoS and setup default queue parameters in iwlwifi
+driver. Mac stack do the same, so we do not need repeat that work here.
+Stack also will tell when disable QoS, this will fix driver when working
+with older APs, that do not have QoS implemented.
+
+Patch make "force = true" in iwl_active_qos() assuming we always want
+to do with QoS what mac stack wish.
+
+Patch also remove unused qos_cap bits, do not initialize qos_active = 0,
+as we have it initialized to zero by kzalloc.
+
+Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
+---
+ drivers/net/wireless/iwlwifi/iwl-agn.c | 9 --
+ drivers/net/wireless/iwlwifi/iwl-core.c | 147 +++------------------------
+ drivers/net/wireless/iwlwifi/iwl-core.h | 3 +-
+ drivers/net/wireless/iwlwifi/iwl-dev.h | 21 ----
+ drivers/net/wireless/iwlwifi/iwl3945-base.c | 7 --
+ 5 files changed, 17 insertions(+), 170 deletions(-)
+
+diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
+index 921dc4a..b05f198 100644
+--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
++++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
+@@ -2172,7 +2172,6 @@ void iwl_post_associate(struct iwl_priv *priv)
+ {
+ struct ieee80211_conf *conf = NULL;
+ int ret = 0;
+- unsigned long flags;
+
+ if (priv->iw_mode == NL80211_IFTYPE_AP) {
+ IWL_ERR(priv, "%s Should not be called in AP mode\n", __func__);
+@@ -2257,10 +2256,6 @@ void iwl_post_associate(struct iwl_priv *priv)
+ if (priv->iw_mode == NL80211_IFTYPE_ADHOC)
+ priv->assoc_station_added = 1;
+
+- spin_lock_irqsave(&priv->lock, flags);
+- iwl_activate_qos(priv, 0);
+- spin_unlock_irqrestore(&priv->lock, flags);
+-
+ /* the chain noise calibration will enabled PM upon completion
+ * If chain noise has already been run, then we need to enable
+ * power management here */
+@@ -2384,7 +2379,6 @@ static int iwl_mac_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+ void iwl_config_ap(struct iwl_priv *priv)
+ {
+ int ret = 0;
+- unsigned long flags;
+
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+@@ -2432,9 +2426,6 @@ void iwl_config_ap(struct iwl_priv *priv)
+ /* restore RXON assoc */
+ priv->staging_rxon.filter_flags |= RXON_FILTER_ASSOC_MSK;
+ iwlcore_commit_rxon(priv);
+- spin_lock_irqsave(&priv->lock, flags);
+- iwl_activate_qos(priv, 1);
+- spin_unlock_irqrestore(&priv->lock, flags);
+ iwl_rxon_add_station(priv, iwl_bcast_addr, 0);
+ }
+ iwl_send_beacon_cmd(priv);
+diff --git a/drivers/net/wireless/iwlwifi/iwl-core.c b/drivers/net/wireless/iwlwifi/iwl-core.c
+index 4a4f7e4..6ce19ea 100644
+--- a/drivers/net/wireless/iwlwifi/iwl-core.c
++++ b/drivers/net/wireless/iwlwifi/iwl-core.c
+@@ -266,17 +266,13 @@ EXPORT_SYMBOL(iwl_hw_nic_init);
+ /*
+ * QoS support
+ */
+-void iwl_activate_qos(struct iwl_priv *priv, u8 force)
++static void iwl_update_qos(struct iwl_priv *priv)
+ {
+ if (test_bit(STATUS_EXIT_PENDING, &priv->status))
+ return;
+
+ priv->qos_data.def_qos_parm.qos_flags = 0;
+
+- if (priv->qos_data.qos_cap.q_AP.queue_request &&
+- !priv->qos_data.qos_cap.q_AP.txop_request)
+- priv->qos_data.def_qos_parm.qos_flags |=
+- QOS_PARAM_FLG_TXOP_TYPE_MSK;
+ if (priv->qos_data.qos_active)
+ priv->qos_data.def_qos_parm.qos_flags |=
+ QOS_PARAM_FLG_UPDATE_EDCA_MSK;
+@@ -284,118 +280,14 @@ void iwl_activate_qos(struct iwl_priv *priv, u8 force)
+ if (priv->current_ht_config.is_ht)
+ priv->qos_data.def_qos_parm.qos_flags |= QOS_PARAM_FLG_TGN_MSK;
+
+- if (force || iwl_is_associated(priv)) {
+- IWL_DEBUG_QOS(priv, "send QoS cmd with Qos active=%d FLAGS=0x%X\n",
+- priv->qos_data.qos_active,
+- priv->qos_data.def_qos_parm.qos_flags);
++ IWL_DEBUG_QOS(priv, "send QoS cmd with Qos active=%d FLAGS=0x%X\n",
++ priv->qos_data.qos_active,
++ priv->qos_data.def_qos_parm.qos_flags);
+
+- iwl_send_cmd_pdu_async(priv, REPLY_QOS_PARAM,
+- sizeof(struct iwl_qosparam_cmd),
+- &priv->qos_data.def_qos_parm, NULL);
+- }
++ iwl_send_cmd_pdu_async(priv, REPLY_QOS_PARAM,
++ sizeof(struct iwl_qosparam_cmd),
++ &priv->qos_data.def_qos_parm, NULL);
+ }
+-EXPORT_SYMBOL(iwl_activate_qos);
+-
+-/*
+- * AC CWmin CW max AIFSN TXOP Limit TXOP Limit
+- * (802.11b) (802.11a/g)
+- * AC_BK 15 1023 7 0 0
+- * AC_BE 15 1023 3 0 0
+- * AC_VI 7 15 2 6.016ms 3.008ms
+- * AC_VO 3 7 2 3.264ms 1.504ms
+- */
+-void iwl_reset_qos(struct iwl_priv *priv)
+-{
+- u16 cw_min = 15;
+- u16 cw_max = 1023;
+- u8 aifs = 2;
+- bool is_legacy = false;
+- unsigned long flags;
+- int i;
+-
+- spin_lock_irqsave(&priv->lock, flags);
+- /* QoS always active in AP and ADHOC mode
+- * In STA mode wait for association
+- */
+- if (priv->iw_mode == NL80211_IFTYPE_ADHOC ||
+- priv->iw_mode == NL80211_IFTYPE_AP)
+- priv->qos_data.qos_active = 1;
+- else
+- priv->qos_data.qos_active = 0;
+-
+- /* check for legacy mode */
+- if ((priv->iw_mode == NL80211_IFTYPE_ADHOC &&
+- (priv->active_rate & IWL_OFDM_RATES_MASK) == 0) ||
+- (priv->iw_mode == NL80211_IFTYPE_STATION &&
+- (priv->staging_rxon.flags & RXON_FLG_SHORT_SLOT_MSK) == 0)) {
+- cw_min = 31;
+- is_legacy = 1;
+- }
+-
+- if (priv->qos_data.qos_active)
+- aifs = 3;
+-
+- /* AC_BE */
+- priv->qos_data.def_qos_parm.ac[0].cw_min = cpu_to_le16(cw_min);
+- priv->qos_data.def_qos_parm.ac[0].cw_max = cpu_to_le16(cw_max);
+- priv->qos_data.def_qos_parm.ac[0].aifsn = aifs;
+- priv->qos_data.def_qos_parm.ac[0].edca_txop = 0;
+- priv->qos_data.def_qos_parm.ac[0].reserved1 = 0;
+-
+- if (priv->qos_data.qos_active) {
+- /* AC_BK */
+- i = 1;
+- priv->qos_data.def_qos_parm.ac[i].cw_min = cpu_to_le16(cw_min);
+- priv->qos_data.def_qos_parm.ac[i].cw_max = cpu_to_le16(cw_max);
+- priv->qos_data.def_qos_parm.ac[i].aifsn = 7;
+- priv->qos_data.def_qos_parm.ac[i].edca_txop = 0;
+- priv->qos_data.def_qos_parm.ac[i].reserved1 = 0;
+-
+- /* AC_VI */
+- i = 2;
+- priv->qos_data.def_qos_parm.ac[i].cw_min =
+- cpu_to_le16((cw_min + 1) / 2 - 1);
+- priv->qos_data.def_qos_parm.ac[i].cw_max =
+- cpu_to_le16(cw_min);
+- priv->qos_data.def_qos_parm.ac[i].aifsn = 2;
+- if (is_legacy)
+- priv->qos_data.def_qos_parm.ac[i].edca_txop =
+- cpu_to_le16(6016);
+- else
+- priv->qos_data.def_qos_parm.ac[i].edca_txop =
+- cpu_to_le16(3008);
+- priv->qos_data.def_qos_parm.ac[i].reserved1 = 0;
+-
+- /* AC_VO */
+- i = 3;
+- priv->qos_data.def_qos_parm.ac[i].cw_min =
+- cpu_to_le16((cw_min + 1) / 4 - 1);
+- priv->qos_data.def_qos_parm.ac[i].cw_max =
+- cpu_to_le16((cw_min + 1) / 2 - 1);
+- priv->qos_data.def_qos_parm.ac[i].aifsn = 2;
+- priv->qos_data.def_qos_parm.ac[i].reserved1 = 0;
+- if (is_legacy)
+- priv->qos_data.def_qos_parm.ac[i].edca_txop =
+- cpu_to_le16(3264);
+- else
+- priv->qos_data.def_qos_parm.ac[i].edca_txop =
+- cpu_to_le16(1504);
+- } else {
+- for (i = 1; i < 4; i++) {
+- priv->qos_data.def_qos_parm.ac[i].cw_min =
+- cpu_to_le16(cw_min);
+- priv->qos_data.def_qos_parm.ac[i].cw_max =
+- cpu_to_le16(cw_max);
+- priv->qos_data.def_qos_parm.ac[i].aifsn = aifs;
+- priv->qos_data.def_qos_parm.ac[i].edca_txop = 0;
+- priv->qos_data.def_qos_parm.ac[i].reserved1 = 0;
+- }
+- }
+- IWL_DEBUG_QOS(priv, "set QoS to default \n");
+-
+- spin_unlock_irqrestore(&priv->lock, flags);
+-}
+-EXPORT_SYMBOL(iwl_reset_qos);
+
+ #define MAX_BIT_RATE_40_MHZ 150 /* Mbps */
+ #define MAX_BIT_RATE_20_MHZ 72 /* Mbps */
+@@ -1503,11 +1395,6 @@ int iwl_init_drv(struct iwl_priv *priv)
+
+ iwl_init_scan_params(priv);
+
+- iwl_reset_qos(priv);
+-
+- priv->qos_data.qos_active = 0;
+- priv->qos_data.qos_cap.val = 0;
+-
+ priv->rates_mask = IWL_RATES_MASK;
+ /* Set the tx_power_user_lmt to the lowest power level
+ * this value will get overwritten by channel max power avg
+@@ -2213,12 +2100,6 @@ int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
+ cpu_to_le16((params->txop * 32));
+
+ priv->qos_data.def_qos_parm.ac[q].reserved1 = 0;
+- priv->qos_data.qos_active = 1;
+-
+- if (priv->iw_mode == NL80211_IFTYPE_AP)
+- iwl_activate_qos(priv, 1);
+- else if (priv->assoc_id && iwl_is_associated(priv))
+- iwl_activate_qos(priv, 0);
+
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+@@ -2452,11 +2333,8 @@ int iwl_mac_beacon_update(struct ieee80211_hw *hw, struct sk_buff *skb)
+ IWL_DEBUG_MAC80211(priv, "leave\n");
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+- iwl_reset_qos(priv);
+-
+ priv->cfg->ops->lib->post_associate(priv);
+
+-
+ return 0;
+ }
+ EXPORT_SYMBOL(iwl_mac_beacon_update);
+@@ -2674,6 +2552,15 @@ int iwl_mac_config(struct ieee80211_hw *hw, u32 changed)
+ if (priv->cfg->ops->hcmd->set_rxon_chain)
+ priv->cfg->ops->hcmd->set_rxon_chain(priv);
+
++ if (changed & IEEE80211_CONF_CHANGE_QOS) {
++ bool qos_active = !!(conf->flags & IEEE80211_CONF_QOS);
++
++ spin_lock_irqsave(&priv->lock, flags);
++ priv->qos_data.qos_active = qos_active;
++ iwl_update_qos(priv);
++ spin_unlock_irqrestore(&priv->lock, flags);
++ }
++
+ if (!iwl_is_ready(priv)) {
+ IWL_DEBUG_MAC80211(priv, "leave - not ready\n");
+ goto out;
+@@ -2744,8 +2631,6 @@ void iwl_mac_reset_tsf(struct ieee80211_hw *hw)
+ memset(&priv->current_ht_config, 0, sizeof(struct iwl_ht_info));
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+- iwl_reset_qos(priv);
+-
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->assoc_id = 0;
+ priv->assoc_capability = 0;
+diff --git a/drivers/net/wireless/iwlwifi/iwl-core.h b/drivers/net/wireless/iwlwifi/iwl-core.h
+index 40ec0c1..d5000c7 100644
+--- a/drivers/net/wireless/iwlwifi/iwl-core.h
++++ b/drivers/net/wireless/iwlwifi/iwl-core.h
+@@ -266,8 +266,7 @@ struct iwl_cfg {
+ struct ieee80211_hw *iwl_alloc_all(struct iwl_cfg *cfg,
+ struct ieee80211_ops *hw_ops);
+ void iwl_hw_detect(struct iwl_priv *priv);
+-void iwl_reset_qos(struct iwl_priv *priv);
+-void iwl_activate_qos(struct iwl_priv *priv, u8 force);
++void iwl_activate_qos(struct iwl_priv *priv);
+ int iwl_mac_conf_tx(struct ieee80211_hw *hw, u16 queue,
+ const struct ieee80211_tx_queue_params *params);
+ void iwl_set_rxon_hwcrypto(struct iwl_priv *priv, int hw_decrypt);
+diff --git a/drivers/net/wireless/iwlwifi/iwl-dev.h b/drivers/net/wireless/iwlwifi/iwl-dev.h
+index cea2ee2..24faad7 100644
+--- a/drivers/net/wireless/iwlwifi/iwl-dev.h
++++ b/drivers/net/wireless/iwlwifi/iwl-dev.h
+@@ -514,30 +514,9 @@ struct iwl_ht_info {
+ u8 non_GF_STA_present;
+ };
+
+-union iwl_qos_capabity {
+- struct {
+- u8 edca_count:4; /* bit 0-3 */
+- u8 q_ack:1; /* bit 4 */
+- u8 queue_request:1; /* bit 5 */
+- u8 txop_request:1; /* bit 6 */
+- u8 reserved:1; /* bit 7 */
+- } q_AP;
+- struct {
+- u8 acvo_APSD:1; /* bit 0 */
+- u8 acvi_APSD:1; /* bit 1 */
+- u8 ac_bk_APSD:1; /* bit 2 */
+- u8 ac_be_APSD:1; /* bit 3 */
+- u8 q_ack:1; /* bit 4 */
+- u8 max_len:2; /* bit 5-6 */
+- u8 more_data_ack:1; /* bit 7 */
+- } q_STA;
+- u8 val;
+-};
+-
+ /* QoS structures */
+ struct iwl_qos_info {
+ int qos_active;
+- union iwl_qos_capabity qos_cap;
+ struct iwl_qosparam_cmd def_qos_parm;
+ };
+
+diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
+index 619590d..95447ca 100644
+--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
++++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
+@@ -3091,8 +3091,6 @@ void iwl3945_post_associate(struct iwl_priv *priv)
+ break;
+ }
+
+- iwl_activate_qos(priv, 0);
+-
+ /* we have just associated, don't start scan too early */
+ priv->next_scan_jiffies = jiffies + IWL_DELAY_NEXT_SCAN;
+ }
+@@ -3805,11 +3803,6 @@ static int iwl3945_init_drv(struct iwl_priv *priv)
+
+ priv->iw_mode = NL80211_IFTYPE_STATION;
+
+- iwl_reset_qos(priv);
+-
+- priv->qos_data.qos_active = 0;
+- priv->qos_data.qos_cap.val = 0;
+-
+ priv->rates_mask = IWL_RATES_MASK;
+ priv->tx_power_user_lmt = IWL_DEFAULT_TX_POWER;
+
+--
+1.6.2.5
+
+_______________________________________________
+kernel mailing list
+kernel@lists.fedoraproject.org
+https://admin.fedoraproject.org/mailman/listinfo/kernel
+
diff --git a/mac80211-fix-supported-rates-IE-if-AP-doesnt-give-us-its-rates.patch b/mac80211-fix-supported-rates-IE-if-AP-doesnt-give-us-its-rates.patch
new file mode 100644
index 0000000..947733f
--- /dev/null
+++ b/mac80211-fix-supported-rates-IE-if-AP-doesnt-give-us-its-rates.patch
@@ -0,0 +1,57 @@
+From: Stanislaw Gruszka <sgruszka@redhat.com>
+To: kernel@lists.fedoraproject.org, "John W. Linville" <linville@redhat.com>
+Subject: [PATCH 4/4 2.6.32.y] mac80211: fix supported rates IE if AP doesn't
+ give us it's rates
+Date: Fri, 11 Jun 2010 17:03:16 +0200
+
+If AP do not provide us supported rates before assiociation, send
+all rates we are supporting instead of empty information element.
+
+Signed-off-by: Stanislaw Gruszka <sgruszka@redhat.com>
+---
+ net/mac80211/mlme.c | 17 +++++++++++------
+ 1 files changed, 11 insertions(+), 6 deletions(-)
+
+diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
+index d3950b7..abd62fc 100644
+--- a/net/mac80211/mlme.c
++++ b/net/mac80211/mlme.c
+@@ -269,12 +269,6 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
+ if (wk->bss->wmm_used)
+ wmm = 1;
+
+- /* get all rates supported by the device and the AP as
+- * some APs don't like getting a superset of their rates
+- * in the association request (e.g. D-Link DAP 1353 in
+- * b-only mode) */
+- rates_len = ieee80211_compatible_rates(wk->bss, sband, &rates);
+-
+ if ((wk->bss->cbss.capability & WLAN_CAPABILITY_SPECTRUM_MGMT) &&
+ (local->hw.flags & IEEE80211_HW_SPECTRUM_MGMT))
+ capab |= WLAN_CAPABILITY_SPECTRUM_MGMT;
+@@ -309,6 +303,17 @@ static void ieee80211_send_assoc(struct ieee80211_sub_if_data *sdata,
+ *pos++ = wk->ssid_len;
+ memcpy(pos, wk->ssid, wk->ssid_len);
+
++ if (wk->bss->supp_rates_len) {
++ /* get all rates supported by the device and the AP as
++ * some APs don't like getting a superset of their rates
++ * in the association request (e.g. D-Link DAP 1353 in
++ * b-only mode) */
++ rates_len = ieee80211_compatible_rates(wk->bss, sband, &rates);
++ } else {
++ rates = ~0;
++ rates_len = sband->n_bitrates;
++ }
++
+ /* add all rates which were marked to be used above */
+ supp_rates_len = rates_len;
+ if (supp_rates_len > 8)
+--
+1.6.2.5
+
+_______________________________________________
+kernel mailing list
+kernel@lists.fedoraproject.org
+https://admin.fedoraproject.org/mailman/listinfo/kernel
+
diff --git a/merge.pl b/merge.pl
new file mode 100755
index 0000000..8c31815
--- /dev/null
+++ b/merge.pl
@@ -0,0 +1,66 @@
+#! /usr/bin/perl
+
+my @args=@ARGV;
+my %configvalues;
+my @configoptions;
+my $configcounter = 0;
+
+# optionally print out the architecture as the first line of our output
+my $arch = $args[2];
+if (defined $arch) {
+ print "# $arch\n";
+}
+
+# first, read the override file
+
+open (FILE,"$args[0]") || die "Could not open $args[0]";
+while (<FILE>) {
+ my $str = $_;
+ my $configname;
+
+ if (/\# ([\w]+) is not set/) {
+ $configname = $1;
+ } elsif (/([\w]+)=/) {
+ $configname = $1;
+ }
+
+ if (defined($configname) && !exists($configvalues{$configname})) {
+ $configvalues{$configname} = $str;
+ $configoptions[$configcounter] = $configname;
+ $configcounter ++;
+ }
+};
+
+# now, read and output the entire configfile, except for the overridden
+# parts... for those the new value is printed.
+
+open (FILE2,"$args[1]") || die "Could not open $args[1]";
+while (<FILE2>) {
+ my $configname;
+
+ if (/\# ([\w]+) is not set/) {
+ $configname = $1;
+ } elsif (/([\w]+)=/) {
+ $configname = $1;
+ }
+
+ if (defined($configname) && exists($configvalues{$configname})) {
+ print "$configvalues{$configname}";
+ delete($configvalues{$configname});
+ } else {
+ print "$_";
+ }
+}
+
+# now print the new values from the overridden configfile
+my $counter = 0;
+
+while ($counter < $configcounter) {
+ my $configname = $configoptions[$counter];
+ if (exists($configvalues{$configname})) {
+ print "$configvalues{$configname}";
+ }
+ $counter++;
+}
+
+1;
diff --git a/pci-acpi-disable-aspm-if-no-osc.patch b/pci-acpi-disable-aspm-if-no-osc.patch
new file mode 100644
index 0000000..82f6a9c
--- /dev/null
+++ b/pci-acpi-disable-aspm-if-no-osc.patch
@@ -0,0 +1,55 @@
+From: Matthew Garrett <mjg@redhat.com>
+Subject: ACPI: Disable ASPM if the platform won't provide _OSC control for PCIe
+
+ACPI: Disable ASPM if the platform won't provide _OSC control for PCIe
+
+[ backport to 2.6.32 ]
+
+The PCI SIG documentation for the _OSC OS/firmware handshaking interface
+states:
+
+"If the _OSC control method is absent from the scope of a host bridge
+device, then the operating system must not enable or attempt to use any
+features defined in this section for the hierarchy originated by the host
+bridge."
+
+The obvious interpretation of this is that the OS should not attempt to use
+PCIe hotplug, PME or AER - however, the specification also notes that an
+_OSC method is *required* for PCIe hierarchies, and experimental validation
+with An Alternative OS indicates that it doesn't use any PCIe functionality
+if the _OSC method is missing. That arguably means we shouldn't be using
+MSI or extended config space, but right now our problems seem to be limited
+to vendors being surprised when ASPM gets enabled on machines when other
+OSs refuse to do so. So, for now, let's just disable ASPM if the _OSC
+method doesn't exist or refuses to hand over PCIe capability control.
+
+Signed-off-by: Matthew Garrett <mjg@redhat.com>
+---
+
+diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
+index 4eac593..1f67057 100644
+--- a/drivers/acpi/pci_root.c
++++ b/drivers/acpi/pci_root.c
+@@ -33,6 +33,7 @@
+ #include <linux/pm.h>
+ #include <linux/pci.h>
+ #include <linux/pci-acpi.h>
++#include <linux/pci-aspm.h>
+ #include <linux/acpi.h>
+ #include <acpi/acpi_bus.h>
+ #include <acpi/acpi_drivers.h>
+@@ -543,6 +544,14 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
+ if (flags != base_flags)
+ acpi_pci_osc_support(root, flags);
+
++ status = acpi_pci_osc_control_set(root->device->handle,
++ 0);
++
++ if (status == AE_NOT_EXIST) {
++ printk(KERN_INFO "Unable to assume PCIe control: Disabling ASPM\n");
++ pcie_no_aspm();
++ }
++
+ return 0;
+
+ end: \ No newline at end of file
diff --git a/pci-aspm-dont-enable-too-early.patch b/pci-aspm-dont-enable-too-early.patch
new file mode 100644
index 0000000..ea91a25
--- /dev/null
+++ b/pci-aspm-dont-enable-too-early.patch
@@ -0,0 +1,50 @@
+From: Matthew Garrett <mjg@redhat.com>
+Date: Wed, 9 Jun 2010 20:05:07 +0000 (-0400)
+Subject: PCI: Don't enable aspm before drivers have had a chance to veto it
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Fjbarnes%2Fpci-2.6.git;a=commitdiff_plain;h=8f0b08c29f1df91315e48adce04462eb23671099
+
+PCI: Don't enable aspm before drivers have had a chance to veto it
+
+The aspm code will currently set the configured aspm policy before drivers
+have had an opportunity to indicate that their hardware doesn't support it.
+Unfortunately, putting some hardware in L0 or L1 can result in the hardware
+no longer responding to any requests, even after aspm is disabled. It makes
+more sense to leave aspm policy at the BIOS defaults at initial setup time,
+reconfiguring it after pci_enable_device() is called. This allows the
+driver to blacklist individual devices beforehand.
+
+Reviewed-by: Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com>
+Signed-off-by: Matthew Garrett <mjg@redhat.com>
+Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
+---
+
+diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
+index be53d98..7122281 100644
+--- a/drivers/pci/pcie/aspm.c
++++ b/drivers/pci/pcie/aspm.c
+@@ -588,11 +588,23 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev)
+ * update through pcie_aspm_cap_init().
+ */
+ pcie_aspm_cap_init(link, blacklist);
+- pcie_config_aspm_path(link);
+
+ /* Setup initial Clock PM state */
+ pcie_clkpm_cap_init(link, blacklist);
+- pcie_set_clkpm(link, policy_to_clkpm_state(link));
++
++ /*
++ * At this stage drivers haven't had an opportunity to change the
++ * link policy setting. Enabling ASPM on broken hardware can cripple
++ * it even before the driver has had a chance to disable ASPM, so
++ * default to a safe level right now. If we're enabling ASPM beyond
++ * the BIOS's expectation, we'll do so once pci_enable_device() is
++ * called.
++ */
++ if (aspm_policy != POLICY_POWERSAVE) {
++ pcie_config_aspm_path(link);
++ pcie_set_clkpm(link, policy_to_clkpm_state(link));
++ }
++
+ unlock:
+ mutex_unlock(&aspm_lock);
+ out:
diff --git a/perf b/perf
new file mode 100644
index 0000000..ea89806
--- /dev/null
+++ b/perf
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# In pathological situations, this will print some error about uname.
+kverrel="`uname -r`" || exit
+
+exec "/usr/libexec/perf.$kverrel" ${1+"$@"}
+rc=$?
+
+# We're still here, so the exec failed.
+echo >&2 "Sorry, your kernel ($kverrel) doesn't support perf."
+
+exit $rc
diff --git a/sched-fix-over-scheduling-bug.patch b/sched-fix-over-scheduling-bug.patch
new file mode 100644
index 0000000..b09c101
--- /dev/null
+++ b/sched-fix-over-scheduling-bug.patch
@@ -0,0 +1,60 @@
+From: Alex,Shi <alex.shi@intel.com>
+Date: Thu, 17 Jun 2010 06:08:13 +0000 (+0800)
+Subject: sched: Fix over-scheduling bug
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=3c93717cfa51316e4dbb471e7c0f9d243359d5f8
+
+sched: Fix over-scheduling bug
+
+Commit e70971591 ("sched: Optimize unused cgroup configuration") introduced
+an imbalanced scheduling bug. [[ in 2.6.32-rc1 ]]
+
+If we do not use CGROUP, function update_h_load won't update h_load. When the
+system has a large number of tasks far more than logical CPU number, the
+incorrect cfs_rq[cpu]->h_load value will cause load_balance() to pull too
+many tasks to the local CPU from the busiest CPU. So the busiest CPU keeps
+going in a round robin. That will hurt performance.
+
+The issue was found originally by a scientific calculation workload that
+developed by Yanmin. With that commit, the workload performance drops
+about 40%.
+
+ CPU before after
+
+ 00 : 2 : 7
+ 01 : 1 : 7
+ 02 : 11 : 6
+ 03 : 12 : 7
+ 04 : 6 : 6
+ 05 : 11 : 7
+ 06 : 10 : 6
+ 07 : 12 : 7
+ 08 : 11 : 6
+ 09 : 12 : 6
+ 10 : 1 : 6
+ 11 : 1 : 6
+ 12 : 6 : 6
+ 13 : 2 : 6
+ 14 : 2 : 6
+ 15 : 1 : 6
+
+Reviewed-by: Yanmin zhang <yanmin.zhang@intel.com>
+Signed-off-by: Alex Shi <alex.shi@intel.com>
+Signed-off-by: Peter Zijlstra <a.p.zijlstra@chello.nl>
+LKML-Reference: <1276754893.9452.5442.camel@debian>
+Signed-off-by: Ingo Molnar <mingo@elte.hu>
+---
+
+diff --git a/kernel/sched.c b/kernel/sched.c
+index 2aaceeb..6c9e7c8 100644
+--- a/kernel/sched.c
++++ b/kernel/sched.c
+@@ -1657,9 +1657,6 @@ static void update_shares(struct sched_domain *sd)
+
+ static void update_h_load(long cpu)
+ {
+- if (root_task_group_empty())
+- return;
+-
+ walk_tg_tree(tg_load_down, tg_nop, (void *)cpu);
+ }
+
diff --git a/sky2-optima-add-missing-write-bits.patch b/sky2-optima-add-missing-write-bits.patch
new file mode 100644
index 0000000..98b924d
--- /dev/null
+++ b/sky2-optima-add-missing-write-bits.patch
@@ -0,0 +1,46 @@
+From: Takashi Iwai <tiwai@suse.de>
+Date: Thu, 3 Dec 2009 05:12:02 +0000 (+0000)
+Subject: net: Add missing TST_CFG_WRITE bits around sky2_pci_write
+X-Git-Tag: v2.6.33-rc1~59^2~38
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=d66f0b20b2f8eac365fadf5ca492efe4ba539446
+
+net: Add missing TST_CFG_WRITE bits around sky2_pci_write
+
+Add missing TST_CFG_WRITE bits around sky2_pci_write*() in Optima
+setup routines. Without the cfg-write bits, the driver may spew endless
+link-up messages through qlink irq.
+
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
+index 050e6b5..013c9f5 100644
+--- a/drivers/net/sky2.c
++++ b/drivers/net/sky2.c
+@@ -2152,7 +2152,9 @@ static void sky2_qlink_intr(struct sky2_hw *hw)
+
+ /* reset PHY Link Detect */
+ phy = sky2_pci_read16(hw, PSM_CONFIG_REG4);
++ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+ sky2_pci_write16(hw, PSM_CONFIG_REG4, phy | 1);
++ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+
+ sky2_link_up(sky2);
+ }
+@@ -3082,6 +3084,7 @@ static void sky2_reset(struct sky2_hw *hw)
+ reg <<= PSM_CONFIG_REG4_TIMER_PHY_LINK_DETECT_BASE;
+
+ /* reset PHY Link Detect */
++ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
+ sky2_pci_write16(hw, PSM_CONFIG_REG4,
+ reg | PSM_CONFIG_REG4_RST_PHY_LINK_DETECT);
+ sky2_pci_write16(hw, PSM_CONFIG_REG4, reg);
+@@ -3099,6 +3102,7 @@ static void sky2_reset(struct sky2_hw *hw)
+ /* restore the PCIe Link Control register */
+ sky2_pci_write16(hw, cap + PCI_EXP_LNKCTL, reg);
+ }
++ sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
+
+ /* re-enable PEX PM in PEX PHY debug reg. 8 (clear bit 12) */
+ sky2_write32(hw, Y2_PEX_PHY_DATA, PEX_DB_ACCESS | (0x08UL << 16));
diff --git a/sky2-optima-add-register-definitions.patch b/sky2-optima-add-register-definitions.patch
new file mode 100644
index 0000000..78311ff
--- /dev/null
+++ b/sky2-optima-add-register-definitions.patch
@@ -0,0 +1,281 @@
+From: Stephen Hemminger <shemminger@vyatta.com>
+Date: Thu, 29 Oct 2009 06:37:06 +0000 (+0000)
+Subject: sky2: add register definitions for new chips
+X-Git-Tag: v2.6.33-rc1~388^2~591
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=e91cd2e65f22a80af87367178bed4957fdc45ecd
+
+sky2: add register definitions for new chips
+
+This adds infrastructure for the newer chip versions and workarounds.
+Extracted from the vendor (GPL) driver.
+
+Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h
+index ed54129..e13da94 100644
+--- a/drivers/net/sky2.h
++++ b/drivers/net/sky2.h
+@@ -16,6 +16,13 @@ enum {
+ PCI_DEV_REG5 = 0x88,
+ PCI_CFG_REG_0 = 0x90,
+ PCI_CFG_REG_1 = 0x94,
++
++ PSM_CONFIG_REG0 = 0x98,
++ PSM_CONFIG_REG1 = 0x9C,
++ PSM_CONFIG_REG2 = 0x160,
++ PSM_CONFIG_REG3 = 0x164,
++ PSM_CONFIG_REG4 = 0x168,
++
+ };
+
+ /* Yukon-2 */
+@@ -48,6 +55,37 @@ enum pci_dev_reg_2 {
+ PCI_USEDATA64 = 1<<0, /* Use 64Bit Data bus ext */
+ };
+
++/* PCI_OUR_REG_3 32 bit Our Register 3 (Yukon-ECU only) */
++enum pci_dev_reg_3 {
++ P_CLK_ASF_REGS_DIS = 1<<18,/* Disable Clock ASF (Yukon-Ext.) */
++ P_CLK_COR_REGS_D0_DIS = 1<<17,/* Disable Clock Core Regs D0 */
++ P_CLK_MACSEC_DIS = 1<<17,/* Disable Clock MACSec (Yukon-Ext.) */
++ P_CLK_PCI_REGS_D0_DIS = 1<<16,/* Disable Clock PCI Regs D0 */
++ P_CLK_COR_YTB_ARB_DIS = 1<<15,/* Disable Clock YTB Arbiter */
++ P_CLK_MAC_LNK1_D3_DIS = 1<<14,/* Disable Clock MAC Link1 D3 */
++ P_CLK_COR_LNK1_D0_DIS = 1<<13,/* Disable Clock Core Link1 D0 */
++ P_CLK_MAC_LNK1_D0_DIS = 1<<12,/* Disable Clock MAC Link1 D0 */
++ P_CLK_COR_LNK1_D3_DIS = 1<<11,/* Disable Clock Core Link1 D3 */
++ P_CLK_PCI_MST_ARB_DIS = 1<<10,/* Disable Clock PCI Master Arb. */
++ P_CLK_COR_REGS_D3_DIS = 1<<9, /* Disable Clock Core Regs D3 */
++ P_CLK_PCI_REGS_D3_DIS = 1<<8, /* Disable Clock PCI Regs D3 */
++ P_CLK_REF_LNK1_GM_DIS = 1<<7, /* Disable Clock Ref. Link1 GMAC */
++ P_CLK_COR_LNK1_GM_DIS = 1<<6, /* Disable Clock Core Link1 GMAC */
++ P_CLK_PCI_COMMON_DIS = 1<<5, /* Disable Clock PCI Common */
++ P_CLK_COR_COMMON_DIS = 1<<4, /* Disable Clock Core Common */
++ P_CLK_PCI_LNK1_BMU_DIS = 1<<3, /* Disable Clock PCI Link1 BMU */
++ P_CLK_COR_LNK1_BMU_DIS = 1<<2, /* Disable Clock Core Link1 BMU */
++ P_CLK_PCI_LNK1_BIU_DIS = 1<<1, /* Disable Clock PCI Link1 BIU */
++ P_CLK_COR_LNK1_BIU_DIS = 1<<0, /* Disable Clock Core Link1 BIU */
++ PCIE_OUR3_WOL_D3_COLD_SET = P_CLK_ASF_REGS_DIS |
++ P_CLK_COR_REGS_D0_DIS |
++ P_CLK_COR_LNK1_D0_DIS |
++ P_CLK_MAC_LNK1_D0_DIS |
++ P_CLK_PCI_MST_ARB_DIS |
++ P_CLK_COR_COMMON_DIS |
++ P_CLK_COR_LNK1_BMU_DIS,
++};
++
+ /* PCI_OUR_REG_4 32 bit Our Register 4 (Yukon-ECU only) */
+ enum pci_dev_reg_4 {
+ /* (Link Training & Status State Machine) */
+@@ -114,7 +152,7 @@ enum pci_dev_reg_5 {
+ P_GAT_PCIE_RX_EL_IDLE,
+ };
+
+-#/* PCI_CFG_REG_1 32 bit Config Register 1 (Yukon-Ext only) */
++/* PCI_CFG_REG_1 32 bit Config Register 1 (Yukon-Ext only) */
+ enum pci_cfg_reg1 {
+ P_CF1_DIS_REL_EVT_RST = 1<<24, /* Dis. Rel. Event during PCIE reset */
+ /* Bit 23..21: Release Clock on Event */
+@@ -145,6 +183,72 @@ enum pci_cfg_reg1 {
+ P_CF1_ENA_TXBMU_WR_IDLE,
+ };
+
++/* Yukon-Optima */
++enum {
++ PSM_CONFIG_REG1_AC_PRESENT_STATUS = 1<<31, /* AC Present Status */
++
++ PSM_CONFIG_REG1_PTP_CLK_SEL = 1<<29, /* PTP Clock Select */
++ PSM_CONFIG_REG1_PTP_MODE = 1<<28, /* PTP Mode */
++
++ PSM_CONFIG_REG1_MUX_PHY_LINK = 1<<27, /* PHY Energy Detect Event */
++
++ PSM_CONFIG_REG1_EN_PIN63_AC_PRESENT = 1<<26, /* Enable LED_DUPLEX for ac_present */
++ PSM_CONFIG_REG1_EN_PCIE_TIMER = 1<<25, /* Enable PCIe Timer */
++ PSM_CONFIG_REG1_EN_SPU_TIMER = 1<<24, /* Enable SPU Timer */
++ PSM_CONFIG_REG1_POLARITY_AC_PRESENT = 1<<23, /* AC Present Polarity */
++
++ PSM_CONFIG_REG1_EN_AC_PRESENT = 1<<21, /* Enable AC Present */
++
++ PSM_CONFIG_REG1_EN_GPHY_INT_PSM = 1<<20, /* Enable GPHY INT for PSM */
++ PSM_CONFIG_REG1_DIS_PSM_TIMER = 1<<19, /* Disable PSM Timer */
++};
++
++/* Yukon-Supreme */
++enum {
++ PSM_CONFIG_REG1_GPHY_ENERGY_STS = 1<<31, /* GPHY Energy Detect Status */
++
++ PSM_CONFIG_REG1_UART_MODE_MSK = 3<<29, /* UART_Mode */
++ PSM_CONFIG_REG1_CLK_RUN_ASF = 1<<28, /* Enable Clock Free Running for ASF Subsystem */
++ PSM_CONFIG_REG1_UART_CLK_DISABLE= 1<<27, /* Disable UART clock */
++ PSM_CONFIG_REG1_VAUX_ONE = 1<<26, /* Tie internal Vaux to 1'b1 */
++ PSM_CONFIG_REG1_UART_FC_RI_VAL = 1<<25, /* Default value for UART_RI_n */
++ PSM_CONFIG_REG1_UART_FC_DCD_VAL = 1<<24, /* Default value for UART_DCD_n */
++ PSM_CONFIG_REG1_UART_FC_DSR_VAL = 1<<23, /* Default value for UART_DSR_n */
++ PSM_CONFIG_REG1_UART_FC_CTS_VAL = 1<<22, /* Default value for UART_CTS_n */
++ PSM_CONFIG_REG1_LATCH_VAUX = 1<<21, /* Enable Latch current Vaux_avlbl */
++ PSM_CONFIG_REG1_FORCE_TESTMODE_INPUT= 1<<20, /* Force Testmode pin as input PAD */
++ PSM_CONFIG_REG1_UART_RST = 1<<19, /* UART_RST */
++ PSM_CONFIG_REG1_PSM_PCIE_L1_POL = 1<<18, /* PCIE L1 Event Polarity for PSM */
++ PSM_CONFIG_REG1_TIMER_STAT = 1<<17, /* PSM Timer Status */
++ PSM_CONFIG_REG1_GPHY_INT = 1<<16, /* GPHY INT Status */
++ PSM_CONFIG_REG1_FORCE_TESTMODE_ZERO= 1<<15, /* Force internal Testmode as 1'b0 */
++ PSM_CONFIG_REG1_EN_INT_ASPM_CLKREQ = 1<<14, /* ENABLE INT for CLKRUN on ASPM and CLKREQ */
++ PSM_CONFIG_REG1_EN_SND_TASK_ASPM_CLKREQ = 1<<13, /* ENABLE Snd_task for CLKRUN on ASPM and CLKREQ */
++ PSM_CONFIG_REG1_DIS_CLK_GATE_SND_TASK = 1<<12, /* Disable CLK_GATE control snd_task */
++ PSM_CONFIG_REG1_DIS_FF_CHIAN_SND_INTA = 1<<11, /* Disable flip-flop chain for sndmsg_inta */
++
++ PSM_CONFIG_REG1_DIS_LOADER = 1<<9, /* Disable Loader SM after PSM Goes back to IDLE */
++ PSM_CONFIG_REG1_DO_PWDN = 1<<8, /* Do Power Down, Start PSM Scheme */
++ PSM_CONFIG_REG1_DIS_PIG = 1<<7, /* Disable Plug-in-Go SM after PSM Goes back to IDLE */
++ PSM_CONFIG_REG1_DIS_PERST = 1<<6, /* Disable Internal PCIe Reset after PSM Goes back to IDLE */
++ PSM_CONFIG_REG1_EN_REG18_PD = 1<<5, /* Enable REG18 Power Down for PSM */
++ PSM_CONFIG_REG1_EN_PSM_LOAD = 1<<4, /* Disable EEPROM Loader after PSM Goes back to IDLE */
++ PSM_CONFIG_REG1_EN_PSM_HOT_RST = 1<<3, /* Enable PCIe Hot Reset for PSM */
++ PSM_CONFIG_REG1_EN_PSM_PERST = 1<<2, /* Enable PCIe Reset Event for PSM */
++ PSM_CONFIG_REG1_EN_PSM_PCIE_L1 = 1<<1, /* Enable PCIe L1 Event for PSM */
++ PSM_CONFIG_REG1_EN_PSM = 1<<0, /* Enable PSM Scheme */
++};
++
++/* PSM_CONFIG_REG4 0x0168 PSM Config Register 4 */
++enum {
++ /* PHY Link Detect Timer */
++ PSM_CONFIG_REG4_TIMER_PHY_LINK_DETECT_MSK = 0xf<<4,
++ PSM_CONFIG_REG4_TIMER_PHY_LINK_DETECT_BASE = 4,
++
++ PSM_CONFIG_REG4_DEBUG_TIMER = 1<<1, /* Debug Timer */
++ PSM_CONFIG_REG4_RST_PHY_LINK_DETECT = 1<<0, /* Reset GPHY Link Detect */
++};
++
+
+ #define PCI_STATUS_ERROR_BITS (PCI_STATUS_DETECTED_PARITY | \
+ PCI_STATUS_SIG_SYSTEM_ERROR | \
+@@ -197,6 +301,9 @@ enum csr_regs {
+ B2_I2C_IRQ = 0x0168,
+ B2_I2C_SW = 0x016c,
+
++ Y2_PEX_PHY_DATA = 0x0170,
++ Y2_PEX_PHY_ADDR = 0x0172,
++
+ B3_RAM_ADDR = 0x0180,
+ B3_RAM_DATA_LO = 0x0184,
+ B3_RAM_DATA_HI = 0x0188,
+@@ -317,6 +424,10 @@ enum {
+ Y2_IS_CHK_TXS2 = 1<<9, /* Descriptor error TXS 2 */
+ Y2_IS_CHK_TXA2 = 1<<8, /* Descriptor error TXA 2 */
+
++ Y2_IS_PSM_ACK = 1<<7, /* PSM Acknowledge (Yukon-Optima only) */
++ Y2_IS_PTP_TIST = 1<<6, /* PTP Time Stamp (Yukon-Optima only) */
++ Y2_IS_PHY_QLNK = 1<<5, /* PHY Quick Link (Yukon-Optima only) */
++
+ Y2_IS_IRQ_PHY1 = 1<<4, /* Interrupt from PHY 1 */
+ Y2_IS_IRQ_MAC1 = 1<<3, /* Interrupt from MAC 1 */
+ Y2_IS_CHK_RX1 = 1<<2, /* Descriptor error Rx 1 */
+@@ -435,6 +546,7 @@ enum {
+ CHIP_ID_YUKON_FE_P = 0xb8, /* YUKON-2 FE+ */
+ CHIP_ID_YUKON_SUPR = 0xb9, /* YUKON-2 Supreme */
+ CHIP_ID_YUKON_UL_2 = 0xba, /* YUKON-2 Ultra 2 */
++ CHIP_ID_YUKON_OPT = 0xbc, /* YUKON-2 Optima */
+ };
+ enum yukon_ec_rev {
+ CHIP_REV_YU_EC_A1 = 0, /* Chip Rev. for Yukon-EC A1/A0 */
+@@ -459,6 +571,8 @@ enum yukon_ex_rev {
+ };
+ enum yukon_supr_rev {
+ CHIP_REV_YU_SU_A0 = 0,
++ CHIP_REV_YU_SU_B0 = 1,
++ CHIP_REV_YU_SU_B1 = 3,
+ };
+
+
+@@ -513,6 +627,12 @@ enum {
+ TIM_T_STEP = 1<<0, /* Test step */
+ };
+
++/* Y2_PEX_PHY_ADDR/DATA PEX PHY address and data reg (Yukon-2 only) */
++enum {
++ PEX_RD_ACCESS = 1<<31, /* Access Mode Read = 1, Write = 0 */
++ PEX_DB_ACCESS = 1<<30, /* Access to debug register */
++};
++
+ /* B3_RAM_ADDR 32 bit RAM Address, to read or write */
+ /* Bit 31..19: reserved */
+ #define RAM_ADR_RAN 0x0007ffffL /* Bit 18.. 0: RAM Address Range */
+@@ -754,6 +874,42 @@ enum {
+ BMU_TX_CLR_IRQ_TCP = 1<<11, /* Clear IRQ on TCP segment length mismatch */
+ };
+
++/* TBMU_TEST 0x06B8 Transmit BMU Test Register */
++enum {
++ TBMU_TEST_BMU_TX_CHK_AUTO_OFF = 1<<31, /* BMU Tx Checksum Auto Calculation Disable */
++ TBMU_TEST_BMU_TX_CHK_AUTO_ON = 1<<30, /* BMU Tx Checksum Auto Calculation Enable */
++ TBMU_TEST_HOME_ADD_PAD_FIX1_EN = 1<<29, /* Home Address Paddiing FIX1 Enable */
++ TBMU_TEST_HOME_ADD_PAD_FIX1_DIS = 1<<28, /* Home Address Paddiing FIX1 Disable */
++ TBMU_TEST_ROUTING_ADD_FIX_EN = 1<<27, /* Routing Address Fix Enable */
++ TBMU_TEST_ROUTING_ADD_FIX_DIS = 1<<26, /* Routing Address Fix Disable */
++ TBMU_TEST_HOME_ADD_FIX_EN = 1<<25, /* Home address checksum fix enable */
++ TBMU_TEST_HOME_ADD_FIX_DIS = 1<<24, /* Home address checksum fix disable */
++
++ TBMU_TEST_TEST_RSPTR_ON = 1<<22, /* Testmode Shadow Read Ptr On */
++ TBMU_TEST_TEST_RSPTR_OFF = 1<<21, /* Testmode Shadow Read Ptr Off */
++ TBMU_TEST_TESTSTEP_RSPTR = 1<<20, /* Teststep Shadow Read Ptr */
++
++ TBMU_TEST_TEST_RPTR_ON = 1<<18, /* Testmode Read Ptr On */
++ TBMU_TEST_TEST_RPTR_OFF = 1<<17, /* Testmode Read Ptr Off */
++ TBMU_TEST_TESTSTEP_RPTR = 1<<16, /* Teststep Read Ptr */
++
++ TBMU_TEST_TEST_WSPTR_ON = 1<<14, /* Testmode Shadow Write Ptr On */
++ TBMU_TEST_TEST_WSPTR_OFF = 1<<13, /* Testmode Shadow Write Ptr Off */
++ TBMU_TEST_TESTSTEP_WSPTR = 1<<12, /* Teststep Shadow Write Ptr */
++
++ TBMU_TEST_TEST_WPTR_ON = 1<<10, /* Testmode Write Ptr On */
++ TBMU_TEST_TEST_WPTR_OFF = 1<<9, /* Testmode Write Ptr Off */
++ TBMU_TEST_TESTSTEP_WPTR = 1<<8, /* Teststep Write Ptr */
++
++ TBMU_TEST_TEST_REQ_NB_ON = 1<<6, /* Testmode Req Nbytes/Addr On */
++ TBMU_TEST_TEST_REQ_NB_OFF = 1<<5, /* Testmode Req Nbytes/Addr Off */
++ TBMU_TEST_TESTSTEP_REQ_NB = 1<<4, /* Teststep Req Nbytes/Addr */
++
++ TBMU_TEST_TEST_DONE_IDX_ON = 1<<2, /* Testmode Done Index On */
++ TBMU_TEST_TEST_DONE_IDX_OFF = 1<<1, /* Testmode Done Index Off */
++ TBMU_TEST_TESTSTEP_DONE_IDX = 1<<0, /* Teststep Done Index */
++};
++
+ /* Queue Prefetch Unit Offsets, use Y2_QADDR() to address (Yukon-2 only)*/
+ /* PREF_UNIT_CTRL 32 bit Prefetch Control register */
+ enum {
+@@ -1674,6 +1830,12 @@ enum {
+
+ /* RX_GMF_CTRL_T 32 bit Rx GMAC FIFO Control/Test */
+ enum {
++ RX_GCLKMAC_ENA = 1<<31, /* RX MAC Clock Gating Enable */
++ RX_GCLKMAC_OFF = 1<<30,
++
++ RX_STFW_DIS = 1<<29, /* RX Store and Forward Enable */
++ RX_STFW_ENA = 1<<28,
++
+ RX_TRUNC_ON = 1<<27, /* enable packet truncation */
+ RX_TRUNC_OFF = 1<<26, /* disable packet truncation */
+ RX_VLAN_STRIP_ON = 1<<25, /* enable VLAN stripping */
+@@ -1711,6 +1873,20 @@ enum {
+ GMF_RX_CTRL_DEF = GMF_OPER_ON | GMF_RX_F_FL_ON,
+ };
+
++/* RX_GMF_FL_CTRL 16 bit Rx GMAC FIFO Flush Control (Yukon-Supreme) */
++enum {
++ RX_IPV6_SA_MOB_ENA = 1<<9, /* IPv6 SA Mobility Support Enable */
++ RX_IPV6_SA_MOB_DIS = 1<<8, /* IPv6 SA Mobility Support Disable */
++ RX_IPV6_DA_MOB_ENA = 1<<7, /* IPv6 DA Mobility Support Enable */
++ RX_IPV6_DA_MOB_DIS = 1<<6, /* IPv6 DA Mobility Support Disable */
++ RX_PTR_SYNCDLY_ENA = 1<<5, /* Pointers Delay Synch Enable */
++ RX_PTR_SYNCDLY_DIS = 1<<4, /* Pointers Delay Synch Disable */
++ RX_ASF_NEWFLAG_ENA = 1<<3, /* RX ASF Flag New Logic Enable */
++ RX_ASF_NEWFLAG_DIS = 1<<2, /* RX ASF Flag New Logic Disable */
++ RX_FLSH_MISSPKT_ENA = 1<<1, /* RX Flush Miss-Packet Enable */
++ RX_FLSH_MISSPKT_DIS = 1<<0, /* RX Flush Miss-Packet Disable */
++};
++
+ /* TX_GMF_EA 32 bit Tx GMAC FIFO End Address */
+ enum {
+ TX_DYN_WM_ENA = 3, /* Yukon-FE+ specific */
diff --git a/sky2-optima-fix-tcp-offload.patch b/sky2-optima-fix-tcp-offload.patch
new file mode 100644
index 0000000..d0f78ed
--- /dev/null
+++ b/sky2-optima-fix-tcp-offload.patch
@@ -0,0 +1,33 @@
+From: Takashi Iwai <tiwai@suse.de>
+Date: Thu, 3 Dec 2009 05:12:01 +0000 (+0000)
+Subject: net: Fix Yukon-2 Optima TCP offload setup
+X-Git-Tag: v2.6.33-rc1~59^2~39
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=b338682dc5c20e8ff986e58407bdb6e3a3e3f0a3
+
+net: Fix Yukon-2 Optima TCP offload setup
+
+Fix the TCP offload setup for Yukon-2 Optima.
+It requires SKY2_HW_NE_LE flag unlike Ultra 2.
+
+Signed-off-by: Takashi Iwai <tiwai@suse.de>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
+index 3943d89..050e6b5 100644
+--- a/drivers/net/sky2.c
++++ b/drivers/net/sky2.c
+@@ -2968,8 +2968,13 @@ static int __devinit sky2_init(struct sky2_hw *hw)
+ break;
+
+ case CHIP_ID_YUKON_UL_2:
++ hw->flags = SKY2_HW_GIGABIT
++ | SKY2_HW_ADV_POWER_CTL;
++ break;
++
+ case CHIP_ID_YUKON_OPT:
+ hw->flags = SKY2_HW_GIGABIT
++ | SKY2_HW_NEW_LE
+ | SKY2_HW_ADV_POWER_CTL;
+ break;
+
diff --git a/sky2-optima-print-chip-name.patch b/sky2-optima-print-chip-name.patch
new file mode 100644
index 0000000..0a2e148
--- /dev/null
+++ b/sky2-optima-print-chip-name.patch
@@ -0,0 +1,27 @@
+From: stephen hemminger <shemminger@vyatta.com>
+Date: Mon, 14 Dec 2009 08:33:47 +0000 (+0000)
+Subject: sky2: print Optima chip name
+X-Git-Tag: v2.6.33-rc1~59^2~9
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=dae3a5112d258764cad9e48439ca7dd05c2edca1
+
+sky2: print Optima chip name
+
+Off by one in name lookup makes Optima display as (chip 0xbc)
+
+Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
+index 89a05d6..9431f64 100644
+--- a/drivers/net/sky2.c
++++ b/drivers/net/sky2.c
+@@ -4530,7 +4530,7 @@ static const char *sky2_name(u8 chipid, char *buf, int sz)
+ "Optima", /* 0xbc */
+ };
+
+- if (chipid >= CHIP_ID_YUKON_XL && chipid < CHIP_ID_YUKON_OPT)
++ if (chipid >= CHIP_ID_YUKON_XL && chipid <= CHIP_ID_YUKON_OPT)
+ strncpy(buf, name[chipid - CHIP_ID_YUKON_XL], sz);
+ else
+ snprintf(buf, sz, "(chip %#x)", chipid);
diff --git a/sky2-optima-support.patch b/sky2-optima-support.patch
new file mode 100644
index 0000000..f56f9ba
--- /dev/null
+++ b/sky2-optima-support.patch
@@ -0,0 +1,157 @@
+From: Stephen Hemminger <shemminger@vyatta.com>
+Date: Thu, 29 Oct 2009 06:37:09 +0000 (+0000)
+Subject: sky2: 88E8059 support
+X-Git-Tag: v2.6.33-rc1~388^2~588
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=0f5aac7070a01ec757ed243febe4fff7c944c4d2
+
+sky2: 88E8059 support
+
+Tentative support for newer Marvell hardware including
+the Yukon-2 Optima chip. Do not have hatdware to test this yet,
+code is based on vendor driver.
+
+Signed-off-by: Stephen Hemminger <shemminger@vyatta.com>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
+index 3387a2f..53cce74 100644
+--- a/drivers/net/sky2.c
++++ b/drivers/net/sky2.c
+@@ -140,6 +140,7 @@ static DEFINE_PCI_DEVICE_TABLE(sky2_id_table) = {
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x436D) }, /* 88E8055 */
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4370) }, /* 88E8075 */
+ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4380) }, /* 88E8057 */
++ { PCI_DEVICE(PCI_VENDOR_ID_MARVELL, 0x4381) }, /* 88E8059 */
+ { 0 }
+ };
+
+@@ -603,6 +604,16 @@ static void sky2_phy_init(struct sky2_hw *hw, unsigned port)
+ /* apply workaround for integrated resistors calibration */
+ gm_phy_write(hw, port, PHY_MARV_PAGE_ADDR, 17);
+ gm_phy_write(hw, port, PHY_MARV_PAGE_DATA, 0x3f60);
++ } else if (hw->chip_id == CHIP_ID_YUKON_OPT && hw->chip_rev == 0) {
++ /* apply fixes in PHY AFE */
++ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0x00ff);
++
++ /* apply RDAC termination workaround */
++ gm_phy_write(hw, port, 24, 0x2800);
++ gm_phy_write(hw, port, 23, 0x2001);
++
++ /* set page register back to 0 */
++ gm_phy_write(hw, port, PHY_MARV_EXT_ADR, 0);
+ } else if (hw->chip_id != CHIP_ID_YUKON_EX &&
+ hw->chip_id < CHIP_ID_YUKON_SUPR) {
+ /* no effect on Yukon-XL */
+@@ -2127,6 +2138,25 @@ out:
+ spin_unlock(&sky2->phy_lock);
+ }
+
++/* Special quick link interrupt (Yukon-2 Optima only) */
++static void sky2_qlink_intr(struct sky2_hw *hw)
++{
++ struct sky2_port *sky2 = netdev_priv(hw->dev[0]);
++ u32 imask;
++ u16 phy;
++
++ /* disable irq */
++ imask = sky2_read32(hw, B0_IMSK);
++ imask &= ~Y2_IS_PHY_QLNK;
++ sky2_write32(hw, B0_IMSK, imask);
++
++ /* reset PHY Link Detect */
++ phy = sky2_pci_read16(hw, PSM_CONFIG_REG4);
++ sky2_pci_write16(hw, PSM_CONFIG_REG4, phy | 1);
++
++ sky2_link_up(sky2);
++}
++
+ /* Transmit timeout is only called if we are running, carrier is up
+ * and tx queue is full (stopped).
+ */
+@@ -2796,6 +2826,9 @@ static int sky2_poll(struct napi_struct *napi, int work_limit)
+ if (status & Y2_IS_IRQ_PHY2)
+ sky2_phy_intr(hw, 1);
+
++ if (status & Y2_IS_PHY_QLNK)
++ sky2_qlink_intr(hw);
++
+ while ((idx = sky2_read16(hw, STAT_PUT_IDX)) != hw->st_idx) {
+ work_done += sky2_status_intr(hw, work_limit - work_done, idx);
+
+@@ -2845,6 +2878,7 @@ static u32 sky2_mhz(const struct sky2_hw *hw)
+ case CHIP_ID_YUKON_EX:
+ case CHIP_ID_YUKON_SUPR:
+ case CHIP_ID_YUKON_UL_2:
++ case CHIP_ID_YUKON_OPT:
+ return 125;
+
+ case CHIP_ID_YUKON_FE:
+@@ -2934,6 +2968,7 @@ static int __devinit sky2_init(struct sky2_hw *hw)
+ break;
+
+ case CHIP_ID_YUKON_UL_2:
++ case CHIP_ID_YUKON_OPT:
+ hw->flags = SKY2_HW_GIGABIT
+ | SKY2_HW_ADV_POWER_CTL;
+ break;
+@@ -3024,6 +3059,46 @@ static void sky2_reset(struct sky2_hw *hw)
+ sky2_pci_write32(hw, PCI_DEV_REG3, P_CLK_MACSEC_DIS);
+ }
+
++ if (hw->chip_id == CHIP_ID_YUKON_OPT) {
++ u16 reg;
++ u32 msk;
++
++ if (hw->chip_rev == 0) {
++ /* disable PCI-E PHY power down (set PHY reg 0x80, bit 7 */
++ sky2_write32(hw, Y2_PEX_PHY_DATA, (0x80UL << 16) | (1 << 7));
++
++ /* set PHY Link Detect Timer to 1.1 second (11x 100ms) */
++ reg = 10;
++ } else {
++ /* set PHY Link Detect Timer to 0.4 second (4x 100ms) */
++ reg = 3;
++ }
++
++ reg <<= PSM_CONFIG_REG4_TIMER_PHY_LINK_DETECT_BASE;
++
++ /* reset PHY Link Detect */
++ sky2_pci_write16(hw, PSM_CONFIG_REG4,
++ reg | PSM_CONFIG_REG4_RST_PHY_LINK_DETECT);
++ sky2_pci_write16(hw, PSM_CONFIG_REG4, reg);
++
++
++ /* enable PHY Quick Link */
++ msk = sky2_read32(hw, B0_IMSK);
++ msk |= Y2_IS_PHY_QLNK;
++ sky2_write32(hw, B0_IMSK, msk);
++
++ /* check if PSMv2 was running before */
++ reg = sky2_pci_read16(hw, PSM_CONFIG_REG3);
++ if (reg & PCI_EXP_LNKCTL_ASPMC) {
++ int cap = pci_find_capability(pdev, PCI_CAP_ID_EXP);
++ /* restore the PCIe Link Control register */
++ sky2_pci_write16(hw, cap + PCI_EXP_LNKCTL, reg);
++ }
++
++ /* re-enable PEX PM in PEX PHY debug reg. 8 (clear bit 12) */
++ sky2_write32(hw, Y2_PEX_PHY_DATA, PEX_DB_ACCESS | (0x08UL << 16));
++ }
++
+ /* Clear I2C IRQ noise */
+ sky2_write32(hw, B2_I2C_IRQ, 1);
+
+@@ -4442,9 +4517,11 @@ static const char *sky2_name(u8 chipid, char *buf, int sz)
+ "FE+", /* 0xb8 */
+ "Supreme", /* 0xb9 */
+ "UL 2", /* 0xba */
++ "Unknown", /* 0xbb */
++ "Optima", /* 0xbc */
+ };
+
+- if (chipid >= CHIP_ID_YUKON_XL && chipid < CHIP_ID_YUKON_UL_2)
++ if (chipid >= CHIP_ID_YUKON_XL && chipid < CHIP_ID_YUKON_OPT)
+ strncpy(buf, name[chipid - CHIP_ID_YUKON_XL], sz);
+ else
+ snprintf(buf, sz, "(chip %#x)", chipid);
diff --git a/sources b/sources
index e69de29..30225e0 100644
--- a/sources
+++ b/sources
@@ -0,0 +1,2 @@
+260551284ac224c3a43c4adac7df4879 linux-2.6.32.tar.bz2
+744890f9651962ceae7663d44b19df65 patch-2.6.32.16.bz2
diff --git a/ssb_check_for_sprom.patch b/ssb_check_for_sprom.patch
new file mode 100644
index 0000000..7df784f
--- /dev/null
+++ b/ssb_check_for_sprom.patch
@@ -0,0 +1,185 @@
+From 380bed7aa858cbe2d4eeb783e2bed7d01828518d Mon Sep 17 00:00:00 2001
+From: John W. Linville <linville@tuxdriver.com>
+Date: Fri, 19 Mar 2010 14:58:01 -0400
+Subject: [PATCH v4] ssb: do not read SPROM if it does not exist
+
+Attempting to read registers that don't exist on the SSB bus can cause
+hangs on some boxes. At least some b43 devices are 'in the wild' that
+don't have SPROMs at all. When the SSB bus support loads, it attempts
+to read these (non-existant) SPROMs and causes hard hangs on the box --
+no console output, etc.
+
+This patch adds some intelligence to determine whether or not the SPROM
+is present before attempting to read it. This avoids those hard hangs
+on those devices with no SPROM attached to their SSB bus. The
+SSB-attached devices (e.g. b43, et al.) won't work, but at least the box
+will survive to test further patches. :-)
+
+Signed-off-by: John W. Linville <linville@tuxdriver.com>
+Cc: Larry Finger <Larry.Finger@lwfinger.net>
+Cc: Michael Buesch <mb@bu3sch.de>
+Cc: stable@kernel.org
+---
+Version 4, move read of ChipCommon status register to ssb_chipcommon_init
+Version 3, add missing semi-colon... :-(
+Version 2, check the correct place for ChipCommon core revision... :-)
+
+ drivers/ssb/driver_chipcommon.c | 3 +++
+ drivers/ssb/pci.c | 3 +++
+ drivers/ssb/sprom.c | 22 ++++++++++++++++++++++
+ include/linux/ssb/ssb.h | 3 +++
+ include/linux/ssb/ssb_driver_chipcommon.h | 15 +++++++++++++++
+ 5 files changed, 46 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/ssb/driver_chipcommon.c b/drivers/ssb/driver_chipcommon.c
+index 9681536..6cf288d 100644
+--- a/drivers/ssb/driver_chipcommon.c
++++ b/drivers/ssb/driver_chipcommon.c
+@@ -233,6 +233,9 @@ void ssb_chipcommon_init(struct ssb_chipcommon *cc)
+ {
+ if (!cc->dev)
+ return; /* We don't have a ChipCommon */
++ if (cc->dev->id.revision >= 11) {
++ cc->status = chipco_read32(cc, SSB_CHIPCO_CHIPSTAT);
++ }
+ ssb_pmu_init(cc);
+ chipco_powercontrol_init(cc);
+ ssb_chipco_set_clockmode(cc, SSB_CLKMODE_FAST);
+diff --git a/drivers/ssb/pci.c b/drivers/ssb/pci.c
+index 9e50896..2f7b16d 100644
+--- a/drivers/ssb/pci.c
++++ b/drivers/ssb/pci.c
+@@ -620,6 +620,9 @@ static int ssb_pci_sprom_get(struct ssb_bus *bus,
+ int err = -ENOMEM;
+ u16 *buf;
+
++ if (!ssb_is_sprom_available(bus))
++ return -ENODEV;
++
+ buf = kcalloc(SSB_SPROMSIZE_WORDS_R123, sizeof(u16), GFP_KERNEL);
+ if (!buf)
+ goto out;
+diff --git a/drivers/ssb/sprom.c b/drivers/ssb/sprom.c
+index d0e6762..55eb9b0 100644
+--- a/drivers/ssb/sprom.c
++++ b/drivers/ssb/sprom.c
+@@ -175,3 +175,25 @@ const struct ssb_sprom *ssb_get_fallback_sprom(void)
+ {
+ return fallback_sprom;
+ }
++
++bool ssb_is_sprom_available(struct ssb_bus *bus)
++{
++ /* status register only exists on chipcomon rev >= 11 */
++ if (bus->chipco.dev->id.revision < 11)
++ return true;
++
++ switch (bus->chip_id) {
++ case 0x4312:
++ return SSB_CHIPCO_CHST_4312_SPROM_PRESENT(bus->chipco.status);
++ case 0x4322:
++ return SSB_CHIPCO_CHST_4322_SPROM_PRESENT(bus->chipco.status);
++ case 0x4325:
++ return SSB_CHIPCO_CHST_4325_SPROM_PRESENT(bus->chipco.status);
++ default:
++ break;
++ }
++ if (bus->chipco.dev->id.revision >= 31)
++ return bus->chipco.capabilities & SSB_CHIPCO_CAP_SPROM;
++
++ return true;
++}
+diff --git a/include/linux/ssb/ssb.h b/include/linux/ssb/ssb.h
+index 24f9885..3b4da23 100644
+--- a/include/linux/ssb/ssb.h
++++ b/include/linux/ssb/ssb.h
+@@ -394,6 +394,9 @@ extern int ssb_bus_sdiobus_register(struct ssb_bus *bus,
+
+ extern void ssb_bus_unregister(struct ssb_bus *bus);
+
++/* Does the device have an SPROM? */
++extern bool ssb_is_sprom_available(struct ssb_bus *bus);
++
+ /* Set a fallback SPROM.
+ * See kdoc at the function definition for complete documentation. */
+ extern int ssb_arch_set_fallback_sprom(const struct ssb_sprom *sprom);
+diff --git a/include/linux/ssb/ssb_driver_chipcommon.h b/include/linux/ssb/ssb_driver_chipcommon.h
+index 4e27acf..2cdf249 100644
+--- a/include/linux/ssb/ssb_driver_chipcommon.h
++++ b/include/linux/ssb/ssb_driver_chipcommon.h
+@@ -53,6 +53,7 @@
+ #define SSB_CHIPCO_CAP_64BIT 0x08000000 /* 64-bit Backplane */
+ #define SSB_CHIPCO_CAP_PMU 0x10000000 /* PMU available (rev >= 20) */
+ #define SSB_CHIPCO_CAP_ECI 0x20000000 /* ECI available (rev >= 20) */
++#define SSB_CHIPCO_CAP_SPROM 0x40000000 /* SPROM present */
+ #define SSB_CHIPCO_CORECTL 0x0008
+ #define SSB_CHIPCO_CORECTL_UARTCLK0 0x00000001 /* Drive UART with internal clock */
+ #define SSB_CHIPCO_CORECTL_SE 0x00000002 /* sync clk out enable (corerev >= 3) */
+@@ -385,6 +386,7 @@
+
+
+ /** Chip specific Chip-Status register contents. */
++#define SSB_CHIPCO_CHST_4322_SPROM_EXISTS 0x00000040 /* SPROM present */
+ #define SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL 0x00000003
+ #define SSB_CHIPCO_CHST_4325_DEFCIS_SEL 0 /* OTP is powered up, use def. CIS, no SPROM */
+ #define SSB_CHIPCO_CHST_4325_SPROM_SEL 1 /* OTP is powered up, SPROM is present */
+@@ -398,6 +400,18 @@
+ #define SSB_CHIPCO_CHST_4325_RCAL_VALUE_SHIFT 4
+ #define SSB_CHIPCO_CHST_4325_PMUTOP_2B 0x00000200 /* 1 for 2b, 0 for to 2a */
+
++/** Macros to determine SPROM presence based on Chip-Status register. */
++#define SSB_CHIPCO_CHST_4312_SPROM_PRESENT(status) \
++ ((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \
++ SSB_CHIPCO_CHST_4325_OTP_SEL)
++#define SSB_CHIPCO_CHST_4322_SPROM_PRESENT(status) \
++ (status & SSB_CHIPCO_CHST_4322_SPROM_EXISTS)
++#define SSB_CHIPCO_CHST_4325_SPROM_PRESENT(status) \
++ (((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \
++ SSB_CHIPCO_CHST_4325_DEFCIS_SEL) && \
++ ((status & SSB_CHIPCO_CHST_4325_SPROM_OTP_SEL) != \
++ SSB_CHIPCO_CHST_4325_OTP_SEL))
++
+
+
+ /** Clockcontrol masks and values **/
+@@ -564,6 +578,7 @@ struct ssb_chipcommon_pmu {
+ struct ssb_chipcommon {
+ struct ssb_device *dev;
+ u32 capabilities;
++ u32 status;
+ /* Fast Powerup Delay constant */
+ u16 fast_pwrup_delay;
+ struct ssb_chipcommon_pmu pmu;
+--
+1.6.2.5
+
+From ec032742062ad1b01dfe75cfccdbc5b850837c23 Mon Sep 17 00:00:00 2001
+From: John W. Linville <linville@tuxdriver.com>
+Date: Tue, 30 Mar 2010 13:47:39 -0400
+Subject: [PATCH] ssb: avoid null ptr deref in ssb_is_sprom_available
+
+Some older devices don't have chipcommon, but they do have SPROM.
+
+Signed-off-by: John W. Linville <linville@tuxdriver.com>
+---
+ drivers/ssb/sprom.c | 4 ++++
+ 1 files changed, 4 insertions(+), 0 deletions(-)
+
+diff --git a/drivers/ssb/sprom.c b/drivers/ssb/sprom.c
+index 55eb9b0..874d8f1 100644
+--- a/drivers/ssb/sprom.c
++++ b/drivers/ssb/sprom.c
+@@ -178,6 +178,10 @@ const struct ssb_sprom *ssb_get_fallback_sprom(void)
+
+ bool ssb_is_sprom_available(struct ssb_bus *bus)
+ {
++ /* some older devices don't have chipcommon, but they have sprom */
++ if (!bus->chipco.dev)
++ return true;
++
+ /* status register only exists on chipcomon rev >= 11 */
+ if (bus->chipco.dev->id.revision < 11)
+ return true;
+--
+1.6.2.5
+
diff --git a/thinkpad-acpi-add-x100e.patch b/thinkpad-acpi-add-x100e.patch
new file mode 100644
index 0000000..0ccd062
--- /dev/null
+++ b/thinkpad-acpi-add-x100e.patch
@@ -0,0 +1,11 @@
+diff -up linux-2.6.32.noarch/drivers/platform/x86/thinkpad_acpi.c.mjg linux-2.6.32.noarch/drivers/platform/x86/thinkpad_acpi.c
+--- linux-2.6.32.noarch/drivers/platform/x86/thinkpad_acpi.c.mjg 2010-04-21 10:02:53.658034129 -0400
++++ linux-2.6.32.noarch/drivers/platform/x86/thinkpad_acpi.c 2010-04-21 10:03:30.402030108 -0400
+@@ -491,6 +491,7 @@ TPACPI_HANDLE(ec, root, "\\_SB.PCI0.ISA.
+ "\\_SB.PCI0.ISA.EC", /* A21e, A2xm/p, T20-22, X20-21 */
+ "\\_SB.PCI0.AD4S.EC0", /* i1400, R30 */
+ "\\_SB.PCI0.ICH3.EC0", /* R31 */
++ "\\_SB.PCI0.LPC0.EC", /* X100e */
+ "\\_SB.PCI0.LPC.EC", /* all others */
+ );
+
diff --git a/thinkpad-acpi-fix-backlight.patch b/thinkpad-acpi-fix-backlight.patch
new file mode 100644
index 0000000..163902c
--- /dev/null
+++ b/thinkpad-acpi-fix-backlight.patch
@@ -0,0 +1,56 @@
+diff -up linux-2.6.32.noarch/drivers/platform/x86/thinkpad_acpi.c.orig linux-2.6.32.noarch/drivers/platform/x86/thinkpad_acpi.c
+--- linux-2.6.32.noarch/drivers/platform/x86/thinkpad_acpi.c.orig 2010-05-17 16:18:05.748224844 -0400
++++ linux-2.6.32.noarch/drivers/platform/x86/thinkpad_acpi.c 2010-05-17 16:25:58.299199699 -0400
+@@ -3387,7 +3387,7 @@ static int __init hotkey_init(struct ibm
+ /* update bright_acpimode... */
+ tpacpi_check_std_acpi_brightness_support();
+
+- if (tp_features.bright_acpimode && acpi_video_backlight_support()) {
++ if (acpi_video_backlight_support()) {
+ printk(TPACPI_INFO
+ "This ThinkPad has standard ACPI backlight "
+ "brightness control, supported by the ACPI "
+@@ -6178,26 +6178,24 @@ static int __init brightness_init(struct
+ * going to publish a backlight interface
+ */
+ b = tpacpi_check_std_acpi_brightness_support();
+- if (b > 0) {
+
+- if (acpi_video_backlight_support()) {
+- if (brightness_enable > 1) {
+- printk(TPACPI_NOTICE
+- "Standard ACPI backlight interface "
+- "available, not loading native one.\n");
+- return 1;
+- } else if (brightness_enable == 1) {
+- printk(TPACPI_NOTICE
+- "Backlight control force enabled, even if standard "
+- "ACPI backlight interface is available\n");
+- }
+- } else {
+- if (brightness_enable > 1) {
+- printk(TPACPI_NOTICE
+- "Standard ACPI backlight interface not "
+- "available, thinkpad_acpi native "
+- "brightness control enabled\n");
+- }
++ if (acpi_video_backlight_support()) {
++ if (brightness_enable > 1) {
++ printk(TPACPI_NOTICE
++ "Standard ACPI backlight interface "
++ "available, not loading native one.\n");
++ return 1;
++ } else if (brightness_enable == 1) {
++ printk(TPACPI_NOTICE
++ "Backlight control force enabled, even if standard "
++ "ACPI backlight interface is available\n");
++ }
++ } else {
++ if (brightness_enable > 1) {
++ printk(TPACPI_NOTICE
++ "Standard ACPI backlight interface not "
++ "available, thinkpad_acpi native "
++ "brightness control enabled\n");
+ }
+ }
+
diff --git a/usb-obey-the-sysfs-power-wakeup-setting.patch b/usb-obey-the-sysfs-power-wakeup-setting.patch
new file mode 100644
index 0000000..8daccf0
--- /dev/null
+++ b/usb-obey-the-sysfs-power-wakeup-setting.patch
@@ -0,0 +1,51 @@
+This patch (as1403b) is a backport of commit
+48826626263d4a61d06fd8c5805da31f925aefa0 (USB: obey the sysfs
+power/wakeup setting) to 2.6.{32,33}.stable. It turns out that the
+bug it fixes does affect quite a few people using USB infrared
+remotes. The symptom is that the remote can no longer wake up the
+system from suspend.
+
+This fixes Bugzilla #16043.
+
+Red Hat Bugzilla #617559
+
+Signed-off-by: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx>
+
+---
+
+Index: 2.6.33.5/drivers/usb/core/driver.c
+===================================================================
+--- 2.6.33.5.orig/drivers/usb/core/driver.c
++++ 2.6.33.5/drivers/usb/core/driver.c
+@@ -1790,9 +1790,6 @@ int usb_external_resume_device(struct us
+
+ static void choose_wakeup(struct usb_device *udev, pm_message_t msg)
+ {
+- int w, i;
+- struct usb_interface *intf;
+-
+ /* Remote wakeup is needed only when we actually go to sleep.
+ * For things like FREEZE and QUIESCE, if the device is already
+ * autosuspended then its current wakeup setting is okay.
+@@ -1802,18 +1799,10 @@ static void choose_wakeup(struct usb_dev
+ return;
+ }
+
+- /* If remote wakeup is permitted, see whether any interface drivers
++ /* Allow remote wakeup if it is enabled, even if no interface drivers
+ * actually want it.
+ */
+- w = 0;
+- if (device_may_wakeup(&udev->dev) && udev->actconfig) {
+- for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
+- intf = udev->actconfig->interface[i];
+- w |= intf->needs_remote_wakeup;
+- }
+- }
+-
+- udev->do_remote_wakeup = w;
++ udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
+ }
+
+ int usb_suspend(struct device *dev, pm_message_t msg)
+
diff --git a/via-hwmon-temp-sensor.patch b/via-hwmon-temp-sensor.patch
new file mode 100644
index 0000000..49ced3e
--- /dev/null
+++ b/via-hwmon-temp-sensor.patch
@@ -0,0 +1,391 @@
+diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
+index 6857560..4414182 100644
+--- a/drivers/hwmon/Kconfig
++++ b/drivers/hwmon/Kconfig
+@@ -795,6 +795,14 @@ config SENSORS_TMP421
+ This driver can also be built as a module. If so, the module
+ will be called tmp421.
+
++config SENSORS_VIA_CPUTEMP
++ tristate "VIA CPU temperature sensor"
++ depends on X86
++ help
++ If you say yes here you get support for the temperature
++ sensor inside your CPU. Supported all are all known variants
++ of the VIA C7 and Nano.
++
+ config SENSORS_VIA686A
+ tristate "VIA686A"
+ depends on PCI
+diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
+index 9f46cb0..32ed56a 100644
+--- a/drivers/hwmon/Makefile
++++ b/drivers/hwmon/Makefile
+@@ -85,6 +85,7 @@ obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
+ obj-$(CONFIG_SENSORS_THMC50) += thmc50.o
+ obj-$(CONFIG_SENSORS_TMP401) += tmp401.o
+ obj-$(CONFIG_SENSORS_TMP421) += tmp421.o
++obj-$(CONFIG_SENSORS_VIA_CPUTEMP)+= via-cputemp.o
+ obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
+ obj-$(CONFIG_SENSORS_VT1211) += vt1211.o
+ obj-$(CONFIG_SENSORS_VT8231) += vt8231.o
+diff --git a/drivers/hwmon/via-cputemp.c b/drivers/hwmon/via-cputemp.c
+new file mode 100644
+index 0000000..2abe516
+--- /dev/null
++++ b/drivers/hwmon/via-cputemp.c
+@@ -0,0 +1,354 @@
++/*
++ * via-cputemp.c - Driver for VIA CPU core temperature monitoring
++ * Copyright (C) 2009 VIA Technologies, Inc.
++ *
++ * based on existing coretemp.c, which is
++ *
++ * Copyright (C) 2007 Rudolf Marek <r.marek@assembler.cz>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; version 2 of the License.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software
++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
++ * 02110-1301 USA.
++ */
++
++#include <linux/module.h>
++#include <linux/delay.h>
++#include <linux/init.h>
++#include <linux/slab.h>
++#include <linux/jiffies.h>
++#include <linux/hwmon.h>
++#include <linux/sysfs.h>
++#include <linux/hwmon-sysfs.h>
++#include <linux/err.h>
++#include <linux/mutex.h>
++#include <linux/list.h>
++#include <linux/platform_device.h>
++#include <linux/cpu.h>
++#include <asm/msr.h>
++#include <asm/processor.h>
++
++#define DRVNAME "via_cputemp"
++
++typedef enum { SHOW_TEMP, SHOW_LABEL, SHOW_NAME } SHOW;
++
++/*
++ * Functions declaration
++ */
++
++struct via_cputemp_data {
++ struct device *hwmon_dev;
++ const char *name;
++ u32 id;
++ u32 msr;
++};
++
++/*
++ * Sysfs stuff
++ */
++
++static ssize_t show_name(struct device *dev, struct device_attribute
++ *devattr, char *buf)
++{
++ int ret;
++ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
++ struct via_cputemp_data *data = dev_get_drvdata(dev);
++
++ if (attr->index == SHOW_NAME)
++ ret = sprintf(buf, "%s\n", data->name);
++ else /* show label */
++ ret = sprintf(buf, "Core %d\n", data->id);
++ return ret;
++}
++
++static ssize_t show_temp(struct device *dev,
++ struct device_attribute *devattr, char *buf)
++{
++ struct via_cputemp_data *data = dev_get_drvdata(dev);
++ u32 eax, edx;
++ int err;
++
++ err = rdmsr_safe_on_cpu(data->id, data->msr, &eax, &edx);
++ if (err)
++ return -EAGAIN;
++
++ err = sprintf(buf, "%d\n", (eax & 0xffffff) * 1000);
++
++ return err;
++}
++
++static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL,
++ SHOW_TEMP);
++static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_name, NULL, SHOW_LABEL);
++static SENSOR_DEVICE_ATTR(name, S_IRUGO, show_name, NULL, SHOW_NAME);
++
++static struct attribute *via_cputemp_attributes[] = {
++ &sensor_dev_attr_name.dev_attr.attr,
++ &sensor_dev_attr_temp1_label.dev_attr.attr,
++ &sensor_dev_attr_temp1_input.dev_attr.attr,
++ NULL
++};
++
++static const struct attribute_group via_cputemp_group = {
++ .attrs = via_cputemp_attributes,
++};
++
++static int __devinit via_cputemp_probe(struct platform_device *pdev)
++{
++ struct via_cputemp_data *data;
++ struct cpuinfo_x86 *c = &cpu_data(pdev->id);
++ int err;
++ u32 eax, edx;
++
++ if (!(data = kzalloc(sizeof(struct via_cputemp_data), GFP_KERNEL))) {
++ err = -ENOMEM;
++ dev_err(&pdev->dev, "Out of memory\n");
++ goto exit;
++ }
++
++ data->id = pdev->id;
++ data->name = "via-cputemp";
++
++ switch (c->x86_model) {
++ case 0xA:
++ /* C7 A */
++ case 0xD:
++ /* C7 D */
++ data->msr = 0x1169;
++ break;
++ case 0xF:
++ /* Nano */
++ data->msr = 0x1423;
++ break;
++ default:
++ err = -ENODEV;
++ goto exit_free;
++ }
++
++ /* test if we can access the TEMPERATURE MSR */
++ err = rdmsr_safe_on_cpu(data->id, data->msr, &eax, &edx);
++ if (err) {
++ dev_err(&pdev->dev,
++ "Unable to access TEMPERATURE MSR, giving up\n");
++ goto exit_free;
++ }
++
++ platform_set_drvdata(pdev, data);
++
++ if ((err = sysfs_create_group(&pdev->dev.kobj, &via_cputemp_group)))
++ goto exit_free;
++
++ data->hwmon_dev = hwmon_device_register(&pdev->dev);
++ if (IS_ERR(data->hwmon_dev)) {
++ err = PTR_ERR(data->hwmon_dev);
++ dev_err(&pdev->dev, "Class registration failed (%d)\n",
++ err);
++ goto exit_class;
++ }
++
++ return 0;
++
++exit_class:
++ sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
++exit_free:
++ kfree(data);
++exit:
++ return err;
++}
++
++static int __devexit via_cputemp_remove(struct platform_device *pdev)
++{
++ struct via_cputemp_data *data = platform_get_drvdata(pdev);
++
++ hwmon_device_unregister(data->hwmon_dev);
++ sysfs_remove_group(&pdev->dev.kobj, &via_cputemp_group);
++ platform_set_drvdata(pdev, NULL);
++ kfree(data);
++ return 0;
++}
++
++static struct platform_driver via_cputemp_driver = {
++ .driver = {
++ .owner = THIS_MODULE,
++ .name = DRVNAME,
++ },
++ .probe = via_cputemp_probe,
++ .remove = __devexit_p(via_cputemp_remove),
++};
++
++struct pdev_entry {
++ struct list_head list;
++ struct platform_device *pdev;
++ unsigned int cpu;
++};
++
++static LIST_HEAD(pdev_list);
++static DEFINE_MUTEX(pdev_list_mutex);
++
++static int __cpuinit via_cputemp_device_add(unsigned int cpu)
++{
++ int err;
++ struct platform_device *pdev;
++ struct pdev_entry *pdev_entry;
++
++ pdev = platform_device_alloc(DRVNAME, cpu);
++ if (!pdev) {
++ err = -ENOMEM;
++ printk(KERN_ERR DRVNAME ": Device allocation failed\n");
++ goto exit;
++ }
++
++ pdev_entry = kzalloc(sizeof(struct pdev_entry), GFP_KERNEL);
++ if (!pdev_entry) {
++ err = -ENOMEM;
++ goto exit_device_put;
++ }
++
++ err = platform_device_add(pdev);
++ if (err) {
++ printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
++ err);
++ goto exit_device_free;
++ }
++
++ pdev_entry->pdev = pdev;
++ pdev_entry->cpu = cpu;
++ mutex_lock(&pdev_list_mutex);
++ list_add_tail(&pdev_entry->list, &pdev_list);
++ mutex_unlock(&pdev_list_mutex);
++
++ return 0;
++
++exit_device_free:
++ kfree(pdev_entry);
++exit_device_put:
++ platform_device_put(pdev);
++exit:
++ return err;
++}
++
++#ifdef CONFIG_HOTPLUG_CPU
++static void via_cputemp_device_remove(unsigned int cpu)
++{
++ struct pdev_entry *p, *n;
++ mutex_lock(&pdev_list_mutex);
++ list_for_each_entry_safe(p, n, &pdev_list, list) {
++ if (p->cpu == cpu) {
++ platform_device_unregister(p->pdev);
++ list_del(&p->list);
++ kfree(p);
++ }
++ }
++ mutex_unlock(&pdev_list_mutex);
++}
++
++static int __cpuinit via_cputemp_cpu_callback(struct notifier_block *nfb,
++ unsigned long action, void *hcpu)
++{
++ unsigned int cpu = (unsigned long) hcpu;
++
++ switch (action) {
++ case CPU_ONLINE:
++ case CPU_DOWN_FAILED:
++ via_cputemp_device_add(cpu);
++ break;
++ case CPU_DOWN_PREPARE:
++ via_cputemp_device_remove(cpu);
++ break;
++ }
++ return NOTIFY_OK;
++}
++
++static struct notifier_block via_cputemp_cpu_notifier __refdata = {
++ .notifier_call = via_cputemp_cpu_callback,
++};
++#endif /* !CONFIG_HOTPLUG_CPU */
++
++static int __init via_cputemp_init(void)
++{
++ int i, err = -ENODEV;
++ struct pdev_entry *p, *n;
++
++ if (cpu_data(0).x86_vendor != X86_VENDOR_CENTAUR) {
++ printk(KERN_DEBUG "not a VIA CPU\n");
++ goto exit;
++ }
++
++ err = platform_driver_register(&via_cputemp_driver);
++ if (err)
++ goto exit;
++
++ for_each_online_cpu(i) {
++ struct cpuinfo_x86 *c = &cpu_data(i);
++
++ if (c->x86 != 6)
++ continue;
++
++ if (c->x86_model < 0x0a)
++ continue;
++
++ if (c->x86_model > 0x0f) {
++ printk(KERN_WARNING DRVNAME ": Unknown CPU "
++ "model %x\n", c->x86_model);
++ continue;
++ }
++
++ err = via_cputemp_device_add(i);
++ if (err)
++ goto exit_devices_unreg;
++ }
++ if (list_empty(&pdev_list)) {
++ err = -ENODEV;
++ goto exit_driver_unreg;
++ }
++
++#ifdef CONFIG_HOTPLUG_CPU
++ register_hotcpu_notifier(&via_cputemp_cpu_notifier);
++#endif
++ return 0;
++
++exit_devices_unreg:
++ mutex_lock(&pdev_list_mutex);
++ list_for_each_entry_safe(p, n, &pdev_list, list) {
++ platform_device_unregister(p->pdev);
++ list_del(&p->list);
++ kfree(p);
++ }
++ mutex_unlock(&pdev_list_mutex);
++exit_driver_unreg:
++ platform_driver_unregister(&via_cputemp_driver);
++exit:
++ return err;
++}
++
++static void __exit via_cputemp_exit(void)
++{
++ struct pdev_entry *p, *n;
++#ifdef CONFIG_HOTPLUG_CPU
++ unregister_hotcpu_notifier(&via_cputemp_cpu_notifier);
++#endif
++ mutex_lock(&pdev_list_mutex);
++ list_for_each_entry_safe(p, n, &pdev_list, list) {
++ platform_device_unregister(p->pdev);
++ list_del(&p->list);
++ kfree(p);
++ }
++ mutex_unlock(&pdev_list_mutex);
++ platform_driver_unregister(&via_cputemp_driver);
++}
++
++MODULE_AUTHOR("Harald Welte <HaraldWelte@viatech.com>");
++MODULE_DESCRIPTION("VIA CPU temperature monitor");
++MODULE_LICENSE("GPL");
++
++module_init(via_cputemp_init)
++module_exit(via_cputemp_exit)
diff --git a/viafb-neuter-device-table.patch b/viafb-neuter-device-table.patch
new file mode 100644
index 0000000..359a0f5
--- /dev/null
+++ b/viafb-neuter-device-table.patch
@@ -0,0 +1,21 @@
+Index: linux-2.6.32.noarch/drivers/video/via/viafbdev.c
+===================================================================
+--- linux-2.6.32.noarch.orig/drivers/video/via/viafbdev.c
++++ linux-2.6.32.noarch/drivers/video/via/viafbdev.c
+@@ -2161,6 +2161,8 @@ static int __init viafb_setup(char *opti
+ #endif
+
+ static struct pci_device_id viafb_pci_table[] __devinitdata = {
++/* We don't want this driver to autoload in F11/F-12 */
++/*
+ { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_CLE266_DID),
+ .driver_data = UNICHROME_CLE266 },
+ { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_PM800_DID),
+@@ -2183,6 +2185,7 @@ static struct pci_device_id viafb_pci_ta
+ .driver_data = UNICHROME_VX800 },
+ { PCI_DEVICE(PCI_VENDOR_ID_VIA, UNICHROME_VX855_DID),
+ .driver_data = UNICHROME_VX855 },
++*/
+ { }
+ };
+ MODULE_DEVICE_TABLE(pci, viafb_pci_table);
diff --git a/wmi-check-find_guid-return-value-to-prevent-oops.patch b/wmi-check-find_guid-return-value-to-prevent-oops.patch
new file mode 100644
index 0000000..341003c
--- /dev/null
+++ b/wmi-check-find_guid-return-value-to-prevent-oops.patch
@@ -0,0 +1,36 @@
+From: Paul Rolland <rol@as2917.net>
+Date: Wed, 30 Dec 2009 06:19:12 +0000 (-0500)
+Subject: wmi: check find_guid() return value to prevent oops
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=b58454ec25e80fdb84e294758aeb22dd6d5ee6f9
+
+wmi: check find_guid() return value to prevent oops
+
+Signed-off-by: rol@as2917.net <Paul Rolland>
+Signed-off-by: Len Brown <len.brown@intel.com>
+Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
+---
+
+diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
+index 9f93d6c..cc9ad74 100644
+--- a/drivers/platform/x86/wmi.c
++++ b/drivers/platform/x86/wmi.c
+@@ -492,8 +492,7 @@ wmi_notify_handler handler, void *data)
+ if (!guid || !handler)
+ return AE_BAD_PARAMETER;
+
+- find_guid(guid, &block);
+- if (!block)
++ if (!find_guid(guid, &block))
+ return AE_NOT_EXIST;
+
+ if (block->handler)
+@@ -521,8 +520,7 @@ acpi_status wmi_remove_notify_handler(const char *guid)
+ if (!guid)
+ return AE_BAD_PARAMETER;
+
+- find_guid(guid, &block);
+- if (!block)
++ if (!find_guid(guid, &block))
+ return AE_NOT_EXIST;
+
+ if (!block->handler)
diff --git a/wmi-survive-bios-with-duplicate-guids.patch b/wmi-survive-bios-with-duplicate-guids.patch
new file mode 100644
index 0000000..15c7be3
--- /dev/null
+++ b/wmi-survive-bios-with-duplicate-guids.patch
@@ -0,0 +1,76 @@
+From: Carlos Corbacho <carlos@strangeworlds.co.uk>
+Date: Sat, 26 Dec 2009 19:14:59 +0000 (+0000)
+Subject: ACPI: WMI: Survive BIOS with duplicate GUIDs
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=d1f9e4970742bb1e22d07b01bd44f9c357d25c42
+
+ACPI: WMI: Survive BIOS with duplicate GUIDs
+
+It would appear that in BIOS's with nVidia hooks, the GUID
+05901221-D566-11D1-B2F0-00A0C9062910 is duplicated. For now, the simplest
+solution is to just ignore any duplicate GUIDs. These particular hooks are not
+currently supported/ used in the kernel, so whoever does that can figure out
+what the 'right' solution should be (if there's a better one).
+
+http://bugzilla.kernel.org/show_bug.cgi?id=14846
+
+Signed-off-by: Carlos Corbacho <carlos@strangeworlds.co.uk>
+Reported-by: Larry Finger <Larry.Finger@lwfinger.net>
+Reported-by: Oldřich Jedlička <oldium.pro@seznam.cz>
+Signed-off-by: Len Brown <len.brown@intel.com>
+---
+
+diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c
+index cc9ad74..b104302 100644
+--- a/drivers/platform/x86/wmi.c
++++ b/drivers/platform/x86/wmi.c
+@@ -714,6 +714,22 @@ static int wmi_class_init(void)
+ return ret;
+ }
+
++static bool guid_already_parsed(const char *guid_string)
++{
++ struct guid_block *gblock;
++ struct wmi_block *wblock;
++ struct list_head *p;
++
++ list_for_each(p, &wmi_blocks.list) {
++ wblock = list_entry(p, struct wmi_block, list);
++ gblock = &wblock->gblock;
++
++ if (strncmp(gblock->guid, guid_string, 16) == 0)
++ return true;
++ }
++ return false;
++}
++
+ /*
+ * Parse the _WDG method for the GUID data blocks
+ */
+@@ -723,6 +739,7 @@ static __init acpi_status parse_wdg(acpi_handle handle)
+ union acpi_object *obj;
+ struct guid_block *gblock;
+ struct wmi_block *wblock;
++ char guid_string[37];
+ acpi_status status;
+ u32 i, total;
+
+@@ -745,6 +762,19 @@ static __init acpi_status parse_wdg(acpi_handle handle)
+ memcpy(gblock, obj->buffer.pointer, obj->buffer.length);
+
+ for (i = 0; i < total; i++) {
++ /*
++ Some WMI devices, like those for nVidia hooks, have a
++ duplicate GUID. It's not clear what we should do in this
++ case yet, so for now, we'll just ignore the duplicate.
++ Anyone who wants to add support for that device can come
++ up with a better workaround for the mess then.
++ */
++ if (guid_already_parsed(gblock[i].guid) == true) {
++ wmi_gtoa(gblock[i].guid, guid_string);
++ printk(KERN_INFO PREFIX "Skipping duplicate GUID %s\n",
++ guid_string);
++ continue;
++ }
+ wblock = kzalloc(sizeof(struct wmi_block), GFP_KERNEL);
+ if (!wblock)
+ return AE_NO_MEMORY;
diff --git a/xfs-prevent-swapext-from-operating-on-write-only-files.patch b/xfs-prevent-swapext-from-operating-on-write-only-files.patch
new file mode 100644
index 0000000..c1e5224
--- /dev/null
+++ b/xfs-prevent-swapext-from-operating-on-write-only-files.patch
@@ -0,0 +1,42 @@
+From: Dan Rosenberg <dan.j.rosenberg@gmail.com>
+Date: Thu, 24 Jun 2010 02:07:47 +0000 (+1000)
+Subject: xfs: prevent swapext from operating on write-only files
+X-Git-Tag: v2.6.35-rc4~4^2~4
+X-Git-Url: http://git.kernel.org/?p=linux%2Fkernel%2Fgit%2Ftorvalds%2Flinux-2.6.git;a=commitdiff_plain;h=1817176a86352f65210139d4c794ad2d19fc6b63
+
+xfs: prevent swapext from operating on write-only files
+[ cve-2010-2266 ]
+[ cebbert@redhat.com : backport to 2.6.32 ]
+
+This patch prevents user "foo" from using the SWAPEXT ioctl to swap
+a write-only file owned by user "bar" into a file owned by "foo" and
+subsequently reading it. It does so by checking that the file
+descriptors passed to the ioctl are also opened for reading.
+
+Signed-off-by: Dan Rosenberg <dan.j.rosenberg@gmail.com>
+Reviewed-by: Christoph Hellwig <hch@lst.de>
+---
+
+diff --git a/fs/xfs/xfs_dfrag.c b/fs/xfs/xfs_dfrag.c
+index 5bba29a..7f159d2 100644
+--- a/fs/xfs/xfs_dfrag.c
++++ b/fs/xfs/xfs_dfrag.c
+@@ -69,7 +69,9 @@ xfs_swapext(
+ goto out;
+ }
+
+- if (!(file->f_mode & FMODE_WRITE) || (file->f_flags & O_APPEND)) {
++ if (!(file->f_mode & FMODE_WRITE) ||
++ !(file->f_mode & FMODE_READ) ||
++ (file->f_flags & O_APPEND)) {
+ error = XFS_ERROR(EBADF);
+ goto out_put_file;
+ }
+@@ -81,6 +83,7 @@ xfs_swapext(
+ }
+
+ if (!(target_file->f_mode & FMODE_WRITE) ||
++ !(target_file->f_mode & FMODE_READ) ||
+ (target_file->f_flags & O_APPEND)) {
+ error = XFS_ERROR(EBADF);
+ goto out_put_target_file;